diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index 4a6c50a..0000000 --- a/.claude/settings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "hooks": { - "Stop": [ - { - "matcher": "", - "hooks": [ - { - "type": "command", - "command": "bash -c 'CHANGED=$(git -C \"$CLAUDE_PROJECT_DIR\" diff --name-only HEAD 2>/dev/null; git -C \"$CLAUDE_PROJECT_DIR\" diff --name-only --cached 2>/dev/null) && echo \"$CHANGED\" | grep -qE \"^main/opengeometry(-three)?/(src|examples-vite|tests)/\" || exit 0; cd \"$CLAUDE_PROJECT_DIR\" && echo \"[hook] running cargo fmt --check\" && cargo fmt --check --manifest-path main/opengeometry/Cargo.toml && echo \"[hook] running cargo check\" && cargo check --manifest-path main/opengeometry/Cargo.toml --quiet && echo \"[hook] running npm run lint:check\" && npm run --silent lint:check && echo \"[hook] gates passed\"'" - } - ] - } - ] - } -} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bfd5911..be7d1bf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,10 +1,7 @@ name: Release Package on: - push: - branches: [main] - pull_request: - branches: [main] + workflow_dispatch: permissions: contents: write @@ -16,43 +13,29 @@ jobs: check-version: runs-on: ubuntu-latest outputs: - version-changed: ${{ steps.version-check.outputs.changed }} - current-version: ${{ steps.version-check.outputs.current-version }} - previous-version: ${{ steps.version-check.outputs.previous-version }} - is-valid-increment: ${{ steps.version-check.outputs.is-valid-increment }} + current-version: ${{ steps.current-version.outputs.version }} npm-exists: ${{ steps.npm-check.outputs.exists }} should-publish: ${{ steps.publish-decision.outputs.should-publish }} steps: - name: Checkout current commit uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '18' + node-version: '26' - name: Get current version id: current-version run: | - CURRENT_VERSION=$(node -p "require('./package.json').version") + CURRENT_VERSION=$(node -p "require('./main/package.json').version") echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT - - name: Get previous version - id: previous-version - run: | - # Get the previous commit - PREV_COMMIT=$(git rev-parse HEAD~1) - # Get package.json from previous commit - PREV_VERSION=$(git show $PREV_COMMIT:package.json | node -p "JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8')).version") - echo "version=$PREV_VERSION" >> $GITHUB_OUTPUT - - name: Check if current version exists on NPM id: npm-check run: | - PACKAGE_NAME=$(node -p "require('./package.json').name") + PACKAGE_NAME=$(node -p "require('./main/package.json').name") CURRENT_VERSION="${{ steps.current-version.outputs.version }}" echo "Checking if $PACKAGE_NAME@$CURRENT_VERSION exists on NPM..." @@ -65,70 +48,27 @@ jobs: echo "❌ Version $CURRENT_VERSION does not exist on NPM" fi - - name: Check version change and validate increment - id: version-check - run: | - CURRENT="${{ steps.current-version.outputs.version }}" - PREVIOUS="${{ steps.previous-version.outputs.version }}" - - echo "current-version=$CURRENT" >> $GITHUB_OUTPUT - echo "previous-version=$PREVIOUS" >> $GITHUB_OUTPUT - - if [ "$CURRENT" != "$PREVIOUS" ]; then - echo "changed=true" >> $GITHUB_OUTPUT - echo "Version changed from $PREVIOUS to $CURRENT" - - # Install semver for version comparison - npm install -g semver - - # Check if current version is greater than previous - if semver gt "$CURRENT" "$PREVIOUS"; then - echo "is-valid-increment=true" >> $GITHUB_OUTPUT - echo "✅ Valid version increment: $PREVIOUS → $CURRENT" - else - echo "is-valid-increment=false" >> $GITHUB_OUTPUT - echo "❌ Invalid version increment: $PREVIOUS → $CURRENT" - echo "Current version must be greater than previous version" - exit 1 - fi - else - echo "changed=false" >> $GITHUB_OUTPUT - echo "Version unchanged: $CURRENT" - fi - - name: Decide whether to publish id: publish-decision run: | - VERSION_CHANGED="${{ steps.version-check.outputs.changed }}" - IS_VALID_INCREMENT="${{ steps.version-check.outputs.is-valid-increment }}" NPM_EXISTS="${{ steps.npm-check.outputs.exists }}" CURRENT_VERSION="${{ steps.current-version.outputs.version }}" - - echo "🔍 Publishing Decision Matrix:" - echo " Version Changed: $VERSION_CHANGED" - echo " Valid Increment: $IS_VALID_INCREMENT" - echo " Exists on NPM: $NPM_EXISTS" - echo " Current Version: $CURRENT_VERSION" - - # Publish if: - # 1. Version changed AND it's a valid increment, OR - # 2. Current version doesn't exist on NPM (failed previous build) - if [[ "$VERSION_CHANGED" == "true" && "$IS_VALID_INCREMENT" == "true" ]] || [[ "$NPM_EXISTS" == "false" ]]; then + + echo "Exists on NPM: $NPM_EXISTS" + echo "Current Version: $CURRENT_VERSION" + + if [[ "$NPM_EXISTS" == "false" ]]; then echo "should-publish=true" >> $GITHUB_OUTPUT - if [[ "$VERSION_CHANGED" == "true" ]]; then - echo "✅ Will publish: Version increment detected" - else - echo "✅ Will publish: Version missing from NPM (likely failed previous build)" - fi + echo "✅ Will publish: Version $CURRENT_VERSION is not on NPM" else echo "should-publish=false" >> $GITHUB_OUTPUT - echo "⏭️ Will skip: Version unchanged and already exists on NPM" + echo "⏭️ Will skip: Version $CURRENT_VERSION already exists on NPM" fi build-and-publish: needs: check-version runs-on: ubuntu-latest - if: needs.check-version.outputs.should-publish == 'true' + if: github.ref == 'refs/heads/main' && needs.check-version.outputs.should-publish == 'true' steps: - name: Checkout code @@ -137,67 +77,82 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '18' + node-version: '26' registry-url: 'https://registry.npmjs.org' - name: Setup Rust - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@master with: - toolchain: stable - target: wasm32-unknown-unknown - override: true + toolchain: '1.88.0' + targets: wasm32-unknown-unknown + components: rustfmt, clippy + + - id: wasm-pack + uses: actions/cache@v4 + with: + path: ~/.cargo/bin/wasm-pack + key: wasm-pack-0.13.1-${{ runner.os }}-${{ runner.arch }} - name: Install wasm-pack - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + if: steps.wasm-pack.outputs.cache-hit != 'true' + run: cargo install wasm-pack --version 0.13.1 --locked - name: Cache Rust dependencies - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | - ~/.cargo/registry - ~/.cargo/git + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db main/opengeometry/target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo- + main/opengeometry/test-support/target + key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('main/**/Cargo.lock') }} + restore-keys: cargo-${{ runner.os }}-${{ runner.arch }}- - name: Cache Node.js dependencies uses: actions/cache@v3 with: path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + key: ${{ runner.os }}-node-${{ hashFiles('main/package-lock.json') }} restore-keys: | ${{ runner.os }}-node- - name: Install dependencies + working-directory: main run: npm ci - - name: Lint TypeScript - run: npm run lint:check + - name: Fetch crate dependencies + working-directory: main + run: | + git ls-files '*Cargo.lock' | while read -r lock; do + cargo fetch --locked --manifest-path "$(dirname "$lock")/Cargo.toml" + done - name: Run build + working-directory: main run: npm run build - name: Verify dist directory run: | echo "Checking dist directory contents:" - ls -la dist/ + ls -la main/dist/ echo "Checking if package.json exists in dist:" - if [ -f "dist/package.json" ]; then + if [ -f "main/dist/package.json" ]; then echo "✅ package.json found in dist" - echo "Dist package version: $(node -p "require('./dist/package.json').version")" + echo "Dist package version: $(node -p "require('./main/dist/package.json').version")" else echo "❌ package.json not found in dist" exit 1 fi - - name: Run tests - run: npm test + - name: Run checks + working-directory: main + run: npm run check - name: Final NPM existence check id: final-npm-check run: | - PACKAGE_NAME=$(node -p "require('./dist/package.json').name") + PACKAGE_NAME=$(node -p "require('./main/dist/package.json').name") VERSION="${{ needs.check-version.outputs.current-version }}" echo "🔍 Final check before publishing..." @@ -211,9 +166,8 @@ jobs: - name: Publish to NPM if: steps.final-npm-check.outputs.exists == 'false' - run: | - cd dist - npm publish --access public + working-directory: main/dist + run: npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} @@ -226,7 +180,7 @@ jobs: body: | ## Changes in v${{ needs.check-version.outputs.current-version }} - ${{ needs.check-version.outputs.version-changed == 'true' && format('Version bumped from {0} to {1}', needs.check-version.outputs.previous-version, needs.check-version.outputs.current-version) || format('Recovery publish for version {0}', needs.check-version.outputs.current-version) }} + Published from a manual run of the release workflow. ### Package Information - **Package Name**: opengeometry @@ -256,11 +210,7 @@ jobs: echo "**Package**: [opengeometry@${{ needs.check-version.outputs.current-version }}](https://www.npmjs.com/package/opengeometry)" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Publish Reason" >> $GITHUB_STEP_SUMMARY - if [[ "${{ needs.check-version.outputs.version-changed }}" == "true" ]]; then - echo "- 📈 Version increment: ${{ needs.check-version.outputs.previous-version }} → ${{ needs.check-version.outputs.current-version }}" >> $GITHUB_STEP_SUMMARY - else - echo "- 🔄 Recovery publish: Version was missing from NPM registry" >> $GITHUB_STEP_SUMMARY - fi + echo "- Manual run: Version was not on NPM" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Next Steps" >> $GITHUB_STEP_SUMMARY echo "- Check the [NPM package page](https://www.npmjs.com/package/opengeometry)" >> $GITHUB_STEP_SUMMARY @@ -286,13 +236,9 @@ jobs: run: | echo "## ⏭️ Build Skipped" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "**Reason**: No version change and package already exists on NPM" >> $GITHUB_STEP_SUMMARY + echo "**Reason**: This version already exists on NPM" >> $GITHUB_STEP_SUMMARY echo "**Current Version**: ${{ needs.check-version.outputs.current-version }}" >> $GITHUB_STEP_SUMMARY echo "**NPM Status**: ✅ Already published" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "### To trigger a new release:" >> $GITHUB_STEP_SUMMARY - echo '```bash' >> $GITHUB_STEP_SUMMARY - echo 'npm version patch # for bug fixes' >> $GITHUB_STEP_SUMMARY - echo 'npm version minor # for new features' >> $GITHUB_STEP_SUMMARY - echo 'npm version major # for breaking changes' >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY \ No newline at end of file + echo "### To publish a new release:" >> $GITHUB_STEP_SUMMARY + echo "Bump the version in main/package.json, main/package-lock.json, main/opengeometry/Cargo.toml, main/opengeometry/Cargo.lock and main/opengeometry/test-support/Cargo.lock, merge to main, then start this workflow by hand on main." >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 0000000..819fff8 --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,96 @@ +name: OpenGeometry 2.5 verification + +on: + push: + paths: + - 'main/**' + - 'README.md' + - 'MIGRATION.md' + - 'LICENSE.md' + - '.github/workflows/verify.yml' + pull_request: + paths: + - 'main/**' + - 'README.md' + - 'MIGRATION.md' + - 'LICENSE.md' + - '.github/workflows/verify.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + verify: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + timeout-minutes: 120 + defaults: + run: + working-directory: main + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: '1.88.0' + targets: wasm32-unknown-unknown + components: rustfmt, clippy + - uses: actions/setup-node@v4 + with: + node-version: '26' + cache: npm + cache-dependency-path: main/package-lock.json + - id: wasm-pack + uses: actions/cache@v4 + with: + path: ~/.cargo/bin/wasm-pack + key: wasm-pack-0.13.1-${{ runner.os }}-${{ runner.arch }} + - name: Install wasm-pack + if: steps.wasm-pack.outputs.cache-hit != 'true' + run: cargo install wasm-pack --version 0.13.1 --locked + - uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db + main/opengeometry/target + main/opengeometry/test-support/target + key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('main/**/Cargo.lock') }} + restore-keys: cargo-${{ runner.os }}-${{ runner.arch }}- + - name: Install SDK dependencies + run: npm ci + - name: Fetch crate dependencies + run: | + git ls-files '*Cargo.lock' | while read -r lock; do + cargo fetch --locked --manifest-path "$(dirname "$lock")/Cargo.toml" + done + - name: Install Chromium + run: npx playwright install --with-deps chromium + - name: Check + run: npm run check + - name: Browser three 0.168 + if: ${{ !cancelled() }} + run: node scripts/check/verify.mjs --full --only browser:three-168 + - name: Browser three 0.184 + if: ${{ !cancelled() }} + run: node scripts/check/verify.mjs --full --only browser:three-184 + - name: Kernel time budgets + if: ${{ !cancelled() }} + run: cargo run --release --offline --manifest-path opengeometry/Cargo.toml --example budgets + - name: Pack check logs + if: always() + run: tar czf "$RUNNER_TEMP/check-logs.tgz" .check + - uses: actions/upload-artifact@v4 + if: always() + with: + name: check-logs-${{ matrix.os }} + path: ${{ runner.temp }}/check-logs.tgz + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() && matrix.os == 'ubuntu-latest' }} + with: + name: step-exports + path: main/test-results/**/*.step diff --git a/.gitignore b/.gitignore index 429560c..d595986 100644 --- a/.gitignore +++ b/.gitignore @@ -25,22 +25,25 @@ node_modules/ # JS/TS build artifacts dist/ -main/opengeometry-three/examples-dist/ -main/opengeometry-three/examples-vite/.generated/ main/opengeometry/pkg/ +main/dist/ +main/opengeometry-three/examples-dist/ +main/**/target/ +main/test-results/ +main/playwright-report/ +main/.check/ +!main/opengeometry/Cargo.lock +!main/opengeometry/test-support/Cargo.lock .DS_Store .vscode/ settings.json -# Track .claude/settings.json (team hook config); keep .claude/settings.local.json ignored. -!.claude/settings.json -.claude/settings.local.json -.claude/scheduled_tasks.lock +.claude/ # AI and documentation files .requests/*.md .github/*.md -# Agent instructions now live outside the repo (see AGENTS.md stub). +# Agent instructions are never committed. AGENTS.md CLAUDE.md diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 0000000..53c30ab --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,198 @@ +# Migration guide + +This guide tells you how to move code from one OpenGeometry version to the next. +The newest version comes first. +It is written for people and for coding assistants alike: each section says what changed, shows the same code before and after, and lists what to replace. + +## How to use this guide + +1. Find the section for the version you are leaving. +2. Go through its "Replace this with that" table and change your code row by row. +3. Read its "Different behaviour" list: it says what now works differently, even where the names look the same. +4. For a name the table does not list, look in "Partly replaced". Anything listed under "Removed" has no replacement: take that code out, or stay on the older version. +5. Finish with its "Check your result" steps. + +## 2.0 to 2.5 + +### What changed + +- Two classes make every body: `Wire` for wires and `Solid` for solids. You pass a kind constant, its parameters and options, for example `new Solid(OG_PRIMITIVE_CUBOID, { width, height, depth })`. +- Points and vectors are plain arrays, `[x, y, z]`. There is no `Vector3`. +- Booleans change the body you call them on and return nothing. In 2.0 they returned a new body. +- Bodies move with `transform`. There is no `setPlacement`. +- `OpenGeometry.create` takes a second argument with the URL of the display worker, `tessellation-worker.js`, which your site must serve as well as `opengeometry_bg.wasm`. +- STEP export is `OpenGeometry.exportStep`. It returns a Promise. +- Every failure throws one error class, `OGError`. + +### Before and after + +2.0: + +```js +import { OpenGeometry, Cuboid, Vector3 } from 'opengeometry'; +await OpenGeometry.create({ wasmURL: '/opengeometry_bg.wasm' }); +const cuboid = new Cuboid({ center: new Vector3(0, 0.8, 0), width: 1.5, height: 1.6, depth: 1.2, color: 0x10b981 }); +cuboid.outline = true; +scene.add(cuboid); +``` + +2.5: + +```ts +import * as THREE from 'three'; +import { OpenGeometry, Solid, OG_PRIMITIVE_CUBOID } from 'opengeometry'; + +await OpenGeometry.create({ wasmURL: '/opengeometry_bg.wasm' }, { workerURL: '/tessellation-worker.js' }); + +const cuboid = new Solid( + OG_PRIMITIVE_CUBOID, + { width: 1.5, height: 1.6, depth: 1.2 }, + { appearance: { color: 0x10b981, outline: true } }, +); + +const scene = new THREE.Scene(); +scene.add(new THREE.HemisphereLight(0xffffff, 0x444444, 2), cuboid); +``` + +Both make the same box: 1.5 m wide, 1.6 m high and 1.2 m deep, standing on the ground at the origin. +The 2.5 block builds its own scene, which is why it adds a light. +Bodies are Three.js objects in both versions, so `scene.add(body)` does not change. + +### Replace this with that + +`[cx, cy, cz]` are the three numbers of the 2.0 `center`, and `[tx, ty, tz]` are those of `translation`. +`[rx, ry, rz]` are the three angles of the 2.0 `rotation`, in radians. +In the 2.5 column, a `?` after a parameter name marks it as optional, and rows that end with "(!)" also behave differently, so read "Different behaviour" before you change them. + +| 2.0 | 2.5 | +| --- | --- | +| `await OpenGeometry.create({ wasmURL })` | `await OpenGeometry.create({ wasmURL }, { workerURL })`, and serve `tessellation-worker.js` too | +| `new Vector3(x, y, z)` | `[x, y, z]` | +| `ogid` option and property | `ogId` option and property | +| `new Cuboid({ center, width, height, depth })` | `new Solid(OG_PRIMITIVE_CUBOID, { width, height, depth }, { plane: { origin: [cx, cy - height / 2, cz] } })`; the 2.0 constructor also took `translation`, `rotation` and `scale`, which you give as in the `setPlacement` rows, translation first (!) | +| `new Cylinder({ center, radius, height })` | `new Solid(OG_PRIMITIVE_CYLINDER, { radius, height }, { plane: { origin: [cx, cy - height / 2, cz] } })` (!) | +| `new AnalyticSolid({ kind: 'cylinder', radius, height, frame })` | `new Solid(OG_PRIMITIVE_CYLINDER, { radius, height }, { plane: { origin: frame.origin, normal: frame.z, xDirection: frame.x } })` | +| `new AnalyticSolid({ kind: 'cuboid', width, depth, height, frame })` | `new Solid(OG_PRIMITIVE_CUBOID, { width, height, depth }, { plane: { origin, normal: frame.z, xDirection: frame.x } })`, with `origin` the 2.0 `frame.origin` moved by `width / 2` along `frame.x` and by `depth / 2` along `frame.y`, because 2.0 put the frame origin at a corner of the base | +| `new Opening({ ... }).subtractFrom(wall)` | make the opening a cuboid `Solid`, then `wall.operate(OG_OPERATION_SUBTRACT, { tools: [opening] })`; do not add the opening solid to the scene (a 2.0 `Opening` was invisible; a 2.5 solid you add is drawn) (!) | +| `new Rectangle({ center, width, breadth })` | `new Wire(OG_PRIMITIVE_RECTANGLE, { width, breadth }, { plane: { origin: [cx, cy, cz] } })`; the 2.0 constructor also took `translation`, `rotation` and `scale`, which you give as in the `setPlacement` rows, translation first | +| `new Line({ start, end })`, `new Polyline({ points })` | `new Wire(OG_PRIMITIVE_POLYLINE, { points, closed? })`; a line is a polyline with two points; the 2.0 constructors also took `translation`, `rotation` and `scale`, which you give as in the `setPlacement` rows, translation first | +| `new Arc({ center, radius })` as a full circle | `new Wire(OG_PRIMITIVE_CIRCLE, { radius }, { plane: { origin: [cx, cy, cz] } })` | +| `new Polygon({ vertices, holes })` as a profile | closed polyline wires, used as `profile` and `holes` | +| `polygon.extrude(h)`, `Solid.extrude(...)`, `extrudeBrepFace(...)` | `new Solid(OG_OPERATION_EXTRUDE, { profile, holes?, distance: h })` | +| `new Sweep({ path, profile })` | `new Solid(OG_OPERATION_SWEEP, { profile, path })`, both wires: the path an open polyline of the 2.0 path points, the profile placed at the start of the path as "Sweeps: where the profile goes" below describes (!) | +| `setPlacement({ translation })` | `transform(OG_TRANSFORM_TRANSLATE, { offset })`, with `offset` the new `translation` minus the old one; or `transform(OG_TRANSFORM_PLACE, { origin })` with `origin` `[cx + tx, cy + ty - height / 2, cz + tz]` for a cuboid or opening, or `[cx + tx, cy + ty, cz + tz]` for a rectangle (!) | +| `setPlacement({ rotation })` | `transform(OG_TRANSFORM_ROTATE, { axis, degrees, pivot })`, one call per angle that is not zero, in this order: `[0, 0, 1]` by `rz`, then `[0, 1, 0]` by `ry`, then `[1, 0, 0]` by `rx`. Use `degrees = radians * 180 / Math.PI` and the same `pivot` each time (see Placement). This is for a body not turned yet: to change a turn, first `PLACE` it at the `origin` the translation row gives for a cuboid, opening or rectangle, or at `[tx, ty, tz]` for a polyline wire made without a `plane`, a solid extruded from such a wire, or a sweep along such a path (this resets turn and scale), then turn and scale it again. (!) | +| `setPlacement({ scale })` | `transform(OG_TRANSFORM_SCALE, { factor, pivot })`, where `factor` is the new `s` divided by the current one (`s` itself on a body not scaled yet), for a 2.0 scale `(s, s, s)`. 2.5 has no uneven scale (!) | +| `getPlacement()` gives `{ translation, rotation, scale }` | `getPlacement()` gives `{ origin, xDirection, normal, scale }` (!) | +| `setConfig({ width })`, `cuboid.width = 3` | `rebuild(OG_PRIMITIVE_CUBOID, { width, height, depth })`, with every parameter given (!) | +| `color` option, `body.color = c`, `body.outline = true`, `deflection` option, `setConfig({ deflection })` | `appearance: { color, outline, deflection }` in the options, or `body.setAppearance({ color, outline, deflection })` (!) | +| `booleanUnion(a, b)`, `a.union(b)` | `a.operate(OG_OPERATION_UNION, { tools: [b] })` (!) | +| `booleanSubtraction(a, b)`, `a.subtract([b, c])`, `executeBooleanSubtractionMany(a, [b, c])` | `a.operate(OG_OPERATION_SUBTRACT, { tools: [b, c] })` (!) | +| `booleanIntersection(a, b)`, `a.intersection(b)` | `a.operate(OG_OPERATION_INTERSECT, { tools: [b] })` (!) | +| `result.report` | `solid.getReport()?.report`, on the body you called `operate` on | +| `solid.exportStep()`, `solid.exportStep('metre')` | `await OpenGeometry.exportStep({ nodes: [solid], unit: 'metre', upAxis: 'Y' })`; for `exportStep('millimetre')` pass `unit: 'millimetre'` (!) | +| `exportBrepToStep(brepJson, configJson)` | `await OpenGeometry.exportStep({ nodes: [solid], unit: 'metre', upAxis: 'Y' })`, which keeps the numbers 2.0 wrote with its default config; the result has `report`, an object, where 2.0 had `reportJson` (!) | +| `getBrepSerialized()` | `getBrep()`, which returns the parsed object, so drop the `JSON.parse`. It describes the shape in the body's own coordinates, without its placement, which includes the `plane` it was made with. It has the fields of the 2.0 `AnalyticSolid` JSON; the older layout that `Rectangle.getBrep()` and `Sweep.getBrep()` returned is gone | +| `getModelBounds()` | `getBounds()` (!) | +| `dispose()` | `dispose()`; `OpenGeometry.reset()` frees every body at once (!) | +| catch `AnalyticGeometryError` or `WorldGraphError`, read `code` and `detail` | catch `OGError`, read `code`, `call` and `details` (!) | + +### Different behaviour + +- **Where a cuboid or cylinder sits.** In 2.0 it was centred on `center`. In 2.5 it stands on its plane origin, which is the middle of its base. That is why the table lowers the origin by `height / 2`. +- **Booleans change the target.** In 2.0 a boolean returned a new body and left the target alone. In 2.5 `operate` changes the target and returns nothing. In both versions the tools stay as they were. +- **No result object.** Because `operate` returns nothing, `const result = a.union(b)` has no 2.5 form: use `a` afterwards, and call `a.duplicate()` first if your code still needs `a` as it was. +- **Placement.** `setPlacement` set absolute values and kept the ones you left out. In 2.5, `TRANSLATE`, `ROTATE` and `SCALE` apply on top of the current placement (`SCALE` multiplies), and `PLACE` sets the origin and resets the turn and scale you leave out. 2.0 turned and scaled a `Cuboid` or `Opening` about the point `translation`, a `Rectangle` about `center + translation`, and a `Line`, `Polyline`, `Polygon` or `Sweep` about the middle of the box around its points moved by `translation`. 2.5 turns and scales a body about its own origin unless you pass `pivot`: the middle of the base for a cuboid or cylinder, the `plane` origin for a rectangle or circle, `[0, 0, 0]` for a polyline made from points. To place a cuboid where a 2.0 one sat, lower its origin by half its height. +- **`getPlacement`.** 2.5's `origin` is the body's origin (the middle of the base for a cuboid), not 2.0's `translation`. +- **Rebuild keeps the base.** A 2.0 `Cuboid` kept its centre when `setConfig` changed its height; `rebuild` keeps the base where it is, so the centre moves. `setConfig({ center })` becomes `TRANSLATE` by the change in `center`, or `PLACE`. +- **Sweeps: where the profile goes.** 2.0 moved the profile to the path's first point P, centred it there and turned it square to the first direction D. 2.5 sweeps the profile where it is, and throws when the profile's plane misses P or is not square to D. So build the profile there: a rectangle or circle wire with `{ plane: { origin: P, normal: D } }`, or, when the 2.0 profile points all had the same y, a closed polyline of those points moved so the average of their distinct points is `[0, 0, 0]`, given the same `plane`; if the points run clockwise seen from above, as a 2.0 `Rectangle`'s did, write each `[x, 0, z]` as `[x, 0, -z]`, or the profile comes out mirrored. A profile in the right plane but off-centre is swept beside the path. A closed path has no 2.5 form. +- **Sweeps: how the profile is turned.** `xDirection` sets how the profile is turned about the path: when D points along x you must give it, and for a rectangle `[0, 1, 0]` matches 2.0. In every other case a profile that is not a circle can come out turned about the path: compare with your 2.0 result and set `xDirection` to match. +- **Do not write `.position`, `.rotation` or `.scale`.** 2.0 placed some shapes that way. In 2.5 such a change is undone, with a `PlacementOverwritten` warning. +- **`Solid` is a different class.** 2.0 had `new Solid({ brep, color })`. 2.5 has `new Solid(kind, params, options)`. +- **No swapping objects.** A body redraws itself after `transform`, `rebuild` or `operate`. There is no `setConfig` and no new object to put into the scene. Geometry is made when the scene is rendered, not when the body is created. +- **STEP units and axes.** `solid.exportStep()` wrote metres. `exportBrepToStep` and `WorldGraph.exportStep` wrote your numbers unchanged under a millimetre header by default. 2.5 reads model numbers as metres: `unit: 'metre'` writes them unchanged, `unit: 'millimetre'` multiplies them by 1000, and the default is millimetres with Z up. 2.0 kept Y up, so pass `upAxis: 'Y'`. +- **Bounds.** `getModelBounds()` gave the body's own bounds as a `THREE.Box3`. `getBounds()` gives bounds in world space as `[minX, minY, minZ, maxX, maxY, maxZ]`, or `null`. +- **`dispose()`** also removes the body from its parent. +- **Colours** are numbers only, such as `0x10b981`. 2.0 `body.color` returned a `THREE.Color`. +- **Errors.** There is one class, `OGError`. Codes are now PascalCase, such as `InvalidParameter`, and `detail` is now `details`. +- **Extruded solids work in booleans.** 2.0 refused them. + +### Partly replaced + +- `WorldGraph`: nodes and hierarchy become `SystemAssembly`, `addChild` or the `parent` option; `getWorldBounds(id)` becomes `body.getBounds()`, six numbers instead of a `THREE.Box3`; `removeNode(id)` becomes `parent.removeChild(body)`, which keeps the body (pass `{ keepWorld: true }` to leave it where it is), or `body.dispose()`, which frees it and everything under it; a body with no parent has only `dispose()`; `exportStep` becomes `OpenGeometry.exportStep({ nodes })` (nodes may be bodies or assemblies). Its `clash`, `clearance`, `distance`, `projectLines`, `projectViews` and `bind` are gone. +- The pattern helpers (`AnalyticPattern`, `linearPattern`, `rectangularPattern`, `circularPattern`): make copies with `solid.instance()` and move each with `transform`; check the positions, since 2.0 drew a pattern around its own origin. `rebuild` and `operate` on an instanced body need `{ instances: 'all' }` or `makeUnique()` first. +- `AnalyticSolid` kinds: `linearExtrusion` becomes `OG_OPERATION_EXTRUDE` with closed polyline wires for the outline and each hole, each 2.0 point `[u, v]` written `[u, 0, -v]` when the 2.0 `frame` was the default (with another `frame`, also give each wire `plane: { origin: frame.origin, normal: frame.z, xDirection: frame.x }` and check which way it extrudes); `revolvedRectangle` (a tube) becomes `OG_OPERATION_EXTRUDE` with a circle wire as `profile` and a smaller circle wire with the same centre as the one hole. + +### Removed in 2.5 + +These are gone: + +- STL and IFC export. PDF export was never in the browser build. +- Projection and 2D views, offset, loft, and public triangulation (`tessellate_brep`, `tessellateFacetedBrep`). +- `Sphere`, `Wedge`, `Curve`, `EllipticalArc`, `AnalyticCurve`, arcs that are not full circles, and cylinder sectors. +- `AnalyticSolid` kinds other than cuboid, cylinder, `linearExtrusion` and `revolvedRectangle`, `AnalyticSolid.fromBrep`, and `new Solid({ brep })`. +- `WorldGraph`'s queries and projection: `clash`, `clearance`, `distance`, `projectLines`, `projectViews` and `bind`. +- The older scene manager (`add*ToScene`, `replaceBrepEntityInScene`, `refreshBrepEntityInScene`), already removed in 2.0.16. +- `shell`, `classifyPoint` and `normalAtFace`. +- The freeform geometry and editor classes, such as `FreeformGeometry` and `FreeformEditor`. +- `SpotLabel`. +- The 2D region booleans (`booleanRegions2D`, `booleanCurvedRegions2D`), `analyzeProfile`, `validateAnalyticBrep` and `WorkPlane`. +- Sweep caps (`capStart`, `capEnd`). +- `OpenGeometry.version`, `enableDebug` and the `debugMeshes` option. +- The `create*Example` builders. + +A 2.0 export this guide does not name has no direct 2.5 form; the full 2.5 surface is `index.d.ts` in the package. + +### Check your result + +1. Open `node_modules/opengeometry/package.json`. Its `version` should start with `2.5`. +2. Run your type check, for example `npx tsc --noEmit`. Imports that no longer exist, such as `Cuboid` or `Vector3`, fail here. +3. Run your app. Check that bodies sit and face the way they did before, cuboids, cylinders and sweeps above all, and that STEP files have the units and up axis you expect. + +## Adding an entry + +When a change to OpenGeometry breaks code written for the previous version, add a section to this guide in the same change. + +- Put the new section at the top, just under "How to use this guide", and name it after the two versions, such as "2.5 to 2.6". If the next version number is not decided when you make the change, name the section "Unreleased" and rename it at release. +- Say what changed, show the same code before and after, give the replace table, list what was removed, and say how to check the result. Add "Different behaviour" when something now works differently, and "Partly replaced" when a name has only a partial new form. +- Code for the current version goes in a code block labelled `ts` that starts at the left margin, ends with a bare closing fence at the left margin, and is a complete module, imports included. The checks type-check every such block against the built package. The checks fail on a `ts`, `typescript` or `tsx` block written any other way, and when the guide has no `ts` block at all. +- Code for an older version goes in a code block labelled `js`, which is not checked. +- When a later change breaks a `ts` block in an older section, change that block's label to `js`. + +Copy this outline: + +```md +## to + +### What changed + +- One short line per change. + +### Before and after + +: a code block labelled js with the old code. + +: a code block labelled ts with the same code for the new version. + +### Replace this with that + +| | | +| --- | --- | +| old call | new call | + +### Different behaviour + +- What now works differently. + +### Partly replaced + +- A name with only a partial new form, and what to use instead. + +### Removed in + +- What has no replacement. + +### Check your result + +1. How to tell the change worked. +``` diff --git a/README.md b/README.md index 24bc3d6..40f9c09 100644 --- a/README.md +++ b/README.md @@ -24,17 +24,88 @@ --- -> **Actively maintained and growing.** We're building OpenGeometry in the open. APIs, examples, and package structure are evolving, we are actively improving and expanding the project. Star the repo to follow along. If you have questions or want to get involved, join the [Discord](https://discord.com/invite/9wJpbfgGGA) or check out the [issues](https://github.com/OpenGeometry-io/OpenGeometry/issues) +> **Actively maintained and growing.** We're building OpenGeometry in the open. APIs, examples, and package structure are evolving, and we are actively improving and expanding the project. Star the repo to follow along. If you have questions or want to get involved, join the [Discord](https://discord.com/invite/9wJpbfgGGA) or check out the [issues](https://github.com/OpenGeometry-io/OpenGeometry/issues). + +> **Version note.** This README describes OpenGeometry 2.5. Check which line you have installed with `npm ls opengeometry`: the 2.0 line has a different API. The hosted documentation still describes 2.0. --- ## What is OpenGeometry? -OpenGeometry is an **open-source, browser-native CAD kernel**. The geometry engine is written in **Rust**, compiled to **WebAssembly**, and paired with a **Three.js-friendly TypeScript layer** so you can build real CAD tools that run in the browser. +OpenGeometry is an **open-source CAD kernel for the browser**. It models **B-rep solids**, runs **boolean operations** (union, subtract, intersect), builds solids by **extrude and sweep**, and writes **STEP** files. The kernel is written in **Rust** and compiled to **WebAssembly**, and a **TypeScript SDK for Three.js** puts every body straight into your scene. OpenGeometry is best suited for **browser-based CAD, configurators, and geometry-heavy web tools**. Whether you're building a parametric modeler, a solid modeling workflow, a geometry viewer, or a custom Three.js modeling tool, OpenGeometry gives you deterministic, kernel-backed primitives and operations without leaving JavaScript. -It is the geometry engine layer, not a full CAD application. OpenPlans is a downstream application/toolkit built on top of OpenGeometry for AEC workflows. In this repository, OpenGeometry is the primary SDK and engine. +It is the geometry engine layer, not a full CAD application. Floorkit is a downstream application/toolkit built on top of OpenGeometry for AEC workflows. In this repository, OpenGeometry is the primary SDK and engine. + +## Features + +| Area | What 2.5 has | +| --- | --- | +| **Primitives** | Wires: rectangle, circle, polyline (open or closed). Solids: cuboid, cylinder. Each can be placed on any plane. | +| **Operations** | Extrude a closed wire into a solid, with holes. Sweep a profile along a path. Union, subtract and intersect, with one to a hundred tool solids in one call. | +| **Parametric edits** | `rebuild` gives an existing body new parameters in place. | +| **Assemblies** | Assemblies with parent and child placement; translate, rotate, scale and place; independent copies and shared instances. | +| **History** | Marks with rollback and release; transactions, with a dry-run option. | +| **Display for Three.js** | Every body is a `THREE.Group`. Level of detail that follows the camera, tessellation in a web worker or inline, picking down to face and edge, outlines, colour and opacity. | +| **Queries** | Bounds, placements, the B-rep of a body, and the report of its last boolean. | +| **Export** | STEP, in millimetres or metres, with Y or Z up, together with an export report. | + +Not in 2.5, though 2.0 had them: IFC and STL export; projection; offset; loft; triangulation as a public call; arcs, curves, spheres and wedges; and the 2.0 classes such as `Cuboid`, `Polygon`, `Opening` and `Vector3`. + +## Installation + +```bash +npm install opengeometry three +``` + +- `three` is a peer dependency: versions `>=0.168.0 <0.185.0` are supported. +- The package is ES modules only, and its TypeScript types are included. +- The package ships `opengeometry_bg.wasm` (the kernel) and `tessellation-worker.js` (the display worker), and your page must be able to fetch both. With no arguments, `OpenGeometry.create()` looks for them next to the package's own `index.js`. If your bundler moves or rewrites `index.js` without the two files, that lookup fails, so the dependable way is the one the quick start uses: copy both files from `node_modules/opengeometry/` into the folder your site serves at its root (in Vite, the `public/` folder by default) and pass their URLs to `create`. + +## Quick start + +```ts +import * as THREE from 'three'; +import { + OpenGeometry, Solid, Wire, + OG_OPERATION_EXTRUDE, OG_OPERATION_SUBTRACT, OG_PRIMITIVE_CUBOID, OG_PRIMITIVE_RECTANGLE, OG_TRANSFORM_TRANSLATE, +} from 'opengeometry'; + +await OpenGeometry.create({ wasmURL: '/opengeometry_bg.wasm' }, { workerURL: '/tessellation-worker.js' }); + +const profile = new Wire(OG_PRIMITIVE_RECTANGLE, { width: 6, breadth: 0.2 }); +const wall = new Solid(OG_OPERATION_EXTRUDE, { profile, distance: 3 }); +const door = new Solid(OG_PRIMITIVE_CUBOID, { width: 0.9, height: 2.1, depth: 0.4 }); +door.transform(OG_TRANSFORM_TRANSLATE, { offset: [2, 0, 0] }); +wall.operate(OG_OPERATION_SUBTRACT, { tools: [door] }); + +const scene = new THREE.Scene(); +scene.add(new THREE.HemisphereLight(0xffffff, 0x444444, 2), wall); +const camera = new THREE.PerspectiveCamera(50, innerWidth / innerHeight, 0.1, 100); +camera.position.set(6, 5, 8); +camera.lookAt(0, 1.5, 0); +const renderer = new THREE.WebGLRenderer({ antialias: true }); +renderer.setSize(innerWidth, innerHeight); +document.body.append(renderer.domElement); +renderer.setAnimationLoop(() => renderer.render(scene, camera)); + +const { text, report } = await OpenGeometry.exportStep({ nodes: [wall], unit: 'millimetre', upAxis: 'Z' }); +``` + +This loads the kernel, makes a 6 m by 0.2 m rectangle wire on the ground plane, extrudes it 3 m up into a wall, and cuts a door opening through the wall with a cuboid placed at x = 2. The wall is a `THREE.Group`, so it goes into the scene like any other object and is drawn on every frame. The last line writes the wall as a STEP file: `text` is the file and `report` counts what was written. + +The two URLs assume both files were copied to the site root, as described under Installation. The block uses top-level `await`, so it must run as a module. If your build rejects top-level `await`, raise its target (the examples set Vite's `build.target` to `'esnext'`) or move the code after the imports into an `async` function and call it. + +Runnable examples live in [`main/opengeometry-three/examples-vite/`](./main/opengeometry-three/examples-vite/): a showcase, a sweep, booleans, an opening, a cuboid, a cylinder and STEP export. From a clone, run `npm --prefix main ci`, `npm run build` and then `npm run dev-example`. + +## Demos + +See OpenGeometry in action — interactive, browser-based demos showcasing the kernel's capabilities: + +**[demos.opengeometry.io](https://demos.opengeometry.io?utm_source=github)** + +Demos include primitives rendering, shape generation, sweep operations, boolean operations, file exports, and more. All running client-side via WebAssembly. ## When to use OpenGeometry @@ -42,20 +113,20 @@ Use OpenGeometry when you need: - browser-based parametric modeling with Rust + WebAssembly performance - cutout subtraction and other solid boolean workflows -- polygon extrusion into solids for CAD or AEC modeling -- IFC, STEP, STL, and PDF-style export/projection in web apps +- profile extrusion into solids for CAD or AEC modeling +- STEP export in web apps - a Three.js-friendly CAD kernel instead of ad hoc mesh math - a deterministic geometry engine behind AI-assisted CAD or design workflows -## Why it works well for AI-powered CAD apps +### Why it works well for AI-powered CAD apps -OpenGeometry is a good fit for **AI-assisted CAD apps** because the kernel layer stays explicit and deterministic. An AI copilot or agent can suggest modeling steps, generate profiles, or orchestrate edits, while OpenGeometry executes the actual geometry operations, booleans, projections, and exports in a predictable browser runtime. +OpenGeometry is a good fit for **AI-assisted CAD apps** because the kernel layer stays explicit and deterministic. An AI assistant or agent can suggest modeling steps, generate profiles, or orchestrate edits, while OpenGeometry executes the actual geometry operations, booleans, and exports in a predictable browser runtime. Good examples include: -- AI copilots that translate user intent into concrete modeling operations +- AI assistants that translate user intent into concrete modeling operations - prompt-to-geometry or agent-driven editing flows inside browser CAD tools -- AI first design interfaces that still need reliable extrusion, boolean, projection, and export workflows +- AI-first design interfaces that still need reliable extrusion, boolean, and export workflows ## Good fit / Not the right fit @@ -63,7 +134,7 @@ Good examples include: - browser CAD, mechanical design, and geometry-heavy web applications - Three.js-based modeling tools that need a real kernel behind them -- AI first CAD frontends that need deterministic geometry execution in the browser +- AI-first CAD frontends that need deterministic geometry execution in the browser **Not the default fit** @@ -71,127 +142,122 @@ Good examples include: - non-browser runtimes with no WebAssembly or browser delivery story - pure visualization-only apps where raw Three.js is enough and kernel-backed modeling is unnecessary -## AI and Coding Agent Entrypoints - -If you are using ChatGPT, Claude, Gemini, Copilot, or other coding agents on this repository, start here: - -- [AGENTS.md](./AGENTS.md) - single source of truth for all coding agents (architecture, commands, gotchas, behavioral rules) -- [README.md](./README.md) - product overview, positioning, and quick start -- [Quickstart](https://docs.opengeometry.io/OpenGeometry/quickstart) -- [Installation](https://docs.opengeometry.io/OpenGeometry/installation) -- [Boolean operations](https://docs.opengeometry.io/OpenGeometry/concepts/booleans) -- [Extrude](https://docs.opengeometry.io/OpenGeometry/api/operations/extrude) - -### What you can do today - -| Category | Capabilities | -| --- | --- | -| **Primitives** | Lines, arcs, curves, polylines, rectangles | -| **Shapes** | Polygons, solids, cuboids, cylinders, spheres, wedges, sweeps, openings | -| **Operations** | Triangulation, extrusion, sweep, offset, boolean operations | -| **Exports** | STL, STEP, IFC, PDF projection (PDF is currently native/Node-only — browser PDF is on the roadmap) | -| **Integration** | Three.js scene management, WebAssembly-powered performance | - -## Demos - -See OpenGeometry in action — interactive, browser-based demos showcasing the kernel's capabilities: - -**[demos.opengeometry.io](https://demos.opengeometry.io)** +## Who is this for? -Demos include primitives rendering, shape generation, sweep operations, boolean operations, file exports, and more. All running client-side via WebAssembly. +- Teams building **browser-based CAD and geometry tools** +- Developers evaluating **WebAssembly-powered 3D** for the web +- Contributors interested in the **Rust → WASM geometry pipeline** +- Anyone exploring **open-source CAD kernel internals** -## Quick Start +If you just want a quick look, start with the [hosted demos](https://demos.opengeometry.io?utm_source=github) or the [examples](./main/opengeometry-three/examples-vite/). -Install from npm: +## Using OpenGeometry with AI coding assistants -```bash -npm install opengeometry -``` +This section is a compact reference for writing OpenGeometry 2.5 code, by hand or with a coding assistant. -A practical browser deployment looks like this: install `opengeometry` from npm, serve `opengeometry_bg.wasm` from your app, then initialize the runtime once with `OpenGeometry.create({ wasmURL })` before creating `Vector3` values or kernel-backed wrappers. +**Package.** `opengeometry` on npm. ES modules only, types included, peer dependency `three` `>=0.168.0 <0.185.0`. -```ts -import { Cuboid, OpenGeometry, Vector3 } from "opengeometry"; - -await OpenGeometry.create({ - wasmURL: new URL( - "node_modules/opengeometry/opengeometry_bg.wasm", - import.meta.url - ).href, -}); - -// Initialize once before constructing Vector3 or any wrapper. -const cuboid = new Cuboid({ - center: new Vector3(0, 0, 0), - width: 2, - height: 1, - depth: 1, - color: 0x33aa66, -}); - -// Placement is strict: scale must be positive + uniform. -cuboid.setPlacement({ - translation: new Vector3(1, 0, 0), - rotation: new Vector3(0, 0.25, 0), - scale: new Vector3(1.25, 1.25, 1.25), -}); -``` +**Which line is installed.** The 2.5 entry exports `OpenGeometry.create`, `Solid` and `Wire`. Code that uses `Cuboid`, `Vector3` or `setPlacement` is written for 2.0 and does not work with 2.5. The `version` in `node_modules/opengeometry/package.json` settles it. -Scenegraph behavior is snapshot-based: `add*ToScene` captures geometry at insert time. If you change placement/config later, push updates explicitly via `replaceBrepEntityInScene` or `refreshBrepEntityInScene`. +**The whole public surface.** The entry exports `OpenGeometry`, `Solid`, `Wire`, `SystemAssembly`, `OGMark`, `OGError` and the `OG_*` constants. Every point and vector is `[x, y, z]`. Model units are metres, and Y is up. -For a complete walkthrough, see the [Quick Start guide](https://docs.opengeometry.io/OpenGeometry/quickstart) or clone the [quickstart-js](https://github.com/OpenGeometry-io/quickstart-js) repo. +| Export | Call and parameters | +| --- | --- | +| `OpenGeometry.create` | `await OpenGeometry.create(input?, options?)`. `input`: `{ wasmURL?, wasmModule? }`. `options`: `{ workerURL?, tessellation?: 'worker' \| 'inline' }`. | +| `Wire` | `new Wire(kind, params, options?)`. `OG_PRIMITIVE_RECTANGLE` `{ width, breadth }`, `OG_PRIMITIVE_CIRCLE` `{ radius }`, `OG_PRIMITIVE_POLYLINE` `{ points, closed? }`. | +| `Solid` | `new Solid(kind, params, options?)`. `OG_PRIMITIVE_CUBOID` `{ width, height, depth }`, `OG_PRIMITIVE_CYLINDER` `{ radius, height }`, `OG_OPERATION_EXTRUDE` `{ profile, holes?, distance }`, `OG_OPERATION_SWEEP` `{ profile, path }`; `profile`, `holes` and `path` are wires. | +| Body options | `{ ogId?, parent?, appearance?, plane? }`. `plane` is `{ origin?, normal?, xDirection? }` and applies to primitives only; by default it is the ground plane through the origin, normal `[0, 1, 0]`, x direction `[1, 0, 0]`. | +| Booleans | `solid.operate(kind, { tools, instances? })` with `OG_OPERATION_SUBTRACT`, `OG_OPERATION_UNION` or `OG_OPERATION_INTERSECT`; `tools` is one to a hundred solids. | +| Transforms | `node.transform(kind, params)` on a body or an assembly: `OG_TRANSFORM_TRANSLATE` `{ offset }`, `OG_TRANSFORM_ROTATE` `{ axis, degrees, pivot? }`, `OG_TRANSFORM_SCALE` `{ factor, pivot? }`, `OG_TRANSFORM_PLACE` `{ origin?, xDirection?, normal?, scale? }`. `getPlacement()` and `getWorldPlacement()` return `{ origin, xDirection, normal, scale }`. | +| Edits | `body.rebuild(kind, params, { instances? })` with the kinds and parameters above; a wire stays a wire and a solid a solid. | +| Copies | `body.duplicate({ ogId?, parent? })` makes an independent copy; `body.instance({ ogId?, parent? })` shares the shape. `getInstanceCount()`, `makeUnique()`. | +| `SystemAssembly` | `new SystemAssembly({ ogId?, parent? })`. `addChild(children, { keepWorld? })`, `removeChild(child, { keepWorld? })`, `getChildren()`, `getParent()`, `getBounds()`, `transform`, `dispose()`. Bodies have the same tree methods. | +| Queries | `getBounds()` (`[minX, minY, minZ, maxX, maxY, maxZ]`, or `null`), `getBrep()`, `solid.getReport()` (the last boolean's report, or `null`). | +| Appearance | `appearance` in the options, or `body.setAppearance({ color?, opacity?, outline?, pickOutline?, deflection? })`. | +| Display | `OpenGeometry.settled({ deflection? })` resolves to `{ failed }`; `OpenGeometry.flush({ geometry?: 'sync' })`; `OpenGeometry.setDisplayDeflection(world?)`; `OpenGeometry.setCameraMotion(moving)`. | +| Picking | `OpenGeometry.resolveHit(intersection)` takes a Three.js raycast hit and returns `{ ogId, shapeRevision, faceId?, edgeId? }` or `undefined`. | +| `OGMark` | `const mark = OpenGeometry.mark()`, then `mark.rollback()` and `mark.release()`. `OpenGeometry.markStats()` returns `{ liveMarks, retainedRevisions }`. `OpenGeometry.transaction(fn, { dryRun? })` runs a synchronous `fn` and rolls back if it throws. | +| Events | `OpenGeometry.on(event, handler)` with `'geometry'`, `'warning'`, `'error'` or `'fatal'`; it returns a function that unsubscribes. | +| STEP | `await OpenGeometry.exportStep({ nodes, unit?, upAxis?, name?, timestamp? })` returns `{ text, report }`. `nodes`: bodies, assemblies or `ogId` strings. `unit`: `'millimetre'` (default) or `'metre'`; `upAxis`: `'Z'` (default) or `'Y'`. | +| `OGError` | Failures throw an `OGError` with `code` (for example `InvalidParameter`, `SharedShape`, `Disposed`), `call`, `message` and `details`. | +| Reset | `OpenGeometry.reset()` frees every body and leaves an empty document. | +| Typed parameters | `OG_PRIMITIVE_PARAMS_(params)`, `OG_OPERATION_PARAMS_EXTRUDE(params)` and `OG_OPERATION_PARAMS_SWEEP(params)` return their argument unchanged; they only give the object a type. | + +**Rules that are easy to get wrong.** + +- Await `OpenGeometry.create` once before creating any body. +- `workerURL` belongs to the second argument of `create`, not the first. +- Node has no `Worker` and cannot fetch a `file:` URL. Pass the compiled module instead; tessellation then runs inline: + + ```js + import { readFileSync } from 'node:fs'; + import { OpenGeometry } from 'opengeometry'; + + const wasm = readFileSync(new URL(import.meta.resolve('opengeometry/opengeometry_bg.wasm'))); + await OpenGeometry.create({ wasmModule: new WebAssembly.Module(wasm) }); + ``` + +- `operate` changes the target's shape once. The tools stay separate, live solids: moving or disposing a tool later does not change the target, and a tool is drawn only if you add it to the scene. +- `rebuild` replaces the shape, so an earlier `operate` result is gone; call `operate` again after it. +- A shape shared by instances cannot be edited one node at a time: pass `{ instances: 'all' }` or call `makeUnique()` first, or the call throws `SharedShape`. +- Move bodies with `transform`. Setting `body.position`, `rotation` or `scale` is reverted with a `PlacementOverwritten` warning. +- Add bodies to the Three.js scene yourself; a `SystemAssembly` is not a Three.js object. +- Surfaces use `MeshStandardMaterial`, so the scene needs a light. +- Rendering is what asks for geometry. Render in a loop, or render once, `await OpenGeometry.settled()`, and render again. `settled` returns at once if nothing has been rendered. +- STEP export defaults to millimetres and Z up. A wire cannot be exported on its own: the selection must contain a solid. +- Call `dispose()` on bodies and assemblies you no longer need. +- Catch `OGError` and branch on `code`. After a `'fatal'` event the runtime refuses every call until `OpenGeometry.reset()`. + +**What does not exist.** IFC, STL and PDF export, projection, offset, loft, public triangulation, arcs, curves, spheres, wedges, and the 2.0 classes. Do not call them. + +**Where the truth is.** `index.d.ts` in the installed package lists every export and its types. The examples in [`main/opengeometry-three/examples-vite/`](./main/opengeometry-three/examples-vite/) are working 2.5 code, except that they import the built package by path (`../../../dist/index.js`) where an app imports from `'opengeometry'`. ## Documentation -Full API reference, guides, and concepts are available at: - -**[docs.opengeometry.io](https://docs.opengeometry.io)** - -Key pages: -- [Installation](https://docs.opengeometry.io/OpenGeometry/installation) -- [Quick Start](https://docs.opengeometry.io/OpenGeometry/quickstart) -- [Architecture & Concepts](https://docs.opengeometry.io/OpenGeometry/concepts/architecture) -- [API Reference](https://docs.opengeometry.io/OpenGeometry/api/) +The hosted documentation at **[docs.opengeometry.io](https://docs.opengeometry.io?utm_source=github)** still describes the 2.0 API. For 2.5, use this README, the types in the package and the examples. To move code written for 2.0 to 2.5, follow [MIGRATION.md](./MIGRATION.md). -## Repository Structure +## Repository structure -``` -main/opengeometry Rust core → WebAssembly -main/opengeometry-three Three.js integration layer -main/opengeometry-webgl WebGL-oriented package (WIP) -main/opengeometry-babylon Babylon.js-oriented package (WIP) -docs/ Documentation source (Mintlify) -``` +| Path | What it holds | +| --- | --- | +| [`main/opengeometry/`](./main/opengeometry/) | The Rust kernel, built to WebAssembly | +| [`main/opengeometry-three/`](./main/opengeometry-three/) | The Three.js SDK, published as `opengeometry` | +| [`main/scripts/`](./main/scripts/) | Build, check, benchmark and snapshot scripts | +| [`docs/`](./docs/) | The 2.0 documentation source, not yet rewritten for 2.5 | -## Building from Source +## Building from source -**Prerequisites:** Node.js, npm, Rust 1.89.0+ with `wasm32-unknown-unknown`, `wasm-pack` +**Prerequisites:** Node.js 26 and npm, Rust 1.88.0 with the `wasm32-unknown-unknown` target (pinned in `main/rust-toolchain.toml`), and `wasm-pack`. ```bash -# Install dependencies -npm install +npm --prefix main ci +npm run build +npm run dev-example +npm test +npm run check +npm run check:full +``` -# Build Rust core → WebAssembly -npm run build-core +- `npm --prefix main ci` installs the dependencies. +- `npm run build` builds the kernel to WebAssembly and the SDK, and writes the package to `main/dist/`. +- `npm run dev-example` serves the example pages on a local Vite server; build first. +- `npm test` runs the kernel's Rust tests. +- `npm run check` runs the gate: formatting, lints, type checks, source rules, the build, the Rust, WebAssembly and Node tests, and the performance budgets. +- `npm run check:full` runs the same gate plus the browser tests on three 0.168 and 0.184. The browser tests need Playwright's Chromium, installed with `npx playwright install chromium` in `main/` (on Linux, add `--with-deps` to install the system libraries, as CI does). -# Build everything (core + Three.js + WASM copy) -npm run build +More in [developer.md](./developer.md). -# Run the Three.js example app locally -npm --prefix main/opengeometry-three run dev-example-three +## Built with OpenGeometry -# Run tests -npm test -``` +If your project uses OpenGeometry, please add the badge to your README and link back to us. It helps other people find the project. -## Who is this for? +[![Built with OpenGeometry](https://img.shields.io/badge/built%20with-OpenGeometry-4460FF?style=flat-square)](https://opengeometry.io?utm_source=built-with-badge) -- Teams building **browser-based CAD and geometry tools** -- Developers evaluating **WebAssembly-powered 3D** for the web -- Contributors interested in the **Rust → WASM geometry pipeline** -- Anyone exploring **open-source CAD kernel internals** +```md +[![Built with OpenGeometry](https://img.shields.io/badge/built%20with-OpenGeometry-4460FF?style=flat-square)](https://opengeometry.io?utm_source=built-with-badge) +``` -If you just want a quick look, start with the [hosted demos](https://demos.opengeometry.io) or the [quickstart repo](https://github.com/OpenGeometry-io/quickstart-js). +Then tell us about it on [Discord](https://discord.com/invite/9wJpbfgGGA): we like to see what you build. ## Community @@ -205,13 +271,11 @@ We'd love to have you involved — whether you're using OpenGeometry, building o ## Contributing -OpenGeometry is open source under the [MPL-2.0 license](./LICENSE.md). Contributions are welcome — check the [issues](https://github.com/OpenGeometry-io/OpenGeometry/issues) for good starting points or open a discussion on [Discord](https://discord.com/invite/9wJpbfgGGA). +Contributions are welcome — check the [issues](https://github.com/OpenGeometry-io/OpenGeometry/issues) for good starting points or open a discussion on [Discord](https://discord.com/invite/9wJpbfgGGA). [developer.md](./developer.md) covers building, checks and releases. -## AI Agent Docs Policy +## License -- If you're an AI agent, start with [AGENTS.md](./AGENTS.md) for architecture, commands, and behavioral rules. -- Agents should not create planning, handoff, or runbook files during a task. The - conversation, the diff, and the commit message are the handoff. +OpenGeometry is released under the Mozilla Public License 2.0 (MPL-2.0). See [LICENSE.md](./LICENSE.md). --- diff --git a/developer.md b/developer.md index 27803f6..49e7e93 100644 --- a/developer.md +++ b/developer.md @@ -1,60 +1,139 @@ # Developer Documentation -Contributor-facing notes. For agent guidance see [AGENTS.md](./AGENTS.md). For end-user -docs see [README.md](./README.md) and [docs.opengeometry.io](https://docs.opengeometry.io). +Contributor-facing notes. For end-user docs see [README.md](./README.md); +[docs.opengeometry.io](https://docs.opengeometry.io) still describes the 2.0 API. ## Prerequisites -- Node.js (CI uses 18; any LTS is fine locally) -- Rust toolchain (stable) -- `wasm-pack` — `brew install wasm-pack` on macOS, or `cargo install wasm-pack` +- Node.js 26 (what CI uses) +- Rust toolchain 1.88.0 +- `wasm-pack` (CI uses 0.13.1) — `brew install wasm-pack` on macOS, or + `cargo install wasm-pack` ## Project layout - `main/opengeometry/` — Rust core compiled to WebAssembly - `main/opengeometry-three/` — Three.js wrapper (the published TypeScript SDK) -- `main/opengeometry-{webgl,babylon,ios,export-io,export-schema}/` — empty scaffolds -- `dist/` — generated NPM bundle (do not edit) +- `main/dist/` — generated npm bundle (do not edit) ## Local build ```bash -npm install -npm run build # Full pipeline: Rust → WASM → TS bundle → dist/ -npm test # Cargo unit + integration tests (no TypeScript tests yet) +npm --prefix main ci +npm run build # Full pipeline: Rust → WASM → TS bundle → main/dist/ +npm test # Cargo unit + integration tests ``` -`npm run build` runs `build-core` (wasm-pack + cargo release), `build-three` -(clean `dist/`, then Rollup), and `prepare-dist` (copy WASM and package metadata) in -order. The clean step removes declarations for deleted sources before Rollup emits -new ones. Running the stages out of order produces stale `pkg/` and bundle -mismatches — see `.claude/skills/wasm-build-flow.md` if you hit that. +`npm run build` runs `build-core` (wasm-pack only), then `rollup -c`, then +`node scripts/build/prepare-dist.mjs` (copy WASM, package metadata, the README and the +licence) in order, all inside `main/`. There is no clean step. Running the stages out of +order produces stale `pkg/` and bundle mismatches. ## Running the example app ```bash -npm --prefix main/opengeometry-three run dev-example-three # Vite dev server -npm --prefix main/opengeometry-three run build-example-three # Static build -npm --prefix main/opengeometry-three run preview-example-three # Preview the build +npm --prefix main run dev-example # Vite dev server +npm --prefix main run build-example # Static build ``` +Run `npm run build` first, because the examples import `main/dist/index.js` by path and +are served with `main/dist/` as their public folder. + The example catalog lives at `main/opengeometry-three/examples-vite/`. Use it (rather than copying the build into a sibling repo) for local validation. +## Verification and CI + +Both check commands work from the repository root and from the `main/` folder, and +write their logs to `main/.check/`. + +- `npm run check` runs the 18-step gate: formatting, clippy and tests of the kernel + and `test-support` crates, the WebAssembly tests, the build, the worker-bundle check, + lint, type checks, source rules, import cycles, duplicates, the example build, and the + binding, Node and performance tests. +- `npm run check:full` runs the same 18 steps plus the Playwright browser tests on + three 0.168 and 0.184 (20 steps). It needs Playwright's Chromium: + `npx playwright install chromium` in `main/` (on Linux with `--with-deps`, as CI does). + +The root `README.md` is part of the gate. In the Node tests, +`main/scripts/build/readme-code.test.mjs` type-checks every `ts` block of the README +against `main/dist/`, fails on a `ts`, `typescript` or `tsx` block it does not read, and +checks that `main/opengeometry-three/tests/browser/pages/quick-start.ts` holds the README's +`OpenGeometry.create(` line. The browser suite runs that page, which is a hand copy of the +README's quick start, so a change to the quick start changes `pages/quick-start.ts` too. +The same test type-checks the `ts` blocks of `MIGRATION.md` and fails on a fence it does +not read there; the quick-start check covers the README only. The build ships the README, +with its relative links made absolute, and `LICENSE.md` in `main/dist/`. `MIGRATION.md` +is not shipped: the package README links to it on GitHub's `main` branch. + +`.github/workflows/verify.yml` runs on every push and pull request that changes `main/**`, +`README.md`, `MIGRATION.md`, `LICENSE.md` or the workflow file, on Ubuntu and macOS. It +runs `npm run check`, then the two browser runs (`node scripts/check/verify.mjs --full +--only browser:three-168` and `--only browser:three-184`), then a fixed two-second guard +on one kernel boolean (`cargo run --release --example budgets` in `main/opengeometry/`), +which compares with no recorded result. It uploads the check logs and, from Ubuntu, the +STEP exports. It can also be started by hand. + +Three checks compare with numbers recorded on one machine, so they run on a developer's +machine and not on CI (where `CI` is `true`). Nothing is recorded on CI. + +- The kernel golden test compares the hash of every scene with the golden file of its + target; the one recorded file is + `main/opengeometry/tests/fixtures/golden/aarch64-apple-darwin.json`. On CI, and on a + target with no golden file, it still builds every scene and compares no hash. Its other + checks apply there too, except that on CI a boolean matrix row that differs from its + stored result is not a failure. `TARGETS.md` in that folder says how to record. +- The timing budgets compare the performance medians and the storey timings with the + block of `timings` in `main/scripts/bench/performance-baseline.json` that is named after + the machine, such as `darwin-arm64`. On CI the timings are measured and printed, not + checked, and a machine with no block is not checked either. The size budgets in the + same file are checked everywhere. To record a machine, copy into its block the + `timings` that `npm run test:performance` prints, and the `renderMs`, + `transformFlushMs` and `stepMs` of the "Storey performance baseline:" line of the + browser run as `storeyFirstRenderMs`, `storeyTransformFlushMs` and `storeyStepMs`, and + set `measuredOn` to the date. A block needs the three storey keys. +- The kernel tests of the `cases` and `step_oracle` test binaries that compare a body + built by the kernel or returned by a boolean, a tessellation, a STEP text or a STEP + report digit for digit with one stored under `main/opengeometry/tests/fixtures/cases/` + make that comparison off CI only. On CI they still run every case and keep their other + checks, such as validity, volumes, handler lists and stored errors. A stored body that + is only read and written back is still compared digit for digit everywhere, and + `main/opengeometry/tests/wasm_core.rs` compares, on every machine, the stored + tessellations within four ULP and the stored metre STEP texts entity for entity with + their numbers within four ULP. + +The kernel tests also read `main/opengeometry/tests/fixtures/cases/`, which holds test +inputs and stored expected results. The BRep bodies are both: each is the stored result +of a builder or of one of five booleans, and an input of the STEP and round-trip tests; +the twelve builder bodies are also inputs of the tessellation tests. The boolean and +batch matrix rows hold their inputs with their stored +result and handlers, and each validity body has its stored verdict. The other stored +results are tessellations, STEP texts, reports and errors, and handler lists. They were +first recorded from the 2.0 kernel by a tool that no longer exists, so no command +regenerates them. A change that moves one edits the stored file by hand in the same +commit and says why. The golden file is the only fixture with a record command. + ## Release process -1. Bump `version` in both `package.json` and `main/opengeometry/Cargo.toml` so they - match. -2. Run the full build + test locally: +1. Bump `version` in `main/package.json`, `main/package-lock.json`, + `main/opengeometry/Cargo.toml`, `main/opengeometry/Cargo.lock` and + `main/opengeometry/test-support/Cargo.lock` so they match (CI fetches with `--locked`). +2. If the release changes how existing code must be written, check that `MIGRATION.md` + has its entry (its "Adding an entry" section asks for it in the change that broke the + code) and rename an "Unreleased" section after the two versions, such as "2.5 to 2.6". +3. Run both gates with `CI` unset on a machine that has a golden file for its target and + a `timings` block, where the golden comparison, the comparisons with stored results and + the timing budgets run: ```bash - npm run build - npm test + npm run check + npm run check:full ``` -3. Push to `main`. The GitHub Action at `.github/workflows/release.yml` detects the - version bump, rebuilds, runs tests (now required to pass), and publishes to NPM if - the version is not already on the registry. -4. The action also creates a GitHub release tagged `v`. +4. Merge to the `main` branch, then start the GitHub Action at + `.github/workflows/release.yml` by hand on the `main` branch; a push does not publish. + It rebuilds, runs `npm run check` (required to pass), and publishes `main/dist/` to npm + if the version is not already on the registry. +5. The action also creates a GitHub release tagged `v`. If the publish step fails for an environmental reason but the version was already -bumped, re-running the workflow will re-attempt publish (the action checks NPM and only +bumped, re-running the workflow will re-attempt publish (the action checks npm and only publishes if the version is missing). diff --git a/docs/AGENTS.md b/docs/AGENTS.md deleted file mode 100644 index cebd973..0000000 --- a/docs/AGENTS.md +++ /dev/null @@ -1,33 +0,0 @@ -> **First-time setup**: Customize this file for your project. Prompt the user to customize this file for their project. -> For Mintlify product knowledge (components, configuration, writing standards), -> install the Mintlify skill: `npx skills add https://mintlify.com/docs` - -# Documentation project instructions - -## About this project - -- This is a documentation site built on [Mintlify](https://mintlify.com) -- Pages are MDX files with YAML frontmatter -- Configuration lives in `docs.json` -- Run `mint dev` to preview locally -- Run `mint broken-links` to check links - -## Terminology - -{/* Add product-specific terms and preferred usage */} -{/* Example: Use "workspace" not "project", "member" not "user" */} - -## Style preferences - -{/* Add any project-specific style rules below */} - -- Use active voice and second person ("you") -- Keep sentences concise — one idea per sentence -- Use sentence case for headings -- Bold for UI elements: Click **Settings** -- Code formatting for file names, commands, paths, and code references - -## Content boundaries - -{/* Define what should and shouldn't be documented */} -{/* Example: Don't document internal admin features */} diff --git a/docs/README.md b/docs/README.md index 4552fbc..055c983 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,18 +12,6 @@ Click the green **Use this template** button at the top of this repo to copy the **[Follow the full quickstart guide](https://starter.mintlify.com/quickstart)** -## AI-assisted writing - -Set up your AI coding tool to work with Mintlify: - -```bash -npx skills add https://mintlify.com/docs -``` - -This command installs Mintlify's documentation skill for your configured AI tools like Claude Code, Cursor, Windsurf, and others. The skill includes component reference, writing standards, and workflow guidance. - -See the [AI tools guides](/ai-tools) for tool-specific setup. - ## Development Install the [Mintlify CLI](https://www.npmjs.com/package/mint) to preview your documentation changes locally. To install, use the following command: diff --git a/docs/api/export/ifc.mdx b/docs/api/export/ifc.mdx index bf042f4..b46c881 100644 --- a/docs/api/export/ifc.mdx +++ b/docs/api/export/ifc.mdx @@ -9,7 +9,7 @@ Analytic IFC export has two v2 workflows: - `solid.exportIfc()` writes one IFC4 product directly from the authoritative BRep. - `solid.prepareIfcExchange()` returns the strict Rust-prepared `AnalyticExchangeBodyV2` used by a - semantic BIM assembler such as OpenPlans and `openplans-ifc-py`. + semantic BIM assembler such as Floorkit and `openplans-ifc-py`. Both workflows retain analytic surfaces and curves, actual face/bound orientations, cavities, general bounded intersection curves, and face provenance. They do not inspect a viewport mesh. diff --git a/docs/api/shapes/analytic-solid.mdx b/docs/api/shapes/analytic-solid.mdx index 0327c5d..ecc60d3 100644 --- a/docs/api/shapes/analytic-solid.mdx +++ b/docs/api/shapes/analytic-solid.mdx @@ -132,7 +132,7 @@ Call `lod.update(camera, renderer.domElement.height)` each frame. Visible bodies Await `lod.refine(worldDeflection)` before image export to require explicit refinement. Positive uniform scale adjusts local deflection; unsupported placement returns an error. Render LOD never refreshes authored scene snapshots. -OpenPlans `OpenThree.enableAnalyticLod(options)` uses this service. Await `syncAnalyticLod()` after authored tree replacements, then `refineAnalyticView(deflection)` before synchronous image capture. The downstream `views/analytic-rendering.html` example exercises those calls and semantic v2 exchange. Current development uses the local tested kernel artifact; a coordinated published major release remains pending. +Floorkit `OpenThree.enableAnalyticLod(options)` uses this service. Await `syncAnalyticLod()` after authored tree replacements, then `refineAnalyticView(deflection)` before synchronous image capture. The downstream `views/analytic-rendering.html` example exercises those calls and semantic v2 exchange. Current development uses the local tested kernel artifact; a coordinated published major release remains pending. ## Feature outlines diff --git a/docs/concepts/exports.mdx b/docs/concepts/exports.mdx index 029b833..7d969be 100644 --- a/docs/concepts/exports.mdx +++ b/docs/concepts/exports.mdx @@ -42,7 +42,7 @@ quality. [`WorldGraph`](/api/scene/scene-management) is the kernel's single scene. Its `exportStl`, `exportStep`, and `exportIfc` write world-placed schema-v1 bodies and refuse analytic definitions; export analytic solids through their own STEP/STL/IFC methods. For semantic multi-element -IFC, collect `prepareIfcExchange()` bodies under schema `"2.0"`; OpenPlans keeps multiple bodies of +IFC, collect `prepareIfcExchange()` bodies under schema `"2.0"`; Floorkit keeps multiple bodies of one authored element in one `FREEFORM_ANALYTIC.geometries` array. ## Runtime differences diff --git a/docs/what-is-opengeometry.mdx b/docs/what-is-opengeometry.mdx index 66cd7c5..c8f450c 100644 --- a/docs/what-is-opengeometry.mdx +++ b/docs/what-is-opengeometry.mdx @@ -11,10 +11,10 @@ OpenGeometry makes it easy to build sophisticated CAD applications, browser-base OpenGeometry works with Three.js with plans to support other 3D libraries as well. -OpenPlans is a dedicated toolkit/application for architects and interior designers that is built on top of OpenGeometry. It is a downstream AEC workflow layer, while OpenGeometry is the core engine. +Floorkit is a dedicated toolkit/application for architects and interior designers that is built on top of OpenGeometry. It is a downstream AEC workflow layer, while OpenGeometry is the core engine. -Check out [OpenPlans](https://openplans.io) for more information. +Check out [Floorkit](https://ploterr.com/floorkit) for more information. ## Why OpenGeometry? diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 6a11890..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,57 +0,0 @@ -import js from '@eslint/js'; -import tseslint from '@typescript-eslint/eslint-plugin'; -import tsparser from '@typescript-eslint/parser'; - -export default [ - js.configs.recommended, - { - files: ['**/*.js', '**/*.mjs', '**/*.cjs'], - languageOptions: { - ecmaVersion: 2020, - sourceType: 'module', - globals: { - console: 'readonly', - process: 'readonly', - }, - }, - }, - { - files: ['**/*.ts', '**/*.tsx'], - languageOptions: { - parser: tsparser, - parserOptions: { - ecmaVersion: 2020, - sourceType: 'module', - project: './tsconfig.json', - }, - globals: { - console: 'readonly', - document: 'readonly', - window: 'readonly', - navigator: 'readonly', - URL: 'readonly', - Worker: 'readonly', - MessageEvent: 'readonly', - DOMException: 'readonly', - performance: 'readonly', - }, - }, - plugins: { - '@typescript-eslint': tseslint, - }, - rules: { - 'no-console': 'warn', - 'no-unused-vars': 'off', - '@typescript-eslint/no-unused-vars': 'warn', - '@typescript-eslint/no-explicit-any': 'warn', - }, - }, - { - ignores: [ - 'dist/', - 'node_modules/', - 'main/opengeometry/pkg/', - 'main/opengeometry/target/', - ], - }, -]; diff --git a/main/eslint.config.js b/main/eslint.config.js new file mode 100644 index 0000000..d6d61a0 --- /dev/null +++ b/main/eslint.config.js @@ -0,0 +1,324 @@ +import js from '@eslint/js'; +import rawPlugin from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/raw-plugin'; +import EXCEPTIONS from './scripts/check/lint-exceptions.json' with { type: 'json' }; + +const SDK = 'opengeometry-three/src'; +const SDK_FILES = [`${SDK}/**/*.ts`, 'opengeometry-three/index.ts']; +const CONSUMER_FILES = ['examples-vite', 'tests'].map((home) => `opengeometry-three/${home}/**/*.{ts,js,mjs}`); +const CONSUMER_MESSAGE = 'Examples and tests use the built SDK, never src/.'; +const SDK_SOURCE_IMPORTS = ['(^|/)src/', '(^|/)(\\.\\.|opengeometry-three)(/|/index(\\.[jt]s)?)?$']; +const JS_FILES = ['**/*.js', '**/*.mjs']; +const WORKER_ENTRY = `${SDK}/rendering/tessellation-worker.ts`; +const WORKER_FILES = [WORKER_ENTRY, `${SDK}/rendering/worker/**/*.ts`]; + +const LAYERS = [ + { name: 'dto', files: [`${SDK}/dto/**/*.ts`], imports: ['(^|/)dto/'] }, + { name: 'errors', files: [`${SDK}/errors.ts`], imports: ['(^|/)errors(\\.js)?$'] }, + { + name: 'constants', + files: [`${SDK}/constants/**/*.ts`, `${SDK}/limits.ts`], + imports: ['(^|/)constants/', '(^|/)limits(\\.js)?$'], + }, + { + name: 'runtime-state', + files: [`${SDK}/runtime/runtime-state.ts`, `${SDK}/runtime/event-bus.ts`], + imports: ['(^|/)runtime-state(\\.js)?$', '(^|/)event-bus(\\.js)?$'], + }, + { name: 'kernel', files: [`${SDK}/kernel/**/*.ts`], imports: ['(^|/)kernel/'] }, + { name: 'world-graph', files: [`${SDK}/world-graph/**/*.ts`], imports: ['(^|/)world-graph/'] }, + { name: 'rendering', files: [`${SDK}/rendering/**/*.ts`], imports: ['(^|/)rendering/'] }, + { name: 'marks', files: [`${SDK}/marks/**/*.ts`], imports: ['(^|/)marks/'] }, + { name: 'bodies', files: [`${SDK}/bodies/**/*.ts`], imports: ['(^|/)bodies/'] }, + { + name: 'picking-export', + files: [`${SDK}/picking/**/*.ts`, `${SDK}/export/**/*.ts`], + imports: ['(^|/)picking/', '(^|/)export/'], + }, + { + name: 'runtime', + files: [`${SDK}/runtime/create-runtime.ts`, `${SDK}/runtime/reset.ts`], + imports: ['(^|/)create-runtime(\\.js)?$', '(^|/)reset(\\.js)?$'], + }, + { name: 'facade', files: [`${SDK}/opengeometry.ts`], imports: ['^(\\.{1,2}/)+(src/)?opengeometry(\\.js)?$'] }, + { + name: 'entries', + files: ['opengeometry-three/index.ts', `${SDK}/testing/**/*.ts`], + imports: ['^(\\.\\./)*\\.\\.(/index(\\.js)?)?$', '(^|/)testing(/|$)'], + }, +]; +const THREE_AND_DOM_FREE = new Set(['dto', 'errors', 'constants', 'runtime-state', 'kernel', 'world-graph']); +const THREE_IMPORTS = [{ regex: '^three(/|$)', message: 'This layer imports no three.' }]; +const GLUE_IMPORT = { regex: '(^|/)opengeometry/pkg/', message: 'Only kernel/** imports the WASM glue.' }; +const JS_SPECIFIER = { + regex: '^(?!.*/(dist|opengeometry/pkg)/)\\.\\.?/.*\\.js$', + message: 'A relative import names the TypeScript module without an extension;' + + ' only dist/*.js and the opengeometry/pkg glue keep .js.', +}; +const WORKER_IMPORTS = [ + ...THREE_IMPORTS, + { regex: '(^|/)(bodies|runtime)/', message: 'The worker imports no three, bodies or runtime.' }, +]; +const WORKER_BARRED_LAYERS = new Set(['bodies', 'runtime']); +const DOM_MESSAGE = 'This layer uses no DOM.'; +const DOM_NAMES = [ + 'window', 'self', 'document', 'navigator', 'location', 'history', 'screen', 'frames', 'parent', 'top', 'opener', + 'localStorage', 'sessionStorage', 'indexedDB', 'caches', 'customElements', 'devicePixelRatio', 'innerWidth', + 'innerHeight', 'addEventListener', 'removeEventListener', 'dispatchEvent', 'postMessage', 'onmessage', + 'requestAnimationFrame', 'cancelAnimationFrame', 'requestIdleCallback', 'cancelIdleCallback', 'getComputedStyle', + 'matchMedia', 'alert', 'confirm', 'prompt', 'createImageBitmap', 'importScripts', + 'Window', 'Document', 'Navigator', 'Node', 'Element', 'HTMLElement', 'HTMLCanvasElement', 'HTMLImageElement', + 'SVGElement', 'Event', 'CustomEvent', 'EventTarget', 'MessageEvent', 'ErrorEvent', 'MessageChannel', 'MessagePort', + 'BroadcastChannel', 'Worker', 'SharedWorker', 'Image', 'ImageData', 'ImageBitmap', 'OffscreenCanvas', 'Path2D', + 'CanvasRenderingContext2D', 'OffscreenCanvasRenderingContext2D', 'WebGLRenderingContext', 'WebGL2RenderingContext', + 'ResizeObserver', 'MutationObserver', 'IntersectionObserver', 'DOMParser', 'XMLHttpRequest', 'FileReader', +]; +const DOM_TYPES = DOM_NAMES.filter((name) => /^[A-Z]/.test(name)); +const DOM_GLOBALS = DOM_NAMES.map((name) => ({ name, message: DOM_MESSAGE })); +const DOM_PROPERTIES = DOM_NAMES.map((property) => ({ object: 'globalThis', property, message: DOM_MESSAGE })); + +const MODULE_STATE_MESSAGE = 'No module-level mutable state outside runtime/runtime-state.ts and the worker entry.'; +const MODULE_DECLARATORS = ['Program', 'Program > ExportNamedDeclaration'] + .map((parent) => `${parent} > VariableDeclaration > VariableDeclarator`); +const STATIC_FIELD = ':matches(PropertyDefinition, AccessorProperty)[static=true]'; +const MUTABLE_VALUE = ':matches(NewExpression, ObjectExpression, ArrayExpression, CallExpression)'; +const TYPE_WRAPPER = ':matches(TSAsExpression, TSTypeAssertion, TSSatisfiesExpression, TSNonNullExpression)' + + ":not([typeAnnotation.typeName.name='const'])"; + +function mutableInitializers(holder, field) { + return [ + `${holder} > ${MUTABLE_VALUE}.${field}`, + `${holder} > ${TYPE_WRAPPER}.${field} > ${MUTABLE_VALUE}.expression`, + `${holder} > ${TYPE_WRAPPER}.${field} > ${TYPE_WRAPPER}.expression > ${MUTABLE_VALUE}.expression`, + ]; +} + +export const RESTRICTED_SYNTAX = { + 'json-parse': [{ + selector: "MemberExpression:matches([object.name='JSON'], [object.property.name='JSON'])" + + ":matches([property.name='parse'], [property.value='parse'])", + message: 'JSON.parse only in world-graph/codec.ts and kernel/kernel-errors.ts.', + }], + 'og-error-throw': [ + { selector: "ThrowStatement > NewExpression:not([callee.name='OGError'])", message: 'Every throw is an OGError.' }, + { selector: 'ThrowStatement > :not(NewExpression, Identifier)', message: 'Every throw is an OGError.' }, + ], + 'module-state': [ + "Program > VariableDeclaration[kind!='const']", + "Program > ExportNamedDeclaration > VariableDeclaration[kind!='const']", + `${STATIC_FIELD}:not([readonly=true])`, + ...MODULE_DECLARATORS.flatMap((declarator) => mutableInitializers(declarator, 'init')), + ...mutableInitializers(STATIC_FIELD, 'value'), + ].map((selector) => ({ selector, message: MODULE_STATE_MESSAGE })), + 'dom-types': [{ + selector: `TSTypeReference > Identifier.typeName[name=/^(HTML|SVG)|^(${DOM_TYPES.join('|')})$/]`, + message: DOM_MESSAGE, + }], +}; +const SYNTAX_HOMES = [ + { file: `${SDK}/world-graph/codec.ts`, allows: ['json-parse'] }, + { file: `${SDK}/kernel/kernel-errors.ts`, allows: ['json-parse'] }, + { file: `${SDK}/runtime/runtime-state.ts`, allows: ['module-state'] }, + { file: WORKER_ENTRY, allows: ['module-state'] }, +]; + +function layerOf(file) { + return LAYERS.find((layer) => layer.files.some((pattern) => pattern === file + || (pattern.endsWith('/**/*.ts') && file.startsWith(pattern.slice(0, -'**/*.ts'.length))))); +} + +function syntaxOptions(layer, allows) { + const groups = ['og-error-throw', 'json-parse', 'module-state']; + if (THREE_AND_DOM_FREE.has(layer.name)) groups.push('dom-types'); + return ['error', ...groups.filter((group) => !allows.has(group)).flatMap((group) => RESTRICTED_SYNTAX[group])]; +} + +function syntaxAllowances(exceptions) { + const allowances = new Map(); + const allow = (file, names) => { + allowances.set(file, new Set([...(allowances.get(file) ?? []), ...names])); + }; + for (const home of SYNTAX_HOMES) allow(home.file, home.allows); + for (const entry of exceptions) { + for (const file of entry.files) if (entry.syntax) allow(file, entry.syntax); + } + return allowances; +} + +function syntaxConfigs(exceptions) { + const layerWide = LAYERS.map((layer) => ({ + files: layer.files, + rules: { 'no-restricted-syntax': syntaxOptions(layer, new Set()) }, + })); + const allowed = [...syntaxAllowances(exceptions)].map(([file, allows]) => ({ + files: [file], + rules: { 'no-restricted-syntax': syntaxOptions(layerOf(file), allows) }, + })); + return [...layerWide, ...allowed]; +} + +function importOptions(layer, index, extra, barred) { + const upper = LAYERS.slice(index + 1) + .filter((above) => !barred.has(above.name)) + .flatMap((above) => above.imports.map((regex) => ({ + regex, + allowTypeImports: true, + message: `Layer order (D21): ${layer.name} may not value-import ${above.name}.`, + }))); + const patterns = [...upper, ...extra]; + if (layer.name !== 'kernel') patterns.push(GLUE_IMPORT); + if (THREE_AND_DOM_FREE.has(layer.name)) patterns.push(...THREE_IMPORTS); + return ['error', { patterns }]; +} + +function importConfigs() { + return LAYERS.flatMap((layer, index) => [ + { + files: layer.files, + ignores: layer.name === 'rendering' ? WORKER_FILES : [], + rules: { '@typescript-eslint/no-restricted-imports': importOptions(layer, index, [], new Set()) }, + }, + ...(layer.name === 'rendering' + ? [{ + files: WORKER_FILES, + rules: { + '@typescript-eslint/no-restricted-imports': importOptions(layer, index, WORKER_IMPORTS, WORKER_BARRED_LAYERS), + }, + }] + : []), + ...(THREE_AND_DOM_FREE.has(layer.name) + ? [{ + files: layer.files, + rules: { + 'no-restricted-globals': ['error', ...DOM_GLOBALS], + 'no-restricted-properties': ['error', ...DOM_PROPERTIES], + }, + }] + : []), + ]); +} + +function specifierSelectors(regex) { + const pattern = `/${regex.replaceAll('/', '\\/')}/i`; + return [ + `ImportExpression > Literal.source[value=${pattern}]`, + `ImportExpression > TemplateLiteral.source[expressions.length=0] > TemplateElement[value.cooked=${pattern}]`, + `TSImportType > TSLiteralType.argument > Literal.literal[value=${pattern}]`, + ]; +} + +function consumerConfig() { + return { + files: CONSUMER_FILES, + rules: { + '@typescript-eslint/no-restricted-imports': ['error', { + patterns: SDK_SOURCE_IMPORTS.map((regex) => ({ regex, message: CONSUMER_MESSAGE })), + }], + 'no-restricted-syntax': [ + 'error', + ...SDK_SOURCE_IMPORTS.flatMap(specifierSelectors).map((selector) => ({ selector, message: CONSUMER_MESSAGE })), + ], + }, + }; +} + +function exceptionConfigs(exceptions) { + return exceptions + .filter((entry) => entry.rules) + .map((entry) => ({ files: entry.files, rules: Object.fromEntries(entry.rules.map((rule) => [rule, 'off'])) })); +} + +const NAMING = [ + 'error', + { selector: 'default', format: ['camelCase'] }, + { selector: 'typeLike', format: ['PascalCase'] }, + { selector: 'enumMember', format: ['PascalCase'] }, + { selector: 'variable', modifiers: ['const', 'global'], format: ['UPPER_CASE'] }, + { selector: 'import', modifiers: ['namespace'], format: ['UPPER_CASE'] }, + { selector: 'import', modifiers: ['default'], format: ['camelCase', 'PascalCase'] }, + { selector: 'parameter', format: ['camelCase'], leadingUnderscore: 'allow' }, + { + selector: ['objectLiteralProperty', 'typeProperty'], + format: ['camelCase'], + leadingUnderscore: 'allowDouble', + }, + { selector: 'objectLiteralProperty', filter: { regex: '^module_or_path$', match: true }, format: null }, + { + selector: ['objectLiteralProperty', 'typeProperty', 'objectLiteralMethod'], + modifiers: ['requiresQuotes'], + format: null, + }, +]; +const KERNEL_KEY_FILES = [`${SDK}/dto/brep.ts`, `${SDK}/dto/boolean-report.ts`]; +const KERNEL_KEY_NAMING = [ + ...NAMING, + { selector: 'typeProperty', filter: { regex: '_', match: true }, format: ['snake_case'] }, +]; + +function sharedRules(returnTypeNames) { + return { + 'no-undef': 'off', + 'max-len': ['error', { code: 120 }], + 'max-lines': ['error', { max: 400 }], + 'max-lines-per-function': ['error', { max: 60 }], + 'max-classes-per-file': ['error', 1], + '@typescript-eslint/naming-convention': NAMING, + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-unsafe-type-assertion': 'error', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/switch-exhaustiveness-check': 'error', + '@typescript-eslint/only-throw-error': 'error', + '@typescript-eslint/consistent-type-imports': 'error', + '@typescript-eslint/explicit-module-boundary-types': ['error', { allowedNames: returnTypeNames }], + '@typescript-eslint/explicit-function-return-type': [ + 'error', + { allowExpressions: true, allowedNames: returnTypeNames }, + ], + '@typescript-eslint/consistent-type-definitions': ['error', 'type'], + '@typescript-eslint/no-extraneous-class': ['error', { allowStaticOnly: true }], + }; +} + +export function lintConfig(exceptions, returnTypeNames) { + return [ + { + ignores: [ + 'dist/**', 'opengeometry-three/examples-dist/**', 'opengeometry/**', 'tools/**', 'test-results/**', + 'playwright-report/**', '**/*.d.ts', + ], + }, + js.configs.recommended, + ...rawPlugin.flatConfigs['flat/strict-type-checked'], + ...rawPlugin.flatConfigs['flat/stylistic-type-checked'], + { + languageOptions: { + parserOptions: { + projectService: { allowDefaultProject: ['playwright.config.ts'], defaultProject: 'tsconfig.tests.json' }, + tsconfigRootDir: import.meta.dirname, + }, + }, + rules: sharedRules(returnTypeNames), + }, + { files: JS_FILES, ...rawPlugin.flatConfigs['flat/disable-type-checked'] }, + { + files: JS_FILES, + rules: { + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/naming-convention': NAMING, + }, + }, + { files: SDK_FILES, rules: { 'max-lines': ['error', { max: 300 }] } }, + { files: KERNEL_KEY_FILES, rules: { '@typescript-eslint/naming-convention': KERNEL_KEY_NAMING } }, + ...importConfigs(), + ...syntaxConfigs(exceptions), + consumerConfig(), + { + files: [...SDK_FILES, 'opengeometry-three/{examples-vite,tests}/**/*.ts'], + rules: { 'no-restricted-imports': ['error', { patterns: [JS_SPECIFIER] }] }, + }, + ...exceptionConfigs(exceptions), + ]; +} + +export default lintConfig(EXCEPTIONS.exceptions, EXCEPTIONS.returnTypeNames.names); diff --git a/main/opengeometry-babylon/README.md b/main/opengeometry-babylon/README.md deleted file mode 100644 index 1f47de3..0000000 --- a/main/opengeometry-babylon/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# opengeometry-babylon - -Scaffold, not yet implemented. A future Babylon.js adapter for the OpenGeometry kernel. - -For the current public SDK, see `main/opengeometry-three/` and the published -[`opengeometry`](https://www.npmjs.com/package/opengeometry) package. diff --git a/main/opengeometry-babylon/package.json b/main/opengeometry-babylon/package.json deleted file mode 100644 index f6588b7..0000000 --- a/main/opengeometry-babylon/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "@opengeometry/kernel-webgl", - "version": "1.0.0", - "description": "OpenGeometry Kernel for Babylon.js", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [ - "opengeometry", - "three", - "geometry" - ], - "author": "Vishwajeet Mane", - "license": "MIT", - "dependencies": {} -} \ No newline at end of file diff --git a/main/opengeometry-export-io/README.md b/main/opengeometry-export-io/README.md deleted file mode 100644 index 783010f..0000000 --- a/main/opengeometry-export-io/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# opengeometry-export-io - -Scaffold, not yet implemented. Reserved for future export I/O code. - -For current export functionality, see `main/opengeometry/src/export/` (STEP, STL, IFC, -projection, native PDF). diff --git a/main/opengeometry-export-schema/README.md b/main/opengeometry-export-schema/README.md deleted file mode 100644 index de942ea..0000000 --- a/main/opengeometry-export-schema/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# opengeometry-export-schema - -Scaffold, not yet implemented. Reserved for future export-schema definitions. - -For current export functionality, see `main/opengeometry/src/export/` (STEP, STL, IFC, -projection, native PDF). diff --git a/main/opengeometry-ios/README.md b/main/opengeometry-ios/README.md deleted file mode 100644 index b84a0ca..0000000 --- a/main/opengeometry-ios/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# opengeometry-ios - -Scaffold, not yet implemented. A future iOS bindings package for the OpenGeometry kernel. - -For the current public SDK, see `main/opengeometry-three/` and the published -[`opengeometry`](https://www.npmjs.com/package/opengeometry) package. diff --git a/main/opengeometry-three/examples-vite/boolean.html b/main/opengeometry-three/examples-vite/boolean.html new file mode 100644 index 0000000..ddc9015 --- /dev/null +++ b/main/opengeometry-three/examples-vite/boolean.html @@ -0,0 +1,63 @@ + + + + OpenGeometry Boolean Operations Example + + + + + + + +
+ Back + Analytic Solid Booleans +
+ + + +
+ + + + + diff --git a/main/opengeometry-three/examples-vite/index.html b/main/opengeometry-three/examples-vite/index.html index ae6a2ea..24856a1 100644 --- a/main/opengeometry-three/examples-vite/index.html +++ b/main/opengeometry-three/examples-vite/index.html @@ -1,839 +1,33 @@ - + - + - OpenGeometry Three Examples - + OpenGeometry 2.5 Example + -
-
-
-

OpenGeometry Three

-

Core examples

-
- -
- -
-
-

Primitives

-

6 core demos

-
-
-
-
-

Arc

-
-

Analytic circular arc with radius, signed sweep, deflection, and edge picking.

-
- Radius - Angles -
- Open example -
-
-
-

Curve

-
-

Explicit piecewise linear curve with direct edits to its sampled points.

-
- Control Points - Curve -
- Open example -
-
-
-

Elliptical Arc

-
-

Analytic ellipse edge with major/minor radii, signed sweep, and deflection.

-
- Major radius - Minor radius - Angles -
- Open example -
-
-
-

Line

-
-

Two-point line primitive with direct start and end point controls.

-
- Start - End -
- Open example -
-
-
-

Polyline

-
-

Ordered point-list polyline example with direct edits to the inner points.

-
- Points - Path -
- Open example -
-
-
-

Rectangle

-
-

Parametric rectangular primitive for base profiles.

-
- Width - Breadth -
- Open example -
-
-
- -
-
-

Shapes

-

16 core demos

-
-
-
-
-

Cuboid

-
-

Rectangular solid for massing, rooms, and equipment blocks.

-
- Width - Depth -
- Open example -
-
-
-

Cuboid Placement

-
-

Focused placement example for translation, rotation, and scale on one cuboid.

-
- Translation - Rotation - Scale -
- Open example -
-
-
-

Cylinder

-
-

Analytic cylindrical volume with chord-error tessellation and face picking.

-
- Radius - Deflection -
- Open example -
-
-

Analytic cylinder sector

-

Trimmed cylindrical side with exact radial planes and deflection-controlled rendering.

-
Analytic BRep v2Sweep angle
- Open example -
-
-
-

Analytic Solids

-
-

Authoritative curved surfaces with adjustable chord error and face picking.

-
- Deflection - Analytic BRep v2 -
- Open example -
-
-

Cone

-

Analytic cone with a declared apex and a planar base.

-
Analytic BRep v2Deflection
- Open example -
-
-

Frustum

-

Trimmed cone with cylinder and cone limits.

-
Analytic BRep v2Deflection
- Open example -
-
-

Ring Torus

-

Periodic toroidal surface with adjustable chord error.

-
Analytic BRep v2Deflection
- Open example -
-
-

Circular Wall

-

True inner and outer arcs, height, thickness, and signed sweep.

-
Analytic BRep v2Deflection
- Open example -
-
-

Circular Wall Arch

-

Stationed arched opening with cylindrical header and corrected wall intersection curves.

-
Curved wallSSI
- Open example -
-
-

Straight Wall Arch

-

Exact semicircle edges, planar jambs, and an authoritative cylindrical header face.

-
Analytic openingShared edges
- Open example -
-
-

Analytic Patterns

-

Linear, rectangular, and circular rigid instances sharing one authoritative BRep and render mesh.

-
InstancingPicking
- Open example -
-
-
-

Opening

-
-

Strict v2 opening cutter with live analytic wall subtraction.

-
- Width - Height -
- Open example -
-
-
-

Polygon

-
-

Polygon face example with direct profile changes and an optional hole.

-
- Vertices - Holes -
- Open example -
-
-
-

Sphere

-
-

Authoritative spherical surface with adjustable chord error and pole handling.

-
- Radius - Deflection -
- Open example -
-
-
-

Sweep

-
-

Sweep solid built from a visible path and a simple profile.

-
- Path - Profile -
- Open example -
-
-
-

Window Frame Sweep

-
-

Closed-loop sweep regression verifier with expected-vs-observed frame dimensions.

-
- Regression - Window Frame -
- Open example -
-
-
-

Wedge

-
-

Tapered solid for ramps and sloped technical elements.

-
- Height - Depth -
- Open example -
-
-
- -
-
-

Operations

-

19 core demos

-
-
-
-
-

Boolean Operations

-
-

Strict BRep v2 union, intersection, and subtraction with provenance.

-
- Solid - Select -
- Open example -
-
-

Analytic Material Booleans

-

In-house union, intersection, subtraction, cavities, contacts, round openings, cylindrical and spherical pockets, and source-face mappings across analytic families.

-
Analytic trimsProvenance
- Open example -
-
-

Restricted Analytic Modeling

-

Coaxial loft, revolve, planar chamfer, supported fillet, and shell/thicken without faceted authority.

-
ModelingAnalytic BRep v2
- Open example -
-
-

Polygon Loft

-

Select two polygon sections and create a capped ruled Plane/Line BRep with automatic vertex correspondence.

-
Ruled loftProfile selection
- Open example -
-
-
-

Polygon Boolean Operations

-
-

Coplanar polygon union, intersection, and subtraction on one page.

-
- Polygon - Select -
- Open example -
-
-
-

Extruded Boolean Operations

-
-

Uses polygon.extrude(height) to build solids before booleaning them.

-
- Extrude - BRep Solid -
- Open example -
-
-
-

Analytic Parametric Editing

-
-

Regenerate an analytic cuboid and inspect its strict v2 topology.

-
- Parametric - BRep v2 -
- Open example -
-
-
-

Offset

-
-

Offset generation with acute-corner and bevel parameters.

-
- Polyline - Offset -
- Open example -
-
-
-

Variable Ring Offset

-
-

- Per-edge ring inset (setback envelope) with live front/side/rear distances and a - splitting dumbbell parcel. -

-
- Ring - Offset - Setback -
- Open example -
-
-
-

General Extrude

-
-

Directional extrude with arbitrary direction and extent type.

-
- Extrude - BRep -
- Open example -
-
-
-

Analytic STEP Export

-
-

Direct STEP export of cuboids, all five surface families, circular walls, sphere cuts and cavities.

-
- Analytic - STEP -
- Open example -
-
-
-

Units & STEP

-
-

Export the same analytic model in metres or millimetres and inspect the STEP unit context.

-
- Units - STEP -
- Open example -
-
-
-

Profile Validation

-
-

Winding correction, self-intersection, duplicate & degeneracy diagnostics (D7).

-
- Profile - Robustness -
- Open example -
-
-
-

BRep diagnostics

-
-

Strict v2 validation, detectable geometry/topology defects and regeneration from parameters.

-
- Validity - Regeneration -
- Open example -
-
-
-

Work Plane Sketch

-
-

A 2D sketch lifted to 3D purely by a datum frame (origin + normal + axes) (D6).

-
- Work Plane - Sketch -
- Open example -
-
-
-

STEP Export

-
-

Single-shape STEP export workflow with direct download.

-
- STEP - Export -
- Open example -
-
-
-

STL Export

-
-

Single-shape binary STL export workflow with direct download.

-
- STL - Export -
- Open example -
-
-
-

Sweep Path + Profile

-
-

Sweep example that keeps both the path and profile easy to follow.

-
- Path - Profile -
- Open example -
-
-
-

Wall from Offsets

-
-

Build a wall solid from one centerline and its two offsets.

-
- Wall - Offsets -
- Open example -
-
-
- - +
+
+
+ Sweep example + Boolean example + Analytic Boolean example + Opening example + Cuboid example + Cylinder example + STEP export example + + + + Loading kernel… +
+ diff --git a/main/opengeometry-three/examples-vite/opening.html b/main/opengeometry-three/examples-vite/opening.html new file mode 100644 index 0000000..266668f --- /dev/null +++ b/main/opengeometry-three/examples-vite/opening.html @@ -0,0 +1,79 @@ + + + + OpenGeometry Opening Example + + + + + + + +
+ Back + Opening +
+ + + +
+ + + + + diff --git a/main/opengeometry-three/examples-vite/operations/analytic-boolean-operations.html b/main/opengeometry-three/examples-vite/operations/analytic-boolean-operations.html index c154aa8..666ae49 100644 --- a/main/opengeometry-three/examples-vite/operations/analytic-boolean-operations.html +++ b/main/opengeometry-three/examples-vite/operations/analytic-boolean-operations.html @@ -4,6 +4,7 @@ OpenGeometry Analytic Boolean Operations +
@@ -11,19 +12,18 @@ Analytic Boolean Operations
- - + diff --git a/main/opengeometry-three/examples-vite/operations/analytic-boolean-operations.ts b/main/opengeometry-three/examples-vite/operations/analytic-boolean-operations.ts new file mode 100644 index 0000000..3717fa9 --- /dev/null +++ b/main/opengeometry-three/examples-vite/operations/analytic-boolean-operations.ts @@ -0,0 +1,166 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import { requiredElement } from '../shared/dom'; +import { startFpsMeter } from '../shared/fps'; +import { bootExample } from '../shared/kernel'; +import { releaseOnUnload } from '../shared/unload'; +import { fitRenderer } from '../shared/viewport'; +import { + OGError, OpenGeometry, Solid, + OG_OPERATION_INTERSECT, OG_OPERATION_SUBTRACT, OG_OPERATION_UNION, + OG_PRIMITIVE_CYLINDER, OG_TRANSFORM_TRANSLATE, +} from '../../../dist/index.js'; + +const VIEWPORT = requiredElement('#app', HTMLDivElement); +const OPERATION_INPUT = requiredElement('#operation', HTMLSelectElement); +const OFFSET_INPUT = requiredElement('#offset', HTMLInputElement); +const DEFLECTION_INPUT = requiredElement('#deflection', HTMLInputElement); +const REPORT = requiredElement('#report', HTMLElement); +const ERROR_READOUT = requiredElement('#error', HTMLElement); +const STATUS = requiredElement('#status', HTMLElement); +await bootExample('../'); + +const SCENE = new THREE.Scene(); +SCENE.background = new THREE.Color(0xeef2ff); +SCENE.add(new THREE.GridHelper(24, 24, 0x7188aa, 0xcbd5e1)); +SCENE.add(new THREE.HemisphereLight(0xffffff, 0x526070, 2)); +const LIGHT = new THREE.DirectionalLight(0xffffff, 2); +LIGHT.position.set(5, 8, 5); +SCENE.add(LIGHT); +const CAMERA = new THREE.PerspectiveCamera(45, 1, 0.1, 100); +CAMERA.position.set(9, 5, 9); +const RENDERER = new THREE.WebGLRenderer({ antialias: true }); +RENDERER.setPixelRatio(Math.min(window.devicePixelRatio || 1, 1.5)); +VIEWPORT.append(RENDERER.domElement); +const CONTROLS = new OrbitControls(CAMERA, RENDERER.domElement); +CONTROLS.target.set(0, 1, 0); +CONTROLS.enableDamping = true; + +type Example = { host: Solid; tool: Solid; result: Solid | undefined; faces: number; contacts: number }; +let displayed: Example | undefined; +let revision = 0; +let updateFrame = 0; +let updateGeometry = true; + +const CHORD_ERROR = (): number => 10 ** Number(DEFLECTION_INPUT.value); +const OPERATION = (): string => OPERATION_INPUT.value === 'union' ? OG_OPERATION_UNION + : OPERATION_INPUT.value === 'intersection' ? OG_OPERATION_INTERSECT : OG_OPERATION_SUBTRACT; + +function bodies(example: Example): Solid[] { + return [example.host, example.tool, example.result].filter((body): body is Solid => Boolean(body)); +} +function dispose(example: Example): void { bodies(example).reverse().forEach((body) => { body.dispose(); }); } +function ready(example: Example): void { + SCENE.updateMatrixWorld(true); + OpenGeometry.flush({ geometry: 'sync' }); + for (const body of bodies(example)) { + if (body.record?.revision !== body.lastInfo.shapeRevision) throw new Error('Boolean geometry did not finish'); + } +} + +function createExample(): Example { + const id = ++revision; + const created: Solid[] = []; + try { + const host = new Solid(OG_PRIMITIVE_CYLINDER, { radius: 1, height: 2.4 }, { ogId: `analytic-host-${String(id)}` }); + created.push(host); + const tool = new Solid(OG_PRIMITIVE_CYLINDER, { radius: 1, height: 2.4 }, { ogId: `analytic-tool-${String(id)}` }); + created.push(tool); + tool.transform(OG_TRANSFORM_TRANSLATE, { offset: [Number(OFFSET_INPUT.value), 0, 0] }); + const result = host.duplicate({ ogId: `analytic-result-${String(id)}` }); + created.push(result); + let faces = 0; + let contacts = 0; + let empty = false; + try { + result.operate(OPERATION(), { tools: [tool] }); + faces = result.getBrep().topology.faces.length; + contacts = result.getReport()?.report.contacts.length ?? 0; + } catch (error) { + if (!(error instanceof OGError && error.code === 'EmptyResult')) throw error; + empty = true; + } + host.transform(OG_TRANSFORM_TRANSLATE, { offset: [-3.5, 0, 0] }); + tool.transform(OG_TRANSFORM_TRANSLATE, { offset: [-3.5, 0, 0] }); + if (!empty) result.transform(OG_TRANSFORM_TRANSLATE, { offset: [3.5, 0, 0] }); + host.setAppearance({ color: 0x3b82f6, opacity: 0.65, outline: true, deflection: CHORD_ERROR() }); + tool.setAppearance({ color: 0xf97316, opacity: 0.65, outline: true, deflection: CHORD_ERROR() }); + if (!empty) result.setAppearance({ color: 0x22c55e, outline: true, deflection: CHORD_ERROR() }); + if (empty) { result.dispose(); created.pop(); } + return { host, tool, result: empty ? undefined : result, faces, contacts }; + } catch (error) { + created.reverse().forEach((body) => { body.dispose(); }); + throw error; + } +} + +function rebuild(): void { + const started = performance.now(); + let next: Example | undefined; + try { + next = createExample(); + for (const body of bodies(next)) SCENE.add(body); + ready(next); + const previous = displayed; + displayed = next; + if (previous) dispose(previous); + const counts = `${String(next.faces)} result faces · ${String(next.contacts)} contacts`; + REPORT.textContent = `${OPERATION_INPUT.value} · analytic\n${counts}`; + ERROR_READOUT.textContent = ''; + STATUS.textContent = `Ready · ${(performance.now() - started).toFixed(1)} ms`; + } catch (error) { + if (next) dispose(next); + ERROR_READOUT.textContent = `Previous result retained. ${String(error)}`; + STATUS.textContent = 'Boolean error'; + } +} + +function retessellate(): void { + if (!displayed) { rebuild(); return; } + const started = performance.now(); + try { + for (const body of bodies(displayed)) body.setAppearance({ deflection: CHORD_ERROR() }); + ready(displayed); + ERROR_READOUT.textContent = ''; + STATUS.textContent = `Ready · mesh ${(performance.now() - started).toFixed(1)} ms`; + } catch (error) { + ERROR_READOUT.textContent = String(error); + STATUS.textContent = 'Tessellation error'; + } +} + +function schedule(geometry: boolean): void { + updateGeometry ||= geometry; + if (updateFrame) return; + updateFrame = requestAnimationFrame(() => { + updateFrame = 0; + if (updateGeometry) rebuild(); else retessellate(); + updateGeometry = false; + }); +} + +OPERATION_INPUT.addEventListener('change', () => { schedule(true); }); +OFFSET_INPUT.addEventListener('input', () => { schedule(true); }); +DEFLECTION_INPUT.addEventListener('input', () => { schedule(false); }); + +const RESIZE_OBSERVER = new ResizeObserver(() => { fitRenderer(VIEWPORT, CAMERA, RENDERER); }); +RESIZE_OBSERVER.observe(VIEWPORT); +CONTROLS.addEventListener('start', () => { OpenGeometry.setCameraMotion(true); }); +CONTROLS.addEventListener('end', () => { OpenGeometry.setCameraMotion(false); }); +const UNSUBSCRIBE_ERROR = OpenGeometry.on('error', (error) => { ERROR_READOUT.textContent = String(error); }); +rebuild(); +updateGeometry = false; +const STOP_FPS_METER = startFpsMeter(RENDERER, SCENE, CAMERA, CONTROLS); + +(window as typeof window & { __ogAnalyticBooleanExample?: unknown }).__ogAnalyticBooleanExample = { + camera: CAMERA, controls: CONTROLS, renderer: RENDERER, + get current() { return displayed; }, + rebuild, +}; + +releaseOnUnload({ + resizeObserver: RESIZE_OBSERVER, stopFpsMeter: STOP_FPS_METER, unsubscribeError: UNSUBSCRIBE_ERROR, + controls: CONTROLS, renderer: RENDERER, +}, () => { if (updateFrame) window.cancelAnimationFrame(updateFrame); }, () => { + if (displayed) dispose(displayed); +}); diff --git a/main/opengeometry-three/examples-vite/operations/analytic-modeling.html b/main/opengeometry-three/examples-vite/operations/analytic-modeling.html deleted file mode 100644 index 1e2a822..0000000 --- a/main/opengeometry-three/examples-vite/operations/analytic-modeling.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - OpenGeometry Analytic Modeling Operations - - -
- Back - Analytic Modeling Operations -
- -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/analytic-step-export.html b/main/opengeometry-three/examples-vite/operations/analytic-step-export.html deleted file mode 100644 index 801323e..0000000 --- a/main/opengeometry-three/examples-vite/operations/analytic-step-export.html +++ /dev/null @@ -1,234 +0,0 @@ - - - - - - OpenGeometry analytic STEP export - - -
BackAnalytic exchange · v2
- -
- - - diff --git a/main/opengeometry-three/examples-vite/operations/boolean-operations.html b/main/opengeometry-three/examples-vite/operations/boolean-operations.html deleted file mode 100644 index d6b17da..0000000 --- a/main/opengeometry-three/examples-vite/operations/boolean-operations.html +++ /dev/null @@ -1,296 +0,0 @@ - - - - OpenGeometry Boolean Operations Example - - - - - - -
- Back - Analytic Solid Booleans -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/brep-validity.html b/main/opengeometry-three/examples-vite/operations/brep-validity.html deleted file mode 100644 index 029b4e2..0000000 --- a/main/opengeometry-three/examples-vite/operations/brep-validity.html +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - OpenGeometry v2 BRep diagnostics - - -
BackBRep diagnostics · v2
- -
- - - diff --git a/main/opengeometry-three/examples-vite/operations/editor-modes.html b/main/opengeometry-three/examples-vite/operations/editor-modes.html deleted file mode 100644 index 22c2e7f..0000000 --- a/main/opengeometry-three/examples-vite/operations/editor-modes.html +++ /dev/null @@ -1,223 +0,0 @@ - - - - OpenGeometry Analytic Parametric Editing Example - - - - - - -
- Back - Analytic Parametric Editing -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/extruded-boolean-operations.html b/main/opengeometry-three/examples-vite/operations/extruded-boolean-operations.html deleted file mode 100644 index d2ab95c..0000000 --- a/main/opengeometry-three/examples-vite/operations/extruded-boolean-operations.html +++ /dev/null @@ -1,255 +0,0 @@ - - - - OpenGeometry Extruded Boolean Example - - - - - - -
- Back - Extruded Boolean Operations -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/general-extrude.html b/main/opengeometry-three/examples-vite/operations/general-extrude.html deleted file mode 100644 index 6d38c4a..0000000 --- a/main/opengeometry-three/examples-vite/operations/general-extrude.html +++ /dev/null @@ -1,391 +0,0 @@ - - - - OpenGeometry General Extrude Example - - - - - - -
- Back - General Extrude -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/offset-ring-variable.html b/main/opengeometry-three/examples-vite/operations/offset-ring-variable.html deleted file mode 100644 index 8f11b54..0000000 --- a/main/opengeometry-three/examples-vite/operations/offset-ring-variable.html +++ /dev/null @@ -1,329 +0,0 @@ - - - - OpenGeometry Variable Ring Offset Example - - - - - - -
- Back - Variable Ring Offset -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/offset.html b/main/opengeometry-three/examples-vite/operations/offset.html deleted file mode 100644 index 87fb220..0000000 --- a/main/opengeometry-three/examples-vite/operations/offset.html +++ /dev/null @@ -1,275 +0,0 @@ - - - - OpenGeometry Offset Example - - - - - - -
- Back - Offset -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/polygon-boolean-operations.html b/main/opengeometry-three/examples-vite/operations/polygon-boolean-operations.html deleted file mode 100644 index 55b5a6d..0000000 --- a/main/opengeometry-three/examples-vite/operations/polygon-boolean-operations.html +++ /dev/null @@ -1,297 +0,0 @@ - - - - OpenGeometry Polygon Boolean Operations - - - - - - - -
- Back - Polygon Boolean Operations -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/polygon-loft.html b/main/opengeometry-three/examples-vite/operations/polygon-loft.html deleted file mode 100644 index d844db2..0000000 --- a/main/opengeometry-three/examples-vite/operations/polygon-loft.html +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - OpenGeometry Polygon Loft - - -
BackPolygon Loft
- -
- - - diff --git a/main/opengeometry-three/examples-vite/operations/profile-validation.html b/main/opengeometry-three/examples-vite/operations/profile-validation.html deleted file mode 100644 index 1d6c66f..0000000 --- a/main/opengeometry-three/examples-vite/operations/profile-validation.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - OpenGeometry Profile Robustness Analyzer Example - - - - - - -
- Back - Profile Validation -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/step-export.html b/main/opengeometry-three/examples-vite/operations/step-export.html index ceebbe1..900989d 100644 --- a/main/opengeometry-three/examples-vite/operations/step-export.html +++ b/main/opengeometry-three/examples-vite/operations/step-export.html @@ -4,8 +4,9 @@ OpenGeometry STEP Export Example - + +
@@ -16,8 +17,7 @@
- + diff --git a/main/opengeometry-three/examples-vite/operations/step-export.ts b/main/opengeometry-three/examples-vite/operations/step-export.ts new file mode 100644 index 0000000..edea86a --- /dev/null +++ b/main/opengeometry-three/examples-vite/operations/step-export.ts @@ -0,0 +1,109 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import { numberControl, onNumberInput } from '../shared/controls'; +import { requiredElement } from '../shared/dom'; +import { startFpsMeter } from '../shared/fps'; +import { bootExample } from '../shared/kernel'; +import { releaseOnUnload } from '../shared/unload'; +import { fitRenderer } from '../shared/viewport'; +import { OpenGeometry, Solid, OG_PRIMITIVE_CUBOID } from '../../../dist/index.js'; + +const APP = requiredElement('#app', HTMLDivElement); +const STATUS = requiredElement('#status', HTMLElement); +const REPORT = requiredElement('#export-report', HTMLElement); +const ERROR = requiredElement('#error', HTMLElement); +const DOWNLOAD = requiredElement('[data-download]', HTMLAnchorElement); +const EXPORT_BUTTON = requiredElement('[data-export]', HTMLButtonElement); +await bootExample('../'); + +const SCENE = new THREE.Scene(); +SCENE.background = new THREE.Color(0xeef2ff); +SCENE.add(new THREE.HemisphereLight(0xffffff, 0x526070, 2)); +const LIGHT = new THREE.DirectionalLight(0xffffff, 2); +LIGHT.position.set(5, 8, 5); +SCENE.add(LIGHT); +SCENE.add(new THREE.GridHelper(20, 20, 0x4460ff, 0xd5ddff)); +const CAMERA = new THREE.PerspectiveCamera(55, 1, 0.1, 100); +CAMERA.position.set(5.8, 4.5, 6.8); +const RENDERER = new THREE.WebGLRenderer({ antialias: true }); +RENDERER.setPixelRatio(Math.min(window.devicePixelRatio || 1, 1.5)); +APP.append(RENDERER.domElement); +const CONTROLS = new OrbitControls(CAMERA, RENDERER.domElement); +CONTROLS.target.set(0, 0.9, 0); +CONTROLS.update(); + +const DIMENSIONS = { width: 1.8, height: 1.4, depth: 1.1 }; +const CUBOID = new Solid(OG_PRIMITIVE_CUBOID, DIMENSIONS, { ogId: 'step-cuboid' }); +CUBOID.setAppearance({ color: 0x0ea5e9, outline: true, deflection: 0.005 }); +SCENE.add(CUBOID); + +function render(): void { RENDERER.render(SCENE, CAMERA); } + +function resize(): void { + fitRenderer(APP, CAMERA, RENDERER); + render(); +} + +let updateFrame = 0; +function updateCuboid(): boolean { + const started = performance.now(); + try { + CUBOID.rebuild(OG_PRIMITIVE_CUBOID, DIMENSIONS); + SCENE.updateMatrixWorld(true); + OpenGeometry.flush({ geometry: 'sync' }); + if (CUBOID.record?.revision !== CUBOID.lastInfo.shapeRevision) { + throw new Error('Cuboid geometry did not finish'); + } + STATUS.textContent = `Cuboid ready · ${(performance.now() - started).toFixed(1)} ms`; + ERROR.textContent = ''; + return true; + } catch (failure) { ERROR.textContent = String(failure); return false; } +} + +function scheduleUpdate(): void { + if (updateFrame) return; + updateFrame = window.requestAnimationFrame(() => { updateFrame = 0; updateCuboid(); }); +} + +for (const name of ['width', 'height', 'depth'] as const) { + onNumberInput(numberControl(name), (value) => { + DIMENSIONS[name] = value; + scheduleUpdate(); + }); +} + +async function exportCuboid(): Promise { + try { + EXPORT_BUTTON.disabled = true; + if (updateFrame) { window.cancelAnimationFrame(updateFrame); updateFrame = 0; if (!updateCuboid()) return; } + const result = await OpenGeometry.exportStep({ nodes: [CUBOID], unit: 'metre', name: 'OpenGeometry cuboid' }); + REPORT.textContent = JSON.stringify(result.report, null, 2); + ERROR.textContent = ''; + const url = URL.createObjectURL(new Blob([result.text], { type: 'application/step' })); + DOWNLOAD.href = url; + DOWNLOAD.download = 'opengeometry-cuboid.step'; + DOWNLOAD.click(); + window.setTimeout(() => { URL.revokeObjectURL(url); }, 1000); + } catch (failure) { ERROR.textContent = `Export failed: ${String(failure)}`; } + finally { EXPORT_BUTTON.disabled = false; } +} +EXPORT_BUTTON.addEventListener('click', () => { void exportCuboid(); }); + +const RESIZE_OBSERVER = new ResizeObserver(resize); +RESIZE_OBSERVER.observe(APP); +CONTROLS.addEventListener('start', () => { OpenGeometry.setCameraMotion(true); }); +CONTROLS.addEventListener('end', () => { OpenGeometry.setCameraMotion(false); render(); }); +const UNSUBSCRIBE_ERROR = OpenGeometry.on('error', (failure) => { ERROR.textContent = String(failure); }); +resize(); +const READY = await OpenGeometry.settled(); +if (READY.failed.length) throw new Error('Cuboid geometry failed'); +render(); +STATUS.textContent = 'Cuboid ready'; +const STOP_FPS_METER = startFpsMeter(RENDERER, SCENE, CAMERA, CONTROLS); + +releaseOnUnload({ + resizeObserver: RESIZE_OBSERVER, stopFpsMeter: STOP_FPS_METER, unsubscribeError: UNSUBSCRIBE_ERROR, + controls: CONTROLS, renderer: RENDERER, +}, () => { if (updateFrame) window.cancelAnimationFrame(updateFrame); }, () => { + CUBOID.dispose(); +}); diff --git a/main/opengeometry-three/examples-vite/operations/stl-export.html b/main/opengeometry-three/examples-vite/operations/stl-export.html deleted file mode 100644 index 97600fe..0000000 --- a/main/opengeometry-three/examples-vite/operations/stl-export.html +++ /dev/null @@ -1,260 +0,0 @@ - - - - OpenGeometry STL Export Example - - - - - - -
- Back - STL Export -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/sweep-path-profile.html b/main/opengeometry-three/examples-vite/operations/sweep-path-profile.html deleted file mode 100644 index d046062..0000000 --- a/main/opengeometry-three/examples-vite/operations/sweep-path-profile.html +++ /dev/null @@ -1,276 +0,0 @@ - - - - OpenGeometry Sweep Path and Profile Example - - - - - - -
- Back - Sweep Path + Profile -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/units-step-export.html b/main/opengeometry-three/examples-vite/operations/units-step-export.html deleted file mode 100644 index d1ba705..0000000 --- a/main/opengeometry-three/examples-vite/operations/units-step-export.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - OpenGeometry Units STEP Export Example - - - - - - -
- Back - Units STEP Export -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/wall-from-offsets.html b/main/opengeometry-three/examples-vite/operations/wall-from-offsets.html deleted file mode 100644 index bf7aaa9..0000000 --- a/main/opengeometry-three/examples-vite/operations/wall-from-offsets.html +++ /dev/null @@ -1,419 +0,0 @@ - - - - OpenGeometry Wall from Offsets Example - - - - - - -
- Back - Wall from Offsets -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/operations/work-plane-sketch.html b/main/opengeometry-three/examples-vite/operations/work-plane-sketch.html deleted file mode 100644 index cb7eea1..0000000 --- a/main/opengeometry-three/examples-vite/operations/work-plane-sketch.html +++ /dev/null @@ -1,327 +0,0 @@ - - - - OpenGeometry WorkPlane Sketch Example - - - - - - -
- Back - WorkPlane Sketch -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/primitives/arc.html b/main/opengeometry-three/examples-vite/primitives/arc.html deleted file mode 100644 index b90b1d9..0000000 --- a/main/opengeometry-three/examples-vite/primitives/arc.html +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - OpenGeometry Analytic Arc Example - - -
- Back - Analytic Arc -
- -
- - - - diff --git a/main/opengeometry-three/examples-vite/primitives/curve.html b/main/opengeometry-three/examples-vite/primitives/curve.html deleted file mode 100644 index ba55f8d..0000000 --- a/main/opengeometry-three/examples-vite/primitives/curve.html +++ /dev/null @@ -1,236 +0,0 @@ - - - - OpenGeometry Curve Example - - - - - - -
- Back - Curve -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/primitives/elliptical_arc.html b/main/opengeometry-three/examples-vite/primitives/elliptical_arc.html deleted file mode 100644 index 99443dc..0000000 --- a/main/opengeometry-three/examples-vite/primitives/elliptical_arc.html +++ /dev/null @@ -1,171 +0,0 @@ - - - - - - OpenGeometry Analytic Elliptical Arc Example - - -
- Back - Analytic Elliptical Arc -
- -
- - - - diff --git a/main/opengeometry-three/examples-vite/primitives/line.html b/main/opengeometry-three/examples-vite/primitives/line.html deleted file mode 100644 index abe736b..0000000 --- a/main/opengeometry-three/examples-vite/primitives/line.html +++ /dev/null @@ -1,273 +0,0 @@ - - - - OpenGeometry Line Example - - - - - - -
- Back - Line -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/primitives/polyline.html b/main/opengeometry-three/examples-vite/primitives/polyline.html deleted file mode 100644 index 7b3fe7a..0000000 --- a/main/opengeometry-three/examples-vite/primitives/polyline.html +++ /dev/null @@ -1,274 +0,0 @@ - - - - OpenGeometry Polyline Example - - - - - - -
- Back - Polyline -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/primitives/rectangle.html b/main/opengeometry-three/examples-vite/primitives/rectangle.html deleted file mode 100644 index 1285479..0000000 --- a/main/opengeometry-three/examples-vite/primitives/rectangle.html +++ /dev/null @@ -1,236 +0,0 @@ - - - - OpenGeometry Rectangle Example - - - - - - -
- Back - Rectangle -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/analytic-patterns.html b/main/opengeometry-three/examples-vite/shapes/analytic-patterns.html deleted file mode 100644 index 48b9fb0..0000000 --- a/main/opengeometry-three/examples-vite/shapes/analytic-patterns.html +++ /dev/null @@ -1,163 +0,0 @@ - - - - - - OpenGeometry Analytic Patterns - - -
BackAnalytic Patterns
- -
- - - diff --git a/main/opengeometry-three/examples-vite/shapes/analytic-solids.html b/main/opengeometry-three/examples-vite/shapes/analytic-solids.html deleted file mode 100644 index bf4d490..0000000 --- a/main/opengeometry-three/examples-vite/shapes/analytic-solids.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - Analytic solids and deflection - - - - - - - diff --git a/main/opengeometry-three/examples-vite/shapes/circular-wall-arched-opening.html b/main/opengeometry-three/examples-vite/shapes/circular-wall-arched-opening.html deleted file mode 100644 index 1e86fe4..0000000 --- a/main/opengeometry-three/examples-vite/shapes/circular-wall-arched-opening.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - OpenGeometry Circular Wall Arched Opening Example - - -
- Back - Circular Wall Arched Opening -
- -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/circular-wall.html b/main/opengeometry-three/examples-vite/shapes/circular-wall.html deleted file mode 100644 index 87a6349..0000000 --- a/main/opengeometry-three/examples-vite/shapes/circular-wall.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - OpenGeometry Analytic Circular Wall Example - - -
- Back - Analytic Circular Wall -
- -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/cone.html b/main/opengeometry-three/examples-vite/shapes/cone.html deleted file mode 100644 index 574033c..0000000 --- a/main/opengeometry-three/examples-vite/shapes/cone.html +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - OpenGeometry Analytic Cone Example - - -
- Back - Analytic Cone -
- -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/cuboid-placement.html b/main/opengeometry-three/examples-vite/shapes/cuboid-placement.html deleted file mode 100644 index a8cad57..0000000 --- a/main/opengeometry-three/examples-vite/shapes/cuboid-placement.html +++ /dev/null @@ -1,243 +0,0 @@ - - - - OpenGeometry Cuboid Placement Example - - - - - - -
- Back - Cuboid Placement -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/cuboid.html b/main/opengeometry-three/examples-vite/shapes/cuboid.html index aac1b6a..b294833 100644 --- a/main/opengeometry-three/examples-vite/shapes/cuboid.html +++ b/main/opengeometry-three/examples-vite/shapes/cuboid.html @@ -6,6 +6,7 @@ +
@@ -61,206 +62,16 @@

Cuboid

Enabled
+

Loading kernel…

- + diff --git a/main/opengeometry-three/examples-vite/shapes/cuboid.ts b/main/opengeometry-three/examples-vite/shapes/cuboid.ts new file mode 100644 index 0000000..014e7e5 --- /dev/null +++ b/main/opengeometry-three/examples-vite/shapes/cuboid.ts @@ -0,0 +1,103 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import { numberControl, onNumberInput } from '../shared/controls'; +import { requiredElement } from '../shared/dom'; +import { startFpsMeter } from '../shared/fps'; +import { bootExample } from '../shared/kernel'; +import { releaseOnUnload } from '../shared/unload'; +import { fitRenderer } from '../shared/viewport'; +import { OpenGeometry, Solid, OG_PRIMITIVE_CUBOID } from '../../../dist/index.js'; + +const APP = requiredElement('#app', HTMLDivElement); +const STATUS = requiredElement('#status', HTMLElement); +await bootExample('../'); + +const SCENE = new THREE.Scene(); +SCENE.background = new THREE.Color(0xeef2ff); +SCENE.add(new THREE.HemisphereLight(0xffffff, 0x526070, 2)); +const LIGHT = new THREE.DirectionalLight(0xffffff, 2); +LIGHT.position.set(5, 8, 5); +SCENE.add(LIGHT); +SCENE.add(new THREE.GridHelper(20, 20, 0x4460ff, 0xd5ddff)); +const CAMERA = new THREE.PerspectiveCamera(55, 1, 0.1, 100); +CAMERA.position.set(5.8, 4.4, 6.8); +const RENDERER = new THREE.WebGLRenderer({ antialias: true }); +RENDERER.setPixelRatio(Math.min(window.devicePixelRatio || 1, 1.5)); +APP.append(RENDERER.domElement); +const CONTROLS = new OrbitControls(CAMERA, RENDERER.domElement); +CONTROLS.target.set(0, 0.8, 0); +CONTROLS.update(); + +const CONFIG = { width: 2, height: 1.6, depth: 1.2, outline: true }; +const CUBOID = new Solid( + OG_PRIMITIVE_CUBOID, { width: CONFIG.width, height: CONFIG.height, depth: CONFIG.depth }, { ogId: 'cuboid' }, +); +CUBOID.setAppearance({ color: 0x0ea5e9, outline: true, deflection: 0.005 }); +SCENE.add(CUBOID); + +function render(): void { RENDERER.render(SCENE, CAMERA); } + +function resize(): void { + fitRenderer(APP, CAMERA, RENDERER); + render(); +} + +let updateFrame = 0; +function updateCuboid(): void { + const started = performance.now(); + try { + CUBOID.rebuild(OG_PRIMITIVE_CUBOID, { width: CONFIG.width, height: CONFIG.height, depth: CONFIG.depth }); + SCENE.updateMatrixWorld(true); + OpenGeometry.flush({ geometry: 'sync' }); + if (CUBOID.record?.revision !== CUBOID.lastInfo.shapeRevision) { + throw new Error('Cuboid geometry did not finish'); + } + STATUS.textContent = `Cuboid ready · ${(performance.now() - started).toFixed(1)} ms`; + } catch (error) { STATUS.textContent = `Cuboid error: ${String(error)}`; } +} + +function scheduleUpdate(): void { + if (updateFrame) return; + updateFrame = window.requestAnimationFrame(() => { updateFrame = 0; updateCuboid(); }); +} + +function attachNumberControl(name: 'width' | 'height' | 'depth'): void { + onNumberInput(numberControl(name), (value) => { + CONFIG[name] = value; + scheduleUpdate(); + }); +} + +attachNumberControl('width'); +attachNumberControl('height'); +attachNumberControl('depth'); +const OUTLINE_ROW = requiredElement('[data-control="outline"]', HTMLElement); +const OUTLINE = requiredElement('[data-toggle]', HTMLInputElement, OUTLINE_ROW); +const OUTLINE_STATE = requiredElement('[data-state]', HTMLElement, OUTLINE_ROW); +OUTLINE.addEventListener('change', () => { + CONFIG.outline = OUTLINE.checked; + OUTLINE_STATE.textContent = OUTLINE.checked ? 'Enabled' : 'Disabled'; + CUBOID.setAppearance({ outline: OUTLINE.checked }); + render(); +}); + +const RESIZE_OBSERVER = new ResizeObserver(resize); +RESIZE_OBSERVER.observe(APP); +CONTROLS.addEventListener('start', () => { OpenGeometry.setCameraMotion(true); }); +CONTROLS.addEventListener('end', () => { OpenGeometry.setCameraMotion(false); render(); }); +const UNSUBSCRIBE_ERROR = OpenGeometry.on('error', (error) => { + STATUS.textContent = `Geometry error: ${String(error)}`; +}); +resize(); +const READY = await OpenGeometry.settled(); +if (READY.failed.length) throw new Error('Cuboid geometry failed'); +render(); +STATUS.textContent = 'Cuboid ready'; +const STOP_FPS_METER = startFpsMeter(RENDERER, SCENE, CAMERA, CONTROLS); + +releaseOnUnload({ + resizeObserver: RESIZE_OBSERVER, stopFpsMeter: STOP_FPS_METER, unsubscribeError: UNSUBSCRIBE_ERROR, + controls: CONTROLS, renderer: RENDERER, +}, () => { if (updateFrame) window.cancelAnimationFrame(updateFrame); }, () => { + CUBOID.dispose(); +}); diff --git a/main/opengeometry-three/examples-vite/shapes/cylinder-sector.html b/main/opengeometry-three/examples-vite/shapes/cylinder-sector.html deleted file mode 100644 index 62006d4..0000000 --- a/main/opengeometry-three/examples-vite/shapes/cylinder-sector.html +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - OpenGeometry Analytic Cylinder Sector Example - - -
- Back - Analytic Cylinder Sector -
- -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/cylinder.html b/main/opengeometry-three/examples-vite/shapes/cylinder.html index 6e0139b..cac42b7 100644 --- a/main/opengeometry-three/examples-vite/shapes/cylinder.html +++ b/main/opengeometry-three/examples-vite/shapes/cylinder.html @@ -4,6 +4,7 @@ OpenGeometry Analytic Cylinder Example +
@@ -13,7 +14,7 @@
- + diff --git a/main/opengeometry-three/examples-vite/shapes/cylinder.ts b/main/opengeometry-three/examples-vite/shapes/cylinder.ts new file mode 100644 index 0000000..129134a --- /dev/null +++ b/main/opengeometry-three/examples-vite/shapes/cylinder.ts @@ -0,0 +1,134 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import { requiredElement } from '../shared/dom'; +import { startFpsMeter } from '../shared/fps'; +import { bootExample } from '../shared/kernel'; +import { releaseOnUnload } from '../shared/unload'; +import { fitRenderer } from '../shared/viewport'; +import { OpenGeometry, Solid, OG_PRIMITIVE_CYLINDER } from '../../../dist/index.js'; + +const APP = requiredElement('#app', HTMLDivElement); +const STATUS = requiredElement('#status', HTMLElement); +const RADIUS_INPUT = requiredElement('#radius', HTMLInputElement); +const HEIGHT_INPUT = requiredElement('#height', HTMLInputElement); +const DEFLECTION_INPUT = requiredElement('#deflection', HTMLInputElement); +const WIREFRAME_INPUT = requiredElement('#wireframe', HTMLInputElement); +const OUTLINE_INPUT = requiredElement('#outline', HTMLInputElement); +const STATISTICS = requiredElement('#statistics', HTMLElement); +const SELECTION = requiredElement('#selection', HTMLElement); +await bootExample('../'); + +const SCENE = new THREE.Scene(); +SCENE.background = new THREE.Color(0xeef2ff); +SCENE.add(new THREE.HemisphereLight(0xffffff, 0x526070, 2)); +const LIGHT = new THREE.DirectionalLight(0xffffff, 2); +LIGHT.position.set(5, 8, 5); +SCENE.add(LIGHT); +SCENE.add(new THREE.GridHelper(16, 16, 0x7188aa, 0xcbd5e1)); +const CAMERA = new THREE.PerspectiveCamera(45, 1, 0.1, 100); +CAMERA.position.set(6, 4.5, 6); +const RENDERER = new THREE.WebGLRenderer({ antialias: true }); +RENDERER.setPixelRatio(Math.min(window.devicePixelRatio || 1, 1.5)); +APP.append(RENDERER.domElement); +const CONTROLS = new OrbitControls(CAMERA, RENDERER.domElement); +CONTROLS.target.set(0, 0.8, 0); +CONTROLS.update(); + +const CYLINDER = new Solid(OG_PRIMITIVE_CYLINDER, { + radius: Number(RADIUS_INPUT.value), height: Number(HEIGHT_INPUT.value), +}, { ogId: 'cylinder' }); +CYLINDER.setAppearance({ color: 0x329ac7, outline: true, deflection: 10 ** Number(DEFLECTION_INPUT.value) }); +SCENE.add(CYLINDER); + +function applyWireframe(): void { + const material = CYLINDER.surface.material; + if (Array.isArray(material) || !('wireframe' in material)) throw new Error('The cylinder surface has no wireframe'); + if (material.wireframe !== WIREFRAME_INPUT.checked) { + material.wireframe = WIREFRAME_INPUT.checked; + material.needsUpdate = true; + } +} + +function render(): void { RENDERER.render(SCENE, CAMERA); } + +function resize(): void { + fitRenderer(APP, CAMERA, RENDERER); + render(); +} + +function showStatistics(): void { + const faces = String(CYLINDER.getBrep().topology.faces.length); + const triangles = (CYLINDER.record?.triangles ?? 0).toLocaleString(); + STATISTICS.textContent = `${faces} analytic faces · ${triangles} render triangles`; +} + +function updateCylinder(rebuild: boolean): void { + const started = performance.now(); + try { + if (rebuild) { + CYLINDER.rebuild(OG_PRIMITIVE_CYLINDER, { + radius: Number(RADIUS_INPUT.value), height: Number(HEIGHT_INPUT.value), + }); + } + CYLINDER.setAppearance({ outline: OUTLINE_INPUT.checked, deflection: 10 ** Number(DEFLECTION_INPUT.value) }); + applyWireframe(); + SCENE.updateMatrixWorld(true); + OpenGeometry.flush({ geometry: 'sync' }); + if (CYLINDER.record?.revision !== CYLINDER.lastInfo.shapeRevision) { + throw new Error('Cylinder geometry did not finish'); + } + showStatistics(); + STATUS.textContent = `Cylinder ready · ${(performance.now() - started).toFixed(1)} ms`; + } catch (error) { STATUS.textContent = `Cylinder error: ${String(error)}`; } +} + +let updateFrame = 0; +function scheduleUpdate(rebuild: boolean): void { + if (updateFrame) window.cancelAnimationFrame(updateFrame); + updateFrame = window.requestAnimationFrame(() => { updateFrame = 0; updateCylinder(rebuild); }); +} + +RADIUS_INPUT.addEventListener('change', () => { scheduleUpdate(true); }); +HEIGHT_INPUT.addEventListener('change', () => { scheduleUpdate(true); }); +DEFLECTION_INPUT.addEventListener('input', () => { scheduleUpdate(false); }); +WIREFRAME_INPUT.addEventListener('change', () => { applyWireframe(); render(); }); +OUTLINE_INPUT.addEventListener('change', () => { + CYLINDER.setAppearance({ outline: OUTLINE_INPUT.checked }); + applyWireframe(); + render(); +}); +const RAYCASTER = new THREE.Raycaster(); +RENDERER.domElement.addEventListener('pointerdown', (event) => { + const bounds = RENDERER.domElement.getBoundingClientRect(); + const pointer = new THREE.Vector2( + ((event.clientX - bounds.left) / bounds.width) * 2 - 1, -((event.clientY - bounds.top) / bounds.height) * 2 + 1, + ); + RAYCASTER.setFromCamera(pointer, CAMERA); + const hit = RAYCASTER.intersectObject(CYLINDER.surface)[0]; + if (!hit) return; + const resolved = OpenGeometry.resolveHit(hit); + if (resolved?.faceId !== undefined) SELECTION.textContent = `Face ${String(resolved.faceId)}`; +}); + +const RESIZE_OBSERVER = new ResizeObserver(resize); +RESIZE_OBSERVER.observe(APP); +CONTROLS.addEventListener('start', () => { OpenGeometry.setCameraMotion(true); }); +CONTROLS.addEventListener('end', () => { OpenGeometry.setCameraMotion(false); render(); }); +const UNSUBSCRIBE_ERROR = OpenGeometry.on('error', (error) => { + STATUS.textContent = `Geometry error: ${String(error)}`; +}); +resize(); +const READY = await OpenGeometry.settled(); +if (READY.failed.length) throw new Error('Cylinder geometry failed'); +applyWireframe(); +render(); +showStatistics(); +STATUS.textContent = 'Cylinder ready'; +const STOP_FPS_METER = startFpsMeter(RENDERER, SCENE, CAMERA, CONTROLS); + +releaseOnUnload({ + resizeObserver: RESIZE_OBSERVER, stopFpsMeter: STOP_FPS_METER, unsubscribeError: UNSUBSCRIBE_ERROR, + controls: CONTROLS, renderer: RENDERER, +}, () => { if (updateFrame) window.cancelAnimationFrame(updateFrame); }, () => { + CYLINDER.dispose(); +}); diff --git a/main/opengeometry-three/examples-vite/shapes/frustum.html b/main/opengeometry-three/examples-vite/shapes/frustum.html deleted file mode 100644 index 4893c96..0000000 --- a/main/opengeometry-three/examples-vite/shapes/frustum.html +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - OpenGeometry Analytic Frustum Example - - -
- Back - Analytic Frustum -
- -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/opening.html b/main/opengeometry-three/examples-vite/shapes/opening.html deleted file mode 100644 index 4b144ba..0000000 --- a/main/opengeometry-three/examples-vite/shapes/opening.html +++ /dev/null @@ -1,264 +0,0 @@ - - - - OpenGeometry Opening Example - - - - - - -
- Back - Opening -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/polygon.html b/main/opengeometry-three/examples-vite/shapes/polygon.html deleted file mode 100644 index 112de79..0000000 --- a/main/opengeometry-three/examples-vite/shapes/polygon.html +++ /dev/null @@ -1,315 +0,0 @@ - - - - OpenGeometry Polygon Example - - - - - - -
- Back - Polygon -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/sphere.html b/main/opengeometry-three/examples-vite/shapes/sphere.html deleted file mode 100644 index 9c95870..0000000 --- a/main/opengeometry-three/examples-vite/shapes/sphere.html +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - OpenGeometry Analytic Sphere Example - - -
- Back - Analytic Sphere -
- -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/straight-wall-arched-opening.html b/main/opengeometry-three/examples-vite/shapes/straight-wall-arched-opening.html deleted file mode 100644 index a16f1cc..0000000 --- a/main/opengeometry-three/examples-vite/shapes/straight-wall-arched-opening.html +++ /dev/null @@ -1,220 +0,0 @@ - - - - - - OpenGeometry Straight Wall Arched Opening Example - - -
- Back - Straight Wall Arched Opening -
- -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/sweep.html b/main/opengeometry-three/examples-vite/shapes/sweep.html deleted file mode 100644 index 97bf16b..0000000 --- a/main/opengeometry-three/examples-vite/shapes/sweep.html +++ /dev/null @@ -1,412 +0,0 @@ - - - - OpenGeometry Sweep Example - - - - - - -
- Back - Sweep -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/torus.html b/main/opengeometry-three/examples-vite/shapes/torus.html deleted file mode 100644 index 00dbfbf..0000000 --- a/main/opengeometry-three/examples-vite/shapes/torus.html +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - OpenGeometry Analytic Ring Torus Example - - -
- Back - Analytic Ring Torus -
- -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/wedge.html b/main/opengeometry-three/examples-vite/shapes/wedge.html deleted file mode 100644 index 63a8a9f..0000000 --- a/main/opengeometry-three/examples-vite/shapes/wedge.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - OpenGeometry Wedge Example - - - - - - -
- Back - Wedge -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/shapes/window-frame-sweep.html b/main/opengeometry-three/examples-vite/shapes/window-frame-sweep.html deleted file mode 100644 index 664ec6b..0000000 --- a/main/opengeometry-three/examples-vite/shapes/window-frame-sweep.html +++ /dev/null @@ -1,562 +0,0 @@ - - - - OpenGeometry Window Frame Sweep - - - - - - - -
- Back - Window Frame Sweep -
- - - -
- - - - diff --git a/main/opengeometry-three/examples-vite/shared/controls.ts b/main/opengeometry-three/examples-vite/shared/controls.ts new file mode 100644 index 0000000..30b98db --- /dev/null +++ b/main/opengeometry-three/examples-vite/shared/controls.ts @@ -0,0 +1,26 @@ +import { requiredElement } from './dom'; + +export type NumberControl = { range: HTMLInputElement; number: HTMLInputElement; value: HTMLElement }; + +export function numberControl(name: string): NumberControl { + const row = requiredElement(`[data-control="${name}"]`, HTMLElement); + return { + range: requiredElement('[data-range]', HTMLInputElement, row), + number: requiredElement('[data-number]', HTMLInputElement, row), + value: requiredElement('[data-value]', HTMLElement, row), + }; +} + +export function onNumberInput(control: NumberControl, accept: (value: number) => void): void { + const commit = (raw: string): void => { + const parsed = Number(raw); + if (raw.trim() === '' || !Number.isFinite(parsed)) return; + if (parsed < Number(control.range.min) || parsed > Number(control.range.max)) return; + control.range.value = String(parsed); + control.number.value = String(parsed); + control.value.textContent = parsed.toFixed(2).replace(/\.?0+$/, ''); + accept(parsed); + }; + control.range.addEventListener('input', () => { commit(control.range.value); }); + control.number.addEventListener('input', () => { commit(control.number.value); }); +} diff --git a/main/opengeometry-three/examples-vite/shared/dom.ts b/main/opengeometry-three/examples-vite/shared/dom.ts new file mode 100644 index 0000000..1081aeb --- /dev/null +++ b/main/opengeometry-three/examples-vite/shared/dom.ts @@ -0,0 +1,9 @@ +export function requiredElement( + selector: string, + type: abstract new () => T, + parent: ParentNode = document, +): T { + const element = parent.querySelector(selector); + if (!(element instanceof type)) throw new Error(`Example page has no ${type.name} at ${selector}`); + return element; +} diff --git a/main/opengeometry-three/examples-vite/shared/fps.ts b/main/opengeometry-three/examples-vite/shared/fps.ts new file mode 100644 index 0000000..f34a816 --- /dev/null +++ b/main/opengeometry-three/examples-vite/shared/fps.ts @@ -0,0 +1,29 @@ +import type * as THREE from 'three'; +import type { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import Stats from 'three/examples/jsm/libs/stats.module.js'; + +export function startFpsMeter( + renderer: THREE.WebGLRenderer, + scene: THREE.Scene, + camera: THREE.Camera, + controls: OrbitControls, +): () => void { + const stats = new Stats(); + stats.showPanel(0); + stats.dom.style.position = 'fixed'; + stats.dom.style.left = '12px'; + stats.dom.style.bottom = '12px'; + stats.dom.style.top = 'auto'; + stats.dom.style.zIndex = '1000'; + document.body.append(stats.dom); + renderer.setAnimationLoop(() => { + if (document.hidden) return; + controls.update(); + renderer.render(scene, camera); + stats.update(); + }); + return () => { + renderer.setAnimationLoop(null); + stats.dom.remove(); + }; +} diff --git a/main/opengeometry-three/examples-vite/shared/kernel.ts b/main/opengeometry-three/examples-vite/shared/kernel.ts new file mode 100644 index 0000000..f9a9b95 --- /dev/null +++ b/main/opengeometry-three/examples-vite/shared/kernel.ts @@ -0,0 +1,9 @@ +import { OpenGeometry } from '../../../dist/index.js'; + +export async function bootExample(base: string): Promise { + const wasmURL = new URL(`${base}opengeometry_bg.wasm`, document.baseURI); + const workerURL = new URL(`${base}tessellation-worker.js`, document.baseURI); + const module = await WebAssembly.compileStreaming(fetch(wasmURL)); + const backend = new URLSearchParams(location.search).get('backend') === 'inline' ? 'inline' : 'worker'; + await OpenGeometry.create({ wasmModule: module }, { workerURL, tessellation: backend }); +} diff --git a/main/opengeometry-three/examples-vite/shared/unload.ts b/main/opengeometry-three/examples-vite/shared/unload.ts new file mode 100644 index 0000000..b489dbe --- /dev/null +++ b/main/opengeometry-three/examples-vite/shared/unload.ts @@ -0,0 +1,29 @@ +import type * as THREE from 'three'; +import type { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import { OpenGeometry } from '../../../dist/index.js'; + +export type ExampleResources = { + resizeObserver: ResizeObserver; + stopFpsMeter: () => void; + unsubscribeError: () => void; + controls: OrbitControls; + renderer: THREE.WebGLRenderer; +}; + +export function releaseOnUnload( + resources: ExampleResources, + cancelUpdate: () => void, + disposeBodies: () => void, +): void { + window.addEventListener('beforeunload', () => { + cancelUpdate(); + resources.resizeObserver.disconnect(); + resources.stopFpsMeter(); + resources.unsubscribeError(); + resources.controls.dispose(); + disposeBodies(); + OpenGeometry.reset(); + resources.renderer.dispose(); + resources.renderer.forceContextLoss(); + }, { once: true }); +} diff --git a/main/opengeometry-three/examples-vite/shared/viewport.ts b/main/opengeometry-three/examples-vite/shared/viewport.ts new file mode 100644 index 0000000..c472940 --- /dev/null +++ b/main/opengeometry-three/examples-vite/shared/viewport.ts @@ -0,0 +1,30 @@ +import type * as THREE from 'three'; + +export type ExampleView = { resetView(): void; render(): void }; + +export function fitRenderer( + container: HTMLElement, + camera: THREE.PerspectiveCamera, + renderer: THREE.WebGLRenderer, +): number { + const width = Math.max(1, container.clientWidth); + const height = Math.max(1, container.clientHeight); + camera.aspect = width / height; + camera.updateProjectionMatrix(); + renderer.setSize(width, height, false); + return width; +} + +export function narrowAwareResize( + container: HTMLElement, + camera: THREE.PerspectiveCamera, + renderer: THREE.WebGLRenderer, + view: ExampleView, +): () => void { + let previousNarrow: boolean | undefined; + return () => { + const narrow = fitRenderer(container, camera, renderer) < 700; + if (previousNarrow !== narrow) { previousNarrow = narrow; view.resetView(); } + else view.render(); + }; +} diff --git a/main/opengeometry-three/examples-vite/showcase/boolean.ts b/main/opengeometry-three/examples-vite/showcase/boolean.ts new file mode 100644 index 0000000..af3372d --- /dev/null +++ b/main/opengeometry-three/examples-vite/showcase/boolean.ts @@ -0,0 +1,156 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import { numberControl, onNumberInput } from '../shared/controls'; +import { requiredElement } from '../shared/dom'; +import { startFpsMeter } from '../shared/fps'; +import { bootExample } from '../shared/kernel'; +import { releaseOnUnload } from '../shared/unload'; +import { narrowAwareResize } from '../shared/viewport'; +import { + OpenGeometry, Solid, + OG_OPERATION_INTERSECT, OG_OPERATION_SUBTRACT, OG_OPERATION_UNION, + OG_PRIMITIVE_CUBOID, OG_TRANSFORM_TRANSLATE, +} from '../../../dist/index.js'; + +const VIEWPORT = requiredElement('#app', HTMLDivElement); +const STATUS = requiredElement('#status', HTMLElement); +const OPERATION_INPUT = requiredElement('[data-control="operation"] [data-select]', HTMLSelectElement); +const OFFSET = numberControl('offset-x'); +const REPORT = requiredElement('#result-report', HTMLElement); +const ERROR_READOUT = requiredElement('#error', HTMLElement); +await bootExample('./'); + +const SCENE = new THREE.Scene(); +SCENE.background = new THREE.Color(0xeef2ff); +SCENE.add(new THREE.HemisphereLight(0xffffff, 0x526070, 2)); +const LIGHT = new THREE.DirectionalLight(0xffffff, 2); +LIGHT.position.set(5, 8, 5); +SCENE.add(LIGHT); +SCENE.add(new THREE.GridHelper(30, 30, 0x4460ff, 0xd5ddff)); +const CAMERA = new THREE.PerspectiveCamera(48, 1, 0.1, 100); +const RENDERER = new THREE.WebGLRenderer({ antialias: true }); +RENDERER.setPixelRatio(Math.min(window.devicePixelRatio || 1, 1.5)); +VIEWPORT.append(RENDERER.domElement); +const CONTROLS = new OrbitControls(CAMERA, RENDERER.domElement); +CONTROLS.enableDamping = false; +CONTROLS.minDistance = 4; +CONTROLS.maxDistance = 35; + +type BooleanBodies = { host: Solid; tool: Solid; result: Solid }; + +let revision = 0; +let current: BooleanBodies | undefined; +let updateFrame = 0; + +function disposeBodies(bodies: Solid[]): void { bodies.slice().reverse().forEach((body) => { body.dispose(); }); } + +function createExample(): BooleanBodies { + const id = ++revision; + const created: Solid[] = []; + try { + const host = new Solid(OG_PRIMITIVE_CUBOID, { width: 2, height: 2, depth: 2 }, { ogId: `host-${String(id)}` }); + created.push(host); + const tool = new Solid(OG_PRIMITIVE_CUBOID, { width: 2, height: 2, depth: 2 }, { ogId: `tool-${String(id)}` }); + created.push(tool); + tool.transform(OG_TRANSFORM_TRANSLATE, { offset: [Number(OFFSET.range.value), 0.4, 0.4] }); + const result = host.duplicate({ ogId: `result-${String(id)}` }); + created.push(result); + const operation = OPERATION_INPUT.value === 'union' ? OG_OPERATION_UNION + : OPERATION_INPUT.value === 'intersection' ? OG_OPERATION_INTERSECT : OG_OPERATION_SUBTRACT; + result.operate(operation, { tools: [tool] }); + host.transform(OG_TRANSFORM_TRANSLATE, { offset: [-4, 0, -1] }); + tool.transform(OG_TRANSFORM_TRANSLATE, { offset: [-4, 0, -1] }); + result.transform(OG_TRANSFORM_TRANSLATE, { offset: [3, 0, -1] }); + host.setAppearance({ color: 0xdc2626, opacity: 0.6, outline: true, deflection: 0.01 }); + tool.setAppearance({ color: 0xf97316, opacity: 0.6, outline: true, deflection: 0.01 }); + const override = OPERATION_INPUT.value === 'subtraction-override'; + result.setAppearance({ + color: override ? 0x22c55e : 0xdc2626, opacity: override ? 0.6 : 1, outline: true, deflection: 0.01, + }); + return { host, tool, result }; + } catch (error) { + disposeBodies(created); + throw error; + } +} + +function render(): void { RENDERER.render(SCENE, CAMERA); } + +function resetView(): void { + const narrow = VIEWPORT.clientWidth < 700; + CAMERA.position.set(narrow ? 9 : 8, narrow ? 6 : 5, narrow ? 19 : 12); + CONTROLS.target.set(-0.1, 1, 0); + CONTROLS.update(); + render(); +} + +const RESIZE = narrowAwareResize(VIEWPORT, CAMERA, RENDERER, { resetView, render }); + +function rebuild(): void { + let next: BooleanBodies | undefined; + const started = performance.now(); + try { + next = createExample(); + SCENE.add(next.host, next.tool, next.result); + SCENE.updateMatrixWorld(true); + OpenGeometry.flush({ geometry: 'sync' }); + if ([next.host, next.tool, next.result].some((body) => body.record?.revision !== body.lastInfo.shapeRevision)) { + throw new Error('Boolean geometry did not finish'); + } + const previous = current; + current = next; + if (previous) disposeBodies([previous.host, previous.tool, previous.result]); + REPORT.textContent = JSON.stringify(next.result.getReport(), null, 2); + ERROR_READOUT.textContent = ''; + const selected = OPERATION_INPUT.selectedOptions[0]; + if (!selected) throw new Error('No Boolean operation is selected'); + const faces = String(next.result.getBrep().topology.faces.length); + const elapsed = (performance.now() - started).toFixed(1); + STATUS.textContent = `${selected.text} ready · ${faces} faces · ${elapsed} ms`; + } catch (error) { + if (next && next !== current) disposeBodies([next.host, next.tool, next.result]); + ERROR_READOUT.textContent = `Previous result retained. ${String(error)}`; + STATUS.textContent = 'Boolean error'; + } +} + +function scheduleRebuild(): void { + if (updateFrame) return; + updateFrame = window.requestAnimationFrame(() => { updateFrame = 0; rebuild(); }); +} + +function rebuildNow(): void { + if (updateFrame) window.cancelAnimationFrame(updateFrame); + updateFrame = 0; + rebuild(); +} + +OPERATION_INPUT.addEventListener('change', scheduleRebuild); +onNumberInput(OFFSET, () => { scheduleRebuild(); }); +OFFSET.range.addEventListener('change', rebuildNow); +OFFSET.number.addEventListener('change', rebuildNow); + +const RESIZE_OBSERVER = new ResizeObserver(RESIZE); +RESIZE_OBSERVER.observe(VIEWPORT); +CONTROLS.addEventListener('start', () => { OpenGeometry.setCameraMotion(true); }); +CONTROLS.addEventListener('end', () => { OpenGeometry.setCameraMotion(false); render(); }); +const UNSUBSCRIBE_ERROR = OpenGeometry.on('error', (error) => { + STATUS.textContent = `Geometry error: ${String(error)}`; +}); +RESIZE(); +resetView(); +rebuild(); +const STOP_FPS_METER = startFpsMeter(RENDERER, SCENE, CAMERA, CONTROLS); + +(window as typeof window & { __ogBooleanExample?: unknown }).__ogBooleanExample = { + camera: CAMERA, controls: CONTROLS, renderer: RENDERER, + get current() { return current; }, + render, rebuild, +}; + +releaseOnUnload({ + resizeObserver: RESIZE_OBSERVER, stopFpsMeter: STOP_FPS_METER, unsubscribeError: UNSUBSCRIBE_ERROR, + controls: CONTROLS, renderer: RENDERER, +}, () => { if (updateFrame) window.cancelAnimationFrame(updateFrame); }, () => { + if (current) disposeBodies([current.host, current.tool, current.result]); +}); diff --git a/main/opengeometry-three/examples-vite/showcase/main.ts b/main/opengeometry-three/examples-vite/showcase/main.ts new file mode 100644 index 0000000..1cf0ec0 --- /dev/null +++ b/main/opengeometry-three/examples-vite/showcase/main.ts @@ -0,0 +1,161 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import { requiredElement } from '../shared/dom'; +import { startFpsMeter } from '../shared/fps'; +import { bootExample } from '../shared/kernel'; +import { releaseOnUnload } from '../shared/unload'; +import { fitRenderer } from '../shared/viewport'; +import { + OpenGeometry, Solid, SystemAssembly, Wire, + OG_OPERATION_EXTRUDE, OG_OPERATION_SUBTRACT, OG_OPERATION_SWEEP, + OG_PRIMITIVE_CIRCLE, OG_PRIMITIVE_CUBOID, OG_PRIMITIVE_CYLINDER, + OG_PRIMITIVE_POLYLINE, OG_PRIMITIVE_RECTANGLE, OG_TRANSFORM_TRANSLATE, +} from '../../../dist/index.js'; + +const VIEWPORT = requiredElement('#viewport', HTMLDivElement); +const STATUS = requiredElement('#status', HTMLElement); +const RESET_BUTTON = requiredElement('#reset-view', HTMLButtonElement); +const EDGES_BUTTON = requiredElement('#toggle-edges', HTMLButtonElement); +const EXPORT_BUTTON = requiredElement('#export-step', HTMLButtonElement); +await bootExample('./'); + +const SCENE = new THREE.Scene(); +SCENE.background = new THREE.Color(0xf1f5f8); +SCENE.add(new THREE.HemisphereLight(0xffffff, 0x526070, 2)); +const LIGHT = new THREE.DirectionalLight(0xffffff, 2); +LIGHT.position.set(5, 8, 5); +SCENE.add(LIGHT); +const CAMERA = new THREE.PerspectiveCamera(48, 1, 0.1, 100); +const RENDERER = new THREE.WebGLRenderer({ antialias: true }); +RENDERER.setPixelRatio(Math.min(window.devicePixelRatio || 1, 1.5)); +VIEWPORT.append(RENDERER.domElement); +const CONTROLS = new OrbitControls(CAMERA, RENDERER.domElement); +CONTROLS.enableDamping = false; +CONTROLS.minDistance = 4; +CONTROLS.maxDistance = 35; +CONTROLS.maxPolarAngle = Math.PI * 0.93; + +const SOLIDS: Solid[] = []; +const LEVEL = new SystemAssembly({ ogId: 'example-level' }); +const PLACE = (solid: Solid, offset: [number, number, number], color: number): Solid => { + solid.transform(OG_TRANSFORM_TRANSLATE, { offset }); + solid.setAppearance({ color }); + SOLIDS.push(solid); + SCENE.add(solid); + return solid; +}; + +PLACE(new Solid(OG_PRIMITIVE_CUBOID, { width: 7, height: 0.24, depth: 6 }, { ogId: 'floor' }), [0, -0.24, 0], 0xc7d6df); +const FLOOR_SURFACE = { width: 6.6, height: 0.07, depth: 5.6 }; +PLACE(new Solid(OG_PRIMITIVE_CUBOID, FLOOR_SURFACE, { ogId: 'floor-surface' }), [0, 0, 0], 0xe5edf1); + +const WALL_PROFILE = new Wire(OG_PRIMITIVE_RECTANGLE, { width: 6, breadth: 0.2 }, { ogId: 'wall-profile' }); +const WALL = new Solid(OG_OPERATION_EXTRUDE, { profile: WALL_PROFILE, distance: 3 }, { ogId: 'entry-wall' }); +const OPENING = new Solid(OG_PRIMITIVE_CUBOID, { width: 0.9, height: 2.1, depth: 0.4 }, { ogId: 'door-opening' }); +OPENING.transform(OG_TRANSFORM_TRANSLATE, { offset: [2, 0, 0] }); +WALL.operate(OG_OPERATION_SUBTRACT, { tools: [OPENING] }); +PLACE(WALL, [0, 0, -2.7], 0x7c9db3); +const SIDE_WALL = { width: 0.2, height: 2.45, depth: 4.9 }; +PLACE(new Solid(OG_PRIMITIVE_CUBOID, SIDE_WALL, { ogId: 'side-wall' }), [-3, 0, 0], 0xa7bcc9); + +const RAIL_PATH = new Wire( + OG_PRIMITIVE_POLYLINE, { points: [[0, 1, 1], [4, 1, 1], [4, 1, 4]], closed: false }, { ogId: 'rail-path' }, +); +const RAIL_PROFILE = new Wire(OG_PRIMITIVE_CIRCLE, { radius: 0.055 }, { + ogId: 'rail-profile', plane: { origin: [0, 1, 1], normal: [1, 0, 0], xDirection: [0, 0, 1] }, +}); +const RAIL = new Solid(OG_OPERATION_SWEEP, { profile: RAIL_PROFILE, path: RAIL_PATH }, { ogId: 'swept-rail' }); +PLACE(RAIL, [-2, 0, -2.2], 0x2476a5); + +const POST_LOCATIONS: [number, number, number][] = [ + [-2, 0, -1.2], [-1, 0, -1.2], [0, 0, -1.2], [1, 0, -1.2], [2, 0, -1.2], + [2, 0, -0.2], [2, 0, 0.8], [2, 0, 1.8], +]; +const POST = new Solid(OG_PRIMITIVE_CYLINDER, { radius: 0.055, height: 1 }, { ogId: 'rail-post-1' }); +const POSTS = POST_LOCATIONS.map((location, index) => ({ + solid: index === 0 ? POST : POST.instance({ ogId: `rail-post-${String(index + 1)}` }), + location, +})); +POSTS.forEach(({ solid, location }) => PLACE(solid, location, 0x2476a5)); +LEVEL.addChild(SOLIDS); +OPENING.dispose(); +WALL_PROFILE.dispose(); +RAIL_PATH.dispose(); +RAIL_PROFILE.dispose(); + +const GRID = new THREE.GridHelper(24, 24, 0xc3d1db, 0xd8e1e8); +GRID.position.y = -0.25; +SCENE.add(GRID); + +function resetView(): void { + const mobile = VIEWPORT.clientWidth < 600; + const scale = mobile ? 1.45 : 1; + CAMERA.position.set(8 * scale, 6 * scale, 10 * scale); + CONTROLS.target.set(0, 0.85, 0); + CONTROLS.update(); + render(); +} + +function resize(): void { + fitRenderer(VIEWPORT, CAMERA, RENDERER); + render(); +} + +function render(): void { RENDERER.render(SCENE, CAMERA); } + +const RESIZE_OBSERVER = new ResizeObserver(resize); +RESIZE_OBSERVER.observe(VIEWPORT); +CONTROLS.addEventListener('start', () => { OpenGeometry.setCameraMotion(true); }); +CONTROLS.addEventListener('end', () => { OpenGeometry.setCameraMotion(false); render(); }); +const UNSUBSCRIBE_ERROR = OpenGeometry.on('error', (error) => { + STATUS.textContent = `Geometry error: ${String(error)}`; +}); +RESET_BUTTON.addEventListener('click', resetView); +EDGES_BUTTON.addEventListener('click', () => { + const enabled = EDGES_BUTTON.getAttribute('aria-pressed') !== 'true'; + EDGES_BUTTON.setAttribute('aria-pressed', String(enabled)); + EDGES_BUTTON.textContent = enabled ? 'Hide edges' : 'Show edges'; + SOLIDS.forEach((solid) => { solid.setAppearance({ outline: enabled }); }); + render(); +}); +async function exportLevel(): Promise { + EXPORT_BUTTON.disabled = true; + STATUS.textContent = 'Exporting STEP…'; + try { + const result = await OpenGeometry.exportStep({ nodes: [LEVEL], name: 'OpenGeometry 2.5 example' }); + const url = URL.createObjectURL(new Blob([result.text], { type: 'application/step' })); + const link = document.createElement('a'); + link.href = url; + link.download = 'opengeometry-example.step'; + link.click(); + setTimeout(() => { URL.revokeObjectURL(url); }, 1000); + STATUS.textContent = 'STEP exported'; + } catch (error) { + STATUS.textContent = `Export failed: ${String(error)}`; + } finally { + EXPORT_BUTTON.disabled = false; + } +} +EXPORT_BUTTON.addEventListener('click', () => { void exportLevel(); }); + +resize(); +resetView(); +const READY = await OpenGeometry.settled(); +if (READY.failed.length) throw new Error(`${String(READY.failed.length)} solids failed to tessellate`); +render(); +STATUS.textContent = `${String(SOLIDS.length)} solids ready · drag to orbit · scroll to zoom`; +const STOP_FPS_METER = startFpsMeter(RENDERER, SCENE, CAMERA, CONTROLS); + +(window as typeof window & { __ogExample?: unknown }).__ogExample = { + camera: CAMERA, controls: CONTROLS, renderer: RENDERER, scene: SCENE, solids: SOLIDS, level: LEVEL, + render, resetView, + exportStep: () => OpenGeometry.exportStep({ nodes: [LEVEL] }), +}; + +releaseOnUnload({ + resizeObserver: RESIZE_OBSERVER, stopFpsMeter: STOP_FPS_METER, unsubscribeError: UNSUBSCRIBE_ERROR, + controls: CONTROLS, renderer: RENDERER, +}, () => undefined, () => { + SOLIDS.slice().reverse().forEach((solid) => { solid.dispose(); }); + LEVEL.dispose(); +}); diff --git a/main/opengeometry-three/examples-vite/showcase/opening.ts b/main/opengeometry-three/examples-vite/showcase/opening.ts new file mode 100644 index 0000000..f94c66e --- /dev/null +++ b/main/opengeometry-three/examples-vite/showcase/opening.ts @@ -0,0 +1,152 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import { numberControl, onNumberInput } from '../shared/controls'; +import { requiredElement } from '../shared/dom'; +import { startFpsMeter } from '../shared/fps'; +import { bootExample } from '../shared/kernel'; +import { releaseOnUnload } from '../shared/unload'; +import { narrowAwareResize } from '../shared/viewport'; +import { + OpenGeometry, Solid, + OG_OPERATION_SUBTRACT, OG_PRIMITIVE_CUBOID, OG_TRANSFORM_TRANSLATE, +} from '../../../dist/index.js'; + +const VIEWPORT = requiredElement('#app', HTMLDivElement); +const STATUS = requiredElement('#status', HTMLElement); +const WIDTH_INPUT = requiredElement('[data-control="width"] [data-range]', HTMLInputElement); +const HEIGHT_INPUT = requiredElement('[data-control="height"] [data-range]', HTMLInputElement); +const DEPTH_INPUT = requiredElement('[data-control="depth"] [data-range]', HTMLInputElement); +const CENTER_INPUT = requiredElement('[data-control="center-y"] [data-range]', HTMLInputElement); +await bootExample('./'); + +const SCENE = new THREE.Scene(); +SCENE.background = new THREE.Color(0xeef2ff); +SCENE.add(new THREE.HemisphereLight(0xffffff, 0x526070, 2)); +const LIGHT = new THREE.DirectionalLight(0xffffff, 2); +LIGHT.position.set(5, 8, 5); +SCENE.add(LIGHT); +SCENE.add(new THREE.GridHelper(20, 20, 0x4460ff, 0xd5ddff)); +const CAMERA = new THREE.PerspectiveCamera(48, 1, 0.1, 100); +const RENDERER = new THREE.WebGLRenderer({ antialias: true }); +RENDERER.setPixelRatio(Math.min(window.devicePixelRatio || 1, 1.5)); +VIEWPORT.append(RENDERER.domElement); +const CONTROLS = new OrbitControls(CAMERA, RENDERER.domElement); +CONTROLS.enableDamping = false; +CONTROLS.minDistance = 3; +CONTROLS.maxDistance = 30; + +type OpeningBodies = { wall: Solid; cutter: Solid }; +type OpeningValues = { width: number; height: number; depth: number; centerY: number }; + +let revision = 0; +let current: OpeningBodies | undefined; +let updateFrame = 0; +const VALUES = (): OpeningValues => ({ + width: Number(WIDTH_INPUT.value), height: Number(HEIGHT_INPUT.value), + depth: Number(DEPTH_INPUT.value), centerY: Number(CENTER_INPUT.value), +}); + +function createExample(): OpeningBodies { + const id = ++revision; + const created: Solid[] = []; + try { + const wallSize = { width: 4.4, height: 3.2, depth: 0.32 }; + const wall = new Solid(OG_PRIMITIVE_CUBOID, wallSize, { ogId: `wall-${String(id)}` }); + created.push(wall); + const { width, height, depth, centerY } = VALUES(); + const cutter = new Solid(OG_PRIMITIVE_CUBOID, { width, height, depth }, { ogId: `opening-${String(id)}` }); + created.push(cutter); + cutter.transform(OG_TRANSFORM_TRANSLATE, { offset: [0, centerY - height / 2, 0] }); + wall.operate(OG_OPERATION_SUBTRACT, { tools: [cutter] }); + wall.setAppearance({ color: 0xcbd5e1, outline: true, deflection: 0.005 }); + cutter.setAppearance({ color: 0xf97316, opacity: 0.22, outline: true, deflection: 0.005 }); + return { wall, cutter }; + } catch (error) { + created.slice().reverse().forEach((body) => { body.dispose(); }); + throw error; + } +} + +function render(): void { RENDERER.render(SCENE, CAMERA); } + +function resetView(): void { + const narrow = VIEWPORT.clientWidth < 700; + CAMERA.position.set(narrow ? 6 : 6, 4.5, narrow ? 12 : 8); + CONTROLS.target.set(0, 1.55, 0); + CONTROLS.update(); + render(); +} + +const RESIZE = narrowAwareResize(VIEWPORT, CAMERA, RENDERER, { resetView, render }); + +function finished(body: Solid): boolean { + return body.record?.revision === body.lastInfo.shapeRevision; +} + +function rebuild(): void { + let next: OpeningBodies | undefined; + const started = performance.now(); + try { + next = createExample(); + SCENE.add(next.wall, next.cutter); + SCENE.updateMatrixWorld(true); + OpenGeometry.flush({ geometry: 'sync' }); + if (!finished(next.wall) || !finished(next.cutter)) throw new Error('Opening geometry did not finish'); + const previous = current; + current = next; + if (previous) { previous.cutter.dispose(); previous.wall.dispose(); } + const faces = String(next.wall.getBrep().topology.faces.length); + STATUS.textContent = `Opening ready · ${faces} wall faces · ${(performance.now() - started).toFixed(1)} ms`; + } catch (error) { + if (next && next !== current) { next.cutter.dispose(); next.wall.dispose(); } + STATUS.textContent = `Opening error: ${String(error)}`; + } +} + +function scheduleRebuild(): void { + if (updateFrame) return; + updateFrame = window.requestAnimationFrame(() => { updateFrame = 0; rebuild(); }); +} + +function rebuildNow(): void { + if (updateFrame) window.cancelAnimationFrame(updateFrame); + updateFrame = 0; + rebuild(); +} + +function attachNumberControl(name: string): void { + const control = numberControl(name); + onNumberInput(control, () => { scheduleRebuild(); }); + control.range.addEventListener('change', rebuildNow); + control.number.addEventListener('change', rebuildNow); +} + +attachNumberControl('width'); +attachNumberControl('height'); +attachNumberControl('depth'); +attachNumberControl('center-y'); + +const RESIZE_OBSERVER = new ResizeObserver(RESIZE); +RESIZE_OBSERVER.observe(VIEWPORT); +CONTROLS.addEventListener('start', () => { OpenGeometry.setCameraMotion(true); }); +CONTROLS.addEventListener('end', () => { OpenGeometry.setCameraMotion(false); render(); }); +const UNSUBSCRIBE_ERROR = OpenGeometry.on('error', (error) => { + STATUS.textContent = `Geometry error: ${String(error)}`; +}); +RESIZE(); +resetView(); +rebuild(); +const STOP_FPS_METER = startFpsMeter(RENDERER, SCENE, CAMERA, CONTROLS); + +(window as typeof window & { __ogOpeningExample?: unknown }).__ogOpeningExample = { + camera: CAMERA, controls: CONTROLS, renderer: RENDERER, + get current() { return current; }, + render, rebuild, +}; + +releaseOnUnload({ + resizeObserver: RESIZE_OBSERVER, stopFpsMeter: STOP_FPS_METER, unsubscribeError: UNSUBSCRIBE_ERROR, + controls: CONTROLS, renderer: RENDERER, +}, () => { if (updateFrame) window.cancelAnimationFrame(updateFrame); }, () => { + if (current) { current.cutter.dispose(); current.wall.dispose(); } +}); diff --git a/main/opengeometry-three/examples-vite/showcase/sweep.ts b/main/opengeometry-three/examples-vite/showcase/sweep.ts new file mode 100644 index 0000000..47e0582 --- /dev/null +++ b/main/opengeometry-three/examples-vite/showcase/sweep.ts @@ -0,0 +1,199 @@ +import * as THREE from 'three'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import { numberControl, onNumberInput } from '../shared/controls'; +import { requiredElement } from '../shared/dom'; +import { startFpsMeter } from '../shared/fps'; +import { bootExample } from '../shared/kernel'; +import { releaseOnUnload } from '../shared/unload'; +import { fitRenderer } from '../shared/viewport'; +import { + OpenGeometry, Solid, Wire, + OG_OPERATION_SWEEP, OG_PRIMITIVE_POLYLINE, +} from '../../../dist/index.js'; + +type Point = [number, number, number]; +type Path = [Point, ...Point[]]; +type Frame = { + profile: Wire; + path: Wire; + solid: Solid; + guide: THREE.Line; + profileGuide: THREE.LineLoop; +}; + +const VIEWPORT = requiredElement('#app', HTMLDivElement); +const STATUS = requiredElement('#status', HTMLElement); +await bootExample('./'); + +const SCENE = new THREE.Scene(); +SCENE.background = new THREE.Color(0xeef2ff); +SCENE.add(new THREE.HemisphereLight(0xffffff, 0x526070, 2)); +const LIGHT = new THREE.DirectionalLight(0xffffff, 2); +LIGHT.position.set(5, 8, 5); +SCENE.add(LIGHT); +const CAMERA = new THREE.PerspectiveCamera(48, 1, 0.1, 100); +const RENDERER = new THREE.WebGLRenderer({ antialias: true }); +RENDERER.setPixelRatio(Math.min(window.devicePixelRatio || 1, 1.5)); +VIEWPORT.append(RENDERER.domElement); +const CONTROLS = new OrbitControls(CAMERA, RENDERER.domElement); +CONTROLS.enableDamping = false; +CONTROLS.minDistance = 4; +CONTROLS.maxDistance = 40; + +const CONFIG = { topRight: 2.3, topLeft: -1.25, width: 0.24, depth: 0.22 }; +const FRAMES: Frame[] = []; +const GUIDE_MATERIAL = (color: number): THREE.LineBasicMaterial => + new THREE.LineBasicMaterial({ color, depthTest: false }); +const PROFILE_POINTS = (start: Point, width: number, depth: number): Point[] => [ + [start[0], start[1] - width / 2, start[2] - depth / 2], + [start[0], start[1] + width / 2, start[2] - depth / 2], + [start[0], start[1] + width / 2, start[2] + depth / 2], + [start[0], start[1] - width / 2, start[2] + depth / 2], +]; +const PRIMARY_PATH = (): Path => [ + [-1.6, 0.1, 0], [1.6, 0.1, 0], [1.15, CONFIG.topRight, 0], [CONFIG.topLeft, 1.95, 0], +]; + +function setGuide(guide: THREE.Line, points: Point[]): void { + guide.geometry.dispose(); + guide.geometry = new THREE.BufferGeometry().setFromPoints(points.map((point) => new THREE.Vector3(...point))); +} + +function addFrame(id: string, points: Path, width: number, depth: number, color: number, guideColor: number): Frame { + const path = new Wire(OG_PRIMITIVE_POLYLINE, { points, closed: false }, { ogId: `${id}-path` }); + const profile = new Wire( + OG_PRIMITIVE_POLYLINE, { points: PROFILE_POINTS(points[0], width, depth), closed: true }, { ogId: `${id}-profile` }, + ); + const solid = new Solid(OG_OPERATION_SWEEP, { profile, path }, { ogId: id }); + solid.setAppearance({ color, outline: true, deflection: 0.01 }); + const guide = new THREE.Line(new THREE.BufferGeometry(), GUIDE_MATERIAL(guideColor)); + const profileGuide = new THREE.LineLoop(new THREE.BufferGeometry(), GUIDE_MATERIAL(0xc54b42)); + guide.position.z = 0.18; + guide.renderOrder = 10; + profileGuide.position.z = 0.24; + profileGuide.renderOrder = 11; + setGuide(guide, points); + setGuide(profileGuide, PROFILE_POINTS(points[0], width, depth)); + SCENE.add(solid, guide, profileGuide); + const frame = { profile, path, solid, guide, profileGuide }; + FRAMES.push(frame); + return frame; +} + +const PRIMARY = addFrame('adjustable-frame', PRIMARY_PATH(), CONFIG.width, CONFIG.depth, 0x3b82f6, 0x111827); +const DOOR = addFrame( + 'door-frame', [[-6.1, 0.1, 0], [-4.3, 0.1, 0], [-4.3, 2.8, 0], [-6.1, 2.8, 0]], 0.2, 0.18, 0xd97706, 0x7c2d12, +); +const WINDOW_FRAME = addFrame( + 'window-frame', [[3.8, 0.75, 0], [6.4, 0.75, 0], [6.1, 2.35, 0], [4.1, 2.1, 0]], 0.18, 0.16, 0x0f766e, 0x134e4a, +); +SCENE.add(new THREE.GridHelper(20, 20, 0x4460ff, 0xd5ddff)); + +function render(): void { RENDERER.render(SCENE, CAMERA); } + +function resetView(): void { + const narrow = VIEWPORT.clientWidth < 700; + DOOR.solid.visible = DOOR.guide.visible = DOOR.profileGuide.visible = !narrow; + WINDOW_FRAME.solid.visible = WINDOW_FRAME.guide.visible = WINDOW_FRAME.profileGuide.visible = !narrow; + CAMERA.position.set(narrow ? 1 : 0.6, 2.7, narrow ? 15 : 17.5); + CONTROLS.target.set(narrow ? 0.6 : 0, 1.35, 0); + CONTROLS.update(); + render(); +} + +let previousNarrow: boolean | undefined; +function resize(): void { + const narrow = fitRenderer(VIEWPORT, CAMERA, RENDERER) < 700; + DOOR.solid.visible = DOOR.guide.visible = DOOR.profileGuide.visible = !narrow; + WINDOW_FRAME.solid.visible = WINDOW_FRAME.guide.visible = WINDOW_FRAME.profileGuide.visible = !narrow; + if (previousNarrow !== narrow) { + previousNarrow = narrow; + resetView(); + } else render(); +} + +let updateFrame = 0; +function updateSweep(): void { + const points = PRIMARY_PATH(); + const started = performance.now(); + try { + PRIMARY.path.rebuild(OG_PRIMITIVE_POLYLINE, { points, closed: false }); + PRIMARY.profile.rebuild(OG_PRIMITIVE_POLYLINE, { + points: PROFILE_POINTS(points[0], CONFIG.width, CONFIG.depth), closed: true, + }); + PRIMARY.solid.rebuild(OG_OPERATION_SWEEP, { profile: PRIMARY.profile, path: PRIMARY.path }); + setGuide(PRIMARY.guide, points); + setGuide(PRIMARY.profileGuide, PROFILE_POINTS(points[0], CONFIG.width, CONFIG.depth)); + SCENE.updateMatrixWorld(true); + OpenGeometry.flush({ geometry: 'sync' }); + if (PRIMARY.solid.record?.revision !== PRIMARY.solid.lastInfo.shapeRevision) { + throw new Error('Sweep geometry did not finish'); + } + STATUS.textContent = `Sweep ready · ${(performance.now() - started).toFixed(1)} ms`; + } catch (error) { + STATUS.textContent = `Sweep error: ${String(error)}`; + } +} + +function scheduleUpdate(): void { + if (updateFrame) return; + updateFrame = window.requestAnimationFrame(() => { updateFrame = 0; updateSweep(); }); +} + +function updateNow(): void { + if (updateFrame) window.cancelAnimationFrame(updateFrame); + updateFrame = 0; + updateSweep(); +} + +function attachNumberControl(name: string, key: keyof typeof CONFIG): void { + const control = numberControl(name); + onNumberInput(control, (value) => { + CONFIG[key] = value; + scheduleUpdate(); + }); + control.range.addEventListener('change', updateNow); + control.number.addEventListener('change', updateNow); +} + +attachNumberControl('top-right-y', 'topRight'); +attachNumberControl('top-left-x', 'topLeft'); +attachNumberControl('frame-width', 'width'); +attachNumberControl('frame-depth', 'depth'); + +const RESIZE_OBSERVER = new ResizeObserver(resize); +RESIZE_OBSERVER.observe(VIEWPORT); +CONTROLS.addEventListener('start', () => { OpenGeometry.setCameraMotion(true); }); +CONTROLS.addEventListener('end', () => { OpenGeometry.setCameraMotion(false); render(); }); +const UNSUBSCRIBE_ERROR = OpenGeometry.on('error', (error) => { + STATUS.textContent = `Geometry error: ${String(error)}`; +}); + +resize(); +resetView(); +const READY = await OpenGeometry.settled(); +if (READY.failed.length) throw new Error(`${String(READY.failed.length)} sweeps failed to tessellate`); +render(); +STATUS.textContent = 'Sweep ready'; +const STOP_FPS_METER = startFpsMeter(RENDERER, SCENE, CAMERA, CONTROLS); + +(window as typeof window & { __ogSweepExample?: unknown }).__ogSweepExample = { + camera: CAMERA, controls: CONTROLS, renderer: RENDERER, frames: FRAMES, + render, resetView, updateSweep, + exportStep: () => OpenGeometry.exportStep({ nodes: FRAMES.map((frame) => frame.solid) }), +}; + +releaseOnUnload({ + resizeObserver: RESIZE_OBSERVER, stopFpsMeter: STOP_FPS_METER, unsubscribeError: UNSUBSCRIBE_ERROR, + controls: CONTROLS, renderer: RENDERER, +}, () => { if (updateFrame) window.cancelAnimationFrame(updateFrame); }, () => { + FRAMES.slice().reverse().forEach((frame) => { + frame.solid.dispose(); + frame.profile.dispose(); + frame.path.dispose(); + frame.guide.geometry.dispose(); + frame.guide.material.dispose(); + frame.profileGuide.geometry.dispose(); + frame.profileGuide.material.dispose(); + }); +}); diff --git a/main/opengeometry-three/examples-vite/sweep.html b/main/opengeometry-three/examples-vite/sweep.html new file mode 100644 index 0000000..8b5c5bb --- /dev/null +++ b/main/opengeometry-three/examples-vite/sweep.html @@ -0,0 +1,79 @@ + + + + OpenGeometry Sweep Example + + + + + + + +
+ Back + Sweep +
+ + + +
+ + + + + diff --git a/main/opengeometry-three/examples-vite/src/styles/theme.css b/main/opengeometry-three/examples-vite/theme.css similarity index 80% rename from main/opengeometry-three/examples-vite/src/styles/theme.css rename to main/opengeometry-three/examples-vite/theme.css index bdd826d..a9fc9f6 100644 --- a/main/opengeometry-three/examples-vite/src/styles/theme.css +++ b/main/opengeometry-three/examples-vite/theme.css @@ -26,6 +26,7 @@ html, body { margin: 0; width: 100%; + height: 100%; min-height: 100%; background: radial-gradient(circle at top left, rgba(68, 96, 255, 0.16), rgba(255, 255, 255, 0)), @@ -54,6 +55,12 @@ code { height: 100%; } +#app canvas { + display: block; + width: 100%; + height: 100%; +} + .og-badge-title-card, .og-controls { corner-shape: squircle; @@ -310,3 +317,58 @@ code { bottom: 10px; } } + +* { box-sizing: border-box; } + +html, body, #app { width: 100%; height: 100%; margin: 0; } + +body { overflow: hidden; font: 13px system-ui, sans-serif; } + +#app { position: relative; height: 100dvh; } + +#viewport { width: 100%; height: 100%; } + +#viewport canvas { display: block; width: 100%; height: 100%; touch-action: none; } + +#controls { + position: absolute; + top: 16px; + left: 16px; + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; + max-width: calc(100% - 32px); + padding: 8px; + border: 1px solid #d8e1e8; + border-radius: 9px; + background: rgba(255, 255, 255, .94); + box-shadow: 0 3px 12px #17304716; +} + +button, .example-link { + display: inline-flex; + align-items: center; + text-decoration: none; + min-height: 34px; + padding: 0 11px; + border: 1px solid #c4d2dc; + border-radius: 5px; + background: white; + color: #173047; + font: inherit; + cursor: pointer; +} + +button:hover, button[aria-pressed="true"], .example-link:hover { background: #e6f1f8; } + +button:focus-visible, .example-link:focus-visible { outline: 2px solid #2e7eb3; outline-offset: 2px; } + +button:disabled { opacity: .5; cursor: wait; } + +#status { padding: 0 5px; color: #42566a; } + +@media (max-width: 560px) { + #controls { top: 8px; left: 8px; max-width: calc(100% - 16px); } + #status { flex-basis: 100%; padding: 0 3px 2px; } +} diff --git a/main/opengeometry-three/examples-vite/tsconfig.json b/main/opengeometry-three/examples-vite/tsconfig.json index 7561cd8..150a096 100644 --- a/main/opengeometry-three/examples-vite/tsconfig.json +++ b/main/opengeometry-three/examples-vite/tsconfig.json @@ -1,18 +1,3 @@ { - "compilerOptions": { - "target": "ES2020", - "module": "ESNext", - "moduleResolution": "Bundler", - "strict": true, - "baseUrl": ".", - "paths": { - "@og-three": ["../index.ts"] - }, - "types": ["vite/client"], - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "skipLibCheck": true, - "noEmit": true - }, - "include": ["src/**/*.ts"] + "extends": "../../tsconfig.examples.json" } diff --git a/main/opengeometry-three/examples-vite/vite.config.ts b/main/opengeometry-three/examples-vite/vite.config.ts new file mode 100644 index 0000000..449c1ad --- /dev/null +++ b/main/opengeometry-three/examples-vite/vite.config.ts @@ -0,0 +1,28 @@ +import { defineConfig } from 'vite'; + +const ROOT = decodeURIComponent(new URL('.', import.meta.url).pathname); + +export default defineConfig({ + root: ROOT, + cacheDir: `../../node_modules/.vite/examples-three-${process.env.OG_THREE_VERSION ?? '168'}`, + ...(process.env.OG_THREE_VERSION === '184' ? { resolve: { alias: { three: 'three-184' } } } : {}), + publicDir: decodeURIComponent(new URL('../../dist', import.meta.url).pathname), + build: { + target: 'esnext', + outDir: '../examples-dist', + emptyOutDir: true, + rollupOptions: { + input: { + index: `${ROOT}/index.html`, + sweep: `${ROOT}/sweep.html`, + boolean: `${ROOT}/boolean.html`, + analyticBoolean: `${ROOT}/operations/analytic-boolean-operations.html`, + opening: `${ROOT}/opening.html`, + cuboid: `${ROOT}/shapes/cuboid.html`, + cylinder: `${ROOT}/shapes/cylinder.html`, + stepExport: `${ROOT}/operations/step-export.html`, + }, + }, + }, + optimizeDeps: { exclude: ['opengeometry'] }, +}); diff --git a/main/opengeometry-three/index.ts b/main/opengeometry-three/index.ts index ae4a0d3..9a8b29b 100644 --- a/main/opengeometry-three/index.ts +++ b/main/opengeometry-three/index.ts @@ -1,169 +1,7 @@ -/** - * Public OpenGeometry JavaScript entrypoint. - */ -import init, { - OGAnalyticBrep, - OGAnalyticTessellation, - WorkPlane, - exportBrepToIfc, - exportBrepToStep, - exportBrepToStl, - analyzeProfile, - extrudeBrepFaceDirectional, - validateAnalyticBrep, - tessellate_brep, - Vector3, -} from "../opengeometry/pkg/opengeometry"; -import { SpotLabel } from "./src/markup/spotMarker"; -import { OPEN_GEOMETRY_THREE_VERSION, OpenGeometryOptions } from "./src/base-types"; - -export type OUTLINE_TYPE = "front" | "side" | "top"; - -/** - * Binary STL export payload returned by export helpers. - */ -export interface OGStlExportResult { - bytes: Uint8Array; - reportJson: string; -} - -/** - * STEP text export payload returned by export helpers. - */ -export interface OGStepExportResult { - text: string; - reportJson: string; -} - -/** - * IFC text export payload returned by export helpers. - */ -export interface OGIfcExportResult { - text: string; - reportJson: string; -} - -/** - * Shared runtime entrypoint used to initialize the OpenGeometry wasm module. - */ -export class OpenGeometry { - static version = OPEN_GEOMETRY_THREE_VERSION; - static instance: OpenGeometry | null = null; - - private _enableDebug: boolean = false; - - get enableDebug() { - return this._enableDebug; - } - - /** - * Enables or disables debug mode for OpenGeometry. - * When enabled, it logs debug information to the console. - * Addtionally, - * 1. The geometry will be rendered with a semi-transparent material. - * 2. The faces will be rendered with a random color. - * 3. The normals will be rendered for better visualization. - * @param value - A boolean indicating whether to enable or disable debug mode. - */ - set enableDebug(value: boolean) { - this._enableDebug = value; - if (this._enableDebug) { - console.log("OpenGeometry Debug Mode Enabled"); - } - } - - constructor() {} - - /** - * Asynchronously initializes the OpenGeometry wasm runtime and returns a - * cached singleton instance on subsequent calls. - * - * Call this before constructing `Vector3` or any kernel-backed wrapper. - * - * @param options - Configuration object for initializing OpenGeometry. - * @returns A promise that resolves to a fully initialized OpenGeometry instance. - * - * @example - * ```ts - * await OpenGeometry.create({ - * wasmURL: "/opengeometry_bg.wasm", - * }); - * ``` - */ - static async create(options: OpenGeometryOptions) { - if (!OpenGeometry.instance) { - const og = new OpenGeometry(); - await og.setup(options.wasmURL); - OpenGeometry.instance = og; - } - return OpenGeometry.instance; - } - - private async setup(wasmURL?: string) { - if (wasmURL) { - await init({ module_or_path: wasmURL }); - return; - } - - await init(); - } -} - -export { exportBrepToIfc, exportBrepToStep, exportBrepToStl }; -export { OGAnalyticBrep, OGAnalyticTessellation }; -export { WorkPlane, analyzeProfile, extrudeBrepFaceDirectional, validateAnalyticBrep }; -export { tessellate_brep }; -export { AnalyticSolid } from "./src/shapes/analytic-solid"; -export { WorldGraph, WorldGraphError, matrixToWorldTransform } from "./src/world/world-graph"; -export type { - WorldClash, WorldClashKind, WorldClashOptions, WorldExportedFile, WorldExportedMesh, WorldGraphErrorCode, WorldNodeOptions, - WorldProjectedLine, WorldProjectedLines, WorldProjectedScene, WorldProjectedSegment, WorldProjectionView, WorldProximity, WorldTransform, -} from "./src/world/world-graph"; -export { AnalyticPattern, circularPattern, linearPattern, rectangularPattern } from "./src/shapes/analytic-pattern"; -export type { AnalyticPatternHit, AnalyticPatternInstance } from "./src/shapes/analytic-pattern"; -export type { - AnalyticBooleanOp, AnalyticBooleanReport, AnalyticBrepOptions, AnalyticFrame, AnalyticAccuracy, AnalyticAnnularSectorOpening, AnalyticProfileEdge, AnalyticPolygonLoftAlignment, AnalyticProjectionCamera, AnalyticProjectionHlr, AnalyticPrimitiveOptions, AnalyticTessellationStats, AnalyticTessellationOptions, AnalyticStepExport, AnalyticStlExport, -} from "./src/shapes/analytic-solid"; - -/** - * Shared wasm-backed vector type used throughout the public API. - * - * Initialize `OpenGeometry.create(...)` before constructing `Vector3`. - */ -export { Vector3 }; - -export { SpotLabel }; - -/** - * Primitive wrappers (line/polyline/arc/rectangle/curve). - */ -export * from "./src/primitives/index"; -/** - * Shape wrappers (polygon/cuboid/cylinder/wedge/opening/sweep/sphere). - */ -export * from "./src/shapes/index"; -/** - * Reusable example builders for quickly wiring demo scenes. - */ -export * from "./src/examples/index"; -/** - * First-class freeform geometry wrapper around the kernel OGFreeformGeometry API. - */ -export * from "./src/freeform/index"; -/** - * Editing helpers that operate on parametric entities and freeform geometry. - */ -export * from "./src/editor/index"; -/** - * Kernel-backed modeling operations (incl. analytic polyline offset → regions). - */ -export * from "./src/operations/index"; - -export { AnalyticCurve } from "./src/primitives/analytic-curve"; -export type { AnalyticCurveOptions, AnalyticCurveStats } from "./src/primitives/analytic-curve"; -export { AnalyticTessellationWorker } from "./src/rendering/analytic-tessellation"; -export type { AnalyticWorkerOptions, AnalyticTessellationData } from "./src/rendering/analytic-tessellation"; -export { AnalyticLodController, deflectionBucket, worldUnitsPerPixel, uniformScale } from "./src/rendering/analytic-lod"; -export type { AnalyticLodOptions } from "./src/rendering/analytic-lod"; -export { analyticIfcText } from "./src/export/analytic-ifc"; -export type { AnalyticExchangeBodyV2, IfcPreparedValue } from "./src/export/analytic-ifc"; +export { OpenGeometry } from './src/opengeometry'; +export { OGMark } from './src/marks/og-mark'; +export { SystemAssembly } from './src/bodies/system-assembly'; +export { Wire } from './src/bodies/wire'; +export { Solid } from './src/bodies/solid'; +export { OGError } from './src/errors'; +export * from './src/constants/index'; diff --git a/main/opengeometry-three/package-lock.json b/main/opengeometry-three/package-lock.json deleted file mode 100644 index 78d2249..0000000 --- a/main/opengeometry-three/package-lock.json +++ /dev/null @@ -1,307 +0,0 @@ -{ - "name": "@opengeometry/kernel-three", - "version": "2.0.3", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@opengeometry/kernel-three", - "version": "2.0.3", - "license": "MIT", - "dependencies": { - "@fortawesome/fontawesome-svg-core": "^6.7.2", - "@fortawesome/free-solid-svg-icons": "^6.7.2", - "@opengeometry/openmaths": "^0.2.3" - }, - "devDependencies": { - "typedoc": "^0.28.14" - } - }, - "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", - "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz", - "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==", - "license": "MIT", - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.7.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz", - "integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==", - "license": "(CC-BY-4.0 AND MIT)", - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.7.2" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@gerrit0/mini-shiki": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.23.0.tgz", - "integrity": "sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/engine-oniguruma": "^3.23.0", - "@shikijs/langs": "^3.23.0", - "@shikijs/themes": "^3.23.0", - "@shikijs/types": "^3.23.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@opengeometry/openmaths": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@opengeometry/openmaths/-/openmaths-0.2.3.tgz", - "integrity": "sha512-WgMeBkpjGzSLf6YZHDB6KBqpAoqec9iwLLsY7fPWYxG5+1O1A631Sdgg42RDTSiVtvXRXRNgYiuhI+soqJGshA==", - "license": "MIT/Apache-2.0" - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", - "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", - "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", - "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0" - } - }, - "node_modules/@shikijs/types": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", - "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "uc.micro": "^2.0.0" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true, - "license": "MIT" - }, - "node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/typedoc": { - "version": "0.28.17", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.17.tgz", - "integrity": "sha512-ZkJ2G7mZrbxrKxinTQMjFqsCoYY6a5Luwv2GKbTnBCEgV2ihYm5CflA9JnJAwH0pZWavqfYxmDkFHPt4yx2oDQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@gerrit0/mini-shiki": "^3.17.0", - "lunr": "^2.3.9", - "markdown-it": "^14.1.0", - "minimatch": "^9.0.5", - "yaml": "^2.8.1" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 18", - "pnpm": ">= 10" - }, - "peerDependencies": { - "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true, - "license": "MIT" - }, - "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - } - } -} diff --git a/main/opengeometry-three/package.json b/main/opengeometry-three/package.json deleted file mode 100644 index f59be68..0000000 --- a/main/opengeometry-three/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "@opengeometry/kernel-three", - "author": "Vishwajeet Vinayak Mane", - "license": "MPL-2.0", - "version": "2.0.14", - "description": "Three.js adapter for OpenGeometry, a CAD kernel for the web built with Rust and WebAssembly.", - "main": "index.js", - "homepage": "https://docs.opengeometry.io", - "scripts": { - "dev-example-three": "vite --config ./vite.examples.config.mjs", - "build-example-three": "vite build --config ./vite.examples.config.mjs && node ./scripts/copy-wasm-assets.mjs", - "preview-example-three": "vite preview --config ./vite.examples.config.mjs", - "docs:api": "typedoc --options ./typedoc.json", - "docs:api:watch": "typedoc --options ./typedoc.json --watch" - }, - "keywords": [ - "opengeometry", - "cad", - "cad-kernel", - "browser-cad", - "threejs", - "three.js", - "geometry", - "webassembly", - "wasm", - "ifc", - "step", - "stl", - "parametric-modeling" - ], - "dependencies": { - "@opengeometry/openmaths": "^0.2.3" - }, - "devDependencies": { - "@fortawesome/fontawesome-svg-core": "^6.7.2", - "@fortawesome/free-solid-svg-icons": "^6.7.2", - "typedoc": "^0.28.14" - } -} diff --git a/main/opengeometry-three/scripts/copy-wasm-assets.mjs b/main/opengeometry-three/scripts/copy-wasm-assets.mjs deleted file mode 100644 index ea3769d..0000000 --- a/main/opengeometry-three/scripts/copy-wasm-assets.mjs +++ /dev/null @@ -1,25 +0,0 @@ -import { copyFileSync, existsSync, mkdirSync } from "node:fs"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); -const packageRoot = resolve(__dirname, ".."); - -const sourceWasm = resolve(packageRoot, "../opengeometry/pkg/opengeometry_bg.wasm"); -const sourceWasmTypes = resolve(packageRoot, "../opengeometry/pkg/opengeometry_bg.wasm.d.ts"); -const targetDir = resolve(packageRoot, "examples-dist/assets/wasm"); - -mkdirSync(targetDir, { recursive: true }); - -if (!existsSync(sourceWasm)) { - throw new Error(`WASM source not found at ${sourceWasm}`); -} - -copyFileSync(sourceWasm, resolve(targetDir, "opengeometry_bg.wasm")); - -if (existsSync(sourceWasmTypes)) { - copyFileSync(sourceWasmTypes, resolve(targetDir, "opengeometry_bg.wasm.d.ts")); -} - -console.log(`Copied wasm assets to ${targetDir}`); diff --git a/main/opengeometry-three/scripts/examples-header-script.mjs b/main/opengeometry-three/scripts/examples-header-script.mjs deleted file mode 100644 index 92d5230..0000000 --- a/main/opengeometry-three/scripts/examples-header-script.mjs +++ /dev/null @@ -1,131 +0,0 @@ -const MIXPANEL_MARKER = ""; -const MIXPANEL_TOKEN = "46cc87ad3cea4d093ec0bc61b0b774d9"; -const MIXPANEL_HOST = "https://api-eu.mixpanel.com"; - -function normalizePagePath(pagePath) { - if (!pagePath || pagePath === "/") { - return "/index.html"; - } - - return pagePath.endsWith(".html") ? pagePath : `${pagePath}.html`; -} - -function createMixpanelSnippet(pagePath) { - return ` -`; -} - -export function createExamplesHeaderPlugin() { - return { - name: "opengeometry-examples-mixpanel", - apply: "build", - transformIndexHtml(html, context) { - if (html.includes("mixpanel.init(")) { - return html; - } - - const pagePath = normalizePagePath(context?.path); - const snippet = createMixpanelSnippet(pagePath); - - if (html.includes(MIXPANEL_MARKER)) { - return html.replace(MIXPANEL_MARKER, snippet); - } - - if (!html.includes("")) { - return html; - } - - return html.replace("", `${snippet}\n `); - }, - }; -} - -export const createExamplesMixpanelPlugin = createExamplesHeaderPlugin; diff --git a/main/opengeometry-three/src/base-types.ts b/main/opengeometry-three/src/base-types.ts deleted file mode 100644 index a3434e8..0000000 --- a/main/opengeometry-three/src/base-types.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Version string exposed by the Three.js adapter at runtime. - */ -export const OPEN_GEOMETRY_THREE_VERSION = "2.0.3"; - -/** - * Options accepted when initializing the OpenGeometry runtime. - * - `wasmURL`: Optional URL to the OpenGeometry wasm binary. If not provided, it defaults to a relative path. - * - `debugMeshes`: Optional flag to enable debug meshes for visualizing geometry. Defaults to `false`. - */ -export interface OpenGeometryOptions { - wasmURL?: string; - debugMeshes?: boolean; -} diff --git a/main/opengeometry-three/src/bodies/body-factory.ts b/main/opengeometry-three/src/bodies/body-factory.ts new file mode 100644 index 0000000..7a147f9 --- /dev/null +++ b/main/opengeometry-three/src/bodies/body-factory.ts @@ -0,0 +1,32 @@ +import { call } from '../kernel/kernel-session'; +import { encode, operationParams, polylinePoints } from '../world-graph/codec'; +import { creation, worldGraph } from '../world-graph/world-graph-client'; +import type { BodyOptions } from './body-options'; + +export function existingParams(ogId: string): Record { + return { existing: existingNodeId, ogId }; +} + +export function createBody( + kind: string, params: Record, options: BodyOptions, bodyType: 'Wire' | 'Solid', +): string { + const existing = existingNodeId(params); + if (existing !== undefined) return existing; + const graph = worldGraph(); + const settings = { ogId: options.ogId, parent: options.parent?.ogId, plane: options.plane, bodyType }; + const label = `${bodyType}.constructor`; + if (kind === 'Polyline') { + const points = polylinePoints(params, label); + const closed = Boolean(params['closed']); + return creation(call(label, () => graph.createPolyline(points, closed, encode(settings))), label); + } + const operation = kind === 'Extrude' || kind === 'Sweep'; + return creation(call(label, () => operation + ? graph.createOperation(encode(operationParams(kind, params, label)), encode(settings)) + : graph.createPrimitive(encode({ kind, ...params }), encode(settings))), label); +} + +function existingNodeId(params: Record): string | undefined { + const ogId = params['ogId']; + return params['existing'] === existingNodeId && typeof ogId === 'string' ? ogId : undefined; +} diff --git a/main/opengeometry-three/src/bodies/body-options.ts b/main/opengeometry-three/src/bodies/body-options.ts new file mode 100644 index 0000000..5433e6a --- /dev/null +++ b/main/opengeometry-three/src/bodies/body-options.ts @@ -0,0 +1,19 @@ +import type { SystemAssembly } from './system-assembly'; + +export type BodyOptions = { + ogId?: string; + parent?: SystemAssembly; + plane?: { + origin?: [number, number, number]; + normal?: [number, number, number]; + xDirection?: [number, number, number]; + }; + appearance?: Partial; +}; +export type Appearance = { + color: number; + opacity: number; + outline: boolean; + pickOutline: boolean; + deflection?: number; +}; diff --git a/main/opengeometry-three/src/bodies/body-placement.ts b/main/opengeometry-three/src/bodies/body-placement.ts new file mode 100644 index 0000000..bd97f95 --- /dev/null +++ b/main/opengeometry-three/src/bodies/body-placement.ts @@ -0,0 +1,42 @@ +import * as THREE from 'three'; +import { emit } from '../runtime/event-bus'; + +export type PoseMemory = { + position: THREE.Vector3; + quaternion: THREE.Quaternion; + scale: THREE.Vector3; + parentWorld: THREE.Matrix4; +}; + +export function poseMemory(): PoseMemory { + return { + position: new THREE.Vector3(), quaternion: new THREE.Quaternion(), scale: new THREE.Vector3(1, 1, 1), + parentWorld: new THREE.Matrix4(), + }; +} + +export function derivePose(memory: PoseMemory, object: THREE.Object3D): void { + if (object.parent) { + object.matrix.copy(object.parent.matrixWorld).invert().multiply(object.matrixWorld); + memory.parentWorld.copy(object.parent.matrixWorld); + } else { + object.matrix.copy(object.matrixWorld); + memory.parentWorld.identity(); + } + object.matrix.decompose(object.position, object.quaternion, object.scale); + memory.position.copy(object.position); + memory.quaternion.copy(object.quaternion); + memory.scale.copy(object.scale); +} + +export function keepPose(memory: PoseMemory, object: THREE.Object3D, ogId: string): void { + if (!object.position.equals(memory.position) || !object.quaternion.equals(memory.quaternion) + || !object.scale.equals(memory.scale)) { + emit('warning', { code: 'PlacementOverwritten', ogId }); + object.position.copy(memory.position); + object.quaternion.copy(memory.quaternion); + object.scale.copy(memory.scale); + } + const parentWorld = object.parent ? object.parent.matrixWorld : new THREE.Matrix4(); + if (!parentWorld.equals(memory.parentWorld)) derivePose(memory, object); +} diff --git a/main/opengeometry-three/src/bodies/body-registry.ts b/main/opengeometry-three/src/bodies/body-registry.ts new file mode 100644 index 0000000..67ce361 --- /dev/null +++ b/main/opengeometry-three/src/bodies/body-registry.ts @@ -0,0 +1,23 @@ +import { bodyKey, reindexShape, releaseShapeIfEmpty, runtime } from '../runtime/runtime-state'; +import type { Body } from './body'; + +export function register(body: Body): void { + const state = runtime(); + state.bodies.set(bodyKey(body.handle, body.generation), body); + state.pickTargets.set(body.surface, body).set(body.outline, body); + reindexShape(state, body, null); +} + +export function unregister(body: Body): void { + const state = runtime(); + state.displayed.delete(body); + state.limbo.delete(body); + state.bodies.delete(bodyKey(body.handle, body.generation)); + state.pickTargets.delete(body.surface); + state.pickTargets.delete(body.outline); + if (body.record) state.records.release(body.record); + const info = body.lastInfo; + if (!info.shapeId) return; + state.byShape.get(info.shapeId)?.delete(body); + releaseShapeIfEmpty(state, info.shapeId, info.shapeRevision ?? 0); +} diff --git a/main/opengeometry-three/src/bodies/body.ts b/main/opengeometry-three/src/bodies/body.ts new file mode 100644 index 0000000..e8b0126 --- /dev/null +++ b/main/opengeometry-three/src/bodies/body.ts @@ -0,0 +1,254 @@ +import * as THREE from 'three'; +import type { Brep } from '../dto/brep'; +import type { NodeInfo } from '../dto/node-info'; +import type { Placement } from '../dto/placement'; +import { OGError } from '../errors'; +import { call } from '../kernel/kernel-session'; +import { enterLimbo, revive } from '../marks/limbo'; +import { flush, wanted } from '../rendering/geometry/geometry-scheduler'; +import { noteDisplayed } from '../rendering/geometry/render-pass'; +import { observeCamera } from '../rendering/lod/camera-observer'; +import { overrideChanged } from '../rendering/lod/lod-controller'; +import { + lineMaterial, releaseLine, releaseSurface, surfaceMaterial, type MaterialAppearance, +} from '../rendering/materials/material-pool'; +import type { GeometryRecord } from '../rendering/records/geometry-record'; +import { bodyKey, runtime } from '../runtime/runtime-state'; +import { encode, operationParams, polylinePoints, scope } from '../world-graph/codec'; +import { node, worldGraph } from '../world-graph/world-graph-client'; +import type { Appearance, BodyOptions } from './body-options'; +import { derivePose, keepPose, poseMemory } from './body-placement'; +import { register, unregister } from './body-registry'; +import { DisplayClone } from './display-clone'; +import { + addChild, checkNode, getBounds, getBrep, getChildren, getInstanceCount, getParent, getPlacement, getWorldPlacement, + localBounds, removeChild, +} from './node-methods'; +import type { SystemAssembly } from './system-assembly'; + +function singleMaterial(material: THREE.Material | THREE.Material[], label: string): THREE.Material { + if (Array.isArray(material)) throw new OGError('InvalidOperand', label, 'display material must be a single material'); + return material; +} + +function lineStyle(bodyType: 'Wire' | 'Solid', appearance: Appearance): MaterialAppearance { + return { color: bodyType === 'Wire' ? appearance.color : 0x243040, opacity: appearance.opacity }; +} + +export abstract class Body extends THREE.Group { + readonly ogId: string; + readonly handle: number; + readonly generation: number; + readonly surface: THREE.Mesh; + readonly outline: THREE.LineSegments; + readonly bodyType: 'Wire' | 'Solid'; + appearance: Appearance; + lastInfo: NodeInfo; + record?: GeometryRecord; + inLimbo = false; + private surfaceKey: string; + private lineKey: string; + private readonly epoch = runtime().epoch; + private readonly pose = poseMemory(); + + protected constructor(ogId: string, bodyType: 'Wire' | 'Solid', options: BodyOptions) { + super(); + this.ogId = ogId; + this.lastInfo = node(ogId, `${bodyType}.constructor`); + this.handle = this.lastInfo.handle; + this.generation = this.lastInfo.generation; + this.bodyType = bodyType; + this.appearance = { + color: bodyType === 'Wire' ? 0x2563eb : 0x6699dd, opacity: 1, outline: bodyType === 'Wire', + pickOutline: bodyType === 'Wire', ...options.appearance, + }; + this.matrixAutoUpdate = false; + this.matrixWorldAutoUpdate = false; + const { shapeId, shapeRevision } = this.lastInfo; + const placeholder = shapeId && shapeRevision !== null + ? runtime().records.placeholder(shapeId, shapeRevision, () => localBounds(`${bodyType}.constructor`, ogId)) + : undefined; + const surfaceStyle = surfaceMaterial(this.appearance); + this.surfaceKey = surfaceStyle.key; + this.surface = new THREE.Mesh(placeholder ?? new THREE.BufferGeometry(), surfaceStyle.material); + this.surface.visible = Boolean(placeholder) && bodyType === 'Solid'; + const outlineStyle = lineMaterial(lineStyle(bodyType, this.appearance)); + this.lineKey = outlineStyle.key; + this.outline = new THREE.LineSegments(placeholder ?? new THREE.BufferGeometry(), outlineStyle.material); + this.outline.visible = Boolean(placeholder) && this.appearance.outline; + if (!this.appearance.pickOutline) this.outline.raycast = THREE.Object3D.prototype.raycast.bind(this.outline); + this.add(this.surface, this.outline); + this.surface.onBeforeRender = (renderer, _scene, camera) => { observeCamera(renderer, camera); }; + this.outline.onBeforeRender = (renderer, _scene, camera) => { observeCamera(renderer, camera); }; + register(this); + this.applyWorldMatrix(call(`${bodyType}.constructor`, () => worldGraph().worldMatrix(ogId))); + } + + protected check(call: string): void { checkNode(call, 'body', this.epoch, this.handle, this.generation); } + + applyWorldMatrix(values: ArrayLike): void { + this.matrixWorld.fromArray(values); + derivePose(this.pose, this); + super.updateMatrixWorld(true); + } + + override updateMatrixWorld(force?: boolean): void { + keepPose(this.pose, this, this.ogId); + if (this.visible) noteDisplayed(this); + super.updateMatrixWorld(force); + } + + swapReadyRecord(): void { + if (!this.visible || !this.lastInfo.shapeId || this.lastInfo.shapeRevision === null) return; + const bucket = wanted(this); + if (bucket === undefined) return; + const record = runtime().records.get(this.lastInfo.shapeId, this.lastInfo.shapeRevision, bucket); + if (record) this.install(record); + } + + install(record: GeometryRecord): void { + if (this.record === record) return; + const pool = runtime().records; + if (this.record) pool.release(this.record); + this.record = record; + pool.retain(record); + const held = pool.releasePlaceholder(this.outline.geometry); + if (record.surface) this.surface.geometry = record.surface; + else if (held) this.surface.geometry = new THREE.BufferGeometry(); + this.outline.geometry = record.outline; + this.surface.position.copy(record.origin); + this.outline.position.copy(record.origin); + this.surface.visible = this.bodyType === 'Solid'; + this.outline.visible = this.appearance.outline; + } + + setAppearance(partial: Partial): void { + this.check(`${this.bodyType}.setAppearance`); + const previous = this.appearance; + this.appearance = { ...previous, ...partial }; + if (previous.color !== this.appearance.color || previous.opacity !== this.appearance.opacity) { + const surfaceStyle = surfaceMaterial(this.appearance); + const outlineStyle = lineMaterial(lineStyle(this.bodyType, this.appearance)); + releaseSurface(this.surfaceKey); + releaseLine(this.lineKey); + this.surfaceKey = surfaceStyle.key; + this.lineKey = outlineStyle.key; + this.surface.material = surfaceStyle.material; + this.outline.material = outlineStyle.material; + } + this.outline.visible = this.appearance.outline; + if (previous.pickOutline !== this.appearance.pickOutline) { + this.outline.raycast = this.appearance.pickOutline + ? THREE.LineSegments.prototype.raycast.bind(this.outline) + : THREE.Object3D.prototype.raycast.bind(this.outline); + } + const shapeId = this.lastInfo.shapeId; + if (shapeId && previous.deflection !== this.appearance.deflection) overrideChanged(runtime(), shapeId); + } + + transform(kind: string, params: Record): void { + this.check(`${this.bodyType}.transform`); + call(`${this.bodyType}.transform`, () => worldGraph().transform(this.ogId, encode({ kind, ...params }))); + } + + getPlacement(): Placement { + this.check(`${this.bodyType}.getPlacement`); + return getPlacement(this.bodyType, this.ogId); + } + + getWorldPlacement(): Placement { + this.check(`${this.bodyType}.getWorldPlacement`); + return getWorldPlacement(this.bodyType, this.ogId); + } + + addChild(children: (Body | SystemAssembly)[], options: { keepWorld?: boolean } = {}): void { + this.check(`${this.bodyType}.addChild`); + addChild(this.bodyType, this.ogId, children, Boolean(options.keepWorld)); + } + + removeChild(child: Body | SystemAssembly, options: { keepWorld?: boolean } = {}): void { + this.check(`${this.bodyType}.removeChild`); + removeChild(this.bodyType, this.ogId, child.ogId, Boolean(options.keepWorld)); + } + + getChildren(): string[] { this.check(`${this.bodyType}.getChildren`); return getChildren(this.bodyType, this.ogId); } + + getParent(): string | null { this.check(`${this.bodyType}.getParent`); return getParent(this.bodyType, this.ogId); } + + getBounds(): [number, number, number, number, number, number] | null { + this.check(`${this.bodyType}.getBounds`); + return getBounds(this.bodyType, this.ogId); + } + + getBrep(): Brep { this.check(`${this.bodyType}.getBrep`); return getBrep(this.bodyType, this.ogId); } + + getInstanceCount(): number { + this.check(`${this.bodyType}.getInstanceCount`); + return getInstanceCount(this.bodyType, this.ogId); + } + + makeUnique(): void { + this.check(`${this.bodyType}.makeUnique`); + call(`${this.bodyType}.makeUnique`, () => worldGraph().makeUnique(this.ogId)); + flush(); + } + + rebuild(kind: string, params: Record, options: { instances?: 'all' } = {}): void { + this.check(`${this.bodyType}.rebuild`); + const graph = worldGraph(); + const label = `${this.bodyType}.rebuild`; + if (kind === 'Polyline') { + const points = polylinePoints(params, label); + call(label, () => graph.rebuildPolyline(this.ogId, points, Boolean(params['closed']), scope(options))); + } else if (kind === 'Extrude' || kind === 'Sweep') { + const operation = encode(operationParams(kind, params, label)); + call(label, () => graph.rebuildOperation(this.ogId, operation, scope(options))); + } else { + call(label, () => graph.rebuildPrimitive(this.ogId, encode({ kind, ...params }), scope(options))); + } + flush(); + } + + hideForDispose(): void { enterLimbo(this); } + + reviveIfRestored(): void { revive(this); } + + finalize(): void { + const state = runtime(); + if (this.epoch !== state.epoch || state.bodies.get(bodyKey(this.handle, this.generation)) !== this) return; + unregister(this); + this.inLimbo = false; + delete this.record; + state.records.releasePlaceholder(this.outline.geometry); + this.surface.geometry = new THREE.BufferGeometry(); + this.outline.geometry = new THREE.BufferGeometry(); + releaseSurface(this.surfaceKey); + releaseLine(this.lineKey); + } + + dispose(): void { + this.check(`${this.bodyType}.dispose`); + call(`${this.bodyType}.dispose`, () => worldGraph().dispose(this.ogId)); + this.hideForDispose(); + flush(); + } + + override clone(): this { + const label = `${this.bodyType}.clone`; + const surface = this.record ? this.surface.geometry : new THREE.BufferGeometry(); + const lines = this.record ? this.outline.geometry : new THREE.BufferGeometry(); + const mesh = new THREE.Mesh(surface, singleMaterial(this.surface.material, label).clone()); + const outline = new THREE.LineSegments(lines, singleMaterial(this.outline.material, label).clone()); + mesh.position.copy(this.surface.position); + outline.position.copy(this.outline.position); + const display = new DisplayClone(this.record, mesh, outline); + display.matrix.copy(this.matrixWorld); + display.matrix.decompose(display.position, display.quaternion, display.scale); + display.matrixWorld.copy(this.matrixWorld); + return display as unknown as this; + } + + override copy(): this { + throw new OGError('InvalidOperand', `${this.bodyType}.copy`, 'Use duplicate() for an independent body'); + } +} diff --git a/main/opengeometry-three/src/bodies/display-clone.ts b/main/opengeometry-three/src/bodies/display-clone.ts new file mode 100644 index 0000000..78910b5 --- /dev/null +++ b/main/opengeometry-three/src/bodies/display-clone.ts @@ -0,0 +1,27 @@ +import * as THREE from 'three'; +import { OGError } from '../errors'; +import type { GeometryRecord } from '../rendering/records/geometry-record'; +import { runtime } from '../runtime/runtime-state'; + +export class DisplayClone extends THREE.Group { + constructor( + readonly sourceRecord: GeometryRecord | undefined, + readonly mesh: THREE.Mesh, + readonly lines: THREE.LineSegments, + ) { + super(); + this.add(mesh, lines); + if (sourceRecord) runtime().records.retain(sourceRecord); + } + + dispose(): void { + if (this.sourceRecord) runtime().records.release(this.sourceRecord); + this.mesh.material.dispose(); + this.lines.material.dispose(); + this.parent?.remove(this); + } + + override clone(): this { + throw new OGError('InvalidOperand', 'DisplayClone.clone', 'a display clone cannot be cloned'); + } +} diff --git a/main/opengeometry-three/src/bodies/node-methods.ts b/main/opengeometry-three/src/bodies/node-methods.ts new file mode 100644 index 0000000..29a0aac --- /dev/null +++ b/main/opengeometry-three/src/bodies/node-methods.ts @@ -0,0 +1,61 @@ +import type { Bounds } from '../dto/bounds'; +import type { Brep } from '../dto/brep'; +import type { Placement } from '../dto/placement'; +import { OGError } from '../errors'; +import { call } from '../kernel/kernel-session'; +import { runtime } from '../runtime/runtime-state'; +import { + decodeBounds, decodeBrep, decodeChildren, decodeParent, decodePlacement, encode, +} from '../world-graph/codec'; +import { worldGraph } from '../world-graph/world-graph-client'; + +export function checkNode(label: string, noun: string, epoch: number, handle: number, generation: number): void { + if (runtime().epoch !== epoch) throw new OGError('Disposed', label, `${noun} belongs to a reset runtime`); + call(label, () => worldGraph().nodeByHandle(handle, generation)); +} + +export function getPlacement(owner: string, ogId: string): Placement { + const label = `${owner}.getPlacement`; + return decodePlacement(call(label, () => worldGraph().placement(ogId)), label); +} + +export function getWorldPlacement(owner: string, ogId: string): Placement { + const label = `${owner}.getWorldPlacement`; + return decodePlacement(call(label, () => worldGraph().worldPlacement(ogId)), label); +} + +export function addChild(owner: string, ogId: string, children: { ogId: string }[], keepWorld: boolean): void { + call(`${owner}.addChild`, () => worldGraph().addChild(ogId, encode(children.map((child) => child.ogId)), keepWorld)); +} + +export function removeChild(owner: string, ogId: string, childId: string, keepWorld: boolean): void { + call(`${owner}.removeChild`, () => worldGraph().removeChild(ogId, childId, keepWorld)); +} + +export function getChildren(owner: string, ogId: string): string[] { + const label = `${owner}.getChildren`; + return decodeChildren(call(label, () => worldGraph().children(ogId)), label); +} + +export function getParent(owner: string, ogId: string): string | null { + const label = `${owner}.getParent`; + return decodeParent(call(label, () => worldGraph().parent(ogId)), label); +} + +export function getBounds(owner: string, ogId: string): Bounds | null { + const label = `${owner}.getBounds`; + return decodeBounds(call(label, () => worldGraph().bounds(ogId)), label); +} + +export function localBounds(label: string, ogId: string): Bounds | null { + return decodeBounds(call(label, () => worldGraph().localBounds(ogId)), label); +} + +export function getBrep(owner: string, ogId: string): Brep { + const label = `${owner}.getBrep`; + return decodeBrep(call(label, () => worldGraph().brep(ogId)), label); +} + +export function getInstanceCount(owner: string, ogId: string): number { + return call(`${owner}.getInstanceCount`, () => worldGraph().instanceCount(ogId)); +} diff --git a/main/opengeometry-three/src/bodies/solid.ts b/main/opengeometry-three/src/bodies/solid.ts new file mode 100644 index 0000000..4f647aa --- /dev/null +++ b/main/opengeometry-three/src/bodies/solid.ts @@ -0,0 +1,44 @@ +import { OGError } from '../errors'; +import { call } from '../kernel/kernel-session'; +import { flush } from '../rendering/geometry/geometry-scheduler'; +import type { ShapeReport } from '../dto/boolean-report'; +import { decodeShapeReport, encode, scope } from '../world-graph/codec'; +import { creation, worldGraph } from '../world-graph/world-graph-client'; +import { Body } from './body'; +import { createBody, existingParams } from './body-factory'; +import type { BodyOptions } from './body-options'; +import type { SystemAssembly } from './system-assembly'; + +export class Solid extends Body { + constructor(kind: string, params: Record, options: BodyOptions = {}) { + super(createBody(kind, params, options, 'Solid'), 'Solid', options); + if (this.lastInfo.bodyType !== 'Solid') { + throw new OGError('BodyTypeMismatch', 'Solid.constructor', 'expected Solid'); + } + } + operate(kind: string, options: { tools: Solid[]; instances?: 'all' }): void { + this.check('Solid.operate'); + call('Solid.operate', () => worldGraph().operate( + this.ogId, encode(kind), encode(options.tools.map((tool) => tool.ogId)), scope(options), + )); + flush(); + } + getReport(): ShapeReport | null { + this.check('Solid.getReport'); + return decodeShapeReport(call('Solid.getReport', () => worldGraph().report(this.ogId)), 'Solid.getReport'); + } + instance(options: { ogId?: string; parent?: SystemAssembly } = {}): Solid { + this.check('Solid.instance'); + const ogId = creation(call('Solid.instance', () => worldGraph().instance( + this.ogId, encode({ ogId: options.ogId, parent: options.parent?.ogId }), + )), 'Solid.instance'); + return new Solid('', existingParams(ogId)); + } + duplicate(options: { ogId?: string; parent?: SystemAssembly } = {}): Solid { + this.check('Solid.duplicate'); + const ogId = creation(call('Solid.duplicate', () => worldGraph().duplicate( + this.ogId, encode({ ogId: options.ogId, parent: options.parent?.ogId }), + )), 'Solid.duplicate'); + return new Solid('', existingParams(ogId)); + } +} diff --git a/main/opengeometry-three/src/bodies/system-assembly.ts b/main/opengeometry-three/src/bodies/system-assembly.ts new file mode 100644 index 0000000..23b8468 --- /dev/null +++ b/main/opengeometry-three/src/bodies/system-assembly.ts @@ -0,0 +1,68 @@ +import type { Placement } from '../dto/placement'; +import { call } from '../kernel/kernel-session'; +import { flush } from '../rendering/geometry/geometry-scheduler'; +import { runtime } from '../runtime/runtime-state'; +import { encode } from '../world-graph/codec'; +import { creation, node, worldGraph } from '../world-graph/world-graph-client'; +import type { Body } from './body'; +import { + addChild, checkNode, getBounds, getChildren, getParent, getPlacement, getWorldPlacement, removeChild, +} from './node-methods'; + +export class SystemAssembly { + readonly ogId: string; + readonly handle: number; + readonly generation: number; + private readonly epoch = runtime().epoch; + + constructor(options: { ogId?: string; parent?: SystemAssembly } = {}) { + this.ogId = creation(call('SystemAssembly.constructor', () => worldGraph().createSystemAssembly( + encode({ ogId: options.ogId, parent: options.parent?.ogId }), + )), 'SystemAssembly.constructor'); + const info = node(this.ogId, 'SystemAssembly.constructor'); + this.handle = info.handle; + this.generation = info.generation; + } + + protected check(call: string): void { checkNode(call, 'assembly', this.epoch, this.handle, this.generation); } + + transform(kind: string, params: Record): void { + this.check('SystemAssembly.transform'); + call('SystemAssembly.transform', () => worldGraph().transform(this.ogId, encode({ kind, ...params }))); + } + + getPlacement(): Placement { + this.check('SystemAssembly.getPlacement'); + return getPlacement('SystemAssembly', this.ogId); + } + + getWorldPlacement(): Placement { + this.check('SystemAssembly.getWorldPlacement'); + return getWorldPlacement('SystemAssembly', this.ogId); + } + + addChild(children: (Body | SystemAssembly)[], options: { keepWorld?: boolean } = {}): void { + this.check('SystemAssembly.addChild'); + addChild('SystemAssembly', this.ogId, children, Boolean(options.keepWorld)); + } + + removeChild(child: Body | SystemAssembly, options: { keepWorld?: boolean } = {}): void { + this.check('SystemAssembly.removeChild'); + removeChild('SystemAssembly', this.ogId, child.ogId, Boolean(options.keepWorld)); + } + + getChildren(): string[] { this.check('SystemAssembly.getChildren'); return getChildren('SystemAssembly', this.ogId); } + + getParent(): string | null { this.check('SystemAssembly.getParent'); return getParent('SystemAssembly', this.ogId); } + + getBounds(): [number, number, number, number, number, number] | null { + this.check('SystemAssembly.getBounds'); + return getBounds('SystemAssembly', this.ogId); + } + + dispose(): void { + this.check('SystemAssembly.dispose'); + call('SystemAssembly.dispose', () => worldGraph().dispose(this.ogId)); + flush(); + } +} diff --git a/main/opengeometry-three/src/bodies/wire.ts b/main/opengeometry-three/src/bodies/wire.ts new file mode 100644 index 0000000..5e7a7e6 --- /dev/null +++ b/main/opengeometry-three/src/bodies/wire.ts @@ -0,0 +1,29 @@ +import { OGError } from '../errors'; +import { call } from '../kernel/kernel-session'; +import { encode } from '../world-graph/codec'; +import { creation, worldGraph } from '../world-graph/world-graph-client'; +import { Body } from './body'; +import { createBody, existingParams } from './body-factory'; +import type { BodyOptions } from './body-options'; +import type { SystemAssembly } from './system-assembly'; + +export class Wire extends Body { + constructor(kind: string, params: Record, options: BodyOptions = {}) { + super(createBody(kind, params, options, 'Wire'), 'Wire', options); + if (this.lastInfo.bodyType !== 'Wire') throw new OGError('BodyTypeMismatch', 'Wire.constructor', 'expected Wire'); + } + instance(options: { ogId?: string; parent?: SystemAssembly } = {}): Wire { + this.check('Wire.instance'); + const ogId = creation(call('Wire.instance', () => worldGraph().instance( + this.ogId, encode({ ogId: options.ogId, parent: options.parent?.ogId }), + )), 'Wire.instance'); + return new Wire('', existingParams(ogId)); + } + duplicate(options: { ogId?: string; parent?: SystemAssembly } = {}): Wire { + this.check('Wire.duplicate'); + const ogId = creation(call('Wire.duplicate', () => worldGraph().duplicate( + this.ogId, encode({ ogId: options.ogId, parent: options.parent?.ogId }), + )), 'Wire.duplicate'); + return new Wire('', existingParams(ogId)); + } +} diff --git a/main/opengeometry-three/src/constants/index.ts b/main/opengeometry-three/src/constants/index.ts new file mode 100644 index 0000000..c9a0ffc --- /dev/null +++ b/main/opengeometry-three/src/constants/index.ts @@ -0,0 +1,3 @@ +export * from './primitives'; +export * from './operations'; +export * from './transforms'; diff --git a/main/opengeometry-three/src/constants/operations.ts b/main/opengeometry-three/src/constants/operations.ts new file mode 100644 index 0000000..5dba4b5 --- /dev/null +++ b/main/opengeometry-three/src/constants/operations.ts @@ -0,0 +1,12 @@ +export const OG_OPERATION_EXTRUDE = 'Extrude' as const; +export const OG_OPERATION_SWEEP = 'Sweep' as const; +export const OG_OPERATION_UNION = 'Union' as const; +export const OG_OPERATION_SUBTRACT = 'Subtract' as const; +export const OG_OPERATION_INTERSECT = 'Intersect' as const; + +export const OG_OPERATION_PARAMS_EXTRUDE = ( + value: { profile: { ogId: string }; holes?: { ogId: string }[]; distance: number }, +): { profile: { ogId: string }; holes?: { ogId: string }[]; distance: number } => value; +export const OG_OPERATION_PARAMS_SWEEP = ( + value: { profile: { ogId: string }; path: { ogId: string } }, +): { profile: { ogId: string }; path: { ogId: string } } => value; diff --git a/main/opengeometry-three/src/constants/primitives.ts b/main/opengeometry-three/src/constants/primitives.ts new file mode 100644 index 0000000..04be247 --- /dev/null +++ b/main/opengeometry-three/src/constants/primitives.ts @@ -0,0 +1,19 @@ +export const OG_PRIMITIVE_RECTANGLE = 'Rectangle' as const; +export const OG_PRIMITIVE_CIRCLE = 'Circle' as const; +export const OG_PRIMITIVE_POLYLINE = 'Polyline' as const; +export const OG_PRIMITIVE_CUBOID = 'Cuboid' as const; +export const OG_PRIMITIVE_CYLINDER = 'Cylinder' as const; + +export const OG_PRIMITIVE_PARAMS_RECTANGLE = ( + value: { width: number; breadth: number }, +): { width: number; breadth: number } => value; +export const OG_PRIMITIVE_PARAMS_CIRCLE = (value: { radius: number }): { radius: number } => value; +export const OG_PRIMITIVE_PARAMS_POLYLINE = ( + value: { points: [number, number, number][]; closed?: boolean }, +): { points: [number, number, number][]; closed?: boolean } => value; +export const OG_PRIMITIVE_PARAMS_CUBOID = ( + value: { width: number; height: number; depth: number }, +): { width: number; height: number; depth: number } => value; +export const OG_PRIMITIVE_PARAMS_CYLINDER = ( + value: { radius: number; height: number }, +): { radius: number; height: number } => value; diff --git a/main/opengeometry-three/src/constants/transforms.ts b/main/opengeometry-three/src/constants/transforms.ts new file mode 100644 index 0000000..ea94380 --- /dev/null +++ b/main/opengeometry-three/src/constants/transforms.ts @@ -0,0 +1,4 @@ +export const OG_TRANSFORM_TRANSLATE = 'Translate' as const; +export const OG_TRANSFORM_ROTATE = 'Rotate' as const; +export const OG_TRANSFORM_SCALE = 'Scale' as const; +export const OG_TRANSFORM_PLACE = 'Place' as const; diff --git a/main/opengeometry-three/src/dto/boolean-report.ts b/main/opengeometry-three/src/dto/boolean-report.ts new file mode 100644 index 0000000..e56bef9 --- /dev/null +++ b/main/opengeometry-three/src/dto/boolean-report.ts @@ -0,0 +1,13 @@ +import type { FaceSource, GeometryQuality } from './brep'; + +export type FaceMapping = { source: FaceSource; result_faces: number[] }; + +export type BooleanReport = { + operation: 'union' | 'intersection' | 'subtraction'; + quality: GeometryQuality; + contacts: [number, number, number][]; + coincident: boolean; + face_mappings: FaceMapping[]; +}; + +export type ShapeReport = { report: BooleanReport; handlers: string[]; revision: number }; diff --git a/main/opengeometry-three/src/dto/bounds.ts b/main/opengeometry-three/src/dto/bounds.ts new file mode 100644 index 0000000..920de1f --- /dev/null +++ b/main/opengeometry-three/src/dto/bounds.ts @@ -0,0 +1 @@ +export type Bounds = [number, number, number, number, number, number]; diff --git a/main/opengeometry-three/src/dto/brep.ts b/main/opengeometry-three/src/dto/brep.ts new file mode 100644 index 0000000..75616c5 --- /dev/null +++ b/main/opengeometry-three/src/dto/brep.ts @@ -0,0 +1,72 @@ +export type GeometryQuality = { kind: 'Analytic' } | { kind: 'Approximate'; max_error: number }; + +export type FaceSource = { entity: string; body: string; key: string; face: number }; + +export type BrepVertex = { + id: number; + position: [number, number, number]; + outgoing_halfedge: number | null; + tolerance: number; +}; + +export type BrepEdge = { + id: number; + geometry: unknown; + halfedge: number; + twin_halfedge: number | null; + tolerance: number; + chart_seam: boolean; +}; + +export type BrepHalfedge = { + id: number; + from: number; + to: number; + twin: number | null; + next: number | null; + prev: number | null; + edge: number; + face: number | null; + loop_ref: number | null; + wire_ref: number | null; + geometry_use: unknown; +}; + +export type BrepLoop = { id: number; start_halfedge: number; face_ref: number; is_hole: boolean }; + +export type BrepFace = { + id: number; + key: string; + surface: number; + sense: 'Forward' | 'Reverse'; + trim: unknown; + shell_ref: number | null; + provenance: { + sources: FaceSource[]; + role: 'Authored' | 'Preserved' | 'Split' | 'Cut' | 'Coincident'; + reversed: boolean; + }; +}; + +export type BrepWire = { id: number; start_halfedge: number; is_closed: boolean }; + +export type BrepShell = { id: number; faces: number[]; is_closed: boolean }; + +export type Brep = { + schema_version: number; + id: string; + revision: number; + geometry: { surfaces: unknown[]; curves: unknown[]; pcurves: unknown[]; intersections: unknown[] }; + topology: { + vertices: BrepVertex[]; + edges: BrepEdge[]; + halfedges: BrepHalfedge[]; + loops: BrepLoop[]; + faces: BrepFace[]; + wires: BrepWire[]; + shells: BrepShell[]; + }; + solids: { outer_shell: number; cavity_shells: number[] }[]; + accuracy: { geometric: number; intersection: number; tessellation: number; exchange: number }; + quality: GeometryQuality; +}; diff --git a/main/opengeometry-three/src/dto/change-set.ts b/main/opengeometry-three/src/dto/change-set.ts new file mode 100644 index 0000000..606e2cf --- /dev/null +++ b/main/opengeometry-three/src/dto/change-set.ts @@ -0,0 +1,14 @@ +export type NodeChange = { + ogId: string; + handle: number; + generation: number; + shapeId: string | null; + shapeRevision: number | null; +}; + +export type ChangeSet = { + revision: number; + added: NodeChange[]; + changed: NodeChange[]; + removed: NodeChange[]; +}; diff --git a/main/opengeometry-three/src/dto/creation.ts b/main/opengeometry-three/src/dto/creation.ts new file mode 100644 index 0000000..4a1eb22 --- /dev/null +++ b/main/opengeometry-three/src/dto/creation.ts @@ -0,0 +1 @@ +export type Creation = { ogId: string }; diff --git a/main/opengeometry-three/src/dto/display-buffers.ts b/main/opengeometry-three/src/dto/display-buffers.ts new file mode 100644 index 0000000..333adc3 --- /dev/null +++ b/main/opengeometry-three/src/dto/display-buffers.ts @@ -0,0 +1,13 @@ +export type DisplayBuffers = { + positions: Float32Array; + normals: Float32Array; + indices: Uint32Array; + faceRanges: Uint32Array; + outline: Float32Array; + edgeIds: Uint32Array; + origin: Float64Array; + revision: number; + bucket: number; + achievedDeflection: number; + triangles: number; +}; diff --git a/main/opengeometry-three/src/dto/events.ts b/main/opengeometry-three/src/dto/events.ts new file mode 100644 index 0000000..5ba07b4 --- /dev/null +++ b/main/opengeometry-three/src/dto/events.ts @@ -0,0 +1,2 @@ +export type Events = 'geometry' | 'warning' | 'error' | 'fatal'; +export type Listener = (event: unknown) => void; diff --git a/main/opengeometry-three/src/dto/mark-stats.ts b/main/opengeometry-three/src/dto/mark-stats.ts new file mode 100644 index 0000000..5c09cb4 --- /dev/null +++ b/main/opengeometry-three/src/dto/mark-stats.ts @@ -0,0 +1 @@ +export type MarkStats = { liveMarks: number; retainedRevisions: number }; diff --git a/main/opengeometry-three/src/dto/node-info.ts b/main/opengeometry-three/src/dto/node-info.ts new file mode 100644 index 0000000..66e0800 --- /dev/null +++ b/main/opengeometry-three/src/dto/node-info.ts @@ -0,0 +1,11 @@ +export type NodeInfo = { + ogId: string; + handle: number; + generation: number; + parent: string | null; + children: string[]; + kind: string; + shapeId: string | null; + shapeRevision: number | null; + bodyType: string | null; +}; diff --git a/main/opengeometry-three/src/dto/pick-result.ts b/main/opengeometry-three/src/dto/pick-result.ts new file mode 100644 index 0000000..0a98353 --- /dev/null +++ b/main/opengeometry-three/src/dto/pick-result.ts @@ -0,0 +1 @@ +export type PickResult = { ogId: string; shapeRevision: number; faceId?: number; edgeId?: number }; diff --git a/main/opengeometry-three/src/dto/placement.ts b/main/opengeometry-three/src/dto/placement.ts new file mode 100644 index 0000000..28fd463 --- /dev/null +++ b/main/opengeometry-three/src/dto/placement.ts @@ -0,0 +1,6 @@ +export type Placement = { + origin: [number, number, number]; + xDirection: [number, number, number]; + normal: [number, number, number]; + scale: number; +}; diff --git a/main/opengeometry-three/src/dto/step-export.ts b/main/opengeometry-three/src/dto/step-export.ts new file mode 100644 index 0000000..c5fb76d --- /dev/null +++ b/main/opengeometry-three/src/dto/step-export.ts @@ -0,0 +1,39 @@ +export type StepBodyReport = { + ogId: string; + shapeId: string; + shapeRevision: number; + solids: number; + faces: number; + edges: number; + cavityShells: number; + collapsedChartUses: number; + fittedCurves: number; + pcurvelessEdges: number; + exchangeErrorBound: number; + geometricTolerance: number; +}; + +export type StepSkipped = { ogId: string; reason: string }; + +export type StepExportReport = { + unit: string; + upAxis: string; + timestamp: string; + products: number; + solids: number; + faces: number; + edges: number; + cavityShells: number; + collapsedChartUses: number; + fittedCurves: number; + pcurvelessEdges: number; + entities: number; + bytes: number; + exchangeErrorBound: number; + geometricTolerance: number; + validationLevel: string; + bodies: StepBodyReport[]; + skipped: StepSkipped[]; +}; + +export type StepExport = { text: string; report: StepExportReport }; diff --git a/main/opengeometry-three/src/editor/freeform.ts b/main/opengeometry-three/src/editor/freeform.ts deleted file mode 100644 index ab3a02b..0000000 --- a/main/opengeometry-three/src/editor/freeform.ts +++ /dev/null @@ -1,485 +0,0 @@ -import { - OGFreeformEditor as KernelFreeformEditor, - Vector3, -} from "../../../opengeometry/pkg/opengeometry"; - -import { FreeformGeometry } from "../freeform"; -import type { ObjectTransformation } from "../freeform/types"; -import type { - EdgeInfo, - EditOperationOptions, - FaceInfo, - FreeformEditCapabilities, - FreeformEditResult, - FreeformFeatureEditCapabilities, - TopologyId, - TopologyRenderData, - TopologyVertexRenderData, - VertexInfo, -} from "./types"; - -type RawFreeformOperationCapabilities = { - can_push_pull_face: boolean; - can_move_face: boolean; - can_extrude_face: boolean; - can_cut_face: boolean; - can_move_edge: boolean; - can_move_vertex: boolean; - can_insert_vertex_on_edge: boolean; - can_remove_vertex: boolean; - can_split_edge: boolean; - can_loop_cut: boolean; - reasons?: string[]; -}; - -type RawFreeformFeatureEditCapabilities = RawFreeformOperationCapabilities & { - domain: "face" | "edge" | "vertex"; - topology_id: TopologyId; -}; - -function parseJson(payload: string): T { - return JSON.parse(payload) as T; -} - -function toVector3(value: { x: number; y: number; z: number }): Vector3 { - return new Vector3(value.x, value.y, value.z); -} - -function toPlainVector3(value: { x: number; y: number; z: number }) { - return { - x: value.x, - y: value.y, - z: value.z, - }; -} - -function normalizePlacement( - placement: ObjectTransformation -): ObjectTransformation { - return { - anchor: toVector3(placement.anchor), - translation: toVector3(placement.translation), - rotation: toVector3(placement.rotation), - scale: toVector3(placement.scale), - }; -} - -function normalizeFaceInfo(face: FaceInfo): FaceInfo { - return { - ...face, - centroid: toVector3(face.centroid), - normal: toVector3(face.normal), - }; -} - -function normalizeEdgeInfo(edge: EdgeInfo): EdgeInfo { - return { - ...edge, - start: toVector3(edge.start), - end: toVector3(edge.end), - }; -} - -function normalizeVertexInfo(vertex: VertexInfo): VertexInfo { - return { - ...vertex, - position: toVector3(vertex.position), - }; -} - -function normalizeTopologyRenderData( - topology: TopologyRenderData -): TopologyRenderData { - return { - ...topology, - vertices: topology.vertices.map((vertex) => - normalizeTopologyVertex(vertex) - ), - }; -} - -function normalizeTopologyVertex( - vertex: TopologyVertexRenderData -): TopologyVertexRenderData { - return { - ...vertex, - position: toVector3(vertex.position), - }; -} - -function normalizeFeatureCapabilities( - raw: RawFreeformFeatureEditCapabilities -): FreeformFeatureEditCapabilities { - return { - domain: raw.domain, - topologyId: raw.topology_id, - canPushPullFace: raw.can_push_pull_face, - canMoveFace: raw.can_move_face, - canExtrudeFace: raw.can_extrude_face, - canCutFace: raw.can_cut_face, - canMoveEdge: raw.can_move_edge, - canMoveVertex: raw.can_move_vertex, - canInsertVertexOnEdge: raw.can_insert_vertex_on_edge, - canRemoveVertex: raw.can_remove_vertex, - canSplitEdge: raw.can_split_edge, - canLoopCut: raw.can_loop_cut, - reasons: raw.reasons, - }; -} - -function normalizeFreeformCapabilities( - raw: RawFreeformOperationCapabilities -): FreeformEditCapabilities { - return { - editingMode: "freeform", - entityType: "freeform", - editFamily: "freeform", - canEditConfig: false, - canEditPlacement: false, - canConvertToFreeform: false, - canPushPullFace: raw.can_push_pull_face, - canMoveFace: raw.can_move_face, - canExtrudeFace: raw.can_extrude_face, - canCutFace: raw.can_cut_face, - canMoveEdge: raw.can_move_edge, - canMoveVertex: raw.can_move_vertex, - canInsertVertexOnEdge: raw.can_insert_vertex_on_edge, - canRemoveVertex: raw.can_remove_vertex, - canSplitEdge: raw.can_split_edge, - canLoopCut: raw.can_loop_cut, - reasons: raw.reasons, - }; -} - -function parseEditResult(payload: string): FreeformEditResult { - const result = parseJson(payload); - return { - ...result, - placement: normalizePlacement(result.placement), - }; -} - -function serializeOptions(options?: EditOperationOptions): string | undefined { - if (!options) { - return undefined; - } - - return JSON.stringify({ - ...options, - constraintAxis: options.constraintAxis - ? toPlainVector3(options.constraintAxis) - : undefined, - constraintPlaneNormal: options.constraintPlaneNormal - ? toPlainVector3(options.constraintPlaneNormal) - : undefined, - }); -} - -/** - * High-level editor facade over the kernel freeform editing API. - */ -export class FreeformEditor { - private readonly geometry: FreeformGeometry; - private readonly editor: KernelFreeformEditor; - - constructor( - geometry: FreeformGeometry, - editor: KernelFreeformEditor = new KernelFreeformEditor() - ) { - this.geometry = geometry; - this.editor = editor; - } - - /** - * Returns the freeform geometry currently managed by this editor. - */ - getFreeformGeometry(): FreeformGeometry { - return this.geometry; - } - - /** - * Returns renderable topology buffers for faces, edges, and vertices. - */ - getTopologyRenderData(): TopologyRenderData { - return normalizeTopologyRenderData( - parseJson( - this.editor.getTopologyRenderData(this.geometry.getKernelGeometry()) - ) - ); - } - - /** - * Returns semantic and topological information for a face id. - */ - getFaceInfo(faceId: TopologyId): FaceInfo { - return normalizeFaceInfo( - parseJson( - this.editor.getFaceInfo(this.geometry.getKernelGeometry(), faceId) - ) - ); - } - - /** - * Returns semantic and topological information for an edge id. - */ - getEdgeInfo(edgeId: TopologyId): EdgeInfo { - return normalizeEdgeInfo( - parseJson( - this.editor.getEdgeInfo(this.geometry.getKernelGeometry(), edgeId) - ) - ); - } - - /** - * Returns semantic and topological information for a vertex id. - */ - getVertexInfo(vertexId: TopologyId): VertexInfo { - return normalizeVertexInfo( - parseJson( - this.editor.getVertexInfo(this.geometry.getKernelGeometry(), vertexId) - ) - ); - } - - /** - * Returns global edit capabilities for the current freeform model. - */ - getEditCapabilities(): FreeformEditCapabilities { - return normalizeFreeformCapabilities( - parseJson( - this.editor.getEditCapabilities(this.geometry.getKernelGeometry()) - ) - ); - } - - /** - * Returns edit capabilities scoped to a single face. - */ - getFaceEditCapabilities(faceId: TopologyId): FreeformFeatureEditCapabilities { - return normalizeFeatureCapabilities( - parseJson( - this.editor.getFaceEditCapabilities(this.geometry.getKernelGeometry(), faceId) - ) - ); - } - - /** - * Returns edit capabilities scoped to a single edge. - */ - getEdgeEditCapabilities(edgeId: TopologyId): FreeformFeatureEditCapabilities { - return normalizeFeatureCapabilities( - parseJson( - this.editor.getEdgeEditCapabilities(this.geometry.getKernelGeometry(), edgeId) - ) - ); - } - - /** - * Returns edit capabilities scoped to a single vertex. - */ - getVertexEditCapabilities( - vertexId: TopologyId - ): FreeformFeatureEditCapabilities { - return normalizeFeatureCapabilities( - parseJson( - this.editor.getVertexEditCapabilities( - this.geometry.getKernelGeometry(), - vertexId - ) - ) - ); - } - - /** - * Offset a face along its editable normal direction. - */ - pushPullFace( - faceId: TopologyId, - distance: number, - options?: EditOperationOptions - ): FreeformEditResult { - return parseEditResult( - this.editor.pushPullFace( - this.geometry.getKernelGeometry(), - faceId, - distance, - serializeOptions(options) - ) - ); - } - - /** - * Creates new side faces by extruding a face region. - */ - extrudeFace( - faceId: TopologyId, - distance: number, - options?: EditOperationOptions - ): FreeformEditResult { - return parseEditResult( - this.editor.extrudeFace( - this.geometry.getKernelGeometry(), - faceId, - distance, - serializeOptions(options) - ) - ); - } - - /** - * Splits a face by connecting two points sampled on its boundary edges. - */ - cutFace( - faceId: TopologyId, - startEdgeId: TopologyId, - startT: number, - endEdgeId: TopologyId, - endT: number, - options?: EditOperationOptions - ): FreeformEditResult { - return parseEditResult( - this.editor.cutFace( - this.geometry.getKernelGeometry(), - faceId, - startEdgeId, - startT, - endEdgeId, - endT, - serializeOptions(options) - ) - ); - } - - /** - * Moves a face by a translation vector, subject to kernel constraints. - */ - moveFace( - faceId: TopologyId, - translation: Vector3, - options?: EditOperationOptions - ): FreeformEditResult { - return parseEditResult( - this.editor.moveFace( - this.geometry.getKernelGeometry(), - faceId, - translation, - serializeOptions(options) - ) - ); - } - - /** - * Moves an edge by a translation vector, subject to kernel constraints. - */ - moveEdge( - edgeId: TopologyId, - translation: Vector3, - options?: EditOperationOptions - ): FreeformEditResult { - return parseEditResult( - this.editor.moveEdge( - this.geometry.getKernelGeometry(), - edgeId, - translation, - serializeOptions(options) - ) - ); - } - - /** - * Moves a vertex by a translation vector, subject to kernel constraints. - */ - moveVertex( - vertexId: TopologyId, - translation: Vector3, - options?: EditOperationOptions - ): FreeformEditResult { - return parseEditResult( - this.editor.moveVertex( - this.geometry.getKernelGeometry(), - vertexId, - translation, - serializeOptions(options) - ) - ); - } - - /** - * Inserts a vertex at parametric position `t` along an edge. - */ - insertVertexOnEdge( - edgeId: TopologyId, - t: number, - options?: EditOperationOptions - ): FreeformEditResult { - return parseEditResult( - this.editor.insertVertexOnEdge( - this.geometry.getKernelGeometry(), - edgeId, - t, - serializeOptions(options) - ) - ); - } - - /** - * Splits an edge at parametric position `t`. - */ - splitEdge( - edgeId: TopologyId, - t: number, - options?: EditOperationOptions - ): FreeformEditResult { - return parseEditResult( - this.editor.splitEdge( - this.geometry.getKernelGeometry(), - edgeId, - t, - serializeOptions(options) - ) - ); - } - - /** - * Creates a loop cut that propagates from the selected edge when possible. - */ - loopCut( - edgeId: TopologyId, - t: number, - options?: EditOperationOptions - ): FreeformEditResult { - return parseEditResult( - this.editor.loopCut( - this.geometry.getKernelGeometry(), - edgeId, - t, - serializeOptions(options) - ) - ); - } - - /** - * Removes a vertex when the current topology allows the edit. - */ - removeVertex( - vertexId: TopologyId, - options?: EditOperationOptions - ): FreeformEditResult { - return parseEditResult( - this.editor.removeVertex( - this.geometry.getKernelGeometry(), - vertexId, - serializeOptions(options) - ) - ); - } -} - -/** - * Convenience factory for creating a freeform editor around a geometry wrapper. - */ -export function createFreeformEditor( - geometry: FreeformGeometry -): FreeformEditor { - return new FreeformEditor(geometry); -} diff --git a/main/opengeometry-three/src/editor/index.ts b/main/opengeometry-three/src/editor/index.ts deleted file mode 100644 index 2837da3..0000000 --- a/main/opengeometry-three/src/editor/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Public editor helpers for parametric and freeform modeling workflows. - */ -export * from "./types"; -export { - clonePlacement, - createParametricEditCapabilities, - toObjectTransformation, -} from "./parametric"; -export { FreeformEditor, createFreeformEditor } from "./freeform"; diff --git a/main/opengeometry-three/src/editor/parametric.ts b/main/opengeometry-three/src/editor/parametric.ts deleted file mode 100644 index 133b834..0000000 --- a/main/opengeometry-three/src/editor/parametric.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; - -import type { - ParametricEditCapabilities, - ParametricEditFamily, - ParametricEntityType, - ParametricPlacement, -} from "./types"; -import type { ObjectTransformation } from "../freeform/types"; - -function cloneVector( - vector: Vector3 | undefined, - fallback: [number, number, number] -): Vector3 { - return vector?.clone() ?? new Vector3(...fallback); -} - -/** - * Returns the canonical capability payload exposed by parametric wrappers. - */ -export function createParametricEditCapabilities( - entityType: ParametricEntityType, - editFamily: ParametricEditFamily -): ParametricEditCapabilities { - return { - editingMode: "parametric", - entityType, - editFamily, - canEditConfig: true, - canEditPlacement: true, - canConvertToFreeform: true, - }; -} - -/** - * Normalizes optional placement fields into concrete vectors. - */ -export function clonePlacement( - placement: Partial -): ParametricPlacement { - return { - translation: cloneVector(placement.translation, [0, 0, 0]), - rotation: cloneVector(placement.rotation, [0, 0, 0]), - scale: cloneVector(placement.scale, [1, 1, 1]), - }; -} - -/** - * Converts a parametric placement payload into the freeform/object transform - * structure expected by BRep-backed helpers. - */ -export function toObjectTransformation( - placement: ParametricPlacement | ObjectTransformation -): ObjectTransformation { - if ("anchor" in placement) { - return { - anchor: cloneVector(placement.anchor, [0, 0, 0]), - translation: cloneVector(placement.translation, [0, 0, 0]), - rotation: cloneVector(placement.rotation, [0, 0, 0]), - scale: cloneVector(placement.scale, [1, 1, 1]), - }; - } - - return { - anchor: new Vector3(0, 0, 0), - translation: cloneVector(placement.translation, [0, 0, 0]), - rotation: cloneVector(placement.rotation, [0, 0, 0]), - scale: cloneVector(placement.scale, [1, 1, 1]), - }; -} diff --git a/main/opengeometry-three/src/editor/types.ts b/main/opengeometry-three/src/editor/types.ts deleted file mode 100644 index 8c846f7..0000000 --- a/main/opengeometry-three/src/editor/types.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; - -import type { ObjectTransformation } from "../freeform/types"; - -/** - * Numeric identifier used for faces, edges, and vertices in freeform topology APIs. - */ -export type TopologyId = number; - -/** - * Kernel edit permissions shared by global and feature-scoped freeform capabilities. - */ -export interface FreeformOperationCapabilities { - canPushPullFace: boolean; - canMoveFace: boolean; - canExtrudeFace: boolean; - canCutFace: boolean; - canMoveEdge: boolean; - canMoveVertex: boolean; - canInsertVertexOnEdge: boolean; - canRemoveVertex: boolean; - canSplitEdge: boolean; - canLoopCut: boolean; - reasons?: string[]; -} - -/** - * Global editing capabilities for a freeform geometry object. - */ -export interface FreeformEditCapabilities extends FreeformOperationCapabilities { - editingMode: "freeform"; - entityType: "freeform"; - editFamily: "freeform"; - canEditConfig: false; - canEditPlacement: false; - canConvertToFreeform: false; -} - -/** - * Feature-scoped editing capabilities for a single face, edge, or vertex. - */ -export interface FreeformFeatureEditCapabilities - extends FreeformOperationCapabilities { - domain: "face" | "edge" | "vertex"; - topologyId: TopologyId; -} - -/** - * How a topology id changed after an edit. - */ -export type TopologyRemapStatus = "unchanged" | "split" | "merged" | "deleted"; - -/** - * Maps an old topology id to the ids that replaced it. - */ -export interface TopologyRemapEntry { - old_id: TopologyId; - new_ids: TopologyId[]; - primary_id: TopologyId | null; - status: TopologyRemapStatus; -} - -/** - * Newly created topology ids emitted by a freeform edit. - */ -export interface TopologyCreatedIds { - faces: TopologyId[]; - edges: TopologyId[]; - vertices: TopologyId[]; -} - -/** - * Topology remap payload returned by edits that change face/edge/vertex ids. - */ -export interface TopologyRemap { - faces: TopologyRemapEntry[]; - edges: TopologyRemapEntry[]; - vertices: TopologyRemapEntry[]; - created_ids: TopologyCreatedIds; -} - -/** - * Diagnostic severities returned by freeform validity checks. - */ -export type DiagnosticSeverity = "info" | "warning" | "error"; - -/** - * Individual diagnostic emitted by a freeform edit or validation pass. - */ -export interface FreeformDiagnostic { - code: string; - severity: DiagnosticSeverity; - message: string; - domain?: string; - topology_id?: TopologyId; -} - -/** - * Validity summary returned after a freeform edit. - */ -export interface FreeformValidity { - ok: boolean; - healed?: boolean; - diagnostics: FreeformDiagnostic[]; -} - -/** - * Face metadata returned by the freeform editor. - */ -export interface FaceInfo { - face_id: TopologyId; - centroid: Vector3; - normal: Vector3; - surface_type: string; - loop_ids: TopologyId[]; - edge_ids: TopologyId[]; - vertex_ids: TopologyId[]; - adjacent_face_ids: TopologyId[]; -} - -/** - * Edge metadata returned by the freeform editor. - */ -export interface EdgeInfo { - edge_id: TopologyId; - curve_type: string; - start_vertex_id: TopologyId; - end_vertex_id: TopologyId; - start: Vector3; - end: Vector3; - incident_face_ids: TopologyId[]; -} - -/** - * Vertex metadata returned by the freeform editor. - */ -export interface VertexInfo { - vertex_id: TopologyId; - position: Vector3; - edge_ids: TopologyId[]; - face_ids: TopologyId[]; -} - -/** - * Triangulated face render data for topology overlays. - */ -export interface TopologyFaceRenderData { - face_id: TopologyId; - positions: number[]; - indices: number[]; -} - -/** - * Edge polyline render data for topology overlays. - */ -export interface TopologyEdgeRenderData { - edge_id: TopologyId; - positions: number[]; -} - -/** - * Vertex render data for topology overlays. - */ -export interface TopologyVertexRenderData { - vertex_id: TopologyId; - position: Vector3; -} - -/** - * Full renderable topology payload emitted by the freeform editor. - */ -export interface TopologyRenderData { - faces: TopologyFaceRenderData[]; - edges: TopologyEdgeRenderData[]; - vertices: TopologyVertexRenderData[]; -} - -/** - * Structured result payload returned by freeform edit operations. - */ -export interface FreeformEditResult { - entity_id: string; - brep_serialized?: string; - local_brep_serialized?: string; - geometry_serialized?: string; - outline_geometry_serialized?: string; - topology_changed: boolean; - topology_remap?: TopologyRemap; - changed_faces?: TopologyId[]; - changed_edges?: TopologyId[]; - changed_vertices?: TopologyId[]; - validity: FreeformValidity; - placement: ObjectTransformation; -} - -/** - * Optional flags that control how much auxiliary data an edit returns. - */ -export interface EditOperationOptions { - includeBrepSerialized?: boolean; - includeLocalBrepSerialized?: boolean; - includeGeometrySerialized?: boolean; - includeOutlineGeometrySerialized?: boolean; - includeTopologyRemap?: boolean; - includeDeltas?: boolean; - constraintAxis?: Vector3; - constraintPlaneNormal?: Vector3; - preserveCoplanarity?: boolean; - constraintFrame?: "local" | "world"; - openSurfaceMode?: boolean; -} - -/** - * High-level editing mode families exposed by public wrappers. - */ -export type EditingMode = "parametric" | "freeform"; -export type ParametricEntityType = - | "line" - | "polyline" - | "arc" - | "curve" - | "elliptical_arc" - | "rectangle" - | "polygon" - | "cuboid" - | "cylinder" - | "sphere" - | "wedge" - | "opening" - | "sweep"; -export type EntityType = ParametricEntityType | "freeform"; -export type EditFamily = - | "profile" - | "curve" - | "box" - | "radial" - | "wedge" - | "sweep" - | "freeform"; -export type ParametricEditFamily = Exclude; - -/** - * Shared placement structure for parametric wrappers. - */ -export interface ParametricPlacement { - translation: Vector3; - rotation: Vector3; - scale: Vector3; -} - -/** - * Base capability payload shared by parametric and freeform wrappers. - */ -export interface BaseEditCapabilities { - editingMode: EditingMode; - entityType: EntityType; - editFamily: EditFamily; - canEditConfig: boolean; - canEditPlacement: boolean; - canConvertToFreeform: boolean; -} - -/** - * Capability payload returned by parametric wrappers such as `Polygon` or `Cuboid`. - */ -export interface ParametricEditCapabilities extends BaseEditCapabilities { - editingMode: "parametric"; - entityType: ParametricEntityType; -} diff --git a/main/opengeometry-three/src/errors.ts b/main/opengeometry-three/src/errors.ts new file mode 100644 index 0000000..28370ca --- /dev/null +++ b/main/opengeometry-three/src/errors.ts @@ -0,0 +1,21 @@ +export class OGError extends Error { + readonly code: string; + readonly call: string; + readonly details: unknown; + + constructor(code: string, call: string, message: string, details: unknown = {}) { + super(message); + this.name = 'OGError'; + this.code = code; + this.call = call; + this.details = details; + } + + toJSON(): { code: string; call: string; message: string; details: unknown } { + return { code: this.code, call: this.call, message: this.message, details: this.details }; + } + + static fromJSON(value: { code: string; call: string; message: string; details?: unknown }): OGError { + return new OGError(value.code, value.call, value.message, value.details); + } +} diff --git a/main/opengeometry-three/src/examples/booleans.ts b/main/opengeometry-three/src/examples/booleans.ts deleted file mode 100644 index cc98daf..0000000 --- a/main/opengeometry-three/src/examples/booleans.ts +++ /dev/null @@ -1,245 +0,0 @@ -import * as THREE from "three"; -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; - -import { - BooleanExecutionOptions, - booleanIntersection, - booleanSubtraction, - booleanUnion, -} from "../operations/boolean"; -import { AnalyticSolid } from "../shapes/analytic-solid"; - -export type BooleanExampleOperation = "union" | "intersection" | "subtraction"; -export type BooleanExampleMode = "solid" | "polygon" | "extruded"; - -export interface BooleanExampleBuildOptions extends BooleanExecutionOptions { - mode: BooleanExampleMode; - sphereCenter?: Vector3; - polygonOffset?: Vector3; - extrudedOffset?: Vector3; -} - -export interface BooleanExampleBuildResult { - group: THREE.Group; - lhs: THREE.Object3D; - rhs: THREE.Object3D; - result: AnalyticSolid; - title: string; - description: string; -} - -/** - * Builds a complete operand/result scene group for a boolean example page. - */ -export function createBooleanExample( - operation: BooleanExampleOperation, - options: BooleanExampleBuildOptions -): BooleanExampleBuildResult { - const preset = createPreset(operation, options); - - const execute = getBooleanExecutor(operation); - const result = execute(preset.lhsOperand, preset.rhsOperand, options); - - const group = new THREE.Group(); - group.add(preset.lhsVisual); - group.add(preset.rhsVisual); - group.add(result); - - return { - group, - lhs: preset.lhsVisual, - rhs: preset.rhsVisual, - result, - title: preset.title, - description: preset.description, - }; -} - -function createPreset( - operation: BooleanExampleOperation, - options: BooleanExampleBuildOptions -) { - switch (options.mode) { - case "solid": - return createSolidPreset(operation, options); - case "polygon": - return createPolygonPreset(operation, options); - case "extruded": - return createExtrudedPreset(operation, options); - } -} - -/** - * Builds the solid-operand preset used by the example pages. - */ -function createSolidPreset( - operation: BooleanExampleOperation, - options: BooleanExampleBuildOptions -) { - const base = new AnalyticSolid({ - kind: "sphere", - radius: 1, - color: 0x60a5fa, - deflection: 0.01, - }); - base.position.set(-0.45, 1, 0); - - const tool = new AnalyticSolid({ - kind: "sphere", - radius: operation === "intersection" ? 0.95 : 0.85, - color: 0xf97316, - deflection: 0.01, - }); - const center = options.sphereCenter ?? new Vector3(0.45, 1, 0.1); - tool.position.set(center.x, center.y, center.z); - - base.outline = options.outline ?? true; - tool.outline = options.outline ?? true; - - return { - title: `${capitalize(operation)} Analytic Spheres`, - description: "Two exact spheres exercise the in-house analytic boolean path and retain face ancestry.", - lhsOperand: base, - rhsOperand: tool, - lhsVisual: base, - rhsVisual: tool, - }; -} - -/** - * Builds the thin planar-profile solid preset used by the example pages. - */ -function createPolygonPreset( - operation: BooleanExampleOperation, - options: BooleanExampleBuildOptions -) { - const polygonOffset = options.polygonOffset ?? new Vector3(0.0, 0.0, 0.0); - const lhsVertices = [ - new Vector3(-1.8, 0.0, -0.8), - new Vector3(0.4, 0.0, -0.8), - new Vector3(0.4, 0.0, 1.0), - new Vector3(-1.8, 0.0, 1.0), - ]; - const rhsVertices = translateVertices( - operation === "subtraction" - ? [ - new Vector3(-0.55, 0.0, -0.35), - new Vector3(1.2, 0.0, -0.35), - new Vector3(1.2, 0.0, 0.65), - new Vector3(-0.55, 0.0, 0.65), - ] - : [ - new Vector3(-0.4, 0.0, -1.1), - new Vector3(1.55, 0.0, -1.1), - new Vector3(1.55, 0.0, 0.7), - new Vector3(-0.4, 0.0, 0.7), - ], - polygonOffset - ); - - const lhs = new AnalyticSolid({ - kind: "linearExtrusion", - outer: lhsVertices.map((point) => [point.x, -point.z]), - holes: [], - height: 0.12, - color: 0x60a5fa, - deflection: 0.01, - }); - const rhs = new AnalyticSolid({ - kind: "linearExtrusion", - outer: rhsVertices.map((point) => [point.x, -point.z]), - holes: [], - height: 0.12, - color: 0xf97316, - deflection: 0.01, - }); - - lhs.outline = options.outline ?? true; - rhs.outline = options.outline ?? true; - - return { - title: `${capitalize(operation)} Planar Profile Solids`, - description: "Two authoritative line-profile extrusions exercise planar imprint, classify, select, and sew.", - lhsOperand: lhs, - rhsOperand: rhs, - lhsVisual: lhs, - rhsVisual: rhs, - }; -} - -/** - * Builds a strict-v2 line-profile extrusion preset with a coextensive slab. - */ -function createExtrudedPreset( - operation: BooleanExampleOperation, - options: BooleanExampleBuildOptions -) { - const extrudedOffset = options.extrudedOffset ?? new Vector3(0.0, 0.0, 0.0); - - const host = new AnalyticSolid({ - kind: "linearExtrusion", - outer: [[-2.2, -0.18], [2.2, -0.18], [2.2, 0.18], [-2.2, 0.18]], - holes: [], - height: 2.8, - color: 0x60a5fa, - deflection: 0.01, - }); - const cutter = new AnalyticSolid({ - kind: "linearExtrusion", - outer: [[-0.7, -0.34], [0.9, -0.34], [0.9, 0.34], [-0.7, 0.34]], - holes: [], - height: 2.8, - color: 0xf97316, - deflection: 0.01, - }); - cutter.position.set(extrudedOffset.x, 0, extrudedOffset.z); - - host.outline = options.outline ?? true; - cutter.outline = options.outline ?? true; - - return { - title: `${capitalize(operation)} Extruded Solid`, - description: - "Two coextensive authoritative line-profile extrusions use the in-house planar arrangement.", - lhsOperand: host, - rhsOperand: cutter, - lhsVisual: host, - rhsVisual: cutter, - }; -} - -/** - * Selects the exported boolean helper that matches the current example mode. - */ -function getBooleanExecutor(operation: BooleanExampleOperation) { - switch (operation) { - case "union": - return booleanUnion; - case "intersection": - return booleanIntersection; - case "subtraction": - return booleanSubtraction; - } -} - -/** - * Formats the operation name for example titles. - */ -function capitalize(value: string) { - return value.charAt(0).toUpperCase() + value.slice(1); -} - -/** - * Applies a uniform translation to the polygon operand so examples can move it - * interactively without mutating the base preset coordinates. - */ -function translateVertices(vertices: Vector3[], offset: Vector3) { - return vertices.map( - (vertex) => - new Vector3( - vertex.x + offset.x, - vertex.y + offset.y, - vertex.z + offset.z - ) - ); -} diff --git a/main/opengeometry-three/src/examples/editor-modes.ts b/main/opengeometry-three/src/examples/editor-modes.ts deleted file mode 100644 index 5313a08..0000000 --- a/main/opengeometry-three/src/examples/editor-modes.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as THREE from "three"; -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import { FreeformEditResult, createFreeformEditor } from "../editor"; -import { Line } from "../primitives/line"; -import { Polygon } from "../shapes/polygon"; -import { Cuboid } from "../shapes/cuboid"; - -/** - * Demonstrates strict v2 parametric solid edits and the separate sampled-wire - * freeform editor. Analytic solid freeform conversion is intentionally unsupported. - */ -export function createEditorModesExample(scene: THREE.Scene) { - const polygon = new Polygon({ - vertices: [ - new Vector3(-3.2, 0.0, -0.8), - new Vector3(-2.2, 0.0, 0.4), - new Vector3(-1.0, 0.0, 0.0), - new Vector3(-1.5, 0.0, -1.2), - ], - color: 0x2563eb, - fatOutlines: true, - outlineWidth: 4, - }); - polygon.outline = true; - - const polygonCapabilities = polygon.getEditCapabilities(); - polygon.setConfig({ - vertices: [ - new Vector3(-3.4, 0.0, -0.9), - new Vector3(-2.0, 0.0, 0.6), - new Vector3(-0.8, 0.0, -0.1), - new Vector3(-1.6, 0.0, -1.4), - ], - }); - polygon.setPlacement({ - translation: new Vector3(0.0, 0.0, -0.2), - }); - - const cuboid = new Cuboid({ - center: new Vector3(1.2, 0.8, 0.0), - width: 1.2, - height: 1.4, - depth: 1.0, - color: 0x10b981, - deflection: 0.01, - }); - cuboid.outline = true; - - const cuboidCapabilities = cuboid.getEditCapabilities(); - cuboid.setConfig({ - width: 1.6, - height: 1.8, - }); - cuboid.setPlacement({ - translation: new Vector3(0.2, 0.0, 0.0), - rotation: new Vector3(0.0, Math.PI / 9.0, 0.0), - }); - - const guideLine = new Line({ - start: new Vector3(-3.4, 0.0, 1.6), - end: new Vector3(-0.8, 0.0, 1.6), - color: 0x475569, - fatLines: true, - width: 4, - }); - guideLine.setPlacement({ - translation: new Vector3(0.0, 0.0, 0.2), - }); - - const guideLineCapabilities = guideLine.getEditCapabilities(); - const guideLineFreeform = guideLine.toFreeform(`${guideLine.ogid}-freeform`); - const guideLineEditor = createFreeformEditor(guideLineFreeform); - const guideLineEdge = - guideLineEditor.getTopologyRenderData().edges[0]?.edge_id ?? null; - let guideLineSplitResult: FreeformEditResult | null = null; - - if (guideLineEdge !== null) { - guideLineSplitResult = guideLineEditor.splitEdge(guideLineEdge, 0.5, { - includeTopologyRemap: true, - }); - } - - scene.add(polygon); - scene.add(cuboid); - scene.add(guideLine); - - return { - polygon, - cuboid, - guideLine, - polygonCapabilities, - cuboidCapabilities, - guideLineCapabilities, - guideLineFreeform, - guideLineEditor, - guideLineSplitResult, - }; -} diff --git a/main/opengeometry-three/src/examples/index.ts b/main/opengeometry-three/src/examples/index.ts deleted file mode 100644 index 4978f70..0000000 --- a/main/opengeometry-three/src/examples/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Reusable scene example builders for primitives, shapes, and operations. - */ -export * from './primitives'; -export * from './shapes'; -export * from './sweep'; -export * from './offset'; -export * from './offset-ring-variable'; -export * from './ribbon-from-offsets'; -export * from './booleans'; -export * from './editor-modes'; diff --git a/main/opengeometry-three/src/examples/offset-ring-variable.ts b/main/opengeometry-three/src/examples/offset-ring-variable.ts deleted file mode 100644 index e40cfff..0000000 --- a/main/opengeometry-three/src/examples/offset-ring-variable.ts +++ /dev/null @@ -1,71 +0,0 @@ -import * as THREE from "three"; -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import { Polyline } from "../primitives/polyline"; -import { offsetRingVariable } from "../operations/offset-regions"; - -/** Close a kernel ring for Polyline rendering (repeat the first point). */ -function closedRingPoints(ring: { x: number; y: number; z: number }[]): Vector3[] { - const points = ring.map((point) => new Vector3(point.x, point.y, point.z)); - if (points.length > 0) { - const first = ring[0]; - points.push(new Vector3(first.x, first.y, first.z)); - } - return points; -} - -/** - * Variable (per-edge) ring inset: a rectangular "parcel" inset by front 6 / - * side 1.5 / rear 3 setbacks, and a waisted (dumbbell) parcel whose uniform - * 3 m inset SPLITS into two disjoint buildable envelopes — the case a uniform - * offset cannot express. - */ -export function createOffsetRingVariableExample(scene: THREE.Scene) { - const scale = 0.2; // metres → scene units, keeps both parcels in frame - - const parcel = [0, 0, 0, 20, 0, 0, 20, 0, 30, 0, 0, 30].map((v) => v * scale); - const parcelSetbacks = [6, 1.5, 3, 1.5].map((v) => v * scale); - - const dumbbell = [ - 0, 0, 0, 20, 0, 0, 20, 0, 14, 12, 0, 14, 12, 0, 26, 20, 0, 26, 20, 0, 40, 0, 0, 40, 0, 0, 26, - 8, 0, 26, 8, 0, 14, 0, 0, 14, - ].map((v, index) => (index % 3 === 0 ? v * scale + 6 : v * scale)); // shifted +x - const dumbbellSetbacks = new Array(12).fill(3 * scale); - - const parcelOutline = new Polyline({ - points: closedRingPoints( - Array.from({ length: parcel.length / 3 }, (_, i) => ({ - x: parcel[i * 3], - y: parcel[i * 3 + 1], - z: parcel[i * 3 + 2], - })), - ), - color: 0x1f2937, - }); - scene.add(parcelOutline); - - const dumbbellOutline = new Polyline({ - points: closedRingPoints( - Array.from({ length: dumbbell.length / 3 }, (_, i) => ({ - x: dumbbell[i * 3], - y: dumbbell[i * 3 + 1], - z: dumbbell[i * 3 + 2], - })), - ), - color: 0x1f2937, - }); - scene.add(dumbbellOutline); - - const envelopes: Polyline[] = []; - for (const region of offsetRingVariable(parcel, parcelSetbacks)) { - const envelope = new Polyline({ points: closedRingPoints(region.outer), color: 0x16a34a }); - envelopes.push(envelope); - scene.add(envelope); - } - for (const region of offsetRingVariable(dumbbell, dumbbellSetbacks)) { - const envelope = new Polyline({ points: closedRingPoints(region.outer), color: 0xdc2626 }); - envelopes.push(envelope); - scene.add(envelope); - } - - return { parcelOutline, dumbbellOutline, envelopes }; -} diff --git a/main/opengeometry-three/src/examples/offset.ts b/main/opengeometry-three/src/examples/offset.ts deleted file mode 100644 index 52fb82e..0000000 --- a/main/opengeometry-three/src/examples/offset.ts +++ /dev/null @@ -1,112 +0,0 @@ -import * as THREE from "three"; -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import { Line } from "../primitives/line"; -import { Polyline } from "../primitives/polyline"; -import { Rectangle } from "../primitives/rectangle"; -import { Curve } from "../primitives/curve"; - -const EPSILON = 1.0e-9; - -function areClose(a: Vector3, b: Vector3): boolean { - const dx = a.x - b.x; - const dy = a.y - b.y; - const dz = a.z - b.z; - return (dx * dx + dy * dy + dz * dz) <= EPSILON * EPSILON; -} - -function toRenderableOffsetPoints(points: Vector3[], isClosed: boolean): Vector3[] { - const output = points.map((point) => point.clone()); - if (!isClosed || output.length === 0) { - return output; - } - - if (!areClose(output[0], output[output.length - 1])) { - output.push(output[0].clone()); - } - - return output; -} - -export function createOffsetExample(scene: THREE.Scene) { - const baseLine = new Line({ - start: new Vector3(-3.0, 0.0, -2.2), - end: new Vector3(-0.7, 0.0, -1.1), - color: 0x1f2937, - }); - - const lineOffset = baseLine.getOffset(0.35, 35.0, true); - const lineOffsetPrimitive = new Line({ - start: lineOffset.points[0], - end: lineOffset.points[1], - color: 0xef4444, - }); - - const basePolyline = new Polyline({ - points: [ - new Vector3(-1.2, 0.0, -2.4), - new Vector3(0.2, 0.0, -1.6), - new Vector3(0.8, 0.0, -0.4), - new Vector3(0.0, 0.0, -0.2), - new Vector3(2.5, 0.0, 1.2), - ], - color: 0x0f172a, - }); - - const polylineOffset = basePolyline.getOffset(0.45, 35.0, true); - const polylineOffsetPrimitive = new Polyline({ - points: polylineOffset.points, - color: 0xdc2626, - }); - - const baseRectangle = new Rectangle({ - center: new Vector3(1.2, 0.0, -2.1), - width: 1.6, - breadth: 1.0, - color: 0x1d4ed8, - }); - - const rectangleOffset = baseRectangle.getOffset(0.25, 40.0, true); - const rectangleOffsetPrimitive = new Polyline({ - points: toRenderableOffsetPoints(rectangleOffset.points, rectangleOffset.isClosed), - color: 0xf97316, - }); - - const baseCurve = new Curve({ - controlPoints: [ - new Vector3(-3.0, 0.0, 1.3), - new Vector3(-2.0, 0.0, 1.7), - new Vector3(-1.1, 0.0, 1.6), - new Vector3(-0.4, 0.0, 2.0), - ], - color: 0x0ea5e9, - }); - - const curveOffset = baseCurve.getOffset(0.3, 45.0, true); - const curveOffsetPrimitive = new Polyline({ - points: curveOffset.points, - color: 0xbe123c, - }); - - scene.add(baseLine); - scene.add(lineOffsetPrimitive); - scene.add(basePolyline); - scene.add(polylineOffsetPrimitive); - scene.add(baseRectangle); - scene.add(rectangleOffsetPrimitive); - scene.add(baseCurve); - scene.add(curveOffsetPrimitive); - - return { - baseLine, - lineOffsetPrimitive, - basePolyline, - polylineOffsetPrimitive, - baseRectangle, - rectangleOffsetPrimitive, - baseCurve, - curveOffsetPrimitive, - polylineOffset, - rectangleOffset, - curveOffset, - }; -} diff --git a/main/opengeometry-three/src/examples/primitives.ts b/main/opengeometry-three/src/examples/primitives.ts deleted file mode 100644 index 849d327..0000000 --- a/main/opengeometry-three/src/examples/primitives.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as THREE from "three"; -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import { Arc } from "../primitives/arc"; -import { Curve } from "../primitives/curve"; -import { Line } from "../primitives/line"; -import { Polyline } from "../primitives/polyline"; -import { Rectangle } from "../primitives/rectangle"; - -/** - * Adds a basic primitives showcase to the provided scene. - */ -export function createPrimitivesExample(scene: THREE.Scene) { - const line = new Line({ - start: new Vector3(-3.0, 0.0, -2.0), - end: new Vector3(-1.0, 0.0, -1.0), - color: 0x111827, - }); - - const polyline = new Polyline({ - points: [ - new Vector3(-2.0, 0.0, 1.6), - new Vector3(-1.4, 0.0, 2.4), - new Vector3(-0.7, 0.0, 1.8), - new Vector3(-0.2, 0.0, 2.6), - ], - color: 0x0284c7, - }); - - const arc = new Arc({ - center: new Vector3(1.0, 0.0, -1.0), - radius: 1.0, - startAngle: 0.0, - sweepAngle: Math.PI * 1.5, - deflection: 0.01, - color: 0xb91c1c, - }); - - const rectangle = new Rectangle({ - center: new Vector3(2.0, 0.0, 1.8), - width: 1.5, - breadth: 0.9, - color: 0x1d4ed8, - }); - - const curve = new Curve({ - controlPoints: [ - new Vector3(-0.8, 0.0, -2.2), - new Vector3(0.0, 0.0, -2.4), - new Vector3(0.9, 0.0, -1.8), - new Vector3(1.6, 0.0, -2.0), - ], - color: 0x7c3aed, - }); - - scene.add(line); - scene.add(polyline); - scene.add(arc); - scene.add(rectangle); - scene.add(curve); - - return { line, polyline, arc, rectangle, curve }; -} diff --git a/main/opengeometry-three/src/examples/ribbon-from-offsets.ts b/main/opengeometry-three/src/examples/ribbon-from-offsets.ts deleted file mode 100644 index 165b22f..0000000 --- a/main/opengeometry-three/src/examples/ribbon-from-offsets.ts +++ /dev/null @@ -1,192 +0,0 @@ -import * as THREE from "three"; -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import { Polyline } from "../primitives/polyline"; -import { AnalyticSolid } from "../shapes/analytic-solid"; -import { Polygon } from "../shapes/polygon"; - -const EPSILON = 1.0e-9; - -function areClose(a: Vector3, b: Vector3): boolean { - const dx = a.x - b.x; - const dy = a.y - b.y; - const dz = a.z - b.z; - return (dx * dx + dy * dy + dz * dz) <= EPSILON * EPSILON; -} - -function buildRibbonOutline(left: Vector3[], right: Vector3[]): Vector3[] { - if (left.length === 0 || right.length === 0) { - return []; - } - - const outline = [...left.map((point) => point.clone())]; - const rightReversed = [...right.map((point) => point.clone())].reverse(); - outline.push(...rightReversed); - - if (outline.length > 2 && areClose(outline[0], outline[outline.length - 1])) { - outline.pop(); - } - - return outline; -} - -export interface RibbonFromOffsetsOptions { - /** Subtract one exact coextensive through slot. Default `false`. */ - cutThroughSlot?: boolean; - /** Width of the through slot along its centerline segment. Default `0.6`. */ - slotWidth?: number; - /** - * Ribbon extrusion height in meters. Default `2.6` to match the example HTML. - */ - height?: number; -} - -function midpoint(a: Vector3, b: Vector3): Vector3 { - return new Vector3((a.x + b.x) * 0.5, (a.y + b.y) * 0.5, (a.z + b.z) * 0.5); -} - -function buildThroughSlot( - centerlinePoints: Vector3[], - ribbonThickness: number, - height: number, - width: number, -): AnalyticSolid { - const a = centerlinePoints[2]; - const b = centerlinePoints[3]; - const mid = midpoint(a, b); - const length = Math.hypot(b.x - a.x, b.z - a.z); - const tangent = [(b.x - a.x) / length, (b.z - a.z) / length]; - const normal = [-tangent[1], tangent[0]]; - const halfWidth = width * 0.5; - const halfDepth = ribbonThickness * 0.5 + 0.1; - const point = (along: number, across: number): [number, number] => [ - mid.x + tangent[0] * along + normal[0] * across, - -(mid.z + tangent[1] * along + normal[1] * across), - ]; - return new AnalyticSolid({ - kind: "linearExtrusion", - outer: [ - point(-halfWidth, -halfDepth), - point(halfWidth, -halfDepth), - point(halfWidth, halfDepth), - point(-halfWidth, halfDepth), - ], - holes: [], - height, - color: 0xef4444, - deflection: 0.01, - }); -} - -/** - * Renders a human-readable description for an analytic geometry error (or any - * other thrown value). Used by the example HTML to surface kernel failures - * in a status panel without losing the structured payload. - */ -export function describeRibbonSubtractError(error: unknown): string { - if (error instanceof Error) { - return error.message; - } - return String(error); -} - -export function createRibbonFromOffsetsExample( - scene: THREE.Scene, - options: RibbonFromOffsetsOptions = {} -) { - const centerline = new Polyline({ - points: [ - new Vector3(-2.6, 0.0, -1.9), - new Vector3(-1.2, 0.0, -1.0), - new Vector3(-0.2, 0.0, 0.1), - new Vector3(0.6, 0.0, 0.2), - new Vector3(0.1, 0.0, 1.0), - new Vector3(2.6, 0.0, 2.0), - ], - color: 0x1f2937, - }); - - const ribbonThickness = 0.45; - const half = ribbonThickness * 0.5; - const acuteThreshold = 35.0; - const bevel = true; - const ribbonHeight = options.height ?? 2.6; - - const leftOffset = centerline.getOffset(half, acuteThreshold, bevel); - const rightOffset = centerline.getOffset(-half, acuteThreshold, bevel); - - const leftOffsetPolyline = new Polyline({ - points: leftOffset.points, - color: 0x22c55e, - }); - - const rightOffsetPolyline = new Polyline({ - points: rightOffset.points, - color: 0xf97316, - }); - - const ribbonOutline = buildRibbonOutline(leftOffset.points, rightOffset.points); - if (ribbonOutline.length < 3) { - throw new Error("Failed to create ribbon polygon from offsets."); - } - - const ribbonPolygon = new Polygon({ - vertices: ribbonOutline, - color: 0x3b82f6, - }); - - ribbonPolygon.position.y = 0.01; - - scene.add(centerline); - scene.add(leftOffsetPolyline); - scene.add(rightOffsetPolyline); - scene.add(ribbonPolygon); - - let cutResult: { ribbon: THREE.Object3D; cutters: AnalyticSolid[] } | null = null; - let cutError: unknown = null; - - if (options.cutThroughSlot ?? false) { - const ribbonSolid = new AnalyticSolid({ - kind: "linearExtrusion", - outer: ribbonOutline.map((point) => [point.x, -point.z]), - holes: [], - height: ribbonHeight, - color: 0x3b82f6, - deflection: 0.01, - }); - const cutter = buildThroughSlot( - [ - new Vector3(-2.6, 0.0, -1.9), - new Vector3(-1.2, 0.0, -1.0), - new Vector3(-0.2, 0.0, 0.1), - new Vector3(0.6, 0.0, 0.2), - new Vector3(0.1, 0.0, 1.0), - new Vector3(2.6, 0.0, 2.0), - ], - ribbonThickness, - ribbonHeight, - options.slotWidth ?? 0.6, - ); - const cutters = [cutter]; - - try { - const cutRibbon = ribbonSolid.subtract(cutters, { color: 0x3b82f6 }); - scene.add(cutRibbon); - cutResult = { ribbon: cutRibbon, cutters }; - } catch (error) { - cutError = error; - scene.add(ribbonSolid); - cutResult = { ribbon: ribbonSolid, cutters }; - } - } - - return { - centerline, - leftOffsetPolyline, - rightOffsetPolyline, - ribbonPolygon, - leftOffset, - rightOffset, - cutResult, - cutError, - }; -} diff --git a/main/opengeometry-three/src/examples/shapes.ts b/main/opengeometry-three/src/examples/shapes.ts deleted file mode 100644 index e0d956c..0000000 --- a/main/opengeometry-three/src/examples/shapes.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as THREE from "three"; -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import { Cuboid } from "../shapes/cuboid"; -import { Cylinder } from "../shapes/cylinder"; -import { Opening } from "../shapes/opening"; -import { Polygon } from "../shapes/polygon"; -import { Sweep } from "../shapes/sweep"; -import { Sphere } from "../shapes/sphere"; -import { Wedge } from "../shapes/wedge"; - -/** - * Adds a basic shapes showcase to the provided scene. - */ -export function createShapesExample(scene: THREE.Scene) { - const fatOutlineWidth = 4; - - const polygon = new Polygon({ - vertices: [ - new Vector3(-3.0, 0.0, -0.5), - new Vector3(-2.2, 0.0, 0.5), - new Vector3(-1.2, 0.0, 0.2), - new Vector3(-1.5, 0.0, -0.8), - ], - color: 0x2563eb, - fatOutlines: true, - outlineWidth: fatOutlineWidth, - }); - polygon.outline = true; - - const cuboid = new Cuboid({ - center: new Vector3(0.0, 0.8, -1.2), - width: 1.2, - height: 1.6, - depth: 1.0, - color: 0x10b981, - deflection: 0.01, - }); - cuboid.outline = true; - - const cylinder = new Cylinder({ - center: new Vector3(1.8, 0.8, -0.8), - radius: 0.6, - height: 1.6, - color: 0xf97316, - deflection: 0.01, - }); - cylinder.outline = true; - - const wedge = new Wedge({ - center: new Vector3(3.4, 0.8, -1.0), - width: 1.2, - height: 1.4, - depth: 1.0, - color: 0x7c3aed, - fatOutlines: true, - outlineWidth: fatOutlineWidth, - }); - wedge.outline = true; - - const sphere = new Sphere({ - center: new Vector3(0.6, 1, 1.2), - radius: 0.9, - color: 0x0ea5e9, - deflection: 0.01, - }); - sphere.outline = true; - - const opening = new Opening({ - center: new Vector3(2.4, 0.9, 1.2), - width: 1.0, - height: 1.8, - depth: 0.3, - color: 0x9ca3af, - deflection: 0.01, - }); - opening.outline = true; - - const sweep = new Sweep({ - path: [ - new Vector3(-2.8, 0.0, 2.4), - new Vector3(-2.2, 0.6, 2.8), - new Vector3(-1.4, 1.2, 2.4), - new Vector3(-0.8, 1.7, 1.8), - ], - profile: [ - new Vector3(-0.2, 0.0, -0.2), - new Vector3(0.2, 0.0, -0.2), - new Vector3(0.2, 0.0, 0.2), - new Vector3(-0.2, 0.0, 0.2), - ], - color: 0x14b8a6, - capStart: true, - capEnd: true, - fatOutlines: true, - outlineWidth: fatOutlineWidth, - }); - sweep.outline = true; - - scene.add(polygon); - scene.add(cuboid); - scene.add(cylinder); - scene.add(wedge); - scene.add(sphere); - scene.add(opening); - scene.add(sweep); - - return { polygon, cuboid, cylinder, wedge, sphere, opening, sweep }; -} diff --git a/main/opengeometry-three/src/examples/sweep.ts b/main/opengeometry-three/src/examples/sweep.ts deleted file mode 100644 index ac51e6c..0000000 --- a/main/opengeometry-three/src/examples/sweep.ts +++ /dev/null @@ -1,69 +0,0 @@ -import * as THREE from "three"; -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import { Polyline } from "../primitives/polyline"; -import { Rectangle } from "../primitives/rectangle"; -import { Sweep } from "../shapes/sweep"; - -type KernelVertex = { - position: { - x: number; - y: number; - z: number; - }; -}; - -type KernelBrep = { - vertices: KernelVertex[]; -}; - -function toVector3List(vertices: KernelVertex[]): Vector3[] { - return vertices.map((vertex) => { - const { x, y, z } = vertex.position; - return new Vector3(x, y, z); - }); -} - -export function createSweepExample(scene: THREE.Scene) { - const pathPrimitive = new Polyline({ - points: [ - new Vector3(-2.0, 0.0, -1.0), - new Vector3(-1.2, 0.5, 0.2), - new Vector3(0.0, 1.1, 0.9), - new Vector3(1.1, 1.7, 0.3), - new Vector3(2.0, 2.2, -0.8), - ], - color: 0x444444, - }); - - const profilePrimitive = new Rectangle({ - center: new Vector3(0, 0, 0), - width: 0.7, - breadth: 0.4, - color: 0x1f77b4, - }); - - // const profilePrimitive = new Arc(); - - const profileBrep = profilePrimitive.getBrep() as KernelBrep; - const profilePoints = toVector3List(profileBrep.vertices); - - const sweep = new Sweep({ - path: pathPrimitive.options.points.map((point) => point.clone()), - profile: profilePoints, - color: 0x2a9d8f, - capStart: true, - capEnd: false, - fatOutlines: true, - outlineWidth: 4, - }); - - scene.add(pathPrimitive); - scene.add(profilePrimitive); - scene.add(sweep); - - return { - pathPrimitive, - profilePrimitive, - sweep, - }; -} diff --git a/main/opengeometry-three/src/export/analytic-ifc.ts b/main/opengeometry-three/src/export/analytic-ifc.ts deleted file mode 100644 index 61745c3..0000000 --- a/main/opengeometry-three/src/export/analytic-ifc.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { getUUID } from "../utils/randomizer"; - -export type IfcPreparedValue = - | { kind: "Ref"; id: number } - | { kind: "List"; values: IfcPreparedValue[] } - | { kind: "Real"; value: number } - | { kind: "Integer"; value: number } - | { kind: "Text" | "Enum"; value: string } - | { kind: "Bool"; value: boolean } - | { kind: "Null" | "Derived" }; - -export interface AnalyticExchangeBodyV2 { - schema_version: 2; - revision: string; - length_unit: "metre"; - up_axis: "Z"; - coordinate_space: "world"; - representation_type: "AdvancedBrep" | "CSG"; - quality: { kind: "Analytic" } | { kind: "Approximate"; max_error: number }; - geometric_tolerance: number; - exchange_error_bound: number; - validation_level: string; - entities: { id: number; entity_type: string; attributes: IfcPreparedValue[] }[]; - solids: number[]; - faces: { - face: number; key: string; entity: number; - provenance: { - sources: { entity: string; body: string; key: string; face: number }[]; - role: "Authored" | "Preserved" | "Split" | "Cut" | "Coincident"; reversed: boolean; - }; - }[]; -} - -function quoted(text: string): string { - let escaped = ""; - for (const character of text) { - const code = character.charCodeAt(0); - if (character === "'") escaped += "''"; - else if (code === 92) escaped += "\\\\"; - else if (code >= 32 && code <= 126) escaped += character; - else { - const utf16 = Array.from({ length: character.length }, (_, i) => ("0000" + character.charCodeAt(i).toString(16).toUpperCase()).slice(-4)).join(""); - escaped += "\\X2\\" + utf16 + "\\X0\\"; - } - } - return "'" + escaped + "'"; -} -function guid(): string { - const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$"; - const uuid = getUUID().replace(/-/g, ""); - const first = parseInt(uuid.slice(0, 2), 16); - let value = alphabet[Math.floor(first / 64)] + alphabet[first % 64]; - for (let i = 2; i < 32; i += 6) { - let block = parseInt(uuid.slice(i, i + 6), 16); - let digits = ""; - for (let j = 0; j < 4; j++) { digits = alphabet[block % 64] + digits; block = Math.floor(block / 64); } - value += digits; - } - return value; -} - -/** Serializes Rust-prepared geometry; this function does not fit curves or tessellate surfaces. */ -export function analyticIfcText(body: AnalyticExchangeBodyV2, name = "Analytic body"): string { - if (body.schema_version !== 2 || body.length_unit !== "metre" || body.up_axis !== "Z" || body.coordinate_space !== "world" - || !Array.isArray(body.entities) || !body.entities.length || body.entities.length > 100_000 - || !Array.isArray(body.solids) || !body.solids.length || !Array.isArray(body.faces) - || !/^(0|[1-9][0-9]{0,19})$/.test(body.revision) || (body.revision.length === 20 && body.revision > "18446744073709551615") - || !Number.isFinite(body.geometric_tolerance) || body.geometric_tolerance <= 0 - || !Number.isFinite(body.exchange_error_bound) || body.exchange_error_bound < body.geometric_tolerance - || typeof name !== "string" || name.length > 4096) throw new Error("Invalid analytic IFC exchange contract"); - const entities: string[] = []; - let budget = 500_000; - function encode(value: IfcPreparedValue, before: number, depth = 0): string { - if (--budget < 0 || depth > 16 || !value || typeof value !== "object") throw new Error("IFC aggregate resource limit exceeded"); - switch (value.kind) { - case "Ref": - if (!Number.isInteger(value.id) || value.id <= 0 || value.id >= before) throw new Error("Invalid IFC entity reference"); - return "#" + value.id; - case "List": - if (!Array.isArray(value.values)) throw new Error("IFC aggregate must be an array"); - return "(" + value.values.map(v => encode(v, before, depth + 1)).join(",") + ")"; - case "Real": - if (!Number.isFinite(value.value)) throw new Error("Non-finite IFC real value"); - return value.value.toExponential(17); - case "Integer": - if (!Number.isSafeInteger(value.value)) throw new Error("Invalid IFC integer value"); - return String(value.value); - case "Text": - if (typeof value.value !== "string" || value.value.length > 4096) throw new Error("Invalid IFC string"); - return quoted(value.value); - case "Enum": - if (!/^[A-Z][A-Z0-9_]*$/.test(value.value)) throw new Error("Invalid IFC enumeration"); - return "." + value.value + "."; - case "Bool": - if (typeof value.value !== "boolean") throw new Error("IFC logical value must be boolean"); - return value.value ? ".T." : ".F."; - case "Null": return "$"; - case "Derived": return "*"; - default: throw new Error("Unsupported IFC prepared value"); - } - } - for (const entity of body.entities) { - if (entity.id !== entities.length + 1 || !/^Ifc[A-Za-z0-9]+$/.test(entity.entity_type) - || !Array.isArray(entity.attributes) || entity.attributes.length > 16) throw new Error("Invalid IFC prepared entity"); - entities.push("#" + entity.id + "=" + entity.entity_type.toUpperCase() + "(" + entity.attributes.map(value => encode(value, entity.id)).join(",") + ");"); - } - const add = (expression: string) => { const id = entities.length + 1; entities.push("#" + id + "=" + expression + ";"); return id; }; - const origin = add("IFCCARTESIANPOINT((0.,0.,0.))"); - const z = add("IFCDIRECTION((0.,0.,1.))"); - const x = add("IFCDIRECTION((1.,0.,0.))"); - const placement = add("IFCAXIS2PLACEMENT3D(#" + origin + ",#" + z + ",#" + x + ")"); - const context = add("IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3," + body.exchange_error_bound.toExponential(17) + ",#" + placement + ",$)"); - const length = add("IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.)"); - const units = add("IFCUNITASSIGNMENT((#" + length + "))"); - add("IFCPROJECT(" + quoted(guid()) + ",$," + quoted(name) + ",$,$,$,$,(#" + context + "),#" + units + ")"); - const solidRefs = body.solids.map(id => { - const expected = body.representation_type === "AdvancedBrep" ? "IfcAdvancedBrep" : "IfcBooleanResult"; - if (!Number.isInteger(id) || body.entities[id - 1]?.entity_type !== expected) throw new Error("Invalid IFC solid reference"); - return "#" + id; - }).join(","); - if (["AdvancedBrep", "CSG"].indexOf(body.representation_type) < 0) throw new Error("Invalid IFC representation type"); - const representation = add("IFCSHAPEREPRESENTATION(#" + context + ",'Body'," + quoted(body.representation_type) + ",(" + solidRefs + "))"); - const productShape = add("IFCPRODUCTDEFINITIONSHAPE($,$,(#" + representation + "))"); - const objectPlacement = add("IFCLOCALPLACEMENT($,#" + placement + ")"); - add("IFCBUILDINGELEMENTPROXY(" + quoted(guid()) + ",$," + quoted(name) + ",$,$,#" + objectPlacement + ",#" + productShape + ",$,.NOTDEFINED.)"); - return [ - "ISO-10303-21;", "HEADER;", "FILE_DESCRIPTION(('OpenGeometry analytic BRep v2'),'2;1');", - "FILE_NAME(" + quoted(name + ".ifc") + ",'1970-01-01T00:00:00',(''),(''),'OpenGeometry','OpenGeometry','');", - "FILE_SCHEMA(('IFC4'));", "ENDSEC;", "DATA;", ...entities, "ENDSEC;", "END-ISO-10303-21;", "", - ].join("\n"); -} diff --git a/main/opengeometry-three/src/export/export-step.ts b/main/opengeometry-three/src/export/export-step.ts new file mode 100644 index 0000000..1e83908 --- /dev/null +++ b/main/opengeometry-three/src/export/export-step.ts @@ -0,0 +1,36 @@ +import type { Body } from '../bodies/body'; +import { SystemAssembly } from '../bodies/system-assembly'; +import type { StepExport } from '../dto/step-export'; +import { OGError } from '../errors'; +import { call } from '../kernel/kernel-session'; +import { bodyKey, runtime } from '../runtime/runtime-state'; +import { decodeStepExport, encode } from '../world-graph/codec'; +import { checkHandle, worldGraph } from '../world-graph/world-graph-client'; + +const LABEL = 'OpenGeometry.exportStep'; + +export function exportStep(options: { + nodes: (Body | SystemAssembly | string)[]; + unit?: 'metre' | 'millimetre'; + upAxis?: 'Y' | 'Z'; + name?: string; + timestamp?: string; +}): Promise { + const { nodes, ...other } = options; + return new Promise((resolve) => { + for (const item of nodes) checkLive(item); + const value = call(LABEL, (): unknown => worldGraph().exportStep( + encode(nodes.map((item) => typeof item === 'string' ? item : item.ogId)), + encode(other), + )); + resolve(decodeStepExport(value, LABEL)); + }); +} + +function checkLive(item: Body | SystemAssembly | string): void { + if (typeof item === 'string') return; + if (!(item instanceof SystemAssembly) && runtime().bodies.get(bodyKey(item.handle, item.generation)) !== item) { + throw new OGError('Disposed', LABEL, `${item.ogId} is not a live body`); + } + checkHandle(LABEL, item); +} diff --git a/main/opengeometry-three/src/freeform/geometry.ts b/main/opengeometry-three/src/freeform/geometry.ts deleted file mode 100644 index 51e87bd..0000000 --- a/main/opengeometry-three/src/freeform/geometry.ts +++ /dev/null @@ -1,277 +0,0 @@ -import { - OGFreeformGeometry, - Vector3, -} from "../../../opengeometry/pkg/opengeometry"; - -import type { - CreateFreeformGeometryOptions, - FreeformSource, - ObjectTransformation, -} from "./types"; - -function parseJson(payload: string): T { - return JSON.parse(payload) as T; -} - -function toVector3(value: { x: number; y: number; z: number }): Vector3 { - return new Vector3(value.x, value.y, value.z); -} - -function toPlainVector3(value: { x: number; y: number; z: number }) { - return { - x: value.x, - y: value.y, - z: value.z, - }; -} - -function cloneVector( - vector: Vector3 | undefined, - fallback: [number, number, number] -): Vector3 { - return vector?.clone() ?? new Vector3(...fallback); -} - -function normalizePlacement( - placement: ObjectTransformation -): ObjectTransformation { - return { - anchor: toVector3(placement.anchor), - translation: toVector3(placement.translation), - rotation: toVector3(placement.rotation), - scale: toVector3(placement.scale), - }; -} - -function toObjectTransformation( - placement: NonNullable -): ObjectTransformation { - return { - anchor: cloneVector(placement.anchor, [0, 0, 0]), - translation: cloneVector(placement.translation, [0, 0, 0]), - rotation: cloneVector(placement.rotation, [0, 0, 0]), - scale: cloneVector(placement.scale, [1, 1, 1]), - }; -} - -function toPlainObjectTransformation(transform: ObjectTransformation) { - return { - anchor: toPlainVector3(transform.anchor), - translation: toPlainVector3(transform.translation), - rotation: toPlainVector3(transform.rotation), - scale: toPlainVector3(transform.scale), - }; -} - -function serializeBrepLike(value: unknown): string { - return typeof value === "string" ? value : JSON.stringify(value); -} - -function normalizeFreeformSerialized(source: FreeformSource): string { - if (typeof source === "string") { - return source; - } - - if (typeof source.getLocalBrepSerialized === "function") { - return source.getLocalBrepSerialized(); - } - - if (typeof source.getLocalBrepData === "function") { - return serializeBrepLike(source.getLocalBrepData()); - } - - if (typeof source.getBrepSerialized === "function") { - return source.getBrepSerialized(); - } - - if (typeof source.getBrepData === "function") { - return serializeBrepLike(source.getBrepData()); - } - - if (typeof source.getBrep === "function") { - return serializeBrepLike(source.getBrep()); - } - - return JSON.stringify(source); -} - -/** - * Thin client-side wrapper around the kernel `OGFreeformGeometry` object. - */ -export class FreeformGeometry { - private readonly geometry: OGFreeformGeometry; - - constructor(geometry: OGFreeformGeometry) { - this.geometry = geometry; - } - - /** - * Returns the stable kernel-side id for this freeform geometry object. - */ - getId(): string { - return this.geometry.id; - } - - /** - * Returns the world-space serialized BRep payload. - */ - getBrepSerialized(): string { - return this.geometry.getBrepSerialized(); - } - - /** - * Returns the local-space serialized BRep payload. - */ - getLocalBrepSerialized(): string { - return this.geometry.getLocalBrepSerialized(); - } - - /** - * Returns the serialized triangle position buffer emitted by the kernel. - */ - getGeometrySerialized(): string { - return this.geometry.getGeometrySerialized(); - } - - /** - * Returns world-space triangle positions as a plain number array. - */ - getGeometryBuffer(): number[] { - return Array.from(this.geometry.getGeometryBuffer()); - } - - /** - * Returns local-space triangle positions as a plain number array. - */ - getLocalGeometryBuffer(): number[] { - return Array.from(this.geometry.getLocalGeometryBuffer()); - } - - /** - * Returns the serialized outline buffer emitted by the kernel. - */ - getOutlineGeometrySerialized(): string { - return this.geometry.getOutlineGeometrySerialized(); - } - - /** - * Returns world-space outline segment positions as a plain number array. - */ - getOutlineGeometryBuffer(): number[] { - return Array.from(this.geometry.getOutlineGeometryBuffer()); - } - - /** - * Returns local-space outline segment positions as a plain number array. - */ - getLocalOutlineGeometryBuffer(): number[] { - return Array.from(this.geometry.getLocalOutlineGeometryBuffer()); - } - - /** - * Returns the current normalized placement for this freeform geometry. - */ - getPlacement(): ObjectTransformation { - return normalizePlacement( - parseJson(this.geometry.getPlacementSerialized()) - ); - } - - /** - * Replaces the full placement transform in one call. - */ - setPlacement(transform: ObjectTransformation): void { - this.geometry.setPlacementSerialized( - JSON.stringify(toPlainObjectTransformation(transform)) - ); - } - - /** - * Sets translation, rotation, and scale without changing the current anchor. - */ - setTransform(translation: Vector3, rotation: Vector3, scale: Vector3): void { - this.geometry.setTransform(translation, rotation, scale); - } - - /** - * Updates only the translation component of the placement. - */ - setTranslation(translation: Vector3): void { - this.geometry.setTranslation(translation); - } - - /** - * Updates only the rotation component of the placement. - */ - setRotation(rotation: Vector3): void { - this.geometry.setRotation(rotation); - } - - /** - * Updates only the scale component of the placement. - */ - setScale(scale: Vector3): void { - this.geometry.setScale(scale); - } - - /** - * Updates the explicit modeling anchor used by downstream transforms. - */ - setAnchor(anchor: Vector3): void { - this.geometry.setAnchor(anchor); - } - - /** @internal */ - getKernelGeometry(): OGFreeformGeometry { - return this.geometry; - } -} - -/** - * Creates a `FreeformGeometry` wrapper from serialized BRep JSON, a parsed - * BRep object, or another wrapper exposing BRep accessors. - */ -export function createFreeformGeometry( - source: FreeformSource, - options?: CreateFreeformGeometryOptions -): FreeformGeometry { - const id = options?.id ?? `freeform-${Date.now()}`; - const localBrepSerialized = normalizeFreeformSerialized(source); - const geometry = new FreeformGeometry( - new OGFreeformGeometry(id, localBrepSerialized) - ); - - if (options?.placement) { - geometry.setPlacement(toObjectTransformation(options.placement)); - } - - return geometry; -} - -/** Triangulates an explicitly faceted BRep, including its planar face holes. */ -export function tessellateFacetedBrep(serialized: string): Float64Array { - if (typeof serialized !== "string" || serialized.length > 64 * 1024 * 1024) { - throw new Error("Faceted BRep must be serialized JSON within the 64 MiB limit"); - } - const data = JSON.parse(serialized) as Record; - if (data === null || typeof data !== "object" || "schema_version" in data) { - throw new Error("Use analytic tessellation for a versioned analytic BRep"); - } - for (const name of ["vertices", "edges", "halfedges", "loops", "faces", "wires", "shells"]) { - const table = data[name]; - if (!Array.isArray(table) || table.length > 2_000_000) { - throw new Error(`Faceted BRep requires a bounded ${name} table`); - } - } - const geometry = new OGFreeformGeometry("faceted-tessellation", serialized); - try { - const positions = geometry.getLocalGeometryBuffer(); - if (!positions.length || positions.length % 9 !== 0 || positions.length > 18_000_000 - || positions.some(value => !Number.isFinite(value))) { - throw new Error("Faceted BRep triangulation did not produce a finite mesh within the triangle limit"); - } - return positions; - } finally { - geometry.free(); - } -} diff --git a/main/opengeometry-three/src/freeform/index.ts b/main/opengeometry-three/src/freeform/index.ts deleted file mode 100644 index 5079641..0000000 --- a/main/opengeometry-three/src/freeform/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Freeform geometry types and helpers for working with raw BRep-backed models. - */ -export * from "./types"; -export { FreeformGeometry, createFreeformGeometry, tessellateFacetedBrep } from "./geometry"; diff --git a/main/opengeometry-three/src/freeform/types.ts b/main/opengeometry-three/src/freeform/types.ts deleted file mode 100644 index f2bffdb..0000000 --- a/main/opengeometry-three/src/freeform/types.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; - -/** - * Placement values accepted when constructing freeform geometry wrappers. - */ -export interface FreeformPlacementInput { - anchor?: Vector3; - translation: Vector3; - rotation: Vector3; - scale: Vector3; -} - -/** - * Fully normalized placement state used by freeform editing APIs. - */ -export interface ObjectTransformation { - anchor: Vector3; - translation: Vector3; - rotation: Vector3; - scale: Vector3; -} - -/** - * Options for building a `FreeformGeometry` wrapper from a BRep-like source. - */ -export interface CreateFreeformGeometryOptions { - id?: string; - placement?: FreeformPlacementInput | ObjectTransformation; -} - -/** - * Accepted sources for creating a freeform wrapper. - */ -export type FreeformSource = - | string - | Record - | { - getLocalBrepSerialized?: () => string; - getLocalBrepData?: () => unknown; - getBrepSerialized?: () => string; - getBrepData?: () => unknown; - getBrep?: () => unknown; - }; diff --git a/main/opengeometry-three/src/kernel/kernel-errors.ts b/main/opengeometry-three/src/kernel/kernel-errors.ts new file mode 100644 index 0000000..58cf4cd --- /dev/null +++ b/main/opengeometry-three/src/kernel/kernel-errors.ts @@ -0,0 +1,12 @@ +export function parseKernelError(text: string): { code: string; message: string; details: unknown } | undefined { + let value: unknown; + try { + value = JSON.parse(text); + } catch { + return undefined; + } + if (typeof value !== 'object' || value === null || !('code' in value) || !('message' in value)) return undefined; + const { code, message } = value; + if (typeof code !== 'string' || typeof message !== 'string' || !code || !message) return undefined; + return { code, message, details: 'details' in value ? value.details : undefined }; +} diff --git a/main/opengeometry-three/src/kernel/kernel-loader.ts b/main/opengeometry-three/src/kernel/kernel-loader.ts new file mode 100644 index 0000000..38e001d --- /dev/null +++ b/main/opengeometry-three/src/kernel/kernel-loader.ts @@ -0,0 +1,19 @@ +import initializeKernel, { + initSync, OGTessellator, OGWorldGraph, type InitOutput, +} from '../../../opengeometry/pkg/opengeometry.js'; + +export { initializeKernel, initSync, OGTessellator, OGWorldGraph }; + +export function compileKernel(wasmURL: string | URL): Promise { + return WebAssembly.compileStreaming(fetch(wasmURL)); +} + +export function initKernel(module: WebAssembly.Module): Promise { + return initializeKernel({ module_or_path: module }); +} + +export function takePanicMessage(): string | undefined { + const message: unknown = Reflect.get(globalThis, '__opengeometryPanic'); + Reflect.deleteProperty(globalThis, '__opengeometryPanic'); + return typeof message === 'string' ? message : undefined; +} diff --git a/main/opengeometry-three/src/kernel/kernel-session.ts b/main/opengeometry-three/src/kernel/kernel-session.ts new file mode 100644 index 0000000..0525144 --- /dev/null +++ b/main/opengeometry-three/src/kernel/kernel-session.ts @@ -0,0 +1,33 @@ +import { OGError } from '../errors'; +import { emit } from '../runtime/event-bus'; +import { runtime as runtimeState } from '../runtime/runtime-state'; +import { parseKernelError } from './kernel-errors'; +import { takePanicMessage } from './kernel-loader'; + +export function kernelCall(call: string, fn: () => T): T { + try { + return fn(); + } catch (cause) { + if (cause instanceof OGError) throw cause; + if (cause instanceof WebAssembly.RuntimeError) { + throw new OGError('KernelPanic', call, takePanicMessage() ?? cause.message); + } + const value = typeof cause === 'string' ? parseKernelError(cause) : undefined; + if (value) throw new OGError(value.code, call, value.message, value.details); + throw new OGError('InvalidGeometry', call, String(cause)); + } +} + +export function call(name: string, fn: () => T): T { + const runtime = runtimeState(); + if (runtime.poisoned) throw new OGError('KernelPanic', name, 'kernel runtime is poisoned'); + try { + return kernelCall(name, fn); + } catch (error) { + if (error instanceof OGError && error.code === 'KernelPanic') { + runtime.poisoned = true; + emit('fatal', error); + } + throw error; + } +} diff --git a/main/opengeometry-three/src/limits.ts b/main/opengeometry-three/src/limits.ts new file mode 100644 index 0000000..5935df9 --- /dev/null +++ b/main/opengeometry-three/src/limits.ts @@ -0,0 +1,7 @@ +export const ACCURACY = { geometric: 1e-8, intersection: 1e-9, tessellation: 0.01, exchange: 1e-6 } as const; +export const MAX_TRIANGLES = 2_000_000 as const; +export const HYSTERESIS_KEEP = [0.75, 4] as const; +export const PIXELS_AT_REST = 0.5 as const; +export const PIXELS_MOVING = 2 as const; +export const EVALUATION_INTERVAL_MS = 100 as const; +export const MOTION_WINDOW_MS = 150 as const; diff --git a/main/opengeometry-three/src/marks/limbo.ts b/main/opengeometry-three/src/marks/limbo.ts new file mode 100644 index 0000000..beae591 --- /dev/null +++ b/main/opengeometry-three/src/marks/limbo.ts @@ -0,0 +1,56 @@ +import type { Body } from '../bodies/body'; +import { call } from '../kernel/kernel-session'; +import { bodyKey, reindexShape, runtime, type Runtime } from '../runtime/runtime-state'; +import { node, worldGraph } from '../world-graph/world-graph-client'; + +export function enterLimbo(body: Body): void { + if (body.inLimbo) return; + const state = runtime(); + const parent = body.parent; + hide(state, body); + body.inLimbo = true; + if (state.marks.size === 0) { + body.finalize(); + return; + } + state.limbo.set(body, { watermark: Math.max(...state.marks.keys()), parent }); +} + +export function finalizeSettled(state: Runtime): void { + const oldest = Math.min(...state.marks.keys()); + for (const [body, entry] of [...state.limbo]) if (entry.watermark < oldest) body.finalize(); +} + +export function revive(body: Body): void { + const state = runtime(); + const entry = state.limbo.get(body); + if (!entry) return; + try { + call('OGMark.rollback', () => worldGraph().nodeByHandle(body.handle, body.generation)); + state.limbo.delete(body); + body.inLimbo = false; + body.visible = true; + entry.parent?.add(body); + const previous = body.lastInfo.shapeId; + body.lastInfo = node(body.ogId, 'OGMark.rollback'); + reindexShape(state, body, previous); + } catch { body.visible = false; } +} + +export function finalizeNow(body: Body): void { + if (!body.inLimbo) hide(runtime(), body); + body.finalize(); +} + +function hide(state: Runtime, body: Body): void { + body.visible = false; + state.displayed.delete(body); + const parent = body.parent; + if (state.flushing || state.renderPassActive) { + queueMicrotask(() => { if (!revived(state, body)) parent?.remove(body); }); + } else parent?.remove(body); +} + +function revived(state: Runtime, body: Body): boolean { + return !body.inLimbo && state.bodies.get(bodyKey(body.handle, body.generation)) === body; +} diff --git a/main/opengeometry-three/src/marks/og-mark.ts b/main/opengeometry-three/src/marks/og-mark.ts new file mode 100644 index 0000000..5922acc --- /dev/null +++ b/main/opengeometry-three/src/marks/og-mark.ts @@ -0,0 +1,46 @@ +import type { MarkStats } from '../dto/mark-stats'; +import { OGError } from '../errors'; +import { call } from '../kernel/kernel-session'; +import { flush } from '../rendering/geometry/geometry-scheduler'; +import { bodyKey, runtime } from '../runtime/runtime-state'; +import { decodeChanges, decodeMarkStats } from '../world-graph/codec'; +import { worldGraph } from '../world-graph/world-graph-client'; +import { finalizeNow, finalizeSettled, revive } from './limbo'; + +export class OGMark { + constructor(private slot: number) {} + private get active(): boolean { return runtime().marks.get(this.slot) === this; } + rollback(): void { + if (!this.active) throw new OGError('InvalidMark', 'OGMark.rollback', 'mark is released'); + const changes = decodeChanges(call('OGMark.rollback', () => worldGraph().rollback(this.slot)), 'OGMark.rollback'); + const state = runtime(); + for (const slot of [...state.marks.keys()]) if (slot > this.slot) state.marks.delete(slot); + for (const change of changes.removed) { + const body = state.bodies.get(bodyKey(change.handle, change.generation)); + if (body) finalizeNow(body); + } + flush(); + for (const change of changes.added) { + const body = state.bodies.get(bodyKey(change.handle, change.generation)); + if (body) revive(body); + } + } + release(): void { + if (!this.active) throw new OGError('InvalidMark', 'OGMark.release', 'mark is released'); + call('OGMark.release', () => { worldGraph().release(this.slot); }); + const state = runtime(); + state.marks.delete(this.slot); + finalizeSettled(state); + } +} + +export function createMark(): OGMark { + const slot = call('OpenGeometry.mark', () => worldGraph().mark()); + const mark = new OGMark(slot); + runtime().marks.set(slot, mark); + return mark; +} + +export function markStats(): MarkStats { + return decodeMarkStats(call('OpenGeometry.markStats', () => worldGraph().markStats()), 'OpenGeometry.markStats'); +} diff --git a/main/opengeometry-three/src/marks/transaction.ts b/main/opengeometry-three/src/marks/transaction.ts new file mode 100644 index 0000000..4ea29da --- /dev/null +++ b/main/opengeometry-three/src/marks/transaction.ts @@ -0,0 +1,19 @@ +import { OGError } from '../errors'; +import { createMark } from './og-mark'; + +export function transaction(fn: () => T, options: { dryRun?: boolean } = {}): T { + const mark = createMark(); + try { + const result = fn(); + if (result && typeof (result as { then?: unknown }).then === 'function') { + throw new OGError('InvalidParameter', 'OpenGeometry.transaction', 'transaction callback must be synchronous'); + } + if (options.dryRun) mark.rollback(); + mark.release(); + return result; + } catch (error) { + mark.rollback(); + mark.release(); + throw error; + } +} diff --git a/main/opengeometry-three/src/markup/spotMarker.ts b/main/opengeometry-three/src/markup/spotMarker.ts deleted file mode 100644 index 8932aeb..0000000 --- a/main/opengeometry-three/src/markup/spotMarker.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer.js"; - -function spotLabelElement() { - const spotLabelElement = document.createElement("div"); - spotLabelElement.style.position = "absolute"; - spotLabelElement.style.width = "3px"; - spotLabelElement.style.height = "3px"; - spotLabelElement.style.backgroundColor = "blue"; - spotLabelElement.style.pointerEvents = "none"; - // spotLabelElement.style.borderRadius = "50%"; - spotLabelElement.style.border = "1px solid black"; - return spotLabelElement; -} - -export class SpotLabel extends CSS2DObject { - constructor() { - const spotLabel = spotLabelElement(); - super(spotLabel); - } -} diff --git a/main/opengeometry-three/src/opengeometry.ts b/main/opengeometry-three/src/opengeometry.ts new file mode 100644 index 0000000..4967e47 --- /dev/null +++ b/main/opengeometry-three/src/opengeometry.ts @@ -0,0 +1,68 @@ +import type * as THREE from 'three'; +import type { Body } from './bodies/body'; +import type { SystemAssembly } from './bodies/system-assembly'; +import type { Events, Listener } from './dto/events'; +import type { MarkStats } from './dto/mark-stats'; +import type { StepExport } from './dto/step-export'; +import { OGError } from './errors'; +import { exportStep } from './export/export-step'; +import { createMark, markStats, type OGMark } from './marks/og-mark'; +import { transaction } from './marks/transaction'; +import { resolveHit } from './picking/resolve-hit'; +import { flush } from './rendering/geometry/geometry-scheduler'; +import { settled } from './rendering/geometry/settle'; +import { motionHint, overrideChanged } from './rendering/lod/lod-controller'; +import { createRuntimeOnce } from './runtime/create-runtime'; +import { on } from './runtime/event-bus'; +import { reset } from './runtime/reset'; +import { currentRuntime, runtime } from './runtime/runtime-state'; + +export class OpenGeometry { + static async create( + input: { wasmURL?: string | URL; wasmModule?: WebAssembly.Module } = {}, + options: { workerURL?: string | URL; tessellation?: 'worker' | 'inline' } = {}, + ): Promise { + if (currentRuntime()) return this; + await createRuntimeOnce(input, options); + return this; + } + + static on(event: Events, handler: Listener): () => boolean { return on(event, handler); } + static flush(options: { geometry?: 'sync' } = {}): void { flush(options); } + static settled(options: { deflection?: number } = {}): Promise<{ failed: { ogId: string; error: unknown }[] }> { + return settled(options); + } + + static setDisplayDeflection(world?: number): void { + if (world !== undefined && (!Number.isFinite(world) || world <= 0)) { + throw new OGError('InvalidParameter', 'setDisplayDeflection', 'deflection must be positive'); + } + Object.assign(runtime(), { displayDeflection: world }); + overrideChanged(runtime()); + } + + static setCameraMotion(moving: boolean): void { + const state = runtime(); + if (moving) motionHint(state, Date.now()); + } + + static resolveHit( + hit: THREE.Intersection, + ): { ogId: string; shapeRevision: number; faceId?: number; edgeId?: number } | undefined { + return resolveHit(hit); + } + + static exportStep(options: { + nodes: (Body | SystemAssembly | string)[]; + unit?: 'metre' | 'millimetre'; + upAxis?: 'Y' | 'Z'; + name?: string; + timestamp?: string; + }): Promise { return exportStep(options); } + + static mark(): OGMark { return createMark(); } + static markStats(): MarkStats { return markStats(); } + static transaction(fn: () => T, options: { dryRun?: boolean } = {}): T { return transaction(fn, options); } + + static reset(): void { reset(); } +} diff --git a/main/opengeometry-three/src/operations/analytic-errors.ts b/main/opengeometry-three/src/operations/analytic-errors.ts deleted file mode 100644 index 10c5bc0..0000000 --- a/main/opengeometry-three/src/operations/analytic-errors.ts +++ /dev/null @@ -1,106 +0,0 @@ -export type AnalyticGeometryErrorCode = - | "math" - | "invalid_geometry" - | "invalid_topology" - | "unsupported_geometry" - | "ambiguous_profile_alignment" - | "coverage_gap" - | "singular_parameterization" - | "missing_reference" - | "unsupported_schema" - | "unresolved_intersection" - | "unresolved_tessellation" - | "limit_exceeded" - | "unknown"; - -export class AnalyticGeometryError extends Error { - readonly code: AnalyticGeometryErrorCode; - readonly detail: unknown; - readonly families?: readonly [string, string]; - - constructor(code: AnalyticGeometryErrorCode, message: string, detail?: unknown) { - super(message); - this.name = "AnalyticGeometryError"; - this.code = code; - this.detail = detail; - if (code === "coverage_gap" && isRecord(detail) && isStringPair(detail.families)) { - this.families = detail.families; - } - } -} - -export function parseAnalyticGeometryError(raw: unknown): AnalyticGeometryError { - const serialized = extractMessage(raw); - try { - const payload = JSON.parse(serialized) as unknown; - if (typeof payload === "string") { - const code = errorCode(payload); - return new AnalyticGeometryError(code, messageFor(code)); - } - if (isRecord(payload)) { - const variant = Object.keys(payload)[0]; - if (variant) { - const detail = payload[variant]; - const code = errorCode(variant); - return new AnalyticGeometryError(code, messageFor(code, detail), detail); - } - } - } catch { - // The fallback below preserves non-kernel JavaScript failures. - } - return new AnalyticGeometryError("unknown", serialized || "Unknown analytic geometry error", raw); -} - -function messageFor(code: AnalyticGeometryErrorCode, detail?: unknown): string { - if (code === "coverage_gap" && isRecord(detail) && isStringPair(detail.families)) { - return `Analytic boolean coverage is unavailable for ${detail.families[0]} and ${detail.families[1]}.`; - } - if (code === "missing_reference" && isRecord(detail)) { - return `Missing ${String(detail.kind ?? "geometry")} reference ${String(detail.index ?? "")}.`.trim(); - } - if (code === "unsupported_schema" && isRecord(detail)) { - return `Unsupported BRep schema version ${String(detail.found ?? "unknown")}; schema version 2 is required.`; - } - if (typeof detail === "string" && detail.length > 0) return detail; - return code.replace(/_/g, " "); -} - -function errorCode(variant: string): AnalyticGeometryErrorCode { - const code = variant.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase(); - switch (code) { - case "math": - case "invalid_geometry": - case "invalid_topology": - case "unsupported_geometry": - case "ambiguous_profile_alignment": - case "coverage_gap": - case "singular_parameterization": - case "missing_reference": - case "unsupported_schema": - case "unresolved_intersection": - case "unresolved_tessellation": - case "limit_exceeded": - return code; - default: - return "unknown"; - } -} - -function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); -} - -function isStringPair(value: unknown): value is [string, string] { - return Array.isArray(value) && value.length === 2 && value.every((item) => typeof item === "string"); -} - -function extractMessage(raw: unknown): string { - if (typeof raw === "string") return raw; - if (raw instanceof Error) return raw.message; - if (isRecord(raw) && typeof raw.message === "string") return raw.message; - try { - return String(raw ?? ""); - } catch { - return "Unknown analytic geometry error"; - } -} diff --git a/main/opengeometry-three/src/operations/boolean-regions.ts b/main/opengeometry-three/src/operations/boolean-regions.ts deleted file mode 100644 index fe26642..0000000 --- a/main/opengeometry-three/src/operations/boolean-regions.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as OGKernel from "../../../opengeometry/pkg/opengeometry"; -import type { OffsetRegion, OffsetRegionPoint } from "./offset-regions"; - -export type PlanarBooleanOperation = "union" | "intersection" | "subtraction"; - -export type CurvedRegionEdge = - | { kind: "line"; from: [number, number]; to: [number, number] } - | { kind: "arc"; center: [number, number]; radius: number; start_angle: number; sweep_angle: number }; - -export interface CurvedRegion2D { - outer: CurvedRegionEdge[]; - holes: CurvedRegionEdge[][]; -} - -type KernelRegionsResult = { regionsSerialized: string }; -type KernelBooleanRegions = ( - aJson: string, - bJson: string, - operation: PlanarBooleanOperation, -) => KernelRegionsResult; - -export function booleanRegions2D( - a: readonly OffsetRegion[], - b: readonly OffsetRegion[], - operation: PlanarBooleanOperation, -): OffsetRegion[] { - const kernelExport = (OGKernel as Record).booleanRegions2D; - if (typeof kernelExport !== "function") { - throw new Error("booleanRegions2D is unavailable in the loaded WASM package"); - } - const point = (value: OffsetRegionPoint): [number, number, number] => [ - value.x, - value.y, - value.z, - ]; - const encode = (regions: readonly OffsetRegion[]) => - JSON.stringify( - regions.map((region) => ({ - outer: region.outer.map(point), - holes: region.holes.map((hole) => hole.map(point)), - })), - ); - const result = (kernelExport as KernelBooleanRegions)(encode(a), encode(b), operation); - return JSON.parse(result.regionsSerialized) as OffsetRegion[]; -} - -/** Analytic line/arc region Boolean; output arcs retain their exact circles. */ -export function booleanCurvedRegions2D( - a: readonly CurvedRegion2D[], - b: readonly CurvedRegion2D[], - operation: PlanarBooleanOperation, -): CurvedRegion2D[] { - const kernelExport = (OGKernel as Record).booleanCurvedRegions2D; - if (typeof kernelExport !== "function") { - throw new Error("booleanCurvedRegions2D is unavailable in the loaded WASM package"); - } - const result = (kernelExport as (a: string, b: string, operation: PlanarBooleanOperation) => string)( - JSON.stringify(a), JSON.stringify(b), operation, - ); - return JSON.parse(result) as CurvedRegion2D[]; -} diff --git a/main/opengeometry-three/src/operations/boolean.ts b/main/opengeometry-three/src/operations/boolean.ts deleted file mode 100644 index 4a26cd8..0000000 --- a/main/opengeometry-three/src/operations/boolean.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as THREE from "three"; - -import { AnalyticSolid, type AnalyticBooleanOp } from "../shapes/analytic-solid"; - -/** Rendering and tessellation options applied to an analytic boolean result. */ -export interface BooleanExecutionOptions { - deflection?: number; - color?: THREE.ColorRepresentation; - opacity?: number; - transparent?: boolean; - side?: THREE.Side; - outline?: boolean; -} - -export type BooleanOperand = AnalyticSolid; - -export function booleanUnion( - lhs: AnalyticSolid, - rhs: AnalyticSolid, - options?: BooleanExecutionOptions, -): AnalyticSolid { - return executeBoolean(lhs, rhs, "union", options); -} - -export function booleanIntersection( - lhs: AnalyticSolid, - rhs: AnalyticSolid, - options?: BooleanExecutionOptions, -): AnalyticSolid { - return executeBoolean(lhs, rhs, "intersection", options); -} - -export function booleanSubtraction( - lhs: AnalyticSolid, - rhs: AnalyticSolid, - options?: BooleanExecutionOptions, -): AnalyticSolid { - return executeBoolean(lhs, rhs, "subtraction", options); -} - -export function executeBooleanSubtractionMany( - lhs: BooleanOperand, - cutters: BooleanOperand[], - options?: BooleanExecutionOptions, -): AnalyticSolid { - const host = analyticOperand(lhs); - if (!Array.isArray(cutters) || cutters.length === 0) { - throw new Error("Analytic subtraction requires a nonempty operand array."); - } - const analyticCutters = cutters.map(analyticOperand); - let result = host.boolean(analyticCutters[0], "subtraction", { - deflection: options?.deflection ?? host.deflection, - color: options?.color, - }); - try { - for (const cutter of analyticCutters.slice(1)) { - const next = result.boolean(cutter, "subtraction", { - deflection: options?.deflection ?? result.deflection, - color: options?.color, - }); - result.dispose(); - result = next; - } - } catch (error) { - result.dispose(); - throw error; - } - applyRenderOptions(result, options); - return result; -} - -function executeBoolean( - lhs: AnalyticSolid, - rhs: AnalyticSolid, - operation: AnalyticBooleanOp, - options?: BooleanExecutionOptions, -): AnalyticSolid { - const host = analyticOperand(lhs); - const tool = analyticOperand(rhs); - const result = host.boolean(tool, operation, { - deflection: options?.deflection ?? host.deflection, - color: options?.color, - }); - applyRenderOptions(result, options); - return result; -} - -function analyticOperand(operand: BooleanOperand): AnalyticSolid { - if (!(operand instanceof AnalyticSolid)) { - throw new Error( - "Boolean operations require strict analytic BRep v2 operands; faceted BRep operands are unsupported.", - ); - } - return operand; -} - -function applyRenderOptions(result: AnalyticSolid, options?: BooleanExecutionOptions): void { - const material = result.surface.material; - if (options?.color !== undefined) material.color.set(options.color); - if (options?.opacity !== undefined) { - material.opacity = options.opacity; - material.transparent = options.opacity < 1 || material.transparent; - } - if (options?.transparent !== undefined) material.transparent = options.transparent; - if (options?.side !== undefined) material.side = options.side; - material.needsUpdate = true; - result.outline = options?.outline ?? true; -} diff --git a/main/opengeometry-three/src/operations/editor/freeform.ts b/main/opengeometry-three/src/operations/editor/freeform.ts deleted file mode 100644 index 74b506f..0000000 --- a/main/opengeometry-three/src/operations/editor/freeform.ts +++ /dev/null @@ -1 +0,0 @@ -export { FreeformEditor, createFreeformEditor } from "../../editor/freeform"; diff --git a/main/opengeometry-three/src/operations/editor/index.ts b/main/opengeometry-three/src/operations/editor/index.ts deleted file mode 100644 index cb6455a..0000000 --- a/main/opengeometry-three/src/operations/editor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "../../editor"; diff --git a/main/opengeometry-three/src/operations/editor/parametric.ts b/main/opengeometry-three/src/operations/editor/parametric.ts deleted file mode 100644 index 9457e3e..0000000 --- a/main/opengeometry-three/src/operations/editor/parametric.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { - clonePlacement, - createParametricEditCapabilities, - toObjectTransformation, -} from "../../editor/parametric"; diff --git a/main/opengeometry-three/src/operations/editor/types.ts b/main/opengeometry-three/src/operations/editor/types.ts deleted file mode 100644 index 65b1767..0000000 --- a/main/opengeometry-three/src/operations/editor/types.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "../../editor/types"; diff --git a/main/opengeometry-three/src/operations/extrude.ts b/main/opengeometry-three/src/operations/extrude.ts deleted file mode 100644 index 2462d46..0000000 --- a/main/opengeometry-three/src/operations/extrude.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as OGKernel from "../../../opengeometry/pkg/opengeometry"; - -import type { FreeformSource } from "../freeform"; - - -type KernelExtrudeBrepFace = (...args: [string, number]) => string; - - -/** - * Sources accepted by the BRep-face extrusion helper. - */ -export type ExtrudeBrepSource = FreeformSource; - -/** - * Extrudes the first face or wire in a local BRep payload into a closed solid BRep. - */ -export function extrudeBrepFace( - source: ExtrudeBrepSource, - height: number -): string { - const extrudeExport = (OGKernel as Record).extrudeBrepFace; - if (typeof extrudeExport !== "function") { - throw new Error( - "extrudeBrepFace is not available in the loaded wasm package. Rebuild opengeometry wasm bindings." - ); - } - - const kernelFunction = extrudeExport as KernelExtrudeBrepFace; - return kernelFunction(resolveLocalBrepSerialized(source), height); -} - -function resolveLocalBrepSerialized(source: ExtrudeBrepSource): string { - if (typeof source === "string") { - return source; - } - - if (typeof source.getLocalBrepSerialized === "function") { - return source.getLocalBrepSerialized(); - } - - if (typeof source.getLocalBrepData === "function") { - return serializeBrepLike(source.getLocalBrepData()); - } - - if (typeof source.getBrepSerialized === "function") { - return source.getBrepSerialized(); - } - - if (typeof source.getBrepData === "function") { - return serializeBrepLike(source.getBrepData()); - } - - if (typeof source.getBrep === "function") { - return serializeBrepLike(source.getBrep()); - } - - return JSON.stringify(source); -} - -function serializeBrepLike(value: unknown): string { - return typeof value === "string" ? value : JSON.stringify(value); -} diff --git a/main/opengeometry-three/src/operations/index.ts b/main/opengeometry-three/src/operations/index.ts deleted file mode 100644 index 99e2985..0000000 --- a/main/opengeometry-three/src/operations/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Kernel-backed boolean helpers and renderable result mesh. - */ -export * from "./boolean"; -export * from "./boolean-regions"; -export * from "./analytic-errors"; -export * from "./extrude"; -export * from "./loft"; -export * from "./offset-regions"; diff --git a/main/opengeometry-three/src/operations/loft.ts b/main/opengeometry-three/src/operations/loft.ts deleted file mode 100644 index 679ec31..0000000 --- a/main/opengeometry-three/src/operations/loft.ts +++ /dev/null @@ -1,46 +0,0 @@ -import * as THREE from "three"; - -import { - AnalyticSolid, - type AnalyticPolygonLoftAlignment, -} from "../shapes/analytic-solid"; - -export type PolygonLoftPoint = readonly [number, number, number]; - -export interface PolygonLoftOptions { - alignment?: AnalyticPolygonLoftAlignment; - deflection?: number; - color?: THREE.ColorRepresentation; -} - -/** - * Creates a capped ruled solid between two planar polygon sections. - * Sections must have equal vertex counts and produce planar side faces. - */ -export function loftPolygonSections( - sections: readonly [readonly PolygonLoftPoint[], readonly PolygonLoftPoint[]], - options: PolygonLoftOptions = {}, -): AnalyticSolid { - const [lower, upper] = sections; - return new AnalyticSolid({ - kind: "polygonLoft", - lower: copySection(lower, "lower"), - upper: copySection(upper, "upper"), - alignment: options.alignment ?? "auto", - deflection: options.deflection, - color: options.color, - }); -} - -function copySection( - section: readonly PolygonLoftPoint[], - label: string, -): [number, number, number][] { - if (section.length < 3) throw new Error(`${label} loft section requires at least three points`); - return section.map((point) => { - if (point.length !== 3 || !point.every(Number.isFinite)) { - throw new Error(`${label} loft section points must contain three finite coordinates`); - } - return [point[0], point[1], point[2]]; - }); -} diff --git a/main/opengeometry-three/src/operations/offset-regions.ts b/main/opengeometry-three/src/operations/offset-regions.ts deleted file mode 100644 index 86b4335..0000000 --- a/main/opengeometry-three/src/operations/offset-regions.ts +++ /dev/null @@ -1,147 +0,0 @@ -import * as OGKernel from "../../../opengeometry/pkg/opengeometry"; - -/** A point of a returned offset region ring (kernel `Vector3` shape). */ -export interface OffsetRegionPoint { - x: number; - y: number; - z: number; -} - -/** One offset region: a CW outer ring + its CCW inner-void hole rings. */ -export interface OffsetRegion { - /** Outer ring (CW, canonical start). */ - outer: OffsetRegionPoint[]; - /** Inner-void hole rings (CCW), e.g. the interior void of a closed loop. */ - holes: OffsetRegionPoint[][]; -} - -type KernelOffsetRegionsResult = { - regionsSerialized: string; -}; - -type KernelOffsetPolylineRegions = ( - centreline: Float64Array, - width: number, - closed: boolean, - miterLimit: number, -) => KernelOffsetRegionsResult; - -/** - * Offset a centreline by ±half-width into one or more filled REGIONS, via the - * deterministic analytic offset — miters / flat bevels at `miterLimit`, concave - * trims, and nonzero-winding resolution of reflex / closed loops. NO boolean union - * (so it is deterministic, unlike the 3D-mesh CSG path). A simple centreline returns - * one region; a self-crossing closed centreline (e.g. a figure-8) returns one per - * simple sub-loop. Returns an EMPTY array for a collapsed input (does - * not throw, so it never looks like a kernel crash). Rings are CW-outer / CCW-holes. - * - * @param centrelineFlat centreline points as `[x,y,z, …]`. - * @param width full stroke width (offset ±width/2 on each side). - * @param miterLimit SVG stroke-miterlimit; corners sharper than this bevel. Default 4. - */ -export function offsetPolylineRegions( - centrelineFlat: number[] | Float64Array, - width: number, - closed: boolean, - miterLimit = 4, -): OffsetRegion[] { - const buildExport = (OGKernel as Record).offsetPolylineRegions; - if (typeof buildExport !== "function") { - throw new Error( - "offsetPolylineRegions is not available in the loaded wasm package. Rebuild opengeometry wasm bindings.", - ); - } - const flat = - centrelineFlat instanceof Float64Array ? centrelineFlat : new Float64Array(centrelineFlat); - const result = (buildExport as KernelOffsetPolylineRegions)(flat, width, closed, miterLimit); - return JSON.parse(result.regionsSerialized); -} - -type KernelOffsetRingVariable = ( - ringFlat: Float64Array, - distances: Float64Array, - holesJson: string, -) => KernelOffsetRegionsResult; - -/** An excluded interior ring of a variable inset. Its closed ring has one - * clearance distance per edge; the inset grows the hole by those distances. */ -export interface OffsetHole { - ring: number[] | Float64Array; - distances: number[] | Float64Array; -} - -/** - * Inset a CLOSED ring inward with one distance per edge (edge i = - * `ring[i] → ring[i+1]`; an explicit closing duplicate point is accepted and - * stripped, the distance count stays one per unique edge). The result is a - * clearance-exact inset region: the ring's interior minus every point within - * `distances[i]` of edge i's segment. Distant boundary segments remain - * effective across notches; different distances produce circumscribed - * clearance arcs. There is no - * `miterLimit` parameter, deliberately: miters and bevels only approximate the - * clearance arc, and a bevel would claim points inside the required distance. - * - * `holes` are excluded interior rings, each grown outward by its own per-edge - * distances — the region keeps clear of a hole's edges exactly as it keeps - * clear of the outer boundary, so a hole can split the region or swallow it. - * - * Returns CW-outer / CCW-hole regions (canonical start vertex), possibly - * SEVERAL when a deep inset (or a hole) splits a waisted ring into disjoint - * lobes, and an EMPTY array when the inset collapses or the outer ring is - * degenerate (< 3 unique points, zero area, self-intersecting). THROWS on - * malformed arguments: a distance count that does not match an edge count, a - * negative / non-finite distance, or a degenerate hole — those are caller - * bugs, not geometry. - * - * @param ring closed ring points as `[x,y,z, …]` (Y is carried through). - * @param distances inward inset distance per edge, metres, each >= 0 - * (0 keeps that boundary edge in place). - * @param holes excluded interior rings with their own per-edge distances. - */ -export function offsetRingVariable( - ring: number[] | Float64Array, - distances: number[] | Float64Array, - holes: OffsetHole[] = [], -): OffsetRegion[] { - const buildExport = (OGKernel as Record).offsetRingVariable; - if (typeof buildExport !== "function") { - throw new Error( - "offsetRingVariable is not available in the loaded wasm package. It requires opengeometry >= 2.0.12 — rebuild or upgrade the opengeometry wasm bindings.", - ); - } - const flatRing = ring instanceof Float64Array ? ring : new Float64Array(ring); - const flatDistances = - distances instanceof Float64Array ? distances : new Float64Array(distances); - const holesJson = JSON.stringify( - holes.map((hole) => ({ ring: Array.from(hole.ring), distances: Array.from(hole.distances) })), - ); - const result = (buildExport as KernelOffsetRingVariable)(flatRing, flatDistances, holesJson); - return JSON.parse(result.regionsSerialized); -} - -/** One polyline in an offset group: centreline `[x,y,z, …]`, stroke width, closed flag. */ -export interface OffsetPolyline { - centreline: number[]; - width: number; - closed: boolean; -} - -type KernelOffsetPolylineGroupRegions = (polylinesJson: string) => KernelOffsetRegionsResult; - -/** - * Merge a GROUP of separate polylines (a crossing T / X / L overlap) into one clean - * region by nonzero-winding union of their mitered bands — the overlapping strokes - * merge into a single region with mitered/bevelled corners, no internal edges. - * Returns CW-outer / CCW-hole regions (one or more); empty if no region remains. - * No CSG. Used to render/extrude an overlapping crossing as one joined mass. - */ -export function offsetPolylineGroupRegions(polylines: OffsetPolyline[]): OffsetRegion[] { - const buildExport = (OGKernel as Record).offsetPolylineGroupRegions; - if (typeof buildExport !== "function") { - throw new Error( - "offsetPolylineGroupRegions is not available in the loaded wasm package. Rebuild opengeometry wasm bindings.", - ); - } - const result = (buildExport as KernelOffsetPolylineGroupRegions)(JSON.stringify(polylines)); - return JSON.parse(result.regionsSerialized); -} diff --git a/main/opengeometry-three/src/picking/pick-targets.ts b/main/opengeometry-three/src/picking/pick-targets.ts new file mode 100644 index 0000000..b910e4b --- /dev/null +++ b/main/opengeometry-three/src/picking/pick-targets.ts @@ -0,0 +1,35 @@ +import type * as THREE from 'three'; +import type { Body } from '../bodies/body'; +import { OGError } from '../errors'; +import type { GeometryRecord } from '../rendering/records/geometry-record'; +import { runtime } from '../runtime/runtime-state'; + +function faceRangeValue(ranges: Uint32Array, index: number): number { + const value = ranges[index]; + if (value === undefined) throw new OGError('InvalidGeometry', 'OpenGeometry.resolveHit', 'face range out of bounds'); + return value; +} + +export function pickTarget(object: THREE.Object3D): Body | undefined { + return runtime().pickTargets.get(object); +} + +export function faceIdAt(ranges: Uint32Array, faceIndex: number): number | undefined { + const length = ranges.length; + if (length % 3 !== 0) throw new OGError('InvalidGeometry', 'OpenGeometry.resolveHit', 'face ranges are not triples'); + let low = 0; + let high = length / 3; + while (low < high) { + const middle = Math.floor((low + high) / 2); + if (faceRangeValue(ranges, 3 * middle + 1) <= faceIndex) low = middle + 1; + else high = middle; + } + if (low === 0) return undefined; + const offset = 3 * (low - 1); + const start = faceRangeValue(ranges, offset + 1); + return faceIndex < start + faceRangeValue(ranges, offset + 2) ? faceRangeValue(ranges, offset) : undefined; +} + +export function edgeIdAt(record: GeometryRecord, index: number): number | undefined { + return record.edgeIds[Math.floor(index / 2)]; +} diff --git a/main/opengeometry-three/src/picking/resolve-hit.ts b/main/opengeometry-three/src/picking/resolve-hit.ts new file mode 100644 index 0000000..45fa876 --- /dev/null +++ b/main/opengeometry-three/src/picking/resolve-hit.ts @@ -0,0 +1,14 @@ +import type * as THREE from 'three'; +import type { PickResult } from '../dto/pick-result'; +import { edgeIdAt, faceIdAt, pickTarget } from './pick-targets'; + +export function resolveHit(hit: THREE.Intersection): PickResult | undefined { + const body = pickTarget(hit.object); + if (!body?.record) return undefined; + const result: PickResult = { ogId: body.ogId, shapeRevision: body.record.revision }; + if (hit.object === body.surface && hit.faceIndex !== undefined) { + const faceId = faceIdAt(body.record.faceRanges, hit.faceIndex); + if (faceId !== undefined) result.faceId = faceId; + } else if (hit.index !== undefined) Object.assign(result, { edgeId: edgeIdAt(body.record, hit.index) }); + return result; +} diff --git a/main/opengeometry-three/src/primitives/analytic-curve.ts b/main/opengeometry-three/src/primitives/analytic-curve.ts deleted file mode 100644 index 7c7709a..0000000 --- a/main/opengeometry-three/src/primitives/analytic-curve.ts +++ /dev/null @@ -1,146 +0,0 @@ -import * as THREE from "three"; -import { OGAnalyticBrep } from "../../../opengeometry/pkg/opengeometry"; -import type { AnalyticAccuracy, AnalyticFrame } from "../shapes/analytic-solid"; -import { getUUID } from "../utils/randomizer"; -import { GEOMETRY_DEFLECTION_SHARE } from "../rendering/analytic-tessellation"; - -export type AnalyticCurveOptions = { - ogid?: string; - frame?: AnalyticFrame; - accuracy?: AnalyticAccuracy; - startAngle: number; - sweepAngle: number; - deflection?: number; - color?: THREE.ColorRepresentation; -} & ( - | { kind: "arc"; radius: number } - | { kind: "ellipticalArc"; majorRadius: number; minorRadius: number } -); - -export interface AnalyticCurveStats { - requestedDeflection: number; - achievedDeflection: number; - segmentCount: number; - revision: bigint; -} - -export class AnalyticCurve extends THREE.Group { - readonly ogid: string; - readonly line: THREE.LineSegments; - protected kernel: OGAnalyticBrep; - private segmentEdges = new Uint32Array(0); - private disposed = false; - - get color(): THREE.Color { return this.line.material.color; } - set color(value: THREE.ColorRepresentation) { - this.checkActive(); - this.line.material.color.set(value); - this.line.material.needsUpdate = true; - } - - constructor(options: AnalyticCurveOptions) { - super(); - this.ogid = options.ogid ?? getUUID(); - const frame = options.frame ?? { - origin: [0, 0, 0], x: [1, 0, 0], y: [0, 0, -1], z: [0, 1, 0], - }; - const radius = options.kind === "arc" ? options.radius : options.majorRadius; - const geometric = Math.max(1e-9, 2 * radius * 1e-8, ...frame.origin.map(value => Math.abs(value) * 64 * Number.EPSILON)); - const accuracy = options.accuracy ?? { geometric, intersection: geometric / 4, tessellation: 0.001, exchange: 0.00001 }; - this.kernel = createKernel(options, this.ogid, frame, accuracy); - this.line = new THREE.LineSegments(new THREE.BufferGeometry(), new THREE.LineBasicMaterial({ color: options.color ?? 0x2563eb })); - this.line.userData.ogid = this.ogid; - this.add(this.line); - try { this.retessellate(options.deflection ?? accuracy.tessellation); } - catch (error) { this.dispose(); throw error; } - } - - getBrepSerialized(): string { - this.checkActive(); - return this.kernel.get_brep_serialized(); - } - - getBrepData(): Record { - return JSON.parse(this.getBrepSerialized()) as Record; - } - - protected replaceDefinition(options: AnalyticCurveOptions): AnalyticCurveStats { - this.checkActive(); - const frame = options.frame ?? { - origin: [0, 0, 0], x: [1, 0, 0], y: [0, 0, -1], z: [0, 1, 0], - }; - const radius = options.kind === "arc" ? options.radius : options.majorRadius; - const geometric = Math.max(1e-9, 2 * radius * 1e-8, ...frame.origin.map(value => Math.abs(value) * 64 * Number.EPSILON)); - const accuracy = options.accuracy ?? { geometric, intersection: geometric / 4, tessellation: 0.001, exchange: 0.00001 }; - const next = createKernel(options, this.ogid, frame, accuracy); - const previous = this.kernel; - this.kernel = next; - try { - const statistics = this.retessellate(options.deflection ?? accuracy.tessellation); - if (options.color !== undefined) this.color = options.color; - previous.free(); - return statistics; - } catch (error) { - this.kernel = previous; - next.free(); - throw error; - } - } - - edgeIdForSegment(segment: number): number | undefined { - if (!Number.isInteger(segment) || segment < 0) return undefined; - return this.segmentEdges[segment]; - } - - retessellate(deflection: number): AnalyticCurveStats { - this.checkActive(); - if (!Number.isFinite(deflection) || deflection <= 0) throw new Error("Deflection must be finite and positive"); - const result = this.kernel.tessellate(deflection * GEOMETRY_DEFLECTION_SHARE); - try { - const bounds = this.kernel.get_bounds(); - const origin = [0, 1, 2].map(i => bounds[i] / 2 + bounds[i + 3] / 2); - const positions = result.outline_positions(); - const packed = new Float32Array(positions.length); - let encodingError = 0; - for (let i = 0; i < positions.length; i += 3) { - const errors = [0, 0, 0]; - for (let j = 0; j < 3; j++) { - const local = positions[i + j] - origin[j]; packed[i + j] = local; - errors[j] = Math.abs(local - packed[i + j]); - } - encodingError = Math.max(encodingError, Math.hypot(...errors)); - } - if (!Number.isFinite(encodingError) || encodingError > deflection / 4) throw new Error("GPU coordinate precision cannot represent the requested deflection"); - const edgeIds = result.outline_edge_ids(); - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute("position", new THREE.BufferAttribute(packed, 3)); - geometry.computeBoundingBox(); geometry.computeBoundingSphere(); - const previous = this.line.geometry; - this.line.geometry = geometry; this.line.position.set(origin[0], origin[1], origin[2]); - this.segmentEdges = edgeIds; previous.dispose(); - return { requestedDeflection: deflection, achievedDeflection: result.achieved_deflection() + encodingError, segmentCount: edgeIds.length, revision: result.revision() }; - } finally { result.free(); } - } - - dispose(): void { - if (this.disposed) return; - this.disposed = true; - this.line.geometry.dispose(); this.line.material.dispose(); this.kernel.free(); - } - private checkActive(): void { if (this.disposed) throw new Error("AnalyticCurve has been disposed"); } -} - -function createKernel( - options: AnalyticCurveOptions, - id: string, - frame: AnalyticFrame, - accuracy: AnalyticAccuracy, -): OGAnalyticBrep { - return OGAnalyticBrep.from_primitive(JSON.stringify({ - kind: options.kind === "arc" ? "arc" : "elliptical_arc", id, frame, accuracy, - start_angle: options.startAngle, sweep_angle: options.sweepAngle, - ...(options.kind === "arc" - ? { radius: options.radius } - : { major_radius: options.majorRadius, minor_radius: options.minorRadius }), - })); -} diff --git a/main/opengeometry-three/src/primitives/arc.ts b/main/opengeometry-three/src/primitives/arc.ts deleted file mode 100644 index 5722499..0000000 --- a/main/opengeometry-three/src/primitives/arc.ts +++ /dev/null @@ -1,105 +0,0 @@ -import * as THREE from "three"; -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; - -import { AnalyticCurve, type AnalyticCurveStats } from "./analytic-curve"; -import type { AnalyticAccuracy, AnalyticFrame } from "../shapes/analytic-solid"; - -export interface IArcOptions { - ogid?: string; - center?: Vector3; - radius?: number; - startAngle?: number; - sweepAngle?: number; - deflection?: number; - accuracy?: AnalyticAccuracy; - color?: THREE.ColorRepresentation; -} - -export type ArcConfigUpdate = Partial>; - -interface NormalizedArcOptions { - ogid?: string; - center: Vector3; - radius: number; - startAngle: number; - sweepAngle: number; - deflection: number; - accuracy?: AnalyticAccuracy; - color: THREE.ColorRepresentation; -} - -/** Authoritative circular edge with transient deflection tessellation. */ -export class Arc extends AnalyticCurve { - options: Readonly; - - constructor(options: IArcOptions = {}) { - const normalized = normalizeOptions(options); - super({ - kind: "arc", - ogid: normalized.ogid, - frame: curveFrame(normalized.center), - accuracy: normalized.accuracy, - radius: normalized.radius, - startAngle: normalized.startAngle, - sweepAngle: normalized.sweepAngle, - deflection: normalized.deflection, - color: normalized.color, - }); - this.options = normalized; - } - - getAnchor(): Vector3 { - return this.options.center.clone(); - } - - getConfig(): Readonly { - return this.options; - } - - setConfig(update: ArcConfigUpdate): AnalyticCurveStats { - const next = normalizeOptions({ ...this.options, ...update, ogid: this.ogid }); - const statistics = this.replaceDefinition({ - kind: "arc", - ogid: this.ogid, - frame: curveFrame(next.center), - accuracy: next.accuracy, - radius: next.radius, - startAngle: next.startAngle, - sweepAngle: next.sweepAngle, - deflection: next.deflection, - color: next.color, - }); - this.options = next; - return statistics; - } - - getBrep(): Record { - return this.getBrepData(); - } - - retessellate(deflection: number): AnalyticCurveStats { - return super.retessellate(deflection); - } -} - -function normalizeOptions(options: IArcOptions): NormalizedArcOptions { - return { - ogid: options.ogid, - center: options.center ?? new Vector3(0, 0, 0), - radius: options.radius ?? 1, - startAngle: options.startAngle ?? 0, - sweepAngle: options.sweepAngle ?? Math.PI * 2, - deflection: options.deflection ?? 0.01, - accuracy: options.accuracy, - color: options.color ?? 0x00ff00, - }; -} - -function curveFrame(center: Vector3): AnalyticFrame { - return { - origin: [center.x, center.y, center.z], - x: [1, 0, 0], - y: [0, 0, -1], - z: [0, 1, 0], - }; -} diff --git a/main/opengeometry-three/src/primitives/curve.ts b/main/opengeometry-three/src/primitives/curve.ts deleted file mode 100644 index 60537b3..0000000 --- a/main/opengeometry-three/src/primitives/curve.ts +++ /dev/null @@ -1,275 +0,0 @@ -import * as THREE from "three"; -import { OGCurve, Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import { getUUID } from "../utils/randomizer"; -import { - clonePlacement, - createParametricEditCapabilities, -} from "../editor"; -import { createFreeformGeometry } from "../freeform"; - -/** - * Construction options for a control-point-driven curve. - */ -export interface ICurveOptions { - ogid?: string; - controlPoints: Vector3[]; - color: number; - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Placement updates accepted by `Curve`. - */ -export interface CurvePlacementOptions { - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Partial config payload accepted by `Curve.setConfig(...)`. - */ -export type CurveConfigUpdate = Partial< - Omit ->; - -/** - * Alias for `Curve` placement updates. - */ -export type CurvePlacementUpdate = CurvePlacementOptions; - -/** - * Offset result returned by `Curve.offset(...)`. - */ -export interface ICurveOffsetResult { - points: Vector3[]; - beveledVertexIndices: number[]; - isClosed: boolean; -} - -type OffsetKernelOutput = { - points: Array<{ x: number; y: number; z: number }>; - beveled_vertex_indices: number[]; - is_closed: boolean; -}; - - -type OffsetKernelFn = ( - distance: number, - acuteThresholdDegrees: number, - bevel: boolean -) => string; - - -/** - * Curve wrapper backed by the kernel OGCurve primitive. - */ -export class Curve extends THREE.Line { - ogid: string; - options: ICurveOptions = { - controlPoints: [], - color: 0x00aa55, - translation: new Vector3(0, 0, 0), - rotation: new Vector3(0, 0, 0), - scale: new Vector3(1, 1, 1), - }; - - private curve: OGCurve; - - set color(color: number) { - this.options.color = color; - if (this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(color); - } - } - - constructor(options?: ICurveOptions) { - super(); - - this.ogid = options?.ogid ?? getUUID(); - this.curve = new OGCurve(this.ogid); - - this.options = { ...this.options, ...options }; - this.options.ogid = this.ogid; - - this.setConfig({ - controlPoints: this.options.controlPoints.map((point) => point.clone()), - color: this.options.color, - }); - this.setPlacement({ - translation: this.options.translation?.clone(), - rotation: this.options.rotation?.clone(), - scale: this.options.scale?.clone(), - }); - } - - setConfig(options: CurveConfigUpdate) { - const nextOptions = { ...this.options, ...options }; - const geometryChanged = "controlPoints" in options; - const colorChanged = "color" in options; - - this.options = nextOptions; - - if (geometryChanged) { - this.curve.set_config( - nextOptions.controlPoints.map((point) => point.clone()) - ); - this.generateGeometry(); - return; - } - - if (colorChanged && this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(this.options.color); - } - } - - getConfig() { - return this.options; - } - - getAnchor() { - const anchor = this.curve.get_anchor(); - return new Vector3(anchor.x, anchor.y, anchor.z); - } - - setAnchor(anchor: Vector3) { - this.curve.set_anchor(anchor.clone()); - this.generateGeometry(); - } - - resetAnchor() { - this.curve.reset_anchor(); - this.generateGeometry(); - } - - setPlacement(placement: CurvePlacementUpdate) { - this.options.translation = placement.translation?.clone() ?? this.options.translation; - this.options.rotation = placement.rotation?.clone() ?? this.options.rotation; - this.options.scale = placement.scale?.clone() ?? this.options.scale; - - this.curve.set_transform( - this.options.translation?.clone() ?? new Vector3(0, 0, 0), - this.options.rotation?.clone() ?? new Vector3(0, 0, 0), - this.options.scale?.clone() ?? new Vector3(1, 1, 1) - ); - this.generateGeometry(); - } - - setTransform(translation: Vector3, rotation: Vector3, scale: Vector3) { - this.setPlacement({ - translation, - rotation, - scale, - }); - } - - setTranslation(translation: Vector3) { - this.setPlacement({ translation }); - } - - setRotation(rotation: Vector3) { - this.setPlacement({ rotation }); - } - - setScale(scale: Vector3) { - this.setPlacement({ scale }); - } - - getPlacement() { - return clonePlacement({ - translation: this.options.translation, - rotation: this.options.rotation, - scale: this.options.scale, - }); - } - - getEditCapabilities() { - return createParametricEditCapabilities("curve", "curve"); - } - - canConvertToFreeform() { - return true; - } - - toFreeform(id: string = this.ogid) { - if (!this.canConvertToFreeform()) { - throw new Error("This entity cannot be converted to freeform."); - } - return createFreeformGeometry(this.curve.get_local_brep_serialized(), { - id, - placement: this.getPlacement(), - }); - } - - private generateGeometry() { - const bufferData = Array.from(this.curve.get_geometry_buffer()); - - this.writePositionsToGeometry(this.geometry, bufferData); - this.geometry.computeBoundingBox(); - this.geometry.computeBoundingSphere(); - - if (this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(this.options.color); - } else { - if (Array.isArray(this.material)) { - this.material.forEach((material) => material.dispose()); - } else { - this.material.dispose(); - } - - this.material = new THREE.LineBasicMaterial({ color: this.options.color }); - } - } - - getOffset( - distance: number, - acuteThresholdDegrees: number = 35.0, - bevel: boolean = true - ): ICurveOffsetResult { - const kernel = this.curve as unknown as { - get_offset_serialized?: OffsetKernelFn; - }; - if (typeof kernel.get_offset_serialized !== "function") { - throw new Error( - "Offset API is not available in OGCurve. Rebuild opengeometry wasm bindings." - ); - } - - const serialized = kernel.get_offset_serialized( - distance, - acuteThresholdDegrees, - bevel - ); - const parsed = JSON.parse(serialized) as OffsetKernelOutput; - - return { - points: parsed.points.map((point) => new Vector3(point.x, point.y, point.z)), - beveledVertexIndices: parsed.beveled_vertex_indices ?? [], - isClosed: Boolean(parsed.is_closed), - }; - } - - private writePositionsToGeometry( - geometry: THREE.BufferGeometry, - positions: number[] - ) { - const existing = geometry.getAttribute("position"); - if ( - !(existing instanceof THREE.BufferAttribute) || - existing.itemSize !== 3 || - existing.count !== positions.length / 3 - ) { - geometry.setAttribute( - "position", - new THREE.Float32BufferAttribute(positions, 3) - ); - return; - } - - const array = existing.array as Float32Array; - array.set(positions); - existing.needsUpdate = true; - } -} diff --git a/main/opengeometry-three/src/primitives/elliptical_arc.ts b/main/opengeometry-three/src/primitives/elliptical_arc.ts deleted file mode 100644 index 2c425f8..0000000 --- a/main/opengeometry-three/src/primitives/elliptical_arc.ts +++ /dev/null @@ -1,110 +0,0 @@ -import * as THREE from "three"; -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; - -import { AnalyticCurve, type AnalyticCurveStats } from "./analytic-curve"; -import type { AnalyticAccuracy, AnalyticFrame } from "../shapes/analytic-solid"; - -export interface IEllipticalArcOptions { - ogid?: string; - center?: Vector3; - majorRadius?: number; - minorRadius?: number; - startAngle?: number; - sweepAngle?: number; - deflection?: number; - accuracy?: AnalyticAccuracy; - color?: THREE.ColorRepresentation; -} - -export type EllipticalArcConfigUpdate = Partial>; - -interface NormalizedEllipticalArcOptions { - ogid?: string; - center: Vector3; - majorRadius: number; - minorRadius: number; - startAngle: number; - sweepAngle: number; - deflection: number; - accuracy?: AnalyticAccuracy; - color: THREE.ColorRepresentation; -} - -/** Authoritative elliptical edge with transient deflection tessellation. */ -export class EllipticalArc extends AnalyticCurve { - options: Readonly; - - constructor(options: IEllipticalArcOptions = {}) { - const normalized = normalizeOptions(options); - super({ - kind: "ellipticalArc", - ogid: normalized.ogid, - frame: curveFrame(normalized.center), - accuracy: normalized.accuracy, - majorRadius: normalized.majorRadius, - minorRadius: normalized.minorRadius, - startAngle: normalized.startAngle, - sweepAngle: normalized.sweepAngle, - deflection: normalized.deflection, - color: normalized.color, - }); - this.options = normalized; - } - - getAnchor(): Vector3 { - return this.options.center.clone(); - } - - getConfig(): Readonly { - return this.options; - } - - setConfig(update: EllipticalArcConfigUpdate): AnalyticCurveStats { - const next = normalizeOptions({ ...this.options, ...update, ogid: this.ogid }); - const statistics = this.replaceDefinition({ - kind: "ellipticalArc", - ogid: this.ogid, - frame: curveFrame(next.center), - accuracy: next.accuracy, - majorRadius: next.majorRadius, - minorRadius: next.minorRadius, - startAngle: next.startAngle, - sweepAngle: next.sweepAngle, - deflection: next.deflection, - color: next.color, - }); - this.options = next; - return statistics; - } - - getBrep(): Record { - return this.getBrepData(); - } - - retessellate(deflection: number): AnalyticCurveStats { - return super.retessellate(deflection); - } -} - -function normalizeOptions(options: IEllipticalArcOptions): NormalizedEllipticalArcOptions { - return { - ogid: options.ogid, - center: options.center ?? new Vector3(0, 0, 0), - majorRadius: options.majorRadius ?? 1, - minorRadius: options.minorRadius ?? 0.5, - startAngle: options.startAngle ?? 0, - sweepAngle: options.sweepAngle ?? Math.PI * 2, - deflection: options.deflection ?? 0.01, - accuracy: options.accuracy, - color: options.color ?? 0x00ff00, - }; -} - -function curveFrame(center: Vector3): AnalyticFrame { - return { - origin: [center.x, center.y, center.z], - x: [1, 0, 0], - y: [0, 0, -1], - z: [0, 1, 0], - }; -} diff --git a/main/opengeometry-three/src/primitives/index.ts b/main/opengeometry-three/src/primitives/index.ts deleted file mode 100644 index 38ec9eb..0000000 --- a/main/opengeometry-three/src/primitives/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * 2D and path primitive wrappers backed by the OpenGeometry kernel. - */ -export * from './line'; -export * from './polyline'; -export * from './arc'; -export * from './rectangle'; -export * from './curve'; -export * from "./elliptical_arc"; \ No newline at end of file diff --git a/main/opengeometry-three/src/primitives/line.ts b/main/opengeometry-three/src/primitives/line.ts deleted file mode 100644 index 8940520..0000000 --- a/main/opengeometry-three/src/primitives/line.ts +++ /dev/null @@ -1,370 +0,0 @@ -import { OGLine, Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import * as THREE from "three"; -import { getUUID } from "../utils/randomizer"; -import { Line2 } from 'three/examples/jsm/lines/Line2.js'; -import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'; -import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'; -import { - clonePlacement, - createParametricEditCapabilities, -} from "../editor"; -import { createFreeformGeometry } from "../freeform"; - -/** - * Construction options for a line segment wrapper. - */ -export interface ILineOptions { - ogid?: string; - start: Vector3; - end: Vector3; - color: number; - fatLines?: boolean; - width?: number; - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Placement updates accepted by `Line`. - */ -export interface LinePlacementOptions { - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Partial config payload accepted by `Line.setConfig(...)`. - */ -export type LineConfigUpdate = Partial< - Omit ->; - -/** - * Alias for `Line` placement updates. - */ -export type LinePlacementUpdate = LinePlacementOptions; - -/** - * Offset result returned by `Line.offset(...)`. - */ -export interface ILineOffsetResult { - points: Vector3[]; - beveledVertexIndices: number[]; - isClosed: boolean; -} - -type OffsetKernelOutput = { - points: Array<{ x: number; y: number; z: number }>; - beveled_vertex_indices: number[]; - is_closed: boolean; -}; - - -type OffsetKernelFn = ( - distance: number, - acuteThresholdDegrees: number, - bevel: boolean -) => string; - - -/** - * Simple Line defined by Two Points - */ -export class Line extends THREE.Line { - ogid: string; - options: ILineOptions = { - start: new Vector3(0, 0, 0.5), - end: new Vector3(1, 0, 0.5), - color: 0x000000, - fatLines: false, - width: 20, - translation: new Vector3(0, 0, 0), - rotation: new Vector3(0, 0, 0), - scale: new Vector3(1, 1, 1), - }; - - private line: OGLine; - private fatLine: Line2 | null = null; - - set color(color: number) { - this.options.color = color; - if (this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(color); - } - if (this.fatLine && this.fatLine.material instanceof LineMaterial) { - this.fatLine.material.color.set(color); - } - } - - constructor(options?: ILineOptions) { - super(); - - this.ogid = options?.ogid ?? getUUID(); - this.line = new OGLine(this.ogid); - - this.options = { ...this.options, ...options }; - this.options.ogid = this.ogid; - - this.setConfig({ - start: this.options.start.clone(), - end: this.options.end.clone(), - color: this.options.color, - fatLines: this.options.fatLines, - width: this.options.width, - }); - this.setPlacement({ - translation: this.options.translation?.clone(), - rotation: this.options.rotation?.clone(), - scale: this.options.scale?.clone(), - }); - } - - validateOptions() { - if (!this.options) { - throw new Error("Options are not defined for Line"); - } - } - - setConfig(options: LineConfigUpdate) { - this.validateOptions(); - - const nextOptions = { ...this.options, ...options }; - const geometryChanged = "start" in options || "end" in options; - const renderChanged = - "color" in options || - "fatLines" in options || - "width" in options; - - this.options = nextOptions; - - if (geometryChanged) { - this.line.set_config( - nextOptions.start.clone(), - nextOptions.end.clone() - ); - this.generateGeometry(); - return; - } - - if (renderChanged) { - this.updateRenderStyle(); - } - } - - getConfig() { - return this.options; - } - - getAnchor() { - const anchor = this.line.get_anchor(); - return new Vector3(anchor.x, anchor.y, anchor.z); - } - - setAnchor(anchor: Vector3) { - this.line.set_anchor(anchor.clone()); - this.generateGeometry(); - } - - resetAnchor() { - this.line.reset_anchor(); - this.generateGeometry(); - } - - setPlacement(placement: LinePlacementUpdate) { - this.options.translation = placement.translation?.clone() ?? this.options.translation; - this.options.rotation = placement.rotation?.clone() ?? this.options.rotation; - this.options.scale = placement.scale?.clone() ?? this.options.scale; - - this.line.set_transform( - this.options.translation?.clone() ?? new Vector3(0, 0, 0), - this.options.rotation?.clone() ?? new Vector3(0, 0, 0), - this.options.scale?.clone() ?? new Vector3(1, 1, 1) - ); - this.generateGeometry(); - } - - setTransform(translation: Vector3, rotation: Vector3, scale: Vector3) { - this.setPlacement({ - translation, - rotation, - scale, - }); - } - - setTranslation(translation: Vector3) { - this.setPlacement({ translation }); - } - - setRotation(rotation: Vector3) { - this.setPlacement({ rotation }); - } - - setScale(scale: Vector3) { - this.setPlacement({ scale }); - } - - getPlacement() { - return clonePlacement({ - translation: this.options.translation, - rotation: this.options.rotation, - scale: this.options.scale, - }); - } - - getEditCapabilities() { - return createParametricEditCapabilities("line", "profile"); - } - - canConvertToFreeform() { - return true; - } - - toFreeform(id: string = this.ogid) { - if (!this.canConvertToFreeform()) { - throw new Error("This entity cannot be converted to freeform."); - } - return createFreeformGeometry(this.line.get_local_brep_serialized(), { - id, - placement: this.getPlacement(), - }); - } - - /** - * Every time there are property changes, geometry needs to be discarded and regenerated. - * This is to ensure that the geometry is always up-to-date with the current state. - */ - discardGeometry() { - this.geometry.dispose(); - } - - private getCurrentPositions() { - const attribute = this.geometry.getAttribute("position"); - if (!attribute || attribute.itemSize !== 3) { - return []; - } - - const positions = []; - for (let index = 0; index < attribute.count; index += 1) { - positions.push( - attribute.getX(index), - attribute.getY(index), - attribute.getZ(index) - ); - } - - return positions; - } - - private updateRenderStyle(bufferData?: number[]) { - const positions = bufferData ?? this.getCurrentPositions(); - - if (this.options.fatLines) { - if (!this.fatLine) { - this.fatLine = new Line2( - new LineGeometry(), - new LineMaterial({ - color: this.options.color, - linewidth: this.options.width, - resolution: new THREE.Vector2(window.innerWidth, window.innerHeight), - }) - ); - this.add(this.fatLine); - } - - this.fatLine.geometry.setPositions(positions); - (this.fatLine.material as LineMaterial).color.set(this.options.color); - (this.fatLine.material as LineMaterial).linewidth = this.options.width ?? 5; - (this.fatLine.material as LineMaterial).resolution.set( - window.innerWidth, - window.innerHeight - ); - this.fatLine.visible = true; - } else if (this.fatLine) { - this.fatLine.visible = false; - } - - if (this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(this.options.color); - this.material.visible = !this.options.fatLines; - } - } - - private generateGeometry() { - const bufferData = Array.from(this.line.get_geometry_buffer()); - - this.writePositionsToGeometry(this.geometry, bufferData); - this.geometry.computeBoundingBox(); - this.geometry.computeBoundingSphere(); - - if (this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(this.options.color); - } else { - if (Array.isArray(this.material)) { - this.material.forEach((material) => material.dispose()); - } else { - this.material.dispose(); - } - - this.material = new THREE.LineBasicMaterial({ color: this.options.color }); - } - - this.updateRenderStyle(bufferData); - } - - getDXF() { - const dxfData = this.line.get_dxf_serialized(); - return dxfData; - } - - getOffset( - distance: number, - acuteThresholdDegrees: number = 35.0, - bevel: boolean = true - ): ILineOffsetResult { - const kernel = this.line as unknown as { - get_offset_serialized?: OffsetKernelFn; - }; - if (typeof kernel.get_offset_serialized !== "function") { - throw new Error( - "Offset API is not available in OGLine. Rebuild opengeometry wasm bindings." - ); - } - - const serialized = kernel.get_offset_serialized( - distance, - acuteThresholdDegrees, - bevel - ); - - const parsed = JSON.parse(serialized) as OffsetKernelOutput; - return { - points: parsed.points.map((point) => new Vector3(point.x, point.y, point.z)), - beveledVertexIndices: parsed.beveled_vertex_indices ?? [], - isClosed: Boolean(parsed.is_closed), - }; - } - - private writePositionsToGeometry( - geometry: THREE.BufferGeometry, - positions: number[] - ) { - const existing = geometry.getAttribute("position"); - if ( - !(existing instanceof THREE.BufferAttribute) || - existing.itemSize !== 3 || - existing.count !== positions.length / 3 - ) { - geometry.setAttribute( - "position", - new THREE.Float32BufferAttribute(positions, 3) - ); - return; - } - - const array = existing.array as Float32Array; - array.set(positions); - existing.needsUpdate = true; - } -} diff --git a/main/opengeometry-three/src/primitives/polyline.ts b/main/opengeometry-three/src/primitives/polyline.ts deleted file mode 100644 index fad1b41..0000000 --- a/main/opengeometry-three/src/primitives/polyline.ts +++ /dev/null @@ -1,377 +0,0 @@ -import * as THREE from 'three'; -import { OGPolyline, Vector3 } from '../../../opengeometry/pkg/opengeometry'; -import { getUUID } from "../utils/randomizer"; -import { Line2 } from 'three/examples/jsm/lines/Line2.js'; -import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'; -import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'; -import { - clonePlacement, - createParametricEditCapabilities, -} from "../editor"; -import { createFreeformGeometry } from "../freeform"; - -/** - * Construction options for an ordered polyline wrapper. - */ -export interface IPolylineOptions { - ogid?: string; - color: number; - points: Vector3[]; - fatLines?: boolean; - width?: number; - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Placement updates accepted by `Polyline`. - */ -export interface PolylinePlacementOptions { - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Partial config payload accepted by `Polyline.setConfig(...)`. - */ -export type PolylineConfigUpdate = Partial< - Omit ->; - -/** - * Alias for `Polyline` placement updates. - */ -export type PolylinePlacementUpdate = PolylinePlacementOptions; - -/** - * Offset result returned by `Polyline.offset(...)`. - */ -export interface IOffsetResult { - points: Vector3[]; - beveledVertexIndices: number[]; - isClosed: boolean; -} - -type OffsetKernelOutput = { - points: Array<{ x: number; y: number; z: number }>; - beveled_vertex_indices: number[]; - is_closed: boolean; -}; - - -type OffsetKernelFn = ( - distance: number, - acuteThresholdDegrees: number, - bevel: boolean -) => string; - - -/** - * Polyline wrapper backed by the kernel OGPolyline primitive. - */ -export class Polyline extends THREE.Line { - ogid: string; - options: IPolylineOptions = { - points: [], - color: 0x00ff00, - fatLines: false, - width: 20, - translation: new Vector3(0, 0, 0), - rotation: new Vector3(0, 0, 0), - scale: new Vector3(1, 1, 1), - }; - - isClosed: boolean = false; - - private polyline: OGPolyline; - private fatLine: Line2 | null = null; - - transformationMatrix: THREE.Matrix4 = new THREE.Matrix4(); - - set color(color: number) { - this.options.color = color; - if (this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(color); - } - if (this.fatLine && this.fatLine.material instanceof LineMaterial) { - this.fatLine.material.color.set(color); - } - } - - constructor(options?: IPolylineOptions) { - super(); - - this.ogid = options?.ogid ?? getUUID(); - this.polyline = new OGPolyline(this.ogid); - - this.options = { ...this.options, ...options }; - this.options.ogid = this.ogid; - - this.setConfig({ - points: this.options.points.map((point) => point.clone()), - color: this.options.color, - fatLines: this.options.fatLines, - width: this.options.width, - }); - this.setPlacement({ - translation: this.options.translation?.clone(), - rotation: this.options.rotation?.clone(), - scale: this.options.scale?.clone(), - }); - } - - validateOptions() { - if (!this.options) { - throw new Error("Options are not defined for Polyline"); - } - } - - setConfig(options: PolylineConfigUpdate) { - this.validateOptions(); - - const nextOptions = { ...this.options, ...options }; - const geometryChanged = "points" in options; - const renderChanged = - "color" in options || - "fatLines" in options || - "width" in options; - - this.options = nextOptions; - - if (geometryChanged) { - this.polyline.set_config(nextOptions.points.map((point) => point.clone())); - this.generateGeometry(); - return; - } - - if (renderChanged) { - this.updateRenderStyle(); - } - } - - getConfig() { - return this.options; - } - - getAnchor() { - const anchor = this.polyline.get_anchor(); - return new Vector3(anchor.x, anchor.y, anchor.z); - } - - setAnchor(anchor: Vector3) { - this.polyline.set_anchor(anchor.clone()); - this.generateGeometry(); - } - - resetAnchor() { - this.polyline.reset_anchor(); - this.generateGeometry(); - } - - setPlacement(placement: PolylinePlacementUpdate) { - this.options.translation = placement.translation?.clone() ?? this.options.translation; - this.options.rotation = placement.rotation?.clone() ?? this.options.rotation; - this.options.scale = placement.scale?.clone() ?? this.options.scale; - - this.polyline.set_transform( - this.options.translation?.clone() ?? new Vector3(0, 0, 0), - this.options.rotation?.clone() ?? new Vector3(0, 0, 0), - this.options.scale?.clone() ?? new Vector3(1, 1, 1) - ); - this.generateGeometry(); - } - - setTransform(translation: Vector3, rotation: Vector3, scale: Vector3) { - this.setPlacement({ - translation, - rotation, - scale, - }); - } - - setTranslation(translation: Vector3) { - this.setPlacement({ translation }); - } - - setRotation(rotation: Vector3) { - this.setPlacement({ rotation }); - } - - setScale(scale: Vector3) { - this.setPlacement({ scale }); - } - - getPlacement() { - return clonePlacement({ - translation: this.options.translation, - rotation: this.options.rotation, - scale: this.options.scale, - }); - } - - getEditCapabilities() { - return createParametricEditCapabilities("polyline", "profile"); - } - - canConvertToFreeform() { - return true; - } - - toFreeform(id: string = this.ogid) { - if (!this.canConvertToFreeform()) { - throw new Error("This entity cannot be converted to freeform."); - } - return createFreeformGeometry(this.polyline.get_local_brep_serialized(), { - id, - placement: this.getPlacement(), - }); - } - - addPoint(point: Vector3) { - if (!this.polyline) return; - - const { points } = this.options; - points.push(point); - - if (this.options.points.length < 2) return; - this.setConfig({ - points: this.options.points.map((currentPoint) => currentPoint.clone()), - }); - } - - /** - * Every time there are property changes, geometry needs to be discarded and regenerated. - * This is to ensure that the geometry is always up-to-date with the current state. - */ - discardGeometry() { - this.geometry.dispose(); - } - - private getCurrentPositions() { - const attribute = this.geometry.getAttribute("position"); - if (!attribute || attribute.itemSize !== 3) { - return []; - } - - const positions = []; - for (let index = 0; index < attribute.count; index += 1) { - positions.push( - attribute.getX(index), - attribute.getY(index), - attribute.getZ(index) - ); - } - - return positions; - } - - private updateRenderStyle(bufferData?: number[]) { - const positions = bufferData ?? this.getCurrentPositions(); - - if (this.options.fatLines) { - if (!this.fatLine) { - this.fatLine = new Line2( - new LineGeometry(), - new LineMaterial({ - color: this.options.color, - linewidth: this.options.width, - resolution: new THREE.Vector2(window.innerWidth, window.innerHeight), - }) - ); - this.add(this.fatLine); - } - - this.fatLine.geometry.setPositions(positions); - (this.fatLine.material as LineMaterial).color.set(this.options.color); - (this.fatLine.material as LineMaterial).linewidth = this.options.width ?? 5; - (this.fatLine.material as LineMaterial).resolution.set( - window.innerWidth, - window.innerHeight - ); - this.fatLine.visible = true; - } else if (this.fatLine) { - this.fatLine.visible = false; - } - - if (this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(this.options.color); - this.material.visible = !this.options.fatLines; - } - } - - private generateGeometry() { - const bufferData = Array.from(this.polyline.get_geometry_buffer()); - - this.writePositionsToGeometry(this.geometry, bufferData); - this.geometry.computeBoundingBox(); - this.geometry.computeBoundingSphere(); - - if (this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(this.options.color); - } else { - if (Array.isArray(this.material)) { - this.material.forEach((material) => material.dispose()); - } else { - this.material.dispose(); - } - - this.material = new THREE.LineBasicMaterial({ color: this.options.color }); - } - - this.updateRenderStyle(bufferData); - - this.isClosed = this.polyline.is_closed(); - } - - getOffset( - distance: number, - acuteThresholdDegrees: number = 35.0, - bevel: boolean = true - ): IOffsetResult { - const kernel = this.polyline as unknown as { - get_offset_serialized?: OffsetKernelFn; - }; - if (typeof kernel.get_offset_serialized !== "function") { - throw new Error( - "Offset API is not available in OGPolyline. Rebuild opengeometry wasm bindings." - ); - } - - const serialized = kernel.get_offset_serialized( - distance, - acuteThresholdDegrees, - bevel - ); - - const parsed = JSON.parse(serialized) as OffsetKernelOutput; - return { - points: parsed.points.map((point) => new Vector3(point.x, point.y, point.z)), - beveledVertexIndices: parsed.beveled_vertex_indices ?? [], - isClosed: Boolean(parsed.is_closed), - }; - } - - private writePositionsToGeometry( - geometry: THREE.BufferGeometry, - positions: number[] - ) { - const existing = geometry.getAttribute("position"); - if ( - !(existing instanceof THREE.BufferAttribute) || - existing.itemSize !== 3 || - existing.count !== positions.length / 3 - ) { - geometry.setAttribute( - "position", - new THREE.Float32BufferAttribute(positions, 3) - ); - return; - } - - const array = existing.array as Float32Array; - array.set(positions); - existing.needsUpdate = true; - } -} diff --git a/main/opengeometry-three/src/primitives/rectangle.ts b/main/opengeometry-three/src/primitives/rectangle.ts deleted file mode 100644 index 2baac03..0000000 --- a/main/opengeometry-three/src/primitives/rectangle.ts +++ /dev/null @@ -1,393 +0,0 @@ -import { OGRectangle, Vector3 } from "./../../../opengeometry/pkg/opengeometry"; -import * as THREE from "three"; -import { getUUID } from "../utils/randomizer"; -import { Line2 } from 'three/examples/jsm/lines/Line2.js'; -import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'; -import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'; -import { - clonePlacement, - createParametricEditCapabilities, -} from "../editor"; -import { createFreeformGeometry } from "../freeform"; - -/** - * Construction options for a rectangle profile wrapper. - */ -export interface IRectangleOptions { - ogid?: string; - center: Vector3; - width: number; - breadth: number; - color: number; - fatLines?: boolean; - lineWidth?: number; - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Placement updates accepted by `Rectangle`. - */ -export interface RectanglePlacementOptions { - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Partial config payload accepted by `Rectangle.setConfig(...)`. - */ -export type RectangleConfigUpdate = Partial< - Omit ->; - -/** - * Alias for `Rectangle` placement updates. - */ -export type RectanglePlacementUpdate = RectanglePlacementOptions; - -/** - * Offset result returned by `Rectangle.offset(...)`. - */ -export interface IRectangleOffsetResult { - points: Vector3[]; - beveledVertexIndices: number[]; - isClosed: boolean; -} - -type OffsetKernelOutput = { - points: Array<{ x: number; y: number; z: number }>; - beveled_vertex_indices: number[]; - is_closed: boolean; -}; - - -type OffsetKernelFn = ( - distance: number, - acuteThresholdDegrees: number, - bevel: boolean -) => string; - - -/** - * Rectangle wrapper backed by the kernel OGRectangle primitive. - */ -export class Rectangle extends THREE.Line { - ogid: string; - options: IRectangleOptions = { - center: new Vector3(0, 0, 0), - width: 1, - breadth: 1, - color: 0x00ff00, - translation: new Vector3(0, 0, 0), - rotation: new Vector3(0, 0, 0), - scale: new Vector3(1, 1, 1), - }; - - private polyLineRectangle: OGRectangle; - private fatLine: Line2 | null = null; - - // set width(width: number) { - // this.options.width = width; - // this.polyLineRectangle.update_width(width); - // this.generateGeometry(); - // } - - // set breadth(breadth: number) { - // this.options.breadth = breadth; - // this.polyLineRectangle.update_breadth(breadth); - // this.generateGeometry(); - // } - - // set center(center: Vector3) { - // this.options.center = center; - // this.polyLineRectangle.update_center(center); - // this.generateGeometry(); - // } - - set color(color: number) { - this.options.color = color; - if (this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(color); - } - if (this.fatLine && this.fatLine.material instanceof LineMaterial) { - this.fatLine.material.color.set(color); - } - } - - // set lineWidth(lineWidth: number) { - // this.options.lineWidth = lineWidth; - // if (this.material instanceof THREE.LineBasicMaterial) { - // (this.material as THREE.LineBasicMaterial).linewidth = lineWidth; - // } - // } - - // FINAL: This flow should be used for other primitives - constructor(options?: IRectangleOptions) { - super(); - this.ogid = options?.ogid ?? getUUID(); - this.polyLineRectangle = new OGRectangle(this.ogid); - - this.options = { ...this.options, ...options }; - this.options.ogid = this.ogid; - - this.setConfig({ - center: this.options.center.clone(), - width: this.options.width, - breadth: this.options.breadth, - color: this.options.color, - fatLines: this.options.fatLines, - lineWidth: this.options.lineWidth, - }); - - this.setPlacement({ - translation: this.options.translation?.clone(), - rotation: this.options.rotation?.clone(), - scale: this.options.scale?.clone(), - }); - } - - validateOptions() { - if (!this.options) { - throw new Error("Options are not defined for Rectangle"); - } - } - - setConfig(options: RectangleConfigUpdate) { - this.validateOptions(); - - const nextOptions = { ...this.options, ...options }; - const geometryChanged = - "center" in options || - "width" in options || - "breadth" in options; - const renderChanged = - "color" in options || - "fatLines" in options || - "lineWidth" in options; - - this.options = nextOptions; - - if (geometryChanged) { - this.polyLineRectangle.set_config( - nextOptions.center.clone(), - nextOptions.width, - nextOptions.breadth, - ); - this.generateGeometry(); - return; - } - - if (renderChanged) { - this.updateRenderStyle(); - } - } - - getAnchor() { - const anchor = this.polyLineRectangle.get_anchor(); - return new Vector3(anchor.x, anchor.y, anchor.z); - } - - setPlacement(placement: RectanglePlacementUpdate) { - this.options.translation = placement.translation?.clone() ?? this.options.translation; - this.options.rotation = placement.rotation?.clone() ?? this.options.rotation; - this.options.scale = placement.scale?.clone() ?? this.options.scale; - - this.polyLineRectangle.set_transform( - this.options.translation?.clone() ?? new Vector3(0, 0, 0), - this.options.rotation?.clone() ?? new Vector3(0, 0, 0), - this.options.scale?.clone() ?? new Vector3(1, 1, 1) - ); - this.generateGeometry(); - } - - setTransform(translation: Vector3, rotation: Vector3, scale: Vector3) { - this.setPlacement({ - translation, - rotation, - scale, - }); - } - - setTranslation(translation: Vector3) { - this.setPlacement({ translation }); - } - - setRotation(rotation: Vector3) { - this.setPlacement({ rotation }); - } - - setScale(scale: Vector3) { - this.setPlacement({ scale }); - } - - getConfig() { - return this.options; - } - - getPlacement() { - return clonePlacement({ - translation: this.options.translation, - rotation: this.options.rotation, - scale: this.options.scale, - }); - } - - getEditCapabilities() { - return createParametricEditCapabilities("rectangle", "profile"); - } - - canConvertToFreeform() { - return true; - } - - toFreeform(id: string = this.ogid) { - if (!this.canConvertToFreeform()) { - throw new Error("This entity cannot be converted to freeform."); - } - return createFreeformGeometry( - this.polyLineRectangle.get_local_brep_serialized(), - { - id, - placement: this.getPlacement(), - } - ); - } - - private getCurrentPositions() { - const attribute = this.geometry.getAttribute("position"); - if (!attribute || attribute.itemSize !== 3) { - return []; - } - - const positions = []; - for (let index = 0; index < attribute.count; index += 1) { - positions.push( - attribute.getX(index), - attribute.getY(index), - attribute.getZ(index) - ); - } - - return positions; - } - - private updateRenderStyle(bufferData?: number[]) { - const positions = bufferData ?? this.getCurrentPositions(); - - if (this.options.fatLines) { - if (!this.fatLine) { - this.fatLine = new Line2( - new LineGeometry(), - new LineMaterial({ - color: this.options.color, - linewidth: this.options.lineWidth, - resolution: new THREE.Vector2(window.innerWidth, window.innerHeight), - }) - ); - this.add(this.fatLine); - } - - this.fatLine.geometry.setPositions(positions); - (this.fatLine.material as LineMaterial).color.set(this.options.color); - (this.fatLine.material as LineMaterial).linewidth = this.options.lineWidth ?? 1; - (this.fatLine.material as LineMaterial).resolution.set( - window.innerWidth, - window.innerHeight - ); - this.fatLine.visible = true; - } else if (this.fatLine) { - this.fatLine.visible = false; - } - - if (this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(this.options.color); - this.material.visible = !this.options.fatLines; - } - } - - private generateGeometry() { - const bufferData = Array.from(this.polyLineRectangle.get_geometry_buffer()); - - this.writePositionsToGeometry(this.geometry, bufferData); - this.geometry.computeBoundingBox(); - this.geometry.computeBoundingSphere(); - - if (this.material instanceof THREE.LineBasicMaterial) { - this.material.color.set(this.options.color); - } else { - if (Array.isArray(this.material)) { - this.material.forEach((material) => material.dispose()); - } else { - this.material.dispose(); - } - - this.material = new THREE.LineBasicMaterial({ color: this.options.color }); - } - - this.updateRenderStyle(bufferData); - } - - getBrep() { - const brepData = this.polyLineRectangle.get_brep_serialized(); - if (!brepData) { - throw new Error("Brep data is not available for Rectangle"); - } - return JSON.parse(brepData); - } - - getOffset( - distance: number, - acuteThresholdDegrees: number = 35.0, - bevel: boolean = true - ): IRectangleOffsetResult { - const kernel = this.polyLineRectangle as unknown as { - get_offset_serialized?: OffsetKernelFn; - }; - if (typeof kernel.get_offset_serialized !== "function") { - throw new Error( - "Offset API is not available in OGRectangle. Rebuild opengeometry wasm bindings." - ); - } - - const serialized = kernel.get_offset_serialized( - distance, - acuteThresholdDegrees, - bevel - ); - const parsed = JSON.parse(serialized) as OffsetKernelOutput; - - return { - points: parsed.points.map((point) => new Vector3(point.x, point.y, point.z)), - beveledVertexIndices: parsed.beveled_vertex_indices ?? [], - isClosed: Boolean(parsed.is_closed), - }; - } - - discardGeometry() { - this.geometry.dispose(); - } - - private writePositionsToGeometry( - geometry: THREE.BufferGeometry, - positions: number[] - ) { - const existing = geometry.getAttribute("position"); - if ( - !(existing instanceof THREE.BufferAttribute) || - existing.itemSize !== 3 || - existing.count !== positions.length / 3 - ) { - geometry.setAttribute( - "position", - new THREE.Float32BufferAttribute(positions, 3) - ); - return; - } - - const array = existing.array as Float32Array; - array.set(positions); - existing.needsUpdate = true; - } -} diff --git a/main/opengeometry-three/src/rendering/analytic-lod.ts b/main/opengeometry-three/src/rendering/analytic-lod.ts deleted file mode 100644 index 5de71b8..0000000 --- a/main/opengeometry-three/src/rendering/analytic-lod.ts +++ /dev/null @@ -1,190 +0,0 @@ -import * as THREE from "three"; -import type { AnalyticSolid, AnalyticTessellationStats } from "../shapes/analytic-solid"; -import { AnalyticTessellationWorker, GEOMETRY_DEFLECTION_SHARE } from "./analytic-tessellation"; -import { getUUID } from "../utils/randomizer"; - -export function deflectionBucket(target: number, previous?: number): number { - if (!Number.isFinite(target) || target <= 0) throw new Error("LOD target must be finite and positive"); - if (previous && target >= previous * 0.75 && target <= previous * 2.5) return previous; - const bucket = 2 ** Math.floor(Math.log2(target)); - if (!Number.isFinite(bucket) || bucket <= 0) throw new Error("LOD target is outside the representable range"); - return bucket; -} - -export function worldUnitsPerPixel(camera: THREE.PerspectiveCamera | THREE.OrthographicCamera, depth: number, physicalHeight: number): number { - if (!Number.isFinite(physicalHeight) || physicalHeight <= 0) throw new Error("Physical viewport height must be positive"); - if (camera instanceof THREE.OrthographicCamera) return (camera.top - camera.bottom) / (camera.zoom * physicalHeight); - if (!Number.isFinite(depth) || depth <= 0) throw new Error("Perspective depth must be positive"); - return 2 * depth * Math.tan(THREE.MathUtils.degToRad(camera.getEffectiveFOV()) / 2) / physicalHeight; -} - -interface Entry { - handle: string; - solid: AnalyticSolid; - bounds: THREE.Box3; - ready: boolean; - desired?: number; - accepted?: number; - generation: number; - failed?: number; -} - -export interface AnalyticLodOptions { - fixedDeflection?: number; - onUpdate?: (solid: AnalyticSolid, statistics: AnalyticTessellationStats) => void; - onError?: (solid: AnalyticSolid, error: Error) => void; -} - -/** Register authored changes explicitly; camera meshes never refresh a scene's authored BRep snapshots. */ -export class AnalyticLodController { - private readonly entries = new Map(); - private readonly cameraState = new Float64Array(34); - private lastMotion = -Infinity; - private lastSchedule = -Infinity; - private initialized = false; - private closed = false; - private refining = false; - private fixedDeflection?: number; - - constructor(private readonly worker: AnalyticTessellationWorker, private readonly options: AnalyticLodOptions = {}) { - this.setDeflection(options.fixedDeflection); - } - - setDeflection(deflection?: number): void { - if (deflection !== undefined && (!Number.isFinite(deflection) || deflection <= 0)) throw new Error("Deflection must be finite and positive"); - this.fixedDeflection = deflection; - for (const entry of this.entries.values()) { - entry.generation++; - entry.desired = entry.failed = undefined; - this.worker.cancel(entry.handle); - } - } - - async register(solid: AnalyticSolid): Promise { - if (this.closed) throw new Error("LOD controller has been disposed"); - await this.unregister(solid); - if (this.entries.size >= 64) throw new Error("LOD body limit exceeded"); - const entry: Entry = { - handle: `lod-${getUUID()}`, solid, bounds: solid.getModelBounds(), ready: false, generation: 0, - }; - this.entries.set(solid, entry); - try { - await this.worker.setModel(entry.handle, solid.getBrepSerialized()); - if (this.entries.get(solid) === entry) entry.ready = true; - else await this.worker.removeModel(entry.handle); - } catch (error) { - if (this.entries.get(solid) === entry) this.entries.delete(solid); - throw error; - } - } - - async unregister(solid: AnalyticSolid): Promise { - const entry = this.entries.get(solid); - if (!entry) return; - this.entries.delete(solid); - entry.generation++; - await this.worker.removeModel(entry.handle); - } - - update(camera: THREE.PerspectiveCamera | THREE.OrthographicCamera, physicalHeight: number, now = performance.now(), motion = false): void { - if (this.closed || this.refining) return; - camera.updateMatrixWorld(); - let changed = !this.initialized || physicalHeight !== this.cameraState[32]; - for (let i = 0; i < 16; i++) { - if (camera.matrixWorld.elements[i] !== this.cameraState[i] || camera.projectionMatrix.elements[i] !== this.cameraState[16 + i]) changed = true; - this.cameraState[i] = camera.matrixWorld.elements[i]; - this.cameraState[16 + i] = camera.projectionMatrix.elements[i]; - } - this.cameraState[32] = physicalHeight; - if (this.initialized && (changed || motion)) this.lastMotion = now; - this.initialized = true; - const moving = now - this.lastMotion < 150; - if (now - this.lastSchedule < 100) return; - this.lastSchedule = now; - const frustum = new THREE.Frustum().setFromProjectionMatrix(new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse)); - const candidates: { entry: Entry; deflection: number; distance: number }[] = []; - for (const entry of this.entries.values()) { - if (!entry.ready || entry.solid.isDisposed || !visible(entry.solid.surface) || !entry.solid.surface.layers.test(camera.layers) || entry.bounds.isEmpty()) continue; - entry.solid.updateWorldMatrix(true, false); - const bounds = entry.bounds.clone().applyMatrix4(entry.solid.matrixWorld); - if (!frustum.intersectsBox(bounds)) continue; - try { - const scale = uniformScale(entry.solid.matrixWorld); - const cameraBounds = bounds.clone().applyMatrix4(camera.matrixWorldInverse); - const depth = Math.max(camera.near, -cameraBounds.max.z); - const target = (this.fixedDeflection ?? worldUnitsPerPixel(camera, depth, physicalHeight) * (moving ? 2 : 0.5)) / scale; - const deflection = this.fixedDeflection === undefined ? deflectionBucket(target, entry.desired) : target; - if (entry.desired === deflection || entry.failed === deflection) continue; - candidates.push({ entry, deflection, distance: depth }); - } catch (error) { this.options.onError?.(entry.solid, asError(error)); } - } - candidates.sort((a, b) => a.distance - b.distance); - for (const { entry, deflection } of candidates) { - entry.desired = deflection; - const generation = ++entry.generation; - const revision = entry.solid.modelRevision; - void this.worker.tessellate(entry.handle, deflection * GEOMETRY_DEFLECTION_SHARE).then((mesh) => { - if (this.closed || this.entries.get(entry.solid) !== entry || generation !== entry.generation || entry.solid.modelRevision !== revision) return; - const stats = entry.solid.applyTessellation(mesh, deflection); - entry.accepted = deflection; - entry.failed = undefined; - this.options.onUpdate?.(entry.solid, stats); - }).catch((error) => { - if (this.closed || this.entries.get(entry.solid) !== entry || generation !== entry.generation || error?.name === "AbortError") return; - entry.failed = deflection; - entry.desired = entry.accepted; - this.options.onError?.(entry.solid, asError(error)); - }); - } - } - - /** Explicit export refinement; a resource or precision failure rejects and retains each body's prior mesh. */ - async refine(deflection: number): Promise { - if (!Number.isFinite(deflection) || deflection <= 0) throw new Error("Deflection must be finite and positive"); - if (this.closed || this.refining) throw new Error("LOD controller is disposed or export refinement is already running"); - this.refining = true; - try { - for (const entry of [...this.entries.values()]) { - if (!visible(entry.solid.surface)) continue; - if (!entry.ready) throw new Error("Await model registration before export refinement"); - entry.solid.updateWorldMatrix(true, false); - const local = deflection / uniformScale(entry.solid.matrixWorld); - const generation = ++entry.generation; - const mesh = await this.worker.tessellate(entry.handle, local * GEOMETRY_DEFLECTION_SHARE); - if (this.closed || this.entries.get(entry.solid) !== entry || generation !== entry.generation) throw new DOMException("Export refinement superseded", "AbortError"); - const stats = entry.solid.applyTessellation(mesh, local); - entry.desired = entry.accepted = local; - entry.failed = undefined; - this.options.onUpdate?.(entry.solid, stats); - } - } finally { this.refining = false; } - } - - dispose(): void { - if (this.closed) return; - this.closed = true; - for (const entry of this.entries.values()) { - entry.generation++; - void this.worker.removeModel(entry.handle).catch((error) => { - if (error?.name !== "AbortError") this.options.onError?.(entry.solid, asError(error)); - }); - } - this.entries.clear(); - } -} - -function visible(object: THREE.Object3D): boolean { - for (let current: THREE.Object3D | null = object; current; current = current.parent) if (!current.visible) return false; - return true; -} -function asError(error: unknown): Error { return error instanceof Error ? error : new Error(String(error)); } -export function uniformScale(matrix: THREE.Matrix4): number { - const x = new THREE.Vector3().setFromMatrixColumn(matrix, 0); - const y = new THREE.Vector3().setFromMatrixColumn(matrix, 1); - const z = new THREE.Vector3().setFromMatrixColumn(matrix, 2); - const scale = x.length(); - if (!Number.isFinite(scale) || scale <= 0 || Math.abs(y.length() / scale - 1) > 1e-9 || Math.abs(z.length() / scale - 1) > 1e-9 - || Math.abs(x.dot(y) / scale ** 2) > 1e-9 || Math.abs(x.dot(z) / scale ** 2) > 1e-9 || Math.abs(y.dot(z) / scale ** 2) > 1e-9 - || matrix.determinant() <= 0) throw new Error("Analytic placement requires positive uniform scale and a rigid frame"); - return scale; -} diff --git a/main/opengeometry-three/src/rendering/analytic-tessellation.ts b/main/opengeometry-three/src/rendering/analytic-tessellation.ts deleted file mode 100644 index 89c304b..0000000 --- a/main/opengeometry-three/src/rendering/analytic-tessellation.ts +++ /dev/null @@ -1,113 +0,0 @@ -export interface AnalyticTessellationData { - positions: Float64Array; - normals: Float32Array; - indices: Uint32Array; - triangleFaceIds: Uint32Array; - outlinePositions: Float64Array; - outlineEdgeIds: Uint32Array; - revision: bigint; - achievedDeflection: number; -} - -export const GEOMETRY_DEFLECTION_SHARE = 0.75; - -export interface AnalyticWorkerOptions { - workerURL: string | URL; - kernelURL: string | URL; - wasmURL: string | URL; -} - -interface Pending { - resolve: (value: unknown) => void; - reject: (reason: Error) => void; - handle?: string; - generation?: number; -} - -export class AnalyticTessellationWorker { - private readonly worker: Worker; - private readonly pending = new Map(); - private readonly generations = new Map(); - private readonly ready: Promise; - private serial = 0; - private closed = false; - - constructor(options: AnalyticWorkerOptions) { - const base = document.baseURI; - this.worker = new Worker(new URL(options.workerURL, base), { type: "module" }); - this.worker.onmessage = (event: MessageEvent) => { - const message = event.data; - const pending = this.pending.get(message?.request); - if (!pending) return; - this.pending.delete(message.request); - if (pending.handle && pending.generation !== this.generations.get(pending.handle)) { - pending.reject(new DOMException("Superseded tessellation request", "AbortError")); - } else if (message.error) { - pending.reject(new Error(typeof message.error === "string" ? message.error : JSON.stringify(message.error))); - } else { - pending.resolve(message.result); - } - }; - this.worker.onerror = (event) => this.fail(new Error(event.message || "Analytic worker failed")); - this.worker.onmessageerror = () => this.fail(new Error("Analytic worker response could not be decoded")); - this.ready = this.send({ - kind: "init", kernelURL: new URL(options.kernelURL, base).href, wasmURL: new URL(options.wasmURL, base).href, - }); - // Initialization errors remain observable through every public operation. - void this.ready.catch(() => undefined); - } - - async setModel(handle: string, serialized: string): Promise { - await this.ready; - if (!handle || handle.length > 1024) throw new Error("Worker model handle must contain 1–1024 characters"); - this.cancel(handle); - await this.send({ kind: "model", handle, serialized }); - } - - async tessellate(handle: string, deflection: number): Promise { - await this.ready; - if (!Number.isFinite(deflection) || deflection <= 0) throw new Error("Deflection must be finite and positive"); - const generation = this.cancel(handle); - return await this.send({ kind: "tessellate", handle, generation, deflection }, handle, generation) as AnalyticTessellationData; - } - - cancel(handle: string): number { - const generation = (this.generations.get(handle) ?? 0) + 1; - this.generations.set(handle, generation); - return generation; - } - - async removeModel(handle: string): Promise { - this.cancel(handle); - await this.ready; - await this.send({ kind: "remove", handle }); - this.generations.delete(handle); - } - - dispose(): void { - this.fail(new DOMException("Analytic worker disposed", "AbortError")); - } - - private send(message: Record, handle?: string, generation?: number): Promise { - if (this.closed) return Promise.reject(new Error("Analytic worker is closed")); - if (this.pending.size >= 128) return Promise.reject(new Error("Analytic worker request limit exceeded; previous mesh retained")); - const request = ++this.serial; - return new Promise((resolve, reject) => { - this.pending.set(request, { resolve, reject, handle, generation }); - try { this.worker.postMessage({ ...message, request }); } - catch (error) { - this.pending.delete(request); - reject(error instanceof Error ? error : new Error(String(error))); - } - }); - } - - private fail(error: Error): void { - if (this.closed) return; - this.closed = true; - this.worker.terminate(); - for (const pending of this.pending.values()) pending.reject(error); - this.pending.clear(); - this.generations.clear(); - } -} diff --git a/main/opengeometry-three/src/rendering/analytic-worker.js b/main/opengeometry-three/src/rendering/analytic-worker.js deleted file mode 100644 index 33175d1..0000000 --- a/main/opengeometry-three/src/rendering/analytic-worker.js +++ /dev/null @@ -1,90 +0,0 @@ -let runtime; -let initialization; -let totalBytes = 0; -const models = new Map(); -const jobs = new Map(); -let scheduled = false; - -function respond(request, result, transfer = []) { - self.postMessage({ request, result }, transfer); -} -function reject(request, error) { - self.postMessage({ request, error: error instanceof Error ? error.message : error }); -} -function drain() { - scheduled = false; - const entry = jobs.entries().next(); - if (entry.done) return; - const [handle, job] = entry.value; - jobs.delete(handle); - let mesh; - try { - const model = models.get(handle); - if (!model) throw new Error("Unknown analytic worker model handle"); - mesh = model.brep.tessellate(job.deflection); - const result = { - positions: mesh.positions(), normals: mesh.normals(), indices: mesh.indices(), - triangleFaceIds: mesh.triangle_face_ids(), outlinePositions: mesh.outline_positions(), - outlineEdgeIds: mesh.outline_edge_ids(), revision: mesh.revision(), achievedDeflection: mesh.achieved_deflection(), - }; - const transfer = [result.positions, result.normals, result.indices, result.triangleFaceIds, - result.outlinePositions, result.outlineEdgeIds].map((array) => array.buffer); - respond(job.request, result, transfer); - } catch (error) { reject(job.request, error); } - finally { mesh?.free(); } - schedule(); -} -function schedule() { - if (scheduled || !jobs.size) return; - scheduled = true; - setTimeout(drain, 0); -} -function invalidate(handle) { - const job = jobs.get(handle); - if (job) { - reject(job.request, "Superseded tessellation request"); - jobs.delete(handle); - } -} -async function receive(message) { - const { request, kind, handle } = message; - try { - if (kind === "init") { - if (initialization) throw new Error("Analytic worker already initialized"); - initialization = (async () => { - runtime = await import(/* @vite-ignore */ message.kernelURL); - await runtime.default({ module_or_path: message.wasmURL }); - })(); - await initialization; - respond(request, true); - return; - } - if (!initialization) throw new Error("Analytic worker has not been initialized"); - await initialization; - if (typeof handle !== "string" || !handle || handle.length > 1024) throw new Error("Invalid analytic worker handle"); - if (kind === "model") { - const bytes = typeof message.serialized === "string" ? new TextEncoder().encode(message.serialized).length : Infinity; - const previous = models.get(handle); - if (bytes > 64 * 1024 * 1024 || totalBytes - (previous?.bytes ?? 0) + bytes > 128 * 1024 * 1024 - || (!previous && models.size >= 64)) throw new Error("Analytic worker model resource limit exceeded"); - const brep = new runtime.OGAnalyticBrep(message.serialized); - invalidate(handle); - previous?.brep.free(); - models.set(handle, { brep, bytes }); - totalBytes += bytes - (previous?.bytes ?? 0); - respond(request, { revision: brep.get_revision() }); - } else if (kind === "remove") { - invalidate(handle); - const previous = models.get(handle); - if (previous) { previous.brep.free(); totalBytes -= previous.bytes; models.delete(handle); } - respond(request, true); - } else if (kind === "tessellate") { - if (!models.has(handle)) throw new Error("Unknown analytic worker model handle"); - if (!Number.isFinite(message.deflection) || message.deflection <= 0) throw new Error("Invalid tessellation deflection"); - invalidate(handle); - jobs.set(handle, message); - schedule(); - } else throw new Error("Unknown analytic worker operation"); - } catch (error) { reject(request, error); } -} -self.onmessage = (event) => { void receive(event.data); }; diff --git a/main/opengeometry-three/src/rendering/deferred.ts b/main/opengeometry-three/src/rendering/deferred.ts new file mode 100644 index 0000000..9b19549 --- /dev/null +++ b/main/opengeometry-three/src/rendering/deferred.ts @@ -0,0 +1,15 @@ +import type { OGError } from '../errors'; + +export class Deferred { + resolve!: (value: T) => void; + reject!: (reason: OGError) => void; + readonly promise: Promise; + + constructor() { + this.promise = new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + }); + this.promise.catch(() => undefined); + } +} diff --git a/main/opengeometry-three/src/rendering/geometry/geometry-scheduler.ts b/main/opengeometry-three/src/rendering/geometry/geometry-scheduler.ts new file mode 100644 index 0000000..9875ee2 --- /dev/null +++ b/main/opengeometry-three/src/rendering/geometry/geometry-scheduler.ts @@ -0,0 +1,197 @@ +import type { ChangeSet } from '../../dto/change-set'; +import type { DisplayBuffers } from '../../dto/display-buffers'; +import { OGError } from '../../errors'; +import { MAX_TRIANGLES } from '../../limits'; +import { call } from '../../kernel/kernel-session'; +import type { Body } from '../../bodies/body'; +import { emit } from '../../runtime/event-bus'; +import { bodyKey, currentRuntime, reindexShape, runtime, type Runtime } from '../../runtime/runtime-state'; +import { decodeChangeSet } from '../../world-graph/codec'; +import { + clearError, displayedBodies, isDisplayed, markFailed, recordError, retryBucket, shapePriority, wantedKey, +} from '../lod/lod-controller'; +import type { TessellationRequest } from '../provider'; +import { GeometryRecord } from '../records/geometry-record'; +import { validateDisplayBuffers } from '../records/validate-display-buffers'; + +type GeometryTarget = { body: Body; shapeId: string; revision: number; bucket: number; key: string }; + +export function flush(options: { geometry?: 'sync' } = {}): void { + const state = runtime(); + const revision = call('OpenGeometry.flush', () => state.graph.revision()); + if (!state.flushing + && (revision !== state.revision || (!state.renderPassActive && state.readyVersion !== state.flushedReadyVersion))) { + state.flushing = true; + try { + const packet = call('OpenGeometry.flush', (): unknown => state.graph.changesSince(state.revision)); + const { changes, matrices } = decodeChangeSet(packet, 'OpenGeometry.flush'); + state.revision = revision; + applyChanges(state, changes, matrices); + for (const body of state.bodies.values()) body.swapReadyRecord(); + purgeChanged(state, changes); + state.flushedReadyVersion = state.readyVersion; + state.flushes++; + } finally { + state.flushing = false; + } + } + if (options.geometry === 'sync') { + for (const body of displayedBodies(state)) ensureGeometry(body, true); + if (state.readyVersion !== state.flushedReadyVersion) flush(); + } +} + +function applyChanges(state: Runtime, changes: ChangeSet, matrices: Float64Array): void { + let offset = 0; + for (const change of [...changes.added, ...changes.changed]) { + const body = state.bodies.get(bodyKey(change.handle, change.generation)); + if (body) { + const previous = body.lastInfo.shapeId; + body.lastInfo = { ...body.lastInfo, shapeId: change.shapeId, shapeRevision: change.shapeRevision }; + reindexShape(state, body, previous); + body.applyWorldMatrix(matrices.subarray(offset, offset + 16)); + } + offset += 16; + } + for (const change of changes.removed) state.bodies.get(bodyKey(change.handle, change.generation))?.hideForDispose(); +} + +function purgeChanged(state: Runtime, changes: ChangeSet): void { + const shapes = new Map(); + for (const { shapeId, shapeRevision } of [...changes.added, ...changes.changed]) { + if (shapeId !== null && shapeRevision !== null) { + shapes.set(`${shapeId}@${String(shapeRevision)}`, { shapeId, revision: shapeRevision }); + } + } + for (const { shapeId, revision } of shapes.values()) state.records.purge(shapeId, revision); +} + +export function wanted(body: Body): number | undefined { + return wantedKey(runtime(), body); +} + +export function ensureGeometry(body: Body, sync = false): void { + const state = runtime(); + const target = geometryTarget(state, body); + if (!target) return; + const record = state.records.get(target.shapeId, target.revision, target.bucket); + if (record) { + body.install(record); + return; + } + if (sync || state.provider.compute) { + computeNow(state, target, sync); + return; + } + if (state.pending.has(target.key)) return; + requestLater(state, target); +} + +function geometryTarget(state: Runtime, body: Body): GeometryTarget | undefined { + const { shapeId, shapeRevision: revision } = body.lastInfo; + if (!shapeId || revision === null || !body.visible) return undefined; + const bucket = wantedKey(state, body); + if (bucket === undefined) return undefined; + return { body, shapeId, revision, bucket, key: pendingKey(shapeId, revision, bucket) }; +} + +function pendingKey(shapeId: string, revision: number, bucket: number): string { + return `${shapeId}@${String(revision)}#${String(bucket)}`; +} + +function computeNow(state: Runtime, target: GeometryTarget, sync: boolean): void { + const first = computeFailure(state, target, sync); + if (!first) return; + const retry = coarserRetry(state, target, first.error); + const failure = retry ? computeFailure(state, retry, false) : first; + if (failure) reportFailure(state, target, failure.error); +} + +function computeFailure(state: Runtime, target: GeometryTarget, sync: boolean): { error: unknown } | undefined { + try { + const request = requestFor(state, target); + if (sync) state.provider.cancelShape(target.shapeId, request.generation); + receive(state, target, computed(state, request)); + return undefined; + } catch (error) { + if (fatal(state, error)) throw error; + return { error }; + } +} + +function coarserRetry(state: Runtime, target: GeometryTarget, error: unknown): GeometryTarget | undefined { + const { shapeId, revision, bucket } = target; + if (!(error instanceof OGError && error.code === 'LimitExceeded') || !markFailed(state, shapeId, revision, bucket)) { + return undefined; + } + emit('warning', { code: 'LimitExceeded', shapeId, revision, bucket, retryBucket: bucket * 2 }); + return { ...target, bucket: bucket * 2, key: pendingKey(shapeId, revision, bucket * 2) }; +} + +function reportFailure(state: Runtime, target: GeometryTarget, error: unknown): void { + recordError(state, target.shapeId, target.revision, error); + emit('error', error); +} + +function computed(state: Runtime, request: TessellationRequest): DisplayBuffers { + if (state.provider.compute) return state.provider.compute(request); + return validateDisplayBuffers(call('OpenGeometry.flush', (): unknown => + state.graph.buffers(request.shapeId, request.bucket, request.maxTriangles))); +} + +function fatal(state: Runtime, error: unknown): boolean { + return state.poisoned && error instanceof OGError && error.code === 'KernelPanic'; +} + +function requestLater(state: Runtime, target: GeometryTarget): void { + let request = requestFor(state, target); + const promise = state.provider.request(request).catch(async (error: unknown) => { + const retry = currentRuntime() === state ? coarserRetry(state, target, error) : undefined; + if (!retry) throw error; + request = requestFor(state, retry); + return state.provider.request(request); + }).then((buffers) => { + if (currentRuntime() === state && accepted(state, target, buffers, request.generation)) { + receive(state, target, buffers); + } + }).catch((error: unknown) => { + if (currentRuntime() !== state || fatal(state, error)) return; + if (!(error instanceof OGError && error.code === 'Cancelled')) reportFailure(state, target, error); + }).finally(() => { state.pending.delete(target.key); }); + state.pending.set(target.key, promise); +} + +function accepted(state: Runtime, target: GeometryTarget, buffers: DisplayBuffers, generation: number): boolean { + const { body, shapeId, revision } = target; + if (generation === (state.generations.get(shapeId) ?? 0)) return true; + if (buffers.bucket === retryBucket(state, shapeId, revision)) return true; + const wanted = wantedKey(state, body); + if (wanted === undefined) return false; + const shown = body.record?.shapeId === shapeId && body.record.revision === revision ? body.record.bucket : undefined; + return shown === undefined || Math.abs(Math.log2(buffers.bucket / wanted)) < Math.abs(Math.log2(shown / wanted)); +} + +function requestFor(state: Runtime, target: GeometryTarget): TessellationRequest { + const { shapeId, revision, bucket } = target; + const generation = (state.generations.get(shapeId) ?? 0) + 1; + state.generations.set(shapeId, generation); + const priority = shapePriority(state, shapeId); + return { shapeId, revision, bucket, priority, maxTriangles: MAX_TRIANGLES, generation }; +} + +function receive(state: Runtime, target: GeometryTarget, buffers: DisplayBuffers): void { + const { shapeId, revision } = target; + const holders = [...(state.byShape.get(shapeId) ?? [])]; + const current = holders.filter((body) => body.lastInfo.shapeRevision === revision); + if (current.length === 0) return; + const record = state.records.put(new GeometryRecord(shapeId, buffers)); + state.readyVersion++; + clearError(state, shapeId, revision); + if (state.renderPassActive) { + for (const body of current) if (isDisplayed(state, body)) body.install(record); + } + const ogIds = holders.map((body) => body.ogId); + emit('geometry', { shapeId, revision, bucket: buffers.bucket, ogIds, stats: { triangles: record.triangles } }); + if (!state.renderPassActive) flush(); + state.records.purge(shapeId, revision); +} diff --git a/main/opengeometry-three/src/rendering/geometry/render-pass.ts b/main/opengeometry-three/src/rendering/geometry/render-pass.ts new file mode 100644 index 0000000..c191606 --- /dev/null +++ b/main/opengeometry-three/src/rendering/geometry/render-pass.ts @@ -0,0 +1,27 @@ +import type { Body } from '../../bodies/body'; +import { currentRuntime, runtime, type Runtime } from '../../runtime/runtime-state'; +import { displayedBodies, evaluatePass, stampDisplayed } from '../lod/lod-controller'; +import { ensureGeometry, flush } from './geometry-scheduler'; + +export function noteDisplayed(body: Body): void { + const state = runtime(); + if (!state.renderPassActive) { + state.renderPassActive = true; + queueMicrotask(() => { endPass(state); }); + } + stampDisplayed(state, body); + flush(); + ensureGeometry(body); +} + +function endPass(state: Runtime): void { + if (currentRuntime() !== state) return; + state.renderPassActive = false; + state.pass++; + if (state.readyVersion !== state.flushedReadyVersion) flush(); + if (evaluatePass(state, Date.now(), false)) refreshDisplayed(state); +} + +export function refreshDisplayed(state: Runtime): void { + for (const body of displayedBodies(state)) ensureGeometry(body); +} diff --git a/main/opengeometry-three/src/rendering/geometry/settle.ts b/main/opengeometry-three/src/rendering/geometry/settle.ts new file mode 100644 index 0000000..e52f443 --- /dev/null +++ b/main/opengeometry-three/src/rendering/geometry/settle.ts @@ -0,0 +1,35 @@ +import { OGError } from '../../errors'; +import { runtime, type Runtime } from '../../runtime/runtime-state'; +import { + capturedError, displayedBodies, popTemporaryTarget, pushTemporaryTarget, settleNow, wantedKey, +} from '../lod/lod-controller'; +import { flush } from './geometry-scheduler'; +import { refreshDisplayed } from './render-pass'; + +type Failure = { ogId: string; error: unknown }; + +export async function settled(options: { deflection?: number } = {}): Promise<{ failed: Failure[] }> { + const state = runtime(); + const token = options.deflection === undefined ? undefined : pushTemporaryTarget(state, options.deflection); + try { + settleNow(state); + refreshDisplayed(state); + await Promise.all([...state.pending.values()]); + flush(); + return { failed: failures(state) }; + } finally { + if (token) popTemporaryTarget(state, token); + } +} + +function failures(state: Runtime): Failure[] { + const failed: Failure[] = []; + for (const body of displayedBodies(state)) { + const { record, lastInfo } = body; + const current = record?.shapeId === lastInfo.shapeId && record.revision === lastInfo.shapeRevision; + if (current && record.bucket === wantedKey(state, body)) continue; + const error = capturedError(state, body) ?? new OGError('Cancelled', 'OpenGeometry.settled', 'no current record'); + failed.push({ ogId: body.ogId, error }); + } + return failed; +} diff --git a/main/opengeometry-three/src/rendering/inline-backend.ts b/main/opengeometry-three/src/rendering/inline-backend.ts new file mode 100644 index 0000000..f2ec2c0 --- /dev/null +++ b/main/opengeometry-three/src/rendering/inline-backend.ts @@ -0,0 +1,29 @@ +import type { DisplayBuffers } from '../dto/display-buffers'; +import type { OGWorldGraph } from '../kernel/kernel-loader'; +import { call } from '../kernel/kernel-session'; +import type { TessellationProvider, TessellationRequest } from './provider'; +import { validateDisplayBuffers } from './records/validate-display-buffers'; + +export class InlineBackend implements TessellationProvider { + readonly activeBackend = 'inline' as const; + + constructor(private graph: OGWorldGraph) {} + + ensureSnapshot(): Promise { + return Promise.resolve(); + } + + compute(value: TessellationRequest): DisplayBuffers { + const { shapeId, bucket, maxTriangles } = value; + const buffers = call('tessellation', (): unknown => this.graph.buffers(shapeId, bucket, maxTriangles)); + return validateDisplayBuffers(buffers); + } + + request(value: TessellationRequest): Promise { + return new Promise((resolve) => { resolve(this.compute(value)); }); + } + + cancelShape(): void {} + drop(): void {} + dispose(): void {} +} diff --git a/main/opengeometry-three/src/rendering/lod/body-size.ts b/main/opengeometry-three/src/rendering/lod/body-size.ts new file mode 100644 index 0000000..43b8170 --- /dev/null +++ b/main/opengeometry-three/src/rendering/lod/body-size.ts @@ -0,0 +1,45 @@ +import type { Body } from '../../bodies/body'; +import { OGError } from '../../errors'; +import type { Runtime } from '../../runtime/runtime-state'; +import { displayBuckets } from '../../world-graph/world-graph-client'; + +export type ShapeBuckets = { revision: number; floor: number; static: number; error?: unknown }; + +export function shapeBuckets(state: Runtime, body: Body): ShapeBuckets | undefined { + const { shapeId, shapeRevision } = body.lastInfo; + if (!shapeId || shapeRevision === null) return undefined; + let entry = state.shapeBuckets.get(shapeId); + if (entry?.revision !== shapeRevision) { + entry = readBuckets(body.ogId, shapeRevision); + state.shapeBuckets.set(shapeId, entry); + } + return entry.error === undefined ? entry : undefined; +} + +function readBuckets(ogId: string, revision: number): ShapeBuckets { + try { + return { revision, ...displayBuckets(ogId, 'OpenGeometry.flush') }; + } catch (error) { + if (!(error instanceof OGError) || error.code === 'KernelPanic') throw error; + return { revision, floor: 0, static: 0, error }; + } +} + +export function localTarget(world: number, body: Body): number | undefined { + const scale = worldScale(body); + return scale > 0 && Number.isFinite(scale) ? world / scale : undefined; +} + +function worldScale(body: Body): number { + return Math.cbrt(Math.abs(body.matrixWorld.determinant())); +} + +export function finestLocal(bodies: Iterable, valueOf: (body: Body) => number | undefined): number | undefined { + let finest: number | undefined; + for (const body of bodies) { + const value = valueOf(body); + const target = value === undefined ? undefined : localTarget(value, body); + if (target !== undefined) finest = finest === undefined ? target : Math.min(finest, target); + } + return finest; +} diff --git a/main/opengeometry-three/src/rendering/lod/camera-observer.ts b/main/opengeometry-three/src/rendering/lod/camera-observer.ts new file mode 100644 index 0000000..c8e1c7f --- /dev/null +++ b/main/opengeometry-three/src/rendering/lod/camera-observer.ts @@ -0,0 +1,7 @@ +import * as THREE from 'three'; +import { runtime } from '../../runtime/runtime-state'; +import { observe } from './lod-controller'; + +export function observeCamera(renderer: THREE.WebGLRenderer, camera: THREE.Camera): void { + observe(runtime(), camera, renderer.getDrawingBufferSize(new THREE.Vector2()).y, Date.now()); +} diff --git a/main/opengeometry-three/src/rendering/lod/deflection.ts b/main/opengeometry-three/src/rendering/lod/deflection.ts new file mode 100644 index 0000000..0a50388 --- /dev/null +++ b/main/opengeometry-three/src/rendering/lod/deflection.ts @@ -0,0 +1,18 @@ +import { OGError } from '../../errors'; +import { HYSTERESIS_KEEP } from '../../limits'; + +export function deflectionBucket(target: number): number { + if (!Number.isFinite(target) || target <= 0) { + throw new OGError('InvalidParameter', 'deflection', 'invalid display deflection'); + } + return 2 ** Math.floor(Math.log2(target)); +} + +export function wantedBucket(target: number, floor: number, previous?: number, moving = false): number { + const value = Math.max(floor, deflectionBucket(target)); + if (previous === undefined) return value; + const [low, high] = HYSTERESIS_KEEP; + const kept = moving ? target >= low * previous : target >= low * previous && target <= high * previous; + if (kept) return previous; + return value; +} diff --git a/main/opengeometry-three/src/rendering/lod/lod-controller.ts b/main/opengeometry-three/src/rendering/lod/lod-controller.ts new file mode 100644 index 0000000..be99c17 --- /dev/null +++ b/main/opengeometry-three/src/rendering/lod/lod-controller.ts @@ -0,0 +1,249 @@ +import type * as THREE from 'three'; +import type { Body } from '../../bodies/body'; +import { EVALUATION_INTERVAL_MS, MOTION_WINDOW_MS, PIXELS_AT_REST, PIXELS_MOVING } from '../../limits'; +import { currentRuntime, type Runtime } from '../../runtime/runtime-state'; +import { finestLocal, localTarget, shapeBuckets, type ShapeBuckets } from './body-size'; +import { deflectionBucket, wantedBucket } from './deflection'; +import { worldUnitsPerPixel } from './screen-metrics'; + +type Tiers = { temporary: number | undefined; own: number | undefined; global: number | undefined }; +type SeenCamera = { camera: THREE.Camera; height: number }; +type ShapeTarget = { target: number; body: Body }; +type TemporaryTarget = { deflection: number }; + +type ShapeLod = { + shapeId: string; + target?: number; + previous?: number; + failed: Set; + pinned?: { revision: number; bucket: number }; + forced?: number; + error?: { revision: number; error: unknown }; + overrides?: { pass: number; tiers: Tiers }; +}; + +export type LodState = { + shapes: Map; + camera: Float64Array; + initialised: boolean; + lastMotion: number; + lastSchedule: number; + settleTimer?: ReturnType; + lastSeen?: SeenCamera; + temporaries: TemporaryTarget[]; + refresh: () => void; +}; + +export function createLodState(refresh: () => void): LodState { + return { + shapes: new Map(), camera: new Float64Array(33), initialised: false, lastMotion: -Infinity, + lastSchedule: -Infinity, temporaries: [], refresh, + }; +} + +export function isDisplayed(state: Runtime, body: Body): boolean { + return body.visible && (state.displayed.get(body) ?? -Infinity) >= state.pass - 1; +} + +export function displayedBodies(state: Runtime): Body[] { + return [...state.displayed.keys()].filter((body) => isDisplayed(state, body)); +} + +export function stampDisplayed(state: Runtime, body: Body): void { + const shapeId = body.lastInfo.shapeId; + if (shapeId && !isDisplayed(state, body)) delete state.lod.shapes.get(shapeId)?.overrides; + state.displayed.set(body, state.pass); +} + +export function observe(state: Runtime, camera: THREE.Camera, height: number, now: number): void { + const lod = state.lod; + const values = [...camera.matrixWorld.elements, ...camera.projectionMatrix.elements, height]; + const changed = values.some((value, index) => value !== lod.camera[index]); + lod.camera.set(values); + if (lod.initialised && changed) motionHint(state, now); + lod.initialised = true; + lod.lastSeen = { camera, height }; +} + +export function motionHint(state: Runtime, now: number): void { + const lod = state.lod; + lod.lastMotion = now; + clearTimeout(lod.settleTimer); + lod.settleTimer = setTimeout(() => { fireSettle(state); }, MOTION_WINDOW_MS); +} + +function fireSettle(state: Runtime): void { + delete state.lod.settleTimer; + if (currentRuntime() !== state || state.poisoned) return; + if (evaluatePass(state, Date.now(), true)) state.lod.refresh(); +} + +export function settleNow(state: Runtime): void { + clearTimeout(state.lod.settleTimer); + delete state.lod.settleTimer; + evaluatePass(state, Date.now(), true); +} + +export function evaluatePass(state: Runtime, now: number, forced: boolean): boolean { + const lod = state.lod; + const seen = lod.lastSeen; + if (!seen || state.poisoned || (!forced && now - lod.lastSchedule < EVALUATION_INTERVAL_MS)) return false; + lod.lastSchedule = now; + const moving = !forced && now - lod.lastMotion < MOTION_WINDOW_MS; + let changed = false; + for (const [shapeId, { target, body }] of shapeTargets(state, seen, moving ? PIXELS_MOVING : PIXELS_AT_REST)) { + const buckets = shapeBuckets(state, body); + if (!buckets) continue; + const entry = shapeLod(state, shapeId); + const previous = entry.previous; + entry.target = target; + entry.previous = wantedBucket(target, buckets.floor, previous, moving); + changed ||= entry.previous !== previous; + } + return changed; +} + +function shapeTargets(state: Runtime, seen: SeenCamera, pixels: number): Map { + const targets = new Map(); + for (const body of displayedBodies(state)) { + const shapeId = body.lastInfo.shapeId; + const bounds = shapeId ? body.getBounds() : null; + const world = bounds ? worldUnitsPerPixel(seen.camera, bounds, seen.height) * pixels : undefined; + const target = world === undefined ? undefined : localTarget(world, body); + if (!shapeId || target === undefined || !Number.isFinite(target) || target <= 0) continue; + const best = targets.get(shapeId); + if (!best || target < best.target) targets.set(shapeId, { target, body }); + } + return targets; +} + +export function wantedKey(state: Runtime, body: Body): number | undefined { + const { shapeId, shapeRevision: revision } = body.lastInfo; + if (!shapeId || revision === null) return undefined; + const buckets = shapeBuckets(state, body); + if (!buckets) return undefined; + const entry = shapeLod(state, shapeId); + const forced = entry.forced; + if (forced !== undefined) { + delete entry.forced; + return forced; + } + if (entry.pinned?.revision === revision) return entry.pinned.bucket; + let bucket = chosenBucket(state, body, entry, buckets); + while (entry.failed.has(failedKey(revision, bucket))) bucket *= 2; + return bucket; +} + +function chosenBucket(state: Runtime, body: Body, entry: ShapeLod, buckets: ShapeBuckets): number { + const override = overrideTarget(state, body, entry); + if (override !== undefined) return Math.max(buckets.floor, deflectionBucket(override)); + if (entry.previous !== undefined) return Math.max(buckets.floor, entry.previous); + return buckets.static; +} + +function overrideTarget(state: Runtime, body: Body, entry: ShapeLod): number | undefined { + if (entry.overrides?.pass !== state.pass) { + const members = [...(state.byShape.get(entry.shapeId) ?? [])].filter((member) => isDisplayed(state, member)); + entry.overrides = { pass: state.pass, tiers: tiers(state, members) }; + } + const shape = entry.overrides.tiers; + const own = tiers(state, [body]); + return finer(shape.temporary, own.temporary) ?? finer(shape.own, own.own) ?? finer(shape.global, own.global); +} + +function tiers(state: Runtime, bodies: Body[]): Tiers { + return { + temporary: finestLocal(bodies, () => state.lod.temporaries.at(-1)?.deflection), + own: finestLocal(bodies, (body) => body.appearance.deflection), + global: finestLocal(bodies, () => state.displayDeflection), + }; +} + +function finer(first: number | undefined, second: number | undefined): number | undefined { + if (first === undefined) return second; + return second === undefined ? first : Math.min(first, second); +} + +function shapeLod(state: Runtime, shapeId: string): ShapeLod { + let entry = state.lod.shapes.get(shapeId); + if (!entry) { + entry = { shapeId, failed: new Set() }; + state.lod.shapes.set(shapeId, entry); + } + return entry; +} + +function failedKey(revision: number, bucket: number): string { + return `${String(revision)}#${String(bucket)}`; +} + +export function overrideChanged(state: Runtime, shapeId?: string): void { + const entries = shapeId === undefined ? [...state.lod.shapes.values()] : [state.lod.shapes.get(shapeId)]; + for (const entry of entries) { + if (!entry) continue; + clearMarks(entry); + delete entry.previous; + } +} + +export function pushTemporaryTarget(state: Runtime, deflection: number): TemporaryTarget { + const token = { deflection }; + state.lod.temporaries.push(token); + clearAllMarks(state); + return token; +} + +export function popTemporaryTarget(state: Runtime, token: TemporaryTarget): void { + const temporaries = state.lod.temporaries; + temporaries.splice(temporaries.indexOf(token), 1); + clearAllMarks(state); +} + +function clearAllMarks(state: Runtime): void { + for (const entry of state.lod.shapes.values()) clearMarks(entry); +} + +function clearMarks(entry: ShapeLod): void { + entry.failed.clear(); + delete entry.pinned; + delete entry.overrides; +} + +export function markFailed(state: Runtime, shapeId: string, revision: number, bucket: number): boolean { + const entry = shapeLod(state, shapeId); + const key = failedKey(revision, bucket); + if (entry.failed.has(key)) return false; + entry.failed.add(key); + entry.pinned = { revision, bucket: bucket * 2 }; + return true; +} + +export function retryBucket(state: Runtime, shapeId: string, revision: number): number | undefined { + const pinned = state.lod.shapes.get(shapeId)?.pinned; + return pinned?.revision === revision ? pinned.bucket : undefined; +} + +export function shapePriority(state: Runtime, shapeId: string): number { + return state.lod.shapes.get(shapeId)?.target ?? 0; +} + +export function recordError(state: Runtime, shapeId: string, revision: number, error: unknown): void { + shapeLod(state, shapeId).error = { revision, error }; +} + +export function clearError(state: Runtime, shapeId: string, revision: number): void { + const entry = state.lod.shapes.get(shapeId); + if (entry?.error?.revision === revision) delete entry.error; +} + +export function capturedError(state: Runtime, body: Body): unknown { + const { shapeId, shapeRevision } = body.lastInfo; + const failure = shapeId ? state.lod.shapes.get(shapeId)?.error : undefined; + if (failure?.revision === shapeRevision) return failure.error; + const buckets = shapeId ? state.shapeBuckets.get(shapeId) : undefined; + return buckets?.revision === shapeRevision ? buckets.error : undefined; +} + +export function forceNextBucket(state: Runtime, shapeId: string, bucket: number): void { + shapeLod(state, shapeId).forced = bucket; +} diff --git a/main/opengeometry-three/src/rendering/lod/screen-metrics.ts b/main/opengeometry-three/src/rendering/lod/screen-metrics.ts new file mode 100644 index 0000000..1c9197a --- /dev/null +++ b/main/opengeometry-three/src/rendering/lod/screen-metrics.ts @@ -0,0 +1,17 @@ +import * as THREE from 'three'; + +export function worldUnitsPerPixel( + camera: THREE.Camera, + bounds: [number, number, number, number, number, number], + height: number, +): number { + if (camera instanceof THREE.OrthographicCamera) return (camera.top - camera.bottom) / (camera.zoom * height); + if (!(camera instanceof THREE.PerspectiveCamera)) return 1 / height; + const centre = new THREE.Vector3( + (bounds[0] + bounds[3]) / 2, (bounds[1] + bounds[4]) / 2, (bounds[2] + bounds[5]) / 2, + ); + centre.applyMatrix4(camera.matrixWorldInverse); + const extent = Math.hypot(bounds[3] - bounds[0], bounds[4] - bounds[1], bounds[5] - bounds[2]) / 2; + const depth = Math.max(camera.near, -centre.z - extent); + return 2 * depth * Math.tan(THREE.MathUtils.degToRad(camera.getEffectiveFOV()) / 2) / height; +} diff --git a/main/opengeometry-three/src/rendering/materials/material-pool.ts b/main/opengeometry-three/src/rendering/materials/material-pool.ts new file mode 100644 index 0000000..994bd5a --- /dev/null +++ b/main/opengeometry-three/src/rendering/materials/material-pool.ts @@ -0,0 +1,32 @@ +import * as THREE from 'three'; +import { runtime } from '../../runtime/runtime-state'; +import { acquire, release, type PoolEntry, type PooledMaterial } from './ref-counted-pool'; + +export type MaterialAppearance = { color: number; opacity: number }; +export type SurfaceEntry = PoolEntry; +export type LineEntry = PoolEntry; + +function styleKey(value: MaterialAppearance): string { + return `${String(value.color)}:${String(value.opacity)}`; +} + +export function surfaceMaterial(value: MaterialAppearance): PooledMaterial { + return acquire(runtime().surfacePool, styleKey(value), () => new THREE.MeshStandardMaterial({ + color: value.color, opacity: value.opacity, transparent: value.opacity < 1, side: THREE.FrontSide, + polygonOffset: true, polygonOffsetFactor: 1, polygonOffsetUnits: 1, + })); +} + +export function lineMaterial(value: MaterialAppearance): PooledMaterial { + return acquire(runtime().linePool, styleKey(value), () => new THREE.LineBasicMaterial({ + color: value.color, opacity: value.opacity, transparent: value.opacity < 1, + })); +} + +export function releaseSurface(key: string): void { + release(runtime().surfacePool, key); +} + +export function releaseLine(key: string): void { + release(runtime().linePool, key); +} diff --git a/main/opengeometry-three/src/rendering/materials/ref-counted-pool.ts b/main/opengeometry-three/src/rendering/materials/ref-counted-pool.ts new file mode 100644 index 0000000..d13c6db --- /dev/null +++ b/main/opengeometry-three/src/rendering/materials/ref-counted-pool.ts @@ -0,0 +1,21 @@ +export type PoolEntry = { material: M; refs: number }; +export type PooledMaterial = { key: string; material: M }; + +export function acquire(pool: Map>, key: string, create: () => M): PooledMaterial { + let entry = pool.get(key); + if (!entry) { + entry = { material: create(), refs: 0 }; + pool.set(key, entry); + } + entry.refs++; + return { key, material: entry.material }; +} + +export function release(pool: Map>, key: string): void { + const entry = pool.get(key); + if (!entry) return; + if (--entry.refs <= 0) { + entry.material.dispose(); + pool.delete(key); + } +} diff --git a/main/opengeometry-three/src/rendering/provider.ts b/main/opengeometry-three/src/rendering/provider.ts new file mode 100644 index 0000000..b7d9a5f --- /dev/null +++ b/main/opengeometry-three/src/rendering/provider.ts @@ -0,0 +1,20 @@ +import type { DisplayBuffers } from '../dto/display-buffers'; + +export type TessellationRequest = { + shapeId: string; + revision: number; + bucket: number; + priority: number; + maxTriangles: number; + generation: number; +}; + +export type TessellationProvider = { + readonly activeBackend: 'worker' | 'inline'; + ensureSnapshot(shapeId: string, revision: number): Promise; + request(value: TessellationRequest): Promise; + cancelShape(shapeId: string, generation: number): void; + drop(shapeId: string, revision: number): void; + dispose(): void; + compute?(value: TessellationRequest): DisplayBuffers; +}; diff --git a/main/opengeometry-three/src/rendering/records/geometry-record.ts b/main/opengeometry-three/src/rendering/records/geometry-record.ts new file mode 100644 index 0000000..804ca4c --- /dev/null +++ b/main/opengeometry-three/src/rendering/records/geometry-record.ts @@ -0,0 +1,50 @@ +import * as THREE from 'three'; +import type { DisplayBuffers } from '../../dto/display-buffers'; +import { validateDisplayBuffers } from './validate-display-buffers'; + +export class GeometryRecord { + readonly key: string; + readonly surface: THREE.BufferGeometry | null; + readonly outline: THREE.BufferGeometry; + readonly origin: THREE.Vector3; + readonly faceRanges: Uint32Array; + readonly edgeIds: Uint32Array; + readonly revision: number; + readonly bucket: number; + readonly triangles: number; + readonly bytes: number; + refs = 0; + lastUse = 0; + + constructor(readonly shapeId: string, buffers: DisplayBuffers) { + validateDisplayBuffers(buffers); + this.key = `${shapeId}@${String(buffers.revision)}#${String(Math.log2(buffers.bucket))}`; + this.revision = buffers.revision; + this.bucket = buffers.bucket; + this.triangles = buffers.triangles; + this.origin = new THREE.Vector3(buffers.origin[0], buffers.origin[1], buffers.origin[2]); + this.faceRanges = buffers.faceRanges; + this.edgeIds = buffers.edgeIds; + this.bytes = buffers.positions.byteLength + buffers.normals.byteLength + buffers.indices.byteLength + + buffers.faceRanges.byteLength + buffers.outline.byteLength + buffers.edgeIds.byteLength; + this.surface = buffers.indices.length ? new THREE.BufferGeometry() : null; + if (this.surface) { + this.surface.setAttribute('position', new THREE.BufferAttribute(buffers.positions, 3)); + this.surface.setAttribute('normal', new THREE.BufferAttribute(buffers.normals, 3)); + this.surface.setIndex(new THREE.BufferAttribute(buffers.indices, 1)); + this.surface.computeBoundingBox(); + this.surface.computeBoundingSphere(); + } + this.outline = new THREE.BufferGeometry(); + this.outline.setAttribute('position', new THREE.BufferAttribute(buffers.outline, 3)); + if (buffers.outline.length) { + this.outline.computeBoundingBox(); + this.outline.computeBoundingSphere(); + } + } + + dispose(): void { + this.surface?.dispose(); + this.outline.dispose(); + } +} diff --git a/main/opengeometry-three/src/rendering/records/record-pool.ts b/main/opengeometry-three/src/rendering/records/record-pool.ts new file mode 100644 index 0000000..f4ee22c --- /dev/null +++ b/main/opengeometry-three/src/rendering/records/record-pool.ts @@ -0,0 +1,104 @@ +import * as THREE from 'three'; +import type { Bounds } from '../../dto/bounds'; +import type { GeometryRecord } from './geometry-record'; + +type Placeholder = { key: string; geometry: THREE.BufferGeometry; holders: number }; + +function placeholderGeometry(bounds: Bounds | null): THREE.BufferGeometry { + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(0), 3)); + geometry.boundingBox = bounds ? new THREE.Box3().setFromArray(bounds) : new THREE.Box3(); + geometry.boundingSphere = geometry.boundingBox.getBoundingSphere(new THREE.Sphere()); + return geometry; +} + +export class RecordPool { + private records = new Map(); + private placeholders = new Map(); + private placeholderOf = new Map(); + private clock = 0; + + get(shapeId: string, revision: number, bucket: number): GeometryRecord | undefined { + const record = this.records.get(`${shapeId}@${String(revision)}#${String(Math.log2(bucket))}`); + if (record) record.lastUse = ++this.clock; + return record; + } + + put(record: GeometryRecord): GeometryRecord { + const previous = this.records.get(record.key); + if (previous) { + record.dispose(); + return previous; + } + record.lastUse = ++this.clock; + this.records.set(record.key, record); + this.evict(); + return record; + } + + retain(record: GeometryRecord): void { + record.refs++; + record.lastUse = ++this.clock; + } + + release(record: GeometryRecord): void { + record.refs = Math.max(0, record.refs - 1); + record.lastUse = ++this.clock; + this.evict(); + } + + purge(shapeId: string, revision?: number): void { + for (const [key, record] of this.records) { + if (record.shapeId === shapeId && (revision === undefined || record.revision !== revision) && record.refs === 0) { + record.dispose(); + this.records.delete(key); + } + } + } + + placeholder(shapeId: string, revision: number, bounds: () => Bounds | null): THREE.BufferGeometry { + const key = `${shapeId}@${String(revision)}`; + let entry = this.placeholders.get(key); + if (!entry) { + entry = { key, geometry: placeholderGeometry(bounds()), holders: 0 }; + this.placeholders.set(key, entry); + this.placeholderOf.set(entry.geometry, entry); + } + entry.holders++; + return entry.geometry; + } + + releasePlaceholder(geometry: THREE.BufferGeometry): boolean { + const entry = this.placeholderOf.get(geometry); + if (!entry) return false; + if (--entry.holders <= 0) { + geometry.dispose(); + this.placeholders.delete(entry.key); + this.placeholderOf.delete(geometry); + } + return true; + } + + clear(): void { + for (const record of this.records.values()) record.dispose(); + this.records.clear(); + for (const entry of this.placeholders.values()) entry.geometry.dispose(); + this.placeholders.clear(); + this.placeholderOf.clear(); + } + + private evict(): void { + const idle = [...this.records.values()].filter((record) => record.refs === 0).sort((a, b) => a.lastUse - b.lastUse); + const perShape = new Map(); + let total = idle.reduce((sum, record) => sum + record.bytes, 0); + for (const record of idle.slice().reverse()) perShape.set(record.shapeId, (perShape.get(record.shapeId) ?? 0) + 1); + for (const record of idle) { + const count = perShape.get(record.shapeId) ?? 0; + if (count <= 2 && total <= 256 * 1024 * 1024) continue; + record.dispose(); + this.records.delete(record.key); + total -= record.bytes; + perShape.set(record.shapeId, count - 1); + } + } +} diff --git a/main/opengeometry-three/src/rendering/records/validate-display-buffers.ts b/main/opengeometry-three/src/rendering/records/validate-display-buffers.ts new file mode 100644 index 0000000..9fa0b3a --- /dev/null +++ b/main/opengeometry-three/src/rendering/records/validate-display-buffers.ts @@ -0,0 +1,28 @@ +import type { DisplayBuffers } from '../../dto/display-buffers'; +import { OGError } from '../../errors'; + +const SCALARS = ['revision', 'bucket', 'achievedDeflection', 'triangles'] as const; + +export function validateDisplayBuffers(value: unknown): DisplayBuffers { + if (!isDisplayBuffers(value) || !hasConsistentLengths(value)) { + throw new OGError('WorkerFailure', 'tessellation', 'invalid display buffer structure'); + } + return value; +} + +function isDisplayBuffers(value: unknown): value is DisplayBuffers { + if (typeof value !== 'object' || value === null) return false; + const field = (key: string): unknown => Reflect.get(value, key); + return field('positions') instanceof Float32Array && field('normals') instanceof Float32Array + && field('indices') instanceof Uint32Array && field('faceRanges') instanceof Uint32Array + && field('outline') instanceof Float32Array && field('edgeIds') instanceof Uint32Array + && field('origin') instanceof Float64Array && SCALARS.every((key) => typeof field(key) === 'number'); +} + +function hasConsistentLengths(buffers: DisplayBuffers): boolean { + return buffers.origin.length === 3 && buffers.positions.length % 3 === 0 + && buffers.normals.length === buffers.positions.length + && buffers.indices.length % 3 === 0 && buffers.indices.length <= 6_000_000 + && buffers.outline.length % 6 === 0 && buffers.outline.length <= 12_000_000 + && buffers.edgeIds.length === buffers.outline.length / 6 && buffers.faceRanges.length % 3 === 0; +} diff --git a/main/opengeometry-three/src/rendering/tessellation-worker.ts b/main/opengeometry-three/src/rendering/tessellation-worker.ts new file mode 100644 index 0000000..aa837bc --- /dev/null +++ b/main/opengeometry-three/src/rendering/tessellation-worker.ts @@ -0,0 +1,106 @@ +import { parseKernelError } from '../kernel/kernel-errors'; +import { initSync, OGTessellator, takePanicMessage } from '../kernel/kernel-loader'; +import { validateDisplayBuffers } from './records/validate-display-buffers'; +import { JobQueue } from './worker/job-queue'; +import { parseWorkerMessage } from './worker/message-guards'; +import { SnapshotCache } from './worker/snapshot-cache'; +import type { TessellateMessage, WorkerError, WorkerMessage, WorkerReply } from './worker-protocol'; + +let tessellator: OGTessellator | undefined; +let scheduled = false; +const SNAPSHOTS = new SnapshotCache(); +const JOBS = new JobQueue(); +const CHANNEL = new MessageChannel(); + +function error(value: unknown): WorkerError { + if (value instanceof WebAssembly.RuntimeError) { + return { code: 'KernelPanic', message: takePanicMessage() ?? value.message, details: {} }; + } + const parsed = typeof value === 'string' ? parseKernelError(value) : undefined; + return parsed ?? { code: 'WorkerFailure', message: String(value), details: {} }; +} + +function reply(message: WorkerReply, transfer: Transferable[] = []): void { + self.postMessage(message, transfer); +} + +function postEvicted(key: string): void { + const at = key.lastIndexOf('@'); + reply({ kind: 'evicted', shapeId: key.slice(0, at), revision: Number(key.slice(at + 1)) }); +} + +function answerSuperseded(request: number | undefined): void { + if (request !== undefined) reply({ request, superseded: true }); +} + +function schedule(): void { + if (scheduled || JOBS.size === 0) return; + scheduled = true; + CHANNEL.port2.postMessage(null); +} + +function run(job: TessellateMessage): void { + const slot = SNAPSHOTS.touch(`${job.shapeId}@${String(job.revision)}`); + if (slot === undefined || !tessellator) { + reply({ request: job.request, missingSnapshot: true }); + return; + } + try { + const buffers = validateDisplayBuffers(tessellator.buffers(slot, job.bucket, job.maxTriangles)); + reply({ request: job.request, ok: true, buffers }, [ + buffers.positions.buffer, buffers.normals.buffer, buffers.indices.buffer, + buffers.faceRanges.buffer, buffers.outline.buffer, buffers.edgeIds.buffer, buffers.origin.buffer, + ]); + } catch (cause) { + reply({ request: job.request, error: error(cause) }); + } +} + +CHANNEL.port1.onmessage = () => { + scheduled = false; + const next = JOBS.next(); + if (!next) return; + run(next); + schedule(); +}; + +function init(module: WebAssembly.Module): void { + initSync({ module }); + tessellator = new OGTessellator(); + reply({ request: 0, ok: true }); +} + +function handle(message: Exclude, loaded: OGTessellator): void { + switch (message.kind) { + case 'snapshot': { + const key = `${message.shapeId}@${String(message.revision)}`; + SNAPSHOTS.load(key, message.bytes, loaded); + for (const evicted of SNAPSHOTS.evict(key, loaded)) postEvicted(evicted); + reply({ request: message.request, ok: true }); + break; + } + case 'tessellate': + answerSuperseded(JOBS.push(message)); + schedule(); + break; + case 'cancel': + answerSuperseded(JOBS.cancel(message.shapeId, message.generation)); + break; + case 'drop': + SNAPSHOTS.drop(`${message.shapeId}@${String(message.revision)}`, loaded); + break; + } +} + +self.onmessage = (event: MessageEvent) => { + const message = parseWorkerMessage(event.data); + const request = 'request' in message ? message.request : undefined; + try { + if ('error' in message) reply(message); + else if (message.kind === 'init') init(message.module); + else if (tessellator) handle(message, tessellator); + else reply({ request, error: { code: 'WorkerFailure', message: 'tessellation worker is not initialised' } }); + } catch (cause) { + reply({ request, error: error(cause) }); + } +}; diff --git a/main/opengeometry-three/src/rendering/worker-backend.ts b/main/opengeometry-three/src/rendering/worker-backend.ts new file mode 100644 index 0000000..44dd96d --- /dev/null +++ b/main/opengeometry-three/src/rendering/worker-backend.ts @@ -0,0 +1,206 @@ +import type { DisplayBuffers } from '../dto/display-buffers'; +import { OGError } from '../errors'; +import type { OGWorldGraph } from '../kernel/kernel-loader'; +import { call } from '../kernel/kernel-session'; +import { Deferred } from './deferred'; +import { InlineBackend } from './inline-backend'; +import type { TessellationProvider, TessellationRequest } from './provider'; +import type { SnapshotMessage, TessellateMessage, WorkerMessage, WorkerReply } from './worker-protocol'; + +export type WorkerSend = { kind: 'tessellate'; shapeId: string; bucket: number; priority: number; generation: number }; + +type SendBody = Omit | Omit; +type PendingSend = { + value: TessellationRequest | undefined; + resolve: (reply: WorkerReply | undefined) => void; + reject: (error: unknown) => void; +}; + +const SEND_LOG_LIMIT = 1_000; + +export class WorkerBackend implements TessellationProvider { + readonly activeBackend = 'worker' as const; + readonly sendLog: WorkerSend[] = []; + private live: Worker | undefined; + private nextRequest = 1; + private pending = new Map(); + private snapshots = new Set(); + private latestGenerations = new Map(); + private readonly ready = new Deferred(); + private restarts = 0; + private failed = false; + private disposed = false; + private fallback: InlineBackend; + + constructor( + private graph: OGWorldGraph, + private module: WebAssembly.Module, + private workerURL: string | URL, + private onFailure: (error: OGError) => void, + ) { + this.fallback = new InlineBackend(graph); + this.boot(); + } + + get worker(): Worker | undefined { + return this.live; + } + + private boot(): void { + try { + const worker = new Worker(this.workerURL, { type: 'module' }); + this.live = worker; + worker.onmessage = (event: MessageEvent) => { this.receive(event.data); }; + worker.onerror = () => { this.crash(); }; + worker.onmessageerror = () => { this.crash(); }; + worker.postMessage({ kind: 'init', request: 0, module: this.module } satisfies WorkerMessage); + } catch { + this.crash(); + } + } + + private crash(): void { + if (this.disposed || this.failed) return; + this.live?.terminate(); + this.live = undefined; + this.snapshots.clear(); + if (this.restarts > 0) { + this.fail(); + return; + } + this.restarts++; + this.drain(); + this.boot(); + } + + private fail(): void { + this.failed = true; + const error = new OGError('WorkerFailure', 'tessellation', 'tessellation worker failed'); + this.ready.reject(error); + this.drain(); + queueMicrotask(() => { if (!this.disposed) this.onFailure(error); }); + } + + private drain(): void { + const entries = [...this.pending.entries()]; + this.pending.clear(); + for (const [request, { value, resolve, reject }] of entries) { + try { + resolve(value ? { request, ok: true, buffers: this.fallback.compute(value) } : undefined); + } catch (error) { + reject(error); + } + } + } + + private receive(reply: WorkerReply): void { + if ('kind' in reply) { + this.snapshots.delete(`${reply.shapeId}@${String(reply.revision)}`); + return; + } + if (reply.request === 0) { + if ('ok' in reply) this.ready.resolve(); + else this.crash(); + return; + } + if (reply.request === undefined) return; + const pending = this.pending.get(reply.request); + if (!pending) return; + this.pending.delete(reply.request); + if ('error' in reply) { + pending.reject(new OGError(reply.error.code, 'tessellation', reply.error.message, reply.error.details)); + if (reply.error.code === 'KernelPanic') this.crash(); + } else pending.resolve(reply); + } + + private async send(message: SendBody, transfer: Transferable[] = []): Promise { + try { + await this.ready.promise; + } catch { + return undefined; + } + const worker = this.live; + if (this.failed || this.disposed || !worker) return undefined; + const request = this.nextRequest++; + const value = message.kind === 'tessellate' ? message : undefined; + if (value) this.recordSend(value); + return new Promise((resolve, reject) => { + this.pending.set(request, { value, resolve, reject }); + worker.postMessage({ ...message, request }, transfer); + }); + } + + private recordSend(value: TessellationRequest): void { + const { shapeId, bucket, priority, generation } = value; + this.sendLog.push({ kind: 'tessellate', shapeId, bucket, priority, generation }); + if (this.sendLog.length > SEND_LOG_LIMIT) this.sendLog.shift(); + } + + async ensureSnapshot(shapeId: string, revision: number): Promise { + const key = `${shapeId}@${String(revision)}`; + if (this.failed || this.snapshots.has(key)) return; + const bytes = call('tessellation', () => this.graph.snapshot(shapeId)); + const reply = await this.send({ kind: 'snapshot', shapeId, revision, bytes }, [bytes.buffer]); + if (!reply) { + if (this.live) await this.ensureSnapshot(shapeId, revision); + return; + } + if (!('ok' in reply)) throw new OGError('WorkerFailure', 'snapshot', 'worker rejected snapshot'); + this.snapshots.add(key); + } + + async request(value: TessellationRequest): Promise { + if (this.failed) return this.fallback.compute(value); + this.latestGenerations.set(value.shapeId, value.generation); + await Promise.resolve(); + if (this.latestGenerations.get(value.shapeId) !== value.generation) { + throw new OGError('Cancelled', 'tessellation', 'tessellation request superseded'); + } + await this.ensureSnapshot(value.shapeId, value.revision); + if (this.latestGenerations.get(value.shapeId) !== value.generation) { + throw new OGError('Cancelled', 'tessellation', 'tessellation request superseded'); + } + let reply = await this.send(tessellate(value)); + if (reply && 'missingSnapshot' in reply) { + this.snapshots.delete(`${value.shapeId}@${String(value.revision)}`); + await this.ensureSnapshot(value.shapeId, value.revision); + reply = await this.send(tessellate(value)); + } + return this.buffersFrom(value, reply); + } + + private buffersFrom(value: TessellationRequest, reply: WorkerReply | undefined): DisplayBuffers { + if (!reply) { + if (this.failed) return this.fallback.compute(value); + throw new OGError('WorkerFailure', 'tessellation', 'runtime disposed'); + } + if ('superseded' in reply) throw new OGError('Cancelled', 'tessellation', 'tessellation request superseded'); + if ('buffers' in reply) return reply.buffers; + throw new OGError('WorkerFailure', 'tessellation', 'worker returned no buffers'); + } + + cancelShape(shapeId: string, generation: number): void { + this.live?.postMessage({ kind: 'cancel', shapeId, generation } satisfies WorkerMessage); + } + + drop(shapeId: string, revision: number): void { + this.snapshots.delete(`${shapeId}@${String(revision)}`); + this.live?.postMessage({ kind: 'drop', shapeId, revision } satisfies WorkerMessage); + } + + dispose(): void { + this.disposed = true; + this.live?.terminate(); + this.live = undefined; + const error = new OGError('WorkerFailure', 'tessellation', 'runtime disposed'); + for (const pending of this.pending.values()) pending.reject(error); + this.pending.clear(); + this.snapshots.clear(); + this.ready.reject(error); + } +} + +function tessellate(value: TessellationRequest): Omit { + const { shapeId, revision, bucket, priority, maxTriangles, generation } = value; + return { kind: 'tessellate', shapeId, revision, bucket, priority, maxTriangles, generation }; +} diff --git a/main/opengeometry-three/src/rendering/worker-protocol.ts b/main/opengeometry-three/src/rendering/worker-protocol.ts new file mode 100644 index 0000000..f0378c3 --- /dev/null +++ b/main/opengeometry-three/src/rendering/worker-protocol.ts @@ -0,0 +1,29 @@ +import type { DisplayBuffers } from '../dto/display-buffers'; +import type { TessellationRequest } from './provider'; + +export type WorkerError = { code: string; message: string; details?: unknown }; + +export type SnapshotMessage = { + kind: 'snapshot'; + request: number; + shapeId: string; + revision: number; + bytes: Uint8Array; +}; + +export type TessellateMessage = TessellationRequest & { kind: 'tessellate'; request: number }; + +export type WorkerMessage = + | { kind: 'init'; request: 0; module: WebAssembly.Module } + | SnapshotMessage + | TessellateMessage + | { kind: 'cancel'; shapeId: string; generation: number } + | { kind: 'drop'; shapeId: string; revision: number }; + +export type WorkerReply = + | { request: number; ok: true } + | { request: number; ok: true; buffers: DisplayBuffers } + | { request: number | undefined; error: WorkerError } + | { request: number; superseded: true } + | { request: number; missingSnapshot: true } + | { kind: 'evicted'; shapeId: string; revision: number }; diff --git a/main/opengeometry-three/src/rendering/worker/job-queue.ts b/main/opengeometry-three/src/rendering/worker/job-queue.ts new file mode 100644 index 0000000..17202da --- /dev/null +++ b/main/opengeometry-three/src/rendering/worker/job-queue.ts @@ -0,0 +1,28 @@ +import type { TessellateMessage } from '../worker-protocol'; + +export class JobQueue { + private readonly jobs = new Map(); + + get size(): number { + return this.jobs.size; + } + + push(job: TessellateMessage): number | undefined { + const previous = this.jobs.get(job.shapeId); + this.jobs.set(job.shapeId, job); + return previous?.request; + } + + cancel(shapeId: string, generation: number): number | undefined { + const previous = this.jobs.get(shapeId); + if (!previous || previous.generation > generation) return undefined; + this.jobs.delete(shapeId); + return previous.request; + } + + next(): TessellateMessage | undefined { + const next = [...this.jobs.values()].sort((a, b) => a.priority - b.priority)[0]; + if (next) this.jobs.delete(next.shapeId); + return next; + } +} diff --git a/main/opengeometry-three/src/rendering/worker/message-guards.ts b/main/opengeometry-three/src/rendering/worker/message-guards.ts new file mode 100644 index 0000000..7f2ef89 --- /dev/null +++ b/main/opengeometry-three/src/rendering/worker/message-guards.ts @@ -0,0 +1,67 @@ +import type { WorkerMessage } from '../worker-protocol'; + +export type GuardFailure = { request: number | undefined; error: { code: 'InvalidParameter'; message: string } }; + +type Check = (value: unknown) => boolean; + +const KINDS = ['init', 'snapshot', 'tessellate', 'cancel', 'drop'] as const; + +export function parseWorkerMessage(data: unknown): WorkerMessage | GuardFailure { + if (isWorkerMessage(data)) return data; + const message = problemOf(data) ?? 'invalid worker message'; + return { request: requestOf(data), error: { code: 'InvalidParameter', message } }; +} + +function isWorkerMessage(data: unknown): data is WorkerMessage { + return problemOf(data) === undefined; +} + +function problemOf(data: unknown): string | undefined { + if (typeof data !== 'object' || data === null) return 'worker message is not an object'; + const kind: unknown = Reflect.get(data, 'kind'); + const known = KINDS.find((name) => name === kind); + if (known === undefined) return `worker message field kind has unknown value ${String(kind)}`; + const checks = fieldChecks(known); + const extra = Object.keys(data).find((key) => !Object.hasOwn(checks, key)); + if (extra !== undefined) return `unknown worker message field ${extra}`; + const invalid = Object.entries(checks).find(([key, check]) => !check(Reflect.get(data, key))); + return invalid === undefined ? undefined : `worker message field ${invalid[0]} is missing or invalid`; +} + +function fieldChecks(kind: (typeof KINDS)[number]): Record { + switch (kind) { + case 'init': + return { kind: isKind, request: (value) => value === 0, module: (value) => value instanceof WebAssembly.Module }; + case 'snapshot': + return { + kind: isKind, request: isFiniteNumber, shapeId: isShapeId, revision: isFiniteNumber, + bytes: (value) => value instanceof Uint8Array, + }; + case 'tessellate': + return { + kind: isKind, request: isFiniteNumber, shapeId: isShapeId, revision: isFiniteNumber, bucket: isFiniteNumber, + priority: isFiniteNumber, maxTriangles: isFiniteNumber, generation: isFiniteNumber, + }; + case 'cancel': + return { kind: isKind, shapeId: isShapeId, generation: isFiniteNumber }; + case 'drop': + return { kind: isKind, shapeId: isShapeId, revision: isFiniteNumber }; + } +} + +function requestOf(data: unknown): number | undefined { + const request: unknown = typeof data === 'object' && data !== null ? Reflect.get(data, 'request') : undefined; + return isFiniteNumber(request) ? request : undefined; +} + +function isKind(): boolean { + return true; +} + +function isFiniteNumber(value: unknown): value is number { + return typeof value === 'number' && Number.isFinite(value); +} + +function isShapeId(value: unknown): boolean { + return typeof value === 'string' && value.length > 0; +} diff --git a/main/opengeometry-three/src/rendering/worker/snapshot-cache.ts b/main/opengeometry-three/src/rendering/worker/snapshot-cache.ts new file mode 100644 index 0000000..5c4f7ba --- /dev/null +++ b/main/opengeometry-three/src/rendering/worker/snapshot-cache.ts @@ -0,0 +1,44 @@ +import type { OGTessellator } from '../../kernel/kernel-loader'; + +type Slot = { slot: number; bytes: number; lastUse: number }; + +export class SnapshotCache { + private readonly entries = new Map(); + private totalBytes = 0; + private clock = 0; + + load(key: string, bytes: Uint8Array, tessellator: OGTessellator): void { + this.drop(key, tessellator); + const slot = tessellator.load(bytes); + this.entries.set(key, { slot, bytes: bytes.byteLength, lastUse: ++this.clock }); + this.totalBytes += bytes.byteLength; + } + + drop(key: string, tessellator: OGTessellator): void { + const old = this.entries.get(key); + if (!old) return; + tessellator.drop(old.slot); + this.entries.delete(key); + this.totalBytes -= old.bytes; + } + + touch(key: string): number | undefined { + const entry = this.entries.get(key); + if (!entry) return undefined; + entry.lastUse = ++this.clock; + return entry.slot; + } + + evict(except: string, tessellator: OGTessellator): string[] { + const evicted: string[] = []; + while (this.totalBytes > 128 * 1024 * 1024) { + const oldest = [...this.entries.entries()] + .filter(([key]) => key !== except) + .sort((a, b) => a[1].lastUse - b[1].lastUse)[0]; + if (!oldest) break; + this.drop(oldest[0], tessellator); + evicted.push(oldest[0]); + } + return evicted; + } +} diff --git a/main/opengeometry-three/src/runtime/create-runtime.ts b/main/opengeometry-three/src/runtime/create-runtime.ts new file mode 100644 index 0000000..4d62ff1 --- /dev/null +++ b/main/opengeometry-three/src/runtime/create-runtime.ts @@ -0,0 +1,62 @@ +import type { Events, Listener } from '../dto/events'; +import type { OGError } from '../errors'; +import { compileKernel, initKernel, OGWorldGraph } from '../kernel/kernel-loader'; +import { ACCURACY } from '../limits'; +import { refreshDisplayed } from '../rendering/geometry/render-pass'; +import { InlineBackend } from '../rendering/inline-backend'; +import { createLodState } from '../rendering/lod/lod-controller'; +import { RecordPool } from '../rendering/records/record-pool'; +import { WorkerBackend } from '../rendering/worker-backend'; +import { encode } from '../world-graph/codec'; +import { emit } from './event-bus'; +import { + nextEpoch, pendingCreate, setPendingCreate, setRuntime, type CreateOptions, type Runtime, +} from './runtime-state'; + +export type KernelInput = { wasmURL?: string | URL; wasmModule?: WebAssembly.Module }; + +export function createRuntimeOnce(input: KernelInput, options: CreateOptions): Promise { + const pending = pendingCreate(); + if (pending) return pending; + const created = loadRuntime(input, options).finally(() => { setPendingCreate(undefined); }); + setPendingCreate(created); + return created; +} + +async function loadRuntime(input: KernelInput, options: CreateOptions): Promise { + const wasmURL = input.wasmURL ?? new URL('./opengeometry_bg.wasm', import.meta.url); + const module = input.wasmModule ?? await compileKernel(wasmURL); + await initKernel(module); + const state = createRuntime(module, options); + setRuntime(state); + return state; +} + +export function createRuntime(module: WebAssembly.Module, options: CreateOptions): Runtime { + const graph = new OGWorldGraph(encode(ACCURACY)); + const listeners = new Map>(); + const provider = options.tessellation === 'inline' || typeof Worker === 'undefined' + ? new InlineBackend(graph) + : new WorkerBackend( + graph, + module, + options.workerURL ?? new URL('./tessellation-worker.js', import.meta.url), + (error) => { fallBack(state, error); }, + ); + const state: Runtime = { + graph, provider, records: new RecordPool(), bodies: new Map(), pickTargets: new WeakMap(), byShape: new Map(), + limbo: new Map(), listeners, + revision: 0n, readyVersion: 0, flushedReadyVersion: -1, renderPassActive: false, pass: 0, displayed: new Map(), + pending: new Map(), generations: new Map(), shapeBuckets: new Map(), + lod: createLodState(() => { refreshDisplayed(state); }), module, createOptions: options, poisoned: false, + flushes: 0, marks: new Map(), flushing: false, surfacePool: new Map(), linePool: new Map(), epoch: nextEpoch(), + }; + return state; +} + +function fallBack(state: Runtime, error: OGError): void { + const worker = state.provider; + state.provider = new InlineBackend(state.graph); + worker.dispose(); + emit('error', error); +} diff --git a/main/opengeometry-three/src/runtime/event-bus.ts b/main/opengeometry-three/src/runtime/event-bus.ts new file mode 100644 index 0000000..b45eadf --- /dev/null +++ b/main/opengeometry-three/src/runtime/event-bus.ts @@ -0,0 +1,14 @@ +import type { Events, Listener } from '../dto/events'; +import { currentRuntime, runtime } from './runtime-state'; + +export function on(event: Events, handler: Listener): () => boolean { + const listeners = runtime().listeners; + const group = listeners.get(event) ?? new Set(); + group.add(handler); + listeners.set(event, group); + return () => group.delete(handler); +} + +export function emit(event: Events, detail: unknown): void { + for (const handler of currentRuntime()?.listeners.get(event) ?? []) handler(detail); +} diff --git a/main/opengeometry-three/src/runtime/reset.ts b/main/opengeometry-three/src/runtime/reset.ts new file mode 100644 index 0000000..cc146aa --- /dev/null +++ b/main/opengeometry-three/src/runtime/reset.ts @@ -0,0 +1,18 @@ +import { createRuntime } from './create-runtime'; +import { currentRuntime, setRuntime } from './runtime-state'; + +export function reset(): void { + const state = currentRuntime(); + if (!state) return; + for (const body of [...state.bodies.values()]) { + body.visible = false; + body.finalize(); + } + state.provider.dispose(); + state.records.clear(); + for (const entry of [...state.surfacePool.values(), ...state.linePool.values()]) entry.material.dispose(); + state.surfacePool.clear(); + state.linePool.clear(); + state.graph.free(); + setRuntime(createRuntime(state.module, state.createOptions)); +} diff --git a/main/opengeometry-three/src/runtime/runtime-state.ts b/main/opengeometry-three/src/runtime/runtime-state.ts new file mode 100644 index 0000000..4e6a42d --- /dev/null +++ b/main/opengeometry-three/src/runtime/runtime-state.ts @@ -0,0 +1,104 @@ +import type { Body } from '../bodies/body'; +import type { Events, Listener } from '../dto/events'; +import { OGError } from '../errors'; +import type { OGWorldGraph } from '../kernel/kernel-loader'; +import type { OGMark } from '../marks/og-mark'; +import type { LodState } from '../rendering/lod/lod-controller'; +import type { LineEntry, SurfaceEntry } from '../rendering/materials/material-pool'; +import type { TessellationProvider } from '../rendering/provider'; +import type { RecordPool } from '../rendering/records/record-pool'; + +export type CreateOptions = { workerURL?: string | URL; tessellation?: 'worker' | 'inline' }; + +export type LimboEntry = { watermark: number; parent: Body['parent'] }; + +export type Runtime = { + graph: OGWorldGraph; + provider: TessellationProvider; + records: RecordPool; + bodies: Map; + pickTargets: WeakMap; + byShape: Map>; + limbo: Map; + listeners: Map>; + revision: bigint; + readyVersion: number; + flushedReadyVersion: number; + renderPassActive: boolean; + pass: number; + displayed: Map; + pending: Map>; + generations: Map; + shapeBuckets: Map; + lod: LodState; + module: WebAssembly.Module; + createOptions: CreateOptions; + displayDeflection?: number; + poisoned: boolean; + flushes: number; + marks: Map; + flushing: boolean; + surfacePool: Map; + linePool: Map; + epoch: number; +}; + +let state: Runtime | undefined; +let creating: Promise | undefined; +let epochs = 0; + +export function runtime(): Runtime { + if (!state) throw new OGError('NotInitialised', 'OpenGeometry', 'call OpenGeometry.create() first'); + return state; +} + +export function currentRuntime(): Runtime | undefined { + return state; +} + +export function setRuntime(value: Runtime | undefined): void { + state = value; +} + +export function pendingCreate(): Promise | undefined { + return creating; +} + +export function setPendingCreate(value: Promise | undefined): void { + creating = value; +} + +export function nextEpoch(): number { + return ++epochs; +} + +export function bodyKey(handle: number, generation: number): string { + return `${String(handle)}:${String(generation)}`; +} + +export function reindexShape(state: Runtime, body: Body, previousShapeId: string | null): void { + const shapeId = body.lastInfo.shapeId; + if (shapeId === previousShapeId) return; + if (previousShapeId) { + const previous = state.byShape.get(previousShapeId); + previous?.delete(body); + if (previous?.size === 0) { + state.byShape.delete(previousShapeId); + forgetShape(state, previousShapeId); + } + } + if (shapeId) state.byShape.set(shapeId, (state.byShape.get(shapeId) ?? new Set()).add(body)); +} + +export function releaseShapeIfEmpty(state: Runtime, shapeId: string, revision: number): void { + if (state.byShape.get(shapeId)?.size) return; + state.byShape.delete(shapeId); + forgetShape(state, shapeId); + state.records.purge(shapeId); + state.provider.drop(shapeId, revision); +} + +function forgetShape(state: Runtime, shapeId: string): void { + state.lod.shapes.delete(shapeId); + state.shapeBuckets.delete(shapeId); +} diff --git a/main/opengeometry-three/src/shapes/analytic-pattern.ts b/main/opengeometry-three/src/shapes/analytic-pattern.ts deleted file mode 100644 index f694f14..0000000 --- a/main/opengeometry-three/src/shapes/analytic-pattern.ts +++ /dev/null @@ -1,173 +0,0 @@ -import * as THREE from "three"; -import { AnalyticSolid } from "./analytic-solid"; - -export interface AnalyticPatternInstance { - id?: string; - matrix: THREE.Matrix4; -} - -export interface AnalyticPatternHit { - pattern: AnalyticPattern; - instanceIndex: number; - instanceId: string; - source: AnalyticSolid; - faceId?: number; -} - -function finiteMatrix(matrix: THREE.Matrix4): boolean { - return matrix.elements.every(Number.isFinite); -} - -/** - * Renders many rigid placements of one authoritative analytic body. The source - * owns the BRep; instances own only identity and placement matrices. - */ -export class AnalyticPattern extends THREE.Group { - readonly source: AnalyticSolid; - readonly surface: THREE.InstancedMesh; - readonly instanceIds: readonly string[]; - private disposed = false; - - constructor(source: AnalyticSolid, instances: readonly AnalyticPatternInstance[]) { - super(); - if (!(source instanceof AnalyticSolid) || source.isDisposed) { - throw new Error("Analytic patterns require an active AnalyticSolid source"); - } - if (instances.length === 0 || instances.length > 100_000) { - throw new Error("Analytic patterns require 1–100,000 instances"); - } - if (instances.some(({ matrix }) => !(matrix instanceof THREE.Matrix4) || !finiteMatrix(matrix))) { - throw new Error("Analytic pattern placements must be finite Matrix4 values"); - } - const ids = instances.map((instance, index) => instance.id ?? `${source.ogid}:instance-${index}`); - if (ids.some((id) => !id) || new Set(ids).size !== ids.length) { - throw new Error("Analytic pattern instance identities must be nonempty and unique"); - } - this.source = source; - this.instanceIds = ids; - this.surface = new THREE.InstancedMesh( - source.surface.geometry, - source.surface.material, - instances.length, - ); - this.surface.name = `${source.name || source.ogid} pattern`; - this.surface.userData.ogid = source.ogid; - this.surface.userData.analyticPattern = this; - instances.forEach((instance, index) => this.surface.setMatrixAt(index, instance.matrix)); - this.surface.instanceMatrix.needsUpdate = true; - this.add(this.surface); - } - - get count(): number { return this.instanceIds.length; } - - setInstanceMatrix(index: number, matrix: THREE.Matrix4): void { - this.checkActive(); - if (!Number.isInteger(index) || index < 0 || index >= this.count) { - throw new Error("Analytic pattern instance index is outside the pattern"); - } - if (!(matrix instanceof THREE.Matrix4) || !finiteMatrix(matrix)) { - throw new Error("Analytic pattern placement must be a finite Matrix4"); - } - this.surface.setMatrixAt(index, matrix); - this.surface.instanceMatrix.needsUpdate = true; - this.surface.computeBoundingBox(); - this.surface.computeBoundingSphere(); - } - - resolveIntersection(intersection: THREE.Intersection): AnalyticPatternHit | undefined { - if (intersection.object !== this.surface || intersection.instanceId === undefined) return undefined; - const instanceIndex = intersection.instanceId; - if (instanceIndex < 0 || instanceIndex >= this.count) return undefined; - const triangleIndex = intersection.faceIndex; - const faceId = triangleIndex === undefined ? undefined : this.source.faceIdForTriangle(triangleIndex); - return { - pattern: this, - instanceIndex, - instanceId: this.instanceIds[instanceIndex], - source: this.source, - faceId, - }; - } - - dispose(): void { - if (this.disposed) return; - this.disposed = true; - this.remove(this.surface); - this.surface.dispose(); - } - - private checkActive(): void { - if (this.disposed) throw new Error("Analytic pattern has been disposed"); - if (this.source.isDisposed) throw new Error("Analytic pattern source has been disposed"); - } -} - -export function linearPattern( - source: AnalyticSolid, - count: number, - spacing: THREE.Vector3, -): AnalyticPattern { - if (!Number.isInteger(count) || count < 1 || count > 100_000) { - throw new Error("Linear pattern count must be an integer from 1 to 100,000"); - } - if (!(spacing instanceof THREE.Vector3) || ![spacing.x, spacing.y, spacing.z].every(Number.isFinite)) { - throw new Error("Linear pattern spacing must be a finite Vector3"); - } - return new AnalyticPattern(source, Array.from({ length: count }, (_, index) => ({ - matrix: new THREE.Matrix4().makeTranslation(spacing.x * index, spacing.y * index, spacing.z * index), - }))); -} - -export function rectangularPattern( - source: AnalyticSolid, - counts: readonly [number, number], - spacing: readonly [THREE.Vector3, THREE.Vector3], -): AnalyticPattern { - if (counts.some((count) => !Number.isInteger(count) || count < 1) || counts[0] * counts[1] > 100_000) { - throw new Error("Rectangular pattern counts must be positive integers with at most 100,000 instances"); - } - if (spacing.some((vector) => !(vector instanceof THREE.Vector3) - || ![vector.x, vector.y, vector.z].every(Number.isFinite))) { - throw new Error("Rectangular pattern spacing vectors must be finite"); - } - const instances: AnalyticPatternInstance[] = []; - for (let row = 0; row < counts[1]; row += 1) { - for (let column = 0; column < counts[0]; column += 1) { - instances.push({ - id: `${source.ogid}:instance-${column}-${row}`, - matrix: new THREE.Matrix4().makeTranslation( - spacing[0].x * column + spacing[1].x * row, - spacing[0].y * column + spacing[1].y * row, - spacing[0].z * column + spacing[1].z * row, - ), - }); - } - } - return new AnalyticPattern(source, instances); -} - -export function circularPattern( - source: AnalyticSolid, - count: number, - radius: number, - options: { startAngle?: number; sweepAngle?: number; rotateInstances?: boolean } = {}, -): AnalyticPattern { - if (!Number.isInteger(count) || count < 1 || count > 100_000) { - throw new Error("Circular pattern count must be an integer from 1 to 100,000"); - } - if (!Number.isFinite(radius) || radius < 0) throw new Error("Circular pattern radius must be finite and nonnegative"); - const start = options.startAngle ?? 0; - const sweep = options.sweepAngle ?? Math.PI * 2; - if (![start, sweep].every(Number.isFinite) || sweep === 0) { - throw new Error("Circular pattern angles must be finite with a nonzero sweep"); - } - const closed = Math.abs(Math.abs(sweep) - Math.PI * 2) <= 64 * Number.EPSILON; - const denominator = closed ? count : Math.max(1, count - 1); - const instances = Array.from({ length: count }, (_, index) => { - const angle = start + sweep * index / denominator; - const matrix = new THREE.Matrix4().makeTranslation(radius * Math.cos(angle), 0, radius * Math.sin(angle)); - if (options.rotateInstances ?? true) matrix.multiply(new THREE.Matrix4().makeRotationY(-angle)); - return { id: `${source.ogid}:instance-${index}`, matrix }; - }); - return new AnalyticPattern(source, instances); -} diff --git a/main/opengeometry-three/src/shapes/analytic-solid.ts b/main/opengeometry-three/src/shapes/analytic-solid.ts deleted file mode 100644 index def85c1..0000000 --- a/main/opengeometry-three/src/shapes/analytic-solid.ts +++ /dev/null @@ -1,760 +0,0 @@ -import * as THREE from "three"; -import { OGAnalyticBrep, tessellate_brep } from "../../../opengeometry/pkg/opengeometry"; -import { getUUID } from "../utils/randomizer"; -import { GEOMETRY_DEFLECTION_SHARE, type AnalyticTessellationData } from "../rendering/analytic-tessellation"; -import { matrixToWorldTransform } from "../world/world-graph"; -import { analyticIfcText, AnalyticExchangeBodyV2 } from "../export/analytic-ifc"; -import { parseAnalyticGeometryError } from "../operations/analytic-errors"; - -export interface AnalyticFrame { - origin: [number, number, number]; - x: [number, number, number]; - y: [number, number, number]; - z: [number, number, number]; -} - -export interface AnalyticAccuracy { - geometric: number; - intersection: number; - tessellation: number; - exchange: number; -} - -export interface AnalyticAnnularSectorOpening { - id: string; - angle: number; - width: number; - bottom: number; - height: number; -} - -export type AnalyticProfileEdge = - | { kind: "line"; from: [number, number]; to: [number, number] } - | { kind: "arc"; center: [number, number]; radius: number; startAngle: number; sweepAngle: number }; - -export type AnalyticPolygonLoftAlignment = "auto" | { - upperStartIndex: number; - reverseUpper?: boolean; -}; - -export interface AnalyticProjectionCamera { - position: { x: number; y: number; z: number }; - target: { x: number; y: number; z: number }; - up: { x: number; y: number; z: number }; - near: number; - projection_mode: "Orthographic" | "Perspective"; -} - -export interface AnalyticProjectionHlr { - hide_hidden_edges: boolean; -} - -interface AnalyticOptions { - ogid?: string; - frame?: AnalyticFrame; - accuracy?: AnalyticAccuracy; - deflection?: number; - color?: THREE.ColorRepresentation; -} - -export type AnalyticPrimitiveOptions = AnalyticOptions & ( - | { kind: "cuboid"; width: number; depth: number; height: number } - | { kind: "linearExtrusion"; outer: [number, number][]; holes: [number, number][][]; height: number } - | { kind: "arcEdgedExtrusion"; outer: AnalyticProfileEdge[]; holes?: AnalyticProfileEdge[][]; height: number } - | { kind: "polygonLoft"; lower: [number, number, number][]; upper: [number, number, number][]; alignment?: AnalyticPolygonLoftAlignment } - | { kind: "boxWithArchedOpening"; width: number; depth: number; height: number; opening: { id: string; station: number; width: number; bottom: number; height: number } } - | { kind: "planarPolyhedron"; vertices: [number, number, number][]; faces: number[][] } - | { kind: "cylinder" | "cone"; radius: number; height: number } - | { kind: "cylinderSector"; radius: number; height: number; startAngle: number; sweepAngle: number } - | { kind: "sphere"; radius: number } - | { kind: "frustum"; lowerRadius: number; upperRadius: number; height: number } - | { kind: "torus"; majorRadius: number; minorRadius: number } - | { kind: "annularSectorExtrusion"; radius: number; thickness: number; height: number; startAngle: number; sweepAngle: number } - | { kind: "annularSectorExtrusionWithOpenings"; radius: number; thickness: number; height: number; startAngle: number; sweepAngle: number; openings: AnalyticAnnularSectorOpening[] } - | { kind: "annularSectorExtrusionWithArchedOpening"; radius: number; thickness: number; height: number; startAngle: number; sweepAngle: number; opening: AnalyticAnnularSectorOpening } - | { kind: "coaxialCircleLoft"; lowerRadius: number; upperRadius: number; height: number } - | { kind: "revolvedRectangle"; innerRadius: number; outerRadius: number; height: number } - | { kind: "chamferedCuboid"; width: number; depth: number; height: number; chamfer: number } - | { kind: "filletedCylinder"; radius: number; height: number; filletRadius: number } -); - -export type AnalyticBooleanOp = "union" | "intersection" | "subtraction"; -export type AnalyticPointClassification = "inside" | "outside" | "boundary" | "unknown"; - -export interface AnalyticBooleanReport { - operation: AnalyticBooleanOp; - quality: { kind: "Analytic" }; - contacts: [number, number, number][]; - coincident: boolean; - face_mappings: { - source: { entity: string; body: string; key: string; face: number }; - result_faces: number[]; - }[]; -} - -export interface AnalyticBrepOptions extends Omit { - kind: "brep"; - serialized: string; -} - -export interface AnalyticTessellationStats { - requestedDeflection: number; - achievedDeflection: number; - triangleCount: number; - faceCount: number; - revision: bigint; -} - -export interface AnalyticTessellationOptions { - maxTriangles?: number; -} - -export interface AnalyticStepExport { - text: string; - report: { - schema_version: 2; - revision: string; - length_unit: "metre" | "millimetre"; - quality: { kind: string }; - faces: number; - edges: number; - solids: number; - cavity_shells: number; - collapsed_chart_uses: number; - geometric_tolerance: number; - exchange_error_bound: number; - validation_level: string; - }; -} - -export interface AnalyticStlExport { - bytes: Uint8Array; - report: { - schema_version: 2; - revision: string; - triangle_count: number; - requested_deflection: number; - achieved_deflection: number; - float32_encoding_error: number; - quality: "tessellated_from_analytic_brep"; - }; -} - -export class AnalyticSolid extends THREE.Group { - readonly ogid: string; - readonly surface: THREE.Mesh; - readonly featureOutline: THREE.LineSegments; - private kernel: OGAnalyticBrep; - private triangleFaces = new Uint32Array(0); - private outlineEdges = new Uint32Array(0); - private disposed = false; - private lastDeflection = 0.001; - private booleanReport?: AnalyticBooleanReport; - - get report(): AnalyticBooleanReport | undefined { return this.booleanReport; } - get isDisposed(): boolean { return this.disposed; } - get modelRevision(): bigint { this.checkActive(); return this.kernel.get_revision(); } - get deflection(): number { return this.lastDeflection; } - get color(): THREE.Color { return this.surface.material.color; } - set color(value: THREE.ColorRepresentation) { - this.checkActive(); - this.surface.material.color.set(value); - this.surface.material.needsUpdate = true; - } - get outline(): boolean { return this.featureOutline.visible; } - set outline(value: boolean) { - this.checkActive(); - this.featureOutline.visible = value; - this.surface.material.polygonOffset = value; - this.surface.material.polygonOffsetFactor = 1; - this.surface.material.polygonOffsetUnits = 1; - } - - constructor(options: AnalyticPrimitiveOptions | AnalyticBrepOptions) { - super(); - const id = options.kind === "brep" ? "" : options.ogid ?? getUUID(); - this.kernel = options.kind === "brep" - ? new OGAnalyticBrep(options.serialized) - : createPrimitiveKernel(options, id); - this.ogid = this.kernel.get_id(); - this.surface = new THREE.Mesh( - new THREE.BufferGeometry(), - new THREE.MeshStandardMaterial({ color: options.color ?? 0x6699dd }), - ); - this.surface.userData.ogid = this.ogid; - this.featureOutline = new THREE.LineSegments(new THREE.BufferGeometry(), new THREE.LineBasicMaterial({ color: 0x243040 })); - this.featureOutline.visible = false; - this.featureOutline.userData.ogid = this.ogid; - this.add(this.surface, this.featureOutline); - try { - this.retessellate(options.deflection ?? (options.kind === "brep" ? 0.001 : options.accuracy?.tessellation ?? 0.001)); - } catch (error) { - this.dispose(); - throw error; - } - } - - static fromBrep(serialized: string, options: Omit = {}): AnalyticSolid { - return new AnalyticSolid({ ...options, kind: "brep", serialized }); - } - - boolean(other: AnalyticSolid, operation: AnalyticBooleanOp, options: { deflection?: number; color?: THREE.ColorRepresentation } = {}): AnalyticSolid { - this.checkActive(); - other.checkActive(); - const host = this.worldKernel(); - let cutter: OGAnalyticBrep | undefined; - let result: OGAnalyticBrep | undefined; - try { - cutter = other.worldKernel(); - result = host.boolean_brep(cutter, operation, getUUID()); - const report = result.get_boolean_report_serialized(); - const solid = AnalyticSolid.fromBrep(result.get_brep_serialized(), { - deflection: options.deflection ?? this.lastDeflection, - color: options.color ?? this.surface.material.color, - }); - solid.booleanReport = report ? JSON.parse(report) as AnalyticBooleanReport : undefined; - solid.name = this.name; - solid.userData = { ...this.userData }; - solid.outline = this.outline; - return solid; - } catch (error) { - throw parseAnalyticGeometryError(error); - } finally { - result?.free(); - cutter?.free(); - host.free(); - } - } - - union(other: AnalyticSolid, options: { deflection?: number; color?: THREE.ColorRepresentation } = {}): AnalyticSolid { - return this.boolean(other, "union", options); - } - - intersection(other: AnalyticSolid, options: { deflection?: number; color?: THREE.ColorRepresentation } = {}): AnalyticSolid { - return this.boolean(other, "intersection", options); - } - - subtract(cutters: AnalyticSolid[], options: { deflection?: number; color?: THREE.ColorRepresentation } = {}): AnalyticSolid { - if (!Array.isArray(cutters) || cutters.length === 0 || cutters.some((cutter) => !(cutter instanceof AnalyticSolid))) { - throw new Error("Analytic subtraction requires a nonempty AnalyticSolid array"); - } - if (cutters.length > 1) { - const host = this.worldKernel(); - const cutterKernels: OGAnalyticBrep[] = []; - let result: OGAnalyticBrep | undefined; - try { - for (const cutter of cutters) cutterKernels.push(cutter.worldKernel()); - const payload = `[${cutterKernels.map((cutter) => cutter.get_brep_serialized()).join(",")}]`; - result = host.subtract_planar_cutters(payload, getUUID()); - const report = result.get_boolean_report_serialized(); - const solid = AnalyticSolid.fromBrep(result.get_brep_serialized(), { - deflection: options.deflection ?? this.lastDeflection, - color: options.color ?? this.surface.material.color, - }); - solid.booleanReport = report ? JSON.parse(report) as AnalyticBooleanReport : undefined; - solid.name = this.name; - solid.userData = { ...this.userData }; - solid.outline = this.outline; - return solid; - } catch (error) { - const parsed = parseAnalyticGeometryError(error); - // The native mixed-cutter path already chose a safe cutter order and - // checked spatial overlap. A serial fallback may drop an earlier - // cylindrical void or accept an invalid arched profile. - if (parsed.code !== "coverage_gap" - || parsed.families?.[0]?.includes("mixed cutter batch")) throw parsed; - } finally { - result?.free(); - cutterKernels.forEach((cutter) => cutter.free()); - host.free(); - } - } - let result: AnalyticSolid | undefined; - try { - for (const cutter of cutters) { - const next = (result ?? this).boolean(cutter, "subtraction", options); - result?.dispose(); - result = next; - } - return result!; - } catch (error) { - result?.dispose(); - throw error; - } - } - - shell(thickness: number, options: { deflection?: number; color?: THREE.ColorRepresentation } = {}): AnalyticSolid { - this.checkActive(); - if (!Number.isFinite(thickness) || thickness <= 0) throw new Error("Shell thickness must be finite and positive"); - const host = this.worldKernel(); - let result: OGAnalyticBrep | undefined; - try { - result = host.shell(thickness, getUUID()); - const report = result.get_boolean_report_serialized(); - const solid = AnalyticSolid.fromBrep(result.get_brep_serialized(), { - deflection: options.deflection ?? this.lastDeflection, - color: options.color ?? this.surface.material.color, - }); - solid.booleanReport = report ? JSON.parse(report) as AnalyticBooleanReport : undefined; - solid.name = this.name; - solid.userData = { ...this.userData }; - solid.outline = this.outline; - return solid; - } catch (error) { - throw parseAnalyticGeometryError(error); - } finally { - result?.free(); - host.free(); - } - } - - getBrepSerialized(): string { - this.checkActive(); - return this.kernel.get_brep_serialized(); - } - - getBrepData(): Record { - return JSON.parse(this.getBrepSerialized()) as Record; - } - - /** Rebuilds authored analytic geometry while preserving the wrapper and placement. */ - protected replaceDefinition(options: AnalyticPrimitiveOptions): AnalyticTessellationStats { - this.checkActive(); - const next = createPrimitiveKernel(options, this.ogid); - const previous = this.kernel; - this.kernel = next; - try { - const stats = this.retessellate(options.deflection ?? this.lastDeflection); - previous.free(); - this.booleanReport = undefined; - return stats; - } catch (error) { - this.kernel = previous; - next.free(); - throw error; - } - } - - getWorldBrepSerialized(): string { - const placed = this.worldKernel(); - try { return placed.get_brep_serialized(); } - finally { placed.free(); } - } - - prepareIfcExchange(): AnalyticExchangeBodyV2 { - const placed = this.worldKernel(); - try { return JSON.parse(placed.prepare_ifc_exchange()) as AnalyticExchangeBodyV2; } - finally { placed.free(); } - } - - exportIfc(): { text: string; report: AnalyticExchangeBodyV2 } { - const report = this.prepareIfcExchange(); - return { text: analyticIfcText(report, this.ogid), report }; - } - - /** Exports world placement and authored metre geometry, converted once to the selected STEP unit. */ - exportStep(lengthUnit: "metre" | "millimetre" = "metre"): AnalyticStepExport { - const placed = this.worldKernel(); - try { return JSON.parse(placed.export_step(lengthUnit)) as AnalyticStepExport; } - finally { placed.free(); } - } - - /** Exports a binary STL tessellated from the placed analytic BRep at an explicit error budget. */ - exportStl(deflection = this.lastDeflection): AnalyticStlExport { - if (!Number.isFinite(deflection) || deflection <= 0) { - throw new Error("STL deflection must be finite and positive"); - } - const placed = this.worldKernel(); - const result = tessellate_brep( - placed.get_brep_serialized(), - deflection * GEOMETRY_DEFLECTION_SHARE, - ); - try { - const positions = result.positions(); - const indices = result.indices(); - const triangleCount = indices.length / 3; - if (!Number.isSafeInteger(triangleCount) || triangleCount > 0xffffffff) { - throw new Error("STL triangle count exceeds the binary format limit"); - } - const bytes = new Uint8Array(84 + triangleCount * 50); - const header = "OpenGeometry BRep v2 analytic tessellation"; - for (let index = 0; index < Math.min(header.length, 80); index += 1) { - bytes[index] = header.charCodeAt(index); - } - const view = new DataView(bytes.buffer); - view.setUint32(80, triangleCount, true); - let encodingError = 0; - const writeFloat = (offset: number, value: number): void => { - view.setFloat32(offset, value, true); - encodingError = Math.max(encodingError, Math.abs(value - view.getFloat32(offset, true))); - }; - for (let triangle = 0; triangle < triangleCount; triangle += 1) { - const ids = [indices[triangle * 3], indices[triangle * 3 + 1], indices[triangle * 3 + 2]]; - const points = ids.map((id) => [ - positions[id * 3], positions[id * 3 + 1], positions[id * 3 + 2], - ] as [number, number, number]); - const ab = points[1].map((value, axis) => value - points[0][axis]); - const ac = points[2].map((value, axis) => value - points[0][axis]); - const cross = [ - ab[1] * ac[2] - ab[2] * ac[1], - ab[2] * ac[0] - ab[0] * ac[2], - ab[0] * ac[1] - ab[1] * ac[0], - ]; - const length = Math.hypot(...cross); - if (!Number.isFinite(length) || length === 0) { - throw new Error("Analytic tessellation produced a degenerate STL triangle"); - } - const offset = 84 + triangle * 50; - cross.forEach((value, axis) => writeFloat(offset + axis * 4, value / length)); - points.forEach((point, vertex) => point.forEach((value, axis) => { - writeFloat(offset + 12 + (vertex * 3 + axis) * 4, value); - })); - view.setUint16(offset + 48, 0, true); - } - const achievedDeflection = result.achieved_deflection() + encodingError; - if (achievedDeflection > deflection) { - throw new Error("Binary STL precision cannot represent the requested deflection"); - } - return { - bytes, - report: { - schema_version: 2, - revision: result.revision().toString(), - triangle_count: triangleCount, - requested_deflection: deflection, - achieved_deflection: achievedDeflection, - float32_encoding_error: encodingError, - quality: "tessellated_from_analytic_brep", - }, - }; - } finally { - result.free(); - placed.free(); - } - } - - faceIdForTriangle(triangle: number): number | undefined { - if (!Number.isInteger(triangle) || triangle < 0) return undefined; - return this.triangleFaces[triangle]; - } - - edgeIdForOutlineSegment(segment: number): number | undefined { - if (!Number.isInteger(segment) || segment < 0) return undefined; - return this.outlineEdges[segment]; - } - - /** Evaluates the oriented support normal at a projected model-space point; this does not classify trim membership. */ - normalAtFace(faceId: number, modelPoint: THREE.Vector3): THREE.Vector3 { - this.checkActive(); - if (!Number.isInteger(faceId) || faceId < 0 || faceId > 0xffffffff) throw new Error("Face ID must be an unsigned integer"); - const normal = this.kernel.face_normal_at(faceId, new Float64Array(modelPoint.toArray())); - return new THREE.Vector3(normal[0], normal[1], normal[2]); - } - - /** Classifies a world-space point against the regularized material represented by this solid. */ - classifyPoint(worldPoint: THREE.Vector3): AnalyticPointClassification { - if (![worldPoint.x, worldPoint.y, worldPoint.z].every(Number.isFinite)) { - throw new Error("Point coordinates must be finite"); - } - const placed = this.worldKernel(); - try { - return placed.classify_point(new Float64Array(worldPoint.toArray())) as AnalyticPointClassification; - } finally { - placed.free(); - } - } - - projectTo2DLines( - camera: AnalyticProjectionCamera, - hlr: AnalyticProjectionHlr, - deflection = this.lastDeflection, - ): string { - if (!Number.isFinite(deflection) || deflection <= 0) { - throw new Error("Projection deflection must be finite and positive"); - } - const placed = this.worldKernel(); - try { - return placed.project_to_2d_lines(JSON.stringify(camera), JSON.stringify(hlr), deflection); - } finally { - placed.free(); - } - } - - getModelBounds(): THREE.Box3 { - this.checkActive(); - const bounds = this.kernel.get_bounds(); - if (bounds.length === 0) return new THREE.Box3(); - return new THREE.Box3( - new THREE.Vector3(bounds[0], bounds[1], bounds[2]), - new THREE.Vector3(bounds[3], bounds[4], bounds[5]), - ); - } - - retessellate(deflection: number): AnalyticTessellationStats { - this.checkActive(); - return this.applyTessellation(this.tessellate(deflection * GEOMETRY_DEFLECTION_SHARE), deflection); - } - - tessellate(deflection: number, options: AnalyticTessellationOptions = {}): AnalyticTessellationData { - this.checkActive(); - if (!Number.isFinite(deflection) || deflection <= 0) throw new Error("Deflection must be finite and positive"); - if (options.maxTriangles !== undefined - && (!Number.isSafeInteger(options.maxTriangles) || options.maxTriangles <= 0 || options.maxTriangles > 2_000_000)) { - throw new Error("maxTriangles must be an integer between 1 and 2000000"); - } - const result = tessellate_brep( - this.kernel.get_brep_serialized(), - deflection, - options.maxTriangles === undefined ? undefined : JSON.stringify({ max_triangles: options.maxTriangles }), - ); - try { - return { - positions: result.positions(), normals: result.normals(), indices: result.indices(), - triangleFaceIds: result.triangle_face_ids(), outlinePositions: result.outline_positions(), - outlineEdgeIds: result.outline_edge_ids(), revision: result.revision(), achievedDeflection: result.achieved_deflection(), - }; - } finally { - result.free(); - } - } - - /** Applies a transient worker mesh only when it still matches this authored model revision. */ - applyTessellation(result: AnalyticTessellationData, deflection: number): AnalyticTessellationStats { - this.checkActive(); - if (!Number.isFinite(deflection) || deflection <= 0 || !Number.isFinite(result.achievedDeflection) - || result.achievedDeflection < 0 || result.achievedDeflection > deflection || result.revision !== this.modelRevision) { - throw new Error("Tessellation does not match the model revision or requested deflection"); - } - const positions = result.positions; - const indices = result.indices; - const faceIds = result.triangleFaceIds; - const outline = result.outlinePositions; - const edgeIds = result.outlineEdgeIds; - if (!(positions instanceof Float64Array) || !(result.normals instanceof Float32Array) - || !(indices instanceof Uint32Array) || !(faceIds instanceof Uint32Array) - || positions.length % 3 || result.normals.length !== positions.length || indices.length % 3 - || indices.length > 6_000_000 || faceIds.length !== indices.length / 3 - || !(outline instanceof Float64Array) || !(edgeIds instanceof Uint32Array) - || outline.length % 6 || outline.length > 12_000_000 || edgeIds.length !== outline.length / 6) { - throw new Error("Invalid or oversized tessellation buffers"); - } - const faceCount = this.kernel.get_face_count(); - const edgeCount = this.kernel.get_edge_count(); - if (indices.some((index) => index >= positions.length / 3) || faceIds.some((id) => id >= faceCount) - || positions.some((value) => !Number.isFinite(value)) || result.normals.some((value) => !Number.isFinite(value)) - || outline.some((value) => !Number.isFinite(value)) || edgeIds.some((id) => id >= edgeCount)) { - throw new Error("Invalid tessellation coordinates or topology mappings"); - } - const min = [Infinity, Infinity, Infinity]; - const max = [-Infinity, -Infinity, -Infinity]; - for (let i = 0; i < positions.length; i++) { - min[i % 3] = Math.min(min[i % 3], positions[i]); - max[i % 3] = Math.max(max[i % 3], positions[i]); - } - const origin = positions.length - ? min.map((value, i) => value / 2 + max[i] / 2) - : [0, 0, 0]; - let encodingError = 0; - const encode = (coordinates: Float64Array): Float32Array => { - const encoded = new Float32Array(coordinates.length); - for (let i = 0; i < coordinates.length; i += 3) { - const errors = [0, 0, 0]; - for (let j = 0; j < 3; j++) { - const local = coordinates[i + j] - origin[j]; - encoded[i + j] = local; - errors[j] = Math.abs(local - encoded[i + j]); - } - encodingError = Math.max(encodingError, Math.hypot(...errors)); - } - return encoded; - }; - const gpuPositions = encode(positions); - const gpuOutline = encode(outline); - if (!Number.isFinite(encodingError) || encodingError > deflection / 4) { - throw new Error("GPU coordinate precision cannot represent the requested deflection"); - } - if (result.achievedDeflection + encodingError > deflection) { - throw new Error("Combined tessellation and GPU encoding error exceeds requested deflection"); - } - const geometry = new THREE.BufferGeometry(); - geometry.setAttribute("position", new THREE.BufferAttribute(gpuPositions, 3)); - geometry.setAttribute("normal", new THREE.BufferAttribute(result.normals, 3)); - geometry.setIndex(new THREE.BufferAttribute(indices, 1)); - geometry.computeBoundingBox(); - geometry.computeBoundingSphere(); - const outlineGeometry = new THREE.BufferGeometry(); - outlineGeometry.setAttribute("position", new THREE.BufferAttribute(gpuOutline, 3)); - if (gpuOutline.length) { outlineGeometry.computeBoundingBox(); outlineGeometry.computeBoundingSphere(); } - const previous = this.surface.geometry; - const previousOutline = this.featureOutline.geometry; - this.surface.geometry = geometry; - this.surface.position.set(origin[0], origin[1], origin[2]); - this.featureOutline.geometry = outlineGeometry; - this.featureOutline.position.copy(this.surface.position); - this.triangleFaces = faceIds; - this.outlineEdges = edgeIds; - previous.dispose(); - previousOutline.dispose(); - this.lastDeflection = deflection; - return { - requestedDeflection: deflection, - achievedDeflection: result.achievedDeflection + encodingError, - triangleCount: indices.length / 3, - faceCount, - revision: result.revision, - }; - } - - dispose(): void { - if (this.disposed) return; - this.disposed = true; - this.surface.geometry.dispose(); - this.surface.material.dispose(); - this.featureOutline.geometry.dispose(); - this.featureOutline.material.dispose(); - this.kernel.free(); - } - - private checkActive(): void { - if (this.disposed) throw new Error("AnalyticSolid has been disposed"); - } - - private worldKernel(): OGAnalyticBrep { - this.checkActive(); - this.updateWorldMatrix(true, false); - const elements = this.matrixWorld.elements; - if (elements.every((value, index) => value === (index % 5 === 0 ? 1 : 0))) { - return new OGAnalyticBrep(this.kernel.get_brep_serialized()); - } - const { frame, scale } = matrixToWorldTransform(this.matrixWorld); - return this.kernel.placed(JSON.stringify(frame), scale); - } -} - -function createPrimitiveKernel(options: AnalyticPrimitiveOptions, ogid: string): OGAnalyticBrep { - const frame = options.frame ?? (options.kind === "planarPolyhedron" || options.kind === "polygonLoft" - ? { origin: [0, 0, 0], x: [1, 0, 0], y: [0, 1, 0], z: [0, 0, 1] } - : { origin: [0, 0, 0], x: [1, 0, 0], y: [0, 0, -1], z: [0, 1, 0] }); - const profileExtent = options.kind === "linearExtrusion" - ? [options.outer, ...options.holes].reduce((extent, loop) => ( - loop.reduce((loopExtent, point) => Math.max(loopExtent, Math.abs(point[0]), Math.abs(point[1])), extent) - ), 0) - : options.kind === "arcEdgedExtrusion" - ? [options.outer, ...(options.holes ?? [])].reduce((extent, ring) => ring.reduce((loopExtent, edge) => edge.kind === "line" - ? Math.max(loopExtent, ...edge.from.map(Math.abs), ...edge.to.map(Math.abs)) - : Math.max(loopExtent, Math.abs(edge.center[0]) + edge.radius, Math.abs(edge.center[1]) + edge.radius), extent), 0) - : 0; - const loftExtent = options.kind === "polygonLoft" - ? [...options.lower, ...options.upper].reduce((extent, point) => Math.max(extent, ...point.map(Math.abs)), 0) - : 0; - const polyhedronExtent = options.kind === "planarPolyhedron" - ? options.vertices.reduce((extent, point) => Math.max(extent, Math.abs(point[0]), Math.abs(point[1]), Math.abs(point[2])), 0) - : 0; - const size = options.kind === "torus" - ? 2 * (options.majorRadius + options.minorRadius) - : options.kind === "coaxialCircleLoft" - ? Math.max(2 * options.lowerRadius, 2 * options.upperRadius, options.height) - : options.kind === "revolvedRectangle" - ? Math.max(2 * options.outerRadius, options.height) - : options.kind === "chamferedCuboid" - ? Math.max(options.width, options.depth, options.height) - : options.kind === "linearExtrusion" || options.kind === "arcEdgedExtrusion" - ? Math.max(options.height, profileExtent) - : options.kind === "polygonLoft" - ? loftExtent - : options.kind === "boxWithArchedOpening" - ? Math.max(options.width, options.depth, options.height) - : options.kind === "planarPolyhedron" - ? polyhedronExtent - : options.kind === "frustum" - ? Math.max(2 * options.lowerRadius, 2 * options.upperRadius, options.height) - : options.kind === "annularSectorExtrusion" || options.kind === "annularSectorExtrusionWithOpenings" || options.kind === "annularSectorExtrusionWithArchedOpening" - ? Math.max(2 * options.radius + options.thickness, options.height) - : options.kind === "cuboid" - ? Math.max(options.width, options.depth, options.height) - : options.kind === "sphere" - ? 2 * options.radius - : Math.max(2 * options.radius, options.height); - const coordinateFloor = Math.max(...frame.origin.map(Math.abs)) * 64 * Number.EPSILON; - const geometric = Math.max(1e-9, size * 1e-8, coordinateFloor); - const accuracy = options.accuracy ?? { - geometric, intersection: geometric / 4, tessellation: 0.001, exchange: 0.00001, - }; - const dimensions = options.kind === "torus" - ? { major_radius: options.majorRadius, minor_radius: options.minorRadius } - : options.kind === "coaxialCircleLoft" - ? { lower_radius: options.lowerRadius, upper_radius: options.upperRadius, height: options.height } - : options.kind === "revolvedRectangle" - ? { inner_radius: options.innerRadius, outer_radius: options.outerRadius, height: options.height } - : options.kind === "chamferedCuboid" - ? { size: [options.width, options.depth, options.height], chamfer: options.chamfer } - : options.kind === "filletedCylinder" - ? { radius: options.radius, height: options.height, fillet_radius: options.filletRadius } - : options.kind === "linearExtrusion" - ? { outer: options.outer, holes: options.holes, height: options.height } - : options.kind === "arcEdgedExtrusion" - ? { outer: options.outer.map((edge) => edge.kind === "line" ? edge : { - kind: "arc", center: edge.center, radius: edge.radius, - start_angle: edge.startAngle, sweep_angle: edge.sweepAngle, - }), holes: (options.holes ?? []).map((ring) => ring.map((edge) => edge.kind === "line" ? edge : { - kind: "arc", center: edge.center, radius: edge.radius, - start_angle: edge.startAngle, sweep_angle: edge.sweepAngle, - })), height: options.height } - : options.kind === "polygonLoft" - ? { - lower: options.lower, - upper: options.upper, - alignment: options.alignment === undefined || options.alignment === "auto" - ? { kind: "auto" } - : { - kind: "indexed", - upper_start: options.alignment.upperStartIndex, - reverse_upper: options.alignment.reverseUpper ?? false, - }, - } - : options.kind === "boxWithArchedOpening" - ? { width: options.width, depth: options.depth, height: options.height, opening: options.opening } - : options.kind === "planarPolyhedron" - ? { vertices: options.vertices, faces: options.faces } - : options.kind === "frustum" - ? { lower_radius: options.lowerRadius, upper_radius: options.upperRadius, height: options.height } - : options.kind === "annularSectorExtrusion" || options.kind === "annularSectorExtrusionWithOpenings" || options.kind === "annularSectorExtrusionWithArchedOpening" - ? { radius: options.radius, thickness: options.thickness, height: options.height, start_angle: options.startAngle, sweep_angle: options.sweepAngle, ...(options.kind === "annularSectorExtrusionWithOpenings" ? { openings: options.openings.map((opening) => ({ id: opening.id, angle: opening.angle, width: opening.width, bottom: opening.bottom, height: opening.height })) } : options.kind === "annularSectorExtrusionWithArchedOpening" ? { opening: { id: options.opening.id, angle: options.opening.angle, width: options.opening.width, bottom: options.opening.bottom, height: options.opening.height } } : {}) } - : options.kind === "cylinderSector" - ? { radius: options.radius, height: options.height, start_angle: options.startAngle, sweep_angle: options.sweepAngle } - : options.kind === "cuboid" - ? { size: [options.width, options.depth, options.height] } - : options.kind === "sphere" - ? { radius: options.radius } - : { radius: options.radius, height: options.height }; - return OGAnalyticBrep.from_primitive(JSON.stringify({ - kind: options.kind === "annularSectorExtrusion" - ? "annular_sector_extrusion" - : options.kind === "annularSectorExtrusionWithOpenings" - ? "annular_sector_extrusion_with_openings" - : options.kind === "annularSectorExtrusionWithArchedOpening" - ? "annular_sector_extrusion_with_arched_opening" - : options.kind === "coaxialCircleLoft" - ? "coaxial_circle_loft" - : options.kind === "revolvedRectangle" - ? "revolved_rectangle" - : options.kind === "chamferedCuboid" - ? "chamfered_cuboid" - : options.kind === "filletedCylinder" - ? "filleted_cylinder" - : options.kind === "linearExtrusion" - ? "linear_extrusion" - : options.kind === "arcEdgedExtrusion" - ? "arc_edged_extrusion" - : options.kind === "polygonLoft" - ? "polygon_loft" - : options.kind === "boxWithArchedOpening" - ? "box_with_arched_opening" - : options.kind === "planarPolyhedron" - ? "planar_polyhedron" - : options.kind === "cylinderSector" - ? "cylinder_sector" - : options.kind, - id: ogid, frame, accuracy, ...dimensions, - })); -} diff --git a/main/opengeometry-three/src/shapes/cuboid.ts b/main/opengeometry-three/src/shapes/cuboid.ts deleted file mode 100644 index c7af15d..0000000 --- a/main/opengeometry-three/src/shapes/cuboid.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import * as THREE from "three"; - -import { createParametricEditCapabilities } from "../editor"; -import { - AnalyticSolid, - type AnalyticAccuracy, - type AnalyticPrimitiveOptions, -} from "./analytic-solid"; - -export interface CuboidPlacementOptions { - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -export interface ICuboidOptions extends CuboidPlacementOptions { - ogid?: string; - center?: Vector3; - width?: number; - height?: number; - depth?: number; - color?: THREE.ColorRepresentation; - deflection?: number; - accuracy?: AnalyticAccuracy; -} - -export type CuboidConfigUpdate = Partial< - Omit ->; -export type CuboidPlacementUpdate = CuboidPlacementOptions; - -interface NormalizedCuboidOptions { - ogid?: string; - center: Vector3; - width: number; - height: number; - depth: number; - color: THREE.ColorRepresentation; - deflection: number; - accuracy?: AnalyticAccuracy; - translation: Vector3; - rotation: Vector3; - scale: Vector3; -} - -/** Authoritative six-plane BRep with transient tessellation. */ -export class Cuboid extends AnalyticSolid { - options: NormalizedCuboidOptions; - - constructor(options: ICuboidOptions = {}) { - const normalized = normalizeOptions(options); - super(primitiveOptions(normalized)); - this.options = normalized; - this.applyPlacement(); - } - - set width(value: number) { this.setConfig({ width: value }); } - get width(): number { return this.options.width; } - set height(value: number) { this.setConfig({ height: value }); } - get height(): number { return this.options.height; } - set depth(value: number) { this.setConfig({ depth: value }); } - get depth(): number { return this.options.depth; } - override set color(value: THREE.ColorRepresentation) { - this.options.color = value; - this.surface.material.color.set(value); - this.surface.material.needsUpdate = true; - } - override get color(): THREE.Color { return this.surface.material.color; } - - setConfig(update: CuboidConfigUpdate): void { - const geometryChanged = ["center", "width", "height", "depth", "accuracy"] - .some((key) => key in update); - const next = { - ...this.options, - ...update, - center: update.center?.clone() ?? this.options.center, - }; - if (geometryChanged) this.replaceDefinition(primitiveOptions(next)); - else if (update.deflection !== undefined) this.retessellate(update.deflection); - this.options = next; - if (update.color !== undefined) this.color = update.color; - } - - getConfig(): Readonly { - return this.options; - } - - setPlacement(update: CuboidPlacementUpdate): void { - this.options.translation = update.translation?.clone() ?? this.options.translation; - this.options.rotation = update.rotation?.clone() ?? this.options.rotation; - this.options.scale = update.scale?.clone() ?? this.options.scale; - this.applyPlacement(); - } - - setTransform(translation: Vector3, rotation: Vector3, scale: Vector3): void { - this.setPlacement({ translation, rotation, scale }); - } - - setTranslation(translation: Vector3): void { this.setPlacement({ translation }); } - setRotation(rotation: Vector3): void { this.setPlacement({ rotation }); } - setScale(scale: Vector3): void { this.setPlacement({ scale }); } - - getPlacement(): Required { - return { - translation: this.options.translation.clone(), - rotation: this.options.rotation.clone(), - scale: this.options.scale.clone(), - }; - } - - getAnchor(): Vector3 { - return this.options.center.clone(); - } - - getEditCapabilities() { - return createParametricEditCapabilities("cuboid", "box"); - } - - canConvertToFreeform(): false { return false; } - - toFreeform(): never { - throw new Error("UnsupportedAnalyticEdit: BRep v2 freeform conversion is not implemented"); - } - - cleanGeometry(): void { this.dispose(); } - discardGeometry(): void { this.surface.geometry.dispose(); } - - private applyPlacement(): void { - this.position.copy(this.options.translation); - this.rotation.set( - this.options.rotation.x, - this.options.rotation.y, - this.options.rotation.z, - ); - this.scale.copy(this.options.scale); - this.updateMatrix(); - } -} - -function normalizeOptions(options: ICuboidOptions): NormalizedCuboidOptions { - return { - ogid: options.ogid, - center: options.center?.clone() ?? new Vector3(0, 0, 0), - width: options.width ?? 1, - height: options.height ?? 1, - depth: options.depth ?? 1, - color: options.color ?? 0x00ff00, - deflection: options.deflection ?? 0.01, - accuracy: options.accuracy, - translation: options.translation?.clone() ?? new Vector3(0, 0, 0), - rotation: options.rotation?.clone() ?? new Vector3(0, 0, 0), - scale: options.scale?.clone() ?? new Vector3(1, 1, 1), - }; -} - -function primitiveOptions(options: NormalizedCuboidOptions): AnalyticPrimitiveOptions { - return { - kind: "cuboid", - ogid: options.ogid, - width: options.width, - depth: options.depth, - height: options.height, - frame: { - origin: [ - options.center.x - options.width / 2, - options.center.y - options.height / 2, - options.center.z + options.depth / 2, - ], - x: [1, 0, 0], - y: [0, 0, -1], - z: [0, 1, 0], - }, - accuracy: options.accuracy, - deflection: options.deflection, - color: options.color, - }; -} diff --git a/main/opengeometry-three/src/shapes/cylinder.ts b/main/opengeometry-three/src/shapes/cylinder.ts deleted file mode 100644 index b356f19..0000000 --- a/main/opengeometry-three/src/shapes/cylinder.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import * as THREE from "three"; - -import { - AnalyticSolid, - type AnalyticAccuracy, - type AnalyticPrimitiveOptions, -} from "./analytic-solid"; - -export interface ICylinderOptions { - ogid?: string; - center?: Vector3; - radius?: number; - height?: number; - startAngle?: number; - sweepAngle?: number; - deflection?: number; - accuracy?: AnalyticAccuracy; - color?: THREE.ColorRepresentation; -} - -interface NormalizedCylinderOptions { - ogid?: string; - center: Vector3; - radius: number; - height: number; - startAngle: number; - sweepAngle: number; - deflection: number; - accuracy?: AnalyticAccuracy; - color: THREE.ColorRepresentation; -} - -/** Authoritative Plane/Cylinder BRep with transient deflection tessellation. */ -export class Cylinder extends AnalyticSolid { - readonly options: Readonly; - - constructor(options: ICylinderOptions = {}) { - const normalized = normalizeOptions(options); - super(primitiveOptions(normalized)); - this.options = normalized; - this.position.set(normalized.center.x, normalized.center.y, normalized.center.z); - } - - get radius(): number { return this.options.radius; } - get height(): number { return this.options.height; } - get startAngle(): number { return this.options.startAngle; } - get sweepAngle(): number { return this.options.sweepAngle; } - - getAnchor(): THREE.Vector3 { - return this.position.clone(); - } -} - -function normalizeOptions(options: ICylinderOptions): NormalizedCylinderOptions { - return { - ogid: options.ogid, - center: options.center ?? new Vector3(0, 0, 0), - radius: options.radius ?? 1, - height: options.height ?? 1, - startAngle: options.startAngle ?? 0, - sweepAngle: options.sweepAngle ?? Math.PI * 2, - deflection: options.deflection ?? 0.01, - accuracy: options.accuracy, - color: options.color ?? 0x00ff00, - }; -} - -function primitiveOptions(options: NormalizedCylinderOptions): AnalyticPrimitiveOptions { - const frame = { - origin: [0, -options.height / 2, 0] as [number, number, number], - x: [1, 0, 0] as [number, number, number], - y: [0, 0, -1] as [number, number, number], - z: [0, 1, 0] as [number, number, number], - }; - const common = { - ogid: options.ogid, - frame, - accuracy: options.accuracy, - deflection: options.deflection, - color: options.color, - radius: options.radius, - height: options.height, - }; - return Math.abs(Math.abs(options.sweepAngle) - Math.PI * 2) <= 64 * Number.EPSILON - ? { ...common, kind: "cylinder" } - : { - ...common, - kind: "cylinderSector", - startAngle: options.startAngle, - sweepAngle: options.sweepAngle, - }; -} diff --git a/main/opengeometry-three/src/shapes/index.ts b/main/opengeometry-three/src/shapes/index.ts deleted file mode 100644 index 367737e..0000000 --- a/main/opengeometry-three/src/shapes/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * High-level 3D shape wrappers backed by the OpenGeometry kernel. - */ -export * from './polygon'; -export * from './solid'; -export * from './cylinder'; -export * from './cuboid'; -export * from './opening'; -export * from './sphere'; -export * from './wedge'; -export * from './sweep'; diff --git a/main/opengeometry-three/src/shapes/opening.ts b/main/opengeometry-three/src/shapes/opening.ts deleted file mode 100644 index f00d21a..0000000 --- a/main/opengeometry-three/src/shapes/opening.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import * as THREE from "three"; - -import { createParametricEditCapabilities } from "../editor"; -import { AnalyticSolid, type AnalyticAccuracy } from "./analytic-solid"; -import { - Cuboid, - type CuboidConfigUpdate, - type CuboidPlacementOptions, - type ICuboidOptions, -} from "./cuboid"; - -export interface IOpeningOptions extends CuboidPlacementOptions { - ogid?: string; - center?: Vector3; - width?: number; - height?: number; - depth?: number; - color?: THREE.ColorRepresentation; - deflection?: number; - accuracy?: AnalyticAccuracy; -} - -export type OpeningPlacementOptions = CuboidPlacementOptions; -export type OpeningConfigUpdate = CuboidConfigUpdate; -export type OpeningPlacementUpdate = OpeningPlacementOptions; - -/** Strict BRep v2 subtractive box. */ -export class Opening extends Cuboid { - constructor(options: IOpeningOptions = {}) { - const normalized: ICuboidOptions = { - center: options.center ?? new Vector3(0, 0, 0), - width: options.width ?? 1, - height: options.height ?? 1, - depth: options.depth ?? 0.2, - color: options.color ?? 0xdad7cd, - ...options, - }; - super(normalized); - this.surface.material.transparent = true; - this.surface.material.opacity = 0; - this.surface.material.depthWrite = false; - this.surface.material.needsUpdate = true; - } - - get dimensions() { - return { width: this.width, height: this.height, depth: this.depth }; - } - - override getEditCapabilities() { - return createParametricEditCapabilities("opening", "box"); - } - - subtractFrom( - host: AnalyticSolid, - options: { deflection?: number; color?: THREE.ColorRepresentation } = {}, - ): AnalyticSolid { - if (!(host instanceof AnalyticSolid)) { - throw new Error("Opening subtraction requires a strict analytic BRep v2 host"); - } - return host.subtract([this], options); - } -} diff --git a/main/opengeometry-three/src/shapes/outline-utils.ts b/main/opengeometry-three/src/shapes/outline-utils.ts deleted file mode 100644 index edd1c46..0000000 --- a/main/opengeometry-three/src/shapes/outline-utils.ts +++ /dev/null @@ -1,127 +0,0 @@ -import * as THREE from "three"; -import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js"; -import { LineSegments2 } from "three/examples/jsm/lines/LineSegments2.js"; -import { LineSegmentsGeometry } from "three/examples/jsm/lines/LineSegmentsGeometry.js"; - -/** - * Outline mesh variants used by shape wrappers. - */ -export type ShapeOutlineMesh = THREE.LineSegments | LineSegments2; - -/** - * Options for building a shape outline mesh. - */ -export interface ShapeOutlineMeshOptions { - positions: number[]; - color?: number; - fatOutlines?: boolean; - outlineWidth?: number; -} - -const DEFAULT_OUTLINE_COLOR = 0x000000; -const DEFAULT_OUTLINE_WIDTH = 1; - -function getOutlineResolution() { - if (typeof window === "undefined") { - return new THREE.Vector2(1, 1); - } - return new THREE.Vector2(window.innerWidth, window.innerHeight); -} - -/** - * Clamps outline width inputs to the supported positive finite range. - */ -export function sanitizeOutlineWidth(width?: number) { - if (!Number.isFinite(width) || typeof width !== "number" || width <= 0) { - return DEFAULT_OUTLINE_WIDTH; - } - return width; -} - -/** - * Creates either a thin or fat outline mesh from line segment positions. - */ -export function createShapeOutlineMesh({ - positions, - color = DEFAULT_OUTLINE_COLOR, - fatOutlines = false, - outlineWidth = DEFAULT_OUTLINE_WIDTH, -}: ShapeOutlineMeshOptions): ShapeOutlineMesh { - if (fatOutlines) { - const fatGeometry = new LineSegmentsGeometry(); - fatGeometry.setPositions(positions); - const fatMaterial = new LineMaterial({ - color, - linewidth: sanitizeOutlineWidth(outlineWidth), - resolution: getOutlineResolution(), - }); - const fatMesh = new LineSegments2(fatGeometry, fatMaterial); - fatMesh.computeLineDistances(); - return fatMesh; - } - - const outlineGeometry = new THREE.BufferGeometry(); - outlineGeometry.setAttribute( - "position", - new THREE.Float32BufferAttribute(positions, 3) - ); - - const outlineMaterial = new THREE.LineBasicMaterial({ color }); - return new THREE.LineSegments(outlineGeometry, outlineMaterial); -} - -/** - * Disposes the geometry and material owned by an outline mesh. - */ -export function disposeShapeOutlineMesh(mesh: ShapeOutlineMesh | null) { - if (!mesh) { - return; - } - - mesh.geometry.dispose(); - if (Array.isArray(mesh.material)) { - mesh.material.forEach((material) => material.dispose()); - return; - } - mesh.material.dispose(); -} - -/** - * Updates the color of an existing outline mesh in place. - */ -export function setShapeOutlineColor(mesh: ShapeOutlineMesh | null, color: number) { - if (!mesh) { - return; - } - - if (mesh.material instanceof THREE.LineBasicMaterial) { - mesh.material.color.set(color); - return; - } - - if (mesh.material instanceof LineMaterial) { - mesh.material.color.set(color); - } -} - -/** - * Reads the current outline color, falling back when no outline exists. - */ -export function getShapeOutlineColor( - mesh: ShapeOutlineMesh | null, - fallback: number = DEFAULT_OUTLINE_COLOR -) { - if (!mesh) { - return fallback; - } - - if (mesh.material instanceof THREE.LineBasicMaterial) { - return mesh.material.color.getHex(); - } - - if (mesh.material instanceof LineMaterial) { - return mesh.material.color.getHex(); - } - - return fallback; -} diff --git a/main/opengeometry-three/src/shapes/polygon.ts b/main/opengeometry-three/src/shapes/polygon.ts deleted file mode 100644 index b01a003..0000000 --- a/main/opengeometry-three/src/shapes/polygon.ts +++ /dev/null @@ -1,501 +0,0 @@ -import * as THREE from "three"; -import { OGPolygon, Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import { LineSegmentsGeometry } from "three/examples/jsm/lines/LineSegmentsGeometry.js"; -import { getUUID } from "../utils/randomizer"; -import { - createShapeOutlineMesh, - disposeShapeOutlineMesh, - getShapeOutlineColor, - sanitizeOutlineWidth, - setShapeOutlineColor, - ShapeOutlineMesh, -} from "./outline-utils"; -import { - clonePlacement, - createParametricEditCapabilities, -} from "../editor"; -import { createFreeformGeometry } from "../freeform"; -import { Solid } from "./solid"; -import { extrudeBrepFace } from "../operations/extrude"; -import { - loftPolygonSections, - type PolygonLoftOptions, - type PolygonLoftPoint, -} from "../operations/loft"; - -/** - * Construction options for a planar polygon. - */ -export interface IPolygonOptions { - ogid?: string; - vertices: Vector3[]; - holes?: Vector3[][]; - color: number; - fatOutlines?: boolean; - outlineWidth?: number; - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -export interface PolygonPlacementOptions { - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Partial polygon config updates that regenerate the profile when geometry changes. - */ -export type PolygonConfigUpdate = Partial< - Omit ->; - -/** - * Placement updates accepted by `Polygon`. - */ -export type PolygonPlacementUpdate = PolygonPlacementOptions; - -/** - * Planar polygon mesh with support for holes, booleans, and extrusion to `Solid`. - */ -export class Polygon extends THREE.Mesh { - ogid: string; - options: IPolygonOptions = { - vertices: [], - holes: [], - color: 0x00ff00, - fatOutlines: false, - outlineWidth: 1, - translation: new Vector3(0, 0, 0), - rotation: new Vector3(0, 0, 0), - scale: new Vector3(1, 1, 1), - }; - polygon: OGPolygon; - #outlineMesh: ShapeOutlineMesh | null = null; - private _outlineEnabled = false; - private _fatOutlines = false; - private _outlineWidth = 1; - private _outlineColor = 0x000000; - - set color(color: number) { - this.options.color = color; - if (this.material instanceof THREE.MeshBasicMaterial) { - this.material.color.set(color); - } - } - - get color() { - return this.options.color; - } - - constructor(options?: IPolygonOptions) { - super(); - this.ogid = options?.ogid ?? getUUID(); - this.polygon = new OGPolygon(this.ogid); - - this.options = { ...this.options, ...options }; - this.options.ogid = this.ogid; - - this.setConfig({ - vertices: this.options.vertices.map((vertex) => vertex.clone()), - holes: (this.options.holes ?? []).map((hole) => - hole.map((vertex) => vertex.clone()) - ), - color: this.options.color, - fatOutlines: this.options.fatOutlines, - outlineWidth: this.options.outlineWidth, - }); - this.setPlacement({ - translation: this.options.translation?.clone(), - rotation: this.options.rotation?.clone(), - scale: this.options.scale?.clone(), - }); - } - - validateOptions() { - if (!this.options) { - throw new Error("Options are not defined for Polygon"); - } - } - - setConfig(options: PolygonConfigUpdate) { - this.validateOptions(); - - const nextOptions = { ...this.options, ...options }; - const geometryChanged = - "vertices" in options || - "holes" in options; - const colorChanged = "color" in options; - const outlineStyleChanged = - "fatOutlines" in options || - "outlineWidth" in options; - - this.options = nextOptions; - this._fatOutlines = this.options.fatOutlines ?? false; - this._outlineWidth = sanitizeOutlineWidth(this.options.outlineWidth); - this.options.fatOutlines = this._fatOutlines; - this.options.outlineWidth = this._outlineWidth; - - if (geometryChanged) { - this.polygon.set_config(this.options.vertices.map((vertex) => vertex.clone())); - (this.options.holes ?? []).forEach((hole) => { - this.polygon.add_holes(hole.map((vertex) => vertex.clone())); - }); - this.generateGeometry(); - return; - } - - if (colorChanged) { - this.color = this.options.color; - } - - if (outlineStyleChanged && this._outlineEnabled) { - this.outline = true; - } - } - - getConfig() { - return this.options; - } - - getAnchor() { - const anchor = this.polygon.get_anchor(); - return new Vector3(anchor.x, anchor.y, anchor.z); - } - - setAnchor(anchor: Vector3) { - this.polygon.set_anchor(anchor.clone()); - this.generateGeometry(); - } - - resetAnchor() { - this.polygon.reset_anchor(); - this.generateGeometry(); - } - - setPlacement(placement: PolygonPlacementUpdate) { - this.options.translation = placement.translation?.clone() ?? this.options.translation; - this.options.rotation = placement.rotation?.clone() ?? this.options.rotation; - this.options.scale = placement.scale?.clone() ?? this.options.scale; - - this.polygon.set_transform( - this.options.translation?.clone() ?? new Vector3(0, 0, 0), - this.options.rotation?.clone() ?? new Vector3(0, 0, 0), - this.options.scale?.clone() ?? new Vector3(1, 1, 1) - ); - this.generateGeometry(); - } - - setTransform(translation: Vector3, rotation: Vector3, scale: Vector3) { - this.setPlacement({ - translation, - rotation, - scale, - }); - } - - setTranslation(translation: Vector3) { - this.setPlacement({ translation }); - } - - setRotation(rotation: Vector3) { - this.setPlacement({ rotation }); - } - - setScale(scale: Vector3) { - this.setPlacement({ scale }); - } - - getPlacement() { - return clonePlacement({ - translation: this.options.translation, - rotation: this.options.rotation, - scale: this.options.scale, - }); - } - - /** - * Returns the editing capability payload advertised by parametric wrappers. - */ - getEditCapabilities() { - return createParametricEditCapabilities("polygon", "profile"); - } - - /** - * Polygon always supports conversion into the freeform editing wrapper. - */ - canConvertToFreeform() { - return true; - } - - /** - * Returns a freeform wrapper for direct BRep editing of this polygon. - */ - toFreeform(id: string = this.ogid) { - if (!this.canConvertToFreeform()) { - throw new Error("This entity cannot be converted to freeform."); - } - return createFreeformGeometry(this.polygon.get_local_brep_serialized(), { - id, - placement: this.getPlacement(), - }); - } - - cleanGeometry() { - this.disposeResources(); - } - - generateGeometry() { - const bufferData = Array.from(this.polygon.get_geometry_buffer()); - - this.writePositionsToGeometry(this.geometry, bufferData); - - if (this.material instanceof THREE.MeshBasicMaterial) { - this.material.color.set(this.options.color); - this.material.side = THREE.DoubleSide; - } else { - if (Array.isArray(this.material)) { - this.material.forEach((material) => material.dispose()); - } else { - this.material.dispose(); - } - - this.material = new THREE.MeshBasicMaterial({ - color: this.options.color, - side: THREE.DoubleSide, - }); - } - - if (bufferData.length > 0) { - this.geometry.computeVertexNormals(); - this.geometry.computeBoundingBox(); - this.geometry.computeBoundingSphere(); - } else { - this.geometry.deleteAttribute("normal"); - this.geometry.boundingBox = null; - this.geometry.boundingSphere = null; - } - - if (!this._outlineEnabled) { - return; - } - - const outlineData = Array.from(this.polygon.get_outline_geometry_buffer()); - if (!this.#outlineMesh) { - this.outline = true; - return; - } - - if (this.#outlineMesh instanceof THREE.LineSegments) { - this.writePositionsToGeometry(this.#outlineMesh.geometry, outlineData); - this.#outlineMesh.geometry.computeBoundingBox(); - this.#outlineMesh.geometry.computeBoundingSphere(); - return; - } - - const fatGeometry = this.#outlineMesh.geometry as LineSegmentsGeometry; - fatGeometry.setPositions(outlineData); - - const fatOutline = this.#outlineMesh as ShapeOutlineMesh & { - computeLineDistances?: () => void; - }; - fatOutline.computeLineDistances?.(); - } - - addVertices(vertices: Vector3[]) { - if (!this.polygon) return; - this.setConfig({ - vertices: vertices.map((vertex) => vertex.clone()), - }); - } - - addHole(holeVertices: Vector3[]) { - if (!this.polygon) return; - this.options.holes = [ - ...(this.options.holes ?? []), - holeVertices.map((vertex) => vertex.clone()), - ]; - this.setConfig({ - holes: (this.options.holes ?? []).map((hole) => - hole.map((vertex) => vertex.clone()) - ), - }); - } - - /** - * Builds a kernel-backed `Solid` by extruding this polygon face by `height`. - * - * This is the recommended public path when a browser CAD or AEC workflow - * starts from a planar profile and needs a boolean-ready solid. - */ - extrude(height: number) { - const config = this.getConfig(); - const placement = this.getPlacement(); - const solid = new Solid({ - brep: extrudeBrepFace(this.polygon.get_local_brep_serialized(), height), - color: config.color, - fatOutlines: config.fatOutlines, - outlineWidth: config.outlineWidth, - anchor: this.getAnchor(), - translation: placement.translation.clone(), - rotation: placement.rotation.clone(), - scale: placement.scale.clone(), - }); - - solid.outline = this.outline; - solid.outlineColor = this.outlineColor; - return solid; - } - - /** Builds a capped analytic ruled loft from this profile to `other`. */ - loftTo(other: Polygon, options: PolygonLoftOptions = {}) { - if (!(other instanceof Polygon)) throw new Error("Polygon.loftTo requires another Polygon"); - if ((this.options.holes?.length ?? 0) > 0 || (other.options.holes?.length ?? 0) > 0) { - throw new Error("Polygon loft holes are not supported yet"); - } - return loftPolygonSections( - [this.worldProfilePoints(), other.worldProfilePoints()], - { color: this.options.color, ...options }, - ); - } - - /** - * Returns the serialized BRep payload for this polygon. - */ - getBrepData() { - if (!this.polygon) return null; - const brepData = this.polygon.get_brep_serialized(); - return brepData; - } - - private worldProfilePoints(): PolygonLoftPoint[] { - const anchor = this.getAnchor(); - const placement = this.getPlacement(); - const matrix = new THREE.Matrix4().compose( - new THREE.Vector3( - anchor.x + placement.translation.x, - anchor.y + placement.translation.y, - anchor.z + placement.translation.z, - ), - new THREE.Quaternion().setFromEuler(new THREE.Euler( - placement.rotation.x, - placement.rotation.y, - placement.rotation.z, - "XYZ", - )), - new THREE.Vector3(placement.scale.x, placement.scale.y, placement.scale.z), - ); - return this.options.vertices.map((point) => { - const transformed = new THREE.Vector3( - point.x - anchor.x, - point.y - anchor.y, - point.z - anchor.z, - ).applyMatrix4(matrix); - return [transformed.x, transformed.y, transformed.z] as const; - }); - } - - set outlineColor(color: number) { - this._outlineColor = color; - setShapeOutlineColor(this.#outlineMesh, color); - } - - get outlineColor() { - return getShapeOutlineColor(this.#outlineMesh, this._outlineColor); - } - - set outline(enable: boolean) { - this._outlineEnabled = enable; - this.clearOutlineMesh(); - if (enable) { - const outline_buf = Array.from(this.polygon.get_outline_geometry_buffer()); - this.#outlineMesh = createShapeOutlineMesh({ - positions: outline_buf, - color: this._outlineColor, - fatOutlines: this._fatOutlines, - outlineWidth: this._outlineWidth, - }); - this.add(this.#outlineMesh); - } - } - - get outline() { - return this._outlineEnabled; - } - - set fatOutlines(value: boolean) { - this._fatOutlines = value; - this.options.fatOutlines = value; - if (this._outlineEnabled) { - this.outline = true; - } - } - - get fatOutlines() { - return this._fatOutlines; - } - - set outlineWidth(value: number) { - this._outlineWidth = sanitizeOutlineWidth(value); - this.options.outlineWidth = this._outlineWidth; - if (this._outlineEnabled) { - this.outline = true; - } - } - - get outlineWidth() { - return this._outlineWidth; - } - - disposeGeometryMaterial() { - this.disposeResources(); - } - - private clearOutlineMesh() { - if (!this.#outlineMesh) { - return; - } - this.remove(this.#outlineMesh); - disposeShapeOutlineMesh(this.#outlineMesh); - this.#outlineMesh = null; - } - - dispose() { - this.disposeResources(); - } - - private writePositionsToGeometry( - geometry: THREE.BufferGeometry, - positions: number[] - ) { - const existing = geometry.getAttribute("position"); - if ( - !(existing instanceof THREE.BufferAttribute) || - existing.itemSize !== 3 || - existing.count !== positions.length / 3 - ) { - geometry.setAttribute( - "position", - new THREE.Float32BufferAttribute(positions, 3) - ); - return; - } - - const array = existing.array as Float32Array; - array.set(positions); - existing.needsUpdate = true; - } - - private disposeResources() { - if (this.geometry) { - this.geometry.dispose(); - } - if (Array.isArray(this.material)) { - this.material.forEach((material) => material.dispose()); - } else if (this.material instanceof THREE.Material) { - this.material.dispose(); - } - this.clearOutlineMesh(); - } -} diff --git a/main/opengeometry-three/src/shapes/solid.ts b/main/opengeometry-three/src/shapes/solid.ts deleted file mode 100644 index b5b9965..0000000 --- a/main/opengeometry-three/src/shapes/solid.ts +++ /dev/null @@ -1,400 +0,0 @@ -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import * as THREE from "three"; -import { LineSegmentsGeometry } from "three/examples/jsm/lines/LineSegmentsGeometry.js"; - -import { clonePlacement, toObjectTransformation } from "../editor"; -import { createFreeformGeometry, type FreeformGeometry } from "../freeform"; -import type { FreeformSource, ObjectTransformation } from "../freeform/types"; -import { extrudeBrepFace } from "../operations/extrude"; -import { getUUID } from "../utils/randomizer"; -import { - createShapeOutlineMesh, - disposeShapeOutlineMesh, - getShapeOutlineColor, - sanitizeOutlineWidth, - setShapeOutlineColor, - ShapeOutlineMesh, -} from "./outline-utils"; - -/** - * Construction options for a generic BRep-backed solid. - */ -export interface ISolidOptions { - ogid?: string; - brep: FreeformSource; - color: number; - fatOutlines?: boolean; - outlineWidth?: number; - anchor?: Vector3; - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Placement updates accepted by `Solid`. - */ -export interface SolidPlacementOptions { - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Additional options accepted when creating a `Solid` through face extrusion. - */ -export interface ExtrudeSolidOptions - extends Omit { - ogid?: string; - anchor?: Vector3; - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Generic renderable BRep-backed solid used for extrusion outputs and other - * kernel results that are not dedicated parametric shapes. - */ -export class Solid extends THREE.Mesh { - ogid: string; - options: ISolidOptions = { - brep: "{}", - color: 0x00ff00, - fatOutlines: false, - outlineWidth: 1, - anchor: new Vector3(0, 0, 0), - translation: new Vector3(0, 0, 0), - rotation: new Vector3(0, 0, 0), - scale: new Vector3(1, 1, 1), - }; - - private solid: FreeformGeometry; - #outlineMesh: ShapeOutlineMesh | null = null; - private _outlineEnabled = false; - private _fatOutlines = false; - private _outlineWidth = 1; - private _outlineColor = 0x000000; - - set color(color: number) { - this.options.color = color; - if (this.material instanceof THREE.MeshStandardMaterial) { - this.material.color.set(color); - } - } - - get color() { - return this.options.color; - } - - constructor(options: ISolidOptions) { - super(); - - this.ogid = options.ogid ?? getUUID(); - this.options = { - ...this.options, - ...options, - ogid: this.ogid, - anchor: cloneVector(options.anchor, [0, 0, 0]), - translation: cloneVector(options.translation, [0, 0, 0]), - rotation: cloneVector(options.rotation, [0, 0, 0]), - scale: cloneVector(options.scale, [1, 1, 1]), - }; - this._fatOutlines = this.options.fatOutlines ?? false; - this._outlineWidth = sanitizeOutlineWidth(this.options.outlineWidth); - this.options.fatOutlines = this._fatOutlines; - this.options.outlineWidth = this._outlineWidth; - - this.solid = createFreeformGeometry(this.options.brep, { - id: this.ogid, - placement: this.getPlacementTransform(), - }); - this.generateGeometry(); - } - - /** - * Extrudes a face-like BRep source and wraps the result as a renderable solid. - * - * Prefer `polygon.extrude(height)` when the source is already a `Polygon`. - */ - static extrude( - source: FreeformSource, - height: number, - options: ExtrudeSolidOptions - ) { - return new Solid({ - ...options, - brep: extrudeBrepFace(source, height), - }); - } - - /** - * Returns the current placement values as cloned vectors. - */ - getPlacement() { - return clonePlacement(this.getPlacementTransform()); - } - - /** - * Applies translation, rotation, and scale updates to the wrapped BRep. - */ - setPlacement(placement: SolidPlacementOptions) { - this.options.translation = cloneVector( - placement.translation ?? this.options.translation, - [0, 0, 0] - ); - this.options.rotation = cloneVector( - placement.rotation ?? this.options.rotation, - [0, 0, 0] - ); - this.options.scale = cloneVector( - placement.scale ?? this.options.scale, - [1, 1, 1] - ); - - this.solid.setPlacement(this.getPlacementTransform()); - this.generateGeometry(); - } - - setTransform(translation: Vector3, rotation: Vector3, scale: Vector3) { - this.setPlacement({ - translation, - rotation, - scale, - }); - } - - setTranslation(translation: Vector3) { - this.setPlacement({ translation }); - } - - setRotation(rotation: Vector3) { - this.setPlacement({ rotation }); - } - - setScale(scale: Vector3) { - this.setPlacement({ scale }); - } - - getAnchor() { - return this.getPlacementTransform().anchor.clone(); - } - - setAnchor(anchor: Vector3) { - this.options.anchor = anchor.clone(); - this.solid.setPlacement(this.getPlacementTransform()); - this.generateGeometry(); - } - - /** - * Solid is always convertible back to the freeform editing wrapper. - */ - canConvertToFreeform() { - return true; - } - - /** - * Returns a freeform wrapper for direct editing of this solid's BRep. - */ - toFreeform(id: string = this.ogid) { - if (!this.canConvertToFreeform()) { - throw new Error("This entity cannot be converted to freeform."); - } - return createFreeformGeometry(this.getLocalBrepSerialized(), { - id, - placement: this.getPlacementTransform(), - }); - } - - /** - * Returns the world-space serialized BRep payload for this solid. - */ - getBrepSerialized() { - return this.solid.getBrepSerialized(); - } - - /** - * Returns the local-space serialized BRep payload for this solid. - */ - getLocalBrepSerialized() { - return this.solid.getLocalBrepSerialized(); - } - - /** - * Returns the parsed world-space BRep object for this solid. - */ - getBrepData() { - const brepSerialized = this.getBrepSerialized(); - if (!brepSerialized) { - throw new Error("Brep data is not available for this solid."); - } - return JSON.parse(brepSerialized); - } - - /** - * Rebuilds the visible mesh and outline from the current freeform geometry. - */ - generateGeometry() { - const bufferData = this.solid.getGeometryBuffer(); - this.writePositionsToGeometry(this.geometry, bufferData); - - let material: THREE.MeshStandardMaterial; - if (this.material instanceof THREE.MeshStandardMaterial) { - material = this.material; - } else { - if (Array.isArray(this.material)) { - this.material.forEach((material) => material.dispose()); - } else { - this.material.dispose(); - } - - material = new THREE.MeshStandardMaterial({ - color: this.options.color, - transparent: false, - opacity: 1, - depthWrite: true, - side: THREE.FrontSide, - }); - this.material = material; - } - - material.color.set(this.options.color); - material.transparent = false; - material.opacity = 1; - material.depthWrite = true; - material.side = THREE.FrontSide; - material.needsUpdate = true; - - if (bufferData.length > 0) { - this.geometry.computeVertexNormals(); - this.geometry.computeBoundingBox(); - this.geometry.computeBoundingSphere(); - } else { - this.geometry.deleteAttribute("normal"); - this.geometry.boundingBox = null; - this.geometry.boundingSphere = null; - } - - if (!this._outlineEnabled) { - return; - } - - const outlineData = this.solid.getOutlineGeometryBuffer(); - if (!this.#outlineMesh) { - this.outline = true; - return; - } - - if (this.#outlineMesh instanceof THREE.LineSegments) { - this.writePositionsToGeometry(this.#outlineMesh.geometry, outlineData); - this.#outlineMesh.geometry.computeBoundingBox(); - this.#outlineMesh.geometry.computeBoundingSphere(); - return; - } - - const fatGeometry = this.#outlineMesh.geometry as LineSegmentsGeometry; - fatGeometry.setPositions(outlineData); - - const fatOutline = this.#outlineMesh as ShapeOutlineMesh & { - computeLineDistances?: () => void; - }; - fatOutline.computeLineDistances?.(); - } - - set outlineColor(color: number) { - this._outlineColor = color; - setShapeOutlineColor(this.#outlineMesh, color); - } - - get outlineColor() { - return getShapeOutlineColor(this.#outlineMesh, this._outlineColor); - } - - set outline(enable: boolean) { - this._outlineEnabled = enable; - this.clearOutlineMesh(); - if (enable) { - this.#outlineMesh = createShapeOutlineMesh({ - positions: this.solid.getOutlineGeometryBuffer(), - color: this._outlineColor, - fatOutlines: this._fatOutlines, - outlineWidth: this._outlineWidth, - }); - this.add(this.#outlineMesh); - } - } - - get outline() { - return this._outlineEnabled; - } - - set fatOutlines(value: boolean) { - this._fatOutlines = value; - this.options.fatOutlines = value; - if (this._outlineEnabled) { - this.outline = true; - } - } - - get fatOutlines() { - return this._fatOutlines; - } - - set outlineWidth(value: number) { - this._outlineWidth = sanitizeOutlineWidth(value); - this.options.outlineWidth = this._outlineWidth; - if (this._outlineEnabled) { - this.outline = true; - } - } - - get outlineWidth() { - return this._outlineWidth; - } - - clearOutlineMesh() { - if (!this.#outlineMesh) { - return; - } - this.remove(this.#outlineMesh); - disposeShapeOutlineMesh(this.#outlineMesh); - this.#outlineMesh = null; - } - - private getPlacementTransform(): ObjectTransformation { - return toObjectTransformation({ - anchor: cloneVector(this.options.anchor, [0, 0, 0]), - translation: cloneVector(this.options.translation, [0, 0, 0]), - rotation: cloneVector(this.options.rotation, [0, 0, 0]), - scale: cloneVector(this.options.scale, [1, 1, 1]), - }); - } - - private writePositionsToGeometry( - geometry: THREE.BufferGeometry, - positions: number[] - ) { - const attribute = geometry.getAttribute("position"); - if ( - !(attribute instanceof THREE.BufferAttribute) || - attribute.itemSize !== 3 || - attribute.count !== positions.length / 3 - ) { - geometry.setAttribute( - "position", - new THREE.Float32BufferAttribute(positions, 3) - ); - return; - } - - (attribute.array as Float32Array).set(positions); - attribute.needsUpdate = true; - } -} - -function cloneVector( - vector: Vector3 | undefined, - fallback: [number, number, number] -): Vector3 { - return vector?.clone() ?? new Vector3(...fallback); -} diff --git a/main/opengeometry-three/src/shapes/sphere.ts b/main/opengeometry-three/src/shapes/sphere.ts deleted file mode 100644 index e762b2d..0000000 --- a/main/opengeometry-three/src/shapes/sphere.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import * as THREE from "three"; - -import { AnalyticSolid, type AnalyticAccuracy } from "./analytic-solid"; - -export interface ISphereOptions { - ogid?: string; - center?: Vector3; - radius?: number; - deflection?: number; - accuracy?: AnalyticAccuracy; - color?: THREE.ColorRepresentation; -} - -interface NormalizedSphereOptions { - ogid?: string; - center: Vector3; - radius: number; - deflection: number; - accuracy?: AnalyticAccuracy; - color: THREE.ColorRepresentation; -} - -/** Authoritative spherical BRep with explicit poles and transient deflection tessellation. */ -export class Sphere extends AnalyticSolid { - readonly options: Readonly; - - constructor(options: ISphereOptions = {}) { - const normalized = normalizeOptions(options); - super({ - kind: "sphere", - ogid: normalized.ogid, - radius: normalized.radius, - accuracy: normalized.accuracy, - deflection: normalized.deflection, - color: normalized.color, - }); - this.options = normalized; - this.position.set(normalized.center.x, normalized.center.y, normalized.center.z); - } - - get radius(): number { return this.options.radius; } - - getAnchor(): THREE.Vector3 { - return this.position.clone(); - } -} - -function normalizeOptions(options: ISphereOptions): NormalizedSphereOptions { - return { - ogid: options.ogid, - center: options.center ?? new Vector3(0, 0, 0), - radius: options.radius ?? 1, - deflection: options.deflection ?? 0.01, - accuracy: options.accuracy, - color: options.color ?? 0x00ff00, - }; -} diff --git a/main/opengeometry-three/src/shapes/sweep.ts b/main/opengeometry-three/src/shapes/sweep.ts deleted file mode 100644 index a2b8cc2..0000000 --- a/main/opengeometry-three/src/shapes/sweep.ts +++ /dev/null @@ -1,425 +0,0 @@ -import * as OGKernel from "../../../opengeometry/pkg/opengeometry"; -import { Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import * as THREE from "three"; -import { LineSegmentsGeometry } from "three/examples/jsm/lines/LineSegmentsGeometry.js"; -import { getUUID } from "../utils/randomizer"; -import { - createShapeOutlineMesh, - disposeShapeOutlineMesh, - sanitizeOutlineWidth, - ShapeOutlineMesh, -} from "./outline-utils"; -import { - clonePlacement, - createParametricEditCapabilities, -} from "../editor"; -import { createFreeformGeometry } from "../freeform"; - -/** - * Construction options for a swept solid. - */ -export interface ISweepOptions { - ogid?: string; - path: Vector3[]; - profile: Vector3[]; - color: number; - capStart?: boolean; - capEnd?: boolean; - fatOutlines?: boolean; - outlineWidth?: number; - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Placement updates accepted by `Sweep`. - */ -export interface SweepPlacementOptions { - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Partial config payload accepted by `Sweep.setConfig(...)`. - */ -export type SweepConfigUpdate = Partial< - Omit ->; - -/** - * Alias for `Sweep` placement updates. - */ -export type SweepPlacementUpdate = SweepPlacementOptions; - - -interface ISweepKernelInstance { - set_config_with_caps: ( - ..._args: [Vector3[], Vector3[], boolean, boolean] - ) => void; - set_transform: (..._args: [Vector3, Vector3, Vector3]) => void; - set_anchor: (..._args: [Vector3]) => void; - reset_anchor: () => void; - get_geometry_buffer(): Float64Array; - get_brep_serialized(): string; - get_local_brep_serialized(): string; - get_outline_geometry_buffer(): Float64Array; - get_anchor(): Vector3; -} - -type SweepKernelConstructor = new (..._args: [string]) => ISweepKernelInstance; - - -/** - * Sweep wrapper backed by the kernel sweep primitive. - */ -export class Sweep extends THREE.Mesh { - ogid: string; - options: ISweepOptions = { - path: [ - new Vector3(0, 0, 0), - new Vector3(0, 1, 0), - ], - profile: [ - new Vector3(-0.25, 0, -0.25), - new Vector3(0.25, 0, -0.25), - new Vector3(0.25, 0, 0.25), - new Vector3(-0.25, 0, 0.25), - ], - color: 0x00ff00, - capStart: true, - capEnd: true, - fatOutlines: false, - outlineWidth: 1, - translation: new Vector3(0, 0, 0), - rotation: new Vector3(0, 0, 0), - scale: new Vector3(1, 1, 1), - }; - - private sweep: ISweepKernelInstance; - #outlineMesh: ShapeOutlineMesh | null = null; - private _outlineEnabled = false; - private _fatOutlines = false; - private _outlineWidth = 1; - - set color(color: number) { - this.options.color = color; - if (this.material instanceof THREE.MeshStandardMaterial) { - this.material.color.set(color); - } - } - - constructor(options?: ISweepOptions) { - super(); - - this.ogid = options?.ogid ?? getUUID(); - const sweepExport = (OGKernel as Record)["OGSweep"]; - if (typeof sweepExport !== "function") { - throw new Error( - "OGSweep is not available in the loaded wasm package. Rebuild opengeometry wasm bindings." - ); - } - const SweepKernel = sweepExport as SweepKernelConstructor; - this.sweep = new SweepKernel(this.ogid); - - this.options = { ...this.options, ...options }; - this.options.ogid = this.ogid; - - this.setConfig({ - path: this.options.path.map((point) => point.clone()), - profile: this.options.profile.map((point) => point.clone()), - color: this.options.color, - capStart: this.options.capStart, - capEnd: this.options.capEnd, - fatOutlines: this.options.fatOutlines, - outlineWidth: this.options.outlineWidth, - }); - this.setPlacement({ - translation: this.options.translation?.clone(), - rotation: this.options.rotation?.clone(), - scale: this.options.scale?.clone(), - }); - } - - validateOptions() { - if (!this.options) { - throw new Error("Options are not defined for Sweep"); - } - - if (this.options.path.length < 2) { - throw new Error("Sweep path requires at least 2 points."); - } - - if (this.options.profile.length < 3) { - throw new Error("Sweep profile requires at least 3 points."); - } - } - - setConfig(options: SweepConfigUpdate) { - const nextOptions = { ...this.options, ...options }; - const geometryChanged = - "path" in options || - "profile" in options || - "capStart" in options || - "capEnd" in options; - const colorChanged = "color" in options; - const outlineStyleChanged = - "fatOutlines" in options || - "outlineWidth" in options; - - this.options = nextOptions; - this._fatOutlines = this.options.fatOutlines ?? false; - this._outlineWidth = sanitizeOutlineWidth(this.options.outlineWidth); - this.options.fatOutlines = this._fatOutlines; - this.options.outlineWidth = this._outlineWidth; - - if (geometryChanged) { - this.validateOptions(); - - const path = this.options.path.map((point) => point.clone()); - const profile = this.options.profile.map((point) => point.clone()); - const capStart = this.options.capStart ?? true; - const capEnd = this.options.capEnd ?? true; - - this.sweep.set_config_with_caps(path, profile, capStart, capEnd); - this.generateGeometry(); - return; - } - - if (colorChanged) { - this.color = this.options.color; - } - - if (outlineStyleChanged && this._outlineEnabled) { - this.outline = true; - } - } - - getConfig() { - return this.options; - } - - getAnchor() { - const anchor = this.sweep.get_anchor(); - return new Vector3(anchor.x, anchor.y, anchor.z); - } - - setAnchor(anchor: Vector3) { - this.sweep.set_anchor(anchor.clone()); - this.generateGeometry(); - } - - resetAnchor() { - this.sweep.reset_anchor(); - this.generateGeometry(); - } - - setPlacement(placement: SweepPlacementUpdate) { - this.options.translation = placement.translation?.clone() ?? this.options.translation; - this.options.rotation = placement.rotation?.clone() ?? this.options.rotation; - this.options.scale = placement.scale?.clone() ?? this.options.scale; - - this.sweep.set_transform( - this.options.translation?.clone() ?? new Vector3(0, 0, 0), - this.options.rotation?.clone() ?? new Vector3(0, 0, 0), - this.options.scale?.clone() ?? new Vector3(1, 1, 1) - ); - this.generateGeometry(); - } - - setTransform(translation: Vector3, rotation: Vector3, scale: Vector3) { - this.setPlacement({ - translation, - rotation, - scale, - }); - } - - setTranslation(translation: Vector3) { - this.setPlacement({ translation }); - } - - setRotation(rotation: Vector3) { - this.setPlacement({ rotation }); - } - - setScale(scale: Vector3) { - this.setPlacement({ scale }); - } - - getPlacement() { - return clonePlacement({ - translation: this.options.translation, - rotation: this.options.rotation, - scale: this.options.scale, - }); - } - - getEditCapabilities() { - return createParametricEditCapabilities("sweep", "sweep"); - } - - canConvertToFreeform() { - return true; - } - - toFreeform(id: string = this.ogid) { - if (!this.canConvertToFreeform()) { - throw new Error("This entity cannot be converted to freeform."); - } - return createFreeformGeometry(this.sweep.get_local_brep_serialized(), { - id, - placement: this.getPlacement(), - }); - } - - cleanGeometry() { - this.geometry.dispose(); - if (Array.isArray(this.material)) { - this.material.forEach((mat) => mat.dispose()); - } else { - this.material.dispose(); - } - } - - generateGeometry() { - const bufferData = Array.from(this.sweep.get_geometry_buffer()); - - this.writePositionsToGeometry(this.geometry, bufferData); - - if (this.material instanceof THREE.MeshStandardMaterial) { - this.material.color.set(this.options.color); - // this.material.transparent = true; - // this.material.opacity = 0.6; - } else { - if (Array.isArray(this.material)) { - this.material.forEach((material) => material.dispose()); - } else { - this.material.dispose(); - } - - this.material = new THREE.MeshStandardMaterial({ - color: this.options.color, - // transparent: true, - // opacity: 0.6, - }); - } - - this.geometry.computeVertexNormals(); - this.geometry.computeBoundingBox(); - this.geometry.computeBoundingSphere(); - - if (!this._outlineEnabled) { - return; - } - - const outlineData = Array.from(this.sweep.get_outline_geometry_buffer()); - if (!this.#outlineMesh) { - this.outline = true; - return; - } - - if (this.#outlineMesh instanceof THREE.LineSegments) { - this.writePositionsToGeometry(this.#outlineMesh.geometry, outlineData); - this.#outlineMesh.geometry.computeBoundingBox(); - this.#outlineMesh.geometry.computeBoundingSphere(); - return; - } - - const fatGeometry = this.#outlineMesh.geometry as LineSegmentsGeometry; - fatGeometry.setPositions(outlineData); - - const fatOutline = this.#outlineMesh as ShapeOutlineMesh & { - computeLineDistances?: () => void; - }; - fatOutline.computeLineDistances?.(); - } - - getBrep() { - const brepData = this.sweep.get_brep_serialized(); - if (!brepData) { - throw new Error("Brep data is not available for Sweep."); - } - return JSON.parse(brepData); - } - - set outline(enable: boolean) { - this._outlineEnabled = enable; - this.clearOutlineMesh(); - if (enable) { - const outlineData = Array.from(this.sweep.get_outline_geometry_buffer()); - this.#outlineMesh = createShapeOutlineMesh({ - positions: outlineData, - color: 0x000000, - fatOutlines: this._fatOutlines, - outlineWidth: this._outlineWidth, - }); - - this.add(this.#outlineMesh); - } - } - - get outline() { - return this._outlineEnabled; - } - - set fatOutlines(value: boolean) { - this._fatOutlines = value; - this.options.fatOutlines = value; - if (this._outlineEnabled) { - this.outline = true; - } - } - - get fatOutlines() { - return this._fatOutlines; - } - - set outlineWidth(value: number) { - this._outlineWidth = sanitizeOutlineWidth(value); - this.options.outlineWidth = this._outlineWidth; - if (this._outlineEnabled) { - this.outline = true; - } - } - - get outlineWidth() { - return this._outlineWidth; - } - - private clearOutlineMesh() { - if (!this.#outlineMesh) { - return; - } - this.remove(this.#outlineMesh); - disposeShapeOutlineMesh(this.#outlineMesh); - this.#outlineMesh = null; - } - - discardGeometry() { - this.geometry.dispose(); - } - - private writePositionsToGeometry( - geometry: THREE.BufferGeometry, - positions: number[] - ) { - const existing = geometry.getAttribute("position"); - if ( - !(existing instanceof THREE.BufferAttribute) || - existing.itemSize !== 3 || - existing.count !== positions.length / 3 - ) { - geometry.setAttribute( - "position", - new THREE.Float32BufferAttribute(positions, 3) - ); - return; - } - - const array = existing.array as Float32Array; - array.set(positions); - existing.needsUpdate = true; - } -} diff --git a/main/opengeometry-three/src/shapes/wedge.ts b/main/opengeometry-three/src/shapes/wedge.ts deleted file mode 100644 index 34830ff..0000000 --- a/main/opengeometry-three/src/shapes/wedge.ts +++ /dev/null @@ -1,371 +0,0 @@ -import { OGWedge, Vector3 } from "../../../opengeometry/pkg/opengeometry"; -import * as THREE from "three"; -import { LineSegmentsGeometry } from "three/examples/jsm/lines/LineSegmentsGeometry.js"; -import { getUUID } from "../utils/randomizer"; -import { - createShapeOutlineMesh, - disposeShapeOutlineMesh, - sanitizeOutlineWidth, - ShapeOutlineMesh, -} from "./outline-utils"; -import { - clonePlacement, - createParametricEditCapabilities, -} from "../editor"; -import { createFreeformGeometry } from "../freeform"; - -/** - * Construction options for a wedge/prism shape. - */ -export interface IWedgeOptions { - ogid?: string; - center: Vector3; - width: number; - height: number; - depth: number; - color: number; - fatOutlines?: boolean; - outlineWidth?: number; - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Placement updates accepted by `Wedge`. - */ -export interface WedgePlacementOptions { - translation?: Vector3; - rotation?: Vector3; - scale?: Vector3; -} - -/** - * Partial config payload accepted by `Wedge.setConfig(...)`. - */ -export type WedgeConfigUpdate = Partial< - Omit ->; - -/** - * Alias for `Wedge` placement updates. - */ -export type WedgePlacementUpdate = WedgePlacementOptions; - -/** - * Wedge wrapper backed by the kernel wedge primitive. - */ -export class Wedge extends THREE.Mesh { - ogid: string; - options: IWedgeOptions = { - center: new Vector3(0, 0, 0), - width: 1, - height: 1, - depth: 1, - color: 0x00ff00, - fatOutlines: false, - outlineWidth: 1, - translation: new Vector3(0, 0, 0), - rotation: new Vector3(0, 0, 0), - scale: new Vector3(1, 1, 1), - }; - - private wedge: OGWedge; - #outlineMesh: ShapeOutlineMesh | null = null; - private _outlineEnabled = false; - private _fatOutlines = false; - private _outlineWidth = 1; - - set color(color: number) { - this.options.color = color; - if (this.material instanceof THREE.MeshStandardMaterial) { - this.material.color.set(color); - } - } - - constructor(options?: IWedgeOptions) { - super(); - this.ogid = options?.ogid ?? getUUID(); - this.wedge = new OGWedge(this.ogid); - - this.options = { ...this.options, ...options }; - this.options.ogid = this.ogid; - - this.setConfig({ - center: this.options.center.clone(), - width: this.options.width, - height: this.options.height, - depth: this.options.depth, - color: this.options.color, - fatOutlines: this.options.fatOutlines, - outlineWidth: this.options.outlineWidth, - }); - this.setPlacement({ - translation: this.options.translation?.clone(), - rotation: this.options.rotation?.clone(), - scale: this.options.scale?.clone(), - }); - } - - validateOptions() { - if (!this.options) { - throw new Error("Options are not defined for Wedge"); - } - } - - setConfig(options: WedgeConfigUpdate) { - this.validateOptions(); - - const nextOptions = { ...this.options, ...options }; - const geometryChanged = - "center" in options || - "width" in options || - "height" in options || - "depth" in options; - const colorChanged = "color" in options; - const outlineStyleChanged = - "fatOutlines" in options || - "outlineWidth" in options; - - this.options = nextOptions; - this._fatOutlines = this.options.fatOutlines ?? false; - this._outlineWidth = sanitizeOutlineWidth(this.options.outlineWidth); - this.options.fatOutlines = this._fatOutlines; - this.options.outlineWidth = this._outlineWidth; - - if (geometryChanged) { - const { width, height, depth, center } = this.options; - this.wedge.set_config(center.clone(), width, height, depth); - this.generateGeometry(); - return; - } - - if (colorChanged) { - this.color = this.options.color; - } - - if (outlineStyleChanged && this._outlineEnabled) { - this.outline = true; - } - } - - getConfig() { - return this.options; - } - - getAnchor() { - const anchor = this.wedge.get_anchor(); - return new Vector3(anchor.x, anchor.y, anchor.z); - } - - setPlacement(placement: WedgePlacementUpdate) { - this.options.translation = placement.translation?.clone() ?? this.options.translation; - this.options.rotation = placement.rotation?.clone() ?? this.options.rotation; - this.options.scale = placement.scale?.clone() ?? this.options.scale; - - this.wedge.set_transform( - this.options.translation?.clone() ?? new Vector3(0, 0, 0), - this.options.rotation?.clone() ?? new Vector3(0, 0, 0), - this.options.scale?.clone() ?? new Vector3(1, 1, 1) - ); - this.generateGeometry(); - } - - setTransform(translation: Vector3, rotation: Vector3, scale: Vector3) { - this.setPlacement({ - translation, - rotation, - scale, - }); - } - - setTranslation(translation: Vector3) { - this.setPlacement({ translation }); - } - - setRotation(rotation: Vector3) { - this.setPlacement({ rotation }); - } - - setScale(scale: Vector3) { - this.setPlacement({ scale }); - } - - getPlacement() { - return clonePlacement({ - translation: this.options.translation, - rotation: this.options.rotation, - scale: this.options.scale, - }); - } - - getEditCapabilities() { - return createParametricEditCapabilities("wedge", "wedge"); - } - - canConvertToFreeform() { - return true; - } - - toFreeform(id: string = this.ogid) { - if (!this.canConvertToFreeform()) { - throw new Error("This entity cannot be converted to freeform."); - } - return createFreeformGeometry(this.wedge.get_local_brep_serialized(), { - id, - placement: this.getPlacement(), - }); - } - - cleanGeometry() { - this.geometry.dispose(); - if (Array.isArray(this.material)) { - this.material.forEach((mat) => mat.dispose()); - } else { - this.material.dispose(); - } - } - - generateGeometry() { - const bufferData = Array.from(this.wedge.get_geometry_buffer()); - - this.writePositionsToGeometry(this.geometry, bufferData); - - if (this.material instanceof THREE.MeshStandardMaterial) { - this.material.color.set(this.options.color); - // this.material.transparent = true; - // this.material.opacity = 0.6; - } else { - if (Array.isArray(this.material)) { - this.material.forEach((material) => material.dispose()); - } else { - this.material.dispose(); - } - - this.material = new THREE.MeshStandardMaterial({ - color: this.options.color, - // transparent: true, - // opacity: 0.6, - }); - } - - this.geometry.computeVertexNormals(); - this.geometry.computeBoundingBox(); - this.geometry.computeBoundingSphere(); - - if (!this._outlineEnabled) { - return; - } - - const outlineData = Array.from(this.wedge.get_outline_geometry_buffer()); - if (!this.#outlineMesh) { - this.outline = true; - return; - } - - if (this.#outlineMesh instanceof THREE.LineSegments) { - this.writePositionsToGeometry(this.#outlineMesh.geometry, outlineData); - this.#outlineMesh.geometry.computeBoundingBox(); - this.#outlineMesh.geometry.computeBoundingSphere(); - return; - } - - const fatGeometry = this.#outlineMesh.geometry as LineSegmentsGeometry; - fatGeometry.setPositions(outlineData); - - const fatOutline = this.#outlineMesh as ShapeOutlineMesh & { - computeLineDistances?: () => void; - }; - fatOutline.computeLineDistances?.(); - } - - getBrepData() { - const brepData = this.wedge.get_brep_serialized(); - if (!brepData) { - throw new Error("Brep data is not available for this wedge."); - } - return JSON.parse(brepData); - } - - set outline(enable: boolean) { - this._outlineEnabled = enable; - this.clearOutlineMesh(); - if (enable) { - const outlineData = Array.from(this.wedge.get_outline_geometry_buffer()); - this.#outlineMesh = createShapeOutlineMesh({ - positions: outlineData, - color: 0x000000, - fatOutlines: this._fatOutlines, - outlineWidth: this._outlineWidth, - }); - this.add(this.#outlineMesh); - } - } - - get outline() { - return this._outlineEnabled; - } - - set fatOutlines(value: boolean) { - this._fatOutlines = value; - this.options.fatOutlines = value; - if (this._outlineEnabled) { - this.outline = true; - } - } - - get fatOutlines() { - return this._fatOutlines; - } - - set outlineWidth(value: number) { - this._outlineWidth = sanitizeOutlineWidth(value); - this.options.outlineWidth = this._outlineWidth; - if (this._outlineEnabled) { - this.outline = true; - } - } - - get outlineWidth() { - return this._outlineWidth; - } - - get outlineMesh() { - return this.#outlineMesh; - } - - private clearOutlineMesh() { - if (!this.#outlineMesh) { - return; - } - this.remove(this.#outlineMesh); - disposeShapeOutlineMesh(this.#outlineMesh); - this.#outlineMesh = null; - } - - discardGeometry() { - this.geometry.dispose(); - } - - private writePositionsToGeometry( - geometry: THREE.BufferGeometry, - positions: number[] - ) { - const existing = geometry.getAttribute("position"); - if ( - !(existing instanceof THREE.BufferAttribute) || - existing.itemSize !== 3 || - existing.count !== positions.length / 3 - ) { - geometry.setAttribute( - "position", - new THREE.Float32BufferAttribute(positions, 3) - ); - return; - } - - const array = existing.array as Float32Array; - array.set(positions); - existing.needsUpdate = true; - } -} diff --git a/main/opengeometry-three/src/testing/internals.ts b/main/opengeometry-three/src/testing/internals.ts new file mode 100644 index 0000000..59f786b --- /dev/null +++ b/main/opengeometry-three/src/testing/internals.ts @@ -0,0 +1,35 @@ +import { OGError } from '../errors'; +import { forceNextBucket } from '../rendering/lod/lod-controller'; +import { WorkerBackend, type WorkerSend } from '../rendering/worker-backend'; +import { runtime } from '../runtime/runtime-state'; + +export { register, unregister } from '../bodies/body-registry'; +export { ensureGeometry, flush, wanted } from '../rendering/geometry/geometry-scheduler'; +export { noteDisplayed } from '../rendering/geometry/render-pass'; +export { wantedBucket } from '../rendering/lod/deflection'; +export { parseWorkerMessage } from '../rendering/worker/message-guards'; +export { emit } from '../runtime/event-bus'; +export { currentRuntime, runtime } from '../runtime/runtime-state'; +export { worldGraph as graph } from '../world-graph/world-graph-client'; + +export function activeBackend(): 'worker' | 'inline' { return runtime().provider.activeBackend; } + +export function flushCount(): number { return runtime().flushes; } + +export function pinWantedBucket(shapeId: string, bucket: number): void { forceNextBucket(runtime(), shapeId, bucket); } + +export function workerSendLog(): WorkerSend[] { + const provider = runtime().provider; + return provider instanceof WorkerBackend ? [...provider.sendLog] : []; +} + +export function postToWorker(message: unknown, options: { awaitCrash?: boolean } = {}): Promise { + const provider = runtime().provider; + const worker = provider instanceof WorkerBackend ? provider.worker : undefined; + if (!worker) return Promise.reject(new OGError('WorkerFailure', 'testing', 'no live worker')); + const crashed = options.awaitCrash + ? new Promise((resolve) => { worker.addEventListener('error', () => { resolve(); }, { once: true }); }) + : Promise.resolve(); + worker.postMessage(message); + return crashed; +} diff --git a/main/opengeometry-three/src/utils/event.ts b/main/opengeometry-three/src/utils/event.ts deleted file mode 100644 index 9e36c40..0000000 --- a/main/opengeometry-three/src/utils/event.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Original source: Simple event handler by [Jason Kleban](https://gist.github.com/JasonKleban/50cee44960c225ac1993c922563aa540). Keep in mind that if you want to remove it later, you might want to declare the callback as an object. If you want to maintain the reference to `this`, you will need to declare the callback as an arrow function. - * Adapted to TypeScript by ThatOpen Company - */ - - -export class Event { - /** - * Add a callback to this event instance. - * @param handler - the callback to be added to this event. - */ - add(handler: T extends void ? { (): void } : { (data: T): void }): void { - this.handlers.push(handler); - } - - /** - * Removes a callback from this event instance. - * @param handler - the callback to be removed from this event. - */ - remove(handler: T extends void ? { (): void } : { (data: T): void }): void { - this.handlers = this.handlers.filter((h) => h !== handler); - } - - /** Triggers all the callbacks assigned to this event. */ - trigger = (data?: T) => { - const handlers = this.handlers.slice(0); - for (const handler of handlers) { - (handler as (data?: T) => void)(data); - } - }; - - /** Gets rid of all the suscribed events. */ - reset() { - this.handlers.length = 0; - } - - private handlers: (T extends void ? { (): void } : { (data: T): void })[] = - []; -} \ No newline at end of file diff --git a/main/opengeometry-three/src/utils/randomizer.ts b/main/opengeometry-three/src/utils/randomizer.ts deleted file mode 100644 index e9fdee8..0000000 --- a/main/opengeometry-three/src/utils/randomizer.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; - -export function getUUID() { - const uuid = uuidv4(); - return uuid; -} diff --git a/main/opengeometry-three/src/world-graph/codec.ts b/main/opengeometry-three/src/world-graph/codec.ts new file mode 100644 index 0000000..9f4b923 --- /dev/null +++ b/main/opengeometry-three/src/world-graph/codec.ts @@ -0,0 +1,199 @@ +import type { ShapeReport } from '../dto/boolean-report'; +import type { Bounds } from '../dto/bounds'; +import type { Brep } from '../dto/brep'; +import type { ChangeSet } from '../dto/change-set'; +import type { Creation } from '../dto/creation'; +import type { MarkStats } from '../dto/mark-stats'; +import type { NodeInfo } from '../dto/node-info'; +import type { Placement } from '../dto/placement'; +import type { StepExport, StepExportReport } from '../dto/step-export'; +import { OGError } from '../errors'; + +type Fields = Record; +type Check = (value: unknown) => value is T; +type ChangePacket = { changesJson: string; matrices: Float64Array }; +type StepGlue = { text: string; reportJson: string }; + +const BREP_TOPOLOGY = ['vertices', 'edges', 'halfedges', 'loops', 'faces', 'wires', 'shells'] as const; +const STEP_COUNTS = ['products', 'solids', 'faces', 'edges', 'pcurvelessEdges', 'entities', 'bytes'] as const; + +export function encode(value: unknown): string { + return JSON.stringify(value); +} + +export function scope(options?: { instances?: 'all' }): string { + return encode(options?.instances === 'all' ? 'AllInstances' : 'Node'); +} + +export function operationParams(kind: string, params: Record, call: string): Record { + if (kind === 'Extrude') { + const holes = params['holes'] ?? []; + if (!Array.isArray(holes)) throw new OGError('InvalidParameter', call, 'holes must be a list of bodies'); + const holeIds = holes.map((hole: unknown) => ogIdOf(hole, call, 'hole')); + return { kind, profile: ogIdOf(params['profile'], call, 'profile'), holes: holeIds, distance: params['distance'] }; + } + if (kind === 'Sweep') { + return { kind, profile: ogIdOf(params['profile'], call, 'profile'), path: ogIdOf(params['path'], call, 'path') }; + } + return { kind, ...params }; +} + +export function polylinePoints(params: Record, call: string): Float64Array { + const points = params['points']; + if (!Array.isArray(points) || !points.every(isPoint)) { + throw new OGError('InvalidParameter', call, 'points must be a list of [x, y, z] points'); + } + return new Float64Array(points.flat()); +} + +function decode(text: string, call: string, payload: string, check: Check): T { + return checked(parsed(text), call, payload, check); +} + +export function decodeNodeInfo(text: string, call: string): NodeInfo { + return decode(text, call, 'node info', isNodeInfo); +} + +export function decodeCreation(text: string, call: string): Creation { + return decode(text, call, 'creation', (value): value is Creation => isFields(value) && isString(value['ogId'])); +} + +export function decodePlacement(text: string, call: string): Placement { + return decode(text, call, 'placement', isPlacement); +} + +export function decodeChildren(text: string, call: string): string[] { + return decode(text, call, 'children', isStrings); +} + +export function decodeParent(text: string, call: string): string | null { + return decode(text, call, 'parent', (value): value is string | null => value === null || isString(value)); +} + +export function decodeBounds(text: string, call: string): Bounds | null { + return decode(text, call, 'bounds', (value): value is Bounds | null => value === null || isNumbers(value, 6)); +} + +export function decodeBrep(text: string, call: string): Brep { + return decode(text, call, 'brep', isBrep); +} + +export function decodeShapeReport(text: string, call: string): ShapeReport | null { + return decode(text, call, 'report', (value): value is ShapeReport | null => value === null || isShapeReport(value)); +} + +export function decodeMarkStats(text: string, call: string): MarkStats { + return decode(text, call, 'mark stats', (value): value is MarkStats => isFields(value) + && isNumber(value['liveMarks']) && isNumber(value['retainedRevisions'])); +} + +export function decodeDisplayBuckets(text: string, call: string): { floor: number; static: number } { + return decode(text, call, 'display buckets', (value): value is { floor: number; static: number } => isFields(value) + && Number.isFinite(value['floor']) && Number.isFinite(value['static'])); +} + +export function decodeChangeSet(packet: unknown, call: string): { changes: ChangeSet; matrices: Float64Array } { + const glue = checked(packet, call, 'change packet', isChangePacket); + return { changes: decode(glue.changesJson, call, 'change set', isChangeSet), matrices: glue.matrices }; +} + +export function decodeChanges(text: string, call: string): ChangeSet { + return decode(text, call, 'change set', isChangeSet); +} + +export function decodeStepExport(value: unknown, call: string): StepExport { + const glue = checked(value, call, 'STEP export', isStepGlue); + return { text: glue.text, report: decode(glue.reportJson, call, 'STEP report', isStepReport) }; +} + +function checked(value: unknown, call: string, payload: string, check: Check): T { + if (!check(value)) throw new OGError('InvalidGeometry', call, `malformed ${payload}`); + return value; +} + +function parsed(text: string): unknown { + try { + return JSON.parse(text); + } catch { + return undefined; + } +} + +function ogIdOf(value: unknown, call: string, role: string): string { + if (isFields(value) && isString(value['ogId'])) return value['ogId']; + throw new OGError('InvalidParameter', call, `${role} must be a body`); +} + +function isFields(value: unknown): value is Fields { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +function isString(value: unknown): value is string { + return typeof value === 'string'; +} + +function isNumber(value: unknown): value is number { + return typeof value === 'number'; +} + +function isStrings(value: unknown): value is string[] { + return Array.isArray(value) && value.every(isString); +} + +function isNumbers(value: unknown, length: number): boolean { + return Array.isArray(value) && value.length === length && value.every(isNumber); +} + +function isPoint(value: unknown): value is [number, number, number] { + return isNumbers(value, 3); +} + +function isNodeInfo(value: unknown): value is NodeInfo { + return isFields(value) && isString(value['ogId']) && isNumber(value['handle']) && isNumber(value['generation']) + && isNullable(value['parent'], isString) && isStrings(value['children']) && isString(value['kind']) + && isNullable(value['shapeId'], isString) && isNullable(value['shapeRevision'], isNumber) + && isNullable(value['bodyType'], isString); +} + +function isNullable(value: unknown, check: (item: unknown) => boolean): boolean { + return value === null || check(value); +} + +function isPlacement(value: unknown): value is Placement { + return isFields(value) && isPoint(value['origin']) && isPoint(value['xDirection']) && isPoint(value['normal']) + && isNumber(value['scale']); +} + +function isBrep(value: unknown): value is Brep { + if (!isFields(value) || !isFields(value['topology'])) return false; + const topology = value['topology']; + return isString(value['id']) && isNumber(value['revision']) && isNumber(value['schema_version']) + && isFields(value['geometry']) && isFields(value['accuracy']) && isFields(value['quality']) + && Array.isArray(value['solids']) && BREP_TOPOLOGY.every((key) => Array.isArray(topology[key])); +} + +function isShapeReport(value: unknown): value is ShapeReport { + return isFields(value) && isFields(value['report']) && isStrings(value['handlers']) && isNumber(value['revision']); +} + +function isChangePacket(value: unknown): value is ChangePacket { + return isFields(value) && isString(value['changesJson']) && value['matrices'] instanceof Float64Array; +} + +function isChangeSet(value: unknown): value is ChangeSet { + const isChanges = (list: unknown): boolean => Array.isArray(list) + && list.every((item: unknown) => isFields(item) && isString(item['ogId']) && isNumber(item['handle']) + && isNumber(item['generation']) && isNullable(item['shapeId'], isString) + && isNullable(item['shapeRevision'], isNumber)); + return isFields(value) && isNumber(value['revision']) + && isChanges(value['added']) && isChanges(value['changed']) && isChanges(value['removed']); +} + +function isStepGlue(value: unknown): value is StepGlue { + return isFields(value) && isString(value['text']) && isString(value['reportJson']); +} + +function isStepReport(value: unknown): value is StepExportReport { + return isFields(value) && STEP_COUNTS.every((key) => isNumber(value[key])) + && Array.isArray(value['bodies']) && Array.isArray(value['skipped']); +} diff --git a/main/opengeometry-three/src/world-graph/world-graph-client.ts b/main/opengeometry-three/src/world-graph/world-graph-client.ts new file mode 100644 index 0000000..152a78a --- /dev/null +++ b/main/opengeometry-three/src/world-graph/world-graph-client.ts @@ -0,0 +1,25 @@ +import type { NodeInfo } from '../dto/node-info'; +import type { OGWorldGraph } from '../kernel/kernel-loader'; +import { call } from '../kernel/kernel-session'; +import { runtime } from '../runtime/runtime-state'; +import { decodeCreation, decodeDisplayBuckets, decodeNodeInfo } from './codec'; + +export function worldGraph(): OGWorldGraph { + return runtime().graph; +} + +export function node(ogId: string, label: string): NodeInfo { + return decodeNodeInfo(call(label, () => worldGraph().node(ogId)), label); +} + +export function checkHandle(label: string, target: { handle: number; generation: number }): void { + call(label, () => worldGraph().nodeByHandle(target.handle, target.generation)); +} + +export function creation(value: string, label: string): string { + return decodeCreation(value, label).ogId; +} + +export function displayBuckets(ogId: string, label: string): { floor: number; static: number } { + return decodeDisplayBuckets(call(label, () => worldGraph().displayBuckets(ogId)), label); +} diff --git a/main/opengeometry-three/src/world/world-graph.ts b/main/opengeometry-three/src/world/world-graph.ts deleted file mode 100644 index 87cf534..0000000 --- a/main/opengeometry-three/src/world/world-graph.ts +++ /dev/null @@ -1,349 +0,0 @@ -import * as THREE from "three"; -import { OGWorldGraph } from "../../../opengeometry/pkg/opengeometry"; -import { uniformScale } from "../rendering/analytic-lod"; -import type { AnalyticFrame, AnalyticProjectionCamera, AnalyticProjectionHlr, AnalyticSolid } from "../shapes/analytic-solid"; - -export interface WorldTransform { - frame: AnalyticFrame; - scale: number; -} - -export interface WorldNodeOptions { - id: string; - parent?: string; - definition?: string; - local?: THREE.Matrix4; - kind?: string; -} - -export interface WorldProjectedLine { - start: { x: number; y: number }; - end: { x: number; y: number }; - stroke_width: number | null; - stroke_color: [number, number, number] | null; - class?: string; -} - -export interface WorldProjectedLines { - name: string | null; - lines: WorldProjectedLine[]; -} - -export interface WorldProjectedSegment { - geometry: unknown; - class: string; - layer?: string | null; - source_entity_id?: string | null; -} - -export interface WorldProjectedScene { - name: string | null; - segments: WorldProjectedSegment[]; -} - -export interface WorldProjectionView { - id: string; - camera: AnalyticProjectionCamera; - hlr?: AnalyticProjectionHlr; -} - -export interface WorldExportedMesh { - bytes: Uint8Array; - report: unknown; -} - -export interface WorldExportedFile { - text: string; - report: unknown; -} - -export type WorldClashKind = "hard" | "touch" | "unresolved"; - -export interface WorldClash { - a: string; - b: string; - kind: WorldClashKind; - parts: [string, string]; - detail?: string; -} - -export interface WorldClashOptions { - depth?: number; -} - -export interface WorldProximity { - a: string; - b: string; - distance: number; - errorBound: number; - parts: [string, string]; - points: [[number, number, number], [number, number, number]]; -} - -export type WorldGraphErrorCode = - | "duplicate_node" - | "unknown_node" - | "duplicate_definition" - | "unknown_definition" - | "definition_in_use" - | "cycle" - | "invalid_input" - | "export" - | "geometry" - | "unknown"; - -export class WorldGraphError extends Error { - readonly code: WorldGraphErrorCode; - readonly detail: unknown; - - constructor(code: WorldGraphErrorCode, message: string, detail?: unknown) { - super(message); - this.name = "WorldGraphError"; - this.code = code; - this.detail = detail; - } -} - -const KNOWN_CODES = new Set([ - "duplicate_node", "unknown_node", "duplicate_definition", "unknown_definition", - "definition_in_use", "cycle", "invalid_input", "export", "geometry", -]); - -function toWorldGraphError(raw: unknown): WorldGraphError { - const text = typeof raw === "string" ? raw : raw instanceof Error ? raw.message : String(raw); - try { - const payload = JSON.parse(text) as unknown; - if (typeof payload === "object" && payload !== null && !Array.isArray(payload)) { - const variant = Object.keys(payload)[0]; - if (variant) { - const detail = (payload as Record)[variant]; - const snake = variant.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase() as WorldGraphErrorCode; - const code = KNOWN_CODES.has(snake) ? snake : "unknown"; - const message = typeof detail === "string" ? `${snake.replace(/_/g, " ")}: ${detail}` : snake.replace(/_/g, " "); - return new WorldGraphError(code, message, detail); - } - } - } catch { - return new WorldGraphError("unknown", text, raw); - } - return new WorldGraphError("unknown", text, raw); -} - -function kernelCall(call: () => T): T { - try { - return call(); - } catch (error) { - throw toWorldGraphError(error); - } -} - -export function matrixToWorldTransform(matrix: THREE.Matrix4): WorldTransform { - const scale = uniformScale(matrix); - const elements = matrix.elements; - const axis = (column: number): [number, number, number] => [ - elements[column * 4] / scale, elements[column * 4 + 1] / scale, elements[column * 4 + 2] / scale, - ]; - return { - frame: { origin: [elements[12], elements[13], elements[14]], x: axis(0), y: axis(1), z: axis(2) }, - scale, - }; -} - -export class WorldGraph { - private readonly kernel = new OGWorldGraph(); - private readonly bindings = new Map(); - private bindingOrder: [string, THREE.Object3D][] | undefined; - private disposed = false; - - get nodeCount(): number { - this.checkActive(); - return this.kernel.node_count(); - } - - get definitionCount(): number { - this.checkActive(); - return this.kernel.definition_count(); - } - - define(id: string, solid: AnalyticSolid): void { - this.checkActive(); - const brep = solid.getBrepSerialized(); - kernelCall(() => this.kernel.define(id, brep)); - } - - defineLegacy(id: string, brepSerialized: string): void { - this.checkActive(); - kernelCall(() => this.kernel.define_legacy(id, brepSerialized)); - } - - hasDefinition(id: string): boolean { - this.checkActive(); - return this.kernel.has_definition(id); - } - - undefine(id: string): void { - this.checkActive(); - kernelCall(() => this.kernel.undefine(id)); - } - - addNode(options: WorldNodeOptions): void { - this.checkActive(); - const spec = JSON.stringify({ - id: options.id, - parent: options.parent, - definition: options.definition, - local: matrixToWorldTransform(options.local ?? new THREE.Matrix4()), - kind: options.kind, - }); - kernelCall(() => this.kernel.add_node(spec)); - } - - hasNode(id: string): boolean { - this.checkActive(); - return this.kernel.has_node(id); - } - - setLocal(id: string, local: THREE.Matrix4): void { - this.checkActive(); - const transform = JSON.stringify(matrixToWorldTransform(local)); - kernelCall(() => this.kernel.set_local(id, transform)); - this.sync(); - } - - reparent(id: string, parent?: string): void { - this.checkActive(); - kernelCall(() => this.kernel.reparent(id, parent)); - this.sync(); - } - - removeNode(id: string): void { - this.checkActive(); - kernelCall(() => this.kernel.remove_node(id)); - for (const nodeId of [...this.bindings.keys()]) { - if (!this.kernel.has_node(nodeId)) this.bindings.delete(nodeId); - } - this.bindingOrder = undefined; - } - - getWorldMatrix(id: string): THREE.Matrix4 { - this.checkActive(); - return new THREE.Matrix4().fromArray(kernelCall(() => this.kernel.get_world(id))); - } - - getWorldBounds(id: string): THREE.Box3 | null { - this.checkActive(); - const bounds = kernelCall(() => this.kernel.get_world_bounds(id)); - if (bounds.length !== 6) return null; - return new THREE.Box3( - new THREE.Vector3(bounds[0], bounds[1], bounds[2]), - new THREE.Vector3(bounds[3], bounds[4], bounds[5]), - ); - } - - clash(itemsA: string[], itemsB: string[], options: WorldClashOptions = {}): WorldClash[] { - this.checkActive(); - const depth = options.depth ?? 0.001; - return JSON.parse(kernelCall(() => this.kernel.clash(JSON.stringify(itemsA), JSON.stringify(itemsB), depth))) as WorldClash[]; - } - - clearance(itemsA: string[], itemsB: string[], clearance: number): WorldProximity[] { - this.checkActive(); - return JSON.parse(kernelCall(() => this.kernel.clearance(JSON.stringify(itemsA), JSON.stringify(itemsB), clearance))) as WorldProximity[]; - } - - distance(itemA: string, itemB: string, tolerance = 0.001): WorldProximity | null { - this.checkActive(); - return JSON.parse(kernelCall(() => this.kernel.distance(itemA, itemB, tolerance))) as WorldProximity | null; - } - - projectLines(items: string[], camera: AnalyticProjectionCamera, hlr?: AnalyticProjectionHlr, deflection = 0.01): WorldProjectedLines { - this.checkActive(); - const hlrJson = hlr === undefined ? undefined : JSON.stringify(hlr); - return JSON.parse(kernelCall(() => this.kernel.project_lines(JSON.stringify(items), JSON.stringify(camera), hlrJson, deflection))) as WorldProjectedLines; - } - - projectViews(items: string[], views: WorldProjectionView[], deflection = 0.01): Record { - this.checkActive(); - return JSON.parse(kernelCall(() => this.kernel.project_views(JSON.stringify(items), JSON.stringify(views), deflection))) as Record; - } - - exportStl(items: string[], config?: Record): WorldExportedMesh { - this.checkActive(); - const result = kernelCall(() => this.kernel.export_stl(JSON.stringify(items), config && JSON.stringify(config))); - try { - return { bytes: result.bytes, report: JSON.parse(result.reportJson) }; - } finally { - result.free(); - } - } - - exportStep(items: string[], config?: Record): WorldExportedFile { - this.checkActive(); - const result = kernelCall(() => this.kernel.export_step(JSON.stringify(items), config && JSON.stringify(config))); - try { - return { text: result.text, report: JSON.parse(result.reportJson) }; - } finally { - result.free(); - } - } - - exportIfc(items: string[], config?: Record): WorldExportedFile { - this.checkActive(); - const result = kernelCall(() => this.kernel.export_ifc(JSON.stringify(items), config && JSON.stringify(config))); - try { - return { text: result.text, report: JSON.parse(result.reportJson) }; - } finally { - result.free(); - } - } - - bind(object: THREE.Object3D, id: string): void { - this.checkActive(); - if (!this.kernel.has_node(id)) throw new WorldGraphError("unknown_node", `unknown node: ${id}`, id); - object.matrixAutoUpdate = false; - this.bindings.set(id, object); - this.bindingOrder = undefined; - this.syncObject(id, object); - } - - unbind(id: string): void { - this.checkActive(); - this.bindings.delete(id); - this.bindingOrder = undefined; - } - - sync(): void { - this.checkActive(); - this.bindingOrder ??= [...this.bindings].sort(([, a], [, b]) => depth(a) - depth(b)); - for (const [id, object] of this.bindingOrder) this.syncObject(id, object); - } - - dispose(): void { - if (this.disposed) return; - this.disposed = true; - this.bindings.clear(); - this.bindingOrder = undefined; - this.kernel.free(); - } - - private syncObject(id: string, object: THREE.Object3D): void { - const world = this.getWorldMatrix(id); - if (object.parent) { - object.parent.updateWorldMatrix(true, false); - world.premultiply(new THREE.Matrix4().copy(object.parent.matrixWorld).invert()); - } - object.matrix.copy(world); - object.matrix.decompose(object.position, object.quaternion, object.scale); - object.updateMatrixWorld(true); - } - - private checkActive(): void { - if (this.disposed) throw new WorldGraphError("invalid_input", "WorldGraph has been disposed"); - } -} - -function depth(object: THREE.Object3D): number { - let count = 0; - for (let parent = object.parent; parent; parent = parent.parent) count += 1; - return count; -} diff --git a/main/opengeometry-three/tests/browser/pages/acceptance-probes.ts b/main/opengeometry-three/tests/browser/pages/acceptance-probes.ts new file mode 100644 index 0000000..13d1ffb --- /dev/null +++ b/main/opengeometry-three/tests/browser/pages/acceptance-probes.ts @@ -0,0 +1,244 @@ +import * as THREE from 'three'; +import { + OpenGeometry, Solid, OGError, OG_PRIMITIVE_CUBOID, OG_OPERATION_SUBTRACT, OG_TRANSFORM_TRANSLATE, +} from '../../../../dist/index.js'; +import { activeBackend, graph, postToWorker, runtime, workerSendLog } from '../../../../dist/testing.js'; +import type { + CrashResult, FailureResult, InstanceMemory, MemoryResult, PixelResult, ReuseResult, StaleResult, TransactionResult, +} from '../support/fixture-types'; +import { recordedErrors, required } from '../support/test-page'; + +export type AcceptancePage = { + renderer: THREE.WebGLRenderer; + scene: THREE.Scene; + camera: THREE.PerspectiveCamera; + wall: Solid; + rail: Solid; +}; + +export async function memoryProbe(page: AcceptancePage): Promise { + const { renderer, scene, camera } = page; + renderer.render(scene, camera); + await OpenGeometry.settled(); + const baseline = renderer.info.memory.geometries; + const plain = await instanceMemory(page, 'memory', false); + const outlined = await instanceMemory(page, 'memory-outline', true); + return { + baseline, ...plain, outlineOne: outlined.one, outlineShared: outlined.shared, outlineUnique: outlined.unique, + outlineAfter: outlined.after, + }; +} + +async function instanceMemory(page: AcceptancePage, prefix: string, outline: boolean): Promise { + const { renderer, scene, camera } = page; + const size = { width: 2, height: 2, depth: 2 }; + const target = new Solid(OG_PRIMITIVE_CUBOID, size, { ogId: `${prefix}-target`, appearance: { outline } }); + const tool = new Solid(OG_PRIMITIVE_CUBOID, { width: 0.5, height: 0.5, depth: 0.5 }, { ogId: `${prefix}-tool` }); + tool.transform(OG_TRANSFORM_TRANSLATE, { offset: [0.5, 0, 0] }); + scene.add(target); + renderer.render(scene, camera); + await OpenGeometry.settled(); + renderer.render(scene, camera); + await OpenGeometry.settled(); + const one = renderer.info.memory.geometries; + const copy = target.instance({ ogId: `${prefix}-instance` }); + copy.setAppearance({ outline }); + scene.add(copy); + renderer.render(scene, camera); + await OpenGeometry.settled(); + const shared = renderer.info.memory.geometries; + const sameGeometry = target.surface.geometry === copy.surface.geometry; + const sharedDetails = { target: target.record?.key, copy: copy.record?.key, same: sameGeometry }; + copy.makeUnique(); + copy.operate(OG_OPERATION_SUBTRACT, { tools: [tool] }); + renderer.render(scene, camera); + await OpenGeometry.settled(); + const unique = renderer.info.memory.geometries; + copy.dispose(); + target.dispose(); + tool.dispose(); + renderer.render(scene, camera); + return { one, shared, unique, after: renderer.info.memory.geometries, sharedDetails }; +} + +function errorCodes(): string[] { + return recordedErrors().map((event) => (event instanceof OGError ? event.code : String(event))); +} + +export function tessellateSends(shapeId: string): number { + return workerSendLog().filter((send) => send.shapeId === shapeId).length; +} + +export async function workerCrashProbe(page: AcceptancePage): Promise { + const { renderer, scene, camera, wall } = page; + const record = wall.record; + await postToWorker({ kind: 'throw' }, { awaitCrash: true }); + const info = JSON.parse(graph().node(wall.ogId)) as { shapeId: string; shapeRevision: number }; + await runtime().provider.ensureSnapshot(info.shapeId, info.shapeRevision); + const afterRestart = activeBackend(); + await postToWorker({ kind: 'throw' }, { awaitCrash: true }); + const errors = errorCodes(); + const afterFallback = activeBackend(); + const body = new Solid(OG_PRIMITIVE_CUBOID, { width: 0.3, height: 0.3, depth: 0.3 }, { ogId: 'after-fallback' }); + scene.add(body); + renderer.render(scene, camera); + const drawnInSameRender = Boolean(body.record); + body.dispose(); + return { afterRestart, afterFallback, sameRecord: wall.record === record, errors, drawnInSameRender }; +} + +export async function workerFailureProbe(page: AcceptancePage): Promise { + const { failed } = await OpenGeometry.settled(); + const hasRecord = Boolean(page.wall.record); + return { failed: failed.length, hasRecord, errors: errorCodes(), backend: activeBackend() }; +} + +export function placementPixelProbe(page: AcceptancePage): PixelResult { + const { renderer } = page; + const isolated = new THREE.Scene(); + isolated.background = new THREE.Color(0xffffff); + isolated.add(new THREE.HemisphereLight(0xffffff, 0x526070, 2)); + const light = new THREE.DirectionalLight(0xffffff, 2); + light.position.set(5, 8, 5); + isolated.add(light); + const view = new THREE.OrthographicCamera(-2, 2, 1.5, -1.5, 0.1, 20); + view.position.set(0, 1, 5); + view.lookAt(0, 0.5, 0); + const cube = new Solid(OG_PRIMITIVE_CUBOID, { width: 1, height: 1, depth: 1 }, { ogId: 'pixel-cube' }); + isolated.add(cube); + renderer.render(isolated, view); + OpenGeometry.flush({ geometry: 'sync' }); + const ray = new THREE.Raycaster(new THREE.Vector3(0, 0.5, 5), new THREE.Vector3(0, 0, -1)); + const syncHit = ray.intersectObject(cube.surface).length > 0; + renderer.render(isolated, view); + const gl = renderer.getContext(); + const pixel = (): number[] => { + const value = new Uint8Array(4); + gl.readPixels(320, 240, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, value); + return Array.from(value); + }; + const before = pixel(); + const geometry = cube.surface.geometry; + const position = geometry.getAttribute('position'); + if (!(position instanceof THREE.BufferAttribute)) throw new Error('Test page expected a position BufferAttribute'); + const version = position.version; + const revision = cube.getBrep().revision; + cube.transform(OG_TRANSFORM_TRANSLATE, { offset: [4, 0, 0] }); + renderer.render(isolated, view); + const after = pixel(); + const kernel = graph().worldMatrix(cube.ogId); + const matrixMatches = cube.matrixWorld.elements + .every((value, index) => Math.abs(value - (kernel[index] ?? Number.NaN)) <= 1e-12); + const geometryMatches = cube.surface.geometry === geometry && geometry.getAttribute('position') === position + && position.version === version && cube.getBrep().revision === revision; + cube.dispose(); + return { before, after, matrixMatches, geometryMatches, syncHit }; +} + +export async function onDemandProbe(page: AcceptancePage): Promise<{ events: number; appeared: boolean }> { + const { renderer, scene, camera } = page; + const body = new Solid(OG_PRIMITIVE_CUBOID, { width: 0.4, height: 0.4, depth: 0.4 }, { ogId: 'on-demand' }); + scene.add(body); + let events = 0; + const ready = new Promise((resolve) => { + const unsubscribe = OpenGeometry.on('geometry', (event) => { + const value = event as { ogIds: string[] }; + if (value.ogIds.includes(body.ogId)) { + events++; + unsubscribe(); + renderer.render(scene, camera); + resolve(); + } + }); + }); + renderer.render(scene, camera); + await ready; + const appeared = Boolean(body.record && body.surface.geometry.getAttribute('position')); + body.dispose(); + return { events, appeared }; +} + +export async function staleWorkerProbe(page: AcceptancePage): Promise { + const { renderer, scene, camera } = page; + const body = new Solid(OG_PRIMITIVE_CUBOID, { width: 1, height: 1, depth: 1 }, { ogId: 'rapid-rebuild' }); + scene.add(body); + renderer.render(scene, camera); + await OpenGeometry.settled(); + const info = JSON.parse(graph().node(body.ogId)) as { shapeId: string }; + const before = tessellateSends(info.shapeId); + for (let i = 0; i < 10; i++) { + body.rebuild(OG_PRIMITIVE_CUBOID, { width: 1 + i * 0.1, height: 1, depth: 1 }); + renderer.render(scene, camera); + } + await OpenGeometry.settled(); + const after = tessellateSends(info.shapeId); + const current = body.getBrep().revision; + const displayed = body.record?.revision; + body.dispose(); + return { current, displayed, jobs: after - before }; +} + +export async function snapshotResendProbe(page: AcceptancePage): Promise<{ triangles: number }> { + const info = JSON.parse(graph().node(page.rail.ogId)) as { shapeId: string; shapeRevision: number }; + const provider = runtime().provider; + await provider.ensureSnapshot(info.shapeId, info.shapeRevision); + await postToWorker({ kind: 'drop', shapeId: info.shapeId, revision: info.shapeRevision }); + const buffers = await provider.request({ + shapeId: info.shapeId, revision: info.shapeRevision, bucket: required(page.rail.record, 'a rail record').bucket * 2, + maxTriangles: 2_000_000, priority: 0, generation: 1, + }); + return { triangles: buffers.triangles }; +} + +export function transactionProbe(): TransactionResult { + const body = new Solid(OG_PRIMITIVE_CUBOID, { width: 1, height: 1, depth: 1 }, { ogId: 'transaction-cube' }); + const before = JSON.stringify(body.getBrep()); + const placement = JSON.stringify(body.getPlacement()); + try { + let threw = false; + try { + OpenGeometry.transaction(() => { + OpenGeometry.transaction(() => { body.dispose(); }); + throw new Error('outer rollback'); + }); + } catch (error) { threw = error instanceof Error && error.message === 'outer rollback'; } + const revived = JSON.stringify(body.getBrep()) === before && !body.inLimbo; + const dryResult = OpenGeometry.transaction(() => { + body.transform(OG_TRANSFORM_TRANSLATE, { offset: [3, 0, 0] }); + return 'trial'; + }, { dryRun: true }); + const dryRestored = JSON.stringify(body.getPlacement()) === placement; + let thenableCode = ''; + try { OpenGeometry.transaction(() => ({ then: () => undefined })); } + catch (error) { if (error instanceof OGError) thenableCode = error.code; } + return { threw, revived, dryResult, dryRestored, thenableCode }; + } finally { body.dispose(); } +} + +export async function reuseProbe(page: AcceptancePage): Promise { + const { renderer, scene, camera } = page; + const errors: unknown[] = []; + const stopErrors = OpenGeometry.on('error', (event) => { errors.push(event); }); + const size = { width: 1, height: 1, depth: 1 }; + const first = new Solid(OG_PRIMITIVE_CUBOID, size, { ogId: 'reuse-cube' }); + try { + scene.add(first); + renderer.render(scene, camera); + await OpenGeometry.settled(); + const mark = OpenGeometry.mark(); + first.dispose(); + const second = new Solid(OG_PRIMITIVE_CUBOID, size, { ogId: 'reuse-cube' }); + scene.add(second); + renderer.render(scene, camera); + await OpenGeometry.settled(); + mark.rollback(); + mark.release(); + renderer.render(scene, camera); + await OpenGeometry.settled(); + const revived = first.parent === scene && !first.inLimbo && Boolean(first.record); + return { revived, gone: second.parent === null && !second.inLimbo, errors }; + } finally { + stopErrors(); + first.dispose(); + } +} diff --git a/main/opengeometry-three/tests/browser/pages/acceptance.html b/main/opengeometry-three/tests/browser/pages/acceptance.html new file mode 100644 index 0000000..0d5a49a --- /dev/null +++ b/main/opengeometry-three/tests/browser/pages/acceptance.html @@ -0,0 +1,23 @@ + + + + + + OpenGeometry 2.5 acceptance + + +
Building acceptance scene
+ + + + diff --git a/main/opengeometry-three/tests/browser/pages/acceptance.ts b/main/opengeometry-three/tests/browser/pages/acceptance.ts new file mode 100644 index 0000000..2dbb186 --- /dev/null +++ b/main/opengeometry-three/tests/browser/pages/acceptance.ts @@ -0,0 +1,193 @@ +import * as THREE from 'three'; +import { + OpenGeometry, SystemAssembly, Wire, Solid, OGError, + OG_PRIMITIVE_RECTANGLE, OG_PRIMITIVE_CUBOID, OG_PRIMITIVE_POLYLINE, OG_PRIMITIVE_CIRCLE, + OG_OPERATION_EXTRUDE, OG_OPERATION_SWEEP, OG_OPERATION_SUBTRACT, + OG_TRANSFORM_TRANSLATE, OG_TRANSFORM_ROTATE, OG_TRANSFORM_PLACE, +} from '../../../../dist/index.js'; +import { activeBackend, graph } from '../../../../dist/testing.js'; +import { + bootKernel, createRenderer, publishFixture, releasePage, required, statusElement, +} from '../support/test-page'; +import { + memoryProbe, onDemandProbe, placementPixelProbe, reuseProbe, snapshotResendProbe, staleWorkerProbe, + transactionProbe, workerCrashProbe, workerFailureProbe, +} from './acceptance-probes'; +import { coarserRetryProbe, lodHysteresisProbe, lodProbe, orbitProbe } from './acceptance/probes/lod'; +import { buildStorey, storeyPerformanceProbe } from './acceptance/probes/storey'; + +type Point = [number, number, number]; + +window.addEventListener('error', (event) => { statusElement().textContent = `Error: ${event.message}`; }); +window.addEventListener('unhandledrejection', (event) => { + statusElement().textContent = `Error: ${String(event.reason)}`; +}); + +await bootKernel(); +const SCENE = new THREE.Scene(); +SCENE.background = new THREE.Color(0xf7f8fb); +SCENE.add(new THREE.HemisphereLight(0xffffff, 0x526070, 2)); +const LIGHT = new THREE.DirectionalLight(0xffffff, 2); +LIGHT.position.set(5, 8, 5); +SCENE.add(LIGHT); +const CAMERA = new THREE.PerspectiveCamera(50, 640 / 480, 0.1, 1000); +CAMERA.position.set(9, 9, 14); +CAMERA.lookAt(2, 3, 1); +const RENDERER = createRenderer(640, 480); +const BODIES: Solid[] = []; + +function assertNear(actual: number, expected: number, tolerance: number, label: string): void { + if (Math.abs(actual - expected) > tolerance) { + throw new Error(`${label}: ${String(actual)} expected ${String(expected)}`); + } +} + +function meshValue(values: ArrayLike, index: number): number { + return required(values[index], `mesh value ${String(index)}`); +} + +function volume(body: Solid, bucket = 0.01): number { + const info = JSON.parse(graph().node(body.ogId)) as { shapeId: string }; + const buffers = graph().buffers(info.shapeId, bucket, 2_000_000) as { + positions: Float32Array; indices: Uint32Array; origin: Float64Array; + }; + let sum = 0; + for (let i = 0; i < buffers.indices.length; i += 3) { + const a = meshValue(buffers.indices, i) * 3; + const b = meshValue(buffers.indices, i + 1) * 3; + const c = meshValue(buffers.indices, i + 2) * 3; + const point = (index: number): Point => [ + meshValue(buffers.positions, index) + meshValue(buffers.origin, 0), + meshValue(buffers.positions, index + 1) + meshValue(buffers.origin, 1), + meshValue(buffers.positions, index + 2) + meshValue(buffers.origin, 2), + ]; + const pointA = point(a); const pointB = point(b); const pointC = point(c); + sum += pointA[0] * (pointB[1] * pointC[2] - pointB[2] * pointC[1]) + + pointA[1] * (pointB[2] * pointC[0] - pointB[0] * pointC[2]) + + pointA[2] * (pointB[0] * pointC[1] - pointB[1] * pointC[0]); + } + return Math.abs(sum / 6); +} + +const LEVEL = new SystemAssembly({ ogId: 'level-1' }); +const PROFILE = new Wire(OG_PRIMITIVE_RECTANGLE, { width: 6, breadth: 0.2 }, { ogId: 'wall-profile' }); +const WALL = new Solid(OG_OPERATION_EXTRUDE, { profile: PROFILE, distance: 3 }, { ogId: 'wall-1' }); +const CUTTER = new Solid(OG_PRIMITIVE_CUBOID, { width: 0.9, height: 2.1, depth: 0.4 }, { ogId: 'door-cutter' }); +const BASE_VOLUME = volume(WALL); +assertNear(BASE_VOLUME, 3.6, 1e-5, 'wall base volume'); +CUTTER.transform(OG_TRANSFORM_TRANSLATE, { offset: [2, 0, 0] }); +WALL.operate(OG_OPERATION_SUBTRACT, { tools: [CUTTER] }); +const CUT_VOLUME = volume(WALL); +assertNear(CUT_VOLUME, 3.222, 1e-5, 'wall cut volume'); +const PATH = new Wire( + OG_PRIMITIVE_POLYLINE, { points: [[0, 1, 1], [4, 1, 1], [4, 1, 4]], closed: false }, { ogId: 'rail-path' }, +); +const DISC = new Wire(OG_PRIMITIVE_CIRCLE, { radius: 0.05 }, { + ogId: 'rail-disc', plane: { origin: [0, 1, 1], normal: [1, 0, 0], xDirection: [0, 0, 1] }, +}); +const RAIL = new Solid(OG_OPERATION_SWEEP, { profile: DISC, path: PATH }, { ogId: 'rail-1' }); +const RAIL_VOLUME = volume(RAIL, 2 ** -14); +assertNear(RAIL_VOLUME, Math.PI * 0.05 ** 2 * 7, 5e-5, 'swept rail volume'); +LEVEL.addChild([PROFILE, WALL, CUTTER, PATH, DISC, RAIL]); +SCENE.add(WALL, RAIL); +BODIES.push(WALL, RAIL); +const SHAPE_REVISIONS = [WALL.getBrep().revision, RAIL.getBrep().revision]; +LEVEL.transform(OG_TRANSFORM_PLACE, { origin: [0, 3, 0] }); +RAIL.transform(OG_TRANSFORM_ROTATE, { axis: [0, 1, 0], degrees: 90 }); +if (WALL.getBrep().revision !== SHAPE_REVISIONS[0] || RAIL.getBrep().revision !== SHAPE_REVISIONS[1]) { + throw new Error('Placement changed a shape revision'); +} +const RAIL_BOUNDS = required(RAIL.getBounds(), 'rail bounds'); +const MOVED_BOUNDS = required(WALL.getBounds(), 'wall bounds'); +assertNear(MOVED_BOUNDS[1], 3, 1e-6, 'moved wall low y'); +WALL.rebuild(OG_OPERATION_EXTRUDE, { profile: PROFILE, distance: 4 }); +const REBUILT_VOLUME = volume(WALL); +assertNear(REBUILT_VOLUME, 4.8, 1e-5, 'wall rebuild volume'); +WALL.operate(OG_OPERATION_SUBTRACT, { tools: [CUTTER] }); +const RECUT_VOLUME = volume(WALL); +assertNear(RECUT_VOLUME, 4.422, 1e-5, 'wall recut volume'); +let coverageGap = false; +const BEFORE_COVERAGE_GAP = performance.now(); +try { + RAIL.operate(OG_OPERATION_SUBTRACT, { tools: [WALL] }); +} catch (error) { coverageGap = error instanceof OGError && error.code === 'CoverageGap'; } +const COVERAGE_GAP_MS = performance.now() - BEFORE_COVERAGE_GAP; +if (!coverageGap) throw new Error('CoverageGap pair did not return CoverageGap'); +const BEFORE_TRIAL = JSON.stringify(WALL.getBrep()); +const MARK = OpenGeometry.mark(); +WALL.rebuild(OG_OPERATION_EXTRUDE, { profile: PROFILE, distance: 5 }); +MARK.rollback(); +MARK.release(); +if (JSON.stringify(WALL.getBrep()) !== BEFORE_TRIAL) throw new Error('Mark rollback changed the wall'); +const RAILS: Solid[] = [RAIL]; +for (let i = 2; i <= 11; i++) { + const copy = RAIL.instance({ ogId: `rail-${String(i)}` }); + copy.transform(OG_TRANSFORM_TRANSLATE, { offset: [0, 0, i * 2] }); + RAILS.push(copy); + BODIES.push(copy); + SCENE.add(copy); +} +const REPARENT = new SystemAssembly({ ogId: 'reparent' }); +const TEST_CHILD = new Solid(OG_PRIMITIVE_CUBOID, { width: 0.1, height: 0.1, depth: 0.1 }, { ogId: 'reparent-test' }); +const BEFORE_KEEP = required(TEST_CHILD.getBounds(), 'child bounds'); +REPARENT.transform(OG_TRANSFORM_TRANSLATE, { offset: [10, 0, 0] }); +REPARENT.addChild([TEST_CHILD], { keepWorld: true }); +assertNear(required(TEST_CHILD.getBounds(), 'child bounds')[0], BEFORE_KEEP[0], 1e-6, 'keepWorld'); +REPARENT.removeChild(TEST_CHILD, { keepWorld: true }); +REPARENT.addChild([TEST_CHILD]); +assertNear(required(TEST_CHILD.getBounds(), 'child bounds')[0], BEFORE_KEEP[0] + 10, 1e-6, 'default local reparent'); +TEST_CHILD.dispose(); +CUTTER.dispose(); +RENDERER.render(SCENE, CAMERA); +await OpenGeometry.settled(); +const FIRST_EXPORT = await OpenGeometry.exportStep({ nodes: [LEVEL] }); +const SECOND_EXPORT = await OpenGeometry.exportStep({ nodes: [LEVEL] }); +if (FIRST_EXPORT.text !== SECOND_EXPORT.text || FIRST_EXPORT.report.products !== 12) { + throw new Error('Level STEP export is not deterministic'); +} +const SKIPPED = FIRST_EXPORT.report.skipped; +const SKIPPED_WIRES = ['wall-profile', 'rail-path', 'rail-disc']; +if (SKIPPED.length !== 3 || !SKIPPED_WIRES.every((id) => SKIPPED.some((item) => item.ogId === id))) { + throw new Error('STEP wire skips differ'); +} +if (FIRST_EXPORT.report.pcurvelessEdges <= 0) throw new Error('Swept rail lost pcurve-less edges'); +const RAIL_PCURVELESS = FIRST_EXPORT.report.bodies + .filter((body) => body.ogId.startsWith('rail-')) + .map((body) => body.pcurvelessEdges); +const RESULT = { + baseVolume: BASE_VOLUME, cutVolume: CUT_VOLUME, railVolume: RAIL_VOLUME, rebuiltVolume: REBUILT_VOLUME, + recutVolume: RECUT_VOLUME, railBounds: RAIL_BOUNDS, wallBounds: MOVED_BOUNDS, coverageGap, + coverageGapMs: COVERAGE_GAP_MS, railPcurveless: RAIL_PCURVELESS, products: FIRST_EXPORT.report.products, + skipped: SKIPPED.length, + pcurvelessEdges: FIRST_EXPORT.report.pcurvelessEdges, stepBytes: FIRST_EXPORT.text.length, +}; +const PAGE = { renderer: RENDERER, scene: SCENE, camera: CAMERA, wall: WALL, rail: RAIL }; + +publishFixture('ogAcceptance', { + bodies: BODIES, renderer: RENDERER, scene: SCENE, camera: CAMERA, level: LEVEL, wall: WALL, rail: RAIL, rails: RAILS, + result: RESULT, + get backend() { return activeBackend(); }, + flush: () => { OpenGeometry.flush(); }, + settled: () => OpenGeometry.settled(), + render: () => { RENDERER.render(SCENE, CAMERA); }, + exportStep: (options = {}) => OpenGeometry.exportStep({ nodes: [LEVEL], ...options }), + buildStorey: () => buildStorey(PAGE), + storeyPerformanceProbe, + memoryProbe: () => memoryProbe(PAGE), + lodProbe: () => lodProbe(PAGE), + workerCrashProbe: () => workerCrashProbe(PAGE), + workerFailureProbe: () => workerFailureProbe(PAGE), + placementPixelProbe: () => placementPixelProbe(PAGE), + onDemandProbe: () => onDemandProbe(PAGE), + staleWorkerProbe: () => staleWorkerProbe(PAGE), + snapshotResendProbe: () => snapshotResendProbe(PAGE), + lodHysteresisProbe: () => lodHysteresisProbe(PAGE), + orbitProbe: () => orbitProbe(PAGE), + coarserRetryProbe, + transactionProbe, + reuseProbe: () => reuseProbe(PAGE), + dispose: () => { + for (const body of [...BODIES].reverse()) { try { body.dispose(); } catch { continue; } } + releasePage(RENDERER); + }, +}, 'Acceptance ready'); diff --git a/main/opengeometry-three/tests/browser/pages/acceptance/probes/lod.ts b/main/opengeometry-three/tests/browser/pages/acceptance/probes/lod.ts new file mode 100644 index 0000000..a2e0824 --- /dev/null +++ b/main/opengeometry-three/tests/browser/pages/acceptance/probes/lod.ts @@ -0,0 +1,116 @@ +import * as THREE from 'three'; +import { OpenGeometry, Solid, OG_PRIMITIVE_CUBOID } from '../../../../../../dist/index.js'; +import * as TESTING from '../../../../../../dist/testing.js'; +import type { RetryResult } from '../../../support/fixture-types'; +import { required } from '../../../support/test-page'; +import { tessellateSends, type AcceptancePage } from '../../acceptance-probes'; + +type RetryWarning = NonNullable; + +function wallBucket(page: AcceptancePage): number { + return required(page.wall.record, 'a wall record').bucket; +} + +async function renderSettled(page: AcceptancePage): Promise { + page.renderer.render(page.scene, page.camera); + await OpenGeometry.settled(); + return wallBucket(page); +} + +function aimAt(camera: THREE.PerspectiveCamera, centre: THREE.Vector3): void { + camera.lookAt(centre); + camera.updateMatrixWorld(); +} + +export async function lodProbe(page: AcceptancePage): Promise<{ before: number; after: number }> { + const before = await renderSettled(page); + const centre = new THREE.Vector3(2, 3, 1); + page.camera.position.sub(centre).multiplyScalar(0.25).add(centre); + aimAt(page.camera, centre); + return { before, after: await renderSettled(page) }; +} + +function placeForTarget(page: AcceptancePage, target: number): void { + const { renderer, camera, wall } = page; + const box = new THREE.Box3().setFromArray(required(wall.getBounds(), 'wall bounds')); + const centre = box.getCenter(new THREE.Vector3()); + const extent = box.getSize(new THREE.Vector3()).length() / 2; + const height = renderer.getDrawingBufferSize(new THREE.Vector2()).y; + const depth = target * height / Math.tan(THREE.MathUtils.degToRad(camera.getEffectiveFOV()) / 2); + const direction = camera.position.clone().sub(centre).normalize(); + camera.position.copy(centre).addScaledVector(direction, depth + extent); + aimAt(camera, centre); +} + +export async function lodHysteresisProbe(page: AcceptancePage): Promise<{ p: number; buckets: number[] }> { + const home = page.camera.position.clone(); + const p = await renderSettled(page); + const buckets: number[] = []; + for (const target of [3.96 * p, 4.04 * p, 0.76 * 4 * p, 0.74 * 4 * p]) { + placeForTarget(page, target); + buckets.push(await renderSettled(page)); + } + page.camera.position.copy(home); + aimAt(page.camera, new THREE.Vector3(2, 3, 1)); + await renderSettled(page); + return { p, buckets }; +} + +export async function orbitProbe( + page: AcceptancePage, +): Promise<{ jobs: number; buckets: number[]; sendBuckets: number[] }> { + const { renderer, scene, camera } = page; + const shapeId = required(page.wall.lastInfo.shapeId, 'a wall shape'); + const buckets = [await renderSettled(page)]; + const before = tessellateSends(shapeId); + const start = performance.now(); + OpenGeometry.setCameraMotion(true); + while (performance.now() - start <= 150) { + await new Promise((resolve) => requestAnimationFrame(() => { resolve(); })); + camera.position.x += 0.02; + aimAt(camera, new THREE.Vector3(2, 3, 1)); + renderer.render(scene, camera); + buckets.push(wallBucket(page)); + } + await OpenGeometry.settled(); + OpenGeometry.setCameraMotion(false); + buckets.push(wallBucket(page)); + const sends = TESTING.workerSendLog().filter((send) => send.shapeId === shapeId).slice(before); + return { jobs: sends.length, buckets, sendBuckets: sends.map((send) => send.bucket) }; +} + +function pinWantedBucket(shapeId: string, bucket: number): void { + const pin: unknown = Reflect.get(TESTING, 'pinWantedBucket'); + if (typeof pin !== 'function') throw new Error('Test page expected pinWantedBucket in dist/testing.js'); + Reflect.apply(pin, undefined, [shapeId, bucket]); +} + +function kernelFloor(body: Solid): number { + const buckets: unknown = JSON.parse(TESTING.graph().displayBuckets(body.ogId)); + const floor: unknown = typeof buckets === 'object' && buckets !== null ? Reflect.get(buckets, 'floor') : undefined; + if (typeof floor !== 'number') throw new Error('Test page expected a display bucket floor'); + return floor; +} + +function isRetryWarning(value: unknown): value is RetryWarning { + return typeof value === 'object' && value !== null && typeof Reflect.get(value, 'code') === 'string' + && typeof Reflect.get(value, 'bucket') === 'number' && typeof Reflect.get(value, 'retryBucket') === 'number'; +} + +export async function coarserRetryProbe(): Promise { + const body = new Solid(OG_PRIMITIVE_CUBOID, { width: 1, height: 1, depth: 1 }, { ogId: 'retry-cube' }); + let warning: RetryWarning | undefined; + const errors: unknown[] = []; + const stopWarnings = OpenGeometry.on('warning', (event) => { if (isRetryWarning(event)) warning = event; }); + const stopErrors = OpenGeometry.on('error', (event) => { errors.push(event); }); + try { + pinWantedBucket(required(body.lastInfo.shapeId, 'a shape id'), kernelFloor(body) / 2); + TESTING.ensureGeometry(body); + await OpenGeometry.settled(); + return { warning, bucket: body.record?.bucket, errors }; + } finally { + stopWarnings(); + stopErrors(); + body.dispose(); + } +} diff --git a/main/opengeometry-three/tests/browser/pages/acceptance/probes/storey.ts b/main/opengeometry-three/tests/browser/pages/acceptance/probes/storey.ts new file mode 100644 index 0000000..224a4fa --- /dev/null +++ b/main/opengeometry-three/tests/browser/pages/acceptance/probes/storey.ts @@ -0,0 +1,133 @@ +import * as THREE from 'three'; +import { + OpenGeometry, SystemAssembly, Wire, Solid, + OG_PRIMITIVE_RECTANGLE, OG_PRIMITIVE_CUBOID, OG_OPERATION_EXTRUDE, OG_OPERATION_SUBTRACT, OG_TRANSFORM_TRANSLATE, +} from '../../../../../../dist/index.js'; +import { flushCount } from '../../../../../../dist/testing.js'; +import type { StoreyPerformance, StoreyResult } from '../../../support/fixture-types'; +import { required } from '../../../support/test-page'; +import type { AcceptancePage } from '../../acceptance-probes'; + +type StoreyRender = Pick; + +const WALLS = 200; +const RAIL_INSTANCES = 1000; +const OPENING = { width: 0.6, height: 2.0, depth: 0.4 }; + +let storeyData: { root: SystemAssembly; bodies: Solid[] } | undefined; + +function nextFrame(): Promise { + return new Promise((resolve) => requestAnimationFrame(() => { resolve(); })); +} + +function grid(index: number, columns: number): [number, number, number] { + return [(index % columns) * 8, 0, Math.floor(index / columns) * 8]; +} + +function buildWall(index: number, members: (Wire | Solid)[]): Solid { + const name = String(index); + const profile = new Wire(OG_PRIMITIVE_RECTANGLE, { width: 6, breadth: 0.2 }, { ogId: `storey-profile-${name}` }); + const wall = new Solid(OG_OPERATION_EXTRUDE, { profile, distance: 3 }, { ogId: `storey-wall-${name}` }); + const cutters: Solid[] = []; + for (let opening = 0; opening < 4; opening++) { + const cutter = new Solid(OG_PRIMITIVE_CUBOID, OPENING, { ogId: `storey-opening-${name}-${String(opening)}` }); + cutter.transform(OG_TRANSFORM_TRANSLATE, { offset: [-2.1 + 1.4 * opening, 0, 0] }); + cutters.push(cutter); + } + wall.operate(OG_OPERATION_SUBTRACT, { tools: cutters }); + for (const cutter of cutters) cutter.dispose(); + wall.transform(OG_TRANSFORM_TRANSLATE, { offset: grid(index, 20) }); + members.push(profile, wall); + return wall; +} + +async function buildWalls(members: (Wire | Solid)[]): Promise { + const walls: Solid[] = []; + for (let index = 0; index < WALLS; index++) { + walls.push(buildWall(index, members)); + if (index % 10 === 9) await nextFrame(); + } + return walls; +} + +async function buildRails(rail: Solid): Promise { + const rails: Solid[] = []; + for (let index = 0; index < RAIL_INSTANCES; index++) { + const copy = rail.instance({ ogId: `storey-rail-${String(index)}` }); + copy.transform(OG_TRANSFORM_TRANSLATE, { offset: grid(index, 40) }); + rails.push(copy); + if (index % 50 === 0) await nextFrame(); + } + return rails; +} + +function openingCount(wall: Solid): number { + const cuts = wall.getBrep().topology.faces.filter((face) => face.provenance.role === 'Cut'); + return new Set(cuts.flatMap((face) => face.provenance.sources.map((source) => source.body))).size; +} + +function openingRange(walls: Solid[]): { min: number; max: number } { + const counts = walls.map(openingCount); + return { min: Math.min(...counts), max: Math.max(...counts) }; +} + +function storeyCamera(bodies: Solid[]): THREE.PerspectiveCamera { + const box = new THREE.Box3(); + for (const body of bodies) { + box.union(new THREE.Box3().setFromArray(required(body.getBounds(), `${body.ogId} bounds`))); + } + const sphere = box.getBoundingSphere(new THREE.Sphere()); + const camera = new THREE.PerspectiveCamera(50, 640 / 480); + const distance = sphere.radius / Math.sin(THREE.MathUtils.degToRad(camera.fov / 2)); + camera.position.copy(sphere.center).addScaledVector(new THREE.Vector3(1, 1.2, 1).normalize(), distance); + camera.near = (distance - sphere.radius) * 0.9; + camera.far = (distance + sphere.radius) * 1.1; + camera.lookAt(sphere.center); + camera.updateProjectionMatrix(); + camera.updateMatrixWorld(); + return camera; +} + +async function renderStorey(page: AcceptancePage, camera: THREE.PerspectiveCamera): Promise { + const beforeRenderFlush = flushCount(); + const beforeRender = performance.now(); + page.renderer.render(page.scene, camera); + const renderMs = performance.now() - beforeRender; + const renderFlushes = flushCount() - beforeRenderFlush; + const { failed } = await OpenGeometry.settled(); + page.renderer.render(page.scene, camera); + return { renderMs, renderFlushes, geometryCount: page.renderer.info.memory.geometries, failed: failed.length }; +} + +export async function buildStorey(page: AcceptancePage): Promise { + const storey = new SystemAssembly({ ogId: 'storey' }); + const members: (Wire | Solid)[] = []; + const walls = await buildWalls(members); + const rails = await buildRails(page.rail); + storey.addChild([...members, ...rails]); + storeyData = { root: storey, bodies: [...walls, ...rails] }; + page.scene.add(...walls, ...rails); + const rendered = await renderStorey(page, storeyCamera(storeyData.bodies)); + return { + walls: walls.length, uniqueWalls: walls.filter((wall) => wall.getInstanceCount() === 1).length, + openingsPerWall: openingRange(walls), instances: rails.length, flushCount: flushCount(), ...rendered, + }; +} + +export async function storeyPerformanceProbe(): Promise { + if (!storeyData) throw new Error('buildStorey must run first'); + const startTransform = performance.now(); + storeyData.root.transform(OG_TRANSFORM_TRANSLATE, { offset: [1, 0, 0] }); + const transformMs = performance.now() - startTransform; + const startFlush = performance.now(); + OpenGeometry.flush(); + const flushMs = performance.now() - startFlush; + const transformFlushMs = transformMs + flushMs; + const startStep = performance.now(); + const exported = await OpenGeometry.exportStep({ nodes: storeyData.bodies }); + const stepMs = performance.now() - startStep; + return { + transformFlushMs, transformMs, flushMs, stepMs, bytes: exported.text.length, + products: exported.report.products, entities: exported.report.entities, + }; +} diff --git a/main/opengeometry-three/tests/browser/pages/crash-worker.ts b/main/opengeometry-three/tests/browser/pages/crash-worker.ts new file mode 100644 index 0000000..313a659 --- /dev/null +++ b/main/opengeometry-three/tests/browser/pages/crash-worker.ts @@ -0,0 +1,8 @@ +import '../../../../dist/tessellation-worker.js'; + +self.addEventListener('message', (event: MessageEvent) => { + const data = event.data; + if (typeof data === 'object' && data !== null && Reflect.get(data, 'kind') === 'throw') { + throw new Error('crash page worker threw on request'); + } +}); diff --git a/main/opengeometry-three/tests/browser/pages/quick-start.html b/main/opengeometry-three/tests/browser/pages/quick-start.html new file mode 100644 index 0000000..aadf35e --- /dev/null +++ b/main/opengeometry-three/tests/browser/pages/quick-start.html @@ -0,0 +1,12 @@ + + + + + + OpenGeometry quick start test + + +
Loading kernel
+ + + diff --git a/main/opengeometry-three/tests/browser/pages/quick-start.ts b/main/opengeometry-three/tests/browser/pages/quick-start.ts new file mode 100644 index 0000000..ca660ba --- /dev/null +++ b/main/opengeometry-three/tests/browser/pages/quick-start.ts @@ -0,0 +1,45 @@ +import * as THREE from 'three'; +import { + OpenGeometry, Solid, Wire, + OG_OPERATION_EXTRUDE, OG_OPERATION_SUBTRACT, OG_PRIMITIVE_CUBOID, OG_PRIMITIVE_RECTANGLE, OG_TRANSFORM_TRANSLATE, +} from '../../../../dist/index.js'; +import { activeBackend } from '../../../../dist/testing.js'; +import { createRenderer, publishFixture, releasePage } from '../support/test-page'; + +await OpenGeometry.create({ wasmURL: '/opengeometry_bg.wasm' }, { workerURL: '/tessellation-worker.js' }); +const ERRORS: unknown[] = []; +OpenGeometry.on('error', (event) => { ERRORS.push(event); }); + +const PROFILE = new Wire(OG_PRIMITIVE_RECTANGLE, { width: 6, breadth: 0.2 }); +const WALL = new Solid(OG_OPERATION_EXTRUDE, { profile: PROFILE, distance: 3 }); +const DOOR = new Solid(OG_PRIMITIVE_CUBOID, { width: 0.9, height: 2.1, depth: 0.4 }); +DOOR.transform(OG_TRANSFORM_TRANSLATE, { offset: [2, 0, 0] }); +WALL.operate(OG_OPERATION_SUBTRACT, { tools: [DOOR] }); + +const SCENE = new THREE.Scene(); +SCENE.add(new THREE.HemisphereLight(0xffffff, 0x444444, 2), WALL); +const CAMERA = new THREE.PerspectiveCamera(50, innerWidth / innerHeight, 0.1, 100); +CAMERA.position.set(6, 5, 8); +CAMERA.lookAt(0, 1.5, 0); +const RENDERER = createRenderer(innerWidth, innerHeight); +RENDERER.render(SCENE, CAMERA); +const SETTLED = await OpenGeometry.settled(); +RENDERER.render(SCENE, CAMERA); +RENDERER.setAnimationLoop(() => { RENDERER.render(SCENE, CAMERA); }); + +const STEP = await OpenGeometry.exportStep({ nodes: [WALL], unit: 'millimetre', upAxis: 'Z' }); + +publishFixture('ogQuickStart', { + wall: WALL, + bounds: WALL.getBounds(), + renderer: RENDERER, + settled: SETTLED, + report: STEP.report, + text: STEP.text, + errors: ERRORS, + get backend() { return activeBackend(); }, + dispose: () => { + RENDERER.setAnimationLoop(null); + releasePage(RENDERER); + }, +}, 'Quick start ready'); diff --git a/main/opengeometry-three/tests/browser/pages/smoke.html b/main/opengeometry-three/tests/browser/pages/smoke.html new file mode 100644 index 0000000..28f584c --- /dev/null +++ b/main/opengeometry-three/tests/browser/pages/smoke.html @@ -0,0 +1,12 @@ + + + + + + OpenGeometry smoke test + + +
Loading kernel
+ + + diff --git a/main/opengeometry-three/tests/browser/pages/smoke.ts b/main/opengeometry-three/tests/browser/pages/smoke.ts new file mode 100644 index 0000000..b383550 --- /dev/null +++ b/main/opengeometry-three/tests/browser/pages/smoke.ts @@ -0,0 +1,86 @@ +import * as THREE from 'three'; +import { OpenGeometry, Solid, Wire, OG_PRIMITIVE_CUBOID, OG_PRIMITIVE_RECTANGLE } from '../../../../dist/index.js'; +import { activeBackend, graph } from '../../../../dist/testing.js'; +import type { SmokeFixture } from '../support/fixture-types'; +import { bootKernel, createRenderer, publishFixture, releasePage, required } from '../support/test-page'; +import { reservedNameCollisions } from './smoke/reserved-names'; + +const WORKER_URL = await bootKernel(); +const SCENE = new THREE.Scene(); +SCENE.background = new THREE.Color(0xf7f8fb); +SCENE.add(new THREE.HemisphereLight(0xffffff, 0x526070, 2)); +const LIGHT = new THREE.DirectionalLight(0xffffff, 2); +LIGHT.position.set(5, 8, 5); +SCENE.add(LIGHT); +const CAMERA = new THREE.PerspectiveCamera(60, 1, 0.1, 100); +CAMERA.position.set(3, 3, 5); +CAMERA.lookAt(0, 0.5, 0); +const RENDERER = createRenderer(320, 240); +const BODY = new Solid(OG_PRIMITIVE_CUBOID, { width: 1, height: 1, depth: 1 }, { ogId: 'smoke-cube' }); +const WIRE = new Wire(OG_PRIMITIVE_RECTANGLE, { width: 1, breadth: 1 }, { ogId: 'smoke-wire' }); +SCENE.add(BODY); +RENDERER.render(SCENE, CAMERA); +const READY = await OpenGeometry.settled(); +if (READY.failed.length) throw new Error('Cube geometry did not settle'); +RENDERER.render(SCENE, CAMERA); +const RESERVED = reservedNameCollisions({ solid: BODY, wire: WIRE }); +const WORKER_PROBES = [ + { kind: 'init', request: 0, module: 'invalid' }, { kind: 'nonsense', request: 7 }, { kind: 'snapshot', request: 8 }, +]; + +function sceneHit(): ReturnType { + const origin = new THREE.Vector3(0.9, 0.5, 5); + const ray = new THREE.Raycaster(origin, new THREE.Vector3(0.45, 0.5, 0.5).sub(origin).normalize()); + const hits = ray.intersectObjects(SCENE.children, true); + const first = hits[0]; + return { + hit: first && OpenGeometry.resolveHit(first), + outlineHit: hits.some((hit) => hit.object === BODY.outline), + }; +} + +function nextReply(worker: Worker, message: object): Promise { + return new Promise((resolve) => { + const timer = setTimeout(() => { resolve(undefined); }, 2_000); + worker.onmessage = (event) => { + clearTimeout(timer); + resolve(event.data); + }; + worker.postMessage(message); + }); +} + +publishFixture('ogSmoke', { + body: BODY, renderer: RENDERER, scene: SCENE, camera: CAMERA, + threeRevision: THREE.REVISION, + reservedNameCollisions: RESERVED.collisions, + reservedNamesDerived: RESERVED.derived, + get backend() { return activeBackend(); }, + render: () => { RENDERER.render(SCENE, CAMERA); }, + settled: () => OpenGeometry.settled(), + exportStep: () => OpenGeometry.exportStep({ nodes: [BODY] }), + resolveHit: () => { + const ray = new THREE.Raycaster(); + ray.set(new THREE.Vector3(2, 0.5, 5), new THREE.Vector3(0, 0, -1)); + const hit = ray.intersectObject(BODY.surface)[0]; + return hit && OpenGeometry.resolveHit(hit); + }, + resolveSceneHit: sceneHit, + getInlineBuffers: () => { + const info = JSON.parse(graph().node(BODY.ogId)) as { shapeId: string }; + return graph().buffers(info.shapeId, required(BODY.record, 'a cube record').bucket, 2_000_000); + }, + workerReplies: async () => { + const worker = new Worker(WORKER_URL, { type: 'module' }); + const replies: unknown[] = []; + try { + for (const message of WORKER_PROBES) replies.push(await nextReply(worker, message)); + return replies; + } finally { worker.terminate(); } + }, + dispose: () => { + WIRE.dispose(); + BODY.dispose(); + releasePage(RENDERER); + }, +}, 'Kernel loaded'); diff --git a/main/opengeometry-three/tests/browser/pages/smoke/reserved-names.ts b/main/opengeometry-three/tests/browser/pages/smoke/reserved-names.ts new file mode 100644 index 0000000..37ea34f --- /dev/null +++ b/main/opengeometry-three/tests/browser/pages/smoke/reserved-names.ts @@ -0,0 +1,26 @@ +import * as THREE from 'three'; +import { Solid, Wire } from '../../../../../dist/index.js'; + +const OVERRIDES = new Set(['updateMatrixWorld', 'clone', 'copy']); + +function ownNames(objects: (object | null)[]): Set { + return new Set(objects.flatMap((object) => (object ? Object.getOwnPropertyNames(object) : []))); +} + +export function reservedNameCollisions( + sample: { solid: Solid; wire: Wire }, +): { collisions: string[]; derived: number } { + const reserved = new Set(); + for (let object: object | null = new THREE.Group(); object; object = Reflect.getPrototypeOf(object)) { + for (const key of Object.getOwnPropertyNames(object)) reserved.add(key); + } + const prototypes = ownNames([Reflect.getPrototypeOf(Wire.prototype), Wire.prototype, Solid.prototype]); + prototypes.delete('constructor'); + const group = ownNames([new THREE.Group()]); + const added = [...new Set([...prototypes, ...ownNames([sample.solid, sample.wire])])] + .filter((name) => !group.has(name)); + return { + collisions: added.filter((name) => reserved.has(name) && !OVERRIDES.has(name)), + derived: added.length, + }; +} diff --git a/main/opengeometry-three/tests/browser/specs/examples.spec.ts b/main/opengeometry-three/tests/browser/specs/examples.spec.ts new file mode 100644 index 0000000..2cd2dd5 --- /dev/null +++ b/main/opengeometry-three/tests/browser/specs/examples.spec.ts @@ -0,0 +1,60 @@ +import { expect, test } from '@playwright/test'; +import { EXAMPLES_URL } from '../support/servers'; + +test('source pages explain how to start the local server when opened as files', async ({ page }) => { + for (const source of ['../../../examples-vite/index.html', '../pages/acceptance.html']) { + await page.goto(new URL(source, import.meta.url).href); + await expect(page.locator('#status')).toContainText('Use the local Vite server'); + } +}); + +for (const backend of ['inline', 'worker'] as const) { + test(`analytic Boolean example updates without errors with ${backend}`, async ({ page }) => { + await page.goto(`${EXAMPLES_URL}/operations/analytic-boolean-operations.html?backend=${backend}`); + await expect(page.locator('#status')).toContainText('Ready'); + await expect(page.locator('#report')).toContainText('subtraction · analytic'); + await expect(page.locator('#error')).toBeEmpty(); + expect(await page.locator('#app canvas').evaluate( + (canvas: HTMLCanvasElement) => canvas.width > 0 && canvas.height > 0, + )).toBe(true); + + await page.locator('#offset').evaluate((element: HTMLInputElement) => { + element.value = '0'; + element.dispatchEvent(new globalThis.Event('input', { bubbles: true })); + }); + await expect(page.locator('#report')).toContainText('0 result faces'); + await expect(page.locator('#error')).toBeEmpty(); + + await page.locator('#operation').selectOption('intersection'); + await page.locator('#offset').evaluate((element: HTMLInputElement) => { + element.value = '2.5'; + element.dispatchEvent(new globalThis.Event('input', { bubbles: true })); + }); + await expect(page.locator('#report')).toContainText('intersection · analytic'); + await expect(page.locator('#report')).toContainText('0 result faces'); + await expect(page.locator('#error')).toBeEmpty(); + + await page.locator('#operation').selectOption('union'); + await page.locator('#offset').evaluate((element: HTMLInputElement) => { + element.value = '1'; + element.dispatchEvent(new globalThis.Event('input', { bubbles: true })); + }); + await expect(page.locator('#report')).toContainText('union · analytic'); + await expect.poll(async () => page.evaluate(() => { + const example: unknown = Reflect.get(window, '__ogAnalyticBooleanExample'); + const current: unknown = typeof example === 'object' && example !== null + ? Reflect.get(example, 'current') : undefined; + const faces: unknown = typeof current === 'object' && current !== null + ? Reflect.get(current, 'faces') : undefined; + return typeof faces === 'number' ? faces : 0; + })).toBeGreaterThan(0); + await expect(page.locator('#error')).toBeEmpty(); + + await page.locator('#deflection').evaluate((element: HTMLInputElement) => { + element.value = '-1.5'; + element.dispatchEvent(new globalThis.Event('input', { bubbles: true })); + }); + await expect(page.locator('#status')).toContainText('Ready · mesh'); + await expect(page.locator('#error')).toBeEmpty(); + }); +} diff --git a/main/opengeometry-three/tests/browser/specs/lod.spec.ts b/main/opengeometry-three/tests/browser/specs/lod.spec.ts new file mode 100644 index 0000000..587bd83 --- /dev/null +++ b/main/opengeometry-three/tests/browser/specs/lod.spec.ts @@ -0,0 +1,68 @@ +import { expect, test } from '@playwright/test'; +import { disposeFixture } from '../support/acceptance-page'; + +for (const backend of ['inline', 'worker'] as const) { + test(`LimitExceeded retries one coarser bucket and reports a warning with ${backend}`, async ({ page }) => { + test.setTimeout(45_000); + await page.goto(`/acceptance.html?backend=${backend}`); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + await page.evaluate(async () => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const result = await fixture.coarserRetryProbe(); + if (result.errors.length !== 0 || result.warning?.code !== 'LimitExceeded' + || result.warning.retryBucket !== result.warning.bucket * 2 || result.bucket !== result.warning.retryBucket) { + throw new Error(`Coarser retry failed: ${JSON.stringify(result)}`); + } + }); + } finally { + await disposeFixture(page); + } + }); +} + +for (const backend of ['inline', 'worker'] as const) { + test(`zoom selects a finer LOD bucket with ${backend}`, async ({ page }) => { + test.setTimeout(45_000); + await page.goto(`/acceptance.html?backend=${backend}`); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + await page.evaluate(async () => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const result = await fixture.lodProbe(); + if (result.after > result.before / 2) { + throw new Error(`LOD did not refine after 4x zoom: ${JSON.stringify(result)}`); + } + }); + } finally { + await disposeFixture(page); + } + }); +} + +test('LOD hysteresis keeps stable buckets and an orbit queues at most one job', async ({ page }) => { + test.setTimeout(45_000); + await page.goto('/acceptance.html?backend=worker'); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + await page.evaluate(async () => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const values = await fixture.lodHysteresisProbe(); + const [kept, raised, held, lowered] = values.buckets; + if (kept !== values.p || raised !== 4 * values.p || held !== raised + || lowered === undefined || lowered >= raised) { + throw new Error(`LOD hysteresis edges differ: ${JSON.stringify(values)}`); + } + const orbit = await fixture.orbitProbe(); + const start = orbit.buckets[0] ?? 0; + const coarsened = orbit.buckets.some((bucket, index) => bucket > (orbit.buckets[index - 1] ?? bucket)) + || orbit.sendBuckets.some((bucket) => bucket > start); + if (orbit.jobs > 1 || coarsened) throw new Error(`Orbit coarsened or queued jobs: ${JSON.stringify(orbit)}`); + }); + } finally { + await disposeFixture(page); + } +}); diff --git a/main/opengeometry-three/tests/browser/specs/memory.spec.ts b/main/opengeometry-three/tests/browser/specs/memory.spec.ts new file mode 100644 index 0000000..f598eb3 --- /dev/null +++ b/main/opengeometry-three/tests/browser/specs/memory.spec.ts @@ -0,0 +1,27 @@ +import { expect, test } from '@playwright/test'; +import { disposeFixture } from '../support/acceptance-page'; + +test('instance geometry memory returns to baseline after disposal', async ({ page }) => { + test.setTimeout(45_000); + await page.goto('/acceptance.html?backend=inline'); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + await page.evaluate(async () => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const result = await fixture.memoryProbe(); + if (result.one <= result.baseline || result.shared !== result.one || result.unique !== result.shared + 1 + || result.after !== result.baseline) { + throw new Error(`Geometry memory changed unexpectedly: ${JSON.stringify(result)}`); + } + if (result.outlineOne - result.after !== 2 * (result.one - result.baseline) + || result.outlineShared !== result.outlineOne + || result.outlineUnique - result.outlineShared !== 2 * (result.unique - result.shared) + || result.outlineAfter !== result.baseline) { + throw new Error(`Outline geometry memory changed unexpectedly: ${JSON.stringify(result)}`); + } + }); + } finally { + await disposeFixture(page); + } +}); diff --git a/main/opengeometry-three/tests/browser/specs/placement.spec.ts b/main/opengeometry-three/tests/browser/specs/placement.spec.ts new file mode 100644 index 0000000..5ffe7ef --- /dev/null +++ b/main/opengeometry-three/tests/browser/specs/placement.spec.ts @@ -0,0 +1,22 @@ +import { expect, test } from '@playwright/test'; +import { disposeFixture } from '../support/acceptance-page'; + +test('transform updates a known pixel without changing geometry buffers', async ({ page }) => { + test.setTimeout(45_000); + await page.goto('/acceptance.html?backend=inline'); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + await page.evaluate(() => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const result = fixture.placementPixelProbe(); + if (!result.matrixMatches || !result.geometryMatches || !result.syncHit) { + throw new Error('Transform changed geometry, matrix diverged or sync hit failed'); + } + if ((result.before[2] ?? 0) <= (result.before[0] ?? 0) || result.after[0] !== 255 || result.after[1] !== 255 + || result.after[2] !== 255) throw new Error(`Known pixel did not move: ${JSON.stringify(result)}`); + }); + } finally { + await disposeFixture(page); + } +}); diff --git a/main/opengeometry-three/tests/browser/specs/quick-start.spec.ts b/main/opengeometry-three/tests/browser/specs/quick-start.spec.ts new file mode 100644 index 0000000..2b508a1 --- /dev/null +++ b/main/opengeometry-three/tests/browser/specs/quick-start.spec.ts @@ -0,0 +1,52 @@ +import { expect, test } from '@playwright/test'; + +function checkQuickStart(): void { + const fixture = window.ogQuickStart; + if (!fixture) throw new Error('The quick start page published no fixture'); + if (fixture.errors.length) throw new Error(`The SDK emitted errors: ${JSON.stringify(fixture.errors)}`); + if (fixture.backend !== 'worker') throw new Error(`Expected worker, got ${fixture.backend}`); + if (fixture.settled.failed.length || !fixture.wall.record) throw new Error('The wall did not settle'); + const { products, solids, faces } = fixture.report; + if (products !== 1 || solids !== 1 || faces !== 22) { + throw new Error(`STEP counts differ: ${JSON.stringify({ products, solids, faces })}`); + } + if (!fixture.text.startsWith('ISO-10303-21')) throw new Error('The STEP text does not start with ISO-10303-21'); + const bounds = fixture.bounds; + const near = (actual: number, target: number): boolean => Math.abs(actual - target) <= 1e-6; + if (!bounds || !near(bounds[0], -3) || !near(bounds[3], 3) || !near(bounds[1], 0) || !near(bounds[4], 3)) { + throw new Error(`The wall bounds differ: ${JSON.stringify(bounds)}`); + } +} + +function countDrawnPixels(): number { + const fixture = window.ogQuickStart; + if (!fixture) throw new Error('The quick start page published no fixture'); + const canvas = document.createElement('canvas'); + canvas.width = fixture.renderer.domElement.width; + canvas.height = fixture.renderer.domElement.height; + const context = canvas.getContext('2d'); + if (!context) throw new Error('The 2D canvas context is unavailable'); + context.drawImage(fixture.renderer.domElement, 0, 0); + const pixels = context.getImageData(0, 0, canvas.width, canvas.height).data; + let drawn = 0; + for (let index = 0; index < pixels.length; index += 4) { + const black = pixels[index] === 0 && pixels[index + 1] === 0 && pixels[index + 2] === 0; + if (pixels[index + 3] === 255 && !black) drawn++; + } + return drawn; +} + +test('the README quick start boots from URLs, draws the wall and exports it', async ({ page }) => { + const problems: string[] = []; + page.on('pageerror', (error) => { problems.push(error.message); }); + page.on('console', (message) => { if (message.type() === 'error') problems.push(message.text()); }); + await page.goto('/quick-start.html'); + try { + await expect(page.locator('#status')).toHaveText('Quick start ready'); + await page.evaluate(checkQuickStart); + expect(await page.evaluate(countDrawnPixels)).toBeGreaterThan(1_000); + expect(problems).toEqual([]); + } finally { + await page.evaluate(() => { window.ogQuickStart?.dispose(); }); + } +}); diff --git a/main/opengeometry-three/tests/browser/specs/scene.spec.ts b/main/opengeometry-three/tests/browser/specs/scene.spec.ts new file mode 100644 index 0000000..003874b --- /dev/null +++ b/main/opengeometry-three/tests/browser/specs/scene.spec.ts @@ -0,0 +1,49 @@ +import { expect, test } from '@playwright/test'; +import { disposeFixture } from '../support/acceptance-page'; + +for (const backend of ['inline', 'worker'] as const) { + test(`acceptance scene has expected geometry and STEP with ${backend}`, async ({ page }) => { + test.setTimeout(40_000); + await page.goto(`/acceptance.html?backend=${backend}`); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + await page.evaluate(async (expected) => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + if (fixture.backend !== expected) throw new Error(`Expected ${expected}, got ${fixture.backend}`); + const result = fixture.result; + const near = (actual: number, target: number, tolerance: number): boolean => + Math.abs(actual - target) <= tolerance; + if (!near(result.baseVolume, 3.6, 1e-5) || !near(result.cutVolume, 3.222, 1e-5) + || !near(result.rebuiltVolume, 4.8, 1e-5) || !near(result.recutVolume, 4.422, 1e-5) + || !near(result.railVolume, Math.PI * 0.05 ** 2 * 7, 5e-5) || !result.coverageGap) { + throw new Error(`Scene volumes or CoverageGap changed: ${JSON.stringify(result)}`); + } + const railBounds = [0.95, 3.95, -4.05, 4.0, 4.05, 0]; + if (!result.railBounds.every((value, index) => near(value, railBounds[index] ?? Number.NaN, 1e-6)) + || !near(result.wallBounds[1], 3, 1e-6)) { + throw new Error(`Placed bounds changed: ${JSON.stringify([result.railBounds, result.wallBounds])}`); + } + if (result.products !== 12 || result.skipped !== 3 || result.pcurvelessEdges <= 0) { + throw new Error('STEP counts changed'); + } + if (result.railPcurveless.length !== 11 || !result.railPcurveless.every((edges) => edges > 0)) { + throw new Error(`Rail pcurve-less edges changed: ${JSON.stringify(result.railPcurveless)}`); + } + if (result.coverageGapMs > 2000) { + throw new Error(`Rail minus wall took ${String(result.coverageGapMs)} ms to return CoverageGap`); + } + const rails = fixture.rails; + if (!rails.every((rail) => rail.record !== undefined && rail.record === rails[0]?.record)) { + throw new Error('Rail instances do not share one record'); + } + const second = await fixture.exportStep(); + if (second.report.products !== 12 || !second.text.includes('MANIFOLD_SOLID_BREP')) { + throw new Error('STEP export changed'); + } + }, backend); + } finally { + await disposeFixture(page); + } + }); +} diff --git a/main/opengeometry-three/tests/browser/specs/smoke.spec.ts b/main/opengeometry-three/tests/browser/specs/smoke.spec.ts new file mode 100644 index 0000000..9ffb2cc --- /dev/null +++ b/main/opengeometry-three/tests/browser/specs/smoke.spec.ts @@ -0,0 +1,101 @@ +import { expect, test } from '@playwright/test'; +import { disposeFixture } from '../support/acceptance-page'; + +async function checkSmokeScene({ expected, version }: { expected: string; version: string }): Promise { + const fixture = window.ogSmoke; + if (!fixture) throw new Error('The smoke page published no fixture'); + if (fixture.backend !== expected) throw new Error(`Expected ${expected}, got ${fixture.backend}`); + if (fixture.threeRevision !== version) throw new Error('unexpected three revision'); + if (fixture.reservedNameCollisions.length) { + throw new Error(`Reserved name collision: ${fixture.reservedNameCollisions.join(', ')}`); + } + if (fixture.reservedNamesDerived < 30) { + throw new Error(`Only ${String(fixture.reservedNamesDerived)} added names were derived`); + } + const settled = await fixture.settled(); + if (settled.failed.length || !fixture.body.record) throw new Error('Geometry did not settle'); + const canvas = document.createElement('canvas'); + canvas.width = fixture.renderer.domElement.width; + canvas.height = fixture.renderer.domElement.height; + const context = canvas.getContext('2d'); + if (!context) throw new Error('The 2D canvas context is unavailable'); + context.drawImage(fixture.renderer.domElement, 0, 0); + const pixels = context.getImageData(0, 0, canvas.width, canvas.height).data; + const channel = (offset: number): number => pixels[offset] ?? Number.NaN; + let bluePixels = 0; + for (let index = 0; index < pixels.length; index += 4) { + if (channel(index + 2) > channel(index) + 30 && channel(index + 2) > channel(index + 1) + 20) bluePixels++; + } + if (bluePixels < 100) throw new Error(`The canvas did not draw the cube: ${String(bluePixels)} blue pixels`); + const inline = fixture.getInlineBuffers(); + const record = fixture.body.record; + const surfaceIndex = record.surface?.getIndex(); + if (!record.surface || !surfaceIndex) throw new Error('The cube record has no indexed surface'); + const equalBytes = (a: ArrayBufferView, b: ArrayBufferView): boolean => { + const left = new Uint8Array(a.buffer, a.byteOffset, a.byteLength); + const right = new Uint8Array(b.buffer, b.byteOffset, b.byteLength); + return left.length === right.length && left.every((value, index) => value === right[index]); + }; + if (!equalBytes(inline.positions, record.surface.getAttribute('position').array) + || !equalBytes(inline.normals, record.surface.getAttribute('normal').array) + || !equalBytes(inline.indices, surfaceIndex.array) + || !equalBytes(inline.faceRanges, record.faceRanges) + || !equalBytes(inline.outline, record.outline.getAttribute('position').array) + || !equalBytes(inline.edgeIds, record.edgeIds) + || !equalBytes(inline.origin, new Float64Array(record.origin.toArray())) + || inline.revision !== record.revision || inline.bucket !== record.bucket + || inline.triangles !== record.triangles) throw new Error('Inline and worker buffers differ'); +} + +async function checkPickAndExport(): Promise { + const fixture = window.ogSmoke; + if (!fixture) throw new Error('The smoke page published no fixture'); + const initial = fixture.body.getBrep().revision; + const scene = fixture.resolveSceneHit(); + if (scene.hit?.faceId === undefined || scene.hit.edgeId !== undefined || scene.outlineHit) { + throw new Error(`An oblique scene ray did not pick a face: ${JSON.stringify(scene)}`); + } + fixture.body.transform('Translate', { offset: [2, 0, 0] }); + fixture.render(); + const translation = fixture.body.matrixWorld.elements[12]; + if (Math.abs(translation - 2) > 1e-9) throw new Error('Placement was not flushed'); + if (fixture.body.getBrep().revision !== initial) throw new Error('Transform changed the BRep revision'); + const hit = fixture.resolveHit(); + if (hit?.ogId !== fixture.body.ogId || hit.faceId === undefined + || hit.shapeRevision !== fixture.body.getBrep().revision) { + throw new Error('Picking did not resolve a face'); + } + const exported = await fixture.exportStep(); + if (exported.report.products !== 1 || !exported.text.includes('MANIFOLD_SOLID_BREP')) { + throw new Error('STEP export failed'); + } +} + +async function checkWorkerGuards(): Promise { + const fixture = window.ogSmoke; + if (!fixture) throw new Error('The smoke page published no fixture'); + const field = (value: unknown, key: string): unknown => + (typeof value === 'object' && value !== null ? Reflect.get(value, key) : undefined); + const [init, nonsense, snapshot] = await fixture.workerReplies(); + if (field(init, 'request') !== 0 || field(init, 'error') === undefined || field(nonsense, 'request') !== 7 + || field(field(nonsense, 'error'), 'code') !== 'InvalidParameter' || field(snapshot, 'request') !== 8 + || field(field(snapshot, 'error'), 'code') !== 'InvalidParameter') { + throw new Error(`Worker guard replies differ: ${JSON.stringify([init, nonsense, snapshot])}`); + } +} + +for (const backend of ['inline', 'worker'] as const) { + test(`SDK renders, transforms, picks and exports with ${backend}`, async ({ page }) => { + await page.goto(`/smoke.html?backend=${backend}`); + await expect(page.locator('#status')).toHaveText('Kernel loaded'); + try { + await page.evaluate(checkSmokeScene, { + expected: backend, version: process.env.OG_THREE_VERSION === '184' ? '184' : '168', + }); + await page.evaluate(checkPickAndExport); + if (backend === 'worker') await page.evaluate(checkWorkerGuards); + } finally { + await disposeFixture(page); + } + }); +} diff --git a/main/opengeometry-three/tests/browser/specs/step.spec.ts b/main/opengeometry-three/tests/browser/specs/step.spec.ts new file mode 100644 index 0000000..7c64113 --- /dev/null +++ b/main/opengeometry-three/tests/browser/specs/step.spec.ts @@ -0,0 +1,63 @@ +import { expect, test } from '@playwright/test'; +import { disposeFixture } from '../support/acceptance-page'; +import { part21Check, writeStep } from '../support/step-output'; + +const LEVEL_EXPORTS = [ + { name: 'level-mm-z', unit: 'millimetre', upAxis: 'Z' }, + { name: 'level-m-y', unit: 'metre', upAxis: 'Y' }, +] as const; +const STOREY_NODES = [ + ...Array.from({ length: 200 }, (_, index) => `storey-wall-${String(index)}`), + ...Array.from({ length: 1000 }, (_, index) => `storey-rail-${String(index)}`), +]; + +async function expectOracle(args: string[], log: string): Promise { + const started = performance.now(); + const status = await part21Check(args, log); + const elapsed = Math.round(performance.now() - started); + console.log(`part21_check ${args.join(' ')}: exit ${String(status)} in ${String(elapsed)} ms`); + expect(status, `part21_check failed, see ${log}`).toBe(0); +} + +for (const backend of ['inline', 'worker'] as const) { + const title = `acceptance STEP passes the Part-21 oracle and equals the native scene with ${backend}`; + test(title, async ({ page }, testInfo) => { + test.setTimeout(120_000); + await page.goto(`/acceptance.html?backend=${backend}`); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + for (const { name, unit, upAxis } of LEVEL_EXPORTS) { + const exported = await page.evaluate(async (options) => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + if (fixture.backend !== options.backend) { + throw new Error(`Expected ${options.backend}, got ${fixture.backend}`); + } + return fixture.exportStep({ unit: options.unit, upAxis: options.upAxis }); + }, { backend, unit, upAxis }); + const files = await writeStep(testInfo, name, exported); + await expectOracle(['acceptance', files.step, files.report, unit, upAxis], files.log); + } + } finally { + await disposeFixture(page); + } + }); +} + +test('storey STEP passes the Part-21 oracle', async ({ page }, testInfo) => { + test.setTimeout(240_000); + await page.goto('/acceptance.html?backend=inline'); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + const exported = await page.evaluate(async (nodes) => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + await fixture.buildStorey(); + return fixture.exportStep({ nodes }); + }, STOREY_NODES); + const files = await writeStep(testInfo, 'storey', exported); + await expectOracle(['storey', files.step, files.report], files.log); + } finally { + await disposeFixture(page); + } +}); diff --git a/main/opengeometry-three/tests/browser/specs/storey.spec.ts b/main/opengeometry-three/tests/browser/specs/storey.spec.ts new file mode 100644 index 0000000..a4be4c7 --- /dev/null +++ b/main/opengeometry-three/tests/browser/specs/storey.spec.ts @@ -0,0 +1,95 @@ +import { expect, test } from '@playwright/test'; +import BASELINE from '../../../../scripts/bench/performance-baseline.json' with { type: 'json' }; +import { disposeFixture } from '../support/acceptance-page'; + +const GEOMETRY_FLOOR = 100; +const GEOMETRY_BOUND = 600; +const PROBE_RUNS = 5; +const MARGIN = 1.25; + +type TimingBlock = (typeof BASELINE.timings)[keyof typeof BASELINE.timings]; + +function median(values: number[]): number { + const sorted = [...values].sort((a, b) => a - b); + return sorted[Math.floor(sorted.length / 2)] ?? Number.NaN; +} + +function timingBaseline(): TimingBlock | undefined { + if (process.env.CI === 'true') return undefined; + return new Map(Object.entries(BASELINE.timings)).get(`${process.platform}-${process.arch}`); +} + +for (const backend of ['inline', 'worker'] as const) { + test( + `storey has 200 distinct walls with four measured openings each and 1000 instances with ${backend}`, + async ({ page }) => { + test.setTimeout(120_000); + await page.goto(`/acceptance.html?backend=${backend}`); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + const measured = await page.evaluate(async (geometry) => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const started = performance.now(); + const result = await fixture.buildStorey(); + const buildMs = performance.now() - started; + if (result.walls !== 200 || result.uniqueWalls !== 200 || result.instances !== 1000) { + throw new Error(`Storey scene counts changed: ${JSON.stringify(result)}`); + } + const { min, max } = result.openingsPerWall; + if (min !== max || min !== 4) throw new Error(`Storey walls have ${String(min)} to ${String(max)} openings`); + if (result.renderFlushes > 1) { + throw new Error(`Storey scene flushed ${String(result.renderFlushes)} times in one render`); + } + if (result.geometryCount < geometry.floor || result.geometryCount > geometry.bound) { + throw new Error(`Storey scene uploaded ${String(result.geometryCount)} geometries`); + } + if (result.failed !== 0) { + throw new Error(`Storey scene left ${String(result.failed)} bodies without current geometry`); + } + return { geometryCount: result.geometryCount, buildMs }; + }, { floor: GEOMETRY_FLOOR, bound: GEOMETRY_BOUND }); + console.log(`Storey scene with ${backend}: ${JSON.stringify(measured)}`); + } finally { + await disposeFixture(page); + } + }, + ); +} + +test('storey transform flush and STEP export stay within file limits', async ({ page }) => { + test.setTimeout(120_000); + await page.goto('/acceptance.html?backend=inline'); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + const probed = await page.evaluate(async (runs) => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const built = await fixture.buildStorey(); + let measured = await fixture.storeyPerformanceProbe(); + const flushRuns = [measured.transformFlushMs]; + const stepRuns = [measured.stepMs]; + for (let run = 1; run < runs; run += 1) { + measured = await fixture.storeyPerformanceProbe(); + flushRuns.push(measured.transformFlushMs); + stepRuns.push(measured.stepMs); + } + if (measured.products !== 1200 || measured.entities > 2_000_000 || measured.bytes > 64 * 1024 * 1024) { + throw new Error(`Storey export exceeded expected limits: ${JSON.stringify(measured)}`); + } + return { renderMs: built.renderMs, ...measured, flushRuns, stepRuns }; + }, PROBE_RUNS); + const result = { ...probed, transformFlushMs: median(probed.flushRuns), stepMs: median(probed.stepRuns) }; + console.log(`Storey performance baseline: ${JSON.stringify(result)}`); + const timings = timingBaseline(); + if (timings) { + expect(result.renderMs).toBeLessThanOrEqual(timings.storeyFirstRenderMs * MARGIN); + expect(result.transformFlushMs).toBeLessThanOrEqual(timings.storeyTransformFlushMs * MARGIN); + expect(result.stepMs).toBeLessThanOrEqual(timings.storeyStepMs * MARGIN); + } + expect(result.bytes).toBeLessThanOrEqual(BASELINE.sizes.storeyStepBytes * MARGIN); + expect(result.entities).toBeLessThanOrEqual(BASELINE.sizes.storeyStepEntities * MARGIN); + } finally { + await disposeFixture(page); + } +}); diff --git a/main/opengeometry-three/tests/browser/specs/transactions.spec.ts b/main/opengeometry-three/tests/browser/specs/transactions.spec.ts new file mode 100644 index 0000000..5479434 --- /dev/null +++ b/main/opengeometry-three/tests/browser/specs/transactions.spec.ts @@ -0,0 +1,41 @@ +import { expect, test } from '@playwright/test'; +import { disposeFixture } from '../support/acceptance-page'; + +test('nested transaction rollback revives a disposed body and dry run restores placement', async ({ page }) => { + test.setTimeout(45_000); + await page.goto('/acceptance.html?backend=inline'); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + await page.evaluate(() => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const result = fixture.transactionProbe(); + if (!result.threw || !result.revived || result.dryResult !== 'trial' || !result.dryRestored + || result.thenableCode !== 'InvalidParameter') { + throw new Error(`Transaction behavior changed: ${JSON.stringify(result)}`); + } + }); + } finally { + await disposeFixture(page); + } +}); + +for (const backend of ['inline', 'worker'] as const) { + test(`dispose, reuse the ogId and rollback revive the old object with ${backend}`, async ({ page }) => { + test.setTimeout(45_000); + await page.goto(`/acceptance.html?backend=${backend}`); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + await page.evaluate(async () => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const result = await fixture.reuseProbe(); + if (!result.revived || !result.gone || result.errors.length !== 0) { + throw new Error(`Reuse rollback failed: ${JSON.stringify(result)}`); + } + }); + } finally { + await disposeFixture(page); + } + }); +} diff --git a/main/opengeometry-three/tests/browser/specs/worker.spec.ts b/main/opengeometry-three/tests/browser/specs/worker.spec.ts new file mode 100644 index 0000000..5d126e7 --- /dev/null +++ b/main/opengeometry-three/tests/browser/specs/worker.spec.ts @@ -0,0 +1,90 @@ +import { expect, test } from '@playwright/test'; +import { disposeFixture } from '../support/acceptance-page'; + +test('a missing worker script falls back inline, emits WorkerFailure and settles', async ({ page }) => { + test.setTimeout(45_000); + await page.goto('/acceptance.html?backend=worker&worker=missing'); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + const result = await page.evaluate(() => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + return fixture.workerFailureProbe(); + }); + expect(result).toEqual({ failed: 0, hasRecord: true, errors: ['WorkerFailure'], backend: 'inline' }); + } finally { + await disposeFixture(page); + } +}); + +test('a real post-init worker crash restarts once, then falls back with the record intact and ' + + 'draws new bodies in the same render', async ({ page }) => { + test.setTimeout(45_000); + await page.goto('/acceptance.html?backend=worker&worker=crash'); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + const result = await page.evaluate(() => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + return fixture.workerCrashProbe(); + }); + expect(result).toEqual({ + afterRestart: 'worker', afterFallback: 'inline', sameRecord: true, errors: ['WorkerFailure'], + drawnInSameRender: true, + }); + } finally { + await disposeFixture(page); + } +}); + +test('worker geometry event lets a render-on-demand app render again', async ({ page }) => { + test.setTimeout(45_000); + await page.goto('/acceptance.html?backend=worker'); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + await page.evaluate(async () => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const result = await fixture.onDemandProbe(); + if (result.events < 1 || !result.appeared) { + throw new Error('Render-on-demand body did not appear after geometry event'); + } + }); + } finally { + await disposeFixture(page); + } +}); + +test('rapid worker rebuilds keep the latest revision with at most two jobs', async ({ page }) => { + test.setTimeout(45_000); + await page.goto('/acceptance.html?backend=worker'); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + await page.evaluate(async () => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const result = await fixture.staleWorkerProbe(); + if (result.displayed !== result.current || result.jobs > 2) { + throw new Error(`Worker showed stale geometry or ran too many jobs: ${JSON.stringify(result)}`); + } + }); + } finally { + await disposeFixture(page); + } +}); + +test('worker re-sends a missing snapshot and retries once', async ({ page }) => { + test.setTimeout(45_000); + await page.goto('/acceptance.html?backend=worker'); + await expect(page.locator('#status')).toHaveText('Acceptance ready', { timeout: 20_000 }); + try { + await page.evaluate(async () => { + const fixture = window.ogAcceptance; + if (!fixture) throw new Error('The acceptance page published no fixture'); + const result = await fixture.snapshotResendProbe(); + if (result.triangles <= 0) throw new Error('Snapshot retry produced no geometry'); + }); + } finally { + await disposeFixture(page); + } +}); diff --git a/main/opengeometry-three/tests/browser/support/acceptance-page.ts b/main/opengeometry-three/tests/browser/support/acceptance-page.ts new file mode 100644 index 0000000..c3010bc --- /dev/null +++ b/main/opengeometry-three/tests/browser/support/acceptance-page.ts @@ -0,0 +1,5 @@ +import type { Page } from '@playwright/test'; + +export async function disposeFixture(page: Page): Promise { + await page.evaluate(() => { (window.ogAcceptance ?? window.ogSmoke)?.dispose(); }); +} diff --git a/main/opengeometry-three/tests/browser/support/fixture-types.ts b/main/opengeometry-three/tests/browser/support/fixture-types.ts new file mode 100644 index 0000000..75dee82 --- /dev/null +++ b/main/opengeometry-three/tests/browser/support/fixture-types.ts @@ -0,0 +1,183 @@ +import type * as THREE from 'three'; +import type { OpenGeometry, Solid, SystemAssembly } from '../../../../dist/index.js'; + +type Bounds = [number, number, number, number, number, number]; +type Settled = ReturnType; +type StepExport = Awaited>; +type PickResult = ReturnType; + +export type InlineBuffers = { + positions: Float32Array; + normals: Float32Array; + indices: Uint32Array; + faceRanges: Uint32Array; + outline: Float32Array; + edgeIds: Uint32Array; + origin: Float64Array; + revision: number; + bucket: number; + triangles: number; +}; + +export type SmokeFixture = { + body: Solid; + renderer: THREE.WebGLRenderer; + scene: THREE.Scene; + camera: THREE.PerspectiveCamera; + threeRevision: string; + reservedNameCollisions: string[]; + reservedNamesDerived: number; + readonly backend: string; + render(): void; + settled(): Settled; + exportStep(): Promise; + resolveHit(): PickResult; + resolveSceneHit(): { hit: PickResult; outlineHit: boolean }; + getInlineBuffers(): InlineBuffers; + workerReplies(): Promise; + dispose(): void; +}; + +export type AcceptanceResult = { + baseVolume: number; + cutVolume: number; + railVolume: number; + rebuiltVolume: number; + recutVolume: number; + railBounds: Bounds; + wallBounds: Bounds; + coverageGap: boolean; + coverageGapMs: number; + railPcurveless: number[]; + products: number; + skipped: number; + pcurvelessEdges: number; + stepBytes: number; +}; + +export type StoreyResult = { + walls: number; + uniqueWalls: number; + openingsPerWall: { min: number; max: number }; + instances: number; + flushCount: number; + renderFlushes: number; + geometryCount: number; + renderMs: number; + failed: number; +}; + +export type StoreyPerformance = { + transformFlushMs: number; + transformMs: number; + flushMs: number; + stepMs: number; + bytes: number; + products: number; + entities: number; +}; + +export type InstanceMemory = { + one: number; + shared: number; + unique: number; + after: number; + sharedDetails: { target: string | undefined; copy: string | undefined; same: boolean }; +}; + +export type MemoryResult = InstanceMemory & { + baseline: number; + outlineOne: number; + outlineShared: number; + outlineUnique: number; + outlineAfter: number; +}; + +export type CrashResult = { + afterRestart: string; + afterFallback: string; + sameRecord: boolean; + errors: string[]; + drawnInSameRender: boolean; +}; + +export type FailureResult = { failed: number; hasRecord: boolean; errors: string[]; backend: string }; + +export type PixelResult = { + before: number[]; + after: number[]; + matrixMatches: boolean; + geometryMatches: boolean; + syncHit: boolean; +}; + +export type StaleResult = { current: number; displayed: number | undefined; jobs: number }; + +export type TransactionResult = { + threw: boolean; + revived: boolean; + dryResult: string; + dryRestored: boolean; + thenableCode: string; +}; + +export type ReuseResult = { revived: boolean; gone: boolean; errors: unknown[] }; + +export type RetryResult = { + warning: { code: string; bucket: number; retryBucket: number } | undefined; + bucket: number | undefined; + errors: unknown[]; +}; + +export type OrbitResult = { jobs: number; buckets: number[]; sendBuckets: number[] }; + +export type AcceptanceFixture = { + bodies: Solid[]; + renderer: THREE.WebGLRenderer; + scene: THREE.Scene; + camera: THREE.PerspectiveCamera; + level: SystemAssembly; + wall: Solid; + rail: Solid; + rails: Solid[]; + result: AcceptanceResult; + readonly backend: string; + flush(): void; + settled(): Settled; + render(): void; + exportStep(options?: { nodes?: string[]; unit?: 'metre' | 'millimetre'; upAxis?: 'Y' | 'Z' }): Promise; + buildStorey(): Promise; + storeyPerformanceProbe(): Promise; + memoryProbe(): Promise; + lodProbe(): Promise<{ before: number; after: number }>; + workerCrashProbe(): Promise; + workerFailureProbe(): Promise; + placementPixelProbe(): PixelResult; + onDemandProbe(): Promise<{ events: number; appeared: boolean }>; + staleWorkerProbe(): Promise; + snapshotResendProbe(): Promise<{ triangles: number }>; + lodHysteresisProbe(): Promise<{ p: number; buckets: number[] }>; + orbitProbe(): Promise; + coarserRetryProbe(): Promise; + transactionProbe(): TransactionResult; + reuseProbe(): Promise; + dispose(): void; +}; + +export type QuickStartFixture = { + wall: Solid; + bounds: Bounds | null; + renderer: THREE.WebGLRenderer; + settled: Awaited; + report: StepExport['report']; + text: StepExport['text']; + errors: unknown[]; + readonly backend: string; + dispose(): void; +}; + +declare global { + var ogSmoke: SmokeFixture | undefined; + var ogAcceptance: AcceptanceFixture | undefined; + var ogQuickStart: QuickStartFixture | undefined; +} diff --git a/main/opengeometry-three/tests/browser/support/servers.ts b/main/opengeometry-three/tests/browser/support/servers.ts new file mode 100644 index 0000000..62af5a6 --- /dev/null +++ b/main/opengeometry-three/tests/browser/support/servers.ts @@ -0,0 +1,3 @@ +export const PORT_BASE = Number(process.env.OG_PW_PORT_BASE ?? '4175'); +export const EXAMPLES_URL = `http://127.0.0.1:${String(PORT_BASE)}`; +export const PAGES_URL = `http://127.0.0.1:${String(PORT_BASE + 1)}`; diff --git a/main/opengeometry-three/tests/browser/support/step-output.ts b/main/opengeometry-three/tests/browser/support/step-output.ts new file mode 100644 index 0000000..9a79567 --- /dev/null +++ b/main/opengeometry-three/tests/browser/support/step-output.ts @@ -0,0 +1,35 @@ +import type { TestInfo } from '@playwright/test'; +import { spawn } from 'node:child_process'; +import { closeSync, openSync } from 'node:fs'; +import { writeFile } from 'node:fs/promises'; +import path from 'node:path'; + +const REPOSITORY_ROOT = path.resolve(import.meta.dirname, '../../../..'); +const MANIFEST = path.join(REPOSITORY_ROOT, 'opengeometry', 'Cargo.toml'); + +export type StepFiles = { step: string; report: string; log: string }; + +export async function writeStep( + testInfo: TestInfo, + name: string, + exported: { text: string; report: unknown }, +): Promise { + const files = { + step: testInfo.outputPath(`${name}.step`), + report: testInfo.outputPath(`${name}.report.json`), + log: testInfo.outputPath(`${name}.check.log`), + }; + await writeFile(files.step, exported.text); + await writeFile(files.report, JSON.stringify(exported.report)); + return files; +} + +export function part21Check(args: string[], log: string): Promise { + const fd = openSync(log, 'w'); + const command = ['run', '--offline', '--manifest-path', MANIFEST, '--example', 'part21_check', '--', ...args]; + const child = spawn('cargo', command, { cwd: REPOSITORY_ROOT, env: process.env, stdio: ['ignore', fd, fd] }); + return new Promise((resolve, reject) => { + child.on('error', reject); + child.on('close', (code) => { resolve(code ?? -1); }); + }).finally(() => { closeSync(fd); }); +} diff --git a/main/opengeometry-three/tests/browser/support/test-page.ts b/main/opengeometry-three/tests/browser/support/test-page.ts new file mode 100644 index 0000000..ac0d84c --- /dev/null +++ b/main/opengeometry-three/tests/browser/support/test-page.ts @@ -0,0 +1,57 @@ +import * as THREE from 'three'; +import { OpenGeometry } from '../../../../dist/index.js'; + +const ERRORS: unknown[] = []; + +export async function bootKernel(): Promise { + const params = new URLSearchParams(location.search); + const wasmURL = new URL('./opengeometry_bg.wasm', document.baseURI); + const workerURL = new URL(workerPath(params.get('worker')), document.baseURI); + const module = await WebAssembly.compileStreaming(fetch(wasmURL)); + const backend = params.get('backend') === 'inline' ? 'inline' : 'worker'; + await OpenGeometry.create({ wasmModule: module }, { workerURL, tessellation: backend }); + OpenGeometry.on('error', (event) => { ERRORS.push(event); }); + return workerURL; +} + +function workerPath(name: string | null): string { + if (name === 'missing') return './missing-worker.js'; + if (name === 'crash') return './crash-worker.ts'; + return './tessellation-worker.js'; +} + +export function recordedErrors(): unknown[] { + return [...ERRORS]; +} + +export function createRenderer(width: number, height: number): THREE.WebGLRenderer { + const renderer = new THREE.WebGLRenderer({ antialias: false, preserveDrawingBuffer: true }); + renderer.setSize(width, height); + document.body.append(renderer.domElement); + return renderer; +} + +export function releasePage(renderer: THREE.WebGLRenderer): void { + OpenGeometry.reset(); + renderer.dispose(); + renderer.forceContextLoss(); + renderer.domElement.remove(); +} + +export function required(value: T | null | undefined, label: string): T { + if (value === null || value === undefined) throw new Error(`Test page expected ${label}`); + return value; +} + +export function statusElement(): Element { + const status = document.querySelector('#status'); + if (!status) throw new Error('test page has no #status element'); + return status; +} + +export function publishFixture( + key: K, fixture: NonNullable<(typeof globalThis)[K]>, status: string, +): void { + globalThis[key] = fixture; + statusElement().textContent = status; +} diff --git a/main/opengeometry-three/tests/browser/vite.config.ts b/main/opengeometry-three/tests/browser/vite.config.ts new file mode 100644 index 0000000..ceb1731 --- /dev/null +++ b/main/opengeometry-three/tests/browser/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite'; + +const ROOT = decodeURIComponent(new URL('./pages', import.meta.url).pathname); + +export default defineConfig({ + root: ROOT, + cacheDir: `../../../../node_modules/.vite/pages-three-${process.env.OG_THREE_VERSION ?? '168'}`, + ...(process.env.OG_THREE_VERSION === '184' ? { resolve: { alias: { three: 'three-184' } } } : {}), + publicDir: decodeURIComponent(new URL('../../../dist', import.meta.url).pathname), + optimizeDeps: { exclude: ['opengeometry'] }, +}); diff --git a/main/opengeometry-three/tests/node/display.test.mjs b/main/opengeometry-three/tests/node/display.test.mjs new file mode 100644 index 0000000..b53ecd9 --- /dev/null +++ b/main/opengeometry-three/tests/node/display.test.mjs @@ -0,0 +1,75 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import * as THREE from 'three'; +import { OG_TRANSFORM_TRANSLATE } from '../../../dist/index.js'; +import { graph } from '../../../dist/testing.js'; +import { boot, cuboid, listen, nextTask } from './support.mjs'; + +function assertNear(actual, expected, tolerance) { + assert.equal(actual.length, expected.length); + actual.forEach((value, index) => { + assert(Math.abs(value - (expected[index] ?? Number.NaN)) <= tolerance, `${String(value)} at ${String(index)}`); + }); +} + +test('a body without a record is visible, culled and bounded by the kernel local bounds', async () => { + await boot(); + await nextTask(); + const body = cuboid('placeholder-cube', 2); + body.transform(OG_TRANSFORM_TRANSLATE, { offset: [5, 0, 0] }); + const copy = body.instance({ ogId: 'placeholder-copy' }); + try { + assert.equal(body.surface.visible, true); + assert.equal(body.surface.frustumCulled, true); + const box = body.surface.geometry.boundingBox; + assert(box); + assertNear([...box.min.toArray(), ...box.max.toArray()], [-1, 0, -0.5, 1, 1, 0.5], 1e-7); + assert.equal(copy.surface.geometry, body.surface.geometry); + } finally { + copy.dispose(); + body.dispose(); + } +}); + +test("writing a body's position emits a placement warning at the next matrix update", async () => { + await boot(); + await nextTask(); + const body = cuboid('overwritten-cube'); + const scene = new THREE.Scene(); + scene.add(body); + const warnings = listen('warning'); + try { + body.position.set(1, 2, 3); + scene.updateMatrixWorld(true); + assert.deepEqual(warnings.events, [{ code: 'PlacementOverwritten', ogId: body.ogId }]); + assert.deepEqual(body.position.toArray(), [0, 0, 0]); + } finally { + warnings.stop(); + body.dispose(); + } +}); + +test('a Three parent that moves after placement keeps the body at the kernel world matrix', async () => { + await boot(); + await nextTask(); + const body = cuboid('moved-parent-cube'); + body.transform(OG_TRANSFORM_TRANSLATE, { offset: [1, 2, 3] }); + const scene = new THREE.Scene(); + const group = new THREE.Group(); + scene.add(group); + group.add(body); + const warnings = listen('warning'); + try { + scene.updateMatrixWorld(true); + group.position.set(3, 0, 0); + group.rotation.set(0, 0.5, 0); + scene.updateMatrixWorld(true); + assertNear(body.matrixWorld.elements, Array.from(graph().worldMatrix(body.ogId)), 1e-12); + const local = group.matrixWorld.clone().invert().multiply(body.matrixWorld); + assertNear(body.matrix.elements, local.elements, 1e-12); + assert.deepEqual(warnings.events, []); + } finally { + warnings.stop(); + body.dispose(); + } +}); diff --git a/main/opengeometry-three/tests/node/export-step.test.mjs b/main/opengeometry-three/tests/node/export-step.test.mjs new file mode 100644 index 0000000..56226c2 --- /dev/null +++ b/main/opengeometry-three/tests/node/export-step.test.mjs @@ -0,0 +1,41 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { OpenGeometry, SystemAssembly } from '../../../dist/index.js'; +import { boot, cuboid, failsWith } from './support.mjs'; + +function rejection(promise) { + return promise.then(() => undefined, (reason) => reason); +} + +test('exportStep on a disposed body whose ogId was reused rejects with Disposed', async () => { + await boot(); + const stale = cuboid('reuse-1'); + stale.dispose(); + cuboid('reuse-1'); + assert(failsWith('Disposed')(await rejection(OpenGeometry.exportStep({ nodes: [stale] })))); +}); + +test('every handle is checked in order', async () => { + await boot(); + const live = cuboid('export-live'); + const stale = cuboid('reuse-2'); + stale.dispose(); + cuboid('reuse-2'); + assert(failsWith('Disposed')(await rejection(OpenGeometry.exportStep({ nodes: [live, stale] })))); +}); + +test('a disposed assembly rejects with Disposed, not UnknownNode', async () => { + await boot(); + const assembly = new SystemAssembly({ ogId: 'export-assembly' }); + assembly.dispose(); + assert(failsWith('Disposed')(await rejection(OpenGeometry.exportStep({ nodes: [assembly] })))); +}); + +test('a body from a reset runtime rejects with Disposed', async () => { + await boot(); + const stale = cuboid('reset-export'); + OpenGeometry.reset(); + await boot(); + cuboid('reset-export'); + assert(failsWith('Disposed')(await rejection(OpenGeometry.exportStep({ nodes: [stale] })))); +}); diff --git a/main/opengeometry-three/tests/node/flush.test.mjs b/main/opengeometry-three/tests/node/flush.test.mjs new file mode 100644 index 0000000..1f5d62b --- /dev/null +++ b/main/opengeometry-three/tests/node/flush.test.mjs @@ -0,0 +1,56 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import * as THREE from 'three'; +import { OpenGeometry, OG_TRANSFORM_TRANSLATE } from '../../../dist/index.js'; +import { graph, runtime } from '../../../dist/testing.js'; +import { boot, cuboid, nextTask } from './support.mjs'; + +function cuboids(prefix) { + return Array.from({ length: 50 }, (_, index) => cuboid(`${prefix}-${String(index)}`, 1 + index / 100)); +} + +function disposeAll(bodies) { + for (const body of bodies) body.dispose(); +} + +test('flush after a transform makes no node call', async () => { + await boot(); + await nextTask(); + const bodies = cuboids('node-call'); + OpenGeometry.flush(); + const target = Object(graph()); + const node = target.node; + let calls = 0; + target.node = (...args) => { calls++; return Reflect.apply(node, target, args); }; + try { + bodies[0]?.transform(OG_TRANSFORM_TRANSLATE, { offset: [1, 0, 0] }); + OpenGeometry.flush(); + } finally { + delete target.node; + disposeAll(bodies); + } + assert.equal(calls, 0); +}); + +test('flush purges records only for the shapes in the change set', async () => { + await boot(); + await nextTask(); + const bodies = cuboids('purge'); + const scene = new THREE.Scene(); + scene.add(...bodies); + scene.updateMatrixWorld(true); + OpenGeometry.flush({ geometry: 'sync' }); + await nextTask(); + const records = Object(runtime().records); + const purge = records.purge; + let calls = 0; + records.purge = (...args) => { calls++; return Reflect.apply(purge, records, args); }; + try { + bodies[0]?.transform(OG_TRANSFORM_TRANSLATE, { offset: [1, 0, 0] }); + OpenGeometry.flush(); + } finally { + delete records.purge; + disposeAll(bodies); + } + assert(calls <= 1, `${String(calls)} purge calls`); +}); diff --git a/main/opengeometry-three/tests/node/kernel-session.test.mjs b/main/opengeometry-three/tests/node/kernel-session.test.mjs new file mode 100644 index 0000000..6929a71 --- /dev/null +++ b/main/opengeometry-three/tests/node/kernel-session.test.mjs @@ -0,0 +1,133 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { test } from 'node:test'; +import { OpenGeometry, OGError, Solid, OG_PRIMITIVE_CUBOID, OG_PRIMITIVE_POLYLINE } from '../../../dist/index.js'; +import * as TESTING from '../../../dist/testing.js'; +import { ensureGeometry, graph, runtime, wanted } from '../../../dist/testing.js'; + +const MODULE = new WebAssembly.Module(readFileSync(new URL('../../../dist/opengeometry_bg.wasm', import.meta.url))); +await OpenGeometry.create({ wasmModule: MODULE }); + +function cube(ogId) { + return new Solid(OG_PRIMITIVE_CUBOID, { width: 1, height: 1, depth: 1 }, { ogId }); +} + +function failsWith(code, call) { + return (error) => error instanceof OGError && error.code === code && error.call === call; +} + +function listen(event) { + const events = []; + const stop = OpenGeometry.on(event, (detail) => { events.push(detail); }); + return { events, stop }; +} + +test('a real LimitExceeded from the inline backend gives a warning and a coarser record', () => { + const body = cube('limit-cube'); + const warnings = listen('warning'); + const errors = listen('error'); + try { + const { floor } = JSON.parse(graph().displayBuckets(body.ogId)); + const shapeId = body.lastInfo.shapeId; + assert(shapeId); + Reflect.get(TESTING, 'pinWantedBucket')(shapeId, floor / 2); + ensureGeometry(body, true); + assert.deepEqual(errors.events, []); + assert.equal(warnings.events.length, 1); + const warning = warnings.events[0]; + assert.equal(Object.getPrototypeOf(warning), Object.prototype); + assert.equal(warning.code, 'LimitExceeded'); + assert.equal(warning.retryBucket, 2 * warning.bucket); + assert.equal(body.record?.bucket, warning.retryBucket); + } finally { + warnings.stop(); + errors.stop(); + body.dispose(); + } +}); + +test('a stale handle error names the public method', () => { + const body = cube('stale-cube'); + body.dispose(); + assert.throws(() => body.getBounds(), failsWith('Disposed', 'Solid.getBounds')); + assert.throws(() => body.getReport(), failsWith('Disposed', 'Solid.getReport')); + const points = [[0, 0, 0], [1, 0, 0], [1, 1, 0]]; + assert.throws(() => new Solid(OG_PRIMITIVE_POLYLINE, { points }, { ogId: 'solid-polyline' }), (error) => + error instanceof OGError && error.call === 'Solid.constructor'); +}); + +test('a malformed display buffer is reported as an OGError', () => { + const body = cube('malformed-buffer-cube'); + const provider = Object(runtime().provider); + const errors = listen('error'); + provider.compute = () => ({}); + try { + ensureGeometry(body, true); + assert.equal(errors.events.length, 1); + assert(errors.events[0] instanceof OGError); + assert.equal(errors.events[0].code, 'WorkerFailure'); + } finally { + delete provider.compute; + errors.stop(); + body.dispose(); + } +}); + +test('a malformed kernel payload is rejected by the decoder', () => { + const body = cube('malformed-brep-cube'); + const graph = Object(runtime().graph); + graph.brep = () => '{}'; + try { + assert.throws(() => body.getBrep(), failsWith('InvalidGeometry', 'Solid.getBrep')); + } finally { + delete graph.brep; + body.dispose(); + } + const before = runtime().bodies.size; + graph.node = () => '{}'; + try { + assert.throws(() => cube('malformed-node-cube'), failsWith('InvalidGeometry', 'Solid.constructor')); + } finally { + delete graph.node; + } + assert.equal(runtime().bodies.size, before); +}); + +test('an invalid deflection is an OGError', () => { + const body = cube('nan-deflection-cube'); + try { + body.setAppearance({ deflection: Number.NaN }); + assert.throws(() => wanted(body), (error) => error instanceof OGError && error.code === 'InvalidParameter'); + } finally { + body.dispose(); + } +}); + +test('a stubbed RuntimeError poisons the runtime emits fatal and names the method', () => { + const body = cube('panic-cube'); + const graph = Object(runtime().graph); + const fatal = listen('fatal'); + runtime().displayed.set(body, runtime().pass); + graph.buffers = () => { throw new WebAssembly.RuntimeError('unreachable'); }; + Reflect.set(globalThis, '__opengeometryPanic', 'index out of bounds'); + try { + try { + OpenGeometry.flush({ geometry: 'sync' }); + } catch (error) { + assert(error instanceof OGError && error.code === 'KernelPanic'); + } + assert.equal(fatal.events.length, 1); + const [panic] = fatal.events; + assert(panic instanceof OGError); + assert.equal(panic.code, 'KernelPanic'); + assert.equal(panic.call, 'tessellation'); + assert.equal(panic.message, 'index out of bounds'); + assert.throws(() => cube('after-panic-cube'), (error) => + error instanceof OGError && error.code === 'KernelPanic' && error.message === 'kernel runtime is poisoned'); + } finally { + delete graph.buffers; + Reflect.deleteProperty(globalThis, '__opengeometryPanic'); + fatal.stop(); + OpenGeometry.reset(); + } +}); diff --git a/main/opengeometry-three/tests/node/lifecycle.test.mjs b/main/opengeometry-three/tests/node/lifecycle.test.mjs new file mode 100644 index 0000000..14dd9bf --- /dev/null +++ b/main/opengeometry-three/tests/node/lifecycle.test.mjs @@ -0,0 +1,159 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import * as THREE from 'three'; +import { OpenGeometry, OGError, Solid, OG_TRANSFORM_TRANSLATE } from '../../../dist/index.js'; +import * as TESTING from '../../../dist/testing.js'; +import { boot, cuboid, failsWith, nextTask } from './support.mjs'; + +function runtime() { + return (Reflect.get(TESTING, 'runtime') ?? Reflect.get(OpenGeometry, 'runtime'))(); +} + +function flushCount() { + const read = Reflect.get(TESTING, 'flushCount'); + return read ? read() : Reflect.get(OpenGeometry, 'flushCount'); +} + +function thrownBy(block) { + try { + block(); + return undefined; + } catch (error) { + return error; + } +} + +test('a create that cannot load the kernel rejects and leaves no runtime', async () => { + const error = await OpenGeometry.create().then(() => undefined, (reason) => reason); + assert(error instanceof Error); + assert.throws(() => runtime(), failsWith('NotInitialised')); +}); + +test('two concurrent create calls share one runtime', async () => { + const instantiate = WebAssembly.instantiate; + let instantiations = 0; + Reflect.set(WebAssembly, 'instantiate', (...args) => { + instantiations++; + return Reflect.apply(instantiate, WebAssembly, args); + }); + try { + const [first, second] = await Promise.all([boot().then(() => runtime()), boot().then(() => runtime())]); + assert.equal(first, second); + assert.equal(instantiations, 1); + } finally { + Reflect.set(WebAssembly, 'instantiate', instantiate); + } +}); + +test('instance and duplicate wrap the new node without the __existing kind', async () => { + await boot(); + const rail = cuboid('rail'); + const instance = rail.instance({ ogId: 'rail-instance' }); + const copy = rail.duplicate({ ogId: 'rail-copy' }); + assert.equal([...runtime().bodies.values()].find((body) => body.ogId === 'rail-instance'), instance); + assert.equal([...runtime().bodies.values()].find((body) => body.ogId === 'rail-copy'), copy); + const wall = cuboid('wall', 2); + const count = runtime().bodies.size; + assert.throws(() => new Solid('__existing', {}, { ogId: 'wall' }), (error) => error instanceof OGError); + assert.equal([...runtime().bodies.values()].find((body) => body.ogId === 'wall'), wall); + assert.equal(runtime().bodies.size, count); +}); + +test("clone places the display copy at the body's world matrix", async () => { + await boot(); + const body = cuboid('clone-source'); + body.transform(OG_TRANSFORM_TRANSLATE, { offset: [1, 2, 3] }); + OpenGeometry.flush(); + const display = body.clone(); + const scene = new THREE.Scene(); + scene.add(display); + scene.updateMatrixWorld(true); + assert.deepEqual(new THREE.Vector3().setFromMatrixPosition(display.matrixWorld).toArray(), [1, 2, 3]); +}); + +test('cloning a display clone throws InvalidOperand', async () => { + await boot(); + const display = cuboid('clone-twice').clone(); + assert.throws(() => display.clone(), failsWith('InvalidOperand')); +}); + +test('rollback in the same tick as a deferred removal keeps the revived body in its parent', async () => { + await boot(); + await nextTask(); + const body = cuboid('revived', 3); + const scene = new THREE.Scene(); + scene.add(body); + const mark = OpenGeometry.mark(); + scene.updateMatrixWorld(true); + body.dispose(); + mark.rollback(); + mark.release(); + await nextTask(); + assert.equal(body.parent, scene); + assert.equal(body.visible, true); +}); + +test('disposing a sibling from a geometry listener during a render traversal defers its removal', async () => { + await boot(); + await nextTask(); + const scene = new THREE.Scene(); + const first = cuboid('first-sibling', 4); + const sibling = cuboid('second-sibling', 5); + scene.add(first); + scene.add(sibling); + const stop = OpenGeometry.on('geometry', () => { if (sibling.visible) sibling.dispose(); }); + try { + assert.equal(thrownBy(() => { scene.updateMatrixWorld(true); }), undefined); + assert.equal(sibling.visible, false); + await nextTask(); + assert.equal(sibling.parent, null); + } finally { + stop(); + } +}); + +test('reset disposes every body material', async () => { + await boot(); + const body = cuboid('material-owner', 6); + const disposed = []; + Object(body.surface.material).addEventListener('dispose', () => { disposed.push('surface'); }); + Object(body.outline.material).addEventListener('dispose', () => { disposed.push('outline'); }); + OpenGeometry.reset(); + assert.deepEqual(disposed.sort(), ['outline', 'surface']); +}); + +test('a body from a reset runtime throws Disposed instead of validating against the new graph', async () => { + OpenGeometry.reset(); + await boot(); + const stale = cuboid('stale-body'); + OpenGeometry.reset(); + await boot(); + cuboid('fresh-body'); + assert.throws(() => stale.getBounds(), failsWith('Disposed')); +}); + +test('reset leaves a usable empty document', async () => { + await boot(); + const before = runtime(); + OpenGeometry.reset(); + assert.notEqual(runtime(), before); + assert.equal(runtime().bodies.size, 0); + const body = cuboid('after-reset'); + assert.equal([...runtime().bodies.values()].find((body) => body.ogId === 'after-reset'), body); +}); + +test('a Solid inside a Solid flushes once on updateMatrixWorld', async () => { + await boot(); + await nextTask(); + const outer = cuboid('outer', 7); + const inner = cuboid('inner', 8); + const scene = new THREE.Scene(); + outer.add(inner); + scene.add(outer); + const before = flushCount(); + const caught = thrownBy(() => { scene.updateMatrixWorld(true); }); + const flushes = flushCount() - before; + outer.remove(inner); + assert.equal(caught, undefined); + assert.equal(flushes, 1); +}); diff --git a/main/opengeometry-three/tests/node/lod.test.mjs b/main/opengeometry-three/tests/node/lod.test.mjs new file mode 100644 index 0000000..361cbbe --- /dev/null +++ b/main/opengeometry-three/tests/node/lod.test.mjs @@ -0,0 +1,304 @@ +import assert from 'node:assert/strict'; +import * as NODE_TEST from 'node:test'; +import { test } from 'node:test'; +import * as THREE from 'three'; +import { + OpenGeometry, OGError, OG_PRIMITIVE_CUBOID, OG_TRANSFORM_ROTATE, OG_TRANSFORM_TRANSLATE, +} from '../../../dist/index.js'; +import * as TESTING from '../../../dist/testing.js'; +import { boot, cuboid, listen } from './support.mjs'; + +const RENDERER = { getDrawingBufferSize: (target) => target.set(1000, 1000) }; +const P = 2 ** -10; +const TIMERS = Reflect.get(NODE_TEST, 'mock').timers; + +async function fresh() { + await boot(); + OpenGeometry.reset(); +} + +function aim(camera, target) { + camera.zoom = 1 / (1000 * target); + camera.updateProjectionMatrix(); + camera.updateMatrixWorld(); + return camera; +} + +function ortho(target) { + return aim(new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 1000), target); +} + +async function frame(scene, camera) { + scene.updateMatrixWorld(true); + scene.traverseVisible((object) => { + if (object instanceof THREE.Mesh) Reflect.apply(object.onBeforeRender, object, [RENDERER, scene, camera]); + }); + await Promise.resolve(); +} + +function computeLog() { + const provider = Object(TESTING.runtime().provider); + const compute = provider.compute.bind(provider); + const requests = []; + provider.compute = (request) => { requests.push(request); return compute(request); }; + return { requests, stop: () => { delete provider.compute; } }; +} + +function kernelBuckets(body) { + return JSON.parse(TESTING.graph().displayBuckets(body.ogId)); +} + +function pinWantedBucket(shapeId, bucket) { + Reflect.get(TESTING, 'pinWantedBucket')(shapeId, bucket); +} + +test('a body removed from the scene is not computed', async () => { + await fresh(); + const scene = new THREE.Scene(); + const body = cuboid('removed-cube'); + const other = cuboid('kept-cube', 2); + scene.add(body, other); + const log = computeLog(); + const mine = () => log.requests.filter((request) => request.shapeId !== other.lastInfo.shapeId).length; + try { + scene.updateMatrixWorld(true); + await Promise.resolve(); + const before = mine(); + scene.remove(body); + scene.updateMatrixWorld(true); + await Promise.resolve(); + body.rebuild(OG_PRIMITIVE_CUBOID, { width: 1.5, height: 1, depth: 1 }); + OpenGeometry.flush({ geometry: 'sync' }); + const { failed } = await OpenGeometry.settled(); + assert.equal(mine(), before); + assert.deepEqual(failed, []); + } finally { + log.stop(); + other.dispose(); + body.dispose(); + } +}); + +test('a hidden rebuilt body is not reported failed', async () => { + await fresh(); + const scene = new THREE.Scene(); + const body = cuboid('hidden-cube'); + scene.add(body); + try { + scene.updateMatrixWorld(true); + await Promise.resolve(); + assert.deepEqual((await OpenGeometry.settled()).failed, []); + body.visible = false; + body.rebuild(OG_PRIMITIVE_CUBOID, { width: 1.5, height: 1, depth: 1 }); + assert.deepEqual((await OpenGeometry.settled()).failed, []); + } finally { + body.dispose(); + } +}); + +test("settled's deflection is temporary and checks the wanted key", async () => { + await fresh(); + const scene = new THREE.Scene(); + const body = cuboid('temporary-cube'); + scene.add(body); + try { + scene.updateMatrixWorld(true); + assert.deepEqual((await OpenGeometry.settled({ deflection: 0.05 })).failed, []); + assert.equal(body.appearance.deflection, undefined); + assert.equal(body.record?.bucket, 2 ** -5); + await frame(scene, ortho(1.5 * P)); + assert.equal(body.record?.bucket, P); + } finally { + body.dispose(); + } +}); + +test('settled returns the captured OGError', async () => { + await fresh(); + const scene = new THREE.Scene(); + const body = cuboid('failing-cube'); + scene.add(body); + const failure = new OGError('InvalidGeometry', 'tessellation', 'stubbed tessellation failure'); + const provider = Object(TESTING.runtime().provider); + const errors = listen('error'); + provider.compute = () => { throw failure; }; + try { + scene.updateMatrixWorld(true); + const { failed } = await OpenGeometry.settled(); + assert.equal(failed.length, 1); + assert.equal(failed[0]?.error, failure); + } finally { + delete provider.compute; + errors.stop(); + body.dispose(); + } +}); + +test('hysteresis keeps the bucket inside 0.75p and 4p and leaves it outside', async () => { + await fresh(); + TIMERS.enable({ apis: ['setTimeout', 'Date'] }); + const scene = new THREE.Scene(); + const body = cuboid('hysteresis-cube'); + scene.add(body); + const camera = ortho(1.5 * P); + try { + await frame(scene, camera); + assert.equal(body.record?.bucket, P); + const steps = [ + { target: 3.99999 * P, bucket: P }, { target: 4.00001 * P, bucket: 4 * P }, + { target: 3.00003 * P, bucket: 4 * P }, { target: 2.99997 * P, bucket: 2 * P }, + ]; + for (const { target, bucket } of steps) { + await frame(scene, aim(camera, target)); + TIMERS.tick(151); + assert.equal(body.record?.bucket, bucket, `target ${String(target / P)}p`); + } + } finally { + TIMERS.reset(); + body.dispose(); + } +}); + +test('moving never coarsens and the settle timer refines 150 ms after the last change', async () => { + await fresh(); + TIMERS.enable({ apis: ['setTimeout', 'Date'] }); + const scene = new THREE.Scene(); + const body = cuboid('moving-cube'); + scene.add(body); + const camera = ortho(1.5 * P); + let log; + try { + await frame(scene, camera); + assert.equal(body.record?.bucket, P); + log = computeLog(); + const buckets = () => log?.requests.map((request) => request.bucket); + TIMERS.tick(100); + await frame(scene, aim(camera, 6 * P)); + TIMERS.tick(100); + await frame(scene, aim(camera, 0.19 * P)); + assert.deepEqual(buckets(), []); + TIMERS.tick(149); + assert.deepEqual(buckets(), []); + TIMERS.tick(1); + assert.deepEqual(buckets(), [P / 8]); + assert.equal(body.record?.bucket, P / 8); + } finally { + log?.stop(); + TIMERS.reset(); + body.dispose(); + } +}); + +test('near and far instances share the finest bucket in both add orders', async () => { + const half = Math.sqrt(3) / 2; + const expected = 2 ** Math.floor(Math.log2(0.5 * 2 * Math.tan(Math.PI / 6) / 1000)); + for (const nearFirst of [true, false]) { + await fresh(); + const near = cuboid('near-cube'); + const far = near.instance({ ogId: 'far-cube' }); + near.transform(OG_TRANSFORM_TRANSLATE, { offset: [0, -0.5, -(1 + half)] }); + far.transform(OG_TRANSFORM_TRANSLATE, { offset: [0, -0.5, -(100 + half)] }); + OpenGeometry.flush(); + const scene = new THREE.Scene(); + scene.add(...(nearFirst ? [near, far] : [far, near])); + const camera = new THREE.PerspectiveCamera(60, 1, 0.1, 1000); + camera.updateMatrixWorld(); + try { + await frame(scene, camera); + assert.equal(expected, 2 ** -11); + assert.equal(near.record?.bucket, expected, nearFirst ? 'near first' : 'far first'); + assert.equal(far.record?.bucket, expected, nearFirst ? 'near first' : 'far first'); + } finally { + far.dispose(); + near.dispose(); + } + } +}); + +test('a failed bucket is skipped and the coarser record is installed', async () => { + await fresh(); + const body = cuboid('failed-bucket-cube'); + const warnings = listen('warning'); + const errors = listen('error'); + const log = computeLog(); + try { + const { floor } = kernelBuckets(body); + pinWantedBucket(body.lastInfo.shapeId, floor / 2); + TESTING.ensureGeometry(body, true); + assert.deepEqual(errors.events, []); + assert.deepEqual(warnings.events, [{ + code: 'LimitExceeded', shapeId: body.lastInfo.shapeId, revision: body.lastInfo.shapeRevision, + bucket: floor / 2, retryBucket: floor, + }]); + assert.equal(body.record?.bucket, floor); + assert.equal(TESTING.wanted(body), floor); + assert.equal(TESTING.wanted(body), floor); + TESTING.ensureGeometry(body, true); + assert.deepEqual(log.requests.map((request) => request.bucket), [floor / 2, floor]); + } finally { + log.stop(); + warnings.stop(); + errors.stop(); + body.dispose(); + } +}); + +test('the finest per-body override wins for one shape', async () => { + for (const finerFirst of [true, false]) { + await fresh(); + const first = cuboid('override-cube'); + const second = first.instance({ ogId: 'override-instance' }); + const [fine, coarse] = finerFirst ? [first, second] : [second, first]; + fine.setAppearance({ deflection: 0.01 }); + coarse.setAppearance({ deflection: 0.1 }); + const scene = new THREE.Scene(); + scene.add(first, second); + try { + scene.updateMatrixWorld(true); + assert.equal(TESTING.wanted(first), 2 ** -7); + assert.equal(TESTING.wanted(second), 2 ** -7); + } finally { + second.dispose(); + first.dispose(); + } + } +}); + +test("the unit cube's floor and static bucket are the kernel's", async () => { + await fresh(); + const aligned = cuboid('aligned-cube'); + const rotated = cuboid('rotated-cube'); + try { + rotated.transform(OG_TRANSFORM_ROTATE, { axis: [0, 1, 0], degrees: 45, pivot: [0, 0, 0] }); + OpenGeometry.flush(); + aligned.setAppearance({ deflection: 1e-12 }); + assert.equal(kernelBuckets(aligned).floor, 2 ** -21); + assert.equal(TESTING.wanted(aligned), 2 ** -21); + assert.equal(kernelBuckets(rotated).static, 2 ** -9); + assert.equal(TESTING.wanted(rotated), 2 ** -9); + } finally { + rotated.dispose(); + aligned.dispose(); + } +}); + +test('overlapping settled calls leave no temporary target behind', async () => { + await fresh(); + const scene = new THREE.Scene(); + const body = cuboid('overlapping-cube'); + scene.add(body); + try { + scene.updateMatrixWorld(true); + assert.deepEqual((await OpenGeometry.settled()).failed, []); + const before = TESTING.wanted(body); + assert.equal(before, 2 ** -9); + const first = OpenGeometry.settled({ deflection: 2 ** -6 }); + const second = await OpenGeometry.settled({ deflection: 2 ** -8 }); + await first; + assert.deepEqual(second.failed, []); + assert.deepEqual((await OpenGeometry.settled()).failed, []); + assert.equal(TESTING.wanted(body), before); + } finally { + body.dispose(); + } +}); diff --git a/main/opengeometry-three/tests/node/marks.test.mjs b/main/opengeometry-three/tests/node/marks.test.mjs new file mode 100644 index 0000000..e36119f --- /dev/null +++ b/main/opengeometry-three/tests/node/marks.test.mjs @@ -0,0 +1,61 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import * as THREE from 'three'; +import { OpenGeometry, OGError } from '../../../dist/index.js'; +import { boot, cuboid, failsWith, nextTask } from './support.mjs'; + +test('rollback revives the old object after its ogId was reused under a mark', async () => { + await boot(); + await nextTask(); + const scene = new THREE.Scene(); + const first = cuboid('reused-id'); + scene.add(first); + scene.updateMatrixWorld(true); + const mark = OpenGeometry.mark(); + first.dispose(); + const second = cuboid('reused-id', 2); + scene.add(second); + mark.rollback(); + mark.release(); + await nextTask(); + try { + assert.equal(first.parent, scene); + assert.equal(first.visible, true); + assert.equal(first.inLimbo, false); + assert.notEqual(first.getBounds(), null); + assert.equal(second.parent, null); + assert.equal(second.inLimbo, false); + assert.throws(() => second.getBounds(), failsWith('Disposed')); + } finally { + first.dispose(); + } +}); + +test('a mark truncated by an earlier rollback is released in the SDK and later limbo bodies finalise', async () => { + await boot(); + await nextTask(); + const first = OpenGeometry.mark(); + const second = OpenGeometry.mark(); + first.rollback(); + first.release(); + const body = cuboid('after-truncation'); + body.dispose(); + assert.equal(body.inLimbo, false); + assert.throws(() => { second.release(); }, (error) => + error instanceof OGError && error.code === 'InvalidMark' && error.message === 'mark is released'); +}); + +test('a limbo body is finalised when no live mark older than its dispose remains', async () => { + await boot(); + await nextTask(); + const body = cuboid('older-mark-body'); + const first = OpenGeometry.mark(); + body.dispose(); + const second = OpenGeometry.mark(); + try { + first.release(); + assert.equal(body.inLimbo, false); + } finally { + second.release(); + } +}); diff --git a/main/opengeometry-three/tests/node/materials.test.mjs b/main/opengeometry-three/tests/node/materials.test.mjs new file mode 100644 index 0000000..bb4b1b7 --- /dev/null +++ b/main/opengeometry-three/tests/node/materials.test.mjs @@ -0,0 +1,65 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import * as THREE from 'three'; +import { OpenGeometry, Wire, OG_PRIMITIVE_RECTANGLE } from '../../../dist/index.js'; +import { runtime } from '../../../dist/testing.js'; +import { boot, cuboid, failsWith } from './support.mjs'; + +function rectangle(ogId, appearance = {}) { + return new Wire(OG_PRIMITIVE_RECTANGLE, { width: 1, breadth: 1 }, { ogId, appearance }); +} + +function lineMaterialOf(body) { + const material = body.outline.material; + assert(material instanceof THREE.LineBasicMaterial); + return material; +} + +test("a wire's colour and opacity follow setAppearance", async () => { + await boot(); + const wire = rectangle('appearance-wire'); + try { + wire.setAppearance({ color: 0xff0000, opacity: 0.5 }); + const material = lineMaterialOf(wire); + assert.equal(material.color.getHex(), 0xff0000); + assert.equal(material.opacity, 0.5); + assert.equal(material.transparent, true); + } finally { + wire.dispose(); + } +}); + +test('surface materials are shaded and line materials are pooled by colour and opacity', async () => { + await boot(); + const first = rectangle('pooled-wire-1', { color: 0x00ff00 }); + const second = rectangle('pooled-wire-2', { color: 0x00ff00 }); + const solid = cuboid('shaded-solid'); + try { + assert.equal(lineMaterialOf(first), lineMaterialOf(second)); + assert.notEqual(lineMaterialOf(solid), lineMaterialOf(first)); + const surface = solid.surface.material; + assert(surface instanceof THREE.MeshStandardMaterial); + assert.equal(surface.polygonOffset, true); + } finally { + first.dispose(); + second.dispose(); + solid.dispose(); + } +}); + +test('setAppearance on a body from a reset runtime throws Disposed and leaves the new pool alone', async () => { + await boot(); + const stale = cuboid('stale-appearance'); + OpenGeometry.reset(); + await boot(); + const fresh = cuboid('fresh-appearance'); + try { + assert.throws(() => { stale.setAppearance({ color: 0x123456 }); }, failsWith('Disposed')); + const entry = runtime().surfacePool.get(`${String(0x6699dd)}:1`); + assert(entry); + assert.equal(entry.refs, 1); + assert.equal(entry.material, fresh.surface.material); + } finally { + fresh.dispose(); + } +}); diff --git a/main/opengeometry-three/tests/node/picking.test.mjs b/main/opengeometry-three/tests/node/picking.test.mjs new file mode 100644 index 0000000..9757086 --- /dev/null +++ b/main/opengeometry-three/tests/node/picking.test.mjs @@ -0,0 +1,100 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import * as THREE from 'three'; +import { OpenGeometry } from '../../../dist/index.js'; +import { runtime } from '../../../dist/testing.js'; +import { boot, cuboid } from './support.mjs'; + +function linearFaceId(ranges, faceIndex) { + for (let offset = 0; offset < ranges.length; offset += 3) { + if (faceIndex >= ranges[offset + 1] && faceIndex < ranges[offset + 1] + ranges[offset + 2]) return ranges[offset]; + } + return undefined; +} + +function hitOn(object, fields) { + return { distance: 0, point: new THREE.Vector3(), object, ...fields }; +} + +function displayed(bodies) { + const scene = new THREE.Scene(); + scene.add(...bodies); + scene.updateMatrixWorld(true); + OpenGeometry.flush({ geometry: 'sync' }); + for (const body of bodies) assert(body.record, `${body.ogId} has a record`); +} + +function countedRanges(faces) { + const ranges = new Uint32Array(faces * 3); + let start = 0; + for (let face = 0; face < faces; face++) { + const count = 1 + (face % 3); + ranges.set([(face * 7919) % faces, start, count], face * 3); + start += count; + } + const counter = { reads: 0, triangles: start }; + const proxy = new Proxy(ranges, { + get(target, property) { + counter.reads++; + return Reflect.get(target, property); + }, + }); + return { ranges, proxy, counter }; +} + +test('resolveHit maps a hit object to its body without scanning the body map', async () => { + await boot(); + const middle = cuboid('pick-b', 2); + const solids = [cuboid('pick-a', 1), middle, cuboid('pick-c', 3)]; + displayed(solids); + const record = middle.record; + assert(record); + const bodies = Object(runtime().bodies); + bodies.values = () => { throw new Error('resolveHit scanned the body map'); }; + try { + assert.deepEqual(OpenGeometry.resolveHit(hitOn(middle.surface, { faceIndex: 0 })), { + ogId: 'pick-b', shapeRevision: record.revision, faceId: linearFaceId(record.faceRanges, 0), + }); + } finally { + delete bodies.values; + } + for (const solid of solids) solid.dispose(); +}); + +test('resolveHit finds the face by binary search over the face ranges', async () => { + await boot(); + const body = cuboid('pick-search'); + const faces = 4096; + const { ranges, proxy, counter } = countedRanges(faces); + const target = Object(body); + const kept = target.record; + target.record = { revision: 7, faceRanges: proxy, edgeIds: new Uint32Array(0) }; + const pick = (faceIndex) => OpenGeometry.resolveHit(hitOn(body.surface, { faceIndex }))?.faceId; + try { + const lastStart = ranges[faces * 3 - 2]; + counter.reads = 0; + assert.equal(pick(lastStart), linearFaceId(ranges, lastStart)); + const limit = 3 * (Math.log2(faces) + 2); + assert(counter.reads <= limit, `${String(counter.reads)} reads for the last face, limit ${String(limit)}`); + for (let offset = 0; offset < ranges.length; offset += 3) { + const first = Number(ranges[offset + 1]); + const last = first + Number(ranges[offset + 2]) - 1; + assert.equal(pick(first), linearFaceId(ranges, first)); + assert.equal(pick(last), linearFaceId(ranges, last)); + } + assert.equal(pick(counter.triangles), undefined); + } finally { + target.record = kept; + } + body.dispose(); +}); + +test("a hit on a disposed body's mesh resolves nothing", async () => { + await boot(); + const body = cuboid('pick-disposed'); + displayed([body]); + const { surface, outline } = body; + body.dispose(); + assert.equal(OpenGeometry.resolveHit(hitOn(surface, { faceIndex: 0 })), undefined); + assert.equal(OpenGeometry.resolveHit(hitOn(outline, { index: 0 })), undefined); +}); diff --git a/main/opengeometry-three/tests/node/sdk.test.mjs b/main/opengeometry-three/tests/node/sdk.test.mjs new file mode 100644 index 0000000..3dc1769 --- /dev/null +++ b/main/opengeometry-three/tests/node/sdk.test.mjs @@ -0,0 +1,111 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { test } from 'node:test'; +import * as THREE from 'three'; +import { + OpenGeometry, OGError, Solid, Wire, OG_PRIMITIVE_CUBOID, OG_PRIMITIVE_PARAMS_RECTANGLE, + OG_PRIMITIVE_RECTANGLE, OG_TRANSFORM_ROTATE, OG_TRANSFORM_TRANSLATE, +} from '../../../dist/index.js'; +import { activeBackend, ensureGeometry, graph, noteDisplayed, runtime, wanted } from '../../../dist/testing.js'; + +const BYTES = readFileSync(new URL('../../../dist/opengeometry_bg.wasm', import.meta.url)); +await OpenGeometry.create({ wasmModule: new WebAssembly.Module(BYTES) }); +assert.equal(activeBackend(), 'inline'); +const BODY = new Solid(OG_PRIMITIVE_CUBOID, { width: 1, height: 1, depth: 1 }, { ogId: 'node-cube' }); +const EXPORTED = await OpenGeometry.exportStep({ nodes: [BODY] }); +assert.equal(EXPORTED.report.products, 1); +noteDisplayed(BODY); +assert.equal((await OpenGeometry.settled()).failed.length, 0); +BODY.dispose(); +assert.equal((await OpenGeometry.settled()).failed.length, 0); +const STYLED = new Solid(OG_PRIMITIVE_CUBOID, { width: 1, height: 1, depth: 1 }, { ogId: 'styled-cube' }); +const MATERIAL = STYLED.surface.material; +STYLED.setAppearance({ deflection: 0.01 }); +assert.equal(STYLED.surface.material, MATERIAL); +STYLED.setAppearance({ color: 0x22c55e }); +assert.notEqual(STYLED.surface.material, MATERIAL); +STYLED.dispose(); +const VISIBLE = new Solid(OG_PRIMITIVE_CUBOID, { width: 2, height: 1, depth: 1 }, { ogId: 'visible-cube' }); +noteDisplayed(VISIBLE); +const PROVIDER = Object(runtime().provider); +const COMPUTE = PROVIDER.compute.bind(PROVIDER); +PROVIDER.compute = undefined; +PROVIDER.request = async (request) => { + await new Promise((resolve) => setTimeout(resolve, 2)); + return COMPUTE(request); +}; +const PREPARED = new Solid(OG_PRIMITIVE_CUBOID, { width: 3, height: 1, depth: 1 }, { ogId: 'prepared-cube' }); +ensureGeometry(PREPARED); +assert(VISIBLE.record); +assert.equal(PREPARED.record, undefined); +assert.equal((await OpenGeometry.settled()).failed.length, 0); +assert(PREPARED.record); +assert(VISIBLE.record); +let cancelled = 0; +PROVIDER.cancelShape = () => { cancelled++; }; +const RAPID = new Solid(OG_PRIMITIVE_CUBOID, { width: 2.5, height: 1, depth: 1 }, { ogId: 'rapid-cube' }); +ensureGeometry(RAPID); +ensureGeometry(RAPID, true); +assert(RAPID.record); +assert.equal(cancelled, 1); +assert.equal((await OpenGeometry.settled()).failed.length, 0); +assert(RAPID.record); +RAPID.dispose(); +PREPARED.dispose(); +VISIBLE.dispose(); +delete PROVIDER.compute; +delete PROVIDER.request; +delete PROVIDER.cancelShape; +assert.equal((await OpenGeometry.settled()).failed.length, 0); +for (let index = 0; index < 24; index++) { + const size = { width: 1 + index / 10, height: 1, depth: 1 }; + const replacement = new Solid(OG_PRIMITIVE_CUBOID, size, { ogId: `replacement-${index}` }); + ensureGeometry(replacement); + assert(replacement.record); + replacement.dispose(); + assert.equal((await OpenGeometry.settled()).failed.length, 0); +} +const LEAK_PARAMS = OG_PRIMITIVE_PARAMS_RECTANGLE({ width: 1, breadth: 1 }); +assert.throws( + () => new Solid(OG_PRIMITIVE_RECTANGLE, LEAK_PARAMS, { ogId: 'leak' }), + (error) => error instanceof OGError && error.code === 'BodyTypeMismatch', +); +new Wire(OG_PRIMITIVE_RECTANGLE, LEAK_PARAMS, { ogId: 'leak' }); +assert.equal((await OpenGeometry.settled()).failed.length, 0); +OpenGeometry.reset(); +console.log('Node SDK passed'); + +test('display buckets are read once per shape and revision', () => { + const sized = new Solid(OG_PRIMITIVE_CUBOID, { width: 1, height: 1, depth: 1 }, { ogId: 'size-cache' }); + const target = Object(graph()); + const read = target.displayBuckets; + let reads = 0; + target.displayBuckets = (...args) => { reads++; return Reflect.apply(read, target, args); }; + const camera = new THREE.PerspectiveCamera(55, 1, 0.1, 100); + camera.position.set(3, 3, 3); + camera.lookAt(0, 0, 0); + camera.updateMatrixWorld(); + const renderer = { getDrawingBufferSize: (size) => size.set(800, 600) }; + try { + for (let index = 0; index < 100; index++) wanted(sized); + assert.equal(reads, 1); + for (let index = 0; index < 100; index++) { + Reflect.apply(sized.surface.onBeforeRender, sized.surface, [renderer, null, camera]); + } + assert.equal(reads, 1); + sized.transform(OG_TRANSFORM_TRANSLATE, { offset: [1, 0, 0] }); + OpenGeometry.flush(); + wanted(sized); + assert.equal(reads, 1); + sized.transform(OG_TRANSFORM_ROTATE, { axis: [0, 1, 0], degrees: 30, pivot: [0, 0, 0] }); + OpenGeometry.flush(); + wanted(sized); + assert.equal(reads, 1); + sized.rebuild(OG_PRIMITIVE_CUBOID, { width: 2, height: 1, depth: 1 }); + wanted(sized); + assert.equal(reads, 2); + } finally { + delete target.displayBuckets; + sized.dispose(); + } +}); diff --git a/main/opengeometry-three/tests/node/support.mjs b/main/opengeometry-three/tests/node/support.mjs new file mode 100644 index 0000000..e38634e --- /dev/null +++ b/main/opengeometry-three/tests/node/support.mjs @@ -0,0 +1,26 @@ +import { readFileSync } from 'node:fs'; +import { OpenGeometry, OGError, Solid, OG_PRIMITIVE_CUBOID } from '../../../dist/index.js'; + +const MODULE = new WebAssembly.Module(readFileSync(new URL('../../../dist/opengeometry_bg.wasm', import.meta.url))); + +export function boot() { + return OpenGeometry.create({ wasmModule: MODULE }); +} + +export function nextTask() { + return new Promise((resolve) => setTimeout(resolve, 0)); +} + +export function cuboid(ogId, width = 1) { + return new Solid(OG_PRIMITIVE_CUBOID, { width, height: 1, depth: 1 }, { ogId }); +} + +export function failsWith(code) { + return (error) => error instanceof OGError && error.code === code; +} + +export function listen(event) { + const events = []; + const stop = OpenGeometry.on(event, (detail) => { events.push(detail); }); + return { events, stop }; +} diff --git a/main/opengeometry-three/tests/node/worker-guards.test.mjs b/main/opengeometry-three/tests/node/worker-guards.test.mjs new file mode 100644 index 0000000..fa54184 --- /dev/null +++ b/main/opengeometry-three/tests/node/worker-guards.test.mjs @@ -0,0 +1,39 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import * as TESTING from '../../../dist/testing.js'; + +const MODULE = new WebAssembly.Module(new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0])); +const JOB = { shapeId: 'shape-1', revision: 2, bucket: 0.01, priority: 0, maxTriangles: 1000, generation: 3 }; + +function parse(message) { + return Reflect.get(TESTING, 'parseWorkerMessage')(message); +} + +test('worker messages with unknown kinds, extra keys or wrong field types are rejected with InvalidParameter', () => { + const rows = [ + { message: { kind: 'nonsense', request: 7 }, key: 'kind', request: 7 }, + { message: { kind: 'init', request: 0, module: 'invalid' }, key: 'module', request: 0 }, + { message: { kind: 'snapshot', request: 2, shapeId: 'shape-1', revision: 2 }, key: 'bytes', request: 2 }, + { message: { kind: 'tessellate', request: 4, ...JOB, extra: 1 }, key: 'extra', request: 4 }, + { message: { kind: 'cancel', shapeId: 'shape-1', generation: Infinity }, key: 'generation', request: undefined }, + ]; + for (const row of rows) { + const failure = parse(row.message); + assert('error' in failure, row.key); + assert.equal(failure.error.code, 'InvalidParameter', row.key); + assert(failure.error.message.includes(row.key), failure.error.message); + assert(Object.hasOwn(failure, 'request'), row.key); + assert.equal(failure.request, row.request, row.key); + } +}); + +test('well-formed worker messages pass the guard unchanged', () => { + const messages = [ + { kind: 'init', request: 0, module: MODULE }, + { kind: 'snapshot', request: 1, shapeId: 'shape-1', revision: 2, bytes: new Uint8Array([1, 2, 3]) }, + { kind: 'tessellate', request: 2, ...JOB }, + { kind: 'cancel', shapeId: 'shape-1', generation: 3 }, + { kind: 'drop', shapeId: 'shape-1', revision: 2 }, + ]; + for (const message of messages) assert.equal(parse(message), message, message.kind); +}); diff --git a/main/opengeometry-three/tests/tsconfig.json b/main/opengeometry-three/tests/tsconfig.json new file mode 100644 index 0000000..52e3466 --- /dev/null +++ b/main/opengeometry-three/tests/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.tests.json" +} diff --git a/main/opengeometry-three/typedoc.json b/main/opengeometry-three/typedoc.json deleted file mode 100644 index e2635e4..0000000 --- a/main/opengeometry-three/typedoc.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "https://typedoc.org/schema.json", - "entryPoints": [ - "index.ts", - "src/base-types.ts", - "src/primitives/index.ts", - "src/shapes/index.ts", - "src/examples/index.ts" - ], - "entryPointStrategy": "resolve", - "compilerOptions": { - "lib": [ - "ES2022", - "DOM", - "ESNext.Disposable" - ] - }, - "exclude": [ - "**/*.test.ts", - "**/*.spec.ts", - "examples-vite/**", - "scripts/**", - "docs/**", - "jsdoc-dist/**", - "**/*.d.ts" - ], - "out": "jsdoc-dist", - "readme": "none", - "categorizeByGroup": false, - "sort": [ - "source-order" - ], - "excludePrivate": true, - "excludeProtected": false, - "excludeExternals": true, - "includeVersion": true, - "name": "OpenGeometry Three API" -} diff --git a/main/opengeometry-three/vite.examples.config.mjs b/main/opengeometry-three/vite.examples.config.mjs deleted file mode 100644 index e068db4..0000000 --- a/main/opengeometry-three/vite.examples.config.mjs +++ /dev/null @@ -1,103 +0,0 @@ -import { readdirSync, statSync } from "node:fs"; -import { dirname, extname, join, relative, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -import { defineConfig } from "vite"; -import { createExamplesHeaderPlugin } from "./scripts/examples-header-script.mjs"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const examplesRoot = resolve(__dirname, "examples-vite"); - -function collectFiles(dir, extension, files = []) { - for (const entry of readdirSync(dir)) { - if (entry.startsWith(".")) { - continue; - } - - const fullPath = join(dir, entry); - const stats = statSync(fullPath); - if (stats.isDirectory()) { - collectFiles(fullPath, extension, files); - continue; - } - - if (extname(fullPath) === extension) { - files.push(fullPath); - } - } - - return files; -} - -function toPosixPath(path) { - return path.replace(/\\/g, "/"); -} - -function createManualChunk(id) { - const normalized = toPosixPath(id); - - if (normalized.includes("/node_modules/three/examples/jsm/libs/stats.module")) { - return "vendor-stats"; - } - - if (normalized.includes("/node_modules/three/examples/jsm/")) { - return "vendor-three-extras"; - } - - if (normalized.includes("/node_modules/three/")) { - return "vendor-three"; - } - - if ( - normalized.includes("/main/opengeometry-three/index.ts") - || normalized.includes("/main/opengeometry-three/src/") - || normalized.includes("/main/opengeometry/pkg/") - ) { - return "vendor-opengeometry"; - } - - return undefined; -} - -function collectHtmlInputs() { - const htmlFiles = collectFiles(examplesRoot, ".html"); - const inputs = {}; - - for (const htmlPath of htmlFiles) { - const key = toPosixPath(relative(examplesRoot, htmlPath)).replace(/\.html$/, ""); - inputs[key] = htmlPath; - } - - return inputs; -} - -const input = collectHtmlInputs(); - -export default defineConfig({ - root: examplesRoot, - base: "./", - plugins: [createExamplesHeaderPlugin()], - build: { - outDir: resolve(__dirname, "examples-dist"), - emptyOutDir: true, - rollupOptions: { - input, - output: { - manualChunks: createManualChunk, - }, - }, - }, - resolve: { - alias: { - opengeometry: resolve(__dirname, "index.ts"), - "@og-three": resolve(__dirname, "index.ts"), - }, - }, - server: { - fs: { - allow: [resolve(__dirname, ".."), __dirname], - }, - }, -}); diff --git a/main/opengeometry-webgl/README.md b/main/opengeometry-webgl/README.md deleted file mode 100644 index 4a2728c..0000000 --- a/main/opengeometry-webgl/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# opengeometry-webgl - -Scaffold, not yet implemented. A future direct-WebGL adapter for the OpenGeometry kernel. - -For the current public SDK, see `main/opengeometry-three/` and the published -[`opengeometry`](https://www.npmjs.com/package/opengeometry) package. diff --git a/main/opengeometry-webgl/package.json b/main/opengeometry-webgl/package.json deleted file mode 100644 index 5a670d8..0000000 --- a/main/opengeometry-webgl/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "@opengeometry/kernel-webgl", - "version": "1.0.0", - "description": "OpenGeometry Kernel for WebGL", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [ - "opengeometry", - "three", - "geometry" - ], - "author": "Vishwajeet Mane", - "license": "MIT", - "dependencies": {} -} \ No newline at end of file diff --git a/main/opengeometry/Cargo.lock b/main/opengeometry/Cargo.lock new file mode 100644 index 0000000..ba3caf4 --- /dev/null +++ b/main/opengeometry/Cargo.lock @@ -0,0 +1,464 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cc" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f360145194ee8e21db5ee7f3fcd4fe52210864c75c985dae33218202c8bbe040" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "earcutr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a09f1c588d65581e4f4ce412fa239dbc2aced01504891cd12360fc1c0e7776a0" +dependencies = [ + "num-traits", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "minicov" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3aa3aa12b448ac225b3102217d1ac5cc717908f02722926524b0599c933c7a0" +dependencies = [ + "cc", + "walkdir", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opengeometry" +version = "2.5.0" +dependencies = [ + "console_error_panic_hook", + "earcutr", + "js-sys", + "opengeometry-test-support", + "serde", + "serde_json", + "sha2", + "wasm-bindgen", + "wasm-bindgen-test", +] + +[[package]] +name = "opengeometry-test-support" +version = "2.5.0" +dependencies = [ + "opengeometry", + "serde_json", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" +dependencies = [ + "js-sys", + "minicov", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/main/opengeometry/Cargo.toml b/main/opengeometry/Cargo.toml index c442e3b..6e4e42c 100644 --- a/main/opengeometry/Cargo.toml +++ b/main/opengeometry/Cargo.toml @@ -1,27 +1,24 @@ [package] name = "opengeometry" -version = "1.0.10" -authors = ["Vishwajeet Mane "] -description = "Graphics Kernel for Next Gen" -license = "MIT/Apache-2.0" -repository = "https://github.com/OpenGeometry-io/OpenGeometry" +version = "2.5.0" edition = "2021" +license = "MPL-2.0" [lib] crate-type = ["cdylib", "rlib"] [dependencies] -wasm-bindgen = "0.2" +wasm-bindgen = "=0.2.100" +js-sys = "=0.3.77" serde = { version = "1.0", features = ["derive"] } -serde-wasm-bindgen = "0.4" -serde_json = { version = "1.0.127", features = ["float_roundtrip"] } -web-sys = { version = "0.3", features = ["console"] } -openmaths = "0.2.4" -uuid = { version = "1.17.0", features = ["v4", "v5", "serde", "js"] } -getrandom = { version = "0.2", features = ["js"] } +serde_json = { version = "1.0", features = ["float_roundtrip"] } earcutr = "=0.3.0" -dxf = "0.6.0" -stl_io = "0.8" +console_error_panic_hook = "0.1" -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -printpdf = "0.5" +[dev-dependencies] +wasm-bindgen-test = "=0.3.50" +sha2 = "0.10" +opengeometry-test-support = { path = "test-support" } + +[profile.release] +codegen-units = 1 diff --git a/main/opengeometry/cad.md b/main/opengeometry/cad.md deleted file mode 100644 index ea988b6..0000000 --- a/main/opengeometry/cad.md +++ /dev/null @@ -1,28 +0,0 @@ -### Creation of Elements and Primitives - -#### Cyclinder -There are two ways to create a Cyclinder -1. Create a Circle Primitive, then create Circle Poly Face and Then Extrude The Polygon by given height -2. Create a Cylinder Primitive and provide height and radius - -### Kernel Validation - -Run from `main/opengeometry`: - -```bash -cargo fmt --check -cargo check --all-targets -cargo test -``` - -Targeted export and projection checks: - -```bash -cargo test test_scene_projection_from_edge_entity -cargo test test_scene_projection_lines_json_payload -cargo test test_scene_stl_export_binary_payload -cargo test test_scene_step_export_text_payload -cargo test test_scene_ifc_export_text_payload -``` - -The crate no longer ships standalone Rust examples, scripts, or the native sandbox. Release validation now happens through compiled library tests and downstream consumers that call the public APIs directly. diff --git a/main/opengeometry/clippy.toml b/main/opengeometry/clippy.toml new file mode 100644 index 0000000..7bada54 --- /dev/null +++ b/main/opengeometry/clippy.toml @@ -0,0 +1,3 @@ +allow-unwrap-in-tests = true +allow-expect-in-tests = true +allow-panic-in-tests = true diff --git a/main/opengeometry/examples/analytic_exchange_fixtures.rs b/main/opengeometry/examples/analytic_exchange_fixtures.rs deleted file mode 100644 index f9d8c4e..0000000 --- a/main/opengeometry/examples/analytic_exchange_fixtures.rs +++ /dev/null @@ -1,108 +0,0 @@ -use opengeometry::analytic::{ - booleans::{boolean_brep, BooleanOp}, - ifc_exchange::prepare_ifc_body, - primitives, - topology::Accuracy, - Frame3, -}; -use std::{error::Error, fs, path::PathBuf}; - -fn main() -> Result<(), Box> { - let output = std::env::args() - .nth(1) - .map(PathBuf::from) - .ok_or("provide an output JSON path")?; - let accuracy = Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-6, - }; - let frame = Frame3 { - origin: [1.0, 2.0, 3.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - ..Frame3::IDENTITY - }; - let host = primitives::sphere("host".into(), frame, 2.0, accuracy)?; - let cutter = primitives::sphere( - "cutter".into(), - Frame3 { - origin: [2.7, 2.0, 3.0], - ..frame - }, - 1.0, - accuracy, - )?; - let inner = primitives::sphere("inner".into(), frame, 0.5, accuracy)?; - let box_host = primitives::cuboid("box-host".into(), frame, [3.0; 3], accuracy)?; - let box_cutter = primitives::cuboid( - "box-cutter".into(), - Frame3 { - origin: frame.point([1.5, 0.5, 0.5]), - ..frame - }, - [3.0; 3], - accuracy, - )?; - let box_inner = primitives::cuboid( - "box-inner".into(), - Frame3 { - origin: frame.point([1.0; 3]), - ..frame - }, - [1.0; 3], - accuracy, - )?; - let bodies = vec![ - primitives::cuboid("cuboid".into(), frame, [2.0, 1.0, 3.0], accuracy)?, - primitives::cylinder("cylinder".into(), frame, 1.0, 2.0, accuracy)?, - primitives::sphere("sphere".into(), frame, 1.0, accuracy)?, - primitives::cone("cone".into(), frame, 1.0, 2.0, accuracy)?, - primitives::frustum("frustum".into(), frame, 1.0, 0.4, 2.0, accuracy)?, - primitives::torus("torus".into(), frame, 2.0, 0.5, accuracy)?, - primitives::annular_sector_extrusion( - "wall".into(), - frame, - 2.4, - 0.3, - 1.4, - 0.15, - -1.8, - accuracy, - )?, - boolean_brep(&host, &cutter, BooleanOp::Subtraction, "cut".into())?.brep, - boolean_brep(&host, &inner, BooleanOp::Subtraction, "cavity".into())?.brep, - boolean_brep(&box_host, &box_cutter, BooleanOp::Union, "box-union".into())?.brep, - boolean_brep( - &box_host, - &box_cutter, - BooleanOp::Intersection, - "box-intersection".into(), - )? - .brep, - boolean_brep( - &box_host, - &box_cutter, - BooleanOp::Subtraction, - "box-cut".into(), - )? - .brep, - boolean_brep( - &box_host, - &box_inner, - BooleanOp::Subtraction, - "box-cavity".into(), - )? - .brep, - ]; - let mut records = Vec::new(); - for body in bodies { - records.push(serde_json::json!({ - "name": body.id, "serialized_brep": body.to_json()?, - "exchange_body": prepare_ifc_body(&body)?, - })); - } - fs::write(output, serde_json::to_string_pretty(&records)?)?; - Ok(()) -} diff --git a/main/opengeometry/examples/budgets.rs b/main/opengeometry/examples/budgets.rs new file mode 100644 index 0000000..f20c402 --- /dev/null +++ b/main/opengeometry/examples/budgets.rs @@ -0,0 +1,35 @@ +use opengeometry::brep::{BrepEnvelope, GeometryError}; +use opengeometry::operations::modifying::boolean::{boolean_brep_outcome_with_handlers, BooleanOp}; +use serde_json::Value; +use std::error::Error; +use std::time::{Duration, Instant}; + +const ROTATED_BOX_UNION: &str = + include_str!("../tests/fixtures/cases/boolean-matrix/rotated-box-union.json"); +const COVERAGE_GAP_BOUND: Duration = Duration::from_secs(2); + +fn main() -> Result<(), Box> { + let fixture: Value = serde_json::from_str(ROTATED_BOX_UNION)?; + let a: BrepEnvelope = serde_json::from_value(fixture["a"].clone())?; + let b: BrepEnvelope = serde_json::from_value(fixture["b"].clone())?; + let started = Instant::now(); + let (result, _) = boolean_brep_outcome_with_handlers( + &a, + &b, + BooleanOp::Union, + "budgets-rotated-box-union".into(), + ); + let elapsed = started.elapsed(); + println!( + "rotated-box union: {} ms against a bound of {} ms", + elapsed.as_secs_f64() * 1000.0, + COVERAGE_GAP_BOUND.as_millis() + ); + if !matches!(result, Err(GeometryError::CoverageGap { .. })) { + return Err("the rotated-box union no longer returns CoverageGap".into()); + } + if elapsed >= COVERAGE_GAP_BOUND { + return Err("the rotated-box union CoverageGap exceeded its bound".into()); + } + Ok(()) +} diff --git a/main/opengeometry/examples/part21_check.rs b/main/opengeometry/examples/part21_check.rs new file mode 100644 index 0000000..306f8b5 --- /dev/null +++ b/main/opengeometry/examples/part21_check.rs @@ -0,0 +1,124 @@ +use opengeometry_test_support::part21::{bits, check_report_matches, Document}; +use opengeometry_test_support::scenes::acceptance::{acceptance_scene, level_export}; +use serde_json::Value; +use std::{env, error::Error, fs}; + +const USAGE: &str = "usage: part21_check acceptance | storey "; + +struct BrowserExport { + text: String, + report: Value, + document: Document, +} + +fn main() -> Result<(), Box> { + let args: Vec = env::args().skip(1).collect(); + let args: Vec<&str> = args.iter().map(String::as_str).collect(); + match args.as_slice() { + ["acceptance", step, report, unit, up_axis] => { + check_acceptance(step, report, unit, up_axis)? + } + ["storey", step, report] => check_storey(step, report)?, + _ => return Err(USAGE.into()), + } + println!("part21_check {}: ok", args[0]); + Ok(()) +} + +fn browser_export(step: &str, report: &str, unit: &str) -> Result> { + let text = fs::read_to_string(step)?; + let report: Value = serde_json::from_str(&fs::read_to_string(report)?)?; + let document = Document::parse(&text)?; + document.check_length_unit(unit)?; + check_report_matches(&document, &text, &report)?; + Ok(BrowserExport { + text, + report, + document, + }) +} + +fn check_acceptance( + step: &str, + report: &str, + unit: &str, + up_axis: &str, +) -> Result<(), Box> { + let browser = browser_export(step, report, unit)?; + let (native_text, native_report) = level_export(&acceptance_scene(), unit, up_axis); + let native_report = serde_json::to_value(&native_report)?; + check_same_value("report", &browser.report, &native_report)?; + check_same_text(&browser.text, &native_text)?; + Ok(()) +} + +fn check_same_value(path: &str, browser: &Value, native: &Value) -> Result<(), String> { + match (browser, native) { + (Value::Object(left), Value::Object(right)) => { + if !left.keys().eq(right.keys()) { + return Err(format!("{path}: keys differ from the native report")); + } + left.iter().try_for_each(|(key, value)| { + check_same_value(&format!("{path}.{key}"), value, &right[key]) + }) + } + (Value::Array(left), Value::Array(right)) => { + if left.len() != right.len() { + return Err(format!( + "{path}: {} items, native {}", + left.len(), + right.len() + )); + } + left.iter() + .zip(right) + .enumerate() + .try_for_each(|(index, (a, b))| check_same_value(&format!("{path}[{index}]"), a, b)) + } + (Value::Number(left), Value::Number(right)) + if left + .as_f64() + .zip(right.as_f64()) + .is_some_and(|(a, b)| bits([a]) == bits([b])) => + { + Ok(()) + } + _ if browser == native => Ok(()), + _ => Err(format!( + "{path}: browser {browser} differs from native {native}" + )), + } +} + +fn check_same_text(browser: &str, native: &str) -> Result<(), String> { + if browser == native { + return Ok(()); + } + let (line, left, right) = browser + .lines() + .zip(native.lines()) + .enumerate() + .find(|(_, (left, right))| left != right) + .map_or((0, "", ""), |(index, (left, right))| { + (index + 1, left, right) + }); + Err(format!( + "text differs from the native export at line {line}: browser {left:?}, native {right:?} ({} and {} bytes)", + browser.len(), + native.len() + )) +} + +fn check_storey(step: &str, report: &str) -> Result<(), Box> { + let browser = browser_export(step, report, "millimetre")?; + let products = browser.document.product_names()?.len(); + let entities = browser.document.entity_count(); + let bytes = browser.text.len(); + if products != 1200 || entities > 2_000_000 || bytes > 64 * 1024 * 1024 { + return Err(format!( + "storey export has {products} products (want 1200), {entities} entities (at most 2000000) and {bytes} bytes (at most 64 MiB)" + ) + .into()); + } + Ok(()) +} diff --git a/main/opengeometry/rust-toolchain.toml b/main/opengeometry/rust-toolchain.toml new file mode 100644 index 0000000..e8346a8 --- /dev/null +++ b/main/opengeometry/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.88.0" +targets = ["wasm32-unknown-unknown"] diff --git a/main/opengeometry/src/analytic/booleans.rs b/main/opengeometry/src/analytic/booleans.rs deleted file mode 100644 index a57be1b..0000000 --- a/main/opengeometry/src/analytic/booleans.rs +++ /dev/null @@ -1,13466 +0,0 @@ -use super::{ - face_intersection::{intersect_breps, intersect_faces, FaceView}, - geometry::{add, cross, dot, norm, scale, sub, unit}, - intersection::IntersectionDefinition, - primitives::{self, boundary, plane_boundary, uv_line, Builder}, - query::{classify_point, face_contains_uv, PointClassification}, - ssi::intersect_surfaces, - topology::*, - Curve, CurveGeometry, Frame3, GeometryError, Point3, Surface, SurfaceGeometry, -}; -use crate::{ - geometry::{ - boolean2d::{boolean_oriented_regions, PlanarBooleanOp, RingRegion}, - poly2d::{self, Pt2}, - }, - math::{ - interval::Interval, - predicates::{sphere_sphere_relation, Sign}, - }, -}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum BooleanOp { - Union, - Intersection, - Subtraction, -} - -#[derive(Clone, Debug, Serialize)] -pub struct FaceMapping { - pub source: FaceSource, - pub result_faces: Vec, -} -#[derive(Clone, Debug, Serialize)] -pub struct BooleanReport { - pub operation: BooleanOp, - pub quality: GeometryQuality, - pub contacts: Vec, - pub coincident: bool, - pub face_mappings: Vec, -} -pub struct BooleanResult { - pub brep: BrepEnvelope, - pub report: BooleanReport, -} - -fn comparable(input: &T) -> Result { - serde_json::to_value(input).map_err(|error| GeometryError::InvalidGeometry(error.to_string())) -} - -fn coordinates_match(a: [f64; N], b: [f64; N], tolerance: f64) -> bool { - a.into_iter() - .zip(b) - .all(|(left, right)| (left - right).abs() <= tolerance) -} - -fn frames_match(a: Frame3, b: Frame3, geometric: f64) -> bool { - coordinates_match(a.origin, b.origin, geometric) - && coordinates_match(a.x, b.x, 1e-12) - && coordinates_match(a.y, b.y, 1e-12) - && coordinates_match(a.z, b.z, 1e-12) -} - -fn cylinder_geometry_matches( - actual: &GeometryStore, - expected: &GeometryStore, - geometric: f64, -) -> bool { - if actual.surfaces.len() != expected.surfaces.len() - || actual.curves.len() != expected.curves.len() - || actual.pcurves.len() != expected.pcurves.len() - || !actual.intersections.is_empty() - || !expected.intersections.is_empty() - { - return false; - } - let scalar = geometric.max(1e-12); - let surfaces = actual - .surfaces - .iter() - .zip(&expected.surfaces) - .all(|(actual, expected)| match (actual, expected) { - ( - SurfaceGeometry::Plane { frame: actual }, - SurfaceGeometry::Plane { frame: expected }, - ) => frames_match(*actual, *expected, geometric), - ( - SurfaceGeometry::Cylinder { - frame: actual_frame, - radius: actual_radius, - }, - SurfaceGeometry::Cylinder { - frame: expected_frame, - radius: expected_radius, - }, - ) => { - frames_match(*actual_frame, *expected_frame, geometric) - && (actual_radius - expected_radius).abs() <= geometric - } - ( - SurfaceGeometry::Cone { - frame: actual_frame, - semi_angle: actual_angle, - }, - SurfaceGeometry::Cone { - frame: expected_frame, - semi_angle: expected_angle, - }, - ) => { - frames_match(*actual_frame, *expected_frame, geometric) - && (actual_angle - expected_angle).abs() <= 1.0e-12 - } - _ => false, - }); - let curves = actual - .curves - .iter() - .zip(&expected.curves) - .all(|(actual, expected)| match (actual, expected) { - ( - CurveGeometry::Circle { - frame: actual_frame, - radius: actual_radius, - }, - CurveGeometry::Circle { - frame: expected_frame, - radius: expected_radius, - }, - ) => { - frames_match(*actual_frame, *expected_frame, geometric) - && (actual_radius - expected_radius).abs() <= geometric - } - ( - CurveGeometry::Line { - origin: actual_origin, - direction: actual_direction, - }, - CurveGeometry::Line { - origin: expected_origin, - direction: expected_direction, - }, - ) => { - coordinates_match(*actual_origin, *expected_origin, geometric) - && coordinates_match(*actual_direction, *expected_direction, 1e-12) - } - _ => false, - }); - let pcurves = actual - .pcurves - .iter() - .zip(&expected.pcurves) - .all(|(actual, expected)| match (actual, expected) { - ( - PcurveGeometry::Line2 { - origin: actual_origin, - direction: actual_direction, - }, - PcurveGeometry::Line2 { - origin: expected_origin, - direction: expected_direction, - }, - ) => { - coordinates_match(*actual_origin, *expected_origin, scalar) - && coordinates_match(*actual_direction, *expected_direction, 1e-12) - } - ( - PcurveGeometry::Conic2 { - origin: actual_origin, - axis_a: actual_a, - axis_b: actual_b, - }, - PcurveGeometry::Conic2 { - origin: expected_origin, - axis_a: expected_a, - axis_b: expected_b, - }, - ) => { - coordinates_match(*actual_origin, *expected_origin, scalar) - && coordinates_match(*actual_a, *expected_a, scalar) - && coordinates_match(*actual_b, *expected_b, scalar) - } - _ => false, - }); - surfaces && curves && pcurves -} - -fn normalize_cylinder_topology(actual: &mut Topology, expected: &Topology, geometric: f64) -> bool { - if actual.vertices.len() != expected.vertices.len() - || actual.edges.len() != expected.edges.len() - || actual.faces.len() != expected.faces.len() - { - return false; - } - let scalar = geometric.max(1e-12); - for (actual, expected) in actual.edges.iter_mut().zip(&expected.edges) { - match (&mut actual.geometry, &expected.geometry) { - ( - EdgeGeometry::Curve { - curve: actual_curve, - range: actual_range, - }, - EdgeGeometry::Curve { - curve: expected_curve, - range: expected_range, - }, - ) if actual_curve == expected_curve - && (actual_range.lo - expected_range.lo).abs() <= scalar - && (actual_range.hi - expected_range.hi).abs() <= scalar => - { - *actual_range = *expected_range; - } - ( - EdgeGeometry::Collapsed { vertex: actual }, - EdgeGeometry::Collapsed { vertex: expected }, - ) if actual == expected => {} - _ => return false, - } - } - for (actual, expected) in actual.faces.iter_mut().zip(&expected.faces) { - for axis in 0..2 { - let a = &mut actual.trim.uv_bounds[axis]; - let e = expected.trim.uv_bounds[axis]; - if (a.lo - e.lo).abs() > scalar || (a.hi - e.hi).abs() > scalar { - return false; - } - *a = e; - } - } - true -} - -struct SphereInput<'a> { - brep: &'a BrepEnvelope, - frame: Frame3, - radius: f64, -} - -struct CylinderInput<'a> { - brep: &'a BrepEnvelope, - frame: Frame3, - radius: f64, - height: f64, -} - -struct TorusInput<'a> { - brep: &'a BrepEnvelope, - frame: Frame3, - major_radius: f64, - minor_radius: f64, -} - -struct ConicSectionInput<'a> { - brep: &'a BrepEnvelope, - frame: Frame3, - semi_angle: f64, - axial_range: Interval, - lower_radius: f64, - upper_radius: f64, -} - -fn torus_gap() -> GeometryError { - GeometryError::CoverageGap { - families: ["canonical full ring torus".into(), "analytic solid".into()], - } -} - -fn full_torus(brep: &BrepEnvelope) -> Result, GeometryError> { - brep.validate()?; - let (frame, major_radius, minor_radius) = match brep.geometry.surfaces.as_slice() { - [SurfaceGeometry::Torus { - frame, - major_radius, - minor_radius, - }] => (*frame, *major_radius, *minor_radius), - _ => return Err(torus_gap()), - }; - if !matches!(brep.quality, GeometryQuality::Analytic) - || !brep.geometry.intersections.is_empty() - || brep.topology.vertices.len() != 1 - || brep.topology.edges.len() != 2 - || brep.topology.halfedges.len() != 4 - || brep.topology.loops.len() != 1 - || brep.topology.faces.len() != 1 - || !brep.topology.wires.is_empty() - || brep.topology.shells.len() != 1 - || brep.solids.len() != 1 - { - return Err(torus_gap()); - } - let canonical = primitives::torus( - brep.id.clone(), - frame, - major_radius, - minor_radius, - brep.accuracy, - )?; - let mut topology = brep.topology.clone(); - for (actual, expected) in topology - .vertices - .iter_mut() - .zip(&canonical.topology.vertices) - { - if norm(sub(actual.position, expected.position)) > brep.accuracy.geometric { - return Err(torus_gap()); - } - actual.position = expected.position; - actual.tolerance = expected.tolerance; - } - for (actual, expected) in topology.edges.iter_mut().zip(&canonical.topology.edges) { - actual.tolerance = expected.tolerance; - let ( - EdgeGeometry::Curve { - curve: actual_curve, - range: actual_range, - }, - EdgeGeometry::Curve { - curve: expected_curve, - range: expected_range, - }, - ) = (&mut actual.geometry, &expected.geometry) - else { - return Err(torus_gap()); - }; - if actual_curve != expected_curve - || (actual_range.lo - expected_range.lo).abs() > brep.accuracy.geometric - || (actual_range.hi - expected_range.hi).abs() > brep.accuracy.geometric - { - return Err(torus_gap()); - } - *actual_range = *expected_range; - } - topology.faces[0].key = canonical.topology.faces[0].key.clone(); - topology.faces[0].provenance = canonical.topology.faces[0].provenance.clone(); - topology.faces[0].trim.uv_bounds = canonical.topology.faces[0].trim.uv_bounds; - if comparable(&brep.geometry)? != comparable(&canonical.geometry)? - || comparable(&topology)? != comparable(&canonical.topology)? - || comparable(&brep.solids)? != comparable(&canonical.solids)? - { - return Err(torus_gap()); - } - Ok(TorusInput { - brep, - frame, - major_radius, - minor_radius, - }) -} - -fn cylinder_gap() -> GeometryError { - GeometryError::CoverageGap { - families: [ - "canonical full cylinder".into(), - "canonical full cylinder".into(), - ], - } -} - -fn full_cylinder(brep: &BrepEnvelope) -> Result, GeometryError> { - brep.validate()?; - let (frame, radius) = match brep.geometry.surfaces.as_slice() { - [SurfaceGeometry::Cylinder { frame, radius }, SurfaceGeometry::Plane { .. }, SurfaceGeometry::Plane { .. }] => { - (*frame, *radius) - } - _ => return Err(cylinder_gap()), - }; - if !matches!(brep.quality, GeometryQuality::Analytic) - || !brep.geometry.intersections.is_empty() - || brep.topology.faces.len() != 3 - || brep.topology.vertices.len() != 2 - || brep.topology.edges.len() != 3 - || brep.topology.halfedges.len() != 6 - || brep.topology.loops.len() != 3 - || !brep.topology.wires.is_empty() - || brep.topology.shells.len() != 1 - || brep.solids.len() != 1 - { - return Err(cylinder_gap()); - } - let axial_range = brep.topology.faces[0].trim.uv_bounds[1]; - if axial_range.lo != 0.0 { - return Err(cylinder_gap()); - } - let height = axial_range.hi; - let canonical = primitives::cylinder(brep.id.clone(), frame, radius, height, brep.accuracy)?; - let mut topology = brep.topology.clone(); - for (face, expected) in topology.faces.iter_mut().zip(&canonical.topology.faces) { - face.key = expected.key.clone(); - face.provenance = expected.provenance.clone(); - } - for vertex in &mut topology.vertices { - vertex.tolerance = brep.accuracy.geometric; - let expected = canonical.topology.vertices[vertex.id as usize].position; - if norm(sub(vertex.position, expected)) > brep.accuracy.geometric { - return Err(cylinder_gap()); - } - vertex.position = expected; - } - for edge in &mut topology.edges { - edge.tolerance = brep.accuracy.geometric; - } - if !normalize_cylinder_topology(&mut topology, &canonical.topology, brep.accuracy.geometric) { - return Err(cylinder_gap()); - } - if !cylinder_geometry_matches(&brep.geometry, &canonical.geometry, brep.accuracy.geometric) - || comparable(&topology)? != comparable(&canonical.topology)? - || comparable(&brep.solids)? != comparable(&canonical.solids)? - { - return Err(cylinder_gap()); - } - Ok(CylinderInput { - brep, - frame, - radius, - height, - }) -} - -fn conic_section_gap() -> GeometryError { - GeometryError::CoverageGap { - families: ["canonical cone or frustum".into(), "analytic solid".into()], - } -} - -fn full_conic_section(brep: &BrepEnvelope) -> Result, GeometryError> { - brep.validate()?; - let (frame, semi_angle) = match brep.geometry.surfaces.first() { - Some(SurfaceGeometry::Cone { frame, semi_angle }) => (*frame, *semi_angle), - _ => return Err(conic_section_gap()), - }; - if !matches!(brep.quality, GeometryQuality::Analytic) - || !brep.geometry.intersections.is_empty() - || !brep.topology.wires.is_empty() - || brep.topology.shells.len() != 1 - || brep.solids.len() != 1 - || !brep.solids[0].cavity_shells.is_empty() - || !matches!(brep.geometry.surfaces.len(), 2 | 3) - || brep.geometry.surfaces[1..] - .iter() - .any(|surface| !matches!(surface, SurfaceGeometry::Plane { .. })) - || brep.topology.faces.len() != brep.geometry.surfaces.len() - { - return Err(conic_section_gap()); - } - let axial_range = brep.topology.faces[0].trim.uv_bounds[1]; - if axial_range.lo < 0.0 - || axial_range.hi <= axial_range.lo - || semi_angle <= 0.0 - || semi_angle >= std::f64::consts::FRAC_PI_2 - { - return Err(conic_section_gap()); - } - let slope = semi_angle.tan(); - let lower_radius = axial_range.lo * slope; - let upper_radius = axial_range.hi * slope; - let canonical = if axial_range.lo <= brep.accuracy.geometric { - if axial_range.lo != 0.0 || brep.geometry.surfaces.len() != 2 { - return Err(conic_section_gap()); - } - let base_frame = Frame3 { - origin: frame.point([0.0, 0.0, axial_range.hi]), - x: frame.x, - y: scale(frame.y, -1.0), - z: scale(frame.z, -1.0), - }; - primitives::cone( - brep.id.clone(), - base_frame, - upper_radius, - axial_range.hi, - brep.accuracy, - )? - } else { - if brep.geometry.surfaces.len() != 3 { - return Err(conic_section_gap()); - } - let base_frame = Frame3 { - origin: frame.point([0.0, 0.0, axial_range.lo]), - ..frame - }; - primitives::frustum( - brep.id.clone(), - base_frame, - lower_radius, - upper_radius, - axial_range.width(), - brep.accuracy, - )? - }; - let mut topology = brep.topology.clone(); - for (face, expected) in topology.faces.iter_mut().zip(&canonical.topology.faces) { - face.key = expected.key.clone(); - face.provenance = expected.provenance.clone(); - } - for vertex in &mut topology.vertices { - vertex.tolerance = brep.accuracy.geometric; - let expected = canonical - .topology - .vertices - .get(vertex.id as usize) - .ok_or_else(conic_section_gap)? - .position; - if norm(sub(vertex.position, expected)) > brep.accuracy.geometric { - return Err(conic_section_gap()); - } - vertex.position = expected; - } - for edge in &mut topology.edges { - edge.tolerance = brep.accuracy.geometric; - } - if !normalize_cylinder_topology(&mut topology, &canonical.topology, brep.accuracy.geometric) - || !cylinder_geometry_matches(&brep.geometry, &canonical.geometry, brep.accuracy.geometric) - || comparable(&topology)? != comparable(&canonical.topology)? - || comparable(&brep.solids)? != comparable(&canonical.solids)? - { - return Err(conic_section_gap()); - } - Ok(ConicSectionInput { - brep, - frame, - semi_angle, - axial_range, - lower_radius, - upper_radius, - }) -} -fn gap() -> GeometryError { - GeometryError::CoverageGap { - families: ["full sphere input".into(), "full sphere input".into()], - } -} -fn full_sphere(brep: &BrepEnvelope) -> Result, GeometryError> { - brep.validate()?; - let (frame, radius) = match brep.geometry.surfaces.as_slice() { - [SurfaceGeometry::Sphere { frame, radius }] => (*frame, *radius), - _ => return Err(gap()), - }; - if !matches!(brep.quality, GeometryQuality::Analytic) - || brep.geometry.curves.len() != 1 - || brep.geometry.pcurves.len() != 4 - || !brep.geometry.intersections.is_empty() - || brep.topology.faces.len() != 1 - || brep.topology.vertices.len() != 2 - || brep.topology.edges.len() != 3 - || brep.topology.halfedges.len() != 4 - || brep.topology.loops.len() != 1 - || !brep.topology.wires.is_empty() - || brep.topology.shells.len() != 1 - || brep.solids.len() != 1 - { - return Err(gap()); - } - let canonical = primitives::sphere(brep.id.clone(), frame, radius, brep.accuracy)?; - let mut topology = brep.topology.clone(); - topology.faces[0].key = canonical.topology.faces[0].key.clone(); - topology.faces[0].provenance = canonical.topology.faces[0].provenance.clone(); - for vertex in &mut topology.vertices { - vertex.tolerance = brep.accuracy.geometric; - let expected = canonical.topology.vertices[vertex.id as usize].position; - if norm(sub(vertex.position, expected)) > brep.accuracy.geometric { - return Err(gap()); - } - vertex.position = expected; - } - for edge in &mut topology.edges { - edge.tolerance = brep.accuracy.geometric; - } - let value = |input| { - serde_json::to_value(input).map_err(|e| GeometryError::InvalidGeometry(e.to_string())) - }; - if value(&brep.geometry)? != value(&canonical.geometry)? - || serde_json::to_value(&topology) - .map_err(|e| GeometryError::InvalidGeometry(e.to_string()))? - != serde_json::to_value(&canonical.topology) - .map_err(|e| GeometryError::InvalidGeometry(e.to_string()))? - || serde_json::to_value(&brep.solids) - .map_err(|e| GeometryError::InvalidGeometry(e.to_string()))? - != serde_json::to_value(&canonical.solids) - .map_err(|e| GeometryError::InvalidGeometry(e.to_string()))? - { - return Err(gap()); - } - Ok(SphereInput { - brep, - frame, - radius, - }) -} -fn source(input: &SphereInput<'_>) -> FaceSource { - FaceSource { - entity: input.brep.id.clone(), - body: input.brep.id.clone(), - key: input.brep.topology.faces[0].key.clone(), - face: 0, - } -} -fn provenance(input: &SphereInput<'_>, role: FaceRole, reversed: bool) -> FaceProvenance { - FaceProvenance { - sources: vec![source(input)], - role, - reversed, - } -} -fn reverse(sense: Orientation) -> Orientation { - if sense == Orientation::Forward { - Orientation::Reverse - } else { - Orientation::Forward - } -} -fn reverse_face(brep: &mut BrepEnvelope, face: u32) { - brep.topology.faces[face as usize].sense = reverse(brep.topology.faces[face as usize].sense); - for h in &mut brep.topology.halfedges { - if h.face == Some(face) { - std::mem::swap(&mut h.from, &mut h.to); - std::mem::swap(&mut h.next, &mut h.prev); - h.geometry_use.sense = reverse(h.geometry_use.sense); - } - } - for vertex in &mut brep.topology.vertices { - vertex.outgoing_halfedge = brep - .topology - .halfedges - .iter() - .find(|h| h.from == vertex.id) - .map(|h| h.id); - } -} - -fn append_analytic_input( - out: &mut BrepEnvelope, - input: &BrepEnvelope, -) -> Result, GeometryError> { - let offset = |value: usize, table: &str| { - u32::try_from(value).map_err(|_| GeometryError::LimitExceeded(table.into())) - }; - let surface_offset = offset(out.geometry.surfaces.len(), "surface IDs")?; - let curve_offset = offset(out.geometry.curves.len(), "curve IDs")?; - let pcurve_offset = offset(out.geometry.pcurves.len(), "pcurve IDs")?; - let intersection_offset = offset(out.geometry.intersections.len(), "intersection IDs")?; - let vertex_offset = offset(out.topology.vertices.len(), "vertex IDs")?; - let edge_offset = offset(out.topology.edges.len(), "edge IDs")?; - let halfedge_offset = offset(out.topology.halfedges.len(), "halfedge IDs")?; - let loop_offset = offset(out.topology.loops.len(), "loop IDs")?; - let face_offset = offset(out.topology.faces.len(), "face IDs")?; - let wire_offset = offset(out.topology.wires.len(), "wire IDs")?; - let shell_offset = offset(out.topology.shells.len(), "shell IDs")?; - - out.geometry - .surfaces - .extend(input.geometry.surfaces.clone()); - out.geometry - .intersections - .extend( - input - .geometry - .intersections - .iter() - .cloned() - .map(|mut definition| { - definition.surfaces = definition.surfaces.map(|id| id + surface_offset); - definition - }), - ); - out.geometry - .curves - .extend(input.geometry.curves.iter().cloned().map(|mut curve| { - if let CurveGeometry::Intersection { definition } = &mut curve { - *definition += intersection_offset; - } - curve - })); - out.geometry - .pcurves - .extend(input.geometry.pcurves.iter().cloned().map(|mut pcurve| { - match &mut pcurve { - PcurveGeometry::ProjectedCurve { curve, surface, .. } => { - *curve += curve_offset; - *surface += surface_offset; - } - PcurveGeometry::IntersectionSide { definition, .. } => { - *definition += intersection_offset; - } - PcurveGeometry::Line2 { .. } | PcurveGeometry::Conic2 { .. } => {} - } - pcurve - })); - - out.topology - .vertices - .extend(input.topology.vertices.iter().cloned().map(|mut vertex| { - vertex.id += vertex_offset; - vertex.outgoing_halfedge = vertex.outgoing_halfedge.map(|id| id + halfedge_offset); - vertex - })); - out.topology - .edges - .extend(input.topology.edges.iter().cloned().map(|mut edge| { - edge.id += edge_offset; - edge.halfedge += halfedge_offset; - edge.twin_halfedge = edge.twin_halfedge.map(|id| id + halfedge_offset); - match &mut edge.geometry { - EdgeGeometry::Curve { curve, .. } => *curve += curve_offset, - EdgeGeometry::Collapsed { vertex } => *vertex += vertex_offset, - } - edge - })); - out.topology.halfedges.extend( - input - .topology - .halfedges - .iter() - .cloned() - .map(|mut halfedge| { - halfedge.id += halfedge_offset; - halfedge.from += vertex_offset; - halfedge.to += vertex_offset; - halfedge.edge += edge_offset; - halfedge.twin = halfedge.twin.map(|id| id + halfedge_offset); - halfedge.next = halfedge.next.map(|id| id + halfedge_offset); - halfedge.prev = halfedge.prev.map(|id| id + halfedge_offset); - halfedge.face = halfedge.face.map(|id| id + face_offset); - halfedge.loop_ref = halfedge.loop_ref.map(|id| id + loop_offset); - halfedge.wire_ref = halfedge.wire_ref.map(|id| id + wire_offset); - halfedge.geometry_use.pcurve = - halfedge.geometry_use.pcurve.map(|id| id + pcurve_offset); - halfedge - }), - ); - out.topology - .loops - .extend(input.topology.loops.iter().cloned().map(|mut loop_| { - loop_.id += loop_offset; - loop_.start_halfedge += halfedge_offset; - loop_.face_ref += face_offset; - loop_ - })); - - let mut mappings = Vec::with_capacity(input.topology.faces.len()); - out.topology - .faces - .extend(input.topology.faces.iter().cloned().map(|mut face| { - let source = FaceSource { - entity: input.id.clone(), - body: input.id.clone(), - key: face.key.clone(), - face: face.id, - }; - face.id += face_offset; - face.key = format!("{}:{}", input.id, face.key); - face.surface += surface_offset; - face.trim.outer += loop_offset; - for hole in &mut face.trim.holes { - *hole += loop_offset; - } - face.shell_ref = face.shell_ref.map(|id| id + shell_offset); - face.provenance = FaceProvenance { - sources: vec![source.clone()], - role: FaceRole::Preserved, - reversed: false, - }; - mappings.push(FaceMapping { - source, - result_faces: vec![face.id], - }); - face - })); - out.topology - .wires - .extend(input.topology.wires.iter().cloned().map(|mut wire| { - wire.id += wire_offset; - wire.start_halfedge += halfedge_offset; - wire - })); - out.topology - .shells - .extend(input.topology.shells.iter().cloned().map(|mut shell| { - shell.id += shell_offset; - for face in &mut shell.faces { - *face += face_offset; - } - shell - })); - out.solids - .extend(input.solids.iter().cloned().map(|mut solid| { - solid.outer_shell += shell_offset; - for shell in &mut solid.cavity_shells { - *shell += shell_offset; - } - solid - })); - Ok(mappings) -} - -fn disjoint_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result, GeometryError> { - let (Some(a_bounds), Some(b_bounds)) = (a.bounds()?, b.bounds()?) else { - return Ok(None); - }; - let clearance = 4.0 * a.accuracy.geometric.max(b.accuracy.geometric); - let separated = (0..3).any(|axis| { - a_bounds.axes[axis].hi + clearance < b_bounds.axes[axis].lo - || b_bounds.axes[axis].hi + clearance < a_bounds.axes[axis].lo - }); - if !separated { - return Ok(None); - } - Ok(Some(separate_boolean(a, b, operation, id, Vec::new())?)) -} - -fn separate_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, - contacts: Vec, -) -> Result { - if !matches!(a.quality, GeometryQuality::Analytic) - || !matches!(b.quality, GeometryQuality::Analytic) - { - return Err(GeometryError::UnsupportedGeometry( - "analytic boolean operands must have analytic quality".into(), - )); - } - let accuracy = Accuracy { - geometric: a.accuracy.geometric.max(b.accuracy.geometric), - intersection: a.accuracy.intersection.max(b.accuracy.intersection), - tessellation: a.accuracy.tessellation.max(b.accuracy.tessellation), - exchange: a.accuracy.exchange.max(b.accuracy.exchange), - }; - let mut out = BrepEnvelope::new(id, accuracy)?; - let mut mappings = match operation { - BooleanOp::Union | BooleanOp::Subtraction => append_analytic_input(&mut out, a)?, - BooleanOp::Intersection => a - .topology - .faces - .iter() - .map(|face| FaceMapping { - source: FaceSource { - entity: a.id.clone(), - body: a.id.clone(), - key: face.key.clone(), - face: face.id, - }, - result_faces: Vec::new(), - }) - .collect(), - }; - match operation { - BooleanOp::Union => mappings.extend(append_analytic_input(&mut out, b)?), - BooleanOp::Intersection | BooleanOp::Subtraction => { - mappings.extend(b.topology.faces.iter().map(|face| FaceMapping { - source: FaceSource { - entity: b.id.clone(), - body: b.id.clone(), - key: face.key.clone(), - face: face.id, - }, - result_faces: Vec::new(), - })) - } - } - out.revision = a - .revision - .max(b.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts, - coincident: false, - face_mappings: mappings, - }, - }) -} - -pub(super) fn analytic_face_mappings<'a>( - out: &BrepEnvelope, - inputs: impl IntoIterator, -) -> Vec { - inputs - .into_iter() - .flat_map(|input| { - input.topology.faces.iter().map(|face| { - let source = FaceSource { - entity: input.id.clone(), - body: input.id.clone(), - key: face.key.clone(), - face: face.id, - }; - let result_faces = out - .topology - .faces - .iter() - .filter(|result| { - result.provenance.sources.iter().any(|candidate| { - candidate.entity == source.entity - && candidate.body == source.body - && candidate.key == source.key - && candidate.face == source.face - }) - }) - .map(|result| result.id) - .collect(); - FaceMapping { - source, - result_faces, - } - }) - }) - .collect() -} - -#[derive(Clone)] -struct PrismaticSide { - from: Point3, - to: Point3, - source: FaceSource, -} - -pub(super) struct PrismaticInput<'a> { - pub(super) brep: &'a BrepEnvelope, - pub(super) frame: Frame3, - pub(super) height: f64, - pub(super) contours: Vec>, - sides: Vec, -} - -fn prismatic_gap() -> GeometryError { - GeometryError::CoverageGap { - families: [ - "canonical planar profile extrusion".into(), - "canonical planar profile extrusion".into(), - ], - } -} - -pub(super) fn brep_face_source(brep: &BrepEnvelope, face: u32) -> FaceSource { - FaceSource { - entity: brep.id.clone(), - body: brep.id.clone(), - key: brep.topology.faces[face as usize].key.clone(), - face, - } -} - -fn loop_points( - brep: &BrepEnvelope, - loop_id: u32, - frame: Frame3, -) -> Result, GeometryError> { - let start = brep - .topology - .loops - .get(loop_id as usize) - .ok_or_else(prismatic_gap)? - .start_halfedge; - let mut current = start; - let mut result = Vec::new(); - loop { - let halfedge = brep - .topology - .halfedges - .get(current as usize) - .ok_or_else(prismatic_gap)?; - let position = brep - .topology - .vertices - .get(halfedge.from as usize) - .ok_or_else(prismatic_gap)? - .position; - let local = frame.local(position); - if local[2].abs() > brep.accuracy.geometric { - return Err(prismatic_gap()); - } - result.push([local[0], local[1]]); - current = halfedge.next.ok_or_else(prismatic_gap)?; - if current == start { - break; - } - if result.len() > brep.topology.halfedges.len() { - return Err(prismatic_gap()); - } - } - Ok(result) -} - -fn planar_extrusion_geometry_matches( - actual: &GeometryStore, - expected: &GeometryStore, - geometric: f64, -) -> bool { - if actual.surfaces.len() != expected.surfaces.len() - || actual.curves.len() != expected.curves.len() - || actual.pcurves.len() != expected.pcurves.len() - || !actual.intersections.is_empty() - || !expected.intersections.is_empty() - { - return false; - } - let surfaces = actual - .surfaces - .iter() - .zip(&expected.surfaces) - .all(|(actual, expected)| match (actual, expected) { - ( - SurfaceGeometry::Plane { frame: actual }, - SurfaceGeometry::Plane { frame: expected }, - ) => frames_match(*actual, *expected, geometric), - _ => false, - }); - let curves = actual - .curves - .iter() - .zip(&expected.curves) - .all(|(actual, expected)| match (actual, expected) { - ( - CurveGeometry::Line { - origin: actual_origin, - direction: actual_direction, - }, - CurveGeometry::Line { - origin: expected_origin, - direction: expected_direction, - }, - ) => { - coordinates_match(*actual_origin, *expected_origin, geometric) - && coordinates_match(*actual_direction, *expected_direction, 1.0e-12) - } - _ => false, - }); - let pcurves = actual - .pcurves - .iter() - .zip(&expected.pcurves) - .all(|(actual, expected)| match (actual, expected) { - ( - PcurveGeometry::Line2 { - origin: actual_origin, - direction: actual_direction, - }, - PcurveGeometry::Line2 { - origin: expected_origin, - direction: expected_direction, - }, - ) => { - coordinates_match(*actual_origin, *expected_origin, geometric) - && coordinates_match(*actual_direction, *expected_direction, 1.0e-12) - } - _ => false, - }); - surfaces && curves && pcurves -} - -fn planar_extrusion_topology_matches( - actual: &Topology, - expected: &Topology, - geometric: f64, -) -> Result { - if actual.vertices.len() != expected.vertices.len() - || actual.edges.len() != expected.edges.len() - || actual.halfedges.len() != expected.halfedges.len() - || actual.loops.len() != expected.loops.len() - || actual.faces.len() != expected.faces.len() - || actual.shells.len() != expected.shells.len() - || actual.wires.len() != expected.wires.len() - { - return Ok(false); - } - let mut normalized = actual.clone(); - for (vertex, expected) in normalized.vertices.iter_mut().zip(&expected.vertices) { - if norm(sub(vertex.position, expected.position)) > geometric { - return Ok(false); - } - vertex.position = expected.position; - vertex.tolerance = expected.tolerance; - } - for (edge, expected) in normalized.edges.iter_mut().zip(&expected.edges) { - edge.tolerance = expected.tolerance; - match (&mut edge.geometry, &expected.geometry) { - ( - EdgeGeometry::Curve { - curve: actual_curve, - range: actual_range, - }, - EdgeGeometry::Curve { - curve: expected_curve, - range: expected_range, - }, - ) if actual_curve == expected_curve - && (actual_range.lo - expected_range.lo).abs() <= geometric - && (actual_range.hi - expected_range.hi).abs() <= geometric => - { - *actual_range = *expected_range; - } - _ => return Ok(false), - } - } - for (face, expected) in normalized.faces.iter_mut().zip(&expected.faces) { - for axis in 0..2 { - if (face.trim.uv_bounds[axis].lo - expected.trim.uv_bounds[axis].lo).abs() > geometric - || (face.trim.uv_bounds[axis].hi - expected.trim.uv_bounds[axis].hi).abs() - > geometric - { - return Ok(false); - } - } - face.trim.uv_bounds = expected.trim.uv_bounds; - face.key = expected.key.clone(); - face.provenance = expected.provenance.clone(); - } - Ok(comparable(&normalized)? == comparable(expected)?) -} - -pub(super) fn full_planar_extrusion( - brep: &BrepEnvelope, -) -> Result, GeometryError> { - brep.validate()?; - if !matches!(brep.quality, GeometryQuality::Analytic) - || brep.topology.faces.len() < 5 - || brep.topology.shells.len() != 1 - || brep.solids.len() != 1 - || !brep.solids[0].cavity_shells.is_empty() - || !brep.topology.wires.is_empty() - { - return Err(prismatic_gap()); - } - let (top_frame, bottom_frame) = match ( - brep.geometry - .surfaces - .get(brep.topology.faces[0].surface as usize), - brep.geometry - .surfaces - .get(brep.topology.faces[1].surface as usize), - ) { - ( - Some(SurfaceGeometry::Plane { frame: top }), - Some(SurfaceGeometry::Plane { frame: bottom }), - ) => (*top, *bottom), - _ => return Err(prismatic_gap()), - }; - let height = dot(sub(top_frame.origin, bottom_frame.origin), top_frame.z); - if height <= 4.0 * brep.accuracy.geometric { - return Err(prismatic_gap()); - } - let frame = Frame3 { - origin: sub(top_frame.origin, scale(top_frame.z, height)), - x: top_frame.x, - y: top_frame.y, - z: top_frame.z, - }; - frame.validate().map_err(|_| prismatic_gap())?; - if norm(sub(bottom_frame.origin, frame.origin)) > brep.accuracy.geometric - || norm(sub(bottom_frame.x, frame.x)) > 1.0e-12 - || norm(sub(bottom_frame.y, scale(frame.y, -1.0))) > 1.0e-12 - || norm(sub(bottom_frame.z, scale(frame.z, -1.0))) > 1.0e-12 - { - return Err(prismatic_gap()); - } - let top = &brep.topology.faces[0]; - let mut profile = Vec::with_capacity(top.trim.holes.len() + 1); - profile.push(loop_points(brep, top.trim.outer, top_frame)?); - for &hole in &top.trim.holes { - profile.push(loop_points(brep, hole, top_frame)?); - } - let canonical = primitives::linear_extrusion( - brep.id.clone(), - frame, - profile[0].clone(), - profile[1..].to_vec(), - height, - brep.accuracy, - )?; - if !planar_extrusion_geometry_matches( - &brep.geometry, - &canonical.geometry, - brep.accuracy.geometric, - ) || !planar_extrusion_topology_matches( - &brep.topology, - &canonical.topology, - brep.accuracy.geometric, - )? || comparable(&brep.solids)? != comparable(&canonical.solids)? - { - return Err(prismatic_gap()); - } - let contours = profile - .iter() - .map(|ring| { - ring.iter() - .map(|point| frame.point([point[0], point[1], 0.0])) - .collect::>() - }) - .collect::>(); - let mut face = 2_u32; - let mut sides = Vec::new(); - for contour in &contours { - for index in 0..contour.len() { - sides.push(PrismaticSide { - from: contour[index], - to: contour[(index + 1) % contour.len()], - source: brep_face_source(brep, face), - }); - face += 1; - } - } - Ok(PrismaticInput { - brep, - frame, - height, - contours, - sides, - }) -} - -fn segment_contains(side: &PrismaticSide, from: Point3, to: Point3, tolerance: f64) -> bool { - let edge = sub(side.to, side.from); - let length = norm(edge); - if length <= tolerance { - return false; - } - let direction = scale(edge, 1.0 / length); - [from, to].into_iter().all(|point| { - let delta = sub(point, side.from); - let parameter = dot(delta, direction); - let closest = add(side.from, scale(direction, parameter)); - norm(sub(point, closest)) <= tolerance - && parameter >= -tolerance - && parameter <= length + tolerance - }) -} - -fn unique_sources(sources: impl IntoIterator) -> Vec { - let mut result = Vec::new(); - for source in sources { - if !result.iter().any(|existing: &FaceSource| { - existing.entity == source.entity - && existing.body == source.body - && existing.face == source.face - && existing.key == source.key - }) { - result.push(source); - } - } - result -} - -fn prismatic_face_provenance( - a: &PrismaticInput<'_>, - b: &PrismaticInput<'_>, - from: Point3, - to: Point3, - operation: BooleanOp, - tolerance: f64, -) -> Result { - let a_sources = a - .sides - .iter() - .filter(|side| segment_contains(side, from, to, tolerance)) - .map(|side| side.source.clone()) - .collect::>(); - let b_sources = b - .sides - .iter() - .filter(|side| segment_contains(side, from, to, tolerance)) - .map(|side| side.source.clone()) - .collect::>(); - if a_sources.is_empty() && b_sources.is_empty() { - return Err(GeometryError::InvalidTopology( - "planar boolean produced a boundary without source ancestry".into(), - )); - } - let reversed = operation == BooleanOp::Subtraction && a_sources.is_empty(); - let role = if !a_sources.is_empty() && !b_sources.is_empty() { - FaceRole::Coincident - } else if reversed { - FaceRole::Cut - } else { - FaceRole::Split - }; - Ok(FaceProvenance { - sources: unique_sources(a_sources.into_iter().chain(b_sources)), - role, - reversed, - }) -} - -fn region_profiles(region: &RingRegion) -> (Vec<[f64; 2]>, Vec>) { - let convert = |ring: &[Pt2]| ring.iter().map(|point| [point.x, point.z]).collect(); - ( - convert(®ion.outer), - region.holes.iter().map(|hole| convert(hole)).collect(), - ) -} - -fn profile_rings_match(a: &[Pt2], b: &[Pt2], tolerance: f64) -> bool { - a.len() == b.len() - && (0..b.len()).any(|offset| { - a.iter().enumerate().all(|(index, point)| { - let candidate = b[(index + offset) % b.len()]; - (point.x - candidate.x).abs() <= tolerance - && (point.z - candidate.z).abs() <= tolerance - }) - }) -} - -fn profiles_match(a: &[Vec], b: &[Vec], tolerance: f64) -> bool { - if a.len() != b.len() || a.is_empty() || !profile_rings_match(&a[0], &b[0], tolerance) { - return false; - } - let mut matched = vec![false; b.len() - 1]; - a[1..].iter().all(|ring| { - let Some(index) = b[1..].iter().enumerate().position(|(index, candidate)| { - !matched[index] && profile_rings_match(ring, candidate, tolerance) - }) else { - return false; - }; - matched[index] = true; - true - }) -} - -fn projected_segment_contains( - side: &PrismaticSide, - frame: Frame3, - from: Pt2, - to: Pt2, - tolerance: f64, -) -> bool { - let project = |point: Point3| { - let local = frame.local(point); - Pt2::new(local[0], local[1]) - }; - let start = project(side.from); - let end = project(side.to); - let edge = Pt2::new(end.x - start.x, end.z - start.z); - let length = (edge.x * edge.x + edge.z * edge.z).sqrt(); - if length <= tolerance { - return false; - } - let direction = Pt2::new(edge.x / length, edge.z / length); - [from, to].into_iter().all(|point| { - let delta = Pt2::new(point.x - start.x, point.z - start.z); - let parameter = delta.x * direction.x + delta.z * direction.z; - let closest = Pt2::new( - start.x + parameter * direction.x, - start.z + parameter * direction.z, - ); - let distance = ((point.x - closest.x).powi(2) + (point.z - closest.z).powi(2)).sqrt(); - distance <= tolerance && parameter >= -tolerance && parameter <= length + tolerance - }) -} - -pub(super) fn prismatic_profile_sources( - input: &PrismaticInput<'_>, - common_frame: Frame3, - from: Pt2, - to: Pt2, - tolerance: f64, -) -> Vec { - input - .sides - .iter() - .filter(|side| projected_segment_contains(side, common_frame, from, to, tolerance)) - .map(|side| side.source.clone()) - .collect() -} - -#[derive(Clone)] -struct PrismaticAxialSegment { - lo: f64, - hi: f64, - lower: FaceProvenance, - upper: FaceProvenance, - include_a_sides: bool, - include_b_sides: bool, - side_role: FaceRole, -} - -fn axial_profile_boolean( - a: &PrismaticInput<'_>, - b: &PrismaticInput<'_>, - a_contours: &[Vec], - a_span: [f64; 2], - b_span: [f64; 2], - operation: BooleanOp, - id: String, - accuracy: Accuracy, -) -> Result { - let a_caps = [brep_face_source(a.brep, 1), brep_face_source(a.brep, 0)]; - let b_caps = [brep_face_source(b.brep, 1), brep_face_source(b.brep, 0)]; - let cap_at = |boundary: f64, span: [f64; 2], caps: &[FaceSource; 2]| { - if (boundary - span[0]).abs() <= accuracy.geometric { - Some(caps[0].clone()) - } else if (boundary - span[1]).abs() <= accuracy.geometric { - Some(caps[1].clone()) - } else { - None - } - }; - let preserved_cap = |sources: Vec| { - let role = if sources.len() > 1 { - FaceRole::Coincident - } else { - FaceRole::Preserved - }; - FaceProvenance { - sources, - role, - reversed: false, - } - }; - let overlap = a_span[1].min(b_span[1]) - a_span[0].max(b_span[0]); - if overlap != 0.0 && overlap.abs() <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "profile extrusion axial overlap or clearance is below geometric resolution".into(), - )); - } - let mut segments = Vec::new(); - match operation { - BooleanOp::Union if overlap >= 0.0 => { - let lo = a_span[0].min(b_span[0]); - let hi = a_span[1].max(b_span[1]); - let lower = [cap_at(lo, a_span, &a_caps), cap_at(lo, b_span, &b_caps)] - .into_iter() - .flatten() - .collect(); - let upper = [cap_at(hi, a_span, &a_caps), cap_at(hi, b_span, &b_caps)] - .into_iter() - .flatten() - .collect(); - segments.push(PrismaticAxialSegment { - lo, - hi, - lower: preserved_cap(lower), - upper: preserved_cap(upper), - include_a_sides: true, - include_b_sides: true, - side_role: FaceRole::Coincident, - }); - } - BooleanOp::Union => { - for (span, caps, include_a_sides, include_b_sides) in [ - (a_span, a_caps.clone(), true, false), - (b_span, b_caps.clone(), false, true), - ] { - segments.push(PrismaticAxialSegment { - lo: span[0], - hi: span[1], - lower: preserved_cap(vec![caps[0].clone()]), - upper: preserved_cap(vec![caps[1].clone()]), - include_a_sides, - include_b_sides, - side_role: FaceRole::Preserved, - }); - } - } - BooleanOp::Intersection if overlap > 0.0 => { - let lo = a_span[0].max(b_span[0]); - let hi = a_span[1].min(b_span[1]); - let lower = [cap_at(lo, a_span, &a_caps), cap_at(lo, b_span, &b_caps)] - .into_iter() - .flatten() - .collect(); - let upper = [cap_at(hi, a_span, &a_caps), cap_at(hi, b_span, &b_caps)] - .into_iter() - .flatten() - .collect(); - segments.push(PrismaticAxialSegment { - lo, - hi, - lower: preserved_cap(lower), - upper: preserved_cap(upper), - include_a_sides: true, - include_b_sides: true, - side_role: FaceRole::Coincident, - }); - } - BooleanOp::Intersection => {} - BooleanOp::Subtraction if overlap <= 0.0 => { - segments.push(PrismaticAxialSegment { - lo: a_span[0], - hi: a_span[1], - lower: preserved_cap(vec![a_caps[0].clone()]), - upper: preserved_cap(vec![a_caps[1].clone()]), - include_a_sides: true, - include_b_sides: false, - side_role: FaceRole::Preserved, - }); - } - BooleanOp::Subtraction => { - if b_span[0] > a_span[0] { - segments.push(PrismaticAxialSegment { - lo: a_span[0], - hi: b_span[0].min(a_span[1]), - lower: preserved_cap(vec![a_caps[0].clone()]), - upper: FaceProvenance { - sources: vec![b_caps[0].clone()], - role: FaceRole::Cut, - reversed: true, - }, - include_a_sides: true, - include_b_sides: false, - side_role: FaceRole::Split, - }); - } - if b_span[1] < a_span[1] { - segments.push(PrismaticAxialSegment { - lo: b_span[1].max(a_span[0]), - hi: a_span[1], - lower: FaceProvenance { - sources: vec![b_caps[1].clone()], - role: FaceRole::Cut, - reversed: true, - }, - upper: preserved_cap(vec![a_caps[1].clone()]), - include_a_sides: true, - include_b_sides: false, - side_role: FaceRole::Split, - }); - } - } - } - - let outer = a_contours[0] - .iter() - .map(|point| [point.x, point.z]) - .collect::>(); - let holes = a_contours[1..] - .iter() - .map(|ring| ring.iter().map(|point| [point.x, point.z]).collect()) - .collect::>>(); - let mut out = BrepEnvelope::new(id, accuracy)?; - for (index, segment) in segments.into_iter().enumerate() { - let mut frame = a.frame; - frame.origin = a.frame.point([0.0, 0.0, segment.lo]); - let part = primitives::linear_extrusion( - format!("{}:axial-region-{index}", out.id), - frame, - outer.clone(), - holes.clone(), - segment.hi - segment.lo, - accuracy, - )?; - let face_offset = out.topology.faces.len() as u32; - append_analytic_input(&mut out, &part)?; - out.topology.faces[face_offset as usize].provenance = segment.upper; - out.topology.faces[(face_offset + 1) as usize].provenance = segment.lower; - let mut local_face = 2_u32; - for ring in a_contours { - for edge in 0..ring.len() { - let from = ring[edge]; - let to = ring[(edge + 1) % ring.len()]; - let mut sources = Vec::new(); - if segment.include_a_sides { - sources.extend(prismatic_profile_sources( - a, - a.frame, - from, - to, - accuracy.geometric, - )); - } - if segment.include_b_sides { - sources.extend(prismatic_profile_sources( - b, - a.frame, - from, - to, - accuracy.geometric, - )); - } - let sources = unique_sources(sources); - if sources.is_empty() { - return Err(GeometryError::InvalidTopology( - "axial profile boolean produced a side without source ancestry".into(), - )); - } - out.topology.faces[(face_offset + local_face) as usize].provenance = - FaceProvenance { - sources, - role: segment.side_role, - reversed: false, - }; - local_face += 1; - } - } - } - out.revision = a - .brep - .revision - .max(b.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let face_mappings = analytic_face_mappings(&out, [a.brep, b.brep]); - Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: overlap >= 0.0, - face_mappings, - }, - }) -} - -fn boolean_planar_extrusions( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let a = full_planar_extrusion(a)?; - let b = full_planar_extrusion(b)?; - let accuracy = Accuracy { - geometric: a.brep.accuracy.geometric.max(b.brep.accuracy.geometric), - intersection: a - .brep - .accuracy - .intersection - .max(b.brep.accuracy.intersection), - tessellation: a - .brep - .accuracy - .tessellation - .max(b.brep.accuracy.tessellation), - exchange: a.brep.accuracy.exchange.max(b.brep.accuracy.exchange), - }; - if dot(a.frame.z, b.frame.z) < 1.0 - 1.0e-12 { - return Err(prismatic_gap()); - } - let to_common = |contours: &[Vec]| { - contours - .iter() - .map(|ring| { - ring.iter() - .map(|point| { - let local = a.frame.local(*point); - Pt2::new(local[0], local[1]) - }) - .collect::>() - }) - .collect::>() - }; - let a_contours = to_common(&a.contours); - let b_contours = to_common(&b.contours); - let b_lo = dot(sub(b.frame.origin, a.frame.origin), a.frame.z); - let b_hi = b_lo + b.height; - if b.contours - .iter() - .flatten() - .any(|point| (a.frame.local(*point)[2] - b_lo).abs() > accuracy.geometric) - { - return Err(prismatic_gap()); - } - let a_span = [0.0, a.height]; - let b_span = [b_lo, b_hi]; - let coextensive = - b_lo.abs() <= accuracy.geometric && (a.height - b_hi).abs() <= accuracy.geometric; - if !coextensive { - if !profiles_match(&a_contours, &b_contours, accuracy.geometric) { - return Err(prismatic_gap()); - } - return axial_profile_boolean(&a, &b, &a_contours, a_span, b_span, operation, id, accuracy); - } - let planar_operation = match operation { - BooleanOp::Union => PlanarBooleanOp::Union, - BooleanOp::Intersection => PlanarBooleanOp::Intersection, - BooleanOp::Subtraction => PlanarBooleanOp::Subtraction, - }; - let regions = boolean_oriented_regions( - &a_contours, - &b_contours, - planar_operation, - accuracy.intersection, - ); - let mut out = BrepEnvelope::new(id, accuracy)?; - for (region_index, region) in regions.iter().enumerate() { - let (outer, holes) = region_profiles(region); - let part = primitives::linear_extrusion( - format!("{}:region-{region_index}", out.id), - a.frame, - outer, - holes, - a.height, - accuracy, - )?; - let face_offset = out.topology.faces.len() as u32; - append_analytic_input(&mut out, &part)?; - let cap_sources = if operation == BooleanOp::Subtraction { - [ - vec![brep_face_source(a.brep, 0)], - vec![brep_face_source(a.brep, 1)], - ] - } else { - [ - vec![brep_face_source(a.brep, 0), brep_face_source(b.brep, 0)], - vec![brep_face_source(a.brep, 1), brep_face_source(b.brep, 1)], - ] - }; - for local in 0..2 { - out.topology.faces[(face_offset + local) as usize].provenance = FaceProvenance { - sources: cap_sources[local as usize].clone(), - role: FaceRole::Split, - reversed: false, - }; - } - let mut local_face = 2_u32; - for ring in std::iter::once(®ion.outer).chain(®ion.holes) { - for index in 0..ring.len() { - let from = a.frame.point([ring[index].x, ring[index].z, 0.0]); - let next = ring[(index + 1) % ring.len()]; - let to = a.frame.point([next.x, next.z, 0.0]); - out.topology.faces[(face_offset + local_face) as usize].provenance = - prismatic_face_provenance(&a, &b, from, to, operation, accuracy.geometric)?; - local_face += 1; - } - } - } - out.revision = a - .brep - .revision - .max(b.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let face_mappings = analytic_face_mappings(&out, [a.brep, b.brep]); - Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings, - }, - }) -} - -fn analytic_containment_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - b_inside_a: bool, - operation: BooleanOp, - id: String, -) -> Result { - let accuracy = Accuracy { - geometric: a.accuracy.geometric.max(b.accuracy.geometric), - intersection: a.accuracy.intersection.max(b.accuracy.intersection), - tessellation: a.accuracy.tessellation.max(b.accuracy.tessellation), - exchange: a.accuracy.exchange.max(b.accuracy.exchange), - }; - let mut out = BrepEnvelope::new(id, accuracy)?; - let (contained, enclosing) = if b_inside_a { (b, a) } else { (a, b) }; - match operation { - BooleanOp::Union => { - append_analytic_input(&mut out, enclosing)?; - } - BooleanOp::Intersection => { - append_analytic_input(&mut out, contained)?; - } - BooleanOp::Subtraction if b_inside_a => { - append_analytic_input(&mut out, a)?; - let face_offset = out.topology.faces.len() as u32; - append_analytic_input(&mut out, b)?; - let cavity = out - .solids - .pop() - .ok_or_else(|| GeometryError::InvalidTopology("missing cutter solid".into()))? - .outer_shell; - out.solids[0].cavity_shells.push(cavity); - for face in face_offset..out.topology.faces.len() as u32 { - reverse_face(&mut out, face); - out.topology.faces[face as usize].provenance.role = FaceRole::Cut; - out.topology.faces[face as usize].provenance.reversed = true; - } - } - BooleanOp::Subtraction => {} - } - out.revision = a - .revision - .max(b.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let face_mappings = analytic_face_mappings(&out, [a, b]); - Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings, - }, - }) -} - -fn interior_sample(brep: &BrepEnvelope) -> Result { - let bounds = brep.bounds()?.ok_or_else(|| { - GeometryError::InvalidTopology("cannot classify an empty solid boundary".into()) - })?; - let model_scale = bounds - .axes - .iter() - .map(|axis| axis.width()) - .fold(brep.accuracy.geometric, f64::max); - let offsets = [ - (model_scale * 1.0e-6).max(32.0 * brep.accuracy.geometric), - 16.0 * brep.accuracy.geometric, - 8.0 * brep.accuracy.geometric, - 4.0 * brep.accuracy.geometric, - ]; - let samples = [0.5, 0.25, 0.75, 0.125, 0.875]; - for face in &brep.topology.faces { - let surface = brep.geometry.surface(face.surface)?; - for u in samples { - for v in samples { - let uv = [ - face.trim.uv_bounds[0].lo + u * face.trim.uv_bounds[0].width(), - face.trim.uv_bounds[1].lo + v * face.trim.uv_bounds[1].width(), - ]; - if face_contains_uv(brep, face, uv)? != Some(true) { - continue; - } - let boundary = surface.point_at(uv)?; - let outward = scale(unit(surface.normal_at(uv)?)?, face.sense.multiplier()); - for offset in offsets { - let candidate = sub(boundary, scale(outward, offset)); - if classify_point(brep, candidate)? == PointClassification::Inside { - return Ok(candidate); - } - } - } - } - } - Err(GeometryError::UnresolvedIntersection( - "could not certify an interior classification sample".into(), - )) -} - -fn generic_non_intersecting_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result, GeometryError> { - let graph = match intersect_breps(a, b) { - Ok(graph) => graph, - Err(GeometryError::CoverageGap { .. }) => return Ok(None), - Err(error) => return Err(error), - }; - if graph - .pairs - .iter() - .any(|pair| pair.graph.coincident || !pair.graph.branches.is_empty()) - { - return Ok(None); - } - let mut contacts = graph - .pairs - .into_iter() - .flat_map(|pair| pair.graph.contacts) - .collect::>(); - let tolerance = a.accuracy.geometric.max(b.accuracy.geometric); - let mut unique = Vec::new(); - for point in contacts.drain(..) { - if unique - .iter() - .all(|existing| norm(sub(*existing, point)) > tolerance) - { - unique.push(point); - } - } - - let a_in_b = classify_point(b, interior_sample(a)?)?; - let b_in_a = classify_point(a, interior_sample(b)?)?; - if matches!( - a_in_b, - PointClassification::Unknown | PointClassification::Boundary - ) || matches!( - b_in_a, - PointClassification::Unknown | PointClassification::Boundary - ) { - return Err(GeometryError::UnresolvedIntersection( - "generic boolean containment classification is unresolved".into(), - )); - } - let a_inside_b = a_in_b == PointClassification::Inside; - let b_inside_a = b_in_a == PointClassification::Inside; - if a_inside_b && b_inside_a { - return Err(GeometryError::UnresolvedIntersection( - "mutual containment without a coincident boundary is unresolved".into(), - )); - } - if a_inside_b || b_inside_a { - return analytic_containment_boolean(a, b, b_inside_a, operation, id).map(Some); - } - separate_boolean(a, b, operation, id, unique).map(Some) -} - -fn remap_closed_loop_pcurve( - pcurve: &PcurveGeometry, - curve: u32, - surface: u32, -) -> Result { - Ok(match pcurve { - PcurveGeometry::Line2 { .. } | PcurveGeometry::Conic2 { .. } => pcurve.clone(), - PcurveGeometry::ProjectedCurve { - chart, - uv_hint, - uv_rate, - parameter_origin, - .. - } => PcurveGeometry::ProjectedCurve { - curve, - surface, - chart: *chart, - uv_hint: *uv_hint, - uv_rate: *uv_rate, - parameter_origin: *parameter_origin, - }, - PcurveGeometry::IntersectionSide { .. } => { - return Err(GeometryError::InvalidGeometry( - "intersection-side pcurve requires a remapped intersection definition".into(), - )) - } - }) -} - -fn exact_closed_circle_pcurve( - source_geometry: &GeometryStore, - target_geometry: &GeometryStore, - source_pcurve: u32, - circle: &CurveGeometry, - surface: u32, - tolerance: f64, -) -> Result, GeometryError> { - if !matches!( - source_geometry.pcurves.get(source_pcurve as usize), - Some(PcurveGeometry::ProjectedCurve { .. }) - ) { - return Ok(None); - } - let CurveGeometry::Circle { - frame: circle, - radius, - } = circle - else { - return Ok(None); - }; - let support = target_geometry.surface(surface)?; - let frame = support.frame(); - let centre = frame.local(circle.origin); - let axis_x = [dot(circle.x, frame.x), dot(circle.x, frame.y)]; - let axis_y = [dot(circle.y, frame.x), dot(circle.y, frame.y)]; - match support { - SurfaceGeometry::Plane { .. } - if centre[2].abs() <= tolerance - && dot(circle.x, frame.z).abs() * radius <= tolerance - && dot(circle.y, frame.z).abs() * radius <= tolerance => - { - Ok(Some(PcurveGeometry::Conic2 { - origin: [centre[0], centre[1]], - axis_a: axis_x.map(|value| value * radius), - axis_b: axis_y.map(|value| value * radius), - })) - } - SurfaceGeometry::Cylinder { - radius: support_radius, - .. - } if centre[0].hypot(centre[1]) <= tolerance - && (radius - support_radius).abs() <= tolerance - && (dot(circle.z, frame.z).abs() - 1.0).abs() <= 1.0e-10 => - { - let rate = dot(cross(circle.x, circle.y), frame.z).signum(); - let phase = axis_x[1].atan2(axis_x[0]); - let original = source_geometry.pcurve_at(source_pcurve, 0.0)?; - let lifted_phase = phase - + ((original[0] - phase) / std::f64::consts::TAU).round() * std::f64::consts::TAU; - Ok(Some(PcurveGeometry::Line2 { - origin: [lifted_phase, centre[2]], - direction: [rate, 0.0], - })) - } - _ => Ok(None), - } -} - -fn pcurve_winding( - geometry: &GeometryStore, - surface: u32, - pcurve: u32, - range: Interval, -) -> Result<[i32; 2], GeometryError> { - let chart = &geometry.surface(surface)?.charts()[0]; - let start = geometry.pcurve_at(pcurve, range.lo)?; - let end = geometry.pcurve_at(pcurve, range.hi)?; - Ok(std::array::from_fn(|axis| { - chart.periods[axis].map_or(0, |period| { - ((end[axis] - start[axis]) / period).round() as i32 - }) - })) -} - -fn pcurve_support_surface(geometry: &GeometryStore, pcurve: u32) -> Result { - match geometry.pcurves.get(pcurve as usize) { - Some(PcurveGeometry::ProjectedCurve { surface, .. }) => Ok(*surface), - Some(PcurveGeometry::IntersectionSide { definition, side }) => { - let definition = geometry - .intersections - .get(*definition as usize) - .ok_or_else(|| GeometryError::MissingReference { - kind: "intersection definition".into(), - index: *definition, - })?; - Ok(definition.surfaces[if *side == IntersectionSide::A { 0 } else { 1 }]) - } - Some(_) => Err(GeometryError::InvalidGeometry( - "periodic pcurve has no support surface reference".into(), - )), - None => Err(GeometryError::MissingReference { - kind: "pcurve".into(), - index: pcurve, - }), - } -} - -fn reframe_sphere_intersection( - geometry: &mut GeometryStore, - definition: &mut IntersectionDefinition, - side: usize, -) -> Result<(), GeometryError> { - let surface_id = definition.surfaces[side]; - let (frame, radius) = match geometry.surface(surface_id)? { - SurfaceGeometry::Sphere { frame, radius } => (*frame, *radius), - _ => { - return Err(GeometryError::CoverageGap { - families: ["periodic winding".into(), "non-spherical support".into()], - }) - } - }; - let references = [frame.x, frame.y, frame.z]; - let mut best: Option<(f64, SurfaceGeometry, Vec<[f64; 2]>)> = None; - for i in -1..=1 { - for j in -1..=1 { - for k in -1..=1 { - if [i, j, k] == [0, 0, 0] { - continue; - } - let axis = unit(frame.vector([i as f64, j as f64, k as f64]))?; - let reference = references - .into_iter() - .min_by(|left, right| { - dot(*left, axis).abs().total_cmp(&dot(*right, axis).abs()) - }) - .unwrap(); - let candidate = SurfaceGeometry::Sphere { - frame: Frame3::from_axis(frame.origin, axis, reference)?, - radius, - }; - let mut coordinates = Vec::with_capacity(definition.anchors.len()); - let mut hint = None; - let mut failed = false; - for anchor in &definition.anchors { - match candidate.project(anchor.point, hint) { - Ok(uv) => { - coordinates.push(uv); - hint = Some(uv); - } - Err(_) => { - failed = true; - break; - } - } - } - if failed { - continue; - } - let longitude_span = (coordinates.last().unwrap()[0] - coordinates[0][0]).abs(); - let pole_proximity = coordinates - .iter() - .map(|uv| uv[1].abs()) - .fold(0.0_f64, f64::max); - if longitude_span >= std::f64::consts::PI - || pole_proximity >= std::f64::consts::FRAC_PI_2 - 1.0e-6 - { - continue; - } - let score = pole_proximity + longitude_span; - if best - .as_ref() - .is_none_or(|(best_score, _, _)| score < *best_score) - { - best = Some((score, candidate, coordinates)); - } - } - } - } - let (_, candidate, coordinates) = best.ok_or_else(|| GeometryError::CoverageGap { - families: ["sphere".into(), "unresolved periodic winding".into()], - })?; - geometry.surfaces[surface_id as usize] = candidate; - for (anchor, uv) in definition.anchors.iter_mut().zip(&coordinates) { - if side == 0 { - anchor.uv_a = *uv; - } else { - anchor.uv_b = *uv; - } - } - let offset = side * 2; - for (index, tube) in definition.uv_tubes.iter_mut().enumerate() { - for axis in 0..2 { - let a = coordinates[index][axis]; - let b = coordinates[index + 1][axis]; - let padding = (a - b).abs().max(definition.residual_tolerance / radius) * 2.0; - tube[offset + axis] = Interval::new(a.min(b) - padding, a.max(b) + padding)?; - } - } - definition.validate(geometry) -} - -fn generic_single_closed_loop_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result, GeometryError> { - if a.solids.len() != 1 - || b.solids.len() != 1 - || a.topology.shells.len() != 1 - || b.topology.shells.len() != 1 - || !a.solids[0].cavity_shells.is_empty() - || !b.solids[0].cavity_shells.is_empty() - { - return Ok(None); - } - let body_graph = intersect_breps(a, b)?; - if body_graph.pairs.len() != 1 { - return Ok(None); - } - let pair = &body_graph.pairs[0]; - if pair.graph.coincident || !pair.graph.contacts.is_empty() || pair.graph.branches.len() != 1 { - return Ok(None); - } - let graph = &pair.graph; - let branch = &graph.branches[0]; - let accuracy = Accuracy { - geometric: a.accuracy.geometric.max(b.accuracy.geometric), - intersection: a.accuracy.intersection.max(b.accuracy.intersection), - tessellation: a.accuracy.tessellation.max(b.accuracy.tessellation), - exchange: a.accuracy.exchange.max(b.accuracy.exchange), - }; - if norm(sub(branch.endpoints[0], branch.endpoints[1])) > accuracy.intersection { - return Ok(None); - } - let windings = [ - pcurve_winding( - &graph.geometry, - pcurve_support_surface(&graph.geometry, branch.pcurves[0])?, - branch.pcurves[0], - branch.range, - )?, - pcurve_winding( - &graph.geometry, - pcurve_support_surface(&graph.geometry, branch.pcurves[1])?, - branch.pcurves[1], - branch.range, - )?, - ]; - for side in 0..2 { - if windings[side] != [0, 0] - && !matches!( - graph.geometry.surface(pcurve_support_surface( - &graph.geometry, - branch.pcurves[side], - )?)?, - SurfaceGeometry::Sphere { .. } - ) - { - return Ok(None); - } - } - let inputs = [a, b]; - let face_ids = pair.faces; - let loop_inside_other = [ - classify_point( - b, - graph.geometry.surfaces[0].point_at(intersection_loop_sample(graph, branch, 0)?)?, - )?, - classify_point( - a, - graph.geometry.surfaces[1].point_at(intersection_loop_sample(graph, branch, 1)?)?, - )?, - ]; - if loop_inside_other.iter().any(|classification| { - matches!( - classification, - PointClassification::Unknown | PointClassification::Boundary - ) - }) { - return Err(GeometryError::UnresolvedIntersection( - "closed SSI loop interior is unclassified".into(), - )); - } - let loop_inside_other = loop_inside_other.map(|value| value == PointClassification::Inside); - let (want_inside, reverse_selected) = match operation { - BooleanOp::Union => ([false, false], [false, false]), - BooleanOp::Intersection => ([true, true], [false, false]), - BooleanOp::Subtraction => ([false, true], [false, true]), - }; - let regions: [LoopRegion; 2] = std::array::from_fn(|side| { - if loop_inside_other[side] == want_inside[side] { - LoopRegion::Patch - } else { - LoopRegion::Complement - } - }); - - let mut out = BrepEnvelope::new(id, accuracy)?; - let mut selected_faces = [None; 2]; - let mut selected_surfaces = [None; 2]; - let mut selected_shells = Vec::new(); - for side in 0..2 { - if regions[side] != LoopRegion::Complement { - continue; - } - let face_offset = out.topology.faces.len() as u32; - let shell_offset = out.topology.shells.len() as u32; - append_analytic_input(&mut out, inputs[side])?; - let selected_face = face_offset + face_ids[side]; - selected_faces[side] = Some(selected_face); - selected_surfaces[side] = Some(out.topology.faces[selected_face as usize].surface); - selected_shells.push(shell_offset); - for face in face_offset..out.topology.faces.len() as u32 { - out.topology.faces[face as usize].provenance.role = if face == selected_face { - FaceRole::Split - } else { - FaceRole::Preserved - }; - } - } - if selected_shells.is_empty() { - out.topology.shells.push(Shell { - id: 0, - faces: Vec::new(), - is_closed: true, - }); - out.solids.push(SolidRegion { - outer_shell: 0, - cavity_shells: Vec::new(), - }); - } else if selected_shells.len() == 2 { - let faces = out.topology.shells[1].faces.clone(); - for face in &faces { - out.topology.faces[*face as usize].shell_ref = Some(0); - } - out.topology.shells[0].faces.extend(faces); - out.topology.shells.pop(); - out.solids.clear(); - out.solids.push(SolidRegion { - outer_shell: 0, - cavity_shells: Vec::new(), - }); - } - for side in 0..2 { - if regions[side] == LoopRegion::Patch { - selected_surfaces[side] = Some(out.geometry.surfaces.len() as u32); - out.geometry - .surfaces - .push(graph.geometry.surfaces[side].clone()); - } - } - - let source_curve = graph - .geometry - .curves - .get(branch.curve as usize) - .ok_or_else(|| GeometryError::MissingReference { - kind: "curve".into(), - index: branch.curve, - })? - .clone(); - let curve = out.geometry.curves.len() as u32; - let pcurves: [PcurveGeometry; 2] = match source_curve { - CurveGeometry::Intersection { definition } => { - let mut definition_geometry = graph - .geometry - .intersections - .get(definition as usize) - .ok_or_else(|| GeometryError::MissingReference { - kind: "intersection definition".into(), - index: definition, - })? - .clone(); - definition_geometry.surfaces = [ - selected_surfaces[definition_geometry.surfaces[0] as usize].ok_or_else(|| { - GeometryError::InvalidTopology( - "closed-loop first support surface was not retained".into(), - ) - })?, - selected_surfaces[definition_geometry.surfaces[1] as usize].ok_or_else(|| { - GeometryError::InvalidTopology( - "closed-loop second support surface was not retained".into(), - ) - })?, - ]; - for side in 0..2 { - if windings[side] == [0, 0] { - continue; - } - let definition_side = match graph.geometry.pcurves[branch.pcurves[side] as usize] { - PcurveGeometry::IntersectionSide { - side: IntersectionSide::A, - .. - } => 0, - PcurveGeometry::IntersectionSide { - side: IntersectionSide::B, - .. - } => 1, - _ => { - return Err(GeometryError::InvalidGeometry( - "winding intersection loop is missing its support pcurve".into(), - )) - } - }; - reframe_sphere_intersection( - &mut out.geometry, - &mut definition_geometry, - definition_side, - )?; - } - let definition = out.geometry.intersections.len() as u32; - out.geometry.intersections.push(definition_geometry); - out.geometry - .curves - .push(CurveGeometry::Intersection { definition }); - [0, 1] - .map( - |side| match graph.geometry.pcurves.get(branch.pcurves[side] as usize) { - Some(PcurveGeometry::IntersectionSide { - side: intersection_side, - .. - }) => Ok(PcurveGeometry::IntersectionSide { - definition, - side: *intersection_side, - }), - Some(_) => Err(GeometryError::InvalidGeometry( - "intersection curve requires intersection-side pcurves".into(), - )), - None => Err(GeometryError::MissingReference { - kind: "pcurve".into(), - index: branch.pcurves[side], - }), - }, - ) - .into_iter() - .collect::, _>>()? - .try_into() - .map_err(|_| { - GeometryError::InvalidGeometry("closed SSI loop requires two pcurves".into()) - })? - } - curve_geometry => { - out.geometry.curves.push(curve_geometry.clone()); - [0, 1] - .map(|side| { - if let Some(exact) = exact_closed_circle_pcurve( - &graph.geometry, - &out.geometry, - branch.pcurves[side], - &curve_geometry, - selected_surfaces[side].unwrap(), - out.accuracy.geometric, - )? { - return Ok(exact); - } - remap_closed_loop_pcurve( - &graph.geometry.pcurves[branch.pcurves[side] as usize], - curve, - selected_surfaces[side].unwrap(), - ) - }) - .into_iter() - .collect::, _>>()? - .try_into() - .map_err(|_| { - GeometryError::InvalidGeometry("closed SSI loop requires two pcurves".into()) - })? - } - }; - let vertex = out.topology.vertices.len() as u32; - out.topology.vertices.push(Vertex { - id: vertex, - position: branch.endpoints[0], - outgoing_halfedge: None, - tolerance: accuracy.geometric, - }); - let edge = out.topology.edges.len() as u32; - out.topology.edges.push(Edge { - id: edge, - geometry: EdgeGeometry::Curve { - curve, - range: branch.range, - }, - halfedge: u32::MAX, - twin_halfedge: None, - tolerance: accuracy.geometric, - chart_seam: false, - }); - for side in 0..2 { - let nominal_sense = if side == 0 { - Orientation::Forward - } else { - Orientation::Reverse - }; - let loop_sense = if reverse_selected[side] { - reverse(nominal_sense) - } else { - nominal_sense - }; - if regions[side] == LoopRegion::Complement { - let face = selected_faces[side].unwrap(); - let loop_id = add_closed_intersection_loop( - &mut out, - edge, - vertex, - face, - pcurves[side].clone(), - loop_sense, - true, - )?; - let loop_bounds = topology_loop_bounds(&out, loop_id, accuracy.intersection)?; - for axis in 0..2 { - let bounds = out.topology.faces[face as usize].trim.uv_bounds[axis]; - out.topology.faces[face as usize].trim.uv_bounds[axis] = Interval::new( - bounds.lo.min(loop_bounds[axis].lo), - bounds.hi.max(loop_bounds[axis].hi), - )?; - } - out.topology.faces[face as usize].trim.holes.push(loop_id); - } else { - let face = out.topology.faces.len() as u32; - let loop_id = add_closed_intersection_loop( - &mut out, - edge, - vertex, - face, - pcurves[side].clone(), - loop_sense, - false, - )?; - let uv_bounds = topology_loop_bounds(&out, loop_id, accuracy.intersection)?; - let source_face = &inputs[side].topology.faces[face_ids[side] as usize]; - out.topology.faces.push(Face { - id: face, - key: format!("{}:intersection-patch", source_face.key), - surface: selected_surfaces[side].unwrap(), - sense: source_face.sense, - trim: TrimRegion { - chart: source_face.trim.chart, - uv_bounds, - outer: loop_id, - holes: Vec::new(), - }, - shell_ref: Some(0), - provenance: FaceProvenance { - sources: vec![brep_face_source(inputs[side], face_ids[side])], - role: if reverse_selected[side] { - FaceRole::Cut - } else { - FaceRole::Split - }, - reversed: reverse_selected[side], - }, - }); - out.topology.shells[0].faces.push(face); - selected_faces[side] = Some(face); - } - } - for side in 0..2 { - if reverse_selected[side] { - reverse_face(&mut out, selected_faces[side].unwrap()); - } - } - out.revision = a - .revision - .max(b.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - Ok(Some(BooleanResult { - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings: analytic_face_mappings(&out, [a, b]), - }, - brep: out, - })) -} - -fn generic_multiple_closed_loops_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result, GeometryError> { - if a.solids.len() != 1 - || b.solids.len() != 1 - || a.topology.shells.len() != 1 - || b.topology.shells.len() != 1 - || !a.solids[0].cavity_shells.is_empty() - || !b.solids[0].cavity_shells.is_empty() - { - return Ok(None); - } - let body_graph = intersect_breps(a, b)?; - if body_graph.pairs.len() != 1 { - return Ok(None); - } - let pair = &body_graph.pairs[0]; - if pair.graph.coincident || !pair.graph.contacts.is_empty() || pair.graph.branches.len() < 2 { - return Ok(None); - } - let graph = &pair.graph; - let accuracy = Accuracy { - geometric: a.accuracy.geometric.max(b.accuracy.geometric), - intersection: a.accuracy.intersection.max(b.accuracy.intersection), - tessellation: a.accuracy.tessellation.max(b.accuracy.tessellation), - exchange: a.accuracy.exchange.max(b.accuracy.exchange), - }; - for branch in &graph.branches { - if norm(sub(branch.endpoints[0], branch.endpoints[1])) > accuracy.intersection { - return Ok(None); - } - for side in 0..2 { - if pcurve_winding( - &graph.geometry, - pcurve_support_surface(&graph.geometry, branch.pcurves[side])?, - branch.pcurves[side], - branch.range, - )? != [0, 0] - { - return Ok(None); - } - } - } - - let inputs = [a, b]; - let face_ids = pair.faces; - let (want_inside, reverse_selected) = match operation { - BooleanOp::Union => ([false, false], [false, false]), - BooleanOp::Intersection => ([true, true], [false, false]), - BooleanOp::Subtraction => ([false, true], [false, true]), - }; - let mut loop_regions = [Vec::new(), Vec::new()]; - for branch in &graph.branches { - for side in 0..2 { - let uv = intersection_loop_sample(graph, branch, side)?; - let point = graph.geometry.surfaces[side].point_at(uv)?; - let inside = match classify_point(inputs[1 - side], point)? { - PointClassification::Inside => true, - PointClassification::Outside => false, - PointClassification::Unknown | PointClassification::Boundary => { - return Err(GeometryError::UnresolvedIntersection( - "closed SSI loop interior is unclassified".into(), - )) - } - }; - loop_regions[side].push(if inside == want_inside[side] { - LoopRegion::Patch - } else { - LoopRegion::Complement - }); - } - } - if [0, 1].into_iter().any(|side| { - loop_regions[side] - .iter() - .any(|region| *region != loop_regions[side][0]) - }) { - return Ok(None); - } - let selected_regions = [loop_regions[0][0], loop_regions[1][0]]; - for side in 0..2 { - if selected_regions[side] == LoopRegion::Patch && inputs[side].topology.faces.len() != 1 { - return Ok(None); - } - } - - let mut out = BrepEnvelope::new(id, accuracy)?; - let mut selected_faces = [Vec::new(), Vec::new()]; - let mut selected_surfaces = [None, None]; - let mut selected_shells = Vec::new(); - for side in 0..2 { - if selected_regions[side] == LoopRegion::Complement { - let face_offset = out.topology.faces.len() as u32; - let shell_offset = out.topology.shells.len() as u32; - append_analytic_input(&mut out, inputs[side])?; - let face = face_offset + face_ids[side]; - selected_faces[side].push(face); - selected_surfaces[side] = Some(out.topology.faces[face as usize].surface); - selected_shells.push(shell_offset); - for candidate in face_offset..out.topology.faces.len() as u32 { - out.topology.faces[candidate as usize].provenance.role = if candidate == face { - FaceRole::Split - } else { - FaceRole::Preserved - }; - } - } else { - selected_surfaces[side] = Some(out.geometry.surfaces.len() as u32); - out.geometry - .surfaces - .push(graph.geometry.surfaces[side].clone()); - } - } - if selected_shells.is_empty() { - out.topology.shells.push(Shell { - id: 0, - faces: Vec::new(), - is_closed: true, - }); - out.solids.push(SolidRegion { - outer_shell: 0, - cavity_shells: Vec::new(), - }); - } else if selected_shells.len() == 2 { - let faces = out.topology.shells[1].faces.clone(); - for face in &faces { - out.topology.faces[*face as usize].shell_ref = Some(0); - } - out.topology.shells[0].faces.extend(faces); - out.topology.shells.pop(); - out.solids.clear(); - out.solids.push(SolidRegion { - outer_shell: 0, - cavity_shells: Vec::new(), - }); - } - - let selected_surfaces = [selected_surfaces[0].unwrap(), selected_surfaces[1].unwrap()]; - for (branch_index, branch) in graph.branches.iter().enumerate() { - let (edge, vertex, pcurves) = - append_closed_branch_geometry(&mut out, graph, branch, selected_surfaces)?; - for side in 0..2 { - let nominal_sense = if side == 0 { - Orientation::Forward - } else { - Orientation::Reverse - }; - let loop_sense = if reverse_selected[side] { - reverse(nominal_sense) - } else { - nominal_sense - }; - if selected_regions[side] == LoopRegion::Complement { - let face = selected_faces[side][0]; - let loop_id = add_closed_intersection_loop( - &mut out, - edge, - vertex, - face, - pcurves[side].clone(), - loop_sense, - true, - )?; - let loop_bounds = topology_loop_bounds(&out, loop_id, accuracy.intersection)?; - for axis in 0..2 { - let bounds = out.topology.faces[face as usize].trim.uv_bounds[axis]; - out.topology.faces[face as usize].trim.uv_bounds[axis] = Interval::new( - bounds.lo.min(loop_bounds[axis].lo), - bounds.hi.max(loop_bounds[axis].hi), - )?; - } - out.topology.faces[face as usize].trim.holes.push(loop_id); - } else { - let face = out.topology.faces.len() as u32; - let loop_id = add_closed_intersection_loop( - &mut out, - edge, - vertex, - face, - pcurves[side].clone(), - loop_sense, - false, - )?; - let uv_bounds = topology_loop_bounds(&out, loop_id, accuracy.intersection)?; - let source_face = &inputs[side].topology.faces[face_ids[side] as usize]; - out.topology.faces.push(Face { - id: face, - key: format!("{}:intersection-patch-{branch_index}", source_face.key), - surface: selected_surfaces[side], - sense: source_face.sense, - trim: TrimRegion { - chart: source_face.trim.chart, - uv_bounds, - outer: loop_id, - holes: Vec::new(), - }, - shell_ref: Some(0), - provenance: FaceProvenance { - sources: vec![brep_face_source(inputs[side], face_ids[side])], - role: if reverse_selected[side] { - FaceRole::Cut - } else { - FaceRole::Split - }, - reversed: reverse_selected[side], - }, - }); - out.topology.shells[0].faces.push(face); - selected_faces[side].push(face); - } - } - } - for side in 0..2 { - if reverse_selected[side] { - for face in selected_faces[side].clone() { - reverse_face(&mut out, face); - } - } - } - out.revision = a - .revision - .max(b.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - Ok(Some(BooleanResult { - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings: analytic_face_mappings(&out, [a, b]), - }, - brep: out, - })) -} - -fn generic_two_sided_cutter_band_subtraction( - host: &BrepEnvelope, - cutter: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result, GeometryError> { - if operation != BooleanOp::Subtraction - || host.solids.len() != 1 - || cutter.solids.len() != 1 - || host.topology.shells.len() != 1 - || cutter.topology.shells.len() != 1 - || !host.solids[0].cavity_shells.is_empty() - || !cutter.solids[0].cavity_shells.is_empty() - { - return Ok(None); - } - let body_graph = intersect_breps(host, cutter)?; - if body_graph.pairs.len() != 2 - || body_graph.pairs[0].faces[1] != body_graph.pairs[1].faces[1] - || body_graph.pairs[0].faces[0] == body_graph.pairs[1].faces[0] - { - return Ok(None); - } - let mut closed_branches = Vec::with_capacity(2); - for pair in &body_graph.pairs { - if pair.graph.coincident - || !pair.graph.contacts.is_empty() - || pair.graph.branches.len() != 1 - { - return Ok(None); - } - let branch = &pair.graph.branches[0]; - let closure = norm(sub(branch.endpoints[0], branch.endpoints[1])); - let intersection = host.accuracy.intersection.max(cutter.accuracy.intersection); - let geometric = host.accuracy.geometric.max(cutter.accuracy.geometric); - let closed = if closure <= intersection { - super::face_intersection::IntersectionBranch { - curve: branch.curve, - pcurves: branch.pcurves, - range: branch.range, - endpoints: branch.endpoints, - } - } else if let Some(CurveGeometry::Circle { frame, radius }) = - pair.graph.geometry.curves.get(branch.curve as usize) - { - let tau = std::f64::consts::TAU; - if closure > 4.0 * geometric - || branch.range.lo.abs() * radius > 4.0 * geometric - || (branch.range.hi - tau).abs() * radius > 4.0 * geometric - { - return Ok(None); - } - let point = add(frame.origin, scale(frame.x, *radius)); - super::face_intersection::IntersectionBranch { - curve: branch.curve, - pcurves: branch.pcurves, - range: Interval::new(0.0, tau)?, - endpoints: [point, point], - } - } else { - return Ok(None); - }; - if pcurve_winding( - &pair.graph.geometry, - pcurve_support_surface(&pair.graph.geometry, closed.pcurves[0])?, - closed.pcurves[0], - closed.range, - )? != [0, 0] - { - return Ok(None); - } - closed_branches.push(closed); - } - - let accuracy = Accuracy { - geometric: host.accuracy.geometric.max(cutter.accuracy.geometric), - intersection: host.accuracy.intersection.max(cutter.accuracy.intersection), - tessellation: host.accuracy.tessellation.max(cutter.accuracy.tessellation), - exchange: host.accuracy.exchange.max(cutter.accuracy.exchange), - }; - let mut out = BrepEnvelope::new(id, accuracy)?; - append_analytic_input(&mut out, host)?; - let cutter_surface = out.geometry.surfaces.len() as u32; - out.geometry - .surfaces - .push(body_graph.pairs[0].graph.geometry.surfaces[1].clone()); - let cut_face = out.topology.faces.len() as u32; - let mut cut_loops = Vec::with_capacity(2); - let mut cut_bounds: Option<[Interval; 2]> = None; - for (pair, branch) in body_graph.pairs.iter().zip(&closed_branches) { - let host_face = pair.faces[0]; - let host_surface = out.topology.faces[host_face as usize].surface; - let (edge, vertex, pcurves) = append_closed_branch_geometry( - &mut out, - &pair.graph, - branch, - [host_surface, cutter_surface], - )?; - let host_loop = add_closed_intersection_loop( - &mut out, - edge, - vertex, - host_face, - pcurves[0].clone(), - Orientation::Forward, - true, - )?; - let host_bounds = topology_loop_bounds(&out, host_loop, accuracy.intersection)?; - for axis in 0..2 { - let bounds = out.topology.faces[host_face as usize].trim.uv_bounds[axis]; - out.topology.faces[host_face as usize].trim.uv_bounds[axis] = Interval::new( - bounds.lo.min(host_bounds[axis].lo), - bounds.hi.max(host_bounds[axis].hi), - )?; - } - out.topology.faces[host_face as usize] - .trim - .holes - .push(host_loop); - out.topology.faces[host_face as usize].provenance.role = FaceRole::Split; - - let cutter_loop = add_closed_intersection_loop( - &mut out, - edge, - vertex, - cut_face, - pcurves[1].clone(), - Orientation::Forward, - !cut_loops.is_empty(), - )?; - let bounds = topology_loop_bounds(&out, cutter_loop, accuracy.intersection)?; - cut_bounds = Some(match cut_bounds { - Some(previous) => [0, 1] - .map(|axis| { - Interval::new( - previous[axis].lo.min(bounds[axis].lo), - previous[axis].hi.max(bounds[axis].hi), - ) - }) - .into_iter() - .collect::, _>>()? - .try_into() - .map_err(|_| GeometryError::InvalidGeometry("cutter band UV bounds".into()))?, - None => bounds, - }); - cut_loops.push(cutter_loop); - } - let source_face = &cutter.topology.faces[body_graph.pairs[0].faces[1] as usize]; - out.topology.faces.push(Face { - id: cut_face, - key: format!("{}:through-cut", source_face.key), - surface: cutter_surface, - sense: source_face.sense, - trim: TrimRegion { - chart: source_face.trim.chart, - uv_bounds: cut_bounds.ok_or_else(|| { - GeometryError::InvalidTopology("cutter band has no trim bounds".into()) - })?, - outer: cut_loops[0], - holes: cut_loops[1..].to_vec(), - }, - shell_ref: Some(0), - provenance: FaceProvenance { - sources: vec![brep_face_source(cutter, source_face.id)], - role: FaceRole::Cut, - reversed: true, - }, - }); - out.topology.shells[0].faces.push(cut_face); - reverse_face(&mut out, cut_face); - out.revision = host - .revision - .max(cutter.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - Ok(Some(BooleanResult { - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings: analytic_face_mappings(&out, [host, cutter]), - }, - brep: out, - })) -} - -fn append_closed_branch_geometry( - out: &mut BrepEnvelope, - graph: &super::face_intersection::IntersectionGraph, - branch: &super::face_intersection::IntersectionBranch, - selected_surfaces: [u32; 2], -) -> Result<(u32, u32, [PcurveGeometry; 2]), GeometryError> { - let source_curve = graph - .geometry - .curves - .get(branch.curve as usize) - .ok_or_else(|| GeometryError::MissingReference { - kind: "curve".into(), - index: branch.curve, - })? - .clone(); - let curve = out.geometry.curves.len() as u32; - let pcurves = match source_curve { - CurveGeometry::Intersection { definition } => { - let mut definition_geometry = graph - .geometry - .intersections - .get(definition as usize) - .ok_or_else(|| GeometryError::MissingReference { - kind: "intersection definition".into(), - index: definition, - })? - .clone(); - definition_geometry.surfaces = definition_geometry - .surfaces - .map(|surface| selected_surfaces[surface as usize]); - let definition = out.geometry.intersections.len() as u32; - out.geometry.intersections.push(definition_geometry); - out.geometry - .curves - .push(CurveGeometry::Intersection { definition }); - [0, 1] - .map( - |side| match graph.geometry.pcurves.get(branch.pcurves[side] as usize) { - Some(PcurveGeometry::IntersectionSide { - side: intersection_side, - .. - }) => Ok(PcurveGeometry::IntersectionSide { - definition, - side: *intersection_side, - }), - Some(_) => Err(GeometryError::InvalidGeometry( - "intersection curve requires intersection-side pcurves".into(), - )), - None => Err(GeometryError::MissingReference { - kind: "pcurve".into(), - index: branch.pcurves[side], - }), - }, - ) - .into_iter() - .collect::, _>>()? - .try_into() - .map_err(|_| { - GeometryError::InvalidGeometry("closed SSI loop requires two pcurves".into()) - })? - } - curve_geometry => { - out.geometry.curves.push(curve_geometry.clone()); - [0, 1] - .map(|side| { - if let Some(exact) = exact_closed_circle_pcurve( - &graph.geometry, - &out.geometry, - branch.pcurves[side], - &curve_geometry, - selected_surfaces[side], - out.accuracy.geometric, - )? { - return Ok(exact); - } - remap_closed_loop_pcurve( - &graph.geometry.pcurves[branch.pcurves[side] as usize], - curve, - selected_surfaces[side], - ) - }) - .into_iter() - .collect::, _>>()? - .try_into() - .map_err(|_| { - GeometryError::InvalidGeometry("closed SSI loop requires two pcurves".into()) - })? - } - }; - let vertex = out.topology.vertices.len() as u32; - out.topology.vertices.push(Vertex { - id: vertex, - position: branch.endpoints[0], - outgoing_halfedge: None, - tolerance: out.accuracy.geometric, - }); - let edge = out.topology.edges.len() as u32; - out.topology.edges.push(Edge { - id: edge, - geometry: EdgeGeometry::Curve { - curve, - range: branch.range, - }, - halfedge: u32::MAX, - twin_halfedge: None, - tolerance: out.accuracy.geometric, - chart_seam: false, - }); - Ok((edge, vertex, pcurves)) -} - -fn coincident_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let accuracy = Accuracy { - geometric: a.accuracy.geometric.max(b.accuracy.geometric), - intersection: a.accuracy.intersection.max(b.accuracy.intersection), - tessellation: a.accuracy.tessellation.max(b.accuracy.tessellation), - exchange: a.accuracy.exchange.max(b.accuracy.exchange), - }; - let mut out = BrepEnvelope::new(id, accuracy)?; - if operation != BooleanOp::Subtraction { - append_analytic_input(&mut out, a)?; - for face in &mut out.topology.faces { - let counterpart = b.topology.faces.get(face.id as usize).ok_or_else(|| { - GeometryError::InvalidTopology( - "coincident operands have incompatible face ownership".into(), - ) - })?; - face.provenance.sources.push(FaceSource { - entity: b.id.clone(), - body: b.id.clone(), - key: counterpart.key.clone(), - face: counterpart.id, - }); - face.provenance.role = FaceRole::Coincident; - } - } - out.revision = a - .revision - .max(b.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - Ok(BooleanResult { - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: true, - face_mappings: analytic_face_mappings(&out, [a, b]), - }, - brep: out, - }) -} - -fn torus_containment_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let a = full_torus(a)?; - let b = full_torus(b)?; - let accuracy = a.brep.accuracy.geometric.max(b.brep.accuracy.geometric); - let axes_match = norm(cross(a.frame.z, b.frame.z)) <= 1e-12 && dot(a.frame.z, b.frame.z) > 0.0; - let displacement = a.frame.local(b.frame.origin); - let radial_offset = displacement[0].hypot(displacement[1]); - if !axes_match || radial_offset > accuracy { - return Err(GeometryError::CoverageGap { - families: ["ring torus".into(), "noncoaxial ring torus".into()], - }); - } - let centerline_distance = (a.major_radius - b.major_radius).hypot(displacement[2]); - let a_contains_b = centerline_distance + b.minor_radius < a.minor_radius - accuracy; - let b_contains_a = centerline_distance + a.minor_radius < b.minor_radius - accuracy; - let same = - centerline_distance <= accuracy && (a.minor_radius - b.minor_radius).abs() <= accuracy; - if same { - return coincident_boolean(a.brep, b.brep, operation, id); - } - if a_contains_b || b_contains_a { - return analytic_containment_boolean(a.brep, b.brep, a_contains_b, operation, id); - } - if (centerline_distance + b.minor_radius - a.minor_radius).abs() <= accuracy - || (centerline_distance + a.minor_radius - b.minor_radius).abs() <= accuracy - { - return Err(GeometryError::UnresolvedIntersection( - "torus containment boundary is below geometric resolution".into(), - )); - } - Err(GeometryError::CoverageGap { - families: ["ring torus".into(), "intersecting ring torus".into()], - }) -} - -fn conic_radius_at(input: &ConicSectionInput<'_>, axial: f64) -> f64 { - axial * input.semi_angle.tan() -} - -fn conic_containment_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let a = full_conic_section(a)?; - let b = full_conic_section(b)?; - let accuracy = a.brep.accuracy.geometric.max(b.brep.accuracy.geometric); - if norm(cross(a.frame.z, b.frame.z)) > 1.0e-12 || dot(a.frame.z, b.frame.z) <= 0.0 { - return Err(GeometryError::CoverageGap { - families: [ - "cone or frustum".into(), - "noncoaxial cone or frustum".into(), - ], - }); - } - let displacement = sub(b.frame.origin, a.frame.origin); - let axial_offset = dot(displacement, a.frame.z); - let radial_offset = norm(sub(displacement, scale(a.frame.z, axial_offset))); - if radial_offset > accuracy { - return Err(GeometryError::CoverageGap { - families: [ - "cone or frustum".into(), - "noncoaxial cone or frustum".into(), - ], - }); - } - - let b_in_a_range = Interval::new( - axial_offset + b.axial_range.lo, - axial_offset + b.axial_range.hi, - )?; - let a_in_b_range = Interval::new( - a.axial_range.lo - axial_offset, - a.axial_range.hi - axial_offset, - )?; - let containment_margins = - |inner: &ConicSectionInput<'_>, outer: &ConicSectionInput<'_>, inner_in_outer: Interval| { - [ - inner_in_outer.lo - outer.axial_range.lo, - outer.axial_range.hi - inner_in_outer.hi, - conic_radius_at(outer, inner_in_outer.lo) - inner.lower_radius, - conic_radius_at(outer, inner_in_outer.hi) - inner.upper_radius, - ] - }; - let b_in_a = containment_margins(&b, &a, b_in_a_range); - let a_in_b = containment_margins(&a, &b, a_in_b_range); - let strictly_inside = |margins: [f64; 4]| margins.into_iter().all(|margin| margin > accuracy); - let near_inside = |margins: [f64; 4]| { - margins.into_iter().all(|margin| margin >= -accuracy) - && margins.into_iter().any(|margin| margin.abs() <= accuracy) - }; - let b_inside_a = strictly_inside(b_in_a); - let a_inside_b = strictly_inside(a_in_b); - if b_inside_a || a_inside_b { - return analytic_containment_boolean(a.brep, b.brep, b_inside_a, operation, id); - } - - let same = a.brep.topology.faces.len() == b.brep.topology.faces.len() - && (b_in_a_range.lo - a.axial_range.lo).abs() <= accuracy - && (b_in_a_range.hi - a.axial_range.hi).abs() <= accuracy - && (b.lower_radius - a.lower_radius).abs() <= accuracy - && (b.upper_radius - a.upper_radius).abs() <= accuracy; - if same { - return coincident_boolean(a.brep, b.brep, operation, id); - } - if near_inside(b_in_a) || near_inside(a_in_b) { - return Err(GeometryError::UnresolvedIntersection( - "cone/frustum containment boundary is below geometric resolution".into(), - )); - } - Err(GeometryError::CoverageGap { - families: [ - "cone or frustum".into(), - "intersecting cone or frustum".into(), - ], - }) -} - -fn sphere_conic_containment( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let (sphere, conic, sphere_is_a) = if matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Sphere { .. }) - ) { - (full_sphere(a)?, full_conic_section(b)?, true) - } else { - (full_sphere(b)?, full_conic_section(a)?, false) - }; - let clearance = 4.0 - * sphere - .brep - .accuracy - .geometric - .max(conic.brep.accuracy.geometric); - let center = conic.frame.local(sphere.frame.origin); - let radial = center[0].hypot(center[1]); - let (sin_angle, cos_angle) = conic.semi_angle.sin_cos(); - let sphere_margin = (center[2] - conic.axial_range.lo - sphere.radius) - .min(conic.axial_range.hi - center[2] - sphere.radius) - .min(center[2] * sin_angle - radial * cos_angle - sphere.radius); - let conic_extent = [ - (radial + conic.lower_radius).hypot(conic.axial_range.lo - center[2]), - (radial + conic.upper_radius).hypot(conic.axial_range.hi - center[2]), - ] - .into_iter() - .fold(0.0_f64, f64::max); - let conic_margin = sphere.radius - conic_extent; - let sphere_inside_conic = sphere_margin > clearance; - let conic_inside_sphere = conic_margin > clearance; - if sphere_inside_conic || conic_inside_sphere { - let b_inside_a = if sphere_is_a { - conic_inside_sphere - } else { - sphere_inside_conic - }; - return analytic_containment_boolean(a, b, b_inside_a, operation, id); - } - if sphere_margin.abs() <= clearance || conic_margin.abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "sphere/cone containment boundary is below geometric resolution".into(), - )); - } - Err(GeometryError::CoverageGap { - families: ["sphere".into(), "intersecting cone or frustum".into()], - }) -} - -fn cylinder_conic_containment( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let (cylinder, conic, cylinder_is_a) = if matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Cylinder { .. }) - ) { - (full_cylinder(a)?, full_conic_section(b)?, true) - } else { - (full_cylinder(b)?, full_conic_section(a)?, false) - }; - let clearance = 4.0 - * cylinder - .brep - .accuracy - .geometric - .max(conic.brep.accuracy.geometric); - let alignment = dot(cylinder.frame.z, conic.frame.z); - if norm(cross(cylinder.frame.z, conic.frame.z)) > 1.0e-12 { - return Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "noncoaxial cone or frustum".into()], - }); - } - let displacement = sub(conic.frame.origin, cylinder.frame.origin); - let cylinder_axis_offset = dot(displacement, cylinder.frame.z); - let radial_offset = norm(sub( - displacement, - scale(cylinder.frame.z, cylinder_axis_offset), - )); - if radial_offset > clearance { - return Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "noncoaxial cone or frustum".into()], - }); - } - - let conic_ends_in_cylinder = [ - cylinder_axis_offset + alignment * conic.axial_range.lo, - cylinder_axis_offset + alignment * conic.axial_range.hi, - ]; - let conic_axial_lo = conic_ends_in_cylinder[0].min(conic_ends_in_cylinder[1]); - let conic_axial_hi = conic_ends_in_cylinder[0].max(conic_ends_in_cylinder[1]); - let conic_margin = conic_axial_lo - .min(cylinder.height - conic_axial_hi) - .min(cylinder.radius - conic.lower_radius.max(conic.upper_radius)); - - let cylinder_origin_in_conic = dot( - sub(cylinder.frame.origin, conic.frame.origin), - conic.frame.z, - ); - let cylinder_ends_in_conic = [ - cylinder_origin_in_conic, - cylinder_origin_in_conic + alignment * cylinder.height, - ]; - let cylinder_axial_lo = cylinder_ends_in_conic[0].min(cylinder_ends_in_conic[1]); - let cylinder_axial_hi = cylinder_ends_in_conic[0].max(cylinder_ends_in_conic[1]); - let cylinder_margin = (cylinder_axial_lo - conic.axial_range.lo) - .min(conic.axial_range.hi - cylinder_axial_hi) - .min(conic_radius_at(&conic, cylinder_ends_in_conic[0]) - cylinder.radius) - .min(conic_radius_at(&conic, cylinder_ends_in_conic[1]) - cylinder.radius); - - let conic_inside_cylinder = conic_margin > clearance; - let cylinder_inside_conic = cylinder_margin > clearance; - if conic_inside_cylinder || cylinder_inside_conic { - let b_inside_a = if cylinder_is_a { - conic_inside_cylinder - } else { - cylinder_inside_conic - }; - return analytic_containment_boolean(a, b, b_inside_a, operation, id); - } - if conic_margin.abs() <= clearance || cylinder_margin.abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "cylinder/cone containment boundary is below geometric resolution".into(), - )); - } - Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "intersecting cone or frustum".into()], - }) -} - -fn conic_box_containment( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let (conic, box_, conic_is_a) = if matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Cone { .. }) - ) { - ( - full_conic_section(a)?, - super::box_booleans::full_box(b)?, - true, - ) - } else { - ( - full_conic_section(b)?, - super::box_booleans::full_box(a)?, - false, - ) - }; - let clearance = 4.0 - * conic - .brep - .accuracy - .geometric - .max(box_.brep.accuracy.geometric); - let box_axes = [box_.frame.x, box_.frame.y, box_.frame.z]; - let endpoint_centers = [ - conic.frame.point([0.0, 0.0, conic.axial_range.lo]), - conic.frame.point([0.0, 0.0, conic.axial_range.hi]), - ]; - let endpoint_radii = [conic.lower_radius, conic.upper_radius]; - let mut conic_margin = f64::INFINITY; - for axis in 0..3 { - let radial_extent_rate = (1.0 - dot(conic.frame.z, box_axes[axis]).powi(2)) - .max(0.0) - .sqrt(); - let extents = [0, 1].map(|endpoint| { - let center = dot( - sub(endpoint_centers[endpoint], box_.frame.origin), - box_axes[axis], - ); - let radial = endpoint_radii[endpoint] * radial_extent_rate; - [center - radial, center + radial] - }); - let lo = extents[0][0].min(extents[1][0]); - let hi = extents[0][1].max(extents[1][1]); - conic_margin = conic_margin.min(lo).min(box_.size[axis] - hi); - } - - let mut box_margin = f64::INFINITY; - for corner in 0..8 { - let point = box_.frame.point(std::array::from_fn(|axis| { - if corner & (1 << axis) == 0 { - 0.0 - } else { - box_.size[axis] - } - })); - let local = conic.frame.local(point); - box_margin = box_margin - .min(local[2] - conic.axial_range.lo) - .min(conic.axial_range.hi - local[2]) - .min(conic_radius_at(&conic, local[2]) - local[0].hypot(local[1])); - } - let conic_inside_box = conic_margin > clearance; - let box_inside_conic = box_margin > clearance; - if conic_inside_box || box_inside_conic { - let b_inside_a = if conic_is_a { - box_inside_conic - } else { - conic_inside_box - }; - return analytic_containment_boolean(a, b, b_inside_a, operation, id); - } - if conic_margin.abs() <= clearance || box_margin.abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "cone/cuboid containment boundary is below geometric resolution".into(), - )); - } - Err(GeometryError::CoverageGap { - families: ["cone or frustum".into(), "cuboid".into()], - }) -} - -fn torus_cylinder_containment( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let (torus, cylinder, torus_is_a) = if matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Torus { .. }) - ) { - (full_torus(a)?, full_cylinder(b)?, true) - } else { - (full_torus(b)?, full_cylinder(a)?, false) - }; - let clearance = 4.0 - * torus - .brep - .accuracy - .geometric - .max(cylinder.brep.accuracy.geometric); - if norm(cross(torus.frame.z, cylinder.frame.z)) > 1.0e-12 { - return Err(GeometryError::CoverageGap { - families: ["ring torus".into(), "noncoaxial cylinder".into()], - }); - } - let center = cylinder.frame.local(torus.frame.origin); - let radial = center[0].hypot(center[1]); - let torus_margin = (cylinder.radius - radial - torus.major_radius - torus.minor_radius) - .min(center[2] - torus.minor_radius) - .min(cylinder.height - center[2] - torus.minor_radius); - if torus_margin > clearance { - let b_inside_a = !torus_is_a; - return analytic_containment_boolean(a, b, b_inside_a, operation, id); - } - if torus_margin.abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "torus/cylinder containment boundary is below geometric resolution".into(), - )); - } - Err(GeometryError::CoverageGap { - families: ["ring torus".into(), "intersecting cylinder".into()], - }) -} - -fn torus_box_containment( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let (torus, box_, torus_is_a) = if matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Torus { .. }) - ) { - (full_torus(a)?, super::box_booleans::full_box(b)?, true) - } else { - (full_torus(b)?, super::box_booleans::full_box(a)?, false) - }; - let clearance = 4.0 - * torus - .brep - .accuracy - .geometric - .max(box_.brep.accuracy.geometric); - let box_axes = [box_.frame.x, box_.frame.y, box_.frame.z]; - let center = box_.frame.local(torus.frame.origin); - let torus_margin = (0..3).fold(f64::INFINITY, |margin, axis| { - let axial_rate = dot(torus.frame.z, box_axes[axis]); - let radial_rate = (1.0 - axial_rate * axial_rate).max(0.0).sqrt(); - let extent = torus.major_radius * radial_rate + torus.minor_radius; - margin - .min(center[axis] - extent) - .min(box_.size[axis] - center[axis] - extent) - }); - if torus_margin > clearance { - let b_inside_a = !torus_is_a; - return analytic_containment_boolean(a, b, b_inside_a, operation, id); - } - if torus_margin.abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "torus/cuboid containment boundary is below geometric resolution".into(), - )); - } - Err(GeometryError::CoverageGap { - families: ["ring torus".into(), "cuboid".into()], - }) -} - -fn torus_conic_containment( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let (torus, conic, torus_is_a) = if matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Torus { .. }) - ) { - (full_torus(a)?, full_conic_section(b)?, true) - } else { - (full_torus(b)?, full_conic_section(a)?, false) - }; - let clearance = 4.0 - * torus - .brep - .accuracy - .geometric - .max(conic.brep.accuracy.geometric); - if norm(cross(torus.frame.z, conic.frame.z)) > 1.0e-12 { - return Err(GeometryError::CoverageGap { - families: ["ring torus".into(), "noncoaxial cone or frustum".into()], - }); - } - let center = conic.frame.local(torus.frame.origin); - let radial = center[0].hypot(center[1]); - let (sin_angle, cos_angle) = conic.semi_angle.sin_cos(); - let torus_margin = (center[2] - conic.axial_range.lo - torus.minor_radius) - .min(conic.axial_range.hi - center[2] - torus.minor_radius) - .min( - center[2] * sin_angle - (radial + torus.major_radius) * cos_angle - torus.minor_radius, - ); - if torus_margin > clearance { - let b_inside_a = !torus_is_a; - return analytic_containment_boolean(a, b, b_inside_a, operation, id); - } - if torus_margin.abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "torus/cone containment boundary is below geometric resolution".into(), - )); - } - Err(GeometryError::CoverageGap { - families: ["ring torus".into(), "intersecting cone or frustum".into()], - }) -} - -fn sphere_torus_containment( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let (sphere, torus, sphere_is_a) = if matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Sphere { .. }) - ) { - (full_sphere(a)?, full_torus(b)?, true) - } else { - (full_sphere(b)?, full_torus(a)?, false) - }; - let accuracy = sphere - .brep - .accuracy - .geometric - .max(torus.brep.accuracy.geometric); - let center = torus.frame.local(sphere.frame.origin); - let radial = center[0].hypot(center[1]); - let centerline_minimum = (radial - torus.major_radius).hypot(center[2]); - let centerline_maximum = (radial + torus.major_radius).hypot(center[2]); - let sphere_inside_torus = centerline_minimum + sphere.radius < torus.minor_radius - accuracy; - let torus_inside_sphere = centerline_maximum + torus.minor_radius < sphere.radius - accuracy; - if sphere_inside_torus || torus_inside_sphere { - let b_inside_a = if sphere_is_a { - torus_inside_sphere - } else { - sphere_inside_torus - }; - return analytic_containment_boolean(a, b, b_inside_a, operation, id); - } - if (centerline_minimum + sphere.radius - torus.minor_radius).abs() <= accuracy - || (centerline_maximum + torus.minor_radius - sphere.radius).abs() <= accuracy - { - return Err(GeometryError::UnresolvedIntersection( - "sphere/torus containment boundary is below geometric resolution".into(), - )); - } - Err(GeometryError::CoverageGap { - families: ["sphere".into(), "intersecting ring torus".into()], - }) -} - -fn sphere_cylinder_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let (sphere, cylinder, sphere_is_a) = if matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Sphere { .. }) - ) { - (full_sphere(a)?, full_cylinder(b)?, true) - } else { - (full_sphere(b)?, full_cylinder(a)?, false) - }; - let clearance = 4.0 - * sphere - .brep - .accuracy - .geometric - .max(cylinder.brep.accuracy.geometric); - let local = cylinder.frame.local(sphere.frame.origin); - let radial = local[0].hypot(local[1]); - let sphere_margin = (cylinder.radius - radial - sphere.radius) - .min(local[2] - sphere.radius) - .min(cylinder.height - local[2] - sphere.radius); - let axial_extent = local[2].abs().max((cylinder.height - local[2]).abs()); - let cylinder_extent = (radial + cylinder.radius).hypot(axial_extent); - let cylinder_margin = sphere.radius - cylinder_extent; - let sphere_inside_cylinder = sphere_margin > clearance; - let cylinder_inside_sphere = cylinder_margin > clearance; - if sphere_inside_cylinder || cylinder_inside_sphere { - let b_inside_a = if sphere_is_a { - cylinder_inside_sphere - } else { - sphere_inside_cylinder - }; - return analytic_containment_boolean(a, b, b_inside_a, operation, id); - } - if sphere_margin.abs() <= clearance || cylinder_margin.abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "sphere/cylinder containment boundary is below geometric resolution".into(), - )); - } - offset_sphere_cylinder_boolean(&sphere, &cylinder, sphere_is_a, operation, id, clearance) -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum LoopRegion { - Patch, - Complement, -} - -fn intersection_loop_sample( - graph: &super::face_intersection::IntersectionGraph, - branch: &super::face_intersection::IntersectionBranch, - side: usize, -) -> Result<[f64; 2], GeometryError> { - let subdivisions = 128; - let mut points = Vec::with_capacity(subdivisions); - for index in 0..subdivisions { - let parameter = branch.range.lo + branch.range.width() * index as f64 / subdivisions as f64; - points.push(graph.geometry.pcurve_at(branch.pcurves[side], parameter)?); - } - let mut twice_area = 0.0; - let mut centroid = [0.0; 2]; - for index in 0..points.len() { - let a = points[index]; - let b = points[(index + 1) % points.len()]; - let cross = a[0] * b[1] - b[0] * a[1]; - twice_area += cross; - centroid[0] += (a[0] + b[0]) * cross; - centroid[1] += (a[1] + b[1]) * cross; - } - if twice_area.abs() <= f64::EPSILON { - return Ok([ - points.iter().map(|point| point[0]).sum::() / points.len() as f64, - points.iter().map(|point| point[1]).sum::() / points.len() as f64, - ]); - } - Ok([ - centroid[0] / (3.0 * twice_area), - centroid[1] / (3.0 * twice_area), - ]) -} - -fn intersection_loop_bounds( - graph: &super::face_intersection::IntersectionGraph, - branch: &super::face_intersection::IntersectionBranch, - side: usize, - tolerance: f64, -) -> Result<[Interval; 2], GeometryError> { - pcurve_loop_bounds( - &graph.geometry, - branch.pcurves[side], - branch.range, - tolerance, - ) -} - -fn topology_loop_bounds( - brep: &BrepEnvelope, - loop_id: u32, - tolerance: f64, -) -> Result<[Interval; 2], GeometryError> { - let loop_record = brep.topology.loops.get(loop_id as usize).ok_or_else(|| { - GeometryError::MissingReference { - kind: "loop".into(), - index: loop_id, - } - })?; - let halfedge = &brep.topology.halfedges[loop_record.start_halfedge as usize]; - let pcurve = halfedge.geometry_use.pcurve.ok_or_else(|| { - GeometryError::InvalidTopology("intersection loop is missing its pcurve".into()) - })?; - pcurve_loop_bounds( - &brep.geometry, - pcurve, - brep.topology.edges[halfedge.edge as usize].geometry.range(), - tolerance, - ) -} - -fn pcurve_loop_bounds( - geometry: &GeometryStore, - pcurve: u32, - range: Interval, - tolerance: f64, -) -> Result<[Interval; 2], GeometryError> { - let mut lo = [f64::INFINITY; 2]; - let mut hi = [f64::NEG_INFINITY; 2]; - for index in 0..=256 { - let parameter = range.lo + range.width() * index as f64 / 256.0; - let uv = geometry.pcurve_at(pcurve, parameter)?; - for axis in 0..2 { - lo[axis] = lo[axis].min(uv[axis]); - hi[axis] = hi[axis].max(uv[axis]); - } - } - let padding = - [0, 1].map(|axis| (hi[axis] - lo[axis]).abs().max(tolerance) * 1.0e-6 + tolerance); - Ok([ - Interval::new(lo[0] - padding[0], hi[0] + padding[0])?, - Interval::new(lo[1] - padding[1], hi[1] + padding[1])?, - ]) -} - -fn add_closed_intersection_loop( - brep: &mut BrepEnvelope, - edge: u32, - vertex: u32, - face: u32, - pcurve_geometry: PcurveGeometry, - sense: Orientation, - is_hole: bool, -) -> Result { - let loop_id = brep.topology.loops.len() as u32; - let halfedge = brep.topology.halfedges.len() as u32; - let pcurve = brep.geometry.pcurves.len() as u32; - brep.geometry.pcurves.push(pcurve_geometry); - let edge_record = brep.topology.edges.get_mut(edge as usize).ok_or_else(|| { - GeometryError::InvalidTopology("intersection loop edge is missing".into()) - })?; - let twin = if edge_record.halfedge == u32::MAX { - edge_record.halfedge = halfedge; - None - } else { - if edge_record.twin_halfedge.is_some() { - return Err(GeometryError::InvalidTopology( - "intersection loop edge is nonmanifold".into(), - )); - } - edge_record.twin_halfedge = Some(halfedge); - brep.topology.halfedges[edge_record.halfedge as usize].twin = Some(halfedge); - Some(edge_record.halfedge) - }; - brep.topology.halfedges.push(HalfEdge { - id: halfedge, - from: vertex, - to: vertex, - twin, - next: Some(halfedge), - prev: Some(halfedge), - edge, - face: Some(face), - loop_ref: Some(loop_id), - wire_ref: None, - geometry_use: HalfEdgeGeometryUse { - sense, - pcurve: Some(pcurve), - periodic_lift: [0; 2], - }, - }); - if brep.topology.vertices[vertex as usize] - .outgoing_halfedge - .is_none() - { - brep.topology.vertices[vertex as usize].outgoing_halfedge = Some(halfedge); - } - brep.topology.loops.push(Loop { - id: loop_id, - start_halfedge: halfedge, - face_ref: face, - is_hole, - }); - Ok(loop_id) -} - -fn offset_sphere_cylinder_boolean( - sphere: &SphereInput<'_>, - cylinder: &CylinderInput<'_>, - sphere_is_a: bool, - operation: BooleanOp, - id: String, - clearance: f64, -) -> Result { - let local = cylinder.frame.local(sphere.frame.origin); - let radial = local[0].hypot(local[1]); - let scale = sphere - .radius - .max(cylinder.radius) - .max(cylinder.height) - .max(1.0); - let roundoff = 128.0 * f64::EPSILON * scale; - if radial <= roundoff { - return coaxial_sphere_cylinder_boolean( - sphere, - cylinder, - sphere_is_a, - operation, - id, - clearance, - ); - } - let sphere_low = local[2] - sphere.radius; - let sphere_high = local[2] + sphere.radius; - if sphere_low <= clearance || cylinder.height - sphere_high <= clearance { - return Err(GeometryError::CoverageGap { - families: ["sphere".into(), "finite cylinder cap intersection".into()], - }); - } - let graph = intersect_faces( - FaceView { - brep: sphere.brep, - face: 0, - }, - FaceView { - brep: cylinder.brep, - face: 0, - }, - )?; - if graph.coincident || !graph.contacts.is_empty() || graph.branches.len() != 1 { - return Err(GeometryError::CoverageGap { - families: ["sphere".into(), "multi-branch or tangent cylinder".into()], - }); - } - let branch = &graph.branches[0]; - if norm(sub(branch.endpoints[0], branch.endpoints[1])) > clearance { - return Err(GeometryError::CoverageGap { - families: ["sphere".into(), "open cylinder intersection branch".into()], - }); - } - let curve_definition = match graph.geometry.curves[branch.curve as usize] { - CurveGeometry::Intersection { definition } => definition, - _ => { - return Err(GeometryError::CoverageGap { - families: ["sphere".into(), "non-numerical cylinder branch".into()], - }) - } - }; - let loop_inside_other = [ - classify_point( - cylinder.brep, - graph.geometry.surfaces[0].point_at(intersection_loop_sample(&graph, branch, 0)?)?, - )?, - classify_point( - sphere.brep, - graph.geometry.surfaces[1].point_at(intersection_loop_sample(&graph, branch, 1)?)?, - )?, - ]; - if loop_inside_other.iter().any(|classification| { - matches!( - classification, - PointClassification::Unknown | PointClassification::Boundary - ) - }) { - return Err(GeometryError::UnresolvedIntersection( - "sphere/cylinder intersection loop interior is unclassified".into(), - )); - } - let loop_inside_other = loop_inside_other.map(|value| value == PointClassification::Inside); - let (want_inside, reverse_selected) = match operation { - BooleanOp::Union => ([false, false], [false, false]), - BooleanOp::Intersection => ([true, true], [false, false]), - BooleanOp::Subtraction if sphere_is_a => ([false, true], [false, true]), - BooleanOp::Subtraction => ([true, false], [true, false]), - }; - let regions: [LoopRegion; 2] = std::array::from_fn(|side| { - if loop_inside_other[side] == want_inside[side] { - LoopRegion::Patch - } else { - LoopRegion::Complement - } - }); - let inputs = [sphere.brep, cylinder.brep]; - let mut out = BrepEnvelope::new( - id, - Accuracy { - geometric: sphere - .brep - .accuracy - .geometric - .max(cylinder.brep.accuracy.geometric), - intersection: sphere - .brep - .accuracy - .intersection - .max(cylinder.brep.accuracy.intersection), - tessellation: sphere - .brep - .accuracy - .tessellation - .max(cylinder.brep.accuracy.tessellation), - exchange: sphere - .brep - .accuracy - .exchange - .max(cylinder.brep.accuracy.exchange), - }, - )?; - let mut selected_faces = [None; 2]; - let mut selected_surfaces = [None; 2]; - let mut selected_shells = Vec::new(); - for side in 0..2 { - if regions[side] != LoopRegion::Complement { - continue; - } - let face_offset = out.topology.faces.len() as u32; - let shell_offset = out.topology.shells.len() as u32; - append_analytic_input(&mut out, inputs[side])?; - selected_faces[side] = Some(face_offset); - selected_surfaces[side] = Some(out.topology.faces[face_offset as usize].surface); - selected_shells.push(shell_offset); - let next_face = out.topology.faces.len() as u32; - for face in face_offset..next_face { - out.topology.faces[face as usize].provenance.role = if face == face_offset { - FaceRole::Split - } else { - FaceRole::Preserved - }; - } - } - if selected_shells.is_empty() { - out.topology.shells.push(Shell { - id: 0, - faces: Vec::new(), - is_closed: true, - }); - out.solids.push(SolidRegion { - outer_shell: 0, - cavity_shells: Vec::new(), - }); - } else if selected_shells.len() == 2 { - let faces = out.topology.shells[1].faces.clone(); - for face in &faces { - out.topology.faces[*face as usize].shell_ref = Some(0); - } - out.topology.shells[0].faces.extend(faces); - out.topology.shells.pop(); - out.solids.clear(); - out.solids.push(SolidRegion { - outer_shell: 0, - cavity_shells: Vec::new(), - }); - } - for side in 0..2 { - if regions[side] == LoopRegion::Patch { - selected_surfaces[side] = Some(out.geometry.surfaces.len() as u32); - out.geometry - .surfaces - .push(graph.geometry.surfaces[side].clone()); - } - } - let mut definition_geometry = graph.geometry.intersections[curve_definition as usize].clone(); - definition_geometry.surfaces = [ - selected_surfaces[definition_geometry.surfaces[0] as usize].ok_or_else(|| { - GeometryError::InvalidTopology( - "sphere-cylinder first support surface was not retained".into(), - ) - })?, - selected_surfaces[definition_geometry.surfaces[1] as usize].ok_or_else(|| { - GeometryError::InvalidTopology( - "sphere-cylinder second support surface was not retained".into(), - ) - })?, - ]; - let definition = out.geometry.intersections.len() as u32; - out.geometry.intersections.push(definition_geometry); - let curve = out.geometry.curves.len() as u32; - out.geometry - .curves - .push(CurveGeometry::Intersection { definition }); - let vertex = out.topology.vertices.len() as u32; - out.topology.vertices.push(Vertex { - id: vertex, - position: branch.endpoints[0], - outgoing_halfedge: None, - tolerance: out.accuracy.geometric, - }); - let edge = out.topology.edges.len() as u32; - out.topology.edges.push(Edge { - id: edge, - geometry: EdgeGeometry::Curve { - curve, - range: branch.range, - }, - halfedge: u32::MAX, - twin_halfedge: None, - tolerance: out.accuracy.geometric, - chart_seam: false, - }); - for side in 0..2 { - let pcurve = match graph.geometry.pcurves[branch.pcurves[side] as usize] { - PcurveGeometry::IntersectionSide { - side: intersection_side, - .. - } => PcurveGeometry::IntersectionSide { - definition, - side: intersection_side, - }, - _ => { - return Err(GeometryError::InvalidGeometry( - "numerical sphere-cylinder curve is missing an intersection-side pcurve".into(), - )) - } - }; - let nominal_sense = if side == 0 { - Orientation::Forward - } else { - Orientation::Reverse - }; - let loop_sense = if reverse_selected[side] { - reverse(nominal_sense) - } else { - nominal_sense - }; - if regions[side] == LoopRegion::Complement { - let face = selected_faces[side].unwrap(); - let loop_bounds = - intersection_loop_bounds(&graph, branch, side, out.accuracy.intersection)?; - for axis in 0..2 { - let bounds = out.topology.faces[face as usize].trim.uv_bounds[axis]; - out.topology.faces[face as usize].trim.uv_bounds[axis] = Interval::new( - bounds.lo.min(loop_bounds[axis].lo), - bounds.hi.max(loop_bounds[axis].hi), - )?; - } - let loop_id = add_closed_intersection_loop( - &mut out, edge, vertex, face, pcurve, loop_sense, true, - )?; - out.topology.faces[face as usize].trim.holes.push(loop_id); - } else { - let face = out.topology.faces.len() as u32; - let loop_id = add_closed_intersection_loop( - &mut out, edge, vertex, face, pcurve, loop_sense, false, - )?; - out.topology.faces.push(Face { - id: face, - key: format!("{}:intersection-patch", inputs[side].topology.faces[0].key), - surface: selected_surfaces[side].unwrap(), - sense: Orientation::Forward, - trim: TrimRegion { - chart: 0, - uv_bounds: intersection_loop_bounds( - &graph, - branch, - side, - out.accuracy.intersection, - )?, - outer: loop_id, - holes: Vec::new(), - }, - shell_ref: Some(0), - provenance: FaceProvenance { - sources: vec![brep_face_source(inputs[side], 0)], - role: if reverse_selected[side] { - FaceRole::Cut - } else { - FaceRole::Split - }, - reversed: reverse_selected[side], - }, - }); - out.topology.shells[0].faces.push(face); - selected_faces[side] = Some(face); - } - } - for side in 0..2 { - if reverse_selected[side] { - reverse_face(&mut out, selected_faces[side].unwrap()); - } - } - out.revision = sphere - .brep - .revision - .max(cylinder.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let mappings = if sphere_is_a { - analytic_face_mappings(&out, [sphere.brep, cylinder.brep]) - } else { - analytic_face_mappings(&out, [cylinder.brep, sphere.brep]) - }; - Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings: mappings, - }, - }) -} - -fn sphere_box_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let (sphere, box_, sphere_is_a) = if matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Sphere { .. }) - ) { - (full_sphere(a)?, super::box_booleans::full_box(b)?, true) - } else { - (full_sphere(b)?, super::box_booleans::full_box(a)?, false) - }; - let clearance = 4.0 - * sphere - .brep - .accuracy - .geometric - .max(box_.brep.accuracy.geometric); - let center = box_.frame.local(sphere.frame.origin); - let sphere_margin = (0..3).fold(f64::INFINITY, |margin, axis| { - margin - .min(center[axis] - sphere.radius) - .min(box_.size[axis] - center[axis] - sphere.radius) - }); - let mut farthest: f64 = 0.0; - for corner in 0..8 { - let point = box_.frame.point(std::array::from_fn(|axis| { - if corner & (1 << axis) == 0 { - 0.0 - } else { - box_.size[axis] - } - })); - farthest = farthest.max(norm(sub(point, sphere.frame.origin))); - } - let box_margin = sphere.radius - farthest; - let sphere_inside_box = sphere_margin > clearance; - let box_inside_sphere = box_margin > clearance; - if sphere_inside_box || box_inside_sphere { - let b_inside_a = if sphere_is_a { - box_inside_sphere - } else { - sphere_inside_box - }; - return analytic_containment_boolean(a, b, b_inside_a, operation, id); - } - if sphere_margin.abs() <= clearance || box_margin.abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "sphere/cuboid containment boundary is below geometric resolution".into(), - )); - } - single_face_sphere_box_boolean(&sphere, &box_, sphere_is_a, operation, id, clearance) -} - -fn box_source(input: &super::box_booleans::BoxInput<'_>, face: usize) -> FaceSource { - FaceSource { - entity: input.brep.id.clone(), - body: input.brep.id.clone(), - key: input.brep.topology.faces[face].key.clone(), - face: face as u32, - } -} - -fn single_face_sphere_box_boolean( - sphere: &SphereInput<'_>, - box_: &super::box_booleans::BoxInput<'_>, - sphere_is_a: bool, - operation: BooleanOp, - id: String, - clearance: f64, -) -> Result { - let accuracy = Accuracy { - geometric: sphere - .brep - .accuracy - .geometric - .max(box_.brep.accuracy.geometric), - intersection: sphere - .brep - .accuracy - .intersection - .max(box_.brep.accuracy.intersection), - tessellation: sphere - .brep - .accuracy - .tessellation - .max(box_.brep.accuracy.tessellation), - exchange: sphere - .brep - .accuracy - .exchange - .max(box_.brep.accuracy.exchange), - }; - let center = box_.frame.local(sphere.frame.origin); - let box_axes = [box_.frame.x, box_.frame.y, box_.frame.z]; - let mut crossings = Vec::new(); - for axis in 0..3 { - for upper in [false, true] { - let signed_plane = if upper { - center[axis] - box_.size[axis] - } else { - -center[axis] - }; - let separation = signed_plane.abs() - sphere.radius; - if separation.abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "sphere is tangent to a cuboid face within geometric resolution".into(), - )); - } - if separation < 0.0 { - crossings.push((axis, upper, signed_plane)); - } else if signed_plane > sphere.radius { - return Err(GeometryError::CoverageGap { - families: ["sphere".into(), "cuboid".into()], - }); - } - } - } - let [(axis, upper, signed_plane)] = crossings.as_slice() else { - return Err(GeometryError::CoverageGap { - families: ["sphere".into(), "multi-face cuboid intersection".into()], - }); - }; - let axis = *axis; - let upper = *upper; - let signed_plane = *signed_plane; - let inward = if upper { - scale(box_axes[axis], -1.0) - } else { - box_axes[axis] - }; - let tangent_axis = match axis { - 0 => box_axes[1], - _ => box_axes[0], - }; - let cap_frame = Frame3::from_axis(sphere.frame.origin, inward, tangent_axis)?; - let latitude = (signed_plane / sphere.radius).asin(); - let circle_radius = - ((sphere.radius - signed_plane.abs()) * (sphere.radius + signed_plane.abs())).sqrt(); - if circle_radius <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "sphere/cuboid intersection circle is below geometric resolution".into(), - )); - } - let circle_frame = Frame3 { - origin: cap_frame.point([0.0, 0.0, signed_plane]), - ..cap_frame - }; - let entry_face = match axis { - 0 => 2 + usize::from(upper), - 1 => 4 + usize::from(upper), - _ => usize::from(upper), - }; - let mut builder = Builder::new(id, accuracy)?; - let circle_vertex = builder.vertex(circle_frame.point([circle_radius, 0.0, 0.0])); - let circle_edge = builder.edge( - CurveGeometry::Circle { - frame: circle_frame, - radius: circle_radius, - }, - Interval::new(0.0, std::f64::consts::TAU)?, - false, - ); - - if operation == BooleanOp::Intersection || (operation == BooleanOp::Subtraction && sphere_is_a) - { - let keep_inside_box = operation == BooleanOp::Intersection; - let disk_frame = if keep_inside_box { - Frame3 { - x: circle_frame.x, - y: scale(circle_frame.y, -1.0), - z: scale(circle_frame.z, -1.0), - ..circle_frame - } - } else { - circle_frame - }; - builder.face( - "cuboid:cap", - SurfaceGeometry::Plane { frame: disk_frame }, - [[-circle_radius, circle_radius]; 2], - vec![boundary( - circle_edge, - circle_vertex, - circle_vertex, - if keep_inside_box { - Orientation::Reverse - } else { - Orientation::Forward - }, - PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [circle_radius, 0.0], - axis_b: [ - 0.0, - if keep_inside_box { - -circle_radius - } else { - circle_radius - }, - ], - }, - )], - )?; - builder.brep.topology.faces[0].provenance = face_provenance( - vec![box_source(box_, entry_face)], - if keep_inside_box { - FaceRole::Split - } else { - FaceRole::Cut - }, - !keep_inside_box, - ); - patch( - &mut builder, - sphere, - cap_frame, - latitude, - keep_inside_box, - circle_edge, - circle_vertex, - false, - )?; - } else { - let points: Vec<_> = (0..8) - .map(|index| { - box_.frame.point(std::array::from_fn(|coordinate| { - if index & (1 << coordinate) == 0 { - 0.0 - } else { - box_.size[coordinate] - } - })) - }) - .collect(); - let vertices = points - .iter() - .map(|point| builder.vertex(*point)) - .collect::>(); - let mut edges = [[u32::MAX; 3]; 8]; - for index in 0..8 { - for coordinate in 0..3 { - if index & (1 << coordinate) == 0 { - edges[index][coordinate] = builder.edge( - CurveGeometry::Line { - origin: points[index], - direction: box_axes[coordinate], - }, - Interval::new(0.0, box_.size[coordinate])?, - false, - ); - } - } - } - for (face_index, (key, indices, normal)) in [ - ("bottom", [0, 2, 3, 1], scale(box_.frame.z, -1.0)), - ("top", [4, 5, 7, 6], box_.frame.z), - ("left", [0, 4, 6, 2], scale(box_.frame.x, -1.0)), - ("right", [1, 3, 7, 5], box_.frame.x), - ("front", [0, 1, 5, 4], scale(box_.frame.y, -1.0)), - ("back", [2, 6, 7, 3], box_.frame.y), - ] - .into_iter() - .enumerate() - { - let face_frame = Frame3::from_axis( - points[indices[0]], - normal, - sub(points[indices[1]], points[indices[0]]), - )?; - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - let mut uses = Vec::with_capacity(4); - for index in 0..4 { - let from = indices[index]; - let to = indices[(index + 1) % 4]; - let coordinate = (from ^ to).trailing_zeros() as usize; - uses.push(plane_boundary( - &builder, - face_frame, - edges[from.min(to)][coordinate], - vertices[from], - vertices[to], - if from < to { - Orientation::Forward - } else { - Orientation::Reverse - }, - )?); - let uv = face_frame.local(points[from]); - for coordinate in 0..2 { - bounds[coordinate][0] = bounds[coordinate][0].min(uv[coordinate]); - bounds[coordinate][1] = bounds[coordinate][1].max(uv[coordinate]); - } - } - for bound in &mut bounds { - bound[0] -= accuracy.geometric; - bound[1] += accuracy.geometric; - } - let holes = if face_index == entry_face { - vec![vec![plane_boundary( - &builder, - face_frame, - circle_edge, - circle_vertex, - circle_vertex, - Orientation::Forward, - )?]] - } else { - Vec::new() - }; - builder.face_with_holes( - key, - SurfaceGeometry::Plane { frame: face_frame }, - bounds, - uses, - holes, - )?; - builder.brep.topology.faces[face_index].provenance = face_provenance( - vec![box_source(box_, face_index)], - if face_index == entry_face { - FaceRole::Split - } else { - FaceRole::Preserved - }, - false, - ); - } - patch( - &mut builder, - sphere, - cap_frame, - latitude, - operation == BooleanOp::Subtraction, - circle_edge, - circle_vertex, - operation == BooleanOp::Subtraction, - )?; - } - - let mut out = builder.finish()?; - out.revision = sphere - .brep - .revision - .max(box_.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let inputs = if sphere_is_a { - [sphere.brep, box_.brep] - } else { - [box_.brep, sphere.brep] - }; - Ok(BooleanResult { - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings: analytic_face_mappings(&out, inputs), - }, - brep: out, - }) -} - -fn perpendicular_cylinder_box_boolean( - box_: &super::box_booleans::BoxInput<'_>, - cylinder: &CylinderInput<'_>, - cylinder_is_a: bool, - operation: BooleanOp, - id: String, - accuracy: Accuracy, -) -> Result { - let box_axes = [box_.frame.x, box_.frame.y, box_.frame.z]; - let (axis, alignment) = box_axes - .iter() - .enumerate() - .map(|(axis, direction)| (axis, dot(cylinder.frame.z, *direction))) - .max_by(|left, right| left.1.abs().total_cmp(&right.1.abs())) - .ok_or_else(|| GeometryError::InvalidGeometry("cuboid has no frame axes".into()))?; - if 1.0 - alignment.abs() > 1.0e-12 { - return Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "oblique cuboid intersection".into()], - }); - } - let local_origin = box_.frame.local(cylinder.frame.origin); - let other_axes = match axis { - 0 => [1, 2], - 1 => [0, 2], - _ => [0, 1], - }; - let radial_margins = other_axes.map(|candidate| { - (local_origin[candidate] - cylinder.radius) - .min(box_.size[candidate] - local_origin[candidate] - cylinder.radius) - }); - if radial_margins - .iter() - .any(|margin| margin.abs() <= 4.0 * accuracy.geometric) - { - return Err(GeometryError::UnresolvedIntersection( - "cylindrical opening is tangent to a cuboid side within geometric resolution".into(), - )); - } - if radial_margins.iter().any(|margin| *margin < 0.0) { - return Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "side-intersecting cuboid".into()], - }); - } - let axial_end = local_origin[axis] + alignment * cylinder.height; - let axial_span = [ - local_origin[axis].min(axial_end), - local_origin[axis].max(axial_end), - ]; - let low_clearance = -axial_span[0]; - let high_clearance = axial_span[1] - box_.size[axis]; - if low_clearance.abs() <= 4.0 * accuracy.geometric - || high_clearance.abs() <= 4.0 * accuracy.geometric - { - return Err(GeometryError::UnresolvedIntersection( - "cylinder cap meets a cuboid boundary within geometric resolution".into(), - )); - } - let crosses_low = low_clearance > 4.0 * accuracy.geometric; - let crosses_high = high_clearance > 4.0 * accuracy.geometric; - let clipped_low = axial_span[0].max(0.0); - let clipped_high = axial_span[1].min(box_.size[axis]); - if clipped_high - clipped_low <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "cylinder/cuboid overlap is below geometric resolution".into(), - )); - } - if !crosses_low && !crosses_high { - return Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "contained cuboid intersection".into()], - }); - } - - let mut center = local_origin; - center[axis] = clipped_low; - let low_center = box_.frame.point(center); - let tunnel_frame = Frame3::from_axis(low_center, box_axes[axis], box_axes[other_axes[0]])?; - let tunnel_height = clipped_high - clipped_low; - let low_face = match axis { - 0 => 2, - 1 => 4, - _ => 0, - }; - let high_face = low_face + 1; - let cylinder_low_face = if alignment > 0.0 { 1 } else { 2 }; - let cylinder_high_face = if alignment > 0.0 { 2 } else { 1 }; - if operation == BooleanOp::Intersection { - let mut out = - primitives::cylinder(id, tunnel_frame, cylinder.radius, tunnel_height, accuracy)?; - out.topology.faces[0].provenance = - face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Split, false); - out.topology.faces[1].provenance = face_provenance( - vec![if crosses_low { - box_source(box_, low_face) - } else { - cylinder_source(cylinder, cylinder_low_face) - }], - FaceRole::Split, - false, - ); - out.topology.faces[2].provenance = face_provenance( - vec![if crosses_high { - box_source(box_, high_face) - } else { - cylinder_source(cylinder, cylinder_high_face) - }], - FaceRole::Split, - false, - ); - out.revision = box_ - .brep - .revision - .max(cylinder.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - return Ok(BooleanResult { - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings: analytic_face_mappings(&out, [box_.brep, cylinder.brep]), - }, - brep: out, - }); - } - if operation == BooleanOp::Subtraction && cylinder_is_a { - let mut out = BrepEnvelope::new(id, accuracy)?; - if crosses_low { - let mut frame = tunnel_frame; - frame.origin = box_.frame.point({ - let mut point = local_origin; - point[axis] = axial_span[0]; - point - }); - append_cylinder_segment( - &mut out, - frame, - cylinder.radius, - -axial_span[0], - [ - face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Split, false), - face_provenance( - vec![cylinder_source(cylinder, cylinder_low_face)], - FaceRole::Preserved, - false, - ), - face_provenance(vec![box_source(box_, low_face)], FaceRole::Cut, true), - ], - 0, - )?; - } - if crosses_high { - let mut frame = tunnel_frame; - frame.origin = box_.frame.point({ - let mut point = local_origin; - point[axis] = box_.size[axis]; - point - }); - append_cylinder_segment( - &mut out, - frame, - cylinder.radius, - axial_span[1] - box_.size[axis], - [ - face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Split, false), - face_provenance(vec![box_source(box_, high_face)], FaceRole::Cut, true), - face_provenance( - vec![cylinder_source(cylinder, cylinder_high_face)], - FaceRole::Preserved, - false, - ), - ], - usize::from(crosses_low), - )?; - } - out.revision = box_ - .brep - .revision - .max(cylinder.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let inputs = if cylinder_is_a { - [cylinder.brep, box_.brep] - } else { - [box_.brep, cylinder.brep] - }; - return Ok(BooleanResult { - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings: analytic_face_mappings(&out, inputs), - }, - brep: out, - }); - } - let mut builder = Builder::new(id, accuracy)?; - let points: Vec<_> = (0..8) - .map(|index| { - box_.frame.point(std::array::from_fn(|coordinate| { - if index & (1 << coordinate) == 0 { - 0.0 - } else { - box_.size[coordinate] - } - })) - }) - .collect(); - let vertices = points - .iter() - .map(|point| builder.vertex(*point)) - .collect::>(); - let mut edges = [[u32::MAX; 3]; 8]; - for index in 0..8 { - for coordinate in 0..3 { - if index & (1 << coordinate) == 0 { - edges[index][coordinate] = builder.edge( - CurveGeometry::Line { - origin: points[index], - direction: box_axes[coordinate], - }, - Interval::new(0.0, box_.size[coordinate])?, - false, - ); - } - } - } - let tau = std::f64::consts::TAU; - let high_frame = Frame3 { - origin: tunnel_frame.point([0.0, 0.0, tunnel_height]), - ..tunnel_frame - }; - let low_boundary = if operation == BooleanOp::Subtraction || crosses_low { - Some(circular_boundary( - &mut builder, - tunnel_frame, - cylinder.radius, - 0.0, - )?) - } else { - None - }; - let high_boundary = if operation == BooleanOp::Subtraction || crosses_high { - Some(circular_boundary( - &mut builder, - tunnel_frame, - cylinder.radius, - tunnel_height, - )?) - } else { - None - }; - let seam = if operation == BooleanOp::Subtraction { - Some(builder.edge( - CurveGeometry::Line { - origin: tunnel_frame.point([cylinder.radius, 0.0, 0.0]), - direction: tunnel_frame.z, - }, - Interval::new(0.0, tunnel_height)?, - true, - )) - } else { - None - }; - for (face_index, (key, indices, normal)) in [ - ("bottom", [0, 2, 3, 1], scale(box_.frame.z, -1.0)), - ("top", [4, 5, 7, 6], box_.frame.z), - ("left", [0, 4, 6, 2], scale(box_.frame.x, -1.0)), - ("right", [1, 3, 7, 5], box_.frame.x), - ("front", [0, 1, 5, 4], scale(box_.frame.y, -1.0)), - ("back", [2, 6, 7, 3], box_.frame.y), - ] - .into_iter() - .enumerate() - { - let face_frame = Frame3::from_axis( - points[indices[0]], - normal, - sub(points[indices[1]], points[indices[0]]), - )?; - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - let mut uses = Vec::with_capacity(4); - for index in 0..4 { - let from = indices[index]; - let to = indices[(index + 1) % 4]; - let coordinate = (from ^ to).trailing_zeros() as usize; - uses.push(plane_boundary( - &builder, - face_frame, - edges[from.min(to)][coordinate], - vertices[from], - vertices[to], - if from < to { - Orientation::Forward - } else { - Orientation::Reverse - }, - )?); - let uv = face_frame.local(points[from]); - for coordinate in 0..2 { - bounds[coordinate][0] = bounds[coordinate][0].min(uv[coordinate]); - bounds[coordinate][1] = bounds[coordinate][1].max(uv[coordinate]); - } - } - for bound in &mut bounds { - bound[0] -= accuracy.geometric; - bound[1] += accuracy.geometric; - } - let holes = if (face_index == low_face && crosses_low) - || (face_index == high_face && crosses_high) - { - let is_low = face_index == low_face; - let circle_frame = if is_low { tunnel_frame } else { high_frame }; - let circle_center = face_frame.local(circle_frame.origin); - let (vertex, circle) = if is_low { - low_boundary.ok_or_else(|| { - GeometryError::InvalidTopology("missing low cylinder boundary".into()) - })? - } else { - high_boundary.ok_or_else(|| { - GeometryError::InvalidTopology("missing high cylinder boundary".into()) - })? - }; - vec![vec![boundary( - circle, - vertex, - vertex, - if is_low { - Orientation::Forward - } else { - Orientation::Reverse - }, - PcurveGeometry::Conic2 { - origin: [circle_center[0], circle_center[1]], - axis_a: [ - cylinder.radius * dot(circle_frame.x, face_frame.x), - cylinder.radius * dot(circle_frame.x, face_frame.y), - ], - axis_b: [ - cylinder.radius * dot(circle_frame.y, face_frame.x), - cylinder.radius * dot(circle_frame.y, face_frame.y), - ], - }, - )]] - } else { - Vec::new() - }; - builder.face_with_holes( - key, - SurfaceGeometry::Plane { frame: face_frame }, - bounds, - uses, - holes, - )?; - builder.brep.topology.faces[face_index].provenance = face_provenance( - vec![box_source(box_, face_index)], - if (face_index == low_face && crosses_low) || (face_index == high_face && crosses_high) - { - FaceRole::Split - } else { - FaceRole::Preserved - }, - false, - ); - } - if operation == BooleanOp::Subtraction { - let (low_vertex, low_circle) = low_boundary.ok_or_else(|| { - GeometryError::InvalidTopology("missing low cylinder boundary".into()) - })?; - let (high_vertex, high_circle) = high_boundary.ok_or_else(|| { - GeometryError::InvalidTopology("missing high cylinder boundary".into()) - })?; - let seam = seam.ok_or_else(|| { - GeometryError::InvalidTopology("missing cylinder seam boundary".into()) - })?; - let tunnel_face = builder.brep.topology.faces.len() as u32; - builder.face( - "cutter:tunnel", - SurfaceGeometry::Cylinder { - frame: tunnel_frame, - radius: cylinder.radius, - }, - [[0.0, tau], [0.0, tunnel_height]], - vec![ - boundary( - low_circle, - low_vertex, - low_vertex, - Orientation::Forward, - uv_line([0.0, 0.0], [1.0, 0.0]), - ), - boundary( - seam, - low_vertex, - high_vertex, - Orientation::Forward, - uv_line([tau, 0.0], [0.0, 1.0]), - ), - boundary( - high_circle, - high_vertex, - high_vertex, - Orientation::Reverse, - uv_line([0.0, tunnel_height], [1.0, 0.0]), - ), - boundary( - seam, - high_vertex, - low_vertex, - Orientation::Reverse, - uv_line([0.0, 0.0], [0.0, 1.0]), - ), - ], - )?; - builder.brep.topology.faces[tunnel_face as usize].provenance = - face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Cut, true); - reverse_face(&mut builder.brep, tunnel_face); - if crosses_low != crosses_high { - let (key, circle, vertex, mut cap_frame, sense, axis_b, cylinder_cap) = if crosses_low { - ( - "cutter:pocket_cap", - high_circle, - high_vertex, - high_frame, - Orientation::Forward, - [0.0, cylinder.radius], - if alignment > 0.0 { 2 } else { 1 }, - ) - } else { - let mut cap_frame = tunnel_frame; - cap_frame.y = scale(cap_frame.y, -1.0); - cap_frame.z = scale(cap_frame.z, -1.0); - ( - "cutter:pocket_cap", - low_circle, - low_vertex, - cap_frame, - Orientation::Reverse, - [0.0, -cylinder.radius], - if alignment > 0.0 { 1 } else { 2 }, - ) - }; - let cap_face = builder.brep.topology.faces.len() as u32; - cap_frame.origin = if crosses_low { - high_frame.origin - } else { - tunnel_frame.origin - }; - builder.face( - key, - SurfaceGeometry::Plane { frame: cap_frame }, - [[-cylinder.radius, cylinder.radius]; 2], - vec![boundary( - circle, - vertex, - vertex, - sense, - PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [cylinder.radius, 0.0], - axis_b, - }, - )], - )?; - builder.brep.topology.faces[cap_face as usize].provenance = face_provenance( - vec![cylinder_source(cylinder, cylinder_cap)], - FaceRole::Cut, - true, - ); - reverse_face(&mut builder.brep, cap_face); - } - } else { - if crosses_low { - let entry = low_boundary.ok_or_else(|| { - GeometryError::InvalidTopology("missing low cylinder boundary".into()) - })?; - let outer_z = axial_span[0] - clipped_low; - let outer = circular_boundary(&mut builder, tunnel_frame, cylinder.radius, outer_z)?; - cylinder_band( - &mut builder, - tunnel_frame, - cylinder.radius, - outer_z, - 0.0, - outer, - entry, - face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Split, false), - false, - )?; - cylinder_cap( - &mut builder, - tunnel_frame, - cylinder.radius, - outer_z, - outer, - false, - face_provenance( - vec![cylinder_source(cylinder, cylinder_low_face)], - FaceRole::Preserved, - false, - ), - )?; - } - if crosses_high { - let entry = high_boundary.ok_or_else(|| { - GeometryError::InvalidTopology("missing high cylinder boundary".into()) - })?; - let outer_z = axial_span[1] - clipped_low; - let outer = circular_boundary(&mut builder, tunnel_frame, cylinder.radius, outer_z)?; - cylinder_band( - &mut builder, - tunnel_frame, - cylinder.radius, - tunnel_height, - outer_z, - entry, - outer, - face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Split, false), - false, - )?; - cylinder_cap( - &mut builder, - tunnel_frame, - cylinder.radius, - outer_z, - outer, - true, - face_provenance( - vec![cylinder_source(cylinder, cylinder_high_face)], - FaceRole::Preserved, - false, - ), - )?; - } - } - let mut out = builder.finish()?; - out.revision = box_ - .brep - .revision - .max(cylinder.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let inputs = if cylinder_is_a { - [cylinder.brep, box_.brep] - } else { - [box_.brep, cylinder.brep] - }; - Ok(BooleanResult { - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings: analytic_face_mappings(&out, inputs), - }, - brep: out, - }) -} - -fn cylinder_box_boolean( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let (cylinder, box_, cylinder_is_a) = if matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Cylinder { .. }) - ) { - (full_cylinder(a)?, super::box_booleans::full_box(b)?, true) - } else { - (full_cylinder(b)?, super::box_booleans::full_box(a)?, false) - }; - let clearance = 4.0 - * cylinder - .brep - .accuracy - .geometric - .max(box_.brep.accuracy.geometric); - let origin = box_.frame.local(cylinder.frame.origin); - let box_axes = [box_.frame.x, box_.frame.y, box_.frame.z]; - let mut cylinder_margin = f64::INFINITY; - for axis in 0..3 { - let axial_rate = dot(cylinder.frame.z, box_axes[axis]); - let radial_extent = cylinder.radius * (1.0 - axial_rate * axial_rate).max(0.0).sqrt(); - let other = origin[axis] + axial_rate * cylinder.height; - let lo = origin[axis].min(other) - radial_extent; - let hi = origin[axis].max(other) + radial_extent; - cylinder_margin = cylinder_margin.min(lo).min(box_.size[axis] - hi); - } - let mut box_margin = f64::INFINITY; - for corner in 0..8 { - let point = box_.frame.point(std::array::from_fn(|axis| { - if corner & (1 << axis) == 0 { - 0.0 - } else { - box_.size[axis] - } - })); - let local = cylinder.frame.local(point); - box_margin = box_margin - .min(cylinder.radius - local[0].hypot(local[1])) - .min(local[2]) - .min(cylinder.height - local[2]); - } - let cylinder_inside_box = cylinder_margin > clearance; - let box_inside_cylinder = box_margin > clearance; - if cylinder_inside_box || box_inside_cylinder { - let b_inside_a = if cylinder_is_a { - box_inside_cylinder - } else { - cylinder_inside_box - }; - return analytic_containment_boolean(a, b, b_inside_a, operation, id); - } - if cylinder_margin.abs() <= clearance || box_margin.abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "cylinder/cuboid containment boundary is below geometric resolution".into(), - )); - } - let accuracy = Accuracy { - geometric: cylinder - .brep - .accuracy - .geometric - .max(box_.brep.accuracy.geometric), - intersection: cylinder - .brep - .accuracy - .intersection - .max(box_.brep.accuracy.intersection), - tessellation: cylinder - .brep - .accuracy - .tessellation - .max(box_.brep.accuracy.tessellation), - exchange: cylinder - .brep - .accuracy - .exchange - .max(box_.brep.accuracy.exchange), - }; - perpendicular_cylinder_box_boolean(&box_, &cylinder, cylinder_is_a, operation, id, accuracy) -} - -fn cylinder_source(input: &CylinderInput<'_>, face: u32) -> FaceSource { - FaceSource { - entity: input.brep.id.clone(), - body: input.brep.id.clone(), - key: input.brep.topology.faces[face as usize].key.clone(), - face, - } -} - -fn face_provenance(sources: Vec, role: FaceRole, reversed: bool) -> FaceProvenance { - FaceProvenance { - sources, - role, - reversed, - } -} - -fn circular_boundary( - builder: &mut Builder, - frame: Frame3, - radius: f64, - z: f64, -) -> Result<(u32, u32), GeometryError> { - let mut circle_frame = frame; - circle_frame.origin = frame.point([0.0, 0.0, z]); - let vertex = builder.vertex(frame.point([radius, 0.0, z])); - let edge = builder.edge( - CurveGeometry::Circle { - frame: circle_frame, - radius, - }, - Interval::new(0.0, std::f64::consts::TAU)?, - false, - ); - Ok((vertex, edge)) -} - -fn cylinder_band( - builder: &mut Builder, - frame: Frame3, - radius: f64, - z0: f64, - z1: f64, - low: (u32, u32), - high: (u32, u32), - provenance: FaceProvenance, - reversed: bool, -) -> Result<(), GeometryError> { - use Orientation::{Forward as F, Reverse as R}; - let seam = builder.edge( - CurveGeometry::Line { - origin: frame.point([radius, 0.0, z0]), - direction: frame.z, - }, - Interval::new(0.0, z1 - z0)?, - true, - ); - let face = builder.brep.topology.faces.len() as u32; - let tau = std::f64::consts::TAU; - builder.face( - &format!("{face}:cylinder-band"), - SurfaceGeometry::Cylinder { frame, radius }, - [[0.0, tau], [z0, z1]], - vec![ - boundary(low.1, low.0, low.0, F, uv_line([0.0, z0], [1.0, 0.0])), - boundary(seam, low.0, high.0, F, uv_line([tau, z0], [0.0, 1.0])), - boundary(high.1, high.0, high.0, R, uv_line([0.0, z1], [1.0, 0.0])), - boundary(seam, high.0, low.0, R, uv_line([0.0, z0], [0.0, 1.0])), - ], - )?; - builder.brep.topology.faces[face as usize].provenance = provenance; - if reversed { - reverse_face(&mut builder.brep, face); - } - Ok(()) -} - -fn cylinder_cap( - builder: &mut Builder, - frame: Frame3, - radius: f64, - z: f64, - boundary_edge: (u32, u32), - top: bool, - provenance: FaceProvenance, -) -> Result<(), GeometryError> { - use Orientation::{Forward as F, Reverse as R}; - let mut surface_frame = frame; - surface_frame.origin = frame.point([0.0, 0.0, z]); - if !top { - surface_frame.y = scale(surface_frame.y, -1.0); - surface_frame.z = scale(surface_frame.z, -1.0); - } - let face = builder.brep.topology.faces.len() as u32; - builder.face( - &format!("{face}:cylinder-cap"), - SurfaceGeometry::Plane { - frame: surface_frame, - }, - [[-radius, radius]; 2], - vec![boundary( - boundary_edge.1, - boundary_edge.0, - boundary_edge.0, - if top { F } else { R }, - PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [radius, 0.0], - axis_b: [0.0, if top { radius } else { -radius }], - }, - )], - )?; - builder.brep.topology.faces[face as usize].provenance = provenance; - Ok(()) -} - -fn sphere_band( - builder: &mut Builder, - input: &SphereInput<'_>, - frame: Frame3, - latitude0: f64, - latitude1: f64, - low: (u32, u32), - high: (u32, u32), - reversed: bool, -) -> Result<(), GeometryError> { - use Orientation::{Forward as F, Reverse as R}; - let seam = builder.edge( - CurveGeometry::Circle { - frame: Frame3 { - x: frame.x, - y: frame.z, - z: scale(frame.y, -1.0), - ..frame - }, - radius: input.radius, - }, - Interval::new(latitude0, latitude1)?, - true, - ); - let face = builder.brep.topology.faces.len() as u32; - let tau = std::f64::consts::TAU; - builder.face( - &format!("{face}:sphere-band"), - SurfaceGeometry::Sphere { - frame, - radius: input.radius, - }, - [[0.0, tau], [latitude0, latitude1]], - vec![ - boundary( - low.1, - low.0, - low.0, - F, - uv_line([0.0, latitude0], [1.0, 0.0]), - ), - boundary(seam, low.0, high.0, F, uv_line([tau, 0.0], [0.0, 1.0])), - boundary( - high.1, - high.0, - high.0, - R, - uv_line([0.0, latitude1], [1.0, 0.0]), - ), - boundary(seam, high.0, low.0, R, uv_line([0.0, 0.0], [0.0, 1.0])), - ], - )?; - builder.brep.topology.faces[face as usize].provenance = provenance( - input, - if reversed { - FaceRole::Cut - } else { - FaceRole::Split - }, - reversed, - ); - if reversed { - reverse_face(&mut builder.brep, face); - } - Ok(()) -} - -fn finish_sphere_cylinder_result( - mut brep: BrepEnvelope, - sphere: &SphereInput<'_>, - cylinder: &CylinderInput<'_>, - sphere_is_a: bool, - operation: BooleanOp, -) -> Result { - brep.revision = sphere - .brep - .revision - .max(cylinder.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - brep.validate()?; - let inputs = if sphere_is_a { - [sphere.brep, cylinder.brep] - } else { - [cylinder.brep, sphere.brep] - }; - let face_mappings = analytic_face_mappings(&brep, inputs); - Ok(BooleanResult { - brep, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings, - }, - }) -} - -fn coaxial_sphere_cylinder_boolean( - sphere: &SphereInput<'_>, - cylinder: &CylinderInput<'_>, - sphere_is_a: bool, - operation: BooleanOp, - id: String, - clearance: f64, -) -> Result { - let local = cylinder.frame.local(sphere.frame.origin); - let radial = local[0].hypot(local[1]); - let scale = sphere - .radius - .max(cylinder.radius) - .max(cylinder.height) - .max(1.0); - let roundoff = 128.0 * f64::EPSILON * scale; - if radial > roundoff { - return Err(if radial <= clearance { - GeometryError::UnresolvedIntersection( - "sphere/cylinder axes differ below geometric resolution".into(), - ) - } else { - GeometryError::CoverageGap { - families: ["sphere".into(), "noncoaxial cylinder".into()], - } - }); - } - let radius_gap = sphere.radius - cylinder.radius; - if radius_gap <= clearance { - return Err(if radius_gap.abs() <= clearance { - GeometryError::UnresolvedIntersection( - "sphere/cylinder tangency is below geometric resolution".into(), - ) - } else { - GeometryError::CoverageGap { - families: ["sphere".into(), "cylinder".into()], - } - }); - } - let sphere_low = local[2] - sphere.radius; - let sphere_high = local[2] + sphere.radius; - let lower_clearance = sphere_low; - let upper_clearance = cylinder.height - sphere_high; - if lower_clearance <= clearance || upper_clearance <= clearance { - let near = lower_clearance.abs() <= clearance || upper_clearance.abs() <= clearance; - return Err(if near { - GeometryError::UnresolvedIntersection( - "sphere/cylinder cap event is below geometric resolution".into(), - ) - } else { - GeometryError::CoverageGap { - families: ["sphere".into(), "finite cylinder cap".into()], - } - }); - } - - let accuracy = Accuracy { - geometric: sphere - .brep - .accuracy - .geometric - .max(cylinder.brep.accuracy.geometric), - intersection: sphere - .brep - .accuracy - .intersection - .max(cylinder.brep.accuracy.intersection), - tessellation: sphere - .brep - .accuracy - .tessellation - .max(cylinder.brep.accuracy.tessellation), - exchange: sphere - .brep - .accuracy - .exchange - .max(cylinder.brep.accuracy.exchange), - }; - let half = ((sphere.radius - cylinder.radius) * (sphere.radius + cylinder.radius)).sqrt(); - let z0 = local[2] - half; - let z1 = local[2] + half; - let latitude0 = (-half / sphere.radius).asin(); - let latitude1 = (half / sphere.radius).asin(); - let sphere_frame = Frame3 { - origin: sphere.frame.origin, - x: cylinder.frame.x, - y: cylinder.frame.y, - z: cylinder.frame.z, - }; - let cylinder_lateral = cylinder_source(cylinder, 0); - let cylinder_caps = [cylinder_source(cylinder, 1), cylinder_source(cylinder, 2)]; - - if operation == BooleanOp::Subtraction && !sphere_is_a { - let mut out = BrepEnvelope::new(id, accuracy)?; - for north in [false, true] { - let mut builder = Builder::new( - format!("{}:{}", out.id, if north { "upper" } else { "lower" }), - accuracy, - )?; - let intersection = circular_boundary( - &mut builder, - cylinder.frame, - cylinder.radius, - if north { z1 } else { z0 }, - )?; - let end = circular_boundary( - &mut builder, - cylinder.frame, - cylinder.radius, - if north { cylinder.height } else { 0.0 }, - )?; - if north { - cylinder_band( - &mut builder, - cylinder.frame, - cylinder.radius, - z1, - cylinder.height, - intersection, - end, - face_provenance(vec![cylinder_lateral.clone()], FaceRole::Split, false), - false, - )?; - cylinder_cap( - &mut builder, - cylinder.frame, - cylinder.radius, - cylinder.height, - end, - true, - face_provenance(vec![cylinder_caps[1].clone()], FaceRole::Preserved, false), - )?; - patch( - &mut builder, - sphere, - sphere_frame, - latitude1, - true, - intersection.1, - intersection.0, - true, - )?; - } else { - cylinder_band( - &mut builder, - cylinder.frame, - cylinder.radius, - 0.0, - z0, - end, - intersection, - face_provenance(vec![cylinder_lateral.clone()], FaceRole::Split, false), - false, - )?; - cylinder_cap( - &mut builder, - cylinder.frame, - cylinder.radius, - 0.0, - end, - false, - face_provenance(vec![cylinder_caps[0].clone()], FaceRole::Preserved, false), - )?; - patch( - &mut builder, - sphere, - sphere_frame, - latitude0, - false, - intersection.1, - intersection.0, - true, - )?; - } - let part = builder.finish()?; - let provenances: Vec<_> = part - .topology - .faces - .iter() - .map(|face| face.provenance.clone()) - .collect(); - let face_offset = out.topology.faces.len(); - append_analytic_input(&mut out, &part)?; - for (face, provenance) in out.topology.faces[face_offset..] - .iter_mut() - .zip(provenances) - { - face.provenance = provenance; - } - } - return finish_sphere_cylinder_result(out, sphere, cylinder, sphere_is_a, operation); - } - - let mut builder = Builder::new(id, accuracy)?; - let low = circular_boundary(&mut builder, cylinder.frame, cylinder.radius, z0)?; - let high = circular_boundary(&mut builder, cylinder.frame, cylinder.radius, z1)?; - match operation { - BooleanOp::Intersection => { - patch( - &mut builder, - sphere, - sphere_frame, - latitude0, - false, - low.1, - low.0, - false, - )?; - cylinder_band( - &mut builder, - cylinder.frame, - cylinder.radius, - z0, - z1, - low, - high, - face_provenance(vec![cylinder_lateral], FaceRole::Split, false), - false, - )?; - patch( - &mut builder, - sphere, - sphere_frame, - latitude1, - true, - high.1, - high.0, - false, - )?; - } - BooleanOp::Subtraction => { - sphere_band( - &mut builder, - sphere, - sphere_frame, - latitude0, - latitude1, - low, - high, - false, - )?; - cylinder_band( - &mut builder, - cylinder.frame, - cylinder.radius, - z0, - z1, - low, - high, - face_provenance(vec![cylinder_lateral], FaceRole::Cut, true), - true, - )?; - } - BooleanOp::Union => { - let bottom = circular_boundary(&mut builder, cylinder.frame, cylinder.radius, 0.0)?; - let top = circular_boundary( - &mut builder, - cylinder.frame, - cylinder.radius, - cylinder.height, - )?; - cylinder_band( - &mut builder, - cylinder.frame, - cylinder.radius, - 0.0, - z0, - bottom, - low, - face_provenance(vec![cylinder_lateral.clone()], FaceRole::Split, false), - false, - )?; - cylinder_cap( - &mut builder, - cylinder.frame, - cylinder.radius, - 0.0, - bottom, - false, - face_provenance(vec![cylinder_caps[0].clone()], FaceRole::Preserved, false), - )?; - sphere_band( - &mut builder, - sphere, - sphere_frame, - latitude0, - latitude1, - low, - high, - false, - )?; - cylinder_band( - &mut builder, - cylinder.frame, - cylinder.radius, - z1, - cylinder.height, - high, - top, - face_provenance(vec![cylinder_lateral], FaceRole::Split, false), - false, - )?; - cylinder_cap( - &mut builder, - cylinder.frame, - cylinder.radius, - cylinder.height, - top, - true, - face_provenance(vec![cylinder_caps[1].clone()], FaceRole::Preserved, false), - )?; - } - } - finish_sphere_cylinder_result(builder.finish()?, sphere, cylinder, sphere_is_a, operation) -} - -fn append_cylinder_segment( - out: &mut BrepEnvelope, - frame: Frame3, - radius: f64, - height: f64, - provenances: [FaceProvenance; 3], - index: usize, -) -> Result<(), GeometryError> { - if height <= 4.0 * out.accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "cylinder boolean leaves a sub-tolerance axial segment".into(), - )); - } - let part = primitives::cylinder( - format!("{}:segment-{index}", out.id), - frame, - radius, - height, - out.accuracy, - )?; - let face_offset = out.topology.faces.len(); - append_analytic_input(out, &part)?; - for (face, provenance) in out.topology.faces[face_offset..] - .iter_mut() - .zip(provenances) - { - face.provenance = provenance; - } - Ok(()) -} - -fn append_annular_cylinder( - out: &mut BrepEnvelope, - frame: Frame3, - inner_radius: f64, - outer_radius: f64, - height: f64, - provenances: [FaceProvenance; 4], -) -> Result<(), GeometryError> { - let part = primitives::annular_cylinder( - format!("{}:annular", out.id), - frame, - inner_radius, - outer_radius, - height, - out.accuracy, - )?; - let face_offset = out.topology.faces.len(); - append_analytic_input(out, &part)?; - for (face, provenance) in out.topology.faces[face_offset..] - .iter_mut() - .zip(provenances) - { - face.provenance = provenance; - } - Ok(()) -} - -struct NumericalCylinderBoundary { - definition: u32, - edge: u32, - vertex: u32, - cutter_start: [f64; 2], - cutter_end: [f64; 2], - cutter_middle: [f64; 2], -} - -fn chart_seam_rotation(graph: &super::face_intersection::IntersectionGraph) -> f64 { - let tau = std::f64::consts::TAU; - let mut parameters = graph - .branches - .iter() - .filter_map( - |branch| match graph.geometry.curves[branch.curve as usize] { - CurveGeometry::Intersection { definition } => Some(definition), - _ => None, - }, - ) - .flat_map(|definition| { - graph.geometry.intersections[definition as usize] - .anchors - .iter() - .map(|anchor| anchor.uv_a[0].rem_euclid(tau)) - }) - .collect::>(); - parameters.sort_by(f64::total_cmp); - parameters.dedup_by(|left, right| (*left - *right).abs() <= f64::EPSILON * 64.0); - if parameters.is_empty() { - return 0.0; - } - let mut largest = (0.0, parameters[0]); - for index in 0..parameters.len() { - let start = parameters[index]; - let end = if index + 1 < parameters.len() { - parameters[index + 1] - } else { - parameters[0] + tau - }; - if end - start > largest.0 { - largest = (end - start, start); - } - } - (largest.1 + 0.5 * largest.0).rem_euclid(tau) -} - -fn rotate_cylinder_chart(frame: Frame3, angle: f64) -> Frame3 { - let (sine, cosine) = angle.sin_cos(); - Frame3 { - x: add(scale(frame.x, cosine), scale(frame.y, sine)), - y: add(scale(frame.y, cosine), scale(frame.x, -sine)), - ..frame - } -} - -fn rotate_first_intersection_chart( - definition: &mut super::intersection::IntersectionDefinition, - angle: f64, - accuracy: Accuracy, -) -> Result<(), GeometryError> { - let tau = std::f64::consts::TAU; - let original_first = definition - .anchors - .first() - .ok_or_else(|| GeometryError::InvalidGeometry("empty intersection branch".into()))? - .uv_a[0]; - let mut previous: Option = None; - for anchor in &mut definition.anchors { - let mut value = (anchor.uv_a[0] - angle).rem_euclid(tau); - if let Some(previous) = previous { - value += ((previous - value) / tau).round() * tau; - } - anchor.uv_a[0] = value; - previous = Some(value); - } - let lo = definition - .anchors - .iter() - .map(|anchor| anchor.uv_a[0]) - .fold(f64::INFINITY, f64::min); - let hi = definition - .anchors - .iter() - .map(|anchor| anchor.uv_a[0]) - .fold(f64::NEG_INFINITY, f64::max); - if hi - lo >= tau - accuracy.intersection { - return Err(GeometryError::CoverageGap { - families: [ - "cylinder through subtraction".into(), - "intersection loop crossing every chart seam".into(), - ], - }); - } - let shift = -(lo / tau).floor() * tau; - for anchor in &mut definition.anchors { - anchor.uv_a[0] += shift; - } - let chart_shift = definition.anchors[0].uv_a[0] - original_first; - for tube in &mut definition.uv_tubes { - tube[0] = Interval::new(tube[0].lo + chart_shift, tube[0].hi + chart_shift)?; - } - Ok(()) -} - -fn transverse_cylinder_through_subtraction( - host: &CylinderInput<'_>, - cutter: &CylinderInput<'_>, - id: String, - accuracy: Accuracy, -) -> Result { - let mut body_graph = intersect_breps(host.brep, cutter.brep)?; - if body_graph.pairs.len() != 1 { - return Err(GeometryError::CoverageGap { - families: [ - "cylinder through subtraction".into(), - "cap-intersecting cylinder".into(), - ], - }); - } - let pair = body_graph - .pairs - .pop() - .ok_or_else(|| GeometryError::CoverageGap { - families: [ - "cylinder through subtraction".into(), - "missing lateral intersection graph".into(), - ], - })?; - if pair.faces != [0, 0] - || pair.graph.coincident - || !pair.graph.contacts.is_empty() - || pair.graph.branches.len() != 2 - { - return Err(GeometryError::CoverageGap { - families: [ - "cylinder through subtraction".into(), - "non-transverse cylinder graph".into(), - ], - }); - } - - let seam_rotation = chart_seam_rotation(&pair.graph); - let host_frame = rotate_cylinder_chart(host.frame, seam_rotation); - let mut builder = Builder::new(id, accuracy)?; - let low = builder.vertex(host_frame.point([host.radius, 0.0, 0.0])); - let high = builder.vertex(host_frame.point([host.radius, 0.0, host.height])); - let tau = std::f64::consts::TAU; - let bottom = builder.edge( - CurveGeometry::Circle { - frame: host_frame, - radius: host.radius, - }, - Interval::new(0.0, tau)?, - false, - ); - let mut top_frame = host_frame; - top_frame.origin = host_frame.point([0.0, 0.0, host.height]); - let top = builder.edge( - CurveGeometry::Circle { - frame: top_frame, - radius: host.radius, - }, - Interval::new(0.0, tau)?, - false, - ); - let host_seam = builder.edge( - CurveGeometry::Line { - origin: host_frame.point([host.radius, 0.0, 0.0]), - direction: host_frame.z, - }, - Interval::new(0.0, host.height)?, - true, - ); - - let mut numerical = Vec::with_capacity(2); - for branch in &pair.graph.branches { - let CurveGeometry::Intersection { definition } = - pair.graph.geometry.curves[branch.curve as usize] - else { - return Err(GeometryError::InvalidGeometry( - "universal face graph did not retain its intersection definition".into(), - )); - }; - let mut definition = pair.graph.geometry.intersections[definition as usize].clone(); - definition.surfaces = [0, 3]; - rotate_first_intersection_chart(&mut definition, seam_rotation, accuracy)?; - let definition_id = builder.brep.geometry.intersections.len() as u32; - builder.brep.geometry.intersections.push(definition); - let curve = CurveGeometry::Intersection { - definition: definition_id, - }; - let start = pair - .graph - .geometry - .curve(branch.curve)? - .point_at(branch.range.lo)?; - let end = pair - .graph - .geometry - .curve(branch.curve)? - .point_at(branch.range.hi)?; - if norm(sub(start, end)) > accuracy.intersection { - return Err(GeometryError::CoverageGap { - families: [ - "cylinder through subtraction".into(), - "open lateral intersection branch".into(), - ], - }); - } - let vertex = builder.vertex(start); - let edge = builder.edge(curve, branch.range, false); - numerical.push(NumericalCylinderBoundary { - definition: definition_id, - edge, - vertex, - cutter_start: pair - .graph - .geometry - .pcurve_at(branch.pcurves[1], branch.range.lo)?, - cutter_end: pair - .graph - .geometry - .pcurve_at(branch.pcurves[1], branch.range.hi)?, - cutter_middle: pair - .graph - .geometry - .pcurve_at(branch.pcurves[1], branch.range.midpoint())?, - }); - } - numerical.sort_by(|left, right| left.cutter_middle[1].total_cmp(&right.cutter_middle[1])); - let upper_shift = - ((numerical[0].cutter_end[0] - numerical[1].cutter_start[0]) / tau).round() * tau; - { - let upper = &mut numerical[1]; - upper.cutter_start[0] += upper_shift; - upper.cutter_end[0] += upper_shift; - upper.cutter_middle[0] += upper_shift; - let definition = &mut builder.brep.geometry.intersections[upper.definition as usize]; - for anchor in &mut definition.anchors { - anchor.uv_b[0] += upper_shift; - } - for tube in &mut definition.uv_tubes { - tube[2] = Interval::new(tube[2].lo + upper_shift, tube[2].hi + upper_shift)?; - } - } - let lower = &numerical[0]; - let upper = &numerical[1]; - if upper.cutter_middle[1] - lower.cutter_middle[1] <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "cylinder through-cut branches are below geometric resolution".into(), - )); - } - - let host_holes = numerical - .iter() - .map(|numeric_boundary| { - vec![boundary( - numeric_boundary.edge, - numeric_boundary.vertex, - numeric_boundary.vertex, - Orientation::Forward, - PcurveGeometry::IntersectionSide { - definition: numeric_boundary.definition, - side: IntersectionSide::A, - }, - )] - }) - .collect(); - builder.face_with_holes( - "host:lateral", - SurfaceGeometry::Cylinder { - frame: host_frame, - radius: host.radius, - }, - [[0.0, tau], [0.0, host.height]], - vec![ - boundary( - bottom, - low, - low, - Orientation::Forward, - uv_line([0.0, 0.0], [1.0, 0.0]), - ), - boundary( - host_seam, - low, - high, - Orientation::Forward, - uv_line([tau, 0.0], [0.0, 1.0]), - ), - boundary( - top, - high, - high, - Orientation::Reverse, - uv_line([0.0, host.height], [1.0, 0.0]), - ), - boundary( - host_seam, - high, - low, - Orientation::Reverse, - uv_line([0.0, 0.0], [0.0, 1.0]), - ), - ], - host_holes, - )?; - - let mut lower_cap_frame = host_frame; - lower_cap_frame.y = scale(lower_cap_frame.y, -1.0); - lower_cap_frame.z = scale(lower_cap_frame.z, -1.0); - builder.face( - "host:lower_cap", - SurfaceGeometry::Plane { - frame: lower_cap_frame, - }, - [[-host.radius, host.radius]; 2], - vec![boundary( - bottom, - low, - low, - Orientation::Reverse, - PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [host.radius, 0.0], - axis_b: [0.0, -host.radius], - }, - )], - )?; - builder.face( - "host:upper_cap", - SurfaceGeometry::Plane { frame: top_frame }, - [[-host.radius, host.radius]; 2], - vec![boundary( - top, - high, - high, - Orientation::Forward, - PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [host.radius, 0.0], - axis_b: [0.0, host.radius], - }, - )], - )?; - - let lower_end = lower.cutter_end; - let upper_start = upper.cutter_start; - let upper_right_u = upper_start[0]; - let lower_v = lower_end[1]; - let upper_v = upper_start[1]; - let arc_range = Interval::new( - lower_end[0].min(upper_right_u), - lower_end[0].max(upper_right_u), - )?; - if arc_range.width() * cutter.radius <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "cylinder cut chart seam arc is below geometric resolution".into(), - )); - } - let mut seam_circle_frame = cutter.frame; - seam_circle_frame.origin = cutter.frame.point([0.0, 0.0, lower_v]); - let seam_arc = builder.edge( - CurveGeometry::Circle { - frame: seam_circle_frame, - radius: cutter.radius, - }, - arc_range, - true, - ); - let seam_middle_point = cutter - .brep - .geometry - .surface(0)? - .point_at([upper_right_u, lower_v])?; - let seam_middle = builder.vertex(seam_middle_point); - let seam_vector = sub( - builder.brep.topology.vertices[upper.vertex as usize].position, - seam_middle_point, - ); - let seam_length = norm(seam_vector); - if seam_length <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "cylinder cut chart seam generator is below geometric resolution".into(), - )); - } - let seam_generator = builder.edge( - CurveGeometry::Line { - origin: seam_middle_point, - direction: unit(seam_vector)?, - }, - Interval::new(0.0, seam_length)?, - true, - ); - let arc_forward = lower_end[0] < upper_right_u; - let first_arc_sense = if arc_forward { - Orientation::Forward - } else { - Orientation::Reverse - }; - let second_arc_sense = reverse(first_arc_sense); - let second_arc_start = if second_arc_sense == Orientation::Forward { - arc_range.lo - } else { - arc_range.hi - }; - let second_arc_end = if second_arc_sense == Orientation::Forward { - arc_range.hi - } else { - arc_range.lo - }; - let arc_shift = lower.cutter_start[0] - second_arc_end; - let winding_error = upper.cutter_end[0] - (second_arc_start + arc_shift); - let winding_residual = winding_error - (winding_error / tau).round() * tau; - if winding_residual.abs() > accuracy.intersection / cutter.radius { - return Err(GeometryError::UnresolvedIntersection( - "cylinder cut chart seam winding is inconsistent".into(), - )); - } - let cutter_face = builder.brep.topology.faces.len() as u32; - let mut cutter_u_bounds = [f64::INFINITY, f64::NEG_INFINITY]; - let mut cutter_v_bounds = [f64::INFINITY, f64::NEG_INFINITY]; - for boundary in &numerical { - let definition = &builder.brep.geometry.intersections[boundary.definition as usize]; - for anchor in &definition.anchors { - cutter_u_bounds[0] = cutter_u_bounds[0].min(anchor.uv_b[0]); - cutter_u_bounds[1] = cutter_u_bounds[1].max(anchor.uv_b[0]); - cutter_v_bounds[0] = cutter_v_bounds[0].min(anchor.uv_b[1]); - cutter_v_bounds[1] = cutter_v_bounds[1].max(anchor.uv_b[1]); - } - } - for value in [upper_right_u, upper.cutter_end[0], lower.cutter_start[0]] { - cutter_u_bounds[0] = cutter_u_bounds[0].min(value); - cutter_u_bounds[1] = cutter_u_bounds[1].max(value); - } - for value in [lower_v, upper_v] { - cutter_v_bounds[0] = cutter_v_bounds[0].min(value); - cutter_v_bounds[1] = cutter_v_bounds[1].max(value); - } - builder.face( - "cutter:tunnel", - SurfaceGeometry::Cylinder { - frame: cutter.frame, - radius: cutter.radius, - }, - [cutter_u_bounds, cutter_v_bounds], - vec![ - boundary( - lower.edge, - lower.vertex, - lower.vertex, - Orientation::Forward, - PcurveGeometry::IntersectionSide { - definition: lower.definition, - side: IntersectionSide::B, - }, - ), - boundary( - seam_arc, - lower.vertex, - seam_middle, - first_arc_sense, - uv_line([0.0, lower_v], [1.0, 0.0]), - ), - boundary( - seam_generator, - seam_middle, - upper.vertex, - Orientation::Forward, - uv_line( - [upper_right_u, lower_v], - [0.0, (upper_v - lower_v) / seam_length], - ), - ), - boundary( - upper.edge, - upper.vertex, - upper.vertex, - Orientation::Forward, - PcurveGeometry::IntersectionSide { - definition: upper.definition, - side: IntersectionSide::B, - }, - ), - boundary( - seam_generator, - upper.vertex, - seam_middle, - Orientation::Reverse, - uv_line( - [upper.cutter_end[0], lower_v], - [0.0, (upper_v - lower_v) / seam_length], - ), - ), - boundary( - seam_arc, - seam_middle, - lower.vertex, - second_arc_sense, - uv_line([arc_shift, lower_v], [1.0, 0.0]), - ), - ], - )?; - builder.brep.topology.faces[0].provenance = - face_provenance(vec![cylinder_source(host, 0)], FaceRole::Split, false); - builder.brep.topology.faces[1].provenance = - face_provenance(vec![cylinder_source(host, 1)], FaceRole::Preserved, false); - builder.brep.topology.faces[2].provenance = - face_provenance(vec![cylinder_source(host, 2)], FaceRole::Preserved, false); - builder.brep.topology.faces[cutter_face as usize].provenance = - face_provenance(vec![cylinder_source(cutter, 0)], FaceRole::Cut, true); - reverse_face(&mut builder.brep, cutter_face); - let mut out = builder.finish()?; - out.revision = host - .brep - .revision - .max(cutter.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let face_mappings = cylinder_face_mappings(&out, [host, cutter]); - Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation: BooleanOp::Subtraction, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings, - }, - }) -} - -fn cylinder_face_mappings(out: &BrepEnvelope, inputs: [&CylinderInput<'_>; 2]) -> Vec { - let mut mappings = Vec::with_capacity(6); - for input in inputs { - for face in 0..3 { - let source = cylinder_source(input, face); - let result_faces = out - .topology - .faces - .iter() - .filter(|result| { - result.provenance.sources.iter().any(|candidate| { - candidate.entity == source.entity - && candidate.body == source.body - && candidate.key == source.key - && candidate.face == source.face - }) - }) - .map(|result| result.id) - .collect(); - mappings.push(FaceMapping { - source, - result_faces, - }); - } - } - mappings -} - -#[derive(Clone, Copy)] -struct CylinderArc { - frame: Frame3, - radius: f64, - range: Interval, - start: usize, - end: usize, - reversed: bool, -} - -fn circle_parameter(frame: Frame3, point: Point3) -> f64 { - let local = frame.local(point); - local[1].atan2(local[0]).rem_euclid(std::f64::consts::TAU) -} - -fn selected_cylinder_arc( - frame: Frame3, - radius: f64, - points: [Point3; 2], - other_center: Point3, - other_radius: f64, - keep_inside: bool, - reversed: bool, -) -> Result { - let angles = points.map(|point| circle_parameter(frame, point)); - let tau = std::f64::consts::TAU; - let forward_end = if angles[1] > angles[0] { - angles[1] - } else { - angles[1] + tau - }; - let candidates = [ - (angles[0], forward_end, 0, 1), - (angles[1], angles[0] + tau, 1, 0), - ]; - for (lo, hi, start, end) in candidates { - let midpoint = frame.point([ - radius * ((lo + hi) * 0.5).cos(), - radius * ((lo + hi) * 0.5).sin(), - 0.0, - ]); - let inside = norm(sub(midpoint, other_center)) < other_radius; - if inside == keep_inside { - return Ok(CylinderArc { - frame, - radius, - range: Interval::new(lo, hi)?, - start, - end, - reversed, - }); - } - } - Err(GeometryError::UnresolvedIntersection( - "could not isolate the selected cylinder boundary arc".into(), - )) -} - -fn arc_angle_at(arc: CylinderArc, point: usize) -> Result { - if point == arc.start { - Ok(arc.range.lo) - } else if point == arc.end { - Ok(arc.range.hi) - } else { - Err(GeometryError::InvalidTopology( - "cylinder arc endpoint is not part of the intersection".into(), - )) - } -} - -fn add_cylinder_arc_face( - builder: &mut Builder, - key: &str, - arc: CylinderArc, - bottom_edge: u32, - top_edge: u32, - vertical_edges: [u32; 2], - vertices: [[u32; 2]; 2], - height: f64, -) -> Result<(), GeometryError> { - use Orientation::{Forward as F, Reverse as R}; - let bottom = vertices[0]; - let top = vertices[1]; - let start = arc.start; - let end = arc.end; - let vertical = |point: usize| -> Result { - Ok(boundary( - vertical_edges[point], - bottom[point], - top[point], - F, - uv_line([arc_angle_at(arc, point)?, 0.0], [0.0, 1.0]), - )) - }; - let vertical_reverse = |point: usize| -> Result { - Ok(boundary( - vertical_edges[point], - top[point], - bottom[point], - R, - uv_line([arc_angle_at(arc, point)?, 0.0], [0.0, 1.0]), - )) - }; - let uses = if arc.reversed { - vec![ - boundary( - bottom_edge, - bottom[end], - bottom[start], - R, - uv_line([0.0; 2], [1.0, 0.0]), - ), - vertical(start)?, - boundary( - top_edge, - top[start], - top[end], - F, - uv_line([0.0, height], [1.0, 0.0]), - ), - vertical_reverse(end)?, - ] - } else { - vec![ - boundary( - bottom_edge, - bottom[start], - bottom[end], - F, - uv_line([0.0; 2], [1.0, 0.0]), - ), - vertical(end)?, - boundary( - top_edge, - top[end], - top[start], - R, - uv_line([0.0, height], [1.0, 0.0]), - ), - vertical_reverse(start)?, - ] - }; - let face = builder.brep.topology.faces.len(); - builder.face( - key, - SurfaceGeometry::Cylinder { - frame: arc.frame, - radius: arc.radius, - }, - [[arc.range.lo, arc.range.hi], [0.0, height]], - uses, - )?; - if arc.reversed { - builder.brep.topology.faces[face].sense = R; - } - Ok(()) -} - -fn shifted_cylinder_arc(mut arc: CylinderArc, axis: Point3, offset: f64) -> CylinderArc { - arc.frame.origin = std::array::from_fn(|i| arc.frame.origin[i] + axis[i] * offset); - arc -} - -fn arc_endpoints(arc: CylinderArc, sense: Orientation) -> (usize, usize) { - if sense == Orientation::Forward { - (arc.start, arc.end) - } else { - (arc.end, arc.start) - } -} - -fn two_arc_plane_uses( - builder: &Builder, - frame: Frame3, - first: (CylinderArc, u32), - second: (CylinderArc, u32), - vertices: [u32; 2], -) -> Result, GeometryError> { - for first_sense in [Orientation::Forward, Orientation::Reverse] { - let (first_from, first_to) = arc_endpoints(first.0, first_sense); - let second_sense = if second.0.start == first_to && second.0.end == first_from { - Orientation::Forward - } else if second.0.end == first_to && second.0.start == first_from { - Orientation::Reverse - } else { - continue; - }; - let mut samples = Vec::with_capacity(34); - for (arc, sense) in [(first.0, first_sense), (second.0, second_sense)] { - for sample in 0..=16 { - if !samples.is_empty() && sample == 0 { - continue; - } - let fraction = sample as f64 / 16.0; - let parameter = if sense == Orientation::Forward { - arc.range.lo + arc.range.width() * fraction - } else { - arc.range.hi - arc.range.width() * fraction - }; - samples.push(frame.local(arc.frame.point([ - arc.radius * parameter.cos(), - arc.radius * parameter.sin(), - 0.0, - ]))); - } - } - let signed_area = samples - .iter() - .zip(samples.iter().cycle().skip(1)) - .take(samples.len()) - .map(|(a, b)| a[0] * b[1] - b[0] * a[1]) - .sum::() - * 0.5; - if signed_area > 0.0 { - return Ok(vec![ - plane_boundary( - builder, - frame, - first.1, - vertices[first_from], - vertices[first_to], - first_sense, - )?, - plane_boundary( - builder, - frame, - second.1, - vertices[arc_endpoints(second.0, second_sense).0], - vertices[arc_endpoints(second.0, second_sense).1], - second_sense, - )?, - ]); - } - } - Err(GeometryError::InvalidTopology( - "two-arc planar boundary does not form a positive loop".into(), - )) -} - -fn two_arc_plane_bounds(frame: Frame3, arcs: [CylinderArc; 2], tolerance: f64) -> [[f64; 2]; 2] { - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - for arc in arcs { - for point in [ - arc.frame.origin, - std::array::from_fn(|i| arc.frame.origin[i] + arc.frame.x[i] * arc.radius), - std::array::from_fn(|i| arc.frame.origin[i] - arc.frame.x[i] * arc.radius), - std::array::from_fn(|i| arc.frame.origin[i] + arc.frame.y[i] * arc.radius), - std::array::from_fn(|i| arc.frame.origin[i] - arc.frame.y[i] * arc.radius), - ] { - let uv = frame.local(point); - for axis in 0..2 { - bounds[axis][0] = bounds[axis][0].min(uv[axis]); - bounds[axis][1] = bounds[axis][1].max(uv[axis]); - } - } - } - for bound in &mut bounds { - bound[0] -= tolerance; - bound[1] += tolerance; - } - bounds -} - -fn parallel_cylinder_boolean( - a: &CylinderInput<'_>, - b: &CylinderInput<'_>, - alignment: f64, - operation: BooleanOp, - id: String, - accuracy: Accuracy, -) -> Result { - let mut b_frame = a.frame; - b_frame.origin = if alignment > 0.0 { - b.frame.origin - } else { - b.frame.point([0.0, 0.0, b.height]) - }; - let delta = sub(b_frame.origin, a.frame.origin); - let axial = dot(delta, a.frame.z); - let radial = sub(delta, scale(a.frame.z, axial)); - let distance = norm(radial); - let sum = a.radius + b.radius; - let difference = (a.radius - b.radius).abs(); - let clearance = 4.0 * accuracy.geometric; - let relation_roundoff = 128.0 - * f64::EPSILON - * distance.max(sum).max(a.height).max( - a.frame - .origin - .into_iter() - .chain(b_frame.origin) - .fold(1.0_f64, |largest, value| largest.max(value.abs())), - ); - if (distance - sum).abs() <= clearance { - if (distance - sum).abs() <= relation_roundoff { - let toward_b = unit(radial)?; - let bottom: Point3 = - std::array::from_fn(|axis| a.frame.origin[axis] + toward_b[axis] * a.radius); - let top: Point3 = std::array::from_fn(|axis| bottom[axis] + a.frame.z[axis] * a.height); - return separate_boolean(a.brep, b.brep, operation, id, vec![bottom, top]); - } - return Err(GeometryError::UnresolvedIntersection( - "parallel cylinders are nearly externally tangent".into(), - )); - } - if (distance - difference).abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "parallel cylinders are internally tangent or nearly tangent".into(), - )); - } - if distance <= difference { - let b_inside_a = b.radius < a.radius; - if operation != BooleanOp::Subtraction || !b_inside_a { - return contained_cylinders(a, b, b_inside_a, operation, id, accuracy); - } - let mut out = primitives::cylinder_with_circular_hole( - id, a.frame, b_frame, b.radius, a.radius, a.height, accuracy, - )?; - out.topology.faces[0].provenance = - face_provenance(vec![cylinder_source(a, 0)], FaceRole::Preserved, false); - out.topology.faces[1].provenance = - face_provenance(vec![cylinder_source(b, 0)], FaceRole::Cut, true); - for (face, source) in [(2, cylinder_source(a, 1)), (3, cylinder_source(a, 2))] { - out.topology.faces[face].provenance = - face_provenance(vec![source], FaceRole::Split, false); - } - out.revision = a - .brep - .revision - .max(b.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let face_mappings = cylinder_face_mappings(&out, [a, b]); - return Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: true, - face_mappings, - }, - }); - } - if distance >= sum { - return separate_boolean(a.brep, b.brep, operation, id, Vec::new()); - } - - let toward_b = unit(radial)?; - let lateral = unit(cross(a.frame.z, toward_b))?; - let along = - (distance * distance + a.radius * a.radius - b.radius * b.radius) / (2.0 * distance); - let half_squared = (a.radius - along) * (a.radius + along); - if half_squared <= clearance * clearance { - return Err(GeometryError::UnresolvedIntersection( - "parallel cylinder intersection chord is below geometric resolution".into(), - )); - } - let half = half_squared.sqrt(); - let chord_center: Point3 = - std::array::from_fn(|axis| a.frame.origin[axis] + toward_b[axis] * along); - let points = [ - std::array::from_fn(|axis| chord_center[axis] + lateral[axis] * half), - std::array::from_fn(|axis| chord_center[axis] - lateral[axis] * half), - ]; - let (a_inside, b_inside, reverse_b) = match operation { - BooleanOp::Union => (false, false, false), - BooleanOp::Intersection => (true, true, false), - BooleanOp::Subtraction => (false, true, true), - }; - let a_arc = selected_cylinder_arc( - a.frame, - a.radius, - points, - b_frame.origin, - b.radius, - a_inside, - false, - )?; - let b_arc = selected_cylinder_arc( - b_frame, - b.radius, - points, - a.frame.origin, - a.radius, - b_inside, - reverse_b, - )?; - - let mut builder = Builder::new(id, accuracy)?; - let bottom_vertices = [builder.vertex(points[0]), builder.vertex(points[1])]; - let top_points = - points.map(|point| std::array::from_fn(|axis| point[axis] + a.frame.z[axis] * a.height)); - let top_vertices = [builder.vertex(top_points[0]), builder.vertex(top_points[1])]; - let vertices = [bottom_vertices, top_vertices]; - let vertical_range = Interval::new(0.0, a.height)?; - let vertical_edges = [0, 1].map(|point| { - builder.edge( - CurveGeometry::Line { - origin: points[point], - direction: a.frame.z, - }, - vertical_range, - false, - ) - }); - let add_arc_edges = - |builder: &mut Builder, arc: CylinderArc| -> Result<[u32; 2], GeometryError> { - let mut top_frame = arc.frame; - top_frame.origin = - std::array::from_fn(|axis| arc.frame.origin[axis] + a.frame.z[axis] * a.height); - Ok([ - builder.edge( - CurveGeometry::Circle { - frame: arc.frame, - radius: arc.radius, - }, - arc.range, - false, - ), - builder.edge( - CurveGeometry::Circle { - frame: top_frame, - radius: arc.radius, - }, - arc.range, - false, - ), - ]) - }; - let a_edges = add_arc_edges(&mut builder, a_arc)?; - let b_edges = add_arc_edges(&mut builder, b_arc)?; - add_cylinder_arc_face( - &mut builder, - "a:lateral", - a_arc, - a_edges[0], - a_edges[1], - vertical_edges, - vertices, - a.height, - )?; - add_cylinder_arc_face( - &mut builder, - "b:lateral", - b_arc, - b_edges[0], - b_edges[1], - vertical_edges, - vertices, - a.height, - )?; - - let mut lower_frame = a.frame; - lower_frame.y = scale(lower_frame.y, -1.0); - lower_frame.z = scale(lower_frame.z, -1.0); - let mut upper_frame = a.frame; - upper_frame.origin = a.frame.point([0.0, 0.0, a.height]); - let mut planar_bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - for point in [ - a.frame.origin, - b_frame.origin, - points[0], - points[1], - std::array::from_fn(|axis| a.frame.origin[axis] + a.frame.x[axis] * a.radius), - std::array::from_fn(|axis| a.frame.origin[axis] - a.frame.x[axis] * a.radius), - std::array::from_fn(|axis| a.frame.origin[axis] + a.frame.y[axis] * a.radius), - std::array::from_fn(|axis| a.frame.origin[axis] - a.frame.y[axis] * a.radius), - std::array::from_fn(|axis| b_frame.origin[axis] + a.frame.x[axis] * b.radius), - std::array::from_fn(|axis| b_frame.origin[axis] - a.frame.x[axis] * b.radius), - std::array::from_fn(|axis| b_frame.origin[axis] + a.frame.y[axis] * b.radius), - std::array::from_fn(|axis| b_frame.origin[axis] - a.frame.y[axis] * b.radius), - ] { - let uv = upper_frame.local(point); - for axis in 0..2 { - planar_bounds[axis][0] = planar_bounds[axis][0].min(uv[axis]); - planar_bounds[axis][1] = planar_bounds[axis][1].max(uv[axis]); - } - } - for bound in &mut planar_bounds { - bound[0] -= accuracy.geometric; - bound[1] += accuracy.geometric; - } - let cap_uses = |builder: &Builder, - frame: Frame3, - top: bool| - -> Result, GeometryError> { - let vertex_set = if top { top_vertices } else { bottom_vertices }; - let edge_index = usize::from(top); - let mut uses = Vec::with_capacity(2); - for (arc, edges) in [(a_arc, a_edges), (b_arc, b_edges)] { - let sense = match (top, arc.reversed) { - (false, false) => Orientation::Reverse, - (false, true) => Orientation::Forward, - (true, false) => Orientation::Forward, - (true, true) => Orientation::Reverse, - }; - let (from, to) = if sense == Orientation::Forward { - (vertex_set[arc.start], vertex_set[arc.end]) - } else { - (vertex_set[arc.end], vertex_set[arc.start]) - }; - uses.push(plane_boundary( - builder, - frame, - edges[edge_index], - from, - to, - sense, - )?); - } - Ok(uses) - }; - let lower_uses = cap_uses(&builder, lower_frame, false)?; - builder.face( - "lower_cap", - SurfaceGeometry::Plane { frame: lower_frame }, - planar_bounds, - lower_uses, - )?; - let upper_uses = cap_uses(&builder, upper_frame, true)?; - builder.face( - "upper_cap", - SurfaceGeometry::Plane { frame: upper_frame }, - planar_bounds, - upper_uses, - )?; - let mut out = builder.finish()?; - out.topology.faces[0].provenance = - face_provenance(vec![cylinder_source(a, 0)], FaceRole::Split, false); - out.topology.faces[1].provenance = face_provenance( - vec![cylinder_source(b, 0)], - if reverse_b { - FaceRole::Cut - } else { - FaceRole::Split - }, - reverse_b, - ); - let a_caps = [cylinder_source(a, 1), cylinder_source(a, 2)]; - let b_caps = if alignment > 0.0 { - [cylinder_source(b, 1), cylinder_source(b, 2)] - } else { - [cylinder_source(b, 2), cylinder_source(b, 1)] - }; - for (face, cap) in [(2, 0), (3, 1)] { - let sources = if operation == BooleanOp::Subtraction { - vec![a_caps[cap].clone()] - } else { - vec![a_caps[cap].clone(), b_caps[cap].clone()] - }; - out.topology.faces[face].provenance = face_provenance(sources, FaceRole::Split, false); - } - out.revision = a - .brep - .revision - .max(b.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let face_mappings = cylinder_face_mappings(&out, [a, b]); - Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: true, - face_mappings, - }, - }) -} - -fn coextensive_cylinder_radii( - a: &CylinderInput<'_>, - b: &CylinderInput<'_>, - alignment: f64, - operation: BooleanOp, - id: String, - accuracy: Accuracy, -) -> Result { - let a_lateral = cylinder_source(a, 0); - let b_lateral = cylinder_source(b, 0); - let a_caps = [cylinder_source(a, 1), cylinder_source(a, 2)]; - let b_caps = if alignment > 0.0 { - [cylinder_source(b, 1), cylinder_source(b, 2)] - } else { - [cylinder_source(b, 2), cylinder_source(b, 1)] - }; - let a_is_larger = a.radius > b.radius; - let mut out = BrepEnvelope::new(id, accuracy)?; - match operation { - BooleanOp::Union | BooleanOp::Intersection => { - let choose_a = if operation == BooleanOp::Union { - a_is_larger - } else { - !a_is_larger - }; - let (radius, lateral) = if choose_a { - (a.radius, a_lateral.clone()) - } else { - (b.radius, b_lateral.clone()) - }; - let part = primitives::cylinder( - format!("{}:radial", out.id), - a.frame, - radius, - a.height, - accuracy, - )?; - append_analytic_input(&mut out, &part)?; - out.topology.faces[0].provenance = - face_provenance(vec![lateral], FaceRole::Preserved, false); - for (face, sources) in [ - (1, vec![a_caps[0].clone(), b_caps[0].clone()]), - (2, vec![a_caps[1].clone(), b_caps[1].clone()]), - ] { - out.topology.faces[face].provenance = - face_provenance(sources, FaceRole::Split, false); - } - } - BooleanOp::Subtraction if a_is_larger => { - append_annular_cylinder( - &mut out, - a.frame, - b.radius, - a.radius, - a.height, - [ - face_provenance(vec![a_lateral], FaceRole::Preserved, false), - face_provenance(vec![b_lateral], FaceRole::Cut, true), - face_provenance(vec![a_caps[0].clone()], FaceRole::Split, false), - face_provenance(vec![a_caps[1].clone()], FaceRole::Split, false), - ], - )?; - } - BooleanOp::Subtraction => {} - } - out.revision = a - .brep - .revision - .max(b.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let face_mappings = cylinder_face_mappings(&out, [a, b]); - Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: true, - face_mappings, - }, - }) -} - -fn contained_cylinders( - a: &CylinderInput<'_>, - b: &CylinderInput<'_>, - b_inside_a: bool, - operation: BooleanOp, - id: String, - accuracy: Accuracy, -) -> Result { - let mut out = BrepEnvelope::new(id, accuracy)?; - let (contained, enclosing) = if b_inside_a { (b, a) } else { (a, b) }; - match operation { - BooleanOp::Union => { - append_analytic_input(&mut out, enclosing.brep)?; - } - BooleanOp::Intersection => { - append_analytic_input(&mut out, contained.brep)?; - } - BooleanOp::Subtraction if b_inside_a => { - append_analytic_input(&mut out, a.brep)?; - let face_offset = out.topology.faces.len() as u32; - append_analytic_input(&mut out, b.brep)?; - let cavity = out - .solids - .pop() - .ok_or_else(|| GeometryError::InvalidTopology("missing cutter solid".into()))? - .outer_shell; - out.solids[0].cavity_shells.push(cavity); - for face in face_offset..out.topology.faces.len() as u32 { - reverse_face(&mut out, face); - out.topology.faces[face as usize].provenance.role = FaceRole::Cut; - out.topology.faces[face as usize].provenance.reversed = true; - } - } - BooleanOp::Subtraction => {} - } - out.revision = a - .brep - .revision - .max(b.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let face_mappings = cylinder_face_mappings(&out, [a, b]); - Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings, - }, - }) -} - -fn embedded_transverse_cylinder_boolean( - a: &CylinderInput<'_>, - b: &CylinderInput<'_>, - alignment: f64, - radial: Point3, - b_span: [f64; 2], - operation: BooleanOp, - id: String, - accuracy: Accuracy, -) -> Result { - if !matches!(operation, BooleanOp::Union | BooleanOp::Subtraction) { - return Err(GeometryError::InvalidGeometry( - "embedded transverse cylinder builder requires union or subtraction".into(), - )); - } - let axis = a.frame.z; - let distance = norm(radial); - let toward_b = unit(radial)?; - let lateral = unit(cross(axis, toward_b))?; - let along = - (distance * distance + a.radius * a.radius - b.radius * b.radius) / (2.0 * distance); - let half_squared = (a.radius - along) * (a.radius + along); - let clearance = 4.0 * accuracy.geometric; - if half_squared <= clearance * clearance { - return Err(GeometryError::UnresolvedIntersection( - "embedded transverse cylinder chord is below geometric resolution".into(), - )); - } - let half = half_squared.sqrt(); - let chord_center: Point3 = std::array::from_fn(|i| a.frame.origin[i] + toward_b[i] * along); - let points = [ - std::array::from_fn(|i| chord_center[i] + lateral[i] * half), - std::array::from_fn(|i| chord_center[i] - lateral[i] * half), - ]; - let mut b_base_frame = a.frame; - b_base_frame.origin = std::array::from_fn(|i| a.frame.origin[i] + radial[i]); - let a_outer = selected_cylinder_arc( - a.frame, - a.radius, - points, - b_base_frame.origin, - b.radius, - false, - false, - )?; - let a_inner = selected_cylinder_arc( - a.frame, - a.radius, - points, - b_base_frame.origin, - b.radius, - true, - false, - )?; - let b_boundary = selected_cylinder_arc( - b_base_frame, - b.radius, - points, - a.frame.origin, - a.radius, - operation == BooleanOp::Subtraction, - operation == BooleanOp::Subtraction, - )?; - let levels = [0.0, b_span[0], b_span[1], a.height]; - if levels.windows(2).any(|span| span[1] - span[0] <= clearance) { - return Err(GeometryError::UnresolvedIntersection( - "embedded transverse cylinder leaves a sub-tolerance axial segment".into(), - )); - } - - let mut builder = Builder::new(id, accuracy)?; - let vertices: [[u32; 2]; 4] = std::array::from_fn(|level| { - points.map(|point| { - builder.vertex(std::array::from_fn(|i| point[i] + axis[i] * levels[level])) - }) - }); - let vertical_ranges = [ - Interval::new(0.0, levels[1] - levels[0])?, - Interval::new(0.0, levels[2] - levels[1])?, - Interval::new(0.0, levels[3] - levels[2])?, - ]; - let vertical_edges: [[u32; 2]; 3] = std::array::from_fn(|segment| { - [0, 1].map(|point| { - builder.edge( - CurveGeometry::Line { - origin: std::array::from_fn(|i| points[point][i] + axis[i] * levels[segment]), - direction: axis, - }, - vertical_ranges[segment], - false, - ) - }) - }); - let mut arc_edge = |arc: CylinderArc, level: usize, seam: bool| { - let shifted = shifted_cylinder_arc(arc, axis, levels[level]); - builder.edge( - CurveGeometry::Circle { - frame: shifted.frame, - radius: shifted.radius, - }, - shifted.range, - seam, - ) - }; - let a_outer_edges = [ - arc_edge(a_outer, 0, false), - arc_edge(a_outer, 1, true), - arc_edge(a_outer, 2, true), - arc_edge(a_outer, 3, false), - ]; - let a_inner_edges = [ - arc_edge(a_inner, 0, false), - arc_edge(a_inner, 1, false), - arc_edge(a_inner, 2, false), - arc_edge(a_inner, 3, false), - ]; - let b_edges = [ - arc_edge(b_boundary, 1, false), - arc_edge(b_boundary, 2, false), - ]; - drop(arc_edge); - - for segment in 0..3 { - let height = levels[segment + 1] - levels[segment]; - add_cylinder_arc_face( - &mut builder, - &format!("a:outer:{segment}"), - shifted_cylinder_arc(a_outer, axis, levels[segment]), - a_outer_edges[segment], - a_outer_edges[segment + 1], - vertical_edges[segment], - [vertices[segment], vertices[segment + 1]], - height, - )?; - let (arc, edges, key) = if segment == 1 { - (b_boundary, b_edges, "b:boundary") - } else { - ( - a_inner, - [a_inner_edges[segment], a_inner_edges[segment + 1]], - if segment == 0 { - "a:inner:lower" - } else { - "a:inner:upper" - }, - ) - }; - add_cylinder_arc_face( - &mut builder, - key, - shifted_cylinder_arc(arc, axis, levels[segment]), - edges[0], - edges[1], - vertical_edges[segment], - [vertices[segment], vertices[segment + 1]], - height, - )?; - } - - let mut bottom_frame = a.frame; - bottom_frame.y = scale(bottom_frame.y, -1.0); - bottom_frame.z = scale(bottom_frame.z, -1.0); - let bottom_arcs = [ - shifted_cylinder_arc(a_outer, axis, levels[0]), - shifted_cylinder_arc(a_inner, axis, levels[0]), - ]; - let bottom_uses = two_arc_plane_uses( - &builder, - bottom_frame, - (bottom_arcs[0], a_outer_edges[0]), - (bottom_arcs[1], a_inner_edges[0]), - vertices[0], - )?; - builder.face( - "a:lower-cap", - SurfaceGeometry::Plane { - frame: bottom_frame, - }, - two_arc_plane_bounds(bottom_frame, bottom_arcs, accuracy.geometric), - bottom_uses, - )?; - - for (level, key) in [(1, "b:lower-interface"), (2, "b:upper-interface")] { - let outward_positive = (operation == BooleanOp::Union && level == 2) - || (operation == BooleanOp::Subtraction && level == 1); - let mut frame = a.frame; - frame.origin = a.frame.point([0.0, 0.0, levels[level]]); - if !outward_positive { - frame.y = scale(frame.y, -1.0); - frame.z = scale(frame.z, -1.0); - } - let arcs = [ - shifted_cylinder_arc(a_inner, axis, levels[level]), - shifted_cylinder_arc(b_boundary, axis, levels[level]), - ]; - let edges = if level == 1 { - [a_inner_edges[1], b_edges[0]] - } else { - [a_inner_edges[2], b_edges[1]] - }; - let uses = two_arc_plane_uses( - &builder, - frame, - (arcs[0], edges[0]), - (arcs[1], edges[1]), - vertices[level], - )?; - builder.face( - key, - SurfaceGeometry::Plane { frame }, - two_arc_plane_bounds(frame, arcs, accuracy.geometric), - uses, - )?; - } - - let mut top_frame = a.frame; - top_frame.origin = a.frame.point([0.0, 0.0, a.height]); - let top_arcs = [ - shifted_cylinder_arc(a_outer, axis, levels[3]), - shifted_cylinder_arc(a_inner, axis, levels[3]), - ]; - let top_uses = two_arc_plane_uses( - &builder, - top_frame, - (top_arcs[0], a_outer_edges[3]), - (top_arcs[1], a_inner_edges[3]), - vertices[3], - )?; - builder.face( - "a:upper-cap", - SurfaceGeometry::Plane { frame: top_frame }, - two_arc_plane_bounds(top_frame, top_arcs, accuracy.geometric), - top_uses, - )?; - - let mut out = builder.finish()?; - let a_lateral = cylinder_source(a, 0); - let b_lateral = cylinder_source(b, 0); - for face in [0, 1, 2, 4, 5] { - out.topology.faces[face].provenance = - face_provenance(vec![a_lateral.clone()], FaceRole::Split, false); - } - out.topology.faces[3].provenance = face_provenance( - vec![b_lateral], - if operation == BooleanOp::Subtraction { - FaceRole::Cut - } else { - FaceRole::Split - }, - operation == BooleanOp::Subtraction, - ); - let a_caps = [cylinder_source(a, 1), cylinder_source(a, 2)]; - out.topology.faces[6].provenance = - face_provenance(vec![a_caps[0].clone()], FaceRole::Preserved, false); - out.topology.faces[9].provenance = - face_provenance(vec![a_caps[1].clone()], FaceRole::Preserved, false); - let b_caps = if alignment > 0.0 { - [cylinder_source(b, 1), cylinder_source(b, 2)] - } else { - [cylinder_source(b, 2), cylinder_source(b, 1)] - }; - for (face, source) in [(7, b_caps[0].clone()), (8, b_caps[1].clone())] { - out.topology.faces[face].provenance = face_provenance( - vec![source], - if operation == BooleanOp::Subtraction { - FaceRole::Cut - } else { - FaceRole::Split - }, - operation == BooleanOp::Subtraction, - ); - } - out.revision = a - .brep - .revision - .max(b.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let face_mappings = cylinder_face_mappings(&out, [a, b]); - Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings, - }, - }) -} - -fn sliced_parallel_cylinder_boolean( - a: &CylinderInput<'_>, - b: &CylinderInput<'_>, - alignment: f64, - radial: Point3, - a_span: [f64; 2], - b_span: [f64; 2], - span: [f64; 2], - operation: BooleanOp, - id: String, - accuracy: Accuracy, - roundoff: f64, -) -> Result { - let mut a_frame = a.frame; - a_frame.origin = a.frame.point([0.0, 0.0, span[0]]); - let mut b_frame = a_frame; - b_frame.origin = std::array::from_fn(|axis| a_frame.origin[axis] + radial[axis]); - let height = span[1] - span[0]; - let a_slice = CylinderInput { - brep: a.brep, - frame: a_frame, - radius: a.radius, - height, - }; - let b_slice = CylinderInput { - brep: b.brep, - frame: b_frame, - radius: b.radius, - height, - }; - let mut result = parallel_cylinder_boolean(&a_slice, &b_slice, 1.0, operation, id, accuracy)?; - result.report.coincident = false; - if operation == BooleanOp::Intersection && result.brep.topology.faces.len() == 4 { - let a_caps = [cylinder_source(a, 1), cylinder_source(a, 2)]; - let b_caps = if alignment > 0.0 { - [cylinder_source(b, 1), cylinder_source(b, 2)] - } else { - [cylinder_source(b, 2), cylinder_source(b, 1)] - }; - for (face, boundary, end) in [(2, span[0], 0), (3, span[1], 1)] { - let mut sources = Vec::with_capacity(2); - if (boundary - a_span[end]).abs() <= roundoff { - sources.push(a_caps[end].clone()); - } - if (boundary - b_span[end]).abs() <= roundoff { - sources.push(b_caps[end].clone()); - } - if sources.len() > 1 { - result.report.coincident = true; - } - if sources.is_empty() { - return Err(GeometryError::InvalidTopology( - "parallel cylinder slice cap has no source face".into(), - )); - } - result.brep.topology.faces[face].provenance = - face_provenance(sources, FaceRole::Split, false); - } - result.report.face_mappings = cylinder_face_mappings(&result.brep, [a, b]); - result.brep.validate()?; - } else if operation == BooleanOp::Subtraction { - result.report.coincident = - (a_span[0] - b_span[0]).abs() <= roundoff || (a_span[1] - b_span[1]).abs() <= roundoff; - } - Ok(result) -} - -fn boolean_cylinders( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let a = full_cylinder(a)?; - let b = full_cylinder(b)?; - let accuracy = Accuracy { - geometric: a.brep.accuracy.geometric.max(b.brep.accuracy.geometric), - intersection: a - .brep - .accuracy - .intersection - .max(b.brep.accuracy.intersection), - tessellation: a - .brep - .accuracy - .tessellation - .max(b.brep.accuracy.tessellation), - exchange: a.brep.accuracy.exchange.max(b.brep.accuracy.exchange), - }; - let alignment = dot(a.frame.z, b.frame.z); - if 1.0 - alignment.abs() > 1e-12 { - if operation == BooleanOp::Subtraction { - return transverse_cylinder_through_subtraction(&a, &b, id, accuracy); - } - return Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "nonparallel cylinder".into()], - }); - } - let delta = sub(b.frame.origin, a.frame.origin); - let axial = dot(delta, a.frame.z); - let radial = sub(delta, scale(a.frame.z, axial)); - let a_span = [0.0, a.height]; - let b_end = axial + alignment.signum() * b.height; - let b_span = [axial.min(b_end), axial.max(b_end)]; - let span_error = (a_span[0] - b_span[0]) - .abs() - .max((a_span[1] - b_span[1]).abs()); - let scale = a - .frame - .origin - .into_iter() - .chain(b.frame.origin) - .chain([a.radius, b.radius, a.height, b.height]) - .fold(1.0_f64, |largest, value| largest.max(value.abs())); - let roundoff = 128.0 * f64::EPSILON * scale; - let radial_distance = norm(radial); - if radial_distance > roundoff { - if radial_distance <= accuracy.intersection { - return Err(GeometryError::UnresolvedIntersection( - "cylinder axes differ within the intersection budget".into(), - )); - } - if span_error <= roundoff { - return parallel_cylinder_boolean(&a, &b, alignment, operation, id, accuracy); - } - let clearance = 4.0 * accuracy.geometric; - let overlap_span = [a_span[0].max(b_span[0]), a_span[1].min(b_span[1])]; - let overlap = overlap_span[1] - overlap_span[0]; - if overlap.abs() <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "parallel cylinder axial contact is below geometric resolution".into(), - )); - } - if overlap < 0.0 { - return separate_boolean(a.brep, b.brep, operation, id, Vec::new()); - } - if operation == BooleanOp::Intersection { - return sliced_parallel_cylinder_boolean( - &a, - &b, - alignment, - radial, - a_span, - b_span, - overlap_span, - operation, - id, - accuracy, - roundoff, - ); - } - if operation == BooleanOp::Subtraction { - let b_covers_a = b_span[0] <= a_span[0] + roundoff && b_span[1] >= a_span[1] - roundoff; - if b_covers_a { - return sliced_parallel_cylinder_boolean( - &a, &b, alignment, radial, a_span, b_span, a_span, operation, id, accuracy, - roundoff, - ); - } - let b_nearly_covers_a = - b_span[0] <= a_span[0] + clearance && b_span[1] >= a_span[1] - clearance; - if b_nearly_covers_a { - return Err(GeometryError::UnresolvedIntersection( - "parallel cylinder subtraction leaves a sub-tolerance axial segment".into(), - )); - } - } - let b_embedded_in_a = - b_span[0] > a_span[0] + clearance && b_span[1] < a_span[1] - clearance; - let transverse_overlap = radial_distance > (a.radius - b.radius).abs() + clearance - && radial_distance < a.radius + b.radius - clearance; - if b_embedded_in_a - && transverse_overlap - && matches!(operation, BooleanOp::Union | BooleanOp::Subtraction) - { - return embedded_transverse_cylinder_boolean( - &a, &b, alignment, radial, b_span, operation, id, accuracy, - ); - } - let a_embedded_in_b = - a_span[0] > b_span[0] + clearance && a_span[1] < b_span[1] - clearance; - if a_embedded_in_b && transverse_overlap && operation == BooleanOp::Union { - return boolean_cylinders(b.brep, a.brep, operation, id); - } - return Err(GeometryError::CoverageGap { - families: [ - "parallel cylinder".into(), - "non-coextensive parallel cylinder".into(), - ], - }); - } - if a.radius != b.radius { - if (a.radius - b.radius).abs() <= accuracy.intersection { - return Err(GeometryError::UnresolvedIntersection( - "cylinder radii differ within the intersection budget".into(), - )); - } - if span_error <= roundoff { - return coextensive_cylinder_radii(&a, &b, alignment, operation, id, accuracy); - } - let clearance = 4.0 * accuracy.geometric; - let b_inside_a = b.radius + clearance < a.radius - && b_span[0] > a_span[0] + clearance - && b_span[1] < a_span[1] - clearance; - let a_inside_b = a.radius + clearance < b.radius - && a_span[0] > b_span[0] + clearance - && a_span[1] < b_span[1] - clearance; - if b_inside_a || a_inside_b { - return contained_cylinders(&a, &b, b_inside_a, operation, id, accuracy); - } - let near_radial_contact = (a.radius - b.radius).abs() <= clearance; - let near_axial_contact = [ - (a_span[0] - b_span[0]).abs(), - (a_span[0] - b_span[1]).abs(), - (a_span[1] - b_span[0]).abs(), - (a_span[1] - b_span[1]).abs(), - ] - .into_iter() - .any(|gap| gap <= clearance); - if near_radial_contact || near_axial_contact { - return Err(GeometryError::UnresolvedIntersection( - "cylinder containment boundary is below geometric resolution".into(), - )); - } - if span_error <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "cylinder axial spans differ below geometric resolution".into(), - )); - } - return Err(GeometryError::CoverageGap { - families: [ - "cylinder".into(), - "different-radius coaxial cylinder with unequal axial spans".into(), - ], - }); - } - let overlap = a_span[1].min(b_span[1]) - a_span[0].max(b_span[0]); - if overlap != 0.0 && overlap.abs() <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "cylinder axial overlap or clearance is below geometric resolution".into(), - )); - } - - let a_lateral = cylinder_source(&a, 0); - let b_lateral = cylinder_source(&b, 0); - let a_caps = [cylinder_source(&a, 1), cylinder_source(&a, 2)]; - let b_caps = if alignment > 0.0 { - [cylinder_source(&b, 1), cylinder_source(&b, 2)] - } else { - [cylinder_source(&b, 2), cylinder_source(&b, 1)] - }; - let cap_at = |boundary: f64, low: bool, include_a: bool, include_b: bool| { - let mut sources = Vec::new(); - if include_a && boundary == if low { a_span[0] } else { a_span[1] } { - sources.push(a_caps[usize::from(!low)].clone()); - } - if include_b && boundary == if low { b_span[0] } else { b_span[1] } { - sources.push(b_caps[usize::from(!low)].clone()); - } - sources - }; - - struct Segment { - lo: f64, - hi: f64, - provenances: [FaceProvenance; 3], - } - let mut segments = Vec::new(); - let coincident = overlap >= 0.0; - match operation { - BooleanOp::Union if overlap >= 0.0 => { - let lo = a_span[0].min(b_span[0]); - let hi = a_span[1].max(b_span[1]); - let lower = cap_at(lo, true, true, true); - let upper = cap_at(hi, false, true, true); - segments.push(Segment { - lo, - hi, - provenances: [ - face_provenance( - vec![a_lateral.clone(), b_lateral.clone()], - FaceRole::Coincident, - false, - ), - face_provenance( - lower.clone(), - if lower.len() > 1 { - FaceRole::Coincident - } else { - FaceRole::Preserved - }, - false, - ), - face_provenance( - upper.clone(), - if upper.len() > 1 { - FaceRole::Coincident - } else { - FaceRole::Preserved - }, - false, - ), - ], - }); - } - BooleanOp::Union => { - for (span, lateral, caps) in [ - (a_span, a_lateral.clone(), a_caps.clone()), - (b_span, b_lateral.clone(), b_caps.clone()), - ] { - segments.push(Segment { - lo: span[0], - hi: span[1], - provenances: [ - face_provenance(vec![lateral], FaceRole::Preserved, false), - face_provenance(vec![caps[0].clone()], FaceRole::Preserved, false), - face_provenance(vec![caps[1].clone()], FaceRole::Preserved, false), - ], - }); - } - } - BooleanOp::Intersection if overlap > 0.0 => { - let lo = a_span[0].max(b_span[0]); - let hi = a_span[1].min(b_span[1]); - let lower = cap_at(lo, true, lo == a_span[0], lo == b_span[0]); - let upper = cap_at(hi, false, hi == a_span[1], hi == b_span[1]); - segments.push(Segment { - lo, - hi, - provenances: [ - face_provenance( - vec![a_lateral.clone(), b_lateral.clone()], - FaceRole::Coincident, - false, - ), - face_provenance( - lower.clone(), - if lower.len() > 1 { - FaceRole::Coincident - } else { - FaceRole::Preserved - }, - false, - ), - face_provenance( - upper.clone(), - if upper.len() > 1 { - FaceRole::Coincident - } else { - FaceRole::Preserved - }, - false, - ), - ], - }); - } - BooleanOp::Intersection => {} - BooleanOp::Subtraction if overlap <= 0.0 => { - segments.push(Segment { - lo: a_span[0], - hi: a_span[1], - provenances: [ - face_provenance(vec![a_lateral.clone()], FaceRole::Preserved, false), - face_provenance(vec![a_caps[0].clone()], FaceRole::Preserved, false), - face_provenance(vec![a_caps[1].clone()], FaceRole::Preserved, false), - ], - }); - } - BooleanOp::Subtraction => { - if b_span[0] > a_span[0] { - segments.push(Segment { - lo: a_span[0], - hi: b_span[0].min(a_span[1]), - provenances: [ - face_provenance(vec![a_lateral.clone()], FaceRole::Split, false), - face_provenance(vec![a_caps[0].clone()], FaceRole::Preserved, false), - face_provenance(vec![b_caps[0].clone()], FaceRole::Cut, true), - ], - }); - } - if b_span[1] < a_span[1] { - segments.push(Segment { - lo: b_span[1].max(a_span[0]), - hi: a_span[1], - provenances: [ - face_provenance(vec![a_lateral.clone()], FaceRole::Split, false), - face_provenance(vec![b_caps[1].clone()], FaceRole::Cut, true), - face_provenance(vec![a_caps[1].clone()], FaceRole::Preserved, false), - ], - }); - } - } - } - - let mut out = BrepEnvelope::new(id, accuracy)?; - for (index, segment) in segments.into_iter().enumerate() { - let mut frame = a.frame; - frame.origin = a.frame.point([0.0, 0.0, segment.lo]); - append_cylinder_segment( - &mut out, - frame, - a.radius, - segment.hi - segment.lo, - segment.provenances, - index, - )?; - } - out.revision = a - .brep - .revision - .max(b.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let face_mappings = cylinder_face_mappings(&out, [&a, &b]); - Ok(BooleanResult { - brep: out, - report: BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident, - face_mappings, - }, - }) -} - -fn append_sphere( - out: &mut BrepEnvelope, - input: &SphereInput<'_>, - reversed: bool, -) -> Result { - let mut part = primitives::sphere( - input.brep.id.clone(), - input.frame, - input.radius, - out.accuracy, - )?; - part.topology.faces[0].provenance = provenance( - input, - if reversed { - FaceRole::Cut - } else { - FaceRole::Preserved - }, - reversed, - ); - if reversed { - reverse_face(&mut part, 0); - } - let [v, e, h, l, f, s, c, p, surface] = [ - out.topology.vertices.len(), - out.topology.edges.len(), - out.topology.halfedges.len(), - out.topology.loops.len(), - out.topology.faces.len(), - out.topology.shells.len(), - out.geometry.curves.len(), - out.geometry.pcurves.len(), - out.geometry.surfaces.len(), - ] - .map(|n| n as u32); - for vertex in &mut part.topology.vertices { - vertex.id += v; - vertex.outgoing_halfedge = vertex.outgoing_halfedge.map(|id| id + h); - } - for edge in &mut part.topology.edges { - edge.id += e; - edge.halfedge += h; - edge.twin_halfedge = edge.twin_halfedge.map(|id| id + h); - match &mut edge.geometry { - EdgeGeometry::Curve { curve, .. } => *curve += c, - EdgeGeometry::Collapsed { vertex } => *vertex += v, - } - } - for halfedge in &mut part.topology.halfedges { - halfedge.id += h; - halfedge.from += v; - halfedge.to += v; - halfedge.edge += e; - halfedge.twin = halfedge.twin.map(|id| id + h); - halfedge.next = halfedge.next.map(|id| id + h); - halfedge.prev = halfedge.prev.map(|id| id + h); - halfedge.face = halfedge.face.map(|id| id + f); - halfedge.loop_ref = halfedge.loop_ref.map(|id| id + l); - halfedge.geometry_use.pcurve = halfedge.geometry_use.pcurve.map(|id| id + p); - } - for loop_ in &mut part.topology.loops { - loop_.id += l; - loop_.start_halfedge += h; - loop_.face_ref += f; - } - for face in &mut part.topology.faces { - face.id += f; - face.key = format!("{f}:{}", face.key); - face.surface += surface; - face.trim.outer += l; - face.shell_ref = Some(s); - } - for shell in &mut part.topology.shells { - shell.id += s; - for face in &mut shell.faces { - *face += f; - } - } - out.geometry.surfaces.extend(part.geometry.surfaces); - out.geometry.curves.extend(part.geometry.curves); - out.geometry.pcurves.extend(part.geometry.pcurves); - out.topology.vertices.extend(part.topology.vertices); - out.topology.edges.extend(part.topology.edges); - out.topology.halfedges.extend(part.topology.halfedges); - out.topology.loops.extend(part.topology.loops); - out.topology.faces.extend(part.topology.faces); - out.topology.shells.extend(part.topology.shells); - Ok(s) -} - -fn patch( - builder: &mut Builder, - input: &SphereInput<'_>, - frame: Frame3, - latitude: f64, - north: bool, - shared_edge: u32, - shared_vertex: u32, - reversed: bool, -) -> Result<(), GeometryError> { - use Orientation::{Forward as F, Reverse as R}; - let pole_lat = if north { - std::f64::consts::FRAC_PI_2 - } else { - -std::f64::consts::FRAC_PI_2 - }; - let (lo, hi) = if north { - (latitude, pole_lat) - } else { - (pole_lat, latitude) - }; - let surface = SurfaceGeometry::Sphere { - frame, - radius: input.radius, - }; - let pole = builder.vertex(surface.point_at([0.0, pole_lat])?); - let collapse = builder.edge_geometry(EdgeGeometry::Collapsed { vertex: pole }, true); - let seam = builder.edge( - CurveGeometry::Circle { - frame: Frame3 { - x: frame.x, - y: frame.z, - z: scale(frame.y, -1.0), - ..frame - }, - radius: input.radius, - }, - Interval::new(lo, hi)?, - true, - ); - let tau = std::f64::consts::TAU; - let (low, high) = if north { - (shared_vertex, pole) - } else { - (pole, shared_vertex) - }; - let bottom = if north { - boundary(shared_edge, low, low, F, uv_line([0.0, lo], [1.0, 0.0])) - } else { - boundary(collapse, low, low, F, uv_line([0.0, lo], [tau, 0.0])) - }; - let top = if north { - boundary(collapse, high, high, F, uv_line([tau, hi], [-tau, 0.0])) - } else { - boundary(shared_edge, high, high, R, uv_line([0.0, hi], [1.0, 0.0])) - }; - let face = builder.brep.topology.faces.len() as u32; - builder.face( - &format!("{face}:{}", input.brep.topology.faces[0].key), - surface, - [[0.0, tau], [lo, hi]], - vec![ - bottom, - boundary(seam, low, high, F, uv_line([tau, 0.0], [0.0, 1.0])), - top, - boundary(seam, high, low, R, uv_line([0.0, 0.0], [0.0, 1.0])), - ], - )?; - builder.brep.topology.faces[face as usize].provenance = provenance( - input, - if reversed { - FaceRole::Cut - } else { - FaceRole::Split - }, - reversed, - ); - if reversed { - reverse_face(&mut builder.brep, face); - } - Ok(()) -} - -fn inset_prismatic_ring( - ring: &[Point3], - frame: Frame3, - thickness: f64, - tolerance: f64, -) -> Result, GeometryError> { - let points = ring - .iter() - .map(|point| { - let local = frame.local(*point); - Pt2::new(local[0], local[1]) - }) - .collect::>(); - if points.len() < 3 { - return Err(prismatic_gap()); - } - let cross2 = |a: Pt2, b: Pt2| a.x * b.z - a.z * b.x; - let direction = |from: Pt2, to: Pt2| -> Result { - let delta = Pt2::new(to.x - from.x, to.z - from.z); - let length = delta.x.hypot(delta.z); - if length <= 4.0 * tolerance { - return Err(GeometryError::UnresolvedIntersection( - "profile shell contains a sub-tolerance edge".into(), - )); - } - Ok(Pt2::new(delta.x / length, delta.z / length)) - }; - let mut result = Vec::with_capacity(points.len()); - for index in 0..points.len() { - let point = points[index]; - let incoming = direction(points[(index + points.len() - 1) % points.len()], point)?; - let outgoing = direction(point, points[(index + 1) % points.len()])?; - let incoming_normal = Pt2::new(-incoming.z, incoming.x); - let outgoing_normal = Pt2::new(-outgoing.z, outgoing.x); - let incoming_origin = Pt2::new( - point.x + thickness * incoming_normal.x, - point.z + thickness * incoming_normal.z, - ); - let outgoing_origin = Pt2::new( - point.x + thickness * outgoing_normal.x, - point.z + thickness * outgoing_normal.z, - ); - let determinant = cross2(incoming, outgoing); - let offset = if determinant.abs() <= 1.0e-12 { - if incoming.x * outgoing.x + incoming.z * outgoing.z < 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "profile shell has a reversing corner".into(), - )); - } - incoming_origin - } else { - let delta = Pt2::new( - outgoing_origin.x - incoming_origin.x, - outgoing_origin.z - incoming_origin.z, - ); - let parameter = cross2(delta, outgoing) / determinant; - Pt2::new( - incoming_origin.x + parameter * incoming.x, - incoming_origin.z + parameter * incoming.z, - ) - }; - if !offset.x.is_finite() - || !offset.z.is_finite() - || (offset.x - point.x).hypot(offset.z - point.z) > thickness * 1.0e6 - { - return Err(GeometryError::UnresolvedIntersection( - "profile shell has an unbounded miter".into(), - )); - } - result.push(offset); - } - let original_area = poly2d::signed_area2(&points); - let result_area = poly2d::signed_area2(&result); - if result_area.abs() <= tolerance * tolerance - || original_area.signum() != result_area.signum() - || poly2d::self_intersects2(&result, tolerance) - { - return Err(GeometryError::UnresolvedIntersection( - "profile shell offset collapses or self-intersects".into(), - )); - } - Ok(result.into_iter().map(|point| [point.x, point.z]).collect()) -} - -pub fn shell_brep( - input: &BrepEnvelope, - thickness: f64, - id: String, -) -> Result { - input.validate()?; - if !thickness.is_finite() || thickness <= 4.0 * input.accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "shell thickness is below geometric resolution".into(), - )); - } - let inner_id = format!("{id}:offset-cavity"); - let inner = match input.geometry.surfaces.first() { - Some(SurfaceGeometry::Sphere { .. }) => { - let input = full_sphere(input)?; - let radius = input.radius - thickness; - if radius <= 4.0 * input.brep.accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "shell thickness consumes the sphere interior".into(), - )); - } - primitives::sphere(inner_id, input.frame, radius, input.brep.accuracy)? - } - Some(SurfaceGeometry::Cylinder { .. }) => { - let input = full_cylinder(input)?; - let radius = input.radius - thickness; - let height = input.height - 2.0 * thickness; - if radius <= 4.0 * input.brep.accuracy.geometric - || height <= 4.0 * input.brep.accuracy.geometric - { - return Err(GeometryError::UnresolvedIntersection( - "shell thickness consumes the cylinder interior".into(), - )); - } - let mut frame = input.frame; - frame.origin = input.frame.point([0.0, 0.0, thickness]); - primitives::cylinder(inner_id, frame, radius, height, input.brep.accuracy)? - } - Some(SurfaceGeometry::Torus { .. }) => { - let input = full_torus(input)?; - let minor_radius = input.minor_radius - thickness; - if minor_radius <= 4.0 * input.brep.accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "shell thickness consumes the torus interior".into(), - )); - } - primitives::torus( - inner_id, - input.frame, - input.major_radius, - minor_radius, - input.brep.accuracy, - )? - } - Some(SurfaceGeometry::Plane { .. }) => match super::box_booleans::full_box(input) { - Ok(input) => { - let size = input.size.map(|dimension| dimension - 2.0 * thickness); - if size - .iter() - .any(|dimension| *dimension <= 4.0 * input.brep.accuracy.geometric) - { - return Err(GeometryError::UnresolvedIntersection( - "shell thickness consumes the cuboid interior".into(), - )); - } - let mut frame = input.frame; - frame.origin = input.frame.point([thickness, thickness, thickness]); - primitives::cuboid(inner_id, frame, size, input.brep.accuracy)? - } - Err(GeometryError::CoverageGap { .. }) => { - let input = full_planar_extrusion(input)?; - let height = input.height - 2.0 * thickness; - if height <= 4.0 * input.brep.accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "shell thickness consumes the profile extrusion height".into(), - )); - } - let inset = input - .contours - .iter() - .map(|ring| { - inset_prismatic_ring( - ring, - input.frame, - thickness, - input.brep.accuracy.geometric, - ) - }) - .collect::, _>>()?; - let mut frame = input.frame; - frame.origin = input.frame.point([0.0, 0.0, thickness]); - primitives::linear_extrusion( - inner_id, - frame, - inset[0].clone(), - inset[1..].to_vec(), - height, - input.brep.accuracy, - ) - .map_err(|error| match error { - GeometryError::InvalidGeometry(_) - | GeometryError::UnresolvedIntersection(_) => { - GeometryError::UnresolvedIntersection( - "shell offset self-intersects or consumes the profile interior".into(), - ) - } - other => other, - })? - } - Err(error) => return Err(error), - }, - Some(SurfaceGeometry::Cone { .. }) => { - let input = full_conic_section(input)?; - let slope = input.semi_angle.tan(); - let normal_scale = (1.0 + slope * slope).sqrt(); - if input.lower_radius <= input.brep.accuracy.geometric { - let apex = thickness * normal_scale / slope; - let base = input.axial_range.hi - thickness; - let height = base - apex; - let radius = height * slope; - if radius <= 4.0 * input.brep.accuracy.geometric - || height <= 4.0 * input.brep.accuracy.geometric - { - return Err(GeometryError::UnresolvedIntersection( - "shell thickness consumes the cone interior".into(), - )); - } - let base_frame = Frame3 { - origin: input.frame.point([0.0, 0.0, base]), - x: input.frame.x, - y: scale(input.frame.y, -1.0), - z: scale(input.frame.z, -1.0), - }; - primitives::cone(inner_id, base_frame, radius, height, input.brep.accuracy)? - } else { - let height = input.axial_range.width() - 2.0 * thickness; - let lower_radius = - input.lower_radius + thickness * slope - thickness * normal_scale; - let upper_radius = - input.upper_radius - thickness * slope - thickness * normal_scale; - if height <= 4.0 * input.brep.accuracy.geometric - || lower_radius <= 4.0 * input.brep.accuracy.geometric - || upper_radius <= 4.0 * input.brep.accuracy.geometric - { - return Err(GeometryError::UnresolvedIntersection( - "shell thickness consumes the frustum interior".into(), - )); - } - let frame = Frame3 { - origin: input - .frame - .point([0.0, 0.0, input.axial_range.lo + thickness]), - ..input.frame - }; - primitives::frustum( - inner_id, - frame, - lower_radius, - upper_radius, - height, - input.brep.accuracy, - )? - } - } - None => { - return Err(GeometryError::CoverageGap { - families: ["restricted analytic shell".into(), "input solid".into()], - }) - } - }; - let inner_identity = inner.id.clone(); - let mut result = analytic_containment_boolean(input, &inner, true, BooleanOp::Subtraction, id)?; - for face in &mut result.brep.topology.faces { - for source in &mut face.provenance.sources { - if source.entity == inner_identity && source.body == inner_identity { - let original = input - .topology - .faces - .get(source.face as usize) - .ok_or_else(|| { - GeometryError::InvalidTopology( - "shell offset face has no source face".into(), - ) - })?; - *source = brep_face_source(input, original.id); - } - } - face.provenance.sources = unique_sources(std::mem::take(&mut face.provenance.sources)); - } - result.report.face_mappings = input - .topology - .faces - .iter() - .map(|face| { - let source = brep_face_source(input, face.id); - let result_faces = result - .brep - .topology - .faces - .iter() - .filter(|candidate| { - candidate.provenance.sources.iter().any(|candidate| { - candidate.entity == source.entity - && candidate.body == source.body - && candidate.key == source.key - && candidate.face == source.face - }) - }) - .map(|candidate| candidate.id) - .collect(); - FaceMapping { - source, - result_faces, - } - }) - .collect(); - result.brep.validate()?; - Ok(result) -} - -pub fn boolean_brep( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - a.validate()?; - b.validate()?; - if comparable(&a.geometry)? == comparable(&b.geometry)? - && comparable(&a.topology)? == comparable(&b.topology)? - && comparable(&a.solids)? == comparable(&b.solids)? - { - return coincident_boolean(a, b, operation, id); - } - if let Some(result) = disjoint_boolean(a, b, operation, id.clone())? { - return Ok(result); - } - let a_is_sphere = matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Sphere { .. }) - ); - let b_is_sphere = matches!( - b.geometry.surfaces.first(), - Some(SurfaceGeometry::Sphere { .. }) - ); - let a_is_cylinder = matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Cylinder { .. }) - ); - let b_is_cylinder = matches!( - b.geometry.surfaces.first(), - Some(SurfaceGeometry::Cylinder { .. }) - ); - let a_is_torus = matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Torus { .. }) - ); - let b_is_torus = matches!( - b.geometry.surfaces.first(), - Some(SurfaceGeometry::Torus { .. }) - ); - let a_is_conic = matches!( - a.geometry.surfaces.first(), - Some(SurfaceGeometry::Cone { .. }) - ); - let b_is_conic = matches!( - b.geometry.surfaces.first(), - Some(SurfaceGeometry::Cone { .. }) - ); - let a_is_box = !a.geometry.surfaces.is_empty() - && a.geometry - .surfaces - .iter() - .all(|surface| matches!(surface, SurfaceGeometry::Plane { .. })); - let b_is_box = !b.geometry.surfaces.is_empty() - && b.geometry - .surfaces - .iter() - .all(|surface| matches!(surface, SurfaceGeometry::Plane { .. })); - let specialized = (|| { - if (a_is_sphere && b_is_cylinder) || (a_is_cylinder && b_is_sphere) { - return sphere_cylinder_boolean(a, b, operation, id.clone()); - } - if (a_is_sphere && b_is_conic) || (a_is_conic && b_is_sphere) { - return sphere_conic_containment(a, b, operation, id.clone()); - } - if (a_is_cylinder && b_is_conic) || (a_is_conic && b_is_cylinder) { - return cylinder_conic_containment(a, b, operation, id.clone()); - } - if (a_is_conic && b_is_box) || (a_is_box && b_is_conic) { - return conic_box_containment(a, b, operation, id.clone()); - } - if (a_is_sphere && b_is_torus) || (a_is_torus && b_is_sphere) { - return sphere_torus_containment(a, b, operation, id.clone()); - } - if (a_is_cylinder && b_is_torus) || (a_is_torus && b_is_cylinder) { - return torus_cylinder_containment(a, b, operation, id.clone()); - } - if (a_is_box && b_is_torus) || (a_is_torus && b_is_box) { - return torus_box_containment(a, b, operation, id.clone()); - } - if (a_is_conic && b_is_torus) || (a_is_torus && b_is_conic) { - return torus_conic_containment(a, b, operation, id.clone()); - } - if a_is_torus && b_is_torus { - return torus_containment_boolean(a, b, operation, id.clone()); - } - if a_is_conic && b_is_conic { - return conic_containment_boolean(a, b, operation, id.clone()); - } - if (a_is_sphere && b_is_box) || (a_is_box && b_is_sphere) { - return sphere_box_boolean(a, b, operation, id.clone()); - } - if (a_is_cylinder && b_is_box) || (a_is_box && b_is_cylinder) { - return cylinder_box_boolean(a, b, operation, id.clone()); - } - if a_is_cylinder && b_is_cylinder { - return boolean_cylinders(a, b, operation, id.clone()); - } - if a_is_box && b_is_box { - return match super::box_booleans::boolean_boxes(a, b, operation, id.clone()) { - Ok(result) => Ok(result), - Err(GeometryError::CoverageGap { .. }) => { - match boolean_planar_extrusions(a, b, operation, id.clone()) { - Ok(result) => Ok(result), - Err(GeometryError::CoverageGap { .. }) => { - match super::box_booleans::boolean_rectilinear( - a, - b, - operation, - id.clone(), - ) { - Ok(result) => Ok(result), - Err(GeometryError::CoverageGap { .. }) - if operation == BooleanOp::Subtraction => - { - match super::planar_booleans::subtract_layered_extrusions( - a, - b, - id.clone(), - ) { - Err(GeometryError::CoverageGap { .. }) => { - super::planar_booleans::subtract_planar_polyhedra( - a, - b, - id.clone(), - ) - } - result => result, - } - } - Err(error) => Err(error), - } - } - Err(error) => Err(error), - } - } - Err(error) => Err(error), - }; - } - boolean_spheres(a, b, operation, id.clone()) - })(); - match specialized { - Err(GeometryError::CoverageGap { .. }) => { - if operation == BooleanOp::Subtraction - && b.geometry - .surfaces - .iter() - .all(|surface| matches!(surface, SurfaceGeometry::Plane { .. })) - && a.geometry - .surfaces - .iter() - .any(|surface| matches!(surface, SurfaceGeometry::Cylinder { .. })) - { - match super::curved_layered_boolean::subtract_vertical_arc_extrusion( - a, - b, - id.clone(), - ) { - Ok(result) => return Ok(result), - Err(GeometryError::CoverageGap { .. }) => {} - Err(error) => return Err(error), - } - } - if let Some(result) = - generic_two_sided_cutter_band_subtraction(a, b, operation, id.clone())? - { - return Ok(result); - } - if let Some(result) = - generic_multiple_closed_loops_boolean(a, b, operation, id.clone())? - { - return Ok(result); - } - if let Some(result) = generic_single_closed_loop_boolean(a, b, operation, id.clone())? { - return Ok(result); - } - if let Some(result) = generic_non_intersecting_boolean(a, b, operation, id.clone())? { - Ok(result) - } else { - Err(GeometryError::CoverageGap { - families: [ - "generic analytic solid".into(), - "intersecting analytic solid".into(), - ], - }) - } - } - result => result, - } -} - -/// Subtract through-depth planar and circular profiles from a rectangular -/// prism in one analytic arrangement. This avoids making a later circle cut -/// through the coplanar face tiles created by an earlier rectangular cut. -fn subtract_prismatic_profile_batch( - host: &BrepEnvelope, - cutters: &[BrepEnvelope], - id: String, -) -> Result, GeometryError> { - use crate::geometry::curved_boolean2d::{boolean_curved_regions, CurveEdge2, CurveRegion2}; - - let Ok(prism) = full_planar_extrusion(host) else { - return Ok(None); - }; - if prism.contours.len() != 1 - || prism.contours[0].len() != 4 - || host.topology.vertices.len() != 8 - { - return Ok(None); - } - let first_axis = cutters.iter().find_map(|cutter| { - full_planar_extrusion(cutter) - .ok() - .map(|input| input.frame.z) - .or_else(|| full_cylinder(cutter).ok().map(|input| input.frame.z)) - }); - let Some(across) = first_axis else { - return Ok(None); - }; - let up = prism.frame.z; - if dot(up, across).abs() > 1.0e-10 { - return Ok(None); - } - let along = unit(cross(up, across))?; - let basis = [along, up, across]; - let mut ranges = [[f64::INFINITY, f64::NEG_INFINITY]; 3]; - for vertex in &host.topology.vertices { - for axis in 0..3 { - let value = dot(vertex.position, basis[axis]); - ranges[axis][0] = ranges[axis][0].min(value); - ranges[axis][1] = ranges[axis][1].max(value); - } - } - let geometric = std::iter::once(host) - .chain(cutters.iter()) - .map(|brep| brep.accuracy.geometric) - .fold(0.0_f64, f64::max); - let dimensions = ranges.map(|range| range[1] - range[0]); - if dimensions.iter().any(|value| *value <= 4.0 * geometric) - || host.topology.vertices.iter().any(|vertex| { - (0..3).any(|axis| { - let value = dot(vertex.position, basis[axis]); - (value - ranges[axis][0]).abs() > geometric - && (value - ranges[axis][1]).abs() > geometric - }) - }) - { - return Ok(None); - } - let frame = Frame3 { - origin: add( - add(scale(along, ranges[0][0]), scale(up, ranges[1][0])), - scale(across, ranges[2][0]), - ), - x: along, - y: up, - z: across, - }; - frame.validate()?; - let line_ring = |points: &[[f64; 2]]| -> Vec { - points - .iter() - .enumerate() - .map(|(index, point)| CurveEdge2::Line { - from: *point, - to: points[(index + 1) % points.len()], - }) - .collect() - }; - let reverse_ring = |ring: &mut Vec| { - *ring = ring.iter().rev().map(CurveEdge2::reverse).collect(); - }; - let winding_ring = |mut ring: Vec, positive: bool| { - let area = ring.iter().map(CurveEdge2::twice_area).sum::(); - if (area > 0.0) != positive { - reverse_ring(&mut ring); - } - ring - }; - let host_region = CurveRegion2 { - outer: line_ring(&[ - [0.0, 0.0], - [0.0, dimensions[1]], - [dimensions[0], dimensions[1]], - [dimensions[0], 0.0], - ]), - holes: Vec::new(), - }; - let mut cut_regions = Vec::with_capacity(cutters.len()); - for cutter in cutters { - let vertices_cover_depth = || { - let mut lo = f64::INFINITY; - let mut hi = f64::NEG_INFINITY; - for vertex in &cutter.topology.vertices { - let value = frame.local(vertex.position)[2]; - lo = lo.min(value); - hi = hi.max(value); - } - lo <= geometric && hi >= dimensions[2] - geometric - }; - if !vertices_cover_depth() { - return Ok(None); - } - if let Ok(input) = full_planar_extrusion(cutter) { - if dot(input.frame.z, across).abs() < 1.0 - 1.0e-10 { - return Ok(None); - } - let mut rings = input.contours.iter().map(|contour| { - line_ring( - &contour - .iter() - .map(|point| { - let local = frame.local(*point); - [local[0], local[1]] - }) - .collect::>(), - ) - }); - let outer = winding_ring( - rings.next().ok_or_else(|| { - GeometryError::InvalidTopology("profile cutter has no outer ring".into()) - })?, - false, - ); - let holes = rings.map(|ring| winding_ring(ring, true)).collect(); - cut_regions.push(CurveRegion2 { outer, holes }); - } else if let Ok(input) = full_cylinder(cutter) { - if dot(input.frame.z, across).abs() < 1.0 - 1.0e-10 { - return Ok(None); - } - let center = frame.local(input.frame.origin); - cut_regions.push(CurveRegion2 { - outer: vec![ - CurveEdge2::Arc { - center: [center[0], center[1]], - radius: input.radius, - start_angle: 0.0, - sweep_angle: -std::f64::consts::PI, - }, - CurveEdge2::Arc { - center: [center[0], center[1]], - radius: input.radius, - start_angle: -std::f64::consts::PI, - sweep_angle: -std::f64::consts::PI, - }, - ], - holes: Vec::new(), - }); - } else { - return Ok(None); - } - } - let regions = boolean_curved_regions( - &[host_region], - &cut_regions, - PlanarBooleanOp::Subtraction, - geometric, - ) - .map_err(|reason| { - GeometryError::UnresolvedIntersection(format!("mixed profile batch arrangement: {reason}")) - })?; - if regions.is_empty() { - return Ok(None); - } - let convert = |ring: Vec| -> Vec { - ring.into_iter() - .map(|edge| match edge { - CurveEdge2::Line { from, to } => primitives::ProfileEdge::Line { from, to }, - CurveEdge2::Arc { - center, - radius, - start_angle, - sweep_angle, - } => primitives::ProfileEdge::Arc { - center, - radius, - start_angle, - sweep_angle, - }, - }) - .collect() - }; - let accuracy = Accuracy { - geometric, - intersection: std::iter::once(host) - .chain(cutters.iter()) - .map(|brep| brep.accuracy.intersection) - .fold(0.0_f64, f64::max), - tessellation: std::iter::once(host) - .chain(cutters.iter()) - .map(|brep| brep.accuracy.tessellation) - .fold(0.0_f64, f64::max), - exchange: std::iter::once(host) - .chain(cutters.iter()) - .map(|brep| brep.accuracy.exchange) - .fold(0.0_f64, f64::max), - }; - let single_region = regions.len() == 1; - let mut parts = Vec::with_capacity(regions.len()); - for (index, region) in regions.into_iter().enumerate() { - parts.push(primitives::arc_edged_extrusion_with_holes( - if single_region { - id.clone() - } else { - format!("{id}:part:{index}") - }, - frame, - convert(region.outer), - region.holes.into_iter().map(convert).collect(), - dimensions[2], - accuracy, - )?); - } - let mut out = if single_region { - parts.pop().ok_or_else(|| { - GeometryError::InvalidTopology("profile batch produced no material region".into()) - })? - } else { - let mut compound = BrepEnvelope::new(id, accuracy)?; - for part in &parts { - append_analytic_input(&mut compound, part)?; - } - compound - }; - let inputs = std::iter::once(host) - .chain(cutters.iter()) - .collect::>(); - for result_face in &mut out.topology.faces { - let surface = out.geometry.surface(result_face.surface)?; - let mut sources = Vec::new(); - let cap = matches!(surface, SurfaceGeometry::Plane { frame: plane } - if dot(plane.z, across).abs() > 1.0 - 1.0e-10); - if cap { - let position = dot(surface.frame().origin, across); - for face in &host.topology.faces { - let SurfaceGeometry::Plane { - frame: source_frame, - } = host.geometry.surface(face.surface)? - else { - continue; - }; - if dot(source_frame.z, across).abs() > 1.0 - 1.0e-10 - && (dot(source_frame.origin, across) - position).abs() <= geometric - { - sources.push(brep_face_source(host, face.id)); - } - } - } else { - let uv = result_face.trim.uv_bounds.map(Interval::midpoint); - let sample = surface.point_at(uv)?; - for input in &inputs { - for face in &input.topology.faces { - let source_surface = input.geometry.surface(face.surface)?; - let on = match source_surface { - SurfaceGeometry::Plane { - frame: source_frame, - } => { - source_frame.local(sample)[2].abs() <= geometric - && dot(source_frame.z, across).abs() < 1.0 - 1.0e-10 - } - SurfaceGeometry::Cylinder { - frame: source_frame, - radius, - } => { - let local = source_frame.local(sample); - (local[0].hypot(local[1]) - radius).abs() <= geometric - } - _ => false, - }; - if !on { - continue; - } - let hint = face.trim.uv_bounds.map(Interval::midpoint); - let source_uv = source_surface.project(sample, Some(hint))?; - if face_contains_uv(input, face, source_uv)? == Some(true) { - sources.push(brep_face_source(input, face.id)); - } - } - } - } - if sources.is_empty() { - return Err(GeometryError::InvalidTopology(format!( - "profile batch output face {} has no source", - result_face.key - ))); - } - let cut = sources - .iter() - .any(|source| cutters.iter().any(|cutter| cutter.id == source.entity)); - result_face.provenance = FaceProvenance { - sources: unique_sources(sources), - role: if cut { FaceRole::Cut } else { FaceRole::Split }, - reversed: cut, - }; - } - out.revision = std::iter::once(host) - .chain(cutters.iter()) - .map(|brep| brep.revision) - .max() - .unwrap_or(0) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - Ok(Some(BooleanResult { - report: BooleanReport { - operation: BooleanOp::Subtraction, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: false, - face_mappings: analytic_face_mappings(&out, inputs), - }, - brep: out, - })) -} - -pub fn subtract_planar_cutters( - host: &BrepEnvelope, - cutters: &[BrepEnvelope], - id: String, -) -> Result { - if cutters.is_empty() || cutters.len() > 100 { - return Err(GeometryError::InvalidGeometry( - "planar batch subtraction requires between one and 100 cutters".into(), - )); - } - if cutters.len() == 1 { - return boolean_brep(host, &cutters[0], BooleanOp::Subtraction, id); - } - let mut accuracy = host.accuracy; - let mut bounds = Vec::with_capacity(cutters.len()); - for cutter in cutters { - cutter.validate()?; - accuracy.geometric = accuracy.geometric.max(cutter.accuracy.geometric); - accuracy.intersection = accuracy.intersection.max(cutter.accuracy.intersection); - accuracy.tessellation = accuracy.tessellation.max(cutter.accuracy.tessellation); - accuracy.exchange = accuracy.exchange.max(cutter.accuracy.exchange); - bounds.push( - cutter.bounds()?.ok_or_else(|| { - GeometryError::InvalidGeometry("planar cutter has no bounds".into()) - })?, - ); - } - let planar = cutters - .iter() - .filter(|cutter| { - cutter - .geometry - .surfaces - .iter() - .all(|surface| matches!(surface, SurfaceGeometry::Plane { .. })) - }) - .collect::>(); - let cylinders = if planar.len() == cutters.len() { - Vec::new() - } else { - cutters - .iter() - .filter(|cutter| full_cylinder(cutter).is_ok()) - .collect::>() - }; - if !cylinders.is_empty() && planar.len() + cylinders.len() == cutters.len() { - if let Some(result) = subtract_prismatic_profile_batch(host, cutters, id.clone())? { - return Ok(result); - } - } - if !planar.is_empty() - && !cylinders.is_empty() - && planar.len() + cylinders.len() == cutters.len() - { - // Reconstruct planar voids first, then subtract transverse cylinders. - // Cutters may overlap outside the host, so their world bounds cannot - // decide whether the host cut is valid. Each chained Boolean must - // validate its exact B-rep or return a coverage error. - let planar_cutters = planar.into_iter().cloned().collect::>(); - let mut result = subtract_planar_cutters(host, &planar_cutters, format!("{id}:planar")) - .map_err(|error| match error { - GeometryError::CoverageGap { .. } => GeometryError::CoverageGap { - families: ["mixed cutter batch".into(), "planar stage".into()], - }, - other => other, - })?; - let count = cylinders.len(); - for (index, cylinder) in cylinders.into_iter().enumerate() { - let prior = result; - let mut next = boolean_brep( - &prior.brep, - cylinder, - BooleanOp::Subtraction, - if index + 1 == count { - id.clone() - } else { - format!("{id}:round-{index}") - }, - ) - .map_err(|error| match error { - GeometryError::CoverageGap { .. } => GeometryError::CoverageGap { - families: ["mixed cutter batch".into(), "cylindrical stage".into()], - }, - other => other, - })?; - // The generic two-sided builder records its immediate input as - // the source of every copied face. Replace that intermediate - // reference with the face's original ancestry so a mixed batch - // still identifies the authored host and planar cutter. - for face in &mut next.brep.topology.faces { - let mut lineage = Vec::new(); - for source in std::mem::take(&mut face.provenance.sources) { - if source.entity == prior.brep.id && source.body == prior.brep.id { - let previous = prior - .brep - .topology - .faces - .get(source.face as usize) - .ok_or_else(|| { - GeometryError::InvalidTopology( - "mixed cut source face is missing".into(), - ) - })?; - if previous.provenance.sources.is_empty() { - lineage.push(source); - } else { - lineage.extend(previous.provenance.sources.iter().cloned()); - } - } else { - lineage.push(source); - } - } - face.provenance.sources = unique_sources(lineage); - } - next.brep.validate()?; - result = next; - } - result.report.face_mappings = - analytic_face_mappings(&result.brep, std::iter::once(host).chain(cutters.iter())); - return Ok(result); - } - if host - .geometry - .surfaces - .iter() - .any(|surface| matches!(surface, SurfaceGeometry::Cylinder { .. })) - && cutters.iter().all(|cutter| { - cutter - .geometry - .surfaces - .iter() - .all(|surface| matches!(surface, SurfaceGeometry::Plane { .. })) - }) - { - match super::curved_layered_boolean::subtract_vertical_arc_extrusion_batch( - host, - &cutters.iter().collect::>(), - id.clone(), - ) { - Ok(result) => return Ok(result), - Err(GeometryError::CoverageGap { .. }) => {} - Err(error) => return Err(error), - } - } - for first in 0..cutters.len() { - for second in first + 1..cutters.len() { - if (0..3).all(|axis| { - bounds[first].axes[axis] - .hi - .min(bounds[second].axes[axis].hi) - - bounds[first].axes[axis] - .lo - .max(bounds[second].axes[axis].lo) - > 4.0 * accuracy.geometric - }) { - return Err(GeometryError::CoverageGap { - families: [ - "overlapping planar batch cutters".into(), - "overlapping planar batch cutters".into(), - ], - }); - } - } - } - let mut combined = BrepEnvelope::new(format!("{id}:cutters"), accuracy)?; - for cutter in cutters { - append_analytic_input(&mut combined, cutter)?; - } - combined.validate()?; - super::box_booleans::boolean_rectilinear(host, &combined, BooleanOp::Subtraction, id) -} - -pub fn boolean_spheres( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - a.validate()?; - b.validate()?; - let family = |brep: &BrepEnvelope| { - match brep.geometry.surfaces.first() { - Some(SurfaceGeometry::Sphere { .. }) => "sphere (canonical full sphere required)", - Some(SurfaceGeometry::Cylinder { .. }) => "cylinder", - Some(SurfaceGeometry::Cone { .. }) => "cone", - Some(SurfaceGeometry::Torus { .. }) => "torus", - Some(SurfaceGeometry::Plane { .. }) => "planar/compound solid", - None => "empty solid", - } - .to_string() - }; - let coverage = |error| match error { - GeometryError::CoverageGap { .. } => GeometryError::CoverageGap { - families: [family(a), family(b)], - }, - other => other, - }; - let a = full_sphere(a).map_err(&coverage)?; - let b = full_sphere(b).map_err(&coverage)?; - let accuracy = Accuracy { - geometric: a.brep.accuracy.geometric.max(b.brep.accuracy.geometric), - intersection: a - .brep - .accuracy - .intersection - .max(b.brep.accuracy.intersection), - tessellation: a - .brep - .accuracy - .tessellation - .max(b.brep.accuracy.tessellation), - exchange: a.brep.accuracy.exchange.max(b.brep.accuracy.exchange), - }; - let mut out = BrepEnvelope::new(id, accuracy)?; - let delta = sub(b.frame.origin, a.frame.origin); - let distance = norm(delta); - let [outer, inner] = - sphere_sphere_relation(a.frame.origin, a.radius, b.frame.origin, b.radius)?; - let coincident = distance == 0.0 && a.radius == b.radius; - let mut supports = GeometryStore::new(); - supports.surfaces = vec![ - SurfaceGeometry::Sphere { - frame: a.frame, - radius: a.radius, - }, - SurfaceGeometry::Sphere { - frame: b.frame, - radius: b.radius, - }, - ]; - let intersections = intersect_surfaces(&mut supports, 0, 1, accuracy)?; - let mut keep = |input: &SphereInput<'_>| -> Result<(), GeometryError> { - let shell = append_sphere(&mut out, input, false)?; - out.solids.push(SolidRegion { - outer_shell: shell, - cavity_shells: Vec::new(), - }); - Ok(()) - }; - if coincident { - if operation != BooleanOp::Subtraction { - keep(&a)?; - out.topology.faces[0].provenance.role = FaceRole::Coincident; - out.topology.faces[0].provenance.sources.push(source(&b)); - } - } else if outer != Sign::Negative { - match operation { - BooleanOp::Union => { - keep(&a)?; - keep(&b)?; - } - BooleanOp::Intersection => {} - BooleanOp::Subtraction => keep(&a)?, - } - } else if inner != Sign::Positive { - let a_outer = a.radius > b.radius; - match operation { - BooleanOp::Union => keep(if a_outer { &a } else { &b })?, - BooleanOp::Intersection => keep(if a_outer { &b } else { &a })?, - BooleanOp::Subtraction if a_outer => { - if inner == Sign::Zero { - return Err(GeometryError::UnresolvedIntersection( - "internally tangent cavity boundaries require contact topology".into(), - )); - } - if a.radius - b.radius - distance <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "cavity clearance is below geometric resolution".into(), - )); - } - let shell = append_sphere(&mut out, &a, false)?; - let cavity = append_sphere(&mut out, &b, true)?; - out.solids.push(SolidRegion { - outer_shell: shell, - cavity_shells: vec![cavity], - }); - } - BooleanOp::Subtraction => {} - } - } else { - let intersection = intersections.curves.first().ok_or_else(|| { - GeometryError::UnresolvedIntersection( - "transverse spheres have no intersection branch".into(), - ) - })?; - let circle = supports.curves[intersection.curve as usize].clone(); - let (circle_frame, circle_radius) = match circle { - CurveGeometry::Circle { frame, radius } => (frame, radius), - _ => return Err(gap()), - }; - let axis = unit(delta)?; - let frame_a = Frame3 { - origin: a.frame.origin, - z: axis, - x: circle_frame.x, - y: circle_frame.y, - }; - let frame_b = Frame3 { - origin: b.frame.origin, - ..frame_a - }; - let latitudes = [(&a, frame_a), (&b, frame_b)].map(|(input, frame)| { - let local = frame.local(circle_frame.origin); - (local[2] / input.radius).asin() - }); - if circle_radius <= 4.0 * accuracy.geometric || latitudes.iter().any(|v| !v.is_finite()) { - return Err(GeometryError::UnresolvedIntersection( - "intersection cap is below geometric resolution".into(), - )); - } - let north_a = operation == BooleanOp::Intersection; - let north_b = operation == BooleanOp::Union; - for (input, latitude, north) in [(&a, latitudes[0], north_a), (&b, latitudes[1], north_b)] { - let cap_height = input.radius - * (1.0 - - if north { - latitude.sin() - } else { - -latitude.sin() - }); - if cap_height <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "retained spherical cap is below geometric resolution".into(), - )); - } - } - let mut builder = Builder::new(out.id.clone(), accuracy)?; - let vertex = builder.vertex(circle.elementary_point(0.0)?); - let edge = builder.edge(circle, Interval::new(0.0, std::f64::consts::TAU)?, false); - patch( - &mut builder, - &a, - frame_a, - latitudes[0], - north_a, - edge, - vertex, - false, - )?; - patch( - &mut builder, - &b, - frame_b, - latitudes[1], - north_b, - edge, - vertex, - operation == BooleanOp::Subtraction, - )?; - out = builder.finish()?; - } - out.revision = a - .brep - .revision - .max(b.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let report = BooleanReport { - operation, - quality: GeometryQuality::Analytic, - contacts: intersections.contacts, - coincident, - face_mappings: [&a, &b] - .map(|input| { - let source = source(input); - let result_faces = out - .topology - .faces - .iter() - .filter(|face| { - face.provenance.sources.iter().any(|s| { - s.entity == source.entity - && s.body == source.body - && s.key == source.key - && s.face == source.face - }) - }) - .map(|face| face.id) - .collect(); - FaceMapping { - source, - result_faces, - } - }) - .into(), - }; - Ok(BooleanResult { brep: out, report }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::{ - geometry::{cross, dot}, - tessellation::tessellate, - }; - fn sphere(id: &str, center: Point3, radius: f64) -> BrepEnvelope { - primitives::sphere( - id.into(), - Frame3 { - origin: center, - ..Frame3::IDENTITY - }, - radius, - Accuracy { - geometric: 1e-9, - intersection: 1e-10, - tessellation: 0.01, - exchange: 1e-5, - }, - ) - .unwrap() - } - fn cylinder(id: &str, origin_z: f64, height: f64, reverse: bool) -> BrepEnvelope { - let frame = if reverse { - Frame3 { - origin: [0.0, 0.0, origin_z], - x: [1.0, 0.0, 0.0], - y: [0.0, -1.0, 0.0], - z: [0.0, 0.0, -1.0], - } - } else { - Frame3 { - origin: [0.0, 0.0, origin_z], - ..Frame3::IDENTITY - } - }; - primitives::cylinder( - id.into(), - frame, - 1.0, - height, - Accuracy { - geometric: 1e-9, - intersection: 1e-10, - tessellation: 0.01, - exchange: 1e-5, - }, - ) - .unwrap() - } - fn volume(brep: &BrepEnvelope) -> f64 { - volume_at_deflection(brep, 0.02) - } - fn volume_at_deflection(brep: &BrepEnvelope, deflection: f64) -> f64 { - let mesh = tessellate(brep, deflection, 2_000_000).unwrap(); - mesh.indices - .chunks_exact(3) - .map(|ids| { - let points: [Point3; 3] = std::array::from_fn(|i| { - let start = ids[i] as usize * 3; - [ - mesh.positions[start], - mesh.positions[start + 1], - mesh.positions[start + 2], - ] - }); - dot(points[0], cross(points[1], points[2])) / 6.0 - }) - .sum() - } - #[test] - fn nonparallel_cylinder_through_cut_keeps_numerical_ssi_authoritative() { - let accuracy = Accuracy { - geometric: 4e-8, - intersection: 1e-8, - tessellation: 0.01, - exchange: 1e-5, - }; - let host = primitives::cylinder( - "host".into(), - Frame3 { - origin: [0.0, 0.0, -2.0], - ..Frame3::IDENTITY - }, - 1.0, - 4.0, - accuracy, - ) - .unwrap(); - let cutter = primitives::cylinder( - "cutter".into(), - Frame3::from_axis([-2.0, 0.0, 0.2], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]).unwrap(), - 0.6, - 4.0, - accuracy, - ) - .unwrap(); - let result = - boolean_brep(&host, &cutter, BooleanOp::Subtraction, "through-cut".into()).unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.topology.faces.len(), 4); - assert_eq!(result.brep.topology.faces[0].trim.holes.len(), 2); - assert_eq!(result.brep.topology.faces[3].provenance.role, FaceRole::Cut); - assert!(result.brep.topology.faces[3].provenance.reversed); - assert_eq!(result.brep.geometry.intersections.len(), 2); - assert_eq!(result.report.face_mappings.len(), 6); - assert_eq!( - super::super::query::classify_point(&result.brep, [0.0, 0.0, 0.2]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&result.brep, [0.0, 0.9, 0.2]).unwrap(), - super::super::query::PointClassification::Inside - ); - let mesh = tessellate(&result.brep, 0.0075, 2_000_000).unwrap(); - assert!( - mesh.triangle_face_ids.len() < 50_000, - "cross-drill tessellation produced {} triangles", - mesh.triangle_face_ids.len() - ); - } - - #[test] - fn perpendicular_cylinders_cut_exact_round_holes_through_every_cuboid_axis() { - let accuracy = Accuracy { - geometric: 1.0e-9, - intersection: 1.0e-10, - tessellation: 0.01, - exchange: 1.0e-5, - }; - let size = [4.0, 3.0, 2.0]; - for axis in 0..3 { - let host = primitives::cuboid("box".into(), Frame3::IDENTITY, size, accuracy).unwrap(); - let mut local_origin = [2.0, 1.5, 1.0]; - local_origin[axis] = -1.0; - let direction = std::array::from_fn(|coordinate| f64::from(coordinate == axis)); - let reference = - std::array::from_fn(|coordinate| f64::from(coordinate == (axis + 1) % 3)); - let cutter = primitives::cylinder( - "round-opening".into(), - Frame3::from_axis(local_origin, direction, reference).unwrap(), - 0.4, - size[axis] + 2.0, - accuracy, - ) - .unwrap(); - - let subtraction = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - format!("cut-{axis}"), - ) - .unwrap(); - subtraction.brep.validate().unwrap(); - assert_eq!(subtraction.brep.topology.faces.len(), 7); - assert_eq!( - subtraction - .brep - .topology - .faces - .iter() - .filter(|face| !face.trim.holes.is_empty()) - .count(), - 2 - ); - assert!(matches!( - subtraction.brep.geometry.surfaces.last(), - Some(SurfaceGeometry::Cylinder { .. }) - )); - assert_eq!( - super::super::query::classify_point(&subtraction.brep, [2.0, 1.5, 1.0]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&subtraction.brep, [0.2, 0.2, 0.2]).unwrap(), - super::super::query::PointClassification::Inside - ); - assert_eq!(subtraction.report.face_mappings.len(), 9); - - let union = - boolean_brep(&host, &cutter, BooleanOp::Union, format!("union-{axis}")).unwrap(); - union.brep.validate().unwrap(); - assert_eq!(union.brep.solids.len(), 1); - assert_eq!(union.brep.topology.faces.len(), 10); - assert_eq!( - union - .brep - .topology - .faces - .iter() - .filter(|face| !face.trim.holes.is_empty()) - .count(), - 2 - ); - for coordinate in [-0.5, size[axis] + 0.5] { - let mut point = [2.0, 1.5, 1.0]; - point[axis] = coordinate; - assert_eq!( - super::super::query::classify_point(&union.brep, point).unwrap(), - super::super::query::PointClassification::Inside - ); - } - assert_eq!( - super::super::query::classify_point(&union.brep, [0.2, 0.2, 0.2]).unwrap(), - super::super::query::PointClassification::Inside - ); - tessellate(&union.brep, 0.01, 2_000_000).unwrap(); - - let intersection = boolean_brep( - &host, - &cutter, - BooleanOp::Intersection, - format!("intersection-{axis}"), - ) - .unwrap(); - intersection.brep.validate().unwrap(); - assert_eq!(intersection.brep.topology.faces.len(), 3); - assert_eq!( - super::super::query::classify_point(&intersection.brep, [2.0, 1.5, 1.0]).unwrap(), - super::super::query::PointClassification::Inside - ); - assert_eq!( - super::super::query::classify_point(&intersection.brep, [0.2, 0.2, 0.2]).unwrap(), - super::super::query::PointClassification::Outside - ); - - let cutter_remainder = boolean_brep( - &cutter, - &host, - BooleanOp::Subtraction, - format!("cylinder-minus-box-{axis}"), - ) - .unwrap(); - cutter_remainder.brep.validate().unwrap(); - assert_eq!(cutter_remainder.brep.solids.len(), 2); - assert_eq!(cutter_remainder.brep.topology.faces.len(), 6); - assert_eq!( - cutter_remainder - .brep - .topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut - && face.provenance.reversed) - .count(), - 2 - ); - for coordinate in [-0.5, size[axis] + 0.5] { - let mut point = [2.0, 1.5, 1.0]; - point[axis] = coordinate; - assert_eq!( - super::super::query::classify_point(&cutter_remainder.brep, point).unwrap(), - super::super::query::PointClassification::Inside - ); - } - assert_eq!( - super::super::query::classify_point(&cutter_remainder.brep, [2.0, 1.5, 1.0]) - .unwrap(), - super::super::query::PointClassification::Outside - ); - } - } - - #[test] - fn perpendicular_cylinders_cut_exact_blind_pockets_from_both_cuboid_sides() { - let accuracy = Accuracy { - geometric: 1.0e-9, - intersection: 1.0e-10, - tessellation: 0.01, - exchange: 1.0e-5, - }; - let size = [4.0, 3.0, 2.0]; - let pocket_depth = 0.75; - for axis in 0..3 { - for entry_from_low in [true, false] { - let host = - primitives::cuboid("box".into(), Frame3::IDENTITY, size, accuracy).unwrap(); - let mut origin = [2.0, 1.5, 1.0]; - origin[axis] = if entry_from_low { - -1.0 - } else { - size[axis] + 1.0 - }; - let direction = std::array::from_fn(|coordinate| { - if coordinate == axis { - if entry_from_low { - 1.0 - } else { - -1.0 - } - } else { - 0.0 - } - }); - let reference = - std::array::from_fn(|coordinate| f64::from(coordinate == (axis + 1) % 3)); - let cutter = primitives::cylinder( - "pocket".into(), - Frame3::from_axis(origin, direction, reference).unwrap(), - 0.4, - 1.0 + pocket_depth, - accuracy, - ) - .unwrap(); - - let subtraction = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - format!("pocket-{axis}-{entry_from_low}"), - ) - .unwrap(); - subtraction.brep.validate().unwrap(); - assert_eq!(subtraction.brep.topology.faces.len(), 8); - assert_eq!( - subtraction - .brep - .topology - .faces - .iter() - .filter(|face| !face.trim.holes.is_empty()) - .count(), - 1 - ); - assert_eq!( - subtraction - .brep - .topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut) - .count(), - 2 - ); - assert!(subtraction - .brep - .topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut) - .all(|face| face.provenance.reversed)); - - let union = boolean_brep( - &host, - &cutter, - BooleanOp::Union, - format!("pocket-union-{axis}-{entry_from_low}"), - ) - .unwrap(); - union.brep.validate().unwrap(); - assert_eq!(union.brep.solids.len(), 1); - assert_eq!(union.brep.topology.faces.len(), 8); - assert_eq!( - union - .brep - .topology - .faces - .iter() - .filter(|face| !face.trim.holes.is_empty()) - .count(), - 1 - ); - - let mut void_point = [2.0, 1.5, 1.0]; - void_point[axis] = if entry_from_low { - pocket_depth / 2.0 - } else { - size[axis] - pocket_depth / 2.0 - }; - assert_eq!( - super::super::query::classify_point(&subtraction.brep, void_point).unwrap(), - super::super::query::PointClassification::Outside - ); - let mut material_point = void_point; - material_point[axis] = if entry_from_low { - pocket_depth + 0.25 - } else { - size[axis] - pocket_depth - 0.25 - }; - assert_eq!( - super::super::query::classify_point(&subtraction.brep, material_point).unwrap(), - super::super::query::PointClassification::Inside - ); - - let intersection = boolean_brep( - &host, - &cutter, - BooleanOp::Intersection, - format!("pocket-intersection-{axis}-{entry_from_low}"), - ) - .unwrap(); - intersection.brep.validate().unwrap(); - assert_eq!(intersection.brep.topology.faces.len(), 3); - assert_eq!( - super::super::query::classify_point(&intersection.brep, void_point).unwrap(), - super::super::query::PointClassification::Inside - ); - assert_eq!( - super::super::query::classify_point(&intersection.brep, material_point) - .unwrap(), - super::super::query::PointClassification::Outside - ); - - let cutter_remainder = boolean_brep( - &cutter, - &host, - BooleanOp::Subtraction, - format!("pocket-cutter-minus-box-{axis}-{entry_from_low}"), - ) - .unwrap(); - cutter_remainder.brep.validate().unwrap(); - assert_eq!(cutter_remainder.brep.solids.len(), 1); - assert_eq!(cutter_remainder.brep.topology.faces.len(), 3); - assert_eq!( - cutter_remainder - .brep - .topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut - && face.provenance.reversed) - .count(), - 1 - ); - let mut exterior_point = [2.0, 1.5, 1.0]; - exterior_point[axis] = if entry_from_low { - -0.5 - } else { - size[axis] + 0.5 - }; - assert_eq!( - super::super::query::classify_point(&cutter_remainder.brep, exterior_point) - .unwrap(), - super::super::query::PointClassification::Inside - ); - assert_eq!( - super::super::query::classify_point(&union.brep, exterior_point).unwrap(), - super::super::query::PointClassification::Inside - ); - assert_eq!( - super::super::query::classify_point(&union.brep, material_point).unwrap(), - super::super::query::PointClassification::Inside - ); - tessellate(&union.brep, 0.01, 2_000_000).unwrap(); - assert_eq!( - super::super::query::classify_point(&cutter_remainder.brep, void_point) - .unwrap(), - super::super::query::PointClassification::Outside - ); - } - } - } - - #[test] - fn spheres_cut_exact_single_face_pockets_on_every_cuboid_side() { - let accuracy = Accuracy { - geometric: 1.0e-9, - intersection: 1.0e-10, - tessellation: 0.01, - exchange: 1.0e-5, - }; - let size = [4.0, 3.0, 2.0]; - let radius = 0.75; - for axis in 0..3 { - for upper in [false, true] { - for center_inside in [false, true] { - let host = - primitives::cuboid("box".into(), Frame3::IDENTITY, size, accuracy).unwrap(); - let mut center = [2.0, 1.5, 1.0]; - center[axis] = match (upper, center_inside) { - (false, false) => -0.25, - (false, true) => 0.25, - (true, false) => size[axis] + 0.25, - (true, true) => size[axis] - 0.25, - }; - let cutter = primitives::sphere( - "spherical-pocket".into(), - Frame3 { - origin: center, - ..Frame3::IDENTITY - }, - radius, - accuracy, - ) - .unwrap(); - - let subtraction = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - format!("sphere-pocket-{axis}-{upper}-{center_inside}"), - ) - .unwrap(); - subtraction.brep.validate().unwrap(); - assert_eq!(subtraction.brep.topology.faces.len(), 7); - assert_eq!( - subtraction - .brep - .topology - .faces - .iter() - .filter(|face| !face.trim.holes.is_empty()) - .count(), - 1 - ); - assert_eq!( - subtraction - .brep - .topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut - && face.provenance.reversed) - .count(), - 1 - ); - assert_eq!(subtraction.report.face_mappings.len(), 7); - - let inward_sign = if upper { -1.0 } else { 1.0 }; - let mut void_point = center; - void_point[axis] = if upper { size[axis] - 0.25 } else { 0.25 }; - let mut material_point = center; - material_point[axis] += inward_sign * (radius + 0.25); - let mut sphere_only_point = center; - sphere_only_point[axis] = if upper { size[axis] + 0.25 } else { -0.25 }; - assert_eq!( - super::super::query::classify_point(&subtraction.brep, void_point).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&subtraction.brep, material_point) - .unwrap(), - super::super::query::PointClassification::Inside - ); - - let intersection = boolean_brep( - &host, - &cutter, - BooleanOp::Intersection, - format!("sphere-intersection-{axis}-{upper}-{center_inside}"), - ) - .unwrap(); - intersection.brep.validate().unwrap(); - assert_eq!(intersection.brep.topology.faces.len(), 2); - assert_eq!( - super::super::query::classify_point(&intersection.brep, void_point) - .unwrap(), - super::super::query::PointClassification::Inside - ); - assert_eq!( - super::super::query::classify_point(&intersection.brep, material_point) - .unwrap(), - super::super::query::PointClassification::Outside - ); - - let union = boolean_brep( - &host, - &cutter, - BooleanOp::Union, - format!("sphere-union-{axis}-{upper}-{center_inside}"), - ) - .unwrap(); - union.brep.validate().unwrap(); - assert_eq!(union.brep.topology.faces.len(), 7); - for point in [void_point, material_point, sphere_only_point] { - assert_eq!( - super::super::query::classify_point(&union.brep, point).unwrap(), - super::super::query::PointClassification::Inside - ); - } - - let sphere_cut = boolean_brep( - &cutter, - &host, - BooleanOp::Subtraction, - format!("sphere-cut-{axis}-{upper}-{center_inside}"), - ) - .unwrap(); - sphere_cut.brep.validate().unwrap(); - assert_eq!(sphere_cut.brep.topology.faces.len(), 2); - assert!(sphere_cut.brep.topology.faces.iter().any(|face| { - face.provenance.role == FaceRole::Cut && face.provenance.reversed - })); - assert_eq!( - super::super::query::classify_point(&sphere_cut.brep, void_point).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&sphere_cut.brep, sphere_only_point) - .unwrap(), - super::super::query::PointClassification::Inside - ); - tessellate(&subtraction.brep, 0.01, 2_000_000).unwrap(); - tessellate(&intersection.brep, 0.01, 2_000_000).unwrap(); - tessellate(&union.brep, 0.01, 2_000_000).unwrap(); - tessellate(&sphere_cut.brep, 0.01, 2_000_000).unwrap(); - } - } - } - } - - #[test] - fn coaxial_cylinder_booleans_split_material_and_keep_cut_cap_ancestry() { - let a = cylinder("a", 0.0, 4.0, false); - let b = cylinder("b", 3.0, 2.0, true); - for (operation, solids, faces, expected_volume) in [ - (BooleanOp::Union, 1, 3, 4.0 * std::f64::consts::PI), - (BooleanOp::Intersection, 1, 3, 2.0 * std::f64::consts::PI), - (BooleanOp::Subtraction, 2, 6, 2.0 * std::f64::consts::PI), - ] { - let result = boolean_brep(&a, &b, operation, format!("{operation:?}")).unwrap(); - assert_eq!(result.brep.solids.len(), solids); - assert_eq!(result.brep.topology.faces.len(), faces); - let measured = volume(&result.brep).abs(); - assert!( - (measured - expected_volume).abs() < 0.2, - "{operation:?}: measured {measured}, expected {expected_volume}" - ); - assert_eq!(result.report.face_mappings.len(), 6); - if operation == BooleanOp::Subtraction { - let cut_caps: Vec<_> = result - .brep - .topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut) - .collect(); - assert_eq!(cut_caps.len(), 2); - assert!(cut_caps.iter().all(|face| { - face.provenance.reversed - && face.provenance.sources.len() == 1 - && face.provenance.sources[0].entity == "b" - })); - } - } - } - - #[test] - fn coaxial_cylinder_contact_is_regularized_and_near_gap_is_unresolved() { - let a = cylinder("a", 0.0, 4.0, false); - let touching = cylinder("b", 4.0, 2.0, false); - let union = boolean_brep(&a, &touching, BooleanOp::Union, "union".into()).unwrap(); - assert_eq!(union.brep.solids.len(), 1); - let union_volume = volume(&union.brep).abs(); - assert!( - (union_volume - 6.0 * std::f64::consts::PI).abs() < 0.3, - "measured union volume {union_volume}" - ); - let intersection = boolean_brep( - &a, - &touching, - BooleanOp::Intersection, - "intersection".into(), - ) - .unwrap(); - assert!(intersection.brep.solids.is_empty()); - let subtraction = - boolean_brep(&a, &touching, BooleanOp::Subtraction, "subtraction".into()).unwrap(); - assert_eq!(subtraction.brep.solids.len(), 1); - let subtraction_volume = volume(&subtraction.brep).abs(); - assert!( - (subtraction_volume - 4.0 * std::f64::consts::PI).abs() < 0.2, - "measured subtraction volume {subtraction_volume}" - ); - - let near = cylinder("near", 4.0 + 2e-9, 2.0, false); - assert!(matches!( - boolean_brep(&a, &near, BooleanOp::Union, "near".into()), - Err(GeometryError::UnresolvedIntersection(_)) - )); - } - - #[test] - fn positive_similarity_roundoff_does_not_reject_full_cylinder_operands() { - let rotation = std::f64::consts::PI / 7.0; - let source_frame = Frame3 { - origin: [0.0, 1.2, 0.0], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }; - let source = primitives::cylinder( - "c".into(), - source_frame, - 1.0, - 2.0, - cylinder("accuracy", 0.0, 1.0, false).accuracy, - ) - .unwrap(); - let placed = crate::analytic::placement::placed( - &source, - Frame3 { - origin: [0.2, 0.1, 0.2], - x: [rotation.cos(), 0.0, -rotation.sin()], - y: [0.0, 1.0, 0.0], - z: [rotation.sin(), 0.0, rotation.cos()], - }, - 1.25, - ) - .unwrap(); - assert!(full_cylinder(&placed).is_ok()); - } - - #[test] - fn translated_y_axis_cylinder_can_split_into_two_valid_solids() { - let frame = Frame3 { - origin: [0.0; 3], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }; - let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; - let host = primitives::cylinder("host".into(), frame, 1.0, 2.0, accuracy).unwrap(); - let cutter = primitives::cylinder("cutter".into(), frame, 1.0, 0.8, accuracy).unwrap(); - let cutter = crate::analytic::placement::placed( - &cutter, - Frame3 { - origin: [0.0, 0.6, 0.0], - ..Frame3::IDENTITY - }, - 1.0, - ) - .unwrap(); - let result = boolean_brep(&host, &cutter, BooleanOp::Subtraction, "result".into()).unwrap(); - assert_eq!(result.brep.solids.len(), 2); - result.brep.validate().unwrap(); - tessellate(&result.brep, 0.01, 2_000_000).unwrap(); - } - #[test] - fn coextensive_unequal_cylinders_produce_exact_radial_results() { - let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; - let outer = - primitives::cylinder("outer".into(), Frame3::IDENTITY, 2.0, 3.0, accuracy).unwrap(); - let inner = - primitives::cylinder("inner".into(), Frame3::IDENTITY, 1.0, 3.0, accuracy).unwrap(); - let union = boolean_brep(&outer, &inner, BooleanOp::Union, "union".into()).unwrap(); - let intersection = boolean_brep( - &outer, - &inner, - BooleanOp::Intersection, - "intersection".into(), - ) - .unwrap(); - let subtraction = - boolean_brep(&outer, &inner, BooleanOp::Subtraction, "tube".into()).unwrap(); - assert_eq!(union.brep.topology.faces.len(), 3); - assert_eq!(intersection.brep.topology.faces.len(), 3); - assert_eq!(subtraction.brep.topology.faces.len(), 4); - assert_eq!(subtraction.brep.topology.faces[2].trim.holes.len(), 1); - assert_eq!(subtraction.brep.topology.faces[3].trim.holes.len(), 1); - assert_eq!( - subtraction.brep.topology.faces[1].provenance.role, - FaceRole::Cut - ); - assert!(subtraction.brep.topology.faces[1].provenance.reversed); - assert_eq!(subtraction.report.face_mappings.len(), 6); - let measured = volume(&subtraction.brep).abs(); - let expected = std::f64::consts::PI * (4.0 - 1.0) * 3.0; - assert!( - (measured - expected).abs() < 0.2, - "{measured} != {expected}" - ); - subtraction.brep.validate().unwrap(); - tessellate(&subtraction.brep, 0.01, 2_000_000).unwrap(); - - let empty = boolean_brep(&inner, &outer, BooleanOp::Subtraction, "empty".into()).unwrap(); - assert!(empty.brep.solids.is_empty()); - let shifted = primitives::cylinder( - "shifted".into(), - Frame3 { - origin: [0.0, 0.0, 0.5], - ..Frame3::IDENTITY - }, - 1.0, - 3.0, - accuracy, - ) - .unwrap(); - assert!(matches!( - boolean_brep(&outer, &shifted, BooleanOp::Subtraction, "gap".into()), - Err(GeometryError::CoverageGap { .. }) - )); - } - #[test] - fn enclosed_unequal_cylinder_becomes_an_oriented_cavity_shell() { - let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; - let host = - primitives::cylinder("host".into(), Frame3::IDENTITY, 2.0, 4.0, accuracy).unwrap(); - let cutter = primitives::cylinder( - "cutter".into(), - Frame3 { - origin: [0.0, 0.0, 1.0], - ..Frame3::IDENTITY - }, - 0.75, - 2.0, - accuracy, - ) - .unwrap(); - let subtraction = - boolean_brep(&host, &cutter, BooleanOp::Subtraction, "cavity".into()).unwrap(); - assert_eq!(subtraction.brep.solids.len(), 1); - assert_eq!(subtraction.brep.topology.shells.len(), 2); - assert_eq!(subtraction.brep.solids[0].cavity_shells, vec![1]); - assert_eq!(subtraction.brep.topology.faces.len(), 6); - assert!(subtraction.brep.topology.faces[3..] - .iter() - .all(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed)); - let measured = volume(&subtraction.brep).abs(); - let expected = std::f64::consts::PI * (2.0_f64.powi(2) * 4.0 - 0.75_f64.powi(2) * 2.0); - assert!( - (measured - expected).abs() < 0.3, - "{measured} != {expected}" - ); - subtraction.brep.validate().unwrap(); - tessellate(&subtraction.brep, 0.01, 2_000_000).unwrap(); - - let union = boolean_brep(&host, &cutter, BooleanOp::Union, "union".into()).unwrap(); - let intersection = boolean_brep( - &host, - &cutter, - BooleanOp::Intersection, - "intersection".into(), - ) - .unwrap(); - assert_eq!(union.brep.topology.faces.len(), 3); - assert_eq!(intersection.brep.topology.faces.len(), 3); - let empty = boolean_brep(&cutter, &host, BooleanOp::Subtraction, "empty".into()).unwrap(); - assert!(empty.brep.solids.is_empty()); - } - #[test] - fn transverse_parallel_cylinders_build_two_arc_caps_and_shared_edges() { - let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; - let a = primitives::cylinder("a".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy).unwrap(); - let b = primitives::cylinder( - "b".into(), - Frame3 { - origin: [1.0, 0.0, 0.0], - ..Frame3::IDENTITY - }, - 1.0, - 2.0, - accuracy, - ) - .unwrap(); - let union = boolean_brep(&a, &b, BooleanOp::Union, "union".into()).unwrap(); - let intersection = - boolean_brep(&a, &b, BooleanOp::Intersection, "intersection".into()).unwrap(); - let subtraction = - boolean_brep(&a, &b, BooleanOp::Subtraction, "subtraction".into()).unwrap(); - for (name, result) in [ - ("union", &union), - ("intersection", &intersection), - ("subtraction", &subtraction), - ] { - assert_eq!(result.brep.solids.len(), 1, "{name}"); - assert_eq!(result.brep.topology.faces.len(), 4, "{name}"); - for cap in 2..4 { - assert_eq!( - result - .brep - .topology - .halfedges - .iter() - .filter(|halfedge| halfedge.face == Some(cap)) - .count(), - 2, - "{name} cap {cap}" - ); - } - result.brep.validate().unwrap(); - assert!(!tessellate(&result.brep, 0.01, 2_000_000) - .unwrap_or_else(|error| panic!("{name}: {error}")) - .indices - .is_empty()); - crate::analytic::exchange::export_step(&result.brep, "metre") - .unwrap_or_else(|error| panic!("{name} STEP: {error}")); - crate::analytic::ifc_exchange::prepare_ifc_body(&result.brep) - .unwrap_or_else(|error| panic!("{name} IFC: {error}")); - } - assert_eq!(union.report.face_mappings.len(), 6); - assert_eq!(intersection.report.face_mappings.len(), 6); - assert_eq!(subtraction.report.face_mappings.len(), 6); - assert_eq!( - subtraction.brep.topology.faces[1].provenance.role, - FaceRole::Cut - ); - assert!(subtraction.brep.topology.faces[1].provenance.reversed); - - let cylinder_volume = 2.0 * std::f64::consts::PI; - let intersection_volume = volume(&intersection.brep).abs(); - assert!( - (volume(&union.brep).abs() + intersection_volume - 2.0 * cylinder_volume).abs() < 0.2 - ); - assert!( - (volume(&subtraction.brep).abs() + intersection_volume - cylinder_volume).abs() < 0.2 - ); - - let reversed = primitives::cylinder( - "reversed".into(), - Frame3 { - origin: [1.0, 0.0, 2.0], - x: [1.0, 0.0, 0.0], - y: [0.0, -1.0, 0.0], - z: [0.0, 0.0, -1.0], - }, - 1.0, - 2.0, - accuracy, - ) - .unwrap(); - let reversed_result = boolean_brep( - &a, - &reversed, - BooleanOp::Intersection, - "reversed-result".into(), - ) - .unwrap(); - reversed_result.brep.validate().unwrap(); - assert!((volume(&reversed_result.brep).abs() - intersection_volume).abs() < 0.1); - - let shifted = primitives::cylinder( - "shifted".into(), - Frame3 { - origin: [1.0, 0.0, 0.25], - ..Frame3::IDENTITY - }, - 1.0, - 2.0, - accuracy, - ) - .unwrap(); - let shifted_union = boolean_brep(&a, &shifted, BooleanOp::Union, "gap".into()); - assert!( - matches!(shifted_union, Err(GeometryError::UnresolvedIntersection(_))), - "unexpected shifted-cylinder result: {:?}", - shifted_union.err() - ); - let tangent = primitives::cylinder( - "tangent".into(), - Frame3 { - origin: [2.0, 0.0, 0.0], - ..Frame3::IDENTITY - }, - 1.0, - 2.0, - accuracy, - ) - .unwrap(); - for (operation, solids) in [ - (BooleanOp::Union, 2), - (BooleanOp::Intersection, 0), - (BooleanOp::Subtraction, 1), - ] { - let contact = - boolean_brep(&a, &tangent, operation, format!("contact-{operation:?}")).unwrap(); - assert_eq!(contact.brep.solids.len(), solids); - assert_eq!(contact.report.contacts.len(), 2); - } - let near_tangent = primitives::cylinder( - "near-tangent".into(), - Frame3 { - origin: [2.0 + 2e-9, 0.0, 0.0], - ..Frame3::IDENTITY - }, - 1.0, - 2.0, - accuracy, - ) - .unwrap(); - assert!(matches!( - boolean_brep( - &a, - &near_tangent, - BooleanOp::Union, - "near-tangent-result".into() - ), - Err(GeometryError::UnresolvedIntersection(_)) - )); - let diagonal = primitives::cylinder( - "diagonal".into(), - Frame3 { - origin: [1.5, 1.5, 0.0], - ..Frame3::IDENTITY - }, - 1.0, - 2.0, - accuracy, - ) - .unwrap(); - let diagonal_union = - boolean_brep(&a, &diagonal, BooleanOp::Union, "diagonal-union".into()).unwrap(); - assert_eq!(diagonal_union.brep.solids.len(), 2); - assert!(diagonal_union.report.contacts.is_empty()); - } - #[test] - fn contained_parallel_cylinder_builds_an_eccentric_analytic_through_hole() { - let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; - let outer = - primitives::cylinder("outer".into(), Frame3::IDENTITY, 2.0, 2.0, accuracy).unwrap(); - let inner = primitives::cylinder( - "inner".into(), - Frame3 { - origin: [0.75, 0.0, 0.0], - ..Frame3::IDENTITY - }, - 0.5, - 2.0, - accuracy, - ) - .unwrap(); - let union = boolean_brep(&outer, &inner, BooleanOp::Union, "union".into()).unwrap(); - let intersection = boolean_brep( - &outer, - &inner, - BooleanOp::Intersection, - "intersection".into(), - ) - .unwrap(); - let subtraction = - boolean_brep(&outer, &inner, BooleanOp::Subtraction, "subtraction".into()).unwrap(); - assert_eq!(union.brep.topology.faces.len(), 3); - assert_eq!(intersection.brep.topology.faces.len(), 3); - assert_eq!(subtraction.brep.topology.faces.len(), 4); - assert_eq!(subtraction.brep.topology.faces[2].trim.holes.len(), 1); - assert_eq!(subtraction.brep.topology.faces[3].trim.holes.len(), 1); - assert_eq!( - subtraction.brep.topology.faces[1].provenance.role, - FaceRole::Cut - ); - assert!(subtraction.brep.topology.faces[1].provenance.reversed); - match subtraction.brep.geometry.surfaces[1] { - SurfaceGeometry::Cylinder { frame, radius } => { - assert_eq!(frame.origin, [0.75, 0.0, 0.0]); - assert_eq!(radius, 0.5); - } - _ => panic!("inner cut face lost its cylinder support"), - } - subtraction.brep.validate().unwrap(); - tessellate(&subtraction.brep, 0.01, 2_000_000).unwrap(); - crate::analytic::exchange::export_step(&subtraction.brep, "metre").unwrap(); - crate::analytic::ifc_exchange::prepare_ifc_body(&subtraction.brep).unwrap(); - let expected = std::f64::consts::PI * (4.0 - 0.25) * 2.0; - assert!((volume(&subtraction.brep).abs() - expected).abs() < 0.25); - - let touching = primitives::cylinder( - "touching".into(), - Frame3 { - origin: [1.5, 0.0, 0.0], - ..Frame3::IDENTITY - }, - 0.5, - 2.0, - accuracy, - ) - .unwrap(); - assert!(matches!( - boolean_brep( - &outer, - &touching, - BooleanOp::Subtraction, - "touching-result".into() - ), - Err(GeometryError::UnresolvedIntersection(_)) - )); - } - #[test] - fn noncoextensive_parallel_cylinders_intersect_and_cut_when_the_span_is_bounded() { - let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; - let host = - primitives::cylinder("host".into(), Frame3::IDENTITY, 1.0, 3.0, accuracy).unwrap(); - let short = primitives::cylinder( - "short".into(), - Frame3 { - origin: [1.0, 0.0, 1.0], - ..Frame3::IDENTITY - }, - 1.0, - 1.0, - accuracy, - ) - .unwrap(); - let intersection = boolean_brep( - &host, - &short, - BooleanOp::Intersection, - "intersection".into(), - ) - .unwrap(); - assert_eq!(intersection.brep.topology.faces.len(), 4); - assert_eq!(intersection.brep.solids.len(), 1); - assert!(intersection.brep.topology.faces[2..].iter().all(|face| face - .provenance - .sources - .len() - == 1 - && face.provenance.sources[0].entity == "short")); - intersection.brep.validate().unwrap(); - tessellate(&intersection.brep, 0.01, 2_000_000).unwrap(); - let lens_area = 2.0 * std::f64::consts::FRAC_PI_3 - 3.0_f64.sqrt() * 0.5; - assert!((volume(&intersection.brep).abs() - lens_area).abs() < 0.12); - let union = boolean_brep(&host, &short, BooleanOp::Union, "partial-union".into()).unwrap(); - let reversed_union = boolean_brep( - &short, - &host, - BooleanOp::Union, - "reversed-partial-union".into(), - ) - .unwrap(); - let subtraction = boolean_brep( - &host, - &short, - BooleanOp::Subtraction, - "partial-subtraction".into(), - ) - .unwrap(); - for (name, result) in [("union", &union), ("subtraction", &subtraction)] { - assert_eq!(result.brep.topology.faces.len(), 10, "{name}"); - assert_eq!(result.brep.solids.len(), 1, "{name}"); - result.brep.validate().unwrap(); - tessellate(&result.brep, 0.01, 2_000_000) - .unwrap_or_else(|error| panic!("{name}: {error}")); - crate::analytic::exchange::export_step(&result.brep, "metre") - .unwrap_or_else(|error| panic!("{name} STEP: {error}")); - crate::analytic::ifc_exchange::prepare_ifc_body(&result.brep) - .unwrap_or_else(|error| panic!("{name} IFC: {error}")); - } - assert_eq!( - subtraction.brep.topology.faces[3].provenance.role, - FaceRole::Cut - ); - assert!(subtraction.brep.topology.faces[3].provenance.reversed); - assert!(subtraction.brep.topology.faces[7..9] - .iter() - .all(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed)); - let host_volume = 3.0 * std::f64::consts::PI; - assert!( - (volume(&union.brep).abs() - (host_volume + std::f64::consts::PI - lens_area)).abs() - < 0.2 - ); - assert!((volume(&subtraction.brep).abs() - (host_volume - lens_area)).abs() < 0.2); - assert!((volume(&reversed_union.brep).abs() - volume(&union.brep).abs()).abs() < 0.1); - reversed_union.brep.validate().unwrap(); - - let through = primitives::cylinder( - "through".into(), - Frame3 { - origin: [1.0, 0.0, -1.0], - ..Frame3::IDENTITY - }, - 1.0, - 5.0, - accuracy, - ) - .unwrap(); - let cut = boolean_brep( - &host, - &through, - BooleanOp::Subtraction, - "through-cut".into(), - ) - .unwrap(); - assert_eq!(cut.brep.topology.faces.len(), 4); - assert_eq!(cut.brep.topology.faces[1].provenance.role, FaceRole::Cut); - assert!(cut.brep.topology.faces[1].provenance.reversed); - assert!(!cut.report.coincident); - cut.brep.validate().unwrap(); - tessellate(&cut.brep, 0.01, 2_000_000).unwrap(); - let expected_cut_volume = (std::f64::consts::PI - lens_area) * 3.0; - assert!((volume(&cut.brep).abs() - expected_cut_volume).abs() < 0.2); - } - #[test] - fn mixed_sphere_cylinder_containment_preserves_exact_cavity_supports() { - let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; - let cylinder_host = - primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 2.0, 4.0, accuracy).unwrap(); - let sphere_cutter = primitives::sphere( - "sphere".into(), - Frame3 { - origin: [0.0, 0.0, 2.0], - ..Frame3::IDENTITY - }, - 0.5, - accuracy, - ) - .unwrap(); - let cylindrical_cavity = boolean_brep( - &cylinder_host, - &sphere_cutter, - BooleanOp::Subtraction, - "cylindrical-cavity".into(), - ) - .unwrap(); - assert_eq!(cylindrical_cavity.brep.solids.len(), 1); - assert_eq!(cylindrical_cavity.brep.topology.shells.len(), 2); - assert_eq!(cylindrical_cavity.brep.solids[0].cavity_shells, vec![1]); - assert_eq!(cylindrical_cavity.brep.topology.faces.len(), 4); - assert_eq!( - cylindrical_cavity.brep.topology.faces[3].provenance.role, - FaceRole::Cut - ); - assert!( - cylindrical_cavity.brep.topology.faces[3] - .provenance - .reversed - ); - cylindrical_cavity.brep.validate().unwrap(); - tessellate(&cylindrical_cavity.brep, 0.01, 2_000_000).unwrap(); - - let sphere_host = - primitives::sphere("sphere-host".into(), Frame3::IDENTITY, 3.0, accuracy).unwrap(); - let cylinder_cutter = primitives::cylinder( - "cylinder-cutter".into(), - Frame3 { - origin: [0.0, 0.0, -0.5], - ..Frame3::IDENTITY - }, - 0.5, - 1.0, - accuracy, - ) - .unwrap(); - let spherical_cavity = boolean_brep( - &sphere_host, - &cylinder_cutter, - BooleanOp::Subtraction, - "spherical-cavity".into(), - ) - .unwrap(); - assert_eq!(spherical_cavity.brep.topology.faces.len(), 4); - assert_eq!(spherical_cavity.brep.solids[0].cavity_shells, vec![1]); - assert!(spherical_cavity.brep.topology.faces[1..] - .iter() - .all(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed)); - spherical_cavity.brep.validate().unwrap(); - tessellate(&spherical_cavity.brep, 0.01, 2_000_000).unwrap(); - - let union = boolean_brep( - &cylinder_host, - &sphere_cutter, - BooleanOp::Union, - "union".into(), - ) - .unwrap(); - let intersection = boolean_brep( - &cylinder_host, - &sphere_cutter, - BooleanOp::Intersection, - "intersection".into(), - ) - .unwrap(); - assert_eq!(union.brep.topology.faces.len(), 3); - assert_eq!(intersection.brep.topology.faces.len(), 1); - let crossing = primitives::sphere( - "crossing".into(), - Frame3 { - origin: [1.8, 0.0, 2.0], - ..Frame3::IDENTITY - }, - 0.5, - accuracy, - ) - .unwrap(); - for (name, left, right, operation) in [ - ( - "crossing-union", - &cylinder_host, - &crossing, - BooleanOp::Union, - ), - ( - "crossing-intersection", - &cylinder_host, - &crossing, - BooleanOp::Intersection, - ), - ( - "crossing-cylinder-cut", - &cylinder_host, - &crossing, - BooleanOp::Subtraction, - ), - ( - "crossing-sphere-cut", - &crossing, - &cylinder_host, - BooleanOp::Subtraction, - ), - ] { - let result = boolean_brep(left, right, operation, name.into()) - .unwrap_or_else(|error| panic!("{name}: {error}")); - assert!( - matches!(result.report.quality, GeometryQuality::Analytic), - "{name}" - ); - assert_eq!(result.brep.solids.len(), 1, "{name}"); - assert!(result.brep.topology.faces.len() >= 2, "{name}"); - assert!(!result.brep.topology.edges.is_empty(), "{name}"); - assert!( - result - .brep - .topology - .faces - .iter() - .all(|face| !face.provenance.sources.is_empty()), - "{name}" - ); - assert!(result - .brep - .geometry - .curves - .iter() - .any(|curve| matches!(curve, CurveGeometry::Intersection { .. }))); - result.brep.validate().unwrap(); - tessellate(&result.brep, 0.01, 2_000_000) - .unwrap_or_else(|error| panic!("{name} tessellation: {error}")); - } - } - #[test] - fn coaxial_sphere_cylinder_intersection_builds_shared_analytic_circles() { - let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; - let sphere = primitives::sphere("sphere".into(), Frame3::IDENTITY, 1.5, accuracy).unwrap(); - let cylinder = primitives::cylinder( - "cylinder".into(), - Frame3 { - origin: [0.0, 0.0, -2.0], - ..Frame3::IDENTITY - }, - 0.75, - 4.0, - accuracy, - ) - .unwrap(); - let union = boolean_brep(&sphere, &cylinder, BooleanOp::Union, "union".into()).unwrap(); - let intersection = boolean_brep( - &sphere, - &cylinder, - BooleanOp::Intersection, - "intersection".into(), - ) - .unwrap(); - let sphere_cut = boolean_brep( - &sphere, - &cylinder, - BooleanOp::Subtraction, - "sphere-cut".into(), - ) - .unwrap(); - let cylinder_cut = boolean_brep( - &cylinder, - &sphere, - BooleanOp::Subtraction, - "cylinder-cut".into(), - ) - .unwrap(); - assert_eq!(union.brep.topology.faces.len(), 5); - assert_eq!(intersection.brep.topology.faces.len(), 3); - assert_eq!(sphere_cut.brep.topology.faces.len(), 2); - assert_eq!(cylinder_cut.brep.topology.faces.len(), 6); - assert_eq!(cylinder_cut.brep.solids.len(), 2); - assert!(sphere_cut - .brep - .topology - .faces - .iter() - .any(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed)); - assert_eq!( - cylinder_cut - .brep - .topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed) - .count(), - 2 - ); - for (name, result) in [ - ("union", &union), - ("intersection", &intersection), - ("sphere-cut", &sphere_cut), - ("cylinder-cut", &cylinder_cut), - ] { - result.brep.validate().unwrap(); - let mesh = tessellate(&result.brep, 0.01, 2_000_000) - .unwrap_or_else(|error| panic!("{name}: {error}")); - assert!(mesh.indices.len() > 0); - assert_eq!(result.report.face_mappings.len(), 4); - crate::analytic::exchange::export_step(&result.brep, "metre") - .unwrap_or_else(|error| panic!("{name} STEP: {error}")); - crate::analytic::ifc_exchange::prepare_ifc_body(&result.brep) - .unwrap_or_else(|error| panic!("{name} IFC: {error}")); - } - let sphere_volume = 4.0 * std::f64::consts::PI * 1.5_f64.powi(3) / 3.0; - let cylinder_volume = std::f64::consts::PI * 0.75_f64.powi(2) * 4.0; - let intersection_volume = volume(&intersection.brep).abs(); - assert!( - (volume(&union.brep).abs() + intersection_volume - sphere_volume - cylinder_volume) - .abs() - < 0.3 - ); - assert!((volume(&sphere_cut.brep).abs() + intersection_volume - sphere_volume).abs() < 0.3); - assert!( - (volume(&cylinder_cut.brep).abs() + intersection_volume - cylinder_volume).abs() < 0.3 - ); - - let noncoaxial = primitives::cylinder( - "noncoaxial".into(), - Frame3 { - origin: [0.2, 0.0, -2.0], - ..Frame3::IDENTITY - }, - 0.75, - 4.0, - accuracy, - ) - .unwrap(); - assert!(matches!( - boolean_brep(&sphere, &noncoaxial, BooleanOp::Union, "gap".into()), - Err(GeometryError::CoverageGap { .. }) - )); - } - #[test] - fn mixed_cuboid_containment_builds_exact_curved_and_planar_cavities() { - let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; - let box_host = primitives::cuboid( - "box".into(), - Frame3 { - origin: [-2.0, -2.0, -2.0], - ..Frame3::IDENTITY - }, - [4.0; 3], - accuracy, - ) - .unwrap(); - let sphere_cutter = - primitives::sphere("sphere".into(), Frame3::IDENTITY, 0.5, accuracy).unwrap(); - let cylinder_cutter = primitives::cylinder( - "cylinder".into(), - Frame3 { - origin: [0.0, 0.0, -0.5], - ..Frame3::IDENTITY - }, - 0.5, - 1.0, - accuracy, - ) - .unwrap(); - for (name, cutter, expected_faces) in [ - ("sphere", &sphere_cutter, 7), - ("cylinder", &cylinder_cutter, 9), - ] { - let result = boolean_brep( - &box_host, - cutter, - BooleanOp::Subtraction, - format!("{name}-cavity"), - ) - .unwrap(); - assert_eq!(result.brep.solids.len(), 1); - assert_eq!(result.brep.topology.shells.len(), 2); - assert_eq!(result.brep.solids[0].cavity_shells, vec![1]); - assert_eq!(result.brep.topology.faces.len(), expected_faces); - assert!(result.brep.topology.faces[6..] - .iter() - .all(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed)); - result.brep.validate().unwrap(); - tessellate(&result.brep, 0.01, 2_000_000).unwrap(); - } - - let sphere_host = - primitives::sphere("sphere-host".into(), Frame3::IDENTITY, 4.0, accuracy).unwrap(); - let small_box = primitives::cuboid( - "small-box".into(), - Frame3 { - origin: [-0.5; 3], - ..Frame3::IDENTITY - }, - [1.0; 3], - accuracy, - ) - .unwrap(); - let sphere_box = boolean_brep( - &sphere_host, - &small_box, - BooleanOp::Subtraction, - "sphere-box".into(), - ) - .unwrap(); - assert_eq!(sphere_box.brep.topology.faces.len(), 7); - assert_eq!(sphere_box.brep.solids[0].cavity_shells, vec![1]); - - let cylinder_host = primitives::cylinder( - "cylinder-host".into(), - Frame3 { - origin: [0.0, 0.0, -2.0], - ..Frame3::IDENTITY - }, - 3.0, - 4.0, - accuracy, - ) - .unwrap(); - let cylinder_box = boolean_brep( - &cylinder_host, - &small_box, - BooleanOp::Subtraction, - "cylinder-box".into(), - ) - .unwrap(); - assert_eq!(cylinder_box.brep.topology.faces.len(), 9); - assert_eq!(cylinder_box.brep.solids[0].cavity_shells, vec![1]); - - let crossing = primitives::sphere( - "crossing".into(), - Frame3 { - origin: [1.8, 0.0, 0.0], - ..Frame3::IDENTITY - }, - 0.5, - accuracy, - ) - .unwrap(); - let crossing_union = boolean_brep( - &box_host, - &crossing, - BooleanOp::Union, - "crossing-union".into(), - ) - .unwrap(); - assert_eq!(crossing_union.brep.topology.faces.len(), 7); - assert_eq!( - crossing_union - .brep - .topology - .faces - .iter() - .filter(|face| !face.trim.holes.is_empty()) - .count(), - 1 - ); - crossing_union.brep.validate().unwrap(); - tessellate(&crossing_union.brep, 0.01, 2_000_000).unwrap(); - } - #[test] - fn transverse_spheres_preserve_surfaces_shared_edge_and_occupancy_volumes() { - let a = sphere("a", [0.0; 3], 1.0); - let b = sphere("b", [1.0, 0.0, 0.0], 1.0); - let unit_volume = 4.0 * std::f64::consts::PI / 3.0; - let lens = 5.0 * std::f64::consts::PI / 12.0; - for (op, expected) in [ - (BooleanOp::Union, 2.0 * unit_volume - lens), - (BooleanOp::Intersection, lens), - (BooleanOp::Subtraction, unit_volume - lens), - ] { - let r = boolean_spheres(&a, &b, op, "r".into()).unwrap(); - assert_eq!(r.brep.topology.faces.len(), 2); - let occupied = |point: Point3| { - let inside_a = norm(sub(point, [0.0; 3])) < 1.0; - let inside_b = norm(sub(point, [1.0, 0.0, 0.0])) < 1.0; - match op { - BooleanOp::Union => inside_a || inside_b, - BooleanOp::Intersection => inside_a && inside_b, - BooleanOp::Subtraction => inside_a && !inside_b, - } - }; - for face in &r.brep.topology.faces { - let surface = r.brep.geometry.surface(face.surface).unwrap(); - let v = face.trim.uv_bounds[1]; - for fraction in [0.25, 0.5, 0.75] { - let uv = [0.9, v.lo + fraction * v.width()]; - let point = surface.point_at(uv).unwrap(); - let outward = scale(surface.normal_at(uv).unwrap(), face.sense.multiplier()); - assert!(occupied(sub(point, scale(outward, 1e-5)))); - assert!(!occupied(crate::analytic::geometry::add( - point, - scale(outward, 1e-5) - ))); - } - } - assert!(r - .brep - .geometry - .surfaces - .iter() - .all(|s| matches!(s, SurfaceGeometry::Sphere { .. }))); - let shared: Vec<_> = r - .brep - .topology - .edges - .iter() - .filter(|e| !e.chart_seam) - .collect(); - assert_eq!(shared.len(), 1); - assert!(shared[0].twin_halfedge.is_some()); - assert!((volume(&r.brep) - expected).abs() < 0.04); - assert_eq!(r.report.face_mappings[0].result_faces, vec![0]); - assert_eq!(r.report.face_mappings[1].result_faces, vec![1]); - assert_eq!( - r.brep.topology.faces[1].provenance.reversed, - op == BooleanOp::Subtraction - ); - } - } - #[test] - fn enclosed_subtraction_orients_cavity_and_preserves_ancestry() { - let a = sphere("host", [0.0; 3], 2.0); - let b = sphere("cutter", [0.2, 0.0, 0.0], 0.5); - let r = boolean_spheres(&a, &b, BooleanOp::Subtraction, "r".into()).unwrap(); - assert_eq!(r.brep.solids[0].cavity_shells, vec![1]); - assert_eq!(r.brep.topology.faces[1].sense, Orientation::Reverse); - assert_eq!( - r.brep.topology.faces[1].provenance.sources[0].entity, - "cutter" - ); - assert!((volume(&r.brep) - 4.0 * std::f64::consts::PI / 3.0 * (8.0 - 0.125)).abs() < 0.1); - } - #[test] - fn contact_disjoint_containment_and_coincident_regularize_deterministically() { - let a = sphere("a", [0.0; 3], 1.0); - for x in [2.0, 2.5] { - let b = sphere("b", [x, 0.0, 0.0], 1.0); - let r = boolean_spheres(&a, &b, BooleanOp::Union, "r".into()).unwrap(); - assert_eq!(r.brep.solids.len(), 2); - assert_eq!(r.report.contacts.len(), usize::from(x == 2.0)); - assert!(boolean_spheres(&a, &b, BooleanOp::Intersection, "r".into()) - .unwrap() - .brep - .solids - .is_empty()); - } - let b = sphere("b", [0.0; 3], 1.0); - let r = boolean_spheres(&a, &b, BooleanOp::Union, "r".into()).unwrap(); - assert!(r.report.coincident); - assert_eq!(r.brep.topology.faces[0].provenance.sources.len(), 2); - assert!(boolean_spheres(&a, &b, BooleanOp::Subtraction, "r".into()) - .unwrap() - .brep - .topology - .faces - .is_empty()); - let big = sphere("big", [0.0; 3], 2.0); - assert!( - boolean_spheres(&a, &big, BooleanOp::Subtraction, "r".into()) - .unwrap() - .brep - .solids - .is_empty() - ); - assert_eq!( - boolean_spheres(&a, &big, BooleanOp::Intersection, "r".into()) - .unwrap() - .brep - .topology - .faces[0] - .provenance - .sources[0] - .entity, - "a" - ); - } - #[test] - fn unsupported_inputs_and_invalid_geometry_never_enter_a_mesh_bridge() { - let a = sphere("a", [0.0; 3], 1.0); - let cylinder = - primitives::cylinder("c".into(), Frame3::IDENTITY, 1.0, 2.0, a.accuracy).unwrap(); - assert!(matches!( - boolean_spheres(&a, &cylinder, BooleanOp::Union, "r".into()), - Err(GeometryError::CoverageGap { .. }) - )); - let mut invalid = a.clone(); - invalid.topology.faces[0].surface = 100; - assert!(matches!( - boolean_spheres(&a, &invalid, BooleanOp::Union, "r".into()), - Err(GeometryError::MissingReference { .. }) - )); - let mut trimmed = a.clone(); - trimmed.topology.faces[0].trim.uv_bounds[1].hi = 0.5; - assert!(boolean_spheres(&a, &trimmed, BooleanOp::Union, "r".into()).is_err()); - } - - #[test] - fn provably_disjoint_surface_families_use_exact_regularized_results() { - let cylinder_accuracy = sphere("reference", [0.0; 3], 1.0).accuracy; - let torus_accuracy = Accuracy { - geometric: cylinder_accuracy.geometric * 2.0, - intersection: cylinder_accuracy.intersection * 2.0, - tessellation: cylinder_accuracy.tessellation * 2.0, - exchange: cylinder_accuracy.exchange * 2.0, - }; - let cylinder = primitives::cylinder( - "cylinder".into(), - Frame3::IDENTITY, - 1.0, - 2.0, - cylinder_accuracy, - ) - .unwrap(); - let torus = primitives::torus( - "torus".into(), - Frame3 { - origin: [12.0, 0.0, 0.0], - ..Frame3::IDENTITY - }, - 3.0, - 1.0, - torus_accuracy, - ) - .unwrap(); - - let union = boolean_brep(&cylinder, &torus, BooleanOp::Union, "union".into()).unwrap(); - assert_eq!(union.brep.solids.len(), 2); - assert_eq!(union.brep.accuracy.geometric, torus_accuracy.geometric); - assert_eq!(union.brep.topology.faces.len(), 4); - assert_eq!(union.report.face_mappings.len(), 4); - assert!(union - .brep - .topology - .faces - .iter() - .all(|face| face.provenance.role == FaceRole::Preserved)); - - let intersection = boolean_brep( - &cylinder, - &torus, - BooleanOp::Intersection, - "intersection".into(), - ) - .unwrap(); - assert!(intersection.brep.solids.is_empty()); - assert!(intersection - .report - .face_mappings - .iter() - .all(|mapping| mapping.result_faces.is_empty())); - - let subtraction = boolean_brep( - &cylinder, - &torus, - BooleanOp::Subtraction, - "subtraction".into(), - ) - .unwrap(); - assert_eq!(subtraction.brep.solids.len(), 1); - assert_eq!(subtraction.brep.topology.faces.len(), 3); - assert_eq!( - subtraction.brep.topology.faces[0].provenance.sources[0].entity, - "cylinder" - ); - } - #[test] - fn positive_similarity_roundoff_does_not_reject_full_sphere_operands() { - let rotation = std::f64::consts::PI / 7.0; - let placement = Frame3 { - origin: [0.2, 0.1, 0.2], - x: [rotation.cos(), 0.0, -rotation.sin()], - y: [0.0, 1.0, 0.0], - z: [rotation.sin(), 0.0, rotation.cos()], - }; - let frame = Frame3 { - origin: [0.0, 1.2, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - ..Frame3::IDENTITY - }; - let first = primitives::sphere( - "a".into(), - frame, - 1.0, - sphere("reference", [0.0; 3], 1.0).accuracy, - ) - .unwrap(); - let second = primitives::sphere( - "b".into(), - Frame3 { - origin: [1.0, 1.2, 0.0], - ..frame - }, - 1.0, - first.accuracy, - ) - .unwrap(); - let first = super::super::placement::placed(&first, placement, 1.25).unwrap(); - let second = super::super::placement::placed(&second, placement, 1.25).unwrap(); - for op in [ - BooleanOp::Union, - BooleanOp::Intersection, - BooleanOp::Subtraction, - ] { - let result = boolean_spheres(&first, &second, op, "placed".into()).unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.topology.faces.len(), 2); - assert_eq!(result.report.face_mappings[0].source.entity, "a"); - } - } - #[test] - fn internal_tangency_and_unresolved_small_caps_are_errors() { - let a = sphere("a", [0.0; 3], 2.0); - let b = sphere("b", [1.0, 0.0, 0.0], 1.0); - assert!(matches!( - boolean_spheres(&a, &b, BooleanOp::Subtraction, "r".into()), - Err(GeometryError::UnresolvedIntersection(_)) - )); - let a = sphere("a", [0.0; 3], 1.0); - let b = sphere("b", [2.0 - 1e-10, 0.0, 0.0], 1.0); - assert!(matches!( - boolean_spheres(&a, &b, BooleanOp::Intersection, "r".into()), - Err(GeometryError::UnresolvedIntersection(_)) - )); - } - #[test] - fn rotated_unequal_spheres_roundtrip_and_keep_identical_boundary_positions() { - let a = sphere("a", [5.0, -3.0, 8.0], 2.0); - let b = sphere("b", [6.0, -2.0, 9.0], 1.25); - let r = boolean_spheres(&a, &b, BooleanOp::Subtraction, "r".into()).unwrap(); - let decoded = BrepEnvelope::from_json(&r.brep.to_json().unwrap()).unwrap(); - let mesh = tessellate(&decoded, 0.02, 2_000_000).unwrap(); - let boundary = decoded.geometry.curves[0].elementary_point(0.0).unwrap(); - let count = mesh - .positions - .chunks_exact(3) - .filter(|p| p == &boundary.as_slice()) - .count(); - assert!(count >= 2); - assert!(mesh.indices.len() > 100); - } - #[test] - fn sphere_booleans_obey_scaled_budgets_and_building_coordinates() { - for size in [1e-6, 1.0, 1e6] { - let accuracy = Accuracy { - geometric: size * 1e-9, - intersection: size * 1e-10, - tessellation: size * 0.01, - exchange: size * 1e-5, - }; - let a = primitives::sphere("a".into(), Frame3::IDENTITY, size, accuracy).unwrap(); - let b = primitives::sphere( - "b".into(), - Frame3 { - origin: [size, 0.0, 0.0], - ..Frame3::IDENTITY - }, - size, - accuracy, - ) - .unwrap(); - let r = boolean_spheres(&a, &b, BooleanOp::Subtraction, "r".into()).unwrap(); - assert_eq!(r.brep.topology.faces.len(), 2); - tessellate(&r.brep, size * 0.02, 2_000_000).unwrap(); - } - let accuracy = Accuracy { - geometric: 1e-6, - intersection: 5e-7, - tessellation: 0.01, - exchange: 1e-5, - }; - let a = primitives::sphere( - "a".into(), - Frame3 { - origin: [1e9; 3], - ..Frame3::IDENTITY - }, - 1.0, - accuracy, - ) - .unwrap(); - let b = primitives::sphere( - "b".into(), - Frame3 { - origin: [1e9 + 1.0, 1e9, 1e9], - ..Frame3::IDENTITY - }, - 1.0, - accuracy, - ) - .unwrap(); - let r = boolean_spheres(&a, &b, BooleanOp::Intersection, "r".into()).unwrap(); - tessellate(&r.brep, 0.01, 2_000_000).unwrap(); - } - - #[test] - fn concentric_torus_booleans_preserve_exact_cavity_supports() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let host = primitives::torus("host".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy).unwrap(); - let cutter = - primitives::torus("cutter".into(), Frame3::IDENTITY, 3.0, 0.4, accuracy).unwrap(); - let subtraction = - boolean_brep(&host, &cutter, BooleanOp::Subtraction, "torus-shell".into()).unwrap(); - subtraction.brep.validate().unwrap(); - assert_eq!(subtraction.brep.solids.len(), 1); - assert_eq!(subtraction.brep.solids[0].cavity_shells.len(), 1); - assert!(subtraction - .brep - .topology - .faces - .iter() - .any(|face| { face.provenance.role == FaceRole::Cut && face.provenance.reversed })); - assert!(subtraction - .brep - .geometry - .surfaces - .iter() - .all(|surface| matches!(surface, SurfaceGeometry::Torus { .. }))); - tessellate(&subtraction.brep, 0.05, 2_000_000).unwrap(); - - let intersection = - boolean_brep(&host, &cutter, BooleanOp::Intersection, "torus-core".into()).unwrap(); - assert_eq!(intersection.brep.topology.faces.len(), 1); - assert!(matches!( - intersection.brep.geometry.surfaces[0], - SurfaceGeometry::Torus { - minor_radius: 0.4, - .. - } - )); - - let coincident = - boolean_brep(&host, &host, BooleanOp::Subtraction, "empty-torus".into()).unwrap(); - assert!(coincident.report.coincident); - assert!(coincident.brep.solids.is_empty()); - } - - #[test] - fn sphere_torus_containment_is_exact_in_both_directions() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let torus = - primitives::torus("torus".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy).unwrap(); - let small_sphere = sphere("small", [3.0, 0.0, 0.0], 0.25); - let torus_cut = boolean_brep( - &torus, - &small_sphere, - BooleanOp::Subtraction, - "torus-cut".into(), - ) - .unwrap(); - assert_eq!(torus_cut.brep.solids[0].cavity_shells.len(), 1); - assert!(torus_cut.brep.geometry.surfaces.iter().any( - |surface| matches!(surface, SurfaceGeometry::Sphere { radius, .. } if *radius == 0.25) - )); - - let large_sphere = sphere("large", [0.0; 3], 5.0); - let sphere_cut = boolean_brep( - &large_sphere, - &torus, - BooleanOp::Subtraction, - "sphere-cut".into(), - ) - .unwrap(); - sphere_cut.brep.validate().unwrap(); - assert_eq!(sphere_cut.brep.solids[0].cavity_shells.len(), 1); - assert!(sphere_cut - .brep - .topology - .faces - .iter() - .any(|face| face.provenance.role == FaceRole::Cut)); - } - - fn extrusion(id: &str, outer: Vec<[f64; 2]>, holes: Vec>) -> BrepEnvelope { - extrusion_span(id, 0.0, 3.0, outer, holes) - } - - fn extrusion_span( - id: &str, - origin_z: f64, - height: f64, - outer: Vec<[f64; 2]>, - holes: Vec>, - ) -> BrepEnvelope { - primitives::linear_extrusion( - id.into(), - Frame3 { - origin: [0.0, 0.0, origin_z], - ..Frame3::IDENTITY - }, - outer, - holes, - height, - Accuracy { - geometric: 1.0e-9, - intersection: 1.0e-10, - tessellation: 0.01, - exchange: 1.0e-5, - }, - ) - .unwrap() - } - - #[test] - fn planar_extrusion_round_through_cutter_keeps_analytic_brep() { - let host = extrusion( - "round-host", - vec![[0.0, 0.0], [4.0, 0.0], [4.0, 0.3], [0.0, 0.3]], - Vec::new(), - ); - let cutter = primitives::cylinder( - "round-opening".into(), - Frame3 { - origin: [2.0, -0.5, 1.5], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }, - 0.5, - 1.3, - host.accuracy, - ) - .unwrap(); - let result = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - "round-host-cut".into(), - ) - .unwrap(); - assert_eq!(result.brep.solids.len(), 1); - result.brep.validate().unwrap(); - assert_eq!( - classify_point(&result.brep, [2.0, 0.15, 1.5]).unwrap(), - PointClassification::Outside - ); - assert_eq!( - classify_point(&result.brep, [2.0, 0.15, 2.2]).unwrap(), - PointClassification::Inside - ); - let cut_face = result - .brep - .topology - .faces - .iter() - .find(|face| face.provenance.role == FaceRole::Cut) - .unwrap(); - assert_eq!( - face_contains_uv(&result.brep, cut_face, [0.0, 0.65]).unwrap(), - Some(true) - ); - tessellate(&result.brep, 0.01, 2_000_000).unwrap(); - crate::analytic::exchange::export_step(&result.brep, "metre").unwrap(); - } - - #[test] - fn rectangular_cut_after_round_cut_never_loses_the_cylindrical_cap() { - let host = extrusion( - "arched-host", - vec![[0.0, 0.0], [4.0, 0.0], [4.0, 0.3], [0.0, 0.3]], - Vec::new(), - ); - let round = primitives::cylinder( - "arched-cap".into(), - Frame3 { - origin: [2.0, -0.5, 2.0], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }, - 0.5, - 1.3, - host.accuracy, - ) - .unwrap(); - let lower = primitives::linear_extrusion( - "arched-lower".into(), - Frame3 { - origin: [0.0, 0.8, 0.0], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, 1.0], - z: [0.0, -1.0, 0.0], - }, - vec![[1.5, 0.5], [2.5, 0.5], [2.5, 2.0], [1.5, 2.0]], - Vec::new(), - 1.3, - host.accuracy, - ) - .unwrap(); - let batch = subtract_planar_cutters( - &host, - &[round.clone(), lower.clone()], - "arched-batch".into(), - ) - .unwrap(); - batch.brep.validate().unwrap(); - assert_eq!( - classify_point(&batch.brep, [2.0, 0.15, 2.2]).unwrap(), - PointClassification::Outside - ); - assert_eq!( - classify_point(&batch.brep, [2.0, 0.15, 1.0]).unwrap(), - PointClassification::Outside - ); - assert!(batch.brep.topology.faces.iter().any(|face| { - matches!( - batch.brep.geometry.surfaces[face.surface as usize], - SurfaceGeometry::Cylinder { .. } - ) - })); - for source in ["arched-cap", "arched-lower"] { - assert!(batch.report.face_mappings.iter().any(|mapping| { - mapping.source.entity == source && !mapping.result_faces.is_empty() - })); - } - let expected = 4.0 * 0.3 * 3.0 - (1.0 * 1.5 + std::f64::consts::PI * 0.5 * 0.5 / 2.0) * 0.3; - assert!((volume_at_deflection(&batch.brep, 0.0001).abs() - expected).abs() < 1.0e-4); - crate::analytic::exchange::export_step(&batch.brep, "metre").unwrap(); - let cap = boolean_brep(&host, &round, BooleanOp::Subtraction, "cap".into()).unwrap(); - match boolean_brep(&cap.brep, &lower, BooleanOp::Subtraction, "arch".into()) { - Ok(result) => { - result.brep.validate().unwrap(); - assert!(result.brep.topology.faces.iter().any(|face| { - matches!( - result.brep.geometry.surfaces[face.surface as usize], - SurfaceGeometry::Cylinder { .. } - ) - })); - assert_eq!( - classify_point(&result.brep, [2.0, 0.15, 2.2]).unwrap(), - PointClassification::Outside - ); - assert_eq!( - classify_point(&result.brep, [2.0, 0.15, 1.0]).unwrap(), - PointClassification::Outside - ); - } - Err(GeometryError::CoverageGap { .. }) => {} - Err(error) => panic!("unexpected chained arch failure: {error:?}"), - } - } - - #[test] - fn disjoint_mixed_cutter_batch_preserves_both_openings_in_any_order() { - let host = extrusion( - "mixed-host", - vec![[0.0, 0.0], [6.0, 0.0], [6.0, 0.3], [0.0, 0.3]], - Vec::new(), - ); - let round = primitives::cylinder( - "round".into(), - Frame3 { - origin: [1.5, -0.5, 1.5], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }, - 0.4, - 1.3, - host.accuracy, - ) - .unwrap(); - let rect = primitives::linear_extrusion( - "rectangle".into(), - Frame3 { - origin: [0.0, 0.8, 0.0], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, 1.0], - z: [0.0, -1.0, 0.0], - }, - vec![[3.5, 0.7], [4.5, 0.7], [4.5, 2.2], [3.5, 2.2]], - Vec::new(), - 1.3, - host.accuracy, - ) - .unwrap(); - for cutters in [[round.clone(), rect.clone()], [rect.clone(), round.clone()]] { - let result = subtract_planar_cutters(&host, &cutters, "mixed".into()).unwrap(); - result.brep.validate().unwrap(); - assert_eq!( - classify_point(&result.brep, [1.5, 0.15, 1.5]).unwrap(), - PointClassification::Outside - ); - assert_eq!( - classify_point(&result.brep, [4.0, 0.15, 1.5]).unwrap(), - PointClassification::Outside - ); - assert_eq!( - classify_point(&result.brep, [2.5, 0.15, 1.5]).unwrap(), - PointClassification::Inside - ); - let expected_volume = - 6.0 * 0.3 * 3.0 - 1.0 * 0.3 * 1.5 - std::f64::consts::PI * 0.4 * 0.4 * 0.3; - let measured_volume = volume_at_deflection(&result.brep, 0.0001).abs(); - assert!( - (measured_volume - expected_volume).abs() < 1.0e-4, - "mixed volume {measured_volume} differs from expected {expected_volume}", - ); - assert!(result.brep.topology.faces.iter().any(|face| { - matches!( - result.brep.geometry.surfaces[face.surface as usize], - SurfaceGeometry::Cylinder { .. } - ) - })); - for source in ["round", "rectangle"] { - assert!( - result.brep.topology.faces.iter().any(|face| { - face.provenance - .sources - .iter() - .any(|origin| origin.entity == source) - }), - "missing source {source}: {:?}", - result - .brep - .topology - .faces - .iter() - .flat_map(|face| face - .provenance - .sources - .iter() - .map(|origin| &origin.entity)) - .collect::>() - ); - assert!(result.report.face_mappings.iter().any(|mapping| { - mapping.source.entity == source && !mapping.result_faces.is_empty() - })); - } - crate::analytic::exchange::export_step(&result.brep, "metre").unwrap(); - } - } - - #[test] - fn two_round_cutter_batch_keeps_both_analytic_voids() { - let host = extrusion( - "two-round-host", - vec![[0.0, 0.0], [6.0, 0.0], [6.0, 0.3], [0.0, 0.3]], - Vec::new(), - ); - let make_round = |name: &str, station: f64| { - primitives::cylinder( - name.into(), - Frame3 { - origin: [station, 0.0, 1.5], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }, - 0.4, - 0.3, - host.accuracy, - ) - .unwrap() - }; - let first = make_round("round-a", 1.5); - let second = make_round("round-b", 4.5); - for cutters in [ - [first.clone(), second.clone()], - [second.clone(), first.clone()], - ] { - let result = subtract_planar_cutters(&host, &cutters, "two-round".into()).unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.solids.len(), 1); - for station in [1.5, 4.5] { - assert_eq!( - classify_point(&result.brep, [station, 0.15, 1.5]).unwrap(), - PointClassification::Outside - ); - } - assert_eq!( - classify_point(&result.brep, [3.0, 0.15, 1.5]).unwrap(), - PointClassification::Inside - ); - let expected = 6.0 * 0.3 * 3.0 - 2.0 * std::f64::consts::PI * 0.4 * 0.4 * 0.3; - assert!((volume_at_deflection(&result.brep, 0.0001).abs() - expected).abs() < 1.0e-4); - for source in ["round-a", "round-b"] { - assert!(result - .report - .face_mappings - .iter() - .any(|mapping| mapping.source.entity == source - && !mapping.result_faces.is_empty())); - } - let (_, report) = - crate::analytic::exchange::export_step(&result.brep, "metre").unwrap(); - assert_eq!(report.solids, 1); - } - } - - #[test] - fn mixed_profile_batch_preserves_disconnected_full_height_host_parts() { - let host = extrusion( - "split-mixed-host", - vec![[0.0, 0.0], [6.0, 0.0], [6.0, 0.3], [0.0, 0.3]], - Vec::new(), - ); - let round = primitives::cylinder( - "split-round".into(), - Frame3 { - origin: [1.5, -0.5, 1.5], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }, - 0.4, - 1.3, - host.accuracy, - ) - .unwrap(); - let through = primitives::linear_extrusion( - "full-height".into(), - Frame3 { - origin: [0.0, 0.3, 0.0], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, 1.0], - z: [0.0, -1.0, 0.0], - }, - vec![[2.5, 0.0], [3.5, 0.0], [3.5, 3.0], [2.5, 3.0]], - Vec::new(), - 0.3, - host.accuracy, - ) - .unwrap(); - for cutters in [ - [round.clone(), through.clone()], - [through.clone(), round.clone()], - ] { - let result = subtract_planar_cutters(&host, &cutters, "split-mixed".into()).unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.solids.len(), 2); - for point in [[1.5, 0.15, 1.5], [3.0, 0.15, 1.5]] { - assert_eq!( - classify_point(&result.brep, point).unwrap(), - PointClassification::Outside - ); - } - for point in [[0.5, 0.15, 1.5], [5.0, 0.15, 1.5]] { - assert_eq!( - classify_point(&result.brep, point).unwrap(), - PointClassification::Inside - ); - } - let expected = - 6.0 * 0.3 * 3.0 - 1.0 * 0.3 * 3.0 - std::f64::consts::PI * 0.4 * 0.4 * 0.3; - assert!((volume_at_deflection(&result.brep, 0.0001).abs() - expected).abs() < 1.0e-4); - for source in ["split-round", "full-height"] { - assert!(result - .report - .face_mappings - .iter() - .any(|mapping| mapping.source.entity == source - && !mapping.result_faces.is_empty())); - } - let (_, report) = - crate::analytic::exchange::export_step(&result.brep, "metre").unwrap(); - assert_eq!(report.solids, 2); - } - } - - #[test] - fn flush_mixed_cutters_cross_a_prior_planar_face_split_without_losing_the_round_hole() { - let host = extrusion( - "flush-mixed-host", - vec![[0.0, 0.0], [6.0, 0.0], [6.0, 0.3], [0.0, 0.3]], - Vec::new(), - ); - let round = primitives::cylinder( - "flush-round".into(), - Frame3 { - origin: [1.5, 0.0, 1.5], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }, - 0.4, - 0.3, - host.accuracy, - ) - .unwrap(); - let rectangle = primitives::linear_extrusion( - "flush-rectangle".into(), - Frame3 { - origin: [0.0, 0.3, 0.0], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, 1.0], - z: [0.0, -1.0, 0.0], - }, - vec![[3.5, 0.7], [4.5, 0.7], [4.5, 1.5], [3.5, 1.5]], - Vec::new(), - 0.3, - host.accuracy, - ) - .unwrap(); - for cutters in [ - [round.clone(), rectangle.clone()], - [rectangle.clone(), round.clone()], - ] { - let result = subtract_planar_cutters(&host, &cutters, "flush-mixed".into()).unwrap(); - result.brep.validate().unwrap(); - assert_eq!( - classify_point(&result.brep, [1.5, 0.15, 1.5]).unwrap(), - PointClassification::Outside - ); - assert_eq!( - classify_point(&result.brep, [4.0, 0.15, 1.4]).unwrap(), - PointClassification::Outside - ); - assert_eq!( - classify_point(&result.brep, [2.5, 0.15, 1.5]).unwrap(), - PointClassification::Inside - ); - assert!(result.brep.topology.faces.iter().any(|face| { - matches!( - result.brep.geometry.surfaces[face.surface as usize], - SurfaceGeometry::Cylinder { .. } - ) - })); - for source in ["flush-round", "flush-rectangle"] { - assert!(result.report.face_mappings.iter().any(|mapping| { - mapping.source.entity == source && !mapping.result_faces.is_empty() - })); - } - crate::analytic::exchange::export_step(&result.brep, "metre").unwrap(); - } - } - - #[test] - fn coextensive_profile_extrusions_use_exact_planar_arrangements() { - let a = extrusion( - "a", - vec![[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]], - Vec::new(), - ); - let b = extrusion( - "b", - vec![[2.0, -1.0], [5.0, -1.0], [5.0, 2.0], [2.0, 2.0]], - Vec::new(), - ); - for (operation, expected_volume) in [ - (BooleanOp::Union, 63.0), - (BooleanOp::Intersection, 12.0), - (BooleanOp::Subtraction, 36.0), - ] { - let result = boolean_brep(&a, &b, operation, format!("{operation:?}")).unwrap(); - result.brep.validate().unwrap(); - assert!(matches!(result.brep.quality, GeometryQuality::Analytic)); - assert!((volume(&result.brep).abs() - expected_volume).abs() < 1.0e-6); - assert!(result - .report - .face_mappings - .iter() - .any(|mapping| !mapping.result_faces.is_empty())); - } - } - - #[test] - fn profile_subtraction_builds_holes_disconnected_solids_and_cut_provenance() { - let host = extrusion( - "host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0]], - Vec::new(), - ); - let inner = extrusion( - "inner", - vec![[4.0, 4.0], [6.0, 4.0], [6.0, 6.0], [4.0, 6.0]], - Vec::new(), - ); - let cavity = boolean_brep(&host, &inner, BooleanOp::Subtraction, "cavity".into()).unwrap(); - assert_eq!(cavity.brep.solids.len(), 1); - assert_eq!(cavity.brep.topology.faces[0].trim.holes.len(), 1); - assert!((volume(&cavity.brep).abs() - 288.0).abs() < 1.0e-6); - assert!(cavity - .brep - .topology - .faces - .iter() - .any(|face| { face.provenance.role == FaceRole::Cut && face.provenance.reversed })); - - let band = extrusion( - "band", - vec![[-1.0, 4.0], [11.0, 4.0], [11.0, 6.0], [-1.0, 6.0]], - Vec::new(), - ); - let split = boolean_brep(&host, &band, BooleanOp::Subtraction, "split".into()).unwrap(); - assert_eq!(split.brep.solids.len(), 2); - assert!((volume(&split.brep).abs() - 240.0).abs() < 1.0e-6); - } - - #[test] - fn matching_profile_extrusions_use_exact_axial_interval_booleans() { - let profile = vec![[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]]; - let a = extrusion_span("a", 0.0, 4.0, profile.clone(), Vec::new()); - let b = extrusion_span( - "b", - 2.0, - 4.0, - vec![[4.0, 4.0], [0.0, 4.0], [0.0, 0.0], [4.0, 0.0]], - Vec::new(), - ); - for (operation, expected_volume) in [ - (BooleanOp::Union, 96.0), - (BooleanOp::Intersection, 32.0), - (BooleanOp::Subtraction, 32.0), - ] { - let result = boolean_brep(&a, &b, operation, format!("axial-{operation:?}")).unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.solids.len(), 1); - assert!((volume(&result.brep).abs() - expected_volume).abs() < 1.0e-6); - assert!(result - .report - .face_mappings - .iter() - .any(|mapping| !mapping.result_faces.is_empty())); - } - - let embedded = extrusion_span("embedded", 1.0, 2.0, profile.clone(), Vec::new()); - let split = - boolean_brep(&a, &embedded, BooleanOp::Subtraction, "axial-split".into()).unwrap(); - split.brep.validate().unwrap(); - assert_eq!(split.brep.solids.len(), 2); - assert!((volume(&split.brep).abs() - 32.0).abs() < 1.0e-6); - assert_eq!( - split - .brep - .topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed) - .count(), - 2 - ); - - let disjoint = extrusion_span("disjoint", 6.0, 2.0, profile, Vec::new()); - let union = boolean_brep(&a, &disjoint, BooleanOp::Union, "axial-disjoint".into()).unwrap(); - assert_eq!(union.brep.solids.len(), 2); - assert!((volume(&union.brep).abs() - 96.0).abs() < 1.0e-6); - let intersection = - boolean_brep(&a, &disjoint, BooleanOp::Intersection, "axial-empty".into()).unwrap(); - assert!(intersection.brep.solids.is_empty()); - } - - #[test] - fn different_height_planar_openings_support_chained_flush_cuts() { - let host = extrusion( - "host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.0, 0.3]], - Vec::new(), - ); - let first = extrusion_span( - "lower_cutout", - 0.0, - 2.1, - vec![[2.0, 0.0], [3.0, 0.0], [3.0, 0.3], [2.0, 0.3]], - Vec::new(), - ); - let cut = boolean_brep( - &host, - &first, - BooleanOp::Subtraction, - "one-lower_cutout".into(), - ) - .unwrap(); - cut.brep.validate().unwrap(); - assert!((volume(&cut.brep).abs() - 8.37).abs() < 1.0e-6); - assert_eq!( - super::super::query::classify_point(&cut.brep, [2.5, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&cut.brep, [2.5, 0.15, 2.5]).unwrap(), - super::super::query::PointClassification::Inside - ); - let second = extrusion_span( - "raised_cutout", - 0.8, - 1.2, - vec![[6.0, 0.0], [7.0, 0.0], [7.0, 0.3], [6.0, 0.3]], - Vec::new(), - ); - let chained = boolean_brep( - &cut.brep, - &second, - BooleanOp::Subtraction, - "lower_cutout-raised_cutout".into(), - ) - .unwrap(); - chained.brep.validate().unwrap(); - assert!((volume(&chained.brep).abs() - 8.01).abs() < 1.0e-6); - assert_eq!( - super::super::query::classify_point(&chained.brep, [6.5, 0.15, 1.2]).unwrap(), - super::super::query::PointClassification::Outside - ); - } - - #[test] - fn chained_mitered_host_openings_keep_top_cap_provenance() { - let host = extrusion_span( - "mitered-host", - 0.0, - 3.8, - vec![ - [-0.16, -0.14], - [0.16, -0.46], - [13.84, -0.46], - [14.16, -0.14], - ], - Vec::new(), - ); - let lower_cutout = extrusion_span( - "lower_cutout", - 0.0, - 2.45, - vec![[1.2, -0.14], [2.8, -0.14], [2.8, -0.46], [1.2, -0.46]], - Vec::new(), - ); - let raised_cutout = extrusion_span( - "raised_cutout", - 0.3, - 2.8, - vec![ - [3.625, -0.14], - [5.175, -0.14], - [5.175, -0.46], - [3.625, -0.46], - ], - Vec::new(), - ); - let first = boolean_brep( - &host, - &lower_cutout, - BooleanOp::Subtraction, - "lower_cutout-cut".into(), - ) - .unwrap(); - let second = boolean_brep( - &first.brep, - &raised_cutout, - BooleanOp::Subtraction, - "raised_cutout-cut".into(), - ) - .unwrap(); - second.brep.validate().unwrap(); - assert_eq!( - super::super::query::classify_point(&second.brep, [2.0, -0.3, 1.0]).unwrap(), - super::super::query::PointClassification::Outside, - ); - assert_eq!( - super::super::query::classify_point(&second.brep, [4.4, -0.3, 1.0]).unwrap(), - super::super::query::PointClassification::Outside, - ); - } - - #[test] - fn ten_flush_planar_cutters_use_one_rectilinear_arrangement() { - let host = extrusion( - "host", - vec![[0.0, 0.0], [20.0, 0.0], [20.0, 0.3], [0.0, 0.3]], - Vec::new(), - ); - let cutters = (0..10) - .map(|index| { - let left = 1.0 + index as f64 * 1.8; - extrusion_span( - &format!("opening-{index}"), - 0.0, - 2.1, - vec![ - [left, 0.0], - [left + 0.5, 0.0], - [left + 0.5, 0.3], - [left, 0.3], - ], - Vec::new(), - ) - }) - .collect::>(); - let result = subtract_planar_cutters(&host, &cutters, "batch".into()).unwrap(); - result.brep.validate().unwrap(); - assert!((volume(&result.brep).abs() - 14.85).abs() < 1.0e-6); - for index in 0..10 { - let station = 1.25 + index as f64 * 1.8; - assert_eq!( - super::super::query::classify_point(&result.brep, [station, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Outside, - ); - } - assert!(result - .report - .face_mappings - .iter() - .any(|mapping| mapping.source.entity == "opening-9")); - } - - #[test] - fn angled_host_cap_accepts_flush_lower_cutout_cutter() { - let host = extrusion( - "angled-host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], - Vec::new(), - ); - let cutter = extrusion_span( - "lower_cutout", - 0.0, - 2.1, - vec![[2.0, 0.0], [3.0, 0.0], [3.0, 0.3], [2.0, 0.3]], - Vec::new(), - ); - let result = - boolean_brep(&host, &cutter, BooleanOp::Subtraction, "angled-cut".into()).unwrap(); - result.brep.validate().unwrap(); - assert!((volume(&result.brep).abs() - (volume(&host).abs() - 0.63)).abs() < 1.0e-6); - assert_eq!( - super::super::query::classify_point(&result.brep, [2.5, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Outside - ); - let raised_cutout = extrusion_span( - "raised_cutout", - 0.8, - 1.2, - vec![[6.0, 0.0], [7.0, 0.0], [7.0, 0.3], [6.0, 0.3]], - Vec::new(), - ); - let chained = boolean_brep( - &result.brep, - &raised_cutout, - BooleanOp::Subtraction, - "angled-chain".into(), - ) - .unwrap(); - chained.brep.validate().unwrap(); - assert!((volume(&chained.brep).abs() - (volume(&host).abs() - 0.99)).abs() < 1.0e-6); - assert_eq!( - super::super::query::classify_point(&chained.brep, [6.5, 0.15, 1.2]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&chained.brep, [6.5, 0.15, 2.5]).unwrap(), - super::super::query::PointClassification::Inside - ); - } - - #[test] - fn full_height_planar_opening_splits_angled_host_into_two_solids() { - let host = extrusion( - "angled-host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], - Vec::new(), - ); - let cutter = extrusion( - "through-lower_cutout", - vec![[4.0, 0.0], [5.0, 0.0], [5.0, 0.3], [4.0, 0.3]], - Vec::new(), - ); - let result = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - "split-angled".into(), - ) - .unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.solids.len(), 2); - assert!((volume(&result.brep).abs() - (volume(&host).abs() - 0.9)).abs() < 1.0e-6); - } - - #[test] - fn overlapping_flush_openings_on_angled_host_remove_the_union_once() { - let host = extrusion( - "angled-host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], - Vec::new(), - ); - let first = extrusion_span( - "first", - 0.0, - 2.1, - vec![[2.0, 0.0], [3.0, 0.0], [3.0, 0.3], [2.0, 0.3]], - Vec::new(), - ); - let second = extrusion_span( - "second", - 0.0, - 2.1, - vec![[2.5, 0.0], [3.5, 0.0], [3.5, 0.3], [2.5, 0.3]], - Vec::new(), - ); - let once = boolean_brep(&host, &first, BooleanOp::Subtraction, "once".into()).unwrap(); - let twice = - boolean_brep(&once.brep, &second, BooleanOp::Subtraction, "twice".into()).unwrap(); - twice.brep.validate().unwrap(); - assert!((volume(&twice.brep).abs() - (volume(&host).abs() - 0.945)).abs() < 1.0e-6); - } - - #[test] - fn layered_planar_cut_supports_an_internal_cavity_shell() { - let host = extrusion( - "angled-host", - vec![ - [0.0, 0.0], - [10.0, 0.0], - [10.0, 10.0], - [0.3, 10.0], - [0.0, 9.8], - ], - Vec::new(), - ); - let cutter = extrusion_span( - "cavity", - 1.0, - 1.0, - vec![[4.0, 4.0], [6.0, 4.0], [6.0, 6.0], [4.0, 6.0]], - Vec::new(), - ); - let result = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - "cavity-result".into(), - ) - .unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.solids.len(), 1); - assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); - assert!((volume(&result.brep).abs() - (volume(&host).abs() - 4.0)).abs() < 1.0e-6); - } - - #[test] - fn angled_host_accepts_rotated_planar_cutter_frame() { - let host = extrusion( - "angled-host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], - Vec::new(), - ); - let angle = std::f64::consts::FRAC_PI_6; - let cutter = primitives::linear_extrusion( - "rotated-cutter".into(), - Frame3 { - origin: [2.5, 0.15, 0.0], - x: [angle.cos(), angle.sin(), 0.0], - y: [-angle.sin(), angle.cos(), 0.0], - z: [0.0, 0.0, 1.0], - }, - vec![[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]], - Vec::new(), - 2.1, - host.accuracy, - ) - .unwrap(); - let result = - boolean_brep(&host, &cutter, BooleanOp::Subtraction, "rotated-cut".into()).unwrap(); - result.brep.validate().unwrap(); - assert_eq!( - super::super::query::classify_point(&result.brep, [2.5, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&result.brep, [2.5, 0.15, 2.5]).unwrap(), - super::super::query::PointClassification::Inside - ); - assert!(volume(&result.brep).abs() < volume(&host).abs()); - } - - #[test] - fn oblique_planar_cutter_crosses_a_host_without_tessellating_the_boolean() { - let host = extrusion( - "oblique-host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], - Vec::new(), - ); - let angle = std::f64::consts::PI / 12.0; - let cutter = primitives::linear_extrusion( - "oblique-cutter".into(), - Frame3 { - origin: [2.5, 0.15, 0.0], - x: [angle.cos(), 0.0, -angle.sin()], - y: [0.0, 1.0, 0.0], - z: [angle.sin(), 0.0, angle.cos()], - }, - vec![[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]], - Vec::new(), - 2.1, - host.accuracy, - ) - .unwrap(); - let result = - boolean_brep(&host, &cutter, BooleanOp::Subtraction, "oblique-cut".into()).unwrap(); - result.brep.validate().unwrap(); - assert!(matches!(result.report.quality, GeometryQuality::Analytic)); - assert_eq!( - super::super::query::classify_point(&result.brep, [2.8, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&result.brep, [2.8, 0.15, 2.5]).unwrap(), - super::super::query::PointClassification::Inside - ); - assert!(volume(&result.brep).abs() < volume(&host).abs()); - } - - #[test] - fn chained_oblique_planar_cuts_keep_both_voids_and_analytic_faces() { - let host = extrusion( - "chained-oblique-host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], - Vec::new(), - ); - let angle = std::f64::consts::PI / 12.0; - let cutter = |name: &str, station: f64, sign: f64| { - primitives::linear_extrusion( - name.into(), - Frame3 { - origin: [station, 0.15, 0.0], - x: [angle.cos(), 0.0, -sign * angle.sin()], - y: [0.0, 1.0, 0.0], - z: [sign * angle.sin(), 0.0, angle.cos()], - }, - vec![[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]], - Vec::new(), - 2.1, - host.accuracy, - ) - .unwrap() - }; - let first = cutter("first-oblique", 2.5, 1.0); - let second = cutter("second-oblique", 5.0, -1.0); - let once = boolean_brep(&host, &first, BooleanOp::Subtraction, "once".into()).unwrap(); - let twice = - boolean_brep(&once.brep, &second, BooleanOp::Subtraction, "twice".into()).unwrap(); - twice.brep.validate().unwrap(); - assert!(matches!(twice.report.quality, GeometryQuality::Analytic)); - for point in [[2.8, 0.15, 1.0], [4.7, 0.15, 1.0]] { - assert_eq!( - super::super::query::classify_point(&twice.brep, point).unwrap(), - super::super::query::PointClassification::Outside - ); - } - assert_eq!( - super::super::query::classify_point(&twice.brep, [3.8, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Inside - ); - assert!(volume(&twice.brep).abs() < volume(&once.brep).abs()); - } - - #[test] - fn oblique_cut_accepts_a_disconnected_planar_host() { - let host = extrusion( - "split-host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.0, 0.3]], - Vec::new(), - ); - let through = extrusion_span( - "through", - -1.0, - 5.0, - vec![[3.0, -1.0], [4.0, -1.0], [4.0, 1.0], [3.0, 1.0]], - Vec::new(), - ); - let split = boolean_brep(&host, &through, BooleanOp::Subtraction, "split".into()).unwrap(); - assert_eq!(split.brep.solids.len(), 2); - let angle = std::f64::consts::PI / 12.0; - let oblique = primitives::linear_extrusion( - "split-oblique".into(), - Frame3 { - origin: [6.0, 0.15, 0.0], - x: [angle.cos(), 0.0, -angle.sin()], - y: [0.0, 1.0, 0.0], - z: [angle.sin(), 0.0, angle.cos()], - }, - vec![[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]], - Vec::new(), - 2.1, - host.accuracy, - ) - .unwrap(); - let result = boolean_brep( - &split.brep, - &oblique, - BooleanOp::Subtraction, - "split-oblique-result".into(), - ) - .unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.solids.len(), 2); - assert_eq!( - super::super::query::classify_point(&result.brep, [6.3, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&result.brep, [1.0, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Inside - ); - } - - #[test] - fn planar_cutter_tilts_about_both_host_axes_keep_exact_occupancy() { - let host = extrusion( - "tilt-sweep-host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.0, 0.3]], - Vec::new(), - ); - for axis in 0..2 { - for degrees in [-25.0_f64, -15.0, -5.0, 5.0, 15.0, 25.0] { - let angle = degrees.to_radians(); - let frame = if axis == 0 { - Frame3 { - origin: [2.5, 0.15, 0.0], - x: [angle.cos(), 0.0, -angle.sin()], - y: [0.0, 1.0, 0.0], - z: [angle.sin(), 0.0, angle.cos()], - } - } else { - Frame3 { - origin: [2.5, 0.15, 0.0], - x: [1.0, 0.0, 0.0], - y: [0.0, angle.cos(), -angle.sin()], - z: [0.0, angle.sin(), angle.cos()], - } - }; - let cutter = primitives::linear_extrusion( - format!("tilt-{axis}-{degrees}"), - frame, - vec![[-0.8, -0.8], [0.8, -0.8], [0.8, 0.8], [-0.8, 0.8]], - Vec::new(), - 2.1, - host.accuracy, - ) - .unwrap(); - let result = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - format!("tilt-result-{axis}-{degrees}"), - ) - .unwrap(); - result.brep.validate().unwrap(); - assert_eq!( - super::super::query::classify_point(&result.brep, [2.5, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Outside, - "axis={axis}, degrees={degrees}" - ); - assert_eq!( - super::super::query::classify_point(&result.brep, [8.0, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Inside, - "axis={axis}, degrees={degrees}" - ); - assert!(volume(&result.brep).abs() < volume(&host).abs()); - } - } - } - - #[test] - fn oblique_cut_preserves_a_planar_host_with_profile_hole_loops() { - let host = extrusion( - "profile-hole-host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 3.0], [0.0, 3.0]], - vec![vec![[2.0, 1.0], [3.0, 1.0], [3.0, 2.0], [2.0, 2.0]]], - ); - assert!(host - .topology - .faces - .iter() - .any(|face| !face.trim.holes.is_empty())); - let angle = std::f64::consts::PI / 12.0; - let oblique = primitives::linear_extrusion( - "pocket-oblique".into(), - Frame3 { - origin: [6.0, 1.5, 0.0], - x: [angle.cos(), 0.0, -angle.sin()], - y: [0.0, 1.0, 0.0], - z: [angle.sin(), 0.0, angle.cos()], - }, - vec![[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]], - Vec::new(), - 2.1, - host.accuracy, - ) - .unwrap(); - let result = - boolean_brep(&host, &oblique, BooleanOp::Subtraction, "two-voids".into()).unwrap(); - result.brep.validate().unwrap(); - for point in [[2.5, 1.5, 1.5], [6.3, 1.5, 1.0]] { - assert_eq!( - super::super::query::classify_point(&result.brep, point).unwrap(), - super::super::query::PointClassification::Outside - ); - } - for point in [[4.0, 1.5, 1.0], [6.3, 1.5, 2.5]] { - assert_eq!( - super::super::query::classify_point(&result.brep, point).unwrap(), - super::super::query::PointClassification::Inside - ); - } - } - - #[test] - fn oblique_nonconvex_planar_cutter_keeps_exact_host_material() { - let host = extrusion( - "nonconvex-host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.0, 0.3]], - Vec::new(), - ); - let angle = std::f64::consts::PI / 12.0; - let cutter = primitives::linear_extrusion( - "nonconvex-cutter".into(), - Frame3 { - origin: [5.0, 0.15, 0.0], - x: [angle.cos(), 0.0, -angle.sin()], - y: [0.0, 1.0, 0.0], - z: [angle.sin(), 0.0, angle.cos()], - }, - vec![ - [-1.0, -1.0], - [1.0, -1.0], - [1.0, -0.05], - [0.0, -0.05], - [0.0, 1.0], - [-1.0, 1.0], - ], - Vec::new(), - 2.1, - host.accuracy, - ) - .unwrap(); - let result = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - "nonconvex-oblique-cut".into(), - ) - .unwrap(); - result.brep.validate().unwrap(); - assert!(matches!(result.report.quality, GeometryQuality::Analytic)); - assert_eq!( - super::super::query::classify_point(&result.brep, [4.5, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&result.brep, [5.5, 0.15, 1.0]).unwrap(), - super::super::query::PointClassification::Inside - ); - } - - #[test] - fn oblique_planar_cut_preserves_an_existing_internal_cavity_shell() { - let host = extrusion( - "cavity-host", - vec![[0.0, 0.0], [10.0, 0.0], [10.0, 3.0], [0.0, 3.0]], - Vec::new(), - ); - let cavity = extrusion_span( - "internal-cavity", - 1.0, - 1.0, - vec![[2.0, 1.0], [3.0, 1.0], [3.0, 2.0], [2.0, 2.0]], - Vec::new(), - ); - let host = boolean_brep(&host, &cavity, BooleanOp::Subtraction, "cavity-host".into()) - .unwrap() - .brep; - assert_eq!(host.solids[0].cavity_shells.len(), 1); - let angle = std::f64::consts::PI / 12.0; - let cutter = primitives::linear_extrusion( - "oblique-after-cavity".into(), - Frame3 { - origin: [6.0, 1.5, 0.0], - x: [angle.cos(), 0.0, -angle.sin()], - y: [0.0, 1.0, 0.0], - z: [angle.sin(), 0.0, angle.cos()], - }, - vec![[-0.5, -2.0], [0.5, -2.0], [0.5, 2.0], [-0.5, 2.0]], - Vec::new(), - 2.1, - host.accuracy, - ) - .unwrap(); - let result = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - "oblique-cavity-cut".into(), - ) - .unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.solids.len(), 1); - assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); - assert_eq!( - super::super::query::classify_point(&result.brep, [2.5, 1.5, 1.5]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&result.brep, [6.2, 1.5, 1.0]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&result.brep, [6.2, 1.5, 2.6]).unwrap(), - super::super::query::PointClassification::Inside - ); - } - - #[test] - fn oblique_cut_assigns_a_remote_cavity_to_its_original_material_component() { - let left = extrusion( - "left-cavity-host", - vec![[0.0, 0.0], [4.0, 0.0], [4.0, 3.0], [0.0, 3.0]], - Vec::new(), - ); - let cavity = extrusion_span( - "left-cavity", - 1.0, - 1.0, - vec![[1.0, 1.0], [2.0, 1.0], [2.0, 2.0], [1.0, 2.0]], - Vec::new(), - ); - let left_with_cavity = boolean_brep( - &left, - &cavity, - BooleanOp::Subtraction, - "left-with-cavity".into(), - ) - .unwrap() - .brep; - let right = extrusion( - "right-host", - vec![[6.0, 0.0], [10.0, 0.0], [10.0, 3.0], [6.0, 3.0]], - Vec::new(), - ); - let host = boolean_brep( - &left_with_cavity, - &right, - BooleanOp::Union, - "two-components".into(), - ) - .unwrap() - .brep; - assert_eq!(host.solids.len(), 2); - let angle = std::f64::consts::PI / 12.0; - let cutter = primitives::linear_extrusion( - "right-oblique-cutter".into(), - Frame3 { - origin: [8.5, 1.5, 0.0], - x: [angle.cos(), 0.0, -angle.sin()], - y: [0.0, 1.0, 0.0], - z: [angle.sin(), 0.0, angle.cos()], - }, - vec![[-0.5, -2.0], [0.5, -2.0], [0.5, 2.0], [-0.5, 2.0]], - Vec::new(), - 2.1, - host.accuracy, - ) - .unwrap(); - let result = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - "remote-cavity-cut".into(), - ) - .unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.solids.len(), 2); - assert_eq!( - result - .brep - .solids - .iter() - .map(|solid| solid.cavity_shells.len()) - .sum::(), - 1 - ); - assert_eq!( - super::super::query::classify_point(&result.brep, [1.5, 1.5, 1.5]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&result.brep, [8.5, 1.5, 1.0]).unwrap(), - super::super::query::PointClassification::Outside - ); - } - - #[test] - fn coaxial_conic_containment_builds_exact_cavities_and_ownership() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let host = - primitives::frustum("host".into(), Frame3::IDENTITY, 3.0, 4.0, 4.0, accuracy).unwrap(); - let cutter = primitives::frustum( - "cutter".into(), - Frame3 { - origin: [0.0, 0.0, 1.0], - ..Frame3::IDENTITY - }, - 1.0, - 1.5, - 1.0, - accuracy, - ) - .unwrap(); - let host_volume = std::f64::consts::PI * 4.0 * (9.0 + 12.0 + 16.0) / 3.0; - let cutter_volume = std::f64::consts::PI * (1.0 + 1.5 + 2.25) / 3.0; - for (operation, expected_volume, expected_faces) in [ - (BooleanOp::Union, host_volume, 3), - (BooleanOp::Intersection, cutter_volume, 3), - (BooleanOp::Subtraction, host_volume - cutter_volume, 6), - ] { - let result = - boolean_brep(&host, &cutter, operation, format!("conic-{operation:?}")).unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.topology.faces.len(), expected_faces); - assert_eq!(result.report.face_mappings.len(), 6); - let measured = volume(&result.brep).abs(); - assert!( - (measured - expected_volume).abs() < 0.75, - "{operation:?}: measured {measured}, expected {expected_volume}" - ); - if operation == BooleanOp::Subtraction { - assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); - assert_eq!( - result - .brep - .topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut - && face.provenance.reversed) - .count(), - 3 - ); - } - } - - let touching = - primitives::frustum("touching".into(), Frame3::IDENTITY, 1.0, 1.5, 1.0, accuracy) - .unwrap(); - assert!(matches!( - boolean_brep( - &host, - &touching, - BooleanOp::Subtraction, - "touching-result".into() - ), - Err(GeometryError::UnresolvedIntersection(_)) - )); - } - - #[test] - fn mixed_conic_containment_preserves_all_analytic_supports() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let conic = - primitives::frustum("frustum".into(), Frame3::IDENTITY, 3.0, 4.0, 4.0, accuracy) - .unwrap(); - let inner_sphere = primitives::sphere( - "inner-sphere".into(), - Frame3 { - origin: [0.0, 0.0, 2.0], - ..Frame3::IDENTITY - }, - 0.5, - accuracy, - ) - .unwrap(); - let outer_sphere = primitives::sphere( - "outer-sphere".into(), - Frame3 { - origin: [0.0, 0.0, 2.0], - ..Frame3::IDENTITY - }, - 6.0, - accuracy, - ) - .unwrap(); - let inner_cylinder = primitives::cylinder( - "inner-cylinder".into(), - Frame3 { - origin: [0.0, 0.0, 1.0], - ..Frame3::IDENTITY - }, - 0.5, - 1.0, - accuracy, - ) - .unwrap(); - let outer_cylinder = primitives::cylinder( - "outer-cylinder".into(), - Frame3 { - origin: [0.0, 0.0, -1.0], - ..Frame3::IDENTITY - }, - 5.0, - 6.0, - accuracy, - ) - .unwrap(); - let inner_box = primitives::cuboid( - "inner-box".into(), - Frame3 { - origin: [-0.25, -0.25, 1.75], - ..Frame3::IDENTITY - }, - [0.5, 0.5, 0.5], - accuracy, - ) - .unwrap(); - let outer_box = primitives::cuboid( - "outer-box".into(), - Frame3 { - origin: [-5.0, -5.0, -1.0], - ..Frame3::IDENTITY - }, - [10.0, 10.0, 6.0], - accuracy, - ) - .unwrap(); - - for (host, cutter, expected_faces) in [ - (&conic, &inner_sphere, 4), - (&outer_sphere, &conic, 4), - (&conic, &inner_cylinder, 6), - (&outer_cylinder, &conic, 6), - (&conic, &inner_box, 9), - (&outer_box, &conic, 9), - ] { - let result = boolean_brep( - host, - cutter, - BooleanOp::Subtraction, - format!("{}-minus-{}", host.id, cutter.id), - ) - .unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.solids.len(), 1); - assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); - assert_eq!(result.brep.topology.faces.len(), expected_faces); - assert_eq!( - result - .brep - .topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut - && face.provenance.reversed) - .count(), - cutter.topology.faces.len() - ); - tessellate(&result.brep, 0.02, 2_000_000).unwrap(); - } - } - - #[test] - fn generic_closed_loop_imprints_an_off_axis_sphere_cone_intersection() { - let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; - let cone = primitives::cone("cone".into(), Frame3::IDENTITY, 2.0, 2.0, accuracy).unwrap(); - let sphere = primitives::sphere( - "sphere".into(), - Frame3 { - origin: [1.42, 0.0, 1.0], - ..Frame3::IDENTITY - }, - 0.35, - accuracy, - ) - .unwrap(); - for operation in [ - BooleanOp::Union, - BooleanOp::Intersection, - BooleanOp::Subtraction, - ] { - let result = boolean_brep( - &cone, - &sphere, - operation, - format!("sphere-cone-{operation:?}"), - ) - .unwrap_or_else(|error| panic!("{operation:?}: {error}")); - assert_eq!(result.brep.solids.len(), 1); - assert!(result - .brep - .geometry - .curves - .iter() - .any(|curve| matches!(curve, CurveGeometry::Intersection { .. }))); - assert!(result - .brep - .topology - .faces - .iter() - .all(|face| !face.provenance.sources.is_empty())); - result.brep.validate().unwrap(); - tessellate(&result.brep, 0.01, 2_000_000) - .unwrap_or_else(|error| panic!("{operation:?} tessellation: {error}")); - } - } - - #[test] - fn generic_closed_loop_handles_periodic_winding() { - let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; - let cone = primitives::cone("cone".into(), Frame3::IDENTITY, 2.0, 2.0, accuracy).unwrap(); - let sphere = primitives::sphere( - "sphere".into(), - Frame3 { - origin: [1.1, 0.0, 0.8], - ..Frame3::IDENTITY - }, - 0.35, - accuracy, - ) - .unwrap(); - let result = - boolean_brep(&cone, &sphere, BooleanOp::Intersection, "winding".into()).unwrap(); - result.brep.validate().unwrap(); - tessellate(&result.brep, 0.01, 2_000_000).unwrap(); - } - - #[test] - fn ring_torus_containment_uses_exact_host_support_bounds() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let torus = primitives::torus( - "torus".into(), - Frame3 { - origin: [0.0, 0.0, 2.0], - ..Frame3::IDENTITY - }, - 2.0, - 0.5, - accuracy, - ) - .unwrap(); - let cylinder = - primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 4.0, 4.0, accuracy).unwrap(); - let box_ = primitives::cuboid( - "box".into(), - Frame3 { - origin: [-4.0, -4.0, -1.0], - ..Frame3::IDENTITY - }, - [8.0, 8.0, 6.0], - accuracy, - ) - .unwrap(); - let conic = - primitives::frustum("frustum".into(), Frame3::IDENTITY, 4.0, 5.0, 4.0, accuracy) - .unwrap(); - for host in [&cylinder, &box_, &conic] { - let result = boolean_brep( - host, - &torus, - BooleanOp::Subtraction, - format!("{}-minus-torus", host.id), - ) - .unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.solids.len(), 1); - assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); - assert!(result.brep.topology.faces.iter().any(|face| { - matches!( - result.brep.geometry.surfaces[face.surface as usize], - SurfaceGeometry::Torus { .. } - ) && face.provenance.role == FaceRole::Cut - && face.provenance.reversed - })); - tessellate(&result.brep, 0.02, 2_000_000).unwrap(); - } - } - - #[test] - fn restricted_shell_offsets_supported_closed_families() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let inputs = [ - ( - primitives::cuboid("box".into(), Frame3::IDENTITY, [2.0; 3], accuracy).unwrap(), - 12, - ), - ( - primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy) - .unwrap(), - 6, - ), - ( - primitives::sphere("sphere".into(), Frame3::IDENTITY, 1.0, accuracy).unwrap(), - 2, - ), - ( - primitives::torus("torus".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy).unwrap(), - 2, - ), - ( - primitives::cone("cone".into(), Frame3::IDENTITY, 2.0, 3.0, accuracy).unwrap(), - 4, - ), - ( - primitives::frustum("frustum".into(), Frame3::IDENTITY, 2.0, 1.0, 3.0, accuracy) - .unwrap(), - 6, - ), - ( - extrusion( - "profile-shell", - vec![[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]], - vec![vec![[1.0, 1.0], [1.0, 3.0], [3.0, 3.0], [3.0, 1.0]]], - ), - 20, - ), - ]; - for (input, expected_faces) in &inputs { - let result = shell_brep(&input, 0.2, format!("{}-shell", input.id)).unwrap(); - result.brep.validate().unwrap(); - assert_eq!(result.brep.solids.len(), 1); - assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); - assert_eq!(result.brep.topology.faces.len(), *expected_faces); - assert_eq!( - result.report.face_mappings.len(), - input.topology.faces.len() - ); - assert!(result.brep.topology.faces.iter().any(|face| { - face.provenance.role == FaceRole::Cut - && face.provenance.reversed - && face - .provenance - .sources - .iter() - .all(|source| source.entity == input.id) - })); - tessellate(&result.brep, 0.02, 2_000_000).unwrap(); - } - assert!(matches!( - shell_brep(&inputs[2].0, 1.0, "invalid".into()), - Err(GeometryError::UnresolvedIntersection(_)) - )); - } - - #[test] - fn identical_general_analytic_body_uses_coincident_ownership() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let host = primitives::annular_sector_extrusion( - "host".into(), - Frame3::IDENTITY, - 3.0, - 0.4, - 2.0, - 0.2, - 1.4, - accuracy, - ) - .unwrap(); - let union = boolean_brep(&host, &host, BooleanOp::Union, "same".into()).unwrap(); - assert!(union.report.coincident); - assert_eq!(union.brep.topology.faces.len(), host.topology.faces.len()); - assert!(union - .brep - .topology - .faces - .iter() - .all(|face| face.provenance.role == FaceRole::Coincident)); - let empty = boolean_brep(&host, &host, BooleanOp::Subtraction, "empty".into()).unwrap(); - assert!(empty.brep.solids.is_empty()); - } - - #[test] - fn generic_pipeline_classifies_noncanonical_planar_solid_containment() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let tetrahedron = primitives::planar_polyhedron( - "tetrahedron".into(), - vec![ - [-0.2, -0.2, 0.8], - [0.2, -0.2, 0.8], - [0.0, 0.2, 0.8], - [0.0, 0.0, 1.2], - ], - vec![vec![0, 2, 1], vec![0, 1, 3], vec![0, 3, 2], vec![1, 2, 3]], - accuracy, - ) - .unwrap(); - let cylinder = - primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 2.0, 2.0, accuracy).unwrap(); - - let union = - boolean_brep(&cylinder, &tetrahedron, BooleanOp::Union, "union".into()).unwrap(); - assert_eq!( - union.brep.topology.faces.len(), - cylinder.topology.faces.len() - ); - - let intersection = boolean_brep( - &cylinder, - &tetrahedron, - BooleanOp::Intersection, - "intersection".into(), - ) - .unwrap(); - assert_eq!( - intersection.brep.topology.faces.len(), - tetrahedron.topology.faces.len() - ); - - let subtraction = boolean_brep( - &cylinder, - &tetrahedron, - BooleanOp::Subtraction, - "subtraction".into(), - ) - .unwrap(); - assert_eq!(subtraction.brep.solids.len(), 1); - assert_eq!(subtraction.brep.solids[0].cavity_shells.len(), 1); - subtraction.brep.validate().unwrap(); - tessellate(&subtraction.brep, 0.01, 2_000_000).unwrap(); - } - - #[test] - fn annular_sector_accepts_an_exact_vertical_sector_opening() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let host = primitives::annular_cylinder( - "ring-host".into(), - Frame3::IDENTITY, - 1.9, - 2.1, - 3.0, - accuracy, - ) - .unwrap(); - let reach = 2.3; - let cutter = primitives::linear_extrusion( - "ring-opening".into(), - Frame3 { - origin: [0.0, 0.0, 0.5], - ..Frame3::IDENTITY - }, - vec![ - [0.0, 0.0], - [reach * (-0.1_f64).cos(), reach * (-0.1_f64).sin()], - [reach * 0.1_f64.cos(), reach * 0.1_f64.sin()], - ], - vec![], - 2.0, - accuracy, - ) - .unwrap(); - let cut = boolean_brep(&host, &cutter, BooleanOp::Subtraction, "ring-cut".into()).unwrap(); - cut.brep.validate().unwrap(); - tessellate(&cut.brep, 0.01, 2_000_000).unwrap(); - assert_eq!( - super::super::query::classify_point(&cut.brep, [2.0, 0.0, 1.0]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&cut.brep, [0.0, 2.0, 1.0]).unwrap(), - super::super::query::PointClassification::Inside - ); - assert_eq!( - super::super::query::classify_point(&cut.brep, [0.0, 0.0, 1.0]).unwrap(), - super::super::query::PointClassification::Outside - ); - let second = primitives::linear_extrusion( - "second-ring-opening".into(), - Frame3 { - origin: [0.0, 0.0, 0.8], - ..Frame3::IDENTITY - }, - vec![ - [0.0, 0.0], - [ - reach * (std::f64::consts::FRAC_PI_2 - 0.1).cos(), - reach * (std::f64::consts::FRAC_PI_2 - 0.1).sin(), - ], - [ - reach * (std::f64::consts::FRAC_PI_2 + 0.1).cos(), - reach * (std::f64::consts::FRAC_PI_2 + 0.1).sin(), - ], - ], - vec![], - 1.3, - accuracy, - ) - .unwrap(); - let chained = boolean_brep( - &cut.brep, - &second, - BooleanOp::Subtraction, - "ring-cut-chained".into(), - ) - .unwrap(); - chained.brep.validate().unwrap(); - tessellate(&chained.brep, 0.01, 2_000_000).unwrap(); - for point in [[2.0, 0.0, 1.0], [0.0, 2.0, 1.0]] { - assert_eq!( - super::super::query::classify_point(&chained.brep, point).unwrap(), - super::super::query::PointClassification::Outside - ); - } - assert_eq!( - super::super::query::classify_point(&chained.brep, [-2.0, 0.0, 1.0]).unwrap(), - super::super::query::PointClassification::Inside - ); - } - - #[test] - fn arc_edged_host_accepts_a_vertical_rectangular_opening_cut() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let quarter = std::f64::consts::FRAC_PI_2; - let host = primitives::arc_edged_extrusion( - "arc-host".into(), - Frame3::IDENTITY, - vec![ - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.0, - start_angle: 0.0, - sweep_angle: quarter, - }, - primitives::ProfileEdge::Line { - from: [0.0, 2.0], - to: [0.0, 1.5], - }, - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.5, - start_angle: quarter, - sweep_angle: -quarter, - }, - primitives::ProfileEdge::Line { - from: [1.5, 0.0], - to: [2.0, 0.0], - }, - ], - 3.0, - accuracy, - ) - .unwrap(); - let cutter = primitives::cuboid( - "opening".into(), - Frame3 { - origin: [1.1, 0.9, 0.5], - ..Frame3::IDENTITY - }, - [1.0, 0.35, 1.5], - accuracy, - ) - .unwrap(); - let assert_curved_normals = |brep: &BrepEnvelope| { - for (radius, expected) in [(2.0, Orientation::Forward), (1.5, Orientation::Reverse)] { - let faces = brep.topology.faces.iter().filter(|face| { - matches!( - brep.geometry.surfaces[face.surface as usize], - SurfaceGeometry::Cylinder { radius: value, .. } if (value - radius).abs() < 1e-9 - ) - }).collect::>(); - assert!(!faces.is_empty(), "missing curved face at radius {radius}"); - assert!( - faces.iter().all(|face| face.sense == expected), - "reconstructed curved face at radius {radius} points into material: {:?}", - faces - .iter() - .map(|face| (&face.key, face.sense)) - .collect::>() - ); - } - }; - let cut = boolean_brep(&host, &cutter, BooleanOp::Subtraction, "arc-cut".into()).unwrap(); - cut.brep.validate().unwrap(); - assert_curved_normals(&cut.brep); - tessellate(&cut.brep, 0.01, 2_000_000).unwrap(); - assert_eq!( - super::super::query::classify_point(&cut.brep, [1.5, 1.1, 1.2]).unwrap(), - super::super::query::PointClassification::Outside - ); - let second_cutter = primitives::cuboid( - "second-opening".into(), - Frame3 { - origin: [0.6, 1.3, 0.8], - ..Frame3::IDENTITY - }, - [0.45, 0.8, 1.2], - accuracy, - ) - .unwrap(); - let batch = subtract_planar_cutters( - &host, - &[cutter, second_cutter.clone()], - "arc-cut-batch".into(), - ) - .unwrap(); - batch.brep.validate().unwrap(); - tessellate(&batch.brep, 0.01, 2_000_000).unwrap(); - assert_curved_normals(&batch.brep); - for point in [[1.5, 1.1, 1.2], [0.9, 1.65, 1.2]] { - assert_eq!( - super::super::query::classify_point(&batch.brep, point).unwrap(), - super::super::query::PointClassification::Outside - ); - } - let chained = boolean_brep( - &cut.brep, - &second_cutter, - BooleanOp::Subtraction, - "arc-cut-chained".into(), - ) - .unwrap(); - chained.brep.validate().unwrap(); - assert_curved_normals(&chained.brep); - for point in [[1.5, 1.1, 1.2], [0.9, 1.65, 1.2]] { - assert_eq!( - super::super::query::classify_point(&chained.brep, point).unwrap(), - super::super::query::PointClassification::Outside - ); - } - let overlapping = primitives::cuboid( - "overlapping-opening".into(), - Frame3 { - origin: [1.4, 0.95, 0.2], - ..Frame3::IDENTITY - }, - [0.5, 0.4, 2.2], - accuracy, - ) - .unwrap(); - let third = boolean_brep( - &chained.brep, - &overlapping, - BooleanOp::Subtraction, - "arc-cut-third".into(), - ) - .unwrap(); - third.brep.validate().unwrap(); - tessellate(&third.brep, 0.01, 2_000_000).unwrap(); - assert_curved_normals(&third.brep); - for point in [ - [1.5, 1.1, 1.2], - [0.9, 1.65, 1.2], - [1.45, 1.2, 0.3], - [1.45, 1.2, 2.2], - ] { - assert_eq!( - super::super::query::classify_point(&third.brep, point).unwrap(), - super::super::query::PointClassification::Outside - ); - } - } - - #[test] - fn curved_internal_cut_creates_one_cavity_shell_in_its_host_solid() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let quarter = std::f64::consts::FRAC_PI_2; - let host = primitives::arc_edged_extrusion( - "curved-cavity-host".into(), - Frame3::IDENTITY, - vec![ - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.0, - start_angle: 0.0, - sweep_angle: quarter, - }, - primitives::ProfileEdge::Line { - from: [0.0, 2.0], - to: [0.0, 1.0], - }, - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.0, - start_angle: quarter, - sweep_angle: -quarter, - }, - primitives::ProfileEdge::Line { - from: [1.0, 0.0], - to: [2.0, 0.0], - }, - ], - 3.0, - accuracy, - ) - .unwrap(); - let cutter = primitives::linear_extrusion( - "curved-interior-cutter".into(), - Frame3 { - origin: [0.0, 0.0, 1.0], - ..Frame3::IDENTITY - }, - vec![[1.0, 1.0], [1.2, 1.0], [1.2, 1.2], [1.0, 1.2]], - vec![], - 1.0, - accuracy, - ) - .unwrap(); - let result = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - "curved-cavity".into(), - ) - .unwrap(); - result.brep.validate().unwrap(); - tessellate(&result.brep, 0.01, 2_000_000).unwrap(); - assert_eq!(result.brep.solids.len(), 1); - assert_eq!(result.brep.topology.shells.len(), 2); - assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); - assert_eq!( - super::super::query::classify_point(&result.brep, [1.1, 1.1, 1.5]).unwrap(), - super::super::query::PointClassification::Outside - ); - assert_eq!( - super::super::query::classify_point(&result.brep, [1.5, 0.5, 1.5]).unwrap(), - super::super::query::PointClassification::Inside - ); - - let split_sector = primitives::linear_extrusion( - "curved-split-sector".into(), - Frame3 { - origin: [0.0, 0.0, -1.0], - ..Frame3::IDENTITY - }, - vec![ - [0.0, 0.0], - [3.0 * 0.2_f64.cos(), 3.0 * 0.2_f64.sin()], - [3.0 * 0.3_f64.cos(), 3.0 * 0.3_f64.sin()], - ], - vec![], - 5.0, - accuracy, - ) - .unwrap(); - let split = boolean_brep( - &host, - &split_sector, - BooleanOp::Subtraction, - "curved-split".into(), - ) - .unwrap(); - assert_eq!(split.brep.solids.len(), 2); - let split_with_cavity = boolean_brep( - &split.brep, - &cutter, - BooleanOp::Subtraction, - "curved-split-with-cavity".into(), - ) - .unwrap(); - split_with_cavity.brep.validate().unwrap(); - assert_eq!(split_with_cavity.brep.solids.len(), 2); - assert_eq!(split_with_cavity.brep.topology.shells.len(), 3); - assert_eq!( - split_with_cavity - .brep - .solids - .iter() - .map(|solid| solid.cavity_shells.len()) - .sum::(), - 1 - ); - assert_eq!( - super::super::query::classify_point(&split_with_cavity.brep, [1.1, 1.1, 1.5]).unwrap(), - super::super::query::PointClassification::Outside - ); - - let second_cutter = primitives::linear_extrusion( - "curved-other-component-cutter".into(), - Frame3 { - origin: [0.0, 0.0, 1.1], - ..Frame3::IDENTITY - }, - vec![[1.55, 0.1], [1.67, 0.1], [1.67, 0.22], [1.55, 0.22]], - vec![], - 0.8, - accuracy, - ) - .unwrap(); - let same_component_cavities = boolean_brep( - &result.brep, - &second_cutter, - BooleanOp::Subtraction, - "curved-same-component-cavities".into(), - ) - .unwrap(); - same_component_cavities.brep.validate().unwrap(); - assert_eq!(same_component_cavities.brep.solids.len(), 1); - assert_eq!(same_component_cavities.brep.topology.shells.len(), 3); - assert_eq!( - same_component_cavities.brep.solids[0].cavity_shells.len(), - 2 - ); - let two_cavities = boolean_brep( - &split_with_cavity.brep, - &second_cutter, - BooleanOp::Subtraction, - "curved-two-cavities".into(), - ) - .unwrap(); - two_cavities.brep.validate().unwrap(); - assert_eq!(two_cavities.brep.solids.len(), 2); - assert_eq!(two_cavities.brep.topology.shells.len(), 4); - assert!(two_cavities - .brep - .solids - .iter() - .all(|solid| solid.cavity_shells.len() == 1)); - for point in [[1.1, 1.1, 1.5], [1.6, 0.16, 1.5]] { - assert_eq!( - super::super::query::classify_point(&two_cavities.brep, point).unwrap(), - super::super::query::PointClassification::Outside - ); - } - } - - #[test] - fn curved_cut_provenance_selects_the_trimmed_coaxial_source_face() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let quarter = std::f64::consts::FRAC_PI_2; - let host = primitives::arc_edged_extrusion( - "split-arc-host".into(), - Frame3::IDENTITY, - vec![ - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.0, - start_angle: 0.0, - sweep_angle: quarter / 2.0, - }, - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.0, - start_angle: quarter / 2.0, - sweep_angle: quarter / 2.0, - }, - primitives::ProfileEdge::Line { - from: [0.0, 2.0], - to: [0.0, 1.5], - }, - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.5, - start_angle: quarter, - sweep_angle: -quarter, - }, - primitives::ProfileEdge::Line { - from: [1.5, 0.0], - to: [2.0, 0.0], - }, - ], - 3.0, - accuracy, - ) - .unwrap(); - let cutter = primitives::cuboid( - "split-arc-opening".into(), - Frame3 { - origin: [1.1, 0.9, 0.5], - ..Frame3::IDENTITY - }, - [1.0, 0.35, 1.5], - accuracy, - ) - .unwrap(); - let cut = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - "split-arc-cut".into(), - ) - .unwrap(); - cut.brep.validate().unwrap(); - let mut outer_faces = 0; - for face in &cut.brep.topology.faces { - let SurfaceGeometry::Cylinder { radius, .. } = - &cut.brep.geometry.surfaces[face.surface as usize] - else { - continue; - }; - if (*radius - 2.0).abs() > accuracy.geometric { - continue; - } - outer_faces += 1; - assert_eq!( - face.provenance - .sources - .iter() - .filter(|source| source.entity == host.id) - .count(), - 1, - "output face {} should belong to exactly one trimmed outer arc", - face.id - ); - } - assert!(outer_faces >= 2); - } - - #[test] - fn chained_curved_cut_keeps_each_split_top_cap_on_its_source_region() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let quarter = std::f64::consts::FRAC_PI_2; - let host = primitives::arc_edged_extrusion( - "split-cap-arc-host".into(), - Frame3::IDENTITY, - vec![ - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.0, - start_angle: 0.0, - sweep_angle: quarter, - }, - primitives::ProfileEdge::Line { - from: [0.0, 2.0], - to: [0.0, 1.5], - }, - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.5, - start_angle: quarter, - sweep_angle: -quarter, - }, - primitives::ProfileEdge::Line { - from: [1.5, 0.0], - to: [2.0, 0.0], - }, - ], - 3.0, - accuracy, - ) - .unwrap(); - let sector = |id: &str, start: f64, end: f64, bottom: f64, height: f64| { - let reach = 3.0; - primitives::linear_extrusion( - id.into(), - Frame3 { - origin: [0.0, 0.0, bottom], - ..Frame3::IDENTITY - }, - vec![ - [0.0, 0.0], - [reach * start.cos(), reach * start.sin()], - [reach * end.cos(), reach * end.sin()], - ], - Vec::new(), - height, - accuracy, - ) - .unwrap() - }; - let first_cutter = sector("full-height-sector", 0.7, 0.9, -1.0, 5.0); - let first = boolean_brep( - &host, - &first_cutter, - BooleanOp::Subtraction, - "split-cap-first".into(), - ) - .unwrap(); - first.brep.validate().unwrap(); - assert_eq!(first.brep.solids.len(), 2); - let second_cutter = sector("lower_cutout-sector", 0.2, 0.3, 0.5, 1.5); - let second = boolean_brep( - &first.brep, - &second_cutter, - BooleanOp::Subtraction, - "split-cap-second".into(), - ) - .unwrap(); - second.brep.validate().unwrap(); - let mut caps = 0; - for face in &second.brep.topology.faces { - let SurfaceGeometry::Plane { frame } = - &second.brep.geometry.surfaces[face.surface as usize] - else { - continue; - }; - if dot(frame.z, [0.0, 0.0, 1.0]) < 1.0 - 1.0e-10 - || (frame.origin[2] - 3.0).abs() > accuracy.intersection - { - continue; - } - caps += 1; - assert_eq!( - face.provenance - .sources - .iter() - .filter(|source| source.entity == first.brep.id) - .count(), - 1, - "top cap {} should come from one prior top region", - face.id - ); - } - assert_eq!(caps, 2); - } - - #[test] - fn wide_curved_opening_uses_multiple_planar_sector_cutters() { - let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; - let start = -std::f64::consts::FRAC_PI_2; - let sweep = 3.0 * std::f64::consts::FRAC_PI_2; - let point = |radius: f64, angle: f64| [radius * angle.cos(), radius * angle.sin()]; - let host = primitives::arc_edged_extrusion( - "wide-arc".into(), - Frame3::IDENTITY, - vec![ - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.1, - start_angle: start, - sweep_angle: sweep, - }, - primitives::ProfileEdge::Line { - from: point(2.1, start + sweep), - to: point(1.9, start + sweep), - }, - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.9, - start_angle: start + sweep, - sweep_angle: -sweep, - }, - primitives::ProfileEdge::Line { - from: point(1.9, start), - to: point(2.1, start), - }, - ], - 3.0, - accuracy, - ) - .unwrap(); - let opening_start = start + 1.2 / 2.0; - let opening_end = start + 8.2 / 2.0; - let cutters = (0..3) - .map(|index| { - let from = opening_start + (opening_end - opening_start) * index as f64 / 3.0; - let to = opening_start + (opening_end - opening_start) * (index + 1) as f64 / 3.0; - let reach = 2.1 / ((to - from) / 2.0).cos() + 0.2; - primitives::linear_extrusion( - format!("sector-{index}"), - Frame3::IDENTITY, - vec![[0.0, 0.0], point(reach, from), point(reach, to)], - vec![], - 2.0, - accuracy, - ) - .unwrap() - }) - .collect::>(); - let cut = subtract_planar_cutters(&host, &cutters, "wide-cut".into()).unwrap(); - cut.brep.validate().unwrap(); - tessellate(&cut.brep, 0.01, 2_000_000).unwrap(); - let angle = start + 4.7 / 2.0; - assert_eq!( - super::super::query::classify_point( - &cut.brep, - [2.0 * angle.cos(), 2.0 * angle.sin(), 1.0] - ) - .unwrap(), - super::super::query::PointClassification::Outside - ); - } -} diff --git a/main/opengeometry/src/analytic/box_booleans.rs b/main/opengeometry/src/analytic/box_booleans.rs deleted file mode 100644 index a2520cb..0000000 --- a/main/opengeometry/src/analytic/box_booleans.rs +++ /dev/null @@ -1,1025 +0,0 @@ -use std::collections::{BTreeMap, VecDeque}; - -use super::{ - booleans::{BooleanOp, BooleanReport, BooleanResult, FaceMapping}, - geometry::{dot, norm, scale, sub, unit}, - primitives::{boundary, cuboid, uv_line, Builder}, - query::{classify_point_validated, face_contains_uv, PointClassification}, - topology::*, - CurveGeometry, Frame3, GeometryError, Point3, SurfaceGeometry, -}; -use crate::math::interval::Interval; - -type GridPoint = [usize; 3]; -pub(super) struct BoxInput<'a> { - pub(super) brep: &'a BrepEnvelope, - pub(super) frame: Frame3, - pub(super) size: Point3, -} -fn coverage() -> GeometryError { - GeometryError::CoverageGap { - families: [ - "canonical aligned cuboid".into(), - "canonical aligned cuboid".into(), - ], - } -} -fn source(input: &BoxInput<'_>, face: usize) -> FaceSource { - let authored = &input.brep.topology.faces[face]; - if matches!(authored.provenance.role, FaceRole::Preserved) - && authored.provenance.sources.len() == 1 - { - return authored.provenance.sources[0].clone(); - } - FaceSource { - entity: input.brep.id.clone(), - body: input.brep.id.clone(), - key: authored.key.clone(), - face: face as u32, - } -} - -fn rectilinear_input<'a>( - brep: &'a BrepEnvelope, - axes: Frame3, -) -> Result, GeometryError> { - brep.validate()?; - if !matches!(brep.quality, GeometryQuality::Analytic) - || brep.geometry.surfaces.is_empty() - || brep.solids.is_empty() - || brep - .geometry - .surfaces - .iter() - .any(|surface| !matches!(surface, SurfaceGeometry::Plane { .. })) - || brep - .geometry - .curves - .iter() - .any(|curve| !matches!(curve, CurveGeometry::Line { .. })) - { - return Err(coverage()); - } - let directions = [axes.x, axes.y, axes.z]; - for surface in &brep.geometry.surfaces { - let normal = surface.frame().z; - if directions - .iter() - .all(|axis| dot(normal, *axis).abs() < 1.0 - 1.0e-10) - { - return Err(coverage()); - } - } - let mut lo = [f64::INFINITY; 3]; - let mut hi = [f64::NEG_INFINITY; 3]; - for vertex in &brep.topology.vertices { - let point = axes.local(vertex.position); - for axis in 0..3 { - lo[axis] = lo[axis].min(point[axis]); - hi[axis] = hi[axis].max(point[axis]); - } - } - if (0..3).any(|axis| hi[axis] - lo[axis] <= 4.0 * brep.accuracy.geometric) { - return Err(coverage()); - } - for edge in &brep.topology.edges { - let EdgeGeometry::Curve { curve, .. } = edge.geometry else { - return Err(coverage()); - }; - let CurveGeometry::Line { direction, .. } = brep.geometry.curves[curve as usize] else { - return Err(coverage()); - }; - if directions - .iter() - .all(|axis| dot(direction, *axis).abs() < 1.0 - 1.0e-10) - { - return Err(coverage()); - } - } - Ok(BoxInput { - brep, - frame: Frame3 { - origin: axes.point(lo), - ..axes - }, - size: std::array::from_fn(|axis| hi[axis] - lo[axis]), - }) -} - -fn classified_inside(brep: &BrepEnvelope, point: Point3) -> Result { - match classify_point_validated(brep, point)? { - PointClassification::Inside => Ok(true), - PointClassification::Outside => Ok(false), - _ => Err(GeometryError::UnresolvedIntersection( - "rectilinear arrangement cell classification is unresolved".into(), - )), - } -} - -fn containing_faces( - input: &BoxInput<'_>, - point: Point3, - outward: Point3, - tolerance: f64, -) -> Result, GeometryError> { - let mut matches = Vec::new(); - for (index, face) in input.brep.topology.faces.iter().enumerate() { - let SurfaceGeometry::Plane { frame } = input.brep.geometry.surfaces[face.surface as usize] - else { - return Err(coverage()); - }; - let local = frame.local(point); - if local[2].abs() > tolerance { - continue; - } - if face_contains_uv(input.brep, face, [local[0], local[1]])? != Some(true) { - continue; - } - let normal = scale(frame.z, face.sense.multiplier()); - let alignment = dot(normal, outward); - if alignment.abs() < 1.0 - 1.0e-10 { - continue; - } - matches.push((index, alignment < 0.0)); - } - Ok(matches) -} -pub(super) fn full_box(brep: &BrepEnvelope) -> Result, GeometryError> { - brep.validate()?; - let t = &brep.topology; - if !matches!(brep.quality, GeometryQuality::Analytic) - || [ - t.vertices.len(), - t.edges.len(), - t.halfedges.len(), - t.loops.len(), - t.faces.len(), - t.shells.len(), - ] != [8, 12, 24, 6, 6, 1] - || !t.wires.is_empty() - || brep.solids.len() != 1 - || brep.geometry.surfaces.len() != 6 - || brep.geometry.curves.len() != 12 - || brep.geometry.pcurves.len() != 24 - || !brep.geometry.intersections.is_empty() - { - return Err(coverage()); - } - let mut axes = [[0.0; 3]; 3]; - let mut size = [0.0; 3]; - for i in 0..3 { - let EdgeGeometry::Curve { curve, range } = t.edges[i].geometry else { - return Err(coverage()); - }; - let CurveGeometry::Line { direction, .. } = brep.geometry.curves[curve as usize] else { - return Err(coverage()); - }; - if range.lo != 0.0 { - return Err(coverage()); - } - axes[i] = direction; - size[i] = range.hi; - } - let frame = Frame3 { - origin: t.vertices[0].position, - x: axes[0], - y: axes[1], - z: axes[2], - }; - frame.validate().map_err(|_| coverage())?; - let canonical = cuboid(brep.id.clone(), frame, size, brep.accuracy)?; - let magnitude = size.into_iter().fold(0.0_f64, f64::max).max( - t.vertices - .iter() - .map(|v| norm(v.position)) - .fold(0.0_f64, f64::max), - ); - let noise = (256.0 * f64::EPSILON * magnitude).min(brep.accuracy.intersection / 8.0); - let close = |a: Point3, b: Point3| norm(sub(a, b)) <= noise; - let direction_close = - |a: Point3, b: Point3| norm(sub(a, b)) * size.into_iter().fold(0.0_f64, f64::max) <= noise; - for (a, b) in brep - .geometry - .surfaces - .iter() - .zip(&canonical.geometry.surfaces) - { - let (SurfaceGeometry::Plane { frame: a }, SurfaceGeometry::Plane { frame: b }) = (a, b) - else { - return Err(coverage()); - }; - if !close(a.origin, b.origin) - || !direction_close(a.x, b.x) - || !direction_close(a.y, b.y) - || !direction_close(a.z, b.z) - { - return Err(coverage()); - } - } - for (a, b) in brep.geometry.curves.iter().zip(&canonical.geometry.curves) { - let ( - CurveGeometry::Line { - origin: ao, - direction: ad, - }, - CurveGeometry::Line { - origin: bo, - direction: bd, - }, - ) = (a, b) - else { - return Err(coverage()); - }; - if !close(*ao, *bo) || !direction_close(*ad, *bd) { - return Err(coverage()); - } - } - for (a, b) in brep - .geometry - .pcurves - .iter() - .zip(&canonical.geometry.pcurves) - { - let ( - PcurveGeometry::Line2 { - origin: ao, - direction: ad, - }, - PcurveGeometry::Line2 { - origin: bo, - direction: bd, - }, - ) = (a, b) - else { - return Err(coverage()); - }; - for i in 0..2 { - if (ao[i] - bo[i]).abs() > noise - || (ad[i] - bd[i]).abs() * size.into_iter().fold(0.0_f64, f64::max) > noise - { - return Err(coverage()); - } - } - } - let mut topology = t.clone(); - for (v, expected) in topology - .vertices - .iter_mut() - .zip(&canonical.topology.vertices) - { - if !close(v.position, expected.position) { - return Err(coverage()); - } - v.position = expected.position; - v.tolerance = expected.tolerance; - } - for e in &mut topology.edges { - e.tolerance = brep.accuracy.geometric; - } - for (f, expected) in topology.faces.iter_mut().zip(&canonical.topology.faces) { - for i in 0..2 { - if (f.trim.uv_bounds[i].lo - expected.trim.uv_bounds[i].lo).abs() > noise - || (f.trim.uv_bounds[i].hi - expected.trim.uv_bounds[i].hi).abs() > noise - { - return Err(coverage()); - } - } - f.trim.uv_bounds = expected.trim.uv_bounds; - f.key = expected.key.clone(); - f.provenance = expected.provenance.clone(); - } - let value = |input| { - serde_json::to_value(input).map_err(|e| GeometryError::InvalidGeometry(e.to_string())) - }; - if value(&topology)? != value(&canonical.topology)? - || serde_json::to_value(&brep.solids) - .map_err(|e| GeometryError::InvalidGeometry(e.to_string()))? - != serde_json::to_value(&canonical.solids) - .map_err(|e| GeometryError::InvalidGeometry(e.to_string()))? - { - return Err(coverage()); - } - Ok(BoxInput { brep, frame, size }) -} -fn face_index(axis: usize, upper: bool) -> usize { - match axis { - 0 => 2 + usize::from(upper), - 1 => 4 + usize::from(upper), - _ => usize::from(upper), - } -} -fn inside(point: Point3, lo: Point3, hi: Point3) -> bool { - (0..3).all(|i| point[i] > lo[i] && point[i] < hi[i]) -} -fn flat_index(p: GridPoint, n: GridPoint) -> usize { - (p[0] * n[1] + p[1]) * n[2] + p[2] -} -fn neighbor(mut p: GridPoint, axis: usize, upper: bool, n: GridPoint) -> Option { - if upper { - p[axis] += 1; - if p[axis] >= n[axis] { - return None; - } - } else { - p[axis] = p[axis].checked_sub(1)?; - } - Some(p) -} - -/// Regularized material booleans for canonical cuboids sharing the same axes. -/// The bounded plane arrangement is independent of rendering tessellation. -pub fn boolean_boxes( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let a = full_box(a)?; - let b = full_box(b)?; - boolean_grid(a, b, operation, id, true) -} - -pub(super) fn boolean_rectilinear( - a: &BrepEnvelope, - b: &BrepEnvelope, - operation: BooleanOp, - id: String, -) -> Result { - let axes = match b.geometry.surfaces.first() { - Some(SurfaceGeometry::Plane { frame }) => *frame, - _ => return Err(coverage()), - }; - let a = rectilinear_input(a, axes)?; - let b = rectilinear_input(b, axes)?; - boolean_grid(a, b, operation, id, false) -} - -fn boolean_grid( - a: BoxInput<'_>, - b: BoxInput<'_>, - operation: BooleanOp, - id: String, - canonical_boxes: bool, -) -> Result { - if [a.frame.x, a.frame.y, a.frame.z] != [b.frame.x, b.frame.y, b.frame.z] { - return Err(coverage()); - } - let accuracy = Accuracy { - geometric: a.brep.accuracy.geometric.max(b.brep.accuracy.geometric), - intersection: a - .brep - .accuracy - .intersection - .max(b.brep.accuracy.intersection), - tessellation: a - .brep - .accuracy - .tessellation - .max(b.brep.accuracy.tessellation), - exchange: a.brep.accuracy.exchange.max(b.brep.accuracy.exchange), - }; - let alo = [0.0; 3]; - let ahi = a.size; - let blo = a.frame.local(b.frame.origin); - let bhi: Point3 = std::array::from_fn(|i| blo[i] + b.size[i]); - if blo.into_iter().chain(bhi).any(|value| !value.is_finite()) { - return Err(GeometryError::UnresolvedIntersection( - "cuboid relative coordinates exceed numerical range".into(), - )); - } - let mut grid: [Vec; 3] = std::array::from_fn(|i| vec![alo[i], ahi[i], blo[i], bhi[i]]); - if !canonical_boxes { - for input in [&a, &b] { - for vertex in &input.brep.topology.vertices { - let point = a.frame.local(vertex.position); - for axis in 0..3 { - grid[axis].push(point[axis]); - } - } - } - } - // Only arithmetic roundoff is merged; actual unresolved feature widths fail. - let magnitude = [a.frame.origin, b.frame.origin] - .into_iter() - .map(norm) - .fold(0.0_f64, f64::max) - .max(a.size.into_iter().fold(0.0_f64, f64::max)) - .max(b.size.into_iter().fold(0.0_f64, f64::max)); - let noise = (256.0 * f64::EPSILON * magnitude).min(accuracy.intersection / 8.0); - for values in &mut grid { - values.sort_by(f64::total_cmp); - let mut unique: Vec = Vec::new(); - for &value in values.iter() { - if let Some(last) = unique.last() { - let gap = value - last; - if gap <= noise { - continue; - } - if gap <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "cuboid arrangement contains sub-tolerance features".into(), - )); - } - } - unique.push(value); - } - *values = unique; - } - let snap = |p: Point3| { - std::array::from_fn(|i| { - grid[i] - .iter() - .copied() - .min_by(|x, y| (x - p[i]).abs().total_cmp(&(y - p[i]).abs())) - .unwrap_or(p[i]) - }) - }; - let alo = snap(alo); - let ahi = snap(ahi); - let blo = snap(blo); - let bhi = snap(bhi); - let coincident = alo == blo && ahi == bhi; - let overlap_lo: Point3 = std::array::from_fn(|i| alo[i].max(blo[i])); - let overlap_hi: Point3 = std::array::from_fn(|i| ahi[i].min(bhi[i])); - let mut contacts = Vec::new(); - if (0..3).all(|i| overlap_lo[i] <= overlap_hi[i]) - && (0..3).any(|i| overlap_lo[i] == overlap_hi[i]) - { - for mask in 0..8 { - let point = a.frame.point(std::array::from_fn(|i| { - if mask & (1 << i) == 0 { - overlap_lo[i] - } else { - overlap_hi[i] - } - })); - if !contacts.contains(&point) { - contacts.push(point); - } - } - } - let n = grid.each_ref().map(|g| g.len() - 1); - let count = n - .into_iter() - .try_fold(1usize, |product, cells| product.checked_mul(cells)) - .filter(|count| *count <= 2_000_000) - .ok_or_else(|| { - GeometryError::LimitExceeded( - "rectilinear Boolean grid exceeds two million cells".into(), - ) - })?; - let mut material = vec![false; count]; - for x in 0..n[0] { - for y in 0..n[1] { - for z in 0..n[2] { - let p = [x, y, z]; - let center = std::array::from_fn(|i| { - grid[i][p[i]] + (grid[i][p[i] + 1] - grid[i][p[i]]) / 2.0 - }); - let ia = if canonical_boxes { - inside(center, alo, ahi) - } else { - classified_inside(a.brep, a.frame.point(center))? - }; - let ib = if canonical_boxes { - inside(center, blo, bhi) - } else { - classified_inside(b.brep, a.frame.point(center))? - }; - material[flat_index(p, n)] = match operation { - BooleanOp::Union => ia || ib, - BooleanOp::Intersection => ia && ib, - BooleanOp::Subtraction => ia && !ib, - }; - } - } - } - let mut components = vec![usize::MAX; count]; - let mut component_count = 0; - for x in 0..n[0] { - for y in 0..n[1] { - for z in 0..n[2] { - let p = [x, y, z]; - let index = flat_index(p, n); - if !material[index] || components[index] != usize::MAX { - continue; - } - components[index] = component_count; - let mut queue = VecDeque::from([p]); - while let Some(cell) = queue.pop_front() { - for axis in 0..3 { - for upper in [false, true] { - if let Some(adjacent) = neighbor(cell, axis, upper, n) { - let j = flat_index(adjacent, n); - if material[j] && components[j] == usize::MAX { - components[j] = component_count; - queue.push_back(adjacent); - } - } - } - } - } - component_count += 1; - } - } - } - let mut builder = Builder::new(id, accuracy)?; - let mut vertex_ids = BTreeMap::new(); - let mut edge_ids = BTreeMap::new(); - let mut face_components = Vec::new(); - let mut volumes = Vec::new(); - for x in 0..n[0] { - for y in 0..n[1] { - for z in 0..n[2] { - let p = [x, y, z]; - let cell = flat_index(p, n); - if !material[cell] { - continue; - } - let component = components[cell]; - for axis in 0..3 { - for upper in [false, true] { - if neighbor(p, axis, upper, n).is_some_and(|q| material[flat_index(q, n)]) { - continue; - } - let coordinate = grid[axis][p[axis] + usize::from(upper)]; - let mut center: Point3 = std::array::from_fn(|i| { - grid[i][p[i]] + (grid[i][p[i] + 1] - grid[i][p[i]]) / 2.0 - }); - center[axis] = coordinate; - let mut sources = Vec::new(); - let normal_sign = if upper { 1.0 } else { -1.0 }; - let mut owner = None; - for (input, lo, hi, cutter) in [(&a, alo, ahi, false), (&b, blo, bhi, true)] - { - let faces = if canonical_boxes { - [false, true] - .into_iter() - .filter_map(|side| { - let plane = if side { hi[axis] } else { lo[axis] }; - (coordinate == plane - && (0..3) - .filter(|i| *i != axis) - .all(|i| center[i] > lo[i] && center[i] < hi[i])) - .then_some((face_index(axis, side), side != upper)) - }) - .collect::>() - } else { - containing_faces( - input, - a.frame.point(center), - scale([a.frame.x, a.frame.y, a.frame.z][axis], normal_sign), - accuracy.intersection, - )? - }; - for (face, reversed) in faces { - if !reversed || (operation == BooleanOp::Subtraction && cutter) { - sources.push(source(input, face)); - if owner.is_none() { - owner = Some((input, face, reversed, cutter, lo, hi)); - } - } - } - } - let (input, source_face, reversed, cutter, lo, hi) = - owner.ok_or_else(|| { - GeometryError::InvalidTopology( - "cuboid boundary has no input-face ancestry".into(), - ) - })?; - let surface = input.brep.geometry.surfaces - [input.brep.topology.faces[source_face].surface as usize] - .clone(); - let face_frame = *surface.frame(); - let u = (axis + 1) % 3; - let v = (axis + 2) % 3; - let mut corners = [[0; 3]; 4]; - for (i, (du, dv)) in - [(0, 0), (1, 0), (1, 1), (0, 1)].into_iter().enumerate() - { - corners[i] = p; - corners[i][axis] += usize::from(upper); - corners[i][u] += du; - corners[i][v] += dv; - } - if !upper { - corners.reverse(); - } - let local = corners.map(|q| std::array::from_fn(|i| grid[i][q[i]])); - let points = local.map(|q| a.frame.point(q)); - let mut vids = [0; 4]; - for i in 0..4 { - vids[i] = *vertex_ids - .entry((component, corners[i])) - .or_insert_with(|| builder.vertex(points[i])); - } - let mut uses = Vec::new(); - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - for i in 0..4 { - let j = (i + 1) % 4; - let (from, to) = if corners[i] < corners[j] { - (i, j) - } else { - (j, i) - }; - let key = (component, corners[from], corners[to]); - let edge = if let Some(&edge) = edge_ids.get(&key) { - edge - } else { - let delta = sub(points[to], points[from]); - let length = norm(delta); - let edge = builder.edge( - CurveGeometry::Line { - origin: points[from], - direction: unit(delta)?, - }, - Interval::new(0.0, length)?, - false, - ); - edge_ids.insert(key, edge); - edge - }; - let EdgeGeometry::Curve { curve, .. } = - builder.brep.topology.edges[edge as usize].geometry - else { - return Err(GeometryError::InvalidTopology( - "cuboid edge is not a line".into(), - )); - }; - let CurveGeometry::Line { origin, direction } = - builder.brep.geometry.curves[curve as usize] - else { - return Err(GeometryError::InvalidTopology( - "cuboid curve is not a line".into(), - )); - }; - let uv = face_frame.local(origin); - uses.push(boundary( - edge, - vids[i], - vids[j], - if i == from { - Orientation::Forward - } else { - Orientation::Reverse - }, - uv_line( - [uv[0], uv[1]], - [dot(direction, face_frame.x), dot(direction, face_frame.y)], - ), - )); - let uv = face_frame.local(points[i]); - for k in 0..2 { - bounds[k][0] = bounds[k][0].min(uv[k]); - bounds[k][1] = bounds[k][1].max(uv[k]); - } - } - for bound in &mut bounds { - bound[0] -= accuracy.geometric; - bound[1] += accuracy.geometric; - } - let face = builder.brep.topology.faces.len(); - builder - .face( - &format!("{}-{face}", input.brep.topology.faces[source_face].key), - surface, - bounds, - uses, - ) - .map_err(|error| match error { - GeometryError::InvalidTopology(message) - if message == "nonmanifold primitive edge" => - { - GeometryError::UnresolvedIntersection( - "cuboid boundary contains an unresolved contact event" - .into(), - ) - } - other => other, - })?; - let whole = canonical_boxes - && [u, v] - .into_iter() - .all(|i| grid[i][p[i]] == lo[i] && grid[i][p[i] + 1] == hi[i]); - let surface_reversed = if canonical_boxes { - reversed - } else { - dot( - face_frame.z, - scale([a.frame.x, a.frame.y, a.frame.z][axis], normal_sign), - ) < 0.0 - }; - builder.brep.topology.faces[face].sense = if surface_reversed { - Orientation::Reverse - } else { - Orientation::Forward - }; - builder.brep.topology.faces[face].provenance = FaceProvenance { - role: if sources.len() > 1 { - FaceRole::Coincident - } else if cutter && operation == BooleanOp::Subtraction { - FaceRole::Cut - } else if whole { - FaceRole::Preserved - } else { - FaceRole::Split - }, - sources, - reversed, - }; - face_components.push(component); - // Local divergence-theorem volume avoids cancellation at building coordinates. - volumes.push( - normal_sign - * coordinate - * (grid[u][p[u] + 1] - grid[u][p[u]]) - * (grid[v][p[v] + 1] - grid[v][p[v]]) - / 3.0, - ); - } - } - } - } - } - let mut out = builder.brep; - let mut assigned = vec![false; out.topology.faces.len()]; - let mut outer = vec![None; component_count]; - let mut cavities = vec![Vec::new(); component_count]; - for start in 0..out.topology.faces.len() { - if assigned[start] { - continue; - } - let shell = out.topology.shells.len() as u32; - let component = face_components[start]; - let mut faces = Vec::new(); - let mut volume = 0.0; - assigned[start] = true; - let mut queue = VecDeque::from([start]); - while let Some(face) = queue.pop_front() { - volume += volumes[face]; - faces.push(face as u32); - out.topology.faces[face].shell_ref = Some(shell); - for h in out - .topology - .halfedges - .iter() - .filter(|h| h.face == Some(face as u32)) - { - let twin = h.twin.ok_or_else(|| { - GeometryError::InvalidTopology("open cuboid boolean boundary".into()) - })?; - let adjacent = out.topology.halfedges[twin as usize].face.ok_or_else(|| { - GeometryError::InvalidTopology("cuboid twin has no face".into()) - })? as usize; - if face_components[adjacent] != component { - return Err(GeometryError::InvalidTopology( - "cuboid contact incorrectly sewed separate material components".into(), - )); - } - if !assigned[adjacent] { - assigned[adjacent] = true; - queue.push_back(adjacent); - } - } - } - out.topology.shells.push(Shell { - id: shell, - faces, - is_closed: true, - }); - if volume > 0.0 { - if outer[component].replace(shell).is_some() { - return Err(GeometryError::UnresolvedIntersection( - "cuboid material component has multiple outer shells".into(), - )); - } - } else if volume < 0.0 { - cavities[component].push(shell); - } else { - return Err(GeometryError::UnresolvedIntersection( - "cuboid shell volume is unresolved".into(), - )); - } - } - for (component, shell) in outer.into_iter().enumerate() { - out.solids.push(SolidRegion { - outer_shell: shell.ok_or_else(|| { - GeometryError::InvalidTopology("cuboid material has no outer shell".into()) - })?, - cavity_shells: cavities[component].clone(), - }); - } - out.revision = a - .brep - .revision - .max(b.brep.revision) - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; - out.validate()?; - let face_mappings = [&a, &b] - .into_iter() - .flat_map(|input| (0..input.brep.topology.faces.len()).map(move |face| source(input, face))) - .map(|source| FaceMapping { - result_faces: out - .topology - .faces - .iter() - .filter(|face| { - face.provenance.sources.iter().any(|s| { - s.entity == source.entity - && s.body == source.body - && s.key == source.key - && s.face == source.face - }) - }) - .map(|face| face.id) - .collect(), - source, - }) - .collect(); - Ok(BooleanResult { - report: BooleanReport { - operation, - quality: out.quality.clone(), - contacts, - coincident, - face_mappings, - }, - brep: out, - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::{ - exchange::export_step, ifc_exchange::prepare_ifc_body, placement::placed, - tessellation::tessellate, - }; - fn box_at(id: &str, origin: Point3, size: Point3) -> BrepEnvelope { - cuboid( - id.into(), - Frame3 { - origin, - x: [1.0, 0.0, 0.0], - y: [0.0, 1.0, 0.0], - z: [0.0, 0.0, 1.0], - }, - size, - Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 1e-3, - exchange: 1e-6, - }, - ) - .unwrap() - } - fn volume(brep: &BrepEnvelope) -> f64 { - let mesh = tessellate(brep, 0.01, 100_000).unwrap(); - mesh.indices - .chunks_exact(3) - .map(|ids| { - let p: [Point3; 3] = std::array::from_fn(|j| { - std::array::from_fn(|i| mesh.positions[3 * ids[j] as usize + i]) - }); - dot(p[0], crate::analytic::geometry::cross(p[1], p[2])) / 6.0 - }) - .sum() - } - #[test] - fn aligned_box_boolean_volume_topology_provenance_and_exchange() { - let a = box_at("host", [0.0; 3], [2.0; 3]); - let b = box_at("cutter", [1.0, 0.5, 0.5], [2.0; 3]); - for (op, expected) in [ - (BooleanOp::Union, 13.75), - (BooleanOp::Intersection, 2.25), - (BooleanOp::Subtraction, 5.75), - ] { - let result = boolean_boxes(&a, &b, op, "result".into()).unwrap(); - assert!((volume(&result.brep) - expected).abs() < 1e-10); - assert_eq!(result.brep.solids.len(), 1); - assert_eq!(result.report.face_mappings.len(), 12); - assert!(result - .brep - .topology - .faces - .iter() - .all(|f| !f.provenance.sources.is_empty())); - assert!(result - .brep - .geometry - .surfaces - .iter() - .all(|s| matches!(s, SurfaceGeometry::Plane { .. }))); - let fine = tessellate(&result.brep, 0.001, 100_000).unwrap(); - let coarse = tessellate(&result.brep, 0.1, 100_000).unwrap(); - assert_eq!(fine.indices.len(), coarse.indices.len()); - if op == BooleanOp::Union { - assert!(fine.outline_edge_ids.len() < result.brep.topology.edges.len()); - } - export_step(&result.brep, "metre").unwrap(); - prepare_ifc_body(&result.brep).unwrap(); - if op == BooleanOp::Subtraction { - assert!(result - .brep - .topology - .faces - .iter() - .any(|f| f.provenance.role == FaceRole::Cut - && f.provenance.reversed - && f.sense == Orientation::Reverse)); - } - } - } - #[test] - fn aligned_box_cavities_split_solids_and_contacts() { - let a = box_at("host", [0.0; 3], [3.0; 3]); - let b = box_at("cavity", [1.0; 3], [1.0; 3]); - let result = boolean_boxes(&a, &b, BooleanOp::Subtraction, "cavity-result".into()).unwrap(); - assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); - assert!((volume(&result.brep) - 26.0).abs() < 1e-10); - let cutter = box_at("through", [1.0, -1.0, -1.0], [1.0, 5.0, 5.0]); - let result = boolean_boxes(&a, &cutter, BooleanOp::Subtraction, "split".into()).unwrap(); - assert_eq!(result.brep.solids.len(), 2); - assert!((volume(&result.brep) - 18.0).abs() < 1e-10); - for (origin, solids, contacts) in [ - ([3.0, 0.0, 0.0], 1, 4), - ([3.0, 3.0, 0.0], 2, 2), - ([3.0; 3], 2, 1), - ] { - let b = box_at("contact", origin, [3.0; 3]); - let result = boolean_boxes(&a, &b, BooleanOp::Union, "contact-result".into()).unwrap(); - assert_eq!(result.brep.solids.len(), solids); - assert_eq!(result.report.contacts.len(), contacts); - let intersection = - boolean_boxes(&a, &b, BooleanOp::Intersection, "empty".into()).unwrap(); - assert!(intersection.brep.solids.is_empty()); - } - } - #[test] - fn aligned_box_boolean_identities_determinism_and_scale() { - let a = box_at("a", [0.0; 3], [2.0; 3]); - for op in [ - BooleanOp::Union, - BooleanOp::Intersection, - BooleanOp::Subtraction, - ] { - let result = - super::super::booleans::boolean_brep(&a, &a, op, "identity".into()).unwrap(); - let expected = if op == BooleanOp::Subtraction { - 0.0 - } else { - 8.0 - }; - assert!((volume(&result.brep) - expected).abs() < 1e-10); - let repeat = - super::super::booleans::boolean_brep(&a, &a, op, "identity".into()).unwrap(); - assert_eq!( - result.brep.to_json().unwrap(), - repeat.brep.to_json().unwrap() - ); - assert_eq!(result.brep.revision, a.revision + 1); - } - for factor in [1e-8, 1.0, 1e5] { - let b = box_at("b", [1.0, 0.5, 0.5], [2.0; 3]); - let frame = Frame3::from_axis([0.0; 3], [0.0, 1.0, 0.0], [0.8, 0.0, 0.6]).unwrap(); - let a = placed(&a, frame, factor).unwrap(); - let b = placed(&b, frame, factor).unwrap(); - let result = boolean_boxes(&a, &b, BooleanOp::Subtraction, "scaled".into()).unwrap(); - let mesh = tessellate(&result.brep, 0.01 * factor, 100_000).unwrap(); - let measured: f64 = mesh - .indices - .chunks_exact(3) - .map(|ids| { - let p: [Point3; 3] = std::array::from_fn(|j| { - std::array::from_fn(|i| mesh.positions[3 * ids[j] as usize + i] / factor) - }); - dot(p[0], crate::analytic::geometry::cross(p[1], p[2])) / 6.0 - }) - .sum(); - assert!((measured - 5.75).abs() < 1e-9); - } - } - #[test] - fn aligned_box_similarity_and_resolution_errors() { - let a = box_at("a", [0.0; 3], [2.0; 3]); - let b = box_at("b", [1.0, 0.5, 0.5], [2.0; 3]); - let frame = Frame3::from_axis([5.0, 6.0, 7.0], [0.0, 1.0, 0.0], [0.8, 0.0, 0.6]).unwrap(); - let a = placed(&a, frame, 1.25).unwrap(); - let b = placed(&b, frame, 1.25).unwrap(); - let result = boolean_boxes(&a, &b, BooleanOp::Subtraction, "placed".into()).unwrap(); - assert!((volume(&result.brep) - 5.75 * 1.25_f64.powi(3)).abs() < 1e-9); - let other = box_at("different-axes", [0.0; 3], [2.0; 3]); - assert!(matches!( - boolean_boxes(&a, &other, BooleanOp::Union, "gap".into()), - Err(GeometryError::CoverageGap { .. }) - )); - let a = box_at("a", [0.0; 3], [2.0; 3]); - let b = box_at("b", [2.0 + 2.0 * a.accuracy.geometric, 0.0, 0.0], [2.0; 3]); - assert!(matches!( - boolean_boxes(&a, &b, BooleanOp::Union, "unresolved".into()), - Err(GeometryError::UnresolvedIntersection(_)) - )); - } -} diff --git a/main/opengeometry/src/analytic/curved_layered_boolean.rs b/main/opengeometry/src/analytic/curved_layered_boolean.rs deleted file mode 100644 index 6d826c8..0000000 --- a/main/opengeometry/src/analytic/curved_layered_boolean.rs +++ /dev/null @@ -1,1540 +0,0 @@ -//! Vertical subtraction for an arc-edged extrusion and a planar prism. -//! The planar arrangement remains analytic at every height slab; the result is -//! assembled from planar caps and planar/cylindrical side faces in one B-rep. - -use std::collections::{BTreeMap, VecDeque}; - -use super::{ - booleans::{analytic_face_mappings, brep_face_source, BooleanOp, BooleanReport, BooleanResult}, - geometry::{add, cross, dot, norm, scale, sub, unit}, - primitives::{boundary, plane_boundary, uv_line, Builder, Use}, - query::{classify_point_in_shell, face_contains_uv, PointClassification}, - topology::{ - Accuracy, BrepEnvelope, EdgeGeometry, Face, FaceProvenance, FaceRole, GeometryQuality, - Orientation, Shell, SolidRegion, - }, - CurveGeometry, Frame3, GeometryError, Point3, Surface, SurfaceGeometry, -}; -use crate::{ - geometry::poly2d::Pt2, - geometry::{ - boolean2d::PlanarBooleanOp, - curved_boolean2d::{ - boolean_curved_regions, intersections, winding, CurveEdge2, CurveRegion2, - }, - }, - math::interval::Interval, -}; - -fn coverage() -> GeometryError { - GeometryError::CoverageGap { - families: [ - "vertical arc-edged extrusion".into(), - "planar cutter".into(), - ], - } -} - -fn area(ring: &[CurveEdge2]) -> f64 { - ring.iter().map(CurveEdge2::twice_area).sum::() / 2.0 -} - -fn canonical_arc_start(start: f64, sweep: f64) -> f64 { - let lower = start.min(start + sweep); - lower.rem_euclid(std::f64::consts::TAU) + start - lower -} - -fn reverse(ring: &[CurveEdge2]) -> Vec { - ring.iter().rev().map(CurveEdge2::reverse).collect() -} - -fn face_region( - host: &BrepEnvelope, - face: &Face, - base: Frame3, -) -> Result { - let read_loop = |loop_id: u32| -> Result, GeometryError> { - let start = host - .topology - .loops - .get(loop_id as usize) - .ok_or_else(coverage)? - .start_halfedge; - let mut current = start; - let mut edges = Vec::new(); - loop { - let halfedge = host - .topology - .halfedges - .get(current as usize) - .ok_or_else(coverage)?; - let from = base.local( - host.topology - .vertices - .get(halfedge.from as usize) - .ok_or_else(coverage)? - .position, - ); - let to = base.local( - host.topology - .vertices - .get(halfedge.to as usize) - .ok_or_else(coverage)? - .position, - ); - let edge = host - .topology - .edges - .get(halfedge.edge as usize) - .ok_or_else(coverage)?; - let EdgeGeometry::Curve { curve, range } = edge.geometry else { - return Err(coverage()); - }; - let curve = host - .geometry - .curves - .get(curve as usize) - .ok_or_else(coverage)?; - edges.push(match curve { - CurveGeometry::Line { .. } => CurveEdge2::Line { - from: [from[0], from[1]], - to: [to[0], to[1]], - }, - CurveGeometry::Circle { frame, radius } => { - if dot(frame.z, base.z).abs() < 1.0 - 1e-10 { - return Err(coverage()); - } - let centre = base.local(frame.origin); - let start_angle = (from[1] - centre[1]).atan2(from[0] - centre[0]); - let sense = if halfedge.geometry_use.sense == Orientation::Forward { - 1.0 - } else { - -1.0 - }; - CurveEdge2::Arc { - center: [centre[0], centre[1]], - radius: *radius, - start_angle, - sweep_angle: sense * dot(frame.z, base.z).signum() * (range.hi - range.lo), - } - } - _ => return Err(coverage()), - }); - current = halfedge.next.ok_or_else(coverage)?; - if current == start { - break; - } - if edges.len() > host.topology.halfedges.len() { - return Err(coverage()); - } - } - if edges.len() == 1 { - if let CurveEdge2::Arc { - center, - radius, - start_angle, - sweep_angle, - } = edges[0].clone() - { - if sweep_angle.abs() >= std::f64::consts::TAU - 1e-9 { - edges = vec![ - CurveEdge2::Arc { - center, - radius, - start_angle, - sweep_angle: sweep_angle / 2.0, - }, - CurveEdge2::Arc { - center, - radius, - start_angle: start_angle + sweep_angle / 2.0, - sweep_angle: sweep_angle / 2.0, - }, - ]; - } - } - } - Ok(edges) - }; - let mut edges = read_loop(face.trim.outer)?; - if area(&edges) > 0.0 { - edges = reverse(&edges); - } - let holes = face - .trim - .holes - .iter() - .map(|loop_id| { - let mut ring = read_loop(*loop_id)?; - if area(&ring) < 0.0 { - ring = reverse(&ring); - } - Ok(ring) - }) - .collect::, GeometryError>>()?; - Ok(CurveRegion2 { - outer: edges, - holes, - }) -} - -fn face_profile( - host: &BrepEnvelope, - base: Frame3, - level: f64, - tolerance: f64, -) -> Result { - let face = host - .topology - .faces - .iter() - .find(|face| { - matches!(host.geometry.surfaces.get(face.surface as usize), - Some(SurfaceGeometry::Plane { frame }) if dot(frame.z, base.z) > 1.0 - 1e-10 - && (base.local(frame.origin)[2] - level).abs() <= tolerance) - }) - .ok_or_else(coverage)?; - face_region(host, face, base) -} - -fn sectional_regions( - host: &BrepEnvelope, - base: Frame3, - level: f64, - tolerance: f64, -) -> Result, GeometryError> { - let mut edges = Vec::new(); - for face in &host.topology.faces { - let start = host.topology.loops[face.trim.outer as usize].start_halfedge; - let mut current = start; - let mut lowest = f64::INFINITY; - let mut highest = f64::NEG_INFINITY; - let mut boundary = Vec::new(); - loop { - let halfedge = &host.topology.halfedges[current as usize]; - let from = host.topology.vertices[halfedge.from as usize].position; - let to = host.topology.vertices[halfedge.to as usize].position; - for point in [from, to] { - let height = base.local(point)[2]; - lowest = lowest.min(height); - highest = highest.max(height); - } - boundary.push((current, from, to)); - current = halfedge.next.ok_or_else(coverage)?; - if current == start { - break; - } - if boundary.len() > host.topology.halfedges.len() { - return Err(coverage()); - } - } - if level <= lowest + tolerance || level >= highest - tolerance { - continue; - } - if !face.trim.holes.is_empty() { - return Err(coverage()); - } - match &host.geometry.surfaces[face.surface as usize] { - SurfaceGeometry::Plane { frame } if dot(frame.z, base.z).abs() > 1.0e-10 => { - return Err(coverage()); - } - SurfaceGeometry::Cylinder { frame, .. } - if dot(frame.z, base.z).abs() < 1.0 - 1.0e-10 => - { - return Err(coverage()); - } - SurfaceGeometry::Plane { .. } | SurfaceGeometry::Cylinder { .. } => {} - _ => return Err(coverage()), - } - let bottom = boundary - .iter() - .filter(|(_, from, to)| { - (base.local(*from)[2] - lowest).abs() <= tolerance - && (base.local(*to)[2] - lowest).abs() <= tolerance - }) - .collect::>(); - if bottom.len() != 1 { - return Err(coverage()); - } - let &(halfedge_id, from, to) = bottom[0]; - let halfedge = &host.topology.halfedges[halfedge_id as usize]; - let edge = &host.topology.edges[halfedge.edge as usize]; - let EdgeGeometry::Curve { curve, range } = edge.geometry else { - return Err(coverage()); - }; - let from = base.local(from); - let to = base.local(to); - match &host.geometry.curves[curve as usize] { - CurveGeometry::Line { .. } => edges.push(CurveEdge2::Line { - from: [from[0], from[1]], - to: [to[0], to[1]], - }), - CurveGeometry::Circle { frame, radius } => { - if dot(frame.z, base.z).abs() < 1.0 - 1.0e-10 { - return Err(coverage()); - } - let centre = base.local(frame.origin); - let start_angle = (from[1] - centre[1]).atan2(from[0] - centre[0]); - let sense = if halfedge.geometry_use.sense == Orientation::Forward { - 1.0 - } else { - -1.0 - }; - let sweep = sense * dot(frame.z, base.z).signum() * (range.hi - range.lo); - if sweep.abs() >= std::f64::consts::TAU - 1.0e-9 { - for half in 0..2 { - edges.push(CurveEdge2::Arc { - center: [centre[0], centre[1]], - radius: *radius, - start_angle: start_angle + sweep * half as f64 / 2.0, - sweep_angle: sweep / 2.0, - }); - } - } else { - edges.push(CurveEdge2::Arc { - center: [centre[0], centre[1]], - radius: *radius, - start_angle, - sweep_angle: sweep, - }); - } - } - _ => return Err(coverage()), - } - } - let mut rings = Vec::>::new(); - while let Some(first) = edges.pop() { - let start = first.point(0.0); - let mut end = first.point(1.0); - let mut ring = vec![first]; - while (end.x - start.x).hypot(end.z - start.z) > tolerance { - let matches = edges - .iter() - .enumerate() - .filter_map(|(index, edge)| { - let from = edge.point(0.0); - let to = edge.point(1.0); - let forward = (from.x - end.x).hypot(from.z - end.z) <= tolerance; - let reverse = (to.x - end.x).hypot(to.z - end.z) <= tolerance; - (forward || reverse).then_some((index, reverse)) - }) - .collect::>(); - if matches.len() != 1 { - return Err(coverage()); - } - let (index, reverse) = matches[0]; - let mut next = edges.swap_remove(index); - if reverse { - next = next.reverse(); - } - end = next.point(1.0); - ring.push(next); - } - if area(&ring).abs() <= tolerance * tolerance { - return Err(coverage()); - } - rings.push(ring); - } - let mut samples = Vec::new(); - for ring in &rings { - let edge = &ring[0]; - let middle = edge.point(0.5); - let before = edge.point(0.49); - let after = edge.point(0.51); - let tangent = Pt2::new(after.x - before.x, after.z - before.z); - let length = tangent.x.hypot(tangent.z); - if length <= tolerance { - return Err(coverage()); - } - let offset = (length * 0.1).min(1.0e-4).max(tolerance * 16.0); - let normal = Pt2::new(-tangent.z * offset / length, tangent.x * offset / length); - let left = Pt2::new(middle.x + normal.x, middle.z + normal.z); - let right = Pt2::new(middle.x - normal.x, middle.z - normal.z); - let left_inside = winding(left, ring, tolerance) != 0; - let right_inside = winding(right, ring, tolerance) != 0; - let sample = match (left_inside, right_inside) { - (true, false) => left, - (false, true) => right, - _ => return Err(coverage()), - }; - samples.push(sample); - } - let depths = samples - .iter() - .enumerate() - .map(|(index, sample)| { - rings - .iter() - .enumerate() - .filter(|(other, ring)| *other != index && winding(*sample, ring, tolerance) != 0) - .count() - }) - .collect::>(); - let mut regions = Vec::<(usize, CurveRegion2)>::new(); - for (index, ring) in rings.iter().enumerate() { - if depths[index] % 2 == 0 { - regions.push(( - index, - CurveRegion2 { - outer: if area(ring) < 0.0 { - ring.clone() - } else { - reverse(ring) - }, - holes: Vec::new(), - }, - )); - } - } - for (index, ring) in rings.iter().enumerate() { - if depths[index] % 2 == 0 { - continue; - } - let parent = regions - .iter() - .enumerate() - .filter(|(_, (outer, region))| { - depths[*outer] + 1 == depths[index] - && winding(samples[index], ®ion.outer, tolerance) != 0 - }) - .map(|(parent, (_, region))| (parent, area(®ion.outer).abs())) - .min_by(|left, right| left.1.total_cmp(&right.1)) - .map(|(parent, _)| parent) - .ok_or_else(coverage)?; - regions[parent].1.holes.push(if area(ring) > 0.0 { - ring.clone() - } else { - reverse(ring) - }); - } - Ok(regions.into_iter().map(|(_, region)| region).collect()) -} - -fn append_points(regions: &[CurveRegion2], points: &mut Vec<[f64; 2]>) { - for region in regions { - for ring in std::iter::once(®ion.outer).chain(®ion.holes) { - points.extend(ring.iter().map(|edge| { - let p = edge.point(0.0); - [p.x, p.z] - })); - } - } -} - -fn split_ring(ring: &[CurveEdge2], points: &[[f64; 2]], tolerance: f64) -> Vec { - ring.iter() - .flat_map(|edge| { - let mut stations = vec![0.0, 1.0]; - for point in points { - if let Some(station) = edge.parameter(Pt2::new(point[0], point[1]), tolerance) { - if station > 1e-10 && station < 1.0 - 1e-10 { - stations.push(station); - } - } - } - stations.sort_by(f64::total_cmp); - stations.dedup_by(|a, b| (*a - *b).abs() < 1e-10); - stations - .windows(2) - .map(|span| edge.slice(span[0], span[1])) - .collect::>() - }) - .collect() -} - -fn split_regions(regions: &mut [CurveRegion2], points: &[[f64; 2]], tolerance: f64) { - for region in regions { - region.outer = split_ring(®ion.outer, points, tolerance); - for hole in &mut region.holes { - *hole = split_ring(hole, points, tolerance); - } - } -} - -struct Facets { - builder: Builder, - vertices: Vec<(Point3, u32)>, - edges: BTreeMap<(u32, u32, i64, i64, i64), (u32, u32, u32, bool)>, - accuracy: Accuracy, - base: Frame3, -} - -struct SidePatch { - edge: CurveEdge2, - levels: Vec, - provenance: FaceProvenance, -} - -fn same_side_patch(a: &SidePatch, b: &SidePatch, tolerance: f64) -> bool { - if std::mem::discriminant(&a.edge) != std::mem::discriminant(&b.edge) - || a.provenance.role != b.provenance.role - || a.provenance.reversed != b.provenance.reversed - || a.provenance.sources.len() != b.provenance.sources.len() - || !a - .provenance - .sources - .iter() - .zip(&b.provenance.sources) - .all(|(a, b)| { - a.entity == b.entity && a.body == b.body && a.key == b.key && a.face == b.face - }) - || (a.edge.length() - b.edge.length()).abs() > tolerance - { - return false; - } - [0.0, 0.5, 1.0].into_iter().all(|station| { - let a = a.edge.point(station); - let b = b.edge.point(station); - (a.x - b.x).hypot(a.z - b.z) <= tolerance - }) -} - -impl Facets { - fn new(id: String, accuracy: Accuracy, base: Frame3) -> Result { - Ok(Self { - builder: Builder::new(id, accuracy)?, - vertices: Vec::new(), - edges: BTreeMap::new(), - accuracy, - base, - }) - } - - fn vertex(&mut self, point: Point3) -> u32 { - if let Some((_, id)) = self - .vertices - .iter() - .find(|(other, _)| norm(sub(*other, point)) <= self.accuracy.geometric / 4.0) - { - return *id; - } - let id = self.builder.vertex(point); - self.vertices.push((point, id)); - id - } - - fn edge( - &mut self, - from: Point3, - to: Point3, - middle: Point3, - circle: Option<([f64; 2], f64, f64, f64)>, - ) -> Result<(u32, u32, u32, Orientation), GeometryError> { - let from_id = self.vertex(from); - let to_id = self.vertex(to); - if from_id == to_id { - return Err(coverage()); - } - let step = self.accuracy.geometric / 4.0; - let key = ( - from_id.min(to_id), - from_id.max(to_id), - (middle[0] / step).round() as i64, - (middle[1] / step).round() as i64, - (middle[2] / step).round() as i64, - ); - if let Some(&(id, stored_from, stored_to, forward)) = self.edges.get(&key) { - let same = from_id == stored_from && to_id == stored_to; - let orientation = if same == forward { - Orientation::Forward - } else { - Orientation::Reverse - }; - return Ok((id, from_id, to_id, orientation)); - } - let (curve, range, forward) = if let Some((centre, radius, start, sweep)) = circle { - let origin = self - .base - .point([centre[0], centre[1], self.base.local(from)[2]]); - let frame = Frame3 { - origin, - ..self.base - }; - let start = canonical_arc_start(start, sweep); - let end = start + sweep; - ( - CurveGeometry::Circle { frame, radius }, - Interval::new(start.min(end), start.max(end))?, - sweep > 0.0, - ) - } else { - let delta = sub(to, from); - ( - CurveGeometry::Line { - origin: from, - direction: unit(delta)?, - }, - Interval::new(0.0, norm(delta))?, - true, - ) - }; - let id = self.builder.edge(curve, range, false); - self.edges.insert(key, (id, from_id, to_id, forward)); - Ok(( - id, - from_id, - to_id, - if forward { - Orientation::Forward - } else { - Orientation::Reverse - }, - )) - } - - fn edge_at( - &mut self, - edge: &CurveEdge2, - level: f64, - ) -> Result<(u32, u32, u32, Orientation), GeometryError> { - let point = |t| { - let p = edge.point(t); - self.base.point([p.x, p.z, level]) - }; - let curve = match edge { - CurveEdge2::Arc { - center, - radius, - start_angle, - sweep_angle, - } => Some((*center, *radius, *start_angle, *sweep_angle)), - CurveEdge2::Line { .. } => None, - }; - self.edge(point(0.0), point(1.0), point(0.5), curve) - } - - fn cap_uses( - &mut self, - ring: &[CurveEdge2], - level: f64, - frame: Frame3, - ) -> Result, GeometryError> { - let mut uses = Vec::new(); - for edge in ring { - let (id, from, to, sense) = self.edge_at(edge, level)?; - uses.push(plane_boundary(&self.builder, frame, id, from, to, sense)?); - } - Ok(uses) - } - - fn cap( - &mut self, - regions: &[CurveRegion2], - level: f64, - up: bool, - provenance: FaceProvenance, - ) -> Result<(), GeometryError> { - let origin = self.base.point([0.0, 0.0, level]); - let frame = if up { - Frame3 { - origin, - ..self.base - } - } else { - Frame3 { - origin, - x: self.base.x, - y: scale(self.base.y, -1.0), - z: scale(self.base.z, -1.0), - } - }; - for region in regions { - let outer = if up { - region.outer.clone() - } else { - reverse(®ion.outer) - }; - let holes = if up { - region.holes.clone() - } else { - region.holes.iter().map(|ring| reverse(ring)).collect() - }; - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - for edge in outer.iter().chain(holes.iter().flatten()) { - let mut stations = vec![0.0, 0.5, 1.0]; - if let CurveEdge2::Arc { center, radius, .. } = edge { - for angle in [ - 0.0, - std::f64::consts::FRAC_PI_2, - std::f64::consts::PI, - 3.0 * std::f64::consts::FRAC_PI_2, - ] { - let point = Pt2::new( - center[0] + radius * angle.cos(), - center[1] + radius * angle.sin(), - ); - if let Some(station) = edge.parameter(point, self.accuracy.intersection) { - stations.push(station); - } - } - } - for t in stations { - let point = edge.point(t); - let local = frame.local(self.base.point([point.x, point.z, level])); - for axis in 0..2 { - bounds[axis][0] = bounds[axis][0].min(local[axis]); - bounds[axis][1] = bounds[axis][1].max(local[axis]); - } - } - } - for bound in &mut bounds { - bound[0] -= self.accuracy.geometric; - bound[1] += self.accuracy.geometric; - } - let outer_uses = self.cap_uses(&outer, level, frame)?; - let hole_uses = holes - .iter() - .map(|ring| self.cap_uses(ring, level, frame)) - .collect::, _>>()?; - let id = self.builder.brep.topology.faces.len(); - self.builder.face_with_holes( - &format!("{}-cap-{id}", if up { "upper" } else { "lower" }), - SurfaceGeometry::Plane { frame }, - bounds, - outer_uses, - hole_uses, - )?; - self.builder.brep.topology.faces[id].provenance = provenance.clone(); - } - Ok(()) - } - - fn side( - &mut self, - edge: &CurveEdge2, - levels: &[f64], - provenance: FaceProvenance, - ) -> Result<(), GeometryError> { - if levels.len() < 2 || levels.windows(2).any(|span| span[1] <= span[0]) { - return Err(coverage()); - } - let lo = levels[0]; - let hi = *levels.last().ok_or_else(coverage)?; - let base = self.base; - let point = |t, level| { - let p = edge.point(t); - base.point([p.x, p.z, level]) - }; - let lower_from = point(0.0, lo); - let lower_to = point(1.0, lo); - let (bottom, bf, bt, bottom_sense) = self.edge_at(edge, lo)?; - let (top, tf, tt, top_sense) = self.edge_at(edge, hi)?; - let mut end_segments = Vec::with_capacity(levels.len() - 1); - let mut start_segments = Vec::with_capacity(levels.len() - 1); - for span in levels.windows(2) { - let from = point(1.0, span[0]); - let to = point(1.0, span[1]); - let (edge, from_id, to_id, sense) = - self.edge(from, to, scale(add(from, to), 0.5), None)?; - end_segments.push((edge, from_id, to_id, sense, span[0], span[1])); - } - for span in levels.windows(2).rev() { - let from = point(0.0, span[1]); - let to = point(0.0, span[0]); - let (edge, from_id, to_id, sense) = - self.edge(from, to, scale(add(from, to), 0.5), None)?; - start_segments.push((edge, from_id, to_id, sense, span[0], span[1])); - } - let id = self.builder.brep.topology.faces.len(); - match edge { - CurveEdge2::Line { .. } => { - let edge_direction = unit(sub(lower_to, lower_from))?; - let normal = unit(cross(edge_direction, self.base.z))?; - let frame = Frame3::from_axis(lower_from, normal, edge_direction)?; - let mut uses = Vec::with_capacity(2 + 2 * end_segments.len()); - uses.push(plane_boundary( - &self.builder, - frame, - bottom, - bf, - bt, - bottom_sense, - )?); - for &(edge, from, to, sense, _, _) in &end_segments { - uses.push(plane_boundary(&self.builder, frame, edge, from, to, sense)?); - } - uses.push(plane_boundary( - &self.builder, - frame, - top, - tt, - tf, - if top_sense == Orientation::Forward { - Orientation::Reverse - } else { - Orientation::Forward - }, - )?); - for &(edge, from, to, sense, _, _) in &start_segments { - uses.push(plane_boundary(&self.builder, frame, edge, from, to, sense)?); - } - self.builder.face( - &format!("vertical-plane-{id}"), - SurfaceGeometry::Plane { frame }, - [ - [ - -self.accuracy.geometric, - norm(sub(lower_to, lower_from)) + self.accuracy.geometric, - ], - [-self.accuracy.geometric, hi - lo + self.accuracy.geometric], - ], - uses, - )?; - // Layer boundaries are walked clockwise with material to - // their right; the side frame's natural normal points left. - self.builder.brep.topology.faces[id].sense = Orientation::Reverse; - } - CurveEdge2::Arc { - center, - radius, - start_angle, - sweep_angle, - } => { - let origin = self.base.point([center[0], center[1], 0.0]); - let frame = Frame3 { - origin, - ..self.base - }; - let start_angle = canonical_arc_start(*start_angle, *sweep_angle); - let end_angle = start_angle + sweep_angle; - let mut uses = Vec::with_capacity(2 + 2 * end_segments.len()); - uses.push(boundary( - bottom, - bf, - bt, - bottom_sense, - uv_line([0.0, lo], [1.0, 0.0]), - )); - for &(edge, from, to, sense, start, end) in &end_segments { - uses.push(boundary( - edge, - from, - to, - sense, - if sense == Orientation::Forward { - uv_line([end_angle, start], [0.0, 1.0]) - } else { - uv_line([end_angle, end], [0.0, -1.0]) - }, - )); - } - uses.push(boundary( - top, - tt, - tf, - if top_sense == Orientation::Forward { - Orientation::Reverse - } else { - Orientation::Forward - }, - uv_line([0.0, hi], [1.0, 0.0]), - )); - for &(edge, from, to, sense, start, end) in &start_segments { - uses.push(boundary( - edge, - from, - to, - sense, - if sense == Orientation::Forward { - uv_line([start_angle, end], [0.0, -1.0]) - } else { - uv_line([start_angle, start], [0.0, 1.0]) - }, - )); - } - self.builder.face( - &format!("vertical-cylinder-{id}"), - SurfaceGeometry::Cylinder { - frame, - radius: *radius, - }, - [ - [ - start_angle.min(end_angle) - self.accuracy.geometric / radius, - start_angle.max(end_angle) + self.accuracy.geometric / radius, - ], - [lo - self.accuracy.geometric, hi + self.accuracy.geometric], - ], - uses, - )?; - // Layer outer rings are clockwise and holes counterclockwise. - // A counterclockwise circular edge therefore bounds an inner - // face; its natural radial normal points into material. - if *sweep_angle > 0.0 { - self.builder.brep.topology.faces[id].sense = Orientation::Reverse; - } - } - } - self.builder.brep.topology.faces[id].provenance = provenance; - Ok(()) - } -} - -fn source_for_side( - brep: &BrepEnvelope, - point: Point3, - axis: Point3, - tolerance: f64, -) -> Result, GeometryError> { - let mut sources = Vec::new(); - for face in &brep.topology.faces { - let surface = brep.geometry.surface(face.surface)?; - let on = match surface { - SurfaceGeometry::Plane { frame } => { - let local = frame.local(point); - local[2].abs() <= tolerance && dot(frame.z, axis).abs() < 1.0 - 1e-10 - } - SurfaceGeometry::Cylinder { frame, radius } => { - let local = frame.local(point); - (local[0].hypot(local[1]) - radius).abs() <= tolerance - } - _ => false, - }; - if !on { - continue; - } - let hint = [ - face.trim.uv_bounds[0].midpoint(), - face.trim.uv_bounds[1].midpoint(), - ]; - let uv = surface.project(point, Some(hint))?; - match face_contains_uv(brep, face, uv)? { - Some(true) => sources.push(brep_face_source(brep, face.id)), - Some(false) => {} - None => return Err(coverage()), - } - } - Ok(sources) -} - -fn contains_region_point(region: &CurveRegion2, point: Pt2, tolerance: f64) -> bool { - winding(point, ®ion.outer, tolerance) != 0 - && region - .holes - .iter() - .all(|hole| winding(point, hole, tolerance) == 0) -} - -// Cap ancestry only asks whether two trimmed regions share positive area. Split -// at the same analytic edge intersections as the Boolean arrangement, then -// probe each open boundary interval on its material side. This also handles -// coincident circular edges without assembling a second, potentially -// degenerate Boolean boundary solely for provenance. -fn cap_regions_overlap(a: &CurveRegion2, b: &CurveRegion2, tolerance: f64) -> bool { - for (region, other) in [(a, b), (b, a)] { - for ring in std::iter::once(®ion.outer).chain(®ion.holes) { - for edge in ring { - let mut parameters = vec![0.0, 1.0]; - for other_ring in std::iter::once(&other.outer).chain(&other.holes) { - for other_edge in other_ring { - parameters.extend( - intersections(edge, other_edge, tolerance) - .into_iter() - .filter_map(|point| edge.parameter(point, tolerance)), - ); - } - } - parameters.sort_by(f64::total_cmp); - parameters - .dedup_by(|left, right| (*left - *right).abs() * edge.length() <= tolerance); - for span in parameters.windows(2) { - let interval_length = (span[1] - span[0]) * edge.length(); - if interval_length <= tolerance * 4.0 { - continue; - } - let midpoint = (span[0] + span[1]) / 2.0; - let point = edge.point(midpoint); - let tangent = edge.tangent(midpoint); - let tangent_length = tangent.x.hypot(tangent.z); - if tangent_length <= tolerance { - continue; - } - let mut offset = (interval_length * 0.1).min(1.0e-4); - for _ in 0..8 { - if offset <= tolerance * 2.0 { - break; - } - let probe = Pt2::new( - point.x + tangent.z * offset / tangent_length, - point.z - tangent.x * offset / tangent_length, - ); - if contains_region_point(region, probe, tolerance) - && contains_region_point(other, probe, tolerance) - { - return true; - } - offset *= 0.5; - } - } - } - } - } - false -} - -#[cfg(test)] -mod cap_overlap_tests { - use super::*; - - fn rectangle(x0: f64, z0: f64, x1: f64, z1: f64) -> CurveRegion2 { - let points = [[x0, z0], [x0, z1], [x1, z1], [x1, z0]]; - CurveRegion2 { - outer: (0..4) - .map(|index| CurveEdge2::Line { - from: points[index], - to: points[(index + 1) % 4], - }) - .collect(), - holes: Vec::new(), - } - } - - #[test] - fn cap_overlap_requires_positive_shared_area() { - let host = rectangle(0.0, 0.0, 2.0, 2.0); - assert!(cap_regions_overlap( - &host, - &rectangle(1.0, 1.0, 3.0, 3.0), - 1e-7 - )); - assert!(cap_regions_overlap( - &host, - &rectangle(0.5, 0.5, 1.5, 1.5), - 1e-7 - )); - assert!(!cap_regions_overlap( - &host, - &rectangle(2.0, 0.0, 3.0, 1.0), - 1e-7 - )); - assert!(!cap_regions_overlap( - &host, - &rectangle(3.0, 0.0, 4.0, 1.0), - 1e-7 - )); - } - - #[test] - fn cap_overlap_excludes_a_cutter_inside_a_hole() { - let mut host = rectangle(0.0, 0.0, 3.0, 3.0); - host.holes - .push(reverse(&rectangle(1.0, 1.0, 2.0, 2.0).outer)); - assert!(!cap_regions_overlap( - &host, - &rectangle(1.2, 1.2, 1.8, 1.8), - 1e-7 - )); - assert!(cap_regions_overlap( - &host, - &rectangle(0.8, 1.2, 1.2, 1.8), - 1e-7 - )); - } -} - -fn source_for_cap_region( - brep: &BrepEnvelope, - base: Frame3, - level: f64, - region: &CurveRegion2, - tolerance: f64, -) -> Result, GeometryError> { - let mut candidates = Vec::new(); - for face in &brep.topology.faces { - let SurfaceGeometry::Plane { frame } = brep.geometry.surface(face.surface)? else { - continue; - }; - if dot(frame.z, base.z).abs() > 1.0 - 1.0e-10 - && (base.local(frame.origin)[2] - level).abs() <= tolerance - { - candidates.push(face); - } - } - let mut sources = Vec::new(); - for face in candidates { - let source_region = face_region(brep, face, base)?; - if cap_regions_overlap(region, &source_region, tolerance) { - sources.push(brep_face_source(brep, face.id)); - } - } - Ok(sources) -} - -fn cap_provenance( - host: &BrepEnvelope, - cutters: &[&BrepEnvelope], - base: Frame3, - level: f64, - region: &CurveRegion2, - tolerance: f64, -) -> Result { - let mut sources = source_for_cap_region(host, base, level, region, tolerance)?; - let mut cut = false; - for cutter in cutters { - let cutter_sources = source_for_cap_region(cutter, base, level, region, tolerance)?; - cut |= !cutter_sources.is_empty(); - sources.extend(cutter_sources); - } - if sources.is_empty() { - return Err(coverage()); - } - Ok(FaceProvenance { - sources, - role: if cut { FaceRole::Cut } else { FaceRole::Split }, - reversed: cut, - }) -} - -fn finish( - mut facets: Facets, - a: &BrepEnvelope, - cutters: &[&BrepEnvelope], -) -> Result { - let mut assigned = vec![false; facets.builder.brep.topology.faces.len()]; - let mut outer_shells = Vec::new(); - let mut cavity_shells = Vec::new(); - for start in 0..assigned.len() { - if assigned[start] { - continue; - } - let shell = facets.builder.brep.topology.shells.len() as u32; - let mut faces = Vec::new(); - let mut queue = VecDeque::from([start]); - assigned[start] = true; - while let Some(face) = queue.pop_front() { - facets.builder.brep.topology.faces[face].shell_ref = Some(shell); - faces.push(face as u32); - for halfedge in facets - .builder - .brep - .topology - .halfedges - .iter() - .filter(|edge| edge.face == Some(face as u32)) - { - let twin = halfedge.twin.ok_or_else(coverage)?; - let adjacent = facets.builder.brep.topology.halfedges[twin as usize] - .face - .ok_or_else(coverage)? as usize; - if !assigned[adjacent] { - assigned[adjacent] = true; - queue.push_back(adjacent); - } - } - } - let mut signed_volume = 0.0; - for &face_id in &faces { - let face = &facets.builder.brep.topology.faces[face_id as usize]; - let SurfaceGeometry::Plane { frame } = facets - .builder - .brep - .geometry - .surfaces - .get(face.surface as usize) - .ok_or_else(coverage)? - else { - continue; - }; - let outward = dot(frame.z, facets.base.z) * face.sense.multiplier(); - if outward.abs() <= 1.0 - 1e-10 { - continue; - } - let region = face_region(&facets.builder.brep, face, facets.base)?; - let cap_area = area(®ion.outer).abs() - - region - .holes - .iter() - .map(|hole| area(hole).abs()) - .sum::(); - if !cap_area.is_finite() || cap_area <= facets.accuracy.geometric.powi(2) { - return Err(coverage()); - } - signed_volume += facets.base.local(frame.origin)[2] * outward * cap_area; - } - if !signed_volume.is_finite() || signed_volume.abs() <= facets.accuracy.geometric.powi(3) { - return Err(coverage()); - } - facets.builder.brep.topology.shells.push(Shell { - id: shell, - faces, - is_closed: true, - }); - if signed_volume > 0.0 { - outer_shells.push((shell, signed_volume)); - } else { - cavity_shells.push(shell); - } - } - for &(outer_shell, _) in &outer_shells { - facets.builder.brep.solids.push(SolidRegion { - outer_shell, - cavity_shells: Vec::new(), - }); - } - for cavity_shell in cavity_shells { - let shell = &facets.builder.brep.topology.shells[cavity_shell as usize]; - let face = &facets.builder.brep.topology.faces[shell.faces[0] as usize]; - let start = facets.builder.brep.topology.loops[face.trim.outer as usize].start_halfedge; - let vertex = facets.builder.brep.topology.halfedges[start as usize].from; - let point = facets.builder.brep.topology.vertices[vertex as usize].position; - let mut enclosing = Vec::new(); - for (index, &(outer_shell, volume)) in outer_shells.iter().enumerate() { - let outer = &facets.builder.brep.topology.shells[outer_shell as usize]; - match classify_point_in_shell(&facets.builder.brep, &outer.faces, point)? { - PointClassification::Inside => enclosing.push((index, volume)), - PointClassification::Outside => {} - PointClassification::Boundary | PointClassification::Unknown => { - return Err(coverage()) - } - } - } - let Some(&(index, _)) = enclosing - .iter() - .min_by(|left, right| left.1.total_cmp(&right.1)) - else { - return Err(coverage()); - }; - facets.builder.brep.solids[index] - .cavity_shells - .push(cavity_shell); - } - let mut out = facets.builder.brep; - out.revision = cutters - .iter() - .fold(a.revision, |revision, cutter| revision.max(cutter.revision)) - .checked_add(1) - .ok_or_else(coverage)?; - out.validate()?; - let face_mappings = - analytic_face_mappings(&out, std::iter::once(a).chain(cutters.iter().copied())); - Ok(BooleanResult { - report: BooleanReport { - operation: BooleanOp::Subtraction, - quality: GeometryQuality::Analytic, - contacts: Vec::new(), - coincident: true, - face_mappings, - }, - brep: out, - }) -} - -pub(super) fn subtract_vertical_arc_extrusion( - a: &BrepEnvelope, - b: &BrepEnvelope, - id: String, -) -> Result { - subtract_vertical_arc_extrusion_batch(a, &[b], id) -} - -pub(super) fn subtract_vertical_arc_extrusion_batch( - a: &BrepEnvelope, - cutters: &[&BrepEnvelope], - id: String, -) -> Result { - if cutters.is_empty() { - return Err(coverage()); - } - let original_host = a - .topology - .faces - .iter() - .any(|face| face.key == "top" || face.key == "upper_cap"); - if !a - .geometry - .surfaces - .iter() - .any(|surface| matches!(surface, SurfaceGeometry::Cylinder { .. })) - { - return Err(coverage()); - } - if cutters.iter().any(|cutter| { - cutter.solids.len() != 1 - || cutter - .geometry - .surfaces - .iter() - .any(|surface| !matches!(surface, SurfaceGeometry::Plane { .. })) - }) { - return Err(coverage()); - } - let accuracy = cutters - .iter() - .fold(a.accuracy, |accuracy, cutter| Accuracy { - geometric: accuracy.geometric.max(cutter.accuracy.geometric), - intersection: accuracy.intersection.max(cutter.accuracy.intersection), - tessellation: accuracy.tessellation.max(cutter.accuracy.tessellation), - exchange: accuracy.exchange.max(cutter.accuracy.exchange), - }); - // A transverse round cut also adds a cylinder to an extruded profile. - // Its axis differs from the host extrusion axis, so it cannot be treated as - // the vertical arc support used by this layered reconstruction. - let cap_axes = a - .topology - .faces - .iter() - .filter(|face| { - face.key == "top" - || face.key == "upper_cap" - || face.key.ends_with(":top") - || face.key.starts_with("upper-cap-") - }) - .filter_map( - |face| match a.geometry.surfaces.get(face.surface as usize) { - Some(SurfaceGeometry::Plane { frame }) => Some(frame.z), - _ => None, - }, - ) - .collect::>(); - let axis = a - .topology - .faces - .iter() - .find_map( - |face| match a.geometry.surfaces.get(face.surface as usize) { - Some(SurfaceGeometry::Cylinder { frame, .. }) - if cap_axes - .iter() - .any(|cap| dot(*cap, frame.z).abs() > 1.0 - 1e-10) => - { - Some(frame.z) - } - _ => None, - }, - ) - .ok_or_else(coverage)?; - let host_top = a - .topology - .faces - .iter() - .filter_map( - |face| match a.geometry.surfaces.get(face.surface as usize) { - Some(SurfaceGeometry::Plane { frame }) if dot(frame.z, axis) > 1.0 - 1e-10 => { - Some(*frame) - } - _ => None, - }, - ) - .max_by(|left, right| dot(left.origin, axis).total_cmp(&dot(right.origin, axis))) - .ok_or_else(coverage)?; - let positions = a - .topology - .vertices - .iter() - .map(|vertex| host_top.local(vertex.position)[2]) - .collect::>(); - let low = positions.iter().copied().fold(f64::INFINITY, f64::min); - let high = positions.iter().copied().fold(f64::NEG_INFINITY, f64::max); - if !low.is_finite() || high - low <= accuracy.geometric * 4.0 { - return Err(coverage()); - } - let base = Frame3 { - origin: host_top.point([0.0, 0.0, low]), - ..host_top - }; - let original_profile = if original_host { - Some(face_profile(a, base, high - low, accuracy.intersection)?) - } else { - None - }; - let cutter_profiles = cutters - .iter() - .map(|cutter| { - let cutter_levels = cutter - .topology - .vertices - .iter() - .map(|vertex| base.local(vertex.position)[2]) - .collect::>(); - let lo = cutter_levels.iter().copied().fold(f64::INFINITY, f64::min); - let hi = cutter_levels - .iter() - .copied() - .fold(f64::NEG_INFINITY, f64::max); - if !lo.is_finite() - || hi - lo <= accuracy.geometric * 4.0 - || cutter_levels.iter().any(|level| { - (level - lo).abs() > accuracy.geometric - && (level - hi).abs() > accuracy.geometric - }) - { - return Err(coverage()); - } - Ok(( - *cutter, - lo, - hi, - face_profile(cutter, base, hi, accuracy.intersection)?, - )) - }) - .collect::, GeometryError>>()?; - let mut levels = vec![0.0, high - low]; - if !original_host { - for vertex in &a.topology.vertices { - let level = base.local(vertex.position)[2]; - if level > accuracy.geometric && level < high - low - accuracy.geometric { - levels.push(level); - } - } - } - for (_, lo, hi, _) in &cutter_profiles { - for level in [*lo, *hi] { - if level > accuracy.geometric && level < high - low - accuracy.geometric { - levels.push(level); - } - } - } - levels.sort_by(f64::total_cmp); - levels.dedup_by(|left, right| (*left - *right).abs() <= accuracy.geometric); - let mut layers = levels - .windows(2) - .map(|span| { - let middle = (span[0] + span[1]) / 2.0; - let host_regions = if let Some(profile) = &original_profile { - vec![profile.clone()] - } else { - sectional_regions(a, base, middle, accuracy.intersection)? - }; - let active = cutter_profiles - .iter() - .filter(|(_, lo, hi, _)| middle > *lo && middle < *hi) - .map(|(_, _, _, profile)| profile.clone()) - .collect::>(); - boolean_curved_regions( - &host_regions, - &active, - PlanarBooleanOp::Subtraction, - accuracy.intersection, - ) - .map_err(GeometryError::UnresolvedIntersection) - }) - .collect::, _>>()?; - let mut exposed_up = (1..layers.len()) - .map(|index| { - boolean_curved_regions( - &layers[index - 1], - &layers[index], - PlanarBooleanOp::Subtraction, - accuracy.intersection, - ) - .map_err(GeometryError::UnresolvedIntersection) - }) - .collect::, _>>()?; - let mut exposed_down = (1..layers.len()) - .map(|index| { - boolean_curved_regions( - &layers[index], - &layers[index - 1], - PlanarBooleanOp::Subtraction, - accuracy.intersection, - ) - .map_err(GeometryError::UnresolvedIntersection) - }) - .collect::, _>>()?; - let mut all_points = Vec::new(); - for region in &layers { - append_points(region, &mut all_points); - } - for region in exposed_up.iter().chain(&exposed_down) { - append_points(region, &mut all_points); - } - for region in &mut layers { - split_regions(region, &all_points, accuracy.intersection); - } - for region in exposed_up.iter_mut().chain(exposed_down.iter_mut()) { - split_regions(region, &all_points, accuracy.intersection); - } - let mut facets = Facets::new(id, accuracy, base)?; - let mut side_patches = Vec::::new(); - for region in &layers[0] { - let provenance = cap_provenance(a, &[], base, 0.0, region, accuracy.intersection)?; - facets.cap(std::slice::from_ref(region), 0.0, false, provenance)?; - } - for (index, regions) in layers.iter().enumerate() { - let lo = levels[index]; - let hi = levels[index + 1]; - for region in regions { - for ring in std::iter::once(®ion.outer).chain(®ion.holes) { - for edge in ring { - let p = edge.point(0.5); - let middle = base.point([p.x, p.z, (lo + hi) / 2.0]); - let from_a = source_for_side(a, middle, base.z, accuracy.intersection)?; - let mut from_b = Vec::new(); - for cutter in cutters { - from_b.extend(source_for_side( - cutter, - middle, - base.z, - accuracy.intersection, - )?); - } - let cut = from_a.is_empty() && !from_b.is_empty(); - let sources = if cut { from_b } else { from_a }; - if sources.is_empty() { - return Err(coverage()); - } - side_patches.push(SidePatch { - edge: edge.clone(), - levels: vec![lo, hi], - provenance: FaceProvenance { - sources, - role: if cut { FaceRole::Cut } else { FaceRole::Split }, - reversed: cut, - }, - }); - } - } - } - if index + 1 < layers.len() { - let cap_level = levels[index + 1]; - for region in &exposed_up[index] { - let provenance = - cap_provenance(a, cutters, base, cap_level, region, accuracy.intersection)?; - facets.cap(std::slice::from_ref(region), cap_level, true, provenance)?; - } - for region in &exposed_down[index] { - let provenance = - cap_provenance(a, cutters, base, cap_level, region, accuracy.intersection)?; - facets.cap(std::slice::from_ref(region), cap_level, false, provenance)?; - } - } - } - for region in layers.last().ok_or_else(coverage)? { - let provenance = cap_provenance(a, &[], base, high - low, region, accuracy.intersection)?; - facets.cap(std::slice::from_ref(region), high - low, true, provenance)?; - } - let mut consumed = vec![false; side_patches.len()]; - for first in 0..side_patches.len() { - if consumed[first] { - continue; - } - consumed[first] = true; - let mut levels = side_patches[first].levels.clone(); - loop { - let candidates = (first + 1..side_patches.len()) - .filter(|&index| { - !consumed[index] - && (side_patches[index].levels[0] - levels[levels.len() - 1]).abs() - <= accuracy.geometric - && same_side_patch( - &side_patches[first], - &side_patches[index], - accuracy.geometric / 4.0, - ) - }) - .collect::>(); - if candidates.len() != 1 { - break; - } - let next = candidates[0]; - consumed[next] = true; - levels.push(side_patches[next].levels[1]); - } - facets.side( - &side_patches[first].edge, - &levels, - side_patches[first].provenance.clone(), - )?; - } - finish(facets, a, cutters) -} diff --git a/main/opengeometry/src/analytic/diagnostics.rs b/main/opengeometry/src/analytic/diagnostics.rs deleted file mode 100644 index 153df67..0000000 --- a/main/opengeometry/src/analytic/diagnostics.rs +++ /dev/null @@ -1,43 +0,0 @@ -use super::{BrepEnvelope, GeometryError}; -use serde::Serialize; - -#[derive(Debug, Serialize)] -pub struct ValidationReport { - pub valid: bool, - pub validation_level: &'static str, - pub error: Option, -} - -pub fn validate_json(serialized: &str) -> ValidationReport { - let result = BrepEnvelope::from_json(serialized); - ValidationReport { - valid: result.is_ok(), - validation_level: "v2 structure and sampled geometry residuals", - error: result.err(), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::topology::tests::circular_face; - - #[test] - fn diagnostics_reject_missing_links_and_detached_curves() { - let brep = circular_face(); - assert!(validate_json(&brep.to_json().unwrap()).valid); - let mut broken = brep.clone(); - broken.topology.halfedges[0].next = Some(u32::MAX); - let report = validate_json(&serde_json::to_string(&broken).unwrap()); - assert!(!report.valid); - assert!(report.error.is_some()); - let mut broken = brep; - if let crate::analytic::CurveGeometry::Circle { radius, .. } = - &mut broken.geometry.curves[0] - { - *radius *= 1.1; - } - assert!(!validate_json(&serde_json::to_string(&broken).unwrap()).valid); - assert!(!validate_json("{\"schema_version\":1}").valid); - } -} diff --git a/main/opengeometry/src/analytic/exchange.rs b/main/opengeometry/src/analytic/exchange.rs deleted file mode 100644 index 88a43e8..0000000 --- a/main/opengeometry/src/analytic/exchange.rs +++ /dev/null @@ -1,869 +0,0 @@ -use super::{ - export_curve::{fit_intersection_curve, FittedIntersectionCurve}, - geometry::{cross, dot, norm, sub, unit}, - topology::{EdgeGeometry, GeometryQuality, Orientation, PcurveGeometry}, - BrepEnvelope, CurveGeometry, Frame3, GeometryError, SurfaceGeometry, -}; -use crate::export::part21::{sanitize_string_literal, Part21Writer}; -use serde::Serialize; - -#[derive(Debug, Serialize)] -pub struct StepReport { - pub schema_version: u32, - pub revision: String, - pub length_unit: String, - pub quality: GeometryQuality, - pub faces: usize, - pub edges: usize, - pub solids: usize, - pub cavity_shells: usize, - pub collapsed_chart_uses: usize, - pub geometric_tolerance: f64, - pub exchange_error_bound: f64, - pub validation_level: &'static str, -} - -fn real(value: f64) -> String { - format!("{value:.17E}") -} -fn refs(ids: &[usize]) -> String { - ids.iter() - .map(|id| format!("#{id}")) - .collect::>() - .join(",") -} -fn sense(value: Orientation) -> &'static str { - if value == Orientation::Forward { - ".T." - } else { - ".F." - } -} -fn frame_error(frame: &Frame3, bounds: super::geometry::PatchBounds) -> Result { - let z = unit(frame.z)?; - let x = unit(sub(frame.x, z.map(|v| v * dot(frame.x, z))))?; - let y = cross(z, x); - let deviation = norm(sub(x, frame.x)) - .max(norm(sub(y, frame.y))) - .max(norm(sub(z, frame.z))); - if deviation == 0.0 { - return Ok(0.0); - } - let distance = norm(std::array::from_fn(|i| { - (bounds.axes[i].lo - frame.origin[i]) - .abs() - .max((bounds.axes[i].hi - frame.origin[i]).abs()) - })); - Ok(4.0 * distance * deviation) -} -fn point(writer: &mut Part21Writer, value: &[f64]) -> usize { - writer.add_entity(format!( - "CARTESIAN_POINT('',({}))", - value.iter().map(|v| real(*v)).collect::>().join(",") - )) -} -fn direction(writer: &mut Part21Writer, value: &[f64]) -> usize { - writer.add_entity(format!( - "DIRECTION('',({}))", - value.iter().map(|v| real(*v)).collect::>().join(",") - )) -} -fn placement(writer: &mut Part21Writer, frame: &Frame3, scale: f64) -> usize { - let p = point(writer, &frame.origin.map(|v| v * scale)); - let z = direction(writer, &frame.z); - let x = direction(writer, &frame.x); - writer.add_entity(format!("AXIS2_PLACEMENT_3D('',#{p},#{z},#{x})")) -} -fn line( - writer: &mut Part21Writer, - origin: &[f64], - velocity: &[f64], -) -> Result { - let length = velocity.iter().fold(0.0_f64, |n, v| n.hypot(*v)); - if !length.is_finite() || length == 0.0 { - return Err(GeometryError::UnsupportedGeometry( - "STEP pcurve has a singular line direction".into(), - )); - } - let p = point(writer, origin); - let d = direction( - writer, - &velocity.iter().map(|v| v / length).collect::>(), - ); - let v = writer.add_entity(format!("VECTOR('',#{d},{})", real(length))); - Ok(writer.add_entity(format!("LINE('',#{p},#{v})"))) -} - -fn bspline_curve( - writer: &mut Part21Writer, - controls: &[Vec], - knots: &[f64], - multiplicities: &[usize], -) -> Result { - if controls.len() < 4 || knots.len() != multiplicities.len() { - return Err(GeometryError::InvalidGeometry( - "invalid fitted cubic curve shape".into(), - )); - } - let points = controls - .iter() - .map(|control| point(writer, control)) - .collect::>(); - Ok(writer.add_entity(format!( - "B_SPLINE_CURVE_WITH_KNOTS('',3,({}),.UNSPECIFIED.,.F.,.F.,({}),({}),.UNSPECIFIED.)", - refs(&points), - multiplicities - .iter() - .map(usize::to_string) - .collect::>() - .join(","), - knots - .iter() - .map(|value| real(*value)) - .collect::>() - .join(",") - ))) -} - -fn curve( - writer: &mut Part21Writer, - geometry: &CurveGeometry, - scale: f64, -) -> Result { - Ok(match geometry { - CurveGeometry::Line { origin, direction } => line( - writer, - &origin.map(|v| v * scale), - &direction.map(|v| v * scale), - )?, - CurveGeometry::Circle { frame, radius } => { - let p = placement(writer, frame, scale); - writer.add_entity(format!("CIRCLE('',#{p},{})", real(radius * scale))) - } - CurveGeometry::Ellipse { - frame, - major_radius, - minor_radius, - } => { - let p = placement(writer, frame, scale); - writer.add_entity(format!( - "ELLIPSE('',#{p},{},{})", - real(major_radius * scale), - real(minor_radius * scale) - )) - } - CurveGeometry::Intersection { .. } => { - return Err(GeometryError::UnsupportedGeometry( - "STEP numerical branches require bounded export-curve fitting".into(), - )) - } - }) -} - -fn pcurve( - writer: &mut Part21Writer, - geometry: &PcurveGeometry, - surface: &SurfaceGeometry, - surface_id: usize, - context: usize, - scale: f64, - lift: [i32; 2], - fitted: Option<&FittedIntersectionCurve>, -) -> Result { - // STEP cone v measures distance along a generator; the kernel uses axial height. - let metric = match surface { - SurfaceGeometry::Plane { .. } => [scale, scale], - SurfaceGeometry::Cylinder { .. } => [1.0, scale], - SurfaceGeometry::Cone { semi_angle, .. } => [1.0, scale / semi_angle.cos()], - _ => [1.0, 1.0], - }; - let period = match surface { - SurfaceGeometry::Plane { .. } => [0.0, 0.0], - SurfaceGeometry::Torus { .. } => [std::f64::consts::TAU; 2], - _ => [std::f64::consts::TAU, 0.0], - }; - let map_origin = |origin: [f64; 2]| { - std::array::from_fn::<_, 2, _>(|i| (origin[i] + f64::from(lift[i]) * period[i]) * metric[i]) - }; - let geometry_id = match geometry { - PcurveGeometry::Line2 { origin, direction } => line( - writer, - &map_origin(*origin), - &std::array::from_fn::<_, 2, _>(|i| direction[i] * metric[i]), - )?, - PcurveGeometry::Conic2 { - origin, - axis_a, - axis_b, - } => { - let a = [axis_a[0] * metric[0], axis_a[1] * metric[1], 0.0]; - let b = [axis_b[0] * metric[0], axis_b[1] * metric[1], 0.0]; - let ra = norm(a); - let rb = norm(b); - if !ra.is_finite() - || !rb.is_finite() - || ra == 0.0 - || rb == 0.0 - || dot(a.map(|v| v / ra), b.map(|v| v / rb)).abs() > 64.0 * f64::EPSILON - { - return Err(GeometryError::UnsupportedGeometry( - "STEP requires an orthogonal conic pcurve parameterization".into(), - )); - } - let axis = if ra >= rb { a } else { b }; - let p = point(writer, &map_origin(*origin)); - let d = direction(writer, &axis[..2]); - let frame = writer.add_entity(format!("AXIS2_PLACEMENT_2D('',#{p},#{d})")); - if ra == rb { - writer.add_entity(format!("CIRCLE('',#{frame},{})", real(ra))) - } else { - writer.add_entity(format!( - "ELLIPSE('',#{frame},{},{})", - real(ra.max(rb)), - real(ra.min(rb)) - )) - } - } - PcurveGeometry::IntersectionSide { side, .. } => { - let fitted = fitted.ok_or_else(|| { - GeometryError::InvalidGeometry( - "STEP intersection pcurve is missing its certified fit".into(), - ) - })?; - let controls = fitted - .pcurve(*side) - .iter() - .map(|control| map_origin(*control).to_vec()) - .collect::>(); - bspline_curve(writer, &controls, &fitted.knots, &fitted.multiplicities)? - } - PcurveGeometry::ProjectedCurve { .. } => { - return Err(GeometryError::UnsupportedGeometry( - "STEP projected pcurves require a certified exchange representation".into(), - )) - } - }; - let definition = writer.add_entity(format!( - "DEFINITIONAL_REPRESENTATION('',(#{geometry_id}),#{context})" - )); - Ok(writer.add_entity(format!("PCURVE('',#{surface_id},#{definition})"))) -} - -pub(super) fn preflight(brep: &BrepEnvelope, scale: f64) -> Result { - brep.validate()?; - if brep.solids.is_empty() - || !brep.topology.wires.is_empty() - || brep.topology.shells.iter().any(|shell| !shell.is_closed) - { - return Err(GeometryError::UnsupportedGeometry( - "analytic analytic exchange export requires nonempty closed solid regions without wires".into(), - )); - } - if brep.topology.vertices.len() - + brep.topology.halfedges.len() - + brep.topology.faces.len() - + brep.geometry.curves.len() - + brep.geometry.surfaces.len() - + brep.geometry.pcurves.len() - + brep.geometry.intersections.len() - + brep.topology.edges.len() - + brep.topology.loops.len() - + brep.topology.shells.len() - + brep.solids.len() - > 100_000 - { - return Err(GeometryError::LimitExceeded( - "analytic exchange body exceeds 100,000 geometry/topology items".into(), - )); - } - if brep.id.len() > 4096 || brep.topology.faces.iter().any(|face| face.key.len() > 4096) { - return Err(GeometryError::LimitExceeded( - "analytic exchange body and face labels must be at most 4 KiB".into(), - )); - } - let bounds = brep.bounds()?.ok_or_else(|| { - GeometryError::InvalidTopology("analytic exchange solid has no bounds".into()) - })?; - let mut magnitude = bounds - .axes - .iter() - .flat_map(|axis| [axis.lo.abs(), axis.hi.abs()]) - .fold(0.0_f64, f64::max); - let mut frame_bound = 0.0_f64; - for face in &brep.topology.faces { - frame_bound = frame_bound.max(frame_error( - brep.geometry.surface(face.surface)?.frame(), - bounds, - )?); - magnitude = brep - .geometry - .surface(face.surface)? - .frame() - .origin - .iter() - .fold(magnitude, |m, v| m.max(v.abs())); - } - for edge in &brep.topology.edges { - if let EdgeGeometry::Curve { curve, .. } = edge.geometry { - let origin = match &brep.geometry.curves[curve as usize] { - CurveGeometry::Line { origin, .. } => origin, - CurveGeometry::Circle { frame, .. } | CurveGeometry::Ellipse { frame, .. } => { - &frame.origin - } - CurveGeometry::Intersection { .. } => continue, - }; - magnitude = origin.iter().fold(magnitude, |m, v| m.max(v.abs())); - if let CurveGeometry::Circle { frame, .. } | CurveGeometry::Ellipse { frame, .. } = - &brep.geometry.curves[curve as usize] - { - frame_bound = frame_bound.max(frame_error(frame, bounds)?); - } - } - } - let exchange_bound = - brep.accuracy.geometric + 2.0 * frame_bound + 64.0 * f64::EPSILON * magnitude; - if !exchange_bound.is_finite() - || exchange_bound > brep.accuracy.exchange - || !(exchange_bound * scale).is_finite() - { - return Err(GeometryError::LimitExceeded( - "analytic exchange exchange budget is below geometric tolerance or coordinate precision".into(), - )); - } - let encoded = - serde_json::to_value(brep).map_err(|e| GeometryError::InvalidGeometry(e.to_string()))?; - fn scalable(value: &serde_json::Value, scale: f64) -> bool { - match value { - serde_json::Value::Number(v) => v.as_f64().is_some_and(|v| (v * scale).is_finite()), - serde_json::Value::Array(v) => v.iter().all(|v| scalable(v, scale)), - serde_json::Value::Object(v) => v.values().all(|v| scalable(v, scale)), - _ => true, - } - } - if !scalable(&encoded, scale) { - return Err(GeometryError::LimitExceeded( - "analytic exchange unit conversion exceeds floating-point range".into(), - )); - } - Ok(exchange_bound) -} - -/// Coordinates in the v2 body are metres. Conversion to the selected output unit occurs here once. -pub fn export_step(brep: &BrepEnvelope, unit: &str) -> Result<(String, StepReport), GeometryError> { - brep.validate()?; - let scale = match unit { - "metre" => 1.0, - "millimetre" => 1000.0, - _ => { - return Err(GeometryError::InvalidGeometry( - "STEP length unit must be metre or millimetre".into(), - )) - } - }; - let mut exchange_bound = preflight(brep, scale)?; - let fitting_budget = brep.accuracy.exchange - exchange_bound; - let mut writer = Part21Writer::new("AUTOMOTIVE_DESIGN"); - writer.set_file_name(&brep.id); - writer.set_description("OpenGeometry authoritative analytic BRep v2"); - let context2 = - writer.add_entity("(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('',''))"); - let mut surfaces = vec![None; brep.geometry.surfaces.len()]; - for face in &brep.topology.faces { - if surfaces[face.surface as usize].is_some() { - continue; - } - let surface = brep.geometry.surface(face.surface)?; - let frame = placement(&mut writer, surface.frame(), scale); - let expression = match surface { - SurfaceGeometry::Plane { .. } => format!("PLANE('',#{frame})"), - SurfaceGeometry::Sphere { radius, .. } => { - format!("SPHERICAL_SURFACE('',#{frame},{})", real(radius * scale)) - } - SurfaceGeometry::Cylinder { radius, .. } => { - format!("CYLINDRICAL_SURFACE('',#{frame},{})", real(radius * scale)) - } - SurfaceGeometry::Cone { semi_angle, .. } => { - format!("CONICAL_SURFACE('',#{frame},0.,{})", real(*semi_angle)) - } - SurfaceGeometry::Torus { - major_radius, - minor_radius, - .. - } => format!( - "TOROIDAL_SURFACE('',#{frame},{},{})", - real(major_radius * scale), - real(minor_radius * scale) - ), - }; - surfaces[face.surface as usize] = Some(writer.add_entity(expression)); - } - let vertices: Vec<_> = brep - .topology - .vertices - .iter() - .map(|v| { - let p = point(&mut writer, &v.position.map(|v| v * scale)); - writer.add_entity(format!("VERTEX_POINT('',#{p})")) - }) - .collect(); - let mut edges = vec![None; brep.topology.edges.len()]; - let mut curves = vec![None; brep.geometry.curves.len()]; - let mut fitted_curves = vec![None; brep.geometry.curves.len()]; - let mut fitting_error: f64 = 0.0; - for edge in &brep.topology.edges { - let EdgeGeometry::Curve { - curve: curve_id, - range, - } = edge.geometry - else { - continue; - }; - let geometry = &brep.geometry.curves[curve_id as usize]; - if matches!( - geometry, - CurveGeometry::Circle { .. } | CurveGeometry::Ellipse { .. } - ) && range.hi - range.lo > std::f64::consts::TAU - { - return Err(GeometryError::UnsupportedGeometry( - "STEP edge spans more than one conic period".into(), - )); - } - let geometry_id = match curves[curve_id as usize] { - Some(id) => id, - None => { - let id = match geometry { - CurveGeometry::Intersection { definition } => { - if fitting_budget <= 0.0 { - return Err(GeometryError::LimitExceeded( - "STEP exchange budget leaves no room for numerical curve fitting" - .into(), - )); - } - let fitted = fit_intersection_curve( - &brep.geometry, - *definition, - fitting_budget, - 20_000, - )?; - fitting_error = fitting_error.max(fitted.error_bound); - let controls = fitted - .controls - .iter() - .map(|control| control.map(|value| value * scale).to_vec()) - .collect::>(); - let id = bspline_curve( - &mut writer, - &controls, - &fitted.knots, - &fitted.multiplicities, - )?; - fitted_curves[curve_id as usize] = Some(fitted); - id - } - _ => curve(&mut writer, geometry, scale)?, - }; - curves[curve_id as usize] = Some(id); - id - } - }; - let mut pcurves = Vec::new(); - for use_id in [Some(edge.halfedge), edge.twin_halfedge] - .into_iter() - .flatten() - { - let use_ = &brep.topology.halfedges[use_id as usize]; - let face = &brep.topology.faces[use_.face.ok_or_else(|| { - GeometryError::InvalidTopology("STEP solid edge lacks a face use".into()) - })? as usize]; - let pc = use_.geometry_use.pcurve.ok_or_else(|| { - GeometryError::InvalidTopology("STEP face use lacks a pcurve".into()) - })?; - let surface_id = surfaces[face.surface as usize].ok_or_else(|| { - GeometryError::InvalidTopology("STEP surface was not emitted".into()) - })?; - pcurves.push(pcurve( - &mut writer, - &brep.geometry.pcurves[pc as usize], - brep.geometry.surface(face.surface)?, - surface_id, - context2, - scale, - use_.geometry_use.periodic_lift, - fitted_curves[curve_id as usize].as_ref(), - )?); - } - let kind = if edge.chart_seam { - "SEAM_CURVE" - } else { - "SURFACE_CURVE" - }; - let geometry_id = writer.add_entity(format!( - "{kind}('',#{geometry_id},({}),.CURVE_3D.)", - refs(&pcurves) - )); - let use_ = &brep.topology.halfedges[edge.halfedge as usize]; - let (from, to) = if use_.geometry_use.sense == Orientation::Forward { - (use_.from, use_.to) - } else { - (use_.to, use_.from) - }; - edges[edge.id as usize] = Some(writer.add_entity(format!( - "EDGE_CURVE('',#{},#{},#{geometry_id},.T.)", - vertices[from as usize], vertices[to as usize] - ))); - } - let mut faces = Vec::new(); - let mut collapsed_chart_uses = 0; - for face in &brep.topology.faces { - let mut bounds = Vec::new(); - for loop_id in std::iter::once(&face.trim.outer).chain(&face.trim.holes) { - let loop_ = &brep.topology.loops[*loop_id as usize]; - let start = loop_.start_halfedge; - let mut current = start; - let mut oriented = Vec::new(); - for _ in 0..=brep.topology.halfedges.len() { - let use_ = &brep.topology.halfedges[current as usize]; - if let Some(edge) = edges[use_.edge as usize] { - oriented.push(writer.add_entity(format!( - "ORIENTED_EDGE('',*,*,#{edge},{})", - sense(use_.geometry_use.sense) - ))); - } else { - // A collapsed chart interval contributes only its vertex to the 3D boundary. - collapsed_chart_uses += 1; - } - current = use_.next.ok_or_else(|| { - GeometryError::InvalidTopology("STEP loop has no next use".into()) - })?; - if current == start { - break; - } - } - let loop_geometry = if oriented.is_empty() { - writer.add_entity(format!( - "VERTEX_LOOP('',#{})", - vertices[brep.topology.halfedges[start as usize].from as usize] - )) - } else { - writer.add_entity(format!("EDGE_LOOP('',({}))", refs(&oriented))) - }; - let kind = if loop_.is_hole { - "FACE_BOUND" - } else { - "FACE_OUTER_BOUND" - }; - bounds.push(writer.add_entity(format!("{kind}('',#{loop_geometry},.T.)"))); - } - let surface = surfaces[face.surface as usize] - .ok_or_else(|| GeometryError::InvalidTopology("STEP face lacks its support".into()))?; - faces.push(writer.add_entity(format!( - "ADVANCED_FACE('{}',({}),#{surface},{})", - sanitize_string_literal(&face.key), - refs(&bounds), - sense(face.sense) - ))); - } - exchange_bound += fitting_error; - if exchange_bound > brep.accuracy.exchange { - return Err(GeometryError::LimitExceeded( - "STEP fitted curves exceed the exchange error budget".into(), - )); - } - let shells: Vec<_> = brep - .topology - .shells - .iter() - .map(|shell| { - writer.add_entity(format!( - "CLOSED_SHELL('',({}))", - refs( - &shell - .faces - .iter() - .map(|id| faces[*id as usize]) - .collect::>() - ) - )) - }) - .collect(); - let mut solids = Vec::new(); - for (i, solid) in brep.solids.iter().enumerate() { - let outer = shells[solid.outer_shell as usize]; - let name = sanitize_string_literal(&format!("{}-{i}", brep.id)); - let expression = if solid.cavity_shells.is_empty() { - format!("MANIFOLD_SOLID_BREP('{name}',#{outer})") - } else { - let voids: Vec<_> = solid - .cavity_shells - .iter() - .map(|id| { - writer.add_entity(format!( - "ORIENTED_CLOSED_SHELL('',*,#{},.T.)", - shells[*id as usize] - )) - }) - .collect(); - format!("BREP_WITH_VOIDS('{name}',#{outer},({}))", refs(&voids)) - }; - solids.push(writer.add_entity(expression)); - } - let prefix = if unit == "millimetre" { ".MILLI." } else { "$" }; - let length = writer.add_entity(format!( - "(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT({prefix},.METRE.))" - )); - let angle = writer.add_entity("(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.))"); - let solid_angle = - writer.add_entity("(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT())"); - let uncertainty = writer.add_entity(format!("UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE({}),#{length},'distance_accuracy_value','exchange error bound')", real(exchange_bound * scale))); - let context = writer.add_entity(format!("(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#{uncertainty})) GLOBAL_UNIT_ASSIGNED_CONTEXT((#{length},#{angle},#{solid_angle})) REPRESENTATION_CONTEXT('',''))")); - let representation = writer.add_entity(format!( - "ADVANCED_BREP_SHAPE_REPRESENTATION('',({}),#{context})", - refs(&solids) - )); - let app = writer.add_entity("APPLICATION_CONTEXT('automotive design')"); - writer.add_entity(format!( - "APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#{app})" - )); - let product_context = writer.add_entity(format!("PRODUCT_CONTEXT('',#{app},'mechanical')")); - let name = sanitize_string_literal(&brep.id); - let product = writer.add_entity(format!( - "PRODUCT('{name}','{name}','',(#{product_context}))" - )); - let formation = writer.add_entity(format!( - "PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#{product},.NOT_KNOWN.)" - )); - let definition_context = writer.add_entity(format!( - "PRODUCT_DEFINITION_CONTEXT('part definition',#{app},'design')" - )); - let definition = writer.add_entity(format!( - "PRODUCT_DEFINITION('','',#{formation},#{definition_context})" - )); - let shape = writer.add_entity(format!("PRODUCT_DEFINITION_SHAPE('','',#{definition})")); - writer.add_entity(format!( - "SHAPE_DEFINITION_REPRESENTATION(#{shape},#{representation})" - )); - let text = writer.build().map_err(GeometryError::InvalidGeometry)?; - let report = StepReport { - schema_version: 2, revision: brep.revision.to_string(), length_unit: unit.into(), quality: brep.quality.clone(), - faces: faces.len(), edges: edges.iter().flatten().count(), solids: solids.len(), - cavity_shells: brep.solids.iter().map(|s| s.cavity_shells.len()).sum(), collapsed_chart_uses, - geometric_tolerance: brep.accuracy.geometric * scale, - exchange_error_bound: exchange_bound * scale, - validation_level: "v2 structure, sampled residuals and Part-21 references; external schema/import validation pending", - }; - Ok((text, report)) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::{ - booleans::{boolean_spheres, BooleanOp}, - primitives, - topology::Accuracy, - Curve, Surface, - }; - use crate::math::interval::Interval; - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-6, - } - } - fn frame() -> Frame3 { - Frame3::from_axis([1.0, 2.0, 3.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]).unwrap() - } - - fn numerical_edge_box() -> BrepEnvelope { - let mut brep = primitives::cuboid( - "numerical-edge".into(), - Frame3::IDENTITY, - [1.0, 2.0, 3.0], - accuracy(), - ) - .unwrap(); - let edge = brep.topology.edges[0].clone(); - let super::EdgeGeometry::Curve { curve, range } = edge.geometry else { - panic!("cuboid edge must be regular") - }; - let use_a = edge.halfedge; - let use_b = edge.twin_halfedge.unwrap(); - let face_a = brep.topology.halfedges[use_a as usize].face.unwrap(); - let face_b = brep.topology.halfedges[use_b as usize].face.unwrap(); - let surface_ids = [ - brep.topology.faces[face_a as usize].surface, - brep.topology.faces[face_b as usize].surface, - ]; - let evaluator = brep.geometry.curve(curve).unwrap(); - let points = [ - evaluator.point_at(range.lo).unwrap(), - evaluator.point_at(range.hi).unwrap(), - ]; - let uv_a = points.map(|point| { - brep.geometry.surfaces[surface_ids[0] as usize] - .project(point, None) - .unwrap() - }); - let uv_b = points.map(|point| { - brep.geometry.surfaces[surface_ids[1] as usize] - .project(point, None) - .unwrap() - }); - let padding = 1e-10; - let tubes = std::array::from_fn(|axis| { - let values = if axis < 2 { - [uv_a[0][axis], uv_a[1][axis]] - } else { - [uv_b[0][axis - 2], uv_b[1][axis - 2]] - }; - Interval::new( - values[0].min(values[1]) - padding, - values[0].max(values[1]) + padding, - ) - .unwrap() - }); - let definition = brep.geometry.intersections.len() as u32; - brep.geometry - .intersections - .push(crate::analytic::intersection::IntersectionDefinition { - surfaces: surface_ids, - anchors: vec![ - crate::analytic::intersection::TraceAnchor { - parameter: range.lo, - point: points[0], - uv_a: uv_a[0], - uv_b: uv_b[0], - }, - crate::analytic::intersection::TraceAnchor { - parameter: range.hi, - point: points[1], - uv_a: uv_a[1], - uv_b: uv_b[1], - }, - ], - uv_tubes: vec![tubes], - residual_tolerance: accuracy().intersection, - }); - brep.geometry.curves[curve as usize] = CurveGeometry::Intersection { definition }; - let pcurve_a = brep.geometry.pcurves.len() as u32; - brep.geometry - .pcurves - .push(PcurveGeometry::IntersectionSide { - definition, - side: crate::analytic::topology::IntersectionSide::A, - }); - let pcurve_b = brep.geometry.pcurves.len() as u32; - brep.geometry - .pcurves - .push(PcurveGeometry::IntersectionSide { - definition, - side: crate::analytic::topology::IntersectionSide::B, - }); - brep.topology.halfedges[use_a as usize].geometry_use.pcurve = Some(pcurve_a); - brep.topology.halfedges[use_b as usize].geometry_use.pcurve = Some(pcurve_b); - brep.validate().unwrap(); - brep - } - #[test] - fn exports_all_families_shared_edges_seams_and_explicit_poles() { - let bodies = [ - primitives::cylinder("c#999's".into(), frame(), 1.0, 2.0, accuracy()).unwrap(), - primitives::sphere("s".into(), frame(), 1.0, accuracy()).unwrap(), - primitives::cone("c".into(), frame(), 1.0, 2.0, accuracy()).unwrap(), - primitives::frustum("f".into(), frame(), 1.0, 0.4, 2.0, accuracy()).unwrap(), - primitives::torus("t".into(), frame(), 2.0, 0.5, accuracy()).unwrap(), - primitives::annular_sector_extrusion( - "w".into(), - frame(), - 2.0, - 0.2, - 1.5, - 0.3, - -2.1, - accuracy(), - ) - .unwrap(), - ]; - for brep in bodies { - let original = brep.to_json().unwrap(); - let (metres, report) = export_step(&brep, "metre").unwrap(); - assert_eq!(report.faces, brep.topology.faces.len()); - assert_eq!(metres.matches("=EDGE_CURVE(").count(), report.edges); - assert!(!metres.contains("POLY_LOOP")); - assert!(metres.contains("PCURVE(")); - if brep.topology.edges.iter().any(|e| e.chart_seam) { - assert!(metres.contains("SEAM_CURVE(")); - } - let (mm, _) = export_step(&brep, "millimetre").unwrap(); - assert!(mm.contains("SI_UNIT(.MILLI.,.METRE.)")); - assert_ne!(metres, mm); - assert_eq!(original, brep.to_json().unwrap()); - } - } - - #[test] - fn numerical_intersection_edges_export_as_bounded_cubic_curves() { - let brep = numerical_edge_box(); - let (step, report) = export_step(&brep, "metre").unwrap(); - assert!(step.contains("B_SPLINE_CURVE_WITH_KNOTS")); - assert!(report.exchange_error_bound <= brep.accuracy.exchange); - let ifc = crate::analytic::ifc_exchange::prepare_ifc_body(&brep).unwrap(); - assert!(ifc - .entities - .iter() - .any(|entity| entity.entity_type == "IfcBSplineCurveWithKnots")); - assert!(ifc.exchange_error_bound <= brep.accuracy.exchange); - } - #[test] - fn preserves_reversed_cut_faces_and_cavity_orientation() { - let a = primitives::sphere("host".into(), frame(), 2.0, accuracy()).unwrap(); - let b = primitives::sphere("cut".into(), frame(), 0.5, accuracy()).unwrap(); - let cavity = boolean_spheres(&a, &b, BooleanOp::Subtraction, "cavity".into()) - .unwrap() - .brep; - let (text, report) = export_step(&cavity, "metre").unwrap(); - assert_eq!(report.cavity_shells, 1); - assert!(text.contains("BREP_WITH_VOIDS(")); - assert!(text - .lines() - .any(|line| line.contains("=ADVANCED_FACE(") && line.ends_with(",.F.);"))); - let mut shifted = frame(); - shifted.origin[0] += 1.7; - let cutter = primitives::sphere("cutter".into(), shifted, 1.0, accuracy()).unwrap(); - let cut = boolean_spheres(&a, &cutter, BooleanOp::Subtraction, "cut".into()) - .unwrap() - .brep; - assert!(export_step(&cut, "metre").is_ok()); - let mut invalid = cut; - invalid.topology.halfedges[0].next = None; - assert!(matches!( - export_step(&invalid, "metre"), - Err(GeometryError::InvalidTopology(_)) - )); - } - - #[test] - fn rejects_unrepresentable_exchange_and_preserves_large_revisions() { - let mut brep = primitives::sphere("budget".into(), frame(), 1.0, accuracy()).unwrap(); - brep.revision = u64::MAX; - let (_, report) = export_step(&brep, "metre").unwrap(); - assert_eq!(report.revision, u64::MAX.to_string()); - brep.accuracy.exchange = 1e-10; - assert!(matches!( - export_step(&brep, "metre"), - Err(GeometryError::LimitExceeded(_)) - )); - brep.accuracy.exchange = 1e-6; - brep.id = "x".repeat(4097); - assert!(matches!( - export_step(&brep, "metre"), - Err(GeometryError::LimitExceeded(_)) - )); - assert!(matches!( - export_step(&brep, "inch"), - Err(GeometryError::InvalidGeometry(_)) - )); - } -} diff --git a/main/opengeometry/src/analytic/export_curve.rs b/main/opengeometry/src/analytic/export_curve.rs deleted file mode 100644 index 3f74d65..0000000 --- a/main/opengeometry/src/analytic/export_curve.rs +++ /dev/null @@ -1,271 +0,0 @@ -use super::{ - geometry::{dot, norm, sub, PatchBounds, Surface}, - topology::{GeometryStore, IntersectionSide}, - Curve, GeometryError, Point3, UV, -}; -use crate::math::interval::Interval; - -#[derive(Clone, Debug)] -pub struct FittedIntersectionCurve { - pub controls: Vec, - pub pcurve_a: Vec, - pub pcurve_b: Vec, - pub knots: Vec, - pub multiplicities: Vec, - pub error_bound: f64, -} - -fn point_segment_distance(point: Point3, a: Point3, b: Point3) -> f64 { - let chord = sub(b, a); - let length_squared = dot(chord, chord); - if length_squared == 0.0 { - return norm(sub(point, a)); - } - let parameter = (dot(sub(point, a), chord) / length_squared).clamp(0.0, 1.0); - norm(sub( - point, - std::array::from_fn(|axis| a[axis] + parameter * chord[axis]), - )) -} - -fn bounds_chord_deviation(bounds: PatchBounds, a: Point3, b: Point3) -> f64 { - let mut maximum: f64 = 0.0; - for mask in 0..8 { - let corner = std::array::from_fn(|axis| { - if mask & (1 << axis) == 0 { - bounds.axes[axis].lo - } else { - bounds.axes[axis].hi - } - }); - maximum = maximum.max(point_segment_distance(corner, a, b)); - } - maximum -} - -fn line_controls(a: [f64; N], b: [f64; N]) -> [[f64; N]; 4] { - [ - a, - std::array::from_fn(|axis| (2.0 * a[axis] + b[axis]) / 3.0), - std::array::from_fn(|axis| (a[axis] + 2.0 * b[axis]) / 3.0), - b, - ] -} - -pub fn fit_intersection_curve( - store: &GeometryStore, - definition_id: u32, - tolerance: f64, - max_segments: usize, -) -> Result { - if !tolerance.is_finite() || tolerance <= 0.0 || max_segments == 0 { - return Err(GeometryError::InvalidGeometry( - "export curve fitting requires positive tolerance and segment budget".into(), - )); - } - let definition = store - .intersections - .get(definition_id as usize) - .ok_or_else(|| GeometryError::MissingReference { - kind: "intersection".into(), - index: definition_id, - })?; - definition.validate(store)?; - let first = definition.anchors.first().ok_or_else(|| { - GeometryError::InvalidGeometry("intersection curve has no first anchor".into()) - })?; - let last = definition.anchors.last().ok_or_else(|| { - GeometryError::InvalidGeometry("intersection curve has no last anchor".into()) - })?; - let curve_id = store - .curves - .iter() - .position(|curve| { - matches!(curve, super::CurveGeometry::Intersection { definition } if *definition == definition_id) - }) - .ok_or_else(|| GeometryError::MissingReference { - kind: "intersection curve".into(), - index: definition_id, - })?; - let curve = store - .curve(u32::try_from(curve_id).map_err(|_| { - GeometryError::LimitExceeded("intersection curve identifiers".into()) - })?)?; - let mut stack = vec![(Interval::new(first.parameter, last.parameter)?, 0usize)]; - let mut accepted = Vec::new(); - let mut visits = 0usize; - let mut achieved: f64 = 0.0; - while let Some((range, depth)) = stack.pop() { - visits = visits - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("export curve fit visits".into()))?; - if visits > max_segments.saturating_mul(2) { - return Err(GeometryError::LimitExceeded( - "export curve fit visits".into(), - )); - } - let a = curve.point_at(range.lo)?; - let b = curve.point_at(range.hi)?; - let deviation = if let Some(bound) = definition.certified_chord_deviation(range, store)? { - bound - } else { - bounds_chord_deviation(curve.enclose(range)?, a, b) - }; - if deviation <= tolerance { - achieved = achieved.max(deviation); - accepted.push(range); - continue; - } - if depth >= 48 || accepted.len() + stack.len() + 2 > max_segments { - return Err(GeometryError::LimitExceeded( - "export curve fit segments".into(), - )); - } - let midpoint = range.midpoint(); - stack.push((Interval::new(midpoint, range.hi)?, depth + 1)); - stack.push((Interval::new(range.lo, midpoint)?, depth + 1)); - } - accepted.sort_by(|a, b| a.lo.total_cmp(&b.lo)); - if accepted.is_empty() { - return Err(GeometryError::UnresolvedIntersection( - "export curve fitting produced no intervals".into(), - )); - } - let supports = [ - store.surface(definition.surfaces[0])?, - store.surface(definition.surfaces[1])?, - ]; - let mut controls = Vec::with_capacity(accepted.len() * 3 + 1); - let mut pcurve_a = Vec::with_capacity(accepted.len() * 3 + 1); - let mut pcurve_b = Vec::with_capacity(accepted.len() * 3 + 1); - for (index, range) in accepted.iter().enumerate() { - let left = definition.evaluate(range.lo, store)?; - let right = definition.evaluate(range.hi, store)?; - let xyz = line_controls(left.point, right.point); - let uv_a = line_controls(left.uv_a, right.uv_a); - let uv_b = line_controls(left.uv_b, right.uv_b); - for sample in 0..=4 { - let fraction = sample as f64 / 4.0; - let t = range.lo + range.width() * fraction; - let authoritative = definition.evaluate(t, store)?; - let fitted: Point3 = std::array::from_fn(|axis| { - left.point[axis] * (1.0 - fraction) + right.point[axis] * fraction - }); - if norm(sub(authoritative.point, fitted)) > tolerance { - return Err(GeometryError::UnresolvedIntersection( - "export curve sample exceeds its certified fitting budget".into(), - )); - } - for (surface, (from, to)) in supports - .into_iter() - .zip([(left.uv_a, right.uv_a), (left.uv_b, right.uv_b)]) - { - let pcurve: UV = - std::array::from_fn(|axis| from[axis] * (1.0 - fraction) + to[axis] * fraction); - if norm(sub(surface.point_at(pcurve)?, fitted)) - > tolerance + definition.residual_tolerance - { - return Err(GeometryError::UnresolvedIntersection( - "export pcurve sample misses the fitted spatial curve".into(), - )); - } - let uv = surface.project(fitted, None)?; - if norm(sub(surface.point_at(uv)?, fitted)) - > tolerance + definition.residual_tolerance - { - return Err(GeometryError::UnresolvedIntersection( - "export curve sample exceeds a retained support budget".into(), - )); - } - } - } - let start = usize::from(index > 0); - controls.extend_from_slice(&xyz[start..]); - pcurve_a.extend_from_slice(&uv_a[start..]); - pcurve_b.extend_from_slice(&uv_b[start..]); - } - let segment_count = accepted.len(); - let knots = (0..=segment_count).map(|value| value as f64).collect(); - let multiplicities = (0..=segment_count) - .map(|index| { - if index == 0 || index == segment_count { - 4 - } else { - 3 - } - }) - .collect(); - Ok(FittedIntersectionCurve { - controls, - pcurve_a, - pcurve_b, - knots, - multiplicities, - error_bound: achieved, - }) -} - -impl FittedIntersectionCurve { - pub fn pcurve(&self, side: IntersectionSide) -> &[[f64; 2]] { - if side == IntersectionSide::A { - &self.pcurve_a - } else { - &self.pcurve_b - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::{ - intersection::SsiBudget, topology::Accuracy, universal_ssi::intersect_patches, Frame3, - SurfaceGeometry, - }; - - #[test] - fn fitted_numerical_curve_has_cubic_shape_and_support_bound() { - let mut store = GeometryStore::new(); - store.surfaces = vec![ - SurfaceGeometry::Torus { - frame: Frame3::IDENTITY, - major_radius: 2.0, - minor_radius: 0.5, - }, - SurfaceGeometry::Plane { - frame: Frame3::from_axis([0.0, 0.0, 0.15], [0.35, -0.2, 1.0], [1.0, 0.0, 0.0]) - .unwrap(), - }, - ]; - let tau = std::f64::consts::TAU; - let result = intersect_patches( - &mut store, - [0, 1], - [ - [Interval::new(0.0, tau).unwrap(); 2], - [Interval::new(-4.0, 4.0).unwrap(); 2], - ], - Accuracy { - geometric: 1e-5, - intersection: 2.5e-6, - tessellation: 1e-3, - exchange: 0.05, - }, - SsiBudget::default(), - ) - .unwrap(); - let definition = match store.curves[result.curves[0].curve as usize] { - super::super::CurveGeometry::Intersection { definition } => definition, - _ => panic!("universal SSI must create an intersection definition"), - }; - let fitted = fit_intersection_curve(&store, definition, 1.0, 20_000).unwrap(); - assert!(fitted.controls.len() >= 4); - assert_eq!((fitted.controls.len() - 1) % 3, 0); - assert_eq!(fitted.controls.len(), fitted.pcurve_a.len()); - assert_eq!(fitted.controls.len(), fitted.pcurve_b.len()); - assert!(fitted.error_bound <= 1.0); - assert_eq!(fitted.knots.len(), fitted.multiplicities.len()); - assert_eq!(fitted.multiplicities.first(), Some(&4)); - assert_eq!(fitted.multiplicities.last(), Some(&4)); - } -} diff --git a/main/opengeometry/src/analytic/face_intersection.rs b/main/opengeometry/src/analytic/face_intersection.rs deleted file mode 100644 index 10e0698..0000000 --- a/main/opengeometry/src/analytic/face_intersection.rs +++ /dev/null @@ -1,1007 +0,0 @@ -use super::{ - geometry::{PatchBounds, UVBox}, - intersection::SsiBudget, - query::face_contains_uv, - ssi::{intersect_surfaces, SsiCurve, SsiResult}, - topology::{Accuracy, BrepEnvelope, Face, GeometryStore, PcurveGeometry}, - universal_ssi, Curve, GeometryError, Point3, Surface, SurfaceGeometry, -}; -use crate::math::{ - interval::Interval, - roots::{quadratic, QuadraticRoots}, -}; - -#[derive(Clone, Copy)] -pub struct FaceView<'a> { - pub brep: &'a BrepEnvelope, - pub face: u32, -} - -pub struct IntersectionBranch { - pub curve: u32, - pub pcurves: [u32; 2], - pub range: Interval, - pub endpoints: [Point3; 2], -} - -pub struct IntersectionGraph { - pub geometry: GeometryStore, - pub branches: Vec, - pub contacts: Vec, - pub coincident: bool, -} - -pub struct FacePairIntersection { - pub faces: [u32; 2], - pub graph: IntersectionGraph, -} - -pub struct BodyIntersectionGraph { - pub pairs: Vec, -} - -fn combined_accuracy(a: Accuracy, b: Accuracy) -> Accuracy { - Accuracy { - geometric: a.geometric.max(b.geometric), - intersection: a.intersection.max(b.intersection), - tessellation: a.tessellation.max(b.tessellation), - exchange: a.exchange.max(b.exchange), - } -} - -fn patch_bounds_overlap(a: PatchBounds, b: PatchBounds, tolerance: f64) -> bool { - (0..3).all(|axis| { - a.axes[axis].lo <= b.axes[axis].hi + tolerance - && a.axes[axis].hi + tolerance >= b.axes[axis].lo - }) -} - -fn face_patch_bounds(brep: &BrepEnvelope, face: &Face) -> Result { - brep.geometry - .surface(face.surface)? - .enclose(face.trim.uv_bounds) -} - -fn clip_coordinate(origin: f64, direction: f64, bounds: Interval, range: &mut [f64; 2]) -> bool { - if direction == 0.0 { - return bounds.contains(origin); - } - let mut lo = (bounds.lo - origin) / direction; - let mut hi = (bounds.hi - origin) / direction; - if lo > hi { - std::mem::swap(&mut lo, &mut hi); - } - range[0] = range[0].max(lo); - range[1] = range[1].min(hi); - range[0] <= range[1] -} - -fn pcurve_line(store: &GeometryStore, id: u32) -> Result<([f64; 2], [f64; 2]), GeometryError> { - match store.pcurves.get(id as usize) { - Some(PcurveGeometry::Line2 { origin, direction }) => Ok((*origin, *direction)), - _ => Err(GeometryError::CoverageGap { - families: ["plane trim".into(), "nonlinear intersection pcurve".into()], - }), - } -} - -fn boundary_parameters( - brep: &BrepEnvelope, - face_id: u32, - line_origin: [f64; 2], - line_direction: [f64; 2], - output: &mut Vec, -) -> Result<(), GeometryError> { - let face = brep.topology.faces.get(face_id as usize).ok_or_else(|| { - GeometryError::MissingReference { - kind: "face".into(), - index: face_id, - } - })?; - for loop_id in std::iter::once(face.trim.outer).chain(face.trim.holes.iter().copied()) { - let loop_ = &brep.topology.loops[loop_id as usize]; - let start = loop_.start_halfedge; - let mut current = start; - for _ in 0..=brep.topology.halfedges.len() { - let use_ = &brep.topology.halfedges[current as usize]; - let pcurve_id = use_.geometry_use.pcurve.ok_or_else(|| { - GeometryError::InvalidTopology("trim boundary is missing its pcurve".into()) - })?; - let edge_range = brep.topology.edges[use_.edge as usize].geometry.range(); - let delta_cross = |a: [f64; 2], b: [f64; 2]| a[0] * b[1] - a[1] * b[0]; - match &brep.geometry.pcurves[pcurve_id as usize] { - PcurveGeometry::Line2 { origin, direction } => { - let determinant = delta_cross(line_direction, *direction); - if determinant != 0.0 { - let delta = [origin[0] - line_origin[0], origin[1] - line_origin[1]]; - let t = delta_cross(delta, *direction) / determinant; - let s = delta_cross(delta, line_direction) / determinant; - if edge_range.contains(s) { - output.push(t); - } - } - } - PcurveGeometry::Conic2 { - origin, - axis_a, - axis_b, - } => { - let determinant = delta_cross(*axis_a, *axis_b); - if determinant == 0.0 { - return Err(GeometryError::InvalidGeometry( - "degenerate planar conic trim".into(), - )); - } - let inverse = |vector: [f64; 2]| { - [ - delta_cross(vector, *axis_b) / determinant, - delta_cross(*axis_a, vector) / determinant, - ] - }; - let offset = inverse([line_origin[0] - origin[0], line_origin[1] - origin[1]]); - let rate = inverse(line_direction); - let roots = quadratic( - rate[0] * rate[0] + rate[1] * rate[1], - 2.0 * (offset[0] * rate[0] + offset[1] * rate[1]), - offset[0] * offset[0] + offset[1] * offset[1] - 1.0, - )?; - let candidates: Vec = match roots { - QuadraticRoots::Empty => Vec::new(), - QuadraticRoots::One(root) => vec![root], - QuadraticRoots::Two(roots) => roots.to_vec(), - QuadraticRoots::IdenticallyZero => { - return Err(GeometryError::UnresolvedIntersection( - "intersection curve coincides with a conic trim".into(), - )) - } - }; - for t in candidates { - let coordinates = [offset[0] + rate[0] * t, offset[1] + rate[1] * t]; - let parameter = coordinates[1].atan2(coordinates[0]); - let tau = std::f64::consts::TAU; - if (-2..=2) - .any(|turn| edge_range.contains(parameter + f64::from(turn) * tau)) - { - output.push(t); - } - } - } - PcurveGeometry::ProjectedCurve { .. } | PcurveGeometry::IntersectionSide { .. } => { - return Err(GeometryError::CoverageGap { - families: ["plane trim".into(), "general numerical boundary".into()], - }) - } - } - current = use_.next.ok_or_else(|| { - GeometryError::InvalidTopology("open face loop during intersection".into()) - })?; - if current == start { - break; - } - } - if current != start { - return Err(GeometryError::InvalidTopology( - "face loop does not close during intersection".into(), - )); - } - } - Ok(()) -} - -fn periodic_value(value: f64, bounds: Interval, period: Option) -> f64 { - period.map_or(value, |period| { - value + ((bounds.midpoint() - value) / period).round() * period - }) -} - -fn uv_in_bounds( - geometry: &GeometryStore, - surface: u32, - bounds: UVBox, - mut uv: [f64; 2], - tolerance: f64, -) -> Result { - let periods = geometry.surface(surface)?.charts()[0].periods; - for axis in 0..2 { - uv[axis] = periodic_value(uv[axis], bounds[axis], periods[axis]); - if uv[axis] < bounds[axis].lo - tolerance || uv[axis] > bounds[axis].hi + tolerance { - return Ok(false); - } - } - Ok(true) -} - -fn loop_halfedges(brep: &BrepEnvelope, loop_id: u32) -> Result, GeometryError> { - let loop_ = brep.topology.loops.get(loop_id as usize).ok_or_else(|| { - GeometryError::MissingReference { - kind: "loop".into(), - index: loop_id, - } - })?; - let mut current = loop_.start_halfedge; - let mut result = Vec::new(); - for _ in 0..=brep.topology.halfedges.len() { - result.push(current); - current = brep.topology.halfedges[current as usize] - .next - .ok_or_else(|| GeometryError::InvalidTopology("open face loop during SSI".into()))?; - if current == loop_.start_halfedge { - return Ok(result); - } - } - Err(GeometryError::InvalidTopology( - "face loop does not close during SSI".into(), - )) -} - -fn is_uv_box_trim(brep: &BrepEnvelope, face: &Face) -> Result { - if !face.trim.holes.is_empty() { - return Ok(false); - } - let surface = brep.geometry.surface(face.surface)?; - let periods = surface.charts()[face.trim.chart as usize].periods; - let tolerance = brep.accuracy.intersection; - let mut sides = [false; 4]; - for halfedge in loop_halfedges(brep, face.trim.outer)? { - let use_ = &brep.topology.halfedges[halfedge as usize]; - let pcurve = use_.geometry_use.pcurve.ok_or_else(|| { - GeometryError::InvalidTopology("trim boundary is missing its pcurve".into()) - })?; - let PcurveGeometry::Line2 { origin, direction } = brep.geometry.pcurves[pcurve as usize] - else { - return Ok(false); - }; - let range = brep.topology.edges[use_.edge as usize].geometry.range(); - let mut ends: [[f64; 2]; 2] = [range.lo, range.hi].map(|parameter| { - std::array::from_fn(|axis| origin[axis] + direction[axis] * parameter) - }); - for end in &mut ends { - for axis in 0..2 { - end[axis] = periodic_value(end[axis], face.trim.uv_bounds[axis], periods[axis]); - } - } - let delta = [ends[1][0] - ends[0][0], ends[1][1] - ends[0][1]]; - let constant_axis = if delta[0].abs() <= tolerance { - 0 - } else if delta[1].abs() <= tolerance { - 1 - } else { - return Ok(false); - }; - let varying_axis = 1 - constant_axis; - let constant = 0.5 * (ends[0][constant_axis] + ends[1][constant_axis]); - let bounds = face.trim.uv_bounds[constant_axis]; - let side = if (constant - bounds.lo).abs() <= tolerance { - 2 * constant_axis - } else if (constant - bounds.hi).abs() <= tolerance { - 2 * constant_axis + 1 - } else { - return Ok(false); - }; - if ends.iter().any(|end| { - end[varying_axis] < face.trim.uv_bounds[varying_axis].lo - tolerance - || end[varying_axis] > face.trim.uv_bounds[varying_axis].hi + tolerance - }) { - return Ok(false); - } - sides[side] = true; - } - Ok(sides.into_iter().all(|present| present)) -} - -fn branch_inside_faces( - geometry: &GeometryStore, - branch: &SsiCurve, - faces: [(FaceView<'_>, &Face); 2], - boxes_only: bool, - tolerance: f64, - parameter: f64, -) -> Result { - for side in 0..2 { - let uv = geometry.pcurve_at(branch.pcurves[side], parameter)?; - let inside = if boxes_only { - uv_in_bounds( - geometry, - side as u32, - faces[side].1.trim.uv_bounds, - uv, - tolerance, - )? - } else { - face_contains_uv(faces[side].0.brep, faces[side].1, uv)? == Some(true) - }; - if !inside { - return Ok(false); - } - } - Ok(true) -} - -fn transition_parameter( - geometry: &GeometryStore, - branch: &SsiCurve, - faces: [(FaceView<'_>, &Face); 2], - boxes_only: bool, - mut outside: f64, - mut inside: f64, - tolerance: f64, -) -> Result { - for _ in 0..64 { - let middle = 0.5 * (outside + inside); - if branch_inside_faces(geometry, branch, faces, boxes_only, tolerance, middle)? { - inside = middle; - } else { - outside = middle; - } - if (inside - outside).abs() <= f64::EPSILON * middle.abs().max(1.0) * 16.0 { - break; - } - } - Ok(inside) -} - -fn tube_may_enter_boxes( - geometry: &GeometryStore, - branch: &SsiCurve, - segment: usize, - bounds: [UVBox; 2], -) -> Result { - let super::CurveGeometry::Intersection { definition } = geometry.curves[branch.curve as usize] - else { - return Ok(true); - }; - let definition = &geometry.intersections[definition as usize]; - let tube = definition.uv_tubes.get(segment).ok_or_else(|| { - GeometryError::InvalidGeometry("intersection branch tube is missing".into()) - })?; - for side in 0..2 { - let definition_side = match geometry.pcurves[branch.pcurves[side] as usize] { - PcurveGeometry::IntersectionSide { - side: super::topology::IntersectionSide::A, - .. - } => 0, - PcurveGeometry::IntersectionSide { - side: super::topology::IntersectionSide::B, - .. - } => 1, - _ => side, - }; - let periods = geometry - .surface(definition.surfaces[definition_side])? - .charts()[0] - .periods; - for axis in 0..2 { - let interval = tube[2 * definition_side + axis]; - let face_bounds = bounds[side][axis]; - let overlaps = periods[axis].map_or_else( - || interval.lo <= face_bounds.hi && interval.hi >= face_bounds.lo, - |period| { - (-2..=2).any(|turn| { - let shift = f64::from(turn) * period; - interval.lo + shift <= face_bounds.hi - && interval.hi + shift >= face_bounds.lo - }) - }, - ); - if !overlaps { - return Ok(false); - } - } - } - Ok(true) -} - -fn clip_face_branch( - geometry: &GeometryStore, - branch: &SsiCurve, - faces: [(FaceView<'_>, &Face); 2], - boxes_only: bool, - bounds: [UVBox; 2], - accuracy: Accuracy, -) -> Result, GeometryError> { - let domain = branch.domain.ok_or_else(|| GeometryError::CoverageGap { - families: [ - "bounded face trim".into(), - "unbounded intersection curve".into(), - ], - })?; - let mut anchors = match geometry.curves[branch.curve as usize] { - super::CurveGeometry::Intersection { definition } => geometry.intersections - [definition as usize] - .anchors - .iter() - .map(|anchor| anchor.parameter) - .collect::>(), - _ => (0..=256) - .map(|index| domain.lo + domain.width() * index as f64 / 256.0) - .collect(), - }; - anchors.retain(|parameter| domain.contains(*parameter)); - anchors.push(domain.lo); - anchors.push(domain.hi); - anchors.sort_by(f64::total_cmp); - anchors.dedup_by(|left, right| (*left - *right).abs() <= f64::EPSILON * 16.0); - - let mut intervals: Vec = Vec::new(); - for (segment, pair) in anchors.windows(2).enumerate() { - let subdivisions = 4; - for part in 0..subdivisions { - let lo = pair[0] + (pair[1] - pair[0]) * part as f64 / subdivisions as f64; - let hi = pair[0] + (pair[1] - pair[0]) * (part + 1) as f64 / subdivisions as f64; - let middle = 0.5 * (lo + hi); - let lo_inside = branch_inside_faces( - geometry, - branch, - faces, - boxes_only, - accuracy.intersection, - lo, - )?; - let middle_inside = branch_inside_faces( - geometry, - branch, - faces, - boxes_only, - accuracy.intersection, - middle, - )?; - let hi_inside = branch_inside_faces( - geometry, - branch, - faces, - boxes_only, - accuracy.intersection, - hi, - )?; - if !lo_inside && !middle_inside && !hi_inside { - if matches!( - geometry.curves[branch.curve as usize], - super::CurveGeometry::Intersection { .. } - ) && tube_may_enter_boxes(geometry, branch, segment, bounds)? - { - return Err(GeometryError::UnresolvedIntersection( - "bounded SSI branch may cross a trim box between certified anchors".into(), - )); - } - continue; - } - let start = if lo_inside { - lo - } else if middle_inside { - transition_parameter( - geometry, - branch, - faces, - boxes_only, - lo, - middle, - accuracy.intersection, - )? - } else { - transition_parameter( - geometry, - branch, - faces, - boxes_only, - middle, - hi, - accuracy.intersection, - )? - }; - let end = if hi_inside { - hi - } else if middle_inside { - transition_parameter( - geometry, - branch, - faces, - boxes_only, - hi, - middle, - accuracy.intersection, - )? - } else { - transition_parameter( - geometry, - branch, - faces, - boxes_only, - middle, - lo, - accuracy.intersection, - )? - }; - let (start, end) = if start <= end { - (start, end) - } else { - (end, start) - }; - if end - start <= accuracy.geometric { - continue; - } - if let Some(previous) = intervals.last_mut() { - // Each trim transition can carry one geometric tolerance of - // correction, so their shared boundary may differ by two. - if start - previous.hi <= accuracy.intersection.max(2.0 * accuracy.geometric) { - *previous = Interval::new(previous.lo, end.max(previous.hi))?; - continue; - } - } - intervals.push(Interval::new(start, end)?); - } - } - Ok(intervals) -} - -fn bound_plane_cylinder_generator( - geometry: &GeometryStore, - branch: &SsiCurve, - faces: [&Face; 2], - accuracy: Accuracy, -) -> Result, GeometryError> { - let super::CurveGeometry::Line { origin, direction } = geometry.curves[branch.curve as usize] - else { - return Err(GeometryError::CoverageGap { - families: ["plane/cylinder trim".into(), "nonlinear generator".into()], - }); - }; - let mut range = [-f64::MAX.sqrt(), f64::MAX.sqrt()]; - for side in 0..2 { - let surface = geometry.surface(side as u32)?; - let start = surface.project(origin, None)?; - let next = surface.project(super::geometry::add(origin, direction), Some(start))?; - let periods = surface.charts()[0].periods; - for axis in 0..2 { - let bounds = faces[side].trim.uv_bounds[axis]; - let initial = periodic_value(start[axis], bounds, periods[axis]); - let later = periodic_value(next[axis], bounds, periods[axis]); - let mut rate = later - initial; - if matches!(surface, SurfaceGeometry::Cylinder { .. }) - && axis == 0 - && rate.abs() > accuracy.intersection - { - return Err(GeometryError::CoverageGap { - families: ["plane/cylinder trim".into(), "nonaxial generator".into()], - }); - } - if matches!(surface, SurfaceGeometry::Cylinder { .. }) && axis == 0 { - rate = 0.0; - } - if !clip_coordinate(initial, rate, bounds, &mut range) { - return Ok(None); - } - } - } - if !range[0].is_finite() || !range[1].is_finite() || range[1] - range[0] <= accuracy.geometric { - return Ok(None); - } - Ok(Some(Interval::new(range[0], range[1])?)) -} - -fn intersect_bounded_surfaces( - geometry: &mut GeometryStore, - faces: [&Face; 2], - accuracy: Accuracy, -) -> Result { - let planar_pair = geometry - .surfaces - .iter() - .take(2) - .all(|surface| matches!(surface, SurfaceGeometry::Plane { .. })); - let plane_cylinder_pair = geometry.surfaces.len() >= 2 - && matches!( - (&geometry.surfaces[0], &geometry.surfaces[1]), - ( - SurfaceGeometry::Plane { .. }, - SurfaceGeometry::Cylinder { .. } - ) | ( - SurfaceGeometry::Cylinder { .. }, - SurfaceGeometry::Plane { .. } - ) - ); - match intersect_surfaces(geometry, 0, 1, accuracy) { - Ok(mut result) if plane_cylinder_pair => { - let mut bounded = Vec::new(); - for mut branch in result.curves { - if branch.domain.is_none() { - branch.domain = - bound_plane_cylinder_generator(geometry, &branch, faces, accuracy)?; - } - if branch.domain.is_some() { - bounded.push(branch); - } - } - result.curves = bounded; - Ok(result) - } - Ok(result) if planar_pair || result.curves.iter().all(|curve| curve.domain.is_some()) => { - Ok(result) - } - Ok(_) | Err(GeometryError::CoverageGap { .. }) => universal_ssi::intersect_patches( - geometry, - [0, 1], - [faces[0].trim.uv_bounds, faces[1].trim.uv_bounds], - accuracy, - SsiBudget::default(), - ), - Err(error) => Err(error), - } -} - -pub fn intersect_faces( - a: FaceView<'_>, - b: FaceView<'_>, -) -> Result { - a.brep.validate()?; - b.brep.validate()?; - let face_a = a.brep.topology.faces.get(a.face as usize).ok_or_else(|| { - GeometryError::MissingReference { - kind: "face".into(), - index: a.face, - } - })?; - let face_b = b.brep.topology.faces.get(b.face as usize).ok_or_else(|| { - GeometryError::MissingReference { - kind: "face".into(), - index: b.face, - } - })?; - let planar = matches!( - a.brep.geometry.surfaces[face_a.surface as usize], - SurfaceGeometry::Plane { .. } - ) && matches!( - b.brep.geometry.surfaces[face_b.surface as usize], - SurfaceGeometry::Plane { .. } - ); - let boxes_only = !planar && is_uv_box_trim(a.brep, face_a)? && is_uv_box_trim(b.brep, face_b)?; - let mut geometry = GeometryStore::new(); - geometry - .surfaces - .push(a.brep.geometry.surfaces[face_a.surface as usize].clone()); - geometry - .surfaces - .push(b.brep.geometry.surfaces[face_b.surface as usize].clone()); - let accuracy = combined_accuracy(a.brep.accuracy, b.brep.accuracy); - let result = intersect_bounded_surfaces(&mut geometry, [face_a, face_b], accuracy)?; - let contacts = result - .contacts - .into_iter() - .filter_map(|point| { - let ua = geometry.surfaces[0].project(point, None).ok()?; - let ub = geometry.surfaces[1].project(point, None).ok()?; - (face_contains_uv(a.brep, face_a, ua).ok()? == Some(true) - && face_contains_uv(b.brep, face_b, ub).ok()? == Some(true)) - .then_some(point) - }) - .collect(); - let mut branches = Vec::new(); - for branch in result.curves { - if !planar { - for range in clip_face_branch( - &geometry, - &branch, - [(a, face_a), (b, face_b)], - boxes_only, - [face_a.trim.uv_bounds, face_b.trim.uv_bounds], - accuracy, - )? { - let curve = geometry.curve(branch.curve)?; - branches.push(IntersectionBranch { - curve: branch.curve, - pcurves: branch.pcurves, - range, - endpoints: [curve.point_at(range.lo)?, curve.point_at(range.hi)?], - }); - } - continue; - } - let (origin_a, direction_a) = pcurve_line(&geometry, branch.pcurves[0])?; - let (origin_b, direction_b) = pcurve_line(&geometry, branch.pcurves[1])?; - let mut range = branch - .domain - .map_or([-f64::MAX.sqrt(), f64::MAX.sqrt()], |range| { - [range.lo, range.hi] - }); - for (origin, direction, face) in [ - (origin_a, direction_a, face_a), - (origin_b, direction_b, face_b), - ] { - for axis in 0..2 { - if !clip_coordinate( - origin[axis], - direction[axis], - face.trim.uv_bounds[axis], - &mut range, - ) { - range[0] = 1.0; - range[1] = 0.0; - break; - } - } - } - if range[0] > range[1] { - continue; - } - let mut splits = vec![range[0], range[1]]; - boundary_parameters(a.brep, a.face, origin_a, direction_a, &mut splits)?; - boundary_parameters(b.brep, b.face, origin_b, direction_b, &mut splits)?; - splits.retain(|value| *value >= range[0] && *value <= range[1] && value.is_finite()); - splits.sort_by(f64::total_cmp); - splits.dedup_by(|left, right| (*left - *right).abs() <= accuracy.intersection); - for interval in splits.windows(2) { - if interval[1] - interval[0] <= accuracy.geometric { - continue; - } - let midpoint = interval[0] / 2.0 + interval[1] / 2.0; - let uv_a = geometry.pcurve_at(branch.pcurves[0], midpoint)?; - let uv_b = geometry.pcurve_at(branch.pcurves[1], midpoint)?; - if face_contains_uv(a.brep, face_a, uv_a)? != Some(true) - || face_contains_uv(b.brep, face_b, uv_b)? != Some(true) - { - continue; - } - let range = Interval::new(interval[0], interval[1])?; - let curve = geometry.curve(branch.curve)?; - branches.push(IntersectionBranch { - curve: branch.curve, - pcurves: branch.pcurves, - range, - endpoints: [curve.point_at(range.lo)?, curve.point_at(range.hi)?], - }); - } - } - Ok(IntersectionGraph { - geometry, - branches, - contacts, - coincident: result.coincident, - }) -} - -pub fn intersect_breps( - a: &BrepEnvelope, - b: &BrepEnvelope, -) -> Result { - a.validate()?; - b.validate()?; - let accuracy = combined_accuracy(a.accuracy, b.accuracy); - let bounds_a = a - .topology - .faces - .iter() - .map(|face| face_patch_bounds(a, face)) - .collect::, _>>()?; - let bounds_b = b - .topology - .faces - .iter() - .map(|face| face_patch_bounds(b, face)) - .collect::, _>>()?; - let mut pairs = Vec::new(); - for (face_a, bounds_a) in a.topology.faces.iter().zip(&bounds_a) { - for (face_b, bounds_b) in b.topology.faces.iter().zip(&bounds_b) { - if !patch_bounds_overlap(*bounds_a, *bounds_b, accuracy.intersection) { - continue; - } - let graph = intersect_faces( - FaceView { - brep: a, - face: face_a.id, - }, - FaceView { - brep: b, - face: face_b.id, - }, - ) - .map_err(|error| match error { - GeometryError::UnresolvedIntersection(message) => { - GeometryError::UnresolvedIntersection(format!( - "face pair {}:{}: {message}", - face_a.id, face_b.id - )) - } - other => other, - })?; - if graph.coincident || !graph.branches.is_empty() || !graph.contacts.is_empty() { - pairs.push(FacePairIntersection { - faces: [face_a.id, face_b.id], - graph, - }); - } - } - } - Ok(BodyIntersectionGraph { pairs }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::{ - geometry::{norm, sub}, - primitives, - topology::Accuracy, - CurveGeometry, Frame3, - }; - - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-5, - } - } - - #[test] - fn plane_face_graph_clips_the_shared_curve_to_both_trims() { - let a = primitives::cuboid("a".into(), Frame3::IDENTITY, [2.0; 3], accuracy()).unwrap(); - let b = primitives::cuboid( - "b".into(), - Frame3 { - origin: [-1.0, 0.5, 1.0], - ..Frame3::IDENTITY - }, - [2.0; 3], - accuracy(), - ) - .unwrap(); - let graph = intersect_faces( - FaceView { brep: &a, face: 1 }, - FaceView { brep: &b, face: 3 }, - ) - .unwrap(); - assert_eq!(graph.branches.len(), 1); - let mut endpoints = graph.branches[0].endpoints; - endpoints.sort_by(|a, b| a[1].total_cmp(&b[1])); - for (point, expected_y) in endpoints.into_iter().zip([0.5, 2.0]) { - assert!((point[0] - 1.0).abs() <= accuracy().intersection); - assert!((point[1] - expected_y).abs() <= accuracy().intersection); - assert!((point[2] - 2.0).abs() <= accuracy().intersection); - } - } - - #[test] - fn vertical_plane_cylinder_generators_remain_exact_and_bounded() { - let quarter = std::f64::consts::FRAC_PI_2; - let host = primitives::arc_edged_extrusion( - "arc-profile".into(), - Frame3::IDENTITY, - vec![ - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.0, - start_angle: 0.0, - sweep_angle: quarter, - }, - primitives::ProfileEdge::Line { - from: [0.0, 2.0], - to: [0.0, 1.5], - }, - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.5, - start_angle: quarter, - sweep_angle: -quarter, - }, - primitives::ProfileEdge::Line { - from: [1.5, 0.0], - to: [2.0, 0.0], - }, - ], - 3.0, - accuracy(), - ) - .unwrap(); - let cutter = primitives::cuboid( - "opening".into(), - Frame3 { - origin: [1.1, 0.9, 0.5], - ..Frame3::IDENTITY - }, - [1.0, 0.35, 1.5], - accuracy(), - ) - .unwrap(); - let graph = intersect_breps(&host, &cutter).unwrap(); - assert!(!graph.pairs.is_empty()); - let branches = graph - .pairs - .iter() - .flat_map(|pair| &pair.graph.branches) - .collect::>(); - assert!(!branches.is_empty()); - for pair in &graph.pairs { - for branch in &pair.graph.branches { - assert!(branch.range.lo.is_finite() && branch.range.hi.is_finite()); - assert!(!matches!( - pair.graph.geometry.curves[branch.curve as usize], - CurveGeometry::Intersection { .. } - )); - } - } - } - - #[test] - fn bounded_nonparallel_cylinder_faces_use_universal_ssi() { - let accuracy = Accuracy { - geometric: 1e-5, - intersection: 2.5e-6, - tessellation: 0.01, - exchange: 1e-5, - }; - let a = primitives::cylinder( - "a".into(), - Frame3 { - origin: [0.0, 0.0, -2.0], - ..Frame3::IDENTITY - }, - 1.0, - 4.0, - accuracy, - ) - .unwrap(); - let b_frame = - Frame3::from_axis([-1.0, 0.0, 0.2], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]).unwrap(); - let b = primitives::cylinder("b".into(), b_frame, 0.6, 2.0, accuracy).unwrap(); - let graph = intersect_faces( - FaceView { brep: &a, face: 0 }, - FaceView { brep: &b, face: 0 }, - ) - .unwrap(); - assert!(!graph.branches.is_empty()); - for branch in &graph.branches { - assert!(matches!( - graph.geometry.curves[branch.curve as usize], - CurveGeometry::Intersection { .. } - )); - let parameter = branch.range.midpoint(); - let point = graph - .geometry - .curve(branch.curve) - .unwrap() - .point_at(parameter) - .unwrap(); - for side in 0..2 { - let uv = graph - .geometry - .pcurve_at(branch.pcurves[side], parameter) - .unwrap(); - let support = graph.geometry.surfaces[side].point_at(uv).unwrap(); - assert!(norm(sub(point, support)) <= accuracy.intersection); - } - } - } - - #[test] - fn body_graph_uses_patch_bounds_before_face_pair_ssi() { - let accuracy = Accuracy { - geometric: 1e-5, - intersection: 2.5e-6, - tessellation: 0.01, - exchange: 1e-5, - }; - let a = primitives::cylinder( - "a".into(), - Frame3 { - origin: [0.0, 0.0, -2.0], - ..Frame3::IDENTITY - }, - 1.0, - 4.0, - accuracy, - ) - .unwrap(); - let b = primitives::cylinder( - "b".into(), - Frame3::from_axis([-2.0, 0.0, 0.2], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]).unwrap(), - 0.6, - 4.0, - accuracy, - ) - .unwrap(); - let graph = intersect_breps(&a, &b).unwrap(); - assert_eq!(graph.pairs.len(), 1); - assert_eq!(graph.pairs[0].faces, [0, 0]); - assert_eq!(graph.pairs[0].graph.branches.len(), 2); - } -} diff --git a/main/opengeometry/src/analytic/geometry.rs b/main/opengeometry/src/analytic/geometry.rs deleted file mode 100644 index f820e44..0000000 --- a/main/opengeometry/src/analytic/geometry.rs +++ /dev/null @@ -1,802 +0,0 @@ -use super::GeometryError; -use crate::math::{finite, interval::Interval}; -use serde::{Deserialize, Serialize}; - -pub type Point3 = [f64; 3]; -pub type UV = [f64; 2]; -pub type UVBox = [Interval; 2]; - -pub(crate) fn add(a: Point3, b: Point3) -> Point3 { - std::array::from_fn(|i| a[i] + b[i]) -} -pub(crate) fn sub(a: Point3, b: Point3) -> Point3 { - std::array::from_fn(|i| a[i] - b[i]) -} -pub(crate) fn scale(a: Point3, s: f64) -> Point3 { - a.map(|v| v * s) -} -pub(crate) fn dot(a: Point3, b: Point3) -> f64 { - a[0].mul_add(b[0], a[1].mul_add(b[1], a[2] * b[2])) -} -pub(crate) fn cross(a: Point3, b: Point3) -> Point3 { - [ - a[1] * b[2] - a[2] * b[1], - a[2] * b[0] - a[0] * b[2], - a[0] * b[1] - a[1] * b[0], - ] -} -pub(crate) fn norm(a: Point3) -> f64 { - a[0].hypot(a[1]).hypot(a[2]) -} -pub(crate) fn unit(a: Point3) -> Result { - let n = finite(norm(a))?; - if n == 0.0 { - return Err(GeometryError::SingularParameterization); - } - checked(a.map(|value| value / n)) -} -fn checked(p: Point3) -> Result { - for v in p { - finite(v)?; - } - Ok(p) -} - -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Frame3 { - pub origin: Point3, - pub x: Point3, - pub y: Point3, - pub z: Point3, -} - -impl Frame3 { - pub const IDENTITY: Self = Self { - origin: [0.0; 3], - x: [1.0, 0.0, 0.0], - y: [0.0, 1.0, 0.0], - z: [0.0, 0.0, 1.0], - }; - - pub fn from_axis( - origin: Point3, - axis: Point3, - reference: Point3, - ) -> Result { - checked(origin)?; - let z = unit(axis)?; - let x = unit(sub(reference, scale(z, dot(z, reference))))?; - let y = unit(cross(z, x))?; - let frame = Self { origin, x, y, z }; - frame.validate()?; - Ok(frame) - } - - pub fn validate(&self) -> Result<(), GeometryError> { - for v in [self.origin, self.x, self.y, self.z] { - checked(v)?; - } - if [self.x, self.y, self.z] - .iter() - .any(|&v| (norm(v) - 1.0).abs() > 1e-12) - || dot(self.x, self.y).abs() > 1e-12 - || dot(self.x, self.z).abs() > 1e-12 - || dot(self.y, self.z).abs() > 1e-12 - || norm(sub(cross(self.x, self.y), self.z)) > 1e-12 - { - return Err(GeometryError::InvalidGeometry( - "frame must be right-handed and orthonormal".into(), - )); - } - Ok(()) - } - - pub fn vector(&self, p: Point3) -> Point3 { - add( - add(scale(self.x, p[0]), scale(self.y, p[1])), - scale(self.z, p[2]), - ) - } - pub fn point(&self, p: Point3) -> Point3 { - add(self.origin, self.vector(p)) - } - pub fn local(&self, p: Point3) -> Point3 { - let d = sub(p, self.origin); - [dot(d, self.x), dot(d, self.y), dot(d, self.z)] - } - - fn bounds(&self, local: [Interval; 3]) -> Result { - let mut axes = [Interval::point(0.0)?; 3]; - for i in 0..3 { - axes[i] = Interval::point(self.origin[i])? - .add(local[0].mul(Interval::point(self.x[i])?)?)? - .add(local[1].mul(Interval::point(self.y[i])?)?)? - .add(local[2].mul(Interval::point(self.z[i])?)?)?; - } - Ok(PatchBounds { axes }) - } -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct PatchBounds { - pub axes: [Interval; 3], -} -impl PatchBounds { - pub fn contains(&self, point: Point3) -> bool { - (0..3).all(|i| self.axes[i].contains(point[i])) - } - pub fn overlaps(&self, other: &Self) -> bool { - (0..3).all(|i| self.axes[i].overlaps(other.axes[i])) - } -} - -#[derive(Clone, Copy, Debug)] -pub struct SurfaceChart { - pub domain: [[f64; 2]; 2], - pub periods: [Option; 2], - pub singular_v: &'static [f64], -} -const PLANE_CHART: [SurfaceChart; 1] = [SurfaceChart { - domain: [[-f64::MAX, f64::MAX]; 2], - periods: [None, None], - singular_v: &[], -}]; -const SPHERE_CHART: [SurfaceChart; 1] = [SurfaceChart { - domain: [ - [0.0, std::f64::consts::TAU], - [-std::f64::consts::FRAC_PI_2, std::f64::consts::FRAC_PI_2], - ], - periods: [Some(std::f64::consts::TAU), None], - singular_v: &[-std::f64::consts::FRAC_PI_2, std::f64::consts::FRAC_PI_2], -}]; -const CYLINDER_CHART: [SurfaceChart; 1] = [SurfaceChart { - domain: [[0.0, std::f64::consts::TAU], [-f64::MAX, f64::MAX]], - periods: [Some(std::f64::consts::TAU), None], - singular_v: &[], -}]; -const CONE_CHART: [SurfaceChart; 1] = [SurfaceChart { - domain: [[0.0, std::f64::consts::TAU], [0.0, f64::MAX]], - periods: [Some(std::f64::consts::TAU), None], - singular_v: &[0.0], -}]; -const TORUS_CHART: [SurfaceChart; 1] = [SurfaceChart { - domain: [[0.0, std::f64::consts::TAU]; 2], - periods: [Some(std::f64::consts::TAU); 2], - singular_v: &[], -}]; - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(tag = "kind", deny_unknown_fields)] -pub enum SurfaceGeometry { - Plane { - frame: Frame3, - }, - Sphere { - frame: Frame3, - radius: f64, - }, - Cylinder { - frame: Frame3, - radius: f64, - }, - Cone { - frame: Frame3, - semi_angle: f64, - }, - Torus { - frame: Frame3, - major_radius: f64, - minor_radius: f64, - }, -} - -#[derive(Clone, Copy, Debug)] -pub struct SurfaceJet2 { - pub point: Point3, - pub du: Point3, - pub dv: Point3, - pub duu: Point3, - pub duv: Point3, - pub dvv: Point3, -} - -pub trait Surface { - fn point_at(&self, uv: UV) -> Result; - fn normal_at(&self, uv: UV) -> Result; - fn derivatives(&self, uv: UV) -> Result; - fn project(&self, p: Point3, hint: Option) -> Result; - fn charts(&self) -> &[SurfaceChart]; - fn enclose(&self, domain: UVBox) -> Result; -} - -impl SurfaceGeometry { - pub fn frame(&self) -> &Frame3 { - match self { - Self::Plane { frame } - | Self::Sphere { frame, .. } - | Self::Cylinder { frame, .. } - | Self::Cone { frame, .. } - | Self::Torus { frame, .. } => frame, - } - } - pub fn validate(&self) -> Result<(), GeometryError> { - self.frame().validate()?; - let valid = match self { - Self::Plane { .. } => true, - Self::Sphere { radius, .. } | Self::Cylinder { radius, .. } => { - radius.is_finite() && *radius > 0.0 - } - Self::Cone { semi_angle, .. } => { - semi_angle.is_finite() - && *semi_angle > 0.0 - && *semi_angle < std::f64::consts::FRAC_PI_2 - } - Self::Torus { - major_radius, - minor_radius, - .. - } => { - major_radius.is_finite() - && minor_radius.is_finite() - && *minor_radius > 0.0 - && major_radius > minor_radius - } - }; - if !valid { - return Err(GeometryError::InvalidGeometry( - "invalid radius, cone angle, or ring torus".into(), - )); - } - Ok(()) - } - - fn check_uv(&self, uv: UV) -> Result<(), GeometryError> { - self.validate()?; - for x in uv { - finite(x)?; - } - if matches!(self, Self::Sphere { .. }) && uv[1].abs() > std::f64::consts::FRAC_PI_2 - || matches!(self, Self::Cone { .. }) && uv[1] < 0.0 - { - return Err(GeometryError::InvalidGeometry( - "UV outside surface domain".into(), - )); - } - Ok(()) - } - - pub(crate) fn parameter_in_domain(&self, uv: UV) -> bool { - uv.into_iter().all(f64::is_finite) - && (!matches!(self, Self::Sphere { .. }) || uv[1].abs() <= std::f64::consts::FRAC_PI_2) - && (!matches!(self, Self::Cone { .. }) || uv[1] >= 0.0) - } -} - -fn lift(angle: f64, hint: Option) -> f64 { - hint.map_or(angle, |h| { - angle + ((h - angle) / std::f64::consts::TAU).round() * std::f64::consts::TAU - }) -} - -impl Surface for SurfaceGeometry { - fn point_at(&self, uv: UV) -> Result { - Ok(self.derivatives(uv)?.point) - } - - fn derivatives(&self, uv: UV) -> Result { - self.check_uv(uv)?; - let (su, cu) = uv[0].sin_cos(); - let (sv, cv) = uv[1].sin_cos(); - let radial = [cu, su, 0.0]; - let tangent = [-su, cu, 0.0]; - let z = [0.0, 0.0, 1.0]; - let zero = [0.0; 3]; - let (p, du, dv, duu, duv, dvv) = match self { - Self::Plane { .. } => ( - [uv[0], uv[1], 0.0], - [1.0, 0.0, 0.0], - [0.0, 1.0, 0.0], - zero, - zero, - zero, - ), - Self::Sphere { radius: r, .. } => { - let p = add(scale(radial, r * cv), scale(z, r * sv)); - ( - p, - scale(tangent, r * cv), - add(scale(radial, -r * sv), scale(z, r * cv)), - scale(radial, -r * cv), - scale(tangent, -r * sv), - scale(p, -1.0), - ) - } - Self::Cylinder { radius: r, .. } => ( - add(scale(radial, *r), scale(z, uv[1])), - scale(tangent, *r), - z, - scale(radial, -r), - zero, - zero, - ), - Self::Cone { semi_angle, .. } => { - let k = semi_angle.tan(); - let r = uv[1] * k; - ( - add(scale(radial, r), scale(z, uv[1])), - scale(tangent, r), - add(scale(radial, k), z), - scale(radial, -r), - scale(tangent, k), - zero, - ) - } - Self::Torus { - major_radius: r, - minor_radius: a, - .. - } => { - let b = r + a * cv; - ( - add(scale(radial, b), scale(z, a * sv)), - scale(tangent, b), - add(scale(radial, -a * sv), scale(z, a * cv)), - scale(radial, -b), - scale(tangent, -a * sv), - add(scale(radial, -a * cv), scale(z, -a * sv)), - ) - } - }; - let f = self.frame(); - Ok(SurfaceJet2 { - point: checked(f.point(p))?, - du: checked(f.vector(du))?, - dv: checked(f.vector(dv))?, - duu: checked(f.vector(duu))?, - duv: checked(f.vector(duv))?, - dvv: checked(f.vector(dvv))?, - }) - } - - fn normal_at(&self, uv: UV) -> Result { - self.check_uv(uv)?; - let (s, c) = uv[0].sin_cos(); - let (sv, cv) = uv[1].sin_cos(); - let n = match self { - Self::Plane { .. } => [0.0, 0.0, 1.0], - Self::Sphere { .. } | Self::Torus { .. } => [cv * c, cv * s, sv], - Self::Cylinder { .. } => [c, s, 0.0], - Self::Cone { semi_angle, .. } => { - if uv[1] == 0.0 { - return Err(GeometryError::SingularParameterization); - } - [ - semi_angle.cos() * c, - semi_angle.cos() * s, - -semi_angle.sin(), - ] - } - }; - checked(self.frame().vector(n)) - } - - fn project(&self, p: Point3, hint: Option) -> Result { - self.validate()?; - checked(p)?; - if let Some(h) = hint { - for v in h { - finite(v)?; - } - } - let p = checked(self.frame().local(p))?; - let radial = p[0].hypot(p[1]); - let u = lift(p[1].atan2(p[0]), hint.map(|h| h[0])); - let result = match self { - Self::Plane { .. } => [p[0], p[1]], - Self::Sphere { .. } => { - if norm(p) == 0.0 { - return Err(GeometryError::SingularParameterization); - } - [u, p[2].atan2(radial)] - } - Self::Cylinder { .. } => { - if radial == 0.0 { - return Err(GeometryError::SingularParameterization); - } - [u, p[2]] - } - Self::Cone { semi_angle, .. } => { - let v = (radial * semi_angle.sin() + p[2] * semi_angle.cos()) * semi_angle.cos(); - if radial == 0.0 || v <= 0.0 { - return Err(GeometryError::SingularParameterization); - } - [u, v] - } - Self::Torus { major_radius, .. } => { - if radial == 0.0 || (radial == *major_radius && p[2] == 0.0) { - return Err(GeometryError::SingularParameterization); - } - [ - u, - lift(p[2].atan2(radial - major_radius), hint.map(|h| h[1])), - ] - } - }; - for x in result { - finite(x)?; - } - Ok(result) - } - - fn charts(&self) -> &[SurfaceChart] { - match self { - Self::Plane { .. } => &PLANE_CHART, - Self::Sphere { .. } => &SPHERE_CHART, - Self::Cylinder { .. } => &CYLINDER_CHART, - Self::Cone { .. } => &CONE_CHART, - Self::Torus { .. } => &TORUS_CHART, - } - } - - fn enclose(&self, domain: UVBox) -> Result { - for d in domain { - Interval::new(d.lo, d.hi)?; - } - self.check_uv([domain[0].lo, domain[1].lo])?; - self.check_uv([domain[0].hi, domain[1].hi])?; - let [u, v] = domain; - let zero = Interval::point(0.0)?; - let c = u.cos()?; - let s = u.sin()?; - let local = match self { - Self::Plane { .. } => [u, v, zero], - Self::Sphere { radius, .. } => { - let r = Interval::point(*radius)?; - let cv = v.cos()?.mul(r)?; - [c.mul(cv)?, s.mul(cv)?, v.sin()?.mul(r)?] - } - Self::Cylinder { radius, .. } => { - let r = Interval::point(*radius)?; - [c.mul(r)?, s.mul(r)?, v] - } - Self::Cone { semi_angle, .. } => { - let angle = Interval::point(*semi_angle)?; - let k = angle.sin()?.div(angle.cos()?)?; - let r = v.mul(k)?; - [c.mul(r)?, s.mul(r)?, v] - } - Self::Torus { - major_radius, - minor_radius, - .. - } => { - let a = Interval::point(*minor_radius)?; - let r = Interval::point(*major_radius)?.add(v.cos()?.mul(a)?)?; - [c.mul(r)?, s.mul(r)?, v.sin()?.mul(a)?] - } - }; - self.frame().bounds(local) - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(tag = "kind", deny_unknown_fields)] -pub enum CurveGeometry { - Line { - origin: Point3, - direction: Point3, - }, - Circle { - frame: Frame3, - radius: f64, - }, - Ellipse { - frame: Frame3, - major_radius: f64, - minor_radius: f64, - }, - Intersection { - definition: u32, - }, -} - -pub trait Curve { - fn point_at(&self, t: f64) -> Result; - fn tangent_at(&self, t: f64) -> Result; - fn enclose(&self, range: Interval) -> Result; -} - -impl CurveGeometry { - pub fn validate(&self) -> Result<(), GeometryError> { - match self { - Self::Line { origin, direction } => { - checked(*origin)?; - checked(*direction)?; - if (norm(*direction) - 1.0).abs() > 1e-12 { - return Err(GeometryError::InvalidGeometry( - "line direction must be unit length".into(), - )); - } - } - Self::Circle { frame, radius } => { - frame.validate()?; - if !radius.is_finite() || *radius <= 0.0 { - return Err(GeometryError::InvalidGeometry( - "circle radius must be positive".into(), - )); - } - } - Self::Ellipse { - frame, - major_radius: a, - minor_radius: b, - } => { - frame.validate()?; - if !a.is_finite() || !b.is_finite() || *b <= 0.0 || a < b { - return Err(GeometryError::InvalidGeometry( - "invalid ellipse radii".into(), - )); - } - } - Self::Intersection { .. } => {} - }; - Ok(()) - } - - pub(crate) fn elementary_point(&self, t: f64) -> Result { - self.validate()?; - finite(t)?; - checked(match self { - Self::Line { origin, direction } => add(*origin, scale(*direction, t)), - Self::Circle { frame, radius } => { - frame.point([radius * t.cos(), radius * t.sin(), 0.0]) - } - Self::Ellipse { - frame, - major_radius: a, - minor_radius: b, - } => frame.point([a * t.cos(), b * t.sin(), 0.0]), - Self::Intersection { definition } => { - return Err(GeometryError::MissingReference { - kind: "intersection".into(), - index: *definition, - }) - } - }) - } - - pub(crate) fn elementary_tangent(&self, t: f64) -> Result { - self.validate()?; - finite(t)?; - checked(match self { - Self::Line { direction, .. } => *direction, - Self::Circle { frame, radius } => { - frame.vector([-radius * t.sin(), radius * t.cos(), 0.0]) - } - Self::Ellipse { - frame, - major_radius: a, - minor_radius: b, - } => frame.vector([-a * t.sin(), b * t.cos(), 0.0]), - Self::Intersection { definition } => { - return Err(GeometryError::MissingReference { - kind: "intersection".into(), - index: *definition, - }) - } - }) - } - - pub(crate) fn elementary_bounds(&self, range: Interval) -> Result { - self.validate()?; - Interval::new(range.lo, range.hi)?; - let zero = Interval::point(0.0)?; - match self { - Self::Line { origin, direction } => { - let mut axes = [zero; 3]; - for i in 0..3 { - axes[i] = Interval::point(origin[i])? - .add(range.mul(Interval::point(direction[i])?)?)?; - } - Ok(PatchBounds { axes }) - } - Self::Circle { frame, radius } => frame.bounds([ - range.cos()?.mul(Interval::point(*radius)?)?, - range.sin()?.mul(Interval::point(*radius)?)?, - zero, - ]), - Self::Ellipse { - frame, - major_radius: a, - minor_radius: b, - } => frame.bounds([ - range.cos()?.mul(Interval::point(*a)?)?, - range.sin()?.mul(Interval::point(*b)?)?, - zero, - ]), - Self::Intersection { definition } => Err(GeometryError::MissingReference { - kind: "intersection".into(), - index: *definition, - }), - } - } - - pub fn sample_elementary( - &self, - range: Interval, - deflection: f64, - max_segments: usize, - ) -> Result, GeometryError> { - self.validate()?; - Interval::new(range.lo, range.hi)?; - if !deflection.is_finite() || deflection <= 0.0 { - return Err(GeometryError::InvalidGeometry( - "deflection must be finite and positive".into(), - )); - } - let bound = match self { - Self::Line { .. } => 0.0, - Self::Circle { radius, .. } => *radius, - Self::Ellipse { major_radius, .. } => *major_radius, - Self::Intersection { definition } => { - return Err(GeometryError::MissingReference { - kind: "intersection".into(), - index: *definition, - }) - } - }; - let minimum = if bound > 0.0 && range.width() >= std::f64::consts::TAU - 1e-12 { - 3.0 - } else { - 1.0 - }; - let count = (range.width().abs() * (bound / (8.0 * deflection)).sqrt()) - .ceil() - .max(minimum); - if !count.is_finite() || count > max_segments as f64 { - return Err(GeometryError::LimitExceeded( - "curve tessellation segments".into(), - )); - } - let n = count as usize; - let mut samples = Vec::with_capacity(n + 1); - for i in 0..=n { - samples.push( - self.elementary_point(range.lo + (range.hi - range.lo) * i as f64 / n as f64)?, - ); - } - Ok(samples) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn surfaces() -> Vec { - let frame = Frame3::from_axis([3.0, -2.0, 7.0], [1.0, 2.0, 3.0], [0.0, 1.0, 0.0]).unwrap(); - vec![ - SurfaceGeometry::Plane { frame }, - SurfaceGeometry::Sphere { frame, radius: 2.0 }, - SurfaceGeometry::Cylinder { frame, radius: 2.0 }, - SurfaceGeometry::Cone { - frame, - semi_angle: 0.4, - }, - SurfaceGeometry::Torus { - frame, - major_radius: 3.0, - minor_radius: 1.0, - }, - ] - } - - #[test] - fn derivatives_projection_and_normals_for_all_families() { - let uv = [0.7, 0.4]; - let h = 1e-5; - for surface in surfaces() { - let jet = surface.derivatives(uv).unwrap(); - let n = surface.normal_at(uv).unwrap(); - assert!((norm(n) - 1.0).abs() < 1e-12); - assert!(dot(n, jet.du).abs() < 1e-12); - assert!(dot(n, jet.dv).abs() < 1e-12); - let projected = surface.project(jet.point, Some(uv)).unwrap(); - assert!((projected[0] - uv[0]).abs() < 1e-12); - assert!((projected[1] - uv[1]).abs() < 1e-12); - for dimension in 0..2 { - let mut lo = uv; - let mut hi = uv; - lo[dimension] -= h; - hi[dimension] += h; - let jl = surface.derivatives(lo).unwrap(); - let jh = surface.derivatives(hi).unwrap(); - let derivative = scale(sub(jh.point, jl.point), 0.5 / h); - assert!( - norm(sub( - derivative, - if dimension == 0 { jet.du } else { jet.dv } - )) < 1e-8 - ); - assert!( - norm(sub( - scale(sub(jh.du, jl.du), 0.5 / h), - if dimension == 0 { jet.duu } else { jet.duv } - )) < 1e-8 - ); - assert!( - norm(sub( - scale(sub(jh.dv, jl.dv), 0.5 / h), - if dimension == 0 { jet.duv } else { jet.dvv } - )) < 1e-8 - ); - } - } - } - - #[test] - fn conservative_boxes_cover_transformed_patches() { - let box_uv = [ - Interval::new(0.2, 1.8).unwrap(), - Interval::new(0.1, 0.9).unwrap(), - ]; - for surface in surfaces() { - let bounds = surface.enclose(box_uv).unwrap(); - for i in 0..=24 { - for j in 0..=24 { - let uv = [0.2 + 1.6 * i as f64 / 24.0, 0.1 + 0.8 * j as f64 / 24.0]; - assert!(bounds.contains(surface.point_at(uv).unwrap())); - } - } - } - } - - #[test] - fn circle_samples_meet_chord_error_at_multiple_lods() { - let curve = CurveGeometry::Circle { - frame: Frame3::IDENTITY, - radius: 2.0, - }; - let range = Interval::new(0.0, std::f64::consts::TAU).unwrap(); - for epsilon in [0.1, 0.01, 0.001] { - let samples = curve.sample_elementary(range, epsilon, 100_000).unwrap(); - let n = samples.len() - 1; - let sagitta = 2.0 * (1.0 - (std::f64::consts::PI / n as f64).cos()); - assert!(sagitta <= epsilon); - assert!(norm(sub(samples[0], samples[n])) < 1e-12); - } - } - - #[test] - fn invalid_frames_tori_and_apices_fail_explicitly() { - Frame3::from_axis([0.0; 3], [0.0, 0.0, 1e-320], [1.0, 0.0, 0.0]) - .unwrap() - .validate() - .unwrap(); - let mut bad = Frame3::IDENTITY; - bad.y = [0.0, -1.0, 0.0]; - assert!(bad.validate().is_err()); - assert!(SurfaceGeometry::Torus { - frame: Frame3::IDENTITY, - major_radius: 1.0, - minor_radius: 1.0 - } - .validate() - .is_err()); - let cone = SurfaceGeometry::Cone { - frame: Frame3::IDENTITY, - semi_angle: 0.3, - }; - assert_eq!( - cone.normal_at([0.0, 0.0]), - Err(GeometryError::SingularParameterization) - ); - assert!(CurveGeometry::Circle { - frame: Frame3::IDENTITY, - radius: 1.0 - } - .sample_elementary(Interval::new(0.0, 1.0).unwrap(), 0.0, 100) - .is_err()); - } -} diff --git a/main/opengeometry/src/analytic/ifc_exchange.rs b/main/opengeometry/src/analytic/ifc_exchange.rs deleted file mode 100644 index 79f81cf..0000000 --- a/main/opengeometry/src/analytic/ifc_exchange.rs +++ /dev/null @@ -1,627 +0,0 @@ -use super::{ - exchange::preflight, - export_curve::fit_intersection_curve, - geometry::{norm, scale}, - placement::placed, - topology::{EdgeGeometry, FaceProvenance, GeometryQuality, Orientation}, - BrepEnvelope, CurveGeometry, Frame3, GeometryError, SurfaceGeometry, -}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(tag = "kind", deny_unknown_fields)] -pub enum IfcValue { - Ref { id: u32 }, - List { values: Vec }, - Real { value: f64 }, - Integer { value: i64 }, - Text { value: String }, - Enum { value: String }, - Bool { value: bool }, - Null, - Derived, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct IfcEntity { - pub id: u32, - pub entity_type: String, - pub attributes: Vec, -} -#[derive(Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct ExchangeFace { - pub face: u32, - pub key: String, - pub entity: u32, - pub provenance: FaceProvenance, -} -#[derive(Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct AnalyticExchangeBodyV2 { - pub schema_version: u32, - pub revision: String, - pub length_unit: String, - pub up_axis: String, - pub coordinate_space: String, - pub representation_type: String, - pub quality: GeometryQuality, - pub geometric_tolerance: f64, - pub exchange_error_bound: f64, - pub validation_level: String, - pub entities: Vec, - pub solids: Vec, - pub faces: Vec, -} - -struct Entities(Vec); -impl Entities { - fn add(&mut self, entity_type: &str, attributes: Vec) -> u32 { - let id = self.0.len() as u32 + 1; - self.0.push(IfcEntity { - id, - entity_type: entity_type.into(), - attributes, - }); - id - } - fn point(&mut self, p: &[f64]) -> u32 { - self.add( - "IfcCartesianPoint", - vec![IfcValue::List { - values: p - .iter() - .map(|value| IfcValue::Real { value: *value }) - .collect(), - }], - ) - } - fn direction(&mut self, p: &[f64]) -> u32 { - self.add( - "IfcDirection", - vec![IfcValue::List { - values: p - .iter() - .map(|value| IfcValue::Real { value: *value }) - .collect(), - }], - ) - } - fn placement(&mut self, frame: Frame3) -> u32 { - let origin = self.point(&frame.origin); - let z = self.direction(&frame.z); - let x = self.direction(&frame.x); - self.add( - "IfcAxis2Placement3D", - vec![reference(origin), reference(z), reference(x)], - ) - } - fn curve(&mut self, geometry: &CurveGeometry) -> Result { - Ok(match geometry { - CurveGeometry::Line { origin, direction } => { - let p = self.point(origin); - let d = self.direction(direction); - let v = self.add( - "IfcVector", - vec![ - reference(d), - IfcValue::Real { - value: norm(*direction), - }, - ], - ); - self.add("IfcLine", vec![reference(p), reference(v)]) - } - CurveGeometry::Circle { frame, radius } => { - let position = self.placement(*frame); - self.add( - "IfcCircle", - vec![reference(position), IfcValue::Real { value: *radius }], - ) - } - CurveGeometry::Ellipse { - frame, - major_radius, - minor_radius, - } => { - let position = self.placement(*frame); - self.add( - "IfcEllipse", - vec![ - reference(position), - IfcValue::Real { - value: *major_radius, - }, - IfcValue::Real { - value: *minor_radius, - }, - ], - ) - } - CurveGeometry::Intersection { .. } => { - return Err(GeometryError::UnsupportedGeometry( - "IFC numerical intersection edges require bounded export-only fitting".into(), - )) - } - }) - } - - fn fitted_curve( - &mut self, - controls: &[super::Point3], - knots: &[f64], - multiplicities: &[usize], - ) -> Result { - if controls.len() < 4 || knots.len() != multiplicities.len() { - return Err(GeometryError::InvalidGeometry( - "invalid IFC fitted cubic curve shape".into(), - )); - } - let points = controls - .iter() - .map(|point| self.point(point)) - .collect::>(); - let multiplicities = multiplicities - .iter() - .map(|value| { - i64::try_from(*value) - .map(|value| IfcValue::Integer { value }) - .map_err(|_| GeometryError::LimitExceeded("IFC knot multiplicity".into())) - }) - .collect::, _>>()?; - Ok(self.add( - "IfcBSplineCurveWithKnots", - vec![ - IfcValue::Integer { value: 3 }, - references(points), - IfcValue::Enum { - value: "UNSPECIFIED".into(), - }, - IfcValue::Bool { value: false }, - IfcValue::Bool { value: false }, - IfcValue::List { - values: multiplicities, - }, - IfcValue::List { - values: knots - .iter() - .map(|value| IfcValue::Real { value: *value }) - .collect(), - }, - IfcValue::Enum { - value: "UNSPECIFIED".into(), - }, - ], - )) - } -} -fn reference(id: u32) -> IfcValue { - IfcValue::Ref { id } -} -fn references(ids: impl IntoIterator) -> IfcValue { - IfcValue::List { - values: ids.into_iter().map(reference).collect(), - } -} -fn boolean(sense: Orientation) -> IfcValue { - IfcValue::Bool { - value: sense == Orientation::Forward, - } -} - -/// Rust prepares geometry in IFC world metres and Z-up; the receiver only assembles entity definitions. -pub fn prepare_ifc_body(input: &BrepEnvelope) -> Result { - let mut exchange_error_bound = preflight(input, 1.0)?; - let fitting_budget = input.accuracy.exchange - exchange_error_bound; - let estimated_entities = input.geometry.surfaces.len() * 20 - + input.geometry.curves.len() * 5 - + input.topology.vertices.len() * 2 - + input.topology.edges.len() - + input.topology.halfedges.len() - + input.topology.loops.len() * 2 - + input.topology.faces.len() - + input.topology.shells.len() - + input.solids.len() * 2 - + input - .solids - .iter() - .map(|s| s.cavity_shells.len() * 2) - .sum::(); - if estimated_entities > 100_000 { - return Err(GeometryError::LimitExceeded( - "IFC prepared entity limit exceeded".into(), - )); - } - let conversion = Frame3 { - origin: [0.0; 3], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, 1.0], - z: [0.0, -1.0, 0.0], - }; - let mut brep = placed(input, conversion, 1.0)?; - let has_cavities = brep - .solids - .iter() - .any(|solid| !solid.cavity_shells.is_empty()); - // IFC4's published VoidsHaveAdvancedFaces rule has an inverted inner test; emit valid CSG operands instead. - if has_cavities { - for shell in brep.solids.iter().flat_map(|solid| &solid.cavity_shells) { - for face in &brep.topology.shells[*shell as usize].faces { - let face_record = &mut brep.topology.faces[*face as usize]; - face_record.sense = if face_record.sense == Orientation::Forward { - Orientation::Reverse - } else { - Orientation::Forward - }; - for use_ in brep - .topology - .halfedges - .iter_mut() - .filter(|use_| use_.face == Some(*face)) - { - std::mem::swap(&mut use_.from, &mut use_.to); - std::mem::swap(&mut use_.next, &mut use_.prev); - use_.geometry_use.sense = if use_.geometry_use.sense == Orientation::Forward { - Orientation::Reverse - } else { - Orientation::Forward - }; - } - } - } - for vertex in &mut brep.topology.vertices { - vertex.outgoing_halfedge = None; - } - for use_ in &brep.topology.halfedges { - brep.topology.vertices[use_.from as usize] - .outgoing_halfedge - .get_or_insert(use_.id); - } - brep.validate()?; - } - let mut entities = Entities(Vec::new()); - let mut surfaces = vec![None; brep.geometry.surfaces.len()]; - for face in &brep.topology.faces { - if surfaces[face.surface as usize].is_some() { - continue; - } - let surface = brep.geometry.surface(face.surface)?; - let position = entities.placement(*surface.frame()); - let id = match surface { - SurfaceGeometry::Plane { .. } => entities.add("IfcPlane", vec![reference(position)]), - SurfaceGeometry::Sphere { radius, .. } => entities.add( - "IfcSphericalSurface", - vec![reference(position), IfcValue::Real { value: *radius }], - ), - SurfaceGeometry::Cylinder { radius, .. } => entities.add( - "IfcCylindricalSurface", - vec![reference(position), IfcValue::Real { value: *radius }], - ), - SurfaceGeometry::Torus { - major_radius, - minor_radius, - .. - } => entities.add( - "IfcToroidalSurface", - vec![ - reference(position), - IfcValue::Real { - value: *major_radius, - }, - IfcValue::Real { - value: *minor_radius, - }, - ], - ), - SurfaceGeometry::Cone { frame, semi_angle } => { - let height = brep - .topology - .faces - .iter() - .filter(|f| f.surface == face.surface) - .map(|f| f.trim.uv_bounds[1].hi) - .fold(0.0_f64, f64::max); - if !height.is_finite() || height <= 0.0 { - return Err(GeometryError::InvalidGeometry( - "IFC cone profile requires a positive finite height".into(), - )); - } - let p0 = entities.point(&[0.0, 0.0]); - let p1 = entities.point(&[height * semi_angle.tan(), height]); - let line = entities.add("IfcPolyline", vec![references([p0, p1])]); - let profile = entities.add( - "IfcArbitraryOpenProfileDef", - vec![ - IfcValue::Enum { - value: "CURVE".into(), - }, - IfcValue::Null, - reference(line), - ], - ); - let profile_frame = Frame3 { - y: frame.z, - z: scale(frame.y, -1.0), - ..*frame - }; - let profile_position = entities.placement(profile_frame); - let origin = entities.point(&[0.0; 3]); - let axis = entities.direction(&[0.0, 1.0, 0.0]); - let axis = entities.add( - "IfcAxis1Placement", - vec![reference(origin), reference(axis)], - ); - entities.add( - "IfcSurfaceOfRevolution", - vec![ - reference(profile), - reference(profile_position), - reference(axis), - ], - ) - } - }; - surfaces[face.surface as usize] = Some(id); - } - let vertices: Vec<_> = brep - .topology - .vertices - .iter() - .map(|v| { - let p = entities.point(&v.position); - entities.add("IfcVertexPoint", vec![reference(p)]) - }) - .collect(); - let mut edges = vec![None; brep.topology.edges.len()]; - let mut curves = vec![None; brep.geometry.curves.len()]; - let mut fitting_error: f64 = 0.0; - for edge in &brep.topology.edges { - let EdgeGeometry::Curve { curve, range } = edge.geometry else { - continue; - }; - let geometry = &brep.geometry.curves[curve as usize]; - if matches!( - geometry, - CurveGeometry::Circle { .. } | CurveGeometry::Ellipse { .. } - ) && range.width() > std::f64::consts::TAU - { - return Err(GeometryError::UnsupportedGeometry( - "IFC conic edges cannot exceed one period".into(), - )); - } - let curve_id = match curves[curve as usize] { - Some(id) => id, - None => { - let id = match geometry { - CurveGeometry::Intersection { definition } => { - if fitting_budget <= 0.0 { - return Err(GeometryError::LimitExceeded( - "IFC exchange budget leaves no room for numerical curve fitting" - .into(), - )); - } - let fitted = fit_intersection_curve( - &brep.geometry, - *definition, - fitting_budget, - 20_000, - )?; - fitting_error = fitting_error.max(fitted.error_bound); - entities.fitted_curve( - &fitted.controls, - &fitted.knots, - &fitted.multiplicities, - )? - } - _ => entities.curve(geometry)?, - }; - curves[curve as usize] = Some(id); - id - } - }; - let use_ = &brep.topology.halfedges[edge.halfedge as usize]; - let (from, to) = if use_.geometry_use.sense == Orientation::Forward { - (use_.from, use_.to) - } else { - (use_.to, use_.from) - }; - edges[edge.id as usize] = Some(entities.add( - "IfcEdgeCurve", - vec![ - reference(vertices[from as usize]), - reference(vertices[to as usize]), - reference(curve_id), - IfcValue::Bool { value: true }, - ], - )); - } - exchange_error_bound += fitting_error; - if exchange_error_bound > input.accuracy.exchange { - return Err(GeometryError::LimitExceeded( - "IFC fitted curves exceed the exchange error budget".into(), - )); - } - let mut faces = Vec::new(); - for face in &brep.topology.faces { - let mut bounds = Vec::new(); - for loop_id in std::iter::once(&face.trim.outer).chain(&face.trim.holes) { - let loop_ = &brep.topology.loops[*loop_id as usize]; - let start = loop_.start_halfedge; - let mut current = start; - let mut uses = Vec::new(); - for _ in 0..=brep.topology.halfedges.len() { - let use_ = &brep.topology.halfedges[current as usize]; - if let Some(edge) = edges[use_.edge as usize] { - uses.push(entities.add( - "IfcOrientedEdge", - vec![ - IfcValue::Derived, - IfcValue::Derived, - reference(edge), - boolean(use_.geometry_use.sense), - ], - )); - } - current = use_ - .next - .ok_or_else(|| GeometryError::InvalidTopology("open IFC loop".into()))?; - if current == start { - break; - } - } - let loop_entity = if uses.is_empty() { - entities.add( - "IfcVertexLoop", - vec![reference( - vertices[brep.topology.halfedges[start as usize].from as usize], - )], - ) - } else { - entities.add("IfcEdgeLoop", vec![references(uses)]) - }; - bounds.push(entities.add( - if loop_.is_hole { - "IfcFaceBound" - } else { - "IfcFaceOuterBound" - }, - vec![reference(loop_entity), IfcValue::Bool { value: true }], - )); - } - let surface = surfaces[face.surface as usize] - .ok_or_else(|| GeometryError::InvalidTopology("missing prepared IFC surface".into()))?; - faces.push(entities.add( - "IfcAdvancedFace", - vec![references(bounds), reference(surface), boolean(face.sense)], - )); - } - let shells: Vec<_> = brep - .topology - .shells - .iter() - .map(|s| { - entities.add( - "IfcClosedShell", - vec![references(s.faces.iter().map(|id| faces[*id as usize]))], - ) - }) - .collect(); - let mut solids: Vec<_> = brep - .solids - .iter() - .map(|solid| { - let mut root = entities.add( - "IfcAdvancedBrep", - vec![reference(shells[solid.outer_shell as usize])], - ); - for shell in &solid.cavity_shells { - let cutter = - entities.add("IfcAdvancedBrep", vec![reference(shells[*shell as usize])]); - root = entities.add( - "IfcBooleanResult", - vec![ - IfcValue::Enum { - value: "DIFFERENCE".into(), - }, - reference(root), - reference(cutter), - ], - ); - } - root - }) - .collect(); - if has_cavities && solids.len() > 1 { - let mut root = solids[0]; - for id in &solids[1..] { - root = entities.add( - "IfcBooleanResult", - vec![ - IfcValue::Enum { - value: "UNION".into(), - }, - reference(root), - reference(*id), - ], - ); - } - solids = vec![root]; - } - let faces = brep - .topology - .faces - .iter() - .map(|face| ExchangeFace { - face: face.id, - key: face.key.clone(), - entity: faces[face.id as usize], - provenance: face.provenance.clone(), - }) - .collect(); - Ok(AnalyticExchangeBodyV2 { - schema_version: 2, revision: input.revision.to_string(), length_unit: "metre".into(), up_axis: "Z".into(), coordinate_space: "world".into(), - representation_type: if has_cavities { "CSG" } else { "AdvancedBrep" }.into(), - quality: input.quality.clone(), geometric_tolerance: input.accuracy.geometric, exchange_error_bound, - validation_level: "v2 structure, sampled residuals and bounded analytic entity preparation; receiver schema validation required".into(), - entities: entities.0, solids, faces, - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::{primitives, topology::Accuracy}; - #[test] - fn prepared_ifc_has_shared_geometry_backreferences_and_single_axis_conversion() { - let accuracy = Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-6, - }; - let frame = Frame3 { - origin: [1.0, 2.0, 3.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - ..Frame3::IDENTITY - }; - let bodies = [ - primitives::cylinder("c".into(), frame, 1.0, 2.0, accuracy).unwrap(), - primitives::sphere("s".into(), frame, 1.0, accuracy).unwrap(), - primitives::cone("cone".into(), frame, 1.0, 2.0, accuracy).unwrap(), - primitives::torus("t".into(), frame, 2.0, 0.5, accuracy).unwrap(), - ]; - fn validate(value: &IfcValue, before: u32) { - match value { - IfcValue::Ref { id } => assert!(*id > 0 && *id < before), - IfcValue::List { values } => { - for value in values { - validate(value, before); - } - } - _ => {} - } - } - for input in bodies { - let original = input.to_json().unwrap(); - let output = prepare_ifc_body(&input).unwrap(); - assert_eq!(output.faces.len(), input.topology.faces.len()); - assert_eq!(output.solids.len(), 1); - assert_eq!(output.up_axis, "Z"); - assert_eq!(output.length_unit, "metre"); - for entity in &output.entities { - for value in &entity.attributes { - validate(value, entity.id); - } - } - assert!(output.entities.iter().any(|e| e.entity_type == "IfcCartesianPoint" && matches!(&e.attributes[0], IfcValue::List { values } if matches!(values.as_slice(), [IfcValue::Real { value: 1.0 }, IfcValue::Real { value: -3.0 }, IfcValue::Real { value: 2.0 }])))); - assert!(!output - .entities - .iter() - .any(|e| e.entity_type == "IfcSurfaceCurve")); - assert_eq!(input.to_json().unwrap(), original); - let serialized = serde_json::to_string(&output).unwrap(); - serde_json::from_str::(&serialized).unwrap(); - } - } -} diff --git a/main/opengeometry/src/analytic/intersection.rs b/main/opengeometry/src/analytic/intersection.rs deleted file mode 100644 index 80f47f5..0000000 --- a/main/opengeometry/src/analytic/intersection.rs +++ /dev/null @@ -1,616 +0,0 @@ -use super::{ - geometry::{ - add, cross, dot, norm, scale, sub, unit, PatchBounds, Surface, SurfaceGeometry, UVBox, - }, - topology::GeometryStore, - GeometryError, Point3, UV, -}; -use crate::math::{finite, interval::Interval, solve::solve}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct SsiBudget { - pub max_patch_pairs: usize, - pub max_steps_per_branch: usize, - pub max_newton_iterations: usize, - pub max_subdivision_depth: usize, -} -impl Default for SsiBudget { - fn default() -> Self { - Self { - max_patch_pairs: 200_000, - max_steps_per_branch: 50_000, - max_newton_iterations: 12, - max_subdivision_depth: 48, - } - } -} -impl SsiBudget { - pub fn validate(self) -> Result<(), GeometryError> { - if self.max_patch_pairs == 0 - || self.max_steps_per_branch == 0 - || self.max_newton_iterations == 0 - || self.max_newton_iterations > 64 - || self.max_subdivision_depth == 0 - || self.max_subdivision_depth > 128 - { - return Err(GeometryError::InvalidGeometry( - "invalid SSI resource limits".into(), - )); - } - Ok(()) - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct TraceAnchor { - pub parameter: f64, - pub point: Point3, - pub uv_a: UV, - pub uv_b: UV, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct IntersectionDefinition { - pub surfaces: [u32; 2], - pub anchors: Vec, - pub uv_tubes: Vec<[Interval; 4]>, - pub residual_tolerance: f64, -} -#[derive(Clone, Copy, Debug)] -pub struct IntersectionPoint { - pub point: Point3, - pub uv_a: UV, - pub uv_b: UV, - pub tangent: Point3, - pub support_error: f64, -} - -fn intersect_interval(a: Interval, b: Interval) -> Option { - Interval::new(a.lo.max(b.lo), a.hi.min(b.hi)).ok() -} - -fn interval_dot( - bounds: PatchBounds, - origin: Point3, - axis: Point3, -) -> Result { - let mut value = Interval::point(0.0)?; - for coordinate in 0..3 { - value = value.add( - bounds.axes[coordinate] - .sub(Interval::point(origin[coordinate])?)? - .mul(Interval::point(axis[coordinate])?)?, - )?; - } - Ok(value) -} - -fn signed_root(radial: Interval, sign: Interval) -> Result, GeometryError> { - if radial.hi < 0.0 { - return Ok(None); - } - let root = Interval::new(radial.lo.max(0.0), radial.hi)?.sqrt()?; - Ok(Some(if sign.lo >= 0.0 { - root - } else if sign.hi <= 0.0 { - Interval::new(-root.hi, -root.lo)? - } else { - Interval::new(-root.hi, root.hi)? - })) -} - -fn perpendicular_cylinder_enclosure( - definition: &IntersectionDefinition, - store: &GeometryStore, - segment: usize, - range: Interval, - parent: PatchBounds, -) -> Result, GeometryError> { - let ( - SurfaceGeometry::Cylinder { - frame: host, - radius: host_radius, - }, - SurfaceGeometry::Cylinder { - frame: cutter, - radius: cutter_radius, - }, - ) = ( - store.surface(definition.surfaces[0])?, - store.surface(definition.surfaces[1])?, - ) - else { - return Ok(None); - }; - let u_axis = cutter.z; - let v_axis = host.z; - if dot(u_axis, v_axis).abs() > 1e-12 { - return Ok(None); - } - let w_axis = cross(u_axis, v_axis); - let host_to_cutter = sub(cutter.origin, host.origin); - let centre = add(host.origin, scale(v_axis, dot(host_to_cutter, v_axis))); - let residual = sub( - sub(centre, cutter.origin), - scale(u_axis, dot(sub(centre, cutter.origin), u_axis)), - ); - if norm(residual) > 1e-12 { - return Ok(None); - } - - let first = &definition.anchors[segment]; - let last = &definition.anchors[segment + 1]; - let guide_axis = unit(sub(last.point, first.point))?; - let certificate = definition.residual_tolerance * 8.0 - + 128.0 * f64::EPSILON * host_radius.max(*cutter_radius).max(norm(centre)).max(1.0); - let theta_parent = definition.uv_tubes[segment][2]; - if theta_parent.width() >= std::f64::consts::PI { - return Ok(None); - } - let u_parent = interval_dot(parent, centre, u_axis)?; - if u_parent.contains(0.0) { - return Ok(None); - } - let host_squared = Interval::new( - (host_radius - certificate).max(0.0).powi(2), - (host_radius + certificate).powi(2), - )?; - let v_cos = dot(cutter.x, v_axis); - let v_sin = dot(cutter.y, v_axis); - let w_cos = dot(cutter.x, w_axis); - let w_sin = dot(cutter.y, w_axis); - let trig = |cosine: Interval, - sine: Interval, - along_cosine: f64, - along_sine: f64| - -> Result { - Ok(cosine - .mul(Interval::point(along_cosine)?)? - .add(sine.mul(Interval::point(along_sine)?)?)? - .mul(Interval::point(*cutter_radius)?)?) - }; - let parent_cosine = theta_parent.cos()?; - let parent_sine = theta_parent.sin()?; - let parent_w = trig(parent_cosine, parent_sine, w_cos, w_sin)?; - let parent_dw = trig(parent_sine, parent_cosine, -w_cos, w_sin)?; - let parent_dv = trig(parent_sine, parent_cosine, -v_cos, v_sin)?; - let Some(parent_u) = signed_root(host_squared.sub(parent_w.square()?)?, u_parent)? else { - return Ok(None); - }; - if parent_u.contains(0.0) { - return Ok(None); - } - let parent_du = Interval::point(-1.0)? - .mul(parent_w)? - .mul(parent_dw)? - .div(parent_u)?; - let derivative = parent_du - .mul(Interval::point(dot(guide_axis, u_axis))?)? - .add(parent_dv.mul(Interval::point(dot(guide_axis, v_axis))?)?)? - .add(parent_dw.mul(Interval::point(dot(guide_axis, w_axis))?)?)?; - if derivative.contains(0.0) { - return Ok(None); - } - - let lo = definition.evaluate(range.lo, store)?.uv_b[0]; - let hi = definition.evaluate(range.hi, store)?.uv_b[0]; - let theta_pad = certificate / cutter_radius + 64.0 * f64::EPSILON; - let theta = Interval::new(lo.min(hi) - theta_pad, lo.max(hi) + theta_pad)?; - if theta.lo < theta_parent.lo - theta_pad || theta.hi > theta_parent.hi + theta_pad { - return Ok(None); - } - let cosine = theta.cos()?; - let sine = theta.sin()?; - let v = trig(cosine, sine, v_cos, v_sin)?; - let w = trig(cosine, sine, w_cos, w_sin)?; - let Some(u) = signed_root(host_squared.sub(w.square()?)?, u_parent)? else { - return Ok(None); - }; - let local = [u, v, w]; - let axes = [u_axis, v_axis, w_axis]; - - let mut world = [Interval::point(0.0)?; 3]; - for coordinate in 0..3 { - let mut value = Interval::point(centre[coordinate])?; - for axis in 0..3 { - value = value.add(local[axis].mul(Interval::point(axes[axis][coordinate])?)?)?; - } - world[coordinate] = value.add(Interval::new(-certificate, certificate)?)?; - } - let minimum_u = ((host_radius - certificate).powi(2) - (cutter_radius + certificate).powi(2)) - .max(0.0) - .sqrt(); - if minimum_u <= certificate { - return Ok(None); - } - let radius = cutter_radius + certificate; - let axial_curvature = radius.powi(2) / minimum_u + radius.powi(4) / minimum_u.powi(3); - let host_angle_curvature = radius / minimum_u + radius.powi(3) / minimum_u.powi(3); - // The exported XYZ chord and both surface pcurves use the same linear - // parameter. Bound their support-space deviations over that parameter. - let host_support_curvature = - host_radius * (host_angle_curvature + (radius / minimum_u).powi(2)); - let curvature = (radius + axial_curvature).max(host_support_curvature); - let chord_error = curvature * theta.width().powi(2) / 8.0 + certificate * 4.0; - Ok(Some((PatchBounds { axes: world }, chord_error))) -} - -impl IntersectionDefinition { - fn shape(&self) -> Result<(), GeometryError> { - if self.anchors.len() < 2 - || self.uv_tubes.len() + 1 != self.anchors.len() - || !self.residual_tolerance.is_finite() - || self.residual_tolerance <= 0.0 - { - return Err(GeometryError::InvalidGeometry( - "invalid intersection branch shape or tolerance".into(), - )); - } - Ok(()) - } - - pub fn validate(&self, store: &GeometryStore) -> Result<(), GeometryError> { - self.shape()?; - let a = store.surface(self.surfaces[0])?; - let b = store.surface(self.surfaces[1])?; - for (i, anchor) in self.anchors.iter().enumerate() { - finite(anchor.parameter)?; - for v in anchor.point { - finite(v)?; - } - if i > 0 && anchor.parameter <= self.anchors[i - 1].parameter { - return Err(GeometryError::InvalidGeometry( - "intersection parameters are not increasing".into(), - )); - } - if norm(sub(a.point_at(anchor.uv_a)?, anchor.point)) > self.residual_tolerance - || norm(sub(b.point_at(anchor.uv_b)?, anchor.point)) > self.residual_tolerance - { - return Err(GeometryError::InvalidGeometry( - "intersection anchor misses its supports".into(), - )); - } - } - for (i, tube) in self.uv_tubes.iter().enumerate() { - for d in tube { - Interval::new(d.lo, d.hi)?; - } - for anchor in [&self.anchors[i], &self.anchors[i + 1]] { - let uv = [ - anchor.uv_a[0], - anchor.uv_a[1], - anchor.uv_b[0], - anchor.uv_b[1], - ]; - if (0..4).any(|j| !tube[j].contains(uv[j])) { - return Err(GeometryError::InvalidGeometry( - "intersection anchor leaves its UV tube".into(), - )); - } - } - if norm(sub(self.anchors[i + 1].point, self.anchors[i].point)) == 0.0 { - return Err(GeometryError::InvalidGeometry( - "zero-length tracing guide".into(), - )); - } - } - Ok(()) - } - - pub fn evaluate( - &self, - t: f64, - store: &GeometryStore, - ) -> Result { - self.evaluate_with_budget(t, store, SsiBudget::default()) - } - - pub fn evaluate_with_budget( - &self, - t: f64, - store: &GeometryStore, - budget: SsiBudget, - ) -> Result { - self.shape()?; - budget.validate()?; - finite(t)?; - let first = &self.anchors[0]; - let last = &self.anchors[self.anchors.len() - 1]; - if t < first.parameter || t > last.parameter { - return Err(GeometryError::InvalidGeometry( - "parameter outside intersection branch".into(), - )); - } - let index = self - .anchors - .partition_point(|a| a.parameter <= t) - .saturating_sub(1) - .min(self.anchors.len() - 2); - let left = &self.anchors[index]; - let right = &self.anchors[index + 1]; - let span = finite(right.parameter - left.parameter)?; - if span <= 0.0 { - return Err(GeometryError::InvalidGeometry( - "invalid trace parameter interval".into(), - )); - } - let w = (t - left.parameter) / span; - let guide = add(scale(left.point, 1.0 - w), scale(right.point, w)); - let velocity = scale(sub(right.point, left.point), 1.0 / span); - let gauge = unit(velocity)?; - let tube = self.uv_tubes[index]; - for d in tube { - Interval::new(d.lo, d.hi)?; - } - let a = store.surface(self.surfaces[0])?; - let b = store.surface(self.surfaces[1])?; - let mut uv = [ - left.uv_a[0] * (1.0 - w) + right.uv_a[0] * w, - left.uv_a[1] * (1.0 - w) + right.uv_a[1] * w, - left.uv_b[0] * (1.0 - w) + right.uv_b[0] * w, - left.uv_b[1] * (1.0 - w) + right.uv_b[1] * w, - ]; - if (0..4).any(|i| !tube[i].contains(uv[i])) { - return Err(GeometryError::InvalidGeometry( - "trace predictor leaves its UV tube".into(), - )); - } - for _ in 0..budget.max_newton_iterations { - let ja = a.derivatives([uv[0], uv[1]])?; - let jb = b.derivatives([uv[2], uv[3]])?; - let diff = sub(ja.point, jb.point); - let residual = [diff[0], diff[1], diff[2], dot(sub(ja.point, guide), gauge)]; - let error = residual.into_iter().map(f64::abs).fold(0.0, f64::max); - if norm(diff) <= self.residual_tolerance && residual[3].abs() <= self.residual_tolerance - { - let direction = unit(cross( - a.normal_at([uv[0], uv[1]])?, - b.normal_at([uv[2], uv[3]])?, - ))?; - let projection = dot(direction, gauge); - if projection.abs() <= 64.0 * f64::EPSILON { - return Err(GeometryError::UnresolvedIntersection( - "trace guide is tangent to correction plane".into(), - )); - } - return Ok(IntersectionPoint { - point: ja.point, - uv_a: [uv[0], uv[1]], - uv_b: [uv[2], uv[3]], - tangent: scale(direction, dot(velocity, gauge) / projection), - support_error: norm(diff), - }); - } - let mut jacobian = [[0.0; 4]; 4]; - for i in 0..3 { - jacobian[i] = [ja.du[i], ja.dv[i], -jb.du[i], -jb.dv[i]]; - } - jacobian[3] = [dot(ja.du, gauge), dot(ja.dv, gauge), 0.0, 0.0]; - let step = solve(jacobian, residual.map(|v| -v)) - .map_err(|e| { - GeometryError::UnresolvedIntersection(format!("intersection correction: {e}")) - })? - .value; - let mut accepted = false; - let mut fraction = 1.0; - for _ in 0..12 { - let candidate = std::array::from_fn(|i| uv[i] + fraction * step[i]); - if (0..4).all(|i| tube[i].contains(candidate[i])) - && a.parameter_in_domain([candidate[0], candidate[1]]) - && b.parameter_in_domain([candidate[2], candidate[3]]) - { - let pa = a.point_at([candidate[0], candidate[1]])?; - let pb = b.point_at([candidate[2], candidate[3]])?; - let diff = sub(pa, pb); - let trial = diff - .into_iter() - .map(f64::abs) - .fold(dot(sub(pa, guide), gauge).abs(), f64::max); - if trial < error { - uv = candidate; - accepted = true; - break; - } - } - fraction *= 0.5; - } - if !accepted { - return Err(GeometryError::UnresolvedIntersection( - "correction leaves branch tube or does not decrease residual".into(), - )); - } - } - Err(GeometryError::UnresolvedIntersection( - "intersection correction budget exhausted".into(), - )) - } - - pub fn enclose( - &self, - range: Interval, - store: &GeometryStore, - ) -> Result { - self.shape()?; - Interval::new(range.lo, range.hi)?; - if range.lo < self.anchors[0].parameter - || range.hi > self.anchors[self.anchors.len() - 1].parameter - { - return Err(GeometryError::InvalidGeometry( - "bounds outside intersection branch".into(), - )); - } - let a = store.surface(self.surfaces[0])?; - let b = store.surface(self.surfaces[1])?; - let mut result: Option = None; - for (i, tube) in self.uv_tubes.iter().enumerate() { - if self.anchors[i + 1].parameter < range.lo || self.anchors[i].parameter > range.hi { - continue; - } - let box_a: UVBox = [tube[0], tube[1]]; - let box_b: UVBox = [tube[2], tube[3]]; - let ba = a.enclose(box_a)?; - let bb = b.enclose(box_b)?; - let mut axes = [Interval::point(0.0)?; 3]; - for j in 0..3 { - axes[j] = Interval::new( - ba.axes[j].lo.max(bb.axes[j].lo), - ba.axes[j].hi.min(bb.axes[j].hi), - ) - .map_err(|_| { - GeometryError::InvalidGeometry("disjoint trace support boxes".into()) - })?; - } - if let Some((tighter, _)) = perpendicular_cylinder_enclosure( - self, - store, - i, - Interval::new( - range.lo.max(self.anchors[i].parameter), - range.hi.min(self.anchors[i + 1].parameter), - )?, - PatchBounds { axes }, - )? { - for axis in 0..3 { - axes[axis] = - intersect_interval(axes[axis], tighter.axes[axis]).unwrap_or(axes[axis]); - } - } - result = Some(match result { - None => PatchBounds { axes }, - Some(previous) => PatchBounds { - axes: std::array::from_fn(|j| previous.axes[j].hull(axes[j])), - }, - }); - } - result.ok_or_else(|| GeometryError::InvalidGeometry("empty trace interval".into())) - } - - pub fn certified_chord_deviation( - &self, - range: Interval, - store: &GeometryStore, - ) -> Result, GeometryError> { - let Some(segment) = self - .anchors - .windows(2) - .position(|pair| range.lo >= pair[0].parameter && range.hi <= pair[1].parameter) - else { - return Ok(None); - }; - let a = store.surface(self.surfaces[0])?; - let b = store.surface(self.surfaces[1])?; - let tube = self.uv_tubes[segment]; - let box_a = a.enclose([tube[0], tube[1]])?; - let box_b = b.enclose([tube[2], tube[3]])?; - let mut axes = [Interval::point(0.0)?; 3]; - for coordinate in 0..3 { - let Some(overlap) = intersect_interval(box_a.axes[coordinate], box_b.axes[coordinate]) - else { - return Ok(None); - }; - axes[coordinate] = overlap; - } - Ok( - perpendicular_cylinder_enclosure(self, store, segment, range, PatchBounds { axes })? - .map(|(_, error)| error), - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::{Curve, CurveGeometry, Frame3, SurfaceGeometry}; - fn circle_trace() -> GeometryStore { - let mut store = GeometryStore::new(); - store.surfaces.push(SurfaceGeometry::Sphere { - frame: Frame3::IDENTITY, - radius: 1.0, - }); - store.surfaces.push(SurfaceGeometry::Plane { - frame: Frame3::IDENTITY, - }); - let mut anchors = Vec::new(); - let mut uv_tubes = Vec::new(); - for i in 0..=8 { - let u = i as f64 * 0.1; - anchors.push(TraceAnchor { - parameter: u, - point: [u.cos(), u.sin(), 0.0], - uv_a: [u, 0.0], - uv_b: [u.cos(), u.sin()], - }); - if i > 0 { - let left = (i - 1) as f64 * 0.1; - uv_tubes.push([ - Interval::new(left - 0.02, u + 0.02).unwrap(), - Interval::new(-0.02, 0.02).unwrap(), - Interval::new( - left.cos().min(u.cos()) - 0.02, - left.cos().max(u.cos()) + 0.02, - ) - .unwrap(), - Interval::new( - left.sin().min(u.sin()) - 0.02, - left.sin().max(u.sin()) + 0.02, - ) - .unwrap(), - ]); - } - } - store.intersections.push(IntersectionDefinition { - surfaces: [0, 1], - anchors, - uv_tubes, - residual_tolerance: 1e-12, - }); - store - .curves - .push(CurveGeometry::Intersection { definition: 0 }); - store - } - #[test] - fn intersection_evaluation_corrects_off_linear_predictor() { - let store = circle_trace(); - store.intersections[0].validate(&store).unwrap(); - let point = store.intersections[0].evaluate(0.35, &store).unwrap(); - assert!((norm(point.point) - 1.0).abs() < 1e-12); - assert!(point.point[2].abs() < 1e-12); - assert!(point.support_error < 1e-12); - let chord = scale( - add( - store.intersections[0].anchors[3].point, - store.intersections[0].anchors[4].point, - ), - 0.5, - ); - assert!(norm(sub(point.point, chord)) > 0.001); - assert!(dot(point.point, point.tangent).abs() < 1e-12); - assert!(store - .curve(0) - .unwrap() - .enclose(Interval::new(0.3, 0.4).unwrap()) - .unwrap() - .contains(point.point)); - } - #[test] - fn budget_failure_and_invalid_anchor_are_explicit() { - let mut store = circle_trace(); - assert!(matches!( - store.intersections[0].evaluate_with_budget( - 0.35, - &store, - SsiBudget { - max_newton_iterations: 1, - ..SsiBudget::default() - } - ), - Err(GeometryError::UnresolvedIntersection(_)) - )); - store.intersections[0].anchors[3].point[2] = 0.1; - assert!(store.intersections[0].validate(&store).is_err()); - } -} diff --git a/main/opengeometry/src/analytic/mod.rs b/main/opengeometry/src/analytic/mod.rs deleted file mode 100644 index 3a7c2b8..0000000 --- a/main/opengeometry/src/analytic/mod.rs +++ /dev/null @@ -1,57 +0,0 @@ -pub mod booleans; -pub mod box_booleans; -mod curved_layered_boolean; -pub mod diagnostics; -pub mod exchange; -pub mod export_curve; -pub mod face_intersection; -pub mod geometry; -pub mod ifc_exchange; -pub mod intersection; -pub mod modeling; -pub mod placement; -mod planar_booleans; -pub mod primitives; -pub mod query; -pub mod ssi; -pub mod tessellation; -pub mod topology; -pub mod universal_ssi; -pub mod wasm; - -use crate::math::MathError; -use serde::{Deserialize, Serialize}; -use std::fmt; - -pub use geometry::{Curve, CurveGeometry, Frame3, Point3, Surface, SurfaceGeometry, UV}; -pub use topology::{BrepEnvelope, GeometryStore}; - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum GeometryError { - Math(MathError), - InvalidGeometry(String), - InvalidTopology(String), - UnsupportedGeometry(String), - AmbiguousProfileAlignment(String), - CoverageGap { families: [String; 2] }, - SingularParameterization, - MissingReference { kind: String, index: u32 }, - UnsupportedSchema { found: u32 }, - UnresolvedIntersection(String), - UnresolvedTessellation(String), - LimitExceeded(String), -} - -impl From for GeometryError { - fn from(error: MathError) -> Self { - Self::Math(error) - } -} - -impl fmt::Display for GeometryError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self:?}") - } -} - -impl std::error::Error for GeometryError {} diff --git a/main/opengeometry/src/analytic/modeling.rs b/main/opengeometry/src/analytic/modeling.rs deleted file mode 100644 index b68ee54..0000000 --- a/main/opengeometry/src/analytic/modeling.rs +++ /dev/null @@ -1,771 +0,0 @@ -use super::{ - geometry::{cross, dot, norm, scale, sub, unit}, - primitives::{self, boundary, uv_line, Builder}, - topology::{Accuracy, BrepEnvelope, Orientation, PcurveGeometry}, - CurveGeometry, Frame3, GeometryError, Point3, SurfaceGeometry, -}; -use crate::math::interval::Interval; - -#[derive(Clone, Copy, Debug)] -pub enum PolygonLoftAlignment { - Auto, - Indexed { - upper_start: usize, - reverse_upper: bool, - }, -} - -fn polygon_normal(points: &[Point3]) -> Result { - let mut normal = [0.0; 3]; - for (a, b) in points.iter().zip(points.iter().cycle().skip(1)) { - normal[0] += (a[1] - b[1]) * (a[2] + b[2]); - normal[1] += (a[2] - b[2]) * (a[0] + b[0]); - normal[2] += (a[0] - b[0]) * (a[1] + b[1]); - } - unit(normal).map_err(|_| GeometryError::InvalidGeometry("loft profile has zero area".into())) -} - -fn polygon_centroid(points: &[Point3]) -> Point3 { - let count = points.len() as f64; - std::array::from_fn(|axis| points.iter().map(|point| point[axis]).sum::() / count) -} - -fn validate_loft_profile( - points: &[Point3], - accuracy: Accuracy, - label: &str, -) -> Result { - use crate::geometry::poly2d::{self_intersects2, Pt2}; - - if points.len() < 3 { - return Err(GeometryError::InvalidGeometry(format!( - "{label} loft profile requires at least three vertices" - ))); - } - if points - .iter() - .flatten() - .any(|coordinate| !coordinate.is_finite()) - { - return Err(GeometryError::InvalidGeometry(format!( - "{label} loft profile contains a non-finite coordinate" - ))); - } - let resolution = 4.0 * accuracy.geometric; - if points - .iter() - .zip(points.iter().cycle().skip(1)) - .any(|(a, b)| norm(sub(*b, *a)) <= resolution) - { - return Err(GeometryError::UnresolvedIntersection(format!( - "{label} loft profile contains an edge below geometric resolution" - ))); - } - let normal = polygon_normal(points)?; - let origin = points[0]; - let reference = sub(points[1], origin); - let frame = Frame3::from_axis(origin, normal, reference)?; - let mut projected = Vec::with_capacity(points.len()); - for point in points { - let local = frame.local(*point); - if local[2].abs() > resolution { - return Err(GeometryError::InvalidGeometry(format!( - "{label} loft profile is not planar" - ))); - } - projected.push(Pt2::new(local[0], local[1])); - } - if self_intersects2(&projected, accuracy.geometric) { - return Err(GeometryError::InvalidGeometry(format!( - "{label} loft profile self-intersects" - ))); - } - Ok(normal) -} - -fn rotate_profile(points: &[Point3], start: usize, reverse: bool) -> Vec { - let count = points.len(); - (0..count) - .map(|offset| { - let index = if reverse { - (start + count - (offset % count)) % count - } else { - (start + offset) % count - }; - points[index] - }) - .collect() -} - -fn connector_score(lower: &[Point3], upper: &[Point3]) -> f64 { - lower - .iter() - .zip(upper) - .map(|(a, b)| { - let delta = sub(*b, *a); - dot(delta, delta) - }) - .sum() -} - -fn align_loft_profiles( - lower: Vec, - upper: Vec, - alignment: PolygonLoftAlignment, - accuracy: Accuracy, -) -> Result<(Vec, Vec), GeometryError> { - let separation = sub(polygon_centroid(&upper), polygon_centroid(&lower)); - if norm(separation) <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "loft profile planes are not separated".into(), - )); - } - let lower_normal = polygon_normal(&lower)?; - let mut lower = lower; - if dot(lower_normal, separation) < 0.0 { - lower.reverse(); - } - - let upper = match alignment { - PolygonLoftAlignment::Indexed { - upper_start, - reverse_upper, - } => { - if upper_start >= upper.len() { - return Err(GeometryError::InvalidGeometry( - "loft upper_start is outside the upper profile".into(), - )); - } - rotate_profile(&upper, upper_start, reverse_upper) - } - PolygonLoftAlignment::Auto => { - let mut candidates = Vec::with_capacity(upper.len() * 2); - for reverse in [false, true] { - for start in 0..upper.len() { - let candidate = rotate_profile(&upper, start, reverse); - let candidate_normal = polygon_normal(&candidate)?; - if dot(candidate_normal, separation) <= 0.0 { - continue; - } - candidates.push(( - connector_score(&lower, &candidate), - start, - reverse, - candidate, - )); - } - } - candidates.sort_by(|a, b| a.0.total_cmp(&b.0).then(a.1.cmp(&b.1)).then(a.2.cmp(&b.2))); - let Some(best) = candidates.first() else { - return Err(GeometryError::InvalidGeometry( - "loft profiles do not have compatible winding".into(), - )); - }; - if let Some(second) = candidates.get(1) { - let score_tolerance = accuracy.geometric * accuracy.geometric * lower.len() as f64; - if (second.0 - best.0).abs() <= score_tolerance { - return Err(GeometryError::AmbiguousProfileAlignment( - "automatic loft correspondence has multiple equally good solutions; provide an indexed alignment".into(), - )); - } - } - best.3.clone() - } - }; - Ok((lower, upper)) -} - -pub fn polygon_loft( - id: String, - lower: Vec, - upper: Vec, - alignment: PolygonLoftAlignment, - accuracy: Accuracy, -) -> Result { - accuracy.validate()?; - if lower.len() != upper.len() { - return Err(GeometryError::UnsupportedGeometry( - "polygon loft profiles must have the same vertex count".into(), - )); - } - let lower_normal = validate_loft_profile(&lower, accuracy, "lower")?; - let upper_normal = validate_loft_profile(&upper, accuracy, "upper")?; - let centroid_delta = sub(polygon_centroid(&upper), polygon_centroid(&lower)); - if dot(lower_normal, centroid_delta).abs() <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "loft profile planes have no resolved normal separation".into(), - )); - } - let angular_tolerance = - (accuracy.geometric / norm(centroid_delta).max(accuracy.geometric)).clamp(1.0e-12, 1.0e-6); - if dot(lower_normal, upper_normal).abs() < 1.0 - angular_tolerance { - return Err(GeometryError::UnsupportedGeometry( - "polygon loft currently requires parallel profile planes".into(), - )); - } - let (lower, upper) = align_loft_profiles(lower, upper, alignment, accuracy)?; - let count = lower.len(); - let mut vertices = lower.clone(); - vertices.extend_from_slice(&upper); - - let mut faces = Vec::with_capacity(count + 2); - faces.push((0..count as u32).rev().collect()); - faces.push( - (0..count as u32) - .map(|index| index + count as u32) - .collect(), - ); - for index in 0..count { - let next = (index + 1) % count; - let quad = [lower[index], lower[next], upper[next], upper[index]]; - let base_normal = cross(sub(quad[1], quad[0]), sub(quad[3], quad[0])); - let denominator = norm(base_normal); - if denominator <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "loft side face collapses below geometric resolution".into(), - )); - } - let distance = dot(base_normal, sub(quad[2], quad[0])).abs() / denominator; - if distance > 4.0 * accuracy.geometric { - return Err(GeometryError::UnsupportedGeometry( - "polygon loft side is warped and requires a ruled surface outside the current analytic surface set".into(), - )); - } - faces.push(vec![ - index as u32, - next as u32, - (next + count) as u32, - (index + count) as u32, - ]); - } - primitives::planar_polyhedron(id, vertices, faces, accuracy) -} - -pub fn coaxial_circle_loft( - id: String, - frame: Frame3, - lower_radius: f64, - upper_radius: f64, - height: f64, - accuracy: Accuracy, -) -> Result { - if !lower_radius.is_finite() - || !upper_radius.is_finite() - || lower_radius < 0.0 - || upper_radius < 0.0 - || (lower_radius == 0.0 && upper_radius == 0.0) - { - return Err(GeometryError::InvalidGeometry( - "coaxial circle loft requires at least one positive finite radius".into(), - )); - } - if lower_radius == 0.0 { - let reversed = Frame3 { - origin: frame.point([0.0, 0.0, height]), - x: frame.x, - y: [-frame.y[0], -frame.y[1], -frame.y[2]], - z: [-frame.z[0], -frame.z[1], -frame.z[2]], - }; - return primitives::cone(id, reversed, upper_radius, height, accuracy); - } - primitives::frustum(id, frame, lower_radius, upper_radius, height, accuracy) -} - -pub fn revolve_rectangle( - id: String, - frame: Frame3, - inner_radius: f64, - outer_radius: f64, - height: f64, - accuracy: Accuracy, -) -> Result { - if !inner_radius.is_finite() || inner_radius < 0.0 { - return Err(GeometryError::InvalidGeometry( - "revolve inner radius must be finite and nonnegative".into(), - )); - } - if inner_radius == 0.0 { - primitives::cylinder(id, frame, outer_radius, height, accuracy) - } else { - primitives::annular_cylinder(id, frame, inner_radius, outer_radius, height, accuracy) - } -} - -pub fn chamfered_cuboid( - id: String, - frame: Frame3, - size: [f64; 3], - chamfer: f64, - accuracy: Accuracy, -) -> Result { - if size - .into_iter() - .any(|value| !value.is_finite() || value <= 0.0) - || !chamfer.is_finite() - || chamfer <= 0.0 - { - return Err(GeometryError::InvalidGeometry( - "chamfered cuboid dimensions must be positive and finite".into(), - )); - } - let [width, depth, height] = size; - if 2.0 * chamfer >= width.min(depth) { - return Err(GeometryError::InvalidGeometry( - "chamfer must be smaller than half the plan dimensions".into(), - )); - } - if chamfer <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "chamfer is below geometric resolution".into(), - )); - } - primitives::linear_extrusion( - id, - frame, - vec![ - [chamfer, 0.0], - [width - chamfer, 0.0], - [width, chamfer], - [width, depth - chamfer], - [width - chamfer, depth], - [chamfer, depth], - [0.0, depth - chamfer], - [0.0, chamfer], - ], - Vec::new(), - height, - accuracy, - ) -} - -pub fn filleted_cylinder( - id: String, - frame: Frame3, - radius: f64, - height: f64, - fillet_radius: f64, - accuracy: Accuracy, -) -> Result { - frame.validate()?; - accuracy.validate()?; - if [radius, height, fillet_radius] - .into_iter() - .any(|value| !value.is_finite() || value <= 0.0) - { - return Err(GeometryError::InvalidGeometry( - "filleted cylinder dimensions must be positive and finite".into(), - )); - } - if fillet_radius * 2.0 >= radius || fillet_radius * 2.0 >= height { - return Err(GeometryError::UnsupportedGeometry( - "filleted cylinder requires a ring-torus fillet smaller than half its radius and height".into(), - )); - } - if fillet_radius <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "fillet radius is below geometric resolution".into(), - )); - } - - let mut builder = Builder::new(id, accuracy)?; - let tau = std::f64::consts::TAU; - let quarter = std::f64::consts::FRAC_PI_2; - let major = radius - fillet_radius; - let levels = [0.0, fillet_radius, height - fillet_radius, height]; - let radii = [major, radius, radius, major]; - let vertices: [u32; 4] = std::array::from_fn(|index| { - builder.vertex(frame.point([radii[index], 0.0, levels[index]])) - }); - let mut ring_frames = [frame; 4]; - for (index, ring_frame) in ring_frames.iter_mut().enumerate() { - ring_frame.origin = frame.point([0.0, 0.0, levels[index]]); - } - let circle_range = Interval::new(0.0, tau)?; - let mut rings = Vec::with_capacity(4); - for index in 0..4 { - rings.push(builder.edge( - CurveGeometry::Circle { - frame: ring_frames[index], - radius: radii[index], - }, - circle_range, - false, - )); - } - let tube_frame = |center_height| Frame3 { - origin: frame.point([major, 0.0, center_height]), - x: frame.x, - y: frame.z, - z: scale(frame.y, -1.0), - }; - let lower_seam = builder.edge( - CurveGeometry::Circle { - frame: tube_frame(fillet_radius), - radius: fillet_radius, - }, - Interval::new(-quarter, 0.0)?, - true, - ); - let upper_seam = builder.edge( - CurveGeometry::Circle { - frame: tube_frame(height - fillet_radius), - radius: fillet_radius, - }, - Interval::new(0.0, quarter)?, - true, - ); - let cylinder_seam = builder.edge( - CurveGeometry::Line { - origin: frame.point([radius, 0.0, fillet_radius]), - direction: frame.z, - }, - Interval::new(0.0, height - 2.0 * fillet_radius)?, - true, - ); - - use Orientation::{Forward as F, Reverse as R}; - let mut lower_frame = frame; - lower_frame.origin = frame.point([0.0, 0.0, fillet_radius]); - builder.face( - "lower_fillet", - SurfaceGeometry::Torus { - frame: lower_frame, - major_radius: major, - minor_radius: fillet_radius, - }, - [[0.0, tau], [-quarter, 0.0]], - vec![ - boundary( - rings[0], - vertices[0], - vertices[0], - F, - uv_line([0.0, -quarter], [1.0, 0.0]), - ), - boundary( - lower_seam, - vertices[0], - vertices[1], - F, - uv_line([tau, 0.0], [0.0, 1.0]), - ), - boundary( - rings[1], - vertices[1], - vertices[1], - R, - uv_line([0.0, 0.0], [1.0, 0.0]), - ), - boundary( - lower_seam, - vertices[1], - vertices[0], - R, - uv_line([0.0, 0.0], [0.0, 1.0]), - ), - ], - )?; - builder.face( - "lateral", - SurfaceGeometry::Cylinder { frame, radius }, - [[0.0, tau], [fillet_radius, height - fillet_radius]], - vec![ - boundary( - rings[1], - vertices[1], - vertices[1], - F, - uv_line([0.0, fillet_radius], [1.0, 0.0]), - ), - boundary( - cylinder_seam, - vertices[1], - vertices[2], - F, - uv_line([tau, fillet_radius], [0.0, 1.0]), - ), - boundary( - rings[2], - vertices[2], - vertices[2], - R, - uv_line([0.0, height - fillet_radius], [1.0, 0.0]), - ), - boundary( - cylinder_seam, - vertices[2], - vertices[1], - R, - uv_line([0.0, fillet_radius], [0.0, 1.0]), - ), - ], - )?; - let mut upper_frame = frame; - upper_frame.origin = frame.point([0.0, 0.0, height - fillet_radius]); - builder.face( - "upper_fillet", - SurfaceGeometry::Torus { - frame: upper_frame, - major_radius: major, - minor_radius: fillet_radius, - }, - [[0.0, tau], [0.0, quarter]], - vec![ - boundary( - rings[2], - vertices[2], - vertices[2], - F, - uv_line([0.0, 0.0], [1.0, 0.0]), - ), - boundary( - upper_seam, - vertices[2], - vertices[3], - F, - uv_line([tau, 0.0], [0.0, 1.0]), - ), - boundary( - rings[3], - vertices[3], - vertices[3], - R, - uv_line([0.0, quarter], [1.0, 0.0]), - ), - boundary( - upper_seam, - vertices[3], - vertices[2], - R, - uv_line([0.0, 0.0], [0.0, 1.0]), - ), - ], - )?; - let bottom_frame = Frame3 { - y: scale(frame.y, -1.0), - z: scale(frame.z, -1.0), - ..frame - }; - builder.face( - "lower_cap", - SurfaceGeometry::Plane { - frame: bottom_frame, - }, - [[-major, major]; 2], - vec![boundary( - rings[0], - vertices[0], - vertices[0], - R, - PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [major, 0.0], - axis_b: [0.0, -major], - }, - )], - )?; - let mut cap_frame = frame; - cap_frame.origin = frame.point([0.0, 0.0, height]); - builder.face( - "upper_cap", - SurfaceGeometry::Plane { frame: cap_frame }, - [[-major, major]; 2], - vec![boundary( - rings[3], - vertices[3], - vertices[3], - F, - PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [major, 0.0], - axis_b: [0.0, major], - }, - )], - )?; - builder.finish() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::SurfaceGeometry; - - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-5, - } - } - - #[test] - fn coaxial_circle_loft_closes_over_cylinder_cone_and_frustum() { - for (lower, upper, expected_faces) in [(1.0, 1.0, 3), (1.0, 0.0, 2), (1.0, 0.5, 3)] { - let result = coaxial_circle_loft( - format!("loft-{lower}-{upper}"), - Frame3::IDENTITY, - lower, - upper, - 2.0, - accuracy(), - ) - .unwrap(); - assert_eq!(result.topology.faces.len(), expected_faces); - assert!(matches!( - result.geometry.surfaces[0], - SurfaceGeometry::Cylinder { .. } | SurfaceGeometry::Cone { .. } - )); - result.validate().unwrap(); - } - let apex_first = - coaxial_circle_loft("apex".into(), Frame3::IDENTITY, 0.0, 1.0, 2.0, accuracy()) - .unwrap(); - apex_first.validate().unwrap(); - } - - #[test] - fn polygon_loft_matches_reversed_shifted_sections_and_builds_shared_planar_faces() { - let lower = vec![ - [-1.0, -1.0, 0.0], - [1.0, -1.0, 0.0], - [1.0, 1.0, 0.0], - [-1.0, 1.0, 0.0], - ]; - let upper = vec![ - [0.6, 0.6, 2.0], - [0.6, -0.6, 2.0], - [-0.6, -0.6, 2.0], - [-0.6, 0.6, 2.0], - ]; - let result = polygon_loft( - "polygon-loft".into(), - lower, - upper, - PolygonLoftAlignment::Auto, - accuracy(), - ) - .unwrap(); - assert_eq!(result.topology.vertices.len(), 8); - assert_eq!(result.topology.edges.len(), 12); - assert_eq!(result.topology.faces.len(), 6); - assert!(result - .geometry - .surfaces - .iter() - .all(|surface| matches!(surface, SurfaceGeometry::Plane { .. }))); - result.validate().unwrap(); - crate::analytic::tessellation::tessellate(&result, 0.01, 2_000_000).unwrap(); - } - - #[test] - fn polygon_loft_rejects_unequal_or_warped_sections() { - let lower = vec![ - [0.0, 0.0, 0.0], - [2.0, 0.0, 0.0], - [2.0, 2.0, 0.0], - [0.0, 2.0, 0.0], - ]; - assert!(matches!( - polygon_loft( - "count".into(), - lower.clone(), - vec![[0.0, 0.0, 1.0], [1.0, 0.0, 1.0], [0.0, 1.0, 1.0]], - PolygonLoftAlignment::Auto, - accuracy(), - ), - Err(GeometryError::UnsupportedGeometry(_)) - )); - assert!(matches!( - polygon_loft( - "warped".into(), - lower, - vec![ - [0.0, 0.0, 1.0], - [2.0, 0.0, 1.0], - [1.5, 2.0, 1.0], - [0.0, 2.0, 1.0], - ], - PolygonLoftAlignment::Indexed { - upper_start: 0, - reverse_upper: false, - }, - accuracy(), - ), - Err(GeometryError::UnsupportedGeometry(_)) - )); - assert!(matches!( - polygon_loft( - "coplanar".into(), - vec![ - [0.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - [1.0, 1.0, 0.0], - [0.0, 1.0, 0.0], - ], - vec![ - [2.0, 0.0, 0.0], - [3.0, 0.0, 0.0], - [3.0, 1.0, 0.0], - [2.0, 1.0, 0.0], - ], - PolygonLoftAlignment::Auto, - accuracy(), - ), - Err(GeometryError::UnresolvedIntersection(_)) - )); - } - - #[test] - fn rectangular_revolve_keeps_cylindrical_hole_analytic() { - let result = - revolve_rectangle("ring".into(), Frame3::IDENTITY, 0.5, 1.0, 2.0, accuracy()).unwrap(); - assert_eq!(result.topology.faces.len(), 4); - assert!(matches!( - result.geometry.surfaces[0], - SurfaceGeometry::Cylinder { radius: 1.0, .. } - )); - assert!(matches!( - result.geometry.surfaces[1], - SurfaceGeometry::Cylinder { radius: 0.5, .. } - )); - } - - #[test] - fn planar_chamfer_rebuilds_a_valid_all_plane_solid() { - let result = chamfered_cuboid( - "chamfer".into(), - Frame3::IDENTITY, - [4.0, 3.0, 2.0], - 0.25, - accuracy(), - ) - .unwrap(); - assert_eq!(result.topology.faces.len(), 10); - assert!(result - .geometry - .surfaces - .iter() - .all(|surface| matches!(surface, SurfaceGeometry::Plane { .. }))); - result.validate().unwrap(); - } - - #[test] - fn circular_edge_fillet_uses_torus_quarters_and_shared_circles() { - let result = - filleted_cylinder("fillet".into(), Frame3::IDENTITY, 2.0, 4.0, 0.3, accuracy()) - .unwrap(); - assert_eq!(result.topology.faces.len(), 5); - assert_eq!( - result - .geometry - .surfaces - .iter() - .filter(|surface| matches!(surface, SurfaceGeometry::Torus { .. })) - .count(), - 2 - ); - result.validate().unwrap(); - crate::analytic::tessellation::tessellate(&result, 0.02, 2_000_000).unwrap(); - } -} diff --git a/main/opengeometry/src/analytic/placement.rs b/main/opengeometry/src/analytic/placement.rs deleted file mode 100644 index 20f935e..0000000 --- a/main/opengeometry/src/analytic/placement.rs +++ /dev/null @@ -1,351 +0,0 @@ -use super::{ - geometry::{norm, scale as vector_scale}, - topology::{EdgeGeometry, GeometryQuality, PcurveGeometry}, - BrepEnvelope, CurveGeometry, Frame3, GeometryError, SurfaceGeometry, -}; -use crate::math::interval::Interval; - -fn metric(surface: &SurfaceGeometry, scale: f64) -> [f64; 2] { - match surface { - SurfaceGeometry::Plane { .. } => [scale; 2], - SurfaceGeometry::Cylinder { .. } | SurfaceGeometry::Cone { .. } => [1.0, scale], - _ => [1.0; 2], - } -} -fn pcurve( - geometry: &mut PcurveGeometry, - metric: [f64; 2], - parameter_scale: f64, -) -> Result<(), GeometryError> { - let map = |value: &mut [f64; 2]| { - for i in 0..2 { - value[i] *= metric[i]; - } - }; - match geometry { - PcurveGeometry::Line2 { origin, direction } => { - map(origin); - map(direction); - for value in direction { - *value /= parameter_scale; - } - } - PcurveGeometry::Conic2 { - origin, - axis_a, - axis_b, - } => { - if parameter_scale != 1.0 { - return Err(GeometryError::UnsupportedGeometry( - "scaled line edge uses a conic pcurve".into(), - )); - } - map(origin); - map(axis_a); - map(axis_b); - } - PcurveGeometry::ProjectedCurve { - uv_hint, - uv_rate, - parameter_origin, - .. - } => { - map(uv_hint); - map(uv_rate); - for value in uv_rate { - *value /= parameter_scale; - } - *parameter_origin *= parameter_scale; - } - PcurveGeometry::IntersectionSide { .. } => {} - } - Ok(()) -} - -/// A positive similarity transforms geometry, UV length coordinates and error budgets together. -pub fn placed( - input: &BrepEnvelope, - placement: Frame3, - scale: f64, -) -> Result { - input.validate()?; - placement.validate()?; - if !scale.is_finite() || scale <= 0.0 { - return Err(GeometryError::InvalidGeometry( - "placement scale must be positive and finite".into(), - )); - } - let mut output = input.clone(); - output.revision = input - .revision - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("BRep revision overflow".into()))?; - let transform_frame = |frame: &mut Frame3| { - frame.origin = placement.point(vector_scale(frame.origin, scale)); - frame.x = placement.vector(frame.x); - frame.y = placement.vector(frame.y); - frame.z = placement.vector(frame.z); - }; - let metrics: Vec<_> = input - .geometry - .surfaces - .iter() - .map(|surface| metric(surface, scale)) - .collect(); - for surface in &mut output.geometry.surfaces { - match surface { - SurfaceGeometry::Plane { frame } | SurfaceGeometry::Cone { frame, .. } => { - transform_frame(frame) - } - SurfaceGeometry::Sphere { frame, radius } - | SurfaceGeometry::Cylinder { frame, radius } => { - transform_frame(frame); - *radius *= scale; - } - SurfaceGeometry::Torus { - frame, - major_radius, - minor_radius, - } => { - transform_frame(frame); - *major_radius *= scale; - *minor_radius *= scale; - } - } - } - for curve in &mut output.geometry.curves { - match curve { - CurveGeometry::Line { origin, direction } => { - *origin = placement.point(vector_scale(*origin, scale)); - *direction = placement.vector(*direction); - } - CurveGeometry::Circle { frame, radius } => { - transform_frame(frame); - *radius *= scale; - } - CurveGeometry::Ellipse { - frame, - major_radius, - minor_radius, - } => { - transform_frame(frame); - *major_radius *= scale; - *minor_radius *= scale; - } - CurveGeometry::Intersection { .. } => {} - } - } - for vertex in &mut output.topology.vertices { - vertex.position = placement.point(vector_scale(vertex.position, scale)); - vertex.tolerance *= scale; - } - for edge in &mut output.topology.edges { - edge.tolerance *= scale; - if let EdgeGeometry::Curve { curve, range } = &mut edge.geometry { - if matches!( - input.geometry.curves[*curve as usize], - CurveGeometry::Line { .. } - ) { - *range = Interval::new(range.lo * scale, range.hi * scale)?; - } - } - } - let mut pcurve_metrics = vec![None; input.geometry.pcurves.len()]; - for use_ in &mut output.topology.halfedges { - let (Some(face), Some(pc)) = (use_.face, use_.geometry_use.pcurve) else { - continue; - }; - let metric = metrics[input.topology.faces[face as usize].surface as usize]; - let parameter_scale = match input.topology.edges[use_.edge as usize].geometry { - EdgeGeometry::Curve { curve, .. } - if matches!( - input.geometry.curves[curve as usize], - CurveGeometry::Line { .. } - ) => - { - scale - } - _ => 1.0, - }; - match pcurve_metrics[pc as usize] { - None => { - pcurve_metrics[pc as usize] = Some((metric, parameter_scale)); - } - Some(previous) if previous != (metric, parameter_scale) => { - let mut copy = input.geometry.pcurves[pc as usize].clone(); - pcurve(&mut copy, metric, parameter_scale)?; - use_.geometry_use.pcurve = Some(output.geometry.pcurves.len() as u32); - output.geometry.pcurves.push(copy); - } - _ => {} - } - } - for (id, original) in input.geometry.pcurves.iter().enumerate() { - let (mut metric, parameter_scale) = pcurve_metrics[id].unwrap_or(([1.0; 2], 1.0)); - if let PcurveGeometry::ProjectedCurve { surface, .. } = original { - metric = metrics[*surface as usize]; - } - pcurve(&mut output.geometry.pcurves[id], metric, parameter_scale)?; - } - for face in &mut output.topology.faces { - for (i, metric) in metrics[face.surface as usize].iter().enumerate() { - face.trim.uv_bounds[i] = Interval::new( - face.trim.uv_bounds[i].lo * metric, - face.trim.uv_bounds[i].hi * metric, - )?; - } - } - for definition in &mut output.geometry.intersections { - let a = metrics[definition.surfaces[0] as usize]; - let b = metrics[definition.surfaces[1] as usize]; - for anchor in &mut definition.anchors { - anchor.point = placement.point(vector_scale(anchor.point, scale)); - for i in 0..2 { - anchor.uv_a[i] *= a[i]; - anchor.uv_b[i] *= b[i]; - } - } - for tube in &mut definition.uv_tubes { - for (i, metric) in a.into_iter().chain(b).enumerate() { - tube[i] = Interval::new(tube[i].lo * metric, tube[i].hi * metric)?; - } - } - definition.residual_tolerance *= scale; - } - output.accuracy.geometric *= scale; - output.accuracy.intersection *= scale; - output.accuracy.tessellation *= scale; - output.accuracy.exchange *= scale; - if let GeometryQuality::Approximate { max_error } = &mut output.quality { - *max_error *= scale; - } - output.validate()?; - if let Some(bounds) = output.bounds()? { - let magnitude = norm(std::array::from_fn(|i| { - bounds.axes[i].lo.abs().max(bounds.axes[i].hi.abs()) - })); - if !magnitude.is_finite() || magnitude * 64.0 * f64::EPSILON > output.accuracy.geometric { - return Err(GeometryError::LimitExceeded( - "placement coordinate precision exceeds geometric tolerance".into(), - )); - } - } - Ok(output) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::{primitives, topology::Accuracy, Curve, Surface}; - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-6, - } - } - #[test] - fn similarity_preserves_all_families_trims_and_curve_parameterization() { - let placement = - Frame3::from_axis([10.0, -3.0, 5.0], [1.0, 2.0, 3.0], [0.0, 1.0, 0.0]).unwrap(); - let bodies = [ - primitives::cylinder("c".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy()).unwrap(), - primitives::cone("cone".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy()).unwrap(), - primitives::sphere("s".into(), Frame3::IDENTITY, 1.0, accuracy()).unwrap(), - primitives::torus("t".into(), Frame3::IDENTITY, 2.0, 0.5, accuracy()).unwrap(), - primitives::annular_sector_extrusion( - "w".into(), - Frame3::IDENTITY, - 2.0, - 0.2, - 2.0, - 0.15, - -1.8, - accuracy(), - ) - .unwrap(), - ]; - for input in bodies { - let json = input.to_json().unwrap(); - let output = placed(&input, placement, 3.5).unwrap(); - assert_eq!(input.to_json().unwrap(), json); - assert_eq!(output.revision, input.revision + 1); - for edge in &input.topology.edges { - if let crate::analytic::topology::EdgeGeometry::Curve { curve, range } = - edge.geometry - { - let t = range.lo / 2.0 + range.hi / 2.0; - let expected = placement.point(vector_scale( - input.geometry.curve(curve).unwrap().point_at(t).unwrap(), - 3.5, - )); - let output_t = if matches!( - input.geometry.curves[curve as usize], - CurveGeometry::Line { .. } - ) { - t * 3.5 - } else { - t - }; - assert!( - norm(super::super::geometry::sub( - output - .geometry - .curve(curve) - .unwrap() - .point_at(output_t) - .unwrap(), - expected - )) < 1e-12 - ); - } - } - for face in &input.topology.faces { - let uv = face.trim.uv_bounds.map(|d| d.lo / 2.0 + d.hi / 2.0); - let metric = metric(input.geometry.surface(face.surface).unwrap(), 3.5); - let expected = placement.point(vector_scale( - input - .geometry - .surface(face.surface) - .unwrap() - .point_at(uv) - .unwrap(), - 3.5, - )); - let actual = output - .geometry - .surface(face.surface) - .unwrap() - .point_at(std::array::from_fn(|i| uv[i] * metric[i])) - .unwrap(); - assert!(norm(super::super::geometry::sub(actual, expected)) < 1e-12); - } - BrepEnvelope::from_json(&output.to_json().unwrap()).unwrap(); - } - } - #[test] - fn invalid_scale_revision_overflow_and_precision_are_structured_errors() { - let input = primitives::sphere("s".into(), Frame3::IDENTITY, 1.0, accuracy()).unwrap(); - for scale in [0.0, -1.0, f64::NAN] { - assert!(placed(&input, Frame3::IDENTITY, scale).is_err()); - } - let mut bad = input.clone(); - bad.revision = u64::MAX; - assert!(matches!( - placed(&bad, Frame3::IDENTITY, 1.0), - Err(GeometryError::LimitExceeded(_)) - )); - assert!(matches!( - placed( - &input, - Frame3 { - origin: [1e12; 3], - ..Frame3::IDENTITY - }, - 1.0 - ), - Err(GeometryError::LimitExceeded(_)) - )); - } -} diff --git a/main/opengeometry/src/analytic/planar_booleans.rs b/main/opengeometry/src/analytic/planar_booleans.rs deleted file mode 100644 index d93ecbe..0000000 --- a/main/opengeometry/src/analytic/planar_booleans.rs +++ /dev/null @@ -1,1579 +0,0 @@ -use std::{ - cell::RefCell, - collections::{BTreeMap, VecDeque}, -}; - -use super::{ - booleans::{ - analytic_face_mappings, brep_face_source, full_planar_extrusion, BooleanOp, BooleanReport, - BooleanResult, PrismaticInput, - }, - geometry::{cross, dot, norm, scale, sub, unit}, - primitives::{plane_boundary, Builder, Use}, - query::face_contains_uv, - topology::{ - Accuracy, BrepEnvelope, EdgeGeometry, FaceProvenance, FaceRole, FaceSource, Orientation, - Shell, SolidRegion, - }, - CurveGeometry, Frame3, GeometryError, Point3, SurfaceGeometry, -}; -use crate::{ - geometry::{ - boolean2d::{boolean_oriented_regions, regions_from_edges_by, PlanarBooleanOp, RingRegion}, - poly2d::{winding_number2, Pt2}, - }, - math::interval::Interval, -}; - -fn coverage() -> GeometryError { - GeometryError::CoverageGap { - families: [ - "layered planar profile extrusion".into(), - "layered planar profile extrusion".into(), - ], - } -} - -fn contours(input: &PrismaticInput<'_>, frame: Frame3) -> Vec> { - input - .contours - .iter() - .map(|ring| { - ring.iter() - .map(|point| { - let local = frame.local(*point); - Pt2::new(local[0], local[1]) - }) - .collect() - }) - .collect() -} - -fn region_contours(regions: &[RingRegion]) -> Vec> { - regions - .iter() - .flat_map(|region| { - std::iter::once(region.outer.clone()).chain(region.holes.iter().cloned()) - }) - .collect() -} - -fn region_points(regions: &[RingRegion], out: &mut Vec) { - for region in regions { - out.extend(region.outer.iter().copied()); - for hole in ®ion.holes { - out.extend(hole.iter().copied()); - } - } -} - -fn loop_positions(brep: &BrepEnvelope, loop_id: u32) -> Result, GeometryError> { - let start = brep - .topology - .loops - .get(loop_id as usize) - .ok_or_else(coverage)? - .start_halfedge; - let mut current = start; - let mut points = Vec::new(); - loop { - let halfedge = brep - .topology - .halfedges - .get(current as usize) - .ok_or_else(coverage)?; - points.push( - brep.topology - .vertices - .get(halfedge.from as usize) - .ok_or_else(coverage)? - .position, - ); - current = halfedge.next.ok_or_else(coverage)?; - if current == start { - break; - } - if points.len() > brep.topology.halfedges.len() { - return Err(coverage()); - } - } - Ok(points) -} - -fn slice_regions( - brep: &BrepEnvelope, - frame: Frame3, - level: f64, - tolerance: f64, -) -> Result, GeometryError> { - let mut segments = Vec::<(Pt2, Pt2)>::new(); - for face in &brep.topology.faces { - let SurfaceGeometry::Plane { frame: face_frame } = brep - .geometry - .surfaces - .get(face.surface as usize) - .ok_or_else(coverage)? - else { - return Err(coverage()); - }; - if dot(face_frame.z, frame.z).abs() > 1.0e-10 { - continue; - } - if !face.trim.holes.is_empty() { - return Err(coverage()); - } - let points = loop_positions(brep, face.trim.outer)?; - let mut crossings = Vec::::new(); - for index in 0..points.len() { - let from = frame.local(points[index]); - let to = frame.local(points[(index + 1) % points.len()]); - if (from[2] < level && to[2] > level) || (from[2] > level && to[2] < level) { - let fraction = (level - from[2]) / (to[2] - from[2]); - let point = Pt2::new( - from[0] + fraction * (to[0] - from[0]), - from[1] + fraction * (to[1] - from[1]), - ); - if crossings - .iter() - .all(|existing| (existing.x - point.x).hypot(existing.z - point.z) > tolerance) - { - crossings.push(point); - } - } - } - if crossings.is_empty() { - continue; - } - if crossings.len() != 2 { - return Err(coverage()); - } - let mut from = crossings[0]; - let mut to = crossings[1]; - let edge = sub( - frame.point([to.x, to.z, level]), - frame.point([from.x, from.z, level]), - ); - let outward = cross(edge, frame.z); - let actual = scale(face_frame.z, face.sense.multiplier()); - if dot(outward, actual) < 0.0 { - std::mem::swap(&mut from, &mut to); - } - segments.push((from, to)); - } - let mut loops = Vec::new(); - while let Some((from, mut to)) = segments.pop() { - let mut ring = vec![from]; - while (to.x - from.x).hypot(to.z - from.z) > tolerance { - ring.push(to); - let Some(next) = segments - .iter() - .position(|(start, _)| (start.x - to.x).hypot(start.z - to.z) <= tolerance) - else { - return Err(coverage()); - }; - let (_, end) = segments.swap_remove(next); - to = end; - if ring.len() > brep.topology.halfedges.len() { - return Err(coverage()); - } - } - if ring.len() < 3 { - return Err(coverage()); - } - loops.push(ring); - } - Ok(boolean_oriented_regions( - &loops, - &[], - PlanarBooleanOp::Union, - tolerance, - )) -} - -fn side_sources( - brep: &BrepEnvelope, - point: Point3, - outward: Point3, - tolerance: f64, -) -> Result, GeometryError> { - let mut sources = Vec::new(); - for face in &brep.topology.faces { - let SurfaceGeometry::Plane { frame } = brep - .geometry - .surfaces - .get(face.surface as usize) - .ok_or_else(coverage)? - else { - return Err(coverage()); - }; - let local = frame.local(point); - if local[2].abs() > tolerance || dot(frame.z, outward).abs() < 1.0 - 1.0e-10 { - continue; - } - if face_contains_uv(brep, face, [local[0], local[1]])? == Some(true) { - sources.push(brep_face_source(brep, face.id)); - } - } - Ok(sources) -} - -fn region_sample(region: &RingRegion) -> Result { - let mut coordinates = Vec::new(); - let mut holes = Vec::new(); - for point in ®ion.outer { - coordinates.extend([point.x, point.z]); - } - for hole in ®ion.holes { - holes.push(coordinates.len() / 2); - for point in hole { - coordinates.extend([point.x, point.z]); - } - } - let triangles = earcutr::earcut(&coordinates, &holes, 2); - // Earcut may put a nearly collinear boundary triangle first. Its centroid - // can round onto the host face and fail cap provenance after a chained cut. - // The largest triangle gives a stable point strictly inside this region. - let triangle = triangles - .chunks_exact(3) - .max_by(|left, right| { - let area = |triangle: &[usize]| { - let point = - |index: usize| Pt2::new(coordinates[index * 2], coordinates[index * 2 + 1]); - let a = point(triangle[0]); - let b = point(triangle[1]); - let c = point(triangle[2]); - ((b.x - a.x) * (c.z - a.z) - (b.z - a.z) * (c.x - a.x)).abs() - }; - area(left).total_cmp(&area(right)) - }) - .ok_or_else(coverage)?; - Ok(Pt2::new( - triangle - .iter() - .map(|&index| coordinates[index * 2]) - .sum::() - / 3.0, - triangle - .iter() - .map(|&index| coordinates[index * 2 + 1]) - .sum::() - / 3.0, - )) -} - -fn cap_sources( - brep: &BrepEnvelope, - point: Point3, - outward: Point3, - cut: bool, - tolerance: f64, -) -> Result, GeometryError> { - let mut sources = Vec::new(); - for face in &brep.topology.faces { - let SurfaceGeometry::Plane { frame } = brep - .geometry - .surfaces - .get(face.surface as usize) - .ok_or_else(coverage)? - else { - return Err(coverage()); - }; - let local = frame.local(point); - if local[2].abs() > tolerance { - continue; - } - let actual = scale(frame.z, face.sense.multiplier()); - let alignment = dot(actual, outward); - if (cut && alignment > -1.0 + 1.0e-10) || (!cut && alignment < 1.0 - 1.0e-10) { - continue; - } - if face_contains_uv(brep, face, [local[0], local[1]])? == Some(true) { - sources.push(brep_face_source(brep, face.id)); - } - } - Ok(sources) -} - -fn split_ring(ring: &[Pt2], vertices: &[Pt2], tolerance: f64) -> Vec { - let mut split = Vec::new(); - for index in 0..ring.len() { - let from = ring[index]; - let to = ring[(index + 1) % ring.len()]; - let delta = Pt2::new(to.x - from.x, to.z - from.z); - let length2 = delta.x * delta.x + delta.z * delta.z; - if length2 <= tolerance * tolerance { - continue; - } - split.push(from); - let mut interior = Vec::new(); - for &point in vertices { - let offset = Pt2::new(point.x - from.x, point.z - from.z); - let t = (offset.x * delta.x + offset.z * delta.z) / length2; - let distance = (offset.x * delta.z - offset.z * delta.x).abs() / length2.sqrt(); - if t > tolerance / length2.sqrt() - && t < 1.0 - tolerance / length2.sqrt() - && distance <= tolerance - { - interior.push((t, point)); - } - } - interior.sort_by(|left, right| left.0.total_cmp(&right.0)); - for (_, point) in interior { - if split - .last() - .is_none_or(|last| (last.x - point.x).hypot(last.z - point.z) > tolerance) - { - split.push(point); - } - } - } - split -} - -struct Facets { - builder: Builder, - vertices: Vec<(Point3, u32)>, - edges: BTreeMap<(u32, u32), u32>, - accuracy: Accuracy, -} - -impl Facets { - fn new(id: String, accuracy: Accuracy) -> Result { - Ok(Self { - builder: Builder::new(id, accuracy)?, - vertices: Vec::new(), - edges: BTreeMap::new(), - accuracy, - }) - } - - fn vertex(&mut self, point: Point3) -> u32 { - if let Some((_, id)) = self - .vertices - .iter() - .find(|(existing, _)| norm(sub(*existing, point)) <= self.accuracy.geometric / 4.0) - { - return *id; - } - let id = self.builder.vertex(point); - self.vertices.push((point, id)); - id - } - - fn uses(&mut self, ring: &[Point3], frame: Frame3) -> Result, GeometryError> { - let ids = ring - .iter() - .map(|point| self.vertex(*point)) - .collect::>(); - let mut uses = Vec::with_capacity(ids.len()); - for index in 0..ids.len() { - let next = (index + 1) % ids.len(); - let from = ids[index]; - let to = ids[next]; - if from == to { - return Err(GeometryError::UnresolvedIntersection( - "layered planar face has a sub-tolerance edge".into(), - )); - } - let key = (from.min(to), from.max(to)); - let edge = if let Some(&edge) = self.edges.get(&key) { - edge - } else { - let delta = sub(ring[next], ring[index]); - let length = norm(delta); - let edge = self.builder.edge( - CurveGeometry::Line { - origin: ring[index], - direction: unit(delta)?, - }, - Interval::new(0.0, length)?, - false, - ); - self.edges.insert(key, edge); - edge - }; - let EdgeGeometry::Curve { curve, .. } = - self.builder.brep.topology.edges[edge as usize].geometry - else { - return Err(coverage()); - }; - let CurveGeometry::Line { origin, direction } = - self.builder.brep.geometry.curves[curve as usize] - else { - return Err(coverage()); - }; - let sense = if norm(sub(origin, ring[index])) <= self.accuracy.geometric / 4.0 - && dot(direction, sub(ring[next], ring[index])) > 0.0 - { - Orientation::Forward - } else { - Orientation::Reverse - }; - uses.push(plane_boundary(&self.builder, frame, edge, from, to, sense)?); - } - Ok(uses) - } - - fn face( - &mut self, - key: &str, - frame: Frame3, - outer: Vec, - holes: Vec>, - provenance: FaceProvenance, - ) -> Result<(), GeometryError> { - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - for point in outer.iter().chain(holes.iter().flatten()) { - let local = frame.local(*point); - if local[2].abs() > 4.0 * self.accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "layered planar face is not coplanar".into(), - )); - } - for axis in 0..2 { - bounds[axis][0] = bounds[axis][0].min(local[axis]); - bounds[axis][1] = bounds[axis][1].max(local[axis]); - } - } - for bound in &mut bounds { - bound[0] -= self.accuracy.geometric; - bound[1] += self.accuracy.geometric; - } - let outer_uses = self.uses(&outer, frame)?; - let hole_uses = holes - .iter() - .map(|hole| self.uses(hole, frame)) - .collect::, _>>()?; - let face = self.builder.brep.topology.faces.len(); - self.builder.face_with_holes( - &format!("{key}-{face}"), - super::SurfaceGeometry::Plane { frame }, - bounds, - outer_uses, - hole_uses, - )?; - self.builder.brep.topology.faces[face].provenance = provenance; - Ok(()) - } -} - -fn cap( - facets: &mut Facets, - base: Frame3, - level: f64, - up: bool, - regions: &[RingRegion], - vertices: &[Pt2], - source_breps: &[(&BrepEnvelope, FaceRole)], -) -> Result<(), GeometryError> { - let origin = base.point([0.0, 0.0, level]); - let frame = if up { - Frame3 { origin, ..base } - } else { - Frame3 { - origin, - x: base.x, - y: scale(base.y, -1.0), - z: scale(base.z, -1.0), - } - }; - for region in regions { - let sample = region_sample(region)?; - let mut selected = None; - for &(brep, role) in source_breps { - let sources = cap_sources( - brep, - base.point([sample.x, sample.z, level]), - frame.z, - role == FaceRole::Cut, - facets.accuracy.intersection, - )?; - if !sources.is_empty() { - selected = Some((sources, role)); - break; - } - } - let (sources, role) = selected.ok_or_else(|| { - GeometryError::InvalidTopology(format!( - "layered planar cap has no source face at level {level}" - )) - })?; - let mut outer = split_ring(®ion.outer, vertices, facets.accuracy.geometric / 4.0) - .into_iter() - .map(|point| base.point([point.x, point.z, level])) - .collect::>(); - let mut holes = region - .holes - .iter() - .map(|ring| { - split_ring(ring, vertices, facets.accuracy.geometric / 4.0) - .into_iter() - .map(|point| base.point([point.x, point.z, level])) - .collect::>() - }) - .collect::>(); - if !up { - outer.reverse(); - for hole in &mut holes { - hole.reverse(); - } - } - facets.face( - if up { "upper-cap" } else { "lower-cap" }, - frame, - outer, - holes, - FaceProvenance { - sources, - role, - reversed: role == FaceRole::Cut, - }, - )?; - } - Ok(()) -} - -fn side_faces( - facets: &mut Facets, - a: &BrepEnvelope, - b: &BrepEnvelope, - base: Frame3, - regions: &[RingRegion], - vertices: &[Pt2], - lo: f64, - hi: f64, -) -> Result<(), GeometryError> { - for region in regions { - for ring in std::iter::once(®ion.outer).chain(®ion.holes) { - let ring = split_ring(ring, vertices, facets.accuracy.geometric / 4.0); - for index in 0..ring.len() { - let from = ring[index]; - let to = ring[(index + 1) % ring.len()]; - let lower_from = base.point([from.x, from.z, lo]); - let lower_to = base.point([to.x, to.z, lo]); - let upper_to = base.point([to.x, to.z, hi]); - let upper_from = base.point([from.x, from.z, hi]); - let normal = unit(cross( - sub(lower_to, lower_from), - sub(upper_from, lower_from), - ))?; - let face_frame = Frame3::from_axis(lower_from, normal, sub(lower_to, lower_from))?; - let midpoint = scale( - [ - lower_from[0] + lower_to[0] + upper_to[0] + upper_from[0], - lower_from[1] + lower_to[1] + upper_to[1] + upper_from[1], - lower_from[2] + lower_to[2] + upper_to[2] + upper_from[2], - ], - 0.25, - ); - let a_sources = side_sources(a, midpoint, normal, facets.accuracy.intersection)?; - let b_sources = side_sources(b, midpoint, normal, facets.accuracy.intersection)?; - let is_cut = a_sources.is_empty() && !b_sources.is_empty(); - let sources = if is_cut { b_sources } else { a_sources }; - if sources.is_empty() { - return Err(GeometryError::InvalidTopology( - "layered planar side has no source face".into(), - )); - } - facets.face( - if is_cut { "cut-side" } else { "host-side" }, - face_frame, - vec![lower_from, lower_to, upper_to, upper_from], - Vec::new(), - FaceProvenance { - sources, - role: if is_cut { - FaceRole::Cut - } else { - FaceRole::Split - }, - reversed: is_cut, - }, - )?; - } - } - } - Ok(()) -} - -fn shell_volume(brep: &BrepEnvelope, faces: &[u32]) -> Result { - let mut volume = 0.0; - for &face_id in faces { - let face = &brep.topology.faces[face_id as usize]; - for &loop_id in std::iter::once(&face.trim.outer).chain(&face.trim.holes) { - let start = brep.topology.loops[loop_id as usize].start_halfedge; - let mut current = start; - let mut points = Vec::new(); - loop { - let halfedge = &brep.topology.halfedges[current as usize]; - points.push(brep.topology.vertices[halfedge.from as usize].position); - current = halfedge.next.ok_or_else(|| { - GeometryError::InvalidTopology("layered planar face has open loop".into()) - })?; - if current == start { - break; - } - if points.len() > brep.topology.halfedges.len() { - return Err(coverage()); - } - } - for index in 1..points.len() - 1 { - volume += dot(points[0], cross(points[index], points[index + 1])) / 6.0; - } - } - } - Ok(volume) -} - -fn shell_contains_point( - brep: &BrepEnvelope, - faces: &[u32], - point: Point3, - tolerance: f64, -) -> Result { - for direction in [ - unit([1.0, 0.371, 0.127])?, - unit([0.193, 1.0, 0.419])?, - unit([0.311, 0.233, 1.0])?, - ] { - let mut hits: Vec = Vec::new(); - let mut uncertain = false; - for &face_id in faces { - let face = &brep.topology.faces[face_id as usize]; - let SurfaceGeometry::Plane { frame } = brep.geometry.surface(face.surface)? else { - return Err(coverage()); - }; - let denominator = dot(frame.z, direction); - let numerator = dot(frame.z, sub(frame.origin, point)); - if denominator.abs() <= 64.0 * f64::EPSILON { - if numerator.abs() <= tolerance { - uncertain = true; - break; - } - continue; - } - let distance = numerator / denominator; - if distance <= tolerance { - continue; - } - let position = [ - point[0] + direction[0] * distance, - point[1] + direction[1] * distance, - point[2] + direction[2] * distance, - ]; - let local = frame.local(position); - match face_contains_uv(brep, face, [local[0], local[1]])? { - Some(true) if hits.iter().any(|hit| (hit - distance).abs() <= tolerance) => { - uncertain = true; - break; - } - Some(true) => hits.push(distance), - Some(false) => {} - None => { - uncertain = true; - break; - } - } - } - if !uncertain { - return Ok(hits.len() % 2 == 1); - } - } - Err(coverage()) -} - -fn finish( - mut facets: Facets, - a: &BrepEnvelope, - b: &BrepEnvelope, -) -> Result { - let mut assigned = vec![false; facets.builder.brep.topology.faces.len()]; - let mut outer_shells = Vec::new(); - let mut cavity_shells = Vec::new(); - for start in 0..assigned.len() { - if assigned[start] { - continue; - } - let shell = facets.builder.brep.topology.shells.len() as u32; - let mut faces = Vec::new(); - let mut queue = VecDeque::from([start]); - assigned[start] = true; - while let Some(face) = queue.pop_front() { - facets.builder.brep.topology.faces[face].shell_ref = Some(shell); - faces.push(face as u32); - for halfedge in facets - .builder - .brep - .topology - .halfedges - .iter() - .filter(|edge| edge.face == Some(face as u32)) - { - let twin = halfedge.twin.ok_or_else(|| { - GeometryError::InvalidTopology( - "layered planar Boolean has an open boundary".into(), - ) - })?; - let adjacent = facets.builder.brep.topology.halfedges[twin as usize] - .face - .ok_or_else(|| { - GeometryError::InvalidTopology("layered planar twin has no face".into()) - })? as usize; - if !assigned[adjacent] { - assigned[adjacent] = true; - queue.push_back(adjacent); - } - } - } - let volume = shell_volume(&facets.builder.brep, &faces)?; - if volume.abs() <= facets.accuracy.geometric.powi(3) { - return Err(coverage()); - } - facets.builder.brep.topology.shells.push(Shell { - id: shell, - faces, - is_closed: true, - }); - if volume > 0.0 { - outer_shells.push((shell, volume)); - } else { - cavity_shells.push(shell); - } - } - for &(outer_shell, _) in &outer_shells { - facets.builder.brep.solids.push(SolidRegion { - outer_shell, - cavity_shells: Vec::new(), - }); - } - for cavity_shell in cavity_shells { - let shell = &facets.builder.brep.topology.shells[cavity_shell as usize]; - let face = &facets.builder.brep.topology.faces[shell.faces[0] as usize]; - let loop_start = - facets.builder.brep.topology.loops[face.trim.outer as usize].start_halfedge; - let vertex = facets.builder.brep.topology.halfedges[loop_start as usize].from; - let point = facets.builder.brep.topology.vertices[vertex as usize].position; - let mut enclosing = Vec::new(); - for (index, &(outer_shell, volume)) in outer_shells.iter().enumerate() { - let outer = &facets.builder.brep.topology.shells[outer_shell as usize]; - if shell_contains_point( - &facets.builder.brep, - &outer.faces, - point, - facets.accuracy.intersection, - )? { - enclosing.push((index, volume)); - } - } - let Some(&(index, _)) = enclosing - .iter() - .min_by(|left, right| left.1.total_cmp(&right.1)) - else { - return Err(coverage()); - }; - facets.builder.brep.solids[index] - .cavity_shells - .push(cavity_shell); - } - let mut out = facets.builder.brep; - out.revision = - a.revision.max(b.revision).checked_add(1).ok_or_else(|| { - GeometryError::LimitExceeded("planar Boolean revision overflow".into()) - })?; - out.validate()?; - let face_mappings = analytic_face_mappings(&out, [a, b]); - Ok(BooleanResult { - report: BooleanReport { - operation: BooleanOp::Subtraction, - quality: out.quality.clone(), - contacts: Vec::new(), - coincident: true, - face_mappings, - }, - brep: out, - }) -} - -pub(super) fn subtract_layered_extrusions( - host: &BrepEnvelope, - cutter: &BrepEnvelope, - id: String, -) -> Result { - host.validate()?; - let b = full_planar_extrusion(cutter)?; - if host - .geometry - .surfaces - .iter() - .any(|surface| !matches!(surface, SurfaceGeometry::Plane { .. })) - || host - .geometry - .curves - .iter() - .any(|curve| !matches!(curve, CurveGeometry::Line { .. })) - || host.solids.is_empty() - { - return Err(coverage()); - } - let accuracy = Accuracy { - geometric: host.accuracy.geometric.max(cutter.accuracy.geometric), - intersection: host.accuracy.intersection.max(cutter.accuracy.intersection), - tessellation: host.accuracy.tessellation.max(cutter.accuracy.tessellation), - exchange: host.accuracy.exchange.max(cutter.accuracy.exchange), - }; - let host_positions = host - .topology - .vertices - .iter() - .map(|vertex| b.frame.local(vertex.position)[2]) - .collect::>(); - let host_lo = host_positions.iter().copied().fold(f64::INFINITY, f64::min); - let host_hi = host_positions - .iter() - .copied() - .fold(f64::NEG_INFINITY, f64::max); - if !host_lo.is_finite() || host_hi - host_lo <= 4.0 * accuracy.geometric { - return Err(coverage()); - } - let frame = Frame3 { - origin: b.frame.point([0.0, 0.0, host_lo]), - ..b.frame - }; - for face in &host.topology.faces { - let SurfaceGeometry::Plane { frame: face_frame } = host - .geometry - .surfaces - .get(face.surface as usize) - .ok_or_else(coverage)? - else { - return Err(coverage()); - }; - let alignment = dot(face_frame.z, frame.z).abs(); - if alignment > 1.0e-10 && alignment < 1.0 - 1.0e-10 { - return Err(coverage()); - } - } - let b_contours = contours(&b, frame); - let b_lo = dot(sub(b.frame.origin, frame.origin), frame.z); - let b_hi = b_lo + b.height; - let host_height = host_hi - host_lo; - if b_hi <= accuracy.geometric || b_lo >= host_height - accuracy.geometric { - return Err(coverage()); - } - let mut levels = host_positions - .iter() - .map(|level| level - host_lo) - .collect::>(); - for level in [b_lo, b_hi] { - if level > accuracy.geometric && level < host_height - accuracy.geometric { - levels.push(level); - } - } - levels.sort_by(f64::total_cmp); - levels.dedup_by(|left, right| (*left - *right).abs() <= accuracy.geometric); - let layers = levels - .windows(2) - .map(|span| -> Result, GeometryError> { - let middle = (span[0] + span[1]) / 2.0; - let host_regions = slice_regions(host, frame, middle, accuracy.intersection)?; - Ok(boolean_oriented_regions( - ®ion_contours(&host_regions), - if middle > b_lo && middle < b_hi { - &b_contours - } else { - &[] - }, - PlanarBooleanOp::Subtraction, - accuracy.intersection, - )) - }) - .collect::, _>>()?; - let mut exposed_up = Vec::new(); - let mut exposed_down = Vec::new(); - for index in 1..layers.len() { - let below = region_contours(&layers[index - 1]); - let above = region_contours(&layers[index]); - exposed_up.push(boolean_oriented_regions( - &below, - &above, - PlanarBooleanOp::Subtraction, - accuracy.intersection, - )); - exposed_down.push(boolean_oriented_regions( - &above, - &below, - PlanarBooleanOp::Subtraction, - accuracy.intersection, - )); - } - let mut vertices = Vec::new(); - for layer in &layers { - region_points(layer, &mut vertices); - } - for regions in exposed_up.iter().chain(&exposed_down) { - region_points(regions, &mut vertices); - } - let mut facets = Facets::new(id, accuracy)?; - cap( - &mut facets, - frame, - 0.0, - false, - &layers[0], - &vertices, - &[(host, FaceRole::Split)], - )?; - for (index, layer) in layers.iter().enumerate() { - side_faces( - &mut facets, - host, - cutter, - frame, - layer, - &vertices, - levels[index], - levels[index + 1], - )?; - if index + 1 < layers.len() { - cap( - &mut facets, - frame, - levels[index + 1], - true, - &exposed_up[index], - &vertices, - &[(host, FaceRole::Split), (cutter, FaceRole::Cut)], - )?; - cap( - &mut facets, - frame, - levels[index + 1], - false, - &exposed_down[index], - &vertices, - &[(host, FaceRole::Split), (cutter, FaceRole::Cut)], - )?; - } - } - cap( - &mut facets, - frame, - host_height, - true, - layers.last().ok_or_else(coverage)?, - &vertices, - &[(host, FaceRole::Split)], - )?; - finish(facets, host, cutter) -} - -struct PlanarFace { - id: u32, - frame: Frame3, - polygon: Vec, - holes: Vec>, -} - -fn planar_faces( - brep: &BrepEnvelope, - tolerance: f64, -) -> Result>, GeometryError> { - if brep.solids.is_empty() - || brep - .geometry - .curves - .iter() - .any(|curve| !matches!(curve, CurveGeometry::Line { .. })) - { - return Ok(None); - } - let mut faces = Vec::with_capacity(brep.topology.faces.len()); - for face in &brep.topology.faces { - let Some(SurfaceGeometry::Plane { frame: surface }) = - brep.geometry.surfaces.get(face.surface as usize) - else { - return Ok(None); - }; - let frame = if face.sense == Orientation::Forward { - *surface - } else { - Frame3 { - origin: surface.origin, - x: surface.x, - y: scale(surface.y, -1.0), - z: scale(surface.z, -1.0), - } - }; - let polygon = loop_positions(brep, face.trim.outer)?; - if polygon.len() < 3 { - return Ok(None); - } - let mut holes = face - .trim - .holes - .iter() - .map(|&loop_id| loop_positions(brep, loop_id)) - .collect::, _>>()?; - let orient = |mut ring: Vec, outer: bool| -> Option> { - let area = ring - .iter() - .enumerate() - .map(|(index, point)| { - let a = frame.local(*point); - let b = frame.local(ring[(index + 1) % ring.len()]); - a[0] * b[1] - b[0] * a[1] - }) - .sum::(); - if ring.len() < 3 || area.abs() <= tolerance * tolerance { - return None; - } - if (area > 0.0) != outer { - ring.reverse(); - } - Some(ring) - }; - let Some(polygon) = orient(polygon, true) else { - return Ok(None); - }; - for hole in &mut holes { - let Some(oriented) = orient(std::mem::take(hole), false) else { - return Ok(None); - }; - *hole = oriented; - } - faces.push(PlanarFace { - id: face.id, - frame, - polygon, - holes, - }); - } - Ok(Some(faces)) -} - -fn convex_faces( - brep: &BrepEnvelope, - tolerance: f64, -) -> Result>, GeometryError> { - let Some(faces) = planar_faces(brep, tolerance)? else { - return Ok(None); - }; - if brep.solids.len() != 1 - || brep.topology.shells.len() != 1 - || faces.iter().any(|face| !face.holes.is_empty()) - { - return Ok(None); - } - for face in &faces { - if brep - .topology - .vertices - .iter() - .any(|vertex| dot(face.frame.z, sub(vertex.position, face.frame.origin)) > tolerance) - { - return Ok(None); - } - } - Ok(Some(faces)) -} - -fn planar_section_segments( - brep: &BrepEnvelope, - faces: &[PlanarFace], - section: Frame3, - tolerance: f64, -) -> Result, GeometryError> { - let mut segments = Vec::<(Pt2, Pt2)>::new(); - for source in faces { - let direction = cross(source.frame.z, section.z); - if norm(direction) <= 1.0e-10 { - continue; - } - let direction = unit(direction)?; - let mut crossings = Vec::new(); - for ring in std::iter::once(&source.polygon).chain(&source.holes) { - for index in 0..ring.len() { - let from = ring[index]; - let to = ring[(index + 1) % ring.len()]; - let from_distance = dot(section.z, sub(from, section.origin)); - let to_distance = dot(section.z, sub(to, section.origin)); - if from_distance.abs() <= tolerance { - crossings.push(from); - } - if (from_distance < -tolerance && to_distance > tolerance) - || (from_distance > tolerance && to_distance < -tolerance) - { - let fraction = from_distance / (from_distance - to_distance); - crossings.push([ - from[0] + fraction * (to[0] - from[0]), - from[1] + fraction * (to[1] - from[1]), - from[2] + fraction * (to[2] - from[2]), - ]); - } - } - } - crossings.sort_by(|left, right| dot(*left, direction).total_cmp(&dot(*right, direction))); - crossings.dedup_by(|left, right| norm(sub(*left, *right)) <= tolerance); - let face = &brep.topology.faces[source.id as usize]; - let SurfaceGeometry::Plane { frame: surface } = - &brep.geometry.surfaces[face.surface as usize] - else { - return Err(coverage()); - }; - for pair in crossings.windows(2) { - if norm(sub(pair[0], pair[1])) <= tolerance { - continue; - } - let midpoint = scale( - [ - pair[0][0] + pair[1][0], - pair[0][1] + pair[1][1], - pair[0][2] + pair[1][2], - ], - 0.5, - ); - let local = surface.local(midpoint); - let on_trim_edge = std::iter::once(&source.polygon) - .chain(&source.holes) - .any(|ring| { - ring.iter().enumerate().any(|(index, &from)| { - let to = ring[(index + 1) % ring.len()]; - let edge = sub(to, from); - let length_squared = dot(edge, edge); - if length_squared <= tolerance * tolerance { - return false; - } - let fraction = dot(sub(midpoint, from), edge) / length_squared; - fraction >= -tolerance - && fraction <= 1.0 + tolerance - && norm(sub(sub(midpoint, from), scale(edge, fraction))) <= tolerance - }) - }); - if face_contains_uv(brep, face, [local[0], local[1]])? != Some(true) && !on_trim_edge { - continue; - } - let a = section.local(pair[0]); - let b = section.local(pair[1]); - let mut from = Pt2::new(a[0], a[1]); - let mut to = Pt2::new(b[0], b[1]); - let edge = sub(pair[1], pair[0]); - if dot(cross(edge, section.z), source.frame.z) < 0.0 { - std::mem::swap(&mut from, &mut to); - } - segments.push((from, to)); - } - } - Ok(segments) -} - -fn clip_convex_face(polygon: &[Point3], planes: &[PlanarFace], tolerance: f64) -> Vec { - let mut result = polygon.to_vec(); - for plane in planes { - if result.len() < 3 { - return Vec::new(); - } - let mut next = Vec::new(); - let mut previous = *result.last().unwrap(); - let mut previous_distance = dot(plane.frame.z, sub(previous, plane.frame.origin)); - for ¤t in &result { - let distance = dot(plane.frame.z, sub(current, plane.frame.origin)); - let previous_inside = previous_distance <= tolerance; - let current_inside = distance <= tolerance; - if previous_inside != current_inside { - let fraction = previous_distance / (previous_distance - distance); - next.push([ - previous[0] + fraction * (current[0] - previous[0]), - previous[1] + fraction * (current[1] - previous[1]), - previous[2] + fraction * (current[2] - previous[2]), - ]); - } - if current_inside { - next.push(current); - } - previous = current; - previous_distance = distance; - } - next.dedup_by(|left, right| norm(sub(*left, *right)) <= tolerance / 4.0); - if next.len() > 1 && norm(sub(next[0], *next.last().unwrap())) <= tolerance / 4.0 { - next.pop(); - } - result = next; - } - if result.len() < 3 { - Vec::new() - } else { - result - } -} - -fn convex_section_polygon( - cutter: &BrepEnvelope, - faces: &[PlanarFace], - section: Frame3, - tolerance: f64, -) -> Vec { - let mut limits = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - for vertex in &cutter.topology.vertices { - let local = section.local(vertex.position); - for axis in 0..2 { - limits[axis][0] = limits[axis][0].min(local[axis]); - limits[axis][1] = limits[axis][1].max(local[axis]); - } - } - if limits.iter().any(|bound| !bound[0].is_finite()) { - return Vec::new(); - } - let span = (limits[0][1] - limits[0][0]) - .max(limits[1][1] - limits[1][0]) - .max(1.0); - let corners = [ - [limits[0][0] - span, limits[1][0] - span], - [limits[0][1] + span, limits[1][0] - span], - [limits[0][1] + span, limits[1][1] + span], - [limits[0][0] - span, limits[1][1] + span], - ] - .map(|point| section.point([point[0], point[1], 0.0])); - let clipped = clip_convex_face(&corners, faces, tolerance); - if clipped.len() < 3 { - return Vec::new(); - } - let result = clipped - .iter() - .map(|point| { - let local = section.local(*point); - Pt2::new(local[0], local[1]) - }) - .collect::>(); - let twice_area = result - .iter() - .enumerate() - .map(|(index, point)| { - let next = result[(index + 1) % result.len()]; - point.x * next.z - next.x * point.z - }) - .sum::(); - if twice_area.abs() <= tolerance * tolerance { - Vec::new() - } else { - result - } -} - -fn split_planar_ring(ring: &[Point3], vertices: &[Point3], tolerance: f64) -> Vec { - let mut split = Vec::new(); - for index in 0..ring.len() { - let from = ring[index]; - let to = ring[(index + 1) % ring.len()]; - let delta = sub(to, from); - let length = norm(delta); - if length <= tolerance { - continue; - } - split.push(from); - let mut interior = Vec::new(); - for &point in vertices { - let offset = sub(point, from); - let fraction = dot(offset, delta) / (length * length); - if fraction > tolerance / length - && fraction < 1.0 - tolerance / length - && norm(sub(offset, scale(delta, fraction))) <= tolerance - { - interior.push((fraction, point)); - } - } - interior.sort_by(|left, right| left.0.total_cmp(&right.0)); - for (_, point) in interior { - if split - .last() - .is_none_or(|last| norm(sub(*last, point)) > tolerance) - { - split.push(point); - } - } - } - split -} - -struct PlanarFacet { - key: String, - frame: Frame3, - outer: Vec, - holes: Vec>, - provenance: FaceProvenance, -} - -pub(super) fn subtract_planar_polyhedra( - host: &BrepEnvelope, - cutter: &BrepEnvelope, - id: String, -) -> Result { - let accuracy = Accuracy { - geometric: host.accuracy.geometric.max(cutter.accuracy.geometric), - intersection: host.accuracy.intersection.max(cutter.accuracy.intersection), - tessellation: host.accuracy.tessellation.max(cutter.accuracy.tessellation), - exchange: host.accuracy.exchange.max(cutter.accuracy.exchange), - }; - let Some(host_faces) = planar_faces(host, accuracy.intersection)? else { - return Err(coverage()); - }; - let Some(cutter_faces) = planar_faces(cutter, accuracy.intersection)? else { - return Err(coverage()); - }; - let convex_cutter_faces = convex_faces(cutter, accuracy.intersection)?; - if cutter.topology.vertices.iter().all(|vertex| { - matches!( - super::query::classify_point(host, vertex.position), - Ok(super::query::PointClassification::Inside) - ) - }) || host.topology.vertices.iter().all(|vertex| { - matches!( - super::query::classify_point(cutter, vertex.position), - Ok(super::query::PointClassification::Inside) - ) - }) { - return Err(coverage()); - } - let project = |points: &[Point3], frame: Frame3| { - points - .iter() - .map(|point| { - let local = frame.local(*point); - Pt2::new(local[0], local[1]) - }) - .collect::>() - }; - let mut pieces = Vec::new(); - for face in &host_faces { - let mut base = vec![project(&face.polygon, face.frame)]; - base.extend(face.holes.iter().map(|ring| project(ring, face.frame))); - let regions = if let Some(convex_faces) = &convex_cutter_faces { - let section = - convex_section_polygon(cutter, convex_faces, face.frame, accuracy.intersection); - let cut = if section.is_empty() { - Vec::new() - } else { - vec![section] - }; - boolean_oriented_regions( - &base, - &cut, - PlanarBooleanOp::Subtraction, - accuracy.intersection, - ) - } else { - let mut edges = - planar_section_segments(cutter, &cutter_faces, face.frame, accuracy.intersection)?; - let coplanar = cutter_faces - .iter() - .filter(|other| { - dot(other.frame.z, face.frame.z).abs() >= 1.0 - 1.0e-10 - && dot(other.frame.z, sub(face.frame.origin, other.frame.origin)).abs() - <= accuracy.intersection - }) - .flat_map(|other| { - std::iter::once(&other.polygon) - .chain(other.holes.iter()) - .map(|ring| project(ring, face.frame)) - }) - .collect::>(); - for ring in base.iter().chain(&coplanar) { - edges.extend( - ring.iter() - .enumerate() - .map(|(index, &point)| (point, ring[(index + 1) % ring.len()])), - ); - } - let failure = RefCell::new(None); - let regions = regions_from_edges_by(&edges, accuracy.intersection, |point| { - if base - .iter() - .map(|ring| winding_number2(point, ring)) - .sum::() - == 0 - { - return false; - } - let position = face.frame.point([point.x, point.z, 0.0]); - let classification = super::query::classify_point(cutter, position); - let classification = match classification { - Ok(super::query::PointClassification::Boundary) => { - super::query::classify_point( - cutter, - sub( - position, - scale( - face.frame.z, - accuracy.geometric.max(accuracy.intersection) * 4.0, - ), - ), - ) - } - other => other, - }; - match classification { - Ok(super::query::PointClassification::Outside) => true, - Ok(super::query::PointClassification::Inside) => false, - Ok(super::query::PointClassification::Boundary) - | Ok(super::query::PointClassification::Unknown) => { - *failure.borrow_mut() = Some(coverage()); - false - } - Err(error) => { - *failure.borrow_mut() = Some(error); - false - } - } - }); - if let Some(error) = failure.into_inner() { - return Err(error); - } - regions - }; - for region in regions { - pieces.push(PlanarFacet { - key: format!("planar-host-{}", face.id), - frame: face.frame, - outer: region - .outer - .iter() - .map(|point| face.frame.point([point.x, point.z, 0.0])) - .collect(), - holes: region - .holes - .iter() - .map(|ring| { - ring.iter() - .map(|point| face.frame.point([point.x, point.z, 0.0])) - .collect() - }) - .collect(), - provenance: FaceProvenance { - sources: vec![brep_face_source(host, face.id)], - role: FaceRole::Split, - reversed: false, - }, - }); - } - } - for face in &cutter_faces { - let mut edges = - planar_section_segments(host, &host_faces, face.frame, accuracy.intersection)?; - let coplanar = host_faces - .iter() - .filter(|other| { - dot(other.frame.z, face.frame.z).abs() >= 1.0 - 1.0e-10 - && dot(other.frame.z, sub(face.frame.origin, other.frame.origin)).abs() - <= accuracy.intersection - }) - .flat_map(|other| { - std::iter::once(&other.polygon) - .chain(other.holes.iter()) - .map(|ring| project(ring, face.frame)) - }) - .collect::>(); - let mut cutter_region = vec![project(&face.polygon, face.frame)]; - cutter_region.extend(face.holes.iter().map(|ring| project(ring, face.frame))); - for ring in cutter_region.iter().chain(&coplanar) { - edges.extend( - ring.iter() - .enumerate() - .map(|(index, &point)| (point, ring[(index + 1) % ring.len()])), - ); - } - let failure = RefCell::new(None); - let regions = regions_from_edges_by(&edges, accuracy.intersection, |point| { - if cutter_region - .iter() - .map(|ring| winding_number2(point, ring)) - .sum::() - == 0 - || coplanar - .iter() - .map(|ring| winding_number2(point, ring)) - .sum::() - != 0 - { - return false; - } - match super::query::classify_point(host, face.frame.point([point.x, point.z, 0.0])) { - Ok(super::query::PointClassification::Inside) - | Ok(super::query::PointClassification::Boundary) => true, - Ok(super::query::PointClassification::Outside) => false, - Ok(super::query::PointClassification::Unknown) => { - *failure.borrow_mut() = Some(coverage()); - false - } - Err(error) => { - *failure.borrow_mut() = Some(error); - false - } - } - }); - if let Some(error) = failure.into_inner() { - return Err(error); - } - let reversed = Frame3 { - origin: face.frame.origin, - x: face.frame.x, - y: scale(face.frame.y, -1.0), - z: scale(face.frame.z, -1.0), - }; - for region in regions { - let mut outer = region - .outer - .iter() - .map(|point| face.frame.point([point.x, point.z, 0.0])) - .collect::>(); - outer.reverse(); - let holes = region - .holes - .iter() - .map(|ring| { - let mut points = ring - .iter() - .map(|point| face.frame.point([point.x, point.z, 0.0])) - .collect::>(); - points.reverse(); - points - }) - .collect(); - pieces.push(PlanarFacet { - key: format!("planar-cut-{}", face.id), - frame: reversed, - outer, - holes, - provenance: FaceProvenance { - sources: vec![brep_face_source(cutter, face.id)], - role: FaceRole::Cut, - reversed: true, - }, - }); - } - } - if pieces.is_empty() { - return Err(coverage()); - } - let vertices = pieces - .iter() - .flat_map(|piece| { - piece - .outer - .iter() - .chain(piece.holes.iter().flatten()) - .copied() - }) - .collect::>(); - let mut facets = Facets::new(id, accuracy)?; - for piece in pieces { - let outer = split_planar_ring(&piece.outer, &vertices, accuracy.geometric / 4.0); - let holes = piece - .holes - .iter() - .map(|ring| split_planar_ring(ring, &vertices, accuracy.geometric / 4.0)) - .collect(); - facets.face(&piece.key, piece.frame, outer, holes, piece.provenance)?; - } - let mut result = finish(facets, host, cutter)?; - result.report.coincident = false; - Ok(result) -} diff --git a/main/opengeometry/src/analytic/primitives.rs b/main/opengeometry/src/analytic/primitives.rs deleted file mode 100644 index 55946be..0000000 --- a/main/opengeometry/src/analytic/primitives.rs +++ /dev/null @@ -1,5022 +0,0 @@ -use super::{ - face_intersection::{intersect_faces, FaceView, IntersectionBranch, IntersectionGraph}, - geometry::{add, cross, dot, norm, scale, sub, unit}, - intersection::{IntersectionDefinition, TraceAnchor}, - topology::*, - Curve, CurveGeometry, Frame3, GeometryError, Point3, Surface, SurfaceGeometry, -}; -use crate::math::interval::Interval; -use serde::Deserialize; -use std::collections::BTreeMap; - -pub(super) struct Use { - edge: u32, - from: u32, - to: u32, - sense: Orientation, - pcurve: PcurveGeometry, -} -pub(super) struct Builder { - pub(super) brep: BrepEnvelope, -} -impl Builder { - pub(super) fn new(id: String, accuracy: Accuracy) -> Result { - Ok(Self { - brep: BrepEnvelope::new(id, accuracy)?, - }) - } - pub(super) fn vertex(&mut self, p: Point3) -> u32 { - let id = self.brep.topology.vertices.len() as u32; - self.brep.topology.vertices.push(Vertex { - id, - position: p, - outgoing_halfedge: None, - tolerance: self.brep.accuracy.geometric, - }); - id - } - pub(super) fn edge(&mut self, curve: CurveGeometry, range: Interval, seam: bool) -> u32 { - let curve_id = self.brep.geometry.curves.len() as u32; - self.brep.geometry.curves.push(curve); - self.edge_geometry( - EdgeGeometry::Curve { - curve: curve_id, - range, - }, - seam, - ) - } - pub(super) fn edge_geometry(&mut self, geometry: EdgeGeometry, seam: bool) -> u32 { - let id = self.brep.topology.edges.len() as u32; - self.brep.topology.edges.push(Edge { - id, - geometry, - halfedge: u32::MAX, - twin_halfedge: None, - tolerance: self.brep.accuracy.geometric, - chart_seam: seam, - }); - id - } - fn face_loop( - &mut self, - face: u32, - is_hole: bool, - uses: Vec, - ) -> Result { - let loop_id = self.brep.topology.loops.len() as u32; - let start = self.brep.topology.halfedges.len() as u32; - let count = uses.len() as u32; - if count == 0 { - return Err(GeometryError::InvalidTopology( - "empty primitive face".into(), - )); - } - for (i, u) in uses.into_iter().enumerate() { - let id = start + i as u32; - let pcurve = self.brep.geometry.pcurves.len() as u32; - self.brep.geometry.pcurves.push(u.pcurve); - let e = self - .brep - .topology - .edges - .get_mut(u.edge as usize) - .ok_or_else(|| { - GeometryError::InvalidTopology("builder references missing edge".into()) - })?; - let twin = if e.halfedge == u32::MAX { - e.halfedge = id; - None - } else { - if e.twin_halfedge.is_some() { - return Err(GeometryError::InvalidTopology( - "nonmanifold primitive edge".into(), - )); - } - e.twin_halfedge = Some(id); - self.brep.topology.halfedges[e.halfedge as usize].twin = Some(id); - Some(e.halfedge) - }; - self.brep.topology.halfedges.push(HalfEdge { - id, - from: u.from, - to: u.to, - twin, - next: Some(start + (i as u32 + 1) % count), - prev: Some(start + (i as u32 + count - 1) % count), - edge: u.edge, - face: Some(face), - loop_ref: Some(loop_id), - wire_ref: None, - geometry_use: HalfEdgeGeometryUse { - sense: u.sense, - pcurve: Some(pcurve), - periodic_lift: [0; 2], - }, - }); - if self.brep.topology.vertices[u.from as usize] - .outgoing_halfedge - .is_none() - { - self.brep.topology.vertices[u.from as usize].outgoing_halfedge = Some(id); - } - } - self.brep.topology.loops.push(Loop { - id: loop_id, - start_halfedge: start, - face_ref: face, - is_hole, - }); - Ok(loop_id) - } - pub(super) fn face( - &mut self, - key: &str, - surface: SurfaceGeometry, - bounds: [[f64; 2]; 2], - uses: Vec, - ) -> Result<(), GeometryError> { - self.face_with_holes(key, surface, bounds, uses, Vec::new()) - } - pub(super) fn face_with_holes( - &mut self, - key: &str, - surface: SurfaceGeometry, - bounds: [[f64; 2]; 2], - outer_uses: Vec, - hole_uses: Vec>, - ) -> Result<(), GeometryError> { - let surface_id = self.brep.geometry.surfaces.len() as u32; - self.brep.geometry.surfaces.push(surface); - let face = self.brep.topology.faces.len() as u32; - let outer = self.face_loop(face, false, outer_uses)?; - let mut holes = Vec::with_capacity(hole_uses.len()); - for uses in hole_uses { - holes.push(self.face_loop(face, true, uses)?); - } - self.brep.topology.faces.push(Face { - id: face, - key: key.into(), - surface: surface_id, - sense: Orientation::Forward, - trim: TrimRegion { - chart: 0, - uv_bounds: [ - Interval::new(bounds[0][0], bounds[0][1])?, - Interval::new(bounds[1][0], bounds[1][1])?, - ], - outer, - holes, - }, - shell_ref: Some(0), - provenance: FaceProvenance { - sources: vec![FaceSource { - entity: self.brep.id.clone(), - body: self.brep.id.clone(), - key: key.into(), - face, - }], - role: FaceRole::Authored, - reversed: false, - }, - }); - Ok(()) - } - pub(super) fn finish(mut self) -> Result { - let faces = (0..self.brep.topology.faces.len() as u32).collect(); - self.brep.topology.shells.push(Shell { - id: 0, - faces, - is_closed: true, - }); - self.brep.solids.push(SolidRegion { - outer_shell: 0, - cavity_shells: Vec::new(), - }); - self.brep.validate()?; - Ok(self.brep) - } -} - -pub(super) fn uv_line(origin: [f64; 2], direction: [f64; 2]) -> PcurveGeometry { - PcurveGeometry::Line2 { origin, direction } -} -pub(super) fn boundary( - edge: u32, - from: u32, - to: u32, - sense: Orientation, - pcurve: PcurveGeometry, -) -> Use { - Use { - edge, - from, - to, - sense, - pcurve, - } -} -fn dimensions(values: &[f64]) -> Result<(), GeometryError> { - if values.iter().any(|&x| !x.is_finite() || x <= 0.0) { - return Err(GeometryError::InvalidGeometry( - "primitive dimensions must be positive and finite".into(), - )); - } - Ok(()) -} - -pub fn arc_wire( - id: String, - curve: CurveGeometry, - start_angle: f64, - sweep_angle: f64, - accuracy: Accuracy, -) -> Result { - curve.validate()?; - accuracy.validate()?; - let radius = match &curve { - CurveGeometry::Circle { radius, .. } => *radius, - CurveGeometry::Ellipse { minor_radius, .. } => *minor_radius, - _ => { - return Err(GeometryError::UnsupportedGeometry( - "arc wire requires a circle or ellipse".into(), - )) - } - }; - if !start_angle.is_finite() - || !sweep_angle.is_finite() - || sweep_angle == 0.0 - || sweep_angle.abs() > std::f64::consts::TAU - { - return Err(GeometryError::InvalidGeometry( - "arc angles require a finite, nonzero sweep of at most one turn".into(), - )); - } - let end = start_angle + sweep_angle; - let range = Interval::new(start_angle.min(end), start_angle.max(end))?; - if range.width() * radius <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "arc span is below geometric resolution".into(), - )); - } - let mut builder = Builder::new(id, accuracy)?; - let closed = sweep_angle.abs() == std::f64::consts::TAU; - let from = builder.vertex(curve.elementary_point(start_angle)?); - let to = if closed { - from - } else { - builder.vertex(curve.elementary_point(end)?) - }; - let edge = builder.edge(curve, range, false); - builder.brep.topology.edges[edge as usize].halfedge = 0; - builder.brep.topology.vertices[from as usize].outgoing_halfedge = Some(0); - builder.brep.topology.halfedges.push(HalfEdge { - id: 0, - from, - to, - twin: None, - next: closed.then_some(0), - prev: closed.then_some(0), - edge, - face: None, - loop_ref: None, - wire_ref: Some(0), - geometry_use: HalfEdgeGeometryUse { - sense: if sweep_angle > 0.0 { - Orientation::Forward - } else { - Orientation::Reverse - }, - pcurve: None, - periodic_lift: [0; 2], - }, - }); - builder.brep.topology.wires.push(Wire { - id: 0, - start_halfedge: 0, - is_closed: closed, - }); - builder.brep.validate()?; - Ok(builder.brep) -} - -fn section( - id: String, - frame: Frame3, - surface: SurfaceGeometry, - r0: f64, - r1: f64, - z0: f64, - z1: f64, - accuracy: Accuracy, -) -> Result { - frame.validate()?; - surface.validate()?; - dimensions(&[r1, z1 - z0])?; - if !r0.is_finite() || r0 < 0.0 { - return Err(GeometryError::InvalidGeometry( - "negative lower radius".into(), - )); - } - let mut b = Builder::new(id, accuracy)?; - let low = b.vertex(frame.point([r0, 0.0, z0])); - let high = b.vertex(frame.point([r1, 0.0, z1])); - let tau = std::f64::consts::TAU; - let mut f0 = frame; - f0.origin = frame.point([0.0, 0.0, z0]); - let mut f1 = frame; - f1.origin = frame.point([0.0, 0.0, z1]); - let bottom = if r0 == 0.0 { - b.edge_geometry(EdgeGeometry::Collapsed { vertex: low }, true) - } else { - b.edge( - CurveGeometry::Circle { - frame: f0, - radius: r0, - }, - Interval::new(0.0, tau)?, - false, - ) - }; - let top = b.edge( - CurveGeometry::Circle { - frame: f1, - radius: r1, - }, - Interval::new(0.0, tau)?, - false, - ); - let p0 = frame.point([r0, 0.0, z0]); - let p1 = frame.point([r1, 0.0, z1]); - let length = norm(sub(p1, p0)); - let seam = b.edge( - CurveGeometry::Line { - origin: p0, - direction: unit(sub(p1, p0))?, - }, - Interval::new(0.0, length)?, - true, - ); - b.face( - "lateral", - surface, - [[0.0, tau], [z0, z1]], - vec![ - boundary( - bottom, - low, - low, - Orientation::Forward, - if r0 == 0.0 { - uv_line([0.0, z0], [tau, 0.0]) - } else { - uv_line([0.0, z0], [1.0, 0.0]) - }, - ), - boundary( - seam, - low, - high, - Orientation::Forward, - uv_line([tau, z0], [0.0, (z1 - z0) / length]), - ), - boundary( - top, - high, - high, - Orientation::Reverse, - uv_line([0.0, z1], [1.0, 0.0]), - ), - boundary( - seam, - high, - low, - Orientation::Reverse, - uv_line([0.0, z0], [0.0, (z1 - z0) / length]), - ), - ], - )?; - if r0 > 0.0 { - f0.y = scale(f0.y, -1.0); - f0.z = scale(f0.z, -1.0); - b.face( - "lower_cap", - SurfaceGeometry::Plane { frame: f0 }, - [[-r0, r0]; 2], - vec![boundary( - bottom, - low, - low, - Orientation::Reverse, - PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [r0, 0.0], - axis_b: [0.0, -r0], - }, - )], - )?; - } - b.face( - "upper_cap", - SurfaceGeometry::Plane { frame: f1 }, - [[-r1, r1]; 2], - vec![boundary( - top, - high, - high, - Orientation::Forward, - PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [r1, 0.0], - axis_b: [0.0, r1], - }, - )], - )?; - b.finish() -} - -pub fn cylinder( - id: String, - frame: Frame3, - radius: f64, - height: f64, - accuracy: Accuracy, -) -> Result { - dimensions(&[radius, height])?; - section( - id, - frame, - SurfaceGeometry::Cylinder { frame, radius }, - radius, - radius, - 0.0, - height, - accuracy, - ) -} - -pub fn cylinder_sector( - id: String, - frame: Frame3, - radius: f64, - height: f64, - start_angle: f64, - sweep_angle: f64, - accuracy: Accuracy, -) -> Result { - frame.validate()?; - accuracy.validate()?; - dimensions(&[radius, height])?; - if !start_angle.is_finite() - || !sweep_angle.is_finite() - || sweep_angle == 0.0 - || sweep_angle.abs() > std::f64::consts::TAU - { - return Err(GeometryError::InvalidGeometry( - "cylinder-sector angles require a finite, nonzero sweep of at most one turn".into(), - )); - } - if (sweep_angle.abs() - std::f64::consts::TAU).abs() <= 64.0 * f64::EPSILON { - return cylinder(id, frame, radius, height, accuracy); - } - if radius * sweep_angle.abs() <= 4.0 * accuracy.geometric - || radius <= 4.0 * accuracy.geometric - || height <= 4.0 * accuracy.geometric - { - return Err(GeometryError::InvalidGeometry( - "cylinder-sector features are below geometric resolution".into(), - )); - } - - let end_angle = start_angle + sweep_angle; - let lo = start_angle.min(end_angle); - let hi = start_angle.max(end_angle); - let range = Interval::new(lo, hi)?; - let point = |angle: f64, elevation: f64| { - frame.point([radius * angle.cos(), radius * angle.sin(), elevation]) - }; - let radial = |angle: f64| frame.vector([angle.cos(), angle.sin(), 0.0]); - let tangent = |angle: f64| frame.vector([-angle.sin(), angle.cos(), 0.0]); - let mut b = Builder::new(id, accuracy)?; - let bottom_center = b.vertex(frame.origin); - let bottom_start = b.vertex(point(lo, 0.0)); - let bottom_end = b.vertex(point(hi, 0.0)); - let top_center = b.vertex(frame.point([0.0, 0.0, height])); - let top_start = b.vertex(point(lo, height)); - let top_end = b.vertex(point(hi, height)); - - let line_edge = |builder: &mut Builder, - origin: Point3, - direction: Point3, - length: f64| - -> Result { - Ok(builder.edge( - CurveGeometry::Line { origin, direction }, - Interval::new(0.0, length)?, - false, - )) - }; - let bottom_start_edge = line_edge(&mut b, frame.origin, radial(lo), radius)?; - let bottom_end_edge = line_edge(&mut b, frame.origin, radial(hi), radius)?; - let top_origin = frame.point([0.0, 0.0, height]); - let top_start_edge = line_edge(&mut b, top_origin, radial(lo), radius)?; - let top_end_edge = line_edge(&mut b, top_origin, radial(hi), radius)?; - let center_edge = line_edge(&mut b, frame.origin, frame.z, height)?; - let start_edge = line_edge(&mut b, point(lo, 0.0), frame.z, height)?; - let end_edge = line_edge(&mut b, point(hi, 0.0), frame.z, height)?; - let bottom_arc = b.edge(CurveGeometry::Circle { frame, radius }, range, false); - let mut top_frame = frame; - top_frame.origin = top_origin; - let top_arc = b.edge( - CurveGeometry::Circle { - frame: top_frame, - radius, - }, - range, - false, - ); - use Orientation::{Forward as F, Reverse as R}; - - b.face( - "lateral", - SurfaceGeometry::Cylinder { frame, radius }, - [[lo, hi], [0.0, height]], - vec![ - boundary( - bottom_arc, - bottom_start, - bottom_end, - F, - uv_line([0.0, 0.0], [1.0, 0.0]), - ), - boundary( - end_edge, - bottom_end, - top_end, - F, - uv_line([hi, 0.0], [0.0, 1.0]), - ), - boundary( - top_arc, - top_end, - top_start, - R, - uv_line([0.0, height], [1.0, 0.0]), - ), - boundary( - start_edge, - top_start, - bottom_start, - R, - uv_line([lo, 0.0], [0.0, 1.0]), - ), - ], - )?; - - let bottom_frame = Frame3 { - origin: frame.origin, - x: frame.x, - y: scale(frame.y, -1.0), - z: scale(frame.z, -1.0), - }; - let bottom_uses = [ - (bottom_end_edge, bottom_center, bottom_end, F), - (bottom_arc, bottom_end, bottom_start, R), - (bottom_start_edge, bottom_start, bottom_center, R), - ] - .into_iter() - .map(|(edge, from, to, sense)| plane_boundary(&b, bottom_frame, edge, from, to, sense)) - .collect::, _>>()?; - b.face( - "lower_cap", - SurfaceGeometry::Plane { - frame: bottom_frame, - }, - [[-radius, radius]; 2], - bottom_uses, - )?; - - let top_uses = [ - (top_start_edge, top_center, top_start, F), - (top_arc, top_start, top_end, F), - (top_end_edge, top_end, top_center, R), - ] - .into_iter() - .map(|(edge, from, to, sense)| plane_boundary(&b, top_frame, edge, from, to, sense)) - .collect::, _>>()?; - b.face( - "upper_cap", - SurfaceGeometry::Plane { frame: top_frame }, - [[-radius, radius]; 2], - top_uses, - )?; - - let start_frame = Frame3 { - origin: frame.origin, - x: radial(lo), - y: frame.z, - z: scale(tangent(lo), -1.0), - }; - let start_uses = [ - (bottom_start_edge, bottom_center, bottom_start, F), - (start_edge, bottom_start, top_start, F), - (top_start_edge, top_start, top_center, R), - (center_edge, top_center, bottom_center, R), - ] - .into_iter() - .map(|(edge, from, to, sense)| plane_boundary(&b, start_frame, edge, from, to, sense)) - .collect::, _>>()?; - b.face( - "start_radial", - SurfaceGeometry::Plane { frame: start_frame }, - [[0.0, radius], [0.0, height]], - start_uses, - )?; - - let end_frame = Frame3 { - origin: frame.origin, - x: frame.z, - y: radial(hi), - z: tangent(hi), - }; - let end_uses = [ - (center_edge, bottom_center, top_center, F), - (top_end_edge, top_center, top_end, F), - (end_edge, top_end, bottom_end, R), - (bottom_end_edge, bottom_end, bottom_center, R), - ] - .into_iter() - .map(|(edge, from, to, sense)| plane_boundary(&b, end_frame, edge, from, to, sense)) - .collect::, _>>()?; - b.face( - "end_radial", - SurfaceGeometry::Plane { frame: end_frame }, - [[0.0, height], [0.0, radius]], - end_uses, - )?; - b.finish() -} - -pub fn annular_cylinder( - id: String, - frame: Frame3, - inner_radius: f64, - outer_radius: f64, - height: f64, - accuracy: Accuracy, -) -> Result { - cylinder_with_circular_hole( - id, - frame, - frame, - inner_radius, - outer_radius, - height, - accuracy, - ) -} - -pub(super) fn cylinder_with_circular_hole( - id: String, - frame: Frame3, - inner_frame: Frame3, - inner_radius: f64, - outer_radius: f64, - height: f64, - accuracy: Accuracy, -) -> Result { - frame.validate()?; - inner_frame.validate()?; - accuracy.validate()?; - dimensions(&[inner_radius, outer_radius, height])?; - let inner_offset = sub(inner_frame.origin, frame.origin); - let axial_offset = dot(inner_offset, frame.z); - let radial_offset = sub(inner_offset, scale(frame.z, axial_offset)); - if norm(sub(inner_frame.z, frame.z)) > 1e-12 || axial_offset.abs() > accuracy.geometric { - return Err(GeometryError::InvalidGeometry( - "circular hole axis must share the outer cylinder span".into(), - )); - } - if norm(radial_offset) + inner_radius >= outer_radius { - return Err(GeometryError::InvalidGeometry( - "circular hole must lie strictly inside the outer cylinder".into(), - )); - } - if outer_radius - inner_radius - norm(radial_offset) <= 4.0 * accuracy.geometric - || height <= 4.0 * accuracy.geometric - { - return Err(GeometryError::UnresolvedIntersection( - "cylindrical shell is below geometric resolution".into(), - )); - } - - let mut b = Builder::new(id, accuracy)?; - let tau = std::f64::consts::TAU; - let range = Interval::new(0.0, tau)?; - let outer_low = b.vertex(frame.point([outer_radius, 0.0, 0.0])); - let outer_high = b.vertex(frame.point([outer_radius, 0.0, height])); - let inner_low = b.vertex(inner_frame.point([inner_radius, 0.0, 0.0])); - let inner_high = b.vertex(inner_frame.point([inner_radius, 0.0, height])); - let mut low_frame = frame; - let mut high_frame = frame; - high_frame.origin = frame.point([0.0, 0.0, height]); - let mut inner_high_frame = inner_frame; - inner_high_frame.origin = inner_frame.point([0.0, 0.0, height]); - - let outer_bottom = b.edge( - CurveGeometry::Circle { - frame: low_frame, - radius: outer_radius, - }, - range, - false, - ); - let outer_top = b.edge( - CurveGeometry::Circle { - frame: high_frame, - radius: outer_radius, - }, - range, - false, - ); - let inner_bottom = b.edge( - CurveGeometry::Circle { - frame: inner_frame, - radius: inner_radius, - }, - range, - false, - ); - let inner_top = b.edge( - CurveGeometry::Circle { - frame: inner_high_frame, - radius: inner_radius, - }, - range, - false, - ); - let outer_seam = b.edge( - CurveGeometry::Line { - origin: frame.point([outer_radius, 0.0, 0.0]), - direction: frame.z, - }, - Interval::new(0.0, height)?, - true, - ); - let inner_seam = b.edge( - CurveGeometry::Line { - origin: inner_frame.point([inner_radius, 0.0, 0.0]), - direction: inner_frame.z, - }, - Interval::new(0.0, height)?, - true, - ); - use Orientation::{Forward as F, Reverse as R}; - let lateral_uses = |bottom, top, seam, low, high| { - vec![ - boundary(bottom, low, low, F, uv_line([0.0, 0.0], [1.0, 0.0])), - boundary(seam, low, high, F, uv_line([tau, 0.0], [0.0, 1.0])), - boundary(top, high, high, R, uv_line([0.0, height], [1.0, 0.0])), - boundary(seam, high, low, R, uv_line([0.0, 0.0], [0.0, 1.0])), - ] - }; - b.face( - "outer", - SurfaceGeometry::Cylinder { - frame, - radius: outer_radius, - }, - [[0.0, tau], [0.0, height]], - lateral_uses(outer_bottom, outer_top, outer_seam, outer_low, outer_high), - )?; - b.face( - "inner", - SurfaceGeometry::Cylinder { - frame: inner_frame, - radius: inner_radius, - }, - [[0.0, tau], [0.0, height]], - lateral_uses(inner_bottom, inner_top, inner_seam, inner_low, inner_high), - )?; - b.brep.topology.faces[1].sense = R; - - low_frame.y = scale(low_frame.y, -1.0); - low_frame.z = scale(low_frame.z, -1.0); - let outer_lower = vec![plane_boundary( - &b, - low_frame, - outer_bottom, - outer_low, - outer_low, - R, - )?]; - let inner_lower = vec![plane_boundary( - &b, - low_frame, - inner_bottom, - inner_low, - inner_low, - R, - )?]; - b.face_with_holes( - "lower_cap", - SurfaceGeometry::Plane { frame: low_frame }, - [[-outer_radius, outer_radius]; 2], - outer_lower, - vec![inner_lower], - )?; - let outer_upper = vec![plane_boundary( - &b, high_frame, outer_top, outer_high, outer_high, F, - )?]; - let inner_upper = vec![plane_boundary( - &b, high_frame, inner_top, inner_high, inner_high, F, - )?]; - b.face_with_holes( - "upper_cap", - SurfaceGeometry::Plane { frame: high_frame }, - [[-outer_radius, outer_radius]; 2], - outer_upper, - vec![inner_upper], - )?; - b.finish() -} - -pub fn cuboid( - id: String, - frame: Frame3, - size: [f64; 3], - accuracy: Accuracy, -) -> Result { - frame.validate()?; - dimensions(&size)?; - if size.iter().any(|s| *s <= 4.0 * accuracy.geometric) { - return Err(GeometryError::UnresolvedIntersection( - "cuboid dimensions are below geometric resolution".into(), - )); - } - let mut builder = Builder::new(id, accuracy)?; - let points: Vec<_> = (0..8) - .map(|i| { - frame.point(std::array::from_fn(|axis| { - if i & (1 << axis) == 0 { - 0.0 - } else { - size[axis] - } - })) - }) - .collect(); - for point in &points { - builder.vertex(*point); - } - let axes = [frame.x, frame.y, frame.z]; - let mut edges = [[u32::MAX; 3]; 8]; - for i in 0..8 { - for axis in 0..3 { - if i & (1 << axis) == 0 { - edges[i][axis] = builder.edge( - CurveGeometry::Line { - origin: points[i], - direction: axes[axis], - }, - Interval::new(0.0, size[axis])?, - false, - ); - } - } - } - for (key, indices, normal) in [ - ("bottom", [0, 2, 3, 1], scale(frame.z, -1.0)), - ("top", [4, 5, 7, 6], frame.z), - ("left", [0, 4, 6, 2], scale(frame.x, -1.0)), - ("right", [1, 3, 7, 5], frame.x), - ("front", [0, 1, 5, 4], scale(frame.y, -1.0)), - ("back", [2, 6, 7, 3], frame.y), - ] { - let face_frame = Frame3::from_axis( - points[indices[0]], - normal, - sub(points[indices[1]], points[indices[0]]), - )?; - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - let mut uses = Vec::new(); - for i in 0..4 { - let from = indices[i]; - let to = indices[(i + 1) % 4]; - let axis = (from ^ to).trailing_zeros() as usize; - let edge = edges[from.min(to)][axis]; - uses.push(plane_boundary( - &builder, - face_frame, - edge, - from as u32, - to as u32, - if from < to { - Orientation::Forward - } else { - Orientation::Reverse - }, - )?); - let uv = face_frame.local(points[from]); - for j in 0..2 { - bounds[j][0] = bounds[j][0].min(uv[j]); - bounds[j][1] = bounds[j][1].max(uv[j]); - } - } - for bound in &mut bounds { - bound[0] -= accuracy.geometric; - bound[1] += accuracy.geometric; - } - builder.face( - key, - SurfaceGeometry::Plane { frame: face_frame }, - bounds, - uses, - )?; - } - builder.finish() -} - -/// Builds a closed, orientable solid whose boundary is described entirely by -/// planar polygon faces. Face loops must be ordered so their normals point out -/// of the material. Vertex and face identity are explicit; only the shared -/// edge uses are paired here. -pub fn planar_polyhedron( - id: String, - vertices: Vec, - faces: Vec>, - accuracy: Accuracy, -) -> Result { - accuracy.validate()?; - if vertices.len() < 4 || faces.len() < 4 { - return Err(GeometryError::InvalidGeometry( - "planar polyhedron requires at least four vertices and four faces".into(), - )); - } - if vertices - .iter() - .flat_map(|point| point.iter()) - .any(|coordinate| !coordinate.is_finite()) - { - return Err(GeometryError::InvalidGeometry( - "planar polyhedron vertices must be finite".into(), - )); - } - - let mut builder = Builder::new(id, accuracy)?; - for point in &vertices { - builder.vertex(*point); - } - - struct SharedEdge { - edge: u32, - from: u32, - to: u32, - uses: u8, - } - let mut edges = BTreeMap::<(u32, u32), SharedEdge>::new(); - for face in &faces { - if face.len() < 3 { - return Err(GeometryError::InvalidGeometry( - "planar polyhedron face requires at least three vertices".into(), - )); - } - for index in 0..face.len() { - let from = face[index]; - let to = face[(index + 1) % face.len()]; - if from == to || from as usize >= vertices.len() || to as usize >= vertices.len() { - return Err(GeometryError::InvalidGeometry( - "planar polyhedron face contains an invalid edge".into(), - )); - } - let key = (from.min(to), from.max(to)); - if let Some(shared) = edges.get_mut(&key) { - if shared.uses != 1 || shared.from != to || shared.to != from { - return Err(GeometryError::InvalidTopology( - "planar polyhedron edge uses must form one opposite-oriented pair".into(), - )); - } - shared.uses = 2; - } else { - let delta = sub(vertices[to as usize], vertices[from as usize]); - let length = norm(delta); - if length <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "planar polyhedron edge is below geometric resolution".into(), - )); - } - let edge = builder.edge( - CurveGeometry::Line { - origin: vertices[from as usize], - direction: scale(delta, 1.0 / length), - }, - Interval::new(0.0, length)?, - false, - ); - edges.insert( - key, - SharedEdge { - edge, - from, - to, - uses: 1, - }, - ); - } - } - } - if edges.values().any(|edge| edge.uses != 2) { - return Err(GeometryError::InvalidTopology( - "planar polyhedron boundary is open".into(), - )); - } - - for (face_index, face) in faces.iter().enumerate() { - let origin = vertices[face[0] as usize]; - let mut normal = [0.0; 3]; - for index in 0..face.len() { - let a = vertices[face[index] as usize]; - let b = vertices[face[(index + 1) % face.len()] as usize]; - normal[0] += (a[1] - b[1]) * (a[2] + b[2]); - normal[1] += (a[2] - b[2]) * (a[0] + b[0]); - normal[2] += (a[0] - b[0]) * (a[1] + b[1]); - } - let normal = unit(normal).map_err(|_| { - GeometryError::InvalidGeometry("planar polyhedron face has zero area".into()) - })?; - let first_edge = sub(vertices[face[1] as usize], origin); - let frame = Frame3::from_axis(origin, normal, first_edge)?; - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - let mut uses = Vec::with_capacity(face.len()); - for index in 0..face.len() { - let from = face[index]; - let to = face[(index + 1) % face.len()]; - let point = vertices[from as usize]; - let local = frame.local(point); - if local[2].abs() > 4.0 * accuracy.geometric { - return Err(GeometryError::InvalidGeometry( - "planar polyhedron face vertices are not coplanar".into(), - )); - } - for axis in 0..2 { - bounds[axis][0] = bounds[axis][0].min(local[axis]); - bounds[axis][1] = bounds[axis][1].max(local[axis]); - } - let shared = &edges[&(from.min(to), from.max(to))]; - uses.push(plane_boundary( - &builder, - frame, - shared.edge, - from, - to, - if shared.from == from { - Orientation::Forward - } else { - Orientation::Reverse - }, - )?); - } - for bound in &mut bounds { - bound[0] -= accuracy.geometric; - bound[1] += accuracy.geometric; - } - builder.face( - &format!("face-{face_index}"), - SurfaceGeometry::Plane { frame }, - bounds, - uses, - )?; - } - builder.finish() -} - -fn signed_area(points: &[[f64; 2]]) -> f64 { - points - .iter() - .zip(points.iter().cycle().skip(1)) - .map(|(a, b)| a[0] * b[1] - b[0] * a[1]) - .sum::() - * 0.5 -} - -fn orient_profile_loop(mut points: Vec<[f64; 2]>, ccw: bool) -> Vec<[f64; 2]> { - if (signed_area(&points) > 0.0) != ccw { - points.reverse(); - } - points -} - -fn segment_distance(point: [f64; 2], a: [f64; 2], b: [f64; 2]) -> f64 { - let edge = [b[0] - a[0], b[1] - a[1]]; - let length_squared = edge[0].mul_add(edge[0], edge[1] * edge[1]); - if length_squared == 0.0 { - return (point[0] - a[0]).hypot(point[1] - a[1]); - } - let t = (((point[0] - a[0]) * edge[0] + (point[1] - a[1]) * edge[1]) / length_squared) - .clamp(0.0, 1.0); - (point[0] - (a[0] + t * edge[0])).hypot(point[1] - (a[1] + t * edge[1])) -} - -fn loops_touch(a: &[[f64; 2]], b: &[[f64; 2]], tolerance: f64) -> bool { - use crate::geometry::poly2d::{segments_cross2, Pt2}; - - a.iter().any(|point| { - b.iter() - .zip(b.iter().cycle().skip(1)) - .any(|(from, to)| segment_distance(*point, *from, *to) <= tolerance) - }) || b.iter().any(|point| { - a.iter() - .zip(a.iter().cycle().skip(1)) - .any(|(from, to)| segment_distance(*point, *from, *to) <= tolerance) - }) || a.iter().zip(a.iter().cycle().skip(1)).any(|(a0, a1)| { - b.iter().zip(b.iter().cycle().skip(1)).any(|(b0, b1)| { - segments_cross2( - Pt2::new(a0[0], a0[1]), - Pt2::new(a1[0], a1[1]), - Pt2::new(b0[0], b0[1]), - Pt2::new(b1[0], b1[1]), - tolerance, - ) - }) - }) -} - -fn validate_profile_loop( - points: &[[f64; 2]], - tolerance: f64, - label: &str, -) -> Result<(), GeometryError> { - use crate::geometry::poly2d::{self_intersects2, Pt2}; - - if points.len() < 3 { - return Err(GeometryError::InvalidGeometry(format!( - "{label} requires at least three vertices" - ))); - } - if points - .iter() - .flatten() - .any(|coordinate| !coordinate.is_finite()) - { - return Err(GeometryError::InvalidGeometry(format!( - "{label} contains a non-finite coordinate" - ))); - } - let resolution = 4.0 * tolerance; - if points - .iter() - .zip(points.iter().cycle().skip(1)) - .any(|(a, b)| (a[0] - b[0]).hypot(a[1] - b[1]) <= resolution) - { - return Err(GeometryError::UnresolvedIntersection(format!( - "{label} contains an edge below geometric resolution" - ))); - } - if signed_area(points).abs() <= resolution * resolution { - return Err(GeometryError::UnresolvedIntersection(format!( - "{label} area is below geometric resolution" - ))); - } - let planar = points - .iter() - .map(|point| Pt2::new(point[0], point[1])) - .collect::>(); - if self_intersects2(&planar, tolerance) { - return Err(GeometryError::InvalidGeometry(format!( - "{label} self-intersects" - ))); - } - Ok(()) -} - -fn profile_point_inside(point: [f64; 2], loop_points: &[[f64; 2]]) -> bool { - use crate::geometry::poly2d::{point_in_ring2, Pt2}; - - let ring = loop_points - .iter() - .map(|point| Pt2::new(point[0], point[1])) - .collect::>(); - point_in_ring2(Pt2::new(point[0], point[1]), &ring) -} - -struct ExtrusionLoop { - points: Vec<[f64; 2]>, - bottom_vertices: Vec, - top_vertices: Vec, - bottom_edges: Vec, - top_edges: Vec, - vertical_edges: Vec, -} - -pub fn linear_extrusion( - id: String, - frame: Frame3, - outer: Vec<[f64; 2]>, - holes: Vec>, - height: f64, - accuracy: Accuracy, -) -> Result { - frame.validate()?; - accuracy.validate()?; - dimensions(&[height])?; - if height <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "extrusion height is below geometric resolution".into(), - )); - } - validate_profile_loop(&outer, accuracy.geometric, "outer profile")?; - for (index, hole) in holes.iter().enumerate() { - validate_profile_loop(hole, accuracy.geometric, &format!("profile hole {index}"))?; - if !profile_point_inside(hole[0], &outer) || loops_touch(&outer, hole, accuracy.geometric) { - return Err(GeometryError::InvalidGeometry(format!( - "profile hole {index} is not strictly inside the outer profile" - ))); - } - } - for first in 0..holes.len() { - for second in (first + 1)..holes.len() { - if loops_touch(&holes[first], &holes[second], accuracy.geometric) - || profile_point_inside(holes[first][0], &holes[second]) - || profile_point_inside(holes[second][0], &holes[first]) - { - return Err(GeometryError::InvalidGeometry(format!( - "profile holes {first} and {second} overlap or nest" - ))); - } - } - } - - let mut loops = Vec::with_capacity(holes.len() + 1); - loops.push(orient_profile_loop(outer, true)); - loops.extend( - holes - .into_iter() - .map(|hole| orient_profile_loop(hole, false)), - ); - let mut builder = Builder::new(id, accuracy)?; - let mut built = Vec::with_capacity(loops.len()); - for points in loops { - let bottom_vertices = points - .iter() - .map(|point| builder.vertex(frame.point([point[0], point[1], 0.0]))) - .collect::>(); - let top_vertices = points - .iter() - .map(|point| builder.vertex(frame.point([point[0], point[1], height]))) - .collect::>(); - let mut bottom_edges = Vec::with_capacity(points.len()); - let mut top_edges = Vec::with_capacity(points.len()); - let mut vertical_edges = Vec::with_capacity(points.len()); - for index in 0..points.len() { - let next = (index + 1) % points.len(); - let direction = unit(sub( - builder.brep.topology.vertices[bottom_vertices[next] as usize].position, - builder.brep.topology.vertices[bottom_vertices[index] as usize].position, - ))?; - let length = norm(sub( - builder.brep.topology.vertices[bottom_vertices[next] as usize].position, - builder.brep.topology.vertices[bottom_vertices[index] as usize].position, - )); - bottom_edges.push(builder.edge( - CurveGeometry::Line { - origin: - builder.brep.topology.vertices[bottom_vertices[index] as usize].position, - direction, - }, - Interval::new(0.0, length)?, - false, - )); - top_edges.push(builder.edge( - CurveGeometry::Line { - origin: builder.brep.topology.vertices[top_vertices[index] as usize].position, - direction, - }, - Interval::new(0.0, length)?, - false, - )); - vertical_edges.push(builder.edge( - CurveGeometry::Line { - origin: - builder.brep.topology.vertices[bottom_vertices[index] as usize].position, - direction: frame.z, - }, - Interval::new(0.0, height)?, - false, - )); - } - built.push(ExtrusionLoop { - points, - bottom_vertices, - top_vertices, - bottom_edges, - top_edges, - vertical_edges, - }); - } - - let cap_bounds = |loops: &[ExtrusionLoop]| { - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - for point in loops.iter().flat_map(|profile| &profile.points) { - for axis in 0..2 { - bounds[axis][0] = bounds[axis][0].min(point[axis]); - bounds[axis][1] = bounds[axis][1].max(point[axis]); - } - } - for bound in &mut bounds { - bound[0] -= accuracy.geometric; - bound[1] += accuracy.geometric; - } - bounds - }; - let top_bounds = cap_bounds(&built); - let bottom_bounds = [top_bounds[0], [-top_bounds[1][1], -top_bounds[1][0]]]; - let mut top_frame = frame; - top_frame.origin = frame.point([0.0, 0.0, height]); - let bottom_frame = Frame3 { - y: scale(frame.y, -1.0), - z: scale(frame.z, -1.0), - ..frame - }; - let cap_uses = |builder: &Builder, - profile: &ExtrusionLoop, - top: bool| - -> Result, GeometryError> { - let count = profile.points.len(); - (0..count) - .map(|offset| { - let index = if top { offset } else { count - 1 - offset }; - let next = (index + 1) % count; - if top { - plane_boundary( - builder, - top_frame, - profile.top_edges[index], - profile.top_vertices[index], - profile.top_vertices[next], - Orientation::Forward, - ) - } else { - plane_boundary( - builder, - bottom_frame, - profile.bottom_edges[index], - profile.bottom_vertices[next], - profile.bottom_vertices[index], - Orientation::Reverse, - ) - } - }) - .collect() - }; - let top_outer = cap_uses(&builder, &built[0], true)?; - let top_holes = built[1..] - .iter() - .map(|profile| cap_uses(&builder, profile, true)) - .collect::, _>>()?; - builder.face_with_holes( - "top", - SurfaceGeometry::Plane { frame: top_frame }, - top_bounds, - top_outer, - top_holes, - )?; - let bottom_outer = cap_uses(&builder, &built[0], false)?; - let bottom_holes = built[1..] - .iter() - .map(|profile| cap_uses(&builder, profile, false)) - .collect::, _>>()?; - builder.face_with_holes( - "bottom", - SurfaceGeometry::Plane { - frame: bottom_frame, - }, - bottom_bounds, - bottom_outer, - bottom_holes, - )?; - - for (loop_index, profile) in built.iter().enumerate() { - for index in 0..profile.points.len() { - let next = (index + 1) % profile.points.len(); - let bottom_from = profile.bottom_vertices[index]; - let bottom_to = profile.bottom_vertices[next]; - let top_from = profile.top_vertices[index]; - let top_to = profile.top_vertices[next]; - let origin = builder.brep.topology.vertices[bottom_from as usize].position; - let edge_direction = unit(sub( - builder.brep.topology.vertices[bottom_to as usize].position, - origin, - ))?; - let face_frame = Frame3::from_axis( - origin, - unit(cross(edge_direction, frame.z))?, - edge_direction, - )?; - let uses = [ - ( - profile.bottom_edges[index], - bottom_from, - bottom_to, - Orientation::Forward, - ), - ( - profile.vertical_edges[next], - bottom_to, - top_to, - Orientation::Forward, - ), - ( - profile.top_edges[index], - top_to, - top_from, - Orientation::Reverse, - ), - ( - profile.vertical_edges[index], - top_from, - bottom_from, - Orientation::Reverse, - ), - ] - .into_iter() - .map(|(edge, from, to, sense)| { - plane_boundary(&builder, face_frame, edge, from, to, sense) - }) - .collect::, _>>()?; - let edge_length = norm(sub( - builder.brep.topology.vertices[bottom_to as usize].position, - origin, - )); - let g = accuracy.geometric; - builder.face( - &format!("side-{loop_index}-{index}"), - SurfaceGeometry::Plane { frame: face_frame }, - [[-g, edge_length + g], [-g, height + g]], - uses, - )?; - } - } - builder.finish() -} - -#[derive(Clone, Debug, Deserialize)] -#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] -pub enum ProfileEdge { - Line { - from: [f64; 2], - to: [f64; 2], - }, - Arc { - center: [f64; 2], - radius: f64, - start_angle: f64, - sweep_angle: f64, - }, -} - -impl ProfileEdge { - fn endpoints(&self) -> ([f64; 2], [f64; 2]) { - match self { - Self::Line { from, to } => (*from, *to), - Self::Arc { - center, - radius, - start_angle, - sweep_angle, - } => { - let point = |angle: f64| { - [ - center[0] + radius * angle.cos(), - center[1] + radius * angle.sin(), - ] - }; - (point(*start_angle), point(start_angle + sweep_angle)) - } - } - } - - fn reversed(&self) -> Self { - match self { - Self::Line { from, to } => Self::Line { - from: *to, - to: *from, - }, - Self::Arc { - center, - radius, - start_angle, - sweep_angle, - } => Self::Arc { - center: *center, - radius: *radius, - start_angle: start_angle + sweep_angle, - sweep_angle: -sweep_angle, - }, - } - } - - fn signed_area_twice(&self) -> f64 { - let (from, to) = self.endpoints(); - match self { - Self::Line { .. } => from[0] * to[1] - to[0] * from[1], - Self::Arc { - center, - radius, - sweep_angle, - .. - } => { - radius * radius * sweep_angle + center[0] * (to[1] - from[1]) - - center[1] * (to[0] - from[0]) - } - } - } -} - -fn profile_edge_contains(edge: &ProfileEdge, point: [f64; 2], tolerance: f64) -> bool { - match edge { - ProfileEdge::Line { from, to } => { - let dx = to[0] - from[0]; - let dy = to[1] - from[1]; - let length = dx.hypot(dy); - let px = point[0] - from[0]; - let py = point[1] - from[1]; - (dx * py - dy * px).abs() <= tolerance * length - && px * dx + py * dy >= -tolerance * length - && px * dx + py * dy <= length * length + tolerance * length - } - ProfileEdge::Arc { - center, - radius, - start_angle, - sweep_angle, - } => { - let dx = point[0] - center[0]; - let dy = point[1] - center[1]; - if (dx.hypot(dy) - radius).abs() > tolerance { - return false; - } - let angle = dy.atan2(dx); - let travel = if *sweep_angle > 0.0 { - (angle - start_angle).rem_euclid(std::f64::consts::TAU) - } else { - (start_angle - angle).rem_euclid(std::f64::consts::TAU) - }; - travel <= sweep_angle.abs() + tolerance / radius - || std::f64::consts::TAU - travel <= tolerance / radius - } - } -} - -fn profile_edge_intersections(a: &ProfileEdge, b: &ProfileEdge, tolerance: f64) -> Vec<[f64; 2]> { - let mut candidates = Vec::new(); - let mut offer = |point: [f64; 2]| { - if point.iter().all(|value| value.is_finite()) - && profile_edge_contains(a, point, tolerance) - && profile_edge_contains(b, point, tolerance) - && !candidates.iter().any(|other: &[f64; 2]| { - (other[0] - point[0]).hypot(other[1] - point[1]) <= tolerance - }) - { - candidates.push(point); - } - }; - match (a, b) { - (ProfileEdge::Line { from: p, to: q }, ProfileEdge::Line { from: r, to: s }) => { - let d = [q[0] - p[0], q[1] - p[1]]; - let e = [s[0] - r[0], s[1] - r[1]]; - let divisor = d[0] * e[1] - d[1] * e[0]; - if divisor.abs() > tolerance * (d[0].hypot(d[1]) + e[0].hypot(e[1])) { - let relative = [r[0] - p[0], r[1] - p[1]]; - let t = (relative[0] * e[1] - relative[1] * e[0]) / divisor; - offer([p[0] + t * d[0], p[1] + t * d[1]]); - } else { - for point in [*p, *q, *r, *s] { - offer(point); - } - } - } - (ProfileEdge::Line { from, to }, ProfileEdge::Arc { center, radius, .. }) - | (ProfileEdge::Arc { center, radius, .. }, ProfileEdge::Line { from, to }) => { - let d = [to[0] - from[0], to[1] - from[1]]; - let f = [from[0] - center[0], from[1] - center[1]]; - let aa = d[0] * d[0] + d[1] * d[1]; - let bb = 2.0 * (f[0] * d[0] + f[1] * d[1]); - let cc = f[0] * f[0] + f[1] * f[1] - radius * radius; - let discriminant = bb * bb - 4.0 * aa * cc; - if discriminant >= -tolerance * tolerance * aa { - let root = discriminant.max(0.0).sqrt(); - for t in [(-bb - root) / (2.0 * aa), (-bb + root) / (2.0 * aa)] { - offer([from[0] + t * d[0], from[1] + t * d[1]]); - } - } - } - ( - ProfileEdge::Arc { - center: ca, - radius: ra, - .. - }, - ProfileEdge::Arc { - center: cb, - radius: rb, - .. - }, - ) => { - let d = (cb[0] - ca[0]).hypot(cb[1] - ca[1]); - if d <= tolerance && (ra - rb).abs() <= tolerance { - let (a0, a1) = a.endpoints(); - let (b0, b1) = b.endpoints(); - for point in [a0, a1, b0, b1] { - offer(point); - } - let ProfileEdge::Arc { - start_angle, - sweep_angle, - .. - } = a - else { - unreachable!() - }; - let midpoint_angle = start_angle + sweep_angle * 0.5; - offer([ - ca[0] + ra * midpoint_angle.cos(), - ca[1] + ra * midpoint_angle.sin(), - ]); - } else if d > tolerance - && d <= ra + rb + tolerance - && d + ra.min(*rb) + tolerance >= ra.max(*rb) - { - let along = (ra * ra - rb * rb + d * d) / (2.0 * d); - let perpendicular_sq = ra * ra - along * along; - if perpendicular_sq >= -tolerance * tolerance { - let ux = (cb[0] - ca[0]) / d; - let uy = (cb[1] - ca[1]) / d; - let base = [ca[0] + along * ux, ca[1] + along * uy]; - let off = perpendicular_sq.max(0.0).sqrt(); - offer([base[0] - off * uy, base[1] + off * ux]); - offer([base[0] + off * uy, base[1] - off * ux]); - } - } - } - } - candidates -} - -fn validate_arc_profile_loop(edges: &[ProfileEdge], g: f64) -> Result { - if edges.len() < 2 { - return Err(GeometryError::InvalidGeometry( - "arc-edged profile needs at least two edges".into(), - )); - } - for (index, edge) in edges.iter().enumerate() { - let (from, to) = edge.endpoints(); - if !from.into_iter().chain(to).all(f64::is_finite) { - return Err(GeometryError::InvalidGeometry( - "profile coordinates must be finite".into(), - )); - } - match edge { - ProfileEdge::Line { .. } => { - if (to[0] - from[0]).hypot(to[1] - from[1]) <= 4.0 * g { - return Err(GeometryError::UnresolvedIntersection( - "profile line is below resolution".into(), - )); - } - } - ProfileEdge::Arc { - center, - radius, - start_angle, - sweep_angle, - } => { - if !center.iter().all(|value| value.is_finite()) - || !radius.is_finite() - || !start_angle.is_finite() - || !sweep_angle.is_finite() - || *radius <= 4.0 * g - || sweep_angle.abs() >= std::f64::consts::TAU - || radius * sweep_angle.abs() <= 4.0 * g - { - return Err(GeometryError::InvalidGeometry( - "profile arc is unresolved".into(), - )); - } - } - } - let (next, _) = edges[(index + 1) % edges.len()].endpoints(); - if (to[0] - next[0]).hypot(to[1] - next[1]) > g { - return Err(GeometryError::InvalidGeometry( - "profile edges do not close".into(), - )); - } - } - for first in 0..edges.len() { - for second in (first + 1)..edges.len() { - let adjacent = second == first + 1 || first == 0 && second == edges.len() - 1; - let expected = if second == first + 1 { - Some(edges[first].endpoints().1) - } else if first == 0 && second == edges.len() - 1 { - Some(edges[first].endpoints().0) - } else { - None - }; - for point in profile_edge_intersections(&edges[first], &edges[second], g) { - let at_expected = expected - .is_some_and(|shared| (point[0] - shared[0]).hypot(point[1] - shared[1]) <= g); - let at_second_join = edges.len() == 2 - && (point[0] - edges[first].endpoints().0[0]) - .hypot(point[1] - edges[first].endpoints().0[1]) - <= g; - if !adjacent || !(at_expected || at_second_join) { - return Err(GeometryError::InvalidGeometry( - "arc-edged profile self-intersects".into(), - )); - } - } - } - } - let signed_area_twice: f64 = edges.iter().map(ProfileEdge::signed_area_twice).sum(); - if signed_area_twice.abs() <= 16.0 * g * g { - return Err(GeometryError::UnresolvedIntersection( - "profile area is below resolution".into(), - )); - } - Ok(signed_area_twice) -} - -fn arc_profile_contains(edges: &[ProfileEdge], point: [f64; 2], g: f64) -> bool { - use crate::geometry::{ - curved_boolean2d::{winding, CurveEdge2}, - poly2d::Pt2, - }; - - let ring = edges - .iter() - .map(|edge| match edge { - ProfileEdge::Line { from, to } => CurveEdge2::Line { - from: *from, - to: *to, - }, - ProfileEdge::Arc { - center, - radius, - start_angle, - sweep_angle, - } => CurveEdge2::Arc { - center: *center, - radius: *radius, - start_angle: *start_angle, - sweep_angle: *sweep_angle, - }, - }) - .collect::>(); - winding(Pt2::new(point[0], point[1]), &ring, g) != 0 -} - -struct ArcExtrusionLoop { - edges: Vec, - bottom_vertices: Vec, - top_vertices: Vec, - bottom_edges: Vec, - top_edges: Vec, - vertical_edges: Vec, -} - -pub fn arc_edged_extrusion( - id: String, - frame: Frame3, - outer: Vec, - height: f64, - accuracy: Accuracy, -) -> Result { - arc_edged_extrusion_with_holes(id, frame, outer, Vec::new(), height, accuracy) -} - -pub fn arc_edged_extrusion_with_holes( - id: String, - frame: Frame3, - mut outer: Vec, - mut holes: Vec>, - height: f64, - accuracy: Accuracy, -) -> Result { - frame.validate()?; - accuracy.validate()?; - dimensions(&[height])?; - let g = accuracy.geometric; - if height <= 4.0 * g { - return Err(GeometryError::UnresolvedIntersection( - "arc-edged extrusion height is below geometric resolution".into(), - )); - } - if validate_arc_profile_loop(&outer, g)? < 0.0 { - outer = outer.iter().rev().map(ProfileEdge::reversed).collect(); - } - for (index, hole) in holes.iter_mut().enumerate() { - if validate_arc_profile_loop(hole, g)? > 0.0 { - *hole = hole.iter().rev().map(ProfileEdge::reversed).collect(); - } - for hole_edge in hole.iter() { - for outer_edge in &outer { - if !profile_edge_intersections(hole_edge, outer_edge, g).is_empty() { - return Err(GeometryError::InvalidGeometry(format!( - "arc-edged profile hole {index} touches its outer boundary" - ))); - } - } - } - if !arc_profile_contains(&outer, hole[0].endpoints().0, g) { - return Err(GeometryError::InvalidGeometry(format!( - "arc-edged profile hole {index} lies outside the outer boundary" - ))); - } - } - for first in 0..holes.len() { - for second in (first + 1)..holes.len() { - if holes[first].iter().any(|a| { - holes[second] - .iter() - .any(|b| !profile_edge_intersections(a, b, g).is_empty()) - }) || arc_profile_contains(&holes[first], holes[second][0].endpoints().0, g) - || arc_profile_contains(&holes[second], holes[first][0].endpoints().0, g) - { - return Err(GeometryError::InvalidGeometry(format!( - "arc-edged profile holes {first} and {second} overlap or nest" - ))); - } - } - } - - let mut builder = Builder::new(id, accuracy)?; - let mut loops = Vec::with_capacity(holes.len() + 1); - for edges in std::iter::once(outer).chain(holes) { - let points = edges - .iter() - .map(|edge| edge.endpoints().0) - .collect::>(); - let bottom_vertices = points - .iter() - .map(|point| builder.vertex(frame.point([point[0], point[1], 0.0]))) - .collect::>(); - let top_vertices = points - .iter() - .map(|point| builder.vertex(frame.point([point[0], point[1], height]))) - .collect::>(); - let mut bottom_edges = Vec::with_capacity(edges.len()); - let mut top_edges = Vec::with_capacity(edges.len()); - let mut vertical_edges = Vec::with_capacity(edges.len()); - for (index, edge) in edges.iter().enumerate() { - let from = builder.brep.topology.vertices[bottom_vertices[index] as usize].position; - let top_from = builder.brep.topology.vertices[top_vertices[index] as usize].position; - let (bottom_curve, top_curve, range) = match edge { - ProfileEdge::Line { from: p, to: q } => { - let vector = frame.vector([q[0] - p[0], q[1] - p[1], 0.0]); - let length = norm(vector); - let direction = unit(vector)?; - ( - CurveGeometry::Line { - origin: from, - direction, - }, - CurveGeometry::Line { - origin: top_from, - direction, - }, - Interval::new(0.0, length)?, - ) - } - ProfileEdge::Arc { - center, - radius, - start_angle, - sweep_angle, - } => { - let origin = frame.point([center[0], center[1], 0.0]); - let circle_frame = Frame3 { origin, ..frame }; - let top_circle_frame = Frame3 { - origin: add(origin, scale(frame.z, height)), - ..frame - }; - let end_angle = start_angle + sweep_angle; - ( - CurveGeometry::Circle { - frame: circle_frame, - radius: *radius, - }, - CurveGeometry::Circle { - frame: top_circle_frame, - radius: *radius, - }, - Interval::new(start_angle.min(end_angle), start_angle.max(end_angle))?, - ) - } - }; - bottom_edges.push(builder.edge(bottom_curve, range, false)); - top_edges.push(builder.edge(top_curve, range, false)); - vertical_edges.push(builder.edge( - CurveGeometry::Line { - origin: from, - direction: frame.z, - }, - Interval::new(0.0, height)?, - false, - )); - } - loops.push(ArcExtrusionLoop { - edges, - bottom_vertices, - top_vertices, - bottom_edges, - top_edges, - vertical_edges, - }); - } - - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - for edge in &loops[0].edges { - match edge { - ProfileEdge::Line { from, to } => { - for point in [from, to] { - for axis in 0..2 { - bounds[axis][0] = bounds[axis][0].min(point[axis]); - bounds[axis][1] = bounds[axis][1].max(point[axis]); - } - } - } - ProfileEdge::Arc { center, radius, .. } => { - for axis in 0..2 { - bounds[axis][0] = bounds[axis][0].min(center[axis] - radius); - bounds[axis][1] = bounds[axis][1].max(center[axis] + radius); - } - } - } - } - for bound in &mut bounds { - bound[0] -= g; - bound[1] += g; - } - let top_frame = Frame3 { - origin: frame.point([0.0, 0.0, height]), - ..frame - }; - let bottom_frame = Frame3 { - y: scale(frame.y, -1.0), - z: scale(frame.z, -1.0), - ..frame - }; - let cap_uses = |profile: &ArcExtrusionLoop, top: bool| -> Result, GeometryError> { - let count = profile.edges.len(); - let mut uses = Vec::with_capacity(count); - for index in 0..count { - let next = (index + 1) % count; - let forward = !matches!(profile.edges[index], ProfileEdge::Arc { sweep_angle, .. } - if sweep_angle < 0.0); - let top_sense = if forward { - Orientation::Forward - } else { - Orientation::Reverse - }; - let bottom_sense = if forward { - Orientation::Reverse - } else { - Orientation::Forward - }; - uses.push(if top { - plane_boundary( - &builder, - top_frame, - profile.top_edges[index], - profile.top_vertices[index], - profile.top_vertices[next], - top_sense, - )? - } else { - plane_boundary( - &builder, - bottom_frame, - profile.bottom_edges[index], - profile.bottom_vertices[next], - profile.bottom_vertices[index], - bottom_sense, - )? - }); - } - if !top { - uses.reverse(); - } - Ok(uses) - }; - let top_outer = cap_uses(&loops[0], true)?; - let top_holes = loops[1..] - .iter() - .map(|profile| cap_uses(profile, true)) - .collect::, _>>()?; - let bottom_outer = cap_uses(&loops[0], false)?; - let bottom_holes = loops[1..] - .iter() - .map(|profile| cap_uses(profile, false)) - .collect::, _>>()?; - builder.face_with_holes( - "top", - SurfaceGeometry::Plane { frame: top_frame }, - bounds, - top_outer, - top_holes, - )?; - builder.face_with_holes( - "bottom", - SurfaceGeometry::Plane { - frame: bottom_frame, - }, - [bounds[0], [-bounds[1][1], -bounds[1][0]]], - bottom_outer, - bottom_holes, - )?; - - for (loop_index, profile) in loops.iter().enumerate() { - for (index, edge) in profile.edges.iter().enumerate() { - let next = (index + 1) % profile.edges.len(); - let bottom_from = profile.bottom_vertices[index]; - let bottom_to = profile.bottom_vertices[next]; - let top_from = profile.top_vertices[index]; - let top_to = profile.top_vertices[next]; - let forward = - !matches!(edge, ProfileEdge::Arc { sweep_angle, .. } if *sweep_angle < 0.0); - let bottom_sense = if forward { - Orientation::Forward - } else { - Orientation::Reverse - }; - let top_sense = if forward { - Orientation::Reverse - } else { - Orientation::Forward - }; - match edge { - ProfileEdge::Line { .. } => { - let origin = builder.brep.topology.vertices[bottom_from as usize].position; - let destination = builder.brep.topology.vertices[bottom_to as usize].position; - let edge_direction = unit(sub(destination, origin))?; - let side_frame = Frame3::from_axis( - origin, - unit(cross(edge_direction, frame.z))?, - edge_direction, - )?; - let uses = [ - ( - profile.bottom_edges[index], - bottom_from, - bottom_to, - Orientation::Forward, - ), - ( - profile.vertical_edges[next], - bottom_to, - top_to, - Orientation::Forward, - ), - ( - profile.top_edges[index], - top_to, - top_from, - Orientation::Reverse, - ), - ( - profile.vertical_edges[index], - top_from, - bottom_from, - Orientation::Reverse, - ), - ] - .into_iter() - .map(|(edge, from, to, sense)| { - plane_boundary(&builder, side_frame, edge, from, to, sense) - }) - .collect::, _>>()?; - builder.face( - &format!("side-{loop_index}-{index}"), - SurfaceGeometry::Plane { frame: side_frame }, - [[-g, norm(sub(destination, origin)) + g], [-g, height + g]], - uses, - )?; - } - ProfileEdge::Arc { - center, - radius, - start_angle, - sweep_angle, - } => { - let end_angle = start_angle + sweep_angle; - let circle_frame = Frame3 { - origin: frame.point([center[0], center[1], 0.0]), - ..frame - }; - let uses = vec![ - boundary( - profile.bottom_edges[index], - bottom_from, - bottom_to, - bottom_sense, - uv_line([0.0, 0.0], [1.0, 0.0]), - ), - boundary( - profile.vertical_edges[next], - bottom_to, - top_to, - Orientation::Forward, - uv_line([end_angle, 0.0], [0.0, 1.0]), - ), - boundary( - profile.top_edges[index], - top_to, - top_from, - top_sense, - uv_line([0.0, height], [1.0, 0.0]), - ), - boundary( - profile.vertical_edges[index], - top_from, - bottom_from, - Orientation::Reverse, - uv_line([*start_angle, 0.0], [0.0, 1.0]), - ), - ]; - let face_id = builder.brep.topology.faces.len(); - builder.face( - &format!("side-{loop_index}-{index}"), - SurfaceGeometry::Cylinder { - frame: circle_frame, - radius: *radius, - }, - [ - [ - start_angle.min(end_angle) - g / radius, - start_angle.max(end_angle) + g / radius, - ], - [-g, height + g], - ], - uses, - )?; - if !forward { - builder.brep.topology.faces[face_id].sense = Orientation::Reverse; - } - } - } - } - } - builder.finish() -} - -#[derive(Clone, Debug)] -pub struct BoxArchedOpening { - pub id: String, - pub station: f64, - pub width: f64, - pub bottom: f64, - pub height: f64, -} - -#[allow(clippy::too_many_arguments)] -pub fn box_with_arched_opening( - id: String, - frame: Frame3, - width: f64, - depth: f64, - height: f64, - opening: BoxArchedOpening, - accuracy: Accuracy, -) -> Result { - frame.validate()?; - accuracy.validate()?; - dimensions(&[width, depth, height, opening.width, opening.height])?; - let radius = opening.width * 0.5; - let spring = opening.bottom + opening.height - radius; - if opening.id.is_empty() - || !opening.station.is_finite() - || !opening.bottom.is_finite() - || spring <= opening.bottom + 4.0 * accuracy.geometric - || opening.station - radius <= 4.0 * accuracy.geometric - || opening.station + radius >= width - 4.0 * accuracy.geometric - || opening.bottom < 0.0 - || opening.bottom + opening.height >= height - 4.0 * accuracy.geometric - { - return Err(GeometryError::InvalidGeometry( - "arched opening must remain resolved and strictly inside the box".into(), - )); - } - let extrusion_frame = Frame3 { - origin: frame.point([0.0, depth, 0.0]), - x: frame.x, - y: frame.z, - z: scale(frame.y, -1.0), - }; - let left = opening.station - radius; - let right = opening.station + radius; - let mut result = linear_extrusion( - id, - extrusion_frame, - vec![[0.0, 0.0], [width, 0.0], [width, height], [0.0, height]], - vec![vec![ - [left, opening.bottom], - [right, opening.bottom], - [right, spring], - [left, spring], - ]], - depth, - accuracy, - )?; - let upper_cap = result - .topology - .faces - .iter() - .find(|face| face.key == "side-1-0") - .map(|face| face.id) - .ok_or_else(|| GeometryError::InvalidTopology("arched upper_cap face is missing".into()))?; - let upper_cap_loop = result.topology.faces[upper_cap as usize].trim.outer; - let upper_cap_uses = loop_halfedges_for_primitive(&result, upper_cap_loop)?; - let mut arch_edges = Vec::new(); - for halfedge in &upper_cap_uses { - let use_ = &result.topology.halfedges[*halfedge as usize]; - let endpoints = [use_.from, use_.to].map(|vertex| { - extrusion_frame.local(result.topology.vertices[vertex as usize].position) - }); - if endpoints - .iter() - .all(|point| (point[1] - spring).abs() <= 4.0 * accuracy.geometric) - && (endpoints[0][2] - endpoints[1][2]).abs() <= 4.0 * accuracy.geometric - { - arch_edges.push(use_.edge); - } - } - arch_edges.sort_unstable(); - arch_edges.dedup(); - if arch_edges.len() != 2 { - return Err(GeometryError::InvalidTopology( - "arched opening requires two shared spring edges".into(), - )); - } - for edge_id in &arch_edges { - let edge = &result.topology.edges[*edge_id as usize]; - let primary = &result.topology.halfedges[edge.halfedge as usize]; - let parameter_start = if primary.geometry_use.sense == Orientation::Forward { - primary.from - } else { - primary.to - }; - let start = result.topology.vertices[parameter_start as usize].position; - let center = add( - extrusion_frame.origin, - add( - scale(extrusion_frame.x, opening.station), - add( - scale(extrusion_frame.y, spring), - scale( - extrusion_frame.z, - dot(sub(start, extrusion_frame.origin), extrusion_frame.z), - ), - ), - ), - ); - let x = unit(sub(start, center))?; - let circle_frame = Frame3 { - origin: center, - x, - y: extrusion_frame.y, - z: unit(cross(x, extrusion_frame.y))?, - }; - circle_frame.validate()?; - let curve = result.geometry.curves.len() as u32; - result.geometry.curves.push(CurveGeometry::Circle { - frame: circle_frame, - radius, - }); - result.topology.edges[*edge_id as usize].geometry = EdgeGeometry::Curve { - curve, - range: Interval::new(0.0, std::f64::consts::PI)?, - }; - } - let cylinder_frame = Frame3 { - origin: extrusion_frame.point([opening.station, spring, 0.0]), - x: scale(extrusion_frame.x, -1.0), - y: extrusion_frame.y, - z: scale(extrusion_frame.z, -1.0), - }; - cylinder_frame.validate()?; - let cylinder_surface = result.geometry.surfaces.len() as u32; - result.geometry.surfaces.push(SurfaceGeometry::Cylinder { - frame: cylinder_frame, - radius, - }); - { - let face = &mut result.topology.faces[upper_cap as usize]; - face.key = format!("{}-arched-cap", opening.id); - face.surface = cylinder_surface; - face.sense = Orientation::Reverse; - face.trim.uv_bounds = [ - Interval::new(0.0, std::f64::consts::PI)?, - Interval::new(-depth, 0.0)?, - ]; - face.provenance.sources[0].key = face.key.clone(); - face.provenance.role = FaceRole::Cut; - face.provenance.reversed = true; - } - let affected = result - .topology - .halfedges - .iter() - .filter(|halfedge| halfedge.face == Some(upper_cap) || arch_edges.contains(&halfedge.edge)) - .map(|halfedge| halfedge.id) - .collect::>(); - for halfedge_id in affected { - let halfedge = &result.topology.halfedges[halfedge_id as usize]; - let face = halfedge.face.ok_or_else(|| { - GeometryError::InvalidTopology("arched opening edge use has no face".into()) - })?; - let surface = result.topology.faces[face as usize].surface; - let (curve, range) = match result.topology.edges[halfedge.edge as usize].geometry { - EdgeGeometry::Curve { curve, range } => (curve, range), - EdgeGeometry::Collapsed { .. } => { - return Err(GeometryError::InvalidTopology( - "arched opening contains a collapsed boundary".into(), - )) - } - }; - let curve_geometry = &result.geometry.curves[curve as usize]; - let surface_geometry = result.geometry.surface(surface)?; - let pcurve_geometry = match (curve_geometry, surface_geometry) { - (CurveGeometry::Line { origin, direction }, SurfaceGeometry::Plane { frame }) => { - PcurveGeometry::Line2 { - origin: [ - dot(sub(*origin, frame.origin), frame.x), - dot(sub(*origin, frame.origin), frame.y), - ], - direction: [dot(*direction, frame.x), dot(*direction, frame.y)], - } - } - ( - CurveGeometry::Circle { - frame: circle, - radius, - }, - SurfaceGeometry::Plane { frame }, - ) => PcurveGeometry::Conic2 { - origin: [ - dot(sub(circle.origin, frame.origin), frame.x), - dot(sub(circle.origin, frame.origin), frame.y), - ], - axis_a: [ - dot(scale(circle.x, *radius), frame.x), - dot(scale(circle.x, *radius), frame.y), - ], - axis_b: [ - dot(scale(circle.y, *radius), frame.x), - dot(scale(circle.y, *radius), frame.y), - ], - }, - ( - CurveGeometry::Line { origin, direction }, - SurfaceGeometry::Cylinder { frame, .. }, - ) => { - let radial = sub(*origin, frame.origin); - PcurveGeometry::Line2 { - origin: [ - dot(radial, frame.y).atan2(dot(radial, frame.x)), - dot(radial, frame.z), - ], - direction: [0.0, dot(*direction, frame.z)], - } - } - ( - CurveGeometry::Circle { frame: circle, .. }, - SurfaceGeometry::Cylinder { frame, .. }, - ) => { - if dot(circle.z, frame.z).abs() < 1.0 - 1e-10 { - return Err(GeometryError::UnsupportedGeometry( - "arched upper_cap circle is not coaxial with its cylindrical face".into(), - )); - } - let angular_rate = dot(circle.z, frame.z).signum(); - let mut angle = dot(circle.x, frame.y).atan2(dot(circle.x, frame.x)); - let middle = angle + angular_rate * range.midpoint(); - angle += ((std::f64::consts::FRAC_PI_2 - middle) / std::f64::consts::TAU).round() - * std::f64::consts::TAU; - PcurveGeometry::Line2 { - origin: [angle, dot(sub(circle.origin, frame.origin), frame.z)], - direction: [angular_rate, 0.0], - } - } - _ => { - return Err(GeometryError::UnsupportedGeometry( - "arched boundary requires a line or circle on a plane or cylinder".into(), - )) - } - }; - let pcurve = result.geometry.pcurves.len() as u32; - result.geometry.pcurves.push(pcurve_geometry); - result.topology.halfedges[halfedge_id as usize] - .geometry_use - .pcurve = Some(pcurve); - } - result.validate()?; - Ok(result) -} - -pub fn cone( - id: String, - frame: Frame3, - radius: f64, - height: f64, - accuracy: Accuracy, -) -> Result { - dimensions(&[radius, height])?; - let semi_angle = radius.atan2(height); - let frame = Frame3 { - origin: frame.point([0.0, 0.0, height]), - x: frame.x, - y: scale(frame.y, -1.0), - z: scale(frame.z, -1.0), - }; - section( - id, - frame, - SurfaceGeometry::Cone { frame, semi_angle }, - 0.0, - radius, - 0.0, - height, - accuracy, - ) -} - -pub fn frustum( - id: String, - frame: Frame3, - lower_radius: f64, - upper_radius: f64, - height: f64, - accuracy: Accuracy, -) -> Result { - frame.validate()?; - dimensions(&[lower_radius, height])?; - if !upper_radius.is_finite() || upper_radius < 0.0 { - return Err(GeometryError::InvalidGeometry( - "invalid frustum upper radius".into(), - )); - } - if lower_radius == upper_radius { - return cylinder(id, frame, lower_radius, height, accuracy); - } - let k = (upper_radius - lower_radius).abs() / height; - let semi_angle = k.atan(); - let mut cone_frame = frame; - let (r0, r1, z0, z1) = if upper_radius > lower_radius { - cone_frame.origin = frame.point([0.0, 0.0, -lower_radius / k]); - ( - lower_radius, - upper_radius, - lower_radius / k, - upper_radius / k, - ) - } else { - cone_frame.origin = frame.point([0.0, 0.0, lower_radius / k]); - cone_frame.y = scale(frame.y, -1.0); - cone_frame.z = scale(frame.z, -1.0); - ( - upper_radius, - lower_radius, - upper_radius / k, - lower_radius / k, - ) - }; - section( - id, - cone_frame, - SurfaceGeometry::Cone { - frame: cone_frame, - semi_angle, - }, - r0, - r1, - z0, - z1, - accuracy, - ) -} - -pub(super) fn plane_boundary( - builder: &Builder, - frame: Frame3, - edge: u32, - from: u32, - to: u32, - sense: Orientation, -) -> Result { - let curve = match builder.brep.topology.edges[edge as usize].geometry { - EdgeGeometry::Curve { curve, .. } => &builder.brep.geometry.curves[curve as usize], - _ => { - return Err(GeometryError::InvalidGeometry( - "collapsed planar boundary".into(), - )) - } - }; - let local = |p: Point3| [dot(p, frame.x), dot(p, frame.y)]; - let pcurve = match curve { - CurveGeometry::Line { origin, direction } => { - uv_line(local(sub(*origin, frame.origin)), local(*direction)) - } - CurveGeometry::Circle { frame: c, radius } => PcurveGeometry::Conic2 { - origin: local(sub(c.origin, frame.origin)), - axis_a: local(scale(c.x, *radius)), - axis_b: local(scale(c.y, *radius)), - }, - _ => { - return Err(GeometryError::UnsupportedGeometry( - "planar primitive boundary".into(), - )) - } - }; - Ok(boundary(edge, from, to, sense, pcurve)) -} - -#[derive(Clone, Debug)] -pub struct AnnularSectorOpening { - pub id: String, - pub angle: f64, - pub width: f64, - pub bottom: f64, - pub height: f64, -} - -struct BuiltAnnularSectorOpening { - source: String, - outer_angles: [f64; 2], - inner_angles: [f64; 2], - elevations: [f64; 2], - vertices: [u32; 8], - lower_outer_arc: Option, - upper_outer_arc: u32, - lower_inner_arc: Option, - upper_inner_arc: u32, - outer_verticals: [u32; 2], - inner_verticals: [u32; 2], - connectors: [[u32; 2]; 2], -} - -impl BuiltAnnularSectorOpening { - fn touches_bottom(&self) -> bool { - self.elevations[0] == 0.0 - } -} - -pub fn annular_sector_extrusion( - id: String, - frame: Frame3, - radius: f64, - thickness: f64, - height: f64, - start_angle: f64, - sweep_angle: f64, - accuracy: Accuracy, -) -> Result { - annular_sector_extrusion_with_openings( - id, - frame, - radius, - thickness, - height, - start_angle, - sweep_angle, - Vec::new(), - accuracy, - ) -} - -#[allow(clippy::too_many_arguments)] -pub fn annular_sector_extrusion_with_openings( - id: String, - frame: Frame3, - radius: f64, - thickness: f64, - height: f64, - start_angle: f64, - sweep_angle: f64, - openings: Vec, - accuracy: Accuracy, -) -> Result { - frame.validate()?; - accuracy.validate()?; - dimensions(&[radius, thickness, height])?; - let inner = radius - thickness * 0.5; - let outer = radius + thickness * 0.5; - if inner <= 0.0 || !outer.is_finite() { - return Err(GeometryError::InvalidGeometry( - "annular sector radius must exceed half thickness".into(), - )); - } - if !start_angle.is_finite() || !sweep_angle.is_finite() || sweep_angle == 0.0 { - return Err(GeometryError::InvalidGeometry( - "annular sector angles must be finite with a nonzero sweep".into(), - )); - } - if sweep_angle.abs() >= std::f64::consts::TAU { - return Err(GeometryError::UnsupportedGeometry( - "annular sector sweep must be smaller than one full turn".into(), - )); - } - let end_angle = start_angle + sweep_angle; - let lo = start_angle.min(end_angle); - let hi = start_angle.max(end_angle); - let range = Interval::new(lo, hi)?; - if range.width() == 0.0 - || inner * (hi - lo) <= 4.0 * accuracy.geometric - || thickness <= 4.0 * accuracy.geometric - || height <= 4.0 * accuracy.geometric - { - return Err(GeometryError::InvalidGeometry( - "annular sector features are below geometric resolution".into(), - )); - } - let mut prepared_openings = Vec::with_capacity(openings.len()); - let mut opening_ids = std::collections::HashSet::new(); - for opening in openings { - if opening.id.is_empty() || !opening_ids.insert(opening.id.clone()) { - return Err(GeometryError::InvalidGeometry( - "annular sector opening identities must be nonempty and unique".into(), - )); - } - if !opening.angle.is_finite() - || !opening.width.is_finite() - || !opening.bottom.is_finite() - || !opening.height.is_finite() - || opening.width <= 4.0 * accuracy.geometric - || opening.height <= 4.0 * accuracy.geometric - { - return Err(GeometryError::InvalidGeometry( - "annular sector opening dimensions and station must be finite and resolved".into(), - )); - } - let half_width = opening.width * 0.5; - if half_width >= inner { - return Err(GeometryError::UnsupportedGeometry( - "annular sector opening width reaches the reference arc".into(), - )); - } - let outer_delta = (half_width / outer).asin(); - let inner_delta = (half_width / inner).asin(); - let outer_angles = [opening.angle - outer_delta, opening.angle + outer_delta]; - let inner_angles = [opening.angle - inner_delta, opening.angle + inner_delta]; - let mut elevations = [opening.bottom, opening.bottom + opening.height]; - let clearance = 4.0 * accuracy.geometric; - if inner * (inner_angles[0] - lo) <= clearance - || inner * (hi - inner_angles[1]) <= clearance - { - return Err(GeometryError::UnsupportedGeometry( - "annular sector opening must stay strictly inside both sweep ends".into(), - )); - } - if elevations[0] < 0.0 || elevations[1] >= height { - return Err(GeometryError::InvalidGeometry( - "annular sector opening elevation lies outside the extrusion".into(), - )); - } - if elevations[0] > 0.0 && elevations[0] <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "annular sector opening lower clearance is below geometric resolution".into(), - )); - } - if height - elevations[1] <= clearance { - return Err(GeometryError::UnresolvedIntersection( - "annular sector opening upper clearance is below geometric resolution".into(), - )); - } - if elevations[0] == -0.0 { - elevations[0] = 0.0; - } - prepared_openings.push((opening.id, outer_angles, inner_angles, elevations)); - } - for first in 0..prepared_openings.len() { - for second in (first + 1)..prepared_openings.len() { - let a = &prepared_openings[first]; - let b = &prepared_openings[second]; - let angular_overlap = a.2[1].min(b.2[1]) - a.2[0].max(b.2[0]); - let vertical_overlap = a.3[1].min(b.3[1]) - a.3[0].max(b.3[0]); - if angular_overlap * inner > -4.0 * accuracy.geometric - && vertical_overlap > -4.0 * accuracy.geometric - { - return Err(GeometryError::UnsupportedGeometry( - "annular sector openings overlap or are below geometric separation".into(), - )); - } - } - } - let mut b = Builder::new(id, accuracy)?; - let angles = [lo, hi]; - let point = |r: f64, a: f64, z: f64| frame.point([r * a.cos(), r * a.sin(), z]); - let mut vertices = [0u32; 8]; - for (i, vertex) in vertices.iter_mut().enumerate() { - *vertex = b.vertex(point( - if i % 4 < 2 { outer } else { inner }, - angles[i % 2], - if i < 4 { 0.0 } else { height }, - )); - } - let [ob0, ob1, ib0, ib1, ot0, ot1, it0, it1] = vertices; - let mut top_frame = frame; - top_frame.origin = frame.point([0.0, 0.0, height]); - let has_bottom_openings = prepared_openings.iter().any(|opening| opening.3[0] == 0.0); - let ob = (!has_bottom_openings).then(|| { - b.edge( - CurveGeometry::Circle { - frame, - radius: outer, - }, - range, - false, - ) - }); - let ot = b.edge( - CurveGeometry::Circle { - frame: top_frame, - radius: outer, - }, - range, - false, - ); - let ib = (!has_bottom_openings).then(|| { - b.edge( - CurveGeometry::Circle { - frame, - radius: inner, - }, - range, - false, - ) - }); - let it = b.edge( - CurveGeometry::Circle { - frame: top_frame, - radius: inner, - }, - range, - false, - ); - let mut radial = [0u32; 4]; - for (i, edge) in radial.iter_mut().enumerate() { - let angle = angles[i % 2]; - *edge = b.edge( - CurveGeometry::Line { - origin: point(inner, angle, if i < 2 { 0.0 } else { height }), - direction: frame.vector([angle.cos(), angle.sin(), 0.0]), - }, - Interval::new(0.0, thickness)?, - false, - ); - } - let [rb0, rb1, rt0, rt1] = radial; - let mut vertical = [0u32; 4]; - for (i, edge) in vertical.iter_mut().enumerate() { - *edge = b.edge( - CurveGeometry::Line { - origin: b.brep.topology.vertices[vertices[i] as usize].position, - direction: frame.z, - }, - Interval::new(0.0, height)?, - false, - ); - } - let [vo0, vo1, vi0, vi1] = vertical; - let mut built_openings = Vec::with_capacity(prepared_openings.len()); - for (source, outer_angles, inner_angles, elevations) in prepared_openings { - let positions = [ - point(outer, outer_angles[0], elevations[0]), - point(outer, outer_angles[1], elevations[0]), - point(inner, inner_angles[0], elevations[0]), - point(inner, inner_angles[1], elevations[0]), - point(outer, outer_angles[0], elevations[1]), - point(outer, outer_angles[1], elevations[1]), - point(inner, inner_angles[0], elevations[1]), - point(inner, inner_angles[1], elevations[1]), - ]; - let vertices = positions.map(|position| b.vertex(position)); - let mut lower_frame = frame; - lower_frame.origin = frame.point([0.0, 0.0, elevations[0]]); - let mut upper_frame = frame; - upper_frame.origin = frame.point([0.0, 0.0, elevations[1]]); - let outer_range = Interval::new(outer_angles[0], outer_angles[1])?; - let inner_range = Interval::new(inner_angles[0], inner_angles[1])?; - let lower_outer_arc = (elevations[0] > 0.0).then(|| { - b.edge( - CurveGeometry::Circle { - frame: lower_frame, - radius: outer, - }, - outer_range, - false, - ) - }); - let upper_outer_arc = b.edge( - CurveGeometry::Circle { - frame: upper_frame, - radius: outer, - }, - outer_range, - false, - ); - let lower_inner_arc = (elevations[0] > 0.0).then(|| { - b.edge( - CurveGeometry::Circle { - frame: lower_frame, - radius: inner, - }, - inner_range, - false, - ) - }); - let upper_inner_arc = b.edge( - CurveGeometry::Circle { - frame: upper_frame, - radius: inner, - }, - inner_range, - false, - ); - let opening_height = elevations[1] - elevations[0]; - let outer_verticals = [ - b.edge( - CurveGeometry::Line { - origin: positions[0], - direction: frame.z, - }, - Interval::new(0.0, opening_height)?, - false, - ), - b.edge( - CurveGeometry::Line { - origin: positions[1], - direction: frame.z, - }, - Interval::new(0.0, opening_height)?, - false, - ), - ]; - let inner_verticals = [ - b.edge( - CurveGeometry::Line { - origin: positions[2], - direction: frame.z, - }, - Interval::new(0.0, opening_height)?, - false, - ), - b.edge( - CurveGeometry::Line { - origin: positions[3], - direction: frame.z, - }, - Interval::new(0.0, opening_height)?, - false, - ), - ]; - let mut connectors = [[0_u32; 2]; 2]; - for elevation in 0..2 { - for side in 0..2 { - let outer_index = elevation * 4 + side; - let inner_index = elevation * 4 + 2 + side; - let delta = sub(positions[outer_index], positions[inner_index]); - connectors[elevation][side] = b.edge( - CurveGeometry::Line { - origin: positions[inner_index], - direction: unit(delta)?, - }, - Interval::new(0.0, norm(delta))?, - false, - ); - } - } - built_openings.push(BuiltAnnularSectorOpening { - source, - outer_angles, - inner_angles, - elevations, - vertices, - lower_outer_arc, - upper_outer_arc, - lower_inner_arc, - upper_inner_arc, - outer_verticals, - inner_verticals, - connectors, - }); - } - use Orientation::{Forward as F, Reverse as R}; - struct BottomSpan { - outer_edge: u32, - inner_edge: u32, - outer_vertices: [u32; 2], - inner_vertices: [u32; 2], - low_connector: u32, - high_connector: u32, - } - let mut bottom_openings = built_openings - .iter() - .enumerate() - .filter(|(_, opening)| opening.touches_bottom()) - .map(|(index, _)| index) - .collect::>(); - bottom_openings.sort_by(|&a, &b| { - built_openings[a].outer_angles[0].total_cmp(&built_openings[b].outer_angles[0]) - }); - let mut bottom_spans = Vec::with_capacity(bottom_openings.len() + 1); - if bottom_openings.is_empty() { - bottom_spans.push(BottomSpan { - outer_edge: ob.ok_or_else(|| { - GeometryError::InvalidTopology("annular sector bottom edge missing".into()) - })?, - inner_edge: ib.ok_or_else(|| { - GeometryError::InvalidTopology("annular sector bottom edge missing".into()) - })?, - outer_vertices: [ob0, ob1], - inner_vertices: [ib0, ib1], - low_connector: rb0, - high_connector: rb1, - }); - } else { - for span_index in 0..=bottom_openings.len() { - let low = span_index - .checked_sub(1) - .map(|index| &built_openings[bottom_openings[index]]); - let high = bottom_openings - .get(span_index) - .map(|&index| &built_openings[index]); - let outer_angles = [ - low.map_or(lo, |opening| opening.outer_angles[1]), - high.map_or(hi, |opening| opening.outer_angles[0]), - ]; - let inner_angles = [ - low.map_or(lo, |opening| opening.inner_angles[1]), - high.map_or(hi, |opening| opening.inner_angles[0]), - ]; - let outer_vertices = [ - low.map_or(ob0, |opening| opening.vertices[1]), - high.map_or(ob1, |opening| opening.vertices[0]), - ]; - let inner_vertices = [ - low.map_or(ib0, |opening| opening.vertices[3]), - high.map_or(ib1, |opening| opening.vertices[2]), - ]; - bottom_spans.push(BottomSpan { - outer_edge: b.edge( - CurveGeometry::Circle { - frame, - radius: outer, - }, - Interval::new(outer_angles[0], outer_angles[1])?, - false, - ), - inner_edge: b.edge( - CurveGeometry::Circle { - frame, - radius: inner, - }, - Interval::new(inner_angles[0], inner_angles[1])?, - false, - ), - outer_vertices, - inner_vertices, - low_connector: low.map_or(rb0, |opening| opening.connectors[0][1]), - high_connector: high.map_or(rb1, |opening| opening.connectors[0][0]), - }); - } - } - let outer_holes = built_openings - .iter() - .filter(|opening| !opening.touches_bottom()) - .map(|opening| { - let [obl, obr, _, _, otl, otr, _, _] = opening.vertices; - Ok(vec![ - boundary( - opening.lower_outer_arc.ok_or_else(|| { - GeometryError::InvalidTopology("opening lower_cap edge missing".into()) - })?, - obr, - obl, - R, - uv_line([0.0, opening.elevations[0]], [1.0, 0.0]), - ), - boundary( - opening.outer_verticals[0], - obl, - otl, - F, - uv_line([opening.outer_angles[0], opening.elevations[0]], [0.0, 1.0]), - ), - boundary( - opening.upper_outer_arc, - otl, - otr, - F, - uv_line([0.0, opening.elevations[1]], [1.0, 0.0]), - ), - boundary( - opening.outer_verticals[1], - otr, - obr, - R, - uv_line([opening.outer_angles[1], opening.elevations[0]], [0.0, 1.0]), - ), - ]) - }) - .collect::, GeometryError>>()?; - let mut outer_boundary = Vec::new(); - for (span_index, span) in bottom_spans.iter().enumerate() { - outer_boundary.push(boundary( - span.outer_edge, - span.outer_vertices[0], - span.outer_vertices[1], - F, - uv_line([0.0, 0.0], [1.0, 0.0]), - )); - if let Some(&opening_index) = bottom_openings.get(span_index) { - let opening = &built_openings[opening_index]; - let [obl, obr, _, _, otl, otr, _, _] = opening.vertices; - outer_boundary.extend([ - boundary( - opening.outer_verticals[0], - obl, - otl, - F, - uv_line([opening.outer_angles[0], 0.0], [0.0, 1.0]), - ), - boundary( - opening.upper_outer_arc, - otl, - otr, - F, - uv_line([0.0, opening.elevations[1]], [1.0, 0.0]), - ), - boundary( - opening.outer_verticals[1], - otr, - obr, - R, - uv_line([opening.outer_angles[1], 0.0], [0.0, 1.0]), - ), - ]); - } - } - outer_boundary.extend([ - boundary(vo1, ob1, ot1, F, uv_line([hi, 0.0], [0.0, 1.0])), - boundary(ot, ot1, ot0, R, uv_line([0.0, height], [1.0, 0.0])), - boundary(vo0, ot0, ob0, R, uv_line([lo, 0.0], [0.0, 1.0])), - ]); - b.face_with_holes( - "outer", - SurfaceGeometry::Cylinder { - frame, - radius: outer, - }, - [[lo, hi], [0.0, height]], - outer_boundary, - outer_holes, - )?; - let inner_holes = built_openings - .iter() - .filter(|opening| !opening.touches_bottom()) - .map(|opening| { - let [_, _, ibl, ibr, _, _, itl, itr] = opening.vertices; - Ok(vec![ - boundary( - opening.lower_inner_arc.ok_or_else(|| { - GeometryError::InvalidTopology("opening lower_cap edge missing".into()) - })?, - ibl, - ibr, - F, - uv_line([0.0, opening.elevations[0]], [1.0, 0.0]), - ), - boundary( - opening.inner_verticals[1], - ibr, - itr, - F, - uv_line([opening.inner_angles[1], opening.elevations[0]], [0.0, 1.0]), - ), - boundary( - opening.upper_inner_arc, - itr, - itl, - R, - uv_line([0.0, opening.elevations[1]], [1.0, 0.0]), - ), - boundary( - opening.inner_verticals[0], - itl, - ibl, - R, - uv_line([opening.inner_angles[0], opening.elevations[0]], [0.0, 1.0]), - ), - ]) - }) - .collect::, GeometryError>>()?; - let mut inner_boundary = Vec::new(); - for span_index in (0..bottom_spans.len()).rev() { - let span = &bottom_spans[span_index]; - inner_boundary.push(boundary( - span.inner_edge, - span.inner_vertices[1], - span.inner_vertices[0], - R, - uv_line([0.0, 0.0], [1.0, 0.0]), - )); - if span_index > 0 { - let opening = &built_openings[bottom_openings[span_index - 1]]; - let [_, _, ibl, ibr, _, _, itl, itr] = opening.vertices; - inner_boundary.extend([ - boundary( - opening.inner_verticals[1], - ibr, - itr, - F, - uv_line([opening.inner_angles[1], 0.0], [0.0, 1.0]), - ), - boundary( - opening.upper_inner_arc, - itr, - itl, - R, - uv_line([0.0, opening.elevations[1]], [1.0, 0.0]), - ), - boundary( - opening.inner_verticals[0], - itl, - ibl, - R, - uv_line([opening.inner_angles[0], 0.0], [0.0, 1.0]), - ), - ]); - } - } - inner_boundary.extend([ - boundary(vi0, ib0, it0, F, uv_line([lo, 0.0], [0.0, 1.0])), - boundary(it, it0, it1, F, uv_line([0.0, height], [1.0, 0.0])), - boundary(vi1, it1, ib1, R, uv_line([hi, 0.0], [0.0, 1.0])), - ]); - b.face_with_holes( - "inner", - SurfaceGeometry::Cylinder { - frame, - radius: inner, - }, - [[lo, hi], [0.0, height]], - inner_boundary, - inner_holes, - )?; - b.brep.topology.faces[1].sense = R; - let bottom_frame = Frame3 { - y: scale(frame.y, -1.0), - z: scale(frame.z, -1.0), - ..frame - }; - for (span_index, span) in bottom_spans.iter().enumerate() { - let bottom_uses = [ - ( - span.outer_edge, - span.outer_vertices[1], - span.outer_vertices[0], - R, - ), - ( - span.low_connector, - span.outer_vertices[0], - span.inner_vertices[0], - R, - ), - ( - span.inner_edge, - span.inner_vertices[0], - span.inner_vertices[1], - F, - ), - ( - span.high_connector, - span.inner_vertices[1], - span.outer_vertices[1], - F, - ), - ] - .into_iter() - .map(|(edge, from, to, sense)| plane_boundary(&b, bottom_frame, edge, from, to, sense)) - .collect::, _>>()?; - let key = if bottom_spans.len() == 1 { - "bottom".into() - } else { - format!("bottom-{span_index}") - }; - b.face( - &key, - SurfaceGeometry::Plane { - frame: bottom_frame, - }, - [[-outer - accuracy.geometric, outer + accuracy.geometric]; 2], - bottom_uses, - )?; - } - let top_uses = [ - (ot, ot0, ot1, F), - (rt1, ot1, it1, R), - (it, it1, it0, R), - (rt0, it0, ot0, F), - ] - .into_iter() - .map(|(e, from, to, sense)| plane_boundary(&b, top_frame, e, from, to, sense)) - .collect::, _>>()?; - b.face( - "top", - SurfaceGeometry::Plane { frame: top_frame }, - [[-outer - accuracy.geometric, outer + accuracy.geometric]; 2], - top_uses, - )?; - for i in 0..2 { - let angle = angles[i]; - let radial_direction = frame.vector([angle.cos(), angle.sin(), 0.0]); - let normal = frame.vector([angle.sin(), -angle.cos(), 0.0]); - let end_frame = Frame3::from_axis( - frame.origin, - scale(normal, if i == 0 { 1.0 } else { -1.0 }), - radial_direction, - )?; - let pairs = if i == 0 { - [ - (rb0, ib0, ob0, F), - (vo0, ob0, ot0, F), - (rt0, ot0, it0, R), - (vi0, it0, ib0, R), - ] - } else { - [ - (rb1, ob1, ib1, R), - (vi1, ib1, it1, F), - (rt1, it1, ot1, F), - (vo1, ot1, ob1, R), - ] - }; - let uses = pairs - .into_iter() - .map(|(e, from, to, sense)| plane_boundary(&b, end_frame, e, from, to, sense)) - .collect::, _>>()?; - let key = if (i == 0) == (sweep_angle > 0.0) { - "axis-start" - } else { - "axis-end" - }; - let g = accuracy.geometric; - b.face( - key, - SurfaceGeometry::Plane { frame: end_frame }, - [ - [inner - g, outer + g], - if i == 0 { - [-g, height + g] - } else { - [-height - g, g] - }, - ], - uses, - )?; - } - for (opening_index, opening) in built_openings.iter().enumerate() { - let [obl, obr, ibl, ibr, otl, otr, itl, itr] = opening.vertices; - let plane_bounds = |builder: &Builder, plane: Frame3, vertices: [u32; 4]| { - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - for vertex in vertices { - let local = plane.local(builder.brep.topology.vertices[vertex as usize].position); - for axis in 0..2 { - bounds[axis][0] = bounds[axis][0].min(local[axis]); - bounds[axis][1] = bounds[axis][1].max(local[axis]); - } - } - for bound in &mut bounds { - bound[0] -= accuracy.geometric; - bound[1] += accuracy.geometric; - } - bounds - }; - let curved_plane_bounds = |builder: &Builder, - plane: Frame3, - edges: [u32; 4]| - -> Result<[[f64; 2]; 2], GeometryError> { - let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; - for edge in edges { - let edge = &builder.brep.topology.edges[edge as usize]; - let edge_bounds = match edge.geometry { - EdgeGeometry::Curve { curve, range } => { - builder.brep.geometry.curve(curve)?.enclose(range)? - } - EdgeGeometry::Collapsed { vertex } => { - let point = builder.brep.topology.vertices[vertex as usize].position; - super::geometry::PatchBounds { - axes: [ - Interval::point(point[0])?, - Interval::point(point[1])?, - Interval::point(point[2])?, - ], - } - } - }; - for corner in 0..8 { - let point = std::array::from_fn(|axis| { - if corner & (1 << axis) == 0 { - edge_bounds.axes[axis].lo - } else { - edge_bounds.axes[axis].hi - } - }); - let local = plane.local(point); - for axis in 0..2 { - bounds[axis][0] = bounds[axis][0].min(local[axis]); - bounds[axis][1] = bounds[axis][1].max(local[axis]); - } - } - } - for bound in &mut bounds { - bound[0] -= accuracy.geometric; - bound[1] += accuracy.geometric; - } - Ok(bounds) - }; - let mark_cut = |builder: &mut Builder, face: u32, local_face: u32, key: String| { - builder.brep.topology.faces[face as usize].provenance = FaceProvenance { - sources: vec![FaceSource { - entity: opening.source.clone(), - body: opening.source.clone(), - key, - face: local_face, - }], - role: FaceRole::Cut, - reversed: true, - }; - }; - - if !opening.touches_bottom() { - let lower_outer_arc = opening.lower_outer_arc.ok_or_else(|| { - GeometryError::InvalidTopology("opening lower_cap edge missing".into()) - })?; - let lower_inner_arc = opening.lower_inner_arc.ok_or_else(|| { - GeometryError::InvalidTopology("opening lower_cap edge missing".into()) - })?; - let mut lower_cap_frame = frame; - lower_cap_frame.origin = frame.point([0.0, 0.0, opening.elevations[0]]); - let lower_cap_uses = [ - (lower_outer_arc, obl, obr, F), - (opening.connectors[0][1], obr, ibr, R), - (lower_inner_arc, ibr, ibl, R), - (opening.connectors[0][0], ibl, obl, F), - ] - .into_iter() - .map(|(edge, from, to, sense)| { - plane_boundary(&b, lower_cap_frame, edge, from, to, sense) - }) - .collect::, _>>()?; - let face = b.brep.topology.faces.len() as u32; - b.face( - &format!("opening-{opening_index}-lower_cap"), - SurfaceGeometry::Plane { - frame: lower_cap_frame, - }, - curved_plane_bounds( - &b, - lower_cap_frame, - [ - lower_outer_arc, - opening.connectors[0][1], - lower_inner_arc, - opening.connectors[0][0], - ], - )?, - lower_cap_uses, - )?; - mark_cut(&mut b, face, 0, "lower_cap".into()); - } - - let upper_cap_frame = Frame3 { - origin: frame.point([0.0, 0.0, opening.elevations[1]]), - y: scale(frame.y, -1.0), - z: scale(frame.z, -1.0), - ..frame - }; - let upper_cap_uses = [ - (opening.upper_outer_arc, otr, otl, R), - (opening.connectors[1][0], otl, itl, R), - (opening.upper_inner_arc, itl, itr, F), - (opening.connectors[1][1], itr, otr, F), - ] - .into_iter() - .map(|(edge, from, to, sense)| plane_boundary(&b, upper_cap_frame, edge, from, to, sense)) - .collect::, _>>()?; - let face = b.brep.topology.faces.len() as u32; - b.face( - &format!("opening-{opening_index}-upper_cap"), - SurfaceGeometry::Plane { - frame: upper_cap_frame, - }, - curved_plane_bounds( - &b, - upper_cap_frame, - [ - opening.upper_outer_arc, - opening.connectors[1][0], - opening.upper_inner_arc, - opening.connectors[1][1], - ], - )?, - upper_cap_uses, - )?; - mark_cut(&mut b, face, 1, "upper_cap".into()); - - let center_tangent = frame.vector([ - -((opening.inner_angles[0] + opening.inner_angles[1]) * 0.5).sin(), - ((opening.inner_angles[0] + opening.inner_angles[1]) * 0.5).cos(), - 0.0, - ]); - let left_frame = Frame3::from_axis( - b.brep.topology.vertices[ibl as usize].position, - center_tangent, - sub( - b.brep.topology.vertices[obl as usize].position, - b.brep.topology.vertices[ibl as usize].position, - ), - )?; - let left_uses = [ - (opening.connectors[0][0], obl, ibl, R), - (opening.inner_verticals[0], ibl, itl, F), - (opening.connectors[1][0], itl, otl, F), - (opening.outer_verticals[0], otl, obl, R), - ] - .into_iter() - .map(|(edge, from, to, sense)| plane_boundary(&b, left_frame, edge, from, to, sense)) - .collect::, _>>()?; - let face = b.brep.topology.faces.len() as u32; - b.face( - &format!("opening-{opening_index}-side-left"), - SurfaceGeometry::Plane { frame: left_frame }, - plane_bounds(&b, left_frame, [obl, ibl, itl, otl]), - left_uses, - )?; - mark_cut(&mut b, face, 2, "side-left".into()); - - let right_frame = Frame3::from_axis( - b.brep.topology.vertices[ibr as usize].position, - scale(center_tangent, -1.0), - sub( - b.brep.topology.vertices[obr as usize].position, - b.brep.topology.vertices[ibr as usize].position, - ), - )?; - let right_uses = [ - (opening.connectors[0][1], ibr, obr, F), - (opening.outer_verticals[1], obr, otr, F), - (opening.connectors[1][1], otr, itr, R), - (opening.inner_verticals[1], itr, ibr, R), - ] - .into_iter() - .map(|(edge, from, to, sense)| plane_boundary(&b, right_frame, edge, from, to, sense)) - .collect::, _>>()?; - let face = b.brep.topology.faces.len() as u32; - b.face( - &format!("opening-{opening_index}-side-right"), - SurfaceGeometry::Plane { frame: right_frame }, - plane_bounds(&b, right_frame, [ibr, obr, otr, itr]), - right_uses, - )?; - mark_cut(&mut b, face, 3, "side-right".into()); - } - if !built_openings.is_empty() { - b.brep.topology.faces[0].provenance.role = FaceRole::Split; - b.brep.topology.faces[1].provenance.role = FaceRole::Split; - } - b.finish() -} - -fn loop_halfedges_for_primitive( - brep: &BrepEnvelope, - loop_id: u32, -) -> Result, GeometryError> { - let start = brep.topology.loops[loop_id as usize].start_halfedge; - let mut current = start; - let mut result = Vec::new(); - for _ in 0..=brep.topology.halfedges.len() { - result.push(current); - current = brep.topology.halfedges[current as usize] - .next - .ok_or_else(|| GeometryError::InvalidTopology("open primitive loop".into()))?; - if current == start { - return Ok(result); - } - } - Err(GeometryError::InvalidTopology( - "primitive loop does not close".into(), - )) -} - -fn arched_opening_top_edge( - brep: &BrepEnvelope, - face: u32, - frame: Frame3, - elevation: f64, -) -> Result { - let face = &brep.topology.faces[face as usize]; - let tolerance = brep.accuracy.geometric * 4.0; - for loop_id in &face.trim.holes { - for halfedge in loop_halfedges_for_primitive(brep, *loop_id)? { - let use_ = &brep.topology.halfedges[halfedge as usize]; - let edge = &brep.topology.edges[use_.edge as usize]; - let EdgeGeometry::Curve { curve, .. } = edge.geometry else { - continue; - }; - if !matches!( - brep.geometry.curves[curve as usize], - CurveGeometry::Circle { .. } - ) { - continue; - } - let elevations = [use_.from, use_.to].map(|vertex| { - dot( - sub( - brep.topology.vertices[vertex as usize].position, - frame.origin, - ), - frame.z, - ) - }); - if elevations - .into_iter() - .all(|value| (value - elevation).abs() <= tolerance) - { - return Ok(edge.id); - } - } - } - Err(GeometryError::InvalidTopology( - "arched opening cap edge is missing".into(), - )) -} - -fn intersection_point_at( - graph: &IntersectionGraph, - branch: &IntersectionBranch, - parameter: f64, -) -> Result { - let CurveGeometry::Intersection { definition } = graph.geometry.curves[branch.curve as usize] - else { - return Err(GeometryError::CoverageGap { - families: ["arched opening".into(), "non-numerical SSI branch".into()], - }); - }; - graph.geometry.intersections[definition as usize].evaluate(parameter, &graph.geometry) -} - -fn upper_intersection_definition( - graph: &IntersectionGraph, - branch: &IntersectionBranch, - vertical_origin: Point3, - vertical: Point3, - desired_start: Point3, - desired_end: Point3, - support_surfaces: [u32; 2], - output_surfaces: [&SurfaceGeometry; 2], - accuracy: Accuracy, -) -> Result { - let range = branch.range; - let signed_height = |parameter| { - intersection_point_at(graph, branch, parameter) - .map(|point| dot(sub(point.point, vertical_origin), vertical)) - }; - let subdivisions = 512; - let mut roots = Vec::new(); - let mut previous_parameter = range.lo; - let mut previous_value = signed_height(previous_parameter)?; - for index in 1..=subdivisions { - let parameter = range.lo + range.width() * index as f64 / subdivisions as f64; - let value = signed_height(parameter)?; - if previous_value == 0.0 { - roots.push(previous_parameter); - } else if value == 0.0 || previous_value.signum() != value.signum() { - let mut lo = previous_parameter; - let mut hi = parameter; - let mut lo_value = previous_value; - for _ in 0..64 { - let middle = 0.5 * (lo + hi); - let middle_value = signed_height(middle)?; - if middle_value == 0.0 || hi - lo <= f64::EPSILON * middle.abs().max(1.0) * 16.0 { - lo = middle; - hi = middle; - break; - } - if lo_value.signum() == middle_value.signum() { - lo = middle; - lo_value = middle_value; - } else { - hi = middle; - } - } - roots.push(0.5 * (lo + hi)); - } - previous_parameter = parameter; - previous_value = value; - } - roots.sort_by(f64::total_cmp); - roots.dedup_by(|a, b| (*a - *b).abs() <= accuracy.intersection); - if roots.len() != 2 { - return Err(GeometryError::UnresolvedIntersection(format!( - "arched opening expected two spring intersections, found {}", - roots.len() - ))); - } - let direct_midpoint = 0.5 * (roots[0] + roots[1]); - let pieces = if signed_height(direct_midpoint)? > 0.0 { - vec![(roots[0], roots[1])] - } else { - vec![(roots[1], range.hi), (range.lo, roots[0])] - }; - let total_parameter = pieces.iter().map(|(lo, hi)| hi - lo).sum::(); - if total_parameter <= accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "arched opening curve is below geometric resolution".into(), - )); - } - let sample_count = 128usize; - let mut source_parameters = Vec::with_capacity(sample_count + 1); - for sample in 0..=sample_count { - let mut distance = total_parameter * sample as f64 / sample_count as f64; - let mut parameter = pieces.last().unwrap().1; - for (lo, hi) in &pieces { - let width = hi - lo; - if distance <= width { - parameter = lo + distance; - break; - } - distance -= width; - } - source_parameters.push(parameter); - } - let periods = output_surfaces.map(|surface| surface.charts()[0].periods); - let mut raw = Vec::with_capacity(source_parameters.len()); - for parameter in source_parameters { - let point = intersection_point_at(graph, branch, parameter)?; - raw.push((point.point, [point.uv_a, point.uv_b])); - } - if norm(sub(raw[0].0, desired_start)) > norm(sub(raw.last().unwrap().0, desired_start)) { - raw.reverse(); - } - for index in 0..raw.len() { - let previous_uv = index.checked_sub(1).map(|previous| raw[previous].1); - let (point, uv) = &mut raw[index]; - if index == 0 { - *point = desired_start; - } else if index + 1 == sample_count + 1 { - *point = desired_end; - } - for side in 0..2 { - uv[side] = output_surfaces[side].project(*point, Some(uv[side]))?; - if let Some(previous_uv) = previous_uv { - for axis in 0..2 { - if let Some(period) = periods[side][axis] { - let previous = previous_uv[side][axis]; - uv[side][axis] += ((previous - uv[side][axis]) / period).round() * period; - } - } - } - } - } - let mut anchors = Vec::with_capacity(raw.len()); - let mut parameter = 0.0; - for (index, (point, uv)) in raw.iter().enumerate() { - if index > 0 { - parameter += norm(sub(*point, raw[index - 1].0)); - } - anchors.push(TraceAnchor { - parameter, - point: *point, - uv_a: uv[0], - uv_b: uv[1], - }); - } - let uv_tubes = anchors - .windows(2) - .map(|pair| { - [ - [pair[0].uv_a[0], pair[1].uv_a[0]], - [pair[0].uv_a[1], pair[1].uv_a[1]], - [pair[0].uv_b[0], pair[1].uv_b[0]], - [pair[0].uv_b[1], pair[1].uv_b[1]], - ] - .map(|values| { - let padding = (values[1] - values[0]).abs().max(accuracy.intersection) * 2.0; - Interval::new( - values[0].min(values[1]) - padding, - values[0].max(values[1]) + padding, - ) - }) - .into_iter() - .collect::, _>>()? - .try_into() - .map_err(|_| GeometryError::InvalidGeometry("arched opening UV tube".into())) - }) - .collect::, GeometryError>>()?; - Ok(IntersectionDefinition { - surfaces: support_surfaces, - anchors, - uv_tubes, - residual_tolerance: accuracy.intersection, - }) -} - -#[allow(clippy::too_many_arguments)] -pub fn annular_sector_extrusion_with_arched_opening( - id: String, - frame: Frame3, - radius: f64, - thickness: f64, - height: f64, - start_angle: f64, - sweep_angle: f64, - opening: AnnularSectorOpening, - accuracy: Accuracy, -) -> Result { - let arch_radius = opening.width * 0.5; - let lower_height = opening.height - arch_radius; - if lower_height <= 4.0 * accuracy.geometric { - return Err(GeometryError::InvalidGeometry( - "arched opening height must exceed its radius".into(), - )); - } - let spring_elevation = opening.bottom + lower_height; - let mut result = annular_sector_extrusion_with_openings( - id, - frame, - radius, - thickness, - height, - start_angle, - sweep_angle, - vec![AnnularSectorOpening { - height: lower_height, - ..opening.clone() - }], - accuracy, - )?; - let plain = annular_sector_extrusion( - "arched-opening-support".into(), - frame, - radius, - thickness, - height, - start_angle, - sweep_angle, - accuracy, - )?; - let (sin_angle, cos_angle) = opening.angle.sin_cos(); - let radial = frame.vector([cos_angle, sin_angle, 0.0]); - let tangent = frame.vector([-sin_angle, cos_angle, 0.0]); - let cutter_frame = Frame3 { - origin: frame.point([ - (radius - thickness) * cos_angle, - (radius - thickness) * sin_angle, - spring_elevation, - ]), - x: tangent, - y: frame.z, - z: radial, - }; - let cutter = cylinder( - opening.id.clone(), - cutter_frame, - arch_radius, - thickness * 2.0, - accuracy, - )?; - let graphs = [0_u32, 1_u32] - .map(|face| { - intersect_faces( - FaceView { brep: &plain, face }, - FaceView { - brep: &cutter, - face: 0, - }, - ) - }) - .into_iter() - .collect::, _>>()?; - if graphs.iter().any(|graph| graph.branches.len() != 1) { - return Err(GeometryError::UnresolvedIntersection( - "arched opening support produced multiple SSI branches".into(), - )); - } - - let outer_edge = arched_opening_top_edge(&result, 0, frame, spring_elevation)?; - let inner_edge = arched_opening_top_edge(&result, 1, frame, spring_elevation)?; - let upper_cap = result - .topology - .faces - .iter() - .find(|face| face.key == "opening-0-upper_cap") - .map(|face| face.id) - .ok_or_else(|| GeometryError::InvalidTopology("arched opening cap face missing".into()))?; - let cutter_surface = result.geometry.surfaces.len() as u32; - result - .geometry - .surfaces - .push(cutter.geometry.surfaces[0].clone()); - let mut endpoint_uvs = Vec::new(); - for (side, edge_id) in [outer_edge, inner_edge].into_iter().enumerate() { - let edge = &result.topology.edges[edge_id as usize]; - let primary = &result.topology.halfedges[edge.halfedge as usize]; - let desired_start_vertex = if primary.geometry_use.sense == Orientation::Forward { - primary.from - } else { - primary.to - }; - let desired_end_vertex = if primary.geometry_use.sense == Orientation::Forward { - primary.to - } else { - primary.from - }; - let desired_start = result.topology.vertices[desired_start_vertex as usize].position; - let desired_end = result.topology.vertices[desired_end_vertex as usize].position; - let definition_geometry = upper_intersection_definition( - &graphs[side], - &graphs[side].branches[0], - cutter_frame.origin, - cutter_frame.y, - desired_start, - desired_end, - [result.topology.faces[side].surface, cutter_surface], - [ - &result.geometry.surfaces[result.topology.faces[side].surface as usize], - &result.geometry.surfaces[cutter_surface as usize], - ], - accuracy, - )?; - endpoint_uvs.extend([ - ( - definition_geometry.anchors[0].point, - definition_geometry.anchors[0].uv_b, - ), - ( - definition_geometry.anchors.last().unwrap().point, - definition_geometry.anchors.last().unwrap().uv_b, - ), - ]); - let range = Interval::new(0.0, definition_geometry.anchors.last().unwrap().parameter)?; - let definition = result.geometry.intersections.len() as u32; - result.geometry.intersections.push(definition_geometry); - let curve = result.geometry.curves.len() as u32; - result - .geometry - .curves - .push(CurveGeometry::Intersection { definition }); - result.topology.edges[edge_id as usize].geometry = EdgeGeometry::Curve { curve, range }; - let support_pcurve = result.geometry.pcurves.len() as u32; - result - .geometry - .pcurves - .push(PcurveGeometry::IntersectionSide { - definition, - side: IntersectionSide::A, - }); - let cutter_pcurve = result.geometry.pcurves.len() as u32; - result - .geometry - .pcurves - .push(PcurveGeometry::IntersectionSide { - definition, - side: IntersectionSide::B, - }); - for halfedge in &mut result.topology.halfedges { - if halfedge.edge == edge_id { - halfedge.geometry_use.pcurve = Some(if halfedge.face == Some(side as u32) { - support_pcurve - } else if halfedge.face == Some(upper_cap) { - cutter_pcurve - } else { - return Err(GeometryError::InvalidTopology( - "arched opening edge has an unexpected face use".into(), - )); - }); - } - } - } - - let cutter_surface_geometry = result.geometry.surfaces[cutter_surface as usize].clone(); - for halfedge_id in loop_halfedges_for_primitive( - &result, - result.topology.faces[upper_cap as usize].trim.outer, - )? { - let halfedge = result.topology.halfedges[halfedge_id as usize].clone(); - if halfedge.edge == outer_edge || halfedge.edge == inner_edge { - continue; - } - let edge = &result.topology.edges[halfedge.edge as usize]; - let EdgeGeometry::Curve { curve, range } = edge.geometry else { - return Err(GeometryError::InvalidTopology( - "arched opening connector is collapsed".into(), - )); - }; - let points: [Point3; 2] = [range.lo, range.hi] - .map(|parameter| result.geometry.curve(curve)?.point_at(parameter)) - .into_iter() - .collect::, _>>()? - .try_into() - .map_err(|_| GeometryError::InvalidGeometry("arched connector points".into()))?; - let mut coordinates: [[f64; 2]; 2] = points - .map(|point| cutter_surface_geometry.project(point, None)) - .into_iter() - .collect::, _>>()? - .try_into() - .map_err(|_| GeometryError::InvalidGeometry("arched connector pcurve".into()))?; - for (point, uv) in points.into_iter().zip(&mut coordinates) { - if let Some((_, endpoint_uv)) = endpoint_uvs - .iter() - .min_by(|a, b| norm(sub(a.0, point)).total_cmp(&norm(sub(b.0, point)))) - { - if norm(sub( - endpoint_uvs - .iter() - .min_by(|a, b| norm(sub(a.0, point)).total_cmp(&norm(sub(b.0, point)))) - .unwrap() - .0, - point, - )) <= accuracy.geometric * 4.0 - { - *uv = *endpoint_uv; - } - } - } - let direction = [ - (coordinates[1][0] - coordinates[0][0]) / range.width(), - (coordinates[1][1] - coordinates[0][1]) / range.width(), - ]; - let origin = [ - coordinates[0][0] - direction[0] * range.lo, - coordinates[0][1] - direction[1] * range.lo, - ]; - let pcurve = result.geometry.pcurves.len() as u32; - result - .geometry - .pcurves - .push(PcurveGeometry::Line2 { origin, direction }); - result.topology.halfedges[halfedge_id as usize] - .geometry_use - .pcurve = Some(pcurve); - } - result.topology.faces[upper_cap as usize].surface = cutter_surface; - result.topology.faces[upper_cap as usize].sense = Orientation::Reverse; - result.topology.faces[upper_cap as usize].key = "opening-0-arch".into(); - let mut bounds = [ - [f64::INFINITY, f64::NEG_INFINITY], - [f64::INFINITY, f64::NEG_INFINITY], - ]; - for halfedge_id in loop_halfedges_for_primitive( - &result, - result.topology.faces[upper_cap as usize].trim.outer, - )? { - let halfedge = &result.topology.halfedges[halfedge_id as usize]; - let pcurve = halfedge.geometry_use.pcurve.unwrap(); - let range = result.topology.edges[halfedge.edge as usize] - .geometry - .range(); - for sample in 0..=64 { - let parameter = range.lo + range.width() * sample as f64 / 64.0; - let uv = result.geometry.pcurve_at(pcurve, parameter)?; - for axis in 0..2 { - bounds[axis][0] = bounds[axis][0].min(uv[axis]); - bounds[axis][1] = bounds[axis][1].max(uv[axis]); - } - } - } - result.topology.faces[upper_cap as usize].trim.uv_bounds = [ - Interval::new(bounds[0][0], bounds[0][1])?, - Interval::new(bounds[1][0], bounds[1][1])?, - ]; - result.revision = result - .revision - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("arched opening revision overflow".into()))?; - result.validate()?; - Ok(result) -} - -pub fn sphere( - id: String, - frame: Frame3, - radius: f64, - accuracy: Accuracy, -) -> Result { - frame.validate()?; - dimensions(&[radius])?; - let mut b = Builder::new(id, accuracy)?; - let pi2 = std::f64::consts::FRAC_PI_2; - let tau = std::f64::consts::TAU; - let south = b.vertex(frame.point([0.0, 0.0, -radius])); - let north = b.vertex(frame.point([0.0, 0.0, radius])); - let bottom = b.edge_geometry(EdgeGeometry::Collapsed { vertex: south }, true); - let top = b.edge_geometry(EdgeGeometry::Collapsed { vertex: north }, true); - let meridian_frame = Frame3 { - origin: frame.origin, - x: frame.x, - y: frame.z, - z: scale(frame.y, -1.0), - }; - let seam = b.edge( - CurveGeometry::Circle { - frame: meridian_frame, - radius, - }, - Interval::new(-pi2, pi2)?, - true, - ); - b.face( - "skin", - SurfaceGeometry::Sphere { frame, radius }, - [[0.0, tau], [-pi2, pi2]], - vec![ - boundary( - bottom, - south, - south, - Orientation::Forward, - uv_line([0.0, -pi2], [tau, 0.0]), - ), - boundary( - seam, - south, - north, - Orientation::Forward, - uv_line([tau, 0.0], [0.0, 1.0]), - ), - boundary( - top, - north, - north, - Orientation::Forward, - uv_line([tau, pi2], [-tau, 0.0]), - ), - boundary( - seam, - north, - south, - Orientation::Reverse, - uv_line([0.0, 0.0], [0.0, 1.0]), - ), - ], - )?; - b.finish() -} - -pub fn torus( - id: String, - frame: Frame3, - major_radius: f64, - minor_radius: f64, - accuracy: Accuracy, -) -> Result { - let surface = SurfaceGeometry::Torus { - frame, - major_radius, - minor_radius, - }; - surface.validate()?; - let mut b = Builder::new(id, accuracy)?; - let vertex = b.vertex(frame.point([major_radius + minor_radius, 0.0, 0.0])); - let tau = std::f64::consts::TAU; - let ring = b.edge( - CurveGeometry::Circle { - frame, - radius: major_radius + minor_radius, - }, - Interval::new(0.0, tau)?, - true, - ); - let tube_frame = Frame3 { - origin: frame.point([major_radius, 0.0, 0.0]), - x: frame.x, - y: frame.z, - z: scale(frame.y, -1.0), - }; - let tube = b.edge( - CurveGeometry::Circle { - frame: tube_frame, - radius: minor_radius, - }, - Interval::new(0.0, tau)?, - true, - ); - b.face( - "skin", - surface, - [[0.0, tau]; 2], - vec![ - boundary( - ring, - vertex, - vertex, - Orientation::Forward, - uv_line([0.0, 0.0], [1.0, 0.0]), - ), - boundary( - tube, - vertex, - vertex, - Orientation::Forward, - uv_line([tau, 0.0], [0.0, 1.0]), - ), - boundary( - ring, - vertex, - vertex, - Orientation::Reverse, - uv_line([0.0, tau], [1.0, 0.0]), - ), - boundary( - tube, - vertex, - vertex, - Orientation::Reverse, - uv_line([0.0, 0.0], [0.0, 1.0]), - ), - ], - )?; - b.finish() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::Surface; - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-9, - intersection: 1e-10, - tessellation: 1e-3, - exchange: 1e-5, - } - } - #[test] - fn cuboid_edges_are_shared_and_all_face_normals_point_outward() { - let frame = Frame3 { - origin: [1.0, 2.0, 3.0], - x: [0.0, 1.0, 0.0], - y: [-1.0, 0.0, 0.0], - ..Frame3::IDENTITY - }; - let body = cuboid("box".into(), frame, [2.0, 3.0, 4.0], accuracy()).unwrap(); - assert_eq!( - ( - body.topology.vertices.len(), - body.topology.edges.len(), - body.topology.faces.len() - ), - (8, 12, 6) - ); - let center = frame.point([1.0, 1.5, 2.0]); - for face in &body.topology.faces { - let surface = body.geometry.surface(face.surface).unwrap(); - let uv = face.trim.uv_bounds.map(|range| (range.lo + range.hi) / 2.0); - assert!( - dot( - sub(surface.point_at(uv).unwrap(), center), - surface.normal_at(uv).unwrap() - ) > 0.0 - ); - } - let serialized = body.to_json().unwrap(); - assert_eq!( - BrepEnvelope::from_json(&serialized) - .unwrap() - .to_json() - .unwrap(), - serialized - ); - assert!(cuboid("bad".into(), frame, [0.0, 1.0, 1.0], accuracy()).is_err()); - } - #[test] - fn linear_extrusion_preserves_planar_holes_and_shared_edges() { - let outer = vec![[0.0, 0.0], [5.0, 0.0], [5.0, 4.0], [0.0, 4.0]]; - let hole = vec![[1.0, 1.0], [1.0, 3.0], [3.0, 3.0], [3.0, 1.0]]; - let body = linear_extrusion( - "profile".into(), - Frame3::IDENTITY, - outer, - vec![hole], - 2.5, - accuracy(), - ) - .unwrap(); - - body.validate().unwrap(); - assert_eq!(body.topology.vertices.len(), 16); - assert_eq!(body.topology.edges.len(), 24); - assert_eq!(body.topology.faces.len(), 10); - assert_eq!(body.topology.shells.len(), 1); - assert_eq!(body.solids.len(), 1); - assert_eq!(body.topology.faces[0].trim.holes.len(), 1); - assert_eq!(body.topology.faces[1].trim.holes.len(), 1); - assert!(body - .topology - .edges - .iter() - .all(|edge| edge.twin_halfedge.is_some())); - let tessellation = crate::analytic::tessellation::tessellate(&body, 0.01, 100_000).unwrap(); - assert!(!tessellation.indices.is_empty()); - assert_eq!( - tessellation.triangle_face_ids.len(), - tessellation.indices.len() / 3 - ); - } - - #[test] - fn arc_edged_extrusion_preserves_cylindrical_sides_for_both_sweep_signs() { - for sign in [1.0, -1.0] { - let sweep = sign * std::f64::consts::FRAC_PI_2; - let outer = 2.0; - let inner = 1.5; - let point = |radius: f64, angle: f64| [radius * angle.cos(), radius * angle.sin()]; - let edges = vec![ - ProfileEdge::Arc { - center: [0.0, 0.0], - radius: outer, - start_angle: 0.0, - sweep_angle: sweep, - }, - ProfileEdge::Line { - from: point(outer, sweep), - to: point(inner, sweep), - }, - ProfileEdge::Arc { - center: [0.0, 0.0], - radius: inner, - start_angle: sweep, - sweep_angle: -sweep, - }, - ProfileEdge::Line { - from: point(inner, 0.0), - to: point(outer, 0.0), - }, - ]; - let body = arc_edged_extrusion( - format!("arc-strip-{sign}"), - Frame3::IDENTITY, - edges, - 3.0, - accuracy(), - ) - .unwrap(); - body.validate().unwrap(); - assert_eq!(body.topology.faces.len(), 6); - assert_eq!(body.topology.shells.len(), 1); - assert_eq!(body.solids.len(), 1); - assert_eq!( - body.geometry - .surfaces - .iter() - .filter(|surface| matches!(surface, SurfaceGeometry::Cylinder { .. })) - .count(), - 2 - ); - assert_eq!( - body.geometry - .curves - .iter() - .filter(|curve| matches!(curve, CurveGeometry::Circle { .. })) - .count(), - 4 - ); - let middle = sweep * 0.5; - assert_eq!( - crate::analytic::query::classify_point( - &body, - [1.75 * middle.cos(), 1.75 * middle.sin(), 1.0], - ) - .unwrap(), - crate::analytic::query::PointClassification::Inside, - ); - assert_eq!( - crate::analytic::query::classify_point( - &body, - [1.0 * middle.cos(), 1.0 * middle.sin(), 1.0], - ) - .unwrap(), - crate::analytic::query::PointClassification::Outside, - ); - let mesh = crate::analytic::tessellation::tessellate(&body, 0.01, 100_000).unwrap(); - assert!(!mesh.indices.is_empty()); - } - } - - #[test] - fn arc_edged_extrusion_preserves_an_analytic_profile_hole() { - let quarter = std::f64::consts::FRAC_PI_2; - let outer = vec![ - ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.0, - start_angle: 0.0, - sweep_angle: quarter, - }, - ProfileEdge::Line { - from: [0.0, 2.0], - to: [0.0, 1.0], - }, - ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.0, - start_angle: quarter, - sweep_angle: -quarter, - }, - ProfileEdge::Line { - from: [1.0, 0.0], - to: [2.0, 0.0], - }, - ]; - let hole = vec![ - ProfileEdge::Arc { - center: [1.2, 1.2], - radius: 0.1, - start_angle: 0.0, - sweep_angle: -std::f64::consts::PI, - }, - ProfileEdge::Arc { - center: [1.2, 1.2], - radius: 0.1, - start_angle: -std::f64::consts::PI, - sweep_angle: -std::f64::consts::PI, - }, - ]; - let body = arc_edged_extrusion_with_holes( - "arc-profile-hole".into(), - Frame3::IDENTITY, - outer, - vec![hole], - 3.0, - accuracy(), - ) - .unwrap(); - body.validate().unwrap(); - assert_eq!(body.topology.faces[0].trim.holes.len(), 1); - assert_eq!(body.topology.faces[1].trim.holes.len(), 1); - assert_eq!(body.topology.shells.len(), 1); - assert_eq!(body.solids.len(), 1); - assert_eq!( - crate::analytic::query::classify_point(&body, [1.2, 1.2, 1.0]).unwrap(), - crate::analytic::query::PointClassification::Outside - ); - assert_eq!( - crate::analytic::query::classify_point(&body, [1.4, 1.0, 1.0]).unwrap(), - crate::analytic::query::PointClassification::Inside - ); - assert!( - !crate::analytic::tessellation::tessellate(&body, 0.01, 100_000) - .unwrap() - .indices - .is_empty() - ); - } - - #[test] - fn arc_edged_extrusion_rejects_holes_outside_or_crossing_its_profile() { - let quarter = std::f64::consts::FRAC_PI_2; - let outer = vec![ - ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.0, - start_angle: 0.0, - sweep_angle: quarter, - }, - ProfileEdge::Line { - from: [0.0, 2.0], - to: [0.0, 1.0], - }, - ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.0, - start_angle: quarter, - sweep_angle: -quarter, - }, - ProfileEdge::Line { - from: [1.0, 0.0], - to: [2.0, 0.0], - }, - ]; - let square = |x0, y0, x1, y1| { - let points = [[x0, y0], [x1, y0], [x1, y1], [x0, y1]]; - (0..4) - .map(|index| ProfileEdge::Line { - from: points[index], - to: points[(index + 1) % 4], - }) - .collect::>() - }; - for hole in [square(3.0, 3.0, 3.2, 3.2), square(1.9, 0.1, 2.1, 0.3)] { - assert!(arc_edged_extrusion_with_holes( - "invalid-arc-hole".into(), - Frame3::IDENTITY, - outer.clone(), - vec![hole], - 3.0, - accuracy(), - ) - .is_err()); - } - } - - #[test] - fn arc_edged_extrusion_rejects_exact_line_arc_crossings_and_overlap() { - let crossing = vec![ - ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.0, - start_angle: 0.0, - sweep_angle: std::f64::consts::PI, - }, - ProfileEdge::Line { - from: [-1.0, 0.0], - to: [0.0, 1.2], - }, - ProfileEdge::Line { - from: [0.0, 1.2], - to: [1.0, 0.0], - }, - ]; - assert!(arc_edged_extrusion( - "crossing".into(), - Frame3::IDENTITY, - crossing, - 2.0, - accuracy(), - ) - .is_err()); - let overlapping = vec![ - ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.0, - start_angle: 0.0, - sweep_angle: std::f64::consts::PI, - }, - ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.0, - start_angle: std::f64::consts::PI, - sweep_angle: -std::f64::consts::FRAC_PI_2, - }, - ProfileEdge::Line { - from: [0.0, 1.0], - to: [1.0, 0.0], - }, - ]; - assert!(arc_edged_extrusion( - "overlap".into(), - Frame3::IDENTITY, - overlapping, - 2.0, - accuracy(), - ) - .is_err()); - } - - #[test] - fn arc_edged_extrusion_accepts_nonradial_join_caps() { - let outer_end = std::f64::consts::FRAC_PI_2; - let inner_start = outer_end - 0.1; - let inner_end = 0.1; - let point = |radius: f64, angle: f64| [radius * angle.cos(), radius * angle.sin()]; - let profile = vec![ - ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.0, - start_angle: 0.0, - sweep_angle: outer_end, - }, - ProfileEdge::Line { - from: point(2.0, outer_end), - to: point(1.5, inner_start), - }, - ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 1.5, - start_angle: inner_start, - sweep_angle: inner_end - inner_start, - }, - ProfileEdge::Line { - from: point(1.5, inner_end), - to: point(2.0, 0.0), - }, - ]; - let body = arc_edged_extrusion( - "curved-join".into(), - Frame3::IDENTITY, - profile, - 2.4, - accuracy(), - ) - .unwrap(); - body.validate().unwrap(); - assert_eq!(body.topology.faces.len(), 6); - assert_eq!(body.solids.len(), 1); - } - - #[test] - fn linear_extrusion_rejects_invalid_profile_relationships() { - let square = vec![[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]]; - let crossing = vec![[1.0, 1.0], [3.0, 3.0], [1.0, 3.0], [3.0, 1.0]]; - assert!(linear_extrusion( - "crossing".into(), - Frame3::IDENTITY, - crossing, - Vec::new(), - 1.0, - accuracy(), - ) - .is_err()); - let outside = vec![[3.0, 1.0], [5.0, 1.0], [5.0, 2.0], [3.0, 2.0]]; - assert!(linear_extrusion( - "outside".into(), - Frame3::IDENTITY, - square.clone(), - vec![outside], - 1.0, - accuracy(), - ) - .is_err()); - let first = vec![[0.5, 0.5], [2.5, 0.5], [2.5, 2.5], [0.5, 2.5]]; - let second = vec![[1.5, 1.5], [3.0, 1.5], [3.0, 3.0], [1.5, 3.0]]; - assert!(linear_extrusion( - "overlap".into(), - Frame3::IDENTITY, - square, - vec![first, second], - 1.0, - accuracy(), - ) - .is_err()); - } - #[test] - fn curved_topology_is_sparse_and_closed() { - let shapes = [ - cylinder("c".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy()).unwrap(), - cone("k".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy()).unwrap(), - sphere("s".into(), Frame3::IDENTITY, 1.0, accuracy()).unwrap(), - torus("t".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy()).unwrap(), - ]; - for (b, faces) in shapes.into_iter().zip([3, 2, 1, 1]) { - b.validate().unwrap(); - assert_eq!(b.topology.faces.len(), faces); - assert!(b.topology.vertices.len() <= 2); - assert_eq!(b.solids.len(), 1); - let json = b.to_json().unwrap(); - BrepEnvelope::from_json(&json).unwrap(); - } - } - #[test] - fn annular_cylinder_has_exact_inner_outer_supports_and_planar_holes() { - let body = - annular_cylinder("tube".into(), Frame3::IDENTITY, 0.5, 1.0, 2.0, accuracy()).unwrap(); - body.validate().unwrap(); - assert_eq!(body.topology.faces.len(), 4); - assert_eq!(body.topology.shells.len(), 1); - assert_eq!(body.solids.len(), 1); - assert_eq!(body.topology.faces[0].sense, Orientation::Forward); - assert_eq!(body.topology.faces[1].sense, Orientation::Reverse); - assert_eq!(body.topology.faces[2].trim.holes.len(), 1); - assert_eq!(body.topology.faces[3].trim.holes.len(), 1); - let mesh = crate::analytic::tessellation::tessellate(&body, 0.01, 100_000).unwrap(); - assert!(mesh.indices.len() > 0); - assert!(annular_cylinder( - "thin".into(), - Frame3::IDENTITY, - 1.0, - 1.0 + accuracy().geometric, - 2.0, - accuracy(), - ) - .is_err()); - } - #[test] - fn frusta_reduce_to_the_supported_surface_set() { - for radii in [(1.0, 2.0), (2.0, 1.0), (2.0, 0.0), (1.0, 1.0)] { - let b = frustum( - "f".into(), - Frame3::IDENTITY, - radii.0, - radii.1, - 3.0, - accuracy(), - ) - .unwrap(); - b.validate().unwrap(); - assert_eq!(b.topology.faces.len(), if radii.1 == 0.0 { 2 } else { 3 }); - } - } - #[test] - fn ring_and_positive_dimensions_are_enforced() { - assert!(torus("t".into(), Frame3::IDENTITY, 1.0, 1.0, accuracy()).is_err()); - assert!(cylinder("c".into(), Frame3::IDENTITY, 1.0, 0.0, accuracy()).is_err()); - } - #[test] - fn gallery_and_arbitrary_host_parameters_round_trip_without_changing_geometry() { - let frame = Frame3 { - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - ..Frame3::IDENTITY - }; - let mut bodies = vec![ - cylinder("c".into(), frame, 0.7, 1.8, accuracy()).unwrap(), - sphere("s".into(), frame, 0.8, accuracy()).unwrap(), - cone("cone".into(), frame, 0.7, 1.8, accuracy()).unwrap(), - frustum("f".into(), frame, 0.8, 0.45, 1.8, accuracy()).unwrap(), - torus("t".into(), frame, 0.75, 0.25, accuracy()).unwrap(), - ]; - for start in [0.15, 0.7, -2.3] { - for sweep in [1.8, -1.8, 0.33] { - bodies.push( - annular_sector_extrusion( - "host".into(), - frame, - 2.4, - 0.3, - 1.4, - start, - sweep, - accuracy(), - ) - .unwrap(), - ); - } - } - for body in bodies { - let json = body.to_json().unwrap(); - let decoded = - BrepEnvelope::from_json(&json).unwrap_or_else(|e| panic!("{}: {e}", body.id)); - assert_eq!(decoded.to_json().unwrap(), json); - } - } - #[test] - fn annular_sector_extrusions_preserve_analytic_boundaries_and_authored_end_identity() { - let frame = Frame3::from_axis([10.0, -4.0, 6.0], [1.0, 2.0, 3.0], [1.0, 0.0, 0.0]).unwrap(); - for sweep in [1.8, -1.8] { - let b = annular_sector_extrusion( - "host".into(), - frame, - 3.0, - 0.4, - 2.5, - 0.7, - sweep, - accuracy(), - ) - .unwrap(); - assert_eq!(b.topology.faces.len(), 6); - assert_eq!(b.topology.vertices.len(), 8); - assert_eq!(b.topology.edges.len(), 12); - assert_eq!( - b.geometry - .curves - .iter() - .filter(|c| matches!(c, CurveGeometry::Circle { .. })) - .count(), - 4 - ); - let end = b - .topology - .faces - .iter() - .find(|f| f.key == "axis-start") - .unwrap(); - let normal = b - .geometry - .surface(end.surface) - .unwrap() - .normal_at([0.0; 2]) - .unwrap(); - let tangent = frame.vector([-0.7_f64.sin(), 0.7_f64.cos(), 0.0]); - assert!((dot(normal, tangent) + sweep.signum()).abs() < 1e-12); - BrepEnvelope::from_json(&b.to_json().unwrap()).unwrap(); - } - assert!(annular_sector_extrusion( - "host".into(), - frame, - 1.0, - 2.0, - 1.0, - 0.0, - 1.0, - accuracy() - ) - .is_err()); - assert!(annular_sector_extrusion( - "host".into(), - frame, - 1.0, - 0.2, - 1.0, - 0.0, - std::f64::consts::TAU, - accuracy() - ) - .is_err()); - } - #[test] - fn annular_sector_extrusion_openings_are_exact_trimmed_cylinder_holes() { - let openings = vec![ - AnnularSectorOpening { - id: "raised_cutout-a".into(), - angle: 0.65, - width: 0.9, - bottom: 0.7, - height: 1.2, - }, - AnnularSectorOpening { - id: "raised_cutout-b".into(), - angle: 1.45, - width: 0.6, - bottom: 1.4, - height: 0.8, - }, - ]; - let host = annular_sector_extrusion_with_openings( - "host".into(), - Frame3::IDENTITY, - 3.0, - 0.4, - 3.0, - 0.0, - 2.1, - openings, - accuracy(), - ) - .unwrap(); - host.validate().unwrap(); - assert_eq!(host.topology.faces.len(), 14); - assert_eq!(host.topology.faces[0].trim.holes.len(), 2); - assert_eq!(host.topology.faces[1].trim.holes.len(), 2); - assert_eq!(host.topology.faces[0].provenance.role, FaceRole::Split); - assert_eq!(host.topology.faces[1].provenance.role, FaceRole::Split); - assert_eq!( - host.topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut) - .count(), - 8 - ); - assert!(host.geometry.surfaces.iter().all(|surface| matches!( - surface, - SurfaceGeometry::Cylinder { .. } | SurfaceGeometry::Plane { .. } - ))); - let coarse = crate::analytic::tessellation::tessellate(&host, 0.04, 100_000).unwrap(); - let mesh = crate::analytic::tessellation::tessellate(&host, 0.01, 100_000).unwrap(); - assert!(mesh.indices.len() > coarse.indices.len()); - let positions = mesh - .positions - .chunks_exact(3) - .map(|point| [point[0].to_bits(), point[1].to_bits(), point[2].to_bits()]) - .collect::>(); - assert!(mesh - .outline_positions - .chunks_exact(3) - .all(|point| positions.contains(&[ - point[0].to_bits(), - point[1].to_bits(), - point[2].to_bits(), - ]))); - BrepEnvelope::from_json(&host.to_json().unwrap()).unwrap(); - - let lower_cutout = annular_sector_extrusion_with_openings( - "host".into(), - Frame3::IDENTITY, - 3.0, - 0.4, - 3.0, - 0.0, - 2.1, - vec![AnnularSectorOpening { - id: "lower_cutout".into(), - angle: 1.0, - width: 0.9, - bottom: 0.0, - height: 2.1, - }], - accuracy(), - ) - .unwrap(); - lower_cutout.validate().unwrap(); - assert_eq!(lower_cutout.topology.faces[0].trim.holes.len(), 0); - assert_eq!(lower_cutout.topology.faces[1].trim.holes.len(), 0); - assert_eq!( - lower_cutout - .topology - .faces - .iter() - .filter(|face| face.key.starts_with("bottom-")) - .count(), - 2 - ); - assert_eq!( - lower_cutout - .topology - .faces - .iter() - .filter(|face| face.provenance.role == FaceRole::Cut) - .count(), - 3 - ); - crate::analytic::tessellation::tessellate(&lower_cutout, 0.01, 100_000).unwrap(); - BrepEnvelope::from_json(&lower_cutout.to_json().unwrap()).unwrap(); - } - - #[test] - fn annular_sector_extrusion_horizontal_opening_faces_bound_curved_edges() { - let sweep = -std::f64::consts::TAU / 3.0; - let host = annular_sector_extrusion_with_openings( - "example-host".into(), - Frame3::IDENTITY, - 2.4, - 0.3, - 1.4, - 0.0, - sweep, - vec![ - AnnularSectorOpening { - id: "lower_cutout".into(), - angle: sweep * 0.22, - width: 0.7, - bottom: 0.0, - height: 1.05, - }, - AnnularSectorOpening { - id: "raised_cutout".into(), - angle: sweep * 0.76, - width: 0.7, - bottom: 0.35, - height: 0.7, - }, - ], - accuracy(), - ) - .unwrap(); - host.validate().unwrap(); - crate::analytic::tessellation::tessellate(&host, 0.01, 100_000).unwrap(); - } - #[test] - fn body_bounds_cover_curved_extrema_absent_from_vertices() { - let b = cylinder("c".into(), Frame3::IDENTITY, 2.0, 3.0, accuracy()).unwrap(); - assert!(b.topology.vertices.iter().all(|v| v.position[0] == 2.0)); - let bounds = b.bounds().unwrap().unwrap(); - for p in [[-2.0, 0.0, 1.5], [0.0, -2.0, 1.5], [0.0, 2.0, 3.0]] { - assert!(bounds.contains(p)); - } - let empty = BrepEnvelope::new("empty".into(), accuracy()).unwrap(); - assert!(empty.bounds().unwrap().is_none()); - } - #[test] - fn cylinder_sector_keeps_one_trimmed_cylinder_face_and_exact_radial_caps() { - let sector = cylinder_sector( - "sector".into(), - Frame3::IDENTITY, - 2.0, - 3.0, - -1.2, - 1.2, - accuracy(), - ) - .unwrap(); - sector.validate().unwrap(); - assert_eq!(sector.topology.faces.len(), 5); - assert_eq!(sector.topology.edges.len(), 9); - assert_eq!(sector.topology.vertices.len(), 6); - assert!(matches!( - sector.geometry.surfaces[sector.topology.faces[0].surface as usize], - SurfaceGeometry::Cylinder { radius: 2.0, .. } - )); - assert!(sector.topology.faces[1..].iter().all(|face| matches!( - sector.geometry.surfaces[face.surface as usize], - SurfaceGeometry::Plane { .. } - ))); - let coarse = crate::analytic::tessellation::tessellate(§or, 0.05, 100_000).unwrap(); - let fine = crate::analytic::tessellation::tessellate(§or, 0.005, 100_000).unwrap(); - assert!(fine.indices.len() > coarse.indices.len()); - BrepEnvelope::from_json(§or.to_json().unwrap()).unwrap(); - - let full = cylinder_sector( - "full".into(), - Frame3::IDENTITY, - 2.0, - 3.0, - 0.0, - std::f64::consts::TAU, - accuracy(), - ) - .unwrap(); - assert_eq!(full.topology.faces.len(), 3); - } - #[test] - fn planar_polyhedron_keeps_explicit_shared_edges_and_plane_authority() { - let body = planar_polyhedron( - "tetrahedron".into(), - vec![ - [0.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - [0.0, 1.0, 0.0], - [0.0, 0.0, 1.0], - ], - vec![vec![0, 2, 1], vec![0, 1, 3], vec![0, 3, 2], vec![1, 2, 3]], - accuracy(), - ) - .unwrap(); - body.validate().unwrap(); - assert_eq!(body.topology.vertices.len(), 4); - assert_eq!(body.topology.edges.len(), 6); - assert_eq!(body.topology.faces.len(), 4); - assert!(body - .geometry - .surfaces - .iter() - .all(|surface| matches!(surface, SurfaceGeometry::Plane { .. }))); - assert!(body - .geometry - .curves - .iter() - .all(|curve| matches!(curve, CurveGeometry::Line { .. }))); - crate::analytic::tessellation::tessellate(&body, 0.01, 100_000).unwrap(); - - let open = planar_polyhedron( - "open".into(), - vec![ - [0.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - [0.0, 1.0, 0.0], - [0.0, 0.0, 1.0], - ], - vec![vec![0, 2, 1], vec![0, 1, 3], vec![0, 3, 2]], - accuracy(), - ); - assert!(open.is_err()); - } - #[test] - fn analytic_arc_wires_retain_signed_endpoints_and_single_edge_authority() { - let curve = CurveGeometry::Circle { - frame: Frame3::IDENTITY, - radius: 2.0, - }; - for sweep in [1.4, -1.4, std::f64::consts::TAU, -std::f64::consts::TAU] { - let brep = arc_wire("arc".into(), curve.clone(), 0.7, sweep, accuracy()).unwrap(); - let h = &brep.topology.halfedges[0]; - assert_eq!(h.geometry_use.sense.multiplier(), sweep.signum()); - assert!( - norm(sub( - brep.topology.vertices[h.from as usize].position, - curve.elementary_point(0.7).unwrap() - )) < 1e-10 - ); - assert_eq!(brep.topology.edges.len(), 1); - assert!(brep.topology.faces.is_empty()); - assert!(h.geometry_use.pcurve.is_none()); - assert_eq!( - brep.topology.wires[0].is_closed, - sweep.abs() == std::f64::consts::TAU - ); - let coarse = crate::analytic::tessellation::tessellate(&brep, 0.05, 100_000).unwrap(); - let fine = crate::analytic::tessellation::tessellate(&brep, 0.005, 100_000).unwrap(); - assert!(fine.outline_edge_ids.len() > coarse.outline_edge_ids.len()); - assert!(fine.outline_edge_ids.iter().all(|id| *id == 0)); - assert!(fine.indices.is_empty()); - BrepEnvelope::from_json(&brep.to_json().unwrap()).unwrap(); - } - } - #[test] - fn elliptical_arc_sampling_meets_deflection_and_rejects_invalid_spans() { - let curve = CurveGeometry::Ellipse { - frame: Frame3::IDENTITY, - major_radius: 3.0, - minor_radius: 0.5, - }; - let brep = arc_wire( - "ellipse".into(), - curve.clone(), - 0.0, - std::f64::consts::TAU, - accuracy(), - ) - .unwrap(); - let mesh = crate::analytic::tessellation::tessellate(&brep, 0.01, 100_000).unwrap(); - let n = mesh.outline_edge_ids.len(); - for (i, pair) in mesh.outline_positions.chunks_exact(6).enumerate() { - let midpoint = curve - .elementary_point(std::f64::consts::TAU * (i as f64 + 0.5) / n as f64) - .unwrap(); - let chord = std::array::from_fn(|j| (pair[j] + pair[j + 3]) / 2.0); - assert!(norm(sub(midpoint, chord)) < 0.01); - } - assert!(arc_wire("bad".into(), curve.clone(), 0.0, 0.0, accuracy()).is_err()); - assert!(arc_wire("bad".into(), curve, 0.0, 7.0, accuracy()).is_err()); - } -} diff --git a/main/opengeometry/src/analytic/query.rs b/main/opengeometry/src/analytic/query.rs deleted file mode 100644 index 4bf0e2d..0000000 --- a/main/opengeometry/src/analytic/query.rs +++ /dev/null @@ -1,591 +0,0 @@ -use super::{ - geometry::{dot, norm, scale, sub, unit, Surface}, - topology::{BrepEnvelope, Face, PcurveGeometry}, - GeometryError, Point3, SurfaceGeometry, UV, -}; -use crate::math::{ - interval::Interval, - roots::{isolate_candidates, quadratic, QuadraticRoots}, -}; - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum PointClassification { - Inside, - Outside, - Boundary, - Unknown, -} - -fn lifted_uv(brep: &BrepEnvelope, halfedge: u32, parameter: f64) -> Result { - let use_ = &brep.topology.halfedges[halfedge as usize]; - let face = &brep.topology.faces[use_ - .face - .ok_or_else(|| GeometryError::InvalidTopology("trim query halfedge has no face".into()))? - as usize]; - let pcurve = use_.geometry_use.pcurve.ok_or_else(|| { - GeometryError::InvalidTopology("trim query halfedge has no pcurve".into()) - })?; - let mut uv = brep.geometry.pcurve_at(pcurve, parameter)?; - let periods = brep.geometry.surface(face.surface)?.charts()[face.trim.chart as usize].periods; - for axis in 0..2 { - uv[axis] += periods[axis].map_or(0.0, |period| { - period * f64::from(use_.geometry_use.periodic_lift[axis]) - }); - } - Ok(uv) -} - -fn trim_samples(brep: &BrepEnvelope, face: &Face, loop_id: u32) -> Result, GeometryError> { - let loop_ = &brep.topology.loops[loop_id as usize]; - let start = loop_.start_halfedge; - let mut current = start; - let mut points = Vec::new(); - for _ in 0..=brep.topology.halfedges.len() { - let use_ = &brep.topology.halfedges[current as usize]; - if use_.face != Some(face.id) { - return Err(GeometryError::InvalidTopology( - "trim query crossed into another face".into(), - )); - } - let edge = &brep.topology.edges[use_.edge as usize]; - let range = edge.geometry.range(); - let pcurve = use_.geometry_use.pcurve.ok_or_else(|| { - GeometryError::InvalidTopology("trim query halfedge has no pcurve".into()) - })?; - let subdivisions = match brep.geometry.pcurves[pcurve as usize] { - PcurveGeometry::Line2 { direction, .. } => { - let periods = - brep.geometry.surface(face.surface)?.charts()[face.trim.chart as usize].periods; - if periods.iter().enumerate().any(|(axis, period)| { - period.is_some_and(|period| { - ((direction[axis] * range.width()).abs() - period).abs() - <= 4.0 * brep.accuracy.geometric - }) - }) { - 64 - } else { - 1 - } - } - PcurveGeometry::Conic2 { .. } => 64, - PcurveGeometry::ProjectedCurve { .. } | PcurveGeometry::IntersectionSide { .. } => 128, - }; - for index in 0..subdivisions { - let fraction = if use_.geometry_use.sense == super::topology::Orientation::Forward { - index as f64 / subdivisions as f64 - } else { - 1.0 - index as f64 / subdivisions as f64 - }; - points.push(lifted_uv( - brep, - current, - range.lo + range.width() * fraction, - )?); - } - current = use_ - .next - .ok_or_else(|| GeometryError::InvalidTopology("open trim loop in query".into()))?; - if current == start { - break; - } - } - if current != start || points.len() < 3 { - return Err(GeometryError::InvalidTopology( - "trim query requires a closed nondegenerate loop".into(), - )); - } - Ok(points) -} - -fn segment_distance_2d(point: UV, a: UV, b: UV) -> f64 { - let direction = [b[0] - a[0], b[1] - a[1]]; - let length_squared = dot( - [direction[0], direction[1], 0.0], - [direction[0], direction[1], 0.0], - ); - if length_squared == 0.0 { - return (point[0] - a[0]).hypot(point[1] - a[1]); - } - let parameter = (((point[0] - a[0]) * direction[0] + (point[1] - a[1]) * direction[1]) - / length_squared) - .clamp(0.0, 1.0); - (point[0] - a[0] - parameter * direction[0]).hypot(point[1] - a[1] - parameter * direction[1]) -} - -fn in_loop(point: UV, polygon: &[UV], tolerance: f64) -> Option { - let mut inside = false; - for index in 0..polygon.len() { - let a = polygon[index]; - let b = polygon[(index + 1) % polygon.len()]; - if segment_distance_2d(point, a, b) <= tolerance { - return None; - } - if (a[1] > point[1]) != (b[1] > point[1]) { - let x = a[0] + (point[1] - a[1]) * (b[0] - a[0]) / (b[1] - a[1]); - if x > point[0] { - inside = !inside; - } - } - } - Some(inside) -} - -fn lift_to_face(surface: &SurfaceGeometry, face: &Face, mut uv: UV) -> UV { - let periods = surface.charts()[face.trim.chart as usize].periods; - for axis in 0..2 { - if let Some(period) = periods[axis] { - let center = face.trim.uv_bounds[axis].midpoint(); - uv[axis] += ((center - uv[axis]) / period).round() * period; - } - } - uv -} - -fn periodic_cylinder_band_limit( - brep: &BrepEnvelope, - face: &Face, - loop_id: u32, -) -> Result, GeometryError> { - let loop_ = &brep.topology.loops[loop_id as usize]; - let use_ = &brep.topology.halfedges[loop_.start_halfedge as usize]; - if use_.next != Some(loop_.start_halfedge) || use_.from != use_.to { - return Ok(None); - } - let edge = &brep.topology.edges[use_.edge as usize]; - let range = edge.geometry.range(); - let Some(pcurve) = use_.geometry_use.pcurve else { - return Ok(None); - }; - let PcurveGeometry::Line2 { direction, .. } = brep.geometry.pcurves[pcurve as usize] else { - return Ok(None); - }; - let period = brep.geometry.surface(face.surface)?.charts()[face.trim.chart as usize].periods[0]; - let Some(period) = period else { - return Ok(None); - }; - if direction[1].abs() * range.width() > brep.accuracy.geometric - || ((direction[0] * range.width()).abs() - period).abs() > 4.0 * brep.accuracy.geometric - { - return Ok(None); - } - Ok(Some(lifted_uv(brep, loop_.start_halfedge, range.lo)?[1])) -} - -pub(crate) fn face_contains_uv( - brep: &BrepEnvelope, - face: &Face, - uv: UV, -) -> Result, GeometryError> { - let surface = brep.geometry.surface(face.surface)?; - let uv = lift_to_face(surface, face, uv); - let jet = surface.derivatives(uv)?; - let metric = norm(jet.du).min(norm(jet.dv)); - let uv_tolerance = if metric > 0.0 { - brep.accuracy.geometric / metric - } else { - return Ok(None); - }; - if !face.trim.uv_bounds[0].contains(uv[0]) || !face.trim.uv_bounds[1].contains(uv[1]) { - return Ok(Some(false)); - } - if matches!(surface, SurfaceGeometry::Cylinder { .. }) && face.trim.holes.len() == 1 { - if let (Some(outer), Some(inner)) = ( - periodic_cylinder_band_limit(brep, face, face.trim.outer)?, - periodic_cylinder_band_limit(brep, face, face.trim.holes[0])?, - ) { - let lo = outer.min(inner); - let hi = outer.max(inner); - if (uv[1] - lo).abs() <= uv_tolerance || (uv[1] - hi).abs() <= uv_tolerance { - return Ok(None); - } - return Ok(Some(uv[1] > lo && uv[1] < hi)); - } - } - let outer = trim_samples(brep, face, face.trim.outer)?; - let Some(mut inside) = in_loop(uv, &outer, uv_tolerance) else { - return Ok(None); - }; - if !inside { - return Ok(Some(false)); - } - for hole in &face.trim.holes { - let polygon = trim_samples(brep, face, *hole)?; - match in_loop(uv, &polygon, uv_tolerance) { - None => return Ok(None), - Some(true) => inside = false, - Some(false) => {} - } - } - Ok(Some(inside)) -} - -fn polynomial_value(coefficients: &[f64], value: f64) -> f64 { - coefficients - .iter() - .rev() - .fold(0.0, |sum, coefficient| sum.mul_add(value, *coefficient)) -} - -fn refine_polynomial_root(coefficients: &[f64], interval: Interval) -> f64 { - let mut t = interval.midpoint(); - let derivative = coefficients - .iter() - .enumerate() - .skip(1) - .map(|(degree, coefficient)| *coefficient * degree as f64) - .collect::>(); - for _ in 0..12 { - let value = polynomial_value(coefficients, t); - let slope = polynomial_value(&derivative, t); - if slope == 0.0 || !slope.is_finite() { - break; - } - let candidate = t - value / slope; - if !candidate.is_finite() || candidate < interval.lo || candidate > interval.hi { - break; - } - t = candidate; - } - t -} - -fn support_roots( - surface: &SurfaceGeometry, - origin: Point3, - direction: Point3, - domain: Interval, - tolerance: f64, -) -> Result>, GeometryError> { - let o = surface.frame().local(origin); - let d = [ - dot(direction, surface.frame().x), - dot(direction, surface.frame().y), - dot(direction, surface.frame().z), - ]; - let quadratic_roots = |a, b, c| -> Result>, GeometryError> { - Ok(Some(match quadratic(a, b, c)? { - QuadraticRoots::Empty => Vec::new(), - QuadraticRoots::One(root) => vec![root], - QuadraticRoots::Two(roots) => roots.to_vec(), - QuadraticRoots::IdenticallyZero => return Ok(None), - })) - }; - let result = match surface { - SurfaceGeometry::Plane { .. } => { - if d[2] == 0.0 { - if o[2].abs() <= tolerance { - None - } else { - Some(Vec::new()) - } - } else { - Some(vec![-o[2] / d[2]]) - } - } - SurfaceGeometry::Sphere { radius, .. } => { - quadratic_roots(dot(d, d), 2.0 * dot(o, d), dot(o, o) - radius * radius)? - } - SurfaceGeometry::Cylinder { radius, .. } => quadratic_roots( - d[0].mul_add(d[0], d[1] * d[1]), - 2.0 * o[0].mul_add(d[0], o[1] * d[1]), - o[0].mul_add(o[0], o[1] * o[1]) - radius * radius, - )?, - SurfaceGeometry::Cone { semi_angle, .. } => { - let k2 = semi_angle.tan().powi(2); - quadratic_roots( - d[0].mul_add(d[0], d[1] * d[1]) - k2 * d[2] * d[2], - 2.0 * (o[0].mul_add(d[0], o[1] * d[1]) - k2 * o[2] * d[2]), - o[0].mul_add(o[0], o[1] * o[1]) - k2 * o[2] * o[2], - )? - .map(|roots| { - roots - .into_iter() - .filter(|root| o[2] + root * d[2] >= -tolerance) - .collect() - }) - } - SurfaceGeometry::Torus { - major_radius, - minor_radius, - .. - } => { - let radial = [ - o[0].mul_add(o[0], o[1] * o[1]), - 2.0 * o[0].mul_add(d[0], o[1] * d[1]), - d[0].mul_add(d[0], d[1] * d[1]), - ]; - let sum = [ - dot(o, o) + major_radius * major_radius - minor_radius * minor_radius, - 2.0 * dot(o, d), - dot(d, d), - ]; - let r4 = 4.0 * major_radius * major_radius; - let coefficients = [ - sum[0] * sum[0] - r4 * radial[0], - 2.0 * sum[0] * sum[1] - r4 * radial[1], - sum[1] * sum[1] + 2.0 * sum[0] * sum[2] - r4 * radial[2], - 2.0 * sum[1] * sum[2], - sum[2] * sum[2], - ]; - let candidates = isolate_candidates(&coefficients, domain, tolerance, 100_000) - .map_err(|error| { - GeometryError::UnresolvedIntersection(format!( - "torus ray root isolation: {error:?}" - )) - })?; - Some( - candidates - .intervals - .into_iter() - .map(|candidate| refine_polynomial_root(&coefficients, candidate)) - .collect(), - ) - } - }; - Ok(result.map(|roots| { - roots - .into_iter() - .filter(|root| domain.contains(*root)) - .collect() - })) -} - -fn ray_box_domain( - brep: &BrepEnvelope, - origin: Point3, - direction: Point3, -) -> Result, GeometryError> { - let Some(bounds) = brep.bounds_unchecked()? else { - return Ok(None); - }; - let mut lo: f64 = 0.0; - let mut hi = f64::MAX; - for axis in 0..3 { - if direction[axis] == 0.0 { - if !bounds.axes[axis].contains(origin[axis]) { - return Ok(None); - } - continue; - } - let mut a = (bounds.axes[axis].lo - origin[axis]) / direction[axis]; - let mut b = (bounds.axes[axis].hi - origin[axis]) / direction[axis]; - if a > b { - std::mem::swap(&mut a, &mut b); - } - lo = lo.max(a); - hi = hi.min(b); - } - if hi < lo.max(0.0) { - Ok(None) - } else { - Ok(Some(Interval::new(lo.max(0.0), hi)?)) - } -} - -fn classify_with_ray<'a>( - brep: &BrepEnvelope, - point: Point3, - direction: Point3, - faces: impl Iterator, -) -> Result { - let Some(domain) = ray_box_domain(brep, point, direction)? else { - return Ok(PointClassification::Outside); - }; - let mut hits: Vec<(f64, Point3)> = Vec::new(); - for face in faces { - let surface = brep.geometry.surface(face.surface)?; - let Some(roots) = support_roots( - surface, - point, - direction, - domain, - brep.accuracy.intersection, - )? - else { - return Ok(PointClassification::Unknown); - }; - for parameter in roots { - let position = [ - point[0] + parameter * direction[0], - point[1] + parameter * direction[1], - point[2] + parameter * direction[2], - ]; - let uv = match surface.project(position, None) { - Ok(uv) => uv, - Err(super::GeometryError::SingularParameterization) => { - return Ok(PointClassification::Unknown) - } - Err(error) => return Err(error), - }; - if norm(sub(surface.point_at(uv)?, position)) > brep.accuracy.intersection { - continue; - } - match face_contains_uv(brep, face, uv)? { - Some(true) => {} - Some(false) => continue, - None => return Ok(PointClassification::Unknown), - } - let normal = scale(surface.normal_at(uv)?, face.sense.multiplier()); - if dot(normal, direction).abs() <= 64.0 * f64::EPSILON { - return Ok(PointClassification::Unknown); - } - if parameter <= brep.accuracy.geometric { - return Ok(PointClassification::Boundary); - } - if hits - .iter() - .all(|(existing, _)| (existing - parameter).abs() > brep.accuracy.geometric) - { - hits.push((parameter, position)); - } - } - } - Ok(if hits.len() % 2 == 1 { - PointClassification::Inside - } else { - PointClassification::Outside - }) -} - -pub fn classify_point( - brep: &BrepEnvelope, - point: Point3, -) -> Result { - brep.validate()?; - classify_point_validated(brep, point) -} - -pub(crate) fn classify_point_validated( - brep: &BrepEnvelope, - point: Point3, -) -> Result { - if point.into_iter().any(|value| !value.is_finite()) { - return Err(GeometryError::InvalidGeometry( - "point classification requires finite coordinates".into(), - )); - } - let directions = [ - unit([1.0, 0.371, 0.127])?, - unit([0.193, 1.0, 0.419])?, - unit([0.311, 0.233, 1.0])?, - ]; - for direction in directions { - let result = classify_with_ray(brep, point, direction, brep.topology.faces.iter())?; - if result != PointClassification::Unknown { - return Ok(result); - } - } - Ok(PointClassification::Unknown) -} - -pub(crate) fn classify_point_in_shell( - brep: &BrepEnvelope, - faces: &[u32], - point: Point3, -) -> Result { - for direction in [ - unit([1.0, 0.371, 0.127])?, - unit([0.193, 1.0, 0.419])?, - unit([0.311, 0.233, 1.0])?, - ] { - let result = classify_with_ray( - brep, - point, - direction, - faces - .iter() - .map(|face| &brep.topology.faces[*face as usize]), - )?; - if result != PointClassification::Unknown { - return Ok(result); - } - } - Ok(PointClassification::Unknown) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::{ - booleans::{boolean_brep, BooleanOp}, - primitives, - topology::Accuracy, - Frame3, - }; - - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-5, - } - } - - #[test] - fn analytic_ray_classification_covers_all_surface_families() { - let bodies = [ - ( - primitives::cuboid("box".into(), Frame3::IDENTITY, [2.0; 3], accuracy()).unwrap(), - [1.0, 1.0, 1.0], - [3.0, 3.0, 3.0], - ), - ( - primitives::sphere("sphere".into(), Frame3::IDENTITY, 1.0, accuracy()).unwrap(), - [0.0; 3], - [2.0, 0.0, 0.0], - ), - ( - primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy()) - .unwrap(), - [0.0, 0.0, 1.0], - [2.0, 0.0, 1.0], - ), - ( - primitives::cone("cone".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy()).unwrap(), - [0.0, 0.0, 1.0], - [2.0, 0.0, 1.0], - ), - ( - primitives::torus("torus".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy()).unwrap(), - [3.0, 0.0, 0.0], - [0.0; 3], - ), - ]; - for (body, inside, outside) in bodies { - assert_eq!( - classify_point(&body, inside).unwrap(), - PointClassification::Inside, - "{}", - body.id - ); - assert_eq!( - classify_point(&body, outside).unwrap(), - PointClassification::Outside, - "{}", - body.id - ); - } - } - - #[test] - fn cavity_shell_uses_regularized_material_occupancy() { - let host = - primitives::torus("host".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy()).unwrap(); - let cutter = - primitives::torus("cutter".into(), Frame3::IDENTITY, 3.0, 0.4, accuracy()).unwrap(); - let result = boolean_brep(&host, &cutter, BooleanOp::Subtraction, "shell".into()).unwrap(); - assert_eq!( - classify_point(&result.brep, [3.0, 0.0, 0.0]).unwrap(), - PointClassification::Outside - ); - assert_eq!( - classify_point(&result.brep, [3.7, 0.0, 0.0]).unwrap(), - PointClassification::Inside - ); - assert_eq!( - classify_point(&result.brep, [0.0; 3]).unwrap(), - PointClassification::Outside - ); - } -} diff --git a/main/opengeometry/src/analytic/ssi.rs b/main/opengeometry/src/analytic/ssi.rs deleted file mode 100644 index 3390ef5..0000000 --- a/main/opengeometry/src/analytic/ssi.rs +++ /dev/null @@ -1,1856 +0,0 @@ -use super::{ - geometry::{add, cross, dot, norm, scale, sub, unit, PatchBounds, Surface}, - topology::{Accuracy, GeometryStore, PcurveGeometry}, - CurveGeometry, Frame3, GeometryError, Point3, SurfaceGeometry, -}; -use crate::math::{ - interval::Interval, - predicates::{plane_sphere_relation, sphere_sphere_relation, Sign}, - roots::{quadratic, QuadraticRoots}, - solve::solve, -}; - -#[derive(Clone, Debug)] -pub struct SsiCurve { - pub curve: u32, - pub pcurves: [u32; 2], - pub domain: Option, -} -#[derive(Clone, Debug, Default)] -pub struct SsiResult { - pub curves: Vec, - pub contacts: Vec, - pub coincident: bool, -} - -enum GeometryResult { - Empty, - Curves(Vec<(CurveGeometry, Option)>), - CurvesAndContacts { - curves: Vec<(CurveGeometry, Option)>, - contacts: Vec, - }, - Contact(Point3), - Coincident, -} - -fn circle(center: Point3, normal: Point3, radius: f64) -> Result { - if !radius.is_finite() || radius <= 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "positive intersection radius is not representable".into(), - )); - } - let reference = if normal[0].abs() < 0.8 { - [1.0, 0.0, 0.0] - } else { - [0.0, 1.0, 0.0] - }; - let frame = Frame3::from_axis(center, normal, reference)?; - Ok(GeometryResult::Curves(vec![( - CurveGeometry::Circle { frame, radius }, - Some(Interval::new(0.0, std::f64::consts::TAU)?), - )])) -} - -fn plane_plane(a: Frame3, b: Frame3, accuracy: Accuracy) -> Result { - let tangent = cross(a.z, b.z); - let length = norm(tangent); - let delta = sub(b.origin, a.origin); - if length == 0.0 { - return Ok(if dot(a.z, delta).abs() <= accuracy.intersection { - GeometryResult::Coincident - } else { - GeometryResult::Empty - }); - } - let tangent = unit(tangent)?; - let solution = solve([a.z, b.z, tangent], [0.0, dot(b.z, delta), 0.0]) - .map_err(|e| GeometryError::UnresolvedIntersection(format!("plane intersection: {e}")))?; - Ok(GeometryResult::Curves(vec![( - CurveGeometry::Line { - origin: add(a.origin, solution.value), - direction: tangent, - }, - None, - )])) -} - -fn plane_sphere( - plane: Frame3, - sphere: Frame3, - radius: f64, -) -> Result { - let relation = plane_sphere_relation(plane.origin, plane.z, sphere.origin, radius)?; - if relation == Sign::Positive { - return Ok(GeometryResult::Empty); - } - let n2 = dot(plane.z, plane.z); - let distance = dot(sub(sphere.origin, plane.origin), plane.z); - let center = sub(sphere.origin, scale(plane.z, distance / n2)); - if relation == Sign::Zero { - return Ok(GeometryResult::Contact(center)); - } - let d = distance.abs() / n2.sqrt(); - let r2 = (radius - d) * (radius + d); - if r2 <= 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "plane/sphere radius cancellation".into(), - )); - } - circle(center, plane.z, r2.sqrt()) -} - -fn sphere_sphere(a: Frame3, ra: f64, b: Frame3, rb: f64) -> Result { - let delta = sub(b.origin, a.origin); - let distance = norm(delta); - if distance == 0.0 { - return Ok(if ra == rb { - GeometryResult::Coincident - } else { - GeometryResult::Empty - }); - } - let [outer, inner] = sphere_sphere_relation(a.origin, ra, b.origin, rb)?; - if outer == Sign::Positive || inner == Sign::Negative { - return Ok(GeometryResult::Empty); - } - let axis = unit(delta)?; - let x = 0.5 * (distance + (ra - rb) * (ra + rb) / distance); - let center = add(a.origin, scale(axis, x)); - if outer == Sign::Zero || inner == Sign::Zero { - return Ok(GeometryResult::Contact(center)); - } - let r2 = (ra - x.abs()) * (ra + x.abs()); - if r2 <= 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "sphere/sphere radius cancellation".into(), - )); - } - circle(center, axis, r2.sqrt()) -} - -fn plane_cylinder( - plane: Frame3, - cylinder: Frame3, - radius: f64, -) -> Result { - let axial = dot(plane.z, cylinder.z); - let offset = dot(sub(cylinder.origin, plane.origin), plane.z); - if axial != 0.0 { - let center = add(cylinder.origin, scale(cylinder.z, -offset / axial)); - if axial.abs() == 1.0 { - return circle(center, plane.z, radius); - } - let major_direction = unit(sub(cylinder.z, scale(plane.z, axial)))?; - let major_radius = radius / axial.abs(); - if !major_radius.is_finite() { - return Err(GeometryError::UnresolvedIntersection( - "plane/cylinder ellipse exceeds numerical range".into(), - )); - } - let frame = Frame3::from_axis(center, plane.z, major_direction)?; - return Ok(GeometryResult::Curves(vec![( - CurveGeometry::Ellipse { - frame, - major_radius, - minor_radius: radius, - }, - Some(Interval::new(0.0, std::f64::consts::TAU)?), - )])); - } - - let relation = plane_sphere_relation(plane.origin, plane.z, cylinder.origin, radius)?; - if relation == Sign::Positive { - return Ok(GeometryResult::Empty); - } - let base = add(cylinder.origin, scale(plane.z, -offset)); - if relation == Sign::Zero { - return Ok(GeometryResult::Curves(vec![( - CurveGeometry::Line { - origin: base, - direction: cylinder.z, - }, - None, - )])); - } - let lateral = unit(cross(cylinder.z, plane.z))?; - let half_separation_squared = (radius - offset.abs()) * (radius + offset.abs()); - if half_separation_squared <= 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "plane/cylinder generator separation cancellation".into(), - )); - } - let half_separation = half_separation_squared.sqrt(); - Ok(GeometryResult::Curves( - [-1.0, 1.0] - .into_iter() - .map(|sense| { - ( - CurveGeometry::Line { - origin: add(base, scale(lateral, sense * half_separation)), - direction: cylinder.z, - }, - None, - ) - }) - .collect(), - )) -} - -fn plane_cone( - plane: Frame3, - cone: Frame3, - semi_angle: f64, -) -> Result { - let axial = dot(plane.z, cone.z); - let tilt = norm(cross(plane.z, cone.z)); - if tilt == 0.0 { - let height = dot(sub(plane.origin, cone.origin), cone.z); - if height < 0.0 { - return Ok(GeometryResult::Empty); - } - if height == 0.0 { - return Ok(GeometryResult::Contact(cone.origin)); - } - let radius = height * semi_angle.tan(); - if !radius.is_finite() || radius <= 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "plane/cone circle exceeds numerical range".into(), - )); - } - return circle(add(cone.origin, scale(cone.z, height)), plane.z, radius); - } - if axial == 0.0 { - return Err(GeometryError::CoverageGap { - families: ["plane".into(), "cone".into()], - }); - } - let height = dot(sub(plane.origin, cone.origin), plane.z) / axial; - let slope = semi_angle.tan(); - let coefficient = axial * axial - slope * slope * tilt * tilt; - if coefficient <= 0.0 { - return Err(GeometryError::CoverageGap { - families: ["plane".into(), "cone".into()], - }); - } - if height < 0.0 { - return Ok(GeometryResult::Empty); - } - if height == 0.0 { - return Ok(GeometryResult::Contact(cone.origin)); - } - let cross_axis = unit(cross(plane.z, cone.z))?; - let down_slope = unit(cross(plane.z, cross_axis))?; - let center_shift = -slope * slope * height * tilt / coefficient; - let center = add( - add(cone.origin, scale(cone.z, height)), - scale(down_slope, center_shift), - ); - let minor_radius = slope * height.abs() * axial.abs() / coefficient.sqrt(); - let major_radius = minor_radius / coefficient.sqrt(); - if !major_radius.is_finite() || !minor_radius.is_finite() || minor_radius <= 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "plane/cone ellipse exceeds numerical range".into(), - )); - } - let frame = Frame3::from_axis(center, plane.z, down_slope)?; - Ok(GeometryResult::Curves(vec![( - CurveGeometry::Ellipse { - frame, - major_radius, - minor_radius, - }, - Some(Interval::new(0.0, std::f64::consts::TAU)?), - )])) -} - -fn parallel_cylinders( - a: Frame3, - ra: f64, - b: Frame3, - rb: f64, -) -> Result { - if norm(cross(a.z, b.z)) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "cylinder".into()], - }); - } - let delta = sub(b.origin, a.origin); - let radial = sub(delta, scale(a.z, dot(delta, a.z))); - let distance = norm(radial); - if distance == 0.0 { - return Ok(if ra == rb { - GeometryResult::Coincident - } else { - GeometryResult::Empty - }); - } - let projected_center = add(a.origin, radial); - let [outer, inner] = sphere_sphere_relation(a.origin, ra, projected_center, rb)?; - if outer == Sign::Positive || inner == Sign::Negative { - return Ok(GeometryResult::Empty); - } - let toward_b = unit(radial)?; - let along = 0.5 * (distance + (ra - rb) * (ra + rb) / distance); - let base = add(a.origin, scale(toward_b, along)); - if outer == Sign::Zero || inner == Sign::Zero { - return Ok(GeometryResult::Curves(vec![( - CurveGeometry::Line { - origin: base, - direction: a.z, - }, - None, - )])); - } - let half_separation_squared = (ra - along.abs()) * (ra + along.abs()); - if half_separation_squared <= 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "parallel-cylinder branch separation cancellation".into(), - )); - } - let lateral = unit(cross(a.z, toward_b))?; - let half_separation = half_separation_squared.sqrt(); - Ok(GeometryResult::Curves( - [-1.0, 1.0] - .into_iter() - .map(|sense| { - ( - CurveGeometry::Line { - origin: add(base, scale(lateral, sense * half_separation)), - direction: a.z, - }, - None, - ) - }) - .collect(), - )) -} - -fn coaxial_cylinder_cone( - cylinder: Frame3, - radius: f64, - cone: Frame3, - semi_angle: f64, -) -> Result { - if norm(cross(cylinder.z, cone.z)) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "cone".into()], - }); - } - let delta = sub(cylinder.origin, cone.origin); - if norm(sub(delta, scale(cone.z, dot(delta, cone.z)))) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "cone".into()], - }); - } - let height = radius / semi_angle.tan(); - if !height.is_finite() || height <= 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "coaxial cylinder/cone section exceeds numerical range".into(), - )); - } - circle(add(cone.origin, scale(cone.z, height)), cone.z, radius) -} - -fn coaxial_sphere_cylinder( - sphere: Frame3, - sphere_radius: f64, - cylinder: Frame3, - cylinder_radius: f64, -) -> Result { - let delta = sub(sphere.origin, cylinder.origin); - if norm(sub(delta, scale(cylinder.z, dot(delta, cylinder.z)))) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["sphere".into(), "cylinder".into()], - }); - } - if cylinder_radius > sphere_radius { - return Ok(GeometryResult::Empty); - } - if cylinder_radius == sphere_radius { - return circle(sphere.origin, cylinder.z, cylinder_radius); - } - let half_height_squared = (sphere_radius - cylinder_radius) * (sphere_radius + cylinder_radius); - if half_height_squared <= 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "sphere/cylinder branch separation cancellation".into(), - )); - } - let half_height = half_height_squared.sqrt(); - let domain = Some(Interval::new(0.0, std::f64::consts::TAU)?); - let mut curves = Vec::with_capacity(2); - for sense in [-1.0, 1.0] { - let center = add(sphere.origin, scale(cylinder.z, sense * half_height)); - let GeometryResult::Curves(mut circle) = circle(center, cylinder.z, cylinder_radius)? - else { - return Err(GeometryError::UnresolvedIntersection( - "sphere/cylinder circle construction returned a non-curve".into(), - )); - }; - let mut curve = circle.pop().ok_or_else(|| { - GeometryError::UnresolvedIntersection("empty sphere/cylinder circle".into()) - })?; - curve.1 = domain; - curves.push(curve); - } - Ok(GeometryResult::Curves(curves)) -} - -fn coaxial_sphere_cone( - sphere: Frame3, - sphere_radius: f64, - cone: Frame3, - semi_angle: f64, -) -> Result { - let delta = sub(sphere.origin, cone.origin); - let center_height = dot(delta, cone.z); - if norm(sub(delta, scale(cone.z, center_height))) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["sphere".into(), "cone".into()], - }); - } - let slope = semi_angle.tan(); - let roots = quadratic( - 1.0 + slope * slope, - -2.0 * center_height, - center_height * center_height - sphere_radius * sphere_radius, - )?; - let roots: Vec = match roots { - QuadraticRoots::Empty => Vec::new(), - QuadraticRoots::One(root) => vec![root], - QuadraticRoots::Two(roots) => roots.into(), - QuadraticRoots::IdenticallyZero => { - return Err(GeometryError::UnresolvedIntersection( - "sphere/cone equation is unexpectedly indeterminate".into(), - )) - } - }; - let apex_contact = roots.contains(&0.0); - let circles = axial_circles( - cone.z, - roots - .into_iter() - .filter(|height| *height > 0.0) - .map(|height| (add(cone.origin, scale(cone.z, height)), slope * height)), - )?; - if !apex_contact { - return Ok(circles); - } - Ok(match circles { - GeometryResult::Empty => GeometryResult::Contact(cone.origin), - GeometryResult::Curves(curves) => GeometryResult::CurvesAndContacts { - curves, - contacts: vec![cone.origin], - }, - _ => { - return Err(GeometryError::UnresolvedIntersection( - "sphere/cone apex classification produced an inconsistent result".into(), - )) - } - }) -} - -fn coaxial_cones( - a: Frame3, - angle_a: f64, - b: Frame3, - angle_b: f64, -) -> Result { - if norm(cross(a.z, b.z)) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["cone".into(), "cone".into()], - }); - } - let delta = sub(b.origin, a.origin); - let axial_offset = dot(delta, a.z); - if norm(sub(delta, scale(a.z, axial_offset))) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["cone".into(), "cone".into()], - }); - } - let same_direction = dot(a.z, b.z) > 0.0; - if axial_offset == 0.0 { - return Ok(if same_direction && angle_a == angle_b { - GeometryResult::Coincident - } else { - GeometryResult::Contact(a.origin) - }); - } - let ka = angle_a.tan(); - let kb = angle_b.tan(); - let height_a = if same_direction { - if ka == kb { - return Ok(GeometryResult::Empty); - } - kb * axial_offset / (kb - ka) - } else { - kb * axial_offset / (ka + kb) - }; - let height_b = if same_direction { - height_a - axial_offset - } else { - axial_offset - height_a - }; - if !height_a.is_finite() || !height_b.is_finite() { - return Err(GeometryError::UnresolvedIntersection( - "coaxial cone section exceeds numerical range".into(), - )); - } - if height_a < 0.0 || height_b < 0.0 { - return Ok(GeometryResult::Empty); - } - if height_a == 0.0 || height_b == 0.0 { - return Ok(GeometryResult::Contact(add(a.origin, scale(a.z, height_a)))); - } - circle(add(a.origin, scale(a.z, height_a)), a.z, ka * height_a) -} - -fn axial_circles( - normal: Point3, - sections: impl IntoIterator, -) -> Result { - let mut curves = Vec::new(); - for (center, radius) in sections { - let GeometryResult::Curves(mut section) = circle(center, normal, radius)? else { - return Err(GeometryError::UnresolvedIntersection( - "axial circle construction returned a non-curve".into(), - )); - }; - curves.append(&mut section); - } - Ok(if curves.is_empty() { - GeometryResult::Empty - } else { - GeometryResult::Curves(curves) - }) -} - -fn trig_roots(a: f64, b: f64, c: f64) -> Result, GeometryError> { - let amplitude = a.hypot(b); - if !amplitude.is_finite() || amplitude == 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "degenerate trigonometric intersection equation".into(), - )); - } - let ratio = -c / amplitude; - if !ratio.is_finite() { - return Err(GeometryError::UnresolvedIntersection( - "trigonometric intersection equation exceeds numerical range".into(), - )); - } - if ratio.abs() > 1.0 { - return Ok(Vec::new()); - } - let phase = b.atan2(a); - let delta = ratio.acos(); - Ok(if delta == 0.0 || delta == std::f64::consts::PI { - vec![phase + delta] - } else { - vec![phase - delta, phase + delta] - }) -} - -fn plane_torus( - plane: Frame3, - torus: Frame3, - major_radius: f64, - minor_radius: f64, -) -> Result { - let parallel = norm(cross(plane.z, torus.z)) == 0.0; - if parallel { - let height = dot(sub(plane.origin, torus.origin), torus.z); - if height.abs() > minor_radius { - return Ok(GeometryResult::Empty); - } - let center = add(torus.origin, scale(torus.z, height)); - if height.abs() == minor_radius { - return axial_circles(plane.z, [(center, major_radius)]); - } - let radial = ((minor_radius - height.abs()) * (minor_radius + height.abs())).sqrt(); - return axial_circles( - plane.z, - [ - (center, major_radius - radial), - (center, major_radius + radial), - ], - ); - } - if dot(plane.z, torus.z) == 0.0 && dot(sub(torus.origin, plane.origin), plane.z) == 0.0 { - let radial = unit(cross(plane.z, torus.z))?; - return axial_circles( - plane.z, - [ - (add(torus.origin, scale(radial, major_radius)), minor_radius), - ( - add(torus.origin, scale(radial, -major_radius)), - minor_radius, - ), - ], - ); - } - Err(GeometryError::CoverageGap { - families: ["plane".into(), "torus".into()], - }) -} - -fn coaxial_sphere_torus( - sphere: Frame3, - sphere_radius: f64, - torus: Frame3, - major_radius: f64, - minor_radius: f64, -) -> Result { - let local = torus.local(sphere.origin); - if local[0] != 0.0 || local[1] != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["sphere".into(), "torus".into()], - }); - } - let roots = trig_roots( - 2.0 * major_radius * minor_radius, - -2.0 * local[2] * minor_radius, - major_radius * major_radius + minor_radius * minor_radius + local[2] * local[2] - - sphere_radius * sphere_radius, - )?; - axial_circles( - torus.z, - roots.into_iter().map(|v| { - let center = add(torus.origin, scale(torus.z, minor_radius * v.sin())); - (center, major_radius + minor_radius * v.cos()) - }), - ) -} - -fn coaxial_cylinder_torus( - cylinder: Frame3, - cylinder_radius: f64, - torus: Frame3, - major_radius: f64, - minor_radius: f64, -) -> Result { - if norm(cross(cylinder.z, torus.z)) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "torus".into()], - }); - } - let delta = sub(cylinder.origin, torus.origin); - if norm(sub(delta, scale(torus.z, dot(delta, torus.z)))) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["cylinder".into(), "torus".into()], - }); - } - let roots = trig_roots(minor_radius, 0.0, major_radius - cylinder_radius)?; - axial_circles( - torus.z, - roots.into_iter().map(|v| { - ( - add(torus.origin, scale(torus.z, minor_radius * v.sin())), - cylinder_radius, - ) - }), - ) -} - -fn coaxial_cone_torus( - cone: Frame3, - semi_angle: f64, - torus: Frame3, - major_radius: f64, - minor_radius: f64, -) -> Result { - if norm(cross(cone.z, torus.z)) != 0.0 || dot(cone.z, torus.z) < 0.0 { - return Err(GeometryError::CoverageGap { - families: ["cone".into(), "torus".into()], - }); - } - let delta = sub(torus.origin, cone.origin); - let height = dot(delta, cone.z); - if norm(sub(delta, scale(cone.z, height))) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["cone".into(), "torus".into()], - }); - } - let slope = semi_angle.tan(); - let roots = trig_roots( - minor_radius, - -slope * minor_radius, - major_radius - slope * height, - )?; - axial_circles( - torus.z, - roots.into_iter().filter_map(|v| { - let z = height + minor_radius * v.sin(); - (z >= 0.0).then(|| { - ( - add(torus.origin, scale(torus.z, minor_radius * v.sin())), - major_radius + minor_radius * v.cos(), - ) - }) - }), - ) -} - -fn coaxial_tori( - a: Frame3, - major_a: f64, - minor_a: f64, - b: Frame3, - major_b: f64, - minor_b: f64, -) -> Result { - if norm(cross(a.z, b.z)) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["torus".into(), "torus".into()], - }); - } - let delta = sub(b.origin, a.origin); - let axial_offset = dot(delta, a.z); - if norm(sub(delta, scale(a.z, axial_offset))) != 0.0 { - return Err(GeometryError::CoverageGap { - families: ["torus".into(), "torus".into()], - }); - } - if axial_offset == 0.0 && major_a == major_b && minor_a == minor_b { - return Ok(GeometryResult::Coincident); - } - let radial_offset = major_b - major_a; - let distance = radial_offset.hypot(axial_offset); - if distance == 0.0 { - return Ok(GeometryResult::Empty); - } - let [outer, inner] = sphere_sphere_relation( - [major_a, 0.0, 0.0], - minor_a, - [major_b, 0.0, axial_offset], - minor_b, - )?; - if outer == Sign::Positive || inner == Sign::Negative { - return Ok(GeometryResult::Empty); - } - let er = radial_offset / distance; - let ez = axial_offset / distance; - let along = 0.5 * (distance + (minor_a - minor_b) * (minor_a + minor_b) / distance); - let base_r = major_a + er * along; - let base_z = ez * along; - let sections = if outer == Sign::Zero || inner == Sign::Zero { - vec![(base_r, base_z)] - } else { - let half_squared = (minor_a - along.abs()) * (minor_a + along.abs()); - if half_squared <= 0.0 { - return Err(GeometryError::UnresolvedIntersection( - "coaxial torus branch separation cancellation".into(), - )); - } - let half = half_squared.sqrt(); - vec![ - (base_r - ez * half, base_z + er * half), - (base_r + ez * half, base_z - er * half), - ] - }; - axial_circles( - a.z, - sections - .into_iter() - .map(|(radius, z)| (add(a.origin, scale(a.z, z)), radius)), - ) -} - -fn chart_domain(surface: &SurfaceGeometry) -> Result<[Interval; 2], GeometryError> { - let chart = surface - .charts() - .first() - .ok_or_else(|| GeometryError::InvalidGeometry("surface has no parameter chart".into()))?; - Ok([ - Interval::new(chart.domain[0][0], chart.domain[0][1])?, - Interval::new(chart.domain[1][0], chart.domain[1][1])?, - ]) -} - -fn finite_target_domain( - surface: &SurfaceGeometry, - source_bounds: PatchBounds, - margin: f64, -) -> Result<[Interval; 2], GeometryError> { - if matches!( - surface, - SurfaceGeometry::Sphere { .. } | SurfaceGeometry::Torus { .. } - ) { - return chart_domain(surface); - } - let mut local_min = [f64::INFINITY; 3]; - let mut local_max = [f64::NEG_INFINITY; 3]; - let mut maximum_radius = 0.0_f64; - for mask in 0..8 { - let point = std::array::from_fn(|axis| { - if mask & (1 << axis) == 0 { - source_bounds.axes[axis].lo - } else { - source_bounds.axes[axis].hi - } - }); - let local = surface.frame().local(point); - for axis in 0..3 { - local_min[axis] = local_min[axis].min(local[axis]); - local_max[axis] = local_max[axis].max(local[axis]); - } - maximum_radius = maximum_radius.max(norm(local)); - } - let expanded = |lo: f64, hi: f64| Interval::new(lo - margin, hi + margin); - match surface { - SurfaceGeometry::Plane { .. } => Ok([ - expanded(local_min[0], local_max[0])?, - expanded(local_min[1], local_max[1])?, - ]), - SurfaceGeometry::Cylinder { .. } => Ok([ - Interval::new(0.0, std::f64::consts::TAU)?, - expanded(local_min[2], local_max[2])?, - ]), - SurfaceGeometry::Cone { .. } => Ok([ - Interval::new(0.0, std::f64::consts::TAU)?, - Interval::new(0.0, maximum_radius + margin)?, - ]), - SurfaceGeometry::Sphere { .. } | SurfaceGeometry::Torus { .. } => unreachable!(), - } -} - -fn universal_fallback( - store: &mut GeometryStore, - a: u32, - b: u32, - sa: &SurfaceGeometry, - sb: &SurfaceGeometry, - accuracy: Accuracy, - original: GeometryError, -) -> Result { - let source_side = if matches!( - sa, - SurfaceGeometry::Sphere { .. } | SurfaceGeometry::Torus { .. } - ) { - 0 - } else if matches!( - sb, - SurfaceGeometry::Sphere { .. } | SurfaceGeometry::Torus { .. } - ) { - 1 - } else { - return Err(original); - }; - let mut domains = [chart_domain(sa)?, chart_domain(sb)?]; - let source_bounds = store - .surface([a, b][source_side])? - .enclose(domains[source_side])?; - domains[1 - source_side] = finite_target_domain( - store.surface([a, b][1 - source_side])?, - source_bounds, - accuracy.intersection, - )?; - super::universal_ssi::intersect_patches_from( - store, - [a, b], - domains, - source_side, - accuracy, - super::intersection::SsiBudget::default(), - ) -} - -pub fn intersect_surfaces( - store: &mut GeometryStore, - a: u32, - b: u32, - accuracy: Accuracy, -) -> Result { - accuracy.validate()?; - let sa = store.surface(a)?.clone(); - let sb = store.surface(b)?.clone(); - sa.validate()?; - sb.validate()?; - let fast_intersection = (|| -> Result { - Ok(match (&sa, &sb) { - (SurfaceGeometry::Plane { frame: a }, SurfaceGeometry::Plane { frame: b }) => { - plane_plane(*a, *b, accuracy)? - } - (SurfaceGeometry::Plane { frame: p }, SurfaceGeometry::Sphere { frame: s, radius }) - | (SurfaceGeometry::Sphere { frame: s, radius }, SurfaceGeometry::Plane { frame: p }) => { - plane_sphere(*p, *s, *radius)? - } - ( - SurfaceGeometry::Plane { frame: p }, - SurfaceGeometry::Cylinder { frame: c, radius }, - ) - | ( - SurfaceGeometry::Cylinder { frame: c, radius }, - SurfaceGeometry::Plane { frame: p }, - ) => plane_cylinder(*p, *c, *radius)?, - ( - SurfaceGeometry::Plane { frame: p }, - SurfaceGeometry::Cone { - frame: c, - semi_angle, - }, - ) - | ( - SurfaceGeometry::Cone { - frame: c, - semi_angle, - }, - SurfaceGeometry::Plane { frame: p }, - ) => plane_cone(*p, *c, *semi_angle)?, - ( - SurfaceGeometry::Sphere { - frame: a, - radius: ra, - }, - SurfaceGeometry::Sphere { - frame: b, - radius: rb, - }, - ) => sphere_sphere(*a, *ra, *b, *rb)?, - ( - SurfaceGeometry::Sphere { - frame: sphere, - radius: sphere_radius, - }, - SurfaceGeometry::Cylinder { - frame: cylinder, - radius: cylinder_radius, - }, - ) - | ( - SurfaceGeometry::Cylinder { - frame: cylinder, - radius: cylinder_radius, - }, - SurfaceGeometry::Sphere { - frame: sphere, - radius: sphere_radius, - }, - ) => coaxial_sphere_cylinder(*sphere, *sphere_radius, *cylinder, *cylinder_radius)?, - ( - SurfaceGeometry::Sphere { - frame: sphere, - radius: sphere_radius, - }, - SurfaceGeometry::Cone { - frame: cone, - semi_angle, - }, - ) - | ( - SurfaceGeometry::Cone { - frame: cone, - semi_angle, - }, - SurfaceGeometry::Sphere { - frame: sphere, - radius: sphere_radius, - }, - ) => coaxial_sphere_cone(*sphere, *sphere_radius, *cone, *semi_angle)?, - ( - SurfaceGeometry::Cylinder { - frame: a, - radius: ra, - }, - SurfaceGeometry::Cylinder { - frame: b, - radius: rb, - }, - ) => parallel_cylinders(*a, *ra, *b, *rb)?, - ( - SurfaceGeometry::Cylinder { - frame: cylinder, - radius, - }, - SurfaceGeometry::Cone { - frame: cone, - semi_angle, - }, - ) - | ( - SurfaceGeometry::Cone { - frame: cone, - semi_angle, - }, - SurfaceGeometry::Cylinder { - frame: cylinder, - radius, - }, - ) => coaxial_cylinder_cone(*cylinder, *radius, *cone, *semi_angle)?, - ( - SurfaceGeometry::Cone { - frame: a, - semi_angle: angle_a, - }, - SurfaceGeometry::Cone { - frame: b, - semi_angle: angle_b, - }, - ) => coaxial_cones(*a, *angle_a, *b, *angle_b)?, - ( - SurfaceGeometry::Plane { frame: plane }, - SurfaceGeometry::Torus { - frame: torus, - major_radius, - minor_radius, - }, - ) - | ( - SurfaceGeometry::Torus { - frame: torus, - major_radius, - minor_radius, - }, - SurfaceGeometry::Plane { frame: plane }, - ) => plane_torus(*plane, *torus, *major_radius, *minor_radius)?, - ( - SurfaceGeometry::Sphere { - frame: sphere, - radius: sphere_radius, - }, - SurfaceGeometry::Torus { - frame: torus, - major_radius, - minor_radius, - }, - ) - | ( - SurfaceGeometry::Torus { - frame: torus, - major_radius, - minor_radius, - }, - SurfaceGeometry::Sphere { - frame: sphere, - radius: sphere_radius, - }, - ) => coaxial_sphere_torus( - *sphere, - *sphere_radius, - *torus, - *major_radius, - *minor_radius, - )?, - ( - SurfaceGeometry::Cylinder { - frame: cylinder, - radius: cylinder_radius, - }, - SurfaceGeometry::Torus { - frame: torus, - major_radius, - minor_radius, - }, - ) - | ( - SurfaceGeometry::Torus { - frame: torus, - major_radius, - minor_radius, - }, - SurfaceGeometry::Cylinder { - frame: cylinder, - radius: cylinder_radius, - }, - ) => coaxial_cylinder_torus( - *cylinder, - *cylinder_radius, - *torus, - *major_radius, - *minor_radius, - )?, - ( - SurfaceGeometry::Cone { - frame: cone, - semi_angle, - }, - SurfaceGeometry::Torus { - frame: torus, - major_radius, - minor_radius, - }, - ) - | ( - SurfaceGeometry::Torus { - frame: torus, - major_radius, - minor_radius, - }, - SurfaceGeometry::Cone { - frame: cone, - semi_angle, - }, - ) => coaxial_cone_torus(*cone, *semi_angle, *torus, *major_radius, *minor_radius)?, - ( - SurfaceGeometry::Torus { - frame: a, - major_radius: major_a, - minor_radius: minor_a, - }, - SurfaceGeometry::Torus { - frame: b, - major_radius: major_b, - minor_radius: minor_b, - }, - ) => coaxial_tori(*a, *major_a, *minor_a, *b, *major_b, *minor_b)?, - }) - })(); - let intersection = match fast_intersection { - Ok(intersection) => intersection, - Err(error @ GeometryError::CoverageGap { .. }) => { - return universal_fallback(store, a, b, &sa, &sb, accuracy, error) - } - Err(error) => return Err(error), - }; - let (intersection, pending_contacts) = match intersection { - GeometryResult::CurvesAndContacts { curves, contacts } => { - (GeometryResult::Curves(curves), contacts) - } - intersection => (intersection, Vec::new()), - }; - let mut result = SsiResult::default(); - match intersection { - GeometryResult::Empty => {} - GeometryResult::Coincident => result.coincident = true, - GeometryResult::Contact(point) => { - for surface in [&sa, &sb] { - let uv = match surface.project(point, None) { - Ok(uv) => uv, - Err(GeometryError::SingularParameterization) if matches!(surface, SurfaceGeometry::Cone { frame, .. } if norm(sub(frame.origin, point)) <= accuracy.intersection) => { - [0.0, 0.0] - } - Err(error) => return Err(error), - }; - if norm(sub(surface.point_at(uv)?, point)) > accuracy.intersection { - return Err(GeometryError::UnresolvedIntersection( - "contact support residual exceeds budget".into(), - )); - } - } - result.contacts.push(point); - } - GeometryResult::Curves(curves) => { - for (curve, domain) in curves { - let curve_id = u32::try_from(store.curves.len()) - .map_err(|_| GeometryError::LimitExceeded("SSI curves".into()))?; - let start = domain.map_or(0.0, |d| d.lo); - let point = curve.elementary_point(start)?; - let mut pcurves = [0; 2]; - let mut pending = Vec::new(); - for (i, (id, surface)) in [(a, &sa), (b, &sb)].into_iter().enumerate() { - let uv = surface.project(point, None)?; - let pcurve = if matches!(surface, SurfaceGeometry::Plane { .. }) - && matches!(curve, CurveGeometry::Line { .. }) - { - let direction = curve.elementary_tangent(start)?; - let frame = surface.frame(); - PcurveGeometry::Line2 { - origin: uv, - direction: [dot(direction, frame.x), dot(direction, frame.y)], - } - } else { - let next = - surface.project(curve.elementary_point(start + 0.01)?, Some(uv))?; - let uv_rate = std::array::from_fn(|j| (next[j] - uv[j]) / 0.01); - PcurveGeometry::ProjectedCurve { - curve: curve_id, - surface: id, - chart: 0, - uv_hint: uv, - uv_rate, - parameter_origin: start, - } - }; - pcurves[i] = u32::try_from(store.pcurves.len() + i) - .map_err(|_| GeometryError::LimitExceeded("SSI pcurves".into()))?; - pending.push(pcurve); - } - for t in [ - start, - start + domain.map_or(1.0, |d| d.width()) * 0.25, - start + domain.map_or(1.0, |d| d.width()) * 0.5, - ] { - let p = curve.elementary_point(t)?; - for surface in [&sa, &sb] { - let uv = surface.project(p, None)?; - if norm(sub(surface.point_at(uv)?, p)) > accuracy.intersection { - return Err(GeometryError::UnresolvedIntersection( - "intersection curve misses support".into(), - )); - } - } - } - store.curves.push(curve); - store.pcurves.extend(pending); - result.curves.push(SsiCurve { - curve: curve_id, - pcurves, - domain, - }); - } - } - GeometryResult::CurvesAndContacts { .. } => { - return Err(GeometryError::InvalidGeometry( - "combined SSI result was not normalized".into(), - )) - } - } - for point in pending_contacts { - for surface in [&sa, &sb] { - let uv = match surface.project(point, None) { - Ok(uv) => uv, - Err(GeometryError::SingularParameterization) if matches!(surface, SurfaceGeometry::Cone { frame, .. } if norm(sub(frame.origin, point)) <= accuracy.intersection) => { - [0.0, 0.0] - } - Err(error) => return Err(error), - }; - if norm(sub(surface.point_at(uv)?, point)) > accuracy.intersection { - return Err(GeometryError::UnresolvedIntersection( - "contact support residual exceeds budget".into(), - )); - } - } - result.contacts.push(point); - } - Ok(result) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::Curve; - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-9, - intersection: 1e-10, - tessellation: 1e-3, - exchange: 1e-5, - } - } - - fn assert_numerical_branches(store: &GeometryStore, result: &SsiResult) { - assert!(!result.curves.is_empty()); - for branch in &result.curves { - let CurveGeometry::Intersection { definition } = store.curves[branch.curve as usize] - else { - panic!("expected a numerical intersection curve") - }; - let support_ids = store.intersections[definition as usize].surfaces; - let domain = branch - .domain - .expect("numerical branch needs a finite domain"); - for i in 0..=8 { - let t = domain.lo + domain.width() * i as f64 / 8.0; - let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); - for side in 0..2 { - let uv = store.pcurve_at(branch.pcurves[side], t).unwrap(); - assert!( - norm(sub( - store.surfaces[support_ids[side] as usize] - .point_at(uv) - .unwrap(), - point - )) <= accuracy().intersection - ); - } - } - } - } - #[test] - fn plane_sphere_emits_shared_curve_and_both_pcurves() { - let mut s = GeometryStore::new(); - s.surfaces.push(SurfaceGeometry::Plane { - frame: Frame3 { - origin: [0.0, 0.0, 0.5], - ..Frame3::IDENTITY - }, - }); - s.surfaces.push(SurfaceGeometry::Sphere { - frame: Frame3::IDENTITY, - radius: 1.0, - }); - let r = intersect_surfaces(&mut s, 0, 1, accuracy()).unwrap(); - assert_eq!(r.curves.len(), 1); - let curve = &r.curves[0]; - for i in 0..=32 { - let t = std::f64::consts::TAU * i as f64 / 32.0; - let p = s.curve(curve.curve).unwrap().point_at(t).unwrap(); - for j in 0..2 { - let uv = s.pcurve_at(curve.pcurves[j], t).unwrap(); - assert!(norm(sub(s.surfaces[j].point_at(uv).unwrap(), p)) < 1e-10); - } - } - } - #[test] - fn sphere_contacts_and_near_misses_do_not_become_polylines() { - for (distance, curves, contacts) in [ - (1.0, 1, 0), - (2.0, 0, 1), - (2.0 + 1e-9, 0, 0), - (2.0 - 1e-9, 1, 0), - ] { - let mut s = GeometryStore::new(); - s.surfaces = vec![ - SurfaceGeometry::Sphere { - frame: Frame3::IDENTITY, - radius: 1.0, - }, - SurfaceGeometry::Sphere { - frame: Frame3 { - origin: [distance, 0.0, 0.0], - ..Frame3::IDENTITY - }, - radius: 1.0, - }, - ]; - let r = intersect_surfaces(&mut s, 0, 1, accuracy()).unwrap(); - assert_eq!(r.curves.len(), curves); - assert_eq!(r.contacts.len(), contacts); - } - } - #[test] - fn plane_plane_and_universal_torus_intersections_are_distinct() { - let mut s = GeometryStore::new(); - s.surfaces = vec![ - SurfaceGeometry::Plane { - frame: Frame3::IDENTITY, - }, - SurfaceGeometry::Plane { - frame: Frame3::from_axis([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]).unwrap(), - }, - SurfaceGeometry::Torus { - frame: Frame3::IDENTITY, - major_radius: 2.0, - minor_radius: 0.5, - }, - SurfaceGeometry::Torus { - frame: Frame3 { - origin: [0.5, 0.0, 0.0], - ..Frame3::IDENTITY - }, - major_radius: 2.0, - minor_radius: 0.5, - }, - ]; - let r = intersect_surfaces(&mut s, 0, 1, accuracy()).unwrap(); - assert!(matches!( - s.curves[r.curves[0].curve as usize], - CurveGeometry::Line { .. } - )); - let numerical = intersect_surfaces(&mut s, 2, 3, accuracy()).unwrap(); - assert_numerical_branches(&s, &numerical); - } - - #[test] - fn plane_cylinder_sections_cover_circle_ellipse_generators_and_tangency() { - let cylinder = SurfaceGeometry::Cylinder { - frame: Frame3::IDENTITY, - radius: 1.0, - }; - let section = |frame: Frame3| { - let mut store = GeometryStore::new(); - store.surfaces = vec![SurfaceGeometry::Plane { frame }, cylinder.clone()]; - let result = intersect_surfaces(&mut store, 0, 1, accuracy()).unwrap(); - (store, result) - }; - - let (circle_store, circle_result) = section(Frame3::IDENTITY); - assert_eq!(circle_result.curves.len(), 1); - assert!(matches!( - circle_store.curves[circle_result.curves[0].curve as usize], - CurveGeometry::Circle { .. } - )); - - let diagonal = 0.5_f64.sqrt(); - let oblique = - Frame3::from_axis([0.0; 3], [0.0, diagonal, diagonal], [1.0, 0.0, 0.0]).unwrap(); - let (ellipse_store, ellipse_result) = section(oblique); - let CurveGeometry::Ellipse { - major_radius, - minor_radius, - .. - } = ellipse_store.curves[ellipse_result.curves[0].curve as usize] - else { - panic!("oblique section must be an ellipse") - }; - assert!((major_radius - 2.0_f64.sqrt()).abs() < 1e-12); - assert!((minor_radius - 1.0).abs() < 1e-12); - for i in 0..=32 { - let t = std::f64::consts::TAU * i as f64 / 32.0; - let point = ellipse_store - .curve(ellipse_result.curves[0].curve) - .unwrap() - .point_at(t) - .unwrap(); - for side in 0..2 { - let uv = ellipse_store - .pcurve_at(ellipse_result.curves[0].pcurves[side], t) - .unwrap(); - assert!( - norm(sub( - ellipse_store.surfaces[side].point_at(uv).unwrap(), - point - )) < 1e-10 - ); - } - } - - let parallel = - |x| Frame3::from_axis([x, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]).unwrap(); - assert_eq!(section(parallel(0.0)).1.curves.len(), 2); - assert_eq!(section(parallel(1.0)).1.curves.len(), 1); - let outside = section(parallel(1.0 + 1e-9)).1; - assert!(outside.curves.is_empty() && outside.contacts.is_empty()); - } - - #[test] - fn parallel_cylinders_cover_two_generators_tangency_coaxial_and_gap() { - let intersect = |origin: Point3, radius: f64, axis: Point3| { - let mut store = GeometryStore::new(); - store.surfaces = vec![ - SurfaceGeometry::Cylinder { - frame: Frame3::IDENTITY, - radius: 1.0, - }, - SurfaceGeometry::Cylinder { - frame: Frame3::from_axis(origin, axis, [1.0, 0.0, 0.0]).unwrap(), - radius, - }, - ]; - let result = intersect_surfaces(&mut store, 0, 1, accuracy()); - (store, result) - }; - - let (store, Ok(two)) = intersect([1.0, 0.0, 3.0], 1.0, [0.0, 0.0, -1.0]) else { - panic!("parallel cylinders should intersect") - }; - assert_eq!(two.curves.len(), 2); - for branch in two.curves { - for t in [-3.0, 0.0, 4.0] { - let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); - for side in 0..2 { - let uv = store.pcurve_at(branch.pcurves[side], t).unwrap(); - assert!(norm(sub(store.surfaces[side].point_at(uv).unwrap(), point)) < 1e-10); - } - } - } - - assert_eq!( - intersect([2.0, 0.0, 0.0], 1.0, [0.0, 0.0, 1.0]) - .1 - .unwrap() - .curves - .len(), - 1 - ); - assert!( - intersect([0.0, 0.0, 2.0], 1.0, [0.0, 0.0, -1.0]) - .1 - .unwrap() - .coincident - ); - assert!(intersect([3.0, 0.0, 0.0], 1.0, [0.0, 0.0, 1.0]) - .1 - .unwrap() - .curves - .is_empty()); - assert!(matches!( - intersect([1.0, 0.0, 0.0], 1.0, [0.0, 1.0, 1.0]).1, - Err(GeometryError::CoverageGap { .. }) - )); - } - - #[test] - fn coaxial_cylinder_cone_and_cone_pairs_are_classified_without_faceting() { - let mut cylinder_cone = GeometryStore::new(); - cylinder_cone.surfaces = vec![ - SurfaceGeometry::Cylinder { - frame: Frame3 { - origin: [0.0, 0.0, -7.0], - ..Frame3::IDENTITY - }, - radius: 2.0, - }, - SurfaceGeometry::Cone { - frame: Frame3::IDENTITY, - semi_angle: std::f64::consts::FRAC_PI_4, - }, - ]; - let section = intersect_surfaces(&mut cylinder_cone, 0, 1, accuracy()).unwrap(); - assert_eq!(section.curves.len(), 1); - let CurveGeometry::Circle { frame, radius } = - cylinder_cone.curves[section.curves[0].curve as usize] - else { - panic!("coaxial cylinder/cone intersection must be a circle") - }; - assert!((radius - 2.0).abs() < 1e-12); - assert!(norm(sub(frame.origin, [0.0, 0.0, 2.0])) < 1e-12); - - let cone = |origin, axis, semi_angle| SurfaceGeometry::Cone { - frame: Frame3::from_axis(origin, axis, [1.0, 0.0, 0.0]).unwrap(), - semi_angle, - }; - let intersect = |a: SurfaceGeometry, b: SurfaceGeometry| { - let mut store = GeometryStore::new(); - store.surfaces = vec![a, b]; - intersect_surfaces(&mut store, 0, 1, accuracy()).unwrap() - }; - assert!( - intersect( - cone([0.0; 3], [0.0, 0.0, 1.0], 0.4), - cone([0.0; 3], [0.0, 0.0, 1.0], 0.4), - ) - .coincident - ); - let apex_contact = intersect( - cone([0.0; 3], [0.0, 0.0, 1.0], 0.3), - cone([0.0; 3], [0.0, 0.0, 1.0], 0.6), - ); - assert_eq!(apex_contact.contacts, vec![[0.0; 3]]); - let opposed = intersect( - cone([0.0; 3], [0.0, 0.0, 1.0], 0.4), - cone([0.0, 0.0, 4.0], [0.0, 0.0, -1.0], 0.4), - ); - assert_eq!(opposed.curves.len(), 1); - } - - #[test] - fn perpendicular_plane_cone_sections_keep_circle_and_apex_contact() { - let section = |height: f64| { - let mut store = GeometryStore::new(); - store.surfaces = vec![ - SurfaceGeometry::Plane { - frame: Frame3 { - origin: [0.0, 0.0, height], - ..Frame3::IDENTITY - }, - }, - SurfaceGeometry::Cone { - frame: Frame3::IDENTITY, - semi_angle: std::f64::consts::FRAC_PI_4, - }, - ]; - let result = intersect_surfaces(&mut store, 0, 1, accuracy()).unwrap(); - (store, result) - }; - let (store, positive) = section(2.0); - let CurveGeometry::Circle { radius, .. } = store.curves[positive.curves[0].curve as usize] - else { - panic!("perpendicular plane/cone section must be a circle") - }; - assert!((radius - 2.0).abs() < 1e-12); - assert_eq!(section(0.0).1.contacts, vec![[0.0; 3]]); - assert!(section(-1.0).1.curves.is_empty()); - - let mut oblique = GeometryStore::new(); - oblique.surfaces = vec![ - SurfaceGeometry::Plane { - frame: Frame3::from_axis([0.0, 0.0, 1.0], [0.0, 1.0, 1.0], [1.0, 0.0, 0.0]) - .unwrap(), - }, - SurfaceGeometry::Cone { - frame: Frame3::IDENTITY, - semi_angle: 0.4, - }, - ]; - let ellipse = intersect_surfaces(&mut oblique, 0, 1, accuracy()).unwrap(); - assert!(matches!( - oblique.curves[ellipse.curves[0].curve as usize], - CurveGeometry::Ellipse { .. } - )); - for i in 0..=32 { - let t = std::f64::consts::TAU * i as f64 / 32.0; - let point = oblique - .curve(ellipse.curves[0].curve) - .unwrap() - .point_at(t) - .unwrap(); - for side in 0..2 { - let uv = oblique - .pcurve_at(ellipse.curves[0].pcurves[side], t) - .unwrap(); - assert!(norm(sub(oblique.surfaces[side].point_at(uv).unwrap(), point)) < 1e-10); - } - } - - let mut hyperbolic = GeometryStore::new(); - hyperbolic.surfaces = vec![ - SurfaceGeometry::Plane { - frame: Frame3::from_axis([0.0, 0.0, 1.0], [0.0, 1.0, 0.1], [1.0, 0.0, 0.0]) - .unwrap(), - }, - SurfaceGeometry::Cone { - frame: Frame3::IDENTITY, - semi_angle: 0.4, - }, - ]; - assert!(matches!( - intersect_surfaces(&mut hyperbolic, 0, 1, accuracy()), - Err(GeometryError::CoverageGap { .. }) - )); - } - - #[test] - fn coaxial_sphere_cylinder_has_two_circles_tangent_circle_or_empty() { - let intersect = |sphere_origin: Point3, cylinder_origin: Point3, cylinder_radius: f64| { - let mut store = GeometryStore::new(); - store.surfaces = vec![ - SurfaceGeometry::Sphere { - frame: Frame3 { - origin: sphere_origin, - ..Frame3::IDENTITY - }, - radius: 2.0, - }, - SurfaceGeometry::Cylinder { - frame: Frame3 { - origin: cylinder_origin, - ..Frame3::IDENTITY - }, - radius: cylinder_radius, - }, - ]; - let result = intersect_surfaces(&mut store, 0, 1, accuracy()); - (store, result) - }; - let (store, Ok(two)) = intersect([0.0, 0.0, 3.0], [0.0; 3], 1.0) else { - panic!("coaxial sphere/cylinder should intersect") - }; - assert_eq!(two.curves.len(), 2); - for branch in two.curves { - for i in 0..=16 { - let t = std::f64::consts::TAU * i as f64 / 16.0; - let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); - for side in 0..2 { - let uv = store.pcurve_at(branch.pcurves[side], t).unwrap(); - assert!(norm(sub(store.surfaces[side].point_at(uv).unwrap(), point)) < 1e-10); - } - } - } - assert_eq!( - intersect([0.0; 3], [0.0, 0.0, 9.0], 2.0) - .1 - .unwrap() - .curves - .len(), - 1 - ); - assert!(intersect([0.0; 3], [0.0; 3], 3.0) - .1 - .unwrap() - .curves - .is_empty()); - let (store, result) = intersect([0.1, 0.0, 0.0], [0.0; 3], 1.0); - assert_numerical_branches(&store, &result.unwrap()); - } - - #[test] - fn coaxial_sphere_cone_classifies_two_single_and_empty_sections() { - let intersect = |center_height: f64, radius: f64| { - let mut store = GeometryStore::new(); - store.surfaces = vec![ - SurfaceGeometry::Sphere { - frame: Frame3 { - origin: [0.0, 0.0, center_height], - ..Frame3::IDENTITY - }, - radius, - }, - SurfaceGeometry::Cone { - frame: Frame3::IDENTITY, - semi_angle: std::f64::consts::FRAC_PI_4, - }, - ]; - let result = intersect_surfaces(&mut store, 0, 1, accuracy()); - (store, result) - }; - let (store, Ok(two)) = intersect(3.0, 2.5) else { - panic!("coaxial sphere/cone should intersect twice") - }; - assert_eq!(two.curves.len(), 2); - for branch in two.curves { - for i in 0..=16 { - let t = std::f64::consts::TAU * i as f64 / 16.0; - let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); - for side in 0..2 { - let uv = store.pcurve_at(branch.pcurves[side], t).unwrap(); - assert!(norm(sub(store.surfaces[side].point_at(uv).unwrap(), point)) < 1e-10); - } - } - } - assert_eq!(intersect(0.0, 1.0).1.unwrap().curves.len(), 1); - assert!(intersect(3.0, 1.0).1.unwrap().curves.is_empty()); - let (_, apex) = intersect(3.0, 3.0); - let apex = apex.unwrap(); - assert_eq!(apex.curves.len(), 1); - assert_eq!(apex.contacts, vec![[0.0; 3]]); - } - - #[test] - fn torus_special_sections_cover_plane_sphere_cylinder_and_cone() { - let torus = SurfaceGeometry::Torus { - frame: Frame3::IDENTITY, - major_radius: 3.0, - minor_radius: 1.0, - }; - let assert_curves = |other: SurfaceGeometry, expected: usize| { - let mut store = GeometryStore::new(); - store.surfaces = vec![other, torus.clone()]; - let result = intersect_surfaces(&mut store, 0, 1, accuracy()).unwrap(); - assert_eq!(result.curves.len(), expected); - for branch in &result.curves { - assert!(matches!( - store.curves[branch.curve as usize], - CurveGeometry::Circle { .. } - )); - for i in 0..=16 { - let t = std::f64::consts::TAU * i as f64 / 16.0; - let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); - for side in 0..2 { - let uv = store.pcurve_at(branch.pcurves[side], t).unwrap(); - assert!( - norm(sub(store.surfaces[side].point_at(uv).unwrap(), point)) < 1e-10 - ); - } - } - } - store - }; - - let horizontal = assert_curves( - SurfaceGeometry::Plane { - frame: Frame3::IDENTITY, - }, - 2, - ); - let mut radii: Vec<_> = horizontal - .curves - .iter() - .filter_map(|curve| match curve { - CurveGeometry::Circle { radius, .. } => Some(*radius), - _ => None, - }) - .collect(); - radii.sort_by(f64::total_cmp); - assert_eq!(radii, vec![2.0, 4.0]); - assert_curves( - SurfaceGeometry::Plane { - frame: Frame3 { - origin: [0.0, 0.0, 1.0], - ..Frame3::IDENTITY - }, - }, - 1, - ); - assert_curves( - SurfaceGeometry::Plane { - frame: Frame3::from_axis([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]).unwrap(), - }, - 2, - ); - assert_curves( - SurfaceGeometry::Sphere { - frame: Frame3::IDENTITY, - radius: 10.0_f64.sqrt(), - }, - 2, - ); - assert_curves( - SurfaceGeometry::Cylinder { - frame: Frame3::IDENTITY, - radius: 3.0, - }, - 2, - ); - assert_curves( - SurfaceGeometry::Cone { - frame: Frame3 { - origin: [0.0, 0.0, -3.0], - ..Frame3::IDENTITY - }, - semi_angle: std::f64::consts::FRAC_PI_4, - }, - 2, - ); - assert_curves( - SurfaceGeometry::Torus { - frame: Frame3 { - origin: [0.0, 0.0, 1.0], - ..Frame3::IDENTITY - }, - major_radius: 3.0, - minor_radius: 1.0, - }, - 2, - ); - assert_curves( - SurfaceGeometry::Torus { - frame: Frame3 { - origin: [0.0, 0.0, 2.0], - ..Frame3::IDENTITY - }, - major_radius: 3.0, - minor_radius: 1.0, - }, - 1, - ); - - let mut coincident = GeometryStore::new(); - coincident.surfaces = vec![torus.clone(), torus.clone()]; - assert!( - intersect_surfaces(&mut coincident, 0, 1, accuracy()) - .unwrap() - .coincident - ); - let mut skew = GeometryStore::new(); - skew.surfaces = vec![ - torus, - SurfaceGeometry::Torus { - frame: Frame3 { - origin: [0.5, 0.0, 0.0], - ..Frame3::IDENTITY - }, - major_radius: 3.0, - minor_radius: 1.0, - }, - ]; - let numerical = intersect_surfaces(&mut skew, 0, 1, accuracy()).unwrap(); - assert_numerical_branches(&skew, &numerical); - } - - #[test] - fn canonical_fast_dispatch_covers_all_fifteen_unordered_family_pairs() { - let families = vec![ - SurfaceGeometry::Plane { - frame: Frame3::IDENTITY, - }, - SurfaceGeometry::Sphere { - frame: Frame3::IDENTITY, - radius: 1.0, - }, - SurfaceGeometry::Cylinder { - frame: Frame3::IDENTITY, - radius: 1.0, - }, - SurfaceGeometry::Cone { - frame: Frame3::IDENTITY, - semi_angle: 0.4, - }, - SurfaceGeometry::Torus { - frame: Frame3::IDENTITY, - major_radius: 3.0, - minor_radius: 0.5, - }, - ]; - let mut count = 0; - for i in 0..families.len() { - for j in i..families.len() { - let mut store = GeometryStore::new(); - store.surfaces = vec![families[i].clone(), families[j].clone()]; - intersect_surfaces(&mut store, 0, 1, accuracy()) - .unwrap_or_else(|error| panic!("family pair {i}/{j}: {error}")); - count += 1; - } - } - assert_eq!(count, 15); - } -} diff --git a/main/opengeometry/src/analytic/tessellation.rs b/main/opengeometry/src/analytic/tessellation.rs deleted file mode 100644 index 4798e3c..0000000 --- a/main/opengeometry/src/analytic/tessellation.rs +++ /dev/null @@ -1,2005 +0,0 @@ -use super::{ - geometry::{cross, dot, norm, scale, sub, Surface}, - topology::{BrepEnvelope, EdgeGeometry, Face, Orientation}, - Curve, CurveGeometry, GeometryError, Point3, SurfaceGeometry, -}; -use crate::math::interval::Interval; -use std::{ - collections::{HashMap, HashSet, VecDeque}, - hash::{DefaultHasher, Hasher}, - io::Write, - sync::Arc, -}; - -#[derive(Clone, Debug)] -pub struct Tessellation { - pub positions: Vec, - pub normals: Vec, - pub indices: Vec, - pub triangle_face_ids: Vec, - pub outline_positions: Vec, - pub outline_edge_ids: Vec, - pub revision: u64, - pub achieved_deflection: f64, -} -impl Tessellation { - fn new(revision: u64, deflection: f64) -> Self { - Self { - positions: Vec::new(), - normals: Vec::new(), - indices: Vec::new(), - triangle_face_ids: Vec::new(), - outline_positions: Vec::new(), - outline_edge_ids: Vec::new(), - revision, - achieved_deflection: deflection, - } - } - fn vertex(&mut self, p: Point3, n: Point3) -> Result { - if p.into_iter().chain(n).any(|v| !v.is_finite()) { - return Err(GeometryError::InvalidGeometry( - "nonfinite tessellation output".into(), - )); - } - let id = u32::try_from(self.positions.len() / 3) - .map_err(|_| GeometryError::LimitExceeded("tessellation vertices".into()))?; - self.positions.extend(p); - self.normals.extend(n.map(|v| v as f32)); - Ok(id) - } - fn point(&self, id: u32) -> Point3 { - let i = id as usize * 3; - [ - self.positions[i], - self.positions[i + 1], - self.positions[i + 2], - ] - } - fn triangle( - &mut self, - mut ids: [u32; 3], - face: u32, - normal: Point3, - max_triangles: usize, - ) -> Result<(), GeometryError> { - let [a, b, c] = ids.map(|id| self.point(id)); - let direction = cross(sub(b, a), sub(c, a)); - if direction.iter().any(|v| !v.is_finite()) { - return Err(GeometryError::UnresolvedTessellation( - "triangle orientation exceeds arithmetic range".into(), - )); - } - if norm(direction) == 0.0 { - return Ok(()); - } - if self.triangle_face_ids.len() >= max_triangles { - return Err(GeometryError::LimitExceeded( - "tessellation triangles".into(), - )); - } - let orientation = dot(direction, normal); - if !orientation.is_finite() || orientation == 0.0 { - return Err(GeometryError::UnresolvedTessellation( - "triangle orientation could not be resolved".into(), - )); - } - if orientation < 0.0 { - ids.swap(1, 2); - } - self.indices.extend(ids); - self.triangle_face_ids.push(face); - Ok(()) - } - fn bytes(&self) -> usize { - self.positions.len() * 8 - + self.normals.len() * 4 - + self.indices.len() * 4 - + self.triangle_face_ids.len() * 4 - + self.outline_positions.len() * 8 - + self.outline_edge_ids.len() * 4 - } -} - -struct Rect { - halfedges: [u32; 4], -} -fn rectangle(brep: &BrepEnvelope, face: &Face) -> Result { - let [u, v] = face.trim.uv_bounds; - let mut sides = [None; 4]; - let start = brep.topology.loops[face.trim.outer as usize].start_halfedge; - let mut current = start; - let mut count = 0; - for _ in 0..128 { - let h = &brep.topology.halfedges[current as usize]; - let edge = &brep.topology.edges[h.edge as usize]; - let range = edge.geometry.range(); - let pcurve = h - .geometry_use - .pcurve - .ok_or_else(|| GeometryError::InvalidTopology("missing face pcurve".into()))?; - if !matches!( - brep.geometry.pcurves[pcurve as usize], - super::topology::PcurveGeometry::Line2 { .. } - ) { - return Err(GeometryError::UnsupportedGeometry( - "nonrectangular curved face trimming".into(), - )); - } - let periods = - brep.geometry.surface(face.surface)?.charts()[face.trim.chart as usize].periods; - let lifted = |uv: [f64; 2]| { - std::array::from_fn(|j| { - uv[j] - + periods[j].map_or(0.0, |period| { - period * h.geometry_use.periodic_lift[j] as f64 - }) - }) - }; - let a: [f64; 2] = lifted(brep.geometry.pcurve_at(pcurve, range.lo)?); - let b: [f64; 2] = lifted(brep.geometry.pcurve_at(pcurve, range.hi)?); - let close = |x: f64, y: f64| (x - y).abs() <= 1e-12; - let covers_u = - (close(a[0], u.lo) && close(b[0], u.hi)) || (close(a[0], u.hi) && close(b[0], u.lo)); - let covers_v = - (close(a[1], v.lo) && close(b[1], v.hi)) || (close(a[1], v.hi) && close(b[1], v.lo)); - let side = if covers_u && close(a[1], v.lo) && close(b[1], v.lo) { - 0 - } else if covers_v && close(a[0], u.hi) && close(b[0], u.hi) { - 1 - } else if covers_u && close(a[1], v.hi) && close(b[1], v.hi) { - 2 - } else if covers_v && close(a[0], u.lo) && close(b[0], u.lo) { - 3 - } else { - return Err(GeometryError::UnsupportedGeometry( - "curved trim is not a chart rectangle".into(), - )); - }; - if sides[side].replace(current).is_some() { - return Err(GeometryError::UnsupportedGeometry( - "repeated rectangle boundary".into(), - )); - } - count += 1; - current = h - .next - .ok_or_else(|| GeometryError::InvalidTopology("open face loop".into()))?; - if current == start { - break; - } - } - if count != 4 || sides.iter().any(Option::is_none) { - return Err(GeometryError::UnsupportedGeometry( - "curved trim is not a four-sided rectangle".into(), - )); - } - Ok(Rect { - halfedges: [ - sides[0].ok_or_else(|| GeometryError::InvalidTopology("bottom edge missing".into()))?, - sides[1].ok_or_else(|| GeometryError::InvalidTopology("right edge missing".into()))?, - sides[2].ok_or_else(|| GeometryError::InvalidTopology("top edge missing".into()))?, - sides[3].ok_or_else(|| GeometryError::InvalidTopology("left edge missing".into()))?, - ], - }) -} - -fn count(value: f64, minimum: usize, max_triangles: usize) -> Result { - let value = value.ceil().max(minimum as f64); - if !value.is_finite() || value > max_triangles as f64 { - return Err(GeometryError::LimitExceeded( - "tessellation subdivisions".into(), - )); - } - Ok(value as usize) -} -fn grid_size( - surface: &SurfaceGeometry, - bounds: [Interval; 2], - error: f64, - max_triangles: usize, -) -> Result<[usize; 2], GeometryError> { - let u = bounds[0].width(); - let v = bounds[1].width(); - let (nu, nv) = match surface { - SurfaceGeometry::Cylinder { radius, .. } => ( - count(u * (radius / (8.0 * error)).sqrt(), 3, max_triangles)?, - 1, - ), - SurfaceGeometry::Cone { semi_angle, .. } => ( - count( - u * (bounds[1].hi * semi_angle.tan() / (8.0 * error)).sqrt(), - 3, - max_triangles, - )?, - 1, - ), - SurfaceGeometry::Sphere { radius, .. } => ( - count(u * (2.0 * radius / error).sqrt(), 3, max_triangles)?, - count(v * (2.0 * radius / error).sqrt(), 2, max_triangles)?, - ), - SurfaceGeometry::Torus { - major_radius: r, - minor_radius: a, - .. - } => ( - count(u * ((r + 2.0 * a) / error).sqrt(), 3, max_triangles)?, - count(v * (2.0 * a / error).sqrt(), 3, max_triangles)?, - ), - SurfaceGeometry::Plane { .. } => { - return Err(GeometryError::InvalidGeometry( - "plane does not use a curved grid".into(), - )) - } - }; - if nu - .checked_mul(nv) - .and_then(|n| n.checked_mul(2)) - .is_none_or(|n| n > max_triangles) - { - return Err(GeometryError::LimitExceeded( - "tessellation grid triangles".into(), - )); - } - Ok([nu, nv]) -} - -fn point_segment_distance(point: Point3, a: Point3, b: Point3) -> f64 { - let chord = sub(b, a); - let length_squared = dot(chord, chord); - if length_squared == 0.0 { - return norm(sub(point, a)); - } - let parameter = (dot(sub(point, a), chord) / length_squared).clamp(0.0, 1.0); - norm(sub( - point, - [ - a[0] + parameter * chord[0], - a[1] + parameter * chord[1], - a[2] + parameter * chord[2], - ], - )) -} - -fn bounds_chord_deviation(bounds: super::geometry::PatchBounds, a: Point3, b: Point3) -> f64 { - let mut maximum: f64 = 0.0; - for mask in 0..8 { - let corner = std::array::from_fn(|axis| { - if mask & (1 << axis) == 0 { - bounds.axes[axis].lo - } else { - bounds.axes[axis].hi - } - }); - maximum = maximum.max(point_segment_distance(corner, a, b)); - } - maximum -} - -#[cfg(test)] -fn bounded_curve_count( - curve: &impl Curve, - range: Interval, - error: f64, - max_segments: usize, -) -> Result { - let mut stack = vec![(range, 0usize)]; - let mut maximum_depth = 0usize; - let mut visits = 0usize; - while let Some((interval, depth)) = stack.pop() { - visits = visits - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("curve tessellation visits".into()))?; - if visits > max_segments.saturating_mul(2) { - return Err(GeometryError::LimitExceeded( - "curve tessellation visits".into(), - )); - } - let a = curve.point_at(interval.lo)?; - let b = curve.point_at(interval.hi)?; - if bounds_chord_deviation(curve.enclose(interval)?, a, b) <= error { - maximum_depth = maximum_depth.max(depth); - continue; - } - if depth >= usize::BITS as usize - 1 || (1usize << (depth + 1)) > max_segments { - return Err(GeometryError::LimitExceeded( - "curve tessellation segments".into(), - )); - } - let midpoint = interval.midpoint(); - let left = Interval::new(interval.lo, midpoint)?; - let right = Interval::new(midpoint, interval.hi)?; - stack.push((right, depth + 1)); - stack.push((left, depth + 1)); - } - Ok(1usize << maximum_depth) -} - -fn intersection_chord_deviation( - brep: &BrepEnvelope, - definition: u32, - range: Interval, - chord_start: Point3, - chord_end: Point3, -) -> Result { - let definition = brep - .geometry - .intersections - .get(definition as usize) - .ok_or_else(|| GeometryError::InvalidGeometry("missing intersection definition".into()))?; - let support_a = brep.geometry.surface(definition.surfaces[0])?; - let support_b = brep.geometry.surface(definition.surfaces[1])?; - let mut maximum = 0.0_f64; - for (index, tube) in definition.uv_tubes.iter().enumerate() { - let anchor_start = definition.anchors[index].parameter; - let anchor_end = definition.anchors[index + 1].parameter; - let lo = range.lo.max(anchor_start); - let hi = range.hi.min(anchor_end); - if lo > hi { - continue; - } - let bounds_a = support_a.enclose([tube[0], tube[1]])?; - let bounds_b = support_b.enclose([tube[2], tube[3]])?; - let mut axes = [Interval::point(0.0)?; 3]; - for axis in 0..3 { - axes[axis] = Interval::new( - bounds_a.axes[axis].lo.max(bounds_b.axes[axis].lo), - bounds_a.axes[axis].hi.min(bounds_b.axes[axis].hi), - ) - .map_err(|_| GeometryError::InvalidGeometry("disjoint trace support boxes".into()))?; - } - let local_start = definition.evaluate(lo, &brep.geometry)?.point; - let local_end = definition.evaluate(hi, &brep.geometry)?.point; - let local_deviation = bounds_chord_deviation( - super::geometry::PatchBounds { axes }, - local_start, - local_end, - ); - let guide_deviation = point_segment_distance(local_start, chord_start, chord_end) - .max(point_segment_distance(local_end, chord_start, chord_end)); - maximum = maximum.max(local_deviation + guide_deviation); - } - Ok(maximum) -} - -fn enclosed_intersection_count( - brep: &BrepEnvelope, - definition: u32, - curve: u32, - range: Interval, - error: f64, - max_segments: usize, -) -> Result { - let evaluator = brep.geometry.curve(curve)?; - let mut stack = vec![(range, 0usize)]; - let mut maximum_depth = 0usize; - let mut visits = 0usize; - while let Some((interval, depth)) = stack.pop() { - visits = visits - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("curve tessellation visits".into()))?; - if visits > max_segments.saturating_mul(2) { - return Err(GeometryError::LimitExceeded( - "curve tessellation visits".into(), - )); - } - let start = evaluator.point_at(interval.lo)?; - let end = evaluator.point_at(interval.hi)?; - if intersection_chord_deviation(brep, definition, interval, start, end)? <= error { - maximum_depth = maximum_depth.max(depth); - continue; - } - if depth >= usize::BITS as usize - 1 || (1usize << (depth + 1)) > max_segments { - return Err(GeometryError::LimitExceeded( - "curve tessellation segments".into(), - )); - } - let midpoint = interval.midpoint(); - stack.push((Interval::new(midpoint, interval.hi)?, depth + 1)); - stack.push((Interval::new(interval.lo, midpoint)?, depth + 1)); - } - Ok(1usize << maximum_depth) -} - -fn adaptive_intersection_count( - brep: &BrepEnvelope, - definition: u32, - curve: u32, - range: Interval, - error: f64, - max_segments: usize, -) -> Result { - let evaluator = brep.geometry.curve(curve)?; - brep.geometry - .intersections - .get(definition as usize) - .ok_or_else(|| GeometryError::InvalidGeometry("missing intersection definition".into()))?; - let closed = norm(sub( - evaluator.point_at(range.lo)?, - evaluator.point_at(range.hi)?, - )) <= brep.accuracy.intersection; - let mut count = if closed { 8 } else { 1 }; - loop { - let mut achieved = 0.0_f64; - for segment in 0..count { - let lo = range.lo + range.width() * segment as f64 / count as f64; - let hi = range.lo + range.width() * (segment + 1) as f64 / count as f64; - let start = evaluator.point_at(lo)?; - let end = evaluator.point_at(hi)?; - let chord = sub(end, start); - let length = norm(chord); - if length == 0.0 { - achieved = f64::INFINITY; - break; - } - let direction = scale(chord, 1.0 / length); - let mut tangent_turn = 0.0_f64; - for fraction in [0.0, 0.25, 0.5, 0.75, 1.0] { - let parameter = lo + (hi - lo) * fraction; - let point = evaluator.point_at(parameter)?; - achieved = achieved.max(point_segment_distance(point, start, end)); - let tangent = evaluator.tangent_at(parameter)?; - let tangent = scale(tangent, 1.0 / norm(tangent)); - tangent_turn = tangent_turn.max(norm(cross(tangent, direction))); - } - achieved = achieved.max(0.25 * length * tangent_turn); - } - if achieved <= error { - return Ok(count); - } - count = count - .checked_mul(2) - .filter(|next| *next <= max_segments) - .ok_or_else(|| GeometryError::LimitExceeded("curve tessellation segments".into()))?; - } -} - -fn bounded_intersection_count( - brep: &BrepEnvelope, - definition: u32, - curve: u32, - range: Interval, - error: f64, - max_segments: usize, -) -> Result { - match enclosed_intersection_count(brep, definition, curve, range, error, max_segments) { - Ok(count) => Ok(count), - Err(GeometryError::LimitExceeded(_)) => { - let count = - adaptive_intersection_count(brep, definition, curve, range, error, max_segments)?; - Ok(count) - } - Err(error) => Err(error), - } -} - -fn edge_count( - brep: &BrepEnvelope, - id: u32, - error: f64, - max_triangles: usize, -) -> Result { - match brep.topology.edges[id as usize].geometry { - EdgeGeometry::Collapsed { .. } => Ok(1), - EdgeGeometry::Curve { curve, range } => match &brep.geometry.curves[curve as usize] { - CurveGeometry::Line { .. } => Ok(1), - CurveGeometry::Circle { radius, .. } => count( - range.width() * (radius / (8.0 * error)).sqrt(), - if range.width() >= std::f64::consts::TAU - 1e-12 { - 3 - } else { - 1 - }, - max_triangles, - ), - CurveGeometry::Ellipse { major_radius, .. } => count( - range.width() * (major_radius / (8.0 * error)).sqrt(), - if range.width() >= std::f64::consts::TAU - 1e-12 { - 3 - } else { - 1 - }, - max_triangles, - ), - CurveGeometry::Intersection { definition } => { - bounded_intersection_count(brep, *definition, curve, range, error, max_triangles) - } - }, - } -} - -fn synchronize_edge_samples( - brep: &BrepEnvelope, - grids: &[Option<(Option, [usize; 2])>], - counts: &mut [usize], -) { - fn find(parent: &mut [usize], value: usize) -> usize { - let mut root = value; - while parent[root] != root { - root = parent[root]; - } - let mut current = value; - while parent[current] != current { - let next = parent[current]; - parent[current] = root; - current = next; - } - root - } - fn union(parent: &mut [usize], a: usize, b: usize) { - let a = find(parent, a); - let b = find(parent, b); - if a != b { - parent[b] = a; - } - } - - let mut parent: Vec<_> = (0..counts.len()).collect(); - for (rect, _) in grids.iter().flatten() { - let Some(rect) = rect else { continue }; - for [a, b] in [[0, 2], [1, 3]] { - let a = brep.topology.halfedges[rect.halfedges[a] as usize].edge as usize; - let b = brep.topology.halfedges[rect.halfedges[b] as usize].edge as usize; - union(&mut parent, a, b); - } - } - let key = |edge: &super::topology::Edge| -> Option<[u64; 14]> { - let EdgeGeometry::Curve { curve, range } = edge.geometry else { - return None; - }; - let frame = match &brep.geometry.curves[curve as usize] { - CurveGeometry::Circle { frame, .. } | CurveGeometry::Ellipse { frame, .. } => frame, - _ => return None, - }; - let values = [frame.origin, frame.x, frame.y, frame.z]; - Some(std::array::from_fn(|i| { - if i < 12 { - values[i / 3][i % 3].to_bits() - } else if i == 12 { - range.lo.to_bits() - } else { - range.hi.to_bits() - } - })) - }; - let mut concentric = HashMap::<[u64; 14], usize>::new(); - for edge in &brep.topology.edges { - if let Some(key) = key(edge) { - if let Some(&other) = concentric.get(&key) { - union(&mut parent, edge.id as usize, other); - } else { - concentric.insert(key, edge.id as usize); - } - } - } - let mut maxima = vec![0; counts.len()]; - for (edge, &count) in counts.iter().enumerate() { - let root = find(&mut parent, edge); - maxima[root] = maxima[root].max(count); - } - for (edge, count) in counts.iter_mut().enumerate() { - *count = maxima[find(&mut parent, edge)]; - } -} - -fn sample_edge(brep: &BrepEnvelope, id: u32, n: usize) -> Result, GeometryError> { - let edge = &brep.topology.edges[id as usize]; - let h = &brep.topology.halfedges[edge.halfedge as usize]; - match edge.geometry { - EdgeGeometry::Collapsed { vertex } => { - Ok(vec![brep.topology.vertices[vertex as usize].position]) - } - EdgeGeometry::Curve { curve, range } => { - let evaluator = brep.geometry.curve(curve)?; - let mut points = Vec::with_capacity(n + 1); - for i in 0..=n { - points.push( - evaluator.point_at(range.lo + (range.hi - range.lo) * i as f64 / n as f64)?, - ); - } - let (first, last) = if h.geometry_use.sense == Orientation::Forward { - (h.from, h.to) - } else { - (h.to, h.from) - }; - points[0] = brep.topology.vertices[first as usize].position; - points[n] = brep.topology.vertices[last as usize].position; - Ok(points) - } - } -} - -fn boundary_point( - brep: &BrepEnvelope, - samples: &[Vec], - halfedge: u32, - index: usize, - n: usize, - axis: usize, -) -> Result { - let h = &brep.topology.halfedges[halfedge as usize]; - let points = &samples[h.edge as usize]; - if points.len() == 1 { - return Ok(points[0]); - } - if points.len() != n + 1 { - return Err(GeometryError::UnsupportedGeometry( - "incompatible opposite edge subdivision counts".into(), - )); - } - let range = brep.topology.edges[h.edge as usize].geometry.range(); - let pcurve = h - .geometry_use - .pcurve - .ok_or_else(|| GeometryError::InvalidTopology("missing pcurve".into()))?; - let a = brep.geometry.pcurve_at(pcurve, range.lo)?; - let b = brep.geometry.pcurve_at(pcurve, range.hi)?; - Ok(points[if a[axis] <= b[axis] { index } else { n - index }]) -} - -fn curved_face( - brep: &BrepEnvelope, - face: &Face, - rect: &Rect, - desired: [usize; 2], - samples: &[Vec], - mesh: &mut Tessellation, - max_triangles: usize, -) -> Result<(), GeometryError> { - let mut size = desired; - for (i, &h) in rect.halfedges.iter().enumerate() { - let n = samples[brep.topology.halfedges[h as usize].edge as usize] - .len() - .saturating_sub(1); - size[i % 2] = size[i % 2].max(n); - } - let [nu, nv] = size; - let [u, v] = face.trim.uv_bounds; - let surface = brep.geometry.surface(face.surface)?; - let start = mesh.positions.len() / 3; - if nu - .checked_add(1) - .and_then(|n| nv.checked_add(1).and_then(|m| n.checked_mul(m))) - .is_none_or(|n| n > max_triangles * 3) - { - return Err(GeometryError::LimitExceeded( - "tessellation grid vertices".into(), - )); - } - for j in 0..=nv { - for i in 0..=nu { - let uv = [ - (u.lo + u.width() * i as f64 / nu as f64).clamp(u.lo, u.hi), - (v.lo + v.width() * j as f64 / nv as f64).clamp(v.lo, v.hi), - ]; - let point = if j == 0 { - boundary_point(brep, samples, rect.halfedges[0], i, nu, 0)? - } else if j == nv { - boundary_point(brep, samples, rect.halfedges[2], i, nu, 0)? - } else if i == 0 { - boundary_point(brep, samples, rect.halfedges[3], j, nv, 1)? - } else if i == nu { - boundary_point(brep, samples, rect.halfedges[1], j, nv, 1)? - } else { - surface.point_at(uv)? - }; - let normal_uv = if matches!(surface, SurfaceGeometry::Cone { .. }) && uv[1] == 0.0 { - [uv[0], v.hi] - } else { - uv - }; - let normal = scale(surface.normal_at(normal_uv)?, face.sense.multiplier()); - mesh.vertex(point, normal)?; - } - } - for j in 0..nv { - for i in 0..nu { - let a = (start + j * (nu + 1) + i) as u32; - let b = a + 1; - let c = a + (nu + 1) as u32; - let d = c + 1; - let uv = [ - u.lo + u.width() * (i as f64 + 0.5) / nu as f64, - v.lo + v.width() * (j as f64 + 0.5) / nv as f64, - ]; - let normal = scale(surface.normal_at(uv)?, face.sense.multiplier()); - mesh.triangle([a, b, d], face.id, normal, max_triangles)?; - mesh.triangle([a, d, c], face.id, normal, max_triangles)?; - } - } - Ok(()) -} - -fn trim_loop_samples( - brep: &BrepEnvelope, - face: &Face, - loop_id: u32, - samples: &[Vec], -) -> Result, GeometryError> { - let periods = brep.geometry.surface(face.surface)?.charts()[face.trim.chart as usize].periods; - let start = brep.topology.loops[loop_id as usize].start_halfedge; - let mut current = start; - let mut result = Vec::new(); - loop { - let halfedge = &brep.topology.halfedges[current as usize]; - let edge = &brep.topology.edges[halfedge.edge as usize]; - let source = &samples[halfedge.edge as usize]; - let pcurve = halfedge - .geometry_use - .pcurve - .ok_or_else(|| GeometryError::InvalidTopology("missing face pcurve".into()))?; - let range = match edge.geometry { - EdgeGeometry::Curve { range, .. } => range, - EdgeGeometry::Collapsed { vertex } => { - let parameter = if halfedge.geometry_use.sense == Orientation::Forward { - 0.0 - } else { - 1.0 - }; - let mut uv = brep.geometry.pcurve_at(pcurve, parameter)?; - for axis in 0..2 { - uv[axis] += periods[axis].map_or(0.0, |period| { - period * halfedge.geometry_use.periodic_lift[axis] as f64 - }); - } - result.push((uv, brep.topology.vertices[vertex as usize].position)); - current = halfedge - .next - .ok_or_else(|| GeometryError::InvalidTopology("open face loop".into()))?; - if current == start { - break; - } - continue; - } - }; - let segment_count = source.len().saturating_sub(1); - if segment_count == 0 { - return Err(GeometryError::InvalidTopology( - "empty curved trim edge sample".into(), - )); - } - for offset in 0..segment_count { - let sample_index = if halfedge.geometry_use.sense == Orientation::Forward { - offset - } else { - segment_count - offset - }; - let parameter = range.lo + range.width() * sample_index as f64 / segment_count as f64; - let mut uv = brep.geometry.pcurve_at(pcurve, parameter)?; - for axis in 0..2 { - uv[axis] += periods[axis].map_or(0.0, |period| { - period * halfedge.geometry_use.periodic_lift[axis] as f64 - }); - } - result.push((uv, source[sample_index])); - } - current = halfedge - .next - .ok_or_else(|| GeometryError::InvalidTopology("open face loop".into()))?; - if current == start { - break; - } - if result.len() - > brep - .topology - .halfedges - .len() - .saturating_mul(maximum_edge_samples(samples)) - { - return Err(GeometryError::LimitExceeded( - "curved trim loop samples".into(), - )); - } - } - if result.len() < 4 { - return Err(GeometryError::InvalidTopology( - "curved trim loop requires at least four samples".into(), - )); - } - Ok(result) -} - -fn maximum_edge_samples(samples: &[Vec]) -> usize { - samples.iter().map(Vec::len).max().unwrap_or(1) -} - -fn periodic_ring_winding(ring: &[([f64; 2], Point3)], axis: usize, period: f64) -> i32 { - let delta = ring - .iter() - .zip(ring.iter().cycle().skip(1)) - .take(ring.len()) - .map(|(a, b)| { - let raw = b.0[axis] - a.0[axis]; - raw - (raw / period).round() * period - }) - .sum::(); - (delta / period).round() as i32 -} - -fn open_periodic_ring( - ring: &[([f64; 2], Point3)], - axis: usize, - period: f64, - seam: f64, - other_period: Option, -) -> Option> { - let other = 1 - axis; - let mut sorted = ring - .iter() - .map(|&(mut uv, point)| { - uv[axis] = seam + (uv[axis] - seam).rem_euclid(period); - (uv, point) - }) - .collect::>(); - sorted.sort_by(|a, b| a.0[axis].total_cmp(&b.0[axis])); - let tolerance = 256.0 * f64::EPSILON * period.max(1.0); - sorted.dedup_by(|a, b| (a.0[axis] - b.0[axis]).abs() <= tolerance); - if sorted.len() < 3 { - return None; - } - - let first = sorted[0]; - let last = *sorted.last()?; - let before = last.0[axis] - period; - let span = first.0[axis] - before; - if span <= tolerance { - return None; - } - let t = ((seam - before) / span).clamp(0.0, 1.0); - let mut other_delta = first.0[other] - last.0[other]; - if let Some(other_period) = other_period { - other_delta -= (other_delta / other_period).round() * other_period; - } - let other_value = last.0[other] + other_delta * t; - let seam_point = std::array::from_fn(|component| { - last.1[component] + (first.1[component] - last.1[component]) * t - }); - let mut seam_uv = first.0; - seam_uv[axis] = seam; - seam_uv[other] = other_value; - - let mut opened = Vec::with_capacity(sorted.len() + 2); - opened.push((seam_uv, seam_point)); - opened.extend( - sorted - .into_iter() - .filter(|(uv, _)| (uv[axis] - seam).abs() > tolerance), - ); - let mut end_uv = seam_uv; - end_uv[axis] += period; - opened.push((end_uv, seam_point)); - Some(opened) -} - -fn periodic_band_loop( - face: &Face, - periods: [Option; 2], - loops: &[Vec<([f64; 2], Point3)>], -) -> Option> { - if loops.len() != 2 { - return None; - } - for axis in 0..2 { - let Some(period) = periods[axis] else { - continue; - }; - let windings = [ - periodic_ring_winding(&loops[0], axis, period), - periodic_ring_winding(&loops[1], axis, period), - ]; - if windings.iter().any(|winding| winding.abs() != 1) { - continue; - } - let other = 1 - axis; - if let Some(other_period) = periods[other] { - if loops - .iter() - .any(|ring| periodic_ring_winding(ring, other, other_period) != 0) - { - continue; - } - } - let seam = face.trim.uv_bounds[axis].lo; - let mut first = open_periodic_ring(&loops[0], axis, period, seam, periods[other])?; - let mut second = open_periodic_ring(&loops[1], axis, period, seam, periods[other])?; - let first_mean = first.iter().map(|(uv, _)| uv[other]).sum::() / first.len() as f64; - let second_mean = second.iter().map(|(uv, _)| uv[other]).sum::() / second.len() as f64; - if first_mean > second_mean { - std::mem::swap(&mut first, &mut second); - } - second.reverse(); - first.extend(second); - return Some(first); - } - None -} - -fn merged_grid_coordinates( - uniform: impl Iterator, - boundary: impl Iterator, -) -> Vec { - let mut values = uniform - .map(|value| (value, false)) - .chain(boundary.map(|value| (value, true))) - .collect::>(); - values.sort_by(|a, b| a.0.total_cmp(&b.0)); - let magnitude = values - .iter() - .map(|(value, _)| value.abs()) - .fold(1.0_f64, f64::max); - let tolerance = 128.0 * f64::EPSILON * magnitude; - let mut merged: Vec<(f64, bool)> = Vec::with_capacity(values.len()); - for value in values { - if let Some(last) = merged.last_mut() { - if (value.0 - last.0).abs() <= tolerance { - if value.1 { - *last = value; - } - continue; - } - } - merged.push(value); - } - merged.into_iter().map(|(value, _)| value).collect() -} - -fn trimmed_surface_edge_deviation( - surface: &SurfaceGeometry, - uv: &[[f64; 2]], - a: usize, - b: usize, -) -> f64 { - let du = (uv[b][0] - uv[a][0]).abs(); - let dv = (uv[b][1] - uv[a][1]).abs(); - let v = [uv[a][1].min(uv[b][1]), uv[a][1].max(uv[b][1])]; - let maximum_trig = |offset: f64, values: fn(f64) -> f64| { - let period = std::f64::consts::PI; - let contains_extremum = - ((v[0] - offset) / period).ceil() <= ((v[1] - offset) / period).floor(); - if contains_extremum { - 1.0 - } else { - values(v[0]).abs().max(values(v[1]).abs()) - } - }; - let acceleration = match surface { - SurfaceGeometry::Plane { .. } => 0.0, - SurfaceGeometry::Sphere { radius, .. } => { - let cos_v = maximum_trig(0.0, f64::cos); - let sin_v = maximum_trig(std::f64::consts::FRAC_PI_2, f64::sin); - radius * (cos_v * du * du + 2.0 * sin_v * du * dv + dv * dv) - } - SurfaceGeometry::Cylinder { radius, .. } => radius * du * du, - SurfaceGeometry::Cone { semi_angle, .. } => { - let slope = semi_angle.tan(); - let radius = uv[a][1].abs().max(uv[b][1].abs()) * slope; - radius * du * du + 2.0 * slope * du * dv - } - SurfaceGeometry::Torus { - major_radius, - minor_radius, - .. - } => { - (major_radius + minor_radius) * du * du - + 2.0 * minor_radius * du * dv - + minor_radius * dv * dv - } - }; - acceleration / 8.0 -} - -fn general_curved_trimmed_face( - brep: &BrepEnvelope, - face: &Face, - _desired: [usize; 2], - loops: &[Vec<([f64; 2], Point3)>], - error: f64, - mesh: &mut Tessellation, - max_triangles: usize, -) -> Result<(), GeometryError> { - let mut uv = Vec::new(); - let mut points = Vec::new(); - let mut holes = Vec::new(); - let mut boundary_edges = HashSet::new(); - for (loop_index, ring) in loops.iter().enumerate() { - if loop_index > 0 { - holes.push(uv.len()); - } - let start = uv.len(); - for &(coordinate, point) in ring { - uv.push(coordinate); - points.push(point); - } - for offset in 0..ring.len() { - let a = start + offset; - let b = start + (offset + 1) % ring.len(); - boundary_edges.insert(if a < b { (a, b) } else { (b, a) }); - } - } - let flattened = uv - .iter() - .flat_map(|coordinate| *coordinate) - .collect::>(); - let indices = earcutr::earcut(&flattened, &holes, 2); - if indices.is_empty() { - return Err(GeometryError::InvalidTopology( - "curved trim triangulation failed".into(), - )); - } - let mut triangles = indices - .chunks_exact(3) - .map(|triangle| [triangle[0], triangle[1], triangle[2]]) - .collect::>(); - let surface = brep.geometry.surface(face.surface)?; - for _ in 0..128 { - let mut split_edges = HashSet::new(); - for triangle in &triangles { - for [a, b] in [ - [triangle[0], triangle[1]], - [triangle[1], triangle[2]], - [triangle[2], triangle[0]], - ] { - let key = if a < b { (a, b) } else { (b, a) }; - if boundary_edges.contains(&key) { - continue; - } - let needs_refinement = - trimmed_surface_edge_deviation(surface, &uv, a, b) > error * 2.0; - if needs_refinement { - split_edges.insert(key); - } - } - } - if split_edges.is_empty() { - break; - } - if triangles - .len() - .checked_mul(4) - .is_none_or(|count| count > max_triangles) - { - return Err(GeometryError::LimitExceeded( - "curved trim refinement triangles".into(), - )); - } - let mut midpoints = HashMap::with_capacity(split_edges.len()); - for &(a, b) in &split_edges { - let coordinate = [(uv[a][0] + uv[b][0]) * 0.5, (uv[a][1] + uv[b][1]) * 0.5]; - let id = uv.len(); - uv.push(coordinate); - points.push(surface.point_at(coordinate)?); - midpoints.insert((a, b), id); - } - let midpoint = |a: usize, b: usize| { - let key = if a < b { (a, b) } else { (b, a) }; - midpoints.get(&key).copied() - }; - let mut refined = Vec::with_capacity(triangles.len() * 4); - for [a, b, c] in triangles { - let ab = midpoint(a, b); - let bc = midpoint(b, c); - let ca = midpoint(c, a); - match (ab, bc, ca) { - (None, None, None) => refined.push([a, b, c]), - (Some(ab), None, None) => refined.extend([[a, ab, c], [ab, b, c]]), - (None, Some(bc), None) => refined.extend([[b, bc, a], [bc, c, a]]), - (None, None, Some(ca)) => refined.extend([[c, ca, b], [ca, a, b]]), - (Some(ab), Some(bc), None) => { - refined.extend([[b, bc, ab], [ab, bc, c], [ab, c, a]]) - } - (None, Some(bc), Some(ca)) => { - refined.extend([[c, ca, bc], [bc, ca, a], [bc, a, b]]) - } - (Some(ab), None, Some(ca)) => { - refined.extend([[a, ab, ca], [ca, ab, b], [ca, b, c]]) - } - (Some(ab), Some(bc), Some(ca)) => { - refined.extend([[a, ab, ca], [ab, b, bc], [ca, bc, c], [ab, bc, ca]]) - } - } - } - triangles = refined; - } - for triangle in &triangles { - for [a, b] in [ - [triangle[0], triangle[1]], - [triangle[1], triangle[2]], - [triangle[2], triangle[0]], - ] { - let key = if a < b { (a, b) } else { (b, a) }; - let needs_refinement = trimmed_surface_edge_deviation(surface, &uv, a, b) > error * 2.0; - if !boundary_edges.contains(&key) && needs_refinement { - return Err(GeometryError::LimitExceeded(format!( - "curved trim refinement depth on face {}: bound {} for edge {:?} to {:?}", - face.id, - trimmed_surface_edge_deviation(surface, &uv, a, b), - uv[a], - uv[b] - ))); - } - } - } - if triangles.len() > max_triangles { - return Err(GeometryError::LimitExceeded("curved trim triangles".into())); - } - let start = mesh.positions.len() / 3; - for (coordinate, point) in uv.iter().zip(points) { - let normal_coordinate = if matches!(surface, SurfaceGeometry::Cone { .. }) - && coordinate[1].abs() <= brep.accuracy.geometric - { - let bounds = face.trim.uv_bounds[1]; - [ - coordinate[0], - if bounds.lo.abs() > bounds.hi.abs() { - bounds.lo - } else { - bounds.hi - }, - ] - } else { - *coordinate - }; - let normal = scale( - surface.normal_at(normal_coordinate)?, - face.sense.multiplier(), - ); - mesh.vertex(point, normal)?; - } - for triangle in triangles { - let center = std::array::from_fn(|axis| { - (uv[triangle[0]][axis] + uv[triangle[1]][axis] + uv[triangle[2]][axis]) / 3.0 - }); - let normal_coordinate = if matches!(surface, SurfaceGeometry::Cone { .. }) - && center[1].abs() <= brep.accuracy.geometric - { - let bounds = face.trim.uv_bounds[1]; - [ - center[0], - if bounds.lo.abs() > bounds.hi.abs() { - bounds.lo - } else { - bounds.hi - }, - ] - } else { - center - }; - let normal = scale( - surface.normal_at(normal_coordinate)?, - face.sense.multiplier(), - ); - mesh.triangle( - triangle.map(|index| (start + index) as u32), - face.id, - normal, - max_triangles, - )?; - } - Ok(()) -} - -fn curved_trimmed_face( - brep: &BrepEnvelope, - face: &Face, - rect: Option<&Rect>, - desired: [usize; 2], - samples: &[Vec], - error: f64, - mesh: &mut Tessellation, - max_triangles: usize, -) -> Result<(), GeometryError> { - use crate::geometry::poly2d::{point_in_ring2, Pt2}; - - let mut size = desired; - if let Some(rect) = rect { - for (axis, &halfedge) in rect.halfedges.iter().enumerate() { - let edge = brep.topology.halfedges[halfedge as usize].edge as usize; - size[axis % 2] = size[axis % 2].max(samples[edge].len().saturating_sub(1)); - } - } - let mut loops = Vec::with_capacity(face.trim.holes.len() + 1); - for &loop_id in std::iter::once(&face.trim.outer).chain(&face.trim.holes) { - let points = trim_loop_samples(brep, face, loop_id, samples)?; - loops.push(points); - } - let surface = brep.geometry.surface(face.surface)?; - let periods = surface.charts()[face.trim.chart as usize].periods; - if let Some(band) = periodic_band_loop(face, periods, &loops) { - loops = vec![band]; - } - let mut axis_aligned = true; - for points in &loops { - let magnitude = points - .iter() - .flat_map(|(uv, _)| uv) - .map(|value| value.abs()) - .fold(1.0_f64, f64::max); - let tolerance = 128.0 * f64::EPSILON * magnitude; - for pair in points - .iter() - .zip(points.iter().cycle().skip(1)) - .take(points.len()) - { - let du = (pair.0 .0[0] - pair.1 .0[0]).abs(); - let dv = (pair.0 .0[1] - pair.1 .0[1]).abs(); - if du > tolerance && dv > tolerance { - axis_aligned = false; - } - } - } - if rect.is_some() && !face.trim.holes.is_empty() { - let mut bounds = face.trim.uv_bounds; - let mut shifted = false; - for axis in 0..2 { - let Some(period) = periods[axis] else { - continue; - }; - let mut lo = f64::INFINITY; - let mut hi = f64::NEG_INFINITY; - for (uv, _) in loops[1..].iter().flatten() { - lo = lo.min(uv[axis]); - hi = hi.max(uv[axis]); - } - if !lo.is_finite() || hi - lo >= period { - continue; - } - let original = face.trim.uv_bounds[axis]; - let tolerance = - 256.0 * f64::EPSILON * original.lo.abs().max(original.hi.abs()).max(1.0); - if lo >= original.lo - tolerance && hi <= original.hi + tolerance { - continue; - } - let center = 0.5 * (lo + hi); - bounds[axis] = Interval::new(center - 0.5 * period, center + 0.5 * period)?; - for ring in &mut loops[1..] { - let ring_center = - ring.iter().map(|(uv, _)| uv[axis]).sum::() / ring.len().max(1) as f64; - let offset = ((center - ring_center) / period).round() * period; - for (uv, _) in ring { - uv[axis] += offset; - } - } - shifted = true; - } - if shifted { - let coordinates = [ - [bounds[0].lo, bounds[1].lo], - [bounds[0].hi, bounds[1].lo], - [bounds[0].hi, bounds[1].hi], - [bounds[0].lo, bounds[1].hi], - ]; - loops[0] = coordinates - .into_iter() - .map(|uv| Ok((uv, surface.point_at(uv)?))) - .collect::, GeometryError>>()?; - } - } - if !axis_aligned { - return general_curved_trimmed_face(brep, face, size, &loops, error, mesh, max_triangles); - } - let [u, v] = face.trim.uv_bounds; - let all_boundary = loops.iter().flatten().collect::>(); - let us = merged_grid_coordinates( - (0..=size[0]).map(|index| u.lo + u.width() * index as f64 / size[0] as f64), - all_boundary.iter().map(|(uv, _)| uv[0]), - ); - let vs = merged_grid_coordinates( - (0..=size[1]).map(|index| v.lo + v.width() * index as f64 / size[1] as f64), - all_boundary.iter().map(|(uv, _)| uv[1]), - ); - let vertex_count = us - .len() - .checked_mul(vs.len()) - .ok_or_else(|| GeometryError::LimitExceeded("trimmed surface grid vertices".into()))?; - let cell_count = us - .len() - .saturating_sub(1) - .checked_mul(vs.len().saturating_sub(1)) - .and_then(|count| count.checked_mul(2)) - .ok_or_else(|| GeometryError::LimitExceeded("trimmed surface grid triangles".into()))?; - if vertex_count > max_triangles.saturating_mul(3) || cell_count > max_triangles { - return Err(GeometryError::LimitExceeded( - "trimmed surface grid budget".into(), - )); - } - let coordinate_tolerance = 256.0 - * f64::EPSILON - * us.iter() - .chain(&vs) - .map(|value| value.abs()) - .fold(1.0_f64, f64::max); - let surface = brep.geometry.surface(face.surface)?; - let start = mesh.positions.len() / 3; - for &chart_v in &vs { - for &chart_u in &us { - let uv = [chart_u, chart_v]; - let point = all_boundary - .iter() - .find(|(boundary_uv, _)| { - (boundary_uv[0] - chart_u).abs() <= coordinate_tolerance - && (boundary_uv[1] - chart_v).abs() <= coordinate_tolerance - }) - .map_or_else(|| surface.point_at(uv), |(_, point)| Ok(*point))?; - let normal_uv = if matches!(surface, SurfaceGeometry::Cone { .. }) && chart_v == 0.0 { - [chart_u, v.hi] - } else { - uv - }; - let normal = scale(surface.normal_at(normal_uv)?, face.sense.multiplier()); - mesh.vertex(point, normal)?; - } - } - let rings = loops - .iter() - .map(|points| { - points - .iter() - .map(|(uv, _)| Pt2::new(uv[0], uv[1])) - .collect::>() - }) - .collect::>(); - for row in 0..vs.len() - 1 { - for column in 0..us.len() - 1 { - let center = Pt2::new( - (us[column] + us[column + 1]) * 0.5, - (vs[row] + vs[row + 1]) * 0.5, - ); - if !point_in_ring2(center, &rings[0]) - || rings[1..].iter().any(|hole| point_in_ring2(center, hole)) - { - continue; - } - let a = (start + row * us.len() + column) as u32; - let b = a + 1; - let c = a + us.len() as u32; - let d = c + 1; - let normal = scale( - surface.normal_at([center.x, center.z])?, - face.sense.multiplier(), - ); - mesh.triangle([a, b, d], face.id, normal, max_triangles)?; - mesh.triangle([a, d, c], face.id, normal, max_triangles)?; - } - } - Ok(()) -} - -fn planar_face( - brep: &BrepEnvelope, - face: &Face, - samples: &[Vec], - mesh: &mut Tessellation, - max_triangles: usize, -) -> Result<(), GeometryError> { - let surface = brep.geometry.surface(face.surface)?; - let normal = scale(surface.normal_at([0.0; 2])?, face.sense.multiplier()); - let mut points = Vec::new(); - let mut uv_flat = Vec::new(); - let mut holes = Vec::new(); - for (i, &loop_id) in std::iter::once(&face.trim.outer) - .chain(&face.trim.holes) - .enumerate() - { - if i > 0 { - holes.push(points.len()); - } - let start = brep.topology.loops[loop_id as usize].start_halfedge; - let mut current = start; - loop { - let h = &brep.topology.halfedges[current as usize]; - let source = &samples[h.edge as usize]; - if source.len() > 1 { - let n = source.len() - 1; - for j in 0..n { - let p = source[if h.geometry_use.sense == Orientation::Forward { - j - } else { - n - j - }]; - let uv = surface.project(p, None)?; - points.push(p); - uv_flat.extend(uv); - } - } - current = h - .next - .ok_or_else(|| GeometryError::InvalidTopology("open planar face loop".into()))?; - if current == start { - break; - } - } - let loop_start = if i == 0 { 0 } else { holes[i - 1] }; - if points.len() - loop_start < 3 { - return Err(GeometryError::InvalidTopology( - "insufficient tessellated planar boundary".into(), - )); - } - } - let triangles = earcutr::earcut(&uv_flat, &holes, 2); - if triangles.is_empty() && points.len() >= 3 { - return Err(GeometryError::InvalidTopology( - "planar triangulation failed".into(), - )); - } - let mut ids = Vec::with_capacity(points.len()); - for p in points { - ids.push(mesh.vertex(p, normal)?); - } - for tri in triangles.chunks_exact(3) { - let a = *ids - .get(tri[0]) - .ok_or_else(|| GeometryError::InvalidTopology("triangulation index".into()))?; - let b = *ids - .get(tri[1]) - .ok_or_else(|| GeometryError::InvalidTopology("triangulation index".into()))?; - let c = *ids - .get(tri[2]) - .ok_or_else(|| GeometryError::InvalidTopology("triangulation index".into()))?; - mesh.triangle([a, b, c], face.id, normal, max_triangles)?; - } - Ok(()) -} - -pub fn tessellate( - brep: &BrepEnvelope, - deflection: f64, - max_triangles: usize, -) -> Result { - brep.validate()?; - if !deflection.is_finite() - || deflection <= 2.0 * brep.accuracy.geometric - || max_triangles == 0 - || max_triangles > 10_000_000 - { - return Err(GeometryError::InvalidGeometry( - "deflection must exceed twice geometric tolerance; triangle limit must be 1..=10000000" - .into(), - )); - } - let error = deflection * 0.5; - let mut magnitude: f64 = 0.0; - for face in &brep.topology.faces { - let surface = brep.geometry.surface(face.surface)?; - for value in surface.frame().origin { - magnitude = magnitude.max(value.abs()); - } - let bounds = surface.enclose(face.trim.uv_bounds)?; - for axis in bounds.axes { - magnitude = magnitude.max(axis.lo.abs()).max(axis.hi.abs()); - } - } - for vertex in &brep.topology.vertices { - for value in vertex.position { - magnitude = magnitude.max(value.abs()); - } - } - for edge in &brep.topology.edges { - if let EdgeGeometry::Curve { curve, range } = edge.geometry { - let curve = brep.geometry.curve(curve)?; - for axis in curve.enclose(range)?.axes { - magnitude = magnitude.max(axis.lo.abs()).max(axis.hi.abs()); - } - match curve.geometry { - CurveGeometry::Circle { frame, radius } => { - magnitude = magnitude.max(*radius); - for value in frame.origin { - magnitude = magnitude.max(value.abs()); - } - } - CurveGeometry::Ellipse { - frame, - major_radius, - .. - } => { - magnitude = magnitude.max(*major_radius); - for value in frame.origin { - magnitude = magnitude.max(value.abs()); - } - } - _ => {} - } - } - } - let coordinate_floor = 64.0 * f64::EPSILON * magnitude; - if deflection <= 2.0 * (brep.accuracy.geometric + coordinate_floor) { - return Err(GeometryError::LimitExceeded( - "coordinate precision cannot meet requested deflection".into(), - )); - } - let mut counts = Vec::with_capacity(brep.topology.edges.len()); - for edge in &brep.topology.edges { - counts.push(edge_count(brep, edge.id, error, max_triangles)?); - } - let mut grids = Vec::with_capacity(brep.topology.faces.len()); - for face in &brep.topology.faces { - let surface = brep.geometry.surface(face.surface)?; - if matches!(surface, SurfaceGeometry::Plane { .. }) { - grids.push(None); - continue; - } - let rect = match rectangle(brep, face) { - Ok(rect) => Some(rect), - Err(GeometryError::UnsupportedGeometry(_)) => None, - Err(error) => return Err(error), - }; - let desired = grid_size(surface, face.trim.uv_bounds, error, max_triangles)?; - if let Some(rect) = &rect { - for (i, &h) in rect.halfedges.iter().enumerate() { - let id = brep.topology.halfedges[h as usize].edge; - counts[id as usize] = counts[id as usize].max(desired[i % 2]); - } - } - grids.push(Some((rect, desired))); - } - // Opposite chart edges and concentric trim curves must share subdivision counts. - synchronize_edge_samples(brep, &grids, &mut counts); - let sample_count = counts - .iter() - .try_fold(0usize, |total, &n| total.checked_add(n + 1)) - .ok_or_else(|| GeometryError::LimitExceeded("edge samples".into()))?; - if sample_count > max_triangles * 3 { - return Err(GeometryError::LimitExceeded("edge sample budget".into())); - } - let mut samples = Vec::with_capacity(counts.len()); - for (id, n) in counts.into_iter().enumerate() { - samples.push(sample_edge(brep, id as u32, n)?); - } - let mut mesh = Tessellation::new(brep.revision, deflection); - for (face, grid) in brep.topology.faces.iter().zip(&grids) { - let before = mesh.triangle_face_ids.len(); - match grid { - Some((Some(rect), desired)) if face.trim.holes.is_empty() => curved_face( - brep, - face, - rect, - *desired, - &samples, - &mut mesh, - max_triangles, - )?, - Some((rect, desired)) => curved_trimmed_face( - brep, - face, - rect.as_ref(), - *desired, - &samples, - error, - &mut mesh, - max_triangles, - )?, - None => planar_face(brep, face, &samples, &mut mesh, max_triangles)?, - } - if mesh.triangle_face_ids.len() == before { - return Err(GeometryError::UnresolvedTessellation(format!( - "face {} produced no nondegenerate tessellation triangles", - face.id - ))); - } - } - for (edge, points) in brep.topology.edges.iter().zip(samples) { - let planar_subdivision = edge - .twin_halfedge - .and_then(|twin| { - let first = &brep.topology.halfedges[edge.halfedge as usize]; - let second = &brep.topology.halfedges[twin as usize]; - let a = &brep.topology.faces[first.face? as usize]; - let b = &brep.topology.faces[second.face? as usize]; - match ( - &brep.geometry.surfaces[a.surface as usize], - &brep.geometry.surfaces[b.surface as usize], - ) { - ( - SurfaceGeometry::Plane { frame: a_frame }, - SurfaceGeometry::Plane { frame: b_frame }, - ) => Some( - scale(a_frame.z, a.sense.multiplier()) - == scale(b_frame.z, b.sense.multiplier()), - ), - _ => Some(false), - } - }) - .unwrap_or(false); - if edge.chart_seam || planar_subdivision { - continue; - } - for pair in points.windows(2) { - mesh.outline_positions.extend(pair[0]); - mesh.outline_positions.extend(pair[1]); - mesh.outline_edge_ids.push(edge.id); - } - } - Ok(mesh) -} - -struct HashWriter(DefaultHasher); -impl Write for HashWriter { - fn write(&mut self, buf: &[u8]) -> std::io::Result { - self.0.write(buf); - Ok(buf.len()) - } - fn flush(&mut self) -> std::io::Result<()> { - Ok(()) - } -} -struct CacheEntry { - body: u64, - signature: u64, - deflection: u64, - max_triangles: usize, - mesh: Arc, -} -pub struct TessellationCache { - entries: VecDeque, - max_bytes: usize, -} -impl TessellationCache { - pub fn new(max_bytes: usize) -> Self { - Self { - entries: VecDeque::new(), - max_bytes, - } - } - pub fn tessellate( - &mut self, - brep: &BrepEnvelope, - deflection: f64, - max_triangles: usize, - ) -> Result, GeometryError> { - brep.validate()?; - let mut writer = HashWriter(DefaultHasher::new()); - serde_json::to_writer(&mut writer, brep) - .map_err(|e| GeometryError::InvalidGeometry(e.to_string()))?; - let signature = writer.0.finish(); - if let Some(i) = self.entries.iter().position(|e| { - e.signature == signature - && e.deflection == deflection.to_bits() - && e.max_triangles == max_triangles - }) { - let entry = self - .entries - .remove(i) - .ok_or_else(|| GeometryError::InvalidGeometry("cache entry missing".into()))?; - let mesh = Arc::clone(&entry.mesh); - self.entries.push_back(entry); - return Ok(mesh); - } - let mesh = Arc::new(tessellate(brep, deflection, max_triangles)?); - if mesh.bytes() > self.max_bytes { - return Ok(mesh); - } - let mut hasher = DefaultHasher::new(); - hasher.write(brep.id.as_bytes()); - let body = hasher.finish(); - while self.entries.iter().filter(|e| e.body == body).count() >= 3 { - if let Some(i) = self.entries.iter().position(|e| e.body == body) { - self.entries.remove(i); - } else { - break; - } - } - while self.entries.iter().map(|e| e.mesh.bytes()).sum::() + mesh.bytes() - > self.max_bytes - { - self.entries.pop_front(); - } - self.entries.push_back(CacheEntry { - body, - signature, - deflection: deflection.to_bits(), - max_triangles, - mesh: Arc::clone(&mesh), - }); - Ok(mesh) - } - pub fn len(&self) -> usize { - self.entries.len() - } - pub fn is_empty(&self) -> bool { - self.entries.is_empty() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::{primitives, topology::Accuracy, Frame3}; - use std::collections::HashMap; - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-9, - intersection: 1e-10, - tessellation: 1e-3, - exchange: 1e-5, - } - } - fn shapes() -> Vec { - vec![ - primitives::cylinder("c".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy()).unwrap(), - primitives::cone("k".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy()).unwrap(), - primitives::frustum("f".into(), Frame3::IDENTITY, 2.0, 1.0, 3.0, accuracy()).unwrap(), - primitives::sphere("s".into(), Frame3::IDENTITY, 1.0, accuracy()).unwrap(), - primitives::torus("t".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy()).unwrap(), - primitives::annular_sector_extrusion( - "annular-sector".into(), - Frame3::IDENTITY, - 3.0, - 0.4, - 2.5, - 0.7, - -1.8, - accuracy(), - ) - .unwrap(), - ] - } - #[test] - fn conservative_curve_subdivision_respects_requested_error() { - let mut store = crate::analytic::topology::GeometryStore::new(); - store.curves.push(CurveGeometry::Circle { - frame: Frame3::from_axis([1.0, 2.0, 3.0], [1.0, 1.0, 1.0], [1.0, -1.0, 0.0]).unwrap(), - radius: 2.0, - }); - let curve = store.curve(0).unwrap(); - let range = Interval::new(0.0, std::f64::consts::TAU).unwrap(); - let loose = bounded_curve_count(&curve, range, 0.2, 1_000_000).unwrap(); - let fine = bounded_curve_count(&curve, range, 0.02, 1_000_000).unwrap(); - assert!(fine > loose); - for index in 0..fine { - let interval = Interval::new( - range.lo + range.width() * index as f64 / fine as f64, - range.lo + range.width() * (index + 1) as f64 / fine as f64, - ) - .unwrap(); - assert!( - bounds_chord_deviation( - curve.enclose(interval).unwrap(), - curve.point_at(interval.lo).unwrap(), - curve.point_at(interval.hi).unwrap(), - ) <= 0.02 - ); - } - } - #[test] - fn lod_changes_mesh_not_topology_and_retains_face_ids() { - for b in shapes() { - let original = b.to_json().unwrap(); - let coarse = tessellate(&b, 0.1, 1_000_000).unwrap(); - let fine = tessellate(&b, 0.02, 1_000_000).unwrap(); - assert!(fine.indices.len() > coarse.indices.len()); - assert_eq!(fine.indices.len() / 3, fine.triangle_face_ids.len()); - assert!(fine - .triangle_face_ids - .iter() - .all(|&f| (f as usize) < b.topology.faces.len())); - assert_eq!(b.to_json().unwrap(), original); - for n in fine.normals.chunks_exact(3) { - assert!((norm([n[0] as f64, n[1] as f64, n[2] as f64]) - 1.0).abs() < 1e-6); - } - } - } - #[test] - fn shared_boundaries_make_a_closed_geometric_mesh() { - for b in shapes() { - let mesh = tessellate(&b, 0.05, 1_000_000).unwrap(); - let key = |p: Point3| p.map(f64::to_bits); - let mut edges = HashMap::<([u64; 3], [u64; 3]), usize>::new(); - for t in mesh.indices.chunks_exact(3) { - for (a, c) in [(t[0], t[1]), (t[1], t[2]), (t[2], t[0])] { - let mut pair = [key(mesh.point(a)), key(mesh.point(c))]; - pair.sort(); - *edges.entry((pair[0], pair[1])).or_default() += 1; - } - } - assert!( - edges.values().all(|&count| count == 2), - "{} has unmatched geometric mesh edges", - b.id - ); - } - } - #[test] - fn spherical_triangle_samples_obey_deflection() { - let b = primitives::sphere("s".into(), Frame3::IDENTITY, 1.0, accuracy()).unwrap(); - let deflection = 0.05; - let mesh = tessellate(&b, deflection, 1_000_000).unwrap(); - for ids in mesh.indices.chunks_exact(3) { - let p = ids - .iter() - .map(|&id| mesh.point(id)) - .fold([0.0; 3], super::super::geometry::add); - assert!(1.0 - norm(scale(p, 1.0 / 3.0)) <= deflection); - } - } - #[test] - fn periodic_chart_lifts_keep_shared_positions_and_hide_seams() { - let b = - primitives::cylinder("lift".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy()).unwrap(); - let original = tessellate(&b, 0.05, 1_000_000).unwrap(); - let mut lifted = b; - for h in &mut lifted.topology.halfedges { - if h.face == Some(0) { - h.geometry_use.periodic_lift = [1, 0]; - } - } - let tau = std::f64::consts::TAU; - lifted.topology.faces[0].trim.uv_bounds[0] = Interval::new(tau, 2.0 * tau).unwrap(); - let mesh = tessellate(&lifted, 0.05, 1_000_000).unwrap(); - assert_eq!(original.positions, mesh.positions); - assert_eq!(original.outline_positions, mesh.outline_positions); - assert_eq!(original.outline_edge_ids, mesh.outline_edge_ids); - } - #[test] - fn thin_annular_sector_extrusion_keeps_its_footprint_at_coarse_deflection() { - let sweep = 7.0 * (0.2_f64 / 3.0).sqrt(); - let thickness = 0.0001; - let b = primitives::annular_sector_extrusion( - "thin".into(), - Frame3::IDENTITY, - 3.0, - thickness, - 2.0, - 0.7, - sweep, - accuracy(), - ) - .unwrap(); - let mesh = tessellate(&b, 0.05, 1_000_000).unwrap(); - let mut area = 0.0; - for (ids, &face) in mesh.indices.chunks_exact(3).zip(&mesh.triangle_face_ids) { - if face != 3 { - continue; - } - let [a, c, d] = [mesh.point(ids[0]), mesh.point(ids[1]), mesh.point(ids[2])]; - area += norm(cross(sub(c, a), sub(d, a))) * 0.5; - } - assert!((area / (3.0 * thickness * sweep) - 1.0).abs() < 0.1); - } - #[test] - fn planar_tessellation_preserves_a_circular_hole() { - let mut b = crate::analytic::topology::tests::circular_face(); - b.geometry.curves[0] = CurveGeometry::Circle { - frame: Frame3::IDENTITY, - radius: 2.0, - }; - b.geometry.pcurves[0] = crate::analytic::topology::PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [2.0, 0.0], - axis_b: [0.0, 2.0], - }; - b.topology.vertices[0].position = [2.0, 0.0, 0.0]; - b.topology.faces[0].trim.uv_bounds = [Interval::new(-2.0, 2.0).unwrap(); 2]; - b.topology.faces[0].trim.holes.push(1); - b.geometry.curves.push(CurveGeometry::Circle { - frame: Frame3::IDENTITY, - radius: 1.0, - }); - b.geometry - .pcurves - .push(crate::analytic::topology::PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [1.0, 0.0], - axis_b: [0.0, 1.0], - }); - let mut vertex = b.topology.vertices[0].clone(); - vertex.id = 1; - vertex.position = [1.0, 0.0, 0.0]; - vertex.outgoing_halfedge = Some(1); - b.topology.vertices.push(vertex); - let mut edge = b.topology.edges[0].clone(); - edge.id = 1; - edge.halfedge = 1; - edge.geometry = EdgeGeometry::Curve { - curve: 1, - range: Interval::new(0.0, std::f64::consts::TAU).unwrap(), - }; - b.topology.edges.push(edge); - let mut h = b.topology.halfedges[0].clone(); - h.id = 1; - h.from = 1; - h.to = 1; - h.next = Some(1); - h.prev = Some(1); - h.edge = 1; - h.loop_ref = Some(1); - h.geometry_use.pcurve = Some(1); - h.geometry_use.sense = Orientation::Reverse; - b.topology.halfedges.push(h); - b.topology.loops.push(crate::analytic::topology::Loop { - id: 1, - start_halfedge: 1, - face_ref: 0, - is_hole: true, - }); - let deflection = 0.02; - let mesh = tessellate(&b, deflection, 1_000_000).unwrap(); - let mut area = 0.0; - for ids in mesh.indices.chunks_exact(3) { - let [a, c, d] = [mesh.point(ids[0]), mesh.point(ids[1]), mesh.point(ids[2])]; - let center = scale( - crate::analytic::geometry::add(crate::analytic::geometry::add(a, c), d), - 1.0 / 3.0, - ); - assert!(center[0].hypot(center[1]) >= 1.0 - deflection); - area += norm(cross(sub(c, a), sub(d, a))) * 0.5; - } - assert!( - (area - 3.0 * std::f64::consts::PI).abs() < 6.0 * std::f64::consts::PI * deflection - ); - } - #[test] - fn precision_limits_do_not_claim_unachievable_deflection() { - let near_cylinder = primitives::frustum( - "near".into(), - Frame3::IDENTITY, - 1.0, - 1.0 + 2.0_f64.powi(-48), - 1.0, - accuracy(), - ) - .unwrap(); - assert!( - matches!(tessellate(&near_cylinder, 0.01, 1_000_000), Err(GeometryError::LimitExceeded(message)) if message.contains("coordinate precision")) - ); - let frame = Frame3 { - origin: [1e12, 0.0, 0.0], - ..Frame3::IDENTITY - }; - let b = primitives::sphere( - "far".into(), - frame, - 1.0, - Accuracy { - geometric: 0.001, - intersection: 0.0001, - ..accuracy() - }, - ) - .unwrap(); - assert!( - matches!(tessellate(&b, 0.01, 1_000_000), Err(GeometryError::LimitExceeded(message)) if message.contains("coordinate precision")) - ); - let mut cache = TessellationCache::new(16_000_000); - let coarse = cache.tessellate(&b, 0.1, 1_000_000).unwrap(); - assert!(cache.tessellate(&b, 0.01, 1_000_000).is_err()); - assert!(Arc::ptr_eq( - &coarse, - &cache.tessellate(&b, 0.1, 1_000_000).unwrap() - )); - } - #[test] - fn extreme_scales_return_errors_instead_of_invalid_or_empty_meshes() { - for (radius, geometric, deflection) in [(1e300, 1e290, 1e299), (1e-200, 1e-210, 1e-201)] { - let b = primitives::sphere( - "extreme".into(), - Frame3::IDENTITY, - radius, - Accuracy { - geometric, - intersection: geometric / 4.0, - ..accuracy() - }, - ) - .unwrap(); - assert!(matches!( - tessellate(&b, deflection, 1_000_000), - Err(GeometryError::UnresolvedTessellation(_)) - )); - } - } - #[test] - fn cache_tracks_geometry_even_when_revision_is_not_incremented() { - let mut b = - primitives::cylinder("c".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy()).unwrap(); - let mut cache = TessellationCache::new(16_000_000); - let a = cache.tessellate(&b, 0.1, 1_000_000).unwrap(); - let again = cache.tessellate(&b, 0.1, 1_000_000).unwrap(); - assert!(Arc::ptr_eq(&a, &again)); - b.topology.faces[0].key = "changed".into(); - let changed = cache.tessellate(&b, 0.1, 1_000_000).unwrap(); - assert!(!Arc::ptr_eq(&a, &changed)); - for d in [0.08, 0.06, 0.04, 0.02] { - cache.tessellate(&b, d, 1_000_000).unwrap(); - } - assert_eq!(cache.len(), 3); - assert!(tessellate(&b, 0.001, 1).is_err()); - } -} diff --git a/main/opengeometry/src/analytic/topology.rs b/main/opengeometry/src/analytic/topology.rs deleted file mode 100644 index 6d5bdf3..0000000 --- a/main/opengeometry/src/analytic/topology.rs +++ /dev/null @@ -1,1381 +0,0 @@ -use super::{ - geometry::{norm, sub, PatchBounds, Surface, UVBox}, - intersection::IntersectionDefinition, - Curve, CurveGeometry, GeometryError, Point3, SurfaceGeometry, UV, -}; -use crate::math::{finite, interval::Interval}; -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; - -pub const SCHEMA_VERSION: u32 = 2; - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub enum Orientation { - Forward, - Reverse, -} -impl Orientation { - pub fn multiplier(self) -> f64 { - if self == Self::Forward { - 1.0 - } else { - -1.0 - } - } -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Accuracy { - pub geometric: f64, - pub intersection: f64, - pub tessellation: f64, - pub exchange: f64, -} -impl Accuracy { - pub fn validate(self) -> Result<(), GeometryError> { - for value in [ - self.geometric, - self.intersection, - self.tessellation, - self.exchange, - ] { - if !value.is_finite() || value <= 0.0 { - return Err(GeometryError::InvalidGeometry( - "accuracy budgets must be positive and finite".into(), - )); - } - } - if self.intersection > self.geometric { - return Err(GeometryError::InvalidGeometry( - "intersection budget exceeds geometric tolerance".into(), - )); - } - Ok(()) - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(tag = "kind", deny_unknown_fields)] -pub enum GeometryQuality { - Analytic, - Approximate { max_error: f64 }, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct FaceSource { - pub entity: String, - pub body: String, - pub key: String, - pub face: u32, -} -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub enum FaceRole { - Authored, - Preserved, - Split, - Cut, - Coincident, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct FaceProvenance { - pub sources: Vec, - pub role: FaceRole, - pub reversed: bool, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(tag = "kind", deny_unknown_fields)] -pub enum PcurveGeometry { - Line2 { - origin: UV, - direction: UV, - }, - Conic2 { - origin: UV, - axis_a: UV, - axis_b: UV, - }, - ProjectedCurve { - curve: u32, - surface: u32, - chart: u32, - uv_hint: UV, - uv_rate: UV, - parameter_origin: f64, - }, - IntersectionSide { - definition: u32, - side: IntersectionSide, - }, -} -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub enum IntersectionSide { - A, - B, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct GeometryStore { - pub surfaces: Vec, - pub curves: Vec, - pub pcurves: Vec, - pub intersections: Vec, -} -impl GeometryStore { - pub fn new() -> Self { - Self { - surfaces: Vec::new(), - curves: Vec::new(), - pcurves: Vec::new(), - intersections: Vec::new(), - } - } - pub fn surface(&self, id: u32) -> Result<&SurfaceGeometry, GeometryError> { - self.surfaces - .get(id as usize) - .ok_or_else(|| missing("surface", id)) - } - pub fn curve(&self, id: u32) -> Result, GeometryError> { - let geometry = self - .curves - .get(id as usize) - .ok_or_else(|| missing("curve", id))?; - Ok(CurveView { - geometry, - store: self, - }) - } - pub fn pcurve_at(&self, id: u32, t: f64) -> Result { - finite(t)?; - let uv = match self - .pcurves - .get(id as usize) - .ok_or_else(|| missing("pcurve", id))? - { - PcurveGeometry::Line2 { origin, direction } => { - std::array::from_fn(|i| origin[i] + direction[i] * t) - } - PcurveGeometry::Conic2 { - origin, - axis_a, - axis_b, - } => std::array::from_fn(|i| origin[i] + axis_a[i] * t.cos() + axis_b[i] * t.sin()), - PcurveGeometry::ProjectedCurve { - curve, - surface, - chart, - uv_hint, - uv_rate, - parameter_origin, - } => { - let surface = self.surface(*surface)?; - if *chart as usize >= surface.charts().len() { - return Err(missing("chart", *chart)); - } - let hint = - std::array::from_fn(|i| uv_hint[i] + uv_rate[i] * (t - parameter_origin)); - surface.project(self.curve(*curve)?.point_at(t)?, Some(hint))? - } - PcurveGeometry::IntersectionSide { definition, side } => { - let point = self - .intersections - .get(*definition as usize) - .ok_or_else(|| missing("intersection", *definition))? - .evaluate(t, self)?; - if *side == IntersectionSide::A { - point.uv_a - } else { - point.uv_b - } - } - }; - for v in uv { - finite(v)?; - } - Ok(uv) - } -} -impl Default for GeometryStore { - fn default() -> Self { - Self::new() - } -} - -pub struct CurveView<'a> { - pub geometry: &'a CurveGeometry, - pub store: &'a GeometryStore, -} -impl Curve for CurveView<'_> { - fn point_at(&self, t: f64) -> Result { - match self.geometry { - CurveGeometry::Intersection { definition } => Ok(self - .store - .intersections - .get(*definition as usize) - .ok_or_else(|| missing("intersection", *definition))? - .evaluate(t, self.store)? - .point), - curve => curve.elementary_point(t), - } - } - fn tangent_at(&self, t: f64) -> Result { - match self.geometry { - CurveGeometry::Intersection { definition } => Ok(self - .store - .intersections - .get(*definition as usize) - .ok_or_else(|| missing("intersection", *definition))? - .evaluate(t, self.store)? - .tangent), - curve => curve.elementary_tangent(t), - } - } - fn enclose(&self, range: Interval) -> Result { - match self.geometry { - CurveGeometry::Intersection { definition } => self - .store - .intersections - .get(*definition as usize) - .ok_or_else(|| missing("intersection", *definition))? - .enclose(range, self.store), - curve => curve.elementary_bounds(range), - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(tag = "kind", deny_unknown_fields)] -pub enum EdgeGeometry { - Curve { curve: u32, range: Interval }, - Collapsed { vertex: u32 }, -} -impl EdgeGeometry { - pub fn range(&self) -> Interval { - match self { - Self::Curve { range, .. } => *range, - Self::Collapsed { .. } => Interval { lo: 0.0, hi: 1.0 }, - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Vertex { - pub id: u32, - pub position: Point3, - pub outgoing_halfedge: Option, - pub tolerance: f64, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Edge { - pub id: u32, - pub geometry: EdgeGeometry, - pub halfedge: u32, - pub twin_halfedge: Option, - pub tolerance: f64, - pub chart_seam: bool, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct HalfEdgeGeometryUse { - pub sense: Orientation, - pub pcurve: Option, - pub periodic_lift: [i32; 2], -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct HalfEdge { - pub id: u32, - pub from: u32, - pub to: u32, - pub twin: Option, - pub next: Option, - pub prev: Option, - pub edge: u32, - pub face: Option, - pub loop_ref: Option, - pub wire_ref: Option, - pub geometry_use: HalfEdgeGeometryUse, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Loop { - pub id: u32, - pub start_halfedge: u32, - pub face_ref: u32, - pub is_hole: bool, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct TrimRegion { - pub chart: u32, - pub uv_bounds: UVBox, - pub outer: u32, - pub holes: Vec, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Face { - pub id: u32, - pub key: String, - pub surface: u32, - pub sense: Orientation, - pub trim: TrimRegion, - pub shell_ref: Option, - pub provenance: FaceProvenance, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Wire { - pub id: u32, - pub start_halfedge: u32, - pub is_closed: bool, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Shell { - pub id: u32, - pub faces: Vec, - pub is_closed: bool, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct SolidRegion { - pub outer_shell: u32, - pub cavity_shells: Vec, -} -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Topology { - pub vertices: Vec, - pub edges: Vec, - pub halfedges: Vec, - pub loops: Vec, - pub faces: Vec, - pub wires: Vec, - pub shells: Vec, -} -impl Topology { - pub fn new() -> Self { - Self { - vertices: Vec::new(), - edges: Vec::new(), - halfedges: Vec::new(), - loops: Vec::new(), - faces: Vec::new(), - wires: Vec::new(), - shells: Vec::new(), - } - } -} -impl Default for Topology { - fn default() -> Self { - Self::new() - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct BrepEnvelope { - pub schema_version: u32, - pub id: String, - pub revision: u64, - pub geometry: GeometryStore, - pub topology: Topology, - pub solids: Vec, - pub accuracy: Accuracy, - pub quality: GeometryQuality, -} - -fn missing(kind: &str, index: u32) -> GeometryError { - GeometryError::MissingReference { - kind: kind.into(), - index, - } -} -fn invalid(message: impl Into) -> GeometryError { - GeometryError::InvalidTopology(message.into()) -} -fn positive(value: f64) -> Result<(), GeometryError> { - if !value.is_finite() || value <= 0.0 { - return Err(GeometryError::InvalidGeometry( - "entity tolerance must be finite and positive".into(), - )); - } - Ok(()) -} - -impl BrepEnvelope { - pub fn face_normal_at(&self, face_id: u32, point: Point3) -> Result { - let face = self - .topology - .faces - .get(face_id as usize) - .ok_or_else(|| missing("face", face_id))?; - let surface = self.geometry.surface(face.surface)?; - let uv = surface.project(point, None)?; - Ok(super::geometry::unit(surface.normal_at(uv)?)?.map(|v| v * face.sense.multiplier())) - } - - pub fn new(id: String, accuracy: Accuracy) -> Result { - accuracy.validate()?; - Ok(Self { - schema_version: SCHEMA_VERSION, - id, - revision: 0, - geometry: GeometryStore::new(), - topology: Topology::new(), - solids: Vec::new(), - accuracy, - quality: GeometryQuality::Analytic, - }) - } - - pub fn from_json(json: &str) -> Result { - if json.len() > 64 * 1024 * 1024 { - return Err(GeometryError::LimitExceeded( - "BRep JSON exceeds 64 MiB".into(), - )); - } - let brep: Self = serde_json::from_str(json) - .map_err(|e| GeometryError::InvalidGeometry(format!("invalid BRep v2 JSON: {e}")))?; - brep.validate()?; - Ok(brep) - } - - pub fn to_json(&self) -> Result { - self.validate()?; - serde_json::to_string(self).map_err(|e| GeometryError::InvalidGeometry(e.to_string())) - } - - pub fn bounds(&self) -> Result, GeometryError> { - self.validate()?; - self.bounds_unchecked() - } - - pub(crate) fn bounds_unchecked(&self) -> Result, GeometryError> { - let mut result: Option = None; - let mut include = |bounds: PatchBounds| { - result = Some(match result { - Some(current) => PatchBounds { - axes: std::array::from_fn(|i| current.axes[i].hull(bounds.axes[i])), - }, - None => bounds, - }); - }; - for face in &self.topology.faces { - include( - self.geometry - .surface(face.surface)? - .enclose(face.trim.uv_bounds)?, - ); - } - for edge in &self.topology.edges { - if let EdgeGeometry::Curve { curve, range } = edge.geometry { - include(self.geometry.curve(curve)?.enclose(range)?); - } - } - for vertex in &self.topology.vertices { - include(PatchBounds { - axes: [ - Interval::point(vertex.position[0])?, - Interval::point(vertex.position[1])?, - Interval::point(vertex.position[2])?, - ], - }); - } - Ok(result) - } - - pub fn validate(&self) -> Result<(), GeometryError> { - if self.schema_version != SCHEMA_VERSION { - return Err(GeometryError::UnsupportedSchema { - found: self.schema_version, - }); - } - if self.id.is_empty() { - return Err(invalid("BRep identity is empty")); - } - self.accuracy.validate()?; - if let GeometryQuality::Approximate { max_error } = self.quality { - positive(max_error)?; - } - for s in &self.geometry.surfaces { - s.validate()?; - } - for c in &self.geometry.curves { - c.validate()?; - if let CurveGeometry::Intersection { definition } = c { - self.geometry - .intersections - .get(*definition as usize) - .ok_or_else(|| missing("intersection", *definition))?; - } - } - for definition in &self.geometry.intersections { - if definition.residual_tolerance > self.accuracy.intersection { - return Err(invalid( - "intersection residual budget exceeds model accuracy", - )); - } - definition.validate(&self.geometry)?; - } - for pcurve in &self.geometry.pcurves { - match pcurve { - PcurveGeometry::Line2 { origin, direction } => { - for v in origin.iter().chain(direction) { - finite(*v)?; - } - } - PcurveGeometry::Conic2 { - origin, - axis_a, - axis_b, - } => { - for v in origin.iter().chain(axis_a).chain(axis_b) { - finite(*v)?; - } - } - PcurveGeometry::ProjectedCurve { - curve, - surface, - chart, - uv_hint, - uv_rate, - parameter_origin, - } => { - self.geometry.curve(*curve)?; - let surface = self.geometry.surface(*surface)?; - if *chart as usize >= surface.charts().len() { - return Err(missing("chart", *chart)); - } - for v in uv_hint - .iter() - .chain(uv_rate) - .chain(std::iter::once(parameter_origin)) - { - finite(*v)?; - } - } - PcurveGeometry::IntersectionSide { definition, .. } => { - self.geometry - .intersections - .get(*definition as usize) - .ok_or_else(|| missing("intersection", *definition))?; - } - } - } - let t = &self.topology; - for length in [ - t.vertices.len(), - t.edges.len(), - t.halfedges.len(), - t.loops.len(), - t.faces.len(), - t.wires.len(), - t.shells.len(), - self.geometry.surfaces.len(), - self.geometry.curves.len(), - self.geometry.pcurves.len(), - self.geometry.intersections.len(), - ] { - if length > 2_000_000 { - return Err(GeometryError::LimitExceeded( - "BRep table exceeds 2000000 records".into(), - )); - } - } - for face in &t.faces { - let surface = self.geometry.surface(face.surface)?; - if face.trim.chart as usize >= surface.charts().len() { - return Err(missing("chart", face.trim.chart)); - } - for d in face.trim.uv_bounds { - Interval::new(d.lo, d.hi)?; - if d.lo == d.hi { - return Err(invalid("face UV bounds must have positive area")); - } - } - } - for (i, v) in t.vertices.iter().enumerate() { - if v.id as usize != i { - return Err(invalid("vertex IDs must be dense")); - } - positive(v.tolerance)?; - if v.tolerance > self.accuracy.geometric { - return Err(invalid( - "vertex tolerance exceeds the model accuracy budget", - )); - } - for x in v.position { - finite(x)?; - } - if let Some(h) = v.outgoing_halfedge { - if t.halfedges - .get(h as usize) - .ok_or_else(|| missing("halfedge", h))? - .from - != v.id - { - return Err(invalid("outgoing halfedge starts at another vertex")); - } - } - } - let mut incident = vec![Vec::new(); t.edges.len()]; - for (i, h) in t.halfedges.iter().enumerate() { - if h.id as usize != i { - return Err(invalid("halfedge IDs must be dense")); - } - t.vertices - .get(h.from as usize) - .ok_or_else(|| missing("vertex", h.from))?; - t.vertices - .get(h.to as usize) - .ok_or_else(|| missing("vertex", h.to))?; - t.edges - .get(h.edge as usize) - .ok_or_else(|| missing("edge", h.edge))?; - incident[h.edge as usize].push(h.id); - if let Some(twin) = h.twin { - let twin = t - .halfedges - .get(twin as usize) - .ok_or_else(|| missing("halfedge", twin))?; - if twin.id == h.id - || twin.twin != Some(h.id) - || twin.edge != h.edge - || twin.from != h.to - || twin.to != h.from - || twin.geometry_use.sense == h.geometry_use.sense - { - return Err(invalid("inconsistent halfedge twins")); - } - } - if let Some(next) = h.next { - let n = t - .halfedges - .get(next as usize) - .ok_or_else(|| missing("halfedge", next))?; - if n.prev != Some(h.id) - || n.from != h.to - || n.face != h.face - || n.wire_ref != h.wire_ref - || n.loop_ref != h.loop_ref - { - return Err(invalid("inconsistent next link")); - } - } - if let Some(prev) = h.prev { - if t.halfedges - .get(prev as usize) - .ok_or_else(|| missing("halfedge", prev))? - .next - != Some(h.id) - { - return Err(invalid("inconsistent previous link")); - } - } - match (h.face, h.wire_ref) { - (Some(face), None) => { - t.faces - .get(face as usize) - .ok_or_else(|| missing("face", face))?; - let p = h - .geometry_use - .pcurve - .ok_or_else(|| invalid("face halfedge has no pcurve"))?; - self.geometry - .pcurves - .get(p as usize) - .ok_or_else(|| missing("pcurve", p))?; - if h.loop_ref.is_none() || h.next.is_none() || h.prev.is_none() { - return Err(invalid("face halfedge has incomplete loop links")); - } - } - (None, Some(wire)) => { - t.wires - .get(wire as usize) - .ok_or_else(|| missing("wire", wire))?; - if h.geometry_use.pcurve.is_some() || h.loop_ref.is_some() { - return Err(invalid("wire use carries face geometry")); - } - } - _ => return Err(invalid("halfedge must belong to one face or wire")), - } - } - for (i, e) in t.edges.iter().enumerate() { - if e.id as usize != i { - return Err(invalid("edge IDs must be dense")); - } - positive(e.tolerance)?; - if e.tolerance > self.accuracy.geometric { - return Err(invalid("edge tolerance exceeds the model accuracy budget")); - } - let list = &incident[i]; - if list.is_empty() || list.len() > 2 || !list.contains(&e.halfedge) { - return Err(invalid("invalid edge incidence")); - } - if list.len() == 2 { - let twin = e - .twin_halfedge - .ok_or_else(|| invalid("two-use edge lacks twin halfedge"))?; - if !list.contains(&twin) - || twin == e.halfedge - || t.halfedges[e.halfedge as usize].twin != Some(twin) - { - return Err(invalid("edge incidence does not match twins")); - } - } else if e.twin_halfedge.is_some() || t.halfedges[e.halfedge as usize].twin.is_some() { - return Err(invalid("one-use edge has an extra twin")); - } - let range = e.geometry.range(); - Interval::new(range.lo, range.hi)?; - if range.lo == range.hi { - return Err(invalid("regular curve range must be nonzero")); - } - let points = match e.geometry { - EdgeGeometry::Curve { curve, .. } => [ - self.geometry.curve(curve)?.point_at(range.lo)?, - self.geometry.curve(curve)?.point_at(range.hi)?, - ], - EdgeGeometry::Collapsed { vertex } => { - let p = t - .vertices - .get(vertex as usize) - .ok_or_else(|| missing("vertex", vertex))? - .position; - [p, p] - } - }; - for &id in list { - let h = &t.halfedges[id as usize]; - let endpoints = if h.geometry_use.sense == Orientation::Forward { - points - } else { - [points[1], points[0]] - }; - let tolerance = e - .tolerance - .max(t.vertices[h.from as usize].tolerance) - .max(t.vertices[h.to as usize].tolerance); - if norm(sub(endpoints[0], t.vertices[h.from as usize].position)) > tolerance - || norm(sub(endpoints[1], t.vertices[h.to as usize].position)) > tolerance - { - return Err(invalid("edge endpoints disagree with vertices")); - } - if h.from == h.to - && matches!(e.geometry, EdgeGeometry::Curve { .. }) - && norm(sub(points[0], points[1])) > tolerance - { - return Err(invalid("self-loop curve is not closed")); - } - if h.from == h.to - && matches!(e.geometry, EdgeGeometry::Curve { curve, .. } if matches!(self.geometry.curves[curve as usize], CurveGeometry::Line { .. })) - { - return Err(invalid("a regular line cannot form a closed edge")); - } - if matches!(e.geometry, EdgeGeometry::Collapsed { .. }) - && (h.from != h.to || h.face.is_none()) - { - return Err(invalid("collapsed edge must be a singular face use")); - } - if let Some(face) = h.face { - self.validate_face_use(h, &t.faces[face as usize], e, tolerance)?; - } - } - } - let mut loop_uses = HashSet::new(); - for (i, l) in t.loops.iter().enumerate() { - if l.id as usize != i { - return Err(invalid("loop IDs must be dense")); - } - let face = t - .faces - .get(l.face_ref as usize) - .ok_or_else(|| missing("face", l.face_ref))?; - if (face.trim.outer == l.id && l.is_hole) - || (!face.trim.holes.contains(&l.id) && face.trim.outer != l.id) - || face.trim.holes.contains(&l.id) && !l.is_hole - { - return Err(invalid("loop face membership or hole flag is inconsistent")); - } - let mut current = l.start_halfedge; - let mut seen = HashSet::new(); - loop { - let h = t - .halfedges - .get(current as usize) - .ok_or_else(|| missing("halfedge", current))?; - if h.loop_ref != Some(l.id) - || h.face != Some(face.id) - || !seen.insert(current) - || !loop_uses.insert(current) - { - return Err(invalid("loop has inconsistent or repeated uses")); - } - let next = h.next.ok_or_else(|| invalid("open face loop"))?; - self.validate_uv_join(h, &t.halfedges[next as usize], face)?; - if next == l.start_halfedge { - break; - } - current = next; - if seen.len() > t.halfedges.len() { - return Err(invalid("nonterminating loop")); - } - } - } - let mut face_keys = HashSet::new(); - for (i, f) in t.faces.iter().enumerate() { - if f.id as usize != i || f.key.is_empty() || !face_keys.insert(&f.key) { - return Err(invalid("face IDs or keys are invalid")); - } - let surface = self.geometry.surface(f.surface)?; - if f.trim.chart as usize >= surface.charts().len() { - return Err(missing("chart", f.trim.chart)); - } - for d in f.trim.uv_bounds { - Interval::new(d.lo, d.hi)?; - } - t.loops - .get(f.trim.outer as usize) - .filter(|l| l.face_ref == f.id && !l.is_hole) - .ok_or_else(|| invalid("face outer loop is invalid"))?; - let mut holes = HashSet::new(); - for &hole in &f.trim.holes { - if !holes.insert(hole) || hole == f.trim.outer { - return Err(invalid("duplicate face hole")); - } - t.loops - .get(hole as usize) - .filter(|l| l.face_ref == f.id && l.is_hole) - .ok_or_else(|| invalid("face hole is invalid"))?; - } - if f.provenance.sources.is_empty() - || f.provenance - .sources - .iter() - .any(|s| s.key.is_empty() || s.entity.is_empty() || s.body.is_empty()) - { - return Err(invalid("face provenance is incomplete")); - } - if let Some(shell) = f.shell_ref { - if !t - .shells - .get(shell as usize) - .ok_or_else(|| missing("shell", shell))? - .faces - .contains(&f.id) - { - return Err(invalid("face shell membership is inconsistent")); - } - } - } - if t.halfedges - .iter() - .any(|h| h.face.is_some() && !loop_uses.contains(&h.id)) - { - return Err(invalid("orphan face halfedge")); - } - let mut wire_uses = HashSet::new(); - for (i, w) in t.wires.iter().enumerate() { - if w.id as usize != i { - return Err(invalid("wire IDs must be dense")); - } - let mut current = w.start_halfedge; - let mut seen = HashSet::new(); - loop { - let h = t - .halfedges - .get(current as usize) - .ok_or_else(|| missing("halfedge", current))?; - if h.wire_ref != Some(w.id) || !seen.insert(current) || !wire_uses.insert(current) { - return Err(invalid("invalid wire membership")); - } - match h.next { - Some(next) if next == w.start_halfedge => { - if !w.is_closed { - return Err(invalid("open wire forms a cycle")); - } - break; - } - Some(next) => current = next, - None => { - if w.is_closed { - return Err(invalid("closed wire is open")); - } - break; - } - } - } - } - if t.halfedges - .iter() - .any(|h| h.wire_ref.is_some() && !wire_uses.contains(&h.id)) - { - return Err(invalid("orphan wire halfedge")); - } - for (i, s) in t.shells.iter().enumerate() { - if s.id as usize != i || s.faces.is_empty() { - return Err(invalid("invalid shell IDs or empty shell")); - } - let mut seen = HashSet::new(); - for &face in &s.faces { - let f = t - .faces - .get(face as usize) - .ok_or_else(|| missing("face", face))?; - if f.shell_ref != Some(s.id) || !seen.insert(face) { - return Err(invalid("inconsistent shell membership")); - } - } - if s.is_closed { - for h in t - .halfedges - .iter() - .filter(|h| h.face.is_some_and(|f| seen.contains(&f))) - { - if matches!( - t.edges[h.edge as usize].geometry, - EdgeGeometry::Collapsed { .. } - ) { - continue; - } - let twin = h - .twin - .ok_or_else(|| invalid("closed shell has a free edge"))?; - if !t.halfedges[twin as usize] - .face - .is_some_and(|f| seen.contains(&f)) - { - return Err(invalid("shell twin belongs to another shell")); - } - } - } - } - let mut shells = HashSet::new(); - for solid in &self.solids { - for shell in std::iter::once(&solid.outer_shell).chain(&solid.cavity_shells) { - if !shells.insert(*shell) - || !t - .shells - .get(*shell as usize) - .ok_or_else(|| missing("shell", *shell))? - .is_closed - { - return Err(invalid("solid shell is open or multiply assigned")); - } - } - } - if t.shells - .iter() - .any(|s| s.is_closed && !shells.contains(&s.id)) - { - return Err(invalid("closed shell has no solid membership")); - } - Ok(()) - } - - fn validate_face_use( - &self, - h: &HalfEdge, - face: &Face, - edge: &Edge, - tolerance: f64, - ) -> Result<(), GeometryError> { - let surface = self.geometry.surface(face.surface)?; - let pcurve = h - .geometry_use - .pcurve - .ok_or_else(|| invalid("missing face pcurve"))?; - match &self.geometry.pcurves[pcurve as usize] { - PcurveGeometry::ProjectedCurve { - curve, - surface: s, - chart, - .. - } => { - if *s != face.surface - || *chart != face.trim.chart - || !matches!(edge.geometry,EdgeGeometry::Curve{curve:c,..}if c==*curve) - { - return Err(invalid("projected pcurve references another edge or face")); - } - } - PcurveGeometry::IntersectionSide { definition, side } => { - let d = &self.geometry.intersections[*definition as usize]; - let s = d.surfaces[if *side == IntersectionSide::A { 0 } else { 1 }]; - if s != face.surface - || !matches!(edge.geometry,EdgeGeometry::Curve{curve,..}if matches!(self.geometry.curves[curve as usize],CurveGeometry::Intersection{definition:df}if df==*definition)) - { - return Err(invalid( - "intersection pcurve references another face or curve", - )); - } - } - _ => {} - } - let range = edge.geometry.range(); - for i in 0..=8 { - let t = (range.lo + (range.hi - range.lo) * i as f64 / 8.0).clamp(range.lo, range.hi); - let mut uv = self.geometry.pcurve_at(pcurve, t)?; - let chart = &surface.charts()[face.trim.chart as usize]; - for j in 0..2 { - if let Some(period) = chart.periods[j] { - uv[j] += h.geometry_use.periodic_lift[j] as f64 * period; - } else if h.geometry_use.periodic_lift[j] != 0 { - return Err(invalid("lift on nonperiodic chart axis")); - } - } - let p = surface.point_at(uv)?; - let expected = match edge.geometry { - EdgeGeometry::Curve { curve, .. } => self.geometry.curve(curve)?.point_at(t)?, - EdgeGeometry::Collapsed { vertex } => { - self.topology.vertices[vertex as usize].position - } - }; - if norm(sub(p, expected)) > tolerance { - return Err(invalid("pcurve does not lie on edge support")); - } - if !face.trim.uv_bounds[0].contains(uv[0]) || !face.trim.uv_bounds[1].contains(uv[1]) { - let clipped = std::array::from_fn(|axis| { - uv[axis].clamp(face.trim.uv_bounds[axis].lo, face.trim.uv_bounds[axis].hi) - }); - let jet = surface.derivatives(clipped)?; - for axis in 0..2 { - let excess = (uv[axis] - clipped[axis]).abs(); - let metric = norm(if axis == 0 { jet.du } else { jet.dv }); - let roundoff = - 128.0 * f64::EPSILON * uv[axis].abs().max(clipped[axis].abs()).max(1.0); - let allowed = roundoff - + if metric > 0.0 { - tolerance / metric - } else { - 0.0 - }; - if excess > allowed { - return Err(invalid(format!( - "pcurve on halfedge {} leaves face {} UV bounds at [{}, {}]", - h.id, face.id, uv[0], uv[1] - ))); - } - } - if norm(sub(surface.point_at(clipped)?, p)) > tolerance { - return Err(invalid(format!( - "pcurve on halfedge {} leaves face {} UV bounds at [{}, {}]", - h.id, face.id, uv[0], uv[1] - ))); - } - } - if matches!(edge.geometry, EdgeGeometry::Collapsed { .. }) { - let singular = match surface { - SurfaceGeometry::Sphere { .. } => uv[1].abs() == std::f64::consts::FRAC_PI_2, - SurfaceGeometry::Cone { .. } => uv[1] == 0.0, - _ => false, - }; - if !singular { - return Err(invalid("collapsed edge is not at a surface singularity")); - } - } - } - Ok(()) - } - - fn validate_uv_join( - &self, - a: &HalfEdge, - b: &HalfEdge, - face: &Face, - ) -> Result<(), GeometryError> { - let range_a = self.topology.edges[a.edge as usize].geometry.range(); - let range_b = self.topology.edges[b.edge as usize].geometry.range(); - let ta = if a.geometry_use.sense == Orientation::Forward { - range_a.hi - } else { - range_a.lo - }; - let tb = if b.geometry_use.sense == Orientation::Forward { - range_b.lo - } else { - range_b.hi - }; - let ua = self.geometry.pcurve_at( - a.geometry_use - .pcurve - .ok_or_else(|| invalid("missing pcurve"))?, - ta, - )?; - let ub = self.geometry.pcurve_at( - b.geometry_use - .pcurve - .ok_or_else(|| invalid("missing pcurve"))?, - tb, - )?; - let surface = self.geometry.surface(face.surface)?; - let chart = &surface.charts()[face.trim.chart as usize]; - let jet = surface.derivatives(ua)?; - let mut displacement = [0.0; 2]; - for j in 0..2 { - displacement[j] = ua[j] - ub[j]; - if let Some(period) = chart.periods[j] { - displacement[j] -= (displacement[j] / period).round() * period; - } - } - let error = norm(super::geometry::add( - super::geometry::scale(jet.du, displacement[0]), - super::geometry::scale(jet.dv, displacement[1]), - )); - if error > self.accuracy.geometric { - return Err(invalid("pcurve loop is discontinuous")); - } - Ok(()) - } -} - -#[cfg(test)] -pub(super) mod tests { - use super::*; - #[test] - fn picked_support_normals_follow_curvature_reversal_and_singularities() { - use crate::analytic::{ - booleans::{boolean_spheres, BooleanOp}, - geometry::{dot, sub, unit}, - primitives, Frame3, - }; - let accuracy = Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-6, - }; - let frame = Frame3::from_axis([3.0, -2.0, 1.0], [1.0, 2.0, 3.0], [0.0, 1.0, 0.0]).unwrap(); - let sphere = primitives::sphere("host".into(), frame.clone(), 2.0, accuracy).unwrap(); - let point = sphere.geometry.surfaces[0].point_at([0.7, 0.3]).unwrap(); - let inward_mesh_point = - std::array::from_fn(|i| frame.origin[i] + (point[i] - frame.origin[i]) * 0.99); - let normal = sphere.face_normal_at(0, inward_mesh_point).unwrap(); - assert!(dot(normal, unit(sub(point, frame.origin)).unwrap()) > 1.0 - 1e-12); - let cutter = primitives::sphere("cutter".into(), frame.clone(), 0.5, accuracy).unwrap(); - let cavity = boolean_spheres(&sphere, &cutter, BooleanOp::Subtraction, "cavity".into()) - .unwrap() - .brep; - let face = cavity - .topology - .faces - .iter() - .find(|face| face.provenance.role == FaceRole::Cut) - .unwrap(); - let point = cavity - .geometry - .surface(face.surface) - .unwrap() - .point_at([0.7, 0.3]) - .unwrap(); - assert!( - dot( - cavity.face_normal_at(face.id, point).unwrap(), - unit(sub(point, frame.origin)).unwrap() - ) < -1.0 + 1e-12 - ); - assert!(matches!( - sphere.face_normal_at(u32::MAX, point), - Err(GeometryError::MissingReference { .. }) - )); - assert!(sphere.face_normal_at(0, [f64::NAN, 0.0, 0.0]).is_err()); - let cone = primitives::cone("cone".into(), frame, 1.0, 2.0, accuracy).unwrap(); - let face = cone - .topology - .faces - .iter() - .find(|face| { - matches!( - cone.geometry.surfaces[face.surface as usize], - SurfaceGeometry::Cone { .. } - ) - }) - .unwrap(); - let apex = cone.geometry.surface(face.surface).unwrap().frame().origin; - assert!(matches!( - cone.face_normal_at(face.id, apex), - Err(GeometryError::SingularParameterization) - )); - } - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-9, - intersection: 1e-10, - tessellation: 1e-3, - exchange: 1e-5, - } - } - pub fn circular_face() -> BrepEnvelope { - let mut b = BrepEnvelope::new("circle".into(), accuracy()).unwrap(); - b.geometry.surfaces.push(SurfaceGeometry::Plane { - frame: super::super::Frame3::IDENTITY, - }); - b.geometry.curves.push(CurveGeometry::Circle { - frame: super::super::Frame3::IDENTITY, - radius: 1.0, - }); - b.geometry.pcurves.push(PcurveGeometry::Conic2 { - origin: [0.0; 2], - axis_a: [1.0, 0.0], - axis_b: [0.0, 1.0], - }); - b.topology.vertices.push(Vertex { - id: 0, - position: [1.0, 0.0, 0.0], - outgoing_halfedge: Some(0), - tolerance: 1e-9, - }); - b.topology.edges.push(Edge { - id: 0, - geometry: EdgeGeometry::Curve { - curve: 0, - range: Interval::new(0.0, std::f64::consts::TAU).unwrap(), - }, - halfedge: 0, - twin_halfedge: None, - tolerance: 1e-9, - chart_seam: false, - }); - b.topology.halfedges.push(HalfEdge { - id: 0, - from: 0, - to: 0, - twin: None, - next: Some(0), - prev: Some(0), - edge: 0, - face: Some(0), - loop_ref: Some(0), - wire_ref: None, - geometry_use: HalfEdgeGeometryUse { - sense: Orientation::Forward, - pcurve: Some(0), - periodic_lift: [0; 2], - }, - }); - b.topology.loops.push(Loop { - id: 0, - start_halfedge: 0, - face_ref: 0, - is_hole: false, - }); - b.topology.faces.push(Face { - id: 0, - key: "disc".into(), - surface: 0, - sense: Orientation::Forward, - trim: TrimRegion { - chart: 0, - uv_bounds: [Interval::new(-1.0, 1.0).unwrap(); 2], - outer: 0, - holes: Vec::new(), - }, - shell_ref: None, - provenance: FaceProvenance { - sources: vec![FaceSource { - entity: "circle".into(), - body: "circle".into(), - key: "disc".into(), - face: 0, - }], - role: FaceRole::Authored, - reversed: false, - }, - }); - b - } - #[test] - fn full_circle_loop_is_valid_and_strictly_versioned() { - let b = circular_face(); - b.validate().unwrap(); - let json = b.to_json().unwrap(); - BrepEnvelope::from_json(&json).unwrap(); - let mut bad: serde_json::Value = serde_json::from_str(&json).unwrap(); - bad["schema_version"] = serde_json::json!(1); - assert!(matches!( - BrepEnvelope::from_json(&bad.to_string()), - Err(GeometryError::UnsupportedSchema { found: 1 }) - )); - bad.as_object_mut().unwrap().remove("schema_version"); - assert!(BrepEnvelope::from_json(&bad.to_string()).is_err()); - } - #[test] - fn missing_pcurve_and_malformed_cycle_are_rejected() { - let mut b = circular_face(); - b.topology.halfedges[0].geometry_use.pcurve = None; - assert!(b.validate().is_err()); - let mut b = circular_face(); - b.topology.halfedges[0].next = Some(10); - assert!(b.validate().is_err()); - let mut b = circular_face(); - b.geometry.pcurves[0] = PcurveGeometry::Line2 { - origin: [0.0; 2], - direction: [1.0, 0.0], - }; - assert!(b.validate().is_err()); - } - #[test] - fn pcurve_outside_uv_bounds_beyond_geometric_tolerance_is_rejected() { - let mut b = circular_face(); - b.topology.faces[0].trim.uv_bounds[0] = Interval::new(-0.999, 0.999).unwrap(); - assert!(matches!( - b.validate(), - Err(GeometryError::InvalidTopology(message)) if message.contains("leaves face 0 UV bounds") - )); - } - #[test] - fn old_json_and_unknown_fields_are_not_accepted() { - assert!(BrepEnvelope::from_json("{\"vertices\":[],\"faces\":[]}").is_err()); - let mut json: serde_json::Value = - serde_json::from_str(&circular_face().to_json().unwrap()).unwrap(); - json["legacy"] = serde_json::json!(true); - assert!(BrepEnvelope::from_json(&json.to_string()).is_err()); - } - #[test] - fn malformed_charts_and_undeclared_degenerate_edges_are_rejected() { - let mut b = circular_face(); - b.topology.faces[0].trim.chart = u32::MAX; - assert!( - matches!(b.validate(), Err(GeometryError::MissingReference { kind, .. }) if kind == "chart") - ); - let mut b = circular_face(); - b.geometry.curves[0] = CurveGeometry::Line { - origin: [1.0, 0.0, 0.0], - direction: [1.0, 0.0, 0.0], - }; - b.topology.edges[0].geometry = EdgeGeometry::Curve { - curve: 0, - range: Interval::new(0.0, 1e-12).unwrap(), - }; - b.geometry.pcurves[0] = PcurveGeometry::Line2 { - origin: [1.0, 0.0], - direction: [1.0, 0.0], - }; - assert!( - matches!(b.validate(), Err(GeometryError::InvalidTopology(message)) if message.contains("regular line")) - ); - let mut b = circular_face(); - b.accuracy.geometric = 10.0; - b.topology.edges[0].geometry = EdgeGeometry::Collapsed { vertex: 0 }; - b.geometry.pcurves[0] = PcurveGeometry::Line2 { - origin: [1.0, 0.0], - direction: [0.0; 2], - }; - assert!( - matches!(b.validate(), Err(GeometryError::InvalidTopology(message)) if message.contains("surface singularity")) - ); - let mut b = circular_face(); - b.topology.vertices[0].tolerance = 1e-18; - b.topology.edges[0].tolerance = 1e-18; - assert!( - matches!(b.validate(), Err(GeometryError::InvalidTopology(message)) if message.contains("edge endpoints")) - ); - } -} diff --git a/main/opengeometry/src/analytic/universal_ssi.rs b/main/opengeometry/src/analytic/universal_ssi.rs deleted file mode 100644 index c86e62e..0000000 --- a/main/opengeometry/src/analytic/universal_ssi.rs +++ /dev/null @@ -1,1194 +0,0 @@ -use super::{ - geometry::{add, cross, dot, norm, scale, sub, unit, PatchBounds, Surface, UVBox}, - intersection::{IntersectionDefinition, SsiBudget, TraceAnchor}, - ssi::{SsiCurve, SsiResult}, - topology::{Accuracy, GeometryStore, IntersectionSide, PcurveGeometry}, - CurveGeometry, GeometryError, Point3, SurfaceGeometry, UV, -}; -use crate::math::{interval::Interval, solve::solve}; -use std::collections::{HashMap, HashSet}; - -#[derive(Clone, Copy)] -struct Root { - uv_a: UV, - uv_b: UV, - point: Point3, -} - -#[derive(Clone, Copy)] -struct Segment { - ends: [Root; 2], - domain: UVBox, -} - -fn pseudo_arclength_correct( - source: &SurfaceGeometry, - target: &SurfaceGeometry, - root: Root, - tolerance: f64, -) -> Result { - let predictor = root.point; - let normal_cross = cross(source.normal_at(root.uv_a)?, target.normal_at(root.uv_b)?); - if norm(normal_cross) <= 1.0e-8 { - let point_a = source.point_at(root.uv_a)?; - let point_b = target.point_at(root.uv_b)?; - if norm(sub(point_a, point_b)) <= tolerance { - return Ok(Root { - point: scale(add(point_a, point_b), 0.5), - ..root - }); - } - return Err(GeometryError::UnresolvedIntersection( - "universal SSI tangent branch root exceeds its support residual".into(), - )); - } - let tangent = unit(normal_cross)?; - let mut parameters = [root.uv_a[0], root.uv_a[1], root.uv_b[0], root.uv_b[1]]; - for _ in 0..12 { - let uv_a = [parameters[0], parameters[1]]; - let uv_b = [parameters[2], parameters[3]]; - let point_a = source.point_at(uv_a)?; - let point_b = target.point_at(uv_b)?; - let midpoint = scale(add(point_a, point_b), 0.5); - let residual = sub(point_a, point_b); - let constraint = dot(sub(midpoint, predictor), tangent); - if norm(residual) <= tolerance * 0.25 && constraint.abs() <= tolerance * 0.25 { - return Ok(Root { - uv_a, - uv_b, - point: midpoint, - }); - } - let jet_a = source.derivatives(uv_a)?; - let jet_b = target.derivatives(uv_b)?; - let matrix = [ - [jet_a.du[0], jet_a.dv[0], -jet_b.du[0], -jet_b.dv[0]], - [jet_a.du[1], jet_a.dv[1], -jet_b.du[1], -jet_b.dv[1]], - [jet_a.du[2], jet_a.dv[2], -jet_b.du[2], -jet_b.dv[2]], - [ - 0.5 * dot(jet_a.du, tangent), - 0.5 * dot(jet_a.dv, tangent), - 0.5 * dot(jet_b.du, tangent), - 0.5 * dot(jet_b.dv, tangent), - ], - ]; - let correction = solve( - matrix, - [-residual[0], -residual[1], -residual[2], -constraint], - ) - .map_err(|_| { - GeometryError::UnresolvedIntersection( - "universal SSI pseudo-arclength correction is singular".into(), - ) - })?; - if correction.minimum_scaled_pivot <= 256.0 * f64::EPSILON { - return Err(GeometryError::UnresolvedIntersection( - "universal SSI pseudo-arclength correction is ill-conditioned".into(), - )); - } - for (parameter, delta) in parameters.iter_mut().zip(correction.value) { - *parameter += delta; - } - if parameters.iter().any(|value| !value.is_finite()) { - return Err(GeometryError::UnresolvedIntersection( - "universal SSI pseudo-arclength correction left finite parameter space".into(), - )); - } - } - Err(GeometryError::UnresolvedIntersection( - "universal SSI pseudo-arclength correction iteration budget exhausted".into(), - )) -} - -fn local_intervals( - surface: &SurfaceGeometry, - bounds: PatchBounds, -) -> Result<[Interval; 3], GeometryError> { - let frame = surface.frame(); - let mut local = [Interval::point(0.0)?; 3]; - for (axis, direction) in [frame.x, frame.y, frame.z].into_iter().enumerate() { - let mut coordinate = Interval::point(0.0)?; - for world_axis in 0..3 { - coordinate = coordinate.add( - bounds.axes[world_axis] - .sub(Interval::point(frame.origin[world_axis])?)? - .mul(Interval::point(direction[world_axis])?)?, - )?; - } - local[axis] = coordinate; - } - Ok(local) -} - -fn implicit_interval( - surface: &SurfaceGeometry, - bounds: PatchBounds, -) -> Result { - let [x, y, z] = local_intervals(surface, bounds)?; - let x2 = x.square()?; - let y2 = y.square()?; - let z2 = z.square()?; - Ok(match surface { - SurfaceGeometry::Plane { .. } => z, - SurfaceGeometry::Sphere { radius, .. } => x2 - .add(y2)? - .add(z2)? - .sub(Interval::point(radius * radius)?)?, - SurfaceGeometry::Cylinder { radius, .. } => { - x2.add(y2)?.sub(Interval::point(radius * radius)?)? - } - SurfaceGeometry::Cone { semi_angle, .. } => { - if z.hi < 0.0 { - return Interval::point(1.0).map_err(Into::into); - } - let slope = semi_angle.tan(); - x2.add(y2)?.sub(z2.mul(Interval::point(slope * slope)?)?)? - } - SurfaceGeometry::Torus { - major_radius, - minor_radius, - .. - } => { - let radial = x2.add(y2)?; - Interval::new(radial.lo.max(0.0), radial.hi)? - .sqrt()? - .sub(Interval::point(*major_radius)?)? - .square()? - .add(z2)? - .sub(Interval::point(minor_radius * minor_radius)?)? - } - }) -} - -fn implicit_value(surface: &SurfaceGeometry, point: Point3) -> Result { - let [x, y, z] = surface.frame().local(point); - let value = match surface { - SurfaceGeometry::Plane { .. } => z, - SurfaceGeometry::Sphere { radius, .. } => { - x.mul_add(x, y.mul_add(y, z * z)) - radius * radius - } - SurfaceGeometry::Cylinder { radius, .. } => x.mul_add(x, y * y) - radius * radius, - SurfaceGeometry::Cone { semi_angle, .. } => { - if z < 0.0 { - return Ok(x.mul_add(x, y.mul_add(y, z * z)) + 1.0); - } - x.mul_add(x, y * y) - z * z * semi_angle.tan().powi(2) - } - SurfaceGeometry::Torus { - major_radius, - minor_radius, - .. - } => { - let radial = x.mul_add(x, y * y); - (radial.sqrt() - major_radius).powi(2) + z * z - minor_radius * minor_radius - } - }; - if value.is_finite() { - Ok(value) - } else { - Err(GeometryError::UnresolvedIntersection( - "universal SSI implicit evaluation exceeded numerical range".into(), - )) - } -} - -fn surface_scale(surface: &SurfaceGeometry) -> f64 { - match surface { - SurfaceGeometry::Plane { .. } => 1.0, - SurfaceGeometry::Sphere { radius, .. } | SurfaceGeometry::Cylinder { radius, .. } => { - *radius - } - SurfaceGeometry::Cone { .. } => 1.0, - SurfaceGeometry::Torus { - major_radius, - minor_radius, - .. - } => major_radius + minor_radius, - } -} - -fn support_root( - source: &SurfaceGeometry, - target: &SurfaceGeometry, - left_uv: UV, - right_uv: UV, - left_value: f64, - right_value: f64, - tolerance: f64, -) -> Result { - let mut lo_uv = left_uv; - let mut hi_uv = right_uv; - let mut lo = left_value; - let mut hi = right_value; - let mut best = if lo.abs() <= hi.abs() { lo_uv } else { hi_uv }; - for _ in 0..80 { - let uv = [(lo_uv[0] + hi_uv[0]) * 0.5, (lo_uv[1] + hi_uv[1]) * 0.5]; - let point = source.point_at(uv)?; - let value = implicit_value(target, point)?; - if value.abs() < implicit_value(target, source.point_at(best)?)?.abs() { - best = uv; - } - let projected = target.project(point, None); - if let Ok(uv_b) = projected { - let target_point = target.point_at(uv_b)?; - if norm(sub(point, target_point)) <= tolerance * 0.01 { - return Ok(Root { - uv_a: uv, - uv_b, - point, - }); - } - } - if lo == 0.0 { - best = lo_uv; - break; - } - if hi == 0.0 { - best = hi_uv; - break; - } - if lo.is_sign_negative() == value.is_sign_negative() { - lo_uv = uv; - lo = value; - } else { - hi_uv = uv; - hi = value; - } - } - let point = source.point_at(best)?; - let uv_b = target.project(point, None).map_err(|_| { - GeometryError::UnresolvedIntersection( - "universal SSI root cannot be projected to its second support".into(), - ) - })?; - if norm(sub(point, target.point_at(uv_b)?)) > tolerance { - return Err(GeometryError::UnresolvedIntersection( - "universal SSI root residual exceeds the intersection budget".into(), - )); - } - Ok(Root { - uv_a: best, - uv_b, - point, - }) -} - -fn edge_root( - source: &SurfaceGeometry, - target: &SurfaceGeometry, - uv: [UV; 2], - values: [f64; 2], - tolerance: f64, -) -> Result, GeometryError> { - if values[0] != 0.0 - && values[1] != 0.0 - && values[0].is_sign_negative() == values[1].is_sign_negative() - { - return Ok(None); - } - support_root( - source, target, uv[0], uv[1], values[0], values[1], tolerance, - ) - .map(Some) -} - -fn cell_segments( - source: &SurfaceGeometry, - target: &SurfaceGeometry, - domain: UVBox, - tolerance: f64, -) -> Result, GeometryError> { - let corners = [ - [domain[0].lo, domain[1].lo], - [domain[0].hi, domain[1].lo], - [domain[0].hi, domain[1].hi], - [domain[0].lo, domain[1].hi], - ]; - let mut values = [0.0; 4]; - for (index, uv) in corners.into_iter().enumerate() { - values[index] = implicit_value(target, source.point_at(uv)?)?; - } - let edge_indices = [[0, 1], [1, 2], [2, 3], [3, 0]]; - let mut roots = Vec::new(); - for [a, b] in edge_indices { - if let Some(root) = edge_root( - source, - target, - [corners[a], corners[b]], - [values[a], values[b]], - tolerance, - )? { - if roots - .iter() - .all(|existing: &Root| norm(sub(existing.point, root.point)) > tolerance) - { - roots.push(root); - } - } - } - Ok(match roots.len() { - 0 => Vec::new(), - 1 => vec![Segment { - ends: [roots[0], roots[0]], - domain, - }], - 2 => vec![Segment { - ends: [roots[0], roots[1]], - domain, - }], - 3 => { - let mut pair = [0, 1]; - let mut separation = 0.0; - for first in 0..3 { - for second in (first + 1)..3 { - let candidate = norm(sub(roots[first].point, roots[second].point)); - if candidate > separation { - separation = candidate; - pair = [first, second]; - } - } - } - let isolated = (0..3).find(|index| !pair.contains(index)).ok_or_else(|| { - GeometryError::UnresolvedIntersection( - "universal SSI could not isolate a grid-vertex event".into(), - ) - })?; - vec![ - Segment { - ends: [roots[pair[0]], roots[pair[1]]], - domain, - }, - Segment { - ends: [roots[isolated], roots[isolated]], - domain, - }, - ] - } - 4 => { - let center = [domain[0].midpoint(), domain[1].midpoint()]; - let center_value = implicit_value(target, source.point_at(center)?)?; - let same_as_corner = center_value.is_sign_negative() == values[0].is_sign_negative(); - if same_as_corner { - vec![ - Segment { - ends: [roots[0], roots[3]], - domain, - }, - Segment { - ends: [roots[1], roots[2]], - domain, - }, - ] - } else { - vec![ - Segment { - ends: [roots[0], roots[1]], - domain, - }, - Segment { - ends: [roots[2], roots[3]], - domain, - }, - ] - } - } - _ => { - return Err(GeometryError::UnresolvedIntersection(format!( - "universal SSI cell contains a singular or unresolved branch event ({} edge roots)", - roots.len() - ))) - } - }) -} - -enum StationaryEvent { - Contact(Point3), - InteriorCritical, -} - -fn stationary_event( - source: &SurfaceGeometry, - target: &SurfaceGeometry, - domain: UVBox, - tolerance: f64, -) -> Result, GeometryError> { - let mut uv_a = [domain[0].midpoint(), domain[1].midpoint()]; - let value = |uv| implicit_value(target, source.point_at(uv)?); - let mut converged = false; - for _ in 0..16 { - let h = [0, 1].map(|axis| { - (domain[axis].width() * 1.0e-3).max(f64::EPSILON.sqrt() * uv_a[axis].abs().max(1.0)) - }); - let center = value(uv_a)?; - let axis_values = [0, 1] - .map(|axis| { - let mut low = uv_a; - let mut high = uv_a; - low[axis] -= h[axis]; - high[axis] += h[axis]; - Ok([value(low)?, value(high)?]) - }) - .into_iter() - .collect::, GeometryError>>()?; - let gradient = - [0, 1].map(|axis| (axis_values[axis][1] - axis_values[axis][0]) / (2.0 * h[axis])); - let diagonal = [0, 1].map(|axis| { - (axis_values[axis][0] - 2.0 * center + axis_values[axis][1]) / (h[axis] * h[axis]) - }); - let mixed = { - let values = [[-1.0, -1.0], [-1.0, 1.0], [1.0, -1.0], [1.0, 1.0]] - .map(|offset| value([uv_a[0] + offset[0] * h[0], uv_a[1] + offset[1] * h[1]])) - .into_iter() - .collect::, GeometryError>>()?; - (values[3] - values[2] - values[1] + values[0]) / (4.0 * h[0] * h[1]) - }; - let determinant = diagonal[0] * diagonal[1] - mixed * mixed; - if !determinant.is_finite() || determinant.abs() <= f64::EPSILON { - return Ok(None); - } - let delta = [ - (diagonal[1] * gradient[0] - mixed * gradient[1]) / determinant, - (diagonal[0] * gradient[1] - mixed * gradient[0]) / determinant, - ]; - let candidate = [uv_a[0] - delta[0], uv_a[1] - delta[1]]; - if (0..2).any(|axis| { - !candidate[axis].is_finite() - || candidate[axis] < domain[axis].lo - || candidate[axis] > domain[axis].hi - }) { - return Ok(None); - } - uv_a = candidate; - if delta[0].abs() <= h[0] * 1.0e-3 && delta[1].abs() <= h[1] * 1.0e-3 { - converged = true; - break; - } - } - if !converged || !source.parameter_in_domain(uv_a) { - return Ok(None); - } - if (0..2).any(|axis| { - uv_a[axis] < domain[axis].lo - tolerance || uv_a[axis] > domain[axis].hi + tolerance - }) { - return Ok(None); - } - let point_a = source.point_at(uv_a)?; - let uv_b = match target.project(point_a, None) { - Ok(uv) if target.parameter_in_domain(uv) => uv, - _ => return Ok(Some(StationaryEvent::InteriorCritical)), - }; - let point_b = target.point_at(uv_b)?; - if norm(sub(point_a, point_b)) > tolerance { - return Ok(Some(StationaryEvent::InteriorCritical)); - } - let normal_a = source.normal_at(uv_a)?; - let normal_b = target.normal_at(uv_b)?; - if norm(cross(normal_a, normal_b)) > 1.0e-5 { - return Ok(Some(StationaryEvent::InteriorCritical)); - } - let h = [0, 1].map(|axis| { - (domain[axis].width() * 1.0e-3).max(f64::EPSILON.sqrt() * uv_a[axis].abs().max(1.0)) - }); - let center = value(uv_a)?; - let axis_values = [0, 1] - .map(|axis| { - let mut low = uv_a; - let mut high = uv_a; - low[axis] -= h[axis]; - high[axis] += h[axis]; - Ok([value(low)?, value(high)?]) - }) - .into_iter() - .collect::, GeometryError>>()?; - let diagonal = [0, 1].map(|axis| { - (axis_values[axis][0] - 2.0 * center + axis_values[axis][1]) / (h[axis] * h[axis]) - }); - let mixed_values = [[-1.0, -1.0], [-1.0, 1.0], [1.0, -1.0], [1.0, 1.0]] - .map(|offset| value([uv_a[0] + offset[0] * h[0], uv_a[1] + offset[1] * h[1]])) - .into_iter() - .collect::, GeometryError>>()?; - let mixed = (mixed_values[3] - mixed_values[2] - mixed_values[1] + mixed_values[0]) - / (4.0 * h[0] * h[1]); - let determinant = diagonal[0] * diagonal[1] - mixed * mixed; - let hessian_scale = diagonal[0] - .abs() - .max(diagonal[1].abs()) - .max(mixed.abs()) - .max(1.0); - if determinant <= 1024.0 * f64::EPSILON * hessian_scale * hessian_scale { - return Ok(Some(StationaryEvent::InteriorCritical)); - } - Ok(Some(StationaryEvent::Contact(scale( - add(point_a, point_b), - 0.5, - )))) -} - -fn split(domain: UVBox, axis: usize) -> Result<[UVBox; 2], GeometryError> { - let midpoint = domain[axis].midpoint(); - let mut left = domain; - let mut right = domain; - left[axis] = Interval::new(domain[axis].lo, midpoint)?; - right[axis] = Interval::new(midpoint, domain[axis].hi)?; - Ok([left, right]) -} - -fn bounds_diameter(bounds: PatchBounds) -> f64 { - bounds - .axes - .iter() - .map(|axis| axis.width() * axis.width()) - .sum::() - .sqrt() -} - -fn bounds_overlap(a: PatchBounds, b: PatchBounds, tolerance: f64) -> bool { - (0..3).all(|axis| { - a.axes[axis].lo <= b.axes[axis].hi + tolerance - && a.axes[axis].hi + tolerance >= b.axes[axis].lo - }) -} - -fn find(parent: &mut [usize], value: usize) -> usize { - let mut root = value; - while parent[root] != root { - root = parent[root]; - } - let mut current = value; - while parent[current] != current { - let next = parent[current]; - parent[current] = root; - current = next; - } - root -} - -fn union(parent: &mut [usize], left: usize, right: usize) { - let left = find(parent, left); - let right = find(parent, right); - if left != right { - parent[right] = left; - } -} - -fn segment_endpoint_tangent( - source: &SurfaceGeometry, - target: &SurfaceGeometry, - segment: Segment, - end: usize, -) -> Result { - let root = segment.ends[end]; - let transverse = cross(source.normal_at(root.uv_a)?, target.normal_at(root.uv_b)?); - if norm(transverse) > 1.0e-8 { - unit(transverse) - } else { - unit(sub(segment.ends[1 - end].point, root.point)).map_err(|_| { - GeometryError::UnresolvedIntersection( - "universal SSI tangent segment has no resolvable direction".into(), - ) - }) - } -} - -fn normalized_key(uv: UV, domain: UVBox, periods: [Option; 2], quantum: [f64; 2]) -> [i64; 2] { - std::array::from_fn(|axis| { - let value = periods[axis].map_or(uv[axis], |period| { - let lifted = (uv[axis] - domain[axis].lo).rem_euclid(period); - if (period - lifted).abs() <= quantum[axis] { - domain[axis].lo - } else { - domain[axis].lo + lifted - } - }); - (value / quantum[axis]).round() as i64 - }) -} - -fn unwrap(value: f64, previous: f64, period: Option) -> f64 { - period.map_or(value, |period| { - value + ((previous - value) / period).round() * period - }) -} - -fn branch_definition( - mut roots: Vec, - mut source_domains: Vec, - surfaces: [u32; 2], - store: &mut GeometryStore, - accuracy: Accuracy, -) -> Result { - if source_domains.len() + 1 != roots.len() { - return Err(GeometryError::InvalidGeometry( - "universal SSI branch domains do not match its roots".into(), - )); - } - let source = store.surface(surfaces[0])?.clone(); - let target = store.surface(surfaces[1])?.clone(); - roots = roots - .into_iter() - .map(|root| pseudo_arclength_correct(&source, &target, root, accuracy.intersection)) - .collect::, _>>()?; - let periods_a = store.surface(surfaces[0])?.charts()[0].periods; - let periods_b = store.surface(surfaces[1])?.charts()[0].periods; - for index in 1..roots.len() { - for axis in 0..2 { - roots[index].uv_a[axis] = unwrap( - roots[index].uv_a[axis], - roots[index - 1].uv_a[axis], - periods_a[axis], - ); - roots[index].uv_b[axis] = unwrap( - roots[index].uv_b[axis], - roots[index - 1].uv_b[axis], - periods_b[axis], - ); - } - } - for (index, domain) in source_domains.iter_mut().enumerate() { - for axis in 0..2 { - if let Some(period) = periods_a[axis] { - let branch_midpoint = 0.5 * (roots[index].uv_a[axis] + roots[index + 1].uv_a[axis]); - let shift = ((branch_midpoint - domain[axis].midpoint()) / period).round() * period; - domain[axis] = Interval::new(domain[axis].lo + shift, domain[axis].hi + shift)?; - } - } - } - let mut parameter = 0.0; - let mut anchors = Vec::with_capacity(roots.len()); - for (index, root) in roots.iter().enumerate() { - if index > 0 { - parameter += norm(sub(root.point, roots[index - 1].point)); - } - anchors.push(TraceAnchor { - parameter, - point: root.point, - uv_a: root.uv_a, - uv_b: root.uv_b, - }); - } - if anchors.len() < 2 || parameter <= 4.0 * accuracy.geometric { - return Err(GeometryError::UnresolvedIntersection( - "universal SSI branch is below geometric resolution".into(), - )); - } - let mut uv_tubes = Vec::with_capacity(anchors.len() - 1); - for (index, pair) in anchors.windows(2).enumerate() { - let coordinates = [ - [pair[0].uv_a[0], pair[1].uv_a[0]], - [pair[0].uv_a[1], pair[1].uv_a[1]], - [pair[0].uv_b[0], pair[1].uv_b[0]], - [pair[0].uv_b[1], pair[1].uv_b[1]], - ]; - let mut intervals = coordinates.map(|values| { - let padding = (values[1] - values[0]).abs().max(accuracy.intersection) * 2.0; - Interval::new( - values[0].min(values[1]) - padding, - values[0].max(values[1]) + padding, - ) - }); - for axis in 0..2 { - let domain = source_domains[index][axis]; - let padding = - domain.width() * 0.05 + 16.0 * f64::EPSILON * domain.lo.abs().max(domain.hi.abs()); - intervals[axis] = Interval::new( - domain - .lo - .min(coordinates[axis][0]) - .min(coordinates[axis][1]) - - padding, - domain - .hi - .max(coordinates[axis][0]) - .max(coordinates[axis][1]) - + padding, - ); - } - let [u0, v0, u1, v1] = intervals; - uv_tubes.push([u0?, v0?, u1?, v1?]); - } - let definition = store.intersections.len() as u32; - store.intersections.push(IntersectionDefinition { - surfaces, - anchors, - uv_tubes, - residual_tolerance: accuracy.intersection, - }); - let curve = store.curves.len() as u32; - store - .curves - .push(CurveGeometry::Intersection { definition }); - let pcurve_a = store.pcurves.len() as u32; - store.pcurves.push(PcurveGeometry::IntersectionSide { - definition, - side: IntersectionSide::A, - }); - let pcurve_b = store.pcurves.len() as u32; - store.pcurves.push(PcurveGeometry::IntersectionSide { - definition, - side: IntersectionSide::B, - }); - let domain = Interval::new(0.0, parameter)?; - store.intersections[definition as usize].validate(store)?; - Ok(SsiCurve { - curve, - pcurves: [pcurve_a, pcurve_b], - domain: Some(domain), - }) -} - -pub fn intersect_patches( - store: &mut GeometryStore, - surfaces: [u32; 2], - domains: [UVBox; 2], - accuracy: Accuracy, - budget: SsiBudget, -) -> Result { - intersect_patches_from(store, surfaces, domains, 0, accuracy, budget) -} - -pub fn intersect_patches_from( - store: &mut GeometryStore, - surfaces: [u32; 2], - domains: [UVBox; 2], - source_side: usize, - accuracy: Accuracy, - budget: SsiBudget, -) -> Result { - if source_side > 1 { - return Err(GeometryError::InvalidGeometry( - "universal SSI source side must be zero or one".into(), - )); - } - let ordered_surfaces = if source_side == 0 { - surfaces - } else { - [surfaces[1], surfaces[0]] - }; - let ordered_domains = if source_side == 0 { - domains - } else { - [domains[1], domains[0]] - }; - accuracy.validate()?; - budget.validate()?; - for domain in ordered_domains { - for axis in domain { - Interval::new(axis.lo, axis.hi)?; - if axis.width() <= 0.0 { - return Err(GeometryError::InvalidGeometry( - "universal SSI patch domain has no area".into(), - )); - } - } - } - let source = store.surface(ordered_surfaces[0])?.clone(); - let target = store.surface(ordered_surfaces[1])?.clone(); - source.validate()?; - target.validate()?; - let source_bounds = source.enclose(ordered_domains[0])?; - let target_bounds = target.enclose(ordered_domains[1])?; - let scale = bounds_diameter(source_bounds) - .max(surface_scale(&source)) - .max(surface_scale(&target)) - .max(accuracy.geometric); - let target_cell = (scale / 64.0).max(16.0 * accuracy.geometric); - let mut stack = vec![(ordered_domains[0], 0usize)]; - let mut visits = 0usize; - let mut segments = Vec::new(); - let mut contacts = Vec::new(); - let mut unresolved = false; - while let Some((domain, depth)) = stack.pop() { - visits = visits - .checked_add(1) - .ok_or_else(|| GeometryError::LimitExceeded("SSI patch visits".into()))?; - if visits > budget.max_patch_pairs { - return Err(GeometryError::UnresolvedIntersection( - "universal SSI patch-pair visit budget exhausted".into(), - )); - } - let bounds = source.enclose(domain)?; - if !bounds_overlap(bounds, target_bounds, accuracy.intersection) { - continue; - } - if !implicit_interval(&target, bounds)?.contains(0.0) { - continue; - } - if bounds_diameter(bounds) <= target_cell || depth == budget.max_subdivision_depth { - let found = cell_segments(&source, &target, domain, accuracy.intersection)?; - if found.is_empty() { - match stationary_event(&source, &target, domain, accuracy.intersection)? { - Some(StationaryEvent::Contact(contact)) => { - if contacts - .iter() - .all(|existing| norm(sub(*existing, contact)) > accuracy.intersection) - { - contacts.push(contact); - } - } - Some(StationaryEvent::InteriorCritical) - if depth == budget.max_subdivision_depth => - { - unresolved = true; - } - Some(StationaryEvent::InteriorCritical) => { - let axis = usize::from(domain[1].width() > domain[0].width()); - let children = split(domain, axis)?; - stack.push((children[1], depth + 1)); - stack.push((children[0], depth + 1)); - } - None => {} - } - } else { - segments.extend(found); - } - continue; - } - let axis = usize::from(domain[1].width() > domain[0].width()); - let children = split(domain, axis)?; - stack.push((children[1], depth + 1)); - stack.push((children[0], depth + 1)); - } - if unresolved { - return Err(GeometryError::UnresolvedIntersection( - "universal SSI could not certify an interval cell containing a possible tangency or sub-cell branch".into(), - )); - } - let mut result = SsiResult { - contacts, - ..SsiResult::default() - }; - if segments.is_empty() { - return Ok(result); - } - - let periods = source.charts()[0].periods; - let quantum = [ - (ordered_domains[0][0].width() / 1_000_000.0).max(accuracy.geometric / scale), - (ordered_domains[0][1].width() / 1_000_000.0).max(accuracy.geometric / scale), - ]; - let mut unique = Vec::new(); - let mut segment_keys = HashSet::new(); - for segment in segments { - let mut keys = segment - .ends - .map(|root| normalized_key(root.uv_a, ordered_domains[0], periods, quantum)); - if keys[0] == keys[1] { - continue; - } - if keys[1] < keys[0] { - keys.swap(0, 1); - } - if segment_keys.insert(keys) { - unique.push(segment); - } - } - let endpoint_count = unique.len() * 2; - let mut parent = (0..endpoint_count).collect::>(); - let stitch_tolerance = - (4.0 * accuracy.geometric.max(accuracy.intersection)).max(target_cell * 0.01); - let mut buckets: HashMap<[i64; 3], Vec> = HashMap::new(); - for endpoint in 0..endpoint_count { - let root = unique[endpoint / 2].ends[endpoint % 2]; - let key = root - .point - .map(|coordinate| (coordinate / stitch_tolerance).floor() as i64); - let tangent = - segment_endpoint_tangent(&source, &target, unique[endpoint / 2], endpoint % 2)?; - for x in (key[0] - 1)..=(key[0] + 1) { - for y in (key[1] - 1)..=(key[1] + 1) { - for z in (key[2] - 1)..=(key[2] + 1) { - for &candidate in buckets.get(&[x, y, z]).into_iter().flatten() { - let other = unique[candidate / 2].ends[candidate % 2]; - if norm(sub(root.point, other.point)) > stitch_tolerance { - continue; - } - let other_tangent = segment_endpoint_tangent( - &source, - &target, - unique[candidate / 2], - candidate % 2, - )?; - if dot(tangent, other_tangent).abs() >= 0.99 { - union(&mut parent, endpoint, candidate); - } - } - } - } - } - buckets.entry(key).or_default().push(endpoint); - } - let collapsed_segments = (0..unique.len()) - .map(|index| find(&mut parent, index * 2) == find(&mut parent, index * 2 + 1)) - .collect::>(); - let mut adjacency: HashMap> = HashMap::new(); - for index in 0..unique.len() { - if collapsed_segments[index] { - continue; - } - for end in 0..2 { - let node = find(&mut parent, index * 2 + end); - adjacency.entry(node).or_default().push((index, end)); - } - } - if adjacency.values().any(|edges| edges.len() > 2) { - return Err(GeometryError::UnresolvedIntersection( - "universal SSI found a singular branch junction".into(), - )); - } - let mut used = collapsed_segments; - for start in 0..unique.len() { - if used[start] { - continue; - } - let endpoint = (0..2) - .find(|&end| { - let node = find(&mut parent, start * 2 + end); - adjacency.get(&node).is_some_and(|edges| edges.len() == 1) - }) - .unwrap_or(0); - let mut branch = Vec::new(); - let mut source_domains = Vec::new(); - let mut current = start; - let mut from_end = endpoint; - loop { - if used[current] { - break; - } - used[current] = true; - let segment = unique[current]; - if branch.is_empty() { - branch.push(segment.ends[from_end]); - } - let to_end = 1 - from_end; - branch.push(segment.ends[to_end]); - source_domains.push(segment.domain); - if branch.len() > budget.max_steps_per_branch { - return Err(GeometryError::UnresolvedIntersection( - "universal SSI branch step budget exhausted".into(), - )); - } - let node = find(&mut parent, current * 2 + to_end); - let Some(next) = adjacency - .get(&node) - .and_then(|edges| edges.iter().copied().find(|(index, _)| !used[*index])) - else { - break; - }; - current = next.0; - from_end = next.1; - } - if branch.len() >= 2 { - result.curves.push(branch_definition( - branch, - source_domains, - ordered_surfaces, - store, - accuracy, - )?); - } - } - if source_side == 1 { - for curve in &mut result.curves { - curve.pcurves.swap(0, 1); - } - } - Ok(result) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::{Curve, Frame3}; - - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-5, - intersection: 2.5e-6, - tessellation: 1e-3, - exchange: 1e-5, - } - } - - #[test] - fn oblique_plane_torus_is_stored_as_corrected_numerical_branches() { - let mut store = GeometryStore::new(); - let plane = - Frame3::from_axis([0.0, 0.0, 0.15], [0.35, -0.2, 1.0], [1.0, 0.0, 0.0]).unwrap(); - store.surfaces = vec![ - SurfaceGeometry::Torus { - frame: Frame3::IDENTITY, - major_radius: 2.0, - minor_radius: 0.5, - }, - SurfaceGeometry::Plane { frame: plane }, - ]; - let tau = std::f64::consts::TAU; - let result = intersect_patches( - &mut store, - [0, 1], - [ - [Interval::new(0.0, tau).unwrap(); 2], - [Interval::new(-4.0, 4.0).unwrap(); 2], - ], - accuracy(), - SsiBudget::default(), - ) - .unwrap(); - assert!(!result.curves.is_empty()); - for branch in result.curves { - assert!(matches!( - store.curves[branch.curve as usize], - CurveGeometry::Intersection { .. } - )); - let domain = branch.domain.unwrap(); - for index in 0..=16 { - let t = domain.lo + domain.width() * index as f64 / 16.0; - let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); - for surface in &store.surfaces { - let uv = surface.project(point, None).unwrap(); - assert!( - norm(sub(surface.point_at(uv).unwrap(), point)) <= accuracy().intersection - ); - } - } - } - } - - #[test] - fn disjoint_bounded_patches_return_empty_without_fabricating_a_branch() { - let mut store = GeometryStore::new(); - store.surfaces = vec![ - SurfaceGeometry::Sphere { - frame: Frame3::IDENTITY, - radius: 1.0, - }, - SurfaceGeometry::Plane { - frame: Frame3 { - origin: [0.0, 0.0, 2.0], - ..Frame3::IDENTITY - }, - }, - ]; - let result = intersect_patches( - &mut store, - [0, 1], - [ - [ - Interval::new(0.0, std::f64::consts::TAU).unwrap(), - Interval::new(-std::f64::consts::FRAC_PI_2, std::f64::consts::FRAC_PI_2) - .unwrap(), - ], - [Interval::new(-2.0, 2.0).unwrap(); 2], - ], - accuracy(), - SsiBudget::default(), - ) - .unwrap(); - assert!(result.curves.is_empty()); - assert!(result.contacts.is_empty()); - } - - #[test] - fn terminal_subdivision_reports_an_isolated_torus_plane_tangency() { - let u: f64 = 0.37; - let v: f64 = 0.41; - let (sin_u, cos_u) = u.sin_cos(); - let (sin_v, cos_v) = v.sin_cos(); - let normal = [cos_v * cos_u, cos_v * sin_u, sin_v]; - let radial = 2.0 + 0.5 * cos_v; - let contact = [radial * cos_u, radial * sin_u, 0.5 * sin_v]; - let mut store = GeometryStore::new(); - store.surfaces = vec![ - SurfaceGeometry::Torus { - frame: Frame3::IDENTITY, - major_radius: 2.0, - minor_radius: 0.5, - }, - SurfaceGeometry::Plane { - frame: Frame3::from_axis(contact, normal, [0.0, 0.0, 1.0]).unwrap(), - }, - ]; - let tau = std::f64::consts::TAU; - let result = intersect_patches( - &mut store, - [0, 1], - [ - [Interval::new(0.0, tau).unwrap(); 2], - [Interval::new(-3.0, 3.0).unwrap(); 2], - ], - accuracy(), - SsiBudget::default(), - ) - .unwrap(); - assert!(result.curves.is_empty()); - assert_eq!(result.contacts.len(), 1); - assert!(norm(sub(result.contacts[0], contact)) <= accuracy().intersection); - } - - #[test] - fn tangent_generator_is_not_collapsed_into_an_isolated_contact() { - let mut store = GeometryStore::new(); - store.surfaces = vec![ - SurfaceGeometry::Cylinder { - frame: Frame3::IDENTITY, - radius: 1.0, - }, - SurfaceGeometry::Plane { - frame: Frame3::from_axis([1.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]) - .unwrap(), - }, - ]; - let result = intersect_patches( - &mut store, - [0, 1], - [ - [ - Interval::new(-0.5, 0.5).unwrap(), - Interval::new(-1.0, 1.0).unwrap(), - ], - [Interval::new(-2.0, 2.0).unwrap(); 2], - ], - accuracy(), - SsiBudget::default(), - ) - .unwrap(); - assert!(!result.curves.is_empty()); - assert!(result.contacts.is_empty()); - } - - #[test] - fn target_patch_bounds_prune_intersections_on_the_unbounded_support() { - let mut store = GeometryStore::new(); - store.surfaces = vec![ - SurfaceGeometry::Cylinder { - frame: Frame3::IDENTITY, - radius: 1.0, - }, - SurfaceGeometry::Cylinder { - frame: Frame3::from_axis([0.0, 0.0, 0.2], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]) - .unwrap(), - radius: 0.6, - }, - ]; - let tau = std::f64::consts::TAU; - let result = intersect_patches( - &mut store, - [0, 1], - [ - [ - Interval::new(0.0, tau).unwrap(), - Interval::new(-1.0, 1.0).unwrap(), - ], - [ - Interval::new(0.0, tau).unwrap(), - Interval::new(10.0, 12.0).unwrap(), - ], - ], - accuracy(), - SsiBudget::default(), - ) - .unwrap(); - assert!(result.curves.is_empty()); - assert!(result.contacts.is_empty()); - } -} diff --git a/main/opengeometry/src/analytic/wasm.rs b/main/opengeometry/src/analytic/wasm.rs deleted file mode 100644 index 0844d45..0000000 --- a/main/opengeometry/src/analytic/wasm.rs +++ /dev/null @@ -1,979 +0,0 @@ -use super::{ - booleans::{boolean_brep, shell_brep, subtract_planar_cutters, BooleanOp, BooleanReport}, - primitives, - query::{classify_point, PointClassification}, - tessellation::{Tessellation, TessellationCache}, - topology::{Accuracy, BrepEnvelope}, - CurveGeometry, Frame3, GeometryError, -}; -use crate::export::projection::{project_analytic_brep_to_scene, CameraParameters, HlrOptions}; -use serde::Deserialize; -use std::{cell::RefCell, sync::Arc}; -use wasm_bindgen::prelude::*; - -const DEFAULT_MAX_TRIANGLES: usize = 2_000_000; - -#[derive(Deserialize, Default)] -#[serde(deny_unknown_fields)] -struct TessellationOptions { - max_triangles: Option, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -struct AnnularSectorOpeningConfig { - id: String, - angle: f64, - width: f64, - bottom: f64, - height: f64, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -struct BoxArchedOpeningConfig { - id: String, - station: f64, - width: f64, - bottom: f64, - height: f64, -} - -#[derive(Deserialize)] -#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] -enum PolygonLoftAlignmentConfig { - Auto, - Indexed { - upper_start: usize, - reverse_upper: bool, - }, -} - -fn tessellation_limit(options_json: Option) -> Result { - let options = match options_json { - Some(json) if !json.trim().is_empty() => serde_json::from_str::(&json) - .map_err(|error| GeometryError::InvalidGeometry(error.to_string()))?, - _ => TessellationOptions::default(), - }; - let limit = options.max_triangles.unwrap_or(DEFAULT_MAX_TRIANGLES); - if limit == 0 || limit > DEFAULT_MAX_TRIANGLES { - return Err(GeometryError::LimitExceeded(format!( - "max_triangles must be between 1 and {DEFAULT_MAX_TRIANGLES}" - ))); - } - Ok(limit) -} - -fn tessellate_cached( - brep: &BrepEnvelope, - deflection: f64, - max_triangles: usize, -) -> Result { - let mesh = CACHE.with(|cache| { - cache - .try_borrow_mut() - .map_err(|_| { - GeometryError::LimitExceeded("tessellation cache is already in use".into()) - })? - .tessellate(brep, deflection, max_triangles) - })?; - Ok(OGAnalyticTessellation { mesh }) -} - -#[derive(Deserialize)] -#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] -enum PrimitiveConfig { - Cuboid { - id: String, - frame: Frame3, - accuracy: Accuracy, - size: [f64; 3], - }, - LinearExtrusion { - id: String, - frame: Frame3, - accuracy: Accuracy, - outer: Vec<[f64; 2]>, - holes: Vec>, - height: f64, - }, - ArcEdgedExtrusion { - id: String, - frame: Frame3, - accuracy: Accuracy, - outer: Vec, - #[serde(default)] - holes: Vec>, - height: f64, - }, - PolygonLoft { - id: String, - frame: Frame3, - accuracy: Accuracy, - lower: Vec<[f64; 3]>, - upper: Vec<[f64; 3]>, - alignment: PolygonLoftAlignmentConfig, - }, - BoxWithArchedOpening { - id: String, - frame: Frame3, - accuracy: Accuracy, - width: f64, - depth: f64, - height: f64, - opening: BoxArchedOpeningConfig, - }, - PlanarPolyhedron { - id: String, - frame: Frame3, - accuracy: Accuracy, - vertices: Vec<[f64; 3]>, - faces: Vec>, - }, - Arc { - id: String, - frame: Frame3, - accuracy: Accuracy, - radius: f64, - start_angle: f64, - sweep_angle: f64, - }, - EllipticalArc { - id: String, - frame: Frame3, - accuracy: Accuracy, - major_radius: f64, - minor_radius: f64, - start_angle: f64, - sweep_angle: f64, - }, - Cylinder { - id: String, - frame: Frame3, - accuracy: Accuracy, - radius: f64, - height: f64, - }, - CylinderSector { - id: String, - frame: Frame3, - accuracy: Accuracy, - radius: f64, - height: f64, - start_angle: f64, - sweep_angle: f64, - }, - Sphere { - id: String, - frame: Frame3, - accuracy: Accuracy, - radius: f64, - }, - Cone { - id: String, - frame: Frame3, - accuracy: Accuracy, - radius: f64, - height: f64, - }, - Frustum { - id: String, - frame: Frame3, - accuracy: Accuracy, - lower_radius: f64, - upper_radius: f64, - height: f64, - }, - Torus { - id: String, - frame: Frame3, - accuracy: Accuracy, - major_radius: f64, - minor_radius: f64, - }, - AnnularSectorExtrusion { - id: String, - frame: Frame3, - accuracy: Accuracy, - radius: f64, - thickness: f64, - height: f64, - start_angle: f64, - sweep_angle: f64, - }, - AnnularSectorExtrusionWithOpenings { - id: String, - frame: Frame3, - accuracy: Accuracy, - radius: f64, - thickness: f64, - height: f64, - start_angle: f64, - sweep_angle: f64, - openings: Vec, - }, - AnnularSectorExtrusionWithArchedOpening { - id: String, - frame: Frame3, - accuracy: Accuracy, - radius: f64, - thickness: f64, - height: f64, - start_angle: f64, - sweep_angle: f64, - opening: AnnularSectorOpeningConfig, - }, - CoaxialCircleLoft { - id: String, - frame: Frame3, - accuracy: Accuracy, - lower_radius: f64, - upper_radius: f64, - height: f64, - }, - RevolvedRectangle { - id: String, - frame: Frame3, - accuracy: Accuracy, - inner_radius: f64, - outer_radius: f64, - height: f64, - }, - ChamferedCuboid { - id: String, - frame: Frame3, - accuracy: Accuracy, - size: [f64; 3], - chamfer: f64, - }, - FilletedCylinder { - id: String, - frame: Frame3, - accuracy: Accuracy, - radius: f64, - height: f64, - fillet_radius: f64, - }, -} -impl PrimitiveConfig { - fn build(self) -> Result { - match self { - Self::Cuboid { - id, - frame, - accuracy, - size, - } => primitives::cuboid(id, frame, size, accuracy), - Self::LinearExtrusion { - id, - frame, - accuracy, - outer, - holes, - height, - } => primitives::linear_extrusion(id, frame, outer, holes, height, accuracy), - Self::ArcEdgedExtrusion { - id, - frame, - accuracy, - outer, - holes, - height, - } => primitives::arc_edged_extrusion_with_holes( - id, frame, outer, holes, height, accuracy, - ), - Self::PolygonLoft { - id, - frame, - accuracy, - lower, - upper, - alignment, - } => super::modeling::polygon_loft( - id, - lower.into_iter().map(|point| frame.point(point)).collect(), - upper.into_iter().map(|point| frame.point(point)).collect(), - match alignment { - PolygonLoftAlignmentConfig::Auto => super::modeling::PolygonLoftAlignment::Auto, - PolygonLoftAlignmentConfig::Indexed { - upper_start, - reverse_upper, - } => super::modeling::PolygonLoftAlignment::Indexed { - upper_start, - reverse_upper, - }, - }, - accuracy, - ), - Self::BoxWithArchedOpening { - id, - frame, - accuracy, - width, - depth, - height, - opening, - } => primitives::box_with_arched_opening( - id, - frame, - width, - depth, - height, - primitives::BoxArchedOpening { - id: opening.id, - station: opening.station, - width: opening.width, - bottom: opening.bottom, - height: opening.height, - }, - accuracy, - ), - Self::PlanarPolyhedron { - id, - frame, - accuracy, - vertices, - faces, - } => { - frame.validate()?; - primitives::planar_polyhedron( - id, - vertices - .into_iter() - .map(|point| frame.point(point)) - .collect(), - faces, - accuracy, - ) - } - Self::Arc { - id, - frame, - accuracy, - radius, - start_angle, - sweep_angle, - } => primitives::arc_wire( - id, - CurveGeometry::Circle { frame, radius }, - start_angle, - sweep_angle, - accuracy, - ), - Self::EllipticalArc { - id, - frame, - accuracy, - major_radius, - minor_radius, - start_angle, - sweep_angle, - } => primitives::arc_wire( - id, - CurveGeometry::Ellipse { - frame, - major_radius, - minor_radius, - }, - start_angle, - sweep_angle, - accuracy, - ), - Self::Cylinder { - id, - frame, - accuracy, - radius, - height, - } => primitives::cylinder(id, frame, radius, height, accuracy), - Self::CylinderSector { - id, - frame, - accuracy, - radius, - height, - start_angle, - sweep_angle, - } => primitives::cylinder_sector( - id, - frame, - radius, - height, - start_angle, - sweep_angle, - accuracy, - ), - Self::Sphere { - id, - frame, - accuracy, - radius, - } => primitives::sphere(id, frame, radius, accuracy), - Self::Cone { - id, - frame, - accuracy, - radius, - height, - } => primitives::cone(id, frame, radius, height, accuracy), - Self::Frustum { - id, - frame, - accuracy, - lower_radius, - upper_radius, - height, - } => primitives::frustum(id, frame, lower_radius, upper_radius, height, accuracy), - Self::Torus { - id, - frame, - accuracy, - major_radius, - minor_radius, - } => primitives::torus(id, frame, major_radius, minor_radius, accuracy), - Self::AnnularSectorExtrusion { - id, - frame, - accuracy, - radius, - thickness, - height, - start_angle, - sweep_angle, - } => primitives::annular_sector_extrusion( - id, - frame, - radius, - thickness, - height, - start_angle, - sweep_angle, - accuracy, - ), - Self::AnnularSectorExtrusionWithOpenings { - id, - frame, - accuracy, - radius, - thickness, - height, - start_angle, - sweep_angle, - openings, - } => primitives::annular_sector_extrusion_with_openings( - id, - frame, - radius, - thickness, - height, - start_angle, - sweep_angle, - openings - .into_iter() - .map(|opening| primitives::AnnularSectorOpening { - id: opening.id, - angle: opening.angle, - width: opening.width, - bottom: opening.bottom, - height: opening.height, - }) - .collect(), - accuracy, - ), - Self::AnnularSectorExtrusionWithArchedOpening { - id, - frame, - accuracy, - radius, - thickness, - height, - start_angle, - sweep_angle, - opening, - } => primitives::annular_sector_extrusion_with_arched_opening( - id, - frame, - radius, - thickness, - height, - start_angle, - sweep_angle, - primitives::AnnularSectorOpening { - id: opening.id, - angle: opening.angle, - width: opening.width, - bottom: opening.bottom, - height: opening.height, - }, - accuracy, - ), - Self::CoaxialCircleLoft { - id, - frame, - accuracy, - lower_radius, - upper_radius, - height, - } => super::modeling::coaxial_circle_loft( - id, - frame, - lower_radius, - upper_radius, - height, - accuracy, - ), - Self::RevolvedRectangle { - id, - frame, - accuracy, - inner_radius, - outer_radius, - height, - } => super::modeling::revolve_rectangle( - id, - frame, - inner_radius, - outer_radius, - height, - accuracy, - ), - Self::ChamferedCuboid { - id, - frame, - accuracy, - size, - chamfer, - } => super::modeling::chamfered_cuboid(id, frame, size, chamfer, accuracy), - Self::FilletedCylinder { - id, - frame, - accuracy, - radius, - height, - fillet_radius, - } => super::modeling::filleted_cylinder( - id, - frame, - radius, - height, - fillet_radius, - accuracy, - ), - } - } -} - -fn js_error(error: GeometryError) -> JsValue { - JsValue::from_str( - &serde_json::to_string(&error) - .unwrap_or_else(|_| "analytic geometry error could not be serialized".into()), - ) -} - -#[wasm_bindgen(js_name = validateAnalyticBrep)] -pub fn validate_analytic_brep(serialized: &str) -> Result { - serde_json::to_string(&super::diagnostics::validate_json(serialized)) - .map_err(|e| js_error(GeometryError::InvalidGeometry(e.to_string()))) -} - -#[wasm_bindgen(js_name = tessellate_brep)] -pub fn tessellate_brep( - serialized: &str, - deflection: f64, - options_json: Option, -) -> Result { - let brep = BrepEnvelope::from_json(serialized).map_err(js_error)?; - let max_triangles = tessellation_limit(options_json).map_err(js_error)?; - tessellate_cached(&brep, deflection, max_triangles).map_err(js_error) -} - -#[wasm_bindgen] -pub struct OGAnalyticBrep { - brep: BrepEnvelope, - boolean_report: Option, -} -thread_local! { - static CACHE: RefCell = RefCell::new(TessellationCache::new(64 * 1024 * 1024)); -} -impl OGAnalyticBrep { - fn from_brep(brep: BrepEnvelope) -> Self { - Self { - brep, - boolean_report: None, - } - } -} -#[wasm_bindgen] -impl OGAnalyticBrep { - #[wasm_bindgen(constructor)] - pub fn new(serialized: &str) -> Result { - Ok(Self::from_brep( - BrepEnvelope::from_json(serialized).map_err(js_error)?, - )) - } - - pub fn from_primitive(config_json: &str) -> Result { - if config_json.len() > 64 * 1024 { - return Err(js_error(GeometryError::LimitExceeded( - "primitive config exceeds 64 KiB".into(), - ))); - } - let config: PrimitiveConfig = serde_json::from_str(config_json) - .map_err(|e| js_error(GeometryError::InvalidGeometry(e.to_string())))?; - Ok(Self::from_brep(config.build().map_err(js_error)?)) - } - - pub fn get_brep_serialized(&self) -> Result { - self.brep.to_json().map_err(js_error) - } - - pub fn export_step(&self, length_unit: &str) -> Result { - let (text, report) = - super::exchange::export_step(&self.brep, length_unit).map_err(js_error)?; - serde_json::to_string(&serde_json::json!({ "text": text, "report": report })) - .map_err(|e| js_error(GeometryError::InvalidGeometry(e.to_string()))) - } - - pub fn placed(&self, frame_json: &str, scale: f64) -> Result { - if frame_json.len() > 4096 { - return Err(js_error(GeometryError::LimitExceeded( - "placement frame exceeds 4 KiB".into(), - ))); - } - let frame: Frame3 = serde_json::from_str(frame_json) - .map_err(|e| js_error(GeometryError::InvalidGeometry(e.to_string())))?; - Ok(Self::from_brep( - super::placement::placed(&self.brep, frame, scale).map_err(js_error)?, - )) - } - - pub fn prepare_ifc_exchange(&self) -> Result { - let body = super::ifc_exchange::prepare_ifc_body(&self.brep).map_err(js_error)?; - serde_json::to_string(&body) - .map_err(|e| js_error(GeometryError::InvalidGeometry(e.to_string()))) - } - - pub fn face_normal_at(&self, face_id: u32, point: &[f64]) -> Result, JsValue> { - let point: [f64; 3] = point.try_into().map_err(|_| { - js_error(GeometryError::InvalidGeometry( - "face normal query requires three coordinates".into(), - )) - })?; - self.brep - .face_normal_at(face_id, point) - .map(|normal| normal.to_vec()) - .map_err(js_error) - } - - pub fn classify_point(&self, point: &[f64]) -> Result { - let point: [f64; 3] = point.try_into().map_err(|_| { - js_error(GeometryError::InvalidGeometry( - "point classification requires three coordinates".into(), - )) - })?; - classify_point(&self.brep, point) - .map(|classification| match classification { - PointClassification::Inside => "inside", - PointClassification::Outside => "outside", - PointClassification::Boundary => "boundary", - PointClassification::Unknown => "unknown", - }) - .map(str::to_owned) - .map_err(js_error) - } - - pub fn project_to_2d_lines( - &self, - camera_json: &str, - hlr_json: &str, - deflection: f64, - ) -> Result { - let camera: CameraParameters = serde_json::from_str(camera_json) - .map_err(|error| js_error(GeometryError::InvalidGeometry(error.to_string())))?; - let hlr: HlrOptions = serde_json::from_str(hlr_json) - .map_err(|error| js_error(GeometryError::InvalidGeometry(error.to_string())))?; - let scene = project_analytic_brep_to_scene( - &self.brep, - &camera, - &hlr, - deflection, - DEFAULT_MAX_TRIANGLES, - ) - .map_err(js_error)?; - serde_json::to_string(&scene.to_lines()) - .map_err(|error| js_error(GeometryError::InvalidGeometry(error.to_string()))) - } - - pub fn boolean_brep( - &self, - other: &OGAnalyticBrep, - operation: &str, - id: &str, - ) -> Result { - let operation = match operation { - "union" => BooleanOp::Union, - "intersection" => BooleanOp::Intersection, - "subtraction" => BooleanOp::Subtraction, - _ => { - return Err(js_error(GeometryError::InvalidGeometry( - "unknown boolean operation".into(), - ))) - } - }; - let result = - boolean_brep(&self.brep, &other.brep, operation, id.into()).map_err(js_error)?; - Ok(Self { - brep: result.brep, - boolean_report: Some(result.report), - }) - } - - pub fn subtract_planar_cutters( - &self, - cutters_json: &str, - id: &str, - ) -> Result { - if cutters_json.len() > 16 * 1024 * 1024 { - return Err(js_error(GeometryError::LimitExceeded( - "planar cutter payload exceeds 16 MiB".into(), - ))); - } - let cutters: Vec = serde_json::from_str(cutters_json) - .map_err(|error| js_error(GeometryError::InvalidGeometry(error.to_string())))?; - let result = subtract_planar_cutters(&self.brep, &cutters, id.into()).map_err(js_error)?; - Ok(Self { - brep: result.brep, - boolean_report: Some(result.report), - }) - } - - pub fn shell(&self, thickness: f64, id: &str) -> Result { - let result = shell_brep(&self.brep, thickness, id.into()).map_err(js_error)?; - Ok(Self { - brep: result.brep, - boolean_report: Some(result.report), - }) - } - - pub fn get_boolean_report_serialized(&self) -> Result, JsValue> { - self.boolean_report - .as_ref() - .map(serde_json::to_string) - .transpose() - .map_err(|e| js_error(GeometryError::InvalidGeometry(e.to_string()))) - } - - pub fn tessellate(&mut self, deflection: f64) -> Result { - tessellate_cached(&self.brep, deflection, DEFAULT_MAX_TRIANGLES).map_err(js_error) - } - - pub fn get_face_count(&self) -> u32 { - self.brep.topology.faces.len() as u32 - } - pub fn get_edge_count(&self) -> u32 { - self.brep.topology.edges.len() as u32 - } - pub fn get_id(&self) -> String { - self.brep.id.clone() - } - pub fn get_bounds(&self) -> Result, JsValue> { - Ok(match self.brep.bounds().map_err(js_error)? { - Some(bounds) => bounds - .axes - .iter() - .map(|i| i.lo) - .chain(bounds.axes.iter().map(|i| i.hi)) - .collect(), - None => Vec::new(), - }) - } - pub fn get_vertex_count(&self) -> u32 { - self.brep.topology.vertices.len() as u32 - } - pub fn get_revision(&self) -> u64 { - self.brep.revision - } -} - -#[wasm_bindgen] -pub struct OGAnalyticTessellation { - mesh: Arc, -} -#[wasm_bindgen] -impl OGAnalyticTessellation { - pub fn positions(&self) -> Vec { - self.mesh.positions.clone() - } - pub fn normals(&self) -> Vec { - self.mesh.normals.clone() - } - pub fn indices(&self) -> Vec { - self.mesh.indices.clone() - } - pub fn triangle_face_ids(&self) -> Vec { - self.mesh.triangle_face_ids.clone() - } - pub fn outline_positions(&self) -> Vec { - self.mesh.outline_positions.clone() - } - pub fn outline_edge_ids(&self) -> Vec { - self.mesh.outline_edge_ids.clone() - } - pub fn revision(&self) -> u64 { - self.mesh.revision - } - pub fn achieved_deflection(&self) -> f64 { - self.mesh.achieved_deflection - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn primitive_config_requires_all_geometry_fields() { - let config = serde_json::json!({"kind":"cylinder","id":"c","frame":{"origin":[0,0,0],"x":[1,0,0],"y":[0,1,0],"z":[0,0,1]},"accuracy":{"geometric":1e-9,"intersection":1e-10,"tessellation":0.001,"exchange":1e-5},"radius":1,"height":2}); - let c: PrimitiveConfig = serde_json::from_value(config.clone()).unwrap(); - assert_eq!(c.build().unwrap().topology.faces.len(), 3); - let mut incomplete = config; - incomplete.as_object_mut().unwrap().remove("frame"); - assert!(serde_json::from_value::(incomplete).is_err()); - } - - #[test] - fn linear_extrusion_config_builds_strict_v2_geometry() { - let config = serde_json::json!({ - "kind": "linear_extrusion", - "id": "profile", - "frame": Frame3::IDENTITY, - "accuracy": { - "geometric": 1e-9, - "intersection": 1e-10, - "tessellation": 0.001, - "exchange": 1e-5 - }, - "outer": [[0.0, 0.0], [3.0, 0.0], [3.0, 2.0], [0.0, 2.0]], - "holes": [], - "height": 1.5 - }); - let body = serde_json::from_value::(config.clone()) - .unwrap() - .build() - .unwrap(); - assert_eq!(body.schema_version, 2); - assert_eq!(body.topology.faces.len(), 6); - let mut incomplete = config; - incomplete.as_object_mut().unwrap().remove("holes"); - assert!(serde_json::from_value::(incomplete).is_err()); - } - - #[test] - fn arc_edged_extrusion_config_preserves_circle_edges() { - let config = serde_json::json!({ - "kind": "arc_edged_extrusion", - "id": "curved-profile", - "frame": Frame3::IDENTITY, - "accuracy": { - "geometric": 1e-9, - "intersection": 1e-10, - "tessellation": 0.001, - "exchange": 1e-5 - }, - "outer": [ - {"kind":"arc","center":[0.0,0.0],"radius":2.0,"start_angle":0.0,"sweep_angle":1.5707963267948966}, - {"kind":"line","from":[0.0,2.0],"to":[0.0,1.5]}, - {"kind":"arc","center":[0.0,0.0],"radius":1.5,"start_angle":1.5707963267948966,"sweep_angle":-1.5707963267948966}, - {"kind":"line","from":[1.5,0.0],"to":[2.0,0.0]} - ], - "height": 3.0 - }); - let body = serde_json::from_value::(config) - .unwrap() - .build() - .unwrap(); - assert_eq!(body.topology.faces.len(), 6); - assert_eq!( - body.geometry - .surfaces - .iter() - .filter(|surface| matches!( - surface, - crate::analytic::SurfaceGeometry::Cylinder { .. } - )) - .count(), - 2 - ); - } - - #[test] - fn polygon_loft_config_builds_capped_planar_brep() { - let config = serde_json::json!({ - "kind": "polygon_loft", - "id": "loft", - "frame": Frame3::IDENTITY, - "accuracy": { - "geometric": 1e-9, - "intersection": 1e-10, - "tessellation": 0.001, - "exchange": 1e-5 - }, - "lower": [[-1.0, -1.0, 0.0], [1.0, -1.0, 0.0], [1.0, 1.0, 0.0], [-1.0, 1.0, 0.0]], - "upper": [[0.5, -0.5, 2.0], [0.5, 0.5, 2.0], [-0.5, 0.5, 2.0], [-0.5, -0.5, 2.0]], - "alignment": { "kind": "auto" } - }); - let body = serde_json::from_value::(config) - .unwrap() - .build() - .unwrap(); - assert_eq!(body.topology.faces.len(), 6); - body.validate().unwrap(); - } - - #[test] - fn annular_sector_extrusion_opening_config_is_strict_and_analytic() { - let config = serde_json::json!({ - "kind": "annular_sector_extrusion_with_openings", - "id": "host", - "frame": Frame3::IDENTITY, - "accuracy": { - "geometric": 1e-9, - "intersection": 1e-10, - "tessellation": 0.001, - "exchange": 1e-5 - }, - "radius": 3.0, - "thickness": 0.3, - "height": 3.0, - "start_angle": 0.0, - "sweep_angle": 2.0, - "openings": [{ - "id": "raised_cutout", - "angle": 1.0, - "width": 0.8, - "bottom": 0.9, - "height": 1.2 - }] - }); - let body = serde_json::from_value::(config.clone()) - .unwrap() - .build() - .unwrap(); - assert_eq!(body.schema_version, 2); - assert_eq!(body.topology.faces.len(), 10); - assert_eq!(body.topology.faces[0].trim.holes.len(), 1); - let mut incomplete = config; - incomplete.as_object_mut().unwrap().remove("openings"); - assert!(serde_json::from_value::(incomplete).is_err()); - } - - #[test] - fn tessellation_options_are_strict_and_bounded() { - assert_eq!(tessellation_limit(None).unwrap(), DEFAULT_MAX_TRIANGLES); - assert_eq!( - tessellation_limit(Some(r#"{"max_triangles":123}"#.into())).unwrap(), - 123 - ); - assert!(tessellation_limit(Some(r#"{"unknown":1}"#.into())).is_err()); - assert!(tessellation_limit(Some(r#"{"max_triangles":0}"#.into())).is_err()); - } -} diff --git a/main/opengeometry/src/bindings/errors.rs b/main/opengeometry/src/bindings/errors.rs new file mode 100644 index 0000000..29b39a9 --- /dev/null +++ b/main/opengeometry/src/bindings/errors.rs @@ -0,0 +1,37 @@ +use crate::world_graph::{ErrorCode, ErrorDetails, GraphError}; +use serde::Serialize; +use wasm_bindgen::JsValue; + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ErrorDto { + pub(crate) code: ErrorCode, + pub(crate) message: String, + pub(crate) details: serde_json::Value, +} + +pub(crate) fn dto(error: GraphError) -> ErrorDto { + let details = match error.details() { + ErrorDetails::None => serde_json::json!({}), + details => serde_json::to_value(details).unwrap_or(serde_json::json!({})), + }; + ErrorDto { + code: error.error_code(), + message: error.message(), + details, + } +} + +pub(super) fn json(error: GraphError) -> JsValue { + let rendered = serde_json::to_value(dto(error)) + .map(|value| value.to_string()) + .unwrap_or_default(); + JsValue::from_str(&rendered) +} + +pub(super) fn serialization(error: serde_json::Error) -> JsValue { + json(GraphError::code( + ErrorCode::InvalidGeometry, + error.to_string(), + )) +} diff --git a/main/opengeometry/src/bindings/mod.rs b/main/opengeometry/src/bindings/mod.rs new file mode 100644 index 0000000..6317cb2 --- /dev/null +++ b/main/opengeometry/src/bindings/mod.rs @@ -0,0 +1,10 @@ +mod errors; +mod panic; +mod params; +mod tessellator; +#[cfg(test)] +mod tests; +mod world_graph; + +pub use tessellator::OGTessellator; +pub use world_graph::OGWorldGraph; diff --git a/main/opengeometry/src/bindings/panic.rs b/main/opengeometry/src/bindings/panic.rs new file mode 100644 index 0000000..0139cde --- /dev/null +++ b/main/opengeometry/src/bindings/panic.rs @@ -0,0 +1,14 @@ +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::JsValue; + +pub(super) fn install_panic_hook() { + #[cfg(target_arch = "wasm32")] + std::panic::set_hook(Box::new(|info| { + let _ = js_sys::Reflect::set( + &js_sys::global(), + &JsValue::from_str("__opengeometryPanic"), + &JsValue::from_str(&info.to_string()), + ); + console_error_panic_hook::hook(info); + })); +} diff --git a/main/opengeometry/src/bindings/params/mod.rs b/main/opengeometry/src/bindings/params/mod.rs new file mode 100644 index 0000000..fa6959b --- /dev/null +++ b/main/opengeometry/src/bindings/params/mod.rs @@ -0,0 +1,107 @@ +use crate::world_graph::{ErrorCode, GraphError}; +use serde::de::{self, DeserializeOwned, Deserializer as _, IgnoredAny, SeqAccess, Visitor}; +use std::fmt; + +#[cfg(test)] +mod tests; + +struct NodeFault<'a> { + fault: &'a mut Option, +} + +impl<'de> Visitor<'de> for NodeFault<'_> { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("an array of node ids") + } + + fn visit_seq>(self, mut seq: A) -> Result, A::Error> { + let mut values = Vec::new(); + while values.len() < 100_000 { + let Some(value) = seq.next_element::()? else { + return Ok(values); + }; + if let Err(error) = id(&value) { + *self.fault = Some(error); + return Err(de::Error::custom("ogId exceeds 1024 characters")); + } + values.push(value); + } + if seq.next_element::()?.is_some() { + *self.fault = Some(GraphError::code( + ErrorCode::LimitExceeded, + "exportStep nodes exceed 100,000 ids", + )); + return Err(de::Error::custom("exportStep nodes exceed 100,000 ids")); + } + Ok(values) + } +} + +pub(super) fn parse(json: &str) -> Result { + if json.len() > 64 * 1024 { + return Err(GraphError::code( + ErrorCode::LimitExceeded, + "params JSON exceeds 64 KiB", + )); + } + serde_json::from_str(json).map_err(|error| { + GraphError::code( + ErrorCode::InvalidParameter, + format!("invalid params JSON: {error}"), + ) + }) +} + +pub(super) fn id(value: &str) -> Result<(), GraphError> { + if value.chars().count() > 1024 { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "ogId exceeds 1024 characters", + )); + } + Ok(()) +} + +pub(super) fn ids(values: &[String]) -> Result<(), GraphError> { + for value in values { + id(value)?; + } + Ok(()) +} + +pub(super) fn points(values: &[f64]) -> Result, GraphError> { + if values.len() % 3 != 0 || values.len() / 3 > 100_000 { + return Err(GraphError::code( + ErrorCode::LimitExceeded, + "polyline point limit", + )); + } + if values.iter().any(|value| !value.is_finite()) { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "polyline point is nonfinite", + )); + } + Ok(values + .chunks_exact(3) + .map(|point| [point[0], point[1], point[2]]) + .collect()) +} + +pub(super) fn nodes(json: &str) -> Result, GraphError> { + let mut fault = None; + let mut deserializer = serde_json::Deserializer::from_str(json); + let values = deserializer + .deserialize_seq(NodeFault { fault: &mut fault }) + .and_then(|values| deserializer.end().map(|()| values)); + values.map_err(|error| { + fault.unwrap_or_else(|| { + GraphError::code( + ErrorCode::InvalidParameter, + format!("invalid params JSON: {error}"), + ) + }) + }) +} diff --git a/main/opengeometry/src/bindings/params/tests.rs b/main/opengeometry/src/bindings/params/tests.rs new file mode 100644 index 0000000..04f9deb --- /dev/null +++ b/main/opengeometry/src/bindings/params/tests.rs @@ -0,0 +1,42 @@ +use super::nodes; +use crate::world_graph::ErrorCode; + +fn node_list(count: usize, id: &str) -> String { + serde_json::to_string(&vec![id; count]).unwrap() +} + +#[test] +fn export_node_list_is_exempt_from_the_params_cap() { + let json = node_list(2_000, "00000000-0000-4000-8000-000000000000"); + assert_eq!(json.len(), 78_001); + assert_eq!(nodes(&json).unwrap().len(), 2_000); +} + +#[test] +fn export_node_list_rejects_more_than_100000_ids() { + assert_eq!(nodes(&node_list(100_000, "a")).unwrap().len(), 100_000); + assert_eq!( + nodes(&node_list(100_001, "a")).unwrap_err().error_code(), + ErrorCode::LimitExceeded + ); +} + +#[test] +fn export_node_id_over_1024_chars_is_invalid_parameter() { + assert!(nodes(&node_list(1, &"é".repeat(1024))).is_ok()); + assert_eq!( + nodes(&node_list(1, &"a".repeat(1025))) + .unwrap_err() + .error_code(), + ErrorCode::InvalidParameter + ); +} + +#[test] +fn malformed_export_node_list_is_invalid_parameter() { + for json in ["[\"a\"", "[\"a\"] x", "{\"a\":1}", "[1]", ""] { + let error = nodes(json).unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::InvalidParameter); + assert!(error.to_string().contains("invalid params JSON: ")); + } +} diff --git a/main/opengeometry/src/bindings/tessellator.rs b/main/opengeometry/src/bindings/tessellator.rs new file mode 100644 index 0000000..5aaa8a1 --- /dev/null +++ b/main/opengeometry/src/bindings/tessellator.rs @@ -0,0 +1,105 @@ +use super::{errors, panic}; +use crate::tessellation::{display::DisplayBuffers, SnapshotStore}; +use js_sys::{Float32Array, Float64Array, Object, Reflect, Uint32Array}; +use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; + +fn field(object: &Object, name: &str, value: &JsValue) -> Result<(), JsValue> { + Reflect::set(object, &JsValue::from_str(name), value).map(|_| ()) +} + +pub(super) fn buffers_to_js(buffers: DisplayBuffers) -> Result { + let result = Object::new(); + field( + &result, + "positions", + &Float32Array::from(buffers.positions.as_slice()), + )?; + field( + &result, + "normals", + &Float32Array::from(buffers.normals.as_slice()), + )?; + field( + &result, + "indices", + &Uint32Array::from(buffers.indices.as_slice()), + )?; + field( + &result, + "faceRanges", + &Uint32Array::from(buffers.face_ranges.as_slice()), + )?; + field( + &result, + "outline", + &Float32Array::from(buffers.outline.as_slice()), + )?; + field( + &result, + "edgeIds", + &Uint32Array::from(buffers.edge_ids.as_slice()), + )?; + field( + &result, + "origin", + &Float64Array::from(buffers.origin.as_slice()), + )?; + field( + &result, + "revision", + &JsValue::from_f64(buffers.revision as f64), + )?; + field(&result, "bucket", &JsValue::from_f64(buffers.bucket))?; + field( + &result, + "achievedDeflection", + &JsValue::from_f64(buffers.achieved_deflection), + )?; + field( + &result, + "triangles", + &JsValue::from_f64(f64::from(buffers.triangles)), + )?; + Ok(result.into()) +} + +#[wasm_bindgen] +pub struct OGTessellator { + inner: SnapshotStore, +} + +#[wasm_bindgen] +impl OGTessellator { + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + panic::install_panic_hook(); + Self { + inner: SnapshotStore::new(), + } + } + + pub fn load(&mut self, bytes: &[u8]) -> Result { + self.inner + .load(bytes) + .map_err(|error| errors::json(error.into())) + } + + pub fn buffers(&self, slot: u32, bucket: f64, max_triangles: u32) -> Result { + buffers_to_js( + self.inner + .buffers(slot, bucket, max_triangles as usize) + .map_err(|error| errors::json(error.into()))?, + ) + } + + #[wasm_bindgen(js_name = drop)] + pub fn release(&mut self, slot: u32) -> bool { + self.inner.release(slot) + } +} + +impl Default for OGTessellator { + fn default() -> Self { + Self::new() + } +} diff --git a/main/opengeometry/src/bindings/tests.rs b/main/opengeometry/src/bindings/tests.rs new file mode 100644 index 0000000..d0faccc --- /dev/null +++ b/main/opengeometry/src/bindings/tests.rs @@ -0,0 +1,257 @@ +use super::errors::dto; +use super::OGWorldGraph; +use crate::brep::{coarse_accuracy, GeometryError}; +use crate::math::MathError; +use crate::tessellation::display::{bucket_floor, static_bucket}; +use crate::world_graph::{ + ChangeSet, CopyOptions, CreateOptions, ErrorCode, ErrorDetails, GraphError, Plane, Primitive, + StepOptions, Transform, WorldGraph, +}; +use serde_json::{json, Value}; + +fn rendered(error: GraphError) -> String { + serde_json::to_value(dto(error)).unwrap().to_string() +} + +#[test] +fn dto_carries_the_code_and_details_of_every_geometry_error_variant() { + let cases = [ + ( + GeometryError::Math(MathError::DivisionByZero), + "InvalidGeometry", + json!({"math": "DivisionByZero"}), + ), + ( + GeometryError::InvalidGeometry("bad".into()), + "InvalidGeometry", + json!({}), + ), + ( + GeometryError::InvalidTopology("bad".into()), + "InvalidTopology", + json!({}), + ), + ( + GeometryError::UnsupportedGeometry("bad".into()), + "UnsupportedGeometry", + json!({}), + ), + ( + GeometryError::AmbiguousProfileAlignment("bad".into()), + "InvalidParameter", + json!({}), + ), + ( + GeometryError::CoverageGap { + families: ["plane".into(), "torus".into()], + }, + "CoverageGap", + json!({"families": ["plane", "torus"]}), + ), + ( + GeometryError::SingularParameterization, + "InvalidGeometry", + json!({}), + ), + ( + GeometryError::MissingReference { + kind: "edge".into(), + index: 3, + }, + "InvalidTopology", + json!({"kind": "edge", "index": 3}), + ), + ( + GeometryError::UnsupportedSchema { found: 9 }, + "InvalidTopology", + json!({}), + ), + ( + GeometryError::UnresolvedIntersection("bad".into()), + "UnresolvedIntersection", + json!({}), + ), + ( + GeometryError::UnresolvedTessellation("bad".into()), + "UnresolvedTessellation", + json!({}), + ), + ( + GeometryError::LimitExceeded("bad".into()), + "LimitExceeded", + json!({}), + ), + ]; + for (error, code, details) in cases { + let value = serde_json::to_value(dto(GraphError::Geometry(error.clone()))).unwrap(); + assert_eq!( + value, + json!({"code": code, "message": error.to_string(), "details": details}), + "{error:?}" + ); + } +} + +#[test] +fn dto_renders_shared_shape_errors_byte_identical_to_the_untyped_form() { + let error = GraphError::Code { + code: ErrorCode::SharedShape, + message: "shape has multiple instances".into(), + details: ErrorDetails::SharedShape { + shape_id: Some("shape-1".into()), + instance_count: 2, + sharing: vec!["host".into(), "host-copy".into()], + }, + }; + assert_eq!( + rendered(error), + r#"{"code":"SharedShape","details":{"instanceCount":2,"shapeId":"shape-1","sharing":["host","host-copy"]},"message":"shape has multiple instances"}"# + ); +} + +#[test] +fn dto_renders_coverage_gap_errors_byte_identical_to_the_untyped_form() { + let error = GraphError::Geometry(GeometryError::CoverageGap { + families: ["plane".into(), "torus".into()], + }); + assert_eq!( + rendered(error), + r#"{"code":"CoverageGap","details":{"families":["plane","torus"]},"message":"CoverageGap { families: [\"plane\", \"torus\"] }"}"# + ); +} + +fn assert_camel_case_keys(value: &Value) { + match value { + Value::Object(map) => { + for (key, inner) in map { + let mut bytes = key.bytes(); + let first = bytes.next().unwrap_or(b'_'); + assert!( + first.is_ascii_lowercase() && bytes.all(|byte| byte.is_ascii_alphanumeric()), + "{key} in {value}" + ); + assert_camel_case_keys(inner); + } + } + Value::Array(items) => items.iter().for_each(assert_camel_case_keys), + _ => {} + } +} + +fn unit_cube(graph: &mut WorldGraph) -> (String, ChangeSet) { + graph + .create_primitive( + Primitive::Cuboid { + width: 1.0, + height: 1.0, + depth: 1.0, + }, + CreateOptions { + og_id: Some("cube".into()), + ..CreateOptions::default() + }, + ) + .unwrap() +} + +#[test] +fn bindings_payloads_are_camel_case() { + let accuracy = coarse_accuracy(1e-6); + let mut graph = WorldGraph::new(accuracy).unwrap(); + let created = unit_cube(&mut graph); + let plane = Plane { + origin: Some([0.0; 3]), + normal: Some([0.0, 0.0, 1.0]), + x_direction: Some([1.0, 0.0, 0.0]), + }; + let error = GraphError::Code { + code: ErrorCode::InvalidOperand, + message: "bad tool".into(), + details: ErrorDetails::Operate { + handlers: vec!["box".into()], + tool_index: 0, + og_ids: vec!["cube".into()], + }, + }; + let payloads = [ + serde_json::to_value(&created).unwrap(), + serde_json::to_value(graph.placement("cube").unwrap()).unwrap(), + serde_json::to_value(Transform::Place { + origin: Some([0.0; 3]), + x_direction: Some([1.0, 0.0, 0.0]), + normal: Some([0.0, 0.0, 1.0]), + scale: Some(1.0), + }) + .unwrap(), + serde_json::to_value(plane).unwrap(), + serde_json::to_value(CreateOptions { + og_id: Some("cube".into()), + parent: Some("root".into()), + plane: Some(plane), + body_type: None, + }) + .unwrap(), + serde_json::to_value(CopyOptions { + og_id: Some("copy".into()), + parent: Some(Some("root".into())), + }) + .unwrap(), + serde_json::to_value(StepOptions::default()).unwrap(), + serde_json::to_value(accuracy).unwrap(), + serde_json::to_value(dto(error)).unwrap(), + ]; + for payload in &payloads { + assert_camel_case_keys(payload); + } +} + +#[test] +fn place_accepts_camel_case_x_direction_and_rejects_snake_case() { + assert!(serde_json::from_str::(r#"{"kind":"Place","xDirection":[1,0,0]}"#).is_ok()); + assert!( + serde_json::from_str::(r#"{"kind":"Place","x_direction":[1,0,0]}"#).is_err() + ); + assert!(serde_json::from_str::(r#"{"ogId":"a"}"#).is_ok()); + assert!(serde_json::from_str::(r#"{"ogId":"a"}"#).is_ok()); + assert!(serde_json::from_str::(r#"{"og_id":"a"}"#).is_err()); + assert!(serde_json::from_str::(r#"{"og_id":"a"}"#).is_err()); + assert!(serde_json::from_str::(r#"{"xDirection":[1,0,0]}"#).is_ok()); + assert!(serde_json::from_str::(r#"{"x_direction":[1,0,0]}"#).is_err()); +} + +#[test] +fn display_buckets_reports_the_kernel_floor_and_static_bucket() { + let mut binding = OGWorldGraph::new( + r#"{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6}"#, + ) + .unwrap(); + binding + .create_primitive( + r#"{"kind":"Cuboid","width":1,"height":1,"depth":1}"#, + r#"{"ogId":"cube"}"#, + ) + .unwrap(); + let buckets: Value = serde_json::from_str(&binding.display_buckets("cube").unwrap()).unwrap(); + let keys: Vec<&str> = buckets + .as_object() + .unwrap() + .keys() + .map(String::as_str) + .collect(); + assert_eq!(keys, ["floor", "static"]); + let mut graph = WorldGraph::new(coarse_accuracy(1e-6)).unwrap(); + unit_cube(&mut graph); + let brep = graph.brep("cube").unwrap(); + let floor = bucket_floor(&brep).unwrap(); + let bucket = static_bucket(&brep).unwrap(); + assert_eq!(floor.to_bits(), 2.0_f64.powi(-21).to_bits()); + assert_eq!(bucket.to_bits(), 2.0_f64.powi(-9).to_bits()); + assert_eq!( + buckets["floor"].as_f64().unwrap().to_bits(), + floor.to_bits() + ); + assert_eq!( + buckets["static"].as_f64().unwrap().to_bits(), + bucket.to_bits() + ); +} diff --git a/main/opengeometry/src/bindings/world_graph/create.rs b/main/opengeometry/src/bindings/world_graph/create.rs new file mode 100644 index 0000000..0fcff0a --- /dev/null +++ b/main/opengeometry/src/bindings/world_graph/create.rs @@ -0,0 +1,80 @@ +use super::{check_options, created, OGWorldGraph}; +use crate::bindings::{errors, params}; +use crate::world_graph::{CreateOptions, CreatingOperation, ErrorCode, GraphError, Primitive}; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +#[wasm_bindgen] +impl OGWorldGraph { + #[wasm_bindgen(js_name = createPrimitive)] + pub fn create_primitive( + &mut self, + primitive_json: &str, + options_json: &str, + ) -> Result { + let primitive: Primitive = params::parse(primitive_json).map_err(errors::json)?; + let options: CreateOptions = params::parse(options_json).map_err(errors::json)?; + check_options(&options).map_err(errors::json)?; + if matches!(primitive, Primitive::Polyline { .. }) { + return Err(errors::json(GraphError::code( + ErrorCode::InvalidParameter, + "polyline points must use createPolyline", + ))); + } + created( + self.inner + .create_primitive(primitive, options) + .map_err(errors::json)?, + ) + } + + #[wasm_bindgen(js_name = createPolyline)] + pub fn create_polyline( + &mut self, + points: &[f64], + closed: bool, + options_json: &str, + ) -> Result { + let options: CreateOptions = params::parse(options_json).map_err(errors::json)?; + check_options(&options).map_err(errors::json)?; + let points = params::points(points).map_err(errors::json)?; + created( + self.inner + .create_primitive(Primitive::Polyline { points, closed }, options) + .map_err(errors::json)?, + ) + } + + #[wasm_bindgen(js_name = createOperation)] + pub fn create_operation( + &mut self, + operation_json: &str, + options_json: &str, + ) -> Result { + let operation: CreatingOperation = params::parse(operation_json).map_err(errors::json)?; + let options: CreateOptions = params::parse(options_json).map_err(errors::json)?; + check_options(&options).map_err(errors::json)?; + if matches!(&operation, CreatingOperation::Extrude { holes, .. } if holes.len() > 1_000) { + return Err(errors::json(GraphError::code( + ErrorCode::LimitExceeded, + "profile hole limit", + ))); + } + created( + self.inner + .create_operation(operation, options) + .map_err(errors::json)?, + ) + } + + #[wasm_bindgen(js_name = createSystemAssembly)] + pub fn create_system_assembly(&mut self, options_json: &str) -> Result { + let options: CreateOptions = params::parse(options_json).map_err(errors::json)?; + check_options(&options).map_err(errors::json)?; + created( + self.inner + .create_system_assembly(options) + .map_err(errors::json)?, + ) + } +} diff --git a/main/opengeometry/src/bindings/world_graph/export.rs b/main/opengeometry/src/bindings/world_graph/export.rs new file mode 100644 index 0000000..5675e2d --- /dev/null +++ b/main/opengeometry/src/bindings/world_graph/export.rs @@ -0,0 +1,31 @@ +use super::{serialise, OGWorldGraph}; +use crate::bindings::{errors, params}; +use crate::world_graph::StepOptions; +use js_sys::{Object, Reflect}; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +#[wasm_bindgen] +impl OGWorldGraph { + #[wasm_bindgen(js_name = exportStep)] + pub fn export_step(&self, nodes_json: &str, options_json: &str) -> Result { + let nodes = params::nodes(nodes_json).map_err(errors::json)?; + let options: StepOptions = params::parse(options_json).map_err(errors::json)?; + let (text, report) = self + .inner + .export_step(&nodes, &options) + .map_err(errors::json)?; + let result = Object::new(); + Reflect::set( + &result, + &JsValue::from_str("text"), + &JsValue::from_str(&text), + )?; + Reflect::set( + &result, + &JsValue::from_str("reportJson"), + &JsValue::from_str(&serialise(&report)?), + )?; + Ok(result.into()) + } +} diff --git a/main/opengeometry/src/bindings/world_graph/hierarchy.rs b/main/opengeometry/src/bindings/world_graph/hierarchy.rs new file mode 100644 index 0000000..bc1ba5e --- /dev/null +++ b/main/opengeometry/src/bindings/world_graph/hierarchy.rs @@ -0,0 +1,110 @@ +use super::{changed, check_copy, created, serialise, OGWorldGraph}; +use crate::bindings::{errors, params}; +use crate::world_graph::CopyOptions; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +#[wasm_bindgen] +impl OGWorldGraph { + pub fn node(&self, og_id: &str) -> Result { + let node = self.inner.node(og_id).map_err(errors::json)?; + let shape = if node.shape.is_some() { + Some(self.inner.shape(og_id).map_err(errors::json)?) + } else { + None + }; + let body_type = shape + .map(|value| value.brep.body_type()) + .transpose() + .map_err(|error| errors::json(error.into()))? + .map(|kind| format!("{kind:?}")); + serialise(&serde_json::json!({ + "ogId": node.og_id, + "handle": node.handle, + "generation": node.generation, + "parent": node.parent, + "children": node.children, + "kind": node.kind, + "shapeId": node.shape, + "shapeRevision": shape.map(|value| value.revision), + "bodyType": body_type, + })) + } + + #[wasm_bindgen(js_name = nodeByHandle)] + pub fn node_by_handle(&self, handle: u32, generation: u32) -> Result { + let node = self + .inner + .node_by_handle(handle, generation) + .map_err(errors::json)?; + self.node(&node.og_id) + } + + pub fn children(&self, og_id: &str) -> Result { + serialise(&self.inner.children(og_id).map_err(errors::json)?) + } + + pub fn parent(&self, og_id: &str) -> Result { + serialise(&self.inner.parent(og_id).map_err(errors::json)?) + } + + #[wasm_bindgen(js_name = addChild)] + pub fn add_child( + &mut self, + parent: &str, + children_json: &str, + keep_world: bool, + ) -> Result { + let children: Vec = params::parse(children_json).map_err(errors::json)?; + params::ids(&children).map_err(errors::json)?; + changed( + self.inner + .add_child(parent, &children, keep_world) + .map_err(errors::json)?, + ) + } + + #[wasm_bindgen(js_name = removeChild)] + pub fn remove_child( + &mut self, + parent: &str, + child: &str, + keep_world: bool, + ) -> Result { + changed( + self.inner + .remove_child(parent, child, keep_world) + .map_err(errors::json)?, + ) + } + + pub fn instance(&mut self, source: &str, options_json: &str) -> Result { + let options: CopyOptions = params::parse(options_json).map_err(errors::json)?; + check_copy(&options).map_err(errors::json)?; + created(self.inner.instance(source, options).map_err(errors::json)?) + } + + pub fn duplicate(&mut self, source: &str, options_json: &str) -> Result { + let options: CopyOptions = params::parse(options_json).map_err(errors::json)?; + check_copy(&options).map_err(errors::json)?; + created( + self.inner + .duplicate(source, options) + .map_err(errors::json)?, + ) + } + + #[wasm_bindgen(js_name = makeUnique)] + pub fn make_unique(&mut self, og_id: &str) -> Result { + serialise(&self.inner.make_unique(og_id).map_err(errors::json)?) + } + + #[wasm_bindgen(js_name = instanceCount)] + pub fn instance_count(&self, og_id: &str) -> Result { + self.inner.instance_count(og_id).map_err(errors::json) + } + + pub fn dispose(&mut self, og_id: &str) -> Result { + changed(self.inner.dispose(og_id).map_err(errors::json)?) + } +} diff --git a/main/opengeometry/src/bindings/world_graph/marks.rs b/main/opengeometry/src/bindings/world_graph/marks.rs new file mode 100644 index 0000000..4b33e81 --- /dev/null +++ b/main/opengeometry/src/bindings/world_graph/marks.rs @@ -0,0 +1,75 @@ +use super::{changed, serialise, OGWorldGraph}; +use crate::bindings::errors; +use crate::world_graph::{ErrorCode, GraphError}; +use js_sys::{Float64Array, Object, Reflect}; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +#[wasm_bindgen] +impl OGWorldGraph { + pub fn mark(&mut self) -> Result { + let slot = self.next_mark; + self.next_mark = self.next_mark.checked_add(1).ok_or_else(|| { + errors::json(GraphError::code( + ErrorCode::LimitExceeded, + "mark handle limit", + )) + })?; + let mark = self.inner.mark().map_err(errors::json)?; + self.marks.insert(slot, mark); + Ok(slot) + } + + pub fn rollback(&mut self, slot: u32) -> Result { + let mark = *self.marks.get(&slot).ok_or_else(|| { + errors::json(GraphError::code( + ErrorCode::InvalidMark, + "mark handle is stale", + )) + })?; + let changes = self.inner.rollback(mark).map_err(errors::json)?; + self.marks.retain(|s, _| *s <= slot); + changed(changes) + } + + pub fn release(&mut self, slot: u32) -> Result<(), JsValue> { + let mark = *self.marks.get(&slot).ok_or_else(|| { + errors::json(GraphError::code( + ErrorCode::InvalidMark, + "mark handle is stale", + )) + })?; + self.inner.release(mark).map_err(errors::json)?; + self.marks.remove(&slot); + Ok(()) + } + + #[wasm_bindgen(js_name = markStats)] + pub fn mark_stats(&self) -> Result { + let stats = self.inner.mark_stats(); + serialise( + &serde_json::json!({"liveMarks": stats.live_marks, "retainedRevisions": stats.retained_revisions}), + ) + } + + #[wasm_bindgen(js_name = changesSince)] + pub fn changes_since(&self, revision: u64) -> Result { + let changes = self.inner.changes_since(revision).map_err(errors::json)?; + let mut matrices = Vec::with_capacity((changes.added.len() + changes.changed.len()) * 16); + for node in changes.added.iter().chain(&changes.changed) { + matrices.extend(self.inner.world_matrix(&node.og_id).map_err(errors::json)?); + } + let result = Object::new(); + Reflect::set( + &result, + &JsValue::from_str("changesJson"), + &JsValue::from_str(&serialise(&changes)?), + )?; + Reflect::set( + &result, + &JsValue::from_str("matrices"), + &Float64Array::from(matrices.as_slice()), + )?; + Ok(result.into()) + } +} diff --git a/main/opengeometry/src/bindings/world_graph/mod.rs b/main/opengeometry/src/bindings/world_graph/mod.rs new file mode 100644 index 0000000..18a7c7c --- /dev/null +++ b/main/opengeometry/src/bindings/world_graph/mod.rs @@ -0,0 +1,66 @@ +mod create; +mod export; +mod hierarchy; +mod marks; +mod placement; +mod rebuild; +mod shapes; + +use crate::bindings::{errors, panic, params}; +use crate::brep::Accuracy; +use crate::world_graph::{ChangeSet, CopyOptions, CreateOptions, GraphError, MarkId, WorldGraph}; +use serde::Serialize; +use std::collections::BTreeMap; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +fn serialise(value: &T) -> Result { + serde_json::to_string(value).map_err(errors::serialization) +} + +fn changed(changes: ChangeSet) -> Result { + serialise(&changes) +} + +fn created(value: (String, ChangeSet)) -> Result { + serialise(&serde_json::json!({"ogId": value.0, "changes": value.1})) +} + +fn check_options(options: &CreateOptions) -> Result<(), GraphError> { + if let Some(id) = &options.og_id { + params::id(id)?; + } + if let Some(parent) = &options.parent { + params::id(parent)?; + } + Ok(()) +} + +fn check_copy(options: &CopyOptions) -> Result<(), GraphError> { + if let Some(id) = &options.og_id { + params::id(id)?; + } + Ok(()) +} + +#[wasm_bindgen] +pub struct OGWorldGraph { + inner: WorldGraph, + marks: BTreeMap, + next_mark: u32, +} + +#[wasm_bindgen] +impl OGWorldGraph { + #[wasm_bindgen(constructor)] + pub fn new(accuracy_json: &str) -> Result { + panic::install_panic_hook(); + let accuracy: Accuracy = params::parse(accuracy_json).map_err(errors::json)?; + let inner = WorldGraph::new(accuracy).map_err(errors::json)?; + Ok(Self { + inner, + marks: BTreeMap::new(), + next_mark: 0, + }) + } +} diff --git a/main/opengeometry/src/bindings/world_graph/placement.rs b/main/opengeometry/src/bindings/world_graph/placement.rs new file mode 100644 index 0000000..79beb81 --- /dev/null +++ b/main/opengeometry/src/bindings/world_graph/placement.rs @@ -0,0 +1,69 @@ +use super::{changed, serialise, OGWorldGraph}; +use crate::bindings::{errors, params}; +use crate::world_graph::{EditScope, ModifyingOperation, Transform}; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +#[wasm_bindgen] +impl OGWorldGraph { + pub fn operate( + &mut self, + og_id: &str, + operation_json: &str, + tools_json: &str, + scope_json: &str, + ) -> Result { + params::id(og_id).map_err(errors::json)?; + let operation: ModifyingOperation = params::parse(operation_json).map_err(errors::json)?; + let tools: Vec = params::parse(tools_json).map_err(errors::json)?; + params::ids(&tools).map_err(errors::json)?; + let scope: EditScope = params::parse(scope_json).map_err(errors::json)?; + changed( + self.inner + .operate(og_id, operation, &tools, scope) + .map_err(errors::json)?, + ) + } + + pub fn transform(&mut self, og_id: &str, transform_json: &str) -> Result { + params::id(og_id).map_err(errors::json)?; + let transform: Transform = params::parse(transform_json).map_err(errors::json)?; + changed( + self.inner + .transform(og_id, transform) + .map_err(errors::json)?, + ) + } + + pub fn placement(&self, og_id: &str) -> Result { + serialise(&self.inner.placement(og_id).map_err(errors::json)?) + } + + #[wasm_bindgen(js_name = worldPlacement)] + pub fn world_placement(&self, og_id: &str) -> Result { + serialise( + &self + .inner + .world_placement_form(og_id) + .map_err(errors::json)?, + ) + } + + #[wasm_bindgen(js_name = worldMatrix)] + pub fn world_matrix(&self, og_id: &str) -> Result, JsValue> { + Ok(self + .inner + .world_matrix(og_id) + .map_err(errors::json)? + .to_vec()) + } + + pub fn bounds(&self, og_id: &str) -> Result { + serialise(&self.inner.bounds(og_id).map_err(errors::json)?) + } + + #[wasm_bindgen(js_name = localBounds)] + pub fn local_bounds(&self, og_id: &str) -> Result { + serialise(&self.inner.local_bounds(og_id).map_err(errors::json)?) + } +} diff --git a/main/opengeometry/src/bindings/world_graph/rebuild.rs b/main/opengeometry/src/bindings/world_graph/rebuild.rs new file mode 100644 index 0000000..64d7de8 --- /dev/null +++ b/main/opengeometry/src/bindings/world_graph/rebuild.rs @@ -0,0 +1,72 @@ +use super::{changed, OGWorldGraph}; +use crate::bindings::{errors, params}; +use crate::world_graph::{CreatingOperation, EditScope, ErrorCode, GraphError, Primitive}; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +#[wasm_bindgen] +impl OGWorldGraph { + #[wasm_bindgen(js_name = rebuildPrimitive)] + pub fn rebuild_primitive( + &mut self, + og_id: &str, + primitive_json: &str, + scope_json: &str, + ) -> Result { + params::id(og_id).map_err(errors::json)?; + let primitive: Primitive = params::parse(primitive_json).map_err(errors::json)?; + if matches!(primitive, Primitive::Polyline { .. }) { + return Err(errors::json(GraphError::code( + ErrorCode::InvalidParameter, + "polyline points must use rebuildPolyline", + ))); + } + let scope: EditScope = params::parse(scope_json).map_err(errors::json)?; + changed( + self.inner + .rebuild_primitive(og_id, primitive, scope) + .map_err(errors::json)?, + ) + } + + #[wasm_bindgen(js_name = rebuildPolyline)] + pub fn rebuild_polyline( + &mut self, + og_id: &str, + points: &[f64], + closed: bool, + scope_json: &str, + ) -> Result { + params::id(og_id).map_err(errors::json)?; + let scope: EditScope = params::parse(scope_json).map_err(errors::json)?; + let points = params::points(points).map_err(errors::json)?; + changed( + self.inner + .rebuild_primitive(og_id, Primitive::Polyline { points, closed }, scope) + .map_err(errors::json)?, + ) + } + + #[wasm_bindgen(js_name = rebuildOperation)] + pub fn rebuild_operation( + &mut self, + og_id: &str, + operation_json: &str, + scope_json: &str, + ) -> Result { + params::id(og_id).map_err(errors::json)?; + let operation: CreatingOperation = params::parse(operation_json).map_err(errors::json)?; + if matches!(&operation, CreatingOperation::Extrude { holes, .. } if holes.len() > 1_000) { + return Err(errors::json(GraphError::code( + ErrorCode::LimitExceeded, + "profile hole limit", + ))); + } + let scope: EditScope = params::parse(scope_json).map_err(errors::json)?; + changed( + self.inner + .rebuild_operation(og_id, operation, scope) + .map_err(errors::json)?, + ) + } +} diff --git a/main/opengeometry/src/bindings/world_graph/shapes.rs b/main/opengeometry/src/bindings/world_graph/shapes.rs new file mode 100644 index 0000000..2ae1574 --- /dev/null +++ b/main/opengeometry/src/bindings/world_graph/shapes.rs @@ -0,0 +1,55 @@ +use super::{serialise, OGWorldGraph}; +use crate::bindings::errors; +use crate::bindings::tessellator::buffers_to_js; +use crate::tessellation::display::{bucket_floor, static_bucket}; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +#[wasm_bindgen] +impl OGWorldGraph { + pub fn revision(&self) -> u64 { + self.inner.revision() + } + + pub fn brep(&self, og_id: &str) -> Result { + self.inner + .brep(og_id) + .map_err(errors::json)? + .to_json() + .map_err(|error| errors::json(error.into())) + } + + pub fn report(&self, og_id: &str) -> Result { + serialise(&self.inner.report(og_id).map_err(errors::json)?) + } + + #[wasm_bindgen(js_name = edgeKeys)] + pub fn edge_keys(&self, og_id: &str) -> Result { + serialise(&self.inner.edge_keys(og_id).map_err(errors::json)?) + } + + #[wasm_bindgen(js_name = displayBuckets)] + pub fn display_buckets(&self, og_id: &str) -> Result { + let brep = self.inner.brep(og_id).map_err(errors::json)?; + let floor = bucket_floor(&brep).map_err(|error| errors::json(error.into()))?; + let bucket = static_bucket(&brep).map_err(|error| errors::json(error.into()))?; + serialise(&serde_json::json!({"floor": floor, "static": bucket})) + } + + pub fn snapshot(&self, shape_id: &str) -> Result, JsValue> { + self.inner.snapshot(shape_id).map_err(errors::json) + } + + pub fn buffers( + &self, + shape_id: &str, + bucket: f64, + max_triangles: u32, + ) -> Result { + buffers_to_js( + self.inner + .buffers(shape_id, bucket, max_triangles as usize) + .map_err(errors::json)?, + ) + } +} diff --git a/main/opengeometry/src/brep/accuracy.rs b/main/opengeometry/src/brep/accuracy.rs new file mode 100644 index 0000000..37a5e4c --- /dev/null +++ b/main/opengeometry/src/brep/accuracy.rs @@ -0,0 +1,49 @@ +use super::error::GeometryError; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Accuracy { + pub geometric: f64, + pub intersection: f64, + pub tessellation: f64, + pub exchange: f64, +} +impl Accuracy { + pub const STANDARD: Accuracy = Accuracy { + geometric: 1e-8, + intersection: 1e-9, + tessellation: 0.01, + exchange: 1e-6, + }; + + pub(crate) fn validate(self) -> Result<(), GeometryError> { + for value in [ + self.geometric, + self.intersection, + self.tessellation, + self.exchange, + ] { + if !value.is_finite() || value <= 0.0 { + return Err(GeometryError::InvalidGeometry( + "accuracy budgets must be positive and finite".into(), + )); + } + } + if self.intersection > self.geometric { + return Err(GeometryError::InvalidGeometry( + "intersection budget exceeds geometric tolerance".into(), + )); + } + Ok(()) + } + + pub(crate) fn combined(a: Accuracy, b: Accuracy) -> Accuracy { + Accuracy { + geometric: a.geometric.max(b.geometric), + intersection: a.intersection.max(b.intersection), + tessellation: a.tessellation.max(b.tessellation), + exchange: a.exchange.max(b.exchange), + } + } +} diff --git a/main/opengeometry/src/brep/body_type.rs b/main/opengeometry/src/brep/body_type.rs new file mode 100644 index 0000000..19c1ce6 --- /dev/null +++ b/main/opengeometry/src/brep/body_type.rs @@ -0,0 +1,30 @@ +use super::error::GeometryError; +use super::topology::BrepEnvelope; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum BodyType { + Wire, + Sheet, + Solid, +} + +impl BrepEnvelope { + pub fn body_type(&self) -> Result { + self.validate()?; + if !self.topology.faces.is_empty() && !self.topology.wires.is_empty() { + return Err(GeometryError::InvalidTopology( + "BRep mixes faces and wires".into(), + )); + } + if !self.solids.is_empty() { + Ok(BodyType::Solid) + } else if !self.topology.faces.is_empty() { + Ok(BodyType::Sheet) + } else if !self.topology.wires.is_empty() { + Ok(BodyType::Wire) + } else { + Err(GeometryError::InvalidTopology("BRep has no body".into())) + } + } +} diff --git a/main/opengeometry/src/brep/bounds.rs b/main/opengeometry/src/brep/bounds.rs new file mode 100644 index 0000000..3dc3d7c --- /dev/null +++ b/main/opengeometry/src/brep/bounds.rs @@ -0,0 +1,20 @@ +use crate::math::{Interval, Point3}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PatchBounds { + pub axes: [Interval; 3], +} +impl PatchBounds { + pub fn contains(&self, point: Point3) -> bool { + (0..3).all(|i| self.axes[i].contains(point[i])) + } + + pub(crate) fn overlaps(&self, other: PatchBounds, tolerance: f64) -> bool { + (0..3).all(|axis| { + self.axes[axis].lo <= other.axes[axis].hi + tolerance + && self.axes[axis].hi + tolerance >= other.axes[axis].lo + }) + } +} diff --git a/main/opengeometry/src/brep/builder.rs b/main/opengeometry/src/brep/builder.rs deleted file mode 100644 index 8daedea..0000000 --- a/main/opengeometry/src/brep/builder.rs +++ /dev/null @@ -1,506 +0,0 @@ -use std::collections::HashMap; - -use openmaths::Vector3; -use uuid::Uuid; - -use crate::operations::triangulate::compute_polygon_normal; - -use super::error::{BrepError, BrepErrorKind}; -use super::{Brep, Edge, Face, HalfEdge, Loop, Shell, Vertex, Wire}; - -#[derive(Clone)] -pub struct BrepBuilder { - brep: Brep, - undirected_edge_map: HashMap<(u32, u32), u32>, - directed_halfedge_map: HashMap<(u32, u32), u32>, - edge_halfedge_count: HashMap, -} - -impl BrepBuilder { - pub fn new(id: Uuid) -> Self { - Self { - brep: Brep::new(id), - undirected_edge_map: HashMap::new(), - directed_halfedge_map: HashMap::new(), - edge_halfedge_count: HashMap::new(), - } - } - - pub fn add_vertex(&mut self, position: Vector3) -> u32 { - let vertex_id = self.brep.vertices.len() as u32; - self.brep.vertices.push(Vertex::new(vertex_id, position)); - vertex_id - } - - pub fn add_vertices(&mut self, positions: &[Vector3]) -> Vec { - positions - .iter() - .map(|position| self.add_vertex(*position)) - .collect() - } - - pub fn add_face(&mut self, outer: &[u32], holes: &[Vec]) -> Result { - let mut staging = self.clone(); - let face_id = staging.add_face_internal(outer, holes)?; - *self = staging; - Ok(face_id) - } - - pub fn add_wire(&mut self, vertex_indices: &[u32], is_closed: bool) -> Result { - let mut staging = self.clone(); - let wire_id = staging.add_wire_internal(vertex_indices, is_closed)?; - *self = staging; - Ok(wire_id) - } - - pub fn add_shell(&mut self, face_ids: &[u32], is_closed: bool) -> Result { - let mut staging = self.clone(); - let shell_id = staging.add_shell_internal(face_ids, is_closed)?; - *self = staging; - Ok(shell_id) - } - - pub fn add_shell_from_all_faces(&mut self, is_closed: bool) -> Result { - if self.brep.faces.is_empty() { - return Err(BrepError::new( - BrepErrorKind::InvalidShell, - "Cannot create shell from an empty face set", - )); - } - - let face_ids: Vec = (0..self.brep.faces.len() as u32).collect(); - self.add_shell(&face_ids, is_closed) - } - - pub fn build(self) -> Result { - self.brep.validate_topology()?; - Ok(self.brep) - } - - fn add_face_internal(&mut self, outer: &[u32], holes: &[Vec]) -> Result { - let outer = sanitize_indices(outer, true, 3, "outer loop")?; - if !is_loop_non_degenerate(&outer) { - return Err(BrepError::new( - BrepErrorKind::DegenerateLoop, - "Outer loop is degenerate", - )); - } - - let mut hole_loops = Vec::new(); - for hole in holes { - let cleaned = sanitize_indices(hole, true, 3, "inner loop")?; - if !is_loop_non_degenerate(&cleaned) { - return Err(BrepError::new( - BrepErrorKind::DegenerateLoop, - "Inner loop is degenerate", - )); - } - hole_loops.push(cleaned); - } - - let face_id = self.brep.faces.len() as u32; - self.brep.faces.push(Face::new( - face_id, - Vector3::new(0.0, 0.0, 0.0), - 0, - Vec::new(), - None, - )); - - let outer_loop_id = self.add_face_loop(face_id, &outer, false)?; - self.brep.faces[face_id as usize].outer_loop = outer_loop_id; - - for hole in &hole_loops { - let loop_id = self.add_face_loop(face_id, hole, true)?; - self.brep.faces[face_id as usize].inner_loops.push(loop_id); - } - - let normal = self.compute_normal_from_loop(&outer)?; - self.brep.faces[face_id as usize].set_normal(normal); - - Ok(face_id) - } - - fn add_wire_internal( - &mut self, - vertex_indices: &[u32], - is_closed: bool, - ) -> Result { - let min_vertices = if is_closed { 3 } else { 2 }; - let vertices = sanitize_indices(vertex_indices, is_closed, min_vertices, "wire")?; - - let wire_id = self.brep.wires.len() as u32; - self.brep - .wires - .push(Wire::new(wire_id, Vec::new(), is_closed)); - - let halfedges = if is_closed { - self.create_closed_halfedge_cycle(&vertices, None, None, Some(wire_id))? - } else { - self.create_open_halfedge_chain(&vertices, None, None, Some(wire_id))? - }; - - self.brep.wires[wire_id as usize].halfedges = halfedges; - - Ok(wire_id) - } - - fn add_shell_internal(&mut self, face_ids: &[u32], is_closed: bool) -> Result { - if face_ids.is_empty() { - return Err(BrepError::new( - BrepErrorKind::InvalidShell, - "Shell must reference at least one face", - )); - } - - for face_id in face_ids { - let idx = *face_id as usize; - let Some(face) = self.brep.faces.get(idx) else { - return Err(BrepError::new( - BrepErrorKind::InvalidFace, - format!("Face {} does not exist", face_id), - )); - }; - - if face.shell_ref.is_some() { - return Err(BrepError::new( - BrepErrorKind::InvalidShell, - format!("Face {} already belongs to a shell", face_id), - )); - } - } - - let shell_id = self.brep.shells.len() as u32; - self.brep - .shells - .push(Shell::new(shell_id, face_ids.to_vec(), is_closed)); - - for face_id in face_ids { - self.brep.faces[*face_id as usize].shell_ref = Some(shell_id); - } - - Ok(shell_id) - } - - fn add_face_loop( - &mut self, - face_id: u32, - vertex_indices: &[u32], - is_hole: bool, - ) -> Result { - let loop_id = self.brep.loops.len() as u32; - self.brep - .loops - .push(Loop::new(loop_id, 0, face_id, is_hole)); - - let halfedges = - self.create_closed_halfedge_cycle(vertex_indices, Some(face_id), Some(loop_id), None)?; - let Some(start_halfedge) = halfedges.first().copied() else { - return Err(BrepError::new( - BrepErrorKind::DegenerateLoop, - "Loop generation produced no halfedges", - )); - }; - - self.brep.loops[loop_id as usize].halfedge = start_halfedge; - Ok(loop_id) - } - - fn create_closed_halfedge_cycle( - &mut self, - vertex_indices: &[u32], - face_ref: Option, - loop_ref: Option, - wire_ref: Option, - ) -> Result, BrepError> { - let mut halfedges = Vec::with_capacity(vertex_indices.len()); - - for index in 0..vertex_indices.len() { - let from = vertex_indices[index]; - let to = vertex_indices[(index + 1) % vertex_indices.len()]; - let halfedge_id = self.create_halfedge(from, to, face_ref, loop_ref, wire_ref)?; - halfedges.push(halfedge_id); - } - - for index in 0..halfedges.len() { - let current = halfedges[index] as usize; - let next = halfedges[(index + 1) % halfedges.len()]; - let prev = halfedges[(index + halfedges.len() - 1) % halfedges.len()]; - - self.brep.halfedges[current].next = Some(next); - self.brep.halfedges[current].prev = Some(prev); - } - - Ok(halfedges) - } - - fn create_open_halfedge_chain( - &mut self, - vertex_indices: &[u32], - face_ref: Option, - loop_ref: Option, - wire_ref: Option, - ) -> Result, BrepError> { - let mut halfedges = Vec::with_capacity(vertex_indices.len().saturating_sub(1)); - - for index in 0..(vertex_indices.len() - 1) { - let from = vertex_indices[index]; - let to = vertex_indices[index + 1]; - let halfedge_id = self.create_halfedge(from, to, face_ref, loop_ref, wire_ref)?; - halfedges.push(halfedge_id); - } - - for index in 0..halfedges.len() { - let current = halfedges[index] as usize; - let next = if index + 1 < halfedges.len() { - Some(halfedges[index + 1]) - } else { - None - }; - let prev = if index > 0 { - Some(halfedges[index - 1]) - } else { - None - }; - - self.brep.halfedges[current].next = next; - self.brep.halfedges[current].prev = prev; - } - - Ok(halfedges) - } - - fn create_halfedge( - &mut self, - from: u32, - to: u32, - face_ref: Option, - loop_ref: Option, - wire_ref: Option, - ) -> Result { - self.ensure_vertex_exists(from)?; - self.ensure_vertex_exists(to)?; - - if from == to { - return Err(BrepError::new( - BrepErrorKind::DegenerateEdge, - "Halfedge endpoints must differ", - )); - } - - if self.directed_halfedge_map.contains_key(&(from, to)) { - return Err(BrepError::new( - BrepErrorKind::InvalidHalfEdge, - format!("Duplicate directed halfedge {} -> {}", from, to), - )); - } - - let undirected = undirected_key(from, to); - let halfedge_id = self.brep.halfedges.len() as u32; - - let edge_id = - if let Some(existing_edge_id) = self.undirected_edge_map.get(&undirected).copied() { - let incidence = self - .edge_halfedge_count - .get(&existing_edge_id) - .copied() - .unwrap_or(0); - - if incidence >= 2 { - return Err(BrepError::new( - BrepErrorKind::NonManifoldEdge, - format!( - "Edge ({}, {}) would become non-manifold with more than two halfedges", - undirected.0, undirected.1 - ), - )); - } - - existing_edge_id - } else { - let new_edge_id = self.brep.edges.len() as u32; - self.undirected_edge_map.insert(undirected, new_edge_id); - self.edge_halfedge_count.insert(new_edge_id, 0); - self.brep - .edges - .push(Edge::new(new_edge_id, halfedge_id, None)); - new_edge_id - }; - - let twin = self.directed_halfedge_map.get(&(to, from)).copied(); - - self.brep.halfedges.push(HalfEdge::new( - halfedge_id, - from, - to, - edge_id, - face_ref, - loop_ref, - wire_ref, - )); - - if let Some(twin_id) = twin { - self.brep.halfedges[halfedge_id as usize].twin = Some(twin_id); - self.brep.halfedges[twin_id as usize].twin = Some(halfedge_id); - self.brep.edges[edge_id as usize].twin_halfedge = Some(halfedge_id); - } - - self.directed_halfedge_map.insert((from, to), halfedge_id); - let incidence = self.edge_halfedge_count.entry(edge_id).or_insert(0); - *incidence += 1; - - if self.brep.vertices[from as usize] - .outgoing_halfedge - .is_none() - { - self.brep.vertices[from as usize].outgoing_halfedge = Some(halfedge_id); - } - - Ok(halfedge_id) - } - - fn ensure_vertex_exists(&self, vertex_id: u32) -> Result<(), BrepError> { - if self.brep.vertices.get(vertex_id as usize).is_none() { - return Err(BrepError::new( - BrepErrorKind::InvalidVertex, - format!("Vertex {} does not exist", vertex_id), - )); - } - - Ok(()) - } - - fn compute_normal_from_loop(&self, loop_indices: &[u32]) -> Result { - if loop_indices.len() < 3 { - return Err(BrepError::new( - BrepErrorKind::DegenerateLoop, - "Loop must have at least three vertices to compute a normal", - )); - } - - let mut points = Vec::with_capacity(loop_indices.len()); - for vertex_id in loop_indices { - let position = self - .brep - .vertices - .get(*vertex_id as usize) - .ok_or_else(|| { - BrepError::new( - BrepErrorKind::InvalidVertex, - format!("Vertex {} does not exist", vertex_id), - ) - })? - .position; - points.push(position); - } - - compute_polygon_normal(&points).ok_or_else(|| { - BrepError::new( - BrepErrorKind::DegenerateLoop, - "Failed to compute a stable face normal from loop vertices", - ) - }) - } -} - -fn sanitize_indices( - indices: &[u32], - is_closed: bool, - min_vertices: usize, - label: &str, -) -> Result, BrepError> { - let mut cleaned: Vec = Vec::with_capacity(indices.len()); - for index in indices { - if cleaned.last().copied() == Some(*index) { - continue; - } - cleaned.push(*index); - } - - if is_closed && cleaned.len() >= 2 && cleaned.first() == cleaned.last() { - cleaned.pop(); - } - - if cleaned.len() < min_vertices { - return Err(BrepError::new( - BrepErrorKind::InvalidLoop, - format!( - "{} must have at least {} unique vertices", - label, min_vertices - ), - )); - } - - Ok(cleaned) -} - -fn is_loop_non_degenerate(loop_indices: &[u32]) -> bool { - if loop_indices.len() < 3 { - return false; - } - - let mut unique = HashMap::new(); - for index in loop_indices { - unique.insert(*index, true); - } - - unique.len() >= 3 -} - -fn undirected_key(a: u32, b: u32) -> (u32, u32) { - if a < b { - (a, b) - } else { - (b, a) - } -} - -#[cfg(test)] -mod tests { - use super::BrepBuilder; - use openmaths::Vector3; - use uuid::Uuid; - - #[test] - fn builder_creates_closed_face_with_twin_halfedges() { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - let vertices = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 1.0), - Vector3::new(0.0, 0.0, 1.0), - ]; - builder.add_vertices(&vertices); - - builder.add_face(&[0, 1, 2, 3], &[]).unwrap(); - builder.add_face(&[0, 3, 2, 1], &[]).unwrap(); - - let brep = builder.build().unwrap(); - - assert_eq!(brep.faces.len(), 2); - assert!(!brep.halfedges.is_empty()); - assert!(brep - .halfedges - .iter() - .any(|halfedge| halfedge.twin.is_some())); - } - - #[test] - fn builder_rejects_non_manifold_edge() { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - let vertices = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 1.0), - Vector3::new(0.0, 0.0, 1.0), - Vector3::new(0.5, 1.0, 0.5), - ]; - builder.add_vertices(&vertices); - - builder.add_face(&[0, 1, 2], &[]).unwrap(); - builder.add_face(&[1, 0, 3], &[]).unwrap(); - let third_face = builder.add_face(&[0, 1, 4], &[]); - - assert!(third_face.is_err()); - } -} diff --git a/main/opengeometry/src/brep/builder/boundary.rs b/main/opengeometry/src/brep/builder/boundary.rs new file mode 100644 index 0000000..3566858 --- /dev/null +++ b/main/opengeometry/src/brep/builder/boundary.rs @@ -0,0 +1,89 @@ +use super::brep_builder::{Builder, Use}; +use crate::brep::error::GeometryError; +use crate::brep::frame::Frame3; +use crate::brep::geometry::CurveGeometry; +use crate::brep::pcurve::PcurveGeometry; +use crate::brep::topology::{EdgeGeometry, Orientation}; +use crate::math::{dot, scale, sub, Point3}; + +pub(crate) fn dimensions(values: &[f64]) -> Result<(), GeometryError> { + if values.iter().any(|&x| !x.is_finite() || x <= 0.0) { + return Err(GeometryError::InvalidGeometry( + "primitive dimensions must be positive and finite".into(), + )); + } + Ok(()) +} + +pub(crate) fn plane_boundary( + builder: &Builder, + frame: Frame3, + edge: u32, + from: u32, + to: u32, + sense: Orientation, +) -> Result { + let curve = match builder.brep.topology.edges[edge as usize].geometry { + EdgeGeometry::Curve { curve, .. } => &builder.brep.geometry.curves[curve as usize], + _ => { + return Err(GeometryError::InvalidGeometry( + "collapsed planar boundary".into(), + )) + } + }; + let local = |p: Point3| [dot(p, frame.x), dot(p, frame.y)]; + let pcurve = match curve { + CurveGeometry::Line { origin, direction } => { + uv_line(local(sub(*origin, frame.origin)), local(*direction)) + } + CurveGeometry::Circle { frame: c, radius } => PcurveGeometry::Conic2 { + origin: local(sub(c.origin, frame.origin)), + axis_a: local(scale(c.x, *radius)), + axis_b: local(scale(c.y, *radius)), + }, + _ => { + return Err(GeometryError::UnsupportedGeometry( + "planar primitive boundary".into(), + )) + } + }; + Ok(boundary(edge, from, to, sense, pcurve)) +} + +pub(crate) fn uv_line(origin: [f64; 2], direction: [f64; 2]) -> PcurveGeometry { + PcurveGeometry::Line2 { origin, direction } +} +pub(crate) fn boundary( + edge: u32, + from: u32, + to: u32, + sense: Orientation, + pcurve: PcurveGeometry, +) -> Use { + Use { + edge, + from, + to, + sense, + pcurve, + } +} + +pub(crate) fn padded_uv_bounds( + points: impl IntoIterator>, + pad: f64, +) -> [[f64; 2]; 2] { + let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; + for point in points { + let uv = point.as_ref(); + for axis in 0..2 { + bounds[axis][0] = bounds[axis][0].min(uv[axis]); + bounds[axis][1] = bounds[axis][1].max(uv[axis]); + } + } + for bound in &mut bounds { + bound[0] -= pad; + bound[1] += pad; + } + bounds +} diff --git a/main/opengeometry/src/brep/builder/brep_builder.rs b/main/opengeometry/src/brep/builder/brep_builder.rs new file mode 100644 index 0000000..3ea1b2f --- /dev/null +++ b/main/opengeometry/src/brep/builder/brep_builder.rs @@ -0,0 +1,199 @@ +use crate::brep::accuracy::Accuracy; +use crate::brep::error::GeometryError; +use crate::brep::geometry::{CurveGeometry, SurfaceGeometry}; +use crate::brep::pcurve::PcurveGeometry; +use crate::brep::topology::{ + BrepEnvelope, Edge, EdgeGeometry, Face, FaceProvenance, FaceRole, FaceSource, HalfEdge, + HalfEdgeGeometryUse, Loop, Orientation, Shell, SolidRegion, TrimRegion, Vertex, +}; +use crate::math::{Interval, Point3}; + +pub(crate) struct Use { + pub(super) edge: u32, + pub(super) from: u32, + pub(super) to: u32, + pub(super) sense: Orientation, + pub(super) pcurve: PcurveGeometry, +} + +pub(crate) struct Builder { + pub(crate) brep: BrepEnvelope, +} +impl Builder { + pub(crate) fn new(id: String, accuracy: Accuracy) -> Result { + Ok(Self { + brep: BrepEnvelope::new(id, accuracy)?, + }) + } + pub(crate) fn vertex(&mut self, p: Point3) -> u32 { + let id = self.brep.topology.vertices.len() as u32; + self.brep.topology.vertices.push(Vertex { + id, + position: p, + outgoing_halfedge: None, + tolerance: self.brep.accuracy.geometric, + }); + id + } + pub(crate) fn edge(&mut self, curve: CurveGeometry, range: Interval, seam: bool) -> u32 { + let curve_id = self.brep.geometry.curves.len() as u32; + self.brep.geometry.curves.push(curve); + self.edge_geometry( + EdgeGeometry::Curve { + curve: curve_id, + range, + }, + seam, + ) + } + pub(crate) fn edge_geometry(&mut self, geometry: EdgeGeometry, seam: bool) -> u32 { + let id = self.brep.topology.edges.len() as u32; + self.brep.topology.edges.push(Edge { + id, + geometry, + halfedge: u32::MAX, + twin_halfedge: None, + tolerance: self.brep.accuracy.geometric, + chart_seam: seam, + }); + id + } + fn face_loop( + &mut self, + face: u32, + is_hole: bool, + uses: Vec, + ) -> Result { + let loop_id = self.brep.topology.loops.len() as u32; + let start = self.brep.topology.halfedges.len() as u32; + let count = uses.len() as u32; + if count == 0 { + return Err(GeometryError::InvalidTopology( + "empty primitive face".into(), + )); + } + for (i, u) in uses.into_iter().enumerate() { + let id = start + i as u32; + let pcurve = self.brep.geometry.pcurves.len() as u32; + self.brep.geometry.pcurves.push(u.pcurve); + let e = self + .brep + .topology + .edges + .get_mut(u.edge as usize) + .ok_or_else(|| { + GeometryError::InvalidTopology("builder references missing edge".into()) + })?; + let twin = if e.halfedge == u32::MAX { + e.halfedge = id; + None + } else { + if e.twin_halfedge.is_some() { + return Err(GeometryError::InvalidTopology( + "nonmanifold primitive edge".into(), + )); + } + e.twin_halfedge = Some(id); + self.brep.topology.halfedges[e.halfedge as usize].twin = Some(id); + Some(e.halfedge) + }; + self.brep.topology.halfedges.push(HalfEdge { + id, + from: u.from, + to: u.to, + twin, + next: Some(start + (i as u32 + 1) % count), + prev: Some(start + (i as u32 + count - 1) % count), + edge: u.edge, + face: Some(face), + loop_ref: Some(loop_id), + wire_ref: None, + geometry_use: HalfEdgeGeometryUse { + sense: u.sense, + pcurve: Some(pcurve), + periodic_lift: [0; 2], + }, + }); + if self.brep.topology.vertices[u.from as usize] + .outgoing_halfedge + .is_none() + { + self.brep.topology.vertices[u.from as usize].outgoing_halfedge = Some(id); + } + } + self.brep.topology.loops.push(Loop { + id: loop_id, + start_halfedge: start, + face_ref: face, + is_hole, + }); + Ok(loop_id) + } + pub(crate) fn face( + &mut self, + key: &str, + surface: SurfaceGeometry, + bounds: [[f64; 2]; 2], + uses: Vec, + ) -> Result<(), GeometryError> { + self.face_with_holes(key, surface, bounds, uses, Vec::new()) + } + pub(crate) fn face_with_holes( + &mut self, + key: &str, + surface: SurfaceGeometry, + bounds: [[f64; 2]; 2], + outer_uses: Vec, + hole_uses: Vec>, + ) -> Result<(), GeometryError> { + let surface_id = self.brep.geometry.surfaces.len() as u32; + self.brep.geometry.surfaces.push(surface); + let face = self.brep.topology.faces.len() as u32; + let outer = self.face_loop(face, false, outer_uses)?; + let mut holes = Vec::with_capacity(hole_uses.len()); + for uses in hole_uses { + holes.push(self.face_loop(face, true, uses)?); + } + self.brep.topology.faces.push(Face { + id: face, + key: key.into(), + surface: surface_id, + sense: Orientation::Forward, + trim: TrimRegion { + chart: 0, + uv_bounds: [ + Interval::new(bounds[0][0], bounds[0][1])?, + Interval::new(bounds[1][0], bounds[1][1])?, + ], + outer, + holes, + }, + shell_ref: Some(0), + provenance: FaceProvenance { + sources: vec![FaceSource { + entity: self.brep.id.clone(), + body: self.brep.id.clone(), + key: key.into(), + face, + }], + role: FaceRole::Authored, + reversed: false, + }, + }); + Ok(()) + } + pub(crate) fn finish_solid(mut self) -> Result { + let faces = (0..self.brep.topology.faces.len() as u32).collect(); + self.brep.topology.shells.push(Shell { + id: 0, + faces, + is_closed: true, + }); + self.brep.solids.push(SolidRegion { + outer_shell: 0, + cavity_shells: Vec::new(), + }); + self.brep.validate()?; + Ok(self.brep) + } +} diff --git a/main/opengeometry/src/brep/builder/mod.rs b/main/opengeometry/src/brep/builder/mod.rs new file mode 100644 index 0000000..dfb958c --- /dev/null +++ b/main/opengeometry/src/brep/builder/mod.rs @@ -0,0 +1,9 @@ +mod boundary; +mod brep_builder; +#[cfg(test)] +mod tests; +mod wire; + +pub(crate) use boundary::{boundary, dimensions, padded_uv_bounds, plane_boundary, uv_line}; +pub(crate) use brep_builder::{Builder, Use}; +pub(crate) use wire::WireUse; diff --git a/main/opengeometry/src/brep/builder/tests.rs b/main/opengeometry/src/brep/builder/tests.rs new file mode 100644 index 0000000..c9b5104 --- /dev/null +++ b/main/opengeometry/src/brep/builder/tests.rs @@ -0,0 +1,60 @@ +use super::wire::WireUse; +use super::Builder; +use crate::brep::accuracy::Accuracy; +use crate::brep::body_type::BodyType; +use crate::brep::geometry::CurveGeometry; +use crate::brep::topology::Orientation; +use crate::math::Interval; + +#[test] +fn multi_edge_open_wire_validates() { + let accuracy = Accuracy { + geometric: 1e-6, + intersection: 1e-7, + tessellation: 1e-3, + exchange: 1e-6, + }; + let mut builder = Builder::new("wire".into(), accuracy).unwrap(); + let a = builder.vertex([0.0, 0.0, 0.0]); + let b = builder.vertex([1.0, 0.0, 0.0]); + let c = builder.vertex([1.0, 1.0, 0.0]); + let first = builder.edge( + CurveGeometry::Line { + origin: [0.0, 0.0, 0.0], + direction: [1.0, 0.0, 0.0], + }, + Interval::new(0.0, 1.0).unwrap(), + false, + ); + let second = builder.edge( + CurveGeometry::Line { + origin: [1.0, 0.0, 0.0], + direction: [0.0, 1.0, 0.0], + }, + Interval::new(0.0, 1.0).unwrap(), + false, + ); + let shape = builder + .finish_wire( + vec![ + WireUse { + edge: first, + from: a, + to: b, + sense: Orientation::Forward, + }, + WireUse { + edge: second, + from: b, + to: c, + sense: Orientation::Forward, + }, + ], + false, + ) + .unwrap(); + assert_eq!(shape.body_type().unwrap(), BodyType::Wire); + assert_eq!(shape.topology.halfedges[0].next, Some(1)); + assert_eq!(shape.topology.halfedges[1].prev, Some(0)); + assert_eq!(shape.topology.halfedges[1].next, None); +} diff --git a/main/opengeometry/src/brep/builder/wire.rs b/main/opengeometry/src/brep/builder/wire.rs new file mode 100644 index 0000000..e37f4f7 --- /dev/null +++ b/main/opengeometry/src/brep/builder/wire.rs @@ -0,0 +1,93 @@ +use super::brep_builder::Builder; +use crate::brep::error::GeometryError; +use crate::brep::topology::{BrepEnvelope, HalfEdge, HalfEdgeGeometryUse, Orientation, Wire}; + +pub(crate) struct WireUse { + pub(crate) edge: u32, + pub(crate) from: u32, + pub(crate) to: u32, + pub(crate) sense: Orientation, +} + +impl Builder { + pub(crate) fn finish_wire( + mut self, + uses: Vec, + is_closed: bool, + ) -> Result { + if uses.is_empty() || !self.brep.topology.faces.is_empty() { + return Err(GeometryError::InvalidTopology( + "wire builder has no edges or contains faces".into(), + )); + } + let start = self.brep.topology.halfedges.len() as u32; + let count = uses.len() as u32; + let wire_id = self.brep.topology.wires.len() as u32; + for (index, use_edge) in uses.into_iter().enumerate() { + let edge = self + .brep + .topology + .edges + .get_mut(use_edge.edge as usize) + .ok_or_else(|| GeometryError::MissingReference { + kind: "edge".into(), + index: use_edge.edge, + })?; + if edge.halfedge != u32::MAX { + return Err(GeometryError::InvalidTopology( + "wire edge already has a use".into(), + )); + } + let id = start + index as u32; + edge.halfedge = id; + let vertex = self + .brep + .topology + .vertices + .get_mut(use_edge.from as usize) + .ok_or_else(|| GeometryError::MissingReference { + kind: "vertex".into(), + index: use_edge.from, + })?; + if vertex.outgoing_halfedge.is_none() { + vertex.outgoing_halfedge = Some(id); + } + self.brep.topology.halfedges.push(HalfEdge { + id, + from: use_edge.from, + to: use_edge.to, + twin: None, + next: if index + 1 < count as usize { + Some(id + 1) + } else if is_closed { + Some(start) + } else { + None + }, + prev: if index > 0 { + Some(id - 1) + } else if is_closed { + Some(start + count - 1) + } else { + None + }, + edge: use_edge.edge, + face: None, + loop_ref: None, + wire_ref: Some(wire_id), + geometry_use: HalfEdgeGeometryUse { + sense: use_edge.sense, + pcurve: None, + periodic_lift: [0; 2], + }, + }); + } + self.brep.topology.wires.push(Wire { + id: wire_id, + start_halfedge: start, + is_closed, + }); + self.brep.validate()?; + Ok(self.brep) + } +} diff --git a/main/opengeometry/src/brep/diagnostics/mod.rs b/main/opengeometry/src/brep/diagnostics/mod.rs new file mode 100644 index 0000000..abe9c06 --- /dev/null +++ b/main/opengeometry/src/brep/diagnostics/mod.rs @@ -0,0 +1,21 @@ +mod tests; + +use super::error::GeometryError; +use super::topology::BrepEnvelope; +use serde::Serialize; + +#[derive(Debug, Serialize)] +struct ValidationReport { + valid: bool, + validation_level: &'static str, + error: Option, +} + +fn validate_json(serialized: &str) -> ValidationReport { + let result = BrepEnvelope::from_json(serialized); + ValidationReport { + valid: result.is_ok(), + validation_level: "structure and sampled geometry residuals", + error: result.err(), + } +} diff --git a/main/opengeometry/src/brep/diagnostics/tests.rs b/main/opengeometry/src/brep/diagnostics/tests.rs new file mode 100644 index 0000000..88442d9 --- /dev/null +++ b/main/opengeometry/src/brep/diagnostics/tests.rs @@ -0,0 +1,20 @@ +use super::*; +use crate::brep::topology::circular_face; +use crate::brep::CurveGeometry; + +#[test] +fn diagnostics_reject_missing_links_and_detached_curves() { + let brep = circular_face(); + assert!(validate_json(&brep.to_json().unwrap()).valid); + let mut broken = brep.clone(); + broken.topology.halfedges[0].next = Some(u32::MAX); + let report = validate_json(&serde_json::to_string(&broken).unwrap()); + assert!(!report.valid); + assert!(report.error.is_some()); + let mut broken = brep; + if let CurveGeometry::Circle { radius, .. } = &mut broken.geometry.curves[0] { + *radius *= 1.1; + } + assert!(!validate_json(&serde_json::to_string(&broken).unwrap()).valid); + assert!(!validate_json("{\"schema_version\":1}").valid); +} diff --git a/main/opengeometry/src/brep/edge.rs b/main/opengeometry/src/brep/edge.rs deleted file mode 100644 index 9500fa5..0000000 --- a/main/opengeometry/src/brep/edge.rs +++ /dev/null @@ -1,42 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use super::geometry::CurveGeometry; - -#[derive(Clone, Serialize, Deserialize)] -pub struct Edge { - pub id: u32, - pub halfedge: u32, - pub twin_halfedge: Option, - /// Exact analytic geometry of this edge (D1). `None` ⇒ a straight segment - /// between its endpoint vertices. Defaulted so legacy (v1) B-rep JSON that - /// predates analytic geometry still deserializes. - #[serde(default)] - pub curve: Option, - /// Per-edge modeling tolerance (D2). `None` ⇒ use the global confusion - /// tolerance. Intersection edges from a boolean carry their own, looser - /// tolerance. Defaulted for v1 back-compat. - #[serde(default)] - pub tolerance: Option, -} - -impl Edge { - pub fn new(id: u32, halfedge: u32, twin_halfedge: Option) -> Self { - Self { - id, - halfedge, - twin_halfedge, - curve: None, - tolerance: None, - } - } - - pub fn with_curve(mut self, curve: CurveGeometry) -> Self { - self.curve = Some(curve); - self - } - - pub fn with_tolerance(mut self, tolerance: f64) -> Self { - self.tolerance = Some(tolerance); - self - } -} diff --git a/main/opengeometry/src/brep/error.rs b/main/opengeometry/src/brep/error.rs index 769cddf..2f50425 100644 --- a/main/opengeometry/src/brep/error.rs +++ b/main/opengeometry/src/brep/error.rs @@ -1,39 +1,57 @@ +use crate::math::MathError; use serde::{Deserialize, Serialize}; +use std::fmt; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub enum BrepErrorKind { - InvalidVertex, - InvalidEdge, - InvalidHalfEdge, - InvalidLoop, - InvalidWire, - InvalidFace, - InvalidShell, - DegenerateEdge, - DegenerateLoop, - NonManifoldEdge, - BrokenTopology, +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub enum GeometryError { + Math(MathError), + InvalidGeometry(String), + InvalidTopology(String), + UnsupportedGeometry(String), + AmbiguousProfileAlignment(String), + CoverageGap { families: [String; 2] }, + SingularParameterization, + MissingReference { kind: String, index: u32 }, + UnsupportedSchema { found: u32 }, + UnresolvedIntersection(String), + UnresolvedTessellation(String), + LimitExceeded(String), } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BrepError { - pub kind: BrepErrorKind, - pub message: String, +impl From for GeometryError { + fn from(error: MathError) -> Self { + Self::Math(error) + } } -impl BrepError { - pub fn new(kind: BrepErrorKind, message: impl Into) -> Self { - Self { - kind, - message: message.into(), - } +impl fmt::Display for GeometryError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:?}") } } -impl core::fmt::Display for BrepError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", self.message) +impl std::error::Error for GeometryError {} + +pub(super) fn missing(kind: &str, index: u32) -> GeometryError { + GeometryError::MissingReference { + kind: kind.into(), + index, + } +} +pub(super) fn invalid(message: impl Into) -> GeometryError { + GeometryError::InvalidTopology(message.into()) +} +pub(super) fn positive(value: f64) -> Result<(), GeometryError> { + if !value.is_finite() || value <= 0.0 { + return Err(GeometryError::InvalidGeometry( + "entity tolerance must be finite and positive".into(), + )); + } + Ok(()) +} +pub(crate) fn coverage_gap(a: &str, b: &str) -> GeometryError { + GeometryError::CoverageGap { + families: [a.into(), b.into()], } } - -impl std::error::Error for BrepError {} diff --git a/main/opengeometry/src/brep/face.rs b/main/opengeometry/src/brep/face.rs deleted file mode 100644 index 9af1429..0000000 --- a/main/opengeometry/src/brep/face.rs +++ /dev/null @@ -1,44 +0,0 @@ -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; - -use super::geometry::SurfaceGeometry; - -#[derive(Clone, Serialize, Deserialize)] -pub struct Face { - pub id: u32, - pub normal: Vector3, - pub outer_loop: u32, - pub inner_loops: Vec, - pub shell_ref: Option, - /// Exact analytic surface of this face (D1). `None` ⇒ a general planar - /// polygon whose plane is implied by its loop. Defaulted for v1 back-compat. - #[serde(default)] - pub surface: Option, -} - -impl Face { - pub fn new( - id: u32, - normal: Vector3, - outer_loop: u32, - inner_loops: Vec, - shell_ref: Option, - ) -> Self { - Self { - id, - normal, - outer_loop, - inner_loops, - shell_ref, - surface: None, - } - } - - pub fn set_normal(&mut self, normal: Vector3) { - self.normal = normal; - } - - pub fn set_surface(&mut self, surface: SurfaceGeometry) { - self.surface = Some(surface); - } -} diff --git a/main/opengeometry/src/brep/frame.rs b/main/opengeometry/src/brep/frame.rs new file mode 100644 index 0000000..0dc69f1 --- /dev/null +++ b/main/opengeometry/src/brep/frame.rs @@ -0,0 +1,104 @@ +use super::bounds::PatchBounds; +use super::error::GeometryError; +use crate::math::{add, cross, dot, finite, norm, scale, sub, Interval, Point3}; +use serde::{Deserialize, Serialize}; + +pub(crate) type UV = [f64; 2]; +pub(crate) type UVBox = [Interval; 2]; + +pub(crate) fn unit(a: Point3) -> Result { + let n = finite(norm(a))?; + if n == 0.0 { + return Err(GeometryError::SingularParameterization); + } + checked(a.map(|value| value / n)) +} +pub(crate) fn checked(p: Point3) -> Result { + for v in p { + finite(v)?; + } + Ok(p) +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Frame3 { + pub origin: Point3, + pub x: Point3, + pub y: Point3, + pub z: Point3, +} + +impl Frame3 { + pub const IDENTITY: Self = Self { + origin: [0.0; 3], + x: [1.0, 0.0, 0.0], + y: [0.0, 1.0, 0.0], + z: [0.0, 0.0, 1.0], + }; + + pub fn from_axis( + origin: Point3, + axis: Point3, + reference: Point3, + ) -> Result { + checked(origin)?; + let z = unit(axis)?; + let x = unit(sub(reference, scale(z, dot(z, reference))))?; + let y = unit(cross(z, x))?; + let frame = Self { origin, x, y, z }; + frame.validate()?; + Ok(frame) + } + + pub(crate) fn validate(&self) -> Result<(), GeometryError> { + for v in [self.origin, self.x, self.y, self.z] { + checked(v)?; + } + if [self.x, self.y, self.z] + .iter() + .any(|&v| (norm(v) - 1.0).abs() > 1e-12) + || dot(self.x, self.y).abs() > 1e-12 + || dot(self.x, self.z).abs() > 1e-12 + || dot(self.y, self.z).abs() > 1e-12 + || norm(sub(cross(self.x, self.y), self.z)) > 1e-12 + { + return Err(GeometryError::InvalidGeometry( + "frame must be right-handed and orthonormal".into(), + )); + } + Ok(()) + } + + pub(crate) fn vector(&self, p: Point3) -> Point3 { + add( + add(scale(self.x, p[0]), scale(self.y, p[1])), + scale(self.z, p[2]), + ) + } + pub fn point(&self, p: Point3) -> Point3 { + add(self.origin, self.vector(p)) + } + pub(crate) fn local(&self, p: Point3) -> Point3 { + let d = sub(p, self.origin); + [dot(d, self.x), dot(d, self.y), dot(d, self.z)] + } + + pub(super) fn bounds(&self, local: [Interval; 3]) -> Result { + let mut axes = [Interval::point(0.0)?; 3]; + for i in 0..3 { + axes[i] = Interval::point(self.origin[i])? + .add_interval(local[0].mul_interval(Interval::point(self.x[i])?)?)? + .add_interval(local[1].mul_interval(Interval::point(self.y[i])?)?)? + .add_interval(local[2].mul_interval(Interval::point(self.z[i])?)?)?; + } + Ok(PatchBounds { axes }) + } +} + +pub(crate) const GROUND: Frame3 = Frame3 { + origin: [0.0; 3], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], +}; diff --git a/main/opengeometry/src/brep/geometry.rs b/main/opengeometry/src/brep/geometry.rs deleted file mode 100644 index 073450b..0000000 --- a/main/opengeometry/src/brep/geometry.rs +++ /dev/null @@ -1,307 +0,0 @@ -//! Analytic curve & surface geometry attached to B-rep edges and faces -//! (debt item D1). -//! -//! Historically the kernel destroyed curvature at construction time: an arc or -//! circle was tessellated into straight `segments` edges and the center/radius -//! discarded, so exactness could never be recovered for export, offset, or any -//! future analytic feature. This module restores the defining property of a -//! B-rep kernel — edges carry an exact [`CurveGeometry`] and faces an exact -//! [`SurfaceGeometry`]. The tessellated vertices remain in the B-rep as the -//! *derived* discretization used by the mesh/topology pipeline; the analytic -//! geometry is the source of truth consulted by inspection and exchange (D9). -//! -//! All geometry is stored in the primitive's local frame; world placement is -//! applied by `Brep::transformed`. - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; - -const TWO_PI: f64 = 2.0 * std::f64::consts::PI; - -/// Exact analytic geometry of an edge. `None` on an edge means a plain straight -/// segment between its endpoint vertices (the legacy/default interpretation, -/// equivalent to a [`CurveGeometry::Line`]). -#[derive(Clone, Serialize, Deserialize)] -#[serde(tag = "type")] -pub enum CurveGeometry { - /// A straight segment. Endpoints are the edge's vertices; stored explicitly - /// so the curve is self-describing for export. - Line { start: Vector3, end: Vector3 }, - /// A circular arc (or full circle when `start_angle`/`end_angle` span 2π). - /// The arc lies on the plane through `center` with the given `normal`; - /// angle 0 points along `x_axis` and sweeps toward `x_axis × normal`. - Circle { - center: Vector3, - normal: Vector3, - x_axis: Vector3, - radius: f64, - start_angle: f64, - end_angle: f64, - }, -} - -impl CurveGeometry { - /// Whether this curve closes on itself (full circle). - pub fn is_closed(&self) -> bool { - match self { - CurveGeometry::Line { .. } => false, - CurveGeometry::Circle { - start_angle, - end_angle, - .. - } => (end_angle - start_angle).abs() >= TWO_PI - 1.0e-9, - } - } - - /// A short stable tag for inspection / serialization consumers. - pub fn kind(&self) -> &'static str { - match self { - CurveGeometry::Line { .. } => "line", - CurveGeometry::Circle { .. } => "circle", - } - } - - /// Samples the curve into `segments` straight chords. A `Line` always yields - /// its two endpoints; a `Circle` yields `segments + 1` points (or `segments` - /// distinct points for a full circle, the closing point omitted). - pub fn tessellate(&self, segments: u32) -> Vec { - match self { - CurveGeometry::Line { start, end } => vec![*start, *end], - CurveGeometry::Circle { - center, - normal, - x_axis, - radius, - start_angle, - end_angle, - } => { - let n = segments.max(1); - let closed = self.is_closed(); - let x = normalize(*x_axis); - // Second in-plane axis. `x_axis × normal` matches the sweep the - // arc/cylinder primitives generate (x = r·cosθ on x_axis, - // advancing toward this axis), so the analytic curve's - // tessellation reproduces the stored facets exactly. - let y = normalize(cross(x, *normal)); - let step = (end_angle - start_angle) / n as f64; - let count = if closed { n } else { n + 1 }; - let mut points = Vec::with_capacity(count as usize); - for i in 0..count { - let a = start_angle + step * i as f64; - let c = a.cos() * radius; - let s = a.sin() * radius; - points.push(Vector3::new( - center.x + x.x * c + y.x * s, - center.y + x.y * c + y.y * s, - center.z + x.z * c + y.z * s, - )); - } - points - } - } - } -} - -impl CurveGeometry { - /// Applies a placement to this curve. `transform_point` maps a local point - /// to world; `scale` is the placement's uniform scale factor (radii are - /// lengths and must scale, directions are renormalized so scale drops out). - pub fn transformed_with( - &self, - transform_point: &impl Fn(Vector3) -> Vector3, - scale: f64, - ) -> CurveGeometry { - match self { - CurveGeometry::Line { start, end } => CurveGeometry::Line { - start: transform_point(*start), - end: transform_point(*end), - }, - CurveGeometry::Circle { - center, - normal, - x_axis, - radius, - start_angle, - end_angle, - } => { - let c = transform_point(*center); - CurveGeometry::Circle { - center: c, - normal: transform_direction(transform_point, *center, *normal), - x_axis: transform_direction(transform_point, *center, *x_axis), - radius: radius * scale, - start_angle: *start_angle, - end_angle: *end_angle, - } - } - } - } -} - -/// Maps a local direction to world by transforming two points and subtracting, -/// then renormalizing. Correct under rotation + translation + uniform scale. -fn transform_direction( - transform_point: &impl Fn(Vector3) -> Vector3, - base: Vector3, - dir: Vector3, -) -> Vector3 { - let p0 = transform_point(base); - let p1 = transform_point(Vector3::new(base.x + dir.x, base.y + dir.y, base.z + dir.z)); - normalize(Vector3::new(p1.x - p0.x, p1.y - p0.y, p1.z - p0.z)) -} - -/// Exact analytic geometry of a face. `None` means a general planar polygon -/// whose plane is implied by its loop (the legacy/default interpretation). -#[derive(Clone, Serialize, Deserialize)] -#[serde(tag = "type")] -pub enum SurfaceGeometry { - /// A planar face through `origin` with outward `normal`. - Plane { origin: Vector3, normal: Vector3 }, - /// A (possibly partial) cylindrical face: the lateral surface swept by a - /// circle of `radius` centred on `origin`, extruded along `axis` for - /// `height`. `ref_direction` fixes angle 0 in the cross-section plane. - Cylinder { - origin: Vector3, - axis: Vector3, - ref_direction: Vector3, - radius: f64, - height: f64, - }, -} - -impl SurfaceGeometry { - pub fn kind(&self) -> &'static str { - match self { - SurfaceGeometry::Plane { .. } => "plane", - SurfaceGeometry::Cylinder { .. } => "cylinder", - } - } - - pub fn transformed_with( - &self, - transform_point: &impl Fn(Vector3) -> Vector3, - scale: f64, - ) -> SurfaceGeometry { - match self { - SurfaceGeometry::Plane { origin, normal } => SurfaceGeometry::Plane { - origin: transform_point(*origin), - normal: transform_direction(transform_point, *origin, *normal), - }, - SurfaceGeometry::Cylinder { - origin, - axis, - ref_direction, - radius, - height, - } => SurfaceGeometry::Cylinder { - origin: transform_point(*origin), - axis: transform_direction(transform_point, *origin, *axis), - ref_direction: transform_direction(transform_point, *origin, *ref_direction), - radius: radius * scale, - height: height * scale, - }, - } - } -} - -fn cross(a: Vector3, b: Vector3) -> Vector3 { - Vector3::new( - a.y * b.z - a.z * b.y, - a.z * b.x - a.x * b.z, - a.x * b.y - a.y * b.x, - ) -} - -fn normalize(v: Vector3) -> Vector3 { - let len = (v.x * v.x + v.y * v.y + v.z * v.z).sqrt(); - if len <= f64::EPSILON { - v - } else { - Vector3::new(v.x / len, v.y / len, v.z / len) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn full_circle(radius: f64) -> CurveGeometry { - CurveGeometry::Circle { - center: Vector3::new(0.0, 0.0, 0.0), - normal: Vector3::new(0.0, 1.0, 0.0), - x_axis: Vector3::new(1.0, 0.0, 0.0), - radius, - start_angle: 0.0, - end_angle: TWO_PI, - } - } - - #[test] - fn line_tessellates_to_endpoints() { - let line = CurveGeometry::Line { - start: Vector3::new(0.0, 0.0, 0.0), - end: Vector3::new(1.0, 0.0, 0.0), - }; - assert_eq!(line.tessellate(8).len(), 2); - assert!(!line.is_closed()); - } - - #[test] - fn full_circle_omits_closing_point() { - let circle = full_circle(2.0); - assert!(circle.is_closed()); - let pts = circle.tessellate(16); - assert_eq!(pts.len(), 16); - // Every sampled point lies exactly on the circle of radius 2. - for p in &pts { - let r = (p.x * p.x + p.z * p.z).sqrt(); - assert!((r - 2.0).abs() < 1.0e-12, "radius drift: {}", r); - } - } - - #[test] - fn analytic_circle_is_exact_unlike_stored_facets() { - // The whole point of D1: refining the tessellation recovers exactness - // because the curve — not the facets — is the source of truth. - let circle = full_circle(1.0); - let coarse = circle.tessellate(4); - let fine = circle.tessellate(256); - // Coarse facets miss the true arc midpoint badly; fine ones do not. - let coarse_mid_err = chord_sagitta_error(&coarse, 1.0); - let fine_mid_err = chord_sagitta_error(&fine, 1.0); - assert!(fine_mid_err < coarse_mid_err); - assert!(fine_mid_err < 1.0e-4); - } - - fn chord_sagitta_error(points: &[Vector3], radius: f64) -> f64 { - // Max deviation of chord midpoints from the true radius. - let mut max = 0.0_f64; - for i in 0..points.len() { - let a = points[i]; - let b = points[(i + 1) % points.len()]; - let mx = (a.x + b.x) * 0.5; - let mz = (a.z + b.z) * 0.5; - let r = (mx * mx + mz * mz).sqrt(); - max = max.max((radius - r).abs()); - } - max - } - - #[test] - fn arc_quarter_sweep_has_correct_endpoints() { - let arc = CurveGeometry::Circle { - center: Vector3::new(0.0, 0.0, 0.0), - normal: Vector3::new(0.0, 1.0, 0.0), - x_axis: Vector3::new(1.0, 0.0, 0.0), - radius: 1.0, - start_angle: 0.0, - end_angle: std::f64::consts::FRAC_PI_2, - }; - let pts = arc.tessellate(8); - assert_eq!(pts.len(), 9); - let first = pts.first().unwrap(); - let last = pts.last().unwrap(); - assert!((first.x - 1.0).abs() < 1.0e-12 && first.z.abs() < 1.0e-12); - assert!(last.x.abs() < 1.0e-12 && (last.z - 1.0).abs() < 1.0e-12); - } -} diff --git a/main/opengeometry/src/brep/geometry/chord.rs b/main/opengeometry/src/brep/geometry/chord.rs new file mode 100644 index 0000000..c668287 --- /dev/null +++ b/main/opengeometry/src/brep/geometry/chord.rs @@ -0,0 +1,34 @@ +use crate::brep::bounds::PatchBounds; +use crate::math::{dot, norm, sub, Point3}; + +pub(crate) fn point_segment_distance(point: Point3, a: Point3, b: Point3) -> f64 { + let chord = sub(b, a); + let length_squared = dot(chord, chord); + if length_squared == 0.0 { + return norm(sub(point, a)); + } + let parameter = (dot(sub(point, a), chord) / length_squared).clamp(0.0, 1.0); + norm(sub( + point, + [ + a[0] + parameter * chord[0], + a[1] + parameter * chord[1], + a[2] + parameter * chord[2], + ], + )) +} + +pub(crate) fn bounds_chord_deviation(bounds: PatchBounds, a: Point3, b: Point3) -> f64 { + let mut maximum: f64 = 0.0; + for mask in 0..8 { + let corner = std::array::from_fn(|axis| { + if mask & (1 << axis) == 0 { + bounds.axes[axis].lo + } else { + bounds.axes[axis].hi + } + }); + maximum = maximum.max(point_segment_distance(corner, a, b)); + } + maximum +} diff --git a/main/opengeometry/src/brep/geometry/curve.rs b/main/opengeometry/src/brep/geometry/curve.rs new file mode 100644 index 0000000..cda9359 --- /dev/null +++ b/main/opengeometry/src/brep/geometry/curve.rs @@ -0,0 +1,196 @@ +use crate::brep::bounds::PatchBounds; +use crate::brep::error::GeometryError; +use crate::brep::frame::{checked, Frame3}; +use crate::math::{add, finite, norm, scale, Interval, Point3}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "kind", deny_unknown_fields)] +pub enum CurveGeometry { + Line { + origin: Point3, + direction: Point3, + }, + Circle { + frame: Frame3, + radius: f64, + }, + Ellipse { + frame: Frame3, + major_radius: f64, + minor_radius: f64, + }, + Intersection { + definition: u32, + }, +} + +pub(crate) trait Curve { + fn point_at(&self, t: f64) -> Result; + fn tangent_at(&self, t: f64) -> Result; + fn enclose(&self, range: Interval) -> Result; +} + +impl CurveGeometry { + pub(crate) fn validate(&self) -> Result<(), GeometryError> { + match self { + Self::Line { origin, direction } => { + checked(*origin)?; + checked(*direction)?; + if (norm(*direction) - 1.0).abs() > 1e-12 { + return Err(GeometryError::InvalidGeometry( + "line direction must be unit length".into(), + )); + } + } + Self::Circle { frame, radius } => { + frame.validate()?; + if !radius.is_finite() || *radius <= 0.0 { + return Err(GeometryError::InvalidGeometry( + "circle radius must be positive".into(), + )); + } + } + Self::Ellipse { + frame, + major_radius: a, + minor_radius: b, + } => { + frame.validate()?; + if !a.is_finite() || !b.is_finite() || *b <= 0.0 || a < b { + return Err(GeometryError::InvalidGeometry( + "invalid ellipse radii".into(), + )); + } + } + Self::Intersection { .. } => {} + }; + Ok(()) + } + + pub(crate) fn elementary_point(&self, t: f64) -> Result { + self.validate()?; + finite(t)?; + checked(match self { + Self::Line { origin, direction } => add(*origin, scale(*direction, t)), + Self::Circle { frame, radius } => { + frame.point([radius * t.cos(), radius * t.sin(), 0.0]) + } + Self::Ellipse { + frame, + major_radius: a, + minor_radius: b, + } => frame.point([a * t.cos(), b * t.sin(), 0.0]), + Self::Intersection { definition } => { + return Err(GeometryError::MissingReference { + kind: "intersection".into(), + index: *definition, + }) + } + }) + } + + pub(crate) fn elementary_tangent(&self, t: f64) -> Result { + self.validate()?; + finite(t)?; + checked(match self { + Self::Line { direction, .. } => *direction, + Self::Circle { frame, radius } => { + frame.vector([-radius * t.sin(), radius * t.cos(), 0.0]) + } + Self::Ellipse { + frame, + major_radius: a, + minor_radius: b, + } => frame.vector([-a * t.sin(), b * t.cos(), 0.0]), + Self::Intersection { definition } => { + return Err(GeometryError::MissingReference { + kind: "intersection".into(), + index: *definition, + }) + } + }) + } + + pub(super) fn elementary_bounds(&self, range: Interval) -> Result { + self.validate()?; + Interval::new(range.lo, range.hi)?; + let zero = Interval::point(0.0)?; + match self { + Self::Line { origin, direction } => { + let mut axes = [zero; 3]; + for i in 0..3 { + axes[i] = Interval::point(origin[i])? + .add_interval(range.mul_interval(Interval::point(direction[i])?)?)?; + } + Ok(PatchBounds { axes }) + } + Self::Circle { frame, radius } => frame.bounds([ + range.cos()?.mul_interval(Interval::point(*radius)?)?, + range.sin()?.mul_interval(Interval::point(*radius)?)?, + zero, + ]), + Self::Ellipse { + frame, + major_radius: a, + minor_radius: b, + } => frame.bounds([ + range.cos()?.mul_interval(Interval::point(*a)?)?, + range.sin()?.mul_interval(Interval::point(*b)?)?, + zero, + ]), + Self::Intersection { definition } => Err(GeometryError::MissingReference { + kind: "intersection".into(), + index: *definition, + }), + } + } + + #[cfg(test)] + pub(super) fn sample_elementary( + &self, + range: Interval, + deflection: f64, + max_segments: usize, + ) -> Result, GeometryError> { + self.validate()?; + Interval::new(range.lo, range.hi)?; + if !deflection.is_finite() || deflection <= 0.0 { + return Err(GeometryError::InvalidGeometry( + "deflection must be finite and positive".into(), + )); + } + let bound = match self { + Self::Line { .. } => 0.0, + Self::Circle { radius, .. } => *radius, + Self::Ellipse { major_radius, .. } => *major_radius, + Self::Intersection { definition } => { + return Err(GeometryError::MissingReference { + kind: "intersection".into(), + index: *definition, + }) + } + }; + let minimum = if bound > 0.0 && range.width() >= std::f64::consts::TAU - 1e-12 { + 3.0 + } else { + 1.0 + }; + let count = (range.width().abs() * (bound / (8.0 * deflection)).sqrt()) + .ceil() + .max(minimum); + if !count.is_finite() || count > max_segments as f64 { + return Err(GeometryError::LimitExceeded( + "curve tessellation segments".into(), + )); + } + let n = count as usize; + let mut samples = Vec::with_capacity(n + 1); + for i in 0..=n { + samples.push( + self.elementary_point(range.lo + (range.hi - range.lo) * i as f64 / n as f64)?, + ); + } + Ok(samples) + } +} diff --git a/main/opengeometry/src/brep/geometry/intersection_definition.rs b/main/opengeometry/src/brep/geometry/intersection_definition.rs new file mode 100644 index 0000000..61b3370 --- /dev/null +++ b/main/opengeometry/src/brep/geometry/intersection_definition.rs @@ -0,0 +1,20 @@ +use crate::brep::frame::UV; +use crate::math::{Interval, Point3}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct TraceAnchor { + pub(crate) parameter: f64, + pub(crate) point: Point3, + pub(crate) uv_a: UV, + pub(crate) uv_b: UV, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct IntersectionDefinition { + pub(crate) surfaces: [u32; 2], + pub(crate) anchors: Vec, + pub(crate) uv_tubes: Vec<[Interval; 4]>, + pub(crate) residual_tolerance: f64, +} diff --git a/main/opengeometry/src/brep/geometry/mod.rs b/main/opengeometry/src/brep/geometry/mod.rs new file mode 100644 index 0000000..af30ee2 --- /dev/null +++ b/main/opengeometry/src/brep/geometry/mod.rs @@ -0,0 +1,17 @@ +mod chord; +mod curve; +mod intersection_definition; +mod store; +mod surface; +#[cfg(test)] +mod tests; +mod trace; + +pub(crate) use chord::{bounds_chord_deviation, point_segment_distance}; +pub(crate) use curve::Curve; +pub use curve::CurveGeometry; +pub(crate) use intersection_definition::{IntersectionDefinition, TraceAnchor}; +pub use store::GeometryStore; +pub use surface::{Surface, SurfaceChart, SurfaceGeometry, SurfaceJet2}; +pub use trace::IntersectionPoint; +pub(crate) use trace::SsiBudget; diff --git a/main/opengeometry/src/brep/geometry/store.rs b/main/opengeometry/src/brep/geometry/store.rs new file mode 100644 index 0000000..22a53bc --- /dev/null +++ b/main/opengeometry/src/brep/geometry/store.rs @@ -0,0 +1,136 @@ +use super::curve::{Curve, CurveGeometry}; +use super::intersection_definition::IntersectionDefinition; +use super::surface::{Surface, SurfaceGeometry}; +use crate::brep::bounds::PatchBounds; +use crate::brep::error::{missing, GeometryError}; +use crate::brep::frame::UV; +use crate::brep::pcurve::{IntersectionSide, PcurveGeometry}; +use crate::math::{finite, Interval, Point3}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct GeometryStore { + pub surfaces: Vec, + pub(crate) curves: Vec, + pub(crate) pcurves: Vec, + pub(crate) intersections: Vec, +} + +pub(crate) struct CurveView<'a> { + pub(crate) geometry: &'a CurveGeometry, + store: &'a GeometryStore, +} + +impl GeometryStore { + pub(crate) fn new() -> Self { + Self { + surfaces: Vec::new(), + curves: Vec::new(), + pcurves: Vec::new(), + intersections: Vec::new(), + } + } + pub fn surface(&self, id: u32) -> Result<&SurfaceGeometry, GeometryError> { + self.surfaces + .get(id as usize) + .ok_or_else(|| missing("surface", id)) + } + pub(crate) fn curve(&self, id: u32) -> Result, GeometryError> { + let geometry = self + .curves + .get(id as usize) + .ok_or_else(|| missing("curve", id))?; + Ok(CurveView { + geometry, + store: self, + }) + } + pub(crate) fn pcurve_at(&self, id: u32, t: f64) -> Result { + finite(t)?; + let uv = match self + .pcurves + .get(id as usize) + .ok_or_else(|| missing("pcurve", id))? + { + PcurveGeometry::Line2 { origin, direction } => { + std::array::from_fn(|i| origin[i] + direction[i] * t) + } + PcurveGeometry::Conic2 { + origin, + axis_a, + axis_b, + } => std::array::from_fn(|i| origin[i] + axis_a[i] * t.cos() + axis_b[i] * t.sin()), + PcurveGeometry::ProjectedCurve { + curve, + surface, + chart, + uv_hint, + uv_rate, + parameter_origin, + } => { + let surface = self.surface(*surface)?; + if *chart as usize >= surface.charts().len() { + return Err(missing("chart", *chart)); + } + let hint = + std::array::from_fn(|i| uv_hint[i] + uv_rate[i] * (t - parameter_origin)); + surface.project(self.curve(*curve)?.point_at(t)?, Some(hint))? + } + PcurveGeometry::IntersectionSide { definition, side } => { + let point = self.intersection(*definition)?.evaluate(t, self)?; + if *side == IntersectionSide::A { + point.uv_a + } else { + point.uv_b + } + } + }; + for v in uv { + finite(v)?; + } + Ok(uv) + } + pub(crate) fn intersection(&self, id: u32) -> Result<&IntersectionDefinition, GeometryError> { + self.intersections + .get(id as usize) + .ok_or_else(|| missing("intersection", id)) + } +} +impl Default for GeometryStore { + fn default() -> Self { + Self::new() + } +} + +impl Curve for CurveView<'_> { + fn point_at(&self, t: f64) -> Result { + match self.geometry { + CurveGeometry::Intersection { definition } => Ok(self + .store + .intersection(*definition)? + .evaluate(t, self.store)? + .point), + curve => curve.elementary_point(t), + } + } + fn tangent_at(&self, t: f64) -> Result { + match self.geometry { + CurveGeometry::Intersection { definition } => Ok(self + .store + .intersection(*definition)? + .evaluate(t, self.store)? + .tangent), + curve => curve.elementary_tangent(t), + } + } + fn enclose(&self, range: Interval) -> Result { + match self.geometry { + CurveGeometry::Intersection { definition } => self + .store + .intersection(*definition)? + .enclose(range, self.store), + curve => curve.elementary_bounds(range), + } + } +} diff --git a/main/opengeometry/src/brep/geometry/surface.rs b/main/opengeometry/src/brep/geometry/surface.rs new file mode 100644 index 0000000..6fffde8 --- /dev/null +++ b/main/opengeometry/src/brep/geometry/surface.rs @@ -0,0 +1,356 @@ +use crate::brep::bounds::PatchBounds; +use crate::brep::error::GeometryError; +use crate::brep::frame::{checked, Frame3, UVBox, UV}; +use crate::math::{add, finite, norm, scale, Interval, Point3}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug)] +pub struct SurfaceChart { + pub(crate) domain: [[f64; 2]; 2], + pub(crate) periods: [Option; 2], +} +const PLANE_CHART: [SurfaceChart; 1] = [SurfaceChart { + domain: [[-f64::MAX, f64::MAX]; 2], + periods: [None, None], +}]; +const SPHERE_CHART: [SurfaceChart; 1] = [SurfaceChart { + domain: [ + [0.0, std::f64::consts::TAU], + [-std::f64::consts::FRAC_PI_2, std::f64::consts::FRAC_PI_2], + ], + periods: [Some(std::f64::consts::TAU), None], +}]; +const CYLINDER_CHART: [SurfaceChart; 1] = [SurfaceChart { + domain: [[0.0, std::f64::consts::TAU], [-f64::MAX, f64::MAX]], + periods: [Some(std::f64::consts::TAU), None], +}]; +const CONE_CHART: [SurfaceChart; 1] = [SurfaceChart { + domain: [[0.0, std::f64::consts::TAU], [0.0, f64::MAX]], + periods: [Some(std::f64::consts::TAU), None], +}]; +const TORUS_CHART: [SurfaceChart; 1] = [SurfaceChart { + domain: [[0.0, std::f64::consts::TAU]; 2], + periods: [Some(std::f64::consts::TAU); 2], +}]; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "kind", deny_unknown_fields)] +pub enum SurfaceGeometry { + Plane { + frame: Frame3, + }, + Sphere { + frame: Frame3, + radius: f64, + }, + Cylinder { + frame: Frame3, + radius: f64, + }, + Cone { + frame: Frame3, + semi_angle: f64, + }, + Torus { + frame: Frame3, + major_radius: f64, + minor_radius: f64, + }, +} + +#[derive(Clone, Copy, Debug)] +pub struct SurfaceJet2 { + pub(super) point: Point3, + pub(crate) du: Point3, + pub(crate) dv: Point3, + pub duu: Point3, + pub duv: Point3, + pub dvv: Point3, +} + +pub trait Surface { + fn point_at(&self, uv: UV) -> Result; + fn normal_at(&self, uv: UV) -> Result; + fn derivatives(&self, uv: UV) -> Result; + fn project(&self, p: Point3, hint: Option) -> Result; + fn charts(&self) -> &[SurfaceChart]; + fn enclose(&self, domain: UVBox) -> Result; +} + +impl SurfaceGeometry { + pub fn frame(&self) -> &Frame3 { + match self { + Self::Plane { frame } + | Self::Sphere { frame, .. } + | Self::Cylinder { frame, .. } + | Self::Cone { frame, .. } + | Self::Torus { frame, .. } => frame, + } + } + pub(crate) fn validate(&self) -> Result<(), GeometryError> { + self.frame().validate()?; + let valid = match self { + Self::Plane { .. } => true, + Self::Sphere { radius, .. } | Self::Cylinder { radius, .. } => { + radius.is_finite() && *radius > 0.0 + } + Self::Cone { semi_angle, .. } => { + semi_angle.is_finite() + && *semi_angle > 0.0 + && *semi_angle < std::f64::consts::FRAC_PI_2 + } + Self::Torus { + major_radius, + minor_radius, + .. + } => { + major_radius.is_finite() + && minor_radius.is_finite() + && *minor_radius > 0.0 + && major_radius > minor_radius + } + }; + if !valid { + return Err(GeometryError::InvalidGeometry( + "invalid radius, cone angle, or ring torus".into(), + )); + } + Ok(()) + } + + fn check_uv(&self, uv: UV) -> Result<(), GeometryError> { + self.validate()?; + for x in uv { + finite(x)?; + } + if matches!(self, Self::Sphere { .. }) && uv[1].abs() > std::f64::consts::FRAC_PI_2 + || matches!(self, Self::Cone { .. }) && uv[1] < 0.0 + { + return Err(GeometryError::InvalidGeometry( + "UV outside surface domain".into(), + )); + } + Ok(()) + } + + pub(crate) fn parameter_in_domain(&self, uv: UV) -> bool { + uv.into_iter().all(f64::is_finite) + && (!matches!(self, Self::Sphere { .. }) || uv[1].abs() <= std::f64::consts::FRAC_PI_2) + && (!matches!(self, Self::Cone { .. }) || uv[1] >= 0.0) + } +} + +fn lift(angle: f64, hint: Option) -> f64 { + hint.map_or(angle, |h| { + angle + ((h - angle) / std::f64::consts::TAU).round() * std::f64::consts::TAU + }) +} + +impl Surface for SurfaceGeometry { + fn point_at(&self, uv: UV) -> Result { + Ok(self.derivatives(uv)?.point) + } + + fn derivatives(&self, uv: UV) -> Result { + self.check_uv(uv)?; + let (su, cu) = uv[0].sin_cos(); + let (sv, cv) = uv[1].sin_cos(); + let radial = [cu, su, 0.0]; + let tangent = [-su, cu, 0.0]; + let z = [0.0, 0.0, 1.0]; + let zero = [0.0; 3]; + let (p, du, dv, duu, duv, dvv) = match self { + Self::Plane { .. } => ( + [uv[0], uv[1], 0.0], + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + zero, + zero, + zero, + ), + Self::Sphere { radius: r, .. } => { + let p = add(scale(radial, r * cv), scale(z, r * sv)); + ( + p, + scale(tangent, r * cv), + add(scale(radial, -r * sv), scale(z, r * cv)), + scale(radial, -r * cv), + scale(tangent, -r * sv), + scale(p, -1.0), + ) + } + Self::Cylinder { radius: r, .. } => ( + add(scale(radial, *r), scale(z, uv[1])), + scale(tangent, *r), + z, + scale(radial, -r), + zero, + zero, + ), + Self::Cone { semi_angle, .. } => { + let k = semi_angle.tan(); + let r = uv[1] * k; + ( + add(scale(radial, r), scale(z, uv[1])), + scale(tangent, r), + add(scale(radial, k), z), + scale(radial, -r), + scale(tangent, k), + zero, + ) + } + Self::Torus { + major_radius: r, + minor_radius: a, + .. + } => { + let b = r + a * cv; + ( + add(scale(radial, b), scale(z, a * sv)), + scale(tangent, b), + add(scale(radial, -a * sv), scale(z, a * cv)), + scale(radial, -b), + scale(tangent, -a * sv), + add(scale(radial, -a * cv), scale(z, -a * sv)), + ) + } + }; + let f = self.frame(); + Ok(SurfaceJet2 { + point: checked(f.point(p))?, + du: checked(f.vector(du))?, + dv: checked(f.vector(dv))?, + duu: checked(f.vector(duu))?, + duv: checked(f.vector(duv))?, + dvv: checked(f.vector(dvv))?, + }) + } + + fn normal_at(&self, uv: UV) -> Result { + self.check_uv(uv)?; + let (s, c) = uv[0].sin_cos(); + let (sv, cv) = uv[1].sin_cos(); + let n = match self { + Self::Plane { .. } => [0.0, 0.0, 1.0], + Self::Sphere { .. } | Self::Torus { .. } => [cv * c, cv * s, sv], + Self::Cylinder { .. } => [c, s, 0.0], + Self::Cone { semi_angle, .. } => { + if uv[1] == 0.0 { + return Err(GeometryError::SingularParameterization); + } + [ + semi_angle.cos() * c, + semi_angle.cos() * s, + -semi_angle.sin(), + ] + } + }; + checked(self.frame().vector(n)) + } + + fn project(&self, p: Point3, hint: Option) -> Result { + self.validate()?; + checked(p)?; + if let Some(h) = hint { + for v in h { + finite(v)?; + } + } + let p = checked(self.frame().local(p))?; + let radial = p[0].hypot(p[1]); + let u = lift(p[1].atan2(p[0]), hint.map(|h| h[0])); + let result = match self { + Self::Plane { .. } => [p[0], p[1]], + Self::Sphere { .. } => { + if norm(p) == 0.0 { + return Err(GeometryError::SingularParameterization); + } + [u, p[2].atan2(radial)] + } + Self::Cylinder { .. } => { + if radial == 0.0 { + return Err(GeometryError::SingularParameterization); + } + [u, p[2]] + } + Self::Cone { semi_angle, .. } => { + let v = (radial * semi_angle.sin() + p[2] * semi_angle.cos()) * semi_angle.cos(); + if radial == 0.0 || v <= 0.0 { + return Err(GeometryError::SingularParameterization); + } + [u, v] + } + Self::Torus { major_radius, .. } => { + if radial == 0.0 || (radial == *major_radius && p[2] == 0.0) { + return Err(GeometryError::SingularParameterization); + } + [ + u, + lift(p[2].atan2(radial - major_radius), hint.map(|h| h[1])), + ] + } + }; + for x in result { + finite(x)?; + } + Ok(result) + } + + fn charts(&self) -> &[SurfaceChart] { + match self { + Self::Plane { .. } => &PLANE_CHART, + Self::Sphere { .. } => &SPHERE_CHART, + Self::Cylinder { .. } => &CYLINDER_CHART, + Self::Cone { .. } => &CONE_CHART, + Self::Torus { .. } => &TORUS_CHART, + } + } + + fn enclose(&self, domain: UVBox) -> Result { + for d in domain { + Interval::new(d.lo, d.hi)?; + } + self.check_uv([domain[0].lo, domain[1].lo])?; + self.check_uv([domain[0].hi, domain[1].hi])?; + let [u, v] = domain; + let zero = Interval::point(0.0)?; + let c = u.cos()?; + let s = u.sin()?; + let local = match self { + Self::Plane { .. } => [u, v, zero], + Self::Sphere { radius, .. } => { + let r = Interval::point(*radius)?; + let cv = v.cos()?.mul_interval(r)?; + [ + c.mul_interval(cv)?, + s.mul_interval(cv)?, + v.sin()?.mul_interval(r)?, + ] + } + Self::Cylinder { radius, .. } => { + let r = Interval::point(*radius)?; + [c.mul_interval(r)?, s.mul_interval(r)?, v] + } + Self::Cone { semi_angle, .. } => { + let angle = Interval::point(*semi_angle)?; + let k = angle.sin()?.div_interval(angle.cos()?)?; + let r = v.mul_interval(k)?; + [c.mul_interval(r)?, s.mul_interval(r)?, v] + } + Self::Torus { + major_radius, + minor_radius, + .. + } => { + let a = Interval::point(*minor_radius)?; + let r = Interval::point(*major_radius)?.add_interval(v.cos()?.mul_interval(a)?)?; + [ + c.mul_interval(r)?, + s.mul_interval(r)?, + v.sin()?.mul_interval(a)?, + ] + } + }; + self.frame().bounds(local) + } +} diff --git a/main/opengeometry/src/brep/geometry/tests.rs b/main/opengeometry/src/brep/geometry/tests.rs new file mode 100644 index 0000000..b2d6a99 --- /dev/null +++ b/main/opengeometry/src/brep/geometry/tests.rs @@ -0,0 +1,131 @@ +use super::curve::CurveGeometry; +use super::surface::{Surface, SurfaceGeometry}; +use crate::brep::error::GeometryError; +use crate::brep::frame::Frame3; +use crate::math::{dot, norm, scale, sub, Interval}; + +fn surfaces() -> Vec { + let frame = Frame3::from_axis([3.0, -2.0, 7.0], [1.0, 2.0, 3.0], [0.0, 1.0, 0.0]).unwrap(); + vec![ + SurfaceGeometry::Plane { frame }, + SurfaceGeometry::Sphere { frame, radius: 2.0 }, + SurfaceGeometry::Cylinder { frame, radius: 2.0 }, + SurfaceGeometry::Cone { + frame, + semi_angle: 0.4, + }, + SurfaceGeometry::Torus { + frame, + major_radius: 3.0, + minor_radius: 1.0, + }, + ] +} + +#[test] +fn derivatives_projection_and_normals_for_all_families() { + let uv = [0.7, 0.4]; + let h = 1e-5; + for surface in surfaces() { + let jet = surface.derivatives(uv).unwrap(); + let n = surface.normal_at(uv).unwrap(); + assert!((norm(n) - 1.0).abs() < 1e-12); + assert!(dot(n, jet.du).abs() < 1e-12); + assert!(dot(n, jet.dv).abs() < 1e-12); + let projected = surface.project(jet.point, Some(uv)).unwrap(); + assert!((projected[0] - uv[0]).abs() < 1e-12); + assert!((projected[1] - uv[1]).abs() < 1e-12); + for dimension in 0..2 { + let mut lo = uv; + let mut hi = uv; + lo[dimension] -= h; + hi[dimension] += h; + let jl = surface.derivatives(lo).unwrap(); + let jh = surface.derivatives(hi).unwrap(); + let derivative = scale(sub(jh.point, jl.point), 0.5 / h); + assert!( + norm(sub( + derivative, + if dimension == 0 { jet.du } else { jet.dv } + )) < 1e-8 + ); + assert!( + norm(sub( + scale(sub(jh.du, jl.du), 0.5 / h), + if dimension == 0 { jet.duu } else { jet.duv } + )) < 1e-8 + ); + assert!( + norm(sub( + scale(sub(jh.dv, jl.dv), 0.5 / h), + if dimension == 0 { jet.duv } else { jet.dvv } + )) < 1e-8 + ); + } + } +} + +#[test] +fn conservative_boxes_cover_transformed_patches() { + let box_uv = [ + Interval::new(0.2, 1.8).unwrap(), + Interval::new(0.1, 0.9).unwrap(), + ]; + for surface in surfaces() { + let bounds = surface.enclose(box_uv).unwrap(); + for i in 0..=24 { + for j in 0..=24 { + let uv = [0.2 + 1.6 * i as f64 / 24.0, 0.1 + 0.8 * j as f64 / 24.0]; + assert!(bounds.contains(surface.point_at(uv).unwrap())); + } + } + } +} + +#[test] +fn circle_samples_meet_chord_error_at_multiple_lods() { + let curve = CurveGeometry::Circle { + frame: Frame3::IDENTITY, + radius: 2.0, + }; + let range = Interval::new(0.0, std::f64::consts::TAU).unwrap(); + for epsilon in [0.1, 0.01, 0.001] { + let samples = curve.sample_elementary(range, epsilon, 100_000).unwrap(); + let n = samples.len() - 1; + let sagitta = 2.0 * (1.0 - (std::f64::consts::PI / n as f64).cos()); + assert!(sagitta <= epsilon); + assert!(norm(sub(samples[0], samples[n])) < 1e-12); + } +} + +#[test] +fn invalid_frames_tori_and_apices_fail_explicitly() { + Frame3::from_axis([0.0; 3], [0.0, 0.0, 1e-320], [1.0, 0.0, 0.0]) + .unwrap() + .validate() + .unwrap(); + let mut bad = Frame3::IDENTITY; + bad.y = [0.0, -1.0, 0.0]; + assert!(bad.validate().is_err()); + assert!(SurfaceGeometry::Torus { + frame: Frame3::IDENTITY, + major_radius: 1.0, + minor_radius: 1.0 + } + .validate() + .is_err()); + let cone = SurfaceGeometry::Cone { + frame: Frame3::IDENTITY, + semi_angle: 0.3, + }; + assert_eq!( + cone.normal_at([0.0, 0.0]), + Err(GeometryError::SingularParameterization) + ); + assert!(CurveGeometry::Circle { + frame: Frame3::IDENTITY, + radius: 1.0 + } + .sample_elementary(Interval::new(0.0, 1.0).unwrap(), 0.0, 100) + .is_err()); +} diff --git a/main/opengeometry/src/brep/geometry/trace/enclosure.rs b/main/opengeometry/src/brep/geometry/trace/enclosure.rs new file mode 100644 index 0000000..869c9aa --- /dev/null +++ b/main/opengeometry/src/brep/geometry/trace/enclosure.rs @@ -0,0 +1,280 @@ +use crate::brep::bounds::PatchBounds; +use crate::brep::error::GeometryError; +use crate::brep::frame::{unit, Frame3}; +use crate::brep::geometry::intersection_definition::IntersectionDefinition; +use crate::brep::geometry::store::GeometryStore; +use crate::brep::geometry::surface::SurfaceGeometry; +use crate::math::{add, cross, dot, norm, scale, sub, Interval, Point3}; + +struct CrossedCylinders { + host_radius: f64, + cutter: Frame3, + cutter_radius: f64, + u_axis: Point3, + v_axis: Point3, + w_axis: Point3, + centre: Point3, +} + +struct CutterProjection { + v_cos: f64, + v_sin: f64, + w_cos: f64, + w_sin: f64, +} + +pub(super) fn intersect_interval(a: Interval, b: Interval) -> Option { + Interval::new(a.lo.max(b.lo), a.hi.min(b.hi)).ok() +} + +pub(super) fn perpendicular_cylinder_enclosure( + definition: &IntersectionDefinition, + store: &GeometryStore, + segment: usize, + range: Interval, + parent: PatchBounds, +) -> Result, GeometryError> { + let Some(cylinders) = crossed_cylinders(definition, store)? else { + return Ok(None); + }; + let CrossedCylinders { + host_radius, + cutter_radius, + u_axis, + v_axis, + w_axis, + centre, + .. + } = cylinders; + + let first = &definition.anchors[segment]; + let last = &definition.anchors[segment + 1]; + let guide_axis = unit(sub(last.point, first.point))?; + let certificate = definition.residual_tolerance * 8.0 + + 128.0 * f64::EPSILON * host_radius.max(cutter_radius).max(norm(centre)).max(1.0); + let theta_parent = definition.uv_tubes[segment][2]; + if theta_parent.width() >= std::f64::consts::PI { + return Ok(None); + } + let u_parent = interval_dot(parent, centre, u_axis)?; + if u_parent.contains(0.0) { + return Ok(None); + } + let host_squared = Interval::new( + (host_radius - certificate).max(0.0).powi(2), + (host_radius + certificate).powi(2), + )?; + let Some(CutterProjection { + v_cos, + v_sin, + w_cos, + w_sin, + }) = certified_projection(&cylinders, guide_axis, theta_parent, host_squared, u_parent)? + else { + return Ok(None); + }; + + let lo = definition.evaluate(range.lo, store)?.uv_b[0]; + let hi = definition.evaluate(range.hi, store)?.uv_b[0]; + let theta_pad = certificate / cutter_radius + 64.0 * f64::EPSILON; + let theta = Interval::new(lo.min(hi) - theta_pad, lo.max(hi) + theta_pad)?; + if theta.lo < theta_parent.lo - theta_pad || theta.hi > theta_parent.hi + theta_pad { + return Ok(None); + } + let cosine = theta.cos()?; + let sine = theta.sin()?; + let v = trig(cosine, sine, v_cos, v_sin, cutter_radius)?; + let w = trig(cosine, sine, w_cos, w_sin, cutter_radius)?; + let Some(u) = signed_root(host_squared.sub_interval(w.square()?)?, u_parent)? else { + return Ok(None); + }; + let local = [u, v, w]; + let axes = [u_axis, v_axis, w_axis]; + + let world = world_bounds(centre, local, axes, certificate)?; + let Some(chord_error) = chord_error_bound(host_radius, cutter_radius, certificate, theta) + else { + return Ok(None); + }; + Ok(Some((PatchBounds { axes: world }, chord_error))) +} + +fn crossed_cylinders( + definition: &IntersectionDefinition, + store: &GeometryStore, +) -> Result, GeometryError> { + let ( + SurfaceGeometry::Cylinder { + frame: host, + radius: host_radius, + }, + SurfaceGeometry::Cylinder { + frame: cutter, + radius: cutter_radius, + }, + ) = ( + store.surface(definition.surfaces[0])?, + store.surface(definition.surfaces[1])?, + ) + else { + return Ok(None); + }; + let u_axis = cutter.z; + let v_axis = host.z; + if dot(u_axis, v_axis).abs() > 1e-12 { + return Ok(None); + } + let w_axis = cross(u_axis, v_axis); + let host_to_cutter = sub(cutter.origin, host.origin); + let centre = add(host.origin, scale(v_axis, dot(host_to_cutter, v_axis))); + let residual = sub( + sub(centre, cutter.origin), + scale(u_axis, dot(sub(centre, cutter.origin), u_axis)), + ); + if norm(residual) > 1e-12 { + return Ok(None); + } + Ok(Some(CrossedCylinders { + host_radius: *host_radius, + cutter: *cutter, + cutter_radius: *cutter_radius, + u_axis, + v_axis, + w_axis, + centre, + })) +} + +fn interval_dot( + bounds: PatchBounds, + origin: Point3, + axis: Point3, +) -> Result { + let mut value = Interval::point(0.0)?; + for coordinate in 0..3 { + value = value.add_interval( + bounds.axes[coordinate] + .sub_interval(Interval::point(origin[coordinate])?)? + .mul_interval(Interval::point(axis[coordinate])?)?, + )?; + } + Ok(value) +} + +fn certified_projection( + cylinders: &CrossedCylinders, + guide_axis: Point3, + theta_parent: Interval, + host_squared: Interval, + u_parent: Interval, +) -> Result, GeometryError> { + let CrossedCylinders { + cutter, + cutter_radius, + u_axis, + v_axis, + w_axis, + .. + } = *cylinders; + let v_cos = dot(cutter.x, v_axis); + let v_sin = dot(cutter.y, v_axis); + let w_cos = dot(cutter.x, w_axis); + let w_sin = dot(cutter.y, w_axis); + let parent_cosine = theta_parent.cos()?; + let parent_sine = theta_parent.sin()?; + let parent_w = trig(parent_cosine, parent_sine, w_cos, w_sin, cutter_radius)?; + let parent_dw = trig(parent_sine, parent_cosine, -w_cos, w_sin, cutter_radius)?; + let parent_dv = trig(parent_sine, parent_cosine, -v_cos, v_sin, cutter_radius)?; + let Some(parent_u) = signed_root(host_squared.sub_interval(parent_w.square()?)?, u_parent)? + else { + return Ok(None); + }; + if parent_u.contains(0.0) { + return Ok(None); + } + let parent_du = Interval::point(-1.0)? + .mul_interval(parent_w)? + .mul_interval(parent_dw)? + .div_interval(parent_u)?; + let derivative = parent_du + .mul_interval(Interval::point(dot(guide_axis, u_axis))?)? + .add_interval(parent_dv.mul_interval(Interval::point(dot(guide_axis, v_axis))?)?)? + .add_interval(parent_dw.mul_interval(Interval::point(dot(guide_axis, w_axis))?)?)?; + if derivative.contains(0.0) { + return Ok(None); + } + Ok(Some(CutterProjection { + v_cos, + v_sin, + w_cos, + w_sin, + })) +} + +fn trig( + cosine: Interval, + sine: Interval, + along_cosine: f64, + along_sine: f64, + cutter_radius: f64, +) -> Result { + Ok(cosine + .mul_interval(Interval::point(along_cosine)?)? + .add_interval(sine.mul_interval(Interval::point(along_sine)?)?)? + .mul_interval(Interval::point(cutter_radius)?)?) +} + +fn signed_root(radial: Interval, sign: Interval) -> Result, GeometryError> { + if radial.hi < 0.0 { + return Ok(None); + } + let root = Interval::new(radial.lo.max(0.0), radial.hi)?.sqrt()?; + Ok(Some(if sign.lo >= 0.0 { + root + } else if sign.hi <= 0.0 { + Interval::new(-root.hi, -root.lo)? + } else { + Interval::new(-root.hi, root.hi)? + })) +} + +fn world_bounds( + centre: Point3, + local: [Interval; 3], + axes: [Point3; 3], + certificate: f64, +) -> Result<[Interval; 3], GeometryError> { + let mut world = [Interval::point(0.0)?; 3]; + for coordinate in 0..3 { + let mut value = Interval::point(centre[coordinate])?; + for axis in 0..3 { + value = value.add_interval( + local[axis].mul_interval(Interval::point(axes[axis][coordinate])?)?, + )?; + } + world[coordinate] = value.add_interval(Interval::new(-certificate, certificate)?)?; + } + Ok(world) +} + +fn chord_error_bound( + host_radius: f64, + cutter_radius: f64, + certificate: f64, + theta: Interval, +) -> Option { + let minimum_u = ((host_radius - certificate).powi(2) - (cutter_radius + certificate).powi(2)) + .max(0.0) + .sqrt(); + if minimum_u <= certificate { + return None; + } + let radius = cutter_radius + certificate; + let axial_curvature = radius.powi(2) / minimum_u + radius.powi(4) / minimum_u.powi(3); + let host_angle_curvature = radius / minimum_u + radius.powi(3) / minimum_u.powi(3); + + let host_support_curvature = + host_radius * (host_angle_curvature + (radius / minimum_u).powi(2)); + let curvature = (radius + axial_curvature).max(host_support_curvature); + Some(curvature * theta.width().powi(2) / 8.0 + certificate * 4.0) +} diff --git a/main/opengeometry/src/brep/geometry/trace/evaluate.rs b/main/opengeometry/src/brep/geometry/trace/evaluate.rs new file mode 100644 index 0000000..c526f39 --- /dev/null +++ b/main/opengeometry/src/brep/geometry/trace/evaluate.rs @@ -0,0 +1,338 @@ +use super::enclosure::{intersect_interval, perpendicular_cylinder_enclosure}; +use super::{IntersectionPoint, SsiBudget}; +use crate::brep::bounds::PatchBounds; +use crate::brep::error::GeometryError; +use crate::brep::frame::{unit, UVBox}; +use crate::brep::geometry::intersection_definition::IntersectionDefinition; +use crate::brep::geometry::store::GeometryStore; +use crate::brep::geometry::surface::{Surface, SurfaceGeometry}; +use crate::math::{add, cross, dot, finite, norm, scale, solve, sub, Interval, Point3}; + +struct TracePredictor<'a> { + a: &'a SurfaceGeometry, + b: &'a SurfaceGeometry, + tube: [Interval; 4], + guide: Point3, + velocity: Point3, + gauge: Point3, + uv: [f64; 4], +} + +impl IntersectionDefinition { + fn shape(&self) -> Result<(), GeometryError> { + if self.anchors.len() < 2 + || self.uv_tubes.len() + 1 != self.anchors.len() + || !self.residual_tolerance.is_finite() + || self.residual_tolerance <= 0.0 + { + return Err(GeometryError::InvalidGeometry( + "invalid intersection branch shape or tolerance".into(), + )); + } + Ok(()) + } + + pub(crate) fn validate(&self, store: &GeometryStore) -> Result<(), GeometryError> { + self.shape()?; + let a = store.surface(self.surfaces[0])?; + let b = store.surface(self.surfaces[1])?; + for (i, anchor) in self.anchors.iter().enumerate() { + finite(anchor.parameter)?; + for v in anchor.point { + finite(v)?; + } + if i > 0 && anchor.parameter <= self.anchors[i - 1].parameter { + return Err(GeometryError::InvalidGeometry( + "intersection parameters are not increasing".into(), + )); + } + if norm(sub(a.point_at(anchor.uv_a)?, anchor.point)) > self.residual_tolerance + || norm(sub(b.point_at(anchor.uv_b)?, anchor.point)) > self.residual_tolerance + { + return Err(GeometryError::InvalidGeometry( + "intersection anchor misses its supports".into(), + )); + } + } + for (i, tube) in self.uv_tubes.iter().enumerate() { + for d in tube { + Interval::new(d.lo, d.hi)?; + } + for anchor in [&self.anchors[i], &self.anchors[i + 1]] { + let uv = [ + anchor.uv_a[0], + anchor.uv_a[1], + anchor.uv_b[0], + anchor.uv_b[1], + ]; + if (0..4).any(|j| !tube[j].contains(uv[j])) { + return Err(GeometryError::InvalidGeometry( + "intersection anchor leaves its UV tube".into(), + )); + } + } + if norm(sub(self.anchors[i + 1].point, self.anchors[i].point)) == 0.0 { + return Err(GeometryError::InvalidGeometry( + "zero-length tracing guide".into(), + )); + } + } + Ok(()) + } + + pub(crate) fn evaluate( + &self, + t: f64, + store: &GeometryStore, + ) -> Result { + self.evaluate_with_budget(t, store, SsiBudget::default()) + } + + pub(super) fn evaluate_with_budget( + &self, + t: f64, + store: &GeometryStore, + budget: SsiBudget, + ) -> Result { + self.shape()?; + budget.validate()?; + finite(t)?; + let TracePredictor { + a, + b, + tube, + guide, + velocity, + gauge, + mut uv, + } = self.trace_predictor(t, store)?; + for _ in 0..budget.max_newton_iterations { + let ja = a.derivatives([uv[0], uv[1]])?; + let jb = b.derivatives([uv[2], uv[3]])?; + let diff = sub(ja.point, jb.point); + let residual = [diff[0], diff[1], diff[2], dot(sub(ja.point, guide), gauge)]; + let error = residual.into_iter().map(f64::abs).fold(0.0, f64::max); + if norm(diff) <= self.residual_tolerance && residual[3].abs() <= self.residual_tolerance + { + return traced_point(a, b, uv, ja.point, diff, velocity, gauge); + } + let mut jacobian = [[0.0; 4]; 4]; + for i in 0..3 { + jacobian[i] = [ja.du[i], ja.dv[i], -jb.du[i], -jb.dv[i]]; + } + jacobian[3] = [dot(ja.du, gauge), dot(ja.dv, gauge), 0.0, 0.0]; + let step = solve(jacobian, residual.map(|v| -v)) + .map_err(|e| { + GeometryError::UnresolvedIntersection(format!("intersection correction: {e}")) + })? + .value; + let mut accepted = false; + let mut fraction = 1.0; + for _ in 0..12 { + let candidate = std::array::from_fn(|i| uv[i] + fraction * step[i]); + if (0..4).all(|i| tube[i].contains(candidate[i])) + && a.parameter_in_domain([candidate[0], candidate[1]]) + && b.parameter_in_domain([candidate[2], candidate[3]]) + { + let pa = a.point_at([candidate[0], candidate[1]])?; + let pb = b.point_at([candidate[2], candidate[3]])?; + let diff = sub(pa, pb); + let trial = diff + .into_iter() + .map(f64::abs) + .fold(dot(sub(pa, guide), gauge).abs(), f64::max); + if trial < error { + uv = candidate; + accepted = true; + break; + } + } + fraction *= 0.5; + } + if !accepted { + return Err(GeometryError::UnresolvedIntersection( + "correction leaves branch tube or does not decrease residual".into(), + )); + } + } + Err(GeometryError::UnresolvedIntersection( + "intersection correction budget exhausted".into(), + )) + } + + fn trace_predictor<'a>( + &self, + t: f64, + store: &'a GeometryStore, + ) -> Result, GeometryError> { + let first = &self.anchors[0]; + let last = &self.anchors[self.anchors.len() - 1]; + if t < first.parameter || t > last.parameter { + return Err(GeometryError::InvalidGeometry( + "parameter outside intersection branch".into(), + )); + } + let index = self + .anchors + .partition_point(|a| a.parameter <= t) + .saturating_sub(1) + .min(self.anchors.len() - 2); + let left = &self.anchors[index]; + let right = &self.anchors[index + 1]; + let span = finite(right.parameter - left.parameter)?; + if span <= 0.0 { + return Err(GeometryError::InvalidGeometry( + "invalid trace parameter interval".into(), + )); + } + let w = (t - left.parameter) / span; + let guide = add(scale(left.point, 1.0 - w), scale(right.point, w)); + let velocity = scale(sub(right.point, left.point), 1.0 / span); + let gauge = unit(velocity)?; + let tube = self.uv_tubes[index]; + for d in tube { + Interval::new(d.lo, d.hi)?; + } + let a = store.surface(self.surfaces[0])?; + let b = store.surface(self.surfaces[1])?; + let uv = [ + left.uv_a[0] * (1.0 - w) + right.uv_a[0] * w, + left.uv_a[1] * (1.0 - w) + right.uv_a[1] * w, + left.uv_b[0] * (1.0 - w) + right.uv_b[0] * w, + left.uv_b[1] * (1.0 - w) + right.uv_b[1] * w, + ]; + if (0..4).any(|i| !tube[i].contains(uv[i])) { + return Err(GeometryError::InvalidGeometry( + "trace predictor leaves its UV tube".into(), + )); + } + Ok(TracePredictor { + a, + b, + tube, + guide, + velocity, + gauge, + uv, + }) + } + + pub(crate) fn enclose( + &self, + range: Interval, + store: &GeometryStore, + ) -> Result { + self.shape()?; + Interval::new(range.lo, range.hi)?; + if range.lo < self.anchors[0].parameter + || range.hi > self.anchors[self.anchors.len() - 1].parameter + { + return Err(GeometryError::InvalidGeometry( + "bounds outside intersection branch".into(), + )); + } + let a = store.surface(self.surfaces[0])?; + let b = store.surface(self.surfaces[1])?; + let mut result: Option = None; + for (i, tube) in self.uv_tubes.iter().enumerate() { + if self.anchors[i + 1].parameter < range.lo || self.anchors[i].parameter > range.hi { + continue; + } + let box_a: UVBox = [tube[0], tube[1]]; + let box_b: UVBox = [tube[2], tube[3]]; + let ba = a.enclose(box_a)?; + let bb = b.enclose(box_b)?; + let mut axes = [Interval::point(0.0)?; 3]; + for j in 0..3 { + axes[j] = Interval::new( + ba.axes[j].lo.max(bb.axes[j].lo), + ba.axes[j].hi.min(bb.axes[j].hi), + ) + .map_err(|_| { + GeometryError::InvalidGeometry("disjoint trace support boxes".into()) + })?; + } + if let Some((tighter, _)) = perpendicular_cylinder_enclosure( + self, + store, + i, + Interval::new( + range.lo.max(self.anchors[i].parameter), + range.hi.min(self.anchors[i + 1].parameter), + )?, + PatchBounds { axes }, + )? { + for axis in 0..3 { + axes[axis] = + intersect_interval(axes[axis], tighter.axes[axis]).unwrap_or(axes[axis]); + } + } + result = Some(match result { + None => PatchBounds { axes }, + Some(previous) => PatchBounds { + axes: std::array::from_fn(|j| previous.axes[j].hull(axes[j])), + }, + }); + } + result.ok_or_else(|| GeometryError::InvalidGeometry("empty trace interval".into())) + } + + pub(crate) fn certified_chord_deviation( + &self, + range: Interval, + store: &GeometryStore, + ) -> Result, GeometryError> { + let Some(segment) = self + .anchors + .windows(2) + .position(|pair| range.lo >= pair[0].parameter && range.hi <= pair[1].parameter) + else { + return Ok(None); + }; + let a = store.surface(self.surfaces[0])?; + let b = store.surface(self.surfaces[1])?; + let tube = self.uv_tubes[segment]; + let box_a = a.enclose([tube[0], tube[1]])?; + let box_b = b.enclose([tube[2], tube[3]])?; + let mut axes = [Interval::point(0.0)?; 3]; + for coordinate in 0..3 { + let Some(overlap) = intersect_interval(box_a.axes[coordinate], box_b.axes[coordinate]) + else { + return Ok(None); + }; + axes[coordinate] = overlap; + } + Ok( + perpendicular_cylinder_enclosure(self, store, segment, range, PatchBounds { axes })? + .map(|(_, error)| error), + ) + } +} + +fn traced_point( + a: &SurfaceGeometry, + b: &SurfaceGeometry, + uv: [f64; 4], + point: Point3, + diff: Point3, + velocity: Point3, + gauge: Point3, +) -> Result { + let direction = unit(cross( + a.normal_at([uv[0], uv[1]])?, + b.normal_at([uv[2], uv[3]])?, + ))?; + let projection = dot(direction, gauge); + if projection.abs() <= 64.0 * f64::EPSILON { + return Err(GeometryError::UnresolvedIntersection( + "trace guide is tangent to correction plane".into(), + )); + } + Ok(IntersectionPoint { + point, + uv_a: [uv[0], uv[1]], + uv_b: [uv[2], uv[3]], + tangent: scale(direction, dot(velocity, gauge) / projection), + support_error: norm(diff), + }) +} diff --git a/main/opengeometry/src/brep/geometry/trace/mod.rs b/main/opengeometry/src/brep/geometry/trace/mod.rs new file mode 100644 index 0000000..acec2ff --- /dev/null +++ b/main/opengeometry/src/brep/geometry/trace/mod.rs @@ -0,0 +1,54 @@ +mod enclosure; +mod evaluate; +#[cfg(test)] +mod tests; + +use crate::brep::error::GeometryError; +use crate::brep::frame::UV; +use crate::math::Point3; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct SsiBudget { + pub(crate) max_patch_pairs: usize, + pub(crate) max_steps_per_branch: usize, + max_newton_iterations: usize, + pub(crate) max_subdivision_depth: usize, +} + +#[derive(Clone, Copy, Debug)] +pub struct IntersectionPoint { + pub(crate) point: Point3, + pub(crate) uv_a: UV, + pub(crate) uv_b: UV, + pub(super) tangent: Point3, + pub support_error: f64, +} + +impl Default for SsiBudget { + fn default() -> Self { + Self { + max_patch_pairs: 200_000, + max_steps_per_branch: 50_000, + max_newton_iterations: 12, + max_subdivision_depth: 48, + } + } +} +impl SsiBudget { + pub(crate) fn validate(self) -> Result<(), GeometryError> { + if self.max_patch_pairs == 0 + || self.max_steps_per_branch == 0 + || self.max_newton_iterations == 0 + || self.max_newton_iterations > 64 + || self.max_subdivision_depth == 0 + || self.max_subdivision_depth > 128 + { + return Err(GeometryError::InvalidGeometry( + "invalid SSI resource limits".into(), + )); + } + Ok(()) + } +} diff --git a/main/opengeometry/src/brep/geometry/trace/tests.rs b/main/opengeometry/src/brep/geometry/trace/tests.rs new file mode 100644 index 0000000..8998e74 --- /dev/null +++ b/main/opengeometry/src/brep/geometry/trace/tests.rs @@ -0,0 +1,98 @@ +use super::SsiBudget; +use crate::brep::error::GeometryError; +use crate::brep::frame::Frame3; +use crate::brep::geometry::curve::{Curve, CurveGeometry}; +use crate::brep::geometry::intersection_definition::{IntersectionDefinition, TraceAnchor}; +use crate::brep::geometry::store::GeometryStore; +use crate::brep::geometry::surface::SurfaceGeometry; +use crate::math::{add, dot, norm, scale, sub, Interval}; + +fn circle_trace() -> GeometryStore { + let mut store = GeometryStore::new(); + store.surfaces.push(SurfaceGeometry::Sphere { + frame: Frame3::IDENTITY, + radius: 1.0, + }); + store.surfaces.push(SurfaceGeometry::Plane { + frame: Frame3::IDENTITY, + }); + let mut anchors = Vec::new(); + let mut uv_tubes = Vec::new(); + for i in 0..=8 { + let u = i as f64 * 0.1; + anchors.push(TraceAnchor { + parameter: u, + point: [u.cos(), u.sin(), 0.0], + uv_a: [u, 0.0], + uv_b: [u.cos(), u.sin()], + }); + if i > 0 { + let left = (i - 1) as f64 * 0.1; + uv_tubes.push([ + Interval::new(left - 0.02, u + 0.02).unwrap(), + Interval::new(-0.02, 0.02).unwrap(), + Interval::new( + left.cos().min(u.cos()) - 0.02, + left.cos().max(u.cos()) + 0.02, + ) + .unwrap(), + Interval::new( + left.sin().min(u.sin()) - 0.02, + left.sin().max(u.sin()) + 0.02, + ) + .unwrap(), + ]); + } + } + store.intersections.push(IntersectionDefinition { + surfaces: [0, 1], + anchors, + uv_tubes, + residual_tolerance: 1e-12, + }); + store + .curves + .push(CurveGeometry::Intersection { definition: 0 }); + store +} +#[test] +fn intersection_evaluation_corrects_off_linear_predictor() { + let store = circle_trace(); + store.intersections[0].validate(&store).unwrap(); + let point = store.intersections[0].evaluate(0.35, &store).unwrap(); + assert!((norm(point.point) - 1.0).abs() < 1e-12); + assert!(point.point[2].abs() < 1e-12); + assert!(point.support_error < 1e-12); + let chord = scale( + add( + store.intersections[0].anchors[3].point, + store.intersections[0].anchors[4].point, + ), + 0.5, + ); + assert!(norm(sub(point.point, chord)) > 0.001); + assert!(dot(point.point, point.tangent).abs() < 1e-12); + assert!(store + .curve(0) + .unwrap() + .enclose(Interval::new(0.3, 0.4).unwrap()) + .unwrap() + .contains(point.point)); +} +#[test] +fn budget_failure_and_invalid_anchor_are_explicit() { + let mut store = circle_trace(); + assert!(matches!( + store.intersections[0].evaluate_with_budget( + 0.35, + &store, + SsiBudget { + max_newton_iterations: 1, + ..SsiBudget::default() + } + ), + Err(GeometryError::UnresolvedIntersection(_)) + )); + store.intersections[0].anchors[3].point[2] = 0.1; + assert!(store.intersections[0].validate(&store).is_err()); +} diff --git a/main/opengeometry/src/brep/halfedge.rs b/main/opengeometry/src/brep/halfedge.rs deleted file mode 100644 index 8152570..0000000 --- a/main/opengeometry/src/brep/halfedge.rs +++ /dev/null @@ -1,40 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Serialize, Deserialize)] -pub struct HalfEdge { - pub id: u32, - pub from: u32, - pub to: u32, - pub twin: Option, - pub next: Option, - pub prev: Option, - pub edge: u32, - pub face: Option, - pub loop_ref: Option, - pub wire_ref: Option, -} - -impl HalfEdge { - pub fn new( - id: u32, - from: u32, - to: u32, - edge: u32, - face: Option, - loop_ref: Option, - wire_ref: Option, - ) -> Self { - Self { - id, - from, - to, - twin: None, - next: None, - prev: None, - edge, - face, - loop_ref, - wire_ref, - } - } -} diff --git a/main/opengeometry/src/brep/loop.rs b/main/opengeometry/src/brep/loop.rs deleted file mode 100644 index e323ede..0000000 --- a/main/opengeometry/src/brep/loop.rs +++ /dev/null @@ -1,20 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Serialize, Deserialize)] -pub struct Loop { - pub id: u32, - pub halfedge: u32, - pub face: u32, - pub is_hole: bool, -} - -impl Loop { - pub fn new(id: u32, halfedge: u32, face: u32, is_hole: bool) -> Self { - Self { - id, - halfedge, - face, - is_hole, - } - } -} diff --git a/main/opengeometry/src/brep/mod.rs b/main/opengeometry/src/brep/mod.rs index b0d7b85..4033673 100644 --- a/main/opengeometry/src/brep/mod.rs +++ b/main/opengeometry/src/brep/mod.rs @@ -1,1134 +1,47 @@ -pub mod builder; -pub mod edge; -pub mod error; -pub mod face; -pub mod geometry; -pub mod halfedge; -pub mod r#loop; -pub mod shell; -pub mod validity; -pub mod vertex; -pub mod wire; - -use std::cell::RefCell; -use std::collections::{hash_map::DefaultHasher, HashMap, HashSet}; -use std::hash::{Hash, Hasher}; - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -use crate::{ - operations::triangulate::{compute_polygon_normal, triangulate_polygon_with_holes}, - spatial::placement::Placement3D, +mod accuracy; +mod body_type; +mod bounds; +mod builder; +#[cfg(test)] +mod diagnostics; +mod error; +mod frame; +mod geometry; +mod pcurve; +mod placement; +mod similarity; +#[cfg(test)] +mod tests; +mod topology; +mod validate; + +pub use accuracy::Accuracy; +pub use body_type::BodyType; +pub use bounds::PatchBounds; +pub(super) use builder::{ + boundary, dimensions, padded_uv_bounds, plane_boundary, uv_line, Builder, Use, WireUse, +}; +pub(super) use error::coverage_gap; +pub use error::GeometryError; +pub use frame::Frame3; +pub(super) use frame::{checked, unit, UVBox, GROUND, UV}; +pub(super) use geometry::{ + bounds_chord_deviation, point_segment_distance, Curve, IntersectionDefinition, SsiBudget, + TraceAnchor, +}; +pub use geometry::{ + CurveGeometry, GeometryStore, IntersectionPoint, Surface, SurfaceChart, SurfaceGeometry, + SurfaceJet2, +}; +pub(super) use pcurve::{period_lifted_uv, IntersectionSide, PcurveGeometry}; +pub use placement::placed; +pub use similarity::Similarity3; +#[cfg(test)] +pub(super) use topology::{circular_face, coarse_accuracy, fine_accuracy}; +pub use topology::{ + BrepEnvelope, Edge, Face, FaceProvenance, FaceRole, FaceSource, HalfEdge, Topology, Vertex, +}; +pub(super) use topology::{ + EdgeGeometry, GeometryQuality, HalfEdgeGeometryUse, Loop, Orientation, Shell, SolidRegion, + TrimRegion, Wire, }; - -pub use builder::BrepBuilder; -pub use edge::Edge; -pub use error::{BrepError, BrepErrorKind}; -pub use face::Face; -pub use geometry::{CurveGeometry, SurfaceGeometry}; -pub use halfedge::HalfEdge; -pub use r#loop::Loop; -pub use shell::Shell; -pub use vertex::Vertex; -pub use wire::Wire; - -const VALIDATION_GUARD_FACTOR: usize = 4; -const TRANSFORM_CACHE_LIMIT: usize = 128; - -#[derive(Clone, Copy, Eq, Hash, PartialEq)] -struct TransformCacheKey { - brep_id: Uuid, - local_signature: u64, - anchor: [u64; 3], - translation: [u64; 3], - rotation: [u64; 3], - scale: [u64; 3], -} - -thread_local! { - static WORLD_BREP_TRANSFORM_CACHE: RefCell> = - RefCell::new(HashMap::new()); -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct Brep { - pub id: Uuid, - pub vertices: Vec, - pub halfedges: Vec, - pub edges: Vec, - pub loops: Vec, - pub faces: Vec, - pub wires: Vec, - pub shells: Vec, -} - -impl Brep { - pub fn new(id: Uuid) -> Self { - Self { - id, - vertices: Vec::new(), - halfedges: Vec::new(), - edges: Vec::new(), - loops: Vec::new(), - faces: Vec::new(), - wires: Vec::new(), - shells: Vec::new(), - } - } - - pub fn clear(&mut self) { - self.vertices.clear(); - self.halfedges.clear(); - self.edges.clear(); - self.loops.clear(); - self.faces.clear(); - self.wires.clear(); - self.shells.clear(); - } - - pub fn get_vertex_count(&self) -> u32 { - self.vertices.len() as u32 - } - - pub fn get_halfedge_count(&self) -> u32 { - self.halfedges.len() as u32 - } - - pub fn get_edge_count(&self) -> u32 { - self.edges.len() as u32 - } - - pub fn get_loop_count(&self) -> u32 { - self.loops.len() as u32 - } - - pub fn get_face_count(&self) -> u32 { - self.faces.len() as u32 - } - - pub fn get_wire_count(&self) -> u32 { - self.wires.len() as u32 - } - - pub fn get_shell_count(&self) -> u32 { - self.shells.len() as u32 - } - - pub fn get_flattened_vertices(&self) -> Vec { - self.vertices.iter().map(|vertex| vertex.position).collect() - } - - /** - * Applies the given placement's world transformation to the BREP. This function transforms all vertices in the BREP from their local space to world space using the transformation defined by the placement. The transformation includes translation, rotation, and scaling as specified in the placement's world matrix. After applying this function, the vertices in the BREP will be updated to reflect their new positions in world space, which can then be used for rendering or further processing. - */ - pub fn apply_transform(&mut self, placement: &Placement3D) { - let placement_matrix = placement.world_matrix(); - self.apply_point_transform( - |v: Vector3| { - let mut p = v; - p.apply_matrix4(placement_matrix.clone()); - p - }, - placement.scale().x, - ); - } - - pub fn apply_point_transform( - &mut self, - transform_point: impl Fn(Vector3) -> Vector3, - scale: f64, - ) { - for vertex in &mut self.vertices { - vertex.position = transform_point(vertex.position); - } - - let has_analytic_geometry = self.edges.iter().any(|edge| edge.curve.is_some()) - || self.faces.iter().any(|face| face.surface.is_some()); - if has_analytic_geometry { - for edge in &mut self.edges { - if let Some(curve) = &edge.curve { - edge.curve = Some(curve.transformed_with(&transform_point, scale)); - } - } - for face in &mut self.faces { - if let Some(surface) = &face.surface { - face.surface = Some(surface.transformed_with(&transform_point, scale)); - } - } - } - - if !self.faces.is_empty() { - self.recompute_face_normals(); - } - } - - pub fn transformed(&self, placement: &Placement3D) -> Brep { - let cache_key = self.transform_cache_key(placement); - if let Some(cached) = - WORLD_BREP_TRANSFORM_CACHE.with(|cache| cache.borrow().get(&cache_key).cloned()) - { - return cached; - } - - let mut transformed = self.clone(); - transformed.apply_transform(placement); - - WORLD_BREP_TRANSFORM_CACHE.with(|cache| { - let mut cache_ref = cache.borrow_mut(); - if cache_ref.len() >= TRANSFORM_CACHE_LIMIT { - cache_ref.clear(); - } - cache_ref.insert(cache_key, transformed.clone()); - }); - - transformed - } - - pub fn bounds_center(&self) -> Option { - let first_vertex = self.vertices.first()?; - let mut min_x = first_vertex.position.x; - let mut min_y = first_vertex.position.y; - let mut min_z = first_vertex.position.z; - let mut max_x = first_vertex.position.x; - let mut max_y = first_vertex.position.y; - let mut max_z = first_vertex.position.z; - - for vertex in &self.vertices[1..] { - min_x = min_x.min(vertex.position.x); - min_y = min_y.min(vertex.position.y); - min_z = min_z.min(vertex.position.z); - max_x = max_x.max(vertex.position.x); - max_y = max_y.max(vertex.position.y); - max_z = max_z.max(vertex.position.z); - } - - Some(Vector3::new( - (min_x + max_x) * 0.5, - (min_y + max_y) * 0.5, - (min_z + max_z) * 0.5, - )) - } - - fn transform_cache_key(&self, placement: &Placement3D) -> TransformCacheKey { - TransformCacheKey { - brep_id: self.id, - local_signature: self.local_signature(), - anchor: vector_bits(placement.anchor), - translation: vector_bits(placement.translation()), - rotation: vector_bits(placement.rotation()), - scale: vector_bits(placement.scale()), - } - } - - fn local_signature(&self) -> u64 { - let mut hasher = DefaultHasher::new(); - - self.vertices.len().hash(&mut hasher); - self.halfedges.len().hash(&mut hasher); - self.edges.len().hash(&mut hasher); - self.loops.len().hash(&mut hasher); - self.faces.len().hash(&mut hasher); - self.wires.len().hash(&mut hasher); - self.shells.len().hash(&mut hasher); - - for vertex in &self.vertices { - vertex.id.hash(&mut hasher); - vertex.position.x.to_bits().hash(&mut hasher); - vertex.position.y.to_bits().hash(&mut hasher); - vertex.position.z.to_bits().hash(&mut hasher); - vertex.outgoing_halfedge.hash(&mut hasher); - } - - for halfedge in &self.halfedges { - halfedge.id.hash(&mut hasher); - halfedge.from.hash(&mut hasher); - halfedge.to.hash(&mut hasher); - halfedge.twin.hash(&mut hasher); - halfedge.next.hash(&mut hasher); - halfedge.prev.hash(&mut hasher); - halfedge.edge.hash(&mut hasher); - halfedge.face.hash(&mut hasher); - halfedge.loop_ref.hash(&mut hasher); - halfedge.wire_ref.hash(&mut hasher); - } - - for edge in &self.edges { - edge.id.hash(&mut hasher); - edge.halfedge.hash(&mut hasher); - edge.twin_halfedge.hash(&mut hasher); - } - - for loop_ref in &self.loops { - loop_ref.id.hash(&mut hasher); - loop_ref.halfedge.hash(&mut hasher); - loop_ref.face.hash(&mut hasher); - loop_ref.is_hole.hash(&mut hasher); - } - - for face in &self.faces { - face.id.hash(&mut hasher); - face.normal.x.to_bits().hash(&mut hasher); - face.normal.y.to_bits().hash(&mut hasher); - face.normal.z.to_bits().hash(&mut hasher); - face.outer_loop.hash(&mut hasher); - face.inner_loops.hash(&mut hasher); - face.shell_ref.hash(&mut hasher); - } - - for wire in &self.wires { - wire.id.hash(&mut hasher); - wire.halfedges.hash(&mut hasher); - wire.is_closed.hash(&mut hasher); - } - - for shell in &self.shells { - shell.id.hash(&mut hasher); - shell.faces.hash(&mut hasher); - shell.is_closed.hash(&mut hasher); - } - - hasher.finish() - } - - pub fn recompute_face_normals(&mut self) { - for face_index in 0..self.faces.len() { - let outer_loop = self.faces[face_index].outer_loop; - let loop_indices = self.get_loop_vertex_indices(outer_loop); - if let Some(normal) = compute_loop_normal(self, &loop_indices) { - self.faces[face_index].set_normal(normal); - } - } - } - - pub fn get_edge_endpoints(&self, edge_id: u32) -> Option<(u32, u32)> { - let edge = self.edges.get(edge_id as usize)?; - let halfedge = self.halfedges.get(edge.halfedge as usize)?; - Some((halfedge.from, halfedge.to)) - } - - pub fn collect_outline_segments(&self) -> Vec<(u32, u32)> { - let mut segments = Vec::new(); - for edge in &self.edges { - if let Some((from, to)) = self.get_edge_endpoints(edge.id) { - segments.push((from, to)); - } - } - segments - } - - /// Collects only feature edges whose adjacent faces form a visible crease - /// or whose topology leaves them on a boundary. - pub fn collect_feature_outline_segments(&self, crease_cos_threshold: f64) -> Vec<(u32, u32)> { - let mut segments = Vec::new(); - - for edge in &self.edges { - let Some((from, to)) = self.get_edge_endpoints(edge.id) else { - continue; - }; - - let Some(halfedge) = self.halfedges.get(edge.halfedge as usize) else { - continue; - }; - - let primary_face = halfedge - .face - .and_then(|face_id| self.faces.get(face_id as usize)); - let twin_face = edge - .twin_halfedge - .and_then(|halfedge_id| self.halfedges.get(halfedge_id as usize)) - .and_then(|halfedge_ref| halfedge_ref.face) - .and_then(|face_id| self.faces.get(face_id as usize)); - - let include = match (primary_face, twin_face) { - (Some(a), Some(b)) => { - let dot = a.normal.dot(&b.normal); - dot < crease_cos_threshold - } - _ => true, - }; - - if include { - segments.push((from, to)); - } - } - - segments - } - - pub fn get_loop_halfedges(&self, loop_id: u32) -> Result, BrepError> { - let loop_ref = self.loops.get(loop_id as usize).ok_or_else(|| { - BrepError::new( - BrepErrorKind::InvalidLoop, - format!("Loop {} does not exist", loop_id), - ) - })?; - - let start = loop_ref.halfedge; - if self.halfedges.get(start as usize).is_none() { - return Err(BrepError::new( - BrepErrorKind::InvalidHalfEdge, - format!("Loop {} points to invalid halfedge {}", loop_id, start), - )); - } - - let mut result = Vec::new(); - let mut visited = HashSet::new(); - let mut current = start; - let guard_limit = self.halfedges.len().saturating_mul(VALIDATION_GUARD_FACTOR); - - for _ in 0..guard_limit { - if !visited.insert(current) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!( - "Loop {} revisits halfedge {} before closure", - loop_id, current - ), - )); - } - - result.push(current); - - let halfedge = &self.halfedges[current as usize]; - let Some(next) = halfedge.next else { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!( - "Loop {} contains halfedge {} without next link", - loop_id, current - ), - )); - }; - - if next == start { - return Ok(result); - } - - if self.halfedges.get(next as usize).is_none() { - return Err(BrepError::new( - BrepErrorKind::InvalidHalfEdge, - format!("Loop {} references invalid next halfedge {}", loop_id, next), - )); - } - - current = next; - } - - Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!("Loop {} exceeded traversal guard", loop_id), - )) - } - - pub fn get_loop_vertex_indices(&self, loop_id: u32) -> Vec { - self.get_loop_halfedges(loop_id) - .map(|halfedges| { - halfedges - .iter() - .filter_map(|halfedge_id| self.halfedges.get(*halfedge_id as usize)) - .map(|halfedge| halfedge.from) - .collect() - }) - .unwrap_or_default() - } - - pub fn get_wire_vertex_indices(&self, wire_id: u32) -> Vec { - let Some(wire) = self.wires.get(wire_id as usize) else { - return Vec::new(); - }; - - if wire.halfedges.is_empty() { - return Vec::new(); - } - - let mut vertices = Vec::with_capacity(wire.halfedges.len() + 1); - for halfedge_id in &wire.halfedges { - if let Some(halfedge) = self.halfedges.get(*halfedge_id as usize) { - vertices.push(halfedge.from); - } - } - - if !wire.is_closed { - if let Some(last_halfedge_id) = wire.halfedges.last() { - if let Some(last_halfedge) = self.halfedges.get(*last_halfedge_id as usize) { - vertices.push(last_halfedge.to); - } - } - } - - vertices - } - - pub fn get_wire_vertex_buffer(&self, wire_id: u32, repeat_first_when_closed: bool) -> Vec { - let Some(wire) = self.wires.get(wire_id as usize) else { - return Vec::new(); - }; - - let mut vertex_ids = self.get_wire_vertex_indices(wire_id); - if repeat_first_when_closed && wire.is_closed { - if let Some(first_vertex_id) = vertex_ids.first().copied() { - vertex_ids.push(first_vertex_id); - } - } - - let mut vertex_buffer = Vec::with_capacity(vertex_ids.len() * 3); - for vertex_id in vertex_ids { - let Some(vertex) = self.vertices.get(vertex_id as usize) else { - continue; - }; - - vertex_buffer.push(vertex.position.x); - vertex_buffer.push(vertex.position.y); - vertex_buffer.push(vertex.position.z); - } - - vertex_buffer - } - - pub fn get_vertices_by_face_id(&self, face_id: u32) -> Vec { - let Some(face) = self.faces.iter().find(|face| face.id == face_id) else { - return Vec::new(); - }; - - self.get_loop_vertex_indices(face.outer_loop) - .into_iter() - .filter_map(|vertex_id| self.vertices.get(vertex_id as usize)) - .map(|vertex| vertex.position) - .collect() - } - - pub fn get_vertices_and_holes_by_face_id( - &self, - face_id: u32, - ) -> (Vec, Vec>) { - let Some(face) = self.faces.iter().find(|face| face.id == face_id) else { - return (Vec::new(), Vec::new()); - }; - - let face_vertices = self - .get_loop_vertex_indices(face.outer_loop) - .into_iter() - .filter_map(|vertex_id| self.vertices.get(vertex_id as usize)) - .map(|vertex| vertex.position) - .collect(); - - let mut holes_vertices = Vec::new(); - for loop_id in &face.inner_loops { - let hole_vertices: Vec = self - .get_loop_vertex_indices(*loop_id) - .into_iter() - .filter_map(|vertex_id| self.vertices.get(vertex_id as usize)) - .map(|vertex| vertex.position) - .collect(); - - holes_vertices.push(hole_vertices); - } - - (face_vertices, holes_vertices) - } - - /// Flattens every face into a triangle vertex buffer suitable for - /// immediate-mode rendering consumers. - pub fn get_triangle_vertex_buffer(&self) -> Vec { - let mut vertex_buffer = Vec::new(); - - for face in &self.faces { - let (face_vertices, holes_vertices) = self.get_vertices_and_holes_by_face_id(face.id); - if face_vertices.len() < 3 { - continue; - } - - let triangles = triangulate_polygon_with_holes(&face_vertices, &holes_vertices); - let all_vertices: Vec = face_vertices - .into_iter() - .chain(holes_vertices.into_iter().flatten()) - .collect(); - - for triangle in triangles { - for vertex_index in triangle { - let Some(vertex) = all_vertices.get(vertex_index) else { - continue; - }; - vertex_buffer.push(vertex.x); - vertex_buffer.push(vertex.y); - vertex_buffer.push(vertex.z); - } - } - } - - vertex_buffer - } - - /// Serializes every edge in the BRep as a flat line-segment buffer. - pub fn get_outline_vertex_buffer(&self) -> Vec { - self.outline_buffer_from_segments(self.collect_outline_segments()) - } - - /// Serializes only feature/boundary edges to keep outlines readable on - /// heavily faceted boolean results. - pub fn get_feature_outline_vertex_buffer(&self, crease_cos_threshold: f64) -> Vec { - self.outline_buffer_from_segments( - self.collect_feature_outline_segments(crease_cos_threshold), - ) - } - - fn outline_buffer_from_segments(&self, segments: Vec<(u32, u32)>) -> Vec { - let mut vertex_buffer = Vec::new(); - - for (start_id, end_id) in segments { - let Some(start_vertex) = self.vertices.get(start_id as usize) else { - continue; - }; - let Some(end_vertex) = self.vertices.get(end_id as usize) else { - continue; - }; - - vertex_buffer.push(start_vertex.position.x); - vertex_buffer.push(start_vertex.position.y); - vertex_buffer.push(start_vertex.position.z); - - vertex_buffer.push(end_vertex.position.x); - vertex_buffer.push(end_vertex.position.y); - vertex_buffer.push(end_vertex.position.z); - } - - vertex_buffer - } - - pub fn validate_topology(&self) -> Result<(), BrepError> { - for (index, vertex) in self.vertices.iter().enumerate() { - if vertex.id as usize != index { - return Err(BrepError::new( - BrepErrorKind::InvalidVertex, - format!("Vertex id mismatch at index {} (id={})", index, vertex.id), - )); - } - - if let Some(outgoing) = vertex.outgoing_halfedge { - let Some(halfedge) = self.halfedges.get(outgoing as usize) else { - return Err(BrepError::new( - BrepErrorKind::InvalidHalfEdge, - format!( - "Vertex {} references missing outgoing halfedge {}", - vertex.id, outgoing - ), - )); - }; - - if halfedge.from != vertex.id { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!( - "Vertex {} outgoing halfedge {} starts at {}", - vertex.id, outgoing, halfedge.from - ), - )); - } - } - } - - let mut edge_to_halfedges: HashMap> = HashMap::new(); - let mut edge_to_faces: HashMap> = HashMap::new(); - - for (index, halfedge) in self.halfedges.iter().enumerate() { - if halfedge.id as usize != index { - return Err(BrepError::new( - BrepErrorKind::InvalidHalfEdge, - format!( - "Halfedge id mismatch at index {} (id={})", - index, halfedge.id - ), - )); - } - - if self.vertices.get(halfedge.from as usize).is_none() - || self.vertices.get(halfedge.to as usize).is_none() - { - return Err(BrepError::new( - BrepErrorKind::InvalidVertex, - format!( - "Halfedge {} has invalid vertex references ({} -> {})", - halfedge.id, halfedge.from, halfedge.to - ), - )); - } - - let Some(edge) = self.edges.get(halfedge.edge as usize) else { - return Err(BrepError::new( - BrepErrorKind::InvalidEdge, - format!( - "Halfedge {} references missing edge {}", - halfedge.id, halfedge.edge - ), - )); - }; - - if edge.id != halfedge.edge { - return Err(BrepError::new( - BrepErrorKind::InvalidEdge, - format!( - "Halfedge {} edge reference mismatch (edge id={}, ref={})", - halfedge.id, edge.id, halfedge.edge - ), - )); - } - - edge_to_halfedges - .entry(halfedge.edge) - .or_default() - .push(halfedge.id); - - if let Some(face_id) = halfedge.face { - if self.faces.get(face_id as usize).is_none() { - return Err(BrepError::new( - BrepErrorKind::InvalidFace, - format!( - "Halfedge {} references missing face {}", - halfedge.id, face_id - ), - )); - } - edge_to_faces - .entry(halfedge.edge) - .or_default() - .insert(face_id); - } - - if let Some(twin_id) = halfedge.twin { - let Some(twin) = self.halfedges.get(twin_id as usize) else { - return Err(BrepError::new( - BrepErrorKind::InvalidHalfEdge, - format!( - "Halfedge {} references missing twin {}", - halfedge.id, twin_id - ), - )); - }; - - if twin.twin != Some(halfedge.id) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!( - "Halfedge {} twin symmetry broken with {}", - halfedge.id, twin_id - ), - )); - } - - if !(halfedge.from == twin.to && halfedge.to == twin.from) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!( - "Halfedge {} twin {} does not reverse endpoints", - halfedge.id, twin_id - ), - )); - } - } - - if let Some(next_id) = halfedge.next { - let Some(next) = self.halfedges.get(next_id as usize) else { - return Err(BrepError::new( - BrepErrorKind::InvalidHalfEdge, - format!("Halfedge {} has missing next {}", halfedge.id, next_id), - )); - }; - - if next.prev != Some(halfedge.id) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!("Halfedge {} next/prev symmetry is broken", halfedge.id), - )); - } - } - - if let Some(prev_id) = halfedge.prev { - let Some(prev) = self.halfedges.get(prev_id as usize) else { - return Err(BrepError::new( - BrepErrorKind::InvalidHalfEdge, - format!("Halfedge {} has missing prev {}", halfedge.id, prev_id), - )); - }; - - if prev.next != Some(halfedge.id) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!("Halfedge {} prev/next symmetry is broken", halfedge.id), - )); - } - } - - if let Some(loop_id) = halfedge.loop_ref { - if self.loops.get(loop_id as usize).is_none() { - return Err(BrepError::new( - BrepErrorKind::InvalidLoop, - format!( - "Halfedge {} references missing loop {}", - halfedge.id, loop_id - ), - )); - } - } - - if let Some(wire_id) = halfedge.wire_ref { - if self.wires.get(wire_id as usize).is_none() { - return Err(BrepError::new( - BrepErrorKind::InvalidWire, - format!( - "Halfedge {} references missing wire {}", - halfedge.id, wire_id - ), - )); - } - } - } - - for (index, edge) in self.edges.iter().enumerate() { - if edge.id as usize != index { - return Err(BrepError::new( - BrepErrorKind::InvalidEdge, - format!("Edge id mismatch at index {} (id={})", index, edge.id), - )); - } - - let Some(halfedge_list) = edge_to_halfedges.get(&edge.id) else { - return Err(BrepError::new( - BrepErrorKind::InvalidEdge, - format!("Edge {} has no halfedges", edge.id), - )); - }; - - if halfedge_list.is_empty() || halfedge_list.len() > 2 { - return Err(BrepError::new( - BrepErrorKind::NonManifoldEdge, - format!( - "Edge {} has invalid incidence count {}", - edge.id, - halfedge_list.len() - ), - )); - } - - if !halfedge_list.contains(&edge.halfedge) { - return Err(BrepError::new( - BrepErrorKind::InvalidEdge, - format!( - "Edge {} primary halfedge {} is not linked to edge", - edge.id, edge.halfedge - ), - )); - } - - if let Some(twin_halfedge) = edge.twin_halfedge { - if !halfedge_list.contains(&twin_halfedge) { - return Err(BrepError::new( - BrepErrorKind::InvalidEdge, - format!( - "Edge {} twin halfedge {} is not linked to edge", - edge.id, twin_halfedge - ), - )); - } - } - - if let Some(face_set) = edge_to_faces.get(&edge.id) { - if face_set.len() > 2 { - return Err(BrepError::new( - BrepErrorKind::NonManifoldEdge, - format!("Edge {} touches more than two faces", edge.id), - )); - } - } - } - - for loop_ref in &self.loops { - if loop_ref.id as usize >= self.loops.len() { - return Err(BrepError::new( - BrepErrorKind::InvalidLoop, - format!("Loop {} has invalid id", loop_ref.id), - )); - } - - let Some(face) = self.faces.get(loop_ref.face as usize) else { - return Err(BrepError::new( - BrepErrorKind::InvalidFace, - format!( - "Loop {} references missing face {}", - loop_ref.id, loop_ref.face - ), - )); - }; - - if loop_ref.is_hole { - if !face.inner_loops.contains(&loop_ref.id) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!( - "Hole loop {} is not listed in face {}", - loop_ref.id, face.id - ), - )); - } - } else if face.outer_loop != loop_ref.id { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!("Face {} outer loop mismatch", face.id), - )); - } - - let loop_halfedges = self.get_loop_halfedges(loop_ref.id)?; - if loop_halfedges.len() < 3 { - return Err(BrepError::new( - BrepErrorKind::DegenerateLoop, - format!("Loop {} has fewer than three halfedges", loop_ref.id), - )); - } - - for halfedge_id in loop_halfedges { - let halfedge = &self.halfedges[halfedge_id as usize]; - if halfedge.loop_ref != Some(loop_ref.id) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!("Halfedge {} loop reference mismatch", halfedge_id), - )); - } - - if halfedge.face != Some(loop_ref.face) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!("Halfedge {} face reference mismatch", halfedge_id), - )); - } - } - } - - for wire in &self.wires { - if wire.id as usize >= self.wires.len() { - return Err(BrepError::new( - BrepErrorKind::InvalidWire, - format!("Wire {} has invalid id", wire.id), - )); - } - - if wire.halfedges.is_empty() { - return Err(BrepError::new( - BrepErrorKind::InvalidWire, - format!("Wire {} is empty", wire.id), - )); - } - - for (index, halfedge_id) in wire.halfedges.iter().enumerate() { - let Some(halfedge) = self.halfedges.get(*halfedge_id as usize) else { - return Err(BrepError::new( - BrepErrorKind::InvalidHalfEdge, - format!( - "Wire {} references missing halfedge {}", - wire.id, halfedge_id - ), - )); - }; - - if halfedge.wire_ref != Some(wire.id) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!( - "Wire {} halfedge {} wire_ref mismatch", - wire.id, halfedge_id - ), - )); - } - - if wire.is_closed { - let expected_next = wire.halfedges[(index + 1) % wire.halfedges.len()]; - let expected_prev = - wire.halfedges[(index + wire.halfedges.len() - 1) % wire.halfedges.len()]; - - if halfedge.next != Some(expected_next) || halfedge.prev != Some(expected_prev) - { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!("Closed wire {} has broken halfedge links", wire.id), - )); - } - } else { - if index == 0 && halfedge.prev.is_some() { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!("Open wire {} first halfedge must not have prev", wire.id), - )); - } - - if index + 1 == wire.halfedges.len() && halfedge.next.is_some() { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!("Open wire {} last halfedge must not have next", wire.id), - )); - } - - if index + 1 < wire.halfedges.len() { - let expected_next = wire.halfedges[index + 1]; - if halfedge.next != Some(expected_next) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!("Open wire {} has broken next link", wire.id), - )); - } - } - } - } - } - - for (index, face) in self.faces.iter().enumerate() { - if face.id as usize != index { - return Err(BrepError::new( - BrepErrorKind::InvalidFace, - format!("Face id mismatch at index {} (id={})", index, face.id), - )); - } - - let Some(outer_loop) = self.loops.get(face.outer_loop as usize) else { - return Err(BrepError::new( - BrepErrorKind::InvalidLoop, - format!( - "Face {} references missing outer loop {}", - face.id, face.outer_loop - ), - )); - }; - - if outer_loop.is_hole { - return Err(BrepError::new( - BrepErrorKind::InvalidLoop, - format!( - "Face {} outer loop {} marked as hole", - face.id, face.outer_loop - ), - )); - } - - for inner_loop_id in &face.inner_loops { - let Some(inner_loop) = self.loops.get(*inner_loop_id as usize) else { - return Err(BrepError::new( - BrepErrorKind::InvalidLoop, - format!( - "Face {} references missing inner loop {}", - face.id, inner_loop_id - ), - )); - }; - - if !inner_loop.is_hole { - return Err(BrepError::new( - BrepErrorKind::InvalidLoop, - format!( - "Face {} inner loop {} is not marked as hole", - face.id, inner_loop_id - ), - )); - } - } - - if let Some(shell_id) = face.shell_ref { - let Some(shell) = self.shells.get(shell_id as usize) else { - return Err(BrepError::new( - BrepErrorKind::InvalidShell, - format!("Face {} references missing shell {}", face.id, shell_id), - )); - }; - - if !shell.faces.contains(&face.id) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!("Face {} shell linkage is inconsistent", face.id), - )); - } - } - } - - for shell in &self.shells { - if shell.id as usize >= self.shells.len() { - return Err(BrepError::new( - BrepErrorKind::InvalidShell, - format!("Shell {} has invalid id", shell.id), - )); - } - - if shell.faces.is_empty() { - return Err(BrepError::new( - BrepErrorKind::InvalidShell, - format!("Shell {} is empty", shell.id), - )); - } - - let mut shell_edge_faces: HashMap> = HashMap::new(); - for face_id in &shell.faces { - let Some(face) = self.faces.get(*face_id as usize) else { - return Err(BrepError::new( - BrepErrorKind::InvalidFace, - format!("Shell {} references missing face {}", shell.id, face_id), - )); - }; - - if face.shell_ref != Some(shell.id) { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!( - "Shell {} face linkage mismatch for face {}", - shell.id, face.id - ), - )); - } - - let mut loop_ids = vec![face.outer_loop]; - loop_ids.extend(face.inner_loops.iter().copied()); - - for loop_id in loop_ids { - for halfedge_id in self.get_loop_halfedges(loop_id)? { - let halfedge = &self.halfedges[halfedge_id as usize]; - shell_edge_faces - .entry(halfedge.edge) - .or_default() - .insert(face.id); - } - } - } - - if shell.is_closed { - for (edge_id, faces) in shell_edge_faces { - if faces.len() != 2 { - return Err(BrepError::new( - BrepErrorKind::BrokenTopology, - format!( - "Closed shell {} has boundary edge {} with {} incident faces", - shell.id, - edge_id, - faces.len() - ), - )); - } - } - } - } - - Ok(()) - } -} - -fn vector_bits(vector: Vector3) -> [u64; 3] { - [vector.x.to_bits(), vector.y.to_bits(), vector.z.to_bits()] -} - -fn compute_loop_normal(brep: &Brep, loop_indices: &[u32]) -> Option { - if loop_indices.len() < 3 { - return None; - } - - let mut points = Vec::with_capacity(loop_indices.len()); - for vertex_id in loop_indices { - points.push(brep.vertices.get(*vertex_id as usize)?.position); - } - - compute_polygon_normal(&points) -} diff --git a/main/opengeometry/src/brep/pcurve.rs b/main/opengeometry/src/brep/pcurve.rs new file mode 100644 index 0000000..e0046c5 --- /dev/null +++ b/main/opengeometry/src/brep/pcurve.rs @@ -0,0 +1,40 @@ +use super::frame::UV; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "kind", deny_unknown_fields)] +pub(crate) enum PcurveGeometry { + Line2 { + origin: UV, + direction: UV, + }, + Conic2 { + origin: UV, + axis_a: UV, + axis_b: UV, + }, + ProjectedCurve { + curve: u32, + surface: u32, + chart: u32, + uv_hint: UV, + uv_rate: UV, + parameter_origin: f64, + }, + IntersectionSide { + definition: u32, + side: IntersectionSide, + }, +} +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub(crate) enum IntersectionSide { + A, + B, +} + +pub(crate) fn period_lifted_uv(mut uv: UV, periods: [Option; 2], lift: [i32; 2]) -> UV { + for axis in 0..2 { + uv[axis] += periods[axis].map_or(0.0, |period| period * f64::from(lift[axis])); + } + uv +} diff --git a/main/opengeometry/src/brep/placement/mod.rs b/main/opengeometry/src/brep/placement/mod.rs new file mode 100644 index 0000000..b994f24 --- /dev/null +++ b/main/opengeometry/src/brep/placement/mod.rs @@ -0,0 +1,276 @@ +#[cfg(test)] +mod tests; + +use super::error::GeometryError; +use super::frame::Frame3; +use super::geometry::{CurveGeometry, IntersectionDefinition, SurfaceGeometry}; +use super::pcurve::PcurveGeometry; +use super::topology::{BrepEnvelope, EdgeGeometry, GeometryQuality}; +use crate::math::{norm, scale as vector_scale, Interval}; + +pub fn placed( + input: &BrepEnvelope, + placement: Frame3, + scale: f64, +) -> Result { + input.validate()?; + placement.validate()?; + if !scale.is_finite() || scale <= 0.0 { + return Err(GeometryError::InvalidGeometry( + "placement scale must be positive and finite".into(), + )); + } + let mut output = input.clone(); + output.revision = input + .revision + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("BRep revision overflow".into()))?; + let metrics: Vec<_> = input + .geometry + .surfaces + .iter() + .map(|surface| metric(surface, scale)) + .collect(); + place_surfaces(&mut output.geometry.surfaces, placement, scale); + place_curves(&mut output.geometry.curves, placement, scale); + for vertex in &mut output.topology.vertices { + vertex.position = placement.point(vector_scale(vertex.position, scale)); + vertex.tolerance *= scale; + } + for edge in &mut output.topology.edges { + edge.tolerance *= scale; + if let EdgeGeometry::Curve { curve, range } = &mut edge.geometry { + if matches!( + input.geometry.curves[*curve as usize], + CurveGeometry::Line { .. } + ) { + *range = Interval::new(range.lo * scale, range.hi * scale)?; + } + } + } + place_pcurves(input, &mut output, &metrics, scale)?; + for face in &mut output.topology.faces { + for (i, metric) in metrics[face.surface as usize].iter().enumerate() { + face.trim.uv_bounds[i] = Interval::new( + face.trim.uv_bounds[i].lo * metric, + face.trim.uv_bounds[i].hi * metric, + )?; + } + } + place_intersections( + &mut output.geometry.intersections, + &metrics, + placement, + scale, + )?; + output.accuracy.geometric *= scale; + output.accuracy.intersection *= scale; + output.accuracy.tessellation *= scale; + output.accuracy.exchange *= scale; + if let GeometryQuality::Approximate { max_error } = &mut output.quality { + *max_error *= scale; + } + output.validate()?; + check_coordinate_precision(&output)?; + Ok(output) +} + +fn metric(surface: &SurfaceGeometry, scale: f64) -> [f64; 2] { + match surface { + SurfaceGeometry::Plane { .. } => [scale; 2], + SurfaceGeometry::Cylinder { .. } | SurfaceGeometry::Cone { .. } => [1.0, scale], + _ => [1.0; 2], + } +} + +fn place_surfaces(surfaces: &mut [SurfaceGeometry], placement: Frame3, scale: f64) { + for surface in surfaces { + match surface { + SurfaceGeometry::Plane { frame } | SurfaceGeometry::Cone { frame, .. } => { + transform_frame(frame, placement, scale) + } + SurfaceGeometry::Sphere { frame, radius } + | SurfaceGeometry::Cylinder { frame, radius } => { + transform_frame(frame, placement, scale); + *radius *= scale; + } + SurfaceGeometry::Torus { + frame, + major_radius, + minor_radius, + } => { + transform_frame(frame, placement, scale); + *major_radius *= scale; + *minor_radius *= scale; + } + } + } +} + +fn transform_frame(frame: &mut Frame3, placement: Frame3, scale: f64) { + frame.origin = placement.point(vector_scale(frame.origin, scale)); + frame.x = placement.vector(frame.x); + frame.y = placement.vector(frame.y); + frame.z = placement.vector(frame.z); +} + +fn place_curves(curves: &mut [CurveGeometry], placement: Frame3, scale: f64) { + for curve in curves { + match curve { + CurveGeometry::Line { origin, direction } => { + *origin = placement.point(vector_scale(*origin, scale)); + *direction = placement.vector(*direction); + } + CurveGeometry::Circle { frame, radius } => { + transform_frame(frame, placement, scale); + *radius *= scale; + } + CurveGeometry::Ellipse { + frame, + major_radius, + minor_radius, + } => { + transform_frame(frame, placement, scale); + *major_radius *= scale; + *minor_radius *= scale; + } + CurveGeometry::Intersection { .. } => {} + } + } +} + +fn place_pcurves( + input: &BrepEnvelope, + output: &mut BrepEnvelope, + metrics: &[[f64; 2]], + scale: f64, +) -> Result<(), GeometryError> { + let mut pcurve_metrics = vec![None; input.geometry.pcurves.len()]; + for use_ in &mut output.topology.halfedges { + let (Some(face), Some(pc)) = (use_.face, use_.geometry_use.pcurve) else { + continue; + }; + let metric = metrics[input.topology.faces[face as usize].surface as usize]; + let parameter_scale = match input.topology.edges[use_.edge as usize].geometry { + EdgeGeometry::Curve { curve, .. } + if matches!( + input.geometry.curves[curve as usize], + CurveGeometry::Line { .. } + ) => + { + scale + } + _ => 1.0, + }; + match pcurve_metrics[pc as usize] { + None => { + pcurve_metrics[pc as usize] = Some((metric, parameter_scale)); + } + Some(previous) if previous != (metric, parameter_scale) => { + let mut copy = input.geometry.pcurves[pc as usize].clone(); + pcurve(&mut copy, metric, parameter_scale)?; + use_.geometry_use.pcurve = Some(output.geometry.pcurves.len() as u32); + output.geometry.pcurves.push(copy); + } + _ => {} + } + } + for (id, original) in input.geometry.pcurves.iter().enumerate() { + let (mut metric, parameter_scale) = pcurve_metrics[id].unwrap_or(([1.0; 2], 1.0)); + if let PcurveGeometry::ProjectedCurve { surface, .. } = original { + metric = metrics[*surface as usize]; + } + pcurve(&mut output.geometry.pcurves[id], metric, parameter_scale)?; + } + Ok(()) +} + +fn pcurve( + geometry: &mut PcurveGeometry, + metric: [f64; 2], + parameter_scale: f64, +) -> Result<(), GeometryError> { + let map = |value: &mut [f64; 2]| { + for i in 0..2 { + value[i] *= metric[i]; + } + }; + match geometry { + PcurveGeometry::Line2 { origin, direction } => { + map(origin); + map(direction); + for value in direction { + *value /= parameter_scale; + } + } + PcurveGeometry::Conic2 { + origin, + axis_a, + axis_b, + } => { + if parameter_scale != 1.0 { + return Err(GeometryError::UnsupportedGeometry( + "scaled line edge uses a conic pcurve".into(), + )); + } + map(origin); + map(axis_a); + map(axis_b); + } + PcurveGeometry::ProjectedCurve { + uv_hint, + uv_rate, + parameter_origin, + .. + } => { + map(uv_hint); + map(uv_rate); + for value in uv_rate { + *value /= parameter_scale; + } + *parameter_origin *= parameter_scale; + } + PcurveGeometry::IntersectionSide { .. } => {} + } + Ok(()) +} + +fn place_intersections( + intersections: &mut [IntersectionDefinition], + metrics: &[[f64; 2]], + placement: Frame3, + scale: f64, +) -> Result<(), GeometryError> { + for definition in intersections { + let a = metrics[definition.surfaces[0] as usize]; + let b = metrics[definition.surfaces[1] as usize]; + for anchor in &mut definition.anchors { + anchor.point = placement.point(vector_scale(anchor.point, scale)); + for i in 0..2 { + anchor.uv_a[i] *= a[i]; + anchor.uv_b[i] *= b[i]; + } + } + for tube in &mut definition.uv_tubes { + for (i, metric) in a.into_iter().chain(b).enumerate() { + tube[i] = Interval::new(tube[i].lo * metric, tube[i].hi * metric)?; + } + } + definition.residual_tolerance *= scale; + } + Ok(()) +} + +fn check_coordinate_precision(output: &BrepEnvelope) -> Result<(), GeometryError> { + if let Some(bounds) = output.bounds()? { + let magnitude = norm(std::array::from_fn(|i| { + bounds.axes[i].lo.abs().max(bounds.axes[i].hi.abs()) + })); + if !magnitude.is_finite() || magnitude * 64.0 * f64::EPSILON > output.accuracy.geometric { + return Err(GeometryError::LimitExceeded( + "placement coordinate precision exceeds geometric tolerance".into(), + )); + } + } + Ok(()) +} diff --git a/main/opengeometry/src/brep/placement/tests.rs b/main/opengeometry/src/brep/placement/tests.rs new file mode 100644 index 0000000..2afa072 --- /dev/null +++ b/main/opengeometry/src/brep/placement/tests.rs @@ -0,0 +1,127 @@ +use super::*; +use crate::brep::geometry::{Curve, Surface}; +use crate::brep::topology::coarse_accuracy; +use crate::math::sub; +use crate::primitives; + +#[test] +fn similarity_preserves_all_families_trims_and_curve_parameterization() { + let placement = Frame3::from_axis([10.0, -3.0, 5.0], [1.0, 2.0, 3.0], [0.0, 1.0, 0.0]).unwrap(); + let bodies = [ + primitives::cylinder( + "c".into(), + Frame3::IDENTITY, + 1.0, + 2.0, + coarse_accuracy(1e-6), + ) + .unwrap(), + primitives::cone( + "cone".into(), + Frame3::IDENTITY, + 1.0, + 2.0, + coarse_accuracy(1e-6), + ) + .unwrap(), + primitives::sphere("s".into(), Frame3::IDENTITY, 1.0, coarse_accuracy(1e-6)).unwrap(), + primitives::torus( + "t".into(), + Frame3::IDENTITY, + 2.0, + 0.5, + coarse_accuracy(1e-6), + ) + .unwrap(), + primitives::cylinder_with_circular_hole( + "w".into(), + Frame3::IDENTITY, + Frame3::IDENTITY, + 0.5, + 2.0, + 2.0, + coarse_accuracy(1e-6), + ) + .unwrap(), + ]; + for input in bodies { + let json = input.to_json().unwrap(); + let output = placed(&input, placement, 3.5).unwrap(); + assert_eq!(input.to_json().unwrap(), json); + assert_eq!(output.revision, input.revision + 1); + for edge in &input.topology.edges { + if let EdgeGeometry::Curve { curve, range } = edge.geometry { + let t = range.lo / 2.0 + range.hi / 2.0; + let expected = placement.point(vector_scale( + input.geometry.curve(curve).unwrap().point_at(t).unwrap(), + 3.5, + )); + let output_t = if matches!( + input.geometry.curves[curve as usize], + CurveGeometry::Line { .. } + ) { + t * 3.5 + } else { + t + }; + assert!( + norm(sub( + output + .geometry + .curve(curve) + .unwrap() + .point_at(output_t) + .unwrap(), + expected + )) < 1e-12 + ); + } + } + for face in &input.topology.faces { + let uv = face.trim.uv_bounds.map(|d| d.lo / 2.0 + d.hi / 2.0); + let metric = metric(input.geometry.surface(face.surface).unwrap(), 3.5); + let expected = placement.point(vector_scale( + input + .geometry + .surface(face.surface) + .unwrap() + .point_at(uv) + .unwrap(), + 3.5, + )); + let actual = output + .geometry + .surface(face.surface) + .unwrap() + .point_at(std::array::from_fn(|i| uv[i] * metric[i])) + .unwrap(); + assert!(norm(sub(actual, expected)) < 1e-12); + } + BrepEnvelope::from_json(&output.to_json().unwrap()).unwrap(); + } +} +#[test] +fn invalid_scale_revision_overflow_and_precision_are_structured_errors() { + let input = + primitives::sphere("s".into(), Frame3::IDENTITY, 1.0, coarse_accuracy(1e-6)).unwrap(); + for scale in [0.0, -1.0, f64::NAN] { + assert!(placed(&input, Frame3::IDENTITY, scale).is_err()); + } + let mut bad = input.clone(); + bad.revision = u64::MAX; + assert!(matches!( + placed(&bad, Frame3::IDENTITY, 1.0), + Err(GeometryError::LimitExceeded(_)) + )); + assert!(matches!( + placed( + &input, + Frame3 { + origin: [1e12; 3], + ..Frame3::IDENTITY + }, + 1.0 + ), + Err(GeometryError::LimitExceeded(_)) + )); +} diff --git a/main/opengeometry/src/brep/shell.rs b/main/opengeometry/src/brep/shell.rs deleted file mode 100644 index d970065..0000000 --- a/main/opengeometry/src/brep/shell.rs +++ /dev/null @@ -1,18 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Serialize, Deserialize)] -pub struct Shell { - pub id: u32, - pub faces: Vec, - pub is_closed: bool, -} - -impl Shell { - pub fn new(id: u32, faces: Vec, is_closed: bool) -> Self { - Self { - id, - faces, - is_closed, - } - } -} diff --git a/main/opengeometry/src/brep/similarity.rs b/main/opengeometry/src/brep/similarity.rs new file mode 100644 index 0000000..546a552 --- /dev/null +++ b/main/opengeometry/src/brep/similarity.rs @@ -0,0 +1,152 @@ +use super::error::GeometryError; +use super::frame::{checked, unit, Frame3}; +use crate::math::{add, cross, dot, norm, scale, Point3}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Similarity3 { + pub frame: Frame3, + pub(crate) scale: f64, +} + +impl Frame3 { + fn reorthonormalized(self) -> Self { + let x = scale(self.x, 1.0 / norm(self.x)); + let z = cross(x, self.y); + let z = scale(z, 1.0 / norm(z)); + let y = cross(z, x); + let snap = |v: f64| { + if v.abs() <= 4.0 * f64::EPSILON { + 0.0 + } else if (v.abs() - 1.0).abs() <= 4.0 * f64::EPSILON { + v.signum() + } else { + v + } + }; + Self { + origin: self.origin, + x: x.map(snap), + y: y.map(snap), + z: z.map(snap), + } + } +} + +impl Default for Similarity3 { + fn default() -> Self { + Self::IDENTITY + } +} + +impl Similarity3 { + pub const IDENTITY: Self = Self { + frame: Frame3::IDENTITY, + scale: 1.0, + }; + + pub(crate) fn from_axis_angle( + origin: Point3, + axis: Point3, + angle: f64, + scale_factor: f64, + ) -> Result { + if !angle.is_finite() { + return Err(GeometryError::InvalidGeometry( + "rotation angle must be finite".into(), + )); + } + Self::from_axis_cos_sin(origin, axis, angle.cos(), angle.sin(), scale_factor) + } + + pub(crate) fn from_axis_cos_sin( + origin: Point3, + axis: Point3, + cos: f64, + sin: f64, + scale_factor: f64, + ) -> Result { + checked(origin)?; + let axis = unit(axis)?; + let rotate = |vector: Point3| { + add( + add(scale(vector, cos), scale(cross(axis, vector), sin)), + scale(axis, dot(axis, vector) * (1.0 - cos)), + ) + }; + let result = Self { + frame: Frame3 { + origin, + x: rotate([1.0, 0.0, 0.0]), + y: rotate([0.0, 1.0, 0.0]), + z: rotate([0.0, 0.0, 1.0]), + } + .reorthonormalized(), + scale: scale_factor, + }; + result.validate()?; + Ok(result) + } + + pub fn validate(&self) -> Result<(), GeometryError> { + self.frame.validate()?; + if !self.scale.is_finite() || self.scale <= 0.0 { + return Err(GeometryError::InvalidGeometry( + "similarity scale must be finite and positive".into(), + )); + } + Ok(()) + } + + pub fn apply_point(&self, point: Point3) -> Point3 { + add(self.frame.origin, self.apply_vector(point)) + } + + pub(crate) fn apply_vector(&self, vector: Point3) -> Point3 { + scale(self.frame.vector(vector), self.scale) + } + + pub fn compose(&self, child: &Self) -> Self { + Self { + frame: Frame3 { + origin: self.apply_point(child.frame.origin), + x: self.frame.vector(child.frame.x), + y: self.frame.vector(child.frame.y), + z: self.frame.vector(child.frame.z), + } + .reorthonormalized(), + scale: self.scale * child.scale, + } + } + + pub(crate) fn inverse(&self) -> Self { + let frame = &self.frame; + let inverse_scale = 1.0 / self.scale; + let rotated_origin = [ + dot(frame.origin, frame.x), + dot(frame.origin, frame.y), + dot(frame.origin, frame.z), + ]; + Self { + frame: Frame3 { + origin: scale(rotated_origin, -inverse_scale), + x: [frame.x[0], frame.y[0], frame.z[0]], + y: [frame.x[1], frame.y[1], frame.z[1]], + z: [frame.x[2], frame.y[2], frame.z[2]], + }, + scale: inverse_scale, + } + } + + pub fn to_column_major(&self) -> [f64; 16] { + let x = scale(self.frame.x, self.scale); + let y = scale(self.frame.y, self.scale); + let z = scale(self.frame.z, self.scale); + let o = self.frame.origin; + [ + x[0], x[1], x[2], 0.0, y[0], y[1], y[2], 0.0, z[0], z[1], z[2], 0.0, o[0], o[1], o[2], + 1.0, + ] + } +} diff --git a/main/opengeometry/src/brep/tests.rs b/main/opengeometry/src/brep/tests.rs new file mode 100644 index 0000000..c6aa0fd --- /dev/null +++ b/main/opengeometry/src/brep/tests.rs @@ -0,0 +1,38 @@ +use super::Similarity3; +use crate::math::Point3; + +fn near(a: Point3, b: Point3) { + for axis in 0..3 { + assert!((a[axis] - b[axis]).abs() < 1e-12, "{a:?} != {b:?}"); + } +} + +#[test] +fn axis_angle_rotates_about_the_requested_axis() { + let rotation = Similarity3::from_axis_angle( + [2.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + std::f64::consts::FRAC_PI_2, + 1.0, + ) + .unwrap(); + near(rotation.apply_point([1.0, 0.0, 0.0]), [2.0, 1.0, 0.0]); + assert_eq!(rotation.frame.x, [0.0, 1.0, 0.0]); + rotation.validate().unwrap(); +} + +#[test] +fn repeated_composition_preserves_orthonormality() { + let step = Similarity3::from_axis_angle([0.0; 3], [0.1, 0.2, 0.3], 0.007, 1.0).unwrap(); + let mut combined = Similarity3::IDENTITY; + for _ in 0..10000 { + combined = combined.compose(&step); + } + combined.validate().unwrap(); + near( + combined + .inverse() + .apply_point(combined.apply_point([1.0, 2.0, 3.0])), + [1.0, 2.0, 3.0], + ); +} diff --git a/main/opengeometry/src/brep/topology/entities.rs b/main/opengeometry/src/brep/topology/entities.rs new file mode 100644 index 0000000..30983e3 --- /dev/null +++ b/main/opengeometry/src/brep/topology/entities.rs @@ -0,0 +1,137 @@ +use super::orientation::Orientation; +use super::provenance::FaceProvenance; +use crate::brep::frame::UVBox; +use crate::math::{Interval, Point3}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "kind", deny_unknown_fields)] +pub(crate) enum EdgeGeometry { + Curve { curve: u32, range: Interval }, + Collapsed { vertex: u32 }, +} +impl EdgeGeometry { + pub(crate) fn range(&self) -> Interval { + match self { + Self::Curve { range, .. } => *range, + Self::Collapsed { .. } => Interval { lo: 0.0, hi: 1.0 }, + } + } +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Vertex { + pub(crate) id: u32, + pub position: Point3, + pub(crate) outgoing_halfedge: Option, + pub(crate) tolerance: f64, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Edge { + pub(crate) id: u32, + pub(crate) geometry: EdgeGeometry, + pub(crate) halfedge: u32, + pub(crate) twin_halfedge: Option, + pub(crate) tolerance: f64, + pub(crate) chart_seam: bool, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct HalfEdgeGeometryUse { + pub(crate) sense: Orientation, + pub(crate) pcurve: Option, + pub(crate) periodic_lift: [i32; 2], +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct HalfEdge { + pub(crate) id: u32, + pub from: u32, + pub to: u32, + pub(crate) twin: Option, + pub(crate) next: Option, + pub(crate) prev: Option, + pub(crate) edge: u32, + pub(crate) face: Option, + pub(crate) loop_ref: Option, + pub(crate) wire_ref: Option, + pub(crate) geometry_use: HalfEdgeGeometryUse, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct Loop { + pub(crate) id: u32, + pub(crate) start_halfedge: u32, + pub(crate) face_ref: u32, + pub(crate) is_hole: bool, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct TrimRegion { + pub(crate) chart: u32, + pub(crate) uv_bounds: UVBox, + pub(crate) outer: u32, + pub(crate) holes: Vec, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Face { + pub id: u32, + pub(crate) key: String, + pub surface: u32, + pub(crate) sense: Orientation, + pub(crate) trim: TrimRegion, + pub(crate) shell_ref: Option, + pub provenance: FaceProvenance, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct Wire { + pub(crate) id: u32, + pub(crate) start_halfedge: u32, + pub(crate) is_closed: bool, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct Shell { + pub(crate) id: u32, + pub(crate) faces: Vec, + pub(crate) is_closed: bool, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct SolidRegion { + pub(crate) outer_shell: u32, + pub(crate) cavity_shells: Vec, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Topology { + pub vertices: Vec, + pub edges: Vec, + pub halfedges: Vec, + pub(crate) loops: Vec, + pub faces: Vec, + pub(crate) wires: Vec, + pub(crate) shells: Vec, +} +impl Topology { + pub(super) fn new() -> Self { + Self { + vertices: Vec::new(), + edges: Vec::new(), + halfedges: Vec::new(), + loops: Vec::new(), + faces: Vec::new(), + wires: Vec::new(), + shells: Vec::new(), + } + } +} +impl Default for Topology { + fn default() -> Self { + Self::new() + } +} diff --git a/main/opengeometry/src/brep/topology/envelope.rs b/main/opengeometry/src/brep/topology/envelope.rs new file mode 100644 index 0000000..d2b4351 --- /dev/null +++ b/main/opengeometry/src/brep/topology/envelope.rs @@ -0,0 +1,113 @@ +use super::entities::{EdgeGeometry, SolidRegion, Topology}; +use crate::brep::accuracy::Accuracy; +use crate::brep::bounds::PatchBounds; +use crate::brep::error::{missing, GeometryError}; +use crate::brep::frame::unit; +use crate::brep::geometry::{Curve, GeometryStore, Surface}; +use crate::math::{Interval, Point3}; +use serde::{Deserialize, Serialize}; + +pub(crate) const SCHEMA_VERSION: u32 = 2; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "kind", deny_unknown_fields)] +pub(crate) enum GeometryQuality { + Analytic, + Approximate { max_error: f64 }, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct BrepEnvelope { + pub(crate) schema_version: u32, + pub id: String, + pub revision: u64, + pub geometry: GeometryStore, + pub topology: Topology, + pub(crate) solids: Vec, + pub accuracy: Accuracy, + pub(crate) quality: GeometryQuality, +} + +impl BrepEnvelope { + pub fn face_normal_at(&self, face_id: u32, point: Point3) -> Result { + let face = self + .topology + .faces + .get(face_id as usize) + .ok_or_else(|| missing("face", face_id))?; + let surface = self.geometry.surface(face.surface)?; + let uv = surface.project(point, None)?; + Ok(unit(surface.normal_at(uv)?)?.map(|v| v * face.sense.multiplier())) + } + + pub(crate) fn new(id: String, accuracy: Accuracy) -> Result { + accuracy.validate()?; + Ok(Self { + schema_version: SCHEMA_VERSION, + id, + revision: 0, + geometry: GeometryStore::new(), + topology: Topology::new(), + solids: Vec::new(), + accuracy, + quality: GeometryQuality::Analytic, + }) + } + + pub fn from_json(json: &str) -> Result { + if json.len() > 64 * 1024 * 1024 { + return Err(GeometryError::LimitExceeded( + "BRep JSON exceeds 64 MiB".into(), + )); + } + let brep: Self = serde_json::from_str(json) + .map_err(|e| GeometryError::InvalidGeometry(format!("invalid BRep JSON: {e}")))?; + brep.validate()?; + Ok(brep) + } + + pub fn to_json(&self) -> Result { + self.validate()?; + serde_json::to_string(self).map_err(|e| GeometryError::InvalidGeometry(e.to_string())) + } + + pub fn bounds(&self) -> Result, GeometryError> { + self.validate()?; + self.bounds_unchecked() + } + + pub(crate) fn bounds_unchecked(&self) -> Result, GeometryError> { + let mut result: Option = None; + let mut include = |bounds: PatchBounds| { + result = Some(match result { + Some(current) => PatchBounds { + axes: std::array::from_fn(|i| current.axes[i].hull(bounds.axes[i])), + }, + None => bounds, + }); + }; + for face in &self.topology.faces { + include( + self.geometry + .surface(face.surface)? + .enclose(face.trim.uv_bounds)?, + ); + } + for edge in &self.topology.edges { + if let EdgeGeometry::Curve { curve, range } = edge.geometry { + include(self.geometry.curve(curve)?.enclose(range)?); + } + } + for vertex in &self.topology.vertices { + include(PatchBounds { + axes: [ + Interval::point(vertex.position[0])?, + Interval::point(vertex.position[1])?, + Interval::point(vertex.position[2])?, + ], + }); + } + Ok(result) + } +} diff --git a/main/opengeometry/src/brep/topology/fixtures.rs b/main/opengeometry/src/brep/topology/fixtures.rs new file mode 100644 index 0000000..6100db2 --- /dev/null +++ b/main/opengeometry/src/brep/topology/fixtures.rs @@ -0,0 +1,109 @@ +use super::entities::{ + Edge, EdgeGeometry, Face, HalfEdge, HalfEdgeGeometryUse, Loop, TrimRegion, Vertex, +}; +use super::envelope::BrepEnvelope; +use super::orientation::Orientation; +use super::provenance::{FaceProvenance, FaceRole, FaceSource}; +use crate::brep::accuracy::Accuracy; +use crate::brep::frame::Frame3; +use crate::brep::geometry::{CurveGeometry, SurfaceGeometry}; +use crate::brep::pcurve::PcurveGeometry; +use crate::math::Interval; + +pub(crate) fn circular_face() -> BrepEnvelope { + let mut b = BrepEnvelope::new("circle".into(), fine_accuracy()).unwrap(); + b.geometry.surfaces.push(SurfaceGeometry::Plane { + frame: Frame3::IDENTITY, + }); + b.geometry.curves.push(CurveGeometry::Circle { + frame: Frame3::IDENTITY, + radius: 1.0, + }); + b.geometry.pcurves.push(PcurveGeometry::Conic2 { + origin: [0.0; 2], + axis_a: [1.0, 0.0], + axis_b: [0.0, 1.0], + }); + b.topology.vertices.push(Vertex { + id: 0, + position: [1.0, 0.0, 0.0], + outgoing_halfedge: Some(0), + tolerance: 1e-9, + }); + b.topology.edges.push(Edge { + id: 0, + geometry: EdgeGeometry::Curve { + curve: 0, + range: Interval::new(0.0, std::f64::consts::TAU).unwrap(), + }, + halfedge: 0, + twin_halfedge: None, + tolerance: 1e-9, + chart_seam: false, + }); + b.topology.halfedges.push(HalfEdge { + id: 0, + from: 0, + to: 0, + twin: None, + next: Some(0), + prev: Some(0), + edge: 0, + face: Some(0), + loop_ref: Some(0), + wire_ref: None, + geometry_use: HalfEdgeGeometryUse { + sense: Orientation::Forward, + pcurve: Some(0), + periodic_lift: [0; 2], + }, + }); + b.topology.loops.push(Loop { + id: 0, + start_halfedge: 0, + face_ref: 0, + is_hole: false, + }); + b.topology.faces.push(Face { + id: 0, + key: "disc".into(), + surface: 0, + sense: Orientation::Forward, + trim: TrimRegion { + chart: 0, + uv_bounds: [Interval::new(-1.0, 1.0).unwrap(); 2], + outer: 0, + holes: Vec::new(), + }, + shell_ref: None, + provenance: FaceProvenance { + sources: vec![FaceSource { + entity: "circle".into(), + body: "circle".into(), + key: "disc".into(), + face: 0, + }], + role: FaceRole::Authored, + reversed: false, + }, + }); + b +} + +pub(crate) fn fine_accuracy() -> Accuracy { + Accuracy { + geometric: 1e-9, + intersection: 1e-10, + tessellation: 1e-3, + exchange: 1e-5, + } +} + +pub(crate) fn coarse_accuracy(exchange: f64) -> Accuracy { + Accuracy { + geometric: 1e-8, + intersection: 1e-9, + tessellation: 0.01, + exchange, + } +} diff --git a/main/opengeometry/src/brep/topology/mod.rs b/main/opengeometry/src/brep/topology/mod.rs new file mode 100644 index 0000000..b881840 --- /dev/null +++ b/main/opengeometry/src/brep/topology/mod.rs @@ -0,0 +1,20 @@ +mod entities; +mod envelope; +#[cfg(test)] +mod fixtures; +mod orientation; +mod provenance; +#[cfg(test)] +mod tests; + +pub use entities::{Edge, Face, HalfEdge, Topology, Vertex}; +pub(crate) use entities::{ + EdgeGeometry, HalfEdgeGeometryUse, Loop, Shell, SolidRegion, TrimRegion, Wire, +}; +pub use envelope::BrepEnvelope; +pub(crate) use envelope::GeometryQuality; +pub(super) use envelope::SCHEMA_VERSION; +#[cfg(test)] +pub(crate) use fixtures::{circular_face, coarse_accuracy, fine_accuracy}; +pub(crate) use orientation::Orientation; +pub use provenance::{FaceProvenance, FaceRole, FaceSource}; diff --git a/main/opengeometry/src/brep/topology/orientation.rs b/main/opengeometry/src/brep/topology/orientation.rs new file mode 100644 index 0000000..a03c866 --- /dev/null +++ b/main/opengeometry/src/brep/topology/orientation.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub(crate) enum Orientation { + Forward, + Reverse, +} +impl Orientation { + pub(crate) fn multiplier(self) -> f64 { + if self == Self::Forward { + 1.0 + } else { + -1.0 + } + } +} diff --git a/main/opengeometry/src/brep/topology/provenance.rs b/main/opengeometry/src/brep/topology/provenance.rs new file mode 100644 index 0000000..2c1e241 --- /dev/null +++ b/main/opengeometry/src/brep/topology/provenance.rs @@ -0,0 +1,25 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct FaceSource { + pub entity: String, + pub body: String, + pub(crate) key: String, + pub(crate) face: u32, +} +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum FaceRole { + Authored, + Preserved, + Split, + Cut, + Coincident, +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct FaceProvenance { + pub sources: Vec, + pub role: FaceRole, + pub(crate) reversed: bool, +} diff --git a/main/opengeometry/src/brep/topology/tests.rs b/main/opengeometry/src/brep/topology/tests.rs new file mode 100644 index 0000000..3bf441a --- /dev/null +++ b/main/opengeometry/src/brep/topology/tests.rs @@ -0,0 +1,95 @@ +use super::entities::EdgeGeometry; +use super::envelope::BrepEnvelope; +use super::fixtures::circular_face; +use crate::brep::error::GeometryError; +use crate::brep::geometry::CurveGeometry; +use crate::brep::pcurve::PcurveGeometry; +use crate::math::Interval; + +#[test] +fn full_circle_loop_is_valid_and_strictly_versioned() { + let b = circular_face(); + b.validate().unwrap(); + let json = b.to_json().unwrap(); + BrepEnvelope::from_json(&json).unwrap(); + let mut bad: serde_json::Value = serde_json::from_str(&json).unwrap(); + bad["schema_version"] = serde_json::json!(1); + assert!(matches!( + BrepEnvelope::from_json(&bad.to_string()), + Err(GeometryError::UnsupportedSchema { found: 1 }) + )); + bad.as_object_mut().unwrap().remove("schema_version"); + assert!(BrepEnvelope::from_json(&bad.to_string()).is_err()); +} +#[test] +fn missing_pcurve_and_malformed_cycle_are_rejected() { + let mut b = circular_face(); + b.topology.halfedges[0].geometry_use.pcurve = None; + assert!(b.validate().is_err()); + let mut b = circular_face(); + b.topology.halfedges[0].next = Some(10); + assert!(b.validate().is_err()); + let mut b = circular_face(); + b.geometry.pcurves[0] = PcurveGeometry::Line2 { + origin: [0.0; 2], + direction: [1.0, 0.0], + }; + assert!(b.validate().is_err()); +} +#[test] +fn pcurve_outside_uv_bounds_beyond_geometric_tolerance_is_rejected() { + let mut b = circular_face(); + b.topology.faces[0].trim.uv_bounds[0] = Interval::new(-0.999, 0.999).unwrap(); + assert!(matches!( + b.validate(), + Err(GeometryError::InvalidTopology(message)) if message.contains("leaves face 0 UV bounds") + )); +} +#[test] +fn old_json_and_unknown_fields_are_not_accepted() { + assert!(BrepEnvelope::from_json("{\"vertices\":[],\"faces\":[]}").is_err()); + let mut json: serde_json::Value = + serde_json::from_str(&circular_face().to_json().unwrap()).unwrap(); + json["legacy"] = serde_json::json!(true); + assert!(BrepEnvelope::from_json(&json.to_string()).is_err()); +} +#[test] +fn malformed_charts_and_undeclared_degenerate_edges_are_rejected() { + let mut b = circular_face(); + b.topology.faces[0].trim.chart = u32::MAX; + assert!( + matches!(b.validate(), Err(GeometryError::MissingReference { kind, .. }) if kind == "chart") + ); + let mut b = circular_face(); + b.geometry.curves[0] = CurveGeometry::Line { + origin: [1.0, 0.0, 0.0], + direction: [1.0, 0.0, 0.0], + }; + b.topology.edges[0].geometry = EdgeGeometry::Curve { + curve: 0, + range: Interval::new(0.0, 1e-12).unwrap(), + }; + b.geometry.pcurves[0] = PcurveGeometry::Line2 { + origin: [1.0, 0.0], + direction: [1.0, 0.0], + }; + assert!( + matches!(b.validate(), Err(GeometryError::InvalidTopology(message)) if message.contains("regular line")) + ); + let mut b = circular_face(); + b.accuracy.geometric = 10.0; + b.topology.edges[0].geometry = EdgeGeometry::Collapsed { vertex: 0 }; + b.geometry.pcurves[0] = PcurveGeometry::Line2 { + origin: [1.0, 0.0], + direction: [0.0; 2], + }; + assert!( + matches!(b.validate(), Err(GeometryError::InvalidTopology(message)) if message.contains("surface singularity")) + ); + let mut b = circular_face(); + b.topology.vertices[0].tolerance = 1e-18; + b.topology.edges[0].tolerance = 1e-18; + assert!( + matches!(b.validate(), Err(GeometryError::InvalidTopology(message)) if message.contains("edge endpoints")) + ); +} diff --git a/main/opengeometry/src/brep/validate/edges.rs b/main/opengeometry/src/brep/validate/edges.rs new file mode 100644 index 0000000..276449b --- /dev/null +++ b/main/opengeometry/src/brep/validate/edges.rs @@ -0,0 +1,208 @@ +use crate::brep::error::{invalid, missing, positive, GeometryError}; +use crate::brep::geometry::{Curve, CurveGeometry}; +use crate::brep::topology::{BrepEnvelope, Edge, EdgeGeometry, HalfEdge, Orientation}; +use crate::math::{norm, sub, Interval, Point3}; + +impl BrepEnvelope { + pub(super) fn incident_halfedges(&self) -> Result>, GeometryError> { + let t = &self.topology; + let mut incident = vec![Vec::new(); t.edges.len()]; + for (i, h) in t.halfedges.iter().enumerate() { + if h.id as usize != i { + return Err(invalid("halfedge IDs must be dense")); + } + t.vertices + .get(h.from as usize) + .ok_or_else(|| missing("vertex", h.from))?; + t.vertices + .get(h.to as usize) + .ok_or_else(|| missing("vertex", h.to))?; + t.edges + .get(h.edge as usize) + .ok_or_else(|| missing("edge", h.edge))?; + incident[h.edge as usize].push(h.id); + self.check_halfedge_links(h)?; + self.check_halfedge_owner(h)?; + } + Ok(incident) + } + + fn check_halfedge_links(&self, h: &HalfEdge) -> Result<(), GeometryError> { + let t = &self.topology; + if let Some(twin) = h.twin { + let twin = t + .halfedges + .get(twin as usize) + .ok_or_else(|| missing("halfedge", twin))?; + if twin.id == h.id + || twin.twin != Some(h.id) + || twin.edge != h.edge + || twin.from != h.to + || twin.to != h.from + || twin.geometry_use.sense == h.geometry_use.sense + { + return Err(invalid("inconsistent halfedge twins")); + } + } + if let Some(next) = h.next { + let n = t + .halfedges + .get(next as usize) + .ok_or_else(|| missing("halfedge", next))?; + if n.prev != Some(h.id) + || n.from != h.to + || n.face != h.face + || n.wire_ref != h.wire_ref + || n.loop_ref != h.loop_ref + { + return Err(invalid("inconsistent next link")); + } + } + if let Some(prev) = h.prev { + if t.halfedges + .get(prev as usize) + .ok_or_else(|| missing("halfedge", prev))? + .next + != Some(h.id) + { + return Err(invalid("inconsistent previous link")); + } + } + Ok(()) + } + + fn check_halfedge_owner(&self, h: &HalfEdge) -> Result<(), GeometryError> { + let t = &self.topology; + match (h.face, h.wire_ref) { + (Some(face), None) => { + t.faces + .get(face as usize) + .ok_or_else(|| missing("face", face))?; + let p = h + .geometry_use + .pcurve + .ok_or_else(|| invalid("face halfedge has no pcurve"))?; + self.geometry + .pcurves + .get(p as usize) + .ok_or_else(|| missing("pcurve", p))?; + if h.loop_ref.is_none() || h.next.is_none() || h.prev.is_none() { + return Err(invalid("face halfedge has incomplete loop links")); + } + } + (None, Some(wire)) => { + t.wires + .get(wire as usize) + .ok_or_else(|| missing("wire", wire))?; + if h.geometry_use.pcurve.is_some() || h.loop_ref.is_some() { + return Err(invalid("wire use carries face geometry")); + } + } + _ => return Err(invalid("halfedge must belong to one face or wire")), + } + Ok(()) + } + + pub(super) fn check_edges(&self, incident: &[Vec]) -> Result<(), GeometryError> { + let t = &self.topology; + for (i, e) in t.edges.iter().enumerate() { + if e.id as usize != i { + return Err(invalid("edge IDs must be dense")); + } + positive(e.tolerance)?; + if e.tolerance > self.accuracy.geometric { + return Err(invalid("edge tolerance exceeds the model accuracy budget")); + } + let list = &incident[i]; + if list.is_empty() || list.len() > 2 || !list.contains(&e.halfedge) { + return Err(invalid("invalid edge incidence")); + } + if list.len() == 2 { + let twin = e + .twin_halfedge + .ok_or_else(|| invalid("two-use edge lacks twin halfedge"))?; + if !list.contains(&twin) + || twin == e.halfedge + || t.halfedges[e.halfedge as usize].twin != Some(twin) + { + return Err(invalid("edge incidence does not match twins")); + } + } else if e.twin_halfedge.is_some() || t.halfedges[e.halfedge as usize].twin.is_some() { + return Err(invalid("one-use edge has an extra twin")); + } + let range = e.geometry.range(); + Interval::new(range.lo, range.hi)?; + if range.lo == range.hi { + return Err(invalid("regular curve range must be nonzero")); + } + let points = self.edge_endpoints(e, range)?; + for &id in list { + let h = &t.halfedges[id as usize]; + self.check_edge_use(e, h, points)?; + } + } + Ok(()) + } + + fn edge_endpoints(&self, e: &Edge, range: Interval) -> Result<[Point3; 2], GeometryError> { + let t = &self.topology; + let points = match e.geometry { + EdgeGeometry::Curve { curve, .. } => [ + self.geometry.curve(curve)?.point_at(range.lo)?, + self.geometry.curve(curve)?.point_at(range.hi)?, + ], + EdgeGeometry::Collapsed { vertex } => { + let p = t + .vertices + .get(vertex as usize) + .ok_or_else(|| missing("vertex", vertex))? + .position; + [p, p] + } + }; + Ok(points) + } + + fn check_edge_use( + &self, + e: &Edge, + h: &HalfEdge, + points: [Point3; 2], + ) -> Result<(), GeometryError> { + let t = &self.topology; + let endpoints = if h.geometry_use.sense == Orientation::Forward { + points + } else { + [points[1], points[0]] + }; + let tolerance = e + .tolerance + .max(t.vertices[h.from as usize].tolerance) + .max(t.vertices[h.to as usize].tolerance); + if norm(sub(endpoints[0], t.vertices[h.from as usize].position)) > tolerance + || norm(sub(endpoints[1], t.vertices[h.to as usize].position)) > tolerance + { + return Err(invalid("edge endpoints disagree with vertices")); + } + if h.from == h.to + && matches!(e.geometry, EdgeGeometry::Curve { .. }) + && norm(sub(points[0], points[1])) > tolerance + { + return Err(invalid("self-loop curve is not closed")); + } + if h.from == h.to + && matches!(e.geometry, EdgeGeometry::Curve { curve, .. } if matches!(self.geometry.curves[curve as usize], CurveGeometry::Line { .. })) + { + return Err(invalid("a regular line cannot form a closed edge")); + } + if matches!(e.geometry, EdgeGeometry::Collapsed { .. }) + && (h.from != h.to || h.face.is_none()) + { + return Err(invalid("collapsed edge must be a singular face use")); + } + if let Some(face) = h.face { + self.validate_face_use(h, &t.faces[face as usize], e, tolerance)?; + } + Ok(()) + } +} diff --git a/main/opengeometry/src/brep/validate/face_use.rs b/main/opengeometry/src/brep/validate/face_use.rs new file mode 100644 index 0000000..f36b45f --- /dev/null +++ b/main/opengeometry/src/brep/validate/face_use.rs @@ -0,0 +1,184 @@ +use crate::brep::error::{invalid, GeometryError}; +use crate::brep::frame::UV; +use crate::brep::geometry::{Curve, CurveGeometry, Surface, SurfaceGeometry}; +use crate::brep::pcurve::{IntersectionSide, PcurveGeometry}; +use crate::brep::topology::{BrepEnvelope, Edge, EdgeGeometry, Face, HalfEdge, Orientation}; +use crate::math::{add, norm, scale, sub, Point3}; + +impl BrepEnvelope { + pub(super) fn validate_face_use( + &self, + h: &HalfEdge, + face: &Face, + edge: &Edge, + tolerance: f64, + ) -> Result<(), GeometryError> { + let surface = self.geometry.surface(face.surface)?; + let pcurve = h + .geometry_use + .pcurve + .ok_or_else(|| invalid("missing face pcurve"))?; + self.check_pcurve_reference(pcurve, face, edge)?; + let range = edge.geometry.range(); + for i in 0..=8 { + let t = (range.lo + (range.hi - range.lo) * i as f64 / 8.0).clamp(range.lo, range.hi); + let mut uv = self.geometry.pcurve_at(pcurve, t)?; + let chart = &surface.charts()[face.trim.chart as usize]; + for j in 0..2 { + if let Some(period) = chart.periods[j] { + uv[j] += h.geometry_use.periodic_lift[j] as f64 * period; + } else if h.geometry_use.periodic_lift[j] != 0 { + return Err(invalid("lift on nonperiodic chart axis")); + } + } + let p = surface.point_at(uv)?; + let expected = match edge.geometry { + EdgeGeometry::Curve { curve, .. } => self.geometry.curve(curve)?.point_at(t)?, + EdgeGeometry::Collapsed { vertex } => { + self.topology.vertices[vertex as usize].position + } + }; + if norm(sub(p, expected)) > tolerance { + return Err(invalid("pcurve does not lie on edge support")); + } + check_uv_bounds(h, face, surface, uv, p, tolerance)?; + if matches!(edge.geometry, EdgeGeometry::Collapsed { .. }) { + let singular = match surface { + SurfaceGeometry::Sphere { .. } => uv[1].abs() == std::f64::consts::FRAC_PI_2, + SurfaceGeometry::Cone { .. } => uv[1] == 0.0, + _ => false, + }; + if !singular { + return Err(invalid("collapsed edge is not at a surface singularity")); + } + } + } + Ok(()) + } + + fn check_pcurve_reference( + &self, + pcurve: u32, + face: &Face, + edge: &Edge, + ) -> Result<(), GeometryError> { + match &self.geometry.pcurves[pcurve as usize] { + PcurveGeometry::ProjectedCurve { + curve, + surface: s, + chart, + .. + } => { + if *s != face.surface + || *chart != face.trim.chart + || !matches!(edge.geometry,EdgeGeometry::Curve{curve:c,..}if c==*curve) + { + return Err(invalid("projected pcurve references another edge or face")); + } + } + PcurveGeometry::IntersectionSide { definition, side } => { + let d = &self.geometry.intersections[*definition as usize]; + let s = d.surfaces[if *side == IntersectionSide::A { 0 } else { 1 }]; + if s != face.surface + || !matches!(edge.geometry,EdgeGeometry::Curve{curve,..}if matches!(self.geometry.curves[curve as usize],CurveGeometry::Intersection{definition:df}if df==*definition)) + { + return Err(invalid( + "intersection pcurve references another face or curve", + )); + } + } + _ => {} + } + Ok(()) + } + + pub(super) fn validate_uv_join( + &self, + a: &HalfEdge, + b: &HalfEdge, + face: &Face, + ) -> Result<(), GeometryError> { + let range_a = self.topology.edges[a.edge as usize].geometry.range(); + let range_b = self.topology.edges[b.edge as usize].geometry.range(); + let ta = if a.geometry_use.sense == Orientation::Forward { + range_a.hi + } else { + range_a.lo + }; + let tb = if b.geometry_use.sense == Orientation::Forward { + range_b.lo + } else { + range_b.hi + }; + let ua = self.geometry.pcurve_at( + a.geometry_use + .pcurve + .ok_or_else(|| invalid("missing pcurve"))?, + ta, + )?; + let ub = self.geometry.pcurve_at( + b.geometry_use + .pcurve + .ok_or_else(|| invalid("missing pcurve"))?, + tb, + )?; + let surface = self.geometry.surface(face.surface)?; + let chart = &surface.charts()[face.trim.chart as usize]; + let jet = surface.derivatives(ua)?; + let mut displacement = [0.0; 2]; + for j in 0..2 { + displacement[j] = ua[j] - ub[j]; + if let Some(period) = chart.periods[j] { + displacement[j] -= (displacement[j] / period).round() * period; + } + } + let error = norm(add( + scale(jet.du, displacement[0]), + scale(jet.dv, displacement[1]), + )); + if error > self.accuracy.geometric { + return Err(invalid("pcurve loop is discontinuous")); + } + Ok(()) + } +} + +fn check_uv_bounds( + h: &HalfEdge, + face: &Face, + surface: &SurfaceGeometry, + uv: UV, + p: Point3, + tolerance: f64, +) -> Result<(), GeometryError> { + if !face.trim.uv_bounds[0].contains(uv[0]) || !face.trim.uv_bounds[1].contains(uv[1]) { + let clipped = std::array::from_fn(|axis| { + uv[axis].clamp(face.trim.uv_bounds[axis].lo, face.trim.uv_bounds[axis].hi) + }); + let jet = surface.derivatives(clipped)?; + for axis in 0..2 { + let excess = (uv[axis] - clipped[axis]).abs(); + let metric = norm(if axis == 0 { jet.du } else { jet.dv }); + let roundoff = 128.0 * f64::EPSILON * uv[axis].abs().max(clipped[axis].abs()).max(1.0); + let allowed = roundoff + + if metric > 0.0 { + tolerance / metric + } else { + 0.0 + }; + if excess > allowed { + return Err(invalid(format!( + "pcurve on halfedge {} leaves face {} UV bounds at [{}, {}]", + h.id, face.id, uv[0], uv[1] + ))); + } + } + if norm(sub(surface.point_at(clipped)?, p)) > tolerance { + return Err(invalid(format!( + "pcurve on halfedge {} leaves face {} UV bounds at [{}, {}]", + h.id, face.id, uv[0], uv[1] + ))); + } + } + Ok(()) +} diff --git a/main/opengeometry/src/brep/validate/mod.rs b/main/opengeometry/src/brep/validate/mod.rs new file mode 100644 index 0000000..1f95ff9 --- /dev/null +++ b/main/opengeometry/src/brep/validate/mod.rs @@ -0,0 +1,394 @@ +mod edges; +mod face_use; + +use super::error::{invalid, missing, positive, GeometryError}; +use super::geometry::{CurveGeometry, Surface}; +use super::pcurve::PcurveGeometry; +use super::topology::{BrepEnvelope, EdgeGeometry, GeometryQuality, SCHEMA_VERSION}; +use crate::math::{finite, Interval}; +use std::collections::HashSet; + +impl BrepEnvelope { + pub fn validate(&self) -> Result<(), GeometryError> { + self.check_header()?; + self.check_geometry()?; + self.check_pcurves()?; + let t = &self.topology; + self.check_table_sizes()?; + self.check_face_domains()?; + self.check_vertices()?; + let incident = self.incident_halfedges()?; + self.check_edges(&incident)?; + let loop_uses = self.loop_uses()?; + self.check_faces()?; + if t.halfedges + .iter() + .any(|h| h.face.is_some() && !loop_uses.contains(&h.id)) + { + return Err(invalid("orphan face halfedge")); + } + let wire_uses = self.wire_uses()?; + if t.halfedges + .iter() + .any(|h| h.wire_ref.is_some() && !wire_uses.contains(&h.id)) + { + return Err(invalid("orphan wire halfedge")); + } + self.check_shells()?; + self.check_solids() + } + + fn check_header(&self) -> Result<(), GeometryError> { + if self.schema_version != SCHEMA_VERSION { + return Err(GeometryError::UnsupportedSchema { + found: self.schema_version, + }); + } + if self.id.is_empty() { + return Err(invalid("BRep identity is empty")); + } + self.accuracy.validate()?; + if let GeometryQuality::Approximate { max_error } = self.quality { + positive(max_error)?; + } + Ok(()) + } + + fn check_geometry(&self) -> Result<(), GeometryError> { + for s in &self.geometry.surfaces { + s.validate()?; + } + for c in &self.geometry.curves { + c.validate()?; + if let CurveGeometry::Intersection { definition } = c { + self.geometry.intersection(*definition)?; + } + } + for definition in &self.geometry.intersections { + if definition.residual_tolerance > self.accuracy.intersection { + return Err(invalid( + "intersection residual budget exceeds model accuracy", + )); + } + definition.validate(&self.geometry)?; + } + Ok(()) + } + + fn check_pcurves(&self) -> Result<(), GeometryError> { + for pcurve in &self.geometry.pcurves { + match pcurve { + PcurveGeometry::Line2 { origin, direction } => { + for v in origin.iter().chain(direction) { + finite(*v)?; + } + } + PcurveGeometry::Conic2 { + origin, + axis_a, + axis_b, + } => { + for v in origin.iter().chain(axis_a).chain(axis_b) { + finite(*v)?; + } + } + PcurveGeometry::ProjectedCurve { + curve, + surface, + chart, + uv_hint, + uv_rate, + parameter_origin, + } => { + self.geometry.curve(*curve)?; + let surface = self.geometry.surface(*surface)?; + if *chart as usize >= surface.charts().len() { + return Err(missing("chart", *chart)); + } + for v in uv_hint + .iter() + .chain(uv_rate) + .chain(std::iter::once(parameter_origin)) + { + finite(*v)?; + } + } + PcurveGeometry::IntersectionSide { definition, .. } => { + self.geometry.intersection(*definition)?; + } + } + } + Ok(()) + } + + fn check_table_sizes(&self) -> Result<(), GeometryError> { + let t = &self.topology; + for length in [ + t.vertices.len(), + t.edges.len(), + t.halfedges.len(), + t.loops.len(), + t.faces.len(), + t.wires.len(), + t.shells.len(), + self.geometry.surfaces.len(), + self.geometry.curves.len(), + self.geometry.pcurves.len(), + self.geometry.intersections.len(), + ] { + if length > 2_000_000 { + return Err(GeometryError::LimitExceeded( + "BRep table exceeds 2000000 records".into(), + )); + } + } + Ok(()) + } + + fn check_face_domains(&self) -> Result<(), GeometryError> { + let t = &self.topology; + for face in &t.faces { + let surface = self.geometry.surface(face.surface)?; + if face.trim.chart as usize >= surface.charts().len() { + return Err(missing("chart", face.trim.chart)); + } + for d in face.trim.uv_bounds { + Interval::new(d.lo, d.hi)?; + if d.lo == d.hi { + return Err(invalid("face UV bounds must have positive area")); + } + } + } + Ok(()) + } + + fn check_vertices(&self) -> Result<(), GeometryError> { + let t = &self.topology; + for (i, v) in t.vertices.iter().enumerate() { + if v.id as usize != i { + return Err(invalid("vertex IDs must be dense")); + } + positive(v.tolerance)?; + if v.tolerance > self.accuracy.geometric { + return Err(invalid( + "vertex tolerance exceeds the model accuracy budget", + )); + } + for x in v.position { + finite(x)?; + } + if let Some(h) = v.outgoing_halfedge { + if t.halfedges + .get(h as usize) + .ok_or_else(|| missing("halfedge", h))? + .from + != v.id + { + return Err(invalid("outgoing halfedge starts at another vertex")); + } + } + } + Ok(()) + } + + fn loop_uses(&self) -> Result, GeometryError> { + let t = &self.topology; + let mut loop_uses = HashSet::new(); + for (i, l) in t.loops.iter().enumerate() { + if l.id as usize != i { + return Err(invalid("loop IDs must be dense")); + } + let face = t + .faces + .get(l.face_ref as usize) + .ok_or_else(|| missing("face", l.face_ref))?; + if (face.trim.outer == l.id && l.is_hole) + || (!face.trim.holes.contains(&l.id) && face.trim.outer != l.id) + || face.trim.holes.contains(&l.id) && !l.is_hole + { + return Err(invalid("loop face membership or hole flag is inconsistent")); + } + let mut current = l.start_halfedge; + let mut seen = HashSet::new(); + loop { + let h = t + .halfedges + .get(current as usize) + .ok_or_else(|| missing("halfedge", current))?; + if h.loop_ref != Some(l.id) + || h.face != Some(face.id) + || !seen.insert(current) + || !loop_uses.insert(current) + { + return Err(invalid("loop has inconsistent or repeated uses")); + } + let next = h.next.ok_or_else(|| invalid("open face loop"))?; + self.validate_uv_join(h, &t.halfedges[next as usize], face)?; + if next == l.start_halfedge { + break; + } + current = next; + if seen.len() > t.halfedges.len() { + return Err(invalid("nonterminating loop")); + } + } + } + Ok(loop_uses) + } + + fn check_faces(&self) -> Result<(), GeometryError> { + let t = &self.topology; + let mut face_keys = HashSet::new(); + for (i, f) in t.faces.iter().enumerate() { + if f.id as usize != i || f.key.is_empty() || !face_keys.insert(&f.key) { + return Err(invalid("face IDs or keys are invalid")); + } + let surface = self.geometry.surface(f.surface)?; + if f.trim.chart as usize >= surface.charts().len() { + return Err(missing("chart", f.trim.chart)); + } + for d in f.trim.uv_bounds { + Interval::new(d.lo, d.hi)?; + } + t.loops + .get(f.trim.outer as usize) + .filter(|l| l.face_ref == f.id && !l.is_hole) + .ok_or_else(|| invalid("face outer loop is invalid"))?; + let mut holes = HashSet::new(); + for &hole in &f.trim.holes { + if !holes.insert(hole) || hole == f.trim.outer { + return Err(invalid("duplicate face hole")); + } + t.loops + .get(hole as usize) + .filter(|l| l.face_ref == f.id && l.is_hole) + .ok_or_else(|| invalid("face hole is invalid"))?; + } + if f.provenance.sources.is_empty() + || f.provenance + .sources + .iter() + .any(|s| s.key.is_empty() || s.entity.is_empty() || s.body.is_empty()) + { + return Err(invalid("face provenance is incomplete")); + } + if let Some(shell) = f.shell_ref { + if !t + .shells + .get(shell as usize) + .ok_or_else(|| missing("shell", shell))? + .faces + .contains(&f.id) + { + return Err(invalid("face shell membership is inconsistent")); + } + } + } + Ok(()) + } + + fn wire_uses(&self) -> Result, GeometryError> { + let t = &self.topology; + let mut wire_uses = HashSet::new(); + for (i, w) in t.wires.iter().enumerate() { + if w.id as usize != i { + return Err(invalid("wire IDs must be dense")); + } + let mut current = w.start_halfedge; + let mut seen = HashSet::new(); + loop { + let h = t + .halfedges + .get(current as usize) + .ok_or_else(|| missing("halfedge", current))?; + if h.wire_ref != Some(w.id) || !seen.insert(current) || !wire_uses.insert(current) { + return Err(invalid("invalid wire membership")); + } + match h.next { + Some(next) if next == w.start_halfedge => { + if !w.is_closed { + return Err(invalid("open wire forms a cycle")); + } + break; + } + Some(next) => current = next, + None => { + if w.is_closed { + return Err(invalid("closed wire is open")); + } + break; + } + } + } + } + Ok(wire_uses) + } + + fn check_shells(&self) -> Result<(), GeometryError> { + let t = &self.topology; + for (i, s) in t.shells.iter().enumerate() { + if s.id as usize != i || s.faces.is_empty() { + return Err(invalid("invalid shell IDs or empty shell")); + } + let mut seen = HashSet::new(); + for &face in &s.faces { + let f = t + .faces + .get(face as usize) + .ok_or_else(|| missing("face", face))?; + if f.shell_ref != Some(s.id) || !seen.insert(face) { + return Err(invalid("inconsistent shell membership")); + } + } + if s.is_closed { + for h in t + .halfedges + .iter() + .filter(|h| h.face.is_some_and(|f| seen.contains(&f))) + { + if matches!( + t.edges[h.edge as usize].geometry, + EdgeGeometry::Collapsed { .. } + ) { + continue; + } + let twin = h + .twin + .ok_or_else(|| invalid("closed shell has a free edge"))?; + if !t.halfedges[twin as usize] + .face + .is_some_and(|f| seen.contains(&f)) + { + return Err(invalid("shell twin belongs to another shell")); + } + } + } + } + Ok(()) + } + + fn check_solids(&self) -> Result<(), GeometryError> { + let t = &self.topology; + let mut shells = HashSet::new(); + for solid in &self.solids { + for shell in std::iter::once(&solid.outer_shell).chain(&solid.cavity_shells) { + if !shells.insert(*shell) + || !t + .shells + .get(*shell as usize) + .ok_or_else(|| missing("shell", *shell))? + .is_closed + { + return Err(invalid("solid shell is open or multiply assigned")); + } + } + } + if t.shells + .iter() + .any(|s| s.is_closed && !shells.contains(&s.id)) + { + return Err(invalid("closed shell has no solid membership")); + } + Ok(()) + } +} diff --git a/main/opengeometry/src/brep/validity.rs b/main/opengeometry/src/brep/validity.rs deleted file mode 100644 index ce9b83f..0000000 --- a/main/opengeometry/src/brep/validity.rs +++ /dev/null @@ -1,366 +0,0 @@ -//! B-rep validity checking and basic healing (debt item D5). -//! -//! `BrepBuilder` validates *topology* at construction, but a kernel also needs -//! a *geometric* validity check — is the shell closed, are edges manifold, are -//! face orientations consistent, is any geometry non-finite or degenerate — to -//! gate bad input and to detect when an operation produced garbage. And it -//! needs basic *healing* to recover near-valid geometry. The checker and the -//! healer are deliberately separate (PRD R4): healing must never quietly mask a -//! real defect, so the validity signal stays honest for upstream gating. - -use std::collections::HashMap; - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; - -use super::Brep; - -/// Wasm entry point: check a serialized B-rep's geometric validity, returning -/// the [`ValidityReport`] as JSON. -#[wasm_bindgen(js_name = checkBrepValidity)] -pub fn check_brep_validity_wasm(brep_json: String) -> Result { - let brep: Brep = serde_json::from_str(&brep_json) - .map_err(|e| JsValue::from_str(&format!("Invalid BRep JSON: {}", e)))?; - let report = check_validity(&brep); - serde_json::to_string(&report) - .map_err(|e| JsValue::from_str(&format!("Failed to serialize report: {}", e))) -} - -/// Wasm entry point: heal a serialized B-rep, returning JSON -/// `{ "brep": , "report": }`. -#[wasm_bindgen(js_name = healBrep)] -pub fn heal_brep_wasm(brep_json: String, tolerance: f64) -> Result { - let brep: Brep = serde_json::from_str(&brep_json) - .map_err(|e| JsValue::from_str(&format!("Invalid BRep JSON: {}", e)))?; - let (healed, report) = heal(&brep, tolerance); - serde_json::to_string(&serde_json::json!({ "brep": healed, "report": report })) - .map_err(|e| JsValue::from_str(&format!("Failed to serialize healed brep: {}", e))) -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct ValidityReport { - /// Construction-time topology invariants hold (ids, links, manifold edges). - pub topology_ok: bool, - /// Every edge is shared by exactly two half-edges (a closed 2-manifold). - pub closed_shell: bool, - /// No edge is used by more than two half-edges. - pub manifold_edges: bool, - /// Each shared edge is traversed in opposite directions by its two - /// half-edges (consistent face orientation). - pub consistent_orientation: bool, - /// All vertex coordinates are finite. - pub finite_geometry: bool, - /// No face collapses to ~zero area. - pub no_degenerate_faces: bool, - pub issues: Vec, -} - -impl ValidityReport { - /// Whether the B-rep is a valid solid by every checked criterion. - pub fn is_valid(&self) -> bool { - self.topology_ok - && self.closed_shell - && self.manifold_edges - && self.consistent_orientation - && self.finite_geometry - && self.no_degenerate_faces - } -} - -const AREA_EPSILON: f64 = 1.0e-12; - -/// Runs the full geometric validity check. Never mutates the input. -pub fn check_validity(brep: &Brep) -> ValidityReport { - let mut report = ValidityReport { - topology_ok: brep.validate_topology().is_ok(), - closed_shell: true, - manifold_edges: true, - consistent_orientation: true, - finite_geometry: true, - no_degenerate_faces: true, - issues: Vec::new(), - }; - - if !report.topology_ok { - report.issues.push("topology invariants violated".into()); - } - - // Half-edge incidence per edge. - let mut incidence: HashMap> = HashMap::new(); - for (idx, he) in brep.halfedges.iter().enumerate() { - incidence.entry(he.edge).or_default().push(idx); - } - - for (edge_id, halfedges) in &incidence { - match halfedges.len() { - 1 => { - report.closed_shell = false; - report - .issues - .push(format!("edge {} is a boundary (open shell)", edge_id)); - } - 2 => { - let a = &brep.halfedges[halfedges[0]]; - let b = &brep.halfedges[halfedges[1]]; - if !(a.from == b.to && a.to == b.from) { - report.consistent_orientation = false; - report - .issues - .push(format!("edge {} has inconsistent orientation", edge_id)); - } - } - n => { - report.manifold_edges = false; - report.issues.push(format!( - "edge {} is non-manifold ({} half-edges)", - edge_id, n - )); - } - } - } - - for vertex in &brep.vertices { - let p = vertex.position; - if !(p.x.is_finite() && p.y.is_finite() && p.z.is_finite()) { - report.finite_geometry = false; - report - .issues - .push(format!("vertex {} has non-finite coordinates", vertex.id)); - } - } - - for face in &brep.faces { - let verts = brep.get_vertices_by_face_id(face.id); - if verts.len() >= 3 && polygon_area(&verts) <= AREA_EPSILON { - report.no_degenerate_faces = false; - report - .issues - .push(format!("face {} is degenerate (~zero area)", face.id)); - } - } - - report -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct HealReport { - pub welded_vertices: usize, - pub recomputed_normals: bool, - pub actions: Vec, -} - -/// Heals common defects: welds near-coincident vertices (a frequent artifact of -/// quantized/generated input) and recomputes face normals. Returns the healed -/// B-rep and a report of what changed. Structural repairs beyond welding -/// (shell sewing, loop-orientation reversal) are intentionally out of scope so -/// the healer stays conservative and the validity signal honest. -pub fn heal(brep: &Brep, tolerance: f64) -> (Brep, HealReport) { - let mut report = HealReport::default(); - let mut healed = brep.clone(); - - let remap = weld_map(&healed.vertices, tolerance); - let welds = remap - .iter() - .enumerate() - .filter(|(i, canonical)| **canonical != *i as u32) - .count(); - - if welds > 0 { - // Rewrite half-edge endpoints and vertex outgoing references through the - // canonical map, then compact the vertex list. - for he in &mut healed.halfedges { - he.from = remap[he.from as usize]; - he.to = remap[he.to as usize]; - } - - let mut compact: Vec = vec![u32::MAX; healed.vertices.len()]; - let mut new_vertices = Vec::new(); - for (old_id, canonical) in remap.iter().enumerate() { - if *canonical == old_id as u32 { - compact[old_id] = new_vertices.len() as u32; - let mut v = healed.vertices[old_id].clone(); - v.id = compact[old_id]; - new_vertices.push(v); - } - } - for he in &mut healed.halfedges { - he.from = compact[remap[he.from as usize] as usize]; - he.to = compact[remap[he.to as usize] as usize]; - } - for v in &mut new_vertices { - if let Some(outgoing) = v.outgoing_halfedge { - let _ = outgoing; // half-edge ids unchanged; endpoints already remapped - } - } - healed.vertices = new_vertices; - report.welded_vertices = welds; - report - .actions - .push(format!("welded {} coincident vertices", welds)); - } - - if !healed.faces.is_empty() { - healed.recompute_face_normals(); - report.recomputed_normals = true; - report.actions.push("recomputed face normals".into()); - } - - (healed, report) -} - -/// Builds a vertex remap where each entry points to the lowest-index vertex -/// within tolerance of it. The threshold for a pair is the *looser* of the -/// global tolerance and either vertex's own per-vertex tolerance (D2) — so an -/// imprecise vertex (e.g. from a boolean intersection) welds at its own scale. -fn weld_map(vertices: &[super::Vertex], tolerance: f64) -> Vec { - let mut remap: Vec = (0..vertices.len() as u32).collect(); - for i in 0..vertices.len() { - if remap[i] != i as u32 { - continue; - } - for j in (i + 1)..vertices.len() { - if remap[j] != j as u32 { - continue; - } - let a = vertices[i].position; - let b = vertices[j].position; - let dx = a.x - b.x; - let dy = a.y - b.y; - let dz = a.z - b.z; - let pair_tol = tolerance - .max(vertices[i].tolerance.unwrap_or(0.0)) - .max(vertices[j].tolerance.unwrap_or(0.0)); - if dx * dx + dy * dy + dz * dz <= pair_tol * pair_tol { - remap[j] = i as u32; - } - } - } - remap -} - -fn polygon_area(points: &[Vector3]) -> f64 { - // Newell's area via the magnitude of the summed cross products. - let mut nx = 0.0; - let mut ny = 0.0; - let mut nz = 0.0; - let n = points.len(); - for i in 0..n { - let a = points[i]; - let b = points[(i + 1) % n]; - nx += a.y * b.z - a.z * b.y; - ny += a.z * b.x - a.x * b.z; - nz += a.x * b.y - a.y * b.x; - } - 0.5 * (nx * nx + ny * ny + nz * nz).sqrt() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::brep::BrepBuilder; - use crate::primitives::cylinder::OGCylinder; - use uuid::Uuid; - - fn closed_tetra() -> Brep { - let mut b = BrepBuilder::new(Uuid::new_v4()); - b.add_vertices(&[ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.5, 0.866, 0.0), - Vector3::new(0.5, 0.288, 0.816), - ]); - b.add_face(&[0, 2, 1], &[]).unwrap(); - b.add_face(&[0, 1, 3], &[]).unwrap(); - b.add_face(&[1, 2, 3], &[]).unwrap(); - b.add_face(&[2, 0, 3], &[]).unwrap(); - b.build().unwrap() - } - - #[test] - fn closed_solid_is_valid() { - let report = check_validity(&closed_tetra()); - assert!(report.is_valid(), "tetra valid: {:?}", report.issues); - assert!(report.closed_shell); - assert!(report.manifold_edges); - assert!(report.consistent_orientation); - } - - #[test] - fn cylinder_primitive_is_valid() { - let mut cyl = OGCylinder::new("v-cyl".into()); - cyl.set_config( - Vector3::new(0.0, 0.0, 0.0), - 1.0, - 2.0, - 2.0 * std::f64::consts::PI, - 24, - ) - .unwrap(); - let report = check_validity(cyl.brep()); - assert!(report.closed_shell, "cylinder closed: {:?}", report.issues); - assert!(report.manifold_edges); - } - - #[test] - fn open_shell_is_flagged_invalid() { - // A single triangle face: its three edges are boundaries (one half-edge). - let mut b = BrepBuilder::new(Uuid::new_v4()); - b.add_vertices(&[ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 1.0), - ]); - b.add_face(&[0, 1, 2], &[]).unwrap(); - let brep = b.build().unwrap(); - - let report = check_validity(&brep); - assert!(!report.closed_shell); - assert!(!report.is_valid()); - assert!(report.issues.iter().any(|i| i.contains("open shell"))); - } - - #[test] - fn per_vertex_tolerance_widens_the_weld() { - // Two vertices 5e-4 apart: too far for a 1e-6 global tolerance, but one - // vertex declares a 1e-3 per-vertex tolerance, so they weld (D2). - let mut b = BrepBuilder::new(Uuid::new_v4()); - b.add_vertices(&[ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 1.0), - ]); - b.add_face(&[0, 1, 2], &[]).unwrap(); - let mut brep = b.build().unwrap(); - let mut imprecise = super::super::Vertex::new(3, Vector3::new(5.0e-4, 0.0, 0.0)); - imprecise.tolerance = Some(1.0e-3); - brep.vertices.push(imprecise); - - // Global tolerance alone (1e-6) would NOT weld them. - let (_, tight) = heal(&brep, 1.0e-6); - // ...but the per-vertex 1e-3 tolerance does. - assert_eq!(tight.welded_vertices, 1, "per-vertex tolerance honored"); - } - - #[test] - fn healing_welds_coincident_vertices() { - // Two vertices a hair apart get welded; the rest is untouched. - let mut b = BrepBuilder::new(Uuid::new_v4()); - b.add_vertices(&[ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 1.0), - ]); - b.add_face(&[0, 1, 2], &[]).unwrap(); - let mut brep = b.build().unwrap(); - // Inject a near-duplicate of vertex 0. - brep.vertices - .push(super::super::Vertex::new(3, Vector3::new(1.0e-7, 0.0, 0.0))); - - let before = brep.vertices.len(); - let (_healed, report) = heal(&brep, 1.0e-6); - assert_eq!(report.welded_vertices, 1); - assert!(report.recomputed_normals); - assert!(before > _healed.vertices.len()); - } -} diff --git a/main/opengeometry/src/brep/vertex.rs b/main/opengeometry/src/brep/vertex.rs deleted file mode 100644 index 24f8022..0000000 --- a/main/opengeometry/src/brep/vertex.rs +++ /dev/null @@ -1,31 +0,0 @@ -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Serialize, Deserialize)] -pub struct Vertex { - pub id: u32, - pub position: Vector3, - pub outgoing_halfedge: Option, - /// Per-vertex modeling tolerance (D2). `None` ⇒ use the global confusion - /// tolerance. Set higher on vertices known to be imprecise (e.g. produced by - /// a boolean intersection or de-quantized generated input). Defaulted for - /// v1 back-compat. - #[serde(default)] - pub tolerance: Option, -} - -impl Vertex { - pub fn new(id: u32, position: Vector3) -> Self { - Self { - id, - position, - outgoing_halfedge: None, - tolerance: None, - } - } - - pub fn with_tolerance(mut self, tolerance: f64) -> Self { - self.tolerance = Some(tolerance); - self - } -} diff --git a/main/opengeometry/src/brep/wire.rs b/main/opengeometry/src/brep/wire.rs deleted file mode 100644 index e76cc5f..0000000 --- a/main/opengeometry/src/brep/wire.rs +++ /dev/null @@ -1,18 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Serialize, Deserialize)] -pub struct Wire { - pub id: u32, - pub halfedges: Vec, - pub is_closed: bool, -} - -impl Wire { - pub fn new(id: u32, halfedges: Vec, is_closed: bool) -> Self { - Self { - id, - halfedges, - is_closed, - } - } -} diff --git a/main/opengeometry/src/editor/capabilities.rs b/main/opengeometry/src/editor/capabilities.rs deleted file mode 100644 index d904315..0000000 --- a/main/opengeometry/src/editor/capabilities.rs +++ /dev/null @@ -1,298 +0,0 @@ -use wasm_bindgen::prelude::JsValue; - -use super::edits::collect_closed_quad_edge_ring; -use super::inspection::incident_faces_for_edge; -use super::validation::normalized; -use super::{EditCapabilities, FeatureEditCapabilities, OGFreeformGeometry}; - -impl OGFreeformGeometry { - pub(super) fn build_entity_edit_capabilities(&self) -> EditCapabilities { - let supports_edge_topology_edits = self.supports_any_edge_topology_edits(); - let supports_face_cut = self.supports_any_face_cut(); - let supports_loop_cut = self.supports_any_loop_cut(); - let supports_vertex_removal = self.supports_single_face_topology_edits(); - let can_remove_vertex = supports_vertex_removal && self.single_face_vertex_count() > 3; - - let mut reasons = Vec::new(); - if !supports_edge_topology_edits { - reasons.push( - "insert/split require an edge that belongs to a rebuildable face loop or wire" - .to_string(), - ); - } - if !supports_vertex_removal { - reasons.push( - "removeVertex currently requires a single-face open surface topology".to_string(), - ); - } - if !supports_face_cut { - reasons.push( - "cutFace currently requires a face without holes and at least four boundary edges" - .to_string(), - ); - } - if !supports_loop_cut { - reasons.push( - "loopCut currently requires a closed quad edge ring on a face-backed solid edge" - .to_string(), - ); - } - - EditCapabilities { - can_push_pull_face: !self.local_brep.faces.is_empty(), - can_move_face: !self.local_brep.faces.is_empty(), - can_extrude_face: self - .local_brep - .faces - .iter() - .any(|face| face.inner_loops.is_empty() && normalized(face.normal).is_some()), - can_cut_face: supports_face_cut, - can_move_edge: !self.local_brep.edges.is_empty(), - can_move_vertex: !self.local_brep.vertices.is_empty(), - can_insert_vertex_on_edge: supports_edge_topology_edits, - can_remove_vertex, - can_split_edge: supports_edge_topology_edits, - can_loop_cut: supports_loop_cut, - reasons, - } - } - - pub(super) fn build_face_edit_capabilities( - &self, - face_id: u32, - ) -> Result { - let Some(face) = self - .local_brep - .faces - .iter() - .find(|candidate| candidate.id == face_id) - else { - return Err(JsValue::from_str(&format!( - "Face {} does not exist", - face_id - ))); - }; - - let can_extrude_face = face.inner_loops.is_empty() && normalized(face.normal).is_some(); - let can_cut_face = self.supports_face_cut_on_face(face_id); - let mut reasons = Vec::new(); - if !can_extrude_face { - reasons.push("extrudeFace currently supports planar faces without holes".to_string()); - } - if !can_cut_face { - reasons.push( - "cutFace currently requires a face without holes and at least four boundary edges" - .to_string(), - ); - } - - Ok(FeatureEditCapabilities { - domain: "face".to_string(), - topology_id: face_id, - can_push_pull_face: true, - can_move_face: true, - can_extrude_face, - can_cut_face, - can_move_edge: false, - can_move_vertex: false, - can_insert_vertex_on_edge: false, - can_remove_vertex: false, - can_split_edge: false, - can_loop_cut: false, - reasons, - }) - } - - pub(super) fn build_edge_edit_capabilities( - &self, - edge_id: u32, - ) -> Result { - if self.local_brep.edges.get(edge_id as usize).is_none() { - return Err(JsValue::from_str(&format!( - "Edge {} does not exist", - edge_id - ))); - } - - let supports_topology_edits = self.supports_rebuildable_edge_topology_edits(edge_id); - let supports_loop_cut = self.supports_loop_cut_from_edge(edge_id); - let mut reasons = Vec::new(); - if !supports_topology_edits { - reasons.push( - "insertVertexOnEdge/splitEdge require an edge that belongs to a rebuildable face loop or wire".to_string(), - ); - } - if !supports_loop_cut { - reasons.push( - "loopCut currently requires an edge that belongs to a closed quad edge ring" - .to_string(), - ); - } - - Ok(FeatureEditCapabilities { - domain: "edge".to_string(), - topology_id: edge_id, - can_push_pull_face: false, - can_move_face: false, - can_extrude_face: false, - can_cut_face: false, - can_move_edge: true, - can_move_vertex: false, - can_insert_vertex_on_edge: supports_topology_edits, - can_remove_vertex: false, - can_split_edge: supports_topology_edits, - can_loop_cut: supports_loop_cut, - reasons, - }) - } - - pub(super) fn build_vertex_edit_capabilities( - &self, - vertex_id: u32, - ) -> Result { - if self.local_brep.vertices.get(vertex_id as usize).is_none() { - return Err(JsValue::from_str(&format!( - "Vertex {} does not exist", - vertex_id - ))); - } - - let supports_topology_edits = self.supports_single_face_topology_edits(); - let can_remove_vertex = supports_topology_edits - && self.single_face_vertex_count() > 3 - && self.single_face_contains_vertex(vertex_id); - - let mut reasons = Vec::new(); - if !supports_topology_edits { - reasons.push("removeVertex requires a single-face open surface topology".to_string()); - } else if !can_remove_vertex { - reasons.push( - "removeVertex requires a boundary vertex on a face with at least four vertices" - .to_string(), - ); - } - - Ok(FeatureEditCapabilities { - domain: "vertex".to_string(), - topology_id: vertex_id, - can_push_pull_face: false, - can_move_face: false, - can_extrude_face: false, - can_cut_face: false, - can_move_edge: false, - can_move_vertex: true, - can_insert_vertex_on_edge: false, - can_remove_vertex, - can_split_edge: false, - can_loop_cut: false, - reasons, - }) - } - - pub(super) fn supports_single_face_topology_edits(&self) -> bool { - if self.local_brep.faces.len() != 1 { - return false; - } - - let Some(face) = self.local_brep.faces.first() else { - return false; - }; - - if !face.inner_loops.is_empty() { - return false; - } - - let loop_vertices = self.local_brep.get_loop_vertex_indices(face.outer_loop); - if loop_vertices.len() < 3 { - return false; - } - - for edge in &self.local_brep.edges { - let incident = incident_faces_for_edge(&self.local_brep, edge.id); - if incident.len() > 1 { - return false; - } - } - - true - } - - pub(super) fn supports_any_edge_topology_edits(&self) -> bool { - self.local_brep - .edges - .iter() - .any(|edge| self.supports_rebuildable_edge_topology_edits(edge.id)) - } - - pub(super) fn supports_rebuildable_edge_topology_edits(&self, edge_id: u32) -> bool { - let Some(edge) = self.local_brep.edges.get(edge_id as usize) else { - return false; - }; - - [Some(edge.halfedge), edge.twin_halfedge] - .into_iter() - .flatten() - .filter_map(|halfedge_id| self.local_brep.halfedges.get(halfedge_id as usize)) - .any(|halfedge| halfedge.face.is_some() || halfedge.wire_ref.is_some()) - } - - pub(super) fn supports_any_face_cut(&self) -> bool { - self.local_brep - .faces - .iter() - .any(|face| self.supports_face_cut_on_face(face.id)) - } - - pub(super) fn supports_face_cut_on_face(&self, face_id: u32) -> bool { - let Some(face) = self - .local_brep - .faces - .iter() - .find(|candidate| candidate.id == face_id) - else { - return false; - }; - - if !face.inner_loops.is_empty() { - return false; - } - - self.local_brep - .get_loop_halfedges(face.outer_loop) - .map(|halfedges| halfedges.len() >= 4) - .unwrap_or(false) - } - - pub(super) fn supports_any_loop_cut(&self) -> bool { - self.local_brep - .edges - .iter() - .any(|edge| self.supports_loop_cut_from_edge(edge.id)) - } - - pub(super) fn supports_loop_cut_from_edge(&self, edge_id: u32) -> bool { - collect_closed_quad_edge_ring(&self.local_brep, edge_id).is_ok() - } - - fn single_face_vertex_count(&self) -> usize { - self.local_brep - .faces - .first() - .map(|face| { - self.local_brep - .get_loop_vertex_indices(face.outer_loop) - .len() - }) - .unwrap_or(0) - } - - fn single_face_contains_vertex(&self, vertex_id: u32) -> bool { - let Some(face) = self.local_brep.faces.first() else { - return false; - }; - - self.local_brep - .get_loop_vertex_indices(face.outer_loop) - .contains(&vertex_id) - } -} diff --git a/main/opengeometry/src/editor/edits.rs b/main/opengeometry/src/editor/edits.rs deleted file mode 100644 index 338cb68..0000000 --- a/main/opengeometry/src/editor/edits.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod edge_insert; -mod extrusion; -mod face_cut; -mod loop_cut; -mod topology; -mod translation; -mod vertex_remove; - -pub(in crate::editor) use loop_cut::collect_closed_quad_edge_ring; diff --git a/main/opengeometry/src/editor/edits/edge_insert.rs b/main/opengeometry/src/editor/edits/edge_insert.rs deleted file mode 100644 index 2efd69c..0000000 --- a/main/opengeometry/src/editor/edits/edge_insert.rs +++ /dev/null @@ -1,201 +0,0 @@ -use crate::brep::BrepBuilder; -use openmaths::Vector3; - -use super::super::{ - BrepDiagnostic, EditEffect, OGFreeformGeometry, TopologyChangeJournal, GEOMETRY_EPSILON, -}; -use super::topology::undirected_edge_key; - -impl OGFreeformGeometry { - pub(in crate::editor) fn insert_vertex_on_edge_internal( - &mut self, - edge_id: u32, - t: f64, - ) -> Result { - if !self.supports_rebuildable_edge_topology_edits(edge_id) { - return Err(BrepDiagnostic::error( - "unsupported_topology", - "insertVertexOnEdge requires an edge used by a rebuildable face loop or wire", - ) - .with_domain("edge", Some(edge_id))); - } - - if !t.is_finite() || t <= GEOMETRY_EPSILON || t >= 1.0 - GEOMETRY_EPSILON { - return Err(BrepDiagnostic::error( - "invalid_parameter", - "insertVertexOnEdge expects t in (0, 1)", - ) - .with_domain("edge", Some(edge_id))); - } - - let old_brep = self.local_brep.clone(); - let (from_id, to_id) = old_brep.get_edge_endpoints(edge_id).ok_or_else(|| { - BrepDiagnostic::error("missing_edge", format!("Edge {} does not exist", edge_id)) - .with_domain("edge", Some(edge_id)) - })?; - - let from = old_brep - .vertices - .get(from_id as usize) - .map(|vertex| vertex.position) - .ok_or_else(|| { - BrepDiagnostic::error( - "missing_vertex_reference", - format!( - "Vertex {} referenced by edge {} is missing", - from_id, edge_id - ), - ) - .with_domain("vertex", Some(from_id)) - })?; - - let to = old_brep - .vertices - .get(to_id as usize) - .map(|vertex| vertex.position) - .ok_or_else(|| { - BrepDiagnostic::error( - "missing_vertex_reference", - format!("Vertex {} referenced by edge {} is missing", to_id, edge_id), - ) - .with_domain("vertex", Some(to_id)) - })?; - - let inserted = Vector3::new( - from.x + (to.x - from.x) * t, - from.y + (to.y - from.y) * t, - from.z + (to.z - from.z) * t, - ); - - let mut positions = old_brep - .vertices - .iter() - .map(|vertex| vertex.position) - .collect::>(); - let inserted_vertex_id = positions.len() as u32; - positions.push(inserted); - - let (updated_faces, affected_faces) = self.rebuild_faces_with_inserted_edge_vertex( - &old_brep, - from_id, - to_id, - inserted_vertex_id, - ); - let (updated_wires, affected_wires) = self.rebuild_wires_with_inserted_edge_vertex( - &old_brep, - from_id, - to_id, - inserted_vertex_id, - ); - - if affected_faces.is_empty() && affected_wires.is_empty() { - return Err(BrepDiagnostic::error( - "unsupported_topology", - "insertVertexOnEdge requires an edge used by a rebuildable face loop or wire", - ) - .with_domain("edge", Some(edge_id))); - } - - let mut builder = BrepBuilder::new(self.local_brep.id); - builder.add_vertices(&positions); - - for (outer, holes) in &updated_faces { - builder.add_face(outer, holes).map_err(|error| { - BrepDiagnostic::error( - "edge_split_failed", - format!("Failed to rebuild face with inserted vertex: {}", error), - ) - .with_domain("edge", Some(edge_id)) - })?; - } - - for (wire_vertices, is_closed) in &updated_wires { - builder - .add_wire(wire_vertices, *is_closed) - .map_err(|error| { - BrepDiagnostic::error( - "edge_split_failed", - format!("Failed to rebuild wire with inserted vertex: {}", error), - ) - .with_domain("edge", Some(edge_id)) - })?; - } - - for shell in &old_brep.shells { - builder - .add_shell(&shell.faces, shell.is_closed) - .map_err(|error| { - BrepDiagnostic::error( - "edge_split_failed", - format!("Failed to rebuild shell after edge split: {}", error), - ) - .with_domain("edge", Some(edge_id)) - })?; - } - - self.local_brep = builder.build().map_err(|error| { - BrepDiagnostic::error( - "edge_split_failed", - format!("Failed to finalize edge split: {}", error), - ) - .with_domain("edge", Some(edge_id)) - })?; - - let mut journal = TopologyChangeJournal::default(); - for face_id in &affected_faces { - journal.faces.map(*face_id, vec![*face_id]); - } - journal.vertices.add_created(inserted_vertex_id); - - let edge_lookup = self.edge_lookup_by_endpoints(); - for edge in &old_brep.edges { - if edge.id == edge_id { - let first_key = undirected_edge_key(from_id, inserted_vertex_id); - let second_key = undirected_edge_key(inserted_vertex_id, to_id); - - let mut mapped = Vec::new(); - if let Some(id) = edge_lookup.get(&first_key).copied() { - mapped.push(id); - } - if let Some(id) = edge_lookup.get(&second_key).copied() { - mapped.push(id); - } - journal.edges.map(edge.id, mapped); - continue; - } - - if let Some((start, end)) = old_brep.get_edge_endpoints(edge.id) { - let key = undirected_edge_key(start, end); - if let Some(new_edge) = edge_lookup.get(&key).copied() { - journal.edges.map(edge.id, vec![new_edge]); - } - } - } - - let mut changed_faces = affected_faces; - changed_faces.sort_unstable(); - changed_faces.dedup(); - let changed_edges = self.local_brep.edges.iter().map(|edge| edge.id).collect(); - let changed_vertices = vec![from_id, to_id, inserted_vertex_id]; - - Ok(EditEffect { - diagnostics: vec![BrepDiagnostic::info( - "vertex_inserted_on_edge", - format!("Inserted vertex on edge {}", edge_id), - ) - .with_domain("edge", Some(edge_id))], - topology_journal: Some(journal), - changed_faces, - changed_edges, - changed_vertices, - }) - } - - pub(in crate::editor) fn split_edge_internal( - &mut self, - edge_id: u32, - t: f64, - ) -> Result { - self.insert_vertex_on_edge_internal(edge_id, t) - } -} diff --git a/main/opengeometry/src/editor/edits/extrusion.rs b/main/opengeometry/src/editor/edits/extrusion.rs deleted file mode 100644 index cd6817c..0000000 --- a/main/opengeometry/src/editor/edits/extrusion.rs +++ /dev/null @@ -1,281 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use openmaths::Vector3; - -use crate::brep::BrepBuilder; - -use super::super::validation::normalized; -use super::super::{ - BrepDiagnostic, ConstraintSettings, EditEffect, OGFreeformGeometry, TopologyChangeJournal, - GEOMETRY_EPSILON, -}; -use super::topology::undirected_edge_key; - -impl OGFreeformGeometry { - pub(in crate::editor) fn extrude_face_internal( - &mut self, - face_id: u32, - distance: f64, - open_surface_mode: bool, - ) -> Result { - if !distance.is_finite() || distance.abs() <= GEOMETRY_EPSILON { - return Err(BrepDiagnostic::error( - "invalid_distance", - "Extrude distance must be a finite non-zero number", - ) - .with_domain("face", Some(face_id))); - } - - let Some(face) = self - .local_brep - .faces - .iter() - .find(|candidate| candidate.id == face_id) - .cloned() - else { - return Err(BrepDiagnostic::error( - "missing_face", - format!("Face {} does not exist", face_id), - ) - .with_domain("face", Some(face_id))); - }; - - if !face.inner_loops.is_empty() { - return Err(BrepDiagnostic::error( - "unsupported_face", - "extrudeFace currently supports faces without holes", - ) - .with_domain("face", Some(face_id))); - } - - if open_surface_mode { - let mut effect = - self.push_pull_face_internal(face_id, distance, &ConstraintSettings::default())?; - effect.diagnostics.push( - BrepDiagnostic::info( - "open_surface_mode", - "openSurfaceMode uses topology-preserving face translation", - ) - .with_domain("face", Some(face_id)), - ); - return Ok(effect); - } - - let loop_vertices = self.local_brep.get_loop_vertex_indices(face.outer_loop); - if loop_vertices.len() < 3 { - return Err(BrepDiagnostic::error( - "invalid_face_topology", - "Face has insufficient vertices for extrusion", - ) - .with_domain("face", Some(face_id))); - } - - let mut boundary_edge_count = 0usize; - for edge in &self.local_brep.edges { - let mut incident_count = 0usize; - if let Some((a, b)) = self.local_brep.get_edge_endpoints(edge.id) { - for index in 0..loop_vertices.len() { - let from = loop_vertices[index]; - let to = loop_vertices[(index + 1) % loop_vertices.len()]; - if undirected_edge_key(a, b) == undirected_edge_key(from, to) { - incident_count = self.incident_face_count(edge.id); - break; - } - } - } - if incident_count == 1 { - boundary_edge_count += 1; - } - } - - if boundary_edge_count == 0 { - let mut effect = - self.push_pull_face_internal(face_id, distance, &ConstraintSettings::default())?; - effect.diagnostics.push( - BrepDiagnostic::info( - "solid_face_extrude", - "Closed-solid face extrusion resolved to push/pull translation", - ) - .with_domain("face", Some(face_id)), - ); - return Ok(effect); - } - - if !self.supports_single_face_topology_edits() { - return Err(BrepDiagnostic::error( - "unsupported_topology", - "extrudeFace currently supports open-surface extrusion on single-face entities", - ) - .with_domain("face", Some(face_id))); - } - - let Some(normal) = normalized(face.normal) else { - return Err(BrepDiagnostic::error( - "invalid_face_normal", - "Face normal is invalid for extrusion", - ) - .with_domain("face", Some(face_id))); - }; - - let old_brep = self.local_brep.clone(); - let old_positions: HashMap = old_brep - .vertices - .iter() - .map(|vertex| (vertex.id, vertex.position)) - .collect(); - - let mut positions = old_brep - .vertices - .iter() - .map(|vertex| vertex.position) - .collect::>(); - - for vertex_id in &loop_vertices { - let Some(vertex) = positions.get_mut(*vertex_id as usize) else { - return Err(BrepDiagnostic::error( - "missing_vertex_reference", - format!("Face {} references missing vertex {}", face_id, vertex_id), - ) - .with_domain("vertex", Some(*vertex_id))); - }; - vertex.x += normal.x * distance; - vertex.y += normal.y * distance; - vertex.z += normal.z * distance; - } - - let mut bottom_vertex_map: HashMap = HashMap::new(); - for vertex_id in &loop_vertices { - let bottom_id = positions.len() as u32; - let Some(original_position) = old_positions.get(vertex_id) else { - return Err(BrepDiagnostic::error( - "missing_vertex_reference", - format!("Vertex {} not found in source face", vertex_id), - ) - .with_domain("vertex", Some(*vertex_id))); - }; - positions.push(*original_position); - bottom_vertex_map.insert(*vertex_id, bottom_id); - } - - let mut builder = BrepBuilder::new(self.local_brep.id); - builder.add_vertices(&positions); - - builder.add_face(&loop_vertices, &[]).map_err(|error| { - BrepDiagnostic::error( - "extrude_failed", - format!("Failed to build extruded top face: {}", error), - ) - .with_domain("face", Some(face_id)) - })?; - - let bottom_loop: Vec = loop_vertices - .iter() - .rev() - .map(|vertex_id| bottom_vertex_map[vertex_id]) - .collect(); - - builder.add_face(&bottom_loop, &[]).map_err(|error| { - BrepDiagnostic::error( - "extrude_failed", - format!("Failed to build extruded bottom face: {}", error), - ) - .with_domain("face", Some(face_id)) - })?; - - for index in 0..loop_vertices.len() { - let top_current = loop_vertices[index]; - let top_next = loop_vertices[(index + 1) % loop_vertices.len()]; - let bottom_current = bottom_vertex_map[&top_current]; - let bottom_next = bottom_vertex_map[&top_next]; - - let side = vec![top_current, bottom_current, bottom_next, top_next]; - builder.add_face(&side, &[]).map_err(|error| { - BrepDiagnostic::error( - "extrude_failed", - format!("Failed to build extruded side face: {}", error), - ) - .with_domain("face", Some(face_id)) - })?; - } - - builder.add_shell_from_all_faces(true).map_err(|error| { - BrepDiagnostic::error( - "extrude_failed", - format!("Failed to build closed shell: {}", error), - ) - .with_domain("face", Some(face_id)) - })?; - - self.local_brep = builder.build().map_err(|error| { - BrepDiagnostic::error( - "extrude_failed", - format!("Failed to finalize extruded topology: {}", error), - ) - .with_domain("face", Some(face_id)) - })?; - - let mut journal = TopologyChangeJournal::default(); - journal.faces.map(face_id, vec![0]); - - let after_edge_lookup = self.edge_lookup_by_endpoints(); - for edge in &old_brep.edges { - if let Some((from, to)) = old_brep.get_edge_endpoints(edge.id) { - let key = undirected_edge_key(from, to); - if let Some(new_edge_id) = after_edge_lookup.get(&key).copied() { - journal.edges.map(edge.id, vec![new_edge_id]); - } - } - } - - let new_face_ids: HashSet = self.local_brep.faces.iter().map(|face| face.id).collect(); - for face in new_face_ids { - if face != 0 { - journal.faces.add_created(face); - } - } - - let mapped_edges: HashSet = - journal.edges.mapping.values().flatten().copied().collect(); - for edge in &self.local_brep.edges { - if !mapped_edges.contains(&edge.id) { - journal.edges.add_created(edge.id); - } - } - - let old_vertex_count = old_brep.vertices.len() as u32; - for vertex_id in old_vertex_count..self.local_brep.vertices.len() as u32 { - journal.vertices.add_created(vertex_id); - } - - let changed_faces = self - .local_brep - .faces - .iter() - .map(|face| face.id) - .collect::>(); - let changed_edges = self - .local_brep - .edges - .iter() - .map(|edge| edge.id) - .collect::>(); - let changed_vertices = self - .local_brep - .vertices - .iter() - .map(|vertex| vertex.id) - .collect::>(); - - Ok(EditEffect { - diagnostics: vec![BrepDiagnostic::info( - "face_extruded", - format!("Face {} extruded successfully", face_id), - ) - .with_domain("face", Some(face_id))], - topology_journal: Some(journal), - changed_faces, - changed_edges, - changed_vertices, - }) - } -} diff --git a/main/opengeometry/src/editor/edits/face_cut.rs b/main/opengeometry/src/editor/edits/face_cut.rs deleted file mode 100644 index 72b9256..0000000 --- a/main/opengeometry/src/editor/edits/face_cut.rs +++ /dev/null @@ -1,327 +0,0 @@ -use std::collections::HashSet; - -use crate::brep::BrepBuilder; - -use super::super::{ - BrepDiagnostic, EditEffect, OGFreeformGeometry, TopologyChangeJournal, GEOMETRY_EPSILON, -}; -use super::topology::{ - face_outer_loop_edges, insert_vertex_into_loop, interpolate_loop_edge_position, - split_loop_between_vertices, undirected_edge_key, -}; - -impl OGFreeformGeometry { - pub(in crate::editor) fn cut_face_internal( - &mut self, - face_id: u32, - start_edge_id: u32, - start_t: f64, - end_edge_id: u32, - end_t: f64, - ) -> Result { - if start_edge_id == end_edge_id { - return Err(BrepDiagnostic::error( - "invalid_topology_selection", - "cutFace requires two distinct edges on the target face", - ) - .with_domain("face", Some(face_id))); - } - - for (edge_id, t_value) in [(start_edge_id, start_t), (end_edge_id, end_t)] { - if !t_value.is_finite() - || t_value <= GEOMETRY_EPSILON - || t_value >= 1.0 - GEOMETRY_EPSILON - { - return Err(BrepDiagnostic::error( - "invalid_parameter", - "cutFace expects t in (0, 1)", - ) - .with_domain("edge", Some(edge_id))); - } - } - - if !self.supports_face_cut_on_face(face_id) { - return Err(BrepDiagnostic::error( - "unsupported_topology", - "cutFace currently requires a face without holes and at least four boundary edges", - ) - .with_domain("face", Some(face_id))); - } - - let old_brep = self.local_brep.clone(); - let face = old_brep - .faces - .iter() - .find(|candidate| candidate.id == face_id) - .cloned() - .ok_or_else(|| { - BrepDiagnostic::error("missing_face", format!("Face {} does not exist", face_id)) - .with_domain("face", Some(face_id)) - })?; - - let face_vertices = old_brep.get_loop_vertex_indices(face.outer_loop); - let face_edges = face_outer_loop_edges(&old_brep, face_id)?; - - let start_edge_index = face_edges - .iter() - .position(|candidate| *candidate == start_edge_id) - .ok_or_else(|| { - BrepDiagnostic::error( - "edge_not_on_face", - format!("Edge {} is not part of face {}", start_edge_id, face_id), - ) - .with_domain("edge", Some(start_edge_id)) - })?; - let end_edge_index = face_edges - .iter() - .position(|candidate| *candidate == end_edge_id) - .ok_or_else(|| { - BrepDiagnostic::error( - "edge_not_on_face", - format!("Edge {} is not part of face {}", end_edge_id, face_id), - ) - .with_domain("edge", Some(end_edge_id)) - })?; - - let start_from = face_vertices[start_edge_index]; - let start_to = face_vertices[(start_edge_index + 1) % face_vertices.len()]; - let end_from = face_vertices[end_edge_index]; - let end_to = face_vertices[(end_edge_index + 1) % face_vertices.len()]; - - let start_inserted = interpolate_loop_edge_position( - &old_brep, - start_from, - start_to, - start_t, - start_edge_id, - )?; - let end_inserted = - interpolate_loop_edge_position(&old_brep, end_from, end_to, end_t, end_edge_id)?; - - let mut positions = old_brep - .vertices - .iter() - .map(|vertex| vertex.position) - .collect::>(); - let start_inserted_vertex_id = positions.len() as u32; - positions.push(start_inserted); - let end_inserted_vertex_id = positions.len() as u32; - positions.push(end_inserted); - - let mut primary_faces = Vec::with_capacity(old_brep.faces.len()); - let mut secondary_face_loop = None; - let mut changed_faces = HashSet::new(); - - for candidate_face in &old_brep.faces { - let outer_loop = old_brep.get_loop_vertex_indices(candidate_face.outer_loop); - let (outer_with_start, outer_start_changed) = insert_vertex_into_loop( - &outer_loop, - start_from, - start_to, - start_inserted_vertex_id, - ); - let (outer_with_both, outer_end_changed) = insert_vertex_into_loop( - &outer_with_start, - end_from, - end_to, - end_inserted_vertex_id, - ); - - let mut holes = Vec::with_capacity(candidate_face.inner_loops.len()); - let mut hole_changed = false; - for loop_id in &candidate_face.inner_loops { - let hole_loop = old_brep.get_loop_vertex_indices(*loop_id); - let (hole_with_start, hole_start_changed) = insert_vertex_into_loop( - &hole_loop, - start_from, - start_to, - start_inserted_vertex_id, - ); - let (hole_with_both, hole_end_changed) = insert_vertex_into_loop( - &hole_with_start, - end_from, - end_to, - end_inserted_vertex_id, - ); - hole_changed |= hole_start_changed || hole_end_changed; - holes.push(hole_with_both); - } - - if candidate_face.id == face_id { - if !outer_start_changed || !outer_end_changed { - return Err(BrepDiagnostic::error( - "cut_face_failed", - format!( - "Failed to insert cut points into the boundary of face {}", - face_id - ), - ) - .with_domain("face", Some(face_id))); - } - - let (primary_loop, secondary_loop) = split_loop_between_vertices( - &outer_with_both, - start_inserted_vertex_id, - end_inserted_vertex_id, - face_id, - )?; - - primary_faces.push((primary_loop, Vec::new())); - secondary_face_loop = Some(secondary_loop); - changed_faces.insert(face_id); - continue; - } - - if outer_start_changed || outer_end_changed || hole_changed { - changed_faces.insert(candidate_face.id); - } - - primary_faces.push((outer_with_both, holes)); - } - - let mut builder = BrepBuilder::new(self.local_brep.id); - builder.add_vertices(&positions); - - for (outer, holes) in &primary_faces { - builder.add_face(outer, holes).map_err(|error| { - BrepDiagnostic::error( - "cut_face_failed", - format!("Failed to rebuild face during cutFace: {}", error), - ) - .with_domain("face", Some(face_id)) - })?; - } - - let secondary_face_loop = secondary_face_loop.ok_or_else(|| { - BrepDiagnostic::error( - "cut_face_failed", - "Failed to create the second face produced by cutFace", - ) - .with_domain("face", Some(face_id)) - })?; - let new_face_id = builder - .add_face(&secondary_face_loop, &[]) - .map_err(|error| { - BrepDiagnostic::error( - "cut_face_failed", - format!("Failed to append the second cut face: {}", error), - ) - .with_domain("face", Some(face_id)) - })?; - - for shell in &old_brep.shells { - let mut shell_faces = Vec::new(); - for old_face_id in &shell.faces { - shell_faces.push(*old_face_id); - if *old_face_id == face_id { - shell_faces.push(new_face_id); - } - } - - builder - .add_shell(&shell_faces, shell.is_closed) - .map_err(|error| { - BrepDiagnostic::error( - "cut_face_failed", - format!("Failed to rebuild shell after cutFace: {}", error), - ) - .with_domain("face", Some(face_id)) - })?; - } - - self.local_brep = builder.build().map_err(|error| { - BrepDiagnostic::error( - "cut_face_failed", - format!("Failed to finalize cutFace: {}", error), - ) - .with_domain("face", Some(face_id)) - })?; - - let mut journal = TopologyChangeJournal::default(); - journal.faces.map(face_id, vec![face_id, new_face_id]); - journal.faces.add_created(new_face_id); - journal.vertices.add_created(start_inserted_vertex_id); - journal.vertices.add_created(end_inserted_vertex_id); - - let edge_lookup = self.edge_lookup_by_endpoints(); - let mut changed_edges = HashSet::new(); - - for edge in &old_brep.edges { - let Some((from, to)) = old_brep.get_edge_endpoints(edge.id) else { - continue; - }; - - let mapped = if edge.id == start_edge_id || edge.id == end_edge_id { - let inserted_vertex_id = if edge.id == start_edge_id { - start_inserted_vertex_id - } else { - end_inserted_vertex_id - }; - - let mut split_edges = Vec::new(); - let first_key = undirected_edge_key(from, inserted_vertex_id); - let second_key = undirected_edge_key(inserted_vertex_id, to); - - if let Some(mapped_id) = edge_lookup.get(&first_key).copied() { - split_edges.push(mapped_id); - } - if let Some(mapped_id) = edge_lookup.get(&second_key).copied() { - split_edges.push(mapped_id); - } - - changed_edges.extend(split_edges.iter().copied()); - split_edges - } else { - let key = undirected_edge_key(from, to); - edge_lookup - .get(&key) - .copied() - .map(|mapped_id| vec![mapped_id]) - .unwrap_or_default() - }; - - journal.edges.map(edge.id, mapped); - } - - let connecting_edge_key = - undirected_edge_key(start_inserted_vertex_id, end_inserted_vertex_id); - let connecting_edge_id = - edge_lookup - .get(&connecting_edge_key) - .copied() - .ok_or_else(|| { - BrepDiagnostic::error( - "cut_face_failed", - "cutFace did not produce the new connecting edge", - ) - .with_domain("face", Some(face_id)) - })?; - journal.edges.add_created(connecting_edge_id); - changed_edges.insert(connecting_edge_id); - - changed_faces.insert(new_face_id); - - let mut changed_faces = changed_faces.into_iter().collect::>(); - changed_faces.sort_unstable(); - - let mut changed_edges = changed_edges.into_iter().collect::>(); - changed_edges.sort_unstable(); - - let changed_vertices = vec![start_inserted_vertex_id, end_inserted_vertex_id]; - - Ok(EditEffect { - diagnostics: vec![BrepDiagnostic::info( - "face_cut_applied", - format!( - "Cut face {} between edges {} and {}", - face_id, start_edge_id, end_edge_id - ), - ) - .with_domain("face", Some(face_id))], - topology_journal: Some(journal), - changed_faces, - changed_edges, - changed_vertices, - }) - } -} diff --git a/main/opengeometry/src/editor/edits/loop_cut.rs b/main/opengeometry/src/editor/edits/loop_cut.rs deleted file mode 100644 index ddb0c2a..0000000 --- a/main/opengeometry/src/editor/edits/loop_cut.rs +++ /dev/null @@ -1,504 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use openmaths::Vector3; - -use crate::brep::{Brep, BrepBuilder}; - -use super::super::inspection::incident_faces_for_edge; -use super::super::validation::normalized; -use super::super::{ - BrepDiagnostic, EditEffect, OGFreeformGeometry, TopologyChangeJournal, GEOMETRY_EPSILON, -}; -use super::topology::{ - face_outer_loop_edges, insert_vertex_into_loop, split_loop_between_vertices, - undirected_edge_key, -}; - -#[derive(Clone)] -pub(in crate::editor) struct LoopCutFaceStep { - pub face_id: u32, - pub input_edge_id: u32, - pub opposite_edge_id: u32, -} - -#[derive(Clone)] -pub(in crate::editor) struct LoopCutRing { - pub edge_ids: Vec, - pub face_steps: Vec, -} - -impl OGFreeformGeometry { - pub(in crate::editor) fn loop_cut_edge_ring_internal( - &mut self, - edge_id: u32, - t: f64, - ) -> Result { - if !t.is_finite() || t <= GEOMETRY_EPSILON || t >= 1.0 - GEOMETRY_EPSILON { - return Err( - BrepDiagnostic::error("invalid_parameter", "loopCut expects t in (0, 1)") - .with_domain("edge", Some(edge_id)), - ); - } - - let old_brep = self.local_brep.clone(); - let ring = collect_closed_quad_edge_ring(&old_brep, edge_id)?; - - let (reference_from, reference_to) = - old_brep.get_edge_endpoints(edge_id).ok_or_else(|| { - BrepDiagnostic::error("missing_edge", format!("Edge {} does not exist", edge_id)) - .with_domain("edge", Some(edge_id)) - })?; - - let reference_direction = old_brep - .vertices - .get(reference_to as usize) - .zip(old_brep.vertices.get(reference_from as usize)) - .and_then(|(to, from)| { - normalized(Vector3::new( - to.position.x - from.position.x, - to.position.y - from.position.y, - to.position.z - from.position.z, - )) - }); - - let mut positions = old_brep - .vertices - .iter() - .map(|vertex| vertex.position) - .collect::>(); - let mut inserted_vertex_by_edge = HashMap::new(); - - for ring_edge_id in &ring.edge_ids { - let (from_id, to_id) = old_brep.get_edge_endpoints(*ring_edge_id).ok_or_else(|| { - BrepDiagnostic::error( - "missing_edge", - format!("Edge {} does not exist", ring_edge_id), - ) - .with_domain("edge", Some(*ring_edge_id)) - })?; - - let from = old_brep - .vertices - .get(from_id as usize) - .map(|vertex| vertex.position) - .ok_or_else(|| { - BrepDiagnostic::error( - "missing_vertex_reference", - format!( - "Vertex {} referenced by edge {} is missing", - from_id, ring_edge_id - ), - ) - .with_domain("vertex", Some(from_id)) - })?; - let to = old_brep - .vertices - .get(to_id as usize) - .map(|vertex| vertex.position) - .ok_or_else(|| { - BrepDiagnostic::error( - "missing_vertex_reference", - format!( - "Vertex {} referenced by edge {} is missing", - to_id, ring_edge_id - ), - ) - .with_domain("vertex", Some(to_id)) - })?; - - let edge_direction = - normalized(Vector3::new(to.x - from.x, to.y - from.y, to.z - from.z)); - let local_t = - if let (Some(reference), Some(direction)) = (reference_direction, edge_direction) { - if reference.dot(&direction) < 0.0 { - 1.0 - t - } else { - t - } - } else { - t - }; - - let inserted = Vector3::new( - from.x + (to.x - from.x) * local_t, - from.y + (to.y - from.y) * local_t, - from.z + (to.z - from.z) * local_t, - ); - - let inserted_vertex_id = positions.len() as u32; - positions.push(inserted); - inserted_vertex_by_edge.insert(*ring_edge_id, inserted_vertex_id); - } - - let face_step_by_id = ring - .face_steps - .iter() - .map(|step| (step.face_id, step)) - .collect::>(); - - let mut primary_faces = Vec::with_capacity(old_brep.faces.len()); - let mut extra_face_specs = Vec::<(u32, Vec)>::new(); - - for face in &old_brep.faces { - let outer_loop = old_brep.get_loop_vertex_indices(face.outer_loop); - - if let Some(step) = face_step_by_id.get(&face.id) { - let inserted_a = *inserted_vertex_by_edge - .get(&step.input_edge_id) - .ok_or_else(|| { - BrepDiagnostic::error( - "loop_cut_failed", - format!("Missing inserted vertex for edge {}", step.input_edge_id), - ) - .with_domain("edge", Some(step.input_edge_id)) - })?; - let inserted_b = *inserted_vertex_by_edge - .get(&step.opposite_edge_id) - .ok_or_else(|| { - BrepDiagnostic::error( - "loop_cut_failed", - format!("Missing inserted vertex for edge {}", step.opposite_edge_id), - ) - .with_domain("edge", Some(step.opposite_edge_id)) - })?; - - let (input_from, input_to) = old_brep - .get_edge_endpoints(step.input_edge_id) - .ok_or_else(|| { - BrepDiagnostic::error( - "missing_edge", - format!("Edge {} does not exist", step.input_edge_id), - ) - .with_domain("edge", Some(step.input_edge_id)) - })?; - let (opposite_from, opposite_to) = old_brep - .get_edge_endpoints(step.opposite_edge_id) - .ok_or_else(|| { - BrepDiagnostic::error( - "missing_edge", - format!("Edge {} does not exist", step.opposite_edge_id), - ) - .with_domain("edge", Some(step.opposite_edge_id)) - })?; - - let (with_first, inserted_first) = - insert_vertex_into_loop(&outer_loop, input_from, input_to, inserted_a); - let (with_both, inserted_second) = - insert_vertex_into_loop(&with_first, opposite_from, opposite_to, inserted_b); - - if !inserted_first || !inserted_second { - return Err(BrepDiagnostic::error( - "loop_cut_failed", - format!( - "Failed to insert loop-cut vertices into face {} boundary", - face.id - ), - ) - .with_domain("face", Some(face.id))); - } - - let (primary_loop, secondary_loop) = - split_loop_between_vertices(&with_both, inserted_a, inserted_b, face.id)?; - - primary_faces.push((primary_loop, Vec::new())); - extra_face_specs.push((face.id, secondary_loop)); - continue; - } - - let holes = face - .inner_loops - .iter() - .map(|loop_id| old_brep.get_loop_vertex_indices(*loop_id)) - .collect::>(); - primary_faces.push((outer_loop, holes)); - } - - let mut builder = BrepBuilder::new(self.local_brep.id); - builder.add_vertices(&positions); - - for (outer, holes) in &primary_faces { - builder.add_face(outer, holes).map_err(|error| { - BrepDiagnostic::error( - "loop_cut_failed", - format!("Failed to rebuild primary face during loop cut: {}", error), - ) - .with_domain("edge", Some(edge_id)) - })?; - } - - let old_face_count = old_brep.faces.len() as u32; - let mut extra_face_id_by_old = HashMap::new(); - - for (index, (old_face_id, outer)) in extra_face_specs.iter().enumerate() { - builder.add_face(outer, &[]).map_err(|error| { - BrepDiagnostic::error( - "loop_cut_failed", - format!("Failed to rebuild split face during loop cut: {}", error), - ) - .with_domain("face", Some(*old_face_id)) - })?; - extra_face_id_by_old.insert(*old_face_id, old_face_count + index as u32); - } - - for shell in &old_brep.shells { - let mut shell_faces = Vec::new(); - for old_face_id in &shell.faces { - shell_faces.push(*old_face_id); - if let Some(extra_face_id) = extra_face_id_by_old.get(old_face_id) { - shell_faces.push(*extra_face_id); - } - } - - builder - .add_shell(&shell_faces, shell.is_closed) - .map_err(|error| { - BrepDiagnostic::error( - "loop_cut_failed", - format!("Failed to rebuild shell after loop cut: {}", error), - ) - .with_domain("edge", Some(edge_id)) - })?; - } - - self.local_brep = builder.build().map_err(|error| { - BrepDiagnostic::error( - "loop_cut_failed", - format!("Failed to finalize loop cut: {}", error), - ) - .with_domain("edge", Some(edge_id)) - })?; - - let mut journal = TopologyChangeJournal::default(); - for (old_face_id, extra_face_id) in &extra_face_id_by_old { - journal - .faces - .map(*old_face_id, vec![*old_face_id, *extra_face_id]); - journal.faces.add_created(*extra_face_id); - } - - for inserted_vertex_id in inserted_vertex_by_edge.values() { - journal.vertices.add_created(*inserted_vertex_id); - } - - let edge_lookup = self.edge_lookup_by_endpoints(); - let mut changed_edges = HashSet::new(); - - for edge in &old_brep.edges { - let Some((from, to)) = old_brep.get_edge_endpoints(edge.id) else { - continue; - }; - - let mapped = if let Some(inserted_vertex_id) = inserted_vertex_by_edge.get(&edge.id) { - let mut split_edges = Vec::new(); - let first_key = undirected_edge_key(from, *inserted_vertex_id); - let second_key = undirected_edge_key(*inserted_vertex_id, to); - - if let Some(id) = edge_lookup.get(&first_key).copied() { - split_edges.push(id); - } - if let Some(id) = edge_lookup.get(&second_key).copied() { - split_edges.push(id); - } - - split_edges - } else { - let key = undirected_edge_key(from, to); - edge_lookup - .get(&key) - .copied() - .map(|id| vec![id]) - .unwrap_or_default() - }; - - changed_edges.extend(mapped.iter().copied()); - journal.edges.map(edge.id, mapped); - } - - for step in &ring.face_steps { - let inserted_a = inserted_vertex_by_edge[&step.input_edge_id]; - let inserted_b = inserted_vertex_by_edge[&step.opposite_edge_id]; - let key = undirected_edge_key(inserted_a, inserted_b); - if let Some(new_edge_id) = edge_lookup.get(&key).copied() { - journal.edges.add_created(new_edge_id); - changed_edges.insert(new_edge_id); - } - } - - let mut changed_faces = ring - .face_steps - .iter() - .map(|step| step.face_id) - .collect::>(); - changed_faces.extend(extra_face_id_by_old.values().copied()); - changed_faces.sort_unstable(); - changed_faces.dedup(); - - let mut changed_edges = changed_edges.into_iter().collect::>(); - changed_edges.sort_unstable(); - - let mut changed_vertices = inserted_vertex_by_edge - .values() - .copied() - .collect::>(); - changed_vertices.sort_unstable(); - changed_vertices.dedup(); - - Ok(EditEffect { - diagnostics: vec![BrepDiagnostic::info( - "loop_cut_applied", - format!( - "Applied loop cut across {} quad faces starting from edge {}", - ring.face_steps.len(), - edge_id - ), - ) - .with_domain("edge", Some(edge_id))], - topology_journal: Some(journal), - changed_faces, - changed_edges, - changed_vertices, - }) - } -} - -pub(in crate::editor) fn collect_closed_quad_edge_ring( - brep: &Brep, - start_edge_id: u32, -) -> Result { - let incident_faces = incident_faces_for_edge(brep, start_edge_id); - if incident_faces.len() != 2 { - return Err(BrepDiagnostic::error( - "unsupported_topology", - "loopCut currently requires an edge with two incident faces in a closed quad ring", - ) - .with_domain("edge", Some(start_edge_id))); - } - - let mut last_error = None; - for start_face_id in incident_faces { - match traverse_closed_quad_edge_ring(brep, start_edge_id, start_face_id) { - Ok(ring) => return Ok(ring), - Err(error) => last_error = Some(error), - } - } - - Err(last_error.unwrap_or_else(|| { - BrepDiagnostic::error( - "unsupported_topology", - "loopCut could not resolve a valid closed quad edge ring", - ) - .with_domain("edge", Some(start_edge_id)) - })) -} - -fn traverse_closed_quad_edge_ring( - brep: &Brep, - start_edge_id: u32, - start_face_id: u32, -) -> Result { - let mut edge_ids = vec![start_edge_id]; - let mut face_steps = Vec::new(); - let mut visited_faces = HashSet::new(); - let mut current_edge_id = start_edge_id; - let mut current_face_id = start_face_id; - - loop { - if !visited_faces.insert(current_face_id) { - return Err(BrepDiagnostic::error( - "unsupported_topology", - "loopCut edge ring revisited a face before closing cleanly", - ) - .with_domain("face", Some(current_face_id))); - } - - let face = brep - .faces - .iter() - .find(|candidate| candidate.id == current_face_id) - .ok_or_else(|| { - BrepDiagnostic::error( - "missing_face", - format!("Face {} does not exist", current_face_id), - ) - .with_domain("face", Some(current_face_id)) - })?; - - if !face.inner_loops.is_empty() { - return Err(BrepDiagnostic::error( - "unsupported_topology", - "loopCut currently supports quad faces without holes", - ) - .with_domain("face", Some(current_face_id))); - } - - let loop_edges = face_outer_loop_edges(brep, face.id)?; - if loop_edges.len() != 4 { - return Err(BrepDiagnostic::error( - "unsupported_topology", - "loopCut currently supports quad edge rings only", - ) - .with_domain("face", Some(current_face_id))); - } - - let current_index = loop_edges - .iter() - .position(|candidate| *candidate == current_edge_id) - .ok_or_else(|| { - BrepDiagnostic::error( - "invalid_face_topology", - format!( - "Edge {} is not part of face {} while traversing loopCut ring", - current_edge_id, current_face_id - ), - ) - .with_domain("edge", Some(current_edge_id)) - })?; - let opposite_edge_id = loop_edges[(current_index + 2) % loop_edges.len()]; - - face_steps.push(LoopCutFaceStep { - face_id: current_face_id, - input_edge_id: current_edge_id, - opposite_edge_id, - }); - - if opposite_edge_id == start_edge_id { - return Ok(LoopCutRing { - edge_ids, - face_steps, - }); - } - - if edge_ids.contains(&opposite_edge_id) { - return Err(BrepDiagnostic::error( - "unsupported_topology", - "loopCut encountered a repeated opposite edge before closing the ring", - ) - .with_domain("edge", Some(opposite_edge_id))); - } - - edge_ids.push(opposite_edge_id); - let incident_faces = incident_faces_for_edge(brep, opposite_edge_id); - if incident_faces.len() != 2 { - return Err(BrepDiagnostic::error( - "unsupported_topology", - "loopCut currently requires a closed quad ring without boundary edges", - ) - .with_domain("edge", Some(opposite_edge_id))); - } - - current_face_id = incident_faces - .into_iter() - .find(|face_id| *face_id != current_face_id) - .ok_or_else(|| { - BrepDiagnostic::error( - "unsupported_topology", - format!( - "Edge {} did not expose a traversable neighboring face for loopCut", - opposite_edge_id - ), - ) - .with_domain("edge", Some(opposite_edge_id)) - })?; - current_edge_id = opposite_edge_id; - } -} diff --git a/main/opengeometry/src/editor/edits/topology.rs b/main/opengeometry/src/editor/edits/topology.rs deleted file mode 100644 index b712e47..0000000 --- a/main/opengeometry/src/editor/edits/topology.rs +++ /dev/null @@ -1,325 +0,0 @@ -use std::collections::HashMap; - -use openmaths::Vector3; - -use crate::brep::Brep; - -use super::super::{BrepDiagnostic, OGFreeformGeometry}; - -impl OGFreeformGeometry { - pub(super) fn edge_lookup_by_endpoints(&self) -> HashMap<(u32, u32), u32> { - let mut lookup = HashMap::new(); - for edge in &self.local_brep.edges { - if let Some((start, end)) = self.local_brep.get_edge_endpoints(edge.id) { - lookup.insert(undirected_edge_key(start, end), edge.id); - } - } - lookup - } - - pub(super) fn rebuild_faces_with_inserted_edge_vertex( - &self, - brep: &Brep, - from_id: u32, - to_id: u32, - inserted_vertex_id: u32, - ) -> (Vec<(Vec, Vec>)>, Vec) { - let mut updated_faces = Vec::with_capacity(brep.faces.len()); - let mut affected_faces = Vec::new(); - - for face in &brep.faces { - let outer_loop = brep.get_loop_vertex_indices(face.outer_loop); - let (outer, outer_changed) = - insert_vertex_into_loop(&outer_loop, from_id, to_id, inserted_vertex_id); - - let mut holes = Vec::with_capacity(face.inner_loops.len()); - let mut hole_changed = false; - for loop_id in &face.inner_loops { - let hole_loop = brep.get_loop_vertex_indices(*loop_id); - let (hole, changed) = - insert_vertex_into_loop(&hole_loop, from_id, to_id, inserted_vertex_id); - holes.push(hole); - hole_changed |= changed; - } - - if outer_changed || hole_changed { - affected_faces.push(face.id); - } - - updated_faces.push((outer, holes)); - } - - (updated_faces, affected_faces) - } - - pub(super) fn rebuild_wires_with_inserted_edge_vertex( - &self, - brep: &Brep, - from_id: u32, - to_id: u32, - inserted_vertex_id: u32, - ) -> (Vec<(Vec, bool)>, Vec) { - let mut updated_wires = Vec::with_capacity(brep.wires.len()); - let mut affected_wires = Vec::new(); - - for wire in &brep.wires { - let wire_vertices = brep.get_wire_vertex_indices(wire.id); - let (updated_vertices, changed) = if wire.is_closed { - insert_vertex_into_loop(&wire_vertices, from_id, to_id, inserted_vertex_id) - } else { - insert_vertex_into_open_chain(&wire_vertices, from_id, to_id, inserted_vertex_id) - }; - - if changed { - affected_wires.push(wire.id); - } - - updated_wires.push((updated_vertices, wire.is_closed)); - } - - (updated_wires, affected_wires) - } - - pub(super) fn single_face_loop_edges(&self, brep: &Brep) -> Result, BrepDiagnostic> { - let face = brep.faces.first().ok_or_else(|| { - BrepDiagnostic::error("missing_face", "Single-face topology has no face") - .with_domain("face", Some(0)) - })?; - - let halfedges = brep.get_loop_halfedges(face.outer_loop).map_err(|error| { - BrepDiagnostic::error( - "invalid_face_topology", - format!("Failed to inspect face loop: {}", error), - ) - .with_domain("face", Some(face.id)) - })?; - - let mut edges = Vec::with_capacity(halfedges.len()); - for halfedge_id in halfedges { - let Some(halfedge) = brep.halfedges.get(halfedge_id as usize) else { - return Err(BrepDiagnostic::error( - "invalid_halfedge_reference", - format!("Missing halfedge {} in face loop", halfedge_id), - ) - .with_domain("face", Some(face.id))); - }; - edges.push(halfedge.edge); - } - - Ok(edges) - } - - pub(super) fn incident_face_count(&self, edge_id: u32) -> usize { - let Some(edge) = self.local_brep.edges.get(edge_id as usize) else { - return 0; - }; - - let mut count = 0usize; - let halfedges = [Some(edge.halfedge), edge.twin_halfedge]; - for halfedge_id in halfedges.into_iter().flatten() { - if let Some(halfedge) = self.local_brep.halfedges.get(halfedge_id as usize) { - if halfedge.face.is_some() { - count += 1; - } - } - } - - count - } -} - -pub(super) fn undirected_edge_key(a: u32, b: u32) -> (u32, u32) { - if a <= b { - (a, b) - } else { - (b, a) - } -} - -pub(super) fn interpolate_loop_edge_position( - brep: &Brep, - from_id: u32, - to_id: u32, - t: f64, - edge_id: u32, -) -> Result { - let from = brep - .vertices - .get(from_id as usize) - .map(|vertex| vertex.position) - .ok_or_else(|| { - BrepDiagnostic::error( - "missing_vertex_reference", - format!( - "Vertex {} referenced by edge {} is missing", - from_id, edge_id - ), - ) - .with_domain("vertex", Some(from_id)) - })?; - let to = brep - .vertices - .get(to_id as usize) - .map(|vertex| vertex.position) - .ok_or_else(|| { - BrepDiagnostic::error( - "missing_vertex_reference", - format!("Vertex {} referenced by edge {} is missing", to_id, edge_id), - ) - .with_domain("vertex", Some(to_id)) - })?; - - Ok(Vector3::new( - from.x + (to.x - from.x) * t, - from.y + (to.y - from.y) * t, - from.z + (to.z - from.z) * t, - )) -} - -pub(super) fn insert_vertex_into_loop( - loop_vertices: &[u32], - from_id: u32, - to_id: u32, - inserted_vertex_id: u32, -) -> (Vec, bool) { - let mut updated = loop_vertices.to_vec(); - - for index in 0..loop_vertices.len() { - let current = loop_vertices[index]; - let next = loop_vertices[(index + 1) % loop_vertices.len()]; - - if (current == from_id && next == to_id) || (current == to_id && next == from_id) { - updated.insert(index + 1, inserted_vertex_id); - return (updated, true); - } - } - - (updated, false) -} - -pub(super) fn insert_vertex_into_open_chain( - chain_vertices: &[u32], - from_id: u32, - to_id: u32, - inserted_vertex_id: u32, -) -> (Vec, bool) { - let mut updated = chain_vertices.to_vec(); - - for index in 0..chain_vertices.len().saturating_sub(1) { - let current = chain_vertices[index]; - let next = chain_vertices[index + 1]; - - if (current == from_id && next == to_id) || (current == to_id && next == from_id) { - updated.insert(index + 1, inserted_vertex_id); - return (updated, true); - } - } - - (updated, false) -} - -pub(super) fn face_outer_loop_edges(brep: &Brep, face_id: u32) -> Result, BrepDiagnostic> { - let face = brep - .faces - .iter() - .find(|candidate| candidate.id == face_id) - .ok_or_else(|| { - BrepDiagnostic::error("missing_face", format!("Face {} does not exist", face_id)) - .with_domain("face", Some(face_id)) - })?; - - let loop_halfedges = brep.get_loop_halfedges(face.outer_loop).map_err(|error| { - BrepDiagnostic::error( - "invalid_face_topology", - format!("Failed to inspect face {} loop: {}", face_id, error), - ) - .with_domain("face", Some(face_id)) - })?; - - let mut edges = Vec::with_capacity(loop_halfedges.len()); - for halfedge_id in loop_halfedges { - let halfedge = brep.halfedges.get(halfedge_id as usize).ok_or_else(|| { - BrepDiagnostic::error( - "invalid_halfedge_reference", - format!( - "Face {} references missing halfedge {}", - face_id, halfedge_id - ), - ) - .with_domain("face", Some(face_id)) - })?; - edges.push(halfedge.edge); - } - - Ok(edges) -} - -pub(super) fn split_loop_between_vertices( - loop_vertices: &[u32], - first_inserted_vertex_id: u32, - second_inserted_vertex_id: u32, - face_id: u32, -) -> Result<(Vec, Vec), BrepDiagnostic> { - let first_index = loop_vertices - .iter() - .position(|vertex_id| *vertex_id == first_inserted_vertex_id) - .ok_or_else(|| { - BrepDiagnostic::error( - "loop_cut_failed", - format!( - "Inserted loop-cut vertex {} is missing from face {} boundary", - first_inserted_vertex_id, face_id - ), - ) - .with_domain("face", Some(face_id)) - })?; - let second_index = loop_vertices - .iter() - .position(|vertex_id| *vertex_id == second_inserted_vertex_id) - .ok_or_else(|| { - BrepDiagnostic::error( - "loop_cut_failed", - format!( - "Inserted loop-cut vertex {} is missing from face {} boundary", - second_inserted_vertex_id, face_id - ), - ) - .with_domain("face", Some(face_id)) - })?; - - if first_index == second_index { - return Err(BrepDiagnostic::error( - "loop_cut_failed", - format!( - "Face {} loopCut vertices collapsed to the same boundary slot", - face_id - ), - ) - .with_domain("face", Some(face_id))); - } - - let primary = slice_cyclic_loop(loop_vertices, first_index, second_index); - let secondary = slice_cyclic_loop(loop_vertices, second_index, first_index); - - if primary.len() < 3 || secondary.len() < 3 { - return Err(BrepDiagnostic::error( - "loop_cut_failed", - format!("Face {} loopCut produced an invalid face boundary", face_id), - ) - .with_domain("face", Some(face_id))); - } - - Ok((primary, secondary)) -} - -fn slice_cyclic_loop(loop_vertices: &[u32], start_index: usize, end_index: usize) -> Vec { - let mut output = vec![loop_vertices[start_index]]; - let mut current_index = start_index; - - while current_index != end_index { - current_index = (current_index + 1) % loop_vertices.len(); - output.push(loop_vertices[current_index]); - } - - output -} diff --git a/main/opengeometry/src/editor/edits/translation.rs b/main/opengeometry/src/editor/edits/translation.rs deleted file mode 100644 index 5191dc4..0000000 --- a/main/opengeometry/src/editor/edits/translation.rs +++ /dev/null @@ -1,350 +0,0 @@ -use std::collections::HashSet; - -use openmaths::Vector3; - -use super::super::inspection::collect_face_vertex_ids; -use super::super::validation::{is_finite_vector, normalized}; -use super::super::{BrepDiagnostic, ConstraintSettings, EditEffect, OGFreeformGeometry}; - -impl OGFreeformGeometry { - pub(in crate::editor) fn push_pull_face_internal( - &mut self, - face_id: u32, - distance: f64, - constraints: &ConstraintSettings, - ) -> Result { - if !distance.is_finite() { - return Err(BrepDiagnostic::error( - "invalid_distance", - "Face push/pull distance must be a finite number", - ) - .with_domain("face", Some(face_id))); - } - - let Some(face) = self - .local_brep - .faces - .iter() - .find(|candidate| candidate.id == face_id) - else { - return Err(BrepDiagnostic::error( - "missing_face", - format!("Face {} does not exist", face_id), - ) - .with_domain("face", Some(face_id))); - }; - - let Some(normal) = normalized(face.normal) else { - return Err(BrepDiagnostic::error( - "invalid_face_normal", - format!("Face {} does not have a valid normal", face_id), - ) - .with_domain("face", Some(face_id))); - }; - - let base_translation = Vector3::new( - normal.x * distance, - normal.y * distance, - normal.z * distance, - ); - self.translate_face_by_vector_internal(face_id, base_translation, constraints) - } - - pub(in crate::editor) fn translate_face_by_vector_internal( - &mut self, - face_id: u32, - translation: Vector3, - constraints: &ConstraintSettings, - ) -> Result { - if !is_finite_vector(translation) { - return Err(BrepDiagnostic::error( - "invalid_translation", - "Face translation must contain finite numbers", - ) - .with_domain("face", Some(face_id))); - } - - let vertex_ids = { - let Some(face) = self - .local_brep - .faces - .iter() - .find(|candidate| candidate.id == face_id) - else { - return Err(BrepDiagnostic::error( - "missing_face", - format!("Face {} does not exist", face_id), - ) - .with_domain("face", Some(face_id))); - }; - collect_face_vertex_ids(&self.local_brep, face).map_err(|message| { - BrepDiagnostic::error("invalid_face_topology", message) - .with_domain("face", Some(face_id)) - })? - }; - - let (translation, mut diagnostics) = - self.apply_constraints(translation, constraints, "face", face_id)?; - - for vertex_id in &vertex_ids { - let Some(vertex) = self.local_brep.vertices.get_mut(*vertex_id as usize) else { - return Err(BrepDiagnostic::error( - "missing_vertex_reference", - format!("Face {} references missing vertex {}", face_id, vertex_id), - ) - .with_domain("vertex", Some(*vertex_id))); - }; - - vertex.position.x += translation.x; - vertex.position.y += translation.y; - vertex.position.z += translation.z; - } - - self.local_brep.recompute_face_normals(); - - let mut moved_vertices = HashSet::new(); - moved_vertices.extend(vertex_ids.iter().copied()); - let (changed_faces, changed_edges, changed_vertices) = - self.collect_changed_domains_for_vertices(&moved_vertices); - - diagnostics.push( - BrepDiagnostic::info( - "face_translated", - format!("Face {} translated successfully", face_id), - ) - .with_domain("face", Some(face_id)), - ); - - Ok(EditEffect { - diagnostics, - topology_journal: None, - changed_faces, - changed_edges, - changed_vertices, - }) - } - - pub(in crate::editor) fn move_edge_internal( - &mut self, - edge_id: u32, - translation: Vector3, - constraints: &ConstraintSettings, - ) -> Result { - if !is_finite_vector(translation) { - return Err(BrepDiagnostic::error( - "invalid_translation", - "Edge translation must contain finite numbers", - ) - .with_domain("edge", Some(edge_id))); - } - - let (start_id, end_id) = self.local_brep.get_edge_endpoints(edge_id).ok_or_else(|| { - BrepDiagnostic::error("missing_edge", format!("Edge {} does not exist", edge_id)) - .with_domain("edge", Some(edge_id)) - })?; - - let (translation, diagnostics) = - self.apply_constraints(translation, constraints, "edge", edge_id)?; - - let mut unique_vertices = HashSet::new(); - unique_vertices.insert(start_id); - unique_vertices.insert(end_id); - - for vertex_id in &unique_vertices { - let Some(vertex) = self.local_brep.vertices.get_mut(*vertex_id as usize) else { - return Err(BrepDiagnostic::error( - "missing_vertex_reference", - format!("Edge {} references missing vertex {}", edge_id, vertex_id), - ) - .with_domain("vertex", Some(*vertex_id))); - }; - - vertex.position.x += translation.x; - vertex.position.y += translation.y; - vertex.position.z += translation.z; - } - - self.local_brep.recompute_face_normals(); - - let (changed_faces, changed_edges, changed_vertices) = - self.collect_changed_domains_for_vertices(&unique_vertices); - - Ok(EditEffect { - diagnostics, - topology_journal: None, - changed_faces, - changed_edges, - changed_vertices, - }) - } - - pub(in crate::editor) fn move_vertex_internal( - &mut self, - vertex_id: u32, - translation: Vector3, - constraints: &ConstraintSettings, - ) -> Result { - if !is_finite_vector(translation) { - return Err(BrepDiagnostic::error( - "invalid_translation", - "Vertex translation must contain finite numbers", - ) - .with_domain("vertex", Some(vertex_id))); - } - - if self.local_brep.vertices.get(vertex_id as usize).is_none() { - return Err(BrepDiagnostic::error( - "missing_vertex", - format!("Vertex {} does not exist", vertex_id), - ) - .with_domain("vertex", Some(vertex_id))); - } - - let (translation, diagnostics) = - self.apply_constraints(translation, constraints, "vertex", vertex_id)?; - - let vertex = self - .local_brep - .vertices - .get_mut(vertex_id as usize) - .expect("vertex existence checked above"); - - vertex.position.x += translation.x; - vertex.position.y += translation.y; - vertex.position.z += translation.z; - - self.local_brep.recompute_face_normals(); - - let mut moved_vertices = HashSet::new(); - moved_vertices.insert(vertex_id); - let (changed_faces, changed_edges, changed_vertices) = - self.collect_changed_domains_for_vertices(&moved_vertices); - - Ok(EditEffect { - diagnostics, - topology_journal: None, - changed_faces, - changed_edges, - changed_vertices, - }) - } - - fn apply_constraints( - &self, - incoming: Vector3, - constraints: &ConstraintSettings, - domain: &str, - topology_id: u32, - ) -> Result<(Vector3, Vec), BrepDiagnostic> { - let mut translation = incoming; - let mut diagnostics = Vec::new(); - - if !is_finite_vector(incoming) { - return Err(BrepDiagnostic::error( - "invalid_translation", - "Translation contains non-finite numbers", - ) - .with_domain(domain, Some(topology_id))); - } - - if constraints.axis.is_some() && constraints.plane_normal.is_some() { - return Err(BrepDiagnostic::error( - "conflicting_constraints", - "Specify either axis or plane constraint, not both", - ) - .with_domain(domain, Some(topology_id))); - } - - if constraints.constraint_frame == "world" { - diagnostics.push( - BrepDiagnostic::warning( - "world_frame_not_supported", - "World-frame constraints are currently interpreted in local space", - ) - .with_domain(domain, Some(topology_id)), - ); - } - - if let Some(axis) = constraints.axis { - let Some(axis_normalized) = normalized(axis) else { - return Err(BrepDiagnostic::error( - "invalid_axis_constraint", - "Axis constraint must have non-zero length", - ) - .with_domain(domain, Some(topology_id))); - }; - - let projection = translation.dot(&axis_normalized); - translation = Vector3::new( - axis_normalized.x * projection, - axis_normalized.y * projection, - axis_normalized.z * projection, - ); - } - - if let Some(plane_normal) = constraints.plane_normal { - let Some(plane_normalized) = normalized(plane_normal) else { - return Err(BrepDiagnostic::error( - "invalid_plane_constraint", - "Plane normal constraint must have non-zero length", - ) - .with_domain(domain, Some(topology_id))); - }; - - let projection = translation.dot(&plane_normalized); - translation = Vector3::new( - translation.x - plane_normalized.x * projection, - translation.y - plane_normalized.y * projection, - translation.z - plane_normalized.z * projection, - ); - } - - if constraints.preserve_coplanarity { - diagnostics.push( - BrepDiagnostic::info( - "preserve_coplanarity_requested", - "preserveCoplanarity hint recorded; direct vertex/edge edits may still alter adjacent coplanarity", - ) - .with_domain(domain, Some(topology_id)), - ); - } - - if !is_finite_vector(translation) { - return Err(BrepDiagnostic::error( - "invalid_translation", - "Constraint resolution produced non-finite translation", - ) - .with_domain(domain, Some(topology_id))); - } - - Ok((translation, diagnostics)) - } - - fn collect_changed_domains_for_vertices( - &self, - vertex_ids: &HashSet, - ) -> (Vec, Vec, Vec) { - let mut changed_faces = HashSet::new(); - let mut changed_edges = HashSet::new(); - - for halfedge in &self.local_brep.halfedges { - if vertex_ids.contains(&halfedge.from) || vertex_ids.contains(&halfedge.to) { - changed_edges.insert(halfedge.edge); - if let Some(face_id) = halfedge.face { - changed_faces.insert(face_id); - } - } - } - - let mut face_ids = changed_faces.into_iter().collect::>(); - face_ids.sort_unstable(); - - let mut edge_ids = changed_edges.into_iter().collect::>(); - edge_ids.sort_unstable(); - - let mut vertices = vertex_ids.iter().copied().collect::>(); - vertices.sort_unstable(); - - (face_ids, edge_ids, vertices) - } -} diff --git a/main/opengeometry/src/editor/edits/vertex_remove.rs b/main/opengeometry/src/editor/edits/vertex_remove.rs deleted file mode 100644 index 6edc520..0000000 --- a/main/opengeometry/src/editor/edits/vertex_remove.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::brep::BrepBuilder; - -use super::super::{BrepDiagnostic, EditEffect, OGFreeformGeometry, TopologyChangeJournal}; -use super::topology::undirected_edge_key; - -impl OGFreeformGeometry { - pub(in crate::editor) fn remove_vertex_internal( - &mut self, - vertex_id: u32, - ) -> Result { - if !self.supports_single_face_topology_edits() { - return Err(BrepDiagnostic::error( - "unsupported_topology", - "removeVertex requires a single-face open surface", - ) - .with_domain("vertex", Some(vertex_id))); - } - - let old_brep = self.local_brep.clone(); - let face = old_brep - .faces - .first() - .expect("single-face topology checked above"); - let mut loop_vertices = old_brep.get_loop_vertex_indices(face.outer_loop); - if loop_vertices.len() <= 3 { - return Err(BrepDiagnostic::error( - "insufficient_vertices", - "removeVertex requires a face with at least four vertices", - ) - .with_domain("vertex", Some(vertex_id))); - } - - let loop_edges = self.single_face_loop_edges(&old_brep)?; - - let Some(vertex_index) = loop_vertices - .iter() - .position(|candidate| *candidate == vertex_id) - else { - return Err(BrepDiagnostic::error( - "vertex_not_on_boundary", - format!("Vertex {} is not part of the editable boundary", vertex_id), - ) - .with_domain("vertex", Some(vertex_id))); - }; - - let previous_index = if vertex_index == 0 { - loop_vertices.len() - 1 - } else { - vertex_index - 1 - }; - let previous_vertex = loop_vertices[previous_index]; - let next_vertex = loop_vertices[(vertex_index + 1) % loop_vertices.len()]; - - let previous_edge = loop_edges[previous_index]; - let next_edge = loop_edges[vertex_index]; - - loop_vertices.remove(vertex_index); - - let positions = old_brep - .vertices - .iter() - .map(|vertex| vertex.position) - .collect::>(); - - let mut builder = BrepBuilder::new(self.local_brep.id); - builder.add_vertices(&positions); - builder.add_face(&loop_vertices, &[]).map_err(|error| { - BrepDiagnostic::error( - "vertex_remove_failed", - format!( - "Failed to rebuild face without vertex {}: {}", - vertex_id, error - ), - ) - .with_domain("vertex", Some(vertex_id)) - })?; - - self.local_brep = builder.build().map_err(|error| { - BrepDiagnostic::error( - "vertex_remove_failed", - format!("Failed to finalize vertex removal: {}", error), - ) - .with_domain("vertex", Some(vertex_id)) - })?; - - let mut journal = TopologyChangeJournal::default(); - journal.faces.map(face.id, vec![0]); - journal.vertices.map(vertex_id, Vec::new()); - - let edge_lookup = self.edge_lookup_by_endpoints(); - let merged_key = undirected_edge_key(previous_vertex, next_vertex); - let merged_edge = edge_lookup.get(&merged_key).copied(); - - for edge in &old_brep.edges { - if edge.id == previous_edge || edge.id == next_edge { - journal - .edges - .map(edge.id, merged_edge.map(|id| vec![id]).unwrap_or_default()); - continue; - } - - if let Some((start, end)) = old_brep.get_edge_endpoints(edge.id) { - let key = undirected_edge_key(start, end); - if let Some(new_edge) = edge_lookup.get(&key).copied() { - journal.edges.map(edge.id, vec![new_edge]); - } - } - } - - let changed_faces = vec![0]; - let changed_edges = self.local_brep.edges.iter().map(|edge| edge.id).collect(); - let changed_vertices = vec![previous_vertex, vertex_id, next_vertex]; - - Ok(EditEffect { - diagnostics: vec![BrepDiagnostic::info( - "vertex_removed", - format!("Removed vertex {} from boundary", vertex_id), - ) - .with_domain("vertex", Some(vertex_id))], - topology_journal: Some(journal), - changed_faces, - changed_edges, - changed_vertices, - }) - } -} diff --git a/main/opengeometry/src/editor/inspection.rs b/main/opengeometry/src/editor/inspection.rs deleted file mode 100644 index 0df088a..0000000 --- a/main/opengeometry/src/editor/inspection.rs +++ /dev/null @@ -1,241 +0,0 @@ -use std::collections::HashSet; - -use openmaths::Vector3; -use wasm_bindgen::prelude::JsValue; - -use crate::brep::{Brep, Face}; - -use super::{EdgeInfo, FaceInfo, OGFreeformGeometry, VertexInfo}; - -impl OGFreeformGeometry { - pub(super) fn build_face_info(&self, face_id: u32) -> Result { - let brep = self.world_brep(); - let face = brep - .faces - .iter() - .find(|face| face.id == face_id) - .ok_or_else(|| JsValue::from_str(&format!("Face {} does not exist", face_id)))?; - - let loop_ids = face_loop_ids(face); - let edge_ids = collect_face_edge_ids(&brep, face)?; - let vertex_ids = collect_face_vertex_ids(&brep, face)?; - let adjacent_face_ids = collect_adjacent_faces(&brep, &edge_ids, face.id); - - Ok(FaceInfo { - face_id, - centroid: compute_face_centroid(&brep, face).unwrap_or(Vector3::new(0.0, 0.0, 0.0)), - normal: face.normal, - surface_type: face - .surface - .as_ref() - .map(|surface| surface.kind().to_string()) - .unwrap_or_else(|| "planar".to_string()), - loop_ids, - edge_ids, - vertex_ids, - adjacent_face_ids, - }) - } - - pub(super) fn build_edge_info(&self, edge_id: u32) -> Result { - let brep = self.world_brep(); - let (start_id, end_id) = brep - .get_edge_endpoints(edge_id) - .ok_or_else(|| JsValue::from_str(&format!("Edge {} does not exist", edge_id)))?; - - let start = brep - .vertices - .get(start_id as usize) - .map(|vertex| vertex.position) - .ok_or_else(|| { - JsValue::from_str(&format!("Edge {} start vertex is missing", edge_id)) - })?; - - let end = brep - .vertices - .get(end_id as usize) - .map(|vertex| vertex.position) - .ok_or_else(|| JsValue::from_str(&format!("Edge {} end vertex is missing", edge_id)))?; - - let curve_type = brep - .edges - .iter() - .find(|edge| edge.id == edge_id) - .and_then(|edge| edge.curve.as_ref()) - .map(|curve| curve.kind().to_string()) - .unwrap_or_else(|| "line".to_string()); - - Ok(EdgeInfo { - edge_id, - curve_type, - start_vertex_id: start_id, - end_vertex_id: end_id, - start, - end, - incident_face_ids: incident_faces_for_edge(&brep, edge_id), - }) - } - - pub(super) fn build_vertex_info(&self, vertex_id: u32) -> Result { - let brep = self.world_brep(); - let vertex = brep - .vertices - .get(vertex_id as usize) - .ok_or_else(|| JsValue::from_str(&format!("Vertex {} does not exist", vertex_id)))?; - - let (edge_ids, face_ids) = connected_edges_and_faces(&brep, vertex_id); - - Ok(VertexInfo { - vertex_id, - position: vertex.position, - edge_ids, - face_ids, - }) - } -} - -pub(super) fn face_loop_ids(face: &Face) -> Vec { - let mut loop_ids = vec![face.outer_loop]; - loop_ids.extend(face.inner_loops.iter().copied()); - loop_ids -} - -pub(super) fn collect_face_vertex_ids(brep: &Brep, face: &Face) -> Result, String> { - let mut vertex_ids = Vec::new(); - let mut seen = HashSet::new(); - - for loop_id in face_loop_ids(face) { - let loop_vertices = brep - .get_loop_vertex_indices(loop_id) - .into_iter() - .collect::>(); - - if loop_vertices.is_empty() { - return Err(format!( - "Loop {} for face {} does not contain vertices", - loop_id, face.id - )); - } - - for vertex_id in loop_vertices { - if seen.insert(vertex_id) { - vertex_ids.push(vertex_id); - } - } - } - - Ok(vertex_ids) -} - -fn collect_face_edge_ids(brep: &Brep, face: &Face) -> Result, JsValue> { - let mut edge_ids = Vec::new(); - let mut seen = HashSet::new(); - - for loop_id in face_loop_ids(face) { - let loop_halfedges = brep.get_loop_halfedges(loop_id).map_err(|error| { - JsValue::from_str(&format!("Failed to read loop {}: {}", loop_id, error)) - })?; - - for halfedge_id in loop_halfedges { - let Some(halfedge) = brep.halfedges.get(halfedge_id as usize) else { - continue; - }; - if seen.insert(halfedge.edge) { - edge_ids.push(halfedge.edge); - } - } - } - - Ok(edge_ids) -} - -fn collect_adjacent_faces(brep: &Brep, edge_ids: &[u32], current_face_id: u32) -> Vec { - let mut adjacent = Vec::new(); - let mut seen = HashSet::new(); - - for edge_id in edge_ids { - for face_id in incident_faces_for_edge(brep, *edge_id) { - if face_id == current_face_id { - continue; - } - if seen.insert(face_id) { - adjacent.push(face_id); - } - } - } - - adjacent -} - -pub(super) fn incident_faces_for_edge(brep: &Brep, edge_id: u32) -> Vec { - let mut faces = Vec::new(); - let mut seen = HashSet::new(); - - let Some(edge) = brep.edges.get(edge_id as usize) else { - return faces; - }; - - let mut halfedges = vec![edge.halfedge]; - if let Some(twin_halfedge) = edge.twin_halfedge { - halfedges.push(twin_halfedge); - } - - for halfedge_id in halfedges { - let Some(halfedge) = brep.halfedges.get(halfedge_id as usize) else { - continue; - }; - - if let Some(face_id) = halfedge.face { - if seen.insert(face_id) { - faces.push(face_id); - } - } - } - - faces -} - -fn connected_edges_and_faces(brep: &Brep, vertex_id: u32) -> (Vec, Vec) { - let mut edge_ids = Vec::new(); - let mut face_ids = Vec::new(); - let mut seen_edges = HashSet::new(); - let mut seen_faces = HashSet::new(); - - for halfedge in &brep.halfedges { - if halfedge.from != vertex_id && halfedge.to != vertex_id { - continue; - } - - if seen_edges.insert(halfedge.edge) { - edge_ids.push(halfedge.edge); - } - - if let Some(face_id) = halfedge.face { - if seen_faces.insert(face_id) { - face_ids.push(face_id); - } - } - } - - (edge_ids, face_ids) -} - -fn compute_face_centroid(brep: &Brep, face: &Face) -> Option { - let vertices = brep.get_vertices_by_face_id(face.id); - if vertices.is_empty() { - return None; - } - - let mut sum_x = 0.0; - let mut sum_y = 0.0; - let mut sum_z = 0.0; - - for vertex in &vertices { - sum_x += vertex.x; - sum_y += vertex.y; - sum_z += vertex.z; - } - - let count = vertices.len() as f64; - Some(Vector3::new(sum_x / count, sum_y / count, sum_z / count)) -} diff --git a/main/opengeometry/src/editor/mod.rs b/main/opengeometry/src/editor/mod.rs deleted file mode 100644 index e97f106..0000000 --- a/main/opengeometry/src/editor/mod.rs +++ /dev/null @@ -1,545 +0,0 @@ -//! Freeform/BRep editing and inspection that operates on `OGFreeformGeometry`. -//! -//! The editor module owns direct geometry edits, topology inspection, edit -//! capability queries, and edit result serialization. `freeform` remains the -//! standalone geometry entity that stores local BRep data plus placement. - -mod capabilities; -mod edits; -mod inspection; -mod remap; -#[cfg(test)] -mod tests; -mod topology_display; -mod types; -mod validation; - -use openmaths::Vector3; -use serde::Deserialize; -use wasm_bindgen::prelude::*; - -use crate::freeform::{OGFreeformGeometry, ObjectTransformation}; - -use remap::{build_topology_remap, topology_changed, TopologySnapshot}; -use validation::validate_geometry; - -pub use types::*; - -#[derive(Clone, Deserialize)] -#[serde(default, rename_all = "camelCase")] -struct EditResultOptions { - include_brep_serialized: bool, - include_local_brep_serialized: bool, - include_geometry_serialized: bool, - include_outline_geometry_serialized: bool, - include_topology_remap: bool, - include_deltas: bool, -} - -impl Default for EditResultOptions { - fn default() -> Self { - Self { - include_brep_serialized: false, - include_local_brep_serialized: false, - include_geometry_serialized: false, - include_outline_geometry_serialized: false, - include_topology_remap: true, - include_deltas: true, - } - } -} - -#[derive(Clone, Deserialize)] -#[serde(default, rename_all = "camelCase")] -struct EditOperationOptions { - #[serde(flatten)] - result: EditResultOptions, - constraint_axis: Option, - constraint_plane_normal: Option, - preserve_coplanarity: bool, - constraint_frame: String, - open_surface_mode: bool, -} - -impl Default for EditOperationOptions { - fn default() -> Self { - Self { - result: EditResultOptions::default(), - constraint_axis: None, - constraint_plane_normal: None, - preserve_coplanarity: false, - constraint_frame: "local".to_string(), - open_surface_mode: false, - } - } -} - -#[derive(Clone, Default)] -pub(super) struct ConstraintSettings { - axis: Option, - plane_normal: Option, - preserve_coplanarity: bool, - constraint_frame: String, -} - -impl From<&EditOperationOptions> for ConstraintSettings { - fn from(options: &EditOperationOptions) -> Self { - Self { - axis: options.constraint_axis, - plane_normal: options.constraint_plane_normal, - preserve_coplanarity: options.preserve_coplanarity, - constraint_frame: options.constraint_frame.clone(), - } - } -} - -#[wasm_bindgen] -pub struct OGFreeformEditor; - -#[wasm_bindgen] -impl OGFreeformEditor { - #[wasm_bindgen(constructor)] - pub fn new() -> Self { - Self - } - - #[wasm_bindgen(js_name = getTopologyRenderData)] - pub fn get_topology_render_data( - &self, - geometry: &OGFreeformGeometry, - ) -> Result { - let payload = geometry.build_topology_display_data(); - serde_json::to_string(&payload).map_err(|error| { - JsValue::from_str(&format!( - "Failed to serialize topology display data: {}", - error - )) - }) - } - - #[wasm_bindgen(js_name = getFaceInfo)] - pub fn get_face_info( - &self, - geometry: &OGFreeformGeometry, - face_id: u32, - ) -> Result { - let info = geometry.build_face_info(face_id)?; - serde_json::to_string(&info).map_err(|error| { - JsValue::from_str(&format!("Failed to serialize face info: {}", error)) - }) - } - - #[wasm_bindgen(js_name = getEdgeInfo)] - pub fn get_edge_info( - &self, - geometry: &OGFreeformGeometry, - edge_id: u32, - ) -> Result { - let info = geometry.build_edge_info(edge_id)?; - serde_json::to_string(&info).map_err(|error| { - JsValue::from_str(&format!("Failed to serialize edge info: {}", error)) - }) - } - - #[wasm_bindgen(js_name = getVertexInfo)] - pub fn get_vertex_info( - &self, - geometry: &OGFreeformGeometry, - vertex_id: u32, - ) -> Result { - let info = geometry.build_vertex_info(vertex_id)?; - serde_json::to_string(&info).map_err(|error| { - JsValue::from_str(&format!("Failed to serialize vertex info: {}", error)) - }) - } - - #[wasm_bindgen(js_name = getEditCapabilities)] - pub fn get_edit_capabilities(&self, geometry: &OGFreeformGeometry) -> Result { - let capabilities = geometry.build_entity_edit_capabilities(); - serde_json::to_string(&capabilities).map_err(|error| { - JsValue::from_str(&format!("Failed to serialize edit capabilities: {}", error)) - }) - } - - #[wasm_bindgen(js_name = getFaceEditCapabilities)] - pub fn get_face_edit_capabilities( - &self, - geometry: &OGFreeformGeometry, - face_id: u32, - ) -> Result { - let capabilities = geometry.build_face_edit_capabilities(face_id)?; - serde_json::to_string(&capabilities).map_err(|error| { - JsValue::from_str(&format!( - "Failed to serialize face edit capabilities: {}", - error - )) - }) - } - - #[wasm_bindgen(js_name = getEdgeEditCapabilities)] - pub fn get_edge_edit_capabilities( - &self, - geometry: &OGFreeformGeometry, - edge_id: u32, - ) -> Result { - let capabilities = geometry.build_edge_edit_capabilities(edge_id)?; - serde_json::to_string(&capabilities).map_err(|error| { - JsValue::from_str(&format!( - "Failed to serialize edge edit capabilities: {}", - error - )) - }) - } - - #[wasm_bindgen(js_name = getVertexEditCapabilities)] - pub fn get_vertex_edit_capabilities( - &self, - geometry: &OGFreeformGeometry, - vertex_id: u32, - ) -> Result { - let capabilities = geometry.build_vertex_edit_capabilities(vertex_id)?; - serde_json::to_string(&capabilities).map_err(|error| { - JsValue::from_str(&format!( - "Failed to serialize vertex edit capabilities: {}", - error - )) - }) - } - - #[wasm_bindgen(js_name = pushPullFace)] - pub fn push_pull_face( - &self, - geometry: &mut OGFreeformGeometry, - face_id: u32, - distance: f64, - options_json: Option, - ) -> Result { - let options = Self::parse_edit_operation_options(options_json)?; - let constraints = ConstraintSettings::from(&options); - self.apply_edit(geometry, &options, |entity| { - entity.push_pull_face_internal(face_id, distance, &constraints) - }) - } - - #[wasm_bindgen(js_name = moveFace)] - pub fn move_face( - &self, - geometry: &mut OGFreeformGeometry, - face_id: u32, - translation: Vector3, - options_json: Option, - ) -> Result { - let options = Self::parse_edit_operation_options(options_json)?; - let constraints = ConstraintSettings::from(&options); - self.apply_edit(geometry, &options, |entity| { - entity.translate_face_by_vector_internal(face_id, translation, &constraints) - }) - } - - #[wasm_bindgen(js_name = moveEdge)] - pub fn move_edge( - &self, - geometry: &mut OGFreeformGeometry, - edge_id: u32, - translation: Vector3, - options_json: Option, - ) -> Result { - let options = Self::parse_edit_operation_options(options_json)?; - let constraints = ConstraintSettings::from(&options); - self.apply_edit(geometry, &options, |entity| { - entity.move_edge_internal(edge_id, translation, &constraints) - }) - } - - #[wasm_bindgen(js_name = moveVertex)] - pub fn move_vertex( - &self, - geometry: &mut OGFreeformGeometry, - vertex_id: u32, - translation: Vector3, - options_json: Option, - ) -> Result { - let options = Self::parse_edit_operation_options(options_json)?; - let constraints = ConstraintSettings::from(&options); - self.apply_edit(geometry, &options, |entity| { - entity.move_vertex_internal(vertex_id, translation, &constraints) - }) - } - - #[wasm_bindgen(js_name = extrudeFace)] - pub fn extrude_face( - &self, - geometry: &mut OGFreeformGeometry, - face_id: u32, - distance: f64, - options_json: Option, - ) -> Result { - let options = Self::parse_edit_operation_options(options_json)?; - self.apply_edit(geometry, &options, |entity| { - entity.extrude_face_internal(face_id, distance, options.open_surface_mode) - }) - } - - #[wasm_bindgen(js_name = cutFace)] - pub fn cut_face( - &self, - geometry: &mut OGFreeformGeometry, - face_id: u32, - start_edge_id: u32, - start_t: f64, - end_edge_id: u32, - end_t: f64, - options_json: Option, - ) -> Result { - let options = Self::parse_edit_operation_options(options_json)?; - self.apply_edit(geometry, &options, |entity| { - entity.cut_face_internal(face_id, start_edge_id, start_t, end_edge_id, end_t) - }) - } - - #[wasm_bindgen(js_name = insertVertexOnEdge)] - pub fn insert_vertex_on_edge( - &self, - geometry: &mut OGFreeformGeometry, - edge_id: u32, - t: f64, - options_json: Option, - ) -> Result { - let options = Self::parse_edit_operation_options(options_json)?; - self.apply_edit(geometry, &options, |entity| { - entity.insert_vertex_on_edge_internal(edge_id, t) - }) - } - - #[wasm_bindgen(js_name = splitEdge)] - pub fn split_edge( - &self, - geometry: &mut OGFreeformGeometry, - edge_id: u32, - t: f64, - options_json: Option, - ) -> Result { - let options = Self::parse_edit_operation_options(options_json)?; - self.apply_edit(geometry, &options, |entity| { - entity.split_edge_internal(edge_id, t) - }) - } - - #[wasm_bindgen(js_name = loopCut)] - pub fn loop_cut( - &self, - geometry: &mut OGFreeformGeometry, - edge_id: u32, - t: f64, - options_json: Option, - ) -> Result { - let options = Self::parse_edit_operation_options(options_json)?; - self.apply_edit(geometry, &options, |entity| { - entity.loop_cut_edge_ring_internal(edge_id, t) - }) - } - - #[wasm_bindgen(js_name = removeVertex)] - pub fn remove_vertex( - &self, - geometry: &mut OGFreeformGeometry, - vertex_id: u32, - options_json: Option, - ) -> Result { - let options = Self::parse_edit_operation_options(options_json)?; - self.apply_edit(geometry, &options, |entity| { - entity.remove_vertex_internal(vertex_id) - }) - } -} - -impl OGFreeformEditor { - fn parse_edit_operation_options( - options_json: Option, - ) -> Result { - match options_json { - Some(raw) => serde_json::from_str(&raw).map_err(|error| { - JsValue::from_str(&format!("Invalid edit options payload: {}", error)) - }), - None => Ok(EditOperationOptions::default()), - } - } - - fn apply_edit( - &self, - geometry: &mut OGFreeformGeometry, - options: &EditOperationOptions, - mut edit: F, - ) -> Result - where - F: FnMut(&mut OGFreeformGeometry) -> Result, - { - let backup_brep = geometry.local_brep.clone(); - let topology_before = TopologySnapshot::from_brep(&geometry.local_brep); - - let mut diagnostics = Vec::new(); - let mut effect = EditEffect::default(); - - match edit(geometry) { - Ok(outcome) => { - diagnostics.extend(outcome.diagnostics.clone()); - effect = outcome; - } - Err(error) => { - diagnostics.push(error); - } - } - - if !has_error_diagnostics(&diagnostics) { - if let Err(error) = geometry.local_brep.validate_topology() { - diagnostics.push(BrepDiagnostic::error( - "invalid_topology", - format!("Edited BRep has invalid topology: {}", error), - )); - } - - diagnostics.extend(validate_geometry(&geometry.local_brep)); - } - - let failed = has_error_diagnostics(&diagnostics); - if failed { - geometry.local_brep = backup_brep; - } - - let validity = BrepValidity { - ok: !failed, - healed: Some(false), - diagnostics, - }; - - self.serialize_edit_result( - geometry, - validity, - &topology_before, - if failed { None } else { Some(&effect) }, - &options.result, - ) - } - - fn serialize_edit_result( - &self, - geometry: &OGFreeformGeometry, - validity: BrepValidity, - topology_before: &TopologySnapshot, - effect: Option<&EditEffect>, - options: &EditResultOptions, - ) -> Result { - let world = geometry.world_brep(); - let topology_after = TopologySnapshot::from_brep(&geometry.local_brep); - let topology_changed_flag = topology_changed(topology_before, &topology_after); - - let fallback_changed_faces = if topology_changed_flag { - Some(topology_after.face_ids().to_vec()) - } else { - None - }; - let fallback_changed_edges = if topology_changed_flag { - Some(topology_after.edge_ids().to_vec()) - } else { - None - }; - let fallback_changed_vertices = if topology_changed_flag { - Some(topology_after.vertex_ids().to_vec()) - } else { - None - }; - - let result = FreeformEditResult { - entity_id: geometry.id.clone(), - brep_serialized: if options.include_brep_serialized { - Some(serde_json::to_string(&world).map_err(|error| { - JsValue::from_str(&format!("Failed to serialize world BRep: {}", error)) - })?) - } else { - None - }, - local_brep_serialized: if options.include_local_brep_serialized { - Some( - serde_json::to_string(&geometry.local_brep).map_err(|error| { - JsValue::from_str(&format!("Failed to serialize local BRep: {}", error)) - })?, - ) - } else { - None - }, - geometry_serialized: if options.include_geometry_serialized { - Some( - serde_json::to_string(&world.get_triangle_vertex_buffer()).map_err( - |error| { - JsValue::from_str(&format!( - "Failed to serialize geometry buffer: {}", - error - )) - }, - )?, - ) - } else { - None - }, - outline_geometry_serialized: if options.include_outline_geometry_serialized { - Some( - serde_json::to_string(&world.get_outline_vertex_buffer()).map_err(|error| { - JsValue::from_str(&format!("Failed to serialize outline buffer: {}", error)) - })?, - ) - } else { - None - }, - topology_changed: topology_changed_flag, - topology_remap: if options.include_topology_remap { - Some(build_topology_remap( - topology_before, - &topology_after, - effect.and_then(|entry| entry.topology_journal.as_ref()), - )) - } else { - None - }, - changed_faces: if options.include_deltas { - effect - .map(|entry| normalize_ids(&entry.changed_faces)) - .or(fallback_changed_faces) - } else { - None - }, - changed_edges: if options.include_deltas { - effect - .map(|entry| normalize_ids(&entry.changed_edges)) - .or(fallback_changed_edges) - } else { - None - }, - changed_vertices: if options.include_deltas { - effect - .map(|entry| normalize_ids(&entry.changed_vertices)) - .or(fallback_changed_vertices) - } else { - None - }, - validity, - placement: ObjectTransformation::from_placement(&geometry.placement), - }; - - serde_json::to_string(&result).map_err(|error| { - JsValue::from_str(&format!("Failed to serialize edit result: {}", error)) - }) - } -} - -fn has_error_diagnostics(diagnostics: &[BrepDiagnostic]) -> bool { - diagnostics - .iter() - .any(|diagnostic| diagnostic.severity == DiagnosticSeverity::Error) -} - -fn normalize_ids(ids: &[u32]) -> Vec { - let mut normalized = ids.to_vec(); - normalized.sort_unstable(); - normalized.dedup(); - normalized -} diff --git a/main/opengeometry/src/editor/remap.rs b/main/opengeometry/src/editor/remap.rs deleted file mode 100644 index c6dec31..0000000 --- a/main/opengeometry/src/editor/remap.rs +++ /dev/null @@ -1,216 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use crate::brep::Brep; - -use super::{ - TopologyChangeJournal, TopologyCreatedIds, TopologyDomainJournal, TopologyRemap, - TopologyRemapEntry, TopologyRemapStatus, -}; - -#[derive(Clone)] -pub(super) struct TopologySnapshot { - shell_ids: Vec, - face_ids: Vec, - loop_ids: Vec, - edge_ids: Vec, - vertex_ids: Vec, -} - -impl TopologySnapshot { - pub(super) fn from_brep(brep: &Brep) -> Self { - Self { - shell_ids: sorted_ids(brep.shells.iter().map(|shell| shell.id).collect()), - face_ids: sorted_ids(brep.faces.iter().map(|face| face.id).collect()), - loop_ids: sorted_ids(brep.loops.iter().map(|loop_ref| loop_ref.id).collect()), - edge_ids: sorted_ids(brep.edges.iter().map(|edge| edge.id).collect()), - vertex_ids: sorted_ids(brep.vertices.iter().map(|vertex| vertex.id).collect()), - } - } - - pub(super) fn face_ids(&self) -> &[u32] { - &self.face_ids - } - - pub(super) fn edge_ids(&self) -> &[u32] { - &self.edge_ids - } - - pub(super) fn vertex_ids(&self) -> &[u32] { - &self.vertex_ids - } -} - -pub(super) fn topology_changed(before: &TopologySnapshot, after: &TopologySnapshot) -> bool { - before.shell_ids != after.shell_ids - || before.face_ids != after.face_ids - || before.loop_ids != after.loop_ids - || before.edge_ids != after.edge_ids - || before.vertex_ids != after.vertex_ids -} - -pub(super) fn build_topology_remap( - before: &TopologySnapshot, - after: &TopologySnapshot, - journal: Option<&TopologyChangeJournal>, -) -> TopologyRemap { - let after_face_ids: HashSet = after.face_ids.iter().copied().collect(); - let after_edge_ids: HashSet = after.edge_ids.iter().copied().collect(); - let after_vertex_ids: HashSet = after.vertex_ids.iter().copied().collect(); - - let face_mapping = build_domain_mapping( - &before.face_ids, - &after_face_ids, - journal.map(|entry| &entry.faces.mapping), - ); - let edge_mapping = build_domain_mapping( - &before.edge_ids, - &after_edge_ids, - journal.map(|entry| &entry.edges.mapping), - ); - let vertex_mapping = build_domain_mapping( - &before.vertex_ids, - &after_vertex_ids, - journal.map(|entry| &entry.vertices.mapping), - ); - - let created_ids = TopologyCreatedIds { - faces: resolve_created_ids( - &before.face_ids, - &after.face_ids, - journal.map(|entry| &entry.faces), - ), - edges: resolve_created_ids( - &before.edge_ids, - &after.edge_ids, - journal.map(|entry| &entry.edges), - ), - vertices: resolve_created_ids( - &before.vertex_ids, - &after.vertex_ids, - journal.map(|entry| &entry.vertices), - ), - }; - - TopologyRemap { - faces: build_domain_entries_from_mapping(&before.face_ids, &face_mapping), - edges: build_domain_entries_from_mapping(&before.edge_ids, &edge_mapping), - vertices: build_domain_entries_from_mapping(&before.vertex_ids, &vertex_mapping), - created_ids, - } -} - -pub(super) fn build_domain_entries_from_mapping( - old_ids: &[u32], - mapping: &HashMap>, -) -> Vec { - let mut normalized_mapping: HashMap> = HashMap::new(); - - for old_id in old_ids { - let mut new_ids = mapping.get(old_id).cloned().unwrap_or_default(); - new_ids.sort_unstable(); - new_ids.dedup(); - normalized_mapping.insert(*old_id, new_ids); - } - - let mut new_id_usage_count: HashMap = HashMap::new(); - for new_ids in normalized_mapping.values() { - for new_id in new_ids { - *new_id_usage_count.entry(*new_id).or_insert(0) += 1; - } - } - - let mut entries = Vec::with_capacity(old_ids.len()); - for old_id in old_ids { - let new_ids = normalized_mapping.get(old_id).cloned().unwrap_or_default(); - let primary_id = new_ids.first().copied(); - let status = classify_status(&new_ids, &new_id_usage_count); - - entries.push(TopologyRemapEntry { - old_id: *old_id, - new_ids, - primary_id, - status, - }); - } - - entries.sort_by_key(|entry| entry.old_id); - entries -} - -fn classify_status( - new_ids: &[u32], - new_id_usage_count: &HashMap, -) -> TopologyRemapStatus { - if new_ids.is_empty() { - return TopologyRemapStatus::Deleted; - } - - if new_ids.len() > 1 { - return TopologyRemapStatus::Split; - } - - let new_id = new_ids[0]; - if new_id_usage_count.get(&new_id).copied().unwrap_or(0) > 1 { - return TopologyRemapStatus::Merged; - } - - TopologyRemapStatus::Unchanged -} - -fn build_domain_mapping( - old_ids: &[u32], - after_ids: &HashSet, - journal_mapping: Option<&HashMap>>, -) -> HashMap> { - let mut mapping = HashMap::new(); - - for old_id in old_ids { - if let Some(mapped) = journal_mapping.and_then(|entry| entry.get(old_id)) { - let mut normalized = mapped.clone(); - normalized.sort_unstable(); - normalized.dedup(); - normalized.retain(|id| after_ids.contains(id)); - mapping.insert(*old_id, normalized); - continue; - } - - if after_ids.contains(old_id) { - mapping.insert(*old_id, vec![*old_id]); - } else { - mapping.insert(*old_id, Vec::new()); - } - } - - mapping -} - -fn resolve_created_ids( - before_ids: &[u32], - after_ids: &[u32], - domain_journal: Option<&TopologyDomainJournal>, -) -> Vec { - let before_set: HashSet = before_ids.iter().copied().collect(); - let after_set: HashSet = after_ids.iter().copied().collect(); - - if let Some(journal) = domain_journal { - let mut created = journal.created_ids.clone(); - created.sort_unstable(); - created.dedup(); - created.retain(|id| after_set.contains(id)); - return created; - } - - let mut inferred = after_ids - .iter() - .copied() - .filter(|id| !before_set.contains(id)) - .collect::>(); - inferred.sort_unstable(); - inferred.dedup(); - inferred -} - -fn sorted_ids(mut ids: Vec) -> Vec { - ids.sort_unstable(); - ids -} diff --git a/main/opengeometry/src/editor/tests.rs b/main/opengeometry/src/editor/tests.rs deleted file mode 100644 index 01589c7..0000000 --- a/main/opengeometry/src/editor/tests.rs +++ /dev/null @@ -1,876 +0,0 @@ -use openmaths::Vector3; -use uuid::Uuid; - -use super::*; -use crate::brep::BrepBuilder; -use crate::freeform::OGFreeformGeometry; -use crate::primitives::cuboid::OGCuboid; -use crate::primitives::line::OGLine; -use crate::primitives::polygon::OGPolygon; -use crate::primitives::polyline::OGPolyline; - -fn parse_edit_result(payload: &str) -> FreeformEditResult { - serde_json::from_str(payload).expect("edit result payload") -} - -fn parse_face_info(payload: &str) -> FaceInfo { - serde_json::from_str(payload).expect("face info payload") -} - -fn parse_vertex_info(payload: &str) -> VertexInfo { - serde_json::from_str(payload).expect("vertex info payload") -} - -fn create_editor() -> OGFreeformEditor { - OGFreeformEditor::new() -} - -fn face_info(editor: &OGFreeformEditor, entity: &OGFreeformGeometry, face_id: u32) -> FaceInfo { - parse_face_info(&editor.get_face_info(entity, face_id).expect("face info")) -} - -fn vertex_info( - editor: &OGFreeformEditor, - entity: &OGFreeformGeometry, - vertex_id: u32, -) -> VertexInfo { - parse_vertex_info( - &editor - .get_vertex_info(entity, vertex_id) - .expect("vertex info"), - ) -} - -fn entity_caps(editor: &OGFreeformEditor, entity: &OGFreeformGeometry) -> EditCapabilities { - serde_json::from_str( - &editor - .get_edit_capabilities(entity) - .expect("entity capabilities payload"), - ) - .expect("entity capabilities json") -} - -fn face_caps( - editor: &OGFreeformEditor, - entity: &OGFreeformGeometry, - face_id: u32, -) -> FeatureEditCapabilities { - serde_json::from_str( - &editor - .get_face_edit_capabilities(entity, face_id) - .expect("face capabilities payload"), - ) - .expect("face capabilities json") -} - -fn edge_caps( - editor: &OGFreeformEditor, - entity: &OGFreeformGeometry, - edge_id: u32, -) -> FeatureEditCapabilities { - serde_json::from_str( - &editor - .get_edge_edit_capabilities(entity, edge_id) - .expect("edge capabilities payload"), - ) - .expect("edge capabilities json") -} - -fn extrude_face( - editor: &OGFreeformEditor, - entity: &mut OGFreeformGeometry, - face_id: u32, - distance: f64, - options: Option<&str>, -) -> FreeformEditResult { - parse_edit_result( - &editor - .extrude_face(entity, face_id, distance, options.map(str::to_string)) - .expect("extrude face payload"), - ) -} - -fn move_vertex( - editor: &OGFreeformEditor, - entity: &mut OGFreeformGeometry, - vertex_id: u32, - translation: Vector3, - options: Option<&str>, -) -> FreeformEditResult { - parse_edit_result( - &editor - .move_vertex(entity, vertex_id, translation, options.map(str::to_string)) - .expect("move vertex payload"), - ) -} - -fn split_edge( - editor: &OGFreeformEditor, - entity: &mut OGFreeformGeometry, - edge_id: u32, - t: f64, - options: Option<&str>, -) -> FreeformEditResult { - parse_edit_result( - &editor - .split_edge(entity, edge_id, t, options.map(str::to_string)) - .expect("split edge payload"), - ) -} - -fn remove_vertex( - editor: &OGFreeformEditor, - entity: &mut OGFreeformGeometry, - vertex_id: u32, - options: Option<&str>, -) -> FreeformEditResult { - parse_edit_result( - &editor - .remove_vertex(entity, vertex_id, options.map(str::to_string)) - .expect("remove vertex payload"), - ) -} - -fn loop_cut( - editor: &OGFreeformEditor, - entity: &mut OGFreeformGeometry, - edge_id: u32, - t: f64, - options: Option<&str>, -) -> FreeformEditResult { - parse_edit_result( - &editor - .loop_cut(entity, edge_id, t, options.map(str::to_string)) - .expect("loop cut payload"), - ) -} - -fn cut_face( - editor: &OGFreeformEditor, - entity: &mut OGFreeformGeometry, - face_id: u32, - start_edge_id: u32, - start_t: f64, - end_edge_id: u32, - end_t: f64, - options: Option<&str>, -) -> FreeformEditResult { - parse_edit_result( - &editor - .cut_face( - entity, - face_id, - start_edge_id, - start_t, - end_edge_id, - end_t, - options.map(str::to_string), - ) - .expect("cut face payload"), - ) -} - -fn assert_vec3_close(actual: Vector3, expected: Vector3) { - assert!((actual.x - expected.x).abs() < 1.0e-9); - assert!((actual.y - expected.y).abs() < 1.0e-9); - assert!((actual.z - expected.z).abs() < 1.0e-9); -} - -fn top_face_id(editor: &OGFreeformEditor, entity: &OGFreeformGeometry) -> u32 { - let mut best_face = 0; - let mut best_y = f64::NEG_INFINITY; - - for face in &entity.local_brep.faces { - let info = face_info(editor, entity, face.id); - if info.centroid.y > best_y { - best_y = info.centroid.y; - best_face = face.id; - } - } - - best_face -} - -fn vertical_edge_id(entity: &OGFreeformGeometry) -> u32 { - entity - .local_brep - .edges - .iter() - .find_map(|edge| { - let (start_id, end_id) = entity.local_brep.get_edge_endpoints(edge.id)?; - let start = entity.local_brep.vertices.get(start_id as usize)?.position; - let end = entity.local_brep.vertices.get(end_id as usize)?.position; - - let dx = (end.x - start.x).abs(); - let dy = (end.y - start.y).abs(); - let dz = (end.z - start.z).abs(); - - if dy > 0.2 && dx <= 1.0e-6 && dz <= 1.0e-6 { - Some(edge.id) - } else { - None - } - }) - .expect("vertical edge") -} - -fn right_side_face_id(editor: &OGFreeformEditor, entity: &OGFreeformGeometry) -> u32 { - entity - .local_brep - .faces - .iter() - .find_map(|face| { - let info = face_info(editor, entity, face.id); - if info.normal.x > 0.5 && info.normal.y.abs() < 1.0e-6 { - Some(face.id) - } else { - None - } - }) - .expect("right side face") -} - -fn opposite_vertical_face_edges( - editor: &OGFreeformEditor, - entity: &OGFreeformGeometry, - face_id: u32, -) -> (u32, u32) { - let info = face_info(editor, entity, face_id); - let mut vertical_edges = info - .edge_ids - .iter() - .filter_map(|edge_id| { - let edge = entity.local_brep.edges.get(*edge_id as usize)?; - let (start_id, end_id) = entity.local_brep.get_edge_endpoints(edge.id)?; - let start = entity.local_brep.vertices.get(start_id as usize)?.position; - let end = entity.local_brep.vertices.get(end_id as usize)?.position; - - let dx = (end.x - start.x).abs(); - let dy = (end.y - start.y).abs(); - let dz = (end.z - start.z).abs(); - - if dy > 0.2 && dx <= 1.0e-6 && dz <= 1.0e-6 { - let avg_x = (start.x + end.x) * 0.5; - Some((avg_x, edge.id)) - } else { - None - } - }) - .collect::>(); - - vertical_edges.sort_by(|left, right| left.0.total_cmp(&right.0)); - - let left_edge = vertical_edges.first().expect("left vertical edge").1; - let right_edge = vertical_edges.last().expect("right vertical edge").1; - - (left_edge, right_edge) -} - -fn remap_entry_for(entries: &[TopologyRemapEntry], old_id: u32) -> &TopologyRemapEntry { - entries - .iter() - .find(|entry| entry.old_id == old_id) - .expect("remap entry") -} - -fn create_freeform_polygon_entity(id: &str) -> OGFreeformGeometry { - let mut polygon = OGPolygon::new(format!("{}-source", id)); - polygon - .set_config(vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 2.0), - Vector3::new(0.0, 0.0, 2.0), - ]) - .expect("polygon config"); - - OGFreeformGeometry::new(id.to_string(), polygon.get_local_brep_serialized()) - .expect("freeform polygon") -} - -fn create_freeform_line_entity(id: &str) -> OGFreeformGeometry { - let mut line = OGLine::new(format!("{}-source", id)); - line.set_config(Vector3::new(-1.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)) - .expect("line config"); - - OGFreeformGeometry::new(id.to_string(), line.get_local_brep_serialized()) - .expect("freeform line") -} - -fn create_freeform_polyline_entity(id: &str, points: Vec) -> OGFreeformGeometry { - let mut polyline = OGPolyline::new(format!("{}-source", id)); - polyline.set_config(points).expect("polyline config"); - - OGFreeformGeometry::new(id.to_string(), polyline.get_local_brep_serialized()) - .expect("freeform polyline") -} - -fn create_mixed_face_and_wire_entity(id: &str) -> OGFreeformGeometry { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - let positions = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 2.0), - Vector3::new(0.0, 0.0, 2.0), - Vector3::new(3.0, 0.0, 0.0), - Vector3::new(4.0, 0.0, 0.0), - ]; - builder.add_vertices(&positions); - builder.add_face(&[0, 1, 2, 3], &[]).expect("face"); - builder.add_wire(&[4, 5], false).expect("wire"); - - let brep = builder.build().expect("mixed topology brep"); - OGFreeformGeometry::new( - id.to_string(), - serde_json::to_string(&brep).expect("mixed topology json"), - ) - .expect("freeform mixed topology") -} - -#[test] -fn extrude_face_on_open_surface_creates_watertight_shell_and_created_ids() { - let editor = create_editor(); - let mut entity = create_freeform_polygon_entity("entity-polygon-extrude"); - - let result = extrude_face(&editor, &mut entity, 0, 1.0, None); - - assert!(result.validity.ok); - assert!(result.topology_changed); - - let remap = result.topology_remap.expect("topology remap"); - let face_entry = remap_entry_for(&remap.faces, 0); - assert_eq!(face_entry.new_ids, vec![0]); - assert_eq!(face_entry.primary_id, Some(0)); - assert_eq!(face_entry.status, TopologyRemapStatus::Unchanged); - - assert_eq!(remap.created_ids.vertices.len(), 4); - assert_eq!(remap.created_ids.faces.len(), 5); - assert!(!remap.created_ids.edges.is_empty()); - - assert_eq!(entity.local_brep.faces.len(), 6); - assert_eq!(entity.local_brep.shells.len(), 1); - assert!(entity.local_brep.shells[0].is_closed); -} - -#[test] -fn extrude_face_on_closed_solid_falls_back_to_push_pull_without_topology_change() { - let editor = create_editor(); - let mut cuboid = OGCuboid::new("cuboid-extrude-solid".to_string()); - cuboid - .set_config(Vector3::new(0.0, 0.0, 0.0), 2.0, 2.0, 2.0) - .expect("cuboid config"); - - let mut entity = OGFreeformGeometry::new( - "entity-cuboid-extrude-solid".to_string(), - cuboid.get_local_brep_serialized(), - ) - .expect("freeform entity"); - - let face_id = top_face_id(&editor, &entity); - let result = extrude_face(&editor, &mut entity, face_id, 0.5, None); - - assert!(result.validity.ok); - assert!(!result.topology_changed); - assert!(result - .validity - .diagnostics - .iter() - .any(|diagnostic| diagnostic.code == "solid_face_extrude")); -} - -#[test] -fn split_edge_and_remove_vertex_emit_semantic_remap_statuses() { - let editor = create_editor(); - let mut entity = create_freeform_polygon_entity("entity-topology-edits"); - - let split = split_edge(&editor, &mut entity, 0, 0.5, None); - - assert!(split.validity.ok); - assert!(split.topology_changed); - - let split_remap = split.topology_remap.expect("split remap"); - let split_entry = remap_entry_for(&split_remap.edges, 0); - assert_eq!(split_entry.status, TopologyRemapStatus::Split); - assert_eq!(split_entry.new_ids.len(), 2); - - let inserted_vertex_id = *split_remap - .created_ids - .vertices - .first() - .expect("inserted vertex id"); - - let removed = remove_vertex(&editor, &mut entity, inserted_vertex_id, None); - - assert!(removed.validity.ok); - assert!(removed.topology_changed); - - let removed_remap = removed.topology_remap.expect("remove remap"); - let removed_vertex_entry = remap_entry_for(&removed_remap.vertices, inserted_vertex_id); - assert_eq!(removed_vertex_entry.status, TopologyRemapStatus::Deleted); - assert!(removed_vertex_entry.new_ids.is_empty()); - assert!(removed_remap - .edges - .iter() - .any(|entry| entry.status == TopologyRemapStatus::Merged)); -} - -#[test] -fn repeated_identical_sequences_are_deterministic() { - let editor = create_editor(); - let mut first = create_freeform_polygon_entity("entity-determinism-a"); - let mut second = create_freeform_polygon_entity("entity-determinism-b"); - - let first_split = split_edge(&editor, &mut first, 1, 0.3, None); - let second_split = split_edge(&editor, &mut second, 1, 0.3, None); - - let first_inserted = first_split - .topology_remap - .as_ref() - .expect("first remap") - .created_ids - .vertices[0]; - let second_inserted = second_split - .topology_remap - .as_ref() - .expect("second remap") - .created_ids - .vertices[0]; - - let first_remove = remove_vertex(&editor, &mut first, first_inserted, None); - let second_remove = remove_vertex(&editor, &mut second, second_inserted, None); - - let first_json = serde_json::to_string(&first_remove.topology_remap).expect("first remap json"); - let second_json = - serde_json::to_string(&second_remove.topology_remap).expect("second remap json"); - - assert_eq!(first_json, second_json); - - let mut first_brep: serde_json::Value = - serde_json::from_str(&first.get_local_brep_serialized()).expect("first brep json"); - let mut second_brep: serde_json::Value = - serde_json::from_str(&second.get_local_brep_serialized()).expect("second brep json"); - - first_brep["id"] = serde_json::Value::Null; - second_brep["id"] = serde_json::Value::Null; - - assert_eq!(first_brep, second_brep); -} - -#[test] -fn unsupported_topology_edit_returns_structured_error_diagnostic() { - let editor = create_editor(); - let mut cuboid = OGCuboid::new("cuboid-unsupported".to_string()); - cuboid - .set_config(Vector3::new(0.0, 0.0, 0.0), 2.0, 2.0, 2.0) - .expect("cuboid config"); - - let mut entity = OGFreeformGeometry::new( - "entity-cuboid-unsupported".to_string(), - cuboid.get_local_brep_serialized(), - ) - .expect("freeform entity"); - - let result = remove_vertex(&editor, &mut entity, 0, None); - - assert!(!result.validity.ok); - assert!(result - .validity - .diagnostics - .iter() - .any(|diagnostic| diagnostic.code == "unsupported_topology")); -} - -#[test] -fn constraints_project_move_vertex_translation_to_axis() { - let editor = create_editor(); - let mut cuboid = OGCuboid::new("cuboid-constraints".to_string()); - cuboid - .set_config(Vector3::new(0.0, 0.0, 0.0), 2.0, 2.0, 2.0) - .expect("cuboid config"); - - let mut entity = OGFreeformGeometry::new( - "entity-cuboid-constraints".to_string(), - cuboid.get_local_brep_serialized(), - ) - .expect("freeform entity"); - - let before = vertex_info(&editor, &entity, 0); - - let options = r#"{ - "constraintAxis": { "x": 0.0, "y": 1.0, "z": 0.0 }, - "preserveCoplanarity": true - }"#; - - let result = move_vertex( - &editor, - &mut entity, - 0, - Vector3::new(1.0, 0.2, 3.0), - Some(options), - ); - - assert!(result.validity.ok); - - let after = vertex_info(&editor, &entity, 0); - - assert!((after.position.x - before.position.x).abs() < 1.0e-9); - assert!((after.position.y - before.position.y - 0.2).abs() < 1.0e-9); - assert!((after.position.z - before.position.z).abs() < 1.0e-9); - - assert!(result - .validity - .diagnostics - .iter() - .any(|diagnostic| diagnostic.code == "preserve_coplanarity_requested")); -} - -#[test] -fn lean_defaults_omit_heavy_payloads_and_include_deltas() { - let editor = create_editor(); - let mut entity = create_freeform_polygon_entity("entity-lean-defaults"); - - let result = move_vertex(&editor, &mut entity, 0, Vector3::new(0.25, 0.0, 0.0), None); - - assert!(result.validity.ok); - assert!(result.brep_serialized.is_none()); - assert!(result.local_brep_serialized.is_none()); - assert!(result.geometry_serialized.is_none()); - assert!(result.outline_geometry_serialized.is_none()); - - assert!(result.topology_remap.is_some()); - assert!(result.changed_vertices.is_some()); - assert!(!result - .changed_vertices - .expect("changed vertices") - .is_empty()); -} - -#[test] -fn capability_endpoints_report_entity_and_feature_flags() { - let editor = create_editor(); - let entity = create_freeform_polygon_entity("entity-capabilities"); - - let entity_caps = entity_caps(&editor, &entity); - - assert!(entity_caps.can_insert_vertex_on_edge); - assert!(entity_caps.can_split_edge); - assert!(entity_caps.can_remove_vertex); - assert!(entity_caps.can_cut_face); - - let face_caps = face_caps(&editor, &entity, 0); - - assert!(face_caps.can_extrude_face); - assert!(face_caps.can_move_face); - assert!(face_caps.can_cut_face); -} - -#[test] -fn freeform_line_capabilities_report_wire_backed_edge_split_support() { - let editor = create_editor(); - let entity = create_freeform_line_entity("entity-line-capabilities"); - - let entity_caps = entity_caps(&editor, &entity); - assert!(entity_caps.can_insert_vertex_on_edge); - assert!(entity_caps.can_split_edge); - assert!(!entity_caps.can_cut_face); - assert!(!entity_caps.can_remove_vertex); - - let edge_caps = edge_caps(&editor, &entity, 0); - assert!(edge_caps.can_insert_vertex_on_edge); - assert!(edge_caps.can_split_edge); - assert!(!edge_caps.can_loop_cut); -} - -#[test] -fn split_edge_on_freeform_line_preserves_open_wire_topology() { - let editor = create_editor(); - let mut entity = create_freeform_line_entity("entity-line-split"); - - let result = split_edge(&editor, &mut entity, 0, 0.5, None); - - assert!(result.validity.ok); - assert!(result.topology_changed); - assert_eq!(entity.local_brep.faces.len(), 0); - assert_eq!(entity.local_brep.wires.len(), 1); - assert!(!entity.local_brep.wires[0].is_closed); - assert_eq!(entity.local_brep.edges.len(), 2); - assert_eq!(entity.local_brep.vertices.len(), 3); - assert_eq!(entity.local_brep.get_wire_vertex_indices(0), vec![0, 2, 1]); - assert_vec3_close( - entity.local_brep.vertices[2].position, - Vector3::new(0.0, 0.0, 0.0), - ); - - let remap = result.topology_remap.expect("topology remap"); - let edge_entry = remap_entry_for(&remap.edges, 0); - assert_eq!(edge_entry.status, TopologyRemapStatus::Split); - assert_eq!(edge_entry.new_ids.len(), 2); - assert_eq!(remap.created_ids.vertices, vec![2]); -} - -#[test] -fn split_edge_on_open_freeform_polyline_inserts_vertex_in_wire_order() { - let editor = create_editor(); - let mut entity = create_freeform_polyline_entity( - "entity-open-polyline-split", - vec![ - Vector3::new(-2.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 0.0), - ], - ); - - let result = split_edge(&editor, &mut entity, 1, 0.25, None); - - assert!(result.validity.ok); - assert!(result.topology_changed); - assert_eq!(entity.local_brep.wires.len(), 1); - assert!(!entity.local_brep.wires[0].is_closed); - assert_eq!( - entity.local_brep.get_wire_vertex_indices(0), - vec![0, 1, 3, 2] - ); - assert_vec3_close( - entity.local_brep.vertices[3].position, - Vector3::new(0.5, 0.0, 0.0), - ); - assert_eq!(entity.local_brep.edges.len(), 3); -} - -#[test] -fn split_edge_on_closed_freeform_polyline_preserves_closed_wire_cycle() { - let editor = create_editor(); - let mut entity = create_freeform_polyline_entity( - "entity-closed-polyline-split", - vec![ - Vector3::new(-1.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 1.5), - Vector3::new(-1.0, 0.0, 0.0), - ], - ); - - let result = split_edge(&editor, &mut entity, 2, 0.5, None); - - assert!(result.validity.ok); - assert!(result.topology_changed); - assert_eq!(entity.local_brep.wires.len(), 1); - assert!(entity.local_brep.wires[0].is_closed); - assert_eq!( - entity.local_brep.get_wire_vertex_indices(0), - vec![0, 1, 2, 3] - ); - assert_vec3_close( - entity.local_brep.vertices[3].position, - Vector3::new(-0.5, 0.0, 0.0), - ); - assert_eq!(entity.local_brep.edges.len(), 4); -} - -#[test] -fn split_edge_on_face_preserves_unrelated_wire_topology() { - let editor = create_editor(); - let mut entity = create_mixed_face_and_wire_entity("entity-mixed-face-wire"); - - let result = split_edge(&editor, &mut entity, 0, 0.5, None); - - assert!(result.validity.ok); - assert!(result.topology_changed); - assert_eq!(entity.local_brep.faces.len(), 1); - assert_eq!(entity.local_brep.wires.len(), 1); - assert_eq!(entity.local_brep.get_wire_vertex_indices(0), vec![4, 5]); - assert_eq!(entity.local_brep.edges.len(), 6); -} - -#[test] -fn split_edge_on_wire_preserves_unrelated_face_topology() { - let editor = create_editor(); - let mut entity = create_mixed_face_and_wire_entity("entity-mixed-wire-face"); - - let result = split_edge(&editor, &mut entity, 4, 0.5, None); - - assert!(result.validity.ok); - assert!(result.topology_changed); - assert_eq!(entity.local_brep.faces.len(), 1); - assert_eq!( - entity - .local_brep - .get_loop_vertex_indices(entity.local_brep.faces[0].outer_loop), - vec![0, 1, 2, 3] - ); - assert_eq!(entity.local_brep.wires.len(), 1); - assert_eq!(entity.local_brep.get_wire_vertex_indices(0), vec![4, 6, 5]); - assert_eq!(entity.local_brep.edges.len(), 6); -} - -#[test] -fn split_edge_on_wire_backed_topology_preserves_invalid_parameter_diagnostics() { - let editor = create_editor(); - let mut entity = create_freeform_line_entity("entity-line-invalid-parameter"); - - let result = split_edge(&editor, &mut entity, 0, 0.0, None); - - assert!(!result.validity.ok); - assert!(result - .validity - .diagnostics - .iter() - .any(|diagnostic| diagnostic.code == "invalid_parameter")); -} - -#[test] -fn converted_cuboid_supports_edge_split_and_returns_created_vertex() { - let editor = create_editor(); - let mut cuboid = OGCuboid::new("cuboid-edge-split".to_string()); - cuboid - .set_config(Vector3::new(0.0, 0.0, 0.0), 2.0, 2.0, 2.0) - .expect("cuboid config"); - - let mut entity = OGFreeformGeometry::new( - "entity-cuboid-edge-split".to_string(), - cuboid.get_local_brep_serialized(), - ) - .expect("freeform entity"); - - let edge_caps = edge_caps(&editor, &entity, 0); - assert!(edge_caps.can_split_edge); - - let result = split_edge(&editor, &mut entity, 0, 0.5, None); - - assert!(result.validity.ok); - assert!(result.topology_changed); - assert_eq!( - result - .topology_remap - .as_ref() - .expect("topology remap") - .created_ids - .vertices - .len(), - 1 - ); -} - -#[test] -fn converted_cuboid_loop_cut_splits_side_ring_into_additional_faces() { - let editor = create_editor(); - let mut cuboid = OGCuboid::new("cuboid-loop-cut".to_string()); - cuboid - .set_config(Vector3::new(0.0, 0.0, 0.0), 2.0, 2.0, 2.0) - .expect("cuboid config"); - - let mut entity = OGFreeformGeometry::new( - "entity-cuboid-loop-cut".to_string(), - cuboid.get_local_brep_serialized(), - ) - .expect("freeform entity"); - - let edge_id = vertical_edge_id(&entity); - let edge_caps = edge_caps(&editor, &entity, edge_id); - assert!(edge_caps.can_loop_cut); - - let result = loop_cut(&editor, &mut entity, edge_id, 0.5, None); - - assert!(result.validity.ok); - assert!(result.topology_changed); - assert_eq!(entity.local_brep.faces.len(), 10); - assert_eq!(entity.local_brep.vertices.len(), 12); - assert_eq!(entity.local_brep.shells.len(), 1); - assert!(entity.local_brep.shells[0].is_closed); - - let remap = result.topology_remap.expect("topology remap"); - assert_eq!(remap.created_ids.vertices.len(), 4); - assert_eq!(remap.created_ids.faces.len(), 4); - assert!(remap.created_ids.edges.len() >= 4); - assert_eq!( - remap - .faces - .iter() - .filter(|entry| entry.status == TopologyRemapStatus::Split) - .count(), - 4 - ); -} - -#[test] -fn converted_cuboid_cut_face_splits_only_the_selected_side_face() { - let editor = create_editor(); - let mut cuboid = OGCuboid::new("cuboid-cut-face".to_string()); - cuboid - .set_config(Vector3::new(0.0, 0.0, 0.0), 2.0, 2.0, 2.0) - .expect("cuboid config"); - - let mut entity = OGFreeformGeometry::new( - "entity-cuboid-cut-face".to_string(), - cuboid.get_local_brep_serialized(), - ) - .expect("freeform entity"); - - let face_id = right_side_face_id(&editor, &entity); - let face_caps = face_caps(&editor, &entity, face_id); - assert!(face_caps.can_cut_face); - - let (start_edge_id, end_edge_id) = opposite_vertical_face_edges(&editor, &entity, face_id); - let result = cut_face( - &editor, - &mut entity, - face_id, - start_edge_id, - 0.5, - end_edge_id, - 0.5, - None, - ); - - assert!(result.validity.ok); - assert!(result.topology_changed); - assert_eq!(entity.local_brep.faces.len(), 7); - assert_eq!(entity.local_brep.vertices.len(), 10); - assert_eq!(entity.local_brep.shells.len(), 1); - - let remap = result.topology_remap.expect("topology remap"); - let face_entry = remap_entry_for(&remap.faces, face_id); - assert_eq!(face_entry.status, TopologyRemapStatus::Split); - assert_eq!(face_entry.new_ids.len(), 2); - assert_eq!(remap.created_ids.faces.len(), 1); - assert_eq!(remap.created_ids.vertices.len(), 2); - assert_eq!(remap.created_ids.edges.len(), 1); - - let created_face_id = remap.created_ids.faces[0]; - let original_face_info = face_info(&editor, &entity, face_id); - let created_face_info = face_info(&editor, &entity, created_face_id); - assert_eq!(original_face_info.edge_ids.len(), 4); - assert_eq!(created_face_info.edge_ids.len(), 4); -} - -#[test] -fn topology_remap_builder_supports_split_merge_and_deleted_statuses() { - let old_ids = vec![10, 11, 12, 13]; - let mut mapping = std::collections::HashMap::>::new(); - mapping.insert(10, vec![100, 101]); - mapping.insert(11, vec![200]); - mapping.insert(12, vec![200]); - mapping.insert(13, Vec::new()); - - let entries = super::remap::build_domain_entries_from_mapping(&old_ids, &mapping); - - let split_entry = remap_entry_for(&entries, 10); - assert_eq!(split_entry.status, TopologyRemapStatus::Split); - assert_eq!(split_entry.primary_id, Some(100)); - assert_eq!(split_entry.new_ids, vec![100, 101]); - - let merged_left = remap_entry_for(&entries, 11); - assert_eq!(merged_left.status, TopologyRemapStatus::Merged); - assert_eq!(merged_left.primary_id, Some(200)); - assert_eq!(merged_left.new_ids, vec![200]); - - let merged_right = remap_entry_for(&entries, 12); - assert_eq!(merged_right.status, TopologyRemapStatus::Merged); - assert_eq!(merged_right.primary_id, Some(200)); - assert_eq!(merged_right.new_ids, vec![200]); - - let deleted_entry = remap_entry_for(&entries, 13); - assert_eq!(deleted_entry.status, TopologyRemapStatus::Deleted); - assert_eq!(deleted_entry.primary_id, None); - assert!(deleted_entry.new_ids.is_empty()); -} diff --git a/main/opengeometry/src/editor/topology_display.rs b/main/opengeometry/src/editor/topology_display.rs deleted file mode 100644 index b4a49f3..0000000 --- a/main/opengeometry/src/editor/topology_display.rs +++ /dev/null @@ -1,90 +0,0 @@ -use openmaths::Vector3; - -use crate::operations::triangulate::triangulate_polygon_with_holes; - -use super::{ - OGFreeformGeometry, TopologyEdgeRenderData, TopologyFaceRenderData, TopologyRenderData, - TopologyVertexRenderData, -}; - -impl OGFreeformGeometry { - pub(super) fn build_topology_display_data(&self) -> TopologyRenderData { - let brep = self.world_brep(); - let mut faces_payload = Vec::with_capacity(brep.faces.len()); - - for face in &brep.faces { - let (face_vertices, hole_vertices) = brep.get_vertices_and_holes_by_face_id(face.id); - if face_vertices.len() < 3 { - continue; - } - - let triangles = triangulate_polygon_with_holes(&face_vertices, &hole_vertices); - let all_vertices: Vec = face_vertices - .iter() - .copied() - .chain(hole_vertices.iter().flatten().copied()) - .collect(); - - let mut positions = Vec::with_capacity(all_vertices.len() * 3); - for vertex in &all_vertices { - positions.push(vertex.x); - positions.push(vertex.y); - positions.push(vertex.z); - } - - let mut indices = Vec::with_capacity(triangles.len() * 3); - for triangle in triangles { - indices.push(triangle[0] as u32); - indices.push(triangle[1] as u32); - indices.push(triangle[2] as u32); - } - - faces_payload.push(TopologyFaceRenderData { - face_id: face.id, - positions, - indices, - }); - } - - let mut edges_payload = Vec::with_capacity(brep.edges.len()); - for edge in &brep.edges { - let Some((start, end)) = brep.get_edge_endpoints(edge.id) else { - continue; - }; - - let Some(start_vertex) = brep.vertices.get(start as usize) else { - continue; - }; - let Some(end_vertex) = brep.vertices.get(end as usize) else { - continue; - }; - - edges_payload.push(TopologyEdgeRenderData { - edge_id: edge.id, - positions: vec![ - start_vertex.position.x, - start_vertex.position.y, - start_vertex.position.z, - end_vertex.position.x, - end_vertex.position.y, - end_vertex.position.z, - ], - }); - } - - let vertices_payload = brep - .vertices - .iter() - .map(|vertex| TopologyVertexRenderData { - vertex_id: vertex.id, - position: vertex.position, - }) - .collect(); - - TopologyRenderData { - faces: faces_payload, - edges: edges_payload, - vertices: vertices_payload, - } - } -} diff --git a/main/opengeometry/src/editor/types.rs b/main/opengeometry/src/editor/types.rs deleted file mode 100644 index c77d00c..0000000 --- a/main/opengeometry/src/editor/types.rs +++ /dev/null @@ -1,259 +0,0 @@ -use std::collections::HashMap; - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; - -use crate::freeform::ObjectTransformation; - -pub const GEOMETRY_EPSILON: f64 = crate::tolerance::MODELING_TOLERANCE_FLOOR; -pub const FACE_AREA_EPSILON: f64 = 1.0e-12; - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum TopologyRemapStatus { - Unchanged, - Split, - Merged, - Deleted, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct TopologyRemapEntry { - pub old_id: u32, - pub new_ids: Vec, - pub primary_id: Option, - pub status: TopologyRemapStatus, -} - -#[derive(Clone, Serialize, Deserialize, Default)] -pub struct TopologyCreatedIds { - #[serde(default)] - pub faces: Vec, - #[serde(default)] - pub edges: Vec, - #[serde(default)] - pub vertices: Vec, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct TopologyRemap { - pub faces: Vec, - pub edges: Vec, - pub vertices: Vec, - pub created_ids: TopologyCreatedIds, -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum DiagnosticSeverity { - Info, - Warning, - Error, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct BrepDiagnostic { - pub code: String, - pub severity: DiagnosticSeverity, - pub message: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub domain: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub topology_id: Option, -} - -impl BrepDiagnostic { - pub fn info(code: impl Into, message: impl Into) -> Self { - Self { - code: code.into(), - severity: DiagnosticSeverity::Info, - message: message.into(), - domain: None, - topology_id: None, - } - } - - pub fn warning(code: impl Into, message: impl Into) -> Self { - Self { - code: code.into(), - severity: DiagnosticSeverity::Warning, - message: message.into(), - domain: None, - topology_id: None, - } - } - - pub fn error(code: impl Into, message: impl Into) -> Self { - Self { - code: code.into(), - severity: DiagnosticSeverity::Error, - message: message.into(), - domain: None, - topology_id: None, - } - } - - pub fn with_domain(mut self, domain: impl Into, topology_id: Option) -> Self { - self.domain = Some(domain.into()); - self.topology_id = topology_id; - self - } -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct BrepValidity { - pub ok: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub healed: Option, - #[serde(default)] - pub diagnostics: Vec, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct FaceInfo { - pub face_id: u32, - pub centroid: Vector3, - pub normal: Vector3, - pub surface_type: String, - pub loop_ids: Vec, - pub edge_ids: Vec, - pub vertex_ids: Vec, - pub adjacent_face_ids: Vec, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct EdgeInfo { - pub edge_id: u32, - pub curve_type: String, - pub start_vertex_id: u32, - pub end_vertex_id: u32, - pub start: Vector3, - pub end: Vector3, - pub incident_face_ids: Vec, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct VertexInfo { - pub vertex_id: u32, - pub position: Vector3, - pub edge_ids: Vec, - pub face_ids: Vec, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct TopologyFaceRenderData { - pub face_id: u32, - pub positions: Vec, - pub indices: Vec, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct TopologyEdgeRenderData { - pub edge_id: u32, - pub positions: Vec, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct TopologyVertexRenderData { - pub vertex_id: u32, - pub position: Vector3, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct TopologyRenderData { - pub faces: Vec, - pub edges: Vec, - pub vertices: Vec, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct FreeformEditResult { - pub entity_id: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub brep_serialized: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub local_brep_serialized: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub geometry_serialized: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub outline_geometry_serialized: Option, - pub topology_changed: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub topology_remap: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub changed_faces: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub changed_edges: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub changed_vertices: Option>, - pub validity: BrepValidity, - pub placement: ObjectTransformation, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct EditCapabilities { - pub can_push_pull_face: bool, - pub can_move_face: bool, - pub can_extrude_face: bool, - pub can_cut_face: bool, - pub can_move_edge: bool, - pub can_move_vertex: bool, - pub can_insert_vertex_on_edge: bool, - pub can_remove_vertex: bool, - pub can_split_edge: bool, - pub can_loop_cut: bool, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub reasons: Vec, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct FeatureEditCapabilities { - pub domain: String, - pub topology_id: u32, - pub can_push_pull_face: bool, - pub can_move_face: bool, - pub can_extrude_face: bool, - pub can_cut_face: bool, - pub can_move_edge: bool, - pub can_move_vertex: bool, - pub can_insert_vertex_on_edge: bool, - pub can_remove_vertex: bool, - pub can_split_edge: bool, - pub can_loop_cut: bool, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub reasons: Vec, -} - -#[derive(Clone, Default)] -pub(super) struct TopologyDomainJournal { - pub mapping: HashMap>, - pub created_ids: Vec, -} - -impl TopologyDomainJournal { - pub fn map(&mut self, old_id: u32, mut new_ids: Vec) { - new_ids.sort_unstable(); - new_ids.dedup(); - self.mapping.insert(old_id, new_ids); - } - - pub fn add_created(&mut self, id: u32) { - self.created_ids.push(id); - } -} - -#[derive(Clone, Default)] -pub(super) struct TopologyChangeJournal { - pub faces: TopologyDomainJournal, - pub edges: TopologyDomainJournal, - pub vertices: TopologyDomainJournal, -} - -#[derive(Default)] -pub(super) struct EditEffect { - pub diagnostics: Vec, - pub topology_journal: Option, - pub changed_faces: Vec, - pub changed_edges: Vec, - pub changed_vertices: Vec, -} diff --git a/main/opengeometry/src/editor/validation.rs b/main/opengeometry/src/editor/validation.rs deleted file mode 100644 index e756898..0000000 --- a/main/opengeometry/src/editor/validation.rs +++ /dev/null @@ -1,147 +0,0 @@ -use openmaths::Vector3; - -use crate::brep::Brep; -use crate::operations::triangulate::triangulate_polygon_with_holes; - -use super::{BrepDiagnostic, FACE_AREA_EPSILON, GEOMETRY_EPSILON}; - -pub(super) fn normalized(vector: Vector3) -> Option { - let length_sq = vector.x * vector.x + vector.y * vector.y + vector.z * vector.z; - if !length_sq.is_finite() || length_sq <= GEOMETRY_EPSILON * GEOMETRY_EPSILON { - return None; - } - - let inverse_length = length_sq.sqrt().recip(); - Some(Vector3::new( - vector.x * inverse_length, - vector.y * inverse_length, - vector.z * inverse_length, - )) -} - -pub(super) fn is_finite_vector(vector: Vector3) -> bool { - vector.x.is_finite() && vector.y.is_finite() && vector.z.is_finite() -} - -fn face_area(brep: &Brep, face_id: u32) -> f64 { - let (face_vertices, hole_vertices) = brep.get_vertices_and_holes_by_face_id(face_id); - if face_vertices.len() < 3 { - return 0.0; - } - - let triangles = triangulate_polygon_with_holes(&face_vertices, &hole_vertices); - let all_vertices: Vec = face_vertices - .iter() - .copied() - .chain(hole_vertices.iter().flatten().copied()) - .collect(); - - let mut area = 0.0; - for triangle in triangles { - let Some(a) = all_vertices.get(triangle[0]) else { - continue; - }; - let Some(b) = all_vertices.get(triangle[1]) else { - continue; - }; - let Some(c) = all_vertices.get(triangle[2]) else { - continue; - }; - - let ab = Vector3::new(b.x - a.x, b.y - a.y, b.z - a.z); - let ac = Vector3::new(c.x - a.x, c.y - a.y, c.z - a.z); - let cross = ab.cross(&ac); - let triangle_area = - (cross.x * cross.x + cross.y * cross.y + cross.z * cross.z).sqrt() * 0.5; - area += triangle_area; - } - - area -} - -pub(super) fn validate_geometry(brep: &Brep) -> Vec { - let mut diagnostics = Vec::new(); - - for vertex in &brep.vertices { - if !is_finite_vector(vertex.position) { - diagnostics.push( - BrepDiagnostic::error( - "non_finite_vertex", - format!("Vertex {} has non-finite coordinates", vertex.id), - ) - .with_domain("vertex", Some(vertex.id)), - ); - } - } - - for edge in &brep.edges { - let Some((start_id, end_id)) = brep.get_edge_endpoints(edge.id) else { - diagnostics.push( - BrepDiagnostic::error( - "invalid_edge_endpoints", - format!("Edge {} has invalid endpoints", edge.id), - ) - .with_domain("edge", Some(edge.id)), - ); - continue; - }; - - let Some(start) = brep - .vertices - .get(start_id as usize) - .map(|vertex| vertex.position) - else { - diagnostics.push( - BrepDiagnostic::error( - "missing_edge_vertex", - format!("Edge {} start vertex {} is missing", edge.id, start_id), - ) - .with_domain("edge", Some(edge.id)), - ); - continue; - }; - let Some(end) = brep - .vertices - .get(end_id as usize) - .map(|vertex| vertex.position) - else { - diagnostics.push( - BrepDiagnostic::error( - "missing_edge_vertex", - format!("Edge {} end vertex {} is missing", edge.id, end_id), - ) - .with_domain("edge", Some(edge.id)), - ); - continue; - }; - - let dx = start.x - end.x; - let dy = start.y - end.y; - let dz = start.z - end.z; - let length_sq = dx * dx + dy * dy + dz * dz; - if !length_sq.is_finite() || length_sq <= GEOMETRY_EPSILON * GEOMETRY_EPSILON { - diagnostics.push( - BrepDiagnostic::error( - "collapsed_edge", - format!("Edge {} collapsed to near-zero length", edge.id), - ) - .with_domain("edge", Some(edge.id)), - ); - } - } - - for face in &brep.faces { - let area = face_area(brep, face.id); - if !area.is_finite() || area <= FACE_AREA_EPSILON { - diagnostics.push( - BrepDiagnostic::error( - "degenerate_face", - format!("Face {} has near-zero area", face.id), - ) - .with_domain("face", Some(face.id)), - ); - } - } - - diagnostics -} diff --git a/main/opengeometry/src/exchange/fit_curve/mod.rs b/main/opengeometry/src/exchange/fit_curve/mod.rs new file mode 100644 index 0000000..2b04c5e --- /dev/null +++ b/main/opengeometry/src/exchange/fit_curve/mod.rs @@ -0,0 +1,235 @@ +#[cfg(test)] +mod tests; + +use crate::brep::{ + bounds_chord_deviation, Curve, CurveGeometry, GeometryError, GeometryStore, + IntersectionDefinition, IntersectionSide, Surface, TraceAnchor, UV, +}; +use crate::math::{norm, sub, Interval, Point3}; + +#[derive(Clone, Debug)] +pub(super) struct FittedIntersectionCurve { + pub(super) controls: Vec, + pcurve_a: Vec, + pcurve_b: Vec, + pub(super) knots: Vec, + pub(super) multiplicities: Vec, + pub(super) error_bound: f64, +} + +struct FittedControls { + controls: Vec, + pcurve_a: Vec, + pcurve_b: Vec, +} + +pub(super) fn fit_intersection_curve( + store: &GeometryStore, + definition_id: u32, + tolerance: f64, + max_segments: usize, +) -> Result { + if !tolerance.is_finite() || tolerance <= 0.0 || max_segments == 0 { + return Err(GeometryError::InvalidGeometry( + "export curve fitting requires positive tolerance and segment budget".into(), + )); + } + let definition = store + .intersections + .get(definition_id as usize) + .ok_or_else(|| GeometryError::MissingReference { + kind: "intersection".into(), + index: definition_id, + })?; + definition.validate(store)?; + let first = definition.anchors.first().ok_or_else(|| { + GeometryError::InvalidGeometry("intersection curve has no first anchor".into()) + })?; + let last = definition.anchors.last().ok_or_else(|| { + GeometryError::InvalidGeometry("intersection curve has no last anchor".into()) + })?; + let curve_id = store + .curves + .iter() + .position(|curve| { + matches!(curve, CurveGeometry::Intersection { definition } if *definition == definition_id) + }) + .ok_or_else(|| GeometryError::MissingReference { + kind: "intersection curve".into(), + index: definition_id, + })?; + let curve = store + .curve(u32::try_from(curve_id).map_err(|_| { + GeometryError::LimitExceeded("intersection curve identifiers".into()) + })?)?; + let (accepted, achieved) = accepted_ranges( + store, + definition, + &curve, + first, + last, + tolerance, + max_segments, + )?; + let FittedControls { + controls, + pcurve_a, + pcurve_b, + } = fitted_controls(store, definition, &accepted, tolerance)?; + let segment_count = accepted.len(); + let knots = (0..=segment_count).map(|value| value as f64).collect(); + let multiplicities = (0..=segment_count) + .map(|index| { + if index == 0 || index == segment_count { + 4 + } else { + 3 + } + }) + .collect(); + Ok(FittedIntersectionCurve { + controls, + pcurve_a, + pcurve_b, + knots, + multiplicities, + error_bound: achieved, + }) +} + +fn accepted_ranges( + store: &GeometryStore, + definition: &IntersectionDefinition, + curve: &impl Curve, + first: &TraceAnchor, + last: &TraceAnchor, + tolerance: f64, + max_segments: usize, +) -> Result<(Vec, f64), GeometryError> { + let mut stack = vec![(Interval::new(first.parameter, last.parameter)?, 0usize)]; + let mut accepted = Vec::new(); + let mut visits = 0usize; + let mut achieved: f64 = 0.0; + while let Some((range, depth)) = stack.pop() { + visits = visits + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("export curve fit visits".into()))?; + if visits > max_segments.saturating_mul(2) { + return Err(GeometryError::LimitExceeded( + "export curve fit visits".into(), + )); + } + let a = curve.point_at(range.lo)?; + let b = curve.point_at(range.hi)?; + let deviation = if let Some(bound) = definition.certified_chord_deviation(range, store)? { + bound + } else { + bounds_chord_deviation(curve.enclose(range)?, a, b) + }; + if deviation <= tolerance { + achieved = achieved.max(deviation); + accepted.push(range); + continue; + } + if depth >= 48 || accepted.len() + stack.len() + 2 > max_segments { + return Err(GeometryError::LimitExceeded( + "export curve fit segments".into(), + )); + } + let midpoint = range.midpoint(); + stack.push((Interval::new(midpoint, range.hi)?, depth + 1)); + stack.push((Interval::new(range.lo, midpoint)?, depth + 1)); + } + accepted.sort_by(|a, b| a.lo.total_cmp(&b.lo)); + if accepted.is_empty() { + return Err(GeometryError::UnresolvedIntersection( + "export curve fitting produced no intervals".into(), + )); + } + Ok((accepted, achieved)) +} + +fn fitted_controls( + store: &GeometryStore, + definition: &IntersectionDefinition, + accepted: &[Interval], + tolerance: f64, +) -> Result { + let supports = [ + store.surface(definition.surfaces[0])?, + store.surface(definition.surfaces[1])?, + ]; + let mut controls = Vec::with_capacity(accepted.len() * 3 + 1); + let mut pcurve_a = Vec::with_capacity(accepted.len() * 3 + 1); + let mut pcurve_b = Vec::with_capacity(accepted.len() * 3 + 1); + for (index, range) in accepted.iter().enumerate() { + let left = definition.evaluate(range.lo, store)?; + let right = definition.evaluate(range.hi, store)?; + let xyz = line_controls(left.point, right.point); + let uv_a = line_controls(left.uv_a, right.uv_a); + let uv_b = line_controls(left.uv_b, right.uv_b); + for sample in 0..=4 { + let fraction = sample as f64 / 4.0; + let t = range.lo + range.width() * fraction; + let authoritative = definition.evaluate(t, store)?; + let fitted: Point3 = std::array::from_fn(|axis| { + left.point[axis] * (1.0 - fraction) + right.point[axis] * fraction + }); + if norm(sub(authoritative.point, fitted)) > tolerance { + return Err(GeometryError::UnresolvedIntersection( + "export curve sample exceeds its certified fitting budget".into(), + )); + } + for (surface, (from, to)) in supports + .into_iter() + .zip([(left.uv_a, right.uv_a), (left.uv_b, right.uv_b)]) + { + let pcurve: UV = + std::array::from_fn(|axis| from[axis] * (1.0 - fraction) + to[axis] * fraction); + if norm(sub(surface.point_at(pcurve)?, fitted)) + > tolerance + definition.residual_tolerance + { + return Err(GeometryError::UnresolvedIntersection( + "export pcurve sample misses the fitted spatial curve".into(), + )); + } + let uv = surface.project(fitted, None)?; + if norm(sub(surface.point_at(uv)?, fitted)) + > tolerance + definition.residual_tolerance + { + return Err(GeometryError::UnresolvedIntersection( + "export curve sample exceeds a retained support budget".into(), + )); + } + } + } + let start = usize::from(index > 0); + controls.extend_from_slice(&xyz[start..]); + pcurve_a.extend_from_slice(&uv_a[start..]); + pcurve_b.extend_from_slice(&uv_b[start..]); + } + Ok(FittedControls { + controls, + pcurve_a, + pcurve_b, + }) +} + +fn line_controls(a: [f64; N], b: [f64; N]) -> [[f64; N]; 4] { + [ + a, + std::array::from_fn(|axis| (2.0 * a[axis] + b[axis]) / 3.0), + std::array::from_fn(|axis| (a[axis] + 2.0 * b[axis]) / 3.0), + b, + ] +} + +impl FittedIntersectionCurve { + pub(super) fn pcurve(&self, side: IntersectionSide) -> &[[f64; 2]] { + if side == IntersectionSide::A { + &self.pcurve_a + } else { + &self.pcurve_b + } + } +} diff --git a/main/opengeometry/src/exchange/fit_curve/tests.rs b/main/opengeometry/src/exchange/fit_curve/tests.rs new file mode 100644 index 0000000..5d6f441 --- /dev/null +++ b/main/opengeometry/src/exchange/fit_curve/tests.rs @@ -0,0 +1,48 @@ +use super::*; +use crate::brep::{Accuracy, CurveGeometry, Frame3, SsiBudget, SurfaceGeometry}; +use crate::intersection::intersect_patches; + +#[test] +fn fitted_numerical_curve_has_cubic_shape_and_support_bound() { + let mut store = GeometryStore::new(); + store.surfaces = vec![ + SurfaceGeometry::Torus { + frame: Frame3::IDENTITY, + major_radius: 2.0, + minor_radius: 0.5, + }, + SurfaceGeometry::Plane { + frame: Frame3::from_axis([0.0, 0.0, 0.15], [0.35, -0.2, 1.0], [1.0, 0.0, 0.0]).unwrap(), + }, + ]; + let tau = std::f64::consts::TAU; + let result = intersect_patches( + &mut store, + [0, 1], + [ + [Interval::new(0.0, tau).unwrap(); 2], + [Interval::new(-4.0, 4.0).unwrap(); 2], + ], + Accuracy { + geometric: 1e-5, + intersection: 2.5e-6, + tessellation: 1e-3, + exchange: 0.05, + }, + SsiBudget::default(), + ) + .unwrap(); + let definition = match store.curves[result.curves[0].curve as usize] { + CurveGeometry::Intersection { definition } => definition, + _ => panic!("universal SSI must create an intersection definition"), + }; + let fitted = fit_intersection_curve(&store, definition, 1.0, 20_000).unwrap(); + assert!(fitted.controls.len() >= 4); + assert_eq!((fitted.controls.len() - 1) % 3, 0); + assert_eq!(fitted.controls.len(), fitted.pcurve_a.len()); + assert_eq!(fitted.controls.len(), fitted.pcurve_b.len()); + assert!(fitted.error_bound <= 1.0); + assert_eq!(fitted.knots.len(), fitted.multiplicities.len()); + assert_eq!(fitted.multiplicities.first(), Some(&4)); + assert_eq!(fitted.multiplicities.last(), Some(&4)); +} diff --git a/main/opengeometry/src/exchange/mod.rs b/main/opengeometry/src/exchange/mod.rs new file mode 100644 index 0000000..1178f92 --- /dev/null +++ b/main/opengeometry/src/exchange/mod.rs @@ -0,0 +1,7 @@ +mod fit_curve; +mod part21; +mod preflight; +mod step; + +pub(super) use step::{export_bodies, StepBodyInput, StepExportFailure}; +pub use step::{export_step, StepBodyReport, StepExportReport, StepReport, StepSkipped}; diff --git a/main/opengeometry/src/exchange/part21/mod.rs b/main/opengeometry/src/exchange/part21/mod.rs new file mode 100644 index 0000000..aa02d49 --- /dev/null +++ b/main/opengeometry/src/exchange/part21/mod.rs @@ -0,0 +1,186 @@ +#[cfg(test)] +mod tests; + +use crate::brep::GeometryError; +use std::collections::HashSet; + +const DEFAULT_PART21_VERSION: &str = "2;1"; + +#[derive(Clone, Debug)] +pub(super) struct Part21Writer { + schema: String, + description: String, + file_name: String, + timestamp: String, + author: String, + organization: String, + preprocessor: String, + originating_system: String, + authorization: String, + next_id: usize, + entries: Vec<(usize, String)>, +} + +impl Part21Writer { + pub(super) fn new(schema: impl Into) -> Self { + Self { + schema: sanitize_string_literal(&schema.into()), + description: "OpenGeometry Export".to_string(), + file_name: "opengeometry-export".to_string(), + timestamp: "1970-01-01T00:00:00".to_string(), + author: "OpenGeometry".to_string(), + organization: "OpenGeometry".to_string(), + preprocessor: "OpenGeometry".to_string(), + originating_system: "OpenGeometry".to_string(), + authorization: String::new(), + next_id: 1, + entries: Vec::new(), + } + } + + pub(super) fn set_description(&mut self, description: impl Into) { + self.description = sanitize_string_literal(&description.into()); + } + + pub(super) fn set_file_name(&mut self, file_name: impl Into) { + self.file_name = sanitize_string_literal(&file_name.into()); + } + + pub(super) fn set_timestamp(&mut self, timestamp: impl Into) { + self.timestamp = sanitize_string_literal(×tamp.into()); + } + + pub(super) fn add_entity(&mut self, expression: impl Into) -> usize { + let id = self.next_id; + self.next_id += 1; + self.entries.push((id, expression.into())); + id + } + + pub(super) fn entity_count(&self) -> usize { + self.entries.len() + } + + #[cfg(test)] + fn reference(id: usize) -> String { + format!("#{id}") + } + + pub(super) fn build(self) -> Result { + if self.entries.is_empty() { + return Err(GeometryError::InvalidGeometry( + "Part-21 writer has no DATA entities".to_string(), + )); + } + + let mut defined = HashSet::new(); + for (id, _) in &self.entries { + if !defined.insert(*id) { + return Err(GeometryError::InvalidGeometry(format!( + "Duplicate Part-21 entity id detected: #{id}" + ))); + } + } + + for (id, expression) in &self.entries { + for reference in extract_references(expression) { + if !defined.contains(&reference) { + return Err(GeometryError::InvalidGeometry(format!( + "Part-21 entity #{id} references undefined id #{reference}" + ))); + } + } + } + + let mut output = String::new(); + output.push_str("ISO-10303-21;\n"); + output.push_str("HEADER;\n"); + output.push_str(&format!( + "FILE_DESCRIPTION(('{}'),'{}');\n", + self.description, DEFAULT_PART21_VERSION + )); + output.push_str(&format!( + "FILE_NAME('{}','{}',('{}'),('{}'),'{}','{}','{}');\n", + self.file_name, + self.timestamp, + self.author, + self.organization, + self.preprocessor, + self.originating_system, + self.authorization + )); + output.push_str(&format!("FILE_SCHEMA(('{}'));\n", self.schema)); + output.push_str("ENDSEC;\n"); + output.push_str("DATA;\n"); + + for (id, expression) in &self.entries { + output.push_str(&format!("#{id}={expression};\n")); + } + + output.push_str("ENDSEC;\n"); + output.push_str("END-ISO-10303-21;\n"); + Ok(output) + } +} + +pub(super) fn sanitize_string_literal(value: &str) -> String { + let mut sanitized = String::with_capacity(value.len()); + for ch in value.chars() { + match ch { + '\'' => sanitized.push_str("''"), + '\\' => sanitized.push_str("\\\\"), + '\n' | '\r' => sanitized.push(' '), + '\u{20}'..='\u{7e}' => sanitized.push(ch), + _ => { + sanitized.push_str("\\X2\\"); + for code_unit in ch.encode_utf16(&mut [0; 2]).iter() { + sanitized.push_str(&format!("{code_unit:04X}")); + } + sanitized.push_str("\\X0\\"); + } + } + } + sanitized +} + +fn extract_references(expression: &str) -> Vec { + let bytes = expression.as_bytes(); + let mut refs = Vec::new(); + let mut idx = 0; + let mut quoted = false; + + while idx < bytes.len() { + if bytes[idx] == b'\'' { + if quoted && bytes.get(idx + 1) == Some(&b'\'') { + idx += 2; + continue; + } + quoted = !quoted; + idx += 1; + continue; + } + if quoted { + idx += 1; + continue; + } + if bytes[idx] != b'#' { + idx += 1; + continue; + } + + idx += 1; + let start = idx; + while idx < bytes.len() && bytes[idx].is_ascii_digit() { + idx += 1; + } + + if idx > start { + let raw = &expression[start..idx]; + if let Ok(id) = raw.parse::() { + refs.push(id); + } + } + } + + refs +} diff --git a/main/opengeometry/src/exchange/part21/tests.rs b/main/opengeometry/src/exchange/part21/tests.rs new file mode 100644 index 0000000..845c005 --- /dev/null +++ b/main/opengeometry/src/exchange/part21/tests.rs @@ -0,0 +1,51 @@ +use super::*; + +#[test] +fn builds_deterministic_part21_document() { + let mut writer = Part21Writer::new("AUTOMOTIVE_DESIGN"); + writer.set_file_name("test"); + let p1 = writer.add_entity("CARTESIAN_POINT('',(0.,0.,0.))"); + let p2 = writer.add_entity("CARTESIAN_POINT('',(1.,0.,0.))"); + writer.add_entity(format!( + "POLY_LOOP('',({},{},{}))", + Part21Writer::reference(p1), + Part21Writer::reference(p2), + Part21Writer::reference(p1) + )); + + let text = writer.build().expect("part21 should build"); + assert!(text.starts_with("ISO-10303-21;")); + assert!(text.contains("FILE_SCHEMA(('AUTOMOTIVE_DESIGN'));")); + assert!(text.contains("#1=CARTESIAN_POINT")); + assert!(text.contains("#2=CARTESIAN_POINT")); + assert!(text.ends_with("END-ISO-10303-21;\n")); +} + +#[test] +fn fails_for_unresolved_reference() { + let mut writer = Part21Writer::new("IFC4"); + writer.add_entity("IFCPROJECT('x',#999,$,$,$,$,$,$)"); + let err = writer + .build() + .expect_err("writer should reject unresolved refs"); + assert!(err.to_string().contains("undefined id #999")); +} + +#[test] +fn sanitizes_non_ascii_literals() { + let raw = "A'B\nCø"; + let sanitized = sanitize_string_literal(raw); + assert_eq!(sanitized, "A''B C\\X2\\00F8\\X0\\"); +} + +#[test] +fn ignores_reference_like_text_inside_escaped_literals() { + let mut writer = Part21Writer::new("AUTOMOTIVE_DESIGN"); + let point = writer.add_entity("CARTESIAN_POINT('body #999''s point',(0.,0.,0.))"); + writer.add_entity(format!("VERTEX_POINT('#888',#{point})")); + assert!(writer.build().is_ok()); + assert_eq!( + extract_references("X('a #999''b',#12,'#88',#3)"), + vec![12, 3] + ); +} diff --git a/main/opengeometry/src/exchange/preflight.rs b/main/opengeometry/src/exchange/preflight.rs new file mode 100644 index 0000000..97fa7bb --- /dev/null +++ b/main/opengeometry/src/exchange/preflight.rs @@ -0,0 +1,422 @@ +use crate::brep::{ + unit, Accuracy, BrepEnvelope, CurveGeometry, Edge, EdgeGeometry, Face, FaceProvenance, + FaceSource, Frame3, GeometryError, GeometryQuality, GeometryStore, HalfEdge, + HalfEdgeGeometryUse, IntersectionDefinition, Loop, PatchBounds, PcurveGeometry, Shell, + SolidRegion, SurfaceGeometry, Topology, TraceAnchor, TrimRegion, Vertex, Wire, +}; +use crate::math::{cross, dot, norm, sub, Interval}; + +pub(super) fn preflight(brep: &BrepEnvelope, scale: f64) -> Result { + brep.validate()?; + if brep.solids.is_empty() + || !brep.topology.wires.is_empty() + || brep.topology.shells.iter().any(|shell| !shell.is_closed) + { + return Err(GeometryError::UnsupportedGeometry( + "analytic analytic exchange export requires nonempty closed solid regions without wires".into(), + )); + } + check_item_count(brep)?; + if brep.id.len() > 4096 || brep.topology.faces.iter().any(|face| face.key.len() > 4096) { + return Err(GeometryError::LimitExceeded( + "analytic exchange body and face labels must be at most 4 KiB".into(), + )); + } + let bounds = brep.bounds()?.ok_or_else(|| { + GeometryError::InvalidTopology("analytic exchange solid has no bounds".into()) + })?; + let mut magnitude = bounds + .axes + .iter() + .flat_map(|axis| [axis.lo.abs(), axis.hi.abs()]) + .fold(0.0_f64, f64::max); + let mut frame_bound = 0.0_f64; + for face in &brep.topology.faces { + frame_bound = frame_bound.max(frame_error( + *brep.geometry.surface(face.surface)?.frame(), + bounds, + )?); + magnitude = brep + .geometry + .surface(face.surface)? + .frame() + .origin + .iter() + .fold(magnitude, |m, v| m.max(v.abs())); + } + for edge in &brep.topology.edges { + if let EdgeGeometry::Curve { curve, .. } = edge.geometry { + let origin = match &brep.geometry.curves[curve as usize] { + CurveGeometry::Line { origin, .. } => origin, + CurveGeometry::Circle { frame, .. } | CurveGeometry::Ellipse { frame, .. } => { + &frame.origin + } + CurveGeometry::Intersection { .. } => continue, + }; + magnitude = origin.iter().fold(magnitude, |m, v| m.max(v.abs())); + if let CurveGeometry::Circle { frame, .. } | CurveGeometry::Ellipse { frame, .. } = + &brep.geometry.curves[curve as usize] + { + frame_bound = frame_bound.max(frame_error(*frame, bounds)?); + } + } + } + let exchange_bound = + brep.accuracy.geometric + 2.0 * frame_bound + 64.0 * f64::EPSILON * magnitude; + if !exchange_bound.is_finite() + || exchange_bound > brep.accuracy.exchange + || !(exchange_bound * scale).is_finite() + { + return Err(GeometryError::LimitExceeded( + "analytic exchange exchange budget is below geometric tolerance or coordinate precision".into(), + )); + } + if !scalable(brep, scale) { + return Err(GeometryError::LimitExceeded( + "analytic exchange unit conversion exceeds floating-point range".into(), + )); + } + Ok(exchange_bound) +} + +fn check_item_count(brep: &BrepEnvelope) -> Result<(), GeometryError> { + if brep.topology.vertices.len() + + brep.topology.halfedges.len() + + brep.topology.faces.len() + + brep.geometry.curves.len() + + brep.geometry.surfaces.len() + + brep.geometry.pcurves.len() + + brep.geometry.intersections.len() + + brep.topology.edges.len() + + brep.topology.loops.len() + + brep.topology.shells.len() + + brep.solids.len() + > 100_000 + { + return Err(GeometryError::LimitExceeded( + "analytic exchange body exceeds 100,000 geometry/topology items".into(), + )); + } + Ok(()) +} + +fn frame_error(frame: Frame3, bounds: PatchBounds) -> Result { + let z = unit(frame.z)?; + let x = unit(sub(frame.x, z.map(|v| v * dot(frame.x, z))))?; + let y = cross(z, x); + let deviation = norm(sub(x, frame.x)) + .max(norm(sub(y, frame.y))) + .max(norm(sub(z, frame.z))); + if deviation == 0.0 { + return Ok(0.0); + } + let distance = norm(std::array::from_fn(|i| { + (bounds.axes[i].lo - frame.origin[i]) + .abs() + .max((bounds.axes[i].hi - frame.origin[i]).abs()) + })); + Ok(4.0 * distance * deviation) +} + +fn scalable(brep: &BrepEnvelope, scale: f64) -> bool { + brep_numbers(brep) + .into_iter() + .filter(|value| value.is_finite()) + .all(|value| (value * scale).is_finite()) +} + +fn brep_numbers(brep: &BrepEnvelope) -> Vec { + let BrepEnvelope { + schema_version, + id: _, + revision, + geometry, + topology, + solids, + accuracy, + quality, + } = brep; + let Accuracy { + geometric, + intersection, + tessellation, + exchange, + } = accuracy; + let mut numbers = vec![f64::from(*schema_version), *revision as f64]; + numbers.extend([geometric, intersection, tessellation, exchange]); + match quality { + GeometryQuality::Analytic => {} + GeometryQuality::Approximate { max_error } => numbers.push(*max_error), + } + geometry_numbers(geometry, &mut numbers); + topology_numbers(topology, &mut numbers); + for SolidRegion { + outer_shell, + cavity_shells, + } in solids + { + index_numbers([outer_shell].into_iter().chain(cavity_shells), &mut numbers); + } + numbers +} + +fn geometry_numbers(geometry: &GeometryStore, numbers: &mut Vec) { + let GeometryStore { + surfaces, + curves, + pcurves, + intersections, + } = geometry; + for surface in surfaces { + surface_numbers(surface, numbers); + } + for curve in curves { + curve_numbers(curve, numbers); + } + for pcurve in pcurves { + pcurve_numbers(pcurve, numbers); + } + for IntersectionDefinition { + surfaces: pair, + anchors, + uv_tubes, + residual_tolerance, + } in intersections + { + index_numbers(pair, numbers); + for TraceAnchor { + parameter, + point, + uv_a, + uv_b, + } in anchors + { + numbers.extend([parameter].into_iter().chain(point).chain(uv_a).chain(uv_b)); + } + for interval in uv_tubes.iter().flatten() { + interval_numbers(*interval, numbers); + } + numbers.push(*residual_tolerance); + } +} + +fn surface_numbers(surface: &SurfaceGeometry, numbers: &mut Vec) { + match surface { + SurfaceGeometry::Plane { frame } => frame_numbers(*frame, numbers), + SurfaceGeometry::Sphere { frame, radius } | SurfaceGeometry::Cylinder { frame, radius } => { + frame_numbers(*frame, numbers); + numbers.push(*radius); + } + SurfaceGeometry::Cone { frame, semi_angle } => { + frame_numbers(*frame, numbers); + numbers.push(*semi_angle); + } + SurfaceGeometry::Torus { + frame, + major_radius, + minor_radius, + } => { + frame_numbers(*frame, numbers); + numbers.extend([major_radius, minor_radius]); + } + } +} + +fn frame_numbers(frame: Frame3, numbers: &mut Vec) { + let Frame3 { origin, x, y, z } = frame; + numbers.extend(origin.into_iter().chain(x).chain(y).chain(z)); +} + +fn curve_numbers(curve: &CurveGeometry, numbers: &mut Vec) { + match curve { + CurveGeometry::Line { origin, direction } => numbers.extend(origin.iter().chain(direction)), + CurveGeometry::Circle { frame, radius } => { + frame_numbers(*frame, numbers); + numbers.push(*radius); + } + CurveGeometry::Ellipse { + frame, + major_radius, + minor_radius, + } => { + frame_numbers(*frame, numbers); + numbers.extend([major_radius, minor_radius]); + } + CurveGeometry::Intersection { definition } => index_numbers([definition], numbers), + } +} + +fn index_numbers<'a>(indices: impl IntoIterator, numbers: &mut Vec) { + numbers.extend(indices.into_iter().map(|&index| f64::from(index))); +} + +fn pcurve_numbers(pcurve: &PcurveGeometry, numbers: &mut Vec) { + match pcurve { + PcurveGeometry::Line2 { origin, direction } => { + numbers.extend(origin.iter().chain(direction)); + } + PcurveGeometry::Conic2 { + origin, + axis_a, + axis_b, + } => numbers.extend(origin.iter().chain(axis_a).chain(axis_b)), + PcurveGeometry::ProjectedCurve { + curve, + surface, + chart, + uv_hint, + uv_rate, + parameter_origin, + } => { + index_numbers([curve, surface, chart], numbers); + numbers.extend(uv_hint.iter().chain(uv_rate).chain([parameter_origin])); + } + PcurveGeometry::IntersectionSide { + definition, + side: _, + } => index_numbers([definition], numbers), + } +} + +fn interval_numbers(interval: Interval, numbers: &mut Vec) { + let Interval { lo, hi } = interval; + numbers.extend([lo, hi]); +} + +fn topology_numbers(topology: &Topology, numbers: &mut Vec) { + let Topology { + vertices, + edges, + halfedges, + loops, + faces, + wires, + shells, + } = topology; + for Vertex { + id, + position, + outgoing_halfedge, + tolerance, + } in vertices + { + index_numbers([id].into_iter().chain(outgoing_halfedge), numbers); + numbers.extend(position.iter().chain([tolerance])); + } + for edge in edges { + edge_numbers(edge, numbers); + } + for halfedge in halfedges { + halfedge_numbers(halfedge, numbers); + } + for Loop { + id, + start_halfedge, + face_ref, + is_hole: _, + } in loops + { + index_numbers([id, start_halfedge, face_ref], numbers); + } + for face in faces { + face_numbers(face, numbers); + } + for Wire { + id, + start_halfedge, + is_closed: _, + } in wires + { + index_numbers([id, start_halfedge], numbers); + } + for Shell { + id, + faces: shell_faces, + is_closed: _, + } in shells + { + index_numbers([id].into_iter().chain(shell_faces), numbers); + } +} + +fn edge_numbers(edge: &Edge, numbers: &mut Vec) { + let Edge { + id, + geometry, + halfedge, + twin_halfedge, + tolerance, + chart_seam: _, + } = edge; + index_numbers([id, halfedge].into_iter().chain(twin_halfedge), numbers); + match geometry { + EdgeGeometry::Curve { curve, range } => { + index_numbers([curve], numbers); + interval_numbers(*range, numbers); + } + EdgeGeometry::Collapsed { vertex } => index_numbers([vertex], numbers), + } + numbers.push(*tolerance); +} + +fn halfedge_numbers(halfedge: &HalfEdge, numbers: &mut Vec) { + let HalfEdge { + id, + from, + to, + twin, + next, + prev, + edge, + face, + loop_ref, + wire_ref, + geometry_use, + } = halfedge; + let HalfEdgeGeometryUse { + sense: _, + pcurve, + periodic_lift, + } = geometry_use; + index_numbers([id, from, to, edge], numbers); + let links = [twin, next, prev, face, loop_ref, wire_ref, pcurve]; + index_numbers(links.into_iter().flatten(), numbers); + numbers.extend(periodic_lift.map(f64::from)); +} + +fn face_numbers(face: &Face, numbers: &mut Vec) { + let Face { + id, + key: _, + surface, + sense: _, + trim, + shell_ref, + provenance, + } = face; + let TrimRegion { + chart, + uv_bounds, + outer, + holes, + } = trim; + let FaceProvenance { + sources, + role: _, + reversed: _, + } = provenance; + index_numbers([id, surface, chart, outer], numbers); + for interval in uv_bounds { + interval_numbers(*interval, numbers); + } + index_numbers(holes.iter().chain(shell_ref), numbers); + for FaceSource { + entity: _, + body: _, + key: _, + face: source_face, + } in sources + { + index_numbers([source_face], numbers); + } +} diff --git a/main/opengeometry/src/exchange/step/body.rs b/main/opengeometry/src/exchange/step/body.rs new file mode 100644 index 0000000..9166646 --- /dev/null +++ b/main/opengeometry/src/exchange/step/body.rs @@ -0,0 +1,422 @@ +use super::entities::{ + bspline_curve, curve, pcurve, placement, point, real, refs, sense, PcurveOnSurface, +}; +use crate::brep::{ + BrepEnvelope, CurveGeometry, Edge, EdgeGeometry, GeometryError, Orientation, PcurveGeometry, + SurfaceGeometry, +}; +use crate::exchange::fit_curve::{fit_intersection_curve, FittedIntersectionCurve}; +use crate::exchange::part21::{sanitize_string_literal, Part21Writer}; + +pub(super) struct BodyEmission { + pub(super) solids: Vec, + pub(super) faces: usize, + pub(super) edges: usize, + pub(super) cavity_shells: usize, + pub(super) collapsed_chart_uses: usize, + pub(super) fitted_curves: usize, + pub(super) pcurveless_edges: usize, + pub(super) exchange_bound: f64, +} + +struct BodyEntities<'a> { + brep: &'a BrepEnvelope, + scale: f64, + context2: usize, + surfaces: &'a [Option], + vertices: &'a [usize], +} + +struct EdgeEmission { + edges: Vec>, + fitted_curves: Vec>, + fitting_error: f64, + pcurveless_edges: usize, +} + +pub(super) fn emit_body( + writer: &mut Part21Writer, + brep: &BrepEnvelope, + scale: f64, + context2: usize, + mut exchange_bound: f64, +) -> Result { + let fitting_budget = brep.accuracy.exchange - exchange_bound; + let surfaces = add_surfaces(writer, brep, scale)?; + let vertices = add_vertices(writer, brep, scale); + let body_entities = BodyEntities { + brep, + scale, + context2, + surfaces: &surfaces, + vertices: &vertices, + }; + let EdgeEmission { + edges, + fitted_curves, + fitting_error, + pcurveless_edges, + } = add_edges(writer, &body_entities, fitting_budget)?; + let (faces, collapsed_chart_uses) = add_faces(writer, brep, &surfaces, &vertices, &edges)?; + exchange_bound += fitting_error; + if exchange_bound > brep.accuracy.exchange { + return Err(GeometryError::LimitExceeded( + "STEP fitted curves exceed the exchange error budget".into(), + )); + } + let shells = add_shells(writer, brep, &faces); + let solids = add_solids(writer, brep, &shells); + Ok(BodyEmission { + faces: faces.len(), + edges: edges.iter().flatten().count(), + cavity_shells: brep + .solids + .iter() + .map(|solid| solid.cavity_shells.len()) + .sum(), + collapsed_chart_uses, + fitted_curves: fitted_curves.iter().flatten().count(), + pcurveless_edges, + solids, + exchange_bound, + }) +} + +fn add_surfaces( + writer: &mut Part21Writer, + brep: &BrepEnvelope, + scale: f64, +) -> Result>, GeometryError> { + let mut surfaces = vec![None; brep.geometry.surfaces.len()]; + for face in &brep.topology.faces { + if surfaces[face.surface as usize].is_some() { + continue; + } + let surface = brep.geometry.surface(face.surface)?; + let frame = placement(writer, *surface.frame(), scale); + let expression = match surface { + SurfaceGeometry::Plane { .. } => format!("PLANE('',#{frame})"), + SurfaceGeometry::Sphere { radius, .. } => { + format!("SPHERICAL_SURFACE('',#{frame},{})", real(radius * scale)) + } + SurfaceGeometry::Cylinder { radius, .. } => { + format!("CYLINDRICAL_SURFACE('',#{frame},{})", real(radius * scale)) + } + SurfaceGeometry::Cone { semi_angle, .. } => { + format!("CONICAL_SURFACE('',#{frame},0.,{})", real(*semi_angle)) + } + SurfaceGeometry::Torus { + major_radius, + minor_radius, + .. + } => format!( + "TOROIDAL_SURFACE('',#{frame},{},{})", + real(major_radius * scale), + real(minor_radius * scale) + ), + }; + surfaces[face.surface as usize] = Some(writer.add_entity(expression)); + } + Ok(surfaces) +} + +fn add_vertices(writer: &mut Part21Writer, brep: &BrepEnvelope, scale: f64) -> Vec { + brep.topology + .vertices + .iter() + .map(|v| { + let p = point(writer, &v.position.map(|v| v * scale)); + writer.add_entity(format!("VERTEX_POINT('',#{p})")) + }) + .collect() +} + +fn add_edges( + writer: &mut Part21Writer, + body: &BodyEntities<'_>, + fitting_budget: f64, +) -> Result { + let BodyEntities { brep, scale, .. } = *body; + let mut edges = vec![None; brep.topology.edges.len()]; + let mut curves = vec![None; brep.geometry.curves.len()]; + let mut fitted_curves = vec![None; brep.geometry.curves.len()]; + let mut fitting_error: f64 = 0.0; + let mut pcurveless_edges = 0; + for edge in &brep.topology.edges { + let EdgeGeometry::Curve { + curve: curve_id, + range, + } = edge.geometry + else { + continue; + }; + let geometry = &brep.geometry.curves[curve_id as usize]; + if matches!( + geometry, + CurveGeometry::Circle { .. } | CurveGeometry::Ellipse { .. } + ) && range.hi - range.lo > std::f64::consts::TAU + { + return Err(GeometryError::UnsupportedGeometry( + "STEP edge spans more than one conic period".into(), + )); + } + let geometry_id = match curves[curve_id as usize] { + Some(id) => id, + None => { + let id = match geometry { + CurveGeometry::Intersection { definition } => { + let (id, fitted) = add_fitted_curve( + writer, + brep, + *definition, + fitting_budget, + scale, + &mut fitting_error, + )?; + fitted_curves[curve_id as usize] = Some(fitted); + id + } + _ => curve(writer, geometry, scale)?, + }; + curves[curve_id as usize] = Some(id); + id + } + }; + edges[edge.id as usize] = Some(add_edge_curve( + writer, + body, + edge, + curve_id, + geometry_id, + &fitted_curves, + &mut pcurveless_edges, + )?); + } + Ok(EdgeEmission { + edges, + fitted_curves, + fitting_error, + pcurveless_edges, + }) +} + +fn add_fitted_curve( + writer: &mut Part21Writer, + brep: &BrepEnvelope, + definition: u32, + fitting_budget: f64, + scale: f64, + fitting_error: &mut f64, +) -> Result<(usize, FittedIntersectionCurve), GeometryError> { + if fitting_budget <= 0.0 { + return Err(GeometryError::LimitExceeded( + "STEP exchange budget leaves no room for numerical curve fitting".into(), + )); + } + let fitted = fit_intersection_curve(&brep.geometry, definition, fitting_budget, 20_000)?; + *fitting_error = fitting_error.max(fitted.error_bound); + let controls = fitted + .controls + .iter() + .map(|control| control.map(|value| value * scale).to_vec()) + .collect::>(); + let id = bspline_curve(writer, &controls, &fitted.knots, &fitted.multiplicities)?; + Ok((id, fitted)) +} + +fn add_edge_curve( + writer: &mut Part21Writer, + body: &BodyEntities<'_>, + edge: &Edge, + curve_id: u32, + geometry_id: usize, + fitted_curves: &[Option], + pcurveless_edges: &mut usize, +) -> Result { + let BodyEntities { + brep, + scale, + context2, + surfaces, + vertices, + } = *body; + let uses = [Some(edge.halfedge), edge.twin_halfedge] + .into_iter() + .flatten() + .collect::>(); + let projected = any_projected_pcurve(brep, &uses)?; + if projected && edge.chart_seam { + return Err(GeometryError::UnsupportedGeometry( + "STEP seam edge has a projected pcurve".into(), + )); + } + let mut pcurves = Vec::new(); + for use_id in uses.into_iter().filter(|_| !projected) { + let use_ = &brep.topology.halfedges[use_id as usize]; + let face = &brep.topology.faces[use_.face.ok_or_else(|| { + GeometryError::InvalidTopology("STEP solid edge lacks a face use".into()) + })? as usize]; + let pc = use_ + .geometry_use + .pcurve + .ok_or_else(|| GeometryError::InvalidTopology("STEP face use lacks a pcurve".into()))?; + let surface_id = surfaces[face.surface as usize] + .ok_or_else(|| GeometryError::InvalidTopology("STEP surface was not emitted".into()))?; + pcurves.push(pcurve( + writer, + &PcurveOnSurface { + geometry: &brep.geometry.pcurves[pc as usize], + surface: brep.geometry.surface(face.surface)?, + surface_id, + }, + context2, + scale, + use_.geometry_use.periodic_lift, + fitted_curves[curve_id as usize].as_ref(), + )?); + } + let geometry_id = if projected { + *pcurveless_edges += 1; + geometry_id + } else { + let kind = if edge.chart_seam { + "SEAM_CURVE" + } else { + "SURFACE_CURVE" + }; + writer.add_entity(format!( + "{kind}('',#{geometry_id},({}),.CURVE_3D.)", + refs(&pcurves) + )) + }; + let use_ = &brep.topology.halfedges[edge.halfedge as usize]; + let (from, to) = if use_.geometry_use.sense == Orientation::Forward { + (use_.from, use_.to) + } else { + (use_.to, use_.from) + }; + Ok(writer.add_entity(format!( + "EDGE_CURVE('',#{},#{},#{geometry_id},.T.)", + vertices[from as usize], vertices[to as usize] + ))) +} + +fn any_projected_pcurve(brep: &BrepEnvelope, uses: &[u32]) -> Result { + let mut projected = false; + for use_id in uses { + let use_ = &brep.topology.halfedges[*use_id as usize]; + let pc = use_ + .geometry_use + .pcurve + .ok_or_else(|| GeometryError::InvalidTopology("STEP face use lacks a pcurve".into()))?; + projected |= matches!( + brep.geometry.pcurves[pc as usize], + PcurveGeometry::ProjectedCurve { .. } + ); + } + Ok(projected) +} + +fn add_faces( + writer: &mut Part21Writer, + brep: &BrepEnvelope, + surfaces: &[Option], + vertices: &[usize], + edges: &[Option], +) -> Result<(Vec, usize), GeometryError> { + let mut faces = Vec::new(); + let mut collapsed_chart_uses = 0; + for face in &brep.topology.faces { + let mut bounds = Vec::new(); + for loop_id in std::iter::once(&face.trim.outer).chain(&face.trim.holes) { + let loop_ = &brep.topology.loops[*loop_id as usize]; + let start = loop_.start_halfedge; + let mut current = start; + let mut oriented = Vec::new(); + for _ in 0..=brep.topology.halfedges.len() { + let use_ = &brep.topology.halfedges[current as usize]; + if let Some(edge) = edges[use_.edge as usize] { + oriented.push(writer.add_entity(format!( + "ORIENTED_EDGE('',*,*,#{edge},{})", + sense(use_.geometry_use.sense) + ))); + } else { + collapsed_chart_uses += 1; + } + current = use_.next.ok_or_else(|| { + GeometryError::InvalidTopology("STEP loop has no next use".into()) + })?; + if current == start { + break; + } + } + let loop_geometry = if oriented.is_empty() { + writer.add_entity(format!( + "VERTEX_LOOP('',#{})", + vertices[brep.topology.halfedges[start as usize].from as usize] + )) + } else { + writer.add_entity(format!("EDGE_LOOP('',({}))", refs(&oriented))) + }; + let kind = if loop_.is_hole { + "FACE_BOUND" + } else { + "FACE_OUTER_BOUND" + }; + bounds.push(writer.add_entity(format!("{kind}('',#{loop_geometry},.T.)"))); + } + let surface = surfaces[face.surface as usize] + .ok_or_else(|| GeometryError::InvalidTopology("STEP face lacks its support".into()))?; + faces.push(writer.add_entity(format!( + "ADVANCED_FACE('{}',({}),#{surface},{})", + sanitize_string_literal(&face.key), + refs(&bounds), + sense(face.sense) + ))); + } + Ok((faces, collapsed_chart_uses)) +} + +fn add_shells(writer: &mut Part21Writer, brep: &BrepEnvelope, faces: &[usize]) -> Vec { + brep.topology + .shells + .iter() + .map(|shell| { + writer.add_entity(format!( + "CLOSED_SHELL('',({}))", + refs( + &shell + .faces + .iter() + .map(|id| faces[*id as usize]) + .collect::>() + ) + )) + }) + .collect() +} + +fn add_solids(writer: &mut Part21Writer, brep: &BrepEnvelope, shells: &[usize]) -> Vec { + let mut solids = Vec::new(); + for (i, solid) in brep.solids.iter().enumerate() { + let outer = shells[solid.outer_shell as usize]; + let name = sanitize_string_literal(&format!("{}-{i}", brep.id)); + let expression = if solid.cavity_shells.is_empty() { + format!("MANIFOLD_SOLID_BREP('{name}',#{outer})") + } else { + let voids: Vec<_> = solid + .cavity_shells + .iter() + .map(|id| { + writer.add_entity(format!( + "ORIENTED_CLOSED_SHELL('',*,#{},.T.)", + shells[*id as usize] + )) + }) + .collect(); + format!("BREP_WITH_VOIDS('{name}',#{outer},({}))", refs(&voids)) + }; + solids.push(writer.add_entity(expression)); + } + solids +} diff --git a/main/opengeometry/src/exchange/step/entities.rs b/main/opengeometry/src/exchange/step/entities.rs new file mode 100644 index 0000000..b8d863b --- /dev/null +++ b/main/opengeometry/src/exchange/step/entities.rs @@ -0,0 +1,237 @@ +use crate::brep::{ + CurveGeometry, Frame3, GeometryError, Orientation, PcurveGeometry, SurfaceGeometry, +}; +use crate::exchange::fit_curve::FittedIntersectionCurve; +use crate::exchange::part21::Part21Writer; +use crate::math::{dot, norm}; + +pub(super) struct PcurveOnSurface<'a> { + pub(super) geometry: &'a PcurveGeometry, + pub(super) surface: &'a SurfaceGeometry, + pub(super) surface_id: usize, +} + +pub(super) fn sense(value: Orientation) -> &'static str { + if value == Orientation::Forward { + ".T." + } else { + ".F." + } +} + +pub(super) fn curve( + writer: &mut Part21Writer, + geometry: &CurveGeometry, + scale: f64, +) -> Result { + Ok(match geometry { + CurveGeometry::Line { origin, direction } => line( + writer, + &origin.map(|v| v * scale), + &direction.map(|v| v * scale), + )?, + CurveGeometry::Circle { frame, radius } => { + let p = placement(writer, *frame, scale); + writer.add_entity(format!("CIRCLE('',#{p},{})", real(radius * scale))) + } + CurveGeometry::Ellipse { + frame, + major_radius, + minor_radius, + } => { + let p = placement(writer, *frame, scale); + writer.add_entity(format!( + "ELLIPSE('',#{p},{},{})", + real(major_radius * scale), + real(minor_radius * scale) + )) + } + CurveGeometry::Intersection { .. } => { + return Err(GeometryError::UnsupportedGeometry( + "STEP numerical branches require bounded export-curve fitting".into(), + )) + } + }) +} + +fn line( + writer: &mut Part21Writer, + origin: &[f64], + velocity: &[f64], +) -> Result { + let length = velocity.iter().fold(0.0_f64, |n, v| n.hypot(*v)); + if !length.is_finite() || length == 0.0 { + return Err(GeometryError::UnsupportedGeometry( + "STEP pcurve has a singular line direction".into(), + )); + } + let p = point(writer, origin); + let d = direction( + writer, + &velocity.iter().map(|v| v / length).collect::>(), + ); + let v = writer.add_entity(format!("VECTOR('',#{d},{})", real(length))); + Ok(writer.add_entity(format!("LINE('',#{p},#{v})"))) +} + +pub(super) fn point(writer: &mut Part21Writer, value: &[f64]) -> usize { + writer.add_entity(format!( + "CARTESIAN_POINT('',({}))", + value.iter().map(|v| real(*v)).collect::>().join(",") + )) +} + +pub(super) fn real(value: f64) -> String { + format!("{:.17E}", if value == 0.0 { 0.0 } else { value }) +} + +fn direction(writer: &mut Part21Writer, value: &[f64]) -> usize { + writer.add_entity(format!( + "DIRECTION('',({}))", + value.iter().map(|v| real(*v)).collect::>().join(",") + )) +} +pub(super) fn placement(writer: &mut Part21Writer, frame: Frame3, scale: f64) -> usize { + let p = point(writer, &frame.origin.map(|v| v * scale)); + let z = direction(writer, &frame.z); + let x = direction(writer, &frame.x); + writer.add_entity(format!("AXIS2_PLACEMENT_3D('',#{p},#{z},#{x})")) +} + +pub(super) fn pcurve( + writer: &mut Part21Writer, + on_surface: &PcurveOnSurface<'_>, + context: usize, + scale: f64, + lift: [i32; 2], + fitted: Option<&FittedIntersectionCurve>, +) -> Result { + let PcurveOnSurface { + geometry, + surface, + surface_id, + } = *on_surface; + let metric = match surface { + SurfaceGeometry::Plane { .. } => [scale, scale], + SurfaceGeometry::Cylinder { .. } => [1.0, scale], + SurfaceGeometry::Cone { semi_angle, .. } => [1.0, scale / semi_angle.cos()], + _ => [1.0, 1.0], + }; + let period = match surface { + SurfaceGeometry::Plane { .. } => [0.0, 0.0], + SurfaceGeometry::Torus { .. } => [std::f64::consts::TAU; 2], + _ => [std::f64::consts::TAU, 0.0], + }; + let map_origin = |origin: [f64; 2]| { + std::array::from_fn::<_, 2, _>(|i| (origin[i] + f64::from(lift[i]) * period[i]) * metric[i]) + }; + let geometry_id = match geometry { + PcurveGeometry::Line2 { origin, direction } => line( + writer, + &map_origin(*origin), + &std::array::from_fn::<_, 2, _>(|i| direction[i] * metric[i]), + )?, + PcurveGeometry::Conic2 { + origin, + axis_a, + axis_b, + } => add_pcurve_conic(writer, *origin, *axis_a, *axis_b, metric, map_origin)?, + PcurveGeometry::IntersectionSide { side, .. } => { + let fitted = fitted.ok_or_else(|| { + GeometryError::InvalidGeometry( + "STEP intersection pcurve is missing its certified fit".into(), + ) + })?; + let controls = fitted + .pcurve(*side) + .iter() + .map(|control| map_origin(*control).to_vec()) + .collect::>(); + bspline_curve(writer, &controls, &fitted.knots, &fitted.multiplicities)? + } + PcurveGeometry::ProjectedCurve { .. } => { + return Err(GeometryError::UnsupportedGeometry( + "STEP projected pcurves require a certified exchange representation".into(), + )) + } + }; + let definition = writer.add_entity(format!( + "DEFINITIONAL_REPRESENTATION('',(#{geometry_id}),#{context})" + )); + Ok(writer.add_entity(format!("PCURVE('',#{surface_id},#{definition})"))) +} + +fn add_pcurve_conic( + writer: &mut Part21Writer, + origin: [f64; 2], + axis_a: [f64; 2], + axis_b: [f64; 2], + metric: [f64; 2], + map_origin: impl Fn([f64; 2]) -> [f64; 2], +) -> Result { + let a = [axis_a[0] * metric[0], axis_a[1] * metric[1], 0.0]; + let b = [axis_b[0] * metric[0], axis_b[1] * metric[1], 0.0]; + let ra = norm(a); + let rb = norm(b); + if !ra.is_finite() + || !rb.is_finite() + || ra == 0.0 + || rb == 0.0 + || dot(a.map(|v| v / ra), b.map(|v| v / rb)).abs() > 64.0 * f64::EPSILON + { + return Err(GeometryError::UnsupportedGeometry( + "STEP requires an orthogonal conic pcurve parameterization".into(), + )); + } + let axis = if ra >= rb { a } else { b }; + let p = point(writer, &map_origin(origin)); + let d = direction(writer, &axis[..2]); + let frame = writer.add_entity(format!("AXIS2_PLACEMENT_2D('',#{p},#{d})")); + Ok(if ra == rb { + writer.add_entity(format!("CIRCLE('',#{frame},{})", real(ra))) + } else { + writer.add_entity(format!( + "ELLIPSE('',#{frame},{},{})", + real(ra.max(rb)), + real(ra.min(rb)) + )) + }) +} + +pub(super) fn bspline_curve( + writer: &mut Part21Writer, + controls: &[Vec], + knots: &[f64], + multiplicities: &[usize], +) -> Result { + if controls.len() < 4 || knots.len() != multiplicities.len() { + return Err(GeometryError::InvalidGeometry( + "invalid fitted cubic curve shape".into(), + )); + } + let points = controls + .iter() + .map(|control| point(writer, control)) + .collect::>(); + Ok(writer.add_entity(format!( + "B_SPLINE_CURVE_WITH_KNOTS('',3,({}),.UNSPECIFIED.,.F.,.F.,({}),({}),.UNSPECIFIED.)", + refs(&points), + multiplicities + .iter() + .map(usize::to_string) + .collect::>() + .join(","), + knots + .iter() + .map(|value| real(*value)) + .collect::>() + .join(",") + ))) +} + +pub(super) fn refs(ids: &[usize]) -> String { + ids.iter() + .map(|id| format!("#{id}")) + .collect::>() + .join(",") +} diff --git a/main/opengeometry/src/exchange/step/mod.rs b/main/opengeometry/src/exchange/step/mod.rs new file mode 100644 index 0000000..1d5fbee --- /dev/null +++ b/main/opengeometry/src/exchange/step/mod.rs @@ -0,0 +1,166 @@ +mod body; +mod entities; +mod product; +mod report; +#[cfg(test)] +mod tests; + +pub(crate) use report::{StepBodyInput, StepExportFailure}; +pub use report::{StepBodyReport, StepExportReport, StepReport, StepSkipped}; + +use super::preflight::preflight; +use crate::brep::{BrepEnvelope, GeometryError}; +use crate::exchange::part21::Part21Writer; +use body::{emit_body, BodyEmission}; +use product::{emit_context, emit_products, emit_representations}; + +pub fn export_step(brep: &BrepEnvelope, unit: &str) -> Result<(String, StepReport), GeometryError> { + let (text, report) = export_bodies( + &[StepBodyInput { + og_id: &brep.id, + shape_id: &brep.id, + shape_revision: brep.revision, + brep, + }], + unit, + "Y", + &brep.id, + "1970-01-01T00:00:00", + Vec::new(), + ) + .map_err(|failure| failure.error)?; + Ok(( + text, + StepReport { + schema_version: 2, + revision: brep.revision.to_string(), + length_unit: unit.into(), + quality: brep.quality.clone(), + faces: report.faces, + edges: report.edges, + solids: report.solids, + cavity_shells: report.cavity_shells, + collapsed_chart_uses: report.collapsed_chart_uses, + geometric_tolerance: report.geometric_tolerance, + exchange_error_bound: report.exchange_error_bound, + validation_level: report.validation_level, + }, + )) +} + +pub(crate) fn export_bodies( + bodies: &[StepBodyInput<'_>], + unit: &str, + up_axis: &str, + name: &str, + timestamp: &str, + skipped: Vec, +) -> Result<(String, StepExportReport), StepExportFailure> { + let scale = match unit { + "metre" => 1.0, + "millimetre" => 1000.0, + _ => { + return Err(GeometryError::InvalidGeometry( + "STEP length unit must be metre or millimetre".into(), + ) + .into()) + } + }; + if bodies.is_empty() { + return Err( + GeometryError::InvalidGeometry("STEP export has no solid bodies".into()).into(), + ); + } + let mut writer = Part21Writer::new("AUTOMOTIVE_DESIGN"); + writer.set_file_name(name); + writer.set_timestamp(timestamp); + writer.set_description("OpenGeometry analytic BRep"); + let context2 = + writer.add_entity("(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('',''))"); + let emitted = add_bodies(&mut writer, bodies, scale, context2)?; + let bound = emitted + .iter() + .map(|body| body.exchange_bound) + .fold(0.0_f64, f64::max); + let context = emit_context(&mut writer, unit, bound, scale); + let representations = emit_representations(&mut writer, &emitted, context); + let names = bodies.iter().map(|body| body.og_id).collect::>(); + emit_products(&mut writer, &names, &representations); + let entities = writer.entity_count(); + if entities > 2_000_000 { + return Err( + GeometryError::LimitExceeded("STEP file exceeds 2,000,000 entities".into()).into(), + ); + } + let text = writer.build()?; + let body_reports = step_body_reports(bodies, &emitted, scale); + let report = StepExportReport { + unit: unit.into(), + up_axis: up_axis.into(), + timestamp: timestamp.into(), + products: bodies.len(), + solids: emitted.iter().map(|body| body.solids.len()).sum(), + faces: emitted.iter().map(|body| body.faces).sum(), + edges: emitted.iter().map(|body| body.edges).sum(), + cavity_shells: emitted.iter().map(|body| body.cavity_shells).sum(), + collapsed_chart_uses: emitted.iter().map(|body| body.collapsed_chart_uses).sum(), + fitted_curves: emitted.iter().map(|body| body.fitted_curves).sum(), + pcurveless_edges: emitted.iter().map(|body| body.pcurveless_edges).sum(), + entities, + bytes: text.len(), + exchange_error_bound: bound * scale, + geometric_tolerance: bodies + .iter() + .map(|body| body.brep.accuracy.geometric) + .fold(0.0_f64, f64::max) + * scale, + validation_level: "structure, sampled residuals and Part-21 references", + bodies: body_reports, + skipped, + }; + Ok((text, report)) +} + +fn add_bodies( + writer: &mut Part21Writer, + bodies: &[StepBodyInput<'_>], + scale: f64, + context2: usize, +) -> Result, StepExportFailure> { + let mut emitted = Vec::with_capacity(bodies.len()); + for (index, input) in bodies.iter().enumerate() { + let body = preflight(input.brep, scale) + .and_then(|initial_bound| emit_body(writer, input.brep, scale, context2, initial_bound)) + .map_err(|error| StepExportFailure { + body_index: Some(index), + error, + })?; + emitted.push(body); + } + Ok(emitted) +} + +fn step_body_reports( + bodies: &[StepBodyInput<'_>], + emitted: &[BodyEmission], + scale: f64, +) -> Vec { + bodies + .iter() + .zip(emitted) + .map(|(input, output)| StepBodyReport { + og_id: input.og_id.into(), + shape_id: input.shape_id.into(), + shape_revision: input.shape_revision, + solids: output.solids.len(), + faces: output.faces, + edges: output.edges, + cavity_shells: output.cavity_shells, + collapsed_chart_uses: output.collapsed_chart_uses, + fitted_curves: output.fitted_curves, + pcurveless_edges: output.pcurveless_edges, + exchange_error_bound: output.exchange_bound * scale, + geometric_tolerance: input.brep.accuracy.geometric * scale, + }) + .collect::>() +} diff --git a/main/opengeometry/src/exchange/step/product.rs b/main/opengeometry/src/exchange/step/product.rs new file mode 100644 index 0000000..ee5b42f --- /dev/null +++ b/main/opengeometry/src/exchange/step/product.rs @@ -0,0 +1,66 @@ +use super::body::BodyEmission; +use super::entities::{real, refs}; +use crate::exchange::part21::{sanitize_string_literal, Part21Writer}; + +pub(super) fn emit_context(writer: &mut Part21Writer, unit: &str, bound: f64, scale: f64) -> usize { + let prefix = if unit == "millimetre" { ".MILLI." } else { "$" }; + let length = writer.add_entity(format!( + "(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT({prefix},.METRE.))" + )); + let angle = writer.add_entity("(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.))"); + let solid_angle = + writer.add_entity("(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT())"); + let uncertainty = writer.add_entity(format!( + "UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE({}),#{length},'distance_accuracy_value','exchange error bound')", + real(bound * scale) + )); + writer.add_entity(format!( + "(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#{uncertainty})) GLOBAL_UNIT_ASSIGNED_CONTEXT((#{length},#{angle},#{solid_angle})) REPRESENTATION_CONTEXT('',''))" + )) +} + +pub(super) fn emit_representations( + writer: &mut Part21Writer, + bodies: &[BodyEmission], + context: usize, +) -> Vec { + bodies + .iter() + .map(|body| { + writer.add_entity(format!( + "ADVANCED_BREP_SHAPE_REPRESENTATION('',({}),#{context})", + refs(&body.solids) + )) + }) + .collect() +} + +pub(super) fn emit_products(writer: &mut Part21Writer, names: &[&str], representations: &[usize]) { + let app = writer.add_entity("APPLICATION_CONTEXT('automotive design')"); + writer.add_entity(format!( + "APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#{app})" + )); + let product_context = writer.add_entity(format!("PRODUCT_CONTEXT('',#{app},'mechanical')")); + let mut definition_context = None; + for (name, representation) in names.iter().zip(representations) { + let name = sanitize_string_literal(name); + let product = writer.add_entity(format!( + "PRODUCT('{name}','{name}','',(#{product_context}))" + )); + let formation = writer.add_entity(format!( + "PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#{product},.NOT_KNOWN.)" + )); + let definition_context = *definition_context.get_or_insert_with(|| { + writer.add_entity(format!( + "PRODUCT_DEFINITION_CONTEXT('part definition',#{app},'design')" + )) + }); + let definition = writer.add_entity(format!( + "PRODUCT_DEFINITION('','',#{formation},#{definition_context})" + )); + let shape = writer.add_entity(format!("PRODUCT_DEFINITION_SHAPE('','',#{definition})")); + writer.add_entity(format!( + "SHAPE_DEFINITION_REPRESENTATION(#{shape},#{representation})" + )); + } +} diff --git a/main/opengeometry/src/exchange/step/report.rs b/main/opengeometry/src/exchange/step/report.rs new file mode 100644 index 0000000..7f2a191 --- /dev/null +++ b/main/opengeometry/src/exchange/step/report.rs @@ -0,0 +1,88 @@ +use crate::brep::{BrepEnvelope, GeometryError, GeometryQuality}; +use serde::Serialize; + +#[derive(Debug, Serialize)] +pub struct StepReport { + pub(super) schema_version: u32, + pub(super) revision: String, + pub(super) length_unit: String, + pub(super) quality: GeometryQuality, + pub(super) faces: usize, + pub(super) edges: usize, + pub(crate) solids: usize, + pub(super) cavity_shells: usize, + pub(super) collapsed_chart_uses: usize, + pub(super) geometric_tolerance: f64, + pub exchange_error_bound: f64, + pub(super) validation_level: &'static str, +} + +#[derive(Debug)] +pub(crate) struct StepBodyInput<'a> { + pub(crate) og_id: &'a str, + pub(crate) shape_id: &'a str, + pub(crate) shape_revision: u64, + pub(crate) brep: &'a BrepEnvelope, +} + +#[derive(Debug)] +pub(crate) struct StepExportFailure { + pub(crate) body_index: Option, + pub(crate) error: GeometryError, +} + +impl From for StepExportFailure { + fn from(error: GeometryError) -> Self { + Self { + body_index: None, + error, + } + } +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct StepBodyReport { + pub og_id: String, + pub(super) shape_id: String, + pub(super) shape_revision: u64, + pub(super) solids: usize, + pub(super) faces: usize, + pub(super) edges: usize, + pub(super) cavity_shells: usize, + pub(super) collapsed_chart_uses: usize, + pub(super) fitted_curves: usize, + pub pcurveless_edges: usize, + pub(super) exchange_error_bound: f64, + pub(super) geometric_tolerance: f64, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct StepSkipped { + pub og_id: String, + pub(crate) reason: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct StepExportReport { + pub(super) unit: String, + pub up_axis: String, + pub(super) timestamp: String, + pub products: usize, + pub solids: usize, + pub faces: usize, + pub edges: usize, + pub(super) cavity_shells: usize, + pub(super) collapsed_chart_uses: usize, + pub(super) fitted_curves: usize, + pub pcurveless_edges: usize, + pub entities: usize, + pub(super) bytes: usize, + pub(super) exchange_error_bound: f64, + pub(super) geometric_tolerance: f64, + pub(super) validation_level: &'static str, + pub bodies: Vec, + pub skipped: Vec, +} diff --git a/main/opengeometry/src/exchange/step/tests.rs b/main/opengeometry/src/exchange/step/tests.rs new file mode 100644 index 0000000..de60a67 --- /dev/null +++ b/main/opengeometry/src/exchange/step/tests.rs @@ -0,0 +1,226 @@ +use super::{export_bodies, export_step, StepBodyInput}; +use crate::brep::{ + coarse_accuracy, BrepEnvelope, Curve, CurveGeometry, EdgeGeometry, Frame3, GeometryError, + IntersectionDefinition, IntersectionSide, PcurveGeometry, Surface, TraceAnchor, +}; +use crate::math::Interval; +use crate::operations::modifying::boolean::{boolean_spheres, BooleanOp}; +use crate::primitives; + +fn frame() -> Frame3 { + Frame3::from_axis([1.0, 2.0, 3.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]).unwrap() +} + +fn numerical_edge_box() -> BrepEnvelope { + let mut brep = primitives::cuboid( + "numerical-edge".into(), + Frame3::IDENTITY, + [1.0, 2.0, 3.0], + coarse_accuracy(1e-6), + ) + .unwrap(); + let edge = brep.topology.edges[0].clone(); + let EdgeGeometry::Curve { curve, range } = edge.geometry else { + panic!("cuboid edge must be regular") + }; + let use_a = edge.halfedge; + let use_b = edge.twin_halfedge.unwrap(); + let face_a = brep.topology.halfedges[use_a as usize].face.unwrap(); + let face_b = brep.topology.halfedges[use_b as usize].face.unwrap(); + let surface_ids = [ + brep.topology.faces[face_a as usize].surface, + brep.topology.faces[face_b as usize].surface, + ]; + let evaluator = brep.geometry.curve(curve).unwrap(); + let points = [ + evaluator.point_at(range.lo).unwrap(), + evaluator.point_at(range.hi).unwrap(), + ]; + let uv_a = points.map(|point| { + brep.geometry.surfaces[surface_ids[0] as usize] + .project(point, None) + .unwrap() + }); + let uv_b = points.map(|point| { + brep.geometry.surfaces[surface_ids[1] as usize] + .project(point, None) + .unwrap() + }); + let padding = 1e-10; + let tubes = std::array::from_fn(|axis| { + let values = if axis < 2 { + [uv_a[0][axis], uv_a[1][axis]] + } else { + [uv_b[0][axis - 2], uv_b[1][axis - 2]] + }; + Interval::new( + values[0].min(values[1]) - padding, + values[0].max(values[1]) + padding, + ) + .unwrap() + }); + let definition = brep.geometry.intersections.len() as u32; + brep.geometry.intersections.push(IntersectionDefinition { + surfaces: surface_ids, + anchors: vec![ + TraceAnchor { + parameter: range.lo, + point: points[0], + uv_a: uv_a[0], + uv_b: uv_b[0], + }, + TraceAnchor { + parameter: range.hi, + point: points[1], + uv_a: uv_a[1], + uv_b: uv_b[1], + }, + ], + uv_tubes: vec![tubes], + residual_tolerance: coarse_accuracy(1e-6).intersection, + }); + brep.geometry.curves[curve as usize] = CurveGeometry::Intersection { definition }; + let pcurve_a = brep.geometry.pcurves.len() as u32; + brep.geometry + .pcurves + .push(PcurveGeometry::IntersectionSide { + definition, + side: IntersectionSide::A, + }); + let pcurve_b = brep.geometry.pcurves.len() as u32; + brep.geometry + .pcurves + .push(PcurveGeometry::IntersectionSide { + definition, + side: IntersectionSide::B, + }); + brep.topology.halfedges[use_a as usize].geometry_use.pcurve = Some(pcurve_a); + brep.topology.halfedges[use_b as usize].geometry_use.pcurve = Some(pcurve_b); + brep.validate().unwrap(); + brep +} +#[test] +fn exports_all_families_shared_edges_seams_and_explicit_poles() { + let bodies = [ + primitives::cylinder("c#999's".into(), frame(), 1.0, 2.0, coarse_accuracy(1e-6)).unwrap(), + primitives::sphere("s".into(), frame(), 1.0, coarse_accuracy(1e-6)).unwrap(), + primitives::cone("c".into(), frame(), 1.0, 2.0, coarse_accuracy(1e-6)).unwrap(), + primitives::frustum("f".into(), frame(), 1.0, 0.4, 2.0, coarse_accuracy(1e-6)).unwrap(), + primitives::torus("t".into(), frame(), 2.0, 0.5, coarse_accuracy(1e-6)).unwrap(), + primitives::cylinder_with_circular_hole( + "w".into(), + frame(), + frame(), + 0.3, + 1.5, + 2.0, + coarse_accuracy(1e-6), + ) + .unwrap(), + ]; + for brep in bodies { + let original = brep.to_json().unwrap(); + let (metres, report) = export_step(&brep, "metre").unwrap(); + assert_eq!(report.faces, brep.topology.faces.len()); + assert_eq!(metres.matches("=EDGE_CURVE(").count(), report.edges); + assert!(!metres.contains("POLY_LOOP")); + assert!(metres.contains("PCURVE(")); + if brep.topology.edges.iter().any(|e| e.chart_seam) { + assert!(metres.contains("SEAM_CURVE(")); + } + let (mm, _) = export_step(&brep, "millimetre").unwrap(); + assert!(mm.contains("SI_UNIT(.MILLI.,.METRE.)")); + assert_ne!(metres, mm); + assert_eq!(original, brep.to_json().unwrap()); + } +} + +#[test] +fn numerical_intersection_edges_export_as_bounded_cubic_curves() { + let brep = numerical_edge_box(); + let (step, report) = export_step(&brep, "metre").unwrap(); + assert!(step.contains("B_SPLINE_CURVE_WITH_KNOTS")); + assert!(report.exchange_error_bound <= brep.accuracy.exchange); +} +#[test] +fn preserves_reversed_cut_faces_and_cavity_orientation() { + let a = primitives::sphere("host".into(), frame(), 2.0, coarse_accuracy(1e-6)).unwrap(); + let b = primitives::sphere("cut".into(), frame(), 0.5, coarse_accuracy(1e-6)).unwrap(); + let cavity = boolean_spheres(&a, &b, BooleanOp::Subtraction, "cavity".into()) + .unwrap() + .brep; + let (text, report) = export_step(&cavity, "metre").unwrap(); + assert_eq!(report.cavity_shells, 1); + assert!(text.contains("BREP_WITH_VOIDS(")); + assert!(text + .lines() + .any(|line| line.contains("=ADVANCED_FACE(") && line.ends_with(",.F.);"))); + let mut shifted = frame(); + shifted.origin[0] += 1.7; + let cutter = primitives::sphere("cutter".into(), shifted, 1.0, coarse_accuracy(1e-6)).unwrap(); + let cut = boolean_spheres(&a, &cutter, BooleanOp::Subtraction, "cut".into()) + .unwrap() + .brep; + assert!(export_step(&cut, "metre").is_ok()); + let mut invalid = cut; + invalid.topology.halfedges[0].next = None; + assert!(matches!( + export_step(&invalid, "metre"), + Err(GeometryError::InvalidTopology(_)) + )); +} + +#[test] +fn rejects_unrepresentable_exchange_and_preserves_large_revisions() { + let mut brep = + primitives::sphere("budget".into(), frame(), 1.0, coarse_accuracy(1e-6)).unwrap(); + brep.revision = u64::MAX; + let (_, report) = export_step(&brep, "metre").unwrap(); + assert_eq!(report.revision, u64::MAX.to_string()); + brep.accuracy.exchange = 1e-10; + assert!(matches!( + export_step(&brep, "metre"), + Err(GeometryError::LimitExceeded(_)) + )); + brep.accuracy.exchange = 1e-6; + brep.id = "x".repeat(4097); + assert!(matches!( + export_step(&brep, "metre"), + Err(GeometryError::LimitExceeded(_)) + )); + assert!(matches!( + export_step(&brep, "inch"), + Err(GeometryError::InvalidGeometry(_)) + )); +} + +#[test] +fn two_million_entity_export_is_limit_exceeded() { + let brep = primitives::cuboid( + "cap".into(), + Frame3::IDENTITY, + [1.0, 2.0, 3.0], + coarse_accuracy(1e-6), + ) + .unwrap(); + let inputs = (0..7_000) + .map(|_| StepBodyInput { + og_id: "cap", + shape_id: "cap", + shape_revision: 0, + brep: &brep, + }) + .collect::>(); + let failure = export_bodies( + &inputs, + "metre", + "Y", + "cap", + "1970-01-01T00:00:00", + Vec::new(), + ) + .unwrap_err(); + assert!( + matches!(&failure.error, GeometryError::LimitExceeded(message) if message.contains("2,000,000")) + ); +} diff --git a/main/opengeometry/src/export/ifc.rs b/main/opengeometry/src/export/ifc.rs deleted file mode 100644 index 3023281..0000000 --- a/main/opengeometry/src/export/ifc.rs +++ /dev/null @@ -1,1775 +0,0 @@ -use std::collections::HashMap; -use std::fmt; - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -use crate::brep::Brep; -use crate::operations::triangulate::triangulate_polygon_with_holes; - -use super::part21::{sanitize_string_literal, Part21Writer}; - -const IFC_LENGTH_EPSILON: f64 = 1.0e-12; -const IFC_CLASS_PROXY: &str = "IFCBUILDINGELEMENTPROXY"; -const IFC_CLASS_SPACE: &str = "IFCSPACE"; -const IFC_CLASS_SITE: &str = "IFCSITE"; -const IFC_ALLOWED_CLASSES: [&str; 14] = [ - IFC_CLASS_PROXY, - "IFCWALL", - "IFCSLAB", - "IFCCOLUMN", - "IFCBEAM", - "IFCMEMBER", - "IFCDOOR", - "IFCWINDOW", - "IFCROOF", - "IFCSTAIR", - "IFCRAILING", - "IFCFOOTING", - IFC_CLASS_SPACE, - IFC_CLASS_SITE, -]; - -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub enum IfcErrorPolicy { - Strict, - BestEffort, -} - -impl Default for IfcErrorPolicy { - fn default() -> Self { - Self::BestEffort - } -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub enum IfcSchemaVersion { - Ifc4Add2, -} - -impl Default for IfcSchemaVersion { - fn default() -> Self { - Self::Ifc4Add2 - } -} - -impl IfcSchemaVersion { - fn as_file_schema(self) -> &'static str { - match self { - Self::Ifc4Add2 => "IFC4", - } - } -} - -/// A typed IfcPropertySingleValue payload. `#[serde(untagged)]` keeps the -/// config JSON natural (`true` / `4500` / `"office"`) and remains backward -/// compatible with the previous string-only property sets. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -#[serde(untagged)] -pub enum IfcPropertyValue { - Bool(bool), - Number(f64), - Text(String), -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct IfcEntitySemantics { - pub ifc_class: Option, - pub name: Option, - pub description: Option, - pub object_type: Option, - pub tag: Option, - #[serde(default)] - pub property_sets: HashMap>, - #[serde(default)] - pub quantity_sets: HashMap>, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct IfcExportConfig { - pub schema: IfcSchemaVersion, - pub project_name: Option, - pub site_name: Option, - pub building_name: Option, - pub storey_name: Option, - pub scale: f64, - pub error_policy: IfcErrorPolicy, - pub validate_topology: bool, - pub require_closed_shell: bool, - pub semantics: Option>, - /// Length unit emitted in the IFC unit assignment (D8). - #[serde(default)] - pub length_unit: crate::units::LengthUnit, - /// D9: emit analytic `IFCADVANCEDBREP` (IFCPLANE / IFCCYLINDRICALSURFACE with - /// IFCLINE / IFCCIRCLE edges) for entities whose faces carry analytic - /// surfaces, instead of an `IFCTRIANGULATEDFACESET`. Falls back to - /// tessellation when a brep has no analytic geometry. - #[serde(default = "default_true_ifc")] - pub analytic_surfaces: bool, - /// Convert source Y-up geometry (Three.js convention) to IFC's Z-up - /// world by mapping `(x, y, z) -> (x, -z, y)` once, before any - /// emission. Default on, since kernel BReps are authored Y-up. - #[serde(default = "default_true_ifc")] - pub up_axis_conversion: bool, -} - -fn default_true_ifc() -> bool { - true -} - -impl Default for IfcExportConfig { - fn default() -> Self { - Self { - schema: IfcSchemaVersion::default(), - project_name: Some("OpenGeometry Project".to_string()), - site_name: Some("OpenGeometry Site".to_string()), - building_name: Some("OpenGeometry Building".to_string()), - storey_name: Some("OpenGeometry Storey".to_string()), - scale: 1.0, - error_policy: IfcErrorPolicy::BestEffort, - validate_topology: true, - require_closed_shell: true, - semantics: None, - length_unit: crate::units::LengthUnit::default(), - analytic_surfaces: true, - up_axis_conversion: true, - } - } -} - -/// Map a single source Y-up coordinate/direction to IFC Z-up: -/// `(x, y, z) -> (x, -z, y)`. A proper rotation (+90° about X, -/// determinant +1) so handedness is preserved and nothing mirrors. -fn to_z_up(v: Vector3) -> Vector3 { - Vector3::new(v.x, -v.z, v.y) -} - -/// Return a Z-up copy of `brep`, transforming vertices, analytic edge -/// curves and face surfaces consistently, then recomputing normals. -/// Applied exactly once at export entry so the rest of the pipeline is -/// untouched and double-application is impossible. -fn brep_to_z_up(brep: &Brep) -> Brep { - let mut converted = brep.clone(); - for vertex in &mut converted.vertices { - vertex.position = to_z_up(vertex.position); - } - for edge in &mut converted.edges { - if let Some(curve) = &edge.curve { - // scale = 1.0: the conversion carries no scale (radii unchanged). - edge.curve = Some(curve.transformed_with(&to_z_up, 1.0)); - } - } - for face in &mut converted.faces { - if let Some(surface) = &face.surface { - face.surface = Some(surface.transformed_with(&to_z_up, 1.0)); - } - } - if !converted.faces.is_empty() { - converted.recompute_face_normals(); - } - converted -} - -/// IFC SI prefix token (e.g. `.MILLI.`) for the length unit, or `$` for the -/// base metre. Non-SI units fall back to metre (IFC conversion-based units are -/// a follow-on). -fn ifc_length_unit_entity(unit: crate::units::LengthUnit) -> String { - let prefix = unit.step_si_prefix().unwrap_or("$"); - format!("IFCSIUNIT(*,.LENGTHUNIT.,{},.METRE.)", prefix) -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct IfcExportReport { - pub input_breps: usize, - pub input_faces: usize, - pub exported_elements: usize, - pub exported_faces: usize, - pub exported_triangles: usize, - pub skipped_entities: usize, - pub skipped_faces: usize, - pub topology_errors: usize, - pub semantics_applied: usize, - pub proxy_fallbacks: usize, - pub property_sets_written: usize, - pub quantity_sets_written: usize, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum IfcExportError { - EmptyInput, - InvalidTopology(String), - UnsupportedEntity(String), - InvalidSemantics(String), - MeshGeneration(String), - Serialization(String), - Io(String), -} - -impl fmt::Display for IfcExportError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - IfcExportError::EmptyInput => write!(f, "No BREP input provided for IFC export"), - IfcExportError::InvalidTopology(msg) => write!(f, "Invalid topology: {}", msg), - IfcExportError::UnsupportedEntity(msg) => write!(f, "Unsupported BREP: {}", msg), - IfcExportError::InvalidSemantics(msg) => write!(f, "Invalid IFC semantics: {}", msg), - IfcExportError::MeshGeneration(msg) => write!(f, "Mesh generation failed: {}", msg), - IfcExportError::Serialization(msg) => write!(f, "IFC serialization failed: {}", msg), - IfcExportError::Io(msg) => write!(f, "IFC I/O failed: {}", msg), - } - } -} - -impl std::error::Error for IfcExportError {} - -#[derive(Clone, Copy)] -pub struct IfcEntityInput<'a> { - pub entity_id: &'a str, - pub kind: &'a str, - pub brep: &'a Brep, -} - -#[derive(Clone)] -struct IfcOwnedEntity<'a> { - entity_id: String, - kind: String, - brep: &'a Brep, -} - -#[derive(Clone)] -struct TessellatedMesh { - points: Vec, - faces: Vec<[usize; 3]>, -} - -pub fn export_brep_to_ifc_text( - brep: &Brep, - config: &IfcExportConfig, -) -> Result<(String, IfcExportReport), IfcExportError> { - let owned = vec![IfcOwnedEntity { - entity_id: "brep-0".to_string(), - kind: "BREP".to_string(), - brep, - }]; - export_owned_entities_to_ifc_text(&owned, config) -} - -pub fn export_breps_to_ifc_text<'a, I>( - breps: I, - config: &IfcExportConfig, -) -> Result<(String, IfcExportReport), IfcExportError> -where - I: IntoIterator, -{ - let mut owned = Vec::new(); - for (index, brep) in breps.into_iter().enumerate() { - owned.push(IfcOwnedEntity { - entity_id: format!("brep-{}", index), - kind: "BREP".to_string(), - brep, - }); - } - - export_owned_entities_to_ifc_text(&owned, config) -} - -pub fn export_scene_entities_to_ifc_text<'a, I>( - entities: I, - config: &IfcExportConfig, -) -> Result<(String, IfcExportReport), IfcExportError> -where - I: IntoIterator>, -{ - let owned: Vec> = entities - .into_iter() - .map(|entity| IfcOwnedEntity { - entity_id: entity.entity_id.to_string(), - kind: entity.kind.to_string(), - brep: entity.brep, - }) - .collect(); - - export_owned_entities_to_ifc_text(&owned, config) -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn export_brep_to_ifc_file( - brep: &Brep, - file_path: &str, - config: &IfcExportConfig, -) -> Result { - let (text, report) = export_brep_to_ifc_text(brep, config)?; - std::fs::write(file_path, text).map_err(|err| IfcExportError::Io(err.to_string()))?; - Ok(report) -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn export_breps_to_ifc_file<'a, I>( - breps: I, - file_path: &str, - config: &IfcExportConfig, -) -> Result -where - I: IntoIterator, -{ - let (text, report) = export_breps_to_ifc_text(breps, config)?; - std::fs::write(file_path, text).map_err(|err| IfcExportError::Io(err.to_string()))?; - Ok(report) -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn export_scene_entities_to_ifc_file<'a, I>( - entities: I, - file_path: &str, - config: &IfcExportConfig, -) -> Result -where - I: IntoIterator>, -{ - let (text, report) = export_scene_entities_to_ifc_text(entities, config)?; - std::fs::write(file_path, text).map_err(|err| IfcExportError::Io(err.to_string()))?; - Ok(report) -} - -fn export_owned_entities_to_ifc_text<'a>( - entities: &[IfcOwnedEntity<'a>], - config: &IfcExportConfig, -) -> Result<(String, IfcExportReport), IfcExportError> { - let scale = validate_config(config)?; - - if entities.is_empty() { - return Err(IfcExportError::EmptyInput); - } - - // Y-up -> Z-up once, up front: convert each BREP and re-bind the - // working entities to the converted geometry. Everything downstream - // (analytic surfaces, tessellation, placements) then operates on - // Z-up data unchanged. - let converted_breps: Vec; - let converted_entities: Vec>; - let entities: &[IfcOwnedEntity<'_>] = if config.up_axis_conversion { - converted_breps = entities.iter().map(|e| brep_to_z_up(e.brep)).collect(); - converted_entities = entities - .iter() - .zip(converted_breps.iter()) - .map(|(e, brep)| IfcOwnedEntity { - entity_id: e.entity_id.clone(), - kind: e.kind.clone(), - brep, - }) - .collect(); - &converted_entities - } else { - entities - }; - - let mut report = IfcExportReport { - input_breps: entities.len(), - ..IfcExportReport::default() - }; - - let project_name = config - .project_name - .clone() - .unwrap_or_else(|| "OpenGeometry Project".to_string()); - - let mut writer = Part21Writer::new(config.schema.as_file_schema()); - writer.set_description("ViewDefinition [CoordinationView]"); - writer.set_file_name(project_name.clone()); - - let origin = writer.add_entity("IFCCARTESIANPOINT((0.,0.,0.))"); - let axis_z = writer.add_entity("IFCDIRECTION((0.,0.,1.))"); - let axis_x = writer.add_entity("IFCDIRECTION((1.,0.,0.))"); - let world_axis = writer.add_entity(format!( - "IFCAXIS2PLACEMENT3D({},{},{})", - Part21Writer::reference(origin), - Part21Writer::reference(axis_z), - Part21Writer::reference(axis_x) - )); - - let geom_context = writer.add_entity(format!( - "IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-5,{},$)", - Part21Writer::reference(world_axis) - )); - - let length_unit = writer.add_entity(ifc_length_unit_entity(config.length_unit)); - let area_unit = writer.add_entity("IFCSIUNIT(*,.AREAUNIT.,$,.SQUARE_METRE.)"); - let volume_unit = writer.add_entity("IFCSIUNIT(*,.VOLUMEUNIT.,$,.CUBIC_METRE.)"); - let angle_unit = writer.add_entity("IFCSIUNIT(*,.PLANEANGLEUNIT.,$,.RADIAN.)"); - let unit_assignment = writer.add_entity(format!( - "IFCUNITASSIGNMENT(({}, {}, {}, {}))", - Part21Writer::reference(length_unit), - Part21Writer::reference(area_unit), - Part21Writer::reference(volume_unit), - Part21Writer::reference(angle_unit) - )); - - let project = writer.add_entity(format!( - "IFCPROJECT('{}',$,'{}',$,$,$,$,({}),{})", - ifc_guid("project"), - sanitize_string_literal(&project_name), - Part21Writer::reference(geom_context), - Part21Writer::reference(unit_assignment) - )); - - let site_axis = writer.add_entity(format!( - "IFCLOCALPLACEMENT($,{})", - Part21Writer::reference(world_axis) - )); - let building_axis = writer.add_entity(format!( - "IFCLOCALPLACEMENT({},{})", - Part21Writer::reference(site_axis), - Part21Writer::reference(world_axis) - )); - let storey_axis = writer.add_entity(format!( - "IFCLOCALPLACEMENT({},{})", - Part21Writer::reference(building_axis), - Part21Writer::reference(world_axis) - )); - - let site = writer.add_entity(format!( - "IFCSITE('{}',$,'{}',$,$,{},$,$,.ELEMENT.,$,$,$,$,$)", - ifc_guid("site"), - sanitize_string_literal( - &config - .site_name - .clone() - .unwrap_or_else(|| "OpenGeometry Site".to_string()) - ), - Part21Writer::reference(site_axis) - )); - - let building = writer.add_entity(format!( - "IFCBUILDING('{}',$,'{}',$,$,{},$,$,.ELEMENT.,$,$,$)", - ifc_guid("building"), - sanitize_string_literal( - &config - .building_name - .clone() - .unwrap_or_else(|| "OpenGeometry Building".to_string()) - ), - Part21Writer::reference(building_axis) - )); - - let storey = writer.add_entity(format!( - "IFCBUILDINGSTOREY('{}',$,'{}',$,$,{},$,$,.ELEMENT.,0.)", - ifc_guid("storey"), - sanitize_string_literal( - &config - .storey_name - .clone() - .unwrap_or_else(|| "OpenGeometry Storey".to_string()) - ), - Part21Writer::reference(storey_axis) - )); - - writer.add_entity(format!( - "IFCRELAGGREGATES('{}',$,$,$,{},({}))", - ifc_guid("rel-project-site"), - Part21Writer::reference(project), - Part21Writer::reference(site) - )); - - writer.add_entity(format!( - "IFCRELAGGREGATES('{}',$,$,$,{},({}))", - ifc_guid("rel-site-building"), - Part21Writer::reference(site), - Part21Writer::reference(building) - )); - - writer.add_entity(format!( - "IFCRELAGGREGATES('{}',$,$,$,{},({}))", - ifc_guid("rel-building-storey"), - Part21Writer::reference(building), - Part21Writer::reference(storey) - )); - - let mut element_ids = Vec::new(); - let mut space_ids = Vec::new(); - let mut site_ids = Vec::new(); - - for entity in entities { - let brep = entity.brep; - - if config.validate_topology { - if let Err(error) = brep.validate_topology() { - if config.error_policy == IfcErrorPolicy::Strict { - return Err(IfcExportError::InvalidTopology(format!( - "Entity '{}' failed topology validation: {}", - entity.entity_id, error - ))); - } - report.topology_errors += 1; - report.skipped_entities += 1; - continue; - } - } - - if config.require_closed_shell && !is_closed_solid(brep) { - if config.error_policy == IfcErrorPolicy::Strict { - return Err(IfcExportError::UnsupportedEntity(format!( - "Entity '{}' is not a closed-shell solid", - entity.entity_id - ))); - } - report.skipped_entities += 1; - continue; - } - - let mesh = triangulate_entity_mesh( - entity, - scale, - config.error_policy, - &mut report, - format!("entity '{}'", entity.entity_id), - )?; - - if mesh.faces.is_empty() || mesh.points.is_empty() { - if config.error_policy == IfcErrorPolicy::Strict { - return Err(IfcExportError::MeshGeneration(format!( - "Entity '{}' generated no exportable mesh", - entity.entity_id - ))); - } - report.skipped_entities += 1; - continue; - } - - let semantics = config - .semantics - .as_ref() - .and_then(|map| map.get(&entity.entity_id)); - - let class_name = resolve_ifc_class(&entity.entity_id, semantics, config, &mut report)?; - - // D9: prefer an analytic IFCADVANCEDBREP when the brep carries analytic - // surfaces; otherwise fall back to the tessellated face set. - let analytic_rep = - if config.analytic_surfaces && brep.faces.iter().any(|f| f.surface.is_some()) { - emit_ifc_advanced_brep(&mut writer, brep, scale, geom_context) - } else { - None - }; - - let shape_representation = if let Some((rep, face_count)) = analytic_rep { - report.exported_faces += face_count; - rep - } else { - let mesh_point_list = writer.add_entity(format!( - "IFCCARTESIANPOINTLIST3D({})", - format_ifc_coord_list(&mesh.points) - )); - - let mesh_faceset = writer.add_entity(format!( - "IFCTRIANGULATEDFACESET({},$,.T.,{},$)", - Part21Writer::reference(mesh_point_list), - format_ifc_face_index_list(&mesh.faces) - )); - - report.exported_triangles += mesh.faces.len(); - report.exported_faces += mesh.faces.len(); - - writer.add_entity(format!( - "IFCSHAPEREPRESENTATION({},'Body','Tessellation',({}))", - Part21Writer::reference(geom_context), - Part21Writer::reference(mesh_faceset) - )) - }; - - let definition_shape = writer.add_entity(format!( - "IFCPRODUCTDEFINITIONSHAPE($,$,({}))", - Part21Writer::reference(shape_representation) - )); - - let placement = writer.add_entity(format!( - "IFCLOCALPLACEMENT({},{})", - Part21Writer::reference(storey_axis), - Part21Writer::reference(world_axis) - )); - - let default_name = format!("{}-{}", entity.kind, entity.entity_id); - let name = semantics - .and_then(|sem| sem.name.clone()) - .unwrap_or(default_name); - let description = semantics - .and_then(|sem| sem.description.clone()) - .unwrap_or_default(); - let object_type = semantics - .and_then(|sem| sem.object_type.clone()) - .unwrap_or_else(|| entity.kind.clone()); - let tag = semantics - .and_then(|sem| sem.tag.clone()) - .unwrap_or_else(|| entity.entity_id.clone()); - - let guid = ifc_guid(&format!("element-{}", entity.entity_id)); - let name_literal = sanitize_string_literal(&name); - let description_expr = if description.is_empty() { - "$".to_string() - } else { - format!("'{}'", sanitize_string_literal(&description)) - }; - let object_type_literal = sanitize_string_literal(&object_type); - let placement_ref = Part21Writer::reference(placement); - let shape_ref = Part21Writer::reference(definition_shape); - let tag_literal = sanitize_string_literal(&tag); - - // IfcSpace and IfcSite are spatial elements with their own attribute - // signatures (LongName + CompositionType instead of Tag); everything - // else uses the shared building-element signature. - let element_expr = match class_name { - IFC_CLASS_SPACE => format!( - "IFCSPACE('{}',$,'{}',{},'{}',{},{},'{}',.ELEMENT.,.NOTDEFINED.,$)", - guid, - name_literal, - description_expr, - object_type_literal, - placement_ref, - shape_ref, - tag_literal - ), - IFC_CLASS_SITE => format!( - "IFCSITE('{}',$,'{}',{},'{}',{},{},'{}',.ELEMENT.,$,$,$,$,$)", - guid, - name_literal, - description_expr, - object_type_literal, - placement_ref, - shape_ref, - tag_literal - ), - _ => format!( - "{}('{}',$,'{}',{},'{}',{},{},'{}',.NOTDEFINED.)", - class_name, - guid, - name_literal, - description_expr, - object_type_literal, - placement_ref, - shape_ref, - tag_literal - ), - }; - - let element_id = writer.add_entity(element_expr); - // Spatial elements may not ride IfcRelContainedInSpatialStructure: - // spaces AGGREGATE under the storey, extra sites under the project. - match class_name { - IFC_CLASS_SPACE => space_ids.push(element_id), - IFC_CLASS_SITE => site_ids.push(element_id), - _ => element_ids.push(element_id), - } - - if let Some(semantics) = semantics { - write_property_sets( - &mut writer, - element_id, - &entity.entity_id, - semantics, - &mut report, - ); - write_quantity_sets( - &mut writer, - element_id, - &entity.entity_id, - semantics, - &mut report, - ); - } - - report.exported_elements += 1; - } - - if element_ids.is_empty() && space_ids.is_empty() && site_ids.is_empty() { - return Err(IfcExportError::MeshGeneration( - "No elements were exported from the provided BREP inputs".to_string(), - )); - } - - if !element_ids.is_empty() { - writer.add_entity(format!( - "IFCRELCONTAINEDINSPATIALSTRUCTURE('{}',$,'ContainedInStorey',$,({}),{})", - ifc_guid("rel-contained-storey"), - join_refs(&element_ids), - Part21Writer::reference(storey) - )); - } - if !space_ids.is_empty() { - writer.add_entity(format!( - "IFCRELAGGREGATES('{}',$,$,$,{},({}))", - ifc_guid("rel-storey-spaces"), - Part21Writer::reference(storey), - join_refs(&space_ids) - )); - } - if !site_ids.is_empty() { - writer.add_entity(format!( - "IFCRELAGGREGATES('{}',$,$,$,{},({}))", - ifc_guid("rel-project-element-sites"), - Part21Writer::reference(project), - join_refs(&site_ids) - )); - } - - let text = writer.build().map_err(IfcExportError::Serialization)?; - Ok((text, report)) -} - -/// D9: emits an analytic `IFCADVANCEDBREP` shape representation for a brep whose -/// faces carry analytic surfaces (D1) — IFCPLANE / IFCCYLINDRICALSURFACE faces -/// with IFCLINE / IFCCIRCLE edge curves. Returns the IFCSHAPEREPRESENTATION ref -/// and the analytic face count, or `None` if no advanced face could be built. -fn emit_ifc_advanced_brep( - writer: &mut Part21Writer, - brep: &Brep, - scale: f64, - geom_context: usize, -) -> Option<(usize, usize)> { - use crate::brep::SurfaceGeometry; - - let mut edge_curves: HashMap = HashMap::new(); - let mut vertex_points: HashMap = HashMap::new(); - let mut surfaces: HashMap = HashMap::new(); - let mut face_ids = Vec::new(); - - let scaled = |v: Vector3| Vector3::new(v.x * scale, v.y * scale, v.z * scale); - - for face in &brep.faces { - let Some(surface) = face.surface.as_ref() else { - return None; // mixed analytic/non-analytic — fall back to tessellation - }; - - let surface_ref = { - let key = ifc_surface_key(surface, scale); - if let Some(existing) = surfaces.get(&key) { - *existing - } else { - let id = match surface { - SurfaceGeometry::Plane { origin, normal } => { - let placement = ifc_axis_placement( - writer, - scaled(*origin), - *normal, - ifc_any_perpendicular(*normal), - ); - writer - .add_entity(format!("IFCPLANE({})", Part21Writer::reference(placement))) - } - SurfaceGeometry::Cylinder { - origin, - axis, - ref_direction, - radius, - .. - } => { - let placement = - ifc_axis_placement(writer, scaled(*origin), *axis, *ref_direction); - writer.add_entity(format!( - "IFCCYLINDRICALSURFACE({},{})", - Part21Writer::reference(placement), - format_ifc_real(radius * scale) - )) - } - }; - surfaces.insert(key, id); - id - } - }; - - let mut bounds = Vec::new(); - let outer = ifc_edge_loop_bound( - writer, - brep, - face.outer_loop, - scale, - true, - &mut edge_curves, - &mut vertex_points, - )?; - bounds.push(outer); - for inner in &face.inner_loops { - if let Some(b) = ifc_edge_loop_bound( - writer, - brep, - *inner, - scale, - false, - &mut edge_curves, - &mut vertex_points, - ) { - bounds.push(b); - } - } - - face_ids.push(writer.add_entity(format!( - "IFCADVANCEDFACE({},{},.T.)", - format_ifc_ref_list(&bounds), - Part21Writer::reference(surface_ref) - ))); - } - - if face_ids.is_empty() { - return None; - } - - let shell = writer.add_entity(format!( - "IFCCLOSEDSHELL({})", - format_ifc_ref_list(&face_ids) - )); - let advanced_brep = writer.add_entity(format!( - "IFCADVANCEDBREP({})", - Part21Writer::reference(shell) - )); - let rep = writer.add_entity(format!( - "IFCSHAPEREPRESENTATION({},'Body','AdvancedBrep',({}))", - Part21Writer::reference(geom_context), - Part21Writer::reference(advanced_brep) - )); - Some((rep, face_ids.len())) -} - -fn ifc_edge_loop_bound( - writer: &mut Part21Writer, - brep: &Brep, - loop_id: u32, - scale: f64, - is_outer: bool, - edge_curves: &mut HashMap, - vertex_points: &mut HashMap, -) -> Option { - let halfedges = brep.get_loop_halfedges(loop_id).ok()?; - if halfedges.len() < 3 { - return None; - } - let mut oriented = Vec::with_capacity(halfedges.len()); - for he_id in halfedges { - let he = brep.halfedges.get(he_id as usize)?; - let edge_curve = ifc_edge_curve(writer, brep, he.edge, scale, edge_curves, vertex_points)?; - oriented.push(writer.add_entity(format!( - "IFCORIENTEDEDGE(*,*,{},.T.)", - Part21Writer::reference(edge_curve) - ))); - } - let edge_loop = writer.add_entity(format!("IFCEDGELOOP({})", format_ifc_ref_list(&oriented))); - let kind = if is_outer { - "IFCFACEOUTERBOUND" - } else { - "IFCFACEBOUND" - }; - Some(writer.add_entity(format!( - "{}({},.T.)", - kind, - Part21Writer::reference(edge_loop) - ))) -} - -fn ifc_edge_curve( - writer: &mut Part21Writer, - brep: &Brep, - edge_id: u32, - scale: f64, - edge_curves: &mut HashMap, - vertex_points: &mut HashMap, -) -> Option { - use crate::brep::CurveGeometry; - if let Some(existing) = edge_curves.get(&edge_id) { - return Some(*existing); - } - let (from_id, to_id) = brep.get_edge_endpoints(edge_id)?; - let from_pos = scaled_v(brep.vertices.get(from_id as usize)?.position, scale); - let to_pos = scaled_v(brep.vertices.get(to_id as usize)?.position, scale); - let v_from = ifc_vertex_point(writer, from_id, from_pos, vertex_points); - let v_to = ifc_vertex_point(writer, to_id, to_pos, vertex_points); - - let edge = brep.edges.iter().find(|e| e.id == edge_id); - let curve_ref = match edge.and_then(|e| e.curve.as_ref()) { - Some(CurveGeometry::Circle { - center, - normal, - x_axis, - radius, - .. - }) => { - let placement = ifc_axis_placement(writer, scaled_v(*center, scale), *normal, *x_axis); - writer.add_entity(format!( - "IFCCIRCLE({},{})", - Part21Writer::reference(placement), - format_ifc_real(radius * scale) - )) - } - _ => { - let dir = ifc_direction_between(from_pos, to_pos); - let d = writer.add_entity(format!( - "IFCDIRECTION(({},{},{}))", - format_ifc_real(dir.x), - format_ifc_real(dir.y), - format_ifc_real(dir.z) - )); - let vector = writer.add_entity(format!( - "IFCVECTOR({},{})", - Part21Writer::reference(d), - format_ifc_real(ifc_distance(from_pos, to_pos).max(1.0)) - )); - let point = writer.add_entity(format!( - "IFCCARTESIANPOINT(({},{},{}))", - format_ifc_real(from_pos.x), - format_ifc_real(from_pos.y), - format_ifc_real(from_pos.z) - )); - writer.add_entity(format!( - "IFCLINE({},{})", - Part21Writer::reference(point), - Part21Writer::reference(vector) - )) - } - }; - - let edge_curve = writer.add_entity(format!( - "IFCEDGECURVE({},{},{},.T.)", - Part21Writer::reference(v_from), - Part21Writer::reference(v_to), - Part21Writer::reference(curve_ref) - )); - edge_curves.insert(edge_id, edge_curve); - Some(edge_curve) -} - -fn ifc_vertex_point( - writer: &mut Part21Writer, - vertex_id: u32, - position: Vector3, - cache: &mut HashMap, -) -> usize { - if let Some(existing) = cache.get(&vertex_id) { - return *existing; - } - let point = writer.add_entity(format!( - "IFCCARTESIANPOINT(({},{},{}))", - format_ifc_real(position.x), - format_ifc_real(position.y), - format_ifc_real(position.z) - )); - let vp = writer.add_entity(format!( - "IFCVERTEXPOINT({})", - Part21Writer::reference(point) - )); - cache.insert(vertex_id, vp); - vp -} - -fn ifc_axis_placement( - writer: &mut Part21Writer, - location: Vector3, - axis: Vector3, - ref_direction: Vector3, -) -> usize { - let point = writer.add_entity(format!( - "IFCCARTESIANPOINT(({},{},{}))", - format_ifc_real(location.x), - format_ifc_real(location.y), - format_ifc_real(location.z) - )); - let axis = ifc_normalize(axis); - let refd = ifc_normalize(ref_direction); - let axis_dir = writer.add_entity(format!( - "IFCDIRECTION(({},{},{}))", - format_ifc_real(axis.x), - format_ifc_real(axis.y), - format_ifc_real(axis.z) - )); - let ref_dir = writer.add_entity(format!( - "IFCDIRECTION(({},{},{}))", - format_ifc_real(refd.x), - format_ifc_real(refd.y), - format_ifc_real(refd.z) - )); - writer.add_entity(format!( - "IFCAXIS2PLACEMENT3D({},{},{})", - Part21Writer::reference(point), - Part21Writer::reference(axis_dir), - Part21Writer::reference(ref_dir) - )) -} - -fn scaled_v(v: Vector3, scale: f64) -> Vector3 { - Vector3::new(v.x * scale, v.y * scale, v.z * scale) -} - -fn ifc_direction_between(from: Vector3, to: Vector3) -> Vector3 { - ifc_normalize(Vector3::new(to.x - from.x, to.y - from.y, to.z - from.z)) -} - -fn ifc_distance(a: Vector3, b: Vector3) -> f64 { - let dx = a.x - b.x; - let dy = a.y - b.y; - let dz = a.z - b.z; - (dx * dx + dy * dy + dz * dz).sqrt() -} - -fn ifc_normalize(v: Vector3) -> Vector3 { - let len = (v.x * v.x + v.y * v.y + v.z * v.z).sqrt(); - if len <= IFC_LENGTH_EPSILON { - Vector3::new(0.0, 0.0, 1.0) - } else { - Vector3::new(v.x / len, v.y / len, v.z / len) - } -} - -fn ifc_any_perpendicular(n: Vector3) -> Vector3 { - let n = ifc_normalize(n); - if n.x.abs() <= n.y.abs() && n.x.abs() <= n.z.abs() { - ifc_normalize(Vector3::new(0.0, -n.z, n.y)) - } else if n.y.abs() <= n.z.abs() { - ifc_normalize(Vector3::new(-n.z, 0.0, n.x)) - } else { - ifc_normalize(Vector3::new(-n.y, n.x, 0.0)) - } -} - -fn ifc_surface_key(surface: &crate::brep::SurfaceGeometry, scale: f64) -> String { - use crate::brep::SurfaceGeometry; - match surface { - SurfaceGeometry::Plane { origin, normal } => format!( - "P|{:.6}|{:.6}|{:.6}|{:.6}|{:.6}|{:.6}", - origin.x * scale, - origin.y * scale, - origin.z * scale, - normal.x, - normal.y, - normal.z - ), - SurfaceGeometry::Cylinder { - origin, - axis, - radius, - .. - } => format!( - "C|{:.6}|{:.6}|{:.6}|{:.6}|{:.6}|{:.6}|{:.6}", - origin.x * scale, - origin.y * scale, - origin.z * scale, - axis.x, - axis.y, - axis.z, - radius * scale - ), - } -} - -fn format_ifc_ref_list(ids: &[usize]) -> String { - format!( - "({})", - ids.iter() - .map(|id| Part21Writer::reference(*id)) - .collect::>() - .join(",") - ) -} - -fn format_ifc_real(value: f64) -> String { - let mut out = format!("{:.9}", value); - while out.contains('.') && out.ends_with('0') { - out.pop(); - } - if out.ends_with('.') { - out.push('0'); - } - out -} - -fn validate_config(config: &IfcExportConfig) -> Result { - if !config.scale.is_finite() || config.scale <= 0.0 { - return Err(IfcExportError::MeshGeneration( - "IFC scale must be a finite positive value".to_string(), - )); - } - Ok(config.scale) -} - -fn is_closed_solid(brep: &Brep) -> bool { - if brep.faces.is_empty() || brep.edges.is_empty() { - return false; - } - - if !brep.shells.is_empty() && brep.shells.iter().all(|shell| !shell.is_closed) { - return false; - } - - brep.edges.iter().all(|edge| edge.twin_halfedge.is_some()) -} - -fn triangulate_entity_mesh( - entity: &IfcOwnedEntity<'_>, - scale: f64, - policy: IfcErrorPolicy, - report: &mut IfcExportReport, - label: String, -) -> Result { - let mut points = Vec::::new(); - let mut point_map = HashMap::::new(); - let mut faces = Vec::<[usize; 3]>::new(); - - for face in &entity.brep.faces { - report.input_faces += 1; - - let (outer_vertices, holes_vertices) = - entity.brep.get_vertices_and_holes_by_face_id(face.id); - - if outer_vertices.len() < 3 { - if policy == IfcErrorPolicy::Strict { - return Err(IfcExportError::MeshGeneration(format!( - "{} face {} has fewer than 3 vertices", - label, face.id - ))); - } - report.skipped_faces += 1; - continue; - } - - if holes_vertices.iter().any(|hole| hole.len() < 3) { - if policy == IfcErrorPolicy::Strict { - return Err(IfcExportError::MeshGeneration(format!( - "{} face {} has invalid hole loops", - label, face.id - ))); - } - report.skipped_faces += 1; - continue; - } - - let triangle_indices = triangulate_polygon_with_holes(&outer_vertices, &holes_vertices); - if triangle_indices.is_empty() { - if policy == IfcErrorPolicy::Strict { - return Err(IfcExportError::MeshGeneration(format!( - "{} face {} produced no triangles", - label, face.id - ))); - } - report.skipped_faces += 1; - continue; - } - - let mut all_vertices = outer_vertices; - for hole in holes_vertices { - all_vertices.extend(hole); - } - - let mut face_has_triangle = false; - - for triangle in triangle_indices { - let Some((&a, &b, &c)) = all_vertices - .get(triangle[0]) - .zip(all_vertices.get(triangle[1])) - .zip(all_vertices.get(triangle[2])) - .map(|((a, b), c)| (a, b, c)) - else { - if policy == IfcErrorPolicy::Strict { - return Err(IfcExportError::MeshGeneration(format!( - "{} face {} emitted out-of-range triangle indices", - label, face.id - ))); - } - continue; - }; - - if !is_finite_vec3(a) || !is_finite_vec3(b) || !is_finite_vec3(c) { - if policy == IfcErrorPolicy::Strict { - return Err(IfcExportError::MeshGeneration(format!( - "{} face {} has non-finite coordinates", - label, face.id - ))); - } - continue; - } - - let scaled = [ - Vector3::new(a.x * scale, a.y * scale, a.z * scale), - Vector3::new(b.x * scale, b.y * scale, b.z * scale), - Vector3::new(c.x * scale, c.y * scale, c.z * scale), - ]; - - if is_degenerate_triangle(scaled[0], scaled[1], scaled[2]) { - if policy == IfcErrorPolicy::Strict { - return Err(IfcExportError::MeshGeneration(format!( - "{} face {} contains degenerate triangle", - label, face.id - ))); - } - continue; - } - - let i0 = get_or_create_mesh_point(&mut points, &mut point_map, scaled[0]); - let i1 = get_or_create_mesh_point(&mut points, &mut point_map, scaled[1]); - let i2 = get_or_create_mesh_point(&mut points, &mut point_map, scaled[2]); - - faces.push([i0 + 1, i1 + 1, i2 + 1]); - face_has_triangle = true; - } - - if !face_has_triangle { - if policy == IfcErrorPolicy::Strict { - return Err(IfcExportError::MeshGeneration(format!( - "{} face {} yielded no valid triangles", - label, face.id - ))); - } - report.skipped_faces += 1; - } - } - - Ok(TessellatedMesh { points, faces }) -} - -fn get_or_create_mesh_point( - points: &mut Vec, - point_map: &mut HashMap, - point: Vector3, -) -> usize { - let key = format!("{:.9}|{:.9}|{:.9}", point.x, point.y, point.z); - if let Some(index) = point_map.get(&key) { - return *index; - } - - let index = points.len(); - points.push(point); - point_map.insert(key, index); - index -} - -fn resolve_ifc_class( - entity_id: &str, - semantics: Option<&IfcEntitySemantics>, - config: &IfcExportConfig, - report: &mut IfcExportReport, -) -> Result<&'static str, IfcExportError> { - let Some(semantics) = semantics else { - return Ok(IFC_CLASS_PROXY); - }; - - let Some(raw_class) = semantics.ifc_class.as_ref() else { - return Ok(IFC_CLASS_PROXY); - }; - - let normalized = raw_class.trim().to_ascii_uppercase(); - if let Some(class_name) = IFC_ALLOWED_CLASSES - .iter() - .find(|candidate| **candidate == normalized) - .copied() - { - report.semantics_applied += 1; - return Ok(class_name); - } - - if config.error_policy == IfcErrorPolicy::Strict { - return Err(IfcExportError::InvalidSemantics(format!( - "Entity '{}' requested unsupported ifc_class '{}'. Allowed classes: {}", - entity_id, - raw_class, - IFC_ALLOWED_CLASSES.join(", ") - ))); - } - - report.proxy_fallbacks += 1; - Ok(IFC_CLASS_PROXY) -} - -fn write_property_sets( - writer: &mut Part21Writer, - element_id: usize, - entity_id: &str, - semantics: &IfcEntitySemantics, - report: &mut IfcExportReport, -) { - // Sorted iteration: HashMap order is arbitrary and the SPF output must be - // deterministic across runs. - let mut set_names: Vec<&String> = semantics.property_sets.keys().collect(); - set_names.sort(); - for set_name in set_names { - let properties = &semantics.property_sets[set_name]; - if properties.is_empty() { - continue; - } - - let mut property_names: Vec<&String> = properties.keys().collect(); - property_names.sort(); - let mut property_ids = Vec::new(); - for property_name in property_names { - let property_value = &properties[property_name]; - let literal = match property_value { - IfcPropertyValue::Bool(value) => { - format!("IFCBOOLEAN({})", if *value { ".T." } else { ".F." }) - } - IfcPropertyValue::Number(value) => format!("IFCREAL({})", format_real(*value)), - IfcPropertyValue::Text(value) => { - format!("IFCTEXT('{}')", sanitize_string_literal(value)) - } - }; - let property = writer.add_entity(format!( - "IFCPROPERTYSINGLEVALUE('{}',$,{},$)", - sanitize_string_literal(property_name), - literal - )); - property_ids.push(property); - } - - let property_set = writer.add_entity(format!( - "IFCPROPERTYSET('{}',$,'{}',$,({}))", - ifc_guid(&format!("pset-{}-{}", entity_id, set_name)), - sanitize_string_literal(set_name), - join_refs(&property_ids) - )); - - writer.add_entity(format!( - "IFCRELDEFINESBYPROPERTIES('{}',$,$,$,({}),{})", - ifc_guid(&format!("pset-rel-{}-{}", entity_id, set_name)), - Part21Writer::reference(element_id), - Part21Writer::reference(property_set) - )); - - report.property_sets_written += 1; - } -} - -fn write_quantity_sets( - writer: &mut Part21Writer, - element_id: usize, - entity_id: &str, - semantics: &IfcEntitySemantics, - report: &mut IfcExportReport, -) { - let mut set_names: Vec<&String> = semantics.quantity_sets.keys().collect(); - set_names.sort(); - for set_name in set_names { - let quantities = &semantics.quantity_sets[set_name]; - if quantities.is_empty() { - continue; - } - - let mut quantity_names: Vec<&String> = quantities.keys().collect(); - quantity_names.sort(); - let mut quantity_ids = Vec::new(); - for quantity_name in quantity_names { - let quantity = writer.add_entity(format!( - "IFCQUANTITYLENGTH('{}',$,$,{},$)", - sanitize_string_literal(quantity_name), - format_real(quantities[quantity_name]) - )); - quantity_ids.push(quantity); - } - - let quantity_set = writer.add_entity(format!( - "IFCELEMENTQUANTITY('{}',$,'{}',$,$,({}))", - ifc_guid(&format!("qset-{}-{}", entity_id, set_name)), - sanitize_string_literal(set_name), - join_refs(&quantity_ids) - )); - - writer.add_entity(format!( - "IFCRELDEFINESBYPROPERTIES('{}',$,$,$,({}),{})", - ifc_guid(&format!("qset-rel-{}-{}", entity_id, set_name)), - Part21Writer::reference(element_id), - Part21Writer::reference(quantity_set) - )); - - report.quantity_sets_written += 1; - } -} - -fn join_refs(ids: &[usize]) -> String { - ids.iter() - .map(|id| Part21Writer::reference(*id)) - .collect::>() - .join(",") -} - -fn format_real(value: f64) -> String { - let mut out = format!("{:.9}", value); - while out.contains('.') && out.ends_with('0') { - out.pop(); - } - if out.ends_with('.') { - out.push('0'); - } - out -} - -fn format_ifc_coord_list(points: &[Vector3]) -> String { - let coords = points - .iter() - .map(|point| { - format!( - "({},{},{})", - format_real(point.x), - format_real(point.y), - format_real(point.z) - ) - }) - .collect::>() - .join(","); - - format!("({})", coords) -} - -fn format_ifc_face_index_list(faces: &[[usize; 3]]) -> String { - let entries = faces - .iter() - .map(|face| format!("({},{},{})", face[0], face[1], face[2])) - .collect::>() - .join(","); - - format!("({})", entries) -} - -fn is_finite_vec3(point: Vector3) -> bool { - point.x.is_finite() && point.y.is_finite() && point.z.is_finite() -} - -fn is_degenerate_triangle(a: Vector3, b: Vector3, c: Vector3) -> bool { - let ab = [b.x - a.x, b.y - a.y, b.z - a.z]; - let ac = [c.x - a.x, c.y - a.y, c.z - a.z]; - - let cross = [ - ab[1] * ac[2] - ab[2] * ac[1], - ab[2] * ac[0] - ab[0] * ac[2], - ab[0] * ac[1] - ab[1] * ac[0], - ]; - - let area_sq = cross[0] * cross[0] + cross[1] * cross[1] + cross[2] * cross[2]; - !area_sq.is_finite() || area_sq <= IFC_LENGTH_EPSILON -} - -fn ifc_guid(seed: &str) -> String { - const IFC_CHARS: &[u8; 64] = - b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$"; - - let uuid = Uuid::new_v5(&Uuid::NAMESPACE_URL, seed.as_bytes()); - let mut number = u128::from_be_bytes(*uuid.as_bytes()); - let mut out = [b'0'; 22]; - - for index in (0..22).rev() { - out[index] = IFC_CHARS[(number & 63) as usize]; - number >>= 6; - } - - String::from_utf8(out.to_vec()).unwrap_or_else(|_| "0000000000000000000000".to_string()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::brep::BrepBuilder; - - fn tetrahedron_brep() -> Brep { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - builder.add_vertices(&[ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.5, 0.8660254, 0.0), - Vector3::new(0.5, 0.2886751, 0.8164966), - ]); - - builder.add_face(&[0, 2, 1], &[]).unwrap(); - builder.add_face(&[0, 1, 3], &[]).unwrap(); - builder.add_face(&[1, 2, 3], &[]).unwrap(); - builder.add_face(&[2, 0, 3], &[]).unwrap(); - - builder.build().unwrap() - } - - #[test] - fn exports_ifc_spf_document() { - let brep = tetrahedron_brep(); - let (text, report) = - export_brep_to_ifc_text(&brep, &IfcExportConfig::default()).expect("ifc export"); - - assert!(text.starts_with("ISO-10303-21;")); - assert!(text.contains("FILE_SCHEMA(('IFC4'));")); - assert!(text.contains("IFCPROJECT(")); - assert!(text.contains("IFCTRIANGULATEDFACESET(")); - assert!(report.exported_elements >= 1); - assert!(report.exported_triangles >= 4); - } - - #[test] - fn cylinder_exports_analytic_ifc_advanced_brep() { - // D9: a cylinder (analytic surfaces present) exports as an - // IFCADVANCEDBREP with one IFCCYLINDRICALSURFACE + circle edges, not a - // triangulated face set. - use crate::primitives::cylinder::OGCylinder; - let mut cyl = OGCylinder::new("ifc-cyl".into()); - cyl.set_config( - Vector3::new(0.0, 0.0, 0.0), - 1.0, - 2.0, - 2.0 * std::f64::consts::PI, - 24, - ) - .unwrap(); - let brep = cyl.world_brep(); - - let (text, _) = - export_brep_to_ifc_text(&brep, &IfcExportConfig::default()).expect("ifc export"); - - assert!(text.contains("IFCADVANCEDBREP(")); - assert_eq!(text.matches("IFCCYLINDRICALSURFACE(").count(), 1); - assert!(text.contains("IFCCIRCLE(")); - assert!(text.contains("IFCPLANE(")); - assert!(!text.contains("IFCTRIANGULATEDFACESET(")); - } - - /// A tetrahedron whose apex is at source (1, 3, 2) so the Y-up/Z-up - /// distinction is unambiguous (no zero components on the apex). - fn oriented_tetrahedron() -> Brep { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - builder.add_vertices(&[ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(4.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 4.0), - Vector3::new(1.0, 3.0, 2.0), - ]); - builder.add_face(&[0, 2, 1], &[]).unwrap(); - builder.add_face(&[0, 1, 3], &[]).unwrap(); - builder.add_face(&[1, 2, 3], &[]).unwrap(); - builder.add_face(&[2, 0, 3], &[]).unwrap(); - builder.build().unwrap() - } - - #[test] - fn up_axis_conversion_default_maps_y_to_z() { - // Default config has up_axis_conversion = true: source apex - // (1, 3, 2) -> IFC (1, -2, 3). - let brep = oriented_tetrahedron(); - let (text, _) = - export_brep_to_ifc_text(&brep, &IfcExportConfig::default()).expect("ifc export"); - assert!( - text.contains("(1.0,-2.0,3.0)"), - "apex should be Z-up (1,-2,3); got:\n{}", - text - ); - assert!( - !text.contains("(1.0,3.0,2.0)"), - "source Y-up apex must not appear when conversion is on" - ); - } - - #[test] - fn up_axis_conversion_off_preserves_source_coordinates() { - // Flag off: geometry is emitted exactly as authored. - let brep = oriented_tetrahedron(); - let config = IfcExportConfig { - up_axis_conversion: false, - ..IfcExportConfig::default() - }; - let (text, _) = export_brep_to_ifc_text(&brep, &config).expect("ifc export"); - assert!( - text.contains("(1.0,3.0,2.0)"), - "apex should be unchanged (1,3,2) when conversion is off; got:\n{}", - text - ); - assert!(!text.contains("(1.0,-2.0,3.0)")); - } - - #[test] - fn up_axis_conversion_config_defaults_true() { - assert!(IfcExportConfig::default().up_axis_conversion); - } - - #[test] - fn applies_semantics_class_when_supported() { - let brep = tetrahedron_brep(); - - let mut semantics = HashMap::new(); - semantics.insert( - "brep-0".to_string(), - IfcEntitySemantics { - ifc_class: Some("IFCWALL".to_string()), - name: Some("Wall A".to_string()), - ..IfcEntitySemantics::default() - }, - ); - - let config = IfcExportConfig { - semantics: Some(semantics), - ..IfcExportConfig::default() - }; - - let (text, report) = export_brep_to_ifc_text(&brep, &config).expect("ifc export"); - assert!(text.contains("IFCWALL(")); - assert_eq!(report.semantics_applied, 1); - } - - #[test] - fn strict_rejects_invalid_ifc_class() { - let brep = tetrahedron_brep(); - - let mut semantics = HashMap::new(); - semantics.insert( - "brep-0".to_string(), - IfcEntitySemantics { - ifc_class: Some("IFCUNKNOWN".to_string()), - ..IfcEntitySemantics::default() - }, - ); - - let config = IfcExportConfig { - semantics: Some(semantics), - error_policy: IfcErrorPolicy::Strict, - ..IfcExportConfig::default() - }; - - let result = export_brep_to_ifc_text(&brep, &config); - assert!(result.is_err()); - } - - /// IFCSPACE exports as a real space — its own attribute signature, - /// AGGREGATED under the storey rather than contained in it — instead of - /// degrading to a building element proxy. - #[test] - fn exports_space_as_aggregated_spatial_element() { - let brep = tetrahedron_brep(); - - let mut semantics = HashMap::new(); - semantics.insert( - "brep-0".to_string(), - IfcEntitySemantics { - ifc_class: Some("IfcSpace".to_string()), - name: Some("Living Room".to_string()), - object_type: Some("residential".to_string()), - ..IfcEntitySemantics::default() - }, - ); - - let config = IfcExportConfig { - semantics: Some(semantics), - error_policy: IfcErrorPolicy::Strict, - ..IfcExportConfig::default() - }; - - let (text, report) = export_brep_to_ifc_text(&brep, &config).expect("ifc export"); - assert!(text.contains("IFCSPACE("), "space class applied"); - assert!( - !text.contains("IFCBUILDINGELEMENTPROXY("), - "no proxy fallback" - ); - assert!( - !text.contains("IFCRELCONTAINEDINSPATIALSTRUCTURE("), - "a lone space is not contained-in-storey" - ); - assert!( - text.contains("'residential'"), - "object type (program/usage) survives" - ); - // Storey → space aggregation exists (beyond the 3 scaffold aggregates). - assert_eq!(text.matches("IFCRELAGGREGATES(").count(), 4); - assert_eq!(report.semantics_applied, 1); - assert_eq!(report.proxy_fallbacks, 0); - } - - /// IFCSITE exports with the site attribute signature, aggregated under the - /// project (a project may hold several sites). - #[test] - fn exports_site_aggregated_under_project() { - let brep = tetrahedron_brep(); - - let mut semantics = HashMap::new(); - semantics.insert( - "brep-0".to_string(), - IfcEntitySemantics { - ifc_class: Some("IFCSITE".to_string()), - name: Some("Parcel 12".to_string()), - ..IfcEntitySemantics::default() - }, - ); - - let config = IfcExportConfig { - semantics: Some(semantics), - error_policy: IfcErrorPolicy::Strict, - ..IfcExportConfig::default() - }; - - let (text, _) = export_brep_to_ifc_text(&brep, &config).expect("ifc export"); - // Scaffold site + the exported parcel site. - assert_eq!(text.matches("IFCSITE(").count(), 2); - assert!(text.contains("'Parcel 12'")); - assert!(!text.contains("IFCRELCONTAINEDINSPATIALSTRUCTURE(")); - } - - /// Property sets carry TYPED values (IFCREAL / IFCBOOLEAN / IFCTEXT) and - /// the writer's output is deterministic (sorted set / property names). - #[test] - fn writes_typed_property_sets() { - let brep = tetrahedron_brep(); - - let mut properties = HashMap::new(); - properties.insert("gfaM2".to_string(), IfcPropertyValue::Number(4500.25)); - properties.insert( - "program".to_string(), - IfcPropertyValue::Text("office".to_string()), - ); - properties.insert("compliant".to_string(), IfcPropertyValue::Bool(true)); - let mut property_sets = HashMap::new(); - property_sets.insert("OpenPlans_Yield".to_string(), properties); - - let mut quantities = HashMap::new(); - quantities.insert("NetSideArea".to_string(), 3735.5); - let mut quantity_sets = HashMap::new(); - quantity_sets.insert("Qto_SpaceBaseQuantities".to_string(), quantities); - - let mut semantics = HashMap::new(); - semantics.insert( - "brep-0".to_string(), - IfcEntitySemantics { - ifc_class: Some("IFCSPACE".to_string()), - property_sets, - quantity_sets, - ..IfcEntitySemantics::default() - }, - ); - - let config = IfcExportConfig { - semantics: Some(semantics), - error_policy: IfcErrorPolicy::Strict, - ..IfcExportConfig::default() - }; - - let (text, report) = export_brep_to_ifc_text(&brep, &config).expect("ifc export"); - assert!(text.contains("IFCPROPERTYSINGLEVALUE('gfaM2',$,IFCREAL(4500.25),$)")); - assert!(text.contains("IFCPROPERTYSINGLEVALUE('program',$,IFCTEXT('office'),$)")); - assert!(text.contains("IFCPROPERTYSINGLEVALUE('compliant',$,IFCBOOLEAN(.T.),$)")); - assert!(text.contains("IFCPROPERTYSET(")); - assert!(text.contains("IFCELEMENTQUANTITY(")); - assert!(text.contains("IFCRELDEFINESBYPROPERTIES(")); - assert_eq!(report.property_sets_written, 1); - assert_eq!(report.quantity_sets_written, 1); - - let (again, _) = export_brep_to_ifc_text( - &brep, - &IfcExportConfig { - semantics: config.semantics.clone(), - error_policy: IfcErrorPolicy::Strict, - ..IfcExportConfig::default() - }, - ) - .expect("ifc export"); - assert_eq!(text, again, "SPF output is deterministic across runs"); - } - - /// Legacy string-only property-set JSON still parses (untagged enum). - #[test] - fn legacy_string_property_sets_still_parse() { - let json = r#"{ - "ifc_class": "IFCWALL", - "property_sets": { "Custom": { "material": "brick", "rating": "A" } } - }"#; - let semantics: IfcEntitySemantics = - serde_json::from_str(json).expect("legacy semantics parse"); - let set = &semantics.property_sets["Custom"]; - assert_eq!(set["material"], IfcPropertyValue::Text("brick".to_string())); - - let typed = r#"{ "property_sets": { "Yield": { "gfa": 12.5, "ok": true } } }"#; - let semantics: IfcEntitySemantics = - serde_json::from_str(typed).expect("typed semantics parse"); - let set = &semantics.property_sets["Yield"]; - assert_eq!(set["gfa"], IfcPropertyValue::Number(12.5)); - assert_eq!(set["ok"], IfcPropertyValue::Bool(true)); - } -} diff --git a/main/opengeometry/src/export/mod.rs b/main/opengeometry/src/export/mod.rs deleted file mode 100644 index af63e4c..0000000 --- a/main/opengeometry/src/export/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -pub mod ifc; -pub mod part21; -pub mod projection; -pub mod step; -pub mod stl; -pub mod wasm; - -#[cfg(not(target_arch = "wasm32"))] -pub mod pdf; - -pub use ifc::{ - export_brep_to_ifc_text, export_breps_to_ifc_text, export_scene_entities_to_ifc_text, - IfcEntityInput, IfcEntitySemantics, IfcErrorPolicy, IfcExportConfig, IfcExportError, - IfcExportReport, IfcPropertyValue, IfcSchemaVersion, -}; -pub use projection::{ - project_brep_to_scene, CameraParameters, ClassifiedSegment, EdgeClass, HlrOptions, Line2D, - Path2D, ProjectionMode, Scene2D, Scene2DLines, Segment2D, Vec2, -}; -pub use step::{ - export_brep_to_step_text, export_breps_to_step_text, StepErrorPolicy, StepExportConfig, - StepExportError, StepExportReport, StepSchema, -}; -pub use stl::{ - export_brep_to_stl_bytes, export_breps_to_stl_bytes, StlErrorPolicy, StlExportConfig, - StlExportError, StlExportReport, -}; - -#[cfg(not(target_arch = "wasm32"))] -pub use ifc::{ - export_brep_to_ifc_file, export_breps_to_ifc_file, export_scene_entities_to_ifc_file, -}; -#[cfg(not(target_arch = "wasm32"))] -pub use step::{export_brep_to_step_file, export_breps_to_step_file}; -#[cfg(not(target_arch = "wasm32"))] -pub use stl::{export_brep_to_stl_file, export_breps_to_stl_file}; diff --git a/main/opengeometry/src/export/part21.rs b/main/opengeometry/src/export/part21.rs deleted file mode 100644 index 047f71d..0000000 --- a/main/opengeometry/src/export/part21.rs +++ /dev/null @@ -1,242 +0,0 @@ -use std::collections::HashSet; - -const DEFAULT_PART21_VERSION: &str = "2;1"; - -#[derive(Clone, Debug)] -pub struct Part21Writer { - schema: String, - description: String, - file_name: String, - timestamp: String, - author: String, - organization: String, - preprocessor: String, - originating_system: String, - authorization: String, - next_id: usize, - entries: Vec<(usize, String)>, -} - -impl Part21Writer { - pub fn new(schema: impl Into) -> Self { - Self { - schema: sanitize_string_literal(&schema.into()), - description: "OpenGeometry Export".to_string(), - file_name: "opengeometry-export".to_string(), - timestamp: "1970-01-01T00:00:00".to_string(), - author: "OpenGeometry".to_string(), - organization: "OpenGeometry".to_string(), - preprocessor: "OpenGeometry".to_string(), - originating_system: "OpenGeometry".to_string(), - authorization: String::new(), - next_id: 1, - entries: Vec::new(), - } - } - - pub fn set_description(&mut self, description: impl Into) { - self.description = sanitize_string_literal(&description.into()); - } - - pub fn set_file_name(&mut self, file_name: impl Into) { - self.file_name = sanitize_string_literal(&file_name.into()); - } - - pub fn set_timestamp(&mut self, timestamp: impl Into) { - self.timestamp = sanitize_string_literal(×tamp.into()); - } - - pub fn set_author(&mut self, author: impl Into) { - self.author = sanitize_string_literal(&author.into()); - } - - pub fn set_organization(&mut self, organization: impl Into) { - self.organization = sanitize_string_literal(&organization.into()); - } - - pub fn set_preprocessor(&mut self, preprocessor: impl Into) { - self.preprocessor = sanitize_string_literal(&preprocessor.into()); - } - - pub fn set_originating_system(&mut self, originating_system: impl Into) { - self.originating_system = sanitize_string_literal(&originating_system.into()); - } - - pub fn set_authorization(&mut self, authorization: impl Into) { - self.authorization = sanitize_string_literal(&authorization.into()); - } - - pub fn add_entity(&mut self, expression: impl Into) -> usize { - let id = self.next_id; - self.next_id += 1; - self.entries.push((id, expression.into())); - id - } - - pub fn reference(id: usize) -> String { - format!("#{}", id) - } - - pub fn build(self) -> Result { - if self.entries.is_empty() { - return Err("Part-21 writer has no DATA entities".to_string()); - } - - let mut defined = HashSet::new(); - for (id, _) in &self.entries { - if !defined.insert(*id) { - return Err(format!("Duplicate Part-21 entity id detected: #{}", id)); - } - } - - for (id, expression) in &self.entries { - for reference in extract_references(expression) { - if !defined.contains(&reference) { - return Err(format!( - "Part-21 entity #{} references undefined id #{}", - id, reference - )); - } - } - } - - let mut output = String::new(); - output.push_str("ISO-10303-21;\n"); - output.push_str("HEADER;\n"); - output.push_str(&format!( - "FILE_DESCRIPTION(('{}'),'{}');\n", - self.description, DEFAULT_PART21_VERSION - )); - output.push_str(&format!( - "FILE_NAME('{}','{}',('{}'),('{}'),'{}','{}','{}');\n", - self.file_name, - self.timestamp, - self.author, - self.organization, - self.preprocessor, - self.originating_system, - self.authorization - )); - output.push_str(&format!("FILE_SCHEMA(('{}'));\n", self.schema)); - output.push_str("ENDSEC;\n"); - output.push_str("DATA;\n"); - - for (id, expression) in &self.entries { - output.push_str(&format!("#{}={};\n", id, expression)); - } - - output.push_str("ENDSEC;\n"); - output.push_str("END-ISO-10303-21;\n"); - Ok(output) - } -} - -pub fn sanitize_string_literal(value: &str) -> String { - let mut sanitized = String::with_capacity(value.len()); - for ch in value.chars() { - match ch { - '\'' => sanitized.push_str("''"), - '\n' | '\r' => sanitized.push(' '), - _ if ch.is_ascii() => sanitized.push(ch), - _ => sanitized.push('?'), - } - } - sanitized -} - -fn extract_references(expression: &str) -> Vec { - let bytes = expression.as_bytes(); - let mut refs = Vec::new(); - let mut idx = 0; - let mut quoted = false; - - while idx < bytes.len() { - if bytes[idx] == b'\'' { - if quoted && bytes.get(idx + 1) == Some(&b'\'') { - idx += 2; - continue; - } - quoted = !quoted; - idx += 1; - continue; - } - if quoted { - idx += 1; - continue; - } - if bytes[idx] != b'#' { - idx += 1; - continue; - } - - idx += 1; - let start = idx; - while idx < bytes.len() && bytes[idx].is_ascii_digit() { - idx += 1; - } - - if idx > start { - let raw = &expression[start..idx]; - if let Ok(id) = raw.parse::() { - refs.push(id); - } - } - } - - refs -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn builds_deterministic_part21_document() { - let mut writer = Part21Writer::new("AUTOMOTIVE_DESIGN"); - writer.set_file_name("test"); - let p1 = writer.add_entity("CARTESIAN_POINT('',(0.,0.,0.))"); - let p2 = writer.add_entity("CARTESIAN_POINT('',(1.,0.,0.))"); - writer.add_entity(format!( - "POLY_LOOP('',({},{},{}))", - Part21Writer::reference(p1), - Part21Writer::reference(p2), - Part21Writer::reference(p1) - )); - - let text = writer.build().expect("part21 should build"); - assert!(text.starts_with("ISO-10303-21;")); - assert!(text.contains("FILE_SCHEMA(('AUTOMOTIVE_DESIGN'));")); - assert!(text.contains("#1=CARTESIAN_POINT")); - assert!(text.contains("#2=CARTESIAN_POINT")); - assert!(text.ends_with("END-ISO-10303-21;\n")); - } - - #[test] - fn fails_for_unresolved_reference() { - let mut writer = Part21Writer::new("IFC4"); - writer.add_entity("IFCPROJECT('x',#999,$,$,$,$,$,$)"); - let err = writer - .build() - .expect_err("writer should reject unresolved refs"); - assert!(err.contains("undefined id #999")); - } - - #[test] - fn sanitizes_non_ascii_literals() { - let raw = "A'B\nCø"; - let sanitized = sanitize_string_literal(raw); - assert_eq!(sanitized, "A''B C?"); - } - - #[test] - fn ignores_reference_like_text_inside_escaped_literals() { - let mut writer = Part21Writer::new("AUTOMOTIVE_DESIGN"); - let point = writer.add_entity("CARTESIAN_POINT('body #999''s point',(0.,0.,0.))"); - writer.add_entity(format!("VERTEX_POINT('#888',#{point})")); - assert!(writer.build().is_ok()); - assert_eq!( - extract_references("X('a #999''b',#12,'#88',#3)"), - vec![12, 3] - ); - } -} diff --git a/main/opengeometry/src/export/pdf.rs b/main/opengeometry/src/export/pdf.rs deleted file mode 100644 index 4651605..0000000 --- a/main/opengeometry/src/export/pdf.rs +++ /dev/null @@ -1,294 +0,0 @@ -use crate::brep::Brep; -use crate::export::projection::{ - project_brep_to_scene, CameraParameters, ClassifiedSegment, EdgeClass, HlrOptions, Scene2D, - Segment2D, Vec2, -}; -use printpdf::*; -use std::fs::File; -use std::io::BufWriter; - -const METERS_TO_MM: f64 = 1000.0; -const DEFAULT_MARGIN_MM: f64 = 10.0; - -/// ISO 128 line weight in mm per edge class. -fn iso128_line_width_mm(class: EdgeClass) -> f64 { - match class { - EdgeClass::VisibleOutline => 0.50, - EdgeClass::VisibleCrease => 0.25, - EdgeClass::VisibleSmooth => 0.18, - EdgeClass::Hidden => 0.18, - EdgeClass::SectionCut => 0.70, - } -} - -#[derive(Clone, Debug)] -pub struct PdfExportConfig { - pub page_width_mm: f64, - pub page_height_mm: f64, - pub margin_mm: f64, - /// Ignored — line widths are now determined by ISO 128 edge class. - #[deprecated(note = "Line width is now set per-class via ISO 128. Use EdgeClass instead.")] - pub line_width_mm: f64, - pub auto_fit: bool, - pub title: Option, -} - -#[allow(deprecated)] -impl Default for PdfExportConfig { - fn default() -> Self { - Self { - page_width_mm: 297.0, - page_height_mm: 210.0, - margin_mm: DEFAULT_MARGIN_MM, - line_width_mm: 0.25, - auto_fit: true, - title: None, - } - } -} - -#[allow(deprecated)] -impl PdfExportConfig { - pub fn a4_portrait() -> Self { - Self { - page_width_mm: 210.0, - page_height_mm: 297.0, - ..Default::default() - } - } - - pub fn a4_landscape() -> Self { - Self::default() - } - - pub fn a3_landscape() -> Self { - Self { - page_width_mm: 420.0, - page_height_mm: 297.0, - ..Default::default() - } - } - - pub fn custom(width_mm: f64, height_mm: f64) -> Self { - Self { - page_width_mm: width_mm, - page_height_mm: height_mm, - ..Default::default() - } - } -} - -pub type PdfExportResult = Result; - -#[derive(Debug)] -pub enum PdfExportError { - DocumentCreation(String), - FileWrite(String), - EmptyScene, - InvalidConfig(String), -} - -impl std::fmt::Display for PdfExportError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - PdfExportError::DocumentCreation(msg) => write!(f, "PDF creation error: {}", msg), - PdfExportError::FileWrite(msg) => write!(f, "File write error: {}", msg), - PdfExportError::EmptyScene => write!(f, "Cannot export empty scene"), - PdfExportError::InvalidConfig(msg) => write!(f, "Invalid config: {}", msg), - } - } -} - -impl std::error::Error for PdfExportError {} - -pub fn export_scene_to_pdf(scene: &Scene2D, file_path: &str) -> PdfExportResult<()> { - export_scene_to_pdf_with_config(scene, file_path, &PdfExportConfig::default()) -} - -pub fn export_brep_to_pdf_with_camera( - brep: &Brep, - camera: &CameraParameters, - hlr: &HlrOptions, - file_path: &str, - config: &PdfExportConfig, -) -> PdfExportResult<()> { - let scene = project_brep_to_scene(brep, camera, hlr); - export_scene_to_pdf_with_config(&scene, file_path, config) -} - -pub fn export_scene_to_pdf_with_config( - scene: &Scene2D, - file_path: &str, - config: &PdfExportConfig, -) -> PdfExportResult<()> { - let (doc, page1, layer1, scale, offset_x, offset_y) = build_pdf_document(scene, config)?; - - let layer = doc.get_page(page1).get_layer(layer1); - draw_classified_segments(&layer, scene.segments(), scale, offset_x, offset_y); - - let file = File::create(file_path).map_err(|e| PdfExportError::FileWrite(e.to_string()))?; - let mut writer = BufWriter::new(file); - doc.save(&mut writer) - .map_err(|e| PdfExportError::FileWrite(e.to_string()))?; - - Ok(()) -} - -pub fn export_scene_to_pdf_bytes( - scene: &Scene2D, - config: &PdfExportConfig, -) -> PdfExportResult> { - let (doc, page1, layer1, scale, offset_x, offset_y) = build_pdf_document(scene, config)?; - - let layer = doc.get_page(page1).get_layer(layer1); - draw_classified_segments(&layer, scene.segments(), scale, offset_x, offset_y); - - doc.save_to_bytes() - .map_err(|e| PdfExportError::DocumentCreation(e.to_string())) -} - -#[allow(clippy::type_complexity)] -fn build_pdf_document( - scene: &Scene2D, - config: &PdfExportConfig, -) -> PdfExportResult<( - PdfDocumentReference, - PdfPageIndex, - PdfLayerIndex, - f64, - f64, - f64, -)> { - if scene.is_empty() { - return Err(PdfExportError::EmptyScene); - } - - let drawable_width_mm = config.page_width_mm - 2.0 * config.margin_mm; - let drawable_height_mm = config.page_height_mm - 2.0 * config.margin_mm; - - if drawable_width_mm <= 0.0 || drawable_height_mm <= 0.0 { - return Err(PdfExportError::InvalidConfig( - "Margins too large for page size".to_string(), - )); - } - - let scene_bounds = scene.bounding_box().ok_or(PdfExportError::EmptyScene)?; - let scene_width = scene_bounds.1.x - scene_bounds.0.x; - let scene_height = scene_bounds.1.y - scene_bounds.0.y; - - let scale = if config.auto_fit && (scene_width > 0.0 || scene_height > 0.0) { - let scale_x = if scene_width > 0.0 { - drawable_width_mm / (scene_width * METERS_TO_MM) - } else { - 1.0 - }; - let scale_y = if scene_height > 0.0 { - drawable_height_mm / (scene_height * METERS_TO_MM) - } else { - 1.0 - }; - scale_x.min(scale_y) - } else { - 1.0 - }; - - let scaled_width = scene_width * METERS_TO_MM * scale; - let scaled_height = scene_height * METERS_TO_MM * scale; - let offset_x = config.margin_mm + (drawable_width_mm - scaled_width) / 2.0 - - scene_bounds.0.x * METERS_TO_MM * scale; - let offset_y = config.margin_mm + (drawable_height_mm - scaled_height) / 2.0 - - scene_bounds.0.y * METERS_TO_MM * scale; - - let title = config - .title - .clone() - .or_else(|| scene.name.clone()) - .unwrap_or_else(|| "OpenGeometry Export".to_string()); - - let (doc, page1, layer1) = PdfDocument::new( - &title, - Mm(config.page_width_mm), - Mm(config.page_height_mm), - "Layer 1", - ); - - Ok((doc, page1, layer1, scale, offset_x, offset_y)) -} - -fn draw_classified_segments( - layer: &PdfLayerReference, - segments: &[ClassifiedSegment], - scale: f64, - offset_x: f64, - offset_y: f64, -) { - layer.set_outline_color(Color::Rgb(Rgb::new(0.0, 0.0, 0.0, None))); - - for seg in segments { - let line_width = iso128_line_width_mm(seg.class); - layer.set_outline_thickness(line_width); - - match &seg.geometry { - Segment2D::Line { start, end } => { - let start_mm = transform_point(start, scale, offset_x, offset_y); - let end_mm = transform_point(end, scale, offset_x, offset_y); - - let line = Line { - points: vec![ - (Point::new(Mm(start_mm.0), Mm(start_mm.1)), false), - (Point::new(Mm(end_mm.0), Mm(end_mm.1)), false), - ], - is_closed: false, - has_fill: false, - has_stroke: true, - is_clipping_path: false, - }; - - layer.add_shape(line); - } - // Arc, Ellipse, CubicBezier segments are produced in Phase 2+. - _ => {} - } - } -} - -fn transform_point(point: &Vec2, scale: f64, offset_x: f64, offset_y: f64) -> (f64, f64) { - ( - point.x * METERS_TO_MM * scale + offset_x, - point.y * METERS_TO_MM * scale + offset_y, - ) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_transform_point() { - let point = Vec2::new(1.0, 2.0); - let result = transform_point(&point, 1.0, 10.0, 20.0); - assert!((result.0 - 1010.0).abs() < 0.001); - assert!((result.1 - 2020.0).abs() < 0.001); - } - - #[test] - fn test_config_defaults() { - #[allow(deprecated)] - let config = PdfExportConfig::default(); - assert_eq!(config.page_width_mm, 297.0); - assert_eq!(config.page_height_mm, 210.0); - } - - #[test] - fn iso128_line_widths_are_within_standard() { - let widths = [ - iso128_line_width_mm(EdgeClass::VisibleOutline), - iso128_line_width_mm(EdgeClass::VisibleCrease), - iso128_line_width_mm(EdgeClass::Hidden), - iso128_line_width_mm(EdgeClass::SectionCut), - ]; - for w in widths { - assert!(w >= 0.13 && w <= 2.0, "width {w} outside ISO 128 range"); - } - } -} diff --git a/main/opengeometry/src/export/projection.rs b/main/opengeometry/src/export/projection.rs deleted file mode 100644 index 93faa78..0000000 --- a/main/opengeometry/src/export/projection.rs +++ /dev/null @@ -1,996 +0,0 @@ -use std::collections::HashMap; - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; - -use crate::{ - analytic::{ - tessellation::tessellate as tessellate_analytic, BrepEnvelope, GeometryError, Surface, - }, - brep::Brep, -}; - -const EPSILON: f64 = crate::tolerance::MODELING_TOLERANCE_FLOOR; -const CREASE_COS_THRESHOLD: f64 = 0.9995; - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub enum ProjectionMode { - Orthographic, - Perspective, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct CameraParameters { - pub position: Vector3, - pub target: Vector3, - pub up: Vector3, - pub near: f64, - pub projection_mode: ProjectionMode, -} - -impl Default for CameraParameters { - fn default() -> Self { - Self { - position: Vector3::new(3.0, 3.0, 3.0), - target: Vector3::new(0.0, 0.0, 0.0), - up: Vector3::new(0.0, 1.0, 0.0), - near: 0.01, - projection_mode: ProjectionMode::Orthographic, - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct HlrOptions { - pub hide_hidden_edges: bool, -} - -impl Default for HlrOptions { - fn default() -> Self { - Self { - hide_hidden_edges: true, - } - } -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -pub struct Vec2 { - pub x: f64, - pub y: f64, -} - -impl Vec2 { - pub fn new(x: f64, y: f64) -> Self { - Self { x, y } - } -} - -/// ISO 128 edge classification. Drives line weight and line type in export. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum EdgeClass { - /// Silhouette: front-face edge adjacent to a back-facing face. Continuous 0.50 mm. - VisibleOutline, - /// Hard crease: two front-facing faces whose normals diverge (cos < 0.9995). Continuous 0.25 mm. - VisibleCrease, - /// Smooth interior edge between co-planar front faces. Hidden by default (opt-in). - VisibleSmooth, - /// Occluded by front-facing geometry. Dashed 0.18 mm when shown. - Hidden, - /// Intersects a section plane. Chain thick 0.70 mm. - SectionCut, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum Segment2D { - Line { - start: Vec2, - end: Vec2, - }, - Arc { - center: Vec2, - radius: f64, - start_angle: f64, - end_angle: f64, - }, - Ellipse { - center: Vec2, - rx: f64, - ry: f64, - rotation: f64, - start_angle: f64, - end_angle: f64, - }, - CubicBezier { - p0: Vec2, - p1: Vec2, - p2: Vec2, - p3: Vec2, - }, -} - -/// One classified output segment from the HLR projection. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ClassifiedSegment { - pub geometry: Segment2D, - pub class: EdgeClass, - /// AIA/NCS layer code (e.g. "A-WALL"). Populated by WorldGraph projection from the node kind. - pub layer: Option, - /// Id of the WorldGraph node whose body produced the segment. - pub source_entity_id: Option, -} - -/// Backward-compat path type. Retained for callers that rely on Path2D structure. -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct Path2D { - pub segments: Vec, - pub stroke_width: Option, - pub stroke_color: Option<(f64, f64, f64)>, -} - -impl Path2D { - pub fn new() -> Self { - Self::default() - } - - pub fn with_segments(segments: Vec) -> Self { - Self { - segments, - ..Self::default() - } - } - - pub fn is_empty(&self) -> bool { - self.segments.is_empty() - } - - pub fn push_segment(&mut self, segment: Segment2D) { - self.segments.push(segment); - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct Scene2D { - pub name: Option, - pub segments: Vec, -} - -/// Flat line representation used by the existing WASM `projectTo2DLines` API. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Line2D { - pub start: Vec2, - pub end: Vec2, - pub stroke_width: Option, - pub stroke_color: Option<(f64, f64, f64)>, - /// ISO 128 edge class serialised as a string (e.g. "VisibleOutline"). - #[serde(skip_serializing_if = "Option::is_none")] - pub class: Option, -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct Scene2DLines { - pub name: Option, - pub lines: Vec, -} - -impl Scene2D { - pub fn new() -> Self { - Self::default() - } - - pub fn with_name(name: impl Into) -> Self { - Self { - name: Some(name.into()), - ..Self::default() - } - } - - pub fn is_empty(&self) -> bool { - self.segments.is_empty() - } - - pub fn add_segment(&mut self, seg: ClassifiedSegment) { - self.segments.push(seg); - } - - /// Backward-compat: convert Path2D into classified segments (class = VisibleCrease). - pub fn add_path(&mut self, path: Path2D) { - for geom in path.segments { - self.segments.push(ClassifiedSegment { - geometry: geom, - class: EdgeClass::VisibleCrease, - layer: None, - source_entity_id: None, - }); - } - } - - pub fn segments(&self) -> &[ClassifiedSegment] { - &self.segments - } - - /// Backward-compat: materialise a Vec from classified segments. - pub fn paths(&self) -> Vec { - self.segments - .iter() - .map(|seg| Path2D::with_segments(vec![seg.geometry.clone()])) - .collect() - } - - pub fn extend(&mut self, other: Scene2D) { - self.segments.extend(other.segments); - } - - pub fn bounding_box(&self) -> Option<(Vec2, Vec2)> { - let mut min_x = f64::INFINITY; - let mut min_y = f64::INFINITY; - let mut max_x = f64::NEG_INFINITY; - let mut max_y = f64::NEG_INFINITY; - let mut has_data = false; - - for seg in &self.segments { - let Some((lo, hi)) = segment_bounds(&seg.geometry) else { - continue; - }; - min_x = min_x.min(lo.x); - min_y = min_y.min(lo.y); - max_x = max_x.max(hi.x); - max_y = max_y.max(hi.y); - has_data = true; - } - - if has_data { - Some((Vec2::new(min_x, min_y), Vec2::new(max_x, max_y))) - } else { - None - } - } - - /// Flatten to Line2D for the existing `projectTo2DLines` WASM path. - pub fn to_lines(&self) -> Scene2DLines { - let mut lines = Vec::new(); - for seg in &self.segments { - if let Segment2D::Line { start, end } = seg.geometry { - lines.push(Line2D { - start, - end, - stroke_width: None, - stroke_color: None, - class: Some(edge_class_str(seg.class).to_string()), - }); - } - } - Scene2DLines { - name: self.name.clone(), - lines, - } - } -} - -fn edge_class_str(class: EdgeClass) -> &'static str { - match class { - EdgeClass::VisibleOutline => "VisibleOutline", - EdgeClass::VisibleCrease => "VisibleCrease", - EdgeClass::VisibleSmooth => "VisibleSmooth", - EdgeClass::Hidden => "Hidden", - EdgeClass::SectionCut => "SectionCut", - } -} - -fn segment_bounds(seg: &Segment2D) -> Option<(Vec2, Vec2)> { - match seg { - Segment2D::Line { start, end } => Some(( - Vec2::new(start.x.min(end.x), start.y.min(end.y)), - Vec2::new(start.x.max(end.x), start.y.max(end.y)), - )), - Segment2D::Arc { - center, - radius, - start_angle, - end_angle, - } => { - let pts = [ - Vec2::new( - center.x + radius * start_angle.cos(), - center.y + radius * start_angle.sin(), - ), - Vec2::new( - center.x + radius * end_angle.cos(), - center.y + radius * end_angle.sin(), - ), - ]; - points_bounds(&pts) - } - Segment2D::Ellipse { - center, - rx, - ry, - rotation, - start_angle, - end_angle, - } => { - let cos_r = rotation.cos(); - let sin_r = rotation.sin(); - let ellipse_pt = |a: f64| { - let ex = rx * a.cos(); - let ey = ry * a.sin(); - Vec2::new( - center.x + ex * cos_r - ey * sin_r, - center.y + ex * sin_r + ey * cos_r, - ) - }; - let pts = [ellipse_pt(*start_angle), ellipse_pt(*end_angle)]; - points_bounds(&pts) - } - Segment2D::CubicBezier { p0, p1, p2, p3 } => points_bounds(&[*p0, *p1, *p2, *p3]), - } -} - -fn points_bounds(pts: &[Vec2]) -> Option<(Vec2, Vec2)> { - if pts.is_empty() { - return None; - } - let mut min_x = f64::INFINITY; - let mut min_y = f64::INFINITY; - let mut max_x = f64::NEG_INFINITY; - let mut max_y = f64::NEG_INFINITY; - for p in pts { - min_x = min_x.min(p.x); - min_y = min_y.min(p.y); - max_x = max_x.max(p.x); - max_y = max_y.max(p.y); - } - Some((Vec2::new(min_x, min_y), Vec2::new(max_x, max_y))) -} - -#[derive(Clone, Copy, Debug)] -struct CameraFrame { - position: [f64; 3], - right: [f64; 3], - up: [f64; 3], - forward: [f64; 3], - near: f64, - mode: ProjectionMode, -} - -#[derive(Clone, Copy, Debug)] -struct ViewPoint { - x: f64, - y: f64, - z: f64, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -struct EdgeKey { - a: u32, - b: u32, -} - -impl EdgeKey { - fn new(v1: u32, v2: u32) -> Option { - if v1 == v2 { - return None; - } - - let (a, b) = if v1 < v2 { (v1, v2) } else { (v2, v1) }; - Some(Self { a, b }) - } -} - -#[derive(Clone, Copy, Debug)] -struct EdgeCandidate { - id: u32, - a: u32, - b: u32, -} - -#[derive(Clone, Copy, Debug)] -struct FaceInfo { - front_facing: bool, - normal: [f64; 3], -} - -pub fn project_brep_to_scene(brep: &Brep, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let mut scene = Scene2D::with_name(format!("BRep {}", brep.id)); - if brep.vertices.is_empty() { - return scene; - } - - let Some(frame) = build_camera_frame(camera) else { - return scene; - }; - - let face_info = compute_face_info(brep, &frame); - let adjacency = build_edge_adjacency(brep); - let candidates = collect_candidate_edges(brep); - let source_id = brep.id.to_string(); - - for edge in candidates { - if !is_edge_vertex_index_valid(edge.a, edge.b, brep.vertices.len()) { - continue; - } - - let class = classify_edge(edge.id, &adjacency, &face_info); - let should_emit = match class { - EdgeClass::VisibleSmooth => false, - EdgeClass::Hidden => !hlr.hide_hidden_edges, - _ => true, - }; - if !should_emit { - continue; - } - - let start_world = &brep.vertices[edge.a as usize].position; - let end_world = &brep.vertices[edge.b as usize].position; - - let start_view = world_to_view(start_world, &frame); - let end_view = world_to_view(end_world, &frame); - let Some((start_clipped, end_clipped)) = - clip_segment_to_near_plane(start_view, end_view, frame.near) - else { - continue; - }; - - let Some(start_2d) = project_view_point(start_clipped, frame.mode) else { - continue; - }; - let Some(end_2d) = project_view_point(end_clipped, frame.mode) else { - continue; - }; - - if is_zero_length_2d(start_2d, end_2d) { - continue; - } - - scene.add_segment(ClassifiedSegment { - geometry: Segment2D::Line { - start: start_2d, - end: end_2d, - }, - class, - layer: None, - source_entity_id: Some(source_id.clone()), - }); - } - - scene -} - -pub fn project_analytic_brep_to_scene( - brep: &BrepEnvelope, - camera: &CameraParameters, - hlr: &HlrOptions, - deflection: f64, - max_triangles: usize, -) -> Result { - let mut scene = Scene2D::with_name(format!("Analytic BRep {}", brep.id)); - let Some(frame) = build_camera_frame(camera) else { - return Err(GeometryError::InvalidGeometry( - "projection camera is degenerate".into(), - )); - }; - let tessellation = tessellate_analytic(brep, deflection, max_triangles)?; - if tessellation.outline_positions.len() != tessellation.outline_edge_ids.len() * 6 { - return Err(GeometryError::InvalidTopology( - "analytic outline mapping is inconsistent".into(), - )); - } - let mut adjacent_faces: HashMap> = HashMap::new(); - for halfedge in &brep.topology.halfedges { - if let Some(face) = halfedge.face { - let faces = adjacent_faces.entry(halfedge.edge).or_default(); - if !faces.contains(&face) { - faces.push(face); - } - } - } - for (segment, &edge_id) in tessellation - .outline_positions - .chunks_exact(6) - .zip(&tessellation.outline_edge_ids) - { - let start = [segment[0], segment[1], segment[2]]; - let end = [segment[3], segment[4], segment[5]]; - let midpoint = std::array::from_fn(|axis| (start[axis] + end[axis]) * 0.5); - let mut face_info = Vec::new(); - for &face_id in adjacent_faces.get(&edge_id).into_iter().flatten() { - let face = brep.topology.faces.get(face_id as usize).ok_or_else(|| { - GeometryError::InvalidTopology("analytic edge references a missing face".into()) - })?; - let surface = brep.geometry.surface(face.surface)?; - let uv = surface.project(midpoint, None)?; - let mut normal = surface.normal_at(uv)?; - normal = mul_scalar(normal, face.sense.multiplier()); - face_info.push(FaceInfo { - front_facing: dot(normal, sub(frame.position, midpoint)) > 0.0, - normal, - }); - } - let class = classify_face_info(&face_info); - if class == EdgeClass::VisibleSmooth - || (class == EdgeClass::Hidden && hlr.hide_hidden_edges) - { - continue; - } - let start_view = array_to_view(start, &frame); - let end_view = array_to_view(end, &frame); - let Some((start_clipped, end_clipped)) = - clip_segment_to_near_plane(start_view, end_view, frame.near) - else { - continue; - }; - let (Some(start), Some(end)) = ( - project_view_point(start_clipped, frame.mode), - project_view_point(end_clipped, frame.mode), - ) else { - continue; - }; - if is_zero_length_2d(start, end) { - continue; - } - scene.add_segment(ClassifiedSegment { - geometry: Segment2D::Line { start, end }, - class, - layer: None, - source_entity_id: Some(brep.id.clone()), - }); - } - Ok(scene) -} - -fn classify_edge( - edge_id: u32, - adjacency: &HashMap>, - face_info: &[FaceInfo], -) -> EdgeClass { - let adjacent_faces = adjacency.get(&edge_id).cloned().unwrap_or_default(); - - let faces: Vec = adjacent_faces - .iter() - .filter_map(|&fi| face_info.get(fi).copied()) - .collect(); - - classify_face_info(&faces) -} - -fn classify_face_info(adjacent_faces: &[FaceInfo]) -> EdgeClass { - if adjacent_faces.is_empty() { - return EdgeClass::VisibleCrease; - } - - let front_faces: Vec = adjacent_faces - .iter() - .copied() - .filter(|face| face.front_facing) - .collect(); - - if front_faces.is_empty() { - return EdgeClass::Hidden; - } - - if front_faces.len() < adjacent_faces.len() { - return EdgeClass::VisibleOutline; - } - - if has_crease(&front_faces) { - EdgeClass::VisibleCrease - } else { - EdgeClass::VisibleSmooth - } -} - -fn build_camera_frame(camera: &CameraParameters) -> Option { - let position = vec3_to_arr(&camera.position); - let target = vec3_to_arr(&camera.target); - let up_hint = vec3_to_arr(&camera.up); - let near = camera.near.max(EPSILON); - - let forward = normalize(sub(target, position))?; - let right = normalize(cross(forward, up_hint)) - .or_else(|| normalize(cross(forward, [0.0, 1.0, 0.0]))) - .or_else(|| normalize(cross(forward, [1.0, 0.0, 0.0])))?; - let up = normalize(cross(right, forward))?; - - Some(CameraFrame { - position, - right, - up, - forward, - near, - mode: camera.projection_mode, - }) -} - -fn world_to_view(point: &Vector3, frame: &CameraFrame) -> ViewPoint { - array_to_view(vec3_to_arr(point), frame) -} - -fn array_to_view(point: [f64; 3], frame: &CameraFrame) -> ViewPoint { - let relative = sub(point, frame.position); - ViewPoint { - x: dot(relative, frame.right), - y: dot(relative, frame.up), - z: dot(relative, frame.forward), - } -} - -fn clip_segment_to_near_plane( - mut start: ViewPoint, - mut end: ViewPoint, - near: f64, -) -> Option<(ViewPoint, ViewPoint)> { - if start.z < near && end.z < near { - return None; - } - - if start.z < near { - let denominator = end.z - start.z; - if denominator.abs() < EPSILON { - return None; - } - let t = (near - start.z) / denominator; - start = interpolate(start, end, t); - start.z = near; - } else if end.z < near { - let denominator = end.z - start.z; - if denominator.abs() < EPSILON { - return None; - } - let t = (near - start.z) / denominator; - end = interpolate(start, end, t); - end.z = near; - } - - Some((start, end)) -} - -fn project_view_point(point: ViewPoint, mode: ProjectionMode) -> Option { - match mode { - ProjectionMode::Orthographic => Some(Vec2::new(point.x, point.y)), - ProjectionMode::Perspective => { - if point.z <= EPSILON { - return None; - } - Some(Vec2::new(point.x / point.z, point.y / point.z)) - } - } -} - -fn build_edge_adjacency(brep: &Brep) -> HashMap> { - let face_index_by_id: HashMap = brep - .faces - .iter() - .enumerate() - .map(|(index, face)| (face.id, index)) - .collect(); - - let mut adjacency: HashMap> = HashMap::new(); - - for edge in &brep.edges { - let mut adjacent_faces: Vec = Vec::new(); - - let mut candidate_halfedges = vec![edge.halfedge]; - if let Some(twin_halfedge) = edge.twin_halfedge { - candidate_halfedges.push(twin_halfedge); - } - - for halfedge_id in candidate_halfedges { - let Some(halfedge) = brep.halfedges.get(halfedge_id as usize) else { - continue; - }; - - if let Some(face_id) = halfedge.face { - if let Some(face_index) = face_index_by_id.get(&face_id) { - adjacent_faces.push(*face_index); - } - } - - if let Some(twin_id) = halfedge.twin { - if let Some(twin) = brep.halfedges.get(twin_id as usize) { - if let Some(face_id) = twin.face { - if let Some(face_index) = face_index_by_id.get(&face_id) { - adjacent_faces.push(*face_index); - } - } - } - } - } - - adjacent_faces.sort_unstable(); - adjacent_faces.dedup(); - adjacency.insert(edge.id, adjacent_faces); - } - - adjacency -} - -fn collect_candidate_edges(brep: &Brep) -> Vec { - let mut candidates = Vec::new(); - - for edge in &brep.edges { - let Some((v1, v2)) = brep.get_edge_endpoints(edge.id) else { - continue; - }; - - let Some(key) = EdgeKey::new(v1, v2) else { - continue; - }; - - candidates.push(EdgeCandidate { - id: edge.id, - a: key.a, - b: key.b, - }); - } - - candidates.sort_by_key(|edge| (edge.a, edge.b, edge.id)); - candidates.dedup_by_key(|edge| edge.id); - candidates -} - -fn compute_face_info(brep: &Brep, frame: &CameraFrame) -> Vec { - let mut info = Vec::with_capacity(brep.faces.len()); - for face in &brep.faces { - let Some((normal, center)) = compute_face_normal_and_center(brep, face) else { - info.push(FaceInfo { - front_facing: true, - normal: [0.0, 0.0, 0.0], - }); - continue; - }; - - let to_camera = sub(frame.position, center); - let front_facing = dot(normal, to_camera) > 0.0; - info.push(FaceInfo { - front_facing, - normal, - }); - } - info -} - -fn compute_face_normal_and_center( - brep: &Brep, - face: &crate::brep::Face, -) -> Option<([f64; 3], [f64; 3])> { - let (face_vertices, _) = brep.get_vertices_and_holes_by_face_id(face.id); - let points: Vec<[f64; 3]> = face_vertices.iter().map(vec3_to_arr).collect(); - - if points.len() < 3 { - return None; - } - - let mut center = [0.0, 0.0, 0.0]; - for point in &points { - center = add(center, *point); - } - center = mul_scalar(center, 1.0 / points.len() as f64); - - let default_normal = normalize(vec3_to_arr(&face.normal)); - if let Some(normal) = default_normal { - return Some((normal, center)); - } - - let p0 = points[0]; - for i in 1..(points.len() - 1) { - let edge_a = sub(points[i], p0); - let edge_b = sub(points[i + 1], p0); - let normal_candidate = cross(edge_a, edge_b); - if let Some(normal) = normalize(normal_candidate) { - return Some((normal, center)); - } - } - - None -} - -fn has_crease(front_faces: &[FaceInfo]) -> bool { - for i in 0..front_faces.len() { - for j in (i + 1)..front_faces.len() { - let dot_product = dot(front_faces[i].normal, front_faces[j].normal); - if dot_product < CREASE_COS_THRESHOLD { - return true; - } - } - } - - false -} - -fn is_edge_vertex_index_valid(a: u32, b: u32, vertex_count: usize) -> bool { - (a as usize) < vertex_count && (b as usize) < vertex_count -} - -fn is_zero_length_2d(start: Vec2, end: Vec2) -> bool { - let dx = end.x - start.x; - let dy = end.y - start.y; - dx * dx + dy * dy <= EPSILON * EPSILON -} - -fn interpolate(start: ViewPoint, end: ViewPoint, t: f64) -> ViewPoint { - ViewPoint { - x: start.x + (end.x - start.x) * t, - y: start.y + (end.y - start.y) * t, - z: start.z + (end.z - start.z) * t, - } -} - -fn vec3_to_arr(vec: &Vector3) -> [f64; 3] { - [vec.x, vec.y, vec.z] -} - -fn add(a: [f64; 3], b: [f64; 3]) -> [f64; 3] { - [a[0] + b[0], a[1] + b[1], a[2] + b[2]] -} - -fn sub(a: [f64; 3], b: [f64; 3]) -> [f64; 3] { - [a[0] - b[0], a[1] - b[1], a[2] - b[2]] -} - -fn mul_scalar(v: [f64; 3], s: f64) -> [f64; 3] { - [v[0] * s, v[1] * s, v[2] * s] -} - -fn dot(a: [f64; 3], b: [f64; 3]) -> f64 { - a[0] * b[0] + a[1] * b[1] + a[2] * b[2] -} - -fn cross(a: [f64; 3], b: [f64; 3]) -> [f64; 3] { - [ - a[1] * b[2] - a[2] * b[1], - a[2] * b[0] - a[0] * b[2], - a[0] * b[1] - a[1] * b[0], - ] -} - -fn normalize(v: [f64; 3]) -> Option<[f64; 3]> { - let len_sq = dot(v, v); - if len_sq <= EPSILON * EPSILON { - return None; - } - let inv = len_sq.sqrt().recip(); - Some([v[0] * inv, v[1] * inv, v[2] * inv]) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::brep::{Brep, BrepBuilder}; - use uuid::Uuid; - - #[test] - fn test_clip_segment_against_near_plane() { - let start = ViewPoint { - x: 0.0, - y: 0.0, - z: 0.5, - }; - let end = ViewPoint { - x: 1.0, - y: 0.0, - z: 2.0, - }; - let clipped = clip_segment_to_near_plane(start, end, 1.0).unwrap(); - - assert!((clipped.0.z - 1.0).abs() < 1.0e-9); - assert!((clipped.1.z - 2.0).abs() < 1.0e-9); - } - - #[test] - fn test_project_perspective() { - let point = ViewPoint { - x: 2.0, - y: 1.0, - z: 4.0, - }; - let projected = project_view_point(point, ProjectionMode::Perspective).unwrap(); - assert!((projected.x - 0.5).abs() < 1.0e-9); - assert!((projected.y - 0.25).abs() < 1.0e-9); - } - - #[test] - fn test_project_edge_only_brep() { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - builder.add_vertices(&[Vector3::new(-1.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)]); - builder.add_wire(&[0, 1], false).unwrap(); - let brep: Brep = builder.build().unwrap(); - - let camera = CameraParameters { - position: Vector3::new(0.0, 0.0, 5.0), - target: Vector3::new(0.0, 0.0, 0.0), - up: Vector3::new(0.0, 1.0, 0.0), - near: 0.01, - projection_mode: ProjectionMode::Orthographic, - }; - - let scene = project_brep_to_scene(&brep, &camera, &HlrOptions::default()); - assert!(!scene.is_empty()); - assert_eq!(scene.paths().len(), 1); - assert_eq!(scene.paths()[0].segments.len(), 1); - - let line_scene = scene.to_lines(); - assert_eq!(line_scene.lines.len(), 1); - } - - #[test] - fn analytic_projection_uses_v2_edges_and_face_normals() { - let brep = crate::analytic::primitives::cuboid( - "prism-volume".into(), - crate::analytic::Frame3::IDENTITY, - [4.0, 0.2, 3.0], - crate::analytic::topology::Accuracy { - geometric: 1.0e-8, - intersection: 1.0e-9, - tessellation: 0.01, - exchange: 1.0e-5, - }, - ) - .unwrap(); - let camera = CameraParameters { - position: Vector3::new(6.0, 5.0, 7.0), - target: Vector3::new(2.0, 0.1, 1.5), - up: Vector3::new(0.0, 0.0, 1.0), - near: 0.01, - projection_mode: ProjectionMode::Orthographic, - }; - let projected = - project_analytic_brep_to_scene(&brep, &camera, &HlrOptions::default(), 0.01, 10_000) - .unwrap(); - assert!(!projected.segments.is_empty()); - assert!(projected - .segments - .iter() - .any(|segment| segment.class == EdgeClass::VisibleOutline)); - assert!(projected.segments.iter().all(|segment| { - segment.source_entity_id.as_deref() == Some("prism-volume") - && matches!(segment.geometry, Segment2D::Line { .. }) - })); - } - - #[test] - fn classify_edge_returns_visible_crease_for_standalone_wire() { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - builder.add_vertices(&[Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)]); - builder.add_wire(&[0, 1], false).unwrap(); - let brep: Brep = builder.build().unwrap(); - - let camera = CameraParameters::default(); - let frame = build_camera_frame(&camera).unwrap(); - let face_info = compute_face_info(&brep, &frame); - let adjacency = build_edge_adjacency(&brep); - - for edge in &brep.edges { - let class = classify_edge(edge.id, &adjacency, &face_info); - assert_eq!(class, EdgeClass::VisibleCrease); - } - } - - #[test] - fn classified_segments_carry_source_entity_id() { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - builder.add_vertices(&[Vector3::new(-1.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)]); - builder.add_wire(&[0, 1], false).unwrap(); - let brep: Brep = builder.build().unwrap(); - let expected_id = brep.id.to_string(); - - let scene = - project_brep_to_scene(&brep, &CameraParameters::default(), &HlrOptions::default()); - assert!(!scene.segments.is_empty()); - for seg in &scene.segments { - assert_eq!(seg.source_entity_id.as_deref(), Some(expected_id.as_str())); - } - } - - #[test] - fn to_lines_carries_edge_class_string() { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - builder.add_vertices(&[Vector3::new(-1.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)]); - builder.add_wire(&[0, 1], false).unwrap(); - let brep: Brep = builder.build().unwrap(); - - let scene = - project_brep_to_scene(&brep, &CameraParameters::default(), &HlrOptions::default()); - let lines = scene.to_lines(); - assert!(!lines.lines.is_empty()); - assert!(lines.lines[0].class.is_some()); - } -} diff --git a/main/opengeometry/src/export/step.rs b/main/opengeometry/src/export/step.rs deleted file mode 100644 index 986b3a0..0000000 --- a/main/opengeometry/src/export/step.rs +++ /dev/null @@ -1,1204 +0,0 @@ -use std::collections::{HashMap, HashSet}; -use std::fmt; - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; - -use crate::brep::Brep; -use crate::operations::triangulate::triangulate_polygon_with_holes; - -use super::part21::{sanitize_string_literal, Part21Writer}; - -const STEP_LENGTH_EPSILON: f64 = 1.0e-12; - -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub enum StepErrorPolicy { - Strict, - BestEffort, -} - -impl Default for StepErrorPolicy { - fn default() -> Self { - Self::BestEffort - } -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub enum StepSchema { - AutomotiveDesign, -} - -impl Default for StepSchema { - fn default() -> Self { - Self::AutomotiveDesign - } -} - -impl StepSchema { - fn as_file_schema(self) -> &'static str { - match self { - Self::AutomotiveDesign => "AUTOMOTIVE_DESIGN", - } - } - - fn as_application_context(self) -> &'static str { - match self { - Self::AutomotiveDesign => "automotive_design", - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct StepExportConfig { - pub schema: StepSchema, - pub product_name: Option, - pub scale: f64, - pub error_policy: StepErrorPolicy, - pub validate_topology: bool, - pub require_closed_shell: bool, - /// D9: when true, faces carrying analytic surface geometry (plane, - /// cylinder) are exported as `ADVANCED_FACE`s on exact `PLANE` / - /// `CYLINDRICAL_SURFACE` with `LINE` / `CIRCLE` edge curves, instead of a - /// planar facet fan. Faces without analytic geometry still facet. - #[serde(default = "default_true")] - pub analytic_surfaces: bool, - /// Length unit emitted in the STEP unit context (D8). - #[serde(default)] - pub length_unit: crate::units::LengthUnit, -} - -fn default_true() -> bool { - true -} - -impl Default for StepExportConfig { - fn default() -> Self { - Self { - schema: StepSchema::default(), - product_name: Some("OpenGeometry STEP Export".to_string()), - scale: 1.0, - error_policy: StepErrorPolicy::BestEffort, - validate_topology: true, - require_closed_shell: true, - analytic_surfaces: true, - length_unit: crate::units::LengthUnit::default(), - } - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct StepExportReport { - pub input_breps: usize, - pub input_faces: usize, - pub exported_solids: usize, - pub exported_faces: usize, - pub exported_triangles: usize, - pub skipped_entities: usize, - pub skipped_faces: usize, - pub topology_errors: usize, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum StepExportError { - EmptyInput, - InvalidTopology(String), - UnsupportedEntity(String), - MeshGeneration(String), - Serialization(String), - Io(String), -} - -impl fmt::Display for StepExportError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - StepExportError::EmptyInput => write!(f, "No BREP input provided for STEP export"), - StepExportError::InvalidTopology(msg) => write!(f, "Invalid topology: {}", msg), - StepExportError::UnsupportedEntity(msg) => write!(f, "Unsupported BREP: {}", msg), - StepExportError::MeshGeneration(msg) => write!(f, "Mesh generation failed: {}", msg), - StepExportError::Serialization(msg) => { - write!(f, "STEP Part-21 serialization failed: {}", msg) - } - StepExportError::Io(msg) => write!(f, "STEP I/O failed: {}", msg), - } - } -} - -impl std::error::Error for StepExportError {} - -pub fn export_brep_to_step_text( - brep: &Brep, - config: &StepExportConfig, -) -> Result<(String, StepExportReport), StepExportError> { - export_breps_to_step_text([brep], config) -} - -pub fn export_breps_to_step_text<'a, I>( - breps: I, - config: &StepExportConfig, -) -> Result<(String, StepExportReport), StepExportError> -where - I: IntoIterator, -{ - let scale = validate_config(config)?; - let breps: Vec<&Brep> = breps.into_iter().collect(); - if breps.is_empty() { - return Err(StepExportError::EmptyInput); - } - - let mut report = StepExportReport { - input_breps: breps.len(), - ..StepExportReport::default() - }; - - let file_name = config - .product_name - .clone() - .unwrap_or_else(|| "opengeometry-export".to_string()); - let mut writer = Part21Writer::new(config.schema.as_file_schema()); - writer.set_description("OpenGeometry STEP Export"); - writer.set_file_name(file_name.clone()); - - let mut solid_ids = Vec::new(); - - for (brep_index, brep) in breps.iter().enumerate() { - if config.validate_topology { - if let Err(error) = brep.validate_topology() { - if config.error_policy == StepErrorPolicy::Strict { - return Err(StepExportError::InvalidTopology(format!( - "BREP {} failed validation: {}", - brep.id, error - ))); - } - report.topology_errors += 1; - report.skipped_entities += 1; - continue; - } - } - - if config.require_closed_shell && !is_closed_solid(brep) { - let message = format!("BREP {} is not a closed shell solid", brep.id); - if config.error_policy == StepErrorPolicy::Strict { - return Err(StepExportError::UnsupportedEntity(message)); - } - report.skipped_entities += 1; - continue; - } - - let mut point_map: HashMap = HashMap::new(); - let mut emitter = AnalyticEmitter::default(); - let mut face_ids = Vec::new(); - - // D9 topological reduction: merge faces sharing one cylindrical surface - // into a single ADVANCED_FACE bounded by its ring edges, so a cylinder - // exports as one analytic face rather than a fan of tagged facets. - let mut consumed: HashSet = HashSet::new(); - if config.analytic_surfaces { - let merged = emit_merged_cylinder_faces( - &mut writer, - &mut point_map, - &mut emitter, - brep, - scale, - &mut consumed, - ); - for face_ref in merged { - face_ids.push(face_ref); - report.exported_faces += 1; - } - } - - for face in &brep.faces { - if consumed.contains(&face.id) { - continue; - } - report.input_faces += 1; - if config.analytic_surfaces && face.surface.is_some() { - match emit_analytic_face( - &mut writer, - &mut point_map, - &mut emitter, - brep, - face, - scale, - ) { - Some(face_ref) => { - face_ids.push(face_ref); - report.exported_faces += 1; - continue; - } - None => { /* fall through to faceting */ } - } - } - - let triangles = triangulate_single_face( - brep, - face, - scale, - config.error_policy, - &mut report, - format!("BREP index {} face {}", brep_index, face.id), - )?; - for triangle in &triangles { - let p0 = get_or_create_point(&mut writer, &mut point_map, triangle[0]); - let p1 = get_or_create_point(&mut writer, &mut point_map, triangle[1]); - let p2 = get_or_create_point(&mut writer, &mut point_map, triangle[2]); - - let poly_loop = writer.add_entity(format!( - "POLY_LOOP('',({},{},{}))", - Part21Writer::reference(p0), - Part21Writer::reference(p1), - Part21Writer::reference(p2) - )); - let outer_bound = writer.add_entity(format!( - "FACE_OUTER_BOUND('',{},.T.)", - Part21Writer::reference(poly_loop) - )); - let face_ref = writer.add_entity(format!( - "FACE('',({}))", - Part21Writer::reference(outer_bound) - )); - face_ids.push(face_ref); - } - report.exported_triangles += triangles.len(); - report.exported_faces += triangles.len(); - } - - if face_ids.is_empty() { - if config.error_policy == StepErrorPolicy::Strict { - return Err(StepExportError::MeshGeneration(format!( - "BREP {} has no exportable faces", - brep.id - ))); - } - report.skipped_entities += 1; - continue; - } - - let shell = writer.add_entity(format!("CLOSED_SHELL('',({}))", join_refs(&face_ids))); - - let solid = writer.add_entity(format!( - "MANIFOLD_SOLID_BREP('{}',{})", - sanitize_string_literal(&format!("solid-{}", brep_index)), - Part21Writer::reference(shell) - )); - - report.exported_solids += 1; - solid_ids.push(solid); - } - - if solid_ids.is_empty() { - return Err(StepExportError::MeshGeneration( - "No solids were exported from the provided BREP inputs".to_string(), - )); - } - - let app_context = writer.add_entity(format!( - "APPLICATION_CONTEXT('{}')", - sanitize_string_literal(config.schema.as_application_context()) - )); - - writer.add_entity(format!( - "APPLICATION_PROTOCOL_DEFINITION('international standard','{}',2000,{})", - sanitize_string_literal(config.schema.as_application_context()), - Part21Writer::reference(app_context) - )); - - let product_context = writer.add_entity(format!( - "PRODUCT_CONTEXT('',{},'mechanical')", - Part21Writer::reference(app_context) - )); - - let product = writer.add_entity(format!( - "PRODUCT('{}','{}','',({}))", - sanitize_string_literal("opengeometry-product"), - sanitize_string_literal(&file_name), - Part21Writer::reference(product_context) - )); - - let product_formation = writer.add_entity(format!( - "PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',{},.NOT_KNOWN.)", - Part21Writer::reference(product) - )); - - writer.add_entity(format!( - "PRODUCT_RELATED_PRODUCT_CATEGORY('part','',({}))", - Part21Writer::reference(product) - )); - - let product_definition_context = writer.add_entity(format!( - "PRODUCT_DEFINITION_CONTEXT('part definition',{},'design')", - Part21Writer::reference(app_context) - )); - - let product_definition = writer.add_entity(format!( - "PRODUCT_DEFINITION('','',{}, {})", - Part21Writer::reference(product_formation), - Part21Writer::reference(product_definition_context) - )); - - let length_unit = emit_length_unit(&mut writer, config.length_unit); - let angle_unit = writer.add_entity("(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.))"); - let solid_angle_unit = - writer.add_entity("(NAMED_UNIT(*) SOLID_ANGLE_UNIT() SI_UNIT($,.STERADIAN.))"); - - let uncertainty = writer.add_entity(format!( - "UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-6),{},'distance_accuracy_value','confusion accuracy')", - Part21Writer::reference(length_unit) - )); - - let geometric_context = writer.add_entity(format!( - "( GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT(({})) GLOBAL_UNIT_ASSIGNED_CONTEXT(({}, {}, {})) REPRESENTATION_CONTEXT('','') )", - Part21Writer::reference(uncertainty), - Part21Writer::reference(length_unit), - Part21Writer::reference(angle_unit), - Part21Writer::reference(solid_angle_unit) - )); - - let shape_representation = writer.add_entity(format!( - "ADVANCED_BREP_SHAPE_REPRESENTATION('',({}),{})", - join_refs(&solid_ids), - Part21Writer::reference(geometric_context) - )); - - let product_shape = writer.add_entity(format!( - "PRODUCT_DEFINITION_SHAPE('','',{})", - Part21Writer::reference(product_definition) - )); - - writer.add_entity(format!( - "SHAPE_DEFINITION_REPRESENTATION({}, {})", - Part21Writer::reference(product_shape), - Part21Writer::reference(shape_representation) - )); - - let text = writer.build().map_err(StepExportError::Serialization)?; - Ok((text, report)) -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn export_brep_to_step_file( - brep: &Brep, - file_path: &str, - config: &StepExportConfig, -) -> Result { - let (text, report) = export_brep_to_step_text(brep, config)?; - std::fs::write(file_path, text).map_err(|err| StepExportError::Io(err.to_string()))?; - Ok(report) -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn export_breps_to_step_file<'a, I>( - breps: I, - file_path: &str, - config: &StepExportConfig, -) -> Result -where - I: IntoIterator, -{ - let (text, report) = export_breps_to_step_text(breps, config)?; - std::fs::write(file_path, text).map_err(|err| StepExportError::Io(err.to_string()))?; - Ok(report) -} - -fn validate_config(config: &StepExportConfig) -> Result { - if !config.scale.is_finite() || config.scale <= 0.0 { - return Err(StepExportError::MeshGeneration( - "STEP scale must be a finite positive value".to_string(), - )); - } - Ok(config.scale) -} - -fn is_closed_solid(brep: &Brep) -> bool { - if brep.faces.is_empty() || brep.edges.is_empty() { - return false; - } - - if !brep.shells.is_empty() && brep.shells.iter().all(|shell| !shell.is_closed) { - return false; - } - - brep.edges.iter().all(|edge| edge.twin_halfedge.is_some()) -} - -fn triangulate_single_face( - brep: &Brep, - face: &crate::brep::Face, - scale: f64, - policy: StepErrorPolicy, - report: &mut StepExportReport, - label: String, -) -> Result, StepExportError> { - let mut triangles = Vec::new(); - let (outer_vertices, holes_vertices) = brep.get_vertices_and_holes_by_face_id(face.id); - - if outer_vertices.len() < 3 { - if policy == StepErrorPolicy::Strict { - return Err(StepExportError::MeshGeneration(format!( - "{} has fewer than 3 vertices", - label - ))); - } - report.skipped_faces += 1; - return Ok(triangles); - } - - if holes_vertices.iter().any(|hole| hole.len() < 3) { - if policy == StepErrorPolicy::Strict { - return Err(StepExportError::MeshGeneration(format!( - "{} has an invalid hole loop", - label - ))); - } - report.skipped_faces += 1; - return Ok(triangles); - } - - let triangle_indices = triangulate_polygon_with_holes(&outer_vertices, &holes_vertices); - if triangle_indices.is_empty() { - if policy == StepErrorPolicy::Strict { - return Err(StepExportError::MeshGeneration(format!( - "{} produced no triangles", - label - ))); - } - report.skipped_faces += 1; - return Ok(triangles); - } - - let mut all_vertices = outer_vertices; - for hole in holes_vertices { - all_vertices.extend(hole); - } - - for triangle in triangle_indices { - let Some((&a, &b, &c)) = all_vertices - .get(triangle[0]) - .zip(all_vertices.get(triangle[1])) - .zip(all_vertices.get(triangle[2])) - .map(|((a, b), c)| (a, b, c)) - else { - continue; - }; - - if !is_finite_vec3(a) || !is_finite_vec3(b) || !is_finite_vec3(c) { - continue; - } - - let scaled = [ - Vector3::new(a.x * scale, a.y * scale, a.z * scale), - Vector3::new(b.x * scale, b.y * scale, b.z * scale), - Vector3::new(c.x * scale, c.y * scale, c.z * scale), - ]; - - if is_degenerate_triangle(scaled[0], scaled[1], scaled[2]) { - continue; - } - - triangles.push(scaled); - } - - Ok(triangles) -} - -/// Caches reusable analytic entities (directions, surfaces, edge curves) so a -/// cylinder's lateral facets all reference a single `CYLINDRICAL_SURFACE` and -/// shared circle edges aren't re-emitted. -#[derive(Default)] -struct AnalyticEmitter { - surfaces: HashMap, - edge_curves: HashMap, - vertex_points: HashMap, -} - -/// Emits one face on its analytic surface as a STEP `ADVANCED_FACE`, with its -/// boundary edges expressed as exact `LINE` / `CIRCLE` `EDGE_CURVE`s. Returns -/// `None` (so the caller facets instead) if the boundary can't be traversed. -fn emit_analytic_face( - writer: &mut Part21Writer, - point_map: &mut HashMap, - emitter: &mut AnalyticEmitter, - brep: &Brep, - face: &crate::brep::Face, - scale: f64, -) -> Option { - let surface = face.surface.as_ref()?; - let surface_ref = emit_surface(writer, point_map, emitter, surface, scale); - - let outer = emit_edge_loop_bound( - writer, - point_map, - emitter, - brep, - face.outer_loop, - scale, - true, - )?; - let mut bounds = vec![outer]; - for inner in &face.inner_loops { - if let Some(b) = - emit_edge_loop_bound(writer, point_map, emitter, brep, *inner, scale, false) - { - bounds.push(b); - } - } - - Some(writer.add_entity(format!( - "ADVANCED_FACE('',({}),{},.T.)", - join_refs(&bounds), - Part21Writer::reference(surface_ref) - ))) -} - -/// Merges every group of faces that share one cylindrical surface into a single -/// `ADVANCED_FACE`. The merged face's boundary is the set of edges used by only -/// one face of the group (the top/bottom circle rings); interior facet edges are -/// dropped. Records the consumed face ids so the caller skips them. Returns the -/// emitted `ADVANCED_FACE` references. -fn emit_merged_cylinder_faces( - writer: &mut Part21Writer, - point_map: &mut HashMap, - emitter: &mut AnalyticEmitter, - brep: &Brep, - scale: f64, - consumed: &mut HashSet, -) -> Vec { - use crate::brep::SurfaceGeometry; - - // Group face ids by cylindrical-surface key. - let mut groups: HashMap> = HashMap::new(); - for face in &brep.faces { - if let Some(surface @ SurfaceGeometry::Cylinder { .. }) = &face.surface { - groups - .entry(surface_key(surface, scale)) - .or_default() - .push(face.id); - } - } - - let mut emitted = Vec::new(); - for (_key, group) in groups { - if group.len() < 2 { - continue; // a lone cylindrical face is handled by the per-face path - } - - // Boundary edges: those used by exactly one face in the group. - let mut edge_use: HashMap = HashMap::new(); - for &face_id in &group { - for edge_id in face_boundary_edges(brep, face_id) { - *edge_use.entry(edge_id).or_insert(0) += 1; - } - } - let boundary: Vec = edge_use - .iter() - .filter(|(_, &count)| count == 1) - .map(|(&edge_id, _)| edge_id) - .collect(); - if boundary.len() < 3 { - continue; - } - - let loops = assemble_edge_loops(brep, &boundary); - if loops.is_empty() { - continue; - } - - let surface = brep - .faces - .iter() - .find(|f| f.id == group[0]) - .and_then(|f| f.surface.clone()); - let Some(surface) = surface else { continue }; - let surface_ref = emit_surface(writer, point_map, emitter, &surface, scale); - - let mut bounds = Vec::new(); - for (i, edge_loop) in loops.iter().enumerate() { - let mut oriented = Vec::with_capacity(edge_loop.len()); - for &edge_id in edge_loop { - if let Some(edge_curve) = - emit_edge_curve(writer, point_map, emitter, brep, edge_id, scale) - { - oriented.push(writer.add_entity(format!( - "ORIENTED_EDGE('',*,*,{},.T.)", - Part21Writer::reference(edge_curve) - ))); - } - } - if oriented.is_empty() { - continue; - } - let loop_ref = writer.add_entity(format!("EDGE_LOOP('',({}))", join_refs(&oriented))); - let kind = if i == 0 { - "FACE_OUTER_BOUND" - } else { - "FACE_BOUND" - }; - bounds.push(writer.add_entity(format!( - "{}('',{},.T.)", - kind, - Part21Writer::reference(loop_ref) - ))); - } - - if bounds.is_empty() { - continue; - } - - emitted.push(writer.add_entity(format!( - "ADVANCED_FACE('',({}),{},.T.)", - join_refs(&bounds), - Part21Writer::reference(surface_ref) - ))); - for face_id in group { - consumed.insert(face_id); - } - } - - emitted -} - -/// All edge ids on a face's outer and inner loops. -fn face_boundary_edges(brep: &Brep, face_id: u32) -> Vec { - let Some(face) = brep.faces.iter().find(|f| f.id == face_id) else { - return Vec::new(); - }; - let mut edges = Vec::new(); - let mut loops = vec![face.outer_loop]; - loops.extend(face.inner_loops.iter().copied()); - for loop_id in loops { - if let Ok(halfedges) = brep.get_loop_halfedges(loop_id) { - for he_id in halfedges { - if let Some(he) = brep.halfedges.get(he_id as usize) { - edges.push(he.edge); - } - } - } - } - edges -} - -/// Assembles a set of boundary edges into ordered closed loops by walking shared -/// vertices. Each returned loop is an ordered list of edge ids. -fn assemble_edge_loops(brep: &Brep, edges: &[u32]) -> Vec> { - let mut endpoints: HashMap = HashMap::new(); - let mut vertex_edges: HashMap> = HashMap::new(); - for &edge_id in edges { - if let Some((a, b)) = brep.get_edge_endpoints(edge_id) { - endpoints.insert(edge_id, (a, b)); - vertex_edges.entry(a).or_default().push(edge_id); - vertex_edges.entry(b).or_default().push(edge_id); - } - } - - let mut remaining: HashSet = edges.iter().copied().collect(); - let mut loops = Vec::new(); - - while let Some(&start_edge) = remaining.iter().next() { - remaining.remove(&start_edge); - let (start_v, mut cur_v) = endpoints[&start_edge]; - let mut loop_edges = vec![start_edge]; - - while cur_v != start_v { - let next = vertex_edges - .get(&cur_v) - .and_then(|candidates| candidates.iter().find(|e| remaining.contains(e)).copied()); - let Some(next_edge) = next else { break }; - remaining.remove(&next_edge); - let (a, b) = endpoints[&next_edge]; - cur_v = if a == cur_v { b } else { a }; - loop_edges.push(next_edge); - } - - if loop_edges.len() >= 3 { - loops.push(loop_edges); - } - } - - // Largest loop first so it becomes the FACE_OUTER_BOUND. - loops.sort_by(|a, b| b.len().cmp(&a.len())); - loops -} - -fn emit_edge_loop_bound( - writer: &mut Part21Writer, - point_map: &mut HashMap, - emitter: &mut AnalyticEmitter, - brep: &Brep, - loop_id: u32, - scale: f64, - is_outer: bool, -) -> Option { - let halfedges = brep.get_loop_halfedges(loop_id).ok()?; - if halfedges.len() < 3 { - return None; - } - let mut oriented = Vec::with_capacity(halfedges.len()); - for he_id in halfedges { - let he = brep.halfedges.get(he_id as usize)?; - let edge_curve = emit_edge_curve(writer, point_map, emitter, brep, he.edge, scale)?; - oriented.push(writer.add_entity(format!( - "ORIENTED_EDGE('',*,*,{},.T.)", - Part21Writer::reference(edge_curve) - ))); - } - let edge_loop = writer.add_entity(format!("EDGE_LOOP('',({}))", join_refs(&oriented))); - let kind = if is_outer { - "FACE_OUTER_BOUND" - } else { - "FACE_BOUND" - }; - Some(writer.add_entity(format!( - "{}('',{},.T.)", - kind, - Part21Writer::reference(edge_loop) - ))) -} - -fn emit_edge_curve( - writer: &mut Part21Writer, - point_map: &mut HashMap, - emitter: &mut AnalyticEmitter, - brep: &Brep, - edge_id: u32, - scale: f64, -) -> Option { - if let Some(existing) = emitter.edge_curves.get(&edge_id) { - return Some(*existing); - } - let (from_id, to_id) = brep.get_edge_endpoints(edge_id)?; - let from_pos = scaled(brep.vertices.get(from_id as usize)?.position, scale); - let to_pos = scaled(brep.vertices.get(to_id as usize)?.position, scale); - let v_from = emit_vertex_point(writer, point_map, emitter, from_id, from_pos); - let v_to = emit_vertex_point(writer, point_map, emitter, to_id, to_pos); - - let edge = brep.edges.iter().find(|e| e.id == edge_id); - let curve_ref = match edge.and_then(|e| e.curve.as_ref()) { - Some(crate::brep::CurveGeometry::Circle { - center, - normal, - x_axis, - radius, - .. - }) => { - let placement = - emit_axis_placement(writer, point_map, scaled(*center, scale), *normal, *x_axis); - writer.add_entity(format!( - "CIRCLE('',{},{})", - Part21Writer::reference(placement), - format_real(radius * scale) - )) - } - _ => { - let dir = direction(from_pos, to_pos); - let d = emit_direction(writer, dir); - let vector = writer.add_entity(format!( - "VECTOR('',{},{})", - Part21Writer::reference(d), - format_real(distance(from_pos, to_pos).max(1.0)) - )); - let line_point = get_or_create_point(writer, point_map, from_pos); - writer.add_entity(format!( - "LINE('',{},{})", - Part21Writer::reference(line_point), - Part21Writer::reference(vector) - )) - } - }; - - let edge_curve = writer.add_entity(format!( - "EDGE_CURVE('',{},{},{},.T.)", - Part21Writer::reference(v_from), - Part21Writer::reference(v_to), - Part21Writer::reference(curve_ref) - )); - emitter.edge_curves.insert(edge_id, edge_curve); - Some(edge_curve) -} - -fn emit_surface( - writer: &mut Part21Writer, - point_map: &mut HashMap, - emitter: &mut AnalyticEmitter, - surface: &crate::brep::SurfaceGeometry, - scale: f64, -) -> usize { - use crate::brep::SurfaceGeometry; - let key = surface_key(surface, scale); - if let Some(existing) = emitter.surfaces.get(&key) { - return *existing; - } - let id = match surface { - SurfaceGeometry::Plane { origin, normal } => { - let placement = emit_axis_placement( - writer, - point_map, - scaled(*origin, scale), - *normal, - any_perpendicular(*normal), - ); - writer.add_entity(format!("PLANE('',{})", Part21Writer::reference(placement))) - } - SurfaceGeometry::Cylinder { - origin, - axis, - ref_direction, - radius, - .. - } => { - let placement = emit_axis_placement( - writer, - point_map, - scaled(*origin, scale), - *axis, - *ref_direction, - ); - writer.add_entity(format!( - "CYLINDRICAL_SURFACE('',{},{})", - Part21Writer::reference(placement), - format_real(radius * scale) - )) - } - }; - emitter.surfaces.insert(key, id); - id -} - -fn emit_axis_placement( - writer: &mut Part21Writer, - point_map: &mut HashMap, - location: Vector3, - axis: Vector3, - ref_direction: Vector3, -) -> usize { - let point = get_or_create_point(writer, point_map, location); - let axis_dir = emit_direction(writer, normalize(axis)); - let ref_dir = emit_direction(writer, normalize(ref_direction)); - writer.add_entity(format!( - "AXIS2_PLACEMENT_3D('',{},{},{})", - Part21Writer::reference(point), - Part21Writer::reference(axis_dir), - Part21Writer::reference(ref_dir) - )) -} - -fn emit_direction(writer: &mut Part21Writer, dir: Vector3) -> usize { - writer.add_entity(format!( - "DIRECTION('',({},{},{}))", - format_real(dir.x), - format_real(dir.y), - format_real(dir.z) - )) -} - -fn emit_vertex_point( - writer: &mut Part21Writer, - point_map: &mut HashMap, - emitter: &mut AnalyticEmitter, - vertex_id: u32, - position: Vector3, -) -> usize { - if let Some(existing) = emitter.vertex_points.get(&vertex_id) { - return *existing; - } - let point = get_or_create_point(writer, point_map, position); - let vertex_point = writer.add_entity(format!( - "VERTEX_POINT('',{})", - Part21Writer::reference(point) - )); - emitter.vertex_points.insert(vertex_id, vertex_point); - vertex_point -} - -fn emit_length_unit(writer: &mut Part21Writer, unit: crate::units::LengthUnit) -> usize { - match unit.step_si_prefix() { - Some(prefix) => writer.add_entity(format!( - "(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT({},.METRE.))", - prefix - )), - None => { - // Non-SI (inch/foot): a conversion-based unit on top of the metre. - let metre = writer.add_entity("(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.))"); - let measure = writer.add_entity(format!( - "LENGTH_MEASURE_WITH_UNIT(LENGTH_MEASURE({}),{})", - format_real(unit.metres_per_unit()), - Part21Writer::reference(metre) - )); - let dimensions = - writer.add_entity("DIMENSIONAL_EXPONENTS(1.,0.,0.,0.,0.,0.,0.)".to_string()); - writer.add_entity(format!( - "( CONVERSION_BASED_UNIT('{}',{}) LENGTH_UNIT() NAMED_UNIT({}) )", - unit.name(), - Part21Writer::reference(measure), - Part21Writer::reference(dimensions) - )) - } - } -} - -fn scaled(v: Vector3, scale: f64) -> Vector3 { - Vector3::new(v.x * scale, v.y * scale, v.z * scale) -} - -fn direction(from: Vector3, to: Vector3) -> Vector3 { - normalize(Vector3::new(to.x - from.x, to.y - from.y, to.z - from.z)) -} - -fn distance(a: Vector3, b: Vector3) -> f64 { - let dx = a.x - b.x; - let dy = a.y - b.y; - let dz = a.z - b.z; - (dx * dx + dy * dy + dz * dz).sqrt() -} - -fn normalize(v: Vector3) -> Vector3 { - let len = (v.x * v.x + v.y * v.y + v.z * v.z).sqrt(); - if len <= 1.0e-12 { - Vector3::new(0.0, 0.0, 1.0) - } else { - Vector3::new(v.x / len, v.y / len, v.z / len) - } -} - -fn any_perpendicular(n: Vector3) -> Vector3 { - let n = normalize(n); - if n.x.abs() <= n.y.abs() && n.x.abs() <= n.z.abs() { - normalize(Vector3::new(0.0, -n.z, n.y)) - } else if n.y.abs() <= n.z.abs() { - normalize(Vector3::new(-n.z, 0.0, n.x)) - } else { - normalize(Vector3::new(-n.y, n.x, 0.0)) - } -} - -fn surface_key(surface: &crate::brep::SurfaceGeometry, scale: f64) -> String { - use crate::brep::SurfaceGeometry; - match surface { - SurfaceGeometry::Plane { origin, normal } => format!( - "P|{:.6}|{:.6}|{:.6}|{:.6}|{:.6}|{:.6}", - origin.x * scale, - origin.y * scale, - origin.z * scale, - normal.x, - normal.y, - normal.z - ), - SurfaceGeometry::Cylinder { - origin, - axis, - radius, - .. - } => format!( - "C|{:.6}|{:.6}|{:.6}|{:.6}|{:.6}|{:.6}|{:.6}", - origin.x * scale, - origin.y * scale, - origin.z * scale, - axis.x, - axis.y, - axis.z, - radius * scale - ), - } -} - -fn get_or_create_point( - writer: &mut Part21Writer, - point_map: &mut HashMap, - point: Vector3, -) -> usize { - let key = format!("{:.9}|{:.9}|{:.9}", point.x, point.y, point.z); - if let Some(existing) = point_map.get(&key) { - return *existing; - } - - let id = writer.add_entity(format!( - "CARTESIAN_POINT('',({},{},{}))", - format_real(point.x), - format_real(point.y), - format_real(point.z) - )); - - point_map.insert(key, id); - id -} - -fn join_refs(ids: &[usize]) -> String { - ids.iter() - .map(|id| Part21Writer::reference(*id)) - .collect::>() - .join(",") -} - -fn format_real(value: f64) -> String { - let mut out = format!("{:.9}", value); - while out.contains('.') && out.ends_with('0') { - out.pop(); - } - if out.ends_with('.') { - out.push('0'); - } - out -} - -fn is_finite_vec3(point: Vector3) -> bool { - point.x.is_finite() && point.y.is_finite() && point.z.is_finite() -} - -fn is_degenerate_triangle(a: Vector3, b: Vector3, c: Vector3) -> bool { - let ab = [b.x - a.x, b.y - a.y, b.z - a.z]; - let ac = [c.x - a.x, c.y - a.y, c.z - a.z]; - - let cross = [ - ab[1] * ac[2] - ab[2] * ac[1], - ab[2] * ac[0] - ab[0] * ac[2], - ab[0] * ac[1] - ab[1] * ac[0], - ]; - - let area_sq = cross[0] * cross[0] + cross[1] * cross[1] + cross[2] * cross[2]; - !area_sq.is_finite() || area_sq <= STEP_LENGTH_EPSILON -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::brep::BrepBuilder; - use uuid::Uuid; - - fn tetrahedron_brep() -> Brep { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - builder.add_vertices(&[ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.5, 0.8660254, 0.0), - Vector3::new(0.5, 0.2886751, 0.8164966), - ]); - - builder.add_face(&[0, 2, 1], &[]).unwrap(); - builder.add_face(&[0, 1, 3], &[]).unwrap(); - builder.add_face(&[1, 2, 3], &[]).unwrap(); - builder.add_face(&[2, 0, 3], &[]).unwrap(); - - builder.build().unwrap() - } - - #[test] - fn exports_step_part21_document() { - let brep = tetrahedron_brep(); - let (text, report) = - export_brep_to_step_text(&brep, &StepExportConfig::default()).expect("step export"); - - assert!(text.starts_with("ISO-10303-21;")); - assert!(text.contains("FILE_SCHEMA(('AUTOMOTIVE_DESIGN'));")); - assert!(text.contains("MANIFOLD_SOLID_BREP")); - assert!(text.contains("ADVANCED_BREP_SHAPE_REPRESENTATION")); - assert!(report.exported_solids >= 1); - assert!(report.exported_triangles >= 4); - } - - #[test] - fn best_effort_skips_non_solid_brep() { - let solid = tetrahedron_brep(); - - let mut wire_builder = BrepBuilder::new(Uuid::new_v4()); - wire_builder.add_vertices(&[Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)]); - wire_builder.add_wire(&[0, 1], false).unwrap(); - let wire = wire_builder.build().unwrap(); - - let (text, report) = - export_breps_to_step_text([&solid, &wire], &StepExportConfig::default()) - .expect("best effort should succeed"); - - assert!(text.contains("MANIFOLD_SOLID_BREP")); - assert_eq!(report.exported_solids, 1); - assert!(report.skipped_entities >= 1); - } - - #[test] - fn cylinder_exports_analytic_cylindrical_surface() { - // Acceptance criterion A1: circle profile → solid → STEP contains a - // single CYLINDRICAL_SURFACE and circular edge curves, not a facet fan. - use crate::primitives::cylinder::OGCylinder; - - let mut cylinder = OGCylinder::new("a1-cyl".to_string()); - cylinder - .set_config( - Vector3::new(0.0, 0.0, 0.0), - 1.0, - 2.0, - 2.0 * std::f64::consts::PI, - 32, - ) - .unwrap(); - let brep = cylinder.world_brep(); - - let (text, report) = - export_brep_to_step_text(&brep, &StepExportConfig::default()).expect("step export"); - - assert_eq!( - text.matches("CYLINDRICAL_SURFACE").count(), - 1, - "exactly one analytic cylindrical surface" - ); - assert!(text.contains("CIRCLE("), "circular edge curves present"); - assert!(text.contains("ADVANCED_FACE"), "analytic faces emitted"); - assert!(text.contains("PLANE("), "planar caps as analytic planes"); - // Topological reduction (D9): the 32 lateral facets collapse into a - // single cylindrical ADVANCED_FACE, so the whole solid is ~3 faces - // (1 cylinder + 2 caps), not 34. - let advanced_faces = text.matches("ADVANCED_FACE").count(); - assert!( - advanced_faces <= 4, - "lateral facets should merge into one cylinder face, got {} advanced faces", - advanced_faces - ); - assert!(report.exported_solids >= 1); - } - - #[test] - fn legacy_config_json_without_new_fields_still_deserializes() { - // The SDK's exportSceneToStep sends JSON predating D8/D9 fields; serde - // defaults must keep it working (analytic on, mm). - let legacy = r#"{ - "schema":"AutomotiveDesign","product_name":"x","scale":1.0, - "error_policy":"BestEffort","validate_topology":true,"require_closed_shell":true - }"#; - let config: StepExportConfig = serde_json::from_str(legacy).expect("legacy json"); - assert!(config.analytic_surfaces); - assert_eq!(config.length_unit, crate::units::LengthUnit::Millimetre); - } - - #[test] - fn unit_context_reflects_configured_length_unit() { - use crate::units::LengthUnit; - let brep = tetrahedron_brep(); - - let mm = StepExportConfig { - length_unit: LengthUnit::Millimetre, - ..StepExportConfig::default() - }; - let (mm_text, _) = export_brep_to_step_text(&brep, &mm).unwrap(); - assert!(mm_text.contains("SI_UNIT(.MILLI.,.METRE.)")); - - let cm = StepExportConfig { - length_unit: LengthUnit::Centimetre, - ..StepExportConfig::default() - }; - let (cm_text, _) = export_brep_to_step_text(&brep, &cm).unwrap(); - assert!(cm_text.contains("SI_UNIT(.CENTI.,.METRE.)")); - } - - #[test] - fn strict_fails_on_non_solid_brep() { - let mut wire_builder = BrepBuilder::new(Uuid::new_v4()); - wire_builder.add_vertices(&[Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)]); - wire_builder.add_wire(&[0, 1], false).unwrap(); - let wire = wire_builder.build().unwrap(); - - let config = StepExportConfig { - error_policy: StepErrorPolicy::Strict, - ..StepExportConfig::default() - }; - - let result = export_brep_to_step_text(&wire, &config); - assert!(result.is_err()); - } -} diff --git a/main/opengeometry/src/export/stl.rs b/main/opengeometry/src/export/stl.rs deleted file mode 100644 index 39d6612..0000000 --- a/main/opengeometry/src/export/stl.rs +++ /dev/null @@ -1,491 +0,0 @@ -use std::fmt; - -use crate::brep::{Brep, Face}; -use crate::operations::triangulate::triangulate_polygon_with_holes; -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; - -const DEFAULT_STL_HEADER: &str = "OpenGeometry STL Export"; -const STL_HEADER_BYTES: usize = 80; -const TRIANGLE_EPSILON: f64 = 1.0e-12; - -#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub enum StlErrorPolicy { - Strict, - BestEffort, -} - -impl Default for StlErrorPolicy { - fn default() -> Self { - Self::BestEffort - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct StlExportConfig { - pub header: Option, - pub scale: f64, - pub error_policy: StlErrorPolicy, - pub validate_topology: bool, -} - -impl Default for StlExportConfig { - fn default() -> Self { - Self { - header: Some(DEFAULT_STL_HEADER.to_string()), - scale: 1.0, - error_policy: StlErrorPolicy::BestEffort, - validate_topology: true, - } - } -} - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct StlExportReport { - pub input_breps: usize, - pub input_faces: usize, - pub exported_triangles: usize, - pub skipped_faces: usize, - pub skipped_triangles: usize, - pub topology_errors: usize, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum StlExportError { - EmptyInput, - InvalidTopology(String), - MeshGeneration(String), - Io(String), -} - -impl fmt::Display for StlExportError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - StlExportError::EmptyInput => write!(f, "No BREP input provided for STL export"), - StlExportError::InvalidTopology(message) => write!(f, "Invalid topology: {}", message), - StlExportError::MeshGeneration(message) => { - write!(f, "Failed to generate STL mesh: {}", message) - } - StlExportError::Io(message) => write!(f, "STL I/O error: {}", message), - } - } -} - -impl std::error::Error for StlExportError {} - -pub fn export_brep_to_stl_bytes( - brep: &Brep, - config: &StlExportConfig, -) -> Result<(Vec, StlExportReport), StlExportError> { - export_breps_to_stl_bytes([brep], config) -} - -pub fn export_breps_to_stl_bytes<'a, I>( - breps: I, - config: &StlExportConfig, -) -> Result<(Vec, StlExportReport), StlExportError> -where - I: IntoIterator, -{ - let scale = validate_config(config)?; - let breps: Vec<&Brep> = breps.into_iter().collect(); - if breps.is_empty() { - return Err(StlExportError::EmptyInput); - } - - let mut triangles: Vec = Vec::new(); - let mut report = StlExportReport { - input_breps: breps.len(), - ..StlExportReport::default() - }; - - for brep in breps { - if config.validate_topology { - if let Err(error) = brep.validate_topology() { - if config.error_policy == StlErrorPolicy::Strict { - return Err(StlExportError::InvalidTopology(format!( - "BREP {} failed validation: {}", - brep.id, error - ))); - } - report.topology_errors += 1; - continue; - } - } - - for face in &brep.faces { - report.input_faces += 1; - triangulate_face( - brep, - face, - scale, - config.error_policy, - &mut triangles, - &mut report, - )?; - } - } - - if triangles.is_empty() { - return Err(StlExportError::MeshGeneration( - "No triangles were exported from the provided BREP inputs".to_string(), - )); - } - - let mut bytes = Vec::with_capacity(STL_HEADER_BYTES + 4 + triangles.len() * 50); - stl_io::write_stl(&mut bytes, triangles.iter()) - .map_err(|error| StlExportError::Io(error.to_string()))?; - apply_header( - &mut bytes, - config.header.as_deref().unwrap_or(DEFAULT_STL_HEADER), - ); - - Ok((bytes, report)) -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn export_brep_to_stl_file( - brep: &Brep, - file_path: &str, - config: &StlExportConfig, -) -> Result { - let (bytes, report) = export_brep_to_stl_bytes(brep, config)?; - std::fs::write(file_path, bytes).map_err(|error| StlExportError::Io(error.to_string()))?; - Ok(report) -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn export_breps_to_stl_file<'a, I>( - breps: I, - file_path: &str, - config: &StlExportConfig, -) -> Result -where - I: IntoIterator, -{ - let (bytes, report) = export_breps_to_stl_bytes(breps, config)?; - std::fs::write(file_path, bytes).map_err(|error| StlExportError::Io(error.to_string()))?; - Ok(report) -} - -fn validate_config(config: &StlExportConfig) -> Result { - if !config.scale.is_finite() || config.scale <= 0.0 { - return Err(StlExportError::MeshGeneration( - "STL scale must be a finite positive number".to_string(), - )); - } - Ok(config.scale) -} - -fn triangulate_face( - brep: &Brep, - face: &Face, - scale: f64, - policy: StlErrorPolicy, - triangles: &mut Vec, - report: &mut StlExportReport, -) -> Result<(), StlExportError> { - let (outer_vertices, holes_vertices) = brep.get_vertices_and_holes_by_face_id(face.id); - if outer_vertices.len() < 3 { - return handle_face_issue( - policy, - report, - format!("Face {} has fewer than 3 outer vertices", face.id), - ); - } - - if holes_vertices.iter().any(|hole| hole.len() < 3) { - return handle_face_issue( - policy, - report, - format!("Face {} has a hole with fewer than 3 vertices", face.id), - ); - } - - let triangle_indices = triangulate_polygon_with_holes(&outer_vertices, &holes_vertices); - if triangle_indices.is_empty() { - return handle_face_issue( - policy, - report, - format!("Face {} triangulation returned no triangles", face.id), - ); - } - - let mut all_vertices = outer_vertices; - for hole in holes_vertices { - all_vertices.extend(hole); - } - - let target_normal = face_normal_hint(face, &all_vertices); - let mut face_has_exported_triangle = false; - - for triangle in triangle_indices { - let Some((&a, &b, &c)) = all_vertices - .get(triangle[0]) - .zip(all_vertices.get(triangle[1])) - .zip(all_vertices.get(triangle[2])) - .map(|((a, b), c)| (a, b, c)) - else { - if policy == StlErrorPolicy::Strict { - return Err(StlExportError::MeshGeneration(format!( - "Face {} produced out-of-range triangle index", - face.id - ))); - } - report.skipped_triangles += 1; - continue; - }; - - if !is_finite_vec3(a) || !is_finite_vec3(b) || !is_finite_vec3(c) { - if policy == StlErrorPolicy::Strict { - return Err(StlExportError::MeshGeneration(format!( - "Face {} contains non-finite coordinates", - face.id - ))); - } - report.skipped_triangles += 1; - continue; - } - - let mut oriented = [a, b, c]; - let Some(mut normal) = compute_triangle_normal(&a, &b, &c) else { - if policy == StlErrorPolicy::Strict { - return Err(StlExportError::MeshGeneration(format!( - "Face {} contains a degenerate triangle", - face.id - ))); - } - report.skipped_triangles += 1; - continue; - }; - - if let Some(target) = target_normal { - if dot(normal, target) < 0.0 { - oriented.swap(1, 2); - normal = [-normal[0], -normal[1], -normal[2]]; - } - } - - let Some(v0) = scaled_vertex(oriented[0], scale) else { - if policy == StlErrorPolicy::Strict { - return Err(StlExportError::MeshGeneration(format!( - "Face {} failed to scale triangle coordinates", - face.id - ))); - } - report.skipped_triangles += 1; - continue; - }; - let Some(v1) = scaled_vertex(oriented[1], scale) else { - if policy == StlErrorPolicy::Strict { - return Err(StlExportError::MeshGeneration(format!( - "Face {} failed to scale triangle coordinates", - face.id - ))); - } - report.skipped_triangles += 1; - continue; - }; - let Some(v2) = scaled_vertex(oriented[2], scale) else { - if policy == StlErrorPolicy::Strict { - return Err(StlExportError::MeshGeneration(format!( - "Face {} failed to scale triangle coordinates", - face.id - ))); - } - report.skipped_triangles += 1; - continue; - }; - - triangles.push(stl_io::Triangle { - normal: stl_io::Normal::new([normal[0] as f32, normal[1] as f32, normal[2] as f32]), - vertices: [ - stl_io::Vertex::new(v0), - stl_io::Vertex::new(v1), - stl_io::Vertex::new(v2), - ], - }); - report.exported_triangles += 1; - face_has_exported_triangle = true; - } - - if !face_has_exported_triangle { - return handle_face_issue( - policy, - report, - format!("Face {} generated no valid triangles", face.id), - ); - } - - Ok(()) -} - -fn handle_face_issue( - policy: StlErrorPolicy, - report: &mut StlExportReport, - message: String, -) -> Result<(), StlExportError> { - if policy == StlErrorPolicy::Strict { - return Err(StlExportError::MeshGeneration(message)); - } - report.skipped_faces += 1; - Ok(()) -} - -fn face_normal_hint(face: &Face, all_vertices: &[Vector3]) -> Option<[f64; 3]> { - let face_normal = [face.normal.x, face.normal.y, face.normal.z]; - normalize(face_normal).or_else(|| compute_polygon_normal(all_vertices)) -} - -fn compute_polygon_normal(vertices: &[Vector3]) -> Option<[f64; 3]> { - if vertices.len() < 3 { - return None; - } - - let mut nx = 0.0; - let mut ny = 0.0; - let mut nz = 0.0; - - for index in 0..vertices.len() { - let current = vertices[index]; - let next = vertices[(index + 1) % vertices.len()]; - nx += (current.y - next.y) * (current.z + next.z); - ny += (current.z - next.z) * (current.x + next.x); - nz += (current.x - next.x) * (current.y + next.y); - } - - normalize([nx, ny, nz]) -} - -fn compute_triangle_normal(a: &Vector3, b: &Vector3, c: &Vector3) -> Option<[f64; 3]> { - let ab = [b.x - a.x, b.y - a.y, b.z - a.z]; - let ac = [c.x - a.x, c.y - a.y, c.z - a.z]; - normalize(cross(ab, ac)) -} - -fn cross(a: [f64; 3], b: [f64; 3]) -> [f64; 3] { - [ - a[1] * b[2] - a[2] * b[1], - a[2] * b[0] - a[0] * b[2], - a[0] * b[1] - a[1] * b[0], - ] -} - -fn dot(a: [f64; 3], b: [f64; 3]) -> f64 { - a[0] * b[0] + a[1] * b[1] + a[2] * b[2] -} - -fn normalize(vector: [f64; 3]) -> Option<[f64; 3]> { - let length_sq = dot(vector, vector); - if !length_sq.is_finite() || length_sq <= TRIANGLE_EPSILON { - return None; - } - let inv_len = length_sq.sqrt().recip(); - Some([ - vector[0] * inv_len, - vector[1] * inv_len, - vector[2] * inv_len, - ]) -} - -fn is_finite_vec3(point: Vector3) -> bool { - point.x.is_finite() && point.y.is_finite() && point.z.is_finite() -} - -fn scaled_vertex(point: Vector3, scale: f64) -> Option<[f32; 3]> { - let x = point.x * scale; - let y = point.y * scale; - let z = point.z * scale; - if !(x.is_finite() && y.is_finite() && z.is_finite()) { - return None; - } - Some([x as f32, y as f32, z as f32]) -} - -fn apply_header(bytes: &mut [u8], header: &str) { - if bytes.len() < STL_HEADER_BYTES { - return; - } - - bytes[..STL_HEADER_BYTES].fill(0); - let header_bytes = header.as_bytes(); - let copy_len = header_bytes.len().min(STL_HEADER_BYTES); - bytes[..copy_len].copy_from_slice(&header_bytes[..copy_len]); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::brep::BrepBuilder; - use uuid::Uuid; - - fn triangle_brep() -> Brep { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - builder.add_vertices(&[ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.0, 1.0, 0.0), - ]); - builder - .add_face(&[0, 1, 2], &[]) - .expect("triangle face should be valid"); - builder.build().expect("triangle brep should be valid") - } - - #[test] - fn exports_binary_stl_with_expected_size_and_count() { - let brep = triangle_brep(); - let config = StlExportConfig::default(); - - let (bytes, report) = export_brep_to_stl_bytes(&brep, &config).expect("export should pass"); - - assert_eq!(report.exported_triangles, 1); - assert_eq!(bytes.len(), 84 + 50); - - let count = u32::from_le_bytes([bytes[80], bytes[81], bytes[82], bytes[83]]); - assert_eq!(count, 1); - } - - #[test] - fn writes_custom_binary_header() { - let brep = triangle_brep(); - let config = StlExportConfig { - header: Some("OpenGeometry Test STL".to_string()), - ..StlExportConfig::default() - }; - - let (bytes, _) = export_brep_to_stl_bytes(&brep, &config).expect("export should pass"); - assert_eq!( - &bytes[..b"OpenGeometry Test STL".len()], - b"OpenGeometry Test STL" - ); - } - - #[test] - fn best_effort_skips_degenerate_triangles() { - let valid = triangle_brep(); - let mut degenerate = triangle_brep(); - degenerate.vertices[2].position = degenerate.vertices[1].position; - - let (bytes, report) = - export_breps_to_stl_bytes([&valid, °enerate], &StlExportConfig::default()) - .expect("best-effort export should still succeed"); - - assert!(!bytes.is_empty()); - assert_eq!(report.exported_triangles, 1); - assert!(report.skipped_triangles >= 1 || report.skipped_faces >= 1); - } - - #[test] - fn strict_policy_fails_on_degenerate_triangles() { - let mut degenerate = triangle_brep(); - degenerate.vertices[2].position = degenerate.vertices[1].position; - - let config = StlExportConfig { - error_policy: StlErrorPolicy::Strict, - ..StlExportConfig::default() - }; - let result = export_brep_to_stl_bytes(°enerate, &config); - assert!(result.is_err()); - } -} diff --git a/main/opengeometry/src/export/wasm.rs b/main/opengeometry/src/export/wasm.rs deleted file mode 100644 index a7d1934..0000000 --- a/main/opengeometry/src/export/wasm.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::brep::Brep; -use crate::export::ifc::{export_brep_to_ifc_text, IfcExportConfig, IfcExportReport}; -use crate::export::step::{export_brep_to_step_text, StepExportConfig, StepExportReport}; -use crate::export::stl::{export_brep_to_stl_bytes, StlExportConfig, StlExportReport}; -use serde::de::DeserializeOwned; -use serde::Serialize; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -pub struct OGStlExportResult { - bytes: Vec, - report_json: String, -} - -#[wasm_bindgen] -impl OGStlExportResult { - #[wasm_bindgen(getter)] - pub fn bytes(&self) -> Vec { - self.bytes.clone() - } - - #[wasm_bindgen(getter, js_name = reportJson)] - pub fn report_json(&self) -> String { - self.report_json.clone() - } -} - -#[wasm_bindgen] -pub struct OGStepExportResult { - text: String, - report_json: String, -} - -#[wasm_bindgen] -impl OGStepExportResult { - #[wasm_bindgen(getter)] - pub fn text(&self) -> String { - self.text.clone() - } - - #[wasm_bindgen(getter, js_name = reportJson)] - pub fn report_json(&self) -> String { - self.report_json.clone() - } -} - -#[wasm_bindgen] -pub struct OGIfcExportResult { - text: String, - report_json: String, -} - -#[wasm_bindgen] -impl OGIfcExportResult { - #[wasm_bindgen(getter)] - pub fn text(&self) -> String { - self.text.clone() - } - - #[wasm_bindgen(getter, js_name = reportJson)] - pub fn report_json(&self) -> String { - self.report_json.clone() - } -} - -fn report_json(report: &impl Serialize) -> Result { - serde_json::to_string(report) - .map_err(|error| format!("Failed to serialize export report: {error}")) -} - -impl OGStlExportResult { - pub(crate) fn from_parts(bytes: Vec, report: StlExportReport) -> Result { - Ok(Self { - bytes, - report_json: report_json(&report)?, - }) - } -} - -impl OGStepExportResult { - pub(crate) fn from_parts(text: String, report: StepExportReport) -> Result { - Ok(Self { - text, - report_json: report_json(&report)?, - }) - } -} - -impl OGIfcExportResult { - pub(crate) fn from_parts(text: String, report: IfcExportReport) -> Result { - Ok(Self { - text, - report_json: report_json(&report)?, - }) - } -} - -pub(crate) fn optional_json( - payload: Option, - what: &str, -) -> Result { - match payload { - Some(json) if !json.trim().is_empty() => serde_json::from_str(&json) - .map_err(|error| format!("Invalid {what} JSON payload: {error}")), - _ => Ok(T::default()), - } -} - -fn parse_brep(brep_serialized: &str) -> Result { - serde_json::from_str(brep_serialized).map_err(|error| { - JsValue::from_str(&format!("Failed to deserialize BRep JSON payload: {error}")) - }) -} - -#[wasm_bindgen(js_name = exportBrepToStl)] -pub fn export_brep_to_stl( - brep_serialized: &str, - config_json: Option, -) -> Result { - let config: StlExportConfig = - optional_json(config_json, "STL config").map_err(|error| JsValue::from_str(&error))?; - let (bytes, report) = export_brep_to_stl_bytes(&parse_brep(brep_serialized)?, &config) - .map_err(|error| JsValue::from_str(&error.to_string()))?; - OGStlExportResult::from_parts(bytes, report).map_err(|error| JsValue::from_str(&error)) -} - -#[wasm_bindgen(js_name = exportBrepToStep)] -pub fn export_brep_to_step( - brep_serialized: &str, - config_json: Option, -) -> Result { - let config: StepExportConfig = - optional_json(config_json, "STEP config").map_err(|error| JsValue::from_str(&error))?; - let (text, report) = export_brep_to_step_text(&parse_brep(brep_serialized)?, &config) - .map_err(|error| JsValue::from_str(&error.to_string()))?; - OGStepExportResult::from_parts(text, report).map_err(|error| JsValue::from_str(&error)) -} - -#[wasm_bindgen(js_name = exportBrepToIfc)] -pub fn export_brep_to_ifc( - brep_serialized: &str, - config_json: Option, -) -> Result { - let config: IfcExportConfig = - optional_json(config_json, "IFC config").map_err(|error| JsValue::from_str(&error))?; - let (text, report) = export_brep_to_ifc_text(&parse_brep(brep_serialized)?, &config) - .map_err(|error| JsValue::from_str(&error.to_string()))?; - OGIfcExportResult::from_parts(text, report).map_err(|error| JsValue::from_str(&error)) -} diff --git a/main/opengeometry/src/freeform/mod.rs b/main/opengeometry/src/freeform/mod.rs deleted file mode 100644 index 9d9aef2..0000000 --- a/main/opengeometry/src/freeform/mod.rs +++ /dev/null @@ -1,171 +0,0 @@ -//! Standalone freeform geometry built on top of OpenGeometry BReps. -//! -//! This module owns the public `OGFreeformGeometry` wasm surface for creating -//! freeform geometry, inspecting its serialized geometry payloads, and managing -//! placement. Direct topology and geometry editing lives in `crate::editor`. - -#[cfg(test)] -mod tests; -mod types; - -use openmaths::Vector3; -use wasm_bindgen::prelude::*; - -use crate::brep::Brep; -use crate::spatial::placement::Placement3D; - -pub use types::ObjectTransformation; - -#[wasm_bindgen] -pub struct OGFreeformGeometry { - pub(crate) id: String, - pub(crate) local_brep: Brep, - pub(crate) placement: Placement3D, -} - -impl ObjectTransformation { - pub(crate) fn from_placement(placement: &Placement3D) -> Self { - Self { - anchor: placement.anchor, - translation: placement.translation(), - rotation: placement.rotation(), - scale: placement.scale(), - } - } - - fn apply_to_placement(&self, placement: &mut Placement3D) -> Result<(), String> { - placement.set_anchor(self.anchor); - placement.set_transform(self.translation, self.rotation, self.scale) - } -} - -#[wasm_bindgen] -impl OGFreeformGeometry { - #[wasm_bindgen(constructor)] - pub fn new(id: String, local_brep_serialized: String) -> Result { - let local_brep: Brep = serde_json::from_str(&local_brep_serialized).map_err(|error| { - JsValue::from_str(&format!( - "Failed to deserialize freeform BRep JSON payload: {}", - error - )) - })?; - - local_brep.validate_topology().map_err(|error| { - JsValue::from_str(&format!("Invalid freeform BRep topology: {}", error)) - })?; - - Ok(Self { - id, - local_brep, - placement: Placement3D::new(), - }) - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(js_name = getBrepSerialized)] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap_or_else(|_| "{}".to_string()) - } - - #[wasm_bindgen(js_name = getLocalBrepSerialized)] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.local_brep).unwrap_or_else(|_| "{}".to_string()) - } - - #[wasm_bindgen(js_name = getGeometrySerialized)] - pub fn get_geometry_serialized(&self) -> String { - let world = self.world_brep(); - serde_json::to_string(&world.get_triangle_vertex_buffer()) - .unwrap_or_else(|_| "[]".to_string()) - } - - #[wasm_bindgen(js_name = getGeometryBuffer)] - pub fn get_geometry_buffer(&self) -> Vec { - self.world_brep().get_triangle_vertex_buffer() - } - - #[wasm_bindgen(js_name = getLocalGeometryBuffer)] - pub fn get_local_geometry_buffer(&self) -> Vec { - self.local_brep.get_triangle_vertex_buffer() - } - - #[wasm_bindgen(js_name = getOutlineGeometrySerialized)] - pub fn get_outline_geometry_serialized(&self) -> String { - let world = self.world_brep(); - serde_json::to_string(&world.get_outline_vertex_buffer()) - .unwrap_or_else(|_| "[]".to_string()) - } - - #[wasm_bindgen(js_name = getOutlineGeometryBuffer)] - pub fn get_outline_geometry_buffer(&self) -> Vec { - self.world_brep().get_outline_vertex_buffer() - } - - #[wasm_bindgen(js_name = getLocalOutlineGeometryBuffer)] - pub fn get_local_outline_geometry_buffer(&self) -> Vec { - self.local_brep.get_outline_vertex_buffer() - } - - #[wasm_bindgen(js_name = getPlacementSerialized)] - pub fn get_placement_serialized(&self) -> String { - serde_json::to_string(&ObjectTransformation::from_placement(&self.placement)) - .unwrap_or_else(|_| "{}".to_string()) - } - - #[wasm_bindgen(js_name = setPlacementSerialized)] - pub fn set_placement_serialized( - &mut self, - placement_serialized: String, - ) -> Result<(), JsValue> { - let transform: ObjectTransformation = serde_json::from_str(&placement_serialized) - .map_err(|error| JsValue::from_str(&format!("Invalid placement payload: {}", error)))?; - - transform - .apply_to_placement(&mut self.placement) - .map_err(|error| JsValue::from_str(&error)) - } - - #[wasm_bindgen(js_name = setTransform)] - pub fn set_transform( - &mut self, - translation: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(translation, rotation, scale) - .map_err(|error| JsValue::from_str(&error)) - } - - #[wasm_bindgen(js_name = setTranslation)] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen(js_name = setRotation)] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen(js_name = setScale)] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|error| JsValue::from_str(&error)) - } - - #[wasm_bindgen(js_name = setAnchor)] - pub fn set_anchor(&mut self, anchor: Vector3) { - self.placement.set_anchor(anchor); - } -} - -impl OGFreeformGeometry { - pub(crate) fn world_brep(&self) -> Brep { - self.local_brep.transformed(&self.placement) - } -} diff --git a/main/opengeometry/src/freeform/tests.rs b/main/opengeometry/src/freeform/tests.rs deleted file mode 100644 index 2eabd68..0000000 --- a/main/opengeometry/src/freeform/tests.rs +++ /dev/null @@ -1,129 +0,0 @@ -use openmaths::Vector3; - -use super::OGFreeformGeometry; -use crate::primitives::arc::OGArc; -use crate::primitives::cuboid::OGCuboid; -use crate::primitives::curve::OGCurve; -use crate::primitives::cylinder::OGCylinder; -use crate::primitives::line::OGLine; -use crate::primitives::polygon::OGPolygon; -use crate::primitives::polyline::OGPolyline; -use crate::primitives::rectangle::OGRectangle; -use crate::primitives::sphere::OGSphere; -use crate::primitives::sweep::OGSweep; -use crate::primitives::wedge::OGWedge; - -#[test] -fn freeform_entity_can_be_created_from_all_native_local_breps() { - let mut line = OGLine::new("line-source".to_string()); - line.set_config(Vector3::new(-1.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)) - .expect("line config"); - - let mut polyline = OGPolyline::new("polyline-source".to_string()); - polyline - .set_config(vec![ - Vector3::new(-1.0, 0.0, -1.0), - Vector3::new(0.0, 0.0, 0.5), - Vector3::new(1.0, 0.0, 0.0), - ]) - .expect("polyline config"); - - let mut arc = OGArc::new("arc-source".to_string()); - arc.set_config( - Vector3::new(0.0, 0.0, 0.0), - 2.0, - 0.0, - std::f64::consts::PI, - 24, - ) - .expect("arc config"); - - let mut curve = OGCurve::new("curve-source".to_string()); - curve - .set_config(vec![ - Vector3::new(-1.0, 0.0, 0.0), - Vector3::new(-0.2, 0.6, 0.2), - Vector3::new(0.5, 0.3, 0.8), - Vector3::new(1.0, 0.0, 0.0), - ]) - .expect("curve config"); - - let mut rectangle = OGRectangle::new("rectangle-source".to_string()); - rectangle - .set_config(Vector3::new(0.0, 0.0, 0.0), 3.0, 2.0) - .expect("rectangle config"); - - let mut polygon = OGPolygon::new("polygon-source".to_string()); - polygon - .set_config(vec![ - Vector3::new(-1.0, 0.0, -1.0), - Vector3::new(1.0, 0.0, -1.0), - Vector3::new(1.5, 0.0, 0.5), - Vector3::new(-1.0, 0.0, 1.0), - ]) - .expect("polygon config"); - - let mut cuboid = OGCuboid::new("cuboid-source".to_string()); - cuboid - .set_config(Vector3::new(0.0, 0.0, 0.0), 2.0, 3.0, 4.0) - .expect("cuboid config"); - - let mut cylinder = OGCylinder::new("cylinder-source".to_string()); - cylinder - .set_config( - Vector3::new(0.0, 0.0, 0.0), - 1.0, - 2.0, - std::f64::consts::TAU, - 24, - ) - .expect("cylinder config"); - - let mut sphere = OGSphere::new("sphere-source".to_string()); - sphere - .set_config(Vector3::new(0.0, 0.0, 0.0), 1.25, 16, 12) - .expect("sphere config"); - - let mut wedge = OGWedge::new("wedge-source".to_string()); - wedge - .set_config(Vector3::new(0.0, 0.0, 0.0), 2.0, 1.5, 1.0) - .expect("wedge config"); - - let mut sweep = OGSweep::new("sweep-source".to_string()); - sweep - .set_config( - vec![ - Vector3::new(-1.0, 0.0, 0.0), - Vector3::new(0.0, 0.8, 0.3), - Vector3::new(1.0, 1.2, 0.0), - ], - vec![ - Vector3::new(-0.2, 0.0, -0.2), - Vector3::new(0.2, 0.0, -0.2), - Vector3::new(0.2, 0.0, 0.2), - Vector3::new(-0.2, 0.0, 0.2), - ], - ) - .expect("sweep config"); - - let sources = vec![ - ("line", line.get_local_brep_serialized()), - ("polyline", polyline.get_local_brep_serialized()), - ("arc", arc.get_local_brep_serialized()), - ("curve", curve.get_local_brep_serialized()), - ("rectangle", rectangle.get_local_brep_serialized()), - ("polygon", polygon.get_local_brep_serialized()), - ("cuboid", cuboid.get_local_brep_serialized()), - ("cylinder", cylinder.get_local_brep_serialized()), - ("sphere", sphere.get_local_brep_serialized()), - ("wedge", wedge.get_local_brep_serialized()), - ("sweep", sweep.get_local_brep_serialized()), - ]; - - for (label, local_brep) in sources { - let entity = OGFreeformGeometry::new(format!("freeform-{}", label), local_brep) - .unwrap_or_else(|_| panic!("{} should convert to freeform", label)); - - assert!(!entity.get_local_brep_serialized().is_empty()); - } -} diff --git a/main/opengeometry/src/freeform/types.rs b/main/opengeometry/src/freeform/types.rs deleted file mode 100644 index 6449f9e..0000000 --- a/main/opengeometry/src/freeform/types.rs +++ /dev/null @@ -1,10 +0,0 @@ -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Serialize, Deserialize)] -pub struct ObjectTransformation { - pub anchor: Vector3, - pub translation: Vector3, - pub rotation: Vector3, - pub scale: Vector3, -} diff --git a/main/opengeometry/src/geom2d/boolean2d.rs b/main/opengeometry/src/geom2d/boolean2d.rs new file mode 100644 index 0000000..4b86ea7 --- /dev/null +++ b/main/opengeometry/src/geom2d/boolean2d.rs @@ -0,0 +1,246 @@ +use super::poly2d::{ + cross2, dedupe_ring, dot2, point_on_edge_t, ring_inside2, segment_cross_t2, signed_area2, + winding_number2, Edge2, Pt2, +}; + +pub(crate) struct RingRegion { + pub(crate) outer: Vec, + pub(crate) holes: Vec>, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum PlanarBooleanOp { + Union, + Intersection, + Subtraction, +} + +pub(crate) fn boolean_oriented_regions( + a: &[Vec], + b: &[Vec], + operation: PlanarBooleanOp, + eps: f64, +) -> Vec { + let mut contours = Vec::with_capacity(a.len() + b.len()); + contours.extend_from_slice(a); + contours.extend_from_slice(b); + winding_regions_by(&contours, eps, |point| { + let in_a = a + .iter() + .map(|ring| winding_number2(point, ring)) + .sum::() + != 0; + let in_b = b + .iter() + .map(|ring| winding_number2(point, ring)) + .sum::() + != 0; + match operation { + PlanarBooleanOp::Union => in_a || in_b, + PlanarBooleanOp::Intersection => in_a && in_b, + PlanarBooleanOp::Subtraction => in_a && !in_b, + } + }) +} + +fn winding_regions_by( + polys: &[Vec], + eps: f64, + filled: impl Fn(Pt2) -> bool, +) -> Vec { + let mut edges: Vec = Vec::new(); + for poly in polys { + let m = poly.len(); + for i in 0..m { + let a = poly[i]; + let b = poly[(i + 1) % m]; + if (a.x - b.x).hypot(a.z - b.z) > eps { + edges.push((a, b)); + } + } + } + regions_from_edges_by(&edges, eps, filled) +} + +pub(crate) fn regions_from_edges_by( + edges: &[Edge2], + eps: f64, + filled: impl Fn(Pt2) -> bool, +) -> Vec { + if edges.is_empty() { + return Vec::new(); + } + let split = subdivided_edges(edges, eps); + let mut boundary = boundary_edges(&split, eps, filled); + + let mut seen: std::collections::HashSet<((i64, i64), (i64, i64))> = + std::collections::HashSet::new(); + boundary.retain(|&(a, b)| seen.insert((quantized_key(a, eps), quantized_key(b, eps)))); + if boundary.is_empty() { + return Vec::new(); + } + + let raw_loops = boundary_loops(&boundary, eps); + nested_regions(raw_loops, eps) +} + +fn subdivided_edges(edges: &[Edge2], eps: f64) -> Vec { + let verts: Vec = edges.iter().flat_map(|&(a, b)| [a, b]).collect(); + + let mut split: Vec = Vec::new(); + for (i, &(ea, eb)) in edges.iter().enumerate() { + let rx = eb.x - ea.x; + let rz = eb.z - ea.z; + let mut ts: Vec = vec![0.0, 1.0]; + for (j, &(fa, fb)) in edges.iter().enumerate() { + if i != j { + if let Some((_, ta, _)) = segment_cross_t2(ea, eb, fa, fb, eps) { + ts.push(ta); + } + } + } + for &v in &verts { + if let Some(t) = point_on_edge_t(ea, eb, v, eps) { + ts.push(t); + } + } + ts.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); + let mut prev = f64::NEG_INFINITY; + let mut clean: Vec = Vec::new(); + for t in ts { + if t - prev > eps { + clean.push(t); + prev = t; + } + } + for w in clean.windows(2) { + let p0 = Pt2::new(ea.x + w[0] * rx, ea.z + w[0] * rz); + let p1 = Pt2::new(ea.x + w[1] * rx, ea.z + w[1] * rz); + if (p0.x - p1.x).hypot(p0.z - p1.z) > eps { + split.push((p0, p1)); + } + } + } + split +} + +fn boundary_edges(split: &[Edge2], eps: f64, filled: impl Fn(Pt2) -> bool) -> Vec { + let mut boundary: Vec = Vec::new(); + for &(a, b) in split { + let dx = b.x - a.x; + let dz = b.z - a.z; + let len = dx.hypot(dz); + if len < eps { + continue; + } + let probe = (len * 0.25).min(1.0e-4); + let nx = -dz / len; + let nz = dx / len; + let mx = (a.x + b.x) / 2.0; + let mz = (a.z + b.z) / 2.0; + let left = filled(Pt2::new(mx + nx * probe, mz + nz * probe)); + let right = filled(Pt2::new(mx - nx * probe, mz - nz * probe)); + if left == right { + continue; + } + if left { + boundary.push((a, b)); + } else { + boundary.push((b, a)); + } + } + boundary +} + +fn quantized_key(point: Pt2, eps: f64) -> (i64, i64) { + let step = (2.0 * eps).max(f64::EPSILON); + ( + (point.x / step).round() as i64, + (point.z / step).round() as i64, + ) +} + +fn boundary_loops(boundary: &[Edge2], eps: f64) -> Vec> { + let mut out_edges: std::collections::HashMap<(i64, i64), Vec> = + std::collections::HashMap::new(); + for (idx, &(a, _)) in boundary.iter().enumerate() { + out_edges + .entry(quantized_key(a, eps)) + .or_default() + .push(idx); + } + let mut used = vec![false; boundary.len()]; + let mut raw_loops: Vec> = Vec::new(); + for start in 0..boundary.len() { + if used[start] { + continue; + } + let mut loop_pts: Vec = Vec::new(); + let mut cur = start; + let mut guard = 0; + while !used[cur] && guard <= boundary.len() { + guard += 1; + used[cur] = true; + let (a, b) = boundary[cur]; + loop_pts.push(a); + let back = Pt2::new(a.x - b.x, a.z - b.z); + let mut best: Option = None; + let mut best_cw = f64::INFINITY; + for &c in out_edges + .get(&quantized_key(b, eps)) + .map(|v| v.as_slice()) + .unwrap_or(&[]) + { + if used[c] { + continue; + } + let (ca, cb) = boundary[c]; + let out_dir = Pt2::new(cb.x - ca.x, cb.z - ca.z); + let mut cw = -cross2(back, out_dir).atan2(dot2(back, out_dir)); + if cw < eps { + cw += 2.0 * std::f64::consts::PI; + } + if cw < best_cw { + best_cw = cw; + best = Some(c); + } + } + match best { + Some(next) => cur = next, + None => break, + } + } + if loop_pts.len() >= 3 { + raw_loops.push(loop_pts); + } + } + raw_loops +} + +fn nested_regions(raw_loops: Vec>, eps: f64) -> Vec { + let mut regions: Vec = Vec::new(); + let mut holes: Vec> = Vec::new(); + for raw in raw_loops { + let ring = dedupe_ring(&raw, eps); + if ring.len() < 3 || signed_area2(&ring).abs() < eps { + continue; + } + if signed_area2(&ring) > 0.0 { + regions.push(RingRegion { + outer: ring, + holes: Vec::new(), + }); + } else { + holes.push(ring); + } + } + for hole in holes { + if let Some(region) = regions + .iter_mut() + .find(|region| ring_inside2(&hole, ®ion.outer, eps)) + { + region.holes.push(hole); + } + } + regions +} diff --git a/main/opengeometry/src/geom2d/curved_boolean2d/edge.rs b/main/opengeometry/src/geom2d/curved_boolean2d/edge.rs new file mode 100644 index 0000000..1962a80 --- /dev/null +++ b/main/opengeometry/src/geom2d/curved_boolean2d/edge.rs @@ -0,0 +1,299 @@ +use crate::geom2d::poly2d::Pt2; +use serde::{Deserialize, Serialize}; +use std::f64::consts::TAU; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] +pub(crate) enum CurveEdge2 { + Line { + from: [f64; 2], + to: [f64; 2], + }, + Arc { + center: [f64; 2], + radius: f64, + start_angle: f64, + sweep_angle: f64, + }, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct CurveRegion2 { + pub(crate) outer: Vec, + #[serde(default)] + pub(crate) holes: Vec>, +} + +pub(super) fn canonicalize_ring(ring: &mut [CurveEdge2]) { + if let Some((index, _)) = ring.iter().enumerate().min_by(|(_, first), (_, second)| { + compare_points(first.point(0.0), second.point(0.0)) + .then_with(|| compare_points(first.point(0.5), second.point(0.5))) + .then_with(|| compare_points(first.point(1.0), second.point(1.0))) + }) { + ring.rotate_left(index); + } +} + +pub(super) fn compare_points(a: Pt2, b: Pt2) -> std::cmp::Ordering { + a.x.total_cmp(&b.x).then_with(|| a.z.total_cmp(&b.z)) +} + +impl CurveEdge2 { + pub(crate) fn point(&self, t: f64) -> Pt2 { + match self { + Self::Line { from, to } => add(p(*from), scale(sub(p(*to), p(*from)), t)), + Self::Arc { + center, + radius, + start_angle, + sweep_angle, + } => { + let angle = start_angle + sweep_angle * t; + Pt2::new( + center[0] + radius * angle.cos(), + center[1] + radius * angle.sin(), + ) + } + } + } + + pub(crate) fn tangent(&self, t: f64) -> Pt2 { + match self { + Self::Line { from, to } => sub(p(*to), p(*from)), + Self::Arc { + radius, + start_angle, + sweep_angle, + .. + } => { + let angle = start_angle + sweep_angle * t; + Pt2::new( + -radius * sweep_angle * angle.sin(), + radius * sweep_angle * angle.cos(), + ) + } + } + } + + pub(crate) fn length(&self) -> f64 { + match self { + Self::Line { .. } => distance(self.point(0.0), self.point(1.0)), + Self::Arc { + radius, + sweep_angle, + .. + } => radius * sweep_angle.abs(), + } + } + + pub(crate) fn reversed(&self) -> Self { + match self { + Self::Line { from, to } => Self::Line { + from: *to, + to: *from, + }, + Self::Arc { + center, + radius, + start_angle, + sweep_angle, + } => Self::Arc { + center: *center, + radius: *radius, + start_angle: start_angle + sweep_angle, + sweep_angle: -*sweep_angle, + }, + } + } + + pub(crate) fn slice(&self, a: f64, b: f64) -> Self { + match self { + Self::Line { .. } => Self::Line { + from: arr(self.point(a)), + to: arr(self.point(b)), + }, + Self::Arc { + center, + radius, + start_angle, + sweep_angle, + } => Self::Arc { + center: *center, + radius: *radius, + start_angle: start_angle + sweep_angle * a, + sweep_angle: sweep_angle * (b - a), + }, + } + } + + pub(crate) fn parameter(&self, point: Pt2, eps: f64) -> Option { + match self { + Self::Line { from, to } => { + let d = sub(p(*to), p(*from)); + let len2 = dot(d, d); + if len2 <= eps * eps { + return None; + } + let t = dot(sub(point, p(*from)), d) / len2; + (t >= -eps / len2.sqrt() + && t <= 1.0 + eps / len2.sqrt() + && distance(self.point(t), point) <= eps) + .then_some(t.clamp(0.0, 1.0)) + } + Self::Arc { + center, + radius, + start_angle, + sweep_angle, + } => { + let radial = sub(point, p(*center)); + if (radial.x.hypot(radial.z) - radius).abs() > eps { + return None; + } + let angle = radial.z.atan2(radial.x); + let travel = if *sweep_angle > 0.0 { + (angle - start_angle).rem_euclid(TAU) + } else { + (start_angle - angle).rem_euclid(TAU) + }; + let delta = eps / radius; + if travel <= sweep_angle.abs() + delta { + Some((travel / sweep_angle.abs()).clamp(0.0, 1.0)) + } else if TAU - travel <= delta { + Some(0.0) + } else { + None + } + } + } + } + + pub(crate) fn twice_area(&self) -> f64 { + let a = self.point(0.0); + let b = self.point(1.0); + match self { + Self::Line { .. } => cross(a, b), + Self::Arc { + center, + radius, + sweep_angle, + .. + } => radius * radius * sweep_angle + center[0] * (b.z - a.z) - center[1] * (b.x - a.x), + } + } +} + +pub(super) fn add(a: Pt2, b: Pt2) -> Pt2 { + Pt2::new(a.x + b.x, a.z + b.z) +} + +pub(super) fn p(a: [f64; 2]) -> Pt2 { + Pt2::new(a[0], a[1]) +} + +pub(super) fn scale(a: Pt2, s: f64) -> Pt2 { + Pt2::new(a.x * s, a.z * s) +} + +fn sub(a: Pt2, b: Pt2) -> Pt2 { + Pt2::new(a.x - b.x, a.z - b.z) +} + +pub(super) fn distance(a: Pt2, b: Pt2) -> f64 { + sub(a, b).x.hypot(sub(a, b).z) +} + +fn arr(a: Pt2) -> [f64; 2] { + [a.x, a.z] +} + +pub(super) fn dot(a: Pt2, b: Pt2) -> f64 { + a.x * b.x + a.z * b.z +} +pub(super) fn cross(a: Pt2, b: Pt2) -> f64 { + a.x * b.z - a.z * b.x +} + +pub(crate) fn reversed_ring(ring: &[CurveEdge2]) -> Vec { + ring.iter().rev().map(CurveEdge2::reversed).collect() +} + +pub(crate) fn intersections(a: &CurveEdge2, b: &CurveEdge2, eps: f64) -> Vec { + let mut candidates = Vec::new(); + let mut offer = |hit: Pt2| { + if a.parameter(hit, eps).is_some() + && b.parameter(hit, eps).is_some() + && !candidates.iter().any(|prior| distance(*prior, hit) <= eps) + { + candidates.push(hit); + } + }; + match (a, b) { + (CurveEdge2::Line { from, to }, CurveEdge2::Line { from: bf, to: bt }) => { + let d = sub(p(*to), p(*from)); + let e = sub(p(*bt), p(*bf)); + let denom = cross(d, e); + if denom.abs() > eps * d.x.hypot(d.z) * e.x.hypot(e.z) { + offer(add( + p(*from), + scale(d, cross(sub(p(*bf), p(*from)), e) / denom), + )); + } else { + for endpoint in [*from, *to, *bf, *bt] { + offer(p(endpoint)); + } + } + } + (CurveEdge2::Line { from, to }, CurveEdge2::Arc { center, radius, .. }) + | (CurveEdge2::Arc { center, radius, .. }, CurveEdge2::Line { from, to }) => { + let d = sub(p(*to), p(*from)); + let f = sub(p(*from), p(*center)); + let aa = dot(d, d); + if aa <= eps * eps { + return candidates; + } + let bb = 2.0 * dot(f, d); + let cc = dot(f, f) - radius * radius; + let disc = bb * bb - 4.0 * aa * cc; + if disc >= -eps * eps * aa { + let root = disc.max(0.0).sqrt(); + for t in [(-bb - root) / (2.0 * aa), (-bb + root) / (2.0 * aa)] { + offer(add(p(*from), scale(d, t))); + } + } + } + ( + CurveEdge2::Arc { + center: ca, + radius: ra, + .. + }, + CurveEdge2::Arc { + center: cb, + radius: rb, + .. + }, + ) => { + let centre_delta = sub(p(*cb), p(*ca)); + let d = centre_delta.x.hypot(centre_delta.z); + if d <= eps && (ra - rb).abs() <= eps { + for endpoint in [a.point(0.0), a.point(1.0), b.point(0.0), b.point(1.0)] { + offer(endpoint); + } + } else if d > eps && d <= ra + rb + eps && d + ra.min(*rb) + eps >= ra.max(*rb) { + let along = (ra * ra - rb * rb + d * d) / (2.0 * d); + let height2 = ra * ra - along * along; + if height2 >= -eps * eps { + let u = scale(centre_delta, 1.0 / d); + let base = add(p(*ca), scale(u, along)); + let normal = Pt2::new(-u.z, u.x); + let h = height2.max(0.0).sqrt(); + offer(add(base, scale(normal, h))); + offer(add(base, scale(normal, -h))); + } + } + } + } + candidates +} diff --git a/main/opengeometry/src/geom2d/curved_boolean2d/mod.rs b/main/opengeometry/src/geom2d/curved_boolean2d/mod.rs new file mode 100644 index 0000000..50a87ce --- /dev/null +++ b/main/opengeometry/src/geom2d/curved_boolean2d/mod.rs @@ -0,0 +1,385 @@ +mod edge; + +pub(crate) use edge::{intersections, reversed_ring, CurveEdge2, CurveRegion2}; + +use super::boolean2d::PlanarBooleanOp; +use super::poly2d::Pt2; +use crate::brep::GeometryError; +use edge::{add, canonicalize_ring, compare_points, cross, distance, dot, p, scale}; +use std::collections::{HashMap, HashSet}; +use std::f64::consts::TAU; + +pub(crate) fn boolean_curved_regions( + a: &[CurveRegion2], + b: &[CurveRegion2], + operation: PlanarBooleanOp, + eps: f64, +) -> Result, GeometryError> { + if !eps.is_finite() || eps <= 0.0 { + return unresolved("Invalid Boolean tolerance"); + } + check_regions(a, eps)?; + check_regions(b, eps)?; + let edges: Vec = a + .iter() + .chain(b) + .flat_map(|region| { + std::iter::once(®ion.outer) + .chain(region.holes.iter()) + .flat_map(|ring| ring.iter().cloned()) + }) + .collect(); + if edges.is_empty() { + return Ok(Vec::new()); + } + let filled = |point: Pt2| { + let left = inside(point, a, eps); + let right = inside(point, b, eps); + match operation { + PlanarBooleanOp::Union => left || right, + PlanarBooleanOp::Intersection => left && right, + PlanarBooleanOp::Subtraction => left && !right, + } + }; + let split = subdivided_edges(&edges, eps); + let boundary = boundary_edges(split, eps, filled); + let loops = boundary_loops(&boundary, eps)?; + let mut result = nested_regions(&loops, eps); + canonicalize_regions(&mut result); + Ok(result) +} + +fn unresolved(reason: &str) -> Result { + Err(GeometryError::UnresolvedIntersection(reason.into())) +} + +fn check_regions(regions: &[CurveRegion2], eps: f64) -> Result<(), GeometryError> { + for region in regions { + validate_ring(®ion.outer, false, eps)?; + for hole in ®ion.holes { + validate_ring(hole, true, eps)?; + } + } + Ok(()) +} + +fn validate_ring( + ring: &[CurveEdge2], + expected_positive: bool, + eps: f64, +) -> Result<(), GeometryError> { + if ring.len() < 2 { + return unresolved("Curved Boolean ring needs at least two edges"); + } + for (index, edge) in ring.iter().enumerate() { + if !edge.length().is_finite() + || edge.length() <= 4.0 * eps + || ![ + edge.point(0.0).x, + edge.point(0.0).z, + edge.point(1.0).x, + edge.point(1.0).z, + ] + .iter() + .all(|v| v.is_finite()) + { + return unresolved("Curved Boolean edge is unresolved"); + } + if let CurveEdge2::Arc { + center, + radius, + start_angle, + sweep_angle, + } = edge + { + if !center.iter().all(|v| v.is_finite()) + || !radius.is_finite() + || !start_angle.is_finite() + || !sweep_angle.is_finite() + || *radius <= 4.0 * eps + || sweep_angle.abs() >= TAU + { + return unresolved("Curved Boolean arc is invalid"); + } + } + let next = &ring[(index + 1) % ring.len()]; + if distance(edge.point(1.0), next.point(0.0)) > eps { + return unresolved("Curved Boolean ring is open"); + } + } + let area = ring_area(ring); + if area.abs() <= eps * eps || (area > 0.0) != expected_positive { + return unresolved("Curved Boolean ring has degenerate or incorrect winding"); + } + Ok(()) +} + +fn ring_area(ring: &[CurveEdge2]) -> f64 { + ring.iter().map(CurveEdge2::twice_area).sum::() * 0.5 +} + +fn inside(point: Pt2, regions: &[CurveRegion2], eps: f64) -> bool { + regions.iter().any(|region| { + winding(point, ®ion.outer, eps) != 0 + && !region + .holes + .iter() + .any(|hole| winding(point, hole, eps) != 0) + }) +} + +pub(crate) fn winding(point: Pt2, ring: &[CurveEdge2], eps: f64) -> i32 { + let mut total = 0; + for edge in ring { + match edge { + CurveEdge2::Line { from, to } => { + let a = p(*from); + let b = p(*to); + let az = if (a.z - point.z).abs() <= eps * 0.25 { + point.z + } else { + a.z + }; + let bz = if (b.z - point.z).abs() <= eps * 0.25 { + point.z + } else { + b.z + }; + if (az <= point.z && bz > point.z) || (bz <= point.z && az > point.z) { + let x = a.x + (point.z - az) * (b.x - a.x) / (bz - az); + if x > point.x + eps * 0.01 { + total += if bz > az { 1 } else { -1 }; + } + } + } + CurveEdge2::Arc { + center, + radius, + start_angle, + sweep_angle, + } => { + let sine = (point.z - center[1]) / radius; + if sine.abs() >= 1.0 { + continue; + } + let first = sine.asin(); + for angle in [first, std::f64::consts::PI - first] { + let hit = Pt2::new(center[0] + radius * angle.cos(), point.z); + if hit.x <= point.x + eps * 0.01 { + continue; + } + let travel = if *sweep_angle > 0.0 { + (angle - start_angle).rem_euclid(TAU) + } else { + (start_angle - angle).rem_euclid(TAU) + }; + let dz = radius * sweep_angle * angle.cos(); + if travel > sweep_angle.abs() + eps / radius + || (travel <= eps / radius && dz < 0.0) + || (travel >= sweep_angle.abs() - eps / radius && dz > 0.0) + { + continue; + } + if dz.abs() > eps * radius { + total += if dz > 0.0 { 1 } else { -1 }; + } + } + } + } + } + total +} + +fn subdivided_edges(edges: &[CurveEdge2], eps: f64) -> Vec { + let mut split = Vec::new(); + for (index, edge) in edges.iter().enumerate() { + let mut ts = vec![0.0, 1.0]; + for (other_index, other) in edges.iter().enumerate() { + if index == other_index { + continue; + } + for point in intersections(edge, other, eps) { + if let Some(t) = edge.parameter(point, eps) { + ts.push(t); + } + } + } + ts.sort_by(|x, y| x.total_cmp(y)); + let mut clean: Vec = Vec::new(); + for t in ts { + if clean + .last() + .is_none_or(|last| (t - last) * edge.length() > eps) + { + clean.push(t); + } + } + if clean + .last() + .is_some_and(|last| 1.0 - last > eps / edge.length()) + { + clean.push(1.0); + } + for window in clean.windows(2) { + let piece = edge.slice(window[0], window[1]); + if piece.length() > eps { + split.push(piece); + } + } + } + split +} + +fn boundary_edges( + split: Vec, + eps: f64, + filled: impl Fn(Pt2) -> bool, +) -> Vec { + let mut boundary = Vec::new(); + let mut seen = HashSet::new(); + for edge in split { + let left = filled(probe(&edge, 1.0, eps)); + let right = filled(probe(&edge, -1.0, eps)); + if left == right { + continue; + } + let oriented = if left { edge } else { edge.reversed() }; + let key = ( + quant(oriented.point(0.0), eps), + quant(oriented.point(0.5), eps), + quant(oriented.point(1.0), eps), + ); + if seen.insert(key) { + boundary.push(oriented); + } + } + boundary +} + +fn probe(edge: &CurveEdge2, side: f64, eps: f64) -> Pt2 { + let mid = edge.point(0.5); + let tangent = edge.tangent(0.5); + let length = tangent.x.hypot(tangent.z); + let distance = (edge.length() * 0.1).min(1.0e-4).max(eps * 2.0); + add( + mid, + Pt2::new( + -side * tangent.z * distance / length, + side * tangent.x * distance / length, + ), + ) +} + +fn quant(point: Pt2, eps: f64) -> (i64, i64) { + let step = eps * 2.0; + ( + (point.x / step).round() as i64, + (point.z / step).round() as i64, + ) +} + +fn boundary_loops( + boundary: &[CurveEdge2], + eps: f64, +) -> Result>, GeometryError> { + let mut outgoing: HashMap<(i64, i64), Vec> = HashMap::new(); + for (index, edge) in boundary.iter().enumerate() { + outgoing + .entry(quant(edge.point(0.0), eps)) + .or_default() + .push(index); + } + let mut used = vec![false; boundary.len()]; + let mut loops = Vec::new(); + for start in 0..boundary.len() { + if used[start] { + continue; + } + let mut current = start; + let mut ring = Vec::new(); + for _ in 0..=boundary.len() { + if used[current] { + break; + } + used[current] = true; + let edge = &boundary[current]; + ring.push(edge.clone()); + let at = quant(edge.point(1.0), eps); + if at == quant(boundary[start].point(0.0), eps) { + break; + } + let reverse = scale(edge.tangent(1.0), -1.0); + let mut best = None; + let mut best_turn = f64::INFINITY; + for &candidate in outgoing.get(&at).map(Vec::as_slice).unwrap_or(&[]) { + if used[candidate] { + continue; + } + let direction = boundary[candidate].tangent(0.0); + let mut turn = -cross(reverse, direction).atan2(dot(reverse, direction)); + if turn <= 1.0e-12 { + turn += TAU; + } + if turn < best_turn { + best_turn = turn; + best = Some(candidate); + } + } + match best { + Some(next) => current = next, + None => break, + } + } + if ring.len() < 2 + || ring + .last() + .is_none_or(|last| distance(last.point(1.0), ring[0].point(0.0)) > eps) + || ring_area(&ring).abs() <= eps * eps + { + return unresolved("Curved Boolean produced an open or degenerate boundary"); + } + loops.push(ring); + } + Ok(loops) +} + +fn nested_regions(loops: &[Vec], eps: f64) -> Vec { + let mut result: Vec = loops + .iter() + .filter(|ring| ring_area(ring) > 0.0) + .map(|ring| CurveRegion2 { + outer: reversed_ring(ring), + holes: Vec::new(), + }) + .collect(); + for hole in loops.iter().filter(|ring| ring_area(ring) < 0.0) { + let sample = probe(&hole[0], -1.0, eps); + let index = result + .iter() + .enumerate() + .filter(|(_, region)| winding(sample, ®ion.outer, eps) != 0) + .min_by(|(_, first), (_, second)| { + ring_area(&first.outer) + .abs() + .total_cmp(&ring_area(&second.outer).abs()) + }) + .map(|(index, _)| index); + if let Some(index) = index { + result[index].holes.push(reversed_ring(hole)); + } + } + result +} + +fn canonicalize_regions(result: &mut [CurveRegion2]) { + for region in result.iter_mut() { + canonicalize_ring(&mut region.outer); + for hole in &mut region.holes { + canonicalize_ring(hole); + } + region + .holes + .sort_by(|a, b| compare_points(a[0].point(0.0), b[0].point(0.0))); + } + result.sort_by(|a, b| compare_points(a.outer[0].point(0.0), b.outer[0].point(0.0))); +} diff --git a/main/opengeometry/src/geom2d/mod.rs b/main/opengeometry/src/geom2d/mod.rs new file mode 100644 index 0000000..0ab5485 --- /dev/null +++ b/main/opengeometry/src/geom2d/mod.rs @@ -0,0 +1,12 @@ +mod boolean2d; +mod curved_boolean2d; +mod poly2d; +pub(super) use boolean2d::{ + boolean_oriented_regions, regions_from_edges_by, PlanarBooleanOp, RingRegion, +}; +pub(super) use curved_boolean2d::{ + boolean_curved_regions, intersections, reversed_ring, winding, CurveEdge2, CurveRegion2, +}; +pub(super) use poly2d::{ + point_in_ring2, segments_cross2, self_intersects2, signed_area2, winding_number2, Pt2, +}; diff --git a/main/opengeometry/src/geom2d/poly2d.rs b/main/opengeometry/src/geom2d/poly2d.rs new file mode 100644 index 0000000..ba2cefa --- /dev/null +++ b/main/opengeometry/src/geom2d/poly2d.rs @@ -0,0 +1,182 @@ +#[derive(Clone, Copy, Debug)] +pub(crate) struct Pt2 { + pub(crate) x: f64, + pub(crate) z: f64, +} + +impl Pt2 { + pub(crate) fn new(x: f64, z: f64) -> Self { + Self { x, z } + } +} + +#[inline] +pub(super) fn cross2(a: Pt2, b: Pt2) -> f64 { + a.x * b.z - a.z * b.x +} + +#[inline] +pub(super) fn dot2(a: Pt2, b: Pt2) -> f64 { + a.x * b.x + a.z * b.z +} + +pub(crate) fn signed_area2(ring: &[Pt2]) -> f64 { + let mut area = 0.0; + let count = ring.len(); + for i in 0..count { + let c = ring[i]; + let n = ring[(i + 1) % count]; + area += c.x * n.z - n.x * c.z; + } + area / 2.0 +} + +pub(crate) fn segments_cross2(a1: Pt2, a2: Pt2, b1: Pt2, b2: Pt2, eps: f64) -> bool { + let d1 = Pt2::new(a2.x - a1.x, a2.z - a1.z); + let d2 = Pt2::new(b2.x - b1.x, b2.z - b1.z); + let denom = cross2(d1, d2); + if denom.abs() < eps { + return false; + } + let diff = Pt2::new(b1.x - a1.x, b1.z - a1.z); + let t = cross2(diff, d2) / denom; + let s = cross2(diff, d1) / denom; + t > eps && t < 1.0 - eps && s > eps && s < 1.0 - eps +} + +pub(crate) fn self_intersects2(ring: &[Pt2], eps: f64) -> bool { + let m = ring.len(); + for i in 0..m { + for j in (i + 1)..m { + if j == i + 1 || (i == 0 && j == m - 1) { + continue; + } + if segments_cross2(ring[i], ring[(i + 1) % m], ring[j], ring[(j + 1) % m], eps) { + return true; + } + } + } + false +} + +pub(super) fn dedupe_ring(ring: &[Pt2], eps: f64) -> Vec { + let mut out: Vec = Vec::with_capacity(ring.len()); + for p in ring { + match out.last() { + Some(last) if (last.x - p.x).hypot(last.z - p.z) <= eps => {} + _ => out.push(*p), + } + } + if out.len() > 1 { + let first = out[0]; + let last = out[out.len() - 1]; + if (first.x - last.x).hypot(first.z - last.z) <= eps { + out.pop(); + } + } + out +} + +pub(crate) fn point_in_ring2(p: Pt2, ring: &[Pt2]) -> bool { + let mut inside = false; + let n = ring.len(); + if n == 0 { + return false; + } + let mut j = n - 1; + for i in 0..n { + let a = ring[i]; + let b = ring[j]; + if (a.z > p.z) != (b.z > p.z) && p.x < (b.x - a.x) * (p.z - a.z) / (b.z - a.z) + a.x { + inside = !inside; + } + j = i; + } + inside +} + +pub(super) fn ring_inside2(inner: &[Pt2], outer: &[Pt2], eps: f64) -> bool { + for p in inner { + if !point_in_ring2(*p, outer) { + return false; + } + } + for i in 0..inner.len() { + for j in 0..outer.len() { + if segments_cross2( + inner[i], + inner[(i + 1) % inner.len()], + outer[j], + outer[(j + 1) % outer.len()], + eps, + ) { + return false; + } + } + } + true +} + +pub(super) fn segment_cross_t2( + a1: Pt2, + a2: Pt2, + b1: Pt2, + b2: Pt2, + eps: f64, +) -> Option<(Pt2, f64, f64)> { + let r = Pt2::new(a2.x - a1.x, a2.z - a1.z); + let s = Pt2::new(b2.x - b1.x, b2.z - b1.z); + let denom = cross2(r, s); + if denom.abs() < eps { + return None; + } + let qp = Pt2::new(b1.x - a1.x, b1.z - a1.z); + let ta = cross2(qp, s) / denom; + let tb = cross2(qp, r) / denom; + if ta > eps && ta < 1.0 - eps && tb > eps && tb < 1.0 - eps { + Some((Pt2::new(a1.x + ta * r.x, a1.z + ta * r.z), ta, tb)) + } else { + None + } +} + +pub(crate) fn winding_number2(p: Pt2, ring: &[Pt2]) -> i32 { + let mut wn = 0; + let count = ring.len(); + for i in 0..count { + let a = ring[i]; + let b = ring[(i + 1) % count]; + let on_left = cross2( + Pt2::new(b.x - a.x, b.z - a.z), + Pt2::new(p.x - a.x, p.z - a.z), + ); + if a.z <= p.z { + if b.z > p.z && on_left > 0.0 { + wn += 1; + } + } else if b.z <= p.z && on_left < 0.0 { + wn -= 1; + } + } + wn +} + +pub(super) type Edge2 = (Pt2, Pt2); + +pub(super) fn point_on_edge_t(a: Pt2, b: Pt2, p: Pt2, eps: f64) -> Option { + let rx = b.x - a.x; + let rz = b.z - a.z; + let len2 = rx * rx + rz * rz; + if len2 < eps * eps { + return None; + } + let t = ((p.x - a.x) * rx + (p.z - a.z) * rz) / len2; + if t <= eps || t >= 1.0 - eps { + return None; + } + if (a.x + t * rx - p.x).hypot(a.z + t * rz - p.z) < eps { + Some(t) + } else { + None + } +} diff --git a/main/opengeometry/src/geometry/boolean2d.rs b/main/opengeometry/src/geometry/boolean2d.rs deleted file mode 100644 index e93ae67..0000000 --- a/main/opengeometry/src/geometry/boolean2d.rs +++ /dev/null @@ -1,581 +0,0 @@ -//! Generic 2D nonzero-winding boolean: self-intersection resolution + multi-contour union. - -use crate::geometry::poly2d::*; - -/// Decompose a (possibly self-intersecting) closed ring into simple loops by -/// splitting at every self-crossing (stack method). Port of `decomposeSelfIntersections`. -/// The `HashMap` here is keyed by integer node id and only ever LOOKED UP (never -/// iterated), so it is deterministic. -pub fn decompose_self_intersections(ring: &[Pt2], eps: f64) -> Vec> { - let n = ring.len(); - if n < 3 { - return Vec::new(); - } - - struct Hit { - t: f64, - point: Pt2, - id: usize, - } - let mut per_edge: Vec> = (0..n).map(|_| Vec::new()).collect(); - let mut interns: Vec<(Pt2, usize)> = Vec::new(); - let mut next_id = n; // 0..n-1 = original vertices - - for i in 0..n { - for j in (i + 1)..n { - if j == (i + 1) % n || i == (j + 1) % n { - continue; // adjacent edges share a vertex - } - if let Some((point, ta, tb)) = - segment_cross_t2(ring[i], ring[(i + 1) % n], ring[j], ring[(j + 1) % n], eps) - { - let mut id = None; - for (pt, existing) in &interns { - if (pt.x - point.x).hypot(pt.z - point.z) < 1.0e-6 { - id = Some(*existing); - break; - } - } - let id = id.unwrap_or_else(|| { - let assigned = next_id; - next_id += 1; - interns.push((point, assigned)); - assigned - }); - per_edge[i].push(Hit { t: ta, point, id }); - per_edge[j].push(Hit { t: tb, point, id }); - } - } - } - - // Augmented cyclic node list: each original vertex, then its edge's crossings in order. - let mut aug: Vec<(usize, Pt2)> = Vec::new(); - for i in 0..n { - aug.push((i, ring[i])); - per_edge[i].sort_by(|a, b| a.t.partial_cmp(&b.t).unwrap_or(std::cmp::Ordering::Equal)); - for hit in &per_edge[i] { - aug.push((hit.id, hit.point)); - } - } - - // Stack decomposition: revisiting a node id pops the simple loop between. - let mut loops: Vec> = Vec::new(); - let mut path: Vec<(usize, Pt2)> = Vec::new(); - let mut index_of_id: std::collections::HashMap = std::collections::HashMap::new(); - for node in &aug { - if let Some(&at) = index_of_id.get(&node.0) { - let loop_pts: Vec = path[at..].iter().map(|nd| nd.1).collect(); - if loop_pts.len() >= 3 { - loops.push(loop_pts); - } - let mut m = path.len(); - while m > at + 1 { - m -= 1; - index_of_id.remove(&path[m].0); - } - path.truncate(at + 1); - } else { - index_of_id.insert(node.0, path.len()); - path.push(*node); - } - } - if path.len() >= 3 { - loops.push(path.iter().map(|nd| nd.1).collect()); - } - loops -} - -/// A point just off the midpoint of `loop`'s first usable edge, on the given side. -/// Port of `edgeProbe2`. -pub fn edge_probe2(ring: &[Pt2], side: f64) -> Option { - for i in 0..ring.len() { - let a = ring[i]; - let b = ring[(i + 1) % ring.len()]; - let ex = b.x - a.x; - let ez = b.z - a.z; - let len = ex.hypot(ez); - if len < 1.0e-9 { - continue; - } - let nx = -ez / len; - let nz = ex / len; - let d = (len * 0.25).min(1.0e-3); - let mx = (a.x + b.x) / 2.0; - let mz = (a.z + b.z) / 2.0; - return Some(Pt2::new(mx + side * nx * d, mz + side * nz * d)); - } - None -} - -/// A nonzero-winding region: outer boundary (CCW) + inner-void holes (CW). -pub struct RingRegion { - pub outer: Vec, - pub holes: Vec>, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum PlanarBooleanOp { - Union, - Intersection, - Subtraction, -} - -/// Resolve a self-intersecting offset ring into the boundary of its nonzero-winding -/// region. Port of `resolveRingNonzero`. Returns `None` only if nothing fillable remains. -pub fn resolve_ring_nonzero(input: &[Pt2], eps: f64) -> Option { - let ring = dedupe_ring(input, eps); - if ring.len() < 3 { - return None; - } - if !self_intersects2(&ring, eps) { - let outer = if signed_area2(&ring) < 0.0 { - ring.iter().rev().copied().collect() - } else { - ring.clone() - }; - return Some(RingRegion { - outer, - holes: Vec::new(), - }); - } - - let loops: Vec> = decompose_self_intersections(&ring, eps) - .into_iter() - .map(|l| dedupe_ring(&l, eps)) - .filter(|l| l.len() >= 3 && signed_area2(l).abs() > eps) - .collect(); - - let mut outer_rings: Vec> = Vec::new(); - let mut hole_rings: Vec> = Vec::new(); - for l in loops { - let (Some(p_in), Some(p_out)) = (edge_probe2(&l, 1.0), edge_probe2(&l, -1.0)) else { - continue; - }; - let filled_left = winding_number2(p_in, &ring) != 0; - let filled_right = winding_number2(p_out, &ring) != 0; - if filled_left == filled_right { - continue; // interior edge of the fill — not a boundary - } - let area = signed_area2(&l); // >0 means the +1 (left) side is the interior - let encloses_left = area > 0.0; - let is_outer = encloses_left == filled_left; - let mut ccw = l.clone(); - if signed_area2(&ccw) < 0.0 { - ccw.reverse(); - } - if is_outer { - outer_rings.push(ccw); - } else { - ccw.reverse(); // hole CW - hole_rings.push(ccw); - } - } - - if outer_rings.is_empty() { - return None; - } - // One connected mass: take the largest outer ring; holes are those nested in it. - outer_rings.sort_by(|a, b| { - signed_area2(b) - .abs() - .partial_cmp(&signed_area2(a).abs()) - .unwrap_or(std::cmp::Ordering::Equal) - }); - let outer = outer_rings.into_iter().next().unwrap(); - let holes = hole_rings - .into_iter() - .filter(|hole| ring_inside2(hole, &outer, eps)) - .collect(); - Some(RingRegion { outer, holes }) -} - -// ── Multi-contour winding boolean (overlapping-band merge / oriented clip) ──── -// Resolve a set of ORIENTED polygons into their filled-region boundary by a -// winding rule: build the planar arrangement (split every edge at all crossings -// + vertex-touches), keep only sub-edges that separate filled from empty, -// orient them filled-on-LEFT, and stitch into loops. Where contours OVERLAP the -// shared interior edges have filled on both sides → dropped. Pure 2D, no CSG. -// -// `union_polygons_nonzero` (winding ≠ 0, all-CCW inputs) is the overlapping -// stroke-band merge; `resolve_polygons_positive` (winding > 0) treats CCW rings -// as additive and CW rings as subtractive — the clip a setback envelope needs. - -/// Union CCW polygons by the NONZERO-WINDING rule (the crossing cleanup the -/// single-ring offset cannot do). -pub fn union_polygons_nonzero(polys: &[Vec], eps: f64) -> Vec { - winding_regions(polys, eps, |winding| winding != 0) -} - -/// Boundary of the POSITIVE-winding area of oriented rings: each CCW ring adds -/// +1, each CW ring −1, and a point is filled when the sum is > 0. With one CCW -/// base ring and CW cutters this is base-minus-cutters, keeping EVERY disjoint -/// remainder (a clip can split the base into separate regions). -pub fn resolve_polygons_positive(polys: &[Vec], eps: f64) -> Vec { - winding_regions(polys, eps, |winding| winding > 0) -} - -/// Boolean two oriented region sets without combining their winding fields. -/// Each set uses nonzero winding internally, so CCW outer loops and CW holes -/// retain their intended meaning even where the two inputs overlap. -pub fn boolean_oriented_regions( - a: &[Vec], - b: &[Vec], - operation: PlanarBooleanOp, - eps: f64, -) -> Vec { - let mut contours = Vec::with_capacity(a.len() + b.len()); - contours.extend_from_slice(a); - contours.extend_from_slice(b); - winding_regions_by(&contours, eps, |point| { - let in_a = a - .iter() - .map(|ring| winding_number2(point, ring)) - .sum::() - != 0; - let in_b = b - .iter() - .map(|ring| winding_number2(point, ring)) - .sum::() - != 0; - match operation { - PlanarBooleanOp::Union => in_a || in_b, - PlanarBooleanOp::Intersection => in_a && in_b, - PlanarBooleanOp::Subtraction => in_a && !in_b, - } - }) -} - -fn winding_regions(polys: &[Vec], eps: f64, filled: impl Fn(i32) -> bool) -> Vec { - winding_regions_by(polys, eps, |point| { - filled(polys.iter().map(|poly| winding_number2(point, poly)).sum()) - }) -} - -fn winding_regions_by( - polys: &[Vec], - eps: f64, - filled: impl Fn(Pt2) -> bool, -) -> Vec { - // 1. directed edges from all oriented polygons + the vertex set for T-touch splits. - let mut edges: Vec = Vec::new(); - for poly in polys { - let m = poly.len(); - for i in 0..m { - let a = poly[i]; - let b = poly[(i + 1) % m]; - if (a.x - b.x).hypot(a.z - b.z) > eps { - edges.push((a, b)); - } - } - } - regions_from_edges_by(&edges, eps, filled) -} - -/// Arrange closed contour edges and open intersection segments, classifying -/// both sides of every segment with the supplied material predicate. -pub fn regions_from_edges_by( - edges: &[Edge2], - eps: f64, - filled: impl Fn(Pt2) -> bool, -) -> Vec { - if edges.is_empty() { - return Vec::new(); - } - let verts: Vec = edges.iter().flat_map(|&(a, b)| [a, b]).collect(); - - // 2. split every edge at all crossings and on-edge vertices. - let mut split: Vec = Vec::new(); - for (i, &(ea, eb)) in edges.iter().enumerate() { - let rx = eb.x - ea.x; - let rz = eb.z - ea.z; - let mut ts: Vec = vec![0.0, 1.0]; - for (j, &(fa, fb)) in edges.iter().enumerate() { - if i != j { - if let Some((_, ta, _)) = segment_cross_t2(ea, eb, fa, fb, eps) { - ts.push(ta); - } - } - } - for &v in &verts { - if let Some(t) = point_on_edge_t(ea, eb, v, eps) { - ts.push(t); - } - } - ts.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); - let mut prev = f64::NEG_INFINITY; - let mut clean: Vec = Vec::new(); - for t in ts { - if t - prev > eps { - clean.push(t); - prev = t; - } - } - for w in clean.windows(2) { - let p0 = Pt2::new(ea.x + w[0] * rx, ea.z + w[0] * rz); - let p1 = Pt2::new(ea.x + w[1] * rx, ea.z + w[1] * rz); - if (p0.x - p1.x).hypot(p0.z - p1.z) > eps { - split.push((p0, p1)); - } - } - } - - // 3. keep sub-edges with filled on exactly one side; orient filled-on-LEFT. - let mut boundary: Vec = Vec::new(); - for &(a, b) in &split { - let dx = b.x - a.x; - let dz = b.z - a.z; - let len = dx.hypot(dz); - if len < eps { - continue; - } - let probe = (len * 0.25).min(1.0e-4); - let nx = -dz / len; - let nz = dx / len; - let mx = (a.x + b.x) / 2.0; - let mz = (a.z + b.z) / 2.0; - let left = filled(Pt2::new(mx + nx * probe, mz + nz * probe)); - let right = filled(Pt2::new(mx - nx * probe, mz - nz * probe)); - if left == right { - continue; // interior or exterior — not a boundary - } - if left { - boundary.push((a, b)); - } else { - boundary.push((b, a)); - } - } - // Drop coincident duplicates (e.g. two contours sharing a corner emit the - // same geometric boundary twice): both copies pass the probe test and the - // stitcher would trace the same loop twice. - let mut seen: std::collections::HashSet<((i64, i64), (i64, i64))> = - std::collections::HashSet::new(); - let qkey = |point: Pt2| { - let step = (2.0 * eps).max(f64::EPSILON); - ( - (point.x / step).round() as i64, - (point.z / step).round() as i64, - ) - }; - boundary.retain(|&(a, b)| seen.insert((qkey(a), qkey(b)))); - if boundary.is_empty() { - return Vec::new(); - } - - // 4. stitch into loops; at each vertex take the next outgoing edge by smallest - // clockwise turn from the reverse of the incoming edge (standard face trace). - let mut out_edges: std::collections::HashMap<(i64, i64), Vec> = - std::collections::HashMap::new(); - for (idx, &(a, _)) in boundary.iter().enumerate() { - out_edges.entry(qkey(a)).or_default().push(idx); - } - let mut used = vec![false; boundary.len()]; - let mut raw_loops: Vec> = Vec::new(); - for start in 0..boundary.len() { - if used[start] { - continue; - } - let mut loop_pts: Vec = Vec::new(); - let mut cur = start; - let mut guard = 0; - while !used[cur] && guard <= boundary.len() { - guard += 1; - used[cur] = true; - let (a, b) = boundary[cur]; - loop_pts.push(a); - let back = Pt2::new(a.x - b.x, a.z - b.z); // reverse incoming, from b toward a - let mut best: Option = None; - let mut best_cw = f64::INFINITY; - for &c in out_edges.get(&qkey(b)).map(|v| v.as_slice()).unwrap_or(&[]) { - if used[c] { - continue; - } - let (ca, cb) = boundary[c]; - let out_dir = Pt2::new(cb.x - ca.x, cb.z - ca.z); - let mut cw = -cross2(back, out_dir).atan2(dot2(back, out_dir)); - if cw < eps { - cw += 2.0 * std::f64::consts::PI; // exclude the straight-back edge unless forced - } - if cw < best_cw { - best_cw = cw; - best = Some(c); - } - } - match best { - Some(next) => cur = next, - None => break, - } - } - if loop_pts.len() >= 3 { - raw_loops.push(loop_pts); - } - } - - // 5. classify loops (CCW = outer / CW = hole) and nest holes into their outer. - let mut regions: Vec = Vec::new(); - let mut holes: Vec> = Vec::new(); - for raw in raw_loops { - let ring = dedupe_ring(&raw, eps); - if ring.len() < 3 || signed_area2(&ring).abs() < eps { - continue; - } - if signed_area2(&ring) > 0.0 { - regions.push(RingRegion { - outer: ring, - holes: Vec::new(), - }); - } else { - holes.push(ring); - } - } - for hole in holes { - if let Some(region) = regions - .iter_mut() - .find(|region| ring_inside2(&hole, ®ion.outer, eps)) - { - region.holes.push(hole); - } - } - regions -} - -#[cfg(test)] -mod tests { - use super::*; - - fn p(x: f64, z: f64) -> Pt2 { - Pt2::new(x, z) - } - - fn ccw_square(x0: f64, z0: f64, x1: f64, z1: f64) -> Vec { - vec![p(x0, z0), p(x1, z0), p(x1, z1), p(x0, z1)] - } - - fn cw(mut ring: Vec) -> Vec { - ring.reverse(); - ring - } - - #[test] - fn open_section_segment_partitions_a_classified_face() { - let square = ccw_square(0.0, 0.0, 2.0, 2.0); - let mut edges = square - .iter() - .enumerate() - .map(|(index, &point)| (point, square[(index + 1) % square.len()])) - .collect::>(); - edges.push((p(1.0, 0.0), p(1.0, 2.0))); - let regions = regions_from_edges_by(&edges, DEFAULT_EPS, |point| { - point.x > 0.0 && point.x < 1.0 && point.z > 0.0 && point.z < 2.0 - }); - assert_eq!(regions.len(), 1); - assert!((signed_area2(®ions[0].outer) - 2.0).abs() < 1.0e-9); - } - - /// A CW cutter strictly inside a CCW base subtracts to an annulus: - /// one region whose hole is the cutter. - #[test] - fn positive_clip_inner_cutter_makes_a_hole() { - let base = ccw_square(0.0, 0.0, 10.0, 10.0); - let cutter = cw(ccw_square(4.0, 4.0, 6.0, 6.0)); - let regions = resolve_polygons_positive(&[base, cutter], DEFAULT_EPS); - assert_eq!(regions.len(), 1, "one annular region"); - assert_eq!(regions[0].holes.len(), 1, "the cutter is its hole"); - assert!((signed_area2(®ions[0].outer) - 100.0).abs() < 1e-6); - assert!( - (signed_area2(®ions[0].holes[0]) + 4.0).abs() < 1e-6, - "hole is CW, area -4" - ); - } - - /// A CW cutter spanning the base's full width splits it into TWO disjoint - /// remainder regions — every remainder is kept, not just the largest. - #[test] - fn positive_clip_keeps_every_disjoint_remainder() { - let base = ccw_square(0.0, 0.0, 10.0, 10.0); - let cutter = cw(ccw_square(-1.0, 4.0, 11.0, 6.0)); - let regions = resolve_polygons_positive(&[base, cutter], DEFAULT_EPS); - assert_eq!(regions.len(), 2, "the band splits the base in two"); - for region in ®ions { - assert!( - (signed_area2(®ion.outer) - 40.0).abs() < 1e-6, - "each half is 10×4" - ); - assert!(region.holes.is_empty()); - } - } - - /// Cutters covering the whole base leave nothing — and a lone CW ring is - /// nothing too (no positive area anywhere), where the nonzero rule fills it. - #[test] - fn positive_clip_total_cover_and_lone_cutter_are_empty() { - let base = ccw_square(0.0, 0.0, 4.0, 4.0); - let cutter = cw(ccw_square(-1.0, -1.0, 5.0, 5.0)); - assert!(resolve_polygons_positive(&[base, cutter], DEFAULT_EPS).is_empty()); - - let lone = cw(ccw_square(0.0, 0.0, 1.0, 1.0)); - assert!(resolve_polygons_positive(&[lone.clone()], DEFAULT_EPS).is_empty()); - assert_eq!( - union_polygons_nonzero(&[lone], DEFAULT_EPS).len(), - 1, - "contrast: the nonzero rule fills a lone CW ring" - ); - } - - /// Overlapping cutters merge: clearance bands from adjacent parcel edges - /// carve one connected bite, not internal-edge artifacts. - #[test] - fn positive_clip_overlapping_cutters_merge() { - let base = ccw_square(0.0, 0.0, 10.0, 10.0); - let cutter_a = cw(ccw_square(-1.0, -1.0, 3.0, 3.0)); - let cutter_b = cw(ccw_square(1.0, 1.0, 4.0, 2.0)); - let regions = resolve_polygons_positive(&[base, cutter_a, cutter_b], DEFAULT_EPS); - assert_eq!(regions.len(), 1); - let region = ®ions[0]; - assert!(region.holes.is_empty()); - assert!( - !self_intersects2(®ion.outer, DEFAULT_EPS), - "merged outline is simple" - ); - let expected = 100.0 - 9.0 - 1.0; // corner bite 3×3 (clipped to base) + 1×1 extra strip - assert!((signed_area2(®ion.outer) - expected).abs() < 1e-6); - } - - #[test] - fn grouped_boolean_preserves_holes_and_all_operations() { - let a = vec![ - ccw_square(0.0, 0.0, 10.0, 10.0), - cw(ccw_square(4.0, 4.0, 6.0, 6.0)), - ]; - let b = vec![ccw_square(5.0, -1.0, 12.0, 5.0)]; - - let union = boolean_oriented_regions(&a, &b, PlanarBooleanOp::Union, DEFAULT_EPS); - assert_eq!(union.len(), 1); - assert_eq!(union[0].holes.len(), 1); - assert!((signed_area2(&union[0].holes[0]) + 3.0).abs() < 1e-6); - - let intersection = - boolean_oriented_regions(&a, &b, PlanarBooleanOp::Intersection, DEFAULT_EPS); - assert_eq!(intersection.len(), 1); - assert!(intersection[0].holes.is_empty()); - assert!((signed_area2(&intersection[0].outer) - 24.0).abs() < 1e-6); - - let subtraction = - boolean_oriented_regions(&a, &b, PlanarBooleanOp::Subtraction, DEFAULT_EPS); - assert_eq!(subtraction.len(), 1); - assert!(subtraction[0].holes.is_empty()); - assert!( - (signed_area2(&subtraction[0].outer) - 72.0).abs() < 1e-6, - "the cutter opens the original void to the exterior" - ); - } - - #[test] - fn arrangement_stitching_uses_the_requested_scale() { - let scale = 1.0e-4; - let a = vec![ccw_square(0.0, 0.0, scale, scale)]; - let b = vec![ccw_square(0.5 * scale, 0.0, 1.5 * scale, scale)]; - let result = boolean_oriented_regions(&a, &b, PlanarBooleanOp::Union, 1.0e-10); - assert_eq!(result.len(), 1); - assert!((signed_area2(&result[0].outer) - 1.5 * scale * scale).abs() < 1.0e-14); - } -} diff --git a/main/opengeometry/src/geometry/curved_boolean2d.rs b/main/opengeometry/src/geometry/curved_boolean2d.rs deleted file mode 100644 index b7ab2af..0000000 --- a/main/opengeometry/src/geometry/curved_boolean2d.rs +++ /dev/null @@ -1,1012 +0,0 @@ -//! Analytic 2D region Boolean for closed line/arc rings in the XZ plane. -//! Curves are split only at exact line/circle and circle/circle intersections; -//! no chords are introduced into the returned boundary. - -use super::boolean2d::PlanarBooleanOp; -use super::poly2d::Pt2; -use serde::{Deserialize, Serialize}; -use std::collections::{HashMap, HashSet}; -use std::f64::consts::TAU; - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] -pub enum CurveEdge2 { - Line { - from: [f64; 2], - to: [f64; 2], - }, - Arc { - center: [f64; 2], - radius: f64, - start_angle: f64, - sweep_angle: f64, - }, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(deny_unknown_fields)] -pub struct CurveRegion2 { - pub outer: Vec, - #[serde(default)] - pub holes: Vec>, -} - -fn p(a: [f64; 2]) -> Pt2 { - Pt2::new(a[0], a[1]) -} -fn arr(a: Pt2) -> [f64; 2] { - [a.x, a.z] -} -fn add(a: Pt2, b: Pt2) -> Pt2 { - Pt2::new(a.x + b.x, a.z + b.z) -} -fn sub(a: Pt2, b: Pt2) -> Pt2 { - Pt2::new(a.x - b.x, a.z - b.z) -} -fn scale(a: Pt2, s: f64) -> Pt2 { - Pt2::new(a.x * s, a.z * s) -} -fn dot(a: Pt2, b: Pt2) -> f64 { - a.x * b.x + a.z * b.z -} -fn cross(a: Pt2, b: Pt2) -> f64 { - a.x * b.z - a.z * b.x -} -fn distance(a: Pt2, b: Pt2) -> f64 { - sub(a, b).x.hypot(sub(a, b).z) -} - -fn compare_points(a: Pt2, b: Pt2) -> std::cmp::Ordering { - a.x.total_cmp(&b.x).then_with(|| a.z.total_cmp(&b.z)) -} - -fn canonicalize_ring(ring: &mut Vec) { - if let Some((index, _)) = ring.iter().enumerate().min_by(|(_, first), (_, second)| { - compare_points(first.point(0.0), second.point(0.0)) - .then_with(|| compare_points(first.point(0.5), second.point(0.5))) - .then_with(|| compare_points(first.point(1.0), second.point(1.0))) - }) { - ring.rotate_left(index); - } -} - -impl CurveEdge2 { - pub fn point(&self, t: f64) -> Pt2 { - match self { - Self::Line { from, to } => add(p(*from), scale(sub(p(*to), p(*from)), t)), - Self::Arc { - center, - radius, - start_angle, - sweep_angle, - } => { - let angle = start_angle + sweep_angle * t; - Pt2::new( - center[0] + radius * angle.cos(), - center[1] + radius * angle.sin(), - ) - } - } - } - - pub(crate) fn tangent(&self, t: f64) -> Pt2 { - match self { - Self::Line { from, to } => sub(p(*to), p(*from)), - Self::Arc { - radius, - start_angle, - sweep_angle, - .. - } => { - let angle = start_angle + sweep_angle * t; - Pt2::new( - -radius * sweep_angle * angle.sin(), - radius * sweep_angle * angle.cos(), - ) - } - } - } - - pub(crate) fn length(&self) -> f64 { - match self { - Self::Line { .. } => distance(self.point(0.0), self.point(1.0)), - Self::Arc { - radius, - sweep_angle, - .. - } => radius * sweep_angle.abs(), - } - } - - pub(crate) fn reverse(&self) -> Self { - match self { - Self::Line { from, to } => Self::Line { - from: *to, - to: *from, - }, - Self::Arc { - center, - radius, - start_angle, - sweep_angle, - } => Self::Arc { - center: *center, - radius: *radius, - start_angle: start_angle + sweep_angle, - sweep_angle: -*sweep_angle, - }, - } - } - - pub(crate) fn slice(&self, a: f64, b: f64) -> Self { - match self { - Self::Line { .. } => Self::Line { - from: arr(self.point(a)), - to: arr(self.point(b)), - }, - Self::Arc { - center, - radius, - start_angle, - sweep_angle, - } => Self::Arc { - center: *center, - radius: *radius, - start_angle: start_angle + sweep_angle * a, - sweep_angle: sweep_angle * (b - a), - }, - } - } - - pub(crate) fn parameter(&self, point: Pt2, eps: f64) -> Option { - match self { - Self::Line { from, to } => { - let d = sub(p(*to), p(*from)); - let len2 = dot(d, d); - if len2 <= eps * eps { - return None; - } - let t = dot(sub(point, p(*from)), d) / len2; - (t >= -eps / len2.sqrt() - && t <= 1.0 + eps / len2.sqrt() - && distance(self.point(t), point) <= eps) - .then_some(t.clamp(0.0, 1.0)) - } - Self::Arc { - center, - radius, - start_angle, - sweep_angle, - } => { - let radial = sub(point, p(*center)); - if (radial.x.hypot(radial.z) - radius).abs() > eps { - return None; - } - let angle = radial.z.atan2(radial.x); - let travel = if *sweep_angle > 0.0 { - (angle - start_angle).rem_euclid(TAU) - } else { - (start_angle - angle).rem_euclid(TAU) - }; - let delta = eps / radius; - if travel <= sweep_angle.abs() + delta { - Some((travel / sweep_angle.abs()).clamp(0.0, 1.0)) - } else if TAU - travel <= delta { - Some(0.0) - } else { - None - } - } - } - } - - pub(crate) fn twice_area(&self) -> f64 { - let a = self.point(0.0); - let b = self.point(1.0); - match self { - Self::Line { .. } => cross(a, b), - Self::Arc { - center, - radius, - sweep_angle, - .. - } => radius * radius * sweep_angle + center[0] * (b.z - a.z) - center[1] * (b.x - a.x), - } - } -} - -pub(crate) fn intersections(a: &CurveEdge2, b: &CurveEdge2, eps: f64) -> Vec { - let mut candidates = Vec::new(); - let mut offer = |hit: Pt2| { - if a.parameter(hit, eps).is_some() - && b.parameter(hit, eps).is_some() - && !candidates.iter().any(|prior| distance(*prior, hit) <= eps) - { - candidates.push(hit); - } - }; - match (a, b) { - (CurveEdge2::Line { from, to }, CurveEdge2::Line { from: bf, to: bt }) => { - let d = sub(p(*to), p(*from)); - let e = sub(p(*bt), p(*bf)); - let denom = cross(d, e); - if denom.abs() > eps * d.x.hypot(d.z) * e.x.hypot(e.z) { - offer(add( - p(*from), - scale(d, cross(sub(p(*bf), p(*from)), e) / denom), - )); - } else { - for endpoint in [*from, *to, *bf, *bt] { - offer(p(endpoint)); - } - } - } - (CurveEdge2::Line { from, to }, CurveEdge2::Arc { center, radius, .. }) - | (CurveEdge2::Arc { center, radius, .. }, CurveEdge2::Line { from, to }) => { - let d = sub(p(*to), p(*from)); - let f = sub(p(*from), p(*center)); - let aa = dot(d, d); - if aa <= eps * eps { - return candidates; - } - let bb = 2.0 * dot(f, d); - let cc = dot(f, f) - radius * radius; - let disc = bb * bb - 4.0 * aa * cc; - if disc >= -eps * eps * aa { - let root = disc.max(0.0).sqrt(); - for t in [(-bb - root) / (2.0 * aa), (-bb + root) / (2.0 * aa)] { - offer(add(p(*from), scale(d, t))); - } - } - } - ( - CurveEdge2::Arc { - center: ca, - radius: ra, - .. - }, - CurveEdge2::Arc { - center: cb, - radius: rb, - .. - }, - ) => { - let centre_delta = sub(p(*cb), p(*ca)); - let d = centre_delta.x.hypot(centre_delta.z); - if d <= eps && (ra - rb).abs() <= eps { - for endpoint in [a.point(0.0), a.point(1.0), b.point(0.0), b.point(1.0)] { - offer(endpoint); - } - } else if d > eps && d <= ra + rb + eps && d + ra.min(*rb) + eps >= ra.max(*rb) { - let along = (ra * ra - rb * rb + d * d) / (2.0 * d); - let height2 = ra * ra - along * along; - if height2 >= -eps * eps { - let u = scale(centre_delta, 1.0 / d); - let base = add(p(*ca), scale(u, along)); - let normal = Pt2::new(-u.z, u.x); - let h = height2.max(0.0).sqrt(); - offer(add(base, scale(normal, h))); - offer(add(base, scale(normal, -h))); - } - } - } - } - candidates -} - -fn ring_area(ring: &[CurveEdge2]) -> f64 { - ring.iter().map(CurveEdge2::twice_area).sum::() * 0.5 -} - -fn validate_ring(ring: &[CurveEdge2], expected_positive: bool, eps: f64) -> Result<(), String> { - if ring.len() < 2 { - return Err("Curved Boolean ring needs at least two edges".into()); - } - for (index, edge) in ring.iter().enumerate() { - if !edge.length().is_finite() - || edge.length() <= 4.0 * eps - || ![ - edge.point(0.0).x, - edge.point(0.0).z, - edge.point(1.0).x, - edge.point(1.0).z, - ] - .iter() - .all(|v| v.is_finite()) - { - return Err("Curved Boolean edge is unresolved".into()); - } - if let CurveEdge2::Arc { - center, - radius, - start_angle, - sweep_angle, - } = edge - { - if !center.iter().all(|v| v.is_finite()) - || !radius.is_finite() - || !start_angle.is_finite() - || !sweep_angle.is_finite() - || *radius <= 4.0 * eps - || sweep_angle.abs() >= TAU - { - return Err("Curved Boolean arc is invalid".into()); - } - } - let next = &ring[(index + 1) % ring.len()]; - if distance(edge.point(1.0), next.point(0.0)) > eps { - return Err("Curved Boolean ring is open".into()); - } - } - let area = ring_area(ring); - if area.abs() <= eps * eps || (area > 0.0) != expected_positive { - return Err("Curved Boolean ring has degenerate or incorrect winding".into()); - } - Ok(()) -} - -pub(crate) fn winding(point: Pt2, ring: &[CurveEdge2], eps: f64) -> i32 { - let mut total = 0; - for edge in ring { - match edge { - CurveEdge2::Line { from, to } => { - let a = p(*from); - let b = p(*to); - let az = if (a.z - point.z).abs() <= eps * 0.25 { - point.z - } else { - a.z - }; - let bz = if (b.z - point.z).abs() <= eps * 0.25 { - point.z - } else { - b.z - }; - if (az <= point.z && bz > point.z) || (bz <= point.z && az > point.z) { - let x = a.x + (point.z - az) * (b.x - a.x) / (bz - az); - if x > point.x + eps * 0.01 { - total += if bz > az { 1 } else { -1 }; - } - } - } - CurveEdge2::Arc { - center, - radius, - start_angle, - sweep_angle, - } => { - let sine = (point.z - center[1]) / radius; - if sine.abs() >= 1.0 { - continue; - } - let first = sine.asin(); - for angle in [first, std::f64::consts::PI - first] { - let hit = Pt2::new(center[0] + radius * angle.cos(), point.z); - if hit.x <= point.x + eps * 0.01 { - continue; - } - let travel = if *sweep_angle > 0.0 { - (angle - start_angle).rem_euclid(TAU) - } else { - (start_angle - angle).rem_euclid(TAU) - }; - let dz = radius * sweep_angle * angle.cos(); - if travel > sweep_angle.abs() + eps / radius - || (travel <= eps / radius && dz < 0.0) - || (travel >= sweep_angle.abs() - eps / radius && dz > 0.0) - { - continue; - } - if dz.abs() > eps * radius { - total += if dz > 0.0 { 1 } else { -1 }; - } - } - } - } - } - total -} - -fn quant(point: Pt2, eps: f64) -> (i64, i64) { - let step = eps * 2.0; - ( - (point.x / step).round() as i64, - (point.z / step).round() as i64, - ) -} - -fn reverse_ring(ring: &[CurveEdge2]) -> Vec { - ring.iter().rev().map(CurveEdge2::reverse).collect() -} - -fn probe(edge: &CurveEdge2, side: f64, eps: f64) -> Pt2 { - let mid = edge.point(0.5); - let tangent = edge.tangent(0.5); - let length = tangent.x.hypot(tangent.z); - let distance = (edge.length() * 0.1).min(1.0e-4).max(eps * 2.0); - add( - mid, - Pt2::new( - -side * tangent.z * distance / length, - side * tangent.x * distance / length, - ), - ) -} - -/// Boolean regions with analytic line and circular-arc boundaries. Input and -/// output use CW outer rings and CCW holes, matching `booleanRegions2D`. -pub fn boolean_curved_regions( - a: &[CurveRegion2], - b: &[CurveRegion2], - operation: PlanarBooleanOp, - eps: f64, -) -> Result, String> { - if !eps.is_finite() || eps <= 0.0 { - return Err("Invalid Boolean tolerance".into()); - } - let validate = |regions: &[CurveRegion2]| -> Result<(), String> { - for region in regions { - validate_ring(®ion.outer, false, eps)?; - for hole in ®ion.holes { - validate_ring(hole, true, eps)?; - } - } - Ok(()) - }; - validate(a)?; - validate(b)?; - let edges: Vec = a - .iter() - .chain(b) - .flat_map(|region| { - std::iter::once(®ion.outer) - .chain(region.holes.iter()) - .flat_map(|ring| ring.iter().cloned()) - }) - .collect(); - if edges.is_empty() { - return Ok(Vec::new()); - } - let inside = |point: Pt2, regions: &[CurveRegion2]| -> bool { - regions.iter().any(|region| { - winding(point, ®ion.outer, eps) != 0 - && !region - .holes - .iter() - .any(|hole| winding(point, hole, eps) != 0) - }) - }; - let filled = |point: Pt2| { - let left = inside(point, a); - let right = inside(point, b); - match operation { - PlanarBooleanOp::Union => left || right, - PlanarBooleanOp::Intersection => left && right, - PlanarBooleanOp::Subtraction => left && !right, - } - }; - let mut split = Vec::new(); - for (index, edge) in edges.iter().enumerate() { - let mut ts = vec![0.0, 1.0]; - for (other_index, other) in edges.iter().enumerate() { - if index == other_index { - continue; - } - for point in intersections(edge, other, eps) { - if let Some(t) = edge.parameter(point, eps) { - ts.push(t); - } - } - } - ts.sort_by(|x, y| x.total_cmp(y)); - let mut clean: Vec = Vec::new(); - for t in ts { - if clean - .last() - .is_none_or(|last| (t - last) * edge.length() > eps) - { - clean.push(t); - } - } - if clean - .last() - .is_some_and(|last| 1.0 - last > eps / edge.length()) - { - clean.push(1.0); - } - for window in clean.windows(2) { - let piece = edge.slice(window[0], window[1]); - if piece.length() > eps { - split.push(piece); - } - } - } - let mut boundary = Vec::new(); - let mut seen = HashSet::new(); - for edge in split { - let left = filled(probe(&edge, 1.0, eps)); - let right = filled(probe(&edge, -1.0, eps)); - if left == right { - continue; - } - let oriented = if left { edge } else { edge.reverse() }; - let key = ( - quant(oriented.point(0.0), eps), - quant(oriented.point(0.5), eps), - quant(oriented.point(1.0), eps), - ); - if seen.insert(key) { - boundary.push(oriented); - } - } - let mut outgoing: HashMap<(i64, i64), Vec> = HashMap::new(); - for (index, edge) in boundary.iter().enumerate() { - outgoing - .entry(quant(edge.point(0.0), eps)) - .or_default() - .push(index); - } - let mut used = vec![false; boundary.len()]; - let mut loops = Vec::new(); - for start in 0..boundary.len() { - if used[start] { - continue; - } - let mut current = start; - let mut ring = Vec::new(); - for _ in 0..=boundary.len() { - if used[current] { - break; - } - used[current] = true; - let edge = &boundary[current]; - ring.push(edge.clone()); - let at = quant(edge.point(1.0), eps); - if at == quant(boundary[start].point(0.0), eps) { - break; - } - let reverse = scale(edge.tangent(1.0), -1.0); - let mut best = None; - let mut best_turn = f64::INFINITY; - for &candidate in outgoing.get(&at).map(Vec::as_slice).unwrap_or(&[]) { - if used[candidate] { - continue; - } - let direction = boundary[candidate].tangent(0.0); - let mut turn = -cross(reverse, direction).atan2(dot(reverse, direction)); - if turn <= 1.0e-12 { - turn += TAU; - } - if turn < best_turn { - best_turn = turn; - best = Some(candidate); - } - } - match best { - Some(next) => current = next, - None => break, - } - } - if ring.len() < 2 - || distance(ring.last().unwrap().point(1.0), ring[0].point(0.0)) > eps - || ring_area(&ring).abs() <= eps * eps - { - return Err("Curved Boolean produced an open or degenerate boundary".into()); - } - loops.push(ring); - } - let mut result: Vec = loops - .iter() - .filter(|ring| ring_area(ring) > 0.0) - .map(|ring| CurveRegion2 { - outer: reverse_ring(ring), - holes: Vec::new(), - }) - .collect(); - for hole in loops.iter().filter(|ring| ring_area(ring) < 0.0) { - let sample = probe(&hole[0], -1.0, eps); - let index = result - .iter() - .enumerate() - .filter(|(_, region)| winding(sample, ®ion.outer, eps) != 0) - .min_by(|(_, first), (_, second)| { - ring_area(&first.outer) - .abs() - .total_cmp(&ring_area(&second.outer).abs()) - }) - .map(|(index, _)| index); - if let Some(index) = index { - result[index].holes.push(reverse_ring(hole)); - } - } - for region in &mut result { - canonicalize_ring(&mut region.outer); - for hole in &mut region.holes { - canonicalize_ring(hole); - } - region - .holes - .sort_by(|a, b| compare_points(a[0].point(0.0), b[0].point(0.0))); - } - result.sort_by(|a, b| compare_points(a.outer[0].point(0.0), b.outer[0].point(0.0))); - Ok(result) -} - -#[cfg(test)] -mod tests { - use super::*; - - fn sector(start: f64, end: f64) -> CurveRegion2 { - let at = |radius: f64, angle: f64| [radius * angle.cos(), radius * angle.sin()]; - CurveRegion2 { - outer: vec![ - CurveEdge2::Arc { - center: [0.0, 0.0], - radius: 2.0, - start_angle: end, - sweep_angle: start - end, - }, - CurveEdge2::Line { - from: at(2.0, start), - to: at(1.0, start), - }, - CurveEdge2::Arc { - center: [0.0, 0.0], - radius: 1.0, - start_angle: start, - sweep_angle: end - start, - }, - CurveEdge2::Line { - from: at(1.0, end), - to: at(2.0, end), - }, - ], - holes: Vec::new(), - } - } - - fn rectangle(x0: f64, z0: f64, x1: f64, z1: f64) -> CurveRegion2 { - let points = [[x0, z0], [x0, z1], [x1, z1], [x1, z0]]; - CurveRegion2 { - outer: (0..4) - .map(|index| CurveEdge2::Line { - from: points[index], - to: points[(index + 1) % 4], - }) - .collect(), - holes: Vec::new(), - } - } - - fn area(regions: &[CurveRegion2]) -> f64 { - regions - .iter() - .map(|region| { - -ring_area(®ion.outer) - - region.holes.iter().map(|hole| ring_area(hole)).sum::() - }) - .sum() - } - - #[test] - fn coincident_arc_boundaries_preserve_exact_overlap() { - let a = sector(0.0, std::f64::consts::FRAC_PI_2); - let b = sector( - std::f64::consts::FRAC_PI_4, - 3.0 * std::f64::consts::FRAC_PI_4, - ); - let expected = 1.5 * std::f64::consts::FRAC_PI_4; - for (operation, target) in [ - (PlanarBooleanOp::Intersection, expected), - ( - PlanarBooleanOp::Union, - 1.5 * 3.0 * std::f64::consts::FRAC_PI_4, - ), - (PlanarBooleanOp::Subtraction, expected), - ] { - let out = boolean_curved_regions(&[a.clone()], &[b.clone()], operation, 1e-7).unwrap(); - assert_eq!(out.len(), 1); - assert!( - (area(&out) - target).abs() < 1e-6, - "{operation:?}: expected {target}, got {}", - area(&out) - ); - assert!(out[0] - .outer - .iter() - .any(|edge| matches!(edge, CurveEdge2::Arc { .. }))); - } - } - - #[test] - fn line_arc_cut_splits_sector_without_chording() { - let base = sector(0.0, std::f64::consts::FRAC_PI_2); - let cutter = rectangle(1.2, -0.2, 1.8, 2.2); - let out = boolean_curved_regions( - &[base.clone()], - &[cutter], - PlanarBooleanOp::Subtraction, - 1e-7, - ) - .unwrap(); - assert_eq!(out.len(), 2); - assert!(out.iter().all(|region| ring_area(®ion.outer) < 0.0)); - assert!( - out.iter() - .flat_map(|region| ®ion.outer) - .filter(|edge| matches!(edge, CurveEdge2::Arc { .. })) - .count() - >= 2 - ); - assert!(area(&out) > 0.0 && area(&out) < area(&[base])); - } - - #[test] - fn identical_arc_region_union_and_subtraction_are_canonical() { - let source = sector(-0.4, 1.7); - let union = boolean_curved_regions( - &[source.clone()], - &[source.clone()], - PlanarBooleanOp::Union, - 1e-7, - ) - .unwrap(); - assert_eq!(union.len(), 1); - assert!((area(&union) - area(&[source.clone()])).abs() < 1e-6); - let difference = boolean_curved_regions( - &[source.clone()], - &[source], - PlanarBooleanOp::Subtraction, - 1e-7, - ) - .unwrap(); - assert!(difference.is_empty()); - } - - #[test] - fn random_concentric_sector_booleans_match_analytic_areas() { - let mut seed = 0x8a53_71de_u64; - let mut random = || { - seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); - ((seed >> 32) as f64) / (u32::MAX as f64) - }; - for case in 0..120 { - let a0 = -0.7 + 1.4 * random(); - let a1 = a0 + 0.2 + 1.0 * random(); - let b0 = -0.7 + 1.4 * random(); - let b1 = b0 + 0.2 + 1.0 * random(); - let a = sector(a0, a1); - let b = sector(b0, b1); - let overlap = (a1.min(b1) - a0.max(b0)).max(0.0); - for (operation, expected) in [ - (PlanarBooleanOp::Union, 1.5 * (a1 - a0 + b1 - b0 - overlap)), - (PlanarBooleanOp::Intersection, 1.5 * overlap), - (PlanarBooleanOp::Subtraction, 1.5 * (a1 - a0 - overlap)), - ] { - let out = - boolean_curved_regions(&[a.clone()], &[b.clone()], operation, 1e-7).unwrap(); - assert!( - (area(&out) - expected).abs() < 2e-6, - "case {case} {operation:?}: expected {expected}, got {}", - area(&out) - ); - assert!( - out.iter() - .flat_map(|region| ®ion.outer) - .any(|edge| matches!(edge, CurveEdge2::Arc { .. })) - || out.is_empty() - ); - } - } - } - - #[test] - fn continuation_pair_subtracts_from_both_sides_of_a_third_host() { - let base = rectangle(-2.0, -0.1, 0.30000000000000027, 0.1); - let north = rectangle(-0.1, 0.0, 0.1, 2.0); - let south_arc = CurveRegion2 { - outer: vec![ - CurveEdge2::Line { - from: [-0.10000000000000009, 1.3471114790620887e-16], - to: [0.09999999999999998, 1.1021821192326179e-16], - }, - CurveEdge2::Arc { - center: [1.0, 0.0], - radius: 0.9, - start_angle: std::f64::consts::PI, - sweep_angle: std::f64::consts::FRAC_PI_2, - }, - CurveEdge2::Line { - from: [0.9999999999999999, -0.9], - to: [0.9999999999999998, -1.1], - }, - CurveEdge2::Arc { - center: [1.0, 0.0], - radius: 1.1, - start_angle: 3.0 * std::f64::consts::FRAC_PI_2, - sweep_angle: -std::f64::consts::FRAC_PI_2, - }, - ], - holes: Vec::new(), - }; - let out = boolean_curved_regions( - &[base], - &[north, south_arc], - PlanarBooleanOp::Subtraction, - 1e-6, - ) - .unwrap(); - assert_eq!(out.len(), 2); - assert!(out - .iter() - .any(|region| region.outer.iter().any(|edge| edge.point(0.5).x < -1.0))); - let original = rectangle(-2.0, -0.1, 0.0, 0.1); - let intersections: Vec<_> = out - .iter() - .map(|region| { - boolean_curved_regions( - &[region.clone()], - &[original.clone()], - PlanarBooleanOp::Intersection, - 1e-6, - ) - .unwrap() - }) - .collect(); - assert_eq!( - intersections - .iter() - .filter(|regions| !regions.is_empty()) - .count(), - 1 - ); - for region in out { - boolean_curved_regions( - &[region], - &[original.clone()], - PlanarBooleanOp::Subtraction, - 1e-6, - ) - .unwrap(); - } - } - - #[test] - fn subtracting_concentric_disk_keeps_an_exact_circular_hole() { - let disk = |radius: f64| CurveRegion2 { - outer: vec![ - CurveEdge2::Arc { - center: [0.0, 0.0], - radius, - start_angle: 0.0, - sweep_angle: -std::f64::consts::PI, - }, - CurveEdge2::Arc { - center: [0.0, 0.0], - radius, - start_angle: -std::f64::consts::PI, - sweep_angle: -std::f64::consts::PI, - }, - ], - holes: Vec::new(), - }; - let out = boolean_curved_regions( - &[disk(2.0)], - &[disk(1.0)], - PlanarBooleanOp::Subtraction, - 1e-7, - ) - .unwrap(); - assert_eq!(out.len(), 1); - assert_eq!(out[0].holes.len(), 1); - assert!((area(&out) - 3.0 * std::f64::consts::PI).abs() < 1e-7); - assert!(out[0].holes[0] - .iter() - .all(|edge| matches!(edge, CurveEdge2::Arc { .. }))); - } - - #[test] - fn intersecting_disks_keep_exact_circle_arcs_and_lens_area() { - let disk = |cx: f64| CurveRegion2 { - outer: vec![ - CurveEdge2::Arc { - center: [cx, 0.0], - radius: 2.0, - start_angle: 0.0, - sweep_angle: -std::f64::consts::PI, - }, - CurveEdge2::Arc { - center: [cx, 0.0], - radius: 2.0, - start_angle: -std::f64::consts::PI, - sweep_angle: -std::f64::consts::PI, - }, - ], - holes: Vec::new(), - }; - let out = boolean_curved_regions( - &[disk(-1.0)], - &[disk(1.0)], - PlanarBooleanOp::Intersection, - 1e-7, - ) - .unwrap(); - let expected = 8.0 * (0.5_f64).acos() - 2.0 * 3.0_f64.sqrt(); - assert_eq!(out.len(), 1); - assert!(out[0] - .outer - .iter() - .all(|edge| matches!(edge, CurveEdge2::Arc { .. }))); - assert!((area(&out) - expected).abs() < 1e-7); - } - - #[test] - fn disjoint_output_has_canonical_region_and_ring_order() { - let left = rectangle(-3.0, -1.0, -2.0, 1.0); - let right = rectangle(2.0, -1.0, 3.0, 1.0); - let first = boolean_curved_regions( - &[right.clone(), left.clone()], - &[], - PlanarBooleanOp::Union, - 1e-7, - ) - .unwrap(); - let second = - boolean_curved_regions(&[left, right], &[], PlanarBooleanOp::Union, 1e-7).unwrap(); - assert_eq!( - serde_json::to_value(&first).unwrap(), - serde_json::to_value(&second).unwrap() - ); - assert_eq!(first.len(), 2); - assert_eq!(first[0].outer[0].point(0.0).x, -3.0); - assert_eq!(first[1].outer[0].point(0.0).x, 2.0); - } - - #[test] - fn translated_and_rotated_circle_lenses_match_analytic_area() { - let disk = |center: [f64; 2], angle: f64| CurveRegion2 { - outer: vec![ - CurveEdge2::Arc { - center, - radius: 2.0, - start_angle: angle, - sweep_angle: -std::f64::consts::PI, - }, - CurveEdge2::Arc { - center, - radius: 2.0, - start_angle: angle - std::f64::consts::PI, - sweep_angle: -std::f64::consts::PI, - }, - ], - holes: Vec::new(), - }; - for index in 0..80 { - let angle = index as f64 * 0.783_124; - let distance = 0.25 + (index % 31) as f64 * 0.115; - let centre = [ - 3.0 + (index % 7) as f64 * 0.23, - -4.0 + (index % 9) as f64 * 0.31, - ]; - let other = [ - centre[0] + distance * angle.cos(), - centre[1] + distance * angle.sin(), - ]; - let actual = boolean_curved_regions( - &[disk(centre, angle / 3.0)], - &[disk(other, angle / 5.0)], - PlanarBooleanOp::Intersection, - 1e-7, - ) - .unwrap(); - let expected = 8.0 * (distance / 4.0).acos() - - 0.5 * distance * (16.0 - distance * distance).sqrt(); - assert_eq!(actual.len(), 1, "case {index}"); - assert!( - (area(&actual) - expected).abs() < 1e-6, - "case {index}: expected {expected}, got {}", - area(&actual) - ); - } - } -} diff --git a/main/opengeometry/src/geometry/geometrybuffer.rs b/main/opengeometry/src/geometry/geometrybuffer.rs deleted file mode 100644 index eba1a06..0000000 --- a/main/opengeometry/src/geometry/geometrybuffer.rs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Geometry Module - * - * The final geometry is stored in geometry buffer. - * It has trangles vertices and outline vertices, nothing more. - * It is the final stage of geometry before it is sent to GPU for rendering. - * The geometry buffer is created from BREP and then sent to GPU or other rendering systems. - */ - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -use crate::spatial::placement::Placement3D; - -#[derive(Clone, Serialize, Deserialize)] -pub struct GeometryBuffer { - id: Uuid, - // flat vertices for triangles, 3 values per vertex (x, y, z) - pub vertices: Vec, - pub outline_vertices: Vec, - // TODO: add other geometry buffer properties such as normals, uvs, etc. -} - -impl GeometryBuffer { - // Why Getter and Setter - https://github.com/rustwasm/wasm-bindgen/issues/1775 - pub fn set_id(&mut self, id: Uuid) { - self.id = id; - } - - pub fn id(&self) -> Uuid { - self.id - } - - pub fn new(id: Uuid) -> GeometryBuffer { - GeometryBuffer { - id, - vertices: Vec::new(), - outline_vertices: Vec::new(), - } - } - - /** - * Applies the given placement's world transformation to the geometry buffer. This function transforms all vertices in the geometry buffer from their local space to world space using the transformation defined by the placement. The transformation includes translation, rotation, and scaling as specified in the placement's world matrix. After applying this function, the vertices in the geometry buffer will be updated to reflect their new positions in world space, which can then be used for rendering or further processing. - */ - pub fn apply_transform(&mut self, placement: &Placement3D) { - let placement_matrix = placement.world_matrix(); - transform_flat_vertex_buffer(&mut self.vertices, &placement_matrix); - transform_flat_vertex_buffer(&mut self.outline_vertices, &placement_matrix); - } - - pub fn transformed(&self, placement: &Placement3D) -> GeometryBuffer { - let mut geometry = self.clone(); - geometry.apply_transform(placement); - geometry - } - - pub fn reset_geometry(&mut self) { - self.vertices.clear(); - self.outline_vertices.clear(); - } - - pub fn get_center(&self) -> Vector3 { - let Some(first_vertex) = self.vertices.chunks_exact(3).next() else { - return Vector3::new(0.0, 0.0, 0.0); - }; - - let mut min_x = first_vertex[0]; - let mut min_y = first_vertex[1]; - let mut min_z = first_vertex[2]; - let mut max_x = first_vertex[0]; - let mut max_y = first_vertex[1]; - let mut max_z = first_vertex[2]; - - for vertex in self.vertices.chunks_exact(3).skip(1) { - min_x = min_x.min(vertex[0]); - min_y = min_y.min(vertex[1]); - min_z = min_z.min(vertex[2]); - max_x = max_x.max(vertex[0]); - max_y = max_y.max(vertex[1]); - max_z = max_z.max(vertex[2]); - } - - Vector3::new( - (min_x + max_x) * 0.5, - (min_y + max_y) * 0.5, - (min_z + max_z) * 0.5, - ) - } -} - -fn transform_flat_vertex_buffer(buffer: &mut [f64], matrix: &openmaths::Matrix4) { - for vertex in buffer.chunks_exact_mut(3) { - let mut point = Vector3::new(vertex[0], vertex[1], vertex[2]); - point.apply_matrix4(matrix.clone()); - vertex[0] = point.x; - vertex[1] = point.y; - vertex[2] = point.z; - } -} diff --git a/main/opengeometry/src/geometry/offset2d.rs b/main/opengeometry/src/geometry/offset2d.rs deleted file mode 100644 index e4befc4..0000000 --- a/main/opengeometry/src/geometry/offset2d.rs +++ /dev/null @@ -1,448 +0,0 @@ -//! Generic 2D polyline offset: miter/bevel joins, mitered bands, offset outline. - -use crate::geometry::boolean2d::*; -use crate::geometry::poly2d::*; -use openmaths::Vector3; - -/// Whether a centreline is (or should be treated as) a closed loop: the caller's -/// `closed` flag OR a first≈last coincidence. Returns the working points (closing -/// duplicate stripped) and the resolved `closed` flag. -pub fn resolve_closed(points: &[Pt2], closed: bool, eps: f64) -> (Vec, bool) { - let mut pts = points.to_vec(); - let mut is_closed = closed; - if pts.len() >= 2 { - let first = pts[0]; - let last = pts[pts.len() - 1]; - if (first.x - last.x).hypot(first.z - last.z) < eps { - is_closed = true; - pts.pop(); - } - } - (pts, is_closed) -} - -/// Drop centreline vertices that create a segment shorter than `min_length`. -pub fn simplify_polyline(centreline: &[Vector3], min_length: f64, closed: bool) -> Vec { - if centreline.len() < 3 || min_length <= 0.0 { - return centreline.to_vec(); - } - let near = |a: &Vector3, b: &Vector3| (a.x - b.x).hypot(a.z - b.z); - - let mut pts = centreline.to_vec(); - let mut had_closing_dup = false; - if closed && pts.len() >= 2 && near(&pts[0], &pts[pts.len() - 1]) < DEFAULT_EPS { - pts.pop(); - had_closing_dup = true; - } - - let mut kept: Vec = vec![pts[0]]; - for current in pts.iter().skip(1) { - if near(current, kept.last().unwrap()) >= min_length { - kept.push(*current); - } - } - - if !closed { - // Keep the true endpoint (overall length matters): replace a short tail, else append. - let endpoint = pts[pts.len() - 1]; - let last = *kept.last().unwrap(); - if near(&endpoint, &last) > DEFAULT_EPS { - if kept.len() >= 2 && near(&endpoint, &last) < min_length { - *kept.last_mut().unwrap() = endpoint; - } else { - kept.push(endpoint); - } - } - } else { - // Drop a too-short closing segment (last kept → first). - if kept.len() > 3 && near(&kept[0], kept.last().unwrap()) < min_length { - kept.pop(); - } - if had_closing_dup { - let first = kept[0]; - kept.push(Vector3::new(first.x, first.y, first.z)); - } - } - - // Never over-collapse below a buildable polyline. - let min_kept = if closed { - if had_closing_dup { - 4 - } else { - 3 - } - } else { - 2 - }; - if kept.len() < min_kept { - return centreline.to_vec(); - } - kept -} - -/// A stroked region as a clean outline: outer ring + inner-ring holes (XZ at base_y). -pub struct Outline2D { - pub outer: Vec, - pub holes: Vec>, -} - -/// Per-segment MITERED bands (each CCW). Convex corners carry the offset-edge miter -/// (a shared apex with the neighbour, so the convex corner is FILLED — no square-cap -/// notch); concave corners carry the segment's own perpendicular cap and let the -/// neighbour band overlap to fill the fold. This is the same per-corner join a -/// simple centreline uses, so a self-crossing centreline's vertices join identically. -/// `miter_limit` ∞ keeps convex corners full miters; spikes are bevelled post-union. -pub fn mitered_offset_bands( - pts: &[Pt2], - closed: bool, - half_width: f64, - miter_limit: f64, - eps: f64, -) -> Vec> { - let n = pts.len(); - if n < 2 { - return Vec::new(); - } - let seg_count = if closed { n } else { n - 1 }; - let mut dir: Vec = Vec::with_capacity(seg_count); - let mut left_normal: Vec = Vec::with_capacity(seg_count); - for i in 0..seg_count { - let a = pts[i]; - let b = pts[(i + 1) % n]; - let length = (b.x - a.x).hypot(b.z - a.z); - if length < eps { - return Vec::new(); - } - dir.push(Pt2::new((b.x - a.x) / length, (b.z - a.z) / length)); - left_normal.push(Pt2::new(-(b.z - a.z) / length, (b.x - a.x) / length)); - } - let cap = |vi: usize, seg: usize, side: f64| -> Pt2 { - Pt2::new( - pts[vi].x + side * half_width * left_normal[seg].x, - pts[vi].z + side * half_width * left_normal[seg].z, - ) - }; - let corner = |vi: usize, in_seg: usize, out_seg: usize, side: f64, this_seg: usize| -> Pt2 { - let turn = cross2(dir[in_seg], dir[out_seg]); - if side * turn >= 0.0 { - return cap(vi, this_seg, side); // concave side → own cap, let overlap fill - } - let Some(meet) = intersect2( - cap(vi, in_seg, side), - dir[in_seg], - cap(vi, out_seg, side), - dir[out_seg], - eps, - ) else { - return cap(vi, this_seg, side); - }; - let cos_deflection = dot2(dir[in_seg], dir[out_seg]).clamp(-1.0, 1.0); - let miter_ratio = 1.0 / (cos_deflection.acos() / 2.0).cos(); - if miter_ratio > miter_limit { - return cap(vi, this_seg, side); - } - meet - }; - - let mut bands: Vec> = Vec::with_capacity(seg_count); - for i in 0..seg_count { - let start_vi = i; - let end_vi = (i + 1) % n; - let start_free = !closed && i == 0; - let end_free = !closed && i == seg_count - 1; - let prev_seg = (i + seg_count - 1) % seg_count; - let next_seg = (i + 1) % seg_count; - let mut quad: Vec = vec![ - if start_free { - cap(start_vi, i, 1.0) - } else { - corner(start_vi, prev_seg, i, 1.0, i) - }, - if end_free { - cap(end_vi, i, 1.0) - } else { - corner(end_vi, i, next_seg, 1.0, i) - }, - if end_free { - cap(end_vi, i, -1.0) - } else { - corner(end_vi, i, next_seg, -1.0, i) - }, - if start_free { - cap(start_vi, i, -1.0) - } else { - corner(start_vi, prev_seg, i, -1.0, i) - }, - ]; - if self_intersects2(&quad, eps) || signed_area2(&quad).abs() < eps { - // Over-deep trim folded the quad — fall back to a plain rectangle band. - quad = vec![ - cap(start_vi, i, 1.0), - cap(end_vi, i, 1.0), - cap(end_vi, i, -1.0), - cap(start_vi, i, -1.0), - ]; - } - if signed_area2(&quad) < 0.0 { - quad.reverse(); - } - bands.push(quad); - } - bands -} - -/// Replace sharp convex miter spikes on a 2D ring with a flat BEVEL (the chamfer -/// joining the two offset points), past `miter_limit` — the same threshold the -/// per-corner joins use. Concave vertices and corners within the limit are kept. -pub fn bevel_sharp_convex_pts( - ring: &[Pt2], - half_width: f64, - miter_limit: f64, - eps: f64, -) -> Vec { - let count = ring.len(); - if count < 3 { - return ring.to_vec(); - } - let ring_is_ccw = signed_area2(ring) > 0.0; - let mut out: Vec = Vec::with_capacity(count); - for i in 0..count { - let prev = ring[(i + count - 1) % count]; - let vertex = ring[i]; - let next = ring[(i + 1) % count]; - let in_dir = Pt2::new(vertex.x - prev.x, vertex.z - prev.z); - let in_len = in_dir.x.hypot(in_dir.z); - let out_dir = Pt2::new(next.x - vertex.x, next.z - vertex.z); - let out_len = out_dir.x.hypot(out_dir.z); - if in_len < eps || out_len < eps { - out.push(vertex); - continue; - } - let to_prev = Pt2::new(-in_dir.x / in_len, -in_dir.z / in_len); - let to_next = Pt2::new(out_dir.x / out_len, out_dir.z / out_len); - let turn = cross2( - Pt2::new(in_dir.x / in_len, in_dir.z / in_len), - Pt2::new(out_dir.x / out_len, out_dir.z / out_len), - ); - let convex = if ring_is_ccw { turn > eps } else { turn < -eps }; - let interior_angle = dot2(to_prev, to_next).clamp(-1.0, 1.0).acos(); - let ratio = if interior_angle > eps { - 1.0 / (interior_angle / 2.0).sin() - } else { - f64::INFINITY - }; - if !convex || ratio <= miter_limit { - out.push(vertex); - continue; - } - let mut trim = half_width / (interior_angle / 2.0).tan(); - trim = trim.min(0.5 * in_len).min(0.5 * out_len); - if trim <= eps { - out.push(vertex); - continue; - } - out.push(Pt2::new( - vertex.x + to_prev.x * trim, - vertex.z + to_prev.z * trim, - )); - out.push(Pt2::new( - vertex.x + to_next.x * trim, - vertex.z + to_next.z * trim, - )); - } - out -} - -/// Boundary points contributed at a vertex on one side. -/// Convex side → miter (1 pt) within the limit, else a flat bevel (2 caps). -/// Concave side → inner trim (1 pt). -#[allow(clippy::too_many_arguments)] -pub fn join_points( - vertex: Pt2, - in_dir: Pt2, - out_dir: Pt2, - in_normal: Pt2, - out_normal: Pt2, - side: f64, - half_width: f64, - miter_limit: f64, - eps: f64, - forward: bool, -) -> Vec { - let in_anchor = Pt2::new( - vertex.x + side * half_width * in_normal.x, - vertex.z + side * half_width * in_normal.z, - ); - let out_anchor = Pt2::new( - vertex.x + side * half_width * out_normal.x, - vertex.z + side * half_width * out_normal.z, - ); - let Some(corner) = intersect2(in_anchor, in_dir, out_anchor, out_dir, eps) else { - return vec![in_anchor]; // collinear — anchors coincide - }; - - let turn = cross2(in_dir, out_dir); - let side_convex = side * turn < 0.0; - if !side_convex { - return vec![corner]; // concave inner trim - } - - let deflection = dot2(in_dir, out_dir).clamp(-1.0, 1.0).acos(); - let miter_ratio = 1.0 / (deflection / 2.0).cos(); - if miter_ratio <= miter_limit { - return vec![corner]; // miter - } - if forward { - vec![in_anchor, out_anchor] - } else { - vec![out_anchor, in_anchor] - } // flat bevel -} - -/// Offset a centreline into a clean outline polygon with miter/bevel joins. -/// Returns `None` if the outline (or, for a closed centreline, either ring) would -/// still self-intersect after nonzero-winding resolution. -pub fn offset_outline( - centreline: &[Vector3], - width: f64, - closed: bool, - miter_limit: f64, - eps: f64, -) -> Option { - let half_width = width / 2.0; - let base_y = centreline.first().map(|v| v.y).unwrap_or(0.0); - - let raw: Vec = centreline.iter().map(xz).collect(); - let (pts, closed) = resolve_closed(&raw, closed, eps); - let n = pts.len(); - if n < 2 || (closed && n < 3) { - return None; - } - - let seg_count = if closed { n } else { n - 1 }; - let mut dir: Vec = Vec::with_capacity(seg_count); - let mut left_normal: Vec = Vec::with_capacity(seg_count); - for i in 0..seg_count { - let a = pts[i]; - let b = pts[(i + 1) % n]; - let dx = b.x - a.x; - let dz = b.z - a.z; - let length = dx.hypot(dz); - if length < eps { - return None; - } - dir.push(Pt2::new(dx / length, dz / length)); - left_normal.push(Pt2::new(-dz / length, dx / length)); - } - - let points_at = - |vi: usize, in_seg: usize, out_seg: usize, side: f64, forward: bool| -> Vec { - join_points( - pts[vi], - dir[in_seg], - dir[out_seg], - left_normal[in_seg], - left_normal[out_seg], - side, - half_width, - miter_limit, - eps, - forward, - ) - }; - let cap = |vi: usize, seg: usize, side: f64| -> Pt2 { - Pt2::new( - pts[vi].x + side * half_width * left_normal[seg].x, - pts[vi].z + side * half_width * left_normal[seg].z, - ) - }; - - let to_v3 = |ring: Vec| -> Vec { - ring.into_iter() - .map(|p| Vector3::new(p.x, base_y, p.z)) - .collect() - }; - - if !closed { - let mut left: Vec = vec![cap(0, 0, 1.0)]; - for vi in 1..=(n - 2) { - left.extend(points_at(vi, vi - 1, vi, 1.0, true)); - } - left.push(cap(n - 1, seg_count - 1, 1.0)); - - let mut right: Vec = vec![cap(n - 1, seg_count - 1, -1.0)]; - for vi in (1..=(n - 2)).rev() { - right.extend(points_at(vi, vi - 1, vi, -1.0, false)); - } - right.push(cap(0, 0, -1.0)); - - let mut combined = left; - combined.extend(right); - let ring = dedupe_ring(&combined, eps); - if ring.len() < 3 { - return None; - } - let resolved = resolve_ring_nonzero(&ring, eps)?; - return Some(Outline2D { - outer: to_v3(resolved.outer), - holes: resolved.holes.into_iter().map(to_v3).collect(), - }); - } - - // A simple closed centreline may not cross itself (e.g. a figure-8): its offset - // can't be a single outer+holes region, so reject cleanly rather than emit a partial lobe. - if self_intersects2(&pts, eps) { - return None; - } - - // Closed: two rings (offset +h and −h all the way round), each resolved by - // nonzero winding. The larger-area region is the outer boundary; the smaller is - // the interior void (a hole) — UNLESS the inner offset collapses or inverts (a - // stroke as wide as its loop), in which case the stroke fills the region: SOLID, no hole. - let ring_for_side = |side: f64| -> Vec { - let mut out: Vec = Vec::new(); - for vi in 0..n { - out.extend(points_at(vi, (vi + n - 1) % n, vi, side, true)); - } - dedupe_ring(&out, eps) - }; - - // Keep only sides that produced a real (positive-area) region; the larger is outer. - let mut regions: Vec = [ - resolve_ring_nonzero(&ring_for_side(1.0), eps), - resolve_ring_nonzero(&ring_for_side(-1.0), eps), - ] - .into_iter() - .flatten() - .filter(|region| signed_area2(®ion.outer).abs() >= eps) - .collect(); - if regions.is_empty() { - return None; // both offset rings collapsed — genuinely unbuildable - } - regions.sort_by(|a, b| { - signed_area2(&b.outer) - .abs() - .partial_cmp(&signed_area2(&a.outer).abs()) - .unwrap_or(std::cmp::Ordering::Equal) - }); - let outer_region = regions.remove(0); - - // The smaller side is the interior void only if it nests cleanly inside the outer; - // otherwise it collapsed/inverted → no hole (solid mass). - let mut hole_rings: Vec> = Vec::new(); - if let Some(inner_region) = regions.into_iter().next() { - if ring_inside2(&inner_region.outer, &outer_region.outer, eps) { - hole_rings.push(inner_region.outer); - } - } - hole_rings.extend(outer_region.holes); - let holes: Vec> = hole_rings - .into_iter() - .filter(|hole| ring_inside2(hole, &outer_region.outer, eps)) - .collect(); - - Some(Outline2D { - outer: to_v3(outer_region.outer), - holes: holes.into_iter().map(to_v3).collect(), - }) -} diff --git a/main/opengeometry/src/geometry/offset_regions.rs b/main/opengeometry/src/geometry/offset_regions.rs deleted file mode 100644 index 82ea72a..0000000 --- a/main/opengeometry/src/geometry/offset_regions.rs +++ /dev/null @@ -1,1517 +0,0 @@ -//! Fixed-width polyline offset: centreline + width -> stroked region(s), plus a -//! group offset that merges several overlapping strokes into one. Built on the -//! generic `geometry` 2D toolkit. It accepts arbitrary planar paths and -//! ring boundaries. - -use crate::geometry::boolean2d::*; -use crate::geometry::curved_boolean2d::{boolean_curved_regions, CurveRegion2}; -use crate::geometry::offset2d::*; -use crate::geometry::poly2d::*; -use openmaths::Vector3; -use wasm_bindgen::prelude::*; - -/// One stroked region: a CW outer ring + its CCW inner-void holes (canonical). -pub type OffsetRegion = (Vec, Vec>); - -pub fn boolean_offset_regions( - a: &[OffsetRegion], - b: &[OffsetRegion], - operation: PlanarBooleanOp, -) -> Result, String> { - let base_y = a - .iter() - .chain(b) - .find_map(|(outer, _)| outer.first().map(|point| point.y)) - .unwrap_or(0.0); - let contours = |regions: &[OffsetRegion]| -> Result>, String> { - let mut loops = Vec::new(); - for (outer, holes) in regions { - for (ring, outer_ring) in - std::iter::once((outer, true)).chain(holes.iter().map(|hole| (hole, false))) - { - if ring.len() < 3 - || ring.iter().any(|point| { - !point.x.is_finite() - || !point.y.is_finite() - || !point.z.is_finite() - || (point.y - base_y).abs() > DEFAULT_EPS - }) - { - return Err( - "Boolean region ring must contain at least three finite coplanar points" - .into(), - ); - } - let area = signed_area_xz(ring); - if area.abs() <= DEFAULT_EPS * DEFAULT_EPS - || (outer_ring && area >= 0.0) - || (!outer_ring && area <= 0.0) - { - return Err("Boolean region ring has degenerate or incorrect winding".into()); - } - loops.push(ring.iter().map(xz).collect()); - } - } - Ok(loops) - }; - let a_contours = contours(a)?; - let b_contours = contours(b)?; - Ok( - boolean_oriented_regions(&a_contours, &b_contours, operation, DEFAULT_EPS) - .into_iter() - .map(|region| finalize_region(®ion.outer, ®ion.holes, base_y)) - .collect(), - ) -} - -#[derive(serde::Deserialize)] -struct BooleanRegionJson { - outer: Vec<[f64; 3]>, - holes: Vec>, -} - -#[wasm_bindgen(js_name = booleanRegions2D)] -pub fn boolean_regions_2d_wasm( - a_json: String, - b_json: String, - operation: String, -) -> Result { - let parse = |json: &str| -> Result, JsValue> { - let regions: Vec = serde_json::from_str(json).map_err(|error| { - JsValue::from_str(&format!("Invalid Boolean regions JSON: {error}")) - })?; - Ok(regions - .into_iter() - .map(|region| { - let points = |ring: Vec<[f64; 3]>| { - ring.into_iter() - .map(|point| Vector3::new(point[0], point[1], point[2])) - .collect() - }; - ( - points(region.outer), - region.holes.into_iter().map(points).collect(), - ) - }) - .collect()) - }; - let operation = match operation.as_str() { - "union" => PlanarBooleanOp::Union, - "intersection" => PlanarBooleanOp::Intersection, - "subtraction" => PlanarBooleanOp::Subtraction, - _ => return Err(JsValue::from_str("Unknown 2D Boolean operation")), - }; - let result = boolean_offset_regions(&parse(&a_json)?, &parse(&b_json)?, operation) - .map_err(|error| JsValue::from_str(&error))?; - OGOffsetRegionsResult::from_regions(result).map_err(|error| JsValue::from_str(&error)) -} - -/// Exact line/arc region Boolean. The JSON rings contain analytic edges rather -/// than vertices, and the result keeps arcs as arcs. As with booleanRegions2D, -/// outer rings are CW in XZ and holes are CCW. -#[wasm_bindgen(js_name = booleanCurvedRegions2D)] -pub fn boolean_curved_regions_2d_wasm( - a_json: String, - b_json: String, - operation: String, -) -> Result { - let parse = |json: &str| -> Result, JsValue> { - serde_json::from_str(json).map_err(|error| { - JsValue::from_str(&format!("Invalid curved Boolean regions JSON: {error}")) - }) - }; - let operation = match operation.as_str() { - "union" => PlanarBooleanOp::Union, - "intersection" => PlanarBooleanOp::Intersection, - "subtraction" => PlanarBooleanOp::Subtraction, - _ => return Err(JsValue::from_str("Unknown 2D Boolean operation")), - }; - let result = boolean_curved_regions(&parse(&a_json)?, &parse(&b_json)?, operation, DEFAULT_EPS) - .map_err(|error| JsValue::from_str(&error))?; - serde_json::to_string(&result).map_err(|error| { - JsValue::from_str(&format!("Cannot serialize curved Boolean result: {error}")) - }) -} - -#[cfg(test)] -mod planar_boolean_export_tests { - use super::*; - - fn ring(points: &[(f64, f64)]) -> Vec { - points - .iter() - .map(|(x, z)| Vector3::new(*x, 0.0, *z)) - .collect() - } - - #[test] - fn exported_boolean_preserves_split_regions_and_winding() { - let a = vec![( - ring(&[(0.0, 0.0), (0.0, 4.0), (10.0, 4.0), (10.0, 0.0)]), - Vec::new(), - )]; - let b = vec![( - ring(&[(4.0, -1.0), (4.0, 5.0), (6.0, 5.0), (6.0, -1.0)]), - Vec::new(), - )]; - let out = boolean_offset_regions(&a, &b, PlanarBooleanOp::Subtraction).unwrap(); - assert_eq!(out.len(), 2); - for (outer, holes) in out { - assert!(holes.is_empty()); - assert!(signed_area_xz(&outer) < 0.0); - } - } - - #[test] - fn exported_boolean_matches_random_rectangle_areas() { - let mut seed = 0x9e37_79b9_u64; - let mut random = || { - seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); - ((seed >> 32) as f64) / (u32::MAX as f64) - }; - let rectangle = |x0: f64, z0: f64, x1: f64, z1: f64| { - vec![(ring(&[(x0, z0), (x0, z1), (x1, z1), (x1, z0)]), Vec::new())] - }; - let area = |regions: Vec| { - regions - .iter() - .map(|(outer, holes)| { - -signed_area_xz(outer) - - holes.iter().map(|hole| signed_area_xz(hole)).sum::() - }) - .sum::() - }; - for _ in 0..100 { - let aw = 0.5 + random() * 4.0; - let ah = 0.5 + random() * 4.0; - let bx = -2.0 + random() * 6.0; - let bz = -2.0 + random() * 6.0; - let bw = 0.5 + random() * 4.0; - let bh = 0.5 + random() * 4.0; - let overlap = (aw.min(bx + bw) - 0.0_f64.max(bx)).max(0.0) - * (ah.min(bz + bh) - 0.0_f64.max(bz)).max(0.0); - let a = rectangle(0.0, 0.0, aw, ah); - let b = rectangle(bx, bz, bx + bw, bz + bh); - for (operation, expected) in [ - (PlanarBooleanOp::Union, aw * ah + bw * bh - overlap), - (PlanarBooleanOp::Intersection, overlap), - (PlanarBooleanOp::Subtraction, aw * ah - overlap), - ] { - let result = boolean_offset_regions(&a, &b, operation).unwrap(); - assert!( - (area(result) - expected).abs() < 1.0e-5, - "{operation:?}: expected {expected}" - ); - } - } - } - - #[test] - fn exported_boolean_preserves_random_oblique_holes() { - let mut seed = 0x8bad_f00d_u64; - let mut random = || { - seed = seed - .wrapping_mul(2862933555777941757) - .wrapping_add(3037000493); - ((seed >> 32) as f64) / (u32::MAX as f64) - }; - let host = vec![( - ring(&[(0.0, 0.0), (0.0, 4.0), (4.0, 4.0), (4.0, 0.0)]), - Vec::new(), - )]; - for _ in 0..40 { - let width = 0.4 + random() * 1.6; - let depth = 0.4 + random() * 1.6; - let angle = random() * std::f64::consts::PI; - let (sin, cos) = angle.sin_cos(); - let corners = [ - (-width / 2.0, -depth / 2.0), - (-width / 2.0, depth / 2.0), - (width / 2.0, depth / 2.0), - (width / 2.0, -depth / 2.0), - ]; - let cutter = vec![( - corners - .iter() - .map(|(x, z)| { - Vector3::new(2.0 + x * cos - z * sin, 0.0, 2.0 + x * sin + z * cos) - }) - .collect(), - Vec::new(), - )]; - let output = - boolean_offset_regions(&host, &cutter, PlanarBooleanOp::Subtraction).unwrap(); - assert_eq!(output.len(), 1); - assert_eq!(output[0].1.len(), 1); - let area = -signed_area_xz(&output[0].0) - signed_area_xz(&output[0].1[0]); - assert!((area - (16.0 - width * depth)).abs() < 1.0e-6); - } - } -} - -/// Offset a centreline by ±half-width into one or more stroked regions, via the -/// deterministic analytic offset. A simple centreline (open, L, closed loop, -/// reflex, tight) yields ONE region from the single-ring offset. A self-crossing -/// CLOSED centreline (e.g. a figure-8) instead UNIONS its per-segment bands by -/// nonzero winding, so the overlapping strokes at the crossing MERGE into one -/// clean region (no internal edges) — the cleanup a single-ring offset can't do. -/// Each region is `normalize_winding` (CW outer / CCW holes) + `canonical_ring`. -/// Empty vec means no region remains. -pub fn offset_polyline_regions( - centreline: &[Vector3], - width: f64, - closed: bool, - miter_limit: f64, - eps: f64, -) -> Vec { - if width <= 0.0 || !width.is_finite() || centreline.len() < 2 { - return Vec::new(); - } - let simplified = simplify_polyline(centreline, width / 2.0, closed); - let base_y = simplified.first().map(|v| v.y).unwrap_or(0.0); - - let raw: Vec = simplified.iter().map(xz).collect(); - let (pts, is_closed) = resolve_closed(&raw, closed, eps); - - // Self-crossing closed centreline (figure-8 etc.): union the per-segment MITERED - // bands by nonzero winding so (a) every vertex joins with a miter like a normal - // corner (no square-cap notch), (b) the overlapping strokes at each crossing - // merge into one region, then bevel any convex spike past the miter limit — the - // same corner treatment every other corner gets. - if is_closed && self_intersects2(&pts, eps) { - let half_width = width / 2.0; - let bands = mitered_offset_bands(&pts, true, half_width, f64::INFINITY, eps); - return union_polygons_nonzero(&bands, eps) - .into_iter() - .map(|region| { - let outer = bevel_sharp_convex_pts(®ion.outer, half_width, miter_limit, eps); - finalize_region(&outer, ®ion.holes, base_y) - }) - .collect(); - } - - // Simple centrelines: the single-ring analytic offset (one region, or none if degenerate). - match offset_outline(&simplified, width, is_closed, miter_limit, eps) { - Some(outline) => { - let outer = canonical_ring(&normalize_winding(&outline.outer, Winding::Cw)); - let holes = outline - .holes - .iter() - .map(|hole| canonical_ring(&normalize_winding(hole, Winding::Ccw))) - .collect(); - vec![(outer, holes)] - } - None => Vec::new(), - } -} - -/// Convert a resolved 2D region (outer + holes, in Pt2) to an `OffsetRegion`: -/// lift to `base_y`, normalise winding (CW outer / CCW holes), canonicalise. -pub fn finalize_region(outer: &[Pt2], hole_rings: &[Vec], base_y: f64) -> OffsetRegion { - let to_v3 = |ring: &[Pt2]| -> Vec { - ring.iter() - .map(|p| Vector3::new(p.x, base_y, p.z)) - .collect() - }; - let outer = canonical_ring(&normalize_winding(&to_v3(outer), Winding::Cw)); - let holes = hole_rings - .iter() - .map(|hole| canonical_ring(&normalize_winding(&to_v3(hole), Winding::Ccw))) - .collect(); - (outer, holes) -} - -/// Segments per stadium end-cap semicircle. Caps are CIRCUMSCRIBED tangent -/// fans, so the forbidden zone always CONTAINS the exact clearance disc — the -/// envelope can under-claim by the tessellation sliver (≈0.9 % radially at 12 -/// segments) but can never claim a point closer to an edge than its distance. -const STADIUM_CAP_SEGMENTS: usize = 12; - -/// The forbidden zone of one edge: every point within `distance` of the -/// segment a→b — a stadium (rectangle + two circumscribed end-cap fans), -/// wound CW so it subtracts under the positive-winding rule. -fn forbidden_stadium(a: Pt2, b: Pt2, distance: f64) -> Vec { - let length = (b.x - a.x).hypot(b.z - a.z); - let u = Pt2::new((b.x - a.x) / length, (b.z - a.z) / length); - let n = Pt2::new(-u.z, u.x); - - let cap = |centre: Pt2, from_angle: f64| -> Vec { - // Tangent fan sweeping a semicircle CLOCKWISE from `from_angle`: - // vertices at the tangent-line intersections, radius d / cos(Δ/2). - let delta = std::f64::consts::PI / STADIUM_CAP_SEGMENTS as f64; - let radius = distance / (delta / 2.0).cos(); - (0..STADIUM_CAP_SEGMENTS) - .map(|j| { - let angle = from_angle - (j as f64 + 0.5) * delta; - Pt2::new( - centre.x + radius * angle.cos(), - centre.z + radius * angle.sin(), - ) - }) - .collect() - }; - - let normal_angle = n.z.atan2(n.x); - let mut ring: Vec = Vec::with_capacity(4 + 2 * STADIUM_CAP_SEGMENTS); - ring.push(Pt2::new(a.x + distance * n.x, a.z + distance * n.z)); - ring.push(Pt2::new(b.x + distance * n.x, b.z + distance * n.z)); - ring.extend(cap(b, normal_angle)); // sweep through +u, beyond b - ring.push(Pt2::new(b.x - distance * n.x, b.z - distance * n.z)); - ring.push(Pt2::new(a.x - distance * n.x, a.z - distance * n.z)); - ring.extend(cap(a, normal_angle + std::f64::consts::PI)); // through -u, beyond a - ring // n = u rotated +90°, so this traversal is CW (winding −1) -} - -/// One interior hole of a ring being inset: its own closed ring plus one -/// clearance distance per hole edge (edge i = ring[i] → ring[i+1]). The hole -/// is an excluded region — an excluded island — so the -/// inset GROWS the hole by its distances (the retained region keeps clear of -/// the hole's edges, exactly as it keeps clear of the outer boundary). -pub struct OffsetHole { - pub ring: Vec, - pub distances: Vec, -} - -/// Prepare one ring of a variable inset: strip the closing duplicate, check the -/// per-edge distance count and values, drop zero-length edges together with -/// their distance slot, and wind the ring the way the positive-winding clip -/// needs it (`want_ccw` = the base ring, else a subtractive hole). Reversing a -/// ring turns edge k into traversed-backward edge (m-2-k) mod m, so the -/// distances are remapped the same way. `Ok(None)` = the ring is geometrically -/// degenerate (< 3 edges, zero area, self-intersecting); `Err` = malformed -/// ARGUMENTS. -fn prepare_inset_ring( - ring: &[Vector3], - distances: &[f64], - want_ccw: bool, - label: &str, - eps: f64, -) -> Result, Vec)>, String> { - let raw: Vec = ring.iter().map(xz).collect(); - let (pts, _) = resolve_closed(&raw, true, eps); - let n = pts.len(); - - if distances.len() != n { - return Err(format!( - "Expected one distance per {} edge (edge i = ring[i] -> ring[i+1]): the ring has {} edges but {} distances were given", - label, - n, - distances.len() - )); - } - for (i, d) in distances.iter().enumerate() { - if !d.is_finite() || *d < 0.0 { - return Err(format!( - "{} distances[{}] = {} is invalid: every per-edge distance must be a finite value >= 0", - label, i, d - )); - } - } - - // Drop zero-length edges together with their distance slot, so the - // per-edge pairing stays aligned. (`simplify_polyline` is deliberately not - // used: its width-based vertex dropping would desync `distances`, and a - // collinear vertex splitting one boundary run into different clearances is a - // feature, not noise.) - let mut pts2: Vec = Vec::with_capacity(n); - let mut dist2: Vec = Vec::with_capacity(n); - for i in 0..n { - let a = pts[i]; - let b = pts[(i + 1) % n]; - if (a.x - b.x).hypot(a.z - b.z) < eps { - continue; - } - pts2.push(a); - dist2.push(distances[i]); - } - - let m = pts2.len(); - if m < 3 || signed_area2(&pts2).abs() <= eps || self_intersects2(&pts2, eps) { - return Ok(None); - } - - let is_ccw = signed_area2(&pts2) > 0.0; - if is_ccw != want_ccw { - pts2.reverse(); - let old = dist2.clone(); - for (k, slot) in dist2.iter_mut().enumerate() { - *slot = old[(2 * m - 2 - k) % m]; - } - } - Ok(Some((pts2, dist2))) -} - -/// Inset a CLOSED ring inward, one distance per edge (edge i = ring[i] → -/// ring[i+1], after stripping an optional closing duplicate). The result is -/// the clearance-exact inset region: the ring's interior minus, per edge, -/// every point within that edge's distance of the edge SEGMENT (not just its -/// line) — resolved as a positive-winding clip of the ring against per-edge -/// forbidden stadiums. Corners therefore become circumscribed clearance arcs -/// where distances differ (no miters, no bevels — both only approximate the -/// arc, and a bevel would violate the clearance), distant edges are honoured -/// across notches, and an over-deep inset collapses to nothing instead of -/// inverting. `holes` are excluded interior rings (each with its own per-edge -/// distances): they are subtracted from the region and their clearance zones -/// grow them outward, so a hole can also split the region or swallow it. -/// Returns zero or more disjoint regions (a deep inset can split a -/// waisted ring into separate lobes), each CW-outer / CCW-holes, canonicalised -/// — the same output contract as `offset_polyline_regions`. `Ok(vec![])` = the -/// inset collapsed or the outer ring is degenerate; `Err` = malformed -/// ARGUMENTS (distance count mismatch, negative / non-finite distance, or a -/// degenerate hole ring — a hole is an explicit argument, so a broken one is a -/// caller bug rather than "no region remains"). -pub fn offset_ring_variable( - ring: &[Vector3], - distances: &[f64], - holes: &[OffsetHole], - eps: f64, -) -> Result, String> { - let base_y = ring.first().map(|v| v.y).unwrap_or(0.0); - - // The clip needs the base ring CCW (+1) against CW cutters (−1). - let Some((pts2, dist2)) = prepare_inset_ring(ring, distances, true, "ring", eps)? else { - return Ok(Vec::new()); // geometrically collapsed input — empty, not an error - }; - let m = pts2.len(); - - let mut hole_rings: Vec<(Vec, Vec)> = Vec::with_capacity(holes.len()); - for (h, hole) in holes.iter().enumerate() { - let label = format!("holes[{}]", h); - match prepare_inset_ring(&hole.ring, &hole.distances, false, &label, eps)? { - Some(prepared) => hole_rings.push(prepared), - None => { - return Err(format!( - "{} is degenerate: a hole needs at least 3 non-collinear, non-self-intersecting points", - label - )) - } - } - } - - let no_inset = dist2.iter().all(|d| *d == 0.0) - && hole_rings.iter().all(|(_, d)| d.iter().all(|d| *d == 0.0)); - if no_inset && hole_rings.is_empty() { - return Ok(vec![finalize_region(&pts2, &[], base_y)]); // no-op inset - } - - let mut polys: Vec> = Vec::with_capacity(1 + m + hole_rings.len() * 8); - polys.push(pts2.clone()); - for i in 0..m { - if dist2[i] > 0.0 { - polys.push(forbidden_stadium(pts2[i], pts2[(i + 1) % m], dist2[i])); - } - } - for (hole, hole_dist) in &hole_rings { - polys.push(hole.clone()); // CW → subtracts the hole itself - let k = hole.len(); - for i in 0..k { - if hole_dist[i] > 0.0 { - polys.push(forbidden_stadium(hole[i], hole[(i + 1) % k], hole_dist[i])); - } - } - } - - Ok(resolve_polygons_positive(&polys, eps) - .into_iter() - .filter(|region| signed_area2(®ion.outer).abs() > eps) - .map(|region| finalize_region(®ion.outer, ®ion.holes, base_y)) - .collect()) -} - -/// One polyline in an offset group: its centreline, stroke width, and closed flag. -pub struct OffsetPolyline { - pub centreline: Vec, - pub width: f64, - pub closed: bool, -} - -/// Merge a GROUP of separate polylines (a crossing T / X / L overlap) into one -/// clean stroked region: every polyline's per-segment MITERED bands are collected -/// and unioned by nonzero winding, so the overlapping strokes at the crossing merge -/// into a single region (no internal edges) with mitered/bevelled corners — the -/// same engine and corner treatment a single stroke uses. Pure 2D, no CSG. Empty -/// vec = nothing built. -pub fn offset_polyline_group_regions( - polylines: &[OffsetPolyline], - miter_limit: f64, - eps: f64, -) -> Vec { - let mut bands: Vec> = Vec::new(); - let mut base_y = 0.0; - let mut max_half = 0.0_f64; - let mut have_base = false; - for polyline in polylines { - if polyline.width <= 0.0 || !polyline.width.is_finite() || polyline.centreline.len() < 2 { - continue; - } - let half = polyline.width / 2.0; - max_half = max_half.max(half); - let simplified = simplify_polyline(&polyline.centreline, half, polyline.closed); - if !have_base { - base_y = simplified.first().map(|v| v.y).unwrap_or(0.0); - have_base = true; - } - let raw: Vec = simplified.iter().map(xz).collect(); - let (pts, is_closed) = resolve_closed(&raw, polyline.closed, eps); - bands.extend(mitered_offset_bands( - &pts, - is_closed, - half, - f64::INFINITY, - eps, - )); - } - if bands.is_empty() { - return Vec::new(); - } - union_polygons_nonzero(&bands, eps) - .into_iter() - .map(|region| { - let outer = bevel_sharp_convex_pts(®ion.outer, max_half, miter_limit, eps); - finalize_region(&outer, ®ion.holes, base_y) - }) - .collect() -} - -// ── wasm boundary ──────────────────────────────────────────────────────────── - -/// One stroked region serialized for the JS boundary: CW outer ring + CCW holes. -#[derive(serde::Serialize)] -struct OffsetRegionJson { - outer: Vec, - holes: Vec>, -} - -/// Result payload for a polyline offset: a list of regions (each an outer ring + -/// inner-void holes). Normally one region; a self-crossing closed centreline (e.g. -/// a figure-8) yields one per simple sub-loop. The deterministic analytic offset — -/// no extrude/solid. Used to build the plan polygons + extruded base solids in JS. -#[wasm_bindgen] -pub struct OGOffsetRegionsResult { - regions_serialized: String, -} - -impl OGOffsetRegionsResult { - fn from_regions(regions: Vec) -> Result { - let json: Vec = regions - .into_iter() - .map(|(outer, holes)| OffsetRegionJson { outer, holes }) - .collect(); - let regions_serialized = serde_json::to_string(&json) - .map_err(|error| format!("Failed to serialize offset regions: {}", error))?; - Ok(Self { regions_serialized }) - } -} - -#[wasm_bindgen] -impl OGOffsetRegionsResult { - #[wasm_bindgen(getter, js_name = regionsSerialized)] - pub fn regions_serialized(&self) -> String { - self.regions_serialized.clone() - } -} - -/// Wasm entry point: offset a centreline by `width` into stroked regions via the -/// deterministic analytic offset. `centreline_flat` is `[x0,y0,z0, …]`. Returns -/// CW-outer / CCW-hole regions, canonicalised; empty (does NOT throw) when degenerate. -#[wasm_bindgen(js_name = offsetPolylineRegions)] -pub fn offset_polyline_regions_wasm( - centreline_flat: Vec, - width: f64, - closed: bool, - miter_limit: f64, -) -> Result { - if centreline_flat.len() % 3 != 0 { - return Err(JsValue::from_str( - "Centreline must be a flat [x,y,z,…] array whose length is a multiple of 3", - )); - } - let centreline: Vec = centreline_flat - .chunks_exact(3) - .map(|c| Vector3::new(c[0], c[1], c[2])) - .collect(); - - let limit = if miter_limit.is_finite() && miter_limit > 0.0 { - miter_limit - } else { - DEFAULT_MITER_LIMIT - }; - - let regions = offset_polyline_regions(¢reline, width, closed, limit, DEFAULT_EPS); - OGOffsetRegionsResult::from_regions(regions).map_err(|error| JsValue::from_str(&error)) -} - -/// One polyline in an offset group, as received over the JS boundary. -#[derive(serde::Deserialize)] -struct OffsetPolylineJson { - centreline: Vec, - width: f64, - closed: bool, -} - -/// Wasm entry point: merge a GROUP of separate polylines (a crossing T / X / L -/// overlap) into one clean stroked region by nonzero-winding union of their mitered -/// bands. `polylines_json` is `[{centreline:[x,y,z,…], width, closed}, …]`. Returns -/// CW-outer / CCW-hole regions (one or more); empty when no region remains. -#[wasm_bindgen(js_name = offsetPolylineGroupRegions)] -pub fn offset_polyline_group_regions_wasm( - polylines_json: String, -) -> Result { - let parsed: Vec = - serde_json::from_str(&polylines_json).map_err(|error| { - JsValue::from_str(&format!("Invalid offset group JSON payload: {}", error)) - })?; - let polylines: Vec = parsed - .into_iter() - .filter(|polyline| polyline.centreline.len() % 3 == 0) - .map(|polyline| OffsetPolyline { - centreline: polyline - .centreline - .chunks_exact(3) - .map(|c| Vector3::new(c[0], c[1], c[2])) - .collect(), - width: polyline.width, - closed: polyline.closed, - }) - .collect(); - - let regions = offset_polyline_group_regions(&polylines, DEFAULT_MITER_LIMIT, DEFAULT_EPS); - OGOffsetRegionsResult::from_regions(regions).map_err(|error| JsValue::from_str(&error)) -} - -/// One hole of a variable inset, as received over the JS boundary. -#[derive(serde::Deserialize)] -struct OffsetHoleJson { - ring: Vec, - distances: Vec, -} - -/// Wasm entry point: inset a CLOSED ring inward with one distance per edge -/// (edge i = ring[i] → ring[i+1]). `ring_flat` is `[x0,y0,z0, …]`; -/// `holes_json` is `[{ring:[x,y,z,…], distances:[…]}, …]` (`"[]"` = none) — -/// excluded interior rings, each grown outward by its own per-edge distances. -/// Returns CW-outer / CCW-hole regions, canonicalised; possibly several when a -/// deep inset (or a hole) splits the ring, and empty (does NOT throw) when the -/// inset collapses or the outer ring is degenerate. THROWS on malformed -/// arguments: a distance count that does not match an edge count, a negative / -/// non-finite distance, a degenerate hole, or unparsable `holes_json`. Corners -/// where distances differ are circumscribed clearance arcs (conservative), so -/// no output point is ever closer to an edge than that edge's distance — -/// there is no miter-limit knob because there are no miters. -#[wasm_bindgen(js_name = offsetRingVariable)] -pub fn offset_ring_variable_wasm( - ring_flat: Vec, - distances: Vec, - holes_json: String, -) -> Result { - if ring_flat.len() % 3 != 0 { - return Err(JsValue::from_str( - "Ring must be a flat [x,y,z,…] array whose length is a multiple of 3", - )); - } - let to_vectors = |flat: &[f64]| -> Vec { - flat.chunks_exact(3) - .map(|c| Vector3::new(c[0], c[1], c[2])) - .collect() - }; - let ring = to_vectors(&ring_flat); - - let parsed: Vec = serde_json::from_str(&holes_json) - .map_err(|error| JsValue::from_str(&format!("Invalid holes JSON payload: {}", error)))?; - let mut holes: Vec = Vec::with_capacity(parsed.len()); - for (h, hole) in parsed.into_iter().enumerate() { - if hole.ring.len() % 3 != 0 { - return Err(JsValue::from_str(&format!( - "holes[{}].ring must be a flat [x,y,z,…] array whose length is a multiple of 3", - h - ))); - } - holes.push(OffsetHole { - ring: to_vectors(&hole.ring), - distances: hole.distances, - }); - } - - let regions = offset_ring_variable(&ring, &distances, &holes, DEFAULT_EPS) - .map_err(|error| JsValue::from_str(&error))?; - OGOffsetRegionsResult::from_regions(regions).map_err(|error| JsValue::from_str(&error)) -} - -#[cfg(test)] -mod tests { - use super::*; - - fn pt(x: f64, z: f64) -> Vector3 { - Vector3::new(x, 0.0, z) - } - - /// A many-pointed star centreline (alternating outer/inner radius) — the - /// stress case with both sharp convex points and reflex notches. - fn star_centreline(points: usize, outer: f64, inner: f64) -> Vec { - let total = points * 2; - let mut ring: Vec = Vec::with_capacity(total + 1); - for i in 0..total { - let radius = if i % 2 == 0 { outer } else { inner }; - let angle = (i as f64 / total as f64) * std::f64::consts::TAU; - ring.push(pt(angle.cos() * radius, angle.sin() * radius)); - } - ring.push(ring[0]); // close - ring - } - - /// Unwrap a result expected to be a single region. - fn single(regions: Vec) -> OffsetRegion { - assert_eq!(regions.len(), 1, "expected exactly one region"); - regions.into_iter().next().unwrap() - } - - /// A straight stroke offsets the centreline by exactly half-width. - #[test] - fn straight_offsets_by_half_width() { - let centreline = vec![pt(0.0, 0.0), pt(2.0, 0.0)]; - let (outer, holes) = single(offset_polyline_regions( - ¢reline, - 0.3, - false, - DEFAULT_MITER_LIMIT, - DEFAULT_EPS, - )); - assert_eq!(outer.len(), 4, "a straight stroke is a 4-point rectangle"); - assert!(holes.is_empty(), "an open stroke has no holes"); - // Centreline runs along +x at z=0; offsets should be at z = ±0.15. - for v in &outer { - assert!((v.z.abs() - 0.15).abs() < 1e-9, "offset z = {}", v.z); - } - } - - /// An X-crossing of two separate strokes merges into one cross-shaped region - /// (no holes), with the crossing centre filled — not two overlapping rects. - #[test] - fn x_group_merges_into_one_region() { - let polylines = vec![ - OffsetPolyline { - centreline: vec![pt(-3.0, 0.0), pt(3.0, 0.0)], - width: 0.4, - closed: false, - }, - OffsetPolyline { - centreline: vec![pt(0.0, -3.0), pt(0.0, 3.0)], - width: 0.4, - closed: false, - }, - ]; - let regions = offset_polyline_group_regions(&polylines, DEFAULT_MITER_LIMIT, DEFAULT_EPS); - assert_eq!(regions.len(), 1, "an X-crossing merges into one region"); - let (outer, holes) = ®ions[0]; - assert!(holes.is_empty(), "a simple cross has no interior void"); - let ring: Vec = outer.iter().map(|v| Pt2::new(v.x, v.z)).collect(); - assert!( - !self_intersects2(&ring, DEFAULT_EPS), - "merged outline is simple" - ); - assert!( - point_in_ring2(Pt2::new(0.0, 0.0), &ring), - "crossing centre is filled" - ); - // A plus sign has 12 corners; certainly more than a single rectangle's 4. - assert!( - outer.len() >= 8, - "cross outline has the crossing's corners ({} pts)", - outer.len() - ); - } - - /// An open L stroke is one simple ring with no holes. - #[test] - fn open_l_is_a_simple_ring() { - let centreline = vec![pt(0.0, 0.0), pt(2.0, 0.0), pt(2.0, 2.0), pt(4.0, 2.0)]; - let (outer, holes) = single(offset_polyline_regions( - ¢reline, - 0.3, - false, - DEFAULT_MITER_LIMIT, - DEFAULT_EPS, - )); - assert!(holes.is_empty(), "open chain has no holes"); - let ring: Vec = outer.iter().map(|v| Pt2::new(v.x, v.z)).collect(); - assert!(ring.len() >= 4, "ring has at least 4 points"); - assert!( - !self_intersects2(&ring, DEFAULT_EPS), - "outer ring must be simple" - ); - assert!( - signed_area2(&ring).abs() > DEFAULT_EPS, - "outer ring must have area" - ); - } - - /// A closed square loop produces an outer ring plus exactly one interior-void hole. - #[test] - fn closed_square_has_outer_and_one_hole() { - let centreline = vec![ - pt(0.0, 0.0), - pt(4.0, 0.0), - pt(4.0, 4.0), - pt(0.0, 4.0), - pt(0.0, 0.0), - ]; - let (outer, holes) = single(offset_polyline_regions( - ¢reline, - 0.4, - true, - DEFAULT_MITER_LIMIT, - DEFAULT_EPS, - )); - assert!(outer.len() >= 4, "outer ring has at least 4 points"); - assert_eq!( - holes.len(), - 1, - "closed loop has exactly one interior-void hole" - ); - let outer2: Vec = outer.iter().map(|v| Pt2::new(v.x, v.z)).collect(); - assert!( - !self_intersects2(&outer2, DEFAULT_EPS), - "outer ring must be simple" - ); - } - - /// The offset is deterministic: the same input yields identical regions every call. - #[test] - fn offset_is_deterministic() { - let centreline = star_centreline(24, 6.0, 3.3); - let same = |a: &[Vector3], b: &[Vector3]| { - a.len() == b.len() - && a.iter().zip(b).all(|(p, q)| { - (p.x - q.x).abs() < 1e-12 - && (p.y - q.y).abs() < 1e-12 - && (p.z - q.z).abs() < 1e-12 - }) - }; - let baseline = - offset_polyline_regions(¢reline, 0.3, true, DEFAULT_MITER_LIMIT, DEFAULT_EPS); - assert!(!baseline.is_empty(), "star offset builds"); - for _ in 0..8 { - let again = - offset_polyline_regions(¢reline, 0.3, true, DEFAULT_MITER_LIMIT, DEFAULT_EPS); - assert_eq!(baseline.len(), again.len(), "region count must be stable"); - for ((o1, h1), (o2, h2)) in baseline.iter().zip(&again) { - assert!(same(o1, o2), "outer ring must be identical across calls"); - assert_eq!(h1.len(), h2.len(), "hole count must be stable"); - for (a, b) in h1.iter().zip(h2) { - assert!(same(a, b), "hole rings must be identical across calls"); - } - } - } - } - - /// A stroke as wide as its loop (1×1 loop, 1.0-wide stroke): the inner offset - /// collapses, so the region is a SOLID block (no interior void), not a failure. - #[test] - fn tight_closed_loop_builds_a_solid_region() { - let centreline = vec![ - pt(0.0, 0.0), - pt(1.0, 0.0), - pt(1.0, 1.0), - pt(0.0, 1.0), - pt(0.0, 0.0), - ]; - let (outer, holes) = single(offset_polyline_regions( - ¢reline, - 1.0, - true, - DEFAULT_MITER_LIMIT, - DEFAULT_EPS, - )); - assert!( - holes.is_empty(), - "the interior void is consumed → no hole (solid)" - ); - let outer2: Vec = outer.iter().map(|v| Pt2::new(v.x, v.z)).collect(); - assert!( - outer.len() >= 4 && !self_intersects2(&outer2, DEFAULT_EPS), - "outer ring is a simple block" - ); - assert!( - signed_area2(&outer2).abs() > DEFAULT_EPS, - "outer ring has area" - ); - } - - /// A self-crossing centreline (figure-8): the per-segment bands UNION by nonzero - /// winding so the overlapping strokes at the crossing MERGE — the crossing centre is - /// filled (not duplicated/internal-edged), and each lobe interior is a void. - #[test] - fn self_crossing_centreline_merges_at_the_crossing() { - let bowtie = vec![ - pt(0.0, 0.0), - pt(3.0, 3.0), - pt(3.0, 0.0), - pt(0.0, 3.0), - pt(0.0, 0.0), - ]; - let regions = offset_polyline_regions(&bowtie, 0.3, true, DEFAULT_MITER_LIMIT, DEFAULT_EPS); - assert!(!regions.is_empty(), "figure-8 builds"); - for (outer, _holes) in ®ions { - let ring: Vec = outer.iter().map(|v| Pt2::new(v.x, v.z)).collect(); - assert!( - ring.len() >= 3 && !self_intersects2(&ring, DEFAULT_EPS), - "each region outer ring is simple (crossing merged, no crossing edges)" - ); - } - // `p` is covered iff inside some region's outer ring and not in any of its holes. - let filled = |p: Pt2| -> bool { - regions.iter().any(|(outer, holes)| { - let o: Vec = outer.iter().map(|v| Pt2::new(v.x, v.z)).collect(); - if !point_in_ring2(p, &o) { - return false; - } - !holes.iter().any(|hole| { - let h: Vec = hole.iter().map(|v| Pt2::new(v.x, v.z)).collect(); - point_in_ring2(p, &h) - }) - }) - }; - // The crossing centre (1.5,1.5) is MERGED — the two diagonal bands overlap there. - assert!( - filled(Pt2::new(1.5, 1.5)), - "crossing centre is filled (merged)" - ); - // A point deep inside a lobe triangle is a void (the area the stroke encloses). - assert!( - !filled(Pt2::new(0.6, 1.5)), - "lobe interior is an empty void" - ); - } - - /// An acute open corner bevels (no runaway miter spike): the region stays a - /// simple ring bounded near the centreline rather than spiking far out. - #[test] - fn acute_open_corner_bevels_and_stays_bounded() { - let centreline = vec![pt(0.0, 0.0), pt(5.0, 0.0), pt(2.0, 1.0)]; - let (outer, holes) = single(offset_polyline_regions( - ¢reline, - 0.5, - false, - DEFAULT_MITER_LIMIT, - DEFAULT_EPS, - )); - assert!(holes.is_empty(), "open stroke has no holes"); - let ring: Vec = outer.iter().map(|v| Pt2::new(v.x, v.z)).collect(); - assert!( - !self_intersects2(&ring, DEFAULT_EPS), - "outer ring is simple" - ); - // A full miter at this acute apex would spike well past the centreline; the - // bevel keeps every vertex within ~width of the centreline bbox (x≤5). - assert!( - outer.iter().all(|v| v.x < 6.0), - "no runaway miter spike (bevelled)" - ); - } - - /// A doubling-back OPEN path (reflex hairpin) resolves by nonzero winding to a - /// single simple outline — no spur, no self-crossing. - #[test] - fn self_overlapping_open_path_resolves_simple() { - let centreline = vec![pt(0.0, 0.0), pt(5.0, 0.0), pt(1.0, 0.4)]; - let (outer, _holes) = single(offset_polyline_regions( - ¢reline, - 0.3, - false, - DEFAULT_MITER_LIMIT, - DEFAULT_EPS, - )); - let ring: Vec = outer.iter().map(|v| Pt2::new(v.x, v.z)).collect(); - assert!( - !self_intersects2(&ring, DEFAULT_EPS), - "resolved outer ring is simple" - ); - assert!( - signed_area2(&ring).abs() > DEFAULT_EPS, - "outer ring has area" - ); - } - - #[test] - fn simplify_drops_subthreshold_jog_but_keeps_endpoint() { - let centreline = vec![pt(0.0, 0.0), pt(1.0, 0.0), pt(1.0, 0.001), pt(3.0, 0.0)]; - let simplified = simplify_polyline(¢reline, 0.1, false); - // The 0.001 jog collapses; endpoints are preserved. - assert!(simplified.len() < centreline.len()); - assert!((simplified[0].x - 0.0).abs() < 1e-9); - let last = simplified.last().unwrap(); - assert!((last.x - 3.0).abs() < 1e-9, "true endpoint preserved"); - } - - // ── offset_ring_variable ───────────────────────────────────────────────── - - fn variable(ring: &[Vector3], distances: &[f64]) -> Result, String> { - offset_ring_variable(ring, distances, &[], DEFAULT_EPS) - } - - fn ring2(region: &OffsetRegion) -> Vec { - region.0.iter().map(|v| Pt2::new(v.x, v.z)).collect() - } - - /// Min distance from `p` to the segment a→b. - fn dist_to_segment(p: Pt2, a: Pt2, b: Pt2) -> f64 { - let abx = b.x - a.x; - let abz = b.z - a.z; - let len2 = abx * abx + abz * abz; - let t = if len2 > 0.0 { - (((p.x - a.x) * abx + (p.z - a.z) * abz) / len2).clamp(0.0, 1.0) - } else { - 0.0 - }; - (p.x - (a.x + t * abx)).hypot(p.z - (a.z + t * abz)) - } - - fn has_vertex(ring: &[Vector3], x: f64, z: f64) -> bool { - ring.iter() - .any(|v| (v.x - x).abs() < 1e-9 && (v.z - z).abs() < 1e-9) - } - - /// A uniform variable inset of a square equals the plain shrunken square. - #[test] - fn variable_inset_uniform_matches_square() { - let ring = vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 20.0), pt(0.0, 20.0)]; - let regions = variable(&ring, &[2.0; 4]).expect("valid arguments"); - let (outer, holes) = single(regions); - assert!(holes.is_empty(), "an inward inset cannot create holes"); - assert_eq!(outer.len(), 4, "square inset stays a quad"); - for (x, z) in [(2.0, 2.0), (18.0, 2.0), (18.0, 18.0), (2.0, 18.0)] { - assert!(has_vertex(&outer, x, z), "missing corner ({}, {})", x, z); - } - } - - /// Distinct clearances on a rectangle land each edge at its own distance. - #[test] - fn variable_inset_distinct_edge_clearances() { - // 20 (x) × 30 (z); edge 0 has clearance 6, edges 1/3 have - // clearance 1.5, and edge 2 has clearance 3. - let ring = vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 30.0), pt(0.0, 30.0)]; - let regions = variable(&ring, &[6.0, 1.5, 3.0, 1.5]).expect("valid arguments"); - let (outer, _) = single(regions); - assert_eq!(outer.len(), 4); - for (x, z) in [(1.5, 6.0), (18.5, 6.0), (18.5, 27.0), (1.5, 27.0)] { - assert!(has_vertex(&outer, x, z), "missing corner ({}, {})", x, z); - } - let area = signed_area2(&ring2(&(outer.clone(), Vec::new()))).abs(); - assert!((area - 17.0 * 21.0).abs() < 1e-9, "area = {}", area); - } - - /// A zero-distance edge stays exactly in place (zero-clearance boundary). - #[test] - fn variable_inset_zero_distance_edge_stays() { - let ring = vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 30.0), pt(0.0, 30.0)]; - let regions = variable(&ring, &[0.0, 1.5, 3.0, 1.5]).expect("valid arguments"); - let (outer, _) = single(regions); - // Edge 0 (z = 0) is untouched: its inset corners sit on it. - assert!(has_vertex(&outer, 1.5, 0.0)); - assert!(has_vertex(&outer, 18.5, 0.0)); - } - - /// All-zero distances are a legitimate no-op inset returning the input ring. - #[test] - fn variable_inset_all_zero_returns_input_ring() { - let ring = vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 30.0), pt(0.0, 30.0)]; - let (outer, holes) = single(variable(&ring, &[0.0; 4]).expect("valid arguments")); - assert!(holes.is_empty()); - assert_eq!(outer.len(), 4); - for (x, z) in [(0.0, 0.0), (20.0, 0.0), (20.0, 30.0), (0.0, 30.0)] { - assert!(has_vertex(&outer, x, z)); - } - } - - #[test] - fn variable_inset_distance_count_mismatch_errors() { - let ring = vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 30.0), pt(0.0, 30.0)]; - let Err(error) = variable(&ring, &[6.0, 1.5, 3.0]) else { - panic!("count mismatch must throw"); - }; - assert!( - error.contains("4 edges"), - "message names the edge count: {}", - error - ); - assert!( - error.contains("3 distances"), - "message names the given count: {}", - error - ); - } - - #[test] - fn variable_inset_negative_distance_errors() { - let ring = vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 30.0), pt(0.0, 30.0)]; - let Err(error) = variable(&ring, &[6.0, -1.5, 3.0, 1.5]) else { - panic!("negative distance must throw"); - }; - assert!( - error.contains("distances[1]"), - "message names the index: {}", - error - ); - } - - /// A ring given with an explicit closing duplicate takes the same N distances - /// and produces the identical canonical result. - #[test] - fn variable_inset_accepts_closing_duplicate() { - let open = vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 30.0), pt(0.0, 30.0)]; - let mut closed = open.clone(); - closed.push(open[0]); - let distances = [6.0, 1.5, 3.0, 1.5]; - let a = single(variable(&open, &distances).expect("open form")); - let b = single(variable(&closed, &distances).expect("closed form")); - assert_eq!(a.0.len(), b.0.len()); - for (p, q) in a.0.iter().zip(&b.0) { - assert!((p.x - q.x).abs() < 1e-12 && (p.z - q.z).abs() < 1e-12); - } - } - - /// A CW ring with reversal-remapped distances matches the CCW result. - #[test] - fn variable_inset_cw_input_matches_ccw() { - let ccw = vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 30.0), pt(0.0, 30.0)]; - let ccw_d = [6.0, 1.5, 3.0, 1.5]; - // Reversed point order: edge k of the CW ring is edge (n-2-k) mod n of - // the CCW ring traversed backward. - let cw: Vec = ccw.iter().rev().copied().collect(); - let n = ccw.len(); - let cw_d: Vec = (0..n).map(|k| ccw_d[(2 * n - 2 - k) % n]).collect(); - let a = single(variable(&ccw, &ccw_d).expect("ccw")); - let b = single(variable(&cw, &cw_d).expect("cw")); - assert_eq!(a.0.len(), b.0.len()); - for (p, q) in a.0.iter().zip(&b.0) { - assert!((p.x - q.x).abs() < 1e-12 && (p.z - q.z).abs() < 1e-12); - } - } - - /// Splitting one boundary run into two collinear edges with different distances - /// transitions between the two inset lines through the deeper edge's - /// clearance arc around the split point — NOT a perpendicular step, which - /// would claim points within the deep distance of the deep segment's - /// endpoint. - #[test] - fn variable_inset_collinear_split_edge_steps() { - let ring = vec![ - pt(0.0, 0.0), - pt(10.0, 0.0), // split point on the z=0 boundary - pt(20.0, 0.0), - pt(20.0, 30.0), - pt(0.0, 30.0), - ]; - let regions = variable(&ring, &[2.0, 5.0, 1.5, 3.0, 1.5]).expect("valid arguments"); - let (outer, _) = single(regions); - let r2 = ring2(&(outer.clone(), Vec::new())); - assert!( - !self_intersects2(&r2, DEFAULT_EPS), - "stepped envelope is simple" - ); - // Both inset lines exist… - assert!( - outer.iter().any(|v| (v.z - 2.0).abs() < 1e-9 && v.x < 5.5), - "shallow inset line at z=2 (left of the deep edge's clearance arc)" - ); - assert!( - outer - .iter() - .any(|v| (v.z - 5.0).abs() < 1e-9 && v.x > 10.0 - 1e-9), - "deep inset line at z=5" - ); - // …joined through the arc anchored at (10, 5) above the split point. - assert!(has_vertex(&outer, 10.0, 5.0), "arc anchor above the split"); - // The would-be step corner (10, 2) is within 5 m of the deep boundary run - // segment, so it must NOT be claimed. - let deep_a = Pt2::new(10.0, 0.0); - let deep_b = Pt2::new(20.0, 0.0); - for v in &outer { - assert!( - dist_to_segment(Pt2::new(v.x, v.z), deep_a, deep_b) >= 5.0 - 1.0e-6, - "vertex ({}, {}) violates the deep edge clearance", - v.x, - v.z - ); - } - } - - /// Every output vertex keeps at least its edge's distance to that edge — - /// the per-edge distance condition this inset must guarantee. Cases - /// include a sharp reflex notch (where a miter/bevel join would spike or - /// under-clear) and a deep notch whose far flank constrains points across - /// exterior space. - #[test] - fn variable_inset_clearance_respected() { - let cases: Vec<(Vec, Vec)> = vec![ - ( - vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 30.0), pt(0.0, 30.0)], - vec![6.0, 1.5, 3.0, 1.5], - ), - ( - // Reflex notch in the top edge (sharp spike pointing into the ring). - vec![ - pt(0.0, 0.0), - pt(40.0, 0.0), - pt(40.0, 30.0), - pt(22.0, 30.0), - pt(20.0, 12.0), // notch tip — sharp reflex on both flanks - pt(18.0, 30.0), - pt(0.0, 30.0), - ], - vec![3.0, 2.0, 1.0, 2.5, 2.5, 1.0, 4.0], - ), - ]; - for (ring, distances) in cases { - let raw: Vec = ring.iter().map(|v| Pt2::new(v.x, v.z)).collect(); - let regions = offset_ring_variable(&ring, &distances, &[], DEFAULT_EPS).expect("valid"); - assert!(!regions.is_empty(), "inset builds"); - for region in ®ions { - for v in ®ion.0 { - let p = Pt2::new(v.x, v.z); - for i in 0..raw.len() { - let clearance = dist_to_segment(p, raw[i], raw[(i + 1) % raw.len()]); - assert!( - clearance >= distances[i] - 1.0e-6, - "vertex ({}, {}) is {} from edge {} (required {})", - p.x, - p.z, - clearance, - i, - distances[i] - ); - } - } - } - } - } - - /// Clearance applies to edge SEGMENTS across exterior space: a narrow - /// notch's far boundary edge constrains the inset on the near side. - #[test] - fn variable_inset_respects_distant_edges_across_a_notch() { - // A 2-wide exterior slot (x 10..12) cut into the top of a 30×20 ring. - let ring = vec![ - pt(0.0, 0.0), - pt(30.0, 0.0), - pt(30.0, 20.0), - pt(12.0, 20.0), - pt(12.0, 8.0), - pt(10.0, 8.0), // slot bottom - pt(10.0, 20.0), - pt(0.0, 20.0), - ]; - // Edge 5 = slot's left flank (10,8)→(10,20) faces the right lobe across - // the 2-wide slot with a 5 m clearance: it must carve into x ∈ (12, 15). - let distances = vec![1.0, 1.0, 1.0, 1.0, 1.0, 5.0, 1.0, 1.0]; - let regions = variable(&ring, &distances).expect("valid arguments"); - assert!(!regions.is_empty()); - let flank_a = Pt2::new(10.0, 8.0); - let flank_b = Pt2::new(10.0, 20.0); - for region in ®ions { - for v in ®ion.0 { - assert!( - dist_to_segment(Pt2::new(v.x, v.z), flank_a, flank_b) >= 5.0 - 1.0e-6, - "vertex ({}, {}) is inside the slot flank's clearance", - v.x, - v.z - ); - } - } - // The point (13, 14) is only 3 m from the slot's left flank — across - // exterior space — and must be excluded from every region. - let probe = Pt2::new(13.0, 14.0); - for region in ®ions { - let r2 = ring2(region); - assert!( - !point_in_ring2(probe, &r2), - "(13, 14) violates the slot flank clearance but was claimed" - ); - } - } - - /// Distances that consume the whole ring collapse to an empty result. - #[test] - fn variable_inset_collapse_returns_empty() { - let ring = vec![pt(0.0, 0.0), pt(4.0, 0.0), pt(4.0, 4.0), pt(0.0, 4.0)]; - let regions = variable(&ring, &[3.0; 4]).expect("valid arguments"); - assert!( - regions.is_empty(), - "fully consumed inset is empty, not an error" - ); - } - - /// A waisted (dumbbell) parcel whose waist is narrower than the side insets - /// splits into TWO disjoint envelope regions — the case the old - /// largest-hole stroke trick could not represent. - #[test] - fn variable_inset_dumbbell_splits_into_two_regions() { - let dumbbell = vec![ - pt(0.0, 0.0), - pt(20.0, 0.0), - pt(20.0, 14.0), // waist starts: narrow corridor z 14..26 at x 8..12 - pt(12.0, 14.0), - pt(12.0, 26.0), - pt(20.0, 26.0), - pt(20.0, 40.0), - pt(0.0, 40.0), - pt(0.0, 26.0), - pt(8.0, 26.0), - pt(8.0, 14.0), - pt(0.0, 14.0), - ]; - // The corridor is 4 wide (x 8..12); a uniform 3.0 inset closes it but - // leaves both 20×14 lobes in the result. - let regions = variable(&dumbbell, &[3.0; 12]).expect("valid arguments"); - assert_eq!(regions.len(), 2, "pinched waist splits the envelope in two"); - let original: Vec = dumbbell.iter().map(|v| Pt2::new(v.x, v.z)).collect(); - for region in ®ions { - let r2 = ring2(region); - assert!( - r2.len() >= 3 && !self_intersects2(&r2, DEFAULT_EPS), - "each lobe is simple" - ); - assert!(signed_area2(&r2).abs() > 1.0, "each lobe has real area"); - assert!( - ring_inside2(&r2, &original, DEFAULT_EPS), - "lobes stay inside the parcel" - ); - } - } - - /// Degenerate rings (too few points, zero area, self-intersecting) are - /// empty results, not errors. - #[test] - fn variable_inset_degenerate_ring_is_empty() { - let two = vec![pt(0.0, 0.0), pt(10.0, 0.0)]; - assert!(variable(&two, &[1.0, 1.0]) - .expect("valid arguments") - .is_empty()); - let collinear = vec![pt(0.0, 0.0), pt(10.0, 0.0), pt(20.0, 0.0)]; - assert!(variable(&collinear, &[1.0; 3]) - .expect("valid arguments") - .is_empty()); - let bowtie = vec![pt(0.0, 0.0), pt(10.0, 10.0), pt(10.0, 0.0), pt(0.0, 10.0)]; - assert!(variable(&bowtie, &[1.0; 4]) - .expect("valid arguments") - .is_empty()); - } - - /// The variable inset is deterministic across repeated calls. - #[test] - fn variable_inset_deterministic() { - let ring = vec![ - pt(0.0, 0.0), - pt(40.0, 0.0), - pt(40.0, 30.0), - pt(22.0, 30.0), - pt(20.0, 12.0), - pt(18.0, 30.0), - pt(0.0, 30.0), - ]; - let distances = [3.0, 2.0, 1.0, 2.5, 2.5, 1.0, 4.0]; - let same = |a: &[Vector3], b: &[Vector3]| { - a.len() == b.len() - && a.iter().zip(b).all(|(p, q)| { - (p.x - q.x).abs() < 1e-12 - && (p.y - q.y).abs() < 1e-12 - && (p.z - q.z).abs() < 1e-12 - }) - }; - let baseline = variable(&ring, &distances).expect("valid"); - assert!(!baseline.is_empty()); - for _ in 0..8 { - let again = variable(&ring, &distances).expect("valid"); - assert_eq!(baseline.len(), again.len()); - for ((o1, h1), (o2, h2)) in baseline.iter().zip(&again) { - assert!(same(o1, o2), "outer ring identical across calls"); - assert_eq!(h1.len(), h2.len()); - } - } - } - - /// A centred hole with its own clearance: the outer ring shrinks inward, the - /// hole GROWS outward, and the retained region is the ring between them. - #[test] - fn variable_inset_grows_holes_by_their_clearance() { - let ring = vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 20.0), pt(0.0, 20.0)]; - let hole = OffsetHole { - ring: vec![pt(8.0, 8.0), pt(12.0, 8.0), pt(12.0, 12.0), pt(8.0, 12.0)], - distances: vec![1.0; 4], - }; - let regions = offset_ring_variable(&ring, &[1.0; 4], &[hole], DEFAULT_EPS).expect("valid"); - let (outer, holes) = single(regions); - for (x, z) in [(1.0, 1.0), (19.0, 1.0), (19.0, 19.0), (1.0, 19.0)] { - assert!( - has_vertex(&outer, x, z), - "outer corner ({}, {}) inset by 1", - x, - z - ); - } - assert_eq!(holes.len(), 1, "the hole survives as one interior void"); - let grown: Vec = holes[0].iter().map(|v| Pt2::new(v.x, v.z)).collect(); - let hole_edges = [ - (Pt2::new(8.0, 8.0), Pt2::new(12.0, 8.0)), - (Pt2::new(12.0, 8.0), Pt2::new(12.0, 12.0)), - (Pt2::new(12.0, 12.0), Pt2::new(8.0, 12.0)), - (Pt2::new(8.0, 12.0), Pt2::new(8.0, 8.0)), - ]; - for p in &grown { - assert!( - p.x >= 7.0 - 1e-6 && p.x <= 13.0 + 1e-6 && p.z >= 7.0 - 1e-6 && p.z <= 13.0 + 1e-6 - ); - let nearest = hole_edges - .iter() - .map(|(a, b)| dist_to_segment(*p, *a, *b)) - .fold(f64::INFINITY, f64::min); - assert!( - nearest >= 1.0 - 1e-6, - "hole vertex ({}, {}) is only {} from the hole", - p.x, - p.z, - nearest - ); - } - // The hole's clearance zone reaches (7, 10): inside the grown hole, outside the retained region. - assert!( - point_in_ring2(Pt2::new(7.2, 10.0), &grown), - "clearance around the hole is void" - ); - } - - /// A hole whose clearance reaches both outer boundary edges splits the region into two lobes. - #[test] - fn variable_inset_hole_clearance_splits_region() { - let ring = vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 10.0), pt(0.0, 10.0)]; - let hole = OffsetHole { - ring: vec![pt(9.0, 2.0), pt(11.0, 2.0), pt(11.0, 8.0), pt(9.0, 8.0)], - distances: vec![3.0; 4], - }; - let regions = offset_ring_variable(&ring, &[0.0; 4], &[hole], DEFAULT_EPS).expect("valid"); - assert_eq!(regions.len(), 2, "the grown hole cuts the ring in two"); - let mut left = 0; - let mut right = 0; - for (outer, holes) in ®ions { - assert!(holes.is_empty(), "each lobe is solid"); - if outer.iter().all(|v| v.x <= 9.0 + 1e-6) { - left += 1; - } - if outer.iter().all(|v| v.x >= 11.0 - 1e-6) { - right += 1; - } - } - assert_eq!((left, right), (1, 1), "one lobe each side of the hole"); - } - - /// A degenerate hole is a caller bug, not "no region remains". - #[test] - fn variable_inset_rejects_degenerate_hole() { - let ring = vec![pt(0.0, 0.0), pt(20.0, 0.0), pt(20.0, 20.0), pt(0.0, 20.0)]; - let hole = OffsetHole { - ring: vec![pt(5.0, 5.0), pt(6.0, 5.0), pt(7.0, 5.0)], - distances: vec![1.0; 3], - }; - assert!(offset_ring_variable(&ring, &[1.0; 4], &[hole], DEFAULT_EPS).is_err()); - } -} diff --git a/main/opengeometry/src/geometry/poly2d.rs b/main/opengeometry/src/geometry/poly2d.rs deleted file mode 100644 index a2a0e86..0000000 --- a/main/opengeometry/src/geometry/poly2d.rs +++ /dev/null @@ -1,264 +0,0 @@ -//! Generic 2D predicates, types and ring utilities (XZ plane). - -use openmaths::Vector3; - -/// Coincidence / parallel tolerance for the 2D predicates. -pub const DEFAULT_EPS: f64 = 1.0e-6; - -/// Default join miter limit (SVG stroke-miterlimit semantics). -pub const DEFAULT_MITER_LIMIT: f64 = 4.0; - -/// A point in the working XZ plane (Y is carried separately, for horizontal geometry). -#[derive(Clone, Copy, Debug)] -pub struct Pt2 { - pub x: f64, - pub z: f64, -} - -impl Pt2 { - pub fn new(x: f64, z: f64) -> Self { - Self { x, z } - } -} - -#[inline] -pub fn cross2(a: Pt2, b: Pt2) -> f64 { - a.x * b.z - a.z * b.x -} - -#[inline] -pub fn dot2(a: Pt2, b: Pt2) -> f64 { - a.x * b.x + a.z * b.z -} - -/// Intersection of line `a + t·da` with line `b + s·db`; `None` when parallel. -pub fn intersect2(a: Pt2, da: Pt2, b: Pt2, db: Pt2, eps: f64) -> Option { - let denom = cross2(da, db); - if denom.abs() < eps { - return None; - } - let t = ((b.x - a.x) * db.z - (b.z - a.z) * db.x) / denom; - Some(Pt2::new(a.x + t * da.x, a.z + t * da.z)) -} - -/// Signed area of a closed ring (CCW positive in XZ). -pub fn signed_area2(ring: &[Pt2]) -> f64 { - let mut area = 0.0; - let count = ring.len(); - for i in 0..count { - let c = ring[i]; - let n = ring[(i + 1) % count]; - area += c.x * n.z - n.x * c.z; - } - area / 2.0 -} - -/// Proper (strict-interior) crossing of two XZ segments. -pub fn segments_cross2(a1: Pt2, a2: Pt2, b1: Pt2, b2: Pt2, eps: f64) -> bool { - let d1 = Pt2::new(a2.x - a1.x, a2.z - a1.z); - let d2 = Pt2::new(b2.x - b1.x, b2.z - b1.z); - let denom = cross2(d1, d2); - if denom.abs() < eps { - return false; - } - let diff = Pt2::new(b1.x - a1.x, b1.z - a1.z); - let t = cross2(diff, d2) / denom; - let s = cross2(diff, d1) / denom; - t > eps && t < 1.0 - eps && s > eps && s < 1.0 - eps -} - -/// True if any pair of non-adjacent edges of `ring` cross. -pub fn self_intersects2(ring: &[Pt2], eps: f64) -> bool { - let m = ring.len(); - for i in 0..m { - for j in (i + 1)..m { - if j == i + 1 || (i == 0 && j == m - 1) { - continue; // adjacent edges share a vertex - } - if segments_cross2(ring[i], ring[(i + 1) % m], ring[j], ring[(j + 1) % m], eps) { - return true; - } - } - } - false -} - -#[inline] -pub fn xz(v: &Vector3) -> Pt2 { - Pt2::new(v.x, v.z) -} - -/// Signed area of a Vector3 ring in the XZ plane (CCW positive). -pub fn signed_area_xz(ring: &[Vector3]) -> f64 { - let pts: Vec = ring.iter().map(xz).collect(); - signed_area2(&pts) -} - -/// Target winding for a ring fed to `OGPolygon` (CW outer / CCW hole). -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum Winding { - Cw, - Ccw, -} - -/// Return a clone of `ring` wound the way `OGPolygon` expects (negative XZ area = CW). -pub fn normalize_winding(ring: &[Vector3], want: Winding) -> Vec { - let is_cw = signed_area_xz(ring) < 0.0; - if is_cw == (want == Winding::Cw) { - ring.to_vec() - } else { - ring.iter().rev().copied().collect() - } -} - -/// Rotate a ring to start at its lexicographically smallest (x, then z) vertex, -/// so the result is deterministic regardless of the source ring's start vertex. -pub fn canonical_ring(ring: &[Vector3]) -> Vec { - if ring.len() < 2 { - return ring.to_vec(); - } - let mut min_index = 0; - for i in 1..ring.len() { - let dx = ring[i].x - ring[min_index].x; - if dx < -1.0e-9 || (dx.abs() <= 1.0e-9 && ring[i].z < ring[min_index].z - 1.0e-9) { - min_index = i; - } - } - let mut out = Vec::with_capacity(ring.len()); - out.extend_from_slice(&ring[min_index..]); - out.extend_from_slice(&ring[..min_index]); - out -} - -// ── Analytic offset-with-joins outline (the deterministic 2D primitive) ────── -// Offsets a centreline by ±half-width with miter/flat-bevel joins, resolves any -// self-crossing (reflex hairpin / closed spike) by NONZERO WINDING — the pure 2D -// replacement for a boolean union — and returns one region (outer ring + inner-void -// holes). No CSG, no boolean, fully deterministic. - -/// Drop consecutive near-duplicate points and a coincident closing point. -pub fn dedupe_ring(ring: &[Pt2], eps: f64) -> Vec { - let mut out: Vec = Vec::with_capacity(ring.len()); - for p in ring { - match out.last() { - Some(last) if (last.x - p.x).hypot(last.z - p.z) <= eps => {} - _ => out.push(*p), - } - } - if out.len() > 1 { - let first = out[0]; - let last = out[out.len() - 1]; - if (first.x - last.x).hypot(first.z - last.z) <= eps { - out.pop(); - } - } - out -} - -/// Even-odd point-in-polygon test in XZ. -pub fn point_in_ring2(p: Pt2, ring: &[Pt2]) -> bool { - let mut inside = false; - let n = ring.len(); - if n == 0 { - return false; - } - let mut j = n - 1; - for i in 0..n { - let a = ring[i]; - let b = ring[j]; - if (a.z > p.z) != (b.z > p.z) && p.x < (b.x - a.x) * (p.z - a.z) / (b.z - a.z) + a.x { - inside = !inside; - } - j = i; - } - inside -} - -/// Every vertex of `inner` lies inside `outer` and no edges cross. -pub fn ring_inside2(inner: &[Pt2], outer: &[Pt2], eps: f64) -> bool { - for p in inner { - if !point_in_ring2(*p, outer) { - return false; - } - } - for i in 0..inner.len() { - for j in 0..outer.len() { - if segments_cross2( - inner[i], - inner[(i + 1) % inner.len()], - outer[j], - outer[(j + 1) % outer.len()], - eps, - ) { - return false; - } - } - } - true -} - -/// Proper interior crossing of two XZ segments; returns the point + both params. -pub fn segment_cross_t2(a1: Pt2, a2: Pt2, b1: Pt2, b2: Pt2, eps: f64) -> Option<(Pt2, f64, f64)> { - let r = Pt2::new(a2.x - a1.x, a2.z - a1.z); - let s = Pt2::new(b2.x - b1.x, b2.z - b1.z); - let denom = cross2(r, s); - if denom.abs() < eps { - return None; // parallel / collinear - } - let qp = Pt2::new(b1.x - a1.x, b1.z - a1.z); - let ta = cross2(qp, s) / denom; - let tb = cross2(qp, r) / denom; - if ta > eps && ta < 1.0 - eps && tb > eps && tb < 1.0 - eps { - Some((Pt2::new(a1.x + ta * r.x, a1.z + ta * r.z), ta, tb)) - } else { - None - } -} - -/// Winding number of `p` about closed `ring` (nonzero rule). -pub fn winding_number2(p: Pt2, ring: &[Pt2]) -> i32 { - let mut wn = 0; - let count = ring.len(); - for i in 0..count { - let a = ring[i]; - let b = ring[(i + 1) % count]; - let on_left = cross2( - Pt2::new(b.x - a.x, b.z - a.z), - Pt2::new(p.x - a.x, p.z - a.z), - ); - if a.z <= p.z { - if b.z > p.z && on_left > 0.0 { - wn += 1; - } - } else if b.z <= p.z && on_left < 0.0 { - wn -= 1; - } - } - wn -} - -pub type Edge2 = (Pt2, Pt2); - -/// Quantize to a ~1e-6 grid so coincident endpoints share a key (deterministic, lookup-only). -pub fn qkey(p: Pt2) -> (i64, i64) { - ((p.x * 1.0e6).round() as i64, (p.z * 1.0e6).round() as i64) -} - -/// Param `t` in (0,1) where `p` lies on the interior of edge `a→b` (collinear within eps). -pub fn point_on_edge_t(a: Pt2, b: Pt2, p: Pt2, eps: f64) -> Option { - let rx = b.x - a.x; - let rz = b.z - a.z; - let len2 = rx * rx + rz * rz; - if len2 < eps * eps { - return None; - } - let t = ((p.x - a.x) * rx + (p.z - a.z) * rz) / len2; - if t <= eps || t >= 1.0 - eps { - return None; - } - if (a.x + t * rx - p.x).hypot(a.z + t * rz - p.z) < eps { - Some(t) - } else { - None - } -} diff --git a/main/opengeometry/src/geometry/triangle.rs b/main/opengeometry/src/geometry/triangle.rs deleted file mode 100644 index b82cc30..0000000 --- a/main/opengeometry/src/geometry/triangle.rs +++ /dev/null @@ -1,41 +0,0 @@ -use openmaths::Vector3; - -#[derive(Clone)] -pub struct Triangle { - pub a: Vector3, - pub b: Vector3, - pub c: Vector3, -} - -impl Triangle { - pub fn new() -> Triangle { - Triangle { - a: Vector3::new(0.0, 0.0, 0.0), - b: Vector3::new(0.0, 0.0, 0.0), - c: Vector3::new(0.0, 0.0, 0.0), - } - } - - pub fn set_vertices(&mut self, a: Vector3, b: Vector3, c: Vector3) { - self.a = a; - self.b = b; - self.c = c; - } - - pub fn is_point_in_triangle(&self, p: Vector3) -> bool { - let ab = self.b.clone().subtract(&self.a); - let bc = self.c.clone().subtract(&self.b); - let ca = self.a.clone().subtract(&self.c); - - let ap = p.clone().subtract(&self.a); - let bp = p.clone().subtract(&self.b); - let cp = p.clone().subtract(&self.c); - - let cross_abp = ab.clone().cross(&ap); - let cross_bcp = bc.clone().cross(&bp); - let cross_cap = ca.clone().cross(&cp); - - (cross_abp.y > 0.0 && cross_bcp.y > 0.0 && cross_cap.y > 0.0) - || (cross_abp.y < 0.0 && cross_bcp.y < 0.0 && cross_cap.y < 0.0) - } -} diff --git a/main/opengeometry/src/intersection/face_intersection/bounded.rs b/main/opengeometry/src/intersection/face_intersection/bounded.rs new file mode 100644 index 0000000..0ddef3e --- /dev/null +++ b/main/opengeometry/src/intersection/face_intersection/bounded.rs @@ -0,0 +1,103 @@ +use super::trim::{clip_coordinate, periodic_value}; +use crate::brep::{ + Accuracy, CurveGeometry, Face, GeometryError, GeometryStore, SsiBudget, Surface, + SurfaceGeometry, +}; +use crate::intersection::ssi::intersect_surfaces; +use crate::intersection::ssi_result::{SsiCurve, SsiResult}; +use crate::intersection::universal_ssi::intersect_patches; +use crate::math::{add, Interval}; + +fn bound_plane_cylinder_generator( + geometry: &GeometryStore, + branch: &SsiCurve, + faces: [&Face; 2], + accuracy: Accuracy, +) -> Result, GeometryError> { + let CurveGeometry::Line { origin, direction } = geometry.curves[branch.curve as usize] else { + return Err(GeometryError::CoverageGap { + families: ["plane/cylinder trim".into(), "nonlinear generator".into()], + }); + }; + let mut range = [-f64::MAX.sqrt(), f64::MAX.sqrt()]; + for side in 0..2 { + let surface = geometry.surface(side as u32)?; + let start = surface.project(origin, None)?; + let next = surface.project(add(origin, direction), Some(start))?; + let periods = surface.charts()[0].periods; + for axis in 0..2 { + let bounds = faces[side].trim.uv_bounds[axis]; + let initial = periodic_value(start[axis], bounds, periods[axis]); + let later = periodic_value(next[axis], bounds, periods[axis]); + let mut rate = later - initial; + if matches!(surface, SurfaceGeometry::Cylinder { .. }) + && axis == 0 + && rate.abs() > accuracy.intersection + { + return Err(GeometryError::CoverageGap { + families: ["plane/cylinder trim".into(), "nonaxial generator".into()], + }); + } + if matches!(surface, SurfaceGeometry::Cylinder { .. }) && axis == 0 { + rate = 0.0; + } + if !clip_coordinate(initial, rate, bounds, &mut range) { + return Ok(None); + } + } + } + if !range[0].is_finite() || !range[1].is_finite() || range[1] - range[0] <= accuracy.geometric { + return Ok(None); + } + Ok(Some(Interval::new(range[0], range[1])?)) +} + +pub(super) fn intersect_bounded_surfaces( + geometry: &mut GeometryStore, + faces: [&Face; 2], + accuracy: Accuracy, +) -> Result { + let planar_pair = geometry + .surfaces + .iter() + .take(2) + .all(|surface| matches!(surface, SurfaceGeometry::Plane { .. })); + let plane_cylinder_pair = geometry.surfaces.len() >= 2 + && matches!( + (&geometry.surfaces[0], &geometry.surfaces[1]), + ( + SurfaceGeometry::Plane { .. }, + SurfaceGeometry::Cylinder { .. } + ) | ( + SurfaceGeometry::Cylinder { .. }, + SurfaceGeometry::Plane { .. } + ) + ); + match intersect_surfaces(geometry, 0, 1, accuracy) { + Ok(mut result) if plane_cylinder_pair => { + let mut bounded = Vec::new(); + for mut branch in result.curves { + if branch.domain.is_none() { + branch.domain = + bound_plane_cylinder_generator(geometry, &branch, faces, accuracy)?; + } + if branch.domain.is_some() { + bounded.push(branch); + } + } + result.curves = bounded; + Ok(result) + } + Ok(result) if planar_pair || result.curves.iter().all(|curve| curve.domain.is_some()) => { + Ok(result) + } + Ok(_) | Err(GeometryError::CoverageGap { .. }) => intersect_patches( + geometry, + [0, 1], + [faces[0].trim.uv_bounds, faces[1].trim.uv_bounds], + accuracy, + SsiBudget::default(), + ), + Err(error) => Err(error), + } +} diff --git a/main/opengeometry/src/intersection/face_intersection/clip.rs b/main/opengeometry/src/intersection/face_intersection/clip.rs new file mode 100644 index 0000000..ebd3f59 --- /dev/null +++ b/main/opengeometry/src/intersection/face_intersection/clip.rs @@ -0,0 +1,232 @@ +use super::face_view::FaceView; +use super::trim::uv_in_bounds; +use crate::brep::{ + Accuracy, CurveGeometry, Face, GeometryError, GeometryStore, IntersectionSide, PcurveGeometry, + Surface, UVBox, +}; +use crate::intersection::ssi_result::SsiCurve; +use crate::math::Interval; +use crate::query::face_contains_uv; + +struct BranchMembership<'a> { + geometry: &'a GeometryStore, + branch: &'a SsiCurve, + faces: [(FaceView<'a>, &'a Face); 2], + boxes_only: bool, + tolerance: f64, +} + +pub(super) fn clip_face_branch( + geometry: &GeometryStore, + branch: &SsiCurve, + faces: [(FaceView<'_>, &Face); 2], + boxes_only: bool, + bounds: [UVBox; 2], + accuracy: Accuracy, +) -> Result, GeometryError> { + let domain = branch.domain.ok_or_else(|| GeometryError::CoverageGap { + families: [ + "bounded face trim".into(), + "unbounded intersection curve".into(), + ], + })?; + let mut anchors = match geometry.curves[branch.curve as usize] { + CurveGeometry::Intersection { definition } => geometry.intersections[definition as usize] + .anchors + .iter() + .map(|anchor| anchor.parameter) + .collect::>(), + _ => (0..=256) + .map(|index| domain.lo + domain.width() * index as f64 / 256.0) + .collect(), + }; + anchors.retain(|parameter| domain.contains(*parameter)); + anchors.push(domain.lo); + anchors.push(domain.hi); + anchors.sort_by(f64::total_cmp); + anchors.dedup_by(|left, right| (*left - *right).abs() <= f64::EPSILON * 16.0); + + let membership = BranchMembership { + geometry, + branch, + faces, + boxes_only, + tolerance: accuracy.intersection, + }; + let mut intervals: Vec = Vec::new(); + for (segment, pair) in anchors.windows(2).enumerate() { + let subdivisions = 4; + for part in 0..subdivisions { + let lo = pair[0] + (pair[1] - pair[0]) * part as f64 / subdivisions as f64; + let hi = pair[0] + (pair[1] - pair[0]) * (part + 1) as f64 / subdivisions as f64; + let Some((start, end)) = clipped_span(&membership, bounds, segment, lo, hi)? else { + continue; + }; + if end - start <= accuracy.geometric { + continue; + } + if let Some(previous) = intervals.last_mut() { + if start - previous.hi <= accuracy.intersection.max(2.0 * accuracy.geometric) { + *previous = Interval::new(previous.lo, end.max(previous.hi))?; + continue; + } + } + intervals.push(Interval::new(start, end)?); + } + } + Ok(intervals) +} + +fn clipped_span( + membership: &BranchMembership, + bounds: [UVBox; 2], + segment: usize, + lo: f64, + hi: f64, +) -> Result, GeometryError> { + let BranchMembership { + geometry, + branch, + faces, + boxes_only, + tolerance, + } = *membership; + let middle = 0.5 * (lo + hi); + let lo_inside = branch_inside_faces(geometry, branch, faces, boxes_only, tolerance, lo)?; + let middle_inside = + branch_inside_faces(geometry, branch, faces, boxes_only, tolerance, middle)?; + let hi_inside = branch_inside_faces(geometry, branch, faces, boxes_only, tolerance, hi)?; + if !lo_inside && !middle_inside && !hi_inside { + if matches!( + geometry.curves[branch.curve as usize], + CurveGeometry::Intersection { .. } + ) && tube_may_enter_boxes(geometry, branch, segment, bounds)? + { + return Err(GeometryError::UnresolvedIntersection( + "bounded SSI branch may cross a trim box between certified anchors".into(), + )); + } + return Ok(None); + } + let start = if lo_inside { + lo + } else if middle_inside { + transition_parameter(geometry, branch, faces, boxes_only, lo, middle, tolerance)? + } else { + transition_parameter(geometry, branch, faces, boxes_only, middle, hi, tolerance)? + }; + let end = if hi_inside { + hi + } else if middle_inside { + transition_parameter(geometry, branch, faces, boxes_only, hi, middle, tolerance)? + } else { + transition_parameter(geometry, branch, faces, boxes_only, middle, lo, tolerance)? + }; + let (start, end) = if start <= end { + (start, end) + } else { + (end, start) + }; + Ok(Some((start, end))) +} + +fn branch_inside_faces( + geometry: &GeometryStore, + branch: &SsiCurve, + faces: [(FaceView<'_>, &Face); 2], + boxes_only: bool, + tolerance: f64, + parameter: f64, +) -> Result { + for side in 0..2 { + let uv = geometry.pcurve_at(branch.pcurves[side], parameter)?; + let inside = if boxes_only { + uv_in_bounds( + geometry, + side as u32, + faces[side].1.trim.uv_bounds, + uv, + tolerance, + )? + } else { + face_contains_uv(faces[side].0.brep, faces[side].1, uv)? == Some(true) + }; + if !inside { + return Ok(false); + } + } + Ok(true) +} + +fn tube_may_enter_boxes( + geometry: &GeometryStore, + branch: &SsiCurve, + segment: usize, + bounds: [UVBox; 2], +) -> Result { + let CurveGeometry::Intersection { definition } = geometry.curves[branch.curve as usize] else { + return Ok(true); + }; + let definition = &geometry.intersections[definition as usize]; + let tube = definition.uv_tubes.get(segment).ok_or_else(|| { + GeometryError::InvalidGeometry("intersection branch tube is missing".into()) + })?; + for side in 0..2 { + let definition_side = match geometry.pcurves[branch.pcurves[side] as usize] { + PcurveGeometry::IntersectionSide { + side: IntersectionSide::A, + .. + } => 0, + PcurveGeometry::IntersectionSide { + side: IntersectionSide::B, + .. + } => 1, + _ => side, + }; + let periods = geometry + .surface(definition.surfaces[definition_side])? + .charts()[0] + .periods; + for axis in 0..2 { + let interval = tube[2 * definition_side + axis]; + let face_bounds = bounds[side][axis]; + let overlaps = periods[axis].map_or_else( + || interval.lo <= face_bounds.hi && interval.hi >= face_bounds.lo, + |period| { + (-2..=2).any(|turn| { + let shift = f64::from(turn) * period; + interval.lo + shift <= face_bounds.hi + && interval.hi + shift >= face_bounds.lo + }) + }, + ); + if !overlaps { + return Ok(false); + } + } + } + Ok(true) +} + +fn transition_parameter( + geometry: &GeometryStore, + branch: &SsiCurve, + faces: [(FaceView<'_>, &Face); 2], + boxes_only: bool, + mut outside: f64, + mut inside: f64, + tolerance: f64, +) -> Result { + for _ in 0..64 { + let middle = 0.5 * (outside + inside); + if branch_inside_faces(geometry, branch, faces, boxes_only, tolerance, middle)? { + inside = middle; + } else { + outside = middle; + } + if (inside - outside).abs() <= f64::EPSILON * middle.abs().max(1.0) * 16.0 { + break; + } + } + Ok(inside) +} diff --git a/main/opengeometry/src/intersection/face_intersection/face_view.rs b/main/opengeometry/src/intersection/face_intersection/face_view.rs new file mode 100644 index 0000000..154b0b2 --- /dev/null +++ b/main/opengeometry/src/intersection/face_intersection/face_view.rs @@ -0,0 +1,7 @@ +use crate::brep::BrepEnvelope; + +#[derive(Clone, Copy)] +pub(crate) struct FaceView<'a> { + pub(crate) brep: &'a BrepEnvelope, + pub(crate) face: u32, +} diff --git a/main/opengeometry/src/intersection/face_intersection/mod.rs b/main/opengeometry/src/intersection/face_intersection/mod.rs new file mode 100644 index 0000000..2fc7062 --- /dev/null +++ b/main/opengeometry/src/intersection/face_intersection/mod.rs @@ -0,0 +1,252 @@ +mod bounded; +mod clip; +mod face_view; +#[cfg(test)] +mod tests; +mod trim; + +pub(crate) use face_view::FaceView; + +use crate::brep::{ + Accuracy, BrepEnvelope, Curve, Face, GeometryError, GeometryStore, Surface, SurfaceGeometry, +}; +use crate::intersection::ssi_result::SsiCurve; +use crate::math::{Interval, Point3}; +use crate::query::face_contains_uv; +use bounded::intersect_bounded_surfaces; +use clip::clip_face_branch; +use trim::{boundary_parameters, clip_coordinate, face_patch_bounds, is_uv_box_trim, pcurve_line}; + +pub(crate) struct IntersectionBranch { + pub(crate) curve: u32, + pub(crate) pcurves: [u32; 2], + pub(crate) range: Interval, + pub(crate) endpoints: [Point3; 2], +} + +pub(crate) struct IntersectionGraph { + pub(crate) geometry: GeometryStore, + pub(crate) branches: Vec, + pub(crate) contacts: Vec, + pub(crate) coincident: bool, +} + +pub(crate) struct FacePairIntersection { + pub(crate) faces: [u32; 2], + pub(crate) graph: IntersectionGraph, +} + +pub(crate) struct BodyIntersectionGraph { + pub(crate) pairs: Vec, +} + +pub(crate) fn intersect_breps( + a: &BrepEnvelope, + b: &BrepEnvelope, +) -> Result { + a.validate()?; + b.validate()?; + let accuracy = Accuracy::combined(a.accuracy, b.accuracy); + let bounds_a = a + .topology + .faces + .iter() + .map(|face| face_patch_bounds(a, face)) + .collect::, _>>()?; + let bounds_b = b + .topology + .faces + .iter() + .map(|face| face_patch_bounds(b, face)) + .collect::, _>>()?; + let mut pairs = Vec::new(); + for (face_a, bounds_a) in a.topology.faces.iter().zip(&bounds_a) { + for (face_b, bounds_b) in b.topology.faces.iter().zip(&bounds_b) { + if !bounds_a.overlaps(*bounds_b, accuracy.intersection) { + continue; + } + let graph = intersect_faces( + FaceView { + brep: a, + face: face_a.id, + }, + FaceView { + brep: b, + face: face_b.id, + }, + ) + .map_err(|error| match error { + GeometryError::UnresolvedIntersection(message) => { + GeometryError::UnresolvedIntersection(format!( + "face pair {}:{}: {message}", + face_a.id, face_b.id + )) + } + other => other, + })?; + if graph.coincident || !graph.branches.is_empty() || !graph.contacts.is_empty() { + pairs.push(FacePairIntersection { + faces: [face_a.id, face_b.id], + graph, + }); + } + } + } + Ok(BodyIntersectionGraph { pairs }) +} + +pub(crate) fn intersect_faces( + a: FaceView<'_>, + b: FaceView<'_>, +) -> Result { + a.brep.validate()?; + b.brep.validate()?; + let face_a = a.brep.topology.faces.get(a.face as usize).ok_or_else(|| { + GeometryError::MissingReference { + kind: "face".into(), + index: a.face, + } + })?; + let face_b = b.brep.topology.faces.get(b.face as usize).ok_or_else(|| { + GeometryError::MissingReference { + kind: "face".into(), + index: b.face, + } + })?; + let planar = matches!( + a.brep.geometry.surfaces[face_a.surface as usize], + SurfaceGeometry::Plane { .. } + ) && matches!( + b.brep.geometry.surfaces[face_b.surface as usize], + SurfaceGeometry::Plane { .. } + ); + let boxes_only = !planar && is_uv_box_trim(a.brep, face_a)? && is_uv_box_trim(b.brep, face_b)?; + let mut geometry = GeometryStore::new(); + geometry + .surfaces + .push(a.brep.geometry.surfaces[face_a.surface as usize].clone()); + geometry + .surfaces + .push(b.brep.geometry.surfaces[face_b.surface as usize].clone()); + let accuracy = Accuracy::combined(a.brep.accuracy, b.brep.accuracy); + let result = intersect_bounded_surfaces(&mut geometry, [face_a, face_b], accuracy)?; + let contacts = face_contacts(&geometry, result.contacts, [(a, face_a), (b, face_b)]); + let mut branches = Vec::new(); + for branch in result.curves { + if !planar { + for range in clip_face_branch( + &geometry, + &branch, + [(a, face_a), (b, face_b)], + boxes_only, + [face_a.trim.uv_bounds, face_b.trim.uv_bounds], + accuracy, + )? { + let curve = geometry.curve(branch.curve)?; + branches.push(IntersectionBranch { + curve: branch.curve, + pcurves: branch.pcurves, + range, + endpoints: [curve.point_at(range.lo)?, curve.point_at(range.hi)?], + }); + } + continue; + } + add_planar_branches( + &geometry, + &branch, + [(a, face_a), (b, face_b)], + accuracy, + &mut branches, + )?; + } + Ok(IntersectionGraph { + geometry, + branches, + contacts, + coincident: result.coincident, + }) +} + +fn face_contacts( + geometry: &GeometryStore, + contacts: Vec, + faces: [(FaceView<'_>, &Face); 2], +) -> Vec { + let [(a, face_a), (b, face_b)] = faces; + contacts + .into_iter() + .filter_map(|point| { + let ua = geometry.surfaces[0].project(point, None).ok()?; + let ub = geometry.surfaces[1].project(point, None).ok()?; + (face_contains_uv(a.brep, face_a, ua).ok()? == Some(true) + && face_contains_uv(b.brep, face_b, ub).ok()? == Some(true)) + .then_some(point) + }) + .collect() +} + +fn add_planar_branches( + geometry: &GeometryStore, + branch: &SsiCurve, + faces: [(FaceView<'_>, &Face); 2], + accuracy: Accuracy, + branches: &mut Vec, +) -> Result<(), GeometryError> { + let [(a, face_a), (b, face_b)] = faces; + let (origin_a, direction_a) = pcurve_line(geometry, branch.pcurves[0])?; + let (origin_b, direction_b) = pcurve_line(geometry, branch.pcurves[1])?; + let mut range = branch + .domain + .map_or([-f64::MAX.sqrt(), f64::MAX.sqrt()], |range| { + [range.lo, range.hi] + }); + for (origin, direction, face) in [ + (origin_a, direction_a, face_a), + (origin_b, direction_b, face_b), + ] { + for axis in 0..2 { + if !clip_coordinate( + origin[axis], + direction[axis], + face.trim.uv_bounds[axis], + &mut range, + ) { + range[0] = 1.0; + range[1] = 0.0; + break; + } + } + } + if range[0] > range[1] { + return Ok(()); + } + let mut splits = vec![range[0], range[1]]; + boundary_parameters(a.brep, a.face, origin_a, direction_a, &mut splits)?; + boundary_parameters(b.brep, b.face, origin_b, direction_b, &mut splits)?; + splits.retain(|value| *value >= range[0] && *value <= range[1] && value.is_finite()); + splits.sort_by(f64::total_cmp); + splits.dedup_by(|left, right| (*left - *right).abs() <= accuracy.intersection); + for interval in splits.windows(2) { + if interval[1] - interval[0] <= accuracy.geometric { + continue; + } + let midpoint = interval[0] / 2.0 + interval[1] / 2.0; + let uv_a = geometry.pcurve_at(branch.pcurves[0], midpoint)?; + let uv_b = geometry.pcurve_at(branch.pcurves[1], midpoint)?; + if face_contains_uv(a.brep, face_a, uv_a)? != Some(true) + || face_contains_uv(b.brep, face_b, uv_b)? != Some(true) + { + continue; + } + let range = Interval::new(interval[0], interval[1])?; + let curve = geometry.curve(branch.curve)?; + branches.push(IntersectionBranch { + curve: branch.curve, + pcurves: branch.pcurves, + range, + endpoints: [curve.point_at(range.lo)?, curve.point_at(range.hi)?], + }); + } + Ok(()) +} diff --git a/main/opengeometry/src/intersection/face_intersection/tests.rs b/main/opengeometry/src/intersection/face_intersection/tests.rs new file mode 100644 index 0000000..e5d703f --- /dev/null +++ b/main/opengeometry/src/intersection/face_intersection/tests.rs @@ -0,0 +1,183 @@ +use super::face_view::FaceView; +use super::{intersect_breps, intersect_faces}; +use crate::brep::{coarse_accuracy, Accuracy, Curve, CurveGeometry, Frame3, Surface}; +use crate::math::{norm, sub}; +use crate::primitives; + +#[test] +fn plane_face_graph_clips_the_shared_curve_to_both_trims() { + let a = primitives::cuboid( + "a".into(), + Frame3::IDENTITY, + [2.0; 3], + coarse_accuracy(1e-5), + ) + .unwrap(); + let b = primitives::cuboid( + "b".into(), + Frame3 { + origin: [-1.0, 0.5, 1.0], + ..Frame3::IDENTITY + }, + [2.0; 3], + coarse_accuracy(1e-5), + ) + .unwrap(); + let graph = intersect_faces( + FaceView { brep: &a, face: 1 }, + FaceView { brep: &b, face: 3 }, + ) + .unwrap(); + assert_eq!(graph.branches.len(), 1); + let mut endpoints = graph.branches[0].endpoints; + endpoints.sort_by(|a, b| a[1].total_cmp(&b[1])); + for (point, expected_y) in endpoints.into_iter().zip([0.5, 2.0]) { + assert!((point[0] - 1.0).abs() <= coarse_accuracy(1e-5).intersection); + assert!((point[1] - expected_y).abs() <= coarse_accuracy(1e-5).intersection); + assert!((point[2] - 2.0).abs() <= coarse_accuracy(1e-5).intersection); + } +} + +#[test] +fn vertical_plane_cylinder_generators_remain_exact_and_bounded() { + let quarter = std::f64::consts::FRAC_PI_2; + let host = primitives::arc_edged_extrusion( + "arc-profile".into(), + Frame3::IDENTITY, + vec![ + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 2.0, + start_angle: 0.0, + sweep_angle: quarter, + }, + primitives::ProfileEdge::Line { + from: [0.0, 2.0], + to: [0.0, 1.5], + }, + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.5, + start_angle: quarter, + sweep_angle: -quarter, + }, + primitives::ProfileEdge::Line { + from: [1.5, 0.0], + to: [2.0, 0.0], + }, + ], + 3.0, + coarse_accuracy(1e-5), + ) + .unwrap(); + let cutter = primitives::cuboid( + "opening".into(), + Frame3 { + origin: [1.1, 0.9, 0.5], + ..Frame3::IDENTITY + }, + [1.0, 0.35, 1.5], + coarse_accuracy(1e-5), + ) + .unwrap(); + let graph = intersect_breps(&host, &cutter).unwrap(); + assert!(!graph.pairs.is_empty()); + let branches = graph + .pairs + .iter() + .flat_map(|pair| &pair.graph.branches) + .collect::>(); + assert!(!branches.is_empty()); + for pair in &graph.pairs { + for branch in &pair.graph.branches { + assert!(branch.range.lo.is_finite() && branch.range.hi.is_finite()); + assert!(!matches!( + pair.graph.geometry.curves[branch.curve as usize], + CurveGeometry::Intersection { .. } + )); + } + } +} + +#[test] +fn bounded_nonparallel_cylinder_faces_use_universal_ssi() { + let accuracy = Accuracy { + geometric: 1e-5, + intersection: 2.5e-6, + tessellation: 0.01, + exchange: 1e-5, + }; + let a = primitives::cylinder( + "a".into(), + Frame3 { + origin: [0.0, 0.0, -2.0], + ..Frame3::IDENTITY + }, + 1.0, + 4.0, + accuracy, + ) + .unwrap(); + let b_frame = Frame3::from_axis([-1.0, 0.0, 0.2], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]).unwrap(); + let b = primitives::cylinder("b".into(), b_frame, 0.6, 2.0, accuracy).unwrap(); + let graph = intersect_faces( + FaceView { brep: &a, face: 0 }, + FaceView { brep: &b, face: 0 }, + ) + .unwrap(); + assert!(!graph.branches.is_empty()); + for branch in &graph.branches { + assert!(matches!( + graph.geometry.curves[branch.curve as usize], + CurveGeometry::Intersection { .. } + )); + let parameter = branch.range.midpoint(); + let point = graph + .geometry + .curve(branch.curve) + .unwrap() + .point_at(parameter) + .unwrap(); + for side in 0..2 { + let uv = graph + .geometry + .pcurve_at(branch.pcurves[side], parameter) + .unwrap(); + let support = graph.geometry.surfaces[side].point_at(uv).unwrap(); + assert!(norm(sub(point, support)) <= accuracy.intersection); + } + } +} + +#[test] +fn body_graph_uses_patch_bounds_before_face_pair_ssi() { + let accuracy = Accuracy { + geometric: 1e-5, + intersection: 2.5e-6, + tessellation: 0.01, + exchange: 1e-5, + }; + let a = primitives::cylinder( + "a".into(), + Frame3 { + origin: [0.0, 0.0, -2.0], + ..Frame3::IDENTITY + }, + 1.0, + 4.0, + accuracy, + ) + .unwrap(); + let b = primitives::cylinder( + "b".into(), + Frame3::from_axis([-2.0, 0.0, 0.2], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]).unwrap(), + 0.6, + 4.0, + accuracy, + ) + .unwrap(); + let graph = intersect_breps(&a, &b).unwrap(); + assert_eq!(graph.pairs.len(), 1); + assert_eq!(graph.pairs[0].faces, [0, 0]); + assert_eq!(graph.pairs[0].graph.branches.len(), 2); +} diff --git a/main/opengeometry/src/intersection/face_intersection/trim.rs b/main/opengeometry/src/intersection/face_intersection/trim.rs new file mode 100644 index 0000000..e4e7023 --- /dev/null +++ b/main/opengeometry/src/intersection/face_intersection/trim.rs @@ -0,0 +1,263 @@ +use crate::brep::{ + BrepEnvelope, Face, GeometryError, GeometryStore, PatchBounds, PcurveGeometry, Surface, UVBox, +}; +use crate::math::{quadratic, Interval, QuadraticRoots}; + +pub(super) fn face_patch_bounds( + brep: &BrepEnvelope, + face: &Face, +) -> Result { + brep.geometry + .surface(face.surface)? + .enclose(face.trim.uv_bounds) +} + +pub(super) fn clip_coordinate( + origin: f64, + direction: f64, + bounds: Interval, + range: &mut [f64; 2], +) -> bool { + if direction == 0.0 { + return bounds.contains(origin); + } + let mut lo = (bounds.lo - origin) / direction; + let mut hi = (bounds.hi - origin) / direction; + if lo > hi { + std::mem::swap(&mut lo, &mut hi); + } + range[0] = range[0].max(lo); + range[1] = range[1].min(hi); + range[0] <= range[1] +} + +pub(super) fn pcurve_line( + store: &GeometryStore, + id: u32, +) -> Result<([f64; 2], [f64; 2]), GeometryError> { + match store.pcurves.get(id as usize) { + Some(PcurveGeometry::Line2 { origin, direction }) => Ok((*origin, *direction)), + _ => Err(GeometryError::CoverageGap { + families: ["plane trim".into(), "nonlinear intersection pcurve".into()], + }), + } +} + +pub(super) fn boundary_parameters( + brep: &BrepEnvelope, + face_id: u32, + line_origin: [f64; 2], + line_direction: [f64; 2], + output: &mut Vec, +) -> Result<(), GeometryError> { + let face = brep.topology.faces.get(face_id as usize).ok_or_else(|| { + GeometryError::MissingReference { + kind: "face".into(), + index: face_id, + } + })?; + for loop_id in std::iter::once(face.trim.outer).chain(face.trim.holes.iter().copied()) { + let loop_ = &brep.topology.loops[loop_id as usize]; + let start = loop_.start_halfedge; + let mut current = start; + for _ in 0..=brep.topology.halfedges.len() { + let use_ = &brep.topology.halfedges[current as usize]; + let pcurve_id = use_.geometry_use.pcurve.ok_or_else(|| { + GeometryError::InvalidTopology("trim boundary is missing its pcurve".into()) + })?; + let edge_range = brep.topology.edges[use_.edge as usize].geometry.range(); + add_pcurve_crossings( + &brep.geometry.pcurves[pcurve_id as usize], + line_origin, + line_direction, + edge_range, + output, + )?; + current = use_.next.ok_or_else(|| { + GeometryError::InvalidTopology("open face loop during intersection".into()) + })?; + if current == start { + break; + } + } + if current != start { + return Err(GeometryError::InvalidTopology( + "face loop does not close during intersection".into(), + )); + } + } + Ok(()) +} + +fn add_pcurve_crossings( + pcurve: &PcurveGeometry, + line_origin: [f64; 2], + line_direction: [f64; 2], + edge_range: Interval, + output: &mut Vec, +) -> Result<(), GeometryError> { + let delta_cross = |a: [f64; 2], b: [f64; 2]| a[0] * b[1] - a[1] * b[0]; + match pcurve { + PcurveGeometry::Line2 { origin, direction } => { + let determinant = delta_cross(line_direction, *direction); + if determinant != 0.0 { + let delta = [origin[0] - line_origin[0], origin[1] - line_origin[1]]; + let t = delta_cross(delta, *direction) / determinant; + let s = delta_cross(delta, line_direction) / determinant; + if edge_range.contains(s) { + output.push(t); + } + } + } + PcurveGeometry::Conic2 { + origin, + axis_a, + axis_b, + } => { + let determinant = delta_cross(*axis_a, *axis_b); + if determinant == 0.0 { + return Err(GeometryError::InvalidGeometry( + "degenerate planar conic trim".into(), + )); + } + let inverse = |vector: [f64; 2]| { + [ + delta_cross(vector, *axis_b) / determinant, + delta_cross(*axis_a, vector) / determinant, + ] + }; + let offset = inverse([line_origin[0] - origin[0], line_origin[1] - origin[1]]); + let rate = inverse(line_direction); + let roots = quadratic( + rate[0] * rate[0] + rate[1] * rate[1], + 2.0 * (offset[0] * rate[0] + offset[1] * rate[1]), + offset[0] * offset[0] + offset[1] * offset[1] - 1.0, + )?; + let candidates: Vec = match roots { + QuadraticRoots::Empty => Vec::new(), + QuadraticRoots::One(root) => vec![root], + QuadraticRoots::Two(roots) => roots.to_vec(), + QuadraticRoots::IdenticallyZero => { + return Err(GeometryError::UnresolvedIntersection( + "intersection curve coincides with a conic trim".into(), + )) + } + }; + for t in candidates { + let coordinates = [offset[0] + rate[0] * t, offset[1] + rate[1] * t]; + let parameter = coordinates[1].atan2(coordinates[0]); + let tau = std::f64::consts::TAU; + if (-2..=2).any(|turn| edge_range.contains(parameter + f64::from(turn) * tau)) { + output.push(t); + } + } + } + PcurveGeometry::ProjectedCurve { .. } | PcurveGeometry::IntersectionSide { .. } => { + return Err(GeometryError::CoverageGap { + families: ["plane trim".into(), "general numerical boundary".into()], + }) + } + } + Ok(()) +} + +pub(super) fn uv_in_bounds( + geometry: &GeometryStore, + surface: u32, + bounds: UVBox, + mut uv: [f64; 2], + tolerance: f64, +) -> Result { + let periods = geometry.surface(surface)?.charts()[0].periods; + for axis in 0..2 { + uv[axis] = periodic_value(uv[axis], bounds[axis], periods[axis]); + if uv[axis] < bounds[axis].lo - tolerance || uv[axis] > bounds[axis].hi + tolerance { + return Ok(false); + } + } + Ok(true) +} + +pub(super) fn periodic_value(value: f64, bounds: Interval, period: Option) -> f64 { + period.map_or(value, |period| { + value + ((bounds.midpoint() - value) / period).round() * period + }) +} + +pub(super) fn is_uv_box_trim(brep: &BrepEnvelope, face: &Face) -> Result { + if !face.trim.holes.is_empty() { + return Ok(false); + } + let surface = brep.geometry.surface(face.surface)?; + let periods = surface.charts()[face.trim.chart as usize].periods; + let tolerance = brep.accuracy.intersection; + let mut sides = [false; 4]; + for halfedge in loop_halfedges(brep, face.trim.outer)? { + let use_ = &brep.topology.halfedges[halfedge as usize]; + let pcurve = use_.geometry_use.pcurve.ok_or_else(|| { + GeometryError::InvalidTopology("trim boundary is missing its pcurve".into()) + })?; + let PcurveGeometry::Line2 { origin, direction } = brep.geometry.pcurves[pcurve as usize] + else { + return Ok(false); + }; + let range = brep.topology.edges[use_.edge as usize].geometry.range(); + let mut ends: [[f64; 2]; 2] = [range.lo, range.hi].map(|parameter| { + std::array::from_fn(|axis| origin[axis] + direction[axis] * parameter) + }); + for end in &mut ends { + for axis in 0..2 { + end[axis] = periodic_value(end[axis], face.trim.uv_bounds[axis], periods[axis]); + } + } + let delta = [ends[1][0] - ends[0][0], ends[1][1] - ends[0][1]]; + let constant_axis = if delta[0].abs() <= tolerance { + 0 + } else if delta[1].abs() <= tolerance { + 1 + } else { + return Ok(false); + }; + let varying_axis = 1 - constant_axis; + let constant = 0.5 * (ends[0][constant_axis] + ends[1][constant_axis]); + let bounds = face.trim.uv_bounds[constant_axis]; + let side = if (constant - bounds.lo).abs() <= tolerance { + 2 * constant_axis + } else if (constant - bounds.hi).abs() <= tolerance { + 2 * constant_axis + 1 + } else { + return Ok(false); + }; + if ends.iter().any(|end| { + end[varying_axis] < face.trim.uv_bounds[varying_axis].lo - tolerance + || end[varying_axis] > face.trim.uv_bounds[varying_axis].hi + tolerance + }) { + return Ok(false); + } + sides[side] = true; + } + Ok(sides.into_iter().all(|present| present)) +} + +fn loop_halfedges(brep: &BrepEnvelope, loop_id: u32) -> Result, GeometryError> { + let loop_ = brep.topology.loops.get(loop_id as usize).ok_or_else(|| { + GeometryError::MissingReference { + kind: "loop".into(), + index: loop_id, + } + })?; + let mut current = loop_.start_halfedge; + let mut result = Vec::new(); + for _ in 0..=brep.topology.halfedges.len() { + result.push(current); + current = brep.topology.halfedges[current as usize] + .next + .ok_or_else(|| GeometryError::InvalidTopology("open face loop during SSI".into()))?; + if current == loop_.start_halfedge { + return Ok(result); + } + } + Err(GeometryError::InvalidTopology( + "face loop does not close during SSI".into(), + )) +} diff --git a/main/opengeometry/src/intersection/mod.rs b/main/opengeometry/src/intersection/mod.rs new file mode 100644 index 0000000..6624798 --- /dev/null +++ b/main/opengeometry/src/intersection/mod.rs @@ -0,0 +1,12 @@ +mod face_intersection; +mod ssi; +mod ssi_result; +mod universal_ssi; + +pub(super) use face_intersection::{ + intersect_breps, intersect_faces, FaceView, IntersectionBranch, IntersectionGraph, +}; +pub(super) use ssi::intersect_surfaces; + +#[cfg(test)] +pub(super) use universal_ssi::intersect_patches; diff --git a/main/opengeometry/src/intersection/ssi/circles.rs b/main/opengeometry/src/intersection/ssi/circles.rs new file mode 100644 index 0000000..9efb14b --- /dev/null +++ b/main/opengeometry/src/intersection/ssi/circles.rs @@ -0,0 +1,45 @@ +use crate::brep::{CurveGeometry, Frame3, GeometryError}; +use crate::intersection::ssi_result::GeometryResult; +use crate::math::{Interval, Point3}; + +pub(super) fn circle( + center: Point3, + normal: Point3, + radius: f64, +) -> Result { + if !radius.is_finite() || radius <= 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "positive intersection radius is not representable".into(), + )); + } + let reference = if normal[0].abs() < 0.8 { + [1.0, 0.0, 0.0] + } else { + [0.0, 1.0, 0.0] + }; + let frame = Frame3::from_axis(center, normal, reference)?; + Ok(GeometryResult::Curves(vec![( + CurveGeometry::Circle { frame, radius }, + Some(Interval::new(0.0, std::f64::consts::TAU)?), + )])) +} + +pub(super) fn axial_circles( + normal: Point3, + sections: impl IntoIterator, +) -> Result { + let mut curves = Vec::new(); + for (center, radius) in sections { + let GeometryResult::Curves(mut section) = circle(center, normal, radius)? else { + return Err(GeometryError::UnresolvedIntersection( + "axial circle construction returned a non-curve".into(), + )); + }; + curves.append(&mut section); + } + Ok(if curves.is_empty() { + GeometryResult::Empty + } else { + GeometryResult::Curves(curves) + }) +} diff --git a/main/opengeometry/src/intersection/ssi/coaxial.rs b/main/opengeometry/src/intersection/ssi/coaxial.rs new file mode 100644 index 0000000..8a4bd26 --- /dev/null +++ b/main/opengeometry/src/intersection/ssi/coaxial.rs @@ -0,0 +1,435 @@ +use super::circles::{axial_circles, circle}; +use crate::brep::{unit, CurveGeometry, Frame3, GeometryError}; +use crate::intersection::ssi_result::GeometryResult; +use crate::math::{ + add, cross, dot, norm, quadratic, scale, sphere_sphere_relation, sub, Interval, QuadraticRoots, + Sign, +}; + +pub(super) fn parallel_cylinders( + a: Frame3, + ra: f64, + b: Frame3, + rb: f64, +) -> Result { + if norm(cross(a.z, b.z)) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "cylinder".into()], + }); + } + let delta = sub(b.origin, a.origin); + let radial = sub(delta, scale(a.z, dot(delta, a.z))); + let distance = norm(radial); + if distance == 0.0 { + return Ok(if ra == rb { + GeometryResult::Coincident + } else { + GeometryResult::Empty + }); + } + let projected_center = add(a.origin, radial); + let [outer, inner] = sphere_sphere_relation(a.origin, ra, projected_center, rb)?; + if outer == Sign::Positive || inner == Sign::Negative { + return Ok(GeometryResult::Empty); + } + let toward_b = unit(radial)?; + let along = 0.5 * (distance + (ra - rb) * (ra + rb) / distance); + let base = add(a.origin, scale(toward_b, along)); + if outer == Sign::Zero || inner == Sign::Zero { + return Ok(GeometryResult::Curves(vec![( + CurveGeometry::Line { + origin: base, + direction: a.z, + }, + None, + )])); + } + let half_separation_squared = (ra - along.abs()) * (ra + along.abs()); + if half_separation_squared <= 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "parallel-cylinder branch separation cancellation".into(), + )); + } + let lateral = unit(cross(a.z, toward_b))?; + let half_separation = half_separation_squared.sqrt(); + Ok(GeometryResult::Curves( + [-1.0, 1.0] + .into_iter() + .map(|sense| { + ( + CurveGeometry::Line { + origin: add(base, scale(lateral, sense * half_separation)), + direction: a.z, + }, + None, + ) + }) + .collect(), + )) +} + +pub(super) fn coaxial_cylinder_cone( + cylinder: Frame3, + radius: f64, + cone: Frame3, + semi_angle: f64, +) -> Result { + if norm(cross(cylinder.z, cone.z)) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "cone".into()], + }); + } + let delta = sub(cylinder.origin, cone.origin); + if norm(sub(delta, scale(cone.z, dot(delta, cone.z)))) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "cone".into()], + }); + } + let height = radius / semi_angle.tan(); + if !height.is_finite() || height <= 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "coaxial cylinder/cone section exceeds numerical range".into(), + )); + } + circle(add(cone.origin, scale(cone.z, height)), cone.z, radius) +} + +pub(super) fn coaxial_sphere_cylinder( + sphere: Frame3, + sphere_radius: f64, + cylinder: Frame3, + cylinder_radius: f64, +) -> Result { + let delta = sub(sphere.origin, cylinder.origin); + if norm(sub(delta, scale(cylinder.z, dot(delta, cylinder.z)))) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["sphere".into(), "cylinder".into()], + }); + } + if cylinder_radius > sphere_radius { + return Ok(GeometryResult::Empty); + } + if cylinder_radius == sphere_radius { + return circle(sphere.origin, cylinder.z, cylinder_radius); + } + let half_height_squared = (sphere_radius - cylinder_radius) * (sphere_radius + cylinder_radius); + if half_height_squared <= 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "sphere/cylinder branch separation cancellation".into(), + )); + } + let half_height = half_height_squared.sqrt(); + let domain = Some(Interval::new(0.0, std::f64::consts::TAU)?); + let mut curves = Vec::with_capacity(2); + for sense in [-1.0, 1.0] { + let center = add(sphere.origin, scale(cylinder.z, sense * half_height)); + let GeometryResult::Curves(mut circle) = circle(center, cylinder.z, cylinder_radius)? + else { + return Err(GeometryError::UnresolvedIntersection( + "sphere/cylinder circle construction returned a non-curve".into(), + )); + }; + let mut curve = circle.pop().ok_or_else(|| { + GeometryError::UnresolvedIntersection("empty sphere/cylinder circle".into()) + })?; + curve.1 = domain; + curves.push(curve); + } + Ok(GeometryResult::Curves(curves)) +} + +pub(super) fn coaxial_sphere_cone( + sphere: Frame3, + sphere_radius: f64, + cone: Frame3, + semi_angle: f64, +) -> Result { + let delta = sub(sphere.origin, cone.origin); + let center_height = dot(delta, cone.z); + if norm(sub(delta, scale(cone.z, center_height))) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["sphere".into(), "cone".into()], + }); + } + let slope = semi_angle.tan(); + let roots = quadratic( + 1.0 + slope * slope, + -2.0 * center_height, + center_height * center_height - sphere_radius * sphere_radius, + )?; + let roots: Vec = match roots { + QuadraticRoots::Empty => Vec::new(), + QuadraticRoots::One(root) => vec![root], + QuadraticRoots::Two(roots) => roots.into(), + QuadraticRoots::IdenticallyZero => { + return Err(GeometryError::UnresolvedIntersection( + "sphere/cone equation is unexpectedly indeterminate".into(), + )) + } + }; + let apex_contact = roots.contains(&0.0); + let circles = axial_circles( + cone.z, + roots + .into_iter() + .filter(|height| *height > 0.0) + .map(|height| (add(cone.origin, scale(cone.z, height)), slope * height)), + )?; + if !apex_contact { + return Ok(circles); + } + Ok(match circles { + GeometryResult::Empty => GeometryResult::Contact(cone.origin), + GeometryResult::Curves(curves) => GeometryResult::CurvesAndContacts { + curves, + contacts: vec![cone.origin], + }, + _ => { + return Err(GeometryError::UnresolvedIntersection( + "sphere/cone apex classification produced an inconsistent result".into(), + )) + } + }) +} + +pub(super) fn coaxial_cones( + a: Frame3, + angle_a: f64, + b: Frame3, + angle_b: f64, +) -> Result { + if norm(cross(a.z, b.z)) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["cone".into(), "cone".into()], + }); + } + let delta = sub(b.origin, a.origin); + let axial_offset = dot(delta, a.z); + if norm(sub(delta, scale(a.z, axial_offset))) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["cone".into(), "cone".into()], + }); + } + let same_direction = dot(a.z, b.z) > 0.0; + if axial_offset == 0.0 { + return Ok(if same_direction && angle_a == angle_b { + GeometryResult::Coincident + } else { + GeometryResult::Contact(a.origin) + }); + } + let ka = angle_a.tan(); + let kb = angle_b.tan(); + let height_a = if same_direction { + if ka == kb { + return Ok(GeometryResult::Empty); + } + kb * axial_offset / (kb - ka) + } else { + kb * axial_offset / (ka + kb) + }; + let height_b = if same_direction { + height_a - axial_offset + } else { + axial_offset - height_a + }; + if !height_a.is_finite() || !height_b.is_finite() { + return Err(GeometryError::UnresolvedIntersection( + "coaxial cone section exceeds numerical range".into(), + )); + } + if height_a < 0.0 || height_b < 0.0 { + return Ok(GeometryResult::Empty); + } + if height_a == 0.0 || height_b == 0.0 { + return Ok(GeometryResult::Contact(add(a.origin, scale(a.z, height_a)))); + } + circle(add(a.origin, scale(a.z, height_a)), a.z, ka * height_a) +} + +fn trig_roots(a: f64, b: f64, c: f64) -> Result, GeometryError> { + let amplitude = a.hypot(b); + if !amplitude.is_finite() || amplitude == 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "degenerate trigonometric intersection equation".into(), + )); + } + let ratio = -c / amplitude; + if !ratio.is_finite() { + return Err(GeometryError::UnresolvedIntersection( + "trigonometric intersection equation exceeds numerical range".into(), + )); + } + if ratio.abs() > 1.0 { + return Ok(Vec::new()); + } + let phase = b.atan2(a); + let delta = ratio.acos(); + Ok(if delta == 0.0 || delta == std::f64::consts::PI { + vec![phase + delta] + } else { + vec![phase - delta, phase + delta] + }) +} + +pub(super) fn coaxial_sphere_torus( + sphere: Frame3, + sphere_radius: f64, + torus: Frame3, + major_radius: f64, + minor_radius: f64, +) -> Result { + let local = torus.local(sphere.origin); + if local[0] != 0.0 || local[1] != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["sphere".into(), "torus".into()], + }); + } + let roots = trig_roots( + 2.0 * major_radius * minor_radius, + -2.0 * local[2] * minor_radius, + major_radius * major_radius + minor_radius * minor_radius + local[2] * local[2] + - sphere_radius * sphere_radius, + )?; + axial_circles( + torus.z, + roots.into_iter().map(|v| { + let center = add(torus.origin, scale(torus.z, minor_radius * v.sin())); + (center, major_radius + minor_radius * v.cos()) + }), + ) +} + +pub(super) fn coaxial_cylinder_torus( + cylinder: Frame3, + cylinder_radius: f64, + torus: Frame3, + major_radius: f64, + minor_radius: f64, +) -> Result { + if norm(cross(cylinder.z, torus.z)) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "torus".into()], + }); + } + let delta = sub(cylinder.origin, torus.origin); + if norm(sub(delta, scale(torus.z, dot(delta, torus.z)))) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "torus".into()], + }); + } + let roots = trig_roots(minor_radius, 0.0, major_radius - cylinder_radius)?; + axial_circles( + torus.z, + roots.into_iter().map(|v| { + ( + add(torus.origin, scale(torus.z, minor_radius * v.sin())), + cylinder_radius, + ) + }), + ) +} + +pub(super) fn coaxial_cone_torus( + cone: Frame3, + semi_angle: f64, + torus: Frame3, + major_radius: f64, + minor_radius: f64, +) -> Result { + if norm(cross(cone.z, torus.z)) != 0.0 || dot(cone.z, torus.z) < 0.0 { + return Err(GeometryError::CoverageGap { + families: ["cone".into(), "torus".into()], + }); + } + let delta = sub(torus.origin, cone.origin); + let height = dot(delta, cone.z); + if norm(sub(delta, scale(cone.z, height))) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["cone".into(), "torus".into()], + }); + } + let slope = semi_angle.tan(); + let roots = trig_roots( + minor_radius, + -slope * minor_radius, + major_radius - slope * height, + )?; + axial_circles( + torus.z, + roots.into_iter().filter_map(|v| { + let z = height + minor_radius * v.sin(); + (z >= 0.0).then(|| { + ( + add(torus.origin, scale(torus.z, minor_radius * v.sin())), + major_radius + minor_radius * v.cos(), + ) + }) + }), + ) +} + +pub(super) fn coaxial_tori( + a: Frame3, + major_a: f64, + minor_a: f64, + b: Frame3, + major_b: f64, + minor_b: f64, +) -> Result { + if norm(cross(a.z, b.z)) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["torus".into(), "torus".into()], + }); + } + let delta = sub(b.origin, a.origin); + let axial_offset = dot(delta, a.z); + if norm(sub(delta, scale(a.z, axial_offset))) != 0.0 { + return Err(GeometryError::CoverageGap { + families: ["torus".into(), "torus".into()], + }); + } + if axial_offset == 0.0 && major_a == major_b && minor_a == minor_b { + return Ok(GeometryResult::Coincident); + } + let radial_offset = major_b - major_a; + let distance = radial_offset.hypot(axial_offset); + if distance == 0.0 { + return Ok(GeometryResult::Empty); + } + let [outer, inner] = sphere_sphere_relation( + [major_a, 0.0, 0.0], + minor_a, + [major_b, 0.0, axial_offset], + minor_b, + )?; + if outer == Sign::Positive || inner == Sign::Negative { + return Ok(GeometryResult::Empty); + } + let er = radial_offset / distance; + let ez = axial_offset / distance; + let along = 0.5 * (distance + (minor_a - minor_b) * (minor_a + minor_b) / distance); + let base_r = major_a + er * along; + let base_z = ez * along; + let sections = if outer == Sign::Zero || inner == Sign::Zero { + vec![(base_r, base_z)] + } else { + let half_squared = (minor_a - along.abs()) * (minor_a + along.abs()); + if half_squared <= 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "coaxial torus branch separation cancellation".into(), + )); + } + let half = half_squared.sqrt(); + vec![ + (base_r - ez * half, base_z + er * half), + (base_r + ez * half, base_z - er * half), + ] + }; + axial_circles( + a.z, + sections + .into_iter() + .map(|(radius, z)| (add(a.origin, scale(a.z, z)), radius)), + ) +} diff --git a/main/opengeometry/src/intersection/ssi/fallback.rs b/main/opengeometry/src/intersection/ssi/fallback.rs new file mode 100644 index 0000000..084dbf7 --- /dev/null +++ b/main/opengeometry/src/intersection/ssi/fallback.rs @@ -0,0 +1,107 @@ +use crate::brep::{ + Accuracy, GeometryError, GeometryStore, PatchBounds, SsiBudget, Surface, SurfaceGeometry, +}; +use crate::intersection::ssi_result::SsiResult; +use crate::intersection::universal_ssi::intersect_patches_from; +use crate::math::{norm, Interval}; + +fn chart_domain(surface: &SurfaceGeometry) -> Result<[Interval; 2], GeometryError> { + let chart = surface + .charts() + .first() + .ok_or_else(|| GeometryError::InvalidGeometry("surface has no parameter chart".into()))?; + Ok([ + Interval::new(chart.domain[0][0], chart.domain[0][1])?, + Interval::new(chart.domain[1][0], chart.domain[1][1])?, + ]) +} + +fn finite_target_domain( + surface: &SurfaceGeometry, + source_bounds: PatchBounds, + margin: f64, +) -> Result<[Interval; 2], GeometryError> { + if matches!( + surface, + SurfaceGeometry::Sphere { .. } | SurfaceGeometry::Torus { .. } + ) { + return chart_domain(surface); + } + let mut local_min = [f64::INFINITY; 3]; + let mut local_max = [f64::NEG_INFINITY; 3]; + let mut maximum_radius = 0.0_f64; + for mask in 0..8 { + let point = std::array::from_fn(|axis| { + if mask & (1 << axis) == 0 { + source_bounds.axes[axis].lo + } else { + source_bounds.axes[axis].hi + } + }); + let local = surface.frame().local(point); + for axis in 0..3 { + local_min[axis] = local_min[axis].min(local[axis]); + local_max[axis] = local_max[axis].max(local[axis]); + } + maximum_radius = maximum_radius.max(norm(local)); + } + let expanded = |lo: f64, hi: f64| Interval::new(lo - margin, hi + margin); + match surface { + SurfaceGeometry::Plane { .. } => Ok([ + expanded(local_min[0], local_max[0])?, + expanded(local_min[1], local_max[1])?, + ]), + SurfaceGeometry::Cylinder { .. } => Ok([ + Interval::new(0.0, std::f64::consts::TAU)?, + expanded(local_min[2], local_max[2])?, + ]), + SurfaceGeometry::Cone { .. } => Ok([ + Interval::new(0.0, std::f64::consts::TAU)?, + Interval::new(0.0, maximum_radius + margin)?, + ]), + SurfaceGeometry::Sphere { .. } | SurfaceGeometry::Torus { .. } => Err( + GeometryError::UnsupportedGeometry("SSI trim surface".into()), + ), + } +} + +pub(super) fn universal_fallback( + store: &mut GeometryStore, + a: u32, + b: u32, + sa: &SurfaceGeometry, + sb: &SurfaceGeometry, + accuracy: Accuracy, + original: GeometryError, +) -> Result { + let source_side = if matches!( + sa, + SurfaceGeometry::Sphere { .. } | SurfaceGeometry::Torus { .. } + ) { + 0 + } else if matches!( + sb, + SurfaceGeometry::Sphere { .. } | SurfaceGeometry::Torus { .. } + ) { + 1 + } else { + return Err(original); + }; + let mut domains = [chart_domain(sa)?, chart_domain(sb)?]; + let source_bounds = store + .surface([a, b][source_side])? + .enclose(domains[source_side])?; + domains[1 - source_side] = finite_target_domain( + store.surface([a, b][1 - source_side])?, + source_bounds, + accuracy.intersection, + )?; + intersect_patches_from( + store, + [a, b], + domains, + source_side, + accuracy, + SsiBudget::default(), + ) +} diff --git a/main/opengeometry/src/intersection/ssi/mod.rs b/main/opengeometry/src/intersection/ssi/mod.rs new file mode 100644 index 0000000..32d2b1f --- /dev/null +++ b/main/opengeometry/src/intersection/ssi/mod.rs @@ -0,0 +1,349 @@ +mod circles; +mod coaxial; +mod fallback; +mod planar; +#[cfg(test)] +mod tests; + +use super::ssi_result::{GeometryResult, SsiCurve, SsiResult}; +use crate::brep::{ + Accuracy, CurveGeometry, Frame3, GeometryError, GeometryStore, PcurveGeometry, Surface, + SurfaceGeometry, +}; +use crate::math::{dot, norm, sub, Interval, Point3}; +use coaxial::{ + coaxial_cone_torus, coaxial_cones, coaxial_cylinder_cone, coaxial_cylinder_torus, + coaxial_sphere_cone, coaxial_sphere_cylinder, coaxial_sphere_torus, coaxial_tori, + parallel_cylinders, +}; +use fallback::universal_fallback; +use planar::{plane_cone, plane_cylinder, plane_plane, plane_sphere, plane_torus, sphere_sphere}; + +pub(crate) fn intersect_surfaces( + store: &mut GeometryStore, + a: u32, + b: u32, + accuracy: Accuracy, +) -> Result { + accuracy.validate()?; + let sa = store.surface(a)?.clone(); + let sb = store.surface(b)?.clone(); + sa.validate()?; + sb.validate()?; + let fast_intersection = analytic_intersection(&sa, &sb, accuracy); + let intersection = match fast_intersection { + Ok(intersection) => intersection, + Err(error @ GeometryError::CoverageGap { .. }) => { + return universal_fallback(store, a, b, &sa, &sb, accuracy, error) + } + Err(error) => return Err(error), + }; + let (intersection, pending_contacts) = match intersection { + GeometryResult::CurvesAndContacts { curves, contacts } => { + (GeometryResult::Curves(curves), contacts) + } + intersection => (intersection, Vec::new()), + }; + let mut result = SsiResult::default(); + match intersection { + GeometryResult::Empty => {} + GeometryResult::Coincident => result.coincident = true, + GeometryResult::Contact(point) => { + check_contact(&sa, &sb, point, accuracy)?; + result.contacts.push(point); + } + GeometryResult::Curves(curves) => { + for (curve, domain) in curves { + let curve_id = u32::try_from(store.curves.len()) + .map_err(|_| GeometryError::LimitExceeded("SSI curves".into()))?; + let start = domain.map_or(0.0, |d| d.lo); + let point = curve.elementary_point(start)?; + let mut pcurves = [0; 2]; + let mut pending = Vec::new(); + for (i, (id, surface)) in [(a, &sa), (b, &sb)].into_iter().enumerate() { + let pcurve = support_pcurve(surface, id, &curve, curve_id, start, point)?; + pcurves[i] = u32::try_from(store.pcurves.len() + i) + .map_err(|_| GeometryError::LimitExceeded("SSI pcurves".into()))?; + pending.push(pcurve); + } + check_curve_support(&curve, start, domain, &sa, &sb, accuracy)?; + store.curves.push(curve); + store.pcurves.extend(pending); + result.curves.push(SsiCurve { + curve: curve_id, + pcurves, + domain, + }); + } + } + GeometryResult::CurvesAndContacts { .. } => { + return Err(GeometryError::InvalidGeometry( + "combined SSI result was not normalized".into(), + )) + } + } + for point in pending_contacts { + check_contact(&sa, &sb, point, accuracy)?; + result.contacts.push(point); + } + Ok(result) +} + +fn analytic_intersection( + sa: &SurfaceGeometry, + sb: &SurfaceGeometry, + accuracy: Accuracy, +) -> Result { + match sa { + SurfaceGeometry::Plane { frame } => plane_pairs(*frame, sb, accuracy), + SurfaceGeometry::Sphere { frame, radius } => sphere_pairs(*frame, *radius, sb), + SurfaceGeometry::Cylinder { frame, radius } => cylinder_pairs(*frame, *radius, sb), + SurfaceGeometry::Cone { frame, semi_angle } => cone_pairs(*frame, *semi_angle, sb), + SurfaceGeometry::Torus { + frame, + major_radius, + minor_radius, + } => torus_pairs(*frame, *major_radius, *minor_radius, sb), + } +} + +fn plane_pairs( + plane: Frame3, + sb: &SurfaceGeometry, + accuracy: Accuracy, +) -> Result { + Ok(match sb { + SurfaceGeometry::Plane { frame: other } => plane_plane(plane, *other, accuracy)?, + SurfaceGeometry::Sphere { + frame: sphere, + radius: sphere_radius, + } => plane_sphere(plane, *sphere, *sphere_radius)?, + SurfaceGeometry::Cylinder { + frame: cylinder, + radius: cylinder_radius, + } => plane_cylinder(plane, *cylinder, *cylinder_radius)?, + SurfaceGeometry::Cone { + frame: cone, + semi_angle, + } => plane_cone(plane, *cone, *semi_angle)?, + SurfaceGeometry::Torus { + frame: torus, + major_radius, + minor_radius, + } => plane_torus(plane, *torus, *major_radius, *minor_radius)?, + }) +} + +fn sphere_pairs( + sphere: Frame3, + sphere_radius: f64, + sb: &SurfaceGeometry, +) -> Result { + Ok(match sb { + SurfaceGeometry::Plane { frame: plane } => plane_sphere(*plane, sphere, sphere_radius)?, + SurfaceGeometry::Sphere { + frame: other, + radius: other_radius, + } => sphere_sphere(sphere, sphere_radius, *other, *other_radius)?, + SurfaceGeometry::Cylinder { + frame: cylinder, + radius: cylinder_radius, + } => coaxial_sphere_cylinder(sphere, sphere_radius, *cylinder, *cylinder_radius)?, + SurfaceGeometry::Cone { + frame: cone, + semi_angle, + } => coaxial_sphere_cone(sphere, sphere_radius, *cone, *semi_angle)?, + SurfaceGeometry::Torus { + frame: torus, + major_radius, + minor_radius, + } => coaxial_sphere_torus(sphere, sphere_radius, *torus, *major_radius, *minor_radius)?, + }) +} + +fn cylinder_pairs( + cylinder: Frame3, + cylinder_radius: f64, + sb: &SurfaceGeometry, +) -> Result { + Ok(match sb { + SurfaceGeometry::Plane { frame: plane } => { + plane_cylinder(*plane, cylinder, cylinder_radius)? + } + SurfaceGeometry::Sphere { + frame: sphere, + radius: sphere_radius, + } => coaxial_sphere_cylinder(*sphere, *sphere_radius, cylinder, cylinder_radius)?, + SurfaceGeometry::Cylinder { + frame: other, + radius: other_radius, + } => parallel_cylinders(cylinder, cylinder_radius, *other, *other_radius)?, + SurfaceGeometry::Cone { + frame: cone, + semi_angle, + } => coaxial_cylinder_cone(cylinder, cylinder_radius, *cone, *semi_angle)?, + SurfaceGeometry::Torus { + frame: torus, + major_radius, + minor_radius, + } => coaxial_cylinder_torus( + cylinder, + cylinder_radius, + *torus, + *major_radius, + *minor_radius, + )?, + }) +} + +fn cone_pairs( + cone: Frame3, + semi_angle: f64, + sb: &SurfaceGeometry, +) -> Result { + Ok(match sb { + SurfaceGeometry::Plane { frame: plane } => plane_cone(*plane, cone, semi_angle)?, + SurfaceGeometry::Sphere { + frame: sphere, + radius: sphere_radius, + } => coaxial_sphere_cone(*sphere, *sphere_radius, cone, semi_angle)?, + SurfaceGeometry::Cylinder { + frame: cylinder, + radius: cylinder_radius, + } => coaxial_cylinder_cone(*cylinder, *cylinder_radius, cone, semi_angle)?, + SurfaceGeometry::Cone { + frame: other, + semi_angle: other_angle, + } => coaxial_cones(cone, semi_angle, *other, *other_angle)?, + SurfaceGeometry::Torus { + frame: torus, + major_radius, + minor_radius, + } => coaxial_cone_torus(cone, semi_angle, *torus, *major_radius, *minor_radius)?, + }) +} + +fn torus_pairs( + torus: Frame3, + major_radius: f64, + minor_radius: f64, + sb: &SurfaceGeometry, +) -> Result { + Ok(match sb { + SurfaceGeometry::Plane { frame: plane } => { + plane_torus(*plane, torus, major_radius, minor_radius)? + } + SurfaceGeometry::Sphere { + frame: sphere, + radius: sphere_radius, + } => coaxial_sphere_torus(*sphere, *sphere_radius, torus, major_radius, minor_radius)?, + SurfaceGeometry::Cylinder { + frame: cylinder, + radius: cylinder_radius, + } => coaxial_cylinder_torus( + *cylinder, + *cylinder_radius, + torus, + major_radius, + minor_radius, + )?, + SurfaceGeometry::Cone { + frame: cone, + semi_angle, + } => coaxial_cone_torus(*cone, *semi_angle, torus, major_radius, minor_radius)?, + SurfaceGeometry::Torus { + frame: other, + major_radius: other_major, + minor_radius: other_minor, + } => coaxial_tori( + torus, + major_radius, + minor_radius, + *other, + *other_major, + *other_minor, + )?, + }) +} + +fn check_contact( + sa: &SurfaceGeometry, + sb: &SurfaceGeometry, + point: Point3, + accuracy: Accuracy, +) -> Result<(), GeometryError> { + for surface in [sa, sb] { + let uv = match surface.project(point, None) { + Ok(uv) => uv, + Err(GeometryError::SingularParameterization) if matches!(surface, SurfaceGeometry::Cone { frame, .. } if norm(sub(frame.origin, point)) <= accuracy.intersection) => { + [0.0, 0.0] + } + Err(error) => return Err(error), + }; + if norm(sub(surface.point_at(uv)?, point)) > accuracy.intersection { + return Err(GeometryError::UnresolvedIntersection( + "contact support residual exceeds budget".into(), + )); + } + } + Ok(()) +} + +fn support_pcurve( + surface: &SurfaceGeometry, + id: u32, + curve: &CurveGeometry, + curve_id: u32, + start: f64, + point: Point3, +) -> Result { + let uv = surface.project(point, None)?; + let pcurve = if matches!(surface, SurfaceGeometry::Plane { .. }) + && matches!(curve, CurveGeometry::Line { .. }) + { + let direction = curve.elementary_tangent(start)?; + let frame = surface.frame(); + PcurveGeometry::Line2 { + origin: uv, + direction: [dot(direction, frame.x), dot(direction, frame.y)], + } + } else { + let next = surface.project(curve.elementary_point(start + 0.01)?, Some(uv))?; + let uv_rate = std::array::from_fn(|j| (next[j] - uv[j]) / 0.01); + PcurveGeometry::ProjectedCurve { + curve: curve_id, + surface: id, + chart: 0, + uv_hint: uv, + uv_rate, + parameter_origin: start, + } + }; + Ok(pcurve) +} + +fn check_curve_support( + curve: &CurveGeometry, + start: f64, + domain: Option, + sa: &SurfaceGeometry, + sb: &SurfaceGeometry, + accuracy: Accuracy, +) -> Result<(), GeometryError> { + for t in [ + start, + start + domain.map_or(1.0, |d| d.width()) * 0.25, + start + domain.map_or(1.0, |d| d.width()) * 0.5, + ] { + let p = curve.elementary_point(t)?; + for surface in [sa, sb] { + let uv = surface.project(p, None)?; + if norm(sub(surface.point_at(uv)?, p)) > accuracy.intersection { + return Err(GeometryError::UnresolvedIntersection( + "intersection curve misses support".into(), + )); + } + } + } + Ok(()) +} diff --git a/main/opengeometry/src/intersection/ssi/planar.rs b/main/opengeometry/src/intersection/ssi/planar.rs new file mode 100644 index 0000000..f17dadb --- /dev/null +++ b/main/opengeometry/src/intersection/ssi/planar.rs @@ -0,0 +1,271 @@ +use super::circles::{axial_circles, circle}; +use crate::brep::{unit, Accuracy, CurveGeometry, Frame3, GeometryError}; +use crate::intersection::ssi_result::GeometryResult; +use crate::math::{ + add, cross, dot, norm, plane_sphere_relation, scale, solve, sphere_sphere_relation, sub, + Interval, Sign, +}; + +pub(super) fn plane_plane( + a: Frame3, + b: Frame3, + accuracy: Accuracy, +) -> Result { + let tangent = cross(a.z, b.z); + let length = norm(tangent); + let delta = sub(b.origin, a.origin); + if length == 0.0 { + return Ok(if dot(a.z, delta).abs() <= accuracy.intersection { + GeometryResult::Coincident + } else { + GeometryResult::Empty + }); + } + let tangent = unit(tangent)?; + let solution = solve([a.z, b.z, tangent], [0.0, dot(b.z, delta), 0.0]) + .map_err(|e| GeometryError::UnresolvedIntersection(format!("plane intersection: {e}")))?; + Ok(GeometryResult::Curves(vec![( + CurveGeometry::Line { + origin: add(a.origin, solution.value), + direction: tangent, + }, + None, + )])) +} + +pub(super) fn plane_sphere( + plane: Frame3, + sphere: Frame3, + radius: f64, +) -> Result { + let relation = plane_sphere_relation(plane.origin, plane.z, sphere.origin, radius)?; + if relation == Sign::Positive { + return Ok(GeometryResult::Empty); + } + let n2 = dot(plane.z, plane.z); + let distance = dot(sub(sphere.origin, plane.origin), plane.z); + let center = sub(sphere.origin, scale(plane.z, distance / n2)); + if relation == Sign::Zero { + return Ok(GeometryResult::Contact(center)); + } + let d = distance.abs() / n2.sqrt(); + let r2 = (radius - d) * (radius + d); + if r2 <= 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "plane/sphere radius cancellation".into(), + )); + } + circle(center, plane.z, r2.sqrt()) +} + +pub(super) fn sphere_sphere( + a: Frame3, + ra: f64, + b: Frame3, + rb: f64, +) -> Result { + let delta = sub(b.origin, a.origin); + let distance = norm(delta); + if distance == 0.0 { + return Ok(if ra == rb { + GeometryResult::Coincident + } else { + GeometryResult::Empty + }); + } + let [outer, inner] = sphere_sphere_relation(a.origin, ra, b.origin, rb)?; + if outer == Sign::Positive || inner == Sign::Negative { + return Ok(GeometryResult::Empty); + } + let axis = unit(delta)?; + let x = 0.5 * (distance + (ra - rb) * (ra + rb) / distance); + let center = add(a.origin, scale(axis, x)); + if outer == Sign::Zero || inner == Sign::Zero { + return Ok(GeometryResult::Contact(center)); + } + let r2 = (ra - x.abs()) * (ra + x.abs()); + if r2 <= 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "sphere/sphere radius cancellation".into(), + )); + } + circle(center, axis, r2.sqrt()) +} + +pub(super) fn plane_cylinder( + plane: Frame3, + cylinder: Frame3, + radius: f64, +) -> Result { + let axial = dot(plane.z, cylinder.z); + let offset = dot(sub(cylinder.origin, plane.origin), plane.z); + if axial != 0.0 { + let center = add(cylinder.origin, scale(cylinder.z, -offset / axial)); + if axial.abs() == 1.0 { + return circle(center, plane.z, radius); + } + let major_direction = unit(sub(cylinder.z, scale(plane.z, axial)))?; + let major_radius = radius / axial.abs(); + if !major_radius.is_finite() { + return Err(GeometryError::UnresolvedIntersection( + "plane/cylinder ellipse exceeds numerical range".into(), + )); + } + let frame = Frame3::from_axis(center, plane.z, major_direction)?; + return Ok(GeometryResult::Curves(vec![( + CurveGeometry::Ellipse { + frame, + major_radius, + minor_radius: radius, + }, + Some(Interval::new(0.0, std::f64::consts::TAU)?), + )])); + } + + let relation = plane_sphere_relation(plane.origin, plane.z, cylinder.origin, radius)?; + if relation == Sign::Positive { + return Ok(GeometryResult::Empty); + } + let base = add(cylinder.origin, scale(plane.z, -offset)); + if relation == Sign::Zero { + return Ok(GeometryResult::Curves(vec![( + CurveGeometry::Line { + origin: base, + direction: cylinder.z, + }, + None, + )])); + } + let lateral = unit(cross(cylinder.z, plane.z))?; + let half_separation_squared = (radius - offset.abs()) * (radius + offset.abs()); + if half_separation_squared <= 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "plane/cylinder generator separation cancellation".into(), + )); + } + let half_separation = half_separation_squared.sqrt(); + Ok(GeometryResult::Curves( + [-1.0, 1.0] + .into_iter() + .map(|sense| { + ( + CurveGeometry::Line { + origin: add(base, scale(lateral, sense * half_separation)), + direction: cylinder.z, + }, + None, + ) + }) + .collect(), + )) +} + +pub(super) fn plane_cone( + plane: Frame3, + cone: Frame3, + semi_angle: f64, +) -> Result { + let axial = dot(plane.z, cone.z); + let tilt = norm(cross(plane.z, cone.z)); + if tilt == 0.0 { + let height = dot(sub(plane.origin, cone.origin), cone.z); + if height < 0.0 { + return Ok(GeometryResult::Empty); + } + if height == 0.0 { + return Ok(GeometryResult::Contact(cone.origin)); + } + let radius = height * semi_angle.tan(); + if !radius.is_finite() || radius <= 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "plane/cone circle exceeds numerical range".into(), + )); + } + return circle(add(cone.origin, scale(cone.z, height)), plane.z, radius); + } + if axial == 0.0 { + return Err(GeometryError::CoverageGap { + families: ["plane".into(), "cone".into()], + }); + } + let height = dot(sub(plane.origin, cone.origin), plane.z) / axial; + let slope = semi_angle.tan(); + let coefficient = axial * axial - slope * slope * tilt * tilt; + if coefficient <= 0.0 { + return Err(GeometryError::CoverageGap { + families: ["plane".into(), "cone".into()], + }); + } + if height < 0.0 { + return Ok(GeometryResult::Empty); + } + if height == 0.0 { + return Ok(GeometryResult::Contact(cone.origin)); + } + let cross_axis = unit(cross(plane.z, cone.z))?; + let down_slope = unit(cross(plane.z, cross_axis))?; + let center_shift = -slope * slope * height * tilt / coefficient; + let center = add( + add(cone.origin, scale(cone.z, height)), + scale(down_slope, center_shift), + ); + let minor_radius = slope * height.abs() * axial.abs() / coefficient.sqrt(); + let major_radius = minor_radius / coefficient.sqrt(); + if !major_radius.is_finite() || !minor_radius.is_finite() || minor_radius <= 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "plane/cone ellipse exceeds numerical range".into(), + )); + } + let frame = Frame3::from_axis(center, plane.z, down_slope)?; + Ok(GeometryResult::Curves(vec![( + CurveGeometry::Ellipse { + frame, + major_radius, + minor_radius, + }, + Some(Interval::new(0.0, std::f64::consts::TAU)?), + )])) +} + +pub(super) fn plane_torus( + plane: Frame3, + torus: Frame3, + major_radius: f64, + minor_radius: f64, +) -> Result { + let parallel = norm(cross(plane.z, torus.z)) == 0.0; + if parallel { + let height = dot(sub(plane.origin, torus.origin), torus.z); + if height.abs() > minor_radius { + return Ok(GeometryResult::Empty); + } + let center = add(torus.origin, scale(torus.z, height)); + if height.abs() == minor_radius { + return axial_circles(plane.z, [(center, major_radius)]); + } + let radial = ((minor_radius - height.abs()) * (minor_radius + height.abs())).sqrt(); + return axial_circles( + plane.z, + [ + (center, major_radius - radial), + (center, major_radius + radial), + ], + ); + } + if dot(plane.z, torus.z) == 0.0 && dot(sub(torus.origin, plane.origin), plane.z) == 0.0 { + let radial = unit(cross(plane.z, torus.z))?; + return axial_circles( + plane.z, + [ + (add(torus.origin, scale(radial, major_radius)), minor_radius), + ( + add(torus.origin, scale(radial, -major_radius)), + minor_radius, + ), + ], + ); + } + Err(GeometryError::CoverageGap { + families: ["plane".into(), "torus".into()], + }) +} diff --git a/main/opengeometry/src/intersection/ssi/tests/closed_form.rs b/main/opengeometry/src/intersection/ssi/tests/closed_form.rs new file mode 100644 index 0000000..58ed3f2 --- /dev/null +++ b/main/opengeometry/src/intersection/ssi/tests/closed_form.rs @@ -0,0 +1,384 @@ +use crate::brep::{ + fine_accuracy, Curve, CurveGeometry, Frame3, GeometryError, GeometryStore, Surface, + SurfaceGeometry, +}; +use crate::intersection::ssi::intersect_surfaces; +use crate::math::{norm, sub, Point3}; + +#[test] +fn plane_sphere_emits_shared_curve_and_both_pcurves() { + let mut s = GeometryStore::new(); + s.surfaces.push(SurfaceGeometry::Plane { + frame: Frame3 { + origin: [0.0, 0.0, 0.5], + ..Frame3::IDENTITY + }, + }); + s.surfaces.push(SurfaceGeometry::Sphere { + frame: Frame3::IDENTITY, + radius: 1.0, + }); + let r = intersect_surfaces(&mut s, 0, 1, fine_accuracy()).unwrap(); + assert_eq!(r.curves.len(), 1); + let curve = &r.curves[0]; + for i in 0..=32 { + let t = std::f64::consts::TAU * i as f64 / 32.0; + let p = s.curve(curve.curve).unwrap().point_at(t).unwrap(); + for j in 0..2 { + let uv = s.pcurve_at(curve.pcurves[j], t).unwrap(); + assert!(norm(sub(s.surfaces[j].point_at(uv).unwrap(), p)) < 1e-10); + } + } +} + +#[test] +fn sphere_contacts_and_near_misses_do_not_become_polylines() { + for (distance, curves, contacts) in [ + (1.0, 1, 0), + (2.0, 0, 1), + (2.0 + 1e-9, 0, 0), + (2.0 - 1e-9, 1, 0), + ] { + let mut s = GeometryStore::new(); + s.surfaces = vec![ + SurfaceGeometry::Sphere { + frame: Frame3::IDENTITY, + radius: 1.0, + }, + SurfaceGeometry::Sphere { + frame: Frame3 { + origin: [distance, 0.0, 0.0], + ..Frame3::IDENTITY + }, + radius: 1.0, + }, + ]; + let r = intersect_surfaces(&mut s, 0, 1, fine_accuracy()).unwrap(); + assert_eq!(r.curves.len(), curves); + assert_eq!(r.contacts.len(), contacts); + } +} + +#[test] +fn plane_cylinder_sections_cover_circle_ellipse_generators_and_tangency() { + let cylinder = SurfaceGeometry::Cylinder { + frame: Frame3::IDENTITY, + radius: 1.0, + }; + let section = |frame: Frame3| { + let mut store = GeometryStore::new(); + store.surfaces = vec![SurfaceGeometry::Plane { frame }, cylinder.clone()]; + let result = intersect_surfaces(&mut store, 0, 1, fine_accuracy()).unwrap(); + (store, result) + }; + + let (circle_store, circle_result) = section(Frame3::IDENTITY); + assert_eq!(circle_result.curves.len(), 1); + assert!(matches!( + circle_store.curves[circle_result.curves[0].curve as usize], + CurveGeometry::Circle { .. } + )); + + let diagonal = 0.5_f64.sqrt(); + let oblique = Frame3::from_axis([0.0; 3], [0.0, diagonal, diagonal], [1.0, 0.0, 0.0]).unwrap(); + let (ellipse_store, ellipse_result) = section(oblique); + let CurveGeometry::Ellipse { + major_radius, + minor_radius, + .. + } = ellipse_store.curves[ellipse_result.curves[0].curve as usize] + else { + panic!("oblique section must be an ellipse") + }; + assert!((major_radius - 2.0_f64.sqrt()).abs() < 1e-12); + assert!((minor_radius - 1.0).abs() < 1e-12); + for i in 0..=32 { + let t = std::f64::consts::TAU * i as f64 / 32.0; + let point = ellipse_store + .curve(ellipse_result.curves[0].curve) + .unwrap() + .point_at(t) + .unwrap(); + for side in 0..2 { + let uv = ellipse_store + .pcurve_at(ellipse_result.curves[0].pcurves[side], t) + .unwrap(); + assert!( + norm(sub( + ellipse_store.surfaces[side].point_at(uv).unwrap(), + point + )) < 1e-10 + ); + } + } + + let parallel = |x| Frame3::from_axis([x, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]).unwrap(); + assert_eq!(section(parallel(0.0)).1.curves.len(), 2); + assert_eq!(section(parallel(1.0)).1.curves.len(), 1); + let outside = section(parallel(1.0 + 1e-9)).1; + assert!(outside.curves.is_empty() && outside.contacts.is_empty()); +} + +#[test] +fn parallel_cylinders_cover_two_generators_tangency_coaxial_and_gap() { + let intersect = |origin: Point3, radius: f64, axis: Point3| { + let mut store = GeometryStore::new(); + store.surfaces = vec![ + SurfaceGeometry::Cylinder { + frame: Frame3::IDENTITY, + radius: 1.0, + }, + SurfaceGeometry::Cylinder { + frame: Frame3::from_axis(origin, axis, [1.0, 0.0, 0.0]).unwrap(), + radius, + }, + ]; + let result = intersect_surfaces(&mut store, 0, 1, fine_accuracy()); + (store, result) + }; + + let (store, Ok(two)) = intersect([1.0, 0.0, 3.0], 1.0, [0.0, 0.0, -1.0]) else { + panic!("parallel cylinders should intersect") + }; + assert_eq!(two.curves.len(), 2); + for branch in two.curves { + for t in [-3.0, 0.0, 4.0] { + let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); + for side in 0..2 { + let uv = store.pcurve_at(branch.pcurves[side], t).unwrap(); + assert!(norm(sub(store.surfaces[side].point_at(uv).unwrap(), point)) < 1e-10); + } + } + } + + assert_eq!( + intersect([2.0, 0.0, 0.0], 1.0, [0.0, 0.0, 1.0]) + .1 + .unwrap() + .curves + .len(), + 1 + ); + assert!( + intersect([0.0, 0.0, 2.0], 1.0, [0.0, 0.0, -1.0]) + .1 + .unwrap() + .coincident + ); + assert!(intersect([3.0, 0.0, 0.0], 1.0, [0.0, 0.0, 1.0]) + .1 + .unwrap() + .curves + .is_empty()); + assert!(matches!( + intersect([1.0, 0.0, 0.0], 1.0, [0.0, 1.0, 1.0]).1, + Err(GeometryError::CoverageGap { .. }) + )); +} + +#[test] +fn coaxial_cylinder_cone_and_cone_pairs_are_classified_without_faceting() { + let mut cylinder_cone = GeometryStore::new(); + cylinder_cone.surfaces = vec![ + SurfaceGeometry::Cylinder { + frame: Frame3 { + origin: [0.0, 0.0, -7.0], + ..Frame3::IDENTITY + }, + radius: 2.0, + }, + SurfaceGeometry::Cone { + frame: Frame3::IDENTITY, + semi_angle: std::f64::consts::FRAC_PI_4, + }, + ]; + let section = intersect_surfaces(&mut cylinder_cone, 0, 1, fine_accuracy()).unwrap(); + assert_eq!(section.curves.len(), 1); + let CurveGeometry::Circle { frame, radius } = + cylinder_cone.curves[section.curves[0].curve as usize] + else { + panic!("coaxial cylinder/cone intersection must be a circle") + }; + assert!((radius - 2.0).abs() < 1e-12); + assert!(norm(sub(frame.origin, [0.0, 0.0, 2.0])) < 1e-12); + + let cone = |origin, axis, semi_angle| SurfaceGeometry::Cone { + frame: Frame3::from_axis(origin, axis, [1.0, 0.0, 0.0]).unwrap(), + semi_angle, + }; + let intersect = |a: SurfaceGeometry, b: SurfaceGeometry| { + let mut store = GeometryStore::new(); + store.surfaces = vec![a, b]; + intersect_surfaces(&mut store, 0, 1, fine_accuracy()).unwrap() + }; + assert!( + intersect( + cone([0.0; 3], [0.0, 0.0, 1.0], 0.4), + cone([0.0; 3], [0.0, 0.0, 1.0], 0.4), + ) + .coincident + ); + let apex_contact = intersect( + cone([0.0; 3], [0.0, 0.0, 1.0], 0.3), + cone([0.0; 3], [0.0, 0.0, 1.0], 0.6), + ); + assert_eq!(apex_contact.contacts, vec![[0.0; 3]]); + let opposed = intersect( + cone([0.0; 3], [0.0, 0.0, 1.0], 0.4), + cone([0.0, 0.0, 4.0], [0.0, 0.0, -1.0], 0.4), + ); + assert_eq!(opposed.curves.len(), 1); +} + +#[test] +fn perpendicular_plane_cone_sections_keep_circle_and_apex_contact() { + let section = |height: f64| { + let mut store = GeometryStore::new(); + store.surfaces = vec![ + SurfaceGeometry::Plane { + frame: Frame3 { + origin: [0.0, 0.0, height], + ..Frame3::IDENTITY + }, + }, + SurfaceGeometry::Cone { + frame: Frame3::IDENTITY, + semi_angle: std::f64::consts::FRAC_PI_4, + }, + ]; + let result = intersect_surfaces(&mut store, 0, 1, fine_accuracy()).unwrap(); + (store, result) + }; + let (store, positive) = section(2.0); + let CurveGeometry::Circle { radius, .. } = store.curves[positive.curves[0].curve as usize] + else { + panic!("perpendicular plane/cone section must be a circle") + }; + assert!((radius - 2.0).abs() < 1e-12); + assert_eq!(section(0.0).1.contacts, vec![[0.0; 3]]); + assert!(section(-1.0).1.curves.is_empty()); + + let mut oblique = GeometryStore::new(); + oblique.surfaces = vec![ + SurfaceGeometry::Plane { + frame: Frame3::from_axis([0.0, 0.0, 1.0], [0.0, 1.0, 1.0], [1.0, 0.0, 0.0]).unwrap(), + }, + SurfaceGeometry::Cone { + frame: Frame3::IDENTITY, + semi_angle: 0.4, + }, + ]; + let ellipse = intersect_surfaces(&mut oblique, 0, 1, fine_accuracy()).unwrap(); + assert!(matches!( + oblique.curves[ellipse.curves[0].curve as usize], + CurveGeometry::Ellipse { .. } + )); + for i in 0..=32 { + let t = std::f64::consts::TAU * i as f64 / 32.0; + let point = oblique + .curve(ellipse.curves[0].curve) + .unwrap() + .point_at(t) + .unwrap(); + for side in 0..2 { + let uv = oblique + .pcurve_at(ellipse.curves[0].pcurves[side], t) + .unwrap(); + assert!(norm(sub(oblique.surfaces[side].point_at(uv).unwrap(), point)) < 1e-10); + } + } + + let mut hyperbolic = GeometryStore::new(); + hyperbolic.surfaces = vec![ + SurfaceGeometry::Plane { + frame: Frame3::from_axis([0.0, 0.0, 1.0], [0.0, 1.0, 0.1], [1.0, 0.0, 0.0]).unwrap(), + }, + SurfaceGeometry::Cone { + frame: Frame3::IDENTITY, + semi_angle: 0.4, + }, + ]; + assert!(matches!( + intersect_surfaces(&mut hyperbolic, 0, 1, fine_accuracy()), + Err(GeometryError::CoverageGap { .. }) + )); +} + +#[test] +fn coaxial_sphere_cone_classifies_two_single_and_empty_sections() { + let intersect = |center_height: f64, radius: f64| { + let mut store = GeometryStore::new(); + store.surfaces = vec![ + SurfaceGeometry::Sphere { + frame: Frame3 { + origin: [0.0, 0.0, center_height], + ..Frame3::IDENTITY + }, + radius, + }, + SurfaceGeometry::Cone { + frame: Frame3::IDENTITY, + semi_angle: std::f64::consts::FRAC_PI_4, + }, + ]; + let result = intersect_surfaces(&mut store, 0, 1, fine_accuracy()); + (store, result) + }; + let (store, Ok(two)) = intersect(3.0, 2.5) else { + panic!("coaxial sphere/cone should intersect twice") + }; + assert_eq!(two.curves.len(), 2); + for branch in two.curves { + for i in 0..=16 { + let t = std::f64::consts::TAU * i as f64 / 16.0; + let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); + for side in 0..2 { + let uv = store.pcurve_at(branch.pcurves[side], t).unwrap(); + assert!(norm(sub(store.surfaces[side].point_at(uv).unwrap(), point)) < 1e-10); + } + } + } + assert_eq!(intersect(0.0, 1.0).1.unwrap().curves.len(), 1); + assert!(intersect(3.0, 1.0).1.unwrap().curves.is_empty()); + let (_, apex) = intersect(3.0, 3.0); + let apex = apex.unwrap(); + assert_eq!(apex.curves.len(), 1); + assert_eq!(apex.contacts, vec![[0.0; 3]]); +} + +#[test] +fn canonical_fast_dispatch_covers_all_fifteen_unordered_family_pairs() { + let families = vec![ + SurfaceGeometry::Plane { + frame: Frame3::IDENTITY, + }, + SurfaceGeometry::Sphere { + frame: Frame3::IDENTITY, + radius: 1.0, + }, + SurfaceGeometry::Cylinder { + frame: Frame3::IDENTITY, + radius: 1.0, + }, + SurfaceGeometry::Cone { + frame: Frame3::IDENTITY, + semi_angle: 0.4, + }, + SurfaceGeometry::Torus { + frame: Frame3::IDENTITY, + major_radius: 3.0, + minor_radius: 0.5, + }, + ]; + let mut count = 0; + for i in 0..families.len() { + for j in i..families.len() { + let mut store = GeometryStore::new(); + store.surfaces = vec![families[i].clone(), families[j].clone()]; + intersect_surfaces(&mut store, 0, 1, fine_accuracy()) + .unwrap_or_else(|error| panic!("family pair {i}/{j}: {error}")); + count += 1; + } + } + assert_eq!(count, 15); +} diff --git a/main/opengeometry/src/intersection/ssi/tests/fallback.rs b/main/opengeometry/src/intersection/ssi/tests/fallback.rs new file mode 100644 index 0000000..5ee6a13 --- /dev/null +++ b/main/opengeometry/src/intersection/ssi/tests/fallback.rs @@ -0,0 +1,251 @@ +use crate::brep::{ + fine_accuracy, Curve, CurveGeometry, Frame3, GeometryStore, Surface, SurfaceGeometry, +}; +use crate::intersection::ssi::intersect_surfaces; +use crate::intersection::ssi_result::SsiResult; +use crate::math::{norm, sub, Point3}; + +fn assert_numerical_branches(store: &GeometryStore, result: &SsiResult) { + assert!(!result.curves.is_empty()); + for branch in &result.curves { + let CurveGeometry::Intersection { definition } = store.curves[branch.curve as usize] else { + panic!("expected a numerical intersection curve") + }; + let support_ids = store.intersections[definition as usize].surfaces; + let domain = branch + .domain + .expect("numerical branch needs a finite domain"); + for i in 0..=8 { + let t = domain.lo + domain.width() * i as f64 / 8.0; + let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); + for side in 0..2 { + let uv = store.pcurve_at(branch.pcurves[side], t).unwrap(); + assert!( + norm(sub( + store.surfaces[support_ids[side] as usize] + .point_at(uv) + .unwrap(), + point + )) <= fine_accuracy().intersection + ); + } + } + } +} + +#[test] +fn plane_plane_and_universal_torus_intersections_are_distinct() { + let mut s = GeometryStore::new(); + s.surfaces = vec![ + SurfaceGeometry::Plane { + frame: Frame3::IDENTITY, + }, + SurfaceGeometry::Plane { + frame: Frame3::from_axis([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]).unwrap(), + }, + SurfaceGeometry::Torus { + frame: Frame3::IDENTITY, + major_radius: 2.0, + minor_radius: 0.5, + }, + SurfaceGeometry::Torus { + frame: Frame3 { + origin: [0.5, 0.0, 0.0], + ..Frame3::IDENTITY + }, + major_radius: 2.0, + minor_radius: 0.5, + }, + ]; + let r = intersect_surfaces(&mut s, 0, 1, fine_accuracy()).unwrap(); + assert!(matches!( + s.curves[r.curves[0].curve as usize], + CurveGeometry::Line { .. } + )); + let numerical = intersect_surfaces(&mut s, 2, 3, fine_accuracy()).unwrap(); + assert_numerical_branches(&s, &numerical); +} + +#[test] +fn coaxial_sphere_cylinder_has_two_circles_tangent_circle_or_empty() { + let intersect = |sphere_origin: Point3, cylinder_origin: Point3, cylinder_radius: f64| { + let mut store = GeometryStore::new(); + store.surfaces = vec![ + SurfaceGeometry::Sphere { + frame: Frame3 { + origin: sphere_origin, + ..Frame3::IDENTITY + }, + radius: 2.0, + }, + SurfaceGeometry::Cylinder { + frame: Frame3 { + origin: cylinder_origin, + ..Frame3::IDENTITY + }, + radius: cylinder_radius, + }, + ]; + let result = intersect_surfaces(&mut store, 0, 1, fine_accuracy()); + (store, result) + }; + let (store, Ok(two)) = intersect([0.0, 0.0, 3.0], [0.0; 3], 1.0) else { + panic!("coaxial sphere/cylinder should intersect") + }; + assert_eq!(two.curves.len(), 2); + for branch in two.curves { + for i in 0..=16 { + let t = std::f64::consts::TAU * i as f64 / 16.0; + let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); + for side in 0..2 { + let uv = store.pcurve_at(branch.pcurves[side], t).unwrap(); + assert!(norm(sub(store.surfaces[side].point_at(uv).unwrap(), point)) < 1e-10); + } + } + } + assert_eq!( + intersect([0.0; 3], [0.0, 0.0, 9.0], 2.0) + .1 + .unwrap() + .curves + .len(), + 1 + ); + assert!(intersect([0.0; 3], [0.0; 3], 3.0) + .1 + .unwrap() + .curves + .is_empty()); + let (store, result) = intersect([0.1, 0.0, 0.0], [0.0; 3], 1.0); + assert_numerical_branches(&store, &result.unwrap()); +} + +#[test] +fn torus_special_sections_cover_plane_sphere_cylinder_and_cone() { + let torus = SurfaceGeometry::Torus { + frame: Frame3::IDENTITY, + major_radius: 3.0, + minor_radius: 1.0, + }; + let assert_curves = |other: SurfaceGeometry, expected: usize| { + let mut store = GeometryStore::new(); + store.surfaces = vec![other, torus.clone()]; + let result = intersect_surfaces(&mut store, 0, 1, fine_accuracy()).unwrap(); + assert_eq!(result.curves.len(), expected); + for branch in &result.curves { + assert!(matches!( + store.curves[branch.curve as usize], + CurveGeometry::Circle { .. } + )); + for i in 0..=16 { + let t = std::f64::consts::TAU * i as f64 / 16.0; + let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); + for side in 0..2 { + let uv = store.pcurve_at(branch.pcurves[side], t).unwrap(); + assert!(norm(sub(store.surfaces[side].point_at(uv).unwrap(), point)) < 1e-10); + } + } + } + store + }; + + let horizontal = assert_curves( + SurfaceGeometry::Plane { + frame: Frame3::IDENTITY, + }, + 2, + ); + let mut radii: Vec<_> = horizontal + .curves + .iter() + .filter_map(|curve| match curve { + CurveGeometry::Circle { radius, .. } => Some(*radius), + _ => None, + }) + .collect(); + radii.sort_by(f64::total_cmp); + assert_eq!(radii, vec![2.0, 4.0]); + assert_curves( + SurfaceGeometry::Plane { + frame: Frame3 { + origin: [0.0, 0.0, 1.0], + ..Frame3::IDENTITY + }, + }, + 1, + ); + assert_curves( + SurfaceGeometry::Plane { + frame: Frame3::from_axis([0.0; 3], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]).unwrap(), + }, + 2, + ); + assert_curves( + SurfaceGeometry::Sphere { + frame: Frame3::IDENTITY, + radius: 10.0_f64.sqrt(), + }, + 2, + ); + assert_curves( + SurfaceGeometry::Cylinder { + frame: Frame3::IDENTITY, + radius: 3.0, + }, + 2, + ); + assert_curves( + SurfaceGeometry::Cone { + frame: Frame3 { + origin: [0.0, 0.0, -3.0], + ..Frame3::IDENTITY + }, + semi_angle: std::f64::consts::FRAC_PI_4, + }, + 2, + ); + assert_curves( + SurfaceGeometry::Torus { + frame: Frame3 { + origin: [0.0, 0.0, 1.0], + ..Frame3::IDENTITY + }, + major_radius: 3.0, + minor_radius: 1.0, + }, + 2, + ); + assert_curves( + SurfaceGeometry::Torus { + frame: Frame3 { + origin: [0.0, 0.0, 2.0], + ..Frame3::IDENTITY + }, + major_radius: 3.0, + minor_radius: 1.0, + }, + 1, + ); + + let mut coincident = GeometryStore::new(); + coincident.surfaces = vec![torus.clone(), torus.clone()]; + assert!( + intersect_surfaces(&mut coincident, 0, 1, fine_accuracy()) + .unwrap() + .coincident + ); + let mut skew = GeometryStore::new(); + skew.surfaces = vec![ + torus, + SurfaceGeometry::Torus { + frame: Frame3 { + origin: [0.5, 0.0, 0.0], + ..Frame3::IDENTITY + }, + major_radius: 3.0, + minor_radius: 1.0, + }, + ]; + let numerical = intersect_surfaces(&mut skew, 0, 1, fine_accuracy()).unwrap(); + assert_numerical_branches(&skew, &numerical); +} diff --git a/main/opengeometry/src/intersection/ssi/tests/mod.rs b/main/opengeometry/src/intersection/ssi/tests/mod.rs new file mode 100644 index 0000000..c3063c5 --- /dev/null +++ b/main/opengeometry/src/intersection/ssi/tests/mod.rs @@ -0,0 +1,2 @@ +mod closed_form; +mod fallback; diff --git a/main/opengeometry/src/intersection/ssi_result.rs b/main/opengeometry/src/intersection/ssi_result.rs new file mode 100644 index 0000000..8e100bd --- /dev/null +++ b/main/opengeometry/src/intersection/ssi_result.rs @@ -0,0 +1,27 @@ +use crate::brep::CurveGeometry; +use crate::math::{Interval, Point3}; + +#[derive(Clone, Debug)] +pub(crate) struct SsiCurve { + pub(crate) curve: u32, + pub(super) pcurves: [u32; 2], + pub(super) domain: Option, +} + +#[derive(Clone, Debug, Default)] +pub(crate) struct SsiResult { + pub(crate) curves: Vec, + pub(crate) contacts: Vec, + pub(super) coincident: bool, +} + +pub(super) enum GeometryResult { + Empty, + Curves(Vec<(CurveGeometry, Option)>), + CurvesAndContacts { + curves: Vec<(CurveGeometry, Option)>, + contacts: Vec, + }, + Contact(Point3), + Coincident, +} diff --git a/main/opengeometry/src/intersection/universal_ssi/branches.rs b/main/opengeometry/src/intersection/universal_ssi/branches.rs new file mode 100644 index 0000000..b60d860 --- /dev/null +++ b/main/opengeometry/src/intersection/universal_ssi/branches.rs @@ -0,0 +1,195 @@ +use super::cells::{Root, Segment}; +use super::correction::pseudo_arclength_correct; +use crate::brep::{ + unit, Accuracy, CurveGeometry, GeometryError, GeometryStore, IntersectionDefinition, + IntersectionSide, PcurveGeometry, Surface, SurfaceGeometry, TraceAnchor, UVBox, UV, +}; +use crate::intersection::ssi_result::SsiCurve; +use crate::math::{cross, norm, sub, Interval, Point3}; + +pub(super) fn segment_endpoint_tangent( + source: &SurfaceGeometry, + target: &SurfaceGeometry, + segment: Segment, + end: usize, +) -> Result { + let root = segment.ends[end]; + let transverse = cross(source.normal_at(root.uv_a)?, target.normal_at(root.uv_b)?); + if norm(transverse) > 1.0e-8 { + unit(transverse) + } else { + unit(sub(segment.ends[1 - end].point, root.point)).map_err(|_| { + GeometryError::UnresolvedIntersection( + "universal SSI tangent segment has no resolvable direction".into(), + ) + }) + } +} + +pub(super) fn normalized_key( + uv: UV, + domain: UVBox, + periods: [Option; 2], + quantum: [f64; 2], +) -> [i64; 2] { + std::array::from_fn(|axis| { + let value = periods[axis].map_or(uv[axis], |period| { + let lifted = (uv[axis] - domain[axis].lo).rem_euclid(period); + if (period - lifted).abs() <= quantum[axis] { + domain[axis].lo + } else { + domain[axis].lo + lifted + } + }); + (value / quantum[axis]).round() as i64 + }) +} + +pub(super) fn branch_definition( + mut roots: Vec, + mut source_domains: Vec, + surfaces: [u32; 2], + store: &mut GeometryStore, + accuracy: Accuracy, +) -> Result { + if source_domains.len() + 1 != roots.len() { + return Err(GeometryError::InvalidGeometry( + "universal SSI branch domains do not match its roots".into(), + )); + } + let source = store.surface(surfaces[0])?.clone(); + let target = store.surface(surfaces[1])?.clone(); + roots = roots + .into_iter() + .map(|root| pseudo_arclength_correct(&source, &target, root, accuracy.intersection)) + .collect::, _>>()?; + let periods_a = store.surface(surfaces[0])?.charts()[0].periods; + let periods_b = store.surface(surfaces[1])?.charts()[0].periods; + unwrap_root_angles(&mut roots, periods_a, periods_b); + for (index, domain) in source_domains.iter_mut().enumerate() { + for axis in 0..2 { + if let Some(period) = periods_a[axis] { + let branch_midpoint = 0.5 * (roots[index].uv_a[axis] + roots[index + 1].uv_a[axis]); + let shift = ((branch_midpoint - domain[axis].midpoint()) / period).round() * period; + domain[axis] = Interval::new(domain[axis].lo + shift, domain[axis].hi + shift)?; + } + } + } + let mut parameter = 0.0; + let mut anchors = Vec::with_capacity(roots.len()); + for (index, root) in roots.iter().enumerate() { + if index > 0 { + parameter += norm(sub(root.point, roots[index - 1].point)); + } + anchors.push(TraceAnchor { + parameter, + point: root.point, + uv_a: root.uv_a, + uv_b: root.uv_b, + }); + } + if anchors.len() < 2 || parameter <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "universal SSI branch is below geometric resolution".into(), + )); + } + let uv_tubes = branch_uv_tubes(&anchors, &source_domains, accuracy)?; + let definition = store.intersections.len() as u32; + store.intersections.push(IntersectionDefinition { + surfaces, + anchors, + uv_tubes, + residual_tolerance: accuracy.intersection, + }); + let curve = store.curves.len() as u32; + store + .curves + .push(CurveGeometry::Intersection { definition }); + let pcurve_a = store.pcurves.len() as u32; + store.pcurves.push(PcurveGeometry::IntersectionSide { + definition, + side: IntersectionSide::A, + }); + let pcurve_b = store.pcurves.len() as u32; + store.pcurves.push(PcurveGeometry::IntersectionSide { + definition, + side: IntersectionSide::B, + }); + let domain = Interval::new(0.0, parameter)?; + store.intersections[definition as usize].validate(store)?; + Ok(SsiCurve { + curve, + pcurves: [pcurve_a, pcurve_b], + domain: Some(domain), + }) +} + +fn unwrap_root_angles( + roots: &mut [Root], + periods_a: [Option; 2], + periods_b: [Option; 2], +) { + for index in 1..roots.len() { + for axis in 0..2 { + roots[index].uv_a[axis] = unwrap_angle( + roots[index].uv_a[axis], + roots[index - 1].uv_a[axis], + periods_a[axis], + ); + roots[index].uv_b[axis] = unwrap_angle( + roots[index].uv_b[axis], + roots[index - 1].uv_b[axis], + periods_b[axis], + ); + } + } +} + +fn unwrap_angle(value: f64, previous: f64, period: Option) -> f64 { + period.map_or(value, |period| { + value + ((previous - value) / period).round() * period + }) +} + +fn branch_uv_tubes( + anchors: &[TraceAnchor], + source_domains: &[UVBox], + accuracy: Accuracy, +) -> Result, GeometryError> { + let mut uv_tubes = Vec::with_capacity(anchors.len() - 1); + for (index, pair) in anchors.windows(2).enumerate() { + let coordinates = [ + [pair[0].uv_a[0], pair[1].uv_a[0]], + [pair[0].uv_a[1], pair[1].uv_a[1]], + [pair[0].uv_b[0], pair[1].uv_b[0]], + [pair[0].uv_b[1], pair[1].uv_b[1]], + ]; + let mut intervals = coordinates.map(|values| { + let padding = (values[1] - values[0]).abs().max(accuracy.intersection) * 2.0; + Interval::new( + values[0].min(values[1]) - padding, + values[0].max(values[1]) + padding, + ) + }); + for axis in 0..2 { + let domain = source_domains[index][axis]; + let padding = + domain.width() * 0.05 + 16.0 * f64::EPSILON * domain.lo.abs().max(domain.hi.abs()); + intervals[axis] = Interval::new( + domain + .lo + .min(coordinates[axis][0]) + .min(coordinates[axis][1]) + - padding, + domain + .hi + .max(coordinates[axis][0]) + .max(coordinates[axis][1]) + + padding, + ); + } + let [u0, v0, u1, v1] = intervals; + uv_tubes.push([u0?, v0?, u1?, v1?]); + } + Ok(uv_tubes) +} diff --git a/main/opengeometry/src/intersection/universal_ssi/cells.rs b/main/opengeometry/src/intersection/universal_ssi/cells.rs new file mode 100644 index 0000000..41691e1 --- /dev/null +++ b/main/opengeometry/src/intersection/universal_ssi/cells.rs @@ -0,0 +1,367 @@ +use super::implicit_bounds::implicit_value; +use crate::brep::{GeometryError, Surface, SurfaceGeometry, UVBox, UV}; +use crate::math::{add, cross, norm, scale, sub, Point3}; + +#[derive(Clone, Copy)] +pub(super) struct Root { + pub(super) uv_a: UV, + pub(super) uv_b: UV, + pub(super) point: Point3, +} + +#[derive(Clone, Copy)] +pub(super) struct Segment { + pub(super) ends: [Root; 2], + pub(super) domain: UVBox, +} + +struct AxisSamples { + h: [f64; 2], + center: f64, + axis_values: Vec<[f64; 2]>, +} + +pub(super) enum StationaryEvent { + Contact(Point3), + InteriorCritical, +} + +pub(super) fn cell_segments( + source: &SurfaceGeometry, + target: &SurfaceGeometry, + domain: UVBox, + tolerance: f64, +) -> Result, GeometryError> { + let corners = [ + [domain[0].lo, domain[1].lo], + [domain[0].hi, domain[1].lo], + [domain[0].hi, domain[1].hi], + [domain[0].lo, domain[1].hi], + ]; + let mut values = [0.0; 4]; + for (index, uv) in corners.into_iter().enumerate() { + values[index] = implicit_value(target, source.point_at(uv)?)?; + } + let edge_indices = [[0, 1], [1, 2], [2, 3], [3, 0]]; + let mut roots = Vec::new(); + for [a, b] in edge_indices { + if let Some(root) = edge_root( + source, + target, + [corners[a], corners[b]], + [values[a], values[b]], + tolerance, + )? { + if roots + .iter() + .all(|existing: &Root| norm(sub(existing.point, root.point)) > tolerance) + { + roots.push(root); + } + } + } + Ok(match roots.len() { + 0 => Vec::new(), + 1 => vec![Segment { + ends: [roots[0], roots[0]], + domain, + }], + 2 => vec![Segment { + ends: [roots[0], roots[1]], + domain, + }], + 3 => three_root_segments(&roots, domain)?, + 4 => four_root_segments(source, target, domain, values, &roots)?, + _ => { + return Err(GeometryError::UnresolvedIntersection(format!( + "universal SSI cell contains a singular or unresolved branch event ({} edge roots)", + roots.len() + ))) + } + }) +} + +fn edge_root( + source: &SurfaceGeometry, + target: &SurfaceGeometry, + uv: [UV; 2], + values: [f64; 2], + tolerance: f64, +) -> Result, GeometryError> { + if values[0] != 0.0 + && values[1] != 0.0 + && values[0].is_sign_negative() == values[1].is_sign_negative() + { + return Ok(None); + } + support_root( + source, target, uv[0], uv[1], values[0], values[1], tolerance, + ) + .map(Some) +} + +fn support_root( + source: &SurfaceGeometry, + target: &SurfaceGeometry, + left_uv: UV, + right_uv: UV, + left_value: f64, + right_value: f64, + tolerance: f64, +) -> Result { + let mut lo_uv = left_uv; + let mut hi_uv = right_uv; + let mut lo = left_value; + let mut hi = right_value; + let mut best = if lo.abs() <= hi.abs() { lo_uv } else { hi_uv }; + for _ in 0..80 { + let uv = [(lo_uv[0] + hi_uv[0]) * 0.5, (lo_uv[1] + hi_uv[1]) * 0.5]; + let point = source.point_at(uv)?; + let value = implicit_value(target, point)?; + if value.abs() < implicit_value(target, source.point_at(best)?)?.abs() { + best = uv; + } + let projected = target.project(point, None); + if let Ok(uv_b) = projected { + let target_point = target.point_at(uv_b)?; + if norm(sub(point, target_point)) <= tolerance * 0.01 { + return Ok(Root { + uv_a: uv, + uv_b, + point, + }); + } + } + if lo == 0.0 { + best = lo_uv; + break; + } + if hi == 0.0 { + best = hi_uv; + break; + } + if lo.is_sign_negative() == value.is_sign_negative() { + lo_uv = uv; + lo = value; + } else { + hi_uv = uv; + hi = value; + } + } + let point = source.point_at(best)?; + let uv_b = target.project(point, None).map_err(|_| { + GeometryError::UnresolvedIntersection( + "universal SSI root cannot be projected to its second support".into(), + ) + })?; + if norm(sub(point, target.point_at(uv_b)?)) > tolerance { + return Err(GeometryError::UnresolvedIntersection( + "universal SSI root residual exceeds the intersection budget".into(), + )); + } + Ok(Root { + uv_a: best, + uv_b, + point, + }) +} + +fn three_root_segments(roots: &[Root], domain: UVBox) -> Result, GeometryError> { + let mut pair = [0, 1]; + let mut separation = 0.0; + for first in 0..3 { + for second in (first + 1)..3 { + let candidate = norm(sub(roots[first].point, roots[second].point)); + if candidate > separation { + separation = candidate; + pair = [first, second]; + } + } + } + let isolated = (0..3).find(|index| !pair.contains(index)).ok_or_else(|| { + GeometryError::UnresolvedIntersection( + "universal SSI could not isolate a grid-vertex event".into(), + ) + })?; + Ok(vec![ + Segment { + ends: [roots[pair[0]], roots[pair[1]]], + domain, + }, + Segment { + ends: [roots[isolated], roots[isolated]], + domain, + }, + ]) +} + +fn four_root_segments( + source: &SurfaceGeometry, + target: &SurfaceGeometry, + domain: UVBox, + values: [f64; 4], + roots: &[Root], +) -> Result, GeometryError> { + let center = [domain[0].midpoint(), domain[1].midpoint()]; + let center_value = implicit_value(target, source.point_at(center)?)?; + let same_as_corner = center_value.is_sign_negative() == values[0].is_sign_negative(); + Ok(if same_as_corner { + vec![ + Segment { + ends: [roots[0], roots[3]], + domain, + }, + Segment { + ends: [roots[1], roots[2]], + domain, + }, + ] + } else { + vec![ + Segment { + ends: [roots[0], roots[1]], + domain, + }, + Segment { + ends: [roots[2], roots[3]], + domain, + }, + ] + }) +} + +pub(super) fn stationary_event( + source: &SurfaceGeometry, + target: &SurfaceGeometry, + domain: UVBox, + tolerance: f64, +) -> Result, GeometryError> { + let uv_a = [domain[0].midpoint(), domain[1].midpoint()]; + let value = |uv| implicit_value(target, source.point_at(uv)?); + let Some(uv_a) = critical_uv(source, &value, domain, uv_a)? else { + return Ok(None); + }; + if (0..2).any(|axis| { + uv_a[axis] < domain[axis].lo - tolerance || uv_a[axis] > domain[axis].hi + tolerance + }) { + return Ok(None); + } + let point_a = source.point_at(uv_a)?; + let uv_b = match target.project(point_a, None) { + Ok(uv) if target.parameter_in_domain(uv) => uv, + _ => return Ok(Some(StationaryEvent::InteriorCritical)), + }; + let point_b = target.point_at(uv_b)?; + if norm(sub(point_a, point_b)) > tolerance { + return Ok(Some(StationaryEvent::InteriorCritical)); + } + let normal_a = source.normal_at(uv_a)?; + let normal_b = target.normal_at(uv_b)?; + if norm(cross(normal_a, normal_b)) > 1.0e-5 { + return Ok(Some(StationaryEvent::InteriorCritical)); + } + let samples = axis_samples(&value, domain, uv_a)?; + let (diagonal, mixed) = second_derivatives(&value, uv_a, &samples)?; + let determinant = diagonal[0] * diagonal[1] - mixed * mixed; + let hessian_scale = diagonal[0] + .abs() + .max(diagonal[1].abs()) + .max(mixed.abs()) + .max(1.0); + if determinant <= 1024.0 * f64::EPSILON * hessian_scale * hessian_scale { + return Ok(Some(StationaryEvent::InteriorCritical)); + } + Ok(Some(StationaryEvent::Contact(scale( + add(point_a, point_b), + 0.5, + )))) +} + +fn critical_uv( + source: &SurfaceGeometry, + value: &impl Fn(UV) -> Result, + domain: UVBox, + mut uv_a: UV, +) -> Result, GeometryError> { + let mut converged = false; + for _ in 0..16 { + let samples = axis_samples(value, domain, uv_a)?; + let AxisSamples { h, axis_values, .. } = &samples; + let gradient = + [0, 1].map(|axis| (axis_values[axis][1] - axis_values[axis][0]) / (2.0 * h[axis])); + let (diagonal, mixed) = second_derivatives(value, uv_a, &samples)?; + let determinant = diagonal[0] * diagonal[1] - mixed * mixed; + if !determinant.is_finite() || determinant.abs() <= f64::EPSILON { + return Ok(None); + } + let delta = [ + (diagonal[1] * gradient[0] - mixed * gradient[1]) / determinant, + (diagonal[0] * gradient[1] - mixed * gradient[0]) / determinant, + ]; + let candidate = [uv_a[0] - delta[0], uv_a[1] - delta[1]]; + if (0..2).any(|axis| { + !candidate[axis].is_finite() + || candidate[axis] < domain[axis].lo + || candidate[axis] > domain[axis].hi + }) { + return Ok(None); + } + uv_a = candidate; + if delta[0].abs() <= h[0] * 1.0e-3 && delta[1].abs() <= h[1] * 1.0e-3 { + converged = true; + break; + } + } + if !converged || !source.parameter_in_domain(uv_a) { + return Ok(None); + } + Ok(Some(uv_a)) +} + +fn axis_samples( + value: &impl Fn(UV) -> Result, + domain: UVBox, + uv: UV, +) -> Result { + let h = [0, 1].map(|axis| { + (domain[axis].width() * 1.0e-3).max(f64::EPSILON.sqrt() * uv[axis].abs().max(1.0)) + }); + let center = value(uv)?; + let axis_values = [0, 1] + .map(|axis| { + let mut low = uv; + let mut high = uv; + low[axis] -= h[axis]; + high[axis] += h[axis]; + Ok([value(low)?, value(high)?]) + }) + .into_iter() + .collect::, GeometryError>>()?; + Ok(AxisSamples { + h, + center, + axis_values, + }) +} + +fn second_derivatives( + value: &impl Fn(UV) -> Result, + uv: UV, + samples: &AxisSamples, +) -> Result<([f64; 2], f64), GeometryError> { + let AxisSamples { + h, + center, + axis_values, + } = samples; + let diagonal = [0, 1].map(|axis| { + (axis_values[axis][0] - 2.0 * center + axis_values[axis][1]) / (h[axis] * h[axis]) + }); + let mixed_values = [[-1.0, -1.0], [-1.0, 1.0], [1.0, -1.0], [1.0, 1.0]] + .map(|offset| value([uv[0] + offset[0] * h[0], uv[1] + offset[1] * h[1]])) + .into_iter() + .collect::, GeometryError>>()?; + let mixed = (mixed_values[3] - mixed_values[2] - mixed_values[1] + mixed_values[0]) + / (4.0 * h[0] * h[1]); + Ok((diagonal, mixed)) +} diff --git a/main/opengeometry/src/intersection/universal_ssi/correction.rs b/main/opengeometry/src/intersection/universal_ssi/correction.rs new file mode 100644 index 0000000..46fa975 --- /dev/null +++ b/main/opengeometry/src/intersection/universal_ssi/correction.rs @@ -0,0 +1,82 @@ +use super::cells::Root; +use crate::brep::{unit, GeometryError, Surface, SurfaceGeometry}; +use crate::math::{add, cross, dot, norm, scale, solve, sub}; + +pub(super) fn pseudo_arclength_correct( + source: &SurfaceGeometry, + target: &SurfaceGeometry, + root: Root, + tolerance: f64, +) -> Result { + let predictor = root.point; + let normal_cross = cross(source.normal_at(root.uv_a)?, target.normal_at(root.uv_b)?); + if norm(normal_cross) <= 1.0e-8 { + let point_a = source.point_at(root.uv_a)?; + let point_b = target.point_at(root.uv_b)?; + if norm(sub(point_a, point_b)) <= tolerance { + return Ok(Root { + point: scale(add(point_a, point_b), 0.5), + ..root + }); + } + return Err(GeometryError::UnresolvedIntersection( + "universal SSI tangent branch root exceeds its support residual".into(), + )); + } + let tangent = unit(normal_cross)?; + let mut parameters = [root.uv_a[0], root.uv_a[1], root.uv_b[0], root.uv_b[1]]; + for _ in 0..12 { + let uv_a = [parameters[0], parameters[1]]; + let uv_b = [parameters[2], parameters[3]]; + let point_a = source.point_at(uv_a)?; + let point_b = target.point_at(uv_b)?; + let midpoint = scale(add(point_a, point_b), 0.5); + let residual = sub(point_a, point_b); + let constraint = dot(sub(midpoint, predictor), tangent); + if norm(residual) <= tolerance * 0.25 && constraint.abs() <= tolerance * 0.25 { + return Ok(Root { + uv_a, + uv_b, + point: midpoint, + }); + } + let jet_a = source.derivatives(uv_a)?; + let jet_b = target.derivatives(uv_b)?; + let matrix = [ + [jet_a.du[0], jet_a.dv[0], -jet_b.du[0], -jet_b.dv[0]], + [jet_a.du[1], jet_a.dv[1], -jet_b.du[1], -jet_b.dv[1]], + [jet_a.du[2], jet_a.dv[2], -jet_b.du[2], -jet_b.dv[2]], + [ + 0.5 * dot(jet_a.du, tangent), + 0.5 * dot(jet_a.dv, tangent), + 0.5 * dot(jet_b.du, tangent), + 0.5 * dot(jet_b.dv, tangent), + ], + ]; + let correction = solve( + matrix, + [-residual[0], -residual[1], -residual[2], -constraint], + ) + .map_err(|_| { + GeometryError::UnresolvedIntersection( + "universal SSI pseudo-arclength correction is singular".into(), + ) + })?; + if correction.minimum_scaled_pivot <= 256.0 * f64::EPSILON { + return Err(GeometryError::UnresolvedIntersection( + "universal SSI pseudo-arclength correction is ill-conditioned".into(), + )); + } + for (parameter, delta) in parameters.iter_mut().zip(correction.value) { + *parameter += delta; + } + if parameters.iter().any(|value| !value.is_finite()) { + return Err(GeometryError::UnresolvedIntersection( + "universal SSI pseudo-arclength correction left finite parameter space".into(), + )); + } + } + Err(GeometryError::UnresolvedIntersection( + "universal SSI pseudo-arclength correction iteration budget exhausted".into(), + )) +} diff --git a/main/opengeometry/src/intersection/universal_ssi/implicit_bounds.rs b/main/opengeometry/src/intersection/universal_ssi/implicit_bounds.rs new file mode 100644 index 0000000..c383eeb --- /dev/null +++ b/main/opengeometry/src/intersection/universal_ssi/implicit_bounds.rs @@ -0,0 +1,113 @@ +use crate::brep::{GeometryError, PatchBounds, SurfaceGeometry}; +use crate::math::{Interval, Point3}; + +fn local_intervals( + surface: &SurfaceGeometry, + bounds: PatchBounds, +) -> Result<[Interval; 3], GeometryError> { + let frame = surface.frame(); + let mut local = [Interval::point(0.0)?; 3]; + for (axis, direction) in [frame.x, frame.y, frame.z].into_iter().enumerate() { + let mut coordinate = Interval::point(0.0)?; + for world_axis in 0..3 { + coordinate = coordinate.add_interval( + bounds.axes[world_axis] + .sub_interval(Interval::point(frame.origin[world_axis])?)? + .mul_interval(Interval::point(direction[world_axis])?)?, + )?; + } + local[axis] = coordinate; + } + Ok(local) +} + +pub(super) fn implicit_interval( + surface: &SurfaceGeometry, + bounds: PatchBounds, +) -> Result { + let [x, y, z] = local_intervals(surface, bounds)?; + let x2 = x.square()?; + let y2 = y.square()?; + let z2 = z.square()?; + Ok(match surface { + SurfaceGeometry::Plane { .. } => z, + SurfaceGeometry::Sphere { radius, .. } => x2 + .add_interval(y2)? + .add_interval(z2)? + .sub_interval(Interval::point(radius * radius)?)?, + SurfaceGeometry::Cylinder { radius, .. } => x2 + .add_interval(y2)? + .sub_interval(Interval::point(radius * radius)?)?, + SurfaceGeometry::Cone { semi_angle, .. } => { + if z.hi < 0.0 { + return Interval::point(1.0).map_err(Into::into); + } + let slope = semi_angle.tan(); + x2.add_interval(y2)? + .sub_interval(z2.mul_interval(Interval::point(slope * slope)?)?)? + } + SurfaceGeometry::Torus { + major_radius, + minor_radius, + .. + } => { + let radial = x2.add_interval(y2)?; + Interval::new(radial.lo.max(0.0), radial.hi)? + .sqrt()? + .sub_interval(Interval::point(*major_radius)?)? + .square()? + .add_interval(z2)? + .sub_interval(Interval::point(minor_radius * minor_radius)?)? + } + }) +} + +pub(super) fn implicit_value( + surface: &SurfaceGeometry, + point: Point3, +) -> Result { + let [x, y, z] = surface.frame().local(point); + let value = match surface { + SurfaceGeometry::Plane { .. } => z, + SurfaceGeometry::Sphere { radius, .. } => { + x.mul_add(x, y.mul_add(y, z * z)) - radius * radius + } + SurfaceGeometry::Cylinder { radius, .. } => x.mul_add(x, y * y) - radius * radius, + SurfaceGeometry::Cone { semi_angle, .. } => { + if z < 0.0 { + return Ok(x.mul_add(x, y.mul_add(y, z * z)) + 1.0); + } + x.mul_add(x, y * y) - z * z * semi_angle.tan().powi(2) + } + SurfaceGeometry::Torus { + major_radius, + minor_radius, + .. + } => { + let radial = x.mul_add(x, y * y); + (radial.sqrt() - major_radius).powi(2) + z * z - minor_radius * minor_radius + } + }; + if value.is_finite() { + Ok(value) + } else { + Err(GeometryError::UnresolvedIntersection( + "universal SSI implicit evaluation exceeded numerical range".into(), + )) + } +} + +pub(super) fn surface_scale(surface: &SurfaceGeometry) -> f64 { + match surface { + SurfaceGeometry::Plane { .. } => 1.0, + SurfaceGeometry::Sphere { radius, .. } | SurfaceGeometry::Cylinder { radius, .. } => { + *radius + } + SurfaceGeometry::Cone { .. } => 1.0, + SurfaceGeometry::Torus { + major_radius, + minor_radius, + .. + } => major_radius + minor_radius, + } +} diff --git a/main/opengeometry/src/intersection/universal_ssi/mod.rs b/main/opengeometry/src/intersection/universal_ssi/mod.rs new file mode 100644 index 0000000..a0a6b5b --- /dev/null +++ b/main/opengeometry/src/intersection/universal_ssi/mod.rs @@ -0,0 +1,360 @@ +mod branches; +mod cells; +mod correction; +mod implicit_bounds; +mod subdivision; +#[cfg(test)] +mod tests; + +use super::ssi_result::SsiResult; +use crate::brep::{ + Accuracy, GeometryError, GeometryStore, PatchBounds, SsiBudget, Surface, SurfaceGeometry, UVBox, +}; +use crate::math::{dot, find, norm, sub, union, Interval, Point3}; +use branches::{branch_definition, normalized_key, segment_endpoint_tangent}; +use cells::{cell_segments, stationary_event, Root, Segment, StationaryEvent}; +use implicit_bounds::{implicit_interval, surface_scale}; +use std::collections::{HashMap, HashSet}; +use subdivision::{bounds_diameter, split}; + +pub(crate) fn intersect_patches( + store: &mut GeometryStore, + surfaces: [u32; 2], + domains: [UVBox; 2], + accuracy: Accuracy, + budget: SsiBudget, +) -> Result { + intersect_patches_from(store, surfaces, domains, 0, accuracy, budget) +} + +pub(super) fn intersect_patches_from( + store: &mut GeometryStore, + surfaces: [u32; 2], + domains: [UVBox; 2], + source_side: usize, + accuracy: Accuracy, + budget: SsiBudget, +) -> Result { + if source_side > 1 { + return Err(GeometryError::InvalidGeometry( + "universal SSI source side must be zero or one".into(), + )); + } + let ordered_surfaces = if source_side == 0 { + surfaces + } else { + [surfaces[1], surfaces[0]] + }; + let ordered_domains = if source_side == 0 { + domains + } else { + [domains[1], domains[0]] + }; + accuracy.validate()?; + budget.validate()?; + check_patch_domains(ordered_domains)?; + let source = store.surface(ordered_surfaces[0])?.clone(); + let target = store.surface(ordered_surfaces[1])?.clone(); + source.validate()?; + target.validate()?; + let source_bounds = source.enclose(ordered_domains[0])?; + let target_bounds = target.enclose(ordered_domains[1])?; + let scale = bounds_diameter(source_bounds) + .max(surface_scale(&source)) + .max(surface_scale(&target)) + .max(accuracy.geometric); + let target_cell = (scale / 64.0).max(16.0 * accuracy.geometric); + let (segments, contacts) = patch_segments( + &source, + &target, + ordered_domains, + target_bounds, + target_cell, + accuracy, + budget, + )?; + let mut result = SsiResult { + contacts, + ..SsiResult::default() + }; + if segments.is_empty() { + return Ok(result); + } + + let unique = unique_segments(&source, segments, ordered_domains, accuracy, scale); + let mut parent = stitched_endpoints(&source, &target, &unique, accuracy, target_cell)?; + add_branch_curves( + store, + &unique, + &mut parent, + ordered_surfaces, + accuracy, + budget, + &mut result, + )?; + if source_side == 1 { + for curve in &mut result.curves { + curve.pcurves.swap(0, 1); + } + } + Ok(result) +} + +fn check_patch_domains(ordered_domains: [UVBox; 2]) -> Result<(), GeometryError> { + for domain in ordered_domains { + for axis in domain { + Interval::new(axis.lo, axis.hi)?; + if axis.width() <= 0.0 { + return Err(GeometryError::InvalidGeometry( + "universal SSI patch domain has no area".into(), + )); + } + } + } + Ok(()) +} + +fn patch_segments( + source: &SurfaceGeometry, + target: &SurfaceGeometry, + ordered_domains: [UVBox; 2], + target_bounds: PatchBounds, + target_cell: f64, + accuracy: Accuracy, + budget: SsiBudget, +) -> Result<(Vec, Vec), GeometryError> { + let mut stack = vec![(ordered_domains[0], 0usize)]; + let mut visits = 0usize; + let mut segments = Vec::new(); + let mut contacts = Vec::new(); + let mut unresolved = false; + while let Some((domain, depth)) = stack.pop() { + visits = visits + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("SSI patch visits".into()))?; + if visits > budget.max_patch_pairs { + return Err(GeometryError::UnresolvedIntersection( + "universal SSI patch-pair visit budget exhausted".into(), + )); + } + let bounds = source.enclose(domain)?; + if !bounds.overlaps(target_bounds, accuracy.intersection) { + continue; + } + if !implicit_interval(target, bounds)?.contains(0.0) { + continue; + } + if bounds_diameter(bounds) <= target_cell || depth == budget.max_subdivision_depth { + let found = cell_segments(source, target, domain, accuracy.intersection)?; + if found.is_empty() { + match stationary_event(source, target, domain, accuracy.intersection)? { + Some(StationaryEvent::Contact(contact)) => { + if contacts + .iter() + .all(|existing| norm(sub(*existing, contact)) > accuracy.intersection) + { + contacts.push(contact); + } + } + Some(StationaryEvent::InteriorCritical) + if depth == budget.max_subdivision_depth => + { + unresolved = true; + } + Some(StationaryEvent::InteriorCritical) => { + let axis = usize::from(domain[1].width() > domain[0].width()); + let children = split(domain, axis)?; + stack.push((children[1], depth + 1)); + stack.push((children[0], depth + 1)); + } + None => {} + } + } else { + segments.extend(found); + } + continue; + } + let axis = usize::from(domain[1].width() > domain[0].width()); + let children = split(domain, axis)?; + stack.push((children[1], depth + 1)); + stack.push((children[0], depth + 1)); + } + if unresolved { + return Err(GeometryError::UnresolvedIntersection( + "universal SSI could not certify an interval cell containing a possible tangency or sub-cell branch".into(), + )); + } + Ok((segments, contacts)) +} + +fn unique_segments( + source: &SurfaceGeometry, + segments: Vec, + ordered_domains: [UVBox; 2], + accuracy: Accuracy, + scale: f64, +) -> Vec { + let periods = source.charts()[0].periods; + let quantum = [ + (ordered_domains[0][0].width() / 1_000_000.0).max(accuracy.geometric / scale), + (ordered_domains[0][1].width() / 1_000_000.0).max(accuracy.geometric / scale), + ]; + let mut unique = Vec::new(); + let mut segment_keys = HashSet::new(); + for segment in segments { + let mut keys = segment + .ends + .map(|root| normalized_key(root.uv_a, ordered_domains[0], periods, quantum)); + if keys[0] == keys[1] { + continue; + } + if keys[1] < keys[0] { + keys.swap(0, 1); + } + if segment_keys.insert(keys) { + unique.push(segment); + } + } + unique +} + +fn stitched_endpoints( + source: &SurfaceGeometry, + target: &SurfaceGeometry, + unique: &[Segment], + accuracy: Accuracy, + target_cell: f64, +) -> Result, GeometryError> { + let endpoint_count = unique.len() * 2; + let mut parent = (0..endpoint_count).collect::>(); + let stitch_tolerance = + (4.0 * accuracy.geometric.max(accuracy.intersection)).max(target_cell * 0.01); + let mut buckets: HashMap<[i64; 3], Vec> = HashMap::new(); + for endpoint in 0..endpoint_count { + let root = unique[endpoint / 2].ends[endpoint % 2]; + let key = root + .point + .map(|coordinate| (coordinate / stitch_tolerance).floor() as i64); + let tangent = segment_endpoint_tangent(source, target, unique[endpoint / 2], endpoint % 2)?; + for x in (key[0] - 1)..=(key[0] + 1) { + for y in (key[1] - 1)..=(key[1] + 1) { + for z in (key[2] - 1)..=(key[2] + 1) { + for &candidate in buckets.get(&[x, y, z]).into_iter().flatten() { + let other = unique[candidate / 2].ends[candidate % 2]; + if norm(sub(root.point, other.point)) > stitch_tolerance { + continue; + } + let other_tangent = segment_endpoint_tangent( + source, + target, + unique[candidate / 2], + candidate % 2, + )?; + if dot(tangent, other_tangent).abs() >= 0.99 { + union(&mut parent, endpoint, candidate); + } + } + } + } + } + buckets.entry(key).or_default().push(endpoint); + } + Ok(parent) +} + +fn add_branch_curves( + store: &mut GeometryStore, + unique: &[Segment], + parent: &mut [usize], + ordered_surfaces: [u32; 2], + accuracy: Accuracy, + budget: SsiBudget, + result: &mut SsiResult, +) -> Result<(), GeometryError> { + let collapsed_segments = (0..unique.len()) + .map(|index| find(parent, index * 2) == find(parent, index * 2 + 1)) + .collect::>(); + let mut adjacency: HashMap> = HashMap::new(); + for index in 0..unique.len() { + if collapsed_segments[index] { + continue; + } + for end in 0..2 { + let node = find(parent, index * 2 + end); + adjacency.entry(node).or_default().push((index, end)); + } + } + if adjacency.values().any(|edges| edges.len() > 2) { + return Err(GeometryError::UnresolvedIntersection( + "universal SSI found a singular branch junction".into(), + )); + } + let mut used = collapsed_segments; + for start in 0..unique.len() { + if used[start] { + continue; + } + let endpoint = (0..2) + .find(|&end| { + let node = find(parent, start * 2 + end); + adjacency.get(&node).is_some_and(|edges| edges.len() == 1) + }) + .unwrap_or(0); + let (branch, source_domains) = branch_roots( + unique, parent, &adjacency, &mut used, start, endpoint, budget, + )?; + if branch.len() >= 2 { + result.curves.push(branch_definition( + branch, + source_domains, + ordered_surfaces, + store, + accuracy, + )?); + } + } + Ok(()) +} + +fn branch_roots( + unique: &[Segment], + parent: &mut [usize], + adjacency: &HashMap>, + used: &mut [bool], + start: usize, + endpoint: usize, + budget: SsiBudget, +) -> Result<(Vec, Vec), GeometryError> { + let mut branch = Vec::new(); + let mut source_domains = Vec::new(); + let mut current = start; + let mut from_end = endpoint; + loop { + if used[current] { + break; + } + used[current] = true; + let segment = unique[current]; + if branch.is_empty() { + branch.push(segment.ends[from_end]); + } + let to_end = 1 - from_end; + branch.push(segment.ends[to_end]); + source_domains.push(segment.domain); + if branch.len() > budget.max_steps_per_branch { + return Err(GeometryError::UnresolvedIntersection( + "universal SSI branch step budget exhausted".into(), + )); + } + let node = find(parent, current * 2 + to_end); + let Some(next) = adjacency + .get(&node) + .and_then(|edges| edges.iter().copied().find(|(index, _)| !used[*index])) + else { + break; + }; + current = next.0; + from_end = next.1; + } + Ok((branch, source_domains)) +} diff --git a/main/opengeometry/src/intersection/universal_ssi/subdivision.rs b/main/opengeometry/src/intersection/universal_ssi/subdivision.rs new file mode 100644 index 0000000..10b292f --- /dev/null +++ b/main/opengeometry/src/intersection/universal_ssi/subdivision.rs @@ -0,0 +1,20 @@ +use crate::brep::{GeometryError, PatchBounds, UVBox}; +use crate::math::Interval; + +pub(super) fn split(domain: UVBox, axis: usize) -> Result<[UVBox; 2], GeometryError> { + let midpoint = domain[axis].midpoint(); + let mut left = domain; + let mut right = domain; + left[axis] = Interval::new(domain[axis].lo, midpoint)?; + right[axis] = Interval::new(midpoint, domain[axis].hi)?; + Ok([left, right]) +} + +pub(super) fn bounds_diameter(bounds: PatchBounds) -> f64 { + bounds + .axes + .iter() + .map(|axis| axis.width() * axis.width()) + .sum::() + .sqrt() +} diff --git a/main/opengeometry/src/intersection/universal_ssi/tests.rs b/main/opengeometry/src/intersection/universal_ssi/tests.rs new file mode 100644 index 0000000..ea063c1 --- /dev/null +++ b/main/opengeometry/src/intersection/universal_ssi/tests.rs @@ -0,0 +1,194 @@ +use super::intersect_patches; +use crate::brep::{ + Accuracy, Curve, CurveGeometry, Frame3, GeometryStore, SsiBudget, Surface, SurfaceGeometry, +}; +use crate::math::{norm, sub, Interval}; + +fn universal_ssi_accuracy() -> Accuracy { + Accuracy { + geometric: 1e-5, + intersection: 2.5e-6, + tessellation: 1e-3, + exchange: 1e-5, + } +} + +#[test] +fn oblique_plane_torus_is_stored_as_corrected_numerical_branches() { + let mut store = GeometryStore::new(); + let plane = Frame3::from_axis([0.0, 0.0, 0.15], [0.35, -0.2, 1.0], [1.0, 0.0, 0.0]).unwrap(); + store.surfaces = vec![ + SurfaceGeometry::Torus { + frame: Frame3::IDENTITY, + major_radius: 2.0, + minor_radius: 0.5, + }, + SurfaceGeometry::Plane { frame: plane }, + ]; + let tau = std::f64::consts::TAU; + let result = intersect_patches( + &mut store, + [0, 1], + [ + [Interval::new(0.0, tau).unwrap(); 2], + [Interval::new(-4.0, 4.0).unwrap(); 2], + ], + universal_ssi_accuracy(), + SsiBudget::default(), + ) + .unwrap(); + assert!(!result.curves.is_empty()); + for branch in result.curves { + assert!(matches!( + store.curves[branch.curve as usize], + CurveGeometry::Intersection { .. } + )); + let domain = branch.domain.unwrap(); + for index in 0..=16 { + let t = domain.lo + domain.width() * index as f64 / 16.0; + let point = store.curve(branch.curve).unwrap().point_at(t).unwrap(); + for surface in &store.surfaces { + let uv = surface.project(point, None).unwrap(); + assert!( + norm(sub(surface.point_at(uv).unwrap(), point)) + <= universal_ssi_accuracy().intersection + ); + } + } + } +} + +#[test] +fn disjoint_bounded_patches_return_empty_without_fabricating_a_branch() { + let mut store = GeometryStore::new(); + store.surfaces = vec![ + SurfaceGeometry::Sphere { + frame: Frame3::IDENTITY, + radius: 1.0, + }, + SurfaceGeometry::Plane { + frame: Frame3 { + origin: [0.0, 0.0, 2.0], + ..Frame3::IDENTITY + }, + }, + ]; + let result = intersect_patches( + &mut store, + [0, 1], + [ + [ + Interval::new(0.0, std::f64::consts::TAU).unwrap(), + Interval::new(-std::f64::consts::FRAC_PI_2, std::f64::consts::FRAC_PI_2).unwrap(), + ], + [Interval::new(-2.0, 2.0).unwrap(); 2], + ], + universal_ssi_accuracy(), + SsiBudget::default(), + ) + .unwrap(); + assert!(result.curves.is_empty()); + assert!(result.contacts.is_empty()); +} + +#[test] +fn terminal_subdivision_reports_an_isolated_torus_plane_tangency() { + let u: f64 = 0.37; + let v: f64 = 0.41; + let (sin_u, cos_u) = u.sin_cos(); + let (sin_v, cos_v) = v.sin_cos(); + let normal = [cos_v * cos_u, cos_v * sin_u, sin_v]; + let radial = 2.0 + 0.5 * cos_v; + let contact = [radial * cos_u, radial * sin_u, 0.5 * sin_v]; + let mut store = GeometryStore::new(); + store.surfaces = vec![ + SurfaceGeometry::Torus { + frame: Frame3::IDENTITY, + major_radius: 2.0, + minor_radius: 0.5, + }, + SurfaceGeometry::Plane { + frame: Frame3::from_axis(contact, normal, [0.0, 0.0, 1.0]).unwrap(), + }, + ]; + let tau = std::f64::consts::TAU; + let result = intersect_patches( + &mut store, + [0, 1], + [ + [Interval::new(0.0, tau).unwrap(); 2], + [Interval::new(-3.0, 3.0).unwrap(); 2], + ], + universal_ssi_accuracy(), + SsiBudget::default(), + ) + .unwrap(); + assert!(result.curves.is_empty()); + assert_eq!(result.contacts.len(), 1); + assert!(norm(sub(result.contacts[0], contact)) <= universal_ssi_accuracy().intersection); +} + +#[test] +fn tangent_generator_is_not_collapsed_into_an_isolated_contact() { + let mut store = GeometryStore::new(); + store.surfaces = vec![ + SurfaceGeometry::Cylinder { + frame: Frame3::IDENTITY, + radius: 1.0, + }, + SurfaceGeometry::Plane { + frame: Frame3::from_axis([1.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]).unwrap(), + }, + ]; + let result = intersect_patches( + &mut store, + [0, 1], + [ + [ + Interval::new(-0.5, 0.5).unwrap(), + Interval::new(-1.0, 1.0).unwrap(), + ], + [Interval::new(-2.0, 2.0).unwrap(); 2], + ], + universal_ssi_accuracy(), + SsiBudget::default(), + ) + .unwrap(); + assert!(!result.curves.is_empty()); + assert!(result.contacts.is_empty()); +} + +#[test] +fn target_patch_bounds_prune_intersections_on_the_unbounded_support() { + let mut store = GeometryStore::new(); + store.surfaces = vec![ + SurfaceGeometry::Cylinder { + frame: Frame3::IDENTITY, + radius: 1.0, + }, + SurfaceGeometry::Cylinder { + frame: Frame3::from_axis([0.0, 0.0, 0.2], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]).unwrap(), + radius: 0.6, + }, + ]; + let tau = std::f64::consts::TAU; + let result = intersect_patches( + &mut store, + [0, 1], + [ + [ + Interval::new(0.0, tau).unwrap(), + Interval::new(-1.0, 1.0).unwrap(), + ], + [ + Interval::new(0.0, tau).unwrap(), + Interval::new(10.0, 12.0).unwrap(), + ], + ], + universal_ssi_accuracy(), + SsiBudget::default(), + ) + .unwrap(); + assert!(result.curves.is_empty()); + assert!(result.contacts.is_empty()); +} diff --git a/main/opengeometry/src/lib.rs b/main/opengeometry/src/lib.rs index 71f6b6f..4e8a0a7 100644 --- a/main/opengeometry/src/lib.rs +++ b/main/opengeometry/src/lib.rs @@ -1,53 +1,39 @@ -pub mod geometry { - pub mod boolean2d; - pub mod curved_boolean2d; - pub mod geometrybuffer; - pub mod offset2d; - pub mod offset_regions; - pub mod poly2d; - pub mod triangle; -} - -pub mod analytic; -pub mod math; +#![cfg_attr( + not(test), + deny( + clippy::unwrap_used, + clippy::expect_used, + clippy::panic, + clippy::unreachable + ) +)] +#![allow(clippy::needless_range_loop, clippy::type_complexity)] +#![deny( + unreachable_pub, + clippy::wildcard_imports, + clippy::todo, + clippy::unimplemented, + clippy::dbg_macro, + clippy::iter_over_hash_type +)] -pub mod operations { - pub mod extrude; - pub mod offset; - pub mod profile; - pub mod sweep; - pub mod triangulate; - pub mod windingsort; -} - -pub mod utility { - pub mod bgeometry; -} - -pub mod primitives { - pub mod arc; - pub mod cuboid; - pub mod curve; - pub mod cylinder; - pub mod elliptical_arc; - pub mod line; - pub mod polygon; - pub mod polyline; - pub mod rectangle; - pub mod sphere; - pub mod sweep; - pub mod wedge; -} - -pub mod spatial { - pub mod placement; - pub mod workplane; -} +pub mod math; pub mod brep; -pub mod editor; -pub mod export; -pub mod freeform; -pub mod tolerance; -pub mod units; -pub mod world; + +pub(crate) mod geom2d; + +pub mod query; + +pub mod primitives; +pub mod tessellation; + +pub(crate) mod intersection; + +pub mod operations; + +pub mod exchange; + +pub mod world_graph; + +pub mod bindings; diff --git a/main/opengeometry/src/math/error.rs b/main/opengeometry/src/math/error.rs new file mode 100644 index 0000000..cf27699 --- /dev/null +++ b/main/opengeometry/src/math/error.rs @@ -0,0 +1,29 @@ +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum MathError { + NonFinite, + ArithmeticRange, + InvalidInterval, + DivisionByZero, + SingularSystem, + InvalidDimension, + IterationLimit, +} + +impl fmt::Display for MathError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:?}") + } +} + +impl std::error::Error for MathError {} + +pub(crate) fn finite(value: f64) -> Result { + if value.is_finite() { + Ok(value) + } else { + Err(MathError::NonFinite) + } +} diff --git a/main/opengeometry/src/math/interval.rs b/main/opengeometry/src/math/interval.rs index 5b64442..7fca280 100644 --- a/main/opengeometry/src/math/interval.rs +++ b/main/opengeometry/src/math/interval.rs @@ -1,13 +1,14 @@ -use super::{finite, MathError}; +use super::error::{finite, MathError}; use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] pub struct Interval { pub lo: f64, pub hi: f64, } -pub fn next_up(x: f64) -> f64 { +fn next_up(x: f64) -> f64 { if x.is_nan() || x == f64::INFINITY { return x; } @@ -21,12 +22,12 @@ pub fn next_up(x: f64) -> f64 { }) } -pub fn next_down(x: f64) -> f64 { +fn next_down(x: f64) -> f64 { -next_up(-x) } impl Interval { - pub fn new(lo: f64, hi: f64) -> Result { + pub(crate) fn new(lo: f64, hi: f64) -> Result { finite(lo)?; finite(hi)?; if lo > hi { @@ -35,27 +36,27 @@ impl Interval { Ok(Self { lo, hi }) } - pub fn point(x: f64) -> Result { + pub(crate) fn point(x: f64) -> Result { Self::new(x, x) } - pub fn contains(self, value: f64) -> bool { + pub(crate) fn contains(self, value: f64) -> bool { self.lo <= value && value <= self.hi } - pub fn overlaps(self, other: Self) -> bool { + fn overlaps(self, other: Self) -> bool { self.lo <= other.hi && other.lo <= self.hi } - pub fn width(self) -> f64 { + pub(crate) fn width(self) -> f64 { self.hi - self.lo } - pub fn midpoint(self) -> f64 { + pub(crate) fn midpoint(self) -> f64 { self.lo * 0.5 + self.hi * 0.5 } - pub fn hull(self, other: Self) -> Self { + pub(crate) fn hull(self, other: Self) -> Self { Self { lo: self.lo.min(other.lo), hi: self.hi.max(other.hi), @@ -66,15 +67,15 @@ impl Interval { Self::new(next_down(finite(lo)?), next_up(finite(hi)?)) } - pub fn add(self, other: Self) -> Result { + pub(crate) fn add_interval(self, other: Self) -> Result { Self::rounded(self.lo + other.lo, self.hi + other.hi) } - pub fn sub(self, other: Self) -> Result { + pub(crate) fn sub_interval(self, other: Self) -> Result { Self::rounded(self.lo - other.hi, self.hi - other.lo) } - pub fn mul(self, other: Self) -> Result { + pub(crate) fn mul_interval(self, other: Self) -> Result { let products = [ self.lo * other.lo, self.lo * other.hi, @@ -90,14 +91,14 @@ impl Interval { ) } - pub fn div(self, other: Self) -> Result { + pub(crate) fn div_interval(self, other: Self) -> Result { if other.contains(0.0) { return Err(MathError::DivisionByZero); } - self.mul(Self::rounded(1.0 / other.hi, 1.0 / other.lo)?) + self.mul_interval(Self::rounded(1.0 / other.hi, 1.0 / other.lo)?) } - pub fn square(self) -> Result { + pub(crate) fn square(self) -> Result { let lower = if self.contains(0.0) { 0.0 } else { @@ -109,7 +110,7 @@ impl Interval { Ok(result) } - pub fn sqrt(self) -> Result { + pub(crate) fn sqrt(self) -> Result { if self.lo < 0.0 { return Err(MathError::InvalidInterval); } @@ -118,14 +119,14 @@ impl Interval { Ok(result) } - pub fn sin(self) -> Result { + pub(crate) fn sin(self) -> Result { Self::new(self.lo, self.hi)?; let pi = Self::new( next_down(std::f64::consts::PI), next_up(std::f64::consts::PI), )?; - let tau = pi.mul(Self::point(2.0)?)?; - // A broad bound avoids unreliable argument reduction for large inputs. + let tau = pi.mul_interval(Self::point(2.0)?)?; + if self.width() >= tau.lo || self.lo.abs().max(self.hi.abs()) > 32.0 * pi.lo { return Self::new(-1.0, 1.0); } @@ -133,7 +134,7 @@ impl Interval { let first = (self.lo / std::f64::consts::PI - 0.5).floor() as i32 - 1; let last = (self.hi / std::f64::consts::PI - 0.5).ceil() as i32 + 1; for k in first..=last { - let critical = pi.mul(Self::point(k as f64 + 0.5)?)?; + let critical = pi.mul_interval(Self::point(k as f64 + 0.5)?)?; if self.overlaps(critical) { result = result.hull(Self::point(if k % 2 == 0 { 1.0 } else { -1.0 })?); } @@ -141,88 +142,32 @@ impl Interval { Self::new(result.lo.max(-1.0), result.hi.min(1.0)) } - pub fn cos(self) -> Result { + pub(crate) fn cos(self) -> Result { let pi = Self::new( next_down(std::f64::consts::PI), next_up(std::f64::consts::PI), )?; - self.add(pi.mul(Self::point(0.5)?)?)?.sin() + self.add_interval(pi.mul_interval(Self::point(0.5)?)?)? + .sin() } } fn sin_endpoint(x: f64, tau: Interval) -> Result { let k = (x / std::f64::consts::TAU).round(); - let y = Interval::point(x)?.sub(tau.mul(Interval::point(k)?)?)?; + let y = Interval::point(x)?.sub_interval(tau.mul_interval(Interval::point(k)?)?)?; let y2 = y.square()?; let mut term = y; let mut sum = term; for n in 1..=20 { let denominator = (2 * n * (2 * n + 1)) as f64; - term = term.mul(y2)?.div(Interval::point(-denominator)?)?; - sum = sum.add(term)?; - } - let next = term.mul(y2)?.div(Interval::point((42 * 43) as f64)?)?; + term = term + .mul_interval(y2)? + .div_interval(Interval::point(-denominator)?)?; + sum = sum.add_interval(term)?; + } + let next = term + .mul_interval(y2)? + .div_interval(Interval::point((42 * 43) as f64)?)?; let remainder = next.lo.abs().max(next.hi.abs()); - sum.add(Interval::new(-remainder, remainder)?) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn arithmetic_encloses_cancellation_and_extrema() { - let a = Interval::point(0.1).unwrap(); - let b = Interval::point(0.2).unwrap(); - assert!(a.add(b).unwrap().contains(0.3)); - assert!(a.sub(a).unwrap().contains(0.0)); - assert_eq!( - a.div(Interval::new(-1.0, 1.0).unwrap()), - Err(MathError::DivisionByZero) - ); - assert!(Interval::new(-2.0, 3.0) - .unwrap() - .square() - .unwrap() - .contains(9.0)); - assert!(Interval::new(0.0, std::f64::consts::PI) - .unwrap() - .sin() - .unwrap() - .contains(1.0)); - assert!(Interval::new(-1000.0, 1000.0) - .unwrap() - .cos() - .unwrap() - .contains(-1.0)); - } - - #[test] - fn trigonometric_bounds_cover_dense_independent_samples() { - for i in -256..256 { - let lo = i as f64 * 0.125; - let hi = lo + 0.375; - let interval = Interval::new(lo, hi).unwrap(); - let sin = interval.sin().unwrap(); - let cos = interval.cos().unwrap(); - for j in 0..=32 { - let x = lo + (hi - lo) * j as f64 / 32.0; - assert!(sin.contains(x.sin()), "{interval:?} {sin:?} {x}"); - assert!(cos.contains(x.cos()), "{interval:?} {cos:?} {x}"); - } - } - } - - #[test] - fn floating_neighbors_cover_zero_and_negative_numbers() { - assert!(next_up(0.0) > 0.0); - assert!(next_down(0.0) < 0.0); - assert!(next_up(-1.0) > -1.0); - assert!(next_down(-1.0) < -1.0); - assert!(Interval::point(2.0) - .unwrap() - .sqrt() - .unwrap() - .contains(2.0_f64.sqrt())); - } + sum.add_interval(Interval::new(-remainder, remainder)?) } diff --git a/main/opengeometry/src/math/mod.rs b/main/opengeometry/src/math/mod.rs index 90f7fed..da727e4 100644 --- a/main/opengeometry/src/math/mod.rs +++ b/main/opengeometry/src/math/mod.rs @@ -1,34 +1,20 @@ -pub mod interval; -pub mod predicates; -pub mod roots; -pub mod solve; +mod error; +mod interval; +mod predicates; +mod roots; +mod solve; +mod union_find; +mod vector; -use serde::{Deserialize, Serialize}; -use std::fmt; - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum MathError { - NonFinite, - ArithmeticRange, - InvalidInterval, - DivisionByZero, - SingularSystem, - InvalidDimension, - IterationLimit, -} - -impl fmt::Display for MathError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self:?}") - } -} - -impl std::error::Error for MathError {} - -pub(crate) fn finite(value: f64) -> Result { - if value.is_finite() { - Ok(value) - } else { - Err(MathError::NonFinite) - } -} +pub(super) use error::finite; +pub use error::MathError; +pub use interval::Interval; +pub use predicates::{discriminant, incircle2d, orient2d, orient3d, Sign}; +pub(super) use predicates::{plane_sphere_relation, sphere_sphere_relation}; +pub use roots::RootCandidates; +pub(super) use roots::{isolate_candidates, quadratic, QuadraticRoots}; +pub(super) use solve::solve; +pub use solve::LinearSolution; +pub(super) use union_find::{find, union}; +pub use vector::Point3; +pub(super) use vector::{add, cross, dot, norm, scale, sub}; diff --git a/main/opengeometry/src/math/predicates.rs b/main/opengeometry/src/math/predicates.rs index 3d11ba0..b6552f4 100644 --- a/main/opengeometry/src/math/predicates.rs +++ b/main/opengeometry/src/math/predicates.rs @@ -1,4 +1,4 @@ -use super::{finite, MathError}; +use super::error::{finite, MathError}; use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] @@ -31,7 +31,7 @@ fn two_sum(a: f64, b: f64) -> Result<(f64, f64), MathError> { fn two_product(a: f64, b: f64) -> Result<(f64, f64), MathError> { let product = finite(a * b)?; - // Below this bound the exact product's low bits may not fit in f64. + if a != 0.0 && b != 0.0 && product.abs() < f64::MIN_POSITIVE * 18_014_398_509_481_984.0 { return Err(MathError::ArithmeticRange); } @@ -213,7 +213,7 @@ pub fn discriminant(a: f64, b: f64, c: f64) -> Result { Ok(discriminant_estimate(a, b, c)?.0) } -pub fn discriminant_estimate(a: f64, b: f64, c: f64) -> Result<(Sign, f64), MathError> { +pub(super) fn discriminant_estimate(a: f64, b: f64, c: f64) -> Result<(Sign, f64), MathError> { for value in [a, b, c] { finite(value)?; } @@ -224,7 +224,7 @@ pub fn discriminant_estimate(a: f64, b: f64, c: f64) -> Result<(Sign, f64), Math Ok((sign(&expansion), finite(expansion.iter().copied().sum())?)) } -pub fn plane_sphere_relation( +pub(crate) fn plane_sphere_relation( origin: [f64; 3], normal: [f64; 3], center: [f64; 3], @@ -254,7 +254,7 @@ pub fn plane_sphere_relation( )?)) } -pub fn sphere_sphere_relation( +pub(crate) fn sphere_sphere_relation( a: [f64; 3], ra: f64, b: [f64; 3], @@ -278,79 +278,3 @@ pub fn sphere_sphere_relation( )?), ]) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn cancellation_retains_exact_orientation() { - let m = 134_217_728.0; - assert_eq!((m + 1.0) * (m - 1.0) - m * m, 0.0); - assert_eq!( - orient2d([0.0, 0.0], [m + 1.0, m], [m, m - 1.0]), - Ok(Sign::Negative) - ); - assert_eq!( - orient2d([0.0, 0.0], [m, m - 1.0], [m + 1.0, m]), - Ok(Sign::Positive) - ); - } - - #[test] - fn tetrahedron_and_cocircular_signs() { - let d = [0.0, 0.0, 0.0]; - assert_eq!( - orient3d([1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0], d), - Ok(Sign::Positive) - ); - assert_eq!( - incircle2d([1.0, 0.0], [0.0, 1.0], [-1.0, 0.0], [0.0, -1.0]), - Ok(Sign::Zero) - ); - assert_eq!( - incircle2d([1.0, 0.0], [0.0, 1.0], [-1.0, 0.0], [0.0, 0.0]), - Ok(Sign::Positive) - ); - } - - #[test] - fn input_and_arithmetic_limits_are_explicit() { - assert_eq!( - orient2d([f64::NAN, 0.0], [1.0, 0.0], [0.0, 1.0]), - Err(MathError::NonFinite) - ); - assert_eq!(discriminant(1.0, 2.0, 1.0), Ok(Sign::Zero)); - assert_eq!( - discriminant(1.0, 2.0, 1.0 + f64::EPSILON), - Ok(Sign::Negative) - ); - assert_eq!( - discriminant(f64::MIN_POSITIVE, 0.0, f64::MIN_POSITIVE), - Err(MathError::ArithmeticRange) - ); - } - - #[test] - fn exact_integer_grid_agrees_with_integer_determinants() { - let mut seed = 7_u64; - for _ in 0..2048 { - let mut p = [[0_i64; 2]; 3]; - for row in &mut p { - for x in row { - seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1); - *x = ((seed >> 32) % 100_000) as i64 - 50_000; - } - } - let det = (p[0][0] - p[2][0]) as i128 * (p[1][1] - p[2][1]) as i128 - - (p[0][1] - p[2][1]) as i128 * (p[1][0] - p[2][0]) as i128; - let expected = match det.cmp(&0) { - std::cmp::Ordering::Less => Sign::Negative, - std::cmp::Ordering::Equal => Sign::Zero, - std::cmp::Ordering::Greater => Sign::Positive, - }; - let f = p.map(|v| v.map(|x| x as f64)); - assert_eq!(orient2d(f[0], f[1], f[2]), Ok(expected)); - } - } -} diff --git a/main/opengeometry/src/math/roots.rs b/main/opengeometry/src/math/roots.rs index 11aadc6..aa33479 100644 --- a/main/opengeometry/src/math/roots.rs +++ b/main/opengeometry/src/math/roots.rs @@ -1,19 +1,16 @@ -use super::{ - finite, - interval::Interval, - predicates::{discriminant_estimate, Sign}, - MathError, -}; +use super::error::{finite, MathError}; +use super::interval::Interval; +use super::predicates::{discriminant_estimate, Sign}; #[derive(Clone, Debug, PartialEq)] -pub enum QuadraticRoots { +pub(crate) enum QuadraticRoots { Empty, One(f64), Two([f64; 2]), IdenticallyZero, } -pub fn quadratic(a: f64, b: f64, c: f64) -> Result { +pub(crate) fn quadratic(a: f64, b: f64, c: f64) -> Result { for value in [a, b, c] { finite(value)?; } @@ -57,23 +54,23 @@ pub fn quadratic(a: f64, b: f64, c: f64) -> Result { } } -pub fn polynomial_bounds(coefficients: &[f64], domain: Interval) -> Result { +fn polynomial_bounds(coefficients: &[f64], domain: Interval) -> Result { Interval::new(domain.lo, domain.hi)?; let mut result = Interval::point(0.0)?; for &coefficient in coefficients.iter().rev() { - result = result.mul(domain)?.add(Interval::point(coefficient)?)?; + result = result + .mul_interval(domain)? + .add_interval(Interval::point(coefficient)?)?; } Ok(result) } #[derive(Clone, Debug)] pub struct RootCandidates { - pub intervals: Vec, - pub visited: usize, + pub(crate) intervals: Vec, } -// Every root is enclosed; a candidate interval need not actually contain a root. -pub fn isolate_candidates( +pub(crate) fn isolate_candidates( coefficients: &[f64], domain: Interval, width: f64, @@ -117,60 +114,5 @@ pub fn isolate_candidates( pending.push(Interval::new(current.lo, middle)?); } } - Ok(RootCandidates { intervals, visited }) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn stable_quadratic_preserves_small_root_and_contacts() { - let QuadraticRoots::Two(roots) = quadratic(1.0, -1e8, 1.0).unwrap() else { - panic!("two roots expected") - }; - assert!((roots[0] - 1e-8).abs() < 1e-22); - assert!((roots[1] - 1e8).abs() < 1e-7); - assert_eq!(quadratic(1.0, 2.0, 1.0), Ok(QuadraticRoots::One(-1.0))); - assert_eq!(quadratic(1.0, 0.0, 1.0), Ok(QuadraticRoots::Empty)); - assert_eq!( - quadratic(0.0, 0.0, 0.0), - Ok(QuadraticRoots::IdenticallyZero) - ); - } - - #[test] - fn subdivision_preserves_simple_and_even_multiplicity_roots() { - let roots = isolate_candidates( - &[0.0, 0.0, -1.0, 0.0, 1.0], - Interval::new(-2.0, 2.0).unwrap(), - 1e-4, - 100_000, - ) - .unwrap(); - for expected in [-1.0, 0.0, 1.0] { - assert!(roots.intervals.iter().any(|i| i.contains(expected))); - } - assert_eq!( - isolate_candidates( - &[1.0, 0.0, 1.0], - Interval::new(-2.0, 2.0).unwrap(), - 1e-4, - 100 - ) - .unwrap() - .intervals - .len(), - 0 - ); - assert!(matches!( - isolate_candidates( - &[-1.0, 0.0, 1.0], - Interval::new(-2.0, 2.0).unwrap(), - 1e-8, - 1 - ), - Err(MathError::IterationLimit) - )); - } + Ok(RootCandidates { intervals }) } diff --git a/main/opengeometry/src/math/solve.rs b/main/opengeometry/src/math/solve.rs index d0bc839..64957c8 100644 --- a/main/opengeometry/src/math/solve.rs +++ b/main/opengeometry/src/math/solve.rs @@ -1,13 +1,13 @@ -use super::{finite, MathError}; +use super::error::{finite, MathError}; #[derive(Clone, Debug)] pub struct LinearSolution { - pub value: [f64; N], + pub(crate) value: [f64; N], pub residual: f64, - pub minimum_scaled_pivot: f64, + pub(crate) minimum_scaled_pivot: f64, } -pub fn solve( +pub(crate) fn solve( matrix: [[f64; N]; N], rhs: [f64; N], ) -> Result, MathError> { @@ -76,37 +76,3 @@ pub fn solve( minimum_scaled_pivot, }) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn pivoting_and_row_scaling_recover_known_solution() { - let matrix = [ - [0.0, 2.0, 1.0, 0.0], - [1e-12, 0.0, 0.0, 2e-12], - [1.0, 1.0, 1.0, 1.0], - [2.0, 0.0, 3.0, 1.0], - ]; - let expected = [1.0, -2.0, 3.0, 0.5]; - let rhs = matrix.map(|row| row.into_iter().zip(expected).map(|(a, x)| a * x).sum()); - let solution = solve(matrix, rhs).unwrap(); - for (a, b) in solution.value.into_iter().zip(expected) { - assert!((a - b).abs() < 1e-12); - } - assert!(solution.residual < 1e-12); - } - - #[test] - fn singular_and_nonfinite_systems_return_errors() { - assert!(matches!( - solve([[1.0, 2.0], [2.0, 4.0]], [1.0, 2.0]), - Err(MathError::SingularSystem) - )); - assert!(matches!( - solve([[f64::NAN]], [1.0]), - Err(MathError::NonFinite) - )); - } -} diff --git a/main/opengeometry/src/math/union_find.rs b/main/opengeometry/src/math/union_find.rs new file mode 100644 index 0000000..e3a5e04 --- /dev/null +++ b/main/opengeometry/src/math/union_find.rs @@ -0,0 +1,21 @@ +pub(crate) fn find(parent: &mut [usize], value: usize) -> usize { + let mut root = value; + while parent[root] != root { + root = parent[root]; + } + let mut current = value; + while parent[current] != current { + let next = parent[current]; + parent[current] = root; + current = next; + } + root +} + +pub(crate) fn union(parent: &mut [usize], left: usize, right: usize) { + let left = find(parent, left); + let right = find(parent, right); + if left != right { + parent[right] = left; + } +} diff --git a/main/opengeometry/src/math/vector.rs b/main/opengeometry/src/math/vector.rs new file mode 100644 index 0000000..08f5abd --- /dev/null +++ b/main/opengeometry/src/math/vector.rs @@ -0,0 +1,24 @@ +pub type Point3 = [f64; 3]; + +pub(crate) fn add(a: Point3, b: Point3) -> Point3 { + std::array::from_fn(|i| a[i] + b[i]) +} +pub(crate) fn sub(a: Point3, b: Point3) -> Point3 { + std::array::from_fn(|i| a[i] - b[i]) +} +pub(crate) fn scale(a: Point3, s: f64) -> Point3 { + a.map(|v| v * s) +} +pub(crate) fn dot(a: Point3, b: Point3) -> f64 { + a[0].mul_add(b[0], a[1].mul_add(b[1], a[2] * b[2])) +} +pub(crate) fn cross(a: Point3, b: Point3) -> Point3 { + [ + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0], + ] +} +pub(crate) fn norm(a: Point3) -> f64 { + a[0].hypot(a[1]).hypot(a[2]) +} diff --git a/main/opengeometry/src/operations/creating/extrude.rs b/main/opengeometry/src/operations/creating/extrude.rs new file mode 100644 index 0000000..fe566b6 --- /dev/null +++ b/main/opengeometry/src/operations/creating/extrude.rs @@ -0,0 +1,104 @@ +use super::holes::{check_annular, check_circle_hole, check_layout}; +use super::profile::{check_lines, edges, profile_frame, ProfileLoop}; +use crate::brep::{Accuracy, BrepEnvelope, Frame3}; +use crate::math::{add, dot, norm, scale, sub, Point3}; +use crate::operations::{invalid, OperationError}; +use crate::primitives; + +pub(crate) fn build( + id: String, + outer: ProfileLoop, + holes: Vec, + distance: f64, + accuracy: Accuracy, +) -> Result { + if !distance.is_finite() || distance.abs() <= 4.0 * accuracy.geometric { + return Err(invalid("extrusion distance is below geometric resolution")); + } + let mut frame = profile_frame(&outer, accuracy.geometric)?; + check_lines(&outer, frame, accuracy.geometric)?; + check_holes(&outer, &holes, frame, accuracy)?; + let original_origin = frame.origin; + if distance < 0.0 { + frame.origin = add(frame.origin, scale(frame.z, distance)); + } + let height = distance.abs(); + let result = match (&outer, holes.as_slice()) { + (ProfileLoop::Circle { radius, .. }, []) => { + primitives::cylinder(id, frame, *radius, height, accuracy)? + } + ( + ProfileLoop::Circle { radius, .. }, + [ProfileLoop::Circle { + frame: inner_frame, + radius: inner_radius, + }], + ) if norm(sub(inner_frame.origin, original_origin)) <= 4.0 * accuracy.geometric => { + primitives::annular_cylinder(id, frame, *inner_radius, *radius, height, accuracy)? + } + (ProfileLoop::Lines(outer_points), _) + if holes + .iter() + .all(|hole| matches!(hole, ProfileLoop::Lines(_))) => + { + let mapped = |points: &[Point3]| { + points + .iter() + .map(|point| { + let local = frame.local(*point); + [local[0], local[1]] + }) + .collect() + }; + primitives::linear_extrusion( + id, + frame, + mapped(outer_points), + holes + .iter() + .filter_map(|hole| match hole { + ProfileLoop::Lines(points) => Some(mapped(points)), + _ => None, + }) + .collect(), + height, + accuracy, + )? + } + _ => primitives::arc_edged_extrusion_with_holes( + id, + frame, + edges(&outer, frame), + holes.iter().map(|hole| edges(hole, frame)).collect(), + height, + accuracy, + )?, + }; + Ok(result) +} + +fn check_holes( + outer: &ProfileLoop, + holes: &[ProfileLoop], + frame: Frame3, + accuracy: Accuracy, +) -> Result<(), OperationError> { + for hole in holes { + check_lines(hole, frame, accuracy.geometric)?; + match hole { + ProfileLoop::Circle { + frame: circle, + radius, + } => check_circle_hole(*circle, *radius, frame, accuracy.geometric)?, + ProfileLoop::Lines(points) => { + for point in points { + if dot(sub(*point, frame.origin), frame.z).abs() > 4.0 * accuracy.geometric { + return Err(invalid("hole is not coplanar")); + } + } + } + } + } + check_annular(outer, holes, accuracy.geometric)?; + check_layout(outer, holes, frame, accuracy.geometric) +} diff --git a/main/opengeometry/src/operations/creating/holes.rs b/main/opengeometry/src/operations/creating/holes.rs new file mode 100644 index 0000000..c221d52 --- /dev/null +++ b/main/opengeometry/src/operations/creating/holes.rs @@ -0,0 +1,145 @@ +use super::profile::{edges, ProfileLoop}; +use crate::brep::Frame3; +use crate::math::{cross, dot, norm, sub}; +use crate::operations::{invalid, OperationError}; +use crate::primitives::{ + arc_profile_contains, loops_touch, profile_edge_intersections, profile_point_inside, + ProfileEdge, +}; + +struct PlanarLoop { + edges: Vec, + points: Option>, + probe: [f64; 2], +} + +pub(super) fn check_circle_hole( + circle: Frame3, + radius: f64, + frame: Frame3, + tolerance: f64, +) -> Result<(), OperationError> { + if dot(sub(circle.origin, frame.origin), frame.z).abs() > 4.0 * tolerance + || radius * norm(cross(circle.z, frame.z)) > 4.0 * tolerance + { + return Err(invalid("hole is not coplanar")); + } + if !radius.is_finite() || radius <= 4.0 * tolerance { + return Err(invalid("hole radius is below geometric resolution")); + } + Ok(()) +} + +pub(super) fn check_annular( + outer: &ProfileLoop, + holes: &[ProfileLoop], + tolerance: f64, +) -> Result<(), OperationError> { + if let ( + ProfileLoop::Circle { + frame: outer_frame, + radius, + }, + [ProfileLoop::Circle { + frame: inner_frame, + radius: inner_radius, + }], + ) = (outer, holes) + { + if norm(sub(inner_frame.origin, outer_frame.origin)) <= 4.0 * tolerance + && inner_radius + 4.0 * tolerance >= *radius + { + return Err(invalid("hole radius is not smaller than the profile")); + } + } + Ok(()) +} + +pub(super) fn check_layout( + outer: &ProfileLoop, + holes: &[ProfileLoop], + frame: Frame3, + tolerance: f64, +) -> Result<(), OperationError> { + let resolution = 4.0 * tolerance; + let outer = planar_loop(outer, frame); + let holes = holes + .iter() + .map(|hole| planar_loop(hole, frame)) + .collect::>(); + check_inside_outer(&outer, &holes, resolution)?; + check_holes_apart(&holes, resolution) +} + +fn check_inside_outer( + outer: &PlanarLoop, + holes: &[PlanarLoop], + resolution: f64, +) -> Result<(), OperationError> { + for hole in holes { + if !contains(outer, hole.probe, resolution) { + return Err(invalid("hole lies outside the profile")); + } + if touch(outer, hole, resolution) { + return Err(invalid("hole touches the profile")); + } + } + Ok(()) +} + +fn check_holes_apart(holes: &[PlanarLoop], resolution: f64) -> Result<(), OperationError> { + for (index, a) in holes.iter().enumerate() { + for b in &holes[index + 1..] { + if touch(a, b, resolution) { + return Err(invalid("holes overlap")); + } + if contains(a, b.probe, resolution) || contains(b, a.probe, resolution) { + return Err(invalid("holes are nested")); + } + } + } + Ok(()) +} + +fn planar_loop(profile: &ProfileLoop, frame: Frame3) -> PlanarLoop { + let edges = edges(profile, frame); + match profile { + ProfileLoop::Circle { frame: circle, .. } => { + let centre = frame.local(circle.origin); + PlanarLoop { + edges, + points: None, + probe: [centre[0], centre[1]], + } + } + ProfileLoop::Lines(_) => { + let points = edges + .iter() + .map(|edge| edge.endpoints().0) + .collect::>(); + PlanarLoop { + probe: points[0], + points: Some(points), + edges, + } + } + } +} + +fn contains(container: &PlanarLoop, point: [f64; 2], resolution: f64) -> bool { + match &container.points { + Some(points) => profile_point_inside(point, points), + None => arc_profile_contains(&container.edges, point, resolution), + } +} + +fn touch(a: &PlanarLoop, b: &PlanarLoop, resolution: f64) -> bool { + match (&a.points, &b.points) { + (Some(a), Some(b)) => loops_touch(a, b, resolution), + _ => a.edges.iter().any(|edge| { + b.edges + .iter() + .any(|other| !profile_edge_intersections(edge, other, resolution).is_empty()) + }), + } +} diff --git a/main/opengeometry/src/operations/creating/mod.rs b/main/opengeometry/src/operations/creating/mod.rs new file mode 100644 index 0000000..54e924a --- /dev/null +++ b/main/opengeometry/src/operations/creating/mod.rs @@ -0,0 +1,6 @@ +pub(crate) mod extrude; +mod holes; +mod profile; +pub(crate) mod sweep; + +pub(crate) use profile::{mapped_frame, ProfileLoop}; diff --git a/main/opengeometry/src/operations/creating/profile.rs b/main/opengeometry/src/operations/creating/profile.rs new file mode 100644 index 0000000..90858c0 --- /dev/null +++ b/main/opengeometry/src/operations/creating/profile.rs @@ -0,0 +1,162 @@ +use crate::brep::{Frame3, Similarity3, GROUND}; +use crate::geom2d::{self_intersects2, Pt2}; +use crate::math::{add, cross, dot, norm, scale, sub, Point3}; +use crate::operations::{invalid, OperationError}; +use crate::primitives::ProfileEdge; + +#[derive(Clone)] +pub(crate) enum ProfileLoop { + Circle { frame: Frame3, radius: f64 }, + Lines(Vec), +} + +pub(crate) fn mapped_frame(frame: Frame3, mapping: Similarity3) -> Frame3 { + Frame3 { + origin: mapping.apply_point(frame.origin), + x: mapping.apply_vector(frame.x), + y: mapping.apply_vector(frame.y), + z: mapping.apply_vector(frame.z), + } +} + +pub(super) fn profile_frame( + profile: &ProfileLoop, + tolerance: f64, +) -> Result { + match profile { + ProfileLoop::Circle { frame, radius } => Ok(circle_frame(*frame, *radius, tolerance)), + ProfileLoop::Lines(points) => lines_frame(points, tolerance), + } +} + +fn circle_frame(frame: Frame3, radius: f64, tolerance: f64) -> Frame3 { + let up = [0.0, 1.0, 0.0]; + if radius * norm(cross(frame.z, up)) <= 4.0 * tolerance && dot(frame.z, up) < 0.0 { + return Frame3 { + origin: frame.origin, + x: frame.x, + y: scale(frame.y, -1.0), + z: scale(frame.z, -1.0), + }; + } + frame +} + +fn lines_frame(points: &[Point3], tolerance: f64) -> Result { + let first = points[0]; + let normal = + newell_normal(points, tolerance).ok_or_else(|| degenerate_error(points, tolerance))?; + if points + .iter() + .any(|point| dot(sub(*point, first), normal).abs() > 4.0 * tolerance) + { + return Err(invalid("profile is not planar")); + } + if points + .iter() + .all(|point| (point[1] - first[1]).abs() <= 4.0 * tolerance) + { + return Ok(Frame3 { + origin: [0.0, first[1], 0.0], + ..GROUND + }); + } + Frame3::from_axis(first, normal, sub(points[1], first)).map_err(Into::into) +} + +pub(super) fn newell_normal(points: &[Point3], tolerance: f64) -> Option { + let first = points[0]; + let sum = (1..points.len() - 1).fold([0.0; 3], |sum, index| { + add( + sum, + cross(sub(points[index], first), sub(points[index + 1], first)), + ) + }); + let length = norm(sum); + let reach = norm(sub(farthest(points), first)); + (length > 4.0 * tolerance * reach).then(|| scale(sum, 1.0 / length)) +} + +fn degenerate_error(points: &[Point3], tolerance: f64) -> OperationError { + let first = points[0]; + let direction = sub(farthest(points), first); + let reach = norm(direction); + if points + .iter() + .any(|point| norm(cross(sub(*point, first), direction)) > 4.0 * tolerance * reach) + { + invalid("profile self-intersects") + } else { + invalid("profile is collinear") + } +} + +fn farthest(points: &[Point3]) -> Point3 { + let first = points[0]; + points + .iter() + .copied() + .max_by(|a, b| norm(sub(*a, first)).total_cmp(&norm(sub(*b, first)))) + .unwrap_or(first) +} + +fn circle_edges(frame: Frame3, radius: f64, base: Frame3) -> Vec { + let centre = base.local(frame.origin); + let x = [dot(frame.x, base.x), dot(frame.x, base.y)]; + let start_angle = x[1].atan2(x[0]); + vec![ + ProfileEdge::Arc { + center: [centre[0], centre[1]], + radius, + start_angle, + sweep_angle: std::f64::consts::PI, + }, + ProfileEdge::Arc { + center: [centre[0], centre[1]], + radius, + start_angle: start_angle + std::f64::consts::PI, + sweep_angle: std::f64::consts::PI, + }, + ] +} + +pub(super) fn edges(profile: &ProfileLoop, frame: Frame3) -> Vec { + match profile { + ProfileLoop::Circle { + frame: circle, + radius, + } => circle_edges(*circle, *radius, frame), + ProfileLoop::Lines(points) => points + .iter() + .enumerate() + .map(|(index, point)| { + let from = frame.local(*point); + let to = frame.local(points[(index + 1) % points.len()]); + ProfileEdge::Line { + from: [from[0], from[1]], + to: [to[0], to[1]], + } + }) + .collect(), + } +} + +pub(super) fn check_lines( + profile: &ProfileLoop, + frame: Frame3, + tolerance: f64, +) -> Result<(), OperationError> { + if let ProfileLoop::Lines(points) = profile { + let ring = points + .iter() + .map(|point| { + let local = frame.local(*point); + Pt2::new(local[0], local[1]) + }) + .collect::>(); + if self_intersects2(&ring, tolerance) { + return Err(invalid("profile self-intersects")); + } + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/creating/sweep/circle.rs b/main/opengeometry/src/operations/creating/sweep/circle.rs new file mode 100644 index 0000000..eab9e2c --- /dev/null +++ b/main/opengeometry/src/operations/creating/sweep/circle.rs @@ -0,0 +1,524 @@ +use crate::brep::{ + boundary, plane_boundary, unit, uv_line, Accuracy, BrepEnvelope, Builder, CurveGeometry, + EdgeGeometry, Frame3, GeometryError, Orientation, PcurveGeometry, SurfaceGeometry, Use, +}; +use crate::math::{add, cross, dot, norm, scale, sub, Interval, Point3}; +use crate::operations::OperationError; + +struct Ring { + centre: Point3, + frame: Frame3, + major: f64, + minor: f64, + vertices: [u32; 2], + edges: [u32; 2], +} + +struct SweptRings { + rings: Vec, + cylinders: Vec, + x: Point3, + offset: Point3, +} + +struct SurfacePoint { + surface: u32, + theta: f64, + v: f64, +} + +pub(super) fn build( + id: String, + profile: Frame3, + radius: f64, + path: Vec, + directions: Vec, + accuracy: Accuracy, +) -> Result { + let mut builder = Builder::new(id, accuracy)?; + let first_axis = directions[0]; + let SweptRings { + mut rings, + cylinders, + x, + offset, + } = add_rings( + &mut builder, + profile, + radius, + &path, + &directions, + first_axis, + )?; + let last = directions.len(); + let end_centre = add(path[last], offset); + rings.push(ring( + &mut builder, + end_centre, + directions[last - 1], + scale(x, radius), + x, + radius, + radius, + )?); + check_miter_planes(&rings, &cylinders, &directions, radius, last, accuracy)?; + let seams = add_seams(&mut builder, &rings, last)?; + let start_frame = Frame3::from_axis(rings[0].centre, scale(first_axis, -1.0), profile.x)?; + let end_frame = Frame3::from_axis(rings[last].centre, directions[last - 1], x)?; + add_caps( + &mut builder, + &rings, + last, + start_frame, + end_frame, + radius, + accuracy, + )?; + add_side_faces( + &mut builder, + &rings, + &cylinders, + &seams, + last, + radius, + accuracy, + )?; + Ok(builder.finish_solid()?) +} + +fn add_rings( + builder: &mut Builder, + profile: Frame3, + radius: f64, + path: &[Point3], + directions: &[Point3], + first_axis: Point3, +) -> Result { + let mut x = profile.x; + let mut offset = sub(profile.origin, path[0]); + let mut rings = Vec::with_capacity(path.len()); + rings.push(ring( + builder, + profile.origin, + first_axis, + scale(x, radius), + x, + radius, + radius, + )?); + let mut cylinders = Vec::with_capacity(directions.len()); + cylinders.push(Frame3::from_axis(add(path[0], offset), first_axis, x)?); + for joint in 1..path.len() - 1 { + let previous = directions[joint - 1]; + let bisector = unit(add(previous, directions[joint])) + .map_err(|_| OperationError::SweepSelfIntersection("sweep path reverses".into()))?; + let denominator = dot(previous, bisector); + let centre = add( + add(path[joint], offset), + scale(previous, -dot(offset, bisector) / denominator), + ); + let projection = + |vector: Point3| sub(vector, scale(previous, dot(vector, bisector) / denominator)); + let radial = scale(projection(x), radius); + let major_axis = unit(sub(previous, scale(bisector, denominator)))?; + let major = radius / denominator; + rings.push(ring( + builder, centre, bisector, radial, major_axis, major, radius, + )?); + offset = reflection(offset, bisector); + x = reflection(x, bisector); + cylinders.push(Frame3::from_axis( + add(path[joint], offset), + directions[joint], + x, + )?); + } + Ok(SweptRings { + rings, + cylinders, + x, + offset, + }) +} + +fn ring( + builder: &mut Builder, + centre: Point3, + axis: Point3, + radial: Point3, + major_axis: Point3, + major: f64, + minor: f64, +) -> Result { + let y_axis = unit(cross(axis, major_axis))?; + let frame = Frame3::from_axis(centre, axis, major_axis)?; + let start = (dot(radial, y_axis) / minor).atan2(dot(radial, major_axis) / major); + let curve = if (major - minor).abs() <= 1e-14 * major { + CurveGeometry::Circle { + frame, + radius: major, + } + } else { + CurveGeometry::Ellipse { + frame, + major_radius: major, + minor_radius: minor, + } + }; + let vertices = [ + builder.vertex(curve.elementary_point(start)?), + builder.vertex(curve.elementary_point(start + std::f64::consts::PI)?), + ]; + let edges = [ + builder.edge( + curve.clone(), + Interval::new(start, start + std::f64::consts::PI).map_err(GeometryError::from)?, + false, + ), + builder.edge( + curve, + Interval::new(start + std::f64::consts::PI, start + std::f64::consts::TAU) + .map_err(GeometryError::from)?, + false, + ), + ]; + Ok(Ring { + centre, + frame, + major, + minor, + vertices, + edges, + }) +} + +fn reflection(vector: Point3, normal: Point3) -> Point3 { + sub(vector, scale(normal, 2.0 * dot(vector, normal))) +} + +fn check_miter_planes( + rings: &[Ring], + cylinders: &[Frame3], + directions: &[Point3], + radius: f64, + last: usize, + accuracy: Accuracy, +) -> Result<(), OperationError> { + for segment in 0..last { + let axis = directions[segment]; + let start = &rings[segment]; + let end = &rings[segment + 1]; + let start_denominator = dot(axis, start.frame.z); + let end_denominator = dot(axis, end.frame.z); + let x = cylinders[segment].x; + let y = cylinders[segment].y; + let coefficient_x = + dot(x, end.frame.z) / end_denominator - dot(x, start.frame.z) / start_denominator; + let coefficient_y = + dot(y, end.frame.z) / end_denominator - dot(y, start.frame.z) / start_denominator; + let minimum = + dot(sub(end.centre, start.centre), axis) - radius * coefficient_x.hypot(coefficient_y); + if minimum <= 4.0 * accuracy.geometric { + return Err(OperationError::SweepSelfIntersection( + "circular miter planes meet within a segment".into(), + )); + } + } + Ok(()) +} + +fn add_seams( + builder: &mut Builder, + rings: &[Ring], + last: usize, +) -> Result, OperationError> { + let mut seams = Vec::with_capacity(last); + for segment in 0..last { + seams.push([ + line_edge( + builder, + rings[segment].vertices[0], + rings[segment + 1].vertices[0], + )?, + line_edge( + builder, + rings[segment].vertices[1], + rings[segment + 1].vertices[1], + )?, + ]); + } + Ok(seams) +} + +fn line_edge(builder: &mut Builder, from: u32, to: u32) -> Result { + let a = builder.brep.topology.vertices[from as usize].position; + let b = builder.brep.topology.vertices[to as usize].position; + let delta = sub(b, a); + let length = norm(delta); + if length <= 4.0 * builder.brep.accuracy.geometric { + return Err(OperationError::SweepSelfIntersection( + "circular sweep seam collapses".into(), + )); + } + Ok(builder.edge( + CurveGeometry::Line { + origin: a, + direction: unit(delta)?, + }, + Interval::new(0.0, length).map_err(GeometryError::from)?, + false, + )) +} + +fn add_caps( + builder: &mut Builder, + rings: &[Ring], + last: usize, + start_frame: Frame3, + end_frame: Frame3, + radius: f64, + accuracy: Accuracy, +) -> Result<(), OperationError> { + let start_uses = vec![ + plane_boundary( + builder, + start_frame, + rings[0].edges[1], + rings[0].vertices[0], + rings[0].vertices[1], + Orientation::Reverse, + )?, + plane_boundary( + builder, + start_frame, + rings[0].edges[0], + rings[0].vertices[1], + rings[0].vertices[0], + Orientation::Reverse, + )?, + ]; + let end_uses = vec![ + plane_boundary( + builder, + end_frame, + rings[last].edges[0], + rings[last].vertices[0], + rings[last].vertices[1], + Orientation::Forward, + )?, + plane_boundary( + builder, + end_frame, + rings[last].edges[1], + rings[last].vertices[1], + rings[last].vertices[0], + Orientation::Forward, + )?, + ]; + builder.face( + "cap-start", + SurfaceGeometry::Plane { frame: start_frame }, + [[-radius - accuracy.geometric, radius + accuracy.geometric]; 2], + start_uses, + )?; + builder.face( + "cap-end", + SurfaceGeometry::Plane { frame: end_frame }, + [[-radius - accuracy.geometric, radius + accuracy.geometric]; 2], + end_uses, + )?; + Ok(()) +} + +fn add_side_faces( + builder: &mut Builder, + rings: &[Ring], + cylinders: &[Frame3], + seams: &[[u32; 2]], + last: usize, + radius: f64, + accuracy: Accuracy, +) -> Result<(), OperationError> { + for segment in 0..last { + let cylinder = cylinders[segment]; + let low = v_extent(&rings[segment], cylinder.origin, cylinder.z); + let high = v_extent(&rings[segment + 1], cylinder.origin, cylinder.z); + let v_bounds = [ + low.0.min(high.0) - accuracy.geometric, + low.1.max(high.1) + accuracy.geometric, + ]; + for half in 0..2 { + let surface = builder.brep.geometry.surfaces.len() as u32; + let uses = side_uses(builder, rings, seams, segment, half, surface, cylinder)?; + builder.face( + &format!("seg-{segment}:side-0-{half}"), + SurfaceGeometry::Cylinder { + frame: cylinder, + radius, + }, + [ + [ + half as f64 * std::f64::consts::PI, + (half + 1) as f64 * std::f64::consts::PI, + ], + v_bounds, + ], + uses, + )?; + } + } + Ok(()) +} + +fn v_extent(ring: &Ring, base: Point3, axis: Point3) -> (f64, f64) { + let centre = dot(sub(ring.centre, base), axis); + let amplitude = + (ring.major * dot(ring.frame.x, axis)).hypot(ring.minor * dot(ring.frame.y, axis)); + (centre - amplitude, centre + amplitude) +} + +fn side_uses( + builder: &Builder, + rings: &[Ring], + seams: &[[u32; 2]], + segment: usize, + half: usize, + surface: u32, + cylinder: Frame3, +) -> Result, OperationError> { + let next = (half + 1) % 2; + let lower = &rings[segment]; + let upper = &rings[segment + 1]; + let start_v = dot(sub(lower.centre, cylinder.origin), cylinder.z); + let end_v = dot(sub(upper.centre, cylinder.origin), cylinder.z); + Ok(vec![ + ring_use( + builder, + lower.edges[half], + &SurfacePoint { + surface, + theta: half as f64 * std::f64::consts::PI, + v: start_v, + }, + lower.vertices[half], + lower.vertices[next], + Orientation::Forward, + segment == 0, + )?, + seam_use( + builder, + seams[segment][next], + lower.vertices[next], + upper.vertices[next], + (half + 1) as f64 * std::f64::consts::PI, + cylinder, + Orientation::Forward, + )?, + ring_use( + builder, + upper.edges[half], + &SurfacePoint { + surface, + theta: half as f64 * std::f64::consts::PI, + v: end_v, + }, + upper.vertices[next], + upper.vertices[half], + Orientation::Reverse, + segment + 2 == rings.len(), + )?, + seam_use( + builder, + seams[segment][half], + upper.vertices[half], + lower.vertices[half], + half as f64 * std::f64::consts::PI, + cylinder, + Orientation::Reverse, + )?, + ]) +} + +fn ring_use( + builder: &Builder, + edge: u32, + point: &SurfacePoint, + from: u32, + to: u32, + sense: Orientation, + cap: bool, +) -> Result { + if cap { + return Ok(boundary( + edge, + from, + to, + sense, + uv_line([0.0, point.v], [1.0, 0.0]), + )); + } + projected(builder, edge, point, from, to, sense) +} + +fn projected( + builder: &Builder, + edge: u32, + point: &SurfacePoint, + from: u32, + to: u32, + sense: Orientation, +) -> Result { + let SurfacePoint { surface, theta, v } = *point; + let EdgeGeometry::Curve { curve, range } = builder.brep.topology.edges[edge as usize].geometry + else { + return Err(OperationError::InvalidTopology( + "sweep edge has no curve".into(), + )); + }; + Ok(boundary( + edge, + from, + to, + sense, + PcurveGeometry::ProjectedCurve { + curve, + surface, + chart: 0, + uv_hint: [theta, v], + uv_rate: [1.0, 0.0], + parameter_origin: range.lo, + }, + )) +} + +fn seam_use( + builder: &Builder, + edge: u32, + from: u32, + to: u32, + theta: f64, + cylinder: Frame3, + sense: Orientation, +) -> Result { + let EdgeGeometry::Curve { curve, .. } = builder.brep.topology.edges[edge as usize].geometry + else { + return Err(OperationError::InvalidTopology( + "sweep seam has no curve".into(), + )); + }; + let CurveGeometry::Line { origin, direction } = builder.brep.geometry.curves[curve as usize] + else { + return Err(OperationError::InvalidTopology( + "sweep seam is not a line".into(), + )); + }; + let v = dot(sub(origin, cylinder.origin), cylinder.z); + let dv = dot(direction, cylinder.z); + Ok(boundary( + edge, + from, + to, + sense, + uv_line([theta, v], [0.0, dv]), + )) +} diff --git a/main/opengeometry/src/operations/creating/sweep/mod.rs b/main/opengeometry/src/operations/creating/sweep/mod.rs new file mode 100644 index 0000000..973a1ec --- /dev/null +++ b/main/opengeometry/src/operations/creating/sweep/mod.rs @@ -0,0 +1,441 @@ +mod circle; +#[cfg(test)] +mod tests; + +use super::extrude; +use super::profile::{check_lines, newell_normal, profile_frame, ProfileLoop}; +use crate::brep::{ + padded_uv_bounds, plane_boundary, unit, Accuracy, BrepEnvelope, Builder, CurveGeometry, Frame3, + GeometryError, Orientation, SurfaceGeometry, Use, +}; +use crate::math::{add, cross, dot, norm, scale, sub, Interval, Point3}; +use crate::operations::{invalid, OperationError}; +use circle as sweep_circle; + +struct SweepTopology { + vertices: Vec>, + ring_edges: Vec>, + longitudinal: Vec>, +} + +pub(crate) fn build( + id: String, + profile: ProfileLoop, + mut path: Vec, + accuracy: Accuracy, +) -> Result { + if path.len() < 2 || path.iter().flatten().any(|value| !value.is_finite()) { + return Err(invalid("sweep path is invalid")); + } + remove_collinear_joints(&mut path, accuracy)?; + let directions = path_directions(&path, accuracy)?; + let frame = profile_frame(&profile, accuracy.geometric)?; + check_lines(&profile, frame, accuracy.geometric)?; + if dot(sub(path[0], frame.origin), frame.z).abs() > 4.0 * accuracy.geometric + || norm(cross(frame.z, directions[0])) > 1e-9 + { + return Err(invalid( + "profile plane is not at the start and perpendicular to the path", + )); + } + if directions.len() == 1 { + return extrude::build( + id, + profile, + Vec::new(), + dot(sub(path[1], path[0]), frame.z), + accuracy, + ); + } + check_segment_overlap(&profile, &path, &directions, accuracy)?; + let points = match profile { + ProfileLoop::Lines(points) => points, + ProfileLoop::Circle { frame, radius } => { + return sweep_circle::build(id, frame, radius, path, directions, accuracy) + } + }; + let mut polygon = points; + let area_normal = newell_normal(&polygon, accuracy.geometric) + .ok_or_else(|| invalid("profile is collinear"))?; + if dot(area_normal, directions[0]) < 0.0 { + polygon.reverse(); + } + let count = polygon.len(); + let rings = miter_rings(polygon, &path, &directions, count)?; + check_miter_spans(&rings, &directions, count, accuracy)?; + let mut builder = Builder::new(id, accuracy)?; + let topology = add_sweep_topology(&mut builder, &rings, &directions, count)?; + add_caps( + &mut builder, + &topology, + &path, + &directions, + &rings, + count, + accuracy, + )?; + add_side_faces( + &mut builder, + &topology, + &directions, + &rings, + count, + accuracy, + )?; + Ok(builder.finish_solid()?) +} + +fn remove_collinear_joints( + path: &mut Vec, + accuracy: Accuracy, +) -> Result<(), OperationError> { + let mut index = 1; + while index + 1 < path.len() { + let before = sub(path[index], path[index - 1]); + let after = sub(path[index + 1], path[index]); + if norm(before) <= 4.0 * accuracy.geometric || norm(after) <= 4.0 * accuracy.geometric { + return Err(invalid("sweep path has a short segment")); + } + let (u, v) = (unit(before)?, unit(after)?); + if norm(add(u, v)) <= 1e-9 { + return Err(OperationError::SweepSelfIntersection( + "sweep path reverses".into(), + )); + } + if norm(cross(u, v)) <= 1e-9 && dot(u, v) > 0.0 { + path.remove(index); + } else { + index += 1; + } + } + Ok(()) +} + +fn path_directions(path: &[Point3], accuracy: Accuracy) -> Result, OperationError> { + path.windows(2) + .map(|pair| { + let delta = sub(pair[1], pair[0]); + if norm(delta) <= 4.0 * accuracy.geometric { + return Err(invalid("sweep path has a short segment")); + } + unit(delta).map_err(Into::into) + }) + .collect::, OperationError>>() +} + +fn check_segment_overlap( + profile: &ProfileLoop, + path: &[Point3], + directions: &[Point3], + accuracy: Accuracy, +) -> Result<(), OperationError> { + let radius = match profile { + ProfileLoop::Lines(points) => points + .iter() + .map(|point| norm(sub(*point, path[0]))) + .fold(0.0_f64, f64::max), + ProfileLoop::Circle { frame, radius } => norm(sub(frame.origin, path[0])) + radius, + }; + for first in 0..directions.len() { + for second in first + 2..directions.len() { + if segment_distance(path[first], path[first + 1], path[second], path[second + 1]) + <= 2.0 * radius + 4.0 * accuracy.geometric + { + return Err(OperationError::SweepSelfIntersection( + "non-adjacent sweep segments overlap".into(), + )); + } + } + } + Ok(()) +} + +fn segment_distance(a: Point3, b: Point3, c: Point3, d: Point3) -> f64 { + let u = sub(b, a); + let v = sub(d, c); + let w = sub(a, c); + let (aa, bb, cc, dd, ee) = (dot(u, u), dot(u, v), dot(v, v), dot(u, w), dot(v, w)); + let denominator = aa * cc - bb * bb; + let mut s = if denominator.abs() <= 1e-20 { + 0.0 + } else { + ((bb * ee - cc * dd) / denominator).clamp(0.0, 1.0) + }; + let t = ((bb * s + ee) / cc).clamp(0.0, 1.0); + s = ((bb * t - dd) / aa).clamp(0.0, 1.0); + norm(sub(add(a, scale(u, s)), add(c, scale(v, t)))) +} + +fn miter_rings( + polygon: Vec, + path: &[Point3], + directions: &[Point3], + count: usize, +) -> Result>, OperationError> { + let mut offsets = polygon + .iter() + .map(|point| sub(*point, path[0])) + .collect::>(); + let mut rings = Vec::with_capacity(path.len()); + rings.push(polygon); + for joint in 1..path.len() - 1 { + let previous = directions[joint - 1]; + let bisector = unit(add(previous, directions[joint])) + .map_err(|_| OperationError::SweepSelfIntersection("sweep path reverses".into()))?; + let mut ring = Vec::with_capacity(count); + for offset in &mut offsets { + let along = -dot(*offset, bisector) / dot(previous, bisector); + ring.push(add(path[joint], add(*offset, scale(previous, along)))); + *offset = sub(*offset, scale(bisector, 2.0 * dot(*offset, bisector))); + } + rings.push(ring); + } + rings.push( + offsets + .iter() + .map(|offset| add(path[path.len() - 1], *offset)) + .collect(), + ); + Ok(rings) +} + +fn check_miter_spans( + rings: &[Vec], + directions: &[Point3], + count: usize, + accuracy: Accuracy, +) -> Result<(), OperationError> { + for segment in 0..directions.len() { + for vertex in 0..count { + let span = dot( + sub(rings[segment + 1][vertex], rings[segment][vertex]), + directions[segment], + ); + if span <= 4.0 * accuracy.geometric { + return Err(OperationError::SweepSelfIntersection( + "miter planes meet within a segment".into(), + )); + } + } + } + Ok(()) +} + +fn add_sweep_topology( + builder: &mut Builder, + rings: &[Vec], + directions: &[Point3], + count: usize, +) -> Result { + let vertices = rings + .iter() + .map(|ring| { + ring.iter() + .map(|point| builder.vertex(*point)) + .collect::>() + }) + .collect::>(); + let mut ring_edges = Vec::with_capacity(rings.len()); + for ring in &vertices { + ring_edges.push( + (0..count) + .map(|edge| line_edge(builder, ring[edge], ring[(edge + 1) % count])) + .collect::, _>>()?, + ); + } + let mut longitudinal = Vec::with_capacity(directions.len()); + for segment in 0..directions.len() { + longitudinal.push( + (0..count) + .map(|vertex| { + line_edge( + builder, + vertices[segment][vertex], + vertices[segment + 1][vertex], + ) + }) + .collect::, _>>()?, + ); + } + Ok(SweepTopology { + vertices, + ring_edges, + longitudinal, + }) +} + +fn line_edge(builder: &mut Builder, from: u32, to: u32) -> Result { + let a = builder.brep.topology.vertices[from as usize].position; + let b = builder.brep.topology.vertices[to as usize].position; + let delta = sub(b, a); + let length = norm(delta); + if length <= 4.0 * builder.brep.accuracy.geometric { + return Err(OperationError::SweepSelfIntersection( + "sweep edge collapses".into(), + )); + } + Ok(builder.edge( + CurveGeometry::Line { + origin: a, + direction: unit(delta)?, + }, + Interval::new(0.0, length).map_err(GeometryError::from)?, + false, + )) +} + +fn add_caps( + builder: &mut Builder, + topology: &SweepTopology, + path: &[Point3], + directions: &[Point3], + rings: &[Vec], + count: usize, + accuracy: Accuracy, +) -> Result<(), OperationError> { + let SweepTopology { + vertices, + ring_edges, + .. + } = topology; + let start_frame = Frame3::from_axis( + path[0], + scale(directions[0], -1.0), + sub(rings[0][1], rings[0][0]), + )?; + let last = directions.len(); + let end_frame = Frame3::from_axis( + path[last], + directions[last - 1], + sub(rings[last][1], rings[last][0]), + )?; + let mut start_uses = Vec::new(); + let mut end_uses = Vec::new(); + for edge in (0..count).rev() { + start_uses.push(plane_boundary( + builder, + start_frame, + ring_edges[0][edge], + vertices[0][(edge + 1) % count], + vertices[0][edge], + Orientation::Reverse, + )?); + } + for edge in 0..count { + end_uses.push(plane_boundary( + builder, + end_frame, + ring_edges[last][edge], + vertices[last][edge], + vertices[last][(edge + 1) % count], + Orientation::Forward, + )?); + } + builder.face( + "cap-start", + SurfaceGeometry::Plane { frame: start_frame }, + padded_uv_bounds( + rings[0].iter().map(|point| start_frame.local(*point)), + accuracy.geometric, + ), + start_uses, + )?; + builder.face( + "cap-end", + SurfaceGeometry::Plane { frame: end_frame }, + padded_uv_bounds( + rings[last].iter().map(|point| end_frame.local(*point)), + accuracy.geometric, + ), + end_uses, + )?; + Ok(()) +} + +fn add_side_faces( + builder: &mut Builder, + topology: &SweepTopology, + directions: &[Point3], + rings: &[Vec], + count: usize, + accuracy: Accuracy, +) -> Result<(), OperationError> { + for segment in 0..directions.len() { + for edge in 0..count { + let next = (edge + 1) % count; + let origin = rings[segment][edge]; + let along_edge = unit(sub(rings[segment][next], origin))?; + let side_frame = Frame3::from_axis( + origin, + unit(cross(along_edge, directions[segment]))?, + along_edge, + )?; + let uses = side_uses(builder, topology, segment, edge, next, side_frame)?; + let quad = [ + rings[segment][edge], + rings[segment][next], + rings[segment + 1][edge], + rings[segment + 1][next], + ]; + builder.face( + &format!("seg-{segment}:side-0-{edge}"), + SurfaceGeometry::Plane { frame: side_frame }, + padded_uv_bounds( + quad.iter().map(|point| side_frame.local(*point)), + accuracy.geometric, + ), + uses, + )?; + } + } + Ok(()) +} + +fn side_uses( + builder: &Builder, + topology: &SweepTopology, + segment: usize, + edge: usize, + next: usize, + side_frame: Frame3, +) -> Result, OperationError> { + let SweepTopology { + vertices, + ring_edges, + longitudinal, + } = topology; + Ok(vec![ + plane_boundary( + builder, + side_frame, + ring_edges[segment][edge], + vertices[segment][edge], + vertices[segment][next], + Orientation::Forward, + )?, + plane_boundary( + builder, + side_frame, + longitudinal[segment][next], + vertices[segment][next], + vertices[segment + 1][next], + Orientation::Forward, + )?, + plane_boundary( + builder, + side_frame, + ring_edges[segment + 1][edge], + vertices[segment + 1][next], + vertices[segment + 1][edge], + Orientation::Reverse, + )?, + plane_boundary( + builder, + side_frame, + longitudinal[segment][edge], + vertices[segment + 1][edge], + vertices[segment][edge], + Orientation::Reverse, + )?, + ]) +} diff --git a/main/opengeometry/src/operations/creating/sweep/tests.rs b/main/opengeometry/src/operations/creating/sweep/tests.rs new file mode 100644 index 0000000..819c88a --- /dev/null +++ b/main/opengeometry/src/operations/creating/sweep/tests.rs @@ -0,0 +1,132 @@ +use super::build; +use crate::brep::{ + coarse_accuracy, BrepEnvelope, CurveGeometry, Edge, EdgeGeometry, PcurveGeometry, + SurfaceGeometry, GROUND, +}; +use crate::math::Point3; +use crate::operations::creating::ProfileLoop; + +const L_PATH: [Point3; 3] = [[0.0, 0.0, 0.0], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]]; +const SPATIAL_PATH: [Point3; 4] = [ + [0.0, 0.0, 0.0], + [0.0, 3.0, 0.0], + [3.0, 3.0, 0.0], + [3.0, 3.0, 3.0], +]; + +fn circular_sweep(radius: f64, path: &[Point3]) -> BrepEnvelope { + build( + "solid".into(), + ProfileLoop::Circle { + frame: GROUND, + radius, + }, + path.to_vec(), + coarse_accuracy(1e-6), + ) + .unwrap() +} + +fn edge_uses<'a>( + body: &'a BrepEnvelope, + edge: &Edge, +) -> Vec<(&'a SurfaceGeometry, &'a PcurveGeometry)> { + [Some(edge.halfedge), edge.twin_halfedge] + .into_iter() + .flatten() + .map(|id| { + let halfedge = &body.topology.halfedges[id as usize]; + let face = &body.topology.faces[halfedge.face.unwrap() as usize]; + ( + &body.geometry.surfaces[face.surface as usize], + &body.geometry.pcurves[halfedge.geometry_use.pcurve.unwrap() as usize], + ) + }) + .collect() +} + +fn is_line(body: &BrepEnvelope, edge: &Edge) -> bool { + matches!( + edge.geometry, + EdgeGeometry::Curve { curve, .. } + if matches!(body.geometry.curves[curve as usize], CurveGeometry::Line { .. }) + ) +} + +fn edge_class_counts(body: &BrepEnvelope) -> [usize; 3] { + let mut counts = [0; 3]; + for edge in &body.topology.edges { + let uses = edge_uses(body, edge); + assert_eq!(uses.len(), 2); + let on_plane = |surface: &SurfaceGeometry| matches!(surface, SurfaceGeometry::Plane { .. }); + if is_line(body, edge) { + assert!(uses.iter().all(|(surface, pcurve)| { + matches!(surface, SurfaceGeometry::Cylinder { .. }) + && matches!(pcurve, PcurveGeometry::Line2 { .. }) + })); + counts[1] += 1; + } else if uses.iter().any(|(surface, _)| on_plane(surface)) { + for (surface, pcurve) in &uses { + if on_plane(surface) { + assert!(matches!(pcurve, PcurveGeometry::Conic2 { .. })); + } else { + assert!(matches!(surface, SurfaceGeometry::Cylinder { .. })); + assert!(matches!( + pcurve, + PcurveGeometry::Line2 { direction, .. } if *direction == [1.0, 0.0] + )); + } + } + counts[0] += 1; + } else { + assert!(uses + .iter() + .all(|(_, pcurve)| matches!(pcurve, PcurveGeometry::ProjectedCurve { .. }))); + counts[2] += 1; + } + } + counts +} + +fn checked_chart_seams(body: &BrepEnvelope) -> usize { + let surface = |halfedge: u32| { + let face = body.topology.halfedges[halfedge as usize].face.unwrap(); + body.topology.faces[face as usize].surface + }; + let mut checked = 0; + for edge in body.topology.edges.iter().filter(|edge| edge.chart_seam) { + let Some(twin) = edge.twin_halfedge else { + continue; + }; + assert_eq!(surface(edge.halfedge), surface(twin)); + checked += 1; + } + checked +} + +#[test] +fn circular_sweep_pcurve_kinds_per_edge_class() { + assert_eq!(edge_class_counts(&circular_sweep(0.5, &L_PATH)), [4, 4, 2]); + assert_eq!( + edge_class_counts(&circular_sweep(0.4, &SPATIAL_PATH)), + [4, 6, 4] + ); +} + +#[test] +fn no_chart_seam_edge_spans_two_surfaces() { + let straight = circular_sweep(0.5, &[[0.0, 0.0, 0.0], [0.0, 3.0, 0.0]]); + assert!(checked_chart_seams(&straight) > 0); + for body in [ + circular_sweep(0.5, &L_PATH), + circular_sweep(0.4, &SPATIAL_PATH), + ] { + checked_chart_seams(&body); + assert!(body + .topology + .edges + .iter() + .filter(|edge| is_line(&body, edge)) + .all(|edge| !edge.chart_seam)); + } +} diff --git a/main/opengeometry/src/operations/error.rs b/main/opengeometry/src/operations/error.rs new file mode 100644 index 0000000..8dcb8f9 --- /dev/null +++ b/main/opengeometry/src/operations/error.rs @@ -0,0 +1,23 @@ +use crate::brep::GeometryError; + +#[derive(Clone, Debug, PartialEq)] +pub enum OperationError { + InvalidParameter(String), + SweepSelfIntersection(String), + InvalidTopology(String), + Geometry(GeometryError), +} + +impl From for OperationError { + fn from(value: GeometryError) -> Self { + Self::Geometry(value) + } +} + +impl std::fmt::Display for OperationError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "{self:?}") + } +} + +impl std::error::Error for OperationError {} diff --git a/main/opengeometry/src/operations/extrude.rs b/main/opengeometry/src/operations/extrude.rs deleted file mode 100644 index a26001d..0000000 --- a/main/opengeometry/src/operations/extrude.rs +++ /dev/null @@ -1,588 +0,0 @@ -use super::windingsort; -use crate::brep::{Brep, BrepBuilder}; -use crate::operations::triangulate::compute_polygon_normal; -use openmaths::Vector3; -use uuid::Uuid; -use wasm_bindgen::prelude::*; - -const EXTRUDE_EPSILON: f64 = crate::tolerance::MODELING_TOLERANCE_FLOOR; - -/// How an extrusion grows relative to the sketch/profile plane (D3). -#[derive(Clone, Copy, Debug)] -pub enum ExtrudeExtent { - /// Grows `distance` along the direction, profile stays on the start cap. - OneSided { distance: f64 }, - /// Grows `distance/2` on each side of the profile plane. - Symmetric { distance: f64 }, - /// Grows `plus` along the direction and `minus` against it (distinct). - TwoSided { plus: f64, minus: f64 }, -} - -impl ExtrudeExtent { - /// The (base-plane shift, extrusion vector) for a unit `direction`. The - /// source cap is built at `profile + shift`; the end cap at `+ vector`. - fn resolve(self, direction: Vector3) -> (Vector3, Vector3) { - let scaled = |t: f64| Vector3::new(direction.x * t, direction.y * t, direction.z * t); - match self { - ExtrudeExtent::OneSided { distance } => (scaled(0.0), scaled(distance)), - ExtrudeExtent::Symmetric { distance } => (scaled(-distance / 2.0), scaled(distance)), - ExtrudeExtent::TwoSided { plus, minus } => (scaled(-minus), scaled(plus + minus)), - } - } - - /// Total span; must be finite and non-zero for a valid extrusion. - fn span(self) -> f64 { - match self { - ExtrudeExtent::OneSided { distance } | ExtrudeExtent::Symmetric { distance } => { - distance - } - ExtrudeExtent::TwoSided { plus, minus } => plus + minus, - } - } -} - -pub fn extrude_profile_loops( - brep_id: Uuid, - outer: &[Vector3], - holes: &[Vec], - height: f64, -) -> Result { - // Backwards-compatible default: blind extrude up the world +Y axis. - extrude_profile_loops_with( - brep_id, - outer, - holes, - Vector3::new(0.0, 1.0, 0.0), - ExtrudeExtent::OneSided { distance: height }, - ) -} - -/// General extrude: arbitrary `direction` (need not be unit; it is normalized) -/// and an [`ExtrudeExtent`] (one-sided, symmetric, or two-sided). -pub fn extrude_profile_loops_with( - brep_id: Uuid, - outer: &[Vector3], - holes: &[Vec], - direction: Vector3, - extent: ExtrudeExtent, -) -> Result { - let span = extent.span(); - if !span.is_finite() || span.abs() <= EXTRUDE_EPSILON { - return Err("Extrude extent must be a finite non-zero distance".to_string()); - } - - let dir_len = - (direction.x * direction.x + direction.y * direction.y + direction.z * direction.z).sqrt(); - if !dir_len.is_finite() || dir_len <= EXTRUDE_EPSILON { - return Err("Extrude direction must be a non-zero vector".to_string()); - } - let direction = Vector3::new( - direction.x / dir_len, - direction.y / dir_len, - direction.z / dir_len, - ); - let (base_shift, extrusion) = extent.resolve(direction); - - let outer = sanitize_loop_points(outer); - if outer.len() < 3 { - return Ok(Brep::new(brep_id)); - } - - let holes: Vec> = holes - .iter() - .map(|hole| sanitize_loop_points(hole)) - .filter(|hole| hole.len() >= 3) - .collect(); - - // Shift the profile onto the start cap (no-op for one-sided extents). - let shift = - |p: &Vector3| Vector3::new(p.x + base_shift.x, p.y + base_shift.y, p.z + base_shift.z); - let outer: Vec = outer.iter().map(shift).collect(); - let holes: Vec> = holes - .iter() - .map(|hole| hole.iter().map(shift).collect()) - .collect(); - - let Some(profile_normal) = compute_polygon_normal(&outer) else { - return Err("Failed to compute a stable normal from the extrusion profile".to_string()); - }; - - let flip_source_face = profile_normal.dot(&extrusion) > 0.0; - - let mut builder = BrepBuilder::new(brep_id); - let mut source_positions = Vec::new(); - - source_positions.extend(outer.iter().copied()); - for hole in &holes { - source_positions.extend(hole.iter().copied()); - } - - let end_positions: Vec = source_positions - .iter() - .map(|point| { - Vector3::new( - point.x + extrusion.x, - point.y + extrusion.y, - point.z + extrusion.z, - ) - }) - .collect(); - - let mut all_positions = source_positions.clone(); - all_positions.extend(end_positions.iter().copied()); - builder.add_vertices(&all_positions); - - let mut next_index = 0u32; - let source_outer = range_indices(next_index, outer.len()); - next_index += outer.len() as u32; - - let mut source_holes = Vec::with_capacity(holes.len()); - for hole in &holes { - let indices = range_indices(next_index, hole.len()); - next_index += hole.len() as u32; - source_holes.push(indices); - } - - let end_offset = source_positions.len() as u32; - let end_outer: Vec = source_outer - .iter() - .map(|index| index + end_offset) - .collect(); - let end_holes: Vec> = source_holes - .iter() - .map(|hole| hole.iter().map(|index| index + end_offset).collect()) - .collect(); - - builder - .add_face( - &orient_loop_indices(&source_outer, flip_source_face), - &orient_hole_indices(&source_holes, flip_source_face), - ) - .map_err(|error| format!("Failed to build source extrusion cap: {}", error))?; - - builder - .add_face( - &orient_loop_indices(&end_outer, !flip_source_face), - &orient_hole_indices(&end_holes, !flip_source_face), - ) - .map_err(|error| format!("Failed to build extruded cap: {}", error))?; - - add_side_faces(&mut builder, &source_outer, &end_outer, flip_source_face)?; - - for (source_hole, end_hole) in source_holes.iter().zip(end_holes.iter()) { - add_side_faces(&mut builder, source_hole, end_hole, flip_source_face)?; - } - - builder - .add_shell_from_all_faces(true) - .map_err(|error| format!("Failed to build extrusion shell: {}", error))?; - - builder - .build() - .map_err(|error| format!("Failed to finalize extrusion BREP: {}", error)) -} - -pub fn try_extrude_brep_face(brep_face: Brep, height: f64) -> Result { - if let Some(face) = brep_face.faces.first() { - let (outer, holes) = brep_face.get_vertices_and_holes_by_face_id(face.id); - return extrude_profile_loops(brep_face.id, &outer, &holes, height); - } - - if let Some(wire) = brep_face.wires.first() { - let outer = brep_face - .get_wire_vertex_indices(wire.id) - .into_iter() - .filter_map(|vertex_id| brep_face.vertices.get(vertex_id as usize)) - .map(|vertex| vertex.position) - .collect::>(); - - return extrude_profile_loops(brep_face.id, &outer, &[], height); - } - - let base_points = windingsort::ccw_test(brep_face.get_flattened_vertices()); - extrude_profile_loops(brep_face.id, &base_points, &[], height) -} - -pub fn extrude_brep_face(brep_face: Brep, height: f64) -> Brep { - let brep_id = brep_face.id; - try_extrude_brep_face(brep_face, height).unwrap_or_else(|_| Brep::new(brep_id)) -} - -#[wasm_bindgen(js_name = extrudeBrepFace)] -pub fn extrude_brep_face_wasm( - local_brep_serialized: String, - height: f64, -) -> Result { - let brep: Brep = serde_json::from_str(&local_brep_serialized).map_err(|error| { - JsValue::from_str(&format!("Invalid local BRep JSON payload: {}", error)) - })?; - let extruded = try_extrude_brep_face(brep, height) - .map_err(|error| JsValue::from_str(&format!("Failed to extrude BRep face: {}", error)))?; - extruded.validate_topology().map_err(|error| { - JsValue::from_str(&format!("Extruded BRep topology is invalid: {}", error)) - })?; - serde_json::to_string(&extruded).map_err(|error| { - JsValue::from_str(&format!("Failed to serialize extruded BRep: {}", error)) - }) -} - -/// Wasm entry point for general extrude. `direction` is `[x,y,z]`; `extent_kind` -/// is `"one_sided" | "symmetric" | "two_sided"`; `d_plus`/`d_minus` are the -/// extent distances (`d_minus` ignored unless two-sided, `d_plus` is the -/// distance for one-sided/symmetric). -#[wasm_bindgen(js_name = extrudeBrepFaceDirectional)] -pub fn extrude_brep_face_directional_wasm( - local_brep_serialized: String, - direction: Vec, - extent_kind: String, - d_plus: f64, - d_minus: f64, -) -> Result { - if direction.len() != 3 { - return Err(JsValue::from_str( - "direction must be a 3-element [x,y,z] array", - )); - } - let dir = Vector3::new(direction[0], direction[1], direction[2]); - let extent = match extent_kind.as_str() { - "one_sided" => ExtrudeExtent::OneSided { distance: d_plus }, - "symmetric" => ExtrudeExtent::Symmetric { distance: d_plus }, - "two_sided" => ExtrudeExtent::TwoSided { - plus: d_plus, - minus: d_minus, - }, - other => { - return Err(JsValue::from_str(&format!( - "Unknown extent kind '{}': expected one_sided | symmetric | two_sided", - other - ))) - } - }; - - let brep: Brep = serde_json::from_str(&local_brep_serialized).map_err(|error| { - JsValue::from_str(&format!("Invalid local BRep JSON payload: {}", error)) - })?; - - let (outer, holes) = if let Some(face) = brep.faces.first() { - brep.get_vertices_and_holes_by_face_id(face.id) - } else { - ( - windingsort::ccw_test(brep.get_flattened_vertices()), - Vec::new(), - ) - }; - - let extruded = extrude_profile_loops_with(brep.id, &outer, &holes, dir, extent) - .map_err(|error| JsValue::from_str(&format!("Failed to extrude BRep face: {}", error)))?; - extruded.validate_topology().map_err(|error| { - JsValue::from_str(&format!("Extruded BRep topology is invalid: {}", error)) - })?; - serde_json::to_string(&extruded).map_err(|error| { - JsValue::from_str(&format!("Failed to serialize extruded BRep: {}", error)) - }) -} - -fn sanitize_loop_points(points: &[Vector3]) -> Vec { - let mut cleaned = Vec::with_capacity(points.len()); - for point in points { - let is_duplicate = cleaned.last().copied().map_or(false, |last| { - let delta = vector_difference(*point, last); - delta.dot(&delta) <= EXTRUDE_EPSILON * EXTRUDE_EPSILON - }); - if !is_duplicate { - cleaned.push(*point); - } - } - - if cleaned.len() > 2 { - let first = cleaned[0]; - let last = cleaned[cleaned.len() - 1]; - let delta = vector_difference(first, last); - if delta.dot(&delta) <= EXTRUDE_EPSILON * EXTRUDE_EPSILON { - cleaned.pop(); - } - } - - cleaned -} - -fn range_indices(start: u32, count: usize) -> Vec { - (0..count as u32).map(|offset| start + offset).collect() -} - -fn orient_loop_indices(loop_indices: &[u32], reverse: bool) -> Vec { - if reverse { - loop_indices.iter().rev().copied().collect() - } else { - loop_indices.to_vec() - } -} - -fn orient_hole_indices(hole_indices: &[Vec], reverse: bool) -> Vec> { - hole_indices - .iter() - .map(|hole| orient_loop_indices(hole, reverse)) - .collect() -} - -fn add_side_faces( - builder: &mut BrepBuilder, - source_loop: &[u32], - end_loop: &[u32], - flip_source_face: bool, -) -> Result<(), String> { - for index in 0..source_loop.len() { - let next = (index + 1) % source_loop.len(); - let source_current = source_loop[index]; - let source_next = source_loop[next]; - let end_next = end_loop[next]; - let end_current = end_loop[index]; - - let quad = if flip_source_face { - vec![source_current, source_next, end_next, end_current] - } else { - vec![source_next, source_current, end_current, end_next] - }; - - builder - .add_face(&quad, &[]) - .map_err(|error| format!("Failed to build extrusion side face: {}", error))?; - } - - Ok(()) -} - -fn vector_difference(lhs: Vector3, rhs: Vector3) -> Vector3 { - Vector3::new(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z) -} - -#[cfg(test)] -mod tests { - use super::*; - - fn build_face_brep(outer: &[Vector3], holes: &[Vec]) -> Brep { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - let mut vertices = outer.to_vec(); - let mut hole_indices = Vec::with_capacity(holes.len()); - - for hole in holes { - let start = vertices.len() as u32; - vertices.extend(hole.iter().copied()); - hole_indices.push(range_indices(start, hole.len())); - } - - builder.add_vertices(&vertices); - builder - .add_face(&range_indices(0, outer.len()), &hole_indices) - .expect("build source face"); - builder.build().expect("build source brep") - } - - fn rectangle(min_x: f64, min_z: f64, max_x: f64, max_z: f64) -> Vec { - vec![ - Vector3::new(min_x, 0.0, min_z), - Vector3::new(max_x, 0.0, min_z), - Vector3::new(max_x, 0.0, max_z), - Vector3::new(min_x, 0.0, max_z), - ] - } - - fn reflex_profile_outline() -> Vec { - vec![ - Vector3::new(-2.721670458045537, 0.0, -1.7107348430402753), - Vector3::new(-1.3465727086811485, 0.0, -0.826743432734597), - Vector3::new(-0.3093165466574247, 0.0, 0.3142383454914993), - Vector3::new(0.2836567956860831, 0.0, 0.38012427241855573), - Vector3::new(-0.2561533171082996, 0.0, 1.0998710894777328), - Vector3::new(2.5164370978203268, 0.0, 2.2089072554491835), - Vector3::new(2.6835629021796734, 0.0, 1.7910927445508167), - Vector3::new(0.45615331710829965, 0.0, 0.9001289105222672), - Vector3::new(1.1163432043139168, 0.0, 0.01987572758144427), - Vector3::new(-0.09068345334257541, 0.0, -0.11423834549149932), - Vector3::new(-1.0534272913188514, 0.0, -1.173256567265403), - Vector3::new(-2.478329541954463, 0.0, -2.0892651569597245), - ] - } - - #[test] - fn extrude_profile_loops_preserves_holes() { - let outer = rectangle(0.0, 0.0, 6.0, 4.0); - let hole = vec![ - Vector3::new(2.0, 0.0, 1.0), - Vector3::new(2.0, 0.0, 3.0), - Vector3::new(4.0, 0.0, 3.0), - Vector3::new(4.0, 0.0, 1.0), - ]; - - let brep = extrude_profile_loops(Uuid::new_v4(), &outer, &[hole], 2.0) - .expect("profile extrusion should succeed"); - - brep.validate_topology().expect("extrusion topology"); - assert_eq!(brep.shells.len(), 1); - assert_eq!(brep.faces.len(), 10); - assert_eq!( - brep.faces - .iter() - .filter(|face| face.inner_loops.len() == 1) - .count(), - 2 - ); - } - - #[test] - fn extrude_profile_loops_preserves_concave_profiles() { - let concave = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(4.0, 0.0, 0.0), - Vector3::new(4.0, 0.0, 1.0), - Vector3::new(2.0, 0.0, 1.0), - Vector3::new(2.0, 0.0, 3.0), - Vector3::new(0.0, 0.0, 3.0), - ]; - - let brep = extrude_profile_loops(Uuid::new_v4(), &concave, &[], 1.5) - .expect("concave extrusion should succeed"); - - brep.validate_topology() - .expect("concave extrusion topology"); - assert_eq!(brep.shells.len(), 1); - assert_eq!(brep.faces.len(), concave.len() + 2); - } - - #[test] - fn extrude_profile_loops_uses_global_winding_for_reflex_first_corner() { - let profile_outline = reflex_profile_outline(); - - let brep = extrude_profile_loops(Uuid::new_v4(), &profile_outline, &[], 2.6) - .expect("profile extrusion should succeed"); - - brep.validate_topology() - .expect("profile extrusion topology"); - assert_eq!(brep.faces.len(), profile_outline.len() + 2); - assert!(brep.faces[0].normal.y < -0.999); - assert!(brep.faces[1].normal.y > 0.999); - } - - #[test] - fn try_extrude_brep_face_preserves_holes_from_face_input() { - let outer = rectangle(0.0, 0.0, 6.0, 4.0); - let hole = vec![ - Vector3::new(2.0, 0.0, 1.0), - Vector3::new(2.0, 0.0, 3.0), - Vector3::new(4.0, 0.0, 3.0), - Vector3::new(4.0, 0.0, 1.0), - ]; - let source = build_face_brep(&outer, &[hole]); - - let brep = try_extrude_brep_face(source, 2.0).expect("face extrusion should succeed"); - - brep.validate_topology().expect("face extrusion topology"); - assert_eq!(brep.shells.len(), 1); - assert_eq!(brep.faces.len(), 10); - assert_eq!( - brep.faces - .iter() - .filter(|face| face.inner_loops.len() == 1) - .count(), - 2 - ); - } - - #[test] - fn symmetric_extrude_spans_both_sides_of_profile() { - let square = rectangle(-1.0, -1.0, 1.0, 1.0); - let brep = extrude_profile_loops_with( - Uuid::new_v4(), - &square, - &[], - Vector3::new(0.0, 1.0, 0.0), - ExtrudeExtent::Symmetric { distance: 2.0 }, - ) - .expect("symmetric extrude"); - brep.validate_topology().expect("topology"); - - let (min_y, max_y) = brep - .vertices - .iter() - .fold((f64::MAX, f64::MIN), |(lo, hi), v| { - (lo.min(v.position.y), hi.max(v.position.y)) - }); - assert!((min_y + 1.0).abs() < 1.0e-9, "spans to -1, got {}", min_y); - assert!((max_y - 1.0).abs() < 1.0e-9, "spans to +1, got {}", max_y); - } - - #[test] - fn two_sided_extrude_uses_distinct_distances() { - let square = rectangle(-0.5, -0.5, 0.5, 0.5); - let brep = extrude_profile_loops_with( - Uuid::new_v4(), - &square, - &[], - Vector3::new(0.0, 1.0, 0.0), - ExtrudeExtent::TwoSided { - plus: 3.0, - minus: 1.0, - }, - ) - .expect("two sided extrude"); - brep.validate_topology().expect("topology"); - - let (min_y, max_y) = brep - .vertices - .iter() - .fold((f64::MAX, f64::MIN), |(lo, hi), v| { - (lo.min(v.position.y), hi.max(v.position.y)) - }); - assert!((min_y + 1.0).abs() < 1.0e-9, "minus side -1, got {}", min_y); - assert!((max_y - 3.0).abs() < 1.0e-9, "plus side +3, got {}", max_y); - } - - #[test] - fn arbitrary_direction_extrude_follows_direction() { - // Non-axis direction: profile in XZ plane extruded along (1,1,0). - let square = rectangle(-0.5, -0.5, 0.5, 0.5); - let dir = Vector3::new(1.0, 1.0, 0.0); - let dist = (2.0_f64).sqrt(); - let brep = extrude_profile_loops_with( - Uuid::new_v4(), - &square, - &[], - dir, - ExtrudeExtent::OneSided { distance: dist }, - ) - .expect("directional extrude"); - brep.validate_topology().expect("topology"); - - // The end cap is offset by the unit direction × distance = (1,1,0). - let max_x = brep - .vertices - .iter() - .fold(f64::MIN, |hi, v| hi.max(v.position.x)); - let max_y = brep - .vertices - .iter() - .fold(f64::MIN, |hi, v| hi.max(v.position.y)); - assert!( - (max_x - 1.5).abs() < 1.0e-9, - "x shifted by 1, got {}", - max_x - ); - assert!( - (max_y - 1.0).abs() < 1.0e-9, - "y shifted by 1, got {}", - max_y - ); - } - - #[test] - fn try_extrude_brep_face_rejects_zero_height() { - let outer = rectangle(0.0, 0.0, 2.0, 2.0); - let source = build_face_brep(&outer, &[]); - - let error = try_extrude_brep_face(source, 0.0) - .err() - .expect("zero height should fail"); - - assert!(error.contains("finite non-zero")); - } -} diff --git a/main/opengeometry/src/operations/mod.rs b/main/opengeometry/src/operations/mod.rs new file mode 100644 index 0000000..ac8b483 --- /dev/null +++ b/main/opengeometry/src/operations/mod.rs @@ -0,0 +1,9 @@ +pub(super) mod creating; +mod error; +pub mod modifying; + +pub use error::OperationError; + +fn invalid(message: impl Into) -> OperationError { + OperationError::InvalidParameter(message.into()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/assembly/append.rs b/main/opengeometry/src/operations/modifying/boolean/assembly/append.rs new file mode 100644 index 0000000..dfbf2b4 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/assembly/append.rs @@ -0,0 +1,311 @@ +use super::provenance::cylinder_face_mappings; +use crate::brep::{ + BrepEnvelope, CurveGeometry, EdgeGeometry, FaceProvenance, FaceRole, FaceSource, GeometryError, + GeometryQuality, PcurveGeometry, +}; +use crate::operations::modifying::boolean::operands::CylinderInput; +use crate::operations::modifying::boolean::types::{ + BooleanOp, BooleanReport, BooleanResult, FaceMapping, +}; + +struct IdOffsets { + surface: u32, + curve: u32, + pcurve: u32, + intersection: u32, + vertex: u32, + edge: u32, + halfedge: u32, + loop_: u32, + face: u32, + wire: u32, + shell: u32, +} + +pub(crate) fn append_analytic_input( + out: &mut BrepEnvelope, + input: &BrepEnvelope, +) -> Result, GeometryError> { + let offsets = id_offsets(out)?; + add_input_geometry(out, input, &offsets); + add_input_loop_topology(out, input, &offsets); + let mappings = add_input_faces(out, input, &offsets); + add_input_wires_shells_and_solids(out, input, &offsets); + Ok(mappings) +} + +fn id_offsets(out: &BrepEnvelope) -> Result { + let offset = |value: usize, table: &str| { + u32::try_from(value).map_err(|_| GeometryError::LimitExceeded(table.into())) + }; + Ok(IdOffsets { + surface: offset(out.geometry.surfaces.len(), "surface IDs")?, + curve: offset(out.geometry.curves.len(), "curve IDs")?, + pcurve: offset(out.geometry.pcurves.len(), "pcurve IDs")?, + intersection: offset(out.geometry.intersections.len(), "intersection IDs")?, + vertex: offset(out.topology.vertices.len(), "vertex IDs")?, + edge: offset(out.topology.edges.len(), "edge IDs")?, + halfedge: offset(out.topology.halfedges.len(), "halfedge IDs")?, + loop_: offset(out.topology.loops.len(), "loop IDs")?, + face: offset(out.topology.faces.len(), "face IDs")?, + wire: offset(out.topology.wires.len(), "wire IDs")?, + shell: offset(out.topology.shells.len(), "shell IDs")?, + }) +} + +fn add_input_geometry(out: &mut BrepEnvelope, input: &BrepEnvelope, offsets: &IdOffsets) { + out.geometry + .surfaces + .extend(input.geometry.surfaces.clone()); + out.geometry + .intersections + .extend( + input + .geometry + .intersections + .iter() + .cloned() + .map(|mut definition| { + definition.surfaces = definition.surfaces.map(|id| id + offsets.surface); + definition + }), + ); + out.geometry + .curves + .extend(input.geometry.curves.iter().cloned().map(|mut curve| { + if let CurveGeometry::Intersection { definition } = &mut curve { + *definition += offsets.intersection; + } + curve + })); + out.geometry + .pcurves + .extend(input.geometry.pcurves.iter().cloned().map(|mut pcurve| { + match &mut pcurve { + PcurveGeometry::ProjectedCurve { curve, surface, .. } => { + *curve += offsets.curve; + *surface += offsets.surface; + } + PcurveGeometry::IntersectionSide { definition, .. } => { + *definition += offsets.intersection; + } + PcurveGeometry::Line2 { .. } | PcurveGeometry::Conic2 { .. } => {} + } + pcurve + })); +} + +fn add_input_loop_topology(out: &mut BrepEnvelope, input: &BrepEnvelope, offsets: &IdOffsets) { + out.topology + .vertices + .extend(input.topology.vertices.iter().cloned().map(|mut vertex| { + vertex.id += offsets.vertex; + vertex.outgoing_halfedge = vertex.outgoing_halfedge.map(|id| id + offsets.halfedge); + vertex + })); + out.topology + .edges + .extend(input.topology.edges.iter().cloned().map(|mut edge| { + edge.id += offsets.edge; + edge.halfedge += offsets.halfedge; + edge.twin_halfedge = edge.twin_halfedge.map(|id| id + offsets.halfedge); + match &mut edge.geometry { + EdgeGeometry::Curve { curve, .. } => *curve += offsets.curve, + EdgeGeometry::Collapsed { vertex } => *vertex += offsets.vertex, + } + edge + })); + out.topology.halfedges.extend( + input + .topology + .halfedges + .iter() + .cloned() + .map(|mut halfedge| { + halfedge.id += offsets.halfedge; + halfedge.from += offsets.vertex; + halfedge.to += offsets.vertex; + halfedge.edge += offsets.edge; + halfedge.twin = halfedge.twin.map(|id| id + offsets.halfedge); + halfedge.next = halfedge.next.map(|id| id + offsets.halfedge); + halfedge.prev = halfedge.prev.map(|id| id + offsets.halfedge); + halfedge.face = halfedge.face.map(|id| id + offsets.face); + halfedge.loop_ref = halfedge.loop_ref.map(|id| id + offsets.loop_); + halfedge.wire_ref = halfedge.wire_ref.map(|id| id + offsets.wire); + halfedge.geometry_use.pcurve = + halfedge.geometry_use.pcurve.map(|id| id + offsets.pcurve); + halfedge + }), + ); + out.topology + .loops + .extend(input.topology.loops.iter().cloned().map(|mut loop_| { + loop_.id += offsets.loop_; + loop_.start_halfedge += offsets.halfedge; + loop_.face_ref += offsets.face; + loop_ + })); +} + +fn add_input_faces( + out: &mut BrepEnvelope, + input: &BrepEnvelope, + offsets: &IdOffsets, +) -> Vec { + let mut mappings = Vec::with_capacity(input.topology.faces.len()); + out.topology + .faces + .extend(input.topology.faces.iter().cloned().map(|mut face| { + let source = FaceSource { + entity: input.id.clone(), + body: input.id.clone(), + key: face.key.clone(), + face: face.id, + }; + face.id += offsets.face; + face.key = format!("{}:{}", input.id, face.key); + face.surface += offsets.surface; + face.trim.outer += offsets.loop_; + for hole in &mut face.trim.holes { + *hole += offsets.loop_; + } + face.shell_ref = face.shell_ref.map(|id| id + offsets.shell); + face.provenance = FaceProvenance { + sources: vec![source.clone()], + role: FaceRole::Preserved, + reversed: false, + }; + mappings.push(FaceMapping { + source, + result_faces: vec![face.id], + }); + face + })); + mappings +} + +fn add_input_wires_shells_and_solids( + out: &mut BrepEnvelope, + input: &BrepEnvelope, + offsets: &IdOffsets, +) { + out.topology + .wires + .extend(input.topology.wires.iter().cloned().map(|mut wire| { + wire.id += offsets.wire; + wire.start_halfedge += offsets.halfedge; + wire + })); + out.topology + .shells + .extend(input.topology.shells.iter().cloned().map(|mut shell| { + shell.id += offsets.shell; + for face in &mut shell.faces { + *face += offsets.face; + } + shell + })); + out.solids + .extend(input.solids.iter().cloned().map(|mut solid| { + solid.outer_shell += offsets.shell; + for shell in &mut solid.cavity_shells { + *shell += offsets.shell; + } + solid + })); +} + +pub(crate) fn finish_analytic_result( + mut out: BrepEnvelope, + first: &BrepEnvelope, + second: &BrepEnvelope, + first_is_a: bool, + operation: BooleanOp, + coincident: bool, +) -> Result { + out.revision = first + .revision + .max(second.revision) + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; + out.validate()?; + let inputs = if first_is_a { + [first, second] + } else { + [second, first] + }; + Ok(BooleanResult { + report: BooleanReport { + operation, + quality: GeometryQuality::Analytic, + contacts: Vec::new(), + coincident, + face_mappings: analytic_face_mappings(&out, inputs), + }, + brep: out, + }) +} + +pub(crate) fn analytic_face_mappings<'a>( + out: &BrepEnvelope, + inputs: impl IntoIterator, +) -> Vec { + inputs + .into_iter() + .flat_map(|input| { + input.topology.faces.iter().map(|face| { + let source = FaceSource { + entity: input.id.clone(), + body: input.id.clone(), + key: face.key.clone(), + face: face.id, + }; + let result_faces = out + .topology + .faces + .iter() + .filter(|result| { + result.provenance.sources.iter().any(|candidate| { + candidate.entity == source.entity + && candidate.body == source.body + && candidate.key == source.key + && candidate.face == source.face + }) + }) + .map(|result| result.id) + .collect(); + FaceMapping { + source, + result_faces, + } + }) + }) + .collect() +} + +pub(crate) fn finish_cylinder_result( + mut out: BrepEnvelope, + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + operation: BooleanOp, + coincident: bool, +) -> Result { + out.revision = a + .brep + .revision + .max(b.brep.revision) + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; + out.validate()?; + let face_mappings = cylinder_face_mappings(&out, [a, b]); + Ok(BooleanResult { + brep: out, + report: BooleanReport { + operation, + quality: GeometryQuality::Analytic, + contacts: Vec::new(), + coincident, + face_mappings, + }, + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/assembly/box_faces.rs b/main/opengeometry/src/operations/modifying/boolean/assembly/box_faces.rs new file mode 100644 index 0000000..412d1b9 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/assembly/box_faces.rs @@ -0,0 +1,45 @@ +use super::provenance::{box_source, face_provenance}; +use crate::brep::{Accuracy, Builder, FaceRole, Frame3, GeometryError, SurfaceGeometry, Use}; +use crate::operations::modifying::boolean::operands::BoxInput; +use crate::primitives::{box_face_boundary, box_face_layout, BoxCorners, BoxFaceLayout}; + +pub(crate) fn add_box_faces( + builder: &mut Builder, + box_: &BoxInput<'_>, + corners: &BoxCorners, + accuracy: Accuracy, + face_holes: impl Fn(&Builder, usize, Frame3) -> Result>, GeometryError>, + is_split: impl Fn(usize) -> bool, +) -> Result<(), GeometryError> { + for ( + face_index, + BoxFaceLayout { + key, + indices, + normal, + }, + ) in box_face_layout(box_.frame).into_iter().enumerate() + { + let face = box_face_boundary(builder, corners, indices, normal, accuracy, |from| { + corners.vertices[from] + })?; + let holes = face_holes(builder, face_index, face.frame)?; + builder.face_with_holes( + key, + SurfaceGeometry::Plane { frame: face.frame }, + face.bounds, + face.uses, + holes, + )?; + builder.brep.topology.faces[face_index].provenance = face_provenance( + vec![box_source(box_, face_index)], + if is_split(face_index) { + FaceRole::Split + } else { + FaceRole::Preserved + }, + false, + ); + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/assembly/closed_loop.rs b/main/opengeometry/src/operations/modifying/boolean/assembly/closed_loop.rs new file mode 100644 index 0000000..8322445 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/assembly/closed_loop.rs @@ -0,0 +1,596 @@ +use super::append::append_analytic_input; +use super::loop_pcurves::{ + exact_closed_circle_pcurve, intersection_loop_sample, remap_closed_loop_pcurve, + topology_loop_bounds, +}; +use super::provenance::reverse; +use crate::brep::{ + unit, BrepEnvelope, CurveGeometry, Edge, EdgeGeometry, Face, FaceProvenance, FaceRole, Frame3, + GeometryError, GeometryStore, HalfEdge, HalfEdgeGeometryUse, IntersectionDefinition, Loop, + Orientation, PcurveGeometry, Shell, SolidRegion, Surface, SurfaceGeometry, TrimRegion, Vertex, +}; +use crate::intersection::{IntersectionBranch, IntersectionGraph}; +use crate::math::{dot, Interval, Point3}; +use crate::operations::modifying::boolean::operands::brep_face_source; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::query::{classify_point, PointClassification}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum LoopRegion { + Patch, + Complement, +} + +pub(crate) struct LoopSides<'a, S> { + pub(crate) inputs: [&'a BrepEnvelope; 2], + pub(crate) face_ids: [u32; 2], + pub(crate) regions: [LoopRegion; 2], + pub(crate) reverse_selected: [bool; 2], + pub(crate) surfaces: [S; 2], +} + +pub(crate) struct ClosedBranchEdge { + pub(crate) edge: u32, + pub(crate) vertex: u32, + pub(crate) pcurves: [PcurveGeometry; 2], +} + +struct SphereCandidate { + score: f64, + surface: SurfaceGeometry, + coordinates: Vec<[f64; 2]>, +} + +pub(crate) struct PatchLoop { + pub(crate) face: u32, + loop_id: u32, + uv_bounds: [Interval; 2], +} + +pub(crate) fn loop_containment( + a: &BrepEnvelope, + b: &BrepEnvelope, + graph: &IntersectionGraph, + branch: &IntersectionBranch, + unclassified: &str, +) -> Result<[bool; 2], GeometryError> { + let loop_inside_other = [ + classify_point( + b, + graph.geometry.surfaces[0].point_at(intersection_loop_sample(graph, branch, 0)?)?, + )?, + classify_point( + a, + graph.geometry.surfaces[1].point_at(intersection_loop_sample(graph, branch, 1)?)?, + )?, + ]; + if loop_inside_other.iter().any(|classification| { + matches!( + classification, + PointClassification::Unknown | PointClassification::Boundary + ) + }) { + return Err(GeometryError::UnresolvedIntersection(unclassified.into())); + } + Ok(loop_inside_other.map(|value| value == PointClassification::Inside)) +} + +pub(crate) fn loop_regions( + operation: BooleanOp, + first_is_a: bool, + loop_inside_other: [bool; 2], +) -> ([LoopRegion; 2], [bool; 2]) { + let (want_inside, reverse_selected) = loop_selection(operation, first_is_a); + let regions: [LoopRegion; 2] = std::array::from_fn(|side| { + if loop_inside_other[side] == want_inside[side] { + LoopRegion::Patch + } else { + LoopRegion::Complement + } + }); + (regions, reverse_selected) +} + +pub(crate) fn loop_selection(operation: BooleanOp, first_is_a: bool) -> ([bool; 2], [bool; 2]) { + match operation { + BooleanOp::Union => ([false, false], [false, false]), + BooleanOp::Intersection => ([true, true], [false, false]), + BooleanOp::Subtraction if first_is_a => ([false, true], [false, true]), + BooleanOp::Subtraction => ([true, false], [true, false]), + } +} + +pub(crate) fn add_complement_input( + out: &mut BrepEnvelope, + input: &BrepEnvelope, + face_id: u32, + selected_surface: &mut Option, + selected_shells: &mut Vec, + record_face: impl FnOnce(u32), +) -> Result<(), GeometryError> { + let face_offset = out.topology.faces.len() as u32; + let shell_offset = out.topology.shells.len() as u32; + append_analytic_input(out, input)?; + let selected_face = face_offset + face_id; + record_face(selected_face); + *selected_surface = Some(out.topology.faces[selected_face as usize].surface); + selected_shells.push(shell_offset); + for face in face_offset..out.topology.faces.len() as u32 { + out.topology.faces[face as usize].provenance.role = if face == selected_face { + FaceRole::Split + } else { + FaceRole::Preserved + }; + } + Ok(()) +} + +pub(crate) fn merge_selected_shells(out: &mut BrepEnvelope, selected_shells: &[u32]) { + if selected_shells.is_empty() { + out.topology.shells.push(Shell { + id: 0, + faces: Vec::new(), + is_closed: true, + }); + out.solids.push(SolidRegion { + outer_shell: 0, + cavity_shells: Vec::new(), + }); + } else if selected_shells.len() == 2 { + let faces = out.topology.shells[1].faces.clone(); + for face in &faces { + out.topology.faces[*face as usize].shell_ref = Some(0); + } + out.topology.shells[0].faces.extend(faces); + out.topology.shells.pop(); + out.solids.clear(); + out.solids.push(SolidRegion { + outer_shell: 0, + cavity_shells: Vec::new(), + }); + } +} + +pub(crate) fn append_closed_branch_geometry( + out: &mut BrepEnvelope, + graph: &IntersectionGraph, + branch: &IntersectionBranch, + tolerance: f64, + retarget: impl FnOnce(&mut GeometryStore, u32) -> Result, + surface: impl Fn(usize) -> Result, +) -> Result { + let source_curve = graph + .geometry + .curves + .get(branch.curve as usize) + .ok_or_else(|| GeometryError::MissingReference { + kind: "curve".into(), + index: branch.curve, + })? + .clone(); + let curve = out.geometry.curves.len() as u32; + let pcurves = match source_curve { + CurveGeometry::Intersection { definition } => { + let definition_geometry = retarget(&mut out.geometry, definition)?; + add_branch_intersection_curve(out, graph, branch, definition_geometry)? + } + curve_geometry => { + add_branch_analytic_curve(out, graph, branch, curve, &curve_geometry, surface)? + } + }; + let vertex = out.topology.vertices.len() as u32; + out.topology.vertices.push(Vertex { + id: vertex, + position: branch.endpoints[0], + outgoing_halfedge: None, + tolerance, + }); + let edge = out.topology.edges.len() as u32; + out.topology.edges.push(Edge { + id: edge, + geometry: EdgeGeometry::Curve { + curve, + range: branch.range, + }, + halfedge: u32::MAX, + twin_halfedge: None, + tolerance, + chart_seam: false, + }); + Ok(ClosedBranchEdge { + edge, + vertex, + pcurves, + }) +} + +fn add_branch_intersection_curve( + out: &mut BrepEnvelope, + graph: &IntersectionGraph, + branch: &IntersectionBranch, + definition_geometry: IntersectionDefinition, +) -> Result<[PcurveGeometry; 2], GeometryError> { + let definition = out.geometry.intersections.len() as u32; + out.geometry.intersections.push(definition_geometry); + out.geometry + .curves + .push(CurveGeometry::Intersection { definition }); + [0, 1] + .map( + |side| match graph.geometry.pcurves.get(branch.pcurves[side] as usize) { + Some(PcurveGeometry::IntersectionSide { + side: intersection_side, + .. + }) => Ok(PcurveGeometry::IntersectionSide { + definition, + side: *intersection_side, + }), + Some(_) => Err(GeometryError::InvalidGeometry( + "intersection curve requires intersection-side pcurves".into(), + )), + None => Err(GeometryError::MissingReference { + kind: "pcurve".into(), + index: branch.pcurves[side], + }), + }, + ) + .into_iter() + .collect::, _>>()? + .try_into() + .map_err(|_| GeometryError::InvalidGeometry("closed SSI loop requires two pcurves".into())) +} + +fn add_branch_analytic_curve( + out: &mut BrepEnvelope, + graph: &IntersectionGraph, + branch: &IntersectionBranch, + curve: u32, + curve_geometry: &CurveGeometry, + surface: impl Fn(usize) -> Result, +) -> Result<[PcurveGeometry; 2], GeometryError> { + out.geometry.curves.push(curve_geometry.clone()); + [0, 1] + .map(|side| { + if let Some(exact) = exact_closed_circle_pcurve( + &graph.geometry, + &out.geometry, + branch.pcurves[side], + curve_geometry, + surface(side)?, + out.accuracy.geometric, + )? { + return Ok(exact); + } + remap_closed_loop_pcurve( + &graph.geometry.pcurves[branch.pcurves[side] as usize], + curve, + surface(side)?, + ) + }) + .into_iter() + .collect::, _>>()? + .try_into() + .map_err(|_| GeometryError::InvalidGeometry("closed SSI loop requires two pcurves".into())) +} + +pub(crate) fn retained_definition( + graph: &IntersectionGraph, + definition: u32, + selected_surfaces: [u32; 2], +) -> Result { + let mut definition_geometry = intersection_definition(graph, definition)?; + definition_geometry.surfaces = definition_geometry + .surfaces + .map(|surface| selected_surfaces[surface as usize]); + Ok(definition_geometry) +} + +pub(crate) fn intersection_definition( + graph: &IntersectionGraph, + definition: u32, +) -> Result { + Ok(graph + .geometry + .intersections + .get(definition as usize) + .ok_or_else(|| GeometryError::MissingReference { + kind: "intersection definition".into(), + index: definition, + })? + .clone()) +} + +pub(crate) fn reframe_sphere_intersection( + geometry: &mut GeometryStore, + definition: &mut IntersectionDefinition, + side: usize, +) -> Result<(), GeometryError> { + let surface_id = definition.surfaces[side]; + let (frame, radius) = match geometry.surface(surface_id)? { + SurfaceGeometry::Sphere { frame, radius } => (*frame, *radius), + _ => { + return Err(GeometryError::CoverageGap { + families: ["periodic winding".into(), "non-spherical support".into()], + }) + } + }; + let references = [frame.x, frame.y, frame.z]; + let mut best: Option = None; + for i in -1..=1 { + for j in -1..=1 { + for k in -1..=1 { + if [i, j, k] == [0, 0, 0] { + continue; + } + let axis = unit(frame.vector([i as f64, j as f64, k as f64]))?; + let Some(candidate) = + scored_sphere_candidate(frame, radius, references, axis, definition)? + else { + continue; + }; + if best + .as_ref() + .is_none_or(|best| candidate.score < best.score) + { + best = Some(candidate); + } + } + } + } + let SphereCandidate { + surface: candidate, + coordinates, + .. + } = best.ok_or_else(|| GeometryError::CoverageGap { + families: ["sphere".into(), "unresolved periodic winding".into()], + })?; + geometry.surfaces[surface_id as usize] = candidate; + for (anchor, uv) in definition.anchors.iter_mut().zip(&coordinates) { + if side == 0 { + anchor.uv_a = *uv; + } else { + anchor.uv_b = *uv; + } + } + let offset = side * 2; + for (index, tube) in definition.uv_tubes.iter_mut().enumerate() { + for axis in 0..2 { + let a = coordinates[index][axis]; + let b = coordinates[index + 1][axis]; + let padding = (a - b).abs().max(definition.residual_tolerance / radius) * 2.0; + tube[offset + axis] = Interval::new(a.min(b) - padding, a.max(b) + padding)?; + } + } + definition.validate(geometry) +} + +fn scored_sphere_candidate( + frame: Frame3, + radius: f64, + references: [Point3; 3], + axis: Point3, + definition: &IntersectionDefinition, +) -> Result, GeometryError> { + let reference = references + .into_iter() + .min_by(|left, right| dot(*left, axis).abs().total_cmp(&dot(*right, axis).abs())) + .ok_or_else(|| GeometryError::InvalidGeometry("no reference axes".into()))?; + let candidate = SurfaceGeometry::Sphere { + frame: Frame3::from_axis(frame.origin, axis, reference)?, + radius, + }; + let mut coordinates = Vec::with_capacity(definition.anchors.len()); + let mut hint = None; + let mut failed = false; + for anchor in &definition.anchors { + match candidate.project(anchor.point, hint) { + Ok(uv) => { + coordinates.push(uv); + hint = Some(uv); + } + Err(_) => { + failed = true; + break; + } + } + } + if failed { + return Ok(None); + } + let Some(last) = coordinates.last() else { + return Ok(None); + }; + let longitude_span = (last[0] - coordinates[0][0]).abs(); + let pole_proximity = coordinates + .iter() + .map(|uv| uv[1].abs()) + .fold(0.0_f64, f64::max); + if longitude_span >= std::f64::consts::PI + || pole_proximity >= std::f64::consts::FRAC_PI_2 - 1.0e-6 + { + return Ok(None); + } + let score = pole_proximity + longitude_span; + Ok(Some(SphereCandidate { + score, + surface: candidate, + coordinates, + })) +} + +pub(crate) fn loop_sense(side: usize, reversed: bool) -> Orientation { + let nominal_sense = if side == 0 { + Orientation::Forward + } else { + Orientation::Reverse + }; + if reversed { + reverse(nominal_sense) + } else { + nominal_sense + } +} + +pub(crate) fn add_hole_loop( + out: &mut BrepEnvelope, + loop_edge: &ClosedBranchEdge, + side: usize, + face: u32, + sense: Orientation, + tolerance: f64, +) -> Result<(), GeometryError> { + let loop_id = add_closed_intersection_loop( + out, + loop_edge.edge, + loop_edge.vertex, + face, + loop_edge.pcurves[side].clone(), + sense, + true, + )?; + let loop_bounds = topology_loop_bounds(out, loop_id, tolerance)?; + widen_trim_bounds(out, face, loop_bounds)?; + out.topology.faces[face as usize].trim.holes.push(loop_id); + Ok(()) +} + +pub(crate) fn add_closed_intersection_loop( + brep: &mut BrepEnvelope, + edge: u32, + vertex: u32, + face: u32, + pcurve_geometry: PcurveGeometry, + sense: Orientation, + is_hole: bool, +) -> Result { + let loop_id = brep.topology.loops.len() as u32; + let halfedge = brep.topology.halfedges.len() as u32; + let pcurve = brep.geometry.pcurves.len() as u32; + brep.geometry.pcurves.push(pcurve_geometry); + let edge_record = brep.topology.edges.get_mut(edge as usize).ok_or_else(|| { + GeometryError::InvalidTopology("intersection loop edge is missing".into()) + })?; + let twin = if edge_record.halfedge == u32::MAX { + edge_record.halfedge = halfedge; + None + } else { + if edge_record.twin_halfedge.is_some() { + return Err(GeometryError::InvalidTopology( + "intersection loop edge is nonmanifold".into(), + )); + } + edge_record.twin_halfedge = Some(halfedge); + brep.topology.halfedges[edge_record.halfedge as usize].twin = Some(halfedge); + Some(edge_record.halfedge) + }; + brep.topology.halfedges.push(HalfEdge { + id: halfedge, + from: vertex, + to: vertex, + twin, + next: Some(halfedge), + prev: Some(halfedge), + edge, + face: Some(face), + loop_ref: Some(loop_id), + wire_ref: None, + geometry_use: HalfEdgeGeometryUse { + sense, + pcurve: Some(pcurve), + periodic_lift: [0; 2], + }, + }); + if brep.topology.vertices[vertex as usize] + .outgoing_halfedge + .is_none() + { + brep.topology.vertices[vertex as usize].outgoing_halfedge = Some(halfedge); + } + brep.topology.loops.push(Loop { + id: loop_id, + start_halfedge: halfedge, + face_ref: face, + is_hole, + }); + Ok(loop_id) +} + +pub(crate) fn widen_trim_bounds( + out: &mut BrepEnvelope, + face: u32, + loop_bounds: [Interval; 2], +) -> Result<(), GeometryError> { + for axis in 0..2 { + let bounds = out.topology.faces[face as usize].trim.uv_bounds[axis]; + out.topology.faces[face as usize].trim.uv_bounds[axis] = Interval::new( + bounds.lo.min(loop_bounds[axis].lo), + bounds.hi.max(loop_bounds[axis].hi), + )?; + } + Ok(()) +} + +pub(crate) fn add_patch_loop( + out: &mut BrepEnvelope, + loop_edge: &ClosedBranchEdge, + side: usize, + sense: Orientation, + tolerance: f64, +) -> Result { + let face = out.topology.faces.len() as u32; + let loop_id = add_closed_intersection_loop( + out, + loop_edge.edge, + loop_edge.vertex, + face, + loop_edge.pcurves[side].clone(), + sense, + false, + )?; + let uv_bounds = topology_loop_bounds(out, loop_id, tolerance)?; + Ok(PatchLoop { + face, + loop_id, + uv_bounds, + }) +} + +pub(crate) fn add_patch_face( + out: &mut BrepEnvelope, + sides: &LoopSides<'_, S>, + side: usize, + patch: &PatchLoop, + branch_index: Option, + surface: impl FnOnce(S) -> Result, +) -> Result<(), GeometryError> { + let source_face = &sides.inputs[side].topology.faces[sides.face_ids[side] as usize]; + out.topology.faces.push(Face { + id: patch.face, + key: match branch_index { + Some(branch_index) => { + format!("{}:intersection-patch-{branch_index}", source_face.key) + } + None => format!("{}:intersection-patch", source_face.key), + }, + surface: surface(sides.surfaces[side])?, + sense: source_face.sense, + trim: TrimRegion { + chart: source_face.trim.chart, + uv_bounds: patch.uv_bounds, + outer: patch.loop_id, + holes: Vec::new(), + }, + shell_ref: Some(0), + provenance: FaceProvenance { + sources: vec![brep_face_source(sides.inputs[side], sides.face_ids[side])], + role: if sides.reverse_selected[side] { + FaceRole::Cut + } else { + FaceRole::Split + }, + reversed: sides.reverse_selected[side], + }, + }); + out.topology.shells[0].faces.push(patch.face); + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/assembly/cylinder_faces.rs b/main/opengeometry/src/operations/modifying/boolean/assembly/cylinder_faces.rs new file mode 100644 index 0000000..7fc1b63 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/assembly/cylinder_faces.rs @@ -0,0 +1,176 @@ +use super::append::append_analytic_input; +use super::provenance::reverse_face; +use crate::brep::{ + boundary, uv_line, BrepEnvelope, Builder, CurveGeometry, FaceProvenance, Frame3, GeometryError, + Orientation, PcurveGeometry, SurfaceGeometry, +}; +use crate::math::{scale, Interval}; +use crate::primitives; +use Orientation::{Forward as F, Reverse as R}; + +pub(crate) struct CylinderBand { + pub(crate) frame: Frame3, + pub(crate) radius: f64, + pub(crate) z0: f64, + pub(crate) z1: f64, + pub(crate) low: (u32, u32), + pub(crate) high: (u32, u32), +} + +pub(crate) fn circular_boundary( + builder: &mut Builder, + frame: Frame3, + radius: f64, + z: f64, +) -> Result<(u32, u32), GeometryError> { + let mut circle_frame = frame; + circle_frame.origin = frame.point([0.0, 0.0, z]); + let vertex = builder.vertex(frame.point([radius, 0.0, z])); + let edge = builder.edge( + CurveGeometry::Circle { + frame: circle_frame, + radius, + }, + Interval::new(0.0, std::f64::consts::TAU)?, + false, + ); + Ok((vertex, edge)) +} + +pub(crate) fn cylinder_band( + builder: &mut Builder, + band: &CylinderBand, + provenance: FaceProvenance, + reversed: bool, +) -> Result<(), GeometryError> { + let CylinderBand { + frame, + radius, + z0, + z1, + low, + high, + } = *band; + let seam = builder.edge( + CurveGeometry::Line { + origin: frame.point([radius, 0.0, z0]), + direction: frame.z, + }, + Interval::new(0.0, z1 - z0)?, + true, + ); + let face = builder.brep.topology.faces.len() as u32; + let tau = std::f64::consts::TAU; + builder.face( + &format!("{face}:cylinder-band"), + SurfaceGeometry::Cylinder { frame, radius }, + [[0.0, tau], [z0, z1]], + vec![ + boundary(low.1, low.0, low.0, F, uv_line([0.0, z0], [1.0, 0.0])), + boundary(seam, low.0, high.0, F, uv_line([tau, z0], [0.0, 1.0])), + boundary(high.1, high.0, high.0, R, uv_line([0.0, z1], [1.0, 0.0])), + boundary(seam, high.0, low.0, R, uv_line([0.0, z0], [0.0, 1.0])), + ], + )?; + builder.brep.topology.faces[face as usize].provenance = provenance; + if reversed { + reverse_face(&mut builder.brep, face); + } + Ok(()) +} + +pub(crate) fn cylinder_cap( + builder: &mut Builder, + frame: Frame3, + radius: f64, + z: f64, + boundary_edge: (u32, u32), + top: bool, + provenance: FaceProvenance, +) -> Result<(), GeometryError> { + let mut surface_frame = frame; + surface_frame.origin = frame.point([0.0, 0.0, z]); + if !top { + surface_frame.y = scale(surface_frame.y, -1.0); + surface_frame.z = scale(surface_frame.z, -1.0); + } + let face = builder.brep.topology.faces.len() as u32; + builder.face( + &format!("{face}:cylinder-cap"), + SurfaceGeometry::Plane { + frame: surface_frame, + }, + [[-radius, radius]; 2], + vec![boundary( + boundary_edge.1, + boundary_edge.0, + boundary_edge.0, + if top { F } else { R }, + PcurveGeometry::Conic2 { + origin: [0.0; 2], + axis_a: [radius, 0.0], + axis_b: [0.0, if top { radius } else { -radius }], + }, + )], + )?; + builder.brep.topology.faces[face as usize].provenance = provenance; + Ok(()) +} + +pub(crate) fn append_cylinder_segment( + out: &mut BrepEnvelope, + frame: Frame3, + radius: f64, + height: f64, + provenances: [FaceProvenance; 3], + index: usize, +) -> Result<(), GeometryError> { + if height <= 4.0 * out.accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "cylinder boolean leaves a sub-tolerance axial segment".into(), + )); + } + let part = primitives::cylinder( + format!("{}:segment-{index}", out.id), + frame, + radius, + height, + out.accuracy, + )?; + let face_offset = out.topology.faces.len(); + append_analytic_input(out, &part)?; + for (face, provenance) in out.topology.faces[face_offset..] + .iter_mut() + .zip(provenances) + { + face.provenance = provenance; + } + Ok(()) +} + +pub(crate) fn append_annular_cylinder( + out: &mut BrepEnvelope, + frame: Frame3, + inner_radius: f64, + outer_radius: f64, + height: f64, + provenances: [FaceProvenance; 4], +) -> Result<(), GeometryError> { + let part = primitives::annular_cylinder( + format!("{}:annular", out.id), + frame, + inner_radius, + outer_radius, + height, + out.accuracy, + )?; + let face_offset = out.topology.faces.len(); + append_analytic_input(out, &part)?; + for (face, provenance) in out.topology.faces[face_offset..] + .iter_mut() + .zip(provenances) + { + face.provenance = provenance; + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/assembly/loop_pcurves.rs b/main/opengeometry/src/operations/modifying/boolean/assembly/loop_pcurves.rs new file mode 100644 index 0000000..ef1d854 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/assembly/loop_pcurves.rs @@ -0,0 +1,230 @@ +use crate::brep::{ + BrepEnvelope, CurveGeometry, GeometryError, GeometryStore, IntersectionSide, PcurveGeometry, + Surface, SurfaceGeometry, +}; +use crate::intersection::{IntersectionBranch, IntersectionGraph}; +use crate::math::{cross, dot, Interval}; + +pub(super) fn remap_closed_loop_pcurve( + pcurve: &PcurveGeometry, + curve: u32, + surface: u32, +) -> Result { + Ok(match pcurve { + PcurveGeometry::Line2 { .. } | PcurveGeometry::Conic2 { .. } => pcurve.clone(), + PcurveGeometry::ProjectedCurve { + chart, + uv_hint, + uv_rate, + parameter_origin, + .. + } => PcurveGeometry::ProjectedCurve { + curve, + surface, + chart: *chart, + uv_hint: *uv_hint, + uv_rate: *uv_rate, + parameter_origin: *parameter_origin, + }, + PcurveGeometry::IntersectionSide { .. } => { + return Err(GeometryError::InvalidGeometry( + "intersection-side pcurve requires a remapped intersection definition".into(), + )) + } + }) +} + +pub(super) fn exact_closed_circle_pcurve( + source_geometry: &GeometryStore, + target_geometry: &GeometryStore, + source_pcurve: u32, + circle: &CurveGeometry, + surface: u32, + tolerance: f64, +) -> Result, GeometryError> { + if !matches!( + source_geometry.pcurves.get(source_pcurve as usize), + Some(PcurveGeometry::ProjectedCurve { .. }) + ) { + return Ok(None); + } + let CurveGeometry::Circle { + frame: circle, + radius, + } = circle + else { + return Ok(None); + }; + let support = target_geometry.surface(surface)?; + let frame = support.frame(); + let centre = frame.local(circle.origin); + let axis_x = [dot(circle.x, frame.x), dot(circle.x, frame.y)]; + let axis_y = [dot(circle.y, frame.x), dot(circle.y, frame.y)]; + match support { + SurfaceGeometry::Plane { .. } + if centre[2].abs() <= tolerance + && dot(circle.x, frame.z).abs() * radius <= tolerance + && dot(circle.y, frame.z).abs() * radius <= tolerance => + { + Ok(Some(PcurveGeometry::Conic2 { + origin: [centre[0], centre[1]], + axis_a: axis_x.map(|value| value * radius), + axis_b: axis_y.map(|value| value * radius), + })) + } + SurfaceGeometry::Cylinder { + radius: support_radius, + .. + } if centre[0].hypot(centre[1]) <= tolerance + && (radius - support_radius).abs() <= tolerance + && (dot(circle.z, frame.z).abs() - 1.0).abs() <= 1.0e-10 => + { + let rate = dot(cross(circle.x, circle.y), frame.z).signum(); + let phase = axis_x[1].atan2(axis_x[0]); + let original = source_geometry.pcurve_at(source_pcurve, 0.0)?; + let lifted_phase = phase + + ((original[0] - phase) / std::f64::consts::TAU).round() * std::f64::consts::TAU; + Ok(Some(PcurveGeometry::Line2 { + origin: [lifted_phase, centre[2]], + direction: [rate, 0.0], + })) + } + _ => Ok(None), + } +} + +pub(crate) fn pcurve_winding( + geometry: &GeometryStore, + surface: u32, + pcurve: u32, + range: Interval, +) -> Result<[i32; 2], GeometryError> { + let chart = &geometry.surface(surface)?.charts()[0]; + let start = geometry.pcurve_at(pcurve, range.lo)?; + let end = geometry.pcurve_at(pcurve, range.hi)?; + Ok(std::array::from_fn(|axis| { + chart.periods[axis].map_or(0, |period| { + ((end[axis] - start[axis]) / period).round() as i32 + }) + })) +} + +pub(crate) fn pcurve_support_surface( + geometry: &GeometryStore, + pcurve: u32, +) -> Result { + match geometry.pcurves.get(pcurve as usize) { + Some(PcurveGeometry::ProjectedCurve { surface, .. }) => Ok(*surface), + Some(PcurveGeometry::IntersectionSide { definition, side }) => { + let definition = geometry + .intersections + .get(*definition as usize) + .ok_or_else(|| GeometryError::MissingReference { + kind: "intersection definition".into(), + index: *definition, + })?; + Ok(definition.surfaces[if *side == IntersectionSide::A { 0 } else { 1 }]) + } + Some(_) => Err(GeometryError::InvalidGeometry( + "periodic pcurve has no support surface reference".into(), + )), + None => Err(GeometryError::MissingReference { + kind: "pcurve".into(), + index: pcurve, + }), + } +} + +pub(crate) fn intersection_loop_sample( + graph: &IntersectionGraph, + branch: &IntersectionBranch, + side: usize, +) -> Result<[f64; 2], GeometryError> { + let subdivisions = 128; + let mut points = Vec::with_capacity(subdivisions); + for index in 0..subdivisions { + let parameter = branch.range.lo + branch.range.width() * index as f64 / subdivisions as f64; + points.push(graph.geometry.pcurve_at(branch.pcurves[side], parameter)?); + } + let mut twice_area = 0.0; + let mut centroid = [0.0; 2]; + for index in 0..points.len() { + let a = points[index]; + let b = points[(index + 1) % points.len()]; + let cross = a[0] * b[1] - b[0] * a[1]; + twice_area += cross; + centroid[0] += (a[0] + b[0]) * cross; + centroid[1] += (a[1] + b[1]) * cross; + } + if twice_area.abs() <= f64::EPSILON { + return Ok([ + points.iter().map(|point| point[0]).sum::() / points.len() as f64, + points.iter().map(|point| point[1]).sum::() / points.len() as f64, + ]); + } + Ok([ + centroid[0] / (3.0 * twice_area), + centroid[1] / (3.0 * twice_area), + ]) +} + +pub(crate) fn intersection_loop_bounds( + graph: &IntersectionGraph, + branch: &IntersectionBranch, + side: usize, + tolerance: f64, +) -> Result<[Interval; 2], GeometryError> { + pcurve_loop_bounds( + &graph.geometry, + branch.pcurves[side], + branch.range, + tolerance, + ) +} + +fn pcurve_loop_bounds( + geometry: &GeometryStore, + pcurve: u32, + range: Interval, + tolerance: f64, +) -> Result<[Interval; 2], GeometryError> { + let mut lo = [f64::INFINITY; 2]; + let mut hi = [f64::NEG_INFINITY; 2]; + for index in 0..=256 { + let parameter = range.lo + range.width() * index as f64 / 256.0; + let uv = geometry.pcurve_at(pcurve, parameter)?; + for axis in 0..2 { + lo[axis] = lo[axis].min(uv[axis]); + hi[axis] = hi[axis].max(uv[axis]); + } + } + let padding = + [0, 1].map(|axis| (hi[axis] - lo[axis]).abs().max(tolerance) * 1.0e-6 + tolerance); + Ok([ + Interval::new(lo[0] - padding[0], hi[0] + padding[0])?, + Interval::new(lo[1] - padding[1], hi[1] + padding[1])?, + ]) +} + +pub(crate) fn topology_loop_bounds( + brep: &BrepEnvelope, + loop_id: u32, + tolerance: f64, +) -> Result<[Interval; 2], GeometryError> { + let loop_record = brep.topology.loops.get(loop_id as usize).ok_or_else(|| { + GeometryError::MissingReference { + kind: "loop".into(), + index: loop_id, + } + })?; + let halfedge = &brep.topology.halfedges[loop_record.start_halfedge as usize]; + let pcurve = halfedge.geometry_use.pcurve.ok_or_else(|| { + GeometryError::InvalidTopology("intersection loop is missing its pcurve".into()) + })?; + pcurve_loop_bounds( + &brep.geometry, + pcurve, + brep.topology.edges[halfedge.edge as usize].geometry.range(), + tolerance, + ) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/assembly/mod.rs b/main/opengeometry/src/operations/modifying/boolean/assembly/mod.rs new file mode 100644 index 0000000..83025ec --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/assembly/mod.rs @@ -0,0 +1,35 @@ +mod append; +mod box_faces; +mod closed_loop; +mod cylinder_faces; +mod loop_pcurves; +mod provenance; +mod shell_components; +mod sphere_patch; +mod welded_vertex; + +pub(super) use append::{ + analytic_face_mappings, append_analytic_input, finish_analytic_result, finish_cylinder_result, +}; +pub(super) use box_faces::add_box_faces; +pub(super) use closed_loop::{ + add_closed_intersection_loop, add_complement_input, add_hole_loop, add_patch_face, + add_patch_loop, append_closed_branch_geometry, intersection_definition, loop_containment, + loop_regions, loop_selection, loop_sense, merge_selected_shells, reframe_sphere_intersection, + retained_definition, widen_trim_bounds, ClosedBranchEdge, LoopRegion, LoopSides, +}; +pub(super) use cylinder_faces::{ + append_annular_cylinder, append_cylinder_segment, circular_boundary, cylinder_band, + cylinder_cap, CylinderBand, +}; +pub(super) use loop_pcurves::{ + intersection_loop_bounds, intersection_loop_sample, pcurve_support_surface, pcurve_winding, + topology_loop_bounds, +}; +pub(super) use provenance::{ + box_source, cylinder_face_mappings, cylinder_source, face_provenance, provenance, reverse, + reverse_face, source, +}; +pub(super) use shell_components::{assign_shell_faces, enclosing_solid}; +pub(super) use sphere_patch::{append_sphere, patch, SpherePatch}; +pub(super) use welded_vertex::add_welded_vertex; diff --git a/main/opengeometry/src/operations/modifying/boolean/assembly/provenance.rs b/main/opengeometry/src/operations/modifying/boolean/assembly/provenance.rs new file mode 100644 index 0000000..f7223d8 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/assembly/provenance.rs @@ -0,0 +1,112 @@ +use crate::brep::{BrepEnvelope, FaceProvenance, FaceRole, FaceSource, Orientation}; +use crate::operations::modifying::boolean::operands::{BoxInput, CylinderInput, SphereInput}; +use crate::operations::modifying::boolean::types::FaceMapping; + +pub(crate) fn source(input: &SphereInput<'_>) -> FaceSource { + FaceSource { + entity: input.brep.id.clone(), + body: input.brep.id.clone(), + key: input.brep.topology.faces[0].key.clone(), + face: 0, + } +} + +pub(crate) fn provenance( + input: &SphereInput<'_>, + role: FaceRole, + reversed: bool, +) -> FaceProvenance { + FaceProvenance { + sources: vec![source(input)], + role, + reversed, + } +} + +pub(crate) fn reverse(sense: Orientation) -> Orientation { + if sense == Orientation::Forward { + Orientation::Reverse + } else { + Orientation::Forward + } +} + +pub(crate) fn reverse_face(brep: &mut BrepEnvelope, face: u32) { + brep.topology.faces[face as usize].sense = reverse(brep.topology.faces[face as usize].sense); + for h in &mut brep.topology.halfedges { + if h.face == Some(face) { + std::mem::swap(&mut h.from, &mut h.to); + std::mem::swap(&mut h.next, &mut h.prev); + h.geometry_use.sense = reverse(h.geometry_use.sense); + } + } + for vertex in &mut brep.topology.vertices { + vertex.outgoing_halfedge = brep + .topology + .halfedges + .iter() + .find(|h| h.from == vertex.id) + .map(|h| h.id); + } +} + +pub(crate) fn box_source(input: &BoxInput<'_>, face: usize) -> FaceSource { + FaceSource { + entity: input.brep.id.clone(), + body: input.brep.id.clone(), + key: input.brep.topology.faces[face].key.clone(), + face: face as u32, + } +} + +pub(crate) fn cylinder_source(input: &CylinderInput<'_>, face: u32) -> FaceSource { + FaceSource { + entity: input.brep.id.clone(), + body: input.brep.id.clone(), + key: input.brep.topology.faces[face as usize].key.clone(), + face, + } +} + +pub(crate) fn face_provenance( + sources: Vec, + role: FaceRole, + reversed: bool, +) -> FaceProvenance { + FaceProvenance { + sources, + role, + reversed, + } +} + +pub(crate) fn cylinder_face_mappings( + out: &BrepEnvelope, + inputs: [&CylinderInput<'_>; 2], +) -> Vec { + let mut mappings = Vec::with_capacity(6); + for input in inputs { + for face in 0..3 { + let source = cylinder_source(input, face); + let result_faces = out + .topology + .faces + .iter() + .filter(|result| { + result.provenance.sources.iter().any(|candidate| { + candidate.entity == source.entity + && candidate.body == source.body + && candidate.key == source.key + && candidate.face == source.face + }) + }) + .map(|result| result.id) + .collect(); + mappings.push(FaceMapping { + source, + result_faces, + }); + } + } + mappings +} diff --git a/main/opengeometry/src/operations/modifying/boolean/assembly/shell_components.rs b/main/opengeometry/src/operations/modifying/boolean/assembly/shell_components.rs new file mode 100644 index 0000000..df24a54 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/assembly/shell_components.rs @@ -0,0 +1,64 @@ +use crate::brep::{BrepEnvelope, GeometryError}; +use crate::math::Point3; +use std::collections::VecDeque; + +pub(crate) fn assign_shell_faces( + brep: &mut BrepEnvelope, + assigned: &mut [bool], + start: usize, + shell: u32, + open_boundary: impl Fn() -> GeometryError, + faceless_twin: impl Fn() -> GeometryError, +) -> Result, GeometryError> { + let mut faces = Vec::new(); + let mut queue = VecDeque::from([start]); + assigned[start] = true; + while let Some(face) = queue.pop_front() { + brep.topology.faces[face].shell_ref = Some(shell); + faces.push(face as u32); + for halfedge in brep + .topology + .halfedges + .iter() + .filter(|edge| edge.face == Some(face as u32)) + { + let twin = halfedge.twin.ok_or_else(&open_boundary)?; + let adjacent = brep.topology.halfedges[twin as usize] + .face + .ok_or_else(&faceless_twin)? as usize; + if !assigned[adjacent] { + assigned[adjacent] = true; + queue.push_back(adjacent); + } + } + } + Ok(faces) +} + +pub(crate) fn enclosing_solid( + brep: &BrepEnvelope, + outer_shells: &[(u32, f64)], + cavity_shell: u32, + contains: impl Fn(&BrepEnvelope, &[u32], Point3) -> Result, + unenclosed: impl Fn() -> GeometryError, +) -> Result { + let shell = &brep.topology.shells[cavity_shell as usize]; + let face = &brep.topology.faces[shell.faces[0] as usize]; + let loop_start = brep.topology.loops[face.trim.outer as usize].start_halfedge; + let vertex = brep.topology.halfedges[loop_start as usize].from; + let point = brep.topology.vertices[vertex as usize].position; + let mut enclosing = Vec::new(); + for (index, &(outer_shell, volume)) in outer_shells.iter().enumerate() { + let outer = &brep.topology.shells[outer_shell as usize]; + if contains(brep, &outer.faces, point)? { + enclosing.push((index, volume)); + } + } + let Some(&(index, _)) = enclosing + .iter() + .min_by(|left, right| left.1.total_cmp(&right.1)) + else { + return Err(unenclosed()); + }; + Ok(index) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/assembly/sphere_patch.rs b/main/opengeometry/src/operations/modifying/boolean/assembly/sphere_patch.rs new file mode 100644 index 0000000..18b036b --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/assembly/sphere_patch.rs @@ -0,0 +1,204 @@ +use super::provenance::{provenance, reverse_face}; +use crate::brep::{ + boundary, uv_line, Accuracy, BrepEnvelope, Builder, CurveGeometry, EdgeGeometry, FaceRole, + Frame3, GeometryError, Orientation, Surface, SurfaceGeometry, +}; +use crate::math::{scale, Interval}; +use crate::operations::modifying::boolean::operands::SphereInput; +use crate::primitives; +use Orientation::{Forward as F, Reverse as R}; + +pub(crate) struct SpherePatch { + pub(crate) frame: Frame3, + pub(crate) latitude: f64, + pub(crate) north: bool, + pub(crate) shared_edge: u32, + pub(crate) shared_vertex: u32, +} + +pub(crate) fn append_sphere( + out: &mut BrepEnvelope, + input: &SphereInput<'_>, + reversed: bool, +) -> Result { + let mut part = oriented_sphere_part(input, out.accuracy, reversed)?; + let [v, e, h, l, f, s, c, p, surface] = [ + out.topology.vertices.len(), + out.topology.edges.len(), + out.topology.halfedges.len(), + out.topology.loops.len(), + out.topology.faces.len(), + out.topology.shells.len(), + out.geometry.curves.len(), + out.geometry.pcurves.len(), + out.geometry.surfaces.len(), + ] + .map(|n| n as u32); + for vertex in &mut part.topology.vertices { + vertex.id += v; + vertex.outgoing_halfedge = vertex.outgoing_halfedge.map(|id| id + h); + } + for edge in &mut part.topology.edges { + edge.id += e; + edge.halfedge += h; + edge.twin_halfedge = edge.twin_halfedge.map(|id| id + h); + match &mut edge.geometry { + EdgeGeometry::Curve { curve, .. } => *curve += c, + EdgeGeometry::Collapsed { vertex } => *vertex += v, + } + } + for halfedge in &mut part.topology.halfedges { + halfedge.id += h; + halfedge.from += v; + halfedge.to += v; + halfedge.edge += e; + halfedge.twin = halfedge.twin.map(|id| id + h); + halfedge.next = halfedge.next.map(|id| id + h); + halfedge.prev = halfedge.prev.map(|id| id + h); + halfedge.face = halfedge.face.map(|id| id + f); + halfedge.loop_ref = halfedge.loop_ref.map(|id| id + l); + halfedge.geometry_use.pcurve = halfedge.geometry_use.pcurve.map(|id| id + p); + } + for loop_ in &mut part.topology.loops { + loop_.id += l; + loop_.start_halfedge += h; + loop_.face_ref += f; + } + for face in &mut part.topology.faces { + face.id += f; + face.key = format!("{f}:{}", face.key); + face.surface += surface; + face.trim.outer += l; + face.shell_ref = Some(s); + } + for shell in &mut part.topology.shells { + shell.id += s; + for face in &mut shell.faces { + *face += f; + } + } + add_part(out, part); + Ok(s) +} + +fn oriented_sphere_part( + input: &SphereInput<'_>, + accuracy: Accuracy, + reversed: bool, +) -> Result { + let mut part = primitives::sphere(input.brep.id.clone(), input.frame, input.radius, accuracy)?; + part.topology.faces[0].provenance = provenance( + input, + if reversed { + FaceRole::Cut + } else { + FaceRole::Preserved + }, + reversed, + ); + if reversed { + reverse_face(&mut part, 0); + } + Ok(part) +} + +fn add_part(out: &mut BrepEnvelope, part: BrepEnvelope) { + out.geometry.surfaces.extend(part.geometry.surfaces); + out.geometry.curves.extend(part.geometry.curves); + out.geometry.pcurves.extend(part.geometry.pcurves); + out.topology.vertices.extend(part.topology.vertices); + out.topology.edges.extend(part.topology.edges); + out.topology.halfedges.extend(part.topology.halfedges); + out.topology.loops.extend(part.topology.loops); + out.topology.faces.extend(part.topology.faces); + out.topology.shells.extend(part.topology.shells); +} + +pub(crate) fn patch( + builder: &mut Builder, + input: &SphereInput<'_>, + sphere_patch: &SpherePatch, + reversed: bool, +) -> Result<(), GeometryError> { + let SpherePatch { + frame, + latitude, + north, + shared_edge, + shared_vertex, + } = *sphere_patch; + let pole_lat = if north { + std::f64::consts::FRAC_PI_2 + } else { + -std::f64::consts::FRAC_PI_2 + }; + let (lo, hi) = if north { + (latitude, pole_lat) + } else { + (pole_lat, latitude) + }; + let surface = SurfaceGeometry::Sphere { + frame, + radius: input.radius, + }; + let pole = builder.vertex(surface.point_at([0.0, pole_lat])?); + let collapse = builder.edge_geometry(EdgeGeometry::Collapsed { vertex: pole }, true); + let seam = builder.edge( + CurveGeometry::Circle { + frame: Frame3 { + x: frame.x, + y: frame.z, + z: scale(frame.y, -1.0), + ..frame + }, + radius: input.radius, + }, + Interval::new(lo, hi)?, + true, + ); + let tau = std::f64::consts::TAU; + let (low, high) = if north { + (shared_vertex, pole) + } else { + (pole, shared_vertex) + }; + let bottom = if north { + boundary(shared_edge, low, low, F, uv_line([0.0, lo], [1.0, 0.0])) + } else { + boundary(collapse, low, low, F, uv_line([0.0, lo], [tau, 0.0])) + }; + let top = if north { + boundary(collapse, high, high, F, uv_line([tau, hi], [-tau, 0.0])) + } else { + boundary(shared_edge, high, high, R, uv_line([0.0, hi], [1.0, 0.0])) + }; + let face = builder.brep.topology.faces.len() as u32; + builder.face( + &format!("{face}:{}", input.brep.topology.faces[0].key), + surface, + [[0.0, tau], [lo, hi]], + vec![ + bottom, + boundary(seam, low, high, F, uv_line([tau, 0.0], [0.0, 1.0])), + top, + boundary(seam, high, low, R, uv_line([0.0, 0.0], [0.0, 1.0])), + ], + )?; + set_patch_provenance(builder, input, face, reversed); + Ok(()) +} + +fn set_patch_provenance(builder: &mut Builder, input: &SphereInput<'_>, face: u32, reversed: bool) { + builder.brep.topology.faces[face as usize].provenance = provenance( + input, + if reversed { + FaceRole::Cut + } else { + FaceRole::Split + }, + reversed, + ); + if reversed { + reverse_face(&mut builder.brep, face); + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/assembly/welded_vertex.rs b/main/opengeometry/src/operations/modifying/boolean/assembly/welded_vertex.rs new file mode 100644 index 0000000..8b46540 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/assembly/welded_vertex.rs @@ -0,0 +1,19 @@ +use crate::brep::{Accuracy, Builder}; +use crate::math::{norm, sub, Point3}; + +pub(crate) fn add_welded_vertex( + builder: &mut Builder, + vertices: &mut Vec<(Point3, u32)>, + point: Point3, + accuracy: Accuracy, +) -> u32 { + if let Some((_, id)) = vertices + .iter() + .find(|(existing, _)| norm(sub(*existing, point)) <= accuracy.geometric / 4.0) + { + return *id; + } + let id = builder.vertex(point); + vertices.push((point, id)); + id +} diff --git a/main/opengeometry/src/operations/modifying/boolean/batch/mod.rs b/main/opengeometry/src/operations/modifying/boolean/batch/mod.rs new file mode 100644 index 0000000..0b44ec5 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/batch/mod.rs @@ -0,0 +1,244 @@ +mod prismatic_profile; + +use super::assembly::{analytic_face_mappings, append_analytic_input}; +use super::dispatch::{boolean_brep_with_handlers, record_handler, HandlerId}; +use super::handlers::{boolean_rectilinear, subtract_vertical_arc_extrusion_batch}; +use super::operands::{all_planar, full_cylinder, unique_sources}; +use super::types::{BooleanOp, BooleanResult}; +use crate::brep::{Accuracy, BrepEnvelope, GeometryError, PatchBounds, SurfaceGeometry}; +use prismatic_profile::subtract_prismatic_profile_batch; + +pub fn subtract_planar_cutters( + host: &BrepEnvelope, + cutters: &[BrepEnvelope], + id: String, +) -> Result { + subtract_planar_cutters_with_handlers(host, cutters, id).map(|result| result.0) +} + +pub fn subtract_planar_cutters_with_handlers( + host: &BrepEnvelope, + cutters: &[BrepEnvelope], + id: String, +) -> Result<(BooleanResult, Vec), GeometryError> { + let (result, handlers) = subtract_planar_cutters_outcome_with_handlers(host, cutters, id); + result.map(|result| (result, handlers)) +} + +pub fn subtract_planar_cutters_outcome_with_handlers( + host: &BrepEnvelope, + cutters: &[BrepEnvelope], + id: String, +) -> (Result, Vec) { + let mut handlers = Vec::new(); + let result = subtract_planar_cutters_inner(host, cutters, id, &mut handlers); + (result, handlers) +} + +fn subtract_planar_cutters_inner( + host: &BrepEnvelope, + cutters: &[BrepEnvelope], + id: String, + handlers: &mut Vec, +) -> Result { + let mut nested = Vec::new(); + let result = record_handler(handlers, HandlerId::SubtractPlanarCutters, || { + planar_cutters_boolean(host, cutters, id, &mut nested) + })?; + handlers.append(&mut nested); + Ok(result) +} + +fn planar_cutters_boolean( + host: &BrepEnvelope, + cutters: &[BrepEnvelope], + id: String, + handlers: &mut Vec, +) -> Result { + if cutters.is_empty() || cutters.len() > 100 { + return Err(GeometryError::InvalidGeometry( + "planar batch subtraction requires between one and 100 cutters".into(), + )); + } + if cutters.len() == 1 { + let (result, nested) = + boolean_brep_with_handlers(host, &cutters[0], BooleanOp::Subtraction, id)?; + handlers.extend(nested); + return Ok(result); + } + let (accuracy, bounds) = accuracy_and_bounds(host, cutters)?; + let planar = cutters + .iter() + .filter(|cutter| all_planar(cutter)) + .collect::>(); + let cylinders = if planar.len() == cutters.len() { + Vec::new() + } else { + cutters + .iter() + .filter(|cutter| full_cylinder(cutter).is_ok()) + .collect::>() + }; + if !cylinders.is_empty() && planar.len() + cylinders.len() == cutters.len() { + if let Some(result) = subtract_prismatic_profile_batch(host, cutters, id.clone())? { + handlers.push(HandlerId::SubtractPrismaticProfileBatch.as_str().into()); + return Ok(result); + } + } + if !planar.is_empty() + && !cylinders.is_empty() + && planar.len() + cylinders.len() == cutters.len() + { + return mixed_cutters_boolean(host, cutters, planar, cylinders, id, handlers); + } + if host + .geometry + .surfaces + .iter() + .any(|surface| matches!(surface, SurfaceGeometry::Cylinder { .. })) + && cutters.iter().all(all_planar) + { + match subtract_vertical_arc_extrusion_batch( + host, + &cutters.iter().collect::>(), + id.clone(), + ) { + Ok(result) => { + handlers.push(HandlerId::SubtractVerticalArcExtrusionBatch.as_str().into()); + return Ok(result); + } + Err(GeometryError::CoverageGap { .. }) => {} + Err(error) => return Err(error), + } + } + check_disjoint_cutters(cutters, &bounds, accuracy)?; + let mut combined = BrepEnvelope::new(format!("{id}:cutters"), accuracy)?; + for cutter in cutters { + append_analytic_input(&mut combined, cutter)?; + } + combined.validate()?; + let result = boolean_rectilinear(host, &combined, BooleanOp::Subtraction, id)?; + handlers.push(HandlerId::BooleanRectilinear.as_str().into()); + Ok(result) +} + +fn accuracy_and_bounds( + host: &BrepEnvelope, + cutters: &[BrepEnvelope], +) -> Result<(Accuracy, Vec), GeometryError> { + let mut accuracy = host.accuracy; + let mut bounds = Vec::with_capacity(cutters.len()); + for cutter in cutters { + cutter.validate()?; + accuracy = Accuracy::combined(accuracy, cutter.accuracy); + bounds.push( + cutter.bounds()?.ok_or_else(|| { + GeometryError::InvalidGeometry("planar cutter has no bounds".into()) + })?, + ); + } + Ok((accuracy, bounds)) +} + +fn mixed_cutters_boolean( + host: &BrepEnvelope, + cutters: &[BrepEnvelope], + planar: Vec<&BrepEnvelope>, + cylinders: Vec<&BrepEnvelope>, + id: String, + handlers: &mut Vec, +) -> Result { + let planar_cutters = planar.into_iter().cloned().collect::>(); + let mut result = + subtract_planar_cutters_inner(host, &planar_cutters, format!("{id}:planar"), handlers) + .map_err(|error| match error { + GeometryError::CoverageGap { .. } => GeometryError::CoverageGap { + families: ["mixed cutter batch".into(), "planar stage".into()], + }, + other => other, + })?; + let count = cylinders.len(); + for (index, cylinder) in cylinders.into_iter().enumerate() { + let prior = result; + let (mut next, nested) = boolean_brep_with_handlers( + &prior.brep, + cylinder, + BooleanOp::Subtraction, + if index + 1 == count { + id.clone() + } else { + format!("{id}:round-{index}") + }, + ) + .map_err(|error| match error { + GeometryError::CoverageGap { .. } => GeometryError::CoverageGap { + families: ["mixed cutter batch".into(), "cylindrical stage".into()], + }, + other => other, + })?; + handlers.extend(nested); + set_lineage_provenance(&mut next.brep, &prior.brep)?; + next.brep.validate()?; + result = next; + } + result.report.face_mappings = + analytic_face_mappings(&result.brep, std::iter::once(host).chain(cutters.iter())); + Ok(result) +} + +fn set_lineage_provenance( + next: &mut BrepEnvelope, + prior: &BrepEnvelope, +) -> Result<(), GeometryError> { + for face in &mut next.topology.faces { + let mut lineage = Vec::new(); + for source in std::mem::take(&mut face.provenance.sources) { + if source.entity == prior.id && source.body == prior.id { + let previous = prior + .topology + .faces + .get(source.face as usize) + .ok_or_else(|| { + GeometryError::InvalidTopology("mixed cut source face is missing".into()) + })?; + if previous.provenance.sources.is_empty() { + lineage.push(source); + } else { + lineage.extend(previous.provenance.sources.iter().cloned()); + } + } else { + lineage.push(source); + } + } + face.provenance.sources = unique_sources(lineage); + } + Ok(()) +} + +fn check_disjoint_cutters( + cutters: &[BrepEnvelope], + bounds: &[PatchBounds], + accuracy: Accuracy, +) -> Result<(), GeometryError> { + for first in 0..cutters.len() { + for second in first + 1..cutters.len() { + if (0..3).all(|axis| { + bounds[first].axes[axis] + .hi + .min(bounds[second].axes[axis].hi) + - bounds[first].axes[axis] + .lo + .max(bounds[second].axes[axis].lo) + > 4.0 * accuracy.geometric + }) { + return Err(GeometryError::CoverageGap { + families: [ + "overlapping planar batch cutters".into(), + "overlapping planar batch cutters".into(), + ], + }); + } + } + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/batch/prismatic_profile.rs b/main/opengeometry/src/operations/modifying/boolean/batch/prismatic_profile.rs new file mode 100644 index 0000000..57b7382 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/batch/prismatic_profile.rs @@ -0,0 +1,447 @@ +use crate::brep::{ + unit, Accuracy, BrepEnvelope, Face, FaceProvenance, FaceRole, FaceSource, Frame3, + GeometryError, GeometryQuality, Surface, SurfaceGeometry, +}; +use crate::geom2d::{ + boolean_curved_regions, reversed_ring, CurveEdge2, CurveRegion2, PlanarBooleanOp, +}; +use crate::math::{add, cross, dot, scale, Interval, Point3}; +use crate::operations::modifying::boolean::assembly::{ + analytic_face_mappings, append_analytic_input, +}; +use crate::operations::modifying::boolean::operands::{ + brep_face_source, full_cylinder, full_planar_extrusion, unique_sources, +}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanReport, BooleanResult}; +use crate::primitives; +use crate::query::face_contains_uv; + +struct ProfileLayout { + frame: Frame3, + across: Point3, + geometric: f64, + dimensions: [f64; 3], +} + +pub(super) fn subtract_prismatic_profile_batch( + host: &BrepEnvelope, + cutters: &[BrepEnvelope], + id: String, +) -> Result, GeometryError> { + let Some(layout) = profile_layout(host, cutters)? else { + return Ok(None); + }; + let dimensions = layout.dimensions; + let host_region = CurveRegion2 { + outer: line_ring(&[ + [0.0, 0.0], + [0.0, dimensions[1]], + [dimensions[0], dimensions[1]], + [dimensions[0], 0.0], + ]), + holes: Vec::new(), + }; + let Some(cut_regions) = cut_regions(cutters, &layout)? else { + return Ok(None); + }; + let regions = boolean_curved_regions( + &[host_region], + &cut_regions, + PlanarBooleanOp::Subtraction, + layout.geometric, + ) + .map_err(mixed_profile_arrangement)?; + if regions.is_empty() { + return Ok(None); + } + let accuracy = profile_accuracy(host, cutters, layout.geometric); + let mut out = profile_solid(regions, id, &layout, accuracy)?; + let inputs = std::iter::once(host) + .chain(cutters.iter()) + .collect::>(); + set_profile_provenance(&mut out, host, cutters, &inputs, &layout)?; + finish_profile_batch_result(out, host, cutters, inputs).map(Some) +} + +fn profile_layout( + host: &BrepEnvelope, + cutters: &[BrepEnvelope], +) -> Result, GeometryError> { + let Ok(prism) = full_planar_extrusion(host) else { + return Ok(None); + }; + if prism.contours.len() != 1 + || prism.contours[0].len() != 4 + || host.topology.vertices.len() != 8 + { + return Ok(None); + } + let first_axis = cutters.iter().find_map(|cutter| { + full_planar_extrusion(cutter) + .ok() + .map(|input| input.frame.z) + .or_else(|| full_cylinder(cutter).ok().map(|input| input.frame.z)) + }); + let Some(across) = first_axis else { + return Ok(None); + }; + let up = prism.frame.z; + if dot(up, across).abs() > 1.0e-10 { + return Ok(None); + } + let along = unit(cross(up, across))?; + let basis = [along, up, across]; + let mut ranges = [[f64::INFINITY, f64::NEG_INFINITY]; 3]; + for vertex in &host.topology.vertices { + for axis in 0..3 { + let value = dot(vertex.position, basis[axis]); + ranges[axis][0] = ranges[axis][0].min(value); + ranges[axis][1] = ranges[axis][1].max(value); + } + } + let geometric = std::iter::once(host) + .chain(cutters.iter()) + .map(|brep| brep.accuracy.geometric) + .fold(0.0_f64, f64::max); + let dimensions = ranges.map(|range| range[1] - range[0]); + if dimensions.iter().any(|value| *value <= 4.0 * geometric) + || host.topology.vertices.iter().any(|vertex| { + (0..3).any(|axis| { + let value = dot(vertex.position, basis[axis]); + (value - ranges[axis][0]).abs() > geometric + && (value - ranges[axis][1]).abs() > geometric + }) + }) + { + return Ok(None); + } + let frame = Frame3 { + origin: add( + add(scale(along, ranges[0][0]), scale(up, ranges[1][0])), + scale(across, ranges[2][0]), + ), + x: along, + y: up, + z: across, + }; + frame.validate()?; + Ok(Some(ProfileLayout { + frame, + across, + geometric, + dimensions, + })) +} + +fn line_ring(points: &[[f64; 2]]) -> Vec { + points + .iter() + .enumerate() + .map(|(index, point)| CurveEdge2::Line { + from: *point, + to: points[(index + 1) % points.len()], + }) + .collect() +} + +fn cut_regions( + cutters: &[BrepEnvelope], + layout: &ProfileLayout, +) -> Result>, GeometryError> { + let mut cut_regions = Vec::with_capacity(cutters.len()); + for cutter in cutters { + let Some(region) = cut_region(cutter, layout)? else { + return Ok(None); + }; + cut_regions.push(region); + } + Ok(Some(cut_regions)) +} + +fn cut_region( + cutter: &BrepEnvelope, + layout: &ProfileLayout, +) -> Result, GeometryError> { + let &ProfileLayout { + frame, + across, + geometric, + dimensions, + } = layout; + let vertices_cover_depth = || { + let mut lo = f64::INFINITY; + let mut hi = f64::NEG_INFINITY; + for vertex in &cutter.topology.vertices { + let value = frame.local(vertex.position)[2]; + lo = lo.min(value); + hi = hi.max(value); + } + lo <= geometric && hi >= dimensions[2] - geometric + }; + if !vertices_cover_depth() { + return Ok(None); + } + if let Ok(input) = full_planar_extrusion(cutter) { + if dot(input.frame.z, across).abs() < 1.0 - 1.0e-10 { + return Ok(None); + } + let mut rings = input.contours.iter().map(|contour| { + line_ring( + &contour + .iter() + .map(|point| { + let local = frame.local(*point); + [local[0], local[1]] + }) + .collect::>(), + ) + }); + let outer = winding_ring( + rings.next().ok_or_else(|| { + GeometryError::InvalidTopology("profile cutter has no outer ring".into()) + })?, + false, + ); + let holes = rings.map(|ring| winding_ring(ring, true)).collect(); + Ok(Some(CurveRegion2 { outer, holes })) + } else if let Ok(input) = full_cylinder(cutter) { + if dot(input.frame.z, across).abs() < 1.0 - 1.0e-10 { + return Ok(None); + } + let center = frame.local(input.frame.origin); + Ok(Some(CurveRegion2 { + outer: vec![ + CurveEdge2::Arc { + center: [center[0], center[1]], + radius: input.radius, + start_angle: 0.0, + sweep_angle: -std::f64::consts::PI, + }, + CurveEdge2::Arc { + center: [center[0], center[1]], + radius: input.radius, + start_angle: -std::f64::consts::PI, + sweep_angle: -std::f64::consts::PI, + }, + ], + holes: Vec::new(), + })) + } else { + Ok(None) + } +} + +fn winding_ring(mut ring: Vec, positive: bool) -> Vec { + let area = ring.iter().map(CurveEdge2::twice_area).sum::(); + if (area > 0.0) != positive { + reverse_ring_in_place(&mut ring); + } + ring +} + +fn reverse_ring_in_place(ring: &mut Vec) { + *ring = reversed_ring(ring); +} + +fn mixed_profile_arrangement(error: GeometryError) -> GeometryError { + match error { + GeometryError::UnresolvedIntersection(reason) => GeometryError::UnresolvedIntersection( + format!("mixed profile batch arrangement: {reason}"), + ), + other => other, + } +} + +fn profile_accuracy(host: &BrepEnvelope, cutters: &[BrepEnvelope], geometric: f64) -> Accuracy { + Accuracy { + geometric, + intersection: std::iter::once(host) + .chain(cutters.iter()) + .map(|brep| brep.accuracy.intersection) + .fold(0.0_f64, f64::max), + tessellation: std::iter::once(host) + .chain(cutters.iter()) + .map(|brep| brep.accuracy.tessellation) + .fold(0.0_f64, f64::max), + exchange: std::iter::once(host) + .chain(cutters.iter()) + .map(|brep| brep.accuracy.exchange) + .fold(0.0_f64, f64::max), + } +} + +fn profile_solid( + regions: Vec, + id: String, + layout: &ProfileLayout, + accuracy: Accuracy, +) -> Result { + let &ProfileLayout { + frame, dimensions, .. + } = layout; + let single_region = regions.len() == 1; + let mut parts = Vec::with_capacity(regions.len()); + for (index, region) in regions.into_iter().enumerate() { + parts.push(primitives::arc_edged_extrusion_with_holes( + if single_region { + id.clone() + } else { + format!("{id}:part:{index}") + }, + frame, + profile_edges(region.outer), + region.holes.into_iter().map(profile_edges).collect(), + dimensions[2], + accuracy, + )?); + } + let out = if single_region { + parts.pop().ok_or_else(|| { + GeometryError::InvalidTopology("profile batch produced no material region".into()) + })? + } else { + let mut compound = BrepEnvelope::new(id, accuracy)?; + for part in &parts { + append_analytic_input(&mut compound, part)?; + } + compound + }; + Ok(out) +} + +fn profile_edges(ring: Vec) -> Vec { + ring.into_iter() + .map(|edge| match edge { + CurveEdge2::Line { from, to } => primitives::ProfileEdge::Line { from, to }, + CurveEdge2::Arc { + center, + radius, + start_angle, + sweep_angle, + } => primitives::ProfileEdge::Arc { + center, + radius, + start_angle, + sweep_angle, + }, + }) + .collect() +} + +fn set_profile_provenance( + out: &mut BrepEnvelope, + host: &BrepEnvelope, + cutters: &[BrepEnvelope], + inputs: &[&BrepEnvelope], + layout: &ProfileLayout, +) -> Result<(), GeometryError> { + for result_face in &mut out.topology.faces { + let surface = out.geometry.surface(result_face.surface)?; + let sources = profile_face_sources(surface, result_face, host, inputs, layout)?; + if sources.is_empty() { + return Err(GeometryError::InvalidTopology(format!( + "profile batch output face {} has no source", + result_face.key + ))); + } + let cut = sources + .iter() + .any(|source| cutters.iter().any(|cutter| cutter.id == source.entity)); + result_face.provenance = FaceProvenance { + sources: unique_sources(sources), + role: if cut { FaceRole::Cut } else { FaceRole::Split }, + reversed: cut, + }; + } + Ok(()) +} + +fn profile_face_sources( + surface: &SurfaceGeometry, + result_face: &Face, + host: &BrepEnvelope, + inputs: &[&BrepEnvelope], + layout: &ProfileLayout, +) -> Result, GeometryError> { + let &ProfileLayout { + across, geometric, .. + } = layout; + let mut sources = Vec::new(); + let cap = matches!(surface, SurfaceGeometry::Plane { frame: plane } + if dot(plane.z, across).abs() > 1.0 - 1.0e-10); + if cap { + let position = dot(surface.frame().origin, across); + for face in &host.topology.faces { + let SurfaceGeometry::Plane { + frame: source_frame, + } = host.geometry.surface(face.surface)? + else { + continue; + }; + if dot(source_frame.z, across).abs() > 1.0 - 1.0e-10 + && (dot(source_frame.origin, across) - position).abs() <= geometric + { + sources.push(brep_face_source(host, face.id)); + } + } + } else { + let uv = result_face.trim.uv_bounds.map(Interval::midpoint); + let sample = surface.point_at(uv)?; + for input in inputs { + for face in &input.topology.faces { + let source_surface = input.geometry.surface(face.surface)?; + let on = match source_surface { + SurfaceGeometry::Plane { + frame: source_frame, + } => { + source_frame.local(sample)[2].abs() <= geometric + && dot(source_frame.z, across).abs() < 1.0 - 1.0e-10 + } + SurfaceGeometry::Cylinder { + frame: source_frame, + radius, + } => { + let local = source_frame.local(sample); + (local[0].hypot(local[1]) - radius).abs() <= geometric + } + _ => false, + }; + if !on { + continue; + } + let hint = face.trim.uv_bounds.map(Interval::midpoint); + let source_uv = source_surface.project(sample, Some(hint))?; + if face_contains_uv(input, face, source_uv)? == Some(true) { + sources.push(brep_face_source(input, face.id)); + } + } + } + } + Ok(sources) +} + +fn finish_profile_batch_result( + mut out: BrepEnvelope, + host: &BrepEnvelope, + cutters: &[BrepEnvelope], + inputs: Vec<&BrepEnvelope>, +) -> Result { + out.revision = std::iter::once(host) + .chain(cutters.iter()) + .map(|brep| brep.revision) + .max() + .unwrap_or(0) + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; + out.validate()?; + Ok(BooleanResult { + report: BooleanReport { + operation: BooleanOp::Subtraction, + quality: GeometryQuality::Analytic, + contacts: Vec::new(), + coincident: false, + face_mappings: analytic_face_mappings(&out, inputs), + }, + brep: out, + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/dispatch/handler_id.rs b/main/opengeometry/src/operations/modifying/boolean/dispatch/handler_id.rs new file mode 100644 index 0000000..1d2d9c8 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/dispatch/handler_id.rs @@ -0,0 +1,70 @@ +#[derive(Clone, Copy)] +pub(crate) enum HandlerId { + CoincidentBoolean, + SeparateBoolean, + SphereCylinderBoolean, + SphereConicContainment, + CylinderConicContainment, + ConicBoxContainment, + SphereTorusContainment, + TorusCylinderContainment, + TorusBoxContainment, + TorusConicContainment, + TorusContainmentBoolean, + ConicContainmentBoolean, + SphereBoxBoolean, + CylinderBoxBoolean, + BooleanCylinders, + BooleanBoxes, + BooleanPlanarExtrusions, + BooleanRectilinear, + SubtractLayeredExtrusions, + SubtractPlanarPolyhedra, + BooleanSpheres, + SubtractVerticalArcExtrusion, + GenericTwoSidedCutterBandSubtraction, + GenericMultipleClosedLoopsBoolean, + GenericSingleClosedLoopBoolean, + GenericNonIntersectingBoolean, + SubtractPlanarCutters, + SubtractPrismaticProfileBatch, + SubtractVerticalArcExtrusionBatch, +} + +impl HandlerId { + pub(crate) fn as_str(self) -> &'static str { + match self { + Self::CoincidentBoolean => "coincident_boolean", + Self::SeparateBoolean => "separate_boolean", + Self::SphereCylinderBoolean => "sphere_cylinder_boolean", + Self::SphereConicContainment => "sphere_conic_containment", + Self::CylinderConicContainment => "cylinder_conic_containment", + Self::ConicBoxContainment => "conic_box_containment", + Self::SphereTorusContainment => "sphere_torus_containment", + Self::TorusCylinderContainment => "torus_cylinder_containment", + Self::TorusBoxContainment => "torus_box_containment", + Self::TorusConicContainment => "torus_conic_containment", + Self::TorusContainmentBoolean => "torus_containment_boolean", + Self::ConicContainmentBoolean => "conic_containment_boolean", + Self::SphereBoxBoolean => "sphere_box_boolean", + Self::CylinderBoxBoolean => "cylinder_box_boolean", + Self::BooleanCylinders => "boolean_cylinders", + Self::BooleanBoxes => "boolean_boxes", + Self::BooleanPlanarExtrusions => "boolean_planar_extrusions", + Self::BooleanRectilinear => "boolean_rectilinear", + Self::SubtractLayeredExtrusions => "subtract_layered_extrusions", + Self::SubtractPlanarPolyhedra => "subtract_planar_polyhedra", + Self::BooleanSpheres => "boolean_spheres", + Self::SubtractVerticalArcExtrusion => "subtract_vertical_arc_extrusion", + Self::GenericTwoSidedCutterBandSubtraction => { + "generic_two_sided_cutter_band_subtraction" + } + Self::GenericMultipleClosedLoopsBoolean => "generic_multiple_closed_loops_boolean", + Self::GenericSingleClosedLoopBoolean => "generic_single_closed_loop_boolean", + Self::GenericNonIntersectingBoolean => "generic_non_intersecting_boolean", + Self::SubtractPlanarCutters => "subtract_planar_cutters", + Self::SubtractPrismaticProfileBatch => "subtract_prismatic_profile_batch", + Self::SubtractVerticalArcExtrusionBatch => "subtract_vertical_arc_extrusion_batch", + } + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/dispatch/mod.rs b/main/opengeometry/src/operations/modifying/boolean/dispatch/mod.rs new file mode 100644 index 0000000..0b62593 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/dispatch/mod.rs @@ -0,0 +1,68 @@ +mod handler_id; +mod table; +mod trace; + +pub(super) use handler_id::HandlerId; +pub(super) use trace::record_handler; + +use super::handlers::{coincident_boolean, disjoint_boolean}; +use super::types::{BooleanOp, BooleanResult}; +use crate::brep::{BrepEnvelope, GeometryError}; +use table::{generic_boolean, specialized_boolean}; +use trace::record_optional; + +pub fn boolean_brep( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + boolean_brep_with_handlers(a, b, operation, id).map(|result| result.0) +} + +pub fn boolean_brep_with_handlers( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result<(BooleanResult, Vec), GeometryError> { + let (result, handlers) = boolean_brep_outcome_with_handlers(a, b, operation, id); + result.map(|result| (result, handlers)) +} + +pub fn boolean_brep_outcome_with_handlers( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> (Result, Vec) { + let mut handlers = Vec::new(); + let result = boolean_brep_inner(a, b, operation, id, &mut handlers); + (result, handlers) +} + +fn boolean_brep_inner( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, + handlers: &mut Vec, +) -> Result { + a.validate()?; + b.validate()?; + if a.geometry == b.geometry && a.topology == b.topology && a.solids == b.solids { + return record_handler(handlers, HandlerId::CoincidentBoolean, || { + coincident_boolean(a, b, operation, id) + }); + } + if let Some(result) = record_optional(handlers, HandlerId::SeparateBoolean, || { + disjoint_boolean(a, b, operation, id.clone()) + })? { + return Ok(result); + } + let specialized = specialized_boolean(a, b, operation, id.clone(), handlers); + match specialized { + Err(GeometryError::CoverageGap { .. }) => generic_boolean(a, b, operation, id, handlers), + result => result, + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/dispatch/table.rs b/main/opengeometry/src/operations/modifying/boolean/dispatch/table.rs new file mode 100644 index 0000000..b2aacd5 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/dispatch/table.rs @@ -0,0 +1,313 @@ +use super::handler_id::HandlerId; +use super::trace::{record_handler, record_optional}; +use crate::brep::{BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::operations::modifying::boolean::handlers::{ + boolean_boxes, boolean_cylinders, boolean_planar_extrusions, boolean_rectilinear, + boolean_spheres, conic_box_containment, conic_containment_boolean, cylinder_box_boolean, + cylinder_conic_containment, generic_multiple_closed_loops_boolean, + generic_non_intersecting_boolean, generic_single_closed_loop_boolean, + generic_two_sided_cutter_band_subtraction, sphere_box_boolean, sphere_conic_containment, + sphere_cylinder_boolean, sphere_torus_containment, subtract_layered_extrusions, + subtract_planar_polyhedra, subtract_vertical_arc_extrusion, torus_box_containment, + torus_conic_containment, torus_containment_boolean, torus_cylinder_containment, +}; +use crate::operations::modifying::boolean::operands::all_planar; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum OperandFamily { + Sphere, + Cylinder, + Torus, + Cone, + Planar, + Other, +} + +fn operand_family(body: &BrepEnvelope) -> OperandFamily { + match body.geometry.surfaces.first() { + Some(SurfaceGeometry::Sphere { .. }) => OperandFamily::Sphere, + Some(SurfaceGeometry::Cylinder { .. }) => OperandFamily::Cylinder, + Some(SurfaceGeometry::Torus { .. }) => OperandFamily::Torus, + Some(SurfaceGeometry::Cone { .. }) => OperandFamily::Cone, + Some(_) if all_planar(body) => OperandFamily::Planar, + _ => OperandFamily::Other, + } +} + +type BooleanHandler = + fn(&BrepEnvelope, &BrepEnvelope, BooleanOp, String) -> Result; + +struct FamilyHandler { + pair: [OperandFamily; 2], + symmetric: bool, + name: HandlerId, + call: BooleanHandler, +} + +const FAMILY_HANDLERS: &[FamilyHandler] = &[ + FamilyHandler { + pair: [OperandFamily::Sphere, OperandFamily::Cylinder], + symmetric: true, + name: HandlerId::SphereCylinderBoolean, + call: sphere_cylinder_boolean, + }, + FamilyHandler { + pair: [OperandFamily::Sphere, OperandFamily::Cone], + symmetric: true, + name: HandlerId::SphereConicContainment, + call: sphere_conic_containment, + }, + FamilyHandler { + pair: [OperandFamily::Cylinder, OperandFamily::Cone], + symmetric: true, + name: HandlerId::CylinderConicContainment, + call: cylinder_conic_containment, + }, + FamilyHandler { + pair: [OperandFamily::Cone, OperandFamily::Planar], + symmetric: true, + name: HandlerId::ConicBoxContainment, + call: conic_box_containment, + }, + FamilyHandler { + pair: [OperandFamily::Sphere, OperandFamily::Torus], + symmetric: true, + name: HandlerId::SphereTorusContainment, + call: sphere_torus_containment, + }, + FamilyHandler { + pair: [OperandFamily::Cylinder, OperandFamily::Torus], + symmetric: true, + name: HandlerId::TorusCylinderContainment, + call: torus_cylinder_containment, + }, + FamilyHandler { + pair: [OperandFamily::Planar, OperandFamily::Torus], + symmetric: true, + name: HandlerId::TorusBoxContainment, + call: torus_box_containment, + }, + FamilyHandler { + pair: [OperandFamily::Cone, OperandFamily::Torus], + symmetric: true, + name: HandlerId::TorusConicContainment, + call: torus_conic_containment, + }, + FamilyHandler { + pair: [OperandFamily::Torus, OperandFamily::Torus], + symmetric: false, + name: HandlerId::TorusContainmentBoolean, + call: torus_containment_boolean, + }, + FamilyHandler { + pair: [OperandFamily::Cone, OperandFamily::Cone], + symmetric: false, + name: HandlerId::ConicContainmentBoolean, + call: conic_containment_boolean, + }, + FamilyHandler { + pair: [OperandFamily::Sphere, OperandFamily::Planar], + symmetric: true, + name: HandlerId::SphereBoxBoolean, + call: sphere_box_boolean, + }, + FamilyHandler { + pair: [OperandFamily::Cylinder, OperandFamily::Planar], + symmetric: true, + name: HandlerId::CylinderBoxBoolean, + call: cylinder_box_boolean, + }, + FamilyHandler { + pair: [OperandFamily::Cylinder, OperandFamily::Cylinder], + symmetric: false, + name: HandlerId::BooleanCylinders, + call: boolean_cylinders, + }, +]; + +struct BoxHandler { + name: HandlerId, + subtract_only: bool, + call: BooleanHandler, +} + +fn dispatch_boxes( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + boolean_boxes(a, b, operation, id) +} + +fn dispatch_rectilinear( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + boolean_rectilinear(a, b, operation, id) +} + +fn dispatch_layered( + a: &BrepEnvelope, + b: &BrepEnvelope, + _: BooleanOp, + id: String, +) -> Result { + subtract_layered_extrusions(a, b, id) +} + +fn dispatch_polyhedra( + a: &BrepEnvelope, + b: &BrepEnvelope, + _: BooleanOp, + id: String, +) -> Result { + subtract_planar_polyhedra(a, b, id) +} + +const BOX_HANDLERS: &[BoxHandler] = &[ + BoxHandler { + name: HandlerId::BooleanBoxes, + subtract_only: false, + call: dispatch_boxes, + }, + BoxHandler { + name: HandlerId::BooleanPlanarExtrusions, + subtract_only: false, + call: boolean_planar_extrusions, + }, + BoxHandler { + name: HandlerId::BooleanRectilinear, + subtract_only: false, + call: dispatch_rectilinear, + }, + BoxHandler { + name: HandlerId::SubtractLayeredExtrusions, + subtract_only: true, + call: dispatch_layered, + }, + BoxHandler { + name: HandlerId::SubtractPlanarPolyhedra, + subtract_only: true, + call: dispatch_polyhedra, + }, +]; + +pub(super) fn specialized_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, + handlers: &mut Vec, +) -> Result { + let pair = [operand_family(a), operand_family(b)]; + for entry in FAMILY_HANDLERS { + if pair == entry.pair || (entry.symmetric && pair == [entry.pair[1], entry.pair[0]]) { + return record_handler(handlers, entry.name, || (entry.call)(a, b, operation, id)); + } + } + if pair == [OperandFamily::Planar, OperandFamily::Planar] { + let mut gap = None; + for entry in BOX_HANDLERS { + if entry.subtract_only && operation != BooleanOp::Subtraction { + break; + } + match record_handler(handlers, entry.name, || { + (entry.call)(a, b, operation, id.clone()) + }) { + Ok(result) => return Ok(result), + Err(error @ GeometryError::CoverageGap { .. }) => gap = Some(error), + Err(error) => return Err(error), + } + } + return Err(gap.unwrap_or_else(|| GeometryError::CoverageGap { + families: ["planar solid".into(), "planar solid".into()], + })); + } + record_handler(handlers, HandlerId::BooleanSpheres, || { + boolean_spheres(a, b, operation, id) + }) +} + +type OptionalBooleanHandler = fn( + &BrepEnvelope, + &BrepEnvelope, + BooleanOp, + String, +) -> Result, GeometryError>; + +struct GenericHandler { + name: HandlerId, + call: OptionalBooleanHandler, +} + +fn maybe_vertical_arc_extrusion( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result, GeometryError> { + if operation != BooleanOp::Subtraction + || !all_planar(b) + || !a + .geometry + .surfaces + .iter() + .any(|surface| matches!(surface, SurfaceGeometry::Cylinder { .. })) + { + return Ok(None); + } + match subtract_vertical_arc_extrusion(a, b, id) { + Ok(result) => Ok(Some(result)), + Err(GeometryError::CoverageGap { .. }) => Ok(None), + Err(error) => Err(error), + } +} + +const GENERIC_HANDLERS: &[GenericHandler] = &[ + GenericHandler { + name: HandlerId::SubtractVerticalArcExtrusion, + call: maybe_vertical_arc_extrusion, + }, + GenericHandler { + name: HandlerId::GenericTwoSidedCutterBandSubtraction, + call: generic_two_sided_cutter_band_subtraction, + }, + GenericHandler { + name: HandlerId::GenericMultipleClosedLoopsBoolean, + call: generic_multiple_closed_loops_boolean, + }, + GenericHandler { + name: HandlerId::GenericSingleClosedLoopBoolean, + call: generic_single_closed_loop_boolean, + }, + GenericHandler { + name: HandlerId::GenericNonIntersectingBoolean, + call: generic_non_intersecting_boolean, + }, +]; + +pub(super) fn generic_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, + handlers: &mut Vec, +) -> Result { + for entry in GENERIC_HANDLERS { + if let Some(result) = record_optional(handlers, entry.name, || { + (entry.call)(a, b, operation, id.clone()) + })? { + return Ok(result); + } + } + Err(GeometryError::CoverageGap { + families: [ + "generic analytic solid".into(), + "intersecting analytic solid".into(), + ], + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/dispatch/trace.rs b/main/opengeometry/src/operations/modifying/boolean/dispatch/trace.rs new file mode 100644 index 0000000..b6a59c8 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/dispatch/trace.rs @@ -0,0 +1,43 @@ +use super::handler_id::HandlerId; +use crate::brep::GeometryError; +use crate::operations::modifying::boolean::types::BooleanResult; + +pub(crate) fn record_handler( + handlers: &mut Vec, + name: HandlerId, + call: impl FnOnce() -> Result, +) -> Result { + let before = handlers.len(); + match call() { + Ok(result) => { + handlers.insert(before, name.as_str().into()); + Ok(result) + } + Err(error) => { + handlers.truncate(before); + Err(error) + } + } +} + +pub(super) fn record_optional( + handlers: &mut Vec, + name: HandlerId, + call: impl FnOnce() -> Result, GeometryError>, +) -> Result, GeometryError> { + let before = handlers.len(); + match call() { + Ok(Some(result)) => { + handlers.insert(before, name.as_str().into()); + Ok(Some(result)) + } + Ok(None) => { + handlers.truncate(before); + Ok(None) + } + Err(error) => { + handlers.truncate(before); + Err(error) + } + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/boxes.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/boxes.rs new file mode 100644 index 0000000..bfe45d8 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/boxes.rs @@ -0,0 +1,15 @@ +use super::rectilinear::boolean_grid; +use crate::brep::{BrepEnvelope, GeometryError}; +use crate::operations::modifying::boolean::operands::full_box; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub fn boolean_boxes( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let a = full_box(a)?; + let b = full_box(b)?; + boolean_grid(a, b, operation, id, true) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/coincident.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/coincident.rs new file mode 100644 index 0000000..39a6da3 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/coincident.rs @@ -0,0 +1,48 @@ +use crate::brep::{Accuracy, BrepEnvelope, FaceRole, FaceSource, GeometryError, GeometryQuality}; +use crate::operations::modifying::boolean::assembly::{ + analytic_face_mappings, append_analytic_input, +}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanReport, BooleanResult}; + +pub(crate) fn coincident_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let accuracy = Accuracy::combined(a.accuracy, b.accuracy); + let mut out = BrepEnvelope::new(id, accuracy)?; + if operation != BooleanOp::Subtraction { + append_analytic_input(&mut out, a)?; + for face in &mut out.topology.faces { + let counterpart = b.topology.faces.get(face.id as usize).ok_or_else(|| { + GeometryError::InvalidTopology( + "coincident operands have incompatible face ownership".into(), + ) + })?; + face.provenance.sources.push(FaceSource { + entity: b.id.clone(), + body: b.id.clone(), + key: counterpart.key.clone(), + face: counterpart.id, + }); + face.provenance.role = FaceRole::Coincident; + } + } + out.revision = a + .revision + .max(b.revision) + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; + out.validate()?; + Ok(BooleanResult { + report: BooleanReport { + operation, + quality: GeometryQuality::Analytic, + contacts: Vec::new(), + coincident: true, + face_mappings: analytic_face_mappings(&out, [a, b]), + }, + brep: out, + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/containment/conic_box.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/conic_box.rs new file mode 100644 index 0000000..a22b78d --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/conic_box.rs @@ -0,0 +1,86 @@ +use super::core::analytic_containment_boolean; +use crate::brep::{BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::math::{dot, sub}; +use crate::operations::modifying::boolean::operands::{ + conic_radius_at, full_box, full_conic_section, +}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(crate) fn conic_box_containment( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let (conic, box_, conic_is_a) = if matches!( + a.geometry.surfaces.first(), + Some(SurfaceGeometry::Cone { .. }) + ) { + (full_conic_section(a)?, full_box(b)?, true) + } else { + (full_conic_section(b)?, full_box(a)?, false) + }; + let clearance = 4.0 + * conic + .brep + .accuracy + .geometric + .max(box_.brep.accuracy.geometric); + let box_axes = [box_.frame.x, box_.frame.y, box_.frame.z]; + let endpoint_centers = [ + conic.frame.point([0.0, 0.0, conic.axial_range.lo]), + conic.frame.point([0.0, 0.0, conic.axial_range.hi]), + ]; + let endpoint_radii = [conic.lower_radius, conic.upper_radius]; + let mut conic_margin = f64::INFINITY; + for axis in 0..3 { + let radial_extent_rate = (1.0 - dot(conic.frame.z, box_axes[axis]).powi(2)) + .max(0.0) + .sqrt(); + let extents = [0, 1].map(|endpoint| { + let center = dot( + sub(endpoint_centers[endpoint], box_.frame.origin), + box_axes[axis], + ); + let radial = endpoint_radii[endpoint] * radial_extent_rate; + [center - radial, center + radial] + }); + let lo = extents[0][0].min(extents[1][0]); + let hi = extents[0][1].max(extents[1][1]); + conic_margin = conic_margin.min(lo).min(box_.size[axis] - hi); + } + + let mut box_margin = f64::INFINITY; + for corner in 0..8 { + let point = box_.frame.point(std::array::from_fn(|axis| { + if corner & (1 << axis) == 0 { + 0.0 + } else { + box_.size[axis] + } + })); + let local = conic.frame.local(point); + box_margin = box_margin + .min(local[2] - conic.axial_range.lo) + .min(conic.axial_range.hi - local[2]) + .min(conic_radius_at(&conic, local[2]) - local[0].hypot(local[1])); + } + let conic_inside_box = conic_margin > clearance; + let box_inside_conic = box_margin > clearance; + if conic_inside_box || box_inside_conic { + let b_inside_a = if conic_is_a { + box_inside_conic + } else { + conic_inside_box + }; + return analytic_containment_boolean(a, b, b_inside_a, operation, id); + } + if conic_margin.abs() <= clearance || box_margin.abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "cone/cuboid containment boundary is below geometric resolution".into(), + )); + } + Err(GeometryError::CoverageGap { + families: ["cone or frustum".into(), "cuboid".into()], + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/containment/conic_conic.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/conic_conic.rs new file mode 100644 index 0000000..959a0c1 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/conic_conic.rs @@ -0,0 +1,88 @@ +use super::core::analytic_containment_boolean; +use crate::brep::{BrepEnvelope, GeometryError}; +use crate::math::{cross, dot, norm, scale, sub, Interval}; +use crate::operations::modifying::boolean::handlers::coincident::coincident_boolean; +use crate::operations::modifying::boolean::operands::{ + conic_radius_at, full_conic_section, ConicSectionInput, +}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(crate) fn conic_containment_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let a = full_conic_section(a)?; + let b = full_conic_section(b)?; + let accuracy = a.brep.accuracy.geometric.max(b.brep.accuracy.geometric); + if norm(cross(a.frame.z, b.frame.z)) > 1.0e-12 || dot(a.frame.z, b.frame.z) <= 0.0 { + return Err(GeometryError::CoverageGap { + families: [ + "cone or frustum".into(), + "noncoaxial cone or frustum".into(), + ], + }); + } + let displacement = sub(b.frame.origin, a.frame.origin); + let axial_offset = dot(displacement, a.frame.z); + let radial_offset = norm(sub(displacement, scale(a.frame.z, axial_offset))); + if radial_offset > accuracy { + return Err(GeometryError::CoverageGap { + families: [ + "cone or frustum".into(), + "noncoaxial cone or frustum".into(), + ], + }); + } + + let b_in_a_range = Interval::new( + axial_offset + b.axial_range.lo, + axial_offset + b.axial_range.hi, + )?; + let a_in_b_range = Interval::new( + a.axial_range.lo - axial_offset, + a.axial_range.hi - axial_offset, + )?; + let containment_margins = + |inner: &ConicSectionInput<'_>, outer: &ConicSectionInput<'_>, inner_in_outer: Interval| { + [ + inner_in_outer.lo - outer.axial_range.lo, + outer.axial_range.hi - inner_in_outer.hi, + conic_radius_at(outer, inner_in_outer.lo) - inner.lower_radius, + conic_radius_at(outer, inner_in_outer.hi) - inner.upper_radius, + ] + }; + let b_in_a = containment_margins(&b, &a, b_in_a_range); + let a_in_b = containment_margins(&a, &b, a_in_b_range); + let strictly_inside = |margins: [f64; 4]| margins.into_iter().all(|margin| margin > accuracy); + let near_inside = |margins: [f64; 4]| { + margins.into_iter().all(|margin| margin >= -accuracy) + && margins.into_iter().any(|margin| margin.abs() <= accuracy) + }; + let b_inside_a = strictly_inside(b_in_a); + let a_inside_b = strictly_inside(a_in_b); + if b_inside_a || a_inside_b { + return analytic_containment_boolean(a.brep, b.brep, b_inside_a, operation, id); + } + + let same = a.brep.topology.faces.len() == b.brep.topology.faces.len() + && (b_in_a_range.lo - a.axial_range.lo).abs() <= accuracy + && (b_in_a_range.hi - a.axial_range.hi).abs() <= accuracy + && (b.lower_radius - a.lower_radius).abs() <= accuracy + && (b.upper_radius - a.upper_radius).abs() <= accuracy; + if same { + return coincident_boolean(a.brep, b.brep, operation, id); + } + if near_inside(b_in_a) || near_inside(a_in_b) { + return Err(GeometryError::UnresolvedIntersection( + "cone/frustum containment boundary is below geometric resolution".into(), + )); + } + Err(GeometryError::CoverageGap { + families: [ + "cone or frustum".into(), + "intersecting cone or frustum".into(), + ], + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/containment/core.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/core.rs new file mode 100644 index 0000000..1ad9e1a --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/core.rs @@ -0,0 +1,88 @@ +use crate::brep::{unit, Accuracy, BrepEnvelope, FaceRole, GeometryError, Surface}; +use crate::math::{scale, sub, Point3}; +use crate::operations::modifying::boolean::assembly::{ + append_analytic_input, finish_analytic_result, reverse_face, +}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use crate::query::{classify_point, face_contains_uv, PointClassification}; + +pub(crate) fn analytic_containment_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + b_inside_a: bool, + operation: BooleanOp, + id: String, +) -> Result { + let accuracy = Accuracy::combined(a.accuracy, b.accuracy); + let mut out = BrepEnvelope::new(id, accuracy)?; + let (contained, enclosing) = if b_inside_a { (b, a) } else { (a, b) }; + match operation { + BooleanOp::Union => { + append_analytic_input(&mut out, enclosing)?; + } + BooleanOp::Intersection => { + append_analytic_input(&mut out, contained)?; + } + BooleanOp::Subtraction if b_inside_a => { + append_analytic_input(&mut out, a)?; + let face_offset = out.topology.faces.len() as u32; + append_analytic_input(&mut out, b)?; + let cavity = out + .solids + .pop() + .ok_or_else(|| GeometryError::InvalidTopology("missing cutter solid".into()))? + .outer_shell; + out.solids[0].cavity_shells.push(cavity); + for face in face_offset..out.topology.faces.len() as u32 { + reverse_face(&mut out, face); + out.topology.faces[face as usize].provenance.role = FaceRole::Cut; + out.topology.faces[face as usize].provenance.reversed = true; + } + } + BooleanOp::Subtraction => {} + } + finish_analytic_result(out, a, b, true, operation, false) +} + +pub(crate) fn interior_sample(brep: &BrepEnvelope) -> Result { + let bounds = brep.bounds()?.ok_or_else(|| { + GeometryError::InvalidTopology("cannot classify an empty solid boundary".into()) + })?; + let model_scale = bounds + .axes + .iter() + .map(|axis| axis.width()) + .fold(brep.accuracy.geometric, f64::max); + let offsets = [ + (model_scale * 1.0e-6).max(32.0 * brep.accuracy.geometric), + 16.0 * brep.accuracy.geometric, + 8.0 * brep.accuracy.geometric, + 4.0 * brep.accuracy.geometric, + ]; + let samples = [0.5, 0.25, 0.75, 0.125, 0.875]; + for face in &brep.topology.faces { + let surface = brep.geometry.surface(face.surface)?; + for u in samples { + for v in samples { + let uv = [ + face.trim.uv_bounds[0].lo + u * face.trim.uv_bounds[0].width(), + face.trim.uv_bounds[1].lo + v * face.trim.uv_bounds[1].width(), + ]; + if face_contains_uv(brep, face, uv)? != Some(true) { + continue; + } + let boundary = surface.point_at(uv)?; + let outward = scale(unit(surface.normal_at(uv)?)?, face.sense.multiplier()); + for offset in offsets { + let candidate = sub(boundary, scale(outward, offset)); + if classify_point(brep, candidate)? == PointClassification::Inside { + return Ok(candidate); + } + } + } + } + } + Err(GeometryError::UnresolvedIntersection( + "could not certify an interior classification sample".into(), + )) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/containment/cylinder_conic.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/cylinder_conic.rs new file mode 100644 index 0000000..ea33ece --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/cylinder_conic.rs @@ -0,0 +1,106 @@ +use super::core::analytic_containment_boolean; +use crate::brep::{BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::math::{cross, dot, norm, scale, sub}; +use crate::operations::modifying::boolean::operands::{ + conic_radius_at, full_conic_section, full_cylinder, ConicSectionInput, CylinderInput, +}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(crate) fn cylinder_conic_containment( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let (cylinder, conic, cylinder_is_a) = if matches!( + a.geometry.surfaces.first(), + Some(SurfaceGeometry::Cylinder { .. }) + ) { + (full_cylinder(a)?, full_conic_section(b)?, true) + } else { + (full_cylinder(b)?, full_conic_section(a)?, false) + }; + let clearance = 4.0 + * cylinder + .brep + .accuracy + .geometric + .max(conic.brep.accuracy.geometric); + let alignment = dot(cylinder.frame.z, conic.frame.z); + if norm(cross(cylinder.frame.z, conic.frame.z)) > 1.0e-12 { + return Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "noncoaxial cone or frustum".into()], + }); + } + let displacement = sub(conic.frame.origin, cylinder.frame.origin); + let cylinder_axis_offset = dot(displacement, cylinder.frame.z); + let radial_offset = norm(sub( + displacement, + scale(cylinder.frame.z, cylinder_axis_offset), + )); + if radial_offset > clearance { + return Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "noncoaxial cone or frustum".into()], + }); + } + + let conic_margin = conic_margin_in_cylinder(&cylinder, &conic, alignment, cylinder_axis_offset); + let cylinder_margin = cylinder_margin_in_conic(&cylinder, &conic, alignment); + + let conic_inside_cylinder = conic_margin > clearance; + let cylinder_inside_conic = cylinder_margin > clearance; + if conic_inside_cylinder || cylinder_inside_conic { + let b_inside_a = if cylinder_is_a { + conic_inside_cylinder + } else { + cylinder_inside_conic + }; + return analytic_containment_boolean(a, b, b_inside_a, operation, id); + } + if conic_margin.abs() <= clearance || cylinder_margin.abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "cylinder/cone containment boundary is below geometric resolution".into(), + )); + } + Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "intersecting cone or frustum".into()], + }) +} + +fn conic_margin_in_cylinder( + cylinder: &CylinderInput<'_>, + conic: &ConicSectionInput<'_>, + alignment: f64, + cylinder_axis_offset: f64, +) -> f64 { + let conic_ends_in_cylinder = [ + cylinder_axis_offset + alignment * conic.axial_range.lo, + cylinder_axis_offset + alignment * conic.axial_range.hi, + ]; + let conic_axial_lo = conic_ends_in_cylinder[0].min(conic_ends_in_cylinder[1]); + let conic_axial_hi = conic_ends_in_cylinder[0].max(conic_ends_in_cylinder[1]); + conic_axial_lo + .min(cylinder.height - conic_axial_hi) + .min(cylinder.radius - conic.lower_radius.max(conic.upper_radius)) +} + +fn cylinder_margin_in_conic( + cylinder: &CylinderInput<'_>, + conic: &ConicSectionInput<'_>, + alignment: f64, +) -> f64 { + let cylinder_origin_in_conic = dot( + sub(cylinder.frame.origin, conic.frame.origin), + conic.frame.z, + ); + let cylinder_ends_in_conic = [ + cylinder_origin_in_conic, + cylinder_origin_in_conic + alignment * cylinder.height, + ]; + let cylinder_axial_lo = cylinder_ends_in_conic[0].min(cylinder_ends_in_conic[1]); + let cylinder_axial_hi = cylinder_ends_in_conic[0].max(cylinder_ends_in_conic[1]); + (cylinder_axial_lo - conic.axial_range.lo) + .min(conic.axial_range.hi - cylinder_axial_hi) + .min(conic_radius_at(conic, cylinder_ends_in_conic[0]) - cylinder.radius) + .min(conic_radius_at(conic, cylinder_ends_in_conic[1]) - cylinder.radius) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/containment/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/mod.rs new file mode 100644 index 0000000..51de1fb --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/mod.rs @@ -0,0 +1,22 @@ +mod conic_box; +mod conic_conic; +mod core; +mod cylinder_conic; +mod sphere_conic; +mod sphere_torus; +mod torus_box; +mod torus_conic; +mod torus_cylinder; +mod torus_torus; + +pub(crate) use self::core::analytic_containment_boolean; +pub(super) use self::core::interior_sample; +pub(crate) use conic_box::conic_box_containment; +pub(crate) use conic_conic::conic_containment_boolean; +pub(crate) use cylinder_conic::cylinder_conic_containment; +pub(crate) use sphere_conic::sphere_conic_containment; +pub(crate) use sphere_torus::sphere_torus_containment; +pub(crate) use torus_box::torus_box_containment; +pub(crate) use torus_conic::torus_conic_containment; +pub(crate) use torus_cylinder::torus_cylinder_containment; +pub(crate) use torus_torus::torus_containment_boolean; diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/containment/sphere_conic.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/sphere_conic.rs new file mode 100644 index 0000000..b476ccb --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/sphere_conic.rs @@ -0,0 +1,57 @@ +use super::core::analytic_containment_boolean; +use crate::brep::{BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::operations::modifying::boolean::operands::{full_conic_section, full_sphere}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(crate) fn sphere_conic_containment( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let (sphere, conic, sphere_is_a) = if matches!( + a.geometry.surfaces.first(), + Some(SurfaceGeometry::Sphere { .. }) + ) { + (full_sphere(a)?, full_conic_section(b)?, true) + } else { + (full_sphere(b)?, full_conic_section(a)?, false) + }; + let clearance = 4.0 + * sphere + .brep + .accuracy + .geometric + .max(conic.brep.accuracy.geometric); + let center = conic.frame.local(sphere.frame.origin); + let radial = center[0].hypot(center[1]); + let (sin_angle, cos_angle) = conic.semi_angle.sin_cos(); + let sphere_margin = (center[2] - conic.axial_range.lo - sphere.radius) + .min(conic.axial_range.hi - center[2] - sphere.radius) + .min(center[2] * sin_angle - radial * cos_angle - sphere.radius); + let conic_extent = [ + (radial + conic.lower_radius).hypot(conic.axial_range.lo - center[2]), + (radial + conic.upper_radius).hypot(conic.axial_range.hi - center[2]), + ] + .into_iter() + .fold(0.0_f64, f64::max); + let conic_margin = sphere.radius - conic_extent; + let sphere_inside_conic = sphere_margin > clearance; + let conic_inside_sphere = conic_margin > clearance; + if sphere_inside_conic || conic_inside_sphere { + let b_inside_a = if sphere_is_a { + conic_inside_sphere + } else { + sphere_inside_conic + }; + return analytic_containment_boolean(a, b, b_inside_a, operation, id); + } + if sphere_margin.abs() <= clearance || conic_margin.abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "sphere/cone containment boundary is below geometric resolution".into(), + )); + } + Err(GeometryError::CoverageGap { + families: ["sphere".into(), "intersecting cone or frustum".into()], + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/containment/sphere_torus.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/sphere_torus.rs new file mode 100644 index 0000000..699b28e --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/sphere_torus.rs @@ -0,0 +1,49 @@ +use super::core::analytic_containment_boolean; +use crate::brep::{BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::operations::modifying::boolean::operands::{full_sphere, full_torus}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(crate) fn sphere_torus_containment( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let (sphere, torus, sphere_is_a) = if matches!( + a.geometry.surfaces.first(), + Some(SurfaceGeometry::Sphere { .. }) + ) { + (full_sphere(a)?, full_torus(b)?, true) + } else { + (full_sphere(b)?, full_torus(a)?, false) + }; + let accuracy = sphere + .brep + .accuracy + .geometric + .max(torus.brep.accuracy.geometric); + let center = torus.frame.local(sphere.frame.origin); + let radial = center[0].hypot(center[1]); + let centerline_minimum = (radial - torus.major_radius).hypot(center[2]); + let centerline_maximum = (radial + torus.major_radius).hypot(center[2]); + let sphere_inside_torus = centerline_minimum + sphere.radius < torus.minor_radius - accuracy; + let torus_inside_sphere = centerline_maximum + torus.minor_radius < sphere.radius - accuracy; + if sphere_inside_torus || torus_inside_sphere { + let b_inside_a = if sphere_is_a { + torus_inside_sphere + } else { + sphere_inside_torus + }; + return analytic_containment_boolean(a, b, b_inside_a, operation, id); + } + if (centerline_minimum + sphere.radius - torus.minor_radius).abs() <= accuracy + || (centerline_maximum + torus.minor_radius - sphere.radius).abs() <= accuracy + { + return Err(GeometryError::UnresolvedIntersection( + "sphere/torus containment boundary is below geometric resolution".into(), + )); + } + Err(GeometryError::CoverageGap { + families: ["sphere".into(), "intersecting ring torus".into()], + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_box.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_box.rs new file mode 100644 index 0000000..b51290f --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_box.rs @@ -0,0 +1,49 @@ +use super::core::analytic_containment_boolean; +use crate::brep::{BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::math::dot; +use crate::operations::modifying::boolean::operands::{full_box, full_torus}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(crate) fn torus_box_containment( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let (torus, box_, torus_is_a) = if matches!( + a.geometry.surfaces.first(), + Some(SurfaceGeometry::Torus { .. }) + ) { + (full_torus(a)?, full_box(b)?, true) + } else { + (full_torus(b)?, full_box(a)?, false) + }; + let clearance = 4.0 + * torus + .brep + .accuracy + .geometric + .max(box_.brep.accuracy.geometric); + let box_axes = [box_.frame.x, box_.frame.y, box_.frame.z]; + let center = box_.frame.local(torus.frame.origin); + let torus_margin = (0..3).fold(f64::INFINITY, |margin, axis| { + let axial_rate = dot(torus.frame.z, box_axes[axis]); + let radial_rate = (1.0 - axial_rate * axial_rate).max(0.0).sqrt(); + let extent = torus.major_radius * radial_rate + torus.minor_radius; + margin + .min(center[axis] - extent) + .min(box_.size[axis] - center[axis] - extent) + }); + if torus_margin > clearance { + let b_inside_a = !torus_is_a; + return analytic_containment_boolean(a, b, b_inside_a, operation, id); + } + if torus_margin.abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "torus/cuboid containment boundary is below geometric resolution".into(), + )); + } + Err(GeometryError::CoverageGap { + families: ["ring torus".into(), "cuboid".into()], + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_conic.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_conic.rs new file mode 100644 index 0000000..11061b7 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_conic.rs @@ -0,0 +1,52 @@ +use super::core::analytic_containment_boolean; +use crate::brep::{BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::math::{cross, norm}; +use crate::operations::modifying::boolean::operands::{full_conic_section, full_torus}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(crate) fn torus_conic_containment( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let (torus, conic, torus_is_a) = if matches!( + a.geometry.surfaces.first(), + Some(SurfaceGeometry::Torus { .. }) + ) { + (full_torus(a)?, full_conic_section(b)?, true) + } else { + (full_torus(b)?, full_conic_section(a)?, false) + }; + let clearance = 4.0 + * torus + .brep + .accuracy + .geometric + .max(conic.brep.accuracy.geometric); + if norm(cross(torus.frame.z, conic.frame.z)) > 1.0e-12 { + return Err(GeometryError::CoverageGap { + families: ["ring torus".into(), "noncoaxial cone or frustum".into()], + }); + } + let center = conic.frame.local(torus.frame.origin); + let radial = center[0].hypot(center[1]); + let (sin_angle, cos_angle) = conic.semi_angle.sin_cos(); + let torus_margin = (center[2] - conic.axial_range.lo - torus.minor_radius) + .min(conic.axial_range.hi - center[2] - torus.minor_radius) + .min( + center[2] * sin_angle - (radial + torus.major_radius) * cos_angle - torus.minor_radius, + ); + if torus_margin > clearance { + let b_inside_a = !torus_is_a; + return analytic_containment_boolean(a, b, b_inside_a, operation, id); + } + if torus_margin.abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "torus/cone containment boundary is below geometric resolution".into(), + )); + } + Err(GeometryError::CoverageGap { + families: ["ring torus".into(), "intersecting cone or frustum".into()], + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_cylinder.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_cylinder.rs new file mode 100644 index 0000000..bb8dbfe --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_cylinder.rs @@ -0,0 +1,49 @@ +use super::core::analytic_containment_boolean; +use crate::brep::{BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::math::{cross, norm}; +use crate::operations::modifying::boolean::operands::{full_cylinder, full_torus}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(crate) fn torus_cylinder_containment( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let (torus, cylinder, torus_is_a) = if matches!( + a.geometry.surfaces.first(), + Some(SurfaceGeometry::Torus { .. }) + ) { + (full_torus(a)?, full_cylinder(b)?, true) + } else { + (full_torus(b)?, full_cylinder(a)?, false) + }; + let clearance = 4.0 + * torus + .brep + .accuracy + .geometric + .max(cylinder.brep.accuracy.geometric); + if norm(cross(torus.frame.z, cylinder.frame.z)) > 1.0e-12 { + return Err(GeometryError::CoverageGap { + families: ["ring torus".into(), "noncoaxial cylinder".into()], + }); + } + let center = cylinder.frame.local(torus.frame.origin); + let radial = center[0].hypot(center[1]); + let torus_margin = (cylinder.radius - radial - torus.major_radius - torus.minor_radius) + .min(center[2] - torus.minor_radius) + .min(cylinder.height - center[2] - torus.minor_radius); + if torus_margin > clearance { + let b_inside_a = !torus_is_a; + return analytic_containment_boolean(a, b, b_inside_a, operation, id); + } + if torus_margin.abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "torus/cylinder containment boundary is below geometric resolution".into(), + )); + } + Err(GeometryError::CoverageGap { + families: ["ring torus".into(), "intersecting cylinder".into()], + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_torus.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_torus.rs new file mode 100644 index 0000000..501c4ff --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/containment/torus_torus.rs @@ -0,0 +1,46 @@ +use super::core::analytic_containment_boolean; +use crate::brep::{BrepEnvelope, GeometryError}; +use crate::math::{cross, dot, norm}; +use crate::operations::modifying::boolean::handlers::coincident::coincident_boolean; +use crate::operations::modifying::boolean::operands::full_torus; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(crate) fn torus_containment_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let a = full_torus(a)?; + let b = full_torus(b)?; + let accuracy = a.brep.accuracy.geometric.max(b.brep.accuracy.geometric); + let axes_match = norm(cross(a.frame.z, b.frame.z)) <= 1e-12 && dot(a.frame.z, b.frame.z) > 0.0; + let displacement = a.frame.local(b.frame.origin); + let radial_offset = displacement[0].hypot(displacement[1]); + if !axes_match || radial_offset > accuracy { + return Err(GeometryError::CoverageGap { + families: ["ring torus".into(), "noncoaxial ring torus".into()], + }); + } + let centerline_distance = (a.major_radius - b.major_radius).hypot(displacement[2]); + let a_contains_b = centerline_distance + b.minor_radius < a.minor_radius - accuracy; + let b_contains_a = centerline_distance + a.minor_radius < b.minor_radius - accuracy; + let same = + centerline_distance <= accuracy && (a.minor_radius - b.minor_radius).abs() <= accuracy; + if same { + return coincident_boolean(a.brep, b.brep, operation, id); + } + if a_contains_b || b_contains_a { + return analytic_containment_boolean(a.brep, b.brep, a_contains_b, operation, id); + } + if (centerline_distance + b.minor_radius - a.minor_radius).abs() <= accuracy + || (centerline_distance + a.minor_radius - b.minor_radius).abs() <= accuracy + { + return Err(GeometryError::UnresolvedIntersection( + "torus containment boundary is below geometric resolution".into(), + )); + } + Err(GeometryError::CoverageGap { + families: ["ring torus".into(), "intersecting ring torus".into()], + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/mod.rs new file mode 100644 index 0000000..0cb7289 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/mod.rs @@ -0,0 +1,73 @@ +mod perpendicular; + +use super::containment::analytic_containment_boolean; +use crate::brep::{Accuracy, BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::math::dot; +use crate::operations::modifying::boolean::operands::{full_box, full_cylinder}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use perpendicular::perpendicular_cylinder_box_boolean; + +pub(crate) fn cylinder_box_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let (cylinder, box_, cylinder_is_a) = if matches!( + a.geometry.surfaces.first(), + Some(SurfaceGeometry::Cylinder { .. }) + ) { + (full_cylinder(a)?, full_box(b)?, true) + } else { + (full_cylinder(b)?, full_box(a)?, false) + }; + let clearance = 4.0 + * cylinder + .brep + .accuracy + .geometric + .max(box_.brep.accuracy.geometric); + let origin = box_.frame.local(cylinder.frame.origin); + let box_axes = [box_.frame.x, box_.frame.y, box_.frame.z]; + let mut cylinder_margin = f64::INFINITY; + for axis in 0..3 { + let axial_rate = dot(cylinder.frame.z, box_axes[axis]); + let radial_extent = cylinder.radius * (1.0 - axial_rate * axial_rate).max(0.0).sqrt(); + let other = origin[axis] + axial_rate * cylinder.height; + let lo = origin[axis].min(other) - radial_extent; + let hi = origin[axis].max(other) + radial_extent; + cylinder_margin = cylinder_margin.min(lo).min(box_.size[axis] - hi); + } + let mut box_margin = f64::INFINITY; + for corner in 0..8 { + let point = box_.frame.point(std::array::from_fn(|axis| { + if corner & (1 << axis) == 0 { + 0.0 + } else { + box_.size[axis] + } + })); + let local = cylinder.frame.local(point); + box_margin = box_margin + .min(cylinder.radius - local[0].hypot(local[1])) + .min(local[2]) + .min(cylinder.height - local[2]); + } + let cylinder_inside_box = cylinder_margin > clearance; + let box_inside_cylinder = box_margin > clearance; + if cylinder_inside_box || box_inside_cylinder { + let b_inside_a = if cylinder_is_a { + box_inside_cylinder + } else { + cylinder_inside_box + }; + return analytic_containment_boolean(a, b, b_inside_a, operation, id); + } + if cylinder_margin.abs() <= clearance || box_margin.abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "cylinder/cuboid containment boundary is below geometric resolution".into(), + )); + } + let accuracy = Accuracy::combined(cylinder.brep.accuracy, box_.brep.accuracy); + perpendicular_cylinder_box_boolean(&box_, &cylinder, cylinder_is_a, operation, id, accuracy) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/perpendicular/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/perpendicular/mod.rs new file mode 100644 index 0000000..b6cb0f3 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/perpendicular/mod.rs @@ -0,0 +1,232 @@ +mod tunnel; +mod tunnel_faces; + +use crate::brep::{ + boundary, Accuracy, BrepEnvelope, Builder, FaceRole, Frame3, GeometryError, Orientation, + PcurveGeometry, Use, +}; +use crate::math::dot; +use crate::operations::modifying::boolean::assembly::{ + add_box_faces, append_cylinder_segment, box_source, cylinder_source, face_provenance, + finish_analytic_result, +}; +use crate::operations::modifying::boolean::operands::{BoxInput, CylinderInput}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use crate::primitives; +use tunnel::{add_tunnel_rims, perpendicular_tunnel, Tunnel, TunnelRims}; +use tunnel_faces::{add_protruding_bands, add_tunnel_opening}; + +pub(super) fn perpendicular_cylinder_box_boolean( + box_: &BoxInput<'_>, + cylinder: &CylinderInput<'_>, + cylinder_is_a: bool, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let box_axes = [box_.frame.x, box_.frame.y, box_.frame.z]; + let tunnel = perpendicular_tunnel(box_, cylinder, box_axes, accuracy)?; + if operation == BooleanOp::Intersection { + return perpendicular_intersection_boolean( + box_, cylinder, &tunnel, operation, id, accuracy, + ); + } + if operation == BooleanOp::Subtraction && cylinder_is_a { + return cylinder_minus_box_boolean( + box_, + cylinder, + &tunnel, + cylinder_is_a, + operation, + id, + accuracy, + ); + } + let mut builder = Builder::new(id, accuracy)?; + let corners = primitives::add_box_corners(&mut builder, box_.frame, box_.size, box_axes)?; + let rims = add_tunnel_rims(&mut builder, cylinder, &tunnel, operation)?; + add_box_faces( + &mut builder, + box_, + &corners, + accuracy, + |_, face_index, face_frame| { + box_face_holes(face_index, face_frame, cylinder, &tunnel, &rims) + }, + |face_index| { + (face_index == tunnel.low_face && tunnel.crosses_low) + || (face_index == tunnel.high_face && tunnel.crosses_high) + }, + )?; + if operation == BooleanOp::Subtraction { + add_tunnel_opening(&mut builder, cylinder, &tunnel, &rims)?; + } else { + add_protruding_bands(&mut builder, cylinder, &tunnel, &rims)?; + } + let out = builder.finish_solid()?; + finish_analytic_result( + out, + box_.brep, + cylinder.brep, + !cylinder_is_a, + operation, + false, + ) +} + +fn perpendicular_intersection_boolean( + box_: &BoxInput<'_>, + cylinder: &CylinderInput<'_>, + tunnel: &Tunnel, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let mut out = primitives::cylinder(id, tunnel.frame, cylinder.radius, tunnel.height, accuracy)?; + out.topology.faces[0].provenance = + face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Split, false); + out.topology.faces[1].provenance = face_provenance( + vec![if tunnel.crosses_low { + box_source(box_, tunnel.low_face) + } else { + cylinder_source(cylinder, tunnel.cylinder_low_face) + }], + FaceRole::Split, + false, + ); + out.topology.faces[2].provenance = face_provenance( + vec![if tunnel.crosses_high { + box_source(box_, tunnel.high_face) + } else { + cylinder_source(cylinder, tunnel.cylinder_high_face) + }], + FaceRole::Split, + false, + ); + finish_analytic_result(out, box_.brep, cylinder.brep, true, operation, false) +} + +fn cylinder_minus_box_boolean( + box_: &BoxInput<'_>, + cylinder: &CylinderInput<'_>, + tunnel: &Tunnel, + cylinder_is_a: bool, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let mut out = BrepEnvelope::new(id, accuracy)?; + if tunnel.crosses_low { + let mut frame = tunnel.frame; + frame.origin = box_.frame.point({ + let mut point = tunnel.local_origin; + point[tunnel.axis] = tunnel.axial_span[0]; + point + }); + append_cylinder_segment( + &mut out, + frame, + cylinder.radius, + -tunnel.axial_span[0], + [ + face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Split, false), + face_provenance( + vec![cylinder_source(cylinder, tunnel.cylinder_low_face)], + FaceRole::Preserved, + false, + ), + face_provenance(vec![box_source(box_, tunnel.low_face)], FaceRole::Cut, true), + ], + 0, + )?; + } + if tunnel.crosses_high { + let mut frame = tunnel.frame; + frame.origin = box_.frame.point({ + let mut point = tunnel.local_origin; + point[tunnel.axis] = box_.size[tunnel.axis]; + point + }); + append_cylinder_segment( + &mut out, + frame, + cylinder.radius, + tunnel.axial_span[1] - box_.size[tunnel.axis], + [ + face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Split, false), + face_provenance( + vec![box_source(box_, tunnel.high_face)], + FaceRole::Cut, + true, + ), + face_provenance( + vec![cylinder_source(cylinder, tunnel.cylinder_high_face)], + FaceRole::Preserved, + false, + ), + ], + usize::from(tunnel.crosses_low), + )?; + } + finish_analytic_result( + out, + box_.brep, + cylinder.brep, + !cylinder_is_a, + operation, + false, + ) +} + +fn box_face_holes( + face_index: usize, + face_frame: Frame3, + cylinder: &CylinderInput<'_>, + tunnel: &Tunnel, + rims: &TunnelRims, +) -> Result>, GeometryError> { + let holes = if (face_index == tunnel.low_face && tunnel.crosses_low) + || (face_index == tunnel.high_face && tunnel.crosses_high) + { + let is_low = face_index == tunnel.low_face; + let circle_frame = if is_low { + tunnel.frame + } else { + rims.high_frame + }; + let circle_center = face_frame.local(circle_frame.origin); + let (vertex, circle) = if is_low { + rims.low_boundary.ok_or_else(|| { + GeometryError::InvalidTopology("missing low cylinder boundary".into()) + })? + } else { + rims.high_boundary.ok_or_else(|| { + GeometryError::InvalidTopology("missing high cylinder boundary".into()) + })? + }; + vec![vec![boundary( + circle, + vertex, + vertex, + if is_low { + Orientation::Forward + } else { + Orientation::Reverse + }, + PcurveGeometry::Conic2 { + origin: [circle_center[0], circle_center[1]], + axis_a: [ + cylinder.radius * dot(circle_frame.x, face_frame.x), + cylinder.radius * dot(circle_frame.x, face_frame.y), + ], + axis_b: [ + cylinder.radius * dot(circle_frame.y, face_frame.x), + cylinder.radius * dot(circle_frame.y, face_frame.y), + ], + }, + )]] + } else { + Vec::new() + }; + Ok(holes) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/perpendicular/tunnel.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/perpendicular/tunnel.rs new file mode 100644 index 0000000..14820a6 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/perpendicular/tunnel.rs @@ -0,0 +1,196 @@ +use crate::brep::{Accuracy, Builder, CurveGeometry, Frame3, GeometryError}; +use crate::math::{dot, Interval, Point3}; +use crate::operations::modifying::boolean::assembly::circular_boundary; +use crate::operations::modifying::boolean::operands::{BoxInput, CylinderInput}; +use crate::operations::modifying::boolean::types::BooleanOp; + +pub(super) struct Tunnel { + pub(super) axis: usize, + pub(super) alignment: f64, + pub(super) local_origin: Point3, + pub(super) axial_span: [f64; 2], + pub(super) crosses_low: bool, + pub(super) crosses_high: bool, + pub(super) clipped_low: f64, + pub(super) frame: Frame3, + pub(super) height: f64, + pub(super) low_face: usize, + pub(super) high_face: usize, + pub(super) cylinder_low_face: u32, + pub(super) cylinder_high_face: u32, +} + +pub(super) struct TunnelRims { + pub(super) high_frame: Frame3, + pub(super) low_boundary: Option<(u32, u32)>, + pub(super) high_boundary: Option<(u32, u32)>, + pub(super) seam: Option, +} + +pub(super) fn perpendicular_tunnel( + box_: &BoxInput<'_>, + cylinder: &CylinderInput<'_>, + box_axes: [Point3; 3], + accuracy: Accuracy, +) -> Result { + let (axis, alignment) = aligned_box_axis(cylinder, box_axes)?; + let local_origin = box_.frame.local(cylinder.frame.origin); + let other_axes = match axis { + 0 => [1, 2], + 1 => [0, 2], + _ => [0, 1], + }; + check_radial_margins(box_, cylinder, local_origin, other_axes, accuracy)?; + let axial_end = local_origin[axis] + alignment * cylinder.height; + let axial_span = [ + local_origin[axis].min(axial_end), + local_origin[axis].max(axial_end), + ]; + let low_clearance = -axial_span[0]; + let high_clearance = axial_span[1] - box_.size[axis]; + if low_clearance.abs() <= 4.0 * accuracy.geometric + || high_clearance.abs() <= 4.0 * accuracy.geometric + { + return Err(GeometryError::UnresolvedIntersection( + "cylinder cap meets a cuboid boundary within geometric resolution".into(), + )); + } + let crosses_low = low_clearance > 4.0 * accuracy.geometric; + let crosses_high = high_clearance > 4.0 * accuracy.geometric; + let clipped_low = axial_span[0].max(0.0); + let clipped_high = axial_span[1].min(box_.size[axis]); + if clipped_high - clipped_low <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "cylinder/cuboid overlap is below geometric resolution".into(), + )); + } + if !crosses_low && !crosses_high { + return Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "contained cuboid intersection".into()], + }); + } + + let mut center = local_origin; + center[axis] = clipped_low; + let low_center = box_.frame.point(center); + let tunnel_frame = Frame3::from_axis(low_center, box_axes[axis], box_axes[other_axes[0]])?; + let tunnel_height = clipped_high - clipped_low; + let low_face = match axis { + 0 => 2, + 1 => 4, + _ => 0, + }; + let high_face = low_face + 1; + let cylinder_low_face = if alignment > 0.0 { 1 } else { 2 }; + let cylinder_high_face = if alignment > 0.0 { 2 } else { 1 }; + Ok(Tunnel { + axis, + alignment, + local_origin, + axial_span, + crosses_low, + crosses_high, + clipped_low, + frame: tunnel_frame, + height: tunnel_height, + low_face, + high_face, + cylinder_low_face, + cylinder_high_face, + }) +} + +fn aligned_box_axis( + cylinder: &CylinderInput<'_>, + box_axes: [Point3; 3], +) -> Result<(usize, f64), GeometryError> { + let (axis, alignment) = box_axes + .iter() + .enumerate() + .map(|(axis, direction)| (axis, dot(cylinder.frame.z, *direction))) + .max_by(|left, right| left.1.abs().total_cmp(&right.1.abs())) + .ok_or_else(|| GeometryError::InvalidGeometry("cuboid has no frame axes".into()))?; + if 1.0 - alignment.abs() > 1.0e-12 { + return Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "oblique cuboid intersection".into()], + }); + } + Ok((axis, alignment)) +} + +fn check_radial_margins( + box_: &BoxInput<'_>, + cylinder: &CylinderInput<'_>, + local_origin: Point3, + other_axes: [usize; 2], + accuracy: Accuracy, +) -> Result<(), GeometryError> { + let radial_margins = other_axes.map(|candidate| { + (local_origin[candidate] - cylinder.radius) + .min(box_.size[candidate] - local_origin[candidate] - cylinder.radius) + }); + if radial_margins + .iter() + .any(|margin| margin.abs() <= 4.0 * accuracy.geometric) + { + return Err(GeometryError::UnresolvedIntersection( + "cylindrical opening is tangent to a cuboid side within geometric resolution".into(), + )); + } + if radial_margins.iter().any(|margin| *margin < 0.0) { + return Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "side-intersecting cuboid".into()], + }); + } + Ok(()) +} + +pub(super) fn add_tunnel_rims( + builder: &mut Builder, + cylinder: &CylinderInput<'_>, + tunnel: &Tunnel, + operation: BooleanOp, +) -> Result { + let high_frame = Frame3 { + origin: tunnel.frame.point([0.0, 0.0, tunnel.height]), + ..tunnel.frame + }; + let low_boundary = if operation == BooleanOp::Subtraction || tunnel.crosses_low { + Some(circular_boundary( + builder, + tunnel.frame, + cylinder.radius, + 0.0, + )?) + } else { + None + }; + let high_boundary = if operation == BooleanOp::Subtraction || tunnel.crosses_high { + Some(circular_boundary( + builder, + tunnel.frame, + cylinder.radius, + tunnel.height, + )?) + } else { + None + }; + let seam = if operation == BooleanOp::Subtraction { + Some(builder.edge( + CurveGeometry::Line { + origin: tunnel.frame.point([cylinder.radius, 0.0, 0.0]), + direction: tunnel.frame.z, + }, + Interval::new(0.0, tunnel.height)?, + true, + )) + } else { + None + }; + Ok(TunnelRims { + high_frame, + low_boundary, + high_boundary, + seam, + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/perpendicular/tunnel_faces.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/perpendicular/tunnel_faces.rs new file mode 100644 index 0000000..3c0bcf8 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_box/perpendicular/tunnel_faces.rs @@ -0,0 +1,243 @@ +use super::tunnel::{Tunnel, TunnelRims}; +use crate::brep::{ + boundary, uv_line, Builder, FaceRole, Frame3, GeometryError, Orientation, PcurveGeometry, + SurfaceGeometry, +}; +use crate::math::scale; +use crate::operations::modifying::boolean::assembly::{ + circular_boundary, cylinder_band, cylinder_cap, cylinder_source, face_provenance, reverse_face, + CylinderBand, +}; +use crate::operations::modifying::boolean::operands::CylinderInput; + +pub(super) fn add_tunnel_opening( + builder: &mut Builder, + cylinder: &CylinderInput<'_>, + tunnel: &Tunnel, + rims: &TunnelRims, +) -> Result<(), GeometryError> { + let (low_vertex, low_circle) = rims + .low_boundary + .ok_or_else(|| GeometryError::InvalidTopology("missing low cylinder boundary".into()))?; + let (high_vertex, high_circle) = rims + .high_boundary + .ok_or_else(|| GeometryError::InvalidTopology("missing high cylinder boundary".into()))?; + let seam = rims + .seam + .ok_or_else(|| GeometryError::InvalidTopology("missing cylinder seam boundary".into()))?; + add_tunnel_face( + builder, + cylinder, + tunnel, + (low_vertex, low_circle), + (high_vertex, high_circle), + seam, + )?; + if tunnel.crosses_low != tunnel.crosses_high { + add_pocket_cap( + builder, + cylinder, + tunnel, + rims.high_frame, + (low_vertex, low_circle), + (high_vertex, high_circle), + )?; + } + Ok(()) +} + +fn add_tunnel_face( + builder: &mut Builder, + cylinder: &CylinderInput<'_>, + tunnel: &Tunnel, + low: (u32, u32), + high: (u32, u32), + seam: u32, +) -> Result<(), GeometryError> { + let tau = std::f64::consts::TAU; + let (low_vertex, low_circle) = low; + let (high_vertex, high_circle) = high; + let tunnel_face = builder.brep.topology.faces.len() as u32; + builder.face( + "cutter:tunnel", + SurfaceGeometry::Cylinder { + frame: tunnel.frame, + radius: cylinder.radius, + }, + [[0.0, tau], [0.0, tunnel.height]], + vec![ + boundary( + low_circle, + low_vertex, + low_vertex, + Orientation::Forward, + uv_line([0.0, 0.0], [1.0, 0.0]), + ), + boundary( + seam, + low_vertex, + high_vertex, + Orientation::Forward, + uv_line([tau, 0.0], [0.0, 1.0]), + ), + boundary( + high_circle, + high_vertex, + high_vertex, + Orientation::Reverse, + uv_line([0.0, tunnel.height], [1.0, 0.0]), + ), + boundary( + seam, + high_vertex, + low_vertex, + Orientation::Reverse, + uv_line([0.0, 0.0], [0.0, 1.0]), + ), + ], + )?; + builder.brep.topology.faces[tunnel_face as usize].provenance = + face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Cut, true); + reverse_face(&mut builder.brep, tunnel_face); + Ok(()) +} + +fn add_pocket_cap( + builder: &mut Builder, + cylinder: &CylinderInput<'_>, + tunnel: &Tunnel, + high_frame: Frame3, + low: (u32, u32), + high: (u32, u32), +) -> Result<(), GeometryError> { + let (low_vertex, low_circle) = low; + let (high_vertex, high_circle) = high; + let (key, circle, vertex, mut cap_frame, sense, axis_b, cylinder_cap) = if tunnel.crosses_low { + ( + "cutter:pocket_cap", + high_circle, + high_vertex, + high_frame, + Orientation::Forward, + [0.0, cylinder.radius], + if tunnel.alignment > 0.0 { 2 } else { 1 }, + ) + } else { + let mut cap_frame = tunnel.frame; + cap_frame.y = scale(cap_frame.y, -1.0); + cap_frame.z = scale(cap_frame.z, -1.0); + ( + "cutter:pocket_cap", + low_circle, + low_vertex, + cap_frame, + Orientation::Reverse, + [0.0, -cylinder.radius], + if tunnel.alignment > 0.0 { 1 } else { 2 }, + ) + }; + let cap_face = builder.brep.topology.faces.len() as u32; + cap_frame.origin = if tunnel.crosses_low { + high_frame.origin + } else { + tunnel.frame.origin + }; + builder.face( + key, + SurfaceGeometry::Plane { frame: cap_frame }, + [[-cylinder.radius, cylinder.radius]; 2], + vec![boundary( + circle, + vertex, + vertex, + sense, + PcurveGeometry::Conic2 { + origin: [0.0; 2], + axis_a: [cylinder.radius, 0.0], + axis_b, + }, + )], + )?; + builder.brep.topology.faces[cap_face as usize].provenance = face_provenance( + vec![cylinder_source(cylinder, cylinder_cap)], + FaceRole::Cut, + true, + ); + reverse_face(&mut builder.brep, cap_face); + Ok(()) +} + +pub(super) fn add_protruding_bands( + builder: &mut Builder, + cylinder: &CylinderInput<'_>, + tunnel: &Tunnel, + rims: &TunnelRims, +) -> Result<(), GeometryError> { + if tunnel.crosses_low { + let entry = rims.low_boundary.ok_or_else(|| { + GeometryError::InvalidTopology("missing low cylinder boundary".into()) + })?; + let outer_z = tunnel.axial_span[0] - tunnel.clipped_low; + let outer = circular_boundary(builder, tunnel.frame, cylinder.radius, outer_z)?; + cylinder_band( + builder, + &CylinderBand { + frame: tunnel.frame, + radius: cylinder.radius, + z0: outer_z, + z1: 0.0, + low: outer, + high: entry, + }, + face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Split, false), + false, + )?; + cylinder_cap( + builder, + tunnel.frame, + cylinder.radius, + outer_z, + outer, + false, + face_provenance( + vec![cylinder_source(cylinder, tunnel.cylinder_low_face)], + FaceRole::Preserved, + false, + ), + )?; + } + if tunnel.crosses_high { + let entry = rims.high_boundary.ok_or_else(|| { + GeometryError::InvalidTopology("missing high cylinder boundary".into()) + })?; + let outer_z = tunnel.axial_span[1] - tunnel.clipped_low; + let outer = circular_boundary(builder, tunnel.frame, cylinder.radius, outer_z)?; + cylinder_band( + builder, + &CylinderBand { + frame: tunnel.frame, + radius: cylinder.radius, + z0: tunnel.height, + z1: outer_z, + low: entry, + high: outer, + }, + face_provenance(vec![cylinder_source(cylinder, 0)], FaceRole::Split, false), + false, + )?; + cylinder_cap( + builder, + tunnel.frame, + cylinder.radius, + outer_z, + outer, + true, + face_provenance( + vec![cylinder_source(cylinder, tunnel.cylinder_high_face)], + FaceRole::Preserved, + false, + ), + )?; + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/arcs.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/arcs.rs new file mode 100644 index 0000000..281b5a5 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/arcs.rs @@ -0,0 +1,288 @@ +use crate::brep::{ + boundary, padded_uv_bounds, plane_boundary, uv_line, Builder, Frame3, GeometryError, + Orientation, SurfaceGeometry, Use, +}; +use crate::math::{norm, sub, Interval, Point3}; +use Orientation::{Forward as F, Reverse as R}; + +#[derive(Clone, Copy)] +pub(super) struct CylinderArc { + pub(super) frame: Frame3, + pub(super) radius: f64, + pub(super) range: Interval, + pub(super) start: usize, + pub(super) end: usize, + pub(super) reversed: bool, +} + +pub(super) struct ArcFaceBoundary { + pub(super) bottom_edge: u32, + pub(super) top_edge: u32, + pub(super) vertical_edges: [u32; 2], + pub(super) vertices: [[u32; 2]; 2], +} + +pub(super) fn selected_cylinder_arc( + frame: Frame3, + radius: f64, + points: [Point3; 2], + other_center: Point3, + other_radius: f64, + keep_inside: bool, + reversed: bool, +) -> Result { + let angles = points.map(|point| circle_parameter(frame, point)); + let tau = std::f64::consts::TAU; + let forward_end = if angles[1] > angles[0] { + angles[1] + } else { + angles[1] + tau + }; + let candidates = [ + (angles[0], forward_end, 0, 1), + (angles[1], angles[0] + tau, 1, 0), + ]; + for (lo, hi, start, end) in candidates { + let midpoint = frame.point([ + radius * ((lo + hi) * 0.5).cos(), + radius * ((lo + hi) * 0.5).sin(), + 0.0, + ]); + let inside = norm(sub(midpoint, other_center)) < other_radius; + if inside == keep_inside { + return Ok(CylinderArc { + frame, + radius, + range: Interval::new(lo, hi)?, + start, + end, + reversed, + }); + } + } + Err(GeometryError::UnresolvedIntersection( + "could not isolate the selected cylinder boundary arc".into(), + )) +} + +fn circle_parameter(frame: Frame3, point: Point3) -> f64 { + let local = frame.local(point); + local[1].atan2(local[0]).rem_euclid(std::f64::consts::TAU) +} + +pub(super) fn add_cylinder_arc_face( + builder: &mut Builder, + key: &str, + arc: CylinderArc, + face_boundary: &ArcFaceBoundary, + height: f64, +) -> Result<(), GeometryError> { + let ArcFaceBoundary { + bottom_edge, + top_edge, + vertical_edges, + vertices, + } = *face_boundary; + let uses = + cylinder_arc_face_uses(arc, bottom_edge, top_edge, vertical_edges, vertices, height)?; + let face = builder.brep.topology.faces.len(); + builder.face( + key, + SurfaceGeometry::Cylinder { + frame: arc.frame, + radius: arc.radius, + }, + [[arc.range.lo, arc.range.hi], [0.0, height]], + uses, + )?; + if arc.reversed { + builder.brep.topology.faces[face].sense = R; + } + Ok(()) +} + +fn cylinder_arc_face_uses( + arc: CylinderArc, + bottom_edge: u32, + top_edge: u32, + vertical_edges: [u32; 2], + vertices: [[u32; 2]; 2], + height: f64, +) -> Result, GeometryError> { + let bottom = vertices[0]; + let top = vertices[1]; + let start = arc.start; + let end = arc.end; + let vertical = |point: usize| -> Result { + Ok(boundary( + vertical_edges[point], + bottom[point], + top[point], + F, + uv_line([arc_angle_at(arc, point)?, 0.0], [0.0, 1.0]), + )) + }; + let vertical_reverse = |point: usize| -> Result { + Ok(boundary( + vertical_edges[point], + top[point], + bottom[point], + R, + uv_line([arc_angle_at(arc, point)?, 0.0], [0.0, 1.0]), + )) + }; + let uses = if arc.reversed { + vec![ + boundary( + bottom_edge, + bottom[end], + bottom[start], + R, + uv_line([0.0; 2], [1.0, 0.0]), + ), + vertical(start)?, + boundary( + top_edge, + top[start], + top[end], + F, + uv_line([0.0, height], [1.0, 0.0]), + ), + vertical_reverse(end)?, + ] + } else { + vec![ + boundary( + bottom_edge, + bottom[start], + bottom[end], + F, + uv_line([0.0; 2], [1.0, 0.0]), + ), + vertical(end)?, + boundary( + top_edge, + top[end], + top[start], + R, + uv_line([0.0, height], [1.0, 0.0]), + ), + vertical_reverse(start)?, + ] + }; + Ok(uses) +} + +fn arc_angle_at(arc: CylinderArc, point: usize) -> Result { + if point == arc.start { + Ok(arc.range.lo) + } else if point == arc.end { + Ok(arc.range.hi) + } else { + Err(GeometryError::InvalidTopology( + "cylinder arc endpoint is not part of the intersection".into(), + )) + } +} + +pub(super) fn shifted_cylinder_arc(mut arc: CylinderArc, axis: Point3, offset: f64) -> CylinderArc { + arc.frame.origin = std::array::from_fn(|i| arc.frame.origin[i] + axis[i] * offset); + arc +} + +pub(super) fn two_arc_plane_uses( + builder: &Builder, + frame: Frame3, + first: (CylinderArc, u32), + second: (CylinderArc, u32), + vertices: [u32; 2], +) -> Result, GeometryError> { + for first_sense in [Orientation::Forward, Orientation::Reverse] { + let (first_from, first_to) = arc_endpoints(first.0, first_sense); + let second_sense = if second.0.start == first_to && second.0.end == first_from { + Orientation::Forward + } else if second.0.end == first_to && second.0.start == first_from { + Orientation::Reverse + } else { + continue; + }; + let mut samples = Vec::with_capacity(34); + for (arc, sense) in [(first.0, first_sense), (second.0, second_sense)] { + for sample in 0..=16 { + if !samples.is_empty() && sample == 0 { + continue; + } + let fraction = sample as f64 / 16.0; + let parameter = if sense == Orientation::Forward { + arc.range.lo + arc.range.width() * fraction + } else { + arc.range.hi - arc.range.width() * fraction + }; + samples.push(frame.local(arc.frame.point([ + arc.radius * parameter.cos(), + arc.radius * parameter.sin(), + 0.0, + ]))); + } + } + let signed_area = samples + .iter() + .zip(samples.iter().cycle().skip(1)) + .take(samples.len()) + .map(|(a, b)| a[0] * b[1] - b[0] * a[1]) + .sum::() + * 0.5; + if signed_area > 0.0 { + return Ok(vec![ + plane_boundary( + builder, + frame, + first.1, + vertices[first_from], + vertices[first_to], + first_sense, + )?, + plane_boundary( + builder, + frame, + second.1, + vertices[arc_endpoints(second.0, second_sense).0], + vertices[arc_endpoints(second.0, second_sense).1], + second_sense, + )?, + ]); + } + } + Err(GeometryError::InvalidTopology( + "two-arc planar boundary does not form a positive loop".into(), + )) +} + +fn arc_endpoints(arc: CylinderArc, sense: Orientation) -> (usize, usize) { + if sense == Orientation::Forward { + (arc.start, arc.end) + } else { + (arc.end, arc.start) + } +} + +pub(super) fn two_arc_plane_bounds( + frame: Frame3, + arcs: [CylinderArc; 2], + tolerance: f64, +) -> [[f64; 2]; 2] { + padded_uv_bounds( + arcs.into_iter() + .flat_map(|arc| { + [ + arc.frame.origin, + std::array::from_fn(|i| arc.frame.origin[i] + arc.frame.x[i] * arc.radius), + std::array::from_fn(|i| arc.frame.origin[i] - arc.frame.x[i] * arc.radius), + std::array::from_fn(|i| arc.frame.origin[i] + arc.frame.y[i] * arc.radius), + std::array::from_fn(|i| arc.frame.origin[i] - arc.frame.y[i] * arc.radius), + ] + }) + .map(|point| frame.local(point)), + tolerance, + ) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/coextensive.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/coextensive.rs new file mode 100644 index 0000000..17d1eaf --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/coextensive.rs @@ -0,0 +1,87 @@ +use crate::brep::{Accuracy, BrepEnvelope, FaceRole, FaceSource, GeometryError}; +use crate::operations::modifying::boolean::assembly::{ + append_analytic_input, append_annular_cylinder, cylinder_source, face_provenance, + finish_cylinder_result, +}; +use crate::operations::modifying::boolean::operands::CylinderInput; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use crate::primitives; + +pub(super) fn coextensive_cylinder_radii( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + alignment: f64, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let a_lateral = cylinder_source(a, 0); + let b_lateral = cylinder_source(b, 0); + let a_caps = [cylinder_source(a, 1), cylinder_source(a, 2)]; + let b_caps = if alignment > 0.0 { + [cylinder_source(b, 1), cylinder_source(b, 2)] + } else { + [cylinder_source(b, 2), cylinder_source(b, 1)] + }; + let a_is_larger = a.radius > b.radius; + let mut out = BrepEnvelope::new(id, accuracy)?; + match operation { + BooleanOp::Union | BooleanOp::Intersection => { + let choose_a = if operation == BooleanOp::Union { + a_is_larger + } else { + !a_is_larger + }; + let (radius, lateral) = if choose_a { + (a.radius, a_lateral.clone()) + } else { + (b.radius, b_lateral.clone()) + }; + add_coextensive_cylinder(&mut out, a, radius, lateral, &a_caps, &b_caps, accuracy)?; + } + BooleanOp::Subtraction if a_is_larger => { + append_annular_cylinder( + &mut out, + a.frame, + b.radius, + a.radius, + a.height, + [ + face_provenance(vec![a_lateral], FaceRole::Preserved, false), + face_provenance(vec![b_lateral], FaceRole::Cut, true), + face_provenance(vec![a_caps[0].clone()], FaceRole::Split, false), + face_provenance(vec![a_caps[1].clone()], FaceRole::Split, false), + ], + )?; + } + BooleanOp::Subtraction => {} + } + finish_cylinder_result(out, a, b, operation, true) +} + +fn add_coextensive_cylinder( + out: &mut BrepEnvelope, + a: &CylinderInput<'_>, + radius: f64, + lateral: FaceSource, + a_caps: &[FaceSource; 2], + b_caps: &[FaceSource; 2], + accuracy: Accuracy, +) -> Result<(), GeometryError> { + let part = primitives::cylinder( + format!("{}:radial", out.id), + a.frame, + radius, + a.height, + accuracy, + )?; + append_analytic_input(out, &part)?; + out.topology.faces[0].provenance = face_provenance(vec![lateral], FaceRole::Preserved, false); + for (face, sources) in [ + (1, vec![a_caps[0].clone(), b_caps[0].clone()]), + (2, vec![a_caps[1].clone(), b_caps[1].clone()]), + ] { + out.topology.faces[face].provenance = face_provenance(sources, FaceRole::Split, false); + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/contained.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/contained.rs new file mode 100644 index 0000000..d06dece --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/contained.rs @@ -0,0 +1,44 @@ +use crate::brep::{Accuracy, BrepEnvelope, FaceRole, GeometryError}; +use crate::operations::modifying::boolean::assembly::{ + append_analytic_input, finish_cylinder_result, reverse_face, +}; +use crate::operations::modifying::boolean::operands::CylinderInput; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(super) fn contained_cylinders( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + b_inside_a: bool, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let mut out = BrepEnvelope::new(id, accuracy)?; + let (contained, enclosing) = if b_inside_a { (b, a) } else { (a, b) }; + match operation { + BooleanOp::Union => { + append_analytic_input(&mut out, enclosing.brep)?; + } + BooleanOp::Intersection => { + append_analytic_input(&mut out, contained.brep)?; + } + BooleanOp::Subtraction if b_inside_a => { + append_analytic_input(&mut out, a.brep)?; + let face_offset = out.topology.faces.len() as u32; + append_analytic_input(&mut out, b.brep)?; + let cavity = out + .solids + .pop() + .ok_or_else(|| GeometryError::InvalidTopology("missing cutter solid".into()))? + .outer_shell; + out.solids[0].cavity_shells.push(cavity); + for face in face_offset..out.topology.faces.len() as u32 { + reverse_face(&mut out, face); + out.topology.faces[face as usize].provenance.role = FaceRole::Cut; + out.topology.faces[face as usize].provenance.reversed = true; + } + } + BooleanOp::Subtraction => {} + } + finish_cylinder_result(out, a, b, operation, false) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/embedded.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/embedded.rs new file mode 100644 index 0000000..0168a48 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/embedded.rs @@ -0,0 +1,395 @@ +use super::arcs::{ + add_cylinder_arc_face, selected_cylinder_arc, shifted_cylinder_arc, two_arc_plane_bounds, + two_arc_plane_uses, ArcFaceBoundary, CylinderArc, +}; +use crate::brep::{ + unit, Accuracy, BrepEnvelope, Builder, CurveGeometry, FaceRole, Frame3, GeometryError, + SurfaceGeometry, +}; +use crate::math::{cross, norm, scale, Interval, Point3}; +use crate::operations::modifying::boolean::assembly::{ + cylinder_source, face_provenance, finish_cylinder_result, +}; +use crate::operations::modifying::boolean::operands::CylinderInput; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(super) struct EmbeddedPlacement { + pub(super) alignment: f64, + pub(super) radial: Point3, + pub(super) b_span: [f64; 2], +} + +struct EmbeddedCut { + axis: Point3, + levels: [f64; 4], + a_outer: CylinderArc, + a_inner: CylinderArc, + b_boundary: CylinderArc, +} + +struct EmbeddedLayout { + vertices: [[u32; 2]; 4], + vertical_edges: [[u32; 2]; 3], + a_outer_edges: [u32; 4], + a_inner_edges: [u32; 4], + b_edges: [u32; 2], +} + +pub(super) fn embedded_transverse_cylinder_boolean( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + placement: &EmbeddedPlacement, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let EmbeddedPlacement { + alignment, + radial, + b_span, + } = *placement; + if !matches!(operation, BooleanOp::Union | BooleanOp::Subtraction) { + return Err(GeometryError::InvalidGeometry( + "embedded transverse cylinder builder requires union or subtraction".into(), + )); + } + let axis = a.frame.z; + let distance = norm(radial); + let toward_b = unit(radial)?; + let lateral = unit(cross(axis, toward_b))?; + let along = + (distance * distance + a.radius * a.radius - b.radius * b.radius) / (2.0 * distance); + let half_squared = (a.radius - along) * (a.radius + along); + let clearance = 4.0 * accuracy.geometric; + if half_squared <= clearance * clearance { + return Err(GeometryError::UnresolvedIntersection( + "embedded transverse cylinder chord is below geometric resolution".into(), + )); + } + let half = half_squared.sqrt(); + let chord_center: Point3 = std::array::from_fn(|i| a.frame.origin[i] + toward_b[i] * along); + let points = [ + std::array::from_fn(|i| chord_center[i] + lateral[i] * half), + std::array::from_fn(|i| chord_center[i] - lateral[i] * half), + ]; + let [a_outer, a_inner, b_boundary] = embedded_boundary_arcs(a, b, radial, points, operation)?; + let levels = [0.0, b_span[0], b_span[1], a.height]; + if levels.windows(2).any(|span| span[1] - span[0] <= clearance) { + return Err(GeometryError::UnresolvedIntersection( + "embedded transverse cylinder leaves a sub-tolerance axial segment".into(), + )); + } + let cut = EmbeddedCut { + axis, + levels, + a_outer, + a_inner, + b_boundary, + }; + + let mut builder = Builder::new(id, accuracy)?; + let layout = add_embedded_edges(&mut builder, points, &cut)?; + add_embedded_lateral_faces(&mut builder, &cut, &layout)?; + add_embedded_planar_faces(&mut builder, a, &cut, &layout, operation, accuracy)?; + + let mut out = builder.finish_solid()?; + set_embedded_provenance(&mut out, a, b, alignment, operation); + finish_cylinder_result(out, a, b, operation, false) +} + +fn embedded_boundary_arcs( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + radial: Point3, + points: [Point3; 2], + operation: BooleanOp, +) -> Result<[CylinderArc; 3], GeometryError> { + let mut b_base_frame = a.frame; + b_base_frame.origin = std::array::from_fn(|i| a.frame.origin[i] + radial[i]); + let a_outer = selected_cylinder_arc( + a.frame, + a.radius, + points, + b_base_frame.origin, + b.radius, + false, + false, + )?; + let a_inner = selected_cylinder_arc( + a.frame, + a.radius, + points, + b_base_frame.origin, + b.radius, + true, + false, + )?; + let b_boundary = selected_cylinder_arc( + b_base_frame, + b.radius, + points, + a.frame.origin, + a.radius, + operation == BooleanOp::Subtraction, + operation == BooleanOp::Subtraction, + )?; + Ok([a_outer, a_inner, b_boundary]) +} + +fn add_embedded_edges( + builder: &mut Builder, + points: [Point3; 2], + cut: &EmbeddedCut, +) -> Result { + let vertices: [[u32; 2]; 4] = std::array::from_fn(|level| { + points.map(|point| { + builder.vertex(std::array::from_fn(|i| { + point[i] + cut.axis[i] * cut.levels[level] + })) + }) + }); + let vertical_ranges = [ + Interval::new(0.0, cut.levels[1] - cut.levels[0])?, + Interval::new(0.0, cut.levels[2] - cut.levels[1])?, + Interval::new(0.0, cut.levels[3] - cut.levels[2])?, + ]; + let vertical_edges: [[u32; 2]; 3] = std::array::from_fn(|segment| { + [0, 1].map(|point| { + builder.edge( + CurveGeometry::Line { + origin: std::array::from_fn(|i| { + points[point][i] + cut.axis[i] * cut.levels[segment] + }), + direction: cut.axis, + }, + vertical_ranges[segment], + false, + ) + }) + }); + let mut arc_edge = |arc: CylinderArc, level: usize, seam: bool| { + let shifted = shifted_cylinder_arc(arc, cut.axis, cut.levels[level]); + builder.edge( + CurveGeometry::Circle { + frame: shifted.frame, + radius: shifted.radius, + }, + shifted.range, + seam, + ) + }; + let a_outer_edges = [ + arc_edge(cut.a_outer, 0, false), + arc_edge(cut.a_outer, 1, true), + arc_edge(cut.a_outer, 2, true), + arc_edge(cut.a_outer, 3, false), + ]; + let a_inner_edges = [ + arc_edge(cut.a_inner, 0, false), + arc_edge(cut.a_inner, 1, false), + arc_edge(cut.a_inner, 2, false), + arc_edge(cut.a_inner, 3, false), + ]; + let b_edges = [ + arc_edge(cut.b_boundary, 1, false), + arc_edge(cut.b_boundary, 2, false), + ]; + Ok(EmbeddedLayout { + vertices, + vertical_edges, + a_outer_edges, + a_inner_edges, + b_edges, + }) +} + +fn add_embedded_lateral_faces( + builder: &mut Builder, + cut: &EmbeddedCut, + layout: &EmbeddedLayout, +) -> Result<(), GeometryError> { + for segment in 0..3 { + let height = cut.levels[segment + 1] - cut.levels[segment]; + add_cylinder_arc_face( + builder, + &format!("a:outer:{segment}"), + shifted_cylinder_arc(cut.a_outer, cut.axis, cut.levels[segment]), + &ArcFaceBoundary { + bottom_edge: layout.a_outer_edges[segment], + top_edge: layout.a_outer_edges[segment + 1], + vertical_edges: layout.vertical_edges[segment], + vertices: [layout.vertices[segment], layout.vertices[segment + 1]], + }, + height, + )?; + let (arc, edges, key) = if segment == 1 { + (cut.b_boundary, layout.b_edges, "b:boundary") + } else { + ( + cut.a_inner, + [ + layout.a_inner_edges[segment], + layout.a_inner_edges[segment + 1], + ], + if segment == 0 { + "a:inner:lower" + } else { + "a:inner:upper" + }, + ) + }; + add_cylinder_arc_face( + builder, + key, + shifted_cylinder_arc(arc, cut.axis, cut.levels[segment]), + &ArcFaceBoundary { + bottom_edge: edges[0], + top_edge: edges[1], + vertical_edges: layout.vertical_edges[segment], + vertices: [layout.vertices[segment], layout.vertices[segment + 1]], + }, + height, + )?; + } + Ok(()) +} + +fn add_embedded_planar_faces( + builder: &mut Builder, + a: &CylinderInput<'_>, + cut: &EmbeddedCut, + layout: &EmbeddedLayout, + operation: BooleanOp, + accuracy: Accuracy, +) -> Result<(), GeometryError> { + let mut bottom_frame = a.frame; + bottom_frame.y = scale(bottom_frame.y, -1.0); + bottom_frame.z = scale(bottom_frame.z, -1.0); + let bottom_arcs = [ + shifted_cylinder_arc(cut.a_outer, cut.axis, cut.levels[0]), + shifted_cylinder_arc(cut.a_inner, cut.axis, cut.levels[0]), + ]; + add_two_arc_plane_face( + builder, + "a:lower-cap", + bottom_frame, + bottom_arcs, + [layout.a_outer_edges[0], layout.a_inner_edges[0]], + layout.vertices[0], + accuracy.geometric, + )?; + + for (level, key) in [(1, "b:lower-interface"), (2, "b:upper-interface")] { + let outward_positive = (operation == BooleanOp::Union && level == 2) + || (operation == BooleanOp::Subtraction && level == 1); + let mut frame = a.frame; + frame.origin = a.frame.point([0.0, 0.0, cut.levels[level]]); + if !outward_positive { + frame.y = scale(frame.y, -1.0); + frame.z = scale(frame.z, -1.0); + } + let arcs = [ + shifted_cylinder_arc(cut.a_inner, cut.axis, cut.levels[level]), + shifted_cylinder_arc(cut.b_boundary, cut.axis, cut.levels[level]), + ]; + let edges = if level == 1 { + [layout.a_inner_edges[1], layout.b_edges[0]] + } else { + [layout.a_inner_edges[2], layout.b_edges[1]] + }; + add_two_arc_plane_face( + builder, + key, + frame, + arcs, + edges, + layout.vertices[level], + accuracy.geometric, + )?; + } + + let mut top_frame = a.frame; + top_frame.origin = a.frame.point([0.0, 0.0, a.height]); + let top_arcs = [ + shifted_cylinder_arc(cut.a_outer, cut.axis, cut.levels[3]), + shifted_cylinder_arc(cut.a_inner, cut.axis, cut.levels[3]), + ]; + add_two_arc_plane_face( + builder, + "a:upper-cap", + top_frame, + top_arcs, + [layout.a_outer_edges[3], layout.a_inner_edges[3]], + layout.vertices[3], + accuracy.geometric, + ) +} + +fn add_two_arc_plane_face( + builder: &mut Builder, + key: &str, + frame: Frame3, + arcs: [CylinderArc; 2], + edges: [u32; 2], + vertices: [u32; 2], + tolerance: f64, +) -> Result<(), GeometryError> { + let uses = two_arc_plane_uses( + builder, + frame, + (arcs[0], edges[0]), + (arcs[1], edges[1]), + vertices, + )?; + builder.face( + key, + SurfaceGeometry::Plane { frame }, + two_arc_plane_bounds(frame, arcs, tolerance), + uses, + ) +} + +fn set_embedded_provenance( + out: &mut BrepEnvelope, + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + alignment: f64, + operation: BooleanOp, +) { + let a_lateral = cylinder_source(a, 0); + let b_lateral = cylinder_source(b, 0); + for face in [0, 1, 2, 4, 5] { + out.topology.faces[face].provenance = + face_provenance(vec![a_lateral.clone()], FaceRole::Split, false); + } + out.topology.faces[3].provenance = face_provenance( + vec![b_lateral], + if operation == BooleanOp::Subtraction { + FaceRole::Cut + } else { + FaceRole::Split + }, + operation == BooleanOp::Subtraction, + ); + let a_caps = [cylinder_source(a, 1), cylinder_source(a, 2)]; + out.topology.faces[6].provenance = + face_provenance(vec![a_caps[0].clone()], FaceRole::Preserved, false); + out.topology.faces[9].provenance = + face_provenance(vec![a_caps[1].clone()], FaceRole::Preserved, false); + let b_caps = if alignment > 0.0 { + [cylinder_source(b, 1), cylinder_source(b, 2)] + } else { + [cylinder_source(b, 2), cylinder_source(b, 1)] + }; + for (face, source) in [(7, b_caps[0].clone()), (8, b_caps[1].clone())] { + out.topology.faces[face].provenance = face_provenance( + vec![source], + if operation == BooleanOp::Subtraction { + FaceRole::Cut + } else { + FaceRole::Split + }, + operation == BooleanOp::Subtraction, + ); + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/mod.rs new file mode 100644 index 0000000..51d7662 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/mod.rs @@ -0,0 +1,566 @@ +mod arcs; +mod coextensive; +mod contained; +mod embedded; +mod parallel; +mod transverse; + +use super::disjoint::separate_boolean; +use crate::brep::{Accuracy, BrepEnvelope, FaceProvenance, FaceRole, FaceSource, GeometryError}; +use crate::math::{dot, norm, scale, sub, Point3}; +use crate::operations::modifying::boolean::assembly::{ + append_cylinder_segment, cylinder_source, face_provenance, finish_cylinder_result, +}; +use crate::operations::modifying::boolean::operands::{full_cylinder, CylinderInput}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use coextensive::coextensive_cylinder_radii; +use contained::contained_cylinders; +use embedded::{embedded_transverse_cylinder_boolean, EmbeddedPlacement}; +use parallel::{parallel_cylinder_boolean, sliced_parallel_cylinder_boolean, ParallelSlice}; +use transverse::transverse_cylinder_through_subtraction; + +struct AxialPlacement { + radial: Point3, + radial_distance: f64, + a_span: [f64; 2], + b_span: [f64; 2], + span_error: f64, + roundoff: f64, +} + +struct AxialOverlap { + alignment: f64, + overlap_span: [f64; 2], + clearance: f64, +} + +struct Segment { + lo: f64, + hi: f64, + provenances: [FaceProvenance; 3], +} + +struct CoaxialSources { + a_lateral: FaceSource, + b_lateral: FaceSource, + a_caps: [FaceSource; 2], + b_caps: [FaceSource; 2], +} + +pub(crate) fn boolean_cylinders( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let a = full_cylinder(a)?; + let b = full_cylinder(b)?; + let accuracy = Accuracy::combined(a.brep.accuracy, b.brep.accuracy); + let alignment = dot(a.frame.z, b.frame.z); + if 1.0 - alignment.abs() > 1e-12 { + if operation == BooleanOp::Subtraction { + return transverse_cylinder_through_subtraction(&a, &b, id, accuracy); + } + return Err(GeometryError::CoverageGap { + families: ["cylinder".into(), "nonparallel cylinder".into()], + }); + } + let placement = axial_placement(&a, &b, alignment); + if placement.radial_distance > placement.roundoff { + return offset_axis_boolean(&a, &b, alignment, &placement, operation, id, accuracy); + } + if a.radius != b.radius { + return coaxial_unequal_radii_boolean( + &a, &b, alignment, &placement, operation, id, accuracy, + ); + } + coaxial_equal_radii_boolean(&a, &b, alignment, &placement, operation, id, accuracy) +} + +fn axial_placement(a: &CylinderInput<'_>, b: &CylinderInput<'_>, alignment: f64) -> AxialPlacement { + let delta = sub(b.frame.origin, a.frame.origin); + let axial = dot(delta, a.frame.z); + let radial = sub(delta, scale(a.frame.z, axial)); + let a_span = [0.0, a.height]; + let b_end = axial + alignment.signum() * b.height; + let b_span = [axial.min(b_end), axial.max(b_end)]; + let span_error = (a_span[0] - b_span[0]) + .abs() + .max((a_span[1] - b_span[1]).abs()); + let scale = a + .frame + .origin + .into_iter() + .chain(b.frame.origin) + .chain([a.radius, b.radius, a.height, b.height]) + .fold(1.0_f64, |largest, value| largest.max(value.abs())); + let roundoff = 128.0 * f64::EPSILON * scale; + let radial_distance = norm(radial); + AxialPlacement { + radial, + radial_distance, + a_span, + b_span, + span_error, + roundoff, + } +} + +fn offset_axis_boolean( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + alignment: f64, + placement: &AxialPlacement, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + if placement.radial_distance <= accuracy.intersection { + return Err(GeometryError::UnresolvedIntersection( + "cylinder axes differ within the intersection budget".into(), + )); + } + if placement.span_error <= placement.roundoff { + return parallel_cylinder_boolean(a, b, alignment, operation, id, accuracy); + } + let clearance = 4.0 * accuracy.geometric; + let overlap_span = [ + placement.a_span[0].max(placement.b_span[0]), + placement.a_span[1].min(placement.b_span[1]), + ]; + let overlap = overlap_span[1] - overlap_span[0]; + if overlap.abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "parallel cylinder axial contact is below geometric resolution".into(), + )); + } + if overlap < 0.0 { + return separate_boolean(a.brep, b.brep, operation, id, Vec::new()); + } + let axial_overlap = AxialOverlap { + alignment, + overlap_span, + clearance, + }; + offset_axis_overlap_boolean(a, b, placement, &axial_overlap, operation, id, accuracy) +} + +fn offset_axis_overlap_boolean( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + placement: &AxialPlacement, + axial_overlap: &AxialOverlap, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let AxialPlacement { + radial, + a_span, + b_span, + roundoff, + .. + } = *placement; + let AxialOverlap { + alignment, + overlap_span, + clearance, + } = *axial_overlap; + if operation == BooleanOp::Intersection { + return sliced_parallel_cylinder_boolean( + a, + b, + &ParallelSlice { + alignment, + radial, + a_span, + b_span, + span: overlap_span, + }, + operation, + id, + accuracy, + roundoff, + ); + } + if operation == BooleanOp::Subtraction { + let b_covers_a = b_span[0] <= a_span[0] + roundoff && b_span[1] >= a_span[1] - roundoff; + if b_covers_a { + return sliced_parallel_cylinder_boolean( + a, + b, + &ParallelSlice { + alignment, + radial, + a_span, + b_span, + span: a_span, + }, + operation, + id, + accuracy, + roundoff, + ); + } + let b_nearly_covers_a = + b_span[0] <= a_span[0] + clearance && b_span[1] >= a_span[1] - clearance; + if b_nearly_covers_a { + return Err(GeometryError::UnresolvedIntersection( + "parallel cylinder subtraction leaves a sub-tolerance axial segment".into(), + )); + } + } + embedded_offset_axis_boolean(a, b, placement, axial_overlap, operation, id, accuracy) +} + +fn embedded_offset_axis_boolean( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + placement: &AxialPlacement, + axial_overlap: &AxialOverlap, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let AxialPlacement { + radial, + radial_distance, + a_span, + b_span, + .. + } = *placement; + let AxialOverlap { + alignment, + clearance, + .. + } = *axial_overlap; + let b_embedded_in_a = b_span[0] > a_span[0] + clearance && b_span[1] < a_span[1] - clearance; + let transverse_overlap = radial_distance > (a.radius - b.radius).abs() + clearance + && radial_distance < a.radius + b.radius - clearance; + if b_embedded_in_a + && transverse_overlap + && matches!(operation, BooleanOp::Union | BooleanOp::Subtraction) + { + return embedded_transverse_cylinder_boolean( + a, + b, + &EmbeddedPlacement { + alignment, + radial, + b_span, + }, + operation, + id, + accuracy, + ); + } + let a_embedded_in_b = a_span[0] > b_span[0] + clearance && a_span[1] < b_span[1] - clearance; + if a_embedded_in_b && transverse_overlap && operation == BooleanOp::Union { + return boolean_cylinders(b.brep, a.brep, operation, id); + } + Err(GeometryError::CoverageGap { + families: [ + "parallel cylinder".into(), + "non-coextensive parallel cylinder".into(), + ], + }) +} + +fn coaxial_unequal_radii_boolean( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + alignment: f64, + placement: &AxialPlacement, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + if (a.radius - b.radius).abs() <= accuracy.intersection { + return Err(GeometryError::UnresolvedIntersection( + "cylinder radii differ within the intersection budget".into(), + )); + } + if placement.span_error <= placement.roundoff { + return coextensive_cylinder_radii(a, b, alignment, operation, id, accuracy); + } + let clearance = 4.0 * accuracy.geometric; + let b_inside_a = b.radius + clearance < a.radius + && placement.b_span[0] > placement.a_span[0] + clearance + && placement.b_span[1] < placement.a_span[1] - clearance; + let a_inside_b = a.radius + clearance < b.radius + && placement.a_span[0] > placement.b_span[0] + clearance + && placement.a_span[1] < placement.b_span[1] - clearance; + if b_inside_a || a_inside_b { + return contained_cylinders(a, b, b_inside_a, operation, id, accuracy); + } + let near_radial_contact = (a.radius - b.radius).abs() <= clearance; + let near_axial_contact = [ + (placement.a_span[0] - placement.b_span[0]).abs(), + (placement.a_span[0] - placement.b_span[1]).abs(), + (placement.a_span[1] - placement.b_span[0]).abs(), + (placement.a_span[1] - placement.b_span[1]).abs(), + ] + .into_iter() + .any(|gap| gap <= clearance); + if near_radial_contact || near_axial_contact { + return Err(GeometryError::UnresolvedIntersection( + "cylinder containment boundary is below geometric resolution".into(), + )); + } + if placement.span_error <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "cylinder axial spans differ below geometric resolution".into(), + )); + } + Err(GeometryError::CoverageGap { + families: [ + "cylinder".into(), + "different-radius coaxial cylinder with unequal axial spans".into(), + ], + }) +} + +fn coaxial_equal_radii_boolean( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + alignment: f64, + placement: &AxialPlacement, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let overlap = + placement.a_span[1].min(placement.b_span[1]) - placement.a_span[0].max(placement.b_span[0]); + if overlap != 0.0 && overlap.abs() <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "cylinder axial overlap or clearance is below geometric resolution".into(), + )); + } + + let a_lateral = cylinder_source(a, 0); + let b_lateral = cylinder_source(b, 0); + let a_caps = [cylinder_source(a, 1), cylinder_source(a, 2)]; + let b_caps = if alignment > 0.0 { + [cylinder_source(b, 1), cylinder_source(b, 2)] + } else { + [cylinder_source(b, 2), cylinder_source(b, 1)] + }; + let face_sources = CoaxialSources { + a_lateral, + b_lateral, + a_caps, + b_caps, + }; + let cap_at = |boundary: f64, low: bool, include_a: bool, include_b: bool| { + let mut sources = Vec::new(); + if include_a + && boundary + == if low { + placement.a_span[0] + } else { + placement.a_span[1] + } + { + sources.push(face_sources.a_caps[usize::from(!low)].clone()); + } + if include_b + && boundary + == if low { + placement.b_span[0] + } else { + placement.b_span[1] + } + { + sources.push(face_sources.b_caps[usize::from(!low)].clone()); + } + sources + }; + + let mut segments = Vec::new(); + let coincident = overlap >= 0.0; + add_coaxial_segments( + &mut segments, + operation, + overlap, + placement.a_span, + placement.b_span, + &face_sources, + cap_at, + ); + coaxial_segments_boolean(a, b, segments, coincident, operation, id, accuracy) +} + +fn add_coaxial_segments( + segments: &mut Vec, + operation: BooleanOp, + overlap: f64, + a_span: [f64; 2], + b_span: [f64; 2], + sources: &CoaxialSources, + cap_at: impl Fn(f64, bool, bool, bool) -> Vec, +) { + match operation { + BooleanOp::Union if overlap >= 0.0 => { + let lo = a_span[0].min(b_span[0]); + let hi = a_span[1].max(b_span[1]); + let lower = cap_at(lo, true, true, true); + let upper = cap_at(hi, false, true, true); + segments.push(coincident_segment( + lo, + hi, + lower, + upper, + &sources.a_lateral, + &sources.b_lateral, + )); + } + BooleanOp::Union => { + for (span, lateral, caps) in [ + (a_span, sources.a_lateral.clone(), sources.a_caps.clone()), + (b_span, sources.b_lateral.clone(), sources.b_caps.clone()), + ] { + segments.push(Segment { + lo: span[0], + hi: span[1], + provenances: [ + face_provenance(vec![lateral], FaceRole::Preserved, false), + face_provenance(vec![caps[0].clone()], FaceRole::Preserved, false), + face_provenance(vec![caps[1].clone()], FaceRole::Preserved, false), + ], + }); + } + } + BooleanOp::Intersection if overlap > 0.0 => { + let lo = a_span[0].max(b_span[0]); + let hi = a_span[1].min(b_span[1]); + let lower = cap_at(lo, true, lo == a_span[0], lo == b_span[0]); + let upper = cap_at(hi, false, hi == a_span[1], hi == b_span[1]); + segments.push(coincident_segment( + lo, + hi, + lower, + upper, + &sources.a_lateral, + &sources.b_lateral, + )); + } + BooleanOp::Intersection => {} + BooleanOp::Subtraction if overlap <= 0.0 => { + segments.push(Segment { + lo: a_span[0], + hi: a_span[1], + provenances: [ + face_provenance(vec![sources.a_lateral.clone()], FaceRole::Preserved, false), + face_provenance(vec![sources.a_caps[0].clone()], FaceRole::Preserved, false), + face_provenance(vec![sources.a_caps[1].clone()], FaceRole::Preserved, false), + ], + }); + } + BooleanOp::Subtraction => { + add_subtraction_segments( + segments, + a_span, + b_span, + &sources.a_lateral, + &sources.a_caps, + &sources.b_caps, + ); + } + } +} + +fn coincident_segment( + lo: f64, + hi: f64, + lower: Vec, + upper: Vec, + a_lateral: &FaceSource, + b_lateral: &FaceSource, +) -> Segment { + Segment { + lo, + hi, + provenances: [ + face_provenance( + vec![a_lateral.clone(), b_lateral.clone()], + FaceRole::Coincident, + false, + ), + face_provenance( + lower.clone(), + if lower.len() > 1 { + FaceRole::Coincident + } else { + FaceRole::Preserved + }, + false, + ), + face_provenance( + upper.clone(), + if upper.len() > 1 { + FaceRole::Coincident + } else { + FaceRole::Preserved + }, + false, + ), + ], + } +} + +fn add_subtraction_segments( + segments: &mut Vec, + a_span: [f64; 2], + b_span: [f64; 2], + a_lateral: &FaceSource, + a_caps: &[FaceSource; 2], + b_caps: &[FaceSource; 2], +) { + if b_span[0] > a_span[0] { + segments.push(Segment { + lo: a_span[0], + hi: b_span[0].min(a_span[1]), + provenances: [ + face_provenance(vec![a_lateral.clone()], FaceRole::Split, false), + face_provenance(vec![a_caps[0].clone()], FaceRole::Preserved, false), + face_provenance(vec![b_caps[0].clone()], FaceRole::Cut, true), + ], + }); + } + if b_span[1] < a_span[1] { + segments.push(Segment { + lo: b_span[1].max(a_span[0]), + hi: a_span[1], + provenances: [ + face_provenance(vec![a_lateral.clone()], FaceRole::Split, false), + face_provenance(vec![b_caps[1].clone()], FaceRole::Cut, true), + face_provenance(vec![a_caps[1].clone()], FaceRole::Preserved, false), + ], + }); + } +} + +fn coaxial_segments_boolean( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + segments: Vec, + coincident: bool, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let mut out = BrepEnvelope::new(id, accuracy)?; + for (index, segment) in segments.into_iter().enumerate() { + let mut frame = a.frame; + frame.origin = a.frame.point([0.0, 0.0, segment.lo]); + append_cylinder_segment( + &mut out, + frame, + a.radius, + segment.hi - segment.lo, + segment.provenances, + index, + )?; + } + finish_cylinder_result(out, a, b, operation, coincident) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/parallel.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/parallel.rs new file mode 100644 index 0000000..085a1db --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/parallel.rs @@ -0,0 +1,478 @@ +use super::arcs::{add_cylinder_arc_face, selected_cylinder_arc, ArcFaceBoundary, CylinderArc}; +use super::contained::contained_cylinders; +use crate::brep::{ + padded_uv_bounds, plane_boundary, unit, Accuracy, BrepEnvelope, Builder, CurveGeometry, + FaceRole, Frame3, GeometryError, Orientation, SurfaceGeometry, Use, +}; +use crate::math::{cross, dot, norm, scale, sub, Interval, Point3}; +use crate::operations::modifying::boolean::assembly::{ + cylinder_face_mappings, cylinder_source, face_provenance, finish_cylinder_result, +}; +use crate::operations::modifying::boolean::handlers::disjoint::separate_boolean; +use crate::operations::modifying::boolean::operands::CylinderInput; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use crate::primitives; + +pub(super) struct ParallelSlice { + pub(super) alignment: f64, + pub(super) radial: Point3, + pub(super) a_span: [f64; 2], + pub(super) b_span: [f64; 2], + pub(super) span: [f64; 2], +} + +struct BaseCrossing { + b_frame: Frame3, + points: [Point3; 2], +} + +struct LateralTopology { + vertices: [[u32; 2]; 2], + a_edges: [u32; 2], + b_edges: [u32; 2], +} + +pub(super) fn sliced_parallel_cylinder_boolean( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + slice: &ParallelSlice, + operation: BooleanOp, + id: String, + accuracy: Accuracy, + roundoff: f64, +) -> Result { + let ParallelSlice { + alignment, + radial, + a_span, + b_span, + span, + } = *slice; + let mut a_frame = a.frame; + a_frame.origin = a.frame.point([0.0, 0.0, span[0]]); + let mut b_frame = a_frame; + b_frame.origin = std::array::from_fn(|axis| a_frame.origin[axis] + radial[axis]); + let height = span[1] - span[0]; + let a_slice = CylinderInput { + brep: a.brep, + frame: a_frame, + radius: a.radius, + height, + }; + let b_slice = CylinderInput { + brep: b.brep, + frame: b_frame, + radius: b.radius, + height, + }; + let mut result = parallel_cylinder_boolean(&a_slice, &b_slice, 1.0, operation, id, accuracy)?; + result.report.coincident = false; + if operation == BooleanOp::Intersection && result.brep.topology.faces.len() == 4 { + let a_caps = [cylinder_source(a, 1), cylinder_source(a, 2)]; + let b_caps = if alignment > 0.0 { + [cylinder_source(b, 1), cylinder_source(b, 2)] + } else { + [cylinder_source(b, 2), cylinder_source(b, 1)] + }; + for (face, boundary, end) in [(2, span[0], 0), (3, span[1], 1)] { + let mut sources = Vec::with_capacity(2); + if (boundary - a_span[end]).abs() <= roundoff { + sources.push(a_caps[end].clone()); + } + if (boundary - b_span[end]).abs() <= roundoff { + sources.push(b_caps[end].clone()); + } + if sources.len() > 1 { + result.report.coincident = true; + } + if sources.is_empty() { + return Err(GeometryError::InvalidTopology( + "parallel cylinder slice cap has no source face".into(), + )); + } + result.brep.topology.faces[face].provenance = + face_provenance(sources, FaceRole::Split, false); + } + result.report.face_mappings = cylinder_face_mappings(&result.brep, [a, b]); + result.brep.validate()?; + } else if operation == BooleanOp::Subtraction { + result.report.coincident = + (a_span[0] - b_span[0]).abs() <= roundoff || (a_span[1] - b_span[1]).abs() <= roundoff; + } + Ok(result) +} + +pub(super) fn parallel_cylinder_boolean( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + alignment: f64, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let mut b_frame = a.frame; + b_frame.origin = if alignment > 0.0 { + b.frame.origin + } else { + b.frame.point([0.0, 0.0, b.height]) + }; + let delta = sub(b_frame.origin, a.frame.origin); + let axial = dot(delta, a.frame.z); + let radial = sub(delta, scale(a.frame.z, axial)); + let distance = norm(radial); + let sum = a.radius + b.radius; + let difference = (a.radius - b.radius).abs(); + let clearance = 4.0 * accuracy.geometric; + let relation_roundoff = 128.0 + * f64::EPSILON + * distance.max(sum).max(a.height).max( + a.frame + .origin + .into_iter() + .chain(b_frame.origin) + .fold(1.0_f64, |largest, value| largest.max(value.abs())), + ); + if (distance - sum).abs() <= clearance { + if (distance - sum).abs() <= relation_roundoff { + let toward_b = unit(radial)?; + let bottom: Point3 = + std::array::from_fn(|axis| a.frame.origin[axis] + toward_b[axis] * a.radius); + let top: Point3 = std::array::from_fn(|axis| bottom[axis] + a.frame.z[axis] * a.height); + return separate_boolean(a.brep, b.brep, operation, id, vec![bottom, top]); + } + return Err(GeometryError::UnresolvedIntersection( + "parallel cylinders are nearly externally tangent".into(), + )); + } + if (distance - difference).abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "parallel cylinders are internally tangent or nearly tangent".into(), + )); + } + if distance <= difference { + return nested_parallel_cylinder_boolean(a, b, b_frame, operation, id, accuracy); + } + if distance >= sum { + return separate_boolean(a.brep, b.brep, operation, id, Vec::new()); + } + + let points = parallel_chord_points(a, b, radial, distance, clearance)?; + let crossing = BaseCrossing { b_frame, points }; + overlapping_parallel_cylinder_boolean(a, b, &crossing, alignment, operation, id, accuracy) +} + +fn nested_parallel_cylinder_boolean( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + b_frame: Frame3, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let b_inside_a = b.radius < a.radius; + if operation != BooleanOp::Subtraction || !b_inside_a { + return contained_cylinders(a, b, b_inside_a, operation, id, accuracy); + } + let mut out = primitives::cylinder_with_circular_hole( + id, a.frame, b_frame, b.radius, a.radius, a.height, accuracy, + )?; + out.topology.faces[0].provenance = + face_provenance(vec![cylinder_source(a, 0)], FaceRole::Preserved, false); + out.topology.faces[1].provenance = + face_provenance(vec![cylinder_source(b, 0)], FaceRole::Cut, true); + for (face, source) in [(2, cylinder_source(a, 1)), (3, cylinder_source(a, 2))] { + out.topology.faces[face].provenance = face_provenance(vec![source], FaceRole::Split, false); + } + finish_cylinder_result(out, a, b, operation, true) +} + +fn parallel_chord_points( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + radial: Point3, + distance: f64, + clearance: f64, +) -> Result<[Point3; 2], GeometryError> { + let toward_b = unit(radial)?; + let lateral = unit(cross(a.frame.z, toward_b))?; + let along = + (distance * distance + a.radius * a.radius - b.radius * b.radius) / (2.0 * distance); + let half_squared = (a.radius - along) * (a.radius + along); + if half_squared <= clearance * clearance { + return Err(GeometryError::UnresolvedIntersection( + "parallel cylinder intersection chord is below geometric resolution".into(), + )); + } + let half = half_squared.sqrt(); + let chord_center: Point3 = + std::array::from_fn(|axis| a.frame.origin[axis] + toward_b[axis] * along); + let points = [ + std::array::from_fn(|axis| chord_center[axis] + lateral[axis] * half), + std::array::from_fn(|axis| chord_center[axis] - lateral[axis] * half), + ]; + Ok(points) +} + +fn overlapping_parallel_cylinder_boolean( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + crossing: &BaseCrossing, + alignment: f64, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let (a_inside, b_inside, reverse_b) = match operation { + BooleanOp::Union => (false, false, false), + BooleanOp::Intersection => (true, true, false), + BooleanOp::Subtraction => (false, true, true), + }; + let a_arc = selected_cylinder_arc( + a.frame, + a.radius, + crossing.points, + crossing.b_frame.origin, + b.radius, + a_inside, + false, + )?; + let b_arc = selected_cylinder_arc( + crossing.b_frame, + b.radius, + crossing.points, + a.frame.origin, + a.radius, + b_inside, + reverse_b, + )?; + + let mut builder = Builder::new(id, accuracy)?; + let LateralTopology { + vertices, + a_edges, + b_edges, + } = add_parallel_lateral_faces(&mut builder, a, crossing.points, a_arc, b_arc)?; + add_parallel_caps( + &mut builder, + a, + b, + crossing, + [(a_arc, a_edges), (b_arc, b_edges)], + vertices, + accuracy, + )?; + let mut out = builder.finish_solid()?; + set_parallel_provenance(&mut out, a, b, alignment, operation, reverse_b); + finish_cylinder_result(out, a, b, operation, true) +} + +fn add_parallel_lateral_faces( + builder: &mut Builder, + a: &CylinderInput<'_>, + points: [Point3; 2], + a_arc: CylinderArc, + b_arc: CylinderArc, +) -> Result { + let bottom_vertices = [builder.vertex(points[0]), builder.vertex(points[1])]; + let top_points = + points.map(|point| std::array::from_fn(|axis| point[axis] + a.frame.z[axis] * a.height)); + let top_vertices = [builder.vertex(top_points[0]), builder.vertex(top_points[1])]; + let vertices = [bottom_vertices, top_vertices]; + let vertical_range = Interval::new(0.0, a.height)?; + let vertical_edges = [0, 1].map(|point| { + builder.edge( + CurveGeometry::Line { + origin: points[point], + direction: a.frame.z, + }, + vertical_range, + false, + ) + }); + let add_arc_edges = + |builder: &mut Builder, arc: CylinderArc| -> Result<[u32; 2], GeometryError> { + let mut top_frame = arc.frame; + top_frame.origin = + std::array::from_fn(|axis| arc.frame.origin[axis] + a.frame.z[axis] * a.height); + Ok([ + builder.edge( + CurveGeometry::Circle { + frame: arc.frame, + radius: arc.radius, + }, + arc.range, + false, + ), + builder.edge( + CurveGeometry::Circle { + frame: top_frame, + radius: arc.radius, + }, + arc.range, + false, + ), + ]) + }; + let a_edges = add_arc_edges(builder, a_arc)?; + let b_edges = add_arc_edges(builder, b_arc)?; + add_cylinder_arc_face( + builder, + "a:lateral", + a_arc, + &ArcFaceBoundary { + bottom_edge: a_edges[0], + top_edge: a_edges[1], + vertical_edges, + vertices, + }, + a.height, + )?; + add_cylinder_arc_face( + builder, + "b:lateral", + b_arc, + &ArcFaceBoundary { + bottom_edge: b_edges[0], + top_edge: b_edges[1], + vertical_edges, + vertices, + }, + a.height, + )?; + Ok(LateralTopology { + vertices, + a_edges, + b_edges, + }) +} + +fn add_parallel_caps( + builder: &mut Builder, + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + crossing: &BaseCrossing, + arc_edges: [(CylinderArc, [u32; 2]); 2], + vertices: [[u32; 2]; 2], + accuracy: Accuracy, +) -> Result<(), GeometryError> { + let mut lower_frame = a.frame; + lower_frame.y = scale(lower_frame.y, -1.0); + lower_frame.z = scale(lower_frame.z, -1.0); + let mut upper_frame = a.frame; + upper_frame.origin = a.frame.point([0.0, 0.0, a.height]); + let planar_bounds = parallel_cap_bounds( + a, + b, + crossing.b_frame, + crossing.points, + upper_frame, + accuracy, + ); + let [bottom_vertices, top_vertices] = vertices; + let cap_uses = + |builder: &Builder, frame: Frame3, top: bool| -> Result, GeometryError> { + let vertex_set = if top { top_vertices } else { bottom_vertices }; + let edge_index = usize::from(top); + let mut uses = Vec::with_capacity(2); + for (arc, edges) in arc_edges { + let sense = match (top, arc.reversed) { + (false, false) => Orientation::Reverse, + (false, true) => Orientation::Forward, + (true, false) => Orientation::Forward, + (true, true) => Orientation::Reverse, + }; + let (from, to) = if sense == Orientation::Forward { + (vertex_set[arc.start], vertex_set[arc.end]) + } else { + (vertex_set[arc.end], vertex_set[arc.start]) + }; + uses.push(plane_boundary( + builder, + frame, + edges[edge_index], + from, + to, + sense, + )?); + } + Ok(uses) + }; + let lower_uses = cap_uses(builder, lower_frame, false)?; + builder.face( + "lower_cap", + SurfaceGeometry::Plane { frame: lower_frame }, + planar_bounds, + lower_uses, + )?; + let upper_uses = cap_uses(builder, upper_frame, true)?; + builder.face( + "upper_cap", + SurfaceGeometry::Plane { frame: upper_frame }, + planar_bounds, + upper_uses, + ) +} + +fn parallel_cap_bounds( + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + b_frame: Frame3, + points: [Point3; 2], + upper_frame: Frame3, + accuracy: Accuracy, +) -> [[f64; 2]; 2] { + padded_uv_bounds( + [ + a.frame.origin, + b_frame.origin, + points[0], + points[1], + std::array::from_fn(|axis| a.frame.origin[axis] + a.frame.x[axis] * a.radius), + std::array::from_fn(|axis| a.frame.origin[axis] - a.frame.x[axis] * a.radius), + std::array::from_fn(|axis| a.frame.origin[axis] + a.frame.y[axis] * a.radius), + std::array::from_fn(|axis| a.frame.origin[axis] - a.frame.y[axis] * a.radius), + std::array::from_fn(|axis| b_frame.origin[axis] + a.frame.x[axis] * b.radius), + std::array::from_fn(|axis| b_frame.origin[axis] - a.frame.x[axis] * b.radius), + std::array::from_fn(|axis| b_frame.origin[axis] + a.frame.y[axis] * b.radius), + std::array::from_fn(|axis| b_frame.origin[axis] - a.frame.y[axis] * b.radius), + ] + .into_iter() + .map(|point| upper_frame.local(point)), + accuracy.geometric, + ) +} + +fn set_parallel_provenance( + out: &mut BrepEnvelope, + a: &CylinderInput<'_>, + b: &CylinderInput<'_>, + alignment: f64, + operation: BooleanOp, + reverse_b: bool, +) { + out.topology.faces[0].provenance = + face_provenance(vec![cylinder_source(a, 0)], FaceRole::Split, false); + out.topology.faces[1].provenance = face_provenance( + vec![cylinder_source(b, 0)], + if reverse_b { + FaceRole::Cut + } else { + FaceRole::Split + }, + reverse_b, + ); + let a_caps = [cylinder_source(a, 1), cylinder_source(a, 2)]; + let b_caps = if alignment > 0.0 { + [cylinder_source(b, 1), cylinder_source(b, 2)] + } else { + [cylinder_source(b, 2), cylinder_source(b, 1)] + }; + for (face, cap) in [(2, 0), (3, 1)] { + let sources = if operation == BooleanOp::Subtraction { + vec![a_caps[cap].clone()] + } else { + vec![a_caps[cap].clone(), b_caps[cap].clone()] + }; + out.topology.faces[face].provenance = face_provenance(sources, FaceRole::Split, false); + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/boundaries.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/boundaries.rs new file mode 100644 index 0000000..18643fd --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/boundaries.rs @@ -0,0 +1,96 @@ +use super::charts::rotate_first_intersection_chart; +use crate::brep::{Accuracy, Builder, Curve, CurveGeometry, GeometryError}; +use crate::intersection::IntersectionGraph; +use crate::math::{norm, sub, Interval}; + +pub(super) struct NumericalCylinderBoundary { + pub(super) definition: u32, + pub(super) edge: u32, + pub(super) vertex: u32, + pub(super) cutter_start: [f64; 2], + pub(super) cutter_end: [f64; 2], + pub(super) cutter_middle: [f64; 2], +} + +pub(super) fn add_numerical_boundaries( + builder: &mut Builder, + graph: &IntersectionGraph, + seam_rotation: f64, + accuracy: Accuracy, +) -> Result, GeometryError> { + let mut numerical = Vec::with_capacity(2); + for branch in &graph.branches { + let CurveGeometry::Intersection { definition } = + graph.geometry.curves[branch.curve as usize] + else { + return Err(GeometryError::InvalidGeometry( + "universal face graph did not retain its intersection definition".into(), + )); + }; + let mut definition = graph.geometry.intersections[definition as usize].clone(); + definition.surfaces = [0, 3]; + rotate_first_intersection_chart(&mut definition, seam_rotation, accuracy)?; + let definition_id = builder.brep.geometry.intersections.len() as u32; + builder.brep.geometry.intersections.push(definition); + let curve = CurveGeometry::Intersection { + definition: definition_id, + }; + let start = graph + .geometry + .curve(branch.curve)? + .point_at(branch.range.lo)?; + let end = graph + .geometry + .curve(branch.curve)? + .point_at(branch.range.hi)?; + if norm(sub(start, end)) > accuracy.intersection { + return Err(GeometryError::CoverageGap { + families: [ + "cylinder through subtraction".into(), + "open lateral intersection branch".into(), + ], + }); + } + let vertex = builder.vertex(start); + let edge = builder.edge(curve, branch.range, false); + numerical.push(NumericalCylinderBoundary { + definition: definition_id, + edge, + vertex, + cutter_start: graph + .geometry + .pcurve_at(branch.pcurves[1], branch.range.lo)?, + cutter_end: graph + .geometry + .pcurve_at(branch.pcurves[1], branch.range.hi)?, + cutter_middle: graph + .geometry + .pcurve_at(branch.pcurves[1], branch.range.midpoint())?, + }); + } + Ok(numerical) +} + +pub(super) fn align_cutter_boundaries( + builder: &mut Builder, + numerical: &mut [NumericalCylinderBoundary], +) -> Result<(), GeometryError> { + let tau = std::f64::consts::TAU; + numerical.sort_by(|left, right| left.cutter_middle[1].total_cmp(&right.cutter_middle[1])); + let upper_shift = + ((numerical[0].cutter_end[0] - numerical[1].cutter_start[0]) / tau).round() * tau; + { + let upper = &mut numerical[1]; + upper.cutter_start[0] += upper_shift; + upper.cutter_end[0] += upper_shift; + upper.cutter_middle[0] += upper_shift; + let definition = &mut builder.brep.geometry.intersections[upper.definition as usize]; + for anchor in &mut definition.anchors { + anchor.uv_b[0] += upper_shift; + } + for tube in &mut definition.uv_tubes { + tube[2] = Interval::new(tube[2].lo + upper_shift, tube[2].hi + upper_shift)?; + } + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/charts.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/charts.rs new file mode 100644 index 0000000..5c51981 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/charts.rs @@ -0,0 +1,99 @@ +use crate::brep::{Accuracy, CurveGeometry, Frame3, GeometryError, IntersectionDefinition}; +use crate::intersection::IntersectionGraph; +use crate::math::{add, scale, Interval}; + +pub(super) fn chart_seam_rotation(graph: &IntersectionGraph) -> f64 { + let tau = std::f64::consts::TAU; + let mut parameters = graph + .branches + .iter() + .filter_map( + |branch| match graph.geometry.curves[branch.curve as usize] { + CurveGeometry::Intersection { definition } => Some(definition), + _ => None, + }, + ) + .flat_map(|definition| { + graph.geometry.intersections[definition as usize] + .anchors + .iter() + .map(|anchor| anchor.uv_a[0].rem_euclid(tau)) + }) + .collect::>(); + parameters.sort_by(f64::total_cmp); + parameters.dedup_by(|left, right| (*left - *right).abs() <= f64::EPSILON * 64.0); + if parameters.is_empty() { + return 0.0; + } + let mut largest = (0.0, parameters[0]); + for index in 0..parameters.len() { + let start = parameters[index]; + let end = if index + 1 < parameters.len() { + parameters[index + 1] + } else { + parameters[0] + tau + }; + if end - start > largest.0 { + largest = (end - start, start); + } + } + (largest.1 + 0.5 * largest.0).rem_euclid(tau) +} + +pub(super) fn rotate_cylinder_chart(frame: Frame3, angle: f64) -> Frame3 { + let (sine, cosine) = angle.sin_cos(); + Frame3 { + x: add(scale(frame.x, cosine), scale(frame.y, sine)), + y: add(scale(frame.y, cosine), scale(frame.x, -sine)), + ..frame + } +} + +pub(super) fn rotate_first_intersection_chart( + definition: &mut IntersectionDefinition, + angle: f64, + accuracy: Accuracy, +) -> Result<(), GeometryError> { + let tau = std::f64::consts::TAU; + let original_first = definition + .anchors + .first() + .ok_or_else(|| GeometryError::InvalidGeometry("empty intersection branch".into()))? + .uv_a[0]; + let mut previous: Option = None; + for anchor in &mut definition.anchors { + let mut value = (anchor.uv_a[0] - angle).rem_euclid(tau); + if let Some(previous) = previous { + value += ((previous - value) / tau).round() * tau; + } + anchor.uv_a[0] = value; + previous = Some(value); + } + let lo = definition + .anchors + .iter() + .map(|anchor| anchor.uv_a[0]) + .fold(f64::INFINITY, f64::min); + let hi = definition + .anchors + .iter() + .map(|anchor| anchor.uv_a[0]) + .fold(f64::NEG_INFINITY, f64::max); + if hi - lo >= tau - accuracy.intersection { + return Err(GeometryError::CoverageGap { + families: [ + "cylinder through subtraction".into(), + "intersection loop crossing every chart seam".into(), + ], + }); + } + let shift = -(lo / tau).floor() * tau; + for anchor in &mut definition.anchors { + anchor.uv_a[0] += shift; + } + let chart_shift = definition.anchors[0].uv_a[0] - original_first; + for tube in &mut definition.uv_tubes { + tube[0] = Interval::new(tube[0].lo + chart_shift, tube[0].hi + chart_shift)?; + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/cutter.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/cutter.rs new file mode 100644 index 0000000..1d340b6 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/cutter.rs @@ -0,0 +1,236 @@ +use super::boundaries::NumericalCylinderBoundary; +use crate::brep::{ + boundary, unit, uv_line, Accuracy, Builder, CurveGeometry, GeometryError, IntersectionSide, + Orientation, PcurveGeometry, Surface, Use, +}; +use crate::math::{norm, sub, Interval}; +use crate::operations::modifying::boolean::assembly::reverse; +use crate::operations::modifying::boolean::operands::CylinderInput; + +pub(super) struct CutterSeam { + arc: u32, + middle: u32, + generator: u32, + length: f64, + first_arc_sense: Orientation, + second_arc_sense: Orientation, + arc_shift: f64, +} + +struct SeamGenerator { + middle: u32, + length: f64, + edge: u32, +} + +pub(super) fn add_cutter_seam( + builder: &mut Builder, + cutter: &CylinderInput<'_>, + lower: &NumericalCylinderBoundary, + upper: &NumericalCylinderBoundary, + accuracy: Accuracy, +) -> Result { + let tau = std::f64::consts::TAU; + let lower_end = lower.cutter_end; + let upper_start = upper.cutter_start; + let upper_right_u = upper_start[0]; + let lower_v = lower_end[1]; + let arc_range = Interval::new( + lower_end[0].min(upper_right_u), + lower_end[0].max(upper_right_u), + )?; + if arc_range.width() * cutter.radius <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "cylinder cut chart seam arc is below geometric resolution".into(), + )); + } + let mut seam_circle_frame = cutter.frame; + seam_circle_frame.origin = cutter.frame.point([0.0, 0.0, lower_v]); + let seam_arc = builder.edge( + CurveGeometry::Circle { + frame: seam_circle_frame, + radius: cutter.radius, + }, + arc_range, + true, + ); + let SeamGenerator { + middle: seam_middle, + length: seam_length, + edge: seam_generator, + } = add_seam_generator(builder, cutter, upper, upper_right_u, lower_v, accuracy)?; + let arc_forward = lower_end[0] < upper_right_u; + let first_arc_sense = if arc_forward { + Orientation::Forward + } else { + Orientation::Reverse + }; + let second_arc_sense = reverse(first_arc_sense); + let second_arc_start = if second_arc_sense == Orientation::Forward { + arc_range.lo + } else { + arc_range.hi + }; + let second_arc_end = if second_arc_sense == Orientation::Forward { + arc_range.hi + } else { + arc_range.lo + }; + let arc_shift = lower.cutter_start[0] - second_arc_end; + let winding_error = upper.cutter_end[0] - (second_arc_start + arc_shift); + let winding_residual = winding_error - (winding_error / tau).round() * tau; + if winding_residual.abs() > accuracy.intersection / cutter.radius { + return Err(GeometryError::UnresolvedIntersection( + "cylinder cut chart seam winding is inconsistent".into(), + )); + } + Ok(CutterSeam { + arc: seam_arc, + middle: seam_middle, + generator: seam_generator, + length: seam_length, + first_arc_sense, + second_arc_sense, + arc_shift, + }) +} + +fn add_seam_generator( + builder: &mut Builder, + cutter: &CylinderInput<'_>, + upper: &NumericalCylinderBoundary, + upper_right_u: f64, + lower_v: f64, + accuracy: Accuracy, +) -> Result { + let seam_middle_point = cutter + .brep + .geometry + .surface(0)? + .point_at([upper_right_u, lower_v])?; + let seam_middle = builder.vertex(seam_middle_point); + let seam_vector = sub( + builder.brep.topology.vertices[upper.vertex as usize].position, + seam_middle_point, + ); + let seam_length = norm(seam_vector); + if seam_length <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "cylinder cut chart seam generator is below geometric resolution".into(), + )); + } + let seam_generator = builder.edge( + CurveGeometry::Line { + origin: seam_middle_point, + direction: unit(seam_vector)?, + }, + Interval::new(0.0, seam_length)?, + true, + ); + Ok(SeamGenerator { + middle: seam_middle, + length: seam_length, + edge: seam_generator, + }) +} + +pub(super) fn cutter_face_bounds( + builder: &Builder, + numerical: &[NumericalCylinderBoundary], + lower: &NumericalCylinderBoundary, + upper: &NumericalCylinderBoundary, +) -> [[f64; 2]; 2] { + let mut cutter_u_bounds = [f64::INFINITY, f64::NEG_INFINITY]; + let mut cutter_v_bounds = [f64::INFINITY, f64::NEG_INFINITY]; + for boundary in numerical { + let definition = &builder.brep.geometry.intersections[boundary.definition as usize]; + for anchor in &definition.anchors { + cutter_u_bounds[0] = cutter_u_bounds[0].min(anchor.uv_b[0]); + cutter_u_bounds[1] = cutter_u_bounds[1].max(anchor.uv_b[0]); + cutter_v_bounds[0] = cutter_v_bounds[0].min(anchor.uv_b[1]); + cutter_v_bounds[1] = cutter_v_bounds[1].max(anchor.uv_b[1]); + } + } + for value in [ + upper.cutter_start[0], + upper.cutter_end[0], + lower.cutter_start[0], + ] { + cutter_u_bounds[0] = cutter_u_bounds[0].min(value); + cutter_u_bounds[1] = cutter_u_bounds[1].max(value); + } + for value in [lower.cutter_end[1], upper.cutter_start[1]] { + cutter_v_bounds[0] = cutter_v_bounds[0].min(value); + cutter_v_bounds[1] = cutter_v_bounds[1].max(value); + } + [cutter_u_bounds, cutter_v_bounds] +} + +pub(super) fn cutter_face_uses( + lower: &NumericalCylinderBoundary, + upper: &NumericalCylinderBoundary, + seam: &CutterSeam, +) -> Vec { + vec![ + boundary( + lower.edge, + lower.vertex, + lower.vertex, + Orientation::Forward, + PcurveGeometry::IntersectionSide { + definition: lower.definition, + side: IntersectionSide::B, + }, + ), + boundary( + seam.arc, + lower.vertex, + seam.middle, + seam.first_arc_sense, + uv_line([0.0, lower.cutter_end[1]], [1.0, 0.0]), + ), + boundary( + seam.generator, + seam.middle, + upper.vertex, + Orientation::Forward, + uv_line( + [upper.cutter_start[0], lower.cutter_end[1]], + [ + 0.0, + (upper.cutter_start[1] - lower.cutter_end[1]) / seam.length, + ], + ), + ), + boundary( + upper.edge, + upper.vertex, + upper.vertex, + Orientation::Forward, + PcurveGeometry::IntersectionSide { + definition: upper.definition, + side: IntersectionSide::B, + }, + ), + boundary( + seam.generator, + upper.vertex, + seam.middle, + Orientation::Reverse, + uv_line( + [upper.cutter_end[0], lower.cutter_end[1]], + [ + 0.0, + (upper.cutter_start[1] - lower.cutter_end[1]) / seam.length, + ], + ), + ), + boundary( + seam.arc, + seam.middle, + lower.vertex, + seam.second_arc_sense, + uv_line([seam.arc_shift, lower.cutter_end[1]], [1.0, 0.0]), + ), + ] +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/host.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/host.rs new file mode 100644 index 0000000..2faa65f --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/host.rs @@ -0,0 +1,171 @@ +use super::boundaries::NumericalCylinderBoundary; +use crate::brep::{ + boundary, uv_line, Builder, CurveGeometry, Frame3, GeometryError, IntersectionSide, + Orientation, PcurveGeometry, SurfaceGeometry, +}; +use crate::math::{scale, Interval}; +use crate::operations::modifying::boolean::operands::CylinderInput; + +pub(super) struct HostRims { + low: u32, + high: u32, + bottom: u32, + top_frame: Frame3, + top: u32, + host_seam: u32, +} + +pub(super) fn add_host_rims( + builder: &mut Builder, + host: &CylinderInput<'_>, + host_frame: Frame3, +) -> Result { + let low = builder.vertex(host_frame.point([host.radius, 0.0, 0.0])); + let high = builder.vertex(host_frame.point([host.radius, 0.0, host.height])); + let tau = std::f64::consts::TAU; + let bottom = builder.edge( + CurveGeometry::Circle { + frame: host_frame, + radius: host.radius, + }, + Interval::new(0.0, tau)?, + false, + ); + let mut top_frame = host_frame; + top_frame.origin = host_frame.point([0.0, 0.0, host.height]); + let top = builder.edge( + CurveGeometry::Circle { + frame: top_frame, + radius: host.radius, + }, + Interval::new(0.0, tau)?, + false, + ); + let host_seam = builder.edge( + CurveGeometry::Line { + origin: host_frame.point([host.radius, 0.0, 0.0]), + direction: host_frame.z, + }, + Interval::new(0.0, host.height)?, + true, + ); + Ok(HostRims { + low, + high, + bottom, + top_frame, + top, + host_seam, + }) +} + +pub(super) fn add_host_lateral_face( + builder: &mut Builder, + host: &CylinderInput<'_>, + host_frame: Frame3, + rims: &HostRims, + numerical: &[NumericalCylinderBoundary], +) -> Result<(), GeometryError> { + let tau = std::f64::consts::TAU; + let host_holes = numerical + .iter() + .map(|numeric_boundary| { + vec![boundary( + numeric_boundary.edge, + numeric_boundary.vertex, + numeric_boundary.vertex, + Orientation::Forward, + PcurveGeometry::IntersectionSide { + definition: numeric_boundary.definition, + side: IntersectionSide::A, + }, + )] + }) + .collect(); + builder.face_with_holes( + "host:lateral", + SurfaceGeometry::Cylinder { + frame: host_frame, + radius: host.radius, + }, + [[0.0, tau], [0.0, host.height]], + vec![ + boundary( + rims.bottom, + rims.low, + rims.low, + Orientation::Forward, + uv_line([0.0, 0.0], [1.0, 0.0]), + ), + boundary( + rims.host_seam, + rims.low, + rims.high, + Orientation::Forward, + uv_line([tau, 0.0], [0.0, 1.0]), + ), + boundary( + rims.top, + rims.high, + rims.high, + Orientation::Reverse, + uv_line([0.0, host.height], [1.0, 0.0]), + ), + boundary( + rims.host_seam, + rims.high, + rims.low, + Orientation::Reverse, + uv_line([0.0, 0.0], [0.0, 1.0]), + ), + ], + host_holes, + ) +} + +pub(super) fn add_host_caps( + builder: &mut Builder, + host: &CylinderInput<'_>, + host_frame: Frame3, + rims: &HostRims, +) -> Result<(), GeometryError> { + let mut lower_cap_frame = host_frame; + lower_cap_frame.y = scale(lower_cap_frame.y, -1.0); + lower_cap_frame.z = scale(lower_cap_frame.z, -1.0); + builder.face( + "host:lower_cap", + SurfaceGeometry::Plane { + frame: lower_cap_frame, + }, + [[-host.radius, host.radius]; 2], + vec![boundary( + rims.bottom, + rims.low, + rims.low, + Orientation::Reverse, + PcurveGeometry::Conic2 { + origin: [0.0; 2], + axis_a: [host.radius, 0.0], + axis_b: [0.0, -host.radius], + }, + )], + )?; + builder.face( + "host:upper_cap", + SurfaceGeometry::Plane { + frame: rims.top_frame, + }, + [[-host.radius, host.radius]; 2], + vec![boundary( + rims.top, + rims.high, + rims.high, + Orientation::Forward, + PcurveGeometry::Conic2 { + origin: [0.0; 2], + axis_a: [host.radius, 0.0], + axis_b: [0.0, host.radius], + }, + )], + ) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/mod.rs new file mode 100644 index 0000000..fdd80a1 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/cylinder_cylinder/transverse/mod.rs @@ -0,0 +1,104 @@ +mod boundaries; +mod charts; +mod cutter; +mod host; + +use crate::brep::{Accuracy, Builder, FaceRole, GeometryError, SurfaceGeometry}; +use crate::intersection::{intersect_breps, IntersectionGraph}; +use crate::operations::modifying::boolean::assembly::{ + cylinder_source, face_provenance, finish_cylinder_result, reverse_face, +}; +use crate::operations::modifying::boolean::operands::CylinderInput; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use boundaries::{add_numerical_boundaries, align_cutter_boundaries}; +use charts::{chart_seam_rotation, rotate_cylinder_chart}; +use cutter::{add_cutter_seam, cutter_face_bounds, cutter_face_uses}; +use host::{add_host_caps, add_host_lateral_face, add_host_rims}; + +pub(super) fn transverse_cylinder_through_subtraction( + host: &CylinderInput<'_>, + cutter: &CylinderInput<'_>, + id: String, + accuracy: Accuracy, +) -> Result { + let graph = lateral_intersection_graph(host, cutter)?; + + let seam_rotation = chart_seam_rotation(&graph); + let host_frame = rotate_cylinder_chart(host.frame, seam_rotation); + let mut builder = Builder::new(id, accuracy)?; + let rims = add_host_rims(&mut builder, host, host_frame)?; + + let mut numerical = add_numerical_boundaries(&mut builder, &graph, seam_rotation, accuracy)?; + align_cutter_boundaries(&mut builder, &mut numerical)?; + let lower = &numerical[0]; + let upper = &numerical[1]; + if upper.cutter_middle[1] - lower.cutter_middle[1] <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "cylinder through-cut branches are below geometric resolution".into(), + )); + } + + add_host_lateral_face(&mut builder, host, host_frame, &rims, &numerical)?; + add_host_caps(&mut builder, host, host_frame, &rims)?; + + let seam = add_cutter_seam(&mut builder, cutter, lower, upper, accuracy)?; + let cutter_face = builder.brep.topology.faces.len() as u32; + let cutter_bounds = cutter_face_bounds(&builder, &numerical, lower, upper); + builder.face( + "cutter:tunnel", + SurfaceGeometry::Cylinder { + frame: cutter.frame, + radius: cutter.radius, + }, + cutter_bounds, + cutter_face_uses(lower, upper, &seam), + )?; + builder.brep.topology.faces[0].provenance = + face_provenance(vec![cylinder_source(host, 0)], FaceRole::Split, false); + builder.brep.topology.faces[1].provenance = + face_provenance(vec![cylinder_source(host, 1)], FaceRole::Preserved, false); + builder.brep.topology.faces[2].provenance = + face_provenance(vec![cylinder_source(host, 2)], FaceRole::Preserved, false); + builder.brep.topology.faces[cutter_face as usize].provenance = + face_provenance(vec![cylinder_source(cutter, 0)], FaceRole::Cut, true); + reverse_face(&mut builder.brep, cutter_face); + let out = builder.finish_solid()?; + finish_cylinder_result(out, host, cutter, BooleanOp::Subtraction, false) +} + +fn lateral_intersection_graph( + host: &CylinderInput<'_>, + cutter: &CylinderInput<'_>, +) -> Result { + let mut body_graph = intersect_breps(host.brep, cutter.brep)?; + if body_graph.pairs.len() != 1 { + return Err(GeometryError::CoverageGap { + families: [ + "cylinder through subtraction".into(), + "cap-intersecting cylinder".into(), + ], + }); + } + let pair = body_graph + .pairs + .pop() + .ok_or_else(|| GeometryError::CoverageGap { + families: [ + "cylinder through subtraction".into(), + "missing lateral intersection graph".into(), + ], + })?; + if pair.faces != [0, 0] + || pair.graph.coincident + || !pair.graph.contacts.is_empty() + || pair.graph.branches.len() != 2 + { + return Err(GeometryError::CoverageGap { + families: [ + "cylinder through subtraction".into(), + "non-transverse cylinder graph".into(), + ], + }); + } + Ok(pair.graph) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/disjoint.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/disjoint.rs new file mode 100644 index 0000000..c27a858 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/disjoint.rs @@ -0,0 +1,91 @@ +use crate::brep::{Accuracy, BrepEnvelope, FaceSource, GeometryError, GeometryQuality}; +use crate::math::Point3; +use crate::operations::modifying::boolean::assembly::append_analytic_input; +use crate::operations::modifying::boolean::types::{ + BooleanOp, BooleanReport, BooleanResult, FaceMapping, +}; + +pub(crate) fn disjoint_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result, GeometryError> { + let (Some(a_bounds), Some(b_bounds)) = (a.bounds()?, b.bounds()?) else { + return Ok(None); + }; + let clearance = 4.0 * a.accuracy.geometric.max(b.accuracy.geometric); + let separated = (0..3).any(|axis| { + a_bounds.axes[axis].hi + clearance < b_bounds.axes[axis].lo + || b_bounds.axes[axis].hi + clearance < a_bounds.axes[axis].lo + }); + if !separated { + return Ok(None); + } + Ok(Some(separate_boolean(a, b, operation, id, Vec::new())?)) +} + +pub(super) fn separate_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, + contacts: Vec, +) -> Result { + if !matches!(a.quality, GeometryQuality::Analytic) + || !matches!(b.quality, GeometryQuality::Analytic) + { + return Err(GeometryError::UnsupportedGeometry( + "analytic boolean operands must have analytic quality".into(), + )); + } + let accuracy = Accuracy::combined(a.accuracy, b.accuracy); + let mut out = BrepEnvelope::new(id, accuracy)?; + let mut mappings = match operation { + BooleanOp::Union | BooleanOp::Subtraction => append_analytic_input(&mut out, a)?, + BooleanOp::Intersection => a + .topology + .faces + .iter() + .map(|face| FaceMapping { + source: FaceSource { + entity: a.id.clone(), + body: a.id.clone(), + key: face.key.clone(), + face: face.id, + }, + result_faces: Vec::new(), + }) + .collect(), + }; + match operation { + BooleanOp::Union => mappings.extend(append_analytic_input(&mut out, b)?), + BooleanOp::Intersection | BooleanOp::Subtraction => { + mappings.extend(b.topology.faces.iter().map(|face| FaceMapping { + source: FaceSource { + entity: b.id.clone(), + body: b.id.clone(), + key: face.key.clone(), + face: face.id, + }, + result_faces: Vec::new(), + })) + } + } + out.revision = a + .revision + .max(b.revision) + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; + out.validate()?; + Ok(BooleanResult { + brep: out, + report: BooleanReport { + operation, + quality: GeometryQuality::Analytic, + contacts, + coincident: false, + face_mappings: mappings, + }, + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/generic/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/generic/mod.rs new file mode 100644 index 0000000..73d97a6 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/generic/mod.rs @@ -0,0 +1,9 @@ +mod multiple_closed_loops; +mod non_intersecting; +mod single_closed_loop; +mod two_sided_cutter_band; + +pub(crate) use multiple_closed_loops::generic_multiple_closed_loops_boolean; +pub(crate) use non_intersecting::generic_non_intersecting_boolean; +pub(crate) use single_closed_loop::generic_single_closed_loop_boolean; +pub(crate) use two_sided_cutter_band::generic_two_sided_cutter_band_subtraction; diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/generic/multiple_closed_loops.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/generic/multiple_closed_loops.rs new file mode 100644 index 0000000..f5afd65 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/generic/multiple_closed_loops.rs @@ -0,0 +1,212 @@ +use crate::brep::{Accuracy, BrepEnvelope, GeometryError, Surface}; +use crate::intersection::{intersect_breps, IntersectionBranch, IntersectionGraph}; +use crate::math::{norm, sub}; +use crate::operations::modifying::boolean::assembly::{ + add_complement_input, add_hole_loop, add_patch_face, add_patch_loop, + append_closed_branch_geometry, finish_analytic_result, intersection_loop_sample, + loop_selection, loop_sense, merge_selected_shells, pcurve_support_surface, pcurve_winding, + retained_definition, reverse_face, LoopRegion, LoopSides, +}; +use crate::operations::modifying::boolean::operands::required_id; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use crate::query::{classify_point, PointClassification}; + +pub(crate) fn generic_multiple_closed_loops_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result, GeometryError> { + if a.solids.len() != 1 + || b.solids.len() != 1 + || a.topology.shells.len() != 1 + || b.topology.shells.len() != 1 + || !a.solids[0].cavity_shells.is_empty() + || !b.solids[0].cavity_shells.is_empty() + { + return Ok(None); + } + let body_graph = intersect_breps(a, b)?; + if body_graph.pairs.len() != 1 { + return Ok(None); + } + let pair = &body_graph.pairs[0]; + if pair.graph.coincident || !pair.graph.contacts.is_empty() || pair.graph.branches.len() < 2 { + return Ok(None); + } + let graph = &pair.graph; + let accuracy = Accuracy::combined(a.accuracy, b.accuracy); + if !branches_closed_and_unwound(graph, accuracy)? { + return Ok(None); + } + + let inputs = [a, b]; + let face_ids = pair.faces; + let (want_inside, reverse_selected) = loop_selection(operation, true); + let Some(selected_regions) = selected_loop_regions(graph, inputs, want_inside)? else { + return Ok(None); + }; + + let mut out = BrepEnvelope::new(id, accuracy)?; + let (mut selected_faces, selected_surfaces) = + add_selected_sides(&mut out, graph, inputs, face_ids, selected_regions)?; + let sides = LoopSides { + inputs, + face_ids, + regions: selected_regions, + reverse_selected, + surfaces: selected_surfaces, + }; + for (branch_index, branch) in graph.branches.iter().enumerate() { + add_branch_loops( + &mut out, + graph, + branch, + branch_index, + &sides, + &mut selected_faces, + accuracy.intersection, + )?; + } + for side in 0..2 { + if reverse_selected[side] { + for face in selected_faces[side].clone() { + reverse_face(&mut out, face); + } + } + } + finish_analytic_result(out, a, b, true, operation, false).map(Some) +} + +fn branches_closed_and_unwound( + graph: &IntersectionGraph, + accuracy: Accuracy, +) -> Result { + for branch in &graph.branches { + if norm(sub(branch.endpoints[0], branch.endpoints[1])) > accuracy.intersection { + return Ok(false); + } + for side in 0..2 { + if pcurve_winding( + &graph.geometry, + pcurve_support_surface(&graph.geometry, branch.pcurves[side])?, + branch.pcurves[side], + branch.range, + )? != [0, 0] + { + return Ok(false); + } + } + } + Ok(true) +} + +fn selected_loop_regions( + graph: &IntersectionGraph, + inputs: [&BrepEnvelope; 2], + want_inside: [bool; 2], +) -> Result, GeometryError> { + let mut loop_regions = [Vec::new(), Vec::new()]; + for branch in &graph.branches { + for side in 0..2 { + let uv = intersection_loop_sample(graph, branch, side)?; + let point = graph.geometry.surfaces[side].point_at(uv)?; + let inside = match classify_point(inputs[1 - side], point)? { + PointClassification::Inside => true, + PointClassification::Outside => false, + PointClassification::Unknown | PointClassification::Boundary => { + return Err(GeometryError::UnresolvedIntersection( + "closed SSI loop interior is unclassified".into(), + )) + } + }; + loop_regions[side].push(if inside == want_inside[side] { + LoopRegion::Patch + } else { + LoopRegion::Complement + }); + } + } + if [0, 1].into_iter().any(|side| { + loop_regions[side] + .iter() + .any(|region| *region != loop_regions[side][0]) + }) { + return Ok(None); + } + let selected_regions = [loop_regions[0][0], loop_regions[1][0]]; + for side in 0..2 { + if selected_regions[side] == LoopRegion::Patch && inputs[side].topology.faces.len() != 1 { + return Ok(None); + } + } + Ok(Some(selected_regions)) +} + +fn add_selected_sides( + out: &mut BrepEnvelope, + graph: &IntersectionGraph, + inputs: [&BrepEnvelope; 2], + face_ids: [u32; 2], + selected_regions: [LoopRegion; 2], +) -> Result<([Vec; 2], [u32; 2]), GeometryError> { + let mut selected_faces = [Vec::new(), Vec::new()]; + let mut selected_surfaces = [None, None]; + let mut selected_shells = Vec::new(); + for side in 0..2 { + if selected_regions[side] == LoopRegion::Complement { + add_complement_input( + out, + inputs[side], + face_ids[side], + &mut selected_surfaces[side], + &mut selected_shells, + |face| selected_faces[side].push(face), + )?; + } else { + selected_surfaces[side] = Some(out.geometry.surfaces.len() as u32); + out.geometry + .surfaces + .push(graph.geometry.surfaces[side].clone()); + } + } + merge_selected_shells(out, &selected_shells); + + let selected_surfaces = [ + required_id(selected_surfaces[0])?, + required_id(selected_surfaces[1])?, + ]; + Ok((selected_faces, selected_surfaces)) +} + +fn add_branch_loops( + out: &mut BrepEnvelope, + graph: &IntersectionGraph, + branch: &IntersectionBranch, + branch_index: usize, + sides: &LoopSides<'_, u32>, + selected_faces: &mut [Vec; 2], + tolerance: f64, +) -> Result<(), GeometryError> { + let geometric = out.accuracy.geometric; + let loop_edge = append_closed_branch_geometry( + out, + graph, + branch, + geometric, + |_, definition| retained_definition(graph, definition, sides.surfaces), + |side| Ok(sides.surfaces[side]), + )?; + for side in 0..2 { + let sense = loop_sense(side, sides.reverse_selected[side]); + if sides.regions[side] == LoopRegion::Complement { + let face = selected_faces[side][0]; + add_hole_loop(out, &loop_edge, side, face, sense, tolerance)?; + } else { + let patch = add_patch_loop(out, &loop_edge, side, sense, tolerance)?; + add_patch_face(out, sides, side, &patch, Some(branch_index), Ok)?; + selected_faces[side].push(patch.face); + } + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/generic/non_intersecting.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/generic/non_intersecting.rs new file mode 100644 index 0000000..41eac5e --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/generic/non_intersecting.rs @@ -0,0 +1,69 @@ +use crate::brep::{BrepEnvelope, GeometryError}; +use crate::intersection::intersect_breps; +use crate::math::{norm, sub}; +use crate::operations::modifying::boolean::handlers::containment::{ + analytic_containment_boolean, interior_sample, +}; +use crate::operations::modifying::boolean::handlers::disjoint::separate_boolean; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use crate::query::{classify_point, PointClassification}; + +pub(crate) fn generic_non_intersecting_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result, GeometryError> { + let graph = match intersect_breps(a, b) { + Ok(graph) => graph, + Err(GeometryError::CoverageGap { .. }) => return Ok(None), + Err(error) => return Err(error), + }; + if graph + .pairs + .iter() + .any(|pair| pair.graph.coincident || !pair.graph.branches.is_empty()) + { + return Ok(None); + } + let mut contacts = graph + .pairs + .into_iter() + .flat_map(|pair| pair.graph.contacts) + .collect::>(); + let tolerance = a.accuracy.geometric.max(b.accuracy.geometric); + let mut unique = Vec::new(); + for point in contacts.drain(..) { + if unique + .iter() + .all(|existing| norm(sub(*existing, point)) > tolerance) + { + unique.push(point); + } + } + + let a_in_b = classify_point(b, interior_sample(a)?)?; + let b_in_a = classify_point(a, interior_sample(b)?)?; + if matches!( + a_in_b, + PointClassification::Unknown | PointClassification::Boundary + ) || matches!( + b_in_a, + PointClassification::Unknown | PointClassification::Boundary + ) { + return Err(GeometryError::UnresolvedIntersection( + "generic boolean containment classification is unresolved".into(), + )); + } + let a_inside_b = a_in_b == PointClassification::Inside; + let b_inside_a = b_in_a == PointClassification::Inside; + if a_inside_b && b_inside_a { + return Err(GeometryError::UnresolvedIntersection( + "mutual containment without a coincident boundary is unresolved".into(), + )); + } + if a_inside_b || b_inside_a { + return analytic_containment_boolean(a, b, b_inside_a, operation, id).map(Some); + } + separate_boolean(a, b, operation, id, unique).map(Some) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/generic/single_closed_loop.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/generic/single_closed_loop.rs new file mode 100644 index 0000000..7bebd7c --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/generic/single_closed_loop.rs @@ -0,0 +1,253 @@ +use crate::brep::{ + Accuracy, BrepEnvelope, GeometryError, GeometryStore, IntersectionDefinition, IntersectionSide, + PcurveGeometry, SurfaceGeometry, +}; +use crate::intersection::{intersect_breps, IntersectionBranch, IntersectionGraph}; +use crate::math::{norm, sub}; +use crate::operations::modifying::boolean::assembly::{ + add_complement_input, add_hole_loop, add_patch_face, add_patch_loop, + append_closed_branch_geometry, finish_analytic_result, intersection_definition, + loop_containment, loop_regions, loop_sense, merge_selected_shells, pcurve_support_surface, + pcurve_winding, reframe_sphere_intersection, reverse_face, ClosedBranchEdge, LoopRegion, + LoopSides, +}; +use crate::operations::modifying::boolean::operands::required_id; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(crate) fn generic_single_closed_loop_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result, GeometryError> { + if a.solids.len() != 1 + || b.solids.len() != 1 + || a.topology.shells.len() != 1 + || b.topology.shells.len() != 1 + || !a.solids[0].cavity_shells.is_empty() + || !b.solids[0].cavity_shells.is_empty() + { + return Ok(None); + } + let body_graph = intersect_breps(a, b)?; + if body_graph.pairs.len() != 1 { + return Ok(None); + } + let pair = &body_graph.pairs[0]; + if pair.graph.coincident || !pair.graph.contacts.is_empty() || pair.graph.branches.len() != 1 { + return Ok(None); + } + let graph = &pair.graph; + let branch = &graph.branches[0]; + let accuracy = Accuracy::combined(a.accuracy, b.accuracy); + if norm(sub(branch.endpoints[0], branch.endpoints[1])) > accuracy.intersection { + return Ok(None); + } + let Some(windings) = supported_windings(graph, branch)? else { + return Ok(None); + }; + let inputs = [a, b]; + let face_ids = pair.faces; + let loop_inside_other = loop_containment( + a, + b, + graph, + branch, + "closed SSI loop interior is unclassified", + )?; + let (regions, reverse_selected) = loop_regions(operation, true, loop_inside_other); + + let mut out = BrepEnvelope::new(id, accuracy)?; + let (mut selected_faces, selected_surfaces) = + add_selected_sides(&mut out, graph, inputs, face_ids, regions)?; + let sides = LoopSides { + inputs, + face_ids, + regions, + reverse_selected, + surfaces: selected_surfaces, + }; + let loop_edge = add_intersection_edge( + &mut out, + graph, + branch, + windings, + sides.surfaces, + accuracy.geometric, + )?; + add_loop_sides( + &mut out, + &sides, + &loop_edge, + &mut selected_faces, + accuracy.intersection, + )?; + for side in 0..2 { + if reverse_selected[side] { + reverse_face(&mut out, required_id(selected_faces[side])?); + } + } + finish_analytic_result(out, a, b, true, operation, false).map(Some) +} + +fn supported_windings( + graph: &IntersectionGraph, + branch: &IntersectionBranch, +) -> Result, GeometryError> { + let windings = [ + pcurve_winding( + &graph.geometry, + pcurve_support_surface(&graph.geometry, branch.pcurves[0])?, + branch.pcurves[0], + branch.range, + )?, + pcurve_winding( + &graph.geometry, + pcurve_support_surface(&graph.geometry, branch.pcurves[1])?, + branch.pcurves[1], + branch.range, + )?, + ]; + for side in 0..2 { + if windings[side] != [0, 0] + && !matches!( + graph.geometry.surface(pcurve_support_surface( + &graph.geometry, + branch.pcurves[side], + )?)?, + SurfaceGeometry::Sphere { .. } + ) + { + return Ok(None); + } + } + Ok(Some(windings)) +} + +fn add_selected_sides( + out: &mut BrepEnvelope, + graph: &IntersectionGraph, + inputs: [&BrepEnvelope; 2], + face_ids: [u32; 2], + regions: [LoopRegion; 2], +) -> Result<([Option; 2], [Option; 2]), GeometryError> { + let mut selected_faces = [None; 2]; + let mut selected_surfaces = [None; 2]; + let mut selected_shells = Vec::new(); + for side in 0..2 { + if regions[side] != LoopRegion::Complement { + continue; + } + add_complement_input( + out, + inputs[side], + face_ids[side], + &mut selected_surfaces[side], + &mut selected_shells, + |face| selected_faces[side] = Some(face), + )?; + } + merge_selected_shells(out, &selected_shells); + for side in 0..2 { + if regions[side] == LoopRegion::Patch { + selected_surfaces[side] = Some(out.geometry.surfaces.len() as u32); + out.geometry + .surfaces + .push(graph.geometry.surfaces[side].clone()); + } + } + Ok((selected_faces, selected_surfaces)) +} + +fn add_intersection_edge( + out: &mut BrepEnvelope, + graph: &IntersectionGraph, + branch: &IntersectionBranch, + windings: [[i32; 2]; 2], + selected_surfaces: [Option; 2], + geometric: f64, +) -> Result { + append_closed_branch_geometry( + out, + graph, + branch, + geometric, + |geometry, definition| { + retarget_loop_definition( + geometry, + graph, + branch, + definition, + windings, + selected_surfaces, + ) + }, + |side| required_id(selected_surfaces[side]), + ) +} + +fn retarget_loop_definition( + geometry: &mut GeometryStore, + graph: &IntersectionGraph, + branch: &IntersectionBranch, + definition: u32, + windings: [[i32; 2]; 2], + selected_surfaces: [Option; 2], +) -> Result { + let mut definition_geometry = intersection_definition(graph, definition)?; + definition_geometry.surfaces = [ + selected_surfaces[definition_geometry.surfaces[0] as usize].ok_or_else(|| { + GeometryError::InvalidTopology( + "closed-loop first support surface was not retained".into(), + ) + })?, + selected_surfaces[definition_geometry.surfaces[1] as usize].ok_or_else(|| { + GeometryError::InvalidTopology( + "closed-loop second support surface was not retained".into(), + ) + })?, + ]; + for side in 0..2 { + if windings[side] == [0, 0] { + continue; + } + let definition_side = match graph.geometry.pcurves[branch.pcurves[side] as usize] { + PcurveGeometry::IntersectionSide { + side: IntersectionSide::A, + .. + } => 0, + PcurveGeometry::IntersectionSide { + side: IntersectionSide::B, + .. + } => 1, + _ => { + return Err(GeometryError::InvalidGeometry( + "winding intersection loop is missing its support pcurve".into(), + )) + } + }; + reframe_sphere_intersection(geometry, &mut definition_geometry, definition_side)?; + } + Ok(definition_geometry) +} + +fn add_loop_sides( + out: &mut BrepEnvelope, + sides: &LoopSides<'_, Option>, + loop_edge: &ClosedBranchEdge, + selected_faces: &mut [Option; 2], + tolerance: f64, +) -> Result<(), GeometryError> { + for side in 0..2 { + let sense = loop_sense(side, sides.reverse_selected[side]); + if sides.regions[side] == LoopRegion::Complement { + let face = required_id(selected_faces[side])?; + add_hole_loop(out, loop_edge, side, face, sense, tolerance)?; + } else { + let patch = add_patch_loop(out, loop_edge, side, sense, tolerance)?; + add_patch_face(out, sides, side, &patch, None, required_id)?; + selected_faces[side] = Some(patch.face); + } + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/generic/two_sided_cutter_band.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/generic/two_sided_cutter_band.rs new file mode 100644 index 0000000..3186f4d --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/generic/two_sided_cutter_band.rs @@ -0,0 +1,235 @@ +use crate::brep::{ + Accuracy, BrepEnvelope, CurveGeometry, Face, FaceProvenance, FaceRole, GeometryError, + Orientation, TrimRegion, +}; +use crate::intersection::{intersect_breps, IntersectionBranch, IntersectionGraph}; +use crate::math::{add, norm, scale, sub, Interval}; +use crate::operations::modifying::boolean::assembly::{ + add_closed_intersection_loop, add_hole_loop, append_analytic_input, + append_closed_branch_geometry, finish_analytic_result, pcurve_support_surface, pcurve_winding, + retained_definition, reverse_face, topology_loop_bounds, +}; +use crate::operations::modifying::boolean::operands::brep_face_source; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +struct CutBand { + face: u32, + surface: u32, +} + +pub(crate) fn generic_two_sided_cutter_band_subtraction( + host: &BrepEnvelope, + cutter: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result, GeometryError> { + if operation != BooleanOp::Subtraction + || host.solids.len() != 1 + || cutter.solids.len() != 1 + || host.topology.shells.len() != 1 + || cutter.topology.shells.len() != 1 + || !host.solids[0].cavity_shells.is_empty() + || !cutter.solids[0].cavity_shells.is_empty() + { + return Ok(None); + } + let body_graph = intersect_breps(host, cutter)?; + if body_graph.pairs.len() != 2 + || body_graph.pairs[0].faces[1] != body_graph.pairs[1].faces[1] + || body_graph.pairs[0].faces[0] == body_graph.pairs[1].faces[0] + { + return Ok(None); + } + let mut closed_branches = Vec::with_capacity(2); + for pair in &body_graph.pairs { + let Some(closed) = single_closed_branch(&pair.graph, host, cutter)? else { + return Ok(None); + }; + closed_branches.push(closed); + } + + let accuracy = Accuracy::combined(host.accuracy, cutter.accuracy); + let mut out = BrepEnvelope::new(id, accuracy)?; + append_analytic_input(&mut out, host)?; + let cutter_surface = out.geometry.surfaces.len() as u32; + out.geometry + .surfaces + .push(body_graph.pairs[0].graph.geometry.surfaces[1].clone()); + let cut_face = out.topology.faces.len() as u32; + let band = CutBand { + face: cut_face, + surface: cutter_surface, + }; + let mut cut_loops = Vec::with_capacity(2); + let mut cut_bounds: Option<[Interval; 2]> = None; + for (pair, branch) in body_graph.pairs.iter().zip(&closed_branches) { + let (cutter_loop, bounds) = add_band_loops( + &mut out, + &pair.graph, + branch, + pair.faces[0], + &band, + &cut_loops, + accuracy.intersection, + )?; + cut_bounds = Some(widened_cut_bounds(cut_bounds, bounds)?); + cut_loops.push(cutter_loop); + } + add_cut_face( + &mut out, + cutter, + body_graph.pairs[0].faces[1], + &band, + cut_bounds, + &cut_loops, + )?; + reverse_face(&mut out, cut_face); + finish_analytic_result(out, host, cutter, true, operation, false).map(Some) +} + +fn single_closed_branch( + graph: &IntersectionGraph, + host: &BrepEnvelope, + cutter: &BrepEnvelope, +) -> Result, GeometryError> { + if graph.coincident || !graph.contacts.is_empty() || graph.branches.len() != 1 { + return Ok(None); + } + let branch = &graph.branches[0]; + let closure = norm(sub(branch.endpoints[0], branch.endpoints[1])); + let intersection = host.accuracy.intersection.max(cutter.accuracy.intersection); + let geometric = host.accuracy.geometric.max(cutter.accuracy.geometric); + let closed = if closure <= intersection { + IntersectionBranch { + curve: branch.curve, + pcurves: branch.pcurves, + range: branch.range, + endpoints: branch.endpoints, + } + } else if let Some(CurveGeometry::Circle { frame, radius }) = + graph.geometry.curves.get(branch.curve as usize) + { + let tau = std::f64::consts::TAU; + if closure > 4.0 * geometric + || branch.range.lo.abs() * radius > 4.0 * geometric + || (branch.range.hi - tau).abs() * radius > 4.0 * geometric + { + return Ok(None); + } + let point = add(frame.origin, scale(frame.x, *radius)); + IntersectionBranch { + curve: branch.curve, + pcurves: branch.pcurves, + range: Interval::new(0.0, tau)?, + endpoints: [point, point], + } + } else { + return Ok(None); + }; + if pcurve_winding( + &graph.geometry, + pcurve_support_surface(&graph.geometry, closed.pcurves[0])?, + closed.pcurves[0], + closed.range, + )? != [0, 0] + { + return Ok(None); + } + Ok(Some(closed)) +} + +fn add_band_loops( + out: &mut BrepEnvelope, + graph: &IntersectionGraph, + branch: &IntersectionBranch, + host_face: u32, + band: &CutBand, + cut_loops: &[u32], + tolerance: f64, +) -> Result<(u32, [Interval; 2]), GeometryError> { + let host_surface = out.topology.faces[host_face as usize].surface; + let selected_surfaces = [host_surface, band.surface]; + let geometric = out.accuracy.geometric; + let loop_edge = append_closed_branch_geometry( + out, + graph, + branch, + geometric, + |_, definition| retained_definition(graph, definition, selected_surfaces), + |side| Ok(selected_surfaces[side]), + )?; + add_hole_loop( + out, + &loop_edge, + 0, + host_face, + Orientation::Forward, + tolerance, + )?; + out.topology.faces[host_face as usize].provenance.role = FaceRole::Split; + + let cutter_loop = add_closed_intersection_loop( + out, + loop_edge.edge, + loop_edge.vertex, + band.face, + loop_edge.pcurves[1].clone(), + Orientation::Forward, + !cut_loops.is_empty(), + )?; + let bounds = topology_loop_bounds(out, cutter_loop, tolerance)?; + Ok((cutter_loop, bounds)) +} + +fn widened_cut_bounds( + cut_bounds: Option<[Interval; 2]>, + bounds: [Interval; 2], +) -> Result<[Interval; 2], GeometryError> { + Ok(match cut_bounds { + Some(previous) => [0, 1] + .map(|axis| { + Interval::new( + previous[axis].lo.min(bounds[axis].lo), + previous[axis].hi.max(bounds[axis].hi), + ) + }) + .into_iter() + .collect::, _>>()? + .try_into() + .map_err(|_| GeometryError::InvalidGeometry("cutter band UV bounds".into()))?, + None => bounds, + }) +} + +fn add_cut_face( + out: &mut BrepEnvelope, + cutter: &BrepEnvelope, + cutter_face: u32, + band: &CutBand, + cut_bounds: Option<[Interval; 2]>, + cut_loops: &[u32], +) -> Result<(), GeometryError> { + let source_face = &cutter.topology.faces[cutter_face as usize]; + out.topology.faces.push(Face { + id: band.face, + key: format!("{}:through-cut", source_face.key), + surface: band.surface, + sense: source_face.sense, + trim: TrimRegion { + chart: source_face.trim.chart, + uv_bounds: cut_bounds.ok_or_else(|| { + GeometryError::InvalidTopology("cutter band has no trim bounds".into()) + })?, + outer: cut_loops[0], + holes: cut_loops[1..].to_vec(), + }, + shell_ref: Some(0), + provenance: FaceProvenance { + sources: vec![brep_face_source(cutter, source_face.id)], + role: FaceRole::Cut, + reversed: true, + }, + }); + out.topology.shells[0].faces.push(band.face); + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/mod.rs new file mode 100644 index 0000000..39fbb6c --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/mod.rs @@ -0,0 +1,39 @@ +mod boxes; +mod coincident; +mod containment; +mod cylinder_box; +mod cylinder_cylinder; +mod disjoint; +mod generic; +mod planar; +mod planar_extrusions; +mod rectilinear; +mod sphere_box; +mod sphere_cylinder; +mod sphere_sphere; +mod vertical_arc_extrusion; + +pub use boxes::boolean_boxes; +pub(super) use coincident::coincident_boolean; +pub(super) use containment::{ + analytic_containment_boolean, conic_box_containment, conic_containment_boolean, + cylinder_conic_containment, sphere_conic_containment, sphere_torus_containment, + torus_box_containment, torus_conic_containment, torus_containment_boolean, + torus_cylinder_containment, +}; +pub(super) use cylinder_box::cylinder_box_boolean; +pub(super) use cylinder_cylinder::boolean_cylinders; +pub(super) use disjoint::disjoint_boolean; +pub(super) use generic::{ + generic_multiple_closed_loops_boolean, generic_non_intersecting_boolean, + generic_single_closed_loop_boolean, generic_two_sided_cutter_band_subtraction, +}; +pub(super) use planar::{subtract_layered_extrusions, subtract_planar_polyhedra}; +pub(super) use planar_extrusions::boolean_planar_extrusions; +pub(super) use rectilinear::boolean_rectilinear; +pub(super) use sphere_box::sphere_box_boolean; +pub(super) use sphere_cylinder::sphere_cylinder_boolean; +pub use sphere_sphere::boolean_spheres; +pub(super) use vertical_arc_extrusion::{ + subtract_vertical_arc_extrusion, subtract_vertical_arc_extrusion_batch, +}; diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/planar/facets.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/facets.rs new file mode 100644 index 0000000..5bb3e51 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/facets.rs @@ -0,0 +1,339 @@ +use crate::brep::{ + coverage_gap, padded_uv_bounds, plane_boundary, unit, Accuracy, BrepEnvelope, Builder, + CurveGeometry, EdgeGeometry, FaceProvenance, Frame3, GeometryError, Orientation, Shell, + SolidRegion, SurfaceGeometry, Use, +}; +use crate::math::{cross, dot, norm, sub, Interval, Point3}; +use crate::operations::modifying::boolean::assembly::{ + add_welded_vertex, analytic_face_mappings, assign_shell_faces, enclosing_solid, +}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanReport, BooleanResult}; +use crate::query::face_contains_uv; +use std::collections::BTreeMap; + +pub(super) struct PlanarFacets { + builder: Builder, + vertices: Vec<(Point3, u32)>, + edges: BTreeMap<(u32, u32), u32>, + pub(super) accuracy: Accuracy, +} + +pub(super) fn loop_positions( + brep: &BrepEnvelope, + loop_id: u32, +) -> Result, GeometryError> { + let start = brep + .topology + .loops + .get(loop_id as usize) + .ok_or_else(coverage)? + .start_halfedge; + let mut current = start; + let mut points = Vec::new(); + loop { + let halfedge = brep + .topology + .halfedges + .get(current as usize) + .ok_or_else(coverage)?; + points.push( + brep.topology + .vertices + .get(halfedge.from as usize) + .ok_or_else(coverage)? + .position, + ); + current = halfedge.next.ok_or_else(coverage)?; + if current == start { + break; + } + if points.len() > brep.topology.halfedges.len() { + return Err(coverage()); + } + } + Ok(points) +} + +pub(super) fn coverage() -> GeometryError { + coverage_gap( + "layered planar profile extrusion", + "layered planar profile extrusion", + ) +} + +impl PlanarFacets { + pub(super) fn new(id: String, accuracy: Accuracy) -> Result { + Ok(Self { + builder: Builder::new(id, accuracy)?, + vertices: Vec::new(), + edges: BTreeMap::new(), + accuracy, + }) + } + + fn vertex(&mut self, point: Point3) -> u32 { + add_welded_vertex(&mut self.builder, &mut self.vertices, point, self.accuracy) + } + + fn uses(&mut self, ring: &[Point3], frame: Frame3) -> Result, GeometryError> { + let ids = ring + .iter() + .map(|point| self.vertex(*point)) + .collect::>(); + let mut uses = Vec::with_capacity(ids.len()); + for index in 0..ids.len() { + let next = (index + 1) % ids.len(); + let from = ids[index]; + let to = ids[next]; + if from == to { + return Err(GeometryError::UnresolvedIntersection( + "layered planar face has a sub-tolerance edge".into(), + )); + } + let key = (from.min(to), from.max(to)); + let edge = if let Some(&edge) = self.edges.get(&key) { + edge + } else { + let delta = sub(ring[next], ring[index]); + let length = norm(delta); + let edge = self.builder.edge( + CurveGeometry::Line { + origin: ring[index], + direction: unit(delta)?, + }, + Interval::new(0.0, length)?, + false, + ); + self.edges.insert(key, edge); + edge + }; + let EdgeGeometry::Curve { curve, .. } = + self.builder.brep.topology.edges[edge as usize].geometry + else { + return Err(coverage()); + }; + let CurveGeometry::Line { origin, direction } = + self.builder.brep.geometry.curves[curve as usize] + else { + return Err(coverage()); + }; + let sense = if norm(sub(origin, ring[index])) <= self.accuracy.geometric / 4.0 + && dot(direction, sub(ring[next], ring[index])) > 0.0 + { + Orientation::Forward + } else { + Orientation::Reverse + }; + uses.push(plane_boundary(&self.builder, frame, edge, from, to, sense)?); + } + Ok(uses) + } + + pub(super) fn face( + &mut self, + key: &str, + frame: Frame3, + outer: Vec, + holes: Vec>, + provenance: FaceProvenance, + ) -> Result<(), GeometryError> { + for point in outer.iter().chain(holes.iter().flatten()) { + let local = frame.local(*point); + if local[2].abs() > 4.0 * self.accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "layered planar face is not coplanar".into(), + )); + } + } + let bounds = padded_uv_bounds( + outer + .iter() + .chain(holes.iter().flatten()) + .map(|point| frame.local(*point)), + self.accuracy.geometric, + ); + let outer_uses = self.uses(&outer, frame)?; + let hole_uses = holes + .iter() + .map(|hole| self.uses(hole, frame)) + .collect::, _>>()?; + let face = self.builder.brep.topology.faces.len(); + self.builder.face_with_holes( + &format!("{key}-{face}"), + SurfaceGeometry::Plane { frame }, + bounds, + outer_uses, + hole_uses, + )?; + self.builder.brep.topology.faces[face].provenance = provenance; + Ok(()) + } +} + +pub(super) fn finish( + mut facets: PlanarFacets, + a: &BrepEnvelope, + b: &BrepEnvelope, +) -> Result { + let mut assigned = vec![false; facets.builder.brep.topology.faces.len()]; + let mut outer_shells = Vec::new(); + let mut cavity_shells = Vec::new(); + for start in 0..assigned.len() { + if assigned[start] { + continue; + } + let shell = facets.builder.brep.topology.shells.len() as u32; + let faces = assign_shell_faces( + &mut facets.builder.brep, + &mut assigned, + start, + shell, + || GeometryError::InvalidTopology("layered planar Boolean has an open boundary".into()), + || GeometryError::InvalidTopology("layered planar twin has no face".into()), + )?; + let volume = shell_volume(&facets.builder.brep, &faces)?; + if volume.abs() <= facets.accuracy.geometric.powi(3) { + return Err(coverage()); + } + facets.builder.brep.topology.shells.push(Shell { + id: shell, + faces, + is_closed: true, + }); + if volume > 0.0 { + outer_shells.push((shell, volume)); + } else { + cavity_shells.push(shell); + } + } + for &(outer_shell, _) in &outer_shells { + facets.builder.brep.solids.push(SolidRegion { + outer_shell, + cavity_shells: Vec::new(), + }); + } + for cavity_shell in cavity_shells { + let index = enclosing_solid( + &facets.builder.brep, + &outer_shells, + cavity_shell, + |brep, faces, point| { + shell_contains_point(brep, faces, point, facets.accuracy.intersection) + }, + coverage, + )?; + facets.builder.brep.solids[index] + .cavity_shells + .push(cavity_shell); + } + finish_planar_result(facets.builder.brep, a, b) +} + +fn shell_volume(brep: &BrepEnvelope, faces: &[u32]) -> Result { + let mut volume = 0.0; + for &face_id in faces { + let face = &brep.topology.faces[face_id as usize]; + for &loop_id in std::iter::once(&face.trim.outer).chain(&face.trim.holes) { + let start = brep.topology.loops[loop_id as usize].start_halfedge; + let mut current = start; + let mut points = Vec::new(); + loop { + let halfedge = &brep.topology.halfedges[current as usize]; + points.push(brep.topology.vertices[halfedge.from as usize].position); + current = halfedge.next.ok_or_else(|| { + GeometryError::InvalidTopology("layered planar face has open loop".into()) + })?; + if current == start { + break; + } + if points.len() > brep.topology.halfedges.len() { + return Err(coverage()); + } + } + for index in 1..points.len() - 1 { + volume += dot(points[0], cross(points[index], points[index + 1])) / 6.0; + } + } + } + Ok(volume) +} + +fn shell_contains_point( + brep: &BrepEnvelope, + faces: &[u32], + point: Point3, + tolerance: f64, +) -> Result { + for direction in [ + unit([1.0, 0.371, 0.127])?, + unit([0.193, 1.0, 0.419])?, + unit([0.311, 0.233, 1.0])?, + ] { + let mut hits: Vec = Vec::new(); + let mut uncertain = false; + for &face_id in faces { + let face = &brep.topology.faces[face_id as usize]; + let SurfaceGeometry::Plane { frame } = brep.geometry.surface(face.surface)? else { + return Err(coverage()); + }; + let denominator = dot(frame.z, direction); + let numerator = dot(frame.z, sub(frame.origin, point)); + if denominator.abs() <= 64.0 * f64::EPSILON { + if numerator.abs() <= tolerance { + uncertain = true; + break; + } + continue; + } + let distance = numerator / denominator; + if distance <= tolerance { + continue; + } + let position = [ + point[0] + direction[0] * distance, + point[1] + direction[1] * distance, + point[2] + direction[2] * distance, + ]; + let local = frame.local(position); + match face_contains_uv(brep, face, [local[0], local[1]])? { + Some(true) if hits.iter().any(|hit| (hit - distance).abs() <= tolerance) => { + uncertain = true; + break; + } + Some(true) => hits.push(distance), + Some(false) => {} + None => { + uncertain = true; + break; + } + } + } + if !uncertain { + return Ok(hits.len() % 2 == 1); + } + } + Err(coverage()) +} + +fn finish_planar_result( + mut out: BrepEnvelope, + a: &BrepEnvelope, + b: &BrepEnvelope, +) -> Result { + out.revision = + a.revision.max(b.revision).checked_add(1).ok_or_else(|| { + GeometryError::LimitExceeded("planar Boolean revision overflow".into()) + })?; + out.validate()?; + let face_mappings = analytic_face_mappings(&out, [a, b]); + Ok(BooleanResult { + report: BooleanReport { + operation: BooleanOp::Subtraction, + quality: out.quality.clone(), + contacts: Vec::new(), + coincident: true, + face_mappings, + }, + brep: out, + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/planar/layered_extrusions/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/layered_extrusions/mod.rs new file mode 100644 index 0000000..e953b0e --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/layered_extrusions/mod.rs @@ -0,0 +1,466 @@ +mod regions; + +use super::facets::{coverage, finish, PlanarFacets}; +use crate::brep::{ + unit, Accuracy, BrepEnvelope, CurveGeometry, FaceProvenance, FaceRole, FaceSource, Frame3, + GeometryError, SurfaceGeometry, +}; +use crate::geom2d::{Pt2, RingRegion}; +use crate::math::{cross, dot, scale, sub, Point3}; +use crate::operations::modifying::boolean::operands::{ + all_planar, brep_face_source, full_planar_extrusion, +}; +use crate::operations::modifying::boolean::types::BooleanResult; +use crate::query::face_contains_uv; +use regions::{contours, layer_boundaries, layer_regions, LayerBoundaries}; + +struct LayerSlab<'a> { + base: Frame3, + regions: &'a [RingRegion], + vertices: &'a [Pt2], + lo: f64, + hi: f64, +} + +pub(crate) fn subtract_layered_extrusions( + host: &BrepEnvelope, + cutter: &BrepEnvelope, + id: String, +) -> Result { + host.validate()?; + let b = full_planar_extrusion(cutter)?; + if !all_planar(host) + || host + .geometry + .curves + .iter() + .any(|curve| !matches!(curve, CurveGeometry::Line { .. })) + || host.solids.is_empty() + { + return Err(coverage()); + } + let accuracy = Accuracy::combined(host.accuracy, cutter.accuracy); + let host_positions = host + .topology + .vertices + .iter() + .map(|vertex| b.frame.local(vertex.position)[2]) + .collect::>(); + let host_lo = host_positions.iter().copied().fold(f64::INFINITY, f64::min); + let host_hi = host_positions + .iter() + .copied() + .fold(f64::NEG_INFINITY, f64::max); + if !host_lo.is_finite() || host_hi - host_lo <= 4.0 * accuracy.geometric { + return Err(coverage()); + } + let frame = Frame3 { + origin: b.frame.point([0.0, 0.0, host_lo]), + ..b.frame + }; + check_layer_alignment(host, frame)?; + let b_contours = contours(&b, frame); + let b_lo = dot(sub(b.frame.origin, frame.origin), frame.z); + let b_hi = b_lo + b.height; + let host_height = host_hi - host_lo; + if b_hi <= accuracy.geometric || b_lo >= host_height - accuracy.geometric { + return Err(coverage()); + } + let levels = layer_levels(&host_positions, host_lo, b_lo, b_hi, host_height, accuracy); + let layers = layer_regions(host, frame, &levels, &b_contours, b_lo, b_hi, accuracy)?; + let boundaries = layer_boundaries(&layers, accuracy); + let mut facets = PlanarFacets::new(id, accuracy)?; + cap( + &mut facets, + frame, + 0.0, + false, + &layers[0], + &boundaries.vertices, + &[(host, FaceRole::Split)], + )?; + add_layer_faces( + &mut facets, + frame, + &levels, + &layers, + &boundaries, + host, + cutter, + )?; + cap( + &mut facets, + frame, + host_height, + true, + layers.last().ok_or_else(coverage)?, + &boundaries.vertices, + &[(host, FaceRole::Split)], + )?; + finish(facets, host, cutter) +} + +fn check_layer_alignment(host: &BrepEnvelope, frame: Frame3) -> Result<(), GeometryError> { + for face in &host.topology.faces { + let SurfaceGeometry::Plane { frame: face_frame } = host + .geometry + .surfaces + .get(face.surface as usize) + .ok_or_else(coverage)? + else { + return Err(coverage()); + }; + let alignment = dot(face_frame.z, frame.z).abs(); + if alignment > 1.0e-10 && alignment < 1.0 - 1.0e-10 { + return Err(coverage()); + } + } + Ok(()) +} + +fn layer_levels( + host_positions: &[f64], + host_lo: f64, + b_lo: f64, + b_hi: f64, + host_height: f64, + accuracy: Accuracy, +) -> Vec { + let mut levels = host_positions + .iter() + .map(|level| level - host_lo) + .collect::>(); + for level in [b_lo, b_hi] { + if level > accuracy.geometric && level < host_height - accuracy.geometric { + levels.push(level); + } + } + levels.sort_by(f64::total_cmp); + levels.dedup_by(|left, right| (*left - *right).abs() <= accuracy.geometric); + levels +} + +fn cap( + facets: &mut PlanarFacets, + base: Frame3, + level: f64, + up: bool, + regions: &[RingRegion], + vertices: &[Pt2], + source_breps: &[(&BrepEnvelope, FaceRole)], +) -> Result<(), GeometryError> { + let origin = base.point([0.0, 0.0, level]); + let frame = if up { + Frame3 { origin, ..base } + } else { + Frame3 { + origin, + x: base.x, + y: scale(base.y, -1.0), + z: scale(base.z, -1.0), + } + }; + for region in regions { + let sample = region_sample(region)?; + let mut selected = None; + for &(brep, role) in source_breps { + let sources = cap_sources( + brep, + base.point([sample.x, sample.z, level]), + frame.z, + role == FaceRole::Cut, + facets.accuracy.intersection, + )?; + if !sources.is_empty() { + selected = Some((sources, role)); + break; + } + } + let (sources, role) = selected.ok_or_else(|| { + GeometryError::InvalidTopology(format!( + "layered planar cap has no source face at level {level}" + )) + })?; + let mut outer = split_ring(®ion.outer, vertices, facets.accuracy.geometric / 4.0) + .into_iter() + .map(|point| base.point([point.x, point.z, level])) + .collect::>(); + let mut holes = region + .holes + .iter() + .map(|ring| { + split_ring(ring, vertices, facets.accuracy.geometric / 4.0) + .into_iter() + .map(|point| base.point([point.x, point.z, level])) + .collect::>() + }) + .collect::>(); + if !up { + outer.reverse(); + for hole in &mut holes { + hole.reverse(); + } + } + facets.face( + if up { "upper-cap" } else { "lower-cap" }, + frame, + outer, + holes, + FaceProvenance { + sources, + role, + reversed: role == FaceRole::Cut, + }, + )?; + } + Ok(()) +} + +fn region_sample(region: &RingRegion) -> Result { + let mut coordinates = Vec::new(); + let mut holes = Vec::new(); + for point in ®ion.outer { + coordinates.extend([point.x, point.z]); + } + for hole in ®ion.holes { + holes.push(coordinates.len() / 2); + for point in hole { + coordinates.extend([point.x, point.z]); + } + } + let triangles = earcutr::earcut(&coordinates, &holes, 2); + let triangle = triangles + .chunks_exact(3) + .max_by(|left, right| { + let area = |triangle: &[usize]| { + let point = + |index: usize| Pt2::new(coordinates[index * 2], coordinates[index * 2 + 1]); + let a = point(triangle[0]); + let b = point(triangle[1]); + let c = point(triangle[2]); + ((b.x - a.x) * (c.z - a.z) - (b.z - a.z) * (c.x - a.x)).abs() + }; + area(left).total_cmp(&area(right)) + }) + .ok_or_else(coverage)?; + Ok(Pt2::new( + triangle + .iter() + .map(|&index| coordinates[index * 2]) + .sum::() + / 3.0, + triangle + .iter() + .map(|&index| coordinates[index * 2 + 1]) + .sum::() + / 3.0, + )) +} + +fn cap_sources( + brep: &BrepEnvelope, + point: Point3, + outward: Point3, + cut: bool, + tolerance: f64, +) -> Result, GeometryError> { + let mut sources = Vec::new(); + for face in &brep.topology.faces { + let SurfaceGeometry::Plane { frame } = brep + .geometry + .surfaces + .get(face.surface as usize) + .ok_or_else(coverage)? + else { + return Err(coverage()); + }; + let local = frame.local(point); + if local[2].abs() > tolerance { + continue; + } + let actual = scale(frame.z, face.sense.multiplier()); + let alignment = dot(actual, outward); + if (cut && alignment > -1.0 + 1.0e-10) || (!cut && alignment < 1.0 - 1.0e-10) { + continue; + } + if face_contains_uv(brep, face, [local[0], local[1]])? == Some(true) { + sources.push(brep_face_source(brep, face.id)); + } + } + Ok(sources) +} + +fn split_ring(ring: &[Pt2], vertices: &[Pt2], tolerance: f64) -> Vec { + let mut split = Vec::new(); + for index in 0..ring.len() { + let from = ring[index]; + let to = ring[(index + 1) % ring.len()]; + let delta = Pt2::new(to.x - from.x, to.z - from.z); + let length2 = delta.x * delta.x + delta.z * delta.z; + if length2 <= tolerance * tolerance { + continue; + } + split.push(from); + let mut interior = Vec::new(); + for &point in vertices { + let offset = Pt2::new(point.x - from.x, point.z - from.z); + let t = (offset.x * delta.x + offset.z * delta.z) / length2; + let distance = (offset.x * delta.z - offset.z * delta.x).abs() / length2.sqrt(); + if t > tolerance / length2.sqrt() + && t < 1.0 - tolerance / length2.sqrt() + && distance <= tolerance + { + interior.push((t, point)); + } + } + interior.sort_by(|left, right| left.0.total_cmp(&right.0)); + for (_, point) in interior { + if split + .last() + .is_none_or(|last| (last.x - point.x).hypot(last.z - point.z) > tolerance) + { + split.push(point); + } + } + } + split +} + +fn add_layer_faces( + facets: &mut PlanarFacets, + frame: Frame3, + levels: &[f64], + layers: &[Vec], + boundaries: &LayerBoundaries, + host: &BrepEnvelope, + cutter: &BrepEnvelope, +) -> Result<(), GeometryError> { + for (index, layer) in layers.iter().enumerate() { + side_faces( + facets, + host, + cutter, + &LayerSlab { + base: frame, + regions: layer, + vertices: &boundaries.vertices, + lo: levels[index], + hi: levels[index + 1], + }, + )?; + if index + 1 < layers.len() { + cap( + facets, + frame, + levels[index + 1], + true, + &boundaries.exposed_up[index], + &boundaries.vertices, + &[(host, FaceRole::Split), (cutter, FaceRole::Cut)], + )?; + cap( + facets, + frame, + levels[index + 1], + false, + &boundaries.exposed_down[index], + &boundaries.vertices, + &[(host, FaceRole::Split), (cutter, FaceRole::Cut)], + )?; + } + } + Ok(()) +} + +fn side_faces( + facets: &mut PlanarFacets, + a: &BrepEnvelope, + b: &BrepEnvelope, + slab: &LayerSlab<'_>, +) -> Result<(), GeometryError> { + let LayerSlab { + base, + regions, + vertices, + lo, + hi, + } = *slab; + for region in regions { + for ring in std::iter::once(®ion.outer).chain(®ion.holes) { + let ring = split_ring(ring, vertices, facets.accuracy.geometric / 4.0); + for index in 0..ring.len() { + let from = ring[index]; + let to = ring[(index + 1) % ring.len()]; + let lower_from = base.point([from.x, from.z, lo]); + let lower_to = base.point([to.x, to.z, lo]); + let upper_to = base.point([to.x, to.z, hi]); + let upper_from = base.point([from.x, from.z, hi]); + let normal = unit(cross( + sub(lower_to, lower_from), + sub(upper_from, lower_from), + ))?; + let face_frame = Frame3::from_axis(lower_from, normal, sub(lower_to, lower_from))?; + let midpoint = scale( + [ + lower_from[0] + lower_to[0] + upper_to[0] + upper_from[0], + lower_from[1] + lower_to[1] + upper_to[1] + upper_from[1], + lower_from[2] + lower_to[2] + upper_to[2] + upper_from[2], + ], + 0.25, + ); + let a_sources = side_sources(a, midpoint, normal, facets.accuracy.intersection)?; + let b_sources = side_sources(b, midpoint, normal, facets.accuracy.intersection)?; + let is_cut = a_sources.is_empty() && !b_sources.is_empty(); + let sources = if is_cut { b_sources } else { a_sources }; + if sources.is_empty() { + return Err(GeometryError::InvalidTopology( + "layered planar side has no source face".into(), + )); + } + facets.face( + if is_cut { "cut-side" } else { "host-side" }, + face_frame, + vec![lower_from, lower_to, upper_to, upper_from], + Vec::new(), + FaceProvenance { + sources, + role: if is_cut { + FaceRole::Cut + } else { + FaceRole::Split + }, + reversed: is_cut, + }, + )?; + } + } + } + Ok(()) +} + +fn side_sources( + brep: &BrepEnvelope, + point: Point3, + outward: Point3, + tolerance: f64, +) -> Result, GeometryError> { + let mut sources = Vec::new(); + for face in &brep.topology.faces { + let SurfaceGeometry::Plane { frame } = brep + .geometry + .surfaces + .get(face.surface as usize) + .ok_or_else(coverage)? + else { + return Err(coverage()); + }; + let local = frame.local(point); + if local[2].abs() > tolerance || dot(frame.z, outward).abs() < 1.0 - 1.0e-10 { + continue; + } + if face_contains_uv(brep, face, [local[0], local[1]])? == Some(true) { + sources.push(brep_face_source(brep, face.id)); + } + } + Ok(sources) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/planar/layered_extrusions/regions.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/layered_extrusions/regions.rs new file mode 100644 index 0000000..8718cd1 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/layered_extrusions/regions.rs @@ -0,0 +1,214 @@ +use crate::brep::{Accuracy, BrepEnvelope, Frame3, GeometryError, SurfaceGeometry}; +use crate::geom2d::{boolean_oriented_regions, PlanarBooleanOp, Pt2, RingRegion}; +use crate::math::{cross, dot, scale, sub}; +use crate::operations::modifying::boolean::handlers::planar::facets::{coverage, loop_positions}; +use crate::operations::modifying::boolean::operands::PrismaticInput; + +pub(super) struct LayerBoundaries { + pub(super) exposed_up: Vec>, + pub(super) exposed_down: Vec>, + pub(super) vertices: Vec, +} + +pub(super) fn contours(input: &PrismaticInput<'_>, frame: Frame3) -> Vec> { + input + .contours + .iter() + .map(|ring| { + ring.iter() + .map(|point| { + let local = frame.local(*point); + Pt2::new(local[0], local[1]) + }) + .collect() + }) + .collect() +} + +pub(super) fn layer_regions( + host: &BrepEnvelope, + frame: Frame3, + levels: &[f64], + b_contours: &[Vec], + b_lo: f64, + b_hi: f64, + accuracy: Accuracy, +) -> Result>, GeometryError> { + levels + .windows(2) + .map(|span| -> Result, GeometryError> { + let middle = (span[0] + span[1]) / 2.0; + let host_regions = slice_regions(host, frame, middle, accuracy.intersection)?; + Ok(boolean_oriented_regions( + ®ion_contours(&host_regions), + if middle > b_lo && middle < b_hi { + b_contours + } else { + &[] + }, + PlanarBooleanOp::Subtraction, + accuracy.intersection, + )) + }) + .collect::, _>>() +} + +fn slice_regions( + brep: &BrepEnvelope, + frame: Frame3, + level: f64, + tolerance: f64, +) -> Result, GeometryError> { + let segments = slice_segments(brep, frame, level, tolerance)?; + let loops = segment_loops(segments, brep, tolerance)?; + Ok(boolean_oriented_regions( + &loops, + &[], + PlanarBooleanOp::Union, + tolerance, + )) +} + +fn slice_segments( + brep: &BrepEnvelope, + frame: Frame3, + level: f64, + tolerance: f64, +) -> Result, GeometryError> { + let mut segments = Vec::<(Pt2, Pt2)>::new(); + for face in &brep.topology.faces { + let SurfaceGeometry::Plane { frame: face_frame } = brep + .geometry + .surfaces + .get(face.surface as usize) + .ok_or_else(coverage)? + else { + return Err(coverage()); + }; + if dot(face_frame.z, frame.z).abs() > 1.0e-10 { + continue; + } + if !face.trim.holes.is_empty() { + return Err(coverage()); + } + let points = loop_positions(brep, face.trim.outer)?; + let mut crossings = Vec::::new(); + for index in 0..points.len() { + let from = frame.local(points[index]); + let to = frame.local(points[(index + 1) % points.len()]); + if (from[2] < level && to[2] > level) || (from[2] > level && to[2] < level) { + let fraction = (level - from[2]) / (to[2] - from[2]); + let point = Pt2::new( + from[0] + fraction * (to[0] - from[0]), + from[1] + fraction * (to[1] - from[1]), + ); + if crossings + .iter() + .all(|existing| (existing.x - point.x).hypot(existing.z - point.z) > tolerance) + { + crossings.push(point); + } + } + } + if crossings.is_empty() { + continue; + } + if crossings.len() != 2 { + return Err(coverage()); + } + let mut from = crossings[0]; + let mut to = crossings[1]; + let edge = sub( + frame.point([to.x, to.z, level]), + frame.point([from.x, from.z, level]), + ); + let outward = cross(edge, frame.z); + let actual = scale(face_frame.z, face.sense.multiplier()); + if dot(outward, actual) < 0.0 { + std::mem::swap(&mut from, &mut to); + } + segments.push((from, to)); + } + Ok(segments) +} + +fn segment_loops( + mut segments: Vec<(Pt2, Pt2)>, + brep: &BrepEnvelope, + tolerance: f64, +) -> Result>, GeometryError> { + let mut loops = Vec::new(); + while let Some((from, mut to)) = segments.pop() { + let mut ring = vec![from]; + while (to.x - from.x).hypot(to.z - from.z) > tolerance { + ring.push(to); + let Some(next) = segments + .iter() + .position(|(start, _)| (start.x - to.x).hypot(start.z - to.z) <= tolerance) + else { + return Err(coverage()); + }; + let (_, end) = segments.swap_remove(next); + to = end; + if ring.len() > brep.topology.halfedges.len() { + return Err(coverage()); + } + } + if ring.len() < 3 { + return Err(coverage()); + } + loops.push(ring); + } + Ok(loops) +} + +fn region_contours(regions: &[RingRegion]) -> Vec> { + regions + .iter() + .flat_map(|region| { + std::iter::once(region.outer.clone()).chain(region.holes.iter().cloned()) + }) + .collect() +} + +pub(super) fn layer_boundaries(layers: &[Vec], accuracy: Accuracy) -> LayerBoundaries { + let mut exposed_up = Vec::new(); + let mut exposed_down = Vec::new(); + for index in 1..layers.len() { + let below = region_contours(&layers[index - 1]); + let above = region_contours(&layers[index]); + exposed_up.push(boolean_oriented_regions( + &below, + &above, + PlanarBooleanOp::Subtraction, + accuracy.intersection, + )); + exposed_down.push(boolean_oriented_regions( + &above, + &below, + PlanarBooleanOp::Subtraction, + accuracy.intersection, + )); + } + let mut vertices = Vec::new(); + for layer in layers { + region_points(layer, &mut vertices); + } + for regions in exposed_up.iter().chain(&exposed_down) { + region_points(regions, &mut vertices); + } + LayerBoundaries { + exposed_up, + exposed_down, + vertices, + } +} + +fn region_points(regions: &[RingRegion], out: &mut Vec) { + for region in regions { + out.extend(region.outer.iter().copied()); + for hole in ®ion.holes { + out.extend(hole.iter().copied()); + } + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/planar/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/mod.rs new file mode 100644 index 0000000..bba4a9f --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/mod.rs @@ -0,0 +1,6 @@ +mod facets; +mod layered_extrusions; +mod polyhedra; + +pub(crate) use layered_extrusions::subtract_layered_extrusions; +pub(crate) use polyhedra::subtract_planar_polyhedra; diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/planar/polyhedra/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/polyhedra/mod.rs new file mode 100644 index 0000000..1361c33 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/polyhedra/mod.rs @@ -0,0 +1,323 @@ +mod sections; + +use super::facets::{coverage, finish, PlanarFacets}; +use crate::brep::{Accuracy, BrepEnvelope, FaceProvenance, FaceRole, Frame3, GeometryError}; +use crate::geom2d::{ + boolean_oriented_regions, regions_from_edges_by, winding_number2, PlanarBooleanOp, Pt2, + RingRegion, +}; +use crate::math::{dot, scale, sub, Point3}; +use crate::operations::modifying::boolean::operands::brep_face_source; +use crate::operations::modifying::boolean::types::BooleanResult; +use crate::query::{classify_point, PointClassification}; +use sections::{ + convex_faces, convex_section_polygon, planar_faces, planar_section_segments, split_planar_ring, + PlanarFace, PlanarFacet, +}; +use std::cell::RefCell; + +pub(crate) fn subtract_planar_polyhedra( + host: &BrepEnvelope, + cutter: &BrepEnvelope, + id: String, +) -> Result { + let accuracy = Accuracy::combined(host.accuracy, cutter.accuracy); + let Some(host_faces) = planar_faces(host, accuracy.intersection)? else { + return Err(coverage()); + }; + let Some(cutter_faces) = planar_faces(cutter, accuracy.intersection)? else { + return Err(coverage()); + }; + let convex_cutter_faces = convex_faces(cutter, accuracy.intersection)?; + check_not_nested(host, cutter)?; + let mut pieces = Vec::new(); + for face in &host_faces { + let mut base = vec![local_ring(&face.polygon, face.frame)]; + base.extend(face.holes.iter().map(|ring| local_ring(ring, face.frame))); + let regions = if let Some(convex_faces) = &convex_cutter_faces { + convex_host_regions(cutter, convex_faces, face, &base, accuracy) + } else { + classified_host_regions(cutter, &cutter_faces, face, &base, accuracy)? + }; + for region in regions { + pieces.push(host_piece(host, face, ®ion)); + } + } + for face in &cutter_faces { + let regions = cutter_face_regions(host, &host_faces, face, accuracy)?; + let reversed = Frame3 { + origin: face.frame.origin, + x: face.frame.x, + y: scale(face.frame.y, -1.0), + z: scale(face.frame.z, -1.0), + }; + for region in regions { + pieces.push(cutter_piece(cutter, face, reversed, ®ion)); + } + } + if pieces.is_empty() { + return Err(coverage()); + } + let vertices = pieces + .iter() + .flat_map(|piece| { + piece + .outer + .iter() + .chain(piece.holes.iter().flatten()) + .copied() + }) + .collect::>(); + let mut facets = PlanarFacets::new(id, accuracy)?; + for piece in pieces { + let outer = split_planar_ring(&piece.outer, &vertices, accuracy.geometric / 4.0); + let holes = piece + .holes + .iter() + .map(|ring| split_planar_ring(ring, &vertices, accuracy.geometric / 4.0)) + .collect(); + facets.face(&piece.key, piece.frame, outer, holes, piece.provenance)?; + } + let mut result = finish(facets, host, cutter)?; + result.report.coincident = false; + Ok(result) +} + +fn check_not_nested(host: &BrepEnvelope, cutter: &BrepEnvelope) -> Result<(), GeometryError> { + if cutter.topology.vertices.iter().all(|vertex| { + matches!( + classify_point(host, vertex.position), + Ok(PointClassification::Inside) + ) + }) || host.topology.vertices.iter().all(|vertex| { + matches!( + classify_point(cutter, vertex.position), + Ok(PointClassification::Inside) + ) + }) { + return Err(coverage()); + } + Ok(()) +} + +fn local_ring(points: &[Point3], frame: Frame3) -> Vec { + points + .iter() + .map(|point| { + let local = frame.local(*point); + Pt2::new(local[0], local[1]) + }) + .collect::>() +} + +fn convex_host_regions( + cutter: &BrepEnvelope, + convex_faces: &[PlanarFace], + face: &PlanarFace, + base: &[Vec], + accuracy: Accuracy, +) -> Vec { + let section = convex_section_polygon(cutter, convex_faces, face.frame, accuracy.intersection); + let cut = if section.is_empty() { + Vec::new() + } else { + vec![section] + }; + boolean_oriented_regions( + base, + &cut, + PlanarBooleanOp::Subtraction, + accuracy.intersection, + ) +} + +fn classified_host_regions( + cutter: &BrepEnvelope, + cutter_faces: &[PlanarFace], + face: &PlanarFace, + base: &[Vec], + accuracy: Accuracy, +) -> Result, GeometryError> { + let mut edges = + planar_section_segments(cutter, cutter_faces, face.frame, accuracy.intersection)?; + let coplanar = coplanar_rings(cutter_faces, face, accuracy); + for ring in base.iter().chain(&coplanar) { + edges.extend( + ring.iter() + .enumerate() + .map(|(index, &point)| (point, ring[(index + 1) % ring.len()])), + ); + } + let failure = RefCell::new(None); + let regions = regions_from_edges_by(&edges, accuracy.intersection, |point| { + if base + .iter() + .map(|ring| winding_number2(point, ring)) + .sum::() + == 0 + { + return false; + } + let position = face.frame.point([point.x, point.z, 0.0]); + let classification = classify_point(cutter, position); + let classification = match classification { + Ok(PointClassification::Boundary) => classify_point( + cutter, + sub( + position, + scale( + face.frame.z, + accuracy.geometric.max(accuracy.intersection) * 4.0, + ), + ), + ), + other => other, + }; + match classification { + Ok(PointClassification::Outside) => true, + Ok(PointClassification::Inside) => false, + Ok(PointClassification::Boundary) | Ok(PointClassification::Unknown) => { + *failure.borrow_mut() = Some(coverage()); + false + } + Err(error) => { + *failure.borrow_mut() = Some(error); + false + } + } + }); + if let Some(error) = failure.into_inner() { + return Err(error); + } + Ok(regions) +} + +fn coplanar_rings(faces: &[PlanarFace], face: &PlanarFace, accuracy: Accuracy) -> Vec> { + faces + .iter() + .filter(|other| { + dot(other.frame.z, face.frame.z).abs() >= 1.0 - 1.0e-10 + && dot(other.frame.z, sub(face.frame.origin, other.frame.origin)).abs() + <= accuracy.intersection + }) + .flat_map(|other| { + std::iter::once(&other.polygon) + .chain(other.holes.iter()) + .map(|ring| local_ring(ring, face.frame)) + }) + .collect::>() +} + +fn host_piece(host: &BrepEnvelope, face: &PlanarFace, region: &RingRegion) -> PlanarFacet { + PlanarFacet { + key: format!("planar-host-{}", face.id), + frame: face.frame, + outer: region + .outer + .iter() + .map(|point| face.frame.point([point.x, point.z, 0.0])) + .collect(), + holes: region + .holes + .iter() + .map(|ring| { + ring.iter() + .map(|point| face.frame.point([point.x, point.z, 0.0])) + .collect() + }) + .collect(), + provenance: FaceProvenance { + sources: vec![brep_face_source(host, face.id)], + role: FaceRole::Split, + reversed: false, + }, + } +} + +fn cutter_face_regions( + host: &BrepEnvelope, + host_faces: &[PlanarFace], + face: &PlanarFace, + accuracy: Accuracy, +) -> Result, GeometryError> { + let mut edges = planar_section_segments(host, host_faces, face.frame, accuracy.intersection)?; + let coplanar = coplanar_rings(host_faces, face, accuracy); + let mut cutter_region = vec![local_ring(&face.polygon, face.frame)]; + cutter_region.extend(face.holes.iter().map(|ring| local_ring(ring, face.frame))); + for ring in cutter_region.iter().chain(&coplanar) { + edges.extend( + ring.iter() + .enumerate() + .map(|(index, &point)| (point, ring[(index + 1) % ring.len()])), + ); + } + let failure = RefCell::new(None); + let regions = regions_from_edges_by(&edges, accuracy.intersection, |point| { + if cutter_region + .iter() + .map(|ring| winding_number2(point, ring)) + .sum::() + == 0 + || coplanar + .iter() + .map(|ring| winding_number2(point, ring)) + .sum::() + != 0 + { + return false; + } + match classify_point(host, face.frame.point([point.x, point.z, 0.0])) { + Ok(PointClassification::Inside) | Ok(PointClassification::Boundary) => true, + Ok(PointClassification::Outside) => false, + Ok(PointClassification::Unknown) => { + *failure.borrow_mut() = Some(coverage()); + false + } + Err(error) => { + *failure.borrow_mut() = Some(error); + false + } + } + }); + if let Some(error) = failure.into_inner() { + return Err(error); + } + Ok(regions) +} + +fn cutter_piece( + cutter: &BrepEnvelope, + face: &PlanarFace, + reversed: Frame3, + region: &RingRegion, +) -> PlanarFacet { + let mut outer = region + .outer + .iter() + .map(|point| face.frame.point([point.x, point.z, 0.0])) + .collect::>(); + outer.reverse(); + let holes = region + .holes + .iter() + .map(|ring| { + let mut points = ring + .iter() + .map(|point| face.frame.point([point.x, point.z, 0.0])) + .collect::>(); + points.reverse(); + points + }) + .collect(); + PlanarFacet { + key: format!("planar-cut-{}", face.id), + frame: reversed, + outer, + holes, + provenance: FaceProvenance { + sources: vec![brep_face_source(cutter, face.id)], + role: FaceRole::Cut, + reversed: true, + }, + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/planar/polyhedra/sections.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/polyhedra/sections.rs new file mode 100644 index 0000000..bdbd908 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/planar/polyhedra/sections.rs @@ -0,0 +1,364 @@ +use crate::brep::{ + unit, BrepEnvelope, CurveGeometry, FaceProvenance, Frame3, GeometryError, Orientation, + SurfaceGeometry, +}; +use crate::geom2d::Pt2; +use crate::math::{cross, dot, norm, scale, sub, Point3}; +use crate::operations::modifying::boolean::handlers::planar::facets::{coverage, loop_positions}; +use crate::query::face_contains_uv; + +pub(super) struct PlanarFace { + pub(super) id: u32, + pub(super) frame: Frame3, + pub(super) polygon: Vec, + pub(super) holes: Vec>, +} + +pub(super) struct PlanarFacet { + pub(super) key: String, + pub(super) frame: Frame3, + pub(super) outer: Vec, + pub(super) holes: Vec>, + pub(super) provenance: FaceProvenance, +} + +pub(super) fn convex_faces( + brep: &BrepEnvelope, + tolerance: f64, +) -> Result>, GeometryError> { + let Some(faces) = planar_faces(brep, tolerance)? else { + return Ok(None); + }; + if brep.solids.len() != 1 + || brep.topology.shells.len() != 1 + || faces.iter().any(|face| !face.holes.is_empty()) + { + return Ok(None); + } + for face in &faces { + if brep + .topology + .vertices + .iter() + .any(|vertex| dot(face.frame.z, sub(vertex.position, face.frame.origin)) > tolerance) + { + return Ok(None); + } + } + Ok(Some(faces)) +} + +pub(super) fn planar_faces( + brep: &BrepEnvelope, + tolerance: f64, +) -> Result>, GeometryError> { + if brep.solids.is_empty() + || brep + .geometry + .curves + .iter() + .any(|curve| !matches!(curve, CurveGeometry::Line { .. })) + { + return Ok(None); + } + let mut faces = Vec::with_capacity(brep.topology.faces.len()); + for face in &brep.topology.faces { + let Some(SurfaceGeometry::Plane { frame: surface }) = + brep.geometry.surfaces.get(face.surface as usize) + else { + return Ok(None); + }; + let frame = if face.sense == Orientation::Forward { + *surface + } else { + Frame3 { + origin: surface.origin, + x: surface.x, + y: scale(surface.y, -1.0), + z: scale(surface.z, -1.0), + } + }; + let polygon = loop_positions(brep, face.trim.outer)?; + if polygon.len() < 3 { + return Ok(None); + } + let mut holes = face + .trim + .holes + .iter() + .map(|&loop_id| loop_positions(brep, loop_id)) + .collect::, _>>()?; + let orient = |mut ring: Vec, outer: bool| -> Option> { + let area = ring + .iter() + .enumerate() + .map(|(index, point)| { + let a = frame.local(*point); + let b = frame.local(ring[(index + 1) % ring.len()]); + a[0] * b[1] - b[0] * a[1] + }) + .sum::(); + if ring.len() < 3 || area.abs() <= tolerance * tolerance { + return None; + } + if (area > 0.0) != outer { + ring.reverse(); + } + Some(ring) + }; + let Some(polygon) = orient(polygon, true) else { + return Ok(None); + }; + for hole in &mut holes { + let Some(oriented) = orient(std::mem::take(hole), false) else { + return Ok(None); + }; + *hole = oriented; + } + faces.push(PlanarFace { + id: face.id, + frame, + polygon, + holes, + }); + } + Ok(Some(faces)) +} + +pub(super) fn planar_section_segments( + brep: &BrepEnvelope, + faces: &[PlanarFace], + section: Frame3, + tolerance: f64, +) -> Result, GeometryError> { + let mut segments = Vec::<(Pt2, Pt2)>::new(); + for source in faces { + let direction = cross(source.frame.z, section.z); + if norm(direction) <= 1.0e-10 { + continue; + } + let direction = unit(direction)?; + let crossings = section_crossings(source, section, direction, tolerance); + let face = &brep.topology.faces[source.id as usize]; + let SurfaceGeometry::Plane { frame: surface } = + &brep.geometry.surfaces[face.surface as usize] + else { + return Err(coverage()); + }; + for pair in crossings.windows(2) { + if norm(sub(pair[0], pair[1])) <= tolerance { + continue; + } + let midpoint = scale( + [ + pair[0][0] + pair[1][0], + pair[0][1] + pair[1][1], + pair[0][2] + pair[1][2], + ], + 0.5, + ); + let local = surface.local(midpoint); + let on_trim_edge = on_source_trim_edge(source, midpoint, tolerance); + if face_contains_uv(brep, face, [local[0], local[1]])? != Some(true) && !on_trim_edge { + continue; + } + let a = section.local(pair[0]); + let b = section.local(pair[1]); + let mut from = Pt2::new(a[0], a[1]); + let mut to = Pt2::new(b[0], b[1]); + let edge = sub(pair[1], pair[0]); + if dot(cross(edge, section.z), source.frame.z) < 0.0 { + std::mem::swap(&mut from, &mut to); + } + segments.push((from, to)); + } + } + Ok(segments) +} + +fn section_crossings( + source: &PlanarFace, + section: Frame3, + direction: Point3, + tolerance: f64, +) -> Vec { + let mut crossings = Vec::new(); + for ring in std::iter::once(&source.polygon).chain(&source.holes) { + for index in 0..ring.len() { + let from = ring[index]; + let to = ring[(index + 1) % ring.len()]; + let from_distance = dot(section.z, sub(from, section.origin)); + let to_distance = dot(section.z, sub(to, section.origin)); + if from_distance.abs() <= tolerance { + crossings.push(from); + } + if (from_distance < -tolerance && to_distance > tolerance) + || (from_distance > tolerance && to_distance < -tolerance) + { + let fraction = from_distance / (from_distance - to_distance); + crossings.push([ + from[0] + fraction * (to[0] - from[0]), + from[1] + fraction * (to[1] - from[1]), + from[2] + fraction * (to[2] - from[2]), + ]); + } + } + } + crossings.sort_by(|left, right| dot(*left, direction).total_cmp(&dot(*right, direction))); + crossings.dedup_by(|left, right| norm(sub(*left, *right)) <= tolerance); + crossings +} + +fn on_source_trim_edge(source: &PlanarFace, midpoint: Point3, tolerance: f64) -> bool { + std::iter::once(&source.polygon) + .chain(&source.holes) + .any(|ring| { + ring.iter().enumerate().any(|(index, &from)| { + let to = ring[(index + 1) % ring.len()]; + let edge = sub(to, from); + let length_squared = dot(edge, edge); + if length_squared <= tolerance * tolerance { + return false; + } + let fraction = dot(sub(midpoint, from), edge) / length_squared; + fraction >= -tolerance + && fraction <= 1.0 + tolerance + && norm(sub(sub(midpoint, from), scale(edge, fraction))) <= tolerance + }) + }) +} + +pub(super) fn convex_section_polygon( + cutter: &BrepEnvelope, + faces: &[PlanarFace], + section: Frame3, + tolerance: f64, +) -> Vec { + let mut limits = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; + for vertex in &cutter.topology.vertices { + let local = section.local(vertex.position); + for axis in 0..2 { + limits[axis][0] = limits[axis][0].min(local[axis]); + limits[axis][1] = limits[axis][1].max(local[axis]); + } + } + if limits.iter().any(|bound| !bound[0].is_finite()) { + return Vec::new(); + } + let span = (limits[0][1] - limits[0][0]) + .max(limits[1][1] - limits[1][0]) + .max(1.0); + let corners = [ + [limits[0][0] - span, limits[1][0] - span], + [limits[0][1] + span, limits[1][0] - span], + [limits[0][1] + span, limits[1][1] + span], + [limits[0][0] - span, limits[1][1] + span], + ] + .map(|point| section.point([point[0], point[1], 0.0])); + let clipped = clip_convex_face(&corners, faces, tolerance); + if clipped.len() < 3 { + return Vec::new(); + } + let result = clipped + .iter() + .map(|point| { + let local = section.local(*point); + Pt2::new(local[0], local[1]) + }) + .collect::>(); + let twice_area = result + .iter() + .enumerate() + .map(|(index, point)| { + let next = result[(index + 1) % result.len()]; + point.x * next.z - next.x * point.z + }) + .sum::(); + if twice_area.abs() <= tolerance * tolerance { + Vec::new() + } else { + result + } +} + +fn clip_convex_face(polygon: &[Point3], planes: &[PlanarFace], tolerance: f64) -> Vec { + let mut result = polygon.to_vec(); + for plane in planes { + if result.len() < 3 { + return Vec::new(); + } + let mut next = Vec::new(); + let Some(&mut_previous) = result.last() else { + return Vec::new(); + }; + let mut previous = mut_previous; + let mut previous_distance = dot(plane.frame.z, sub(previous, plane.frame.origin)); + for ¤t in &result { + let distance = dot(plane.frame.z, sub(current, plane.frame.origin)); + let previous_inside = previous_distance <= tolerance; + let current_inside = distance <= tolerance; + if previous_inside != current_inside { + let fraction = previous_distance / (previous_distance - distance); + next.push([ + previous[0] + fraction * (current[0] - previous[0]), + previous[1] + fraction * (current[1] - previous[1]), + previous[2] + fraction * (current[2] - previous[2]), + ]); + } + if current_inside { + next.push(current); + } + previous = current; + previous_distance = distance; + } + next.dedup_by(|left, right| norm(sub(*left, *right)) <= tolerance / 4.0); + if next.len() > 1 && norm(sub(next[0], next[next.len() - 1])) <= tolerance / 4.0 { + next.pop(); + } + result = next; + } + if result.len() < 3 { + Vec::new() + } else { + result + } +} + +pub(super) fn split_planar_ring( + ring: &[Point3], + vertices: &[Point3], + tolerance: f64, +) -> Vec { + let mut split = Vec::new(); + for index in 0..ring.len() { + let from = ring[index]; + let to = ring[(index + 1) % ring.len()]; + let delta = sub(to, from); + let length = norm(delta); + if length <= tolerance { + continue; + } + split.push(from); + let mut interior = Vec::new(); + for &point in vertices { + let offset = sub(point, from); + let fraction = dot(offset, delta) / (length * length); + if fraction > tolerance / length + && fraction < 1.0 - tolerance / length + && norm(sub(offset, scale(delta, fraction))) <= tolerance + { + interior.push((fraction, point)); + } + } + interior.sort_by(|left, right| left.0.total_cmp(&right.0)); + for (_, point) in interior { + if split + .last() + .is_none_or(|last| norm(sub(*last, point)) > tolerance) + { + split.push(point); + } + } + } + split +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/planar_extrusions.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/planar_extrusions.rs new file mode 100644 index 0000000..a966a05 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/planar_extrusions.rs @@ -0,0 +1,417 @@ +use crate::brep::{Accuracy, BrepEnvelope, FaceProvenance, FaceRole, FaceSource, GeometryError}; +use crate::geom2d::{boolean_oriented_regions, PlanarBooleanOp, Pt2, RingRegion}; +use crate::math::{dot, sub, Point3}; +use crate::operations::modifying::boolean::assembly::{ + append_analytic_input, finish_analytic_result, +}; +use crate::operations::modifying::boolean::operands::{ + brep_face_source, full_planar_extrusion, prismatic_face_provenance, prismatic_gap, + prismatic_profile_sources, profiles_match, region_profiles, unique_sources, + PrismaticAxialSegment, PrismaticInput, +}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use crate::primitives; + +struct AxialProfile<'a> { + a_contours: &'a [Vec], + a_span: [f64; 2], + b_span: [f64; 2], +} + +pub(crate) fn boolean_planar_extrusions( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let a = full_planar_extrusion(a)?; + let b = full_planar_extrusion(b)?; + let accuracy = Accuracy::combined(a.brep.accuracy, b.brep.accuracy); + if dot(a.frame.z, b.frame.z) < 1.0 - 1.0e-12 { + return Err(prismatic_gap()); + } + let to_common = |contours: &[Vec]| { + contours + .iter() + .map(|ring| { + ring.iter() + .map(|point| { + let local = a.frame.local(*point); + Pt2::new(local[0], local[1]) + }) + .collect::>() + }) + .collect::>() + }; + let a_contours = to_common(&a.contours); + let b_contours = to_common(&b.contours); + let b_lo = dot(sub(b.frame.origin, a.frame.origin), a.frame.z); + let b_hi = b_lo + b.height; + if b.contours + .iter() + .flatten() + .any(|point| (a.frame.local(*point)[2] - b_lo).abs() > accuracy.geometric) + { + return Err(prismatic_gap()); + } + let a_span = [0.0, a.height]; + let b_span = [b_lo, b_hi]; + let coextensive = + b_lo.abs() <= accuracy.geometric && (a.height - b_hi).abs() <= accuracy.geometric; + if !coextensive { + if !profiles_match(&a_contours, &b_contours, accuracy.geometric) { + return Err(prismatic_gap()); + } + return axial_profile_boolean( + &a, + &b, + &AxialProfile { + a_contours: &a_contours, + a_span, + b_span, + }, + operation, + id, + accuracy, + ); + } + let planar_operation = match operation { + BooleanOp::Union => PlanarBooleanOp::Union, + BooleanOp::Intersection => PlanarBooleanOp::Intersection, + BooleanOp::Subtraction => PlanarBooleanOp::Subtraction, + }; + let regions = boolean_oriented_regions( + &a_contours, + &b_contours, + planar_operation, + accuracy.intersection, + ); + let out = region_solid(&a, &b, ®ions, operation, id, accuracy)?; + finish_analytic_result(out, a.brep, b.brep, true, operation, false) +} + +fn axial_profile_boolean( + a: &PrismaticInput<'_>, + b: &PrismaticInput<'_>, + profile: &AxialProfile<'_>, + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let AxialProfile { + a_contours, + a_span, + b_span, + } = *profile; + let a_caps = [brep_face_source(a.brep, 1), brep_face_source(a.brep, 0)]; + let b_caps = [brep_face_source(b.brep, 1), brep_face_source(b.brep, 0)]; + let overlap = a_span[1].min(b_span[1]) - a_span[0].max(b_span[0]); + if overlap != 0.0 && overlap.abs() <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "profile extrusion axial overlap or clearance is below geometric resolution".into(), + )); + } + let segments = axial_segments( + operation, overlap, a_span, b_span, &a_caps, &b_caps, accuracy, + ); + let out = axial_solid(a, b, a_contours, segments, id, accuracy)?; + finish_analytic_result(out, a.brep, b.brep, true, operation, overlap >= 0.0) +} + +fn axial_segments( + operation: BooleanOp, + overlap: f64, + a_span: [f64; 2], + b_span: [f64; 2], + a_caps: &[FaceSource; 2], + b_caps: &[FaceSource; 2], + accuracy: Accuracy, +) -> Vec { + let mut segments = Vec::new(); + match operation { + BooleanOp::Union if overlap >= 0.0 => { + let lo = a_span[0].min(b_span[0]); + let hi = a_span[1].max(b_span[1]); + segments.push(coincident_segment( + lo, hi, a_span, b_span, a_caps, b_caps, accuracy, + )); + } + BooleanOp::Union => { + for (span, caps, include_a_sides, include_b_sides) in [ + (a_span, a_caps.clone(), true, false), + (b_span, b_caps.clone(), false, true), + ] { + segments.push(PrismaticAxialSegment { + lo: span[0], + hi: span[1], + lower: preserved_cap(vec![caps[0].clone()]), + upper: preserved_cap(vec![caps[1].clone()]), + include_a_sides, + include_b_sides, + side_role: FaceRole::Preserved, + }); + } + } + BooleanOp::Intersection if overlap > 0.0 => { + let lo = a_span[0].max(b_span[0]); + let hi = a_span[1].min(b_span[1]); + segments.push(coincident_segment( + lo, hi, a_span, b_span, a_caps, b_caps, accuracy, + )); + } + BooleanOp::Intersection => {} + BooleanOp::Subtraction if overlap <= 0.0 => { + segments.push(PrismaticAxialSegment { + lo: a_span[0], + hi: a_span[1], + lower: preserved_cap(vec![a_caps[0].clone()]), + upper: preserved_cap(vec![a_caps[1].clone()]), + include_a_sides: true, + include_b_sides: false, + side_role: FaceRole::Preserved, + }); + } + BooleanOp::Subtraction => { + if b_span[0] > a_span[0] { + segments.push(segment_below_cutter(a_span, b_span, a_caps, b_caps)); + } + if b_span[1] < a_span[1] { + segments.push(segment_above_cutter(a_span, b_span, a_caps, b_caps)); + } + } + } + segments +} + +fn coincident_segment( + lo: f64, + hi: f64, + a_span: [f64; 2], + b_span: [f64; 2], + a_caps: &[FaceSource; 2], + b_caps: &[FaceSource; 2], + accuracy: Accuracy, +) -> PrismaticAxialSegment { + let lower = [ + cap_at(lo, a_span, a_caps, accuracy), + cap_at(lo, b_span, b_caps, accuracy), + ] + .into_iter() + .flatten() + .collect(); + let upper = [ + cap_at(hi, a_span, a_caps, accuracy), + cap_at(hi, b_span, b_caps, accuracy), + ] + .into_iter() + .flatten() + .collect(); + PrismaticAxialSegment { + lo, + hi, + lower: preserved_cap(lower), + upper: preserved_cap(upper), + include_a_sides: true, + include_b_sides: true, + side_role: FaceRole::Coincident, + } +} + +fn cap_at( + boundary: f64, + span: [f64; 2], + caps: &[FaceSource; 2], + accuracy: Accuracy, +) -> Option { + if (boundary - span[0]).abs() <= accuracy.geometric { + Some(caps[0].clone()) + } else if (boundary - span[1]).abs() <= accuracy.geometric { + Some(caps[1].clone()) + } else { + None + } +} + +fn preserved_cap(sources: Vec) -> FaceProvenance { + let role = if sources.len() > 1 { + FaceRole::Coincident + } else { + FaceRole::Preserved + }; + FaceProvenance { + sources, + role, + reversed: false, + } +} + +fn segment_below_cutter( + a_span: [f64; 2], + b_span: [f64; 2], + a_caps: &[FaceSource; 2], + b_caps: &[FaceSource; 2], +) -> PrismaticAxialSegment { + PrismaticAxialSegment { + lo: a_span[0], + hi: b_span[0].min(a_span[1]), + lower: preserved_cap(vec![a_caps[0].clone()]), + upper: FaceProvenance { + sources: vec![b_caps[0].clone()], + role: FaceRole::Cut, + reversed: true, + }, + include_a_sides: true, + include_b_sides: false, + side_role: FaceRole::Split, + } +} + +fn segment_above_cutter( + a_span: [f64; 2], + b_span: [f64; 2], + a_caps: &[FaceSource; 2], + b_caps: &[FaceSource; 2], +) -> PrismaticAxialSegment { + PrismaticAxialSegment { + lo: b_span[1].max(a_span[0]), + hi: a_span[1], + lower: FaceProvenance { + sources: vec![b_caps[1].clone()], + role: FaceRole::Cut, + reversed: true, + }, + upper: preserved_cap(vec![a_caps[1].clone()]), + include_a_sides: true, + include_b_sides: false, + side_role: FaceRole::Split, + } +} + +fn axial_solid( + a: &PrismaticInput<'_>, + b: &PrismaticInput<'_>, + a_contours: &[Vec], + segments: Vec, + id: String, + accuracy: Accuracy, +) -> Result { + let outer = a_contours[0] + .iter() + .map(|point| [point.x, point.z]) + .collect::>(); + let holes = a_contours[1..] + .iter() + .map(|ring| ring.iter().map(|point| [point.x, point.z]).collect()) + .collect::>>(); + let mut out = BrepEnvelope::new(id, accuracy)?; + for (index, segment) in segments.into_iter().enumerate() { + let mut frame = a.frame; + frame.origin = a.frame.point([0.0, 0.0, segment.lo]); + let part = primitives::linear_extrusion( + format!("{}:axial-region-{index}", out.id), + frame, + outer.clone(), + holes.clone(), + segment.hi - segment.lo, + accuracy, + )?; + let face_offset = out.topology.faces.len() as u32; + append_analytic_input(&mut out, &part)?; + out.topology.faces[face_offset as usize].provenance = segment.upper; + out.topology.faces[(face_offset + 1) as usize].provenance = segment.lower; + let mut local_face = 2_u32; + for ring in a_contours { + for edge in 0..ring.len() { + let from = ring[edge]; + let to = ring[(edge + 1) % ring.len()]; + let mut sources = Vec::new(); + if segment.include_a_sides { + sources.extend(prismatic_profile_sources( + a, + a.frame, + from, + to, + accuracy.geometric, + )); + } + if segment.include_b_sides { + sources.extend(prismatic_profile_sources( + b, + a.frame, + from, + to, + accuracy.geometric, + )); + } + let sources = unique_sources(sources); + if sources.is_empty() { + return Err(GeometryError::InvalidTopology( + "axial profile boolean produced a side without source ancestry".into(), + )); + } + out.topology.faces[(face_offset + local_face) as usize].provenance = + FaceProvenance { + sources, + role: segment.side_role, + reversed: false, + }; + local_face += 1; + } + } + } + Ok(out) +} + +fn region_solid( + a: &PrismaticInput<'_>, + b: &PrismaticInput<'_>, + regions: &[RingRegion], + operation: BooleanOp, + id: String, + accuracy: Accuracy, +) -> Result { + let mut out = BrepEnvelope::new(id, accuracy)?; + for (region_index, region) in regions.iter().enumerate() { + let (outer, holes) = region_profiles(region); + let part = primitives::linear_extrusion( + format!("{}:region-{region_index}", out.id), + a.frame, + outer, + holes, + a.height, + accuracy, + )?; + let face_offset = out.topology.faces.len() as u32; + append_analytic_input(&mut out, &part)?; + let cap_sources = if operation == BooleanOp::Subtraction { + [ + vec![brep_face_source(a.brep, 0)], + vec![brep_face_source(a.brep, 1)], + ] + } else { + [ + vec![brep_face_source(a.brep, 0), brep_face_source(b.brep, 0)], + vec![brep_face_source(a.brep, 1), brep_face_source(b.brep, 1)], + ] + }; + for local in 0..2 { + out.topology.faces[(face_offset + local) as usize].provenance = FaceProvenance { + sources: cap_sources[local as usize].clone(), + role: FaceRole::Split, + reversed: false, + }; + } + let mut local_face = 2_u32; + for ring in std::iter::once(®ion.outer).chain(®ion.holes) { + for index in 0..ring.len() { + let from = a.frame.point([ring[index].x, ring[index].z, 0.0]); + let next = ring[(index + 1) % ring.len()]; + let to = a.frame.point([next.x, next.z, 0.0]); + out.topology.faces[(face_offset + local_face) as usize].provenance = + prismatic_face_provenance(a, b, from, to, operation, accuracy.geometric)?; + local_face += 1; + } + } + } + Ok(out) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/grid/boundary.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/grid/boundary.rs new file mode 100644 index 0000000..29add37 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/grid/boundary.rs @@ -0,0 +1,380 @@ +use super::cells::{flat_index, neighbor, GridArrangement}; +use crate::brep::{ + boundary, padded_uv_bounds, unit, uv_line, BrepEnvelope, Builder, CurveGeometry, EdgeGeometry, + FaceProvenance, FaceRole, FaceSource, Frame3, GeometryError, Orientation, SurfaceGeometry, Use, +}; +use crate::math::{dot, norm, scale, sub, Interval, Point3}; +use crate::operations::modifying::boolean::operands::{ + containing_faces, source, BoxInput, GridPoint, +}; +use crate::operations::modifying::boolean::types::BooleanOp; +use std::collections::BTreeMap; + +struct GridBoundary { + builder: Builder, + vertex_ids: BTreeMap<(usize, GridPoint), u32>, + edge_ids: BTreeMap<(usize, GridPoint, GridPoint), u32>, + face_components: Vec, + volumes: Vec, +} + +pub(super) struct BoundaryFaces { + pub(super) brep: BrepEnvelope, + pub(super) face_components: Vec, + pub(super) volumes: Vec, +} + +struct FaceOwner<'a> { + input: &'a BoxInput<'a>, + source_face: usize, + reversed: bool, + cutter: bool, + lo: Point3, + hi: Point3, +} + +pub(super) fn boundary_faces( + arrangement: &GridArrangement<'_>, + material: &[bool], + components: &[usize], + id: String, +) -> Result { + let &GridArrangement { n, accuracy, .. } = arrangement; + let mut state = GridBoundary { + builder: Builder::new(id, accuracy)?, + vertex_ids: BTreeMap::new(), + edge_ids: BTreeMap::new(), + face_components: Vec::new(), + volumes: Vec::new(), + }; + for x in 0..n[0] { + for y in 0..n[1] { + for z in 0..n[2] { + let p = [x, y, z]; + let cell = flat_index(p, n); + if !material[cell] { + continue; + } + let component = components[cell]; + for axis in 0..3 { + for upper in [false, true] { + if neighbor(p, axis, upper, n).is_some_and(|q| material[flat_index(q, n)]) { + continue; + } + add_boundary_face(&mut state, arrangement, p, axis, upper, component)?; + } + } + } + } + } + Ok(BoundaryFaces { + brep: state.builder.brep, + face_components: state.face_components, + volumes: state.volumes, + }) +} + +fn add_boundary_face( + state: &mut GridBoundary, + arrangement: &GridArrangement<'_>, + p: GridPoint, + axis: usize, + upper: bool, + component: usize, +) -> Result<(), GeometryError> { + let &GridArrangement { + a, + grid, + canonical_boxes, + operation, + .. + } = arrangement; + let coordinate = grid[axis][p[axis] + usize::from(upper)]; + let mut center: Point3 = + std::array::from_fn(|i| grid[i][p[i]] + (grid[i][p[i] + 1] - grid[i][p[i]]) / 2.0); + center[axis] = coordinate; + let mut sources = Vec::new(); + let normal_sign = if upper { 1.0 } else { -1.0 }; + let FaceOwner { + input, + source_face, + reversed, + cutter, + lo, + hi, + } = face_owner( + &mut sources, + arrangement, + axis, + upper, + coordinate, + center, + normal_sign, + )?; + let surface = input.brep.geometry.surfaces + [input.brep.topology.faces[source_face].surface as usize] + .clone(); + let face_frame = *surface.frame(); + let u = (axis + 1) % 3; + let v = (axis + 2) % 3; + let corners = face_corners(p, axis, upper, u, v); + let (uses, bounds) = face_uses(state, arrangement, component, corners, face_frame)?; + let face = add_side_face(state, input, source_face, surface, bounds, uses)?; + let whole = canonical_boxes + && [u, v] + .into_iter() + .all(|i| grid[i][p[i]] == lo[i] && grid[i][p[i] + 1] == hi[i]); + let surface_reversed = if canonical_boxes { + reversed + } else { + dot( + face_frame.z, + scale([a.frame.x, a.frame.y, a.frame.z][axis], normal_sign), + ) < 0.0 + }; + state.builder.brep.topology.faces[face].sense = if surface_reversed { + Orientation::Reverse + } else { + Orientation::Forward + }; + state.builder.brep.topology.faces[face].provenance = + side_provenance(sources, reversed, cutter, operation, whole); + state.face_components.push(component); + state.volumes.push( + normal_sign + * coordinate + * (grid[u][p[u] + 1] - grid[u][p[u]]) + * (grid[v][p[v] + 1] - grid[v][p[v]]) + / 3.0, + ); + Ok(()) +} + +fn face_owner<'a>( + sources: &mut Vec, + arrangement: &GridArrangement<'a>, + axis: usize, + upper: bool, + coordinate: f64, + center: Point3, + normal_sign: f64, +) -> Result, GeometryError> { + let &GridArrangement { + a, + b, + alo, + ahi, + blo, + bhi, + canonical_boxes, + operation, + accuracy, + .. + } = arrangement; + let mut owner = None; + for (input, lo, hi, cutter) in [(a, alo, ahi, false), (b, blo, bhi, true)] { + let faces = if canonical_boxes { + [false, true] + .into_iter() + .filter_map(|side| { + let plane = if side { hi[axis] } else { lo[axis] }; + (coordinate == plane + && (0..3) + .filter(|i| *i != axis) + .all(|i| center[i] > lo[i] && center[i] < hi[i])) + .then_some((face_index(axis, side), side != upper)) + }) + .collect::>() + } else { + containing_faces( + input, + a.frame.point(center), + scale([a.frame.x, a.frame.y, a.frame.z][axis], normal_sign), + accuracy.intersection, + )? + }; + for (face, reversed) in faces { + if !reversed || (operation == BooleanOp::Subtraction && cutter) { + sources.push(source(input, face)); + if owner.is_none() { + owner = Some((input, face, reversed, cutter, lo, hi)); + } + } + } + } + let (input, source_face, reversed, cutter, lo, hi) = owner.ok_or_else(|| { + GeometryError::InvalidTopology("cuboid boundary has no input-face ancestry".into()) + })?; + Ok(FaceOwner { + input, + source_face, + reversed, + cutter, + lo, + hi, + }) +} + +fn face_index(axis: usize, upper: bool) -> usize { + match axis { + 0 => 2 + usize::from(upper), + 1 => 4 + usize::from(upper), + _ => usize::from(upper), + } +} + +fn face_corners(p: GridPoint, axis: usize, upper: bool, u: usize, v: usize) -> [GridPoint; 4] { + let mut corners = [[0; 3]; 4]; + for (i, (du, dv)) in [(0, 0), (1, 0), (1, 1), (0, 1)].into_iter().enumerate() { + corners[i] = p; + corners[i][axis] += usize::from(upper); + corners[i][u] += du; + corners[i][v] += dv; + } + if !upper { + corners.reverse(); + } + corners +} + +fn face_uses( + state: &mut GridBoundary, + arrangement: &GridArrangement<'_>, + component: usize, + corners: [GridPoint; 4], + face_frame: Frame3, +) -> Result<(Vec, [[f64; 2]; 2]), GeometryError> { + let &GridArrangement { + a, grid, accuracy, .. + } = arrangement; + let local = corners.map(|q| std::array::from_fn(|i| grid[i][q[i]])); + let points = local.map(|q| a.frame.point(q)); + let mut vids = [0; 4]; + for i in 0..4 { + vids[i] = *state + .vertex_ids + .entry((component, corners[i])) + .or_insert_with(|| state.builder.vertex(points[i])); + } + let mut uses = Vec::new(); + for i in 0..4 { + let j = (i + 1) % 4; + let (from, to) = if corners[i] < corners[j] { + (i, j) + } else { + (j, i) + }; + let key = (component, corners[from], corners[to]); + let edge = grid_edge(state, key, points, from, to)?; + let EdgeGeometry::Curve { curve, .. } = + state.builder.brep.topology.edges[edge as usize].geometry + else { + return Err(GeometryError::InvalidTopology( + "cuboid edge is not a line".into(), + )); + }; + let CurveGeometry::Line { origin, direction } = + state.builder.brep.geometry.curves[curve as usize] + else { + return Err(GeometryError::InvalidTopology( + "cuboid curve is not a line".into(), + )); + }; + let uv = face_frame.local(origin); + uses.push(boundary( + edge, + vids[i], + vids[j], + if i == from { + Orientation::Forward + } else { + Orientation::Reverse + }, + uv_line( + [uv[0], uv[1]], + [dot(direction, face_frame.x), dot(direction, face_frame.y)], + ), + )); + } + let bounds = padded_uv_bounds( + points.map(|point| face_frame.local(point)), + accuracy.geometric, + ); + Ok((uses, bounds)) +} + +fn grid_edge( + state: &mut GridBoundary, + key: (usize, GridPoint, GridPoint), + points: [Point3; 4], + from: usize, + to: usize, +) -> Result { + let edge = if let Some(&edge) = state.edge_ids.get(&key) { + edge + } else { + let delta = sub(points[to], points[from]); + let length = norm(delta); + let edge = state.builder.edge( + CurveGeometry::Line { + origin: points[from], + direction: unit(delta)?, + }, + Interval::new(0.0, length)?, + false, + ); + state.edge_ids.insert(key, edge); + edge + }; + Ok(edge) +} + +fn add_side_face( + state: &mut GridBoundary, + input: &BoxInput<'_>, + source_face: usize, + surface: SurfaceGeometry, + bounds: [[f64; 2]; 2], + uses: Vec, +) -> Result { + let face = state.builder.brep.topology.faces.len(); + state + .builder + .face( + &format!("{}-{face}", input.brep.topology.faces[source_face].key), + surface, + bounds, + uses, + ) + .map_err(|error| match error { + GeometryError::InvalidTopology(message) if message == "nonmanifold primitive edge" => { + GeometryError::UnresolvedIntersection( + "cuboid boundary contains an unresolved contact event".into(), + ) + } + other => other, + })?; + Ok(face) +} + +fn side_provenance( + sources: Vec, + reversed: bool, + cutter: bool, + operation: BooleanOp, + whole: bool, +) -> FaceProvenance { + FaceProvenance { + role: if sources.len() > 1 { + FaceRole::Coincident + } else if cutter && operation == BooleanOp::Subtraction { + FaceRole::Cut + } else if whole { + FaceRole::Preserved + } else { + FaceRole::Split + }, + sources, + reversed, + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/grid/cells.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/grid/cells.rs new file mode 100644 index 0000000..1c1e011 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/grid/cells.rs @@ -0,0 +1,127 @@ +use crate::brep::{Accuracy, GeometryError}; +use crate::math::Point3; +use crate::operations::modifying::boolean::operands::{classified_inside, BoxInput, GridPoint}; +use crate::operations::modifying::boolean::types::BooleanOp; +use std::collections::VecDeque; + +pub(super) struct GridArrangement<'a> { + pub(super) a: &'a BoxInput<'a>, + pub(super) b: &'a BoxInput<'a>, + pub(super) grid: &'a [Vec; 3], + pub(super) n: GridPoint, + pub(super) alo: Point3, + pub(super) ahi: Point3, + pub(super) blo: Point3, + pub(super) bhi: Point3, + pub(super) canonical_boxes: bool, + pub(super) operation: BooleanOp, + pub(super) accuracy: Accuracy, +} + +pub(super) fn material_cells( + arrangement: &GridArrangement<'_>, + count: usize, +) -> Result, GeometryError> { + let &GridArrangement { + a, + b, + grid, + n, + alo, + ahi, + blo, + bhi, + canonical_boxes, + operation, + .. + } = arrangement; + let mut material = vec![false; count]; + for x in 0..n[0] { + for y in 0..n[1] { + for z in 0..n[2] { + let p = [x, y, z]; + let center = std::array::from_fn(|i| { + grid[i][p[i]] + (grid[i][p[i] + 1] - grid[i][p[i]]) / 2.0 + }); + let ia = if canonical_boxes { + inside(center, alo, ahi) + } else { + classified_inside(a.brep, a.frame.point(center))? + }; + let ib = if canonical_boxes { + inside(center, blo, bhi) + } else { + classified_inside(b.brep, a.frame.point(center))? + }; + material[flat_index(p, n)] = match operation { + BooleanOp::Union => ia || ib, + BooleanOp::Intersection => ia && ib, + BooleanOp::Subtraction => ia && !ib, + }; + } + } + } + Ok(material) +} + +fn inside(point: Point3, lo: Point3, hi: Point3) -> bool { + (0..3).all(|i| point[i] > lo[i] && point[i] < hi[i]) +} + +pub(super) fn flat_index(p: GridPoint, n: GridPoint) -> usize { + (p[0] * n[1] + p[1]) * n[2] + p[2] +} + +pub(super) fn material_components( + material: &[bool], + n: GridPoint, + count: usize, +) -> (Vec, usize) { + let mut components = vec![usize::MAX; count]; + let mut component_count = 0; + for x in 0..n[0] { + for y in 0..n[1] { + for z in 0..n[2] { + let p = [x, y, z]; + let index = flat_index(p, n); + if !material[index] || components[index] != usize::MAX { + continue; + } + components[index] = component_count; + let mut queue = VecDeque::from([p]); + while let Some(cell) = queue.pop_front() { + for axis in 0..3 { + for upper in [false, true] { + if let Some(adjacent) = neighbor(cell, axis, upper, n) { + let j = flat_index(adjacent, n); + if material[j] && components[j] == usize::MAX { + components[j] = component_count; + queue.push_back(adjacent); + } + } + } + } + } + component_count += 1; + } + } + } + (components, component_count) +} + +pub(super) fn neighbor( + mut p: GridPoint, + axis: usize, + upper: bool, + n: GridPoint, +) -> Option { + if upper { + p[axis] += 1; + if p[axis] >= n[axis] { + return None; + } + } else { + p[axis] = p[axis].checked_sub(1)?; + } + Some(p) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/grid/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/grid/mod.rs new file mode 100644 index 0000000..ede51c3 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/grid/mod.rs @@ -0,0 +1,276 @@ +mod boundary; +mod cells; + +use crate::brep::{Accuracy, BrepEnvelope, GeometryError, Shell, SolidRegion}; +use crate::math::{norm, Point3}; +use crate::operations::modifying::boolean::operands::{coverage, source, BoxInput}; +use crate::operations::modifying::boolean::types::{ + BooleanOp, BooleanReport, BooleanResult, FaceMapping, +}; +use boundary::{boundary_faces, BoundaryFaces}; +use cells::{material_cells, material_components, GridArrangement}; +use std::collections::VecDeque; + +pub(crate) fn boolean_grid( + a: BoxInput<'_>, + b: BoxInput<'_>, + operation: BooleanOp, + id: String, + canonical_boxes: bool, +) -> Result { + if [a.frame.x, a.frame.y, a.frame.z] != [b.frame.x, b.frame.y, b.frame.z] { + return Err(coverage()); + } + let accuracy = Accuracy::combined(a.brep.accuracy, b.brep.accuracy); + let alo = [0.0; 3]; + let ahi = a.size; + let blo = a.frame.local(b.frame.origin); + let bhi: Point3 = std::array::from_fn(|i| blo[i] + b.size[i]); + if blo.into_iter().chain(bhi).any(|value| !value.is_finite()) { + return Err(GeometryError::UnresolvedIntersection( + "cuboid relative coordinates exceed numerical range".into(), + )); + } + let mut grid: [Vec; 3] = std::array::from_fn(|i| vec![alo[i], ahi[i], blo[i], bhi[i]]); + if !canonical_boxes { + for input in [&a, &b] { + for vertex in &input.brep.topology.vertices { + let point = a.frame.local(vertex.position); + for axis in 0..3 { + grid[axis].push(point[axis]); + } + } + } + } + deduplicate_grid(&mut grid, &a, &b, accuracy)?; + let alo = snapped_point(&grid, alo); + let ahi = snapped_point(&grid, ahi); + let blo = snapped_point(&grid, blo); + let bhi = snapped_point(&grid, bhi); + let coincident = alo == blo && ahi == bhi; + let overlap_lo: Point3 = std::array::from_fn(|i| alo[i].max(blo[i])); + let overlap_hi: Point3 = std::array::from_fn(|i| ahi[i].min(bhi[i])); + let contacts = contact_points(&a, overlap_lo, overlap_hi); + let n = grid.each_ref().map(|g| g.len() - 1); + let count = n + .into_iter() + .try_fold(1usize, |product, cells| product.checked_mul(cells)) + .filter(|count| *count <= 2_000_000) + .ok_or_else(|| { + GeometryError::LimitExceeded( + "rectilinear Boolean grid exceeds two million cells".into(), + ) + })?; + let arrangement = GridArrangement { + a: &a, + b: &b, + grid: &grid, + n, + alo, + ahi, + blo, + bhi, + canonical_boxes, + operation, + accuracy, + }; + let material = material_cells(&arrangement, count)?; + let (components, component_count) = material_components(&material, n, count); + let BoundaryFaces { + brep: mut out, + face_components, + volumes, + } = boundary_faces(&arrangement, &material, &components, id)?; + add_grid_shells(&mut out, &face_components, &volumes, component_count)?; + finish_grid_result(out, &a, &b, operation, contacts, coincident) +} + +fn deduplicate_grid( + grid: &mut [Vec; 3], + a: &BoxInput<'_>, + b: &BoxInput<'_>, + accuracy: Accuracy, +) -> Result<(), GeometryError> { + let magnitude = [a.frame.origin, b.frame.origin] + .into_iter() + .map(norm) + .fold(0.0_f64, f64::max) + .max(a.size.into_iter().fold(0.0_f64, f64::max)) + .max(b.size.into_iter().fold(0.0_f64, f64::max)); + let noise = (256.0 * f64::EPSILON * magnitude).min(accuracy.intersection / 8.0); + for values in grid { + values.sort_by(f64::total_cmp); + let mut unique: Vec = Vec::new(); + for &value in values.iter() { + if let Some(last) = unique.last() { + let gap = value - last; + if gap <= noise { + continue; + } + if gap <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "cuboid arrangement contains sub-tolerance features".into(), + )); + } + } + unique.push(value); + } + *values = unique; + } + Ok(()) +} + +fn snapped_point(grid: &[Vec; 3], p: Point3) -> Point3 { + std::array::from_fn(|i| { + grid[i] + .iter() + .copied() + .min_by(|x, y| (x - p[i]).abs().total_cmp(&(y - p[i]).abs())) + .unwrap_or(p[i]) + }) +} + +fn contact_points(a: &BoxInput<'_>, overlap_lo: Point3, overlap_hi: Point3) -> Vec { + let mut contacts = Vec::new(); + if (0..3).all(|i| overlap_lo[i] <= overlap_hi[i]) + && (0..3).any(|i| overlap_lo[i] == overlap_hi[i]) + { + for mask in 0..8 { + let point = a.frame.point(std::array::from_fn(|i| { + if mask & (1 << i) == 0 { + overlap_lo[i] + } else { + overlap_hi[i] + } + })); + if !contacts.contains(&point) { + contacts.push(point); + } + } + } + contacts +} + +fn add_grid_shells( + out: &mut BrepEnvelope, + face_components: &[usize], + volumes: &[f64], + component_count: usize, +) -> Result<(), GeometryError> { + let mut assigned = vec![false; out.topology.faces.len()]; + let mut outer = vec![None; component_count]; + let mut cavities = vec![Vec::new(); component_count]; + for start in 0..out.topology.faces.len() { + if assigned[start] { + continue; + } + let shell = out.topology.shells.len() as u32; + let component = face_components[start]; + let mut faces = Vec::new(); + let mut volume = 0.0; + assigned[start] = true; + let mut queue = VecDeque::from([start]); + while let Some(face) = queue.pop_front() { + volume += volumes[face]; + faces.push(face as u32); + out.topology.faces[face].shell_ref = Some(shell); + for h in out + .topology + .halfedges + .iter() + .filter(|h| h.face == Some(face as u32)) + { + let twin = h.twin.ok_or_else(|| { + GeometryError::InvalidTopology("open cuboid boolean boundary".into()) + })?; + let adjacent = out.topology.halfedges[twin as usize].face.ok_or_else(|| { + GeometryError::InvalidTopology("cuboid twin has no face".into()) + })? as usize; + if face_components[adjacent] != component { + return Err(GeometryError::InvalidTopology( + "cuboid contact incorrectly sewed separate material components".into(), + )); + } + if !assigned[adjacent] { + assigned[adjacent] = true; + queue.push_back(adjacent); + } + } + } + out.topology.shells.push(Shell { + id: shell, + faces, + is_closed: true, + }); + if volume > 0.0 { + if outer[component].replace(shell).is_some() { + return Err(GeometryError::UnresolvedIntersection( + "cuboid material component has multiple outer shells".into(), + )); + } + } else if volume < 0.0 { + cavities[component].push(shell); + } else { + return Err(GeometryError::UnresolvedIntersection( + "cuboid shell volume is unresolved".into(), + )); + } + } + for (component, shell) in outer.into_iter().enumerate() { + out.solids.push(SolidRegion { + outer_shell: shell.ok_or_else(|| { + GeometryError::InvalidTopology("cuboid material has no outer shell".into()) + })?, + cavity_shells: cavities[component].clone(), + }); + } + Ok(()) +} + +fn finish_grid_result( + mut out: BrepEnvelope, + a: &BoxInput<'_>, + b: &BoxInput<'_>, + operation: BooleanOp, + contacts: Vec, + coincident: bool, +) -> Result { + out.revision = a + .brep + .revision + .max(b.brep.revision) + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; + out.validate()?; + let face_mappings = [a, b] + .into_iter() + .flat_map(|input| (0..input.brep.topology.faces.len()).map(move |face| source(input, face))) + .map(|source| FaceMapping { + result_faces: out + .topology + .faces + .iter() + .filter(|face| { + face.provenance.sources.iter().any(|s| { + s.entity == source.entity + && s.body == source.body + && s.key == source.key + && s.face == source.face + }) + }) + .map(|face| face.id) + .collect(), + source, + }) + .collect(); + Ok(BooleanResult { + report: BooleanReport { + operation, + quality: out.quality.clone(), + contacts, + coincident, + face_mappings, + }, + brep: out, + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/mod.rs new file mode 100644 index 0000000..3dc5f6e --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/rectilinear/mod.rs @@ -0,0 +1,22 @@ +mod grid; + +pub(super) use grid::boolean_grid; + +use crate::brep::{BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::operations::modifying::boolean::operands::{coverage, rectilinear_input}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +pub(crate) fn boolean_rectilinear( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let axes = match b.geometry.surfaces.first() { + Some(SurfaceGeometry::Plane { frame }) => *frame, + _ => return Err(coverage()), + }; + let a = rectilinear_input(a, axes)?; + let b = rectilinear_input(b, axes)?; + boolean_grid(a, b, operation, id, false) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_box.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_box.rs new file mode 100644 index 0000000..6f0fa9e --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_box.rs @@ -0,0 +1,333 @@ +use super::containment::analytic_containment_boolean; +use crate::brep::{ + boundary, plane_boundary, Accuracy, BrepEnvelope, Builder, CurveGeometry, FaceRole, Frame3, + GeometryError, Orientation, PcurveGeometry, SurfaceGeometry, Use, +}; +use crate::math::{norm, scale, sub, Interval, Point3}; +use crate::operations::modifying::boolean::assembly::{ + add_box_faces, box_source, face_provenance, finish_analytic_result, patch, SpherePatch, +}; +use crate::operations::modifying::boolean::operands::{ + full_box, full_sphere, BoxInput, SphereInput, +}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use crate::primitives::add_box_corners; + +struct CrossedFace { + axis: usize, + upper: bool, + signed_plane: f64, +} + +struct CapCircle { + cap_frame: Frame3, + latitude: f64, + circle_frame: Frame3, + circle_radius: f64, + entry_face: usize, +} + +pub(crate) fn sphere_box_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let (sphere, box_, sphere_is_a) = if matches!( + a.geometry.surfaces.first(), + Some(SurfaceGeometry::Sphere { .. }) + ) { + (full_sphere(a)?, full_box(b)?, true) + } else { + (full_sphere(b)?, full_box(a)?, false) + }; + let clearance = 4.0 + * sphere + .brep + .accuracy + .geometric + .max(box_.brep.accuracy.geometric); + let center = box_.frame.local(sphere.frame.origin); + let sphere_margin = (0..3).fold(f64::INFINITY, |margin, axis| { + margin + .min(center[axis] - sphere.radius) + .min(box_.size[axis] - center[axis] - sphere.radius) + }); + let mut farthest: f64 = 0.0; + for corner in 0..8 { + let point = box_.frame.point(std::array::from_fn(|axis| { + if corner & (1 << axis) == 0 { + 0.0 + } else { + box_.size[axis] + } + })); + farthest = farthest.max(norm(sub(point, sphere.frame.origin))); + } + let box_margin = sphere.radius - farthest; + let sphere_inside_box = sphere_margin > clearance; + let box_inside_sphere = box_margin > clearance; + if sphere_inside_box || box_inside_sphere { + let b_inside_a = if sphere_is_a { + box_inside_sphere + } else { + sphere_inside_box + }; + return analytic_containment_boolean(a, b, b_inside_a, operation, id); + } + if sphere_margin.abs() <= clearance || box_margin.abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "sphere/cuboid containment boundary is below geometric resolution".into(), + )); + } + single_face_sphere_box_boolean(&sphere, &box_, sphere_is_a, operation, id, clearance) +} + +fn single_face_sphere_box_boolean( + sphere: &SphereInput<'_>, + box_: &BoxInput<'_>, + sphere_is_a: bool, + operation: BooleanOp, + id: String, + clearance: f64, +) -> Result { + let accuracy = Accuracy::combined(sphere.brep.accuracy, box_.brep.accuracy); + let center = box_.frame.local(sphere.frame.origin); + let box_axes = [box_.frame.x, box_.frame.y, box_.frame.z]; + let CrossedFace { + axis, + upper, + signed_plane, + } = crossed_face_plane(sphere, box_, center, clearance)?; + let cap = cap_circle(sphere, box_axes, axis, upper, signed_plane, clearance)?; + let mut builder = Builder::new(id, accuracy)?; + let circle_vertex = builder.vertex(cap.circle_frame.point([cap.circle_radius, 0.0, 0.0])); + let circle_edge = builder.edge( + CurveGeometry::Circle { + frame: cap.circle_frame, + radius: cap.circle_radius, + }, + Interval::new(0.0, std::f64::consts::TAU)?, + false, + ); + + if operation == BooleanOp::Intersection || (operation == BooleanOp::Subtraction && sphere_is_a) + { + add_closed_sphere_cap( + &mut builder, + sphere, + box_, + operation, + &cap, + (circle_vertex, circle_edge), + )?; + } else { + let corners = add_box_corners(&mut builder, box_.frame, box_.size, box_axes)?; + add_box_faces( + &mut builder, + box_, + &corners, + accuracy, + |builder, face_index, face_frame| { + entry_hole( + builder, + face_frame, + face_index == cap.entry_face, + (circle_vertex, circle_edge), + ) + }, + |face_index| face_index == cap.entry_face, + )?; + patch( + &mut builder, + sphere, + &SpherePatch { + frame: cap.cap_frame, + latitude: cap.latitude, + north: operation == BooleanOp::Subtraction, + shared_edge: circle_edge, + shared_vertex: circle_vertex, + }, + operation == BooleanOp::Subtraction, + )?; + } + + let out = builder.finish_solid()?; + finish_analytic_result(out, sphere.brep, box_.brep, sphere_is_a, operation, false) +} + +fn crossed_face_plane( + sphere: &SphereInput<'_>, + box_: &BoxInput<'_>, + center: Point3, + clearance: f64, +) -> Result { + let mut crossings = Vec::new(); + for axis in 0..3 { + for upper in [false, true] { + let signed_plane = if upper { + center[axis] - box_.size[axis] + } else { + -center[axis] + }; + let separation = signed_plane.abs() - sphere.radius; + if separation.abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "sphere is tangent to a cuboid face within geometric resolution".into(), + )); + } + if separation < 0.0 { + crossings.push((axis, upper, signed_plane)); + } else if signed_plane > sphere.radius { + return Err(GeometryError::CoverageGap { + families: ["sphere".into(), "cuboid".into()], + }); + } + } + } + let [(axis, upper, signed_plane)] = crossings.as_slice() else { + return Err(GeometryError::CoverageGap { + families: ["sphere".into(), "multi-face cuboid intersection".into()], + }); + }; + Ok(CrossedFace { + axis: *axis, + upper: *upper, + signed_plane: *signed_plane, + }) +} + +fn cap_circle( + sphere: &SphereInput<'_>, + box_axes: [Point3; 3], + axis: usize, + upper: bool, + signed_plane: f64, + clearance: f64, +) -> Result { + let inward = if upper { + scale(box_axes[axis], -1.0) + } else { + box_axes[axis] + }; + let tangent_axis = match axis { + 0 => box_axes[1], + _ => box_axes[0], + }; + let cap_frame = Frame3::from_axis(sphere.frame.origin, inward, tangent_axis)?; + let latitude = (signed_plane / sphere.radius).asin(); + let circle_radius = + ((sphere.radius - signed_plane.abs()) * (sphere.radius + signed_plane.abs())).sqrt(); + if circle_radius <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "sphere/cuboid intersection circle is below geometric resolution".into(), + )); + } + let circle_frame = Frame3 { + origin: cap_frame.point([0.0, 0.0, signed_plane]), + ..cap_frame + }; + let entry_face = match axis { + 0 => 2 + usize::from(upper), + 1 => 4 + usize::from(upper), + _ => usize::from(upper), + }; + Ok(CapCircle { + cap_frame, + latitude, + circle_frame, + circle_radius, + entry_face, + }) +} + +fn add_closed_sphere_cap( + builder: &mut Builder, + sphere: &SphereInput<'_>, + box_: &BoxInput<'_>, + operation: BooleanOp, + cap: &CapCircle, + circle: (u32, u32), +) -> Result<(), GeometryError> { + let (circle_vertex, circle_edge) = circle; + let keep_inside_box = operation == BooleanOp::Intersection; + let disk_frame = if keep_inside_box { + Frame3 { + x: cap.circle_frame.x, + y: scale(cap.circle_frame.y, -1.0), + z: scale(cap.circle_frame.z, -1.0), + ..cap.circle_frame + } + } else { + cap.circle_frame + }; + builder.face( + "cuboid:cap", + SurfaceGeometry::Plane { frame: disk_frame }, + [[-cap.circle_radius, cap.circle_radius]; 2], + vec![boundary( + circle_edge, + circle_vertex, + circle_vertex, + if keep_inside_box { + Orientation::Reverse + } else { + Orientation::Forward + }, + PcurveGeometry::Conic2 { + origin: [0.0; 2], + axis_a: [cap.circle_radius, 0.0], + axis_b: [ + 0.0, + if keep_inside_box { + -cap.circle_radius + } else { + cap.circle_radius + }, + ], + }, + )], + )?; + builder.brep.topology.faces[0].provenance = face_provenance( + vec![box_source(box_, cap.entry_face)], + if keep_inside_box { + FaceRole::Split + } else { + FaceRole::Cut + }, + !keep_inside_box, + ); + patch( + builder, + sphere, + &SpherePatch { + frame: cap.cap_frame, + latitude: cap.latitude, + north: keep_inside_box, + shared_edge: circle_edge, + shared_vertex: circle_vertex, + }, + false, + ) +} + +fn entry_hole( + builder: &Builder, + face_frame: Frame3, + is_entry_face: bool, + circle: (u32, u32), +) -> Result>, GeometryError> { + let (circle_vertex, circle_edge) = circle; + Ok(if is_entry_face { + vec![vec![plane_boundary( + builder, + face_frame, + circle_edge, + circle_vertex, + circle_vertex, + Orientation::Forward, + )?]] + } else { + Vec::new() + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/coaxial.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/coaxial.rs new file mode 100644 index 0000000..620473e --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/coaxial.rs @@ -0,0 +1,456 @@ +use super::faces::{sphere_band, SphereBand}; +use crate::brep::{Accuracy, BrepEnvelope, Builder, FaceRole, FaceSource, Frame3, GeometryError}; +use crate::math::Point3; +use crate::operations::modifying::boolean::assembly::{ + append_analytic_input, circular_boundary, cylinder_band, cylinder_cap, cylinder_source, + face_provenance, finish_analytic_result, patch, CylinderBand, SpherePatch, +}; +use crate::operations::modifying::boolean::operands::{CylinderInput, SphereInput}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +struct CoaxialCut<'a> { + sphere: &'a SphereInput<'a>, + cylinder: &'a CylinderInput<'a>, + sphere_frame: Frame3, + z0: f64, + z1: f64, + latitude0: f64, + latitude1: f64, +} + +pub(super) fn coaxial_sphere_cylinder_boolean( + sphere: &SphereInput<'_>, + cylinder: &CylinderInput<'_>, + sphere_is_a: bool, + operation: BooleanOp, + id: String, + clearance: f64, +) -> Result { + let local = cylinder.frame.local(sphere.frame.origin); + check_coaxial_fit(sphere, cylinder, local, clearance)?; + + let accuracy = Accuracy::combined(sphere.brep.accuracy, cylinder.brep.accuracy); + let half = ((sphere.radius - cylinder.radius) * (sphere.radius + cylinder.radius)).sqrt(); + let z0 = local[2] - half; + let z1 = local[2] + half; + let latitude0 = (-half / sphere.radius).asin(); + let latitude1 = (half / sphere.radius).asin(); + let sphere_frame = Frame3 { + origin: sphere.frame.origin, + x: cylinder.frame.x, + y: cylinder.frame.y, + z: cylinder.frame.z, + }; + let cylinder_lateral = cylinder_source(cylinder, 0); + let cylinder_caps = [cylinder_source(cylinder, 1), cylinder_source(cylinder, 2)]; + let cut = CoaxialCut { + sphere, + cylinder, + sphere_frame, + z0, + z1, + latitude0, + latitude1, + }; + + if operation == BooleanOp::Subtraction && !sphere_is_a { + let out = cylinder_minus_sphere(&cut, id, accuracy, &cylinder_lateral, &cylinder_caps)?; + return finish_analytic_result( + out, + sphere.brep, + cylinder.brep, + sphere_is_a, + operation, + false, + ); + } + + let mut builder = Builder::new(id, accuracy)?; + let low = circular_boundary(&mut builder, cylinder.frame, cylinder.radius, z0)?; + let high = circular_boundary(&mut builder, cylinder.frame, cylinder.radius, z1)?; + match operation { + BooleanOp::Intersection => { + add_intersection_faces(&mut builder, &cut, low, high, cylinder_lateral)?; + } + BooleanOp::Subtraction => { + add_sphere_minus_cylinder_faces(&mut builder, &cut, low, high, cylinder_lateral)?; + } + BooleanOp::Union => { + add_union_faces( + &mut builder, + &cut, + low, + high, + cylinder_lateral, + &cylinder_caps, + )?; + } + } + finish_analytic_result( + builder.finish_solid()?, + sphere.brep, + cylinder.brep, + sphere_is_a, + operation, + false, + ) +} + +fn check_coaxial_fit( + sphere: &SphereInput<'_>, + cylinder: &CylinderInput<'_>, + local: Point3, + clearance: f64, +) -> Result<(), GeometryError> { + let radial = local[0].hypot(local[1]); + let scale = sphere + .radius + .max(cylinder.radius) + .max(cylinder.height) + .max(1.0); + let roundoff = 128.0 * f64::EPSILON * scale; + if radial > roundoff { + return Err(if radial <= clearance { + GeometryError::UnresolvedIntersection( + "sphere/cylinder axes differ below geometric resolution".into(), + ) + } else { + GeometryError::CoverageGap { + families: ["sphere".into(), "noncoaxial cylinder".into()], + } + }); + } + let radius_gap = sphere.radius - cylinder.radius; + if radius_gap <= clearance { + return Err(if radius_gap.abs() <= clearance { + GeometryError::UnresolvedIntersection( + "sphere/cylinder tangency is below geometric resolution".into(), + ) + } else { + GeometryError::CoverageGap { + families: ["sphere".into(), "cylinder".into()], + } + }); + } + let sphere_low = local[2] - sphere.radius; + let sphere_high = local[2] + sphere.radius; + let lower_clearance = sphere_low; + let upper_clearance = cylinder.height - sphere_high; + if lower_clearance <= clearance || upper_clearance <= clearance { + let near = lower_clearance.abs() <= clearance || upper_clearance.abs() <= clearance; + return Err(if near { + GeometryError::UnresolvedIntersection( + "sphere/cylinder cap event is below geometric resolution".into(), + ) + } else { + GeometryError::CoverageGap { + families: ["sphere".into(), "finite cylinder cap".into()], + } + }); + } + Ok(()) +} + +fn cylinder_minus_sphere( + cut: &CoaxialCut<'_>, + id: String, + accuracy: Accuracy, + cylinder_lateral: &FaceSource, + cylinder_caps: &[FaceSource; 2], +) -> Result { + let mut out = BrepEnvelope::new(id, accuracy)?; + for north in [false, true] { + let mut builder = Builder::new( + format!("{}:{}", out.id, if north { "upper" } else { "lower" }), + accuracy, + )?; + let intersection = circular_boundary( + &mut builder, + cut.cylinder.frame, + cut.cylinder.radius, + if north { cut.z1 } else { cut.z0 }, + )?; + let end = circular_boundary( + &mut builder, + cut.cylinder.frame, + cut.cylinder.radius, + if north { cut.cylinder.height } else { 0.0 }, + )?; + if north { + add_upper_piece_faces( + &mut builder, + cut, + intersection, + end, + cylinder_lateral, + cylinder_caps, + )?; + } else { + add_lower_piece_faces( + &mut builder, + cut, + intersection, + end, + cylinder_lateral, + cylinder_caps, + )?; + } + let part = builder.finish_solid()?; + let provenances: Vec<_> = part + .topology + .faces + .iter() + .map(|face| face.provenance.clone()) + .collect(); + let face_offset = out.topology.faces.len(); + append_analytic_input(&mut out, &part)?; + for (face, provenance) in out.topology.faces[face_offset..] + .iter_mut() + .zip(provenances) + { + face.provenance = provenance; + } + } + Ok(out) +} + +fn add_upper_piece_faces( + builder: &mut Builder, + cut: &CoaxialCut<'_>, + intersection: (u32, u32), + end: (u32, u32), + cylinder_lateral: &FaceSource, + cylinder_caps: &[FaceSource; 2], +) -> Result<(), GeometryError> { + cylinder_band( + builder, + &CylinderBand { + frame: cut.cylinder.frame, + radius: cut.cylinder.radius, + z0: cut.z1, + z1: cut.cylinder.height, + low: intersection, + high: end, + }, + face_provenance(vec![cylinder_lateral.clone()], FaceRole::Split, false), + false, + )?; + cylinder_cap( + builder, + cut.cylinder.frame, + cut.cylinder.radius, + cut.cylinder.height, + end, + true, + face_provenance(vec![cylinder_caps[1].clone()], FaceRole::Preserved, false), + )?; + patch( + builder, + cut.sphere, + &SpherePatch { + frame: cut.sphere_frame, + latitude: cut.latitude1, + north: true, + shared_edge: intersection.1, + shared_vertex: intersection.0, + }, + true, + ) +} + +fn add_lower_piece_faces( + builder: &mut Builder, + cut: &CoaxialCut<'_>, + intersection: (u32, u32), + end: (u32, u32), + cylinder_lateral: &FaceSource, + cylinder_caps: &[FaceSource; 2], +) -> Result<(), GeometryError> { + cylinder_band( + builder, + &CylinderBand { + frame: cut.cylinder.frame, + radius: cut.cylinder.radius, + z0: 0.0, + z1: cut.z0, + low: end, + high: intersection, + }, + face_provenance(vec![cylinder_lateral.clone()], FaceRole::Split, false), + false, + )?; + cylinder_cap( + builder, + cut.cylinder.frame, + cut.cylinder.radius, + 0.0, + end, + false, + face_provenance(vec![cylinder_caps[0].clone()], FaceRole::Preserved, false), + )?; + patch( + builder, + cut.sphere, + &SpherePatch { + frame: cut.sphere_frame, + latitude: cut.latitude0, + north: false, + shared_edge: intersection.1, + shared_vertex: intersection.0, + }, + true, + ) +} + +fn add_intersection_faces( + builder: &mut Builder, + cut: &CoaxialCut<'_>, + low: (u32, u32), + high: (u32, u32), + cylinder_lateral: FaceSource, +) -> Result<(), GeometryError> { + patch( + builder, + cut.sphere, + &SpherePatch { + frame: cut.sphere_frame, + latitude: cut.latitude0, + north: false, + shared_edge: low.1, + shared_vertex: low.0, + }, + false, + )?; + cylinder_band( + builder, + &CylinderBand { + frame: cut.cylinder.frame, + radius: cut.cylinder.radius, + z0: cut.z0, + z1: cut.z1, + low, + high, + }, + face_provenance(vec![cylinder_lateral], FaceRole::Split, false), + false, + )?; + patch( + builder, + cut.sphere, + &SpherePatch { + frame: cut.sphere_frame, + latitude: cut.latitude1, + north: true, + shared_edge: high.1, + shared_vertex: high.0, + }, + false, + ) +} + +fn add_sphere_minus_cylinder_faces( + builder: &mut Builder, + cut: &CoaxialCut<'_>, + low: (u32, u32), + high: (u32, u32), + cylinder_lateral: FaceSource, +) -> Result<(), GeometryError> { + sphere_band( + builder, + cut.sphere, + &SphereBand { + frame: cut.sphere_frame, + latitude0: cut.latitude0, + latitude1: cut.latitude1, + low, + high, + }, + false, + )?; + cylinder_band( + builder, + &CylinderBand { + frame: cut.cylinder.frame, + radius: cut.cylinder.radius, + z0: cut.z0, + z1: cut.z1, + low, + high, + }, + face_provenance(vec![cylinder_lateral], FaceRole::Cut, true), + true, + ) +} + +fn add_union_faces( + builder: &mut Builder, + cut: &CoaxialCut<'_>, + low: (u32, u32), + high: (u32, u32), + cylinder_lateral: FaceSource, + cylinder_caps: &[FaceSource; 2], +) -> Result<(), GeometryError> { + let bottom = circular_boundary(builder, cut.cylinder.frame, cut.cylinder.radius, 0.0)?; + let top = circular_boundary( + builder, + cut.cylinder.frame, + cut.cylinder.radius, + cut.cylinder.height, + )?; + cylinder_band( + builder, + &CylinderBand { + frame: cut.cylinder.frame, + radius: cut.cylinder.radius, + z0: 0.0, + z1: cut.z0, + low: bottom, + high: low, + }, + face_provenance(vec![cylinder_lateral.clone()], FaceRole::Split, false), + false, + )?; + cylinder_cap( + builder, + cut.cylinder.frame, + cut.cylinder.radius, + 0.0, + bottom, + false, + face_provenance(vec![cylinder_caps[0].clone()], FaceRole::Preserved, false), + )?; + sphere_band( + builder, + cut.sphere, + &SphereBand { + frame: cut.sphere_frame, + latitude0: cut.latitude0, + latitude1: cut.latitude1, + low, + high, + }, + false, + )?; + cylinder_band( + builder, + &CylinderBand { + frame: cut.cylinder.frame, + radius: cut.cylinder.radius, + z0: cut.z1, + z1: cut.cylinder.height, + low: high, + high: top, + }, + face_provenance(vec![cylinder_lateral], FaceRole::Split, false), + false, + )?; + cylinder_cap( + builder, + cut.cylinder.frame, + cut.cylinder.radius, + cut.cylinder.height, + top, + true, + face_provenance(vec![cylinder_caps[1].clone()], FaceRole::Preserved, false), + ) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/faces.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/faces.rs new file mode 100644 index 0000000..52421c8 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/faces.rs @@ -0,0 +1,85 @@ +use crate::brep::{ + boundary, uv_line, Builder, CurveGeometry, FaceRole, Frame3, GeometryError, Orientation, + SurfaceGeometry, +}; +use crate::math::{scale, Interval}; +use crate::operations::modifying::boolean::assembly::{provenance, reverse_face}; +use crate::operations::modifying::boolean::operands::SphereInput; +use Orientation::{Forward as F, Reverse as R}; + +pub(super) struct SphereBand { + pub(super) frame: Frame3, + pub(super) latitude0: f64, + pub(super) latitude1: f64, + pub(super) low: (u32, u32), + pub(super) high: (u32, u32), +} + +pub(super) fn sphere_band( + builder: &mut Builder, + input: &SphereInput<'_>, + band: &SphereBand, + reversed: bool, +) -> Result<(), GeometryError> { + let SphereBand { + frame, + latitude0, + latitude1, + low, + high, + } = *band; + let seam = builder.edge( + CurveGeometry::Circle { + frame: Frame3 { + x: frame.x, + y: frame.z, + z: scale(frame.y, -1.0), + ..frame + }, + radius: input.radius, + }, + Interval::new(latitude0, latitude1)?, + true, + ); + let face = builder.brep.topology.faces.len() as u32; + let tau = std::f64::consts::TAU; + builder.face( + &format!("{face}:sphere-band"), + SurfaceGeometry::Sphere { + frame, + radius: input.radius, + }, + [[0.0, tau], [latitude0, latitude1]], + vec![ + boundary( + low.1, + low.0, + low.0, + F, + uv_line([0.0, latitude0], [1.0, 0.0]), + ), + boundary(seam, low.0, high.0, F, uv_line([tau, 0.0], [0.0, 1.0])), + boundary( + high.1, + high.0, + high.0, + R, + uv_line([0.0, latitude1], [1.0, 0.0]), + ), + boundary(seam, high.0, low.0, R, uv_line([0.0, 0.0], [0.0, 1.0])), + ], + )?; + builder.brep.topology.faces[face as usize].provenance = provenance( + input, + if reversed { + FaceRole::Cut + } else { + FaceRole::Split + }, + reversed, + ); + if reversed { + reverse_face(&mut builder.brep, face); + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/mod.rs new file mode 100644 index 0000000..f2eeee0 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/mod.rs @@ -0,0 +1,55 @@ +mod coaxial; +mod faces; +mod offset; + +use super::containment::analytic_containment_boolean; +use crate::brep::{BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::operations::modifying::boolean::operands::{full_cylinder, full_sphere}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; +use offset::offset_sphere_cylinder_boolean; + +pub(crate) fn sphere_cylinder_boolean( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let (sphere, cylinder, sphere_is_a) = if matches!( + a.geometry.surfaces.first(), + Some(SurfaceGeometry::Sphere { .. }) + ) { + (full_sphere(a)?, full_cylinder(b)?, true) + } else { + (full_sphere(b)?, full_cylinder(a)?, false) + }; + let clearance = 4.0 + * sphere + .brep + .accuracy + .geometric + .max(cylinder.brep.accuracy.geometric); + let local = cylinder.frame.local(sphere.frame.origin); + let radial = local[0].hypot(local[1]); + let sphere_margin = (cylinder.radius - radial - sphere.radius) + .min(local[2] - sphere.radius) + .min(cylinder.height - local[2] - sphere.radius); + let axial_extent = local[2].abs().max((cylinder.height - local[2]).abs()); + let cylinder_extent = (radial + cylinder.radius).hypot(axial_extent); + let cylinder_margin = sphere.radius - cylinder_extent; + let sphere_inside_cylinder = sphere_margin > clearance; + let cylinder_inside_sphere = cylinder_margin > clearance; + if sphere_inside_cylinder || cylinder_inside_sphere { + let b_inside_a = if sphere_is_a { + cylinder_inside_sphere + } else { + sphere_inside_cylinder + }; + return analytic_containment_boolean(a, b, b_inside_a, operation, id); + } + if sphere_margin.abs() <= clearance || cylinder_margin.abs() <= clearance { + return Err(GeometryError::UnresolvedIntersection( + "sphere/cylinder containment boundary is below geometric resolution".into(), + )); + } + offset_sphere_cylinder_boolean(&sphere, &cylinder, sphere_is_a, operation, id, clearance) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/offset.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/offset.rs new file mode 100644 index 0000000..7dbae61 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_cylinder/offset.rs @@ -0,0 +1,344 @@ +use super::coaxial::coaxial_sphere_cylinder_boolean; +use crate::brep::{ + Accuracy, BrepEnvelope, CurveGeometry, Edge, EdgeGeometry, Face, FaceProvenance, FaceRole, + GeometryError, Orientation, PcurveGeometry, TrimRegion, Vertex, +}; +use crate::intersection::{intersect_faces, FaceView, IntersectionBranch, IntersectionGraph}; +use crate::math::{norm, sub}; +use crate::operations::modifying::boolean::assembly::{ + add_closed_intersection_loop, add_complement_input, finish_analytic_result, + intersection_loop_bounds, loop_containment, loop_regions, loop_sense, merge_selected_shells, + reverse_face, widen_trim_bounds, LoopRegion, +}; +use crate::operations::modifying::boolean::operands::{ + brep_face_source, required_id, CylinderInput, SphereInput, +}; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanResult}; + +struct ClassifiedLoop<'a> { + graph: &'a IntersectionGraph, + branch: &'a IntersectionBranch, + curve_definition: u32, + regions: [LoopRegion; 2], + reverse_selected: [bool; 2], +} + +struct IntersectionEdge { + definition: u32, + vertex: u32, + edge: u32, +} + +pub(super) fn offset_sphere_cylinder_boolean( + sphere: &SphereInput<'_>, + cylinder: &CylinderInput<'_>, + sphere_is_a: bool, + operation: BooleanOp, + id: String, + clearance: f64, +) -> Result { + let local = cylinder.frame.local(sphere.frame.origin); + let radial = local[0].hypot(local[1]); + let scale = sphere + .radius + .max(cylinder.radius) + .max(cylinder.height) + .max(1.0); + let roundoff = 128.0 * f64::EPSILON * scale; + if radial <= roundoff { + return coaxial_sphere_cylinder_boolean( + sphere, + cylinder, + sphere_is_a, + operation, + id, + clearance, + ); + } + let sphere_low = local[2] - sphere.radius; + let sphere_high = local[2] + sphere.radius; + if sphere_low <= clearance || cylinder.height - sphere_high <= clearance { + return Err(GeometryError::CoverageGap { + families: ["sphere".into(), "finite cylinder cap intersection".into()], + }); + } + let graph = intersect_faces( + FaceView { + brep: sphere.brep, + face: 0, + }, + FaceView { + brep: cylinder.brep, + face: 0, + }, + )?; + let (branch, curve_definition) = single_closed_branch(&graph, clearance)?; + let loop_inside_other = loop_containment( + sphere.brep, + cylinder.brep, + &graph, + branch, + "sphere/cylinder intersection loop interior is unclassified", + )?; + let (regions, reverse_selected) = loop_regions(operation, sphere_is_a, loop_inside_other); + let classified = ClassifiedLoop { + graph: &graph, + branch, + curve_definition, + regions, + reverse_selected, + }; + classified_loop_boolean(sphere, cylinder, sphere_is_a, operation, id, &classified) +} + +fn single_closed_branch( + graph: &IntersectionGraph, + clearance: f64, +) -> Result<(&IntersectionBranch, u32), GeometryError> { + if graph.coincident || !graph.contacts.is_empty() || graph.branches.len() != 1 { + return Err(GeometryError::CoverageGap { + families: ["sphere".into(), "multi-branch or tangent cylinder".into()], + }); + } + let branch = &graph.branches[0]; + if norm(sub(branch.endpoints[0], branch.endpoints[1])) > clearance { + return Err(GeometryError::CoverageGap { + families: ["sphere".into(), "open cylinder intersection branch".into()], + }); + } + let curve_definition = match graph.geometry.curves[branch.curve as usize] { + CurveGeometry::Intersection { definition } => definition, + _ => { + return Err(GeometryError::CoverageGap { + families: ["sphere".into(), "non-numerical cylinder branch".into()], + }) + } + }; + Ok((branch, curve_definition)) +} + +fn classified_loop_boolean( + sphere: &SphereInput<'_>, + cylinder: &CylinderInput<'_>, + sphere_is_a: bool, + operation: BooleanOp, + id: String, + classified: &ClassifiedLoop<'_>, +) -> Result { + let inputs = [sphere.brep, cylinder.brep]; + let mut out = BrepEnvelope::new( + id, + Accuracy::combined(sphere.brep.accuracy, cylinder.brep.accuracy), + )?; + let mut selected_faces = [None; 2]; + let mut selected_surfaces = [None; 2]; + let selected_shells = add_complement_sides( + &mut out, + inputs, + classified.regions, + &mut selected_faces, + &mut selected_surfaces, + )?; + merge_selected_shells(&mut out, &selected_shells); + for side in 0..2 { + if classified.regions[side] == LoopRegion::Patch { + selected_surfaces[side] = Some(out.geometry.surfaces.len() as u32); + out.geometry + .surfaces + .push(classified.graph.geometry.surfaces[side].clone()); + } + } + let loop_edge = add_intersection_edge(&mut out, classified, selected_surfaces)?; + add_loop_sides( + &mut out, + classified, + inputs, + &loop_edge, + &mut selected_faces, + selected_surfaces, + )?; + for side in 0..2 { + if classified.reverse_selected[side] { + reverse_face(&mut out, required_id(selected_faces[side])?); + } + } + finish_analytic_result( + out, + sphere.brep, + cylinder.brep, + sphere_is_a, + operation, + false, + ) +} + +fn add_complement_sides( + out: &mut BrepEnvelope, + inputs: [&BrepEnvelope; 2], + regions: [LoopRegion; 2], + selected_faces: &mut [Option; 2], + selected_surfaces: &mut [Option; 2], +) -> Result, GeometryError> { + let mut selected_shells = Vec::new(); + for side in 0..2 { + if regions[side] != LoopRegion::Complement { + continue; + } + add_complement_input( + out, + inputs[side], + 0, + &mut selected_surfaces[side], + &mut selected_shells, + |face| selected_faces[side] = Some(face), + )?; + } + Ok(selected_shells) +} + +fn add_intersection_edge( + out: &mut BrepEnvelope, + classified: &ClassifiedLoop<'_>, + selected_surfaces: [Option; 2], +) -> Result { + let mut definition_geometry = + classified.graph.geometry.intersections[classified.curve_definition as usize].clone(); + definition_geometry.surfaces = [ + selected_surfaces[definition_geometry.surfaces[0] as usize].ok_or_else(|| { + GeometryError::InvalidTopology( + "sphere-cylinder first support surface was not retained".into(), + ) + })?, + selected_surfaces[definition_geometry.surfaces[1] as usize].ok_or_else(|| { + GeometryError::InvalidTopology( + "sphere-cylinder second support surface was not retained".into(), + ) + })?, + ]; + let definition = out.geometry.intersections.len() as u32; + out.geometry.intersections.push(definition_geometry); + let curve = out.geometry.curves.len() as u32; + out.geometry + .curves + .push(CurveGeometry::Intersection { definition }); + let vertex = out.topology.vertices.len() as u32; + out.topology.vertices.push(Vertex { + id: vertex, + position: classified.branch.endpoints[0], + outgoing_halfedge: None, + tolerance: out.accuracy.geometric, + }); + let edge = out.topology.edges.len() as u32; + out.topology.edges.push(Edge { + id: edge, + geometry: EdgeGeometry::Curve { + curve, + range: classified.branch.range, + }, + halfedge: u32::MAX, + twin_halfedge: None, + tolerance: out.accuracy.geometric, + chart_seam: false, + }); + Ok(IntersectionEdge { + definition, + vertex, + edge, + }) +} + +fn add_loop_sides( + out: &mut BrepEnvelope, + classified: &ClassifiedLoop<'_>, + inputs: [&BrepEnvelope; 2], + loop_edge: &IntersectionEdge, + selected_faces: &mut [Option; 2], + selected_surfaces: [Option; 2], +) -> Result<(), GeometryError> { + for side in 0..2 { + let (pcurve, sense) = side_pcurve(classified, side, loop_edge.definition)?; + if classified.regions[side] == LoopRegion::Complement { + let face = required_id(selected_faces[side])?; + let loop_bounds = intersection_loop_bounds( + classified.graph, + classified.branch, + side, + out.accuracy.intersection, + )?; + widen_trim_bounds(out, face, loop_bounds)?; + let loop_id = add_closed_intersection_loop( + out, + loop_edge.edge, + loop_edge.vertex, + face, + pcurve, + sense, + true, + )?; + out.topology.faces[face as usize].trim.holes.push(loop_id); + } else { + let face = out.topology.faces.len() as u32; + let loop_id = add_closed_intersection_loop( + out, + loop_edge.edge, + loop_edge.vertex, + face, + pcurve, + sense, + false, + )?; + out.topology.faces.push(Face { + id: face, + key: format!("{}:intersection-patch", inputs[side].topology.faces[0].key), + surface: required_id(selected_surfaces[side])?, + sense: Orientation::Forward, + trim: TrimRegion { + chart: 0, + uv_bounds: intersection_loop_bounds( + classified.graph, + classified.branch, + side, + out.accuracy.intersection, + )?, + outer: loop_id, + holes: Vec::new(), + }, + shell_ref: Some(0), + provenance: FaceProvenance { + sources: vec![brep_face_source(inputs[side], 0)], + role: if classified.reverse_selected[side] { + FaceRole::Cut + } else { + FaceRole::Split + }, + reversed: classified.reverse_selected[side], + }, + }); + out.topology.shells[0].faces.push(face); + selected_faces[side] = Some(face); + } + } + Ok(()) +} + +fn side_pcurve( + classified: &ClassifiedLoop<'_>, + side: usize, + definition: u32, +) -> Result<(PcurveGeometry, Orientation), GeometryError> { + let pcurve = match classified.graph.geometry.pcurves[classified.branch.pcurves[side] as usize] { + PcurveGeometry::IntersectionSide { + side: intersection_side, + .. + } => PcurveGeometry::IntersectionSide { + definition, + side: intersection_side, + }, + _ => { + return Err(GeometryError::InvalidGeometry( + "numerical sphere-cylinder curve is missing an intersection-side pcurve".into(), + )) + } + }; + Ok((pcurve, loop_sense(side, classified.reverse_selected[side]))) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_sphere.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_sphere.rs new file mode 100644 index 0000000..24977e0 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/sphere_sphere.rs @@ -0,0 +1,268 @@ +use crate::brep::{ + unit, Accuracy, BrepEnvelope, Builder, CurveGeometry, FaceRole, Frame3, GeometryError, + GeometryQuality, GeometryStore, SolidRegion, SurfaceGeometry, +}; +use crate::intersection::intersect_surfaces; +use crate::math::{norm, sphere_sphere_relation, sub, Interval, Point3, Sign}; +use crate::operations::modifying::boolean::assembly::{append_sphere, patch, source, SpherePatch}; +use crate::operations::modifying::boolean::operands::{full_sphere, gap, SphereInput}; +use crate::operations::modifying::boolean::types::{ + BooleanOp, BooleanReport, BooleanResult, FaceMapping, +}; + +pub fn boolean_spheres( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: String, +) -> Result { + let (a, b) = sphere_operands(a, b)?; + let accuracy = Accuracy::combined(a.brep.accuracy, b.brep.accuracy); + let mut out = BrepEnvelope::new(id, accuracy)?; + let delta = sub(b.frame.origin, a.frame.origin); + let distance = norm(delta); + let [outer, inner] = + sphere_sphere_relation(a.frame.origin, a.radius, b.frame.origin, b.radius)?; + let coincident = distance == 0.0 && a.radius == b.radius; + let mut supports = sphere_supports(&a, &b); + let intersections = intersect_surfaces(&mut supports, 0, 1, accuracy)?; + let mut keep = |input: &SphereInput<'_>| -> Result<(), GeometryError> { + let shell = append_sphere(&mut out, input, false)?; + out.solids.push(SolidRegion { + outer_shell: shell, + cavity_shells: Vec::new(), + }); + Ok(()) + }; + if coincident { + if operation != BooleanOp::Subtraction { + keep(&a)?; + out.topology.faces[0].provenance.role = FaceRole::Coincident; + out.topology.faces[0].provenance.sources.push(source(&b)); + } + } else if outer != Sign::Negative { + match operation { + BooleanOp::Union => { + keep(&a)?; + keep(&b)?; + } + BooleanOp::Intersection => {} + BooleanOp::Subtraction => keep(&a)?, + } + } else if inner != Sign::Positive { + let a_outer = a.radius > b.radius; + match operation { + BooleanOp::Union => keep(if a_outer { &a } else { &b })?, + BooleanOp::Intersection => keep(if a_outer { &b } else { &a })?, + BooleanOp::Subtraction if a_outer => { + if inner == Sign::Zero { + return Err(GeometryError::UnresolvedIntersection( + "internally tangent cavity boundaries require contact topology".into(), + )); + } + if a.radius - b.radius - distance <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "cavity clearance is below geometric resolution".into(), + )); + } + let shell = append_sphere(&mut out, &a, false)?; + let cavity = append_sphere(&mut out, &b, true)?; + out.solids.push(SolidRegion { + outer_shell: shell, + cavity_shells: vec![cavity], + }); + } + BooleanOp::Subtraction => {} + } + } else { + let intersection = intersections.curves.first().ok_or_else(|| { + GeometryError::UnresolvedIntersection( + "transverse spheres have no intersection branch".into(), + ) + })?; + let circle = supports.curves[intersection.curve as usize].clone(); + out = transverse_caps(&out.id, &a, &b, operation, accuracy, delta, circle)?; + } + finish_sphere_sphere_result(out, &a, &b, operation, intersections.contacts, coincident) +} + +fn sphere_operands<'a>( + a: &'a BrepEnvelope, + b: &'a BrepEnvelope, +) -> Result<(SphereInput<'a>, SphereInput<'a>), GeometryError> { + a.validate()?; + b.validate()?; + let family = |brep: &BrepEnvelope| { + match brep.geometry.surfaces.first() { + Some(SurfaceGeometry::Sphere { .. }) => "sphere (canonical full sphere required)", + Some(SurfaceGeometry::Cylinder { .. }) => "cylinder", + Some(SurfaceGeometry::Cone { .. }) => "cone", + Some(SurfaceGeometry::Torus { .. }) => "torus", + Some(SurfaceGeometry::Plane { .. }) => "planar/compound solid", + None => "empty solid", + } + .to_string() + }; + let coverage = |error| match error { + GeometryError::CoverageGap { .. } => GeometryError::CoverageGap { + families: [family(a), family(b)], + }, + other => other, + }; + let a = full_sphere(a).map_err(coverage)?; + let b = full_sphere(b).map_err(coverage)?; + Ok((a, b)) +} + +fn sphere_supports(a: &SphereInput<'_>, b: &SphereInput<'_>) -> GeometryStore { + let mut supports = GeometryStore::new(); + supports.surfaces = vec![ + SurfaceGeometry::Sphere { + frame: a.frame, + radius: a.radius, + }, + SurfaceGeometry::Sphere { + frame: b.frame, + radius: b.radius, + }, + ]; + supports +} + +fn transverse_caps( + id: &str, + a: &SphereInput<'_>, + b: &SphereInput<'_>, + operation: BooleanOp, + accuracy: Accuracy, + delta: Point3, + circle: CurveGeometry, +) -> Result { + let (circle_frame, circle_radius) = match circle { + CurveGeometry::Circle { frame, radius } => (frame, radius), + _ => return Err(gap()), + }; + let axis = unit(delta)?; + let frame_a = Frame3 { + origin: a.frame.origin, + z: axis, + x: circle_frame.x, + y: circle_frame.y, + }; + let frame_b = Frame3 { + origin: b.frame.origin, + ..frame_a + }; + let latitudes = [(a, frame_a), (b, frame_b)].map(|(input, frame)| { + let local = frame.local(circle_frame.origin); + (local[2] / input.radius).asin() + }); + if circle_radius <= 4.0 * accuracy.geometric || latitudes.iter().any(|v| !v.is_finite()) { + return Err(GeometryError::UnresolvedIntersection( + "intersection cap is below geometric resolution".into(), + )); + } + let north_a = operation == BooleanOp::Intersection; + let north_b = operation == BooleanOp::Union; + check_retained_caps(a, b, latitudes, north_a, north_b, accuracy)?; + let mut builder = Builder::new(id.to_owned(), accuracy)?; + let vertex = builder.vertex(circle.elementary_point(0.0)?); + let edge = builder.edge(circle, Interval::new(0.0, std::f64::consts::TAU)?, false); + patch( + &mut builder, + a, + &SpherePatch { + frame: frame_a, + latitude: latitudes[0], + north: north_a, + shared_edge: edge, + shared_vertex: vertex, + }, + false, + )?; + patch( + &mut builder, + b, + &SpherePatch { + frame: frame_b, + latitude: latitudes[1], + north: north_b, + shared_edge: edge, + shared_vertex: vertex, + }, + operation == BooleanOp::Subtraction, + )?; + builder.finish_solid() +} + +fn check_retained_caps( + a: &SphereInput<'_>, + b: &SphereInput<'_>, + latitudes: [f64; 2], + north_a: bool, + north_b: bool, + accuracy: Accuracy, +) -> Result<(), GeometryError> { + for (input, latitude, north) in [(a, latitudes[0], north_a), (b, latitudes[1], north_b)] { + let cap_height = input.radius + * (1.0 + - if north { + latitude.sin() + } else { + -latitude.sin() + }); + if cap_height <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "retained spherical cap is below geometric resolution".into(), + )); + } + } + Ok(()) +} + +fn finish_sphere_sphere_result( + mut out: BrepEnvelope, + a: &SphereInput<'_>, + b: &SphereInput<'_>, + operation: BooleanOp, + contacts: Vec, + coincident: bool, +) -> Result { + out.revision = a + .brep + .revision + .max(b.brep.revision) + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("boolean revision overflow".into()))?; + out.validate()?; + let report = BooleanReport { + operation, + quality: GeometryQuality::Analytic, + contacts, + coincident, + face_mappings: [a, b] + .map(|input| { + let source = source(input); + let result_faces = out + .topology + .faces + .iter() + .filter(|face| { + face.provenance.sources.iter().any(|s| { + s.entity == source.entity + && s.body == source.body + && s.key == source.key + && s.face == source.face + }) + }) + .map(|face| face.id) + .collect(); + FaceMapping { + source, + result_faces, + } + }) + .into(), + }; + Ok(BooleanResult { brep: out, report }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/cap_provenance.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/cap_provenance.rs new file mode 100644 index 0000000..45c83e0 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/cap_provenance.rs @@ -0,0 +1,283 @@ +use super::facets::Facets; +use super::section::{area, coverage, face_region}; +use crate::brep::{ + BrepEnvelope, FaceProvenance, FaceRole, FaceSource, Frame3, GeometryError, GeometryQuality, + Shell, SolidRegion, Surface, SurfaceGeometry, +}; +use crate::geom2d::{intersections, winding, CurveRegion2, Pt2}; +use crate::math::{dot, Point3}; +use crate::operations::modifying::boolean::assembly::{ + analytic_face_mappings, assign_shell_faces, enclosing_solid, +}; +use crate::operations::modifying::boolean::operands::brep_face_source; +use crate::operations::modifying::boolean::types::{BooleanOp, BooleanReport, BooleanResult}; +use crate::query::{classify_point_in_shell, face_contains_uv, PointClassification}; + +pub(super) fn source_for_side( + brep: &BrepEnvelope, + point: Point3, + axis: Point3, + tolerance: f64, +) -> Result, GeometryError> { + let mut sources = Vec::new(); + for face in &brep.topology.faces { + let surface = brep.geometry.surface(face.surface)?; + let on = match surface { + SurfaceGeometry::Plane { frame } => { + let local = frame.local(point); + local[2].abs() <= tolerance && dot(frame.z, axis).abs() < 1.0 - 1e-10 + } + SurfaceGeometry::Cylinder { frame, radius } => { + let local = frame.local(point); + (local[0].hypot(local[1]) - radius).abs() <= tolerance + } + _ => false, + }; + if !on { + continue; + } + let hint = [ + face.trim.uv_bounds[0].midpoint(), + face.trim.uv_bounds[1].midpoint(), + ]; + let uv = surface.project(point, Some(hint))?; + match face_contains_uv(brep, face, uv)? { + Some(true) => sources.push(brep_face_source(brep, face.id)), + Some(false) => {} + None => return Err(coverage()), + } + } + Ok(sources) +} + +pub(super) fn cap_provenance( + host: &BrepEnvelope, + cutters: &[&BrepEnvelope], + base: Frame3, + level: f64, + region: &CurveRegion2, + tolerance: f64, +) -> Result { + let mut sources = source_for_cap_region(host, base, level, region, tolerance)?; + let mut cut = false; + for cutter in cutters { + let cutter_sources = source_for_cap_region(cutter, base, level, region, tolerance)?; + cut |= !cutter_sources.is_empty(); + sources.extend(cutter_sources); + } + if sources.is_empty() { + return Err(coverage()); + } + Ok(FaceProvenance { + sources, + role: if cut { FaceRole::Cut } else { FaceRole::Split }, + reversed: cut, + }) +} + +fn source_for_cap_region( + brep: &BrepEnvelope, + base: Frame3, + level: f64, + region: &CurveRegion2, + tolerance: f64, +) -> Result, GeometryError> { + let mut candidates = Vec::new(); + for face in &brep.topology.faces { + let SurfaceGeometry::Plane { frame } = brep.geometry.surface(face.surface)? else { + continue; + }; + if dot(frame.z, base.z).abs() > 1.0 - 1.0e-10 + && (base.local(frame.origin)[2] - level).abs() <= tolerance + { + candidates.push(face); + } + } + let mut sources = Vec::new(); + for face in candidates { + let source_region = face_region(brep, face, base)?; + if cap_regions_overlap(region, &source_region, tolerance) { + sources.push(brep_face_source(brep, face.id)); + } + } + Ok(sources) +} + +pub(super) fn cap_regions_overlap(a: &CurveRegion2, b: &CurveRegion2, tolerance: f64) -> bool { + for (region, other) in [(a, b), (b, a)] { + for ring in std::iter::once(®ion.outer).chain(®ion.holes) { + for edge in ring { + let mut parameters = vec![0.0, 1.0]; + for other_ring in std::iter::once(&other.outer).chain(&other.holes) { + for other_edge in other_ring { + parameters.extend( + intersections(edge, other_edge, tolerance) + .into_iter() + .filter_map(|point| edge.parameter(point, tolerance)), + ); + } + } + parameters.sort_by(f64::total_cmp); + parameters + .dedup_by(|left, right| (*left - *right).abs() * edge.length() <= tolerance); + for span in parameters.windows(2) { + let interval_length = (span[1] - span[0]) * edge.length(); + if interval_length <= tolerance * 4.0 { + continue; + } + let midpoint = (span[0] + span[1]) / 2.0; + let point = edge.point(midpoint); + let tangent = edge.tangent(midpoint); + let tangent_length = tangent.x.hypot(tangent.z); + if tangent_length <= tolerance { + continue; + } + let mut offset = (interval_length * 0.1).min(1.0e-4); + for _ in 0..8 { + if offset <= tolerance * 2.0 { + break; + } + let probe = Pt2::new( + point.x + tangent.z * offset / tangent_length, + point.z - tangent.x * offset / tangent_length, + ); + if contains_region_point(region, probe, tolerance) + && contains_region_point(other, probe, tolerance) + { + return true; + } + offset *= 0.5; + } + } + } + } + } + false +} + +fn contains_region_point(region: &CurveRegion2, point: Pt2, tolerance: f64) -> bool { + winding(point, ®ion.outer, tolerance) != 0 + && region + .holes + .iter() + .all(|hole| winding(point, hole, tolerance) == 0) +} + +pub(super) fn finish( + mut facets: Facets, + a: &BrepEnvelope, + cutters: &[&BrepEnvelope], +) -> Result { + let mut assigned = vec![false; facets.builder.brep.topology.faces.len()]; + let mut outer_shells = Vec::new(); + let mut cavity_shells = Vec::new(); + for start in 0..assigned.len() { + if assigned[start] { + continue; + } + let shell = facets.builder.brep.topology.shells.len() as u32; + let faces = assign_shell_faces( + &mut facets.builder.brep, + &mut assigned, + start, + shell, + coverage, + coverage, + )?; + let signed_volume = signed_shell_volume(&facets, &faces)?; + if !signed_volume.is_finite() || signed_volume.abs() <= facets.accuracy.geometric.powi(3) { + return Err(coverage()); + } + facets.builder.brep.topology.shells.push(Shell { + id: shell, + faces, + is_closed: true, + }); + if signed_volume > 0.0 { + outer_shells.push((shell, signed_volume)); + } else { + cavity_shells.push(shell); + } + } + for &(outer_shell, _) in &outer_shells { + facets.builder.brep.solids.push(SolidRegion { + outer_shell, + cavity_shells: Vec::new(), + }); + } + for cavity_shell in cavity_shells { + let index = enclosing_solid( + &facets.builder.brep, + &outer_shells, + cavity_shell, + |brep, faces, point| match classify_point_in_shell(brep, faces, point)? { + PointClassification::Inside => Ok(true), + PointClassification::Outside => Ok(false), + PointClassification::Boundary | PointClassification::Unknown => Err(coverage()), + }, + coverage, + )?; + facets.builder.brep.solids[index] + .cavity_shells + .push(cavity_shell); + } + finish_arc_extrusion_result(facets.builder.brep, a, cutters) +} + +fn signed_shell_volume(facets: &Facets, faces: &[u32]) -> Result { + let mut signed_volume = 0.0; + for &face_id in faces { + let face = &facets.builder.brep.topology.faces[face_id as usize]; + let SurfaceGeometry::Plane { frame } = facets + .builder + .brep + .geometry + .surfaces + .get(face.surface as usize) + .ok_or_else(coverage)? + else { + continue; + }; + let outward = dot(frame.z, facets.base.z) * face.sense.multiplier(); + if outward.abs() <= 1.0 - 1e-10 { + continue; + } + let region = face_region(&facets.builder.brep, face, facets.base)?; + let cap_area = area(®ion.outer).abs() + - region + .holes + .iter() + .map(|hole| area(hole).abs()) + .sum::(); + if !cap_area.is_finite() || cap_area <= facets.accuracy.geometric.powi(2) { + return Err(coverage()); + } + signed_volume += facets.base.local(frame.origin)[2] * outward * cap_area; + } + Ok(signed_volume) +} + +fn finish_arc_extrusion_result( + mut out: BrepEnvelope, + a: &BrepEnvelope, + cutters: &[&BrepEnvelope], +) -> Result { + out.revision = cutters + .iter() + .fold(a.revision, |revision, cutter| revision.max(cutter.revision)) + .checked_add(1) + .ok_or_else(coverage)?; + out.validate()?; + let face_mappings = + analytic_face_mappings(&out, std::iter::once(a).chain(cutters.iter().copied())); + Ok(BooleanResult { + report: BooleanReport { + operation: BooleanOp::Subtraction, + quality: GeometryQuality::Analytic, + contacts: Vec::new(), + coincident: true, + face_mappings, + }, + brep: out, + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/facets.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/facets.rs new file mode 100644 index 0000000..af7ba63 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/facets.rs @@ -0,0 +1,492 @@ +use super::section::{canonical_arc_start, coverage}; +use crate::brep::{ + boundary, padded_uv_bounds, plane_boundary, unit, uv_line, Accuracy, Builder, CurveGeometry, + FaceProvenance, Frame3, GeometryError, Orientation, SurfaceGeometry, Use, +}; +use crate::geom2d::{reversed_ring, CurveEdge2, CurveRegion2, Pt2}; +use crate::math::{add, cross, norm, scale, sub, Interval, Point3}; +use crate::operations::modifying::boolean::assembly::add_welded_vertex; +use std::collections::BTreeMap; + +pub(super) struct Facets { + pub(super) builder: Builder, + vertices: Vec<(Point3, u32)>, + edges: BTreeMap<(u32, u32, i64, i64, i64), (u32, u32, u32, bool)>, + pub(super) accuracy: Accuracy, + pub(super) base: Frame3, +} + +struct SideLoop { + lo: f64, + hi: f64, + lower_from: Point3, + lower_to: Point3, + bottom: (u32, u32, u32, Orientation), + top: (u32, u32, u32, Orientation), + end_segments: Vec<(u32, u32, u32, Orientation, f64, f64)>, + start_segments: Vec<(u32, u32, u32, Orientation, f64, f64)>, +} + +pub(super) struct SidePatch { + pub(super) edge: CurveEdge2, + pub(super) levels: Vec, + pub(super) provenance: FaceProvenance, +} + +pub(super) fn same_side_patch(a: &SidePatch, b: &SidePatch, tolerance: f64) -> bool { + if std::mem::discriminant(&a.edge) != std::mem::discriminant(&b.edge) + || a.provenance.role != b.provenance.role + || a.provenance.reversed != b.provenance.reversed + || a.provenance.sources.len() != b.provenance.sources.len() + || !a + .provenance + .sources + .iter() + .zip(&b.provenance.sources) + .all(|(a, b)| { + a.entity == b.entity && a.body == b.body && a.key == b.key && a.face == b.face + }) + || (a.edge.length() - b.edge.length()).abs() > tolerance + { + return false; + } + [0.0, 0.5, 1.0].into_iter().all(|station| { + let a = a.edge.point(station); + let b = b.edge.point(station); + (a.x - b.x).hypot(a.z - b.z) <= tolerance + }) +} + +impl Facets { + pub(super) fn new(id: String, accuracy: Accuracy, base: Frame3) -> Result { + Ok(Self { + builder: Builder::new(id, accuracy)?, + vertices: Vec::new(), + edges: BTreeMap::new(), + accuracy, + base, + }) + } + + fn vertex(&mut self, point: Point3) -> u32 { + add_welded_vertex(&mut self.builder, &mut self.vertices, point, self.accuracy) + } + + fn edge( + &mut self, + from: Point3, + to: Point3, + middle: Point3, + circle: Option<([f64; 2], f64, f64, f64)>, + ) -> Result<(u32, u32, u32, Orientation), GeometryError> { + let from_id = self.vertex(from); + let to_id = self.vertex(to); + if from_id == to_id { + return Err(coverage()); + } + let step = self.accuracy.geometric / 4.0; + let key = ( + from_id.min(to_id), + from_id.max(to_id), + (middle[0] / step).round() as i64, + (middle[1] / step).round() as i64, + (middle[2] / step).round() as i64, + ); + if let Some(&(id, stored_from, stored_to, forward)) = self.edges.get(&key) { + let same = from_id == stored_from && to_id == stored_to; + let orientation = if same == forward { + Orientation::Forward + } else { + Orientation::Reverse + }; + return Ok((id, from_id, to_id, orientation)); + } + let (curve, range, forward) = if let Some((centre, radius, start, sweep)) = circle { + let origin = self + .base + .point([centre[0], centre[1], self.base.local(from)[2]]); + let frame = Frame3 { + origin, + ..self.base + }; + let start = canonical_arc_start(start, sweep); + let end = start + sweep; + ( + CurveGeometry::Circle { frame, radius }, + Interval::new(start.min(end), start.max(end))?, + sweep > 0.0, + ) + } else { + let delta = sub(to, from); + ( + CurveGeometry::Line { + origin: from, + direction: unit(delta)?, + }, + Interval::new(0.0, norm(delta))?, + true, + ) + }; + let id = self.builder.edge(curve, range, false); + self.edges.insert(key, (id, from_id, to_id, forward)); + Ok(( + id, + from_id, + to_id, + if forward { + Orientation::Forward + } else { + Orientation::Reverse + }, + )) + } + + fn edge_at( + &mut self, + edge: &CurveEdge2, + level: f64, + ) -> Result<(u32, u32, u32, Orientation), GeometryError> { + let point = |t| { + let p = edge.point(t); + self.base.point([p.x, p.z, level]) + }; + let curve = match edge { + CurveEdge2::Arc { + center, + radius, + start_angle, + sweep_angle, + } => Some((*center, *radius, *start_angle, *sweep_angle)), + CurveEdge2::Line { .. } => None, + }; + self.edge(point(0.0), point(1.0), point(0.5), curve) + } + + fn cap_uses( + &mut self, + ring: &[CurveEdge2], + level: f64, + frame: Frame3, + ) -> Result, GeometryError> { + let mut uses = Vec::new(); + for edge in ring { + let (id, from, to, sense) = self.edge_at(edge, level)?; + uses.push(plane_boundary(&self.builder, frame, id, from, to, sense)?); + } + Ok(uses) + } + + pub(super) fn cap( + &mut self, + regions: &[CurveRegion2], + level: f64, + up: bool, + provenance: FaceProvenance, + ) -> Result<(), GeometryError> { + let origin = self.base.point([0.0, 0.0, level]); + let frame = if up { + Frame3 { + origin, + ..self.base + } + } else { + Frame3 { + origin, + x: self.base.x, + y: scale(self.base.y, -1.0), + z: scale(self.base.z, -1.0), + } + }; + for region in regions { + let outer = if up { + region.outer.clone() + } else { + reversed_ring(®ion.outer) + }; + let holes = if up { + region.holes.clone() + } else { + region + .holes + .iter() + .map(|ring| reversed_ring(ring)) + .collect() + }; + let bounds = self.cap_bounds(&outer, &holes, level, frame); + let outer_uses = self.cap_uses(&outer, level, frame)?; + let hole_uses = holes + .iter() + .map(|ring| self.cap_uses(ring, level, frame)) + .collect::, _>>()?; + let id = self.builder.brep.topology.faces.len(); + self.builder.face_with_holes( + &format!("{}-cap-{id}", if up { "upper" } else { "lower" }), + SurfaceGeometry::Plane { frame }, + bounds, + outer_uses, + hole_uses, + )?; + self.builder.brep.topology.faces[id].provenance = provenance.clone(); + } + Ok(()) + } + + fn cap_bounds( + &self, + outer: &[CurveEdge2], + holes: &[Vec], + level: f64, + frame: Frame3, + ) -> [[f64; 2]; 2] { + let points = outer.iter().chain(holes.iter().flatten()).flat_map(|edge| { + let mut stations = vec![0.0, 0.5, 1.0]; + if let CurveEdge2::Arc { center, radius, .. } = edge { + for angle in [ + 0.0, + std::f64::consts::FRAC_PI_2, + std::f64::consts::PI, + 3.0 * std::f64::consts::FRAC_PI_2, + ] { + let point = Pt2::new( + center[0] + radius * angle.cos(), + center[1] + radius * angle.sin(), + ); + if let Some(station) = edge.parameter(point, self.accuracy.intersection) { + stations.push(station); + } + } + } + stations.into_iter().map(move |t| { + let point = edge.point(t); + frame.local(self.base.point([point.x, point.z, level])) + }) + }); + padded_uv_bounds(points, self.accuracy.geometric) + } + + pub(super) fn side( + &mut self, + edge: &CurveEdge2, + levels: &[f64], + provenance: FaceProvenance, + ) -> Result<(), GeometryError> { + let side_loop = self.side_loop(edge, levels)?; + let id = self.builder.brep.topology.faces.len(); + match edge { + CurveEdge2::Line { .. } => { + self.add_plane_side(id, &side_loop)?; + } + CurveEdge2::Arc { + center, + radius, + start_angle, + sweep_angle, + } => { + self.add_cylinder_side( + id, + &side_loop, + *center, + *radius, + *start_angle, + *sweep_angle, + )?; + } + } + self.builder.brep.topology.faces[id].provenance = provenance; + Ok(()) + } + + fn side_loop(&mut self, edge: &CurveEdge2, levels: &[f64]) -> Result { + if levels.len() < 2 || levels.windows(2).any(|span| span[1] <= span[0]) { + return Err(coverage()); + } + let lo = levels[0]; + let hi = *levels.last().ok_or_else(coverage)?; + let base = self.base; + let point = |t, level| { + let p = edge.point(t); + base.point([p.x, p.z, level]) + }; + let lower_from = point(0.0, lo); + let lower_to = point(1.0, lo); + let bottom = self.edge_at(edge, lo)?; + let top = self.edge_at(edge, hi)?; + let mut end_segments = Vec::with_capacity(levels.len() - 1); + let mut start_segments = Vec::with_capacity(levels.len() - 1); + for span in levels.windows(2) { + let from = point(1.0, span[0]); + let to = point(1.0, span[1]); + let (edge, from_id, to_id, sense) = + self.edge(from, to, scale(add(from, to), 0.5), None)?; + end_segments.push((edge, from_id, to_id, sense, span[0], span[1])); + } + for span in levels.windows(2).rev() { + let from = point(0.0, span[1]); + let to = point(0.0, span[0]); + let (edge, from_id, to_id, sense) = + self.edge(from, to, scale(add(from, to), 0.5), None)?; + start_segments.push((edge, from_id, to_id, sense, span[0], span[1])); + } + Ok(SideLoop { + lo, + hi, + lower_from, + lower_to, + bottom, + top, + end_segments, + start_segments, + }) + } + + fn add_plane_side(&mut self, id: usize, side_loop: &SideLoop) -> Result<(), GeometryError> { + let &SideLoop { + lo, + hi, + lower_from, + lower_to, + bottom: (bottom, bf, bt, bottom_sense), + top: (top, tf, tt, top_sense), + ref end_segments, + ref start_segments, + } = side_loop; + let edge_direction = unit(sub(lower_to, lower_from))?; + let normal = unit(cross(edge_direction, self.base.z))?; + let frame = Frame3::from_axis(lower_from, normal, edge_direction)?; + let mut uses = Vec::with_capacity(2 + 2 * end_segments.len()); + uses.push(plane_boundary( + &self.builder, + frame, + bottom, + bf, + bt, + bottom_sense, + )?); + for &(edge, from, to, sense, _, _) in end_segments { + uses.push(plane_boundary(&self.builder, frame, edge, from, to, sense)?); + } + uses.push(plane_boundary( + &self.builder, + frame, + top, + tt, + tf, + if top_sense == Orientation::Forward { + Orientation::Reverse + } else { + Orientation::Forward + }, + )?); + for &(edge, from, to, sense, _, _) in start_segments { + uses.push(plane_boundary(&self.builder, frame, edge, from, to, sense)?); + } + self.builder.face( + &format!("vertical-plane-{id}"), + SurfaceGeometry::Plane { frame }, + [ + [ + -self.accuracy.geometric, + norm(sub(lower_to, lower_from)) + self.accuracy.geometric, + ], + [-self.accuracy.geometric, hi - lo + self.accuracy.geometric], + ], + uses, + )?; + self.builder.brep.topology.faces[id].sense = Orientation::Reverse; + Ok(()) + } + + fn add_cylinder_side( + &mut self, + id: usize, + side_loop: &SideLoop, + center: [f64; 2], + radius: f64, + start_angle: f64, + sweep_angle: f64, + ) -> Result<(), GeometryError> { + let &SideLoop { lo, hi, .. } = side_loop; + let origin = self.base.point([center[0], center[1], 0.0]); + let frame = Frame3 { + origin, + ..self.base + }; + let start_angle = canonical_arc_start(start_angle, sweep_angle); + let end_angle = start_angle + sweep_angle; + let uses = cylinder_side_uses(side_loop, start_angle, end_angle); + self.builder.face( + &format!("vertical-cylinder-{id}"), + SurfaceGeometry::Cylinder { frame, radius }, + [ + [ + start_angle.min(end_angle) - self.accuracy.geometric / radius, + start_angle.max(end_angle) + self.accuracy.geometric / radius, + ], + [lo - self.accuracy.geometric, hi + self.accuracy.geometric], + ], + uses, + )?; + if sweep_angle > 0.0 { + self.builder.brep.topology.faces[id].sense = Orientation::Reverse; + } + Ok(()) + } +} + +fn cylinder_side_uses(side_loop: &SideLoop, start_angle: f64, end_angle: f64) -> Vec { + let &SideLoop { + lo, + hi, + bottom: (bottom, bf, bt, bottom_sense), + top: (top, tf, tt, top_sense), + ref end_segments, + ref start_segments, + .. + } = side_loop; + let mut uses = Vec::with_capacity(2 + 2 * end_segments.len()); + uses.push(boundary( + bottom, + bf, + bt, + bottom_sense, + uv_line([0.0, lo], [1.0, 0.0]), + )); + for &(edge, from, to, sense, start, end) in end_segments { + uses.push(boundary( + edge, + from, + to, + sense, + if sense == Orientation::Forward { + uv_line([end_angle, start], [0.0, 1.0]) + } else { + uv_line([end_angle, end], [0.0, -1.0]) + }, + )); + } + uses.push(boundary( + top, + tt, + tf, + if top_sense == Orientation::Forward { + Orientation::Reverse + } else { + Orientation::Forward + }, + uv_line([0.0, hi], [1.0, 0.0]), + )); + for &(edge, from, to, sense, start, end) in start_segments { + uses.push(boundary( + edge, + from, + to, + sense, + if sense == Orientation::Forward { + uv_line([start_angle, end], [0.0, -1.0]) + } else { + uv_line([start_angle, start], [0.0, 1.0]) + }, + )); + } + uses +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/mod.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/mod.rs new file mode 100644 index 0000000..72fcb94 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/mod.rs @@ -0,0 +1,470 @@ +mod cap_provenance; +mod facets; +mod section; +#[cfg(test)] +mod tests; + +use crate::brep::{ + Accuracy, BrepEnvelope, FaceProvenance, FaceRole, Frame3, GeometryError, SurfaceGeometry, +}; +use crate::geom2d::{boolean_curved_regions, CurveEdge2, CurveRegion2, PlanarBooleanOp}; +use crate::math::dot; +use crate::operations::modifying::boolean::operands::all_planar; +use crate::operations::modifying::boolean::types::BooleanResult; +use cap_provenance::{cap_provenance, finish, source_for_side}; +use facets::{same_side_patch, Facets, SidePatch}; +use section::{append_points, coverage, face_profile, sectional_regions, split_regions}; + +struct SectionFrame { + base: Frame3, + low: f64, + high: f64, +} + +struct SectionCut<'a> { + a: &'a BrepEnvelope, + cutters: &'a [&'a BrepEnvelope], + base: Frame3, + accuracy: Accuracy, +} + +pub(crate) fn subtract_vertical_arc_extrusion( + a: &BrepEnvelope, + b: &BrepEnvelope, + id: String, +) -> Result { + subtract_vertical_arc_extrusion_batch(a, &[b], id) +} + +pub(crate) fn subtract_vertical_arc_extrusion_batch( + a: &BrepEnvelope, + cutters: &[&BrepEnvelope], + id: String, +) -> Result { + if cutters.is_empty() { + return Err(coverage()); + } + let original_host = a + .topology + .faces + .iter() + .any(|face| face.key == "top" || face.key == "upper_cap"); + if !a + .geometry + .surfaces + .iter() + .any(|surface| matches!(surface, SurfaceGeometry::Cylinder { .. })) + { + return Err(coverage()); + } + if cutters + .iter() + .any(|cutter| cutter.solids.len() != 1 || !all_planar(cutter)) + { + return Err(coverage()); + } + let accuracy = cutters.iter().fold(a.accuracy, |accuracy, cutter| { + Accuracy::combined(accuracy, cutter.accuracy) + }); + let SectionFrame { base, low, high } = section_frame(a, accuracy)?; + let original_profile = if original_host { + Some(face_profile(a, base, high - low, accuracy.intersection)?) + } else { + None + }; + let cutter_profiles = cutter_profile_spans(cutters, base, accuracy)?; + let section = SectionCut { + a, + cutters, + base, + accuracy, + }; + let levels = section_levels(§ion, original_host, low, high, &cutter_profiles); + let mut layers = layer_regions(§ion, &levels, &original_profile, &cutter_profiles)?; + let (mut exposed_up, mut exposed_down) = exposed_regions(&layers, accuracy)?; + split_at_shared_points(&mut layers, &mut exposed_up, &mut exposed_down, accuracy); + let mut facets = Facets::new(id, accuracy, base)?; + let mut side_patches = Vec::::new(); + add_caps(&mut facets, §ion, &[], &layers[0], 0.0, false)?; + add_layer_faces( + &mut facets, + &mut side_patches, + §ion, + &layers, + &levels, + &exposed_up, + &exposed_down, + )?; + add_caps( + &mut facets, + §ion, + &[], + layers.last().ok_or_else(coverage)?, + high - low, + true, + )?; + add_sides(&mut facets, &side_patches, accuracy)?; + finish(facets, a, cutters) +} + +fn section_frame(a: &BrepEnvelope, accuracy: Accuracy) -> Result { + let host_top = host_top_frame(a)?; + let positions = a + .topology + .vertices + .iter() + .map(|vertex| host_top.local(vertex.position)[2]) + .collect::>(); + let low = positions.iter().copied().fold(f64::INFINITY, f64::min); + let high = positions.iter().copied().fold(f64::NEG_INFINITY, f64::max); + if !low.is_finite() || high - low <= accuracy.geometric * 4.0 { + return Err(coverage()); + } + let base = Frame3 { + origin: host_top.point([0.0, 0.0, low]), + ..host_top + }; + Ok(SectionFrame { base, low, high }) +} + +fn host_top_frame(a: &BrepEnvelope) -> Result { + let cap_axes = a + .topology + .faces + .iter() + .filter(|face| { + face.key == "top" + || face.key == "upper_cap" + || face.key.ends_with(":top") + || face.key.starts_with("upper-cap-") + }) + .filter_map( + |face| match a.geometry.surfaces.get(face.surface as usize) { + Some(SurfaceGeometry::Plane { frame }) => Some(frame.z), + _ => None, + }, + ) + .collect::>(); + let axis = a + .topology + .faces + .iter() + .find_map( + |face| match a.geometry.surfaces.get(face.surface as usize) { + Some(SurfaceGeometry::Cylinder { frame, .. }) + if cap_axes + .iter() + .any(|cap| dot(*cap, frame.z).abs() > 1.0 - 1e-10) => + { + Some(frame.z) + } + _ => None, + }, + ) + .ok_or_else(coverage)?; + a.topology + .faces + .iter() + .filter_map( + |face| match a.geometry.surfaces.get(face.surface as usize) { + Some(SurfaceGeometry::Plane { frame }) if dot(frame.z, axis) > 1.0 - 1e-10 => { + Some(*frame) + } + _ => None, + }, + ) + .max_by(|left, right| dot(left.origin, axis).total_cmp(&dot(right.origin, axis))) + .ok_or_else(coverage) +} + +fn cutter_profile_spans<'a>( + cutters: &[&'a BrepEnvelope], + base: Frame3, + accuracy: Accuracy, +) -> Result, GeometryError> { + cutters + .iter() + .map(|cutter| { + let cutter_levels = cutter + .topology + .vertices + .iter() + .map(|vertex| base.local(vertex.position)[2]) + .collect::>(); + let lo = cutter_levels.iter().copied().fold(f64::INFINITY, f64::min); + let hi = cutter_levels + .iter() + .copied() + .fold(f64::NEG_INFINITY, f64::max); + if !lo.is_finite() + || hi - lo <= accuracy.geometric * 4.0 + || cutter_levels.iter().any(|level| { + (level - lo).abs() > accuracy.geometric + && (level - hi).abs() > accuracy.geometric + }) + { + return Err(coverage()); + } + Ok(( + *cutter, + lo, + hi, + face_profile(cutter, base, hi, accuracy.intersection)?, + )) + }) + .collect::, GeometryError>>() +} + +fn section_levels( + section: &SectionCut<'_>, + original_host: bool, + low: f64, + high: f64, + cutter_profiles: &[(&BrepEnvelope, f64, f64, CurveRegion2)], +) -> Vec { + let &SectionCut { + a, base, accuracy, .. + } = section; + let mut levels = vec![0.0, high - low]; + if !original_host { + for vertex in &a.topology.vertices { + let level = base.local(vertex.position)[2]; + if level > accuracy.geometric && level < high - low - accuracy.geometric { + levels.push(level); + } + } + } + for (_, lo, hi, _) in cutter_profiles { + for level in [*lo, *hi] { + if level > accuracy.geometric && level < high - low - accuracy.geometric { + levels.push(level); + } + } + } + levels.sort_by(f64::total_cmp); + levels.dedup_by(|left, right| (*left - *right).abs() <= accuracy.geometric); + levels +} + +fn layer_regions( + section: &SectionCut<'_>, + levels: &[f64], + original_profile: &Option, + cutter_profiles: &[(&BrepEnvelope, f64, f64, CurveRegion2)], +) -> Result>, GeometryError> { + let &SectionCut { + a, base, accuracy, .. + } = section; + levels + .windows(2) + .map(|span| { + let middle = (span[0] + span[1]) / 2.0; + let host_regions = if let Some(profile) = original_profile { + vec![profile.clone()] + } else { + sectional_regions(a, base, middle, accuracy.intersection)? + }; + let active = cutter_profiles + .iter() + .filter(|(_, lo, hi, _)| middle > *lo && middle < *hi) + .map(|(_, _, _, profile)| profile.clone()) + .collect::>(); + boolean_curved_regions( + &host_regions, + &active, + PlanarBooleanOp::Subtraction, + accuracy.intersection, + ) + }) + .collect::, _>>() +} + +fn exposed_regions( + layers: &[Vec], + accuracy: Accuracy, +) -> Result<(Vec>, Vec>), GeometryError> { + let exposed_up = (1..layers.len()) + .map(|index| { + boolean_curved_regions( + &layers[index - 1], + &layers[index], + PlanarBooleanOp::Subtraction, + accuracy.intersection, + ) + }) + .collect::, _>>()?; + let exposed_down = (1..layers.len()) + .map(|index| { + boolean_curved_regions( + &layers[index], + &layers[index - 1], + PlanarBooleanOp::Subtraction, + accuracy.intersection, + ) + }) + .collect::, _>>()?; + Ok((exposed_up, exposed_down)) +} + +fn split_at_shared_points( + layers: &mut [Vec], + exposed_up: &mut [Vec], + exposed_down: &mut [Vec], + accuracy: Accuracy, +) { + let mut all_points = Vec::new(); + for region in layers.iter() { + append_points(region, &mut all_points); + } + for region in exposed_up.iter().chain(exposed_down.iter()) { + append_points(region, &mut all_points); + } + for region in layers.iter_mut() { + split_regions(region, &all_points, accuracy.intersection); + } + for region in exposed_up.iter_mut().chain(exposed_down.iter_mut()) { + split_regions(region, &all_points, accuracy.intersection); + } +} + +fn add_caps( + facets: &mut Facets, + section: &SectionCut<'_>, + cutters: &[&BrepEnvelope], + regions: &[CurveRegion2], + level: f64, + up: bool, +) -> Result<(), GeometryError> { + let &SectionCut { + a, base, accuracy, .. + } = section; + for region in regions { + let provenance = cap_provenance(a, cutters, base, level, region, accuracy.intersection)?; + facets.cap(std::slice::from_ref(region), level, up, provenance)?; + } + Ok(()) +} + +fn add_layer_faces( + facets: &mut Facets, + side_patches: &mut Vec, + section: &SectionCut<'_>, + layers: &[Vec], + levels: &[f64], + exposed_up: &[Vec], + exposed_down: &[Vec], +) -> Result<(), GeometryError> { + let cutters = section.cutters; + for (index, regions) in layers.iter().enumerate() { + let lo = levels[index]; + let hi = levels[index + 1]; + for region in regions { + for ring in std::iter::once(®ion.outer).chain(®ion.holes) { + for edge in ring { + side_patches.push(side_patch(section, edge, lo, hi)?); + } + } + } + if index + 1 < layers.len() { + let cap_level = levels[index + 1]; + add_caps( + facets, + section, + cutters, + &exposed_up[index], + cap_level, + true, + )?; + add_caps( + facets, + section, + cutters, + &exposed_down[index], + cap_level, + false, + )?; + } + } + Ok(()) +} + +fn side_patch( + section: &SectionCut<'_>, + edge: &CurveEdge2, + lo: f64, + hi: f64, +) -> Result { + let &SectionCut { + a, + cutters, + base, + accuracy, + } = section; + let p = edge.point(0.5); + let middle = base.point([p.x, p.z, (lo + hi) / 2.0]); + let from_a = source_for_side(a, middle, base.z, accuracy.intersection)?; + let mut from_b = Vec::new(); + for cutter in cutters { + from_b.extend(source_for_side( + cutter, + middle, + base.z, + accuracy.intersection, + )?); + } + let cut = from_a.is_empty() && !from_b.is_empty(); + let sources = if cut { from_b } else { from_a }; + if sources.is_empty() { + return Err(coverage()); + } + Ok(SidePatch { + edge: edge.clone(), + levels: vec![lo, hi], + provenance: FaceProvenance { + sources, + role: if cut { FaceRole::Cut } else { FaceRole::Split }, + reversed: cut, + }, + }) +} + +fn add_sides( + facets: &mut Facets, + side_patches: &[SidePatch], + accuracy: Accuracy, +) -> Result<(), GeometryError> { + let mut consumed = vec![false; side_patches.len()]; + for first in 0..side_patches.len() { + if consumed[first] { + continue; + } + consumed[first] = true; + let mut levels = side_patches[first].levels.clone(); + loop { + let candidates = (first + 1..side_patches.len()) + .filter(|&index| { + !consumed[index] + && (side_patches[index].levels[0] - levels[levels.len() - 1]).abs() + <= accuracy.geometric + && same_side_patch( + &side_patches[first], + &side_patches[index], + accuracy.geometric / 4.0, + ) + }) + .collect::>(); + if candidates.len() != 1 { + break; + } + let next = candidates[0]; + consumed[next] = true; + levels.push(side_patches[next].levels[1]); + } + facets.side( + &side_patches[first].edge, + &levels, + side_patches[first].provenance.clone(), + )?; + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/section.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/section.rs new file mode 100644 index 0000000..434d472 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/section.rs @@ -0,0 +1,502 @@ +use crate::brep::{ + coverage_gap, BrepEnvelope, CurveGeometry, EdgeGeometry, Face, Frame3, GeometryError, HalfEdge, + Orientation, SurfaceGeometry, +}; +use crate::geom2d::{reversed_ring, winding, CurveEdge2, CurveRegion2, Pt2}; +use crate::math::{dot, Point3}; + +struct FaceBoundary { + boundary: Vec<(u32, Point3, Point3)>, + lowest: f64, + highest: f64, +} + +pub(super) fn canonical_arc_start(start: f64, sweep: f64) -> f64 { + let lower = start.min(start + sweep); + lower.rem_euclid(std::f64::consts::TAU) + start - lower +} + +pub(super) fn face_profile( + host: &BrepEnvelope, + base: Frame3, + level: f64, + tolerance: f64, +) -> Result { + let face = host + .topology + .faces + .iter() + .find(|face| { + matches!(host.geometry.surfaces.get(face.surface as usize), + Some(SurfaceGeometry::Plane { frame }) if dot(frame.z, base.z) > 1.0 - 1e-10 + && (base.local(frame.origin)[2] - level).abs() <= tolerance) + }) + .ok_or_else(coverage)?; + face_region(host, face, base) +} + +pub(super) fn coverage() -> GeometryError { + coverage_gap("vertical arc-edged extrusion", "planar cutter") +} + +pub(super) fn face_region( + host: &BrepEnvelope, + face: &Face, + base: Frame3, +) -> Result { + let mut edges = loop_edges(host, face.trim.outer, base)?; + if area(&edges) > 0.0 { + edges = reversed_ring(&edges); + } + let holes = face + .trim + .holes + .iter() + .map(|loop_id| { + let mut ring = loop_edges(host, *loop_id, base)?; + if area(&ring) < 0.0 { + ring = reversed_ring(&ring); + } + Ok(ring) + }) + .collect::, GeometryError>>()?; + Ok(CurveRegion2 { + outer: edges, + holes, + }) +} + +fn loop_edges( + host: &BrepEnvelope, + loop_id: u32, + base: Frame3, +) -> Result, GeometryError> { + let start = host + .topology + .loops + .get(loop_id as usize) + .ok_or_else(coverage)? + .start_halfedge; + let mut current = start; + let mut edges = Vec::new(); + loop { + let halfedge = host + .topology + .halfedges + .get(current as usize) + .ok_or_else(coverage)?; + edges.push(halfedge_edge(host, halfedge, base)?); + current = halfedge.next.ok_or_else(coverage)?; + if current == start { + break; + } + if edges.len() > host.topology.halfedges.len() { + return Err(coverage()); + } + } + if edges.len() == 1 { + if let CurveEdge2::Arc { + center, + radius, + start_angle, + sweep_angle, + } = edges[0].clone() + { + if sweep_angle.abs() >= std::f64::consts::TAU - 1e-9 { + edges = vec![ + CurveEdge2::Arc { + center, + radius, + start_angle, + sweep_angle: sweep_angle / 2.0, + }, + CurveEdge2::Arc { + center, + radius, + start_angle: start_angle + sweep_angle / 2.0, + sweep_angle: sweep_angle / 2.0, + }, + ]; + } + } + } + Ok(edges) +} + +fn halfedge_edge( + host: &BrepEnvelope, + halfedge: &HalfEdge, + base: Frame3, +) -> Result { + let from = base.local( + host.topology + .vertices + .get(halfedge.from as usize) + .ok_or_else(coverage)? + .position, + ); + let to = base.local( + host.topology + .vertices + .get(halfedge.to as usize) + .ok_or_else(coverage)? + .position, + ); + let edge = host + .topology + .edges + .get(halfedge.edge as usize) + .ok_or_else(coverage)?; + let EdgeGeometry::Curve { curve, range } = edge.geometry else { + return Err(coverage()); + }; + let curve = host + .geometry + .curves + .get(curve as usize) + .ok_or_else(coverage)?; + Ok(match curve { + CurveGeometry::Line { .. } => CurveEdge2::Line { + from: [from[0], from[1]], + to: [to[0], to[1]], + }, + CurveGeometry::Circle { frame, radius } => { + if dot(frame.z, base.z).abs() < 1.0 - 1e-10 { + return Err(coverage()); + } + let centre = base.local(frame.origin); + let start_angle = (from[1] - centre[1]).atan2(from[0] - centre[0]); + let sense = if halfedge.geometry_use.sense == Orientation::Forward { + 1.0 + } else { + -1.0 + }; + CurveEdge2::Arc { + center: [centre[0], centre[1]], + radius: *radius, + start_angle, + sweep_angle: sense * dot(frame.z, base.z).signum() * (range.hi - range.lo), + } + } + _ => return Err(coverage()), + }) +} + +pub(super) fn area(ring: &[CurveEdge2]) -> f64 { + ring.iter().map(CurveEdge2::twice_area).sum::() / 2.0 +} + +pub(super) fn sectional_regions( + host: &BrepEnvelope, + base: Frame3, + level: f64, + tolerance: f64, +) -> Result, GeometryError> { + let edges = section_edges(host, base, level, tolerance)?; + let rings = section_rings(edges, tolerance)?; + let samples = ring_samples(&rings, tolerance)?; + let depths = samples + .iter() + .enumerate() + .map(|(index, sample)| { + rings + .iter() + .enumerate() + .filter(|(other, ring)| *other != index && winding(*sample, ring, tolerance) != 0) + .count() + }) + .collect::>(); + nested_regions(&rings, &samples, &depths, tolerance) +} + +fn section_edges( + host: &BrepEnvelope, + base: Frame3, + level: f64, + tolerance: f64, +) -> Result, GeometryError> { + let mut edges = Vec::new(); + for face in &host.topology.faces { + let FaceBoundary { + boundary, + lowest, + highest, + } = face_boundary(host, face, base)?; + if level <= lowest + tolerance || level >= highest - tolerance { + continue; + } + if !face.trim.holes.is_empty() { + return Err(coverage()); + } + match &host.geometry.surfaces[face.surface as usize] { + SurfaceGeometry::Plane { frame } if dot(frame.z, base.z).abs() > 1.0e-10 => { + return Err(coverage()); + } + SurfaceGeometry::Cylinder { frame, .. } + if dot(frame.z, base.z).abs() < 1.0 - 1.0e-10 => + { + return Err(coverage()); + } + SurfaceGeometry::Plane { .. } | SurfaceGeometry::Cylinder { .. } => {} + _ => return Err(coverage()), + } + let bottom = boundary + .iter() + .filter(|(_, from, to)| { + (base.local(*from)[2] - lowest).abs() <= tolerance + && (base.local(*to)[2] - lowest).abs() <= tolerance + }) + .collect::>(); + if bottom.len() != 1 { + return Err(coverage()); + } + let &(halfedge_id, from, to) = bottom[0]; + add_bottom_edges(&mut edges, host, base, halfedge_id, from, to)?; + } + Ok(edges) +} + +fn face_boundary( + host: &BrepEnvelope, + face: &Face, + base: Frame3, +) -> Result { + let start = host.topology.loops[face.trim.outer as usize].start_halfedge; + let mut current = start; + let mut lowest = f64::INFINITY; + let mut highest = f64::NEG_INFINITY; + let mut boundary = Vec::new(); + loop { + let halfedge = &host.topology.halfedges[current as usize]; + let from = host.topology.vertices[halfedge.from as usize].position; + let to = host.topology.vertices[halfedge.to as usize].position; + for point in [from, to] { + let height = base.local(point)[2]; + lowest = lowest.min(height); + highest = highest.max(height); + } + boundary.push((current, from, to)); + current = halfedge.next.ok_or_else(coverage)?; + if current == start { + break; + } + if boundary.len() > host.topology.halfedges.len() { + return Err(coverage()); + } + } + Ok(FaceBoundary { + boundary, + lowest, + highest, + }) +} + +fn add_bottom_edges( + edges: &mut Vec, + host: &BrepEnvelope, + base: Frame3, + halfedge_id: u32, + from: Point3, + to: Point3, +) -> Result<(), GeometryError> { + let halfedge = &host.topology.halfedges[halfedge_id as usize]; + let edge = &host.topology.edges[halfedge.edge as usize]; + let EdgeGeometry::Curve { curve, range } = edge.geometry else { + return Err(coverage()); + }; + let from = base.local(from); + let to = base.local(to); + match &host.geometry.curves[curve as usize] { + CurveGeometry::Line { .. } => edges.push(CurveEdge2::Line { + from: [from[0], from[1]], + to: [to[0], to[1]], + }), + CurveGeometry::Circle { frame, radius } => { + if dot(frame.z, base.z).abs() < 1.0 - 1.0e-10 { + return Err(coverage()); + } + let centre = base.local(frame.origin); + let start_angle = (from[1] - centre[1]).atan2(from[0] - centre[0]); + let sense = if halfedge.geometry_use.sense == Orientation::Forward { + 1.0 + } else { + -1.0 + }; + let sweep = sense * dot(frame.z, base.z).signum() * (range.hi - range.lo); + if sweep.abs() >= std::f64::consts::TAU - 1.0e-9 { + for half in 0..2 { + edges.push(CurveEdge2::Arc { + center: [centre[0], centre[1]], + radius: *radius, + start_angle: start_angle + sweep * half as f64 / 2.0, + sweep_angle: sweep / 2.0, + }); + } + } else { + edges.push(CurveEdge2::Arc { + center: [centre[0], centre[1]], + radius: *radius, + start_angle, + sweep_angle: sweep, + }); + } + } + _ => return Err(coverage()), + } + Ok(()) +} + +fn section_rings( + mut edges: Vec, + tolerance: f64, +) -> Result>, GeometryError> { + let mut rings = Vec::>::new(); + while let Some(first) = edges.pop() { + let start = first.point(0.0); + let mut end = first.point(1.0); + let mut ring = vec![first]; + while (end.x - start.x).hypot(end.z - start.z) > tolerance { + let matches = edges + .iter() + .enumerate() + .filter_map(|(index, edge)| { + let from = edge.point(0.0); + let to = edge.point(1.0); + let forward = (from.x - end.x).hypot(from.z - end.z) <= tolerance; + let reverse = (to.x - end.x).hypot(to.z - end.z) <= tolerance; + (forward || reverse).then_some((index, reverse)) + }) + .collect::>(); + if matches.len() != 1 { + return Err(coverage()); + } + let (index, reverse) = matches[0]; + let mut next = edges.swap_remove(index); + if reverse { + next = next.reversed(); + } + end = next.point(1.0); + ring.push(next); + } + if area(&ring).abs() <= tolerance * tolerance { + return Err(coverage()); + } + rings.push(ring); + } + Ok(rings) +} + +fn ring_samples(rings: &[Vec], tolerance: f64) -> Result, GeometryError> { + let mut samples = Vec::new(); + for ring in rings { + let edge = &ring[0]; + let middle = edge.point(0.5); + let before = edge.point(0.49); + let after = edge.point(0.51); + let tangent = Pt2::new(after.x - before.x, after.z - before.z); + let length = tangent.x.hypot(tangent.z); + if length <= tolerance { + return Err(coverage()); + } + let offset = (length * 0.1).min(1.0e-4).max(tolerance * 16.0); + let normal = Pt2::new(-tangent.z * offset / length, tangent.x * offset / length); + let left = Pt2::new(middle.x + normal.x, middle.z + normal.z); + let right = Pt2::new(middle.x - normal.x, middle.z - normal.z); + let left_inside = winding(left, ring, tolerance) != 0; + let right_inside = winding(right, ring, tolerance) != 0; + let sample = match (left_inside, right_inside) { + (true, false) => left, + (false, true) => right, + _ => return Err(coverage()), + }; + samples.push(sample); + } + Ok(samples) +} + +fn nested_regions( + rings: &[Vec], + samples: &[Pt2], + depths: &[usize], + tolerance: f64, +) -> Result, GeometryError> { + let mut regions = Vec::<(usize, CurveRegion2)>::new(); + for (index, ring) in rings.iter().enumerate() { + if depths[index] % 2 == 0 { + regions.push(( + index, + CurveRegion2 { + outer: if area(ring) < 0.0 { + ring.clone() + } else { + reversed_ring(ring) + }, + holes: Vec::new(), + }, + )); + } + } + for (index, ring) in rings.iter().enumerate() { + if depths[index] % 2 == 0 { + continue; + } + let parent = regions + .iter() + .enumerate() + .filter(|(_, (outer, region))| { + depths[*outer] + 1 == depths[index] + && winding(samples[index], ®ion.outer, tolerance) != 0 + }) + .map(|(parent, (_, region))| (parent, area(®ion.outer).abs())) + .min_by(|left, right| left.1.total_cmp(&right.1)) + .map(|(parent, _)| parent) + .ok_or_else(coverage)?; + regions[parent].1.holes.push(if area(ring) > 0.0 { + ring.clone() + } else { + reversed_ring(ring) + }); + } + Ok(regions.into_iter().map(|(_, region)| region).collect()) +} + +pub(super) fn append_points(regions: &[CurveRegion2], points: &mut Vec<[f64; 2]>) { + for region in regions { + for ring in std::iter::once(®ion.outer).chain(®ion.holes) { + points.extend(ring.iter().map(|edge| { + let p = edge.point(0.0); + [p.x, p.z] + })); + } + } +} + +pub(super) fn split_regions(regions: &mut [CurveRegion2], points: &[[f64; 2]], tolerance: f64) { + for region in regions { + region.outer = split_ring(®ion.outer, points, tolerance); + for hole in &mut region.holes { + *hole = split_ring(hole, points, tolerance); + } + } +} + +fn split_ring(ring: &[CurveEdge2], points: &[[f64; 2]], tolerance: f64) -> Vec { + ring.iter() + .flat_map(|edge| { + let mut stations = vec![0.0, 1.0]; + for point in points { + if let Some(station) = edge.parameter(Pt2::new(point[0], point[1]), tolerance) { + if station > 1e-10 && station < 1.0 - 1e-10 { + stations.push(station); + } + } + } + stations.sort_by(f64::total_cmp); + stations.dedup_by(|a, b| (*a - *b).abs() < 1e-10); + stations + .windows(2) + .map(|span| edge.slice(span[0], span[1])) + .collect::>() + }) + .collect() +} diff --git a/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/tests.rs b/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/tests.rs new file mode 100644 index 0000000..b3f1200 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/handlers/vertical_arc_extrusion/tests.rs @@ -0,0 +1,57 @@ +use super::cap_provenance::cap_regions_overlap; +use crate::geom2d::{reversed_ring, CurveEdge2, CurveRegion2}; + +fn rectangle(x0: f64, z0: f64, x1: f64, z1: f64) -> CurveRegion2 { + let points = [[x0, z0], [x0, z1], [x1, z1], [x1, z0]]; + CurveRegion2 { + outer: (0..4) + .map(|index| CurveEdge2::Line { + from: points[index], + to: points[(index + 1) % 4], + }) + .collect(), + holes: Vec::new(), + } +} + +#[test] +fn cap_overlap_requires_positive_shared_area() { + let host = rectangle(0.0, 0.0, 2.0, 2.0); + assert!(cap_regions_overlap( + &host, + &rectangle(1.0, 1.0, 3.0, 3.0), + 1e-7 + )); + assert!(cap_regions_overlap( + &host, + &rectangle(0.5, 0.5, 1.5, 1.5), + 1e-7 + )); + assert!(!cap_regions_overlap( + &host, + &rectangle(2.0, 0.0, 3.0, 1.0), + 1e-7 + )); + assert!(!cap_regions_overlap( + &host, + &rectangle(3.0, 0.0, 4.0, 1.0), + 1e-7 + )); +} + +#[test] +fn cap_overlap_excludes_a_cutter_inside_a_hole() { + let mut host = rectangle(0.0, 0.0, 3.0, 3.0); + host.holes + .push(reversed_ring(&rectangle(1.0, 1.0, 2.0, 2.0).outer)); + assert!(!cap_regions_overlap( + &host, + &rectangle(1.2, 1.2, 1.8, 1.8), + 1e-7 + )); + assert!(cap_regions_overlap( + &host, + &rectangle(0.8, 1.2, 1.2, 1.8), + 1e-7 + )); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/mod.rs b/main/opengeometry/src/operations/modifying/boolean/mod.rs new file mode 100644 index 0000000..875071a --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/mod.rs @@ -0,0 +1,20 @@ +mod assembly; +mod batch; +mod dispatch; +mod handlers; +mod multi_tool; +mod operands; +mod shell; +#[cfg(test)] +mod tests; +mod types; + +pub use batch::{ + subtract_planar_cutters, subtract_planar_cutters_outcome_with_handlers, + subtract_planar_cutters_with_handlers, +}; +pub use dispatch::{boolean_brep, boolean_brep_outcome_with_handlers, boolean_brep_with_handlers}; +pub use handlers::{boolean_boxes, boolean_spheres}; +pub use multi_tool::{multi_tool_boolean, MultiToolOutcome}; +pub use shell::shell_brep; +pub use types::{BooleanOp, BooleanReport, BooleanResult}; diff --git a/main/opengeometry/src/operations/modifying/boolean/multi_tool.rs b/main/opengeometry/src/operations/modifying/boolean/multi_tool.rs new file mode 100644 index 0000000..c486e60 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/multi_tool.rs @@ -0,0 +1,88 @@ +use super::batch::subtract_planar_cutters_outcome_with_handlers; +use super::dispatch::boolean_brep_outcome_with_handlers; +use super::types::{BooleanOp, BooleanResult}; +use crate::brep::{BrepEnvelope, GeometryError}; + +pub struct MultiToolOutcome { + pub result: Result, + pub handlers: Vec, + pub tool_index: usize, +} + +pub fn multi_tool_boolean( + host: &BrepEnvelope, + tools: &[BrepEnvelope], + operation: BooleanOp, + id: String, +) -> MultiToolOutcome { + if tools.len() == 1 { + let (result, handlers) = boolean_brep_outcome_with_handlers(host, &tools[0], operation, id); + return MultiToolOutcome { + result, + handlers, + tool_index: 0, + }; + } + if operation != BooleanOp::Subtraction { + let count = tools.len(); + return serial_outcome(host, tools, operation, |index| { + if index + 1 == count { + id.clone() + } else { + format!("{id}:part:{index}") + } + }); + } + let (result, handlers) = subtract_planar_cutters_outcome_with_handlers(host, tools, id.clone()); + match result { + Err(GeometryError::CoverageGap { families }) + if !families[0].contains("mixed cutter batch") => + { + serial_outcome(host, tools, operation, |index| format!("{id}:part:{index}")) + } + result => MultiToolOutcome { + result, + handlers, + tool_index: 0, + }, + } +} + +fn serial_outcome( + host: &BrepEnvelope, + tools: &[BrepEnvelope], + operation: BooleanOp, + step_id: impl Fn(usize) -> String, +) -> MultiToolOutcome { + let mut handlers = Vec::new(); + let mut current: Option = None; + for (index, tool) in tools.iter().enumerate() { + let base = current.as_ref().map_or(host, |result| &result.brep); + let (result, step_handlers) = + boolean_brep_outcome_with_handlers(base, tool, operation, step_id(index)); + handlers.extend(step_handlers); + match result { + Ok(result) + if result.brep.solids.is_empty() && result.brep.topology.faces.is_empty() => + { + current = Some(result); + break; + } + Ok(result) => current = Some(result), + Err(error) => { + return MultiToolOutcome { + result: Err(error), + handlers, + tool_index: index, + } + } + } + } + MultiToolOutcome { + result: current.ok_or_else(|| { + GeometryError::InvalidGeometry("multi-tool boolean requires at least one tool".into()) + }), + handlers, + tool_index: 0, + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/operands/compare.rs b/main/opengeometry/src/operations/modifying/boolean/operands/compare.rs new file mode 100644 index 0000000..f07a718 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/operands/compare.rs @@ -0,0 +1,204 @@ +use crate::brep::{ + CurveGeometry, EdgeGeometry, Frame3, GeometryError, GeometryStore, PcurveGeometry, + SurfaceGeometry, Topology, +}; + +pub(crate) fn required_id(id: Option) -> Result { + id.ok_or_else(|| GeometryError::InvalidTopology("boolean selection is missing".into())) +} + +pub(super) fn cylinder_geometry_matches( + actual: &GeometryStore, + expected: &GeometryStore, + geometric: f64, +) -> bool { + if actual.surfaces.len() != expected.surfaces.len() + || actual.curves.len() != expected.curves.len() + || actual.pcurves.len() != expected.pcurves.len() + || !actual.intersections.is_empty() + || !expected.intersections.is_empty() + { + return false; + } + let scalar = geometric.max(1e-12); + let surfaces = actual + .surfaces + .iter() + .zip(&expected.surfaces) + .all(|(actual, expected)| surface_matches(actual, expected, geometric)); + let curves = actual + .curves + .iter() + .zip(&expected.curves) + .all(|(actual, expected)| curve_matches(actual, expected, geometric)); + let pcurves = actual + .pcurves + .iter() + .zip(&expected.pcurves) + .all(|(actual, expected)| pcurve_matches(actual, expected, scalar)); + surfaces && curves && pcurves +} + +fn surface_matches(actual: &SurfaceGeometry, expected: &SurfaceGeometry, geometric: f64) -> bool { + match (actual, expected) { + (SurfaceGeometry::Plane { frame: actual }, SurfaceGeometry::Plane { frame: expected }) => { + frames_match(*actual, *expected, geometric) + } + ( + SurfaceGeometry::Cylinder { + frame: actual_frame, + radius: actual_radius, + }, + SurfaceGeometry::Cylinder { + frame: expected_frame, + radius: expected_radius, + }, + ) => { + frames_match(*actual_frame, *expected_frame, geometric) + && (actual_radius - expected_radius).abs() <= geometric + } + ( + SurfaceGeometry::Cone { + frame: actual_frame, + semi_angle: actual_angle, + }, + SurfaceGeometry::Cone { + frame: expected_frame, + semi_angle: expected_angle, + }, + ) => { + frames_match(*actual_frame, *expected_frame, geometric) + && (actual_angle - expected_angle).abs() <= 1.0e-12 + } + _ => false, + } +} + +pub(super) fn frames_match(a: Frame3, b: Frame3, geometric: f64) -> bool { + coordinates_match(a.origin, b.origin, geometric) + && coordinates_match(a.x, b.x, 1e-12) + && coordinates_match(a.y, b.y, 1e-12) + && coordinates_match(a.z, b.z, 1e-12) +} + +pub(super) fn coordinates_match(a: [f64; N], b: [f64; N], tolerance: f64) -> bool { + a.into_iter() + .zip(b) + .all(|(left, right)| (left - right).abs() <= tolerance) +} + +fn curve_matches(actual: &CurveGeometry, expected: &CurveGeometry, geometric: f64) -> bool { + match (actual, expected) { + ( + CurveGeometry::Circle { + frame: actual_frame, + radius: actual_radius, + }, + CurveGeometry::Circle { + frame: expected_frame, + radius: expected_radius, + }, + ) => { + frames_match(*actual_frame, *expected_frame, geometric) + && (actual_radius - expected_radius).abs() <= geometric + } + ( + CurveGeometry::Line { + origin: actual_origin, + direction: actual_direction, + }, + CurveGeometry::Line { + origin: expected_origin, + direction: expected_direction, + }, + ) => { + coordinates_match(*actual_origin, *expected_origin, geometric) + && coordinates_match(*actual_direction, *expected_direction, 1e-12) + } + _ => false, + } +} + +fn pcurve_matches(actual: &PcurveGeometry, expected: &PcurveGeometry, scalar: f64) -> bool { + match (actual, expected) { + ( + PcurveGeometry::Line2 { + origin: actual_origin, + direction: actual_direction, + }, + PcurveGeometry::Line2 { + origin: expected_origin, + direction: expected_direction, + }, + ) => { + coordinates_match(*actual_origin, *expected_origin, scalar) + && coordinates_match(*actual_direction, *expected_direction, 1e-12) + } + ( + PcurveGeometry::Conic2 { + origin: actual_origin, + axis_a: actual_a, + axis_b: actual_b, + }, + PcurveGeometry::Conic2 { + origin: expected_origin, + axis_a: expected_a, + axis_b: expected_b, + }, + ) => { + coordinates_match(*actual_origin, *expected_origin, scalar) + && coordinates_match(*actual_a, *expected_a, scalar) + && coordinates_match(*actual_b, *expected_b, scalar) + } + _ => false, + } +} + +pub(super) fn normalize_cylinder_topology( + actual: &mut Topology, + expected: &Topology, + geometric: f64, +) -> bool { + if actual.vertices.len() != expected.vertices.len() + || actual.edges.len() != expected.edges.len() + || actual.faces.len() != expected.faces.len() + { + return false; + } + let scalar = geometric.max(1e-12); + for (actual, expected) in actual.edges.iter_mut().zip(&expected.edges) { + match (&mut actual.geometry, &expected.geometry) { + ( + EdgeGeometry::Curve { + curve: actual_curve, + range: actual_range, + }, + EdgeGeometry::Curve { + curve: expected_curve, + range: expected_range, + }, + ) if actual_curve == expected_curve + && (actual_range.lo - expected_range.lo).abs() <= scalar + && (actual_range.hi - expected_range.hi).abs() <= scalar => + { + *actual_range = *expected_range; + } + ( + EdgeGeometry::Collapsed { vertex: actual }, + EdgeGeometry::Collapsed { vertex: expected }, + ) if actual == expected => {} + _ => return false, + } + } + for (actual, expected) in actual.faces.iter_mut().zip(&expected.faces) { + for axis in 0..2 { + let a = &mut actual.trim.uv_bounds[axis]; + let e = expected.trim.uv_bounds[axis]; + if (a.lo - e.lo).abs() > scalar || (a.hi - e.hi).abs() > scalar { + return false; + } + *a = e; + } + } + true +} diff --git a/main/opengeometry/src/operations/modifying/boolean/operands/conic.rs b/main/opengeometry/src/operations/modifying/boolean/operands/conic.rs new file mode 100644 index 0000000..f540942 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/operands/conic.rs @@ -0,0 +1,138 @@ +use super::compare::{cylinder_geometry_matches, normalize_cylinder_topology}; +use crate::brep::{ + coverage_gap, BrepEnvelope, Frame3, GeometryError, GeometryQuality, SurfaceGeometry, +}; +use crate::math::{norm, scale, sub, Interval}; +use crate::primitives; + +pub(crate) struct ConicSectionInput<'a> { + pub(crate) brep: &'a BrepEnvelope, + pub(crate) frame: Frame3, + pub(crate) semi_angle: f64, + pub(crate) axial_range: Interval, + pub(crate) lower_radius: f64, + pub(crate) upper_radius: f64, +} + +pub(crate) fn full_conic_section( + brep: &BrepEnvelope, +) -> Result, GeometryError> { + brep.validate()?; + let (frame, semi_angle) = match brep.geometry.surfaces.first() { + Some(SurfaceGeometry::Cone { frame, semi_angle }) => (*frame, *semi_angle), + _ => return Err(conic_section_gap()), + }; + if !matches!(brep.quality, GeometryQuality::Analytic) + || !brep.geometry.intersections.is_empty() + || !brep.topology.wires.is_empty() + || brep.topology.shells.len() != 1 + || brep.solids.len() != 1 + || !brep.solids[0].cavity_shells.is_empty() + || !matches!(brep.geometry.surfaces.len(), 2 | 3) + || brep.geometry.surfaces[1..] + .iter() + .any(|surface| !matches!(surface, SurfaceGeometry::Plane { .. })) + || brep.topology.faces.len() != brep.geometry.surfaces.len() + { + return Err(conic_section_gap()); + } + let axial_range = brep.topology.faces[0].trim.uv_bounds[1]; + if axial_range.lo < 0.0 + || axial_range.hi <= axial_range.lo + || semi_angle <= 0.0 + || semi_angle >= std::f64::consts::FRAC_PI_2 + { + return Err(conic_section_gap()); + } + let slope = semi_angle.tan(); + let lower_radius = axial_range.lo * slope; + let upper_radius = axial_range.hi * slope; + let canonical = canonical_conic_section(brep, frame, axial_range, lower_radius, upper_radius)?; + let mut topology = brep.topology.clone(); + for (face, expected) in topology.faces.iter_mut().zip(&canonical.topology.faces) { + face.key = expected.key.clone(); + face.provenance = expected.provenance.clone(); + } + for vertex in &mut topology.vertices { + vertex.tolerance = brep.accuracy.geometric; + let expected = canonical + .topology + .vertices + .get(vertex.id as usize) + .ok_or_else(conic_section_gap)? + .position; + if norm(sub(vertex.position, expected)) > brep.accuracy.geometric { + return Err(conic_section_gap()); + } + vertex.position = expected; + } + for edge in &mut topology.edges { + edge.tolerance = brep.accuracy.geometric; + } + if !normalize_cylinder_topology(&mut topology, &canonical.topology, brep.accuracy.geometric) + || !cylinder_geometry_matches(&brep.geometry, &canonical.geometry, brep.accuracy.geometric) + || topology != canonical.topology + || brep.solids != canonical.solids + { + return Err(conic_section_gap()); + } + Ok(ConicSectionInput { + brep, + frame, + semi_angle, + axial_range, + lower_radius, + upper_radius, + }) +} + +fn conic_section_gap() -> GeometryError { + coverage_gap("canonical cone or frustum", "analytic solid") +} + +fn canonical_conic_section( + brep: &BrepEnvelope, + frame: Frame3, + axial_range: Interval, + lower_radius: f64, + upper_radius: f64, +) -> Result { + if axial_range.lo <= brep.accuracy.geometric { + if axial_range.lo != 0.0 || brep.geometry.surfaces.len() != 2 { + return Err(conic_section_gap()); + } + let base_frame = Frame3 { + origin: frame.point([0.0, 0.0, axial_range.hi]), + x: frame.x, + y: scale(frame.y, -1.0), + z: scale(frame.z, -1.0), + }; + primitives::cone( + brep.id.clone(), + base_frame, + upper_radius, + axial_range.hi, + brep.accuracy, + ) + } else { + if brep.geometry.surfaces.len() != 3 { + return Err(conic_section_gap()); + } + let base_frame = Frame3 { + origin: frame.point([0.0, 0.0, axial_range.lo]), + ..frame + }; + primitives::frustum( + brep.id.clone(), + base_frame, + lower_radius, + upper_radius, + axial_range.width(), + brep.accuracy, + ) + } +} + +pub(crate) fn conic_radius_at(input: &ConicSectionInput<'_>, axial: f64) -> f64 { + axial * input.semi_angle.tan() +} diff --git a/main/opengeometry/src/operations/modifying/boolean/operands/cuboid.rs b/main/opengeometry/src/operations/modifying/boolean/operands/cuboid.rs new file mode 100644 index 0000000..9ad17c8 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/operands/cuboid.rs @@ -0,0 +1,327 @@ +use super::planar::all_planar; +use crate::brep::{ + coverage_gap, BrepEnvelope, CurveGeometry, EdgeGeometry, FaceRole, FaceSource, Frame3, + GeometryError, GeometryQuality, PcurveGeometry, SurfaceGeometry, Topology, +}; +use crate::math::{dot, norm, scale, sub, Point3}; +use crate::primitives::cuboid; +use crate::query::{classify_point_validated, face_contains_uv, PointClassification}; + +pub(crate) struct BoxInput<'a> { + pub(crate) brep: &'a BrepEnvelope, + pub(crate) frame: Frame3, + pub(crate) size: Point3, +} + +pub(crate) type GridPoint = [usize; 3]; + +pub(crate) fn source(input: &BoxInput<'_>, face: usize) -> FaceSource { + let authored = &input.brep.topology.faces[face]; + if matches!(authored.provenance.role, FaceRole::Preserved) + && authored.provenance.sources.len() == 1 + { + return authored.provenance.sources[0].clone(); + } + FaceSource { + entity: input.brep.id.clone(), + body: input.brep.id.clone(), + key: authored.key.clone(), + face: face as u32, + } +} + +pub(crate) fn rectilinear_input<'a>( + brep: &'a BrepEnvelope, + axes: Frame3, +) -> Result, GeometryError> { + brep.validate()?; + if !matches!(brep.quality, GeometryQuality::Analytic) + || brep.geometry.surfaces.is_empty() + || brep.solids.is_empty() + || !all_planar(brep) + || brep + .geometry + .curves + .iter() + .any(|curve| !matches!(curve, CurveGeometry::Line { .. })) + { + return Err(coverage()); + } + let directions = [axes.x, axes.y, axes.z]; + for surface in &brep.geometry.surfaces { + let normal = surface.frame().z; + if directions + .iter() + .all(|axis| dot(normal, *axis).abs() < 1.0 - 1.0e-10) + { + return Err(coverage()); + } + } + let mut lo = [f64::INFINITY; 3]; + let mut hi = [f64::NEG_INFINITY; 3]; + for vertex in &brep.topology.vertices { + let point = axes.local(vertex.position); + for axis in 0..3 { + lo[axis] = lo[axis].min(point[axis]); + hi[axis] = hi[axis].max(point[axis]); + } + } + if (0..3).any(|axis| hi[axis] - lo[axis] <= 4.0 * brep.accuracy.geometric) { + return Err(coverage()); + } + for edge in &brep.topology.edges { + let EdgeGeometry::Curve { curve, .. } = edge.geometry else { + return Err(coverage()); + }; + let CurveGeometry::Line { direction, .. } = brep.geometry.curves[curve as usize] else { + return Err(coverage()); + }; + if directions + .iter() + .all(|axis| dot(direction, *axis).abs() < 1.0 - 1.0e-10) + { + return Err(coverage()); + } + } + Ok(BoxInput { + brep, + frame: Frame3 { + origin: axes.point(lo), + ..axes + }, + size: std::array::from_fn(|axis| hi[axis] - lo[axis]), + }) +} + +pub(crate) fn coverage() -> GeometryError { + coverage_gap("canonical aligned cuboid", "canonical aligned cuboid") +} + +pub(crate) fn classified_inside(brep: &BrepEnvelope, point: Point3) -> Result { + match classify_point_validated(brep, point)? { + PointClassification::Inside => Ok(true), + PointClassification::Outside => Ok(false), + _ => Err(GeometryError::UnresolvedIntersection( + "rectilinear arrangement cell classification is unresolved".into(), + )), + } +} + +pub(crate) fn containing_faces( + input: &BoxInput<'_>, + point: Point3, + outward: Point3, + tolerance: f64, +) -> Result, GeometryError> { + let mut matches = Vec::new(); + for (index, face) in input.brep.topology.faces.iter().enumerate() { + let SurfaceGeometry::Plane { frame } = input.brep.geometry.surfaces[face.surface as usize] + else { + return Err(coverage()); + }; + let local = frame.local(point); + if local[2].abs() > tolerance { + continue; + } + if face_contains_uv(input.brep, face, [local[0], local[1]])? != Some(true) { + continue; + } + let normal = scale(frame.z, face.sense.multiplier()); + let alignment = dot(normal, outward); + if alignment.abs() < 1.0 - 1.0e-10 { + continue; + } + matches.push((index, alignment < 0.0)); + } + Ok(matches) +} + +pub(crate) fn full_box(brep: &BrepEnvelope) -> Result, GeometryError> { + brep.validate()?; + let t = &brep.topology; + if !matches!(brep.quality, GeometryQuality::Analytic) + || [ + t.vertices.len(), + t.edges.len(), + t.halfedges.len(), + t.loops.len(), + t.faces.len(), + t.shells.len(), + ] != [8, 12, 24, 6, 6, 1] + || !t.wires.is_empty() + || brep.solids.len() != 1 + || brep.geometry.surfaces.len() != 6 + || brep.geometry.curves.len() != 12 + || brep.geometry.pcurves.len() != 24 + || !brep.geometry.intersections.is_empty() + { + return Err(coverage()); + } + let mut axes = [[0.0; 3]; 3]; + let mut size = [0.0; 3]; + for i in 0..3 { + let EdgeGeometry::Curve { curve, range } = t.edges[i].geometry else { + return Err(coverage()); + }; + let CurveGeometry::Line { direction, .. } = brep.geometry.curves[curve as usize] else { + return Err(coverage()); + }; + if range.lo != 0.0 { + return Err(coverage()); + } + axes[i] = direction; + size[i] = range.hi; + } + let frame = Frame3 { + origin: t.vertices[0].position, + x: axes[0], + y: axes[1], + z: axes[2], + }; + frame.validate().map_err(|_| coverage())?; + let canonical = cuboid(brep.id.clone(), frame, size, brep.accuracy)?; + let magnitude = size.into_iter().fold(0.0_f64, f64::max).max( + t.vertices + .iter() + .map(|v| norm(v.position)) + .fold(0.0_f64, f64::max), + ); + let noise = (256.0 * f64::EPSILON * magnitude).min(brep.accuracy.intersection / 8.0); + let close = |a: Point3, b: Point3| norm(sub(a, b)) <= noise; + let direction_close = + |a: Point3, b: Point3| norm(sub(a, b)) * size.into_iter().fold(0.0_f64, f64::max) <= noise; + check_canonical_surfaces(brep, &canonical, close, direction_close)?; + check_canonical_curves(brep, &canonical, close, direction_close)?; + check_canonical_pcurves(brep, &canonical, noise, size)?; + let topology = snapped_topology(brep, &canonical, close, noise)?; + if topology != canonical.topology || brep.solids != canonical.solids { + return Err(coverage()); + } + Ok(BoxInput { brep, frame, size }) +} + +fn check_canonical_surfaces( + brep: &BrepEnvelope, + canonical: &BrepEnvelope, + close: impl Fn(Point3, Point3) -> bool, + direction_close: impl Fn(Point3, Point3) -> bool, +) -> Result<(), GeometryError> { + for (a, b) in brep + .geometry + .surfaces + .iter() + .zip(&canonical.geometry.surfaces) + { + let (SurfaceGeometry::Plane { frame: a }, SurfaceGeometry::Plane { frame: b }) = (a, b) + else { + return Err(coverage()); + }; + if !close(a.origin, b.origin) + || !direction_close(a.x, b.x) + || !direction_close(a.y, b.y) + || !direction_close(a.z, b.z) + { + return Err(coverage()); + } + } + Ok(()) +} + +fn check_canonical_curves( + brep: &BrepEnvelope, + canonical: &BrepEnvelope, + close: impl Fn(Point3, Point3) -> bool, + direction_close: impl Fn(Point3, Point3) -> bool, +) -> Result<(), GeometryError> { + for (a, b) in brep.geometry.curves.iter().zip(&canonical.geometry.curves) { + let ( + CurveGeometry::Line { + origin: ao, + direction: ad, + }, + CurveGeometry::Line { + origin: bo, + direction: bd, + }, + ) = (a, b) + else { + return Err(coverage()); + }; + if !close(*ao, *bo) || !direction_close(*ad, *bd) { + return Err(coverage()); + } + } + Ok(()) +} + +fn check_canonical_pcurves( + brep: &BrepEnvelope, + canonical: &BrepEnvelope, + noise: f64, + size: Point3, +) -> Result<(), GeometryError> { + for (a, b) in brep + .geometry + .pcurves + .iter() + .zip(&canonical.geometry.pcurves) + { + let ( + PcurveGeometry::Line2 { + origin: ao, + direction: ad, + }, + PcurveGeometry::Line2 { + origin: bo, + direction: bd, + }, + ) = (a, b) + else { + return Err(coverage()); + }; + for i in 0..2 { + if (ao[i] - bo[i]).abs() > noise + || (ad[i] - bd[i]).abs() * size.into_iter().fold(0.0_f64, f64::max) > noise + { + return Err(coverage()); + } + } + } + Ok(()) +} + +fn snapped_topology( + brep: &BrepEnvelope, + canonical: &BrepEnvelope, + close: impl Fn(Point3, Point3) -> bool, + noise: f64, +) -> Result { + let mut topology = brep.topology.clone(); + for (v, expected) in topology + .vertices + .iter_mut() + .zip(&canonical.topology.vertices) + { + if !close(v.position, expected.position) { + return Err(coverage()); + } + v.position = expected.position; + v.tolerance = expected.tolerance; + } + for e in &mut topology.edges { + e.tolerance = brep.accuracy.geometric; + } + for (f, expected) in topology.faces.iter_mut().zip(&canonical.topology.faces) { + for i in 0..2 { + if (f.trim.uv_bounds[i].lo - expected.trim.uv_bounds[i].lo).abs() > noise + || (f.trim.uv_bounds[i].hi - expected.trim.uv_bounds[i].hi).abs() > noise + { + return Err(coverage()); + } + } + f.trim.uv_bounds = expected.trim.uv_bounds; + f.key = expected.key.clone(); + f.provenance = expected.provenance.clone(); + } + Ok(topology) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/operands/cylinder.rs b/main/opengeometry/src/operations/modifying/boolean/operands/cylinder.rs new file mode 100644 index 0000000..f5248ea --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/operands/cylinder.rs @@ -0,0 +1,77 @@ +use super::compare::{cylinder_geometry_matches, normalize_cylinder_topology}; +use crate::brep::{ + coverage_gap, BrepEnvelope, Frame3, GeometryError, GeometryQuality, SurfaceGeometry, +}; +use crate::math::{norm, sub}; +use crate::primitives; + +pub(crate) struct CylinderInput<'a> { + pub(crate) brep: &'a BrepEnvelope, + pub(crate) frame: Frame3, + pub(crate) radius: f64, + pub(crate) height: f64, +} + +fn cylinder_gap() -> GeometryError { + coverage_gap("canonical full cylinder", "canonical full cylinder") +} + +pub(crate) fn full_cylinder(brep: &BrepEnvelope) -> Result, GeometryError> { + brep.validate()?; + let (frame, radius) = match brep.geometry.surfaces.as_slice() { + [SurfaceGeometry::Cylinder { frame, radius }, SurfaceGeometry::Plane { .. }, SurfaceGeometry::Plane { .. }] => { + (*frame, *radius) + } + _ => return Err(cylinder_gap()), + }; + if !matches!(brep.quality, GeometryQuality::Analytic) + || !brep.geometry.intersections.is_empty() + || brep.topology.faces.len() != 3 + || brep.topology.vertices.len() != 2 + || brep.topology.edges.len() != 3 + || brep.topology.halfedges.len() != 6 + || brep.topology.loops.len() != 3 + || !brep.topology.wires.is_empty() + || brep.topology.shells.len() != 1 + || brep.solids.len() != 1 + { + return Err(cylinder_gap()); + } + let axial_range = brep.topology.faces[0].trim.uv_bounds[1]; + if axial_range.lo != 0.0 { + return Err(cylinder_gap()); + } + let height = axial_range.hi; + let canonical = primitives::cylinder(brep.id.clone(), frame, radius, height, brep.accuracy)?; + let mut topology = brep.topology.clone(); + for (face, expected) in topology.faces.iter_mut().zip(&canonical.topology.faces) { + face.key = expected.key.clone(); + face.provenance = expected.provenance.clone(); + } + for vertex in &mut topology.vertices { + vertex.tolerance = brep.accuracy.geometric; + let expected = canonical.topology.vertices[vertex.id as usize].position; + if norm(sub(vertex.position, expected)) > brep.accuracy.geometric { + return Err(cylinder_gap()); + } + vertex.position = expected; + } + for edge in &mut topology.edges { + edge.tolerance = brep.accuracy.geometric; + } + if !normalize_cylinder_topology(&mut topology, &canonical.topology, brep.accuracy.geometric) { + return Err(cylinder_gap()); + } + if !cylinder_geometry_matches(&brep.geometry, &canonical.geometry, brep.accuracy.geometric) + || topology != canonical.topology + || brep.solids != canonical.solids + { + return Err(cylinder_gap()); + } + Ok(CylinderInput { + brep, + frame, + radius, + height, + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/operands/mod.rs b/main/opengeometry/src/operations/modifying/boolean/operands/mod.rs new file mode 100644 index 0000000..00aa753 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/operands/mod.rs @@ -0,0 +1,24 @@ +mod compare; +mod conic; +mod cuboid; +mod cylinder; +mod planar; +mod prismatic; +mod sphere; +mod torus; + +pub(super) use compare::required_id; +pub(super) use conic::{conic_radius_at, full_conic_section, ConicSectionInput}; +pub(super) use cuboid::{ + classified_inside, containing_faces, coverage, full_box, rectilinear_input, source, BoxInput, + GridPoint, +}; +pub(super) use cylinder::{full_cylinder, CylinderInput}; +pub(super) use planar::all_planar; +pub(super) use prismatic::{ + brep_face_source, full_planar_extrusion, prismatic_face_provenance, prismatic_gap, + prismatic_profile_sources, profiles_match, region_profiles, unique_sources, + PrismaticAxialSegment, PrismaticInput, +}; +pub(super) use sphere::{full_sphere, gap, SphereInput}; +pub(super) use torus::full_torus; diff --git a/main/opengeometry/src/operations/modifying/boolean/operands/planar.rs b/main/opengeometry/src/operations/modifying/boolean/operands/planar.rs new file mode 100644 index 0000000..44747aa --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/operands/planar.rs @@ -0,0 +1,8 @@ +use crate::brep::{BrepEnvelope, SurfaceGeometry}; + +pub(crate) fn all_planar(body: &BrepEnvelope) -> bool { + body.geometry + .surfaces + .iter() + .all(|surface| matches!(surface, SurfaceGeometry::Plane { .. })) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/operands/prismatic.rs b/main/opengeometry/src/operations/modifying/boolean/operands/prismatic.rs new file mode 100644 index 0000000..aeb01f0 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/operands/prismatic.rs @@ -0,0 +1,502 @@ +use super::compare::{coordinates_match, frames_match}; +use crate::brep::{ + coverage_gap, BrepEnvelope, CurveGeometry, EdgeGeometry, FaceProvenance, FaceRole, FaceSource, + Frame3, GeometryError, GeometryQuality, GeometryStore, PcurveGeometry, SurfaceGeometry, + Topology, +}; +use crate::geom2d::{Pt2, RingRegion}; +use crate::math::{add, dot, norm, scale, sub, Point3}; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; + +#[derive(Clone)] +struct PrismaticSide { + from: Point3, + to: Point3, + source: FaceSource, +} + +struct ExtrusionFrame { + top_frame: Frame3, + frame: Frame3, + height: f64, +} + +pub(crate) struct PrismaticInput<'a> { + pub(crate) brep: &'a BrepEnvelope, + pub(crate) frame: Frame3, + pub(crate) height: f64, + pub(crate) contours: Vec>, + sides: Vec, +} + +#[derive(Clone)] +pub(crate) struct PrismaticAxialSegment { + pub(crate) lo: f64, + pub(crate) hi: f64, + pub(crate) lower: FaceProvenance, + pub(crate) upper: FaceProvenance, + pub(crate) include_a_sides: bool, + pub(crate) include_b_sides: bool, + pub(crate) side_role: FaceRole, +} + +pub(crate) fn full_planar_extrusion( + brep: &BrepEnvelope, +) -> Result, GeometryError> { + brep.validate()?; + if !matches!(brep.quality, GeometryQuality::Analytic) + || brep.topology.faces.len() < 5 + || brep.topology.shells.len() != 1 + || brep.solids.len() != 1 + || !brep.solids[0].cavity_shells.is_empty() + || !brep.topology.wires.is_empty() + { + return Err(prismatic_gap()); + } + let ExtrusionFrame { + top_frame, + frame, + height, + } = extrusion_frame(brep)?; + let top = &brep.topology.faces[0]; + let mut profile = Vec::with_capacity(top.trim.holes.len() + 1); + profile.push(loop_points(brep, top.trim.outer, top_frame)?); + for &hole in &top.trim.holes { + profile.push(loop_points(brep, hole, top_frame)?); + } + check_canonical_extrusion(brep, frame, &profile, height)?; + let contours = profile + .iter() + .map(|ring| { + ring.iter() + .map(|point| frame.point([point[0], point[1], 0.0])) + .collect::>() + }) + .collect::>(); + let mut face = 2_u32; + let mut sides = Vec::new(); + for contour in &contours { + for index in 0..contour.len() { + sides.push(PrismaticSide { + from: contour[index], + to: contour[(index + 1) % contour.len()], + source: brep_face_source(brep, face), + }); + face += 1; + } + } + Ok(PrismaticInput { + brep, + frame, + height, + contours, + sides, + }) +} + +pub(crate) fn prismatic_gap() -> GeometryError { + coverage_gap( + "canonical planar profile extrusion", + "canonical planar profile extrusion", + ) +} + +fn extrusion_frame(brep: &BrepEnvelope) -> Result { + let (top_frame, bottom_frame) = match ( + brep.geometry + .surfaces + .get(brep.topology.faces[0].surface as usize), + brep.geometry + .surfaces + .get(brep.topology.faces[1].surface as usize), + ) { + ( + Some(SurfaceGeometry::Plane { frame: top }), + Some(SurfaceGeometry::Plane { frame: bottom }), + ) => (*top, *bottom), + _ => return Err(prismatic_gap()), + }; + let height = dot(sub(top_frame.origin, bottom_frame.origin), top_frame.z); + if height <= 4.0 * brep.accuracy.geometric { + return Err(prismatic_gap()); + } + let frame = Frame3 { + origin: sub(top_frame.origin, scale(top_frame.z, height)), + x: top_frame.x, + y: top_frame.y, + z: top_frame.z, + }; + frame.validate().map_err(|_| prismatic_gap())?; + if norm(sub(bottom_frame.origin, frame.origin)) > brep.accuracy.geometric + || norm(sub(bottom_frame.x, frame.x)) > 1.0e-12 + || norm(sub(bottom_frame.y, scale(frame.y, -1.0))) > 1.0e-12 + || norm(sub(bottom_frame.z, scale(frame.z, -1.0))) > 1.0e-12 + { + return Err(prismatic_gap()); + } + Ok(ExtrusionFrame { + top_frame, + frame, + height, + }) +} + +fn loop_points( + brep: &BrepEnvelope, + loop_id: u32, + frame: Frame3, +) -> Result, GeometryError> { + let start = brep + .topology + .loops + .get(loop_id as usize) + .ok_or_else(prismatic_gap)? + .start_halfedge; + let mut current = start; + let mut result = Vec::new(); + loop { + let halfedge = brep + .topology + .halfedges + .get(current as usize) + .ok_or_else(prismatic_gap)?; + let position = brep + .topology + .vertices + .get(halfedge.from as usize) + .ok_or_else(prismatic_gap)? + .position; + let local = frame.local(position); + if local[2].abs() > brep.accuracy.geometric { + return Err(prismatic_gap()); + } + result.push([local[0], local[1]]); + current = halfedge.next.ok_or_else(prismatic_gap)?; + if current == start { + break; + } + if result.len() > brep.topology.halfedges.len() { + return Err(prismatic_gap()); + } + } + Ok(result) +} + +fn check_canonical_extrusion( + brep: &BrepEnvelope, + frame: Frame3, + profile: &[Vec<[f64; 2]>], + height: f64, +) -> Result<(), GeometryError> { + let canonical = primitives::linear_extrusion( + brep.id.clone(), + frame, + profile[0].clone(), + profile[1..].to_vec(), + height, + brep.accuracy, + )?; + if !planar_extrusion_geometry_matches( + &brep.geometry, + &canonical.geometry, + brep.accuracy.geometric, + ) || !planar_extrusion_topology_matches( + &brep.topology, + &canonical.topology, + brep.accuracy.geometric, + )? || brep.solids != canonical.solids + { + return Err(prismatic_gap()); + } + Ok(()) +} + +fn planar_extrusion_geometry_matches( + actual: &GeometryStore, + expected: &GeometryStore, + geometric: f64, +) -> bool { + if actual.surfaces.len() != expected.surfaces.len() + || actual.curves.len() != expected.curves.len() + || actual.pcurves.len() != expected.pcurves.len() + || !actual.intersections.is_empty() + || !expected.intersections.is_empty() + { + return false; + } + let surfaces = actual + .surfaces + .iter() + .zip(&expected.surfaces) + .all(|(actual, expected)| match (actual, expected) { + ( + SurfaceGeometry::Plane { frame: actual }, + SurfaceGeometry::Plane { frame: expected }, + ) => frames_match(*actual, *expected, geometric), + _ => false, + }); + let curves = actual + .curves + .iter() + .zip(&expected.curves) + .all(|(actual, expected)| match (actual, expected) { + ( + CurveGeometry::Line { + origin: actual_origin, + direction: actual_direction, + }, + CurveGeometry::Line { + origin: expected_origin, + direction: expected_direction, + }, + ) => { + coordinates_match(*actual_origin, *expected_origin, geometric) + && coordinates_match(*actual_direction, *expected_direction, 1.0e-12) + } + _ => false, + }); + let pcurves = actual + .pcurves + .iter() + .zip(&expected.pcurves) + .all(|(actual, expected)| match (actual, expected) { + ( + PcurveGeometry::Line2 { + origin: actual_origin, + direction: actual_direction, + }, + PcurveGeometry::Line2 { + origin: expected_origin, + direction: expected_direction, + }, + ) => { + coordinates_match(*actual_origin, *expected_origin, geometric) + && coordinates_match(*actual_direction, *expected_direction, 1.0e-12) + } + _ => false, + }); + surfaces && curves && pcurves +} + +fn planar_extrusion_topology_matches( + actual: &Topology, + expected: &Topology, + geometric: f64, +) -> Result { + if actual.vertices.len() != expected.vertices.len() + || actual.edges.len() != expected.edges.len() + || actual.halfedges.len() != expected.halfedges.len() + || actual.loops.len() != expected.loops.len() + || actual.faces.len() != expected.faces.len() + || actual.shells.len() != expected.shells.len() + || actual.wires.len() != expected.wires.len() + { + return Ok(false); + } + let mut normalized = actual.clone(); + for (vertex, expected) in normalized.vertices.iter_mut().zip(&expected.vertices) { + if norm(sub(vertex.position, expected.position)) > geometric { + return Ok(false); + } + vertex.position = expected.position; + vertex.tolerance = expected.tolerance; + } + for (edge, expected) in normalized.edges.iter_mut().zip(&expected.edges) { + edge.tolerance = expected.tolerance; + match (&mut edge.geometry, &expected.geometry) { + ( + EdgeGeometry::Curve { + curve: actual_curve, + range: actual_range, + }, + EdgeGeometry::Curve { + curve: expected_curve, + range: expected_range, + }, + ) if actual_curve == expected_curve + && (actual_range.lo - expected_range.lo).abs() <= geometric + && (actual_range.hi - expected_range.hi).abs() <= geometric => + { + *actual_range = *expected_range; + } + _ => return Ok(false), + } + } + for (face, expected) in normalized.faces.iter_mut().zip(&expected.faces) { + for axis in 0..2 { + if (face.trim.uv_bounds[axis].lo - expected.trim.uv_bounds[axis].lo).abs() > geometric + || (face.trim.uv_bounds[axis].hi - expected.trim.uv_bounds[axis].hi).abs() + > geometric + { + return Ok(false); + } + } + face.trim.uv_bounds = expected.trim.uv_bounds; + face.key = expected.key.clone(); + face.provenance = expected.provenance.clone(); + } + Ok(normalized == *expected) +} + +pub(crate) fn brep_face_source(brep: &BrepEnvelope, face: u32) -> FaceSource { + FaceSource { + entity: brep.id.clone(), + body: brep.id.clone(), + key: brep.topology.faces[face as usize].key.clone(), + face, + } +} + +pub(crate) fn prismatic_face_provenance( + a: &PrismaticInput<'_>, + b: &PrismaticInput<'_>, + from: Point3, + to: Point3, + operation: BooleanOp, + tolerance: f64, +) -> Result { + let a_sources = a + .sides + .iter() + .filter(|side| segment_contains(side, from, to, tolerance)) + .map(|side| side.source.clone()) + .collect::>(); + let b_sources = b + .sides + .iter() + .filter(|side| segment_contains(side, from, to, tolerance)) + .map(|side| side.source.clone()) + .collect::>(); + if a_sources.is_empty() && b_sources.is_empty() { + return Err(GeometryError::InvalidTopology( + "planar boolean produced a boundary without source ancestry".into(), + )); + } + let reversed = operation == BooleanOp::Subtraction && a_sources.is_empty(); + let role = if !a_sources.is_empty() && !b_sources.is_empty() { + FaceRole::Coincident + } else if reversed { + FaceRole::Cut + } else { + FaceRole::Split + }; + Ok(FaceProvenance { + sources: unique_sources(a_sources.into_iter().chain(b_sources)), + role, + reversed, + }) +} + +fn segment_contains(side: &PrismaticSide, from: Point3, to: Point3, tolerance: f64) -> bool { + let edge = sub(side.to, side.from); + let length = norm(edge); + if length <= tolerance { + return false; + } + let direction = scale(edge, 1.0 / length); + [from, to].into_iter().all(|point| { + let delta = sub(point, side.from); + let parameter = dot(delta, direction); + let closest = add(side.from, scale(direction, parameter)); + norm(sub(point, closest)) <= tolerance + && parameter >= -tolerance + && parameter <= length + tolerance + }) +} + +pub(crate) fn unique_sources(sources: impl IntoIterator) -> Vec { + let mut result = Vec::new(); + for source in sources { + if !result.iter().any(|existing: &FaceSource| { + existing.entity == source.entity + && existing.body == source.body + && existing.face == source.face + && existing.key == source.key + }) { + result.push(source); + } + } + result +} + +pub(crate) fn region_profiles(region: &RingRegion) -> (Vec<[f64; 2]>, Vec>) { + let convert = |ring: &[Pt2]| ring.iter().map(|point| [point.x, point.z]).collect(); + ( + convert(®ion.outer), + region.holes.iter().map(|hole| convert(hole)).collect(), + ) +} + +pub(crate) fn profiles_match(a: &[Vec], b: &[Vec], tolerance: f64) -> bool { + if a.len() != b.len() || a.is_empty() || !profile_rings_match(&a[0], &b[0], tolerance) { + return false; + } + let mut matched = vec![false; b.len() - 1]; + a[1..].iter().all(|ring| { + let Some(index) = b[1..].iter().enumerate().position(|(index, candidate)| { + !matched[index] && profile_rings_match(ring, candidate, tolerance) + }) else { + return false; + }; + matched[index] = true; + true + }) +} + +fn profile_rings_match(a: &[Pt2], b: &[Pt2], tolerance: f64) -> bool { + a.len() == b.len() + && (0..b.len()).any(|offset| { + a.iter().enumerate().all(|(index, point)| { + let candidate = b[(index + offset) % b.len()]; + (point.x - candidate.x).abs() <= tolerance + && (point.z - candidate.z).abs() <= tolerance + }) + }) +} + +pub(crate) fn prismatic_profile_sources( + input: &PrismaticInput<'_>, + common_frame: Frame3, + from: Pt2, + to: Pt2, + tolerance: f64, +) -> Vec { + input + .sides + .iter() + .filter(|side| projected_segment_contains(side, common_frame, from, to, tolerance)) + .map(|side| side.source.clone()) + .collect() +} + +fn projected_segment_contains( + side: &PrismaticSide, + frame: Frame3, + from: Pt2, + to: Pt2, + tolerance: f64, +) -> bool { + let project = |point: Point3| { + let local = frame.local(point); + Pt2::new(local[0], local[1]) + }; + let start = project(side.from); + let end = project(side.to); + let edge = Pt2::new(end.x - start.x, end.z - start.z); + let length = (edge.x * edge.x + edge.z * edge.z).sqrt(); + if length <= tolerance { + return false; + } + let direction = Pt2::new(edge.x / length, edge.z / length); + [from, to].into_iter().all(|point| { + let delta = Pt2::new(point.x - start.x, point.z - start.z); + let parameter = delta.x * direction.x + delta.z * direction.z; + let closest = Pt2::new( + start.x + parameter * direction.x, + start.z + parameter * direction.z, + ); + let distance = ((point.x - closest.x).powi(2) + (point.z - closest.z).powi(2)).sqrt(); + distance <= tolerance && parameter >= -tolerance && parameter <= length + tolerance + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/operands/sphere.rs b/main/opengeometry/src/operations/modifying/boolean/operands/sphere.rs new file mode 100644 index 0000000..4664208 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/operands/sphere.rs @@ -0,0 +1,64 @@ +use crate::brep::{ + coverage_gap, BrepEnvelope, Frame3, GeometryError, GeometryQuality, SurfaceGeometry, +}; +use crate::math::{norm, sub}; +use crate::primitives; + +pub(crate) struct SphereInput<'a> { + pub(crate) brep: &'a BrepEnvelope, + pub(crate) frame: Frame3, + pub(crate) radius: f64, +} + +pub(crate) fn gap() -> GeometryError { + coverage_gap("full sphere input", "full sphere input") +} + +pub(crate) fn full_sphere(brep: &BrepEnvelope) -> Result, GeometryError> { + brep.validate()?; + let (frame, radius) = match brep.geometry.surfaces.as_slice() { + [SurfaceGeometry::Sphere { frame, radius }] => (*frame, *radius), + _ => return Err(gap()), + }; + if !matches!(brep.quality, GeometryQuality::Analytic) + || brep.geometry.curves.len() != 1 + || brep.geometry.pcurves.len() != 4 + || !brep.geometry.intersections.is_empty() + || brep.topology.faces.len() != 1 + || brep.topology.vertices.len() != 2 + || brep.topology.edges.len() != 3 + || brep.topology.halfedges.len() != 4 + || brep.topology.loops.len() != 1 + || !brep.topology.wires.is_empty() + || brep.topology.shells.len() != 1 + || brep.solids.len() != 1 + { + return Err(gap()); + } + let canonical = primitives::sphere(brep.id.clone(), frame, radius, brep.accuracy)?; + let mut topology = brep.topology.clone(); + topology.faces[0].key = canonical.topology.faces[0].key.clone(); + topology.faces[0].provenance = canonical.topology.faces[0].provenance.clone(); + for vertex in &mut topology.vertices { + vertex.tolerance = brep.accuracy.geometric; + let expected = canonical.topology.vertices[vertex.id as usize].position; + if norm(sub(vertex.position, expected)) > brep.accuracy.geometric { + return Err(gap()); + } + vertex.position = expected; + } + for edge in &mut topology.edges { + edge.tolerance = brep.accuracy.geometric; + } + if brep.geometry != canonical.geometry + || topology != canonical.topology + || brep.solids != canonical.solids + { + return Err(gap()); + } + Ok(SphereInput { + brep, + frame, + radius, + }) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/operands/torus.rs b/main/opengeometry/src/operations/modifying/boolean/operands/torus.rs new file mode 100644 index 0000000..ca83dcb --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/operands/torus.rs @@ -0,0 +1,112 @@ +use crate::brep::{ + coverage_gap, BrepEnvelope, Edge, EdgeGeometry, Frame3, GeometryError, GeometryQuality, + SurfaceGeometry, +}; +use crate::math::{norm, sub}; +use crate::primitives; + +pub(crate) struct TorusInput<'a> { + pub(crate) brep: &'a BrepEnvelope, + pub(crate) frame: Frame3, + pub(crate) major_radius: f64, + pub(crate) minor_radius: f64, +} + +pub(crate) fn full_torus(brep: &BrepEnvelope) -> Result, GeometryError> { + brep.validate()?; + let (frame, major_radius, minor_radius) = match brep.geometry.surfaces.as_slice() { + [SurfaceGeometry::Torus { + frame, + major_radius, + minor_radius, + }] => (*frame, *major_radius, *minor_radius), + _ => return Err(torus_gap()), + }; + if !matches!(brep.quality, GeometryQuality::Analytic) + || !brep.geometry.intersections.is_empty() + || brep.topology.vertices.len() != 1 + || brep.topology.edges.len() != 2 + || brep.topology.halfedges.len() != 4 + || brep.topology.loops.len() != 1 + || brep.topology.faces.len() != 1 + || !brep.topology.wires.is_empty() + || brep.topology.shells.len() != 1 + || brep.solids.len() != 1 + { + return Err(torus_gap()); + } + let canonical = primitives::torus( + brep.id.clone(), + frame, + major_radius, + minor_radius, + brep.accuracy, + )?; + let mut topology = brep.topology.clone(); + for (actual, expected) in topology + .vertices + .iter_mut() + .zip(&canonical.topology.vertices) + { + if norm(sub(actual.position, expected.position)) > brep.accuracy.geometric { + return Err(torus_gap()); + } + actual.position = expected.position; + actual.tolerance = expected.tolerance; + } + snap_torus_edges( + &mut topology.edges, + &canonical.topology.edges, + brep.accuracy.geometric, + )?; + topology.faces[0].key = canonical.topology.faces[0].key.clone(); + topology.faces[0].provenance = canonical.topology.faces[0].provenance.clone(); + topology.faces[0].trim.uv_bounds = canonical.topology.faces[0].trim.uv_bounds; + if brep.geometry != canonical.geometry + || topology != canonical.topology + || brep.solids != canonical.solids + { + return Err(torus_gap()); + } + Ok(TorusInput { + brep, + frame, + major_radius, + minor_radius, + }) +} + +fn torus_gap() -> GeometryError { + coverage_gap("canonical full ring torus", "analytic solid") +} + +fn snap_torus_edges( + edges: &mut [Edge], + expected_edges: &[Edge], + geometric: f64, +) -> Result<(), GeometryError> { + for (actual, expected) in edges.iter_mut().zip(expected_edges) { + actual.tolerance = expected.tolerance; + let ( + EdgeGeometry::Curve { + curve: actual_curve, + range: actual_range, + }, + EdgeGeometry::Curve { + curve: expected_curve, + range: expected_range, + }, + ) = (&mut actual.geometry, &expected.geometry) + else { + return Err(torus_gap()); + }; + if actual_curve != expected_curve + || (actual_range.lo - expected_range.lo).abs() > geometric + || (actual_range.hi - expected_range.hi).abs() > geometric + { + return Err(torus_gap()); + } + *actual_range = *expected_range; + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/shell.rs b/main/opengeometry/src/operations/modifying/boolean/shell.rs new file mode 100644 index 0000000..bf4aa13 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/shell.rs @@ -0,0 +1,367 @@ +use super::handlers::analytic_containment_boolean; +use super::operands::{ + brep_face_source, full_box, full_conic_section, full_cylinder, full_planar_extrusion, + full_sphere, full_torus, prismatic_gap, unique_sources, BoxInput, +}; +use super::types::{BooleanOp, BooleanResult, FaceMapping}; +use crate::brep::{BrepEnvelope, Frame3, GeometryError, SurfaceGeometry}; +use crate::geom2d::{self_intersects2, signed_area2, Pt2}; +use crate::math::{scale, Point3}; +use crate::primitives; + +pub fn shell_brep( + input: &BrepEnvelope, + thickness: f64, + id: String, +) -> Result { + input.validate()?; + if !thickness.is_finite() || thickness <= 4.0 * input.accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "shell thickness is below geometric resolution".into(), + )); + } + let inner_id = format!("{id}:offset-cavity"); + let inner = offset_cavity(input, thickness, inner_id)?; + let inner_identity = inner.id.clone(); + let mut result = analytic_containment_boolean(input, &inner, true, BooleanOp::Subtraction, id)?; + set_cavity_provenance(&mut result.brep, input, &inner_identity)?; + result.report.face_mappings = input + .topology + .faces + .iter() + .map(|face| { + let source = brep_face_source(input, face.id); + let result_faces = result + .brep + .topology + .faces + .iter() + .filter(|candidate| { + candidate.provenance.sources.iter().any(|candidate| { + candidate.entity == source.entity + && candidate.body == source.body + && candidate.key == source.key + && candidate.face == source.face + }) + }) + .map(|candidate| candidate.id) + .collect(); + FaceMapping { + source, + result_faces, + } + }) + .collect(); + result.brep.validate()?; + Ok(result) +} + +fn offset_cavity( + input: &BrepEnvelope, + thickness: f64, + inner_id: String, +) -> Result { + match input.geometry.surfaces.first() { + Some(SurfaceGeometry::Sphere { .. }) => sphere_cavity(input, thickness, inner_id), + Some(SurfaceGeometry::Cylinder { .. }) => cylinder_cavity(input, thickness, inner_id), + Some(SurfaceGeometry::Torus { .. }) => torus_cavity(input, thickness, inner_id), + Some(SurfaceGeometry::Plane { .. }) => match full_box(input) { + Ok(input) => cuboid_cavity(&input, thickness, inner_id), + Err(GeometryError::CoverageGap { .. }) => extrusion_cavity(input, thickness, inner_id), + Err(error) => Err(error), + }, + Some(SurfaceGeometry::Cone { .. }) => cone_cavity(input, thickness, inner_id), + None => Err(GeometryError::CoverageGap { + families: ["restricted analytic shell".into(), "input solid".into()], + }), + } +} + +fn sphere_cavity( + input: &BrepEnvelope, + thickness: f64, + inner_id: String, +) -> Result { + let input = full_sphere(input)?; + let radius = input.radius - thickness; + if radius <= 4.0 * input.brep.accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "shell thickness consumes the sphere interior".into(), + )); + } + primitives::sphere(inner_id, input.frame, radius, input.brep.accuracy) +} + +fn cylinder_cavity( + input: &BrepEnvelope, + thickness: f64, + inner_id: String, +) -> Result { + let input = full_cylinder(input)?; + let radius = input.radius - thickness; + let height = input.height - 2.0 * thickness; + if radius <= 4.0 * input.brep.accuracy.geometric + || height <= 4.0 * input.brep.accuracy.geometric + { + return Err(GeometryError::UnresolvedIntersection( + "shell thickness consumes the cylinder interior".into(), + )); + } + let mut frame = input.frame; + frame.origin = input.frame.point([0.0, 0.0, thickness]); + primitives::cylinder(inner_id, frame, radius, height, input.brep.accuracy) +} + +fn torus_cavity( + input: &BrepEnvelope, + thickness: f64, + inner_id: String, +) -> Result { + let input = full_torus(input)?; + let minor_radius = input.minor_radius - thickness; + if minor_radius <= 4.0 * input.brep.accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "shell thickness consumes the torus interior".into(), + )); + } + primitives::torus( + inner_id, + input.frame, + input.major_radius, + minor_radius, + input.brep.accuracy, + ) +} + +fn cuboid_cavity( + input: &BoxInput<'_>, + thickness: f64, + inner_id: String, +) -> Result { + let size = input.size.map(|dimension| dimension - 2.0 * thickness); + if size + .iter() + .any(|dimension| *dimension <= 4.0 * input.brep.accuracy.geometric) + { + return Err(GeometryError::UnresolvedIntersection( + "shell thickness consumes the cuboid interior".into(), + )); + } + let mut frame = input.frame; + frame.origin = input.frame.point([thickness, thickness, thickness]); + primitives::cuboid(inner_id, frame, size, input.brep.accuracy) +} + +fn extrusion_cavity( + input: &BrepEnvelope, + thickness: f64, + inner_id: String, +) -> Result { + let input = full_planar_extrusion(input)?; + let height = input.height - 2.0 * thickness; + if height <= 4.0 * input.brep.accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "shell thickness consumes the profile extrusion height".into(), + )); + } + let inset = input + .contours + .iter() + .map(|ring| { + inset_prismatic_ring(ring, input.frame, thickness, input.brep.accuracy.geometric) + }) + .collect::, _>>()?; + let mut frame = input.frame; + frame.origin = input.frame.point([0.0, 0.0, thickness]); + primitives::linear_extrusion( + inner_id, + frame, + inset[0].clone(), + inset[1..].to_vec(), + height, + input.brep.accuracy, + ) + .map_err(|error| match error { + GeometryError::InvalidGeometry(_) | GeometryError::UnresolvedIntersection(_) => { + GeometryError::UnresolvedIntersection( + "shell offset self-intersects or consumes the profile interior".into(), + ) + } + other => other, + }) +} + +fn inset_prismatic_ring( + ring: &[Point3], + frame: Frame3, + thickness: f64, + tolerance: f64, +) -> Result, GeometryError> { + let points = ring + .iter() + .map(|point| { + let local = frame.local(*point); + Pt2::new(local[0], local[1]) + }) + .collect::>(); + if points.len() < 3 { + return Err(prismatic_gap()); + } + let direction = |from: Pt2, to: Pt2| -> Result { + let delta = Pt2::new(to.x - from.x, to.z - from.z); + let length = delta.x.hypot(delta.z); + if length <= 4.0 * tolerance { + return Err(GeometryError::UnresolvedIntersection( + "profile shell contains a sub-tolerance edge".into(), + )); + } + Ok(Pt2::new(delta.x / length, delta.z / length)) + }; + let mut result = Vec::with_capacity(points.len()); + for index in 0..points.len() { + let point = points[index]; + let incoming = direction(points[(index + points.len() - 1) % points.len()], point)?; + let outgoing = direction(point, points[(index + 1) % points.len()])?; + let offset = miter_offset(point, incoming, outgoing, thickness)?; + if !offset.x.is_finite() + || !offset.z.is_finite() + || (offset.x - point.x).hypot(offset.z - point.z) > thickness * 1.0e6 + { + return Err(GeometryError::UnresolvedIntersection( + "profile shell has an unbounded miter".into(), + )); + } + result.push(offset); + } + let original_area = signed_area2(&points); + let result_area = signed_area2(&result); + if result_area.abs() <= tolerance * tolerance + || original_area.signum() != result_area.signum() + || self_intersects2(&result, tolerance) + { + return Err(GeometryError::UnresolvedIntersection( + "profile shell offset collapses or self-intersects".into(), + )); + } + Ok(result.into_iter().map(|point| [point.x, point.z]).collect()) +} + +fn miter_offset( + point: Pt2, + incoming: Pt2, + outgoing: Pt2, + thickness: f64, +) -> Result { + let cross2 = |a: Pt2, b: Pt2| a.x * b.z - a.z * b.x; + let incoming_normal = Pt2::new(-incoming.z, incoming.x); + let outgoing_normal = Pt2::new(-outgoing.z, outgoing.x); + let incoming_origin = Pt2::new( + point.x + thickness * incoming_normal.x, + point.z + thickness * incoming_normal.z, + ); + let outgoing_origin = Pt2::new( + point.x + thickness * outgoing_normal.x, + point.z + thickness * outgoing_normal.z, + ); + let determinant = cross2(incoming, outgoing); + let offset = if determinant.abs() <= 1.0e-12 { + if incoming.x * outgoing.x + incoming.z * outgoing.z < 0.0 { + return Err(GeometryError::UnresolvedIntersection( + "profile shell has a reversing corner".into(), + )); + } + incoming_origin + } else { + let delta = Pt2::new( + outgoing_origin.x - incoming_origin.x, + outgoing_origin.z - incoming_origin.z, + ); + let parameter = cross2(delta, outgoing) / determinant; + Pt2::new( + incoming_origin.x + parameter * incoming.x, + incoming_origin.z + parameter * incoming.z, + ) + }; + Ok(offset) +} + +fn cone_cavity( + input: &BrepEnvelope, + thickness: f64, + inner_id: String, +) -> Result { + let input = full_conic_section(input)?; + let slope = input.semi_angle.tan(); + let normal_scale = (1.0 + slope * slope).sqrt(); + if input.lower_radius <= input.brep.accuracy.geometric { + let apex = thickness * normal_scale / slope; + let base = input.axial_range.hi - thickness; + let height = base - apex; + let radius = height * slope; + if radius <= 4.0 * input.brep.accuracy.geometric + || height <= 4.0 * input.brep.accuracy.geometric + { + return Err(GeometryError::UnresolvedIntersection( + "shell thickness consumes the cone interior".into(), + )); + } + let base_frame = Frame3 { + origin: input.frame.point([0.0, 0.0, base]), + x: input.frame.x, + y: scale(input.frame.y, -1.0), + z: scale(input.frame.z, -1.0), + }; + primitives::cone(inner_id, base_frame, radius, height, input.brep.accuracy) + } else { + let height = input.axial_range.width() - 2.0 * thickness; + let lower_radius = input.lower_radius + thickness * slope - thickness * normal_scale; + let upper_radius = input.upper_radius - thickness * slope - thickness * normal_scale; + if height <= 4.0 * input.brep.accuracy.geometric + || lower_radius <= 4.0 * input.brep.accuracy.geometric + || upper_radius <= 4.0 * input.brep.accuracy.geometric + { + return Err(GeometryError::UnresolvedIntersection( + "shell thickness consumes the frustum interior".into(), + )); + } + let frame = Frame3 { + origin: input + .frame + .point([0.0, 0.0, input.axial_range.lo + thickness]), + ..input.frame + }; + primitives::frustum( + inner_id, + frame, + lower_radius, + upper_radius, + height, + input.brep.accuracy, + ) + } +} + +fn set_cavity_provenance( + brep: &mut BrepEnvelope, + input: &BrepEnvelope, + inner_identity: &str, +) -> Result<(), GeometryError> { + for face in &mut brep.topology.faces { + for source in &mut face.provenance.sources { + if source.entity == inner_identity && source.body == inner_identity { + let original = input + .topology + .faces + .get(source.face as usize) + .ok_or_else(|| { + GeometryError::InvalidTopology( + "shell offset face has no source face".into(), + ) + })?; + *source = brep_face_source(input, original.id); + } + } + face.provenance.sources = unique_sources(std::mem::take(&mut face.provenance.sources)); + } + Ok(()) +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/arc_extrusions_openings.rs b/main/opengeometry/src/operations/modifying/boolean/tests/arc_extrusions_openings.rs new file mode 100644 index 0000000..816c4a9 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/arc_extrusions_openings.rs @@ -0,0 +1,383 @@ +use super::support::sphere; +use crate::brep::{BrepEnvelope, Frame3, Orientation, SurfaceGeometry}; +use crate::operations::modifying::boolean::batch::subtract_planar_cutters; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::query::{classify_point, PointClassification}; +use crate::tessellation::tessellate; + +#[test] +fn annular_sector_accepts_an_exact_vertical_sector_opening() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let host = primitives::annular_cylinder( + "ring-host".into(), + Frame3::IDENTITY, + 1.9, + 2.1, + 3.0, + accuracy, + ) + .unwrap(); + let reach = 2.3; + let cutter = primitives::linear_extrusion( + "ring-opening".into(), + Frame3 { + origin: [0.0, 0.0, 0.5], + ..Frame3::IDENTITY + }, + vec![ + [0.0, 0.0], + [reach * (-0.1_f64).cos(), reach * (-0.1_f64).sin()], + [reach * 0.1_f64.cos(), reach * 0.1_f64.sin()], + ], + vec![], + 2.0, + accuracy, + ) + .unwrap(); + let cut = boolean_brep(&host, &cutter, BooleanOp::Subtraction, "ring-cut".into()).unwrap(); + cut.brep.validate().unwrap(); + tessellate(&cut.brep, 0.01, 2_000_000).unwrap(); + assert_eq!( + classify_point(&cut.brep, [2.0, 0.0, 1.0]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&cut.brep, [0.0, 2.0, 1.0]).unwrap(), + PointClassification::Inside + ); + assert_eq!( + classify_point(&cut.brep, [0.0, 0.0, 1.0]).unwrap(), + PointClassification::Outside + ); + let second = primitives::linear_extrusion( + "second-ring-opening".into(), + Frame3 { + origin: [0.0, 0.0, 0.8], + ..Frame3::IDENTITY + }, + vec![ + [0.0, 0.0], + [ + reach * (std::f64::consts::FRAC_PI_2 - 0.1).cos(), + reach * (std::f64::consts::FRAC_PI_2 - 0.1).sin(), + ], + [ + reach * (std::f64::consts::FRAC_PI_2 + 0.1).cos(), + reach * (std::f64::consts::FRAC_PI_2 + 0.1).sin(), + ], + ], + vec![], + 1.3, + accuracy, + ) + .unwrap(); + let chained = boolean_brep( + &cut.brep, + &second, + BooleanOp::Subtraction, + "ring-cut-chained".into(), + ) + .unwrap(); + chained.brep.validate().unwrap(); + tessellate(&chained.brep, 0.01, 2_000_000).unwrap(); + for point in [[2.0, 0.0, 1.0], [0.0, 2.0, 1.0]] { + assert_eq!( + classify_point(&chained.brep, point).unwrap(), + PointClassification::Outside + ); + } + assert_eq!( + classify_point(&chained.brep, [-2.0, 0.0, 1.0]).unwrap(), + PointClassification::Inside + ); +} + +#[test] +fn arc_edged_host_accepts_a_vertical_rectangular_opening_cut() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let quarter = std::f64::consts::FRAC_PI_2; + let host = primitives::arc_edged_extrusion( + "arc-host".into(), + Frame3::IDENTITY, + vec![ + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 2.0, + start_angle: 0.0, + sweep_angle: quarter, + }, + primitives::ProfileEdge::Line { + from: [0.0, 2.0], + to: [0.0, 1.5], + }, + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.5, + start_angle: quarter, + sweep_angle: -quarter, + }, + primitives::ProfileEdge::Line { + from: [1.5, 0.0], + to: [2.0, 0.0], + }, + ], + 3.0, + accuracy, + ) + .unwrap(); + let cutter = primitives::cuboid( + "opening".into(), + Frame3 { + origin: [1.1, 0.9, 0.5], + ..Frame3::IDENTITY + }, + [1.0, 0.35, 1.5], + accuracy, + ) + .unwrap(); + let assert_curved_normals = |brep: &BrepEnvelope| { + for (radius, expected) in [(2.0, Orientation::Forward), (1.5, Orientation::Reverse)] { + let faces = brep.topology.faces.iter().filter(|face| { + matches!( + brep.geometry.surfaces[face.surface as usize], + SurfaceGeometry::Cylinder { radius: value, .. } if (value - radius).abs() < 1e-9 + ) + }).collect::>(); + assert!(!faces.is_empty(), "missing curved face at radius {radius}"); + assert!( + faces.iter().all(|face| face.sense == expected), + "reconstructed curved face at radius {radius} points into material: {:?}", + faces + .iter() + .map(|face| (&face.key, face.sense)) + .collect::>() + ); + } + }; + let cut = boolean_brep(&host, &cutter, BooleanOp::Subtraction, "arc-cut".into()).unwrap(); + cut.brep.validate().unwrap(); + assert_curved_normals(&cut.brep); + tessellate(&cut.brep, 0.01, 2_000_000).unwrap(); + assert_eq!( + classify_point(&cut.brep, [1.5, 1.1, 1.2]).unwrap(), + PointClassification::Outside + ); + let second_cutter = primitives::cuboid( + "second-opening".into(), + Frame3 { + origin: [0.6, 1.3, 0.8], + ..Frame3::IDENTITY + }, + [0.45, 0.8, 1.2], + accuracy, + ) + .unwrap(); + let batch = subtract_planar_cutters( + &host, + &[cutter, second_cutter.clone()], + "arc-cut-batch".into(), + ) + .unwrap(); + batch.brep.validate().unwrap(); + tessellate(&batch.brep, 0.01, 2_000_000).unwrap(); + assert_curved_normals(&batch.brep); + for point in [[1.5, 1.1, 1.2], [0.9, 1.65, 1.2]] { + assert_eq!( + classify_point(&batch.brep, point).unwrap(), + PointClassification::Outside + ); + } + let chained = boolean_brep( + &cut.brep, + &second_cutter, + BooleanOp::Subtraction, + "arc-cut-chained".into(), + ) + .unwrap(); + chained.brep.validate().unwrap(); + assert_curved_normals(&chained.brep); + for point in [[1.5, 1.1, 1.2], [0.9, 1.65, 1.2]] { + assert_eq!( + classify_point(&chained.brep, point).unwrap(), + PointClassification::Outside + ); + } + let overlapping = primitives::cuboid( + "overlapping-opening".into(), + Frame3 { + origin: [1.4, 0.95, 0.2], + ..Frame3::IDENTITY + }, + [0.5, 0.4, 2.2], + accuracy, + ) + .unwrap(); + let third = boolean_brep( + &chained.brep, + &overlapping, + BooleanOp::Subtraction, + "arc-cut-third".into(), + ) + .unwrap(); + third.brep.validate().unwrap(); + tessellate(&third.brep, 0.01, 2_000_000).unwrap(); + assert_curved_normals(&third.brep); + for point in [ + [1.5, 1.1, 1.2], + [0.9, 1.65, 1.2], + [1.45, 1.2, 0.3], + [1.45, 1.2, 2.2], + ] { + assert_eq!( + classify_point(&third.brep, point).unwrap(), + PointClassification::Outside + ); + } +} + +#[test] +fn curved_cut_provenance_selects_the_trimmed_coaxial_source_face() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let quarter = std::f64::consts::FRAC_PI_2; + let host = primitives::arc_edged_extrusion( + "split-arc-host".into(), + Frame3::IDENTITY, + vec![ + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 2.0, + start_angle: 0.0, + sweep_angle: quarter / 2.0, + }, + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 2.0, + start_angle: quarter / 2.0, + sweep_angle: quarter / 2.0, + }, + primitives::ProfileEdge::Line { + from: [0.0, 2.0], + to: [0.0, 1.5], + }, + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.5, + start_angle: quarter, + sweep_angle: -quarter, + }, + primitives::ProfileEdge::Line { + from: [1.5, 0.0], + to: [2.0, 0.0], + }, + ], + 3.0, + accuracy, + ) + .unwrap(); + let cutter = primitives::cuboid( + "split-arc-opening".into(), + Frame3 { + origin: [1.1, 0.9, 0.5], + ..Frame3::IDENTITY + }, + [1.0, 0.35, 1.5], + accuracy, + ) + .unwrap(); + let cut = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + "split-arc-cut".into(), + ) + .unwrap(); + cut.brep.validate().unwrap(); + let mut outer_faces = 0; + for face in &cut.brep.topology.faces { + let SurfaceGeometry::Cylinder { radius, .. } = + &cut.brep.geometry.surfaces[face.surface as usize] + else { + continue; + }; + if (*radius - 2.0).abs() > accuracy.geometric { + continue; + } + outer_faces += 1; + assert_eq!( + face.provenance + .sources + .iter() + .filter(|source| source.entity == host.id) + .count(), + 1, + "output face {} should belong to exactly one trimmed outer arc", + face.id + ); + } + assert!(outer_faces >= 2); +} + +#[test] +fn wide_curved_opening_uses_multiple_planar_sector_cutters() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let start = -std::f64::consts::FRAC_PI_2; + let sweep = 3.0 * std::f64::consts::FRAC_PI_2; + let point = |radius: f64, angle: f64| [radius * angle.cos(), radius * angle.sin()]; + let host = primitives::arc_edged_extrusion( + "wide-arc".into(), + Frame3::IDENTITY, + vec![ + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 2.1, + start_angle: start, + sweep_angle: sweep, + }, + primitives::ProfileEdge::Line { + from: point(2.1, start + sweep), + to: point(1.9, start + sweep), + }, + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.9, + start_angle: start + sweep, + sweep_angle: -sweep, + }, + primitives::ProfileEdge::Line { + from: point(1.9, start), + to: point(2.1, start), + }, + ], + 3.0, + accuracy, + ) + .unwrap(); + let opening_start = start + 1.2 / 2.0; + let opening_end = start + 8.2 / 2.0; + let cutters = (0..3) + .map(|index| { + let from = opening_start + (opening_end - opening_start) * index as f64 / 3.0; + let to = opening_start + (opening_end - opening_start) * (index + 1) as f64 / 3.0; + let reach = 2.1 / ((to - from) / 2.0).cos() + 0.2; + primitives::linear_extrusion( + format!("sector-{index}"), + Frame3::IDENTITY, + vec![[0.0, 0.0], point(reach, from), point(reach, to)], + vec![], + 2.0, + accuracy, + ) + .unwrap() + }) + .collect::>(); + let cut = subtract_planar_cutters(&host, &cutters, "wide-cut".into()).unwrap(); + cut.brep.validate().unwrap(); + tessellate(&cut.brep, 0.01, 2_000_000).unwrap(); + let angle = start + 4.7 / 2.0; + assert_eq!( + classify_point(&cut.brep, [2.0 * angle.cos(), 2.0 * angle.sin(), 1.0]).unwrap(), + PointClassification::Outside + ); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/arc_extrusions_splits.rs b/main/opengeometry/src/operations/modifying/boolean/tests/arc_extrusions_splits.rs new file mode 100644 index 0000000..d7264c0 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/arc_extrusions_splits.rs @@ -0,0 +1,269 @@ +use super::support::sphere; +use crate::brep::{Frame3, SurfaceGeometry}; +use crate::math::dot; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::query::{classify_point, PointClassification}; +use crate::tessellation::tessellate; + +#[test] +fn curved_internal_cut_creates_one_cavity_shell_in_its_host_solid() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let quarter = std::f64::consts::FRAC_PI_2; + let host = primitives::arc_edged_extrusion( + "curved-cavity-host".into(), + Frame3::IDENTITY, + vec![ + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 2.0, + start_angle: 0.0, + sweep_angle: quarter, + }, + primitives::ProfileEdge::Line { + from: [0.0, 2.0], + to: [0.0, 1.0], + }, + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.0, + start_angle: quarter, + sweep_angle: -quarter, + }, + primitives::ProfileEdge::Line { + from: [1.0, 0.0], + to: [2.0, 0.0], + }, + ], + 3.0, + accuracy, + ) + .unwrap(); + let cutter = primitives::linear_extrusion( + "curved-interior-cutter".into(), + Frame3 { + origin: [0.0, 0.0, 1.0], + ..Frame3::IDENTITY + }, + vec![[1.0, 1.0], [1.2, 1.0], [1.2, 1.2], [1.0, 1.2]], + vec![], + 1.0, + accuracy, + ) + .unwrap(); + let result = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + "curved-cavity".into(), + ) + .unwrap(); + result.brep.validate().unwrap(); + tessellate(&result.brep, 0.01, 2_000_000).unwrap(); + assert_eq!(result.brep.solids.len(), 1); + assert_eq!(result.brep.topology.shells.len(), 2); + assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); + assert_eq!( + classify_point(&result.brep, [1.1, 1.1, 1.5]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [1.5, 0.5, 1.5]).unwrap(), + PointClassification::Inside + ); + + let split_sector = primitives::linear_extrusion( + "curved-split-sector".into(), + Frame3 { + origin: [0.0, 0.0, -1.0], + ..Frame3::IDENTITY + }, + vec![ + [0.0, 0.0], + [3.0 * 0.2_f64.cos(), 3.0 * 0.2_f64.sin()], + [3.0 * 0.3_f64.cos(), 3.0 * 0.3_f64.sin()], + ], + vec![], + 5.0, + accuracy, + ) + .unwrap(); + let split = boolean_brep( + &host, + &split_sector, + BooleanOp::Subtraction, + "curved-split".into(), + ) + .unwrap(); + assert_eq!(split.brep.solids.len(), 2); + let split_with_cavity = boolean_brep( + &split.brep, + &cutter, + BooleanOp::Subtraction, + "curved-split-with-cavity".into(), + ) + .unwrap(); + split_with_cavity.brep.validate().unwrap(); + assert_eq!(split_with_cavity.brep.solids.len(), 2); + assert_eq!(split_with_cavity.brep.topology.shells.len(), 3); + assert_eq!( + split_with_cavity + .brep + .solids + .iter() + .map(|solid| solid.cavity_shells.len()) + .sum::(), + 1 + ); + assert_eq!( + classify_point(&split_with_cavity.brep, [1.1, 1.1, 1.5]).unwrap(), + PointClassification::Outside + ); + + let second_cutter = primitives::linear_extrusion( + "curved-other-component-cutter".into(), + Frame3 { + origin: [0.0, 0.0, 1.1], + ..Frame3::IDENTITY + }, + vec![[1.55, 0.1], [1.67, 0.1], [1.67, 0.22], [1.55, 0.22]], + vec![], + 0.8, + accuracy, + ) + .unwrap(); + let same_component_cavities = boolean_brep( + &result.brep, + &second_cutter, + BooleanOp::Subtraction, + "curved-same-component-cavities".into(), + ) + .unwrap(); + same_component_cavities.brep.validate().unwrap(); + assert_eq!(same_component_cavities.brep.solids.len(), 1); + assert_eq!(same_component_cavities.brep.topology.shells.len(), 3); + assert_eq!( + same_component_cavities.brep.solids[0].cavity_shells.len(), + 2 + ); + let two_cavities = boolean_brep( + &split_with_cavity.brep, + &second_cutter, + BooleanOp::Subtraction, + "curved-two-cavities".into(), + ) + .unwrap(); + two_cavities.brep.validate().unwrap(); + assert_eq!(two_cavities.brep.solids.len(), 2); + assert_eq!(two_cavities.brep.topology.shells.len(), 4); + assert!(two_cavities + .brep + .solids + .iter() + .all(|solid| solid.cavity_shells.len() == 1)); + for point in [[1.1, 1.1, 1.5], [1.6, 0.16, 1.5]] { + assert_eq!( + classify_point(&two_cavities.brep, point).unwrap(), + PointClassification::Outside + ); + } +} + +#[test] +fn chained_curved_cut_keeps_each_split_top_cap_on_its_source_region() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let quarter = std::f64::consts::FRAC_PI_2; + let host = primitives::arc_edged_extrusion( + "split-cap-arc-host".into(), + Frame3::IDENTITY, + vec![ + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 2.0, + start_angle: 0.0, + sweep_angle: quarter, + }, + primitives::ProfileEdge::Line { + from: [0.0, 2.0], + to: [0.0, 1.5], + }, + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.5, + start_angle: quarter, + sweep_angle: -quarter, + }, + primitives::ProfileEdge::Line { + from: [1.5, 0.0], + to: [2.0, 0.0], + }, + ], + 3.0, + accuracy, + ) + .unwrap(); + let sector = |id: &str, start: f64, end: f64, bottom: f64, height: f64| { + let reach = 3.0; + primitives::linear_extrusion( + id.into(), + Frame3 { + origin: [0.0, 0.0, bottom], + ..Frame3::IDENTITY + }, + vec![ + [0.0, 0.0], + [reach * start.cos(), reach * start.sin()], + [reach * end.cos(), reach * end.sin()], + ], + Vec::new(), + height, + accuracy, + ) + .unwrap() + }; + let first_cutter = sector("full-height-sector", 0.7, 0.9, -1.0, 5.0); + let first = boolean_brep( + &host, + &first_cutter, + BooleanOp::Subtraction, + "split-cap-first".into(), + ) + .unwrap(); + first.brep.validate().unwrap(); + assert_eq!(first.brep.solids.len(), 2); + let second_cutter = sector("lower_cutout-sector", 0.2, 0.3, 0.5, 1.5); + let second = boolean_brep( + &first.brep, + &second_cutter, + BooleanOp::Subtraction, + "split-cap-second".into(), + ) + .unwrap(); + second.brep.validate().unwrap(); + let mut caps = 0; + for face in &second.brep.topology.faces { + let SurfaceGeometry::Plane { frame } = + &second.brep.geometry.surfaces[face.surface as usize] + else { + continue; + }; + if dot(frame.z, [0.0, 0.0, 1.0]) < 1.0 - 1.0e-10 + || (frame.origin[2] - 3.0).abs() > accuracy.intersection + { + continue; + } + caps += 1; + assert_eq!( + face.provenance + .sources + .iter() + .filter(|source| source.entity == first.brep.id) + .count(), + 1, + "top cap {} should come from one prior top region", + face.id + ); + } + assert_eq!(caps, 2); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/conic_torus.rs b/main/opengeometry/src/operations/modifying/boolean/tests/conic_torus.rs new file mode 100644 index 0000000..1ded39c --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/conic_torus.rs @@ -0,0 +1,296 @@ +use super::support::{sphere, volume}; +use crate::brep::{FaceRole, Frame3, GeometryError, SurfaceGeometry}; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::tessellation::tessellate; + +#[test] +fn concentric_torus_booleans_preserve_exact_cavity_supports() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let host = primitives::torus("host".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy).unwrap(); + let cutter = primitives::torus("cutter".into(), Frame3::IDENTITY, 3.0, 0.4, accuracy).unwrap(); + let subtraction = + boolean_brep(&host, &cutter, BooleanOp::Subtraction, "torus-shell".into()).unwrap(); + subtraction.brep.validate().unwrap(); + assert_eq!(subtraction.brep.solids.len(), 1); + assert_eq!(subtraction.brep.solids[0].cavity_shells.len(), 1); + assert!(subtraction + .brep + .topology + .faces + .iter() + .any(|face| { face.provenance.role == FaceRole::Cut && face.provenance.reversed })); + assert!(subtraction + .brep + .geometry + .surfaces + .iter() + .all(|surface| matches!(surface, SurfaceGeometry::Torus { .. }))); + tessellate(&subtraction.brep, 0.05, 2_000_000).unwrap(); + + let intersection = + boolean_brep(&host, &cutter, BooleanOp::Intersection, "torus-core".into()).unwrap(); + assert_eq!(intersection.brep.topology.faces.len(), 1); + assert!(matches!( + intersection.brep.geometry.surfaces[0], + SurfaceGeometry::Torus { + minor_radius: 0.4, + .. + } + )); + + let coincident = + boolean_brep(&host, &host, BooleanOp::Subtraction, "empty-torus".into()).unwrap(); + assert!(coincident.report.coincident); + assert!(coincident.brep.solids.is_empty()); +} + +#[test] +fn sphere_torus_containment_is_exact_in_both_directions() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let torus = primitives::torus("torus".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy).unwrap(); + let small_sphere = sphere("small", [3.0, 0.0, 0.0], 0.25); + let torus_cut = boolean_brep( + &torus, + &small_sphere, + BooleanOp::Subtraction, + "torus-cut".into(), + ) + .unwrap(); + assert_eq!(torus_cut.brep.solids[0].cavity_shells.len(), 1); + assert!(torus_cut.brep.geometry.surfaces.iter().any( + |surface| matches!(surface, SurfaceGeometry::Sphere { radius, .. } if *radius == 0.25) + )); + + let large_sphere = sphere("large", [0.0; 3], 5.0); + let sphere_cut = boolean_brep( + &large_sphere, + &torus, + BooleanOp::Subtraction, + "sphere-cut".into(), + ) + .unwrap(); + sphere_cut.brep.validate().unwrap(); + assert_eq!(sphere_cut.brep.solids[0].cavity_shells.len(), 1); + assert!(sphere_cut + .brep + .topology + .faces + .iter() + .any(|face| face.provenance.role == FaceRole::Cut)); +} + +#[test] +fn coaxial_conic_containment_builds_exact_cavities_and_ownership() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let host = + primitives::frustum("host".into(), Frame3::IDENTITY, 3.0, 4.0, 4.0, accuracy).unwrap(); + let cutter = primitives::frustum( + "cutter".into(), + Frame3 { + origin: [0.0, 0.0, 1.0], + ..Frame3::IDENTITY + }, + 1.0, + 1.5, + 1.0, + accuracy, + ) + .unwrap(); + let host_volume = std::f64::consts::PI * 4.0 * (9.0 + 12.0 + 16.0) / 3.0; + let cutter_volume = std::f64::consts::PI * (1.0 + 1.5 + 2.25) / 3.0; + for (operation, expected_volume, expected_faces) in [ + (BooleanOp::Union, host_volume, 3), + (BooleanOp::Intersection, cutter_volume, 3), + (BooleanOp::Subtraction, host_volume - cutter_volume, 6), + ] { + let result = + boolean_brep(&host, &cutter, operation, format!("conic-{operation:?}")).unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.topology.faces.len(), expected_faces); + assert_eq!(result.report.face_mappings.len(), 6); + let measured = volume(&result.brep).abs(); + assert!( + (measured - expected_volume).abs() < 0.75, + "{operation:?}: measured {measured}, expected {expected_volume}" + ); + if operation == BooleanOp::Subtraction { + assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); + assert_eq!( + result + .brep + .topology + .faces + .iter() + .filter(|face| face.provenance.role == FaceRole::Cut + && face.provenance.reversed) + .count(), + 3 + ); + } + } + + let touching = + primitives::frustum("touching".into(), Frame3::IDENTITY, 1.0, 1.5, 1.0, accuracy).unwrap(); + assert!(matches!( + boolean_brep( + &host, + &touching, + BooleanOp::Subtraction, + "touching-result".into() + ), + Err(GeometryError::UnresolvedIntersection(_)) + )); +} + +#[test] +fn mixed_conic_containment_preserves_all_analytic_supports() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let conic = + primitives::frustum("frustum".into(), Frame3::IDENTITY, 3.0, 4.0, 4.0, accuracy).unwrap(); + let inner_sphere = primitives::sphere( + "inner-sphere".into(), + Frame3 { + origin: [0.0, 0.0, 2.0], + ..Frame3::IDENTITY + }, + 0.5, + accuracy, + ) + .unwrap(); + let outer_sphere = primitives::sphere( + "outer-sphere".into(), + Frame3 { + origin: [0.0, 0.0, 2.0], + ..Frame3::IDENTITY + }, + 6.0, + accuracy, + ) + .unwrap(); + let inner_cylinder = primitives::cylinder( + "inner-cylinder".into(), + Frame3 { + origin: [0.0, 0.0, 1.0], + ..Frame3::IDENTITY + }, + 0.5, + 1.0, + accuracy, + ) + .unwrap(); + let outer_cylinder = primitives::cylinder( + "outer-cylinder".into(), + Frame3 { + origin: [0.0, 0.0, -1.0], + ..Frame3::IDENTITY + }, + 5.0, + 6.0, + accuracy, + ) + .unwrap(); + let inner_box = primitives::cuboid( + "inner-box".into(), + Frame3 { + origin: [-0.25, -0.25, 1.75], + ..Frame3::IDENTITY + }, + [0.5, 0.5, 0.5], + accuracy, + ) + .unwrap(); + let outer_box = primitives::cuboid( + "outer-box".into(), + Frame3 { + origin: [-5.0, -5.0, -1.0], + ..Frame3::IDENTITY + }, + [10.0, 10.0, 6.0], + accuracy, + ) + .unwrap(); + + for (host, cutter, expected_faces) in [ + (&conic, &inner_sphere, 4), + (&outer_sphere, &conic, 4), + (&conic, &inner_cylinder, 6), + (&outer_cylinder, &conic, 6), + (&conic, &inner_box, 9), + (&outer_box, &conic, 9), + ] { + let result = boolean_brep( + host, + cutter, + BooleanOp::Subtraction, + format!("{}-minus-{}", host.id, cutter.id), + ) + .unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.solids.len(), 1); + assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); + assert_eq!(result.brep.topology.faces.len(), expected_faces); + assert_eq!( + result + .brep + .topology + .faces + .iter() + .filter(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed) + .count(), + cutter.topology.faces.len() + ); + tessellate(&result.brep, 0.02, 2_000_000).unwrap(); + } +} + +#[test] +fn ring_torus_containment_uses_exact_host_support_bounds() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let torus = primitives::torus( + "torus".into(), + Frame3 { + origin: [0.0, 0.0, 2.0], + ..Frame3::IDENTITY + }, + 2.0, + 0.5, + accuracy, + ) + .unwrap(); + let cylinder = + primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 4.0, 4.0, accuracy).unwrap(); + let box_ = primitives::cuboid( + "box".into(), + Frame3 { + origin: [-4.0, -4.0, -1.0], + ..Frame3::IDENTITY + }, + [8.0, 8.0, 6.0], + accuracy, + ) + .unwrap(); + let conic = + primitives::frustum("frustum".into(), Frame3::IDENTITY, 4.0, 5.0, 4.0, accuracy).unwrap(); + for host in [&cylinder, &box_, &conic] { + let result = boolean_brep( + host, + &torus, + BooleanOp::Subtraction, + format!("{}-minus-torus", host.id), + ) + .unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.solids.len(), 1); + assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); + assert!(result.brep.topology.faces.iter().any(|face| { + matches!( + result.brep.geometry.surfaces[face.surface as usize], + SurfaceGeometry::Torus { .. } + ) && face.provenance.role == FaceRole::Cut + && face.provenance.reversed + })); + tessellate(&result.brep, 0.02, 2_000_000).unwrap(); + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/cuboids.rs b/main/opengeometry/src/operations/modifying/boolean/tests/cuboids.rs new file mode 100644 index 0000000..924b8f4 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/cuboids.rs @@ -0,0 +1,301 @@ +use super::support::cylinder; +use crate::brep::{ + placed, Accuracy, BrepEnvelope, FaceRole, Frame3, GeometryError, Orientation, SurfaceGeometry, +}; +use crate::math::{cross, dot, Point3}; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::handlers::boolean_boxes; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::primitives::cuboid; +use crate::tessellation::tessellate; + +fn box_at(id: &str, origin: Point3, size: Point3) -> BrepEnvelope { + cuboid( + id.into(), + Frame3 { + origin, + x: [1.0, 0.0, 0.0], + y: [0.0, 1.0, 0.0], + z: [0.0, 0.0, 1.0], + }, + size, + Accuracy { + geometric: 1e-8, + intersection: 1e-9, + tessellation: 1e-3, + exchange: 1e-6, + }, + ) + .unwrap() +} + +fn volume(brep: &BrepEnvelope) -> f64 { + let mesh = tessellate(brep, 0.01, 100_000).unwrap(); + mesh.indices + .chunks_exact(3) + .map(|ids| { + let p: [Point3; 3] = std::array::from_fn(|j| { + std::array::from_fn(|i| mesh.positions[3 * ids[j] as usize + i]) + }); + dot(p[0], cross(p[1], p[2])) / 6.0 + }) + .sum() +} + +#[test] +fn aligned_box_boolean_volume_topology_provenance_and_exchange() { + let a = box_at("host", [0.0; 3], [2.0; 3]); + let b = box_at("cutter", [1.0, 0.5, 0.5], [2.0; 3]); + for (op, expected) in [ + (BooleanOp::Union, 13.75), + (BooleanOp::Intersection, 2.25), + (BooleanOp::Subtraction, 5.75), + ] { + let result = boolean_boxes(&a, &b, op, "result".into()).unwrap(); + assert!((volume(&result.brep) - expected).abs() < 1e-10); + assert_eq!(result.brep.solids.len(), 1); + assert_eq!(result.report.face_mappings.len(), 12); + assert!(result + .brep + .topology + .faces + .iter() + .all(|f| !f.provenance.sources.is_empty())); + assert!(result + .brep + .geometry + .surfaces + .iter() + .all(|s| matches!(s, SurfaceGeometry::Plane { .. }))); + let fine = tessellate(&result.brep, 0.001, 100_000).unwrap(); + let coarse = tessellate(&result.brep, 0.1, 100_000).unwrap(); + assert_eq!(fine.indices.len(), coarse.indices.len()); + if op == BooleanOp::Union { + assert!(fine.outline_edge_ids.len() < result.brep.topology.edges.len()); + } + if op == BooleanOp::Subtraction { + assert!(result + .brep + .topology + .faces + .iter() + .any(|f| f.provenance.role == FaceRole::Cut + && f.provenance.reversed + && f.sense == Orientation::Reverse)); + } + } +} + +#[test] +fn aligned_box_cavities_split_solids_and_contacts() { + let a = box_at("host", [0.0; 3], [3.0; 3]); + let b = box_at("cavity", [1.0; 3], [1.0; 3]); + let result = boolean_boxes(&a, &b, BooleanOp::Subtraction, "cavity-result".into()).unwrap(); + assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); + assert!((volume(&result.brep) - 26.0).abs() < 1e-10); + let cutter = box_at("through", [1.0, -1.0, -1.0], [1.0, 5.0, 5.0]); + let result = boolean_boxes(&a, &cutter, BooleanOp::Subtraction, "split".into()).unwrap(); + assert_eq!(result.brep.solids.len(), 2); + assert!((volume(&result.brep) - 18.0).abs() < 1e-10); + for (origin, solids, contacts) in [ + ([3.0, 0.0, 0.0], 1, 4), + ([3.0, 3.0, 0.0], 2, 2), + ([3.0; 3], 2, 1), + ] { + let b = box_at("contact", origin, [3.0; 3]); + let result = boolean_boxes(&a, &b, BooleanOp::Union, "contact-result".into()).unwrap(); + assert_eq!(result.brep.solids.len(), solids); + assert_eq!(result.report.contacts.len(), contacts); + let intersection = boolean_boxes(&a, &b, BooleanOp::Intersection, "empty".into()).unwrap(); + assert!(intersection.brep.solids.is_empty()); + } +} + +#[test] +fn aligned_box_boolean_identities_determinism_and_scale() { + let a = box_at("a", [0.0; 3], [2.0; 3]); + for op in [ + BooleanOp::Union, + BooleanOp::Intersection, + BooleanOp::Subtraction, + ] { + let result = boolean_brep(&a, &a, op, "identity".into()).unwrap(); + let expected = if op == BooleanOp::Subtraction { + 0.0 + } else { + 8.0 + }; + assert!((volume(&result.brep) - expected).abs() < 1e-10); + let repeat = boolean_brep(&a, &a, op, "identity".into()).unwrap(); + assert_eq!( + result.brep.to_json().unwrap(), + repeat.brep.to_json().unwrap() + ); + assert_eq!(result.brep.revision, a.revision + 1); + } + for factor in [1e-8, 1.0, 1e5] { + let b = box_at("b", [1.0, 0.5, 0.5], [2.0; 3]); + let frame = Frame3::from_axis([0.0; 3], [0.0, 1.0, 0.0], [0.8, 0.0, 0.6]).unwrap(); + let a = placed(&a, frame, factor).unwrap(); + let b = placed(&b, frame, factor).unwrap(); + let result = boolean_boxes(&a, &b, BooleanOp::Subtraction, "scaled".into()).unwrap(); + let mesh = tessellate(&result.brep, 0.01 * factor, 100_000).unwrap(); + let measured: f64 = mesh + .indices + .chunks_exact(3) + .map(|ids| { + let p: [Point3; 3] = std::array::from_fn(|j| { + std::array::from_fn(|i| mesh.positions[3 * ids[j] as usize + i] / factor) + }); + dot(p[0], cross(p[1], p[2])) / 6.0 + }) + .sum(); + assert!((measured - 5.75).abs() < 1e-9); + } +} + +#[test] +fn aligned_box_similarity_and_resolution_errors() { + let a = box_at("a", [0.0; 3], [2.0; 3]); + let b = box_at("b", [1.0, 0.5, 0.5], [2.0; 3]); + let frame = Frame3::from_axis([5.0, 6.0, 7.0], [0.0, 1.0, 0.0], [0.8, 0.0, 0.6]).unwrap(); + let a = placed(&a, frame, 1.25).unwrap(); + let b = placed(&b, frame, 1.25).unwrap(); + let result = boolean_boxes(&a, &b, BooleanOp::Subtraction, "placed".into()).unwrap(); + assert!((volume(&result.brep) - 5.75 * 1.25_f64.powi(3)).abs() < 1e-9); + let other = box_at("different-axes", [0.0; 3], [2.0; 3]); + assert!(matches!( + boolean_boxes(&a, &other, BooleanOp::Union, "gap".into()), + Err(GeometryError::CoverageGap { .. }) + )); + let a = box_at("a", [0.0; 3], [2.0; 3]); + let b = box_at("b", [2.0 + 2.0 * a.accuracy.geometric, 0.0, 0.0], [2.0; 3]); + assert!(matches!( + boolean_boxes(&a, &b, BooleanOp::Union, "unresolved".into()), + Err(GeometryError::UnresolvedIntersection(_)) + )); +} + +#[test] +fn mixed_cuboid_containment_builds_exact_curved_and_planar_cavities() { + let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; + let box_host = primitives::cuboid( + "box".into(), + Frame3 { + origin: [-2.0, -2.0, -2.0], + ..Frame3::IDENTITY + }, + [4.0; 3], + accuracy, + ) + .unwrap(); + let sphere_cutter = + primitives::sphere("sphere".into(), Frame3::IDENTITY, 0.5, accuracy).unwrap(); + let cylinder_cutter = primitives::cylinder( + "cylinder".into(), + Frame3 { + origin: [0.0, 0.0, -0.5], + ..Frame3::IDENTITY + }, + 0.5, + 1.0, + accuracy, + ) + .unwrap(); + for (name, cutter, expected_faces) in [ + ("sphere", &sphere_cutter, 7), + ("cylinder", &cylinder_cutter, 9), + ] { + let result = boolean_brep( + &box_host, + cutter, + BooleanOp::Subtraction, + format!("{name}-cavity"), + ) + .unwrap(); + assert_eq!(result.brep.solids.len(), 1); + assert_eq!(result.brep.topology.shells.len(), 2); + assert_eq!(result.brep.solids[0].cavity_shells, vec![1]); + assert_eq!(result.brep.topology.faces.len(), expected_faces); + assert!(result.brep.topology.faces[6..] + .iter() + .all(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed)); + result.brep.validate().unwrap(); + tessellate(&result.brep, 0.01, 2_000_000).unwrap(); + } + + let sphere_host = + primitives::sphere("sphere-host".into(), Frame3::IDENTITY, 4.0, accuracy).unwrap(); + let small_box = primitives::cuboid( + "small-box".into(), + Frame3 { + origin: [-0.5; 3], + ..Frame3::IDENTITY + }, + [1.0; 3], + accuracy, + ) + .unwrap(); + let sphere_box = boolean_brep( + &sphere_host, + &small_box, + BooleanOp::Subtraction, + "sphere-box".into(), + ) + .unwrap(); + assert_eq!(sphere_box.brep.topology.faces.len(), 7); + assert_eq!(sphere_box.brep.solids[0].cavity_shells, vec![1]); + + let cylinder_host = primitives::cylinder( + "cylinder-host".into(), + Frame3 { + origin: [0.0, 0.0, -2.0], + ..Frame3::IDENTITY + }, + 3.0, + 4.0, + accuracy, + ) + .unwrap(); + let cylinder_box = boolean_brep( + &cylinder_host, + &small_box, + BooleanOp::Subtraction, + "cylinder-box".into(), + ) + .unwrap(); + assert_eq!(cylinder_box.brep.topology.faces.len(), 9); + assert_eq!(cylinder_box.brep.solids[0].cavity_shells, vec![1]); + + let crossing = primitives::sphere( + "crossing".into(), + Frame3 { + origin: [1.8, 0.0, 0.0], + ..Frame3::IDENTITY + }, + 0.5, + accuracy, + ) + .unwrap(); + let crossing_union = boolean_brep( + &box_host, + &crossing, + BooleanOp::Union, + "crossing-union".into(), + ) + .unwrap(); + assert_eq!(crossing_union.brep.topology.faces.len(), 7); + assert_eq!( + crossing_union + .brep + .topology + .faces + .iter() + .filter(|face| !face.trim.holes.is_empty()) + .count(), + 1 + ); + crossing_union.brep.validate().unwrap(); + tessellate(&crossing_union.brep, 0.01, 2_000_000).unwrap(); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/cuboids_curved_cuts.rs b/main/opengeometry/src/operations/modifying/boolean/tests/cuboids_curved_cuts.rs new file mode 100644 index 0000000..7ede891 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/cuboids_curved_cuts.rs @@ -0,0 +1,475 @@ +use crate::brep::{Accuracy, FaceRole, Frame3, SurfaceGeometry}; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::query::{classify_point, PointClassification}; +use crate::tessellation::tessellate; + +#[test] +fn perpendicular_cylinders_cut_exact_round_holes_through_every_cuboid_axis() { + let accuracy = Accuracy { + geometric: 1.0e-9, + intersection: 1.0e-10, + tessellation: 0.01, + exchange: 1.0e-5, + }; + let size = [4.0, 3.0, 2.0]; + for axis in 0..3 { + let host = primitives::cuboid("box".into(), Frame3::IDENTITY, size, accuracy).unwrap(); + let mut local_origin = [2.0, 1.5, 1.0]; + local_origin[axis] = -1.0; + let direction = std::array::from_fn(|coordinate| f64::from(coordinate == axis)); + let reference = std::array::from_fn(|coordinate| f64::from(coordinate == (axis + 1) % 3)); + let cutter = primitives::cylinder( + "round-opening".into(), + Frame3::from_axis(local_origin, direction, reference).unwrap(), + 0.4, + size[axis] + 2.0, + accuracy, + ) + .unwrap(); + + let subtraction = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + format!("cut-{axis}"), + ) + .unwrap(); + subtraction.brep.validate().unwrap(); + assert_eq!(subtraction.brep.topology.faces.len(), 7); + assert_eq!( + subtraction + .brep + .topology + .faces + .iter() + .filter(|face| !face.trim.holes.is_empty()) + .count(), + 2 + ); + assert!(matches!( + subtraction.brep.geometry.surfaces.last(), + Some(SurfaceGeometry::Cylinder { .. }) + )); + assert_eq!( + classify_point(&subtraction.brep, [2.0, 1.5, 1.0]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&subtraction.brep, [0.2, 0.2, 0.2]).unwrap(), + PointClassification::Inside + ); + assert_eq!(subtraction.report.face_mappings.len(), 9); + + let union = + boolean_brep(&host, &cutter, BooleanOp::Union, format!("union-{axis}")).unwrap(); + union.brep.validate().unwrap(); + assert_eq!(union.brep.solids.len(), 1); + assert_eq!(union.brep.topology.faces.len(), 10); + assert_eq!( + union + .brep + .topology + .faces + .iter() + .filter(|face| !face.trim.holes.is_empty()) + .count(), + 2 + ); + for coordinate in [-0.5, size[axis] + 0.5] { + let mut point = [2.0, 1.5, 1.0]; + point[axis] = coordinate; + assert_eq!( + classify_point(&union.brep, point).unwrap(), + PointClassification::Inside + ); + } + assert_eq!( + classify_point(&union.brep, [0.2, 0.2, 0.2]).unwrap(), + PointClassification::Inside + ); + tessellate(&union.brep, 0.01, 2_000_000).unwrap(); + + let intersection = boolean_brep( + &host, + &cutter, + BooleanOp::Intersection, + format!("intersection-{axis}"), + ) + .unwrap(); + intersection.brep.validate().unwrap(); + assert_eq!(intersection.brep.topology.faces.len(), 3); + assert_eq!( + classify_point(&intersection.brep, [2.0, 1.5, 1.0]).unwrap(), + PointClassification::Inside + ); + assert_eq!( + classify_point(&intersection.brep, [0.2, 0.2, 0.2]).unwrap(), + PointClassification::Outside + ); + + let cutter_remainder = boolean_brep( + &cutter, + &host, + BooleanOp::Subtraction, + format!("cylinder-minus-box-{axis}"), + ) + .unwrap(); + cutter_remainder.brep.validate().unwrap(); + assert_eq!(cutter_remainder.brep.solids.len(), 2); + assert_eq!(cutter_remainder.brep.topology.faces.len(), 6); + assert_eq!( + cutter_remainder + .brep + .topology + .faces + .iter() + .filter(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed) + .count(), + 2 + ); + for coordinate in [-0.5, size[axis] + 0.5] { + let mut point = [2.0, 1.5, 1.0]; + point[axis] = coordinate; + assert_eq!( + classify_point(&cutter_remainder.brep, point).unwrap(), + PointClassification::Inside + ); + } + assert_eq!( + classify_point(&cutter_remainder.brep, [2.0, 1.5, 1.0]).unwrap(), + PointClassification::Outside + ); + } +} + +#[test] +fn perpendicular_cylinders_cut_exact_blind_pockets_from_both_cuboid_sides() { + let accuracy = Accuracy { + geometric: 1.0e-9, + intersection: 1.0e-10, + tessellation: 0.01, + exchange: 1.0e-5, + }; + let size = [4.0, 3.0, 2.0]; + let pocket_depth = 0.75; + for axis in 0..3 { + for entry_from_low in [true, false] { + let host = primitives::cuboid("box".into(), Frame3::IDENTITY, size, accuracy).unwrap(); + let mut origin = [2.0, 1.5, 1.0]; + origin[axis] = if entry_from_low { + -1.0 + } else { + size[axis] + 1.0 + }; + let direction = std::array::from_fn(|coordinate| { + if coordinate == axis { + if entry_from_low { + 1.0 + } else { + -1.0 + } + } else { + 0.0 + } + }); + let reference = + std::array::from_fn(|coordinate| f64::from(coordinate == (axis + 1) % 3)); + let cutter = primitives::cylinder( + "pocket".into(), + Frame3::from_axis(origin, direction, reference).unwrap(), + 0.4, + 1.0 + pocket_depth, + accuracy, + ) + .unwrap(); + + let subtraction = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + format!("pocket-{axis}-{entry_from_low}"), + ) + .unwrap(); + subtraction.brep.validate().unwrap(); + assert_eq!(subtraction.brep.topology.faces.len(), 8); + assert_eq!( + subtraction + .brep + .topology + .faces + .iter() + .filter(|face| !face.trim.holes.is_empty()) + .count(), + 1 + ); + assert_eq!( + subtraction + .brep + .topology + .faces + .iter() + .filter(|face| face.provenance.role == FaceRole::Cut) + .count(), + 2 + ); + assert!(subtraction + .brep + .topology + .faces + .iter() + .filter(|face| face.provenance.role == FaceRole::Cut) + .all(|face| face.provenance.reversed)); + + let union = boolean_brep( + &host, + &cutter, + BooleanOp::Union, + format!("pocket-union-{axis}-{entry_from_low}"), + ) + .unwrap(); + union.brep.validate().unwrap(); + assert_eq!(union.brep.solids.len(), 1); + assert_eq!(union.brep.topology.faces.len(), 8); + assert_eq!( + union + .brep + .topology + .faces + .iter() + .filter(|face| !face.trim.holes.is_empty()) + .count(), + 1 + ); + + let mut void_point = [2.0, 1.5, 1.0]; + void_point[axis] = if entry_from_low { + pocket_depth / 2.0 + } else { + size[axis] - pocket_depth / 2.0 + }; + assert_eq!( + classify_point(&subtraction.brep, void_point).unwrap(), + PointClassification::Outside + ); + let mut material_point = void_point; + material_point[axis] = if entry_from_low { + pocket_depth + 0.25 + } else { + size[axis] - pocket_depth - 0.25 + }; + assert_eq!( + classify_point(&subtraction.brep, material_point).unwrap(), + PointClassification::Inside + ); + + let intersection = boolean_brep( + &host, + &cutter, + BooleanOp::Intersection, + format!("pocket-intersection-{axis}-{entry_from_low}"), + ) + .unwrap(); + intersection.brep.validate().unwrap(); + assert_eq!(intersection.brep.topology.faces.len(), 3); + assert_eq!( + classify_point(&intersection.brep, void_point).unwrap(), + PointClassification::Inside + ); + assert_eq!( + classify_point(&intersection.brep, material_point).unwrap(), + PointClassification::Outside + ); + + let cutter_remainder = boolean_brep( + &cutter, + &host, + BooleanOp::Subtraction, + format!("pocket-cutter-minus-box-{axis}-{entry_from_low}"), + ) + .unwrap(); + cutter_remainder.brep.validate().unwrap(); + assert_eq!(cutter_remainder.brep.solids.len(), 1); + assert_eq!(cutter_remainder.brep.topology.faces.len(), 3); + assert_eq!( + cutter_remainder + .brep + .topology + .faces + .iter() + .filter(|face| face.provenance.role == FaceRole::Cut + && face.provenance.reversed) + .count(), + 1 + ); + let mut exterior_point = [2.0, 1.5, 1.0]; + exterior_point[axis] = if entry_from_low { + -0.5 + } else { + size[axis] + 0.5 + }; + assert_eq!( + classify_point(&cutter_remainder.brep, exterior_point).unwrap(), + PointClassification::Inside + ); + assert_eq!( + classify_point(&union.brep, exterior_point).unwrap(), + PointClassification::Inside + ); + assert_eq!( + classify_point(&union.brep, material_point).unwrap(), + PointClassification::Inside + ); + tessellate(&union.brep, 0.01, 2_000_000).unwrap(); + assert_eq!( + classify_point(&cutter_remainder.brep, void_point).unwrap(), + PointClassification::Outside + ); + } + } +} + +#[test] +fn spheres_cut_exact_single_face_pockets_on_every_cuboid_side() { + let accuracy = Accuracy { + geometric: 1.0e-9, + intersection: 1.0e-10, + tessellation: 0.01, + exchange: 1.0e-5, + }; + let size = [4.0, 3.0, 2.0]; + let radius = 0.75; + for axis in 0..3 { + for upper in [false, true] { + for center_inside in [false, true] { + let host = + primitives::cuboid("box".into(), Frame3::IDENTITY, size, accuracy).unwrap(); + let mut center = [2.0, 1.5, 1.0]; + center[axis] = match (upper, center_inside) { + (false, false) => -0.25, + (false, true) => 0.25, + (true, false) => size[axis] + 0.25, + (true, true) => size[axis] - 0.25, + }; + let cutter = primitives::sphere( + "spherical-pocket".into(), + Frame3 { + origin: center, + ..Frame3::IDENTITY + }, + radius, + accuracy, + ) + .unwrap(); + + let subtraction = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + format!("sphere-pocket-{axis}-{upper}-{center_inside}"), + ) + .unwrap(); + subtraction.brep.validate().unwrap(); + assert_eq!(subtraction.brep.topology.faces.len(), 7); + assert_eq!( + subtraction + .brep + .topology + .faces + .iter() + .filter(|face| !face.trim.holes.is_empty()) + .count(), + 1 + ); + assert_eq!( + subtraction + .brep + .topology + .faces + .iter() + .filter(|face| face.provenance.role == FaceRole::Cut + && face.provenance.reversed) + .count(), + 1 + ); + assert_eq!(subtraction.report.face_mappings.len(), 7); + + let inward_sign = if upper { -1.0 } else { 1.0 }; + let mut void_point = center; + void_point[axis] = if upper { size[axis] - 0.25 } else { 0.25 }; + let mut material_point = center; + material_point[axis] += inward_sign * (radius + 0.25); + let mut sphere_only_point = center; + sphere_only_point[axis] = if upper { size[axis] + 0.25 } else { -0.25 }; + assert_eq!( + classify_point(&subtraction.brep, void_point).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&subtraction.brep, material_point).unwrap(), + PointClassification::Inside + ); + + let intersection = boolean_brep( + &host, + &cutter, + BooleanOp::Intersection, + format!("sphere-intersection-{axis}-{upper}-{center_inside}"), + ) + .unwrap(); + intersection.brep.validate().unwrap(); + assert_eq!(intersection.brep.topology.faces.len(), 2); + assert_eq!( + classify_point(&intersection.brep, void_point).unwrap(), + PointClassification::Inside + ); + assert_eq!( + classify_point(&intersection.brep, material_point).unwrap(), + PointClassification::Outside + ); + + let union = boolean_brep( + &host, + &cutter, + BooleanOp::Union, + format!("sphere-union-{axis}-{upper}-{center_inside}"), + ) + .unwrap(); + union.brep.validate().unwrap(); + assert_eq!(union.brep.topology.faces.len(), 7); + for point in [void_point, material_point, sphere_only_point] { + assert_eq!( + classify_point(&union.brep, point).unwrap(), + PointClassification::Inside + ); + } + + let sphere_cut = boolean_brep( + &cutter, + &host, + BooleanOp::Subtraction, + format!("sphere-cut-{axis}-{upper}-{center_inside}"), + ) + .unwrap(); + sphere_cut.brep.validate().unwrap(); + assert_eq!(sphere_cut.brep.topology.faces.len(), 2); + assert!(sphere_cut.brep.topology.faces.iter().any(|face| { + face.provenance.role == FaceRole::Cut && face.provenance.reversed + })); + assert_eq!( + classify_point(&sphere_cut.brep, void_point).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&sphere_cut.brep, sphere_only_point).unwrap(), + PointClassification::Inside + ); + tessellate(&subtraction.brep, 0.01, 2_000_000).unwrap(); + tessellate(&intersection.brep, 0.01, 2_000_000).unwrap(); + tessellate(&union.brep, 0.01, 2_000_000).unwrap(); + tessellate(&sphere_cut.brep, 0.01, 2_000_000).unwrap(); + } + } + } +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/cylinders_coaxial.rs b/main/opengeometry/src/operations/modifying/boolean/tests/cylinders_coaxial.rs new file mode 100644 index 0000000..dc740a9 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/cylinders_coaxial.rs @@ -0,0 +1,235 @@ +use super::support::{cylinder, volume}; +use crate::brep::{placed, FaceRole, Frame3, GeometryError}; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::operands::full_cylinder; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::tessellation::tessellate; + +#[test] +fn coaxial_cylinder_booleans_split_material_and_keep_cut_cap_ancestry() { + let a = cylinder("a", 0.0, 4.0, false); + let b = cylinder("b", 3.0, 2.0, true); + for (operation, solids, faces, expected_volume) in [ + (BooleanOp::Union, 1, 3, 4.0 * std::f64::consts::PI), + (BooleanOp::Intersection, 1, 3, 2.0 * std::f64::consts::PI), + (BooleanOp::Subtraction, 2, 6, 2.0 * std::f64::consts::PI), + ] { + let result = boolean_brep(&a, &b, operation, format!("{operation:?}")).unwrap(); + assert_eq!(result.brep.solids.len(), solids); + assert_eq!(result.brep.topology.faces.len(), faces); + let measured = volume(&result.brep).abs(); + assert!( + (measured - expected_volume).abs() < 0.2, + "{operation:?}: measured {measured}, expected {expected_volume}" + ); + assert_eq!(result.report.face_mappings.len(), 6); + if operation == BooleanOp::Subtraction { + let cut_caps: Vec<_> = result + .brep + .topology + .faces + .iter() + .filter(|face| face.provenance.role == FaceRole::Cut) + .collect(); + assert_eq!(cut_caps.len(), 2); + assert!(cut_caps.iter().all(|face| { + face.provenance.reversed + && face.provenance.sources.len() == 1 + && face.provenance.sources[0].entity == "b" + })); + } + } +} + +#[test] +fn coaxial_cylinder_contact_is_regularized_and_near_gap_is_unresolved() { + let a = cylinder("a", 0.0, 4.0, false); + let touching = cylinder("b", 4.0, 2.0, false); + let union = boolean_brep(&a, &touching, BooleanOp::Union, "union".into()).unwrap(); + assert_eq!(union.brep.solids.len(), 1); + let union_volume = volume(&union.brep).abs(); + assert!( + (union_volume - 6.0 * std::f64::consts::PI).abs() < 0.3, + "measured union volume {union_volume}" + ); + let intersection = boolean_brep( + &a, + &touching, + BooleanOp::Intersection, + "intersection".into(), + ) + .unwrap(); + assert!(intersection.brep.solids.is_empty()); + let subtraction = + boolean_brep(&a, &touching, BooleanOp::Subtraction, "subtraction".into()).unwrap(); + assert_eq!(subtraction.brep.solids.len(), 1); + let subtraction_volume = volume(&subtraction.brep).abs(); + assert!( + (subtraction_volume - 4.0 * std::f64::consts::PI).abs() < 0.2, + "measured subtraction volume {subtraction_volume}" + ); + + let near = cylinder("near", 4.0 + 2e-9, 2.0, false); + assert!(matches!( + boolean_brep(&a, &near, BooleanOp::Union, "near".into()), + Err(GeometryError::UnresolvedIntersection(_)) + )); +} + +#[test] +fn positive_similarity_roundoff_does_not_reject_full_cylinder_operands() { + let rotation = std::f64::consts::PI / 7.0; + let source_frame = Frame3 { + origin: [0.0, 1.2, 0.0], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + }; + let source = primitives::cylinder( + "c".into(), + source_frame, + 1.0, + 2.0, + cylinder("accuracy", 0.0, 1.0, false).accuracy, + ) + .unwrap(); + let placed = placed( + &source, + Frame3 { + origin: [0.2, 0.1, 0.2], + x: [rotation.cos(), 0.0, -rotation.sin()], + y: [0.0, 1.0, 0.0], + z: [rotation.sin(), 0.0, rotation.cos()], + }, + 1.25, + ) + .unwrap(); + assert!(full_cylinder(&placed).is_ok()); +} + +#[test] +fn translated_y_axis_cylinder_can_split_into_two_valid_solids() { + let frame = Frame3 { + origin: [0.0; 3], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + }; + let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; + let host = primitives::cylinder("host".into(), frame, 1.0, 2.0, accuracy).unwrap(); + let cutter = primitives::cylinder("cutter".into(), frame, 1.0, 0.8, accuracy).unwrap(); + let cutter = placed( + &cutter, + Frame3 { + origin: [0.0, 0.6, 0.0], + ..Frame3::IDENTITY + }, + 1.0, + ) + .unwrap(); + let result = boolean_brep(&host, &cutter, BooleanOp::Subtraction, "result".into()).unwrap(); + assert_eq!(result.brep.solids.len(), 2); + result.brep.validate().unwrap(); + tessellate(&result.brep, 0.01, 2_000_000).unwrap(); +} + +#[test] +fn coextensive_unequal_cylinders_produce_exact_radial_results() { + let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; + let outer = primitives::cylinder("outer".into(), Frame3::IDENTITY, 2.0, 3.0, accuracy).unwrap(); + let inner = primitives::cylinder("inner".into(), Frame3::IDENTITY, 1.0, 3.0, accuracy).unwrap(); + let union = boolean_brep(&outer, &inner, BooleanOp::Union, "union".into()).unwrap(); + let intersection = boolean_brep( + &outer, + &inner, + BooleanOp::Intersection, + "intersection".into(), + ) + .unwrap(); + let subtraction = boolean_brep(&outer, &inner, BooleanOp::Subtraction, "tube".into()).unwrap(); + assert_eq!(union.brep.topology.faces.len(), 3); + assert_eq!(intersection.brep.topology.faces.len(), 3); + assert_eq!(subtraction.brep.topology.faces.len(), 4); + assert_eq!(subtraction.brep.topology.faces[2].trim.holes.len(), 1); + assert_eq!(subtraction.brep.topology.faces[3].trim.holes.len(), 1); + assert_eq!( + subtraction.brep.topology.faces[1].provenance.role, + FaceRole::Cut + ); + assert!(subtraction.brep.topology.faces[1].provenance.reversed); + assert_eq!(subtraction.report.face_mappings.len(), 6); + let measured = volume(&subtraction.brep).abs(); + let expected = std::f64::consts::PI * (4.0 - 1.0) * 3.0; + assert!( + (measured - expected).abs() < 0.2, + "{measured} != {expected}" + ); + subtraction.brep.validate().unwrap(); + tessellate(&subtraction.brep, 0.01, 2_000_000).unwrap(); + + let empty = boolean_brep(&inner, &outer, BooleanOp::Subtraction, "empty".into()).unwrap(); + assert!(empty.brep.solids.is_empty()); + let shifted = primitives::cylinder( + "shifted".into(), + Frame3 { + origin: [0.0, 0.0, 0.5], + ..Frame3::IDENTITY + }, + 1.0, + 3.0, + accuracy, + ) + .unwrap(); + assert!(matches!( + boolean_brep(&outer, &shifted, BooleanOp::Subtraction, "gap".into()), + Err(GeometryError::CoverageGap { .. }) + )); +} + +#[test] +fn enclosed_unequal_cylinder_becomes_an_oriented_cavity_shell() { + let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; + let host = primitives::cylinder("host".into(), Frame3::IDENTITY, 2.0, 4.0, accuracy).unwrap(); + let cutter = primitives::cylinder( + "cutter".into(), + Frame3 { + origin: [0.0, 0.0, 1.0], + ..Frame3::IDENTITY + }, + 0.75, + 2.0, + accuracy, + ) + .unwrap(); + let subtraction = + boolean_brep(&host, &cutter, BooleanOp::Subtraction, "cavity".into()).unwrap(); + assert_eq!(subtraction.brep.solids.len(), 1); + assert_eq!(subtraction.brep.topology.shells.len(), 2); + assert_eq!(subtraction.brep.solids[0].cavity_shells, vec![1]); + assert_eq!(subtraction.brep.topology.faces.len(), 6); + assert!(subtraction.brep.topology.faces[3..] + .iter() + .all(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed)); + let measured = volume(&subtraction.brep).abs(); + let expected = std::f64::consts::PI * (2.0_f64.powi(2) * 4.0 - 0.75_f64.powi(2) * 2.0); + assert!( + (measured - expected).abs() < 0.3, + "{measured} != {expected}" + ); + subtraction.brep.validate().unwrap(); + tessellate(&subtraction.brep, 0.01, 2_000_000).unwrap(); + + let union = boolean_brep(&host, &cutter, BooleanOp::Union, "union".into()).unwrap(); + let intersection = boolean_brep( + &host, + &cutter, + BooleanOp::Intersection, + "intersection".into(), + ) + .unwrap(); + assert_eq!(union.brep.topology.faces.len(), 3); + assert_eq!(intersection.brep.topology.faces.len(), 3); + let empty = boolean_brep(&cutter, &host, BooleanOp::Subtraction, "empty".into()).unwrap(); + assert!(empty.brep.solids.is_empty()); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/cylinders_noncoaxial.rs b/main/opengeometry/src/operations/modifying/boolean/tests/cylinders_noncoaxial.rs new file mode 100644 index 0000000..816db2d --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/cylinders_noncoaxial.rs @@ -0,0 +1,386 @@ +use super::support::{cylinder, volume}; +use crate::brep::{Accuracy, FaceRole, Frame3, GeometryError, SurfaceGeometry}; +use crate::exchange::export_step; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::query::{classify_point, PointClassification}; +use crate::tessellation::tessellate; + +#[test] +fn nonparallel_cylinder_through_cut_keeps_numerical_ssi_authoritative() { + let accuracy = Accuracy { + geometric: 4e-8, + intersection: 1e-8, + tessellation: 0.01, + exchange: 1e-5, + }; + let host = primitives::cylinder( + "host".into(), + Frame3 { + origin: [0.0, 0.0, -2.0], + ..Frame3::IDENTITY + }, + 1.0, + 4.0, + accuracy, + ) + .unwrap(); + let cutter = primitives::cylinder( + "cutter".into(), + Frame3::from_axis([-2.0, 0.0, 0.2], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0]).unwrap(), + 0.6, + 4.0, + accuracy, + ) + .unwrap(); + let result = + boolean_brep(&host, &cutter, BooleanOp::Subtraction, "through-cut".into()).unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.topology.faces.len(), 4); + assert_eq!(result.brep.topology.faces[0].trim.holes.len(), 2); + assert_eq!(result.brep.topology.faces[3].provenance.role, FaceRole::Cut); + assert!(result.brep.topology.faces[3].provenance.reversed); + assert_eq!(result.brep.geometry.intersections.len(), 2); + assert_eq!(result.report.face_mappings.len(), 6); + assert_eq!( + classify_point(&result.brep, [0.0, 0.0, 0.2]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [0.0, 0.9, 0.2]).unwrap(), + PointClassification::Inside + ); + let mesh = tessellate(&result.brep, 0.0075, 2_000_000).unwrap(); + assert!( + mesh.triangle_face_ids.len() < 50_000, + "cross-drill tessellation produced {} triangles", + mesh.triangle_face_ids.len() + ); +} + +#[test] +fn transverse_parallel_cylinders_build_two_arc_caps_and_shared_edges() { + let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; + let a = primitives::cylinder("a".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy).unwrap(); + let b = primitives::cylinder( + "b".into(), + Frame3 { + origin: [1.0, 0.0, 0.0], + ..Frame3::IDENTITY + }, + 1.0, + 2.0, + accuracy, + ) + .unwrap(); + let union = boolean_brep(&a, &b, BooleanOp::Union, "union".into()).unwrap(); + let intersection = + boolean_brep(&a, &b, BooleanOp::Intersection, "intersection".into()).unwrap(); + let subtraction = boolean_brep(&a, &b, BooleanOp::Subtraction, "subtraction".into()).unwrap(); + for (name, result) in [ + ("union", &union), + ("intersection", &intersection), + ("subtraction", &subtraction), + ] { + assert_eq!(result.brep.solids.len(), 1, "{name}"); + assert_eq!(result.brep.topology.faces.len(), 4, "{name}"); + for cap in 2..4 { + assert_eq!( + result + .brep + .topology + .halfedges + .iter() + .filter(|halfedge| halfedge.face == Some(cap)) + .count(), + 2, + "{name} cap {cap}" + ); + } + result.brep.validate().unwrap(); + assert!(!tessellate(&result.brep, 0.01, 2_000_000) + .unwrap_or_else(|error| panic!("{name}: {error}")) + .indices + .is_empty()); + export_step(&result.brep, "metre").unwrap_or_else(|error| panic!("{name} STEP: {error}")); + } + assert_eq!(union.report.face_mappings.len(), 6); + assert_eq!(intersection.report.face_mappings.len(), 6); + assert_eq!(subtraction.report.face_mappings.len(), 6); + assert_eq!( + subtraction.brep.topology.faces[1].provenance.role, + FaceRole::Cut + ); + assert!(subtraction.brep.topology.faces[1].provenance.reversed); + + let cylinder_volume = 2.0 * std::f64::consts::PI; + let intersection_volume = volume(&intersection.brep).abs(); + assert!((volume(&union.brep).abs() + intersection_volume - 2.0 * cylinder_volume).abs() < 0.2); + assert!((volume(&subtraction.brep).abs() + intersection_volume - cylinder_volume).abs() < 0.2); + + let reversed = primitives::cylinder( + "reversed".into(), + Frame3 { + origin: [1.0, 0.0, 2.0], + x: [1.0, 0.0, 0.0], + y: [0.0, -1.0, 0.0], + z: [0.0, 0.0, -1.0], + }, + 1.0, + 2.0, + accuracy, + ) + .unwrap(); + let reversed_result = boolean_brep( + &a, + &reversed, + BooleanOp::Intersection, + "reversed-result".into(), + ) + .unwrap(); + reversed_result.brep.validate().unwrap(); + assert!((volume(&reversed_result.brep).abs() - intersection_volume).abs() < 0.1); + + let shifted = primitives::cylinder( + "shifted".into(), + Frame3 { + origin: [1.0, 0.0, 0.25], + ..Frame3::IDENTITY + }, + 1.0, + 2.0, + accuracy, + ) + .unwrap(); + let shifted_union = boolean_brep(&a, &shifted, BooleanOp::Union, "gap".into()); + assert!( + matches!(shifted_union, Err(GeometryError::UnresolvedIntersection(_))), + "unexpected shifted-cylinder result: {:?}", + shifted_union.err() + ); + let tangent = primitives::cylinder( + "tangent".into(), + Frame3 { + origin: [2.0, 0.0, 0.0], + ..Frame3::IDENTITY + }, + 1.0, + 2.0, + accuracy, + ) + .unwrap(); + for (operation, solids) in [ + (BooleanOp::Union, 2), + (BooleanOp::Intersection, 0), + (BooleanOp::Subtraction, 1), + ] { + let contact = + boolean_brep(&a, &tangent, operation, format!("contact-{operation:?}")).unwrap(); + assert_eq!(contact.brep.solids.len(), solids); + assert_eq!(contact.report.contacts.len(), 2); + } + let near_tangent = primitives::cylinder( + "near-tangent".into(), + Frame3 { + origin: [2.0 + 2e-9, 0.0, 0.0], + ..Frame3::IDENTITY + }, + 1.0, + 2.0, + accuracy, + ) + .unwrap(); + assert!(matches!( + boolean_brep( + &a, + &near_tangent, + BooleanOp::Union, + "near-tangent-result".into() + ), + Err(GeometryError::UnresolvedIntersection(_)) + )); + let diagonal = primitives::cylinder( + "diagonal".into(), + Frame3 { + origin: [1.5, 1.5, 0.0], + ..Frame3::IDENTITY + }, + 1.0, + 2.0, + accuracy, + ) + .unwrap(); + let diagonal_union = + boolean_brep(&a, &diagonal, BooleanOp::Union, "diagonal-union".into()).unwrap(); + assert_eq!(diagonal_union.brep.solids.len(), 2); + assert!(diagonal_union.report.contacts.is_empty()); +} + +#[test] +fn contained_parallel_cylinder_builds_an_eccentric_analytic_through_hole() { + let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; + let outer = primitives::cylinder("outer".into(), Frame3::IDENTITY, 2.0, 2.0, accuracy).unwrap(); + let inner = primitives::cylinder( + "inner".into(), + Frame3 { + origin: [0.75, 0.0, 0.0], + ..Frame3::IDENTITY + }, + 0.5, + 2.0, + accuracy, + ) + .unwrap(); + let union = boolean_brep(&outer, &inner, BooleanOp::Union, "union".into()).unwrap(); + let intersection = boolean_brep( + &outer, + &inner, + BooleanOp::Intersection, + "intersection".into(), + ) + .unwrap(); + let subtraction = + boolean_brep(&outer, &inner, BooleanOp::Subtraction, "subtraction".into()).unwrap(); + assert_eq!(union.brep.topology.faces.len(), 3); + assert_eq!(intersection.brep.topology.faces.len(), 3); + assert_eq!(subtraction.brep.topology.faces.len(), 4); + assert_eq!(subtraction.brep.topology.faces[2].trim.holes.len(), 1); + assert_eq!(subtraction.brep.topology.faces[3].trim.holes.len(), 1); + assert_eq!( + subtraction.brep.topology.faces[1].provenance.role, + FaceRole::Cut + ); + assert!(subtraction.brep.topology.faces[1].provenance.reversed); + match subtraction.brep.geometry.surfaces[1] { + SurfaceGeometry::Cylinder { frame, radius } => { + assert_eq!(frame.origin, [0.75, 0.0, 0.0]); + assert_eq!(radius, 0.5); + } + _ => panic!("inner cut face lost its cylinder support"), + } + subtraction.brep.validate().unwrap(); + tessellate(&subtraction.brep, 0.01, 2_000_000).unwrap(); + export_step(&subtraction.brep, "metre").unwrap(); + let expected = std::f64::consts::PI * (4.0 - 0.25) * 2.0; + assert!((volume(&subtraction.brep).abs() - expected).abs() < 0.25); + + let touching = primitives::cylinder( + "touching".into(), + Frame3 { + origin: [1.5, 0.0, 0.0], + ..Frame3::IDENTITY + }, + 0.5, + 2.0, + accuracy, + ) + .unwrap(); + assert!(matches!( + boolean_brep( + &outer, + &touching, + BooleanOp::Subtraction, + "touching-result".into() + ), + Err(GeometryError::UnresolvedIntersection(_)) + )); +} + +#[test] +fn noncoextensive_parallel_cylinders_intersect_and_cut_when_the_span_is_bounded() { + let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; + let host = primitives::cylinder("host".into(), Frame3::IDENTITY, 1.0, 3.0, accuracy).unwrap(); + let short = primitives::cylinder( + "short".into(), + Frame3 { + origin: [1.0, 0.0, 1.0], + ..Frame3::IDENTITY + }, + 1.0, + 1.0, + accuracy, + ) + .unwrap(); + let intersection = boolean_brep( + &host, + &short, + BooleanOp::Intersection, + "intersection".into(), + ) + .unwrap(); + assert_eq!(intersection.brep.topology.faces.len(), 4); + assert_eq!(intersection.brep.solids.len(), 1); + assert!(intersection.brep.topology.faces[2..] + .iter() + .all(|face| face.provenance.sources.len() == 1 + && face.provenance.sources[0].entity == "short")); + intersection.brep.validate().unwrap(); + tessellate(&intersection.brep, 0.01, 2_000_000).unwrap(); + let lens_area = 2.0 * std::f64::consts::FRAC_PI_3 - 3.0_f64.sqrt() * 0.5; + assert!((volume(&intersection.brep).abs() - lens_area).abs() < 0.12); + let union = boolean_brep(&host, &short, BooleanOp::Union, "partial-union".into()).unwrap(); + let reversed_union = boolean_brep( + &short, + &host, + BooleanOp::Union, + "reversed-partial-union".into(), + ) + .unwrap(); + let subtraction = boolean_brep( + &host, + &short, + BooleanOp::Subtraction, + "partial-subtraction".into(), + ) + .unwrap(); + for (name, result) in [("union", &union), ("subtraction", &subtraction)] { + assert_eq!(result.brep.topology.faces.len(), 10, "{name}"); + assert_eq!(result.brep.solids.len(), 1, "{name}"); + result.brep.validate().unwrap(); + tessellate(&result.brep, 0.01, 2_000_000).unwrap_or_else(|error| panic!("{name}: {error}")); + export_step(&result.brep, "metre").unwrap_or_else(|error| panic!("{name} STEP: {error}")); + } + assert_eq!( + subtraction.brep.topology.faces[3].provenance.role, + FaceRole::Cut + ); + assert!(subtraction.brep.topology.faces[3].provenance.reversed); + assert!(subtraction.brep.topology.faces[7..9] + .iter() + .all(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed)); + let host_volume = 3.0 * std::f64::consts::PI; + assert!( + (volume(&union.brep).abs() - (host_volume + std::f64::consts::PI - lens_area)).abs() < 0.2 + ); + assert!((volume(&subtraction.brep).abs() - (host_volume - lens_area)).abs() < 0.2); + assert!((volume(&reversed_union.brep).abs() - volume(&union.brep).abs()).abs() < 0.1); + reversed_union.brep.validate().unwrap(); + + let through = primitives::cylinder( + "through".into(), + Frame3 { + origin: [1.0, 0.0, -1.0], + ..Frame3::IDENTITY + }, + 1.0, + 5.0, + accuracy, + ) + .unwrap(); + let cut = boolean_brep( + &host, + &through, + BooleanOp::Subtraction, + "through-cut".into(), + ) + .unwrap(); + assert_eq!(cut.brep.topology.faces.len(), 4); + assert_eq!(cut.brep.topology.faces[1].provenance.role, FaceRole::Cut); + assert!(cut.brep.topology.faces[1].provenance.reversed); + assert!(!cut.report.coincident); + cut.brep.validate().unwrap(); + tessellate(&cut.brep, 0.01, 2_000_000).unwrap(); + let expected_cut_volume = (std::f64::consts::PI - lens_area) * 3.0; + assert!((volume(&cut.brep).abs() - expected_cut_volume).abs() < 0.2); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/generic.rs b/main/opengeometry/src/operations/modifying/boolean/tests/generic.rs new file mode 100644 index 0000000..c2b761e --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/generic.rs @@ -0,0 +1,205 @@ +use super::support::{cylinder, sphere}; +use crate::brep::{Accuracy, CurveGeometry, FaceRole, Frame3}; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::tessellation::tessellate; + +#[test] +fn provably_disjoint_surface_families_use_exact_regularized_results() { + let cylinder_accuracy = sphere("reference", [0.0; 3], 1.0).accuracy; + let torus_accuracy = Accuracy { + geometric: cylinder_accuracy.geometric * 2.0, + intersection: cylinder_accuracy.intersection * 2.0, + tessellation: cylinder_accuracy.tessellation * 2.0, + exchange: cylinder_accuracy.exchange * 2.0, + }; + let cylinder = primitives::cylinder( + "cylinder".into(), + Frame3::IDENTITY, + 1.0, + 2.0, + cylinder_accuracy, + ) + .unwrap(); + let torus = primitives::torus( + "torus".into(), + Frame3 { + origin: [12.0, 0.0, 0.0], + ..Frame3::IDENTITY + }, + 3.0, + 1.0, + torus_accuracy, + ) + .unwrap(); + + let union = boolean_brep(&cylinder, &torus, BooleanOp::Union, "union".into()).unwrap(); + assert_eq!(union.brep.solids.len(), 2); + assert_eq!(union.brep.accuracy.geometric, torus_accuracy.geometric); + assert_eq!(union.brep.topology.faces.len(), 4); + assert_eq!(union.report.face_mappings.len(), 4); + assert!(union + .brep + .topology + .faces + .iter() + .all(|face| face.provenance.role == FaceRole::Preserved)); + + let intersection = boolean_brep( + &cylinder, + &torus, + BooleanOp::Intersection, + "intersection".into(), + ) + .unwrap(); + assert!(intersection.brep.solids.is_empty()); + assert!(intersection + .report + .face_mappings + .iter() + .all(|mapping| mapping.result_faces.is_empty())); + + let subtraction = boolean_brep( + &cylinder, + &torus, + BooleanOp::Subtraction, + "subtraction".into(), + ) + .unwrap(); + assert_eq!(subtraction.brep.solids.len(), 1); + assert_eq!(subtraction.brep.topology.faces.len(), 3); + assert_eq!( + subtraction.brep.topology.faces[0].provenance.sources[0].entity, + "cylinder" + ); +} + +#[test] +fn generic_closed_loop_imprints_an_off_axis_sphere_cone_intersection() { + let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; + let cone = primitives::cone("cone".into(), Frame3::IDENTITY, 2.0, 2.0, accuracy).unwrap(); + let sphere = primitives::sphere( + "sphere".into(), + Frame3 { + origin: [1.42, 0.0, 1.0], + ..Frame3::IDENTITY + }, + 0.35, + accuracy, + ) + .unwrap(); + for operation in [ + BooleanOp::Union, + BooleanOp::Intersection, + BooleanOp::Subtraction, + ] { + let result = boolean_brep( + &cone, + &sphere, + operation, + format!("sphere-cone-{operation:?}"), + ) + .unwrap_or_else(|error| panic!("{operation:?}: {error}")); + assert_eq!(result.brep.solids.len(), 1); + assert!(result + .brep + .geometry + .curves + .iter() + .any(|curve| matches!(curve, CurveGeometry::Intersection { .. }))); + assert!(result + .brep + .topology + .faces + .iter() + .all(|face| !face.provenance.sources.is_empty())); + result.brep.validate().unwrap(); + tessellate(&result.brep, 0.01, 2_000_000) + .unwrap_or_else(|error| panic!("{operation:?} tessellation: {error}")); + } +} + +#[test] +fn generic_closed_loop_handles_periodic_winding() { + let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; + let cone = primitives::cone("cone".into(), Frame3::IDENTITY, 2.0, 2.0, accuracy).unwrap(); + let sphere = primitives::sphere( + "sphere".into(), + Frame3 { + origin: [1.1, 0.0, 0.8], + ..Frame3::IDENTITY + }, + 0.35, + accuracy, + ) + .unwrap(); + let result = boolean_brep(&cone, &sphere, BooleanOp::Intersection, "winding".into()).unwrap(); + result.brep.validate().unwrap(); + tessellate(&result.brep, 0.01, 2_000_000).unwrap(); +} + +#[test] +fn identical_general_analytic_body_uses_coincident_ownership() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let host = + primitives::annular_cylinder("host".into(), Frame3::IDENTITY, 0.4, 2.0, 3.0, accuracy) + .unwrap(); + let union = boolean_brep(&host, &host, BooleanOp::Union, "same".into()).unwrap(); + assert!(union.report.coincident); + assert_eq!(union.brep.topology.faces.len(), host.topology.faces.len()); + assert!(union + .brep + .topology + .faces + .iter() + .all(|face| face.provenance.role == FaceRole::Coincident)); + let empty = boolean_brep(&host, &host, BooleanOp::Subtraction, "empty".into()).unwrap(); + assert!(empty.brep.solids.is_empty()); +} + +#[test] +fn generic_pipeline_classifies_noncanonical_planar_solid_containment() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let prism = primitives::linear_extrusion( + "prism".into(), + Frame3 { + origin: [0.0, 0.0, 0.8], + ..Frame3::IDENTITY + }, + vec![[-0.2, -0.2], [0.2, -0.2], [0.0, 0.2]], + Vec::new(), + 0.4, + accuracy, + ) + .unwrap(); + let cylinder = + primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 2.0, 2.0, accuracy).unwrap(); + let union = boolean_brep(&cylinder, &prism, BooleanOp::Union, "union".into()).unwrap(); + assert_eq!( + union.brep.topology.faces.len(), + cylinder.topology.faces.len() + ); + let intersection = boolean_brep( + &cylinder, + &prism, + BooleanOp::Intersection, + "intersection".into(), + ) + .unwrap(); + assert_eq!( + intersection.brep.topology.faces.len(), + prism.topology.faces.len() + ); + let subtraction = boolean_brep( + &cylinder, + &prism, + BooleanOp::Subtraction, + "subtraction".into(), + ) + .unwrap(); + assert_eq!(subtraction.brep.solids.len(), 1); + assert_eq!(subtraction.brep.solids[0].cavity_shells.len(), 1); + subtraction.brep.validate().unwrap(); + tessellate(&subtraction.brep, 0.01, 2_000_000).unwrap(); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/mod.rs b/main/opengeometry/src/operations/modifying/boolean/tests/mod.rs new file mode 100644 index 0000000..10d799e --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/mod.rs @@ -0,0 +1,15 @@ +mod arc_extrusions_openings; +mod arc_extrusions_splits; +mod conic_torus; +mod cuboids; +mod cuboids_curved_cuts; +mod cylinders_coaxial; +mod cylinders_noncoaxial; +mod generic; +mod planar_batch; +mod planar_extrusions; +mod planar_oblique; +mod shell; +mod sphere_cylinder; +mod spheres; +mod support; diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/planar_batch.rs b/main/opengeometry/src/operations/modifying/boolean/tests/planar_batch.rs new file mode 100644 index 0000000..8eb6376 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/planar_batch.rs @@ -0,0 +1,471 @@ +use super::support::{extrusion, extrusion_span, volume, volume_at_deflection}; +use crate::brep::{FaceRole, Frame3, GeometryError, SurfaceGeometry}; +use crate::exchange::export_step; +use crate::operations::modifying::boolean::batch::subtract_planar_cutters; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::query::{classify_point, face_contains_uv, PointClassification}; +use crate::tessellation::tessellate; + +#[test] +fn planar_extrusion_round_through_cutter_keeps_analytic_brep() { + let host = extrusion( + "round-host", + vec![[0.0, 0.0], [4.0, 0.0], [4.0, 0.3], [0.0, 0.3]], + Vec::new(), + ); + let cutter = primitives::cylinder( + "round-opening".into(), + Frame3 { + origin: [2.0, -0.5, 1.5], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + }, + 0.5, + 1.3, + host.accuracy, + ) + .unwrap(); + let result = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + "round-host-cut".into(), + ) + .unwrap(); + assert_eq!(result.brep.solids.len(), 1); + result.brep.validate().unwrap(); + assert_eq!( + classify_point(&result.brep, [2.0, 0.15, 1.5]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [2.0, 0.15, 2.2]).unwrap(), + PointClassification::Inside + ); + let cut_face = result + .brep + .topology + .faces + .iter() + .find(|face| face.provenance.role == FaceRole::Cut) + .unwrap(); + assert_eq!( + face_contains_uv(&result.brep, cut_face, [0.0, 0.65]).unwrap(), + Some(true) + ); + tessellate(&result.brep, 0.01, 2_000_000).unwrap(); + export_step(&result.brep, "metre").unwrap(); +} + +#[test] +fn rectangular_cut_after_round_cut_never_loses_the_cylindrical_cap() { + let host = extrusion( + "arched-host", + vec![[0.0, 0.0], [4.0, 0.0], [4.0, 0.3], [0.0, 0.3]], + Vec::new(), + ); + let round = primitives::cylinder( + "arched-cap".into(), + Frame3 { + origin: [2.0, -0.5, 2.0], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + }, + 0.5, + 1.3, + host.accuracy, + ) + .unwrap(); + let lower = primitives::linear_extrusion( + "arched-lower".into(), + Frame3 { + origin: [0.0, 0.8, 0.0], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, 1.0], + z: [0.0, -1.0, 0.0], + }, + vec![[1.5, 0.5], [2.5, 0.5], [2.5, 2.0], [1.5, 2.0]], + Vec::new(), + 1.3, + host.accuracy, + ) + .unwrap(); + let batch = subtract_planar_cutters( + &host, + &[round.clone(), lower.clone()], + "arched-batch".into(), + ) + .unwrap(); + batch.brep.validate().unwrap(); + assert_eq!( + classify_point(&batch.brep, [2.0, 0.15, 2.2]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&batch.brep, [2.0, 0.15, 1.0]).unwrap(), + PointClassification::Outside + ); + assert!(batch.brep.topology.faces.iter().any(|face| { + matches!( + batch.brep.geometry.surfaces[face.surface as usize], + SurfaceGeometry::Cylinder { .. } + ) + })); + for source in ["arched-cap", "arched-lower"] { + assert!(batch.report.face_mappings.iter().any(|mapping| { + mapping.source.entity == source && !mapping.result_faces.is_empty() + })); + } + let expected = 4.0 * 0.3 * 3.0 - (1.0 * 1.5 + std::f64::consts::PI * 0.5 * 0.5 / 2.0) * 0.3; + assert!((volume_at_deflection(&batch.brep, 0.0001).abs() - expected).abs() < 1.0e-4); + export_step(&batch.brep, "metre").unwrap(); + let cap = boolean_brep(&host, &round, BooleanOp::Subtraction, "cap".into()).unwrap(); + match boolean_brep(&cap.brep, &lower, BooleanOp::Subtraction, "arch".into()) { + Ok(result) => { + result.brep.validate().unwrap(); + assert!(result.brep.topology.faces.iter().any(|face| { + matches!( + result.brep.geometry.surfaces[face.surface as usize], + SurfaceGeometry::Cylinder { .. } + ) + })); + assert_eq!( + classify_point(&result.brep, [2.0, 0.15, 2.2]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [2.0, 0.15, 1.0]).unwrap(), + PointClassification::Outside + ); + } + Err(GeometryError::CoverageGap { .. }) => {} + Err(error) => panic!("unexpected chained arch failure: {error:?}"), + } +} + +#[test] +fn disjoint_mixed_cutter_batch_preserves_both_openings_in_any_order() { + let host = extrusion( + "mixed-host", + vec![[0.0, 0.0], [6.0, 0.0], [6.0, 0.3], [0.0, 0.3]], + Vec::new(), + ); + let round = primitives::cylinder( + "round".into(), + Frame3 { + origin: [1.5, -0.5, 1.5], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + }, + 0.4, + 1.3, + host.accuracy, + ) + .unwrap(); + let rect = primitives::linear_extrusion( + "rectangle".into(), + Frame3 { + origin: [0.0, 0.8, 0.0], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, 1.0], + z: [0.0, -1.0, 0.0], + }, + vec![[3.5, 0.7], [4.5, 0.7], [4.5, 2.2], [3.5, 2.2]], + Vec::new(), + 1.3, + host.accuracy, + ) + .unwrap(); + for cutters in [[round.clone(), rect.clone()], [rect.clone(), round.clone()]] { + let result = subtract_planar_cutters(&host, &cutters, "mixed".into()).unwrap(); + result.brep.validate().unwrap(); + assert_eq!( + classify_point(&result.brep, [1.5, 0.15, 1.5]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [4.0, 0.15, 1.5]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [2.5, 0.15, 1.5]).unwrap(), + PointClassification::Inside + ); + let expected_volume = + 6.0 * 0.3 * 3.0 - 1.0 * 0.3 * 1.5 - std::f64::consts::PI * 0.4 * 0.4 * 0.3; + let measured_volume = volume_at_deflection(&result.brep, 0.0001).abs(); + assert!( + (measured_volume - expected_volume).abs() < 1.0e-4, + "mixed volume {measured_volume} differs from expected {expected_volume}", + ); + assert!(result.brep.topology.faces.iter().any(|face| { + matches!( + result.brep.geometry.surfaces[face.surface as usize], + SurfaceGeometry::Cylinder { .. } + ) + })); + for source in ["round", "rectangle"] { + assert!( + result.brep.topology.faces.iter().any(|face| { + face.provenance + .sources + .iter() + .any(|origin| origin.entity == source) + }), + "missing source {source}: {:?}", + result + .brep + .topology + .faces + .iter() + .flat_map(|face| face.provenance.sources.iter().map(|origin| &origin.entity)) + .collect::>() + ); + assert!(result.report.face_mappings.iter().any(|mapping| { + mapping.source.entity == source && !mapping.result_faces.is_empty() + })); + } + export_step(&result.brep, "metre").unwrap(); + } +} + +#[test] +fn two_round_cutter_batch_keeps_both_analytic_voids() { + let host = extrusion( + "two-round-host", + vec![[0.0, 0.0], [6.0, 0.0], [6.0, 0.3], [0.0, 0.3]], + Vec::new(), + ); + let make_round = |name: &str, station: f64| { + primitives::cylinder( + name.into(), + Frame3 { + origin: [station, 0.0, 1.5], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + }, + 0.4, + 0.3, + host.accuracy, + ) + .unwrap() + }; + let first = make_round("round-a", 1.5); + let second = make_round("round-b", 4.5); + for cutters in [ + [first.clone(), second.clone()], + [second.clone(), first.clone()], + ] { + let result = subtract_planar_cutters(&host, &cutters, "two-round".into()).unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.solids.len(), 1); + for station in [1.5, 4.5] { + assert_eq!( + classify_point(&result.brep, [station, 0.15, 1.5]).unwrap(), + PointClassification::Outside + ); + } + assert_eq!( + classify_point(&result.brep, [3.0, 0.15, 1.5]).unwrap(), + PointClassification::Inside + ); + let expected = 6.0 * 0.3 * 3.0 - 2.0 * std::f64::consts::PI * 0.4 * 0.4 * 0.3; + assert!((volume_at_deflection(&result.brep, 0.0001).abs() - expected).abs() < 1.0e-4); + for source in ["round-a", "round-b"] { + assert!( + result + .report + .face_mappings + .iter() + .any(|mapping| mapping.source.entity == source + && !mapping.result_faces.is_empty()) + ); + } + let (_, report) = export_step(&result.brep, "metre").unwrap(); + assert_eq!(report.solids, 1); + } +} + +#[test] +fn mixed_profile_batch_preserves_disconnected_full_height_host_parts() { + let host = extrusion( + "split-mixed-host", + vec![[0.0, 0.0], [6.0, 0.0], [6.0, 0.3], [0.0, 0.3]], + Vec::new(), + ); + let round = primitives::cylinder( + "split-round".into(), + Frame3 { + origin: [1.5, -0.5, 1.5], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + }, + 0.4, + 1.3, + host.accuracy, + ) + .unwrap(); + let through = primitives::linear_extrusion( + "full-height".into(), + Frame3 { + origin: [0.0, 0.3, 0.0], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, 1.0], + z: [0.0, -1.0, 0.0], + }, + vec![[2.5, 0.0], [3.5, 0.0], [3.5, 3.0], [2.5, 3.0]], + Vec::new(), + 0.3, + host.accuracy, + ) + .unwrap(); + for cutters in [ + [round.clone(), through.clone()], + [through.clone(), round.clone()], + ] { + let result = subtract_planar_cutters(&host, &cutters, "split-mixed".into()).unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.solids.len(), 2); + for point in [[1.5, 0.15, 1.5], [3.0, 0.15, 1.5]] { + assert_eq!( + classify_point(&result.brep, point).unwrap(), + PointClassification::Outside + ); + } + for point in [[0.5, 0.15, 1.5], [5.0, 0.15, 1.5]] { + assert_eq!( + classify_point(&result.brep, point).unwrap(), + PointClassification::Inside + ); + } + let expected = 6.0 * 0.3 * 3.0 - 1.0 * 0.3 * 3.0 - std::f64::consts::PI * 0.4 * 0.4 * 0.3; + assert!((volume_at_deflection(&result.brep, 0.0001).abs() - expected).abs() < 1.0e-4); + for source in ["split-round", "full-height"] { + assert!( + result + .report + .face_mappings + .iter() + .any(|mapping| mapping.source.entity == source + && !mapping.result_faces.is_empty()) + ); + } + let (_, report) = export_step(&result.brep, "metre").unwrap(); + assert_eq!(report.solids, 2); + } +} + +#[test] +fn flush_mixed_cutters_cross_a_prior_planar_face_split_without_losing_the_round_hole() { + let host = extrusion( + "flush-mixed-host", + vec![[0.0, 0.0], [6.0, 0.0], [6.0, 0.3], [0.0, 0.3]], + Vec::new(), + ); + let round = primitives::cylinder( + "flush-round".into(), + Frame3 { + origin: [1.5, 0.0, 1.5], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + }, + 0.4, + 0.3, + host.accuracy, + ) + .unwrap(); + let rectangle = primitives::linear_extrusion( + "flush-rectangle".into(), + Frame3 { + origin: [0.0, 0.3, 0.0], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, 1.0], + z: [0.0, -1.0, 0.0], + }, + vec![[3.5, 0.7], [4.5, 0.7], [4.5, 1.5], [3.5, 1.5]], + Vec::new(), + 0.3, + host.accuracy, + ) + .unwrap(); + for cutters in [ + [round.clone(), rectangle.clone()], + [rectangle.clone(), round.clone()], + ] { + let result = subtract_planar_cutters(&host, &cutters, "flush-mixed".into()).unwrap(); + result.brep.validate().unwrap(); + assert_eq!( + classify_point(&result.brep, [1.5, 0.15, 1.5]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [4.0, 0.15, 1.4]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [2.5, 0.15, 1.5]).unwrap(), + PointClassification::Inside + ); + assert!(result.brep.topology.faces.iter().any(|face| { + matches!( + result.brep.geometry.surfaces[face.surface as usize], + SurfaceGeometry::Cylinder { .. } + ) + })); + for source in ["flush-round", "flush-rectangle"] { + assert!(result.report.face_mappings.iter().any(|mapping| { + mapping.source.entity == source && !mapping.result_faces.is_empty() + })); + } + export_step(&result.brep, "metre").unwrap(); + } +} + +#[test] +fn ten_flush_planar_cutters_use_one_rectilinear_arrangement() { + let host = extrusion( + "host", + vec![[0.0, 0.0], [20.0, 0.0], [20.0, 0.3], [0.0, 0.3]], + Vec::new(), + ); + let cutters = (0..10) + .map(|index| { + let left = 1.0 + index as f64 * 1.8; + extrusion_span( + &format!("opening-{index}"), + 0.0, + 2.1, + vec![ + [left, 0.0], + [left + 0.5, 0.0], + [left + 0.5, 0.3], + [left, 0.3], + ], + Vec::new(), + ) + }) + .collect::>(); + let result = subtract_planar_cutters(&host, &cutters, "batch".into()).unwrap(); + result.brep.validate().unwrap(); + assert!((volume(&result.brep).abs() - 14.85).abs() < 1.0e-6); + for index in 0..10 { + let station = 1.25 + index as f64 * 1.8; + assert_eq!( + classify_point(&result.brep, [station, 0.15, 1.0]).unwrap(), + PointClassification::Outside, + ); + } + assert!(result + .report + .face_mappings + .iter() + .any(|mapping| mapping.source.entity == "opening-9")); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/planar_extrusions.rs b/main/opengeometry/src/operations/modifying/boolean/tests/planar_extrusions.rs new file mode 100644 index 0000000..fb35e99 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/planar_extrusions.rs @@ -0,0 +1,398 @@ +use super::support::{extrusion, extrusion_span, volume}; +use crate::brep::{FaceRole, Frame3, GeometryQuality}; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::query::{classify_point, PointClassification}; + +#[test] +fn coextensive_profile_extrusions_use_exact_planar_arrangements() { + let a = extrusion( + "a", + vec![[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]], + Vec::new(), + ); + let b = extrusion( + "b", + vec![[2.0, -1.0], [5.0, -1.0], [5.0, 2.0], [2.0, 2.0]], + Vec::new(), + ); + for (operation, expected_volume) in [ + (BooleanOp::Union, 63.0), + (BooleanOp::Intersection, 12.0), + (BooleanOp::Subtraction, 36.0), + ] { + let result = boolean_brep(&a, &b, operation, format!("{operation:?}")).unwrap(); + result.brep.validate().unwrap(); + assert!(matches!(result.brep.quality, GeometryQuality::Analytic)); + assert!((volume(&result.brep).abs() - expected_volume).abs() < 1.0e-6); + assert!(result + .report + .face_mappings + .iter() + .any(|mapping| !mapping.result_faces.is_empty())); + } +} + +#[test] +fn profile_subtraction_builds_holes_disconnected_solids_and_cut_provenance() { + let host = extrusion( + "host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0]], + Vec::new(), + ); + let inner = extrusion( + "inner", + vec![[4.0, 4.0], [6.0, 4.0], [6.0, 6.0], [4.0, 6.0]], + Vec::new(), + ); + let cavity = boolean_brep(&host, &inner, BooleanOp::Subtraction, "cavity".into()).unwrap(); + assert_eq!(cavity.brep.solids.len(), 1); + assert_eq!(cavity.brep.topology.faces[0].trim.holes.len(), 1); + assert!((volume(&cavity.brep).abs() - 288.0).abs() < 1.0e-6); + assert!(cavity + .brep + .topology + .faces + .iter() + .any(|face| { face.provenance.role == FaceRole::Cut && face.provenance.reversed })); + + let band = extrusion( + "band", + vec![[-1.0, 4.0], [11.0, 4.0], [11.0, 6.0], [-1.0, 6.0]], + Vec::new(), + ); + let split = boolean_brep(&host, &band, BooleanOp::Subtraction, "split".into()).unwrap(); + assert_eq!(split.brep.solids.len(), 2); + assert!((volume(&split.brep).abs() - 240.0).abs() < 1.0e-6); +} + +#[test] +fn matching_profile_extrusions_use_exact_axial_interval_booleans() { + let profile = vec![[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]]; + let a = extrusion_span("a", 0.0, 4.0, profile.clone(), Vec::new()); + let b = extrusion_span( + "b", + 2.0, + 4.0, + vec![[4.0, 4.0], [0.0, 4.0], [0.0, 0.0], [4.0, 0.0]], + Vec::new(), + ); + for (operation, expected_volume) in [ + (BooleanOp::Union, 96.0), + (BooleanOp::Intersection, 32.0), + (BooleanOp::Subtraction, 32.0), + ] { + let result = boolean_brep(&a, &b, operation, format!("axial-{operation:?}")).unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.solids.len(), 1); + assert!((volume(&result.brep).abs() - expected_volume).abs() < 1.0e-6); + assert!(result + .report + .face_mappings + .iter() + .any(|mapping| !mapping.result_faces.is_empty())); + } + + let embedded = extrusion_span("embedded", 1.0, 2.0, profile.clone(), Vec::new()); + let split = boolean_brep(&a, &embedded, BooleanOp::Subtraction, "axial-split".into()).unwrap(); + split.brep.validate().unwrap(); + assert_eq!(split.brep.solids.len(), 2); + assert!((volume(&split.brep).abs() - 32.0).abs() < 1.0e-6); + assert_eq!( + split + .brep + .topology + .faces + .iter() + .filter(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed) + .count(), + 2 + ); + + let disjoint = extrusion_span("disjoint", 6.0, 2.0, profile, Vec::new()); + let union = boolean_brep(&a, &disjoint, BooleanOp::Union, "axial-disjoint".into()).unwrap(); + assert_eq!(union.brep.solids.len(), 2); + assert!((volume(&union.brep).abs() - 96.0).abs() < 1.0e-6); + let intersection = + boolean_brep(&a, &disjoint, BooleanOp::Intersection, "axial-empty".into()).unwrap(); + assert!(intersection.brep.solids.is_empty()); +} + +#[test] +fn different_height_planar_openings_support_chained_flush_cuts() { + let host = extrusion( + "host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.0, 0.3]], + Vec::new(), + ); + let first = extrusion_span( + "lower_cutout", + 0.0, + 2.1, + vec![[2.0, 0.0], [3.0, 0.0], [3.0, 0.3], [2.0, 0.3]], + Vec::new(), + ); + let cut = boolean_brep( + &host, + &first, + BooleanOp::Subtraction, + "one-lower_cutout".into(), + ) + .unwrap(); + cut.brep.validate().unwrap(); + assert!((volume(&cut.brep).abs() - 8.37).abs() < 1.0e-6); + assert_eq!( + classify_point(&cut.brep, [2.5, 0.15, 1.0]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&cut.brep, [2.5, 0.15, 2.5]).unwrap(), + PointClassification::Inside + ); + let second = extrusion_span( + "raised_cutout", + 0.8, + 1.2, + vec![[6.0, 0.0], [7.0, 0.0], [7.0, 0.3], [6.0, 0.3]], + Vec::new(), + ); + let chained = boolean_brep( + &cut.brep, + &second, + BooleanOp::Subtraction, + "lower_cutout-raised_cutout".into(), + ) + .unwrap(); + chained.brep.validate().unwrap(); + assert!((volume(&chained.brep).abs() - 8.01).abs() < 1.0e-6); + assert_eq!( + classify_point(&chained.brep, [6.5, 0.15, 1.2]).unwrap(), + PointClassification::Outside + ); +} + +#[test] +fn chained_mitered_host_openings_keep_top_cap_provenance() { + let host = extrusion_span( + "mitered-host", + 0.0, + 3.8, + vec![ + [-0.16, -0.14], + [0.16, -0.46], + [13.84, -0.46], + [14.16, -0.14], + ], + Vec::new(), + ); + let lower_cutout = extrusion_span( + "lower_cutout", + 0.0, + 2.45, + vec![[1.2, -0.14], [2.8, -0.14], [2.8, -0.46], [1.2, -0.46]], + Vec::new(), + ); + let raised_cutout = extrusion_span( + "raised_cutout", + 0.3, + 2.8, + vec![ + [3.625, -0.14], + [5.175, -0.14], + [5.175, -0.46], + [3.625, -0.46], + ], + Vec::new(), + ); + let first = boolean_brep( + &host, + &lower_cutout, + BooleanOp::Subtraction, + "lower_cutout-cut".into(), + ) + .unwrap(); + let second = boolean_brep( + &first.brep, + &raised_cutout, + BooleanOp::Subtraction, + "raised_cutout-cut".into(), + ) + .unwrap(); + second.brep.validate().unwrap(); + assert_eq!( + classify_point(&second.brep, [2.0, -0.3, 1.0]).unwrap(), + PointClassification::Outside, + ); + assert_eq!( + classify_point(&second.brep, [4.4, -0.3, 1.0]).unwrap(), + PointClassification::Outside, + ); +} + +#[test] +fn angled_host_cap_accepts_flush_lower_cutout_cutter() { + let host = extrusion( + "angled-host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], + Vec::new(), + ); + let cutter = extrusion_span( + "lower_cutout", + 0.0, + 2.1, + vec![[2.0, 0.0], [3.0, 0.0], [3.0, 0.3], [2.0, 0.3]], + Vec::new(), + ); + let result = boolean_brep(&host, &cutter, BooleanOp::Subtraction, "angled-cut".into()).unwrap(); + result.brep.validate().unwrap(); + assert!((volume(&result.brep).abs() - (volume(&host).abs() - 0.63)).abs() < 1.0e-6); + assert_eq!( + classify_point(&result.brep, [2.5, 0.15, 1.0]).unwrap(), + PointClassification::Outside + ); + let raised_cutout = extrusion_span( + "raised_cutout", + 0.8, + 1.2, + vec![[6.0, 0.0], [7.0, 0.0], [7.0, 0.3], [6.0, 0.3]], + Vec::new(), + ); + let chained = boolean_brep( + &result.brep, + &raised_cutout, + BooleanOp::Subtraction, + "angled-chain".into(), + ) + .unwrap(); + chained.brep.validate().unwrap(); + assert!((volume(&chained.brep).abs() - (volume(&host).abs() - 0.99)).abs() < 1.0e-6); + assert_eq!( + classify_point(&chained.brep, [6.5, 0.15, 1.2]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&chained.brep, [6.5, 0.15, 2.5]).unwrap(), + PointClassification::Inside + ); +} + +#[test] +fn full_height_planar_opening_splits_angled_host_into_two_solids() { + let host = extrusion( + "angled-host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], + Vec::new(), + ); + let cutter = extrusion( + "through-lower_cutout", + vec![[4.0, 0.0], [5.0, 0.0], [5.0, 0.3], [4.0, 0.3]], + Vec::new(), + ); + let result = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + "split-angled".into(), + ) + .unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.solids.len(), 2); + assert!((volume(&result.brep).abs() - (volume(&host).abs() - 0.9)).abs() < 1.0e-6); +} + +#[test] +fn overlapping_flush_openings_on_angled_host_remove_the_union_once() { + let host = extrusion( + "angled-host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], + Vec::new(), + ); + let first = extrusion_span( + "first", + 0.0, + 2.1, + vec![[2.0, 0.0], [3.0, 0.0], [3.0, 0.3], [2.0, 0.3]], + Vec::new(), + ); + let second = extrusion_span( + "second", + 0.0, + 2.1, + vec![[2.5, 0.0], [3.5, 0.0], [3.5, 0.3], [2.5, 0.3]], + Vec::new(), + ); + let once = boolean_brep(&host, &first, BooleanOp::Subtraction, "once".into()).unwrap(); + let twice = boolean_brep(&once.brep, &second, BooleanOp::Subtraction, "twice".into()).unwrap(); + twice.brep.validate().unwrap(); + assert!((volume(&twice.brep).abs() - (volume(&host).abs() - 0.945)).abs() < 1.0e-6); +} + +#[test] +fn layered_planar_cut_supports_an_internal_cavity_shell() { + let host = extrusion( + "angled-host", + vec![ + [0.0, 0.0], + [10.0, 0.0], + [10.0, 10.0], + [0.3, 10.0], + [0.0, 9.8], + ], + Vec::new(), + ); + let cutter = extrusion_span( + "cavity", + 1.0, + 1.0, + vec![[4.0, 4.0], [6.0, 4.0], [6.0, 6.0], [4.0, 6.0]], + Vec::new(), + ); + let result = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + "cavity-result".into(), + ) + .unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.solids.len(), 1); + assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); + assert!((volume(&result.brep).abs() - (volume(&host).abs() - 4.0)).abs() < 1.0e-6); +} + +#[test] +fn angled_host_accepts_rotated_planar_cutter_frame() { + let host = extrusion( + "angled-host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], + Vec::new(), + ); + let angle = std::f64::consts::FRAC_PI_6; + let cutter = primitives::linear_extrusion( + "rotated-cutter".into(), + Frame3 { + origin: [2.5, 0.15, 0.0], + x: [angle.cos(), angle.sin(), 0.0], + y: [-angle.sin(), angle.cos(), 0.0], + z: [0.0, 0.0, 1.0], + }, + vec![[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]], + Vec::new(), + 2.1, + host.accuracy, + ) + .unwrap(); + let result = + boolean_brep(&host, &cutter, BooleanOp::Subtraction, "rotated-cut".into()).unwrap(); + result.brep.validate().unwrap(); + assert_eq!( + classify_point(&result.brep, [2.5, 0.15, 1.0]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [2.5, 0.15, 2.5]).unwrap(), + PointClassification::Inside + ); + assert!(volume(&result.brep).abs() < volume(&host).abs()); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/planar_oblique.rs b/main/opengeometry/src/operations/modifying/boolean/tests/planar_oblique.rs new file mode 100644 index 0000000..03b87f1 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/planar_oblique.rs @@ -0,0 +1,420 @@ +use super::support::{extrusion, extrusion_span, volume}; +use crate::brep::{Frame3, GeometryQuality}; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::query::{classify_point, PointClassification}; + +#[test] +fn oblique_planar_cutter_crosses_a_host_without_tessellating_the_boolean() { + let host = extrusion( + "oblique-host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], + Vec::new(), + ); + let angle = std::f64::consts::PI / 12.0; + let cutter = primitives::linear_extrusion( + "oblique-cutter".into(), + Frame3 { + origin: [2.5, 0.15, 0.0], + x: [angle.cos(), 0.0, -angle.sin()], + y: [0.0, 1.0, 0.0], + z: [angle.sin(), 0.0, angle.cos()], + }, + vec![[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]], + Vec::new(), + 2.1, + host.accuracy, + ) + .unwrap(); + let result = + boolean_brep(&host, &cutter, BooleanOp::Subtraction, "oblique-cut".into()).unwrap(); + result.brep.validate().unwrap(); + assert!(matches!(result.report.quality, GeometryQuality::Analytic)); + assert_eq!( + classify_point(&result.brep, [2.8, 0.15, 1.0]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [2.8, 0.15, 2.5]).unwrap(), + PointClassification::Inside + ); + assert!(volume(&result.brep).abs() < volume(&host).abs()); +} + +#[test] +fn chained_oblique_planar_cuts_keep_both_voids_and_analytic_faces() { + let host = extrusion( + "chained-oblique-host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]], + Vec::new(), + ); + let angle = std::f64::consts::PI / 12.0; + let cutter = |name: &str, station: f64, sign: f64| { + primitives::linear_extrusion( + name.into(), + Frame3 { + origin: [station, 0.15, 0.0], + x: [angle.cos(), 0.0, -sign * angle.sin()], + y: [0.0, 1.0, 0.0], + z: [sign * angle.sin(), 0.0, angle.cos()], + }, + vec![[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]], + Vec::new(), + 2.1, + host.accuracy, + ) + .unwrap() + }; + let first = cutter("first-oblique", 2.5, 1.0); + let second = cutter("second-oblique", 5.0, -1.0); + let once = boolean_brep(&host, &first, BooleanOp::Subtraction, "once".into()).unwrap(); + let twice = boolean_brep(&once.brep, &second, BooleanOp::Subtraction, "twice".into()).unwrap(); + twice.brep.validate().unwrap(); + assert!(matches!(twice.report.quality, GeometryQuality::Analytic)); + for point in [[2.8, 0.15, 1.0], [4.7, 0.15, 1.0]] { + assert_eq!( + classify_point(&twice.brep, point).unwrap(), + PointClassification::Outside + ); + } + assert_eq!( + classify_point(&twice.brep, [3.8, 0.15, 1.0]).unwrap(), + PointClassification::Inside + ); + assert!(volume(&twice.brep).abs() < volume(&once.brep).abs()); +} + +#[test] +fn oblique_cut_accepts_a_disconnected_planar_host() { + let host = extrusion( + "split-host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.0, 0.3]], + Vec::new(), + ); + let through = extrusion_span( + "through", + -1.0, + 5.0, + vec![[3.0, -1.0], [4.0, -1.0], [4.0, 1.0], [3.0, 1.0]], + Vec::new(), + ); + let split = boolean_brep(&host, &through, BooleanOp::Subtraction, "split".into()).unwrap(); + assert_eq!(split.brep.solids.len(), 2); + let angle = std::f64::consts::PI / 12.0; + let oblique = primitives::linear_extrusion( + "split-oblique".into(), + Frame3 { + origin: [6.0, 0.15, 0.0], + x: [angle.cos(), 0.0, -angle.sin()], + y: [0.0, 1.0, 0.0], + z: [angle.sin(), 0.0, angle.cos()], + }, + vec![[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]], + Vec::new(), + 2.1, + host.accuracy, + ) + .unwrap(); + let result = boolean_brep( + &split.brep, + &oblique, + BooleanOp::Subtraction, + "split-oblique-result".into(), + ) + .unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.solids.len(), 2); + assert_eq!( + classify_point(&result.brep, [6.3, 0.15, 1.0]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [1.0, 0.15, 1.0]).unwrap(), + PointClassification::Inside + ); +} + +#[test] +fn planar_cutter_tilts_about_both_host_axes_keep_exact_occupancy() { + let host = extrusion( + "tilt-sweep-host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.0, 0.3]], + Vec::new(), + ); + for axis in 0..2 { + for degrees in [-25.0_f64, -15.0, -5.0, 5.0, 15.0, 25.0] { + let angle = degrees.to_radians(); + let frame = if axis == 0 { + Frame3 { + origin: [2.5, 0.15, 0.0], + x: [angle.cos(), 0.0, -angle.sin()], + y: [0.0, 1.0, 0.0], + z: [angle.sin(), 0.0, angle.cos()], + } + } else { + Frame3 { + origin: [2.5, 0.15, 0.0], + x: [1.0, 0.0, 0.0], + y: [0.0, angle.cos(), -angle.sin()], + z: [0.0, angle.sin(), angle.cos()], + } + }; + let cutter = primitives::linear_extrusion( + format!("tilt-{axis}-{degrees}"), + frame, + vec![[-0.8, -0.8], [0.8, -0.8], [0.8, 0.8], [-0.8, 0.8]], + Vec::new(), + 2.1, + host.accuracy, + ) + .unwrap(); + let result = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + format!("tilt-result-{axis}-{degrees}"), + ) + .unwrap(); + result.brep.validate().unwrap(); + assert_eq!( + classify_point(&result.brep, [2.5, 0.15, 1.0]).unwrap(), + PointClassification::Outside, + "axis={axis}, degrees={degrees}" + ); + assert_eq!( + classify_point(&result.brep, [8.0, 0.15, 1.0]).unwrap(), + PointClassification::Inside, + "axis={axis}, degrees={degrees}" + ); + assert!(volume(&result.brep).abs() < volume(&host).abs()); + } + } +} + +#[test] +fn oblique_cut_preserves_a_planar_host_with_profile_hole_loops() { + let host = extrusion( + "profile-hole-host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 3.0], [0.0, 3.0]], + vec![vec![[2.0, 1.0], [3.0, 1.0], [3.0, 2.0], [2.0, 2.0]]], + ); + assert!(host + .topology + .faces + .iter() + .any(|face| !face.trim.holes.is_empty())); + let angle = std::f64::consts::PI / 12.0; + let oblique = primitives::linear_extrusion( + "pocket-oblique".into(), + Frame3 { + origin: [6.0, 1.5, 0.0], + x: [angle.cos(), 0.0, -angle.sin()], + y: [0.0, 1.0, 0.0], + z: [angle.sin(), 0.0, angle.cos()], + }, + vec![[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]], + Vec::new(), + 2.1, + host.accuracy, + ) + .unwrap(); + let result = boolean_brep(&host, &oblique, BooleanOp::Subtraction, "two-voids".into()).unwrap(); + result.brep.validate().unwrap(); + for point in [[2.5, 1.5, 1.5], [6.3, 1.5, 1.0]] { + assert_eq!( + classify_point(&result.brep, point).unwrap(), + PointClassification::Outside + ); + } + for point in [[4.0, 1.5, 1.0], [6.3, 1.5, 2.5]] { + assert_eq!( + classify_point(&result.brep, point).unwrap(), + PointClassification::Inside + ); + } +} + +#[test] +fn oblique_nonconvex_planar_cutter_keeps_exact_host_material() { + let host = extrusion( + "nonconvex-host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.0, 0.3]], + Vec::new(), + ); + let angle = std::f64::consts::PI / 12.0; + let cutter = primitives::linear_extrusion( + "nonconvex-cutter".into(), + Frame3 { + origin: [5.0, 0.15, 0.0], + x: [angle.cos(), 0.0, -angle.sin()], + y: [0.0, 1.0, 0.0], + z: [angle.sin(), 0.0, angle.cos()], + }, + vec![ + [-1.0, -1.0], + [1.0, -1.0], + [1.0, -0.05], + [0.0, -0.05], + [0.0, 1.0], + [-1.0, 1.0], + ], + Vec::new(), + 2.1, + host.accuracy, + ) + .unwrap(); + let result = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + "nonconvex-oblique-cut".into(), + ) + .unwrap(); + result.brep.validate().unwrap(); + assert!(matches!(result.report.quality, GeometryQuality::Analytic)); + assert_eq!( + classify_point(&result.brep, [4.5, 0.15, 1.0]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [5.5, 0.15, 1.0]).unwrap(), + PointClassification::Inside + ); +} + +#[test] +fn oblique_planar_cut_preserves_an_existing_internal_cavity_shell() { + let host = extrusion( + "cavity-host", + vec![[0.0, 0.0], [10.0, 0.0], [10.0, 3.0], [0.0, 3.0]], + Vec::new(), + ); + let cavity = extrusion_span( + "internal-cavity", + 1.0, + 1.0, + vec![[2.0, 1.0], [3.0, 1.0], [3.0, 2.0], [2.0, 2.0]], + Vec::new(), + ); + let host = boolean_brep(&host, &cavity, BooleanOp::Subtraction, "cavity-host".into()) + .unwrap() + .brep; + assert_eq!(host.solids[0].cavity_shells.len(), 1); + let angle = std::f64::consts::PI / 12.0; + let cutter = primitives::linear_extrusion( + "oblique-after-cavity".into(), + Frame3 { + origin: [6.0, 1.5, 0.0], + x: [angle.cos(), 0.0, -angle.sin()], + y: [0.0, 1.0, 0.0], + z: [angle.sin(), 0.0, angle.cos()], + }, + vec![[-0.5, -2.0], [0.5, -2.0], [0.5, 2.0], [-0.5, 2.0]], + Vec::new(), + 2.1, + host.accuracy, + ) + .unwrap(); + let result = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + "oblique-cavity-cut".into(), + ) + .unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.solids.len(), 1); + assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); + assert_eq!( + classify_point(&result.brep, [2.5, 1.5, 1.5]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [6.2, 1.5, 1.0]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [6.2, 1.5, 2.6]).unwrap(), + PointClassification::Inside + ); +} + +#[test] +fn oblique_cut_assigns_a_remote_cavity_to_its_original_material_component() { + let left = extrusion( + "left-cavity-host", + vec![[0.0, 0.0], [4.0, 0.0], [4.0, 3.0], [0.0, 3.0]], + Vec::new(), + ); + let cavity = extrusion_span( + "left-cavity", + 1.0, + 1.0, + vec![[1.0, 1.0], [2.0, 1.0], [2.0, 2.0], [1.0, 2.0]], + Vec::new(), + ); + let left_with_cavity = boolean_brep( + &left, + &cavity, + BooleanOp::Subtraction, + "left-with-cavity".into(), + ) + .unwrap() + .brep; + let right = extrusion( + "right-host", + vec![[6.0, 0.0], [10.0, 0.0], [10.0, 3.0], [6.0, 3.0]], + Vec::new(), + ); + let host = boolean_brep( + &left_with_cavity, + &right, + BooleanOp::Union, + "two-components".into(), + ) + .unwrap() + .brep; + assert_eq!(host.solids.len(), 2); + let angle = std::f64::consts::PI / 12.0; + let cutter = primitives::linear_extrusion( + "right-oblique-cutter".into(), + Frame3 { + origin: [8.5, 1.5, 0.0], + x: [angle.cos(), 0.0, -angle.sin()], + y: [0.0, 1.0, 0.0], + z: [angle.sin(), 0.0, angle.cos()], + }, + vec![[-0.5, -2.0], [0.5, -2.0], [0.5, 2.0], [-0.5, 2.0]], + Vec::new(), + 2.1, + host.accuracy, + ) + .unwrap(); + let result = boolean_brep( + &host, + &cutter, + BooleanOp::Subtraction, + "remote-cavity-cut".into(), + ) + .unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.solids.len(), 2); + assert_eq!( + result + .brep + .solids + .iter() + .map(|solid| solid.cavity_shells.len()) + .sum::(), + 1 + ); + assert_eq!( + classify_point(&result.brep, [1.5, 1.5, 1.5]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [8.5, 1.5, 1.0]).unwrap(), + PointClassification::Outside + ); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/shell.rs b/main/opengeometry/src/operations/modifying/boolean/tests/shell.rs new file mode 100644 index 0000000..254f61c --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/shell.rs @@ -0,0 +1,70 @@ +use super::support::{extrusion, sphere}; +use crate::brep::{FaceRole, Frame3, GeometryError}; +use crate::operations::modifying::boolean::shell::shell_brep; +use crate::primitives; +use crate::tessellation::tessellate; + +#[test] +fn restricted_shell_offsets_supported_closed_families() { + let accuracy = sphere("accuracy", [0.0; 3], 1.0).accuracy; + let inputs = [ + ( + primitives::cuboid("box".into(), Frame3::IDENTITY, [2.0; 3], accuracy).unwrap(), + 12, + ), + ( + primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy).unwrap(), + 6, + ), + ( + primitives::sphere("sphere".into(), Frame3::IDENTITY, 1.0, accuracy).unwrap(), + 2, + ), + ( + primitives::torus("torus".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy).unwrap(), + 2, + ), + ( + primitives::cone("cone".into(), Frame3::IDENTITY, 2.0, 3.0, accuracy).unwrap(), + 4, + ), + ( + primitives::frustum("frustum".into(), Frame3::IDENTITY, 2.0, 1.0, 3.0, accuracy) + .unwrap(), + 6, + ), + ( + extrusion( + "profile-shell", + vec![[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]], + vec![vec![[1.0, 1.0], [1.0, 3.0], [3.0, 3.0], [3.0, 1.0]]], + ), + 20, + ), + ]; + for (input, expected_faces) in &inputs { + let result = shell_brep(input, 0.2, format!("{}-shell", input.id)).unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.solids.len(), 1); + assert_eq!(result.brep.solids[0].cavity_shells.len(), 1); + assert_eq!(result.brep.topology.faces.len(), *expected_faces); + assert_eq!( + result.report.face_mappings.len(), + input.topology.faces.len() + ); + assert!(result.brep.topology.faces.iter().any(|face| { + face.provenance.role == FaceRole::Cut + && face.provenance.reversed + && face + .provenance + .sources + .iter() + .all(|source| source.entity == input.id) + })); + tessellate(&result.brep, 0.02, 2_000_000).unwrap(); + } + assert!(matches!( + shell_brep(&inputs[2].0, 1.0, "invalid".into()), + Err(GeometryError::UnresolvedIntersection(_)) + )); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/sphere_cylinder.rs b/main/opengeometry/src/operations/modifying/boolean/tests/sphere_cylinder.rs new file mode 100644 index 0000000..315cd31 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/sphere_cylinder.rs @@ -0,0 +1,253 @@ +use super::support::{cylinder, volume}; +use crate::brep::{CurveGeometry, FaceRole, Frame3, GeometryError, GeometryQuality}; +use crate::exchange::export_step; +use crate::operations::modifying::boolean::dispatch::boolean_brep; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::tessellation::tessellate; + +#[test] +fn mixed_sphere_cylinder_containment_preserves_exact_cavity_supports() { + let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; + let cylinder_host = + primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 2.0, 4.0, accuracy).unwrap(); + let sphere_cutter = primitives::sphere( + "sphere".into(), + Frame3 { + origin: [0.0, 0.0, 2.0], + ..Frame3::IDENTITY + }, + 0.5, + accuracy, + ) + .unwrap(); + let cylindrical_cavity = boolean_brep( + &cylinder_host, + &sphere_cutter, + BooleanOp::Subtraction, + "cylindrical-cavity".into(), + ) + .unwrap(); + assert_eq!(cylindrical_cavity.brep.solids.len(), 1); + assert_eq!(cylindrical_cavity.brep.topology.shells.len(), 2); + assert_eq!(cylindrical_cavity.brep.solids[0].cavity_shells, vec![1]); + assert_eq!(cylindrical_cavity.brep.topology.faces.len(), 4); + assert_eq!( + cylindrical_cavity.brep.topology.faces[3].provenance.role, + FaceRole::Cut + ); + assert!( + cylindrical_cavity.brep.topology.faces[3] + .provenance + .reversed + ); + cylindrical_cavity.brep.validate().unwrap(); + tessellate(&cylindrical_cavity.brep, 0.01, 2_000_000).unwrap(); + + let sphere_host = + primitives::sphere("sphere-host".into(), Frame3::IDENTITY, 3.0, accuracy).unwrap(); + let cylinder_cutter = primitives::cylinder( + "cylinder-cutter".into(), + Frame3 { + origin: [0.0, 0.0, -0.5], + ..Frame3::IDENTITY + }, + 0.5, + 1.0, + accuracy, + ) + .unwrap(); + let spherical_cavity = boolean_brep( + &sphere_host, + &cylinder_cutter, + BooleanOp::Subtraction, + "spherical-cavity".into(), + ) + .unwrap(); + assert_eq!(spherical_cavity.brep.topology.faces.len(), 4); + assert_eq!(spherical_cavity.brep.solids[0].cavity_shells, vec![1]); + assert!(spherical_cavity.brep.topology.faces[1..] + .iter() + .all(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed)); + spherical_cavity.brep.validate().unwrap(); + tessellate(&spherical_cavity.brep, 0.01, 2_000_000).unwrap(); + + let union = boolean_brep( + &cylinder_host, + &sphere_cutter, + BooleanOp::Union, + "union".into(), + ) + .unwrap(); + let intersection = boolean_brep( + &cylinder_host, + &sphere_cutter, + BooleanOp::Intersection, + "intersection".into(), + ) + .unwrap(); + assert_eq!(union.brep.topology.faces.len(), 3); + assert_eq!(intersection.brep.topology.faces.len(), 1); + let crossing = primitives::sphere( + "crossing".into(), + Frame3 { + origin: [1.8, 0.0, 2.0], + ..Frame3::IDENTITY + }, + 0.5, + accuracy, + ) + .unwrap(); + for (name, left, right, operation) in [ + ( + "crossing-union", + &cylinder_host, + &crossing, + BooleanOp::Union, + ), + ( + "crossing-intersection", + &cylinder_host, + &crossing, + BooleanOp::Intersection, + ), + ( + "crossing-cylinder-cut", + &cylinder_host, + &crossing, + BooleanOp::Subtraction, + ), + ( + "crossing-sphere-cut", + &crossing, + &cylinder_host, + BooleanOp::Subtraction, + ), + ] { + let result = boolean_brep(left, right, operation, name.into()) + .unwrap_or_else(|error| panic!("{name}: {error}")); + assert!( + matches!(result.report.quality, GeometryQuality::Analytic), + "{name}" + ); + assert_eq!(result.brep.solids.len(), 1, "{name}"); + assert!(result.brep.topology.faces.len() >= 2, "{name}"); + assert!(!result.brep.topology.edges.is_empty(), "{name}"); + assert!( + result + .brep + .topology + .faces + .iter() + .all(|face| !face.provenance.sources.is_empty()), + "{name}" + ); + assert!(result + .brep + .geometry + .curves + .iter() + .any(|curve| matches!(curve, CurveGeometry::Intersection { .. }))); + result.brep.validate().unwrap(); + tessellate(&result.brep, 0.01, 2_000_000) + .unwrap_or_else(|error| panic!("{name} tessellation: {error}")); + } +} + +#[test] +fn coaxial_sphere_cylinder_intersection_builds_shared_analytic_circles() { + let accuracy = cylinder("accuracy", 0.0, 1.0, false).accuracy; + let sphere = primitives::sphere("sphere".into(), Frame3::IDENTITY, 1.5, accuracy).unwrap(); + let cylinder = primitives::cylinder( + "cylinder".into(), + Frame3 { + origin: [0.0, 0.0, -2.0], + ..Frame3::IDENTITY + }, + 0.75, + 4.0, + accuracy, + ) + .unwrap(); + let union = boolean_brep(&sphere, &cylinder, BooleanOp::Union, "union".into()).unwrap(); + let intersection = boolean_brep( + &sphere, + &cylinder, + BooleanOp::Intersection, + "intersection".into(), + ) + .unwrap(); + let sphere_cut = boolean_brep( + &sphere, + &cylinder, + BooleanOp::Subtraction, + "sphere-cut".into(), + ) + .unwrap(); + let cylinder_cut = boolean_brep( + &cylinder, + &sphere, + BooleanOp::Subtraction, + "cylinder-cut".into(), + ) + .unwrap(); + assert_eq!(union.brep.topology.faces.len(), 5); + assert_eq!(intersection.brep.topology.faces.len(), 3); + assert_eq!(sphere_cut.brep.topology.faces.len(), 2); + assert_eq!(cylinder_cut.brep.topology.faces.len(), 6); + assert_eq!(cylinder_cut.brep.solids.len(), 2); + assert!(sphere_cut + .brep + .topology + .faces + .iter() + .any(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed)); + assert_eq!( + cylinder_cut + .brep + .topology + .faces + .iter() + .filter(|face| face.provenance.role == FaceRole::Cut && face.provenance.reversed) + .count(), + 2 + ); + for (name, result) in [ + ("union", &union), + ("intersection", &intersection), + ("sphere-cut", &sphere_cut), + ("cylinder-cut", &cylinder_cut), + ] { + result.brep.validate().unwrap(); + let mesh = tessellate(&result.brep, 0.01, 2_000_000) + .unwrap_or_else(|error| panic!("{name}: {error}")); + assert!(!mesh.indices.is_empty()); + assert_eq!(result.report.face_mappings.len(), 4); + export_step(&result.brep, "metre").unwrap_or_else(|error| panic!("{name} STEP: {error}")); + } + let sphere_volume = 4.0 * std::f64::consts::PI * 1.5_f64.powi(3) / 3.0; + let cylinder_volume = std::f64::consts::PI * 0.75_f64.powi(2) * 4.0; + let intersection_volume = volume(&intersection.brep).abs(); + assert!( + (volume(&union.brep).abs() + intersection_volume - sphere_volume - cylinder_volume).abs() + < 0.3 + ); + assert!((volume(&sphere_cut.brep).abs() + intersection_volume - sphere_volume).abs() < 0.3); + assert!((volume(&cylinder_cut.brep).abs() + intersection_volume - cylinder_volume).abs() < 0.3); + + let noncoaxial = primitives::cylinder( + "noncoaxial".into(), + Frame3 { + origin: [0.2, 0.0, -2.0], + ..Frame3::IDENTITY + }, + 0.75, + 4.0, + accuracy, + ) + .unwrap(); + assert!(matches!( + boolean_brep(&sphere, &noncoaxial, BooleanOp::Union, "gap".into()), + Err(GeometryError::CoverageGap { .. }) + )); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/spheres.rs b/main/opengeometry/src/operations/modifying/boolean/tests/spheres.rs new file mode 100644 index 0000000..58002f0 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/spheres.rs @@ -0,0 +1,279 @@ +use super::support::{sphere, volume}; +use crate::brep::{ + placed, Accuracy, BrepEnvelope, Frame3, GeometryError, Orientation, Surface, SurfaceGeometry, +}; +use crate::math::{add, norm, scale, sub, Point3}; +use crate::operations::modifying::boolean::handlers::boolean_spheres; +use crate::operations::modifying::boolean::types::BooleanOp; +use crate::primitives; +use crate::tessellation::tessellate; + +#[test] +fn transverse_spheres_preserve_surfaces_shared_edge_and_occupancy_volumes() { + let a = sphere("a", [0.0; 3], 1.0); + let b = sphere("b", [1.0, 0.0, 0.0], 1.0); + let unit_volume = 4.0 * std::f64::consts::PI / 3.0; + let lens = 5.0 * std::f64::consts::PI / 12.0; + for (op, expected) in [ + (BooleanOp::Union, 2.0 * unit_volume - lens), + (BooleanOp::Intersection, lens), + (BooleanOp::Subtraction, unit_volume - lens), + ] { + let r = boolean_spheres(&a, &b, op, "r".into()).unwrap(); + assert_eq!(r.brep.topology.faces.len(), 2); + let occupied = |point: Point3| { + let inside_a = norm(sub(point, [0.0; 3])) < 1.0; + let inside_b = norm(sub(point, [1.0, 0.0, 0.0])) < 1.0; + match op { + BooleanOp::Union => inside_a || inside_b, + BooleanOp::Intersection => inside_a && inside_b, + BooleanOp::Subtraction => inside_a && !inside_b, + } + }; + for face in &r.brep.topology.faces { + let surface = r.brep.geometry.surface(face.surface).unwrap(); + let v = face.trim.uv_bounds[1]; + for fraction in [0.25, 0.5, 0.75] { + let uv = [0.9, v.lo + fraction * v.width()]; + let point = surface.point_at(uv).unwrap(); + let outward = scale(surface.normal_at(uv).unwrap(), face.sense.multiplier()); + assert!(occupied(sub(point, scale(outward, 1e-5)))); + assert!(!occupied(add(point, scale(outward, 1e-5)))); + } + } + assert!(r + .brep + .geometry + .surfaces + .iter() + .all(|s| matches!(s, SurfaceGeometry::Sphere { .. }))); + let shared: Vec<_> = r + .brep + .topology + .edges + .iter() + .filter(|e| !e.chart_seam) + .collect(); + assert_eq!(shared.len(), 1); + assert!(shared[0].twin_halfedge.is_some()); + assert!((volume(&r.brep) - expected).abs() < 0.04); + assert_eq!(r.report.face_mappings[0].result_faces, vec![0]); + assert_eq!(r.report.face_mappings[1].result_faces, vec![1]); + assert_eq!( + r.brep.topology.faces[1].provenance.reversed, + op == BooleanOp::Subtraction + ); + } +} + +#[test] +fn enclosed_subtraction_orients_cavity_and_preserves_ancestry() { + let a = sphere("host", [0.0; 3], 2.0); + let b = sphere("cutter", [0.2, 0.0, 0.0], 0.5); + let r = boolean_spheres(&a, &b, BooleanOp::Subtraction, "r".into()).unwrap(); + assert_eq!(r.brep.solids[0].cavity_shells, vec![1]); + assert_eq!(r.brep.topology.faces[1].sense, Orientation::Reverse); + assert_eq!( + r.brep.topology.faces[1].provenance.sources[0].entity, + "cutter" + ); + assert!((volume(&r.brep) - 4.0 * std::f64::consts::PI / 3.0 * (8.0 - 0.125)).abs() < 0.1); +} + +#[test] +fn contact_disjoint_containment_and_coincident_regularize_deterministically() { + let a = sphere("a", [0.0; 3], 1.0); + for x in [2.0, 2.5] { + let b = sphere("b", [x, 0.0, 0.0], 1.0); + let r = boolean_spheres(&a, &b, BooleanOp::Union, "r".into()).unwrap(); + assert_eq!(r.brep.solids.len(), 2); + assert_eq!(r.report.contacts.len(), usize::from(x == 2.0)); + assert!(boolean_spheres(&a, &b, BooleanOp::Intersection, "r".into()) + .unwrap() + .brep + .solids + .is_empty()); + } + let b = sphere("b", [0.0; 3], 1.0); + let r = boolean_spheres(&a, &b, BooleanOp::Union, "r".into()).unwrap(); + assert!(r.report.coincident); + assert_eq!(r.brep.topology.faces[0].provenance.sources.len(), 2); + assert!(boolean_spheres(&a, &b, BooleanOp::Subtraction, "r".into()) + .unwrap() + .brep + .topology + .faces + .is_empty()); + let big = sphere("big", [0.0; 3], 2.0); + assert!( + boolean_spheres(&a, &big, BooleanOp::Subtraction, "r".into()) + .unwrap() + .brep + .solids + .is_empty() + ); + assert_eq!( + boolean_spheres(&a, &big, BooleanOp::Intersection, "r".into()) + .unwrap() + .brep + .topology + .faces[0] + .provenance + .sources[0] + .entity, + "a" + ); +} + +#[test] +fn unsupported_inputs_and_invalid_geometry_never_enter_a_mesh_bridge() { + let a = sphere("a", [0.0; 3], 1.0); + let cylinder = + primitives::cylinder("c".into(), Frame3::IDENTITY, 1.0, 2.0, a.accuracy).unwrap(); + assert!(matches!( + boolean_spheres(&a, &cylinder, BooleanOp::Union, "r".into()), + Err(GeometryError::CoverageGap { .. }) + )); + let mut invalid = a.clone(); + invalid.topology.faces[0].surface = 100; + assert!(matches!( + boolean_spheres(&a, &invalid, BooleanOp::Union, "r".into()), + Err(GeometryError::MissingReference { .. }) + )); + let mut trimmed = a.clone(); + trimmed.topology.faces[0].trim.uv_bounds[1].hi = 0.5; + assert!(boolean_spheres(&a, &trimmed, BooleanOp::Union, "r".into()).is_err()); +} + +#[test] +fn positive_similarity_roundoff_does_not_reject_full_sphere_operands() { + let rotation = std::f64::consts::PI / 7.0; + let placement = Frame3 { + origin: [0.2, 0.1, 0.2], + x: [rotation.cos(), 0.0, -rotation.sin()], + y: [0.0, 1.0, 0.0], + z: [rotation.sin(), 0.0, rotation.cos()], + }; + let frame = Frame3 { + origin: [0.0, 1.2, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + ..Frame3::IDENTITY + }; + let first = primitives::sphere( + "a".into(), + frame, + 1.0, + sphere("reference", [0.0; 3], 1.0).accuracy, + ) + .unwrap(); + let second = primitives::sphere( + "b".into(), + Frame3 { + origin: [1.0, 1.2, 0.0], + ..frame + }, + 1.0, + first.accuracy, + ) + .unwrap(); + let first = placed(&first, placement, 1.25).unwrap(); + let second = placed(&second, placement, 1.25).unwrap(); + for op in [ + BooleanOp::Union, + BooleanOp::Intersection, + BooleanOp::Subtraction, + ] { + let result = boolean_spheres(&first, &second, op, "placed".into()).unwrap(); + result.brep.validate().unwrap(); + assert_eq!(result.brep.topology.faces.len(), 2); + assert_eq!(result.report.face_mappings[0].source.entity, "a"); + } +} + +#[test] +fn internal_tangency_and_unresolved_small_caps_are_errors() { + let a = sphere("a", [0.0; 3], 2.0); + let b = sphere("b", [1.0, 0.0, 0.0], 1.0); + assert!(matches!( + boolean_spheres(&a, &b, BooleanOp::Subtraction, "r".into()), + Err(GeometryError::UnresolvedIntersection(_)) + )); + let a = sphere("a", [0.0; 3], 1.0); + let b = sphere("b", [2.0 - 1e-10, 0.0, 0.0], 1.0); + assert!(matches!( + boolean_spheres(&a, &b, BooleanOp::Intersection, "r".into()), + Err(GeometryError::UnresolvedIntersection(_)) + )); +} + +#[test] +fn rotated_unequal_spheres_roundtrip_and_keep_identical_boundary_positions() { + let a = sphere("a", [5.0, -3.0, 8.0], 2.0); + let b = sphere("b", [6.0, -2.0, 9.0], 1.25); + let r = boolean_spheres(&a, &b, BooleanOp::Subtraction, "r".into()).unwrap(); + let decoded = BrepEnvelope::from_json(&r.brep.to_json().unwrap()).unwrap(); + let mesh = tessellate(&decoded, 0.02, 2_000_000).unwrap(); + let boundary = decoded.geometry.curves[0].elementary_point(0.0).unwrap(); + let count = mesh + .positions + .chunks_exact(3) + .filter(|p| p == &boundary.as_slice()) + .count(); + assert!(count >= 2); + assert!(mesh.indices.len() > 100); +} + +#[test] +fn sphere_booleans_obey_scaled_budgets_and_building_coordinates() { + for size in [1e-6, 1.0, 1e6] { + let accuracy = Accuracy { + geometric: size * 1e-9, + intersection: size * 1e-10, + tessellation: size * 0.01, + exchange: size * 1e-5, + }; + let a = primitives::sphere("a".into(), Frame3::IDENTITY, size, accuracy).unwrap(); + let b = primitives::sphere( + "b".into(), + Frame3 { + origin: [size, 0.0, 0.0], + ..Frame3::IDENTITY + }, + size, + accuracy, + ) + .unwrap(); + let r = boolean_spheres(&a, &b, BooleanOp::Subtraction, "r".into()).unwrap(); + assert_eq!(r.brep.topology.faces.len(), 2); + tessellate(&r.brep, size * 0.02, 2_000_000).unwrap(); + } + let accuracy = Accuracy { + geometric: 1e-6, + intersection: 5e-7, + tessellation: 0.01, + exchange: 1e-5, + }; + let a = primitives::sphere( + "a".into(), + Frame3 { + origin: [1e9; 3], + ..Frame3::IDENTITY + }, + 1.0, + accuracy, + ) + .unwrap(); + let b = primitives::sphere( + "b".into(), + Frame3 { + origin: [1e9 + 1.0, 1e9, 1e9], + ..Frame3::IDENTITY + }, + 1.0, + accuracy, + ) + .unwrap(); + let r = boolean_spheres(&a, &b, BooleanOp::Intersection, "r".into()).unwrap(); + tessellate(&r.brep, 0.01, 2_000_000).unwrap(); +} diff --git a/main/opengeometry/src/operations/modifying/boolean/tests/support.rs b/main/opengeometry/src/operations/modifying/boolean/tests/support.rs new file mode 100644 index 0000000..f1eb13d --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/tests/support.rs @@ -0,0 +1,103 @@ +use crate::brep::{Accuracy, BrepEnvelope, Frame3}; +use crate::math::{cross, dot, Point3}; +use crate::primitives; +use crate::tessellation::tessellate; + +pub(super) fn sphere(id: &str, center: Point3, radius: f64) -> BrepEnvelope { + primitives::sphere( + id.into(), + Frame3 { + origin: center, + ..Frame3::IDENTITY + }, + radius, + Accuracy { + geometric: 1e-9, + intersection: 1e-10, + tessellation: 0.01, + exchange: 1e-5, + }, + ) + .unwrap() +} + +pub(super) fn cylinder(id: &str, origin_z: f64, height: f64, reverse: bool) -> BrepEnvelope { + let frame = if reverse { + Frame3 { + origin: [0.0, 0.0, origin_z], + x: [1.0, 0.0, 0.0], + y: [0.0, -1.0, 0.0], + z: [0.0, 0.0, -1.0], + } + } else { + Frame3 { + origin: [0.0, 0.0, origin_z], + ..Frame3::IDENTITY + } + }; + primitives::cylinder( + id.into(), + frame, + 1.0, + height, + Accuracy { + geometric: 1e-9, + intersection: 1e-10, + tessellation: 0.01, + exchange: 1e-5, + }, + ) + .unwrap() +} + +pub(super) fn volume(brep: &BrepEnvelope) -> f64 { + volume_at_deflection(brep, 0.02) +} + +pub(super) fn volume_at_deflection(brep: &BrepEnvelope, deflection: f64) -> f64 { + let mesh = tessellate(brep, deflection, 2_000_000).unwrap(); + mesh.indices + .chunks_exact(3) + .map(|ids| { + let points: [Point3; 3] = std::array::from_fn(|i| { + let start = ids[i] as usize * 3; + [ + mesh.positions[start], + mesh.positions[start + 1], + mesh.positions[start + 2], + ] + }); + dot(points[0], cross(points[1], points[2])) / 6.0 + }) + .sum() +} + +pub(super) fn extrusion(id: &str, outer: Vec<[f64; 2]>, holes: Vec>) -> BrepEnvelope { + extrusion_span(id, 0.0, 3.0, outer, holes) +} + +pub(super) fn extrusion_span( + id: &str, + origin_z: f64, + height: f64, + outer: Vec<[f64; 2]>, + holes: Vec>, +) -> BrepEnvelope { + primitives::linear_extrusion( + id.into(), + Frame3 { + origin: [0.0, 0.0, origin_z], + ..Frame3::IDENTITY + }, + outer, + holes, + height, + Accuracy { + geometric: 1.0e-9, + intersection: 1.0e-10, + tessellation: 0.01, + exchange: 1.0e-5, + }, + ) + .unwrap() +} diff --git a/main/opengeometry/src/operations/modifying/boolean/types.rs b/main/opengeometry/src/operations/modifying/boolean/types.rs new file mode 100644 index 0000000..1e94241 --- /dev/null +++ b/main/opengeometry/src/operations/modifying/boolean/types.rs @@ -0,0 +1,31 @@ +use crate::brep::{BrepEnvelope, FaceSource, GeometryQuality}; +use crate::math::Point3; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum BooleanOp { + Union, + Intersection, + Subtraction, +} + +#[derive(Clone, Debug, Serialize)] +pub(super) struct FaceMapping { + pub(super) source: FaceSource, + pub(super) result_faces: Vec, +} + +#[derive(Clone, Debug, Serialize)] +pub struct BooleanReport { + pub(super) operation: BooleanOp, + pub(super) quality: GeometryQuality, + pub(super) contacts: Vec, + pub(super) coincident: bool, + pub(super) face_mappings: Vec, +} + +pub struct BooleanResult { + pub brep: BrepEnvelope, + pub report: BooleanReport, +} diff --git a/main/opengeometry/src/operations/modifying/mod.rs b/main/opengeometry/src/operations/modifying/mod.rs new file mode 100644 index 0000000..9fa2a6e --- /dev/null +++ b/main/opengeometry/src/operations/modifying/mod.rs @@ -0,0 +1 @@ +pub mod boolean; diff --git a/main/opengeometry/src/operations/offset.rs b/main/opengeometry/src/operations/offset.rs deleted file mode 100644 index 0f22a20..0000000 --- a/main/opengeometry/src/operations/offset.rs +++ /dev/null @@ -1,541 +0,0 @@ -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; - -const EPSILON: f64 = crate::tolerance::MODELING_TOLERANCE_FLOOR; - -#[derive(Clone, Copy)] -pub struct OffsetOptions { - pub bevel: bool, - pub acute_threshold_degrees: f64, -} - -impl Default for OffsetOptions { - fn default() -> Self { - OffsetOptions { - bevel: true, - acute_threshold_degrees: 35.0, - } - } -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct OffsetResult { - pub points: Vec, - pub beveled_vertex_indices: Vec, - pub is_closed: bool, -} - -impl OffsetResult { - pub fn empty(is_closed: bool) -> Self { - OffsetResult { - points: Vec::new(), - beveled_vertex_indices: Vec::new(), - is_closed, - } - } -} - -#[derive(Clone, Copy)] -struct Point2 { - x: f64, - z: f64, -} - -impl Point2 { - fn new(x: f64, z: f64) -> Self { - Point2 { x, z } - } - - fn from_vec3(v: Vector3) -> Self { - Point2::new(v.x, v.z) - } - - fn add(self, other: Point2) -> Point2 { - Point2::new(self.x + other.x, self.z + other.z) - } - - fn sub(self, other: Point2) -> Point2 { - Point2::new(self.x - other.x, self.z - other.z) - } - - fn scale(self, scalar: f64) -> Point2 { - Point2::new(self.x * scalar, self.z * scalar) - } - - fn dot(self, other: Point2) -> f64 { - self.x * other.x + self.z * other.z - } - - fn cross(self, other: Point2) -> f64 { - self.x * other.z - self.z * other.x - } - - fn length(self) -> f64 { - (self.x * self.x + self.z * self.z).sqrt() - } - - fn normalize(self) -> Option { - let len = self.length(); - if len <= EPSILON { - None - } else { - Some(self.scale(1.0 / len)) - } - } -} - -pub fn offset_path( - points: &[Vector3], - distance: f64, - force_closed: Option, - options: OffsetOptions, -) -> OffsetResult { - let (clean_points, is_closed) = sanitize_path(points, force_closed); - - if clean_points.len() < 2 { - return OffsetResult::empty(is_closed); - } - - if is_closed && clean_points.len() < 3 { - return OffsetResult::empty(is_closed); - } - - if distance.abs() <= EPSILON { - let mut result = OffsetResult { - points: clean_points, - beveled_vertex_indices: Vec::new(), - is_closed, - }; - close_result_if_needed(&mut result); - return result; - } - - let segment_count = if is_closed { - clean_points.len() - } else { - clean_points.len() - 1 - }; - - let mut segment_dirs: Vec = Vec::with_capacity(segment_count); - let mut segment_normals: Vec = Vec::with_capacity(segment_count); - - for segment_index in 0..segment_count { - let i0 = segment_index; - let i1 = if is_closed { - (segment_index + 1) % clean_points.len() - } else { - segment_index + 1 - }; - - let start = Point2::from_vec3(clean_points[i0]); - let end = Point2::from_vec3(clean_points[i1]); - let dir = end.sub(start); - - let Some(unit_dir) = dir.normalize() else { - return OffsetResult::empty(is_closed); - }; - - let left_normal = Point2::new(-unit_dir.z, unit_dir.x); - segment_dirs.push(unit_dir); - segment_normals.push(left_normal); - } - - let mut result = OffsetResult::empty(is_closed); - - if !is_closed { - let first = clean_points[0]; - let first_offset = Point2::from_vec3(first).add(segment_normals[0].scale(distance)); - push_unique( - &mut result.points, - Vector3::new(first_offset.x, first.y, first_offset.z), - ); - - for vertex_index in 1..(clean_points.len() - 1) { - append_offset_corner( - &clean_points, - &segment_dirs, - &segment_normals, - false, - vertex_index, - distance, - options, - &mut result, - ); - } - - let last = clean_points[clean_points.len() - 1]; - let last_offset = - Point2::from_vec3(last).add(segment_normals[segment_normals.len() - 1].scale(distance)); - push_unique( - &mut result.points, - Vector3::new(last_offset.x, last.y, last_offset.z), - ); - - close_result_if_needed(&mut result); - return result; - } - - for vertex_index in 0..clean_points.len() { - append_offset_corner( - &clean_points, - &segment_dirs, - &segment_normals, - true, - vertex_index, - distance, - options, - &mut result, - ); - } - - close_result_if_needed(&mut result); - result -} - -fn sanitize_path(points: &[Vector3], force_closed: Option) -> (Vec, bool) { - let mut clean: Vec = Vec::new(); - - for point in points { - if clean - .last() - .map(|last| are_close_3d(*last, *point)) - .unwrap_or(false) - { - continue; - } - clean.push(*point); - } - - if clean.is_empty() { - return (clean, force_closed.unwrap_or(false)); - } - - let mut is_closed = force_closed.unwrap_or(false); - if force_closed.is_none() && clean.len() >= 3 { - is_closed = are_close_3d(clean[0], clean[clean.len() - 1]); - } - - if is_closed && clean.len() >= 2 && are_close_3d(clean[0], clean[clean.len() - 1]) { - clean.pop(); - } - - (clean, is_closed) -} - -fn append_offset_corner( - clean_points: &[Vector3], - segment_dirs: &[Point2], - segment_normals: &[Point2], - is_closed_path: bool, - vertex_index: usize, - distance: f64, - options: OffsetOptions, - result: &mut OffsetResult, -) { - let point = clean_points[vertex_index]; - let corner = Point2::from_vec3(point); - - let prev_segment = if vertex_index == 0 { - segment_dirs.len() - 1 - } else { - vertex_index - 1 - }; - let next_segment = vertex_index % segment_dirs.len(); - - let prev_dir = segment_dirs[prev_segment]; - let next_dir = segment_dirs[next_segment]; - - let prev_normal = segment_normals[prev_segment]; - let next_normal = segment_normals[next_segment]; - - let prev_offset_anchor = corner.add(prev_normal.scale(distance)); - let next_offset_anchor = corner.add(next_normal.scale(distance)); - - let dot = prev_dir.dot(next_dir).clamp(-1.0, 1.0); - let turn_angle = dot.acos(); - let interior_angle = std::f64::consts::PI - turn_angle; - let threshold = options - .acute_threshold_degrees - .clamp(1.0, 179.0) - .to_radians(); - - let turn_cross = prev_dir.cross(next_dir); - let nearly_collinear = turn_cross.abs() <= 1.0e-7; - - if nearly_collinear && dot > 0.9999 { - push_unique( - &mut result.points, - Vector3::new(prev_offset_anchor.x, point.y, prev_offset_anchor.z), - ); - return; - } - - let turn_sign = turn_cross * distance; - let is_outer_corner = turn_sign > EPSILON; - let is_inner_corner = turn_sign < -EPSILON; - - // Open-path inner corners only need clipping once they become acute enough - // to create oversized miters. Broader turns should keep a clean miter so - // they do not look like an unexpected bevel. - if !is_closed_path && is_inner_corner && interior_angle <= threshold { - push_unique( - &mut result.points, - Vector3::new(prev_offset_anchor.x, point.y, prev_offset_anchor.z), - ); - push_unique( - &mut result.points, - Vector3::new(next_offset_anchor.x, point.y, next_offset_anchor.z), - ); - return; - } - let bevel_due_to_angle = options.bevel && is_outer_corner && interior_angle <= threshold; - - if bevel_due_to_angle { - push_unique( - &mut result.points, - Vector3::new(prev_offset_anchor.x, point.y, prev_offset_anchor.z), - ); - push_unique( - &mut result.points, - Vector3::new(next_offset_anchor.x, point.y, next_offset_anchor.z), - ); - result.beveled_vertex_indices.push(vertex_index as u32); - return; - } - - if let Some(intersection) = - line_intersection_2d(prev_offset_anchor, prev_dir, next_offset_anchor, next_dir) - { - push_unique( - &mut result.points, - Vector3::new(intersection.x, point.y, intersection.z), - ); - } else if options.bevel && is_outer_corner { - push_unique( - &mut result.points, - Vector3::new(prev_offset_anchor.x, point.y, prev_offset_anchor.z), - ); - push_unique( - &mut result.points, - Vector3::new(next_offset_anchor.x, point.y, next_offset_anchor.z), - ); - result.beveled_vertex_indices.push(vertex_index as u32); - } else { - let midpoint = Point2::new( - (prev_offset_anchor.x + next_offset_anchor.x) * 0.5, - (prev_offset_anchor.z + next_offset_anchor.z) * 0.5, - ); - push_unique( - &mut result.points, - Vector3::new(midpoint.x, point.y, midpoint.z), - ); - } -} - -fn close_result_if_needed(result: &mut OffsetResult) { - if !result.is_closed || result.points.len() < 2 { - return; - } - - let first = result.points[0]; - let last = result.points[result.points.len() - 1]; - if !are_close_3d(first, last) { - result.points.push(first); - } -} - -fn line_intersection_2d(p1: Point2, d1: Point2, p2: Point2, d2: Point2) -> Option { - let denom = d1.cross(d2); - if denom.abs() <= EPSILON { - return None; - } - - let delta = p2.sub(p1); - let t = delta.cross(d2) / denom; - Some(p1.add(d1.scale(t))) -} - -fn are_close_3d(a: Vector3, b: Vector3) -> bool { - let dx = a.x - b.x; - let dy = a.y - b.y; - let dz = a.z - b.z; - (dx * dx + dy * dy + dz * dz) <= EPSILON * EPSILON -} - -fn push_unique(points: &mut Vec, candidate: Vector3) { - if points - .last() - .map(|last| are_close_3d(*last, candidate)) - .unwrap_or(false) - { - return; - } - points.push(candidate); -} - -#[cfg(test)] -mod tests { - use super::{offset_path, OffsetOptions}; - use openmaths::Vector3; - - fn approx_eq(a: f64, b: f64) -> bool { - (a - b).abs() < 1.0e-6 - } - - #[test] - fn line_offset_generates_parallel_points() { - let input = vec![Vector3::new(0.0, 0.0, 0.0), Vector3::new(5.0, 0.0, 0.0)]; - - let result = offset_path(&input, 1.0, Some(false), OffsetOptions::default()); - - assert_eq!(result.points.len(), 2); - assert!((result.points[0].z - 1.0).abs() < 1.0e-6); - assert!((result.points[1].z - 1.0).abs() < 1.0e-6); - } - - #[test] - fn acute_corner_can_beveled() { - let input = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.3), - ]; - - let result = offset_path( - &input, - 0.3, - Some(false), - OffsetOptions { - bevel: true, - acute_threshold_degrees: 45.0, - }, - ); - - assert!(result.points.len() >= 4); - assert!(!result.beveled_vertex_indices.is_empty()); - } - - #[test] - fn closed_offset_keeps_closed_flag() { - let input = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 2.0), - Vector3::new(0.0, 0.0, 2.0), - Vector3::new(0.0, 0.0, 0.0), - ]; - - let result = offset_path(&input, 0.2, None, OffsetOptions::default()); - - assert!(result.is_closed); - assert!(result.points.len() >= 5); - assert!((result.points[0].x - result.points[result.points.len() - 1].x).abs() < 1.0e-6); - assert!((result.points[0].z - result.points[result.points.len() - 1].z).abs() < 1.0e-6); - } - - #[test] - fn inner_acute_corner_is_not_beveled() { - let input = vec![ - Vector3::new(-1.2, 0.0, -2.4), - Vector3::new(0.2, 0.0, -1.7), - Vector3::new(0.8, 0.0, -0.6), - Vector3::new(0.0, 0.0, -0.3), - Vector3::new(2.4, 0.0, 1.2), - ]; - - let result = offset_path( - &input, - 0.45, - Some(false), - OffsetOptions { - bevel: true, - acute_threshold_degrees: 90.0, - }, - ); - - assert!(result.beveled_vertex_indices.contains(&2)); - assert!(!result.beveled_vertex_indices.contains(&3)); - } - - #[test] - fn open_path_inner_corner_is_mitered_when_above_threshold() { - let input = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 1.0), - ]; - - let result = offset_path( - &input, - -0.2, - Some(false), - OffsetOptions { - bevel: false, - acute_threshold_degrees: 35.0, - }, - ); - - assert_eq!(result.points.len(), 3); - assert!(result.beveled_vertex_indices.is_empty()); - assert!(approx_eq(result.points[1].x, 1.2)); - assert!(approx_eq(result.points[1].z, -0.2)); - } - - #[test] - fn open_path_inner_corner_is_clipped_when_at_or_below_threshold() { - let input = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 1.0), - ]; - - let result = offset_path( - &input, - -0.2, - Some(false), - OffsetOptions { - bevel: false, - acute_threshold_degrees: 90.0, - }, - ); - - assert_eq!(result.points.len(), 4); - assert!(result.beveled_vertex_indices.is_empty()); - assert!(approx_eq(result.points[1].x, 1.0)); - assert!(approx_eq(result.points[1].z, -0.2)); - assert!(approx_eq(result.points[2].x, 1.2)); - assert!(approx_eq(result.points[2].z, 0.0)); - } - - #[test] - fn acute_open_path_inner_corner_is_clipped() { - let input = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 1.0), - Vector3::new(3.0, 0.0, 1.0), - ]; - - let result = offset_path( - &input, - 0.5, - Some(false), - OffsetOptions { - bevel: false, - acute_threshold_degrees: 90.0, - }, - ); - - // Start + outer corner + two clipped inner-corner anchors + end. - assert_eq!(result.points.len(), 5); - - let sqrt2 = 2.0_f64.sqrt(); - let expected_prev_anchor_x = 1.0 - 0.5 / sqrt2; - let expected_prev_anchor_z = 1.0 - 0.5 / sqrt2; - - assert!(approx_eq(result.points[2].x, expected_prev_anchor_x)); - assert!(approx_eq(result.points[2].z, expected_prev_anchor_z)); - assert!(approx_eq(result.points[3].x, 1.0)); - assert!(approx_eq(result.points[3].z, 1.5)); - } -} diff --git a/main/opengeometry/src/operations/profile.rs b/main/opengeometry/src/operations/profile.rs deleted file mode 100644 index 23beb84..0000000 --- a/main/opengeometry/src/operations/profile.rs +++ /dev/null @@ -1,236 +0,0 @@ -//! Profile robustness (debt item D7). -//! -//! Generated and imported profiles routinely contain artifacts — wrong winding, -//! coincident/duplicate points, degenerate edges, self-intersections. The -//! kernel previously assumed clean CCW-outer/CW-hole input and only computed -//! winding in the hardcoded XZ plane; ear-clipping then silently produced -//! garbage faces on bad input with no diagnostic. This module validates a loop -//! before face creation: it cleans duplicates, computes winding on the loop's -//! actual plane (any orientation), auto-corrects to CCW-outer, and detects -//! self-intersection and degeneracy — reporting what it found rather than -//! emitting a malformed face. - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; - -use crate::geometry::poly2d::{self_intersects2, signed_area2, Pt2, DEFAULT_EPS}; -use crate::operations::triangulate::compute_polygon_normal; - -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct ProfileReport { - /// The loop has at least 3 non-degenerate vertices and does not self-intersect. - pub is_valid: bool, - /// Duplicate / coincident vertices removed during cleanup. - pub removed_duplicates: usize, - /// Winding was reversed to make the outer loop CCW (in its own plane). - pub winding_corrected: bool, - /// Two non-adjacent edges cross — a face built from this loop is invalid. - pub self_intersecting: bool, - /// Fewer than 3 vertices remain, or the loop encloses ~zero area. - pub degenerate: bool, - /// Human-readable diagnostics for each issue found. - pub diagnostics: Vec, -} - -/// Analyzes (and conceptually repairs) a profile loop. Returns the report plus -/// the cleaned, correctly-wound points ready for face creation. The returned -/// points are empty when the loop is too degenerate to use. -pub fn analyze_profile(points: &[Vector3]) -> (ProfileReport, Vec) { - let mut report = ProfileReport::default(); - - // 1. Remove consecutive duplicates and a closing duplicate. - let mut cleaned: Vec = Vec::with_capacity(points.len()); - for p in points { - let dup = cleaned - .last() - .map_or(false, |last: &Vector3| coincident(*last, *p)); - if dup { - report.removed_duplicates += 1; - } else { - cleaned.push(*p); - } - } - if cleaned.len() > 1 && coincident(cleaned[0], *cleaned.last().unwrap()) { - cleaned.pop(); - report.removed_duplicates += 1; - } - if report.removed_duplicates > 0 { - report.diagnostics.push(format!( - "removed {} duplicate vertices", - report.removed_duplicates - )); - } - - if cleaned.len() < 3 { - report.degenerate = true; - report - .diagnostics - .push("fewer than 3 unique vertices".into()); - return (report, Vec::new()); - } - - // 2. Project onto the loop's dominant plane to reason in 2D. - let normal = compute_polygon_normal(&cleaned).unwrap_or(Vector3::new(0.0, 1.0, 0.0)); - let ring = project_to_plane(&cleaned, normal); - let area = signed_area2(&ring); - - // 3. Self-intersection. Checked first: a bowtie has ~zero signed area (its - // sub-triangles cancel) yet is self-intersecting, not merely degenerate. - if self_intersects2(&ring, DEFAULT_EPS) { - report.self_intersecting = true; - report - .diagnostics - .push("loop self-intersects (non-adjacent edges cross)".into()); - } - - // 4. Degeneracy: ~zero enclosed area and not self-intersecting (collinear). - if !report.self_intersecting && area.abs() <= DEFAULT_EPS { - report.degenerate = true; - report - .diagnostics - .push("loop encloses ~zero area (collinear/degenerate)".into()); - return (report, Vec::new()); - } - - // 5. Winding: make a sound outer loop CCW in its projection plane. - if !report.self_intersecting && area < 0.0 { - cleaned.reverse(); - report.winding_corrected = true; - report - .diagnostics - .push("winding reversed to CCW-outer".into()); - } - - report.is_valid = !report.self_intersecting && !report.degenerate; - let out = if report.is_valid { cleaned } else { Vec::new() }; - (report, out) -} - -/// Wasm entry point: analyze a flat `[x0,y0,z0,…]` loop, returning the report as -/// JSON (the corrected points are not returned here; use the kernel API). -#[wasm_bindgen(js_name = analyzeProfile)] -pub fn analyze_profile_wasm(flat_points: Vec) -> Result { - let points: Vec = flat_points - .chunks_exact(3) - .map(|c| Vector3::new(c[0], c[1], c[2])) - .collect(); - let (report, _) = analyze_profile(&points); - serde_json::to_string(&report) - .map_err(|e| JsValue::from_str(&format!("Failed to serialize profile report: {}", e))) -} - -fn coincident(a: Vector3, b: Vector3) -> bool { - let dx = a.x - b.x; - let dy = a.y - b.y; - let dz = a.z - b.z; - (dx * dx + dy * dy + dz * dz) <= DEFAULT_EPS * DEFAULT_EPS -} - -/// Projects 3D points onto a 2D ring in the plane whose normal is `normal`, -/// dropping the dominant normal axis (so winding/area are computed in-plane). -fn project_to_plane(points: &[Vector3], normal: Vector3) -> Vec { - let (ax, az) = if normal.y.abs() >= normal.x.abs() && normal.y.abs() >= normal.z.abs() { - (0, 2) // dominant Y → project to XZ - } else if normal.x.abs() >= normal.z.abs() { - (1, 2) // dominant X → project to YZ - } else { - (0, 1) // dominant Z → project to XY - }; - let comp = |v: &Vector3, i: usize| match i { - 0 => v.x, - 1 => v.y, - _ => v.z, - }; - points - .iter() - .map(|v| Pt2::new(comp(v, ax), comp(v, az))) - .collect() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn clean_ccw_square_is_valid_unchanged() { - let square = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 1.0), - Vector3::new(0.0, 0.0, 1.0), - ]; - let (report, pts) = analyze_profile(&square); - assert!(report.is_valid); - assert_eq!(pts.len(), 4); - } - - #[test] - fn clockwise_loop_is_corrected() { - // Clockwise in XZ → negative area → reversed. - let cw = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 1.0), - Vector3::new(1.0, 0.0, 1.0), - Vector3::new(1.0, 0.0, 0.0), - ]; - let (report, _) = analyze_profile(&cw); - assert!(report.winding_corrected); - assert!(report.is_valid); - } - - #[test] - fn duplicate_points_are_removed() { - let dup = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 1.0), - Vector3::new(0.0, 0.0, 1.0), - ]; - let (report, pts) = analyze_profile(&dup); - assert_eq!(report.removed_duplicates, 1); - assert_eq!(pts.len(), 4); - } - - #[test] - fn bowtie_is_flagged_self_intersecting() { - // Classic self-intersecting "bowtie" quad. - let bowtie = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 1.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 1.0), - ]; - let (report, _) = analyze_profile(&bowtie); - assert!(report.self_intersecting); - assert!(!report.is_valid); - } - - #[test] - fn collinear_points_are_degenerate() { - let line = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 0.0), - ]; - let (report, pts) = analyze_profile(&line); - assert!(report.degenerate); - assert!(pts.is_empty()); - } - - #[test] - fn winding_works_on_tilted_plane() { - // A loop on the XY plane (normal +Z) must still get a correct winding - // verdict — the old XZ-only signed area would have read ~zero here. - let square_xy = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(1.0, 1.0, 0.0), - Vector3::new(0.0, 1.0, 0.0), - ]; - let (report, pts) = analyze_profile(&square_xy); - assert!(!report.degenerate, "XY loop must not read as zero-area"); - assert_eq!(pts.len(), 4); - } -} diff --git a/main/opengeometry/src/operations/sweep.rs b/main/opengeometry/src/operations/sweep.rs deleted file mode 100644 index f04e0ba..0000000 --- a/main/opengeometry/src/operations/sweep.rs +++ /dev/null @@ -1,1486 +0,0 @@ -use std::error::Error; -use std::f64::consts::PI; -use std::fmt; - -use crate::brep::{Brep, BrepBuilder, BrepError}; -use openmaths::Vector3; -use uuid::Uuid; - -const EPSILON: f64 = crate::tolerance::MODELING_TOLERANCE_FLOOR; -const PLANAR_TOLERANCE_FACTOR: f64 = 1.0e-7; - -#[derive(Clone, Copy)] -pub struct SweepOptions { - pub cap_start: bool, - pub cap_end: bool, -} - -impl Default for SweepOptions { - fn default() -> Self { - SweepOptions { - cap_start: true, - cap_end: true, - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum SweepErrorKind { - InvalidPath, - InvalidProfile, - NonPlanarProfile, - NonPlanarClosedPath, - PathReversal, - ProjectionFailure, - TopologyError, -} - -#[derive(Debug, Clone)] -pub struct SweepError { - kind: SweepErrorKind, - message: String, -} - -impl SweepError { - fn new(kind: SweepErrorKind, message: impl Into) -> Self { - Self { - kind, - message: message.into(), - } - } - - pub fn kind(&self) -> SweepErrorKind { - self.kind - } -} - -impl fmt::Display for SweepError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.message) - } -} - -impl Error for SweepError {} - -impl From for SweepError { - fn from(error: BrepError) -> Self { - SweepError::new( - SweepErrorKind::TopologyError, - format!("BRep construction failed: {}", error), - ) - } -} - -#[derive(Clone, Copy, Debug)] -struct Vec2f { - x: f64, - y: f64, -} - -impl Vec2f { - fn new(x: f64, y: f64) -> Self { - Self { x, y } - } -} - -#[derive(Clone, Copy, Debug)] -struct Vec3f { - x: f64, - y: f64, - z: f64, -} - -impl Vec3f { - fn new(x: f64, y: f64, z: f64) -> Self { - Vec3f { x, y, z } - } - - fn from_vector3(v: &Vector3) -> Self { - Vec3f::new(v.x, v.y, v.z) - } - - fn to_vector3(self) -> Vector3 { - Vector3::new(self.x, self.y, self.z) - } - - fn add(self, other: Vec3f) -> Vec3f { - Vec3f::new(self.x + other.x, self.y + other.y, self.z + other.z) - } - - fn sub(self, other: Vec3f) -> Vec3f { - Vec3f::new(self.x - other.x, self.y - other.y, self.z - other.z) - } - - fn scale(self, s: f64) -> Vec3f { - Vec3f::new(self.x * s, self.y * s, self.z * s) - } - - fn dot(self, other: Vec3f) -> f64 { - self.x * other.x + self.y * other.y + self.z * other.z - } - - fn cross(self, other: Vec3f) -> Vec3f { - Vec3f::new( - self.y * other.z - self.z * other.y, - self.z * other.x - self.x * other.z, - self.x * other.y - self.y * other.x, - ) - } - - fn norm_sq(self) -> f64 { - self.dot(self) - } - - fn norm(self) -> f64 { - self.norm_sq().sqrt() - } - - fn normalized(self) -> Option { - let n = self.norm(); - if n <= EPSILON { - None - } else { - Some(self.scale(1.0 / n)) - } - } - - #[cfg(test)] - fn distance(self, other: Vec3f) -> f64 { - self.sub(other).norm() - } -} - -#[derive(Clone, Copy, Debug)] -struct SectionPlane { - origin: Vec3f, - normal: Vec3f, -} - -#[derive(Clone, Debug)] -struct ProfileData { - local_points: Vec, - normal: Vec3f, - u_axis: Vec3f, - scale: f64, -} - -#[cfg_attr(not(test), allow(dead_code))] -#[derive(Clone, Debug)] -struct PreparedSweep { - path: Vec, - is_closed: bool, - segment_dirs: Vec, - section_planes: Vec, - sections: Vec>, -} - -pub fn sweep_profile_along_path( - path_points: &[Vector3], - profile_points: &[Vector3], - options: SweepOptions, -) -> Result { - let prepared = build_prepared_sweep(path_points, profile_points)?; - - let mut builder = BrepBuilder::new(Uuid::new_v4()); - let mut section_vertex_ids: Vec> = Vec::with_capacity(prepared.sections.len()); - - for section in &prepared.sections { - let section_vertices: Vec = - section.iter().map(|point| point.to_vector3()).collect(); - section_vertex_ids.push(builder.add_vertices(§ion_vertices)); - } - - let ring_size = prepared.sections[0].len(); - let side_segments = if prepared.is_closed { - prepared.sections.len() - } else { - prepared.sections.len() - 1 - }; - - for section_index in 0..side_segments { - let next_section = (section_index + 1) % prepared.sections.len(); - - for profile_index in 0..ring_size { - let next_profile = (profile_index + 1) % ring_size; - let face = orient_side_face_indices( - &prepared.sections[section_index], - &prepared.sections[next_section], - §ion_vertex_ids[section_index], - §ion_vertex_ids[next_section], - prepared.path[section_index], - prepared.path[next_section], - profile_index, - next_profile, - ); - builder.add_face(&face, &[])?; - } - } - - if !prepared.is_closed { - if options.cap_start { - let start_indices = orient_cap_indices( - &prepared.sections[0], - section_vertex_ids[0].clone(), - prepared.section_planes[0].normal.scale(-1.0), - ); - builder.add_face(&start_indices, &[])?; - } - - if options.cap_end { - let last_index = prepared.sections.len() - 1; - let end_indices = orient_cap_indices( - &prepared.sections[last_index], - section_vertex_ids[last_index].clone(), - prepared.section_planes[last_index].normal, - ); - builder.add_face(&end_indices, &[])?; - } - } - - let shell_closed = prepared.is_closed || (options.cap_start && options.cap_end); - builder.add_shell_from_all_faces(shell_closed)?; - builder.build().map_err(Into::into) -} - -fn build_prepared_sweep( - path_points: &[Vector3], - profile_points: &[Vector3], -) -> Result { - let (path, is_closed) = sanitize_path(path_points)?; - let profile = sanitize_profile(profile_points)?; - let segment_dirs = build_segment_dirs(&path, is_closed)?; - - if is_closed { - ensure_closed_path_is_planar(&path)?; - } - - let section_planes = build_section_planes(&path, &segment_dirs, is_closed)?; - let sections = build_sections(&path, &segment_dirs, §ion_planes, &profile, is_closed)?; - - Ok(PreparedSweep { - path, - is_closed, - segment_dirs, - section_planes, - sections, - }) -} - -fn sanitize_path(path: &[Vector3]) -> Result<(Vec, bool), SweepError> { - let mut cleaned = remove_consecutive_duplicates(path); - - if cleaned.len() < 2 { - return Err(SweepError::new( - SweepErrorKind::InvalidPath, - "Sweep path requires at least 2 distinct points.", - )); - } - - let mut is_closed = false; - if cleaned.len() >= 3 { - let tolerance_sq = tolerance_for_vec3_points(&cleaned, PLANAR_TOLERANCE_FACTOR).powi(2); - let first = cleaned[0]; - let last = cleaned[cleaned.len() - 1]; - if first.sub(last).norm_sq() <= tolerance_sq { - cleaned.pop(); - is_closed = true; - } - } - - let minimum_points = if is_closed { 3 } else { 2 }; - if cleaned.len() < minimum_points { - return Err(SweepError::new( - SweepErrorKind::InvalidPath, - "Sweep path degenerates after duplicate-point cleanup.", - )); - } - - Ok((cleaned, is_closed)) -} - -fn sanitize_profile(profile: &[Vector3]) -> Result { - let mut cleaned = remove_consecutive_duplicates(profile); - - if cleaned.len() >= 3 { - let tolerance_sq = tolerance_for_vec3_points(&cleaned, PLANAR_TOLERANCE_FACTOR).powi(2); - let first = cleaned[0]; - let last = cleaned[cleaned.len() - 1]; - if first.sub(last).norm_sq() <= tolerance_sq { - cleaned.pop(); - } - } - - if cleaned.len() < 3 { - return Err(SweepError::new( - SweepErrorKind::InvalidProfile, - "Sweep profile requires at least 3 distinct points.", - )); - } - - let centroid = cleaned - .iter() - .copied() - .fold(Vec3f::new(0.0, 0.0, 0.0), |sum, point| sum.add(point)) - .scale(1.0 / cleaned.len() as f64); - - let Some(normal) = compute_polygon_normal(&cleaned) else { - return Err(SweepError::new( - SweepErrorKind::InvalidProfile, - "Sweep profile is degenerate and does not define a plane.", - )); - }; - - let tolerance = tolerance_for_vec3_points(&cleaned, PLANAR_TOLERANCE_FACTOR); - for point in &cleaned { - let distance = point.sub(centroid).dot(normal).abs(); - if distance > tolerance { - return Err(SweepError::new( - SweepErrorKind::NonPlanarProfile, - format!( - "Sweep profile must be planar; point deviates from the profile plane by {:.3e}.", - distance - ), - )); - } - } - - let reference_axis = cleaned - .iter() - .enumerate() - .find_map(|(index, point)| { - let next = cleaned[(index + 1) % cleaned.len()]; - let edge = next.sub(*point); - let projected = edge.sub(normal.scale(edge.dot(normal))); - projected.normalized() - }) - .or_else(|| { - cleaned.iter().find_map(|point| { - let delta = point.sub(centroid); - let projected = delta.sub(normal.scale(delta.dot(normal))); - projected.normalized() - }) - }); - - let Some(reference_axis) = reference_axis else { - return Err(SweepError::new( - SweepErrorKind::InvalidProfile, - "Sweep profile collapses to its centroid after planar projection.", - )); - }; - - let v_axis = normal - .cross(reference_axis) - .normalized() - .unwrap_or_else(|| any_orthogonal(normal)); - let u_axis = v_axis.cross(normal).normalized().unwrap_or(reference_axis); - - let mut local_points: Vec = cleaned - .iter() - .map(|point| { - let delta = point.sub(centroid); - Vec2f::new(delta.dot(u_axis), delta.dot(v_axis)) - }) - .collect(); - - let signed_area = signed_area_2d(&local_points); - if signed_area.abs() <= tolerance { - return Err(SweepError::new( - SweepErrorKind::InvalidProfile, - "Sweep profile has near-zero signed area.", - )); - } - - if signed_area < 0.0 { - local_points.reverse(); - local_points.rotate_right(1); - } - - Ok(ProfileData { - local_points, - normal, - u_axis, - scale: point_set_scale_vec3(&cleaned), - }) -} - -fn build_segment_dirs(path: &[Vec3f], is_closed: bool) -> Result, SweepError> { - let segment_count = if is_closed { - path.len() - } else { - path.len() - 1 - }; - let mut segment_dirs = Vec::with_capacity(segment_count); - - for index in 0..segment_count { - let next_index = (index + 1) % path.len(); - let delta = path[next_index].sub(path[index]); - let Some(direction) = delta.normalized() else { - return Err(SweepError::new( - SweepErrorKind::InvalidPath, - format!("Sweep path segment {} is degenerate.", index), - )); - }; - segment_dirs.push(direction); - } - - Ok(segment_dirs) -} - -fn ensure_closed_path_is_planar(path: &[Vec3f]) -> Result<(), SweepError> { - let Some(normal) = compute_polygon_normal(path) else { - return Err(SweepError::new( - SweepErrorKind::NonPlanarClosedPath, - "Closed sweep paths must define a planar loop with non-zero area.", - )); - }; - - let tolerance = tolerance_for_vec3_points(path, PLANAR_TOLERANCE_FACTOR); - let origin = path[0]; - - for point in path { - let distance = point.sub(origin).dot(normal).abs(); - if distance > tolerance { - return Err(SweepError::new( - SweepErrorKind::NonPlanarClosedPath, - format!( - "Closed sweep paths must be planar; point deviates from the loop plane by {:.3e}.", - distance - ), - )); - } - } - - Ok(()) -} - -fn build_section_planes( - path: &[Vec3f], - segment_dirs: &[Vec3f], - is_closed: bool, -) -> Result, SweepError> { - let mut planes = Vec::with_capacity(path.len()); - - if is_closed { - for index in 0..path.len() { - let prev_dir = segment_dirs[(index + path.len() - 1) % path.len()]; - let next_dir = segment_dirs[index]; - let normal = corner_plane_normal(prev_dir, next_dir)?; - planes.push(SectionPlane { - origin: path[index], - normal, - }); - } - - return Ok(planes); - } - - planes.push(SectionPlane { - origin: path[0], - normal: segment_dirs[0], - }); - - for index in 1..(path.len() - 1) { - let normal = corner_plane_normal(segment_dirs[index - 1], segment_dirs[index])?; - planes.push(SectionPlane { - origin: path[index], - normal, - }); - } - - planes.push(SectionPlane { - origin: path[path.len() - 1], - normal: segment_dirs[segment_dirs.len() - 1], - }); - - Ok(planes) -} - -fn build_sections( - path: &[Vec3f], - segment_dirs: &[Vec3f], - section_planes: &[SectionPlane], - profile: &ProfileData, - is_closed: bool, -) -> Result>, SweepError> { - if is_closed { - return build_closed_planar_sections(path, segment_dirs, section_planes, profile); - } - - let mut sections = vec![Vec::new(); path.len()]; - sections[0] = build_initial_section(profile, §ion_planes[0]); - - let projection_tolerance = tolerance_for_scale( - point_set_scale_vec3(path).max(profile.scale), - PLANAR_TOLERANCE_FACTOR, - ); - - for index in 0..(path.len() - 1) { - sections[index + 1] = project_section_to_plane( - §ions[index], - segment_dirs[index], - §ion_planes[index + 1], - projection_tolerance, - )?; - } - - Ok(sections) -} - -fn build_closed_planar_sections( - path: &[Vec3f], - segment_dirs: &[Vec3f], - section_planes: &[SectionPlane], - profile: &ProfileData, -) -> Result>, SweepError> { - let Some(path_normal) = compute_polygon_normal(path) else { - return Err(SweepError::new( - SweepErrorKind::NonPlanarClosedPath, - "Closed sweep paths must define a planar loop with non-zero area.", - )); - }; - - let rotated_hint = rotate_between_normals( - profile.u_axis, - profile.normal, - section_planes[0].normal, - profile.u_axis, - ); - let (axis_u, axis_v) = build_plane_basis(section_planes[0].normal, rotated_hint); - let lateral_axis = - select_closed_path_lateral_axis(axis_u, axis_v, path_normal, section_planes[0].normal); - - let projection_tolerance = tolerance_for_scale( - point_set_scale_vec3(path).max(profile.scale), - PLANAR_TOLERANCE_FACTOR, - ); - - let first_corner_probe = offset_corner_point( - path[0], - segment_dirs[segment_dirs.len() - 1], - segment_dirs[0], - 1.0, - path_normal, - 1.0, - projection_tolerance, - )?; - let lateral_normal_sign = if first_corner_probe.sub(path[0]).dot(lateral_axis) >= 0.0 { - 1.0 - } else { - -1.0 - }; - - let profile_offsets: Vec<(f64, f64)> = profile - .local_points - .iter() - .map(|point| { - let delta = axis_u.scale(point.x).add(axis_v.scale(point.y)); - (delta.dot(lateral_axis), delta.dot(path_normal)) - }) - .collect(); - - let mut sections = Vec::with_capacity(path.len()); - for index in 0..path.len() { - let prev_dir = segment_dirs[(index + path.len() - 1) % path.len()]; - let next_dir = segment_dirs[index]; - let mut section = Vec::with_capacity(profile_offsets.len()); - - for (lateral_offset, depth_offset) in &profile_offsets { - let offset_corner = offset_corner_point( - path[index], - prev_dir, - next_dir, - *lateral_offset, - path_normal, - lateral_normal_sign, - projection_tolerance, - )?; - section.push(offset_corner.add(path_normal.scale(*depth_offset))); - } - - sections.push(section); - } - - Ok(sections) -} - -fn build_initial_section(profile: &ProfileData, plane: &SectionPlane) -> Vec { - let rotated_hint = - rotate_between_normals(profile.u_axis, profile.normal, plane.normal, profile.u_axis); - let (u_axis, v_axis) = build_plane_basis(plane.normal, rotated_hint); - - profile - .local_points - .iter() - .map(|point| { - plane - .origin - .add(u_axis.scale(point.x)) - .add(v_axis.scale(point.y)) - }) - .collect() -} - -fn select_closed_path_lateral_axis( - axis_u: Vec3f, - axis_v: Vec3f, - path_normal: Vec3f, - section_normal: Vec3f, -) -> Vec3f { - let projected_u = axis_u.sub(path_normal.scale(axis_u.dot(path_normal))); - let projected_v = axis_v.sub(path_normal.scale(axis_v.dot(path_normal))); - - if projected_u.norm_sq() >= projected_v.norm_sq() { - if let Some(axis) = projected_u.normalized() { - return axis; - } - } else if let Some(axis) = projected_v.normalized() { - return axis; - } - - path_normal - .cross(section_normal) - .normalized() - .or_else(|| section_normal.cross(path_normal).normalized()) - .unwrap_or_else(|| any_orthogonal(path_normal)) -} - -fn offset_corner_point( - origin: Vec3f, - prev_dir: Vec3f, - next_dir: Vec3f, - offset: f64, - path_normal: Vec3f, - lateral_normal_sign: f64, - tolerance: f64, -) -> Result { - let prev_normal = path_normal.cross(prev_dir).scale(lateral_normal_sign); - let next_normal = path_normal.cross(next_dir).scale(lateral_normal_sign); - let prev_point = origin.add(prev_normal.scale(offset)); - let next_point = origin.add(next_normal.scale(offset)); - let denominator = prev_dir.cross(next_dir).dot(path_normal); - - if denominator.abs() <= tolerance { - return Ok(prev_point.add(next_point).scale(0.5)); - } - - let delta = next_point.sub(prev_point); - let t = delta.cross(next_dir).dot(path_normal) / denominator; - Ok(prev_point.add(prev_dir.scale(t))) -} - -fn project_section_to_plane( - section: &[Vec3f], - direction: Vec3f, - target_plane: &SectionPlane, - tolerance: f64, -) -> Result, SweepError> { - let denominator = target_plane.normal.dot(direction); - if denominator.abs() <= tolerance { - return Err(SweepError::new( - SweepErrorKind::ProjectionFailure, - "Sweep section projection became parallel to its target plane.", - )); - } - - Ok(section - .iter() - .map(|point| { - let t = target_plane.normal.dot(target_plane.origin.sub(*point)) / denominator; - point.add(direction.scale(t)) - }) - .collect()) -} - -fn corner_plane_normal(prev_dir: Vec3f, next_dir: Vec3f) -> Result { - let combined = prev_dir.add(next_dir); - combined.normalized().ok_or_else(|| { - SweepError::new( - SweepErrorKind::PathReversal, - "Sweep path contains a 180-degree reversal, which cannot be mitred robustly.", - ) - }) -} - -fn orient_cap_indices(section: &[Vec3f], indices: Vec, desired_normal: Vec3f) -> Vec { - let actual_normal = compute_polygon_normal(section).unwrap_or(desired_normal); - if actual_normal.dot(desired_normal) >= 0.0 { - indices - } else { - let mut reversed = indices; - reversed.reverse(); - reversed - } -} - -fn orient_side_face_indices( - current_section: &[Vec3f], - next_section: &[Vec3f], - current_indices: &[u32], - next_indices: &[u32], - segment_start: Vec3f, - segment_end: Vec3f, - profile_index: usize, - next_profile: usize, -) -> Vec { - let face_points = [ - current_section[profile_index], - current_section[next_profile], - next_section[next_profile], - next_section[profile_index], - ]; - let mut face_indices = vec![ - current_indices[profile_index], - current_indices[next_profile], - next_indices[next_profile], - next_indices[profile_index], - ]; - - let Some(face_normal) = compute_polygon_normal(&face_points) else { - return face_indices; - }; - - let face_centroid = centroid_vec3f(&face_points); - let segment_midpoint = segment_start.add(segment_end).scale(0.5); - let radial_reference = face_centroid.sub(segment_midpoint); - - if radial_reference.norm_sq() > EPSILON * EPSILON && face_normal.dot(radial_reference) < 0.0 { - face_indices.reverse(); - } - - face_indices -} - -fn remove_consecutive_duplicates(points: &[Vector3]) -> Vec { - let mut cleaned: Vec = Vec::new(); - for point in points { - let candidate = Vec3f::from_vector3(point); - if cleaned - .last() - .map(|last| candidate.sub(*last).norm_sq() <= EPSILON * EPSILON) - .unwrap_or(false) - { - continue; - } - cleaned.push(candidate); - } - cleaned -} - -fn build_plane_basis(normal: Vec3f, hint: Vec3f) -> (Vec3f, Vec3f) { - let projected_hint = hint.sub(normal.scale(hint.dot(normal))); - let u_axis = projected_hint - .normalized() - .unwrap_or_else(|| any_orthogonal(normal)); - let v_axis = normal - .cross(u_axis) - .normalized() - .unwrap_or_else(|| any_orthogonal(normal)); - let u_axis = v_axis.cross(normal).normalized().unwrap_or(u_axis); - (u_axis, v_axis) -} - -fn rotate_between_normals( - vector: Vec3f, - from_normal: Vec3f, - to_normal: Vec3f, - fallback_axis: Vec3f, -) -> Vec3f { - let dot = from_normal.dot(to_normal).clamp(-1.0, 1.0); - if dot >= 1.0 - EPSILON { - return vector; - } - - if dot <= -1.0 + EPSILON { - return rotate_around_axis(vector, fallback_axis, PI); - } - - let axis = from_normal - .cross(to_normal) - .normalized() - .unwrap_or(fallback_axis); - rotate_around_axis(vector, axis, dot.acos()) -} - -fn rotate_around_axis(vector: Vec3f, axis: Vec3f, angle: f64) -> Vec3f { - let axis = axis.normalized().unwrap_or(axis); - let cos_theta = angle.cos(); - let sin_theta = angle.sin(); - - vector - .scale(cos_theta) - .add(axis.cross(vector).scale(sin_theta)) - .add(axis.scale(axis.dot(vector) * (1.0 - cos_theta))) -} - -fn any_orthogonal(direction: Vec3f) -> Vec3f { - let mut reference = Vec3f::new(0.0, 1.0, 0.0); - if direction.dot(reference).abs() > 0.95 { - reference = Vec3f::new(1.0, 0.0, 0.0); - } - - let mut orthogonal = reference.cross(direction); - if orthogonal.norm_sq() <= EPSILON * EPSILON { - orthogonal = Vec3f::new(0.0, 0.0, 1.0).cross(direction); - } - - orthogonal.normalized().unwrap_or(Vec3f::new(1.0, 0.0, 0.0)) -} - -fn compute_polygon_normal(points: &[Vec3f]) -> Option { - if points.len() < 3 { - return None; - } - - let mut accumulated = Vec3f::new(0.0, 0.0, 0.0); - for index in 0..points.len() { - let current = points[index]; - let next = points[(index + 1) % points.len()]; - accumulated = accumulated.add(current.cross(next)); - } - - accumulated.normalized() -} - -fn centroid_vec3f(points: &[Vec3f]) -> Vec3f { - if points.is_empty() { - return Vec3f::new(0.0, 0.0, 0.0); - } - - points - .iter() - .copied() - .fold(Vec3f::new(0.0, 0.0, 0.0), |sum, point| sum.add(point)) - .scale(1.0 / points.len() as f64) -} - -fn signed_area_2d(points: &[Vec2f]) -> f64 { - let mut area = 0.0; - for index in 0..points.len() { - let current = points[index]; - let next = points[(index + 1) % points.len()]; - area += current.x * next.y - next.x * current.y; - } - area * 0.5 -} - -fn point_set_scale_vec3(points: &[Vec3f]) -> f64 { - if points.is_empty() { - return 1.0; - } - - let mut min = points[0]; - let mut max = points[0]; - - for point in &points[1..] { - min.x = min.x.min(point.x); - min.y = min.y.min(point.y); - min.z = min.z.min(point.z); - max.x = max.x.max(point.x); - max.y = max.y.max(point.y); - max.z = max.z.max(point.z); - } - - max.sub(min).norm().max(1.0) -} - -fn tolerance_for_vec3_points(points: &[Vec3f], factor: f64) -> f64 { - tolerance_for_scale(point_set_scale_vec3(points), factor) -} - -fn tolerance_for_scale(scale: f64, factor: f64) -> f64 { - scale.max(1.0) * factor -} - -#[cfg(test)] -mod tests { - use super::{ - build_prepared_sweep, centroid_vec3f, compute_polygon_normal, project_section_to_plane, - sweep_profile_along_path, PreparedSweep, SectionPlane, SweepErrorKind, SweepOptions, Vec3f, - EPSILON, - }; - use crate::brep::Brep; - use openmaths::Vector3; - - fn rectangle_profile(width: f64, depth: f64) -> Vec { - let hw = width * 0.5; - let hd = depth * 0.5; - vec![ - Vector3::new(-hw, 0.0, -hd), - Vector3::new(hw, 0.0, -hd), - Vector3::new(hw, 0.0, hd), - Vector3::new(-hw, 0.0, hd), - ] - } - - fn concave_profile() -> Vec { - vec![ - Vector3::new(-0.6, 0.0, -0.4), - Vector3::new(0.1, 0.0, -0.4), - Vector3::new(0.1, 0.0, -0.1), - Vector3::new(0.45, 0.0, -0.1), - Vector3::new(0.45, 0.0, 0.45), - Vector3::new(-0.6, 0.0, 0.45), - ] - } - - fn assert_edge_lengths(section: &[Vec3f], expected: &[f64]) { - assert_eq!(section.len(), expected.len()); - for index in 0..section.len() { - let actual = section[index].distance(section[(index + 1) % section.len()]); - let error = (actual - expected[index]).abs(); - assert!( - error <= 1.0e-6, - "edge {} length mismatch: actual={:.9} expected={:.9} error={:.3e}", - index, - actual, - expected[index], - error - ); - } - } - - fn assert_section_edges_axis_aligned(section: &[Vec3f]) { - for index in 0..section.len() { - let current = section[index]; - let next = section[(index + 1) % section.len()]; - let delta = next.sub(current); - - assert!( - delta.z.abs() <= 1.0e-6, - "section edge {} drifted off the cross-section plane: dz={:.3e}", - index, - delta.z - ); - - let aligned_x = delta.y.abs() <= 1.0e-6 && delta.x.abs() > 1.0e-6; - let aligned_y = delta.x.abs() <= 1.0e-6 && delta.y.abs() > 1.0e-6; - - assert!( - aligned_x || aligned_y, - "section edge {} is rotated off-axis: dx={:.6}, dy={:.6}", - index, - delta.x, - delta.y - ); - } - } - - fn assert_corner_projection_preserves_rectangle( - prepared: &PreparedSweep, - corner_index: usize, - incoming_segment: usize, - outgoing_segment: usize, - width: f64, - depth: f64, - ) { - let corner_origin = prepared.path[corner_index]; - let incoming_plane = SectionPlane { - origin: corner_origin, - normal: prepared.segment_dirs[incoming_segment], - }; - let outgoing_plane = SectionPlane { - origin: corner_origin, - normal: prepared.segment_dirs[outgoing_segment], - }; - - let incoming_projection = project_section_to_plane( - &prepared.sections[corner_index], - prepared.segment_dirs[incoming_segment], - &incoming_plane, - 1.0e-9, - ) - .expect("incoming corner projection must succeed"); - let outgoing_projection = project_section_to_plane( - &prepared.sections[corner_index], - prepared.segment_dirs[outgoing_segment], - &outgoing_plane, - 1.0e-9, - ) - .expect("outgoing corner projection must succeed"); - - assert_edge_lengths(&incoming_projection, &[width, depth, width, depth]); - assert_edge_lengths(&outgoing_projection, &[width, depth, width, depth]); - } - - fn assert_closed_side_faces_point_outward(path: &[Vector3], profile: &[Vector3]) { - let prepared = build_prepared_sweep(path, profile).expect("prepared sweep should succeed"); - assert!(prepared.is_closed, "test path must be closed"); - - let brep = sweep_profile_along_path(path, profile, SweepOptions::default()) - .expect("closed sweep should succeed"); - brep.validate_topology() - .expect("closed sweep topology should validate"); - - let ring_size = prepared.sections[0].len(); - let side_face_count = prepared.sections.len() * ring_size; - assert_eq!( - brep.faces.len(), - side_face_count, - "closed sweeps should only emit side faces" - ); - - for section_index in 0..prepared.sections.len() { - let next_section = (section_index + 1) % prepared.sections.len(); - let segment_midpoint = prepared.path[section_index] - .add(prepared.path[next_section]) - .scale(0.5); - - for profile_index in 0..ring_size { - let face_id = (section_index * ring_size + profile_index) as u32; - let vertices = brep.get_vertices_by_face_id(face_id); - let face_points: Vec = vertices.iter().map(Vec3f::from_vector3).collect(); - let face_normal = - compute_polygon_normal(&face_points).expect("side face should have a normal"); - let face_centroid = centroid_vec3f(&face_points); - let radial_reference = face_centroid.sub(segment_midpoint); - let alignment = face_normal.dot(radial_reference); - - assert!( - radial_reference.norm_sq() > EPSILON * EPSILON, - "side face {} centroid should not lie on the path midpoint", - face_id - ); - assert!( - alignment > 1.0e-9, - "side face {} points inward: alignment={:.3e}", - face_id, - alignment - ); - } - } - } - - fn unique_sorted_values(values: impl IntoIterator, tolerance: f64) -> Vec { - let mut ordered: Vec = values.into_iter().collect(); - ordered.sort_by(|lhs, rhs| lhs.partial_cmp(rhs).unwrap()); - - let mut unique: Vec = Vec::new(); - for value in ordered { - if unique - .last() - .map(|existing| (value - *existing).abs() <= tolerance) - .unwrap_or(false) - { - continue; - } - unique.push(value); - } - - unique - } - - fn assert_rectangular_frame_dimensions( - brep: &Brep, - expected_outer_width: f64, - expected_inner_width: f64, - expected_outer_height: f64, - expected_inner_height: f64, - ) { - let tolerance = 1.0e-6; - let xs = unique_sorted_values( - brep.vertices.iter().map(|vertex| vertex.position.x), - tolerance, - ); - let ys = unique_sorted_values( - brep.vertices.iter().map(|vertex| vertex.position.y), - tolerance, - ); - - assert_eq!( - xs.len(), - 4, - "window frame should have four distinct x bands" - ); - assert_eq!( - ys.len(), - 4, - "window frame should have four distinct y bands" - ); - - let observed_outer_width = xs[xs.len() - 1] - xs[0]; - let observed_inner_width = xs[xs.len() - 2] - xs[1]; - let observed_outer_height = ys[ys.len() - 1] - ys[0]; - let observed_inner_height = ys[ys.len() - 2] - ys[1]; - - assert!( - (observed_outer_width - expected_outer_width).abs() <= tolerance, - "outer width mismatch: actual={:.9} expected={:.9}", - observed_outer_width, - expected_outer_width - ); - assert!( - (observed_inner_width - expected_inner_width).abs() <= tolerance, - "inner width mismatch: actual={:.9} expected={:.9}", - observed_inner_width, - expected_inner_width - ); - assert!( - (observed_outer_height - expected_outer_height).abs() <= tolerance, - "outer height mismatch: actual={:.9} expected={:.9}", - observed_outer_height, - expected_outer_height - ); - assert!( - (observed_inner_height - expected_inner_height).abs() <= tolerance, - "inner height mismatch: actual={:.9} expected={:.9}", - observed_inner_height, - expected_inner_height - ); - } - - #[test] - fn open_sweep_with_caps_has_expected_topology() { - let path = vec![Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 2.0, 0.0)]; - let profile = rectangle_profile(1.0, 1.0); - - let brep = sweep_profile_along_path(&path, &profile, SweepOptions::default()) - .expect("open sweep should succeed"); - - assert_eq!(brep.vertices.len(), 8); - assert_eq!(brep.faces.len(), 6); - assert_eq!(brep.shells.len(), 1); - assert!(brep.shells[0].is_closed); - assert!(!brep.edges.is_empty()); - } - - #[test] - fn open_sweep_without_caps_only_generates_side_faces() { - let path = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 1.0, 0.0), - Vector3::new(1.0, 2.0, 0.0), - ]; - let profile = rectangle_profile(1.0, 0.5); - - let brep = sweep_profile_along_path( - &path, - &profile, - SweepOptions { - cap_start: false, - cap_end: false, - }, - ) - .expect("open sweep without caps should succeed"); - - assert_eq!(brep.vertices.len(), 12); - assert_eq!(brep.faces.len(), 8); - assert_eq!(brep.shells.len(), 1); - assert!(!brep.shells[0].is_closed); - } - - #[test] - fn closed_path_sweep_does_not_generate_end_caps() { - let path = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 1.0), - Vector3::new(0.0, 0.0, 1.0), - Vector3::new(0.0, 0.0, 0.0), - ]; - let profile = rectangle_profile(0.4, 0.4); - - let brep = sweep_profile_along_path(&path, &profile, SweepOptions::default()) - .expect("closed planar sweep should succeed"); - - assert_eq!(brep.vertices.len(), 16); - assert_eq!(brep.faces.len(), 16); - assert_eq!(brep.shells.len(), 1); - assert!(brep.shells[0].is_closed); - } - - #[test] - fn closed_rectangular_loop_side_faces_point_outward() { - let path = vec![ - Vector3::new(-2.2, 0.0, -1.4), - Vector3::new(2.4, 0.0, -1.4), - Vector3::new(2.4, 0.0, 1.7), - Vector3::new(-2.2, 0.0, 1.7), - Vector3::new(-2.2, 0.0, -1.4), - ]; - let profile = rectangle_profile(0.45, 0.28); - - assert_closed_side_faces_point_outward(&path, &profile); - } - - #[test] - fn closed_trapezoid_loop_side_faces_point_outward() { - let path = vec![ - Vector3::new(-2.1, 0.0, -1.8), - Vector3::new(1.8, 0.0, -1.5), - Vector3::new(1.2, 0.0, 1.4), - Vector3::new(-1.6, 0.0, 1.0), - Vector3::new(-2.1, 0.0, -1.8), - ]; - let profile = rectangle_profile(0.52, 0.24); - - assert_closed_side_faces_point_outward(&path, &profile); - } - - #[test] - fn right_angle_corner_projection_preserves_rectangle_profile() { - let path = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 2.0, 0.0), - Vector3::new(2.0, 2.0, 0.0), - ]; - let profile = rectangle_profile(1.0, 0.5); - - let prepared = - build_prepared_sweep(&path, &profile).expect("prepared sweep should succeed"); - assert_corner_projection_preserves_rectangle(&prepared, 1, 0, 1, 1.0, 0.5); - } - - #[test] - fn right_angle_corner_projection_is_independent_of_segment_length() { - let path = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 4.0, 0.0), - Vector3::new(8.0, 4.0, 0.0), - ]; - let profile = rectangle_profile(1.2, 0.8); - - let prepared = - build_prepared_sweep(&path, &profile).expect("prepared sweep should succeed"); - assert_corner_projection_preserves_rectangle(&prepared, 1, 0, 1, 1.2, 0.8); - } - - #[test] - fn straight_path_keeps_rectangle_edges_axis_aligned() { - let path = vec![Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 3.0)]; - let profile = rectangle_profile(1.0, 0.5); - - let prepared = - build_prepared_sweep(&path, &profile).expect("prepared sweep should succeed"); - - assert_section_edges_axis_aligned(&prepared.sections[0]); - assert_section_edges_axis_aligned(&prepared.sections[1]); - } - - #[test] - fn closed_rectangular_frame_loop_preserves_expected_dimensions() { - let aperture_width = 1.2; - let frame_width = 0.12; - let frame_depth = 0.12; - let aperture_height = 1.0; - let base_elevation = 1.05; - let half_aperture_width = aperture_width * 0.5; - let half_frame_width = frame_width * 0.5; - - let path = vec![ - Vector3::new( - -(half_aperture_width + half_frame_width), - base_elevation - half_frame_width, - 0.0, - ), - Vector3::new( - -(half_aperture_width + half_frame_width), - base_elevation + aperture_height + half_frame_width, - 0.0, - ), - Vector3::new( - half_aperture_width + half_frame_width, - base_elevation + aperture_height + half_frame_width, - 0.0, - ), - Vector3::new( - half_aperture_width + half_frame_width, - base_elevation - half_frame_width, - 0.0, - ), - Vector3::new( - -(half_aperture_width + half_frame_width), - base_elevation - half_frame_width, - 0.0, - ), - ]; - let profile = rectangle_profile(frame_width, frame_depth); - - let brep = sweep_profile_along_path(&path, &profile, SweepOptions::default()) - .expect("closed rectangular frame sweep should succeed"); - brep.validate_topology() - .expect("closed rectangular frame topology should validate"); - - assert_rectangular_frame_dimensions( - &brep, - aperture_width + frame_width * 2.0, - aperture_width, - aperture_height + frame_width * 2.0, - aperture_height, - ); - } - - #[test] - fn larger_closed_rectangular_frame_loop_preserves_expected_dimensions() { - let aperture_width = 1.6; - let frame_width = 0.14; - let frame_depth = 0.2; - let aperture_height = 1.2; - let base_elevation = 1.0; - let half_aperture_width = aperture_width * 0.5; - let half_frame_width = frame_width * 0.5; - - let path = vec![ - Vector3::new( - -(half_aperture_width + half_frame_width), - base_elevation - half_frame_width, - 0.0, - ), - Vector3::new( - -(half_aperture_width + half_frame_width), - base_elevation + aperture_height + half_frame_width, - 0.0, - ), - Vector3::new( - half_aperture_width + half_frame_width, - base_elevation + aperture_height + half_frame_width, - 0.0, - ), - Vector3::new( - half_aperture_width + half_frame_width, - base_elevation - half_frame_width, - 0.0, - ), - Vector3::new( - -(half_aperture_width + half_frame_width), - base_elevation - half_frame_width, - 0.0, - ), - ]; - let profile = rectangle_profile(frame_width, frame_depth); - - let brep = sweep_profile_along_path(&path, &profile, SweepOptions::default()) - .expect("larger closed rectangular frame sweep should succeed"); - brep.validate_topology() - .expect("larger closed rectangular frame topology should validate"); - - assert_rectangular_frame_dimensions( - &brep, - aperture_width + frame_width * 2.0, - aperture_width, - aperture_height + frame_width * 2.0, - aperture_height, - ); - } - - #[test] - fn open_u_frame_path_keeps_requested_widths() { - let panel_width = 1.0; - let frame_width = 0.2; - let frame_depth = 0.3; - let opening_height = 2.1; - let half_panel_width = panel_width * 0.5; - let half_frame_width = frame_width * 0.5; - - let path = vec![ - Vector3::new(-(half_panel_width + half_frame_width), 0.0, 0.0), - Vector3::new( - -(half_panel_width + half_frame_width), - opening_height + half_frame_width, - 0.0, - ), - Vector3::new( - half_panel_width + half_frame_width, - opening_height + half_frame_width, - 0.0, - ), - Vector3::new(half_panel_width + half_frame_width, 0.0, 0.0), - ]; - let profile = rectangle_profile(frame_width, frame_depth); - - let brep = sweep_profile_along_path(&path, &profile, SweepOptions::default()) - .expect("open open frame sweep should succeed"); - brep.validate_topology() - .expect("open open frame topology should validate"); - - let tolerance = 1.0e-6; - let xs = unique_sorted_values( - brep.vertices.iter().map(|vertex| vertex.position.x), - tolerance, - ); - let ys = unique_sorted_values( - brep.vertices.iter().map(|vertex| vertex.position.y), - tolerance, - ); - - assert_eq!(xs.len(), 4, "open frame should preserve four x bands"); - assert_eq!(ys.len(), 3, "open frame should preserve three y bands"); - assert!( - ((xs[xs.len() - 1] - xs[0]) - (panel_width + frame_width * 2.0)).abs() <= tolerance, - "open frame outer width changed unexpectedly" - ); - assert!( - ((xs[xs.len() - 2] - xs[1]) - panel_width).abs() <= tolerance, - "open frame inner width changed unexpectedly" - ); - assert!( - ((ys[ys.len() - 1] - ys[0]) - (opening_height + frame_width)).abs() <= tolerance, - "open frame outer height changed unexpectedly" - ); - } - - #[test] - fn concave_profile_multibend_sweep_has_non_degenerate_faces() { - let path = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 1.2, 0.0), - Vector3::new(1.3, 1.2, 0.0), - Vector3::new(1.3, 2.0, 0.8), - Vector3::new(2.1, 2.0, 0.8), - ]; - - let brep = sweep_profile_along_path(&path, &concave_profile(), SweepOptions::default()) - .expect("concave sweep should succeed"); - brep.validate_topology() - .expect("concave sweep topology should validate"); - - for face in &brep.faces { - let vertices = brep.get_vertices_by_face_id(face.id); - let face_points: Vec = vertices.iter().map(Vec3f::from_vector3).collect(); - assert!( - compute_polygon_normal(&face_points).is_some(), - "face {} should not be degenerate", - face.id - ); - } - } - - #[test] - fn non_planar_closed_path_is_rejected() { - let path = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(1.0, 1.0, 1.0), - Vector3::new(0.0, 1.0, 0.0), - Vector3::new(0.0, 0.0, 0.0), - ]; - - let error = match sweep_profile_along_path( - &path, - &rectangle_profile(0.4, 0.4), - SweepOptions::default(), - ) { - Ok(_) => panic!("non-planar closed path should fail"), - Err(error) => error, - }; - assert_eq!(error.kind(), SweepErrorKind::NonPlanarClosedPath); - } - - #[test] - fn one_eighty_degree_reversal_is_rejected() { - let path = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 1.0, 0.0), - Vector3::new(0.0, -1.0, 0.0), - ]; - - let error = match sweep_profile_along_path( - &path, - &rectangle_profile(0.4, 0.4), - SweepOptions::default(), - ) { - Ok(_) => panic!("180-degree reversal should fail"), - Err(error) => error, - }; - assert_eq!(error.kind(), SweepErrorKind::PathReversal); - } -} diff --git a/main/opengeometry/src/operations/triangulate.rs b/main/opengeometry/src/operations/triangulate.rs deleted file mode 100644 index 637f34c..0000000 --- a/main/opengeometry/src/operations/triangulate.rs +++ /dev/null @@ -1,144 +0,0 @@ -use openmaths::Vector3; - -pub fn triangulate_polygon_with_holes( - face_vertices: &Vec, - holes: &Vec>, -) -> Vec<[usize; 3]> { - if face_vertices.len() < 3 { - return Vec::new(); - } - - // --- 1. Projection to 2D --- - // First, determine the best 2D plane to project onto by finding the - // dominant axis of the face's normal. - let normal = compute_polygon_normal(face_vertices).unwrap_or(Vector3::new(0.0, 0.0, 1.0)); - - let (axis_u, axis_v) = if normal.z.abs() > normal.x.abs() && normal.z.abs() > normal.y.abs() { - // Project to XY plane - (0, 1) // Corresponds to (x, y) - } else if normal.x.abs() > normal.y.abs() { - // Project to YZ plane - (1, 2) // Corresponds to (y, z) - } else { - // Project to XZ plane - (0, 2) // Corresponds to (x, z) - }; - - // --- 2. Flatten Data for earcutr --- - // earcutr needs a flat list of 2D coordinates and a list of indices - // where the holes begin. - - let mut vertices_2d = Vec::with_capacity( - (face_vertices.len() + holes.iter().map(|h| h.len()).sum::()) * 2, - ); - let mut hole_indices = Vec::with_capacity(holes.len()); - - // Add outer loop vertices - for v in face_vertices { - let coord_u = match axis_u { - 0 => v.x, - 1 => v.y, - 2 => v.z, - _ => unreachable!("Invalid axis_u"), - }; - let coord_v = match axis_v { - 0 => v.x, - 1 => v.y, - 2 => v.z, - _ => unreachable!("Invalid axis_v"), - }; - vertices_2d.push(coord_u); - vertices_2d.push(coord_v); - } - - // Add hole vertices - if !holes.is_empty() { - let mut current_index = face_vertices.len(); - for hole in holes { - hole_indices.push(current_index); - for v in hole { - let coord_u = match axis_u { - 0 => v.x, - 1 => v.y, - 2 => v.z, - _ => unreachable!("Invalid axis_u"), - }; - let coord_v = match axis_v { - 0 => v.x, - 1 => v.y, - 2 => v.z, - _ => unreachable!("Invalid axis_v"), - }; - vertices_2d.push(coord_u); - vertices_2d.push(coord_v); - current_index += 1; - } - } - } - - // --- 3. Run Earcut Algorithm --- - let triangle_indices = earcutr::earcut(&vertices_2d, &hole_indices, 2); - - // --- 4. Reshape the Result --- - // The result is a flat list of indices. Group them into triangles. - let triangle_indices: Vec<[usize; 3]> = triangle_indices - .chunks_exact(3) - .map(|chunk| [chunk[0], chunk[1], chunk[2]]) - .collect(); - - let all_vertices: Vec = face_vertices - .iter() - .copied() - .chain(holes.iter().flatten().copied()) - .collect(); - - triangle_indices - .into_iter() - .filter_map(|mut tri| { - let a = *all_vertices.get(tri[0])?; - let b = *all_vertices.get(tri[1])?; - let c = *all_vertices.get(tri[2])?; - - let ab = Vector3::new(b.x - a.x, b.y - a.y, b.z - a.z); - let ac = Vector3::new(c.x - a.x, c.y - a.y, c.z - a.z); - let triangle_normal = ab.cross(&ac); - - if triangle_normal.dot(&normal) < 0.0 { - tri.swap(1, 2); - } - - Some(tri) - }) - .collect() -} - -/** - * Calculates the average normal of a polygon. - * This is used to determine the best projection plane. - */ -pub(crate) fn compute_polygon_normal(vertices: &[Vector3]) -> Option { - if vertices.len() < 3 { - return None; - } - - let mut normal = Vector3::new(0.0, 0.0, 0.0); - for i in 0..vertices.len() { - let p1 = &vertices[i]; - let p2 = &vertices[(i + 1) % vertices.len()]; - normal.x += (p1.y - p2.y) * (p1.z + p2.z); - normal.y += (p1.z - p2.z) * (p1.x + p2.x); - normal.z += (p1.x - p2.x) * (p1.y + p2.y); - } - - let length_sq = normal.dot(&normal); - if length_sq <= 1.0e-18 { - return None; - } - - let inv_length = length_sq.sqrt().recip(); - Some(Vector3::new( - normal.x * inv_length, - normal.y * inv_length, - normal.z * inv_length, - )) -} diff --git a/main/opengeometry/src/operations/windingsort.rs b/main/opengeometry/src/operations/windingsort.rs deleted file mode 100644 index dfe8ccd..0000000 --- a/main/opengeometry/src/operations/windingsort.rs +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Code for sorting the winding order of a given vertices - */ -use openmaths::Vector3; - -fn compute_signed_area(points: &[Vector3]) -> f64 { - let n = points.len(); - let mut sum = 0.0; - for i in 0..n { - let p1 = &points[i]; - let p2 = &points[(i + 1) % n]; - sum += p1.x * p2.z - p2.x * p1.z; - } - sum / 2.0 -} - -pub fn ccw_test(raw_points: Vec) -> Vec { - let mut points = raw_points; - let area = compute_signed_area(&points); - if area < 0.0 { - points.reverse(); - } - points -} - -pub fn is_ccw_need(raw_points: Vec) -> CcwAndFlag { - let mut points = raw_points; - let area = compute_signed_area(&points); - if area < 0.0 { - points.reverse(); - CcwAndFlag { - ccw: points, - flag: true, - } - } else { - CcwAndFlag { - ccw: points, - flag: false, - } - } -} - -pub struct CcwAndFlag { - pub ccw: Vec, - pub flag: bool, -} - -#[allow(non_camel_case_types)] -pub type ccw_and_flag = CcwAndFlag; diff --git a/main/opengeometry/src/primitives/arc.rs b/main/opengeometry/src/primitives/arc.rs deleted file mode 100644 index be22821..0000000 --- a/main/opengeometry/src/primitives/arc.rs +++ /dev/null @@ -1,233 +0,0 @@ -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; - -use crate::brep::{Brep, BrepBuilder}; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::spatial::placement::Placement3D; -use openmaths::Vector3; -use uuid::Uuid; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGArc { - id: String, - center: Vector3, - radius: f64, - start_angle: f64, - end_angle: f64, - segments: u32, - placement: Placement3D, - brep: Brep, -} - -#[wasm_bindgen] -impl OGArc { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGArc { - let internal_id = Uuid::new_v4(); - - OGArc { - id, - center: Vector3::new(0.0, 0.0, 0.0), - radius: 1.0, - start_angle: 0.0, - end_angle: 2.0 * std::f64::consts::PI, - segments: 32, - placement: Placement3D::new(), - brep: Brep::new(internal_id), - } - } - - #[wasm_bindgen] - pub fn set_config( - &mut self, - center: Vector3, - radius: f64, - start_angle: f64, - end_angle: f64, - segments: u32, - ) -> Result<(), JsValue> { - self.center = center; - self.radius = radius; - self.start_angle = start_angle; - self.end_angle = end_angle; - self.segments = segments.max(1); - self.placement.set_anchor(self.center); - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn set_center(&mut self, center: Vector3) { - self.center = center; - self.placement.set_anchor(self.center); - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - let segment_count = self.segments.max(1); - let angle_step = (self.end_angle - self.start_angle) / segment_count as f64; - - let mut points = Vec::with_capacity((segment_count + 1) as usize); - let mut angle = self.start_angle; - for _ in 0..=segment_count { - let x = self.radius * angle.cos(); - let y = 0.0; - let z = self.radius * angle.sin(); - points.push(Vector3::new(x, y, z)); - angle += angle_step; - } - - let is_closed = - (self.end_angle - self.start_angle).abs() >= 2.0 * std::f64::consts::PI - 1.0e-9; - - if is_closed && points.len() > 2 { - let first = points[0]; - let last = *points.last().unwrap(); - let dx = first.x - last.x; - let dy = first.y - last.y; - let dz = first.z - last.z; - if dx * dx + dy * dy + dz * dz <= 1.0e-12 { - points.pop(); - } - } - - let mut builder = BrepBuilder::new(self.brep.id); - builder.add_vertices(&points); - - if points.len() >= 2 { - let indices: Vec = (0..points.len() as u32).collect(); - builder - .add_wire(&indices, is_closed && points.len() > 2) - .map_err(|err| JsValue::from_str(&format!("Failed to build arc wire: {}", err)))?; - } - - self.brep = builder - .build() - .map_err(|err| JsValue::from_str(&format!("Failed to finalize arc BREP: {}", err)))?; - - // D1: preserve the exact circle the (tessellated) wire approximates. - // Stored in local space (centred at the origin, in the XZ plane); the - // placement anchor/transform carries it to world. - let circle = crate::brep::CurveGeometry::Circle { - center: Vector3::new(0.0, 0.0, 0.0), - normal: Vector3::new(0.0, 1.0, 0.0), - x_axis: Vector3::new(1.0, 0.0, 0.0), - radius: self.radius, - start_angle: self.start_angle, - end_angle: self.end_angle, - }; - for edge in &mut self.brep.edges { - edge.curve = Some(circle.clone()); - } - - Ok(()) - } - - #[wasm_bindgen] - pub fn dispose_points(&mut self) { - self.brep.clear(); - } - - #[wasm_bindgen] - pub fn destroy(&mut self) { - self.brep.clear(); - self.id.clear(); - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - serde_json::to_string(&wire_geometry_buffer(&self.world_brep())).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&wire_geometry_buffer(&self.brep)).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - wire_geometry_buffer(&self.world_brep()) - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - wire_geometry_buffer(&self.brep) - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } -} - -impl OGArc { - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let world_brep = self.world_brep(); - project_brep_to_scene(&world_brep, camera, hlr) - } -} - -fn wire_geometry_buffer(brep: &Brep) -> Vec { - let Some(wire) = brep.wires.first() else { - return Vec::new(); - }; - - brep.get_wire_vertex_buffer(wire.id, wire.is_closed) -} diff --git a/main/opengeometry/src/primitives/box_boundary.rs b/main/opengeometry/src/primitives/box_boundary.rs new file mode 100644 index 0000000..996738c --- /dev/null +++ b/main/opengeometry/src/primitives/box_boundary.rs @@ -0,0 +1,143 @@ +use crate::brep::{ + padded_uv_bounds, plane_boundary, Accuracy, Builder, CurveGeometry, Frame3, GeometryError, + Orientation, Use, +}; +use crate::math::{scale, sub, Interval, Point3}; + +pub(crate) struct BoxCorners { + points: Vec, + pub(crate) vertices: Vec, + edges: [[u32; 3]; 8], +} + +pub(crate) struct BoxFaceBoundary { + pub(crate) frame: Frame3, + pub(crate) uses: Vec, + pub(crate) bounds: [[f64; 2]; 2], +} + +pub(crate) struct BoxFaceLayout { + pub(crate) key: &'static str, + pub(crate) indices: [usize; 4], + pub(crate) normal: Point3, +} + +pub(crate) fn add_box_corners( + builder: &mut Builder, + frame: Frame3, + size: [f64; 3], + axes: [Point3; 3], +) -> Result { + let points: Vec<_> = (0..8) + .map(|index| { + frame.point(std::array::from_fn(|coordinate| { + if index & (1 << coordinate) == 0 { + 0.0 + } else { + size[coordinate] + } + })) + }) + .collect(); + let vertices = points + .iter() + .map(|point| builder.vertex(*point)) + .collect::>(); + let mut edges = [[u32::MAX; 3]; 8]; + for index in 0..8 { + for coordinate in 0..3 { + if index & (1 << coordinate) == 0 { + edges[index][coordinate] = builder.edge( + CurveGeometry::Line { + origin: points[index], + direction: axes[coordinate], + }, + Interval::new(0.0, size[coordinate])?, + false, + ); + } + } + } + Ok(BoxCorners { + points, + vertices, + edges, + }) +} + +pub(crate) fn box_face_layout(frame: Frame3) -> [BoxFaceLayout; 6] { + [ + BoxFaceLayout { + key: "bottom", + indices: [0, 2, 3, 1], + normal: scale(frame.z, -1.0), + }, + BoxFaceLayout { + key: "top", + indices: [4, 5, 7, 6], + normal: frame.z, + }, + BoxFaceLayout { + key: "left", + indices: [0, 4, 6, 2], + normal: scale(frame.x, -1.0), + }, + BoxFaceLayout { + key: "right", + indices: [1, 3, 7, 5], + normal: frame.x, + }, + BoxFaceLayout { + key: "front", + indices: [0, 1, 5, 4], + normal: scale(frame.y, -1.0), + }, + BoxFaceLayout { + key: "back", + indices: [2, 6, 7, 3], + normal: frame.y, + }, + ] +} + +pub(crate) fn box_face_boundary( + builder: &Builder, + corners: &BoxCorners, + indices: [usize; 4], + normal: Point3, + accuracy: Accuracy, + vertex: impl Fn(usize) -> u32, +) -> Result { + let face_frame = Frame3::from_axis( + corners.points[indices[0]], + normal, + sub(corners.points[indices[1]], corners.points[indices[0]]), + )?; + let mut uses = Vec::with_capacity(4); + for index in 0..4 { + let from = indices[index]; + let to = indices[(index + 1) % 4]; + let coordinate = (from ^ to).trailing_zeros() as usize; + uses.push(plane_boundary( + builder, + face_frame, + corners.edges[from.min(to)][coordinate], + vertex(from), + vertex(to), + if from < to { + Orientation::Forward + } else { + Orientation::Reverse + }, + )?); + } + let bounds = padded_uv_bounds( + indices.map(|from| face_frame.local(corners.points[from])), + accuracy.geometric, + ); + Ok(BoxFaceBoundary { + frame: face_frame, + uses, + bounds, + }) +} diff --git a/main/opengeometry/src/primitives/circle.rs b/main/opengeometry/src/primitives/circle.rs new file mode 100644 index 0000000..01c1b5e --- /dev/null +++ b/main/opengeometry/src/primitives/circle.rs @@ -0,0 +1,80 @@ +use crate::brep::{ + Accuracy, BrepEnvelope, Builder, CurveGeometry, GeometryError, HalfEdge, HalfEdgeGeometryUse, + Orientation, Wire, +}; +use crate::math::Interval; + +pub fn arc_wire( + id: String, + curve: CurveGeometry, + start_angle: f64, + sweep_angle: f64, + accuracy: Accuracy, +) -> Result { + curve.validate()?; + accuracy.validate()?; + let radius = match &curve { + CurveGeometry::Circle { radius, .. } => *radius, + CurveGeometry::Ellipse { minor_radius, .. } => *minor_radius, + _ => { + return Err(GeometryError::UnsupportedGeometry( + "arc wire requires a circle or ellipse".into(), + )) + } + }; + if !start_angle.is_finite() + || !sweep_angle.is_finite() + || sweep_angle == 0.0 + || sweep_angle.abs() > std::f64::consts::TAU + { + return Err(GeometryError::InvalidGeometry( + "arc angles require a finite, nonzero sweep of at most one turn".into(), + )); + } + let end = start_angle + sweep_angle; + let range = Interval::new(start_angle.min(end), start_angle.max(end))?; + if range.width() * radius <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "arc span is below geometric resolution".into(), + )); + } + let mut builder = Builder::new(id, accuracy)?; + let closed = sweep_angle.abs() == std::f64::consts::TAU; + let from = builder.vertex(curve.elementary_point(start_angle)?); + let to = if closed { + from + } else { + builder.vertex(curve.elementary_point(end)?) + }; + let edge = builder.edge(curve, range, false); + builder.brep.topology.edges[edge as usize].halfedge = 0; + builder.brep.topology.vertices[from as usize].outgoing_halfedge = Some(0); + builder.brep.topology.halfedges.push(HalfEdge { + id: 0, + from, + to, + twin: None, + next: closed.then_some(0), + prev: closed.then_some(0), + edge, + face: None, + loop_ref: None, + wire_ref: Some(0), + geometry_use: HalfEdgeGeometryUse { + sense: if sweep_angle > 0.0 { + Orientation::Forward + } else { + Orientation::Reverse + }, + pcurve: None, + periodic_lift: [0; 2], + }, + }); + builder.brep.topology.wires.push(Wire { + id: 0, + start_halfedge: 0, + is_closed: closed, + }); + builder.brep.validate()?; + Ok(builder.brep) +} diff --git a/main/opengeometry/src/primitives/cuboid.rs b/main/opengeometry/src/primitives/cuboid.rs index 3d4439b..6ac8f7e 100644 --- a/main/opengeometry/src/primitives/cuboid.rs +++ b/main/opengeometry/src/primitives/cuboid.rs @@ -1,335 +1,50 @@ -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; - -use crate::brep::{Brep, BrepBuilder}; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::geometry::geometrybuffer::GeometryBuffer; -use crate::spatial::placement::Placement3D; - -use openmaths::Vector3; -use uuid::Uuid; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGCuboid { +use super::box_boundary::{ + add_box_corners, box_face_boundary, box_face_layout, BoxCorners, BoxFaceLayout, +}; +use crate::brep::{ + dimensions, Accuracy, BrepEnvelope, Builder, Frame3, GeometryError, SurfaceGeometry, +}; + +pub fn cuboid( id: String, - center: Vector3, - width: f64, - height: f64, - depth: f64, - placement: Placement3D, - brep: Brep, - geometry_buffer: GeometryBuffer, + frame: Frame3, + size: [f64; 3], + accuracy: Accuracy, +) -> Result { + frame.validate()?; + dimensions(&size)?; + if size.iter().any(|s| *s <= 4.0 * accuracy.geometric) { + return Err(GeometryError::UnresolvedIntersection( + "cuboid dimensions are below geometric resolution".into(), + )); + } + let mut builder = Builder::new(id, accuracy)?; + let corners = add_box_corners(&mut builder, frame, size, [frame.x, frame.y, frame.z])?; + add_cuboid_faces(&mut builder, frame, &corners, accuracy)?; + builder.finish_solid() } -#[wasm_bindgen] -impl OGCuboid { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGCuboid { - let internal_id = Uuid::new_v4(); - - OGCuboid { - id, - center: Vector3::new(0.0, 0.0, 0.0), - width: 1.0, - height: 1.0, - depth: 1.0, - placement: Placement3D::new(), - brep: Brep::new(internal_id), - geometry_buffer: GeometryBuffer::new(internal_id), - } - } - - #[wasm_bindgen] - pub fn set_config( - &mut self, - center: Vector3, - width: f64, - height: f64, - depth: f64, - ) -> Result<(), JsValue> { - self.center = center; - self.width = width; - self.height = height; - self.depth = depth; - - self.placement.set_anchor(self.center); - self.build_local_brep()?; - self.build_geometry(); - Ok(()) - } - - #[wasm_bindgen] - pub fn set_center(&mut self, center: Vector3) { - self.center = center; - self.placement.set_anchor(self.center); - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - self.build_local_brep()?; - self.build_geometry(); - Ok(()) - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - serde_json::to_string(&self.world_geometry_buffer().vertices).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&self.geometry_buffer.vertices).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - self.world_geometry_buffer().vertices - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - self.geometry_buffer.vertices.clone() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_serialized(&self) -> String { - serde_json::to_string(&self.world_geometry_buffer().outline_vertices).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_serialized(&self) -> String { - serde_json::to_string(&self.geometry_buffer.outline_vertices).unwrap() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_buffer(&self) -> Vec { - self.world_geometry_buffer().outline_vertices - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_buffer(&self) -> Vec { - self.geometry_buffer.outline_vertices.clone() - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } - - // TODO: add dispose method to clean up resources -} - -impl OGCuboid { - fn build_local_brep(&mut self) -> Result<(), JsValue> { - let half_width = self.width * 0.5; - let half_height = self.height * 0.5; - let half_depth = self.depth * 0.5; - - let vertices = vec![ - Vector3::new(-half_width, -half_height, -half_depth), - Vector3::new(half_width, -half_height, -half_depth), - Vector3::new(half_width, -half_height, half_depth), - Vector3::new(-half_width, -half_height, half_depth), - Vector3::new(-half_width, half_height, -half_depth), - Vector3::new(half_width, half_height, -half_depth), - Vector3::new(half_width, half_height, half_depth), - Vector3::new(-half_width, half_height, half_depth), - ]; - - let faces = vec![ - vec![0, 1, 2, 3], - vec![4, 7, 6, 5], - vec![0, 4, 5, 1], - vec![3, 2, 6, 7], - vec![0, 3, 7, 4], - vec![1, 5, 6, 2], - ]; - - let mut builder = BrepBuilder::new(self.brep.id); - builder.add_vertices(&vertices); - - for face in &faces { - builder.add_face(face, &[]).map_err(|error| { - JsValue::from_str(&format!("Failed to build cuboid face: {}", error)) - })?; - } - - builder.add_shell_from_all_faces(true).map_err(|error| { - JsValue::from_str(&format!("Failed to build cuboid shell: {}", error)) +fn add_cuboid_faces( + builder: &mut Builder, + frame: Frame3, + corners: &BoxCorners, + accuracy: Accuracy, +) -> Result<(), GeometryError> { + for BoxFaceLayout { + key, + indices, + normal, + } in box_face_layout(frame) + { + let face = box_face_boundary(builder, corners, indices, normal, accuracy, |from| { + from as u32 })?; - - self.brep = builder.build().map_err(|error| { - JsValue::from_str(&format!("Failed to finalize cuboid BREP: {}", error)) - })?; - Ok(()) - } - - /** - * Builds the geometry buffer - */ - fn build_geometry(&mut self) { - self.geometry_buffer.vertices = self.brep.get_triangle_vertex_buffer(); - self.geometry_buffer.outline_vertices = self.brep.get_outline_vertex_buffer(); - } - - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } - - pub fn world_geometry_buffer(&self) -> GeometryBuffer { - self.geometry_buffer.transformed(&self.placement) - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - project_brep_to_scene(&self.world_brep(), camera, hlr) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn assert_close(actual: f64, expected: f64) { - let delta = (actual - expected).abs(); - assert!( - delta <= 1.0e-9, - "expected {expected}, got {actual}, delta {delta}" - ); - } - - #[test] - fn placement_world_getters_transform_geometry_and_leave_local_buffers_unchanged() { - let mut cuboid = OGCuboid::new("placement-cuboid".to_string()); - cuboid - .set_config(Vector3::new(10.0, 20.0, 30.0), 2.0, 4.0, 6.0) - .expect("cuboid config"); - - let local_geometry = cuboid.get_local_geometry_serialized(); - let local_outline = cuboid.get_local_outline_geometry_serialized(); - let local_brep = cuboid.get_local_brep_serialized(); - - cuboid - .set_transform( - Vector3::new(1.0, -2.0, 3.0), - Vector3::new(0.25, 0.5, -0.35), - Vector3::new(1.5, 1.5, 1.5), - ) - .expect("uniform positive scale should be accepted"); - - assert_eq!(local_brep, cuboid.get_local_brep_serialized()); - assert_eq!(local_geometry, cuboid.get_local_geometry_serialized()); - assert_eq!( - local_outline, - cuboid.get_local_outline_geometry_serialized() - ); - - let expected_world_geometry = cuboid.geometry_buffer.transformed(&cuboid.placement); - - let world_geometry: Vec = - serde_json::from_str(&cuboid.get_geometry_serialized()).expect("world geometry"); - let world_outline: Vec = - serde_json::from_str(&cuboid.get_outline_geometry_serialized()).expect("world outline"); - - assert_eq!(world_geometry.len(), expected_world_geometry.vertices.len()); - for (actual, expected) in world_geometry - .iter() - .zip(expected_world_geometry.vertices.iter()) - { - assert_close(*actual, *expected); - } - - assert_eq!( - world_outline.len(), - expected_world_geometry.outline_vertices.len() - ); - for (actual, expected) in world_outline - .iter() - .zip(expected_world_geometry.outline_vertices.iter()) - { - assert_close(*actual, *expected); - } - - let world_brep: Brep = - serde_json::from_str(&cuboid.get_brep_serialized()).expect("world brep"); - let world_center = world_brep.bounds_center().expect("world bounds"); - assert_close(world_center.x, 11.0); - assert_close(world_center.y, 18.0); - assert_close(world_center.z, 33.0); - } - - #[test] - fn placement_rejects_mirrored_and_non_uniform_scale() { - let mut cuboid = OGCuboid::new("placement-rejection-cuboid".to_string()); - cuboid - .set_config(Vector3::new(0.0, 0.0, 0.0), 1.0, 1.0, 1.0) - .expect("cuboid config"); - - let mirrored = cuboid.placement.set_transform( - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(-1.0, -1.0, -1.0), - ); - assert!(mirrored.is_err()); - - let non_uniform = cuboid.placement.set_transform( - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 1.2, 1.0), - ); - assert!(non_uniform.is_err()); - } + builder.face( + key, + SurfaceGeometry::Plane { frame: face.frame }, + face.bounds, + face.uses, + )?; + } + Ok(()) } diff --git a/main/opengeometry/src/primitives/curve.rs b/main/opengeometry/src/primitives/curve.rs deleted file mode 100644 index 8e38458..0000000 --- a/main/opengeometry/src/primitives/curve.rs +++ /dev/null @@ -1,282 +0,0 @@ -/** - * Copyright (c) 2025, OpenGeometry. All rights reserved. - * Curve Primitive for OpenGeometry. - */ -use crate::brep::{Brep, BrepBuilder}; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::operations::offset::{offset_path, OffsetOptions, OffsetResult}; -use crate::spatial::placement::{ - bounds_center_from_points, points_relative_to_anchor, transform_points_with_placement, - Placement3D, -}; -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGCurve { - id: String, - control_points: Vec, - placement: Placement3D, - brep: Brep, - anchor_initialized: bool, -} - -#[wasm_bindgen] -impl OGCurve { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGCurve { - OGCurve { - id, - control_points: Vec::new(), - placement: Placement3D::new(), - brep: Brep::new(Uuid::new_v4()), - anchor_initialized: false, - } - } - - #[wasm_bindgen] - pub fn set_config(&mut self, control_points: Vec) -> Result<(), JsValue> { - self.control_points = control_points; - self.ensure_anchor_initialized(); - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - if self.control_points.is_empty() { - self.brep.clear(); - return Ok(()); - } - - self.brep.clear(); - - let local_points = self.local_points(); - let mut builder = BrepBuilder::new(self.brep.id); - builder.add_vertices(&local_points); - - if local_points.len() >= 2 { - let indices: Vec = (0..local_points.len() as u32).collect(); - builder.add_wire(&indices, false).map_err(|err| { - JsValue::from_str(&format!("Failed to build curve wire: {}", err)) - })?; - } - - self.brep = builder - .build() - .map_err(|err| JsValue::from_str(&format!("Failed to finalize curve BREP: {}", err)))?; - - Ok(()) - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - serde_json::to_string(&wire_geometry_buffer(&self.world_brep())).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&wire_geometry_buffer(&self.brep)).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - wire_geometry_buffer(&self.world_brep()) - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - wire_geometry_buffer(&self.brep) - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } - - #[wasm_bindgen] - pub fn set_anchor(&mut self, anchor: Vector3) -> Result<(), JsValue> { - self.placement.set_anchor(anchor); - self.anchor_initialized = true; - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn reset_anchor(&mut self) -> Result<(), JsValue> { - self.recompute_anchor_from_bounds(); - self.anchor_initialized = true; - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn get_offset_serialized( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> String { - let result = self.get_offset_result(distance, acute_threshold_degrees, bevel); - serde_json::to_string(&result).unwrap() - } - - #[wasm_bindgen] - pub fn dispose(&mut self) { - self.brep.clear(); - self.control_points.clear(); - } -} - -impl OGCurve { - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } - - pub fn get_raw_points(&self) -> Vec { - self.control_points.clone() - } - - pub fn get_offset_result( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> OffsetResult { - let options = OffsetOptions { - bevel, - acute_threshold_degrees, - }; - let points = transform_points_with_placement(&self.control_points, &self.placement); - offset_path(&points, distance, Some(false), options) - } - - pub fn get_offset_points( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> Vec { - self.get_offset_result(distance, acute_threshold_degrees, bevel) - .points - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let world_brep = self.world_brep(); - project_brep_to_scene(&world_brep, camera, hlr) - } - - fn local_points(&self) -> Vec { - points_relative_to_anchor(&self.control_points, self.placement.anchor) - } - - fn recompute_anchor_from_bounds(&mut self) { - let anchor = - bounds_center_from_points(&self.control_points).unwrap_or(Vector3::new(0.0, 0.0, 0.0)); - self.placement.set_anchor(anchor); - } - - fn ensure_anchor_initialized(&mut self) { - if self.anchor_initialized { - return; - } - self.recompute_anchor_from_bounds(); - self.anchor_initialized = true; - } -} - -fn wire_geometry_buffer(brep: &Brep) -> Vec { - let Some(wire) = brep.wires.first() else { - return brep - .vertices - .iter() - .flat_map(|vertex| [vertex.position.x, vertex.position.y, vertex.position.z]) - .collect(); - }; - - brep.get_wire_vertex_buffer(wire.id, false) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn anchor_stays_stable_across_curve_config_updates_until_reset() { - let mut curve = OGCurve::new("curve-anchor".to_string()); - curve - .set_config(vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(10.0, 0.0, 0.0), - ]) - .expect("curve config"); - let initial_anchor = curve.get_anchor(); - assert_eq!(initial_anchor.x, 5.0); - - curve - .set_config(vec![ - Vector3::new(10.0, 0.0, 0.0), - Vector3::new(20.0, 0.0, 0.0), - ]) - .expect("curve config update"); - let anchor_after_update = curve.get_anchor(); - assert_eq!(anchor_after_update.x, 5.0); - - curve.reset_anchor().expect("reset anchor"); - let anchor_after_reset = curve.get_anchor(); - assert_eq!(anchor_after_reset.x, 15.0); - } -} diff --git a/main/opengeometry/src/primitives/cylinder.rs b/main/opengeometry/src/primitives/cylinder.rs index ff5932d..e8871be 100644 --- a/main/opengeometry/src/primitives/cylinder.rs +++ b/main/opengeometry/src/primitives/cylinder.rs @@ -1,308 +1,24 @@ -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; +use crate::brep::{dimensions, Accuracy, BrepEnvelope, Frame3, GeometryError, SurfaceGeometry}; +use crate::primitives::revolved_section::{section, SectionExtent}; -use crate::brep::{Brep, BrepBuilder}; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::operations::extrude::extrude_brep_face; -use crate::spatial::placement::Placement3D; -use openmaths::Vector3; -use uuid::Uuid; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGCylinder { +pub fn cylinder( id: String, - center: Vector3, + frame: Frame3, radius: f64, height: f64, - angle: f64, - segments: u32, - placement: Placement3D, - brep: Brep, -} - -#[wasm_bindgen] -impl OGCylinder { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGCylinder { - let internal_id = Uuid::new_v4(); - - OGCylinder { - id, - center: Vector3::new(0.0, 0.0, 0.0), - radius: 1.0, - height: 1.0, - angle: 2.0 * std::f64::consts::PI, - segments: 32, - placement: Placement3D::new(), - brep: Brep::new(internal_id), - } - } - - #[wasm_bindgen] - pub fn set_config( - &mut self, - center: Vector3, - radius: f64, - height: f64, - angle: f64, - segments: u32, - ) -> Result<(), JsValue> { - self.center = center; - self.radius = radius.max(1.0e-6); - self.height = height; - self.angle = angle; - self.segments = segments.max(3); - self.placement.set_anchor(self.center); - - self.generate_brep() - } - - #[wasm_bindgen] - pub fn set_center(&mut self, center: Vector3) { - self.center = center; - self.placement.set_anchor(self.center); - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - pub fn generate_brep(&mut self) -> Result<(), JsValue> { - self.clean_geometry(); - self.generate_geometry() - } - - pub fn clean_geometry(&mut self) { - self.brep.clear(); - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - let half_height = self.height / 2.0; - let mut base_points = Vec::new(); - let full_circle = self.angle >= 2.0 * std::f64::consts::PI - 1.0e-9; - - if !full_circle { - base_points.push(Vector3::new(0.0, -half_height, 0.0)); - } - - let segment_count = self.segments.max(3); - let steps = if full_circle { - segment_count - } else { - segment_count + 1 - }; - let angle_step = if full_circle { - self.angle / segment_count as f64 - } else { - self.angle / (steps - 1) as f64 - }; - - let mut angle: f64 = 0.0; - for _ in 0..steps { - let x = self.radius * angle.cos(); - let y = -half_height; - let z = self.radius * angle.sin(); - base_points.push(Vector3::new(x, y, z)); - angle += angle_step; - } - - if full_circle && base_points.len() > 3 { - let first = base_points[0]; - let last = *base_points.last().unwrap(); - let dx = first.x - last.x; - let dy = first.y - last.y; - let dz = first.z - last.z; - if dx * dx + dy * dy + dz * dz <= 1.0e-12 { - base_points.pop(); - } - } - - let mut base_builder = BrepBuilder::new(Uuid::new_v4()); - base_builder.add_vertices(&base_points); - let base_loop_indices: Vec = (0..base_points.len() as u32).collect(); - base_builder - .add_face(&base_loop_indices, &[]) - .map_err(|err| { - JsValue::from_str(&format!("Failed to build cylinder base face: {}", err)) - })?; - - let base_brep = base_builder.build().map_err(|err| { - JsValue::from_str(&format!("Failed to finalize cylinder base: {}", err)) - })?; - - self.brep = extrude_brep_face(base_brep, self.height); - self.tag_analytic_geometry(full_circle); - - Ok(()) - } - - /// D1: attach exact analytic surfaces/curves to the tessellated cylinder so - /// inspection and STEP export (D9) can recover a single cylindrical surface - /// and circular edges instead of a facet fan. Local space: axis = +Y, base - /// centred at `(0, -h/2, 0)`. Only tagged for a full revolution; partial - /// wedges have planar radial faces and keep the default interpretation. - fn tag_analytic_geometry(&mut self, full_circle: bool) { - if !full_circle { - return; - } - use crate::brep::{CurveGeometry, SurfaceGeometry}; - let half_height = self.height / 2.0; - let radius = self.radius; - - let cylinder = SurfaceGeometry::Cylinder { - origin: Vector3::new(0.0, -half_height, 0.0), - axis: Vector3::new(0.0, 1.0, 0.0), - ref_direction: Vector3::new(1.0, 0.0, 0.0), - radius, - height: self.height, - }; - - for face in &mut self.brep.faces { - if face.normal.y.abs() > 0.9 { - let y = if face.normal.y >= 0.0 { - half_height - } else { - -half_height - }; - face.surface = Some(SurfaceGeometry::Plane { - origin: Vector3::new(0.0, y, 0.0), - normal: face.normal, - }); - } else { - face.surface = Some(cylinder.clone()); - } - } - - let on_ring = |p: &Vector3| -> bool { - ((p.x * p.x + p.z * p.z).sqrt() - radius).abs() < radius * 1.0e-6 + 1.0e-9 - }; - for index in 0..self.brep.edges.len() { - let he = self.brep.edges[index].halfedge as usize; - let (from, to) = { - let h = &self.brep.halfedges[he]; - (h.from as usize, h.to as usize) - }; - let a = self.brep.vertices[from].position; - let b = self.brep.vertices[to].position; - if on_ring(&a) && on_ring(&b) && (a.y - b.y).abs() < 1.0e-9 { - self.brep.edges[index].curve = Some(CurveGeometry::Circle { - center: Vector3::new(0.0, a.y, 0.0), - normal: Vector3::new(0.0, 1.0, 0.0), - x_axis: Vector3::new(1.0, 0.0, 0.0), - radius, - start_angle: 0.0, - end_angle: 2.0 * std::f64::consts::PI, - }); - } - } - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - let world_brep = self.world_brep(); - serde_json::to_string(&world_brep.get_triangle_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&self.brep.get_triangle_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - self.world_brep().get_triangle_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - self.brep.get_triangle_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_serialized(&self) -> String { - let world_brep = self.world_brep(); - serde_json::to_string(&world_brep.get_outline_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_serialized(&self) -> String { - serde_json::to_string(&self.brep.get_outline_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_buffer(&self) -> Vec { - self.world_brep().get_outline_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_buffer(&self) -> Vec { - self.brep.get_outline_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } -} - -impl OGCylinder { - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let world_brep = self.world_brep(); - project_brep_to_scene(&world_brep, camera, hlr) - } + accuracy: Accuracy, +) -> Result { + dimensions(&[radius, height])?; + section( + id, + frame, + SurfaceGeometry::Cylinder { frame, radius }, + &SectionExtent { + r0: radius, + r1: radius, + z0: 0.0, + z1: height, + }, + accuracy, + ) } diff --git a/main/opengeometry/src/primitives/elliptical_arc.rs b/main/opengeometry/src/primitives/elliptical_arc.rs deleted file mode 100644 index 52bd112..0000000 --- a/main/opengeometry/src/primitives/elliptical_arc.rs +++ /dev/null @@ -1,226 +0,0 @@ -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; - -use crate::brep::{Brep, BrepBuilder}; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::spatial::placement::Placement3D; -use openmaths::Vector3; -use uuid::Uuid; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGEllipticalArc { - id: String, - center: Vector3, - radius_x: f64, - radius_z: f64, - start_angle: f64, - end_angle: f64, - segments: u32, - placement: Placement3D, - brep: Brep, -} - -#[wasm_bindgen] -impl OGEllipticalArc { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGEllipticalArc { - let internal_id = Uuid::new_v4(); - - OGEllipticalArc { - id, - center: Vector3::new(0.0, 0.0, 0.0), - radius_x: 1.0, - radius_z: 0.5, - start_angle: 0.0, - end_angle: 2.0 * std::f64::consts::PI, - segments: 32, - placement: Placement3D::new(), - brep: Brep::new(internal_id), - } - } - - #[wasm_bindgen] - pub fn set_config( - &mut self, - center: Vector3, - radius_x: f64, - radius_z: f64, - start_angle: f64, - end_angle: f64, - segments: u32, - ) -> Result<(), JsValue> { - self.center = center; - self.radius_x = radius_x.max(1.0e-6); - self.radius_z = radius_z.max(1.0e-6); - self.start_angle = start_angle; - self.end_angle = end_angle; - self.segments = segments.max(3); - self.placement.set_anchor(self.center); - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn set_center(&mut self, center: Vector3) { - self.center = center; - self.placement.set_anchor(self.center); - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - let segment_count = self.segments.max(3); - let angle_step = (self.end_angle - self.start_angle) / segment_count as f64; - - let mut points = Vec::with_capacity((segment_count + 1) as usize); - let mut angle = self.start_angle; - for _ in 0..=segment_count { - // Key difference from OGArc: radius_x on X axis, radius_z on Z axis - let x = self.radius_x * angle.cos(); - let y = 0.0; - let z = self.radius_z * angle.sin(); - points.push(Vector3::new(x, y, z)); - angle += angle_step; - } - - let is_closed = - (self.end_angle - self.start_angle).abs() >= 2.0 * std::f64::consts::PI - 1.0e-9; - - // Deduplicate first and last point when closed - if is_closed && points.len() > 2 { - let first = points[0]; - let last = *points.last().unwrap(); - let dx = first.x - last.x; - let dy = first.y - last.y; - let dz = first.z - last.z; - if dx * dx + dy * dy + dz * dz <= 1.0e-12 { - points.pop(); - } - } - - let mut builder = BrepBuilder::new(self.brep.id); - builder.add_vertices(&points); - - if points.len() >= 2 { - let indices: Vec = (0..points.len() as u32).collect(); - builder - .add_wire(&indices, is_closed && points.len() > 2) - .map_err(|err| { - JsValue::from_str(&format!("Failed to build elliptical arc wire: {}", err)) - })?; - } - - self.brep = builder.build().map_err(|err| { - JsValue::from_str(&format!("Failed to finalize elliptical arc BREP: {}", err)) - })?; - - Ok(()) - } - - #[wasm_bindgen] - pub fn dispose_points(&mut self) { - self.brep.clear(); - } - - #[wasm_bindgen] - pub fn destroy(&mut self) { - self.brep.clear(); - self.id.clear(); - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - serde_json::to_string(&wire_geometry_buffer(&self.world_brep())).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&wire_geometry_buffer(&self.brep)).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - wire_geometry_buffer(&self.world_brep()) - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - wire_geometry_buffer(&self.brep) - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } -} - -impl OGEllipticalArc { - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let world_brep = self.world_brep(); - project_brep_to_scene(&world_brep, camera, hlr) - } -} - -fn wire_geometry_buffer(brep: &Brep) -> Vec { - let Some(wire) = brep.wires.first() else { - return Vec::new(); - }; - - brep.get_wire_vertex_buffer(wire.id, wire.is_closed) -} diff --git a/main/opengeometry/src/primitives/internal/annular_cylinder.rs b/main/opengeometry/src/primitives/internal/annular_cylinder.rs new file mode 100644 index 0000000..e0b524c --- /dev/null +++ b/main/opengeometry/src/primitives/internal/annular_cylinder.rs @@ -0,0 +1,21 @@ +use super::cylinder_with_circular_hole::cylinder_with_circular_hole; +use crate::brep::{Accuracy, BrepEnvelope, Frame3, GeometryError}; + +pub fn annular_cylinder( + id: String, + frame: Frame3, + inner_radius: f64, + outer_radius: f64, + height: f64, + accuracy: Accuracy, +) -> Result { + cylinder_with_circular_hole( + id, + frame, + frame, + inner_radius, + outer_radius, + height, + accuracy, + ) +} diff --git a/main/opengeometry/src/primitives/internal/arc_edged_extrusion.rs b/main/opengeometry/src/primitives/internal/arc_edged_extrusion.rs new file mode 100644 index 0000000..b9faef3 --- /dev/null +++ b/main/opengeometry/src/primitives/internal/arc_edged_extrusion.rs @@ -0,0 +1,525 @@ +use super::extruded_loop::{add_plane_side, ExtrudedLoop, SideFace}; +use crate::brep::{ + boundary, dimensions, plane_boundary, unit, uv_line, Accuracy, BrepEnvelope, Builder, + CurveGeometry, Frame3, GeometryError, Orientation, SurfaceGeometry, Use, +}; +use crate::math::{add, norm, scale, Interval, Point3}; +use crate::primitives::profile::{ + arc_profile_contains, profile_edge_intersections, validate_arc_profile_loop, ProfileEdge, +}; + +struct EdgeCurves { + bottom: CurveGeometry, + top: CurveGeometry, + range: Interval, +} + +struct OrientedSide { + face: SideFace, + forward: bool, + bottom_sense: Orientation, + top_sense: Orientation, +} + +pub fn arc_edged_extrusion( + id: String, + frame: Frame3, + outer: Vec, + height: f64, + accuracy: Accuracy, +) -> Result { + arc_edged_extrusion_with_holes(id, frame, outer, Vec::new(), height, accuracy) +} + +pub fn arc_edged_extrusion_with_holes( + id: String, + frame: Frame3, + mut outer: Vec, + mut holes: Vec>, + height: f64, + accuracy: Accuracy, +) -> Result { + frame.validate()?; + accuracy.validate()?; + dimensions(&[height])?; + let g = accuracy.geometric; + if height <= 4.0 * g { + return Err(GeometryError::UnresolvedIntersection( + "arc-edged extrusion height is below geometric resolution".into(), + )); + } + if validate_arc_profile_loop(&outer, g)? < 0.0 { + outer = outer.iter().rev().map(ProfileEdge::reversed).collect(); + } + for (index, hole) in holes.iter_mut().enumerate() { + if validate_arc_profile_loop(hole, g)? > 0.0 { + *hole = hole.iter().rev().map(ProfileEdge::reversed).collect(); + } + for hole_edge in hole.iter() { + for outer_edge in &outer { + if !profile_edge_intersections(hole_edge, outer_edge, g).is_empty() { + return Err(GeometryError::InvalidGeometry(format!( + "arc-edged profile hole {index} touches its outer boundary" + ))); + } + } + } + if !arc_profile_contains(&outer, hole[0].endpoints().0, g) { + return Err(GeometryError::InvalidGeometry(format!( + "arc-edged profile hole {index} lies outside the outer boundary" + ))); + } + } + check_hole_pairs(&holes, g)?; + + let mut builder = Builder::new(id, accuracy)?; + let profiles = std::iter::once(outer).chain(holes).collect::>(); + let loops = add_arc_extrusion_loops(&mut builder, frame, &profiles, height)?; + + let bounds = cap_bounds(&profiles[0], g); + let top_frame = Frame3 { + origin: frame.point([0.0, 0.0, height]), + ..frame + }; + let bottom_frame = Frame3 { + y: scale(frame.y, -1.0), + z: scale(frame.z, -1.0), + ..frame + }; + add_cap_faces( + &mut builder, + &profiles, + &loops, + top_frame, + bottom_frame, + bounds, + )?; + + add_side_faces(&mut builder, &profiles, &loops, frame, height, g)?; + builder.finish_solid() +} + +fn check_hole_pairs(holes: &[Vec], g: f64) -> Result<(), GeometryError> { + for first in 0..holes.len() { + for second in (first + 1)..holes.len() { + if holes[first].iter().any(|a| { + holes[second] + .iter() + .any(|b| !profile_edge_intersections(a, b, g).is_empty()) + }) || arc_profile_contains(&holes[first], holes[second][0].endpoints().0, g) + || arc_profile_contains(&holes[second], holes[first][0].endpoints().0, g) + { + return Err(GeometryError::InvalidGeometry(format!( + "arc-edged profile holes {first} and {second} overlap or nest" + ))); + } + } + } + Ok(()) +} + +fn add_arc_extrusion_loops( + builder: &mut Builder, + frame: Frame3, + profiles: &[Vec], + height: f64, +) -> Result, GeometryError> { + let mut loops = Vec::with_capacity(profiles.len()); + for edges in profiles { + let points = edges + .iter() + .map(|edge| edge.endpoints().0) + .collect::>(); + let bottom_vertices = points + .iter() + .map(|point| builder.vertex(frame.point([point[0], point[1], 0.0]))) + .collect::>(); + let top_vertices = points + .iter() + .map(|point| builder.vertex(frame.point([point[0], point[1], height]))) + .collect::>(); + let mut bottom_edges = Vec::with_capacity(edges.len()); + let mut top_edges = Vec::with_capacity(edges.len()); + let mut vertical_edges = Vec::with_capacity(edges.len()); + for (index, edge) in edges.iter().enumerate() { + let from = builder.brep.topology.vertices[bottom_vertices[index] as usize].position; + let top_from = builder.brep.topology.vertices[top_vertices[index] as usize].position; + let EdgeCurves { bottom, top, range } = + profile_edge_curves(edge, frame, from, top_from, height)?; + bottom_edges.push(builder.edge(bottom, range, false)); + top_edges.push(builder.edge(top, range, false)); + vertical_edges.push(builder.edge( + CurveGeometry::Line { + origin: from, + direction: frame.z, + }, + Interval::new(0.0, height)?, + false, + )); + } + loops.push(ExtrudedLoop { + bottom_vertices, + top_vertices, + bottom_edges, + top_edges, + vertical_edges, + }); + } + Ok(loops) +} + +fn profile_edge_curves( + edge: &ProfileEdge, + frame: Frame3, + from: Point3, + top_from: Point3, + height: f64, +) -> Result { + let curves = match edge { + ProfileEdge::Line { from: p, to: q } => { + let vector = frame.vector([q[0] - p[0], q[1] - p[1], 0.0]); + let length = norm(vector); + let direction = unit(vector)?; + EdgeCurves { + bottom: CurveGeometry::Line { + origin: from, + direction, + }, + top: CurveGeometry::Line { + origin: top_from, + direction, + }, + range: Interval::new(0.0, length)?, + } + } + ProfileEdge::Arc { + center, + radius, + start_angle, + sweep_angle, + } => { + let origin = frame.point([center[0], center[1], 0.0]); + let circle_frame = Frame3 { origin, ..frame }; + let top_circle_frame = Frame3 { + origin: add(origin, scale(frame.z, height)), + ..frame + }; + let end_angle = start_angle + sweep_angle; + EdgeCurves { + bottom: CurveGeometry::Circle { + frame: circle_frame, + radius: *radius, + }, + top: CurveGeometry::Circle { + frame: top_circle_frame, + radius: *radius, + }, + range: Interval::new(start_angle.min(end_angle), start_angle.max(end_angle))?, + } + } + }; + Ok(curves) +} + +fn cap_bounds(edges: &[ProfileEdge], g: f64) -> [[f64; 2]; 2] { + let mut bounds = [[f64::INFINITY, f64::NEG_INFINITY]; 2]; + for edge in edges { + match edge { + ProfileEdge::Line { from, to } => { + for point in [from, to] { + for axis in 0..2 { + bounds[axis][0] = bounds[axis][0].min(point[axis]); + bounds[axis][1] = bounds[axis][1].max(point[axis]); + } + } + } + ProfileEdge::Arc { center, radius, .. } => { + for axis in 0..2 { + bounds[axis][0] = bounds[axis][0].min(center[axis] - radius); + bounds[axis][1] = bounds[axis][1].max(center[axis] + radius); + } + } + } + } + for bound in &mut bounds { + bound[0] -= g; + bound[1] += g; + } + bounds +} + +fn add_cap_faces( + builder: &mut Builder, + profiles: &[Vec], + loops: &[ExtrudedLoop], + top_frame: Frame3, + bottom_frame: Frame3, + bounds: [[f64; 2]; 2], +) -> Result<(), GeometryError> { + let top_outer = cap_uses( + builder, + &profiles[0], + &loops[0], + true, + top_frame, + bottom_frame, + )?; + let top_holes = profiles[1..] + .iter() + .zip(&loops[1..]) + .map(|(edges, profile)| cap_uses(builder, edges, profile, true, top_frame, bottom_frame)) + .collect::, _>>()?; + let bottom_outer = cap_uses( + builder, + &profiles[0], + &loops[0], + false, + top_frame, + bottom_frame, + )?; + let bottom_holes = profiles[1..] + .iter() + .zip(&loops[1..]) + .map(|(edges, profile)| cap_uses(builder, edges, profile, false, top_frame, bottom_frame)) + .collect::, _>>()?; + builder.face_with_holes( + "top", + SurfaceGeometry::Plane { frame: top_frame }, + bounds, + top_outer, + top_holes, + )?; + builder.face_with_holes( + "bottom", + SurfaceGeometry::Plane { + frame: bottom_frame, + }, + [bounds[0], [-bounds[1][1], -bounds[1][0]]], + bottom_outer, + bottom_holes, + ) +} + +fn cap_uses( + builder: &Builder, + edges: &[ProfileEdge], + profile: &ExtrudedLoop, + top: bool, + top_frame: Frame3, + bottom_frame: Frame3, +) -> Result, GeometryError> { + let count = edges.len(); + let mut uses = Vec::with_capacity(count); + for index in 0..count { + let next = (index + 1) % count; + let forward = !matches!(edges[index], ProfileEdge::Arc { sweep_angle, .. } + if sweep_angle < 0.0); + let top_sense = if forward { + Orientation::Forward + } else { + Orientation::Reverse + }; + let bottom_sense = if forward { + Orientation::Reverse + } else { + Orientation::Forward + }; + uses.push(if top { + plane_boundary( + builder, + top_frame, + profile.top_edges[index], + profile.top_vertices[index], + profile.top_vertices[next], + top_sense, + )? + } else { + plane_boundary( + builder, + bottom_frame, + profile.bottom_edges[index], + profile.bottom_vertices[next], + profile.bottom_vertices[index], + bottom_sense, + )? + }); + } + if !top { + uses.reverse(); + } + Ok(uses) +} + +fn add_side_faces( + builder: &mut Builder, + profiles: &[Vec], + loops: &[ExtrudedLoop], + frame: Frame3, + height: f64, + g: f64, +) -> Result<(), GeometryError> { + for (loop_index, (edges, profile)) in profiles.iter().zip(loops).enumerate() { + for (index, edge) in edges.iter().enumerate() { + let next = (index + 1) % edges.len(); + let bottom_from = profile.bottom_vertices[index]; + let bottom_to = profile.bottom_vertices[next]; + let top_from = profile.top_vertices[index]; + let top_to = profile.top_vertices[next]; + let forward = + !matches!(edge, ProfileEdge::Arc { sweep_angle, .. } if *sweep_angle < 0.0); + let bottom_sense = if forward { + Orientation::Forward + } else { + Orientation::Reverse + }; + let top_sense = if forward { + Orientation::Reverse + } else { + Orientation::Forward + }; + let side = OrientedSide { + face: SideFace { + frame, + height, + g, + loop_index, + index, + next, + bottom_from, + bottom_to, + top_from, + top_to, + }, + forward, + bottom_sense, + top_sense, + }; + match edge { + ProfileEdge::Line { .. } => { + add_plane_side(builder, profile, &side.face)?; + } + ProfileEdge::Arc { + center, + radius, + start_angle, + sweep_angle, + } => { + add_cylinder_side( + builder, + profile, + &side, + *center, + *radius, + *start_angle, + *sweep_angle, + )?; + } + } + } + } + Ok(()) +} + +fn add_cylinder_side( + builder: &mut Builder, + profile: &ExtrudedLoop, + side: &OrientedSide, + center: [f64; 2], + radius: f64, + start_angle: f64, + sweep_angle: f64, +) -> Result<(), GeometryError> { + let &OrientedSide { + face: + SideFace { + frame, + height, + g, + loop_index, + index, + .. + }, + forward, + .. + } = side; + let end_angle = start_angle + sweep_angle; + let circle_frame = Frame3 { + origin: frame.point([center[0], center[1], 0.0]), + ..frame + }; + let uses = cylinder_side_uses(profile, side, start_angle, end_angle); + let face_id = builder.brep.topology.faces.len(); + builder.face( + &format!("side-{loop_index}-{index}"), + SurfaceGeometry::Cylinder { + frame: circle_frame, + radius, + }, + [ + [ + start_angle.min(end_angle) - g / radius, + start_angle.max(end_angle) + g / radius, + ], + [-g, height + g], + ], + uses, + )?; + if !forward { + builder.brep.topology.faces[face_id].sense = Orientation::Reverse; + } + Ok(()) +} + +fn cylinder_side_uses( + profile: &ExtrudedLoop, + side: &OrientedSide, + start_angle: f64, + end_angle: f64, +) -> Vec { + let &OrientedSide { + face: + SideFace { + height, + index, + next, + bottom_from, + bottom_to, + top_from, + top_to, + .. + }, + bottom_sense, + top_sense, + .. + } = side; + vec![ + boundary( + profile.bottom_edges[index], + bottom_from, + bottom_to, + bottom_sense, + uv_line([0.0, 0.0], [1.0, 0.0]), + ), + boundary( + profile.vertical_edges[next], + bottom_to, + top_to, + Orientation::Forward, + uv_line([end_angle, 0.0], [0.0, 1.0]), + ), + boundary( + profile.top_edges[index], + top_to, + top_from, + top_sense, + uv_line([0.0, height], [1.0, 0.0]), + ), + boundary( + profile.vertical_edges[index], + top_from, + bottom_from, + Orientation::Reverse, + uv_line([start_angle, 0.0], [0.0, 1.0]), + ), + ] +} diff --git a/main/opengeometry/src/primitives/internal/cone.rs b/main/opengeometry/src/primitives/internal/cone.rs new file mode 100644 index 0000000..b03c9a2 --- /dev/null +++ b/main/opengeometry/src/primitives/internal/cone.rs @@ -0,0 +1,32 @@ +use crate::brep::{dimensions, Accuracy, BrepEnvelope, Frame3, GeometryError, SurfaceGeometry}; +use crate::math::scale; +use crate::primitives::revolved_section::{section, SectionExtent}; + +pub fn cone( + id: String, + frame: Frame3, + radius: f64, + height: f64, + accuracy: Accuracy, +) -> Result { + dimensions(&[radius, height])?; + let semi_angle = radius.atan2(height); + let frame = Frame3 { + origin: frame.point([0.0, 0.0, height]), + x: frame.x, + y: scale(frame.y, -1.0), + z: scale(frame.z, -1.0), + }; + section( + id, + frame, + SurfaceGeometry::Cone { frame, semi_angle }, + &SectionExtent { + r0: 0.0, + r1: radius, + z0: 0.0, + z1: height, + }, + accuracy, + ) +} diff --git a/main/opengeometry/src/primitives/internal/cylinder_sector.rs b/main/opengeometry/src/primitives/internal/cylinder_sector.rs new file mode 100644 index 0000000..31116a2 --- /dev/null +++ b/main/opengeometry/src/primitives/internal/cylinder_sector.rs @@ -0,0 +1,336 @@ +use crate::brep::{ + boundary, dimensions, plane_boundary, uv_line, Accuracy, BrepEnvelope, Builder, CurveGeometry, + Frame3, GeometryError, Orientation, SurfaceGeometry, +}; +use crate::math::{scale, Interval, Point3}; +use crate::primitives::cylinder::cylinder; +use Orientation::{Forward as F, Reverse as R}; + +struct SectorTopology { + bottom_center: u32, + bottom_start: u32, + bottom_end: u32, + top_center: u32, + top_start: u32, + top_end: u32, + bottom_start_edge: u32, + bottom_end_edge: u32, + top_start_edge: u32, + top_end_edge: u32, + center_edge: u32, + start_edge: u32, + end_edge: u32, + bottom_arc: u32, + top_arc: u32, + top_frame: Frame3, +} + +pub fn cylinder_sector( + id: String, + frame: Frame3, + radius: f64, + height: f64, + start_angle: f64, + sweep_angle: f64, + accuracy: Accuracy, +) -> Result { + frame.validate()?; + accuracy.validate()?; + dimensions(&[radius, height])?; + if !start_angle.is_finite() + || !sweep_angle.is_finite() + || sweep_angle == 0.0 + || sweep_angle.abs() > std::f64::consts::TAU + { + return Err(GeometryError::InvalidGeometry( + "cylinder-sector angles require a finite, nonzero sweep of at most one turn".into(), + )); + } + if (sweep_angle.abs() - std::f64::consts::TAU).abs() <= 64.0 * f64::EPSILON { + return cylinder(id, frame, radius, height, accuracy); + } + if radius * sweep_angle.abs() <= 4.0 * accuracy.geometric + || radius <= 4.0 * accuracy.geometric + || height <= 4.0 * accuracy.geometric + { + return Err(GeometryError::InvalidGeometry( + "cylinder-sector features are below geometric resolution".into(), + )); + } + + let end_angle = start_angle + sweep_angle; + let lo = start_angle.min(end_angle); + let hi = start_angle.max(end_angle); + let range = Interval::new(lo, hi)?; + let mut b = Builder::new(id, accuracy)?; + let topology = add_sector_topology(&mut b, frame, radius, height, lo, hi, range)?; + add_lateral_face(&mut b, &topology, frame, radius, height, lo, hi)?; + add_sector_caps(&mut b, &topology, frame, radius)?; + add_radial_faces(&mut b, &topology, frame, radius, height, lo, hi)?; + b.finish_solid() +} + +fn add_sector_topology( + b: &mut Builder, + frame: Frame3, + radius: f64, + height: f64, + lo: f64, + hi: f64, + range: Interval, +) -> Result { + let point = |angle: f64, elevation: f64| { + frame.point([radius * angle.cos(), radius * angle.sin(), elevation]) + }; + let bottom_center = b.vertex(frame.origin); + let bottom_start = b.vertex(point(lo, 0.0)); + let bottom_end = b.vertex(point(hi, 0.0)); + let top_center = b.vertex(frame.point([0.0, 0.0, height])); + let top_start = b.vertex(point(lo, height)); + let top_end = b.vertex(point(hi, height)); + + let line_edge = |builder: &mut Builder, + origin: Point3, + direction: Point3, + length: f64| + -> Result { + Ok(builder.edge( + CurveGeometry::Line { origin, direction }, + Interval::new(0.0, length)?, + false, + )) + }; + let bottom_start_edge = line_edge(b, frame.origin, radial(frame, lo), radius)?; + let bottom_end_edge = line_edge(b, frame.origin, radial(frame, hi), radius)?; + let top_origin = frame.point([0.0, 0.0, height]); + let top_start_edge = line_edge(b, top_origin, radial(frame, lo), radius)?; + let top_end_edge = line_edge(b, top_origin, radial(frame, hi), radius)?; + let center_edge = line_edge(b, frame.origin, frame.z, height)?; + let start_edge = line_edge(b, point(lo, 0.0), frame.z, height)?; + let end_edge = line_edge(b, point(hi, 0.0), frame.z, height)?; + let bottom_arc = b.edge(CurveGeometry::Circle { frame, radius }, range, false); + let mut top_frame = frame; + top_frame.origin = top_origin; + let top_arc = b.edge( + CurveGeometry::Circle { + frame: top_frame, + radius, + }, + range, + false, + ); + Ok(SectorTopology { + bottom_center, + bottom_start, + bottom_end, + top_center, + top_start, + top_end, + bottom_start_edge, + bottom_end_edge, + top_start_edge, + top_end_edge, + center_edge, + start_edge, + end_edge, + bottom_arc, + top_arc, + top_frame, + }) +} + +fn radial(frame: Frame3, angle: f64) -> Point3 { + frame.vector([angle.cos(), angle.sin(), 0.0]) +} + +fn add_lateral_face( + b: &mut Builder, + topology: &SectorTopology, + frame: Frame3, + radius: f64, + height: f64, + lo: f64, + hi: f64, +) -> Result<(), GeometryError> { + let &SectorTopology { + bottom_start, + bottom_end, + top_start, + top_end, + start_edge, + end_edge, + bottom_arc, + top_arc, + .. + } = topology; + b.face( + "lateral", + SurfaceGeometry::Cylinder { frame, radius }, + [[lo, hi], [0.0, height]], + vec![ + boundary( + bottom_arc, + bottom_start, + bottom_end, + F, + uv_line([0.0, 0.0], [1.0, 0.0]), + ), + boundary( + end_edge, + bottom_end, + top_end, + F, + uv_line([hi, 0.0], [0.0, 1.0]), + ), + boundary( + top_arc, + top_end, + top_start, + R, + uv_line([0.0, height], [1.0, 0.0]), + ), + boundary( + start_edge, + top_start, + bottom_start, + R, + uv_line([lo, 0.0], [0.0, 1.0]), + ), + ], + ) +} + +fn add_sector_caps( + b: &mut Builder, + topology: &SectorTopology, + frame: Frame3, + radius: f64, +) -> Result<(), GeometryError> { + let &SectorTopology { + bottom_center, + bottom_start, + bottom_end, + top_center, + top_start, + top_end, + bottom_start_edge, + bottom_end_edge, + top_start_edge, + top_end_edge, + bottom_arc, + top_arc, + top_frame, + .. + } = topology; + let bottom_frame = Frame3 { + origin: frame.origin, + x: frame.x, + y: scale(frame.y, -1.0), + z: scale(frame.z, -1.0), + }; + let bottom_uses = [ + (bottom_end_edge, bottom_center, bottom_end, F), + (bottom_arc, bottom_end, bottom_start, R), + (bottom_start_edge, bottom_start, bottom_center, R), + ] + .into_iter() + .map(|(edge, from, to, sense)| plane_boundary(b, bottom_frame, edge, from, to, sense)) + .collect::, _>>()?; + b.face( + "lower_cap", + SurfaceGeometry::Plane { + frame: bottom_frame, + }, + [[-radius, radius]; 2], + bottom_uses, + )?; + + let top_uses = [ + (top_start_edge, top_center, top_start, F), + (top_arc, top_start, top_end, F), + (top_end_edge, top_end, top_center, R), + ] + .into_iter() + .map(|(edge, from, to, sense)| plane_boundary(b, top_frame, edge, from, to, sense)) + .collect::, _>>()?; + b.face( + "upper_cap", + SurfaceGeometry::Plane { frame: top_frame }, + [[-radius, radius]; 2], + top_uses, + ) +} + +fn add_radial_faces( + b: &mut Builder, + topology: &SectorTopology, + frame: Frame3, + radius: f64, + height: f64, + lo: f64, + hi: f64, +) -> Result<(), GeometryError> { + let &SectorTopology { + bottom_center, + bottom_start, + bottom_end, + top_center, + top_start, + top_end, + bottom_start_edge, + bottom_end_edge, + top_start_edge, + top_end_edge, + center_edge, + start_edge, + end_edge, + .. + } = topology; + let start_frame = Frame3 { + origin: frame.origin, + x: radial(frame, lo), + y: frame.z, + z: scale(tangent(frame, lo), -1.0), + }; + let start_uses = [ + (bottom_start_edge, bottom_center, bottom_start, F), + (start_edge, bottom_start, top_start, F), + (top_start_edge, top_start, top_center, R), + (center_edge, top_center, bottom_center, R), + ] + .into_iter() + .map(|(edge, from, to, sense)| plane_boundary(b, start_frame, edge, from, to, sense)) + .collect::, _>>()?; + b.face( + "start_radial", + SurfaceGeometry::Plane { frame: start_frame }, + [[0.0, radius], [0.0, height]], + start_uses, + )?; + + let end_frame = Frame3 { + origin: frame.origin, + x: frame.z, + y: radial(frame, hi), + z: tangent(frame, hi), + }; + let end_uses = [ + (center_edge, bottom_center, top_center, F), + (top_end_edge, top_center, top_end, F), + (end_edge, top_end, bottom_end, R), + (bottom_end_edge, bottom_end, bottom_center, R), + ] + .into_iter() + .map(|(edge, from, to, sense)| plane_boundary(b, end_frame, edge, from, to, sense)) + .collect::, _>>()?; + b.face( + "end_radial", + SurfaceGeometry::Plane { frame: end_frame }, + [[0.0, height], [0.0, radius]], + end_uses, + ) +} + +fn tangent(frame: Frame3, angle: f64) -> Point3 { + frame.vector([-angle.sin(), angle.cos(), 0.0]) +} diff --git a/main/opengeometry/src/primitives/internal/cylinder_with_circular_hole.rs b/main/opengeometry/src/primitives/internal/cylinder_with_circular_hole.rs new file mode 100644 index 0000000..6c1913f --- /dev/null +++ b/main/opengeometry/src/primitives/internal/cylinder_with_circular_hole.rs @@ -0,0 +1,286 @@ +use crate::brep::{ + boundary, dimensions, plane_boundary, uv_line, Accuracy, BrepEnvelope, Builder, CurveGeometry, + Frame3, GeometryError, Orientation, SurfaceGeometry, +}; +use crate::math::{dot, norm, scale, sub, Interval}; +use Orientation::{Forward as F, Reverse as R}; + +struct AnnulusTopology { + outer_low: u32, + outer_high: u32, + inner_low: u32, + inner_high: u32, + high_frame: Frame3, + outer_bottom: u32, + outer_top: u32, + inner_bottom: u32, + inner_top: u32, + outer_seam: u32, + inner_seam: u32, +} + +pub fn cylinder_with_circular_hole( + id: String, + frame: Frame3, + inner_frame: Frame3, + inner_radius: f64, + outer_radius: f64, + height: f64, + accuracy: Accuracy, +) -> Result { + frame.validate()?; + inner_frame.validate()?; + accuracy.validate()?; + dimensions(&[inner_radius, outer_radius, height])?; + let inner_offset = sub(inner_frame.origin, frame.origin); + let axial_offset = dot(inner_offset, frame.z); + let radial_offset = sub(inner_offset, scale(frame.z, axial_offset)); + if norm(sub(inner_frame.z, frame.z)) > 1e-12 || axial_offset.abs() > accuracy.geometric { + return Err(GeometryError::InvalidGeometry( + "circular hole axis must share the outer cylinder span".into(), + )); + } + if norm(radial_offset) + inner_radius >= outer_radius { + return Err(GeometryError::InvalidGeometry( + "circular hole must lie strictly inside the outer cylinder".into(), + )); + } + if outer_radius - inner_radius - norm(radial_offset) <= 4.0 * accuracy.geometric + || height <= 4.0 * accuracy.geometric + { + return Err(GeometryError::UnresolvedIntersection( + "cylindrical shell is below geometric resolution".into(), + )); + } + + let mut b = Builder::new(id, accuracy)?; + let tau = std::f64::consts::TAU; + let range = Interval::new(0.0, tau)?; + let topology = add_annulus_topology( + &mut b, + frame, + inner_frame, + inner_radius, + outer_radius, + height, + range, + )?; + add_lateral_faces( + &mut b, + &topology, + frame, + inner_frame, + inner_radius, + outer_radius, + height, + )?; + add_annulus_caps(&mut b, &topology, frame, outer_radius)?; + b.finish_solid() +} + +fn add_annulus_topology( + b: &mut Builder, + frame: Frame3, + inner_frame: Frame3, + inner_radius: f64, + outer_radius: f64, + height: f64, + range: Interval, +) -> Result { + let outer_low = b.vertex(frame.point([outer_radius, 0.0, 0.0])); + let outer_high = b.vertex(frame.point([outer_radius, 0.0, height])); + let inner_low = b.vertex(inner_frame.point([inner_radius, 0.0, 0.0])); + let inner_high = b.vertex(inner_frame.point([inner_radius, 0.0, height])); + let low_frame = frame; + let mut high_frame = frame; + high_frame.origin = frame.point([0.0, 0.0, height]); + let mut inner_high_frame = inner_frame; + inner_high_frame.origin = inner_frame.point([0.0, 0.0, height]); + + let outer_bottom = b.edge( + CurveGeometry::Circle { + frame: low_frame, + radius: outer_radius, + }, + range, + false, + ); + let outer_top = b.edge( + CurveGeometry::Circle { + frame: high_frame, + radius: outer_radius, + }, + range, + false, + ); + let inner_bottom = b.edge( + CurveGeometry::Circle { + frame: inner_frame, + radius: inner_radius, + }, + range, + false, + ); + let inner_top = b.edge( + CurveGeometry::Circle { + frame: inner_high_frame, + radius: inner_radius, + }, + range, + false, + ); + let [outer_seam, inner_seam] = + add_seam_edges(b, frame, inner_frame, inner_radius, outer_radius, height)?; + Ok(AnnulusTopology { + outer_low, + outer_high, + inner_low, + inner_high, + high_frame, + outer_bottom, + outer_top, + inner_bottom, + inner_top, + outer_seam, + inner_seam, + }) +} + +fn add_seam_edges( + b: &mut Builder, + frame: Frame3, + inner_frame: Frame3, + inner_radius: f64, + outer_radius: f64, + height: f64, +) -> Result<[u32; 2], GeometryError> { + let outer_seam = b.edge( + CurveGeometry::Line { + origin: frame.point([outer_radius, 0.0, 0.0]), + direction: frame.z, + }, + Interval::new(0.0, height)?, + true, + ); + let inner_seam = b.edge( + CurveGeometry::Line { + origin: inner_frame.point([inner_radius, 0.0, 0.0]), + direction: inner_frame.z, + }, + Interval::new(0.0, height)?, + true, + ); + Ok([outer_seam, inner_seam]) +} + +fn add_lateral_faces( + b: &mut Builder, + topology: &AnnulusTopology, + frame: Frame3, + inner_frame: Frame3, + inner_radius: f64, + outer_radius: f64, + height: f64, +) -> Result<(), GeometryError> { + let tau = std::f64::consts::TAU; + let &AnnulusTopology { + outer_low, + outer_high, + inner_low, + inner_high, + outer_bottom, + outer_top, + inner_bottom, + inner_top, + outer_seam, + inner_seam, + .. + } = topology; + let lateral_uses = |bottom, top, seam, low, high| { + vec![ + boundary(bottom, low, low, F, uv_line([0.0, 0.0], [1.0, 0.0])), + boundary(seam, low, high, F, uv_line([tau, 0.0], [0.0, 1.0])), + boundary(top, high, high, R, uv_line([0.0, height], [1.0, 0.0])), + boundary(seam, high, low, R, uv_line([0.0, 0.0], [0.0, 1.0])), + ] + }; + b.face( + "outer", + SurfaceGeometry::Cylinder { + frame, + radius: outer_radius, + }, + [[0.0, tau], [0.0, height]], + lateral_uses(outer_bottom, outer_top, outer_seam, outer_low, outer_high), + )?; + b.face( + "inner", + SurfaceGeometry::Cylinder { + frame: inner_frame, + radius: inner_radius, + }, + [[0.0, tau], [0.0, height]], + lateral_uses(inner_bottom, inner_top, inner_seam, inner_low, inner_high), + )?; + b.brep.topology.faces[1].sense = R; + Ok(()) +} + +fn add_annulus_caps( + b: &mut Builder, + topology: &AnnulusTopology, + frame: Frame3, + outer_radius: f64, +) -> Result<(), GeometryError> { + let &AnnulusTopology { + outer_low, + outer_high, + inner_low, + inner_high, + high_frame, + outer_bottom, + outer_top, + inner_bottom, + inner_top, + .. + } = topology; + let mut low_frame = frame; + low_frame.y = scale(low_frame.y, -1.0); + low_frame.z = scale(low_frame.z, -1.0); + let outer_lower = vec![plane_boundary( + b, + low_frame, + outer_bottom, + outer_low, + outer_low, + R, + )?]; + let inner_lower = vec![plane_boundary( + b, + low_frame, + inner_bottom, + inner_low, + inner_low, + R, + )?]; + b.face_with_holes( + "lower_cap", + SurfaceGeometry::Plane { frame: low_frame }, + [[-outer_radius, outer_radius]; 2], + outer_lower, + vec![inner_lower], + )?; + let outer_upper = vec![plane_boundary( + b, high_frame, outer_top, outer_high, outer_high, F, + )?]; + let inner_upper = vec![plane_boundary( + b, high_frame, inner_top, inner_high, inner_high, F, + )?]; + b.face_with_holes( + "upper_cap", + SurfaceGeometry::Plane { frame: high_frame }, + [[-outer_radius, outer_radius]; 2], + outer_upper, + vec![inner_upper], + ) +} diff --git a/main/opengeometry/src/primitives/internal/extruded_loop.rs b/main/opengeometry/src/primitives/internal/extruded_loop.rs new file mode 100644 index 0000000..769eaee --- /dev/null +++ b/main/opengeometry/src/primitives/internal/extruded_loop.rs @@ -0,0 +1,87 @@ +use crate::brep::{ + plane_boundary, unit, Builder, Frame3, GeometryError, Orientation, SurfaceGeometry, +}; +use crate::math::{cross, norm, sub}; + +pub(super) struct ExtrudedLoop { + pub(super) bottom_vertices: Vec, + pub(super) top_vertices: Vec, + pub(super) bottom_edges: Vec, + pub(super) top_edges: Vec, + pub(super) vertical_edges: Vec, +} + +pub(super) struct SideFace { + pub(super) frame: Frame3, + pub(super) height: f64, + pub(super) g: f64, + pub(super) loop_index: usize, + pub(super) index: usize, + pub(super) next: usize, + pub(super) bottom_from: u32, + pub(super) bottom_to: u32, + pub(super) top_from: u32, + pub(super) top_to: u32, +} + +pub(super) fn add_plane_side( + builder: &mut Builder, + profile: &ExtrudedLoop, + side: &SideFace, +) -> Result<(), GeometryError> { + let &SideFace { + frame, + height, + g, + loop_index, + index, + next, + bottom_from, + bottom_to, + top_from, + top_to, + } = side; + let origin = builder.brep.topology.vertices[bottom_from as usize].position; + let destination = builder.brep.topology.vertices[bottom_to as usize].position; + let edge_direction = unit(sub(destination, origin))?; + let side_frame = Frame3::from_axis( + origin, + unit(cross(edge_direction, frame.z))?, + edge_direction, + )?; + let uses = [ + ( + profile.bottom_edges[index], + bottom_from, + bottom_to, + Orientation::Forward, + ), + ( + profile.vertical_edges[next], + bottom_to, + top_to, + Orientation::Forward, + ), + ( + profile.top_edges[index], + top_to, + top_from, + Orientation::Reverse, + ), + ( + profile.vertical_edges[index], + top_from, + bottom_from, + Orientation::Reverse, + ), + ] + .into_iter() + .map(|(edge, from, to, sense)| plane_boundary(builder, side_frame, edge, from, to, sense)) + .collect::, _>>()?; + builder.face( + &format!("side-{loop_index}-{index}"), + SurfaceGeometry::Plane { frame: side_frame }, + [[-g, norm(sub(destination, origin)) + g], [-g, height + g]], + uses, + ) +} diff --git a/main/opengeometry/src/primitives/internal/frustum.rs b/main/opengeometry/src/primitives/internal/frustum.rs new file mode 100644 index 0000000..b2512c7 --- /dev/null +++ b/main/opengeometry/src/primitives/internal/frustum.rs @@ -0,0 +1,56 @@ +use crate::brep::{dimensions, Accuracy, BrepEnvelope, Frame3, GeometryError, SurfaceGeometry}; +use crate::math::scale; +use crate::primitives::cylinder::cylinder; +use crate::primitives::revolved_section::{section, SectionExtent}; + +pub fn frustum( + id: String, + frame: Frame3, + lower_radius: f64, + upper_radius: f64, + height: f64, + accuracy: Accuracy, +) -> Result { + frame.validate()?; + dimensions(&[lower_radius, height])?; + if !upper_radius.is_finite() || upper_radius < 0.0 { + return Err(GeometryError::InvalidGeometry( + "invalid frustum upper radius".into(), + )); + } + if lower_radius == upper_radius { + return cylinder(id, frame, lower_radius, height, accuracy); + } + let k = (upper_radius - lower_radius).abs() / height; + let semi_angle = k.atan(); + let mut cone_frame = frame; + let (r0, r1, z0, z1) = if upper_radius > lower_radius { + cone_frame.origin = frame.point([0.0, 0.0, -lower_radius / k]); + ( + lower_radius, + upper_radius, + lower_radius / k, + upper_radius / k, + ) + } else { + cone_frame.origin = frame.point([0.0, 0.0, lower_radius / k]); + cone_frame.y = scale(frame.y, -1.0); + cone_frame.z = scale(frame.z, -1.0); + ( + upper_radius, + lower_radius, + upper_radius / k, + lower_radius / k, + ) + }; + section( + id, + cone_frame, + SurfaceGeometry::Cone { + frame: cone_frame, + semi_angle, + }, + &SectionExtent { r0, r1, z0, z1 }, + accuracy, + ) +} diff --git a/main/opengeometry/src/primitives/internal/linear_extrusion.rs b/main/opengeometry/src/primitives/internal/linear_extrusion.rs new file mode 100644 index 0000000..4fb7a05 --- /dev/null +++ b/main/opengeometry/src/primitives/internal/linear_extrusion.rs @@ -0,0 +1,261 @@ +use super::extruded_loop::{add_plane_side, ExtrudedLoop, SideFace}; +use crate::brep::{ + dimensions, padded_uv_bounds, plane_boundary, unit, Accuracy, BrepEnvelope, Builder, + CurveGeometry, Frame3, GeometryError, Orientation, SurfaceGeometry, Use, +}; +use crate::math::{norm, scale, sub, Interval}; +use crate::primitives::profile::{ + loops_touch, orient_profile_loop, profile_point_inside, validate_profile_loop, +}; + +pub fn linear_extrusion( + id: String, + frame: Frame3, + outer: Vec<[f64; 2]>, + holes: Vec>, + height: f64, + accuracy: Accuracy, +) -> Result { + frame.validate()?; + accuracy.validate()?; + dimensions(&[height])?; + if height <= 4.0 * accuracy.geometric { + return Err(GeometryError::UnresolvedIntersection( + "extrusion height is below geometric resolution".into(), + )); + } + validate_profile_loop(&outer, accuracy.geometric, "outer profile")?; + for (index, hole) in holes.iter().enumerate() { + validate_profile_loop(hole, accuracy.geometric, &format!("profile hole {index}"))?; + if !profile_point_inside(hole[0], &outer) || loops_touch(&outer, hole, accuracy.geometric) { + return Err(GeometryError::InvalidGeometry(format!( + "profile hole {index} is not strictly inside the outer profile" + ))); + } + } + for first in 0..holes.len() { + for second in (first + 1)..holes.len() { + if loops_touch(&holes[first], &holes[second], accuracy.geometric) + || profile_point_inside(holes[first][0], &holes[second]) + || profile_point_inside(holes[second][0], &holes[first]) + { + return Err(GeometryError::InvalidGeometry(format!( + "profile holes {first} and {second} overlap or nest" + ))); + } + } + } + + let mut loops = Vec::with_capacity(holes.len() + 1); + loops.push(orient_profile_loop(outer, true)); + loops.extend( + holes + .into_iter() + .map(|hole| orient_profile_loop(hole, false)), + ); + let mut builder = Builder::new(id, accuracy)?; + let built = add_extrusion_loops(&mut builder, frame, &loops, height)?; + + let top_bounds = padded_uv_bounds(loops.iter().flatten(), accuracy.geometric); + let bottom_bounds = [top_bounds[0], [-top_bounds[1][1], -top_bounds[1][0]]]; + let mut top_frame = frame; + top_frame.origin = frame.point([0.0, 0.0, height]); + let bottom_frame = Frame3 { + y: scale(frame.y, -1.0), + z: scale(frame.z, -1.0), + ..frame + }; + add_cap_faces( + &mut builder, + &loops, + &built, + top_frame, + bottom_frame, + top_bounds, + bottom_bounds, + )?; + add_side_faces(&mut builder, &loops, &built, frame, height, accuracy)?; + builder.finish_solid() +} + +fn add_extrusion_loops( + builder: &mut Builder, + frame: Frame3, + loops: &[Vec<[f64; 2]>], + height: f64, +) -> Result, GeometryError> { + let mut built = Vec::with_capacity(loops.len()); + for points in loops { + let bottom_vertices = points + .iter() + .map(|point| builder.vertex(frame.point([point[0], point[1], 0.0]))) + .collect::>(); + let top_vertices = points + .iter() + .map(|point| builder.vertex(frame.point([point[0], point[1], height]))) + .collect::>(); + let mut bottom_edges = Vec::with_capacity(points.len()); + let mut top_edges = Vec::with_capacity(points.len()); + let mut vertical_edges = Vec::with_capacity(points.len()); + for index in 0..points.len() { + let next = (index + 1) % points.len(); + let direction = unit(sub( + builder.brep.topology.vertices[bottom_vertices[next] as usize].position, + builder.brep.topology.vertices[bottom_vertices[index] as usize].position, + ))?; + let length = norm(sub( + builder.brep.topology.vertices[bottom_vertices[next] as usize].position, + builder.brep.topology.vertices[bottom_vertices[index] as usize].position, + )); + bottom_edges.push(builder.edge( + CurveGeometry::Line { + origin: + builder.brep.topology.vertices[bottom_vertices[index] as usize].position, + direction, + }, + Interval::new(0.0, length)?, + false, + )); + top_edges.push(builder.edge( + CurveGeometry::Line { + origin: builder.brep.topology.vertices[top_vertices[index] as usize].position, + direction, + }, + Interval::new(0.0, length)?, + false, + )); + vertical_edges.push(builder.edge( + CurveGeometry::Line { + origin: + builder.brep.topology.vertices[bottom_vertices[index] as usize].position, + direction: frame.z, + }, + Interval::new(0.0, height)?, + false, + )); + } + built.push(ExtrudedLoop { + bottom_vertices, + top_vertices, + bottom_edges, + top_edges, + vertical_edges, + }); + } + Ok(built) +} + +fn add_cap_faces( + builder: &mut Builder, + loops: &[Vec<[f64; 2]>], + built: &[ExtrudedLoop], + top_frame: Frame3, + bottom_frame: Frame3, + top_bounds: [[f64; 2]; 2], + bottom_bounds: [[f64; 2]; 2], +) -> Result<(), GeometryError> { + let top_outer = cap_uses(builder, &loops[0], &built[0], true, top_frame, bottom_frame)?; + let top_holes = loops[1..] + .iter() + .zip(&built[1..]) + .map(|(points, profile)| cap_uses(builder, points, profile, true, top_frame, bottom_frame)) + .collect::, _>>()?; + builder.face_with_holes( + "top", + SurfaceGeometry::Plane { frame: top_frame }, + top_bounds, + top_outer, + top_holes, + )?; + let bottom_outer = cap_uses( + builder, + &loops[0], + &built[0], + false, + top_frame, + bottom_frame, + )?; + let bottom_holes = loops[1..] + .iter() + .zip(&built[1..]) + .map(|(points, profile)| cap_uses(builder, points, profile, false, top_frame, bottom_frame)) + .collect::, _>>()?; + builder.face_with_holes( + "bottom", + SurfaceGeometry::Plane { + frame: bottom_frame, + }, + bottom_bounds, + bottom_outer, + bottom_holes, + ) +} + +fn cap_uses( + builder: &Builder, + points: &[[f64; 2]], + profile: &ExtrudedLoop, + top: bool, + top_frame: Frame3, + bottom_frame: Frame3, +) -> Result, GeometryError> { + let count = points.len(); + (0..count) + .map(|offset| { + let index = if top { offset } else { count - 1 - offset }; + let next = (index + 1) % count; + if top { + plane_boundary( + builder, + top_frame, + profile.top_edges[index], + profile.top_vertices[index], + profile.top_vertices[next], + Orientation::Forward, + ) + } else { + plane_boundary( + builder, + bottom_frame, + profile.bottom_edges[index], + profile.bottom_vertices[next], + profile.bottom_vertices[index], + Orientation::Reverse, + ) + } + }) + .collect() +} + +fn add_side_faces( + builder: &mut Builder, + loops: &[Vec<[f64; 2]>], + built: &[ExtrudedLoop], + frame: Frame3, + height: f64, + accuracy: Accuracy, +) -> Result<(), GeometryError> { + for (loop_index, (points, profile)) in loops.iter().zip(built).enumerate() { + for index in 0..points.len() { + let next = (index + 1) % points.len(); + let bottom_from = profile.bottom_vertices[index]; + let bottom_to = profile.bottom_vertices[next]; + let top_from = profile.top_vertices[index]; + let top_to = profile.top_vertices[next]; + let side = SideFace { + frame, + height, + g: accuracy.geometric, + loop_index, + index, + next, + bottom_from, + bottom_to, + top_from, + top_to, + }; + add_plane_side(builder, profile, &side)?; + } + } + Ok(()) +} diff --git a/main/opengeometry/src/primitives/internal/mod.rs b/main/opengeometry/src/primitives/internal/mod.rs new file mode 100644 index 0000000..4aa53c9 --- /dev/null +++ b/main/opengeometry/src/primitives/internal/mod.rs @@ -0,0 +1,20 @@ +mod annular_cylinder; +mod arc_edged_extrusion; +mod cone; +mod cylinder_sector; +mod cylinder_with_circular_hole; +mod extruded_loop; +mod frustum; +mod linear_extrusion; +mod sphere; +mod torus; + +pub use annular_cylinder::annular_cylinder; +pub use arc_edged_extrusion::{arc_edged_extrusion, arc_edged_extrusion_with_holes}; +pub use cone::cone; +pub use cylinder_sector::cylinder_sector; +pub use cylinder_with_circular_hole::cylinder_with_circular_hole; +pub use frustum::frustum; +pub use linear_extrusion::linear_extrusion; +pub use sphere::sphere; +pub use torus::torus; diff --git a/main/opengeometry/src/primitives/internal/sphere.rs b/main/opengeometry/src/primitives/internal/sphere.rs new file mode 100644 index 0000000..6cb089c --- /dev/null +++ b/main/opengeometry/src/primitives/internal/sphere.rs @@ -0,0 +1,72 @@ +use crate::brep::{ + boundary, dimensions, uv_line, Accuracy, BrepEnvelope, Builder, CurveGeometry, EdgeGeometry, + Frame3, GeometryError, Orientation, SurfaceGeometry, +}; +use crate::math::{scale, Interval}; + +pub fn sphere( + id: String, + frame: Frame3, + radius: f64, + accuracy: Accuracy, +) -> Result { + frame.validate()?; + dimensions(&[radius])?; + let mut b = Builder::new(id, accuracy)?; + let pi2 = std::f64::consts::FRAC_PI_2; + let tau = std::f64::consts::TAU; + let south = b.vertex(frame.point([0.0, 0.0, -radius])); + let north = b.vertex(frame.point([0.0, 0.0, radius])); + let bottom = b.edge_geometry(EdgeGeometry::Collapsed { vertex: south }, true); + let top = b.edge_geometry(EdgeGeometry::Collapsed { vertex: north }, true); + let meridian_frame = Frame3 { + origin: frame.origin, + x: frame.x, + y: frame.z, + z: scale(frame.y, -1.0), + }; + let seam = b.edge( + CurveGeometry::Circle { + frame: meridian_frame, + radius, + }, + Interval::new(-pi2, pi2)?, + true, + ); + b.face( + "skin", + SurfaceGeometry::Sphere { frame, radius }, + [[0.0, tau], [-pi2, pi2]], + vec![ + boundary( + bottom, + south, + south, + Orientation::Forward, + uv_line([0.0, -pi2], [tau, 0.0]), + ), + boundary( + seam, + south, + north, + Orientation::Forward, + uv_line([tau, 0.0], [0.0, 1.0]), + ), + boundary( + top, + north, + north, + Orientation::Forward, + uv_line([tau, pi2], [-tau, 0.0]), + ), + boundary( + seam, + north, + south, + Orientation::Reverse, + uv_line([0.0, 0.0], [0.0, 1.0]), + ), + ], + )?; + b.finish_solid() +} diff --git a/main/opengeometry/src/primitives/internal/torus.rs b/main/opengeometry/src/primitives/internal/torus.rs new file mode 100644 index 0000000..ece67d8 --- /dev/null +++ b/main/opengeometry/src/primitives/internal/torus.rs @@ -0,0 +1,81 @@ +use crate::brep::{ + boundary, uv_line, Accuracy, BrepEnvelope, Builder, CurveGeometry, Frame3, GeometryError, + Orientation, SurfaceGeometry, +}; +use crate::math::{scale, Interval}; + +pub fn torus( + id: String, + frame: Frame3, + major_radius: f64, + minor_radius: f64, + accuracy: Accuracy, +) -> Result { + let surface = SurfaceGeometry::Torus { + frame, + major_radius, + minor_radius, + }; + surface.validate()?; + let mut b = Builder::new(id, accuracy)?; + let vertex = b.vertex(frame.point([major_radius + minor_radius, 0.0, 0.0])); + let tau = std::f64::consts::TAU; + let ring = b.edge( + CurveGeometry::Circle { + frame, + radius: major_radius + minor_radius, + }, + Interval::new(0.0, tau)?, + true, + ); + let tube_frame = Frame3 { + origin: frame.point([major_radius, 0.0, 0.0]), + x: frame.x, + y: frame.z, + z: scale(frame.y, -1.0), + }; + let tube = b.edge( + CurveGeometry::Circle { + frame: tube_frame, + radius: minor_radius, + }, + Interval::new(0.0, tau)?, + true, + ); + b.face( + "skin", + surface, + [[0.0, tau]; 2], + vec![ + boundary( + ring, + vertex, + vertex, + Orientation::Forward, + uv_line([0.0, 0.0], [1.0, 0.0]), + ), + boundary( + tube, + vertex, + vertex, + Orientation::Forward, + uv_line([tau, 0.0], [0.0, 1.0]), + ), + boundary( + ring, + vertex, + vertex, + Orientation::Reverse, + uv_line([0.0, tau], [1.0, 0.0]), + ), + boundary( + tube, + vertex, + vertex, + Orientation::Reverse, + uv_line([0.0, 0.0], [0.0, 1.0]), + ), + ], + )?; + b.finish_solid() +} diff --git a/main/opengeometry/src/primitives/line.rs b/main/opengeometry/src/primitives/line.rs deleted file mode 100644 index 0eb15e1..0000000 --- a/main/opengeometry/src/primitives/line.rs +++ /dev/null @@ -1,304 +0,0 @@ -/** - * Copyright (c) 2025, OpenGeometry. All rights reserved. - * Line Primitive for OpenGeometry. - */ -use crate::brep::{Brep, BrepBuilder}; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::operations::offset::{offset_path, OffsetOptions, OffsetResult}; -use crate::spatial::placement::{ - bounds_center_from_points, points_relative_to_anchor, Placement3D, -}; -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGLine { - id: String, - brep: Brep, - start: Vector3, - end: Vector3, - placement: Placement3D, - anchor_initialized: bool, -} - -impl Drop for OGLine { - fn drop(&mut self) { - self.brep.clear(); - self.id.clear(); - } -} - -#[wasm_bindgen] -impl OGLine { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGLine { - OGLine { - id, - start: Vector3::new(1.0, 0.0, 0.0), - end: Vector3::new(-1.0, 0.0, 0.0), - brep: Brep::new(Uuid::new_v4()), - placement: Placement3D::new(), - anchor_initialized: false, - } - } - - #[wasm_bindgen] - pub fn set_config(&mut self, start: Vector3, end: Vector3) -> Result<(), JsValue> { - self.start = start; - self.end = end; - self.ensure_anchor_initialized(); - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - self.brep.clear(); - - let local_points = self.local_points(); - let mut builder = BrepBuilder::new(self.brep.id); - builder.add_vertices(&local_points); - builder - .add_wire(&[0, 1], false) - .map_err(|err| JsValue::from_str(&format!("Failed to build line wire: {}", err)))?; - - self.brep = builder - .build() - .map_err(|err| JsValue::from_str(&format!("Failed to finalize line BREP: {}", err)))?; - - // D1: a line edge carries an exact straight-curve geometry. - if let (Some(edge), Some(start), Some(end)) = ( - self.brep.edges.first_mut(), - local_points.first().copied(), - local_points.get(1).copied(), - ) { - edge.curve = Some(crate::brep::CurveGeometry::Line { start, end }); - } - - Ok(()) - } - - #[wasm_bindgen] - pub fn dispose_points(&mut self) { - self.brep.clear(); - } - - #[wasm_bindgen] - pub fn destroy(&mut self) { - self.brep.clear(); - self.id.clear(); - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - serde_json::to_string(&wire_geometry_buffer(&self.world_brep())).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&wire_geometry_buffer(&self.brep)).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - wire_geometry_buffer(&self.world_brep()) - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - wire_geometry_buffer(&self.brep) - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } - - #[wasm_bindgen] - pub fn set_anchor(&mut self, anchor: Vector3) -> Result<(), JsValue> { - self.placement.set_anchor(anchor); - self.anchor_initialized = true; - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn reset_anchor(&mut self) -> Result<(), JsValue> { - self.recompute_anchor_from_bounds(); - self.anchor_initialized = true; - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn get_offset_serialized( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> String { - let result = self.get_offset_result(distance, acute_threshold_degrees, bevel); - serde_json::to_string(&result).unwrap() - } - - pub fn get_dxf_serialized(&self) -> String { - String::new() - } -} - -impl OGLine { - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } - - pub fn get_offset_result( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> OffsetResult { - let options = OffsetOptions { - bevel, - acute_threshold_degrees, - }; - let points = self.world_points(); - offset_path(&points, distance, Some(false), options) - } - - pub fn get_offset_points( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> Vec { - self.get_offset_result(distance, acute_threshold_degrees, bevel) - .points - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let world_brep = self.world_brep(); - project_brep_to_scene(&world_brep, camera, hlr) - } - - pub fn world_points(&self) -> Vec { - let world_brep = self.world_brep(); - world_brep - .vertices - .iter() - .map(|vertex| vertex.position) - .collect() - } - - fn local_points(&self) -> Vec { - let source_points = vec![self.start, self.end]; - let anchor = self.placement.anchor; - points_relative_to_anchor(&source_points, anchor) - } - - fn recompute_anchor_from_bounds(&mut self) { - let source_points = vec![self.start, self.end]; - let anchor = - bounds_center_from_points(&source_points).unwrap_or(Vector3::new(0.0, 0.0, 0.0)); - self.placement.set_anchor(anchor); - } - - fn ensure_anchor_initialized(&mut self) { - if self.anchor_initialized { - return; - } - self.recompute_anchor_from_bounds(); - self.anchor_initialized = true; - } -} - -fn wire_geometry_buffer(brep: &Brep) -> Vec { - let Some(wire) = brep.wires.first() else { - return Vec::new(); - }; - - brep.get_wire_vertex_buffer(wire.id, false) -} - -#[cfg(test)] -mod tests { - use super::*; - - fn assert_close(actual: f64, expected: f64) { - let delta = (actual - expected).abs(); - assert!( - delta <= 1.0e-9, - "expected {expected}, got {actual}, delta {delta}" - ); - } - - #[test] - fn anchor_stays_stable_across_config_updates_until_reset() { - let mut line = OGLine::new("line-anchor".to_string()); - line.set_config(Vector3::new(0.0, 0.0, 0.0), Vector3::new(10.0, 0.0, 0.0)) - .expect("line config"); - let initial_anchor = line.get_anchor(); - assert_close(initial_anchor.x, 5.0); - - line.set_config(Vector3::new(10.0, 0.0, 0.0), Vector3::new(20.0, 0.0, 0.0)) - .expect("line config update"); - let anchor_after_update = line.get_anchor(); - assert_close(anchor_after_update.x, 5.0); - - line.reset_anchor().expect("reset anchor"); - let anchor_after_reset = line.get_anchor(); - assert_close(anchor_after_reset.x, 15.0); - } -} diff --git a/main/opengeometry/src/primitives/mod.rs b/main/opengeometry/src/primitives/mod.rs new file mode 100644 index 0000000..8a529d4 --- /dev/null +++ b/main/opengeometry/src/primitives/mod.rs @@ -0,0 +1,29 @@ +mod box_boundary; +mod circle; +mod cuboid; +mod cylinder; +mod internal; +mod polyline; +mod profile; +mod rectangle; +mod revolved_section; +#[cfg(test)] +mod tests; +mod wire_points; + +pub(super) use box_boundary::{ + add_box_corners, box_face_boundary, box_face_layout, BoxCorners, BoxFaceLayout, +}; +pub use circle::arc_wire; +pub use cuboid::cuboid; +pub use cylinder::cylinder; +pub use internal::{ + annular_cylinder, arc_edged_extrusion, arc_edged_extrusion_with_holes, cone, cylinder_sector, + cylinder_with_circular_hole, frustum, linear_extrusion, sphere, torus, +}; +pub use polyline::{polyline, polyline_with_keys}; +pub use profile::ProfileEdge; +pub(super) use profile::{ + arc_profile_contains, loops_touch, profile_edge_intersections, profile_point_inside, +}; +pub use rectangle::{rectangle, rectangle_with_keys}; diff --git a/main/opengeometry/src/primitives/polygon.rs b/main/opengeometry/src/primitives/polygon.rs deleted file mode 100644 index b4ba758..0000000 --- a/main/opengeometry/src/primitives/polygon.rs +++ /dev/null @@ -1,351 +0,0 @@ -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; - -use crate::brep::{Brep, BrepBuilder, SurfaceGeometry}; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::operations::profile::analyze_profile; -use crate::spatial::placement::{ - bounds_center_from_point_sets, points_relative_to_anchor, Placement3D, -}; -use crate::utility::bgeometry::BufferGeometry; -use openmaths::Vector3; -use uuid::Uuid; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGPolygon { - id: String, - points: Vec, - holes: Vec>, - placement: Placement3D, - geometry: BufferGeometry, - brep: Brep, - anchor_initialized: bool, -} - -impl OGPolygon { - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let world_brep = self.world_brep(); - project_brep_to_scene(&world_brep, camera, hlr) - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } -} - -#[wasm_bindgen] -impl OGPolygon { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGPolygon { - let internal_id = Uuid::new_v4(); - - OGPolygon { - id, - points: Vec::new(), - holes: Vec::new(), - placement: Placement3D::new(), - geometry: BufferGeometry::new(internal_id), - brep: Brep::new(internal_id), - anchor_initialized: false, - } - } - - #[wasm_bindgen] - pub fn set_config(&mut self, points: Vec) -> Result<(), JsValue> { - self.points = points; - self.holes.clear(); - self.ensure_anchor_initialized(); - self.generate_brep() - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn add_vertices(&mut self, vertices: Vec) -> Result<(), JsValue> { - self.points = vertices; - self.holes.clear(); - self.ensure_anchor_initialized(); - self.generate_brep() - } - - #[wasm_bindgen] - pub fn add_holes(&mut self, hole: Vec) -> Result<(), JsValue> { - self.holes.push(hole); - self.ensure_anchor_initialized(); - self.generate_brep() - } - - #[wasm_bindgen] - pub fn clean_geometry(&mut self) { - self.brep.clear(); - self.geometry.clear(); - } - - #[wasm_bindgen] - pub fn generate_brep(&mut self) -> Result<(), JsValue> { - if self.points.len() < 3 { - self.clean_geometry(); - return Ok(()); - } - - self.clean_geometry(); - - let mut builder = BrepBuilder::new(self.brep.id); - - let anchor = self.placement.anchor; - let raw_local = points_relative_to_anchor(&self.points, anchor); - - // D7: validate and repair the outer profile before face creation — - // remove duplicates, auto-correct winding to CCW-outer, and reject - // self-intersecting / degenerate loops with a diagnostic instead of - // silently emitting a garbage face. - let (report, local_points) = analyze_profile(&raw_local); - if !report.is_valid { - return Err(JsValue::from_str(&format!( - "Invalid polygon profile: {}", - report.diagnostics.join("; ") - ))); - } - - let mut all_vertices = local_points.clone(); - let mut hole_index_sets: Vec> = Vec::new(); - - for hole in &self.holes { - if hole.len() < 3 { - continue; - } - - let local_hole = points_relative_to_anchor(hole, anchor); - // Clean hole loops too; skip any that are degenerate/self-intersecting. - let (hole_report, cleaned_hole) = analyze_profile(&local_hole); - if !hole_report.is_valid { - continue; - } - let start = all_vertices.len() as u32; - let count = cleaned_hole.len() as u32; - all_vertices.extend(cleaned_hole); - let indices: Vec = (0..count).map(|offset| start + offset).collect(); - hole_index_sets.push(indices); - } - - builder.add_vertices(&all_vertices); - let outer_indices: Vec = (0..local_points.len() as u32).collect(); - - let face_id = builder - .add_face(&outer_indices, &hole_index_sets) - .map_err(|err| JsValue::from_str(&format!("Failed to build polygon face: {}", err)))?; - - self.brep = builder.build().map_err(|err| { - JsValue::from_str(&format!("Failed to finalize polygon BREP: {}", err)) - })?; - - // D1: a polygon is a planar face — record its exact plane so analytic - // export (D9) emits a PLANE rather than inferring one from facets. - if let Some(face) = self.brep.faces.get_mut(face_id as usize) { - let normal = face.normal; - face.surface = Some(SurfaceGeometry::Plane { - origin: local_points[0], - normal, - }); - } - - Ok(()) - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - self.generate_brep() - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - let world_brep = self.world_brep(); - serde_json::to_string(&world_brep.get_triangle_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&self.brep.get_triangle_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - self.world_brep().get_triangle_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - self.brep.get_triangle_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_serialized(&self) -> String { - let world_brep = self.world_brep(); - serde_json::to_string(&world_brep.get_outline_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_serialized(&self) -> String { - serde_json::to_string(&self.brep.get_outline_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_buffer(&self) -> Vec { - self.world_brep().get_outline_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_buffer(&self) -> Vec { - self.brep.get_outline_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } - - #[wasm_bindgen] - pub fn set_anchor(&mut self, anchor: Vector3) -> Result<(), JsValue> { - self.placement.set_anchor(anchor); - self.anchor_initialized = true; - self.generate_brep() - } - - #[wasm_bindgen] - pub fn reset_anchor(&mut self) -> Result<(), JsValue> { - self.recompute_anchor_from_bounds(); - self.anchor_initialized = true; - self.generate_brep() - } -} - -impl OGPolygon { - fn recompute_anchor_from_bounds(&mut self) { - let mut point_sets = Vec::with_capacity(self.holes.len() + 1); - point_sets.push(self.points.as_slice()); - for hole in &self.holes { - point_sets.push(hole.as_slice()); - } - - let anchor = - bounds_center_from_point_sets(&point_sets).unwrap_or(Vector3::new(0.0, 0.0, 0.0)); - self.placement.set_anchor(anchor); - } - - fn ensure_anchor_initialized(&mut self) { - if self.anchor_initialized { - return; - } - self.recompute_anchor_from_bounds(); - self.anchor_initialized = true; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn anchor_stays_stable_across_polygon_config_updates_until_reset() { - let mut polygon = OGPolygon::new("polygon-anchor".to_string()); - polygon - .set_config(vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(10.0, 0.0, 0.0), - Vector3::new(10.0, 0.0, 10.0), - Vector3::new(0.0, 0.0, 10.0), - ]) - .expect("polygon config"); - let initial_anchor = polygon.get_anchor(); - assert_eq!(initial_anchor.x, 5.0); - assert_eq!(initial_anchor.z, 5.0); - - polygon - .set_config(vec![ - Vector3::new(10.0, 0.0, 10.0), - Vector3::new(20.0, 0.0, 10.0), - Vector3::new(20.0, 0.0, 20.0), - Vector3::new(10.0, 0.0, 20.0), - ]) - .expect("polygon config update"); - let anchor_after_update = polygon.get_anchor(); - assert_eq!(anchor_after_update.x, 5.0); - assert_eq!(anchor_after_update.z, 5.0); - - polygon.reset_anchor().expect("reset anchor"); - let anchor_after_reset = polygon.get_anchor(); - assert_eq!(anchor_after_reset.x, 15.0); - assert_eq!(anchor_after_reset.z, 15.0); - } - - #[test] - fn placement_rejects_non_uniform_scale() { - let mut polygon = OGPolygon::new("polygon-scale".to_string()); - polygon - .set_config(vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 1.0), - ]) - .expect("polygon config"); - - let result = polygon.placement.set_transform( - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 1.2, 1.0), - ); - assert!(result.is_err()); - } -} diff --git a/main/opengeometry/src/primitives/polyline.rs b/main/opengeometry/src/primitives/polyline.rs index 6b3dae7..abb3a42 100644 --- a/main/opengeometry/src/primitives/polyline.rs +++ b/main/opengeometry/src/primitives/polyline.rs @@ -1,366 +1,25 @@ -/** - * Copyright (c) 2025, OpenGeometry. All rights reserved. - * Polyline Primitive for OpenGeometry. - */ -use crate::brep::{Brep, BrepBuilder}; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::operations::offset::{offset_path, OffsetOptions, OffsetResult}; -use crate::spatial::placement::{ - bounds_center_from_points, points_relative_to_anchor, transform_points_with_placement, - Placement3D, -}; -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; -use wasm_bindgen::prelude::*; +use super::wire_points::wire_from_points; +use crate::brep::{Accuracy, BrepEnvelope, GeometryError}; +use crate::math::Point3; -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGPolyline { +pub fn polyline( id: String, - points: Vec, - is_closed: bool, - placement: Placement3D, - brep: Brep, - anchor_initialized: bool, + points: &[Point3], + closed: bool, + accuracy: Accuracy, +) -> Result { + wire_from_points(id, points, closed, accuracy) } -impl OGPolyline { - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let world_brep = self.world_brep(); - project_brep_to_scene(&world_brep, camera, hlr) - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } -} - -impl Drop for OGPolyline { - fn drop(&mut self) { - self.points.clear(); - self.is_closed = false; - self.brep.clear(); - self.id.clear(); - } -} - -#[wasm_bindgen] -impl OGPolyline { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGPolyline { - OGPolyline { - id, - points: Vec::new(), - is_closed: false, - placement: Placement3D::new(), - brep: Brep::new(Uuid::new_v4()), - anchor_initialized: false, - } - } - - #[wasm_bindgen] - pub fn clone(&self) -> OGPolyline { - OGPolyline { - id: self.id.clone(), - points: self.points.clone(), - is_closed: self.is_closed, - placement: self.placement.clone(), - brep: self.brep.clone(), - anchor_initialized: self.anchor_initialized, - } - } - - pub fn set_config(&mut self, points: Vec) -> Result<(), JsValue> { - self.points = points; - self.check_closed_test(); - self.ensure_anchor_initialized(); - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - if self.points.is_empty() { - self.brep.clear(); - return Ok(()); - } - - self.brep.clear(); - - let mut effective_points = self.local_points(); - if self.is_closed && effective_points.len() > 2 { - let first = effective_points[0]; - let last = *effective_points.last().unwrap(); - let dx = first.x - last.x; - let dy = first.y - last.y; - let dz = first.z - last.z; - let duplicate_end = dx * dx + dy * dy + dz * dz <= 1.0e-12; - if duplicate_end { - effective_points.pop(); - } - } - - let mut builder = BrepBuilder::new(self.brep.id); - builder.add_vertices(&effective_points); - - if effective_points.len() >= 2 { - let indices: Vec = (0..effective_points.len() as u32).collect(); - let closed_wire = self.is_closed && effective_points.len() > 2; - builder.add_wire(&indices, closed_wire).map_err(|err| { - JsValue::from_str(&format!("Failed to build polyline wire: {}", err)) - })?; - } - - self.brep = builder.build().map_err(|err| { - JsValue::from_str(&format!("Failed to finalize polyline BREP: {}", err)) - })?; - - Ok(()) - } - - #[wasm_bindgen] - pub fn add_multiple_points(&mut self, points: Vec) -> Result<(), JsValue> { - self.points = points; - self.check_closed_test(); - self.ensure_anchor_initialized(); - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn add_point(&mut self, point: Vector3) -> Result<(), JsValue> { - self.points.push(point); - self.check_closed_test(); - self.ensure_anchor_initialized(); - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn get_points(&self) -> String { - serde_json::to_string(&self.points).unwrap() - } - - pub fn get_raw_points(&self) -> Vec { - self.points.clone() - } - - #[wasm_bindgen] - pub fn is_closed(&self) -> bool { - self.is_closed - } - - #[wasm_bindgen] - pub fn get_offset_serialized( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> String { - let result = self.get_offset_result(distance, acute_threshold_degrees, bevel); - serde_json::to_string(&result).unwrap() - } - - pub fn check_closed_test(&mut self) { - self.is_closed = false; - if self.points.len() > 2 { - let first = self.points[0]; - let last = self.points[self.points.len() - 1]; - if first.x == last.x && first.y == last.y && first.z == last.z { - self.is_closed = true; - } - } - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - serde_json::to_string(&wire_geometry_buffer(&self.world_brep())).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&wire_geometry_buffer(&self.brep)).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - wire_geometry_buffer(&self.world_brep()) - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - wire_geometry_buffer(&self.brep) - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } - - #[wasm_bindgen] - pub fn set_anchor(&mut self, anchor: Vector3) -> Result<(), JsValue> { - self.placement.set_anchor(anchor); - self.anchor_initialized = true; - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn reset_anchor(&mut self) -> Result<(), JsValue> { - self.recompute_anchor_from_bounds(); - self.anchor_initialized = true; - self.generate_geometry() - } -} - -impl OGPolyline { - pub fn get_offset_result( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> OffsetResult { - let options = OffsetOptions { - bevel, - acute_threshold_degrees, - }; - let points = transform_points_with_placement(&self.points, &self.placement); - offset_path(&points, distance, Some(self.is_closed), options) - } - - pub fn get_offset_points( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> Vec { - self.get_offset_result(distance, acute_threshold_degrees, bevel) - .points - } - - fn local_points(&self) -> Vec { - points_relative_to_anchor(&self.points, self.placement.anchor) - } - - fn recompute_anchor_from_bounds(&mut self) { - let anchor = bounds_center_from_points(&self.points).unwrap_or(Vector3::new(0.0, 0.0, 0.0)); - self.placement.set_anchor(anchor); - } - - fn ensure_anchor_initialized(&mut self) { - if self.anchor_initialized { - return; - } - self.recompute_anchor_from_bounds(); - self.anchor_initialized = true; - } -} - -fn wire_geometry_buffer(brep: &Brep) -> Vec { - let Some(wire) = brep.wires.first() else { - return Vec::new(); - }; - - brep.get_wire_vertex_buffer(wire.id, wire.is_closed) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn closed_polyline_builds_wire_without_duplicate_halfedge_error() { - let mut polyline = OGPolyline::new("polyline-test".to_string()); - let points = vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 0.0), - Vector3::new(2.0, 0.0, 2.0), - Vector3::new(0.0, 0.0, 2.0), - Vector3::new(0.0, 0.0, 0.0), - ]; - - polyline - .set_config(points) - .expect("closed polyline should build without duplicate directed halfedge"); - - assert!(polyline.is_closed()); - assert_eq!(polyline.brep.wires.len(), 1); - assert_eq!(polyline.brep.faces.len(), 0); - } - - #[test] - fn anchor_stays_stable_across_polyline_config_updates_until_reset() { - let mut polyline = OGPolyline::new("polyline-anchor".to_string()); - polyline - .set_config(vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(10.0, 0.0, 0.0), - ]) - .expect("polyline config"); - let initial_anchor = polyline.get_anchor(); - assert_eq!(initial_anchor.x, 5.0); - - polyline - .set_config(vec![ - Vector3::new(10.0, 0.0, 0.0), - Vector3::new(20.0, 0.0, 0.0), - ]) - .expect("polyline config update"); - let anchor_after_update = polyline.get_anchor(); - assert_eq!(anchor_after_update.x, 5.0); - - polyline.reset_anchor().expect("reset anchor"); - let anchor_after_reset = polyline.get_anchor(); - assert_eq!(anchor_after_reset.x, 15.0); - } +pub fn polyline_with_keys( + id: String, + points: &[Point3], + closed: bool, + accuracy: Accuracy, +) -> Result<(BrepEnvelope, Vec), GeometryError> { + let brep = polyline(id, points, closed, accuracy)?; + let keys = (0..brep.topology.edges.len()) + .map(|index| format!("seg-{index}")) + .collect(); + Ok((brep, keys)) } diff --git a/main/opengeometry/src/primitives/profile/arc.rs b/main/opengeometry/src/primitives/profile/arc.rs new file mode 100644 index 0000000..c81c184 --- /dev/null +++ b/main/opengeometry/src/primitives/profile/arc.rs @@ -0,0 +1,339 @@ +use crate::brep::GeometryError; +use crate::geom2d::{winding, CurveEdge2, Pt2}; +use serde::Deserialize; + +#[derive(Clone, Debug, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)] +pub enum ProfileEdge { + Line { + from: [f64; 2], + to: [f64; 2], + }, + Arc { + center: [f64; 2], + radius: f64, + start_angle: f64, + sweep_angle: f64, + }, +} + +impl ProfileEdge { + pub(crate) fn endpoints(&self) -> ([f64; 2], [f64; 2]) { + match self { + Self::Line { from, to } => (*from, *to), + Self::Arc { + center, + radius, + start_angle, + sweep_angle, + } => { + let point = |angle: f64| { + [ + center[0] + radius * angle.cos(), + center[1] + radius * angle.sin(), + ] + }; + (point(*start_angle), point(start_angle + sweep_angle)) + } + } + } + + pub(crate) fn reversed(&self) -> Self { + match self { + Self::Line { from, to } => Self::Line { + from: *to, + to: *from, + }, + Self::Arc { + center, + radius, + start_angle, + sweep_angle, + } => Self::Arc { + center: *center, + radius: *radius, + start_angle: start_angle + sweep_angle, + sweep_angle: -sweep_angle, + }, + } + } + + fn signed_area_twice(&self) -> f64 { + let (from, to) = self.endpoints(); + match self { + Self::Line { .. } => from[0] * to[1] - to[0] * from[1], + Self::Arc { + center, + radius, + sweep_angle, + .. + } => { + radius * radius * sweep_angle + center[0] * (to[1] - from[1]) + - center[1] * (to[0] - from[0]) + } + } + } +} + +pub(crate) fn validate_arc_profile_loop( + edges: &[ProfileEdge], + g: f64, +) -> Result { + if edges.len() < 2 { + return Err(GeometryError::InvalidGeometry( + "arc-edged profile needs at least two edges".into(), + )); + } + for (index, edge) in edges.iter().enumerate() { + let (from, to) = edge.endpoints(); + if !from.into_iter().chain(to).all(f64::is_finite) { + return Err(GeometryError::InvalidGeometry( + "profile coordinates must be finite".into(), + )); + } + match edge { + ProfileEdge::Line { .. } => { + if (to[0] - from[0]).hypot(to[1] - from[1]) <= 4.0 * g { + return Err(GeometryError::UnresolvedIntersection( + "profile line is below resolution".into(), + )); + } + } + ProfileEdge::Arc { + center, + radius, + start_angle, + sweep_angle, + } => { + if !center.iter().all(|value| value.is_finite()) + || !radius.is_finite() + || !start_angle.is_finite() + || !sweep_angle.is_finite() + || *radius <= 4.0 * g + || sweep_angle.abs() >= std::f64::consts::TAU + || radius * sweep_angle.abs() <= 4.0 * g + { + return Err(GeometryError::InvalidGeometry( + "profile arc is unresolved".into(), + )); + } + } + } + let (next, _) = edges[(index + 1) % edges.len()].endpoints(); + if (to[0] - next[0]).hypot(to[1] - next[1]) > g { + return Err(GeometryError::InvalidGeometry( + "profile edges do not close".into(), + )); + } + } + check_self_intersections(edges, g)?; + let signed_area_twice: f64 = edges.iter().map(ProfileEdge::signed_area_twice).sum(); + if signed_area_twice.abs() <= 16.0 * g * g { + return Err(GeometryError::UnresolvedIntersection( + "profile area is below resolution".into(), + )); + } + Ok(signed_area_twice) +} + +fn check_self_intersections(edges: &[ProfileEdge], g: f64) -> Result<(), GeometryError> { + for first in 0..edges.len() { + for second in (first + 1)..edges.len() { + let adjacent = second == first + 1 || first == 0 && second == edges.len() - 1; + let expected = if second == first + 1 { + Some(edges[first].endpoints().1) + } else if first == 0 && second == edges.len() - 1 { + Some(edges[first].endpoints().0) + } else { + None + }; + for point in profile_edge_intersections(&edges[first], &edges[second], g) { + let at_expected = expected + .is_some_and(|shared| (point[0] - shared[0]).hypot(point[1] - shared[1]) <= g); + let at_second_join = edges.len() == 2 + && (point[0] - edges[first].endpoints().0[0]) + .hypot(point[1] - edges[first].endpoints().0[1]) + <= g; + if !adjacent || !(at_expected || at_second_join) { + return Err(GeometryError::InvalidGeometry( + "arc-edged profile self-intersects".into(), + )); + } + } + } + } + Ok(()) +} + +pub(crate) fn profile_edge_intersections( + a: &ProfileEdge, + b: &ProfileEdge, + tolerance: f64, +) -> Vec<[f64; 2]> { + let mut candidates = Vec::new(); + let mut offer = |point: [f64; 2]| { + if point.iter().all(|value| value.is_finite()) + && profile_edge_contains(a, point, tolerance) + && profile_edge_contains(b, point, tolerance) + && !candidates.iter().any(|other: &[f64; 2]| { + (other[0] - point[0]).hypot(other[1] - point[1]) <= tolerance + }) + { + candidates.push(point); + } + }; + match (a, b) { + (ProfileEdge::Line { from: p, to: q }, ProfileEdge::Line { from: r, to: s }) => { + offer_line_crossing(*p, *q, *r, *s, tolerance, &mut offer); + } + (ProfileEdge::Line { from, to }, ProfileEdge::Arc { center, radius, .. }) + | (ProfileEdge::Arc { center, radius, .. }, ProfileEdge::Line { from, to }) => { + offer_line_circle_points(*from, *to, *center, *radius, tolerance, &mut offer); + } + ( + ProfileEdge::Arc { + center: ca, + radius: ra, + start_angle, + sweep_angle, + }, + ProfileEdge::Arc { + center: cb, + radius: rb, + .. + }, + ) => { + let d = (cb[0] - ca[0]).hypot(cb[1] - ca[1]); + if d <= tolerance && (ra - rb).abs() <= tolerance { + let (a0, a1) = a.endpoints(); + let (b0, b1) = b.endpoints(); + for point in [a0, a1, b0, b1] { + offer(point); + } + let midpoint_angle = start_angle + sweep_angle * 0.5; + offer([ + ca[0] + ra * midpoint_angle.cos(), + ca[1] + ra * midpoint_angle.sin(), + ]); + } else if d > tolerance + && d <= ra + rb + tolerance + && d + ra.min(*rb) + tolerance >= ra.max(*rb) + { + let along = (ra * ra - rb * rb + d * d) / (2.0 * d); + let perpendicular_sq = ra * ra - along * along; + if perpendicular_sq >= -tolerance * tolerance { + let ux = (cb[0] - ca[0]) / d; + let uy = (cb[1] - ca[1]) / d; + let base = [ca[0] + along * ux, ca[1] + along * uy]; + let off = perpendicular_sq.max(0.0).sqrt(); + offer([base[0] - off * uy, base[1] + off * ux]); + offer([base[0] + off * uy, base[1] - off * ux]); + } + } + } + } + candidates +} + +fn profile_edge_contains(edge: &ProfileEdge, point: [f64; 2], tolerance: f64) -> bool { + match edge { + ProfileEdge::Line { from, to } => { + let dx = to[0] - from[0]; + let dy = to[1] - from[1]; + let length = dx.hypot(dy); + let px = point[0] - from[0]; + let py = point[1] - from[1]; + (dx * py - dy * px).abs() <= tolerance * length + && px * dx + py * dy >= -tolerance * length + && px * dx + py * dy <= length * length + tolerance * length + } + ProfileEdge::Arc { + center, + radius, + start_angle, + sweep_angle, + } => { + let dx = point[0] - center[0]; + let dy = point[1] - center[1]; + if (dx.hypot(dy) - radius).abs() > tolerance { + return false; + } + let angle = dy.atan2(dx); + let travel = if *sweep_angle > 0.0 { + (angle - start_angle).rem_euclid(std::f64::consts::TAU) + } else { + (start_angle - angle).rem_euclid(std::f64::consts::TAU) + }; + travel <= sweep_angle.abs() + tolerance / radius + || std::f64::consts::TAU - travel <= tolerance / radius + } + } +} + +fn offer_line_crossing( + p: [f64; 2], + q: [f64; 2], + r: [f64; 2], + s: [f64; 2], + tolerance: f64, + offer: &mut impl FnMut([f64; 2]), +) { + let d = [q[0] - p[0], q[1] - p[1]]; + let e = [s[0] - r[0], s[1] - r[1]]; + let divisor = d[0] * e[1] - d[1] * e[0]; + if divisor.abs() > tolerance * (d[0].hypot(d[1]) + e[0].hypot(e[1])) { + let relative = [r[0] - p[0], r[1] - p[1]]; + let t = (relative[0] * e[1] - relative[1] * e[0]) / divisor; + offer([p[0] + t * d[0], p[1] + t * d[1]]); + } else { + for point in [p, q, r, s] { + offer(point); + } + } +} + +fn offer_line_circle_points( + from: [f64; 2], + to: [f64; 2], + center: [f64; 2], + radius: f64, + tolerance: f64, + offer: &mut impl FnMut([f64; 2]), +) { + let d = [to[0] - from[0], to[1] - from[1]]; + let f = [from[0] - center[0], from[1] - center[1]]; + let aa = d[0] * d[0] + d[1] * d[1]; + let bb = 2.0 * (f[0] * d[0] + f[1] * d[1]); + let cc = f[0] * f[0] + f[1] * f[1] - radius * radius; + let discriminant = bb * bb - 4.0 * aa * cc; + if discriminant >= -tolerance * tolerance * aa { + let root = discriminant.max(0.0).sqrt(); + for t in [(-bb - root) / (2.0 * aa), (-bb + root) / (2.0 * aa)] { + offer([from[0] + t * d[0], from[1] + t * d[1]]); + } + } +} + +pub(crate) fn arc_profile_contains(edges: &[ProfileEdge], point: [f64; 2], g: f64) -> bool { + let ring = edges + .iter() + .map(|edge| match edge { + ProfileEdge::Line { from, to } => CurveEdge2::Line { + from: *from, + to: *to, + }, + ProfileEdge::Arc { + center, + radius, + start_angle, + sweep_angle, + } => CurveEdge2::Arc { + center: *center, + radius: *radius, + start_angle: *start_angle, + sweep_angle: *sweep_angle, + }, + }) + .collect::>(); + winding(Pt2::new(point[0], point[1]), &ring, g) != 0 +} diff --git a/main/opengeometry/src/primitives/profile/mod.rs b/main/opengeometry/src/primitives/profile/mod.rs new file mode 100644 index 0000000..72d6d7f --- /dev/null +++ b/main/opengeometry/src/primitives/profile/mod.rs @@ -0,0 +1,8 @@ +mod arc; +mod polygon; + +pub(super) use arc::validate_arc_profile_loop; +pub use arc::ProfileEdge; +pub(crate) use arc::{arc_profile_contains, profile_edge_intersections}; +pub(crate) use polygon::{loops_touch, profile_point_inside}; +pub(super) use polygon::{orient_profile_loop, validate_profile_loop}; diff --git a/main/opengeometry/src/primitives/profile/polygon.rs b/main/opengeometry/src/primitives/profile/polygon.rs new file mode 100644 index 0000000..ee2dda5 --- /dev/null +++ b/main/opengeometry/src/primitives/profile/polygon.rs @@ -0,0 +1,105 @@ +use crate::brep::GeometryError; +use crate::geom2d::{point_in_ring2, segments_cross2, self_intersects2, Pt2}; + +fn signed_area(points: &[[f64; 2]]) -> f64 { + points + .iter() + .zip(points.iter().cycle().skip(1)) + .map(|(a, b)| a[0] * b[1] - b[0] * a[1]) + .sum::() + * 0.5 +} + +pub(crate) fn orient_profile_loop(mut points: Vec<[f64; 2]>, ccw: bool) -> Vec<[f64; 2]> { + if (signed_area(&points) > 0.0) != ccw { + points.reverse(); + } + points +} + +fn segment_distance(point: [f64; 2], a: [f64; 2], b: [f64; 2]) -> f64 { + let edge = [b[0] - a[0], b[1] - a[1]]; + let length_squared = edge[0].mul_add(edge[0], edge[1] * edge[1]); + if length_squared == 0.0 { + return (point[0] - a[0]).hypot(point[1] - a[1]); + } + let t = (((point[0] - a[0]) * edge[0] + (point[1] - a[1]) * edge[1]) / length_squared) + .clamp(0.0, 1.0); + (point[0] - (a[0] + t * edge[0])).hypot(point[1] - (a[1] + t * edge[1])) +} + +pub(crate) fn loops_touch(a: &[[f64; 2]], b: &[[f64; 2]], tolerance: f64) -> bool { + a.iter().any(|point| { + b.iter() + .zip(b.iter().cycle().skip(1)) + .any(|(from, to)| segment_distance(*point, *from, *to) <= tolerance) + }) || b.iter().any(|point| { + a.iter() + .zip(a.iter().cycle().skip(1)) + .any(|(from, to)| segment_distance(*point, *from, *to) <= tolerance) + }) || a.iter().zip(a.iter().cycle().skip(1)).any(|(a0, a1)| { + b.iter().zip(b.iter().cycle().skip(1)).any(|(b0, b1)| { + segments_cross2( + Pt2::new(a0[0], a0[1]), + Pt2::new(a1[0], a1[1]), + Pt2::new(b0[0], b0[1]), + Pt2::new(b1[0], b1[1]), + tolerance, + ) + }) + }) +} + +pub(crate) fn validate_profile_loop( + points: &[[f64; 2]], + tolerance: f64, + label: &str, +) -> Result<(), GeometryError> { + if points.len() < 3 { + return Err(GeometryError::InvalidGeometry(format!( + "{label} requires at least three vertices" + ))); + } + if points + .iter() + .flatten() + .any(|coordinate| !coordinate.is_finite()) + { + return Err(GeometryError::InvalidGeometry(format!( + "{label} contains a non-finite coordinate" + ))); + } + let resolution = 4.0 * tolerance; + if points + .iter() + .zip(points.iter().cycle().skip(1)) + .any(|(a, b)| (a[0] - b[0]).hypot(a[1] - b[1]) <= resolution) + { + return Err(GeometryError::UnresolvedIntersection(format!( + "{label} contains an edge below geometric resolution" + ))); + } + if signed_area(points).abs() <= resolution * resolution { + return Err(GeometryError::UnresolvedIntersection(format!( + "{label} area is below geometric resolution" + ))); + } + let planar = points + .iter() + .map(|point| Pt2::new(point[0], point[1])) + .collect::>(); + if self_intersects2(&planar, tolerance) { + return Err(GeometryError::InvalidGeometry(format!( + "{label} self-intersects" + ))); + } + Ok(()) +} + +pub(crate) fn profile_point_inside(point: [f64; 2], loop_points: &[[f64; 2]]) -> bool { + let ring = loop_points + .iter() + .map(|point| Pt2::new(point[0], point[1])) + .collect::>(); + point_in_ring2(Pt2::new(point[0], point[1]), &ring) +} diff --git a/main/opengeometry/src/primitives/rectangle.rs b/main/opengeometry/src/primitives/rectangle.rs index 1b9c3af..74a549f 100644 --- a/main/opengeometry/src/primitives/rectangle.rs +++ b/main/opengeometry/src/primitives/rectangle.rs @@ -1,259 +1,41 @@ -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; +use super::wire_points::wire_from_points; +use crate::brep::{dimensions, Accuracy, BrepEnvelope, Frame3, GeometryError}; -use crate::brep::{Brep, BrepBuilder}; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::operations::offset::{offset_path, OffsetOptions, OffsetResult}; -use crate::spatial::placement::Placement3D; -use crate::utility::bgeometry::BufferGeometry; -use openmaths::Vector3; -use uuid::Uuid; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGRectangle { +pub fn rectangle( id: String, - center: Vector3, + frame: Frame3, width: f64, breadth: f64, - placement: Placement3D, - geometry: BufferGeometry, - brep: Brep, -} - -#[wasm_bindgen] -impl OGRectangle { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGRectangle { - let internal_id = Uuid::new_v4(); - - OGRectangle { - id, - center: Vector3::new(0.0, 0.0, 0.0), - width: 1.0, - breadth: 1.0, - placement: Placement3D::new(), - geometry: BufferGeometry::new(internal_id), - brep: Brep::new(internal_id), - } - } - - #[wasm_bindgen] - pub fn set_config(&mut self, center: Vector3, width: f64, breadth: f64) -> Result<(), JsValue> { - self.center = center; - self.width = width; - self.breadth = breadth; - self.placement.set_anchor(self.center); - self.generate_geometry() - } - - #[wasm_bindgen] - pub fn set_center(&mut self, center: Vector3) { - self.center = center; - self.placement.set_anchor(self.center); - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - let points = self.get_local_points(); - - let mut builder = BrepBuilder::new(self.brep.id); - builder.add_vertices(&points); - - let indices = vec![0, 1, 2, 3]; - builder.add_face(&indices, &[]).map_err(|err| { - JsValue::from_str(&format!("Failed to build rectangle face: {}", err)) - })?; - - self.brep = builder.build().map_err(|err| { - JsValue::from_str(&format!("Failed to finalize rectangle BREP: {}", err)) - })?; - - Ok(()) - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - serde_json::to_string(&self.world_geometry_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&self.local_geometry_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - self.world_geometry_buffer() - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - self.local_geometry_buffer() - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } - - #[wasm_bindgen] - pub fn get_offset_serialized( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> String { - let result = self.get_offset_result(distance, acute_threshold_degrees, bevel); - serde_json::to_string(&result).unwrap() - } + accuracy: Accuracy, +) -> Result { + frame.validate()?; + dimensions(&[width, breadth])?; + if width <= 4.0 * accuracy.geometric || breadth <= 4.0 * accuracy.geometric { + return Err(GeometryError::InvalidGeometry( + "rectangle is below geometric resolution".into(), + )); + } + let half_width = width / 2.0; + let half_breadth = breadth / 2.0; + let points = [ + frame.point([half_width, -half_breadth, 0.0]), + frame.point([half_width, half_breadth, 0.0]), + frame.point([-half_width, half_breadth, 0.0]), + frame.point([-half_width, -half_breadth, 0.0]), + ]; + wire_from_points(id, &points, true, accuracy) } -impl OGRectangle { - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn get_raw_points(&self) -> Vec { - let half_width = self.width / 2.0; - let half_breadth = self.breadth / 2.0; - let center = self.center; - - vec![ - Vector3::new(-half_width, 0.0, -half_breadth).add(¢er), - Vector3::new(half_width, 0.0, -half_breadth).add(¢er), - Vector3::new(half_width, 0.0, half_breadth).add(¢er), - Vector3::new(-half_width, 0.0, half_breadth).add(¢er), - ] - } - - pub fn get_local_points(&self) -> Vec { - let half_width = self.width / 2.0; - let half_breadth = self.breadth / 2.0; - - vec![ - Vector3::new(-half_width, 0.0, -half_breadth), - Vector3::new(half_width, 0.0, -half_breadth), - Vector3::new(half_width, 0.0, half_breadth), - Vector3::new(-half_width, 0.0, half_breadth), - ] - } - - pub fn get_offset_result( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> OffsetResult { - let options = OffsetOptions { - bevel, - acute_threshold_degrees, - }; - let points = self.world_face_points(); - offset_path(&points, distance, Some(true), options) - } - - pub fn get_offset_points( - &self, - distance: f64, - acute_threshold_degrees: f64, - bevel: bool, - ) -> Vec { - self.get_offset_result(distance, acute_threshold_degrees, bevel) - .points - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let world_brep = self.world_brep(); - project_brep_to_scene(&world_brep, camera, hlr) - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } - - fn local_geometry_buffer(&self) -> Vec { - face_loop_buffer(&self.brep) - } - - fn world_geometry_buffer(&self) -> Vec { - face_loop_buffer(&self.world_brep()) - } - - fn world_face_points(&self) -> Vec { - let world_brep = self.world_brep(); - if let Some(face) = world_brep.faces.first() { - return world_brep.get_vertices_by_face_id(face.id); - } - - Vec::new() - } -} - -fn face_loop_buffer(brep: &Brep) -> Vec { - let mut vertex_buffer = Vec::new(); - - if let Some(face) = brep.faces.first() { - let mut vertices = brep.get_vertices_by_face_id(face.id); - if let Some(first) = vertices.first().copied() { - vertices.push(first); - } - - for vertex in vertices { - vertex_buffer.push(vertex.x); - vertex_buffer.push(vertex.y); - vertex_buffer.push(vertex.z); - } - } - - vertex_buffer +pub fn rectangle_with_keys( + id: String, + frame: Frame3, + width: f64, + breadth: f64, + accuracy: Accuracy, +) -> Result<(BrepEnvelope, Vec), GeometryError> { + let brep = rectangle(id, frame, width, breadth, accuracy)?; + let keys = (0..brep.topology.edges.len()) + .map(|index| format!("edge-{index}")) + .collect(); + Ok((brep, keys)) } diff --git a/main/opengeometry/src/primitives/revolved_section.rs b/main/opengeometry/src/primitives/revolved_section.rs new file mode 100644 index 0000000..85bd894 --- /dev/null +++ b/main/opengeometry/src/primitives/revolved_section.rs @@ -0,0 +1,192 @@ +use crate::brep::{ + boundary, dimensions, unit, uv_line, Accuracy, BrepEnvelope, Builder, CurveGeometry, + EdgeGeometry, Frame3, GeometryError, Orientation, PcurveGeometry, SurfaceGeometry, Use, +}; +use crate::math::{norm, scale, sub, Interval}; + +pub(super) struct SectionExtent { + pub(super) r0: f64, + pub(super) r1: f64, + pub(super) z0: f64, + pub(super) z1: f64, +} + +struct SectionTopology { + low: u32, + high: u32, + bottom: u32, + top: u32, + seam: u32, +} + +pub(super) fn section( + id: String, + frame: Frame3, + surface: SurfaceGeometry, + extent: &SectionExtent, + accuracy: Accuracy, +) -> Result { + let SectionExtent { r0, r1, z0, z1 } = *extent; + frame.validate()?; + surface.validate()?; + dimensions(&[r1, z1 - z0])?; + if !r0.is_finite() || r0 < 0.0 { + return Err(GeometryError::InvalidGeometry( + "negative lower radius".into(), + )); + } + let mut b = Builder::new(id, accuracy)?; + let low = b.vertex(frame.point([r0, 0.0, z0])); + let high = b.vertex(frame.point([r1, 0.0, z1])); + let tau = std::f64::consts::TAU; + let mut f0 = frame; + f0.origin = frame.point([0.0, 0.0, z0]); + let mut f1 = frame; + f1.origin = frame.point([0.0, 0.0, z1]); + let bottom = if r0 == 0.0 { + b.edge_geometry(EdgeGeometry::Collapsed { vertex: low }, true) + } else { + b.edge( + CurveGeometry::Circle { + frame: f0, + radius: r0, + }, + Interval::new(0.0, tau)?, + false, + ) + }; + let top = b.edge( + CurveGeometry::Circle { + frame: f1, + radius: r1, + }, + Interval::new(0.0, tau)?, + false, + ); + let p0 = frame.point([r0, 0.0, z0]); + let p1 = frame.point([r1, 0.0, z1]); + let length = norm(sub(p1, p0)); + let seam = b.edge( + CurveGeometry::Line { + origin: p0, + direction: unit(sub(p1, p0))?, + }, + Interval::new(0.0, length)?, + true, + ); + let topology = SectionTopology { + low, + high, + bottom, + top, + seam, + }; + b.face( + "lateral", + surface, + [[0.0, tau], [z0, z1]], + lateral_uses(&topology, r0, z0, z1, length), + )?; + if r0 > 0.0 { + add_lower_cap(&mut b, f0, bottom, low, r0)?; + } + add_upper_cap(&mut b, f1, top, high, r1)?; + b.finish_solid() +} + +fn lateral_uses(topology: &SectionTopology, r0: f64, z0: f64, z1: f64, length: f64) -> Vec { + let tau = std::f64::consts::TAU; + let &SectionTopology { + low, + high, + bottom, + top, + seam, + } = topology; + vec![ + boundary( + bottom, + low, + low, + Orientation::Forward, + if r0 == 0.0 { + uv_line([0.0, z0], [tau, 0.0]) + } else { + uv_line([0.0, z0], [1.0, 0.0]) + }, + ), + boundary( + seam, + low, + high, + Orientation::Forward, + uv_line([tau, z0], [0.0, (z1 - z0) / length]), + ), + boundary( + top, + high, + high, + Orientation::Reverse, + uv_line([0.0, z1], [1.0, 0.0]), + ), + boundary( + seam, + high, + low, + Orientation::Reverse, + uv_line([0.0, z0], [0.0, (z1 - z0) / length]), + ), + ] +} + +fn add_lower_cap( + b: &mut Builder, + mut f0: Frame3, + bottom: u32, + low: u32, + r0: f64, +) -> Result<(), GeometryError> { + f0.y = scale(f0.y, -1.0); + f0.z = scale(f0.z, -1.0); + b.face( + "lower_cap", + SurfaceGeometry::Plane { frame: f0 }, + [[-r0, r0]; 2], + vec![boundary( + bottom, + low, + low, + Orientation::Reverse, + PcurveGeometry::Conic2 { + origin: [0.0; 2], + axis_a: [r0, 0.0], + axis_b: [0.0, -r0], + }, + )], + ) +} + +fn add_upper_cap( + b: &mut Builder, + f1: Frame3, + top: u32, + high: u32, + r1: f64, +) -> Result<(), GeometryError> { + b.face( + "upper_cap", + SurfaceGeometry::Plane { frame: f1 }, + [[-r1, r1]; 2], + vec![boundary( + top, + high, + high, + Orientation::Forward, + PcurveGeometry::Conic2 { + origin: [0.0; 2], + axis_a: [r1, 0.0], + axis_b: [0.0, r1], + }, + )], + ) +} diff --git a/main/opengeometry/src/primitives/sphere.rs b/main/opengeometry/src/primitives/sphere.rs deleted file mode 100644 index 94900c3..0000000 --- a/main/opengeometry/src/primitives/sphere.rs +++ /dev/null @@ -1,274 +0,0 @@ -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; - -use crate::brep::{Brep, BrepBuilder}; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::spatial::placement::Placement3D; -use openmaths::Vector3; -use uuid::Uuid; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGSphere { - id: String, - center: Vector3, - radius: f64, - width_segments: u32, - height_segments: u32, - placement: Placement3D, - brep: Brep, -} - -#[wasm_bindgen] -impl OGSphere { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGSphere { - let internal_id = Uuid::new_v4(); - - OGSphere { - id, - center: Vector3::new(0.0, 0.0, 0.0), - radius: 1.0, - width_segments: 24, - height_segments: 16, - placement: Placement3D::new(), - brep: Brep::new(internal_id), - } - } - - #[wasm_bindgen] - pub fn set_config( - &mut self, - center: Vector3, - radius: f64, - width_segments: u32, - height_segments: u32, - ) -> Result<(), JsValue> { - self.center = center; - self.radius = radius.max(1.0e-6); - self.width_segments = width_segments.max(3); - self.height_segments = height_segments.max(2); - self.placement.set_anchor(self.center); - - self.generate_brep() - } - - #[wasm_bindgen] - pub fn set_center(&mut self, center: Vector3) { - self.center = center; - self.placement.set_anchor(self.center); - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - pub fn generate_brep(&mut self) -> Result<(), JsValue> { - self.clean_geometry(); - self.generate_geometry() - } - - pub fn clean_geometry(&mut self) { - self.brep.clear(); - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - let width = self.width_segments.max(3) as usize; - let height = self.height_segments.max(2) as usize; - - let mut vertices = Vec::new(); - // Top pole. - vertices.push(Vector3::new(0.0, self.radius, 0.0)); - - // Intermediate rings. - for iy in 1..height { - let v = iy as f64 / height as f64; - let theta = v * std::f64::consts::PI; - let sin_theta = theta.sin(); - let cos_theta = theta.cos(); - - for ix in 0..width { - let u = ix as f64 / width as f64; - let phi = u * std::f64::consts::PI * 2.0; - let sin_phi = phi.sin(); - let cos_phi = phi.cos(); - - vertices.push(Vector3::new( - self.radius * sin_theta * cos_phi, - self.radius * cos_theta, - self.radius * sin_theta * sin_phi, - )); - } - } - - let bottom_id = vertices.len() as u32; - vertices.push(Vector3::new(0.0, -self.radius, 0.0)); - - let ring_vertex = |ring: usize, ix: usize| -> u32 { - // ring is 1..height-1 - 1 + ((ring - 1) * width + ix) as u32 - }; - - let mut faces: Vec> = Vec::new(); - - // Top cap. - for ix in 0..width { - let next = (ix + 1) % width; - faces.push(vec![0, ring_vertex(1, next), ring_vertex(1, ix)]); - } - - // Body quads split into triangles. - if height > 2 { - for ring in 1..(height - 1) { - for ix in 0..width { - let next = (ix + 1) % width; - let a = ring_vertex(ring, ix); - let b = ring_vertex(ring, next); - let c = ring_vertex(ring + 1, next); - let d = ring_vertex(ring + 1, ix); - - faces.push(vec![a, b, c]); - faces.push(vec![a, c, d]); - } - } - } - - // Bottom cap. - let last_ring = height - 1; - for ix in 0..width { - let next = (ix + 1) % width; - faces.push(vec![ - bottom_id, - ring_vertex(last_ring, ix), - ring_vertex(last_ring, next), - ]); - } - - let mut builder = BrepBuilder::new(self.brep.id); - builder.add_vertices(&vertices); - - for face in &faces { - builder.add_face(face, &[]).map_err(|err| { - JsValue::from_str(&format!("Failed to build sphere face: {}", err)) - })?; - } - - builder - .add_shell_from_all_faces(true) - .map_err(|err| JsValue::from_str(&format!("Failed to build sphere shell: {}", err)))?; - - self.brep = builder.build().map_err(|err| { - JsValue::from_str(&format!("Failed to finalize sphere BREP: {}", err)) - })?; - - Ok(()) - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - let world_brep = self.world_brep(); - serde_json::to_string(&world_brep.get_triangle_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&self.brep.get_triangle_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - self.world_brep().get_triangle_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - self.brep.get_triangle_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_serialized(&self) -> String { - let world_brep = self.world_brep(); - serde_json::to_string(&world_brep.get_outline_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_serialized(&self) -> String { - serde_json::to_string(&self.brep.get_outline_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_buffer(&self) -> Vec { - self.world_brep().get_outline_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_buffer(&self) -> Vec { - self.brep.get_outline_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } -} - -impl OGSphere { - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let world_brep = self.world_brep(); - project_brep_to_scene(&world_brep, camera, hlr) - } -} diff --git a/main/opengeometry/src/primitives/sweep.rs b/main/opengeometry/src/primitives/sweep.rs deleted file mode 100644 index d1f3fe4..0000000 --- a/main/opengeometry/src/primitives/sweep.rs +++ /dev/null @@ -1,345 +0,0 @@ -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; - -use crate::brep::Brep; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::operations::sweep::{sweep_profile_along_path, SweepOptions}; -use crate::primitives::line::OGLine; -use crate::primitives::polyline::OGPolyline; -use crate::primitives::rectangle::OGRectangle; -use crate::spatial::placement::{ - bounds_center_from_point_sets, points_relative_to_anchor, Placement3D, -}; -use openmaths::Vector3; -use uuid::Uuid; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGSweep { - id: String, - path_points: Vec, - profile_points: Vec, - cap_start: bool, - cap_end: bool, - placement: Placement3D, - brep: Brep, - anchor_initialized: bool, -} - -#[wasm_bindgen] -impl OGSweep { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGSweep { - let internal_id = Uuid::new_v4(); - - OGSweep { - id, - path_points: Vec::new(), - profile_points: Vec::new(), - cap_start: true, - cap_end: true, - placement: Placement3D::new(), - brep: Brep::new(internal_id), - anchor_initialized: false, - } - } - - #[wasm_bindgen] - pub fn set_config( - &mut self, - path_points: Vec, - profile_points: Vec, - ) -> Result<(), JsValue> { - self.path_points = path_points; - self.profile_points = profile_points; - self.cap_start = true; - self.cap_end = true; - self.ensure_anchor_initialized(); - self.generate_brep() - } - - #[wasm_bindgen] - pub fn set_config_with_caps( - &mut self, - path_points: Vec, - profile_points: Vec, - cap_start: bool, - cap_end: bool, - ) -> Result<(), JsValue> { - self.path_points = path_points; - self.profile_points = profile_points; - self.cap_start = cap_start; - self.cap_end = cap_end; - self.ensure_anchor_initialized(); - self.generate_brep() - } - - #[wasm_bindgen] - pub fn set_caps(&mut self, cap_start: bool, cap_end: bool) -> Result<(), JsValue> { - self.cap_start = cap_start; - self.cap_end = cap_end; - self.generate_brep() - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - pub fn generate_brep(&mut self) -> Result<(), JsValue> { - self.clean_geometry(); - self.generate_geometry() - } - - pub fn clean_geometry(&mut self) { - self.brep.clear(); - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - let options = SweepOptions { - cap_start: self.cap_start, - cap_end: self.cap_end, - }; - - let local_path_points = points_relative_to_anchor(&self.path_points, self.placement.anchor); - let local_profile_points = - points_relative_to_anchor(&self.profile_points, self.placement.anchor); - - self.brep = sweep_profile_along_path(&local_path_points, &local_profile_points, options) - .map_err(|err| JsValue::from_str(&format!("Sweep generation failed: {}", err)))?; - self.brep - .validate_topology() - .map_err(|err| JsValue::from_str(&format!("Invalid sweep topology: {}", err)))?; - - Ok(()) - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - let world_brep = self.world_brep(); - serde_json::to_string(&world_brep.get_triangle_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&self.brep.get_triangle_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - self.world_brep().get_triangle_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - self.brep.get_triangle_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_serialized(&self) -> String { - let world_brep = self.world_brep(); - serde_json::to_string(&world_brep.get_outline_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_serialized(&self) -> String { - serde_json::to_string(&self.brep.get_outline_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_buffer(&self) -> Vec { - self.world_brep().get_outline_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_buffer(&self) -> Vec { - self.brep.get_outline_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } - - #[wasm_bindgen] - pub fn set_anchor(&mut self, anchor: Vector3) -> Result<(), JsValue> { - self.placement.set_anchor(anchor); - self.anchor_initialized = true; - self.generate_brep() - } - - #[wasm_bindgen] - pub fn reset_anchor(&mut self) -> Result<(), JsValue> { - self.recompute_anchor_from_bounds(); - self.anchor_initialized = true; - self.generate_brep() - } -} - -impl OGSweep { - pub fn set_path_from_polyline(&mut self, polyline: &OGPolyline) -> Result<(), JsValue> { - self.path_points = wire_points_from_brep(&polyline.world_brep()); - self.ensure_anchor_initialized(); - self.generate_brep() - } - - pub fn set_path_from_line(&mut self, line: &OGLine) -> Result<(), JsValue> { - self.path_points = wire_points_from_brep(&line.world_brep()); - self.ensure_anchor_initialized(); - self.generate_brep() - } - - pub fn set_profile_from_polyline(&mut self, profile: &OGPolyline) -> Result<(), JsValue> { - self.profile_points = wire_points_from_brep(&profile.world_brep()); - self.ensure_anchor_initialized(); - self.generate_brep() - } - - pub fn set_profile_from_rectangle(&mut self, rectangle: &OGRectangle) -> Result<(), JsValue> { - self.profile_points = face_points_from_brep(&rectangle.world_brep()); - self.ensure_anchor_initialized(); - self.generate_brep() - } - - pub fn path_points(&self) -> Vec { - self.path_points.clone() - } - - pub fn profile_points(&self) -> Vec { - self.profile_points.clone() - } - - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let world_brep = self.world_brep(); - project_brep_to_scene(&world_brep, camera, hlr) - } - - fn recompute_anchor_from_bounds(&mut self) { - let point_sets = [self.path_points.as_slice(), self.profile_points.as_slice()]; - let anchor = - bounds_center_from_point_sets(&point_sets).unwrap_or(Vector3::new(0.0, 0.0, 0.0)); - self.placement.set_anchor(anchor); - } - - fn ensure_anchor_initialized(&mut self) { - if self.anchor_initialized { - return; - } - self.recompute_anchor_from_bounds(); - self.anchor_initialized = true; - } -} - -fn wire_points_from_brep(brep: &Brep) -> Vec { - let Some(wire) = brep.wires.first() else { - return Vec::new(); - }; - - brep.get_wire_vertex_indices(wire.id) - .into_iter() - .filter_map(|vertex_id| brep.vertices.get(vertex_id as usize)) - .map(|vertex| vertex.position) - .collect() -} - -fn face_points_from_brep(brep: &Brep) -> Vec { - let Some(face) = brep.faces.first() else { - return Vec::new(); - }; - - brep.get_vertices_by_face_id(face.id) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn anchor_stays_stable_across_sweep_config_updates_until_reset() { - let mut sweep = OGSweep::new("sweep-anchor".to_string()); - sweep - .set_config( - vec![Vector3::new(0.0, 0.0, 0.0), Vector3::new(10.0, 0.0, 0.0)], - vec![ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 1.0), - Vector3::new(0.0, 1.0, 1.0), - Vector3::new(0.0, 1.0, 0.0), - ], - ) - .expect("sweep config"); - let initial_anchor = sweep.get_anchor(); - assert_eq!(initial_anchor.x, 5.0); - - sweep - .set_config( - vec![Vector3::new(10.0, 0.0, 0.0), Vector3::new(20.0, 0.0, 0.0)], - vec![ - Vector3::new(10.0, 0.0, 0.0), - Vector3::new(10.0, 0.0, 1.0), - Vector3::new(10.0, 1.0, 1.0), - Vector3::new(10.0, 1.0, 0.0), - ], - ) - .expect("sweep config update"); - let anchor_after_update = sweep.get_anchor(); - assert_eq!(anchor_after_update.x, 5.0); - - sweep.reset_anchor().expect("reset anchor"); - let anchor_after_reset = sweep.get_anchor(); - assert_eq!(anchor_after_reset.x, 15.0); - } -} diff --git a/main/opengeometry/src/primitives/tests/extrusions.rs b/main/opengeometry/src/primitives/tests/extrusions.rs new file mode 100644 index 0000000..56718fc --- /dev/null +++ b/main/opengeometry/src/primitives/tests/extrusions.rs @@ -0,0 +1,350 @@ +use crate::brep::fine_accuracy; +use crate::brep::{CurveGeometry, Frame3, SurfaceGeometry}; +use crate::primitives::internal::{ + arc_edged_extrusion, arc_edged_extrusion_with_holes, linear_extrusion, +}; +use crate::primitives::profile::ProfileEdge; +use crate::query::{classify_point, PointClassification}; +use crate::tessellation::tessellate; + +#[test] +fn linear_extrusion_preserves_planar_holes_and_shared_edges() { + let outer = vec![[0.0, 0.0], [5.0, 0.0], [5.0, 4.0], [0.0, 4.0]]; + let hole = vec![[1.0, 1.0], [1.0, 3.0], [3.0, 3.0], [3.0, 1.0]]; + let body = linear_extrusion( + "profile".into(), + Frame3::IDENTITY, + outer, + vec![hole], + 2.5, + fine_accuracy(), + ) + .unwrap(); + + body.validate().unwrap(); + assert_eq!(body.topology.vertices.len(), 16); + assert_eq!(body.topology.edges.len(), 24); + assert_eq!(body.topology.faces.len(), 10); + assert_eq!(body.topology.shells.len(), 1); + assert_eq!(body.solids.len(), 1); + assert_eq!(body.topology.faces[0].trim.holes.len(), 1); + assert_eq!(body.topology.faces[1].trim.holes.len(), 1); + assert!(body + .topology + .edges + .iter() + .all(|edge| edge.twin_halfedge.is_some())); + let tessellation = tessellate(&body, 0.01, 100_000).unwrap(); + assert!(!tessellation.indices.is_empty()); + assert_eq!( + tessellation.triangle_face_ids.len(), + tessellation.indices.len() / 3 + ); +} +#[test] +fn arc_edged_extrusion_preserves_cylindrical_sides_for_both_sweep_signs() { + for sign in [1.0, -1.0] { + let sweep = sign * std::f64::consts::FRAC_PI_2; + let outer = 2.0; + let inner = 1.5; + let point = |radius: f64, angle: f64| [radius * angle.cos(), radius * angle.sin()]; + let edges = vec![ + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: outer, + start_angle: 0.0, + sweep_angle: sweep, + }, + ProfileEdge::Line { + from: point(outer, sweep), + to: point(inner, sweep), + }, + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: inner, + start_angle: sweep, + sweep_angle: -sweep, + }, + ProfileEdge::Line { + from: point(inner, 0.0), + to: point(outer, 0.0), + }, + ]; + let body = arc_edged_extrusion( + format!("arc-strip-{sign}"), + Frame3::IDENTITY, + edges, + 3.0, + fine_accuracy(), + ) + .unwrap(); + body.validate().unwrap(); + assert_eq!(body.topology.faces.len(), 6); + assert_eq!(body.topology.shells.len(), 1); + assert_eq!(body.solids.len(), 1); + assert_eq!( + body.geometry + .surfaces + .iter() + .filter(|surface| matches!(surface, SurfaceGeometry::Cylinder { .. })) + .count(), + 2 + ); + assert_eq!( + body.geometry + .curves + .iter() + .filter(|curve| matches!(curve, CurveGeometry::Circle { .. })) + .count(), + 4 + ); + let middle = sweep * 0.5; + assert_eq!( + classify_point(&body, [1.75 * middle.cos(), 1.75 * middle.sin(), 1.0],).unwrap(), + PointClassification::Inside, + ); + assert_eq!( + classify_point(&body, [1.0 * middle.cos(), 1.0 * middle.sin(), 1.0],).unwrap(), + PointClassification::Outside, + ); + let mesh = tessellate(&body, 0.01, 100_000).unwrap(); + assert!(!mesh.indices.is_empty()); + } +} +#[test] +fn arc_edged_extrusion_preserves_an_analytic_profile_hole() { + let quarter = std::f64::consts::FRAC_PI_2; + let outer = vec![ + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 2.0, + start_angle: 0.0, + sweep_angle: quarter, + }, + ProfileEdge::Line { + from: [0.0, 2.0], + to: [0.0, 1.0], + }, + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.0, + start_angle: quarter, + sweep_angle: -quarter, + }, + ProfileEdge::Line { + from: [1.0, 0.0], + to: [2.0, 0.0], + }, + ]; + let hole = vec![ + ProfileEdge::Arc { + center: [1.2, 1.2], + radius: 0.1, + start_angle: 0.0, + sweep_angle: -std::f64::consts::PI, + }, + ProfileEdge::Arc { + center: [1.2, 1.2], + radius: 0.1, + start_angle: -std::f64::consts::PI, + sweep_angle: -std::f64::consts::PI, + }, + ]; + let body = arc_edged_extrusion_with_holes( + "arc-profile-hole".into(), + Frame3::IDENTITY, + outer, + vec![hole], + 3.0, + fine_accuracy(), + ) + .unwrap(); + body.validate().unwrap(); + assert_eq!(body.topology.faces[0].trim.holes.len(), 1); + assert_eq!(body.topology.faces[1].trim.holes.len(), 1); + assert_eq!(body.topology.shells.len(), 1); + assert_eq!(body.solids.len(), 1); + assert_eq!( + classify_point(&body, [1.2, 1.2, 1.0]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&body, [1.4, 1.0, 1.0]).unwrap(), + PointClassification::Inside + ); + assert!(!tessellate(&body, 0.01, 100_000).unwrap().indices.is_empty()); +} +#[test] +fn arc_edged_extrusion_rejects_holes_outside_or_crossing_its_profile() { + let quarter = std::f64::consts::FRAC_PI_2; + let outer = vec![ + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 2.0, + start_angle: 0.0, + sweep_angle: quarter, + }, + ProfileEdge::Line { + from: [0.0, 2.0], + to: [0.0, 1.0], + }, + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.0, + start_angle: quarter, + sweep_angle: -quarter, + }, + ProfileEdge::Line { + from: [1.0, 0.0], + to: [2.0, 0.0], + }, + ]; + let square = |x0, y0, x1, y1| { + let points = [[x0, y0], [x1, y0], [x1, y1], [x0, y1]]; + (0..4) + .map(|index| ProfileEdge::Line { + from: points[index], + to: points[(index + 1) % 4], + }) + .collect::>() + }; + for hole in [square(3.0, 3.0, 3.2, 3.2), square(1.9, 0.1, 2.1, 0.3)] { + assert!(arc_edged_extrusion_with_holes( + "invalid-arc-hole".into(), + Frame3::IDENTITY, + outer.clone(), + vec![hole], + 3.0, + fine_accuracy(), + ) + .is_err()); + } +} +#[test] +fn arc_edged_extrusion_rejects_exact_line_arc_crossings_and_overlap() { + let crossing = vec![ + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.0, + start_angle: 0.0, + sweep_angle: std::f64::consts::PI, + }, + ProfileEdge::Line { + from: [-1.0, 0.0], + to: [0.0, 1.2], + }, + ProfileEdge::Line { + from: [0.0, 1.2], + to: [1.0, 0.0], + }, + ]; + assert!(arc_edged_extrusion( + "crossing".into(), + Frame3::IDENTITY, + crossing, + 2.0, + fine_accuracy(), + ) + .is_err()); + let overlapping = vec![ + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.0, + start_angle: 0.0, + sweep_angle: std::f64::consts::PI, + }, + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.0, + start_angle: std::f64::consts::PI, + sweep_angle: -std::f64::consts::FRAC_PI_2, + }, + ProfileEdge::Line { + from: [0.0, 1.0], + to: [1.0, 0.0], + }, + ]; + assert!(arc_edged_extrusion( + "overlap".into(), + Frame3::IDENTITY, + overlapping, + 2.0, + fine_accuracy(), + ) + .is_err()); +} +#[test] +fn arc_edged_extrusion_accepts_nonradial_join_caps() { + let outer_end = std::f64::consts::FRAC_PI_2; + let inner_start = outer_end - 0.1; + let inner_end = 0.1; + let point = |radius: f64, angle: f64| [radius * angle.cos(), radius * angle.sin()]; + let profile = vec![ + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 2.0, + start_angle: 0.0, + sweep_angle: outer_end, + }, + ProfileEdge::Line { + from: point(2.0, outer_end), + to: point(1.5, inner_start), + }, + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 1.5, + start_angle: inner_start, + sweep_angle: inner_end - inner_start, + }, + ProfileEdge::Line { + from: point(1.5, inner_end), + to: point(2.0, 0.0), + }, + ]; + let body = arc_edged_extrusion( + "curved-join".into(), + Frame3::IDENTITY, + profile, + 2.4, + fine_accuracy(), + ) + .unwrap(); + body.validate().unwrap(); + assert_eq!(body.topology.faces.len(), 6); + assert_eq!(body.solids.len(), 1); +} +#[test] +fn linear_extrusion_rejects_invalid_profile_relationships() { + let square = vec![[0.0, 0.0], [4.0, 0.0], [4.0, 4.0], [0.0, 4.0]]; + let crossing = vec![[1.0, 1.0], [3.0, 3.0], [1.0, 3.0], [3.0, 1.0]]; + assert!(linear_extrusion( + "crossing".into(), + Frame3::IDENTITY, + crossing, + Vec::new(), + 1.0, + fine_accuracy(), + ) + .is_err()); + let outside = vec![[3.0, 1.0], [5.0, 1.0], [5.0, 2.0], [3.0, 2.0]]; + assert!(linear_extrusion( + "outside".into(), + Frame3::IDENTITY, + square.clone(), + vec![outside], + 1.0, + fine_accuracy(), + ) + .is_err()); + let first = vec![[0.5, 0.5], [2.5, 0.5], [2.5, 2.5], [0.5, 2.5]]; + let second = vec![[1.5, 1.5], [3.0, 1.5], [3.0, 3.0], [1.5, 3.0]]; + assert!(linear_extrusion( + "overlap".into(), + Frame3::IDENTITY, + square, + vec![first, second], + 1.0, + fine_accuracy(), + ) + .is_err()); +} diff --git a/main/opengeometry/src/primitives/tests/mod.rs b/main/opengeometry/src/primitives/tests/mod.rs new file mode 100644 index 0000000..1b07c15 --- /dev/null +++ b/main/opengeometry/src/primitives/tests/mod.rs @@ -0,0 +1,3 @@ +mod extrusions; +mod solids; +mod wires; diff --git a/main/opengeometry/src/primitives/tests/solids.rs b/main/opengeometry/src/primitives/tests/solids.rs new file mode 100644 index 0000000..1ea8274 --- /dev/null +++ b/main/opengeometry/src/primitives/tests/solids.rs @@ -0,0 +1,169 @@ +use crate::brep::fine_accuracy; +use crate::brep::{BrepEnvelope, Frame3, Orientation, Surface, SurfaceGeometry}; +use crate::math::{dot, sub}; +use crate::primitives::cuboid::cuboid; +use crate::primitives::cylinder::cylinder; +use crate::primitives::internal::{ + annular_cylinder, cone, cylinder_sector, frustum, sphere, torus, +}; +use crate::tessellation::tessellate; + +#[test] +fn cuboid_edges_are_shared_and_all_face_normals_point_outward() { + let frame = Frame3 { + origin: [1.0, 2.0, 3.0], + x: [0.0, 1.0, 0.0], + y: [-1.0, 0.0, 0.0], + ..Frame3::IDENTITY + }; + let body = cuboid("box".into(), frame, [2.0, 3.0, 4.0], fine_accuracy()).unwrap(); + assert_eq!( + ( + body.topology.vertices.len(), + body.topology.edges.len(), + body.topology.faces.len() + ), + (8, 12, 6) + ); + let center = frame.point([1.0, 1.5, 2.0]); + for face in &body.topology.faces { + let surface = body.geometry.surface(face.surface).unwrap(); + let uv = face.trim.uv_bounds.map(|range| (range.lo + range.hi) / 2.0); + assert!( + dot( + sub(surface.point_at(uv).unwrap(), center), + surface.normal_at(uv).unwrap() + ) > 0.0 + ); + } + let serialized = body.to_json().unwrap(); + assert_eq!( + BrepEnvelope::from_json(&serialized) + .unwrap() + .to_json() + .unwrap(), + serialized + ); + assert!(cuboid("bad".into(), frame, [0.0, 1.0, 1.0], fine_accuracy()).is_err()); +} +#[test] +fn curved_topology_is_sparse_and_closed() { + let shapes = [ + cylinder("c".into(), Frame3::IDENTITY, 1.0, 2.0, fine_accuracy()).unwrap(), + cone("k".into(), Frame3::IDENTITY, 1.0, 2.0, fine_accuracy()).unwrap(), + sphere("s".into(), Frame3::IDENTITY, 1.0, fine_accuracy()).unwrap(), + torus("t".into(), Frame3::IDENTITY, 3.0, 1.0, fine_accuracy()).unwrap(), + ]; + for (b, faces) in shapes.into_iter().zip([3, 2, 1, 1]) { + b.validate().unwrap(); + assert_eq!(b.topology.faces.len(), faces); + assert!(b.topology.vertices.len() <= 2); + assert_eq!(b.solids.len(), 1); + let json = b.to_json().unwrap(); + BrepEnvelope::from_json(&json).unwrap(); + } +} +#[test] +fn annular_cylinder_has_exact_inner_outer_supports_and_planar_holes() { + let body = annular_cylinder( + "tube".into(), + Frame3::IDENTITY, + 0.5, + 1.0, + 2.0, + fine_accuracy(), + ) + .unwrap(); + body.validate().unwrap(); + assert_eq!(body.topology.faces.len(), 4); + assert_eq!(body.topology.shells.len(), 1); + assert_eq!(body.solids.len(), 1); + assert_eq!(body.topology.faces[0].sense, Orientation::Forward); + assert_eq!(body.topology.faces[1].sense, Orientation::Reverse); + assert_eq!(body.topology.faces[2].trim.holes.len(), 1); + assert_eq!(body.topology.faces[3].trim.holes.len(), 1); + let mesh = tessellate(&body, 0.01, 100_000).unwrap(); + assert!(!mesh.indices.is_empty()); + assert!(annular_cylinder( + "thin".into(), + Frame3::IDENTITY, + 1.0, + 1.0 + fine_accuracy().geometric, + 2.0, + fine_accuracy(), + ) + .is_err()); +} +#[test] +fn frusta_reduce_to_the_supported_surface_set() { + for radii in [(1.0, 2.0), (2.0, 1.0), (2.0, 0.0), (1.0, 1.0)] { + let b = frustum( + "f".into(), + Frame3::IDENTITY, + radii.0, + radii.1, + 3.0, + fine_accuracy(), + ) + .unwrap(); + b.validate().unwrap(); + assert_eq!(b.topology.faces.len(), if radii.1 == 0.0 { 2 } else { 3 }); + } +} +#[test] +fn ring_and_positive_dimensions_are_enforced() { + assert!(torus("t".into(), Frame3::IDENTITY, 1.0, 1.0, fine_accuracy()).is_err()); + assert!(cylinder("c".into(), Frame3::IDENTITY, 1.0, 0.0, fine_accuracy()).is_err()); +} +#[test] +fn body_bounds_cover_curved_extrema_absent_from_vertices() { + let b = cylinder("c".into(), Frame3::IDENTITY, 2.0, 3.0, fine_accuracy()).unwrap(); + assert!(b.topology.vertices.iter().all(|v| v.position[0] == 2.0)); + let bounds = b.bounds().unwrap().unwrap(); + for p in [[-2.0, 0.0, 1.5], [0.0, -2.0, 1.5], [0.0, 2.0, 3.0]] { + assert!(bounds.contains(p)); + } + let empty = BrepEnvelope::new("empty".into(), fine_accuracy()).unwrap(); + assert!(empty.bounds().unwrap().is_none()); +} +#[test] +fn cylinder_sector_keeps_one_trimmed_cylinder_face_and_exact_radial_caps() { + let sector = cylinder_sector( + "sector".into(), + Frame3::IDENTITY, + 2.0, + 3.0, + -1.2, + 1.2, + fine_accuracy(), + ) + .unwrap(); + sector.validate().unwrap(); + assert_eq!(sector.topology.faces.len(), 5); + assert_eq!(sector.topology.edges.len(), 9); + assert_eq!(sector.topology.vertices.len(), 6); + assert!(matches!( + sector.geometry.surfaces[sector.topology.faces[0].surface as usize], + SurfaceGeometry::Cylinder { radius: 2.0, .. } + )); + assert!(sector.topology.faces[1..].iter().all(|face| matches!( + sector.geometry.surfaces[face.surface as usize], + SurfaceGeometry::Plane { .. } + ))); + let coarse = tessellate(§or, 0.05, 100_000).unwrap(); + let fine = tessellate(§or, 0.005, 100_000).unwrap(); + assert!(fine.indices.len() > coarse.indices.len()); + BrepEnvelope::from_json(§or.to_json().unwrap()).unwrap(); + + let full = cylinder_sector( + "full".into(), + Frame3::IDENTITY, + 2.0, + 3.0, + 0.0, + std::f64::consts::TAU, + fine_accuracy(), + ) + .unwrap(); + assert_eq!(full.topology.faces.len(), 3); +} diff --git a/main/opengeometry/src/primitives/tests/wires.rs b/main/opengeometry/src/primitives/tests/wires.rs new file mode 100644 index 0000000..22fdd69 --- /dev/null +++ b/main/opengeometry/src/primitives/tests/wires.rs @@ -0,0 +1,64 @@ +use crate::brep::fine_accuracy; +use crate::brep::{BrepEnvelope, CurveGeometry, Frame3}; +use crate::math::{norm, sub}; +use crate::primitives::circle::arc_wire; +use crate::tessellation::tessellate; + +#[test] +fn analytic_arc_wires_retain_signed_endpoints_and_single_edge_authority() { + let curve = CurveGeometry::Circle { + frame: Frame3::IDENTITY, + radius: 2.0, + }; + for sweep in [1.4, -1.4, std::f64::consts::TAU, -std::f64::consts::TAU] { + let brep = arc_wire("arc".into(), curve.clone(), 0.7, sweep, fine_accuracy()).unwrap(); + let h = &brep.topology.halfedges[0]; + assert_eq!(h.geometry_use.sense.multiplier(), sweep.signum()); + assert!( + norm(sub( + brep.topology.vertices[h.from as usize].position, + curve.elementary_point(0.7).unwrap() + )) < 1e-10 + ); + assert_eq!(brep.topology.edges.len(), 1); + assert!(brep.topology.faces.is_empty()); + assert!(h.geometry_use.pcurve.is_none()); + assert_eq!( + brep.topology.wires[0].is_closed, + sweep.abs() == std::f64::consts::TAU + ); + let coarse = tessellate(&brep, 0.05, 100_000).unwrap(); + let fine = tessellate(&brep, 0.005, 100_000).unwrap(); + assert!(fine.outline_edge_ids.len() > coarse.outline_edge_ids.len()); + assert!(fine.outline_edge_ids.iter().all(|id| *id == 0)); + assert!(fine.indices.is_empty()); + BrepEnvelope::from_json(&brep.to_json().unwrap()).unwrap(); + } +} +#[test] +fn elliptical_arc_sampling_meets_deflection_and_rejects_invalid_spans() { + let curve = CurveGeometry::Ellipse { + frame: Frame3::IDENTITY, + major_radius: 3.0, + minor_radius: 0.5, + }; + let brep = arc_wire( + "ellipse".into(), + curve.clone(), + 0.0, + std::f64::consts::TAU, + fine_accuracy(), + ) + .unwrap(); + let mesh = tessellate(&brep, 0.01, 100_000).unwrap(); + let n = mesh.outline_edge_ids.len(); + for (i, pair) in mesh.outline_positions.chunks_exact(6).enumerate() { + let midpoint = curve + .elementary_point(std::f64::consts::TAU * (i as f64 + 0.5) / n as f64) + .unwrap(); + let chord = std::array::from_fn(|j| (pair[j] + pair[j + 3]) / 2.0); + assert!(norm(sub(midpoint, chord)) < 0.01); + } + assert!(arc_wire("bad".into(), curve.clone(), 0.0, 0.0, fine_accuracy()).is_err()); + assert!(arc_wire("bad".into(), curve, 0.0, 7.0, fine_accuracy()).is_err()); +} diff --git a/main/opengeometry/src/primitives/wedge.rs b/main/opengeometry/src/primitives/wedge.rs deleted file mode 100644 index ec9d266..0000000 --- a/main/opengeometry/src/primitives/wedge.rs +++ /dev/null @@ -1,230 +0,0 @@ -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; - -use crate::brep::{Brep, BrepBuilder}; -use crate::export::projection::{project_brep_to_scene, CameraParameters, HlrOptions, Scene2D}; -use crate::spatial::placement::Placement3D; -use openmaths::Vector3; -use uuid::Uuid; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct OGWedge { - id: String, - center: Vector3, - width: f64, - height: f64, - depth: f64, - placement: Placement3D, - brep: Brep, -} - -#[wasm_bindgen] -impl OGWedge { - #[wasm_bindgen(setter)] - pub fn set_id(&mut self, id: String) { - self.id = id; - } - - #[wasm_bindgen(getter)] - pub fn id(&self) -> String { - self.id.clone() - } - - #[wasm_bindgen(constructor)] - pub fn new(id: String) -> OGWedge { - let internal_id = Uuid::new_v4(); - - OGWedge { - id, - center: Vector3::new(0.0, 0.0, 0.0), - width: 1.0, - height: 1.0, - depth: 1.0, - placement: Placement3D::new(), - brep: Brep::new(internal_id), - } - } - - #[wasm_bindgen] - pub fn set_config( - &mut self, - center: Vector3, - width: f64, - height: f64, - depth: f64, - ) -> Result<(), JsValue> { - self.center = center; - self.width = width; - self.height = height; - self.depth = depth; - self.placement.set_anchor(self.center); - self.generate_brep() - } - - #[wasm_bindgen] - pub fn set_center(&mut self, center: Vector3) { - self.center = center; - self.placement.set_anchor(self.center); - } - - #[wasm_bindgen] - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), JsValue> { - self.placement - .set_transform(position, rotation, scale) - .map_err(|err| JsValue::from_str(&err)) - } - - #[wasm_bindgen] - pub fn set_translation(&mut self, translation: Vector3) { - self.placement.set_translation(translation); - } - - #[wasm_bindgen] - pub fn set_rotation(&mut self, rotation: Vector3) { - self.placement.set_rotation(rotation); - } - - #[wasm_bindgen] - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), JsValue> { - self.placement - .set_scale(scale) - .map_err(|err| JsValue::from_str(&err)) - } - - pub fn generate_brep(&mut self) -> Result<(), JsValue> { - self.clean_geometry(); - self.generate_geometry() - } - - pub fn clean_geometry(&mut self) { - self.brep.clear(); - } - - #[wasm_bindgen] - pub fn generate_geometry(&mut self) -> Result<(), JsValue> { - let half_width = self.width / 2.0; - let half_height = self.height / 2.0; - let half_depth = self.depth / 2.0; - - let x_min = -half_width; - let x_max = half_width; - let y_min = -half_height; - let y_max = half_height; - let z_min = -half_depth; - let z_max = half_depth; - - let vertices = vec![ - Vector3::new(x_min, y_min, z_min), - Vector3::new(x_max, y_min, z_min), - Vector3::new(x_min, y_max, z_min), - Vector3::new(x_min, y_min, z_max), - Vector3::new(x_max, y_min, z_max), - Vector3::new(x_min, y_max, z_max), - ]; - - let faces = vec![ - vec![0, 1, 4, 3], - vec![0, 2, 1], - vec![3, 4, 5], - vec![0, 3, 5, 2], - vec![1, 2, 5, 4], - ]; - - let mut builder = BrepBuilder::new(self.brep.id); - builder.add_vertices(&vertices); - - for face in &faces { - builder.add_face(face, &[]).map_err(|err| { - JsValue::from_str(&format!("Failed to build wedge face: {}", err)) - })?; - } - - builder - .add_shell_from_all_faces(true) - .map_err(|err| JsValue::from_str(&format!("Failed to build wedge shell: {}", err)))?; - - self.brep = builder - .build() - .map_err(|err| JsValue::from_str(&format!("Failed to finalize wedge BREP: {}", err)))?; - - Ok(()) - } - - #[wasm_bindgen] - pub fn get_brep_serialized(&self) -> String { - serde_json::to_string(&self.world_brep()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_brep_serialized(&self) -> String { - serde_json::to_string(&self.brep).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_serialized(&self) -> String { - let world_brep = self.world_brep(); - serde_json::to_string(&world_brep.get_triangle_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_geometry_serialized(&self) -> String { - serde_json::to_string(&self.brep.get_triangle_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_geometry_buffer(&self) -> Vec { - self.world_brep().get_triangle_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_local_geometry_buffer(&self) -> Vec { - self.brep.get_triangle_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_serialized(&self) -> String { - let world_brep = self.world_brep(); - serde_json::to_string(&world_brep.get_outline_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_serialized(&self) -> String { - serde_json::to_string(&self.brep.get_outline_vertex_buffer()).unwrap() - } - - #[wasm_bindgen] - pub fn get_outline_geometry_buffer(&self) -> Vec { - self.world_brep().get_outline_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_local_outline_geometry_buffer(&self) -> Vec { - self.brep.get_outline_vertex_buffer() - } - - #[wasm_bindgen] - pub fn get_anchor(&self) -> Vector3 { - self.placement.anchor - } -} - -impl OGWedge { - pub fn brep(&self) -> &Brep { - &self.brep - } - - pub fn world_brep(&self) -> Brep { - self.brep.transformed(&self.placement) - } - - pub fn to_projected_scene2d(&self, camera: &CameraParameters, hlr: &HlrOptions) -> Scene2D { - let world_brep = self.world_brep(); - project_brep_to_scene(&world_brep, camera, hlr) - } -} diff --git a/main/opengeometry/src/primitives/wire_points.rs b/main/opengeometry/src/primitives/wire_points.rs new file mode 100644 index 0000000..7e0b3c7 --- /dev/null +++ b/main/opengeometry/src/primitives/wire_points.rs @@ -0,0 +1,60 @@ +use crate::brep::{ + checked, Accuracy, BrepEnvelope, Builder, CurveGeometry, GeometryError, Orientation, WireUse, +}; +use crate::math::{norm, scale, sub, Interval, Point3}; + +pub(super) fn wire_from_points( + id: String, + points: &[Point3], + closed: bool, + accuracy: Accuracy, +) -> Result { + accuracy.validate()?; + if points.len() < if closed { 3 } else { 2 } { + return Err(GeometryError::InvalidGeometry( + "wire has too few points".into(), + )); + } + if points.len() > u32::MAX as usize { + return Err(GeometryError::LimitExceeded("wire vertex count".into())); + } + let mut builder = Builder::new(id, accuracy)?; + let vertices = points + .iter() + .map(|&point| checked(point).map(|valid| builder.vertex(valid))) + .collect::, _>>()?; + let mut uses = Vec::with_capacity(if closed { + points.len() + } else { + points.len() - 1 + }); + for index in 0..if closed { + points.len() + } else { + points.len() - 1 + } { + let next = (index + 1) % points.len(); + let delta = sub(points[next], points[index]); + let length = norm(delta); + if length <= 4.0 * accuracy.geometric { + return Err(GeometryError::InvalidGeometry( + "wire edge is below geometric resolution".into(), + )); + } + let edge = builder.edge( + CurveGeometry::Line { + origin: points[index], + direction: scale(delta, 1.0 / length), + }, + Interval::new(0.0, length)?, + false, + ); + uses.push(WireUse { + edge, + from: vertices[index], + to: vertices[next], + sense: Orientation::Forward, + }); + } + builder.finish_wire(uses, closed) +} diff --git a/main/opengeometry/src/query/classification.rs b/main/opengeometry/src/query/classification.rs new file mode 100644 index 0000000..0c8d383 --- /dev/null +++ b/main/opengeometry/src/query/classification.rs @@ -0,0 +1,14 @@ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PointClassification { + Inside, + Outside, + Boundary, + Unknown, +} + +impl PointClassification { + #[cfg(test)] + pub(super) fn is_proven_outside(self) -> bool { + self == Self::Outside + } +} diff --git a/main/opengeometry/src/query/classify.rs b/main/opengeometry/src/query/classify.rs new file mode 100644 index 0000000..1d9ed85 --- /dev/null +++ b/main/opengeometry/src/query/classify.rs @@ -0,0 +1,117 @@ +use super::classification::PointClassification; +use super::face_trim::face_contains_uv; +use super::ray_roots::{ray_box_domain, support_roots}; +use crate::brep::{unit, BrepEnvelope, Face, GeometryError, Surface}; +use crate::math::{dot, norm, scale, sub, Point3}; + +fn classify_with_ray<'a>( + brep: &BrepEnvelope, + point: Point3, + direction: Point3, + faces: impl Iterator, +) -> Result { + let Some(domain) = ray_box_domain(brep, point, direction)? else { + return Ok(PointClassification::Outside); + }; + let mut hits: Vec<(f64, Point3)> = Vec::new(); + for face in faces { + let surface = brep.geometry.surface(face.surface)?; + let Some(roots) = support_roots( + surface, + point, + direction, + domain, + brep.accuracy.intersection, + )? + else { + return Ok(PointClassification::Unknown); + }; + for parameter in roots { + let position = [ + point[0] + parameter * direction[0], + point[1] + parameter * direction[1], + point[2] + parameter * direction[2], + ]; + let uv = match surface.project(position, None) { + Ok(uv) => uv, + Err(GeometryError::SingularParameterization) => { + return Ok(PointClassification::Unknown) + } + Err(error) => return Err(error), + }; + if norm(sub(surface.point_at(uv)?, position)) > brep.accuracy.intersection { + continue; + } + match face_contains_uv(brep, face, uv)? { + Some(true) => {} + Some(false) => continue, + None => return Ok(PointClassification::Unknown), + } + let normal = scale(surface.normal_at(uv)?, face.sense.multiplier()); + if dot(normal, direction).abs() <= 64.0 * f64::EPSILON { + return Ok(PointClassification::Unknown); + } + if parameter <= brep.accuracy.geometric { + return Ok(PointClassification::Boundary); + } + if hits + .iter() + .all(|(existing, _)| (existing - parameter).abs() > brep.accuracy.geometric) + { + hits.push((parameter, position)); + } + } + } + Ok(if hits.len() % 2 == 1 { + PointClassification::Inside + } else { + PointClassification::Outside + }) +} + +pub fn classify_point( + brep: &BrepEnvelope, + point: Point3, +) -> Result { + brep.validate()?; + classify_point_validated(brep, point) +} + +pub(crate) fn classify_point_validated( + brep: &BrepEnvelope, + point: Point3, +) -> Result { + if point.into_iter().any(|value| !value.is_finite()) { + return Err(GeometryError::InvalidGeometry( + "point classification requires finite coordinates".into(), + )); + } + let faces: Vec = brep.topology.faces.iter().map(|face| face.id).collect(); + classify_point_in_shell(brep, &faces, point) +} + +pub(crate) fn classify_point_in_shell( + brep: &BrepEnvelope, + faces: &[u32], + point: Point3, +) -> Result { + let directions = [ + unit([1.0, 0.371, 0.127])?, + unit([0.193, 1.0, 0.419])?, + unit([0.311, 0.233, 1.0])?, + ]; + for direction in directions { + let result = classify_with_ray( + brep, + point, + direction, + faces + .iter() + .map(|face| &brep.topology.faces[*face as usize]), + )?; + if result != PointClassification::Unknown { + return Ok(result); + } + } + Ok(PointClassification::Unknown) +} diff --git a/main/opengeometry/src/query/face_trim.rs b/main/opengeometry/src/query/face_trim.rs new file mode 100644 index 0000000..91e5cfc --- /dev/null +++ b/main/opengeometry/src/query/face_trim.rs @@ -0,0 +1,207 @@ +use crate::brep::{ + period_lifted_uv, BrepEnvelope, Face, GeometryError, Orientation, PcurveGeometry, Surface, + SurfaceGeometry, UV, +}; +use crate::math::{dot, norm}; + +pub(crate) fn face_contains_uv( + brep: &BrepEnvelope, + face: &Face, + uv: UV, +) -> Result, GeometryError> { + let surface = brep.geometry.surface(face.surface)?; + let uv = lift_to_face(surface, face, uv); + let jet = surface.derivatives(uv)?; + let metric = norm(jet.du).min(norm(jet.dv)); + let uv_tolerance = if metric > 0.0 { + brep.accuracy.geometric / metric + } else { + return Ok(None); + }; + if !face.trim.uv_bounds[0].contains(uv[0]) || !face.trim.uv_bounds[1].contains(uv[1]) { + return Ok(Some(false)); + } + if matches!(surface, SurfaceGeometry::Cylinder { .. }) && face.trim.holes.len() == 1 { + if let (Some(outer), Some(inner)) = ( + periodic_cylinder_band_limit(brep, face, face.trim.outer)?, + periodic_cylinder_band_limit(brep, face, face.trim.holes[0])?, + ) { + let lo = outer.min(inner); + let hi = outer.max(inner); + if (uv[1] - lo).abs() <= uv_tolerance || (uv[1] - hi).abs() <= uv_tolerance { + return Ok(None); + } + return Ok(Some(uv[1] > lo && uv[1] < hi)); + } + } + let outer = trim_samples(brep, face, face.trim.outer)?; + let Some(mut inside) = in_loop(uv, &outer, uv_tolerance) else { + return Ok(None); + }; + if !inside { + return Ok(Some(false)); + } + for hole in &face.trim.holes { + let polygon = trim_samples(brep, face, *hole)?; + match in_loop(uv, &polygon, uv_tolerance) { + None => return Ok(None), + Some(true) => inside = false, + Some(false) => {} + } + } + Ok(Some(inside)) +} + +fn lift_to_face(surface: &SurfaceGeometry, face: &Face, mut uv: UV) -> UV { + let periods = surface.charts()[face.trim.chart as usize].periods; + for axis in 0..2 { + if let Some(period) = periods[axis] { + let center = face.trim.uv_bounds[axis].midpoint(); + uv[axis] += ((center - uv[axis]) / period).round() * period; + } + } + uv +} + +fn periodic_cylinder_band_limit( + brep: &BrepEnvelope, + face: &Face, + loop_id: u32, +) -> Result, GeometryError> { + let loop_ = &brep.topology.loops[loop_id as usize]; + let use_ = &brep.topology.halfedges[loop_.start_halfedge as usize]; + if use_.next != Some(loop_.start_halfedge) || use_.from != use_.to { + return Ok(None); + } + let edge = &brep.topology.edges[use_.edge as usize]; + let range = edge.geometry.range(); + let Some(pcurve) = use_.geometry_use.pcurve else { + return Ok(None); + }; + let PcurveGeometry::Line2 { direction, .. } = brep.geometry.pcurves[pcurve as usize] else { + return Ok(None); + }; + let period = brep.geometry.surface(face.surface)?.charts()[face.trim.chart as usize].periods[0]; + let Some(period) = period else { + return Ok(None); + }; + if direction[1].abs() * range.width() > brep.accuracy.geometric + || ((direction[0] * range.width()).abs() - period).abs() > 4.0 * brep.accuracy.geometric + { + return Ok(None); + } + Ok(Some(lifted_uv(brep, loop_.start_halfedge, range.lo)?[1])) +} + +fn lifted_uv(brep: &BrepEnvelope, halfedge: u32, parameter: f64) -> Result { + let use_ = &brep.topology.halfedges[halfedge as usize]; + let face = &brep.topology.faces[use_ + .face + .ok_or_else(|| GeometryError::InvalidTopology("trim query halfedge has no face".into()))? + as usize]; + let pcurve = use_.geometry_use.pcurve.ok_or_else(|| { + GeometryError::InvalidTopology("trim query halfedge has no pcurve".into()) + })?; + let uv = brep.geometry.pcurve_at(pcurve, parameter)?; + let periods = brep.geometry.surface(face.surface)?.charts()[face.trim.chart as usize].periods; + Ok(period_lifted_uv( + uv, + periods, + use_.geometry_use.periodic_lift, + )) +} + +fn trim_samples(brep: &BrepEnvelope, face: &Face, loop_id: u32) -> Result, GeometryError> { + let loop_ = &brep.topology.loops[loop_id as usize]; + let start = loop_.start_halfedge; + let mut current = start; + let mut points = Vec::new(); + for _ in 0..=brep.topology.halfedges.len() { + let use_ = &brep.topology.halfedges[current as usize]; + if use_.face != Some(face.id) { + return Err(GeometryError::InvalidTopology( + "trim query crossed into another face".into(), + )); + } + let edge = &brep.topology.edges[use_.edge as usize]; + let range = edge.geometry.range(); + let pcurve = use_.geometry_use.pcurve.ok_or_else(|| { + GeometryError::InvalidTopology("trim query halfedge has no pcurve".into()) + })?; + let subdivisions = match brep.geometry.pcurves[pcurve as usize] { + PcurveGeometry::Line2 { direction, .. } => { + let periods = + brep.geometry.surface(face.surface)?.charts()[face.trim.chart as usize].periods; + if periods.iter().enumerate().any(|(axis, period)| { + period.is_some_and(|period| { + ((direction[axis] * range.width()).abs() - period).abs() + <= 4.0 * brep.accuracy.geometric + }) + }) { + 64 + } else { + 1 + } + } + PcurveGeometry::Conic2 { .. } => 64, + PcurveGeometry::ProjectedCurve { .. } | PcurveGeometry::IntersectionSide { .. } => 128, + }; + for index in 0..subdivisions { + let fraction = if use_.geometry_use.sense == Orientation::Forward { + index as f64 / subdivisions as f64 + } else { + 1.0 - index as f64 / subdivisions as f64 + }; + points.push(lifted_uv( + brep, + current, + range.lo + range.width() * fraction, + )?); + } + current = use_ + .next + .ok_or_else(|| GeometryError::InvalidTopology("open trim loop in query".into()))?; + if current == start { + break; + } + } + if current != start || points.len() < 3 { + return Err(GeometryError::InvalidTopology( + "trim query requires a closed nondegenerate loop".into(), + )); + } + Ok(points) +} + +fn in_loop(point: UV, polygon: &[UV], tolerance: f64) -> Option { + let mut inside = false; + for index in 0..polygon.len() { + let a = polygon[index]; + let b = polygon[(index + 1) % polygon.len()]; + if segment_distance_2d(point, a, b) <= tolerance { + return None; + } + if (a[1] > point[1]) != (b[1] > point[1]) { + let x = a[0] + (point[1] - a[1]) * (b[0] - a[0]) / (b[1] - a[1]); + if x > point[0] { + inside = !inside; + } + } + } + Some(inside) +} + +fn segment_distance_2d(point: UV, a: UV, b: UV) -> f64 { + let direction = [b[0] - a[0], b[1] - a[1]]; + let length_squared = dot( + [direction[0], direction[1], 0.0], + [direction[0], direction[1], 0.0], + ); + if length_squared == 0.0 { + return (point[0] - a[0]).hypot(point[1] - a[1]); + } + let parameter = (((point[0] - a[0]) * direction[0] + (point[1] - a[1]) * direction[1]) + / length_squared) + .clamp(0.0, 1.0); + (point[0] - a[0] - parameter * direction[0]).hypot(point[1] - a[1] - parameter * direction[1]) +} diff --git a/main/opengeometry/src/query/mod.rs b/main/opengeometry/src/query/mod.rs new file mode 100644 index 0000000..46e3f4c --- /dev/null +++ b/main/opengeometry/src/query/mod.rs @@ -0,0 +1,11 @@ +mod classification; +mod classify; +mod face_trim; +mod ray_roots; +#[cfg(test)] +mod tests; + +pub use classification::PointClassification; +pub use classify::classify_point; +pub(super) use classify::{classify_point_in_shell, classify_point_validated}; +pub(super) use face_trim::face_contains_uv; diff --git a/main/opengeometry/src/query/ray_roots.rs b/main/opengeometry/src/query/ray_roots.rs new file mode 100644 index 0000000..4fb9de5 --- /dev/null +++ b/main/opengeometry/src/query/ray_roots.rs @@ -0,0 +1,177 @@ +use crate::brep::{BrepEnvelope, GeometryError, SurfaceGeometry}; +use crate::math::{dot, isolate_candidates, quadratic, Interval, Point3, QuadraticRoots}; + +pub(super) fn support_roots( + surface: &SurfaceGeometry, + origin: Point3, + direction: Point3, + domain: Interval, + tolerance: f64, +) -> Result>, GeometryError> { + let o = surface.frame().local(origin); + let d = [ + dot(direction, surface.frame().x), + dot(direction, surface.frame().y), + dot(direction, surface.frame().z), + ]; + let quadratic_roots = |a, b, c| -> Result>, GeometryError> { + Ok(Some(match quadratic(a, b, c)? { + QuadraticRoots::Empty => Vec::new(), + QuadraticRoots::One(root) => vec![root], + QuadraticRoots::Two(roots) => roots.to_vec(), + QuadraticRoots::IdenticallyZero => return Ok(None), + })) + }; + let result = match surface { + SurfaceGeometry::Plane { .. } => { + if d[2] == 0.0 { + if o[2].abs() <= tolerance { + None + } else { + Some(Vec::new()) + } + } else { + Some(vec![-o[2] / d[2]]) + } + } + SurfaceGeometry::Sphere { radius, .. } => { + quadratic_roots(dot(d, d), 2.0 * dot(o, d), dot(o, o) - radius * radius)? + } + SurfaceGeometry::Cylinder { radius, .. } => quadratic_roots( + d[0].mul_add(d[0], d[1] * d[1]), + 2.0 * o[0].mul_add(d[0], o[1] * d[1]), + o[0].mul_add(o[0], o[1] * o[1]) - radius * radius, + )?, + SurfaceGeometry::Cone { semi_angle, .. } => { + let k2 = semi_angle.tan().powi(2); + quadratic_roots( + d[0].mul_add(d[0], d[1] * d[1]) - k2 * d[2] * d[2], + 2.0 * (o[0].mul_add(d[0], o[1] * d[1]) - k2 * o[2] * d[2]), + o[0].mul_add(o[0], o[1] * o[1]) - k2 * o[2] * o[2], + )? + .map(|roots| { + roots + .into_iter() + .filter(|root| o[2] + root * d[2] >= -tolerance) + .collect() + }) + } + SurfaceGeometry::Torus { + major_radius, + minor_radius, + .. + } => Some(torus_roots( + o, + d, + *major_radius, + *minor_radius, + domain, + tolerance, + )?), + }; + Ok(result.map(|roots| { + roots + .into_iter() + .filter(|root| domain.contains(*root)) + .collect() + })) +} + +fn torus_roots( + o: Point3, + d: Point3, + major_radius: f64, + minor_radius: f64, + domain: Interval, + tolerance: f64, +) -> Result, GeometryError> { + let radial = [ + o[0].mul_add(o[0], o[1] * o[1]), + 2.0 * o[0].mul_add(d[0], o[1] * d[1]), + d[0].mul_add(d[0], d[1] * d[1]), + ]; + let sum = [ + dot(o, o) + major_radius * major_radius - minor_radius * minor_radius, + 2.0 * dot(o, d), + dot(d, d), + ]; + let r4 = 4.0 * major_radius * major_radius; + let coefficients = [ + sum[0] * sum[0] - r4 * radial[0], + 2.0 * sum[0] * sum[1] - r4 * radial[1], + sum[1] * sum[1] + 2.0 * sum[0] * sum[2] - r4 * radial[2], + 2.0 * sum[1] * sum[2], + sum[2] * sum[2], + ]; + let candidates = + isolate_candidates(&coefficients, domain, tolerance, 100_000).map_err(|error| { + GeometryError::UnresolvedIntersection(format!("torus ray root isolation: {error:?}")) + })?; + Ok(candidates + .intervals + .into_iter() + .map(|candidate| refine_polynomial_root(&coefficients, candidate)) + .collect()) +} + +fn refine_polynomial_root(coefficients: &[f64], interval: Interval) -> f64 { + let mut t = interval.midpoint(); + let derivative = coefficients + .iter() + .enumerate() + .skip(1) + .map(|(degree, coefficient)| *coefficient * degree as f64) + .collect::>(); + for _ in 0..12 { + let value = polynomial_value(coefficients, t); + let slope = polynomial_value(&derivative, t); + if slope == 0.0 || !slope.is_finite() { + break; + } + let candidate = t - value / slope; + if !candidate.is_finite() || candidate < interval.lo || candidate > interval.hi { + break; + } + t = candidate; + } + t +} + +fn polynomial_value(coefficients: &[f64], value: f64) -> f64 { + coefficients + .iter() + .rev() + .fold(0.0, |sum, coefficient| sum.mul_add(value, *coefficient)) +} + +pub(super) fn ray_box_domain( + brep: &BrepEnvelope, + origin: Point3, + direction: Point3, +) -> Result, GeometryError> { + let Some(bounds) = brep.bounds_unchecked()? else { + return Ok(None); + }; + let mut lo: f64 = 0.0; + let mut hi = f64::MAX; + for axis in 0..3 { + if direction[axis] == 0.0 { + if !bounds.axes[axis].contains(origin[axis]) { + return Ok(None); + } + continue; + } + let mut a = (bounds.axes[axis].lo - origin[axis]) / direction[axis]; + let mut b = (bounds.axes[axis].hi - origin[axis]) / direction[axis]; + if a > b { + std::mem::swap(&mut a, &mut b); + } + lo = lo.max(a); + hi = hi.min(b); + } + if hi < lo.max(0.0) { + Ok(None) + } else { + Ok(Some(Interval::new(lo.max(0.0), hi)?)) + } +} diff --git a/main/opengeometry/src/query/tests.rs b/main/opengeometry/src/query/tests.rs new file mode 100644 index 0000000..290a938 --- /dev/null +++ b/main/opengeometry/src/query/tests.rs @@ -0,0 +1,89 @@ +use super::classification::PointClassification; +use super::classify::classify_point; +use crate::brep::{coarse_accuracy, Frame3}; +use crate::primitives; + +#[test] +fn i3_unknown_classification_is_never_treated_as_outside() { + assert!(!PointClassification::Unknown.is_proven_outside()); + assert!(!PointClassification::Boundary.is_proven_outside()); + assert!(!PointClassification::Inside.is_proven_outside()); + assert!(PointClassification::Outside.is_proven_outside()); +} +#[test] +fn analytic_ray_classification_covers_all_surface_families() { + let bodies = [ + ( + primitives::cuboid( + "box".into(), + Frame3::IDENTITY, + [2.0; 3], + coarse_accuracy(1e-5), + ) + .unwrap(), + [1.0, 1.0, 1.0], + [3.0, 3.0, 3.0], + ), + ( + primitives::sphere( + "sphere".into(), + Frame3::IDENTITY, + 1.0, + coarse_accuracy(1e-5), + ) + .unwrap(), + [0.0; 3], + [2.0, 0.0, 0.0], + ), + ( + primitives::cylinder( + "cylinder".into(), + Frame3::IDENTITY, + 1.0, + 2.0, + coarse_accuracy(1e-5), + ) + .unwrap(), + [0.0, 0.0, 1.0], + [2.0, 0.0, 1.0], + ), + ( + primitives::cone( + "cone".into(), + Frame3::IDENTITY, + 1.0, + 2.0, + coarse_accuracy(1e-5), + ) + .unwrap(), + [0.0, 0.0, 1.0], + [2.0, 0.0, 1.0], + ), + ( + primitives::torus( + "torus".into(), + Frame3::IDENTITY, + 3.0, + 1.0, + coarse_accuracy(1e-5), + ) + .unwrap(), + [3.0, 0.0, 0.0], + [0.0; 3], + ), + ]; + for (body, inside, outside) in bodies { + assert_eq!( + classify_point(&body, inside).unwrap(), + PointClassification::Inside, + "{}", + body.id + ); + assert_eq!( + classify_point(&body, outside).unwrap(), + PointClassification::Outside, + "{}", + body.id + ); + } +} diff --git a/main/opengeometry/src/spatial/placement.rs b/main/opengeometry/src/spatial/placement.rs deleted file mode 100644 index f105dff..0000000 --- a/main/opengeometry/src/spatial/placement.rs +++ /dev/null @@ -1,379 +0,0 @@ -use openmaths::{Matrix4, Vector3}; -use serde::{Deserialize, Serialize}; - -// Keep scale bounds aligned with downstream tolerance assumptions used by -// boolean and export paths. -const MIN_UNIFORM_SCALE: f64 = 1.0e-6; -const UNIFORM_SCALE_REL_TOLERANCE: f64 = 1.0e-9; - -#[derive(Clone, Serialize, Deserialize)] -pub struct Placement3D { - pub anchor: Vector3, - translation: Vector3, - rotation: Vector3, - scale: Vector3, -} - -impl Default for Placement3D { - fn default() -> Self { - Self::new() - } -} - -impl Placement3D { - pub fn new() -> Self { - Self { - anchor: Vector3::new(0.0, 0.0, 0.0), - translation: Vector3::new(0.0, 0.0, 0.0), - rotation: Vector3::new(0.0, 0.0, 0.0), - scale: Vector3::new(1.0, 1.0, 1.0), - } - } - - pub fn translation(&self) -> Vector3 { - self.translation - } - - pub fn rotation(&self) -> Vector3 { - self.rotation - } - - pub fn scale(&self) -> Vector3 { - self.scale - } - - pub fn set_anchor(&mut self, anchor: Vector3) { - self.anchor = sanitize_vector(anchor, 0.0); - } - - pub fn set_transform( - &mut self, - position: Vector3, - rotation: Vector3, - scale: Vector3, - ) -> Result<(), String> { - let validated_scale = validate_uniform_positive_scale(scale)?; - self.translation = sanitize_vector(position, 0.0); - self.rotation = sanitize_vector(rotation, 0.0); - self.scale = validated_scale; - Ok(()) - } - - pub fn set_translation(&mut self, translation: Vector3) { - self.translation = sanitize_vector(translation, 0.0); - } - - pub fn set_rotation(&mut self, rotation: Vector3) { - self.rotation = sanitize_vector(rotation, 0.0); - } - - pub fn set_scale(&mut self, scale: Vector3) -> Result<(), String> { - self.scale = validate_uniform_positive_scale(scale)?; - Ok(()) - } - - pub fn world_matrix(&self) -> Matrix4 { - let rotation = self.rotation; - let scale = self.scale; - let translation = Vector3::new( - self.anchor.x + self.translation.x, - self.anchor.y + self.translation.y, - self.anchor.z + self.translation.z, - ); - - let cos_x = rotation.x.cos(); - let sin_x = rotation.x.sin(); - let cos_y = rotation.y.cos(); - let sin_y = rotation.y.sin(); - let cos_z = rotation.z.cos(); - let sin_z = rotation.z.sin(); - - let m11 = cos_y * cos_z; - let m12 = -cos_y * sin_z; - let m13 = sin_y; - - let m21 = sin_x * sin_y * cos_z + cos_x * sin_z; - let m22 = cos_x * cos_z - sin_x * sin_y * sin_z; - let m23 = -sin_x * cos_y; - - let m31 = sin_x * sin_z - cos_x * sin_y * cos_z; - let m32 = sin_x * cos_z + cos_x * sin_y * sin_z; - let m33 = cos_x * cos_y; - - Matrix4::set( - m11 * scale.x, - m12 * scale.y, - m13 * scale.z, - translation.x, - m21 * scale.x, - m22 * scale.y, - m23 * scale.z, - translation.y, - m31 * scale.x, - m32 * scale.y, - m33 * scale.z, - translation.z, - 0.0, - 0.0, - 0.0, - 1.0, - ) - } -} - -pub fn bounds_center_from_points(points: &[Vector3]) -> Option { - let first = points.first()?; - let mut min_x = first.x; - let mut min_y = first.y; - let mut min_z = first.z; - let mut max_x = first.x; - let mut max_y = first.y; - let mut max_z = first.z; - - for point in &points[1..] { - min_x = min_x.min(point.x); - min_y = min_y.min(point.y); - min_z = min_z.min(point.z); - max_x = max_x.max(point.x); - max_y = max_y.max(point.y); - max_z = max_z.max(point.z); - } - - Some(Vector3::new( - (min_x + max_x) * 0.5, - (min_y + max_y) * 0.5, - (min_z + max_z) * 0.5, - )) -} - -pub fn bounds_center_from_point_sets(point_sets: &[&[Vector3]]) -> Option { - let mut min_x = f64::INFINITY; - let mut min_y = f64::INFINITY; - let mut min_z = f64::INFINITY; - let mut max_x = f64::NEG_INFINITY; - let mut max_y = f64::NEG_INFINITY; - let mut max_z = f64::NEG_INFINITY; - let mut has_points = false; - - for point_set in point_sets { - for point in *point_set { - has_points = true; - min_x = min_x.min(point.x); - min_y = min_y.min(point.y); - min_z = min_z.min(point.z); - max_x = max_x.max(point.x); - max_y = max_y.max(point.y); - max_z = max_z.max(point.z); - } - } - - if !has_points { - return None; - } - - Some(Vector3::new( - (min_x + max_x) * 0.5, - (min_y + max_y) * 0.5, - (min_z + max_z) * 0.5, - )) -} - -pub fn points_relative_to_anchor(points: &[Vector3], anchor: Vector3) -> Vec { - points - .iter() - .map(|point| Vector3::new(point.x - anchor.x, point.y - anchor.y, point.z - anchor.z)) - .collect() -} - -pub fn transform_points_with_placement( - points: &[Vector3], - placement: &Placement3D, -) -> Vec { - let local_points = points_relative_to_anchor(points, placement.anchor); - let matrix = placement.world_matrix(); - - local_points - .into_iter() - .map(|mut point| { - point.apply_matrix4(matrix.clone()); - point - }) - .collect() -} - -fn sanitize_vector(vector: Vector3, default: f64) -> Vector3 { - Vector3::new( - sanitize_component(vector.x, default), - sanitize_component(vector.y, default), - sanitize_component(vector.z, default), - ) -} - -fn sanitize_component(value: f64, default: f64) -> f64 { - if value.is_finite() { - value - } else { - default - } -} - -pub fn validate_uniform_positive_scale(scale: Vector3) -> Result { - let components = [scale.x, scale.y, scale.z]; - if components.iter().any(|value| !value.is_finite()) { - return Err( - "Invalid placement scale: all scale components must be finite numbers.".to_string(), - ); - } - - if components.iter().any(|value| *value < MIN_UNIFORM_SCALE) { - return Err(format!( - "Invalid placement scale: all scale components must be >= {}.", - MIN_UNIFORM_SCALE - )); - } - - let max_component = components.iter().copied().fold(f64::NEG_INFINITY, f64::max); - let min_component = components.iter().copied().fold(f64::INFINITY, f64::min); - let tolerance = UNIFORM_SCALE_REL_TOLERANCE * max_component.max(1.0); - if (max_component - min_component) > tolerance { - return Err( - "Invalid placement scale: non-uniform scale is not allowed for placement.".to_string(), - ); - } - - Ok(scale) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::f64::{consts::PI, INFINITY, NAN}; - - fn assert_close(actual: f64, expected: f64) { - let delta = (actual - expected).abs(); - assert!( - delta <= 1.0e-9, - "expected {expected}, got {actual}, delta {delta}" - ); - } - - fn assert_vec_close(actual: Vector3, expected: Vector3) { - assert_close(actual.x, expected.x); - assert_close(actual.y, expected.y); - assert_close(actual.z, expected.z); - } - - fn multiply_quaternions( - lhs: (f64, f64, f64, f64), - rhs: (f64, f64, f64, f64), - ) -> (f64, f64, f64, f64) { - ( - lhs.3 * rhs.0 + lhs.0 * rhs.3 + lhs.1 * rhs.2 - lhs.2 * rhs.1, - lhs.3 * rhs.1 - lhs.0 * rhs.2 + lhs.1 * rhs.3 + lhs.2 * rhs.0, - lhs.3 * rhs.2 + lhs.0 * rhs.1 - lhs.1 * rhs.0 + lhs.2 * rhs.3, - lhs.3 * rhs.3 - lhs.0 * rhs.0 - lhs.1 * rhs.1 - lhs.2 * rhs.2, - ) - } - - fn rotate_point_with_xyz_quaternion(point: Vector3, rotation: Vector3) -> Vector3 { - let half_x = rotation.x * 0.5; - let half_y = rotation.y * 0.5; - let half_z = rotation.z * 0.5; - - let qx = (half_x.sin(), 0.0, 0.0, half_x.cos()); - let qy = (0.0, half_y.sin(), 0.0, half_y.cos()); - let qz = (0.0, 0.0, half_z.sin(), half_z.cos()); - let quaternion = multiply_quaternions(multiply_quaternions(qx, qy), qz); - - let u = Vector3::new(quaternion.0, quaternion.1, quaternion.2); - let s = quaternion.3; - let dot = u.x * point.x + u.y * point.y + u.z * point.z; - let cross = Vector3::new( - u.y * point.z - u.z * point.y, - u.z * point.x - u.x * point.z, - u.x * point.y - u.y * point.x, - ); - let uu = u.x * u.x + u.y * u.y + u.z * u.z; - - Vector3::new( - 2.0 * dot * u.x + (s * s - uu) * point.x + 2.0 * s * cross.x, - 2.0 * dot * u.y + (s * s - uu) * point.y + 2.0 * s * cross.y, - 2.0 * dot * u.z + (s * s - uu) * point.z + 2.0 * s * cross.z, - ) - } - - #[test] - fn set_transform_rejects_non_finite_non_positive_and_non_uniform_scale() { - let mut placement = Placement3D::new(); - placement.set_anchor(Vector3::new(NAN, INFINITY, -INFINITY)); - let non_finite = placement.set_transform( - Vector3::new(NAN, 2.5, INFINITY), - Vector3::new(-INFINITY, PI * 0.25, NAN), - Vector3::new(1.0, f64::NAN, 1.0), - ); - assert!(non_finite.is_err()); - - let non_positive = placement.set_transform( - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(-1.0, -1.0, -1.0), - ); - assert!(non_positive.is_err()); - - let non_uniform = placement.set_transform( - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 1.25, 1.0), - ); - assert!(non_uniform.is_err()); - } - - #[test] - fn set_transform_sanitizes_non_finite_translation_and_rotation_for_valid_uniform_scale() { - let mut placement = Placement3D::new(); - placement.set_anchor(Vector3::new(NAN, INFINITY, -INFINITY)); - placement - .set_transform( - Vector3::new(NAN, 2.5, INFINITY), - Vector3::new(-INFINITY, PI * 0.25, NAN), - Vector3::new(1.2, 1.2, 1.2), - ) - .expect("uniform positive scale should be accepted"); - - assert_vec_close(placement.anchor, Vector3::new(0.0, 0.0, 0.0)); - assert_vec_close(placement.translation(), Vector3::new(0.0, 2.5, 0.0)); - assert_vec_close(placement.rotation(), Vector3::new(0.0, PI * 0.25, 0.0)); - assert_vec_close(placement.scale(), Vector3::new(1.2, 1.2, 1.2)); - } - - #[test] - fn world_matrix_applies_translation_rotation_and_scale_in_xyz_order() { - let mut placement = Placement3D::new(); - placement.set_anchor(Vector3::new(10.0, -3.0, 4.0)); - placement - .set_transform( - Vector3::new(1.0, 2.0, -5.0), - Vector3::new(0.4, -0.3, 0.25), - Vector3::new(1.5, 1.5, 1.5), - ) - .expect("uniform positive scale should be accepted"); - - let local_point = Vector3::new(1.25, -2.0, 0.5); - let mut transformed = local_point; - transformed.apply_matrix4(placement.world_matrix()); - - let scaled = Vector3::new( - local_point.x * placement.scale().x, - local_point.y * placement.scale().y, - local_point.z * placement.scale().z, - ); - let rotated = rotate_point_with_xyz_quaternion(scaled, placement.rotation()); - let expected = Vector3::new( - placement.anchor.x + placement.translation().x + rotated.x, - placement.anchor.y + placement.translation().y + rotated.y, - placement.anchor.z + placement.translation().z + rotated.z, - ); - - assert_vec_close(transformed, expected); - } -} diff --git a/main/opengeometry/src/spatial/workplane.rs b/main/opengeometry/src/spatial/workplane.rs deleted file mode 100644 index 4f8dcb5..0000000 --- a/main/opengeometry/src/spatial/workplane.rs +++ /dev/null @@ -1,186 +0,0 @@ -//! Work-plane / sketch coordinate system (debt item D6). -//! -//! A sketch is conceptually "a coordinate frame + 2D geometry." The kernel -//! historically had no such object: every consumer re-derived the world -//! transform of a profile by hand (baking Euler angles into vertex positions), -//! duplicating error-prone math. [`WorkPlane`] is the first-class datum frame — -//! an origin, a normal, and two in-plane axes — that lifts 2D `(u, v)` -//! coordinates to 3D world space, so sketches can be authored on any plane. - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; -use wasm_bindgen::prelude::*; - -#[wasm_bindgen] -#[derive(Clone, Serialize, Deserialize)] -pub struct WorkPlane { - origin: Vector3, - normal: Vector3, - u_axis: Vector3, - v_axis: Vector3, -} - -#[wasm_bindgen] -impl WorkPlane { - /// Builds a work plane from an origin and normal, choosing a stable in-plane - /// `u` axis automatically (`v = normal × u`). - #[wasm_bindgen(js_name = fromOriginNormal)] - pub fn from_origin_normal(origin: Vector3, normal: Vector3) -> WorkPlane { - let n = normalize(normal, Vector3::new(0.0, 1.0, 0.0)); - let u = normalize(any_perpendicular(n), Vector3::new(1.0, 0.0, 0.0)); - let v = normalize(cross(n, u), Vector3::new(0.0, 0.0, 1.0)); - WorkPlane { - origin, - normal: n, - u_axis: u, - v_axis: v, - } - } - - /// Builds a work plane from an origin, normal, and a preferred `u` axis. The - /// `u` axis is projected onto the plane and orthonormalized; `v = normal × u`. - #[wasm_bindgen(constructor)] - pub fn new(origin: Vector3, normal: Vector3, u_hint: Vector3) -> WorkPlane { - let n = normalize(normal, Vector3::new(0.0, 1.0, 0.0)); - // Remove the normal component from the u hint so u lies in the plane. - let dot = u_hint.x * n.x + u_hint.y * n.y + u_hint.z * n.z; - let projected = Vector3::new( - u_hint.x - n.x * dot, - u_hint.y - n.y * dot, - u_hint.z - n.z * dot, - ); - let u = normalize(projected, any_perpendicular(n)); - let v = normalize(cross(n, u), Vector3::new(0.0, 0.0, 1.0)); - WorkPlane { - origin, - normal: n, - u_axis: u, - v_axis: v, - } - } - - /// Lifts a single 2D in-plane coordinate to 3D world space. - #[wasm_bindgen(js_name = liftPoint)] - pub fn lift_point(&self, u: f64, v: f64) -> Vector3 { - Vector3::new( - self.origin.x + self.u_axis.x * u + self.v_axis.x * v, - self.origin.y + self.u_axis.y * u + self.v_axis.y * v, - self.origin.z + self.u_axis.z * u + self.v_axis.z * v, - ) - } - - /// Lifts a flat `[u0,v0,u1,v1,…]` buffer to a flat `[x0,y0,z0,…]` world buffer. - #[wasm_bindgen(js_name = liftPoints)] - pub fn lift_points_flat(&self, uv: Vec) -> Vec { - let mut out = Vec::with_capacity(uv.len() / 2 * 3); - for pair in uv.chunks_exact(2) { - let p = self.lift_point(pair[0], pair[1]); - out.push(p.x); - out.push(p.y); - out.push(p.z); - } - out - } - - #[wasm_bindgen(getter)] - pub fn origin(&self) -> Vector3 { - self.origin - } - - #[wasm_bindgen(getter)] - pub fn normal(&self) -> Vector3 { - self.normal - } -} - -impl WorkPlane { - pub fn u_axis(&self) -> Vector3 { - self.u_axis - } - pub fn v_axis(&self) -> Vector3 { - self.v_axis - } - /// Lifts owned 2D points to world `Vector3`s. - pub fn lift(&self, points: &[(f64, f64)]) -> Vec { - points - .iter() - .map(|(u, v)| self.lift_point(*u, *v)) - .collect() - } -} - -fn cross(a: Vector3, b: Vector3) -> Vector3 { - Vector3::new( - a.y * b.z - a.z * b.y, - a.z * b.x - a.x * b.z, - a.x * b.y - a.y * b.x, - ) -} - -fn normalize(v: Vector3, fallback: Vector3) -> Vector3 { - let len = (v.x * v.x + v.y * v.y + v.z * v.z).sqrt(); - if len <= 1.0e-12 { - fallback - } else { - Vector3::new(v.x / len, v.y / len, v.z / len) - } -} - -fn any_perpendicular(n: Vector3) -> Vector3 { - if n.x.abs() <= n.y.abs() && n.x.abs() <= n.z.abs() { - Vector3::new(0.0, -n.z, n.y) - } else if n.y.abs() <= n.z.abs() { - Vector3::new(-n.z, 0.0, n.x) - } else { - Vector3::new(-n.y, n.x, 0.0) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn approx(a: Vector3, b: Vector3) -> bool { - (a.x - b.x).abs() < 1.0e-9 && (a.y - b.y).abs() < 1.0e-9 && (a.z - b.z).abs() < 1.0e-9 - } - - #[test] - fn xy_plane_lifts_into_world() { - let plane = - WorkPlane::from_origin_normal(Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 1.0)); - // Origin maps to origin; axes are orthonormal in the z=0 plane. - assert!(approx( - plane.lift_point(0.0, 0.0), - Vector3::new(0.0, 0.0, 0.0) - )); - let p = plane.lift_point(1.0, 0.0); - assert!((p.z).abs() < 1.0e-9, "stays in plane"); - } - - #[test] - fn tilted_plane_positions_by_frame_not_by_hand() { - // A plane with normal +X and origin offset: a (u,v) sketch point lands - // on x = origin.x, lifted purely by the frame. - let plane = WorkPlane::new( - Vector3::new(5.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.0, 1.0, 0.0), - ); - let p = plane.lift_point(2.0, 3.0); - assert!((p.x - 5.0).abs() < 1.0e-9, "in-plane points keep x=5"); - } - - #[test] - fn axes_are_orthonormal() { - let plane = - WorkPlane::from_origin_normal(Vector3::new(1.0, 2.0, 3.0), Vector3::new(1.0, 1.0, 1.0)); - let u = plane.u_axis(); - let v = plane.v_axis(); - let n = plane.normal(); - let dot = |a: Vector3, b: Vector3| a.x * b.x + a.y * b.y + a.z * b.z; - assert!(dot(u, v).abs() < 1.0e-9); - assert!(dot(u, n).abs() < 1.0e-9); - assert!(dot(v, n).abs() < 1.0e-9); - assert!((dot(u, u) - 1.0).abs() < 1.0e-9); - } -} diff --git a/main/opengeometry/src/tessellation/cache.rs b/main/opengeometry/src/tessellation/cache.rs new file mode 100644 index 0000000..6420c82 --- /dev/null +++ b/main/opengeometry/src/tessellation/cache.rs @@ -0,0 +1,92 @@ +use super::mesh::Tessellation; +use super::tessellate::tessellate; +use crate::brep::{BrepEnvelope, GeometryError}; +use std::collections::VecDeque; +use std::hash::{DefaultHasher, Hasher}; +use std::io::Write; +use std::sync::Arc; + +struct HashWriter(DefaultHasher); +impl Write for HashWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.0.write(buf); + Ok(buf.len()) + } + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} +struct CacheEntry { + body: u64, + signature: u64, + deflection: u64, + max_triangles: usize, + mesh: Arc, +} +pub(super) struct TessellationCache { + entries: VecDeque, + max_bytes: usize, +} +impl TessellationCache { + pub(super) fn new(max_bytes: usize) -> Self { + Self { + entries: VecDeque::new(), + max_bytes, + } + } + pub(super) fn tessellate( + &mut self, + brep: &BrepEnvelope, + deflection: f64, + max_triangles: usize, + ) -> Result, GeometryError> { + brep.validate()?; + let mut writer = HashWriter(DefaultHasher::new()); + serde_json::to_writer(&mut writer, brep) + .map_err(|e| GeometryError::InvalidGeometry(e.to_string()))?; + let signature = writer.0.finish(); + if let Some(i) = self.entries.iter().position(|e| { + e.signature == signature + && e.deflection == deflection.to_bits() + && e.max_triangles == max_triangles + }) { + let entry = self + .entries + .remove(i) + .ok_or_else(|| GeometryError::InvalidGeometry("cache entry missing".into()))?; + let mesh = Arc::clone(&entry.mesh); + self.entries.push_back(entry); + return Ok(mesh); + } + let mesh = Arc::new(tessellate(brep, deflection, max_triangles)?); + if mesh.bytes() > self.max_bytes { + return Ok(mesh); + } + let mut hasher = DefaultHasher::new(); + hasher.write(brep.id.as_bytes()); + let body = hasher.finish(); + while self.entries.iter().filter(|e| e.body == body).count() >= 3 { + if let Some(i) = self.entries.iter().position(|e| e.body == body) { + self.entries.remove(i); + } else { + break; + } + } + while self.entries.iter().map(|e| e.mesh.bytes()).sum::() + mesh.bytes() + > self.max_bytes + { + self.entries.pop_front(); + } + self.entries.push_back(CacheEntry { + body, + signature, + deflection: deflection.to_bits(), + max_triangles, + mesh: Arc::clone(&mesh), + }); + Ok(mesh) + } + pub(super) fn len(&self) -> usize { + self.entries.len() + } +} diff --git a/main/opengeometry/src/tessellation/curved_face.rs b/main/opengeometry/src/tessellation/curved_face.rs new file mode 100644 index 0000000..a2071a5 --- /dev/null +++ b/main/opengeometry/src/tessellation/curved_face.rs @@ -0,0 +1,78 @@ +use super::edges::boundary_point; +use super::grid::Rect; +use super::mesh::Tessellation; +use crate::brep::{BrepEnvelope, Face, GeometryError, Surface, SurfaceGeometry}; +use crate::math::{scale, Point3}; + +pub(super) fn curved_face( + brep: &BrepEnvelope, + face: &Face, + rect: &Rect, + desired: [usize; 2], + samples: &[Vec], + mesh: &mut Tessellation, + max_triangles: usize, +) -> Result<(), GeometryError> { + let mut size = desired; + for (i, &h) in rect.halfedges.iter().enumerate() { + let n = samples[brep.topology.halfedges[h as usize].edge as usize] + .len() + .saturating_sub(1); + size[i % 2] = size[i % 2].max(n); + } + let [nu, nv] = size; + let [u, v] = face.trim.uv_bounds; + let surface = brep.geometry.surface(face.surface)?; + let start = mesh.positions.len() / 3; + if nu + .checked_add(1) + .and_then(|n| nv.checked_add(1).and_then(|m| n.checked_mul(m))) + .is_none_or(|n| n > max_triangles * 3) + { + return Err(GeometryError::LimitExceeded( + "tessellation grid vertices".into(), + )); + } + for j in 0..=nv { + for i in 0..=nu { + let uv = [ + (u.lo + u.width() * i as f64 / nu as f64).clamp(u.lo, u.hi), + (v.lo + v.width() * j as f64 / nv as f64).clamp(v.lo, v.hi), + ]; + let point = if j == 0 { + boundary_point(brep, samples, rect.halfedges[0], i, nu, 0)? + } else if j == nv { + boundary_point(brep, samples, rect.halfedges[2], i, nu, 0)? + } else if i == 0 { + boundary_point(brep, samples, rect.halfedges[3], j, nv, 1)? + } else if i == nu { + boundary_point(brep, samples, rect.halfedges[1], j, nv, 1)? + } else { + surface.point_at(uv)? + }; + let normal_uv = if matches!(surface, SurfaceGeometry::Cone { .. }) && uv[1] == 0.0 { + [uv[0], v.hi] + } else { + uv + }; + let normal = scale(surface.normal_at(normal_uv)?, face.sense.multiplier()); + mesh.vertex(point, normal)?; + } + } + for j in 0..nv { + for i in 0..nu { + let a = (start + j * (nu + 1) + i) as u32; + let b = a + 1; + let c = a + (nu + 1) as u32; + let d = c + 1; + let uv = [ + u.lo + u.width() * (i as f64 + 0.5) / nu as f64, + v.lo + v.width() * (j as f64 + 0.5) / nv as f64, + ]; + let normal = scale(surface.normal_at(uv)?, face.sense.multiplier()); + mesh.triangle([a, b, d], face.id, normal, max_triangles)?; + mesh.triangle([a, d, c], face.id, normal, max_triangles)?; + } + } + Ok(()) +} diff --git a/main/opengeometry/src/tessellation/deviation.rs b/main/opengeometry/src/tessellation/deviation.rs new file mode 100644 index 0000000..d6cc2a6 --- /dev/null +++ b/main/opengeometry/src/tessellation/deviation.rs @@ -0,0 +1,195 @@ +use super::grid::count; +use crate::brep::{ + bounds_chord_deviation, point_segment_distance, BrepEnvelope, Curve, CurveGeometry, + EdgeGeometry, GeometryError, PatchBounds, Surface, +}; +use crate::math::{cross, norm, scale, sub, Interval, Point3}; + +fn intersection_chord_deviation( + brep: &BrepEnvelope, + definition: u32, + range: Interval, + chord_start: Point3, + chord_end: Point3, +) -> Result { + let definition = brep + .geometry + .intersections + .get(definition as usize) + .ok_or_else(|| GeometryError::InvalidGeometry("missing intersection definition".into()))?; + let support_a = brep.geometry.surface(definition.surfaces[0])?; + let support_b = brep.geometry.surface(definition.surfaces[1])?; + let mut maximum = 0.0_f64; + for (index, tube) in definition.uv_tubes.iter().enumerate() { + let anchor_start = definition.anchors[index].parameter; + let anchor_end = definition.anchors[index + 1].parameter; + let lo = range.lo.max(anchor_start); + let hi = range.hi.min(anchor_end); + if lo > hi { + continue; + } + let bounds_a = support_a.enclose([tube[0], tube[1]])?; + let bounds_b = support_b.enclose([tube[2], tube[3]])?; + let mut axes = [Interval::point(0.0)?; 3]; + for axis in 0..3 { + axes[axis] = Interval::new( + bounds_a.axes[axis].lo.max(bounds_b.axes[axis].lo), + bounds_a.axes[axis].hi.min(bounds_b.axes[axis].hi), + ) + .map_err(|_| GeometryError::InvalidGeometry("disjoint trace support boxes".into()))?; + } + let local_start = definition.evaluate(lo, &brep.geometry)?.point; + let local_end = definition.evaluate(hi, &brep.geometry)?.point; + let local_deviation = bounds_chord_deviation(PatchBounds { axes }, local_start, local_end); + let guide_deviation = point_segment_distance(local_start, chord_start, chord_end) + .max(point_segment_distance(local_end, chord_start, chord_end)); + maximum = maximum.max(local_deviation + guide_deviation); + } + Ok(maximum) +} + +fn enclosed_intersection_count( + brep: &BrepEnvelope, + definition: u32, + curve: u32, + range: Interval, + error: f64, + max_segments: usize, +) -> Result { + let evaluator = brep.geometry.curve(curve)?; + let mut stack = vec![(range, 0usize)]; + let mut maximum_depth = 0usize; + let mut visits = 0usize; + while let Some((interval, depth)) = stack.pop() { + visits = visits + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("curve tessellation visits".into()))?; + if visits > max_segments.saturating_mul(2) { + return Err(GeometryError::LimitExceeded( + "curve tessellation visits".into(), + )); + } + let start = evaluator.point_at(interval.lo)?; + let end = evaluator.point_at(interval.hi)?; + if intersection_chord_deviation(brep, definition, interval, start, end)? <= error { + maximum_depth = maximum_depth.max(depth); + continue; + } + if depth >= usize::BITS as usize - 1 || (1usize << (depth + 1)) > max_segments { + return Err(GeometryError::LimitExceeded( + "curve tessellation segments".into(), + )); + } + let midpoint = interval.midpoint(); + stack.push((Interval::new(midpoint, interval.hi)?, depth + 1)); + stack.push((Interval::new(interval.lo, midpoint)?, depth + 1)); + } + Ok(1usize << maximum_depth) +} + +fn adaptive_intersection_count( + brep: &BrepEnvelope, + definition: u32, + curve: u32, + range: Interval, + error: f64, + max_segments: usize, +) -> Result { + let evaluator = brep.geometry.curve(curve)?; + brep.geometry + .intersections + .get(definition as usize) + .ok_or_else(|| GeometryError::InvalidGeometry("missing intersection definition".into()))?; + let closed = norm(sub( + evaluator.point_at(range.lo)?, + evaluator.point_at(range.hi)?, + )) <= brep.accuracy.intersection; + let mut count = if closed { 8 } else { 1 }; + loop { + let mut achieved = 0.0_f64; + for segment in 0..count { + let lo = range.lo + range.width() * segment as f64 / count as f64; + let hi = range.lo + range.width() * (segment + 1) as f64 / count as f64; + let start = evaluator.point_at(lo)?; + let end = evaluator.point_at(hi)?; + let chord = sub(end, start); + let length = norm(chord); + if length == 0.0 { + achieved = f64::INFINITY; + break; + } + let direction = scale(chord, 1.0 / length); + let mut tangent_turn = 0.0_f64; + for fraction in [0.0, 0.25, 0.5, 0.75, 1.0] { + let parameter = lo + (hi - lo) * fraction; + let point = evaluator.point_at(parameter)?; + achieved = achieved.max(point_segment_distance(point, start, end)); + let tangent = evaluator.tangent_at(parameter)?; + let tangent = scale(tangent, 1.0 / norm(tangent)); + tangent_turn = tangent_turn.max(norm(cross(tangent, direction))); + } + achieved = achieved.max(0.25 * length * tangent_turn); + } + if achieved <= error { + return Ok(count); + } + count = count + .checked_mul(2) + .filter(|next| *next <= max_segments) + .ok_or_else(|| GeometryError::LimitExceeded("curve tessellation segments".into()))?; + } +} + +fn bounded_intersection_count( + brep: &BrepEnvelope, + definition: u32, + curve: u32, + range: Interval, + error: f64, + max_segments: usize, +) -> Result { + match enclosed_intersection_count(brep, definition, curve, range, error, max_segments) { + Ok(count) => Ok(count), + Err(GeometryError::LimitExceeded(_)) => { + let count = + adaptive_intersection_count(brep, definition, curve, range, error, max_segments)?; + Ok(count) + } + Err(error) => Err(error), + } +} + +pub(super) fn edge_count( + brep: &BrepEnvelope, + id: u32, + error: f64, + max_triangles: usize, +) -> Result { + match brep.topology.edges[id as usize].geometry { + EdgeGeometry::Collapsed { .. } => Ok(1), + EdgeGeometry::Curve { curve, range } => match &brep.geometry.curves[curve as usize] { + CurveGeometry::Line { .. } => Ok(1), + CurveGeometry::Circle { radius, .. } => count( + range.width() * (radius / (8.0 * error)).sqrt(), + if range.width() >= std::f64::consts::TAU - 1e-12 { + 3 + } else { + 1 + }, + max_triangles, + ), + CurveGeometry::Ellipse { major_radius, .. } => count( + range.width() * (major_radius / (8.0 * error)).sqrt(), + if range.width() >= std::f64::consts::TAU - 1e-12 { + 3 + } else { + 1 + }, + max_triangles, + ), + CurveGeometry::Intersection { definition } => { + bounded_intersection_count(brep, *definition, curve, range, error, max_triangles) + } + }, + } +} diff --git a/main/opengeometry/src/tessellation/display.rs b/main/opengeometry/src/tessellation/display.rs new file mode 100644 index 0000000..0cb7d3f --- /dev/null +++ b/main/opengeometry/src/tessellation/display.rs @@ -0,0 +1,219 @@ +use super::mesh::Tessellation; +use super::tessellate::{coordinate_magnitude, tessellate}; +use crate::brep::{BrepEnvelope, GeometryError}; +use std::collections::BTreeSet; + +#[derive(Clone, Debug, PartialEq)] +pub struct DisplayBuffers { + pub positions: Vec, + pub normals: Vec, + pub indices: Vec, + pub face_ranges: Vec, + pub outline: Vec, + pub edge_ids: Vec, + pub origin: [f64; 3], + pub revision: u64, + pub bucket: f64, + pub achieved_deflection: f64, + pub triangles: u32, +} + +pub fn static_bucket(brep: &BrepEnvelope) -> Result { + let floor = bucket_floor(brep)?; + let bounds = brep + .bounds()? + .ok_or_else(|| GeometryError::InvalidGeometry("display shape has no bounds".into()))?; + let diagonal = bounds + .axes + .iter() + .map(|axis| axis.width().powi(2)) + .sum::() + .sqrt(); + deflection_bucket((diagonal / 500.0).min(1.0).max(floor)) +} + +pub fn bucket_floor(brep: &BrepEnvelope) -> Result { + brep.validate()?; + let magnitude = coordinate_magnitude(brep)?; + let bounds = brep + .bounds()? + .ok_or_else(|| GeometryError::InvalidGeometry("display shape has no bounds".into()))?; + let half_diagonal = bounds + .axes + .iter() + .map(|axis| axis.width().powi(2)) + .sum::() + .sqrt() + / 2.0; + let floor = 2.0 * (brep.accuracy.geometric + 64.0 * f64::EPSILON * magnitude) / 0.75; + let encoding = 4.0 * 2.0_f64.powi(-24) * 3.0_f64.sqrt() * half_diagonal; + let required = floor.max(encoding); + if !required.is_finite() || required <= 0.0 { + return Err(limit("display precision floor is out of range")); + } + let mut bucket = 2.0_f64.powf(required.log2().ceil()); + if 0.75 * bucket <= 2.0 * (brep.accuracy.geometric + 64.0 * f64::EPSILON * magnitude) { + bucket *= 2.0; + } + if !bucket.is_finite() || bucket <= 0.0 { + return Err(limit("display precision floor is out of range")); + } + Ok(bucket) +} + +pub(super) fn limit(message: &str) -> GeometryError { + GeometryError::LimitExceeded(message.into()) +} + +pub fn deflection_bucket(target: f64) -> Result { + if !target.is_finite() || target <= 0.0 { + return Err(GeometryError::InvalidGeometry( + "invalid display deflection".into(), + )); + } + let bucket = 2.0_f64.powf(target.log2().floor()); + if !bucket.is_finite() || bucket <= 0.0 { + return Err(limit("display deflection is out of range")); + } + Ok(bucket) +} + +pub fn display_buffers( + brep: &BrepEnvelope, + bucket: f64, + max_triangles: usize, +) -> Result { + if !bucket.is_finite() || bucket <= 0.0 || bucket < bucket_floor(brep)? { + return Err(limit("display bucket is below the precision floor")); + } + if max_triangles == 0 || max_triangles > 2_000_000 { + return Err(limit("display triangle budget")); + } + let mesh = tessellate(brep, 0.75 * bucket, max_triangles)?; + check_display_mesh(brep, &mesh)?; + let origin = display_origin(&mesh); + let mut encoding_error = 0.0; + let positions = encode(&mesh.positions, origin, &mut encoding_error)?; + let outline = encode(&mesh.outline_positions, origin, &mut encoding_error)?; + if !encoding_error.is_finite() + || encoding_error > bucket / 4.0 + || 0.75 * bucket + encoding_error > bucket + { + return Err(limit("display encoding exceeds requested deflection")); + } + let face_ranges = display_face_ranges(&mesh)?; + Ok(DisplayBuffers { + positions, + normals: mesh.normals, + indices: mesh.indices, + face_ranges, + outline, + edge_ids: mesh.outline_edge_ids, + origin, + revision: mesh.revision, + bucket, + achieved_deflection: 0.75 * bucket + encoding_error, + triangles: mesh.triangle_face_ids.len() as u32, + }) +} + +fn check_display_mesh(brep: &BrepEnvelope, mesh: &Tessellation) -> Result<(), GeometryError> { + if mesh.positions.len() % 3 != 0 + || mesh.normals.len() != mesh.positions.len() + || mesh.indices.len() % 3 != 0 + || mesh.indices.len() > 6_000_000 + || mesh.triangle_face_ids.len() != mesh.indices.len() / 3 + || mesh.outline_positions.len() % 6 != 0 + || mesh.outline_positions.len() > 12_000_000 + || mesh.outline_edge_ids.len() != mesh.outline_positions.len() / 6 + { + return Err(limit("invalid or oversized display arrays")); + } + let vertex_count = mesh.positions.len() / 3; + if mesh + .indices + .iter() + .any(|&index| index as usize >= vertex_count) + || mesh + .triangle_face_ids + .iter() + .any(|&face| face as usize >= brep.topology.faces.len()) + || mesh + .outline_edge_ids + .iter() + .any(|&edge| edge as usize >= brep.topology.edges.len()) + || mesh + .positions + .iter() + .chain(&mesh.outline_positions) + .any(|value| !value.is_finite()) + || mesh.normals.iter().any(|value| !value.is_finite()) + { + return Err(GeometryError::InvalidGeometry( + "invalid display coordinates or topology mapping".into(), + )); + } + Ok(()) +} + +fn display_origin(mesh: &Tessellation) -> [f64; 3] { + let reference = if mesh.positions.is_empty() { + &mesh.outline_positions + } else { + &mesh.positions + }; + let mut low = [f64::INFINITY; 3]; + let mut high = [f64::NEG_INFINITY; 3]; + for point in reference.chunks_exact(3) { + for axis in 0..3 { + low[axis] = low[axis].min(point[axis]); + high[axis] = high[axis].max(point[axis]); + } + } + if reference.is_empty() { + [0.0; 3] + } else { + std::array::from_fn(|axis| low[axis] / 2.0 + high[axis] / 2.0) + } +} + +pub(super) fn encode( + values: &[f64], + origin: [f64; 3], + error: &mut f64, +) -> Result, GeometryError> { + let mut encoded = Vec::with_capacity(values.len()); + for point in values.chunks_exact(3) { + let mut errors = [0.0; 3]; + for axis in 0..3 { + let local = point[axis] - origin[axis]; + let rounded = local as f32; + if !local.is_finite() || !rounded.is_finite() { + return Err(limit("display coordinate encoding is nonfinite")); + } + errors[axis] = (local - f64::from(rounded)).abs(); + encoded.push(rounded); + } + *error = error.max(errors[0].hypot(errors[1]).hypot(errors[2])); + } + Ok(encoded) +} + +fn display_face_ranges(mesh: &Tessellation) -> Result, GeometryError> { + let mut face_ranges = Vec::new(); + let mut seen = BTreeSet::new(); + for (triangle, &face) in mesh.triangle_face_ids.iter().enumerate() { + if face_ranges.len() >= 3 && face_ranges[face_ranges.len() - 3] == face { + let last = face_ranges.len() - 1; + face_ranges[last] += 1; + continue; + } + if !seen.insert(face) { + return Err(GeometryError::InvalidTopology( + "face triangles are not contiguous".into(), + )); + } + face_ranges.extend([face, triangle as u32, 1]); + } + Ok(face_ranges) +} diff --git a/main/opengeometry/src/tessellation/edges.rs b/main/opengeometry/src/tessellation/edges.rs new file mode 100644 index 0000000..ae00e92 --- /dev/null +++ b/main/opengeometry/src/tessellation/edges.rs @@ -0,0 +1,118 @@ +use super::grid::Rect; +use crate::brep::{ + BrepEnvelope, Curve, CurveGeometry, Edge, EdgeGeometry, GeometryError, Orientation, +}; +use crate::math::{find, union, Point3}; +use std::collections::HashMap; + +pub(super) fn synchronize_edge_samples( + brep: &BrepEnvelope, + grids: &[Option<(Option, [usize; 2])>], + counts: &mut [usize], +) { + let mut parent: Vec<_> = (0..counts.len()).collect(); + for (rect, _) in grids.iter().flatten() { + let Some(rect) = rect else { continue }; + for [a, b] in [[0, 2], [1, 3]] { + let a = brep.topology.halfedges[rect.halfedges[a] as usize].edge as usize; + let b = brep.topology.halfedges[rect.halfedges[b] as usize].edge as usize; + union(&mut parent, a, b); + } + } + let key = |edge: &Edge| -> Option<[u64; 14]> { + let EdgeGeometry::Curve { curve, range } = edge.geometry else { + return None; + }; + let frame = match &brep.geometry.curves[curve as usize] { + CurveGeometry::Circle { frame, .. } | CurveGeometry::Ellipse { frame, .. } => frame, + _ => return None, + }; + let values = [frame.origin, frame.x, frame.y, frame.z]; + Some(std::array::from_fn(|i| { + if i < 12 { + values[i / 3][i % 3].to_bits() + } else if i == 12 { + range.lo.to_bits() + } else { + range.hi.to_bits() + } + })) + }; + let mut concentric = HashMap::<[u64; 14], usize>::new(); + for edge in &brep.topology.edges { + if let Some(key) = key(edge) { + if let Some(&other) = concentric.get(&key) { + union(&mut parent, edge.id as usize, other); + } else { + concentric.insert(key, edge.id as usize); + } + } + } + let mut maxima = vec![0; counts.len()]; + for (edge, &count) in counts.iter().enumerate() { + let root = find(&mut parent, edge); + maxima[root] = maxima[root].max(count); + } + for (edge, count) in counts.iter_mut().enumerate() { + *count = maxima[find(&mut parent, edge)]; + } +} + +pub(super) fn sample_edge( + brep: &BrepEnvelope, + id: u32, + n: usize, +) -> Result, GeometryError> { + let edge = &brep.topology.edges[id as usize]; + let h = &brep.topology.halfedges[edge.halfedge as usize]; + match edge.geometry { + EdgeGeometry::Collapsed { vertex } => { + Ok(vec![brep.topology.vertices[vertex as usize].position]) + } + EdgeGeometry::Curve { curve, range } => { + let evaluator = brep.geometry.curve(curve)?; + let mut points = Vec::with_capacity(n + 1); + for i in 0..=n { + points.push( + evaluator.point_at(range.lo + (range.hi - range.lo) * i as f64 / n as f64)?, + ); + } + let (first, last) = if h.geometry_use.sense == Orientation::Forward { + (h.from, h.to) + } else { + (h.to, h.from) + }; + points[0] = brep.topology.vertices[first as usize].position; + points[n] = brep.topology.vertices[last as usize].position; + Ok(points) + } + } +} + +pub(super) fn boundary_point( + brep: &BrepEnvelope, + samples: &[Vec], + halfedge: u32, + index: usize, + n: usize, + axis: usize, +) -> Result { + let h = &brep.topology.halfedges[halfedge as usize]; + let points = &samples[h.edge as usize]; + if points.len() == 1 { + return Ok(points[0]); + } + if points.len() != n + 1 { + return Err(GeometryError::UnsupportedGeometry( + "incompatible opposite edge subdivision counts".into(), + )); + } + let range = brep.topology.edges[h.edge as usize].geometry.range(); + let pcurve = h + .geometry_use + .pcurve + .ok_or_else(|| GeometryError::InvalidTopology("missing pcurve".into()))?; + let a = brep.geometry.pcurve_at(pcurve, range.lo)?; + let b = brep.geometry.pcurve_at(pcurve, range.hi)?; + Ok(points[if a[axis] <= b[axis] { index } else { n - index }]) +} diff --git a/main/opengeometry/src/tessellation/grid.rs b/main/opengeometry/src/tessellation/grid.rs new file mode 100644 index 0000000..09e766c --- /dev/null +++ b/main/opengeometry/src/tessellation/grid.rs @@ -0,0 +1,152 @@ +use crate::brep::{ + period_lifted_uv, BrepEnvelope, Face, GeometryError, PcurveGeometry, Surface, SurfaceGeometry, +}; +use crate::math::Interval; + +pub(super) struct Rect { + pub(super) halfedges: [u32; 4], +} +pub(super) fn rectangle(brep: &BrepEnvelope, face: &Face) -> Result { + let [u, v] = face.trim.uv_bounds; + let mut sides = [None; 4]; + let start = brep.topology.loops[face.trim.outer as usize].start_halfedge; + let mut current = start; + let mut count = 0; + for _ in 0..128 { + let h = &brep.topology.halfedges[current as usize]; + let edge = &brep.topology.edges[h.edge as usize]; + let range = edge.geometry.range(); + let pcurve = h + .geometry_use + .pcurve + .ok_or_else(|| GeometryError::InvalidTopology("missing face pcurve".into()))?; + if !matches!( + brep.geometry.pcurves[pcurve as usize], + PcurveGeometry::Line2 { .. } + ) { + return Err(GeometryError::UnsupportedGeometry( + "nonrectangular curved face trimming".into(), + )); + } + let periods = + brep.geometry.surface(face.surface)?.charts()[face.trim.chart as usize].periods; + let a = period_lifted_uv( + brep.geometry.pcurve_at(pcurve, range.lo)?, + periods, + h.geometry_use.periodic_lift, + ); + let b = period_lifted_uv( + brep.geometry.pcurve_at(pcurve, range.hi)?, + periods, + h.geometry_use.periodic_lift, + ); + let close = |x: f64, y: f64| (x - y).abs() <= 1e-12; + let covers_u = + (close(a[0], u.lo) && close(b[0], u.hi)) || (close(a[0], u.hi) && close(b[0], u.lo)); + let covers_v = + (close(a[1], v.lo) && close(b[1], v.hi)) || (close(a[1], v.hi) && close(b[1], v.lo)); + let side = if covers_u && close(a[1], v.lo) && close(b[1], v.lo) { + 0 + } else if covers_v && close(a[0], u.hi) && close(b[0], u.hi) { + 1 + } else if covers_u && close(a[1], v.hi) && close(b[1], v.hi) { + 2 + } else if covers_v && close(a[0], u.lo) && close(b[0], u.lo) { + 3 + } else { + return Err(GeometryError::UnsupportedGeometry( + "curved trim is not a chart rectangle".into(), + )); + }; + if sides[side].replace(current).is_some() { + return Err(GeometryError::UnsupportedGeometry( + "repeated rectangle boundary".into(), + )); + } + count += 1; + current = h + .next + .ok_or_else(|| GeometryError::InvalidTopology("open face loop".into()))?; + if current == start { + break; + } + } + if count != 4 || sides.iter().any(Option::is_none) { + return Err(GeometryError::UnsupportedGeometry( + "curved trim is not a four-sided rectangle".into(), + )); + } + Ok(Rect { + halfedges: [ + sides[0].ok_or_else(|| GeometryError::InvalidTopology("bottom edge missing".into()))?, + sides[1].ok_or_else(|| GeometryError::InvalidTopology("right edge missing".into()))?, + sides[2].ok_or_else(|| GeometryError::InvalidTopology("top edge missing".into()))?, + sides[3].ok_or_else(|| GeometryError::InvalidTopology("left edge missing".into()))?, + ], + }) +} + +pub(super) fn grid_size( + surface: &SurfaceGeometry, + bounds: [Interval; 2], + error: f64, + max_triangles: usize, +) -> Result<[usize; 2], GeometryError> { + let u = bounds[0].width(); + let v = bounds[1].width(); + let (nu, nv) = match surface { + SurfaceGeometry::Cylinder { radius, .. } => ( + count(u * (radius / (8.0 * error)).sqrt(), 3, max_triangles)?, + 1, + ), + SurfaceGeometry::Cone { semi_angle, .. } => ( + count( + u * (bounds[1].hi * semi_angle.tan() / (8.0 * error)).sqrt(), + 3, + max_triangles, + )?, + 1, + ), + SurfaceGeometry::Sphere { radius, .. } => ( + count(u * (2.0 * radius / error).sqrt(), 3, max_triangles)?, + count(v * (2.0 * radius / error).sqrt(), 2, max_triangles)?, + ), + SurfaceGeometry::Torus { + major_radius: r, + minor_radius: a, + .. + } => ( + count(u * ((r + 2.0 * a) / error).sqrt(), 3, max_triangles)?, + count(v * (2.0 * a / error).sqrt(), 3, max_triangles)?, + ), + SurfaceGeometry::Plane { .. } => { + return Err(GeometryError::InvalidGeometry( + "plane does not use a curved grid".into(), + )) + } + }; + if nu + .checked_mul(nv) + .and_then(|n| n.checked_mul(2)) + .is_none_or(|n| n > max_triangles) + { + return Err(GeometryError::LimitExceeded( + "tessellation grid triangles".into(), + )); + } + Ok([nu, nv]) +} + +pub(super) fn count( + value: f64, + minimum: usize, + max_triangles: usize, +) -> Result { + let value = value.ceil().max(minimum as f64); + if !value.is_finite() || value > max_triangles as f64 { + return Err(GeometryError::LimitExceeded( + "tessellation subdivisions".into(), + )); + } + Ok(value as usize) +} diff --git a/main/opengeometry/src/tessellation/loop_minimum.rs b/main/opengeometry/src/tessellation/loop_minimum.rs new file mode 100644 index 0000000..0c7717c --- /dev/null +++ b/main/opengeometry/src/tessellation/loop_minimum.rs @@ -0,0 +1,51 @@ +use crate::brep::{BrepEnvelope, CurveGeometry, EdgeGeometry, GeometryError, SurfaceGeometry}; + +pub(super) fn raise_short_planar_loops( + brep: &BrepEnvelope, + counts: &mut [usize], +) -> Result<(), GeometryError> { + for minimum in [2, 3] { + let points = planar_loop_points(brep, counts)?; + for h in &brep.topology.halfedges { + let short = h + .loop_ref + .and_then(|id| points[id as usize]) + .is_some_and(|n| n < 3); + if short && conic(brep, h.edge) { + counts[h.edge as usize] = counts[h.edge as usize].max(minimum); + } + } + } + Ok(()) +} + +fn planar_loop_points( + brep: &BrepEnvelope, + counts: &[usize], +) -> Result>, GeometryError> { + let mut points = Vec::with_capacity(brep.topology.loops.len()); + for face_loop in &brep.topology.loops { + let face = &brep.topology.faces[face_loop.face_ref as usize]; + let surface = brep.geometry.surface(face.surface)?; + points.push(matches!(surface, SurfaceGeometry::Plane { .. }).then_some(0)); + } + for h in &brep.topology.halfedges { + let edge = &brep.topology.edges[h.edge as usize]; + if let (Some(id), EdgeGeometry::Curve { .. }) = (h.loop_ref, &edge.geometry) { + if let Some(total) = &mut points[id as usize] { + *total += counts[h.edge as usize]; + } + } + } + Ok(points) +} + +fn conic(brep: &BrepEnvelope, edge: u32) -> bool { + match brep.topology.edges[edge as usize].geometry { + EdgeGeometry::Curve { curve, .. } => matches!( + brep.geometry.curves[curve as usize], + CurveGeometry::Circle { .. } | CurveGeometry::Ellipse { .. } + ), + EdgeGeometry::Collapsed { .. } => false, + } +} diff --git a/main/opengeometry/src/tessellation/mesh.rs b/main/opengeometry/src/tessellation/mesh.rs new file mode 100644 index 0000000..6544344 --- /dev/null +++ b/main/opengeometry/src/tessellation/mesh.rs @@ -0,0 +1,92 @@ +use crate::brep::GeometryError; +use crate::math::{cross, dot, norm, sub, Point3}; + +#[derive(Clone, Debug)] +pub struct Tessellation { + pub positions: Vec, + pub normals: Vec, + pub indices: Vec, + pub triangle_face_ids: Vec, + pub outline_positions: Vec, + pub outline_edge_ids: Vec, + pub revision: u64, + pub achieved_deflection: f64, +} +impl Tessellation { + pub(super) fn new(revision: u64, deflection: f64) -> Self { + Self { + positions: Vec::new(), + normals: Vec::new(), + indices: Vec::new(), + triangle_face_ids: Vec::new(), + outline_positions: Vec::new(), + outline_edge_ids: Vec::new(), + revision, + achieved_deflection: deflection, + } + } + pub(super) fn vertex(&mut self, p: Point3, n: Point3) -> Result { + if p.into_iter().chain(n).any(|v| !v.is_finite()) { + return Err(GeometryError::InvalidGeometry( + "nonfinite tessellation output".into(), + )); + } + let id = u32::try_from(self.positions.len() / 3) + .map_err(|_| GeometryError::LimitExceeded("tessellation vertices".into()))?; + self.positions.extend(p); + self.normals.extend(n.map(|v| v as f32)); + Ok(id) + } + pub(super) fn point(&self, id: u32) -> Point3 { + let i = id as usize * 3; + [ + self.positions[i], + self.positions[i + 1], + self.positions[i + 2], + ] + } + pub(super) fn triangle( + &mut self, + mut ids: [u32; 3], + face: u32, + normal: Point3, + max_triangles: usize, + ) -> Result<(), GeometryError> { + let [a, b, c] = ids.map(|id| self.point(id)); + let direction = cross(sub(b, a), sub(c, a)); + if direction.iter().any(|v| !v.is_finite()) { + return Err(GeometryError::UnresolvedTessellation( + "triangle orientation exceeds arithmetic range".into(), + )); + } + if norm(direction) == 0.0 { + return Ok(()); + } + if self.triangle_face_ids.len() >= max_triangles { + return Err(GeometryError::LimitExceeded( + "tessellation triangles".into(), + )); + } + let orientation = dot(direction, normal); + if !orientation.is_finite() || orientation == 0.0 { + return Err(GeometryError::UnresolvedTessellation( + "triangle orientation could not be resolved".into(), + )); + } + if orientation < 0.0 { + ids.swap(1, 2); + } + self.indices.extend(ids); + self.triangle_face_ids.push(face); + Ok(()) + } + #[cfg(test)] + pub(super) fn bytes(&self) -> usize { + self.positions.len() * 8 + + self.normals.len() * 4 + + self.indices.len() * 4 + + self.triangle_face_ids.len() * 4 + + self.outline_positions.len() * 8 + + self.outline_edge_ids.len() * 4 + } +} diff --git a/main/opengeometry/src/tessellation/mod.rs b/main/opengeometry/src/tessellation/mod.rs new file mode 100644 index 0000000..11c5c3e --- /dev/null +++ b/main/opengeometry/src/tessellation/mod.rs @@ -0,0 +1,20 @@ +#[cfg(test)] +mod cache; +mod curved_face; +mod deviation; +pub mod display; +mod edges; +mod grid; +mod loop_minimum; +mod mesh; +mod planar_face; +mod snapshot_store; +mod tessellate; +#[cfg(test)] +mod tests; +mod trim; +mod trimmed_face; + +pub use mesh::Tessellation; +pub use snapshot_store::SnapshotStore; +pub use tessellate::tessellate; diff --git a/main/opengeometry/src/tessellation/planar_face.rs b/main/opengeometry/src/tessellation/planar_face.rs new file mode 100644 index 0000000..8321e0c --- /dev/null +++ b/main/opengeometry/src/tessellation/planar_face.rs @@ -0,0 +1,79 @@ +use super::mesh::Tessellation; +use crate::brep::{BrepEnvelope, Face, GeometryError, Orientation, Surface}; +use crate::math::{scale, Point3}; + +pub(super) fn planar_face( + brep: &BrepEnvelope, + face: &Face, + samples: &[Vec], + mesh: &mut Tessellation, + max_triangles: usize, +) -> Result<(), GeometryError> { + let surface = brep.geometry.surface(face.surface)?; + let normal = scale(surface.normal_at([0.0; 2])?, face.sense.multiplier()); + let mut points = Vec::new(); + let mut uv_flat = Vec::new(); + let mut holes = Vec::new(); + for (i, &loop_id) in std::iter::once(&face.trim.outer) + .chain(&face.trim.holes) + .enumerate() + { + if i > 0 { + holes.push(points.len()); + } + let start = brep.topology.loops[loop_id as usize].start_halfedge; + let mut current = start; + loop { + let h = &brep.topology.halfedges[current as usize]; + let source = &samples[h.edge as usize]; + if source.len() > 1 { + let n = source.len() - 1; + for j in 0..n { + let p = source[if h.geometry_use.sense == Orientation::Forward { + j + } else { + n - j + }]; + let uv = surface.project(p, None)?; + points.push(p); + uv_flat.extend(uv); + } + } + current = h + .next + .ok_or_else(|| GeometryError::InvalidTopology("open planar face loop".into()))?; + if current == start { + break; + } + } + let loop_start = if i == 0 { 0 } else { holes[i - 1] }; + if points.len() - loop_start < 3 { + return Err(GeometryError::InvalidTopology( + "insufficient tessellated planar boundary".into(), + )); + } + } + let triangles = earcutr::earcut(&uv_flat, &holes, 2); + if triangles.is_empty() && points.len() >= 3 { + return Err(GeometryError::InvalidTopology( + "planar triangulation failed".into(), + )); + } + let mut ids = Vec::with_capacity(points.len()); + for p in points { + ids.push(mesh.vertex(p, normal)?); + } + for tri in triangles.chunks_exact(3) { + let a = *ids + .get(tri[0]) + .ok_or_else(|| GeometryError::InvalidTopology("triangulation index".into()))?; + let b = *ids + .get(tri[1]) + .ok_or_else(|| GeometryError::InvalidTopology("triangulation index".into()))?; + let c = *ids + .get(tri[2]) + .ok_or_else(|| GeometryError::InvalidTopology("triangulation index".into()))?; + mesh.triangle([a, b, c], face.id, normal, max_triangles)?; + } + Ok(()) +} diff --git a/main/opengeometry/src/tessellation/snapshot_store.rs b/main/opengeometry/src/tessellation/snapshot_store.rs new file mode 100644 index 0000000..b969a09 --- /dev/null +++ b/main/opengeometry/src/tessellation/snapshot_store.rs @@ -0,0 +1,51 @@ +use super::display::{display_buffers, limit, DisplayBuffers}; +use crate::brep::{BrepEnvelope, GeometryError}; +use std::collections::BTreeMap; + +#[derive(Default)] +pub struct SnapshotStore { + next_slot: u32, + snapshots: BTreeMap, +} + +impl SnapshotStore { + pub fn new() -> Self { + Self::default() + } + + pub fn load(&mut self, bytes: &[u8]) -> Result { + if bytes.len() > 64 * 1024 * 1024 { + return Err(limit("snapshot exceeds 64 MiB")); + } + let json = std::str::from_utf8(bytes) + .map_err(|_| GeometryError::InvalidGeometry("snapshot is not UTF-8".into()))?; + let brep = BrepEnvelope::from_json(json)?; + let slot = self.next_slot; + self.next_slot = self + .next_slot + .checked_add(1) + .ok_or_else(|| limit("tessellator slots"))?; + self.snapshots.insert(slot, brep); + Ok(slot) + } + + pub fn buffers( + &self, + slot: u32, + bucket: f64, + max_triangles: usize, + ) -> Result { + let brep = self + .snapshots + .get(&slot) + .ok_or_else(|| GeometryError::MissingReference { + kind: "snapshot slot".into(), + index: slot, + })?; + display_buffers(brep, bucket, max_triangles) + } + + pub fn release(&mut self, slot: u32) -> bool { + self.snapshots.remove(&slot).is_some() + } +} diff --git a/main/opengeometry/src/tessellation/tessellate.rs b/main/opengeometry/src/tessellation/tessellate.rs new file mode 100644 index 0000000..c433be0 --- /dev/null +++ b/main/opengeometry/src/tessellation/tessellate.rs @@ -0,0 +1,201 @@ +use super::curved_face::curved_face; +use super::deviation::edge_count; +use super::edges::{sample_edge, synchronize_edge_samples}; +use super::grid::{grid_size, rectangle, Rect}; +use super::loop_minimum::raise_short_planar_loops; +use super::mesh::Tessellation; +use super::planar_face::planar_face; +use super::trimmed_face::{curved_trimmed_face, FaceGrid}; +use crate::brep::{ + BrepEnvelope, Curve, CurveGeometry, EdgeGeometry, GeometryError, Surface, SurfaceGeometry, +}; +use crate::math::{scale, Point3}; + +pub fn tessellate( + brep: &BrepEnvelope, + deflection: f64, + max_triangles: usize, +) -> Result { + brep.validate()?; + if !deflection.is_finite() + || deflection <= 2.0 * brep.accuracy.geometric + || max_triangles == 0 + || max_triangles > 10_000_000 + { + return Err(GeometryError::InvalidGeometry( + "deflection must exceed twice geometric tolerance; triangle limit must be 1..=10000000" + .into(), + )); + } + let error = deflection * 0.5; + let magnitude = coordinate_magnitude(brep)?; + let coordinate_floor = 64.0 * f64::EPSILON * magnitude; + if deflection <= 2.0 * (brep.accuracy.geometric + coordinate_floor) { + return Err(GeometryError::LimitExceeded( + "coordinate precision cannot meet requested deflection".into(), + )); + } + let mut counts = Vec::with_capacity(brep.topology.edges.len()); + for edge in &brep.topology.edges { + counts.push(edge_count(brep, edge.id, error, max_triangles)?); + } + raise_short_planar_loops(brep, &mut counts)?; + let mut grids = Vec::with_capacity(brep.topology.faces.len()); + for face in &brep.topology.faces { + let surface = brep.geometry.surface(face.surface)?; + if matches!(surface, SurfaceGeometry::Plane { .. }) { + grids.push(None); + continue; + } + let rect = match rectangle(brep, face) { + Ok(rect) => Some(rect), + Err(GeometryError::UnsupportedGeometry(_)) => None, + Err(error) => return Err(error), + }; + let desired = grid_size(surface, face.trim.uv_bounds, error, max_triangles)?; + if let Some(rect) = &rect { + for (i, &h) in rect.halfedges.iter().enumerate() { + let id = brep.topology.halfedges[h as usize].edge; + counts[id as usize] = counts[id as usize].max(desired[i % 2]); + } + } + grids.push(Some((rect, desired))); + } + + synchronize_edge_samples(brep, &grids, &mut counts); + let sample_count = counts + .iter() + .try_fold(0usize, |total, &n| total.checked_add(n + 1)) + .ok_or_else(|| GeometryError::LimitExceeded("edge samples".into()))?; + if sample_count > max_triangles * 3 { + return Err(GeometryError::LimitExceeded("edge sample budget".into())); + } + let mut samples = Vec::with_capacity(counts.len()); + for (id, n) in counts.into_iter().enumerate() { + samples.push(sample_edge(brep, id as u32, n)?); + } + let mut mesh = Tessellation::new(brep.revision, deflection); + add_face_triangles(brep, &grids, &samples, error, &mut mesh, max_triangles)?; + add_edge_outlines(brep, samples, &mut mesh); + Ok(mesh) +} + +pub(super) fn coordinate_magnitude(brep: &BrepEnvelope) -> Result { + let mut magnitude: f64 = 0.0; + for face in &brep.topology.faces { + let surface = brep.geometry.surface(face.surface)?; + for value in surface.frame().origin { + magnitude = magnitude.max(value.abs()); + } + let bounds = surface.enclose(face.trim.uv_bounds)?; + for axis in bounds.axes { + magnitude = magnitude.max(axis.lo.abs()).max(axis.hi.abs()); + } + } + for vertex in &brep.topology.vertices { + for value in vertex.position { + magnitude = magnitude.max(value.abs()); + } + } + for edge in &brep.topology.edges { + if let EdgeGeometry::Curve { curve, range } = edge.geometry { + let curve = brep.geometry.curve(curve)?; + for axis in curve.enclose(range)?.axes { + magnitude = magnitude.max(axis.lo.abs()).max(axis.hi.abs()); + } + match curve.geometry { + CurveGeometry::Circle { frame, radius } => { + magnitude = magnitude.max(*radius); + for value in frame.origin { + magnitude = magnitude.max(value.abs()); + } + } + CurveGeometry::Ellipse { + frame, + major_radius, + .. + } => { + magnitude = magnitude.max(*major_radius); + for value in frame.origin { + magnitude = magnitude.max(value.abs()); + } + } + _ => {} + } + } + } + Ok(magnitude) +} + +fn add_face_triangles( + brep: &BrepEnvelope, + grids: &[Option<(Option, [usize; 2])>], + samples: &[Vec], + error: f64, + mesh: &mut Tessellation, + max_triangles: usize, +) -> Result<(), GeometryError> { + for (face, grid) in brep.topology.faces.iter().zip(grids) { + let before = mesh.triangle_face_ids.len(); + match grid { + Some((Some(rect), desired)) if face.trim.holes.is_empty() => { + curved_face(brep, face, rect, *desired, samples, mesh, max_triangles)? + } + Some((rect, desired)) => curved_trimmed_face( + brep, + face, + &FaceGrid { + rect: rect.as_ref(), + desired: *desired, + }, + samples, + error, + mesh, + max_triangles, + )?, + None => planar_face(brep, face, samples, mesh, max_triangles)?, + } + if mesh.triangle_face_ids.len() == before { + return Err(GeometryError::UnresolvedTessellation(format!( + "face {} produced no nondegenerate tessellation triangles", + face.id + ))); + } + } + Ok(()) +} + +fn add_edge_outlines(brep: &BrepEnvelope, samples: Vec>, mesh: &mut Tessellation) { + for (edge, points) in brep.topology.edges.iter().zip(samples) { + let planar_subdivision = edge + .twin_halfedge + .and_then(|twin| { + let first = &brep.topology.halfedges[edge.halfedge as usize]; + let second = &brep.topology.halfedges[twin as usize]; + let a = &brep.topology.faces[first.face? as usize]; + let b = &brep.topology.faces[second.face? as usize]; + match ( + &brep.geometry.surfaces[a.surface as usize], + &brep.geometry.surfaces[b.surface as usize], + ) { + ( + SurfaceGeometry::Plane { frame: a_frame }, + SurfaceGeometry::Plane { frame: b_frame }, + ) => Some( + scale(a_frame.z, a.sense.multiplier()) + == scale(b_frame.z, b.sense.multiplier()), + ), + _ => Some(false), + } + }) + .unwrap_or(false); + if edge.chart_seam || planar_subdivision { + continue; + } + for pair in points.windows(2) { + mesh.outline_positions.extend(pair[0]); + mesh.outline_positions.extend(pair[1]); + mesh.outline_edge_ids.push(edge.id); + } + } +} diff --git a/main/opengeometry/src/tessellation/tests/display.rs b/main/opengeometry/src/tessellation/tests/display.rs new file mode 100644 index 0000000..b824451 --- /dev/null +++ b/main/opengeometry/src/tessellation/tests/display.rs @@ -0,0 +1,7 @@ +use crate::tessellation::display::encode; + +#[test] +fn nonfinite_f32_encoding_is_rejected() { + let mut error = 0.0; + assert!(encode(&[f64::MAX, 0.0, 0.0], [0.0; 3], &mut error).is_err()); +} diff --git a/main/opengeometry/src/tessellation/tests/mod.rs b/main/opengeometry/src/tessellation/tests/mod.rs new file mode 100644 index 0000000..31eb3c0 --- /dev/null +++ b/main/opengeometry/src/tessellation/tests/mod.rs @@ -0,0 +1,262 @@ +mod display; +mod seams; +mod short_loops; +mod support; + +use super::cache::TessellationCache; +use super::tessellate::tessellate; +use crate::brep::{ + bounds_chord_deviation, circular_face, fine_accuracy, Accuracy, BrepEnvelope, Curve, + CurveGeometry, EdgeGeometry, Frame3, GeometryError, GeometryStore, Loop, Orientation, + PcurveGeometry, +}; +use crate::math::{add, cross, norm, scale, sub, Interval, Point3}; +use crate::primitives; +use std::collections::HashMap; +use std::sync::Arc; +use support::bounded_curve_count; + +fn shapes() -> Vec { + vec![ + primitives::cylinder("c".into(), Frame3::IDENTITY, 1.0, 2.0, fine_accuracy()).unwrap(), + primitives::cone("k".into(), Frame3::IDENTITY, 1.0, 2.0, fine_accuracy()).unwrap(), + primitives::frustum("f".into(), Frame3::IDENTITY, 2.0, 1.0, 3.0, fine_accuracy()).unwrap(), + primitives::sphere("s".into(), Frame3::IDENTITY, 1.0, fine_accuracy()).unwrap(), + primitives::torus("t".into(), Frame3::IDENTITY, 3.0, 1.0, fine_accuracy()).unwrap(), + ] +} +#[test] +fn conservative_curve_subdivision_respects_requested_error() { + let mut store = GeometryStore::new(); + store.curves.push(CurveGeometry::Circle { + frame: Frame3::from_axis([1.0, 2.0, 3.0], [1.0, 1.0, 1.0], [1.0, -1.0, 0.0]).unwrap(), + radius: 2.0, + }); + let curve = store.curve(0).unwrap(); + let range = Interval::new(0.0, std::f64::consts::TAU).unwrap(); + let loose = bounded_curve_count(&curve, range, 0.2, 1_000_000).unwrap(); + let fine = bounded_curve_count(&curve, range, 0.02, 1_000_000).unwrap(); + assert!(fine > loose); + for index in 0..fine { + let interval = Interval::new( + range.lo + range.width() * index as f64 / fine as f64, + range.lo + range.width() * (index + 1) as f64 / fine as f64, + ) + .unwrap(); + assert!( + bounds_chord_deviation( + curve.enclose(interval).unwrap(), + curve.point_at(interval.lo).unwrap(), + curve.point_at(interval.hi).unwrap(), + ) <= 0.02 + ); + } +} +#[test] +fn lod_changes_mesh_not_topology_and_retains_face_ids() { + for b in shapes() { + let original = b.to_json().unwrap(); + let coarse = tessellate(&b, 0.1, 1_000_000).unwrap(); + let fine = tessellate(&b, 0.02, 1_000_000).unwrap(); + assert!(fine.indices.len() > coarse.indices.len()); + assert_eq!(fine.indices.len() / 3, fine.triangle_face_ids.len()); + assert!(fine + .triangle_face_ids + .iter() + .all(|&f| (f as usize) < b.topology.faces.len())); + assert_eq!(b.to_json().unwrap(), original); + for n in fine.normals.chunks_exact(3) { + assert!((norm([n[0] as f64, n[1] as f64, n[2] as f64]) - 1.0).abs() < 1e-6); + } + } +} +#[test] +fn shared_boundaries_make_a_closed_geometric_mesh() { + for b in shapes() { + let mesh = tessellate(&b, 0.05, 1_000_000).unwrap(); + let key = |p: Point3| p.map(f64::to_bits); + let mut edges = HashMap::<([u64; 3], [u64; 3]), usize>::new(); + for t in mesh.indices.chunks_exact(3) { + for (a, c) in [(t[0], t[1]), (t[1], t[2]), (t[2], t[0])] { + let mut pair = [key(mesh.point(a)), key(mesh.point(c))]; + pair.sort(); + *edges.entry((pair[0], pair[1])).or_default() += 1; + } + } + assert!( + edges.values().all(|&count| count == 2), + "{} has unmatched geometric mesh edges", + b.id + ); + } +} +#[test] +fn spherical_triangle_samples_obey_deflection() { + let b = primitives::sphere("s".into(), Frame3::IDENTITY, 1.0, fine_accuracy()).unwrap(); + let deflection = 0.05; + let mesh = tessellate(&b, deflection, 1_000_000).unwrap(); + for ids in mesh.indices.chunks_exact(3) { + let p = ids.iter().map(|&id| mesh.point(id)).fold([0.0; 3], add); + assert!(1.0 - norm(scale(p, 1.0 / 3.0)) <= deflection); + } +} +#[test] +fn periodic_chart_lifts_keep_shared_positions_and_hide_seams() { + let b = + primitives::cylinder("lift".into(), Frame3::IDENTITY, 1.0, 2.0, fine_accuracy()).unwrap(); + let original = tessellate(&b, 0.05, 1_000_000).unwrap(); + let mut lifted = b; + for h in &mut lifted.topology.halfedges { + if h.face == Some(0) { + h.geometry_use.periodic_lift = [1, 0]; + } + } + let tau = std::f64::consts::TAU; + lifted.topology.faces[0].trim.uv_bounds[0] = Interval::new(tau, 2.0 * tau).unwrap(); + let mesh = tessellate(&lifted, 0.05, 1_000_000).unwrap(); + assert_eq!(original.positions, mesh.positions); + assert_eq!(original.outline_positions, mesh.outline_positions); + assert_eq!(original.outline_edge_ids, mesh.outline_edge_ids); +} +#[test] +fn planar_tessellation_preserves_a_circular_hole() { + let mut b = circular_face(); + b.geometry.curves[0] = CurveGeometry::Circle { + frame: Frame3::IDENTITY, + radius: 2.0, + }; + b.geometry.pcurves[0] = PcurveGeometry::Conic2 { + origin: [0.0; 2], + axis_a: [2.0, 0.0], + axis_b: [0.0, 2.0], + }; + b.topology.vertices[0].position = [2.0, 0.0, 0.0]; + b.topology.faces[0].trim.uv_bounds = [Interval::new(-2.0, 2.0).unwrap(); 2]; + b.topology.faces[0].trim.holes.push(1); + b.geometry.curves.push(CurveGeometry::Circle { + frame: Frame3::IDENTITY, + radius: 1.0, + }); + b.geometry.pcurves.push(PcurveGeometry::Conic2 { + origin: [0.0; 2], + axis_a: [1.0, 0.0], + axis_b: [0.0, 1.0], + }); + let mut vertex = b.topology.vertices[0].clone(); + vertex.id = 1; + vertex.position = [1.0, 0.0, 0.0]; + vertex.outgoing_halfedge = Some(1); + b.topology.vertices.push(vertex); + let mut edge = b.topology.edges[0].clone(); + edge.id = 1; + edge.halfedge = 1; + edge.geometry = EdgeGeometry::Curve { + curve: 1, + range: Interval::new(0.0, std::f64::consts::TAU).unwrap(), + }; + b.topology.edges.push(edge); + let mut h = b.topology.halfedges[0].clone(); + h.id = 1; + h.from = 1; + h.to = 1; + h.next = Some(1); + h.prev = Some(1); + h.edge = 1; + h.loop_ref = Some(1); + h.geometry_use.pcurve = Some(1); + h.geometry_use.sense = Orientation::Reverse; + b.topology.halfedges.push(h); + b.topology.loops.push(Loop { + id: 1, + start_halfedge: 1, + face_ref: 0, + is_hole: true, + }); + let deflection = 0.02; + let mesh = tessellate(&b, deflection, 1_000_000).unwrap(); + let mut area = 0.0; + for ids in mesh.indices.chunks_exact(3) { + let [a, c, d] = [mesh.point(ids[0]), mesh.point(ids[1]), mesh.point(ids[2])]; + let center = scale(add(add(a, c), d), 1.0 / 3.0); + assert!(center[0].hypot(center[1]) >= 1.0 - deflection); + area += norm(cross(sub(c, a), sub(d, a))) * 0.5; + } + assert!((area - 3.0 * std::f64::consts::PI).abs() < 6.0 * std::f64::consts::PI * deflection); +} +#[test] +fn precision_limits_do_not_claim_unachievable_deflection() { + let near_cylinder = primitives::frustum( + "near".into(), + Frame3::IDENTITY, + 1.0, + 1.0 + 2.0_f64.powi(-48), + 1.0, + fine_accuracy(), + ) + .unwrap(); + assert!( + matches!(tessellate(&near_cylinder, 0.01, 1_000_000), Err(GeometryError::LimitExceeded(message)) if message.contains("coordinate precision")) + ); + let frame = Frame3 { + origin: [1e12, 0.0, 0.0], + ..Frame3::IDENTITY + }; + let b = primitives::sphere( + "far".into(), + frame, + 1.0, + Accuracy { + geometric: 0.001, + intersection: 0.0001, + ..fine_accuracy() + }, + ) + .unwrap(); + assert!( + matches!(tessellate(&b, 0.01, 1_000_000), Err(GeometryError::LimitExceeded(message)) if message.contains("coordinate precision")) + ); + let mut cache = TessellationCache::new(16_000_000); + let coarse = cache.tessellate(&b, 0.1, 1_000_000).unwrap(); + assert!(cache.tessellate(&b, 0.01, 1_000_000).is_err()); + assert!(Arc::ptr_eq( + &coarse, + &cache.tessellate(&b, 0.1, 1_000_000).unwrap() + )); +} +#[test] +fn extreme_scales_return_errors_instead_of_invalid_or_empty_meshes() { + for (radius, geometric, deflection) in [(1e300, 1e290, 1e299), (1e-200, 1e-210, 1e-201)] { + let b = primitives::sphere( + "extreme".into(), + Frame3::IDENTITY, + radius, + Accuracy { + geometric, + intersection: geometric / 4.0, + ..fine_accuracy() + }, + ) + .unwrap(); + assert!(matches!( + tessellate(&b, deflection, 1_000_000), + Err(GeometryError::UnresolvedTessellation(_)) + )); + } +} +#[test] +fn cache_tracks_geometry_even_when_revision_is_not_incremented() { + let mut b = + primitives::cylinder("c".into(), Frame3::IDENTITY, 1.0, 2.0, fine_accuracy()).unwrap(); + let mut cache = TessellationCache::new(16_000_000); + let a = cache.tessellate(&b, 0.1, 1_000_000).unwrap(); + let again = cache.tessellate(&b, 0.1, 1_000_000).unwrap(); + assert!(Arc::ptr_eq(&a, &again)); + b.topology.faces[0].key = "changed".into(); + let changed = cache.tessellate(&b, 0.1, 1_000_000).unwrap(); + assert!(!Arc::ptr_eq(&a, &changed)); + for d in [0.08, 0.06, 0.04, 0.02] { + cache.tessellate(&b, d, 1_000_000).unwrap(); + } + assert_eq!(cache.len(), 3); + assert!(tessellate(&b, 0.001, 1).is_err()); +} diff --git a/main/opengeometry/src/tessellation/tests/seams.rs b/main/opengeometry/src/tessellation/tests/seams.rs new file mode 100644 index 0000000..20c9b57 --- /dev/null +++ b/main/opengeometry/src/tessellation/tests/seams.rs @@ -0,0 +1,31 @@ +use crate::brep::{coarse_accuracy, GROUND}; +use crate::operations::creating::{sweep, ProfileLoop}; +use crate::tessellation::tessellate; +use std::collections::HashMap; + +#[test] +fn circular_sweep_midpoints_reuse_boundary_points_for_bitwise_watertight_seams() { + let profile = ProfileLoop::Circle { + frame: GROUND, + radius: 0.5, + }; + let path = vec![[0.0, 0.0, 0.0], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]]; + let body = sweep::build("solid".into(), profile, path, coarse_accuracy(1e-6)).unwrap(); + let mesh = tessellate(&body, 0.02, 1_000_000).unwrap(); + let bits = |index: u32| -> Vec { + let start = index as usize * 3; + mesh.positions[start..start + 3] + .iter() + .map(|value| value.to_bits()) + .collect() + }; + let mut uses: HashMap>, usize> = HashMap::new(); + for corner in 0..mesh.indices.len() { + let next = corner - corner % 3 + (corner + 1) % 3; + let mut key = vec![bits(mesh.indices[corner]), bits(mesh.indices[next])]; + key.sort(); + *uses.entry(key).or_default() += 1; + } + assert!(!uses.is_empty()); + assert!(uses.values().all(|&count| count == 2)); +} diff --git a/main/opengeometry/src/tessellation/tests/short_loops.rs b/main/opengeometry/src/tessellation/tests/short_loops.rs new file mode 100644 index 0000000..8d4d5f8 --- /dev/null +++ b/main/opengeometry/src/tessellation/tests/short_loops.rs @@ -0,0 +1,84 @@ +use crate::brep::{fine_accuracy, Frame3, SurfaceGeometry}; +use crate::math::{cross, norm, sub}; +use crate::primitives::{self, ProfileEdge}; +use crate::tessellation::tessellate; +use std::collections::BTreeSet; +use std::f64::consts::PI; + +const RADIUS: f64 = 0.05; +const STOREY_DEFLECTION: f64 = 0.1875; + +fn half_circle(start_angle: f64) -> ProfileEdge { + ProfileEdge::Arc { + center: [0.0, 0.0], + radius: RADIUS, + start_angle, + sweep_angle: PI, + } +} + +fn cap_points_and_smallest_area(profile: Vec) -> Vec<(usize, f64)> { + let body = primitives::arc_edged_extrusion( + "rail".into(), + Frame3::IDENTITY, + profile, + 7.0, + fine_accuracy(), + ) + .unwrap(); + let mesh = tessellate(&body, STOREY_DEFLECTION, 1_000_000).unwrap(); + let mut caps = Vec::new(); + for face in &body.topology.faces { + let SurfaceGeometry::Plane { frame } = &body.geometry.surfaces[face.surface as usize] + else { + continue; + }; + if frame.z[2].abs() < 0.5 { + continue; + } + let mut ids = BTreeSet::new(); + let mut smallest = f64::INFINITY; + for (triangle, &owner) in mesh.indices.chunks_exact(3).zip(&mesh.triangle_face_ids) { + if owner != face.id { + continue; + } + ids.extend(triangle.iter().copied()); + let [a, b, c] = [triangle[0], triangle[1], triangle[2]].map(|id| mesh.point(id)); + smallest = smallest.min(0.5 * norm(cross(sub(b, a), sub(c, a)))); + } + caps.push((ids.len(), smallest)); + } + caps +} + +#[test] +fn two_arc_planar_loop_tessellates_at_a_coarse_deflection() { + let caps = cap_points_and_smallest_area(vec![half_circle(0.0), half_circle(PI)]); + assert_eq!(caps.len(), 2); + for (points, smallest) in caps { + assert!(points >= 4, "cap has {points} boundary points"); + assert!( + smallest > 0.1 * RADIUS * RADIUS, + "cap triangle area {smallest}" + ); + } +} + +#[test] +fn arc_and_line_planar_loop_tessellates_at_a_coarse_deflection() { + let caps = cap_points_and_smallest_area(vec![ + half_circle(0.0), + ProfileEdge::Line { + from: [-RADIUS, 0.0], + to: [RADIUS, 0.0], + }, + ]); + assert_eq!(caps.len(), 2); + for (points, smallest) in caps { + assert!(points >= 3, "cap has {points} boundary points"); + assert!( + smallest > 0.1 * RADIUS * RADIUS, + "cap triangle area {smallest}" + ); + } +} diff --git a/main/opengeometry/src/tessellation/tests/support.rs b/main/opengeometry/src/tessellation/tests/support.rs new file mode 100644 index 0000000..3d1da0e --- /dev/null +++ b/main/opengeometry/src/tessellation/tests/support.rs @@ -0,0 +1,40 @@ +use crate::brep::{bounds_chord_deviation, Curve, GeometryError}; +use crate::math::Interval; + +pub(super) fn bounded_curve_count( + curve: &impl Curve, + range: Interval, + error: f64, + max_segments: usize, +) -> Result { + let mut stack = vec![(range, 0usize)]; + let mut maximum_depth = 0usize; + let mut visits = 0usize; + while let Some((interval, depth)) = stack.pop() { + visits = visits + .checked_add(1) + .ok_or_else(|| GeometryError::LimitExceeded("curve tessellation visits".into()))?; + if visits > max_segments.saturating_mul(2) { + return Err(GeometryError::LimitExceeded( + "curve tessellation visits".into(), + )); + } + let a = curve.point_at(interval.lo)?; + let b = curve.point_at(interval.hi)?; + if bounds_chord_deviation(curve.enclose(interval)?, a, b) <= error { + maximum_depth = maximum_depth.max(depth); + continue; + } + if depth >= usize::BITS as usize - 1 || (1usize << (depth + 1)) > max_segments { + return Err(GeometryError::LimitExceeded( + "curve tessellation segments".into(), + )); + } + let midpoint = interval.midpoint(); + let left = Interval::new(interval.lo, midpoint)?; + let right = Interval::new(midpoint, interval.hi)?; + stack.push((right, depth + 1)); + stack.push((left, depth + 1)); + } + Ok(1usize << maximum_depth) +} diff --git a/main/opengeometry/src/tessellation/trim.rs b/main/opengeometry/src/tessellation/trim.rs new file mode 100644 index 0000000..75e8771 --- /dev/null +++ b/main/opengeometry/src/tessellation/trim.rs @@ -0,0 +1,288 @@ +use crate::brep::{ + period_lifted_uv, BrepEnvelope, EdgeGeometry, Face, GeometryError, HalfEdge, Orientation, + Surface, SurfaceGeometry, +}; +use crate::math::Point3; + +pub(super) fn trim_loop_samples( + brep: &BrepEnvelope, + face: &Face, + loop_id: u32, + samples: &[Vec], +) -> Result, GeometryError> { + let periods = brep.geometry.surface(face.surface)?.charts()[face.trim.chart as usize].periods; + let start = brep.topology.loops[loop_id as usize].start_halfedge; + let mut current = start; + let mut result = Vec::new(); + loop { + let halfedge = &brep.topology.halfedges[current as usize]; + let edge = &brep.topology.edges[halfedge.edge as usize]; + let source = &samples[halfedge.edge as usize]; + let pcurve = halfedge + .geometry_use + .pcurve + .ok_or_else(|| GeometryError::InvalidTopology("missing face pcurve".into()))?; + let range = match edge.geometry { + EdgeGeometry::Curve { range, .. } => range, + EdgeGeometry::Collapsed { vertex } => { + let parameter = if halfedge.geometry_use.sense == Orientation::Forward { + 0.0 + } else { + 1.0 + }; + let uv = lifted_uv(brep, halfedge, pcurve, parameter, periods)?; + result.push((uv, brep.topology.vertices[vertex as usize].position)); + current = halfedge + .next + .ok_or_else(|| GeometryError::InvalidTopology("open face loop".into()))?; + if current == start { + break; + } + continue; + } + }; + let segment_count = source.len().saturating_sub(1); + if segment_count == 0 { + return Err(GeometryError::InvalidTopology( + "empty curved trim edge sample".into(), + )); + } + for offset in 0..segment_count { + let sample_index = if halfedge.geometry_use.sense == Orientation::Forward { + offset + } else { + segment_count - offset + }; + let parameter = range.lo + range.width() * sample_index as f64 / segment_count as f64; + let uv = lifted_uv(brep, halfedge, pcurve, parameter, periods)?; + result.push((uv, source[sample_index])); + } + current = halfedge + .next + .ok_or_else(|| GeometryError::InvalidTopology("open face loop".into()))?; + if current == start { + break; + } + if result.len() + > brep + .topology + .halfedges + .len() + .saturating_mul(maximum_edge_samples(samples)) + { + return Err(GeometryError::LimitExceeded( + "curved trim loop samples".into(), + )); + } + } + if result.len() < 4 { + return Err(GeometryError::InvalidTopology( + "curved trim loop requires at least four samples".into(), + )); + } + Ok(result) +} + +fn lifted_uv( + brep: &BrepEnvelope, + halfedge: &HalfEdge, + pcurve: u32, + parameter: f64, + periods: [Option; 2], +) -> Result<[f64; 2], GeometryError> { + let uv = brep.geometry.pcurve_at(pcurve, parameter)?; + Ok(period_lifted_uv( + uv, + periods, + halfedge.geometry_use.periodic_lift, + )) +} + +fn maximum_edge_samples(samples: &[Vec]) -> usize { + samples.iter().map(Vec::len).max().unwrap_or(1) +} + +pub(super) fn periodic_band_loop( + face: &Face, + periods: [Option; 2], + loops: &[Vec<([f64; 2], Point3)>], +) -> Option> { + if loops.len() != 2 { + return None; + } + for axis in 0..2 { + let Some(period) = periods[axis] else { + continue; + }; + let windings = [ + periodic_ring_winding(&loops[0], axis, period), + periodic_ring_winding(&loops[1], axis, period), + ]; + if windings.iter().any(|winding| winding.abs() != 1) { + continue; + } + let other = 1 - axis; + if let Some(other_period) = periods[other] { + if loops + .iter() + .any(|ring| periodic_ring_winding(ring, other, other_period) != 0) + { + continue; + } + } + let seam = face.trim.uv_bounds[axis].lo; + let mut first = open_periodic_ring(&loops[0], axis, period, seam, periods[other])?; + let mut second = open_periodic_ring(&loops[1], axis, period, seam, periods[other])?; + let first_mean = first.iter().map(|(uv, _)| uv[other]).sum::() / first.len() as f64; + let second_mean = second.iter().map(|(uv, _)| uv[other]).sum::() / second.len() as f64; + if first_mean > second_mean { + std::mem::swap(&mut first, &mut second); + } + second.reverse(); + first.extend(second); + return Some(first); + } + None +} + +fn periodic_ring_winding(ring: &[([f64; 2], Point3)], axis: usize, period: f64) -> i32 { + let delta = ring + .iter() + .zip(ring.iter().cycle().skip(1)) + .take(ring.len()) + .map(|(a, b)| { + let raw = b.0[axis] - a.0[axis]; + raw - (raw / period).round() * period + }) + .sum::(); + (delta / period).round() as i32 +} + +fn open_periodic_ring( + ring: &[([f64; 2], Point3)], + axis: usize, + period: f64, + seam: f64, + other_period: Option, +) -> Option> { + let other = 1 - axis; + let mut sorted = ring + .iter() + .map(|&(mut uv, point)| { + uv[axis] = seam + (uv[axis] - seam).rem_euclid(period); + (uv, point) + }) + .collect::>(); + sorted.sort_by(|a, b| a.0[axis].total_cmp(&b.0[axis])); + let tolerance = 256.0 * f64::EPSILON * period.max(1.0); + sorted.dedup_by(|a, b| (a.0[axis] - b.0[axis]).abs() <= tolerance); + if sorted.len() < 3 { + return None; + } + + let first = sorted[0]; + let last = *sorted.last()?; + let before = last.0[axis] - period; + let span = first.0[axis] - before; + if span <= tolerance { + return None; + } + let t = ((seam - before) / span).clamp(0.0, 1.0); + let mut other_delta = first.0[other] - last.0[other]; + if let Some(other_period) = other_period { + other_delta -= (other_delta / other_period).round() * other_period; + } + let other_value = last.0[other] + other_delta * t; + let seam_point = std::array::from_fn(|component| { + last.1[component] + (first.1[component] - last.1[component]) * t + }); + let mut seam_uv = first.0; + seam_uv[axis] = seam; + seam_uv[other] = other_value; + + let mut opened = Vec::with_capacity(sorted.len() + 2); + opened.push((seam_uv, seam_point)); + opened.extend( + sorted + .into_iter() + .filter(|(uv, _)| (uv[axis] - seam).abs() > tolerance), + ); + let mut end_uv = seam_uv; + end_uv[axis] += period; + opened.push((end_uv, seam_point)); + Some(opened) +} + +pub(super) fn merged_grid_coordinates( + uniform: impl Iterator, + boundary: impl Iterator, +) -> Vec { + let mut values = uniform + .map(|value| (value, false)) + .chain(boundary.map(|value| (value, true))) + .collect::>(); + values.sort_by(|a, b| a.0.total_cmp(&b.0)); + let magnitude = values + .iter() + .map(|(value, _)| value.abs()) + .fold(1.0_f64, f64::max); + let tolerance = 128.0 * f64::EPSILON * magnitude; + let mut merged: Vec<(f64, bool)> = Vec::with_capacity(values.len()); + for value in values { + if let Some(last) = merged.last_mut() { + if (value.0 - last.0).abs() <= tolerance { + if value.1 { + *last = value; + } + continue; + } + } + merged.push(value); + } + merged.into_iter().map(|(value, _)| value).collect() +} + +pub(super) fn trimmed_surface_edge_deviation( + surface: &SurfaceGeometry, + uv: &[[f64; 2]], + a: usize, + b: usize, +) -> f64 { + let du = (uv[b][0] - uv[a][0]).abs(); + let dv = (uv[b][1] - uv[a][1]).abs(); + let v = [uv[a][1].min(uv[b][1]), uv[a][1].max(uv[b][1])]; + let maximum_trig = |offset: f64, values: fn(f64) -> f64| { + let period = std::f64::consts::PI; + let contains_extremum = + ((v[0] - offset) / period).ceil() <= ((v[1] - offset) / period).floor(); + if contains_extremum { + 1.0 + } else { + values(v[0]).abs().max(values(v[1]).abs()) + } + }; + let acceleration = match surface { + SurfaceGeometry::Plane { .. } => 0.0, + SurfaceGeometry::Sphere { radius, .. } => { + let cos_v = maximum_trig(0.0, f64::cos); + let sin_v = maximum_trig(std::f64::consts::FRAC_PI_2, f64::sin); + radius * (cos_v * du * du + 2.0 * sin_v * du * dv + dv * dv) + } + SurfaceGeometry::Cylinder { radius, .. } => radius * du * du, + SurfaceGeometry::Cone { semi_angle, .. } => { + let slope = semi_angle.tan(); + let radius = uv[a][1].abs().max(uv[b][1].abs()) * slope; + radius * du * du + 2.0 * slope * du * dv + } + SurfaceGeometry::Torus { + major_radius, + minor_radius, + .. + } => { + (major_radius + minor_radius) * du * du + + 2.0 * minor_radius * du * dv + + minor_radius * dv * dv + } + }; + acceleration / 8.0 +} diff --git a/main/opengeometry/src/tessellation/trimmed_face.rs b/main/opengeometry/src/tessellation/trimmed_face.rs new file mode 100644 index 0000000..919b4ac --- /dev/null +++ b/main/opengeometry/src/tessellation/trimmed_face.rs @@ -0,0 +1,495 @@ +use super::grid::Rect; +use super::mesh::Tessellation; +use super::trim::{ + merged_grid_coordinates, periodic_band_loop, trim_loop_samples, trimmed_surface_edge_deviation, +}; +use crate::brep::{BrepEnvelope, Face, GeometryError, Surface, SurfaceGeometry}; +use crate::geom2d::{point_in_ring2, Pt2}; +use crate::math::{scale, Interval, Point3}; +use std::collections::{BTreeSet, HashMap}; + +pub(super) struct FaceGrid<'a> { + pub(super) rect: Option<&'a Rect>, + pub(super) desired: [usize; 2], +} + +struct TrimGrid<'a> { + us: &'a [f64], + vs: &'a [f64], + surface: &'a SurfaceGeometry, + face: &'a Face, +} + +pub(super) fn curved_trimmed_face( + brep: &BrepEnvelope, + face: &Face, + face_grid: &FaceGrid<'_>, + samples: &[Vec], + error: f64, + mesh: &mut Tessellation, + max_triangles: usize, +) -> Result<(), GeometryError> { + let FaceGrid { rect, desired } = *face_grid; + let mut size = desired; + if let Some(rect) = rect { + for (axis, &halfedge) in rect.halfedges.iter().enumerate() { + let edge = brep.topology.halfedges[halfedge as usize].edge as usize; + size[axis % 2] = size[axis % 2].max(samples[edge].len().saturating_sub(1)); + } + } + let mut loops = Vec::with_capacity(face.trim.holes.len() + 1); + for &loop_id in std::iter::once(&face.trim.outer).chain(&face.trim.holes) { + let points = trim_loop_samples(brep, face, loop_id, samples)?; + loops.push(points); + } + let surface = brep.geometry.surface(face.surface)?; + let periods = surface.charts()[face.trim.chart as usize].periods; + if let Some(band) = periodic_band_loop(face, periods, &loops) { + loops = vec![band]; + } + let axis_aligned = all_axis_aligned(&loops); + if rect.is_some() && !face.trim.holes.is_empty() { + shift_periodic_holes(face, surface, periods, &mut loops)?; + } + if !axis_aligned { + return general_curved_trimmed_face(brep, face, size, &loops, error, mesh, max_triangles); + } + let [u, v] = face.trim.uv_bounds; + let all_boundary = loops.iter().flatten().collect::>(); + let us = merged_grid_coordinates( + (0..=size[0]).map(|index| u.lo + u.width() * index as f64 / size[0] as f64), + all_boundary.iter().map(|(uv, _)| uv[0]), + ); + let vs = merged_grid_coordinates( + (0..=size[1]).map(|index| v.lo + v.width() * index as f64 / size[1] as f64), + all_boundary.iter().map(|(uv, _)| uv[1]), + ); + check_grid_budget(&us, &vs, max_triangles)?; + let coordinate_tolerance = 256.0 + * f64::EPSILON + * us.iter() + .chain(&vs) + .map(|value| value.abs()) + .fold(1.0_f64, f64::max); + let surface = brep.geometry.surface(face.surface)?; + let start = mesh.positions.len() / 3; + let grid = TrimGrid { + us: &us, + vs: &vs, + surface, + face, + }; + add_grid_vertices(&grid, &all_boundary, coordinate_tolerance, v, mesh)?; + add_grid_triangles(&grid, &loops, start, mesh, max_triangles)?; + Ok(()) +} + +fn all_axis_aligned(loops: &[Vec<([f64; 2], Point3)>]) -> bool { + let mut axis_aligned = true; + for points in loops { + let magnitude = points + .iter() + .flat_map(|(uv, _)| uv) + .map(|value| value.abs()) + .fold(1.0_f64, f64::max); + let tolerance = 128.0 * f64::EPSILON * magnitude; + for pair in points + .iter() + .zip(points.iter().cycle().skip(1)) + .take(points.len()) + { + let du = (pair.0 .0[0] - pair.1 .0[0]).abs(); + let dv = (pair.0 .0[1] - pair.1 .0[1]).abs(); + if du > tolerance && dv > tolerance { + axis_aligned = false; + } + } + } + axis_aligned +} + +fn shift_periodic_holes( + face: &Face, + surface: &SurfaceGeometry, + periods: [Option; 2], + loops: &mut [Vec<([f64; 2], Point3)>], +) -> Result<(), GeometryError> { + let mut bounds = face.trim.uv_bounds; + let mut shifted = false; + for axis in 0..2 { + let Some(period) = periods[axis] else { + continue; + }; + let mut lo = f64::INFINITY; + let mut hi = f64::NEG_INFINITY; + for (uv, _) in loops[1..].iter().flatten() { + lo = lo.min(uv[axis]); + hi = hi.max(uv[axis]); + } + if !lo.is_finite() || hi - lo >= period { + continue; + } + let original = face.trim.uv_bounds[axis]; + let tolerance = 256.0 * f64::EPSILON * original.lo.abs().max(original.hi.abs()).max(1.0); + if lo >= original.lo - tolerance && hi <= original.hi + tolerance { + continue; + } + let center = 0.5 * (lo + hi); + bounds[axis] = Interval::new(center - 0.5 * period, center + 0.5 * period)?; + for ring in &mut loops[1..] { + let ring_center = + ring.iter().map(|(uv, _)| uv[axis]).sum::() / ring.len().max(1) as f64; + let offset = ((center - ring_center) / period).round() * period; + for (uv, _) in ring { + uv[axis] += offset; + } + } + shifted = true; + } + if shifted { + let coordinates = [ + [bounds[0].lo, bounds[1].lo], + [bounds[0].hi, bounds[1].lo], + [bounds[0].hi, bounds[1].hi], + [bounds[0].lo, bounds[1].hi], + ]; + loops[0] = coordinates + .into_iter() + .map(|uv| Ok((uv, surface.point_at(uv)?))) + .collect::, GeometryError>>()?; + } + Ok(()) +} + +fn general_curved_trimmed_face( + brep: &BrepEnvelope, + face: &Face, + _desired: [usize; 2], + loops: &[Vec<([f64; 2], Point3)>], + error: f64, + mesh: &mut Tessellation, + max_triangles: usize, +) -> Result<(), GeometryError> { + let mut uv = Vec::new(); + let mut points = Vec::new(); + let mut holes = Vec::new(); + let mut boundary_edges = BTreeSet::new(); + for (loop_index, ring) in loops.iter().enumerate() { + if loop_index > 0 { + holes.push(uv.len()); + } + let start = uv.len(); + for &(coordinate, point) in ring { + uv.push(coordinate); + points.push(point); + } + for offset in 0..ring.len() { + let a = start + offset; + let b = start + (offset + 1) % ring.len(); + boundary_edges.insert(if a < b { (a, b) } else { (b, a) }); + } + } + let boundary_count = uv.len(); + let mut triangles = earcut_triangles(&uv, &holes)?; + let surface = brep.geometry.surface(face.surface)?; + for _ in 0..128 { + let split_edges = edges_to_split(&triangles, &boundary_edges, surface, &uv, error); + if split_edges.is_empty() { + break; + } + if triangles + .len() + .checked_mul(4) + .is_none_or(|count| count > max_triangles) + { + return Err(GeometryError::LimitExceeded( + "curved trim refinement triangles".into(), + )); + } + let midpoints = + add_edge_midpoints(&split_edges, surface, &mut uv, &mut points, boundary_count)?; + triangles = refined_triangles(triangles, &midpoints); + } + check_trim_refinement(face, surface, &triangles, &boundary_edges, &uv, error)?; + if triangles.len() > max_triangles { + return Err(GeometryError::LimitExceeded("curved trim triangles".into())); + } + let start = mesh.positions.len() / 3; + for (coordinate, point) in uv.iter().zip(points) { + let normal = trim_normal(brep, face, surface, *coordinate)?; + mesh.vertex(point, normal)?; + } + for triangle in triangles { + let center = std::array::from_fn(|axis| { + (uv[triangle[0]][axis] + uv[triangle[1]][axis] + uv[triangle[2]][axis]) / 3.0 + }); + let normal = trim_normal(brep, face, surface, center)?; + mesh.triangle( + triangle.map(|index| (start + index) as u32), + face.id, + normal, + max_triangles, + )?; + } + Ok(()) +} + +fn earcut_triangles(uv: &[[f64; 2]], holes: &[usize]) -> Result, GeometryError> { + let flattened = uv + .iter() + .flat_map(|coordinate| *coordinate) + .collect::>(); + let indices = earcutr::earcut(&flattened, holes, 2); + if indices.is_empty() { + return Err(GeometryError::InvalidTopology( + "curved trim triangulation failed".into(), + )); + } + Ok(indices + .chunks_exact(3) + .map(|triangle| [triangle[0], triangle[1], triangle[2]]) + .collect::>()) +} + +fn edges_to_split( + triangles: &[[usize; 3]], + boundary_edges: &BTreeSet<(usize, usize)>, + surface: &SurfaceGeometry, + uv: &[[f64; 2]], + error: f64, +) -> BTreeSet<(usize, usize)> { + let mut split_edges = BTreeSet::new(); + for triangle in triangles { + for [a, b] in [ + [triangle[0], triangle[1]], + [triangle[1], triangle[2]], + [triangle[2], triangle[0]], + ] { + let key = if a < b { (a, b) } else { (b, a) }; + if boundary_edges.contains(&key) { + continue; + } + let needs_refinement = trimmed_surface_edge_deviation(surface, uv, a, b) > error * 2.0; + if needs_refinement { + split_edges.insert(key); + } + } + } + split_edges +} + +fn add_edge_midpoints( + split_edges: &BTreeSet<(usize, usize)>, + surface: &SurfaceGeometry, + uv: &mut Vec<[f64; 2]>, + points: &mut Vec, + boundary_count: usize, +) -> Result, GeometryError> { + let mut midpoints = HashMap::with_capacity(split_edges.len()); + for &(a, b) in split_edges { + let coordinate = [(uv[a][0] + uv[b][0]) * 0.5, (uv[a][1] + uv[b][1]) * 0.5]; + let id = uv.len(); + uv.push(coordinate); + let match_boundary = (0..boundary_count).find(|&index| { + (0..2).all(|axis| { + (uv[index][axis] - coordinate[axis]).abs() + <= 4.0 * f64::EPSILON * coordinate[axis].abs().max(1.0) + }) + }); + points.push(if let Some(index) = match_boundary { + points[index] + } else { + surface.point_at(coordinate)? + }); + midpoints.insert((a, b), id); + } + Ok(midpoints) +} + +fn refined_triangles( + triangles: Vec<[usize; 3]>, + midpoints: &HashMap<(usize, usize), usize>, +) -> Vec<[usize; 3]> { + let midpoint = |a: usize, b: usize| { + let key = if a < b { (a, b) } else { (b, a) }; + midpoints.get(&key).copied() + }; + let mut refined = Vec::with_capacity(triangles.len() * 4); + for [a, b, c] in triangles { + let ab = midpoint(a, b); + let bc = midpoint(b, c); + let ca = midpoint(c, a); + match (ab, bc, ca) { + (None, None, None) => refined.push([a, b, c]), + (Some(ab), None, None) => refined.extend([[a, ab, c], [ab, b, c]]), + (None, Some(bc), None) => refined.extend([[b, bc, a], [bc, c, a]]), + (None, None, Some(ca)) => refined.extend([[c, ca, b], [ca, a, b]]), + (Some(ab), Some(bc), None) => refined.extend([[b, bc, ab], [ab, bc, c], [ab, c, a]]), + (None, Some(bc), Some(ca)) => refined.extend([[c, ca, bc], [bc, ca, a], [bc, a, b]]), + (Some(ab), None, Some(ca)) => refined.extend([[a, ab, ca], [ca, ab, b], [ca, b, c]]), + (Some(ab), Some(bc), Some(ca)) => { + refined.extend([[a, ab, ca], [ab, b, bc], [ca, bc, c], [ab, bc, ca]]) + } + } + } + refined +} + +fn check_trim_refinement( + face: &Face, + surface: &SurfaceGeometry, + triangles: &[[usize; 3]], + boundary_edges: &BTreeSet<(usize, usize)>, + uv: &[[f64; 2]], + error: f64, +) -> Result<(), GeometryError> { + for triangle in triangles { + for [a, b] in [ + [triangle[0], triangle[1]], + [triangle[1], triangle[2]], + [triangle[2], triangle[0]], + ] { + let key = if a < b { (a, b) } else { (b, a) }; + let needs_refinement = trimmed_surface_edge_deviation(surface, uv, a, b) > error * 2.0; + if !boundary_edges.contains(&key) && needs_refinement { + return Err(GeometryError::LimitExceeded(format!( + "curved trim refinement depth on face {}: bound {} for edge {:?} to {:?}", + face.id, + trimmed_surface_edge_deviation(surface, uv, a, b), + uv[a], + uv[b] + ))); + } + } + } + Ok(()) +} + +fn trim_normal( + brep: &BrepEnvelope, + face: &Face, + surface: &SurfaceGeometry, + coordinate: [f64; 2], +) -> Result { + let normal_coordinate = if matches!(surface, SurfaceGeometry::Cone { .. }) + && coordinate[1].abs() <= brep.accuracy.geometric + { + let bounds = face.trim.uv_bounds[1]; + [ + coordinate[0], + if bounds.lo.abs() > bounds.hi.abs() { + bounds.lo + } else { + bounds.hi + }, + ] + } else { + coordinate + }; + Ok(scale( + surface.normal_at(normal_coordinate)?, + face.sense.multiplier(), + )) +} + +fn check_grid_budget(us: &[f64], vs: &[f64], max_triangles: usize) -> Result<(), GeometryError> { + let vertex_count = us + .len() + .checked_mul(vs.len()) + .ok_or_else(|| GeometryError::LimitExceeded("trimmed surface grid vertices".into()))?; + let cell_count = us + .len() + .saturating_sub(1) + .checked_mul(vs.len().saturating_sub(1)) + .and_then(|count| count.checked_mul(2)) + .ok_or_else(|| GeometryError::LimitExceeded("trimmed surface grid triangles".into()))?; + if vertex_count > max_triangles.saturating_mul(3) || cell_count > max_triangles { + return Err(GeometryError::LimitExceeded( + "trimmed surface grid budget".into(), + )); + } + Ok(()) +} + +fn add_grid_vertices( + grid: &TrimGrid<'_>, + all_boundary: &[&([f64; 2], Point3)], + coordinate_tolerance: f64, + v: Interval, + mesh: &mut Tessellation, +) -> Result<(), GeometryError> { + let TrimGrid { + us, + vs, + surface, + face, + } = *grid; + for &chart_v in vs { + for &chart_u in us { + let uv = [chart_u, chart_v]; + let point = all_boundary + .iter() + .find(|(boundary_uv, _)| { + (boundary_uv[0] - chart_u).abs() <= coordinate_tolerance + && (boundary_uv[1] - chart_v).abs() <= coordinate_tolerance + }) + .map_or_else(|| surface.point_at(uv), |(_, point)| Ok(*point))?; + let normal_uv = if matches!(surface, SurfaceGeometry::Cone { .. }) && chart_v == 0.0 { + [chart_u, v.hi] + } else { + uv + }; + let normal = scale(surface.normal_at(normal_uv)?, face.sense.multiplier()); + mesh.vertex(point, normal)?; + } + } + Ok(()) +} + +fn add_grid_triangles( + grid: &TrimGrid<'_>, + loops: &[Vec<([f64; 2], Point3)>], + start: usize, + mesh: &mut Tessellation, + max_triangles: usize, +) -> Result<(), GeometryError> { + let TrimGrid { + us, + vs, + surface, + face, + } = *grid; + let rings = loops + .iter() + .map(|points| { + points + .iter() + .map(|(uv, _)| Pt2::new(uv[0], uv[1])) + .collect::>() + }) + .collect::>(); + for row in 0..vs.len() - 1 { + for column in 0..us.len() - 1 { + let center = Pt2::new( + (us[column] + us[column + 1]) * 0.5, + (vs[row] + vs[row + 1]) * 0.5, + ); + if !point_in_ring2(center, &rings[0]) + || rings[1..].iter().any(|hole| point_in_ring2(center, hole)) + { + continue; + } + let a = (start + row * us.len() + column) as u32; + let b = a + 1; + let c = a + us.len() as u32; + let d = c + 1; + let normal = scale( + surface.normal_at([center.x, center.z])?, + face.sense.multiplier(), + ); + mesh.triangle([a, b, d], face.id, normal, max_triangles)?; + mesh.triangle([a, d, c], face.id, normal, max_triangles)?; + } + } + Ok(()) +} diff --git a/main/opengeometry/src/tolerance.rs b/main/opengeometry/src/tolerance.rs deleted file mode 100644 index cfbeb21..0000000 --- a/main/opengeometry/src/tolerance.rs +++ /dev/null @@ -1,205 +0,0 @@ -//! Modeling tolerance hierarchy (debt item D2). -//! -//! A single global scalar cannot express that distances, areas, and angles -//! tolerate different errors, nor that geometry produced by an operation may -//! carry its own tolerance. Historically this kernel scattered ad-hoc epsilon -//! constants across modules (`EXTRUDE_EPSILON`, `CAP_ALIGNMENT_THRESHOLD`, -//! `STEP_LENGTH_EPSILON`, operation-specific geometric tolerances, …). This -//! module is the single tolerance model that all comparison-based operations -//! consult, so near-coincident geometry is handled consistently instead of -//! per-operation. -//! -//! Conventions: -//! - Lengths are in **model units** (typically metres in OpenGeometry-using -//! apps; see the unit model, debt D8). -//! - Angles are in **radians**. -//! -//! This is the interface other foundational items (D1 geometry, D4 booleans, -//! D5 validity, D7 profiles) are expected to consume. Per-entity tolerance -//! (per-vertex / per-edge) layers on top via [`ToleranceContext::effective`]. - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; - -/// Absolute floor for any auto-derived length tolerance, in model units. -/// Below this, floating-point noise dominates and comparisons are meaningless. -pub const MODELING_TOLERANCE_FLOOR: f64 = 1.0e-9; - -/// Relative factor applied to an operand's bounding-box diagonal when a length -/// tolerance is auto-derived. Matches the historical boolean default so the -/// auto-scale path is byte-for-byte unchanged. -pub const DEFAULT_RELATIVE_TOLERANCE: f64 = 1.0e-8; - -/// Default angular tolerance in radians (~5.7e-2 degrees). Two directions -/// whose angle is within this are treated as parallel. -pub const DEFAULT_ANGULAR_TOLERANCE: f64 = 1.0e-3; - -/// Global modeling tolerances consulted by all comparison-based operations. -/// -/// - `confusion` is the distance below which two points are considered the -/// same point. It is the core length comparison threshold (OCC's -/// `Precision::Confusion`). -/// - `modeling` is the working/build tolerance (`>= confusion`); it is the -/// default per-entity tolerance assigned to geometry an operation produces. -/// - `angular` is the angle (radians) below which two directions are parallel -/// (OCC's `Precision::Angular`). -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] -pub struct ToleranceContext { - pub confusion: f64, - pub modeling: f64, - pub angular: f64, -} - -impl Default for ToleranceContext { - fn default() -> Self { - Self { - confusion: MODELING_TOLERANCE_FLOOR, - modeling: MODELING_TOLERANCE_FLOOR, - angular: DEFAULT_ANGULAR_TOLERANCE, - } - } -} - -impl ToleranceContext { - /// Builds a context from an explicit modeling tolerance, using it for both - /// `modeling` and `confusion` (clamped to the floor). - pub fn from_modeling(modeling: f64) -> Self { - let modeling = modeling.max(MODELING_TOLERANCE_FLOOR); - Self { - confusion: modeling, - modeling, - angular: DEFAULT_ANGULAR_TOLERANCE, - } - } - - /// Auto-derives a modeling tolerance from a bounding-box diagonal, using the - /// same `diagonal * 1e-8` (floored at `1e-9`) rule the boolean pipeline has - /// always used. Returned as a bare scalar so existing call sites can adopt - /// it without behavioural change. - pub fn derived_modeling_for_diagonal(diagonal: f64) -> f64 { - if !diagonal.is_finite() { - return MODELING_TOLERANCE_FLOOR; - } - (diagonal * DEFAULT_RELATIVE_TOLERANCE).max(MODELING_TOLERANCE_FLOOR) - } - - /// Builds a context whose `modeling`/`confusion` are auto-derived from a - /// bounding-box diagonal. - pub fn from_diagonal(diagonal: f64) -> Self { - Self::from_modeling(Self::derived_modeling_for_diagonal(diagonal)) - } - - /// The effective length tolerance for a comparison, taking an optional - /// per-entity override (per-vertex / per-edge tolerance, D2 follow-on) into - /// account. The looser of the two wins, since either party tolerating the - /// error makes the comparison pass. - pub fn effective(&self, entity_tolerance: Option) -> f64 { - match entity_tolerance { - Some(t) => self.confusion.max(t), - None => self.confusion, - } - } - - /// Whether a (non-negative) distance is within confusion tolerance. - pub fn distance_within(&self, distance: f64) -> bool { - distance <= self.confusion - } - - /// Whether two scalar lengths are equal within confusion tolerance. - pub fn lengths_equal(&self, a: f64, b: f64) -> bool { - (a - b).abs() <= self.confusion - } - - /// Whether two points are coincident within confusion tolerance. - /// Uses squared distance to avoid a `sqrt`. - pub fn points_coincident(&self, a: Vector3, b: Vector3) -> bool { - let dx = a.x - b.x; - let dy = a.y - b.y; - let dz = a.z - b.z; - (dx * dx + dy * dy + dz * dz) <= self.confusion * self.confusion - } - - /// Cosine of the angular tolerance — the threshold a direction dot product - /// must meet to count as aligned. - pub fn angular_cos(&self) -> f64 { - self.angular.cos() - } - - /// Whether two unit directions are parallel within angular tolerance - /// (either same or opposite sense). - pub fn directions_parallel(&self, cos_angle: f64) -> bool { - cos_angle.abs() >= self.angular_cos() - } - - /// Whether two unit directions are aligned (same sense) within angular - /// tolerance. - pub fn directions_aligned(&self, cos_angle: f64) -> bool { - cos_angle >= self.angular_cos() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn default_is_floored() { - let t = ToleranceContext::default(); - assert_eq!(t.confusion, MODELING_TOLERANCE_FLOOR); - assert_eq!(t.modeling, MODELING_TOLERANCE_FLOOR); - } - - #[test] - fn from_modeling_clamps_to_floor() { - let t = ToleranceContext::from_modeling(0.0); - assert_eq!(t.modeling, MODELING_TOLERANCE_FLOOR); - assert_eq!(t.confusion, MODELING_TOLERANCE_FLOOR); - } - - #[test] - fn derived_matches_historical_boolean_rule() { - // Historical rule: (diagonal * 1e-8).max(1e-9). - assert_eq!( - ToleranceContext::derived_modeling_for_diagonal(10.0), - (10.0_f64 * 1.0e-8).max(1.0e-9) - ); - // Small diagonal floors out. - assert_eq!( - ToleranceContext::derived_modeling_for_diagonal(0.01), - 1.0e-9 - ); - // Non-finite is safe. - assert_eq!( - ToleranceContext::derived_modeling_for_diagonal(f64::INFINITY), - MODELING_TOLERANCE_FLOOR - ); - } - - #[test] - fn effective_takes_the_looser_tolerance() { - let t = ToleranceContext::from_modeling(1.0e-6); - assert_eq!(t.effective(None), 1.0e-6); - assert_eq!(t.effective(Some(1.0e-3)), 1.0e-3); - assert_eq!(t.effective(Some(1.0e-9)), 1.0e-6); - } - - #[test] - fn points_coincident_respects_confusion() { - let t = ToleranceContext::from_modeling(1.0e-3); - let a = Vector3::new(0.0, 0.0, 0.0); - let near = Vector3::new(5.0e-4, 0.0, 0.0); - let far = Vector3::new(2.0e-3, 0.0, 0.0); - assert!(t.points_coincident(a, near)); - assert!(!t.points_coincident(a, far)); - } - - #[test] - fn directions_parallel_handles_both_senses() { - let t = ToleranceContext::default(); - assert!(t.directions_parallel(1.0)); - assert!(t.directions_parallel(-1.0)); - assert!(t.directions_aligned(1.0)); - assert!(!t.directions_aligned(-1.0)); - assert!(!t.directions_parallel(0.0)); - } -} diff --git a/main/opengeometry/src/units.rs b/main/opengeometry/src/units.rs deleted file mode 100644 index 248cbec..0000000 --- a/main/opengeometry/src/units.rs +++ /dev/null @@ -1,120 +0,0 @@ -//! Length unit & scale model (debt item D8). -//! -//! The kernel historically carried no unit: geometry lived in abstract scene -//! units and STEP/IFC export hardcoded `SI_UNIT(.METRE.)`, so an exported file -//! could not state what its numbers meant and tools imported parts at the -//! wrong size. A model now carries an explicit [`LengthUnit`]; exporters emit -//! the matching unit context (D9) so exchange is unit-correct. -//! -//! The conventional default is millimetres, matching mechanical CAD and the -//! STEP ecosystem. - -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum LengthUnit { - Micrometre, - Millimetre, - Centimetre, - Metre, - Kilometre, - Inch, - Foot, -} - -impl Default for LengthUnit { - /// mm — the mechanical-CAD / STEP convention. - fn default() -> Self { - LengthUnit::Millimetre - } -} - -impl LengthUnit { - /// Length of one unit expressed in metres. Exact for SI and for the - /// internationally-defined inch (0.0254 m) and foot. - pub fn metres_per_unit(self) -> f64 { - match self { - LengthUnit::Micrometre => 1.0e-6, - LengthUnit::Millimetre => 1.0e-3, - LengthUnit::Centimetre => 1.0e-2, - LengthUnit::Metre => 1.0, - LengthUnit::Kilometre => 1.0e3, - LengthUnit::Inch => 0.0254, - LengthUnit::Foot => 0.3048, - } - } - - /// The factor to multiply a length in `self` by to express it in `target`. - pub fn conversion_factor_to(self, target: LengthUnit) -> f64 { - self.metres_per_unit() / target.metres_per_unit() - } - - /// Whether this unit is a metric/SI multiple of the metre (and so can be - /// emitted as a STEP `SI_UNIT` with a prefix). - pub fn is_si(self) -> bool { - !matches!(self, LengthUnit::Inch | LengthUnit::Foot) - } - - /// STEP `SI_UNIT` prefix token (including the surrounding dots), or `$` for - /// the base metre. `None` for non-SI units (use a conversion-based unit). - pub fn step_si_prefix(self) -> Option<&'static str> { - match self { - LengthUnit::Micrometre => Some(".MICRO."), - LengthUnit::Millimetre => Some(".MILLI."), - LengthUnit::Centimetre => Some(".CENTI."), - LengthUnit::Metre => Some("$"), - LengthUnit::Kilometre => Some(".KILO."), - LengthUnit::Inch | LengthUnit::Foot => None, - } - } - - /// Lowercase name used in conversion-based unit definitions / IFC. - pub fn name(self) -> &'static str { - match self { - LengthUnit::Micrometre => "micrometre", - LengthUnit::Millimetre => "millimetre", - LengthUnit::Centimetre => "centimetre", - LengthUnit::Metre => "metre", - LengthUnit::Kilometre => "kilometre", - LengthUnit::Inch => "inch", - LengthUnit::Foot => "foot", - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn default_is_millimetre() { - assert_eq!(LengthUnit::default(), LengthUnit::Millimetre); - } - - #[test] - fn conversions_are_consistent() { - assert!( - (LengthUnit::Metre.conversion_factor_to(LengthUnit::Millimetre) - 1000.0).abs() - < 1.0e-9 - ); - assert!( - (LengthUnit::Centimetre.conversion_factor_to(LengthUnit::Millimetre) - 10.0).abs() - < 1.0e-9 - ); - assert!( - (LengthUnit::Inch.conversion_factor_to(LengthUnit::Millimetre) - 25.4).abs() < 1.0e-9 - ); - // round trip - let f = LengthUnit::Foot.conversion_factor_to(LengthUnit::Inch); - assert!((f - 12.0).abs() < 1.0e-9); - } - - #[test] - fn si_classification() { - assert!(LengthUnit::Millimetre.is_si()); - assert!(!LengthUnit::Inch.is_si()); - assert_eq!(LengthUnit::Millimetre.step_si_prefix(), Some(".MILLI.")); - assert_eq!(LengthUnit::Metre.step_si_prefix(), Some("$")); - assert_eq!(LengthUnit::Inch.step_si_prefix(), None); - } -} diff --git a/main/opengeometry/src/utility/bgeometry.rs b/main/opengeometry/src/utility/bgeometry.rs deleted file mode 100644 index 4647502..0000000 --- a/main/opengeometry/src/utility/bgeometry.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Rename this file to buffergeometry.rs - -use openmaths::Vector3; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -#[derive(Clone, Serialize, Deserialize)] -pub struct BufferGeometry { - pub id: Uuid, - pub vertices: Vec, - pub indices: Vec, -} - -impl BufferGeometry { - pub fn new(id: Uuid) -> Self { - BufferGeometry { - id, - vertices: Vec::new(), - indices: Vec::new(), - } - } - - pub fn add_vertex(&mut self, vertex: Vector3) { - self.vertices.push(vertex); - } - - pub fn add_index(&mut self, index: u32) { - self.indices.push(index); - } - - pub fn clear(&mut self) { - self.vertices.clear(); - self.indices.clear(); - } - - pub fn get_vertices(&self) -> &Vec { - &self.vertices - } - - pub fn get_indices(&self) -> &Vec { - &self.indices - } - - pub fn get_geometry_(&self) -> String { - // serialize geometry - let serialized = serde_json::to_string(&self).unwrap(); - serialized - } -} diff --git a/main/opengeometry/src/world/bvh.rs b/main/opengeometry/src/world/bvh.rs deleted file mode 100644 index a100493..0000000 --- a/main/opengeometry/src/world/bvh.rs +++ /dev/null @@ -1,177 +0,0 @@ -use super::graph::Bounds3; - -const LEAF_SIZE: usize = 4; - -enum BvhNode { - Leaf { - bounds: Bounds3, - start: usize, - count: usize, - }, - Inner { - bounds: Bounds3, - left: usize, - right: usize, - }, -} - -pub(super) struct Bvh { - nodes: Vec, - items: Vec, - boxes: Vec, -} - -impl Bvh { - pub(super) fn build(boxes: Vec) -> Self { - let mut bvh = Self { - nodes: Vec::new(), - items: (0..boxes.len()).collect(), - boxes, - }; - if !bvh.boxes.is_empty() { - bvh.split(0, bvh.items.len()); - } - bvh - } - - pub(super) fn query(&self, target: &Bounds3, hits: &mut Vec) { - if self.nodes.is_empty() { - return; - } - let mut pending = vec![0]; - while let Some(index) = pending.pop() { - match &self.nodes[index] { - BvhNode::Leaf { - bounds, - start, - count, - } => { - if overlaps(bounds, target) { - hits.extend( - self.items[*start..*start + *count] - .iter() - .copied() - .filter(|item| overlaps(&self.boxes[*item], target)), - ); - } - } - BvhNode::Inner { - bounds, - left, - right, - } => { - if overlaps(bounds, target) { - pending.push(*left); - pending.push(*right); - } - } - } - } - } - - fn split(&mut self, start: usize, end: usize) -> usize { - let bounds = self.items[start..end] - .iter() - .map(|item| self.boxes[*item]) - .reduce(union) - .unwrap_or([0.0; 6]); - let index = self.nodes.len(); - if end - start <= LEAF_SIZE { - self.nodes.push(BvhNode::Leaf { - bounds, - start, - count: end - start, - }); - return index; - } - let axis = (0..3) - .max_by(|a, b| (bounds[a + 3] - bounds[*a]).total_cmp(&(bounds[b + 3] - bounds[*b]))) - .unwrap_or(0); - let boxes = &self.boxes; - self.items[start..end] - .sort_by(|a, b| center(&boxes[*a], axis).total_cmp(¢er(&boxes[*b], axis))); - self.nodes.push(BvhNode::Leaf { - bounds, - start, - count: 0, - }); - let middle = start + (end - start) / 2; - let left = self.split(start, middle); - let right = self.split(middle, end); - self.nodes[index] = BvhNode::Inner { - bounds, - left, - right, - }; - index - } -} - -pub(super) fn overlaps(a: &Bounds3, b: &Bounds3) -> bool { - (0..3).all(|axis| a[axis] <= b[axis + 3] && b[axis] <= a[axis + 3]) -} - -fn union(a: Bounds3, b: Bounds3) -> Bounds3 { - [ - a[0].min(b[0]), - a[1].min(b[1]), - a[2].min(b[2]), - a[3].max(b[3]), - a[4].max(b[4]), - a[5].max(b[5]), - ] -} - -fn center(bounds: &Bounds3, axis: usize) -> f64 { - bounds[axis] / 2.0 + bounds[axis + 3] / 2.0 -} - -#[cfg(test)] -mod tests { - use super::*; - - fn pseudo_random_boxes(count: usize) -> Vec { - let mut state = 0x2545_f491_4f6c_dd1d_u64; - let mut next = || { - state ^= state << 13; - state ^= state >> 7; - state ^= state << 17; - (state % 10_000) as f64 / 100.0 - }; - (0..count) - .map(|_| { - let [x, y, z, w, h, d] = [ - next(), - next(), - next(), - next() / 10.0, - next() / 10.0, - next() / 10.0, - ]; - [x, y, z, x + w, y + h, z + d] - }) - .collect() - } - - #[test] - fn query_matches_brute_force() { - let boxes = pseudo_random_boxes(500); - let bvh = Bvh::build(boxes.clone()); - for target in pseudo_random_boxes(60) { - let mut hits = Vec::new(); - bvh.query(&target, &mut hits); - hits.sort_unstable(); - let expected: Vec = (0..boxes.len()) - .filter(|index| overlaps(&boxes[*index], &target)) - .collect(); - assert_eq!(hits, expected); - } - } - - #[test] - fn empty_tree_returns_nothing() { - let mut hits = Vec::new(); - Bvh::build(Vec::new()).query(&[0.0; 6], &mut hits); - assert!(hits.is_empty()); - } -} diff --git a/main/opengeometry/src/world/clash.rs b/main/opengeometry/src/world/clash.rs deleted file mode 100644 index 4da6f60..0000000 --- a/main/opengeometry/src/world/clash.rs +++ /dev/null @@ -1,484 +0,0 @@ -use super::graph::{Geometry, WorldError, WorldGraph}; -use super::pairs::Leaf; -use crate::analytic::face_intersection::intersect_breps; -use crate::analytic::geometry::{add, norm, scale}; -use crate::analytic::placement::placed; -use crate::analytic::query::{classify_point, PointClassification}; -use crate::analytic::{BrepEnvelope, Curve, GeometryError, Point3}; -use serde::Serialize; -use std::collections::HashMap; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum ClashKind { - Touch, - Hard, - Unresolved, -} - -#[derive(Clone, Debug, PartialEq, Serialize)] -pub struct Clash { - pub a: String, - pub b: String, - pub kind: ClashKind, - pub parts: [String; 2], - #[serde(skip_serializing_if = "Option::is_none")] - pub detail: Option, -} - -impl WorldGraph { - pub fn clash( - &self, - items_a: &[String], - items_b: &[String], - depth: f64, - ) -> Result, WorldError> { - if !depth.is_finite() || depth <= 0.0 { - return Err(WorldError::InvalidInput( - "clash depth must be finite and positive".into(), - )); - } - let candidates = self.candidate_pairs(items_a, items_b, 0.0)?; - let mut results: HashMap<(usize, usize), Clash> = HashMap::new(); - for &(index_a, index_b) in &candidates.pairs { - let key = candidates.item_key(index_a, index_b); - if results - .get(&key) - .is_some_and(|clash| clash.kind == ClashKind::Hard) - { - continue; - } - let (leaf_a, leaf_b) = (&candidates.leaves[index_a], &candidates.leaves[index_b]); - let (kind, detail) = match self.classify_pair(leaf_a, leaf_b, depth) { - Ok(Some(kind)) => (kind, None), - Ok(None) => continue, - Err(error) => (ClashKind::Unresolved, Some(format!("{error:?}"))), - }; - let candidate = Clash { - a: candidates.items[leaf_a.item].clone(), - b: candidates.items[leaf_b.item].clone(), - kind, - parts: [leaf_a.node.clone(), leaf_b.node.clone()], - detail, - }; - match results.get(&key) { - Some(existing) if existing.kind >= kind => {} - _ => { - results.insert(key, candidate); - } - } - } - let mut clashes: Vec = results.into_values().collect(); - clashes.sort_by(|x, y| (&x.a, &x.b).cmp(&(&y.a, &y.b))); - Ok(clashes) - } - - fn classify_pair( - &self, - a: &Leaf, - b: &Leaf, - depth: f64, - ) -> Result, GeometryError> { - let brep_a = self.definition_brep(&a.definition)?; - let brep_b = self.placed_relative(a, b)?; - let local_depth = depth / a.world.scale; - let minimum = 16.0 * brep_a.accuracy.geometric.max(brep_b.accuracy.geometric); - if local_depth <= minimum { - return Err(GeometryError::InvalidGeometry(format!( - "clash depth {depth} is below the geometric tolerance of the bodies" - ))); - } - let graph = intersect_breps(brep_a, &brep_b)?; - let containment = [ - vertex_containment(brep_a, &brep_b)?, - vertex_containment(&brep_b, brep_a)?, - ]; - if containment.contains(&Containment::Inside) { - return Ok(Some(ClashKind::Hard)); - } - if graph.pairs.is_empty() { - if containment.contains(&Containment::Undecided) { - return Err(GeometryError::UnresolvedIntersection( - "containment could not be decided from any vertex".into(), - )); - } - return Ok(None); - } - for pair in &graph.pairs { - for branch in &pair.graph.branches { - let curve = pair.graph.geometry.curve(branch.curve)?; - let midpoint = curve.point_at(branch.range.lo / 2.0 + branch.range.hi / 2.0)?; - let normal_a = brep_a.face_normal_at(pair.faces[0], midpoint)?; - let normal_b = brep_b.face_normal_at(pair.faces[1], midpoint)?; - let outward = add(normal_a, normal_b); - let length = norm(outward); - if length <= 1e-9 { - continue; - } - let probe = add(midpoint, scale(outward, -local_depth / length)); - if inside(brep_a, probe)? && inside(&brep_b, probe)? { - return Ok(Some(ClashKind::Hard)); - } - } - } - Ok(Some(ClashKind::Touch)) - } - - pub(super) fn placed_relative( - &self, - a: &Leaf, - b: &Leaf, - ) -> Result { - let relative = a.world.inverse().compose(&b.world); - placed( - self.definition_brep(&b.definition)?, - relative.frame, - relative.scale, - ) - } - - pub(super) fn contains_either(&self, a: &Leaf, b: &Leaf) -> Result { - let brep_a = self.definition_brep(&a.definition)?; - let brep_b = self.placed_relative(a, b)?; - Ok(vertex_containment(brep_a, &brep_b)? == Containment::Inside - || vertex_containment(&brep_b, brep_a)? == Containment::Inside) - } - - pub(super) fn definition_brep(&self, id: &str) -> Result<&BrepEnvelope, GeometryError> { - match self - .definitions - .get(id) - .map(|definition| &definition.geometry) - { - Some(Geometry::Analytic(brep)) => Ok(brep), - Some(Geometry::Legacy(_)) => Err(GeometryError::UnsupportedGeometry(format!( - "definition {id} is a legacy BRep; clash and clearance need analytic geometry" - ))), - None => Err(GeometryError::InvalidTopology(format!( - "unknown definition {id}" - ))), - } - } -} - -fn inside(brep: &BrepEnvelope, point: Point3) -> Result { - match classify_point(brep, point)? { - PointClassification::Inside => Ok(true), - PointClassification::Outside | PointClassification::Boundary => Ok(false), - PointClassification::Unknown => Err(GeometryError::UnresolvedIntersection( - "point classification was inconclusive".into(), - )), - } -} - -#[derive(PartialEq)] -enum Containment { - Inside, - NotInside, - Undecided, -} - -fn vertex_containment( - container: &BrepEnvelope, - body: &BrepEnvelope, -) -> Result { - if body.topology.vertices.is_empty() { - return Err(GeometryError::UnsupportedGeometry( - "containment test needs a body with vertices".into(), - )); - } - let mut decided = false; - for vertex in &body.topology.vertices { - match classify_point(container, vertex.position)? { - PointClassification::Inside => return Ok(Containment::Inside), - PointClassification::Outside | PointClassification::Boundary => decided = true, - PointClassification::Unknown => {} - } - } - Ok(if decided { - Containment::NotInside - } else { - Containment::Undecided - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::primitives; - use crate::analytic::topology::Accuracy; - use crate::analytic::Frame3; - use crate::world::{NodeSpec, Similarity3}; - - const DEPTH: f64 = 1e-3; - - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-6, - } - } - - fn at(x: f64, y: f64, z: f64) -> Similarity3 { - Similarity3 { - frame: Frame3 { - origin: [x, y, z], - ..Frame3::IDENTITY - }, - scale: 1.0, - } - } - - fn quarter_turn_about_z(x: f64, y: f64, z: f64) -> Similarity3 { - Similarity3 { - frame: Frame3 { - origin: [x, y, z], - x: [0.0, 1.0, 0.0], - y: [-1.0, 0.0, 0.0], - z: [0.0, 0.0, 1.0], - }, - scale: 1.0, - } - } - - fn graph_with_shapes() -> WorldGraph { - let mut graph = WorldGraph::new(); - graph - .define( - "unit", - primitives::cuboid("unit".into(), Frame3::IDENTITY, [1.0, 1.0, 1.0], accuracy()) - .unwrap(), - ) - .unwrap(); - graph - .define( - "small", - primitives::cuboid( - "small".into(), - Frame3::IDENTITY, - [0.2, 0.2, 0.2], - accuracy(), - ) - .unwrap(), - ) - .unwrap(); - graph - .define( - "post", - primitives::cylinder("post".into(), Frame3::IDENTITY, 0.25, 1.0, accuracy()) - .unwrap(), - ) - .unwrap(); - graph - } - - fn place( - graph: &mut WorldGraph, - id: &str, - parent: Option<&str>, - definition: Option<&str>, - local: Similarity3, - ) { - graph - .add_node(NodeSpec { - id: id.into(), - parent: parent.map(str::to_string), - definition: definition.map(str::to_string), - local, - kind: None, - }) - .unwrap(); - } - - fn kinds(graph: &WorldGraph, a: &[&str], b: &[&str]) -> Vec<(String, String, ClashKind)> { - let owned = |ids: &[&str]| ids.iter().map(|id| id.to_string()).collect::>(); - graph - .clash(&owned(a), &owned(b), DEPTH) - .unwrap() - .into_iter() - .map(|clash| (clash.a, clash.b, clash.kind)) - .collect() - } - - fn pair(a: &str, b: &str, kind: ClashKind) -> Vec<(String, String, ClashKind)> { - vec![(a.into(), b.into(), kind)] - } - - #[test] - fn separated_bodies_do_not_clash() { - let mut graph = graph_with_shapes(); - place(&mut graph, "a", None, Some("unit"), at(0.0, 0.0, 0.0)); - place(&mut graph, "b", None, Some("unit"), at(1.5, 0.0, 0.0)); - assert!(kinds(&graph, &["a"], &["b"]).is_empty()); - } - - #[test] - fn face_to_face_contact_is_a_touch() { - let mut graph = graph_with_shapes(); - place(&mut graph, "a", None, Some("unit"), at(0.0, 0.0, 0.0)); - place(&mut graph, "b", None, Some("unit"), at(1.0, 0.25, 0.0)); - assert_eq!( - kinds(&graph, &["a"], &["b"]), - pair("a", "b", ClashKind::Touch) - ); - } - - #[test] - fn edge_contact_is_a_touch() { - let mut graph = graph_with_shapes(); - place(&mut graph, "a", None, Some("unit"), at(0.0, 0.0, 0.0)); - place(&mut graph, "b", None, Some("unit"), at(1.0, 1.0, 0.0)); - assert_eq!( - kinds(&graph, &["a"], &["b"]), - pair("a", "b", ClashKind::Touch) - ); - } - - #[test] - fn overlapping_bodies_are_a_hard_clash() { - let mut graph = graph_with_shapes(); - place(&mut graph, "a", None, Some("unit"), at(0.0, 0.0, 0.0)); - place(&mut graph, "b", None, Some("unit"), at(0.6, 0.3, 0.2)); - assert_eq!( - kinds(&graph, &["a"], &["b"]), - pair("a", "b", ClashKind::Hard) - ); - } - - #[test] - fn a_body_inside_another_is_a_hard_clash() { - let mut graph = graph_with_shapes(); - place(&mut graph, "a", None, Some("unit"), at(0.0, 0.0, 0.0)); - place(&mut graph, "b", None, Some("small"), at(0.4, 0.4, 0.4)); - assert_eq!( - kinds(&graph, &["a"], &["b"]), - pair("a", "b", ClashKind::Hard) - ); - assert_eq!( - kinds(&graph, &["b"], &["a"]), - pair("b", "a", ClashKind::Hard) - ); - } - - #[test] - fn a_flush_body_inside_another_is_a_hard_clash() { - let mut graph = graph_with_shapes(); - place(&mut graph, "a", None, Some("unit"), at(0.0, 0.0, 0.0)); - place(&mut graph, "b", None, Some("small"), at(0.4, 0.4, 0.8)); - assert_eq!( - kinds(&graph, &["a"], &["b"]), - pair("a", "b", ClashKind::Hard) - ); - } - - #[test] - fn an_overlap_shallower_than_the_depth_is_not_hard() { - let mut graph = graph_with_shapes(); - place(&mut graph, "a", None, Some("unit"), at(0.0, 0.0, 0.0)); - place( - &mut graph, - "b", - None, - Some("unit"), - at(1.0 - DEPTH / 4.0, 0.0, 0.0), - ); - assert_eq!( - kinds(&graph, &["a"], &["b"]), - pair("a", "b", ClashKind::Touch) - ); - } - - #[test] - fn clashes_follow_parent_transforms() { - let mut graph = graph_with_shapes(); - place(&mut graph, "a", None, Some("unit"), at(0.0, 0.0, 0.0)); - place( - &mut graph, - "group", - None, - None, - quarter_turn_about_z(2.5, 0.0, 0.0), - ); - place( - &mut graph, - "b", - Some("group"), - Some("unit"), - at(0.0, 0.0, 0.0), - ); - assert!(kinds(&graph, &["a"], &["group"]).is_empty()); - graph - .set_local("group", quarter_turn_about_z(1.5, 0.2, 0.0)) - .unwrap(); - assert_eq!( - kinds(&graph, &["a"], &["group"]), - pair("a", "group", ClashKind::Hard) - ); - } - - #[test] - fn parts_of_one_item_are_not_tested_against_each_other() { - let mut graph = graph_with_shapes(); - place(&mut graph, "cabinet", None, None, at(0.0, 0.0, 0.0)); - place( - &mut graph, - "cabinet/left", - Some("cabinet"), - Some("unit"), - at(0.0, 0.0, 0.0), - ); - place( - &mut graph, - "cabinet/right", - Some("cabinet"), - Some("unit"), - at(0.5, 0.0, 0.0), - ); - place(&mut graph, "chair", None, Some("small"), at(3.0, 0.0, 0.0)); - assert!(kinds(&graph, &["cabinet", "chair"], &["cabinet", "chair"]).is_empty()); - graph.set_local("chair", at(1.3, 0.4, 0.4)).unwrap(); - assert_eq!( - kinds(&graph, &["cabinet", "chair"], &["cabinet", "chair"]), - pair("cabinet", "chair", ClashKind::Hard) - ); - } - - #[test] - fn nested_items_are_skipped() { - let mut graph = graph_with_shapes(); - place(&mut graph, "room", None, None, at(0.0, 0.0, 0.0)); - place( - &mut graph, - "box", - Some("room"), - Some("unit"), - at(0.0, 0.0, 0.0), - ); - assert!(kinds(&graph, &["room"], &["box"]).is_empty()); - } - - #[test] - fn a_cylinder_through_a_box_is_a_hard_clash() { - let mut graph = graph_with_shapes(); - place(&mut graph, "box", None, Some("unit"), at(0.0, 0.0, 0.0)); - place(&mut graph, "post", None, Some("post"), at(1.1, 0.5, 0.0)); - assert_eq!( - kinds(&graph, &["box"], &["post"]), - pair("box", "post", ClashKind::Hard) - ); - graph.set_local("post", at(1.4, 0.5, 0.0)).unwrap(); - assert!(kinds(&graph, &["box"], &["post"]).is_empty()); - } - - #[test] - fn a_depth_below_geometric_tolerance_is_rejected() { - let mut graph = graph_with_shapes(); - place(&mut graph, "a", None, Some("unit"), at(0.0, 0.0, 0.0)); - place(&mut graph, "b", None, Some("unit"), at(0.5, 0.0, 0.0)); - let clashes = graph.clash(&["a".into()], &["b".into()], 1e-9).unwrap(); - assert_eq!(clashes[0].kind, ClashKind::Unresolved); - assert!(graph.clash(&["a".into()], &["b".into()], 0.0).is_err()); - } -} diff --git a/main/opengeometry/src/world/export.rs b/main/opengeometry/src/world/export.rs deleted file mode 100644 index 1872a96..0000000 --- a/main/opengeometry/src/world/export.rs +++ /dev/null @@ -1,277 +0,0 @@ -use super::graph::{Geometry, WorldError, WorldGraph}; -use super::projection::legacy_in_world; -use crate::analytic::GeometryError; -use crate::brep::Brep; -use crate::export::ifc::{ - export_scene_entities_to_ifc_text, IfcEntityInput, IfcExportConfig, IfcExportReport, -}; -use crate::export::step::{export_breps_to_step_text, StepExportConfig, StepExportReport}; -use crate::export::stl::{export_breps_to_stl_bytes, StlExportConfig, StlExportReport}; - -#[cfg(not(target_arch = "wasm32"))] -use crate::export::ifc::export_scene_entities_to_ifc_file; -#[cfg(not(target_arch = "wasm32"))] -use crate::export::pdf::{export_scene_to_pdf_with_config, PdfExportConfig}; -#[cfg(not(target_arch = "wasm32"))] -use crate::export::projection::{CameraParameters, HlrOptions}; -#[cfg(not(target_arch = "wasm32"))] -use crate::export::step::export_breps_to_step_file; -#[cfg(not(target_arch = "wasm32"))] -use crate::export::stl::export_breps_to_stl_file; - -struct LegacyBody { - node: String, - kind: String, - brep: Brep, -} - -fn export_error(error: impl std::fmt::Display) -> WorldError { - WorldError::Export(error.to_string()) -} - -impl WorldGraph { - fn legacy_bodies(&self, items: &[String]) -> Result, WorldError> { - let mut bodies = Vec::new(); - for leaf in self.leaves_of(items)? { - let definition = self - .definitions - .get(&leaf.definition) - .ok_or_else(|| WorldError::UnknownDefinition(leaf.definition.clone()))?; - let Geometry::Legacy(brep) = &definition.geometry else { - return Err(WorldError::Geometry(GeometryError::UnsupportedGeometry( - format!( - "{} is analytic; export it with the analytic STEP/STL/IFC exporters", - leaf.node - ), - ))); - }; - bodies.push(LegacyBody { - kind: self.kind_of(&leaf.node)?.unwrap_or("brep").to_string(), - brep: legacy_in_world(brep, &leaf.world), - node: leaf.node, - }); - } - Ok(bodies) - } - - pub fn export_stl( - &self, - items: &[String], - config: &StlExportConfig, - ) -> Result<(Vec, StlExportReport), WorldError> { - let bodies = self.legacy_bodies(items)?; - export_breps_to_stl_bytes(bodies.iter().map(|body| &body.brep), config) - .map_err(export_error) - } - - pub fn export_step( - &self, - items: &[String], - config: &StepExportConfig, - ) -> Result<(String, StepExportReport), WorldError> { - let bodies = self.legacy_bodies(items)?; - export_breps_to_step_text(bodies.iter().map(|body| &body.brep), config) - .map_err(export_error) - } - - pub fn export_ifc( - &self, - items: &[String], - config: &IfcExportConfig, - ) -> Result<(String, IfcExportReport), WorldError> { - let bodies = self.legacy_bodies(items)?; - export_scene_entities_to_ifc_text(ifc_entities(&bodies), config).map_err(export_error) - } - - #[cfg(not(target_arch = "wasm32"))] - pub fn export_stl_file( - &self, - items: &[String], - file_path: &str, - config: &StlExportConfig, - ) -> Result { - let bodies = self.legacy_bodies(items)?; - export_breps_to_stl_file(bodies.iter().map(|body| &body.brep), file_path, config) - .map_err(export_error) - } - - #[cfg(not(target_arch = "wasm32"))] - pub fn export_step_file( - &self, - items: &[String], - file_path: &str, - config: &StepExportConfig, - ) -> Result { - let bodies = self.legacy_bodies(items)?; - export_breps_to_step_file(bodies.iter().map(|body| &body.brep), file_path, config) - .map_err(export_error) - } - - #[cfg(not(target_arch = "wasm32"))] - pub fn export_ifc_file( - &self, - items: &[String], - file_path: &str, - config: &IfcExportConfig, - ) -> Result { - let bodies = self.legacy_bodies(items)?; - export_scene_entities_to_ifc_file(ifc_entities(&bodies), file_path, config) - .map_err(export_error) - } - - #[cfg(not(target_arch = "wasm32"))] - pub fn project_to_pdf( - &self, - items: &[String], - camera: &CameraParameters, - hlr: &HlrOptions, - deflection: f64, - file_path: &str, - config: &PdfExportConfig, - ) -> Result<(), WorldError> { - let scene = self.project(items, camera, hlr, deflection)?; - export_scene_to_pdf_with_config(&scene, file_path, config).map_err(export_error) - } -} - -fn ifc_entities(bodies: &[LegacyBody]) -> impl Iterator> { - bodies.iter().map(|body| IfcEntityInput { - entity_id: body.node.as_str(), - kind: body.kind.as_str(), - brep: &body.brep, - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::primitives; - use crate::analytic::topology::Accuracy; - use crate::analytic::Frame3; - use crate::brep::BrepBuilder; - use crate::world::{NodeSpec, Similarity3}; - use openmaths::Vector3; - use uuid::Uuid; - - fn tetrahedron() -> Brep { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - builder.add_vertices(&[ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.5, 0.8660254, 0.0), - Vector3::new(0.5, 0.2886751, 0.8164966), - ]); - builder.add_face(&[0, 2, 1], &[]).unwrap(); - builder.add_face(&[0, 1, 3], &[]).unwrap(); - builder.add_face(&[1, 2, 3], &[]).unwrap(); - builder.add_face(&[2, 0, 3], &[]).unwrap(); - builder.build().unwrap() - } - - fn triangle() -> Brep { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - builder.add_vertices(&[ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.0, 1.0, 0.0), - ]); - builder.add_face(&[0, 1, 2], &[]).unwrap(); - builder.build().unwrap() - } - - fn graph_with(id: &str, brep: Brep, kind: Option<&str>, origin: [f64; 3]) -> WorldGraph { - let mut graph = WorldGraph::new(); - graph.define_legacy(id, brep).unwrap(); - graph - .add_node(NodeSpec { - id: id.into(), - parent: None, - definition: Some(id.into()), - local: Similarity3 { - frame: Frame3 { - origin, - ..Frame3::IDENTITY - }, - scale: 1.0, - }, - kind: kind.map(str::to_string), - }) - .unwrap(); - graph - } - - #[test] - fn stl_export_writes_legacy_triangles_in_world_space() { - let graph = graph_with("tri", triangle(), None, [2.0, 0.0, 0.0]); - let (bytes, report) = graph - .export_stl(&["tri".into()], &StlExportConfig::default()) - .unwrap(); - assert_eq!(report.exported_triangles, 1); - let count = u32::from_le_bytes([bytes[80], bytes[81], bytes[82], bytes[83]]); - assert_eq!(count, 1); - let xs: Vec = (0..3) - .map(|vertex| { - let offset = 84 + 12 + vertex * 12; - f32::from_le_bytes([ - bytes[offset], - bytes[offset + 1], - bytes[offset + 2], - bytes[offset + 3], - ]) - }) - .collect(); - assert!(xs.iter().all(|x| *x >= 2.0 - 1e-6), "{xs:?}"); - } - - #[test] - fn step_export_writes_a_manifold_solid() { - let graph = graph_with("tetra", tetrahedron(), None, [0.0; 3]); - let (text, report) = graph - .export_step(&["tetra".into()], &StepExportConfig::default()) - .unwrap(); - assert!(text.starts_with("ISO-10303-21;")); - assert!(text.contains("MANIFOLD_SOLID_BREP")); - assert_eq!(report.exported_solids, 1); - } - - #[test] - fn ifc_export_writes_one_element_per_legacy_node() { - let graph = graph_with("tetra", tetrahedron(), Some("wall"), [0.0; 3]); - let (text, report) = graph - .export_ifc(&["tetra".into()], &IfcExportConfig::default()) - .unwrap(); - assert!(text.contains("FILE_SCHEMA(('IFC4'));")); - assert!(text.contains("IFCTRIANGULATEDFACESET(")); - assert_eq!(report.exported_elements, 1); - } - - #[test] - fn analytic_items_are_refused_by_the_legacy_exporters() { - let accuracy = Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-6, - }; - let mut graph = WorldGraph::new(); - graph - .define( - "box", - primitives::cuboid("box".into(), Frame3::IDENTITY, [1.0; 3], accuracy).unwrap(), - ) - .unwrap(); - graph - .add_node(NodeSpec { - id: "box".into(), - parent: None, - definition: Some("box".into()), - local: Similarity3::IDENTITY, - kind: None, - }) - .unwrap(); - assert!(matches!( - graph.export_stl(&["box".into()], &StlExportConfig::default()), - Err(WorldError::Geometry(GeometryError::UnsupportedGeometry(_))) - )); - } -} diff --git a/main/opengeometry/src/world/graph.rs b/main/opengeometry/src/world/graph.rs deleted file mode 100644 index bf907ca..0000000 --- a/main/opengeometry/src/world/graph.rs +++ /dev/null @@ -1,524 +0,0 @@ -use super::transform::Similarity3; -use crate::analytic::{BrepEnvelope, GeometryError, Point3}; -use crate::brep::Brep; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Clone, Debug, PartialEq, Serialize)] -pub enum WorldError { - DuplicateNode(String), - UnknownNode(String), - DuplicateDefinition(String), - UnknownDefinition(String), - DefinitionInUse(String), - Cycle(String), - InvalidInput(String), - Export(String), - Geometry(GeometryError), -} - -impl From for WorldError { - fn from(error: GeometryError) -> Self { - Self::Geometry(error) - } -} - -#[derive(Clone, Debug, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct NodeSpec { - pub id: String, - #[serde(default)] - pub parent: Option, - #[serde(default)] - pub definition: Option, - #[serde(default)] - pub local: Similarity3, - #[serde(default)] - pub kind: Option, -} - -pub type Bounds3 = [f64; 6]; - -pub(super) enum Geometry { - Analytic(BrepEnvelope), - Legacy(Brep), -} - -pub(super) struct Definition { - pub(super) geometry: Geometry, - pub(super) local_bounds: Option, - pub(super) users: usize, -} - -pub(super) struct Node { - pub(super) parent: Option, - pub(super) children: Vec, - pub(super) definition: Option, - pub(super) local: Similarity3, - pub(super) kind: Option, -} - -#[derive(Default)] -pub struct WorldGraph { - pub(super) definitions: HashMap, - pub(super) nodes: HashMap, -} - -impl WorldGraph { - pub fn new() -> Self { - Self::default() - } - - pub fn node_count(&self) -> usize { - self.nodes.len() - } - - pub fn definition_count(&self) -> usize { - self.definitions.len() - } - - pub fn has_node(&self, id: &str) -> bool { - self.nodes.contains_key(id) - } - - pub fn has_definition(&self, id: &str) -> bool { - self.definitions.contains_key(id) - } - - pub fn define(&mut self, id: &str, brep: BrepEnvelope) -> Result<(), WorldError> { - if self.definitions.contains_key(id) { - return Err(WorldError::DuplicateDefinition(id.into())); - } - let local_bounds = brep.bounds()?.map(|bounds| { - [ - bounds.axes[0].lo, - bounds.axes[1].lo, - bounds.axes[2].lo, - bounds.axes[0].hi, - bounds.axes[1].hi, - bounds.axes[2].hi, - ] - }); - self.insert_definition(id, Geometry::Analytic(brep), local_bounds) - } - - pub fn define_legacy(&mut self, id: &str, brep: Brep) -> Result<(), WorldError> { - if self.definitions.contains_key(id) { - return Err(WorldError::DuplicateDefinition(id.into())); - } - brep.validate_topology() - .map_err(|error| WorldError::InvalidInput(format!("legacy BRep {id}: {error}")))?; - let local_bounds = brep.vertices.iter().fold(None, |bounds, vertex| { - Some(include( - bounds, - [vertex.position.x, vertex.position.y, vertex.position.z], - )) - }); - self.insert_definition(id, Geometry::Legacy(brep), local_bounds) - } - - fn insert_definition( - &mut self, - id: &str, - geometry: Geometry, - local_bounds: Option, - ) -> Result<(), WorldError> { - self.definitions.insert( - id.into(), - Definition { - geometry, - local_bounds, - users: 0, - }, - ); - Ok(()) - } - - pub fn undefine(&mut self, id: &str) -> Result<(), WorldError> { - match self.definitions.get(id) { - None => Err(WorldError::UnknownDefinition(id.into())), - Some(definition) if definition.users > 0 => Err(WorldError::DefinitionInUse(id.into())), - Some(_) => { - self.definitions.remove(id); - Ok(()) - } - } - } - - pub fn add_node(&mut self, spec: NodeSpec) -> Result<(), WorldError> { - if spec.id.is_empty() { - return Err(WorldError::InvalidInput("node id must not be empty".into())); - } - if self.nodes.contains_key(&spec.id) { - return Err(WorldError::DuplicateNode(spec.id)); - } - spec.local.validate()?; - if let Some(parent) = &spec.parent { - if !self.nodes.contains_key(parent) { - return Err(WorldError::UnknownNode(parent.clone())); - } - } - if let Some(definition) = &spec.definition { - self.definitions - .get_mut(definition) - .ok_or_else(|| WorldError::UnknownDefinition(definition.clone()))? - .users += 1; - } - if let Some(parent) = &spec.parent { - self.node_mut(parent)?.children.push(spec.id.clone()); - } - self.nodes.insert( - spec.id, - Node { - parent: spec.parent, - children: Vec::new(), - definition: spec.definition, - local: spec.local, - kind: spec.kind, - }, - ); - Ok(()) - } - - pub fn set_local(&mut self, id: &str, local: Similarity3) -> Result<(), WorldError> { - local.validate()?; - self.node_mut(id)?.local = local; - Ok(()) - } - - pub fn reparent(&mut self, id: &str, parent: Option<&str>) -> Result<(), WorldError> { - let previous = self.node(id)?.parent.clone(); - if let Some(parent) = parent { - let mut cursor = Some(parent.to_string()); - while let Some(current) = cursor { - if current == id { - return Err(WorldError::Cycle(id.into())); - } - cursor = self.node(¤t)?.parent.clone(); - } - } - if let Some(previous) = previous { - self.node_mut(&previous)? - .children - .retain(|child| child != id); - } - if let Some(parent) = parent { - self.node_mut(parent)?.children.push(id.into()); - } - self.node_mut(id)?.parent = parent.map(str::to_string); - Ok(()) - } - - pub fn remove_node(&mut self, id: &str) -> Result<(), WorldError> { - if let Some(parent) = self.node(id)?.parent.clone() { - self.node_mut(&parent)?.children.retain(|child| child != id); - } - let mut pending = vec![id.to_string()]; - while let Some(current) = pending.pop() { - let node = self - .nodes - .remove(¤t) - .ok_or_else(|| WorldError::UnknownNode(current.clone()))?; - if let Some(definition) = node.definition { - if let Some(definition) = self.definitions.get_mut(&definition) { - definition.users -= 1; - } - } - pending.extend(node.children); - } - Ok(()) - } - - pub fn world(&self, id: &str) -> Result { - let mut chain = vec![self.node(id)?.local]; - let mut cursor = self.node(id)?.parent.clone(); - while let Some(current) = cursor { - let node = self.node(¤t)?; - chain.push(node.local); - cursor = node.parent.clone(); - } - Ok(chain - .iter() - .rev() - .fold(Similarity3::IDENTITY, |world, local| world.compose(local))) - } - - pub fn world_bounds(&self, id: &str) -> Result, WorldError> { - let mut bounds: Option = None; - let mut pending = vec![(id.to_string(), self.world(id)?)]; - while let Some((current, world)) = pending.pop() { - let node = self.node(¤t)?; - if let Some(definition) = &node.definition { - let local_bounds = self - .definitions - .get(definition) - .ok_or_else(|| WorldError::UnknownDefinition(definition.clone()))? - .local_bounds; - if let Some(local_bounds) = local_bounds { - for corner in corners(local_bounds) { - bounds = Some(include(bounds, world.apply_point(corner))); - } - } - } - for child in &node.children { - pending.push((child.clone(), world.compose(&self.node(child)?.local))); - } - } - Ok(bounds) - } - - pub(super) fn kind_of(&self, id: &str) -> Result, WorldError> { - let mut cursor = Some(id); - while let Some(current) = cursor { - let node = self.node(current)?; - if let Some(kind) = &node.kind { - return Ok(Some(kind)); - } - cursor = node.parent.as_deref(); - } - Ok(None) - } - - pub(super) fn node(&self, id: &str) -> Result<&Node, WorldError> { - self.nodes - .get(id) - .ok_or_else(|| WorldError::UnknownNode(id.into())) - } - - fn node_mut(&mut self, id: &str) -> Result<&mut Node, WorldError> { - self.nodes - .get_mut(id) - .ok_or_else(|| WorldError::UnknownNode(id.into())) - } -} - -pub(super) fn corners(bounds: Bounds3) -> [Point3; 8] { - std::array::from_fn(|index| { - [ - bounds[if index & 1 == 0 { 0 } else { 3 }], - bounds[if index & 2 == 0 { 1 } else { 4 }], - bounds[if index & 4 == 0 { 2 } else { 5 }], - ] - }) -} - -pub(super) fn include(bounds: Option, point: Point3) -> Bounds3 { - match bounds { - None => [point[0], point[1], point[2], point[0], point[1], point[2]], - Some(b) => [ - b[0].min(point[0]), - b[1].min(point[1]), - b[2].min(point[2]), - b[3].max(point[0]), - b[4].max(point[1]), - b[5].max(point[2]), - ], - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::primitives; - use crate::analytic::topology::Accuracy; - use crate::analytic::Frame3; - - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-6, - } - } - - fn unit_box() -> BrepEnvelope { - primitives::cuboid("box".into(), Frame3::IDENTITY, [1.0, 2.0, 3.0], accuracy()).unwrap() - } - - fn translation(x: f64, y: f64, z: f64) -> Similarity3 { - Similarity3 { - frame: Frame3 { - origin: [x, y, z], - ..Frame3::IDENTITY - }, - scale: 1.0, - } - } - - fn quarter_turn_about_z(x: f64, y: f64, z: f64) -> Similarity3 { - Similarity3 { - frame: Frame3 { - origin: [x, y, z], - x: [0.0, 1.0, 0.0], - y: [-1.0, 0.0, 0.0], - z: [0.0, 0.0, 1.0], - }, - scale: 1.0, - } - } - - fn node( - id: &str, - parent: Option<&str>, - definition: Option<&str>, - local: Similarity3, - ) -> NodeSpec { - NodeSpec { - id: id.into(), - parent: parent.map(str::to_string), - definition: definition.map(str::to_string), - local, - kind: None, - } - } - - fn assert_bounds_close(actual: Bounds3, expected: Bounds3) { - for index in 0..6 { - assert!( - (actual[index] - expected[index]).abs() <= 2.0 * accuracy().geometric, - "{actual:?} != {expected:?}" - ); - } - } - - fn room() -> WorldGraph { - let mut graph = WorldGraph::new(); - graph.define("box", unit_box()).unwrap(); - graph - .add_node(node("room", None, None, translation(10.0, 0.0, 0.0))) - .unwrap(); - graph - .add_node(node( - "cluster", - Some("room"), - None, - quarter_turn_about_z(0.0, 5.0, 0.0), - )) - .unwrap(); - graph - .add_node(node( - "item", - Some("cluster"), - Some("box"), - translation(1.0, 0.0, 0.0), - )) - .unwrap(); - graph - } - - #[test] - fn world_transform_composes_every_ancestor() { - let graph = room(); - let world = graph.world("item").unwrap(); - let origin = world.apply_point([0.0, 0.0, 0.0]); - assert_bounds_close( - [origin[0], origin[1], origin[2], 0.0, 0.0, 0.0], - [10.0, 6.0, 0.0, 0.0, 0.0, 0.0], - ); - } - - #[test] - fn moving_a_parent_moves_its_descendants() { - let mut graph = room(); - graph - .set_local("room", translation(-3.0, 0.0, 2.0)) - .unwrap(); - assert_bounds_close( - graph.world_bounds("item").unwrap().unwrap(), - [-5.0, 6.0, 2.0, -3.0, 7.0, 5.0], - ); - } - - #[test] - fn world_bounds_cover_the_whole_subtree() { - let mut graph = room(); - graph - .add_node(node( - "second", - Some("room"), - Some("box"), - translation(0.0, 0.0, 0.0), - )) - .unwrap(); - assert_bounds_close( - graph.world_bounds("room").unwrap().unwrap(), - [8.0, 0.0, 0.0, 11.0, 7.0, 3.0], - ); - assert_eq!( - graph.world_bounds("cluster").unwrap(), - graph.world_bounds("item").unwrap() - ); - } - - #[test] - fn reparent_rejects_cycles_and_keeps_the_tree_intact() { - let mut graph = room(); - assert_eq!( - graph.reparent("room", Some("item")), - Err(WorldError::Cycle("room".into())) - ); - assert_eq!( - graph.reparent("room", Some("room")), - Err(WorldError::Cycle("room".into())) - ); - graph.reparent("item", Some("room")).unwrap(); - assert_bounds_close( - graph.world_bounds("item").unwrap().unwrap(), - [11.0, 0.0, 0.0, 12.0, 2.0, 3.0], - ); - assert_eq!(graph.world_bounds("cluster").unwrap(), None); - } - - #[test] - fn shared_definitions_are_stored_once_and_protected_while_used() { - let mut graph = room(); - graph - .add_node(node( - "copy", - Some("room"), - Some("box"), - translation(4.0, 0.0, 0.0), - )) - .unwrap(); - assert_eq!(graph.definition_count(), 1); - assert_eq!( - graph.undefine("box"), - Err(WorldError::DefinitionInUse("box".into())) - ); - graph.remove_node("room").unwrap(); - assert_eq!(graph.node_count(), 0); - graph.undefine("box").unwrap(); - assert_eq!(graph.definition_count(), 0); - } - - #[test] - fn invalid_references_are_rejected() { - let mut graph = room(); - assert_eq!( - graph.add_node(node("item", None, None, Similarity3::IDENTITY)), - Err(WorldError::DuplicateNode("item".into())) - ); - assert_eq!( - graph.add_node(node("orphan", Some("missing"), None, Similarity3::IDENTITY)), - Err(WorldError::UnknownNode("missing".into())) - ); - assert_eq!( - graph.add_node(node("ghost", None, Some("missing"), Similarity3::IDENTITY)), - Err(WorldError::UnknownDefinition("missing".into())) - ); - assert_eq!( - graph.define("box", unit_box()), - Err(WorldError::DuplicateDefinition("box".into())) - ); - assert!(graph - .set_local( - "item", - Similarity3 { - scale: -1.0, - ..Similarity3::IDENTITY - } - ) - .is_err()); - } -} diff --git a/main/opengeometry/src/world/mod.rs b/main/opengeometry/src/world/mod.rs deleted file mode 100644 index 41c8e15..0000000 --- a/main/opengeometry/src/world/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -mod bvh; -mod clash; -mod export; -mod graph; -mod pairs; -mod projection; -mod proximity; -mod transform; -mod triangle; -mod wasm; - -pub use clash::{Clash, ClashKind}; -pub use graph::{NodeSpec, WorldError, WorldGraph}; -pub use projection::ProjectionView; -pub use proximity::Proximity; -pub use transform::Similarity3; -pub use wasm::OGWorldGraph; diff --git a/main/opengeometry/src/world/pairs.rs b/main/opengeometry/src/world/pairs.rs deleted file mode 100644 index 40b4be5..0000000 --- a/main/opengeometry/src/world/pairs.rs +++ /dev/null @@ -1,159 +0,0 @@ -use super::bvh::Bvh; -use super::graph::{corners, include, Bounds3, WorldError, WorldGraph}; -use super::transform::Similarity3; -use std::collections::{HashMap, HashSet}; - -pub(super) struct Leaf { - pub(super) item: usize, - pub(super) node: String, - pub(super) definition: String, - pub(super) world: Similarity3, - pub(super) bounds: Bounds3, -} - -pub(super) struct ItemPairs { - pub(super) items: Vec, - pub(super) leaves: Vec, - pub(super) pairs: Vec<(usize, usize)>, -} - -impl ItemPairs { - pub(super) fn item_key(&self, leaf_a: usize, leaf_b: usize) -> (usize, usize) { - let (a, b) = (self.leaves[leaf_a].item, self.leaves[leaf_b].item); - (a.min(b), a.max(b)) - } -} - -impl WorldGraph { - pub(super) fn candidate_pairs( - &self, - items_a: &[String], - items_b: &[String], - reach: f64, - ) -> Result { - let mut seen = HashSet::new(); - let items: Vec = items_a - .iter() - .chain(items_b) - .filter(|id| seen.insert(id.as_str())) - .cloned() - .collect(); - let item_index: HashMap<&str, usize> = items - .iter() - .enumerate() - .map(|(index, id)| (id.as_str(), index)) - .collect(); - let mut leaves = Vec::new(); - for (index, item) in items.iter().enumerate() { - self.collect_leaves(index, item, &mut leaves)?; - } - let side = |ids: &[String]| -> HashSet { - ids.iter().map(|id| item_index[id.as_str()]).collect() - }; - let (side_a, side_b) = (side(items_a), side(items_b)); - let bvh = Bvh::build(leaves.iter().map(|leaf| leaf.bounds).collect()); - - let mut pairs = Vec::new(); - let mut hits = Vec::new(); - for (index_a, leaf_a) in leaves.iter().enumerate() { - if !side_a.contains(&leaf_a.item) { - continue; - } - hits.clear(); - bvh.query(&inflate(leaf_a.bounds, reach), &mut hits); - for &index_b in &hits { - let leaf_b = &leaves[index_b]; - let mirrored = leaf_a.item > leaf_b.item - && side_a.contains(&leaf_b.item) - && side_b.contains(&leaf_a.item); - if leaf_a.item == leaf_b.item - || !side_b.contains(&leaf_b.item) - || mirrored - || self.nested(&items[leaf_a.item], &items[leaf_b.item])? - { - continue; - } - pairs.push((index_a, index_b)); - } - } - Ok(ItemPairs { - items, - leaves, - pairs, - }) - } - - pub(super) fn leaves_of(&self, items: &[String]) -> Result, WorldError> { - let mut leaves = Vec::new(); - for (index, item) in items.iter().enumerate() { - self.collect_leaves(index, item, &mut leaves)?; - } - let mut seen = HashSet::new(); - leaves.retain(|leaf| seen.insert(leaf.node.clone())); - Ok(leaves) - } - - fn collect_leaves( - &self, - item: usize, - root: &str, - leaves: &mut Vec, - ) -> Result<(), WorldError> { - let mut pending = vec![(root.to_string(), self.world(root)?)]; - while let Some((id, world)) = pending.pop() { - let node = self.node(&id)?; - if let Some(definition) = &node.definition { - let local_bounds = self - .definitions - .get(definition) - .ok_or_else(|| WorldError::UnknownDefinition(definition.clone()))? - .local_bounds; - if let Some(local_bounds) = local_bounds { - let bounds = corners(local_bounds) - .into_iter() - .fold(None, |bounds, corner| { - Some(include(bounds, world.apply_point(corner))) - }) - .unwrap_or(local_bounds); - leaves.push(Leaf { - item, - node: id.clone(), - definition: definition.clone(), - world, - bounds, - }); - } - } - for child in &node.children { - pending.push((child.clone(), world.compose(&self.node(child)?.local))); - } - } - Ok(()) - } - - fn nested(&self, a: &str, b: &str) -> Result { - Ok(self.is_ancestor(a, b)? || self.is_ancestor(b, a)?) - } - - fn is_ancestor(&self, ancestor: &str, id: &str) -> Result { - let mut cursor = self.node(id)?.parent.clone(); - while let Some(current) = cursor { - if current == ancestor { - return Ok(true); - } - cursor = self.node(¤t)?.parent.clone(); - } - Ok(false) - } -} - -pub(super) fn inflate(bounds: Bounds3, reach: f64) -> Bounds3 { - [ - bounds[0] - reach, - bounds[1] - reach, - bounds[2] - reach, - bounds[3] + reach, - bounds[4] + reach, - bounds[5] + reach, - ] -} diff --git a/main/opengeometry/src/world/projection.rs b/main/opengeometry/src/world/projection.rs deleted file mode 100644 index 7c49af8..0000000 --- a/main/opengeometry/src/world/projection.rs +++ /dev/null @@ -1,347 +0,0 @@ -use super::graph::{Geometry, WorldError, WorldGraph}; -use super::transform::Similarity3; -use crate::analytic::placement::placed; -use crate::brep::Brep; -use crate::export::projection::{ - project_analytic_brep_to_scene, project_brep_to_scene, CameraParameters, HlrOptions, Scene2D, -}; -use openmaths::Vector3; -use serde::Deserialize; -use std::collections::HashMap; - -const MAX_PROJECTION_TRIANGLES: usize = 2_000_000; - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -pub struct ProjectionView { - pub id: String, - pub camera: CameraParameters, - #[serde(default)] - pub hlr: HlrOptions, -} - -impl WorldGraph { - pub fn project( - &self, - items: &[String], - camera: &CameraParameters, - hlr: &HlrOptions, - deflection: f64, - ) -> Result { - if !deflection.is_finite() || deflection <= 0.0 { - return Err(WorldError::InvalidInput( - "projection deflection must be finite and positive".into(), - )); - } - let mut scene = Scene2D::new(); - for leaf in self.leaves_of(items)? { - let definition = self - .definitions - .get(&leaf.definition) - .ok_or_else(|| WorldError::UnknownDefinition(leaf.definition.clone()))?; - let mut projected = match &definition.geometry { - Geometry::Analytic(brep) => project_analytic_brep_to_scene( - &placed(brep, leaf.world.frame, leaf.world.scale)?, - camera, - hlr, - deflection, - MAX_PROJECTION_TRIANGLES, - )?, - Geometry::Legacy(brep) => { - project_brep_to_scene(&legacy_in_world(brep, &leaf.world), camera, hlr) - } - }; - let layer = self - .kind_of(&leaf.node)? - .and_then(aia_layer) - .map(str::to_string); - for segment in &mut projected.segments { - segment.layer = layer.clone(); - segment.source_entity_id = Some(leaf.node.clone()); - } - scene.extend(projected); - } - Ok(scene) - } - - pub fn project_views( - &self, - items: &[String], - views: &[ProjectionView], - deflection: f64, - ) -> Result, WorldError> { - let mut projected = HashMap::with_capacity(views.len()); - for view in views { - let mut scene = self.project(items, &view.camera, &view.hlr, deflection)?; - scene.name = Some(view.id.clone()); - projected.insert(view.id.clone(), scene); - } - Ok(projected) - } -} - -pub(super) fn legacy_in_world(brep: &Brep, world: &Similarity3) -> Brep { - let mut placed = brep.clone(); - if *world != Similarity3::IDENTITY { - placed.apply_point_transform( - |point: Vector3| { - let [x, y, z] = world.apply_point([point.x, point.y, point.z]); - Vector3::new(x, y, z) - }, - world.scale, - ); - } - placed -} - -fn aia_layer(kind: &str) -> Option<&'static str> { - match kind.to_lowercase().trim() { - "wall" => Some("A-WALL"), - "door" => Some("A-DOOR"), - "window" | "glazing" | "glaz" => Some("A-GLAZ"), - "slab" | "floor" | "ceiling" => Some("A-FLOR"), - "stair" | "stairs" => Some("A-FLOR-STRS"), - "column" | "col" => Some("A-COLS"), - "beam" => Some("S-BEAM"), - "roof" => Some("A-ROOF"), - _ => None, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::primitives; - use crate::analytic::topology::Accuracy; - use crate::analytic::Frame3; - use crate::brep::BrepBuilder; - use crate::world::NodeSpec; - use uuid::Uuid; - - fn wire() -> Brep { - let mut builder = BrepBuilder::new(Uuid::new_v4()); - builder.add_vertices(&[Vector3::new(-1.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)]); - builder.add_wire(&[0, 1], false).unwrap(); - builder.build().unwrap() - } - - fn at(x: f64, y: f64, z: f64) -> Similarity3 { - Similarity3 { - frame: Frame3 { - origin: [x, y, z], - ..Frame3::IDENTITY - }, - scale: 1.0, - } - } - - fn node( - id: &str, - parent: Option<&str>, - definition: Option<&str>, - kind: Option<&str>, - local: Similarity3, - ) -> NodeSpec { - NodeSpec { - id: id.into(), - parent: parent.map(str::to_string), - definition: definition.map(str::to_string), - local, - kind: kind.map(str::to_string), - } - } - - fn ids(ids: &[&str]) -> Vec { - ids.iter().map(|id| id.to_string()).collect() - } - - fn line_keys(scene: &Scene2D) -> Vec { - let mut keys: Vec = scene - .to_lines() - .lines - .iter() - .map(|line| { - format!( - "{:.9},{:.9},{:.9},{:.9},{:?}", - line.start.x, line.start.y, line.end.x, line.end.y, line.class - ) - }) - .collect(); - keys.sort(); - keys - } - - #[test] - fn legacy_lines_carry_the_node_id_and_the_inherited_aia_layer() { - let mut graph = WorldGraph::new(); - graph.define_legacy("wire", wire()).unwrap(); - graph - .add_node(node( - "walls", - None, - None, - Some("wall"), - Similarity3::IDENTITY, - )) - .unwrap(); - graph - .add_node(node( - "wall-1", - Some("walls"), - Some("wire"), - None, - Similarity3::IDENTITY, - )) - .unwrap(); - let scene = graph - .project( - &ids(&["walls"]), - &CameraParameters::default(), - &HlrOptions::default(), - 0.01, - ) - .unwrap(); - assert!(!scene.segments.is_empty()); - for segment in &scene.segments { - assert_eq!(segment.layer.as_deref(), Some("A-WALL")); - assert_eq!(segment.source_entity_id.as_deref(), Some("wall-1")); - } - } - - #[test] - fn legacy_geometry_follows_its_parent_transform() { - let mut graph = WorldGraph::new(); - graph.define_legacy("wire", wire()).unwrap(); - graph - .add_node(node("group", None, None, None, at(0.5, 2.0, -3.0))) - .unwrap(); - graph - .add_node(node( - "piece", - Some("group"), - Some("wire"), - None, - Similarity3::IDENTITY, - )) - .unwrap(); - let camera = CameraParameters::default(); - let hlr = HlrOptions::default(); - let projected = graph - .project(&ids(&["group"]), &camera, &hlr, 0.01) - .unwrap(); - let mut moved = wire(); - moved.apply_point_transform(|p| Vector3::new(p.x + 0.5, p.y + 2.0, p.z - 3.0), 1.0); - assert_eq!( - line_keys(&projected), - line_keys(&project_brep_to_scene(&moved, &camera, &hlr)) - ); - } - - #[test] - fn analytic_projection_matches_projecting_the_placed_body_directly() { - let accuracy = Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-6, - }; - let body = - primitives::cylinder("post".into(), Frame3::IDENTITY, 0.3, 1.2, accuracy).unwrap(); - let mut graph = WorldGraph::new(); - graph.define("post", body.clone()).unwrap(); - graph - .add_node(node("post", None, Some("post"), None, at(1.0, 0.0, 2.0))) - .unwrap(); - let camera = CameraParameters::default(); - let hlr = HlrOptions::default(); - let expected = project_analytic_brep_to_scene( - &placed(&body, at(1.0, 0.0, 2.0).frame, 1.0).unwrap(), - &camera, - &hlr, - 0.01, - MAX_PROJECTION_TRIANGLES, - ) - .unwrap(); - let projected = graph.project(&ids(&["post"]), &camera, &hlr, 0.01).unwrap(); - assert!(!expected.segments.is_empty()); - assert_eq!(line_keys(&projected), line_keys(&expected)); - } - - #[test] - fn every_requested_view_is_returned_and_removed_nodes_disappear() { - let mut graph = WorldGraph::new(); - graph.define_legacy("wire", wire()).unwrap(); - graph - .add_node(node("a", None, Some("wire"), None, Similarity3::IDENTITY)) - .unwrap(); - let views: Vec = serde_json::from_str(&format!( - r#"[{{"id":"plan","camera":{camera}}},{{"id":"elevation","camera":{camera}}}]"#, - camera = serde_json::to_string(&CameraParameters::default()).unwrap() - )) - .unwrap(); - let projected = graph.project_views(&ids(&["a"]), &views, 0.01).unwrap(); - assert_eq!(projected.len(), 2); - assert_eq!(projected["plan"].name.as_deref(), Some("plan")); - assert!(!projected["elevation"].segments.is_empty()); - graph.remove_node("a").unwrap(); - assert!(graph - .project( - &ids(&["a"]), - &CameraParameters::default(), - &HlrOptions::default(), - 0.01 - ) - .is_err()); - } - - #[test] - fn nested_items_are_projected_once() { - let mut graph = WorldGraph::new(); - graph.define_legacy("wire", wire()).unwrap(); - graph - .add_node(node("group", None, None, None, Similarity3::IDENTITY)) - .unwrap(); - graph - .add_node(node( - "piece", - Some("group"), - Some("wire"), - None, - Similarity3::IDENTITY, - )) - .unwrap(); - let camera = CameraParameters::default(); - let hlr = HlrOptions::default(); - let once = graph - .project(&ids(&["group"]), &camera, &hlr, 0.01) - .unwrap(); - let twice = graph - .project(&ids(&["group", "piece"]), &camera, &hlr, 0.01) - .unwrap(); - assert_eq!(once.segments.len(), twice.segments.len()); - } - - #[test] - fn aia_layer_maps_known_kinds() { - assert_eq!(aia_layer("wall"), Some("A-WALL")); - assert_eq!(aia_layer("door"), Some("A-DOOR")); - assert_eq!(aia_layer("window"), Some("A-GLAZ")); - assert_eq!(aia_layer("slab"), Some("A-FLOR")); - assert_eq!(aia_layer("stair"), Some("A-FLOR-STRS")); - assert_eq!(aia_layer("column"), Some("A-COLS")); - assert_eq!(aia_layer("unknown"), None); - } - - #[test] - fn invalid_deflection_is_rejected() { - let graph = WorldGraph::new(); - assert!(graph - .project( - &[], - &CameraParameters::default(), - &HlrOptions::default(), - 0.0 - ) - .is_err()); - } -} diff --git a/main/opengeometry/src/world/proximity.rs b/main/opengeometry/src/world/proximity.rs deleted file mode 100644 index 2428ecf..0000000 --- a/main/opengeometry/src/world/proximity.rs +++ /dev/null @@ -1,343 +0,0 @@ -use super::bvh::{overlaps, Bvh}; -use super::graph::{WorldError, WorldGraph}; -use super::pairs::{inflate, ItemPairs, Leaf}; -use super::triangle::{triangle_distance, Triangle}; -use crate::analytic::geometry::{norm, sub}; -use crate::analytic::tessellation::tessellate; -use crate::analytic::Point3; -use serde::Serialize; -use std::collections::HashMap; - -const MAX_TRIANGLES_PER_PART: usize = 2_000_000; - -#[derive(Clone, Debug, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Proximity { - pub a: String, - pub b: String, - pub distance: f64, - pub error_bound: f64, - pub parts: [String; 2], - pub points: [Point3; 2], -} - -struct Mesh { - triangles: Vec, - bvh: Bvh, - error: f64, -} - -impl WorldGraph { - pub fn clearance( - &self, - items_a: &[String], - items_b: &[String], - clearance: f64, - ) -> Result, WorldError> { - if !clearance.is_finite() || clearance <= 0.0 { - return Err(WorldError::InvalidInput( - "clearance must be finite and positive".into(), - )); - } - let candidates = self.candidate_pairs(items_a, items_b, clearance)?; - Ok(self - .nearest_per_item_pair(&candidates, clearance, clearance / 20.0)? - .into_iter() - .filter(|proximity| proximity.distance < clearance) - .collect()) - } - - pub fn distance( - &self, - item_a: &str, - item_b: &str, - tolerance: f64, - ) -> Result, WorldError> { - if !tolerance.is_finite() || tolerance <= 0.0 { - return Err(WorldError::InvalidInput( - "distance tolerance must be finite and positive".into(), - )); - } - let candidates = self.candidate_pairs(&[item_a.into()], &[item_b.into()], f64::INFINITY)?; - Ok(self - .nearest_per_item_pair(&candidates, f64::INFINITY, tolerance)? - .into_iter() - .next()) - } - - fn nearest_per_item_pair( - &self, - candidates: &ItemPairs, - reach: f64, - tolerance: f64, - ) -> Result, WorldError> { - let mut meshes: HashMap = HashMap::new(); - let mut nearest: HashMap<(usize, usize), Proximity> = HashMap::new(); - for &(index_a, index_b) in &candidates.pairs { - let key = candidates.item_key(index_a, index_b); - let limit = nearest - .get(&key) - .map_or(reach, |proximity| proximity.distance.min(reach)); - if limit == 0.0 { - continue; - } - let (leaf_a, leaf_b) = (&candidates.leaves[index_a], &candidates.leaves[index_b]); - for index in [index_a, index_b] { - if !meshes.contains_key(&index) { - meshes.insert( - index, - self.world_mesh(&candidates.leaves[index], tolerance)?, - ); - } - } - let (mesh_a, mesh_b) = (&meshes[&index_a], &meshes[&index_b]); - let surface = mesh_distance(mesh_a, mesh_b, limit); - let contained = surface.as_ref().is_none_or(|(distance, _)| *distance > 0.0) - && overlaps(&leaf_a.bounds, &leaf_b.bounds) - && self.contains_either(leaf_a, leaf_b)?; - let (distance, points) = match (surface, contained) { - (_, true) => { - let inside = overlap_center(&leaf_a.bounds, &leaf_b.bounds); - (0.0, [inside, inside]) - } - (Some(found), false) => found, - (None, false) => continue, - }; - nearest.insert( - key, - Proximity { - a: candidates.items[leaf_a.item].clone(), - b: candidates.items[leaf_b.item].clone(), - distance, - error_bound: mesh_a.error + mesh_b.error, - parts: [leaf_a.node.clone(), leaf_b.node.clone()], - points, - }, - ); - } - let mut proximities: Vec = nearest.into_values().collect(); - proximities.sort_by(|x, y| (&x.a, &x.b).cmp(&(&y.a, &y.b))); - Ok(proximities) - } - - fn world_mesh(&self, leaf: &Leaf, tolerance: f64) -> Result { - let brep = self.definition_brep(&leaf.definition)?; - let deflection = tolerance / 2.0 / leaf.world.scale; - if deflection <= 2.0 * brep.accuracy.geometric { - return Err(WorldError::InvalidInput(format!( - "tolerance {tolerance} is below the geometric tolerance of {}", - leaf.node - ))); - } - let mesh = tessellate(brep, deflection, MAX_TRIANGLES_PER_PART)?; - let point = |index: u32| -> Point3 { - let offset = index as usize * 3; - leaf.world.apply_point([ - mesh.positions[offset], - mesh.positions[offset + 1], - mesh.positions[offset + 2], - ]) - }; - let triangles: Vec = mesh - .indices - .chunks_exact(3) - .map(|corner| [point(corner[0]), point(corner[1]), point(corner[2])]) - .collect(); - let bvh = Bvh::build(triangles.iter().map(triangle_bounds).collect()); - Ok(Mesh { - triangles, - bvh, - error: mesh.achieved_deflection * leaf.world.scale, - }) - } -} - -fn mesh_distance(a: &Mesh, b: &Mesh, limit: f64) -> Option<(f64, [Point3; 2])> { - let (first_a, first_b) = (a.triangles.first()?, b.triangles.first()?); - let mut bound = if limit.is_finite() { - limit - } else { - norm(sub(first_a[0], first_b[0])) - }; - let mut best = None; - let mut hits = Vec::new(); - for triangle in &a.triangles { - hits.clear(); - b.bvh - .query(&inflate(triangle_bounds(triangle), bound), &mut hits); - for &index in &hits { - let (distance, on_a, on_b) = triangle_distance(*triangle, b.triangles[index]); - if distance <= bound { - bound = distance; - best = Some((distance, [on_a, on_b])); - if distance == 0.0 { - return best; - } - } - } - } - best -} - -fn overlap_center(a: &[f64; 6], b: &[f64; 6]) -> Point3 { - std::array::from_fn(|axis| a[axis].max(b[axis]) / 2.0 + a[axis + 3].min(b[axis + 3]) / 2.0) -} - -fn triangle_bounds(triangle: &Triangle) -> [f64; 6] { - let mut bounds = [ - f64::INFINITY, - f64::INFINITY, - f64::INFINITY, - f64::NEG_INFINITY, - f64::NEG_INFINITY, - f64::NEG_INFINITY, - ]; - for point in triangle { - for axis in 0..3 { - bounds[axis] = bounds[axis].min(point[axis]); - bounds[axis + 3] = bounds[axis + 3].max(point[axis]); - } - } - bounds -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::analytic::primitives; - use crate::analytic::topology::Accuracy; - use crate::analytic::Frame3; - use crate::world::{NodeSpec, Similarity3}; - - fn accuracy() -> Accuracy { - Accuracy { - geometric: 1e-8, - intersection: 1e-9, - tessellation: 0.01, - exchange: 1e-6, - } - } - - fn at(x: f64, y: f64, z: f64) -> Similarity3 { - Similarity3 { - frame: Frame3 { - origin: [x, y, z], - ..Frame3::IDENTITY - }, - scale: 1.0, - } - } - - fn graph_with(bodies: &[(&str, &str, Similarity3)]) -> WorldGraph { - let mut graph = WorldGraph::new(); - graph - .define( - "unit", - primitives::cuboid("unit".into(), Frame3::IDENTITY, [1.0; 3], accuracy()).unwrap(), - ) - .unwrap(); - graph - .define( - "small", - primitives::cuboid("small".into(), Frame3::IDENTITY, [0.2; 3], accuracy()).unwrap(), - ) - .unwrap(); - graph - .define( - "post", - primitives::cylinder("post".into(), Frame3::IDENTITY, 0.25, 1.0, accuracy()) - .unwrap(), - ) - .unwrap(); - for (id, definition, local) in bodies { - graph - .add_node(NodeSpec { - id: (*id).into(), - parent: None, - definition: Some((*definition).into()), - local: *local, - kind: None, - }) - .unwrap(); - } - graph - } - - fn ids(ids: &[&str]) -> Vec { - ids.iter().map(|id| id.to_string()).collect() - } - - #[test] - fn planar_bodies_report_their_exact_gap() { - let graph = graph_with(&[ - ("a", "unit", at(0.0, 0.0, 0.0)), - ("b", "unit", at(1.3, 0.2, 0.0)), - ]); - let found = graph.clearance(&ids(&["a"]), &ids(&["b"]), 0.5).unwrap(); - assert_eq!(found.len(), 1); - assert!((found[0].distance - 0.3).abs() < 1e-12); - assert!(found[0].error_bound <= 0.5 / 20.0); - assert!((found[0].points[0][0] - 1.0).abs() < 1e-12); - assert!((found[0].points[1][0] - 1.3).abs() < 1e-12); - } - - #[test] - fn bodies_farther_than_the_clearance_are_not_reported() { - let graph = graph_with(&[ - ("a", "unit", at(0.0, 0.0, 0.0)), - ("b", "unit", at(1.6, 0.0, 0.0)), - ]); - assert!(graph - .clearance(&ids(&["a"]), &ids(&["b"]), 0.5) - .unwrap() - .is_empty()); - } - - #[test] - fn overlapping_and_contained_bodies_are_zero_apart() { - let graph = graph_with(&[ - ("a", "unit", at(0.0, 0.0, 0.0)), - ("overlap", "unit", at(0.5, 0.5, 0.0)), - ("inside", "small", at(0.4, 0.4, 0.4)), - ]); - let found = graph - .clearance(&ids(&["a"]), &ids(&["overlap", "inside"]), 0.1) - .unwrap(); - assert_eq!(found.len(), 2); - assert!(found.iter().all(|proximity| proximity.distance == 0.0)); - } - - #[test] - fn curved_bodies_stay_within_the_reported_error_bound() { - let graph = graph_with(&[ - ("box", "unit", at(0.0, 0.0, 0.0)), - ("post", "post", at(1.5, 0.5, 0.0)), - ]); - let found = graph - .clearance(&ids(&["box"]), &ids(&["post"]), 0.4) - .unwrap(); - assert_eq!(found.len(), 1); - assert!(found[0].error_bound > 0.0 && found[0].error_bound <= 0.4 / 20.0); - assert!((found[0].distance - 0.25).abs() <= found[0].error_bound); - } - - #[test] - fn distance_measures_far_apart_items() { - let graph = graph_with(&[ - ("a", "unit", at(0.0, 0.0, 0.0)), - ("b", "unit", at(6.0, 0.0, 0.0)), - ]); - let found = graph.distance("a", "b", 0.01).unwrap().unwrap(); - assert!((found.distance - 5.0).abs() < 1e-12); - } - - #[test] - fn invalid_clearance_and_tolerance_are_rejected() { - let graph = graph_with(&[ - ("a", "unit", at(0.0, 0.0, 0.0)), - ("b", "unit", at(2.0, 0.0, 0.0)), - ]); - assert!(graph.clearance(&ids(&["a"]), &ids(&["b"]), 0.0).is_err()); - assert!(graph.distance("a", "b", f64::NAN).is_err()); - } -} diff --git a/main/opengeometry/src/world/transform.rs b/main/opengeometry/src/world/transform.rs deleted file mode 100644 index bbacfa4..0000000 --- a/main/opengeometry/src/world/transform.rs +++ /dev/null @@ -1,183 +0,0 @@ -use crate::analytic::geometry::{add, dot, scale}; -use crate::analytic::{Frame3, GeometryError, Point3}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Similarity3 { - pub frame: Frame3, - pub scale: f64, -} - -impl Default for Similarity3 { - fn default() -> Self { - Self::IDENTITY - } -} - -impl Similarity3 { - pub const IDENTITY: Self = Self { - frame: Frame3::IDENTITY, - scale: 1.0, - }; - - pub fn validate(&self) -> Result<(), GeometryError> { - self.frame.validate()?; - if !self.scale.is_finite() || self.scale <= 0.0 { - return Err(GeometryError::InvalidGeometry( - "similarity scale must be finite and positive".into(), - )); - } - Ok(()) - } - - pub fn apply_point(&self, point: Point3) -> Point3 { - add(self.frame.origin, self.apply_vector(point)) - } - - pub fn apply_vector(&self, vector: Point3) -> Point3 { - scale(self.frame.vector(vector), self.scale) - } - - pub fn compose(&self, child: &Self) -> Self { - Self { - frame: Frame3 { - origin: self.apply_point(child.frame.origin), - x: self.frame.vector(child.frame.x), - y: self.frame.vector(child.frame.y), - z: self.frame.vector(child.frame.z), - }, - scale: self.scale * child.scale, - } - } - - pub fn inverse(&self) -> Self { - let frame = &self.frame; - let inverse_scale = 1.0 / self.scale; - let rotated_origin = [ - dot(frame.origin, frame.x), - dot(frame.origin, frame.y), - dot(frame.origin, frame.z), - ]; - Self { - frame: Frame3 { - origin: scale(rotated_origin, -inverse_scale), - x: [frame.x[0], frame.y[0], frame.z[0]], - y: [frame.x[1], frame.y[1], frame.z[1]], - z: [frame.x[2], frame.y[2], frame.z[2]], - }, - scale: inverse_scale, - } - } - - pub fn to_column_major(&self) -> [f64; 16] { - let x = scale(self.frame.x, self.scale); - let y = scale(self.frame.y, self.scale); - let z = scale(self.frame.z, self.scale); - let o = self.frame.origin; - [ - x[0], x[1], x[2], 0.0, y[0], y[1], y[2], 0.0, z[0], z[1], z[2], 0.0, o[0], o[1], o[2], - 1.0, - ] - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn turn_about_y(origin: Point3, scale: f64) -> Similarity3 { - Similarity3 { - frame: Frame3 { - origin, - x: [0.0, 0.0, -1.0], - y: [0.0, 1.0, 0.0], - z: [1.0, 0.0, 0.0], - }, - scale, - } - } - - fn assert_point_close(actual: Point3, expected: Point3) { - for axis in 0..3 { - assert!( - (actual[axis] - expected[axis]).abs() < 1e-12, - "{actual:?} != {expected:?}" - ); - } - } - - #[test] - fn inverse_undoes_the_transform() { - let transform = turn_about_y([2.0, -1.0, 3.0], 2.5); - let point = [0.3, 0.7, -1.9]; - assert_point_close( - transform - .inverse() - .apply_point(transform.apply_point(point)), - point, - ); - assert_point_close( - transform.compose(&transform.inverse()).apply_point(point), - point, - ); - } - - #[test] - fn compose_applies_the_child_first() { - let parent = turn_about_y([10.0, 0.0, 0.0], 1.0); - let child = Similarity3 { - frame: Frame3 { - origin: [1.0, 0.0, 0.0], - ..Frame3::IDENTITY - }, - scale: 2.0, - }; - let point = [0.5, 1.0, 0.0]; - assert_point_close( - parent.compose(&child).apply_point(point), - parent.apply_point(child.apply_point(point)), - ); - assert_point_close(parent.compose(&child).apply_point(point), [10.0, 2.0, -2.0]); - } - - #[test] - fn composition_is_associative() { - let a = turn_about_y([1.0, 2.0, 3.0], 1.5); - let b = turn_about_y([-4.0, 0.5, 2.0], 0.5); - let c = turn_about_y([0.0, -3.0, 1.0], 3.0); - let point = [0.25, -0.5, 0.75]; - assert_point_close( - a.compose(&b).compose(&c).apply_point(point), - a.compose(&b.compose(&c)).apply_point(point), - ); - } - - #[test] - fn validate_rejects_non_positive_scale_and_skewed_frames() { - assert!(turn_about_y([0.0; 3], 0.0).validate().is_err()); - assert!(turn_about_y([0.0; 3], f64::NAN).validate().is_err()); - let skewed = Similarity3 { - frame: Frame3 { - x: [1.0, 0.1, 0.0], - ..Frame3::IDENTITY - }, - scale: 1.0, - }; - assert!(skewed.validate().is_err()); - assert!(turn_about_y([0.0; 3], 2.0).validate().is_ok()); - } - - #[test] - fn column_major_matrix_matches_apply_point() { - let transform = turn_about_y([2.0, -1.0, 3.0], 2.0); - let m = transform.to_column_major(); - let p = [0.3, 0.7, -1.9]; - let by_matrix = [ - m[0] * p[0] + m[4] * p[1] + m[8] * p[2] + m[12], - m[1] * p[0] + m[5] * p[1] + m[9] * p[2] + m[13], - m[2] * p[0] + m[6] * p[1] + m[10] * p[2] + m[14], - ]; - assert_point_close(by_matrix, transform.apply_point(p)); - } -} diff --git a/main/opengeometry/src/world/triangle.rs b/main/opengeometry/src/world/triangle.rs deleted file mode 100644 index dd6a113..0000000 --- a/main/opengeometry/src/world/triangle.rs +++ /dev/null @@ -1,199 +0,0 @@ -use crate::analytic::geometry::{add, cross, dot, norm, scale, sub}; -use crate::analytic::Point3; - -pub(super) type Triangle = [Point3; 3]; - -const DEGENERATE: f64 = 1e-24; - -pub(super) fn closest_point_on_triangle(p: Point3, [a, b, c]: Triangle) -> Point3 { - let ab = sub(b, a); - let ac = sub(c, a); - let ap = sub(p, a); - let d1 = dot(ab, ap); - let d2 = dot(ac, ap); - if d1 <= 0.0 && d2 <= 0.0 { - return a; - } - let bp = sub(p, b); - let d3 = dot(ab, bp); - let d4 = dot(ac, bp); - if d3 >= 0.0 && d4 <= d3 { - return b; - } - let vc = d1 * d4 - d3 * d2; - if vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0 { - return add(a, scale(ab, d1 / (d1 - d3))); - } - let cp = sub(p, c); - let d5 = dot(ab, cp); - let d6 = dot(ac, cp); - if d6 >= 0.0 && d5 <= d6 { - return c; - } - let vb = d5 * d2 - d1 * d6; - if vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0 { - return add(a, scale(ac, d2 / (d2 - d6))); - } - let va = d3 * d6 - d5 * d4; - if va <= 0.0 && d4 - d3 >= 0.0 && d5 - d6 >= 0.0 { - return add(b, scale(sub(c, b), (d4 - d3) / ((d4 - d3) + (d5 - d6)))); - } - let denominator = va + vb + vc; - if denominator.abs() <= DEGENERATE { - return a; - } - add( - a, - add(scale(ab, vb / denominator), scale(ac, vc / denominator)), - ) -} - -pub(super) fn closest_points_on_segments( - p1: Point3, - q1: Point3, - p2: Point3, - q2: Point3, -) -> (Point3, Point3) { - let d1 = sub(q1, p1); - let d2 = sub(q2, p2); - let r = sub(p1, p2); - let a = dot(d1, d1); - let e = dot(d2, d2); - let f = dot(d2, r); - let (s, t) = if a <= DEGENERATE && e <= DEGENERATE { - (0.0, 0.0) - } else if a <= DEGENERATE { - (0.0, (f / e).clamp(0.0, 1.0)) - } else { - let c = dot(d1, r); - if e <= DEGENERATE { - ((-c / a).clamp(0.0, 1.0), 0.0) - } else { - let b = dot(d1, d2); - let denominator = a * e - b * b; - let s = if denominator > DEGENERATE { - ((b * f - c * e) / denominator).clamp(0.0, 1.0) - } else { - 0.0 - }; - let t = (b * s + f) / e; - if t < 0.0 { - ((-c / a).clamp(0.0, 1.0), 0.0) - } else if t > 1.0 { - (((b - c) / a).clamp(0.0, 1.0), 1.0) - } else { - (s, t) - } - } - }; - (add(p1, scale(d1, s)), add(p2, scale(d2, t))) -} - -pub(super) fn segment_hits_triangle(p: Point3, q: Point3, [a, b, c]: Triangle) -> Option { - let direction = sub(q, p); - let edge1 = sub(b, a); - let edge2 = sub(c, a); - let h = cross(direction, edge2); - let determinant = dot(edge1, h); - if determinant.abs() <= DEGENERATE { - return None; - } - let inverse = 1.0 / determinant; - let s = sub(p, a); - let u = inverse * dot(s, h); - if !(0.0..=1.0).contains(&u) { - return None; - } - let qv = cross(s, edge1); - let v = inverse * dot(direction, qv); - if v < 0.0 || u + v > 1.0 { - return None; - } - let t = inverse * dot(edge2, qv); - (0.0..=1.0) - .contains(&t) - .then(|| add(p, scale(direction, t))) -} - -pub(super) fn triangle_distance(first: Triangle, second: Triangle) -> (f64, Point3, Point3) { - for (edges_of, other) in [(first, second), (second, first)] { - for index in 0..3 { - if let Some(hit) = - segment_hits_triangle(edges_of[index], edges_of[(index + 1) % 3], other) - { - return (0.0, hit, hit); - } - } - } - let mut best = (f64::INFINITY, first[0], second[0]); - let mut consider = |on_first: Point3, on_second: Point3| { - let distance = norm(sub(on_first, on_second)); - if distance < best.0 { - best = (distance, on_first, on_second); - } - }; - for vertex in first { - consider(vertex, closest_point_on_triangle(vertex, second)); - } - for vertex in second { - consider(closest_point_on_triangle(vertex, first), vertex); - } - for i in 0..3 { - for j in 0..3 { - let (on_first, on_second) = closest_points_on_segments( - first[i], - first[(i + 1) % 3], - second[j], - second[(j + 1) % 3], - ); - consider(on_first, on_second); - } - } - best -} - -#[cfg(test)] -mod tests { - use super::*; - - const FLOOR: Triangle = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]; - - fn close(a: f64, b: f64) -> bool { - (a - b).abs() < 1e-12 - } - - #[test] - fn a_triangle_above_another_is_its_height_away() { - let above = [[0.2, 0.2, 0.5], [0.6, 0.2, 0.5], [0.2, 0.6, 0.5]]; - assert!(close(triangle_distance(FLOOR, above).0, 0.5)); - } - - #[test] - fn crossing_triangles_are_zero_distance() { - let crossing = [[0.2, 0.2, -1.0], [0.2, 0.2, 1.0], [0.3, 0.1, 0.0]]; - assert_eq!(triangle_distance(FLOOR, crossing).0, 0.0); - } - - #[test] - fn skew_triangles_report_their_closest_points() { - let skew = [[2.0, -1.0, 1.0], [2.0, 1.0, 1.0], [3.0, 0.0, 1.0]]; - let (distance, on_first, on_second) = triangle_distance(FLOOR, skew); - assert!(close(distance, 2.0_f64.sqrt())); - assert_eq!(on_first, [1.0, 0.0, 0.0]); - assert_eq!(on_second, [2.0, 0.0, 1.0]); - } - - #[test] - fn coplanar_triangles_side_by_side_are_the_edge_gap_apart() { - let beside = [[1.5, 0.0, 0.0], [2.5, 0.0, 0.0], [1.5, 1.0, 0.0]]; - assert!(close(triangle_distance(FLOOR, beside).0, 0.5)); - } - - #[test] - fn a_point_above_the_interior_projects_straight_down() { - assert_eq!( - closest_point_on_triangle([0.25, 0.25, 3.0], FLOOR), - [0.25, 0.25, 0.0] - ); - } -} diff --git a/main/opengeometry/src/world/wasm.rs b/main/opengeometry/src/world/wasm.rs deleted file mode 100644 index cabb38e..0000000 --- a/main/opengeometry/src/world/wasm.rs +++ /dev/null @@ -1,268 +0,0 @@ -use super::{NodeSpec, ProjectionView, Similarity3, WorldError, WorldGraph}; -use crate::analytic::BrepEnvelope; -use crate::brep::Brep; -use crate::export::projection::{CameraParameters, HlrOptions}; -use crate::export::wasm::{ - optional_json, OGIfcExportResult, OGStepExportResult, OGStlExportResult, -}; -use crate::export::{IfcExportConfig, StepExportConfig, StlExportConfig}; -use wasm_bindgen::prelude::*; - -const MAX_NODE_JSON_BYTES: usize = 8 * 1024; -const MAX_ID_LIST_BYTES: usize = 4 * 1024 * 1024; -const MAX_LEGACY_BREP_BYTES: usize = 64 * 1024 * 1024; - -fn js_error(error: WorldError) -> JsValue { - JsValue::from_str( - &serde_json::to_string(&error) - .unwrap_or_else(|_| "world graph error could not be serialized".into()), - ) -} - -fn parse(json: &str, what: &str) -> Result { - if json.len() > MAX_NODE_JSON_BYTES { - return Err(js_error(WorldError::InvalidInput(format!( - "{what} exceeds 8 KiB" - )))); - } - serde_json::from_str(json) - .map_err(|error| js_error(WorldError::InvalidInput(format!("{what}: {error}")))) -} - -fn parse_options( - json: Option, - what: &str, -) -> Result { - optional_json(json, what).map_err(|error| js_error(WorldError::InvalidInput(error))) -} - -fn export_failure(error: String) -> JsValue { - js_error(WorldError::Export(error)) -} - -fn parse_ids(json: &str) -> Result, JsValue> { - if json.len() > MAX_ID_LIST_BYTES { - return Err(js_error(WorldError::InvalidInput( - "node id list exceeds 4 MiB".into(), - ))); - } - serde_json::from_str(json) - .map_err(|error| js_error(WorldError::InvalidInput(format!("node id list: {error}")))) -} - -#[wasm_bindgen] -pub struct OGWorldGraph { - graph: WorldGraph, -} - -impl Default for OGWorldGraph { - fn default() -> Self { - Self::new() - } -} - -#[wasm_bindgen] -impl OGWorldGraph { - #[wasm_bindgen(constructor)] - pub fn new() -> OGWorldGraph { - Self { - graph: WorldGraph::new(), - } - } - - pub fn define(&mut self, id: &str, brep_json: &str) -> Result<(), JsValue> { - let brep = BrepEnvelope::from_json(brep_json) - .map_err(|error| js_error(WorldError::Geometry(error)))?; - self.graph.define(id, brep).map_err(js_error) - } - - pub fn define_legacy(&mut self, id: &str, brep_json: &str) -> Result<(), JsValue> { - if brep_json.len() > MAX_LEGACY_BREP_BYTES { - return Err(js_error(WorldError::InvalidInput( - "legacy BRep JSON exceeds 64 MiB".into(), - ))); - } - let brep: Brep = serde_json::from_str(brep_json).map_err(|error| { - js_error(WorldError::InvalidInput(format!( - "legacy BRep {id}: {error}" - ))) - })?; - self.graph.define_legacy(id, brep).map_err(js_error) - } - - pub fn undefine(&mut self, id: &str) -> Result<(), JsValue> { - self.graph.undefine(id).map_err(js_error) - } - - pub fn has_definition(&self, id: &str) -> bool { - self.graph.has_definition(id) - } - - pub fn add_node(&mut self, spec_json: &str) -> Result<(), JsValue> { - let spec: NodeSpec = parse(spec_json, "node spec")?; - self.graph.add_node(spec).map_err(js_error) - } - - pub fn set_local(&mut self, id: &str, local_json: &str) -> Result<(), JsValue> { - let local: Similarity3 = parse(local_json, "local transform")?; - self.graph.set_local(id, local).map_err(js_error) - } - - pub fn reparent(&mut self, id: &str, parent: Option) -> Result<(), JsValue> { - self.graph.reparent(id, parent.as_deref()).map_err(js_error) - } - - pub fn remove_node(&mut self, id: &str) -> Result<(), JsValue> { - self.graph.remove_node(id).map_err(js_error) - } - - pub fn has_node(&self, id: &str) -> bool { - self.graph.has_node(id) - } - - pub fn get_world(&self, id: &str) -> Result, JsValue> { - Ok(self - .graph - .world(id) - .map_err(js_error)? - .to_column_major() - .to_vec()) - } - - pub fn get_world_bounds(&self, id: &str) -> Result, JsValue> { - Ok(self - .graph - .world_bounds(id) - .map_err(js_error)? - .map(|bounds| bounds.to_vec()) - .unwrap_or_default()) - } - - pub fn clash( - &self, - items_a_json: &str, - items_b_json: &str, - depth: f64, - ) -> Result { - let items_a: Vec = parse_ids(items_a_json)?; - let items_b: Vec = parse_ids(items_b_json)?; - let clashes = self - .graph - .clash(&items_a, &items_b, depth) - .map_err(js_error)?; - serde_json::to_string(&clashes) - .map_err(|error| js_error(WorldError::InvalidInput(error.to_string()))) - } - - pub fn clearance( - &self, - items_a_json: &str, - items_b_json: &str, - clearance: f64, - ) -> Result { - let items_a: Vec = parse_ids(items_a_json)?; - let items_b: Vec = parse_ids(items_b_json)?; - let found = self - .graph - .clearance(&items_a, &items_b, clearance) - .map_err(js_error)?; - serde_json::to_string(&found) - .map_err(|error| js_error(WorldError::InvalidInput(error.to_string()))) - } - - pub fn distance(&self, item_a: &str, item_b: &str, tolerance: f64) -> Result { - let found = self - .graph - .distance(item_a, item_b, tolerance) - .map_err(js_error)?; - serde_json::to_string(&found) - .map_err(|error| js_error(WorldError::InvalidInput(error.to_string()))) - } - - pub fn project_lines( - &self, - items_json: &str, - camera_json: &str, - hlr_json: Option, - deflection: f64, - ) -> Result { - let items = parse_ids(items_json)?; - let camera: CameraParameters = parse_options(Some(camera_json.to_string()), "camera")?; - let hlr: HlrOptions = parse_options(hlr_json, "HLR")?; - let scene = self - .graph - .project(&items, &camera, &hlr, deflection) - .map_err(js_error)?; - serde_json::to_string(&scene.to_lines()) - .map_err(|error| js_error(WorldError::InvalidInput(error.to_string()))) - } - - pub fn project_views( - &self, - items_json: &str, - views_json: &str, - deflection: f64, - ) -> Result { - let items = parse_ids(items_json)?; - if views_json.len() > MAX_ID_LIST_BYTES { - return Err(js_error(WorldError::InvalidInput( - "projection views exceed 4 MiB".into(), - ))); - } - let views: Vec = serde_json::from_str(views_json) - .map_err(|error| js_error(WorldError::InvalidInput(format!("views: {error}"))))?; - let projected = self - .graph - .project_views(&items, &views, deflection) - .map_err(js_error)?; - serde_json::to_string(&projected) - .map_err(|error| js_error(WorldError::InvalidInput(error.to_string()))) - } - - pub fn export_stl( - &self, - items_json: &str, - config_json: Option, - ) -> Result { - let config: StlExportConfig = parse_options(config_json, "STL config")?; - let (bytes, report) = self - .graph - .export_stl(&parse_ids(items_json)?, &config) - .map_err(js_error)?; - OGStlExportResult::from_parts(bytes, report).map_err(export_failure) - } - - pub fn export_step( - &self, - items_json: &str, - config_json: Option, - ) -> Result { - let config: StepExportConfig = parse_options(config_json, "STEP config")?; - let (text, report) = self - .graph - .export_step(&parse_ids(items_json)?, &config) - .map_err(js_error)?; - OGStepExportResult::from_parts(text, report).map_err(export_failure) - } - - pub fn export_ifc( - &self, - items_json: &str, - config_json: Option, - ) -> Result { - let config: IfcExportConfig = parse_options(config_json, "IFC config")?; - let (text, report) = self - .graph - .export_ifc(&parse_ids(items_json)?, &config) - .map_err(js_error)?; - OGIfcExportResult::from_parts(text, report).map_err(export_failure) - } - - pub fn node_count(&self) -> u32 { - self.graph.node_count() as u32 - } - - pub fn definition_count(&self) -> u32 { - self.graph.definition_count() as u32 - } -} diff --git a/main/opengeometry/src/world_graph/assembly.rs b/main/opengeometry/src/world_graph/assembly.rs new file mode 100644 index 0000000..a5d32fd --- /dev/null +++ b/main/opengeometry/src/world_graph/assembly.rs @@ -0,0 +1,48 @@ +use super::candidate::IdKind; +use super::change_log::ChangeSet; +use super::error::{ErrorCode, GraphError}; +use super::graph::WorldGraph; +use super::node::NodeKind; +use super::primitive::{check_no_plane, CreateOptions}; +use crate::brep::Similarity3; + +impl WorldGraph { + pub fn create_system_assembly( + &mut self, + options: CreateOptions, + ) -> Result<(String, ChangeSet), GraphError> { + if options.body_type.is_some() { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "assemblies have no body type", + )); + } + check_no_plane(options.plane)?; + let mut created = None; + let changes = self.mutate(|draft| { + if let Some(parent) = &options.parent { + if !draft.nodes.contains_key(parent) { + return Err(GraphError::code(ErrorCode::UnknownNode, parent)); + } + } + let id = draft.new_og_id(options.og_id.as_deref(), IdKind::Assembly)?; + let handle = draft.new_handle()?; + draft.add_node( + &id, + handle, + &options.parent, + Similarity3::IDENTITY, + None, + NodeKind::SystemAssembly, + ); + created = Some(id); + Ok(()) + })?; + Ok(( + created.ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "created node is missing") + })?, + changes, + )) + } +} diff --git a/main/opengeometry/src/world_graph/bounds.rs b/main/opengeometry/src/world_graph/bounds.rs new file mode 100644 index 0000000..95a280a --- /dev/null +++ b/main/opengeometry/src/world_graph/bounds.rs @@ -0,0 +1,74 @@ +use super::error::{ErrorCode, GraphError}; +use super::graph::WorldGraph; +use crate::brep::PatchBounds; +use crate::math::Point3; + +type Bounds3 = [f64; 6]; + +fn corners(bounds: PatchBounds) -> [Point3; 8] { + std::array::from_fn(|index| { + std::array::from_fn(|axis| { + if index & (1 << axis) == 0 { + bounds.axes[axis].lo + } else { + bounds.axes[axis].hi + } + }) + }) +} + +fn include(bounds: Option, point: Point3) -> Bounds3 { + match bounds { + Some(bounds) => [ + bounds[0].min(point[0]), + bounds[1].min(point[1]), + bounds[2].min(point[2]), + bounds[3].max(point[0]), + bounds[4].max(point[1]), + bounds[5].max(point[2]), + ], + None => [point[0], point[1], point[2], point[0], point[1], point[2]], + } +} + +impl WorldGraph { + pub fn bounds(&self, og_id: &str) -> Result, GraphError> { + let mut stack = vec![og_id.to_string()]; + let mut bounds = None; + while let Some(current) = stack.pop() { + let node = self.node(¤t)?; + stack.extend(node.children.iter().rev().cloned()); + if let Some(shape_id) = &node.shape { + let shape = self.shapes.shapes.get(shape_id).ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "shape reference is missing") + })?; + if let Some(local_bounds) = shape.brep.bounds()? { + let placement = self.world_placement(¤t)?; + for corner in corners(local_bounds) { + bounds = Some(include(bounds, placement.apply_point(corner))); + } + } + } + } + Ok(bounds) + } + + pub fn local_bounds(&self, og_id: &str) -> Result, GraphError> { + let Some(shape_id) = &self.node(og_id)?.shape else { + return Ok(None); + }; + let shape = self.shapes.shapes.get(shape_id).ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "shape reference is missing") + })?; + Ok(shape.brep.bounds()?.map(|bounds| { + [ + bounds.axes[0].lo, + bounds.axes[1].lo, + bounds.axes[2].lo, + bounds.axes[0].hi, + bounds.axes[1].hi, + bounds.axes[2].hi, + ] + })) + } +} diff --git a/main/opengeometry/src/world_graph/candidate.rs b/main/opengeometry/src/world_graph/candidate.rs new file mode 100644 index 0000000..cfeb7de --- /dev/null +++ b/main/opengeometry/src/world_graph/candidate.rs @@ -0,0 +1,199 @@ +use super::error::{ErrorCode, GraphError}; +use super::node::{Node, NodeKind}; +use super::shape_store::{Shape, ShapeId}; +use crate::brep::{BodyType, BrepEnvelope, Similarity3}; +use std::collections::{BTreeMap, BTreeSet}; +use std::sync::Arc; + +#[derive(Clone, Copy)] +pub(super) struct IdCounters { + solid: u64, + wire: u64, + assembly: u64, +} + +#[derive(Clone, Copy)] +pub(super) enum IdKind { + Solid, + Wire, + Assembly, +} + +#[derive(Clone)] +pub(super) struct Candidate { + pub(super) nodes: BTreeMap, + pub(super) shapes: BTreeMap, + pub(super) next_shape_id: u64, + pub(super) next_handle: u32, + pub(super) next_generation: u32, + pub(super) next_og_ids: IdCounters, + pub(super) affected: BTreeSet, +} + +impl Candidate { + pub(super) fn copy_kind(&self, shape_id: &str) -> Result { + let shape = self.shapes.get(shape_id).ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "shape reference is missing") + })?; + IdKind::of_shape(&shape.brep) + } + + pub(super) fn new_og_id( + &mut self, + supplied: Option<&str>, + kind: IdKind, + ) -> Result { + if let Some(id) = supplied { + if id.is_empty() { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "ogId is empty", + )); + } + if id.chars().count() > 1024 { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "ogId exceeds 1024 characters", + )); + } + if self.nodes.contains_key(id) { + return Err(GraphError::code(ErrorCode::DuplicateNode, id)); + } + return Ok(id.into()); + } + loop { + let counter = self.next_og_ids.slot(kind); + let id = format!("{}-{}", kind.prefix(), *counter); + *counter = counter.checked_add(1).ok_or_else(|| { + GraphError::code(ErrorCode::LimitExceeded, "node id counter overflow") + })?; + if !self.nodes.contains_key(&id) { + return Ok(id); + } + } + } + + pub(super) fn new_shape_id(&mut self) -> Result { + let id = format!("shape-{}", self.next_shape_id); + self.next_shape_id = self.next_shape_id.checked_add(1).ok_or_else(|| { + GraphError::code(ErrorCode::LimitExceeded, "shape id counter overflow") + })?; + Ok(id) + } + + pub(super) fn new_handle(&mut self) -> Result<(u32, u32), GraphError> { + let handle = self.next_handle; + let generation = self.next_generation; + self.next_handle = self + .next_handle + .checked_add(1) + .ok_or_else(|| GraphError::code(ErrorCode::LimitExceeded, "handle counter overflow"))?; + self.next_generation = self.next_generation.checked_add(1).ok_or_else(|| { + GraphError::code(ErrorCode::LimitExceeded, "generation counter overflow") + })?; + Ok((handle, generation)) + } + + pub(super) fn add_body( + &mut self, + id: &str, + shape_id: ShapeId, + brep: BrepEnvelope, + edge_keys: Vec, + parent: &Option, + local: Similarity3, + ) -> Result<(), GraphError> { + let handle = self.new_handle()?; + self.shapes.insert( + shape_id.clone(), + Shape { + brep: Arc::new(brep), + revision: 0, + users: 1, + edge_keys, + report: None, + }, + ); + self.add_node(id, handle, parent, local, Some(shape_id), NodeKind::Body); + Ok(()) + } + + pub(super) fn add_node( + &mut self, + id: &str, + handle: (u32, u32), + parent: &Option, + local: Similarity3, + shape: Option, + kind: NodeKind, + ) { + let (handle, generation) = handle; + self.nodes.insert( + id.to_string(), + Node { + og_id: id.to_string(), + handle, + generation, + parent: parent.clone(), + children: Vec::new(), + local, + shape, + kind, + place_input: None, + }, + ); + if let Some(parent) = parent { + if let Some(parent_node) = self.nodes.get_mut(parent) { + parent_node.children.push(id.to_string()); + } + } + self.affected.insert(id.to_string()); + } +} + +impl IdKind { + pub(super) fn of_body(body_type: BodyType) -> Result { + match body_type { + BodyType::Solid => Ok(Self::Solid), + BodyType::Wire => Ok(Self::Wire), + BodyType::Sheet => Err(GraphError::code( + ErrorCode::InvalidTopology, + "graph bodies are never sheets", + )), + } + } + + fn of_shape(brep: &BrepEnvelope) -> Result { + if !brep.solids.is_empty() { + Ok(Self::Solid) + } else if brep.topology.faces.is_empty() && !brep.topology.wires.is_empty() { + Ok(Self::Wire) + } else { + Self::of_body(BodyType::Sheet) + } + } + + fn prefix(self) -> &'static str { + match self { + Self::Solid => "solid", + Self::Wire => "wire", + Self::Assembly => "assembly", + } + } +} + +impl IdCounters { + pub(super) const FIRST: Self = Self { + solid: 1, + wire: 1, + assembly: 1, + }; + + fn slot(&mut self, kind: IdKind) -> &mut u64 { + match kind { + IdKind::Solid => &mut self.solid, + IdKind::Wire => &mut self.wire, + IdKind::Assembly => &mut self.assembly, + } + } +} diff --git a/main/opengeometry/src/world_graph/change_log.rs b/main/opengeometry/src/world_graph/change_log.rs new file mode 100644 index 0000000..25165b2 --- /dev/null +++ b/main/opengeometry/src/world_graph/change_log.rs @@ -0,0 +1,21 @@ +use super::shape_store::ShapeId; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct NodeChange { + pub og_id: String, + pub handle: u32, + pub(super) generation: u32, + pub(super) shape_id: Option, + pub shape_revision: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct ChangeSet { + pub(super) revision: u64, + pub added: Vec, + pub changed: Vec, + pub removed: Vec, +} diff --git a/main/opengeometry/src/world_graph/commit.rs b/main/opengeometry/src/world_graph/commit.rs new file mode 100644 index 0000000..45420b0 --- /dev/null +++ b/main/opengeometry/src/world_graph/commit.rs @@ -0,0 +1,255 @@ +use super::candidate::Candidate; +use super::change_log::{ChangeSet, NodeChange}; +use super::error::{ErrorCode, GraphError}; +use super::graph::{JournalEntry, WorldGraph}; +use super::node::Node; +use super::shape_store::{Reserved, Shape, ShapeId}; +use crate::brep::BrepEnvelope; +use crate::math::Point3; +use std::collections::{BTreeMap, BTreeSet}; +use std::sync::Arc; + +impl WorldGraph { + pub fn reserve(&self, new_shapes: usize, changed: &[ShapeId]) -> Result { + let mut shape_ids = Vec::with_capacity(new_shapes); + let mut next_id = self.shapes.next_id; + while shape_ids.len() < new_shapes { + let id = format!("shape-{next_id}"); + next_id = next_id.checked_add(1).ok_or_else(|| { + GraphError::code(ErrorCode::LimitExceeded, "shape id counter overflow") + })?; + if !self.shapes.shapes.contains_key(&id) && !self.shapes.high_water.contains_key(&id) { + shape_ids.push(id); + } + } + let mut revisions = Vec::with_capacity(changed.len()); + for id in changed { + let high = self.shapes.high_water.get(id).ok_or_else(|| { + GraphError::code(ErrorCode::InvalidOperand, "shape has no revision history") + })?; + let next = high.checked_add(1).ok_or_else(|| { + GraphError::code(ErrorCode::LimitExceeded, "shape revision overflow") + })?; + revisions.push((id.clone(), next)); + } + Ok(Reserved { + shape_ids, + revisions, + }) + } + + fn draft(&self) -> Candidate { + Candidate { + nodes: self.nodes.clone(), + shapes: self.shapes.shapes.clone(), + next_shape_id: self.shapes.next_id, + next_handle: self.next_handle, + next_generation: self.next_generation, + next_og_ids: self.next_og_ids, + affected: BTreeSet::new(), + } + } + + pub(super) fn change_for(node: &Node, shapes: &BTreeMap) -> NodeChange { + NodeChange { + og_id: node.og_id.clone(), + handle: node.handle, + generation: node.generation, + shape_id: node.shape.clone(), + shape_revision: node + .shape + .as_ref() + .and_then(|id| shapes.get(id).map(|shape| shape.revision)), + } + } + + fn precision_guard(&self, brep: &BrepEnvelope) -> Result<(), GraphError> { + brep.validate()?; + if let Some(bounds) = brep.bounds()? { + let corner: Point3 = std::array::from_fn(|axis| { + bounds.axes[axis].lo.abs().max(bounds.axes[axis].hi.abs()) + }); + let magnitude = corner[0].hypot(corner[1]).hypot(corner[2]); + if magnitude * 64.0 * f64::EPSILON > self.accuracy.geometric { + return Err(GraphError::code( + ErrorCode::LimitExceeded, + "shape exceeds precision extent", + )); + } + } + Ok(()) + } + + fn commit(&mut self, draft: Candidate) -> Result { + self.check_changed_shapes(&draft)?; + let revision = self + .revision + .checked_add(1) + .ok_or_else(|| GraphError::code(ErrorCode::LimitExceeded, "graph revision overflow"))?; + let mut before_nodes = BTreeMap::new(); + let mut before_shapes = BTreeMap::new(); + let mut changes = ChangeSet { + revision, + added: Vec::new(), + changed: Vec::new(), + removed: Vec::new(), + }; + self.add_node_changes(&draft, &mut before_nodes, &mut changes); + self.add_shape_changes(&draft, &mut before_shapes, &mut changes); + if !self.marks.is_empty() { + self.journal.push(JournalEntry { + nodes: before_nodes, + shapes: before_shapes, + }); + } + for (id, shape) in &draft.shapes { + self.shapes + .high_water + .entry(id.clone()) + .and_modify(|high| *high = (*high).max(shape.revision)) + .or_insert(shape.revision); + } + self.nodes = draft.nodes; + self.shapes.shapes = draft.shapes; + self.shapes.next_id = draft.next_shape_id; + self.next_handle = draft.next_handle; + self.next_generation = draft.next_generation; + self.next_og_ids = draft.next_og_ids; + self.revision = revision; + self.world_cache + .borrow_mut() + .retain(|id, _| !draft.affected.contains(id)); + self.changes.push(changes.clone()); + Ok(changes) + } + + fn check_changed_shapes(&self, draft: &Candidate) -> Result<(), GraphError> { + for (id, shape) in &draft.shapes { + let changed = self + .shapes + .shapes + .get(id) + .is_none_or(|old| !Arc::ptr_eq(&old.brep, &shape.brep)); + if changed { + self.precision_guard(&shape.brep)?; + let expected = self + .shapes + .high_water + .get(id) + .map_or(0, |value| value.saturating_add(1)); + if shape.revision != expected || shape.brep.revision != expected { + return Err(GraphError::code( + ErrorCode::RevisionConflict, + "reserved shape revision changed", + )); + } + } + } + Ok(()) + } + + fn add_node_changes( + &self, + draft: &Candidate, + before_nodes: &mut BTreeMap>, + changes: &mut ChangeSet, + ) { + let node_keys: BTreeSet<_> = self + .nodes + .keys() + .chain(draft.nodes.keys()) + .cloned() + .collect(); + for id in node_keys { + let old = self.nodes.get(&id); + let new = draft.nodes.get(&id); + if old != new { + before_nodes.insert(id.clone(), old.cloned()); + match (old, new) { + (None, Some(node)) => changes.added.push(Self::change_for(node, &draft.shapes)), + (Some(node), None) => changes + .removed + .push(Self::change_for(node, &self.shapes.shapes)), + (Some(previous), Some(node)) if previous.generation != node.generation => { + changes + .removed + .push(Self::change_for(previous, &self.shapes.shapes)); + changes.added.push(Self::change_for(node, &draft.shapes)); + } + (Some(_), Some(node)) => { + changes.changed.push(Self::change_for(node, &draft.shapes)) + } + (None, None) => {} + } + } else if draft.affected.contains(&id) { + if let Some(node) = new { + changes.changed.push(Self::change_for(node, &draft.shapes)); + } + } + } + } + + fn add_shape_changes( + &self, + draft: &Candidate, + before_shapes: &mut BTreeMap>, + changes: &mut ChangeSet, + ) { + let shape_keys: BTreeSet<_> = self + .shapes + .shapes + .keys() + .chain(draft.shapes.keys()) + .cloned() + .collect(); + for id in shape_keys { + let old = self.shapes.shapes.get(&id); + let new = draft.shapes.get(&id); + let changed = match (old, new) { + (Some(a), Some(b)) => { + !Arc::ptr_eq(&a.brep, &b.brep) + || a.revision != b.revision + || a.users != b.users + || a.edge_keys != b.edge_keys + || a.report != b.report + } + (None, None) => false, + _ => true, + }; + if changed { + before_shapes.insert(id.clone(), old.cloned()); + if let Some(shape) = new { + if old.is_none_or(|previous| previous.revision != shape.revision) { + for node in draft + .nodes + .values() + .filter(|node| node.shape.as_ref() == Some(&id)) + { + let item = Self::change_for(node, &draft.shapes); + if !changes + .added + .iter() + .any(|change| change.og_id == item.og_id) + && !changes + .changed + .iter() + .any(|change| change.og_id == item.og_id) + { + changes.changed.push(item); + } + } + } + } + } + } + } + + pub(super) fn mutate( + &mut self, + compute: impl FnOnce(&mut Candidate) -> Result<(), GraphError>, + ) -> Result { + let mut draft = self.draft(); + compute(&mut draft)?; + self.commit(draft) + } +} diff --git a/main/opengeometry/src/world_graph/creating.rs b/main/opengeometry/src/world_graph/creating.rs new file mode 100644 index 0000000..641d98a --- /dev/null +++ b/main/opengeometry/src/world_graph/creating.rs @@ -0,0 +1,185 @@ +use super::candidate::IdKind; +use super::change_log::ChangeSet; +use super::error::{ErrorCode, ErrorContext, GraphError}; +use super::graph::WorldGraph; +use super::node::EditScope; +use super::operands::{path_points, profile_loop}; +use super::primitive::{check_body_type, check_no_plane, CreateOptions}; +use crate::brep::{BodyType, BrepEnvelope, Similarity3}; +use crate::operations::creating::{extrude, sweep}; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "kind", deny_unknown_fields)] +pub enum CreatingOperation { + Extrude { + profile: String, + holes: Vec, + distance: f64, + }, + Sweep { + profile: String, + path: String, + }, +} + +impl CreatingOperation { + fn anchor(&self) -> &str { + match self { + Self::Extrude { profile, .. } => profile, + Self::Sweep { path, .. } => path, + } + } +} + +impl WorldGraph { + fn build_operation( + &self, + id: String, + operation: &CreatingOperation, + target: &str, + ) -> Result { + match operation { + CreatingOperation::Extrude { + profile, + holes, + distance, + } => { + let outer = profile_loop(self, profile, target)?; + let holes = holes + .iter() + .map(|hole| { + if hole == profile { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "hole is the profile", + )); + } + profile_loop(self, hole, target) + }) + .collect::, _>>()?; + extrude::build(id, outer, holes, *distance, self.accuracy).map_err(GraphError::from) + } + CreatingOperation::Sweep { profile, path } => { + let profile = profile_loop(self, profile, target)?; + let path = path_points(self, path, target)?; + sweep::build(id, profile, path, self.accuracy).map_err(GraphError::from) + } + } + } + + pub fn create_operation( + &mut self, + operation: CreatingOperation, + options: CreateOptions, + ) -> Result<(String, ChangeSet), GraphError> { + self.try_create_operation(operation, options) + .map_err(|error| error.in_context(ErrorContext::Create)) + } + + fn try_create_operation( + &mut self, + operation: CreatingOperation, + options: CreateOptions, + ) -> Result<(String, ChangeSet), GraphError> { + check_body_type(options.body_type, BodyType::Solid)?; + check_no_plane(options.plane)?; + let anchor = operation.anchor(); + let anchor_node = self.node(anchor)?; + let parent = options.parent.or_else(|| anchor_node.parent.clone()); + if let Some(parent_id) = &parent { + self.node(parent_id)?; + } + let local = if parent == anchor_node.parent { + anchor_node.local + } else { + let world = self.world_placement(anchor)?; + let parent_world = if let Some(parent_id) = &parent { + self.world_placement(parent_id)? + } else { + Similarity3::IDENTITY + }; + parent_world.inverse().compose(&world) + }; + let reserved = self.reserve(1, &[])?; + let shape_id = reserved.shape_ids[0].clone(); + let brep = self.build_operation(shape_id.clone(), &operation, anchor)?; + if brep.body_type()? != BodyType::Solid { + return Err(GraphError::code( + ErrorCode::BodyTypeMismatch, + "operation did not produce a solid", + )); + } + let mut created = None; + let changes = self.mutate(|draft| { + let id = draft.new_og_id(options.og_id.as_deref(), IdKind::Solid)?; + let allocated = draft.new_shape_id()?; + if allocated != shape_id { + return Err(GraphError::code( + ErrorCode::RevisionConflict, + "reserved shape id changed", + )); + } + draft.add_body(&id, shape_id, brep, Vec::new(), &parent, local)?; + created = Some(id); + Ok(()) + })?; + Ok(( + created.ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "created body is missing") + })?, + changes, + )) + } + + pub fn rebuild_operation( + &mut self, + og_id: &str, + operation: CreatingOperation, + scope: EditScope, + ) -> Result { + self.try_rebuild_operation(og_id, operation, scope) + .map_err(|error| error.in_context(ErrorContext::Rebuild)) + } + + fn try_rebuild_operation( + &mut self, + og_id: &str, + operation: CreatingOperation, + scope: EditScope, + ) -> Result { + let previous = self.ensure_editable(og_id, scope)?; + if previous.brep.body_type()? != BodyType::Solid { + return Err(GraphError::code( + ErrorCode::BodyTypeMismatch, + "body is not a solid", + )); + } + let shape_id = self + .node(og_id)? + .shape + .clone() + .ok_or_else(|| GraphError::code(ErrorCode::InvalidOperand, "node is not a body"))?; + let revision = self.reserve(0, std::slice::from_ref(&shape_id))?.revisions[0].1; + let mut brep = self.build_operation(shape_id.clone(), &operation, og_id)?; + if brep.body_type()? != BodyType::Solid { + return Err(GraphError::code( + ErrorCode::BodyTypeMismatch, + "rebuild changed body type", + )); + } + brep.revision = revision; + brep.validate()?; + self.mutate(|draft| { + let shape = draft.shapes.get_mut(&shape_id).ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "shape reference is missing") + })?; + shape.brep = Arc::new(brep); + shape.revision = revision; + shape.edge_keys.clear(); + shape.report = None; + Ok(()) + }) + } +} diff --git a/main/opengeometry/src/world_graph/error.rs b/main/opengeometry/src/world_graph/error.rs new file mode 100644 index 0000000..b69c30d --- /dev/null +++ b/main/opengeometry/src/world_graph/error.rs @@ -0,0 +1,197 @@ +use crate::brep::GeometryError; +use crate::operations::OperationError; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum ErrorCode { + NotInitialised, + InvalidParameter, + InvalidTransform, + InvalidOperand, + UnknownNode, + DuplicateNode, + CycleDetected, + NotAChild, + Disposed, + BodyTypeMismatch, + SharedShape, + ToolSharesTargetShape, + EmptyResult, + SweepSelfIntersection, + CoverageGap, + UnresolvedIntersection, + UnresolvedTessellation, + UnsupportedGeometry, + LimitExceeded, + InvalidGeometry, + InvalidTopology, + InvalidMark, + WorkerFailure, + KernelPanic, + RevisionConflict, + Cancelled, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ErrorContext { + Create, + Rebuild, + Transform, + Operate, + Export, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(untagged, rename_all_fields = "camelCase")] +pub enum ErrorDetails { + None, + SharedShape { + shape_id: Option, + instance_count: u32, + sharing: Vec, + }, + MissingReference { + kind: String, + index: u32, + }, + CoverageGap { + families: [String; 2], + }, + Math { + math: String, + }, + Operate { + handlers: Vec, + tool_index: usize, + og_ids: Vec, + }, + Export { + og_id: String, + }, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub enum GraphError { + Code { + code: ErrorCode, + message: String, + details: ErrorDetails, + }, + Geometry(GeometryError), +} + +impl GraphError { + pub(crate) fn code(code: ErrorCode, message: impl Into) -> Self { + Self::Code { + code, + message: message.into(), + details: ErrorDetails::None, + } + } + + pub(super) fn with_details( + code: ErrorCode, + message: impl Into, + details: ErrorDetails, + ) -> Self { + Self::Code { + code, + message: message.into(), + details, + } + } + + pub fn error_code(&self) -> ErrorCode { + match self { + Self::Code { code, .. } => *code, + Self::Geometry(error) => match error { + GeometryError::Math(_) | GeometryError::InvalidGeometry(_) => { + ErrorCode::InvalidGeometry + } + GeometryError::InvalidTopology(_) + | GeometryError::MissingReference { .. } + | GeometryError::UnsupportedSchema { .. } => ErrorCode::InvalidTopology, + GeometryError::UnsupportedGeometry(_) => ErrorCode::UnsupportedGeometry, + GeometryError::AmbiguousProfileAlignment(_) => ErrorCode::InvalidParameter, + GeometryError::SingularParameterization => ErrorCode::InvalidGeometry, + GeometryError::CoverageGap { .. } => ErrorCode::CoverageGap, + GeometryError::UnresolvedIntersection(_) => ErrorCode::UnresolvedIntersection, + GeometryError::UnresolvedTessellation(_) => ErrorCode::UnresolvedTessellation, + GeometryError::LimitExceeded(_) => ErrorCode::LimitExceeded, + }, + } + } + + pub fn details(&self) -> ErrorDetails { + match self { + Self::Code { details, .. } => details.clone(), + Self::Geometry(GeometryError::MissingReference { kind, index }) => { + ErrorDetails::MissingReference { + kind: kind.clone(), + index: *index, + } + } + Self::Geometry(GeometryError::CoverageGap { families }) => ErrorDetails::CoverageGap { + families: families.clone(), + }, + Self::Geometry(GeometryError::Math(math)) => ErrorDetails::Math { + math: format!("{math:?}"), + }, + Self::Geometry(_) => ErrorDetails::None, + } + } + + pub fn message(&self) -> String { + match self { + Self::Code { message, .. } => message.clone(), + Self::Geometry(error) => error.to_string(), + } + } + + pub fn in_context(self, context: ErrorContext) -> GraphError { + if self != Self::Geometry(GeometryError::SingularParameterization) { + return self; + } + match context { + ErrorContext::Create | ErrorContext::Rebuild => { + Self::code(ErrorCode::InvalidParameter, "SingularParameterization") + } + ErrorContext::Transform => { + Self::code(ErrorCode::InvalidTransform, "SingularParameterization") + } + ErrorContext::Operate | ErrorContext::Export => self, + } + } +} + +impl From for GraphError { + fn from(value: GeometryError) -> Self { + Self::Geometry(value) + } +} + +impl From for GraphError { + fn from(value: OperationError) -> Self { + match value { + OperationError::InvalidParameter(message) => { + Self::code(ErrorCode::InvalidParameter, message) + } + OperationError::SweepSelfIntersection(message) => { + Self::code(ErrorCode::SweepSelfIntersection, message) + } + OperationError::InvalidTopology(message) => { + Self::code(ErrorCode::InvalidTopology, message) + } + OperationError::Geometry(error) => Self::Geometry(error), + } + } +} + +impl std::fmt::Display for GraphError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "{self:?}") + } +} + +impl std::error::Error for GraphError {} diff --git a/main/opengeometry/src/world_graph/export.rs b/main/opengeometry/src/world_graph/export.rs new file mode 100644 index 0000000..9598698 --- /dev/null +++ b/main/opengeometry/src/world_graph/export.rs @@ -0,0 +1,294 @@ +use super::error::{ErrorCode, ErrorContext, ErrorDetails, GraphError}; +use super::graph::WorldGraph; +use super::node::NodeKind; +use super::placement::is_identity; +use crate::brep::{placed, BodyType, BrepEnvelope, Frame3, GeometryError, Similarity3}; +use crate::exchange::{ + export_bodies, StepBodyInput, StepExportFailure, StepExportReport, StepSkipped, +}; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeSet; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(default, rename_all = "camelCase", deny_unknown_fields)] +pub struct StepOptions { + pub unit: String, + pub up_axis: String, + pub name: String, + pub timestamp: String, +} + +impl Default for StepOptions { + fn default() -> Self { + Self { + unit: "millimetre".into(), + up_axis: "Z".into(), + name: "opengeometry-export".into(), + timestamp: "1970-01-01T00:00:00".into(), + } + } +} + +impl WorldGraph { + fn export_world_placement(&self, og_id: &str) -> Result { + let mut path = Vec::new(); + let mut cursor = Some(og_id); + while let Some(current) = cursor { + let node = self.node(current)?; + path.push(node.local); + cursor = node.parent.as_deref(); + } + let mut world = Similarity3::IDENTITY; + for local in path.iter().rev() { + world = world.compose(local); + } + world.validate()?; + Ok(world) + } + + fn expand_export( + &self, + og_id: &str, + direct: bool, + seen: &mut BTreeSet, + selected: &mut Vec, + skipped: &mut Vec, + ) -> Result<(), GraphError> { + let node = self.node(og_id)?; + if !seen.insert(node.og_id.clone()) { + return Ok(()); + } + if node.kind != NodeKind::SystemAssembly { + let shape = self.shape(og_id)?; + match shape.brep.body_type()? { + BodyType::Solid => selected.push(og_id.into()), + BodyType::Wire if direct => { + return Err(GraphError::code( + ErrorCode::InvalidOperand, + "a Wire cannot be exported directly", + )) + } + BodyType::Wire => skipped.push(StepSkipped { + og_id: og_id.into(), + reason: "Wire".into(), + }), + BodyType::Sheet if direct => { + return Err(GraphError::code( + ErrorCode::InvalidOperand, + "a Sheet cannot be exported directly", + )) + } + BodyType::Sheet => skipped.push(StepSkipped { + og_id: og_id.into(), + reason: "Sheet".into(), + }), + } + } + for child in &node.children { + self.expand_export(child, false, seen, selected, skipped)?; + } + Ok(()) + } + + pub fn export_step( + &self, + nodes: &[String], + options: &StepOptions, + ) -> Result<(String, StepExportReport), GraphError> { + self.try_export_step(nodes, options) + .map_err(|error| error.in_context(ErrorContext::Export)) + } + + fn try_export_step( + &self, + nodes: &[String], + options: &StepOptions, + ) -> Result<(String, StepExportReport), GraphError> { + if nodes.is_empty() { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "STEP export requires at least one node", + )); + } + if !matches!(options.unit.as_str(), "metre" | "millimetre") + || !matches!(options.up_axis.as_str(), "Y" | "Z") + || !valid_timestamp(&options.timestamp) + || options.name.is_empty() + { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "invalid STEP export options", + )); + } + if options.name.len() > 4096 { + return Err(GraphError::code( + ErrorCode::LimitExceeded, + "STEP export name exceeds 4096 bytes", + )); + } + let (selected, skipped) = self.step_selection(nodes)?; + if selected.is_empty() { + return Err(GraphError::code( + ErrorCode::EmptyResult, + "STEP selection has no solids", + )); + } + let conversion = if options.up_axis == "Z" { + Similarity3 { + frame: Frame3 { + origin: [0.0; 3], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, 1.0], + z: [0.0, -1.0, 0.0], + }, + scale: 1.0, + } + } else { + Similarity3::IDENTITY + }; + let owned = self.placed_bodies(selected, conversion)?; + let inputs = owned + .iter() + .map(|(og_id, shape_id, revision, brep)| StepBodyInput { + og_id, + shape_id, + shape_revision: *revision, + brep, + }) + .collect::>(); + export_bodies( + &inputs, + &options.unit, + &options.up_axis, + &options.name, + &options.timestamp, + skipped, + ) + .map_err(|failure| body_error(failure, &owned)) + } + + fn step_selection( + &self, + nodes: &[String], + ) -> Result<(Vec, Vec), GraphError> { + let mut seen = BTreeSet::new(); + let mut selected = Vec::new(); + let mut skipped = Vec::new(); + for og_id in nodes { + let node = self.node(og_id)?; + if node.kind == NodeKind::Body + && self.shape(og_id)?.brep.body_type()? != BodyType::Solid + { + return Err(GraphError::code( + ErrorCode::InvalidOperand, + "a non-solid body cannot be exported directly", + )); + } + self.expand_export(og_id, true, &mut seen, &mut selected, &mut skipped)?; + } + Ok((selected, skipped)) + } + + fn placed_bodies( + &self, + selected: Vec, + conversion: Similarity3, + ) -> Result, GraphError> { + let mut owned = Vec::<(String, String, u64, BrepEnvelope)>::with_capacity(selected.len()); + for og_id in selected { + let node = self.node(&og_id)?; + let shape_id = node + .shape + .as_ref() + .ok_or_else(|| GraphError::code(ErrorCode::InvalidTopology, "body has no shape"))?; + let shape = self.shape(&og_id)?; + let placement = conversion.compose(&self.export_world_placement(&og_id)?); + let mut brep = if is_identity(placement) { + (*shape.brep).clone() + } else { + placed(&shape.brep, placement.frame, placement.scale).map_err( + |error| match error { + GeometryError::LimitExceeded(message) => GraphError::with_details( + ErrorCode::LimitExceeded, + format!("{og_id}: {message}"), + ErrorDetails::Export { + og_id: og_id.clone(), + }, + ), + other => GraphError::Geometry(other), + }, + )? + }; + brep.id = og_id.clone(); + brep.revision = shape.revision; + brep.validate()?; + owned.push((og_id, shape_id.clone(), shape.revision, brep)); + } + Ok(owned) + } +} + +fn body_error( + failure: StepExportFailure, + owned: &[(String, String, u64, BrepEnvelope)], +) -> GraphError { + let og_id = failure + .body_index + .and_then(|index| owned.get(index)) + .map(|body| body.0.clone()); + let source = GraphError::Geometry(failure.error); + match og_id { + Some(og_id) => GraphError::with_details( + source.error_code(), + source.message(), + ErrorDetails::Export { og_id }, + ), + None => source, + } +} + +fn valid_timestamp(timestamp: &str) -> bool { + let bytes = timestamp.as_bytes(); + if bytes.len() != 19 + || [4, 7, 10, 13, 16] + .into_iter() + .zip([b'-', b'-', b'T', b':', b':']) + .any(|(index, separator)| bytes[index] != separator) + || bytes + .iter() + .enumerate() + .any(|(index, byte)| ![4, 7, 10, 13, 16].contains(&index) && !byte.is_ascii_digit()) + { + return false; + } + let year = timestamp[0..4].parse::().ok(); + let month = timestamp[5..7].parse::().ok(); + let day = timestamp[8..10].parse::().ok(); + let hour = timestamp[11..13].parse::().ok(); + let minute = timestamp[14..16].parse::().ok(); + let second = timestamp[17..19].parse::().ok(); + let (Some(year), Some(month), Some(day), Some(hour), Some(minute), Some(second)) = + (year, month, day, hour, minute, second) + else { + return false; + }; + if year == 0 || month == 0 || month > 12 || hour > 23 || minute > 59 || second > 59 { + return false; + } + let leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + let days = [ + 31, + if leap { 29 } else { 28 }, + 31, + 30, + 31, + 30, + 31, + 31, + 30, + 31, + 30, + 31, + ]; + day > 0 && day <= days[(month - 1) as usize] +} diff --git a/main/opengeometry/src/world_graph/graph.rs b/main/opengeometry/src/world_graph/graph.rs new file mode 100644 index 0000000..ca985ec --- /dev/null +++ b/main/opengeometry/src/world_graph/graph.rs @@ -0,0 +1,90 @@ +use super::candidate::IdCounters; +use super::change_log::ChangeSet; +use super::error::{ErrorCode, GraphError}; +use super::node::{MarkId, Node}; +use super::shape_store::{Shape, ShapeId, ShapeStore}; +use crate::brep::{Accuracy, Similarity3}; +use std::cell::RefCell; +use std::collections::BTreeMap; + +pub(super) struct JournalEntry { + pub(super) nodes: BTreeMap>, + pub(super) shapes: BTreeMap>, +} + +pub(super) struct LiveMark { + pub(super) id: MarkId, + pub(super) position: usize, +} + +pub struct WorldGraph { + pub(super) accuracy: Accuracy, + pub(super) shapes: ShapeStore, + pub(super) nodes: BTreeMap, + pub(super) revision: u64, + pub(super) changes: Vec, + pub(super) journal: Vec, + pub(super) marks: Vec, + pub(super) next_mark: u64, + pub(super) next_handle: u32, + pub(super) next_generation: u32, + pub(super) next_og_ids: IdCounters, + pub(super) world_cache: RefCell>, +} + +impl WorldGraph { + pub fn new(accuracy: Accuracy) -> Result { + accuracy.validate()?; + check_standard(accuracy)?; + Ok(Self { + accuracy, + shapes: ShapeStore::default(), + nodes: BTreeMap::new(), + revision: 0, + changes: Vec::new(), + journal: Vec::new(), + marks: Vec::new(), + next_mark: 0, + next_handle: 0, + next_generation: 0, + next_og_ids: IdCounters::FIRST, + world_cache: RefCell::new(BTreeMap::new()), + }) + } + + pub fn revision(&self) -> u64 { + self.revision + } + + pub fn accuracy(&self) -> Accuracy { + self.accuracy + } + + pub fn shape_count(&self) -> usize { + self.shapes.shapes.len() + } + + pub fn node_count(&self) -> usize { + self.nodes.len() + } +} + +fn check_standard(accuracy: Accuracy) -> Result<(), GraphError> { + let standard = Accuracy::STANDARD; + let equal = [ + (accuracy.geometric, standard.geometric), + (accuracy.intersection, standard.intersection), + (accuracy.tessellation, standard.tessellation), + (accuracy.exchange, standard.exchange), + ] + .iter() + .all(|(value, expected)| value.to_bits() == expected.to_bits()); + if equal { + Ok(()) + } else { + Err(GraphError::code( + ErrorCode::InvalidParameter, + "accuracy must be the standard value", + )) + } +} diff --git a/main/opengeometry/src/world_graph/hierarchy.rs b/main/opengeometry/src/world_graph/hierarchy.rs new file mode 100644 index 0000000..894d481 --- /dev/null +++ b/main/opengeometry/src/world_graph/hierarchy.rs @@ -0,0 +1,211 @@ +use super::candidate::Candidate; +use super::change_log::ChangeSet; +use super::error::{ErrorCode, GraphError}; +use super::graph::WorldGraph; +use crate::brep::{BrepEnvelope, Similarity3}; + +impl Candidate { + pub(super) fn world(&self, id: &str) -> Result { + let mut path = Vec::new(); + let mut cursor = Some(id.to_string()); + while let Some(current) = cursor { + let node = self + .nodes + .get(¤t) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, ¤t))?; + cursor = node.parent.clone(); + path.push(node.local); + } + let mut world = Similarity3::IDENTITY; + for local in path.iter().rev() { + world = world.compose(local); + } + world.validate().map_err(|error| { + GraphError::code( + ErrorCode::InvalidTransform, + format!("invalid world placement: {error}"), + ) + })?; + Ok(world) + } + + fn check_parent(&self, child: &str, parent: &str) -> Result<(), GraphError> { + let mut cursor = Some(parent.to_string()); + while let Some(current) = cursor { + if current == child { + return Err(GraphError::code( + ErrorCode::CycleDetected, + "reparent would create a cycle", + )); + } + cursor = self + .nodes + .get(¤t) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, ¤t))? + .parent + .clone(); + } + Ok(()) + } + + fn reparent( + &mut self, + child: &str, + parent: Option<&str>, + keep_world: bool, + ) -> Result<(), GraphError> { + if let Some(parent) = parent { + self.check_parent(child, parent)?; + } + let previous = self + .nodes + .get(child) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, child))? + .parent + .clone(); + let old_world = if keep_world { + Some(self.world(child)?) + } else { + None + }; + let parent_world = if keep_world { + Some(match parent { + Some(parent) => self.world(parent)?, + None => Similarity3::IDENTITY, + }) + } else { + None + }; + if previous.as_deref() == parent && !keep_world { + return Ok(()); + } + if let Some(previous) = &previous { + let previous_node = self.nodes.get_mut(previous).ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "previous parent is missing") + })?; + previous_node.children.retain(|id| id != child); + } + if let Some(parent) = parent { + let parent_node = self + .nodes + .get_mut(parent) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, parent))?; + parent_node.children.push(child.to_string()); + } + let descendants = self.descendants(child)?; + let node = self + .nodes + .get_mut(child) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, child))?; + node.parent = parent.map(str::to_string); + if let (Some(old_world), Some(parent_world)) = (old_world, parent_world) { + node.local = parent_world.inverse().compose(&old_world); + node.local.validate().map_err(|error| { + GraphError::code( + ErrorCode::InvalidTransform, + format!("invalid preserved world placement: {error}"), + ) + })?; + } + self.affected.extend(descendants); + Ok(()) + } +} + +pub(super) fn restamp( + mut brep: BrepEnvelope, + previous: &str, + next: &str, +) -> Result { + brep.id = next.into(); + brep.revision = 0; + for face in &mut brep.topology.faces { + for source in &mut face.provenance.sources { + if source.entity == previous { + source.entity = next.into(); + } + if source.body == previous { + source.body = next.into(); + } + } + } + brep.validate()?; + Ok(brep) +} + +impl WorldGraph { + pub fn children(&self, og_id: &str) -> Result<&[String], GraphError> { + Ok(&self.node(og_id)?.children) + } + + pub fn parent(&self, og_id: &str) -> Result, GraphError> { + Ok(self.node(og_id)?.parent.as_deref()) + } + + pub fn add_child( + &mut self, + parent: &str, + children: &[String], + keep_world: bool, + ) -> Result { + self.mutate(|draft| { + if !draft.nodes.contains_key(parent) { + return Err(GraphError::code(ErrorCode::UnknownNode, parent)); + } + for child in children { + draft.reparent(child, Some(parent), keep_world)?; + } + Ok(()) + }) + } + + pub fn remove_child( + &mut self, + parent: &str, + child: &str, + keep_world: bool, + ) -> Result { + self.mutate(|draft| { + let actual = draft + .nodes + .get(child) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, child))? + .parent + .as_deref(); + if actual != Some(parent) { + return Err(GraphError::code(ErrorCode::NotAChild, child)); + } + draft.reparent(child, None, keep_world) + }) + } + + pub fn dispose(&mut self, og_id: &str) -> Result { + self.mutate(|draft| { + let removed = draft.descendants(og_id)?; + let parent = draft.nodes.get(og_id).and_then(|node| node.parent.clone()); + if let Some(parent) = parent { + if let Some(parent_node) = draft.nodes.get_mut(&parent) { + parent_node.children.retain(|child| child != og_id); + } + } + for id in removed { + if let Some(node) = draft.nodes.remove(&id) { + if let Some(shape_id) = node.shape { + let shape = draft.shapes.get_mut(&shape_id).ok_or_else(|| { + GraphError::code( + ErrorCode::InvalidTopology, + "shape reference is missing", + ) + })?; + shape.users -= 1; + if shape.users == 0 { + draft.shapes.remove(&shape_id); + } + } + draft.affected.insert(id); + } + } + Ok(()) + }) + } +} diff --git a/main/opengeometry/src/world_graph/instancing.rs b/main/opengeometry/src/world_graph/instancing.rs new file mode 100644 index 0000000..553aed8 --- /dev/null +++ b/main/opengeometry/src/world_graph/instancing.rs @@ -0,0 +1,215 @@ +use super::candidate::Candidate; +use super::change_log::ChangeSet; +use super::error::{ErrorCode, GraphError}; +use super::graph::WorldGraph; +use super::hierarchy::restamp; +use super::node::Node; +use super::shape_store::Shape; +use crate::brep::Similarity3; +use serde::{Deserialize, Deserializer, Serialize}; +use std::sync::Arc; + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct CopyOptions { + pub og_id: Option, + #[serde(default, deserialize_with = "explicit_parent")] + pub parent: Option>, +} + +fn explicit_parent<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result>, D::Error> { + Option::::deserialize(deserializer).map(Some) +} + +impl WorldGraph { + pub fn instance( + &mut self, + source: &str, + options: CopyOptions, + ) -> Result<(String, ChangeSet), GraphError> { + let mut created = None; + let changes = self.mutate(|draft| { + let original = draft + .nodes + .get(source) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, source))? + .clone(); + let shape_id = original.shape.clone().ok_or_else(|| { + GraphError::code(ErrorCode::InvalidOperand, "an assembly cannot be instanced") + })?; + let (parent, local) = copy_placement( + draft, + source, + &options, + &original, + "invalid instance placement", + )?; + let kind = draft.copy_kind(&shape_id)?; + let id = draft.new_og_id(options.og_id.as_deref(), kind)?; + let handle = draft.new_handle()?; + let shape = draft.shapes.get_mut(&shape_id).ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "shape reference is missing") + })?; + shape.users = shape.users.checked_add(1).ok_or_else(|| { + GraphError::code(ErrorCode::LimitExceeded, "instance count overflow") + })?; + draft.add_node(&id, handle, &parent, local, Some(shape_id), original.kind); + copy_place_input(draft, &id, &original); + created = Some(id); + Ok(()) + })?; + Ok(( + created.ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "created instance is missing") + })?, + changes, + )) + } + + pub fn duplicate( + &mut self, + source: &str, + options: CopyOptions, + ) -> Result<(String, ChangeSet), GraphError> { + let mut created = None; + let changes = self.mutate(|draft| { + let original = draft + .nodes + .get(source) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, source))? + .clone(); + let old_shape_id = original.shape.as_ref().ok_or_else(|| { + GraphError::code( + ErrorCode::InvalidOperand, + "an assembly cannot be duplicated", + ) + })?; + let old_shape = draft + .shapes + .get(old_shape_id) + .ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "shape reference is missing") + })? + .clone(); + let (parent, local) = copy_placement( + draft, + source, + &options, + &original, + "invalid duplicate placement", + )?; + let kind = draft.copy_kind(old_shape_id)?; + let id = draft.new_og_id(options.og_id.as_deref(), kind)?; + let shape_id = draft.new_shape_id()?; + let brep = restamp((*old_shape.brep).clone(), old_shape_id, &shape_id)?; + let handle = draft.new_handle()?; + draft.shapes.insert( + shape_id.clone(), + Shape { + brep: Arc::new(brep), + revision: 0, + users: 1, + edge_keys: old_shape.edge_keys, + report: None, + }, + ); + draft.add_node(&id, handle, &parent, local, Some(shape_id), original.kind); + copy_place_input(draft, &id, &original); + created = Some(id); + Ok(()) + })?; + Ok(( + created.ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "created duplicate is missing") + })?, + changes, + )) + } + + pub fn make_unique(&mut self, og_id: &str) -> Result, GraphError> { + if self.shape(og_id)?.users == 1 { + return Ok(None); + } + self.mutate(|draft| { + let old_shape_id = draft + .nodes + .get(og_id) + .and_then(|node| node.shape.clone()) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, og_id))?; + let old_shape = draft + .shapes + .get(&old_shape_id) + .ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "shape reference is missing") + })? + .clone(); + let new_shape_id = draft.new_shape_id()?; + let brep = restamp((*old_shape.brep).clone(), &old_shape_id, &new_shape_id)?; + draft.shapes.insert( + new_shape_id.clone(), + Shape { + brep: Arc::new(brep), + revision: 0, + users: 1, + edge_keys: old_shape.edge_keys, + report: None, + }, + ); + draft + .shapes + .get_mut(&old_shape_id) + .ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "shape reference is missing") + })? + .users -= 1; + draft + .nodes + .get_mut(og_id) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, og_id))? + .shape = Some(new_shape_id); + draft.affected.insert(og_id.into()); + Ok(()) + }) + .map(Some) + } +} + +fn copy_placement( + draft: &Candidate, + source: &str, + options: &CopyOptions, + original: &Node, + message: &str, +) -> Result<(Option, Similarity3), GraphError> { + let parent = options + .parent + .clone() + .unwrap_or_else(|| original.parent.clone()); + if let Some(parent) = &parent { + if !draft.nodes.contains_key(parent) { + return Err(GraphError::code(ErrorCode::UnknownNode, parent)); + } + } + let local = if parent != original.parent { + let world = draft.world(source)?; + let parent_world = match &parent { + Some(parent) => draft.world(parent)?, + None => Similarity3::IDENTITY, + }; + parent_world.inverse().compose(&world) + } else { + original.local + }; + local.validate().map_err(|error| { + GraphError::code(ErrorCode::InvalidTransform, format!("{message}: {error}")) + })?; + Ok((parent, local)) +} + +fn copy_place_input(draft: &mut Candidate, id: &str, original: &Node) { + if let Some(node) = draft.nodes.get_mut(id) { + node.place_input = original.place_input; + } +} diff --git a/main/opengeometry/src/world_graph/journal.rs b/main/opengeometry/src/world_graph/journal.rs new file mode 100644 index 0000000..3dd3f50 --- /dev/null +++ b/main/opengeometry/src/world_graph/journal.rs @@ -0,0 +1,252 @@ +use super::change_log::{ChangeSet, NodeChange}; +use super::error::{ErrorCode, GraphError}; +use super::graph::{LiveMark, WorldGraph}; +use super::node::{MarkId, Node}; +use super::shape_store::{Shape, ShapeId}; +use super::tree_walk::descendants; +use std::collections::{BTreeMap, BTreeSet, HashSet}; +use std::sync::Arc; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct MarkStats { + pub live_marks: usize, + pub retained_revisions: usize, +} + +impl WorldGraph { + pub fn mark(&mut self) -> Result { + let id = MarkId(self.next_mark); + self.next_mark = self + .next_mark + .checked_add(1) + .ok_or_else(|| GraphError::code(ErrorCode::LimitExceeded, "mark counter overflow"))?; + self.marks.push(LiveMark { + id, + position: self.journal.len(), + }); + Ok(id) + } + + pub fn mark_stats(&self) -> MarkStats { + let mut retained = HashSet::new(); + for entry in &self.journal { + for shape in entry.shapes.values().flatten() { + retained.insert(Arc::as_ptr(&shape.brep) as usize); + } + } + MarkStats { + live_marks: self.marks.len(), + retained_revisions: retained.len(), + } + } + + pub fn release(&mut self, mark: MarkId) -> Result<(), GraphError> { + let index = self + .marks + .iter() + .position(|item| item.id == mark) + .ok_or_else(|| GraphError::code(ErrorCode::InvalidMark, "mark is not live"))?; + self.marks.remove(index); + if let Some(earliest) = self.marks.iter().map(|item| item.position).min() { + if earliest > 0 { + self.journal.drain(..earliest); + for item in &mut self.marks { + item.position -= earliest; + } + } + } else { + self.journal.clear(); + } + Ok(()) + } + + pub fn rollback(&mut self, mark: MarkId) -> Result { + let index = self + .marks + .iter() + .position(|item| item.id == mark) + .ok_or_else(|| GraphError::code(ErrorCode::InvalidMark, "mark is not live"))?; + let position = self.marks[index].position; + if position > self.journal.len() { + return Err(GraphError::code( + ErrorCode::InvalidMark, + "journal position is invalid", + )); + } + let revision = self + .revision + .checked_add(1) + .ok_or_else(|| GraphError::code(ErrorCode::LimitExceeded, "graph revision overflow"))?; + let old_nodes = self.nodes.clone(); + let old_shapes = self.shapes.shapes.clone(); + self.unwind_journal(position)?; + self.marks.truncate(index + 1); + self.world_cache.borrow_mut().clear(); + let changes = self.rollback_changes(revision, &old_nodes, &old_shapes); + self.revision = revision; + self.changes.push(changes.clone()); + Ok(changes) + } + + fn unwind_journal(&mut self, position: usize) -> Result<(), GraphError> { + while self.journal.len() > position { + let entry = self.journal.pop().ok_or_else(|| { + GraphError::code(ErrorCode::InvalidMark, "journal position is invalid") + })?; + for (id, before) in entry.nodes { + match before { + Some(node) => { + self.nodes.insert(id, node); + } + None => { + self.nodes.remove(&id); + } + } + } + for (id, before) in entry.shapes { + match before { + Some(shape) => { + self.shapes.shapes.insert(id, shape); + } + None => { + self.shapes.shapes.remove(&id); + } + } + } + } + Ok(()) + } + + fn rollback_changes( + &self, + revision: u64, + old_nodes: &BTreeMap, + old_shapes: &BTreeMap, + ) -> ChangeSet { + let mut changes = ChangeSet { + revision, + added: Vec::new(), + changed: Vec::new(), + removed: Vec::new(), + }; + let mut affected = BTreeSet::new(); + let ids: BTreeSet<_> = old_nodes.keys().chain(self.nodes.keys()).cloned().collect(); + for id in ids { + let before = old_nodes.get(&id); + let after = self.nodes.get(&id); + if before != after { + affected.extend(descendants(old_nodes, &id)); + affected.extend(descendants(&self.nodes, &id)); + match (before, after) { + (None, Some(node)) => changes + .added + .push(Self::change_for(node, &self.shapes.shapes)), + (Some(node), None) => changes.removed.push(Self::change_for(node, old_shapes)), + (Some(old), Some(new)) if old.generation != new.generation => { + changes.removed.push(Self::change_for(old, old_shapes)); + changes + .added + .push(Self::change_for(new, &self.shapes.shapes)); + } + (Some(_), Some(node)) => changes + .changed + .push(Self::change_for(node, &self.shapes.shapes)), + (None, None) => {} + } + } + } + let shape_ids: BTreeSet = old_shapes + .keys() + .chain(self.shapes.shapes.keys()) + .cloned() + .collect(); + for id in shape_ids { + let old_revision = old_shapes.get(&id).map(|shape| shape.revision); + let new_revision = self.shapes.shapes.get(&id).map(|shape| shape.revision); + if old_revision != new_revision { + affected.extend( + self.nodes + .values() + .filter(|node| node.shape.as_ref() == Some(&id)) + .map(|node| node.og_id.clone()), + ); + } + } + for id in affected { + if changes.added.iter().any(|item| item.og_id == id) + || changes.removed.iter().any(|item| item.og_id == id) + || changes.changed.iter().any(|item| item.og_id == id) + { + continue; + } + if let Some(node) = self.nodes.get(&id) { + changes + .changed + .push(Self::change_for(node, &self.shapes.shapes)); + } + } + changes + } + + pub fn changes_since(&self, revision: u64) -> Result { + if revision > self.revision { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "revision is in the future", + )); + } + #[derive(Clone, Copy)] + enum Kind { + Added, + Changed, + Removed, + } + let mut compact: BTreeMap<(String, u32, u32), (Kind, NodeChange)> = BTreeMap::new(); + for change_set in self + .changes + .iter() + .filter(|change| change.revision > revision) + { + for (kind, entries) in [ + (Kind::Added, &change_set.added), + (Kind::Changed, &change_set.changed), + (Kind::Removed, &change_set.removed), + ] { + for entry in entries { + let key = (entry.og_id.clone(), entry.handle, entry.generation); + match (compact.get(&key).map(|(kind, _)| *kind), kind) { + (Some(Kind::Added), Kind::Removed) => { + compact.remove(&key); + } + (Some(Kind::Added), Kind::Changed) => { + compact.insert(key, (Kind::Added, entry.clone())); + } + (Some(Kind::Removed), Kind::Added) => { + compact.insert(key, (Kind::Changed, entry.clone())); + } + (Some(Kind::Changed), Kind::Removed) => { + compact.insert(key, (Kind::Removed, entry.clone())); + } + _ => { + compact.insert(key, (kind, entry.clone())); + } + } + } + } + } + let mut result = ChangeSet { + revision: self.revision, + added: Vec::new(), + changed: Vec::new(), + removed: Vec::new(), + }; + for (_, (kind, entry)) in compact { + match kind { + Kind::Added => result.added.push(entry), + Kind::Changed => result.changed.push(entry), + Kind::Removed => result.removed.push(entry), + } + } + Ok(result) + } +} diff --git a/main/opengeometry/src/world_graph/mod.rs b/main/opengeometry/src/world_graph/mod.rs new file mode 100644 index 0000000..cc6f97c --- /dev/null +++ b/main/opengeometry/src/world_graph/mod.rs @@ -0,0 +1,33 @@ +mod assembly; +mod bounds; +mod candidate; +mod change_log; +mod commit; +mod creating; +mod error; +mod export; +mod graph; +mod hierarchy; +mod instancing; +mod journal; +mod modifying; +mod node; +mod operands; +mod placement; +mod primitive; +mod queries; +mod shape_store; +mod tree_walk; + +pub use change_log::{ChangeSet, NodeChange}; +pub use creating::CreatingOperation; +pub use error::{ErrorCode, ErrorContext, ErrorDetails, GraphError}; +pub use export::StepOptions; +pub use graph::WorldGraph; +pub use instancing::CopyOptions; +pub use journal::MarkStats; +pub use modifying::ModifyingOperation; +pub use node::{EditScope, MarkId, Node}; +pub use placement::{Placement, Transform}; +pub use primitive::{CreateOptions, Plane, Primitive}; +pub use shape_store::{Reserved, Shape}; diff --git a/main/opengeometry/src/world_graph/modifying.rs b/main/opengeometry/src/world_graph/modifying.rs new file mode 100644 index 0000000..180182d --- /dev/null +++ b/main/opengeometry/src/world_graph/modifying.rs @@ -0,0 +1,199 @@ +use super::change_log::ChangeSet; +use super::error::{ErrorCode, ErrorContext, ErrorDetails, GraphError}; +use super::graph::WorldGraph; +use super::node::EditScope; +use super::placement::is_identity; +use super::shape_store::{Shape, ShapeId}; +use crate::brep::{placed, BodyType, BrepEnvelope, GeometryError, GeometryQuality}; +use crate::operations::modifying::boolean::{ + multi_tool_boolean, BooleanOp, BooleanResult, MultiToolOutcome, +}; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum ModifyingOperation { + Union, + Subtract, + Intersect, +} + +struct Operated { + result: BooleanResult, + handlers: Vec, + revision: u64, + shape_id: ShapeId, +} + +struct ToolFailure { + tool_index: usize, + error: GeometryError, + handlers: Vec, +} + +impl WorldGraph { + pub fn report(&self, og_id: &str) -> Result, GraphError> { + Ok(self.shape(og_id)?.report.as_ref()) + } + + pub fn operate( + &mut self, + target: &str, + operation: ModifyingOperation, + tools: &[String], + scope: EditScope, + ) -> Result { + let Operated { + result, + handlers, + revision, + shape_id, + } = self + .operated(target, operation, tools, scope) + .map_err(|error| error.in_context(ErrorContext::Operate))?; + let report = serde_json::json!({ "report": result.report, "handlers": handlers, "revision": revision }); + self.mutate(|draft| { + let shape = draft.shapes.get_mut(&shape_id).ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "target shape is missing") + })?; + shape.brep = Arc::new(result.brep); + shape.revision = revision; + shape.edge_keys.clear(); + shape.report = Some(report); + Ok(()) + }) + .map_err(|error| error.in_context(ErrorContext::Operate)) + } + + fn operated( + &self, + target: &str, + operation: ModifyingOperation, + tools: &[String], + scope: EditScope, + ) -> Result { + if tools.is_empty() || tools.len() > 100 { + return Err(GraphError::code( + ErrorCode::LimitExceeded, + "boolean requires 1..=100 tools", + )); + } + let target_shape = self.ensure_editable(target, scope)?; + validate_solid(&target_shape.brep)?; + let shape_id = + self.node(target)?.shape.clone().ok_or_else(|| { + GraphError::code(ErrorCode::InvalidOperand, "target is not a body") + })?; + let revision = self.reserve(0, std::slice::from_ref(&shape_id))?.revisions[0].1; + let mapped = self.mapped_tools(target, tools, &shape_id)?; + let (mut result, handlers) = kernel_result(target_shape, &mapped, operation, &shape_id) + .map_err(|failure| failure.graph_error(target, tools))?; + if result.brep.solids.is_empty() && result.brep.topology.faces.is_empty() { + return Err(GraphError::code( + ErrorCode::EmptyResult, + "boolean result is empty", + )); + } + validate_solid(&result.brep)?; + result.brep.revision = revision; + result.brep.validate()?; + Ok(Operated { + result, + handlers, + revision, + shape_id, + }) + } + + fn mapped_tools( + &self, + target: &str, + tools: &[String], + shape_id: &ShapeId, + ) -> Result, GraphError> { + let mut mapped = Vec::with_capacity(tools.len()); + for tool in tools { + let tool_node = self.node(tool)?; + if tool == target || tool_node.shape.as_ref() == Some(shape_id) { + return Err(GraphError::code( + ErrorCode::ToolSharesTargetShape, + "tool shares target shape", + )); + } + let shape = self.shape(tool)?; + validate_solid(&shape.brep)?; + let transform = self.relative_placement(tool, target)?; + if transform.scale.to_bits() != 1.0f64.to_bits() { + return Err(GraphError::code( + ErrorCode::InvalidOperand, + "tool has a relative scale", + )); + } + mapped.push(if is_identity(transform) { + (*shape.brep).clone() + } else { + placed(&shape.brep, transform.frame, 1.0)? + }); + } + Ok(mapped) + } +} + +fn validate_solid(body: &BrepEnvelope) -> Result<(), GraphError> { + if body.body_type()? != BodyType::Solid { + return Err(GraphError::code( + ErrorCode::InvalidOperand, + "boolean operand is not a solid", + )); + } + if !matches!(body.quality, GeometryQuality::Analytic) { + return Err(GraphError::code( + ErrorCode::UnsupportedGeometry, + "boolean operand is not analytic", + )); + } + Ok(()) +} + +impl ToolFailure { + fn graph_error(self, target: &str, tools: &[String]) -> GraphError { + let source = GraphError::Geometry(self.error); + GraphError::with_details( + source.error_code(), + source.message(), + ErrorDetails::Operate { + handlers: self.handlers, + tool_index: self.tool_index, + og_ids: std::iter::once(target.to_string()) + .chain(tools.iter().cloned()) + .collect(), + }, + ) + } +} + +fn kernel_result( + target_shape: &Shape, + mapped: &[BrepEnvelope], + operation: ModifyingOperation, + shape_id: &ShapeId, +) -> Result<(BooleanResult, Vec), ToolFailure> { + let kernel_op = match operation { + ModifyingOperation::Union => BooleanOp::Union, + ModifyingOperation::Subtract => BooleanOp::Subtraction, + ModifyingOperation::Intersect => BooleanOp::Intersection, + }; + let MultiToolOutcome { + result, + handlers, + tool_index, + } = multi_tool_boolean(&target_shape.brep, mapped, kernel_op, shape_id.clone()); + match result { + Ok(result) => Ok((result, handlers)), + Err(error) => Err(ToolFailure { + tool_index, + error, + handlers, + }), + } +} diff --git a/main/opengeometry/src/world_graph/node.rs b/main/opengeometry/src/world_graph/node.rs new file mode 100644 index 0000000..ff54230 --- /dev/null +++ b/main/opengeometry/src/world_graph/node.rs @@ -0,0 +1,40 @@ +use super::shape_store::ShapeId; +use crate::brep::Similarity3; +use crate::math::Point3; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub(crate) enum NodeKind { + SystemAssembly, + Body, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum EditScope { + Node, + AllInstances, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct Node { + pub og_id: String, + pub handle: u32, + pub generation: u32, + pub(crate) parent: Option, + pub(crate) children: Vec, + pub local: Similarity3, + pub shape: Option, + pub(crate) kind: NodeKind, + pub(crate) place_input: Option, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) struct PlaceInput { + pub(crate) origin: Point3, + pub(crate) x_direction: Point3, + pub(crate) normal: Point3, + pub(crate) scale: f64, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct MarkId(pub(super) u64); diff --git a/main/opengeometry/src/world_graph/operands.rs b/main/opengeometry/src/world_graph/operands.rs new file mode 100644 index 0000000..858bb4f --- /dev/null +++ b/main/opengeometry/src/world_graph/operands.rs @@ -0,0 +1,148 @@ +use super::error::{ErrorCode, GraphError}; +use super::graph::WorldGraph; +use crate::brep::{CurveGeometry, EdgeGeometry, Similarity3}; +use crate::math::Point3; +use crate::operations::creating::{mapped_frame, ProfileLoop}; + +pub(super) fn path_points( + graph: &WorldGraph, + path: &str, + target: &str, +) -> Result, GraphError> { + let mapping = graph.relative_placement(path, target)?; + if mapping.scale.to_bits() != 1.0f64.to_bits() { + return Err(GraphError::code( + ErrorCode::InvalidOperand, + "path has a relative scale", + )); + } + let brep = graph.brep(path)?; + let wire = brep + .topology + .wires + .first() + .ok_or_else(|| GraphError::code(ErrorCode::InvalidOperand, "path is not a wire"))?; + if wire.is_closed || brep.topology.wires.len() != 1 { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "path must be one open wire", + )); + } + let mut result = Vec::new(); + let mut cursor = wire.start_halfedge; + for _ in 0..brep.topology.halfedges.len() { + let halfedge = &brep.topology.halfedges[cursor as usize]; + let edge = &brep.topology.edges[halfedge.edge as usize]; + let EdgeGeometry::Curve { curve, .. } = edge.geometry else { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "path has a collapsed edge", + )); + }; + if !matches!( + brep.geometry.curves[curve as usize], + CurveGeometry::Line { .. } + ) { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "path has a curved edge", + )); + } + result.push(mapping.apply_point(brep.topology.vertices[halfedge.from as usize].position)); + if halfedge.next.is_none() { + result.push(mapping.apply_point(brep.topology.vertices[halfedge.to as usize].position)); + break; + } + cursor = halfedge.next.ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "path ends unexpectedly") + })?; + } + if result.len() < 2 { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "path is too short", + )); + } + Ok(result) +} + +pub(super) fn profile_loop( + graph: &WorldGraph, + profile: &str, + target: &str, +) -> Result { + let mapping = graph.relative_placement(profile, target)?; + profile_loop_mapped(graph, profile, mapping) +} + +fn profile_loop_mapped( + graph: &WorldGraph, + profile: &str, + mapping: Similarity3, +) -> Result { + if mapping.scale.to_bits() != 1.0f64.to_bits() { + return Err(GraphError::code( + ErrorCode::InvalidOperand, + "profile has a relative scale", + )); + } + let brep = graph.brep(profile)?; + let wire = brep + .topology + .wires + .first() + .ok_or_else(|| GraphError::code(ErrorCode::InvalidOperand, "profile is not a wire"))?; + if !wire.is_closed || brep.topology.wires.len() != 1 { + return Err(invalid("profile must be one closed wire")); + } + let mut points = Vec::new(); + let mut cursor = wire.start_halfedge; + for _ in 0..brep.topology.halfedges.len() { + let halfedge = brep + .topology + .halfedges + .get(cursor as usize) + .ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "wire halfedge is missing") + })?; + let edge = &brep.topology.edges[halfedge.edge as usize]; + if let EdgeGeometry::Curve { curve, range } = edge.geometry { + if let CurveGeometry::Circle { frame, radius } = brep.geometry.curves[curve as usize] { + if brep.topology.halfedges.len() == 1 + && (range.hi - range.lo - std::f64::consts::TAU).abs() <= 1e-12 + { + return Ok(ProfileLoop::Circle { + frame: mapped_frame(frame, mapping), + radius, + }); + } + } + if !matches!( + brep.geometry.curves[curve as usize], + CurveGeometry::Line { .. } + ) { + return Err(GraphError::code( + ErrorCode::UnsupportedGeometry, + "profile curve is not supported", + )); + } + } else { + return Err(invalid("profile contains a collapsed edge")); + } + points.push(mapping.apply_point(brep.topology.vertices[halfedge.from as usize].position)); + cursor = halfedge + .next + .ok_or_else(|| GraphError::code(ErrorCode::InvalidTopology, "wire chain is open"))?; + if cursor == wire.start_halfedge { + break; + } + } + if points.len() < 3 { + return Err(invalid("profile has fewer than three vertices")); + } + Ok(ProfileLoop::Lines(points)) +} + +fn invalid(message: impl Into) -> GraphError { + GraphError::code(ErrorCode::InvalidParameter, message) +} diff --git a/main/opengeometry/src/world_graph/placement.rs b/main/opengeometry/src/world_graph/placement.rs new file mode 100644 index 0000000..086955c --- /dev/null +++ b/main/opengeometry/src/world_graph/placement.rs @@ -0,0 +1,342 @@ +use super::change_log::ChangeSet; +use super::error::{ErrorCode, ErrorContext, GraphError}; +use super::graph::WorldGraph; +use super::node::PlaceInput; +use crate::brep::{Frame3, Similarity3, GROUND}; +use crate::math::{add, scale, sub, Point3}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Placement { + pub origin: Point3, + pub x_direction: Point3, + pub normal: Point3, + pub scale: f64, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all_fields = "camelCase", deny_unknown_fields)] +pub enum Transform { + Translate { + offset: Point3, + }, + Rotate { + axis: Point3, + degrees: f64, + pivot: Option, + }, + Scale { + factor: f64, + pivot: Option, + }, + Place { + origin: Option, + x_direction: Option, + normal: Option, + scale: Option, + }, +} + +pub(super) fn is_identity(transform: Similarity3) -> bool { + same_bits(transform, Similarity3::IDENTITY) +} + +fn same_bits(a: Similarity3, b: Similarity3) -> bool { + entries(a) + .iter() + .zip(entries(b)) + .all(|(a, b)| a.to_bits() == b.to_bits()) +} + +fn entries(transform: Similarity3) -> [f64; 13] { + [ + transform.frame.origin[0], + transform.frame.origin[1], + transform.frame.origin[2], + transform.frame.x[0], + transform.frame.x[1], + transform.frame.x[2], + transform.frame.y[0], + transform.frame.y[1], + transform.frame.y[2], + transform.frame.z[0], + transform.frame.z[1], + transform.frame.z[2], + transform.scale, + ] +} + +impl Transform { + fn apply(self, local: Similarity3) -> Result { + local + .validate() + .map_err(|error| invalid_transform(format!("invalid current placement: {error}")))?; + let result = match self { + Self::Translate { offset } => translated_local(local, offset)?, + Self::Rotate { + axis, + degrees, + pivot, + } => rotated_local(local, axis, degrees, pivot)?, + Self::Scale { factor, pivot } => scaled_local(local, factor, pivot)?, + Self::Place { + origin, + x_direction, + normal, + scale, + } => from_place(place_input(origin, x_direction, normal, scale))?, + }; + result + .validate() + .map_err(|error| invalid_transform(format!("invalid transform result: {error}")))?; + Ok(result) + } +} + +fn invalid_transform(message: impl Into) -> GraphError { + GraphError::code(ErrorCode::InvalidTransform, message) +} + +fn translated_local(local: Similarity3, offset: Point3) -> Result { + if offset.iter().any(|value| !value.is_finite()) { + return Err(invalid_transform("translation must be finite")); + } + Ok(Similarity3 { + frame: Frame3 { + origin: add(local.frame.origin, offset), + ..local.frame + }, + ..local + }) +} + +fn rotated_local( + local: Similarity3, + axis: Point3, + degrees: f64, + pivot: Option, +) -> Result { + if !degrees.is_finite() { + return Err(invalid_transform("rotation must be finite")); + } + let pivot = pivot.unwrap_or(local.frame.origin); + if pivot.iter().any(|value| !value.is_finite()) { + return Err(invalid_transform("pivot must be finite")); + } + let reduced = degrees % 360.0; + let rotation = match quarter_turn(reduced) { + Some((cos, sin)) => Similarity3::from_axis_cos_sin([0.0; 3], axis, cos, sin, 1.0), + None => Similarity3::from_axis_angle([0.0; 3], axis, reduced.to_radians(), 1.0), + } + .map_err(|error| invalid_transform(format!("invalid rotation: {error}")))?; + let origin = sub(pivot, rotation.apply_vector(pivot)); + Ok(Similarity3 { + frame: Frame3 { + origin, + ..rotation.frame + }, + ..rotation + } + .compose(&local)) +} + +fn quarter_turn(degrees: f64) -> Option<(f64, f64)> { + [ + (0.0, (1.0, 0.0)), + (90.0, (0.0, 1.0)), + (180.0, (-1.0, 0.0)), + (270.0, (0.0, -1.0)), + (-90.0, (0.0, -1.0)), + (-180.0, (-1.0, 0.0)), + (-270.0, (0.0, 1.0)), + ] + .into_iter() + .find(|(turn, _)| *turn == degrees) + .map(|(_, cos_sin)| cos_sin) +} + +fn scaled_local( + local: Similarity3, + factor: f64, + pivot: Option, +) -> Result { + if !factor.is_finite() || factor <= 0.0 { + return Err(invalid_transform("scale must be finite and positive")); + } + let pivot = pivot.unwrap_or(local.frame.origin); + if pivot.iter().any(|value| !value.is_finite()) { + return Err(invalid_transform("pivot must be finite")); + } + Ok(Similarity3 { + frame: Frame3 { + origin: add(pivot, scale(sub(local.frame.origin, pivot), factor)), + ..local.frame + }, + scale: local.scale * factor, + }) +} + +fn place_input( + origin: Option, + x_direction: Option, + normal: Option, + scale: Option, +) -> PlaceInput { + PlaceInput { + origin: origin.unwrap_or([0.0; 3]), + x_direction: x_direction.unwrap_or([1.0, 0.0, 0.0]), + normal: normal.unwrap_or([0.0, 1.0, 0.0]), + scale: scale.unwrap_or(1.0), + } +} + +fn from_place(input: PlaceInput) -> Result { + let frame = Frame3::from_axis(input.origin, input.normal, input.x_direction) + .map_err(|error| invalid_transform(format!("invalid placement frame: {error}")))?; + let local = Similarity3 { + frame, + scale: input.scale, + } + .compose( + &Similarity3 { + frame: GROUND, + scale: 1.0, + } + .inverse(), + ); + local + .validate() + .map_err(|error| invalid_transform(format!("invalid placement: {error}")))?; + Ok(local) +} + +impl WorldGraph { + pub fn transform( + &mut self, + og_id: &str, + transform: Transform, + ) -> Result { + self.mutate(|draft| { + let descendants = draft.descendants(og_id)?; + let node = draft + .nodes + .get_mut(og_id) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, og_id))?; + node.local = transform.apply(node.local)?; + if let Transform::Place { + origin, + x_direction, + normal, + scale, + } = transform + { + node.place_input = Some(place_input(origin, x_direction, normal, scale)); + } + draft.affected.extend(descendants); + Ok(()) + }) + .map_err(|error| error.in_context(ErrorContext::Transform)) + } + + pub fn placement(&self, og_id: &str) -> Result { + let node = self.node(og_id)?; + if let Some(input) = node.place_input { + if same_bits(from_place(input)?, node.local) { + return Ok(Placement { + origin: input.origin, + x_direction: input.x_direction, + normal: input.normal, + scale: input.scale, + }); + } + } + Ok(placement_form(node.local)) + } + + pub fn world_placement(&self, og_id: &str) -> Result { + if let Some(cached) = self.world_cache.borrow().get(og_id) { + return Ok(*cached); + } + let mut chain = Vec::new(); + let mut cursor = Some(og_id.to_string()); + let mut result = Similarity3::IDENTITY; + while let Some(current) = cursor { + if let Some(cached) = self.world_cache.borrow().get(¤t) { + result = *cached; + break; + } + let node = self.node(¤t)?; + chain.push(current); + cursor = node.parent.clone(); + } + while let Some(current) = chain.pop() { + let node = self.node(¤t)?; + result = result.compose(&node.local); + result + .validate() + .map_err(|error| invalid_transform(format!("invalid world placement: {error}")))?; + self.world_cache.borrow_mut().insert(current, result); + } + Ok(result) + } + + pub fn world_placement_form(&self, og_id: &str) -> Result { + Ok(placement_form(self.world_placement(og_id)?)) + } + + pub fn world_matrix(&self, og_id: &str) -> Result<[f64; 16], GraphError> { + Ok(self.world_placement(og_id)?.to_column_major()) + } + + pub fn relative_placement( + &self, + source: &str, + target: &str, + ) -> Result { + let ancestors = |id: &str| -> Result, GraphError> { + let mut path = Vec::new(); + let mut current = Some(id.to_string()); + while let Some(og_id) = current { + let node = self.node(&og_id)?; + current = node.parent.clone(); + path.push(og_id); + } + Ok(path) + }; + let mut a = ancestors(source)?; + let mut b = ancestors(target)?; + while a.last().is_some() && a.last() == b.last() { + a.pop(); + b.pop(); + } + let mut source_to_lca = Similarity3::IDENTITY; + for id in a.iter().rev() { + source_to_lca = source_to_lca.compose(&self.node(id)?.local); + } + let mut target_to_lca = Similarity3::IDENTITY; + for id in b.iter().rev() { + target_to_lca = target_to_lca.compose(&self.node(id)?.local); + } + let result = target_to_lca.inverse().compose(&source_to_lca); + result + .validate() + .map_err(|error| invalid_transform(format!("invalid relative placement: {error}")))?; + Ok(result) + } +} + +fn placement_form(transform: Similarity3) -> Placement { + let frame = transform + .compose(&Similarity3 { + frame: GROUND, + scale: 1.0, + }) + .frame; + Placement { + origin: frame.origin, + x_direction: frame.x, + normal: frame.z, + scale: transform.scale, + } +} diff --git a/main/opengeometry/src/world_graph/primitive.rs b/main/opengeometry/src/world_graph/primitive.rs new file mode 100644 index 0000000..69a9b12 --- /dev/null +++ b/main/opengeometry/src/world_graph/primitive.rs @@ -0,0 +1,317 @@ +use super::candidate::IdKind; +use super::change_log::ChangeSet; +use super::error::{ErrorCode, ErrorContext, GraphError}; +use super::graph::WorldGraph; +use super::node::EditScope; +use crate::brep::{Accuracy, BodyType, BrepEnvelope, CurveGeometry, Frame3, Similarity3, GROUND}; +use crate::math::{norm, sub, Point3}; +use crate::primitives; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "kind", deny_unknown_fields)] +pub enum Primitive { + Cuboid { width: f64, height: f64, depth: f64 }, + Cylinder { radius: f64, height: f64 }, + Rectangle { width: f64, breadth: f64 }, + Circle { radius: f64 }, + Polyline { points: Vec, closed: bool }, +} + +#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct Plane { + pub origin: Option, + pub normal: Option, + pub x_direction: Option, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct CreateOptions { + pub og_id: Option, + pub parent: Option, + pub plane: Option, + pub body_type: Option, +} + +impl Primitive { + fn body_type(&self) -> BodyType { + match self { + Self::Cuboid { .. } | Self::Cylinder { .. } => BodyType::Solid, + Self::Rectangle { .. } | Self::Circle { .. } | Self::Polyline { .. } => BodyType::Wire, + } + } +} + +impl WorldGraph { + pub fn create_primitive( + &mut self, + primitive: Primitive, + options: CreateOptions, + ) -> Result<(String, ChangeSet), GraphError> { + self.try_create_primitive(primitive, options) + .map_err(|error| error.in_context(ErrorContext::Create)) + } + + fn try_create_primitive( + &mut self, + primitive: Primitive, + options: CreateOptions, + ) -> Result<(String, ChangeSet), GraphError> { + let body_type = primitive.body_type(); + check_body_type(options.body_type, body_type)?; + let kind = IdKind::of_body(body_type)?; + let accuracy = self.accuracy; + let local = initial_placement(options.plane)?; + let reserved = self.reserve(1, &[])?; + let mut created = None; + let changes = self.mutate(|draft| { + if let Some(parent) = &options.parent { + if !draft.nodes.contains_key(parent) { + return Err(GraphError::code(ErrorCode::UnknownNode, parent)); + } + } + let id = draft.new_og_id(options.og_id.as_deref(), kind)?; + let shape_id = draft.new_shape_id()?; + if reserved.shape_ids.first() != Some(&shape_id) { + return Err(GraphError::code( + ErrorCode::RevisionConflict, + "reserved shape id changed", + )); + } + let (brep, edge_keys) = build_primitive(shape_id.clone(), primitive, accuracy)?; + if brep.body_type()? != body_type { + return Err(GraphError::code( + ErrorCode::BodyTypeMismatch, + "primitive produced the wrong body type", + )); + } + draft.add_body(&id, shape_id, brep, edge_keys, &options.parent, local)?; + created = Some(id); + Ok(()) + })?; + Ok(( + created.ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "created node is missing") + })?, + changes, + )) + } + + pub fn rebuild_primitive( + &mut self, + og_id: &str, + primitive: Primitive, + scope: EditScope, + ) -> Result { + self.try_rebuild_primitive(og_id, primitive, scope) + .map_err(|error| error.in_context(ErrorContext::Rebuild)) + } + + fn try_rebuild_primitive( + &mut self, + og_id: &str, + primitive: Primitive, + scope: EditScope, + ) -> Result { + let previous = self.ensure_editable(og_id, scope)?; + let expected_type = previous.brep.body_type()?; + let previous_revision = previous.revision; + let shape_id = self + .node(og_id)? + .shape + .clone() + .ok_or_else(|| GraphError::code(ErrorCode::InvalidOperand, "node is not a body"))?; + let reserved = self.reserve(0, std::slice::from_ref(&shape_id))?; + let next_revision = reserved.revisions[0].1; + let accuracy = self.accuracy; + let actual_type = primitive.body_type(); + self.mutate(|candidate| { + let (mut brep, edge_keys) = build_primitive(shape_id.clone(), primitive, accuracy)?; + if actual_type != expected_type { + return Err(GraphError::code( + ErrorCode::BodyTypeMismatch, + "rebuild changed body type", + )); + } + brep.revision = next_revision; + brep.validate()?; + let shape = candidate.shapes.get_mut(&shape_id).ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "shape reference is missing") + })?; + if shape.revision != previous_revision { + return Err(GraphError::code( + ErrorCode::RevisionConflict, + "shape revision changed before commit", + )); + } + shape.brep = Arc::new(brep); + shape.revision = next_revision; + shape.edge_keys = edge_keys; + shape.report = None; + Ok(()) + }) + } +} + +pub(super) fn check_body_type( + expected: Option, + actual: BodyType, +) -> Result<(), GraphError> { + match expected { + Some(expected) if expected != actual => Err(GraphError::code( + ErrorCode::BodyTypeMismatch, + format!("expected {expected:?}, got {actual:?}"), + )), + _ => Ok(()), + } +} + +pub(super) fn check_no_plane(plane: Option) -> Result<(), GraphError> { + if plane.is_some() { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "plane applies only to primitives", + )); + } + Ok(()) +} + +fn initial_placement(plane: Option) -> Result { + let Some(plane) = plane else { + return Ok(Similarity3::IDENTITY); + }; + let origin = plane.origin.unwrap_or([0.0; 3]); + let normal = plane.normal.unwrap_or([0.0, 1.0, 0.0]); + let x_direction = plane.x_direction.unwrap_or([1.0, 0.0, 0.0]); + let frame = Frame3::from_axis(origin, normal, x_direction).map_err(|error| { + GraphError::code( + ErrorCode::InvalidParameter, + format!("invalid primitive plane: {error}"), + ) + })?; + let placement = Similarity3 { frame, scale: 1.0 }.compose( + &Similarity3 { + frame: GROUND, + scale: 1.0, + } + .inverse(), + ); + placement.validate().map_err(|error| { + GraphError::code( + ErrorCode::InvalidTransform, + format!("invalid primitive plane placement: {error}"), + ) + })?; + Ok(placement) +} + +fn build_primitive( + shape_id: String, + primitive: Primitive, + accuracy: Accuracy, +) -> Result<(BrepEnvelope, Vec), GraphError> { + let result = match primitive { + Primitive::Cuboid { + width, + height, + depth, + } => { + for (name, value) in [("width", width), ("height", height), ("depth", depth)] { + required_size(value, accuracy.geometric, name)?; + } + let frame = Frame3 { + origin: [-width / 2.0, 0.0, depth / 2.0], + ..GROUND + }; + ( + primitives::cuboid(shape_id.clone(), frame, [width, depth, height], accuracy)?, + Vec::new(), + ) + } + Primitive::Cylinder { radius, height } => { + required_size(radius, accuracy.geometric, "radius")?; + required_size(height, accuracy.geometric, "height")?; + ( + primitives::cylinder(shape_id.clone(), GROUND, radius, height, accuracy)?, + Vec::new(), + ) + } + Primitive::Rectangle { width, breadth } => { + required_size(width, accuracy.geometric, "width")?; + required_size(breadth, accuracy.geometric, "breadth")?; + let (brep, keys) = primitives::rectangle_with_keys( + shape_id.clone(), + GROUND, + width, + breadth, + accuracy, + )?; + (brep, keys) + } + Primitive::Circle { radius } => { + required_size(radius, accuracy.geometric, "radius")?; + ( + primitives::arc_wire( + shape_id.clone(), + CurveGeometry::Circle { + frame: GROUND, + radius, + }, + 0.0, + std::f64::consts::TAU, + accuracy, + )?, + vec!["edge-0".into()], + ) + } + Primitive::Polyline { points, closed } => { + check_polyline(&points, closed, accuracy)?; + let (brep, keys) = + primitives::polyline_with_keys(shape_id.clone(), &points, closed, accuracy)?; + (brep, keys) + } + }; + Ok(result) +} + +fn required_size(value: f64, accuracy: f64, name: &str) -> Result<(), GraphError> { + if !value.is_finite() || value <= 4.0 * accuracy { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + format!("{name} must exceed four times geometric tolerance"), + )); + } + Ok(()) +} + +fn check_polyline(points: &[Point3], closed: bool, accuracy: Accuracy) -> Result<(), GraphError> { + if points.len() < if closed { 3 } else { 2 } { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "polyline has too few points", + )); + } + if points.iter().flatten().any(|value| !value.is_finite()) { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "polyline points must be finite", + )); + } + for index in 0..if closed { + points.len() + } else { + points.len() - 1 + } { + let next = (index + 1) % points.len(); + if norm(sub(points[next], points[index])) <= 4.0 * accuracy.geometric { + return Err(GraphError::code( + ErrorCode::InvalidParameter, + "polyline has a zero-length segment", + )); + } + } + Ok(()) +} diff --git a/main/opengeometry/src/world_graph/queries.rs b/main/opengeometry/src/world_graph/queries.rs new file mode 100644 index 0000000..a4b92eb --- /dev/null +++ b/main/opengeometry/src/world_graph/queries.rs @@ -0,0 +1,99 @@ +use super::error::{ErrorCode, ErrorDetails, GraphError}; +use super::graph::WorldGraph; +use super::node::{EditScope, Node}; +use super::shape_store::Shape; +use crate::brep::BrepEnvelope; +use crate::tessellation::display::{display_buffers, DisplayBuffers}; +use std::sync::Arc; + +impl WorldGraph { + pub fn node(&self, og_id: &str) -> Result<&Node, GraphError> { + self.nodes + .get(og_id) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, og_id)) + } + + pub fn shape(&self, og_id: &str) -> Result<&Shape, GraphError> { + let node = self.node(og_id)?; + let id = node + .shape + .as_ref() + .ok_or_else(|| GraphError::code(ErrorCode::InvalidOperand, "node is not a body"))?; + self.shapes.shapes.get(id).ok_or_else(|| { + GraphError::code(ErrorCode::InvalidTopology, "shape reference is missing") + }) + } + + pub fn brep(&self, og_id: &str) -> Result, GraphError> { + Ok(Arc::clone(&self.shape(og_id)?.brep)) + } + + pub fn snapshot(&self, shape_id: &str) -> Result, GraphError> { + let shape = self + .shapes + .shapes + .get(shape_id) + .ok_or_else(|| GraphError::code(ErrorCode::InvalidOperand, "shape is not live"))?; + let bytes = shape.brep.to_json()?.into_bytes(); + if bytes.len() > 64 * 1024 * 1024 { + return Err(GraphError::code( + ErrorCode::LimitExceeded, + "snapshot exceeds 64 MiB", + )); + } + Ok(bytes) + } + + pub fn buffers( + &self, + shape_id: &str, + bucket: f64, + max_triangles: usize, + ) -> Result { + let shape = self + .shapes + .shapes + .get(shape_id) + .ok_or_else(|| GraphError::code(ErrorCode::InvalidOperand, "shape is not live"))?; + Ok(display_buffers(&shape.brep, bucket, max_triangles)?) + } + + pub(crate) fn edge_keys(&self, og_id: &str) -> Result<&[String], GraphError> { + Ok(&self.shape(og_id)?.edge_keys) + } + + pub fn instance_count(&self, og_id: &str) -> Result { + Ok(self.shape(og_id)?.users) + } + + pub fn ensure_editable(&self, og_id: &str, scope: EditScope) -> Result<&Shape, GraphError> { + let shape = self.shape(og_id)?; + if shape.users > 1 && scope == EditScope::Node { + let shape_id = self.node(og_id)?.shape.as_ref(); + let sharing: Vec<_> = self + .nodes + .values() + .filter(|node| node.shape.as_ref() == shape_id) + .take(16) + .map(|node| node.og_id.clone()) + .collect(); + return Err(GraphError::with_details( + ErrorCode::SharedShape, + "shape has multiple instances", + ErrorDetails::SharedShape { + shape_id: shape_id.cloned(), + instance_count: shape.users, + sharing, + }, + )); + } + Ok(shape) + } + + pub fn node_by_handle(&self, handle: u32, generation: u32) -> Result<&Node, GraphError> { + self.nodes + .values() + .find(|node| node.handle == handle && node.generation == generation) + .ok_or_else(|| GraphError::code(ErrorCode::Disposed, "node handle is stale")) + } +} diff --git a/main/opengeometry/src/world_graph/shape_store.rs b/main/opengeometry/src/world_graph/shape_store.rs new file mode 100644 index 0000000..30d2858 --- /dev/null +++ b/main/opengeometry/src/world_graph/shape_store.rs @@ -0,0 +1,27 @@ +use crate::brep::BrepEnvelope; +use std::collections::BTreeMap; +use std::sync::Arc; + +pub(super) type ShapeId = String; + +#[derive(Clone, Debug)] +pub struct Shape { + pub brep: Arc, + pub revision: u64, + pub users: u32, + pub edge_keys: Vec, + pub report: Option, +} + +#[derive(Default)] +pub(super) struct ShapeStore { + pub(super) shapes: BTreeMap, + pub(super) high_water: BTreeMap, + pub(super) next_id: u64, +} + +#[derive(Clone, Debug)] +pub struct Reserved { + pub shape_ids: Vec, + pub(super) revisions: Vec<(ShapeId, u64)>, +} diff --git a/main/opengeometry/src/world_graph/tree_walk.rs b/main/opengeometry/src/world_graph/tree_walk.rs new file mode 100644 index 0000000..dd5a451 --- /dev/null +++ b/main/opengeometry/src/world_graph/tree_walk.rs @@ -0,0 +1,32 @@ +use super::candidate::Candidate; +use super::error::{ErrorCode, GraphError}; +use super::node::Node; +use std::collections::BTreeMap; + +pub(super) fn descendants(nodes: &BTreeMap, id: &str) -> Vec { + let mut stack = vec![id.to_string()]; + let mut result = Vec::new(); + while let Some(current) = stack.pop() { + if let Some(node) = nodes.get(¤t) { + result.push(current); + stack.extend(node.children.iter().rev().cloned()); + } + } + result +} + +impl Candidate { + pub(super) fn descendants(&self, id: &str) -> Result, GraphError> { + let mut stack = vec![id.to_string()]; + let mut result = Vec::new(); + while let Some(current) = stack.pop() { + let node = self + .nodes + .get(¤t) + .ok_or_else(|| GraphError::code(ErrorCode::UnknownNode, ¤t))?; + result.push(current); + stack.extend(node.children.iter().rev().cloned()); + } + Ok(result) + } +} diff --git a/main/opengeometry/test-support/Cargo.lock b/main/opengeometry/test-support/Cargo.lock new file mode 100644 index 0000000..8f1f9f0 --- /dev/null +++ b/main/opengeometry/test-support/Cargo.lock @@ -0,0 +1,262 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "earcutr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a09f1c588d65581e4f4ce412fa239dbc2aced01504891cd12360fc1c0e7776a0" +dependencies = [ + "num-traits", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "opengeometry" +version = "2.5.0" +dependencies = [ + "console_error_panic_hook", + "earcutr", + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", +] + +[[package]] +name = "opengeometry-test-support" +version = "2.5.0" +dependencies = [ + "opengeometry", + "serde_json", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/main/opengeometry/test-support/Cargo.toml b/main/opengeometry/test-support/Cargo.toml new file mode 100644 index 0000000..5e157fe --- /dev/null +++ b/main/opengeometry/test-support/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "opengeometry-test-support" +version = "2.5.0" +edition = "2021" +publish = false + +[dependencies] +opengeometry = { path = ".." } +serde_json = { version = "1.0", features = ["float_roundtrip"] } diff --git a/main/opengeometry/test-support/src/accuracy.rs b/main/opengeometry/test-support/src/accuracy.rs new file mode 100644 index 0000000..a5cf1ae --- /dev/null +++ b/main/opengeometry/test-support/src/accuracy.rs @@ -0,0 +1,10 @@ +use opengeometry::brep::Accuracy; + +pub fn accuracy(exchange: f64) -> Accuracy { + Accuracy { + geometric: 1e-8, + intersection: 1e-9, + tessellation: 0.01, + exchange, + } +} diff --git a/main/opengeometry/test-support/src/lib.rs b/main/opengeometry/test-support/src/lib.rs new file mode 100644 index 0000000..9d1f146 --- /dev/null +++ b/main/opengeometry/test-support/src/lib.rs @@ -0,0 +1,8 @@ +pub mod accuracy; +pub mod part21; +pub mod scenes; +pub mod stored; +pub mod tolerance; +pub(crate) mod vector; +pub mod volume; +pub mod world_graph; diff --git a/main/opengeometry/test-support/src/part21/context.rs b/main/opengeometry/test-support/src/part21/context.rs new file mode 100644 index 0000000..31f7282 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/context.rs @@ -0,0 +1,214 @@ +use super::document::Document; +use super::lexer::{fields, keyword, record, records, reference, references}; +use std::collections::BTreeMap; + +const LINKS: [(&str, usize); 5] = [ + ("APPLICATION_PROTOCOL_DEFINITION", 3), + ("PRODUCT_CONTEXT", 1), + ("PRODUCT_DEFINITION_CONTEXT", 1), + ("ADVANCED_BREP_SHAPE_REPRESENTATION", 2), + ("DEFINITIONAL_REPRESENTATION", 2), +]; + +const UNIT_KINDS: [&str; 3] = ["LENGTH_UNIT", "PLANE_ANGLE_UNIT", "SOLID_ANGLE_UNIT"]; + +struct Link { + owner: usize, + kind: &'static str, + target: Option, +} + +struct ContextEntities { + applications: Vec, + contexts2: Vec, + contexts3: Vec, + units: BTreeMap, + assigned_units: BTreeMap>, + assigned_uncertainties: BTreeMap>, + uncertainties: Vec<(usize, Option)>, + links: Vec, +} + +impl Document { + pub(super) fn check_contexts(&self) -> Result<(), String> { + let entities = self.context_entities()?; + check_context_presence(&entities)?; + check_context_links(&entities)?; + check_units(&entities) + } + + pub fn length_unit(&self) -> Result<&'static str, String> { + let length = length_unit_id(&self.context_entities()?)?; + let unit = record(self.entity(length)?, "SI_UNIT")? + .ok_or_else(|| format!("length unit #{length} is not an SI_UNIT"))?; + let args = fields(unit)?; + match (args[0].as_str(), args.get(1).map(String::as_str)) { + ("$", Some(".METRE.")) => Ok("metre"), + (".MILLI.", Some(".METRE.")) => Ok("millimetre"), + _ => Err(format!("unsupported length unit {unit}")), + } + } + + pub fn check_length_unit(&self, unit: &str) -> Result<(), String> { + let actual = self.length_unit()?; + if actual != unit { + return Err(format!("file length unit is {actual}, expected {unit}")); + } + Ok(()) + } + + fn context_entities(&self) -> Result { + let mut entities = ContextEntities { + applications: Vec::new(), + contexts2: Vec::new(), + contexts3: Vec::new(), + units: BTreeMap::new(), + assigned_units: BTreeMap::new(), + assigned_uncertainties: BTreeMap::new(), + uncertainties: Vec::new(), + links: Vec::new(), + }; + for (id, expression) in &self.entities { + for record in records(expression)? { + add_context_record(&mut entities, *id, record)?; + } + } + Ok(entities) + } +} + +fn add_context_record( + entities: &mut ContextEntities, + id: usize, + record: &str, +) -> Result<(), String> { + let name = keyword(record); + if let Some((kind, field)) = LINKS.into_iter().find(|(kind, _)| *kind == name) { + let target = link_target(&fields(record)?, field)?; + entities.links.push(Link { + owner: id, + kind, + target, + }); + return Ok(()); + } + if let Some(kind) = UNIT_KINDS.into_iter().find(|kind| *kind == name) { + entities.units.insert(id, kind); + return Ok(()); + } + match name { + "APPLICATION_CONTEXT" => entities.applications.push(id), + "UNCERTAINTY_MEASURE_WITH_UNIT" => { + let unit = link_target(&fields(record)?, 1)?; + entities.uncertainties.push((id, unit)); + } + "GEOMETRIC_REPRESENTATION_CONTEXT" => match fields(record)?[0].as_str() { + "2" => entities.contexts2.push(id), + "3" => entities.contexts3.push(id), + dimension => return Err(format!("#{id} has context dimension {dimension}")), + }, + "GLOBAL_UNIT_ASSIGNED_CONTEXT" => { + entities.assigned_units.insert(id, references(record)); + } + "GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT" => { + entities + .assigned_uncertainties + .insert(id, references(record)); + } + _ => {} + } + Ok(()) +} + +fn link_target(args: &[String], index: usize) -> Result, String> { + let field = args.get(index).ok_or("context link lacks its field")?; + if field == "$" { + return Ok(None); + } + reference(field).map(Some) +} + +fn check_context_presence(entities: &ContextEntities) -> Result<(), String> { + for (kind, ids) in [ + ("APPLICATION_CONTEXT", &entities.applications), + ("2D GEOMETRIC_REPRESENTATION_CONTEXT", &entities.contexts2), + ("3D GEOMETRIC_REPRESENTATION_CONTEXT", &entities.contexts3), + ] { + if ids.len() != 1 { + return Err(format!("expected exactly one {kind}, found {}", ids.len())); + } + } + for kind in [ + "APPLICATION_PROTOCOL_DEFINITION", + "PRODUCT_CONTEXT", + "PRODUCT_DEFINITION_CONTEXT", + ] { + if !entities.links.iter().any(|link| link.kind == kind) { + return Err(format!("missing {kind}")); + } + } + Ok(()) +} + +fn check_context_links(entities: &ContextEntities) -> Result<(), String> { + for link in &entities.links { + let (expected, name) = match link.kind { + "ADVANCED_BREP_SHAPE_REPRESENTATION" => (entities.contexts3[0], "the 3D context"), + "DEFINITIONAL_REPRESENTATION" => (entities.contexts2[0], "the 2D context"), + _ => (entities.applications[0], "the APPLICATION_CONTEXT"), + }; + if link.target != Some(expected) { + return Err(format!( + "{} #{} does not reference {name}", + link.kind, link.owner + )); + } + } + Ok(()) +} + +fn check_units(entities: &ContextEntities) -> Result<(), String> { + let length = length_unit_id(entities)?; + let [(uncertainty, unit)] = entities.uncertainties[..] else { + return Err(format!( + "expected exactly one UNCERTAINTY_MEASURE_WITH_UNIT, found {}", + entities.uncertainties.len() + )); + }; + if unit != Some(length) { + return Err(format!( + "UNCERTAINTY_MEASURE_WITH_UNIT #{uncertainty} does not name the length unit" + )); + } + let assigned = entities.assigned_uncertainties.get(&entities.contexts3[0]); + if assigned.map(Vec::as_slice) != Some(&[uncertainty]) { + return Err( + "GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT does not reference the UNCERTAINTY_MEASURE_WITH_UNIT" + .into(), + ); + } + Ok(()) +} + +fn length_unit_id(entities: &ContextEntities) -> Result { + let context = entities + .contexts3 + .first() + .ok_or("missing 3D GEOMETRIC_REPRESENTATION_CONTEXT")?; + let assigned = entities + .assigned_units + .get(context) + .map(Vec::as_slice) + .unwrap_or_default(); + let unit_kind = |id: &usize| entities.units.get(id).copied().unwrap_or_default(); + let mut kinds = assigned.iter().map(unit_kind).collect::>(); + kinds.sort_unstable(); + if kinds != UNIT_KINDS { + return Err("GLOBAL_UNIT_ASSIGNED_CONTEXT does not name one length, one plane-angle and one solid-angle unit".into()); + } + assigned + .iter() + .copied() + .find(|id| unit_kind(id) == "LENGTH_UNIT") + .ok_or_else(|| "missing length unit".into()) +} diff --git a/main/opengeometry/test-support/src/part21/curves.rs b/main/opengeometry/test-support/src/part21/curves.rs new file mode 100644 index 0000000..955d90c --- /dev/null +++ b/main/opengeometry/test-support/src/part21/curves.rs @@ -0,0 +1,131 @@ +use super::document::Document; +use super::lexer::{fields, reference, references}; +use crate::vector::{cross, dot, norm, sub}; +use std::f64::consts::{FRAC_PI_2, TAU}; + +pub(super) struct Conic3 { + pub(super) origin: [f64; 3], + pub(super) x: [f64; 3], + pub(super) y: [f64; 3], + z: [f64; 3], + pub(super) a: f64, + pub(super) b: f64, +} + +impl Document { + pub(super) fn curve_distance(&self, id: usize, point: [f64; 3]) -> Result { + let expression = self.entity(id)?; + let args = fields(expression)?; + if expression.starts_with("LINE(") { + let origin = self.coordinates(reference(&args[1])?)?; + let vector = fields(self.entity(reference(&args[2])?)?)?; + let direction = self.coordinates(reference(&vector[1])?)?; + let delta = sub(point, origin); + let projection = dot(delta, direction); + return Ok(norm(std::array::from_fn(|index| { + delta[index] - projection * direction[index] + }))); + } + if expression.starts_with("CIRCLE(") || expression.starts_with("ELLIPSE(") { + let conic = self.conic(id)?; + let delta = sub(point, conic.origin); + let (px, py, pz) = ( + dot(delta, conic.x), + dot(delta, conic.y), + dot(delta, conic.z), + ); + if expression.starts_with("CIRCLE(") { + return Ok((pz * pz + (px.hypot(py) - conic.a).powi(2)).sqrt()); + } + return Ok(ellipse_distance(px, py, pz, conic.a, conic.b)); + } + if expression.starts_with("B_SPLINE_CURVE_WITH_KNOTS(") { + let controls = references(&args[2]); + let first = self.coordinates(*controls.first().ok_or("empty spline controls")?)?; + let last = self.coordinates(*controls.last().ok_or("empty spline controls")?)?; + return Ok(norm(sub(point, first)).min(norm(sub(point, last)))); + } + Err(format!("#{id} is not an edge curve support")) + } + + pub(super) fn curve_point(&self, id: usize, parameter: f64) -> Result<[f64; 3], String> { + let expression = self.entity(id)?; + if expression.starts_with("B_SPLINE_CURVE_WITH_KNOTS(") { + let point = self.spline_point(id, parameter)?; + if point.len() != 3 { + return Err("3D spline is not three-dimensional".into()); + } + return Ok([point[0], point[1], point[2]]); + } + if expression.starts_with("LINE(") { + let args = fields(expression)?; + let origin = self.coordinates(reference(&args[1])?)?; + let vector = fields(self.entity(reference(&args[2])?)?)?; + let direction = self.coordinates(reference(&vector[1])?)?; + let length: f64 = vector[2].parse().map_err(|_| "invalid vector length")?; + return Ok(std::array::from_fn(|axis| { + origin[axis] + parameter * length * direction[axis] + })); + } + if expression.starts_with("CIRCLE(") || expression.starts_with("ELLIPSE(") { + let conic = self.conic(id)?; + let radii = [conic.a, conic.b]; + return Ok(conic_point( + conic.origin, + conic.x, + conic.y, + radii, + TAU * parameter, + )); + } + Err(format!("#{id} has no comparable parameterization")) + } + + pub(super) fn conic(&self, id: usize) -> Result { + let expression = self.entity(id)?; + let args = fields(expression)?; + let (origin, z, x) = self.axis(reference(&args[1])?)?; + let a: f64 = args[2].parse().map_err(|_| "invalid conic radius")?; + let b = if expression.starts_with("ELLIPSE(") { + args[3].parse().map_err(|_| "invalid ellipse radius")? + } else { + a + }; + Ok(Conic3 { + origin, + x, + y: cross(z, x), + z, + a, + b, + }) + } +} + +pub(super) fn conic_point( + origin: [f64; N], + x: [f64; N], + y: [f64; N], + radii: [f64; 2], + angle: f64, +) -> [f64; N] { + let (sin, cos) = angle.sin_cos(); + std::array::from_fn(|axis| origin[axis] + radii[0] * cos * x[axis] + radii[1] * sin * y[axis]) +} + +fn ellipse_distance(px: f64, py: f64, pz: f64, a: f64, b: f64) -> f64 { + let (x, y) = (px.abs(), py.abs()); + let mut t = (a * y).atan2(b * x); + for _ in 0..32 { + let (sin, cos) = t.sin_cos(); + let slope = (b * b - a * a) * sin * cos + a * x * sin - b * y * cos; + let bend = (b * b - a * a) * (2.0 * t).cos() + a * x * cos + b * y * sin; + let next = (t - slope / bend).clamp(0.0, FRAC_PI_2); + if next == t { + break; + } + t = next; + } + let (sin, cos) = t.sin_cos(); + (pz * pz + (a * cos - x).powi(2) + (b * sin - y).powi(2)).sqrt() +} diff --git a/main/opengeometry/test-support/src/part21/document.rs b/main/opengeometry/test-support/src/part21/document.rs new file mode 100644 index 0000000..31447a7 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/document.rs @@ -0,0 +1,228 @@ +use super::curves::conic_point; +use super::lexer::{fields, keywords, reference, references}; +use std::collections::BTreeMap; +use std::f64::consts::TAU; + +pub struct Document { + pub(super) entities: BTreeMap, +} + +type Axis3 = ([f64; 3], [f64; 3], [f64; 3]); + +impl Document { + pub fn count(&self, kind: &str) -> usize { + self.entities + .values() + .filter(|expression| keywords(expression).iter().any(|word| word == kind)) + .count() + } + + pub fn entity_count(&self) -> usize { + self.entities.len() + } + + pub fn entity(&self, id: usize) -> Result<&str, String> { + self.entities + .get(&id) + .map(String::as_str) + .ok_or_else(|| format!("missing entity #{id}")) + } + + pub(super) fn tuple(&self, expression: &str) -> Result, String> { + let start = expression + .find('(') + .ok_or("tuple lacks opening parenthesis")?; + let end = expression + .rfind(')') + .ok_or("tuple lacks closing parenthesis")?; + expression[start + 1..end] + .split(',') + .map(|value| value.trim().parse().map_err(|_| "invalid real".into())) + .collect() + } + + pub(super) fn coordinates(&self, id: usize) -> Result<[f64; 3], String> { + let expression = self.entity(id)?; + if !expression.starts_with("CARTESIAN_POINT(") && !expression.starts_with("DIRECTION(") { + return Err(format!("#{id} is not a point or direction")); + } + let args = fields(expression)?; + let values = self.tuple(&args[1])?; + if values.len() != 3 { + return Err(format!("#{id} is not three-dimensional")); + } + Ok([values[0], values[1], values[2]]) + } + + pub(super) fn vertex(&self, id: usize) -> Result<[f64; 3], String> { + let expression = self.entity(id)?; + if !expression.starts_with("VERTEX_POINT(") { + return Err(format!("#{id} is not a vertex")); + } + let args = fields(expression)?; + self.coordinates(reference(&args[1])?) + } + + pub(super) fn axis(&self, id: usize) -> Result { + let expression = self.entity(id)?; + if !expression.starts_with("AXIS2_PLACEMENT_3D(") { + return Err(format!("#{id} is not a 3D placement")); + } + let args = fields(expression)?; + Ok(( + self.coordinates(reference(&args[1])?)?, + self.coordinates(reference(&args[2])?)?, + self.coordinates(reference(&args[3])?)?, + )) + } + + fn coordinates2(&self, id: usize) -> Result<[f64; 2], String> { + let expression = self.entity(id)?; + if !expression.starts_with("CARTESIAN_POINT(") && !expression.starts_with("DIRECTION(") { + return Err(format!("#{id} is not a point or direction")); + } + let args = fields(expression)?; + let values = self.tuple(&args[1])?; + if values.len() != 2 { + return Err(format!("#{id} is not two-dimensional")); + } + Ok([values[0], values[1]]) + } + + pub(super) fn spline_point(&self, id: usize, parameter: f64) -> Result, String> { + let expression = self.entity(id)?; + if !expression.starts_with("B_SPLINE_CURVE_WITH_KNOTS(") { + return Err(format!("#{id} is not a spline")); + } + let args = fields(expression)?; + let controls = references(&args[2]); + if controls.len() < 4 || (controls.len() - 1) % 3 != 0 { + return Err(format!("#{id} has invalid cubic controls")); + } + let segments = (controls.len() - 1) / 3; + let position = parameter * segments as f64; + let segment = (position.floor() as usize).min(segments - 1); + let local = if parameter == 1.0 { + 1.0 + } else { + position - segment as f64 + }; + let first = 3 * segment; + let points = controls[first..first + 4] + .iter() + .map(|id| { + let args = fields(self.entity(*id)?)?; + self.tuple(&args[1]) + }) + .collect::, _>>()?; + let dimension = points[0].len(); + if points.iter().any(|point| point.len() != dimension) { + return Err("mixed spline dimensions".into()); + } + let weights = [ + (1.0 - local).powi(3), + 3.0 * local * (1.0 - local).powi(2), + 3.0 * local.powi(2) * (1.0 - local), + local.powi(3), + ]; + Ok((0..dimension) + .map(|axis| { + (0..4) + .map(|index| weights[index] * points[index][axis]) + .sum() + }) + .collect()) + } + + pub(super) fn pcurve_point(&self, id: usize, parameter: f64) -> Result<[f64; 2], String> { + let expression = self.entity(id)?; + if expression.starts_with("B_SPLINE_CURVE_WITH_KNOTS(") { + let point = self.spline_point(id, parameter)?; + if point.len() != 2 { + return Err("pcurve spline is not two-dimensional".into()); + } + return Ok([point[0], point[1]]); + } + let args = fields(expression)?; + if expression.starts_with("LINE(") { + let origin = self.coordinates2(reference(&args[1])?)?; + let vector = fields(self.entity(reference(&args[2])?)?)?; + let direction = self.coordinates2(reference(&vector[1])?)?; + let length: f64 = vector[2].parse().map_err(|_| "invalid vector length")?; + return Ok([ + origin[0] + parameter * direction[0] * length, + origin[1] + parameter * direction[1] * length, + ]); + } + if expression.starts_with("CIRCLE(") || expression.starts_with("ELLIPSE(") { + let placement = fields(self.entity(reference(&args[1])?)?)?; + let origin = self.coordinates2(reference(&placement[1])?)?; + let x = self.coordinates2(reference(&placement[2])?)?; + let length = x[0].hypot(x[1]); + if length == 0.0 { + return Err("singular pcurve direction".into()); + } + let x = [x[0] / length, x[1] / length]; + let y = [-x[1], x[0]]; + let major: f64 = args[2].parse().map_err(|_| "invalid pcurve radius")?; + let minor: f64 = if expression.starts_with("CIRCLE(") { + major + } else { + args[3].parse().map_err(|_| "invalid pcurve minor radius")? + }; + return Ok(conic_point(origin, x, y, [major, minor], TAU * parameter)); + } + Err(format!("#{id} is not a supported pcurve")) + } + + pub(super) fn pcurve_support(&self, pcurve: usize) -> Result<(usize, usize), String> { + let pcurve_args = fields(self.entity(pcurve)?)?; + let surface = reference(&pcurve_args[1])?; + let definition = reference(&pcurve_args[2])?; + let representation = fields(self.entity(definition)?)?; + let uv_curve = *references(&representation[1]) + .first() + .ok_or("empty pcurve representation")?; + Ok((surface, uv_curve)) + } + + pub(super) fn surface_point(&self, id: usize, uv: [f64; 2]) -> Result<[f64; 3], String> { + let expression = self.entity(id)?; + let args = fields(expression)?; + let (origin, z, x) = self.axis(reference(&args[1])?)?; + let y = [ + z[1] * x[2] - z[2] * x[1], + z[2] * x[0] - z[0] * x[2], + z[0] * x[1] - z[1] * x[0], + ]; + let (sin_u, cos_u) = uv[0].sin_cos(); + let (sin_v, cos_v) = uv[1].sin_cos(); + let (radial, axial) = if expression.starts_with("PLANE(") { + return Ok(std::array::from_fn(|axis| { + origin[axis] + uv[0] * x[axis] + uv[1] * y[axis] + })); + } else if expression.starts_with("CYLINDRICAL_SURFACE(") { + ( + args[2] + .parse::() + .map_err(|_| "invalid cylinder radius")?, + uv[1], + ) + } else if expression.starts_with("SPHERICAL_SURFACE(") { + let radius: f64 = args[2].parse().map_err(|_| "invalid sphere radius")?; + (radius * cos_v, radius * sin_v) + } else if expression.starts_with("CONICAL_SURFACE(") { + let angle: f64 = args[3].parse().map_err(|_| "invalid cone angle")?; + (uv[1] * angle.sin(), uv[1] * angle.cos()) + } else if expression.starts_with("TOROIDAL_SURFACE(") { + let major: f64 = args[2].parse().map_err(|_| "invalid torus major radius")?; + let minor: f64 = args[3].parse().map_err(|_| "invalid torus minor radius")?; + (major + minor * cos_v, minor * sin_v) + } else { + return Err(format!("#{id} is not a supported surface")); + }; + Ok(std::array::from_fn(|axis| { + origin[axis] + radial * (cos_u * x[axis] + sin_u * y[axis]) + axial * z[axis] + })) + } +} diff --git a/main/opengeometry/test-support/src/part21/expected.rs b/main/opengeometry/test-support/src/part21/expected.rs new file mode 100644 index 0000000..5c2f292 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/expected.rs @@ -0,0 +1,342 @@ +use super::document::Document; +use opengeometry::brep::BrepEnvelope; +use serde_json::Value; +use std::collections::{BTreeMap, BTreeSet}; +use std::f64::consts::TAU; + +#[derive(Default)] +pub struct ExpectedValues { + points3: BTreeSet<[u64; 3]>, + directions3: BTreeSet<[u64; 3]>, + points2: BTreeSet<[u64; 2]>, + directions2: BTreeSet<[u64; 2]>, + radii3: BTreeSet, + radii2: BTreeSet, +} + +pub struct PcurveExpectation { + pub curve: String, + pub surface: String, + pub bits: Option<[u64; 4]>, +} + +struct PcurveUse<'a> { + pcurve: &'a Value, + surface: &'a Value, + lift: [f64; 2], + scale: f64, +} + +struct MappedPcurve { + origin: [f64; 2], + direction: [f64; 2], + radii: Vec, +} + +pub fn pcurve_expectations(brep: &BrepEnvelope, unit_scale: f64) -> Vec { + let body = body_json(brep); + let mut expectations = Vec::new(); + for edge in array(&body["topology"]["edges"]) { + if edge["geometry"]["kind"] != "Curve" { + continue; + } + let curve = kind(&body["geometry"]["curves"][index(&edge["geometry"]["curve"])]); + for use_ in pcurve_uses(&body, edge, unit_scale) { + expectations.push(PcurveExpectation { + curve: curve.to_string(), + surface: kind(use_.surface).to_string(), + bits: mapped_pcurve(&use_).map(|mapped| { + let ([u, v], [du, dv]) = (bits(mapped.origin), bits(mapped.direction)); + [u, v, du, dv] + }), + }); + } + } + expectations +} + +impl ExpectedValues { + pub fn add_body(&mut self, brep: &BrepEnvelope, unit_scale: f64) { + let body = body_json(brep); + let mut surfaces = BTreeSet::new(); + for face in array(&body["topology"]["faces"]) { + let surface = index(&face["surface"]); + if surfaces.insert(surface) { + self.add_surface(&body["geometry"]["surfaces"][surface], unit_scale); + } + } + for vertex in array(&body["topology"]["vertices"]) { + let position = vector::<3>(&vertex["position"]).map(|v| v * unit_scale); + self.points3.insert(bits(position)); + } + let mut curves = BTreeSet::new(); + for edge in array(&body["topology"]["edges"]) { + if edge["geometry"]["kind"] != "Curve" { + continue; + } + let curve = index(&edge["geometry"]["curve"]); + if curves.insert(curve) { + self.add_curve(&body["geometry"]["curves"][curve], unit_scale); + } + self.add_edge_pcurves(&body, edge, unit_scale); + } + } + + fn add_surface(&mut self, surface: &Value, scale: f64) { + self.add_placement(&surface["frame"], scale); + match kind(surface) { + "Cylinder" | "Sphere" => { + self.radii3 + .insert(bits([number(&surface["radius"]) * scale])[0]); + } + "Torus" => { + for key in ["major_radius", "minor_radius"] { + self.radii3.insert(bits([number(&surface[key]) * scale])[0]); + } + } + _ => {} + } + } + + fn add_placement(&mut self, frame: &Value, scale: f64) { + let origin = vector::<3>(&frame["origin"]).map(|v| v * scale); + self.points3.insert(bits(origin)); + self.directions3.insert(bits(vector::<3>(&frame["z"]))); + self.directions3.insert(bits(vector::<3>(&frame["x"]))); + } + + fn add_curve(&mut self, curve: &Value, scale: f64) { + match kind(curve) { + "Line" => { + let origin = vector::<3>(&curve["origin"]).map(|v| v * scale); + let velocity = vector::<3>(&curve["direction"]).map(|v| v * scale); + let length = fold_length(&velocity); + self.points3.insert(bits(origin)); + self.directions3.insert(bits(velocity.map(|v| v / length))); + } + "Circle" => { + self.add_placement(&curve["frame"], scale); + self.radii3 + .insert(bits([number(&curve["radius"]) * scale])[0]); + } + "Ellipse" => { + self.add_placement(&curve["frame"], scale); + for key in ["major_radius", "minor_radius"] { + self.radii3.insert(bits([number(&curve[key]) * scale])[0]); + } + } + _ => {} + } + } + + fn add_edge_pcurves(&mut self, body: &Value, edge: &Value, scale: f64) { + for use_ in pcurve_uses(body, edge, scale) { + if let Some(mapped) = mapped_pcurve(&use_) { + self.points2.insert(bits(mapped.origin)); + self.directions2.insert(bits(mapped.direction)); + for radius in mapped.radii { + self.radii2.insert(bits([radius])[0]); + } + } + } + } +} + +fn body_json(brep: &BrepEnvelope) -> Value { + let json = brep + .to_json() + .unwrap_or_else(|error| panic!("BRep does not serialise: {error}")); + serde_json::from_str(&json).unwrap_or_else(|error| panic!("BRep JSON does not parse: {error}")) +} + +fn pcurve_uses<'a>(body: &'a Value, edge: &'a Value, scale: f64) -> Vec> { + let halfedges = &body["topology"]["halfedges"]; + let uses = [&edge["halfedge"], &edge["twin_halfedge"]] + .into_iter() + .filter(|use_| !use_.is_null()) + .map(|use_| &halfedges[index(use_)]) + .collect::>(); + let pcurve = + |use_: &Value| &body["geometry"]["pcurves"][index(&use_["geometry_use"]["pcurve"])]; + if uses + .iter() + .any(|use_| kind(pcurve(use_)) == "ProjectedCurve") + { + return Vec::new(); + } + uses.into_iter() + .map(|use_| { + let face = &body["topology"]["faces"][index(&use_["face"])]; + PcurveUse { + pcurve: pcurve(use_), + surface: &body["geometry"]["surfaces"][index(&face["surface"])], + lift: vector::<2>(&use_["geometry_use"]["periodic_lift"]), + scale, + } + }) + .collect() +} + +fn mapped_pcurve(use_: &PcurveUse<'_>) -> Option { + let s = use_.scale; + let metric = match kind(use_.surface) { + "Plane" => [s, s], + "Cylinder" => [1.0, s], + "Cone" => [1.0, s / number(&use_.surface["semi_angle"]).cos()], + _ => [1.0, 1.0], + }; + let period = match kind(use_.surface) { + "Plane" => [0.0, 0.0], + "Torus" => [TAU; 2], + _ => [TAU, 0.0], + }; + let lift = use_.lift; + let pcurve = use_.pcurve; + let origin = || -> [f64; 2] { + let origin = vector::<2>(&pcurve["origin"]); + std::array::from_fn(|i| (origin[i] + lift[i] * period[i]) * metric[i]) + }; + match kind(pcurve) { + "Line2" => { + let direction = vector::<2>(&pcurve["direction"]); + let velocity: [f64; 2] = std::array::from_fn(|i| direction[i] * metric[i]); + let length = fold_length(&velocity); + Some(MappedPcurve { + origin: origin(), + direction: velocity.map(|v| v / length), + radii: Vec::new(), + }) + } + "Conic2" => { + let axis_a = vector::<2>(&pcurve["axis_a"]); + let axis_b = vector::<2>(&pcurve["axis_b"]); + let a = [axis_a[0] * metric[0], axis_a[1] * metric[1], 0.0]; + let b = [axis_b[0] * metric[0], axis_b[1] * metric[1], 0.0]; + let (ra, rb) = (fold_length(&a), fold_length(&b)); + let axis = if ra >= rb { a } else { b }; + Some(MappedPcurve { + origin: origin(), + direction: [axis[0], axis[1]], + radii: vec![ra.max(rb), ra.min(rb)], + }) + } + _ => None, + } +} + +pub fn check_values_belong(document: &Document, expected: &ExpectedValues) -> Result<(), String> { + let controls = document.spline_control_points()?; + let none = BTreeSet::new(); + check_members( + "CARTESIAN_POINT", + document.points3()?, + &controls, + &expected.points3, + )?; + check_members( + "CARTESIAN_POINT", + document.points2()?, + &controls, + &expected.points2, + )?; + check_members( + "DIRECTION", + document.directions3()?, + &none, + &expected.directions3, + )?; + check_members( + "DIRECTION", + document.directions2()?, + &none, + &expected.directions2, + )?; + for curve in document.curve_radii()? { + let radii = if curve.dimension == 2 { + &expected.radii2 + } else { + &expected.radii3 + }; + check_radii( + &format!("{} #{}", curve.kind, curve.curve), + &curve.radii, + radii, + )?; + } + for (surface, radii) in document.surface_radii()? { + check_radii(&format!("{surface} surface"), &radii, &expected.radii3)?; + } + Ok(()) +} + +fn check_members( + kind: &str, + values: BTreeMap, + excluded: &BTreeSet, + expected: &BTreeSet<[u64; N]>, +) -> Result<(), String> { + match values + .into_iter() + .find(|(id, value)| !excluded.contains(id) && !expected.contains(&bits(*value))) + { + Some((id, value)) => Err(missing_value(&format!("{kind} #{id}"), &value)), + None => Ok(()), + } +} + +fn check_radii(owner: &str, radii: &[f64], expected: &BTreeSet) -> Result<(), String> { + match radii + .iter() + .find(|radius| !expected.contains(&bits([**radius])[0])) + { + Some(radius) => Err(missing_value(&format!("{owner} radius"), &[*radius])), + None => Ok(()), + } +} + +fn missing_value(owner: &str, value: &[f64]) -> String { + format!( + "{owner} {value:?} is not a value of the placed BRep (B-spline controls and knots are excluded)" + ) +} + +fn fold_length(values: &[f64]) -> f64 { + values.iter().fold(0.0_f64, |n, v| n.hypot(*v)) +} + +pub fn bits(values: [f64; N]) -> [u64; N] { + values.map(|v| if v == 0.0 { 0.0_f64 } else { v }.to_bits()) +} + +fn kind(value: &Value) -> &str { + value["kind"] + .as_str() + .unwrap_or_else(|| panic!("BRep JSON {value} has no kind")) +} + +fn array(value: &Value) -> &[Value] { + value + .as_array() + .unwrap_or_else(|| panic!("BRep JSON {value} is not an array")) +} + +fn index(value: &Value) -> usize { + value + .as_u64() + .and_then(|id| usize::try_from(id).ok()) + .unwrap_or_else(|| panic!("BRep JSON {value} is not an index")) +} + +fn number(value: &Value) -> f64 { + value + .as_f64() + .unwrap_or_else(|| panic!("BRep JSON {value} is not a number")) +} + +fn vector(value: &Value) -> [f64; N] { + let values = array(value); + if values.len() != N { + panic!("BRep JSON {value} does not hold {N} numbers"); + } + std::array::from_fn(|i| number(&values[i])) +} diff --git a/main/opengeometry/test-support/src/part21/lexer.rs b/main/opengeometry/test-support/src/part21/lexer.rs new file mode 100644 index 0000000..6af4a43 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/lexer.rs @@ -0,0 +1,172 @@ +pub fn references(expression: &str) -> Vec { + let bytes = expression.as_bytes(); + let mut result = Vec::new(); + let mut index = 0; + let mut quoted = false; + while index < bytes.len() { + if bytes[index] == b'\'' { + if quoted && bytes.get(index + 1) == Some(&b'\'') { + index += 2; + continue; + } + quoted = !quoted; + index += 1; + continue; + } + if !quoted && bytes[index] == b'#' { + let start = index + 1; + index = start; + while index < bytes.len() && bytes[index].is_ascii_digit() { + index += 1; + } + if let Ok(value) = expression[start..index].parse() { + result.push(value); + } + continue; + } + index += 1; + } + result +} + +pub(super) fn fields(expression: &str) -> Result, String> { + let start = expression + .find('(') + .ok_or("entity has no opening parenthesis")?; + let end = expression + .rfind(')') + .ok_or("entity has no closing parenthesis")?; + if end <= start { + return Err("empty entity expression".into()); + } + let content = &expression[start + 1..end]; + let bytes = content.as_bytes(); + let mut result = Vec::new(); + let mut depth = 0; + let mut quoted = false; + let mut from = 0; + let mut index = 0; + while index < bytes.len() { + match bytes[index] { + b'\'' if quoted && bytes.get(index + 1) == Some(&b'\'') => index += 2, + b'\'' => { + quoted = !quoted; + index += 1; + } + b'(' if !quoted => { + depth += 1; + index += 1; + } + b')' if !quoted => { + if depth == 0 { + return Err("unbalanced entity expression".into()); + } + depth -= 1; + index += 1; + } + b',' if !quoted && depth == 0 => { + result.push(content[from..index].trim().to_string()); + from = index + 1; + index += 1; + } + _ => index += 1, + } + } + if quoted || depth != 0 { + return Err("unbalanced entity expression".into()); + } + result.push(content[from..].trim().to_string()); + Ok(result) +} + +pub(super) fn reference(field: &str) -> Result { + field + .trim() + .strip_prefix('#') + .ok_or("missing entity reference")? + .parse() + .map_err(|_| "invalid entity reference".into()) +} + +pub(super) fn keywords(expression: &str) -> Vec { + let bytes = expression.as_bytes(); + let mut result = Vec::new(); + let mut index = 0; + let mut quoted = false; + while index < bytes.len() { + if bytes[index] == b'\'' { + if quoted && bytes.get(index + 1) == Some(&b'\'') { + index += 2; + continue; + } + quoted = !quoted; + index += 1; + continue; + } + if !quoted && bytes[index].is_ascii_uppercase() { + let start = index; + index += 1; + while index < bytes.len() + && (bytes[index].is_ascii_uppercase() + || bytes[index].is_ascii_digit() + || bytes[index] == b'_') + { + index += 1; + } + if bytes.get(index) == Some(&b'(') { + result.push(expression[start..index].into()); + } + continue; + } + index += 1; + } + result +} + +pub(super) fn records(expression: &str) -> Result, String> { + let Some(inner) = expression.strip_prefix('(') else { + return Ok(vec![expression]); + }; + let inner = inner.strip_suffix(')').ok_or("unbalanced complex entity")?; + let bytes = inner.as_bytes(); + let mut result = Vec::new(); + let mut depth = 0usize; + let mut quoted = false; + let mut start = None; + let mut index = 0; + while index < bytes.len() { + match bytes[index] { + b'\'' if quoted && bytes.get(index + 1) == Some(&b'\'') => index += 1, + b'\'' => quoted = !quoted, + b'(' if !quoted => depth += 1, + b')' if !quoted => { + depth = depth.checked_sub(1).ok_or("unbalanced complex entity")?; + if depth == 0 { + let from: usize = start.take().ok_or("complex entity record lacks a name")?; + result.push(&inner[from..=index]); + } + } + byte if !quoted && depth == 0 && start.is_none() && !byte.is_ascii_whitespace() => { + start = Some(index) + } + _ => {} + } + index += 1; + } + if quoted || depth != 0 || start.is_some() { + return Err("unbalanced complex entity".into()); + } + Ok(result) +} + +pub(super) fn record<'a>(expression: &'a str, kind: &str) -> Result, String> { + Ok(records(expression)?.into_iter().find(|record| { + record + .strip_prefix(kind) + .is_some_and(|rest| rest.starts_with('(')) + })) +} + +pub(super) fn keyword(expression: &str) -> &str { + &expression[..expression.find('(').unwrap_or(expression.len())] +} diff --git a/main/opengeometry/test-support/src/part21/mod.rs b/main/opengeometry/test-support/src/part21/mod.rs new file mode 100644 index 0000000..aded827 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/mod.rs @@ -0,0 +1,24 @@ +mod context; +mod curves; +mod document; +mod expected; +mod lexer; +mod normalise; +mod report; +mod report_match; +mod rules; +mod solids; +#[cfg(test)] +mod tests; +mod values; + +pub use document::Document; +pub use expected::{ + bits, check_values_belong, pcurve_expectations, ExpectedValues, PcurveExpectation, +}; +pub use lexer::references; +pub use normalise::normalise_step; +pub use report::ParameterDirectionReport; +pub use report_match::{check_report_matches, check_single_report_matches}; +pub use solids::SolidEntry; +pub use values::{CurveRadii, EdgeSupport, ProductCounts}; diff --git a/main/opengeometry/test-support/src/part21/normalise.rs b/main/opengeometry/test-support/src/part21/normalise.rs new file mode 100644 index 0000000..a17f090 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/normalise.rs @@ -0,0 +1,61 @@ +use super::lexer::fields; + +pub fn normalise_step(text: &str) -> String { + let mut result = String::with_capacity(text.len()); + for line in text.lines() { + result.push_str(&positive_zeros(&renamed(line))); + result.push('\n'); + } + result +} + +fn renamed(line: &str) -> String { + let Some(expression) = line.strip_suffix(';') else { + return line.into(); + }; + let head = &expression[..expression.find('(').unwrap_or(0)]; + let names: &[(usize, &str)] = match head.split_once('=').map_or(head, |(_, kind)| kind) { + "FILE_DESCRIPTION" => &[(0, "('description')")], + "FILE_NAME" => &[(0, "'name'")], + "PRODUCT" => &[(0, "'product'"), (1, "'product'")], + "MANIFOLD_SOLID_BREP" | "BREP_WITH_VOIDS" => &[(0, "'solid'")], + _ => return line.into(), + }; + let Ok(mut args) = fields(expression) else { + return line.into(); + }; + for (index, name) in names { + if let Some(field) = args.get_mut(*index) { + *field = (*name).into(); + } + } + format!("{head}({});", args.join(",")) +} + +fn positive_zeros(line: &str) -> String { + let bytes = line.as_bytes(); + let mut result = String::with_capacity(line.len()); + let mut quoted = false; + let mut from = 0; + for (index, byte) in bytes.iter().enumerate() { + match byte { + b'\'' => quoted = !quoted, + b'-' if !quoted + && index > 0 + && matches!(bytes[index - 1], b'(' | b',') + && negative_zero(&line[index..]) => + { + result.push_str(&line[from..index]); + from = index + 1; + } + _ => {} + } + } + result.push_str(&line[from..]); + result +} + +fn negative_zero(token: &str) -> bool { + let end = token.find([',', ')']).unwrap_or(token.len()); + token[..end].parse::().is_ok_and(|value| value == 0.0) +} diff --git a/main/opengeometry/test-support/src/part21/report.rs b/main/opengeometry/test-support/src/part21/report.rs new file mode 100644 index 0000000..ef2be16 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/report.rs @@ -0,0 +1,166 @@ +use super::document::Document; +use super::lexer::{fields, reference, references}; +use crate::vector::{dot, sub}; +use std::f64::consts::TAU; + +pub struct ParameterDirectionReport { + pub assessed: usize, + pub aligned: usize, + pub reversed: usize, + pub conic_assessed: usize, + pub conic_aligned: usize, + pub conic_reversed: usize, +} + +impl Document { + pub fn pcurveless_edges(&self) -> Result { + let mut count = 0; + for expression in self + .entities + .values() + .filter(|expression| expression.starts_with("EDGE_CURVE(")) + { + let args = fields(expression)?; + let curve = self.entity(reference(&args[3])?)?; + if !curve.starts_with("SURFACE_CURVE(") && !curve.starts_with("SEAM_CURVE(") { + count += 1; + } + } + Ok(count) + } + + pub fn product_names(&self) -> Result, String> { + self.entities + .values() + .filter(|expression| expression.starts_with("PRODUCT(")) + .map(|expression| fields(expression).map(|args| args[0].clone())) + .collect() + } + + pub fn vertex_positions(&self) -> Result, String> { + self.entities + .iter() + .filter(|(_, expression)| expression.starts_with("VERTEX_POINT(")) + .map(|(id, _)| self.vertex(*id)) + .collect() + } + + pub fn surface_radii(&self) -> Result)>, String> { + self.entities + .values() + .filter_map(|expression| { + let kind = if expression.starts_with("CYLINDRICAL_SURFACE(") { + "cylinder" + } else if expression.starts_with("SPHERICAL_SURFACE(") { + "sphere" + } else if expression.starts_with("TOROIDAL_SURFACE(") { + "torus" + } else { + return None; + }; + Some(fields(expression).and_then(|args| { + args[2..] + .iter() + .map(|value| value.parse().map_err(|_| "invalid surface radius".into())) + .collect::, String>>() + .map(|radii| (kind.into(), radii)) + })) + }) + .collect() + } + + pub fn parameter_direction_report(&self) -> Result { + let mut report = ParameterDirectionReport { + assessed: 0, + aligned: 0, + reversed: 0, + conic_assessed: 0, + conic_aligned: 0, + conic_reversed: 0, + }; + for expression in self.entities.values() { + if !expression.starts_with("SURFACE_CURVE(") && !expression.starts_with("SEAM_CURVE(") { + continue; + } + let args = fields(expression)?; + let curve = reference(&args[1])?; + let curve_expression = self.entity(curve)?; + let sampled = curve_expression.starts_with("LINE(") + || curve_expression.starts_with("B_SPLINE_CURVE_WITH_KNOTS("); + let conic = + curve_expression.starts_with("CIRCLE(") || curve_expression.starts_with("ELLIPSE("); + if !sampled && !conic { + continue; + } + for pcurve in references(&args[2]) { + let (surface, uv_curve) = self.pcurve_support(pcurve)?; + if sampled { + self.add_sampled_direction(&mut report, curve, surface, uv_curve)?; + } else { + let turn = self.conic_turn(curve, surface, uv_curve)?; + add_conic_direction(&mut report, pcurve, turn)?; + } + } + } + Ok(report) + } + + fn add_sampled_direction( + &self, + report: &mut ParameterDirectionReport, + curve: usize, + surface: usize, + uv_curve: usize, + ) -> Result<(), String> { + let sample_distance = |reverse: bool| -> Result { + let mut total = 0.0; + for parameter in [0.25, 0.75] { + let uv = + self.pcurve_point(uv_curve, if reverse { 1.0 - parameter } else { parameter })?; + let point = self.surface_point(surface, uv)?; + let support = self.curve_point(curve, parameter)?; + total += point + .iter() + .zip(support) + .map(|(a, b)| (a - b).powi(2)) + .sum::(); + } + Ok(total) + }; + report.assessed += 1; + if sample_distance(false)? <= sample_distance(true)? { + report.aligned += 1; + } else { + report.reversed += 1; + } + Ok(()) + } + + fn conic_turn(&self, curve: usize, surface: usize, uv_curve: usize) -> Result { + let conic = self.conic(curve)?; + let mut angles = [0.0; 2]; + for (angle, parameter) in angles.iter_mut().zip([0.25, 0.25 + 1.0 / 64.0]) { + let point = self.surface_point(surface, self.pcurve_point(uv_curve, parameter)?)?; + let delta = sub(point, conic.origin); + *angle = (dot(delta, conic.y) / conic.b).atan2(dot(delta, conic.x) / conic.a); + } + let turn = angles[1] - angles[0]; + Ok(turn - TAU * (turn / TAU).round()) + } +} + +fn add_conic_direction( + report: &mut ParameterDirectionReport, + pcurve: usize, + turn: f64, +) -> Result<(), String> { + report.conic_assessed += 1; + if turn > 0.0 { + report.conic_aligned += 1; + } else if turn < 0.0 { + report.conic_reversed += 1; + } else { + return Err(format!("conic pcurve #{pcurve} has no direction")); + } + Ok(()) +} diff --git a/main/opengeometry/test-support/src/part21/report_match.rs b/main/opengeometry/test-support/src/part21/report_match.rs new file mode 100644 index 0000000..4925210 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/report_match.rs @@ -0,0 +1,133 @@ +use super::document::Document; +use super::values::ProductCounts; +use serde_json::Value; +use std::collections::BTreeSet; + +const BODY_KEYS: [&str; 6] = [ + "solids", + "faces", + "edges", + "cavityShells", + "fittedCurves", + "pcurvelessEdges", +]; + +pub fn check_report_matches(document: &Document, text: &str, report: &Value) -> Result<(), String> { + let products = document.product_counts()?; + check_figure("products", figure(report, "products")?, products.len())?; + let bodies = report["bodies"].as_array().ok_or("report has no bodies")?; + check_figure("bodies", bodies.len(), products.len())?; + for (index, (body, counts)) in bodies.iter().zip(&products).enumerate() { + for (key, counted) in BODY_KEYS.into_iter().zip(body_figures(counts)) { + check_figure( + &format!("bodies[{index}].{key}"), + figure(body, key)?, + counted, + )?; + } + } + check_body_sums(report, bodies)?; + check_totals(document, text, report) +} + +pub fn check_single_report_matches(document: &Document, report: &Value) -> Result<(), String> { + check_shape_figures( + document, + report, + ["solids", "faces", "edges", "cavity_shells"], + )?; + let unit = report["length_unit"] + .as_str() + .ok_or("report has no length_unit")?; + document.check_length_unit(unit) +} + +fn body_figures(counts: &ProductCounts) -> [usize; 6] { + [ + counts.solids, + counts.faces, + counts.edges, + counts.voids, + counts.fitted_curves, + counts.pcurveless_edges, + ] +} + +fn check_body_sums(report: &Value, bodies: &[Value]) -> Result<(), String> { + for key in BODY_KEYS { + let sum = bodies + .iter() + .map(|body| figure(body, key)) + .sum::>()?; + let total = figure(report, key)?; + if sum != total { + return Err(format!("report {key} is {total}, the bodies sum to {sum}")); + } + } + Ok(()) +} + +fn check_totals(document: &Document, text: &str, report: &Value) -> Result<(), String> { + check_shape_figures( + document, + report, + ["solids", "faces", "edges", "cavityShells"], + )?; + let fitted = document + .edge_supports()? + .into_iter() + .filter(|support| support.kind == "B_SPLINE_CURVE_WITH_KNOTS") + .map(|support| support.curve) + .collect::>(); + for (key, counted) in [ + ( + "products", + document.kind_counts().get("PRODUCT").copied().unwrap_or(0), + ), + ("fittedCurves", fitted.len()), + ("pcurvelessEdges", document.pcurveless_edges()?), + ("entities", document.entity_count()), + ("bytes", text.len()), + ] { + check_figure(key, figure(report, key)?, counted)?; + } + let unit = report["unit"].as_str().ok_or("report has no unit")?; + document.check_length_unit(unit) +} + +fn check_shape_figures(document: &Document, report: &Value, keys: [&str; 4]) -> Result<(), String> { + for (key, counted) in keys.into_iter().zip(shape_figures(document)?) { + check_figure(key, figure(report, key)?, counted)?; + } + Ok(()) +} + +fn shape_figures(document: &Document) -> Result<[usize; 4], String> { + let kinds = document.kind_counts(); + let kind = |name: &str| kinds.get(name).copied().unwrap_or(0); + let voids = document.solids()?.iter().map(|solid| solid.voids).sum(); + check_figure("void map", voids, kind("ORIENTED_CLOSED_SHELL"))?; + Ok([ + kind("MANIFOLD_SOLID_BREP") + kind("BREP_WITH_VOIDS"), + kind("ADVANCED_FACE"), + kind("EDGE_CURVE"), + voids, + ]) +} + +fn figure(report: &Value, key: &str) -> Result { + report + .get(key) + .and_then(Value::as_u64) + .and_then(|value| usize::try_from(value).ok()) + .ok_or_else(|| format!("report has no {key}")) +} + +fn check_figure(key: &str, reported: usize, counted: usize) -> Result<(), String> { + if reported != counted { + return Err(format!( + "report {key} is {reported}, the file has {counted}" + )); + } + Ok(()) +} diff --git a/main/opengeometry/test-support/src/part21/rules.rs b/main/opengeometry/test-support/src/part21/rules.rs new file mode 100644 index 0000000..bf85a01 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/rules.rs @@ -0,0 +1,282 @@ +use super::document::Document; +use super::lexer::{fields, keywords, reference, references}; +use std::collections::{BTreeMap, BTreeSet}; + +fn allowed(keyword: &str) -> bool { + matches!( + keyword, + "ADVANCED_BREP_SHAPE_REPRESENTATION" + | "ADVANCED_FACE" + | "APPLICATION_CONTEXT" + | "APPLICATION_PROTOCOL_DEFINITION" + | "AXIS2_PLACEMENT_2D" + | "AXIS2_PLACEMENT_3D" + | "BREP_WITH_VOIDS" + | "B_SPLINE_CURVE_WITH_KNOTS" + | "CARTESIAN_POINT" + | "CIRCLE" + | "CLOSED_SHELL" + | "CONICAL_SURFACE" + | "CYLINDRICAL_SURFACE" + | "DEFINITIONAL_REPRESENTATION" + | "DIRECTION" + | "EDGE_CURVE" + | "EDGE_LOOP" + | "ELLIPSE" + | "FACE_BOUND" + | "FACE_OUTER_BOUND" + | "GEOMETRIC_REPRESENTATION_CONTEXT" + | "GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT" + | "GLOBAL_UNIT_ASSIGNED_CONTEXT" + | "LENGTH_MEASURE" + | "LENGTH_UNIT" + | "LINE" + | "MANIFOLD_SOLID_BREP" + | "NAMED_UNIT" + | "ORIENTED_CLOSED_SHELL" + | "ORIENTED_EDGE" + | "PCURVE" + | "PLANE" + | "PLANE_ANGLE_UNIT" + | "PRODUCT" + | "PRODUCT_CONTEXT" + | "PRODUCT_DEFINITION" + | "PRODUCT_DEFINITION_CONTEXT" + | "PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE" + | "PRODUCT_DEFINITION_SHAPE" + | "REPRESENTATION_CONTEXT" + | "SEAM_CURVE" + | "SHAPE_DEFINITION_REPRESENTATION" + | "SI_UNIT" + | "SOLID_ANGLE_UNIT" + | "SPHERICAL_SURFACE" + | "SURFACE_CURVE" + | "TOROIDAL_SURFACE" + | "UNCERTAINTY_MEASURE_WITH_UNIT" + | "VECTOR" + | "VERTEX_LOOP" + | "VERTEX_POINT" + ) +} + +impl Document { + pub fn parse(text: &str) -> Result { + if !text.starts_with("ISO-10303-21;\nHEADER;\n") + || !text.ends_with("ENDSEC;\nEND-ISO-10303-21;\n") + { + return Err("invalid Part-21 envelope".into()); + } + let mut entities = BTreeMap::new(); + for line in text.lines() { + if !line.starts_with('#') { + continue; + } + let (id, expression) = line[1..].split_once('=').ok_or("entity lacks assignment")?; + let id: usize = id.parse().map_err(|_| "invalid entity id")?; + let expression = expression + .strip_suffix(';') + .ok_or("entity lacks terminator")?; + if entities.insert(id, expression.to_string()).is_some() { + return Err("duplicate entity id".into()); + } + for keyword in keywords(expression) { + if !allowed(&keyword) { + return Err(format!("unsupported entity keyword {keyword}")); + } + } + } + if entities.is_empty() { + return Err("empty Part-21 data section".into()); + } + for (expected, id) in entities.keys().enumerate() { + if *id != expected + 1 { + return Err(format!("nonsequential entity id #{id}")); + } + } + for (id, expression) in &entities { + for target in references(expression) { + if !entities.contains_key(&target) { + return Err(format!("#{id} has unresolved reference #{target}")); + } + } + } + let document = Self { entities }; + document.check_contexts()?; + document.check_solids()?; + document.validate_loops_and_shells()?; + document.validate_curve_endpoints()?; + document.validate_pcurves()?; + Ok(document) + } + + fn oriented_edge(&self, id: usize) -> Result<(usize, usize, usize, bool), String> { + let expression = self.entity(id)?; + if !expression.starts_with("ORIENTED_EDGE(") { + return Err(format!("#{id} is not ORIENTED_EDGE")); + } + let args = fields(expression)?; + let edge = reference(&args[3])?; + let edge_expression = self.entity(edge)?; + if !edge_expression.starts_with("EDGE_CURVE(") { + return Err(format!("#{edge} is not EDGE_CURVE")); + } + let edge_args = fields(edge_expression)?; + let (mut from, mut to) = (reference(&edge_args[1])?, reference(&edge_args[2])?); + let forward = args[4] == ".T."; + if !forward { + std::mem::swap(&mut from, &mut to); + } + Ok((edge, from, to, forward)) + } + + pub(super) fn face_edges(&self, face_id: usize) -> Result, String> { + let face = self.entity(face_id)?; + if !face.starts_with("ADVANCED_FACE(") { + return Err(format!("#{face_id} is not ADVANCED_FACE")); + } + let face_args = fields(face)?; + let mut edges = Vec::new(); + for bound in references(&face_args[1]) { + let bound_args = fields(self.entity(bound)?)?; + let loop_id = reference(&bound_args[1])?; + let loop_expression = self.entity(loop_id)?; + if loop_expression.starts_with("VERTEX_LOOP(") { + continue; + } + if !loop_expression.starts_with("EDGE_LOOP(") { + return Err(format!("#{loop_id} is not an edge loop")); + } + let loop_args = fields(loop_expression)?; + let oriented = references(&loop_args[1]); + if oriented.is_empty() { + return Err(format!("#{loop_id} is empty")); + } + let mut first = None; + let mut prior = None; + for use_id in oriented { + let (edge, from, to, forward) = self.oriented_edge(use_id)?; + if let Some(previous) = prior { + if previous != from { + return Err(format!("#{loop_id} does not close at #{use_id}")); + } + } else { + first = Some(from); + } + prior = Some(to); + edges.push((edge, forward)); + } + if prior != first { + return Err(format!("#{loop_id} has distinct endpoints")); + } + } + Ok(edges) + } + + fn validate_loops_and_shells(&self) -> Result<(), String> { + let mut encountered = BTreeSet::new(); + for (id, expression) in &self.entities { + if !expression.starts_with("CLOSED_SHELL(") { + continue; + } + let args = fields(expression)?; + let mut uses: BTreeMap> = BTreeMap::new(); + for face in references(&args[1]) { + for (edge, forward) in self.face_edges(face)? { + uses.entry(edge).or_default().push(forward); + encountered.insert(edge); + } + } + for (edge, senses) in uses { + if senses.len() != 2 || senses[0] == senses[1] { + return Err(format!("shell #{id} does not pair edge #{edge} oppositely")); + } + } + } + for (id, expression) in &self.entities { + if expression.starts_with("EDGE_CURVE(") && !encountered.contains(id) { + return Err(format!("edge #{id} is not in a closed shell")); + } + } + Ok(()) + } + + fn uncertainty(&self) -> Result { + let expression = self + .entities + .values() + .find(|expression| expression.starts_with("UNCERTAINTY_MEASURE_WITH_UNIT(")) + .ok_or("missing uncertainty")?; + let args = fields(expression)?; + let values = self.tuple(&args[0])?; + values + .first() + .copied() + .ok_or_else(|| "empty uncertainty".into()) + } + + fn validate_curve_endpoints(&self) -> Result<(), String> { + let tolerance = self.uncertainty()?; + if !tolerance.is_finite() || tolerance <= 0.0 { + return Err("invalid uncertainty".into()); + } + for (id, expression) in &self.entities { + if !expression.starts_with("EDGE_CURVE(") { + continue; + } + let args = fields(expression)?; + let mut geometry = reference(&args[3])?; + let support = self.entity(geometry)?; + if support.starts_with("SURFACE_CURVE(") || support.starts_with("SEAM_CURVE(") { + geometry = reference(&fields(support)?[1])?; + } + for vertex in [reference(&args[1])?, reference(&args[2])?] { + let point = self.vertex(vertex)?; + let distance = self.curve_distance(geometry, point)?; + if !distance.is_finite() || distance > tolerance { + return Err(format!("edge #{id} vertex #{vertex} misses curve #{geometry}: {distance} > {tolerance}")); + } + } + } + Ok(()) + } + + fn validate_pcurves(&self) -> Result<(), String> { + let tolerance = self.uncertainty()?; + for (id, expression) in &self.entities { + if !expression.starts_with("SURFACE_CURVE(") && !expression.starts_with("SEAM_CURVE(") { + continue; + } + let args = fields(expression)?; + let curve = reference(&args[1])?; + let curve_is_spline = self + .entity(curve)? + .starts_with("B_SPLINE_CURVE_WITH_KNOTS("); + for pcurve in references(&args[2]) { + let (surface, uv_curve) = self.pcurve_support(pcurve)?; + for sample in 0..=16 { + let parameter = sample as f64 / 16.0; + let uv = self.pcurve_point(uv_curve, parameter)?; + let point = self.surface_point(surface, uv)?; + let distance = if curve_is_spline { + let on_curve = self.spline_point(curve, parameter)?; + if on_curve.len() != 3 { + return Err("3D spline is not three-dimensional".into()); + } + point + .iter() + .zip(on_curve) + .map(|(a, b)| (a - b).powi(2)) + .sum::() + .sqrt() + } else { + self.curve_distance(curve, point)? + }; + if !distance.is_finite() || distance > tolerance { + return Err(format!("surface curve #{id} pcurve #{pcurve} sample {sample} misses 3D curve: {distance} > {tolerance}")); + } + } + } + } + Ok(()) + } +} diff --git a/main/opengeometry/test-support/src/part21/solids.rs b/main/opengeometry/test-support/src/part21/solids.rs new file mode 100644 index 0000000..406b486 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/solids.rs @@ -0,0 +1,63 @@ +use super::document::Document; +use super::lexer::{fields, reference, references}; +use std::collections::BTreeMap; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SolidEntry { + pub voids: usize, + pub with_voids: bool, +} + +impl Document { + pub fn solids(&self) -> Result, String> { + let mut result = Vec::new(); + for expression in self.entities.values() { + if expression.starts_with("MANIFOLD_SOLID_BREP(") { + result.push(SolidEntry { + voids: 0, + with_voids: false, + }); + } else if expression.starts_with("BREP_WITH_VOIDS(") { + result.push(SolidEntry { + voids: references(&fields(expression)?[2]).len(), + with_voids: true, + }); + } + } + Ok(result) + } + + pub(super) fn check_solids(&self) -> Result<(), String> { + let mut uses = BTreeMap::new(); + for (id, expression) in &self.entities { + if expression.starts_with("ORIENTED_CLOSED_SHELL(") { + let shell = reference(&fields(expression)?[2])?; + if !self.entity(shell)?.starts_with("CLOSED_SHELL(") { + return Err(format!( + "ORIENTED_CLOSED_SHELL #{id} does not orient a CLOSED_SHELL" + )); + } + uses.entry(*id).or_insert(0); + } else if expression.starts_with("BREP_WITH_VOIDS(") { + let voids = references(&fields(expression)?[2]); + if voids.is_empty() { + return Err(format!("BREP_WITH_VOIDS #{id} has no void")); + } + for void in voids { + if !self.entity(void)?.starts_with("ORIENTED_CLOSED_SHELL(") { + return Err(format!( + "void #{void} of #{id} is not an ORIENTED_CLOSED_SHELL" + )); + } + *uses.entry(void).or_insert(0) += 1; + } + } + } + match uses.into_iter().find(|(_, count)| *count != 1) { + Some((shell, count)) => Err(format!( + "ORIENTED_CLOSED_SHELL #{shell} is used by {count} solids" + )), + None => Ok(()), + } + } +} diff --git a/main/opengeometry/test-support/src/part21/tests/base.rs b/main/opengeometry/test-support/src/part21/tests/base.rs new file mode 100644 index 0000000..07fd7f3 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/tests/base.rs @@ -0,0 +1,386 @@ +use std::f64::consts::TAU; + +pub(super) const UNCERTAINTY: f64 = 1e-3; + +const HEADER: &str = "ISO-10303-21;\nHEADER;\nFILE_DESCRIPTION(('OpenGeometry analytic BRep'),'2;1');\nFILE_NAME('crafted','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry','');\nFILE_SCHEMA(('AUTOMOTIVE_DESIGN'));\nENDSEC;\nDATA;\n"; + +pub(super) struct Base { + pub(super) text: String, + pub(super) entities: usize, + pub(super) cylinder: Cylinder, + pub(super) lens: EllipseLens, + pub(super) spline: SplineLens, + pub(super) oriented_shell: usize, + pub(super) solids: [usize; 2], + pub(super) context: Context, + pub(super) representations: [usize; 2], + pub(super) products: Products, +} + +pub(super) struct Rim { + pub(super) circle: usize, + pub(super) conic: usize, + pub(super) definition: usize, + pub(super) edge: usize, +} + +pub(super) struct Cylinder { + pub(super) shell: usize, + pub(super) lower: Rim, + pub(super) upper: Rim, + pub(super) lower_cap_normal: usize, + pub(super) seam_origin: usize, + pub(super) seam_line: usize, + pub(super) seam: usize, +} + +pub(super) struct EllipseLens { + pub(super) shell: usize, + pub(super) ellipse: usize, + pub(super) vertex_point: usize, + pub(super) edge: usize, + pub(super) faces: [usize; 2], +} + +pub(super) struct SplineLens { + pub(super) shell: usize, + pub(super) curve: usize, + pub(super) edge: usize, +} + +pub(super) struct Context { + pub(super) length: usize, + pub(super) angle: usize, + pub(super) solid_angle: usize, + pub(super) uncertainty: usize, + pub(super) context3: usize, +} + +pub(super) struct Products { + pub(super) application: usize, + pub(super) product_context: usize, + pub(super) products: [usize; 2], +} + +struct Writer { + lines: Vec, +} + +pub(super) fn real(value: f64) -> String { + format!("{value:.17E}") +} + +fn reals(values: &[f64]) -> String { + values + .iter() + .map(|value| real(*value)) + .collect::>() + .join(",") +} + +fn refs(ids: &[usize]) -> String { + ids.iter() + .map(|id| format!("#{id}")) + .collect::>() + .join(",") +} + +fn flag(value: bool) -> &'static str { + if value { + ".T." + } else { + ".F." + } +} + +impl Writer { + fn add(&mut self, expression: impl Into) -> usize { + self.lines.push(expression.into()); + self.lines.len() + } + + fn point(&mut self, values: &[f64]) -> usize { + self.add(format!("CARTESIAN_POINT('',({}))", reals(values))) + } + + fn direction(&mut self, values: &[f64]) -> usize { + self.add(format!("DIRECTION('',({}))", reals(values))) + } + + fn placement(&mut self, origin: [f64; 3], z: [f64; 3], x: [f64; 3]) -> usize { + let origin = self.point(&origin); + let z = self.direction(&z); + let x = self.direction(&x); + self.add(format!("AXIS2_PLACEMENT_3D('',#{origin},#{z},#{x})")) + } + + fn plane(&mut self, origin: [f64; 3]) -> usize { + let axis = self.placement(origin, [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]); + self.add(format!("PLANE('',#{axis})")) + } + + fn line(&mut self, origin: usize, direction: &[f64]) -> usize { + let direction = self.direction(direction); + let vector = self.add(format!("VECTOR('',#{direction},{})", real(1.0))); + self.add(format!("LINE('',#{origin},#{vector})")) + } + + fn line_at(&mut self, origin: &[f64], direction: &[f64]) -> usize { + let origin = self.point(origin); + self.line(origin, direction) + } + + fn pcurve(&mut self, surface: usize, curve: usize) -> usize { + let definition = self.add(format!("DEFINITIONAL_REPRESENTATION('',(#{curve}),#1)")); + self.add(format!("PCURVE('',#{surface},#{definition})")) + } + + fn vertex(&mut self, position: &[f64]) -> usize { + let point = self.point(position); + self.add(format!("VERTEX_POINT('',#{point})")) + } + + fn edge(&mut self, ends: [usize; 2], support: usize) -> usize { + self.add(format!( + "EDGE_CURVE('',#{},#{},#{support},.T.)", + ends[0], ends[1] + )) + } + + fn face(&mut self, uses: &[(usize, bool)], surface: usize, sense: bool) -> usize { + let oriented = uses + .iter() + .map(|(edge, forward)| { + self.add(format!("ORIENTED_EDGE('',*,*,#{edge},{})", flag(*forward))) + }) + .collect::>(); + let edge_loop = self.add(format!("EDGE_LOOP('',({}))", refs(&oriented))); + let bound = self.add(format!("FACE_OUTER_BOUND('',#{edge_loop},.T.)")); + self.add(format!( + "ADVANCED_FACE('',(#{bound}),#{surface},{})", + flag(sense) + )) + } + + fn shell(&mut self, faces: &[usize]) -> usize { + self.add(format!("CLOSED_SHELL('',({}))", refs(faces))) + } + + fn rim(&mut self, lateral: usize, cap: usize, height: f64, vertex: usize) -> Rim { + let axis = self.placement([0.0, height, 0.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]); + let circle = self.add(format!("CIRCLE('',#{axis},{})", real(1.0))); + let line = self.line_at(&[0.0, height], &[1.0, 0.0]); + let on_lateral = self.pcurve(lateral, line); + let origin = self.point(&[0.0, 0.0]); + let x = self.direction(&[1.0, 0.0]); + let placement = self.add(format!("AXIS2_PLACEMENT_2D('',#{origin},#{x})")); + let conic = self.add(format!("CIRCLE('',#{placement},{})", real(1.0))); + let definition = self.add(format!("DEFINITIONAL_REPRESENTATION('',(#{conic}),#1)")); + let on_cap = self.add(format!("PCURVE('',#{cap},#{definition})")); + let support = self.add(format!( + "SURFACE_CURVE('',#{circle},(#{on_lateral},#{on_cap}),.CURVE_3D.)" + )); + let edge = self.edge([vertex, vertex], support); + Rim { + circle, + conic, + definition, + edge, + } + } +} + +pub(super) fn base() -> Base { + let mut writer = Writer { lines: Vec::new() }; + writer.add("(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('',''))"); + let cylinder = add_cylinder(&mut writer); + let lens = add_ellipse_lens(&mut writer); + let oriented_shell = writer.add(format!("ORIENTED_CLOSED_SHELL('',*,#{},.T.)", lens.shell)); + let solid_a = writer.add(format!( + "BREP_WITH_VOIDS('a-0',#{},(#{oriented_shell}))", + cylinder.shell + )); + let spline = add_spline_lens(&mut writer); + let solid_b = writer.add(format!("MANIFOLD_SOLID_BREP('b-0',#{})", spline.shell)); + let context = add_context(&mut writer); + let representations = [solid_a, solid_b].map(|solid| { + writer.add(format!( + "ADVANCED_BREP_SHAPE_REPRESENTATION('',(#{solid}),#{})", + context.context3 + )) + }); + let products = add_products(&mut writer, representations); + let mut text = HEADER.to_string(); + for (index, line) in writer.lines.iter().enumerate() { + text.push_str(&format!("#{}={line};\n", index + 1)); + } + text.push_str("ENDSEC;\nEND-ISO-10303-21;\n"); + Base { + text, + entities: writer.lines.len(), + cylinder, + lens, + spline, + oriented_shell, + solids: [solid_a, solid_b], + context, + representations, + products, + } +} + +fn add_cylinder(writer: &mut Writer) -> Cylinder { + let axis = writer.placement([0.0; 3], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]); + let lateral = writer.add(format!("CYLINDRICAL_SURFACE('',#{axis},{})", real(1.0))); + let lower_origin = writer.point(&[0.0; 3]); + let lower_cap_normal = writer.direction(&[-0.0, -1.0, -0.0]); + let lower_x = writer.direction(&[1.0, 0.0, 0.0]); + let lower_axis = writer.add(format!( + "AXIS2_PLACEMENT_3D('',#{lower_origin},#{lower_cap_normal},#{lower_x})" + )); + let lower_cap = writer.add(format!("PLANE('',#{lower_axis})")); + let upper_axis = writer.placement([0.0, 2.0, 0.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0]); + let upper_cap = writer.add(format!("PLANE('',#{upper_axis})")); + let bottom = writer.vertex(&[1.0, 0.0, 0.0]); + let top = writer.vertex(&[1.0, 2.0, 0.0]); + let lower = writer.rim(lateral, lower_cap, 0.0, bottom); + let upper = writer.rim(lateral, upper_cap, 2.0, top); + let seam_origin = writer.point(&[1.0, 0.0, 0.0]); + let seam_line = writer.line(seam_origin, &[0.0, 1.0, 0.0]); + let after = writer.line_at(&[TAU, 0.0], &[0.0, 1.0]); + let before = writer.line_at(&[0.0, 0.0], &[0.0, 1.0]); + let pcurves = [ + writer.pcurve(lateral, after), + writer.pcurve(lateral, before), + ]; + let support = writer.add(format!( + "SEAM_CURVE('',#{seam_line},({}),.CURVE_3D.)", + refs(&pcurves) + )); + let seam = writer.edge([bottom, top], support); + let faces = [ + writer.face( + &[ + (lower.edge, true), + (seam, true), + (upper.edge, false), + (seam, false), + ], + lateral, + true, + ), + writer.face(&[(lower.edge, false)], lower_cap, true), + writer.face(&[(upper.edge, true)], upper_cap, true), + ]; + Cylinder { + shell: writer.shell(&faces), + lower, + upper, + lower_cap_normal, + seam_origin, + seam_line, + seam, + } +} + +fn add_ellipse_lens(writer: &mut Writer) -> EllipseLens { + let plane = writer.plane([10.0, 0.0, 0.0]); + let axis = writer.placement([10.0, 0.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]); + let ellipse = writer.add(format!("ELLIPSE('',#{axis},{},{})", real(2.0), real(1.0))); + let vertex_point = writer.point(&[12.0, 0.0, 0.0]); + let vertex = writer.add(format!("VERTEX_POINT('',#{vertex_point})")); + let edge = writer.edge([vertex, vertex], ellipse); + let faces = [ + writer.face(&[(edge, true)], plane, true), + writer.face(&[(edge, false)], plane, false), + ]; + EllipseLens { + shell: writer.shell(&faces), + ellipse, + vertex_point, + edge, + faces, + } +} + +fn add_spline_lens(writer: &mut Writer) -> SplineLens { + let plane = writer.plane([20.0, 0.0, 0.0]); + let controls = [ + [20.0, 0.0, 0.0], + [21.0, 1.0, 0.0], + [19.0, 1.0, 0.0], + [20.0, 0.0, 0.0], + ] + .map(|control| writer.point(&control)); + let curve = writer.add(format!( + "B_SPLINE_CURVE_WITH_KNOTS('',3,({}),.UNSPECIFIED.,.F.,.F.,(4,4),({}),.UNSPECIFIED.)", + refs(&controls), + reals(&[0.0, 1.0]) + )); + let vertex = writer.vertex(&[20.0, 0.0, 0.0]); + let edge = writer.edge([vertex, vertex], curve); + let faces = [ + writer.face(&[(edge, true)], plane, true), + writer.face(&[(edge, false)], plane, false), + ]; + SplineLens { + shell: writer.shell(&faces), + curve, + edge, + } +} + +fn add_context(writer: &mut Writer) -> Context { + let length = writer.add("(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.))"); + let angle = writer.add("(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.))"); + let solid_angle = writer.add("(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT())"); + let uncertainty = writer.add(format!( + "UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE({}),#{length},'distance_accuracy_value','exchange error bound')", + real(UNCERTAINTY) + )); + let context3 = writer.add(format!( + "(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#{uncertainty})) GLOBAL_UNIT_ASSIGNED_CONTEXT((#{length},#{angle},#{solid_angle})) REPRESENTATION_CONTEXT('',''))" + )); + Context { + length, + angle, + solid_angle, + uncertainty, + context3, + } +} + +fn add_products(writer: &mut Writer, representations: [usize; 2]) -> Products { + let application = writer.add("APPLICATION_CONTEXT('automotive design')"); + writer.add(format!( + "APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#{application})" + )); + let product_context = writer.add(format!("PRODUCT_CONTEXT('',#{application},'mechanical')")); + let mut products = [0; 2]; + let mut definition_context = None; + for (index, name) in ["a", "b"].into_iter().enumerate() { + let product = writer.add(format!( + "PRODUCT('{name}','{name}','',(#{product_context}))" + )); + let formation = writer.add(format!( + "PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#{product},.NOT_KNOWN.)" + )); + let context = *definition_context.get_or_insert_with(|| { + writer.add(format!( + "PRODUCT_DEFINITION_CONTEXT('part definition',#{application},'design')" + )) + }); + let definition = writer.add(format!("PRODUCT_DEFINITION('','',#{formation},#{context})")); + let shape = writer.add(format!("PRODUCT_DEFINITION_SHAPE('','',#{definition})")); + writer.add(format!( + "SHAPE_DEFINITION_REPRESENTATION(#{shape},#{})", + representations[index] + )); + products[index] = product; + } + Products { + application, + product_context, + products, + } +} diff --git a/main/opengeometry/test-support/src/part21/tests/mod.rs b/main/opengeometry/test-support/src/part21/tests/mod.rs new file mode 100644 index 0000000..9bca7f6 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/tests/mod.rs @@ -0,0 +1,91 @@ +mod base; +mod reports; +mod rules; +mod values; + +use super::Document; +use std::cmp::Ordering; + +fn parsed(text: &str) -> Document { + Document::parse(text).unwrap_or_else(|error| panic!("{error}")) +} + +fn assert_rejected(text: &str, needle: &str) { + match Document::parse(text) { + Ok(_) => panic!("the text parsed; expected an error naming {needle}"), + Err(error) => assert!(error.contains(needle), "{error} does not name {needle}"), + } +} + +fn assert_error(result: Result<(), String>, needle: &str) { + let error = result.expect_err(needle); + assert!(error.contains(needle), "{error} does not name {needle}"); +} + +fn assert_close(actual: [f64; 3], expected: [f64; 3]) { + for axis in 0..3 { + assert!( + (actual[axis] - expected[axis]).abs() < 1e-12, + "{actual:?} is not {expected:?}" + ); + } +} + +fn edited(text: &str, id: usize, from: &str, to: &str) -> String { + let prefix = format!("#{id}="); + let mut found = false; + let mut result = String::with_capacity(text.len()); + for line in text.lines() { + if line.starts_with(&prefix) && line.contains(from) { + found = true; + result.push_str(&line.replacen(from, to, 1)); + } else { + result.push_str(line); + } + result.push('\n'); + } + assert!(found, "#{id} lacks {from}"); + result +} + +fn without(text: &str, id: usize) -> String { + let prefix = format!("#{id}="); + let mut result = String::with_capacity(text.len()); + for line in text.lines().filter(|line| !line.starts_with(&prefix)) { + result.push_str(&renumbered(line, id)); + result.push('\n'); + } + result +} + +fn renumbered(line: &str, removed: usize) -> String { + let mut result = String::with_capacity(line.len()); + let mut rest = line; + while let Some(index) = rest.find('#') { + result.push_str(&rest[..index]); + let digits = rest[index + 1..] + .bytes() + .take_while(u8::is_ascii_digit) + .count(); + let target: usize = rest[index + 1..index + 1 + digits].parse().unwrap(); + match target.cmp(&removed) { + Ordering::Less => result.push_str(&format!("#{target}")), + Ordering::Equal => result.push('$'), + Ordering::Greater => result.push_str(&format!("#{}", target - 1)), + } + rest = &rest[index + 1 + digits..]; + } + result.push_str(rest); + result +} + +fn appended(text: &str, expression: &str) -> String { + let count = text.lines().filter(|line| line.starts_with('#')).count(); + let end = text.rfind("ENDSEC;").unwrap(); + format!( + "{}#{}={expression};\n{}", + &text[..end], + count + 1, + &text[end..] + ) +} diff --git a/main/opengeometry/test-support/src/part21/tests/reports.rs b/main/opengeometry/test-support/src/part21/tests/reports.rs new file mode 100644 index 0000000..02eb0b8 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/tests/reports.rs @@ -0,0 +1,117 @@ +use super::base::{base, Base}; +use super::{assert_error, parsed}; +use crate::part21::{check_report_matches, check_single_report_matches, normalise_step}; +use serde_json::{json, Value}; + +fn matching_report(base: &Base) -> Value { + json!({ + "unit": "metre", + "products": 2, + "solids": 2, + "faces": 7, + "edges": 5, + "cavityShells": 1, + "fittedCurves": 1, + "pcurvelessEdges": 2, + "entities": base.entities, + "bytes": base.text.len(), + "bodies": [ + {"solids": 1, "faces": 5, "edges": 4, "cavityShells": 1, "fittedCurves": 0, "pcurvelessEdges": 1}, + {"solids": 1, "faces": 2, "edges": 1, "cavityShells": 0, "fittedCurves": 1, "pcurvelessEdges": 1}, + ], + }) +} + +#[test] +fn report_matcher_rejects_a_product_count_off_by_one() { + let base = base(); + let document = parsed(&base.text); + let mut report = matching_report(&base); + assert_eq!(check_report_matches(&document, &base.text, &report), Ok(())); + report["products"] = json!(3); + let result = check_report_matches(&document, &base.text, &report); + assert_error(result, "report products is 3, the file has 2"); +} + +#[test] +fn report_matcher_checks_each_body_against_its_product() { + let base = base(); + let mut report = matching_report(&base); + report["bodies"][1]["fittedCurves"] = json!(0); + let result = check_report_matches(&parsed(&base.text), &base.text, &report); + assert_error(result, "report bodies[1].fittedCurves is 0, the file has 1"); +} + +#[test] +fn report_matcher_checks_body_sums_against_totals() { + let base = base(); + let mut report = matching_report(&base); + report["faces"] = json!(8); + let result = check_report_matches(&parsed(&base.text), &base.text, &report); + assert_error(result, "report faces is 8, the bodies sum to 7"); +} + +#[test] +fn report_matcher_checks_bytes_and_unit_against_the_file() { + let base = base(); + let document = parsed(&base.text); + let mut report = matching_report(&base); + report["bytes"] = json!(base.text.len() + 1); + let result = check_report_matches(&document, &base.text, &report); + assert_error(result, "report bytes is"); + let mut report = matching_report(&base); + report["unit"] = json!("millimetre"); + let result = check_report_matches(&document, &base.text, &report); + assert_error(result, "file length unit is metre, expected millimetre"); +} + +#[test] +fn single_report_matcher_checks_the_free_function_figures() { + let document = parsed(&base().text); + let mut report = json!({ + "length_unit": "metre", + "faces": 7, + "edges": 5, + "solids": 2, + "cavity_shells": 1, + }); + assert_eq!(check_single_report_matches(&document, &report), Ok(())); + report["faces"] = json!(6); + let result = check_single_report_matches(&document, &report); + assert_error(result, "report faces is 6, the file has 7"); +} + +#[test] +fn normalise_step_erases_names_and_negative_zero() { + let source = [ + "ISO-10303-21;", + "HEADER;", + "FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'3;1');", + "FILE_NAME('cylinder','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry','');", + "DATA;", + "#1=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0));", + "#2=CARTESIAN_POINT('-0.0',(-5.0E-1,-1.0E-5,-0.0,1.0E-0));", + "#3=MANIFOLD_SOLID_BREP('cylinder-0',#9);", + "#4=BREP_WITH_VOIDS('box-cavity-0',#9,(#8));", + "#5=PRODUCT('cylinder','cylinder','',(#7));", + "#6=PRODUCT_CONTEXT('',#100,'mechanical');", + "END-ISO-10303-21;", + "", + ]; + let expected = [ + "ISO-10303-21;", + "HEADER;", + "FILE_DESCRIPTION(('description'),'3;1');", + "FILE_NAME('name','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry','');", + "DATA;", + "#1=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0,0.00000000000000000E0));", + "#2=CARTESIAN_POINT('-0.0',(-5.0E-1,-1.0E-5,0.0,1.0E-0));", + "#3=MANIFOLD_SOLID_BREP('solid',#9);", + "#4=BREP_WITH_VOIDS('solid',#9,(#8));", + "#5=PRODUCT('product','product','',(#7));", + "#6=PRODUCT_CONTEXT('',#100,'mechanical');", + "END-ISO-10303-21;", + "", + ]; + assert_eq!(normalise_step(&source.join("\n")), expected.join("\n")); +} diff --git a/main/opengeometry/test-support/src/part21/tests/rules.rs b/main/opengeometry/test-support/src/part21/tests/rules.rs new file mode 100644 index 0000000..0e22ad9 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/tests/rules.rs @@ -0,0 +1,199 @@ +use super::base::{base, real, UNCERTAINTY}; +use super::{appended, assert_error, assert_rejected, edited, parsed, without}; +use crate::part21::Document; +use std::collections::BTreeMap; +use std::f64::consts::FRAC_PI_3; + +#[test] +fn base_text_parses_under_every_rule() { + let base = base(); + let document = parsed(&base.text); + assert_eq!(document.entity_count(), base.entities); + assert_eq!(document.count("EDGE_CURVE"), 5); +} + +#[test] +fn vertex_off_its_curve_by_more_than_the_file_uncertainty_is_rejected() { + let base = base(); + let moved = |factor: f64| { + let to = format!(",{}))", real(factor * UNCERTAINTY)); + edited( + &base.text, + base.cylinder.seam_origin, + &format!(",{}))", real(0.0)), + &to, + ) + }; + parsed(&moved(0.95)); + assert_rejected(&moved(1.05), "misses curve"); +} + +#[test] +fn pcurve_sample_beyond_the_file_uncertainty_is_rejected() { + let base = base(); + let grown = |factor: f64| { + let to = format!(",{})", real(1.0 + factor * UNCERTAINTY)); + edited( + &base.text, + base.cylinder.lower.conic, + &format!(",{})", real(1.0)), + &to, + ) + }; + parsed(&grown(0.95)); + assert_rejected(&grown(1.05), "misses 3D curve"); +} + +#[test] +fn ellipse_distance_is_the_closest_point_distance() { + let document = Document { + entities: BTreeMap::from([ + (1, "CARTESIAN_POINT('',(0.0,0.0,0.0))".to_string()), + (2, "DIRECTION('',(0.0,0.0,1.0))".to_string()), + (3, "DIRECTION('',(1.0,0.0,0.0))".to_string()), + (4, "AXIS2_PLACEMENT_3D('',#1,#2,#3)".to_string()), + (5, "ELLIPSE('',#4,2.0,1.0)".to_string()), + ]), + }; + let distance = |point: [f64; 3]| document.curve_distance(5, point).unwrap(); + assert!((distance([2.25, 0.0, 0.0]) - 0.25).abs() < 1e-12); + let (sin, cos) = FRAC_PI_3.sin_cos(); + let normal = [cos, 2.0 * sin]; + let length = normal[0].hypot(normal[1]); + let point = [ + 2.0 * cos + 0.25 * normal[0] / length, + sin + 0.25 * normal[1] / length, + 0.0, + ]; + assert!((distance(point) - 0.25).abs() < 1e-12); +} + +#[test] +fn vertex_off_an_ellipse_by_more_than_the_uncertainty_is_rejected() { + let base = base(); + let moved = |factor: f64| { + let to = format!("({},", real(12.0 + factor * UNCERTAINTY)); + edited( + &base.text, + base.lens.vertex_point, + &format!("({},", real(12.0)), + &to, + ) + }; + parsed(&moved(0.95)); + assert_rejected(&moved(1.5), "misses curve"); +} + +#[test] +fn file_without_application_context_is_rejected() { + let base = base(); + let text = without(&base.text, base.products.application); + assert_rejected(&text, "exactly one APPLICATION_CONTEXT"); +} + +#[test] +fn file_without_a_2d_context_is_rejected() { + let text = without(&base().text, 1); + assert_rejected(&text, "exactly one 2D GEOMETRIC_REPRESENTATION_CONTEXT"); +} + +#[test] +fn shape_representation_must_reference_the_3d_context() { + let base = base(); + let from = format!("),#{})", base.context.context3); + let text = edited(&base.text, base.representations[0], &from, "),#1)"); + assert_rejected(&text, "does not reference the 3D context"); +} + +#[test] +fn definitional_representation_must_reference_the_2d_context() { + let base = base(); + let to = format!("),#{})", base.context.context3); + let text = edited(&base.text, base.cylinder.lower.definition, "),#1)", &to); + assert_rejected(&text, "does not reference the 2D context"); +} + +#[test] +fn product_contexts_must_reference_the_application_context() { + let base = base(); + let from = format!("'',#{},", base.products.application); + let to = format!("'',#{},", base.context.context3); + let text = edited(&base.text, base.products.product_context, &from, &to); + assert_rejected(&text, "does not reference the APPLICATION_CONTEXT"); +} + +#[test] +fn unit_assignment_must_name_length_angle_and_solid_angle_units() { + let base = base(); + let from = format!(",#{}))", base.context.solid_angle); + let to = format!(",#{}))", base.context.angle); + let text = edited(&base.text, base.context.context3, &from, &to); + assert_rejected(&text, "GLOBAL_UNIT_ASSIGNED_CONTEXT does not name"); +} + +#[test] +fn uncertainty_must_name_the_length_unit_and_belong_to_the_3d_context() { + let base = base(); + let context = &base.context; + let from = format!("),#{},", context.length); + let to = format!("),#{},", context.angle); + let text = edited(&base.text, context.uncertainty, &from, &to); + assert_rejected(&text, "does not name the length unit"); + let from = format!("((#{}))", context.uncertainty); + let to = format!("((#{}))", context.length); + let text = edited(&base.text, context.context3, &from, &to); + assert_rejected( + &text, + "GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT does not reference", + ); +} + +#[test] +fn length_unit_must_match_the_requested_unit() { + let base = base(); + let prefixed = |prefix: &str| { + let to = format!("SI_UNIT({prefix},"); + parsed(&edited(&base.text, base.context.length, "SI_UNIT($,", &to)) + }; + let millimetre = prefixed(".MILLI."); + assert_eq!(millimetre.length_unit(), Ok("millimetre")); + assert_eq!(millimetre.check_length_unit("millimetre"), Ok(())); + assert_error(millimetre.check_length_unit("metre"), "millimetre"); + assert_eq!(parsed(&base.text).length_unit(), Ok("metre")); + assert!(prefixed(".KILO.").length_unit().is_err()); +} + +#[test] +fn brep_with_voids_without_a_void_is_rejected() { + let base = base(); + let from = format!("(#{})", base.oriented_shell); + let text = edited(&base.text, base.solids[0], &from, "()"); + assert_rejected(&text, "has no void"); +} + +#[test] +fn void_must_be_an_oriented_closed_shell_over_a_closed_shell() { + let base = base(); + let from = format!("(#{})", base.oriented_shell); + let to = format!("(#{})", base.lens.shell); + let text = edited(&base.text, base.solids[0], &from, &to); + assert_rejected(&text, "is not an ORIENTED_CLOSED_SHELL"); + let from = format!("*,#{},", base.lens.shell); + let to = format!("*,#{},", base.lens.faces[0]); + let text = edited(&base.text, base.oriented_shell, &from, &to); + assert_rejected(&text, "does not orient a CLOSED_SHELL"); +} + +#[test] +fn oriented_closed_shell_must_be_used_by_exactly_one_solid() { + let base = base(); + let from = format!("MANIFOLD_SOLID_BREP('b-0',#{})", base.spline.shell); + let to = format!( + "BREP_WITH_VOIDS('b-0',#{},(#{}))", + base.spline.shell, base.oriented_shell + ); + let text = edited(&base.text, base.solids[1], &from, &to); + assert_rejected(&text, "is used by 2 solids"); + let expression = format!("ORIENTED_CLOSED_SHELL('',*,#{},.T.)", base.lens.shell); + assert_rejected(&appended(&base.text, &expression), "is used by 0 solids"); +} diff --git a/main/opengeometry/test-support/src/part21/tests/values.rs b/main/opengeometry/test-support/src/part21/tests/values.rs new file mode 100644 index 0000000..6df529f --- /dev/null +++ b/main/opengeometry/test-support/src/part21/tests/values.rs @@ -0,0 +1,183 @@ +use super::base::{base, real}; +use super::{appended, assert_close, edited, parsed}; +use crate::part21::{CurveRadii, EdgeSupport, ProductCounts, SolidEntry}; + +#[test] +fn curve_point_follows_circle_and_ellipse_parameterisation() { + let base = base(); + let document = parsed(&base.text); + let circle = document.curve_point(base.cylinder.lower.circle, 0.25); + assert_close(circle.unwrap(), [0.0, 0.0, -1.0]); + let ellipse = |parameter: f64| document.curve_point(base.lens.ellipse, parameter).unwrap(); + assert_close(ellipse(0.25), [10.0, 1.0, 0.0]); + assert_close(ellipse(0.5), [8.0, 0.0, 0.0]); +} + +#[test] +fn void_map_counts_each_solids_cavity_shells() { + let base = base(); + let solids = parsed(&base.text).solids().unwrap(); + let with = |voids| SolidEntry { + voids, + with_voids: true, + }; + let manifold = SolidEntry { + voids: 0, + with_voids: false, + }; + assert_eq!(solids, [with(1), manifold]); + let second = base.entities + 1; + let from = format!("(#{})", base.oriented_shell); + let to = format!("(#{},#{second})", base.oriented_shell); + let text = edited(&base.text, base.solids[0], &from, &to); + let expression = format!("ORIENTED_CLOSED_SHELL('',*,#{},.T.)", base.lens.shell); + let solids = parsed(&appended(&text, &expression)).solids().unwrap(); + assert_eq!(solids, [with(2), manifold]); +} + +#[test] +fn kind_counts_agree_with_count_for_every_kind() { + let document = parsed(&base().text); + let counts = document.kind_counts(); + for (kind, count) in &counts { + assert_eq!(document.count(kind), *count, "{kind}"); + } + assert_eq!(counts["EDGE_CURVE"], 5); + assert_eq!(counts["ADVANCED_FACE"], 7); + assert_eq!(counts["LENGTH_MEASURE"], 1); + assert_eq!(counts["SI_UNIT"], 3); +} + +#[test] +fn typed_accessors_read_points_and_directions_by_dimension() { + let base = base(); + let document = parsed(&base.text); + let points3 = document.points3().unwrap(); + let points2 = document.points2().unwrap(); + let directions3 = document.directions3().unwrap(); + let directions2 = document.directions2().unwrap(); + assert_eq!(points3[&base.lens.vertex_point], [12.0, 0.0, 0.0]); + assert_eq!( + points2.len() + points3.len(), + document.count("CARTESIAN_POINT") + ); + assert_eq!( + directions3[&base.cylinder.lower_cap_normal], + [0.0, -1.0, 0.0] + ); + assert!(directions2 + .values() + .all(|value| value[0].hypot(value[1]) == 1.0)); + assert_eq!( + directions2.len() + directions3.len(), + document.count("DIRECTION") + ); + let controls = document.spline_control_points().unwrap(); + assert_eq!(controls.len(), 4); + assert!(controls.iter().all(|id| points3.contains_key(id))); +} + +#[test] +fn edge_supports_name_each_edges_curve_radii_and_pcurves() { + let base = base(); + let (cylinder, lens) = (&base.cylinder, &base.lens); + let expected = [ + ( + cylinder.lower.edge, + cylinder.lower.circle, + "CIRCLE", + vec![1.0], + true, + ), + ( + cylinder.upper.edge, + cylinder.upper.circle, + "CIRCLE", + vec![1.0], + true, + ), + (cylinder.seam, cylinder.seam_line, "LINE", vec![], true), + (lens.edge, lens.ellipse, "ELLIPSE", vec![2.0, 1.0], false), + ( + base.spline.edge, + base.spline.curve, + "B_SPLINE_CURVE_WITH_KNOTS", + vec![], + false, + ), + ] + .map(|(edge, curve, kind, radii, with_pcurves)| EdgeSupport { + edge, + curve, + kind: kind.into(), + radii, + with_pcurves, + }); + assert_eq!(parsed(&base.text).edge_supports().unwrap(), expected); +} + +#[test] +fn curve_radii_cover_3d_and_2d_conics() { + let base = base(); + let cylinder = &base.cylinder; + let expected = [ + (cylinder.lower.circle, "CIRCLE", 3, vec![1.0]), + (cylinder.lower.conic, "CIRCLE", 2, vec![1.0]), + (cylinder.upper.circle, "CIRCLE", 3, vec![1.0]), + (cylinder.upper.conic, "CIRCLE", 2, vec![1.0]), + (base.lens.ellipse, "ELLIPSE", 3, vec![2.0, 1.0]), + ] + .map(|(curve, kind, dimension, radii)| CurveRadii { + curve, + kind: kind.into(), + dimension, + radii, + }); + assert_eq!(parsed(&base.text).curve_radii().unwrap(), expected); +} + +#[test] +fn product_counts_follow_each_products_shells() { + let base = base(); + let expected = [ + ProductCounts { + product: base.products.products[0], + solids: 1, + faces: 5, + edges: 4, + voids: 1, + pcurveless_edges: 1, + fitted_curves: 0, + }, + ProductCounts { + product: base.products.products[1], + solids: 1, + faces: 2, + edges: 1, + voids: 0, + pcurveless_edges: 1, + fitted_curves: 1, + }, + ]; + assert_eq!(parsed(&base.text).product_counts().unwrap(), expected); +} + +#[test] +fn reversed_conic_pcurve_is_reported_reversed() { + let base = base(); + let figures = |text: &str| { + let report = parsed(text).parameter_direction_report().unwrap(); + [ + report.assessed, + report.aligned, + report.reversed, + report.conic_assessed, + report.conic_aligned, + report.conic_reversed, + ] + }; + assert_eq!(figures(&base.text), [2, 2, 0, 4, 3, 1]); + let normal = base.cylinder.lower_cap_normal; + let flipped = edited(&base.text, normal, &real(-1.0), &real(1.0)); + assert_eq!(figures(&flipped), [2, 2, 0, 4, 4, 0]); +} diff --git a/main/opengeometry/test-support/src/part21/values.rs b/main/opengeometry/test-support/src/part21/values.rs new file mode 100644 index 0000000..bbcde18 --- /dev/null +++ b/main/opengeometry/test-support/src/part21/values.rs @@ -0,0 +1,271 @@ +use super::document::Document; +use super::lexer::{fields, keyword, keywords, reference, references}; +use std::collections::{BTreeMap, BTreeSet}; + +#[derive(Debug, Clone, PartialEq)] +pub struct EdgeSupport { + pub edge: usize, + pub curve: usize, + pub kind: String, + pub radii: Vec, + pub with_pcurves: bool, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct CurveRadii { + pub curve: usize, + pub kind: String, + pub dimension: usize, + pub radii: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ProductCounts { + pub product: usize, + pub solids: usize, + pub faces: usize, + pub edges: usize, + pub voids: usize, + pub pcurveless_edges: usize, + pub fitted_curves: usize, +} + +struct ProductLinks { + products: Vec, + parents: BTreeMap, + shapes: Vec<(usize, usize)>, +} + +impl Document { + pub fn kind_counts(&self) -> BTreeMap { + let mut counts = BTreeMap::new(); + for expression in self.entities.values() { + for kind in keywords(expression).into_iter().collect::>() { + *counts.entry(kind).or_insert(0) += 1; + } + } + counts + } + + pub fn points3(&self) -> Result, String> { + self.typed_tuples("CARTESIAN_POINT(") + } + + pub fn points2(&self) -> Result, String> { + self.typed_tuples("CARTESIAN_POINT(") + } + + pub fn directions3(&self) -> Result, String> { + self.typed_tuples("DIRECTION(") + } + + pub fn directions2(&self) -> Result, String> { + self.typed_tuples("DIRECTION(") + } + + fn typed_tuples( + &self, + prefix: &str, + ) -> Result, String> { + let tuples = self.tuples(prefix, N)?; + Ok(tuples + .into_iter() + .map(|(id, v)| (id, std::array::from_fn(|axis| v[axis]))) + .collect()) + } + + fn tuples(&self, prefix: &str, dimension: usize) -> Result>, String> { + let mut result = BTreeMap::new(); + for (id, expression) in &self.entities { + if !expression.starts_with(prefix) { + continue; + } + let values = self.tuple(&fields(expression)?[1])?; + if values.len() == dimension { + result.insert(*id, values); + } + } + Ok(result) + } + + pub fn spline_control_points(&self) -> Result, String> { + let mut result = BTreeSet::new(); + for expression in self.entities.values() { + if expression.starts_with("B_SPLINE_CURVE_WITH_KNOTS(") { + result.extend(references(&fields(expression)?[2])); + } + } + Ok(result) + } + + pub fn edge_supports(&self) -> Result, String> { + self.entities + .iter() + .filter(|(_, expression)| expression.starts_with("EDGE_CURVE(")) + .map(|(id, expression)| self.edge_support(*id, expression)) + .collect() + } + + fn edge_support(&self, edge: usize, expression: &str) -> Result { + let mut curve = reference(&fields(expression)?[3])?; + let support = self.entity(curve)?; + let with_pcurves = + support.starts_with("SURFACE_CURVE(") || support.starts_with("SEAM_CURVE("); + if with_pcurves { + curve = reference(&fields(support)?[1])?; + } + let curve_expression = self.entity(curve)?; + Ok(EdgeSupport { + edge, + curve, + kind: keyword(curve_expression).into(), + radii: conic_radii(curve_expression)?, + with_pcurves, + }) + } + + pub fn curve_radii(&self) -> Result, String> { + let mut result = Vec::new(); + for (id, expression) in &self.entities { + let radii = conic_radii(expression)?; + if radii.is_empty() { + continue; + } + let placement = self.entity(reference(&fields(expression)?[1])?)?; + let dimension = match keyword(placement) { + "AXIS2_PLACEMENT_2D" => 2, + "AXIS2_PLACEMENT_3D" => 3, + _ => return Err(format!("#{id} has no conic placement")), + }; + result.push(CurveRadii { + curve: *id, + kind: keyword(expression).into(), + dimension, + radii, + }); + } + Ok(result) + } + + pub fn product_counts(&self) -> Result, String> { + let links = self.product_links()?; + let mut result = Vec::new(); + for (shape, representation) in &links.shapes { + let mut product = *shape; + for _ in 0..3 { + product = *links + .parents + .get(&product) + .ok_or_else(|| format!("shape #{shape} does not reach a PRODUCT"))?; + } + result.push(self.representation_counts(product, *representation)?); + } + result.sort_by_key(|counts| counts.product); + if !result + .iter() + .map(|counts| counts.product) + .eq(links.products.iter().copied()) + { + return Err("every PRODUCT needs exactly one shape representation".into()); + } + Ok(result) + } + + fn product_links(&self) -> Result { + let mut links = ProductLinks { + products: Vec::new(), + parents: BTreeMap::new(), + shapes: Vec::new(), + }; + for (id, expression) in &self.entities { + let parent_field = match keyword(expression) { + "PRODUCT" => { + links.products.push(*id); + continue; + } + "SHAPE_DEFINITION_REPRESENTATION" => { + let args = fields(expression)?; + links + .shapes + .push((reference(&args[0])?, reference(&args[1])?)); + continue; + } + "PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE" + | "PRODUCT_DEFINITION" + | "PRODUCT_DEFINITION_SHAPE" => 2, + _ => continue, + }; + let parent = reference(&fields(expression)?[parent_field])?; + links.parents.insert(*id, parent); + } + Ok(links) + } + + fn representation_counts( + &self, + product: usize, + representation: usize, + ) -> Result { + let mut counts = ProductCounts { + product, + solids: 0, + faces: 0, + edges: 0, + voids: 0, + pcurveless_edges: 0, + fitted_curves: 0, + }; + let mut seen = BTreeSet::new(); + for solid in references(&fields(self.entity(representation)?)?[1]) { + self.add_solid(&mut counts, solid, &mut seen)?; + } + Ok(counts) + } + + fn add_solid( + &self, + counts: &mut ProductCounts, + solid: usize, + seen: &mut BTreeSet, + ) -> Result<(), String> { + let expression = self.entity(solid)?; + let args = fields(expression)?; + counts.solids += 1; + let mut shells = vec![reference(&args[1])?]; + if expression.starts_with("BREP_WITH_VOIDS(") { + for void in references(&args[2]) { + counts.voids += 1; + shells.push(reference(&fields(self.entity(void)?)?[2])?); + } + } + for shell in shells { + for face in references(&fields(self.entity(shell)?)?[1]) { + counts.faces += 1; + for (edge, _) in self.face_edges(face)? { + if !seen.insert(edge) { + continue; + } + counts.edges += 1; + let support = self.edge_support(edge, self.entity(edge)?)?; + counts.pcurveless_edges += usize::from(!support.with_pcurves); + if support.kind == "B_SPLINE_CURVE_WITH_KNOTS" && seen.insert(support.curve) { + counts.fitted_curves += 1; + } + } + } + } + Ok(()) + } +} + +fn conic_radii(expression: &str) -> Result, String> { + let count = match keyword(expression) { + "CIRCLE" => 1, + "ELLIPSE" => 2, + _ => return Ok(Vec::new()), + }; + fields(expression)?[2..2 + count] + .iter() + .map(|value| value.parse().map_err(|_| "invalid conic radius".into())) + .collect() +} diff --git a/main/opengeometry/test-support/src/scenes/acceptance.rs b/main/opengeometry/test-support/src/scenes/acceptance.rs new file mode 100644 index 0000000..8c7abbf --- /dev/null +++ b/main/opengeometry/test-support/src/scenes/acceptance.rs @@ -0,0 +1,224 @@ +use crate::world_graph::{graph, named}; +use opengeometry::exchange::StepExportReport; +use opengeometry::world_graph::{ + CopyOptions, CreateOptions, CreatingOperation, EditScope, ErrorCode, GraphError, + ModifyingOperation, Plane, Primitive, StepOptions, Transform, WorldGraph, +}; + +pub struct AcceptanceScene { + pub graph: WorldGraph, + pub level: String, + pub wall: String, + pub rail: String, + pub rails: Vec, +} + +pub fn recut_scene() -> AcceptanceScene { + let mut graph = graph(); + let (level, _) = graph.create_system_assembly(named("level-1")).unwrap(); + let wall = build_wall(&mut graph); + let rail = build_rail(&mut graph); + let members = [ + "wall-profile", + "wall-1", + "door-cutter", + "rail-path", + "rail-disc", + "rail-1", + ] + .map(String::from); + graph.add_child(&level, &members, false).unwrap(); + let mut scene = AcceptanceScene { + graph, + level, + wall, + rails: vec![rail.clone()], + rail, + }; + place_and_recut(&mut scene); + scene +} + +pub fn acceptance_scene() -> AcceptanceScene { + let mut scene = recut_scene(); + let _ = attempt_rail_minus_wall(&mut scene); + trial_rebuild(&mut scene); + add_rail_instances(&mut scene); + reparent_test_child(&mut scene.graph); + scene.graph.dispose("reparent-test").unwrap(); + scene.graph.dispose("door-cutter").unwrap(); + scene +} + +pub fn level_export( + scene: &AcceptanceScene, + unit: &str, + up_axis: &str, +) -> (String, StepExportReport) { + let options = StepOptions { + unit: unit.into(), + up_axis: up_axis.into(), + ..StepOptions::default() + }; + scene + .graph + .export_step(&[scene.level.clone()], &options) + .unwrap() +} + +fn build_wall(graph: &mut WorldGraph) -> String { + let wall = create_wall(graph, "wall-profile", "wall-1"); + let cutter = create_cutter(graph, "door-cutter", [0.9, 2.1, 0.4], 2.0); + subtract(graph, &wall, &[cutter]); + wall +} + +fn build_rail(graph: &mut WorldGraph) -> String { + let points = vec![[0.0, 1.0, 1.0], [4.0, 1.0, 1.0], [4.0, 1.0, 4.0]]; + let path = Primitive::Polyline { + points, + closed: false, + }; + let (path, _) = graph.create_primitive(path, named("rail-path")).unwrap(); + let plane = Plane { + origin: Some([0.0, 1.0, 1.0]), + normal: Some([1.0, 0.0, 0.0]), + x_direction: Some([0.0, 0.0, 1.0]), + }; + let disc = CreateOptions { + plane: Some(plane), + ..named("rail-disc") + }; + let (profile, _) = graph + .create_primitive(Primitive::Circle { radius: 0.05 }, disc) + .unwrap(); + let sweep = CreatingOperation::Sweep { profile, path }; + graph.create_operation(sweep, named("rail-1")).unwrap().0 +} + +fn place_and_recut(scene: &mut AcceptanceScene) { + let graph = &mut scene.graph; + let place = Transform::Place { + origin: Some([0.0, 3.0, 0.0]), + x_direction: None, + normal: None, + scale: None, + }; + graph.transform(&scene.level, place).unwrap(); + let rotate = Transform::Rotate { + axis: [0.0, 1.0, 0.0], + degrees: 90.0, + pivot: None, + }; + graph.transform(&scene.rail, rotate).unwrap(); + let rebuilt = wall_extrusion("wall-profile", 4.0); + graph + .rebuild_operation(&scene.wall, rebuilt, EditScope::Node) + .unwrap(); + subtract(graph, &scene.wall, &["door-cutter".into()]); +} + +pub fn attempt_rail_minus_wall(scene: &mut AcceptanceScene) -> GraphError { + let error = scene + .graph + .operate( + &scene.rail, + ModifyingOperation::Subtract, + &[scene.wall.clone()], + EditScope::Node, + ) + .unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::CoverageGap, "{error:?}"); + error +} + +fn trial_rebuild(scene: &mut AcceptanceScene) { + let graph = &mut scene.graph; + let mark = graph.mark().unwrap(); + let trial = wall_extrusion("wall-profile", 5.0); + graph + .rebuild_operation(&scene.wall, trial, EditScope::Node) + .unwrap(); + graph.rollback(mark).unwrap(); + graph.release(mark).unwrap(); +} + +fn add_rail_instances(scene: &mut AcceptanceScene) { + for index in 2..=11 { + let og_id = format!("rail-{index}"); + let offset = [0.0, 0.0, f64::from(index) * 2.0]; + let copy = instance_rail(&mut scene.graph, &scene.rail, &og_id, offset); + scene.rails.push(copy); + } +} + +pub(super) fn instance_rail( + graph: &mut WorldGraph, + rail: &str, + og_id: &str, + offset: [f64; 3], +) -> String { + let options = CopyOptions { + og_id: Some(og_id.into()), + parent: None, + }; + let (copy, _) = graph.instance(rail, options).unwrap(); + graph + .transform(©, Transform::Translate { offset }) + .unwrap(); + copy +} + +fn reparent_test_child(graph: &mut WorldGraph) { + let (reparent, _) = graph.create_system_assembly(named("reparent")).unwrap(); + let child = vec![create_cuboid(graph, "reparent-test", [0.1, 0.1, 0.1])]; + let offset = [10.0, 0.0, 0.0]; + graph + .transform(&reparent, Transform::Translate { offset }) + .unwrap(); + graph.add_child(&reparent, &child, true).unwrap(); + graph.remove_child(&reparent, &child[0], true).unwrap(); + graph.add_child(&reparent, &child, false).unwrap(); +} + +pub(super) fn create_wall(graph: &mut WorldGraph, profile: &str, wall: &str) -> String { + let rectangle = Primitive::Rectangle { + width: 6.0, + breadth: 0.2, + }; + graph.create_primitive(rectangle, named(profile)).unwrap(); + let extrusion = wall_extrusion(profile, 3.0); + graph.create_operation(extrusion, named(wall)).unwrap().0 +} + +pub(super) fn create_cutter(graph: &mut WorldGraph, og_id: &str, size: [f64; 3], x: f64) -> String { + let cutter = create_cuboid(graph, og_id, size); + let offset = [x, 0.0, 0.0]; + graph + .transform(&cutter, Transform::Translate { offset }) + .unwrap(); + cutter +} + +fn create_cuboid(graph: &mut WorldGraph, og_id: &str, size: [f64; 3]) -> String { + let cuboid = Primitive::Cuboid { + width: size[0], + height: size[1], + depth: size[2], + }; + graph.create_primitive(cuboid, named(og_id)).unwrap().0 +} + +pub(super) fn subtract(graph: &mut WorldGraph, target: &str, tools: &[String]) { + graph + .operate(target, ModifyingOperation::Subtract, tools, EditScope::Node) + .unwrap(); +} + +fn wall_extrusion(profile: &str, distance: f64) -> CreatingOperation { + CreatingOperation::Extrude { + profile: profile.into(), + holes: Vec::new(), + distance, + } +} diff --git a/main/opengeometry/test-support/src/scenes/coverage_gap.rs b/main/opengeometry/test-support/src/scenes/coverage_gap.rs new file mode 100644 index 0000000..e9f2826 --- /dev/null +++ b/main/opengeometry/test-support/src/scenes/coverage_gap.rs @@ -0,0 +1,172 @@ +use crate::vector::{add, dot, norm, scale, sub, unit}; +use opengeometry::brep::{BrepEnvelope, Similarity3, SurfaceGeometry}; +use opengeometry::math::Point3; +use opengeometry::world_graph::WorldGraph; + +struct Axis { + points: Vec, + radius: f64, +} + +struct Ring { + centre: Point3, + normal: Point3, + direction: Point3, +} + +struct WorldPlane { + origin: Point3, + normal: Point3, +} + +pub fn rail_minus_wall_clearance(graph: &WorldGraph, rail: &str, wall: &str) -> f64 { + let axis = rail_axis(graph, rail); + let brep = graph.brep(wall).unwrap(); + let placement = graph.world_placement(wall).unwrap(); + let planes = wall_planes(&brep, placement); + let ring_gap = rings(&axis) + .iter() + .flat_map(|ring| { + planes + .iter() + .map(|plane| ring_plane_gap(ring, plane, axis.radius)) + }) + .fold(f64::INFINITY, f64::min); + let edge_gap = wall_segments(&brep, placement) + .iter() + .map(|segment| edge_gap(*segment, &axis)) + .fold(f64::INFINITY, f64::min); + ring_gap.min(edge_gap) +} + +fn rail_axis(graph: &WorldGraph, rail: &str) -> Axis { + let brep = graph.brep(rail).unwrap(); + let placement = graph.world_placement(rail).unwrap(); + let mut cylinders = Vec::new(); + for face in &brep.topology.faces { + if let SurfaceGeometry::Cylinder { frame, radius } = + brep.geometry.surfaces[face.surface as usize] + { + if cylinders.last().map(|(origin, _, _)| *origin) != Some(frame.origin) { + cylinders.push((frame.origin, frame.z, radius)); + } + } + } + let (_, last_direction, radius) = *cylinders.last().unwrap(); + let end = brep + .geometry + .surfaces + .iter() + .find_map(|surface| match surface { + SurfaceGeometry::Plane { frame } if dot(frame.z, last_direction) > 1.0 - 1e-12 => { + Some(frame.origin) + } + _ => None, + }) + .unwrap(); + let mut points: Vec = cylinders.iter().map(|(origin, _, _)| *origin).collect(); + points.push(end); + Axis { + points: points + .into_iter() + .map(|point| placement.apply_point(point)) + .collect(), + radius, + } +} + +fn rings(axis: &Axis) -> Vec { + let points = &axis.points; + let directions: Vec = points + .windows(2) + .map(|pair| unit(sub(pair[1], pair[0]))) + .collect(); + let last = directions.len(); + let mut rings = vec![Ring { + centre: points[0], + normal: directions[0], + direction: directions[0], + }]; + for joint in 1..last { + rings.push(Ring { + centre: points[joint], + normal: unit(add(directions[joint - 1], directions[joint])), + direction: directions[joint - 1], + }); + } + rings.push(Ring { + centre: points[last], + normal: directions[last - 1], + direction: directions[last - 1], + }); + rings +} + +fn wall_planes(brep: &BrepEnvelope, placement: Similarity3) -> Vec { + brep.topology + .faces + .iter() + .map(|face| match brep.geometry.surfaces[face.surface as usize] { + SurfaceGeometry::Plane { frame } => { + let origin = placement.apply_point(frame.origin); + let tip = placement.apply_point(add(frame.origin, frame.z)); + WorldPlane { + origin, + normal: unit(sub(tip, origin)), + } + } + _ => panic!("the wall has a face that is not planar"), + }) + .collect() +} + +fn wall_segments(brep: &BrepEnvelope, placement: Similarity3) -> Vec<[Point3; 2]> { + let position = + |vertex: u32| placement.apply_point(brep.topology.vertices[vertex as usize].position); + brep.topology + .halfedges + .iter() + .map(|halfedge| [position(halfedge.from), position(halfedge.to)]) + .collect() +} + +fn ring_plane_gap(ring: &Ring, plane: &WorldPlane, radius: f64) -> f64 { + let n = plane.normal; + let d = ring.direction; + let b = ring.normal; + let offset = dot(n, sub(ring.centre, plane.origin)); + let g = sub(n, scale(b, dot(n, d) / dot(b, d))); + let reach = norm(sub(g, scale(d, dot(g, d)))); + (offset.abs() - radius * reach).max(0.0) +} + +fn edge_gap(segment: [Point3; 2], axis: &Axis) -> f64 { + let distance = axis + .points + .windows(2) + .map(|pair| segment_distance(segment, [pair[0], pair[1]])) + .fold(f64::INFINITY, f64::min); + (distance - axis.radius).max(0.0) +} + +fn segment_distance(p: [Point3; 2], q: [Point3; 2]) -> f64 { + let u = sub(p[1], p[0]); + let v = sub(q[1], q[0]); + let w = sub(p[0], q[0]); + let (a, b, c, e, f) = (dot(u, u), dot(u, v), dot(u, w), dot(v, v), dot(v, w)); + let denominator = a * e - b * b; + let mut s = if denominator > 1e-12 * a * e { + ((b * f - c * e) / denominator).clamp(0.0, 1.0) + } else { + 0.0 + }; + let mut t = (b * s + f) / e; + if t < 0.0 { + t = 0.0; + s = (-c / a).clamp(0.0, 1.0); + } else if t > 1.0 { + t = 1.0; + s = ((b - c) / a).clamp(0.0, 1.0); + } + norm(sub(add(p[0], scale(u, s)), add(q[0], scale(v, t)))) +} diff --git a/main/opengeometry/test-support/src/scenes/mod.rs b/main/opengeometry/test-support/src/scenes/mod.rs new file mode 100644 index 0000000..4a27a07 --- /dev/null +++ b/main/opengeometry/test-support/src/scenes/mod.rs @@ -0,0 +1,3 @@ +pub mod acceptance; +pub mod coverage_gap; +pub mod storey; diff --git a/main/opengeometry/test-support/src/scenes/storey.rs b/main/opengeometry/test-support/src/scenes/storey.rs new file mode 100644 index 0000000..f470608 --- /dev/null +++ b/main/opengeometry/test-support/src/scenes/storey.rs @@ -0,0 +1,76 @@ +use super::acceptance::{create_cutter, create_wall, instance_rail, subtract, AcceptanceScene}; +use crate::world_graph::named; +use opengeometry::brep::{BrepEnvelope, FaceRole}; +use opengeometry::world_graph::{Transform, WorldGraph}; +use std::collections::BTreeSet; + +pub struct StoreySize { + pub walls: usize, + pub rail_instances: usize, +} + +pub struct Storey { + pub root: String, + pub walls: Vec, + pub rails: Vec, +} + +pub fn build_storey(scene: &mut AcceptanceScene, size: StoreySize) -> Storey { + let graph = &mut scene.graph; + let (root, _) = graph.create_system_assembly(named("storey")).unwrap(); + let mut members = Vec::new(); + let walls: Vec = (0..size.walls) + .map(|index| add_storey_wall(graph, index, &mut members)) + .collect(); + let rails: Vec = (0..size.rail_instances) + .map(|index| { + let og_id = format!("storey-rail-{index}"); + instance_rail(graph, &scene.rail, &og_id, grid(index, 40)) + }) + .collect(); + members.extend(rails.iter().cloned()); + graph.add_child(&root, &members, false).unwrap(); + Storey { root, walls, rails } +} + +pub fn openings(brep: &BrepEnvelope) -> usize { + brep.topology + .faces + .iter() + .filter(|face| face.provenance.role == FaceRole::Cut) + .flat_map(|face| face.provenance.sources.iter().map(|source| &source.body)) + .collect::>() + .len() +} + +fn add_storey_wall(graph: &mut WorldGraph, index: usize, members: &mut Vec) -> String { + let profile = format!("storey-profile-{index}"); + let wall = create_wall(graph, &profile, &format!("storey-wall-{index}")); + let cutters: Vec = (0..4) + .map(|opening| { + let og_id = format!("storey-opening-{index}-{opening}"); + let x = -2.1 + 1.4 * f64::from(opening); + create_cutter(graph, &og_id, [0.6, 2.0, 0.4], x) + }) + .collect(); + subtract(graph, &wall, &cutters); + for cutter in &cutters { + graph.dispose(cutter).unwrap(); + } + graph + .transform( + &wall, + Transform::Translate { + offset: grid(index, 20), + }, + ) + .unwrap(); + members.extend([profile, wall.clone()]); + wall +} + +fn grid(index: usize, columns: usize) -> [f64; 3] { + let column = (index % columns) as f64; + let row = (index / columns) as f64; + [column * 8.0, 0.0, row * 8.0] +} diff --git a/main/opengeometry/test-support/src/stored.rs b/main/opengeometry/test-support/src/stored.rs new file mode 100644 index 0000000..54c70f7 --- /dev/null +++ b/main/opengeometry/test-support/src/stored.rs @@ -0,0 +1,7 @@ +pub fn compared_when(ci: Option<&str>) -> bool { + ci != Some("true") +} + +pub fn compared() -> bool { + compared_when(std::env::var("CI").ok().as_deref()) +} diff --git a/main/opengeometry/test-support/src/tolerance.rs b/main/opengeometry/test-support/src/tolerance.rs new file mode 100644 index 0000000..3059436 --- /dev/null +++ b/main/opengeometry/test-support/src/tolerance.rs @@ -0,0 +1,93 @@ +use serde_json::Value; + +pub fn ordered_bits(value: f64) -> u64 { + let bits = value.to_bits(); + if bits >> 63 == 0 { + bits | (1 << 63) + } else { + !bits + } +} + +pub fn within_four_ulp(actual: &Value, expected: &Value) -> bool { + match (actual, expected) { + (Value::Number(left), Value::Number(right)) => { + if left == right { + return true; + } + match (left.as_f64(), right.as_f64()) { + (Some(a), Some(b)) if a == 0.0 && b == 0.0 => true, + (Some(a), Some(b)) => ordered_bits(a).abs_diff(ordered_bits(b)) <= 4, + _ => false, + } + } + (Value::Array(left), Value::Array(right)) => { + left.len() == right.len() && left.iter().zip(right).all(|(a, b)| within_four_ulp(a, b)) + } + (Value::Object(left), Value::Object(right)) => { + left.len() == right.len() + && left.iter().all(|(key, value)| { + right + .get(key) + .is_some_and(|other| within_four_ulp(value, other)) + }) + } + _ => actual == expected, + } +} + +pub fn step_parts(text: &str) -> (String, Vec) { + let normalized = text + .lines() + .map(|line| { + if line.starts_with("FILE_DESCRIPTION(") { + "FILE_DESCRIPTION(('OpenGeometry analytic BRep'),'2;1');".to_string() + } else { + line.to_string() + } + }) + .collect::>() + .join("\n"); + let bytes = normalized.as_bytes(); + let mut structure = String::with_capacity(normalized.len()); + let mut reals = Vec::new(); + let mut cursor = 0; + let mut index = 0; + while index < bytes.len() { + let start = index; + if bytes[index] == b'-' || bytes[index] == b'+' { + index += 1; + } + let digits = index; + while index < bytes.len() && bytes[index].is_ascii_digit() { + index += 1; + } + if index > digits && index < bytes.len() && bytes[index] == b'.' { + index += 1; + let fractional = index; + while index < bytes.len() && bytes[index].is_ascii_digit() { + index += 1; + } + if index > fractional && index < bytes.len() && bytes[index] == b'E' { + index += 1; + if index < bytes.len() && (bytes[index] == b'-' || bytes[index] == b'+') { + index += 1; + } + let exponent = index; + while index < bytes.len() && bytes[index].is_ascii_digit() { + index += 1; + } + if index > exponent { + structure.push_str(&normalized[cursor..start]); + structure.push('@'); + reals.push(normalized[start..index].parse().unwrap()); + cursor = index; + continue; + } + } + } + index = start + 1; + } + structure.push_str(&normalized[cursor..]); + (structure, reals) +} diff --git a/main/opengeometry/test-support/src/vector.rs b/main/opengeometry/test-support/src/vector.rs new file mode 100644 index 0000000..5bad19e --- /dev/null +++ b/main/opengeometry/test-support/src/vector.rs @@ -0,0 +1,31 @@ +pub(crate) fn add(a: [f64; 3], b: [f64; 3]) -> [f64; 3] { + [a[0] + b[0], a[1] + b[1], a[2] + b[2]] +} + +pub(crate) fn sub(a: [f64; 3], b: [f64; 3]) -> [f64; 3] { + [a[0] - b[0], a[1] - b[1], a[2] - b[2]] +} + +pub(crate) fn scale(a: [f64; 3], s: f64) -> [f64; 3] { + [a[0] * s, a[1] * s, a[2] * s] +} + +pub(crate) fn dot(a: [f64; 3], b: [f64; 3]) -> f64 { + a[0] * b[0] + a[1] * b[1] + a[2] * b[2] +} + +pub(crate) fn norm(a: [f64; 3]) -> f64 { + dot(a, a).sqrt() +} + +pub(crate) fn cross(a: [f64; 3], b: [f64; 3]) -> [f64; 3] { + [ + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0], + ] +} + +pub(crate) fn unit(a: [f64; 3]) -> [f64; 3] { + scale(a, 1.0 / norm(a)) +} diff --git a/main/opengeometry/test-support/src/volume.rs b/main/opengeometry/test-support/src/volume.rs new file mode 100644 index 0000000..b80d94e --- /dev/null +++ b/main/opengeometry/test-support/src/volume.rs @@ -0,0 +1,51 @@ +use opengeometry::brep::{BrepEnvelope, SurfaceGeometry}; +use opengeometry::tessellation::tessellate; + +pub struct VolumeEstimate { + pub value: f64, + pub error_bound: f64, +} + +pub fn estimate(body: &BrepEnvelope, deflection: f64) -> VolumeEstimate { + let mesh = tessellate(body, deflection, 2_000_000).unwrap(); + let point = |index: u32| { + let at = index as usize * 3; + [ + mesh.positions[at], + mesh.positions[at + 1], + mesh.positions[at + 2], + ] + }; + let mut six_volume = 0.0; + let mut area = 0.0; + for indices in mesh.indices.chunks_exact(3) { + let [a, b, c] = [point(indices[0]), point(indices[1]), point(indices[2])]; + let cross = [ + b[1] * c[2] - b[2] * c[1], + b[2] * c[0] - b[0] * c[2], + b[0] * c[1] - b[1] * c[0], + ]; + six_volume += a[0] * cross[0] + a[1] * cross[1] + a[2] * cross[2]; + let ab = [b[0] - a[0], b[1] - a[1], b[2] - a[2]]; + let ac = [c[0] - a[0], c[1] - a[1], c[2] - a[2]]; + let normal = [ + ab[1] * ac[2] - ab[2] * ac[1], + ab[2] * ac[0] - ab[0] * ac[2], + ab[0] * ac[1] - ab[1] * ac[0], + ]; + area += normal[0].hypot(normal[1]).hypot(normal[2]) * 0.5; + } + let curved = body + .geometry + .surfaces + .iter() + .any(|surface| !matches!(surface, SurfaceGeometry::Plane { .. })); + VolumeEstimate { + value: six_volume.abs() / 6.0, + error_bound: if curved { + area * deflection * 4.0 + } else { + 1e-10 * area.max(1.0) + }, + } +} diff --git a/main/opengeometry/test-support/src/world_graph.rs b/main/opengeometry/test-support/src/world_graph.rs new file mode 100644 index 0000000..1bb30e4 --- /dev/null +++ b/main/opengeometry/test-support/src/world_graph.rs @@ -0,0 +1,13 @@ +use crate::accuracy::accuracy; +use opengeometry::world_graph::{CreateOptions, WorldGraph}; + +pub fn graph() -> WorldGraph { + WorldGraph::new(accuracy(1e-6)).unwrap() +} + +pub fn named(id: &str) -> CreateOptions { + CreateOptions { + og_id: Some(id.into()), + ..CreateOptions::default() + } +} diff --git a/main/opengeometry/test-support/tests/scenes.rs b/main/opengeometry/test-support/tests/scenes.rs new file mode 100644 index 0000000..9d4a458 --- /dev/null +++ b/main/opengeometry/test-support/tests/scenes.rs @@ -0,0 +1,69 @@ +use opengeometry::world_graph::StepOptions; +use opengeometry_test_support::part21::Document; +use opengeometry_test_support::scenes::acceptance::{acceptance_scene, level_export}; +use opengeometry_test_support::scenes::storey::{build_storey, openings, StoreySize}; +use std::collections::BTreeSet; + +#[test] +fn acceptance_scene_exports_twelve_products_and_skips_three_wires_byte_identically() { + let scene = acceptance_scene(); + let (text, report) = level_export(&scene, "millimetre", "Z"); + let (again, _) = level_export(&scene, "millimetre", "Z"); + assert!(text == again, "two level exports differ"); + assert_eq!(report.products, 12); + let bodies: Vec<&str> = report + .bodies + .iter() + .map(|body| body.og_id.as_str()) + .collect(); + let mut expected = vec!["wall-1".to_string()]; + expected.extend((1..=11).map(|index| format!("rail-{index}"))); + assert_eq!(bodies, expected); + let skipped: Vec<&str> = report + .skipped + .iter() + .map(|item| item.og_id.as_str()) + .collect(); + assert_eq!(skipped, ["wall-profile", "rail-path", "rail-disc"]); + Document::parse(&text).unwrap(); +} + +#[test] +fn acceptance_scene_rails_each_export_pcurveless_edges() { + let scene = acceptance_scene(); + let (_, report) = level_export(&scene, "millimetre", "Z"); + let rails: Vec = report + .bodies + .iter() + .filter(|body| body.og_id.starts_with("rail-")) + .map(|body| body.pcurveless_edges) + .collect(); + assert_eq!(rails.len(), 11); + assert!(rails.iter().all(|edges| *edges > 0), "{rails:?}"); +} + +#[test] +fn storey_walls_are_distinct_shapes_with_four_measured_openings() { + let mut scene = acceptance_scene(); + let size = StoreySize { + walls: 3, + rail_instances: 4, + }; + let storey = build_storey(&mut scene, size); + let shapes: BTreeSet = storey + .walls + .iter() + .map(|wall| scene.graph.brep(wall).unwrap().id.clone()) + .collect(); + assert_eq!(shapes.len(), 3); + for wall in &storey.walls { + assert_eq!(openings(&scene.graph.brep(wall).unwrap()), 4, "{wall}"); + } + let nodes: Vec = storey.walls.iter().chain(&storey.rails).cloned().collect(); + let (text, report) = scene + .graph + .export_step(&nodes, &StepOptions::default()) + .unwrap(); + assert_eq!(report.products, 7); + Document::parse(&text).unwrap(); +} diff --git a/main/opengeometry/test-support/tests/stored.rs b/main/opengeometry/test-support/tests/stored.rs new file mode 100644 index 0000000..34ea55c --- /dev/null +++ b/main/opengeometry/test-support/tests/stored.rs @@ -0,0 +1,10 @@ +use opengeometry_test_support::stored::compared_when; + +#[test] +fn stored_results_are_compared_unless_ci_is_true() { + assert!(compared_when(None)); + assert!(compared_when(Some("false"))); + assert!(compared_when(Some("1"))); + assert!(compared_when(Some(""))); + assert!(!compared_when(Some("true"))); +} diff --git a/main/opengeometry/test-support/tests/volume.rs b/main/opengeometry/test-support/tests/volume.rs new file mode 100644 index 0000000..a6a22ac --- /dev/null +++ b/main/opengeometry/test-support/tests/volume.rs @@ -0,0 +1,17 @@ +use opengeometry::brep::Frame3; +use opengeometry::primitives; +use opengeometry_test_support::accuracy::accuracy; +use opengeometry_test_support::volume::estimate; + +#[test] +fn planar_volume_is_exact_under_translation() { + let frame = Frame3 { + origin: [10.0, -7.0, 4.0], + ..Frame3::IDENTITY + }; + let body = + primitives::cuboid("translated".into(), frame, [2.0, 3.0, 4.0], accuracy(1e-6)).unwrap(); + let measured = estimate(&body, 0.01); + assert!((measured.value - 24.0).abs() < 1e-10); + assert!(measured.error_bound < 1e-8); +} diff --git a/main/opengeometry/tests/analytic_geometry.rs b/main/opengeometry/tests/analytic_geometry.rs deleted file mode 100644 index 553d95c..0000000 --- a/main/opengeometry/tests/analytic_geometry.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! D1 analytic-geometry acceptance tests, doubling as the analytic oracle for -//! the program (PRD §2.7 A1 prerequisite). Exactness is verified against -//! closed-form references — known radii, cylinder cross-sections — rather than -//! an external OCC kernel, which cannot be linked into this WASM crate. - -use opengeometry::brep::{CurveGeometry, SurfaceGeometry}; -use opengeometry::primitives::arc::OGArc; -use opengeometry::primitives::cylinder::OGCylinder; -use openmaths::Vector3; - -const TWO_PI: f64 = 2.0 * std::f64::consts::PI; - -#[test] -fn arc_edges_carry_exact_circle_curve() { - let mut arc = OGArc::new("arc-analytic".to_string()); - arc.set_config(Vector3::new(0.0, 0.0, 0.0), 2.5, 0.0, TWO_PI, 24) - .unwrap(); - - let brep = arc.brep(); - assert!(!brep.edges.is_empty()); - for edge in &brep.edges { - match edge.curve.as_ref().expect("arc edge must carry a curve") { - CurveGeometry::Circle { radius, .. } => { - assert!((radius - 2.5).abs() < 1.0e-12, "exact radius preserved"); - } - other => panic!("expected Circle, got {:?}", other.kind()), - } - } -} - -#[test] -fn cylinder_has_one_cylindrical_surface_and_circular_edges() { - let mut cylinder = OGCylinder::new("cyl-analytic".to_string()); - cylinder - .set_config(Vector3::new(0.0, 0.0, 0.0), 1.5, 4.0, TWO_PI, 32) - .unwrap(); - - let brep = cylinder.brep(); - - // Caps are planar, the lateral surface is one analytic cylinder of radius 1.5. - let mut cylinder_faces = 0usize; - let mut plane_faces = 0usize; - for face in &brep.faces { - match face.surface.as_ref().expect("cylinder face must be tagged") { - SurfaceGeometry::Cylinder { radius, height, .. } => { - assert!((radius - 1.5).abs() < 1.0e-9); - assert!((height - 4.0).abs() < 1.0e-9); - cylinder_faces += 1; - } - SurfaceGeometry::Plane { .. } => plane_faces += 1, - } - } - assert_eq!(plane_faces, 2, "top + bottom caps"); - assert!( - cylinder_faces >= 1, - "lateral faces share the cylinder surface" - ); - - // The top/bottom rings are exact circles of radius 1.5. - let circle_edges = brep - .edges - .iter() - .filter(|e| matches!(e.curve, Some(CurveGeometry::Circle { .. }))) - .count(); - assert!( - circle_edges >= 2, - "two circular rings, got {}", - circle_edges - ); -} - -#[test] -fn analytic_geometry_survives_world_transform() { - // Exactness must hold after placement: radius scales, geometry stays a - // cylinder (not silently downgraded to facets). - let mut cylinder = OGCylinder::new("cyl-xform".to_string()); - cylinder - .set_config(Vector3::new(0.0, 0.0, 0.0), 1.0, 2.0, TWO_PI, 16) - .unwrap(); - cylinder - .set_transform( - Vector3::new(5.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(3.0, 3.0, 3.0), - ) - .unwrap(); - - let world = cylinder.world_brep(); - let scaled = world.faces.iter().find_map(|f| match &f.surface { - Some(SurfaceGeometry::Cylinder { radius, .. }) => Some(*radius), - _ => None, - }); - assert!( - (scaled.expect("cylinder surface present after transform") - 3.0).abs() < 1.0e-9, - "radius scaled by uniform factor 3" - ); -} diff --git a/main/opengeometry/tests/annular_sector_round_opening.rs b/main/opengeometry/tests/annular_sector_round_opening.rs deleted file mode 100644 index 71ef052..0000000 --- a/main/opengeometry/tests/annular_sector_round_opening.rs +++ /dev/null @@ -1,304 +0,0 @@ -use opengeometry::analytic::{ - booleans::{boolean_brep, BooleanOp}, - export_curve::fit_intersection_curve, - face_intersection::intersect_breps, - primitives, - query::{classify_point, PointClassification}, - tessellation::tessellate, - topology::Accuracy, - Frame3, -}; - -#[test] -fn horizontal_cylinder_cuts_a_tessellated_round_opening_through_an_annular_sector_extrusion() { - let accuracy = Accuracy { - geometric: 6.3e-8, - intersection: 1.575e-8, - tessellation: 0.01, - exchange: 1e-5, - }; - let host_frame = Frame3 { - origin: [0.0, 0.0, 0.0], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }; - let host = primitives::annular_sector_extrusion( - "host".into(), - host_frame, - 3.0, - 0.3, - 3.0, - 0.8, - -1.6, - accuracy, - ) - .unwrap(); - let cutter_frame = Frame3 { - origin: [2.7, 1.5, 0.0], - x: [0.0, 0.0, -1.0], - y: [0.0, 1.0, 0.0], - z: [1.0, 0.0, 0.0], - }; - let cutter = - primitives::cylinder("round-opening".into(), cutter_frame, 0.5, 0.6, accuracy).unwrap(); - - let result = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - "host-with-round-opening".into(), - ) - .unwrap(); - result.brep.validate().unwrap(); - let mesh = tessellate(&result.brep, 0.01, 2_000_000).unwrap(); - - assert_eq!(result.brep.topology.faces.len(), 7); - assert!(!mesh.indices.is_empty()); - assert!(mesh.triangle_face_ids.contains(&6)); -} - -#[test] -fn horizontal_cylinder_cuts_the_equivalent_arc_edged_extrusion() { - let accuracy = Accuracy { - geometric: 6.3e-8, - intersection: 1.575e-8, - tessellation: 0.01, - exchange: 1e-5, - }; - let host_frame = Frame3 { - origin: [0.0, 0.0, 0.0], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }; - let outer_start = 0.8_f64; - let inner_end = -0.8_f64; - let point = |radius: f64, angle: f64| [radius * angle.cos(), radius * angle.sin()]; - let host = primitives::arc_edged_extrusion( - "arc-host".into(), - host_frame, - vec![ - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 3.0, - start_angle: outer_start, - sweep_angle: -1.6, - }, - primitives::ProfileEdge::Line { - from: point(3.0, inner_end), - to: point(2.7, inner_end), - }, - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.7, - start_angle: inner_end, - sweep_angle: 1.6, - }, - primitives::ProfileEdge::Line { - from: point(2.7, outer_start), - to: point(3.0, outer_start), - }, - ], - 3.0, - accuracy, - ) - .unwrap(); - let aperture_radius = 0.5_f64; - // The radial cutter must start inside the cavity across its full aperture. - // The inner curved face reaches sqrt(2.7² − 0.5²) ≈ 2.6533 m there; - // starting at nominal radius 2.7 m is tangent, not a through-opening. - let cutter_start = 2.65; - let cutter = primitives::cylinder( - "round-opening".into(), - Frame3 { - origin: [cutter_start, 1.5, 0.0], - x: [0.0, 0.0, -1.0], - y: [0.0, 1.0, 0.0], - z: [1.0, 0.0, 0.0], - }, - aperture_radius, - 3.05 - cutter_start, - accuracy, - ) - .unwrap(); - let graph = intersect_breps(&host, &cutter).unwrap(); - assert_eq!(graph.pairs.len(), 2); - for pair in &graph.pairs { - assert_eq!(pair.graph.branches.len(), 1); - assert!(!pair.graph.coincident); - assert!(pair.graph.contacts.is_empty()); - let branch = &pair.graph.branches[0]; - assert!(branch.endpoints[0] - .iter() - .zip(branch.endpoints[1]) - .all(|(left, right)| (left - right).abs() <= accuracy.geometric)); - } - let result = boolean_brep( - &host, - &cutter, - BooleanOp::Subtraction, - "arc-host-with-round-opening".into(), - ) - .unwrap(); - result.brep.validate().unwrap(); - tessellate(&result.brep, 0.01, 2_000_000).unwrap(); - for definition in 0..result.brep.geometry.intersections.len() { - let fit = fit_intersection_curve( - &result.brep.geometry, - definition as u32, - accuracy.exchange, - 20_000, - ) - .unwrap(); - assert!(fit.error_bound <= accuracy.exchange); - let trace = &result.brep.geometry.intersections[definition]; - for (segment, pair) in trace.anchors.windows(2).enumerate().step_by(11) { - let lo = pair[0].parameter + 0.1 * (pair[1].parameter - pair[0].parameter); - let hi = pair[0].parameter + 0.9 * (pair[1].parameter - pair[0].parameter); - let range = opengeometry::math::interval::Interval::new(lo, hi).unwrap(); - let bound = trace - .certified_chord_deviation(range, &result.brep.geometry) - .unwrap() - .expect("perpendicular cylinder segment has a bound"); - let a = trace.evaluate(lo, &result.brep.geometry).unwrap().point; - let b = trace.evaluate(hi, &result.brep.geometry).unwrap().point; - let chord = [b[0] - a[0], b[1] - a[1], b[2] - a[2]]; - let chord_squared = chord.iter().map(|value| value * value).sum::(); - for sample in 0..=32 { - let t = lo + (hi - lo) * sample as f64 / 32.0; - let point = trace.evaluate(t, &result.brep.geometry).unwrap().point; - let offset = [point[0] - a[0], point[1] - a[1], point[2] - a[2]]; - let portion = (offset.iter().zip(chord).map(|(x, y)| x * y).sum::() - / chord_squared) - .clamp(0.0, 1.0); - let distance = (0..3) - .map(|axis| (point[axis] - a[axis] - portion * chord[axis]).powi(2)) - .sum::() - .sqrt(); - assert!( - distance <= bound, - "trace {definition} segment {segment}: {distance} > {bound}" - ); - } - } - } - let (step, report) = - opengeometry::analytic::exchange::export_step(&result.brep, "metre").unwrap(); - assert_eq!(report.solids, 1); - assert!(step.contains("B_SPLINE_CURVE_WITH_KNOTS")); - assert_eq!( - classify_point(&result.brep, [2.8, 1.5, 0.0]).unwrap(), - PointClassification::Outside - ); - assert_eq!( - classify_point(&result.brep, [2.8, 2.2, 0.0]).unwrap(), - PointClassification::Inside - ); - assert_eq!( - classify_point(&result.brep, [2.8, 1.5, 0.7]).unwrap(), - PointClassification::Inside - ); -} - -#[test] -fn arched_annular_sector_extrusion_opening_reuses_the_spring_edges_and_an_analytic_cylinder_cap() { - let accuracy = Accuracy { - geometric: 6.3e-8, - intersection: 1.575e-8, - tessellation: 0.01, - exchange: 1e-5, - }; - let host_frame = Frame3 { - origin: [0.0, 0.0, 0.0], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }; - let host = primitives::annular_sector_extrusion_with_arched_opening( - "arched-host".into(), - host_frame, - 3.0, - 0.3, - 3.0, - 1.6, - -1.6, - primitives::AnnularSectorOpening { - id: "arched-opening".into(), - angle: 0.8, - width: 1.0, - bottom: 0.5, - height: 1.25, - }, - accuracy, - ) - .unwrap(); - - host.validate().unwrap(); - assert_eq!(host.topology.faces.len(), 10); - assert!(host - .topology - .faces - .iter() - .any(|face| face.key == "opening-0-arch")); - assert!(host.geometry.curves.iter().any(|curve| matches!( - curve, - opengeometry::analytic::CurveGeometry::Intersection { .. } - ))); - let mesh = tessellate(&host, 0.01, 2_000_000).unwrap(); - assert!(!mesh.indices.is_empty()); -} - -#[test] -fn box_arched_opening_has_shared_circle_edges_and_a_cylindrical_arch_cap() { - let accuracy = Accuracy { - geometric: 4.0e-8, - intersection: 1.0e-8, - tessellation: 0.01, - exchange: 1e-5, - }; - let host = primitives::box_with_arched_opening( - "straight-arched-host".into(), - Frame3::IDENTITY, - 4.0, - 0.3, - 3.0, - primitives::BoxArchedOpening { - id: "arched-opening".into(), - station: 2.0, - width: 1.0, - bottom: 0.4, - height: 1.4, - }, - accuracy, - ) - .unwrap(); - - host.validate().unwrap(); - let arch_cap = host - .topology - .faces - .iter() - .find(|face| face.key == "arched-opening-arched-cap") - .unwrap(); - assert!(matches!( - host.geometry.surfaces[arch_cap.surface as usize], - opengeometry::analytic::SurfaceGeometry::Cylinder { .. } - )); - assert_eq!( - host.topology - .edges - .iter() - .filter(|edge| matches!( - edge.geometry, - opengeometry::analytic::topology::EdgeGeometry::Curve { curve, .. } - if matches!(host.geometry.curves[curve as usize], opengeometry::analytic::CurveGeometry::Circle { .. }) - )) - .count(), - 2 - ); - let mesh = tessellate(&host, 0.01, 2_000_000).unwrap(); - assert!(mesh.triangle_face_ids.contains(&arch_cap.id)); - let (_, report) = opengeometry::analytic::exchange::export_step(&host, "metre").unwrap(); - assert_eq!(report.solids, 1); -} diff --git a/main/opengeometry/tests/booleans/atomicity.rs b/main/opengeometry/tests/booleans/atomicity.rs new file mode 100644 index 0000000..70aa104 --- /dev/null +++ b/main/opengeometry/tests/booleans/atomicity.rs @@ -0,0 +1,248 @@ +use super::support::cuboid; +use opengeometry::brep::FaceRole; +use opengeometry::world_graph::{ + CopyOptions, EditScope, ErrorCode, ErrorDetails, GraphError, ModifyingOperation, Primitive, + Transform, +}; +use opengeometry_test_support::volume; +use opengeometry_test_support::world_graph::{graph, named}; + +#[test] +fn box_subtraction_is_analytic_atomic_and_reports_handler() { + let mut world = graph(); + world.create_primitive(cuboid(2.0), named("host")).unwrap(); + world.create_primitive(cuboid(2.0), named("tool")).unwrap(); + world + .transform( + "tool", + Transform::Translate { + offset: [1.0, 0.0, 0.0], + }, + ) + .unwrap(); + let tool_before = world.brep("tool").unwrap().to_json().unwrap(); + world + .operate( + "host", + ModifyingOperation::Subtract, + &["tool".into()], + EditScope::Node, + ) + .unwrap(); + let host = world.brep("host").unwrap(); + let measured = volume::estimate(&host, 0.01); + assert!((measured.value - 4.0).abs() <= measured.error_bound); + assert!(host + .topology + .faces + .iter() + .any(|face| face.provenance.role == FaceRole::Cut)); + assert_eq!( + world.report("host").unwrap().unwrap()["handlers"], + serde_json::json!(["boolean_boxes"]) + ); + assert_eq!(world.brep("tool").unwrap().to_json().unwrap(), tool_before); + assert_eq!(host.revision, 1); +} + +#[test] +fn shared_target_requires_explicit_all_instances_and_tool_instance_is_allowed() { + let mut world = graph(); + world.create_primitive(cuboid(2.0), named("host")).unwrap(); + world.create_primitive(cuboid(2.0), named("tool")).unwrap(); + world + .transform( + "tool", + Transform::Translate { + offset: [1.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .instance( + "host", + CopyOptions { + og_id: Some("host-copy".into()), + ..CopyOptions::default() + }, + ) + .unwrap(); + world + .instance( + "tool", + CopyOptions { + og_id: Some("tool-copy".into()), + ..CopyOptions::default() + }, + ) + .unwrap(); + let error = world + .operate( + "host", + ModifyingOperation::Subtract, + &["tool-copy".into()], + EditScope::Node, + ) + .unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::SharedShape); + if let GraphError::Code { + details: + ErrorDetails::SharedShape { + instance_count, + sharing, + .. + }, + .. + } = error + { + assert_eq!(instance_count, 2); + assert_eq!(sharing.len(), 2); + } else { + panic!("expected SharedShape details"); + } + world + .operate( + "host", + ModifyingOperation::Subtract, + &["tool-copy".into()], + EditScope::AllInstances, + ) + .unwrap(); + assert_eq!(world.brep("host").unwrap().revision, 1); + assert_eq!(world.brep("host-copy").unwrap().revision, 1); + assert_eq!( + world + .operate( + "host", + ModifyingOperation::Subtract, + &["host-copy".into()], + EditScope::AllInstances + ) + .unwrap_err() + .error_code(), + ErrorCode::ToolSharesTargetShape + ); +} + +#[test] +fn empty_intersection_and_tool_limit_leave_graph_unchanged() { + let mut world = graph(); + world.create_primitive(cuboid(2.0), named("host")).unwrap(); + world.create_primitive(cuboid(2.0), named("tool")).unwrap(); + world + .transform( + "tool", + Transform::Translate { + offset: [10.0, 0.0, 0.0], + }, + ) + .unwrap(); + let revision = world.revision(); + let before = world.brep("host").unwrap().to_json().unwrap(); + assert_eq!( + world + .operate( + "host", + ModifyingOperation::Intersect, + &["tool".into()], + EditScope::Node + ) + .unwrap_err() + .error_code(), + ErrorCode::EmptyResult + ); + assert_eq!( + world + .operate("host", ModifyingOperation::Union, &[], EditScope::Node) + .unwrap_err() + .error_code(), + ErrorCode::LimitExceeded + ); + assert_eq!(world.revision(), revision); + assert_eq!(world.brep("host").unwrap().to_json().unwrap(), before); +} + +#[test] +fn empty_subtraction_maps_to_empty_result_without_a_commit() { + let mut world = graph(); + world + .create_primitive( + Primitive::Cuboid { + width: 1.0, + height: 1.0, + depth: 1.0, + }, + named("small"), + ) + .unwrap(); + world + .create_primitive( + Primitive::Cuboid { + width: 2.0, + height: 2.0, + depth: 2.0, + }, + named("large"), + ) + .unwrap(); + let before = world.brep("small").unwrap().to_json().unwrap(); + let revision = world.revision(); + assert_eq!( + world + .operate( + "small", + ModifyingOperation::Subtract, + &["large".into()], + EditScope::Node + ) + .unwrap_err() + .error_code(), + ErrorCode::EmptyResult, + ); + assert_eq!(world.brep("small").unwrap().to_json().unwrap(), before); + assert_eq!(world.revision(), revision); +} + +#[test] +fn make_unique_then_operate_uses_new_shape_provenance() { + let mut world = graph(); + world.create_primitive(cuboid(2.0), named("host")).unwrap(); + world.create_primitive(cuboid(2.0), named("tool")).unwrap(); + world + .transform( + "tool", + Transform::Translate { + offset: [1.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .instance( + "host", + CopyOptions { + og_id: Some("instance".into()), + ..CopyOptions::default() + }, + ) + .unwrap(); + let old_shape = world.node("host").unwrap().shape.clone().unwrap(); + world.make_unique("host").unwrap(); + let new_shape = world.node("host").unwrap().shape.clone().unwrap(); + assert_ne!(old_shape, new_shape); + world + .operate( + "host", + ModifyingOperation::Subtract, + &["tool".into()], + EditScope::Node, + ) + .unwrap(); + let body = world.brep("host").unwrap(); + assert!(body + .topology + .faces + .iter() + .flat_map(|face| &face.provenance.sources) + .any(|source| source.body == new_shape)); + assert_eq!(world.brep("instance").unwrap().revision, 0); +} diff --git a/main/opengeometry/tests/booleans/batch.rs b/main/opengeometry/tests/booleans/batch.rs new file mode 100644 index 0000000..50fc254 --- /dev/null +++ b/main/opengeometry/tests/booleans/batch.rs @@ -0,0 +1,143 @@ +use opengeometry::brep::FaceRole; +use opengeometry::world_graph::{ + CreatingOperation, EditScope, ModifyingOperation, Primitive, Transform, +}; +use opengeometry_test_support::volume; +use opengeometry_test_support::world_graph::{graph, named}; + +#[test] +fn planar_batch_subtraction_commits_one_shape_revision() { + let mut world = graph(); + world + .create_primitive( + Primitive::Cuboid { + width: 6.0, + height: 2.0, + depth: 2.0, + }, + named("host"), + ) + .unwrap(); + for (name, x) in [("left", -1.5), ("right", 1.5)] { + world + .create_primitive( + Primitive::Cuboid { + width: 1.0, + height: 2.0, + depth: 2.0, + }, + named(name), + ) + .unwrap(); + world + .transform( + name, + Transform::Translate { + offset: [x, 0.0, 0.0], + }, + ) + .unwrap(); + } + world + .operate( + "host", + ModifyingOperation::Subtract, + &["left".into(), "right".into()], + EditScope::Node, + ) + .unwrap(); + assert_eq!(world.brep("host").unwrap().revision, 1); + let measured = volume::estimate(&world.brep("host").unwrap(), 0.01); + assert!((measured.value - 16.0).abs() <= measured.error_bound); + assert_eq!( + world.report("host").unwrap().unwrap()["handlers"][0], + "subtract_planar_cutters" + ); +} + +#[test] +fn mixed_cutter_batch_keeps_cut_faces_and_one_revision() { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 6.0, + breadth: 0.3, + }, + named("profile"), + ) + .unwrap(); + world + .create_operation( + CreatingOperation::Extrude { + profile: "profile".into(), + holes: Vec::new(), + distance: 3.0, + }, + named("wall"), + ) + .unwrap(); + world + .create_primitive( + Primitive::Cuboid { + width: 0.8, + height: 3.0, + depth: 1.0, + }, + named("rect"), + ) + .unwrap(); + world + .transform( + "rect", + Transform::Translate { + offset: [1.5, 0.0, 0.5], + }, + ) + .unwrap(); + world + .create_primitive( + Primitive::Cylinder { + radius: 0.4, + height: 2.0, + }, + named("round"), + ) + .unwrap(); + world + .transform( + "round", + Transform::Rotate { + axis: [1.0, 0.0, 0.0], + degrees: 90.0, + pivot: Some([0.0; 3]), + }, + ) + .unwrap(); + world + .transform( + "round", + Transform::Translate { + offset: [-1.5, 1.5, -1.0], + }, + ) + .unwrap(); + world + .operate( + "wall", + ModifyingOperation::Subtract, + &["rect".into(), "round".into()], + EditScope::Node, + ) + .unwrap(); + let result = world.brep("wall").unwrap(); + result.validate().unwrap(); + assert_eq!(result.revision, 1); + assert!(result + .topology + .faces + .iter() + .any(|face| face.provenance.role == FaceRole::Cut)); + let handlers = &world.report("wall").unwrap().unwrap()["handlers"]; + assert_eq!(handlers[0], "subtract_planar_cutters"); +} diff --git a/main/opengeometry/tests/booleans/coverage_gap.rs b/main/opengeometry/tests/booleans/coverage_gap.rs new file mode 100644 index 0000000..fc4f9ce --- /dev/null +++ b/main/opengeometry/tests/booleans/coverage_gap.rs @@ -0,0 +1,31 @@ +use opengeometry::world_graph::{ErrorCode, ErrorDetails}; +use opengeometry_test_support::scenes::acceptance::{attempt_rail_minus_wall, recut_scene}; +use opengeometry_test_support::scenes::coverage_gap::rail_minus_wall_clearance; + +#[test] +fn rail_minus_wall_returns_coverage_gap_with_clearance_and_leaves_graph_unchanged() { + let mut scene = recut_scene(); + let clearance = rail_minus_wall_clearance(&scene.graph, &scene.rail, &scene.wall); + let accuracy = scene.graph.accuracy().geometric; + assert!(clearance > 10.0 * accuracy, "clearance {clearance}"); + let revision = scene.graph.revision(); + let node_count = scene.graph.node_count(); + let rail_before = scene.graph.brep(&scene.rail).unwrap().to_json().unwrap(); + let wall_before = scene.graph.brep(&scene.wall).unwrap().to_json().unwrap(); + let error = attempt_rail_minus_wall(&mut scene); + assert_eq!(error.error_code(), ErrorCode::CoverageGap, "{error:?}"); + assert_eq!( + error.details(), + ErrorDetails::Operate { + handlers: Vec::new(), + tool_index: 0, + og_ids: vec!["rail-1".into(), "wall-1".into()], + } + ); + assert_eq!(scene.graph.revision(), revision); + assert_eq!(scene.graph.node_count(), node_count); + let rail_after = scene.graph.brep(&scene.rail).unwrap().to_json().unwrap(); + let wall_after = scene.graph.brep(&scene.wall).unwrap().to_json().unwrap(); + assert_eq!(rail_after, rail_before); + assert_eq!(wall_after, wall_before); +} diff --git a/main/opengeometry/tests/booleans/curved_openings.rs b/main/opengeometry/tests/booleans/curved_openings.rs new file mode 100644 index 0000000..c1cb6da --- /dev/null +++ b/main/opengeometry/tests/booleans/curved_openings.rs @@ -0,0 +1,95 @@ +use opengeometry::brep::{Accuracy, Frame3}; +use opengeometry::operations::modifying::boolean::subtract_planar_cutters; +use opengeometry::primitives; +use opengeometry::query::{classify_point, PointClassification}; +use opengeometry::tessellation::tessellate; +#[test] +fn disjoint_planar_and_round_openings_share_a_curved_host_height_level() { + let accuracy = Accuracy { + geometric: 6.3e-8, + intersection: 1.575e-8, + tessellation: 0.01, + exchange: 1e-5, + }; + let host_frame = Frame3 { + origin: [0.0, 0.0, 0.0], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + }; + let point = |radius: f64, angle: f64| [radius * angle.cos(), radius * angle.sin()]; + let host = primitives::arc_edged_extrusion( + "mixed-curved-host".into(), + host_frame, + vec![ + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 3.0, + start_angle: 0.8, + sweep_angle: -1.6, + }, + primitives::ProfileEdge::Line { + from: point(3.0, -0.8), + to: point(2.7, -0.8), + }, + primitives::ProfileEdge::Arc { + center: [0.0, 0.0], + radius: 2.7, + start_angle: -0.8, + sweep_angle: 1.6, + }, + primitives::ProfileEdge::Line { + from: point(2.7, 0.8), + to: point(3.0, 0.8), + }, + ], + 3.0, + accuracy, + ) + .unwrap(); + let rectangle = primitives::cuboid( + "rectangular-opening".into(), + Frame3 { + origin: [2.2, 1.0, 1.1], + x: [1.0, 0.0, 0.0], + y: [0.0, 1.0, 0.0], + z: [0.0, 0.0, 1.0], + }, + [1.0, 0.5, 0.4], + accuracy, + ) + .unwrap(); + let round = primitives::cylinder( + "round-opening".into(), + Frame3 { + origin: [2.65, 1.5, 0.0], + x: [0.0, 0.0, -1.0], + y: [0.0, 1.0, 0.0], + z: [1.0, 0.0, 0.0], + }, + 0.5, + 0.4, + accuracy, + ) + .unwrap(); + for cutters in [ + [rectangle.clone(), round.clone()], + [round.clone(), rectangle.clone()], + ] { + let result = subtract_planar_cutters(&host, &cutters, "mixed-curved-cut".into()).unwrap(); + result.brep.validate().unwrap(); + tessellate(&result.brep, 0.01, 2_000_000).unwrap(); + assert_eq!( + classify_point(&result.brep, [2.6, 1.25, 1.3]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [2.8, 1.5, 0.0]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [2.8, 2.5, 0.0]).unwrap(), + PointClassification::Inside + ); + } +} diff --git a/main/opengeometry/tests/booleans/face_normals.rs b/main/opengeometry/tests/booleans/face_normals.rs new file mode 100644 index 0000000..a95f2eb --- /dev/null +++ b/main/opengeometry/tests/booleans/face_normals.rs @@ -0,0 +1,79 @@ +use opengeometry::brep::{Accuracy, FaceRole, GeometryError, Surface, SurfaceGeometry}; +use opengeometry::{ + brep::Frame3, + operations::modifying::boolean::{boolean_spheres, BooleanOp}, + primitives, +}; +fn sub(a: [f64; 3], b: [f64; 3]) -> [f64; 3] { + std::array::from_fn(|i| a[i] - b[i]) +} +fn dot(a: [f64; 3], b: [f64; 3]) -> f64 { + a[0] * b[0] + a[1] * b[1] + a[2] * b[2] +} +fn unit(a: [f64; 3]) -> Result<[f64; 3], GeometryError> { + let n = dot(a, a).sqrt(); + if n == 0.0 { + return Err(GeometryError::SingularParameterization); + } + Ok(a.map(|v| v / n)) +} +#[test] +fn picked_support_normals_follow_curvature_reversal_and_singularities() { + let accuracy = Accuracy { + geometric: 1e-8, + intersection: 1e-9, + tessellation: 0.01, + exchange: 1e-6, + }; + let frame = Frame3::from_axis([3.0, -2.0, 1.0], [1.0, 2.0, 3.0], [0.0, 1.0, 0.0]).unwrap(); + let sphere = primitives::sphere("host".into(), frame, 2.0, accuracy).unwrap(); + let point = sphere.geometry.surfaces[0].point_at([0.7, 0.3]).unwrap(); + let inward_mesh_point = + std::array::from_fn(|i| frame.origin[i] + (point[i] - frame.origin[i]) * 0.99); + let normal = sphere.face_normal_at(0, inward_mesh_point).unwrap(); + assert!(dot(normal, unit(sub(point, frame.origin)).unwrap()) > 1.0 - 1e-12); + let cutter = primitives::sphere("cutter".into(), frame, 0.5, accuracy).unwrap(); + let cavity = boolean_spheres(&sphere, &cutter, BooleanOp::Subtraction, "cavity".into()) + .unwrap() + .brep; + let face = cavity + .topology + .faces + .iter() + .find(|face| face.provenance.role == FaceRole::Cut) + .unwrap(); + let point = cavity + .geometry + .surface(face.surface) + .unwrap() + .point_at([0.7, 0.3]) + .unwrap(); + assert!( + dot( + cavity.face_normal_at(face.id, point).unwrap(), + unit(sub(point, frame.origin)).unwrap() + ) < -1.0 + 1e-12 + ); + assert!(matches!( + sphere.face_normal_at(u32::MAX, point), + Err(GeometryError::MissingReference { .. }) + )); + assert!(sphere.face_normal_at(0, [f64::NAN, 0.0, 0.0]).is_err()); + let cone = primitives::cone("cone".into(), frame, 1.0, 2.0, accuracy).unwrap(); + let face = cone + .topology + .faces + .iter() + .find(|face| { + matches!( + cone.geometry.surfaces[face.surface as usize], + SurfaceGeometry::Cone { .. } + ) + }) + .unwrap(); + let apex = cone.geometry.surface(face.surface).unwrap().frame().origin; + assert!(matches!( + cone.face_normal_at(face.id, apex), + Err(GeometryError::SingularParameterization) + )); +} diff --git a/main/opengeometry/tests/booleans/main.rs b/main/opengeometry/tests/booleans/main.rs new file mode 100644 index 0000000..ab94a83 --- /dev/null +++ b/main/opengeometry/tests/booleans/main.rs @@ -0,0 +1,9 @@ +mod atomicity; +mod batch; +mod coverage_gap; +mod curved_openings; +mod face_normals; +mod multi_tool; +mod point_classification; +mod routing; +mod support; diff --git a/main/opengeometry/tests/booleans/multi_tool.rs b/main/opengeometry/tests/booleans/multi_tool.rs new file mode 100644 index 0000000..6d6f1de --- /dev/null +++ b/main/opengeometry/tests/booleans/multi_tool.rs @@ -0,0 +1,262 @@ +use opengeometry::world_graph::{ + CreatingOperation, EditScope, ErrorCode, ErrorDetails, ModifyingOperation, Primitive, + Transform, WorldGraph, +}; +use opengeometry_test_support::volume; +use opengeometry_test_support::world_graph::{graph, named}; + +struct Tool { + og_id: &'static str, + size: [f64; 3], + axis: [f64; 3], + degrees: f64, + offset: [f64; 3], +} + +const OVERLAPPING_DOORS: [Tool; 2] = [ + Tool { + og_id: "first", + size: [1.0, 2.1, 1.0], + axis: [0.0, 0.0, 1.0], + degrees: 0.0, + offset: [-2.5, -0.5, 0.0], + }, + Tool { + og_id: "second", + size: [1.0, 2.1, 1.0], + axis: [0.0, 0.0, 1.0], + degrees: 0.0, + offset: [-2.0, -0.5, 0.0], + }, +]; + +const OBLIQUE_OPENINGS: [Tool; 2] = [ + Tool { + og_id: "first", + size: [1.0, 1.0, 1.0], + axis: [0.0, 0.0, 1.0], + degrees: 30.0, + offset: [-2.5, 1.5, 0.0], + }, + Tool { + og_id: "second", + size: [1.0, 1.0, 1.0], + axis: [0.0, 0.0, 1.0], + degrees: 30.0, + offset: [2.5, 1.5, 0.0], + }, +]; + +fn wall_with(tools: &[Tool]) -> WorldGraph { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 10.0, + breadth: 0.3, + }, + named("profile"), + ) + .unwrap(); + world + .create_operation( + CreatingOperation::Extrude { + profile: "profile".into(), + holes: Vec::new(), + distance: 3.0, + }, + named("wall"), + ) + .unwrap(); + for tool in tools { + let [width, height, depth] = tool.size; + world + .create_primitive( + Primitive::Cuboid { + width, + height, + depth, + }, + named(tool.og_id), + ) + .unwrap(); + world + .transform( + tool.og_id, + Transform::Rotate { + axis: tool.axis, + degrees: tool.degrees, + pivot: Some([0.0; 3]), + }, + ) + .unwrap(); + world + .transform( + tool.og_id, + Transform::Translate { + offset: tool.offset, + }, + ) + .unwrap(); + } + world +} + +fn subtract(world: &mut WorldGraph, tools: &[&str]) -> Result<(), ErrorCode> { + world + .operate( + "wall", + ModifyingOperation::Subtract, + &tools + .iter() + .map(|tool| tool.to_string()) + .collect::>(), + EditScope::Node, + ) + .map(|_| ()) + .map_err(|error| error.error_code()) +} + +fn handlers(world: &WorldGraph) -> Vec { + serde_json::from_value(world.report("wall").unwrap().unwrap()["handlers"].clone()).unwrap() +} + +#[test] +fn plain_coverage_gap_batch_falls_back_serially() { + for (tools, expected_volume) in [(OVERLAPPING_DOORS, 8.28), (OBLIQUE_OPENINGS, 8.4)] { + let mut serial = wall_with(&tools); + let mut expected_handlers = Vec::new(); + for tool in &tools { + subtract(&mut serial, &[tool.og_id]).unwrap(); + expected_handlers.extend(handlers(&serial)); + } + let mut world = wall_with(&tools); + subtract(&mut world, &["first", "second"]).unwrap(); + let result = world.brep("wall").unwrap(); + result.validate().unwrap(); + assert_eq!(result.revision, 1); + assert_eq!(handlers(&world), expected_handlers); + let measured = volume::estimate(&result, 0.01); + assert!( + (measured.value - expected_volume).abs() <= measured.error_bound, + "{} against {expected_volume}", + measured.value + ); + } +} + +#[test] +fn mixed_cutter_batch_gap_never_falls_back() { + let rotated = |og_id, x| Tool { + og_id, + size: [0.6, 1.0, 1.0], + axis: [0.0, 1.0, 0.0], + degrees: 30.0, + offset: [x, 1.5, 0.0], + }; + let mut world = wall_with(&[rotated("first", -2.5), rotated("second", 0.0)]); + world + .create_primitive( + Primitive::Cylinder { + radius: 0.4, + height: 2.0, + }, + named("round"), + ) + .unwrap(); + world + .transform( + "round", + Transform::Rotate { + axis: [1.0, 0.0, 0.0], + degrees: 90.0, + pivot: Some([0.0; 3]), + }, + ) + .unwrap(); + world + .transform( + "round", + Transform::Translate { + offset: [2.5, 1.5, -1.0], + }, + ) + .unwrap(); + let revision = world.revision(); + let node_count = world.node_count(); + let brep_before = world.brep("wall").unwrap().to_json().unwrap(); + let report_before = world.report("wall").unwrap().cloned(); + let tools = ["first", "second", "round"].map(String::from); + let error = world + .operate( + "wall", + ModifyingOperation::Subtract, + &tools, + EditScope::Node, + ) + .unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::CoverageGap); + assert!( + error.message().contains("mixed cutter batch"), + "{}", + error.message() + ); + assert_eq!( + error.details(), + ErrorDetails::Operate { + handlers: Vec::new(), + tool_index: 0, + og_ids: vec![ + "wall".into(), + "first".into(), + "second".into(), + "round".into() + ], + } + ); + assert_eq!(world.revision(), revision); + assert_eq!(world.node_count(), node_count); + assert_eq!(world.brep("wall").unwrap().to_json().unwrap(), brep_before); + assert_eq!(world.report("wall").unwrap().cloned(), report_before); +} + +#[test] +fn intermediate_empty_two_tool_intersect_gives_empty_result() { + let mut world = graph(); + for (og_id, x) in [("target", 0.0), ("far", 10.0), ("near", 0.5)] { + world + .create_primitive( + Primitive::Cuboid { + width: 2.0, + height: 2.0, + depth: 2.0, + }, + named(og_id), + ) + .unwrap(); + world + .transform( + og_id, + Transform::Translate { + offset: [x, 0.0, 0.0], + }, + ) + .unwrap(); + } + let revision = world.revision(); + let brep_before = world.brep("target").unwrap().to_json().unwrap(); + let error = world + .operate( + "target", + ModifyingOperation::Intersect, + &["far".into(), "near".into()], + EditScope::Node, + ) + .unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::EmptyResult, "{error:?}"); + assert_eq!(world.revision(), revision); + assert_eq!( + world.brep("target").unwrap().to_json().unwrap(), + brep_before + ); +} diff --git a/main/opengeometry/tests/booleans/point_classification.rs b/main/opengeometry/tests/booleans/point_classification.rs new file mode 100644 index 0000000..a89485f --- /dev/null +++ b/main/opengeometry/tests/booleans/point_classification.rs @@ -0,0 +1,78 @@ +use opengeometry::brep::Frame3; +use opengeometry::operations::modifying::boolean::{boolean_brep, BooleanOp}; +use opengeometry::primitives; +use opengeometry::query::{classify_point, PointClassification}; +use opengeometry_test_support::accuracy::accuracy; + +#[test] +fn analytic_ray_classification_covers_all_surface_families() { + let bodies = [ + ( + primitives::cuboid("box".into(), Frame3::IDENTITY, [2.0; 3], accuracy(1e-5)).unwrap(), + [1.0, 1.0, 1.0], + [3.0, 3.0, 3.0], + ), + ( + primitives::sphere("sphere".into(), Frame3::IDENTITY, 1.0, accuracy(1e-5)).unwrap(), + [0.0; 3], + [2.0, 0.0, 0.0], + ), + ( + primitives::cylinder( + "cylinder".into(), + Frame3::IDENTITY, + 1.0, + 2.0, + accuracy(1e-5), + ) + .unwrap(), + [0.0, 0.0, 1.0], + [2.0, 0.0, 1.0], + ), + ( + primitives::cone("cone".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy(1e-5)).unwrap(), + [0.0, 0.0, 1.0], + [2.0, 0.0, 1.0], + ), + ( + primitives::torus("torus".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy(1e-5)).unwrap(), + [3.0, 0.0, 0.0], + [0.0; 3], + ), + ]; + for (body, inside, outside) in bodies { + assert_eq!( + classify_point(&body, inside).unwrap(), + PointClassification::Inside, + "{}", + body.id + ); + assert_eq!( + classify_point(&body, outside).unwrap(), + PointClassification::Outside, + "{}", + body.id + ); + } +} + +#[test] +fn cavity_shell_uses_regularized_material_occupancy() { + let host = + primitives::torus("host".into(), Frame3::IDENTITY, 3.0, 1.0, accuracy(1e-5)).unwrap(); + let cutter = + primitives::torus("cutter".into(), Frame3::IDENTITY, 3.0, 0.4, accuracy(1e-5)).unwrap(); + let result = boolean_brep(&host, &cutter, BooleanOp::Subtraction, "shell".into()).unwrap(); + assert_eq!( + classify_point(&result.brep, [3.0, 0.0, 0.0]).unwrap(), + PointClassification::Outside + ); + assert_eq!( + classify_point(&result.brep, [3.7, 0.0, 0.0]).unwrap(), + PointClassification::Inside + ); + assert_eq!( + classify_point(&result.brep, [0.0; 3]).unwrap(), + PointClassification::Outside + ); +} diff --git a/main/opengeometry/tests/booleans/routing.rs b/main/opengeometry/tests/booleans/routing.rs new file mode 100644 index 0000000..7d0ec61 --- /dev/null +++ b/main/opengeometry/tests/booleans/routing.rs @@ -0,0 +1,198 @@ +use super::support::cuboid; +use opengeometry::operations::modifying::boolean::{boolean_brep_outcome_with_handlers, BooleanOp}; +use opengeometry::world_graph::{ + CreateOptions, CreatingOperation, EditScope, ModifyingOperation, Primitive, Transform, +}; +use opengeometry_test_support::world_graph::{graph, named}; + +#[test] +fn sweep_result_boolean_routing_is_pinned() { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 2.0, + breadth: 2.0, + }, + named("profile"), + ) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: vec![[0.0; 3], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]], + closed: false, + }, + named("path"), + ) + .unwrap(); + world + .create_operation( + CreatingOperation::Sweep { + profile: "profile".into(), + path: "path".into(), + }, + named("sweep"), + ) + .unwrap(); + world + .create_primitive( + Primitive::Cuboid { + width: 1.0, + height: 1.0, + depth: 1.0, + }, + named("tool"), + ) + .unwrap(); + let sweep = world.brep("sweep").unwrap(); + let tool = world.brep("tool").unwrap(); + let (result, handlers) = boolean_brep_outcome_with_handlers( + &sweep, + &tool, + BooleanOp::Subtraction, + "sweep-cut".into(), + ); + assert_eq!(handlers, ["subtract_planar_polyhedra"]); + result.unwrap().brep.validate().unwrap(); +} + +#[test] +fn corotated_levels_keep_box_handler_and_replay_bytes() { + let recipe = |degrees: f64| { + let mut world = graph(); + world.create_system_assembly(named("level")).unwrap(); + world + .create_primitive( + cuboid(2.0), + CreateOptions { + parent: Some("level".into()), + ..named("host") + }, + ) + .unwrap(); + world + .create_primitive( + cuboid(2.0), + CreateOptions { + parent: Some("level".into()), + ..named("tool") + }, + ) + .unwrap(); + world + .transform( + "tool", + Transform::Translate { + offset: [1.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .transform( + "level", + Transform::Rotate { + axis: [0.0, 1.0, 0.0], + degrees, + pivot: Some([0.0, 0.0, 0.0]), + }, + ) + .unwrap(); + world + .operate( + "host", + ModifyingOperation::Subtract, + &["tool".into()], + EditScope::Node, + ) + .unwrap(); + ( + world.brep("host").unwrap().to_json().unwrap(), + world.report("host").unwrap().unwrap()["handlers"].clone(), + ) + }; + let original = recipe(0.0); + assert_eq!(original, recipe(90.0)); + assert_eq!(original, recipe(30.0)); + assert_eq!(original.1, serde_json::json!(["boolean_boxes"])); +} + +#[test] +fn corotated_levels_keep_wall_handler_and_replay_bytes() { + let recipe = |degrees: f64| { + let mut world = graph(); + world.create_system_assembly(named("level")).unwrap(); + world + .create_primitive( + Primitive::Rectangle { + width: 4.0, + breadth: 0.5, + }, + CreateOptions { + parent: Some("level".into()), + ..named("profile") + }, + ) + .unwrap(); + world + .create_operation( + CreatingOperation::Extrude { + profile: "profile".into(), + holes: Vec::new(), + distance: 3.0, + }, + CreateOptions { + parent: Some("level".into()), + ..named("wall") + }, + ) + .unwrap(); + world + .create_primitive( + Primitive::Cuboid { + width: 0.8, + height: 3.0, + depth: 1.0, + }, + CreateOptions { + parent: Some("level".into()), + ..named("cutter") + }, + ) + .unwrap(); + world + .transform( + "cutter", + Transform::Translate { + offset: [0.0, 0.0, 0.5], + }, + ) + .unwrap(); + world + .transform( + "level", + Transform::Rotate { + axis: [0.0, 1.0, 0.0], + degrees, + pivot: Some([0.0; 3]), + }, + ) + .unwrap(); + world + .operate( + "wall", + ModifyingOperation::Subtract, + &["cutter".into()], + EditScope::Node, + ) + .unwrap(); + ( + world.brep("wall").unwrap().to_json().unwrap(), + world.report("wall").unwrap().unwrap()["handlers"].clone(), + ) + }; + let original = recipe(0.0); + assert_eq!(original, recipe(90.0)); + assert_eq!(original, recipe(30.0)); + assert_eq!(original.1, serde_json::json!(["boolean_rectilinear"])); +} diff --git a/main/opengeometry/tests/booleans/support.rs b/main/opengeometry/tests/booleans/support.rs new file mode 100644 index 0000000..267bbec --- /dev/null +++ b/main/opengeometry/tests/booleans/support.rs @@ -0,0 +1,9 @@ +use opengeometry::world_graph::Primitive; + +pub(super) fn cuboid(width: f64) -> Primitive { + Primitive::Cuboid { + width, + height: 2.0, + depth: 2.0, + } +} diff --git a/main/opengeometry/tests/cases/batch.rs b/main/opengeometry/tests/cases/batch.rs new file mode 100644 index 0000000..aca6005 --- /dev/null +++ b/main/opengeometry/tests/cases/batch.rs @@ -0,0 +1,80 @@ +use crate::support::{case_files, read_fixture}; +use opengeometry::brep::BrepEnvelope; +use opengeometry::operations::modifying::boolean::{ + multi_tool_boolean, subtract_planar_cutters_with_handlers, BooleanOp, MultiToolOutcome, +}; +use opengeometry_test_support::stored; +use serde_json::json; +use std::collections::BTreeSet; + +#[test] +fn batch_subtraction_matches_stored_brep_and_handlers() { + let files = case_files("batch-matrix", |name| !name.contains(".fallback.")); + assert_eq!(files.len(), 23); + for path in files { + let name = path.file_stem().unwrap().to_str().unwrap(); + let fixture = read_fixture(&path); + let host: BrepEnvelope = serde_json::from_value(fixture["host"].clone()).unwrap(); + let cutters: Vec = + serde_json::from_value(fixture["cutters"].clone()).unwrap(); + match subtract_planar_cutters_with_handlers(&host, &cutters, format!("batch-{name}")) { + Ok((result, handlers)) => { + result.brep.validate().unwrap(); + let actual = json!({"brep": serde_json::to_value(result.brep).unwrap()}); + if stored::compared() { + assert_eq!(actual, fixture["result"], "{name}"); + } + assert_eq!(json!(handlers), fixture["handlers"], "{name}"); + } + Err(error) => assert_eq!(json!({"error": error}), fixture["result"], "{name}"), + } + } +} + +#[test] +fn fallback_rows_parse() { + let expected = BTreeSet::from(["cutters", "handlers", "host", "result"]); + let files = case_files("batch-matrix", |name| name.contains(".fallback.")); + assert_eq!(files.len(), 3); + for path in files { + let fixture = read_fixture(&path); + let keys = fixture + .as_object() + .unwrap() + .keys() + .map(String::as_str) + .collect::>(); + assert_eq!(keys, expected, "{}", path.display()); + } +} + +#[test] +fn fallback_rows_match_stored_serial_results() { + for path in case_files("batch-matrix", |name| name.contains(".fallback.")) { + let stem = path.file_stem().unwrap().to_str().unwrap(); + let name = stem.strip_suffix(".fallback").unwrap(); + let fixture = read_fixture(&path); + let host: BrepEnvelope = serde_json::from_value(fixture["host"].clone()).unwrap(); + let cutters: Vec = + serde_json::from_value(fixture["cutters"].clone()).unwrap(); + let MultiToolOutcome { + result, handlers, .. + } = multi_tool_boolean( + &host, + &cutters, + BooleanOp::Subtraction, + format!("batch-{name}"), + ); + let actual = match result { + Ok(result) => { + result.brep.validate().unwrap(); + json!({"brep": serde_json::to_value(result.brep).unwrap()}) + } + Err(error) => json!({"error": error}), + }; + if stored::compared() || fixture["result"].get("error").is_some() { + assert_eq!(actual, fixture["result"], "{name}"); + } + assert_eq!(json!(handlers), fixture["handlers"], "{name}"); + } +} diff --git a/main/opengeometry/tests/cases/builders.rs b/main/opengeometry/tests/cases/builders.rs new file mode 100644 index 0000000..65b3754 --- /dev/null +++ b/main/opengeometry/tests/cases/builders.rs @@ -0,0 +1,183 @@ +use opengeometry::brep::{Accuracy, BodyType, CurveGeometry, Frame3}; +use opengeometry::primitives; +use opengeometry_test_support::stored; +use serde_json::json; + +#[test] +fn primitive_builders_match_stored_json() { + let accuracy = Accuracy { + geometric: 1e-8, + intersection: 1e-9, + tessellation: 0.01, + exchange: 1e-6, + }; + let frame = Frame3 { + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + ..Frame3::IDENTITY + }; + let bodies = [ + ( + "cuboid", + primitives::cuboid("cuboid".into(), frame, [2.0, 1.0, 3.0], accuracy).unwrap(), + ), + ( + "cylinder", + primitives::cylinder("cylinder".into(), frame, 1.0, 2.0, accuracy).unwrap(), + ), + ( + "sphere", + primitives::sphere("sphere".into(), frame, 1.0, accuracy).unwrap(), + ), + ( + "cone", + primitives::cone("cone".into(), frame, 1.0, 2.0, accuracy).unwrap(), + ), + ( + "frustum", + primitives::frustum("frustum".into(), frame, 1.0, 0.4, 2.0, accuracy).unwrap(), + ), + ( + "torus", + primitives::torus("torus".into(), frame, 2.0, 0.5, accuracy).unwrap(), + ), + ( + "annular-cylinder", + primitives::annular_cylinder("annular-cylinder".into(), frame, 0.4, 1.0, 2.0, accuracy) + .unwrap(), + ), + ( + "cylinder-with-hole", + primitives::cylinder_with_circular_hole( + "cylinder-with-hole".into(), + frame, + frame, + 0.4, + 1.0, + 2.0, + accuracy, + ) + .unwrap(), + ), + ( + "circle", + primitives::arc_wire( + "circle".into(), + CurveGeometry::Circle { frame, radius: 1.0 }, + 0.0, + std::f64::consts::TAU, + accuracy, + ) + .unwrap(), + ), + ]; + for (name, body) in bodies { + assert_eq!( + body.body_type().unwrap(), + if name == "circle" { + BodyType::Wire + } else { + BodyType::Solid + } + ); + if stored::compared() { + let expected = match name { + "cuboid" => include_str!("../fixtures/cases/cuboid.brep.json"), + "cylinder" => include_str!("../fixtures/cases/cylinder.brep.json"), + "sphere" => include_str!("../fixtures/cases/sphere.brep.json"), + "cone" => include_str!("../fixtures/cases/cone.brep.json"), + "frustum" => include_str!("../fixtures/cases/frustum.brep.json"), + "torus" => include_str!("../fixtures/cases/torus.brep.json"), + "annular-cylinder" => include_str!("../fixtures/cases/annular-cylinder.brep.json"), + "cylinder-with-hole" => { + include_str!("../fixtures/cases/cylinder-with-hole.brep.json") + } + "circle" => include_str!("../fixtures/cases/circle.brep.json"), + _ => unreachable!(), + }; + assert_eq!(body.to_json().unwrap(), expected, "{name}"); + } + } +} + +#[test] +fn extrusions_match_stored_json() { + let accuracy = Accuracy { + geometric: 1e-8, + intersection: 1e-9, + tessellation: 0.01, + exchange: 1e-6, + }; + let frame = Frame3 { + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + ..Frame3::IDENTITY + }; + let outer = json!([ + { "kind": "line", "from": [-2.0, -2.0], "to": [2.0, -2.0] }, + { "kind": "arc", "center": [2.0, 0.0], "radius": 2.0, "start_angle": -std::f64::consts::FRAC_PI_2, "sweep_angle": std::f64::consts::PI }, + { "kind": "line", "from": [2.0, 2.0], "to": [-2.0, 2.0] }, + { "kind": "line", "from": [-2.0, 2.0], "to": [-2.0, -2.0] } + ]); + let hole = json!([ + { "kind": "line", "from": [-0.5, -0.5], "to": [0.5, -0.5] }, + { "kind": "line", "from": [0.5, -0.5], "to": [0.5, 0.5] }, + { "kind": "line", "from": [0.5, 0.5], "to": [-0.5, 0.5] }, + { "kind": "line", "from": [-0.5, 0.5], "to": [-0.5, -0.5] } + ]); + let bodies = [ + ( + "linear-extrusion", + primitives::linear_extrusion( + "linear-extrusion".into(), + frame, + vec![[-1.0, -0.5], [1.0, -0.5], [1.0, 0.5], [-1.0, 0.5]], + Vec::new(), + 2.0, + accuracy, + ) + .unwrap(), + ), + ( + "arc-edged-extrusion", + primitives::arc_edged_extrusion( + "arc-edged-extrusion".into(), + frame, + serde_json::from_value(outer.clone()).unwrap(), + 2.0, + accuracy, + ) + .unwrap(), + ), + ( + "arc-edged-extrusion-with-holes", + primitives::arc_edged_extrusion_with_holes( + "arc-edged-extrusion-with-holes".into(), + frame, + serde_json::from_value(outer).unwrap(), + vec![serde_json::from_value(hole).unwrap()], + 2.0, + accuracy, + ) + .unwrap(), + ), + ]; + for (name, body) in bodies { + assert_eq!(body.body_type().unwrap(), BodyType::Solid); + if stored::compared() { + let expected = match name { + "linear-extrusion" => include_str!("../fixtures/cases/linear-extrusion.brep.json"), + "arc-edged-extrusion" => { + include_str!("../fixtures/cases/arc-edged-extrusion.brep.json") + } + "arc-edged-extrusion-with-holes" => { + include_str!("../fixtures/cases/arc-edged-extrusion-with-holes.brep.json") + } + _ => unreachable!(), + }; + assert_eq!(body.to_json().unwrap(), expected, "{name}"); + } + } +} diff --git a/main/opengeometry/tests/cases/main.rs b/main/opengeometry/tests/cases/main.rs new file mode 100644 index 0000000..7747fa0 --- /dev/null +++ b/main/opengeometry/tests/cases/main.rs @@ -0,0 +1,6 @@ +mod batch; +mod builders; +mod matrix; +mod support; +mod tessellation; +mod validity; diff --git a/main/opengeometry/tests/cases/matrix.rs b/main/opengeometry/tests/cases/matrix.rs new file mode 100644 index 0000000..0e94664 --- /dev/null +++ b/main/opengeometry/tests/cases/matrix.rs @@ -0,0 +1,223 @@ +use opengeometry::brep::{Accuracy, BrepEnvelope, Frame3}; +use opengeometry::operations::modifying::boolean::{ + boolean_brep_outcome_with_handlers, boolean_brep_with_handlers, BooleanOp, +}; +use opengeometry::primitives; +use opengeometry_test_support::{stored, volume}; +use serde_json::json; + +#[test] +fn boolean_fixtures_match_stored_brep_and_handlers() { + let accuracy = Accuracy { + geometric: 1e-8, + intersection: 1e-9, + tessellation: 0.01, + exchange: 1e-6, + }; + let frame = Frame3 { + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + ..Frame3::IDENTITY + }; + let host = primitives::cuboid("box-host".into(), frame, [3.0; 3], accuracy).unwrap(); + let cutter = primitives::cuboid( + "box-cutter".into(), + Frame3 { + origin: frame.point([1.5, 0.5, 0.5]), + ..frame + }, + [3.0; 3], + accuracy, + ) + .unwrap(); + let inner = primitives::cuboid( + "box-inner".into(), + Frame3 { + origin: frame.point([1.0; 3]), + ..frame + }, + [1.0; 3], + accuracy, + ) + .unwrap(); + let sphere = primitives::sphere("sphere-host".into(), frame, 2.0, accuracy).unwrap(); + let sphere_cutter = primitives::sphere( + "sphere-cutter".into(), + Frame3 { + origin: frame.point([0.7, 0.0, 0.0]), + ..frame + }, + 1.0, + accuracy, + ) + .unwrap(); + macro_rules! check { + ($name:literal, $a:expr, $b:expr, $operation:expr) => {{ + let (result, handlers) = + boolean_brep_with_handlers($a, $b, $operation, $name.into()).unwrap(); + let json = result.brep.to_json().unwrap(); + if stored::compared() { + assert_eq!( + json, + include_str!(concat!("../fixtures/cases/", $name, ".brep.json")), + $name + ); + } + let expected: Vec = serde_json::from_str(include_str!(concat!( + "../fixtures/cases/", + $name, + ".handlers.json" + ))) + .unwrap(); + assert_eq!(handlers, expected, $name); + }}; + } + check!("box-union", &host, &cutter, BooleanOp::Union); + check!("box-intersection", &host, &cutter, BooleanOp::Intersection); + check!("box-cut", &host, &cutter, BooleanOp::Subtraction); + check!("box-cavity", &host, &inner, BooleanOp::Subtraction); + check!( + "sphere-cut", + &sphere, + &sphere_cutter, + BooleanOp::Subtraction + ); +} + +#[test] +fn boolean_support_matrix_matches_stored_outcomes_and_handlers() { + let directory = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/cases/boolean-matrix"); + let mut files = std::fs::read_dir(directory) + .unwrap() + .map(|entry| entry.unwrap().path()) + .filter(|path| { + path.extension() + .is_some_and(|extension| extension == "json") + }) + .collect::>(); + files.sort(); + assert_eq!(files.len(), 18); + for path in files { + let name = path.file_stem().unwrap().to_str().unwrap(); + let fixture: serde_json::Value = + serde_json::from_slice(&std::fs::read(&path).unwrap()).unwrap(); + let a: BrepEnvelope = serde_json::from_value(fixture["a"].clone()).unwrap(); + let b: BrepEnvelope = serde_json::from_value(fixture["b"].clone()).unwrap(); + let operation: BooleanOp = serde_json::from_value(fixture["operation"].clone()).unwrap(); + let (result, handlers) = + boolean_brep_outcome_with_handlers(&a, &b, operation, format!("matrix-{name}")); + let actual = match result { + Ok(result) => { + result.brep.validate().unwrap(); + let expected_volume = match name { + "box-union" => Some(27.0), + "box-intersection" => Some(8.0), + "box-subtract" => Some(19.0), + "wall-rectilinear" | "planar-extrusions" => Some(4.8), + "wall-round" => Some(6.0 - std::f64::consts::PI / 8.0), + "box-round" => Some(27.0 - 3.0 * std::f64::consts::PI * 0.4 * 0.4), + "cylinder-coaxial" => Some(3.0 * std::f64::consts::PI * (1.0 - 0.4 * 0.4)), + "sphere-contained" => Some(10.5 * std::f64::consts::PI), + "cone-contained" => Some(std::f64::consts::PI * (1.0 - 0.04 / 3.0)), + "torus-contained" => Some(1.28 * std::f64::consts::PI.powi(2)), + _ => None, + }; + if let Some(expected) = expected_volume { + let measured = volume::estimate(&result.brep, 0.01); + assert!( + (measured.value - expected).abs() <= measured.error_bound, + "{name}: measured={} expected={} bound={}", + measured.value, + expected, + measured.error_bound, + ); + } + json!({"brep": serde_json::to_value(result.brep).unwrap()}) + } + Err(error) => json!({"error": serde_json::to_value(error).unwrap()}), + }; + if stored::compared() || fixture["result"].get("error").is_some() { + assert_eq!(actual, fixture["result"], "{name}"); + } + assert_eq!(json!(handlers), fixture["handlers"], "{name}"); + } +} + +#[test] +fn i2_uncovered_rotated_box_returns_coverage_gap_without_mesh_fallback() { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/cases/boolean-matrix/rotated-box-union.json"); + let fixture: serde_json::Value = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); + let a: BrepEnvelope = serde_json::from_value(fixture["a"].clone()).unwrap(); + let b: BrepEnvelope = serde_json::from_value(fixture["b"].clone()).unwrap(); + let nearest_vertex_to_plane = |vertices: &BrepEnvelope, planes: &BrepEnvelope| { + vertices + .topology + .vertices + .iter() + .flat_map(|vertex| { + planes.geometry.surfaces.iter().filter_map(move |surface| { + if let opengeometry::brep::SurfaceGeometry::Plane { frame } = surface { + let delta = std::array::from_fn::<_, 3, _>(|i| { + vertex.position[i] - frame.origin[i] + }); + Some((0..3).map(|i| delta[i] * frame.z[i]).sum::().abs()) + } else { + None + } + }) + }) + .fold(f64::INFINITY, f64::min) + }; + let clearance = nearest_vertex_to_plane(&a, &b).min(nearest_vertex_to_plane(&b, &a)); + assert!( + clearance > 10.0 * a.accuracy.geometric, + "clearance={clearance}" + ); + let (result, handlers) = boolean_brep_outcome_with_handlers( + &a, + &b, + BooleanOp::Union, + "matrix-rotated-box-union".into(), + ); + assert!(matches!( + result, + Err(opengeometry::brep::GeometryError::CoverageGap { .. }) + )); + assert_eq!(json!(handlers), fixture["handlers"]); +} + +#[test] +fn parallel_boolean_candidates_have_identical_outputs() { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/cases/boolean-matrix/wall-round.json"); + let fixture: serde_json::Value = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); + let a: BrepEnvelope = serde_json::from_value(fixture["a"].clone()).unwrap(); + let b: BrepEnvelope = serde_json::from_value(fixture["b"].clone()).unwrap(); + let outputs = std::thread::scope(|scope| { + let first = scope.spawn(|| { + let (result, handlers) = boolean_brep_outcome_with_handlers( + &a, + &b, + BooleanOp::Subtraction, + "matrix-wall-round".into(), + ); + (result.unwrap().brep.to_json().unwrap(), handlers) + }); + let second = scope.spawn(|| { + let (result, handlers) = boolean_brep_outcome_with_handlers( + &a, + &b, + BooleanOp::Subtraction, + "matrix-wall-round".into(), + ); + (result.unwrap().brep.to_json().unwrap(), handlers) + }); + (first.join().unwrap(), second.join().unwrap()) + }); + let (first, second) = outputs; + assert_eq!(first, second); + assert_eq!(json!(first.1), fixture["handlers"]); +} diff --git a/main/opengeometry/tests/cases/support.rs b/main/opengeometry/tests/cases/support.rs new file mode 100644 index 0000000..e4cbe43 --- /dev/null +++ b/main/opengeometry/tests/cases/support.rs @@ -0,0 +1,22 @@ +use serde_json::Value; +use std::path::{Path, PathBuf}; + +pub(super) fn case_files(folder: &str, keep: impl Fn(&str) -> bool) -> Vec { + let directory = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/cases") + .join(folder); + let mut files = std::fs::read_dir(directory) + .unwrap() + .map(|entry| entry.unwrap().path()) + .filter(|path| { + let name = path.file_name().unwrap().to_string_lossy(); + name.ends_with(".json") && keep(&name) + }) + .collect::>(); + files.sort(); + files +} + +pub(super) fn read_fixture(path: &Path) -> Value { + serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap() +} diff --git a/main/opengeometry/tests/cases/tessellation.rs b/main/opengeometry/tests/cases/tessellation.rs new file mode 100644 index 0000000..c765b9e --- /dev/null +++ b/main/opengeometry/tests/cases/tessellation.rs @@ -0,0 +1,92 @@ +use opengeometry::brep::BrepEnvelope; +use opengeometry::tessellation::tessellate; +use opengeometry_test_support::stored; +use serde_json::json; + +#[test] +fn primitive_tessellation_matches_stored_fixtures() { + macro_rules! check { + ($name:literal) => {{ + let body = BrepEnvelope::from_json(include_str!(concat!("../fixtures/cases/", $name, ".brep.json"))).unwrap(); + let mesh = tessellate(&body, 0.01, 2_000_000).unwrap(); + let actual = json!({ + "positions": mesh.positions, + "normals": mesh.normals, + "indices": mesh.indices, + "triangleFaceIds": mesh.triangle_face_ids, + "outlinePositions": mesh.outline_positions, + "outlineEdgeIds": mesh.outline_edge_ids, + "revision": mesh.revision, + "achievedDeflection": mesh.achieved_deflection, + }); + if stored::compared() { + let expected: serde_json::Value = serde_json::from_str(include_str!(concat!("../fixtures/cases/", $name, ".tess.json"))).unwrap(); + assert_eq!(actual, expected, "{}", $name); + } + }}; + } + check!("cuboid"); + check!("cylinder"); + check!("sphere"); + check!("cone"); + check!("frustum"); + check!("torus"); + check!("annular-cylinder"); + check!("cylinder-with-hole"); + check!("circle"); + check!("linear-extrusion"); + check!("arc-edged-extrusion"); + check!("arc-edged-extrusion-with-holes"); +} + +#[test] +fn i7_repeated_tessellation_is_bitwise_identical() { + for source in [ + include_str!("../fixtures/cases/cylinder.brep.json"), + include_str!("../fixtures/cases/torus.brep.json"), + include_str!("../fixtures/cases/arc-edged-extrusion.brep.json"), + ] { + let body = BrepEnvelope::from_json(source).unwrap(); + let first = tessellate(&body, 0.01, 2_000_000).unwrap(); + let second = tessellate(&body, 0.01, 2_000_000).unwrap(); + assert_eq!( + first + .positions + .iter() + .map(|value| value.to_bits()) + .collect::>(), + second + .positions + .iter() + .map(|value| value.to_bits()) + .collect::>() + ); + assert_eq!( + first + .normals + .iter() + .map(|value| value.to_bits()) + .collect::>(), + second + .normals + .iter() + .map(|value| value.to_bits()) + .collect::>() + ); + assert_eq!(first.indices, second.indices); + assert_eq!(first.triangle_face_ids, second.triangle_face_ids); + assert_eq!( + first + .outline_positions + .iter() + .map(|value| value.to_bits()) + .collect::>(), + second + .outline_positions + .iter() + .map(|value| value.to_bits()) + .collect::>() + ); + assert_eq!(first.outline_edge_ids, second.outline_edge_ids); + } +} diff --git a/main/opengeometry/tests/cases/validity.rs b/main/opengeometry/tests/cases/validity.rs new file mode 100644 index 0000000..c457bb4 --- /dev/null +++ b/main/opengeometry/tests/cases/validity.rs @@ -0,0 +1,29 @@ +use opengeometry::brep::BrepEnvelope; + +#[test] +fn stored_brep_json_round_trips_byte_identically() { + macro_rules! check { + ($name:literal) => {{ + let source = include_str!(concat!("../fixtures/cases/", $name, ".brep.json")); + let body = BrepEnvelope::from_json(source).expect($name); + assert_eq!(body.to_json().expect($name), source, "{}", $name); + }}; + } + check!("cuboid"); + check!("cylinder"); + check!("sphere"); + check!("cone"); + check!("frustum"); + check!("torus"); + check!("annular-cylinder"); + check!("cylinder-with-hole"); + check!("circle"); + check!("linear-extrusion"); + check!("arc-edged-extrusion"); + check!("arc-edged-extrusion-with-holes"); + check!("box-union"); + check!("box-intersection"); + check!("box-cut"); + check!("box-cavity"); + check!("sphere-cut"); +} diff --git a/main/opengeometry/tests/creating/extrude.rs b/main/opengeometry/tests/creating/extrude.rs new file mode 100644 index 0000000..084d83c --- /dev/null +++ b/main/opengeometry/tests/creating/extrude.rs @@ -0,0 +1,321 @@ +use super::support::extrude; +use opengeometry::world_graph::{ + CreateOptions, CreatingOperation, ErrorCode, Primitive, Transform, +}; +use opengeometry_test_support::volume; +use opengeometry_test_support::world_graph::{graph, named}; + +#[test] +fn rectangle_extrusion_has_independent_volume() { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 2.0, + breadth: 3.0, + }, + named("profile"), + ) + .unwrap(); + world + .create_operation(extrude("profile", 4.0), named("solid")) + .unwrap(); + let measured = volume::estimate(&world.brep("solid").unwrap(), 0.001); + assert!((measured.value - 24.0).abs() <= measured.error_bound); +} + +#[test] +fn circle_extrusion_matches_cylinder_builder() { + let mut world = graph(); + world + .create_primitive(Primitive::Circle { radius: 2.0 }, named("profile")) + .unwrap(); + world + .create_operation(extrude("profile", 3.0), named("solid")) + .unwrap(); + world + .create_primitive( + Primitive::Cylinder { + radius: 2.0, + height: 3.0, + }, + named("cylinder"), + ) + .unwrap(); + let a = world.brep("solid").unwrap(); + let b = world.brep("cylinder").unwrap(); + let mut a = (*a).clone(); + let mut b = (*b).clone(); + a.id = "same".into(); + b.id = "same".into(); + for body in [&mut a, &mut b] { + for face in &mut body.topology.faces { + for source in &mut face.provenance.sources { + source.entity = "same".into(); + source.body = "same".into(); + } + } + } + assert_eq!(a.to_json().unwrap(), b.to_json().unwrap()); +} + +#[test] +fn negative_extrusion_and_circular_hole_have_expected_volume() { + let mut world = graph(); + world + .create_primitive(Primitive::Circle { radius: 2.0 }, named("outer")) + .unwrap(); + world + .create_primitive(Primitive::Circle { radius: 1.0 }, named("hole")) + .unwrap(); + world + .create_operation( + CreatingOperation::Extrude { + profile: "outer".into(), + holes: vec!["hole".into()], + distance: -3.0, + }, + named("solid"), + ) + .unwrap(); + let measured = volume::estimate(&world.brep("solid").unwrap(), 0.001); + assert!((measured.value - 9.0 * std::f64::consts::PI).abs() <= measured.error_bound); + let bounds = world.bounds("solid").unwrap().unwrap(); + assert!((bounds[1] + 3.0).abs() < 2e-8); + assert!(bounds[4] < 2e-8); +} + +#[test] +fn rectangle_with_round_hole_uses_curved_extrusion() { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 4.0, + breadth: 4.0, + }, + named("outer"), + ) + .unwrap(); + world + .create_primitive(Primitive::Circle { radius: 1.0 }, named("hole")) + .unwrap(); + world + .create_operation( + CreatingOperation::Extrude { + profile: "outer".into(), + holes: vec!["hole".into()], + distance: 2.0, + }, + named("solid"), + ) + .unwrap(); + let measured = volume::estimate(&world.brep("solid").unwrap(), 0.001); + assert!((measured.value - 2.0 * (16.0 - std::f64::consts::PI)).abs() <= measured.error_bound); +} + +#[test] +fn tilted_polyline_extrudes_along_its_normal() { + let mut world = graph(); + world + .create_primitive( + Primitive::Polyline { + points: vec![ + [0.0, 0.0, 0.0], + [2.0, 0.0, 0.0], + [2.0, 3.0, 0.0], + [0.0, 3.0, 0.0], + ], + closed: true, + }, + named("profile"), + ) + .unwrap(); + world + .create_operation(extrude("profile", 4.0), named("solid")) + .unwrap(); + let measured = volume::estimate(&world.brep("solid").unwrap(), 0.001); + assert!((measured.value - 24.0).abs() <= measured.error_bound); + let bounds = world.bounds("solid").unwrap().unwrap(); + assert!(bounds[2] >= -2e-8, "{bounds:?}"); + assert!((bounds[5] - 4.0).abs() < 2e-8, "{bounds:?}"); +} + +#[test] +fn concave_tilted_polyline_extrudes_to_right_hand_rule_side() { + let first = [0.0, 0.0, 0.0]; + let normal = [0.0, -0.8, 0.6]; + let mut world = graph(); + world + .create_primitive( + Primitive::Polyline { + points: vec![ + first, + [1.0, 0.6, 0.8], + [2.0, 0.0, 0.0], + [2.0, 1.2, 1.6], + [0.0, 1.2, 1.6], + ], + closed: true, + }, + named("profile"), + ) + .unwrap(); + world + .create_operation(extrude("profile", 2.0), named("solid")) + .unwrap(); + let heights = world + .brep("solid") + .unwrap() + .topology + .vertices + .iter() + .map(|vertex| { + (0..3) + .map(|axis| (vertex.position[axis] - first[axis]) * normal[axis]) + .sum::() + }) + .collect::>(); + assert!(heights.iter().all(|height| *height >= -4e-8), "{heights:?}"); + assert!( + heights.iter().any(|height| *height >= 2.0 - 4e-8), + "{heights:?}" + ); +} + +#[test] +fn profile_tilted_1e_6_rad_is_not_flattened() { + let points = vec![ + [0.0, 0.0, 0.0], + [0.0, -2.9999999999995e-6, 2.9999999999985], + [2.0, -2.9999999999995e-6, 2.9999999999985], + [2.0, 0.0, 0.0], + ]; + let normal = [0.0, 0.9999999999995, 9.999999999998333e-7]; + let mut world = graph(); + world + .create_primitive( + Primitive::Polyline { + points: points.clone(), + closed: true, + }, + named("profile"), + ) + .unwrap(); + world + .create_operation(extrude("profile", 2.0), named("solid")) + .unwrap(); + let body = world.brep("solid").unwrap(); + let near_vertex = |point: [f64; 3]| { + body.topology.vertices.iter().any(|vertex| { + (0..3) + .map(|axis| (vertex.position[axis] - point[axis]).powi(2)) + .sum::() + .sqrt() + <= 4e-8 + }) + }; + for point in points { + assert!(near_vertex(point), "{point:?}"); + let top = [0, 1, 2].map(|axis| point[axis] + 2.0 * normal[axis]); + assert!(near_vertex(top), "{top:?}"); + } +} + +#[test] +fn translated_parent_and_far_profile_keep_local_geometry_small() { + let mut world = graph(); + world.create_system_assembly(named("assembly")).unwrap(); + world + .transform( + "assembly", + Transform::Translate { + offset: [10_000.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .create_primitive( + Primitive::Rectangle { + width: 2.0, + breadth: 2.0, + }, + CreateOptions { + parent: Some("assembly".into()), + ..named("profile") + }, + ) + .unwrap(); + world + .create_operation(extrude("profile", 3.0), named("solid")) + .unwrap(); + let local = world.brep("solid").unwrap().bounds().unwrap().unwrap(); + assert!(local.axes[0].hi < 2.0); + let bounds = world.bounds("solid").unwrap().unwrap(); + assert!((bounds[0] - 9999.0).abs() < 2e-8); + assert!((bounds[3] - 10001.0).abs() < 2e-8); +} + +#[test] +fn closed_polyline_profile_rejects_self_crossing() { + let mut world = graph(); + world + .create_primitive( + Primitive::Polyline { + points: vec![ + [-1.0, 0.0, -1.0], + [1.0, 0.0, 1.0], + [-1.0, 0.0, 1.0], + [1.0, 0.0, -1.0], + ], + closed: true, + }, + named("profile"), + ) + .unwrap(); + assert_eq!( + world + .create_operation(extrude("profile", 2.0), named("solid")) + .unwrap_err() + .error_code(), + ErrorCode::InvalidParameter + ); +} + +#[test] +fn ground_polyline_normal_does_not_depend_on_order() { + let points = vec![ + [-1.0, 0.0, -1.0], + [1.0, 0.0, -1.0], + [1.0, 0.0, 1.0], + [-1.0, 0.0, 1.0], + ]; + let mut world = graph(); + world + .create_primitive( + Primitive::Polyline { + points: points.clone(), + closed: true, + }, + named("forward"), + ) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: points.into_iter().rev().collect(), + closed: true, + }, + named("reverse"), + ) + .unwrap(); + world + .create_operation(extrude("forward", 2.0), named("a")) + .unwrap(); + world + .create_operation(extrude("reverse", 2.0), named("b")) + .unwrap(); + assert!(world.bounds("a").unwrap().unwrap()[1] >= -2e-8); + assert!(world.bounds("b").unwrap().unwrap()[1] >= -2e-8); + assert!((world.bounds("b").unwrap().unwrap()[4] - 2.0).abs() < 2e-8); +} diff --git a/main/opengeometry/tests/creating/holes.rs b/main/opengeometry/tests/creating/holes.rs new file mode 100644 index 0000000..460719f --- /dev/null +++ b/main/opengeometry/tests/creating/holes.rs @@ -0,0 +1,112 @@ +use opengeometry::world_graph::{ + CreateOptions, CreatingOperation, ErrorCode, Plane, Primitive, WorldGraph, +}; +use opengeometry_test_support::world_graph::{graph, named}; + +fn square_profile() -> WorldGraph { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 4.0, + breadth: 4.0, + }, + named("outer"), + ) + .unwrap(); + world +} + +fn circle_at(world: &mut WorldGraph, id: &str, radius: f64, origin: [f64; 3]) { + world + .create_primitive( + Primitive::Circle { radius }, + CreateOptions { + plane: Some(Plane { + origin: Some(origin), + ..Plane::default() + }), + ..named(id) + }, + ) + .unwrap(); +} + +fn assert_rejected(mut world: WorldGraph, holes: &[&str], message: &str) { + let revision = world.revision(); + let nodes = world.node_count(); + let error = world + .create_operation( + CreatingOperation::Extrude { + profile: "outer".into(), + holes: holes.iter().map(|hole| (*hole).into()).collect(), + distance: 2.0, + }, + named("solid"), + ) + .unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::InvalidParameter); + assert_eq!(error.message(), message); + assert_eq!(world.revision(), revision); + assert_eq!(world.node_count(), nodes); +} + +#[test] +fn hole_outside_the_profile_is_invalid_parameter() { + let mut world = square_profile(); + circle_at(&mut world, "hole", 0.5, [5.0, 0.0, 0.0]); + assert_rejected(world, &["hole"], "hole lies outside the profile"); +} + +#[test] +fn hole_touching_the_profile_is_invalid_parameter() { + let mut world = square_profile(); + circle_at(&mut world, "hole", 1.0, [0.0, 0.0, 1.0]); + assert_rejected(world, &["hole"], "hole touches the profile"); +} + +#[test] +fn overlapping_holes_are_invalid_parameter() { + let mut world = square_profile(); + circle_at(&mut world, "a", 0.5, [-0.3, 0.0, 0.0]); + circle_at(&mut world, "b", 0.5, [0.3, 0.0, 0.0]); + assert_rejected(world, &["a", "b"], "holes overlap"); +} + +#[test] +fn nested_holes_are_invalid_parameter() { + let mut world = square_profile(); + circle_at(&mut world, "a", 1.5, [0.0; 3]); + circle_at(&mut world, "b", 0.5, [0.0; 3]); + assert_rejected(world, &["a", "b"], "holes are nested"); +} + +#[test] +fn annular_hole_not_smaller_than_the_profile_is_invalid_parameter() { + let mut world = graph(); + circle_at(&mut world, "outer", 2.0, [0.0; 3]); + circle_at(&mut world, "hole", 2.0, [0.0; 3]); + assert_rejected( + world, + &["hole"], + "hole radius is not smaller than the profile", + ); +} + +#[test] +fn tilted_circle_hole_rim_off_the_plane_is_invalid_parameter() { + let mut world = square_profile(); + world + .create_primitive( + Primitive::Circle { radius: 1.0 }, + CreateOptions { + plane: Some(Plane { + normal: Some([0.0, (1e-5_f64).cos(), (1e-5_f64).sin()]), + ..Plane::default() + }), + ..named("hole") + }, + ) + .unwrap(); + assert_rejected(world, &["hole"], "hole is not coplanar"); +} diff --git a/main/opengeometry/tests/creating/main.rs b/main/opengeometry/tests/creating/main.rs new file mode 100644 index 0000000..8fef1fa --- /dev/null +++ b/main/opengeometry/tests/creating/main.rs @@ -0,0 +1,5 @@ +mod extrude; +mod holes; +mod rebuild; +mod support; +mod sweep; diff --git a/main/opengeometry/tests/creating/rebuild.rs b/main/opengeometry/tests/creating/rebuild.rs new file mode 100644 index 0000000..9481759 --- /dev/null +++ b/main/opengeometry/tests/creating/rebuild.rs @@ -0,0 +1,114 @@ +use super::support::extrude; +use opengeometry::world_graph::{CopyOptions, EditScope, ErrorCode, Primitive, Transform}; +use opengeometry_test_support::world_graph::{graph, named}; + +#[test] +fn rebuild_maps_profile_to_body_and_preserves_placement() { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 2.0, + breadth: 2.0, + }, + named("profile"), + ) + .unwrap(); + world + .create_operation(extrude("profile", 2.0), named("solid")) + .unwrap(); + world + .transform( + "solid", + Transform::Translate { + offset: [5.0, 0.0, 0.0], + }, + ) + .unwrap(); + let before = world.world_placement("solid").unwrap(); + world + .rebuild_operation("solid", extrude("profile", 3.0), EditScope::Node) + .unwrap(); + assert_eq!(before, world.world_placement("solid").unwrap()); + let bounds = world.bounds("solid").unwrap().unwrap(); + assert!((bounds[0] + 1.0).abs() < 2e-8, "{bounds:?}"); + assert!((bounds[3] - 1.0).abs() < 2e-8, "{bounds:?}"); +} + +#[test] +fn rebuild_obeys_shared_shape_scope() { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 2.0, + breadth: 2.0, + }, + named("profile"), + ) + .unwrap(); + world + .create_operation(extrude("profile", 2.0), named("solid")) + .unwrap(); + world + .instance( + "solid", + CopyOptions { + og_id: Some("instance".into()), + ..CopyOptions::default() + }, + ) + .unwrap(); + assert_eq!( + world + .rebuild_operation("solid", extrude("profile", 3.0), EditScope::Node) + .unwrap_err() + .error_code(), + ErrorCode::SharedShape + ); + world + .rebuild_operation("solid", extrude("profile", 3.0), EditScope::AllInstances) + .unwrap(); + assert_eq!(world.brep("solid").unwrap().revision, 1); + assert_eq!(world.brep("instance").unwrap().revision, 1); +} + +#[test] +fn rebuild_after_180_about_x_extrudes_circle_and_rectangle_the_same_way() { + let mut world = graph(); + world + .create_primitive(Primitive::Circle { radius: 1.0 }, named("circle")) + .unwrap(); + world + .create_primitive( + Primitive::Rectangle { + width: 2.0, + breadth: 2.0, + }, + named("rectangle"), + ) + .unwrap(); + for (profile, body) in [("circle", "circle-solid"), ("rectangle", "rectangle-solid")] { + world + .create_operation(extrude(profile, 2.0), named(body)) + .unwrap(); + world + .transform( + body, + Transform::Rotate { + axis: [1.0, 0.0, 0.0], + degrees: 180.0, + pivot: Some([0.0; 3]), + }, + ) + .unwrap(); + world + .rebuild_operation(body, extrude(profile, 2.0), EditScope::Node) + .unwrap(); + } + for body in ["circle-solid", "rectangle-solid"] { + let bounds = world.bounds(body).unwrap().unwrap(); + assert!((bounds[1] + 2.0).abs() < 2e-8, "{body} {bounds:?}"); + assert!(bounds[4] < 2e-8, "{body} {bounds:?}"); + } +} diff --git a/main/opengeometry/tests/creating/support.rs b/main/opengeometry/tests/creating/support.rs new file mode 100644 index 0000000..c1e2a57 --- /dev/null +++ b/main/opengeometry/tests/creating/support.rs @@ -0,0 +1,9 @@ +use opengeometry::world_graph::CreatingOperation; + +pub(super) fn extrude(profile: &str, distance: f64) -> CreatingOperation { + CreatingOperation::Extrude { + profile: profile.into(), + holes: Vec::new(), + distance, + } +} diff --git a/main/opengeometry/tests/creating/sweep.rs b/main/opengeometry/tests/creating/sweep.rs new file mode 100644 index 0000000..c0c7cd6 --- /dev/null +++ b/main/opengeometry/tests/creating/sweep.rs @@ -0,0 +1,382 @@ +use super::support::extrude; +use opengeometry::world_graph::{ + CreateOptions, CreatingOperation, EditScope, ErrorCode, Plane, Primitive, Transform, +}; +use opengeometry_test_support::volume; +use opengeometry_test_support::world_graph::{graph, named}; + +#[test] +fn l_path_sweep_has_centroid_path_volume() { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 2.0, + breadth: 2.0, + }, + named("profile"), + ) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: vec![[0.0, 0.0, 0.0], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]], + closed: false, + }, + named("path"), + ) + .unwrap(); + world + .create_operation( + CreatingOperation::Sweep { + profile: "profile".into(), + path: "path".into(), + }, + named("solid"), + ) + .unwrap(); + let body = world.brep("solid").unwrap(); + assert_eq!(body.topology.faces.len(), 10); + let measured = volume::estimate(&body, 0.001); + assert!( + (measured.value - 24.0).abs() <= measured.error_bound, + "{}", + measured.value + ); +} + +#[test] +fn noncoplanar_path_sweep_is_valid_and_repeatable() { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 1.0, + breadth: 1.0, + }, + named("profile"), + ) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: vec![ + [0.0, 0.0, 0.0], + [0.0, 3.0, 0.0], + [3.0, 3.0, 0.0], + [3.0, 3.0, 3.0], + ], + closed: false, + }, + named("path"), + ) + .unwrap(); + let operation = CreatingOperation::Sweep { + profile: "profile".into(), + path: "path".into(), + }; + world + .create_operation(operation.clone(), named("solid")) + .unwrap(); + let first = world.brep("solid").unwrap().to_json().unwrap(); + world + .rebuild_operation("solid", operation, EditScope::Node) + .unwrap(); + let mut second = (*world.brep("solid").unwrap()).clone(); + second.revision = 0; + assert_eq!(first, second.to_json().unwrap()); +} + +#[test] +fn circular_l_path_sweep_is_watertight() { + let mut world = graph(); + world + .create_primitive(Primitive::Circle { radius: 0.5 }, named("profile")) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: vec![[0.0, 0.0, 0.0], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]], + closed: false, + }, + named("path"), + ) + .unwrap(); + world + .create_operation( + CreatingOperation::Sweep { + profile: "profile".into(), + path: "path".into(), + }, + named("solid"), + ) + .unwrap(); + let body = world.brep("solid").unwrap(); + body.validate().unwrap(); + assert_eq!(body.topology.faces.len(), 6); + let measured = volume::estimate(&body, 0.002); + assert!((measured.value - std::f64::consts::PI * 0.25 * 6.0).abs() <= measured.error_bound); + let mesh = opengeometry::tessellation::tessellate(&body, 0.02, 1_000_000).unwrap(); + let point = |index: u32| { + let offset = index as usize * 3; + [ + mesh.positions[offset].to_bits(), + mesh.positions[offset + 1].to_bits(), + mesh.positions[offset + 2].to_bits(), + ] + }; + let mut edges = std::collections::BTreeMap::new(); + for triangle in mesh.indices.chunks_exact(3) { + for (from, to) in [ + (triangle[0], triangle[1]), + (triangle[1], triangle[2]), + (triangle[2], triangle[0]), + ] { + let mut pair = [point(from), point(to)]; + pair.sort(); + *edges.entry(pair).or_insert(0usize) += 1; + } + } + assert!(edges.values().all(|count| *count == 2)); +} + +#[test] +fn circular_noncoplanar_sweep_is_valid() { + let mut world = graph(); + world + .create_primitive(Primitive::Circle { radius: 0.4 }, named("profile")) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: vec![ + [0.0, 0.0, 0.0], + [0.0, 3.0, 0.0], + [3.0, 3.0, 0.0], + [3.0, 3.0, 3.0], + ], + closed: false, + }, + named("path"), + ) + .unwrap(); + world + .create_operation( + CreatingOperation::Sweep { + profile: "profile".into(), + path: "path".into(), + }, + named("solid"), + ) + .unwrap(); + world.brep("solid").unwrap().validate().unwrap(); +} + +#[test] +fn circular_sweep_keeps_profile_offset_from_path() { + let mut world = graph(); + world + .create_primitive(Primitive::Circle { radius: 0.3 }, named("profile")) + .unwrap(); + world + .transform( + "profile", + Transform::Translate { + offset: [1.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: vec![[0.0, 0.0, 0.0], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]], + closed: false, + }, + named("path"), + ) + .unwrap(); + world + .create_operation( + CreatingOperation::Sweep { + profile: "profile".into(), + path: "path".into(), + }, + named("solid"), + ) + .unwrap(); + let measured = volume::estimate(&world.brep("solid").unwrap(), 0.001); + assert!((measured.value - std::f64::consts::PI * 0.09 * 4.0).abs() <= measured.error_bound); +} + +#[test] +fn invalid_sweep_and_extrude_leave_graph_unchanged() { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 2.0, + breadth: 2.0, + }, + named("profile"), + ) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: vec![[0.0, 1.0, 0.0], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]], + closed: false, + }, + named("path"), + ) + .unwrap(); + let revision = world.revision(); + let nodes = world.node_count(); + let error = world + .create_operation( + CreatingOperation::Sweep { + profile: "profile".into(), + path: "path".into(), + }, + named("bad"), + ) + .unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::InvalidParameter); + assert_eq!( + world + .create_operation(extrude("profile", 1e-9), named("bad")) + .unwrap_err() + .error_code(), + ErrorCode::InvalidParameter + ); + assert_eq!(world.revision(), revision); + assert_eq!(world.node_count(), nodes); +} + +#[test] +fn sweep_rejects_miter_and_separated_segment_collisions() { + for points in [ + vec![[0.0, 0.0, 0.0], [0.0, 0.2, 0.0], [2.0, 0.2, 0.0]], + vec![ + [0.0, 0.0, 0.0], + [0.0, 3.0, 0.0], + [1.0, 3.0, 0.0], + [1.0, 0.0, 0.0], + ], + ] { + let mut world = graph(); + world + .create_primitive(Primitive::Circle { radius: 0.6 }, named("profile")) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points, + closed: false, + }, + named("path"), + ) + .unwrap(); + let error = world + .create_operation( + CreatingOperation::Sweep { + profile: "profile".into(), + path: "path".into(), + }, + named("solid"), + ) + .unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::SweepSelfIntersection); + } +} + +#[test] +fn sweep_profile_tilted_1e_6_rad_gives_invalid_parameter() { + let tilt = 1e-6_f64; + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 2.0, + breadth: 2.0, + }, + CreateOptions { + og_id: Some("tilted".into()), + plane: Some(Plane { + normal: Some([0.0, -tilt.sin(), tilt.cos()]), + ..Plane::default() + }), + ..CreateOptions::default() + }, + ) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: vec![[0.0, 0.0, 0.0], [0.0, 0.0, 3.0], [3.0, 0.0, 3.0]], + closed: false, + }, + named("rise"), + ) + .unwrap(); + let revision = world.revision(); + let nodes = world.node_count(); + let error = world + .create_operation( + CreatingOperation::Sweep { + profile: "tilted".into(), + path: "rise".into(), + }, + named("tilted-sweep"), + ) + .unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::InvalidParameter, "{error:?}"); + assert_eq!((world.revision(), world.node_count()), (revision, nodes)); +} + +#[test] +fn collinear_diagonal_path_merges() { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 0.1, + breadth: 0.1, + }, + CreateOptions { + og_id: Some("square".into()), + plane: Some(Plane { + normal: Some([1.0, 2.0, 3.0]), + ..Plane::default() + }), + ..CreateOptions::default() + }, + ) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: vec![[0.0, 0.0, 0.0], [0.1, 0.2, 0.3], [0.3, 0.6, 0.9]], + closed: false, + }, + named("diagonal"), + ) + .unwrap(); + world + .create_operation( + CreatingOperation::Sweep { + profile: "square".into(), + path: "diagonal".into(), + }, + named("prism"), + ) + .unwrap(); + let prism = world.brep("prism").unwrap(); + assert_eq!(prism.topology.faces.len(), 6); + let expected = 0.01 * 1.1224972160321824; + let measured = volume::estimate(&prism, 0.0005); + assert!( + (measured.value - expected).abs() <= measured.error_bound, + "{} vs {expected}", + measured.value + ); +} diff --git a/main/opengeometry/tests/curved_mixed_openings.rs b/main/opengeometry/tests/curved_mixed_openings.rs deleted file mode 100644 index 024e805..0000000 --- a/main/opengeometry/tests/curved_mixed_openings.rs +++ /dev/null @@ -1,102 +0,0 @@ -use opengeometry::analytic::{ - booleans::subtract_planar_cutters, - primitives, - query::{classify_point, PointClassification}, - tessellation::tessellate, - topology::Accuracy, - Frame3, -}; - -#[test] -fn disjoint_planar_and_round_openings_share_a_curved_host_height_level() { - let accuracy = Accuracy { - geometric: 6.3e-8, - intersection: 1.575e-8, - tessellation: 0.01, - exchange: 1e-5, - }; - let host_frame = Frame3 { - origin: [0.0, 0.0, 0.0], - x: [1.0, 0.0, 0.0], - y: [0.0, 0.0, -1.0], - z: [0.0, 1.0, 0.0], - }; - let point = |radius: f64, angle: f64| [radius * angle.cos(), radius * angle.sin()]; - let host = primitives::arc_edged_extrusion( - "mixed-curved-host".into(), - host_frame, - vec![ - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 3.0, - start_angle: 0.8, - sweep_angle: -1.6, - }, - primitives::ProfileEdge::Line { - from: point(3.0, -0.8), - to: point(2.7, -0.8), - }, - primitives::ProfileEdge::Arc { - center: [0.0, 0.0], - radius: 2.7, - start_angle: -0.8, - sweep_angle: 1.6, - }, - primitives::ProfileEdge::Line { - from: point(2.7, 0.8), - to: point(3.0, 0.8), - }, - ], - 3.0, - accuracy, - ) - .unwrap(); - let rectangle = primitives::cuboid( - "rectangular-opening".into(), - Frame3 { - origin: [2.2, 1.0, 1.1], - x: [1.0, 0.0, 0.0], - y: [0.0, 1.0, 0.0], - z: [0.0, 0.0, 1.0], - }, - [1.0, 0.5, 0.4], - accuracy, - ) - .unwrap(); - let round = primitives::cylinder( - "round-opening".into(), - Frame3 { - origin: [2.65, 1.5, 0.0], - x: [0.0, 0.0, -1.0], - y: [0.0, 1.0, 0.0], - z: [1.0, 0.0, 0.0], - }, - 0.5, - 0.4, - accuracy, - ) - .unwrap(); - for cutters in [ - [rectangle.clone(), round.clone()], - [round.clone(), rectangle.clone()], - ] { - let result = subtract_planar_cutters(&host, &cutters, "mixed-curved-cut".into()).unwrap(); - result.brep.validate().unwrap(); - tessellate(&result.brep, 0.01, 2_000_000).unwrap(); - assert_eq!( - classify_point(&result.brep, [2.6, 1.25, 1.3]).unwrap(), - PointClassification::Outside - ); - assert_eq!( - classify_point(&result.brep, [2.8, 1.5, 0.0]).unwrap(), - PointClassification::Outside - ); - assert_eq!( - classify_point(&result.brep, [2.8, 2.5, 0.0]).unwrap(), - PointClassification::Inside - ); - let (_, report) = - opengeometry::analytic::exchange::export_step(&result.brep, "metre").unwrap(); - assert_eq!(report.solids, 1); - } -} diff --git a/main/opengeometry/tests/display_buffers.rs b/main/opengeometry/tests/display_buffers.rs new file mode 100644 index 0000000..64af07d --- /dev/null +++ b/main/opengeometry/tests/display_buffers.rs @@ -0,0 +1,127 @@ +use opengeometry::tessellation::{ + display::{bucket_floor, deflection_bucket, display_buffers, static_bucket}, + tessellate, SnapshotStore, +}; +use opengeometry::world_graph::{CreateOptions, Primitive, StepOptions, Transform}; +use opengeometry_test_support::world_graph::graph; + +#[test] +fn i1_brep_remains_canonical_when_display_uses_snapshot_buffers() { + let mut graph = graph(); + graph + .create_primitive( + Primitive::Cuboid { + width: 1.0, + height: 1.0, + depth: 1.0, + }, + CreateOptions { + og_id: Some("cube".into()), + ..CreateOptions::default() + }, + ) + .unwrap(); + let shape_id = graph.node("cube").unwrap().shape.clone().unwrap(); + let brep_before = graph.brep("cube").unwrap().to_json().unwrap(); + let step_before = graph + .export_step(&["cube".into()], &StepOptions::default()) + .unwrap() + .0; + let mut tessellator = SnapshotStore::new(); + let slot = tessellator + .load(&graph.snapshot(&shape_id).unwrap()) + .unwrap(); + for bucket in [0.01, 0.02] { + let direct = graph.buffers(&shape_id, bucket, 2_000_000).unwrap(); + let loaded = tessellator.buffers(slot, bucket, 2_000_000).unwrap(); + assert_eq!(direct, loaded); + assert_eq!(direct.triangles, 12); + assert_eq!(direct.face_ranges.len(), 6 * 3); + assert_eq!(direct.edge_ids.len(), 12); + assert!(direct.achieved_deflection <= bucket); + assert!(direct.positions.iter().all(|value| value.abs() <= 1.0)); + let source = tessellate(&graph.brep("cube").unwrap(), 0.75 * bucket, 2_000_000).unwrap(); + for range in direct.face_ranges.chunks_exact(3) { + let (face, first, count) = (range[0], range[1] as usize, range[2] as usize); + assert!(source.triangle_face_ids[first..first + count] + .iter() + .all(|id| *id == face)); + } + assert_eq!(direct.edge_ids, source.outline_edge_ids); + } + assert_eq!(graph.brep("cube").unwrap().to_json().unwrap(), brep_before); + assert_eq!( + graph + .export_step(&["cube".into()], &StepOptions::default()) + .unwrap() + .0, + step_before + ); + assert!(tessellator.release(slot)); + assert!(tessellator.buffers(slot, 0.01, 1000).is_err()); +} + +#[test] +fn distant_placement_does_not_enter_local_display_coordinates() { + let mut graph = graph(); + graph + .create_primitive( + Primitive::Cuboid { + width: 1.0, + height: 1.0, + depth: 1.0, + }, + CreateOptions { + og_id: Some("cube".into()), + ..CreateOptions::default() + }, + ) + .unwrap(); + graph + .transform( + "cube", + Transform::Translate { + offset: [500_000.0, 0.0, -5_000_000.0], + }, + ) + .unwrap(); + let bounds = graph.bounds("cube").unwrap().unwrap(); + assert!(bounds[0] <= 499_999.5 && bounds[3] >= 500_000.5); + assert!(bounds[2] <= -5_000_000.5 && bounds[5] >= -4_999_999.5); + let shape_id = graph.node("cube").unwrap().shape.clone().unwrap(); + let buffers = graph.buffers(&shape_id, 0.01, 1000).unwrap(); + assert!(buffers.positions.iter().all(|value| value.abs() <= 1.0)); + assert_eq!(buffers.origin, [0.0, 0.5, 0.0]); +} + +#[test] +fn i4_display_buckets_leave_brep_and_step_unchanged_and_enforce_limits() { + let graph = graph(); + let brep = opengeometry::primitives::cuboid( + "cube".into(), + opengeometry::brep::Frame3::IDENTITY, + [1.0; 3], + graph.accuracy(), + ) + .unwrap(); + let canonical = brep.to_json().unwrap(); + let step = opengeometry::exchange::export_step(&brep, "metre") + .unwrap() + .0; + let floor = bucket_floor(&brep).unwrap(); + assert!(floor > 0.0 && floor.log2().fract() == 0.0); + assert!(static_bucket(&brep).unwrap() >= floor); + assert_eq!(deflection_bucket(0.03).unwrap(), 0.015625); + assert!(display_buffers(&brep, floor / 2.0, 1000).is_err()); + assert!(display_buffers(&brep, 0.01, 0).is_err()); + assert!(display_buffers(&brep, 0.01, 2_000_001).is_err()); + let mut tessellator = SnapshotStore::new(); + assert!(tessellator.load(&vec![0; 64 * 1024 * 1024 + 1]).is_err()); + assert_eq!(brep.to_json().unwrap(), canonical); + assert_eq!( + opengeometry::exchange::export_step(&brep, "metre") + .unwrap() + .0, + step + ); +} diff --git a/main/opengeometry/tests/errors.rs b/main/opengeometry/tests/errors.rs new file mode 100644 index 0000000..bc0fed9 --- /dev/null +++ b/main/opengeometry/tests/errors.rs @@ -0,0 +1,272 @@ +#![cfg(not(target_arch = "wasm32"))] +use opengeometry::brep::GeometryError; +use opengeometry::math::MathError; +use opengeometry::operations::OperationError; +use opengeometry::world_graph::{ + CreatingOperation, EditScope, ErrorCode, ErrorContext, ErrorDetails, GraphError, + ModifyingOperation, Primitive, StepOptions, Transform, WorldGraph, +}; +use opengeometry_test_support::world_graph::{graph, named}; + +const CONTEXTS: [(ErrorContext, ErrorCode); 5] = [ + (ErrorContext::Create, ErrorCode::InvalidParameter), + (ErrorContext::Rebuild, ErrorCode::InvalidParameter), + (ErrorContext::Transform, ErrorCode::InvalidTransform), + (ErrorContext::Operate, ErrorCode::InvalidGeometry), + (ErrorContext::Export, ErrorCode::InvalidGeometry), +]; + +fn coded(code: ErrorCode, message: &str) -> GraphError { + GraphError::Code { + code, + message: message.into(), + details: ErrorDetails::None, + } +} + +#[test] +fn singular_parameterization_takes_the_code_of_its_context() { + for (context, expected) in CONTEXTS { + let error = GraphError::from(GeometryError::SingularParameterization).in_context(context); + assert_eq!(error.error_code(), expected, "{context:?}"); + let gap = GraphError::from(GeometryError::CoverageGap { + families: ["plane".into(), "torus".into()], + }); + assert_eq!(gap.clone().in_context(context), gap, "{context:?}"); + } +} + +#[test] +fn operation_errors_keep_their_code_and_message_as_graph_errors() { + let cases = [ + ( + OperationError::InvalidParameter("hole is not coplanar".into()), + coded(ErrorCode::InvalidParameter, "hole is not coplanar"), + ), + ( + OperationError::SweepSelfIntersection("sweep path reverses".into()), + coded(ErrorCode::SweepSelfIntersection, "sweep path reverses"), + ), + ( + OperationError::InvalidTopology("seam use is missing".into()), + coded(ErrorCode::InvalidTopology, "seam use is missing"), + ), + ( + OperationError::Geometry(GeometryError::SingularParameterization), + GraphError::Geometry(GeometryError::SingularParameterization), + ), + ]; + for (operation, expected) in cases { + assert_eq!(GraphError::from(operation), expected); + } +} + +#[test] +fn extrusion_below_resolution_reports_invalid_parameter_through_the_graph() { + let mut world = graph(); + world + .create_primitive( + Primitive::Rectangle { + width: 2.0, + breadth: 3.0, + }, + named("profile"), + ) + .unwrap(); + let operation = CreatingOperation::Extrude { + profile: "profile".into(), + holes: Vec::new(), + distance: 0.0, + }; + assert_eq!( + world + .create_operation(operation, named("solid")) + .unwrap_err(), + coded( + ErrorCode::InvalidParameter, + "extrusion distance is below geometric resolution" + ), + ); +} + +fn geometry_errors() -> [(GeometryError, ErrorCode); 12] { + [ + ( + GeometryError::Math(MathError::DivisionByZero), + ErrorCode::InvalidGeometry, + ), + ( + GeometryError::InvalidGeometry("bad".into()), + ErrorCode::InvalidGeometry, + ), + ( + GeometryError::InvalidTopology("bad".into()), + ErrorCode::InvalidTopology, + ), + ( + GeometryError::UnsupportedGeometry("bad".into()), + ErrorCode::UnsupportedGeometry, + ), + ( + GeometryError::AmbiguousProfileAlignment("bad".into()), + ErrorCode::InvalidParameter, + ), + ( + GeometryError::CoverageGap { + families: ["plane".into(), "torus".into()], + }, + ErrorCode::CoverageGap, + ), + ( + GeometryError::SingularParameterization, + ErrorCode::InvalidGeometry, + ), + ( + GeometryError::MissingReference { + kind: "edge".into(), + index: 3, + }, + ErrorCode::InvalidTopology, + ), + ( + GeometryError::UnsupportedSchema { found: 9 }, + ErrorCode::InvalidTopology, + ), + ( + GeometryError::UnresolvedIntersection("bad".into()), + ErrorCode::UnresolvedIntersection, + ), + ( + GeometryError::UnresolvedTessellation("bad".into()), + ErrorCode::UnresolvedTessellation, + ), + ( + GeometryError::LimitExceeded("bad".into()), + ErrorCode::LimitExceeded, + ), + ] +} + +#[test] +fn every_geometry_error_variant_keeps_its_code_in_every_context() { + for (error, code) in geometry_errors() { + for (context, singular_code) in CONTEXTS { + let actual = GraphError::from(error.clone()).in_context(context); + let expected = match (&error, context) { + ( + GeometryError::SingularParameterization, + ErrorContext::Create | ErrorContext::Rebuild | ErrorContext::Transform, + ) => coded(singular_code, "SingularParameterization"), + _ => GraphError::Geometry(error.clone()), + }; + assert_eq!(actual, expected, "{error:?} {context:?}"); + if expected == GraphError::Geometry(error.clone()) { + assert_eq!(actual.error_code(), code, "{error:?} {context:?}"); + } + } + } +} + +fn unit_cuboid(world: &mut WorldGraph, og_id: &str, size: f64) { + world + .create_primitive( + Primitive::Cuboid { + width: size, + height: size, + depth: size, + }, + named(og_id), + ) + .unwrap(); +} + +fn translate(world: &mut WorldGraph, og_id: &str, offset: [f64; 3]) { + world + .transform(og_id, Transform::Translate { offset }) + .unwrap(); +} + +#[test] +fn multi_tool_operate_failure_carries_handlers_tool_index_and_og_ids() { + let mut world = graph(); + unit_cuboid(&mut world, "target", 2.0); + unit_cuboid(&mut world, "tool-1", 2.0); + translate(&mut world, "tool-1", [1.0, 0.0, 0.0]); + unit_cuboid(&mut world, "tool-2", 1.0); + translate(&mut world, "tool-2", [1.5, 0.5, 0.0]); + world + .transform( + "tool-2", + Transform::Rotate { + axis: [0.0, 1.0, 0.0], + degrees: 30.0, + pivot: Some([1.5, 0.5, 0.0]), + }, + ) + .unwrap(); + let mut step_one = graph(); + unit_cuboid(&mut step_one, "target", 2.0); + unit_cuboid(&mut step_one, "tool-1", 2.0); + translate(&mut step_one, "tool-1", [1.0, 0.0, 0.0]); + step_one + .operate( + "target", + ModifyingOperation::Union, + &["tool-1".into()], + EditScope::Node, + ) + .unwrap(); + let step_one_handlers: Vec = + serde_json::from_value(step_one.report("target").unwrap().unwrap()["handlers"].clone()) + .unwrap(); + let revision = world.revision(); + let brep_before = world.brep("target").unwrap().to_json().unwrap(); + let error = world + .operate( + "target", + ModifyingOperation::Union, + &["tool-1".into(), "tool-2".into()], + EditScope::Node, + ) + .unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::CoverageGap); + assert_eq!( + error.details(), + ErrorDetails::Operate { + handlers: step_one_handlers, + tool_index: 1, + og_ids: vec!["target".into(), "tool-1".into(), "tool-2".into()], + } + ); + assert_eq!(world.revision(), revision); + assert_eq!( + world.brep("target").unwrap().to_json().unwrap(), + brep_before + ); +} + +#[test] +fn two_body_export_failing_on_the_second_body_carries_its_og_id() { + let mut world = graph(); + unit_cuboid(&mut world, "near", 1.0); + unit_cuboid(&mut world, "far", 1.0); + translate(&mut world, "far", [1e6, 0.0, 0.0]); + let options = StepOptions { + unit: "metre".into(), + up_axis: "Y".into(), + ..StepOptions::default() + }; + let error = world + .export_step(&["near".into(), "far".into()], &options) + .unwrap_err(); + assert_eq!( + error, + GraphError::Code { + code: ErrorCode::LimitExceeded, + message: "far: placement coordinate precision exceeds geometric tolerance".into(), + details: ErrorDetails::Export { + og_id: "far".into() + }, + } + ); +} diff --git a/main/opengeometry/tests/fixtures/cases/annular-cylinder.brep.json b/main/opengeometry/tests/fixtures/cases/annular-cylinder.brep.json new file mode 100644 index 0000000..6755c58 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/annular-cylinder.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"annular-cylinder","revision":0,"geometry":{"surfaces":[{"kind":"Cylinder","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0},{"kind":"Cylinder","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":0.4},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}}],"curves":[{"kind":"Circle","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0},{"kind":"Circle","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0},{"kind":"Circle","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":0.4},{"kind":"Circle","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":0.4},{"kind":"Line","origin":[1.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.4,0.0,0.0],"direction":[0.0,1.0,0.0]}],"pcurves":[{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[6.283185307179586,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[6.283185307179586,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[1.0,0.0],"axis_b":[0.0,1.0]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[0.4,0.0],"axis_b":[0.0,0.4]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[1.0,0.0,0.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[1.0,2.0,0.0],"outgoing_halfedge":2,"tolerance":1e-8},{"id":2,"position":[0.4,0.0,0.0],"outgoing_halfedge":4,"tolerance":1e-8},{"id":3,"position":[0.4,2.0,0.0],"outgoing_halfedge":6,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":0,"twin_halfedge":8,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":2,"twin_halfedge":10,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":4,"twin_halfedge":9,"tolerance":1e-8,"chart_seam":false},{"id":3,"geometry":{"kind":"Curve","curve":3,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":6,"twin_halfedge":11,"tolerance":1e-8,"chart_seam":false},{"id":4,"geometry":{"kind":"Curve","curve":4,"range":{"lo":0.0,"hi":2.0}},"halfedge":1,"twin_halfedge":3,"tolerance":1e-8,"chart_seam":true},{"id":5,"geometry":{"kind":"Curve","curve":5,"range":{"lo":0.0,"hi":2.0}},"halfedge":5,"twin_halfedge":7,"tolerance":1e-8,"chart_seam":true}],"halfedges":[{"id":0,"from":0,"to":0,"twin":8,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":0,"to":1,"twin":3,"next":2,"prev":0,"edge":4,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":1,"to":1,"twin":10,"next":3,"prev":1,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":1,"to":0,"twin":1,"next":0,"prev":2,"edge":4,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":2,"to":2,"twin":9,"next":5,"prev":7,"edge":2,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":2,"to":3,"twin":7,"next":6,"prev":4,"edge":5,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":5,"periodic_lift":[0,0]}},{"id":6,"from":3,"to":3,"twin":11,"next":7,"prev":5,"edge":3,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":6,"periodic_lift":[0,0]}},{"id":7,"from":3,"to":2,"twin":5,"next":4,"prev":6,"edge":5,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":7,"periodic_lift":[0,0]}},{"id":8,"from":0,"to":0,"twin":0,"next":8,"prev":8,"edge":0,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":8,"periodic_lift":[0,0]}},{"id":9,"from":2,"to":2,"twin":4,"next":9,"prev":9,"edge":2,"face":2,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":9,"periodic_lift":[0,0]}},{"id":10,"from":1,"to":1,"twin":2,"next":10,"prev":10,"edge":1,"face":3,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":10,"periodic_lift":[0,0]}},{"id":11,"from":3,"to":3,"twin":6,"next":11,"prev":11,"edge":3,"face":3,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":11,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false},{"id":2,"start_halfedge":8,"face_ref":2,"is_hole":false},{"id":3,"start_halfedge":9,"face_ref":2,"is_hole":true},{"id":4,"start_halfedge":10,"face_ref":3,"is_hole":false},{"id":5,"start_halfedge":11,"face_ref":3,"is_hole":true}],"faces":[{"id":0,"key":"outer","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.0,"hi":6.283185307179586},{"lo":0.0,"hi":2.0}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"annular-cylinder","body":"annular-cylinder","key":"outer","face":0}],"role":"Authored","reversed":false}},{"id":1,"key":"inner","surface":1,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":0.0,"hi":6.283185307179586},{"lo":0.0,"hi":2.0}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"annular-cylinder","body":"annular-cylinder","key":"inner","face":1}],"role":"Authored","reversed":false}},{"id":2,"key":"lower_cap","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.0,"hi":1.0},{"lo":-1.0,"hi":1.0}],"outer":2,"holes":[3]},"shell_ref":0,"provenance":{"sources":[{"entity":"annular-cylinder","body":"annular-cylinder","key":"lower_cap","face":2}],"role":"Authored","reversed":false}},{"id":3,"key":"upper_cap","surface":3,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.0,"hi":1.0},{"lo":-1.0,"hi":1.0}],"outer":4,"holes":[5]},"shell_ref":0,"provenance":{"sources":[{"entity":"annular-cylinder","body":"annular-cylinder","key":"upper_cap","face":3}],"role":"Authored","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,1,2,3],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.m b/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.m new file mode 100644 index 0000000..f20cf88 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.m @@ -0,0 +1,195 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('annular-cylinder','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=CYLINDRICAL_SURFACE('',#5,1.00000000000000000E0); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=CYLINDRICAL_SURFACE('',#10,4.00000000000000022E-1); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=VERTEX_POINT('',#22); +#24=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#25=VERTEX_POINT('',#24); +#26=CARTESIAN_POINT('',(4.00000000000000022E-1,0.00000000000000000E0,0.00000000000000000E0)); +#27=VERTEX_POINT('',#26); +#28=CARTESIAN_POINT('',(4.00000000000000022E-1,2.00000000000000000E0,0.00000000000000000E0)); +#29=VERTEX_POINT('',#28); +#30=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#31=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#32=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=AXIS2_PLACEMENT_3D('',#30,#31,#32); +#34=CIRCLE('',#33,1.00000000000000000E0); +#35=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#36=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#37=VECTOR('',#36,1.00000000000000000E0); +#38=LINE('',#35,#37); +#39=DEFINITIONAL_REPRESENTATION('',(#38),#1); +#40=PCURVE('',#6,#39); +#41=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#42=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#43=AXIS2_PLACEMENT_2D('',#41,#42); +#44=CIRCLE('',#43,1.00000000000000000E0); +#45=DEFINITIONAL_REPRESENTATION('',(#44),#1); +#46=PCURVE('',#16,#45); +#47=SURFACE_CURVE('',#34,(#40,#46),.CURVE_3D.); +#48=EDGE_CURVE('',#23,#23,#47,.T.); +#49=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#50=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#51=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#52=AXIS2_PLACEMENT_3D('',#49,#50,#51); +#53=CIRCLE('',#52,1.00000000000000000E0); +#54=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#55=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#56=VECTOR('',#55,1.00000000000000000E0); +#57=LINE('',#54,#56); +#58=DEFINITIONAL_REPRESENTATION('',(#57),#1); +#59=PCURVE('',#6,#58); +#60=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#61=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#62=AXIS2_PLACEMENT_2D('',#60,#61); +#63=CIRCLE('',#62,1.00000000000000000E0); +#64=DEFINITIONAL_REPRESENTATION('',(#63),#1); +#65=PCURVE('',#21,#64); +#66=SURFACE_CURVE('',#53,(#59,#65),.CURVE_3D.); +#67=EDGE_CURVE('',#25,#25,#66,.T.); +#68=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#69=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#70=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#71=AXIS2_PLACEMENT_3D('',#68,#69,#70); +#72=CIRCLE('',#71,4.00000000000000022E-1); +#73=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#74=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#75=VECTOR('',#74,1.00000000000000000E0); +#76=LINE('',#73,#75); +#77=DEFINITIONAL_REPRESENTATION('',(#76),#1); +#78=PCURVE('',#11,#77); +#79=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#80=DIRECTION('',(4.00000000000000022E-1,0.00000000000000000E0)); +#81=AXIS2_PLACEMENT_2D('',#79,#80); +#82=CIRCLE('',#81,4.00000000000000022E-1); +#83=DEFINITIONAL_REPRESENTATION('',(#82),#1); +#84=PCURVE('',#16,#83); +#85=SURFACE_CURVE('',#72,(#78,#84),.CURVE_3D.); +#86=EDGE_CURVE('',#27,#27,#85,.T.); +#87=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#88=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#90=AXIS2_PLACEMENT_3D('',#87,#88,#89); +#91=CIRCLE('',#90,4.00000000000000022E-1); +#92=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#93=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#94=VECTOR('',#93,1.00000000000000000E0); +#95=LINE('',#92,#94); +#96=DEFINITIONAL_REPRESENTATION('',(#95),#1); +#97=PCURVE('',#11,#96); +#98=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#99=DIRECTION('',(4.00000000000000022E-1,0.00000000000000000E0)); +#100=AXIS2_PLACEMENT_2D('',#98,#99); +#101=CIRCLE('',#100,4.00000000000000022E-1); +#102=DEFINITIONAL_REPRESENTATION('',(#101),#1); +#103=PCURVE('',#21,#102); +#104=SURFACE_CURVE('',#91,(#97,#103),.CURVE_3D.); +#105=EDGE_CURVE('',#29,#29,#104,.T.); +#106=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#107=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E0); +#109=LINE('',#106,#108); +#110=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#111=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#112=VECTOR('',#111,1.00000000000000000E0); +#113=LINE('',#110,#112); +#114=DEFINITIONAL_REPRESENTATION('',(#113),#1); +#115=PCURVE('',#6,#114); +#116=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#117=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#118=VECTOR('',#117,1.00000000000000000E0); +#119=LINE('',#116,#118); +#120=DEFINITIONAL_REPRESENTATION('',(#119),#1); +#121=PCURVE('',#6,#120); +#122=SEAM_CURVE('',#109,(#115,#121),.CURVE_3D.); +#123=EDGE_CURVE('',#23,#25,#122,.T.); +#124=CARTESIAN_POINT('',(4.00000000000000022E-1,0.00000000000000000E0,0.00000000000000000E0)); +#125=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E0); +#127=LINE('',#124,#126); +#128=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#129=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#130=VECTOR('',#129,1.00000000000000000E0); +#131=LINE('',#128,#130); +#132=DEFINITIONAL_REPRESENTATION('',(#131),#1); +#133=PCURVE('',#11,#132); +#134=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#135=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#136=VECTOR('',#135,1.00000000000000000E0); +#137=LINE('',#134,#136); +#138=DEFINITIONAL_REPRESENTATION('',(#137),#1); +#139=PCURVE('',#11,#138); +#140=SEAM_CURVE('',#127,(#133,#139),.CURVE_3D.); +#141=EDGE_CURVE('',#27,#29,#140,.T.); +#142=ORIENTED_EDGE('',*,*,#48,.T.); +#143=ORIENTED_EDGE('',*,*,#123,.T.); +#144=ORIENTED_EDGE('',*,*,#67,.F.); +#145=ORIENTED_EDGE('',*,*,#123,.F.); +#146=EDGE_LOOP('',(#142,#143,#144,#145)); +#147=FACE_OUTER_BOUND('',#146,.T.); +#148=ADVANCED_FACE('outer',(#147),#6,.T.); +#149=ORIENTED_EDGE('',*,*,#86,.T.); +#150=ORIENTED_EDGE('',*,*,#141,.T.); +#151=ORIENTED_EDGE('',*,*,#105,.F.); +#152=ORIENTED_EDGE('',*,*,#141,.F.); +#153=EDGE_LOOP('',(#149,#150,#151,#152)); +#154=FACE_OUTER_BOUND('',#153,.T.); +#155=ADVANCED_FACE('inner',(#154),#11,.F.); +#156=ORIENTED_EDGE('',*,*,#48,.F.); +#157=EDGE_LOOP('',(#156)); +#158=FACE_OUTER_BOUND('',#157,.T.); +#159=ORIENTED_EDGE('',*,*,#86,.F.); +#160=EDGE_LOOP('',(#159)); +#161=FACE_BOUND('',#160,.T.); +#162=ADVANCED_FACE('lower_cap',(#158,#161),#16,.T.); +#163=ORIENTED_EDGE('',*,*,#67,.T.); +#164=EDGE_LOOP('',(#163)); +#165=FACE_OUTER_BOUND('',#164,.T.); +#166=ORIENTED_EDGE('',*,*,#105,.T.); +#167=EDGE_LOOP('',(#166)); +#168=FACE_BOUND('',#167,.T.); +#169=ADVANCED_FACE('upper_cap',(#165,#168),#21,.T.); +#170=CLOSED_SHELL('',(#148,#155,#162,#169)); +#171=MANIFOLD_SOLID_BREP('annular-cylinder-0',#170); +#172=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#173=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#174=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#175=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217094306E-8),#172,'distance_accuracy_value','exchange error bound'); +#176=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#175)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#172,#173,#174)) REPRESENTATION_CONTEXT('','')); +#177=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#171),#176); +#178=APPLICATION_CONTEXT('automotive design'); +#179=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#178); +#180=PRODUCT_CONTEXT('',#178,'mechanical'); +#181=PRODUCT('annular-cylinder','annular-cylinder','',(#180)); +#182=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#181,.NOT_KNOWN.); +#183=PRODUCT_DEFINITION_CONTEXT('part definition',#178,'design'); +#184=PRODUCT_DEFINITION('','',#182,#183); +#185=PRODUCT_DEFINITION_SHAPE('','',#184); +#186=SHAPE_DEFINITION_REPRESENTATION(#185,#177); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.m.report.json b/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.m.report.json new file mode 100644 index 0000000..3b49082 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":6,"exchange_error_bound":1.000002842170943e-8,"faces":4,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.mm b/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.mm new file mode 100644 index 0000000..c9fce4a --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.mm @@ -0,0 +1,195 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('annular-cylinder','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=CYLINDRICAL_SURFACE('',#5,1.00000000000000000E3); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=CYLINDRICAL_SURFACE('',#10,4.00000000000000000E2); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#18=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#23=VERTEX_POINT('',#22); +#24=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#25=VERTEX_POINT('',#24); +#26=CARTESIAN_POINT('',(4.00000000000000000E2,0.00000000000000000E0,0.00000000000000000E0)); +#27=VERTEX_POINT('',#26); +#28=CARTESIAN_POINT('',(4.00000000000000000E2,2.00000000000000000E3,0.00000000000000000E0)); +#29=VERTEX_POINT('',#28); +#30=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#31=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#32=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=AXIS2_PLACEMENT_3D('',#30,#31,#32); +#34=CIRCLE('',#33,1.00000000000000000E3); +#35=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#36=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#37=VECTOR('',#36,1.00000000000000000E0); +#38=LINE('',#35,#37); +#39=DEFINITIONAL_REPRESENTATION('',(#38),#1); +#40=PCURVE('',#6,#39); +#41=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#42=DIRECTION('',(1.00000000000000000E3,0.00000000000000000E0)); +#43=AXIS2_PLACEMENT_2D('',#41,#42); +#44=CIRCLE('',#43,1.00000000000000000E3); +#45=DEFINITIONAL_REPRESENTATION('',(#44),#1); +#46=PCURVE('',#16,#45); +#47=SURFACE_CURVE('',#34,(#40,#46),.CURVE_3D.); +#48=EDGE_CURVE('',#23,#23,#47,.T.); +#49=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#50=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#51=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#52=AXIS2_PLACEMENT_3D('',#49,#50,#51); +#53=CIRCLE('',#52,1.00000000000000000E3); +#54=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#55=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#56=VECTOR('',#55,1.00000000000000000E0); +#57=LINE('',#54,#56); +#58=DEFINITIONAL_REPRESENTATION('',(#57),#1); +#59=PCURVE('',#6,#58); +#60=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#61=DIRECTION('',(1.00000000000000000E3,0.00000000000000000E0)); +#62=AXIS2_PLACEMENT_2D('',#60,#61); +#63=CIRCLE('',#62,1.00000000000000000E3); +#64=DEFINITIONAL_REPRESENTATION('',(#63),#1); +#65=PCURVE('',#21,#64); +#66=SURFACE_CURVE('',#53,(#59,#65),.CURVE_3D.); +#67=EDGE_CURVE('',#25,#25,#66,.T.); +#68=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#69=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#70=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#71=AXIS2_PLACEMENT_3D('',#68,#69,#70); +#72=CIRCLE('',#71,4.00000000000000000E2); +#73=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#74=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#75=VECTOR('',#74,1.00000000000000000E0); +#76=LINE('',#73,#75); +#77=DEFINITIONAL_REPRESENTATION('',(#76),#1); +#78=PCURVE('',#11,#77); +#79=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#80=DIRECTION('',(4.00000000000000000E2,0.00000000000000000E0)); +#81=AXIS2_PLACEMENT_2D('',#79,#80); +#82=CIRCLE('',#81,4.00000000000000000E2); +#83=DEFINITIONAL_REPRESENTATION('',(#82),#1); +#84=PCURVE('',#16,#83); +#85=SURFACE_CURVE('',#72,(#78,#84),.CURVE_3D.); +#86=EDGE_CURVE('',#27,#27,#85,.T.); +#87=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#88=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#90=AXIS2_PLACEMENT_3D('',#87,#88,#89); +#91=CIRCLE('',#90,4.00000000000000000E2); +#92=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#93=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#94=VECTOR('',#93,1.00000000000000000E0); +#95=LINE('',#92,#94); +#96=DEFINITIONAL_REPRESENTATION('',(#95),#1); +#97=PCURVE('',#11,#96); +#98=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#99=DIRECTION('',(4.00000000000000000E2,0.00000000000000000E0)); +#100=AXIS2_PLACEMENT_2D('',#98,#99); +#101=CIRCLE('',#100,4.00000000000000000E2); +#102=DEFINITIONAL_REPRESENTATION('',(#101),#1); +#103=PCURVE('',#21,#102); +#104=SURFACE_CURVE('',#91,(#97,#103),.CURVE_3D.); +#105=EDGE_CURVE('',#29,#29,#104,.T.); +#106=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#107=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E3); +#109=LINE('',#106,#108); +#110=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#111=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#112=VECTOR('',#111,1.00000000000000000E3); +#113=LINE('',#110,#112); +#114=DEFINITIONAL_REPRESENTATION('',(#113),#1); +#115=PCURVE('',#6,#114); +#116=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#117=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#118=VECTOR('',#117,1.00000000000000000E3); +#119=LINE('',#116,#118); +#120=DEFINITIONAL_REPRESENTATION('',(#119),#1); +#121=PCURVE('',#6,#120); +#122=SEAM_CURVE('',#109,(#115,#121),.CURVE_3D.); +#123=EDGE_CURVE('',#23,#25,#122,.T.); +#124=CARTESIAN_POINT('',(4.00000000000000000E2,0.00000000000000000E0,0.00000000000000000E0)); +#125=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E3); +#127=LINE('',#124,#126); +#128=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#129=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#130=VECTOR('',#129,1.00000000000000000E3); +#131=LINE('',#128,#130); +#132=DEFINITIONAL_REPRESENTATION('',(#131),#1); +#133=PCURVE('',#11,#132); +#134=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#135=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#136=VECTOR('',#135,1.00000000000000000E3); +#137=LINE('',#134,#136); +#138=DEFINITIONAL_REPRESENTATION('',(#137),#1); +#139=PCURVE('',#11,#138); +#140=SEAM_CURVE('',#127,(#133,#139),.CURVE_3D.); +#141=EDGE_CURVE('',#27,#29,#140,.T.); +#142=ORIENTED_EDGE('',*,*,#48,.T.); +#143=ORIENTED_EDGE('',*,*,#123,.T.); +#144=ORIENTED_EDGE('',*,*,#67,.F.); +#145=ORIENTED_EDGE('',*,*,#123,.F.); +#146=EDGE_LOOP('',(#142,#143,#144,#145)); +#147=FACE_OUTER_BOUND('',#146,.T.); +#148=ADVANCED_FACE('outer',(#147),#6,.T.); +#149=ORIENTED_EDGE('',*,*,#86,.T.); +#150=ORIENTED_EDGE('',*,*,#141,.T.); +#151=ORIENTED_EDGE('',*,*,#105,.F.); +#152=ORIENTED_EDGE('',*,*,#141,.F.); +#153=EDGE_LOOP('',(#149,#150,#151,#152)); +#154=FACE_OUTER_BOUND('',#153,.T.); +#155=ADVANCED_FACE('inner',(#154),#11,.F.); +#156=ORIENTED_EDGE('',*,*,#48,.F.); +#157=EDGE_LOOP('',(#156)); +#158=FACE_OUTER_BOUND('',#157,.T.); +#159=ORIENTED_EDGE('',*,*,#86,.F.); +#160=EDGE_LOOP('',(#159)); +#161=FACE_BOUND('',#160,.T.); +#162=ADVANCED_FACE('lower_cap',(#158,#161),#16,.T.); +#163=ORIENTED_EDGE('',*,*,#67,.T.); +#164=EDGE_LOOP('',(#163)); +#165=FACE_OUTER_BOUND('',#164,.T.); +#166=ORIENTED_EDGE('',*,*,#105,.T.); +#167=EDGE_LOOP('',(#166)); +#168=FACE_BOUND('',#167,.T.); +#169=ADVANCED_FACE('upper_cap',(#165,#168),#21,.T.); +#170=CLOSED_SHELL('',(#148,#155,#162,#169)); +#171=MANIFOLD_SOLID_BREP('annular-cylinder-0',#170); +#172=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#173=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#174=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#175=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217094312E-5),#172,'distance_accuracy_value','exchange error bound'); +#176=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#175)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#172,#173,#174)) REPRESENTATION_CONTEXT('','')); +#177=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#171),#176); +#178=APPLICATION_CONTEXT('automotive design'); +#179=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#178); +#180=PRODUCT_CONTEXT('',#178,'mechanical'); +#181=PRODUCT('annular-cylinder','annular-cylinder','',(#180)); +#182=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#181,.NOT_KNOWN.); +#183=PRODUCT_DEFINITION_CONTEXT('part definition',#178,'design'); +#184=PRODUCT_DEFINITION('','',#182,#183); +#185=PRODUCT_DEFINITION_SHAPE('','',#184); +#186=SHAPE_DEFINITION_REPRESENTATION(#185,#177); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.mm.report.json new file mode 100644 index 0000000..5eec7ae --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/annular-cylinder.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":6,"exchange_error_bound":0.000010000028421709431,"faces":4,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/annular-cylinder.tess.json b/main/opengeometry/tests/fixtures/cases/annular-cylinder.tess.json new file mode 100644 index 0000000..a8b25c8 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/annular-cylinder.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[0,1,34,0,34,33,1,2,35,1,35,34,2,3,36,2,36,35,3,4,37,3,37,36,4,5,38,4,38,37,5,6,39,5,39,38,6,7,40,6,40,39,7,8,41,7,41,40,8,9,42,8,42,41,9,10,43,9,43,42,10,11,44,10,44,43,11,12,45,11,45,44,12,13,46,12,46,45,13,14,47,13,47,46,14,15,48,14,48,47,15,16,49,15,49,48,16,17,50,16,50,49,17,18,51,17,51,50,18,19,52,18,52,51,19,20,53,19,53,52,20,21,54,20,54,53,21,22,55,21,55,54,22,23,56,22,56,55,23,24,57,23,57,56,24,25,58,24,58,57,25,26,59,25,59,58,26,27,60,26,60,59,27,28,61,27,61,60,28,29,62,28,62,61,29,30,63,29,63,62,30,31,64,30,64,63,31,32,65,31,65,64,66,100,67,66,99,100,67,101,68,67,100,101,68,102,69,68,101,102,69,103,70,69,102,103,70,104,71,70,103,104,71,105,72,71,104,105,72,106,73,72,105,106,73,107,74,73,106,107,74,108,75,74,107,108,75,109,76,75,108,109,76,110,77,76,109,110,77,111,78,77,110,111,78,112,79,78,111,112,79,113,80,79,112,113,80,114,81,80,113,114,81,115,82,81,114,115,82,116,83,82,115,116,83,117,84,83,116,117,84,118,85,84,117,118,85,119,86,85,118,119,86,120,87,86,119,120,87,121,88,87,120,121,88,122,89,88,121,122,89,123,90,89,122,123,90,124,91,90,123,124,91,125,92,91,124,125,92,126,93,92,125,126,93,127,94,93,126,127,94,128,95,94,127,128,95,129,96,95,128,129,96,130,97,96,129,130,97,131,98,97,130,131,163,132,133,133,134,135,135,136,137,137,138,139,139,140,141,141,142,143,143,144,145,145,146,147,147,148,180,181,180,148,148,149,150,150,151,152,152,153,154,154,155,156,156,157,158,158,159,160,160,161,162,162,163,133,133,135,137,137,139,141,141,143,145,145,147,180,182,181,148,148,150,152,152,154,156,156,158,160,160,162,133,133,137,141,145,180,179,183,182,148,148,152,156,156,160,133,141,145,179,184,183,148,141,179,178,185,184,148,141,178,177,186,185,148,141,177,176,186,148,156,141,176,175,187,186,156,141,175,174,188,187,156,141,174,173,189,188,156,141,173,172,190,189,156,141,172,171,191,190,156,133,141,171,192,191,156,133,171,170,193,192,156,133,170,169,194,193,156,133,169,168,194,156,133,133,168,167,195,194,133,133,167,166,164,195,133,133,166,165,165,164,133,227,196,197,197,198,199,199,200,201,201,202,203,203,204,205,205,206,207,207,208,209,209,210,211,211,212,244,245,244,212,212,213,214,214,215,216,216,217,218,218,219,220,220,221,222,222,223,224,224,225,226,226,227,197,197,199,201,201,203,205,205,207,209,209,211,244,246,245,212,212,214,216,216,218,220,220,222,224,224,226,197,197,201,205,209,244,243,247,246,212,212,216,220,220,224,197,205,209,243,248,247,212,205,243,242,249,248,212,205,242,241,250,249,212,205,241,240,250,212,220,205,240,239,251,250,220,205,239,238,252,251,220,205,238,237,253,252,220,205,237,236,254,253,220,205,236,235,255,254,220,197,205,235,256,255,220,197,235,234,257,256,220,197,234,233,258,257,220,197,233,232,258,220,197,197,232,231,259,258,197,197,231,230,228,259,197,197,230,229,229,228,197],"normals":[1.0,0.0,0.0,0.9807852506637573,0.0,-0.19509032368659973,0.9238795042037964,0.0,-0.3826834261417389,0.8314695954322815,0.0,-0.5555702447891235,0.7071067690849304,0.0,-0.7071067690849304,0.5555702447891235,0.0,-0.8314695954322815,0.3826834261417389,0.0,-0.9238795042037964,0.19509032368659973,0.0,-0.9807852506637573,6.123234262925839e-17,0.0,-1.0,-0.19509032368659973,0.0,-0.9807852506637573,-0.3826834261417389,0.0,-0.9238795042037964,-0.5555702447891235,0.0,-0.8314695954322815,-0.7071067690849304,0.0,-0.7071067690849304,-0.8314695954322815,0.0,-0.5555702447891235,-0.9238795042037964,0.0,-0.3826834261417389,-0.9807852506637573,0.0,-0.19509032368659973,-1.0,0.0,-1.2246468525851679e-16,-0.9807852506637573,0.0,0.19509032368659973,-0.9238795042037964,0.0,0.3826834261417389,-0.8314695954322815,0.0,0.5555702447891235,-0.7071067690849304,0.0,0.7071067690849304,-0.5555702447891235,0.0,0.8314695954322815,-0.3826834261417389,0.0,0.9238795042037964,-0.19509032368659973,0.0,0.9807852506637573,-1.8369701465288538e-16,0.0,1.0,0.19509032368659973,0.0,0.9807852506637573,0.3826834261417389,0.0,0.9238795042037964,0.5555702447891235,0.0,0.8314695954322815,0.7071067690849304,0.0,0.7071067690849304,0.8314695954322815,0.0,0.5555702447891235,0.9238795042037964,0.0,0.3826834261417389,0.9807852506637573,0.0,0.19509032368659973,1.0,0.0,2.4492937051703357e-16,1.0,0.0,0.0,0.9807852506637573,0.0,-0.19509032368659973,0.9238795042037964,0.0,-0.3826834261417389,0.8314695954322815,0.0,-0.5555702447891235,0.7071067690849304,0.0,-0.7071067690849304,0.5555702447891235,0.0,-0.8314695954322815,0.3826834261417389,0.0,-0.9238795042037964,0.19509032368659973,0.0,-0.9807852506637573,6.123234262925839e-17,0.0,-1.0,-0.19509032368659973,0.0,-0.9807852506637573,-0.3826834261417389,0.0,-0.9238795042037964,-0.5555702447891235,0.0,-0.8314695954322815,-0.7071067690849304,0.0,-0.7071067690849304,-0.8314695954322815,0.0,-0.5555702447891235,-0.9238795042037964,0.0,-0.3826834261417389,-0.9807852506637573,0.0,-0.19509032368659973,-1.0,0.0,-1.2246468525851679e-16,-0.9807852506637573,0.0,0.19509032368659973,-0.9238795042037964,0.0,0.3826834261417389,-0.8314695954322815,0.0,0.5555702447891235,-0.7071067690849304,0.0,0.7071067690849304,-0.5555702447891235,0.0,0.8314695954322815,-0.3826834261417389,0.0,0.9238795042037964,-0.19509032368659973,0.0,0.9807852506637573,-1.8369701465288538e-16,0.0,1.0,0.19509032368659973,0.0,0.9807852506637573,0.3826834261417389,0.0,0.9238795042037964,0.5555702447891235,0.0,0.8314695954322815,0.7071067690849304,0.0,0.7071067690849304,0.8314695954322815,0.0,0.5555702447891235,0.9238795042037964,0.0,0.3826834261417389,0.9807852506637573,0.0,0.19509032368659973,1.0,0.0,2.4492937051703357e-16,-1.0,-0.0,-0.0,-0.9807852506637573,-0.0,0.19509032368659973,-0.9238795042037964,-0.0,0.3826834261417389,-0.8314695954322815,-0.0,0.5555702447891235,-0.7071067690849304,-0.0,0.7071067690849304,-0.5555702447891235,-0.0,0.8314695954322815,-0.3826834261417389,-0.0,0.9238795042037964,-0.19509032368659973,-0.0,0.9807852506637573,-6.123234262925839e-17,-0.0,1.0,0.19509032368659973,-0.0,0.9807852506637573,0.3826834261417389,-0.0,0.9238795042037964,0.5555702447891235,-0.0,0.8314695954322815,0.7071067690849304,-0.0,0.7071067690849304,0.8314695954322815,-0.0,0.5555702447891235,0.9238795042037964,-0.0,0.3826834261417389,0.9807852506637573,-0.0,0.19509032368659973,1.0,-0.0,1.2246468525851679e-16,0.9807852506637573,-0.0,-0.19509032368659973,0.9238795042037964,-0.0,-0.3826834261417389,0.8314695954322815,-0.0,-0.5555702447891235,0.7071067690849304,-0.0,-0.7071067690849304,0.5555702447891235,-0.0,-0.8314695954322815,0.3826834261417389,-0.0,-0.9238795042037964,0.19509032368659973,-0.0,-0.9807852506637573,1.8369701465288538e-16,-0.0,-1.0,-0.19509032368659973,-0.0,-0.9807852506637573,-0.3826834261417389,-0.0,-0.9238795042037964,-0.5555702447891235,-0.0,-0.8314695954322815,-0.7071067690849304,-0.0,-0.7071067690849304,-0.8314695954322815,-0.0,-0.5555702447891235,-0.9238795042037964,-0.0,-0.3826834261417389,-0.9807852506637573,-0.0,-0.19509032368659973,-1.0,-0.0,-2.4492937051703357e-16,-1.0,-0.0,-0.0,-0.9807852506637573,-0.0,0.19509032368659973,-0.9238795042037964,-0.0,0.3826834261417389,-0.8314695954322815,-0.0,0.5555702447891235,-0.7071067690849304,-0.0,0.7071067690849304,-0.5555702447891235,-0.0,0.8314695954322815,-0.3826834261417389,-0.0,0.9238795042037964,-0.19509032368659973,-0.0,0.9807852506637573,-6.123234262925839e-17,-0.0,1.0,0.19509032368659973,-0.0,0.9807852506637573,0.3826834261417389,-0.0,0.9238795042037964,0.5555702447891235,-0.0,0.8314695954322815,0.7071067690849304,-0.0,0.7071067690849304,0.8314695954322815,-0.0,0.5555702447891235,0.9238795042037964,-0.0,0.3826834261417389,0.9807852506637573,-0.0,0.19509032368659973,1.0,-0.0,1.2246468525851679e-16,0.9807852506637573,-0.0,-0.19509032368659973,0.9238795042037964,-0.0,-0.3826834261417389,0.8314695954322815,-0.0,-0.5555702447891235,0.7071067690849304,-0.0,-0.7071067690849304,0.5555702447891235,-0.0,-0.8314695954322815,0.3826834261417389,-0.0,-0.9238795042037964,0.19509032368659973,-0.0,-0.9807852506637573,1.8369701465288538e-16,-0.0,-1.0,-0.19509032368659973,-0.0,-0.9807852506637573,-0.3826834261417389,-0.0,-0.9238795042037964,-0.5555702447891235,-0.0,-0.8314695954322815,-0.7071067690849304,-0.0,-0.7071067690849304,-0.8314695954322815,-0.0,-0.5555702447891235,-0.9238795042037964,-0.0,-0.3826834261417389,-0.9807852506637573,-0.0,-0.19509032368659973,-1.0,-0.0,-2.4492937051703357e-16,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0],"outlineEdgeIds":[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,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,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],"outlinePositions":[1.0,0.0,0.0,0.9807852804032304,0.0,-0.19509032201612825,0.9807852804032304,0.0,-0.19509032201612825,0.9238795325112867,0.0,-0.3826834323650898,0.9238795325112867,0.0,-0.3826834323650898,0.8314696123025452,0.0,-0.5555702330196022,0.8314696123025452,0.0,-0.5555702330196022,0.7071067811865476,0.0,-0.7071067811865475,0.7071067811865476,0.0,-0.7071067811865475,0.5555702330196023,0.0,-0.8314696123025452,0.5555702330196023,0.0,-0.8314696123025452,0.38268343236508984,0.0,-0.9238795325112867,0.38268343236508984,0.0,-0.9238795325112867,0.19509032201612833,0.0,-0.9807852804032304,0.19509032201612833,0.0,-0.9807852804032304,6.123233995736766e-17,0.0,-1.0,6.123233995736766e-17,0.0,-1.0,-0.1950903220161282,0.0,-0.9807852804032304,-0.1950903220161282,0.0,-0.9807852804032304,-0.3826834323650897,0.0,-0.9238795325112867,-0.3826834323650897,0.0,-0.9238795325112867,-0.555570233019602,0.0,-0.8314696123025453,-0.555570233019602,0.0,-0.8314696123025453,-0.7071067811865475,0.0,-0.7071067811865476,-0.7071067811865475,0.0,-0.7071067811865476,-0.8314696123025453,0.0,-0.5555702330196022,-0.8314696123025453,0.0,-0.5555702330196022,-0.9238795325112867,0.0,-0.3826834323650899,-0.9238795325112867,0.0,-0.3826834323650899,-0.9807852804032304,0.0,-0.1950903220161286,-0.9807852804032304,0.0,-0.1950903220161286,-1.0,0.0,-1.2246467991473532e-16,-1.0,0.0,-1.2246467991473532e-16,-0.9807852804032304,0.0,0.19509032201612836,-0.9807852804032304,0.0,0.19509032201612836,-0.9238795325112868,0.0,0.38268343236508967,-0.9238795325112868,0.0,0.38268343236508967,-0.8314696123025455,0.0,0.555570233019602,-0.8314696123025455,0.0,0.555570233019602,-0.7071067811865477,0.0,0.7071067811865475,-0.7071067811865477,0.0,0.7071067811865475,-0.5555702330196022,0.0,0.8314696123025452,-0.5555702330196022,0.0,0.8314696123025452,-0.38268343236509034,0.0,0.9238795325112865,-0.38268343236509034,0.0,0.9238795325112865,-0.19509032201612866,0.0,0.9807852804032303,-0.19509032201612866,0.0,0.9807852804032303,-1.8369701987210297e-16,0.0,1.0,-1.8369701987210297e-16,0.0,1.0,0.1950903220161283,0.0,0.9807852804032304,0.1950903220161283,0.0,0.9807852804032304,0.38268343236509,0.0,0.9238795325112866,0.38268343236509,0.0,0.9238795325112866,0.5555702330196018,0.0,0.8314696123025455,0.5555702330196018,0.0,0.8314696123025455,0.7071067811865474,0.0,0.7071067811865477,0.7071067811865474,0.0,0.7071067811865477,0.8314696123025452,0.0,0.5555702330196022,0.8314696123025452,0.0,0.5555702330196022,0.9238795325112865,0.0,0.3826834323650904,0.9238795325112865,0.0,0.3826834323650904,0.9807852804032303,0.0,0.19509032201612872,0.9807852804032303,0.0,0.19509032201612872,1.0,0.0,0.0,1.0,2.0,0.0,0.9807852804032304,2.0,-0.19509032201612825,0.9807852804032304,2.0,-0.19509032201612825,0.9238795325112867,2.0,-0.3826834323650898,0.9238795325112867,2.0,-0.3826834323650898,0.8314696123025452,2.0,-0.5555702330196022,0.8314696123025452,2.0,-0.5555702330196022,0.7071067811865476,2.0,-0.7071067811865475,0.7071067811865476,2.0,-0.7071067811865475,0.5555702330196023,2.0,-0.8314696123025452,0.5555702330196023,2.0,-0.8314696123025452,0.38268343236508984,2.0,-0.9238795325112867,0.38268343236508984,2.0,-0.9238795325112867,0.19509032201612833,2.0,-0.9807852804032304,0.19509032201612833,2.0,-0.9807852804032304,6.123233995736766e-17,2.0,-1.0,6.123233995736766e-17,2.0,-1.0,-0.1950903220161282,2.0,-0.9807852804032304,-0.1950903220161282,2.0,-0.9807852804032304,-0.3826834323650897,2.0,-0.9238795325112867,-0.3826834323650897,2.0,-0.9238795325112867,-0.555570233019602,2.0,-0.8314696123025453,-0.555570233019602,2.0,-0.8314696123025453,-0.7071067811865475,2.0,-0.7071067811865476,-0.7071067811865475,2.0,-0.7071067811865476,-0.8314696123025453,2.0,-0.5555702330196022,-0.8314696123025453,2.0,-0.5555702330196022,-0.9238795325112867,2.0,-0.3826834323650899,-0.9238795325112867,2.0,-0.3826834323650899,-0.9807852804032304,2.0,-0.1950903220161286,-0.9807852804032304,2.0,-0.1950903220161286,-1.0,2.0,-1.2246467991473532e-16,-1.0,2.0,-1.2246467991473532e-16,-0.9807852804032304,2.0,0.19509032201612836,-0.9807852804032304,2.0,0.19509032201612836,-0.9238795325112868,2.0,0.38268343236508967,-0.9238795325112868,2.0,0.38268343236508967,-0.8314696123025455,2.0,0.555570233019602,-0.8314696123025455,2.0,0.555570233019602,-0.7071067811865477,2.0,0.7071067811865475,-0.7071067811865477,2.0,0.7071067811865475,-0.5555702330196022,2.0,0.8314696123025452,-0.5555702330196022,2.0,0.8314696123025452,-0.38268343236509034,2.0,0.9238795325112865,-0.38268343236509034,2.0,0.9238795325112865,-0.19509032201612866,2.0,0.9807852804032303,-0.19509032201612866,2.0,0.9807852804032303,-1.8369701987210297e-16,2.0,1.0,-1.8369701987210297e-16,2.0,1.0,0.1950903220161283,2.0,0.9807852804032304,0.1950903220161283,2.0,0.9807852804032304,0.38268343236509,2.0,0.9238795325112866,0.38268343236509,2.0,0.9238795325112866,0.5555702330196018,2.0,0.8314696123025455,0.5555702330196018,2.0,0.8314696123025455,0.7071067811865474,2.0,0.7071067811865477,0.7071067811865474,2.0,0.7071067811865477,0.8314696123025452,2.0,0.5555702330196022,0.8314696123025452,2.0,0.5555702330196022,0.9238795325112865,2.0,0.3826834323650904,0.9238795325112865,2.0,0.3826834323650904,0.9807852804032303,2.0,0.19509032201612872,0.9807852804032303,2.0,0.19509032201612872,1.0,2.0,0.0,0.4,0.0,0.0,0.3923141121612922,0.0,-0.0780361288064513,0.3923141121612922,0.0,-0.0780361288064513,0.36955181300451473,0.0,-0.15307337294603593,0.36955181300451473,0.0,-0.15307337294603593,0.3325878449210181,0.0,-0.22222809320784087,0.3325878449210181,0.0,-0.22222809320784087,0.28284271247461906,0.0,-0.282842712474619,0.28284271247461906,0.0,-0.282842712474619,0.22222809320784093,0.0,-0.3325878449210181,0.22222809320784093,0.0,-0.3325878449210181,0.15307337294603596,0.0,-0.36955181300451473,0.15307337294603596,0.0,-0.36955181300451473,0.07803612880645133,0.0,-0.3923141121612922,0.07803612880645133,0.0,-0.3923141121612922,2.4492935982947065e-17,0.0,-0.4,2.4492935982947065e-17,0.0,-0.4,-0.07803612880645128,0.0,-0.3923141121612922,-0.07803612880645128,0.0,-0.3923141121612922,-0.1530733729460359,0.0,-0.36955181300451473,-0.1530733729460359,0.0,-0.36955181300451473,-0.2222280932078408,0.0,-0.33258784492101817,-0.2222280932078408,0.0,-0.33258784492101817,-0.282842712474619,0.0,-0.28284271247461906,-0.282842712474619,0.0,-0.28284271247461906,-0.33258784492101817,0.0,-0.22222809320784087,-0.33258784492101817,0.0,-0.22222809320784087,-0.36955181300451473,0.0,-0.15307337294603596,-0.36955181300451473,0.0,-0.15307337294603596,-0.3923141121612922,0.0,-0.07803612880645144,-0.3923141121612922,0.0,-0.07803612880645144,-0.4,0.0,-4.898587196589413e-17,-0.4,0.0,-4.898587196589413e-17,-0.3923141121612922,0.0,0.07803612880645135,-0.3923141121612922,0.0,0.07803612880645135,-0.3695518130045148,0.0,0.15307337294603587,-0.3695518130045148,0.0,0.15307337294603587,-0.3325878449210182,0.0,0.2222280932078408,-0.3325878449210182,0.0,0.2222280932078408,-0.28284271247461906,0.0,0.282842712474619,-0.28284271247461906,0.0,0.282842712474619,-0.22222809320784087,0.0,0.3325878449210181,-0.22222809320784087,0.0,0.3325878449210181,-0.15307337294603615,0.0,0.3695518130045146,-0.15307337294603615,0.0,0.3695518130045146,-0.07803612880645147,0.0,0.39231411216129214,-0.07803612880645147,0.0,0.39231411216129214,-7.347880794884119e-17,0.0,0.4,-7.347880794884119e-17,0.0,0.4,0.07803612880645133,0.0,0.3923141121612922,0.07803612880645133,0.0,0.3923141121612922,0.153073372946036,0.0,0.3695518130045147,0.153073372946036,0.0,0.3695518130045147,0.22222809320784076,0.0,0.3325878449210182,0.22222809320784076,0.0,0.3325878449210182,0.28284271247461895,0.0,0.28284271247461906,0.28284271247461895,0.0,0.28284271247461906,0.3325878449210181,0.0,0.22222809320784087,0.3325878449210181,0.0,0.22222809320784087,0.3695518130045146,0.0,0.15307337294603618,0.3695518130045146,0.0,0.15307337294603618,0.39231411216129214,0.0,0.0780361288064515,0.39231411216129214,0.0,0.0780361288064515,0.4,0.0,0.0,0.4,2.0,0.0,0.3923141121612922,2.0,-0.0780361288064513,0.3923141121612922,2.0,-0.0780361288064513,0.36955181300451473,2.0,-0.15307337294603593,0.36955181300451473,2.0,-0.15307337294603593,0.3325878449210181,2.0,-0.22222809320784087,0.3325878449210181,2.0,-0.22222809320784087,0.28284271247461906,2.0,-0.282842712474619,0.28284271247461906,2.0,-0.282842712474619,0.22222809320784093,2.0,-0.3325878449210181,0.22222809320784093,2.0,-0.3325878449210181,0.15307337294603596,2.0,-0.36955181300451473,0.15307337294603596,2.0,-0.36955181300451473,0.07803612880645133,2.0,-0.3923141121612922,0.07803612880645133,2.0,-0.3923141121612922,2.4492935982947065e-17,2.0,-0.4,2.4492935982947065e-17,2.0,-0.4,-0.07803612880645128,2.0,-0.3923141121612922,-0.07803612880645128,2.0,-0.3923141121612922,-0.1530733729460359,2.0,-0.36955181300451473,-0.1530733729460359,2.0,-0.36955181300451473,-0.2222280932078408,2.0,-0.33258784492101817,-0.2222280932078408,2.0,-0.33258784492101817,-0.282842712474619,2.0,-0.28284271247461906,-0.282842712474619,2.0,-0.28284271247461906,-0.33258784492101817,2.0,-0.22222809320784087,-0.33258784492101817,2.0,-0.22222809320784087,-0.36955181300451473,2.0,-0.15307337294603596,-0.36955181300451473,2.0,-0.15307337294603596,-0.3923141121612922,2.0,-0.07803612880645144,-0.3923141121612922,2.0,-0.07803612880645144,-0.4,2.0,-4.898587196589413e-17,-0.4,2.0,-4.898587196589413e-17,-0.3923141121612922,2.0,0.07803612880645135,-0.3923141121612922,2.0,0.07803612880645135,-0.3695518130045148,2.0,0.15307337294603587,-0.3695518130045148,2.0,0.15307337294603587,-0.3325878449210182,2.0,0.2222280932078408,-0.3325878449210182,2.0,0.2222280932078408,-0.28284271247461906,2.0,0.282842712474619,-0.28284271247461906,2.0,0.282842712474619,-0.22222809320784087,2.0,0.3325878449210181,-0.22222809320784087,2.0,0.3325878449210181,-0.15307337294603615,2.0,0.3695518130045146,-0.15307337294603615,2.0,0.3695518130045146,-0.07803612880645147,2.0,0.39231411216129214,-0.07803612880645147,2.0,0.39231411216129214,-7.347880794884119e-17,2.0,0.4,-7.347880794884119e-17,2.0,0.4,0.07803612880645133,2.0,0.3923141121612922,0.07803612880645133,2.0,0.3923141121612922,0.153073372946036,2.0,0.3695518130045147,0.153073372946036,2.0,0.3695518130045147,0.22222809320784076,2.0,0.3325878449210182,0.22222809320784076,2.0,0.3325878449210182,0.28284271247461895,2.0,0.28284271247461906,0.28284271247461895,2.0,0.28284271247461906,0.3325878449210181,2.0,0.22222809320784087,0.3325878449210181,2.0,0.22222809320784087,0.3695518130045146,2.0,0.15307337294603618,0.3695518130045146,2.0,0.15307337294603618,0.39231411216129214,2.0,0.0780361288064515,0.39231411216129214,2.0,0.0780361288064515,0.4,2.0,0.0],"positions":[1.0,0.0,0.0,0.9807852804032304,0.0,-0.19509032201612825,0.9238795325112867,0.0,-0.3826834323650898,0.8314696123025452,0.0,-0.5555702330196022,0.7071067811865476,0.0,-0.7071067811865475,0.5555702330196023,0.0,-0.8314696123025452,0.38268343236508984,0.0,-0.9238795325112867,0.19509032201612833,0.0,-0.9807852804032304,6.123233995736766e-17,0.0,-1.0,-0.1950903220161282,0.0,-0.9807852804032304,-0.3826834323650897,0.0,-0.9238795325112867,-0.555570233019602,0.0,-0.8314696123025453,-0.7071067811865475,0.0,-0.7071067811865476,-0.8314696123025453,0.0,-0.5555702330196022,-0.9238795325112867,0.0,-0.3826834323650899,-0.9807852804032304,0.0,-0.1950903220161286,-1.0,0.0,-1.2246467991473532e-16,-0.9807852804032304,0.0,0.19509032201612836,-0.9238795325112868,0.0,0.38268343236508967,-0.8314696123025455,0.0,0.555570233019602,-0.7071067811865477,0.0,0.7071067811865475,-0.5555702330196022,0.0,0.8314696123025452,-0.38268343236509034,0.0,0.9238795325112865,-0.19509032201612866,0.0,0.9807852804032303,-1.8369701987210297e-16,0.0,1.0,0.1950903220161283,0.0,0.9807852804032304,0.38268343236509,0.0,0.9238795325112866,0.5555702330196018,0.0,0.8314696123025455,0.7071067811865474,0.0,0.7071067811865477,0.8314696123025452,0.0,0.5555702330196022,0.9238795325112865,0.0,0.3826834323650904,0.9807852804032303,0.0,0.19509032201612872,1.0,0.0,0.0,1.0,2.0,0.0,0.9807852804032304,2.0,-0.19509032201612825,0.9238795325112867,2.0,-0.3826834323650898,0.8314696123025452,2.0,-0.5555702330196022,0.7071067811865476,2.0,-0.7071067811865475,0.5555702330196023,2.0,-0.8314696123025452,0.38268343236508984,2.0,-0.9238795325112867,0.19509032201612833,2.0,-0.9807852804032304,6.123233995736766e-17,2.0,-1.0,-0.1950903220161282,2.0,-0.9807852804032304,-0.3826834323650897,2.0,-0.9238795325112867,-0.555570233019602,2.0,-0.8314696123025453,-0.7071067811865475,2.0,-0.7071067811865476,-0.8314696123025453,2.0,-0.5555702330196022,-0.9238795325112867,2.0,-0.3826834323650899,-0.9807852804032304,2.0,-0.1950903220161286,-1.0,2.0,-1.2246467991473532e-16,-0.9807852804032304,2.0,0.19509032201612836,-0.9238795325112868,2.0,0.38268343236508967,-0.8314696123025455,2.0,0.555570233019602,-0.7071067811865477,2.0,0.7071067811865475,-0.5555702330196022,2.0,0.8314696123025452,-0.38268343236509034,2.0,0.9238795325112865,-0.19509032201612866,2.0,0.9807852804032303,-1.8369701987210297e-16,2.0,1.0,0.1950903220161283,2.0,0.9807852804032304,0.38268343236509,2.0,0.9238795325112866,0.5555702330196018,2.0,0.8314696123025455,0.7071067811865474,2.0,0.7071067811865477,0.8314696123025452,2.0,0.5555702330196022,0.9238795325112865,2.0,0.3826834323650904,0.9807852804032303,2.0,0.19509032201612872,1.0,2.0,0.0,0.4,0.0,0.0,0.3923141121612922,0.0,-0.0780361288064513,0.36955181300451473,0.0,-0.15307337294603593,0.3325878449210181,0.0,-0.22222809320784087,0.28284271247461906,0.0,-0.282842712474619,0.22222809320784093,0.0,-0.3325878449210181,0.15307337294603596,0.0,-0.36955181300451473,0.07803612880645133,0.0,-0.3923141121612922,2.4492935982947065e-17,0.0,-0.4,-0.07803612880645128,0.0,-0.3923141121612922,-0.1530733729460359,0.0,-0.36955181300451473,-0.2222280932078408,0.0,-0.33258784492101817,-0.282842712474619,0.0,-0.28284271247461906,-0.33258784492101817,0.0,-0.22222809320784087,-0.36955181300451473,0.0,-0.15307337294603596,-0.3923141121612922,0.0,-0.07803612880645144,-0.4,0.0,-4.898587196589413e-17,-0.3923141121612922,0.0,0.07803612880645135,-0.3695518130045148,0.0,0.15307337294603587,-0.3325878449210182,0.0,0.2222280932078408,-0.28284271247461906,0.0,0.282842712474619,-0.22222809320784087,0.0,0.3325878449210181,-0.15307337294603615,0.0,0.3695518130045146,-0.07803612880645147,0.0,0.39231411216129214,-7.347880794884119e-17,0.0,0.4,0.07803612880645133,0.0,0.3923141121612922,0.153073372946036,0.0,0.3695518130045147,0.22222809320784076,0.0,0.3325878449210182,0.28284271247461895,0.0,0.28284271247461906,0.3325878449210181,0.0,0.22222809320784087,0.3695518130045146,0.0,0.15307337294603618,0.39231411216129214,0.0,0.0780361288064515,0.4,0.0,0.0,0.4,2.0,0.0,0.3923141121612922,2.0,-0.0780361288064513,0.36955181300451473,2.0,-0.15307337294603593,0.3325878449210181,2.0,-0.22222809320784087,0.28284271247461906,2.0,-0.282842712474619,0.22222809320784093,2.0,-0.3325878449210181,0.15307337294603596,2.0,-0.36955181300451473,0.07803612880645133,2.0,-0.3923141121612922,2.4492935982947065e-17,2.0,-0.4,-0.07803612880645128,2.0,-0.3923141121612922,-0.1530733729460359,2.0,-0.36955181300451473,-0.2222280932078408,2.0,-0.33258784492101817,-0.282842712474619,2.0,-0.28284271247461906,-0.33258784492101817,2.0,-0.22222809320784087,-0.36955181300451473,2.0,-0.15307337294603596,-0.3923141121612922,2.0,-0.07803612880645144,-0.4,2.0,-4.898587196589413e-17,-0.3923141121612922,2.0,0.07803612880645135,-0.3695518130045148,2.0,0.15307337294603587,-0.3325878449210182,2.0,0.2222280932078408,-0.28284271247461906,2.0,0.282842712474619,-0.22222809320784087,2.0,0.3325878449210181,-0.15307337294603615,2.0,0.3695518130045146,-0.07803612880645147,2.0,0.39231411216129214,-7.347880794884119e-17,2.0,0.4,0.07803612880645133,2.0,0.3923141121612922,0.153073372946036,2.0,0.3695518130045147,0.22222809320784076,2.0,0.3325878449210182,0.28284271247461895,2.0,0.28284271247461906,0.3325878449210181,2.0,0.22222809320784087,0.3695518130045146,2.0,0.15307337294603618,0.39231411216129214,2.0,0.0780361288064515,0.4,2.0,0.0,1.0,0.0,0.0,0.9807852804032303,0.0,0.19509032201612872,0.9238795325112865,0.0,0.3826834323650904,0.8314696123025452,0.0,0.5555702330196022,0.7071067811865474,0.0,0.7071067811865477,0.5555702330196018,0.0,0.8314696123025455,0.38268343236509,0.0,0.9238795325112866,0.1950903220161283,0.0,0.9807852804032304,-1.8369701987210297e-16,0.0,1.0,-0.19509032201612866,0.0,0.9807852804032303,-0.38268343236509034,0.0,0.9238795325112865,-0.5555702330196022,0.0,0.8314696123025452,-0.7071067811865477,0.0,0.7071067811865475,-0.8314696123025455,0.0,0.555570233019602,-0.9238795325112868,0.0,0.38268343236508967,-0.9807852804032304,0.0,0.19509032201612836,-1.0,0.0,-1.2246467991473532e-16,-0.9807852804032304,0.0,-0.1950903220161286,-0.9238795325112867,0.0,-0.3826834323650899,-0.8314696123025453,0.0,-0.5555702330196022,-0.7071067811865475,0.0,-0.7071067811865476,-0.555570233019602,0.0,-0.8314696123025453,-0.3826834323650897,0.0,-0.9238795325112867,-0.1950903220161282,0.0,-0.9807852804032304,6.123233995736766e-17,0.0,-1.0,0.19509032201612833,0.0,-0.9807852804032304,0.38268343236508984,0.0,-0.9238795325112867,0.5555702330196023,0.0,-0.8314696123025452,0.7071067811865476,0.0,-0.7071067811865475,0.8314696123025452,0.0,-0.5555702330196022,0.9238795325112867,0.0,-0.3826834323650898,0.9807852804032304,0.0,-0.19509032201612825,0.4,0.0,0.0,0.39231411216129214,0.0,0.0780361288064515,0.3695518130045146,0.0,0.15307337294603618,0.3325878449210181,0.0,0.22222809320784087,0.28284271247461895,0.0,0.28284271247461906,0.22222809320784076,0.0,0.3325878449210182,0.153073372946036,0.0,0.3695518130045147,0.07803612880645133,0.0,0.3923141121612922,-7.347880794884119e-17,0.0,0.4,-0.07803612880645147,0.0,0.39231411216129214,-0.15307337294603615,0.0,0.3695518130045146,-0.22222809320784087,0.0,0.3325878449210181,-0.28284271247461906,0.0,0.282842712474619,-0.3325878449210182,0.0,0.2222280932078408,-0.3695518130045148,0.0,0.15307337294603587,-0.3923141121612922,0.0,0.07803612880645135,-0.4,0.0,-4.898587196589413e-17,-0.3923141121612922,0.0,-0.07803612880645144,-0.36955181300451473,0.0,-0.15307337294603596,-0.33258784492101817,0.0,-0.22222809320784087,-0.282842712474619,0.0,-0.28284271247461906,-0.2222280932078408,0.0,-0.33258784492101817,-0.1530733729460359,0.0,-0.36955181300451473,-0.07803612880645128,0.0,-0.3923141121612922,2.4492935982947065e-17,0.0,-0.4,0.07803612880645133,0.0,-0.3923141121612922,0.15307337294603596,0.0,-0.36955181300451473,0.22222809320784093,0.0,-0.3325878449210181,0.28284271247461906,0.0,-0.282842712474619,0.3325878449210181,0.0,-0.22222809320784087,0.36955181300451473,0.0,-0.15307337294603593,0.3923141121612922,0.0,-0.0780361288064513,1.0,2.0,0.0,0.9807852804032304,2.0,-0.19509032201612825,0.9238795325112867,2.0,-0.3826834323650898,0.8314696123025452,2.0,-0.5555702330196022,0.7071067811865476,2.0,-0.7071067811865475,0.5555702330196023,2.0,-0.8314696123025452,0.38268343236508984,2.0,-0.9238795325112867,0.19509032201612833,2.0,-0.9807852804032304,6.123233995736766e-17,2.0,-1.0,-0.1950903220161282,2.0,-0.9807852804032304,-0.3826834323650897,2.0,-0.9238795325112867,-0.555570233019602,2.0,-0.8314696123025453,-0.7071067811865475,2.0,-0.7071067811865476,-0.8314696123025453,2.0,-0.5555702330196022,-0.9238795325112867,2.0,-0.3826834323650899,-0.9807852804032304,2.0,-0.1950903220161286,-1.0,2.0,-1.2246467991473532e-16,-0.9807852804032304,2.0,0.19509032201612836,-0.9238795325112868,2.0,0.38268343236508967,-0.8314696123025455,2.0,0.555570233019602,-0.7071067811865477,2.0,0.7071067811865475,-0.5555702330196022,2.0,0.8314696123025452,-0.38268343236509034,2.0,0.9238795325112865,-0.19509032201612866,2.0,0.9807852804032303,-1.8369701987210297e-16,2.0,1.0,0.1950903220161283,2.0,0.9807852804032304,0.38268343236509,2.0,0.9238795325112866,0.5555702330196018,2.0,0.8314696123025455,0.7071067811865474,2.0,0.7071067811865477,0.8314696123025452,2.0,0.5555702330196022,0.9238795325112865,2.0,0.3826834323650904,0.9807852804032303,2.0,0.19509032201612872,0.4,2.0,0.0,0.3923141121612922,2.0,-0.0780361288064513,0.36955181300451473,2.0,-0.15307337294603593,0.3325878449210181,2.0,-0.22222809320784087,0.28284271247461906,2.0,-0.282842712474619,0.22222809320784093,2.0,-0.3325878449210181,0.15307337294603596,2.0,-0.36955181300451473,0.07803612880645133,2.0,-0.3923141121612922,2.4492935982947065e-17,2.0,-0.4,-0.07803612880645128,2.0,-0.3923141121612922,-0.1530733729460359,2.0,-0.36955181300451473,-0.2222280932078408,2.0,-0.33258784492101817,-0.282842712474619,2.0,-0.28284271247461906,-0.33258784492101817,2.0,-0.22222809320784087,-0.36955181300451473,2.0,-0.15307337294603596,-0.3923141121612922,2.0,-0.07803612880645144,-0.4,2.0,-4.898587196589413e-17,-0.3923141121612922,2.0,0.07803612880645135,-0.3695518130045148,2.0,0.15307337294603587,-0.3325878449210182,2.0,0.2222280932078408,-0.28284271247461906,2.0,0.282842712474619,-0.22222809320784087,2.0,0.3325878449210181,-0.15307337294603615,2.0,0.3695518130045146,-0.07803612880645147,2.0,0.39231411216129214,-7.347880794884119e-17,2.0,0.4,0.07803612880645133,2.0,0.3923141121612922,0.153073372946036,2.0,0.3695518130045147,0.22222809320784076,2.0,0.3325878449210182,0.28284271247461895,2.0,0.28284271247461906,0.3325878449210181,2.0,0.22222809320784087,0.3695518130045146,2.0,0.15307337294603618,0.39231411216129214,2.0,0.0780361288064515],"revision":0,"triangleFaceIds":[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,0,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,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.brep.json b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.brep.json new file mode 100644 index 0000000..c75fd0d --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"arc-edged-extrusion-with-holes","revision":0,"geometry":{"surfaces":[{"kind":"Plane","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[-2.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]}},{"kind":"Cylinder","frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":2.0},{"kind":"Plane","frame":{"origin":[2.0,0.0,-2.0],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[-2.0,0.0,-2.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[-0.5,0.0,0.5],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]}},{"kind":"Plane","frame":{"origin":[-0.5,0.0,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.5,0.0,-0.5],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.5,0.0,0.5],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]}}],"curves":[{"kind":"Line","origin":[-2.0,0.0,2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[-2.0,2.0,2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[-2.0,0.0,2.0],"direction":[0.0,1.0,0.0]},{"kind":"Circle","frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":2.0},{"kind":"Circle","frame":{"origin":[2.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":2.0},{"kind":"Line","origin":[2.0,0.0,2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[2.0,0.0,-2.0],"direction":[-1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,2.0,-2.0],"direction":[-1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,0.0,-2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[-2.0,0.0,-2.0],"direction":[0.0,0.0,1.0]},{"kind":"Line","origin":[-2.0,2.0,-2.0],"direction":[0.0,0.0,1.0]},{"kind":"Line","origin":[-2.0,0.0,-2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[-0.5,0.0,0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[-0.5,2.0,0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[-0.5,0.0,0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[-0.5,0.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[-0.5,2.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[-0.5,0.0,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.5,0.0,-0.5],"direction":[0.0,0.0,1.0]},{"kind":"Line","origin":[0.5,2.0,-0.5],"direction":[0.0,0.0,1.0]},{"kind":"Line","origin":[0.5,0.0,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.5,0.0,0.5],"direction":[-1.0,0.0,0.0]},{"kind":"Line","origin":[0.5,2.0,0.5],"direction":[-1.0,0.0,0.0]},{"kind":"Line","origin":[0.5,0.0,0.5],"direction":[0.0,1.0,0.0]}],"pcurves":[{"kind":"Line2","origin":[-2.0,-2.0],"direction":[1.0,0.0]},{"kind":"Conic2","origin":[2.0,0.0],"axis_a":[2.0,0.0],"axis_b":[0.0,2.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[-1.0,0.0]},{"kind":"Line2","origin":[-2.0,2.0],"direction":[0.0,-1.0]},{"kind":"Line2","origin":[-0.5,-0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[-0.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[0.0,-1.0]},{"kind":"Line2","origin":[0.5,-0.5],"direction":[-1.0,0.0]},{"kind":"Line2","origin":[-2.0,-2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,-2.0],"direction":[-1.0,0.0]},{"kind":"Conic2","origin":[2.0,0.0],"axis_a":[2.0,0.0],"axis_b":[0.0,-2.0]},{"kind":"Line2","origin":[-2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[-1.0,0.0]},{"kind":"Line2","origin":[0.5,-0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[-0.5,-0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[-0.5,0.5],"direction":[0.0,-1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[4.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5707963267948966,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[-1.5707963267948966,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[4.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[4.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[-2.0,0.0,2.0],"outgoing_halfedge":8,"tolerance":1e-8},{"id":1,"position":[2.0,0.0,2.0],"outgoing_halfedge":11,"tolerance":1e-8},{"id":2,"position":[2.0,0.0,-2.0],"outgoing_halfedge":10,"tolerance":1e-8},{"id":3,"position":[-2.0,0.0,-2.0],"outgoing_halfedge":9,"tolerance":1e-8},{"id":4,"position":[-2.0,2.0,2.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":5,"position":[2.0,2.0,2.0],"outgoing_halfedge":1,"tolerance":1e-8},{"id":6,"position":[2.0,2.0,-2.0],"outgoing_halfedge":2,"tolerance":1e-8},{"id":7,"position":[-2.0,2.0,-2.0],"outgoing_halfedge":3,"tolerance":1e-8},{"id":8,"position":[-0.5,0.0,0.5],"outgoing_halfedge":12,"tolerance":1e-8},{"id":9,"position":[-0.5,0.0,-0.5],"outgoing_halfedge":15,"tolerance":1e-8},{"id":10,"position":[0.5,0.0,-0.5],"outgoing_halfedge":14,"tolerance":1e-8},{"id":11,"position":[0.5,0.0,0.5],"outgoing_halfedge":13,"tolerance":1e-8},{"id":12,"position":[-0.5,2.0,0.5],"outgoing_halfedge":4,"tolerance":1e-8},{"id":13,"position":[-0.5,2.0,-0.5],"outgoing_halfedge":5,"tolerance":1e-8},{"id":14,"position":[0.5,2.0,-0.5],"outgoing_halfedge":6,"tolerance":1e-8},{"id":15,"position":[0.5,2.0,0.5],"outgoing_halfedge":7,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":4.0}},"halfedge":11,"twin_halfedge":16,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":4.0}},"halfedge":0,"twin_halfedge":18,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":2.0}},"halfedge":19,"twin_halfedge":29,"tolerance":1e-8,"chart_seam":false},{"id":3,"geometry":{"kind":"Curve","curve":3,"range":{"lo":-1.5707963267948966,"hi":1.5707963267948966}},"halfedge":10,"twin_halfedge":20,"tolerance":1e-8,"chart_seam":false},{"id":4,"geometry":{"kind":"Curve","curve":4,"range":{"lo":-1.5707963267948966,"hi":1.5707963267948966}},"halfedge":1,"twin_halfedge":22,"tolerance":1e-8,"chart_seam":false},{"id":5,"geometry":{"kind":"Curve","curve":5,"range":{"lo":0.0,"hi":2.0}},"halfedge":17,"twin_halfedge":23,"tolerance":1e-8,"chart_seam":false},{"id":6,"geometry":{"kind":"Curve","curve":6,"range":{"lo":0.0,"hi":4.0}},"halfedge":9,"twin_halfedge":24,"tolerance":1e-8,"chart_seam":false},{"id":7,"geometry":{"kind":"Curve","curve":7,"range":{"lo":0.0,"hi":4.0}},"halfedge":2,"twin_halfedge":26,"tolerance":1e-8,"chart_seam":false},{"id":8,"geometry":{"kind":"Curve","curve":8,"range":{"lo":0.0,"hi":2.0}},"halfedge":21,"twin_halfedge":27,"tolerance":1e-8,"chart_seam":false},{"id":9,"geometry":{"kind":"Curve","curve":9,"range":{"lo":0.0,"hi":4.0}},"halfedge":8,"twin_halfedge":28,"tolerance":1e-8,"chart_seam":false},{"id":10,"geometry":{"kind":"Curve","curve":10,"range":{"lo":0.0,"hi":4.0}},"halfedge":3,"twin_halfedge":30,"tolerance":1e-8,"chart_seam":false},{"id":11,"geometry":{"kind":"Curve","curve":11,"range":{"lo":0.0,"hi":2.0}},"halfedge":25,"twin_halfedge":31,"tolerance":1e-8,"chart_seam":false},{"id":12,"geometry":{"kind":"Curve","curve":12,"range":{"lo":0.0,"hi":1.0}},"halfedge":15,"twin_halfedge":32,"tolerance":1e-8,"chart_seam":false},{"id":13,"geometry":{"kind":"Curve","curve":13,"range":{"lo":0.0,"hi":1.0}},"halfedge":4,"twin_halfedge":34,"tolerance":1e-8,"chart_seam":false},{"id":14,"geometry":{"kind":"Curve","curve":14,"range":{"lo":0.0,"hi":2.0}},"halfedge":35,"twin_halfedge":45,"tolerance":1e-8,"chart_seam":false},{"id":15,"geometry":{"kind":"Curve","curve":15,"range":{"lo":0.0,"hi":1.0}},"halfedge":14,"twin_halfedge":36,"tolerance":1e-8,"chart_seam":false},{"id":16,"geometry":{"kind":"Curve","curve":16,"range":{"lo":0.0,"hi":1.0}},"halfedge":5,"twin_halfedge":38,"tolerance":1e-8,"chart_seam":false},{"id":17,"geometry":{"kind":"Curve","curve":17,"range":{"lo":0.0,"hi":2.0}},"halfedge":33,"twin_halfedge":39,"tolerance":1e-8,"chart_seam":false},{"id":18,"geometry":{"kind":"Curve","curve":18,"range":{"lo":0.0,"hi":1.0}},"halfedge":13,"twin_halfedge":40,"tolerance":1e-8,"chart_seam":false},{"id":19,"geometry":{"kind":"Curve","curve":19,"range":{"lo":0.0,"hi":1.0}},"halfedge":6,"twin_halfedge":42,"tolerance":1e-8,"chart_seam":false},{"id":20,"geometry":{"kind":"Curve","curve":20,"range":{"lo":0.0,"hi":2.0}},"halfedge":37,"twin_halfedge":43,"tolerance":1e-8,"chart_seam":false},{"id":21,"geometry":{"kind":"Curve","curve":21,"range":{"lo":0.0,"hi":1.0}},"halfedge":12,"twin_halfedge":44,"tolerance":1e-8,"chart_seam":false},{"id":22,"geometry":{"kind":"Curve","curve":22,"range":{"lo":0.0,"hi":1.0}},"halfedge":7,"twin_halfedge":46,"tolerance":1e-8,"chart_seam":false},{"id":23,"geometry":{"kind":"Curve","curve":23,"range":{"lo":0.0,"hi":2.0}},"halfedge":41,"twin_halfedge":47,"tolerance":1e-8,"chart_seam":false}],"halfedges":[{"id":0,"from":4,"to":5,"twin":18,"next":1,"prev":3,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":5,"to":6,"twin":22,"next":2,"prev":0,"edge":4,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":6,"to":7,"twin":26,"next":3,"prev":1,"edge":7,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":7,"to":4,"twin":30,"next":0,"prev":2,"edge":10,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":12,"to":13,"twin":34,"next":5,"prev":7,"edge":13,"face":0,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":13,"to":14,"twin":38,"next":6,"prev":4,"edge":16,"face":0,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":5,"periodic_lift":[0,0]}},{"id":6,"from":14,"to":15,"twin":42,"next":7,"prev":5,"edge":19,"face":0,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":6,"periodic_lift":[0,0]}},{"id":7,"from":15,"to":12,"twin":46,"next":4,"prev":6,"edge":22,"face":0,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":7,"periodic_lift":[0,0]}},{"id":8,"from":0,"to":3,"twin":28,"next":9,"prev":11,"edge":9,"face":1,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":8,"periodic_lift":[0,0]}},{"id":9,"from":3,"to":2,"twin":24,"next":10,"prev":8,"edge":6,"face":1,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":9,"periodic_lift":[0,0]}},{"id":10,"from":2,"to":1,"twin":20,"next":11,"prev":9,"edge":3,"face":1,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":10,"periodic_lift":[0,0]}},{"id":11,"from":1,"to":0,"twin":16,"next":8,"prev":10,"edge":0,"face":1,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":11,"periodic_lift":[0,0]}},{"id":12,"from":8,"to":11,"twin":44,"next":13,"prev":15,"edge":21,"face":1,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":12,"periodic_lift":[0,0]}},{"id":13,"from":11,"to":10,"twin":40,"next":14,"prev":12,"edge":18,"face":1,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":13,"periodic_lift":[0,0]}},{"id":14,"from":10,"to":9,"twin":36,"next":15,"prev":13,"edge":15,"face":1,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":14,"periodic_lift":[0,0]}},{"id":15,"from":9,"to":8,"twin":32,"next":12,"prev":14,"edge":12,"face":1,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":15,"periodic_lift":[0,0]}},{"id":16,"from":0,"to":1,"twin":11,"next":17,"prev":19,"edge":0,"face":2,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":16,"periodic_lift":[0,0]}},{"id":17,"from":1,"to":5,"twin":23,"next":18,"prev":16,"edge":5,"face":2,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":17,"periodic_lift":[0,0]}},{"id":18,"from":5,"to":4,"twin":0,"next":19,"prev":17,"edge":1,"face":2,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":18,"periodic_lift":[0,0]}},{"id":19,"from":4,"to":0,"twin":29,"next":16,"prev":18,"edge":2,"face":2,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":19,"periodic_lift":[0,0]}},{"id":20,"from":1,"to":2,"twin":10,"next":21,"prev":23,"edge":3,"face":3,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":20,"periodic_lift":[0,0]}},{"id":21,"from":2,"to":6,"twin":27,"next":22,"prev":20,"edge":8,"face":3,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":21,"periodic_lift":[0,0]}},{"id":22,"from":6,"to":5,"twin":1,"next":23,"prev":21,"edge":4,"face":3,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":22,"periodic_lift":[0,0]}},{"id":23,"from":5,"to":1,"twin":17,"next":20,"prev":22,"edge":5,"face":3,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":23,"periodic_lift":[0,0]}},{"id":24,"from":2,"to":3,"twin":9,"next":25,"prev":27,"edge":6,"face":4,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":24,"periodic_lift":[0,0]}},{"id":25,"from":3,"to":7,"twin":31,"next":26,"prev":24,"edge":11,"face":4,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":25,"periodic_lift":[0,0]}},{"id":26,"from":7,"to":6,"twin":2,"next":27,"prev":25,"edge":7,"face":4,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":26,"periodic_lift":[0,0]}},{"id":27,"from":6,"to":2,"twin":21,"next":24,"prev":26,"edge":8,"face":4,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":27,"periodic_lift":[0,0]}},{"id":28,"from":3,"to":0,"twin":8,"next":29,"prev":31,"edge":9,"face":5,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":28,"periodic_lift":[0,0]}},{"id":29,"from":0,"to":4,"twin":19,"next":30,"prev":28,"edge":2,"face":5,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":29,"periodic_lift":[0,0]}},{"id":30,"from":4,"to":7,"twin":3,"next":31,"prev":29,"edge":10,"face":5,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":30,"periodic_lift":[0,0]}},{"id":31,"from":7,"to":3,"twin":25,"next":28,"prev":30,"edge":11,"face":5,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":31,"periodic_lift":[0,0]}},{"id":32,"from":8,"to":9,"twin":15,"next":33,"prev":35,"edge":12,"face":6,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":32,"periodic_lift":[0,0]}},{"id":33,"from":9,"to":13,"twin":39,"next":34,"prev":32,"edge":17,"face":6,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":33,"periodic_lift":[0,0]}},{"id":34,"from":13,"to":12,"twin":4,"next":35,"prev":33,"edge":13,"face":6,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":34,"periodic_lift":[0,0]}},{"id":35,"from":12,"to":8,"twin":45,"next":32,"prev":34,"edge":14,"face":6,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":35,"periodic_lift":[0,0]}},{"id":36,"from":9,"to":10,"twin":14,"next":37,"prev":39,"edge":15,"face":7,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":36,"periodic_lift":[0,0]}},{"id":37,"from":10,"to":14,"twin":43,"next":38,"prev":36,"edge":20,"face":7,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":37,"periodic_lift":[0,0]}},{"id":38,"from":14,"to":13,"twin":5,"next":39,"prev":37,"edge":16,"face":7,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":38,"periodic_lift":[0,0]}},{"id":39,"from":13,"to":9,"twin":33,"next":36,"prev":38,"edge":17,"face":7,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":39,"periodic_lift":[0,0]}},{"id":40,"from":10,"to":11,"twin":13,"next":41,"prev":43,"edge":18,"face":8,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":40,"periodic_lift":[0,0]}},{"id":41,"from":11,"to":15,"twin":47,"next":42,"prev":40,"edge":23,"face":8,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":41,"periodic_lift":[0,0]}},{"id":42,"from":15,"to":14,"twin":6,"next":43,"prev":41,"edge":19,"face":8,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":42,"periodic_lift":[0,0]}},{"id":43,"from":14,"to":10,"twin":37,"next":40,"prev":42,"edge":20,"face":8,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":43,"periodic_lift":[0,0]}},{"id":44,"from":11,"to":8,"twin":12,"next":45,"prev":47,"edge":21,"face":9,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":44,"periodic_lift":[0,0]}},{"id":45,"from":8,"to":12,"twin":35,"next":46,"prev":44,"edge":14,"face":9,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":45,"periodic_lift":[0,0]}},{"id":46,"from":12,"to":15,"twin":7,"next":47,"prev":45,"edge":22,"face":9,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":46,"periodic_lift":[0,0]}},{"id":47,"from":15,"to":11,"twin":41,"next":44,"prev":46,"edge":23,"face":9,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":47,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":0,"is_hole":true},{"id":2,"start_halfedge":8,"face_ref":1,"is_hole":false},{"id":3,"start_halfedge":12,"face_ref":1,"is_hole":true},{"id":4,"start_halfedge":16,"face_ref":2,"is_hole":false},{"id":5,"start_halfedge":20,"face_ref":3,"is_hole":false},{"id":6,"start_halfedge":24,"face_ref":4,"is_hole":false},{"id":7,"start_halfedge":28,"face_ref":5,"is_hole":false},{"id":8,"start_halfedge":32,"face_ref":6,"is_hole":false},{"id":9,"start_halfedge":36,"face_ref":7,"is_hole":false},{"id":10,"start_halfedge":40,"face_ref":8,"is_hole":false},{"id":11,"start_halfedge":44,"face_ref":9,"is_hole":false}],"faces":[{"id":0,"key":"top","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-2.00000001,"hi":4.00000001},{"lo":-2.00000001,"hi":2.00000001}],"outer":0,"holes":[1]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion-with-holes","body":"arc-edged-extrusion-with-holes","key":"top","face":0}],"role":"Authored","reversed":false}},{"id":1,"key":"bottom","surface":1,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-2.00000001,"hi":4.00000001},{"lo":-2.00000001,"hi":2.00000001}],"outer":2,"holes":[3]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion-with-holes","body":"arc-edged-extrusion-with-holes","key":"bottom","face":1}],"role":"Authored","reversed":false}},{"id":2,"key":"side-0-0","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":4.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":4,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion-with-holes","body":"arc-edged-extrusion-with-holes","key":"side-0-0","face":2}],"role":"Authored","reversed":false}},{"id":3,"key":"side-0-1","surface":3,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.5707963317948965,"hi":1.5707963317948965},{"lo":-1e-8,"hi":2.00000001}],"outer":5,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion-with-holes","body":"arc-edged-extrusion-with-holes","key":"side-0-1","face":3}],"role":"Authored","reversed":false}},{"id":4,"key":"side-0-2","surface":4,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":4.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":6,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion-with-holes","body":"arc-edged-extrusion-with-holes","key":"side-0-2","face":4}],"role":"Authored","reversed":false}},{"id":5,"key":"side-0-3","surface":5,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":4.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":7,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion-with-holes","body":"arc-edged-extrusion-with-holes","key":"side-0-3","face":5}],"role":"Authored","reversed":false}},{"id":6,"key":"side-1-0","surface":6,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":8,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion-with-holes","body":"arc-edged-extrusion-with-holes","key":"side-1-0","face":6}],"role":"Authored","reversed":false}},{"id":7,"key":"side-1-1","surface":7,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":9,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion-with-holes","body":"arc-edged-extrusion-with-holes","key":"side-1-1","face":7}],"role":"Authored","reversed":false}},{"id":8,"key":"side-1-2","surface":8,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":10,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion-with-holes","body":"arc-edged-extrusion-with-holes","key":"side-1-2","face":8}],"role":"Authored","reversed":false}},{"id":9,"key":"side-1-3","surface":9,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":11,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion-with-holes","body":"arc-edged-extrusion-with-holes","key":"side-1-3","face":9}],"role":"Authored","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,1,2,3,4,5,6,7,8,9],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.m b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.m new file mode 100644 index 0000000..16fec6b --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.m @@ -0,0 +1,625 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('arc-edged-extrusion-with-holes','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#13=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=CYLINDRICAL_SURFACE('',#20,2.00000000000000000E0); +#22=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#23=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#24=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#28=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(-5.00000000000000000E-1,0.00000000000000000E0,5.00000000000000000E-1)); +#33=DIRECTION('',(1.00000000000000000E0,-0.00000000000000000E0,0.00000000000000000E0)); +#34=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#35=AXIS2_PLACEMENT_3D('',#32,#33,#34); +#36=PLANE('',#35); +#37=CARTESIAN_POINT('',(-5.00000000000000000E-1,0.00000000000000000E0,-5.00000000000000000E-1)); +#38=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#39=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#40=AXIS2_PLACEMENT_3D('',#37,#38,#39); +#41=PLANE('',#40); +#42=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0,-5.00000000000000000E-1)); +#43=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#44=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#45=AXIS2_PLACEMENT_3D('',#42,#43,#44); +#46=PLANE('',#45); +#47=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0,5.00000000000000000E-1)); +#48=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#49=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#50=AXIS2_PLACEMENT_3D('',#47,#48,#49); +#51=PLANE('',#50); +#52=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#53=VERTEX_POINT('',#52); +#54=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#55=VERTEX_POINT('',#54); +#56=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#57=VERTEX_POINT('',#56); +#58=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#59=VERTEX_POINT('',#58); +#60=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0,2.00000000000000000E0)); +#61=VERTEX_POINT('',#60); +#62=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,2.00000000000000000E0)); +#63=VERTEX_POINT('',#62); +#64=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#65=VERTEX_POINT('',#64); +#66=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#67=VERTEX_POINT('',#66); +#68=CARTESIAN_POINT('',(-5.00000000000000000E-1,0.00000000000000000E0,5.00000000000000000E-1)); +#69=VERTEX_POINT('',#68); +#70=CARTESIAN_POINT('',(-5.00000000000000000E-1,0.00000000000000000E0,-5.00000000000000000E-1)); +#71=VERTEX_POINT('',#70); +#72=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0,-5.00000000000000000E-1)); +#73=VERTEX_POINT('',#72); +#74=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0,5.00000000000000000E-1)); +#75=VERTEX_POINT('',#74); +#76=CARTESIAN_POINT('',(-5.00000000000000000E-1,2.00000000000000000E0,5.00000000000000000E-1)); +#77=VERTEX_POINT('',#76); +#78=CARTESIAN_POINT('',(-5.00000000000000000E-1,2.00000000000000000E0,-5.00000000000000000E-1)); +#79=VERTEX_POINT('',#78); +#80=CARTESIAN_POINT('',(5.00000000000000000E-1,2.00000000000000000E0,-5.00000000000000000E-1)); +#81=VERTEX_POINT('',#80); +#82=CARTESIAN_POINT('',(5.00000000000000000E-1,2.00000000000000000E0,5.00000000000000000E-1)); +#83=VERTEX_POINT('',#82); +#84=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#85=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#86=VECTOR('',#85,1.00000000000000000E0); +#87=LINE('',#84,#86); +#88=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#90=VECTOR('',#89,1.00000000000000000E0); +#91=LINE('',#88,#90); +#92=DEFINITIONAL_REPRESENTATION('',(#91),#1); +#93=PCURVE('',#11,#92); +#94=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#95=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#96=VECTOR('',#95,1.00000000000000000E0); +#97=LINE('',#94,#96); +#98=DEFINITIONAL_REPRESENTATION('',(#97),#1); +#99=PCURVE('',#16,#98); +#100=SURFACE_CURVE('',#87,(#93,#99),.CURVE_3D.); +#101=EDGE_CURVE('',#53,#55,#100,.T.); +#102=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0,2.00000000000000000E0)); +#103=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#104=VECTOR('',#103,1.00000000000000000E0); +#105=LINE('',#102,#104); +#106=CARTESIAN_POINT('',(-2.00000000000000000E0,-2.00000000000000000E0)); +#107=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E0); +#109=LINE('',#106,#108); +#110=DEFINITIONAL_REPRESENTATION('',(#109),#1); +#111=PCURVE('',#6,#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#113=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#114=VECTOR('',#113,1.00000000000000000E0); +#115=LINE('',#112,#114); +#116=DEFINITIONAL_REPRESENTATION('',(#115),#1); +#117=PCURVE('',#16,#116); +#118=SURFACE_CURVE('',#105,(#111,#117),.CURVE_3D.); +#119=EDGE_CURVE('',#61,#63,#118,.T.); +#120=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#121=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#122=VECTOR('',#121,1.00000000000000000E0); +#123=LINE('',#120,#122); +#124=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#125=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E0); +#127=LINE('',#124,#126); +#128=DEFINITIONAL_REPRESENTATION('',(#127),#1); +#129=PCURVE('',#16,#128); +#130=CARTESIAN_POINT('',(4.00000000000000000E0,0.00000000000000000E0)); +#131=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#132=VECTOR('',#131,1.00000000000000000E0); +#133=LINE('',#130,#132); +#134=DEFINITIONAL_REPRESENTATION('',(#133),#1); +#135=PCURVE('',#31,#134); +#136=SURFACE_CURVE('',#123,(#129,#135),.CURVE_3D.); +#137=EDGE_CURVE('',#53,#61,#136,.T.); +#138=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#139=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#140=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#141=AXIS2_PLACEMENT_3D('',#138,#139,#140); +#142=CIRCLE('',#141,2.00000000000000000E0); +#143=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#144=DIRECTION('',(2.00000000000000000E0,0.00000000000000000E0)); +#145=AXIS2_PLACEMENT_2D('',#143,#144); +#146=CIRCLE('',#145,2.00000000000000000E0); +#147=DEFINITIONAL_REPRESENTATION('',(#146),#1); +#148=PCURVE('',#11,#147); +#149=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#150=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#151=VECTOR('',#150,1.00000000000000000E0); +#152=LINE('',#149,#151); +#153=DEFINITIONAL_REPRESENTATION('',(#152),#1); +#154=PCURVE('',#21,#153); +#155=SURFACE_CURVE('',#142,(#148,#154),.CURVE_3D.); +#156=EDGE_CURVE('',#55,#57,#155,.T.); +#157=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#158=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#159=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#160=AXIS2_PLACEMENT_3D('',#157,#158,#159); +#161=CIRCLE('',#160,2.00000000000000000E0); +#162=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#163=DIRECTION('',(2.00000000000000000E0,0.00000000000000000E0)); +#164=AXIS2_PLACEMENT_2D('',#162,#163); +#165=CIRCLE('',#164,2.00000000000000000E0); +#166=DEFINITIONAL_REPRESENTATION('',(#165),#1); +#167=PCURVE('',#6,#166); +#168=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#169=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#170=VECTOR('',#169,1.00000000000000000E0); +#171=LINE('',#168,#170); +#172=DEFINITIONAL_REPRESENTATION('',(#171),#1); +#173=PCURVE('',#21,#172); +#174=SURFACE_CURVE('',#161,(#167,#173),.CURVE_3D.); +#175=EDGE_CURVE('',#63,#65,#174,.T.); +#176=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#177=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#178=VECTOR('',#177,1.00000000000000000E0); +#179=LINE('',#176,#178); +#180=CARTESIAN_POINT('',(4.00000000000000000E0,0.00000000000000000E0)); +#181=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#182=VECTOR('',#181,1.00000000000000000E0); +#183=LINE('',#180,#182); +#184=DEFINITIONAL_REPRESENTATION('',(#183),#1); +#185=PCURVE('',#16,#184); +#186=CARTESIAN_POINT('',(-1.57079632679489656E0,0.00000000000000000E0)); +#187=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#188=VECTOR('',#187,1.00000000000000000E0); +#189=LINE('',#186,#188); +#190=DEFINITIONAL_REPRESENTATION('',(#189),#1); +#191=PCURVE('',#21,#190); +#192=SURFACE_CURVE('',#179,(#185,#191),.CURVE_3D.); +#193=EDGE_CURVE('',#55,#63,#192,.T.); +#194=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#195=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#196=VECTOR('',#195,1.00000000000000000E0); +#197=LINE('',#194,#196); +#198=CARTESIAN_POINT('',(2.00000000000000000E0,-2.00000000000000000E0)); +#199=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#200=VECTOR('',#199,1.00000000000000000E0); +#201=LINE('',#198,#200); +#202=DEFINITIONAL_REPRESENTATION('',(#201),#1); +#203=PCURVE('',#11,#202); +#204=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#205=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#206=VECTOR('',#205,1.00000000000000000E0); +#207=LINE('',#204,#206); +#208=DEFINITIONAL_REPRESENTATION('',(#207),#1); +#209=PCURVE('',#26,#208); +#210=SURFACE_CURVE('',#197,(#203,#209),.CURVE_3D.); +#211=EDGE_CURVE('',#57,#59,#210,.T.); +#212=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#213=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#214=VECTOR('',#213,1.00000000000000000E0); +#215=LINE('',#212,#214); +#216=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#217=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#218=VECTOR('',#217,1.00000000000000000E0); +#219=LINE('',#216,#218); +#220=DEFINITIONAL_REPRESENTATION('',(#219),#1); +#221=PCURVE('',#6,#220); +#222=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#223=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#224=VECTOR('',#223,1.00000000000000000E0); +#225=LINE('',#222,#224); +#226=DEFINITIONAL_REPRESENTATION('',(#225),#1); +#227=PCURVE('',#26,#226); +#228=SURFACE_CURVE('',#215,(#221,#227),.CURVE_3D.); +#229=EDGE_CURVE('',#65,#67,#228,.T.); +#230=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#231=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#232=VECTOR('',#231,1.00000000000000000E0); +#233=LINE('',#230,#232); +#234=CARTESIAN_POINT('',(1.57079632679489656E0,0.00000000000000000E0)); +#235=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#236=VECTOR('',#235,1.00000000000000000E0); +#237=LINE('',#234,#236); +#238=DEFINITIONAL_REPRESENTATION('',(#237),#1); +#239=PCURVE('',#21,#238); +#240=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#241=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#242=VECTOR('',#241,1.00000000000000000E0); +#243=LINE('',#240,#242); +#244=DEFINITIONAL_REPRESENTATION('',(#243),#1); +#245=PCURVE('',#26,#244); +#246=SURFACE_CURVE('',#233,(#239,#245),.CURVE_3D.); +#247=EDGE_CURVE('',#57,#65,#246,.T.); +#248=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#249=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#250=VECTOR('',#249,1.00000000000000000E0); +#251=LINE('',#248,#250); +#252=CARTESIAN_POINT('',(-2.00000000000000000E0,-2.00000000000000000E0)); +#253=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#254=VECTOR('',#253,1.00000000000000000E0); +#255=LINE('',#252,#254); +#256=DEFINITIONAL_REPRESENTATION('',(#255),#1); +#257=PCURVE('',#11,#256); +#258=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#259=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#260=VECTOR('',#259,1.00000000000000000E0); +#261=LINE('',#258,#260); +#262=DEFINITIONAL_REPRESENTATION('',(#261),#1); +#263=PCURVE('',#31,#262); +#264=SURFACE_CURVE('',#251,(#257,#263),.CURVE_3D.); +#265=EDGE_CURVE('',#59,#53,#264,.T.); +#266=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#267=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#268=VECTOR('',#267,1.00000000000000000E0); +#269=LINE('',#266,#268); +#270=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0)); +#271=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#272=VECTOR('',#271,1.00000000000000000E0); +#273=LINE('',#270,#272); +#274=DEFINITIONAL_REPRESENTATION('',(#273),#1); +#275=PCURVE('',#6,#274); +#276=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#277=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#278=VECTOR('',#277,1.00000000000000000E0); +#279=LINE('',#276,#278); +#280=DEFINITIONAL_REPRESENTATION('',(#279),#1); +#281=PCURVE('',#31,#280); +#282=SURFACE_CURVE('',#269,(#275,#281),.CURVE_3D.); +#283=EDGE_CURVE('',#67,#61,#282,.T.); +#284=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#285=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#286=VECTOR('',#285,1.00000000000000000E0); +#287=LINE('',#284,#286); +#288=CARTESIAN_POINT('',(4.00000000000000000E0,0.00000000000000000E0)); +#289=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#290=VECTOR('',#289,1.00000000000000000E0); +#291=LINE('',#288,#290); +#292=DEFINITIONAL_REPRESENTATION('',(#291),#1); +#293=PCURVE('',#26,#292); +#294=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#295=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#296=VECTOR('',#295,1.00000000000000000E0); +#297=LINE('',#294,#296); +#298=DEFINITIONAL_REPRESENTATION('',(#297),#1); +#299=PCURVE('',#31,#298); +#300=SURFACE_CURVE('',#287,(#293,#299),.CURVE_3D.); +#301=EDGE_CURVE('',#59,#67,#300,.T.); +#302=CARTESIAN_POINT('',(-5.00000000000000000E-1,0.00000000000000000E0,5.00000000000000000E-1)); +#303=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#304=VECTOR('',#303,1.00000000000000000E0); +#305=LINE('',#302,#304); +#306=CARTESIAN_POINT('',(-5.00000000000000000E-1,5.00000000000000000E-1)); +#307=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#308=VECTOR('',#307,1.00000000000000000E0); +#309=LINE('',#306,#308); +#310=DEFINITIONAL_REPRESENTATION('',(#309),#1); +#311=PCURVE('',#11,#310); +#312=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#313=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#314=VECTOR('',#313,1.00000000000000000E0); +#315=LINE('',#312,#314); +#316=DEFINITIONAL_REPRESENTATION('',(#315),#1); +#317=PCURVE('',#36,#316); +#318=SURFACE_CURVE('',#305,(#311,#317),.CURVE_3D.); +#319=EDGE_CURVE('',#69,#71,#318,.T.); +#320=CARTESIAN_POINT('',(-5.00000000000000000E-1,2.00000000000000000E0,5.00000000000000000E-1)); +#321=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#322=VECTOR('',#321,1.00000000000000000E0); +#323=LINE('',#320,#322); +#324=CARTESIAN_POINT('',(-5.00000000000000000E-1,-5.00000000000000000E-1)); +#325=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#326=VECTOR('',#325,1.00000000000000000E0); +#327=LINE('',#324,#326); +#328=DEFINITIONAL_REPRESENTATION('',(#327),#1); +#329=PCURVE('',#6,#328); +#330=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#331=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#332=VECTOR('',#331,1.00000000000000000E0); +#333=LINE('',#330,#332); +#334=DEFINITIONAL_REPRESENTATION('',(#333),#1); +#335=PCURVE('',#36,#334); +#336=SURFACE_CURVE('',#323,(#329,#335),.CURVE_3D.); +#337=EDGE_CURVE('',#77,#79,#336,.T.); +#338=CARTESIAN_POINT('',(-5.00000000000000000E-1,0.00000000000000000E0,5.00000000000000000E-1)); +#339=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#340=VECTOR('',#339,1.00000000000000000E0); +#341=LINE('',#338,#340); +#342=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#343=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#344=VECTOR('',#343,1.00000000000000000E0); +#345=LINE('',#342,#344); +#346=DEFINITIONAL_REPRESENTATION('',(#345),#1); +#347=PCURVE('',#36,#346); +#348=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#349=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#350=VECTOR('',#349,1.00000000000000000E0); +#351=LINE('',#348,#350); +#352=DEFINITIONAL_REPRESENTATION('',(#351),#1); +#353=PCURVE('',#51,#352); +#354=SURFACE_CURVE('',#341,(#347,#353),.CURVE_3D.); +#355=EDGE_CURVE('',#69,#77,#354,.T.); +#356=CARTESIAN_POINT('',(-5.00000000000000000E-1,0.00000000000000000E0,-5.00000000000000000E-1)); +#357=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#358=VECTOR('',#357,1.00000000000000000E0); +#359=LINE('',#356,#358); +#360=CARTESIAN_POINT('',(-5.00000000000000000E-1,-5.00000000000000000E-1)); +#361=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#362=VECTOR('',#361,1.00000000000000000E0); +#363=LINE('',#360,#362); +#364=DEFINITIONAL_REPRESENTATION('',(#363),#1); +#365=PCURVE('',#11,#364); +#366=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#367=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#368=VECTOR('',#367,1.00000000000000000E0); +#369=LINE('',#366,#368); +#370=DEFINITIONAL_REPRESENTATION('',(#369),#1); +#371=PCURVE('',#41,#370); +#372=SURFACE_CURVE('',#359,(#365,#371),.CURVE_3D.); +#373=EDGE_CURVE('',#71,#73,#372,.T.); +#374=CARTESIAN_POINT('',(-5.00000000000000000E-1,2.00000000000000000E0,-5.00000000000000000E-1)); +#375=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#376=VECTOR('',#375,1.00000000000000000E0); +#377=LINE('',#374,#376); +#378=CARTESIAN_POINT('',(-5.00000000000000000E-1,5.00000000000000000E-1)); +#379=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#380=VECTOR('',#379,1.00000000000000000E0); +#381=LINE('',#378,#380); +#382=DEFINITIONAL_REPRESENTATION('',(#381),#1); +#383=PCURVE('',#6,#382); +#384=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#385=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#386=VECTOR('',#385,1.00000000000000000E0); +#387=LINE('',#384,#386); +#388=DEFINITIONAL_REPRESENTATION('',(#387),#1); +#389=PCURVE('',#41,#388); +#390=SURFACE_CURVE('',#377,(#383,#389),.CURVE_3D.); +#391=EDGE_CURVE('',#79,#81,#390,.T.); +#392=CARTESIAN_POINT('',(-5.00000000000000000E-1,0.00000000000000000E0,-5.00000000000000000E-1)); +#393=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#394=VECTOR('',#393,1.00000000000000000E0); +#395=LINE('',#392,#394); +#396=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#397=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#398=VECTOR('',#397,1.00000000000000000E0); +#399=LINE('',#396,#398); +#400=DEFINITIONAL_REPRESENTATION('',(#399),#1); +#401=PCURVE('',#36,#400); +#402=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#403=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#404=VECTOR('',#403,1.00000000000000000E0); +#405=LINE('',#402,#404); +#406=DEFINITIONAL_REPRESENTATION('',(#405),#1); +#407=PCURVE('',#41,#406); +#408=SURFACE_CURVE('',#395,(#401,#407),.CURVE_3D.); +#409=EDGE_CURVE('',#71,#79,#408,.T.); +#410=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0,-5.00000000000000000E-1)); +#411=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#412=VECTOR('',#411,1.00000000000000000E0); +#413=LINE('',#410,#412); +#414=CARTESIAN_POINT('',(5.00000000000000000E-1,-5.00000000000000000E-1)); +#415=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#416=VECTOR('',#415,1.00000000000000000E0); +#417=LINE('',#414,#416); +#418=DEFINITIONAL_REPRESENTATION('',(#417),#1); +#419=PCURVE('',#11,#418); +#420=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#421=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#422=VECTOR('',#421,1.00000000000000000E0); +#423=LINE('',#420,#422); +#424=DEFINITIONAL_REPRESENTATION('',(#423),#1); +#425=PCURVE('',#46,#424); +#426=SURFACE_CURVE('',#413,(#419,#425),.CURVE_3D.); +#427=EDGE_CURVE('',#73,#75,#426,.T.); +#428=CARTESIAN_POINT('',(5.00000000000000000E-1,2.00000000000000000E0,-5.00000000000000000E-1)); +#429=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#430=VECTOR('',#429,1.00000000000000000E0); +#431=LINE('',#428,#430); +#432=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#433=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#434=VECTOR('',#433,1.00000000000000000E0); +#435=LINE('',#432,#434); +#436=DEFINITIONAL_REPRESENTATION('',(#435),#1); +#437=PCURVE('',#6,#436); +#438=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#439=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#440=VECTOR('',#439,1.00000000000000000E0); +#441=LINE('',#438,#440); +#442=DEFINITIONAL_REPRESENTATION('',(#441),#1); +#443=PCURVE('',#46,#442); +#444=SURFACE_CURVE('',#431,(#437,#443),.CURVE_3D.); +#445=EDGE_CURVE('',#81,#83,#444,.T.); +#446=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0,-5.00000000000000000E-1)); +#447=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#448=VECTOR('',#447,1.00000000000000000E0); +#449=LINE('',#446,#448); +#450=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#451=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#452=VECTOR('',#451,1.00000000000000000E0); +#453=LINE('',#450,#452); +#454=DEFINITIONAL_REPRESENTATION('',(#453),#1); +#455=PCURVE('',#41,#454); +#456=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#457=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#458=VECTOR('',#457,1.00000000000000000E0); +#459=LINE('',#456,#458); +#460=DEFINITIONAL_REPRESENTATION('',(#459),#1); +#461=PCURVE('',#46,#460); +#462=SURFACE_CURVE('',#449,(#455,#461),.CURVE_3D.); +#463=EDGE_CURVE('',#73,#81,#462,.T.); +#464=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0,5.00000000000000000E-1)); +#465=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#466=VECTOR('',#465,1.00000000000000000E0); +#467=LINE('',#464,#466); +#468=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#469=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#470=VECTOR('',#469,1.00000000000000000E0); +#471=LINE('',#468,#470); +#472=DEFINITIONAL_REPRESENTATION('',(#471),#1); +#473=PCURVE('',#11,#472); +#474=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#475=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#476=VECTOR('',#475,1.00000000000000000E0); +#477=LINE('',#474,#476); +#478=DEFINITIONAL_REPRESENTATION('',(#477),#1); +#479=PCURVE('',#51,#478); +#480=SURFACE_CURVE('',#467,(#473,#479),.CURVE_3D.); +#481=EDGE_CURVE('',#75,#69,#480,.T.); +#482=CARTESIAN_POINT('',(5.00000000000000000E-1,2.00000000000000000E0,5.00000000000000000E-1)); +#483=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#484=VECTOR('',#483,1.00000000000000000E0); +#485=LINE('',#482,#484); +#486=CARTESIAN_POINT('',(5.00000000000000000E-1,-5.00000000000000000E-1)); +#487=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#488=VECTOR('',#487,1.00000000000000000E0); +#489=LINE('',#486,#488); +#490=DEFINITIONAL_REPRESENTATION('',(#489),#1); +#491=PCURVE('',#6,#490); +#492=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#493=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#494=VECTOR('',#493,1.00000000000000000E0); +#495=LINE('',#492,#494); +#496=DEFINITIONAL_REPRESENTATION('',(#495),#1); +#497=PCURVE('',#51,#496); +#498=SURFACE_CURVE('',#485,(#491,#497),.CURVE_3D.); +#499=EDGE_CURVE('',#83,#77,#498,.T.); +#500=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0,5.00000000000000000E-1)); +#501=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#502=VECTOR('',#501,1.00000000000000000E0); +#503=LINE('',#500,#502); +#504=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#505=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#506=VECTOR('',#505,1.00000000000000000E0); +#507=LINE('',#504,#506); +#508=DEFINITIONAL_REPRESENTATION('',(#507),#1); +#509=PCURVE('',#46,#508); +#510=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#511=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#512=VECTOR('',#511,1.00000000000000000E0); +#513=LINE('',#510,#512); +#514=DEFINITIONAL_REPRESENTATION('',(#513),#1); +#515=PCURVE('',#51,#514); +#516=SURFACE_CURVE('',#503,(#509,#515),.CURVE_3D.); +#517=EDGE_CURVE('',#75,#83,#516,.T.); +#518=ORIENTED_EDGE('',*,*,#119,.T.); +#519=ORIENTED_EDGE('',*,*,#175,.T.); +#520=ORIENTED_EDGE('',*,*,#229,.T.); +#521=ORIENTED_EDGE('',*,*,#283,.T.); +#522=EDGE_LOOP('',(#518,#519,#520,#521)); +#523=FACE_OUTER_BOUND('',#522,.T.); +#524=ORIENTED_EDGE('',*,*,#337,.T.); +#525=ORIENTED_EDGE('',*,*,#391,.T.); +#526=ORIENTED_EDGE('',*,*,#445,.T.); +#527=ORIENTED_EDGE('',*,*,#499,.T.); +#528=EDGE_LOOP('',(#524,#525,#526,#527)); +#529=FACE_BOUND('',#528,.T.); +#530=ADVANCED_FACE('top',(#523,#529),#6,.T.); +#531=ORIENTED_EDGE('',*,*,#265,.F.); +#532=ORIENTED_EDGE('',*,*,#211,.F.); +#533=ORIENTED_EDGE('',*,*,#156,.F.); +#534=ORIENTED_EDGE('',*,*,#101,.F.); +#535=EDGE_LOOP('',(#531,#532,#533,#534)); +#536=FACE_OUTER_BOUND('',#535,.T.); +#537=ORIENTED_EDGE('',*,*,#481,.F.); +#538=ORIENTED_EDGE('',*,*,#427,.F.); +#539=ORIENTED_EDGE('',*,*,#373,.F.); +#540=ORIENTED_EDGE('',*,*,#319,.F.); +#541=EDGE_LOOP('',(#537,#538,#539,#540)); +#542=FACE_BOUND('',#541,.T.); +#543=ADVANCED_FACE('bottom',(#536,#542),#11,.T.); +#544=ORIENTED_EDGE('',*,*,#101,.T.); +#545=ORIENTED_EDGE('',*,*,#193,.T.); +#546=ORIENTED_EDGE('',*,*,#119,.F.); +#547=ORIENTED_EDGE('',*,*,#137,.F.); +#548=EDGE_LOOP('',(#544,#545,#546,#547)); +#549=FACE_OUTER_BOUND('',#548,.T.); +#550=ADVANCED_FACE('side-0-0',(#549),#16,.T.); +#551=ORIENTED_EDGE('',*,*,#156,.T.); +#552=ORIENTED_EDGE('',*,*,#247,.T.); +#553=ORIENTED_EDGE('',*,*,#175,.F.); +#554=ORIENTED_EDGE('',*,*,#193,.F.); +#555=EDGE_LOOP('',(#551,#552,#553,#554)); +#556=FACE_OUTER_BOUND('',#555,.T.); +#557=ADVANCED_FACE('side-0-1',(#556),#21,.T.); +#558=ORIENTED_EDGE('',*,*,#211,.T.); +#559=ORIENTED_EDGE('',*,*,#301,.T.); +#560=ORIENTED_EDGE('',*,*,#229,.F.); +#561=ORIENTED_EDGE('',*,*,#247,.F.); +#562=EDGE_LOOP('',(#558,#559,#560,#561)); +#563=FACE_OUTER_BOUND('',#562,.T.); +#564=ADVANCED_FACE('side-0-2',(#563),#26,.T.); +#565=ORIENTED_EDGE('',*,*,#265,.T.); +#566=ORIENTED_EDGE('',*,*,#137,.T.); +#567=ORIENTED_EDGE('',*,*,#283,.F.); +#568=ORIENTED_EDGE('',*,*,#301,.F.); +#569=EDGE_LOOP('',(#565,#566,#567,#568)); +#570=FACE_OUTER_BOUND('',#569,.T.); +#571=ADVANCED_FACE('side-0-3',(#570),#31,.T.); +#572=ORIENTED_EDGE('',*,*,#319,.T.); +#573=ORIENTED_EDGE('',*,*,#409,.T.); +#574=ORIENTED_EDGE('',*,*,#337,.F.); +#575=ORIENTED_EDGE('',*,*,#355,.F.); +#576=EDGE_LOOP('',(#572,#573,#574,#575)); +#577=FACE_OUTER_BOUND('',#576,.T.); +#578=ADVANCED_FACE('side-1-0',(#577),#36,.T.); +#579=ORIENTED_EDGE('',*,*,#373,.T.); +#580=ORIENTED_EDGE('',*,*,#463,.T.); +#581=ORIENTED_EDGE('',*,*,#391,.F.); +#582=ORIENTED_EDGE('',*,*,#409,.F.); +#583=EDGE_LOOP('',(#579,#580,#581,#582)); +#584=FACE_OUTER_BOUND('',#583,.T.); +#585=ADVANCED_FACE('side-1-1',(#584),#41,.T.); +#586=ORIENTED_EDGE('',*,*,#427,.T.); +#587=ORIENTED_EDGE('',*,*,#517,.T.); +#588=ORIENTED_EDGE('',*,*,#445,.F.); +#589=ORIENTED_EDGE('',*,*,#463,.F.); +#590=EDGE_LOOP('',(#586,#587,#588,#589)); +#591=FACE_OUTER_BOUND('',#590,.T.); +#592=ADVANCED_FACE('side-1-2',(#591),#46,.T.); +#593=ORIENTED_EDGE('',*,*,#481,.T.); +#594=ORIENTED_EDGE('',*,*,#355,.T.); +#595=ORIENTED_EDGE('',*,*,#499,.F.); +#596=ORIENTED_EDGE('',*,*,#517,.F.); +#597=EDGE_LOOP('',(#593,#594,#595,#596)); +#598=FACE_OUTER_BOUND('',#597,.T.); +#599=ADVANCED_FACE('side-1-3',(#598),#51,.T.); +#600=CLOSED_SHELL('',(#530,#543,#550,#557,#564,#571,#578,#585,#592,#599)); +#601=MANIFOLD_SOLID_BREP('arc-edged-extrusion-with-holes-0',#600); +#602=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#603=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#604=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#605=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000568434190033E-8),#602,'distance_accuracy_value','exchange error bound'); +#606=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#605)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#602,#603,#604)) REPRESENTATION_CONTEXT('','')); +#607=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#601),#606); +#608=APPLICATION_CONTEXT('automotive design'); +#609=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#608); +#610=PRODUCT_CONTEXT('',#608,'mechanical'); +#611=PRODUCT('arc-edged-extrusion-with-holes','arc-edged-extrusion-with-holes','',(#610)); +#612=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#611,.NOT_KNOWN.); +#613=PRODUCT_DEFINITION_CONTEXT('part definition',#608,'design'); +#614=PRODUCT_DEFINITION('','',#612,#613); +#615=PRODUCT_DEFINITION_SHAPE('','',#614); +#616=SHAPE_DEFINITION_REPRESENTATION(#615,#607); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.m.report.json b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.m.report.json new file mode 100644 index 0000000..80781da --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":24,"exchange_error_bound":1.0000056843419003e-8,"faces":10,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.mm b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.mm new file mode 100644 index 0000000..fe3de6f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.mm @@ -0,0 +1,625 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('arc-edged-extrusion-with-holes','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#13=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=CYLINDRICAL_SURFACE('',#20,2.00000000000000000E3); +#22=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#23=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#24=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#28=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(-5.00000000000000000E2,0.00000000000000000E0,5.00000000000000000E2)); +#33=DIRECTION('',(1.00000000000000000E0,-0.00000000000000000E0,0.00000000000000000E0)); +#34=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#35=AXIS2_PLACEMENT_3D('',#32,#33,#34); +#36=PLANE('',#35); +#37=CARTESIAN_POINT('',(-5.00000000000000000E2,0.00000000000000000E0,-5.00000000000000000E2)); +#38=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#39=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#40=AXIS2_PLACEMENT_3D('',#37,#38,#39); +#41=PLANE('',#40); +#42=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0,-5.00000000000000000E2)); +#43=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#44=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#45=AXIS2_PLACEMENT_3D('',#42,#43,#44); +#46=PLANE('',#45); +#47=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0,5.00000000000000000E2)); +#48=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#49=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#50=AXIS2_PLACEMENT_3D('',#47,#48,#49); +#51=PLANE('',#50); +#52=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#53=VERTEX_POINT('',#52); +#54=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#55=VERTEX_POINT('',#54); +#56=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#57=VERTEX_POINT('',#56); +#58=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#59=VERTEX_POINT('',#58); +#60=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3,2.00000000000000000E3)); +#61=VERTEX_POINT('',#60); +#62=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,2.00000000000000000E3)); +#63=VERTEX_POINT('',#62); +#64=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#65=VERTEX_POINT('',#64); +#66=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#67=VERTEX_POINT('',#66); +#68=CARTESIAN_POINT('',(-5.00000000000000000E2,0.00000000000000000E0,5.00000000000000000E2)); +#69=VERTEX_POINT('',#68); +#70=CARTESIAN_POINT('',(-5.00000000000000000E2,0.00000000000000000E0,-5.00000000000000000E2)); +#71=VERTEX_POINT('',#70); +#72=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0,-5.00000000000000000E2)); +#73=VERTEX_POINT('',#72); +#74=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0,5.00000000000000000E2)); +#75=VERTEX_POINT('',#74); +#76=CARTESIAN_POINT('',(-5.00000000000000000E2,2.00000000000000000E3,5.00000000000000000E2)); +#77=VERTEX_POINT('',#76); +#78=CARTESIAN_POINT('',(-5.00000000000000000E2,2.00000000000000000E3,-5.00000000000000000E2)); +#79=VERTEX_POINT('',#78); +#80=CARTESIAN_POINT('',(5.00000000000000000E2,2.00000000000000000E3,-5.00000000000000000E2)); +#81=VERTEX_POINT('',#80); +#82=CARTESIAN_POINT('',(5.00000000000000000E2,2.00000000000000000E3,5.00000000000000000E2)); +#83=VERTEX_POINT('',#82); +#84=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#85=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#86=VECTOR('',#85,1.00000000000000000E3); +#87=LINE('',#84,#86); +#88=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#90=VECTOR('',#89,1.00000000000000000E3); +#91=LINE('',#88,#90); +#92=DEFINITIONAL_REPRESENTATION('',(#91),#1); +#93=PCURVE('',#11,#92); +#94=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#95=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#96=VECTOR('',#95,1.00000000000000000E3); +#97=LINE('',#94,#96); +#98=DEFINITIONAL_REPRESENTATION('',(#97),#1); +#99=PCURVE('',#16,#98); +#100=SURFACE_CURVE('',#87,(#93,#99),.CURVE_3D.); +#101=EDGE_CURVE('',#53,#55,#100,.T.); +#102=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3,2.00000000000000000E3)); +#103=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#104=VECTOR('',#103,1.00000000000000000E3); +#105=LINE('',#102,#104); +#106=CARTESIAN_POINT('',(-2.00000000000000000E3,-2.00000000000000000E3)); +#107=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E3); +#109=LINE('',#106,#108); +#110=DEFINITIONAL_REPRESENTATION('',(#109),#1); +#111=PCURVE('',#6,#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#113=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#114=VECTOR('',#113,1.00000000000000000E3); +#115=LINE('',#112,#114); +#116=DEFINITIONAL_REPRESENTATION('',(#115),#1); +#117=PCURVE('',#16,#116); +#118=SURFACE_CURVE('',#105,(#111,#117),.CURVE_3D.); +#119=EDGE_CURVE('',#61,#63,#118,.T.); +#120=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#121=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#122=VECTOR('',#121,1.00000000000000000E3); +#123=LINE('',#120,#122); +#124=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#125=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E3); +#127=LINE('',#124,#126); +#128=DEFINITIONAL_REPRESENTATION('',(#127),#1); +#129=PCURVE('',#16,#128); +#130=CARTESIAN_POINT('',(4.00000000000000000E3,0.00000000000000000E0)); +#131=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#132=VECTOR('',#131,1.00000000000000000E3); +#133=LINE('',#130,#132); +#134=DEFINITIONAL_REPRESENTATION('',(#133),#1); +#135=PCURVE('',#31,#134); +#136=SURFACE_CURVE('',#123,(#129,#135),.CURVE_3D.); +#137=EDGE_CURVE('',#53,#61,#136,.T.); +#138=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#139=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#140=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#141=AXIS2_PLACEMENT_3D('',#138,#139,#140); +#142=CIRCLE('',#141,2.00000000000000000E3); +#143=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#144=DIRECTION('',(2.00000000000000000E3,0.00000000000000000E0)); +#145=AXIS2_PLACEMENT_2D('',#143,#144); +#146=CIRCLE('',#145,2.00000000000000000E3); +#147=DEFINITIONAL_REPRESENTATION('',(#146),#1); +#148=PCURVE('',#11,#147); +#149=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#150=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#151=VECTOR('',#150,1.00000000000000000E0); +#152=LINE('',#149,#151); +#153=DEFINITIONAL_REPRESENTATION('',(#152),#1); +#154=PCURVE('',#21,#153); +#155=SURFACE_CURVE('',#142,(#148,#154),.CURVE_3D.); +#156=EDGE_CURVE('',#55,#57,#155,.T.); +#157=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#158=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#159=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#160=AXIS2_PLACEMENT_3D('',#157,#158,#159); +#161=CIRCLE('',#160,2.00000000000000000E3); +#162=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#163=DIRECTION('',(2.00000000000000000E3,0.00000000000000000E0)); +#164=AXIS2_PLACEMENT_2D('',#162,#163); +#165=CIRCLE('',#164,2.00000000000000000E3); +#166=DEFINITIONAL_REPRESENTATION('',(#165),#1); +#167=PCURVE('',#6,#166); +#168=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#169=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#170=VECTOR('',#169,1.00000000000000000E0); +#171=LINE('',#168,#170); +#172=DEFINITIONAL_REPRESENTATION('',(#171),#1); +#173=PCURVE('',#21,#172); +#174=SURFACE_CURVE('',#161,(#167,#173),.CURVE_3D.); +#175=EDGE_CURVE('',#63,#65,#174,.T.); +#176=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#177=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#178=VECTOR('',#177,1.00000000000000000E3); +#179=LINE('',#176,#178); +#180=CARTESIAN_POINT('',(4.00000000000000000E3,0.00000000000000000E0)); +#181=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#182=VECTOR('',#181,1.00000000000000000E3); +#183=LINE('',#180,#182); +#184=DEFINITIONAL_REPRESENTATION('',(#183),#1); +#185=PCURVE('',#16,#184); +#186=CARTESIAN_POINT('',(-1.57079632679489656E0,0.00000000000000000E0)); +#187=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#188=VECTOR('',#187,1.00000000000000000E3); +#189=LINE('',#186,#188); +#190=DEFINITIONAL_REPRESENTATION('',(#189),#1); +#191=PCURVE('',#21,#190); +#192=SURFACE_CURVE('',#179,(#185,#191),.CURVE_3D.); +#193=EDGE_CURVE('',#55,#63,#192,.T.); +#194=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#195=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#196=VECTOR('',#195,1.00000000000000000E3); +#197=LINE('',#194,#196); +#198=CARTESIAN_POINT('',(2.00000000000000000E3,-2.00000000000000000E3)); +#199=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#200=VECTOR('',#199,1.00000000000000000E3); +#201=LINE('',#198,#200); +#202=DEFINITIONAL_REPRESENTATION('',(#201),#1); +#203=PCURVE('',#11,#202); +#204=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#205=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#206=VECTOR('',#205,1.00000000000000000E3); +#207=LINE('',#204,#206); +#208=DEFINITIONAL_REPRESENTATION('',(#207),#1); +#209=PCURVE('',#26,#208); +#210=SURFACE_CURVE('',#197,(#203,#209),.CURVE_3D.); +#211=EDGE_CURVE('',#57,#59,#210,.T.); +#212=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#213=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#214=VECTOR('',#213,1.00000000000000000E3); +#215=LINE('',#212,#214); +#216=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#217=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#218=VECTOR('',#217,1.00000000000000000E3); +#219=LINE('',#216,#218); +#220=DEFINITIONAL_REPRESENTATION('',(#219),#1); +#221=PCURVE('',#6,#220); +#222=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#223=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#224=VECTOR('',#223,1.00000000000000000E3); +#225=LINE('',#222,#224); +#226=DEFINITIONAL_REPRESENTATION('',(#225),#1); +#227=PCURVE('',#26,#226); +#228=SURFACE_CURVE('',#215,(#221,#227),.CURVE_3D.); +#229=EDGE_CURVE('',#65,#67,#228,.T.); +#230=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#231=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#232=VECTOR('',#231,1.00000000000000000E3); +#233=LINE('',#230,#232); +#234=CARTESIAN_POINT('',(1.57079632679489656E0,0.00000000000000000E0)); +#235=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#236=VECTOR('',#235,1.00000000000000000E3); +#237=LINE('',#234,#236); +#238=DEFINITIONAL_REPRESENTATION('',(#237),#1); +#239=PCURVE('',#21,#238); +#240=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#241=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#242=VECTOR('',#241,1.00000000000000000E3); +#243=LINE('',#240,#242); +#244=DEFINITIONAL_REPRESENTATION('',(#243),#1); +#245=PCURVE('',#26,#244); +#246=SURFACE_CURVE('',#233,(#239,#245),.CURVE_3D.); +#247=EDGE_CURVE('',#57,#65,#246,.T.); +#248=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#249=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#250=VECTOR('',#249,1.00000000000000000E3); +#251=LINE('',#248,#250); +#252=CARTESIAN_POINT('',(-2.00000000000000000E3,-2.00000000000000000E3)); +#253=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#254=VECTOR('',#253,1.00000000000000000E3); +#255=LINE('',#252,#254); +#256=DEFINITIONAL_REPRESENTATION('',(#255),#1); +#257=PCURVE('',#11,#256); +#258=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#259=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#260=VECTOR('',#259,1.00000000000000000E3); +#261=LINE('',#258,#260); +#262=DEFINITIONAL_REPRESENTATION('',(#261),#1); +#263=PCURVE('',#31,#262); +#264=SURFACE_CURVE('',#251,(#257,#263),.CURVE_3D.); +#265=EDGE_CURVE('',#59,#53,#264,.T.); +#266=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#267=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#268=VECTOR('',#267,1.00000000000000000E3); +#269=LINE('',#266,#268); +#270=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3)); +#271=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#272=VECTOR('',#271,1.00000000000000000E3); +#273=LINE('',#270,#272); +#274=DEFINITIONAL_REPRESENTATION('',(#273),#1); +#275=PCURVE('',#6,#274); +#276=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#277=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#278=VECTOR('',#277,1.00000000000000000E3); +#279=LINE('',#276,#278); +#280=DEFINITIONAL_REPRESENTATION('',(#279),#1); +#281=PCURVE('',#31,#280); +#282=SURFACE_CURVE('',#269,(#275,#281),.CURVE_3D.); +#283=EDGE_CURVE('',#67,#61,#282,.T.); +#284=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#285=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#286=VECTOR('',#285,1.00000000000000000E3); +#287=LINE('',#284,#286); +#288=CARTESIAN_POINT('',(4.00000000000000000E3,0.00000000000000000E0)); +#289=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#290=VECTOR('',#289,1.00000000000000000E3); +#291=LINE('',#288,#290); +#292=DEFINITIONAL_REPRESENTATION('',(#291),#1); +#293=PCURVE('',#26,#292); +#294=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#295=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#296=VECTOR('',#295,1.00000000000000000E3); +#297=LINE('',#294,#296); +#298=DEFINITIONAL_REPRESENTATION('',(#297),#1); +#299=PCURVE('',#31,#298); +#300=SURFACE_CURVE('',#287,(#293,#299),.CURVE_3D.); +#301=EDGE_CURVE('',#59,#67,#300,.T.); +#302=CARTESIAN_POINT('',(-5.00000000000000000E2,0.00000000000000000E0,5.00000000000000000E2)); +#303=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#304=VECTOR('',#303,1.00000000000000000E3); +#305=LINE('',#302,#304); +#306=CARTESIAN_POINT('',(-5.00000000000000000E2,5.00000000000000000E2)); +#307=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#308=VECTOR('',#307,1.00000000000000000E3); +#309=LINE('',#306,#308); +#310=DEFINITIONAL_REPRESENTATION('',(#309),#1); +#311=PCURVE('',#11,#310); +#312=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#313=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#314=VECTOR('',#313,1.00000000000000000E3); +#315=LINE('',#312,#314); +#316=DEFINITIONAL_REPRESENTATION('',(#315),#1); +#317=PCURVE('',#36,#316); +#318=SURFACE_CURVE('',#305,(#311,#317),.CURVE_3D.); +#319=EDGE_CURVE('',#69,#71,#318,.T.); +#320=CARTESIAN_POINT('',(-5.00000000000000000E2,2.00000000000000000E3,5.00000000000000000E2)); +#321=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#322=VECTOR('',#321,1.00000000000000000E3); +#323=LINE('',#320,#322); +#324=CARTESIAN_POINT('',(-5.00000000000000000E2,-5.00000000000000000E2)); +#325=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#326=VECTOR('',#325,1.00000000000000000E3); +#327=LINE('',#324,#326); +#328=DEFINITIONAL_REPRESENTATION('',(#327),#1); +#329=PCURVE('',#6,#328); +#330=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#331=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#332=VECTOR('',#331,1.00000000000000000E3); +#333=LINE('',#330,#332); +#334=DEFINITIONAL_REPRESENTATION('',(#333),#1); +#335=PCURVE('',#36,#334); +#336=SURFACE_CURVE('',#323,(#329,#335),.CURVE_3D.); +#337=EDGE_CURVE('',#77,#79,#336,.T.); +#338=CARTESIAN_POINT('',(-5.00000000000000000E2,0.00000000000000000E0,5.00000000000000000E2)); +#339=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#340=VECTOR('',#339,1.00000000000000000E3); +#341=LINE('',#338,#340); +#342=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#343=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#344=VECTOR('',#343,1.00000000000000000E3); +#345=LINE('',#342,#344); +#346=DEFINITIONAL_REPRESENTATION('',(#345),#1); +#347=PCURVE('',#36,#346); +#348=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#349=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#350=VECTOR('',#349,1.00000000000000000E3); +#351=LINE('',#348,#350); +#352=DEFINITIONAL_REPRESENTATION('',(#351),#1); +#353=PCURVE('',#51,#352); +#354=SURFACE_CURVE('',#341,(#347,#353),.CURVE_3D.); +#355=EDGE_CURVE('',#69,#77,#354,.T.); +#356=CARTESIAN_POINT('',(-5.00000000000000000E2,0.00000000000000000E0,-5.00000000000000000E2)); +#357=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#358=VECTOR('',#357,1.00000000000000000E3); +#359=LINE('',#356,#358); +#360=CARTESIAN_POINT('',(-5.00000000000000000E2,-5.00000000000000000E2)); +#361=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#362=VECTOR('',#361,1.00000000000000000E3); +#363=LINE('',#360,#362); +#364=DEFINITIONAL_REPRESENTATION('',(#363),#1); +#365=PCURVE('',#11,#364); +#366=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#367=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#368=VECTOR('',#367,1.00000000000000000E3); +#369=LINE('',#366,#368); +#370=DEFINITIONAL_REPRESENTATION('',(#369),#1); +#371=PCURVE('',#41,#370); +#372=SURFACE_CURVE('',#359,(#365,#371),.CURVE_3D.); +#373=EDGE_CURVE('',#71,#73,#372,.T.); +#374=CARTESIAN_POINT('',(-5.00000000000000000E2,2.00000000000000000E3,-5.00000000000000000E2)); +#375=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#376=VECTOR('',#375,1.00000000000000000E3); +#377=LINE('',#374,#376); +#378=CARTESIAN_POINT('',(-5.00000000000000000E2,5.00000000000000000E2)); +#379=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#380=VECTOR('',#379,1.00000000000000000E3); +#381=LINE('',#378,#380); +#382=DEFINITIONAL_REPRESENTATION('',(#381),#1); +#383=PCURVE('',#6,#382); +#384=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#385=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#386=VECTOR('',#385,1.00000000000000000E3); +#387=LINE('',#384,#386); +#388=DEFINITIONAL_REPRESENTATION('',(#387),#1); +#389=PCURVE('',#41,#388); +#390=SURFACE_CURVE('',#377,(#383,#389),.CURVE_3D.); +#391=EDGE_CURVE('',#79,#81,#390,.T.); +#392=CARTESIAN_POINT('',(-5.00000000000000000E2,0.00000000000000000E0,-5.00000000000000000E2)); +#393=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#394=VECTOR('',#393,1.00000000000000000E3); +#395=LINE('',#392,#394); +#396=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#397=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#398=VECTOR('',#397,1.00000000000000000E3); +#399=LINE('',#396,#398); +#400=DEFINITIONAL_REPRESENTATION('',(#399),#1); +#401=PCURVE('',#36,#400); +#402=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#403=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#404=VECTOR('',#403,1.00000000000000000E3); +#405=LINE('',#402,#404); +#406=DEFINITIONAL_REPRESENTATION('',(#405),#1); +#407=PCURVE('',#41,#406); +#408=SURFACE_CURVE('',#395,(#401,#407),.CURVE_3D.); +#409=EDGE_CURVE('',#71,#79,#408,.T.); +#410=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0,-5.00000000000000000E2)); +#411=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#412=VECTOR('',#411,1.00000000000000000E3); +#413=LINE('',#410,#412); +#414=CARTESIAN_POINT('',(5.00000000000000000E2,-5.00000000000000000E2)); +#415=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#416=VECTOR('',#415,1.00000000000000000E3); +#417=LINE('',#414,#416); +#418=DEFINITIONAL_REPRESENTATION('',(#417),#1); +#419=PCURVE('',#11,#418); +#420=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#421=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#422=VECTOR('',#421,1.00000000000000000E3); +#423=LINE('',#420,#422); +#424=DEFINITIONAL_REPRESENTATION('',(#423),#1); +#425=PCURVE('',#46,#424); +#426=SURFACE_CURVE('',#413,(#419,#425),.CURVE_3D.); +#427=EDGE_CURVE('',#73,#75,#426,.T.); +#428=CARTESIAN_POINT('',(5.00000000000000000E2,2.00000000000000000E3,-5.00000000000000000E2)); +#429=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#430=VECTOR('',#429,1.00000000000000000E3); +#431=LINE('',#428,#430); +#432=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#433=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#434=VECTOR('',#433,1.00000000000000000E3); +#435=LINE('',#432,#434); +#436=DEFINITIONAL_REPRESENTATION('',(#435),#1); +#437=PCURVE('',#6,#436); +#438=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#439=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#440=VECTOR('',#439,1.00000000000000000E3); +#441=LINE('',#438,#440); +#442=DEFINITIONAL_REPRESENTATION('',(#441),#1); +#443=PCURVE('',#46,#442); +#444=SURFACE_CURVE('',#431,(#437,#443),.CURVE_3D.); +#445=EDGE_CURVE('',#81,#83,#444,.T.); +#446=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0,-5.00000000000000000E2)); +#447=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#448=VECTOR('',#447,1.00000000000000000E3); +#449=LINE('',#446,#448); +#450=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#451=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#452=VECTOR('',#451,1.00000000000000000E3); +#453=LINE('',#450,#452); +#454=DEFINITIONAL_REPRESENTATION('',(#453),#1); +#455=PCURVE('',#41,#454); +#456=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#457=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#458=VECTOR('',#457,1.00000000000000000E3); +#459=LINE('',#456,#458); +#460=DEFINITIONAL_REPRESENTATION('',(#459),#1); +#461=PCURVE('',#46,#460); +#462=SURFACE_CURVE('',#449,(#455,#461),.CURVE_3D.); +#463=EDGE_CURVE('',#73,#81,#462,.T.); +#464=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0,5.00000000000000000E2)); +#465=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#466=VECTOR('',#465,1.00000000000000000E3); +#467=LINE('',#464,#466); +#468=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#469=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#470=VECTOR('',#469,1.00000000000000000E3); +#471=LINE('',#468,#470); +#472=DEFINITIONAL_REPRESENTATION('',(#471),#1); +#473=PCURVE('',#11,#472); +#474=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#475=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#476=VECTOR('',#475,1.00000000000000000E3); +#477=LINE('',#474,#476); +#478=DEFINITIONAL_REPRESENTATION('',(#477),#1); +#479=PCURVE('',#51,#478); +#480=SURFACE_CURVE('',#467,(#473,#479),.CURVE_3D.); +#481=EDGE_CURVE('',#75,#69,#480,.T.); +#482=CARTESIAN_POINT('',(5.00000000000000000E2,2.00000000000000000E3,5.00000000000000000E2)); +#483=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#484=VECTOR('',#483,1.00000000000000000E3); +#485=LINE('',#482,#484); +#486=CARTESIAN_POINT('',(5.00000000000000000E2,-5.00000000000000000E2)); +#487=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#488=VECTOR('',#487,1.00000000000000000E3); +#489=LINE('',#486,#488); +#490=DEFINITIONAL_REPRESENTATION('',(#489),#1); +#491=PCURVE('',#6,#490); +#492=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#493=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#494=VECTOR('',#493,1.00000000000000000E3); +#495=LINE('',#492,#494); +#496=DEFINITIONAL_REPRESENTATION('',(#495),#1); +#497=PCURVE('',#51,#496); +#498=SURFACE_CURVE('',#485,(#491,#497),.CURVE_3D.); +#499=EDGE_CURVE('',#83,#77,#498,.T.); +#500=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0,5.00000000000000000E2)); +#501=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#502=VECTOR('',#501,1.00000000000000000E3); +#503=LINE('',#500,#502); +#504=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#505=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#506=VECTOR('',#505,1.00000000000000000E3); +#507=LINE('',#504,#506); +#508=DEFINITIONAL_REPRESENTATION('',(#507),#1); +#509=PCURVE('',#46,#508); +#510=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#511=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#512=VECTOR('',#511,1.00000000000000000E3); +#513=LINE('',#510,#512); +#514=DEFINITIONAL_REPRESENTATION('',(#513),#1); +#515=PCURVE('',#51,#514); +#516=SURFACE_CURVE('',#503,(#509,#515),.CURVE_3D.); +#517=EDGE_CURVE('',#75,#83,#516,.T.); +#518=ORIENTED_EDGE('',*,*,#119,.T.); +#519=ORIENTED_EDGE('',*,*,#175,.T.); +#520=ORIENTED_EDGE('',*,*,#229,.T.); +#521=ORIENTED_EDGE('',*,*,#283,.T.); +#522=EDGE_LOOP('',(#518,#519,#520,#521)); +#523=FACE_OUTER_BOUND('',#522,.T.); +#524=ORIENTED_EDGE('',*,*,#337,.T.); +#525=ORIENTED_EDGE('',*,*,#391,.T.); +#526=ORIENTED_EDGE('',*,*,#445,.T.); +#527=ORIENTED_EDGE('',*,*,#499,.T.); +#528=EDGE_LOOP('',(#524,#525,#526,#527)); +#529=FACE_BOUND('',#528,.T.); +#530=ADVANCED_FACE('top',(#523,#529),#6,.T.); +#531=ORIENTED_EDGE('',*,*,#265,.F.); +#532=ORIENTED_EDGE('',*,*,#211,.F.); +#533=ORIENTED_EDGE('',*,*,#156,.F.); +#534=ORIENTED_EDGE('',*,*,#101,.F.); +#535=EDGE_LOOP('',(#531,#532,#533,#534)); +#536=FACE_OUTER_BOUND('',#535,.T.); +#537=ORIENTED_EDGE('',*,*,#481,.F.); +#538=ORIENTED_EDGE('',*,*,#427,.F.); +#539=ORIENTED_EDGE('',*,*,#373,.F.); +#540=ORIENTED_EDGE('',*,*,#319,.F.); +#541=EDGE_LOOP('',(#537,#538,#539,#540)); +#542=FACE_BOUND('',#541,.T.); +#543=ADVANCED_FACE('bottom',(#536,#542),#11,.T.); +#544=ORIENTED_EDGE('',*,*,#101,.T.); +#545=ORIENTED_EDGE('',*,*,#193,.T.); +#546=ORIENTED_EDGE('',*,*,#119,.F.); +#547=ORIENTED_EDGE('',*,*,#137,.F.); +#548=EDGE_LOOP('',(#544,#545,#546,#547)); +#549=FACE_OUTER_BOUND('',#548,.T.); +#550=ADVANCED_FACE('side-0-0',(#549),#16,.T.); +#551=ORIENTED_EDGE('',*,*,#156,.T.); +#552=ORIENTED_EDGE('',*,*,#247,.T.); +#553=ORIENTED_EDGE('',*,*,#175,.F.); +#554=ORIENTED_EDGE('',*,*,#193,.F.); +#555=EDGE_LOOP('',(#551,#552,#553,#554)); +#556=FACE_OUTER_BOUND('',#555,.T.); +#557=ADVANCED_FACE('side-0-1',(#556),#21,.T.); +#558=ORIENTED_EDGE('',*,*,#211,.T.); +#559=ORIENTED_EDGE('',*,*,#301,.T.); +#560=ORIENTED_EDGE('',*,*,#229,.F.); +#561=ORIENTED_EDGE('',*,*,#247,.F.); +#562=EDGE_LOOP('',(#558,#559,#560,#561)); +#563=FACE_OUTER_BOUND('',#562,.T.); +#564=ADVANCED_FACE('side-0-2',(#563),#26,.T.); +#565=ORIENTED_EDGE('',*,*,#265,.T.); +#566=ORIENTED_EDGE('',*,*,#137,.T.); +#567=ORIENTED_EDGE('',*,*,#283,.F.); +#568=ORIENTED_EDGE('',*,*,#301,.F.); +#569=EDGE_LOOP('',(#565,#566,#567,#568)); +#570=FACE_OUTER_BOUND('',#569,.T.); +#571=ADVANCED_FACE('side-0-3',(#570),#31,.T.); +#572=ORIENTED_EDGE('',*,*,#319,.T.); +#573=ORIENTED_EDGE('',*,*,#409,.T.); +#574=ORIENTED_EDGE('',*,*,#337,.F.); +#575=ORIENTED_EDGE('',*,*,#355,.F.); +#576=EDGE_LOOP('',(#572,#573,#574,#575)); +#577=FACE_OUTER_BOUND('',#576,.T.); +#578=ADVANCED_FACE('side-1-0',(#577),#36,.T.); +#579=ORIENTED_EDGE('',*,*,#373,.T.); +#580=ORIENTED_EDGE('',*,*,#463,.T.); +#581=ORIENTED_EDGE('',*,*,#391,.F.); +#582=ORIENTED_EDGE('',*,*,#409,.F.); +#583=EDGE_LOOP('',(#579,#580,#581,#582)); +#584=FACE_OUTER_BOUND('',#583,.T.); +#585=ADVANCED_FACE('side-1-1',(#584),#41,.T.); +#586=ORIENTED_EDGE('',*,*,#427,.T.); +#587=ORIENTED_EDGE('',*,*,#517,.T.); +#588=ORIENTED_EDGE('',*,*,#445,.F.); +#589=ORIENTED_EDGE('',*,*,#463,.F.); +#590=EDGE_LOOP('',(#586,#587,#588,#589)); +#591=FACE_OUTER_BOUND('',#590,.T.); +#592=ADVANCED_FACE('side-1-2',(#591),#46,.T.); +#593=ORIENTED_EDGE('',*,*,#481,.T.); +#594=ORIENTED_EDGE('',*,*,#355,.T.); +#595=ORIENTED_EDGE('',*,*,#499,.F.); +#596=ORIENTED_EDGE('',*,*,#517,.F.); +#597=EDGE_LOOP('',(#593,#594,#595,#596)); +#598=FACE_OUTER_BOUND('',#597,.T.); +#599=ADVANCED_FACE('side-1-3',(#598),#51,.T.); +#600=CLOSED_SHELL('',(#530,#543,#550,#557,#564,#571,#578,#585,#592,#599)); +#601=MANIFOLD_SOLID_BREP('arc-edged-extrusion-with-holes-0',#600); +#602=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#603=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#604=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#605=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000568434190039E-5),#602,'distance_accuracy_value','exchange error bound'); +#606=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#605)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#602,#603,#604)) REPRESENTATION_CONTEXT('','')); +#607=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#601),#606); +#608=APPLICATION_CONTEXT('automotive design'); +#609=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#608); +#610=PRODUCT_CONTEXT('',#608,'mechanical'); +#611=PRODUCT('arc-edged-extrusion-with-holes','arc-edged-extrusion-with-holes','',(#610)); +#612=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#611,.NOT_KNOWN.); +#613=PRODUCT_DEFINITION_CONTEXT('part definition',#608,'design'); +#614=PRODUCT_DEFINITION('','',#612,#613); +#615=PRODUCT_DEFINITION_SHAPE('','',#614); +#616=SHAPE_DEFINITION_REPRESENTATION(#615,#607); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.mm.report.json new file mode 100644 index 0000000..d189224 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":24,"exchange_error_bound":0.000010000056843419004,"faces":10,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.tess.json b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.tess.json new file mode 100644 index 0000000..0495368 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion-with-holes.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[25,0,26,29,26,0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,10,10,11,12,12,13,14,14,15,16,16,17,18,18,19,20,20,21,22,22,23,24,25,26,27,29,0,2,2,4,6,6,8,10,10,12,14,14,16,18,18,20,22,22,24,25,25,27,28,28,29,2,2,6,10,10,14,18,18,22,25,28,2,10,10,18,25,25,28,10,30,31,56,59,56,31,31,32,33,33,34,35,35,36,37,37,38,39,39,40,41,41,42,43,43,44,45,45,46,47,47,48,49,49,50,51,51,52,53,53,54,55,55,30,56,58,59,31,31,33,35,35,37,39,39,41,43,43,45,47,47,49,51,51,53,55,55,56,57,58,31,35,35,39,43,43,47,51,51,55,57,57,58,35,35,43,51,51,57,35,62,63,60,60,61,62,113,114,162,113,162,161,114,115,163,114,163,162,115,116,164,115,164,163,116,117,165,116,165,164,117,118,166,117,166,165,118,119,167,118,167,166,119,120,168,119,168,167,120,121,169,120,169,168,121,122,170,121,170,169,122,123,171,122,171,170,123,124,172,123,172,171,124,125,173,124,173,172,125,126,174,125,174,173,126,127,175,126,175,174,127,128,176,127,176,175,128,129,177,128,177,176,129,130,178,129,178,177,130,131,179,130,179,178,131,132,180,131,180,179,132,133,181,132,181,180,133,134,182,133,182,181,134,135,183,134,183,182,135,136,184,135,184,183,136,137,185,136,185,184,137,138,186,137,186,185,138,139,187,138,187,186,139,140,188,139,188,187,140,141,189,140,189,188,141,142,190,141,190,189,142,143,191,142,191,190,143,144,192,143,192,191,144,145,193,144,193,192,145,146,194,145,194,193,146,147,195,146,195,194,147,148,196,147,196,195,148,149,197,148,197,196,149,150,198,149,198,197,150,151,199,150,199,198,151,152,200,151,200,199,152,153,201,152,201,200,153,154,202,153,202,201,154,155,203,154,203,202,155,156,204,155,204,203,156,157,205,156,205,204,157,158,206,157,206,205,258,259,256,256,257,258,262,263,260,260,261,262,266,267,264,264,265,266,270,271,268,268,269,270,274,275,272,272,273,274,278,279,276,276,277,278],"normals":[0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,-4.999999969612645e-9,0.0,1.0,6.123234262925839e-17,0.0,1.0,0.13616664707660675,0.0,0.9906859397888184,0.13616664707660675,0.0,0.9906859397888184,0.269796758890152,0.0,0.9629172682762146,0.269796758890152,0.0,0.9629172682762146,0.39840108156204224,0.0,0.9172112941741943,0.39840108156204224,0.0,0.9172112941741943,0.5195839405059814,0.0,0.8544194102287292,0.5195839405059814,0.0,0.8544194102287292,0.6310879588127136,0.0,0.7757112979888916,0.6310879588127136,0.0,0.7757112979888916,0.7308359742164612,0.0,0.6825531721115112,0.7308359742164612,0.0,0.6825531721115112,0.8169698715209961,0.0,0.5766803026199341,0.8169698715209961,0.0,0.5766803026199341,0.8878852128982544,0.0,0.4600650370121002,0.8878852128982544,0.0,0.4600650370121002,0.9422609210014343,0.0,0.334879606962204,0.9422609210014343,0.0,0.334879606962204,0.9790840744972229,0.0,0.20345601439476013,0.9790840744972229,0.0,0.20345601439476013,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9790840744972229,0.0,-0.20345601439476013,0.9790840744972229,0.0,-0.20345601439476013,0.9422609210014343,0.0,-0.334879606962204,0.9422609210014343,0.0,-0.334879606962204,0.8878852128982544,0.0,-0.4600650370121002,0.8878852128982544,0.0,-0.4600650370121002,0.8169698715209961,0.0,-0.5766803026199341,0.8169698715209961,0.0,-0.5766803026199341,0.7308359742164612,0.0,-0.6825531721115112,0.7308359742164612,0.0,-0.6825531721115112,0.6310879588127136,0.0,-0.7757112979888916,0.6310879588127136,0.0,-0.7757112979888916,0.5195839405059814,0.0,-0.8544194102287292,0.5195839405059814,0.0,-0.8544194102287292,0.39840108156204224,0.0,-0.9172112941741943,0.39840108156204224,0.0,-0.9172112941741943,0.269796758890152,0.0,-0.9629172682762146,0.269796758890152,0.0,-0.9629172682762146,0.13616664707660675,0.0,-0.9906859397888184,0.13616664707660675,0.0,-0.9906859397888184,6.123234262925839e-17,0.0,-1.0,-4.999999969612645e-9,0.0,-1.0,-4.999999969612645e-9,0.0,1.0,6.123234262925839e-17,0.0,1.0,0.13616664707660675,0.0,0.9906859397888184,0.13616664707660675,0.0,0.9906859397888184,0.269796758890152,0.0,0.9629172682762146,0.269796758890152,0.0,0.9629172682762146,0.39840108156204224,0.0,0.9172112941741943,0.39840108156204224,0.0,0.9172112941741943,0.5195839405059814,0.0,0.8544194102287292,0.5195839405059814,0.0,0.8544194102287292,0.6310879588127136,0.0,0.7757112979888916,0.6310879588127136,0.0,0.7757112979888916,0.7308359742164612,0.0,0.6825531721115112,0.7308359742164612,0.0,0.6825531721115112,0.8169698715209961,0.0,0.5766803026199341,0.8169698715209961,0.0,0.5766803026199341,0.8878852128982544,0.0,0.4600650370121002,0.8878852128982544,0.0,0.4600650370121002,0.9422609210014343,0.0,0.334879606962204,0.9422609210014343,0.0,0.334879606962204,0.9790840744972229,0.0,0.20345601439476013,0.9790840744972229,0.0,0.20345601439476013,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9790840744972229,0.0,-0.20345601439476013,0.9790840744972229,0.0,-0.20345601439476013,0.9422609210014343,0.0,-0.334879606962204,0.9422609210014343,0.0,-0.334879606962204,0.8878852128982544,0.0,-0.4600650370121002,0.8878852128982544,0.0,-0.4600650370121002,0.8169698715209961,0.0,-0.5766803026199341,0.8169698715209961,0.0,-0.5766803026199341,0.7308359742164612,0.0,-0.6825531721115112,0.7308359742164612,0.0,-0.6825531721115112,0.6310879588127136,0.0,-0.7757112979888916,0.6310879588127136,0.0,-0.7757112979888916,0.5195839405059814,0.0,-0.8544194102287292,0.5195839405059814,0.0,-0.8544194102287292,0.39840108156204224,0.0,-0.9172112941741943,0.39840108156204224,0.0,-0.9172112941741943,0.269796758890152,0.0,-0.9629172682762146,0.269796758890152,0.0,-0.9629172682762146,0.13616664707660675,0.0,-0.9906859397888184,0.13616664707660675,0.0,-0.9906859397888184,6.123234262925839e-17,0.0,-1.0,-4.999999969612645e-9,0.0,-1.0,-4.999999969612645e-9,0.0,1.0,6.123234262925839e-17,0.0,1.0,0.13616664707660675,0.0,0.9906859397888184,0.13616664707660675,0.0,0.9906859397888184,0.269796758890152,0.0,0.9629172682762146,0.269796758890152,0.0,0.9629172682762146,0.39840108156204224,0.0,0.9172112941741943,0.39840108156204224,0.0,0.9172112941741943,0.5195839405059814,0.0,0.8544194102287292,0.5195839405059814,0.0,0.8544194102287292,0.6310879588127136,0.0,0.7757112979888916,0.6310879588127136,0.0,0.7757112979888916,0.7308359742164612,0.0,0.6825531721115112,0.7308359742164612,0.0,0.6825531721115112,0.8169698715209961,0.0,0.5766803026199341,0.8169698715209961,0.0,0.5766803026199341,0.8878852128982544,0.0,0.4600650370121002,0.8878852128982544,0.0,0.4600650370121002,0.9422609210014343,0.0,0.334879606962204,0.9422609210014343,0.0,0.334879606962204,0.9790840744972229,0.0,0.20345601439476013,0.9790840744972229,0.0,0.20345601439476013,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9790840744972229,0.0,-0.20345601439476013,0.9790840744972229,0.0,-0.20345601439476013,0.9422609210014343,0.0,-0.334879606962204,0.9422609210014343,0.0,-0.334879606962204,0.8878852128982544,0.0,-0.4600650370121002,0.8878852128982544,0.0,-0.4600650370121002,0.8169698715209961,0.0,-0.5766803026199341,0.8169698715209961,0.0,-0.5766803026199341,0.7308359742164612,0.0,-0.6825531721115112,0.7308359742164612,0.0,-0.6825531721115112,0.6310879588127136,0.0,-0.7757112979888916,0.6310879588127136,0.0,-0.7757112979888916,0.5195839405059814,0.0,-0.8544194102287292,0.5195839405059814,0.0,-0.8544194102287292,0.39840108156204224,0.0,-0.9172112941741943,0.39840108156204224,0.0,-0.9172112941741943,0.269796758890152,0.0,-0.9629172682762146,0.269796758890152,0.0,-0.9629172682762146,0.13616664707660675,0.0,-0.9906859397888184,0.13616664707660675,0.0,-0.9906859397888184,6.123234262925839e-17,0.0,-1.0,-4.999999969612645e-9,0.0,-1.0,-4.999999969612645e-9,0.0,1.0,6.123234262925839e-17,0.0,1.0,0.13616664707660675,0.0,0.9906859397888184,0.13616664707660675,0.0,0.9906859397888184,0.269796758890152,0.0,0.9629172682762146,0.269796758890152,0.0,0.9629172682762146,0.39840108156204224,0.0,0.9172112941741943,0.39840108156204224,0.0,0.9172112941741943,0.5195839405059814,0.0,0.8544194102287292,0.5195839405059814,0.0,0.8544194102287292,0.6310879588127136,0.0,0.7757112979888916,0.6310879588127136,0.0,0.7757112979888916,0.7308359742164612,0.0,0.6825531721115112,0.7308359742164612,0.0,0.6825531721115112,0.8169698715209961,0.0,0.5766803026199341,0.8169698715209961,0.0,0.5766803026199341,0.8878852128982544,0.0,0.4600650370121002,0.8878852128982544,0.0,0.4600650370121002,0.9422609210014343,0.0,0.334879606962204,0.9422609210014343,0.0,0.334879606962204,0.9790840744972229,0.0,0.20345601439476013,0.9790840744972229,0.0,0.20345601439476013,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9790840744972229,0.0,-0.20345601439476013,0.9790840744972229,0.0,-0.20345601439476013,0.9422609210014343,0.0,-0.334879606962204,0.9422609210014343,0.0,-0.334879606962204,0.8878852128982544,0.0,-0.4600650370121002,0.8878852128982544,0.0,-0.4600650370121002,0.8169698715209961,0.0,-0.5766803026199341,0.8169698715209961,0.0,-0.5766803026199341,0.7308359742164612,0.0,-0.6825531721115112,0.7308359742164612,0.0,-0.6825531721115112,0.6310879588127136,0.0,-0.7757112979888916,0.6310879588127136,0.0,-0.7757112979888916,0.5195839405059814,0.0,-0.8544194102287292,0.5195839405059814,0.0,-0.8544194102287292,0.39840108156204224,0.0,-0.9172112941741943,0.39840108156204224,0.0,-0.9172112941741943,0.269796758890152,0.0,-0.9629172682762146,0.269796758890152,0.0,-0.9629172682762146,0.13616664707660675,0.0,-0.9906859397888184,0.13616664707660675,0.0,-0.9906859397888184,6.123234262925839e-17,0.0,-1.0,-4.999999969612645e-9,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0],"outlineEdgeIds":[0,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],"outlinePositions":[-2.0,0.0,2.0,2.0,0.0,2.0,-2.0,2.0,2.0,2.0,2.0,2.0,-2.0,0.0,2.0,-2.0,2.0,2.0,2.0,0.0,2.0,2.272333298192493,0.0,1.9813718920726615,2.272333298192493,0.0,1.9813718920726615,2.5395935423140483,0.0,1.9258345746955987,2.5395935423140483,0.0,1.9258345746955987,2.796802179692483,0.0,1.8344226030109059,2.796802179692483,0.0,1.8344226030109059,3.039167900070867,0.0,1.7088388090929771,3.039167900070867,0.0,1.7088388090929771,3.2621758886521057,0.0,1.5514225814088396,3.2621758886521057,0.0,1.5514225814088396,3.4616719285562483,0.0,1.3651062864373082,3.4616719285562483,0.0,1.3651062864373082,3.633939786020884,0.0,1.1533606442297342,3.633939786020884,0.0,1.1533606442297342,3.77577043680475,0.0,0.9201300754623043,3.77577043680475,0.0,0.9201300754623043,3.884521844237641,0.0,0.6697592243419724,3.884521844237641,0.0,0.6697592243419724,3.958168175364646,0.0,0.4069120261052674,3.958168175364646,0.0,0.4069120261052674,3.9953375383810785,0.0,0.13648482672934234,3.9953375383810785,0.0,0.13648482672934234,3.9953375383810785,0.0,-0.1364848267293419,3.9953375383810785,0.0,-0.1364848267293419,3.9581681753646456,0.0,-0.40691202610526783,3.9581681753646456,0.0,-0.40691202610526783,3.884521844237641,0.0,-0.6697592243419724,3.884521844237641,0.0,-0.6697592243419724,3.77577043680475,0.0,-0.9201300754623043,3.77577043680475,0.0,-0.9201300754623043,3.633939786020884,0.0,-1.1533606442297342,3.633939786020884,0.0,-1.1533606442297342,3.461671928556248,0.0,-1.3651062864373087,3.461671928556248,0.0,-1.3651062864373087,3.2621758886521057,0.0,-1.5514225814088394,3.2621758886521057,0.0,-1.5514225814088394,3.0391679000708676,0.0,-1.708838809092977,3.0391679000708676,0.0,-1.708838809092977,2.7968021796924827,0.0,-1.834422603010906,2.7968021796924827,0.0,-1.834422603010906,2.5395935423140483,0.0,-1.9258345746955987,2.5395935423140483,0.0,-1.9258345746955987,2.272333298192494,0.0,-1.9813718920726613,2.272333298192494,0.0,-1.9813718920726613,2.0,0.0,-2.0,2.0,2.0,2.0,2.272333298192493,2.0,1.9813718920726615,2.272333298192493,2.0,1.9813718920726615,2.5395935423140483,2.0,1.9258345746955987,2.5395935423140483,2.0,1.9258345746955987,2.796802179692483,2.0,1.8344226030109059,2.796802179692483,2.0,1.8344226030109059,3.039167900070867,2.0,1.7088388090929771,3.039167900070867,2.0,1.7088388090929771,3.2621758886521057,2.0,1.5514225814088396,3.2621758886521057,2.0,1.5514225814088396,3.4616719285562483,2.0,1.3651062864373082,3.4616719285562483,2.0,1.3651062864373082,3.633939786020884,2.0,1.1533606442297342,3.633939786020884,2.0,1.1533606442297342,3.77577043680475,2.0,0.9201300754623043,3.77577043680475,2.0,0.9201300754623043,3.884521844237641,2.0,0.6697592243419724,3.884521844237641,2.0,0.6697592243419724,3.958168175364646,2.0,0.4069120261052674,3.958168175364646,2.0,0.4069120261052674,3.9953375383810785,2.0,0.13648482672934234,3.9953375383810785,2.0,0.13648482672934234,3.9953375383810785,2.0,-0.1364848267293419,3.9953375383810785,2.0,-0.1364848267293419,3.9581681753646456,2.0,-0.40691202610526783,3.9581681753646456,2.0,-0.40691202610526783,3.884521844237641,2.0,-0.6697592243419724,3.884521844237641,2.0,-0.6697592243419724,3.77577043680475,2.0,-0.9201300754623043,3.77577043680475,2.0,-0.9201300754623043,3.633939786020884,2.0,-1.1533606442297342,3.633939786020884,2.0,-1.1533606442297342,3.461671928556248,2.0,-1.3651062864373087,3.461671928556248,2.0,-1.3651062864373087,3.2621758886521057,2.0,-1.5514225814088394,3.2621758886521057,2.0,-1.5514225814088394,3.0391679000708676,2.0,-1.708838809092977,3.0391679000708676,2.0,-1.708838809092977,2.7968021796924827,2.0,-1.834422603010906,2.7968021796924827,2.0,-1.834422603010906,2.5395935423140483,2.0,-1.9258345746955987,2.5395935423140483,2.0,-1.9258345746955987,2.272333298192494,2.0,-1.9813718920726613,2.272333298192494,2.0,-1.9813718920726613,2.0,2.0,-2.0,2.0,0.0,2.0,2.0,2.0,2.0,2.0,0.0,-2.0,-2.0,0.0,-2.0,2.0,2.0,-2.0,-2.0,2.0,-2.0,2.0,0.0,-2.0,2.0,2.0,-2.0,-2.0,0.0,-2.0,-2.0,0.0,2.0,-2.0,2.0,-2.0,-2.0,2.0,2.0,-2.0,0.0,-2.0,-2.0,2.0,-2.0,-0.5,0.0,0.5,-0.5,0.0,-0.5,-0.5,2.0,0.5,-0.5,2.0,-0.5,-0.5,0.0,0.5,-0.5,2.0,0.5,-0.5,0.0,-0.5,0.5,0.0,-0.5,-0.5,2.0,-0.5,0.5,2.0,-0.5,-0.5,0.0,-0.5,-0.5,2.0,-0.5,0.5,0.0,-0.5,0.5,0.0,0.5,0.5,2.0,-0.5,0.5,2.0,0.5,0.5,0.0,-0.5,0.5,2.0,-0.5,0.5,0.0,0.5,-0.5,0.0,0.5,0.5,2.0,0.5,-0.5,2.0,0.5,0.5,0.0,0.5,0.5,2.0,0.5],"positions":[-2.0,2.0,2.0,2.0,2.0,2.0,2.272333298192493,2.0,1.9813718920726615,2.5395935423140483,2.0,1.9258345746955987,2.796802179692483,2.0,1.8344226030109059,3.039167900070867,2.0,1.7088388090929771,3.2621758886521057,2.0,1.5514225814088396,3.4616719285562483,2.0,1.3651062864373082,3.633939786020884,2.0,1.1533606442297342,3.77577043680475,2.0,0.9201300754623043,3.884521844237641,2.0,0.6697592243419724,3.958168175364646,2.0,0.4069120261052674,3.9953375383810785,2.0,0.13648482672934234,3.9953375383810785,2.0,-0.1364848267293419,3.9581681753646456,2.0,-0.40691202610526783,3.884521844237641,2.0,-0.6697592243419724,3.77577043680475,2.0,-0.9201300754623043,3.633939786020884,2.0,-1.1533606442297342,3.461671928556248,2.0,-1.3651062864373087,3.2621758886521057,2.0,-1.5514225814088394,3.0391679000708676,2.0,-1.708838809092977,2.7968021796924827,2.0,-1.834422603010906,2.5395935423140483,2.0,-1.9258345746955987,2.272333298192494,2.0,-1.9813718920726613,2.0,2.0,-2.0,-2.0,2.0,-2.0,-0.5,2.0,0.5,-0.5,2.0,-0.5,0.5,2.0,-0.5,0.5,2.0,0.5,-2.0,0.0,2.0,-2.0,0.0,-2.0,2.0,0.0,-2.0,2.272333298192494,0.0,-1.9813718920726613,2.5395935423140483,0.0,-1.9258345746955987,2.7968021796924827,0.0,-1.834422603010906,3.0391679000708676,0.0,-1.708838809092977,3.2621758886521057,0.0,-1.5514225814088394,3.461671928556248,0.0,-1.3651062864373087,3.633939786020884,0.0,-1.1533606442297342,3.77577043680475,0.0,-0.9201300754623043,3.884521844237641,0.0,-0.6697592243419724,3.9581681753646456,0.0,-0.40691202610526783,3.9953375383810785,0.0,-0.1364848267293419,3.9953375383810785,0.0,0.13648482672934234,3.958168175364646,0.0,0.4069120261052674,3.884521844237641,0.0,0.6697592243419724,3.77577043680475,0.0,0.9201300754623043,3.633939786020884,0.0,1.1533606442297342,3.4616719285562483,0.0,1.3651062864373082,3.2621758886521057,0.0,1.5514225814088396,3.039167900070867,0.0,1.7088388090929771,2.796802179692483,0.0,1.8344226030109059,2.5395935423140483,0.0,1.9258345746955987,2.272333298192493,0.0,1.9813718920726615,2.0,0.0,2.0,-0.5,0.0,0.5,0.5,0.0,0.5,0.5,0.0,-0.5,-0.5,0.0,-0.5,-2.0,0.0,2.0,2.0,0.0,2.0,2.0,2.0,2.0,-2.0,2.0,2.0,1.9999999900000003,-1e-8,2.0,2.0,-1e-8,2.0,2.2723332891470998,-1e-8,1.9813718933159221,2.272333298192493,-1e-8,1.9813718920726615,2.5395935343595144,-1e-8,1.9258345769243546,2.5395935423140483,-1e-8,1.9258345746955987,2.796802172913095,-1e-8,1.8344226059556097,2.796802179692483,-1e-8,1.8344226030109059,3.0391678944985667,-1e-8,1.708838812481568,3.039167900070867,-1e-8,1.7088388090929771,3.2621758842676507,-1e-8,1.5514225849758583,3.2621758886521057,-1e-8,1.5514225814088396,3.4616719252918635,-1e-8,1.3651062899326107,3.4616719285562483,-1e-8,1.3651062864373082,3.633939783764309,-1e-8,1.153360647426573,3.633939786020884,-1e-8,1.1533606442297342,3.7757704354045525,-1e-8,0.9201300781645637,3.77577043680475,-1e-8,0.9201300754623043,3.884521843509642,-1e-8,0.6697592263903653,3.884521844237641,-1e-8,0.6697592243419724,3.958168175099268,-1e-8,0.4069120273823335,3.958168175364646,-1e-8,0.4069120261052674,3.995337538351408,-1e-8,0.1364848271631112,3.9953375383810785,-1e-8,0.13648482672934234,3.9953375383810785,-1e-8,-0.1364848267293419,3.995337538351408,-1e-8,-0.13648482716311075,3.9581681753646456,-1e-8,-0.40691202610526783,3.958168175099268,-1e-8,-0.40691202738233395,3.884521844237641,-1e-8,-0.6697592243419724,3.884521843509642,-1e-8,-0.6697592263903653,3.77577043680475,-1e-8,-0.9201300754623043,3.7757704354045525,-1e-8,-0.9201300781645637,3.633939786020884,-1e-8,-1.1533606442297342,3.6339397837643093,-1e-8,-1.1533606474265727,3.461671928556248,-1e-8,-1.3651062864373087,3.4616719252918635,-1e-8,-1.3651062899326107,3.2621758886521057,-1e-8,-1.5514225814088394,3.2621758842676503,-1e-8,-1.5514225849758587,3.0391679000708676,-1e-8,-1.708838809092977,3.0391678944985667,-1e-8,-1.708838812481568,2.7968021796924827,-1e-8,-1.834422603010906,2.796802172913095,-1e-8,-1.83442260595561,2.5395935423140483,-1e-8,-1.9258345746955987,2.5395935343595144,-1e-8,-1.9258345769243546,2.272333298192494,-1e-8,-1.9813718920726613,2.2723332891471,-1e-8,-1.9813718933159221,2.0,-1e-8,-2.0,1.9999999900000003,-1e-8,-2.0,1.9999999900000003,0.0,2.0,2.0,0.0,2.0,2.2723332891470998,0.0,1.9813718933159221,2.272333298192493,0.0,1.9813718920726615,2.5395935343595144,0.0,1.9258345769243546,2.5395935423140483,0.0,1.9258345746955987,2.796802172913095,0.0,1.8344226059556097,2.796802179692483,0.0,1.8344226030109059,3.0391678944985667,0.0,1.708838812481568,3.039167900070867,0.0,1.7088388090929771,3.2621758842676507,0.0,1.5514225849758583,3.2621758886521057,0.0,1.5514225814088396,3.4616719252918635,0.0,1.3651062899326107,3.4616719285562483,0.0,1.3651062864373082,3.633939783764309,0.0,1.153360647426573,3.633939786020884,0.0,1.1533606442297342,3.7757704354045525,0.0,0.9201300781645637,3.77577043680475,0.0,0.9201300754623043,3.884521843509642,0.0,0.6697592263903653,3.884521844237641,0.0,0.6697592243419724,3.958168175099268,0.0,0.4069120273823335,3.958168175364646,0.0,0.4069120261052674,3.995337538351408,0.0,0.1364848271631112,3.9953375383810785,0.0,0.13648482672934234,3.9953375383810785,0.0,-0.1364848267293419,3.995337538351408,0.0,-0.13648482716311075,3.9581681753646456,0.0,-0.40691202610526783,3.958168175099268,0.0,-0.40691202738233395,3.884521844237641,0.0,-0.6697592243419724,3.884521843509642,0.0,-0.6697592263903653,3.77577043680475,0.0,-0.9201300754623043,3.7757704354045525,0.0,-0.9201300781645637,3.633939786020884,0.0,-1.1533606442297342,3.6339397837643093,0.0,-1.1533606474265727,3.461671928556248,0.0,-1.3651062864373087,3.4616719252918635,0.0,-1.3651062899326107,3.2621758886521057,0.0,-1.5514225814088394,3.2621758842676503,0.0,-1.5514225849758587,3.0391679000708676,0.0,-1.708838809092977,3.0391678944985667,0.0,-1.708838812481568,2.7968021796924827,0.0,-1.834422603010906,2.796802172913095,0.0,-1.83442260595561,2.5395935423140483,0.0,-1.9258345746955987,2.5395935343595144,0.0,-1.9258345769243546,2.272333298192494,0.0,-1.9813718920726613,2.2723332891471,0.0,-1.9813718933159221,2.0,0.0,-2.0,1.9999999900000003,0.0,-2.0,1.9999999900000003,2.0,2.0,2.0,2.0,2.0,2.2723332891470998,2.0,1.9813718933159221,2.272333298192493,2.0,1.9813718920726615,2.5395935343595144,2.0,1.9258345769243546,2.5395935423140483,2.0,1.9258345746955987,2.796802172913095,2.0,1.8344226059556097,2.796802179692483,2.0,1.8344226030109059,3.0391678944985667,2.0,1.708838812481568,3.039167900070867,2.0,1.7088388090929771,3.2621758842676507,2.0,1.5514225849758583,3.2621758886521057,2.0,1.5514225814088396,3.4616719252918635,2.0,1.3651062899326107,3.4616719285562483,2.0,1.3651062864373082,3.633939783764309,2.0,1.153360647426573,3.633939786020884,2.0,1.1533606442297342,3.7757704354045525,2.0,0.9201300781645637,3.77577043680475,2.0,0.9201300754623043,3.884521843509642,2.0,0.6697592263903653,3.884521844237641,2.0,0.6697592243419724,3.958168175099268,2.0,0.4069120273823335,3.958168175364646,2.0,0.4069120261052674,3.995337538351408,2.0,0.1364848271631112,3.9953375383810785,2.0,0.13648482672934234,3.9953375383810785,2.0,-0.1364848267293419,3.995337538351408,2.0,-0.13648482716311075,3.9581681753646456,2.0,-0.40691202610526783,3.958168175099268,2.0,-0.40691202738233395,3.884521844237641,2.0,-0.6697592243419724,3.884521843509642,2.0,-0.6697592263903653,3.77577043680475,2.0,-0.9201300754623043,3.7757704354045525,2.0,-0.9201300781645637,3.633939786020884,2.0,-1.1533606442297342,3.6339397837643093,2.0,-1.1533606474265727,3.461671928556248,2.0,-1.3651062864373087,3.4616719252918635,2.0,-1.3651062899326107,3.2621758886521057,2.0,-1.5514225814088394,3.2621758842676503,2.0,-1.5514225849758587,3.0391679000708676,2.0,-1.708838809092977,3.0391678944985667,2.0,-1.708838812481568,2.7968021796924827,2.0,-1.834422603010906,2.796802172913095,2.0,-1.83442260595561,2.5395935423140483,2.0,-1.9258345746955987,2.5395935343595144,2.0,-1.9258345769243546,2.272333298192494,2.0,-1.9813718920726613,2.2723332891471,2.0,-1.9813718933159221,2.0,2.0,-2.0,1.9999999900000003,2.0,-2.0,1.9999999900000003,2.00000001,2.0,2.0,2.00000001,2.0,2.2723332891470998,2.00000001,1.9813718933159221,2.272333298192493,2.00000001,1.9813718920726615,2.5395935343595144,2.00000001,1.9258345769243546,2.5395935423140483,2.00000001,1.9258345746955987,2.796802172913095,2.00000001,1.8344226059556097,2.796802179692483,2.00000001,1.8344226030109059,3.0391678944985667,2.00000001,1.708838812481568,3.039167900070867,2.00000001,1.7088388090929771,3.2621758842676507,2.00000001,1.5514225849758583,3.2621758886521057,2.00000001,1.5514225814088396,3.4616719252918635,2.00000001,1.3651062899326107,3.4616719285562483,2.00000001,1.3651062864373082,3.633939783764309,2.00000001,1.153360647426573,3.633939786020884,2.00000001,1.1533606442297342,3.7757704354045525,2.00000001,0.9201300781645637,3.77577043680475,2.00000001,0.9201300754623043,3.884521843509642,2.00000001,0.6697592263903653,3.884521844237641,2.00000001,0.6697592243419724,3.958168175099268,2.00000001,0.4069120273823335,3.958168175364646,2.00000001,0.4069120261052674,3.995337538351408,2.00000001,0.1364848271631112,3.9953375383810785,2.00000001,0.13648482672934234,3.9953375383810785,2.00000001,-0.1364848267293419,3.995337538351408,2.00000001,-0.13648482716311075,3.9581681753646456,2.00000001,-0.40691202610526783,3.958168175099268,2.00000001,-0.40691202738233395,3.884521844237641,2.00000001,-0.6697592243419724,3.884521843509642,2.00000001,-0.6697592263903653,3.77577043680475,2.00000001,-0.9201300754623043,3.7757704354045525,2.00000001,-0.9201300781645637,3.633939786020884,2.00000001,-1.1533606442297342,3.6339397837643093,2.00000001,-1.1533606474265727,3.461671928556248,2.00000001,-1.3651062864373087,3.4616719252918635,2.00000001,-1.3651062899326107,3.2621758886521057,2.00000001,-1.5514225814088394,3.2621758842676503,2.00000001,-1.5514225849758587,3.0391679000708676,2.00000001,-1.708838809092977,3.0391678944985667,2.00000001,-1.708838812481568,2.7968021796924827,2.00000001,-1.834422603010906,2.796802172913095,2.00000001,-1.83442260595561,2.5395935423140483,2.00000001,-1.9258345746955987,2.5395935343595144,2.00000001,-1.9258345769243546,2.272333298192494,2.00000001,-1.9813718920726613,2.2723332891471,2.00000001,-1.9813718933159221,2.0,2.00000001,-2.0,1.9999999900000003,2.00000001,-2.0,2.0,0.0,-2.0,-2.0,0.0,-2.0,-2.0,2.0,-2.0,2.0,2.0,-2.0,-2.0,0.0,-2.0,-2.0,0.0,2.0,-2.0,2.0,2.0,-2.0,2.0,-2.0,-0.5,0.0,0.5,-0.5,0.0,-0.5,-0.5,2.0,-0.5,-0.5,2.0,0.5,-0.5,0.0,-0.5,0.5,0.0,-0.5,0.5,2.0,-0.5,-0.5,2.0,-0.5,0.5,0.0,-0.5,0.5,0.0,0.5,0.5,2.0,0.5,0.5,2.0,-0.5,0.5,0.0,0.5,-0.5,0.0,0.5,-0.5,2.0,0.5,0.5,2.0,0.5],"revision":0,"triangleFaceIds":[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,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,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,5,5,6,6,7,7,8,8,9,9]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.brep.json b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.brep.json new file mode 100644 index 0000000..f5ffe40 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"arc-edged-extrusion","revision":0,"geometry":{"surfaces":[{"kind":"Plane","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[-2.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]}},{"kind":"Cylinder","frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":2.0},{"kind":"Plane","frame":{"origin":[2.0,0.0,-2.0],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[-2.0,0.0,-2.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]}}],"curves":[{"kind":"Line","origin":[-2.0,0.0,2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[-2.0,2.0,2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[-2.0,0.0,2.0],"direction":[0.0,1.0,0.0]},{"kind":"Circle","frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":2.0},{"kind":"Circle","frame":{"origin":[2.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":2.0},{"kind":"Line","origin":[2.0,0.0,2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[2.0,0.0,-2.0],"direction":[-1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,2.0,-2.0],"direction":[-1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,0.0,-2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[-2.0,0.0,-2.0],"direction":[0.0,0.0,1.0]},{"kind":"Line","origin":[-2.0,2.0,-2.0],"direction":[0.0,0.0,1.0]},{"kind":"Line","origin":[-2.0,0.0,-2.0],"direction":[0.0,1.0,0.0]}],"pcurves":[{"kind":"Line2","origin":[-2.0,-2.0],"direction":[1.0,0.0]},{"kind":"Conic2","origin":[2.0,0.0],"axis_a":[2.0,0.0],"axis_b":[0.0,2.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[-1.0,0.0]},{"kind":"Line2","origin":[-2.0,2.0],"direction":[0.0,-1.0]},{"kind":"Line2","origin":[-2.0,-2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,-2.0],"direction":[-1.0,0.0]},{"kind":"Conic2","origin":[2.0,0.0],"axis_a":[2.0,0.0],"axis_b":[0.0,-2.0]},{"kind":"Line2","origin":[-2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[4.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5707963267948966,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[-1.5707963267948966,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[4.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[4.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[-2.0,0.0,2.0],"outgoing_halfedge":4,"tolerance":1e-8},{"id":1,"position":[2.0,0.0,2.0],"outgoing_halfedge":7,"tolerance":1e-8},{"id":2,"position":[2.0,0.0,-2.0],"outgoing_halfedge":6,"tolerance":1e-8},{"id":3,"position":[-2.0,0.0,-2.0],"outgoing_halfedge":5,"tolerance":1e-8},{"id":4,"position":[-2.0,2.0,2.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":5,"position":[2.0,2.0,2.0],"outgoing_halfedge":1,"tolerance":1e-8},{"id":6,"position":[2.0,2.0,-2.0],"outgoing_halfedge":2,"tolerance":1e-8},{"id":7,"position":[-2.0,2.0,-2.0],"outgoing_halfedge":3,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":4.0}},"halfedge":7,"twin_halfedge":8,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":4.0}},"halfedge":0,"twin_halfedge":10,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":2.0}},"halfedge":11,"twin_halfedge":21,"tolerance":1e-8,"chart_seam":false},{"id":3,"geometry":{"kind":"Curve","curve":3,"range":{"lo":-1.5707963267948966,"hi":1.5707963267948966}},"halfedge":6,"twin_halfedge":12,"tolerance":1e-8,"chart_seam":false},{"id":4,"geometry":{"kind":"Curve","curve":4,"range":{"lo":-1.5707963267948966,"hi":1.5707963267948966}},"halfedge":1,"twin_halfedge":14,"tolerance":1e-8,"chart_seam":false},{"id":5,"geometry":{"kind":"Curve","curve":5,"range":{"lo":0.0,"hi":2.0}},"halfedge":9,"twin_halfedge":15,"tolerance":1e-8,"chart_seam":false},{"id":6,"geometry":{"kind":"Curve","curve":6,"range":{"lo":0.0,"hi":4.0}},"halfedge":5,"twin_halfedge":16,"tolerance":1e-8,"chart_seam":false},{"id":7,"geometry":{"kind":"Curve","curve":7,"range":{"lo":0.0,"hi":4.0}},"halfedge":2,"twin_halfedge":18,"tolerance":1e-8,"chart_seam":false},{"id":8,"geometry":{"kind":"Curve","curve":8,"range":{"lo":0.0,"hi":2.0}},"halfedge":13,"twin_halfedge":19,"tolerance":1e-8,"chart_seam":false},{"id":9,"geometry":{"kind":"Curve","curve":9,"range":{"lo":0.0,"hi":4.0}},"halfedge":4,"twin_halfedge":20,"tolerance":1e-8,"chart_seam":false},{"id":10,"geometry":{"kind":"Curve","curve":10,"range":{"lo":0.0,"hi":4.0}},"halfedge":3,"twin_halfedge":22,"tolerance":1e-8,"chart_seam":false},{"id":11,"geometry":{"kind":"Curve","curve":11,"range":{"lo":0.0,"hi":2.0}},"halfedge":17,"twin_halfedge":23,"tolerance":1e-8,"chart_seam":false}],"halfedges":[{"id":0,"from":4,"to":5,"twin":10,"next":1,"prev":3,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":5,"to":6,"twin":14,"next":2,"prev":0,"edge":4,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":6,"to":7,"twin":18,"next":3,"prev":1,"edge":7,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":7,"to":4,"twin":22,"next":0,"prev":2,"edge":10,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":0,"to":3,"twin":20,"next":5,"prev":7,"edge":9,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":3,"to":2,"twin":16,"next":6,"prev":4,"edge":6,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":5,"periodic_lift":[0,0]}},{"id":6,"from":2,"to":1,"twin":12,"next":7,"prev":5,"edge":3,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":6,"periodic_lift":[0,0]}},{"id":7,"from":1,"to":0,"twin":8,"next":4,"prev":6,"edge":0,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":7,"periodic_lift":[0,0]}},{"id":8,"from":0,"to":1,"twin":7,"next":9,"prev":11,"edge":0,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":8,"periodic_lift":[0,0]}},{"id":9,"from":1,"to":5,"twin":15,"next":10,"prev":8,"edge":5,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":9,"periodic_lift":[0,0]}},{"id":10,"from":5,"to":4,"twin":0,"next":11,"prev":9,"edge":1,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":10,"periodic_lift":[0,0]}},{"id":11,"from":4,"to":0,"twin":21,"next":8,"prev":10,"edge":2,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":11,"periodic_lift":[0,0]}},{"id":12,"from":1,"to":2,"twin":6,"next":13,"prev":15,"edge":3,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":12,"periodic_lift":[0,0]}},{"id":13,"from":2,"to":6,"twin":19,"next":14,"prev":12,"edge":8,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":13,"periodic_lift":[0,0]}},{"id":14,"from":6,"to":5,"twin":1,"next":15,"prev":13,"edge":4,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":14,"periodic_lift":[0,0]}},{"id":15,"from":5,"to":1,"twin":9,"next":12,"prev":14,"edge":5,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":15,"periodic_lift":[0,0]}},{"id":16,"from":2,"to":3,"twin":5,"next":17,"prev":19,"edge":6,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":16,"periodic_lift":[0,0]}},{"id":17,"from":3,"to":7,"twin":23,"next":18,"prev":16,"edge":11,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":17,"periodic_lift":[0,0]}},{"id":18,"from":7,"to":6,"twin":2,"next":19,"prev":17,"edge":7,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":18,"periodic_lift":[0,0]}},{"id":19,"from":6,"to":2,"twin":13,"next":16,"prev":18,"edge":8,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":19,"periodic_lift":[0,0]}},{"id":20,"from":3,"to":0,"twin":4,"next":21,"prev":23,"edge":9,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":20,"periodic_lift":[0,0]}},{"id":21,"from":0,"to":4,"twin":11,"next":22,"prev":20,"edge":2,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":21,"periodic_lift":[0,0]}},{"id":22,"from":4,"to":7,"twin":3,"next":23,"prev":21,"edge":10,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":22,"periodic_lift":[0,0]}},{"id":23,"from":7,"to":3,"twin":17,"next":20,"prev":22,"edge":11,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":23,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false},{"id":2,"start_halfedge":8,"face_ref":2,"is_hole":false},{"id":3,"start_halfedge":12,"face_ref":3,"is_hole":false},{"id":4,"start_halfedge":16,"face_ref":4,"is_hole":false},{"id":5,"start_halfedge":20,"face_ref":5,"is_hole":false}],"faces":[{"id":0,"key":"top","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-2.00000001,"hi":4.00000001},{"lo":-2.00000001,"hi":2.00000001}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion","body":"arc-edged-extrusion","key":"top","face":0}],"role":"Authored","reversed":false}},{"id":1,"key":"bottom","surface":1,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-2.00000001,"hi":4.00000001},{"lo":-2.00000001,"hi":2.00000001}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion","body":"arc-edged-extrusion","key":"bottom","face":1}],"role":"Authored","reversed":false}},{"id":2,"key":"side-0-0","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":4.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":2,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion","body":"arc-edged-extrusion","key":"side-0-0","face":2}],"role":"Authored","reversed":false}},{"id":3,"key":"side-0-1","surface":3,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.5707963317948965,"hi":1.5707963317948965},{"lo":-1e-8,"hi":2.00000001}],"outer":3,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion","body":"arc-edged-extrusion","key":"side-0-1","face":3}],"role":"Authored","reversed":false}},{"id":4,"key":"side-0-2","surface":4,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":4.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":4,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion","body":"arc-edged-extrusion","key":"side-0-2","face":4}],"role":"Authored","reversed":false}},{"id":5,"key":"side-0-3","surface":5,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":4.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":5,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"arc-edged-extrusion","body":"arc-edged-extrusion","key":"side-0-3","face":5}],"role":"Authored","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,1,2,3,4,5],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.m b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.m new file mode 100644 index 0000000..9016fde --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.m @@ -0,0 +1,333 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('arc-edged-extrusion','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#13=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=CYLINDRICAL_SURFACE('',#20,2.00000000000000000E0); +#22=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#23=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#24=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#28=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#33=VERTEX_POINT('',#32); +#34=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#35=VERTEX_POINT('',#34); +#36=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#37=VERTEX_POINT('',#36); +#38=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#39=VERTEX_POINT('',#38); +#40=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0,2.00000000000000000E0)); +#41=VERTEX_POINT('',#40); +#42=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,2.00000000000000000E0)); +#43=VERTEX_POINT('',#42); +#44=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#45=VERTEX_POINT('',#44); +#46=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#47=VERTEX_POINT('',#46); +#48=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#49=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#50=VECTOR('',#49,1.00000000000000000E0); +#51=LINE('',#48,#50); +#52=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0)); +#53=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#54=VECTOR('',#53,1.00000000000000000E0); +#55=LINE('',#52,#54); +#56=DEFINITIONAL_REPRESENTATION('',(#55),#1); +#57=PCURVE('',#11,#56); +#58=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#59=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#60=VECTOR('',#59,1.00000000000000000E0); +#61=LINE('',#58,#60); +#62=DEFINITIONAL_REPRESENTATION('',(#61),#1); +#63=PCURVE('',#16,#62); +#64=SURFACE_CURVE('',#51,(#57,#63),.CURVE_3D.); +#65=EDGE_CURVE('',#33,#35,#64,.T.); +#66=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0,2.00000000000000000E0)); +#67=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#68=VECTOR('',#67,1.00000000000000000E0); +#69=LINE('',#66,#68); +#70=CARTESIAN_POINT('',(-2.00000000000000000E0,-2.00000000000000000E0)); +#71=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#72=VECTOR('',#71,1.00000000000000000E0); +#73=LINE('',#70,#72); +#74=DEFINITIONAL_REPRESENTATION('',(#73),#1); +#75=PCURVE('',#6,#74); +#76=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#77=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#78=VECTOR('',#77,1.00000000000000000E0); +#79=LINE('',#76,#78); +#80=DEFINITIONAL_REPRESENTATION('',(#79),#1); +#81=PCURVE('',#16,#80); +#82=SURFACE_CURVE('',#69,(#75,#81),.CURVE_3D.); +#83=EDGE_CURVE('',#41,#43,#82,.T.); +#84=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#85=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#86=VECTOR('',#85,1.00000000000000000E0); +#87=LINE('',#84,#86); +#88=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#90=VECTOR('',#89,1.00000000000000000E0); +#91=LINE('',#88,#90); +#92=DEFINITIONAL_REPRESENTATION('',(#91),#1); +#93=PCURVE('',#16,#92); +#94=CARTESIAN_POINT('',(4.00000000000000000E0,0.00000000000000000E0)); +#95=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#96=VECTOR('',#95,1.00000000000000000E0); +#97=LINE('',#94,#96); +#98=DEFINITIONAL_REPRESENTATION('',(#97),#1); +#99=PCURVE('',#31,#98); +#100=SURFACE_CURVE('',#87,(#93,#99),.CURVE_3D.); +#101=EDGE_CURVE('',#33,#41,#100,.T.); +#102=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#103=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#104=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#105=AXIS2_PLACEMENT_3D('',#102,#103,#104); +#106=CIRCLE('',#105,2.00000000000000000E0); +#107=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#108=DIRECTION('',(2.00000000000000000E0,0.00000000000000000E0)); +#109=AXIS2_PLACEMENT_2D('',#107,#108); +#110=CIRCLE('',#109,2.00000000000000000E0); +#111=DEFINITIONAL_REPRESENTATION('',(#110),#1); +#112=PCURVE('',#11,#111); +#113=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#114=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#115=VECTOR('',#114,1.00000000000000000E0); +#116=LINE('',#113,#115); +#117=DEFINITIONAL_REPRESENTATION('',(#116),#1); +#118=PCURVE('',#21,#117); +#119=SURFACE_CURVE('',#106,(#112,#118),.CURVE_3D.); +#120=EDGE_CURVE('',#35,#37,#119,.T.); +#121=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#122=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#123=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#124=AXIS2_PLACEMENT_3D('',#121,#122,#123); +#125=CIRCLE('',#124,2.00000000000000000E0); +#126=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#127=DIRECTION('',(2.00000000000000000E0,0.00000000000000000E0)); +#128=AXIS2_PLACEMENT_2D('',#126,#127); +#129=CIRCLE('',#128,2.00000000000000000E0); +#130=DEFINITIONAL_REPRESENTATION('',(#129),#1); +#131=PCURVE('',#6,#130); +#132=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#133=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#134=VECTOR('',#133,1.00000000000000000E0); +#135=LINE('',#132,#134); +#136=DEFINITIONAL_REPRESENTATION('',(#135),#1); +#137=PCURVE('',#21,#136); +#138=SURFACE_CURVE('',#125,(#131,#137),.CURVE_3D.); +#139=EDGE_CURVE('',#43,#45,#138,.T.); +#140=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,2.00000000000000000E0)); +#141=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#142=VECTOR('',#141,1.00000000000000000E0); +#143=LINE('',#140,#142); +#144=CARTESIAN_POINT('',(4.00000000000000000E0,0.00000000000000000E0)); +#145=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#146=VECTOR('',#145,1.00000000000000000E0); +#147=LINE('',#144,#146); +#148=DEFINITIONAL_REPRESENTATION('',(#147),#1); +#149=PCURVE('',#16,#148); +#150=CARTESIAN_POINT('',(-1.57079632679489656E0,0.00000000000000000E0)); +#151=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#152=VECTOR('',#151,1.00000000000000000E0); +#153=LINE('',#150,#152); +#154=DEFINITIONAL_REPRESENTATION('',(#153),#1); +#155=PCURVE('',#21,#154); +#156=SURFACE_CURVE('',#143,(#149,#155),.CURVE_3D.); +#157=EDGE_CURVE('',#35,#43,#156,.T.); +#158=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#159=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#160=VECTOR('',#159,1.00000000000000000E0); +#161=LINE('',#158,#160); +#162=CARTESIAN_POINT('',(2.00000000000000000E0,-2.00000000000000000E0)); +#163=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#164=VECTOR('',#163,1.00000000000000000E0); +#165=LINE('',#162,#164); +#166=DEFINITIONAL_REPRESENTATION('',(#165),#1); +#167=PCURVE('',#11,#166); +#168=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#169=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#170=VECTOR('',#169,1.00000000000000000E0); +#171=LINE('',#168,#170); +#172=DEFINITIONAL_REPRESENTATION('',(#171),#1); +#173=PCURVE('',#26,#172); +#174=SURFACE_CURVE('',#161,(#167,#173),.CURVE_3D.); +#175=EDGE_CURVE('',#37,#39,#174,.T.); +#176=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#177=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#178=VECTOR('',#177,1.00000000000000000E0); +#179=LINE('',#176,#178); +#180=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#181=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#182=VECTOR('',#181,1.00000000000000000E0); +#183=LINE('',#180,#182); +#184=DEFINITIONAL_REPRESENTATION('',(#183),#1); +#185=PCURVE('',#6,#184); +#186=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#187=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#188=VECTOR('',#187,1.00000000000000000E0); +#189=LINE('',#186,#188); +#190=DEFINITIONAL_REPRESENTATION('',(#189),#1); +#191=PCURVE('',#26,#190); +#192=SURFACE_CURVE('',#179,(#185,#191),.CURVE_3D.); +#193=EDGE_CURVE('',#45,#47,#192,.T.); +#194=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#195=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#196=VECTOR('',#195,1.00000000000000000E0); +#197=LINE('',#194,#196); +#198=CARTESIAN_POINT('',(1.57079632679489656E0,0.00000000000000000E0)); +#199=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#200=VECTOR('',#199,1.00000000000000000E0); +#201=LINE('',#198,#200); +#202=DEFINITIONAL_REPRESENTATION('',(#201),#1); +#203=PCURVE('',#21,#202); +#204=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#205=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#206=VECTOR('',#205,1.00000000000000000E0); +#207=LINE('',#204,#206); +#208=DEFINITIONAL_REPRESENTATION('',(#207),#1); +#209=PCURVE('',#26,#208); +#210=SURFACE_CURVE('',#197,(#203,#209),.CURVE_3D.); +#211=EDGE_CURVE('',#37,#45,#210,.T.); +#212=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#213=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#214=VECTOR('',#213,1.00000000000000000E0); +#215=LINE('',#212,#214); +#216=CARTESIAN_POINT('',(-2.00000000000000000E0,-2.00000000000000000E0)); +#217=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#218=VECTOR('',#217,1.00000000000000000E0); +#219=LINE('',#216,#218); +#220=DEFINITIONAL_REPRESENTATION('',(#219),#1); +#221=PCURVE('',#11,#220); +#222=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#223=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#224=VECTOR('',#223,1.00000000000000000E0); +#225=LINE('',#222,#224); +#226=DEFINITIONAL_REPRESENTATION('',(#225),#1); +#227=PCURVE('',#31,#226); +#228=SURFACE_CURVE('',#215,(#221,#227),.CURVE_3D.); +#229=EDGE_CURVE('',#39,#33,#228,.T.); +#230=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#231=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#232=VECTOR('',#231,1.00000000000000000E0); +#233=LINE('',#230,#232); +#234=CARTESIAN_POINT('',(-2.00000000000000000E0,2.00000000000000000E0)); +#235=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#236=VECTOR('',#235,1.00000000000000000E0); +#237=LINE('',#234,#236); +#238=DEFINITIONAL_REPRESENTATION('',(#237),#1); +#239=PCURVE('',#6,#238); +#240=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#241=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#242=VECTOR('',#241,1.00000000000000000E0); +#243=LINE('',#240,#242); +#244=DEFINITIONAL_REPRESENTATION('',(#243),#1); +#245=PCURVE('',#31,#244); +#246=SURFACE_CURVE('',#233,(#239,#245),.CURVE_3D.); +#247=EDGE_CURVE('',#47,#41,#246,.T.); +#248=CARTESIAN_POINT('',(-2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#249=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#250=VECTOR('',#249,1.00000000000000000E0); +#251=LINE('',#248,#250); +#252=CARTESIAN_POINT('',(4.00000000000000000E0,0.00000000000000000E0)); +#253=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#254=VECTOR('',#253,1.00000000000000000E0); +#255=LINE('',#252,#254); +#256=DEFINITIONAL_REPRESENTATION('',(#255),#1); +#257=PCURVE('',#26,#256); +#258=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#259=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#260=VECTOR('',#259,1.00000000000000000E0); +#261=LINE('',#258,#260); +#262=DEFINITIONAL_REPRESENTATION('',(#261),#1); +#263=PCURVE('',#31,#262); +#264=SURFACE_CURVE('',#251,(#257,#263),.CURVE_3D.); +#265=EDGE_CURVE('',#39,#47,#264,.T.); +#266=ORIENTED_EDGE('',*,*,#83,.T.); +#267=ORIENTED_EDGE('',*,*,#139,.T.); +#268=ORIENTED_EDGE('',*,*,#193,.T.); +#269=ORIENTED_EDGE('',*,*,#247,.T.); +#270=EDGE_LOOP('',(#266,#267,#268,#269)); +#271=FACE_OUTER_BOUND('',#270,.T.); +#272=ADVANCED_FACE('top',(#271),#6,.T.); +#273=ORIENTED_EDGE('',*,*,#229,.F.); +#274=ORIENTED_EDGE('',*,*,#175,.F.); +#275=ORIENTED_EDGE('',*,*,#120,.F.); +#276=ORIENTED_EDGE('',*,*,#65,.F.); +#277=EDGE_LOOP('',(#273,#274,#275,#276)); +#278=FACE_OUTER_BOUND('',#277,.T.); +#279=ADVANCED_FACE('bottom',(#278),#11,.T.); +#280=ORIENTED_EDGE('',*,*,#65,.T.); +#281=ORIENTED_EDGE('',*,*,#157,.T.); +#282=ORIENTED_EDGE('',*,*,#83,.F.); +#283=ORIENTED_EDGE('',*,*,#101,.F.); +#284=EDGE_LOOP('',(#280,#281,#282,#283)); +#285=FACE_OUTER_BOUND('',#284,.T.); +#286=ADVANCED_FACE('side-0-0',(#285),#16,.T.); +#287=ORIENTED_EDGE('',*,*,#120,.T.); +#288=ORIENTED_EDGE('',*,*,#211,.T.); +#289=ORIENTED_EDGE('',*,*,#139,.F.); +#290=ORIENTED_EDGE('',*,*,#157,.F.); +#291=EDGE_LOOP('',(#287,#288,#289,#290)); +#292=FACE_OUTER_BOUND('',#291,.T.); +#293=ADVANCED_FACE('side-0-1',(#292),#21,.T.); +#294=ORIENTED_EDGE('',*,*,#175,.T.); +#295=ORIENTED_EDGE('',*,*,#265,.T.); +#296=ORIENTED_EDGE('',*,*,#193,.F.); +#297=ORIENTED_EDGE('',*,*,#211,.F.); +#298=EDGE_LOOP('',(#294,#295,#296,#297)); +#299=FACE_OUTER_BOUND('',#298,.T.); +#300=ADVANCED_FACE('side-0-2',(#299),#26,.T.); +#301=ORIENTED_EDGE('',*,*,#229,.T.); +#302=ORIENTED_EDGE('',*,*,#101,.T.); +#303=ORIENTED_EDGE('',*,*,#247,.F.); +#304=ORIENTED_EDGE('',*,*,#265,.F.); +#305=EDGE_LOOP('',(#301,#302,#303,#304)); +#306=FACE_OUTER_BOUND('',#305,.T.); +#307=ADVANCED_FACE('side-0-3',(#306),#31,.T.); +#308=CLOSED_SHELL('',(#272,#279,#286,#293,#300,#307)); +#309=MANIFOLD_SOLID_BREP('arc-edged-extrusion-0',#308); +#310=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#311=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#312=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#313=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000568434190033E-8),#310,'distance_accuracy_value','exchange error bound'); +#314=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#313)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#310,#311,#312)) REPRESENTATION_CONTEXT('','')); +#315=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#309),#314); +#316=APPLICATION_CONTEXT('automotive design'); +#317=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#316); +#318=PRODUCT_CONTEXT('',#316,'mechanical'); +#319=PRODUCT('arc-edged-extrusion','arc-edged-extrusion','',(#318)); +#320=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#319,.NOT_KNOWN.); +#321=PRODUCT_DEFINITION_CONTEXT('part definition',#316,'design'); +#322=PRODUCT_DEFINITION('','',#320,#321); +#323=PRODUCT_DEFINITION_SHAPE('','',#322); +#324=SHAPE_DEFINITION_REPRESENTATION(#323,#315); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.m.report.json b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.m.report.json new file mode 100644 index 0000000..2675cf2 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":12,"exchange_error_bound":1.0000056843419003e-8,"faces":6,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.mm b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.mm new file mode 100644 index 0000000..39c1444 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.mm @@ -0,0 +1,333 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('arc-edged-extrusion','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#13=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=CYLINDRICAL_SURFACE('',#20,2.00000000000000000E3); +#22=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#23=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#24=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#28=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#33=VERTEX_POINT('',#32); +#34=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#35=VERTEX_POINT('',#34); +#36=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#37=VERTEX_POINT('',#36); +#38=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#39=VERTEX_POINT('',#38); +#40=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3,2.00000000000000000E3)); +#41=VERTEX_POINT('',#40); +#42=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,2.00000000000000000E3)); +#43=VERTEX_POINT('',#42); +#44=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#45=VERTEX_POINT('',#44); +#46=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#47=VERTEX_POINT('',#46); +#48=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#49=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#50=VECTOR('',#49,1.00000000000000000E3); +#51=LINE('',#48,#50); +#52=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3)); +#53=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#54=VECTOR('',#53,1.00000000000000000E3); +#55=LINE('',#52,#54); +#56=DEFINITIONAL_REPRESENTATION('',(#55),#1); +#57=PCURVE('',#11,#56); +#58=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#59=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#60=VECTOR('',#59,1.00000000000000000E3); +#61=LINE('',#58,#60); +#62=DEFINITIONAL_REPRESENTATION('',(#61),#1); +#63=PCURVE('',#16,#62); +#64=SURFACE_CURVE('',#51,(#57,#63),.CURVE_3D.); +#65=EDGE_CURVE('',#33,#35,#64,.T.); +#66=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3,2.00000000000000000E3)); +#67=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#68=VECTOR('',#67,1.00000000000000000E3); +#69=LINE('',#66,#68); +#70=CARTESIAN_POINT('',(-2.00000000000000000E3,-2.00000000000000000E3)); +#71=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#72=VECTOR('',#71,1.00000000000000000E3); +#73=LINE('',#70,#72); +#74=DEFINITIONAL_REPRESENTATION('',(#73),#1); +#75=PCURVE('',#6,#74); +#76=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#77=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#78=VECTOR('',#77,1.00000000000000000E3); +#79=LINE('',#76,#78); +#80=DEFINITIONAL_REPRESENTATION('',(#79),#1); +#81=PCURVE('',#16,#80); +#82=SURFACE_CURVE('',#69,(#75,#81),.CURVE_3D.); +#83=EDGE_CURVE('',#41,#43,#82,.T.); +#84=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#85=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#86=VECTOR('',#85,1.00000000000000000E3); +#87=LINE('',#84,#86); +#88=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#90=VECTOR('',#89,1.00000000000000000E3); +#91=LINE('',#88,#90); +#92=DEFINITIONAL_REPRESENTATION('',(#91),#1); +#93=PCURVE('',#16,#92); +#94=CARTESIAN_POINT('',(4.00000000000000000E3,0.00000000000000000E0)); +#95=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#96=VECTOR('',#95,1.00000000000000000E3); +#97=LINE('',#94,#96); +#98=DEFINITIONAL_REPRESENTATION('',(#97),#1); +#99=PCURVE('',#31,#98); +#100=SURFACE_CURVE('',#87,(#93,#99),.CURVE_3D.); +#101=EDGE_CURVE('',#33,#41,#100,.T.); +#102=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#103=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#104=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#105=AXIS2_PLACEMENT_3D('',#102,#103,#104); +#106=CIRCLE('',#105,2.00000000000000000E3); +#107=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#108=DIRECTION('',(2.00000000000000000E3,0.00000000000000000E0)); +#109=AXIS2_PLACEMENT_2D('',#107,#108); +#110=CIRCLE('',#109,2.00000000000000000E3); +#111=DEFINITIONAL_REPRESENTATION('',(#110),#1); +#112=PCURVE('',#11,#111); +#113=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#114=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#115=VECTOR('',#114,1.00000000000000000E0); +#116=LINE('',#113,#115); +#117=DEFINITIONAL_REPRESENTATION('',(#116),#1); +#118=PCURVE('',#21,#117); +#119=SURFACE_CURVE('',#106,(#112,#118),.CURVE_3D.); +#120=EDGE_CURVE('',#35,#37,#119,.T.); +#121=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#122=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#123=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#124=AXIS2_PLACEMENT_3D('',#121,#122,#123); +#125=CIRCLE('',#124,2.00000000000000000E3); +#126=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#127=DIRECTION('',(2.00000000000000000E3,0.00000000000000000E0)); +#128=AXIS2_PLACEMENT_2D('',#126,#127); +#129=CIRCLE('',#128,2.00000000000000000E3); +#130=DEFINITIONAL_REPRESENTATION('',(#129),#1); +#131=PCURVE('',#6,#130); +#132=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#133=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#134=VECTOR('',#133,1.00000000000000000E0); +#135=LINE('',#132,#134); +#136=DEFINITIONAL_REPRESENTATION('',(#135),#1); +#137=PCURVE('',#21,#136); +#138=SURFACE_CURVE('',#125,(#131,#137),.CURVE_3D.); +#139=EDGE_CURVE('',#43,#45,#138,.T.); +#140=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,2.00000000000000000E3)); +#141=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#142=VECTOR('',#141,1.00000000000000000E3); +#143=LINE('',#140,#142); +#144=CARTESIAN_POINT('',(4.00000000000000000E3,0.00000000000000000E0)); +#145=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#146=VECTOR('',#145,1.00000000000000000E3); +#147=LINE('',#144,#146); +#148=DEFINITIONAL_REPRESENTATION('',(#147),#1); +#149=PCURVE('',#16,#148); +#150=CARTESIAN_POINT('',(-1.57079632679489656E0,0.00000000000000000E0)); +#151=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#152=VECTOR('',#151,1.00000000000000000E3); +#153=LINE('',#150,#152); +#154=DEFINITIONAL_REPRESENTATION('',(#153),#1); +#155=PCURVE('',#21,#154); +#156=SURFACE_CURVE('',#143,(#149,#155),.CURVE_3D.); +#157=EDGE_CURVE('',#35,#43,#156,.T.); +#158=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#159=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#160=VECTOR('',#159,1.00000000000000000E3); +#161=LINE('',#158,#160); +#162=CARTESIAN_POINT('',(2.00000000000000000E3,-2.00000000000000000E3)); +#163=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#164=VECTOR('',#163,1.00000000000000000E3); +#165=LINE('',#162,#164); +#166=DEFINITIONAL_REPRESENTATION('',(#165),#1); +#167=PCURVE('',#11,#166); +#168=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#169=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#170=VECTOR('',#169,1.00000000000000000E3); +#171=LINE('',#168,#170); +#172=DEFINITIONAL_REPRESENTATION('',(#171),#1); +#173=PCURVE('',#26,#172); +#174=SURFACE_CURVE('',#161,(#167,#173),.CURVE_3D.); +#175=EDGE_CURVE('',#37,#39,#174,.T.); +#176=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#177=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#178=VECTOR('',#177,1.00000000000000000E3); +#179=LINE('',#176,#178); +#180=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#181=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#182=VECTOR('',#181,1.00000000000000000E3); +#183=LINE('',#180,#182); +#184=DEFINITIONAL_REPRESENTATION('',(#183),#1); +#185=PCURVE('',#6,#184); +#186=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#187=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#188=VECTOR('',#187,1.00000000000000000E3); +#189=LINE('',#186,#188); +#190=DEFINITIONAL_REPRESENTATION('',(#189),#1); +#191=PCURVE('',#26,#190); +#192=SURFACE_CURVE('',#179,(#185,#191),.CURVE_3D.); +#193=EDGE_CURVE('',#45,#47,#192,.T.); +#194=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#195=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#196=VECTOR('',#195,1.00000000000000000E3); +#197=LINE('',#194,#196); +#198=CARTESIAN_POINT('',(1.57079632679489656E0,0.00000000000000000E0)); +#199=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#200=VECTOR('',#199,1.00000000000000000E3); +#201=LINE('',#198,#200); +#202=DEFINITIONAL_REPRESENTATION('',(#201),#1); +#203=PCURVE('',#21,#202); +#204=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#205=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#206=VECTOR('',#205,1.00000000000000000E3); +#207=LINE('',#204,#206); +#208=DEFINITIONAL_REPRESENTATION('',(#207),#1); +#209=PCURVE('',#26,#208); +#210=SURFACE_CURVE('',#197,(#203,#209),.CURVE_3D.); +#211=EDGE_CURVE('',#37,#45,#210,.T.); +#212=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#213=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#214=VECTOR('',#213,1.00000000000000000E3); +#215=LINE('',#212,#214); +#216=CARTESIAN_POINT('',(-2.00000000000000000E3,-2.00000000000000000E3)); +#217=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#218=VECTOR('',#217,1.00000000000000000E3); +#219=LINE('',#216,#218); +#220=DEFINITIONAL_REPRESENTATION('',(#219),#1); +#221=PCURVE('',#11,#220); +#222=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#223=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#224=VECTOR('',#223,1.00000000000000000E3); +#225=LINE('',#222,#224); +#226=DEFINITIONAL_REPRESENTATION('',(#225),#1); +#227=PCURVE('',#31,#226); +#228=SURFACE_CURVE('',#215,(#221,#227),.CURVE_3D.); +#229=EDGE_CURVE('',#39,#33,#228,.T.); +#230=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#231=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#232=VECTOR('',#231,1.00000000000000000E3); +#233=LINE('',#230,#232); +#234=CARTESIAN_POINT('',(-2.00000000000000000E3,2.00000000000000000E3)); +#235=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#236=VECTOR('',#235,1.00000000000000000E3); +#237=LINE('',#234,#236); +#238=DEFINITIONAL_REPRESENTATION('',(#237),#1); +#239=PCURVE('',#6,#238); +#240=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#241=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#242=VECTOR('',#241,1.00000000000000000E3); +#243=LINE('',#240,#242); +#244=DEFINITIONAL_REPRESENTATION('',(#243),#1); +#245=PCURVE('',#31,#244); +#246=SURFACE_CURVE('',#233,(#239,#245),.CURVE_3D.); +#247=EDGE_CURVE('',#47,#41,#246,.T.); +#248=CARTESIAN_POINT('',(-2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#249=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#250=VECTOR('',#249,1.00000000000000000E3); +#251=LINE('',#248,#250); +#252=CARTESIAN_POINT('',(4.00000000000000000E3,0.00000000000000000E0)); +#253=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#254=VECTOR('',#253,1.00000000000000000E3); +#255=LINE('',#252,#254); +#256=DEFINITIONAL_REPRESENTATION('',(#255),#1); +#257=PCURVE('',#26,#256); +#258=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#259=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#260=VECTOR('',#259,1.00000000000000000E3); +#261=LINE('',#258,#260); +#262=DEFINITIONAL_REPRESENTATION('',(#261),#1); +#263=PCURVE('',#31,#262); +#264=SURFACE_CURVE('',#251,(#257,#263),.CURVE_3D.); +#265=EDGE_CURVE('',#39,#47,#264,.T.); +#266=ORIENTED_EDGE('',*,*,#83,.T.); +#267=ORIENTED_EDGE('',*,*,#139,.T.); +#268=ORIENTED_EDGE('',*,*,#193,.T.); +#269=ORIENTED_EDGE('',*,*,#247,.T.); +#270=EDGE_LOOP('',(#266,#267,#268,#269)); +#271=FACE_OUTER_BOUND('',#270,.T.); +#272=ADVANCED_FACE('top',(#271),#6,.T.); +#273=ORIENTED_EDGE('',*,*,#229,.F.); +#274=ORIENTED_EDGE('',*,*,#175,.F.); +#275=ORIENTED_EDGE('',*,*,#120,.F.); +#276=ORIENTED_EDGE('',*,*,#65,.F.); +#277=EDGE_LOOP('',(#273,#274,#275,#276)); +#278=FACE_OUTER_BOUND('',#277,.T.); +#279=ADVANCED_FACE('bottom',(#278),#11,.T.); +#280=ORIENTED_EDGE('',*,*,#65,.T.); +#281=ORIENTED_EDGE('',*,*,#157,.T.); +#282=ORIENTED_EDGE('',*,*,#83,.F.); +#283=ORIENTED_EDGE('',*,*,#101,.F.); +#284=EDGE_LOOP('',(#280,#281,#282,#283)); +#285=FACE_OUTER_BOUND('',#284,.T.); +#286=ADVANCED_FACE('side-0-0',(#285),#16,.T.); +#287=ORIENTED_EDGE('',*,*,#120,.T.); +#288=ORIENTED_EDGE('',*,*,#211,.T.); +#289=ORIENTED_EDGE('',*,*,#139,.F.); +#290=ORIENTED_EDGE('',*,*,#157,.F.); +#291=EDGE_LOOP('',(#287,#288,#289,#290)); +#292=FACE_OUTER_BOUND('',#291,.T.); +#293=ADVANCED_FACE('side-0-1',(#292),#21,.T.); +#294=ORIENTED_EDGE('',*,*,#175,.T.); +#295=ORIENTED_EDGE('',*,*,#265,.T.); +#296=ORIENTED_EDGE('',*,*,#193,.F.); +#297=ORIENTED_EDGE('',*,*,#211,.F.); +#298=EDGE_LOOP('',(#294,#295,#296,#297)); +#299=FACE_OUTER_BOUND('',#298,.T.); +#300=ADVANCED_FACE('side-0-2',(#299),#26,.T.); +#301=ORIENTED_EDGE('',*,*,#229,.T.); +#302=ORIENTED_EDGE('',*,*,#101,.T.); +#303=ORIENTED_EDGE('',*,*,#247,.F.); +#304=ORIENTED_EDGE('',*,*,#265,.F.); +#305=EDGE_LOOP('',(#301,#302,#303,#304)); +#306=FACE_OUTER_BOUND('',#305,.T.); +#307=ADVANCED_FACE('side-0-3',(#306),#31,.T.); +#308=CLOSED_SHELL('',(#272,#279,#286,#293,#300,#307)); +#309=MANIFOLD_SOLID_BREP('arc-edged-extrusion-0',#308); +#310=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#311=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#312=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#313=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000568434190039E-5),#310,'distance_accuracy_value','exchange error bound'); +#314=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#313)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#310,#311,#312)) REPRESENTATION_CONTEXT('','')); +#315=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#309),#314); +#316=APPLICATION_CONTEXT('automotive design'); +#317=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#316); +#318=PRODUCT_CONTEXT('',#316,'mechanical'); +#319=PRODUCT('arc-edged-extrusion','arc-edged-extrusion','',(#318)); +#320=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#319,.NOT_KNOWN.); +#321=PRODUCT_DEFINITION_CONTEXT('part definition',#316,'design'); +#322=PRODUCT_DEFINITION('','',#320,#321); +#323=PRODUCT_DEFINITION_SHAPE('','',#322); +#324=SHAPE_DEFINITION_REPRESENTATION(#323,#315); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.mm.report.json new file mode 100644 index 0000000..1ade8f0 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":12,"exchange_error_bound":0.000010000056843419004,"faces":6,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.tess.json b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.tess.json new file mode 100644 index 0000000..2d24eae --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/arc-edged-extrusion.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[24,25,0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,10,10,11,12,12,13,14,14,15,16,16,17,18,18,19,20,20,21,22,22,23,24,24,0,2,2,4,6,6,8,10,10,12,14,14,16,18,18,20,22,22,24,2,2,6,10,10,14,18,18,22,2,2,10,18,50,51,26,26,27,28,28,29,30,30,31,32,32,33,34,34,35,36,36,37,38,38,39,40,40,41,42,42,43,44,44,45,46,46,47,48,48,49,50,50,26,28,28,30,32,32,34,36,36,38,40,40,42,44,44,46,48,48,50,28,28,32,36,36,40,44,44,48,28,28,36,44,54,55,52,52,53,54,105,106,154,105,154,153,106,107,155,106,155,154,107,108,156,107,156,155,108,109,157,108,157,156,109,110,158,109,158,157,110,111,159,110,159,158,111,112,160,111,160,159,112,113,161,112,161,160,113,114,162,113,162,161,114,115,163,114,163,162,115,116,164,115,164,163,116,117,165,116,165,164,117,118,166,117,166,165,118,119,167,118,167,166,119,120,168,119,168,167,120,121,169,120,169,168,121,122,170,121,170,169,122,123,171,122,171,170,123,124,172,123,172,171,124,125,173,124,173,172,125,126,174,125,174,173,126,127,175,126,175,174,127,128,176,127,176,175,128,129,177,128,177,176,129,130,178,129,178,177,130,131,179,130,179,178,131,132,180,131,180,179,132,133,181,132,181,180,133,134,182,133,182,181,134,135,183,134,183,182,135,136,184,135,184,183,136,137,185,136,185,184,137,138,186,137,186,185,138,139,187,138,187,186,139,140,188,139,188,187,140,141,189,140,189,188,141,142,190,141,190,189,142,143,191,142,191,190,143,144,192,143,192,191,144,145,193,144,193,192,145,146,194,145,194,193,146,147,195,146,195,194,147,148,196,147,196,195,148,149,197,148,197,196,149,150,198,149,198,197,250,251,248,248,249,250,254,255,252,252,253,254],"normals":[0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,-4.999999969612645e-9,0.0,1.0,6.123234262925839e-17,0.0,1.0,0.13616664707660675,0.0,0.9906859397888184,0.13616664707660675,0.0,0.9906859397888184,0.269796758890152,0.0,0.9629172682762146,0.269796758890152,0.0,0.9629172682762146,0.39840108156204224,0.0,0.9172112941741943,0.39840108156204224,0.0,0.9172112941741943,0.5195839405059814,0.0,0.8544194102287292,0.5195839405059814,0.0,0.8544194102287292,0.6310879588127136,0.0,0.7757112979888916,0.6310879588127136,0.0,0.7757112979888916,0.7308359742164612,0.0,0.6825531721115112,0.7308359742164612,0.0,0.6825531721115112,0.8169698715209961,0.0,0.5766803026199341,0.8169698715209961,0.0,0.5766803026199341,0.8878852128982544,0.0,0.4600650370121002,0.8878852128982544,0.0,0.4600650370121002,0.9422609210014343,0.0,0.334879606962204,0.9422609210014343,0.0,0.334879606962204,0.9790840744972229,0.0,0.20345601439476013,0.9790840744972229,0.0,0.20345601439476013,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9790840744972229,0.0,-0.20345601439476013,0.9790840744972229,0.0,-0.20345601439476013,0.9422609210014343,0.0,-0.334879606962204,0.9422609210014343,0.0,-0.334879606962204,0.8878852128982544,0.0,-0.4600650370121002,0.8878852128982544,0.0,-0.4600650370121002,0.8169698715209961,0.0,-0.5766803026199341,0.8169698715209961,0.0,-0.5766803026199341,0.7308359742164612,0.0,-0.6825531721115112,0.7308359742164612,0.0,-0.6825531721115112,0.6310879588127136,0.0,-0.7757112979888916,0.6310879588127136,0.0,-0.7757112979888916,0.5195839405059814,0.0,-0.8544194102287292,0.5195839405059814,0.0,-0.8544194102287292,0.39840108156204224,0.0,-0.9172112941741943,0.39840108156204224,0.0,-0.9172112941741943,0.269796758890152,0.0,-0.9629172682762146,0.269796758890152,0.0,-0.9629172682762146,0.13616664707660675,0.0,-0.9906859397888184,0.13616664707660675,0.0,-0.9906859397888184,6.123234262925839e-17,0.0,-1.0,-4.999999969612645e-9,0.0,-1.0,-4.999999969612645e-9,0.0,1.0,6.123234262925839e-17,0.0,1.0,0.13616664707660675,0.0,0.9906859397888184,0.13616664707660675,0.0,0.9906859397888184,0.269796758890152,0.0,0.9629172682762146,0.269796758890152,0.0,0.9629172682762146,0.39840108156204224,0.0,0.9172112941741943,0.39840108156204224,0.0,0.9172112941741943,0.5195839405059814,0.0,0.8544194102287292,0.5195839405059814,0.0,0.8544194102287292,0.6310879588127136,0.0,0.7757112979888916,0.6310879588127136,0.0,0.7757112979888916,0.7308359742164612,0.0,0.6825531721115112,0.7308359742164612,0.0,0.6825531721115112,0.8169698715209961,0.0,0.5766803026199341,0.8169698715209961,0.0,0.5766803026199341,0.8878852128982544,0.0,0.4600650370121002,0.8878852128982544,0.0,0.4600650370121002,0.9422609210014343,0.0,0.334879606962204,0.9422609210014343,0.0,0.334879606962204,0.9790840744972229,0.0,0.20345601439476013,0.9790840744972229,0.0,0.20345601439476013,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9790840744972229,0.0,-0.20345601439476013,0.9790840744972229,0.0,-0.20345601439476013,0.9422609210014343,0.0,-0.334879606962204,0.9422609210014343,0.0,-0.334879606962204,0.8878852128982544,0.0,-0.4600650370121002,0.8878852128982544,0.0,-0.4600650370121002,0.8169698715209961,0.0,-0.5766803026199341,0.8169698715209961,0.0,-0.5766803026199341,0.7308359742164612,0.0,-0.6825531721115112,0.7308359742164612,0.0,-0.6825531721115112,0.6310879588127136,0.0,-0.7757112979888916,0.6310879588127136,0.0,-0.7757112979888916,0.5195839405059814,0.0,-0.8544194102287292,0.5195839405059814,0.0,-0.8544194102287292,0.39840108156204224,0.0,-0.9172112941741943,0.39840108156204224,0.0,-0.9172112941741943,0.269796758890152,0.0,-0.9629172682762146,0.269796758890152,0.0,-0.9629172682762146,0.13616664707660675,0.0,-0.9906859397888184,0.13616664707660675,0.0,-0.9906859397888184,6.123234262925839e-17,0.0,-1.0,-4.999999969612645e-9,0.0,-1.0,-4.999999969612645e-9,0.0,1.0,6.123234262925839e-17,0.0,1.0,0.13616664707660675,0.0,0.9906859397888184,0.13616664707660675,0.0,0.9906859397888184,0.269796758890152,0.0,0.9629172682762146,0.269796758890152,0.0,0.9629172682762146,0.39840108156204224,0.0,0.9172112941741943,0.39840108156204224,0.0,0.9172112941741943,0.5195839405059814,0.0,0.8544194102287292,0.5195839405059814,0.0,0.8544194102287292,0.6310879588127136,0.0,0.7757112979888916,0.6310879588127136,0.0,0.7757112979888916,0.7308359742164612,0.0,0.6825531721115112,0.7308359742164612,0.0,0.6825531721115112,0.8169698715209961,0.0,0.5766803026199341,0.8169698715209961,0.0,0.5766803026199341,0.8878852128982544,0.0,0.4600650370121002,0.8878852128982544,0.0,0.4600650370121002,0.9422609210014343,0.0,0.334879606962204,0.9422609210014343,0.0,0.334879606962204,0.9790840744972229,0.0,0.20345601439476013,0.9790840744972229,0.0,0.20345601439476013,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9790840744972229,0.0,-0.20345601439476013,0.9790840744972229,0.0,-0.20345601439476013,0.9422609210014343,0.0,-0.334879606962204,0.9422609210014343,0.0,-0.334879606962204,0.8878852128982544,0.0,-0.4600650370121002,0.8878852128982544,0.0,-0.4600650370121002,0.8169698715209961,0.0,-0.5766803026199341,0.8169698715209961,0.0,-0.5766803026199341,0.7308359742164612,0.0,-0.6825531721115112,0.7308359742164612,0.0,-0.6825531721115112,0.6310879588127136,0.0,-0.7757112979888916,0.6310879588127136,0.0,-0.7757112979888916,0.5195839405059814,0.0,-0.8544194102287292,0.5195839405059814,0.0,-0.8544194102287292,0.39840108156204224,0.0,-0.9172112941741943,0.39840108156204224,0.0,-0.9172112941741943,0.269796758890152,0.0,-0.9629172682762146,0.269796758890152,0.0,-0.9629172682762146,0.13616664707660675,0.0,-0.9906859397888184,0.13616664707660675,0.0,-0.9906859397888184,6.123234262925839e-17,0.0,-1.0,-4.999999969612645e-9,0.0,-1.0,-4.999999969612645e-9,0.0,1.0,6.123234262925839e-17,0.0,1.0,0.13616664707660675,0.0,0.9906859397888184,0.13616664707660675,0.0,0.9906859397888184,0.269796758890152,0.0,0.9629172682762146,0.269796758890152,0.0,0.9629172682762146,0.39840108156204224,0.0,0.9172112941741943,0.39840108156204224,0.0,0.9172112941741943,0.5195839405059814,0.0,0.8544194102287292,0.5195839405059814,0.0,0.8544194102287292,0.6310879588127136,0.0,0.7757112979888916,0.6310879588127136,0.0,0.7757112979888916,0.7308359742164612,0.0,0.6825531721115112,0.7308359742164612,0.0,0.6825531721115112,0.8169698715209961,0.0,0.5766803026199341,0.8169698715209961,0.0,0.5766803026199341,0.8878852128982544,0.0,0.4600650370121002,0.8878852128982544,0.0,0.4600650370121002,0.9422609210014343,0.0,0.334879606962204,0.9422609210014343,0.0,0.334879606962204,0.9790840744972229,0.0,0.20345601439476013,0.9790840744972229,0.0,0.20345601439476013,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9976687431335449,0.0,-0.06824241578578949,0.9790840744972229,0.0,-0.20345601439476013,0.9790840744972229,0.0,-0.20345601439476013,0.9422609210014343,0.0,-0.334879606962204,0.9422609210014343,0.0,-0.334879606962204,0.8878852128982544,0.0,-0.4600650370121002,0.8878852128982544,0.0,-0.4600650370121002,0.8169698715209961,0.0,-0.5766803026199341,0.8169698715209961,0.0,-0.5766803026199341,0.7308359742164612,0.0,-0.6825531721115112,0.7308359742164612,0.0,-0.6825531721115112,0.6310879588127136,0.0,-0.7757112979888916,0.6310879588127136,0.0,-0.7757112979888916,0.5195839405059814,0.0,-0.8544194102287292,0.5195839405059814,0.0,-0.8544194102287292,0.39840108156204224,0.0,-0.9172112941741943,0.39840108156204224,0.0,-0.9172112941741943,0.269796758890152,0.0,-0.9629172682762146,0.269796758890152,0.0,-0.9629172682762146,0.13616664707660675,0.0,-0.9906859397888184,0.13616664707660675,0.0,-0.9906859397888184,6.123234262925839e-17,0.0,-1.0,-4.999999969612645e-9,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0],"outlineEdgeIds":[0,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,6,7,8,9,10,11],"outlinePositions":[-2.0,0.0,2.0,2.0,0.0,2.0,-2.0,2.0,2.0,2.0,2.0,2.0,-2.0,0.0,2.0,-2.0,2.0,2.0,2.0,0.0,2.0,2.272333298192493,0.0,1.9813718920726615,2.272333298192493,0.0,1.9813718920726615,2.5395935423140483,0.0,1.9258345746955987,2.5395935423140483,0.0,1.9258345746955987,2.796802179692483,0.0,1.8344226030109059,2.796802179692483,0.0,1.8344226030109059,3.039167900070867,0.0,1.7088388090929771,3.039167900070867,0.0,1.7088388090929771,3.2621758886521057,0.0,1.5514225814088396,3.2621758886521057,0.0,1.5514225814088396,3.4616719285562483,0.0,1.3651062864373082,3.4616719285562483,0.0,1.3651062864373082,3.633939786020884,0.0,1.1533606442297342,3.633939786020884,0.0,1.1533606442297342,3.77577043680475,0.0,0.9201300754623043,3.77577043680475,0.0,0.9201300754623043,3.884521844237641,0.0,0.6697592243419724,3.884521844237641,0.0,0.6697592243419724,3.958168175364646,0.0,0.4069120261052674,3.958168175364646,0.0,0.4069120261052674,3.9953375383810785,0.0,0.13648482672934234,3.9953375383810785,0.0,0.13648482672934234,3.9953375383810785,0.0,-0.1364848267293419,3.9953375383810785,0.0,-0.1364848267293419,3.9581681753646456,0.0,-0.40691202610526783,3.9581681753646456,0.0,-0.40691202610526783,3.884521844237641,0.0,-0.6697592243419724,3.884521844237641,0.0,-0.6697592243419724,3.77577043680475,0.0,-0.9201300754623043,3.77577043680475,0.0,-0.9201300754623043,3.633939786020884,0.0,-1.1533606442297342,3.633939786020884,0.0,-1.1533606442297342,3.461671928556248,0.0,-1.3651062864373087,3.461671928556248,0.0,-1.3651062864373087,3.2621758886521057,0.0,-1.5514225814088394,3.2621758886521057,0.0,-1.5514225814088394,3.0391679000708676,0.0,-1.708838809092977,3.0391679000708676,0.0,-1.708838809092977,2.7968021796924827,0.0,-1.834422603010906,2.7968021796924827,0.0,-1.834422603010906,2.5395935423140483,0.0,-1.9258345746955987,2.5395935423140483,0.0,-1.9258345746955987,2.272333298192494,0.0,-1.9813718920726613,2.272333298192494,0.0,-1.9813718920726613,2.0,0.0,-2.0,2.0,2.0,2.0,2.272333298192493,2.0,1.9813718920726615,2.272333298192493,2.0,1.9813718920726615,2.5395935423140483,2.0,1.9258345746955987,2.5395935423140483,2.0,1.9258345746955987,2.796802179692483,2.0,1.8344226030109059,2.796802179692483,2.0,1.8344226030109059,3.039167900070867,2.0,1.7088388090929771,3.039167900070867,2.0,1.7088388090929771,3.2621758886521057,2.0,1.5514225814088396,3.2621758886521057,2.0,1.5514225814088396,3.4616719285562483,2.0,1.3651062864373082,3.4616719285562483,2.0,1.3651062864373082,3.633939786020884,2.0,1.1533606442297342,3.633939786020884,2.0,1.1533606442297342,3.77577043680475,2.0,0.9201300754623043,3.77577043680475,2.0,0.9201300754623043,3.884521844237641,2.0,0.6697592243419724,3.884521844237641,2.0,0.6697592243419724,3.958168175364646,2.0,0.4069120261052674,3.958168175364646,2.0,0.4069120261052674,3.9953375383810785,2.0,0.13648482672934234,3.9953375383810785,2.0,0.13648482672934234,3.9953375383810785,2.0,-0.1364848267293419,3.9953375383810785,2.0,-0.1364848267293419,3.9581681753646456,2.0,-0.40691202610526783,3.9581681753646456,2.0,-0.40691202610526783,3.884521844237641,2.0,-0.6697592243419724,3.884521844237641,2.0,-0.6697592243419724,3.77577043680475,2.0,-0.9201300754623043,3.77577043680475,2.0,-0.9201300754623043,3.633939786020884,2.0,-1.1533606442297342,3.633939786020884,2.0,-1.1533606442297342,3.461671928556248,2.0,-1.3651062864373087,3.461671928556248,2.0,-1.3651062864373087,3.2621758886521057,2.0,-1.5514225814088394,3.2621758886521057,2.0,-1.5514225814088394,3.0391679000708676,2.0,-1.708838809092977,3.0391679000708676,2.0,-1.708838809092977,2.7968021796924827,2.0,-1.834422603010906,2.7968021796924827,2.0,-1.834422603010906,2.5395935423140483,2.0,-1.9258345746955987,2.5395935423140483,2.0,-1.9258345746955987,2.272333298192494,2.0,-1.9813718920726613,2.272333298192494,2.0,-1.9813718920726613,2.0,2.0,-2.0,2.0,0.0,2.0,2.0,2.0,2.0,2.0,0.0,-2.0,-2.0,0.0,-2.0,2.0,2.0,-2.0,-2.0,2.0,-2.0,2.0,0.0,-2.0,2.0,2.0,-2.0,-2.0,0.0,-2.0,-2.0,0.0,2.0,-2.0,2.0,-2.0,-2.0,2.0,2.0,-2.0,0.0,-2.0,-2.0,2.0,-2.0],"positions":[-2.0,2.0,2.0,2.0,2.0,2.0,2.272333298192493,2.0,1.9813718920726615,2.5395935423140483,2.0,1.9258345746955987,2.796802179692483,2.0,1.8344226030109059,3.039167900070867,2.0,1.7088388090929771,3.2621758886521057,2.0,1.5514225814088396,3.4616719285562483,2.0,1.3651062864373082,3.633939786020884,2.0,1.1533606442297342,3.77577043680475,2.0,0.9201300754623043,3.884521844237641,2.0,0.6697592243419724,3.958168175364646,2.0,0.4069120261052674,3.9953375383810785,2.0,0.13648482672934234,3.9953375383810785,2.0,-0.1364848267293419,3.9581681753646456,2.0,-0.40691202610526783,3.884521844237641,2.0,-0.6697592243419724,3.77577043680475,2.0,-0.9201300754623043,3.633939786020884,2.0,-1.1533606442297342,3.461671928556248,2.0,-1.3651062864373087,3.2621758886521057,2.0,-1.5514225814088394,3.0391679000708676,2.0,-1.708838809092977,2.7968021796924827,2.0,-1.834422603010906,2.5395935423140483,2.0,-1.9258345746955987,2.272333298192494,2.0,-1.9813718920726613,2.0,2.0,-2.0,-2.0,2.0,-2.0,-2.0,0.0,2.0,-2.0,0.0,-2.0,2.0,0.0,-2.0,2.272333298192494,0.0,-1.9813718920726613,2.5395935423140483,0.0,-1.9258345746955987,2.7968021796924827,0.0,-1.834422603010906,3.0391679000708676,0.0,-1.708838809092977,3.2621758886521057,0.0,-1.5514225814088394,3.461671928556248,0.0,-1.3651062864373087,3.633939786020884,0.0,-1.1533606442297342,3.77577043680475,0.0,-0.9201300754623043,3.884521844237641,0.0,-0.6697592243419724,3.9581681753646456,0.0,-0.40691202610526783,3.9953375383810785,0.0,-0.1364848267293419,3.9953375383810785,0.0,0.13648482672934234,3.958168175364646,0.0,0.4069120261052674,3.884521844237641,0.0,0.6697592243419724,3.77577043680475,0.0,0.9201300754623043,3.633939786020884,0.0,1.1533606442297342,3.4616719285562483,0.0,1.3651062864373082,3.2621758886521057,0.0,1.5514225814088396,3.039167900070867,0.0,1.7088388090929771,2.796802179692483,0.0,1.8344226030109059,2.5395935423140483,0.0,1.9258345746955987,2.272333298192493,0.0,1.9813718920726615,2.0,0.0,2.0,-2.0,0.0,2.0,2.0,0.0,2.0,2.0,2.0,2.0,-2.0,2.0,2.0,1.9999999900000003,-1e-8,2.0,2.0,-1e-8,2.0,2.2723332891470998,-1e-8,1.9813718933159221,2.272333298192493,-1e-8,1.9813718920726615,2.5395935343595144,-1e-8,1.9258345769243546,2.5395935423140483,-1e-8,1.9258345746955987,2.796802172913095,-1e-8,1.8344226059556097,2.796802179692483,-1e-8,1.8344226030109059,3.0391678944985667,-1e-8,1.708838812481568,3.039167900070867,-1e-8,1.7088388090929771,3.2621758842676507,-1e-8,1.5514225849758583,3.2621758886521057,-1e-8,1.5514225814088396,3.4616719252918635,-1e-8,1.3651062899326107,3.4616719285562483,-1e-8,1.3651062864373082,3.633939783764309,-1e-8,1.153360647426573,3.633939786020884,-1e-8,1.1533606442297342,3.7757704354045525,-1e-8,0.9201300781645637,3.77577043680475,-1e-8,0.9201300754623043,3.884521843509642,-1e-8,0.6697592263903653,3.884521844237641,-1e-8,0.6697592243419724,3.958168175099268,-1e-8,0.4069120273823335,3.958168175364646,-1e-8,0.4069120261052674,3.995337538351408,-1e-8,0.1364848271631112,3.9953375383810785,-1e-8,0.13648482672934234,3.9953375383810785,-1e-8,-0.1364848267293419,3.995337538351408,-1e-8,-0.13648482716311075,3.9581681753646456,-1e-8,-0.40691202610526783,3.958168175099268,-1e-8,-0.40691202738233395,3.884521844237641,-1e-8,-0.6697592243419724,3.884521843509642,-1e-8,-0.6697592263903653,3.77577043680475,-1e-8,-0.9201300754623043,3.7757704354045525,-1e-8,-0.9201300781645637,3.633939786020884,-1e-8,-1.1533606442297342,3.6339397837643093,-1e-8,-1.1533606474265727,3.461671928556248,-1e-8,-1.3651062864373087,3.4616719252918635,-1e-8,-1.3651062899326107,3.2621758886521057,-1e-8,-1.5514225814088394,3.2621758842676503,-1e-8,-1.5514225849758587,3.0391679000708676,-1e-8,-1.708838809092977,3.0391678944985667,-1e-8,-1.708838812481568,2.7968021796924827,-1e-8,-1.834422603010906,2.796802172913095,-1e-8,-1.83442260595561,2.5395935423140483,-1e-8,-1.9258345746955987,2.5395935343595144,-1e-8,-1.9258345769243546,2.272333298192494,-1e-8,-1.9813718920726613,2.2723332891471,-1e-8,-1.9813718933159221,2.0,-1e-8,-2.0,1.9999999900000003,-1e-8,-2.0,1.9999999900000003,0.0,2.0,2.0,0.0,2.0,2.2723332891470998,0.0,1.9813718933159221,2.272333298192493,0.0,1.9813718920726615,2.5395935343595144,0.0,1.9258345769243546,2.5395935423140483,0.0,1.9258345746955987,2.796802172913095,0.0,1.8344226059556097,2.796802179692483,0.0,1.8344226030109059,3.0391678944985667,0.0,1.708838812481568,3.039167900070867,0.0,1.7088388090929771,3.2621758842676507,0.0,1.5514225849758583,3.2621758886521057,0.0,1.5514225814088396,3.4616719252918635,0.0,1.3651062899326107,3.4616719285562483,0.0,1.3651062864373082,3.633939783764309,0.0,1.153360647426573,3.633939786020884,0.0,1.1533606442297342,3.7757704354045525,0.0,0.9201300781645637,3.77577043680475,0.0,0.9201300754623043,3.884521843509642,0.0,0.6697592263903653,3.884521844237641,0.0,0.6697592243419724,3.958168175099268,0.0,0.4069120273823335,3.958168175364646,0.0,0.4069120261052674,3.995337538351408,0.0,0.1364848271631112,3.9953375383810785,0.0,0.13648482672934234,3.9953375383810785,0.0,-0.1364848267293419,3.995337538351408,0.0,-0.13648482716311075,3.9581681753646456,0.0,-0.40691202610526783,3.958168175099268,0.0,-0.40691202738233395,3.884521844237641,0.0,-0.6697592243419724,3.884521843509642,0.0,-0.6697592263903653,3.77577043680475,0.0,-0.9201300754623043,3.7757704354045525,0.0,-0.9201300781645637,3.633939786020884,0.0,-1.1533606442297342,3.6339397837643093,0.0,-1.1533606474265727,3.461671928556248,0.0,-1.3651062864373087,3.4616719252918635,0.0,-1.3651062899326107,3.2621758886521057,0.0,-1.5514225814088394,3.2621758842676503,0.0,-1.5514225849758587,3.0391679000708676,0.0,-1.708838809092977,3.0391678944985667,0.0,-1.708838812481568,2.7968021796924827,0.0,-1.834422603010906,2.796802172913095,0.0,-1.83442260595561,2.5395935423140483,0.0,-1.9258345746955987,2.5395935343595144,0.0,-1.9258345769243546,2.272333298192494,0.0,-1.9813718920726613,2.2723332891471,0.0,-1.9813718933159221,2.0,0.0,-2.0,1.9999999900000003,0.0,-2.0,1.9999999900000003,2.0,2.0,2.0,2.0,2.0,2.2723332891470998,2.0,1.9813718933159221,2.272333298192493,2.0,1.9813718920726615,2.5395935343595144,2.0,1.9258345769243546,2.5395935423140483,2.0,1.9258345746955987,2.796802172913095,2.0,1.8344226059556097,2.796802179692483,2.0,1.8344226030109059,3.0391678944985667,2.0,1.708838812481568,3.039167900070867,2.0,1.7088388090929771,3.2621758842676507,2.0,1.5514225849758583,3.2621758886521057,2.0,1.5514225814088396,3.4616719252918635,2.0,1.3651062899326107,3.4616719285562483,2.0,1.3651062864373082,3.633939783764309,2.0,1.153360647426573,3.633939786020884,2.0,1.1533606442297342,3.7757704354045525,2.0,0.9201300781645637,3.77577043680475,2.0,0.9201300754623043,3.884521843509642,2.0,0.6697592263903653,3.884521844237641,2.0,0.6697592243419724,3.958168175099268,2.0,0.4069120273823335,3.958168175364646,2.0,0.4069120261052674,3.995337538351408,2.0,0.1364848271631112,3.9953375383810785,2.0,0.13648482672934234,3.9953375383810785,2.0,-0.1364848267293419,3.995337538351408,2.0,-0.13648482716311075,3.9581681753646456,2.0,-0.40691202610526783,3.958168175099268,2.0,-0.40691202738233395,3.884521844237641,2.0,-0.6697592243419724,3.884521843509642,2.0,-0.6697592263903653,3.77577043680475,2.0,-0.9201300754623043,3.7757704354045525,2.0,-0.9201300781645637,3.633939786020884,2.0,-1.1533606442297342,3.6339397837643093,2.0,-1.1533606474265727,3.461671928556248,2.0,-1.3651062864373087,3.4616719252918635,2.0,-1.3651062899326107,3.2621758886521057,2.0,-1.5514225814088394,3.2621758842676503,2.0,-1.5514225849758587,3.0391679000708676,2.0,-1.708838809092977,3.0391678944985667,2.0,-1.708838812481568,2.7968021796924827,2.0,-1.834422603010906,2.796802172913095,2.0,-1.83442260595561,2.5395935423140483,2.0,-1.9258345746955987,2.5395935343595144,2.0,-1.9258345769243546,2.272333298192494,2.0,-1.9813718920726613,2.2723332891471,2.0,-1.9813718933159221,2.0,2.0,-2.0,1.9999999900000003,2.0,-2.0,1.9999999900000003,2.00000001,2.0,2.0,2.00000001,2.0,2.2723332891470998,2.00000001,1.9813718933159221,2.272333298192493,2.00000001,1.9813718920726615,2.5395935343595144,2.00000001,1.9258345769243546,2.5395935423140483,2.00000001,1.9258345746955987,2.796802172913095,2.00000001,1.8344226059556097,2.796802179692483,2.00000001,1.8344226030109059,3.0391678944985667,2.00000001,1.708838812481568,3.039167900070867,2.00000001,1.7088388090929771,3.2621758842676507,2.00000001,1.5514225849758583,3.2621758886521057,2.00000001,1.5514225814088396,3.4616719252918635,2.00000001,1.3651062899326107,3.4616719285562483,2.00000001,1.3651062864373082,3.633939783764309,2.00000001,1.153360647426573,3.633939786020884,2.00000001,1.1533606442297342,3.7757704354045525,2.00000001,0.9201300781645637,3.77577043680475,2.00000001,0.9201300754623043,3.884521843509642,2.00000001,0.6697592263903653,3.884521844237641,2.00000001,0.6697592243419724,3.958168175099268,2.00000001,0.4069120273823335,3.958168175364646,2.00000001,0.4069120261052674,3.995337538351408,2.00000001,0.1364848271631112,3.9953375383810785,2.00000001,0.13648482672934234,3.9953375383810785,2.00000001,-0.1364848267293419,3.995337538351408,2.00000001,-0.13648482716311075,3.9581681753646456,2.00000001,-0.40691202610526783,3.958168175099268,2.00000001,-0.40691202738233395,3.884521844237641,2.00000001,-0.6697592243419724,3.884521843509642,2.00000001,-0.6697592263903653,3.77577043680475,2.00000001,-0.9201300754623043,3.7757704354045525,2.00000001,-0.9201300781645637,3.633939786020884,2.00000001,-1.1533606442297342,3.6339397837643093,2.00000001,-1.1533606474265727,3.461671928556248,2.00000001,-1.3651062864373087,3.4616719252918635,2.00000001,-1.3651062899326107,3.2621758886521057,2.00000001,-1.5514225814088394,3.2621758842676503,2.00000001,-1.5514225849758587,3.0391679000708676,2.00000001,-1.708838809092977,3.0391678944985667,2.00000001,-1.708838812481568,2.7968021796924827,2.00000001,-1.834422603010906,2.796802172913095,2.00000001,-1.83442260595561,2.5395935423140483,2.00000001,-1.9258345746955987,2.5395935343595144,2.00000001,-1.9258345769243546,2.272333298192494,2.00000001,-1.9813718920726613,2.2723332891471,2.00000001,-1.9813718933159221,2.0,2.00000001,-2.0,1.9999999900000003,2.00000001,-2.0,2.0,0.0,-2.0,-2.0,0.0,-2.0,-2.0,2.0,-2.0,2.0,2.0,-2.0,-2.0,0.0,-2.0,-2.0,0.0,2.0,-2.0,2.0,2.0,-2.0,2.0,-2.0],"revision":0,"triangleFaceIds":[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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,5,5]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.fifty-openings.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.fifty-openings.json new file mode 100644 index 0000000..21a4f2d --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.fifty-openings.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-27.25,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-27.25,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-27.25,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-26.75,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-26.75,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-27.25,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-27.25,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-26.75,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-27.25,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-27.25,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-26.75,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-27.25,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-27.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-27.25,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-27.25,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-26.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-27.25,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-27.25,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-0","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-0","entity":"cut-0","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-0","entity":"cut-0","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-0","entity":"cut-0","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-0","entity":"cut-0","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-0","entity":"cut-0","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-0","entity":"cut-0","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-27.25,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-26.75,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-27.25,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-26.75,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-27.25,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-26.75,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-27.25,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-26.75,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.15,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-26.15,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-26.15,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-25.65,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-25.65,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.15,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-26.15,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-25.65,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.15,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-26.15,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-25.65,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.15,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-26.15,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-26.15,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-26.15,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-25.65,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-26.15,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-26.15,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-1","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-1","entity":"cut-1","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-1","entity":"cut-1","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-1","entity":"cut-1","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-1","entity":"cut-1","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-1","entity":"cut-1","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-1","entity":"cut-1","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-26.15,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-25.65,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-26.15,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-25.65,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-26.15,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-25.65,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-26.15,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-25.65,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.05,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-25.05,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-25.05,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-24.55,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-24.55,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.05,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-25.05,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-24.55,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.05,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-25.05,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-24.55,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.05,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-25.05,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-25.05,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-25.05,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-24.55,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-25.05,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-25.05,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-2","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-2","entity":"cut-2","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-2","entity":"cut-2","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-2","entity":"cut-2","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-2","entity":"cut-2","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-2","entity":"cut-2","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-2","entity":"cut-2","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-25.05,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-24.55,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-25.05,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-24.55,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-25.05,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-24.55,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-25.05,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-24.55,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.95,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-23.95,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-23.95,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-23.45,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-23.45,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.95,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-23.95,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-23.45,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.95,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-23.95,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-23.45,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.95,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-23.95,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-23.95,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-23.95,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-23.45,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-23.95,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-23.95,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-3","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-3","entity":"cut-3","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-3","entity":"cut-3","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-3","entity":"cut-3","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-3","entity":"cut-3","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-3","entity":"cut-3","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-3","entity":"cut-3","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-23.95,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-23.45,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-23.95,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-23.45,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-23.95,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-23.45,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-23.95,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-23.45,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.85,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-22.85,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-22.85,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-22.35,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-22.35,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.85,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-22.85,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-22.35,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.85,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-22.85,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-22.35,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.85,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-22.85,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-22.85,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-22.85,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-22.35,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-22.85,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-22.85,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-4","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-4","entity":"cut-4","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-4","entity":"cut-4","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-4","entity":"cut-4","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-4","entity":"cut-4","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-4","entity":"cut-4","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-4","entity":"cut-4","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-22.85,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-22.35,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-22.85,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-22.35,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-22.85,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-22.35,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-22.85,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-22.35,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.75,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-21.75,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-21.75,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-21.25,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-21.25,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.75,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-21.75,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-21.25,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.75,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-21.75,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-21.25,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.75,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-21.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-21.75,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-21.75,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-21.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-21.75,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-21.75,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-5","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-5","entity":"cut-5","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-5","entity":"cut-5","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-5","entity":"cut-5","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-5","entity":"cut-5","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-5","entity":"cut-5","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-5","entity":"cut-5","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-21.75,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-21.25,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-21.75,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-21.25,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-21.75,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-21.25,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-21.75,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-21.25,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.65,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-20.65,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-20.65,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-20.15,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-20.15,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.65,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-20.65,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-20.15,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.65,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-20.65,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-20.15,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.65,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-20.65,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-20.65,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-20.65,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-20.15,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-20.65,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-20.65,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-6","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-6","entity":"cut-6","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-6","entity":"cut-6","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-6","entity":"cut-6","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-6","entity":"cut-6","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-6","entity":"cut-6","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-6","entity":"cut-6","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-20.65,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-20.15,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-20.65,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-20.15,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-20.65,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-20.15,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-20.65,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-20.15,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.549999999999997,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-19.549999999999997,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-19.549999999999997,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-19.049999999999997,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-19.049999999999997,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.549999999999997,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-19.549999999999997,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-19.049999999999997,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.549999999999997,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-19.549999999999997,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-19.049999999999997,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.549999999999997,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-19.549999999999997,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-19.549999999999997,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-19.549999999999997,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-19.049999999999997,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-19.549999999999997,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-19.549999999999997,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-7","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-7","entity":"cut-7","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-7","entity":"cut-7","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-7","entity":"cut-7","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-7","entity":"cut-7","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-7","entity":"cut-7","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-7","entity":"cut-7","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-19.549999999999997,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-19.049999999999997,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-19.549999999999997,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-19.049999999999997,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-19.549999999999997,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-19.049999999999997,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-19.549999999999997,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-19.049999999999997,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-18.45,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-18.45,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-18.45,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-17.95,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-17.95,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-18.45,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-18.45,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-17.95,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-18.45,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-18.45,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-17.95,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-18.45,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-18.45,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-18.45,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-18.45,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-17.95,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-18.45,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-18.45,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-8","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-8","entity":"cut-8","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-8","entity":"cut-8","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-8","entity":"cut-8","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-8","entity":"cut-8","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-8","entity":"cut-8","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-8","entity":"cut-8","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-18.45,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-17.95,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-18.45,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-17.95,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-18.45,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-17.95,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-18.45,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-17.95,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.35,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-17.35,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-17.35,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-16.85,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-16.85,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.35,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-17.35,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-16.85,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.35,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-17.35,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-16.85,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.35,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-17.35,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-17.35,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-17.35,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-16.85,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-17.35,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-17.35,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-9","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-9","entity":"cut-9","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-9","entity":"cut-9","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-9","entity":"cut-9","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-9","entity":"cut-9","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-9","entity":"cut-9","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-9","entity":"cut-9","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-17.35,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-16.85,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-17.35,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-16.85,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-17.35,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-16.85,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-17.35,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-16.85,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.25,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-16.25,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-16.25,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-15.75,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-15.75,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.25,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-16.25,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-15.75,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.25,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-16.25,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-15.75,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.25,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-16.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-16.25,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-16.25,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-15.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-16.25,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-16.25,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-10","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-10","entity":"cut-10","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-10","entity":"cut-10","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-10","entity":"cut-10","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-10","entity":"cut-10","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-10","entity":"cut-10","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-10","entity":"cut-10","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-16.25,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-15.75,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-16.25,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-15.75,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-16.25,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-15.75,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-16.25,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-15.75,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.149999999999999,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-15.149999999999999,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-15.149999999999999,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-14.649999999999999,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-14.649999999999999,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.149999999999999,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-15.149999999999999,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-14.649999999999999,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.149999999999999,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-15.149999999999999,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-14.649999999999999,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.149999999999999,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-15.149999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-15.149999999999999,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-15.149999999999999,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-14.649999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-15.149999999999999,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-15.149999999999999,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-11","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-11","entity":"cut-11","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-11","entity":"cut-11","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-11","entity":"cut-11","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-11","entity":"cut-11","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-11","entity":"cut-11","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-11","entity":"cut-11","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-15.149999999999999,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-14.649999999999999,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-15.149999999999999,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-14.649999999999999,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-15.149999999999999,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-14.649999999999999,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-15.149999999999999,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-14.649999999999999,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.049999999999999,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-14.049999999999999,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-14.049999999999999,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-13.549999999999999,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-13.549999999999999,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.049999999999999,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-14.049999999999999,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-13.549999999999999,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.049999999999999,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-14.049999999999999,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-13.549999999999999,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.049999999999999,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-14.049999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-14.049999999999999,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-14.049999999999999,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-13.549999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-14.049999999999999,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-14.049999999999999,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-12","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-12","entity":"cut-12","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-12","entity":"cut-12","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-12","entity":"cut-12","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-12","entity":"cut-12","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-12","entity":"cut-12","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-12","entity":"cut-12","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-14.049999999999999,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-13.549999999999999,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-14.049999999999999,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-13.549999999999999,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-14.049999999999999,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-13.549999999999999,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-14.049999999999999,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-13.549999999999999,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.95,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-12.95,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-12.95,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-12.45,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-12.45,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.95,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-12.95,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-12.45,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.95,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-12.95,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-12.45,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.95,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-12.95,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-12.95,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-12.95,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-12.45,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-12.95,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-12.95,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-13","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-13","entity":"cut-13","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-13","entity":"cut-13","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-13","entity":"cut-13","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-13","entity":"cut-13","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-13","entity":"cut-13","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-13","entity":"cut-13","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-12.95,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-12.45,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-12.95,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-12.45,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-12.95,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-12.45,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-12.95,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-12.45,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.849999999999998,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-11.849999999999998,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-11.849999999999998,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-11.349999999999998,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-11.349999999999998,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.849999999999998,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-11.849999999999998,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-11.349999999999998,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.849999999999998,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-11.849999999999998,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-11.349999999999998,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.849999999999998,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-11.849999999999998,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-11.849999999999998,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-11.849999999999998,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-11.349999999999998,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-11.849999999999998,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-11.849999999999998,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-14","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-14","entity":"cut-14","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-14","entity":"cut-14","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-14","entity":"cut-14","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-14","entity":"cut-14","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-14","entity":"cut-14","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-14","entity":"cut-14","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-11.849999999999998,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-11.349999999999998,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-11.849999999999998,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-11.349999999999998,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-11.849999999999998,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-11.349999999999998,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-11.849999999999998,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-11.349999999999998,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.75,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-10.75,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-10.75,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-10.25,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-10.25,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.75,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-10.75,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-10.25,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.75,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-10.75,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-10.25,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.75,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-10.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-10.75,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-10.75,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-10.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-10.75,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-10.75,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-15","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-15","entity":"cut-15","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-15","entity":"cut-15","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-15","entity":"cut-15","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-15","entity":"cut-15","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-15","entity":"cut-15","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-15","entity":"cut-15","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-10.75,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-10.25,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-10.75,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-10.25,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-10.75,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-10.25,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-10.75,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-10.25,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.649999999999999,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-9.649999999999999,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-9.649999999999999,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-9.149999999999999,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-9.149999999999999,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.649999999999999,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-9.649999999999999,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-9.149999999999999,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.649999999999999,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-9.649999999999999,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-9.149999999999999,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.649999999999999,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-9.649999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-9.649999999999999,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-9.649999999999999,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-9.149999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-9.649999999999999,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-9.649999999999999,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-16","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-16","entity":"cut-16","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-16","entity":"cut-16","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-16","entity":"cut-16","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-16","entity":"cut-16","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-16","entity":"cut-16","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-16","entity":"cut-16","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-9.649999999999999,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-9.149999999999999,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-9.649999999999999,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-9.149999999999999,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-9.649999999999999,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-9.149999999999999,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-9.649999999999999,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-9.149999999999999,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.549999999999997,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-8.549999999999997,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-8.549999999999997,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-8.049999999999997,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-8.049999999999997,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.549999999999997,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-8.549999999999997,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-8.049999999999997,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.549999999999997,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-8.549999999999997,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-8.049999999999997,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.549999999999997,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-8.549999999999997,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-8.549999999999997,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-8.549999999999997,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-8.049999999999997,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-8.549999999999997,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-8.549999999999997,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-17","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-17","entity":"cut-17","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-17","entity":"cut-17","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-17","entity":"cut-17","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-17","entity":"cut-17","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-17","entity":"cut-17","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-17","entity":"cut-17","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-8.549999999999997,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-8.049999999999997,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-8.549999999999997,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-8.049999999999997,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-8.549999999999997,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-8.049999999999997,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-8.549999999999997,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-8.049999999999997,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-7.449999999999999,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-7.449999999999999,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-7.449999999999999,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-6.949999999999999,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-6.949999999999999,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-7.449999999999999,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-7.449999999999999,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-6.949999999999999,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-7.449999999999999,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-7.449999999999999,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-6.949999999999999,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-7.449999999999999,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-7.449999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-7.449999999999999,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-7.449999999999999,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-6.949999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-7.449999999999999,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-7.449999999999999,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-18","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-18","entity":"cut-18","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-18","entity":"cut-18","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-18","entity":"cut-18","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-18","entity":"cut-18","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-18","entity":"cut-18","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-18","entity":"cut-18","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-7.449999999999999,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-6.949999999999999,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-7.449999999999999,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-6.949999999999999,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-7.449999999999999,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-6.949999999999999,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-7.449999999999999,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-6.949999999999999,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.349999999999998,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-6.349999999999998,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-6.349999999999998,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-5.849999999999998,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-5.849999999999998,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.349999999999998,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-6.349999999999998,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-5.849999999999998,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.349999999999998,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-6.349999999999998,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-5.849999999999998,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.349999999999998,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-6.349999999999998,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-6.349999999999998,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-6.349999999999998,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-5.849999999999998,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-6.349999999999998,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-6.349999999999998,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-19","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-19","entity":"cut-19","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-19","entity":"cut-19","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-19","entity":"cut-19","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-19","entity":"cut-19","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-19","entity":"cut-19","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-19","entity":"cut-19","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-6.349999999999998,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-5.849999999999998,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-6.349999999999998,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-5.849999999999998,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-6.349999999999998,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-5.849999999999998,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-6.349999999999998,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-5.849999999999998,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.25,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-5.25,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-5.25,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-4.75,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-4.75,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.25,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-5.25,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-4.75,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.25,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-5.25,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-4.75,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.25,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-5.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-5.25,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-5.25,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-4.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-5.25,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-5.25,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-20","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-20","entity":"cut-20","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-20","entity":"cut-20","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-20","entity":"cut-20","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-20","entity":"cut-20","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-20","entity":"cut-20","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-20","entity":"cut-20","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-5.25,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-4.75,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-5.25,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-4.75,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-5.25,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-4.75,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-5.25,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-4.75,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.149999999999999,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-4.149999999999999,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-4.149999999999999,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-3.6499999999999986,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-3.6499999999999986,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.149999999999999,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-4.149999999999999,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-3.6499999999999986,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.149999999999999,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-4.149999999999999,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-3.6499999999999986,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.149999999999999,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-4.149999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-4.149999999999999,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-4.149999999999999,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-3.6499999999999986,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-4.149999999999999,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-4.149999999999999,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-21","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-21","entity":"cut-21","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-21","entity":"cut-21","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-21","entity":"cut-21","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-21","entity":"cut-21","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-21","entity":"cut-21","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-21","entity":"cut-21","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-4.149999999999999,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-3.6499999999999986,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-4.149999999999999,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-3.6499999999999986,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-4.149999999999999,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-3.6499999999999986,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-4.149999999999999,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-3.6499999999999986,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.049999999999997,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-3.049999999999997,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-3.049999999999997,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-2.549999999999997,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.549999999999997,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.049999999999997,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-3.049999999999997,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.549999999999997,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.049999999999997,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-3.049999999999997,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-2.549999999999997,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.049999999999997,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-3.049999999999997,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-3.049999999999997,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-3.049999999999997,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-2.549999999999997,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-3.049999999999997,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-3.049999999999997,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-22","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-22","entity":"cut-22","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-22","entity":"cut-22","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-22","entity":"cut-22","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-22","entity":"cut-22","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-22","entity":"cut-22","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-22","entity":"cut-22","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-3.049999999999997,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-2.549999999999997,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-3.049999999999997,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-2.549999999999997,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-3.049999999999997,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-2.549999999999997,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-3.049999999999997,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-2.549999999999997,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.9499999999999993,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-1.9499999999999993,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-1.9499999999999993,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-1.4499999999999993,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-1.4499999999999993,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.9499999999999993,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-1.9499999999999993,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-1.4499999999999993,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.9499999999999993,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-1.9499999999999993,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-1.4499999999999993,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.9499999999999993,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-1.9499999999999993,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-1.9499999999999993,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-1.9499999999999993,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-1.4499999999999993,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-1.9499999999999993,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-1.9499999999999993,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-23","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-23","entity":"cut-23","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-23","entity":"cut-23","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-23","entity":"cut-23","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-23","entity":"cut-23","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-23","entity":"cut-23","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-23","entity":"cut-23","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-1.9499999999999993,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-1.4499999999999993,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-1.9499999999999993,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-1.4499999999999993,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-1.9499999999999993,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-1.4499999999999993,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-1.9499999999999993,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-1.4499999999999993,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.8499999999999979,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.8499999999999979,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.8499999999999979,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.34999999999999787,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.34999999999999787,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.8499999999999979,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.8499999999999979,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.34999999999999787,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.8499999999999979,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.8499999999999979,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.34999999999999787,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.8499999999999979,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-0.8499999999999979,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-0.8499999999999979,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-0.8499999999999979,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-0.34999999999999787,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-0.8499999999999979,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-0.8499999999999979,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-24","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-24","entity":"cut-24","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-24","entity":"cut-24","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-24","entity":"cut-24","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-24","entity":"cut-24","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-24","entity":"cut-24","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-24","entity":"cut-24","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-0.8499999999999979,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[-0.34999999999999787,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-0.8499999999999979,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[-0.34999999999999787,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-0.8499999999999979,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-0.34999999999999787,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-0.8499999999999979,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[-0.34999999999999787,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.25000000000000355,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.25000000000000355,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.25000000000000355,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.7500000000000036,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.7500000000000036,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.25000000000000355,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.25000000000000355,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.7500000000000036,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.25000000000000355,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.25000000000000355,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.7500000000000036,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.25000000000000355,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.25000000000000355,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.25000000000000355,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.25000000000000355,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.7500000000000036,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.25000000000000355,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.25000000000000355,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-25","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-25","entity":"cut-25","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-25","entity":"cut-25","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-25","entity":"cut-25","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-25","entity":"cut-25","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-25","entity":"cut-25","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-25","entity":"cut-25","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.25000000000000355,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[0.7500000000000036,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.25000000000000355,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[0.7500000000000036,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.25000000000000355,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[0.7500000000000036,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.25000000000000355,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[0.7500000000000036,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.3500000000000014,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.3500000000000014,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.3500000000000014,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.8500000000000014,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8500000000000014,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.3500000000000014,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.3500000000000014,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8500000000000014,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.3500000000000014,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.3500000000000014,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.8500000000000014,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.3500000000000014,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.3500000000000014,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.3500000000000014,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.3500000000000014,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.8500000000000014,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.3500000000000014,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.3500000000000014,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-26","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-26","entity":"cut-26","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-26","entity":"cut-26","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-26","entity":"cut-26","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-26","entity":"cut-26","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-26","entity":"cut-26","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-26","entity":"cut-26","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.3500000000000014,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[1.8500000000000014,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[1.3500000000000014,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[1.8500000000000014,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.3500000000000014,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[1.8500000000000014,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[1.3500000000000014,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[1.8500000000000014,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.450000000000003,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.450000000000003,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.450000000000003,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.950000000000003,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.950000000000003,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.450000000000003,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.450000000000003,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.950000000000003,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.450000000000003,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.450000000000003,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.950000000000003,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.450000000000003,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[2.450000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.450000000000003,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.450000000000003,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.950000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.450000000000003,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.450000000000003,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-27","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-27","entity":"cut-27","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-27","entity":"cut-27","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-27","entity":"cut-27","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-27","entity":"cut-27","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-27","entity":"cut-27","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-27","entity":"cut-27","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[2.450000000000003,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[2.950000000000003,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[2.450000000000003,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[2.950000000000003,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[2.450000000000003,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[2.950000000000003,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[2.450000000000003,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[2.950000000000003,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5500000000000043,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5500000000000043,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5500000000000043,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.050000000000004,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.050000000000004,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5500000000000043,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5500000000000043,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.050000000000004,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5500000000000043,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5500000000000043,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.050000000000004,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5500000000000043,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[3.5500000000000043,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5500000000000043,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.5500000000000043,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.050000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.5500000000000043,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.5500000000000043,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-28","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-28","entity":"cut-28","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-28","entity":"cut-28","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-28","entity":"cut-28","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-28","entity":"cut-28","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-28","entity":"cut-28","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-28","entity":"cut-28","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[3.5500000000000043,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[4.050000000000004,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[3.5500000000000043,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[4.050000000000004,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[3.5500000000000043,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[4.050000000000004,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[3.5500000000000043,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[4.050000000000004,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.650000000000002,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.650000000000002,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.650000000000002,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.150000000000002,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.150000000000002,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.650000000000002,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.650000000000002,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.150000000000002,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.650000000000002,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.650000000000002,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.150000000000002,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.650000000000002,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[4.650000000000002,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.650000000000002,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.650000000000002,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[5.150000000000002,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.650000000000002,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[4.650000000000002,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-29","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-29","entity":"cut-29","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-29","entity":"cut-29","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-29","entity":"cut-29","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-29","entity":"cut-29","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-29","entity":"cut-29","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-29","entity":"cut-29","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[4.650000000000002,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[5.150000000000002,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[4.650000000000002,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[5.150000000000002,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[4.650000000000002,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[5.150000000000002,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[4.650000000000002,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[5.150000000000002,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.75,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.75,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.75,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.25,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.25,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.75,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.75,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.25,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.75,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.75,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.25,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.75,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[5.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[5.75,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[5.75,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[5.75,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[5.75,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-30","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-30","entity":"cut-30","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-30","entity":"cut-30","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-30","entity":"cut-30","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-30","entity":"cut-30","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-30","entity":"cut-30","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-30","entity":"cut-30","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[5.75,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[6.25,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[5.75,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[6.25,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[5.75,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[6.25,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[5.75,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[6.25,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.850000000000001,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.850000000000001,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.850000000000001,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[7.350000000000001,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[7.350000000000001,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.850000000000001,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.850000000000001,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[7.350000000000001,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.850000000000001,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.850000000000001,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[7.350000000000001,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.850000000000001,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[6.850000000000001,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.850000000000001,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.850000000000001,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[7.350000000000001,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.850000000000001,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[6.850000000000001,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-31","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-31","entity":"cut-31","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-31","entity":"cut-31","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-31","entity":"cut-31","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-31","entity":"cut-31","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-31","entity":"cut-31","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-31","entity":"cut-31","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[6.850000000000001,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[7.350000000000001,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[6.850000000000001,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[7.350000000000001,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[6.850000000000001,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[7.350000000000001,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[6.850000000000001,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[7.350000000000001,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.950000000000003,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[7.950000000000003,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[7.950000000000003,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[8.450000000000003,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[8.450000000000003,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.950000000000003,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[7.950000000000003,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[8.450000000000003,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.950000000000003,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[7.950000000000003,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[8.450000000000003,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.950000000000003,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[7.950000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[7.950000000000003,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[7.950000000000003,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[8.450000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[7.950000000000003,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[7.950000000000003,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-32","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-32","entity":"cut-32","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-32","entity":"cut-32","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-32","entity":"cut-32","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-32","entity":"cut-32","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-32","entity":"cut-32","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-32","entity":"cut-32","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[7.950000000000003,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[8.450000000000003,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[7.950000000000003,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[8.450000000000003,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[7.950000000000003,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[8.450000000000003,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[7.950000000000003,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[8.450000000000003,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.050000000000004,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.050000000000004,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[9.050000000000004,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.550000000000004,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[9.550000000000004,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.050000000000004,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[9.050000000000004,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[9.550000000000004,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.050000000000004,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.050000000000004,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.550000000000004,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.050000000000004,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[9.050000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[9.050000000000004,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[9.050000000000004,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[9.550000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[9.050000000000004,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[9.050000000000004,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-33","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-33","entity":"cut-33","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-33","entity":"cut-33","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-33","entity":"cut-33","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-33","entity":"cut-33","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-33","entity":"cut-33","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-33","entity":"cut-33","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[9.050000000000004,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[9.550000000000004,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[9.050000000000004,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[9.550000000000004,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[9.050000000000004,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[9.550000000000004,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[9.050000000000004,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[9.550000000000004,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.150000000000006,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[10.150000000000006,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.150000000000006,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[10.650000000000006,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.650000000000006,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.150000000000006,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.150000000000006,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.650000000000006,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.150000000000006,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[10.150000000000006,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[10.650000000000006,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.150000000000006,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[10.150000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[10.150000000000006,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.150000000000006,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[10.650000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.150000000000006,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[10.150000000000006,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-34","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-34","entity":"cut-34","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-34","entity":"cut-34","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-34","entity":"cut-34","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-34","entity":"cut-34","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-34","entity":"cut-34","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-34","entity":"cut-34","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[10.150000000000006,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[10.650000000000006,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[10.150000000000006,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[10.650000000000006,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[10.150000000000006,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[10.650000000000006,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[10.150000000000006,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[10.650000000000006,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.25,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[11.25,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[11.25,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[11.75,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[11.75,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.25,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[11.25,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[11.75,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.25,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[11.25,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[11.75,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.25,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[11.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[11.25,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[11.25,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[11.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[11.25,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[11.25,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-35","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-35","entity":"cut-35","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-35","entity":"cut-35","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-35","entity":"cut-35","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-35","entity":"cut-35","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-35","entity":"cut-35","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-35","entity":"cut-35","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[11.25,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[11.75,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[11.25,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[11.75,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[11.25,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[11.75,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[11.25,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[11.75,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.350000000000001,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[12.350000000000001,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[12.350000000000001,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[12.850000000000001,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[12.850000000000001,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.350000000000001,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[12.350000000000001,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[12.850000000000001,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.350000000000001,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[12.350000000000001,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[12.850000000000001,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.350000000000001,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[12.350000000000001,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[12.350000000000001,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[12.350000000000001,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[12.850000000000001,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[12.350000000000001,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[12.350000000000001,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-36","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-36","entity":"cut-36","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-36","entity":"cut-36","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-36","entity":"cut-36","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-36","entity":"cut-36","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-36","entity":"cut-36","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-36","entity":"cut-36","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[12.350000000000001,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[12.850000000000001,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[12.350000000000001,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[12.850000000000001,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[12.350000000000001,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[12.850000000000001,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[12.350000000000001,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[12.850000000000001,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.450000000000003,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[13.450000000000003,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[13.450000000000003,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[13.950000000000003,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[13.950000000000003,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.450000000000003,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[13.450000000000003,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[13.950000000000003,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.450000000000003,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[13.450000000000003,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[13.950000000000003,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.450000000000003,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[13.450000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[13.450000000000003,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[13.450000000000003,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[13.950000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[13.450000000000003,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[13.450000000000003,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-37","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-37","entity":"cut-37","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-37","entity":"cut-37","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-37","entity":"cut-37","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-37","entity":"cut-37","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-37","entity":"cut-37","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-37","entity":"cut-37","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[13.450000000000003,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[13.950000000000003,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[13.450000000000003,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[13.950000000000003,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[13.450000000000003,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[13.950000000000003,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[13.450000000000003,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[13.950000000000003,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.550000000000004,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[14.550000000000004,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[14.550000000000004,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[15.050000000000004,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.050000000000004,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.550000000000004,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[14.550000000000004,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.050000000000004,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.550000000000004,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[14.550000000000004,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[15.050000000000004,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.550000000000004,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[14.550000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[14.550000000000004,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[14.550000000000004,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[15.050000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[14.550000000000004,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[14.550000000000004,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-38","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-38","entity":"cut-38","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-38","entity":"cut-38","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-38","entity":"cut-38","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-38","entity":"cut-38","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-38","entity":"cut-38","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-38","entity":"cut-38","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[14.550000000000004,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[15.050000000000004,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[14.550000000000004,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[15.050000000000004,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[14.550000000000004,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[15.050000000000004,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[14.550000000000004,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[15.050000000000004,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.650000000000006,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[15.650000000000006,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.650000000000006,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[16.150000000000006,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[16.150000000000006,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.650000000000006,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.650000000000006,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[16.150000000000006,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.650000000000006,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[15.650000000000006,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[16.150000000000006,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.650000000000006,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[15.650000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[15.650000000000006,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[15.650000000000006,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[16.150000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[15.650000000000006,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[15.650000000000006,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-39","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-39","entity":"cut-39","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-39","entity":"cut-39","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-39","entity":"cut-39","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-39","entity":"cut-39","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-39","entity":"cut-39","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-39","entity":"cut-39","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[15.650000000000006,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[16.150000000000006,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[15.650000000000006,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[16.150000000000006,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[15.650000000000006,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[16.150000000000006,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[15.650000000000006,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[16.150000000000006,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.75,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[16.75,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[16.75,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[17.25,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.25,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.75,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[16.75,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.25,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.75,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[16.75,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[17.25,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.75,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[16.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[16.75,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[16.75,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[17.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[16.75,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[16.75,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-40","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-40","entity":"cut-40","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-40","entity":"cut-40","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-40","entity":"cut-40","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-40","entity":"cut-40","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-40","entity":"cut-40","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-40","entity":"cut-40","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[16.75,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[17.25,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[16.75,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[17.25,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[16.75,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[17.25,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[16.75,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[17.25,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.85,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[17.85,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.85,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[18.35,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[18.35,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.85,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.85,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[18.35,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.85,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[17.85,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[18.35,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.85,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[17.85,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[17.85,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[17.85,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[18.35,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[17.85,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[17.85,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-41","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-41","entity":"cut-41","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-41","entity":"cut-41","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-41","entity":"cut-41","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-41","entity":"cut-41","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-41","entity":"cut-41","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-41","entity":"cut-41","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[17.85,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[18.35,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[17.85,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[18.35,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[17.85,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[18.35,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[17.85,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[18.35,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.950000000000003,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[18.950000000000003,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[18.950000000000003,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[19.450000000000003,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[19.450000000000003,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.950000000000003,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[18.950000000000003,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[19.450000000000003,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.950000000000003,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[18.950000000000003,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[19.450000000000003,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.950000000000003,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[18.950000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[18.950000000000003,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[18.950000000000003,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[19.450000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[18.950000000000003,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[18.950000000000003,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-42","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-42","entity":"cut-42","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-42","entity":"cut-42","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-42","entity":"cut-42","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-42","entity":"cut-42","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-42","entity":"cut-42","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-42","entity":"cut-42","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[18.950000000000003,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[19.450000000000003,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[18.950000000000003,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[19.450000000000003,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[18.950000000000003,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[19.450000000000003,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[18.950000000000003,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[19.450000000000003,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.050000000000004,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[20.050000000000004,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[20.050000000000004,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[20.550000000000004,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[20.550000000000004,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.050000000000004,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[20.050000000000004,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[20.550000000000004,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.050000000000004,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[20.050000000000004,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[20.550000000000004,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.050000000000004,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[20.050000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[20.050000000000004,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.050000000000004,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[20.550000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.050000000000004,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[20.050000000000004,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-43","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-43","entity":"cut-43","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-43","entity":"cut-43","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-43","entity":"cut-43","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-43","entity":"cut-43","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-43","entity":"cut-43","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-43","entity":"cut-43","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[20.050000000000004,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[20.550000000000004,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[20.050000000000004,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[20.550000000000004,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[20.050000000000004,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[20.550000000000004,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[20.050000000000004,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[20.550000000000004,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.150000000000006,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[21.150000000000006,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[21.150000000000006,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[21.650000000000006,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[21.650000000000006,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.150000000000006,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[21.150000000000006,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[21.650000000000006,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.150000000000006,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[21.150000000000006,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[21.650000000000006,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.150000000000006,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[21.150000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[21.150000000000006,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[21.150000000000006,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[21.650000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[21.150000000000006,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[21.150000000000006,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-44","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-44","entity":"cut-44","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-44","entity":"cut-44","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-44","entity":"cut-44","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-44","entity":"cut-44","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-44","entity":"cut-44","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-44","entity":"cut-44","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[21.150000000000006,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[21.650000000000006,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[21.150000000000006,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[21.650000000000006,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[21.150000000000006,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[21.650000000000006,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[21.150000000000006,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[21.650000000000006,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.250000000000007,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[22.250000000000007,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[22.250000000000007,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[22.750000000000007,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[22.750000000000007,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.250000000000007,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[22.250000000000007,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[22.750000000000007,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.250000000000007,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[22.250000000000007,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[22.750000000000007,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.250000000000007,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[22.250000000000007,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[22.250000000000007,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[22.250000000000007,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[22.750000000000007,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[22.250000000000007,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[22.250000000000007,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-45","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-45","entity":"cut-45","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-45","entity":"cut-45","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-45","entity":"cut-45","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-45","entity":"cut-45","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-45","entity":"cut-45","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-45","entity":"cut-45","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[22.250000000000007,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[22.750000000000007,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[22.250000000000007,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[22.750000000000007,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[22.250000000000007,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[22.750000000000007,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[22.250000000000007,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[22.750000000000007,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.35,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[23.35,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[23.35,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[23.85,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[23.85,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.35,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[23.35,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[23.85,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.35,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[23.35,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[23.85,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.35,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[23.35,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[23.35,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[23.35,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[23.85,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[23.35,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[23.35,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-46","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-46","entity":"cut-46","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-46","entity":"cut-46","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-46","entity":"cut-46","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-46","entity":"cut-46","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-46","entity":"cut-46","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-46","entity":"cut-46","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[23.35,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[23.85,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[23.35,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[23.85,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[23.35,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[23.85,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[23.35,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[23.85,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.450000000000003,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[24.450000000000003,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[24.450000000000003,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[24.950000000000003,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[24.950000000000003,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.450000000000003,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[24.450000000000003,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[24.950000000000003,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.450000000000003,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[24.450000000000003,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[24.950000000000003,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.450000000000003,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[24.450000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[24.450000000000003,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[24.450000000000003,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[24.950000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[24.450000000000003,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[24.450000000000003,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-47","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-47","entity":"cut-47","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-47","entity":"cut-47","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-47","entity":"cut-47","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-47","entity":"cut-47","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-47","entity":"cut-47","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-47","entity":"cut-47","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[24.450000000000003,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[24.950000000000003,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[24.450000000000003,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[24.950000000000003,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[24.450000000000003,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[24.950000000000003,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[24.450000000000003,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[24.950000000000003,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[25.550000000000004,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[25.550000000000004,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[25.550000000000004,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[26.050000000000004,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[26.050000000000004,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[25.550000000000004,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[25.550000000000004,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[26.050000000000004,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[25.550000000000004,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[25.550000000000004,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[26.050000000000004,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[25.550000000000004,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[25.550000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[25.550000000000004,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[25.550000000000004,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[26.050000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[25.550000000000004,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[25.550000000000004,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-48","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-48","entity":"cut-48","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-48","entity":"cut-48","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-48","entity":"cut-48","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-48","entity":"cut-48","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-48","entity":"cut-48","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-48","entity":"cut-48","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[25.550000000000004,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[26.050000000000004,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[25.550000000000004,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[26.050000000000004,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[25.550000000000004,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[26.050000000000004,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[25.550000000000004,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[26.050000000000004,2.0,-0.2],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.650000000000006,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[26.650000000000006,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[26.650000000000006,0.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[27.150000000000006,0.0,0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[27.150000000000006,0.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.650000000000006,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[26.650000000000006,0.0,-0.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[27.150000000000006,0.0,-0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.650000000000006,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[26.650000000000006,2.0,0.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[27.150000000000006,2.0,0.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.650000000000006,2.0,-0.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[26.650000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[26.650000000000006,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[26.650000000000006,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[27.150000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[26.650000000000006,0.0,0.2],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[26.650000000000006,0.0,-0.2],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cut-49","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-49","entity":"cut-49","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-49","entity":"cut-49","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-49","entity":"cut-49","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.40000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-49","entity":"cut-49","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.40000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-49","entity":"cut-49","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cut-49","entity":"cut-49","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[26.650000000000006,0.0,0.2],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[27.150000000000006,0.0,0.2],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[26.650000000000006,0.0,-0.2],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[27.150000000000006,0.0,-0.2],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[26.650000000000006,2.0,0.2],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[27.150000000000006,2.0,0.2],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[26.650000000000006,2.0,-0.2],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[27.150000000000006,2.0,-0.2],"tolerance":1e-8}],"wires":[]}}],"handlers":["subtract_planar_cutters","boolean_rectilinear"],"host":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-30.0,0.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-30.0,3.0,0.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-30.0,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[30.0,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[30.0,3.0,0.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[30.0,0.0,0.1]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[30.0,0.0,-0.1]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[30.0,3.0,-0.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[30.0,0.0,-0.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-30.0,0.0,-0.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-30.0,3.0,-0.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-30.0,0.0,-0.1]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-30.0,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[30.0,-0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[30.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-30.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-30.0,-0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[30.0,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-30.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[60.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[60.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,0.1],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,-0.1],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"wall","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":60.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":60.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":60.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":60.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-8,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":30.00000001,"lo":-30.00000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":30.00000001,"lo":-30.00000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":60.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wall","entity":"wall","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.20000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":60.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wall","entity":"wall","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.20000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[-30.0,0.0,0.1],"tolerance":1e-8},{"id":1,"outgoing_halfedge":7,"position":[30.0,0.0,0.1],"tolerance":1e-8},{"id":2,"outgoing_halfedge":6,"position":[30.0,0.0,-0.1],"tolerance":1e-8},{"id":3,"outgoing_halfedge":5,"position":[-30.0,0.0,-0.1],"tolerance":1e-8},{"id":4,"outgoing_halfedge":0,"position":[-30.0,3.0,0.1],"tolerance":1e-8},{"id":5,"outgoing_halfedge":1,"position":[30.0,3.0,0.1],"tolerance":1e-8},{"id":6,"outgoing_halfedge":2,"position":[30.0,3.0,-0.1],"tolerance":1e-8},{"id":7,"outgoing_halfedge":3,"position":[-30.0,3.0,-0.1],"tolerance":1e-8}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-30.0,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-27.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-30.0,3.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-30.0,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-30.0,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-27.25,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-30.0,2.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-30.0,3.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-30.0,2.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-30.0,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-27.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-30.0,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-27.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-30.0,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-27.25,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-30.0,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-30.0,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-30.0,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-27.25,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-27.25,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-27.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-26.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-27.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-27.25,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-26.75,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-27.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-26.75,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-26.75,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-26.15,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.75,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-26.15,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-26.15,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.75,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-26.15,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-26.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-26.15,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.75,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-26.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-26.75,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-26.15,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-26.15,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.15,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-25.65,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.15,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.15,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-25.65,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-26.15,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-25.65,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-25.65,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.65,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-25.05,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.65,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.65,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-25.05,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.65,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-25.05,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.65,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-25.05,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-25.65,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-25.05,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.65,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-25.65,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-25.65,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-25.05,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-25.05,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.05,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-24.55,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.05,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.05,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-24.55,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-25.05,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-24.55,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-24.55,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-24.55,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-23.95,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-24.55,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-24.55,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-23.95,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-24.55,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-23.95,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-24.55,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-23.95,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-24.55,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-23.95,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-24.55,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-24.55,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-24.55,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-23.95,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-23.95,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.95,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-23.45,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.95,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.95,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-23.45,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.95,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-23.45,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-23.45,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.45,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-22.85,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.45,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.45,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-22.85,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.45,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-22.85,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.45,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-22.85,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-23.45,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-22.85,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-23.45,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-23.45,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-23.45,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-22.85,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-22.85,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.85,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-22.35,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.85,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.85,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-22.35,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.85,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-22.35,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-22.35,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.35,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-21.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.35,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.35,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-21.75,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.35,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-21.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.35,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-21.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-22.35,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-21.75,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-22.35,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-22.35,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-22.35,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-21.75,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-21.75,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-21.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.75,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-21.25,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-21.25,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-21.25,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-20.65,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.25,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-20.65,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-20.65,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.25,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-20.65,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-21.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-20.65,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-21.25,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-21.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-21.25,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-20.65,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-20.65,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.65,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-20.15,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.65,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.65,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-20.15,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.65,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-20.15,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-20.15,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.15,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-19.549999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.15,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.15,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-19.549999999999997,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.15,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-19.549999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.15,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-19.549999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-20.15,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-19.549999999999997,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-20.15,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-20.15,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-20.15,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-19.549999999999997,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-19.549999999999997,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.549999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-19.049999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.549999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.549999999999997,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-19.049999999999997,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.549999999999997,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-19.049999999999997,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-19.049999999999997,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.049999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-18.45,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.049999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.049999999999997,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-18.45,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.049999999999997,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-18.45,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.049999999999997,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-18.45,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-19.049999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-18.45,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-19.049999999999997,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-19.049999999999997,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-19.049999999999997,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-18.45,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-18.45,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-18.45,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-17.95,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-18.45,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-18.45,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-17.95,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-18.45,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-17.95,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-17.95,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.95,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-17.35,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.95,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.95,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-17.35,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.95,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-17.35,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.95,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-17.35,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-17.95,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-17.35,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.95,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-17.95,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-17.95,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-17.35,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-17.35,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.35,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-16.85,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.35,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.35,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-16.85,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-17.35,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-16.85,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-16.85,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.85,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-16.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.85,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.85,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-16.25,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.85,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-16.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.85,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-16.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-16.85,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-16.25,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.85,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-16.85,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-16.85,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-16.25,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-16.25,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-15.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.25,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-15.75,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-16.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-15.75,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-15.75,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-15.149999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.75,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-15.149999999999999,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-15.149999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.75,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-15.149999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-15.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-15.149999999999999,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.75,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-15.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-15.75,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-15.149999999999999,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-15.149999999999999,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.149999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-14.649999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.149999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.149999999999999,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-14.649999999999999,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-15.149999999999999,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-14.649999999999999,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-14.649999999999999,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.649999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-14.049999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.649999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.649999999999999,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-14.049999999999999,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.649999999999999,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-14.049999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.649999999999999,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-14.049999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-14.649999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-14.049999999999999,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.649999999999999,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-14.649999999999999,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-14.649999999999999,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-14.049999999999999,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-14.049999999999999,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.049999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-13.549999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.049999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.049999999999999,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-13.549999999999997,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-14.049999999999999,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-13.549999999999997,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-13.549999999999997,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-13.549999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-12.95,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-13.549999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-13.549999999999997,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-12.95,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-13.549999999999997,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-12.95,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-13.549999999999997,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-12.95,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-13.549999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-12.95,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-13.549999999999997,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-13.549999999999997,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-13.549999999999997,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-12.95,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-12.95,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.95,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-12.45,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.95,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.95,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-12.45,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.95,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-12.45,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-12.45,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.45,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-11.849999999999998,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.45,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.45,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-11.849999999999998,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.45,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-11.849999999999998,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.45,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-11.849999999999998,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-12.45,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-11.849999999999998,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-12.45,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-12.45,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-12.45,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-11.849999999999998,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-11.849999999999998,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.849999999999998,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-11.349999999999998,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.849999999999998,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.849999999999998,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-11.349999999999998,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.849999999999998,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-11.349999999999998,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-11.349999999999998,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.349999999999998,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-10.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.349999999999998,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.349999999999998,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-10.75,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.349999999999998,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-10.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.349999999999998,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-10.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-11.349999999999998,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-10.75,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-11.349999999999998,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-11.349999999999998,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-11.349999999999998,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-10.75,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-10.75,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-10.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.75,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-10.25,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-10.25,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-10.25,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-9.649999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.25,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-9.649999999999999,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-9.649999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.25,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-9.649999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-10.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-9.649999999999999,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-10.25,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-10.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-10.25,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-9.649999999999999,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-9.649999999999999,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.649999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-9.149999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.649999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.649999999999999,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-9.149999999999999,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.649999999999999,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-9.149999999999999,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-9.149999999999999,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.149999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-8.549999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.149999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.149999999999999,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-8.549999999999997,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.149999999999999,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-8.549999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.149999999999999,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-8.549999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-9.149999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-8.549999999999997,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-9.149999999999999,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-9.149999999999999,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-9.149999999999999,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-8.549999999999997,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-8.549999999999997,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.549999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-8.049999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.549999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.549999999999997,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-8.049999999999997,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.549999999999997,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-8.049999999999997,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-8.049999999999997,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.049999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-7.449999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.049999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.049999999999997,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-7.449999999999999,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.049999999999997,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-7.449999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.049999999999997,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-7.449999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-8.049999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-7.449999999999999,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-8.049999999999997,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-8.049999999999997,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-8.049999999999997,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-7.449999999999999,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-7.449999999999999,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-7.449999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-6.949999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-7.449999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-7.449999999999999,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-6.949999999999999,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-7.449999999999999,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-6.949999999999999,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-6.949999999999999,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.949999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-6.349999999999998,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.949999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.949999999999999,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-6.349999999999998,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.949999999999999,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-6.349999999999998,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.949999999999999,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-6.349999999999998,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-6.949999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-6.349999999999998,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.949999999999999,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-6.949999999999999,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-6.949999999999999,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-6.349999999999998,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-6.349999999999998,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.349999999999998,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-5.849999999999998,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.349999999999998,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.349999999999998,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-5.849999999999998,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-6.349999999999998,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-5.849999999999998,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-5.849999999999998,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.849999999999998,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-5.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.849999999999998,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.849999999999998,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-5.25,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.849999999999998,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-5.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.849999999999998,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-5.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-5.849999999999998,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-5.25,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.849999999999998,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-5.849999999999998,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-5.849999999999998,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-5.25,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-5.25,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-4.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.25,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-4.75,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-5.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-4.75,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-4.75,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-4.149999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.75,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-4.149999999999999,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-4.149999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.75,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-4.149999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-4.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-4.149999999999999,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.75,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-4.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-4.75,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-4.149999999999999,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-4.149999999999999,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.149999999999999,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-3.6499999999999986,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.149999999999999,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.149999999999999,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-3.6499999999999986,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-4.149999999999999,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-3.6499999999999986,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-3.6499999999999986,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.6499999999999986,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-3.049999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.6499999999999986,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.6499999999999986,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-3.049999999999997,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.6499999999999986,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-3.049999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.6499999999999986,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-3.049999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-3.6499999999999986,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-3.049999999999997,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.6499999999999986,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-3.6499999999999986,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-3.6499999999999986,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-3.049999999999997,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-3.049999999999997,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.049999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-2.549999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.049999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.049999999999997,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-2.549999999999997,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-3.049999999999997,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-2.549999999999997,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-2.549999999999997,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.549999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-1.9499999999999993,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.549999999999997,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.549999999999997,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-1.9499999999999993,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.549999999999997,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-1.9499999999999993,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.549999999999997,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-1.9499999999999993,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-2.549999999999997,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-1.9499999999999993,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.549999999999997,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-2.549999999999997,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-2.549999999999997,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-1.9499999999999993,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-1.9499999999999993,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.9499999999999993,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-1.4499999999999993,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.9499999999999993,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.9499999999999993,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-1.4499999999999993,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.9499999999999993,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-1.4499999999999993,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-1.4499999999999993,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.4499999999999993,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-0.8499999999999979,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.4499999999999993,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.4499999999999993,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-0.8499999999999979,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.4499999999999993,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.8499999999999979,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.4499999999999993,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-0.8499999999999979,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-1.4499999999999993,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-0.8499999999999979,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.4499999999999993,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-1.4499999999999993,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-1.4499999999999993,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.8499999999999979,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.8499999999999979,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.8499999999999979,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-0.34999999999999787,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.8499999999999979,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.8499999999999979,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-0.34999999999999787,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.8499999999999979,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.34999999999999787,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.34999999999999787,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.34999999999999787,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.25000000000000355,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.34999999999999787,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.34999999999999787,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.25000000000000355,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.34999999999999787,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.25000000000000355,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.34999999999999787,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.25000000000000355,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-0.34999999999999787,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.25000000000000355,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.34999999999999787,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-0.34999999999999787,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.34999999999999787,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.25000000000000355,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.25000000000000355,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.25000000000000355,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.7500000000000036,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.25000000000000355,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.25000000000000355,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.7500000000000036,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.25000000000000355,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.7500000000000036,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.7500000000000036,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.7500000000000036,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.3500000000000014,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.7500000000000036,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.7500000000000036,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.3500000000000014,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.7500000000000036,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.3500000000000014,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.7500000000000036,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.3500000000000014,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.7500000000000036,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.3500000000000014,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.7500000000000036,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.7500000000000036,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.7500000000000036,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.3500000000000014,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.3500000000000014,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.3500000000000014,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8500000000000014,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.3500000000000014,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.3500000000000014,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8500000000000014,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.3500000000000014,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.8500000000000014,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.8500000000000014,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.8500000000000014,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.8500000000000014,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.8500000000000014,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.450000000000003,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.8500000000000014,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.8500000000000014,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.450000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8500000000000014,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.450000000000003,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.8500000000000014,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8500000000000014,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.8500000000000014,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.450000000000003,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.450000000000003,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.450000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.450000000000003,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.950000000000003,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.450000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.950000000000003,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.950000000000003,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.950000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5500000000000043,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.950000000000003,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5500000000000043,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.950000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5500000000000043,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.950000000000003,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5500000000000043,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.950000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5500000000000043,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.950000000000003,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.950000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.950000000000003,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5500000000000043,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5500000000000043,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5500000000000043,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5500000000000043,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5500000000000043,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.050000000000004,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5500000000000043,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.050000000000004,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.050000000000004,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.050000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.050000000000004,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.650000000000006,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.050000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.050000000000004,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.650000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.050000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.650000000000006,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.050000000000004,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.050000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.050000000000004,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.650000000000006,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.650000000000006,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.650000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.650000000000006,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.150000000000006,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.650000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.150000000000006,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.150000000000006,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.150000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.150000000000006,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.75,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.150000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.150000000000006,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.150000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.75,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.150000000000006,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.150000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.150000000000006,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.75,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.75,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.75,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.25,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.25,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.25,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.850000000000001,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.25,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.850000000000001,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.850000000000001,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.25,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.850000000000001,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.850000000000001,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.25,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.25,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.850000000000001,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.850000000000001,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.850000000000001,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[7.350000000000001,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.850000000000001,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.850000000000001,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[7.350000000000001,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.850000000000001,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[7.350000000000001,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[7.350000000000001,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.350000000000001,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[7.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.350000000000001,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.350000000000001,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[7.950000000000003,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.350000000000001,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[7.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.350000000000001,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[7.950000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[7.350000000000001,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[7.950000000000003,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.350000000000001,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[7.350000000000001,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[7.350000000000001,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[7.950000000000003,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[7.950000000000003,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.950000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[8.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.950000000000003,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[8.450000000000003,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[7.950000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[8.450000000000003,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[8.450000000000003,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.450000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[9.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.450000000000003,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[9.050000000000004,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.450000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.450000000000003,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[9.050000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[8.450000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[9.050000000000004,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.450000000000003,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[8.450000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[8.450000000000003,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.050000000000004,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.050000000000004,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.050000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[9.550000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.050000000000004,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[9.550000000000004,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.050000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.550000000000004,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.550000000000004,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.550000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.550000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.550000000000004,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.150000000000006,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.550000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[10.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.550000000000004,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.150000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[9.550000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.150000000000006,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[9.550000000000004,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[9.550000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.550000000000004,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[10.150000000000006,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[10.150000000000006,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.150000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.150000000000006,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.650000000000006,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.150000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[10.650000000000006,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[10.650000000000006,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.650000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[11.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.650000000000006,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[11.25,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.650000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[11.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.650000000000006,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[11.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.650000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[11.25,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.650000000000006,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.650000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[10.650000000000006,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[11.25,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[11.25,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[11.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.25,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[11.75,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[11.75,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[11.75,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[12.350000000000001,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.75,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[12.350000000000001,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[12.350000000000001,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.75,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[12.350000000000001,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[11.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[12.350000000000001,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.75,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[11.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[11.75,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[12.350000000000001,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[12.350000000000001,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.350000000000001,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[12.850000000000001,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.350000000000001,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.350000000000001,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[12.850000000000001,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.350000000000001,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[12.850000000000001,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[12.850000000000001,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.850000000000001,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[13.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.850000000000001,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.850000000000001,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[13.450000000000003,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.850000000000001,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[13.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.850000000000001,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[13.450000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[12.850000000000001,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[13.450000000000003,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.850000000000001,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[12.850000000000001,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[12.850000000000001,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[13.450000000000003,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[13.450000000000003,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.450000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[13.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.450000000000003,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[13.950000000000003,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.450000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[13.950000000000003,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[13.950000000000003,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.950000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[14.550000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.950000000000003,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[14.550000000000004,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.950000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[14.550000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.950000000000003,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[14.550000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[13.950000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[14.550000000000004,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.950000000000003,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[13.950000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[13.950000000000003,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[14.550000000000004,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[14.550000000000004,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.550000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[15.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.550000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.550000000000004,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[15.050000000000004,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.550000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[15.050000000000004,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[15.050000000000004,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.050000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[15.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.050000000000004,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[15.650000000000006,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.050000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[15.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.050000000000004,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[15.650000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[15.050000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[15.650000000000006,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.050000000000004,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[15.050000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[15.050000000000004,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[15.650000000000006,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[15.650000000000006,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.650000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[16.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.650000000000006,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[16.150000000000006,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.650000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[16.150000000000006,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[16.150000000000006,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.150000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[16.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.150000000000006,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[16.75,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.150000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[16.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.150000000000006,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[16.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[16.150000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[16.75,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.150000000000006,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[16.150000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[16.150000000000006,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[16.75,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[16.75,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.75,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[17.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.75,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.75,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[17.25,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[16.75,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[17.25,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[17.25,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[17.85,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.25,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.25,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[17.85,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[17.85,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.25,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[17.85,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[17.25,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[17.85,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.25,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[17.25,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[17.25,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[17.85,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[17.85,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.85,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[18.35,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.85,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.85,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[18.35,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.85,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[18.35,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[18.35,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.35,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[18.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.35,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.35,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[18.950000000000003,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.35,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[18.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.35,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[18.950000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[18.35,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[18.950000000000003,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.35,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[18.35,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[18.35,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[18.950000000000003,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[18.950000000000003,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.950000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[19.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.950000000000003,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[19.450000000000003,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[18.950000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[19.450000000000003,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[19.450000000000003,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[19.450000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[20.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[19.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[19.450000000000003,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[20.050000000000004,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[19.450000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[20.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[19.450000000000003,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[20.050000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[19.450000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[20.050000000000004,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[19.450000000000003,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[19.450000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[19.450000000000003,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[20.050000000000004,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[20.050000000000004,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.050000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[20.550000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.050000000000004,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[20.550000000000004,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.050000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[20.550000000000004,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[20.550000000000004,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.550000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[21.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.550000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.550000000000004,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[21.150000000000006,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.550000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[21.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.550000000000004,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[21.150000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[20.550000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[21.150000000000006,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[20.550000000000004,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[20.550000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[20.550000000000004,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[21.150000000000006,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[21.150000000000006,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.150000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[21.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.150000000000006,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[21.650000000000006,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.150000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[21.650000000000006,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[21.650000000000006,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.650000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[22.250000000000007,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.650000000000006,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[22.250000000000007,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.650000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[22.250000000000007,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.650000000000006,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[22.250000000000007,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[21.650000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[22.250000000000007,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[21.650000000000006,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[21.650000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[21.650000000000006,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[22.250000000000007,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[22.250000000000007,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.250000000000007,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[22.750000000000007,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.250000000000007,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.250000000000007,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[22.750000000000007,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.250000000000007,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[22.750000000000007,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[22.750000000000007,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.750000000000007,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[23.35,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.750000000000007,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.750000000000007,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[23.35,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.750000000000007,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[23.35,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.750000000000007,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[23.35,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[22.750000000000007,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[23.35,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[22.750000000000007,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[22.750000000000007,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[22.750000000000007,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[23.35,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[23.35,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.35,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[23.85,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.35,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.35,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[23.85,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.35,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[23.85,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[23.85,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.85,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[24.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.85,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.85,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[24.450000000000003,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.85,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[24.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.85,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[24.450000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[23.85,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[24.450000000000003,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[23.85,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[23.85,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[23.85,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[24.450000000000003,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[24.450000000000003,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.450000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[24.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.450000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.450000000000003,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[24.950000000000003,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.450000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[24.950000000000003,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[24.950000000000003,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.950000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[25.550000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.950000000000003,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.950000000000003,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[25.550000000000004,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.950000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[25.550000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.950000000000003,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[25.550000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[24.950000000000003,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[25.550000000000004,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[24.950000000000003,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[24.950000000000003,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[24.950000000000003,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[25.550000000000004,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[25.550000000000004,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[25.550000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[26.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[25.550000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[25.550000000000004,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[26.050000000000004,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[25.550000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[26.050000000000004,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[26.050000000000004,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.050000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[26.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.050000000000004,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.050000000000004,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[26.650000000000006,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.050000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[26.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.050000000000004,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[26.650000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[26.050000000000004,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[26.650000000000006,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.050000000000004,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[26.050000000000004,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[26.050000000000004,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[26.650000000000006,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[26.650000000000006,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.650000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[27.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.650000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.650000000000006,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[27.150000000000006,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[26.650000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[27.150000000000006,3.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[27.150000000000006,2.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[27.150000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[30.0,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[27.150000000000006,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[27.150000000000006,3.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[30.0,3.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[27.150000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[30.0,2.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[30.0,3.0,0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[27.150000000000006,0.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[30.0,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[27.150000000000006,2.0,0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[30.0,2.0,-0.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[27.150000000000006,0.0,-0.1]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[27.150000000000006,2.0,-0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[27.150000000000006,0.0,0.1]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[30.0,0.0,0.1]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.75,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[60.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[57.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[60.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[60.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.2,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.2,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.2,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-27.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-30.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-30.0,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-30.0,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.75,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[60.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[57.25,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[60.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[60.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.2,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.2,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.2,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-30.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-30.0,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-27.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-30.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.25,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[57.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[57.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[57.25,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-26.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-27.25,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-27.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-27.25,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.8500000000000014,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[56.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.15,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[56.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.75,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-26.15,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-26.75,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-26.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-26.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.25,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.8500000000000014,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.25,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[56.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.15,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[56.75,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-26.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-26.75,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-26.15,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-26.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.8500000000000014,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.350000000000001,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.8500000000000014,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.8500000000000014,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[56.15,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[55.65,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[56.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.15,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-25.65,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-26.15,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-26.15,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-26.15,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.350000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.949999999999999,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.350000000000001,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.350000000000001,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[55.65,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[55.05,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[55.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[55.65,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-25.05,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-25.65,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-25.65,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-25.65,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.350000000000001,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.949999999999999,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.350000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.350000000000001,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[55.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[55.05,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[55.65,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[55.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-25.65,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-25.65,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-25.05,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-25.65,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.949999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.449999999999999,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.949999999999999,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.949999999999999,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[55.05,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[54.55,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[55.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[55.05,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-24.55,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-25.05,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-25.05,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-25.05,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.449999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.050000000000001,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.449999999999999,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.449999999999999,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[54.55,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.95,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[54.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[54.55,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-23.95,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-24.55,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-24.55,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-24.55,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.449999999999999,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.050000000000001,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.449999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.449999999999999,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[54.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.95,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[54.55,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[54.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-24.55,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-24.55,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-23.95,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-24.55,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.050000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.550000000000001,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.050000000000001,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.050000000000001,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[53.95,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.45,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[53.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.95,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-23.45,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-23.95,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-23.95,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-23.95,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.550000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.149999999999999,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.550000000000001,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.550000000000001,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[53.45,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.85,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[53.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.45,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-22.85,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-23.45,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-23.45,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-23.45,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.550000000000001,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.149999999999999,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.550000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.550000000000001,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[53.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.85,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[53.45,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-23.45,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-23.45,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-22.85,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-23.45,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.149999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.649999999999999,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.149999999999999,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.149999999999999,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[52.85,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.35,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[52.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.85,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-22.35,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-22.85,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-22.85,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-22.85,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.649999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.25,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.649999999999999,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.649999999999999,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[52.35,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[52.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.35,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-21.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-22.35,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-22.35,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-22.35,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.649999999999999,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.25,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.649999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.649999999999999,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[52.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.75,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[52.35,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-22.35,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-22.35,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-21.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-22.35,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.75,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[51.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[51.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.75,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-21.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-21.75,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-21.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-21.75,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.350000000000001,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[51.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.65,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[51.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.25,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-20.65,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-21.25,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-21.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-21.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.75,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.350000000000001,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.75,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[51.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.65,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[51.25,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-21.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-21.25,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-20.65,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-21.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.350000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.850000000000001,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.350000000000001,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.350000000000001,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[50.65,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.15,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[50.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.65,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-20.15,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-20.65,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-20.65,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-20.65,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.850000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.450000000000003,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.850000000000001,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.850000000000001,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[50.15,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[49.55,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[50.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.15,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-19.549999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-20.15,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-20.15,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-20.15,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.850000000000001,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.450000000000003,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.850000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.850000000000001,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[50.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[49.55,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[50.15,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-20.15,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-20.15,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-19.549999999999997,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-20.15,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.450000000000003,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.950000000000003,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.450000000000003,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.450000000000003,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[49.55,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[49.05,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[49.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[49.55,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-19.049999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-19.549999999999997,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-19.549999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-19.549999999999997,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.950000000000003,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.55,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.950000000000003,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.950000000000003,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[49.05,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[48.45,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[49.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[49.05,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-18.45,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-19.049999999999997,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-19.049999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-19.049999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.950000000000003,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.55,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.950000000000003,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.950000000000003,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[49.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[48.45,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[49.05,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[49.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-19.049999999999997,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-19.049999999999997,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-18.45,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-19.049999999999997,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.05,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.55,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.55,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[48.45,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.95,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[48.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[48.45,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-17.95,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-18.45,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-18.45,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-18.45,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.649999999999999,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.05,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.05,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[47.95,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.35,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[47.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.95,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-17.35,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-17.95,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-17.95,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-17.95,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.05,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.649999999999999,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.05,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[47.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.35,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[47.95,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-17.95,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-17.95,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-17.35,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-17.95,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.649999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.149999999999999,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.649999999999999,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.649999999999999,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[47.35,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.85,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[47.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.35,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-16.85,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-17.35,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-17.35,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-17.35,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.149999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.75,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.149999999999999,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.149999999999999,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[46.85,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[46.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.85,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-16.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-16.85,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-16.85,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-16.85,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.149999999999999,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.75,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.149999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.149999999999999,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[46.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.25,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[46.85,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-16.85,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-16.85,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-16.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-16.85,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.25,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[46.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[46.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.25,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-15.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-16.25,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-16.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-16.25,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.850000000000001,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[45.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.15,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[45.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.75,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-15.149999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-15.75,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-15.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-15.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.25,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.850000000000001,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.25,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[45.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.15,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[45.75,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-15.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-15.75,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-15.149999999999999,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-15.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.850000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.350000000000001,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.850000000000001,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.850000000000001,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[45.15,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[44.65,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[45.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.15,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-14.649999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-15.149999999999999,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-15.149999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-15.149999999999999,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.350000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.950000000000001,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.350000000000001,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.350000000000001,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[44.65,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[44.05,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[44.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[44.65,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-14.049999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-14.649999999999999,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-14.649999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-14.649999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.350000000000001,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.950000000000001,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.350000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.350000000000001,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[44.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[44.05,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[44.65,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[44.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-14.649999999999999,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-14.649999999999999,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-14.049999999999999,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-14.649999999999999,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.950000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[16.450000000000003,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.950000000000001,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.950000000000001,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[44.05,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[43.55,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[44.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[44.05,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-13.549999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-14.049999999999999,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-14.049999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-14.049999999999999,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5000000000000018,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[16.450000000000003,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.05,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[16.450000000000003,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[16.450000000000003,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[43.55,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.95,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[43.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[43.55,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-12.95,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-13.549999999999997,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-13.549999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-13.549999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[16.450000000000003,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.05,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[16.450000000000003,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[16.450000000000003,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[43.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.95,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[43.55,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[43.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-13.549999999999997,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-13.549999999999997,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-12.95,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-13.549999999999997,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.55,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.05,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.05,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[42.95,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.45,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[42.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.95,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-12.45,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-12.95,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-12.95,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-12.95,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.150000000000002,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.55,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.55,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[42.45,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.849999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[42.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.45,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-11.849999999999998,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-12.45,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-12.45,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-12.45,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.55,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.150000000000002,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.55,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[42.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.849999999999994,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[42.45,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-12.45,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-12.45,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-11.849999999999998,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-12.45,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.150000000000002,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.650000000000002,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.150000000000002,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.150000000000002,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[41.849999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.349999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[41.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.849999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-11.349999999999998,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-11.849999999999998,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-11.849999999999998,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-11.849999999999998,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.650000000000002,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.25,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.650000000000002,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.650000000000002,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[41.349999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[41.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.349999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-10.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-11.349999999999998,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-11.349999999999998,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-11.349999999999998,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.650000000000002,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.25,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.650000000000002,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.650000000000002,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[41.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.75,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[41.349999999999994,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-11.349999999999998,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-11.349999999999998,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-10.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-11.349999999999998,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[19.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.75,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[19.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[40.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[40.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.75,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-10.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-10.75,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-10.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-10.75,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[19.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.35,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[19.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[40.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.65,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[40.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.25,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-9.649999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-10.25,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-10.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-10.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[19.75,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.35,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[19.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.75,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[40.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.65,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[40.25,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-10.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-10.25,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-9.649999999999999,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-10.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.85,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.35,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.35,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[39.65,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.15,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[39.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.65,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-9.149999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-9.649999999999999,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-9.649999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-9.649999999999999,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[21.450000000000003,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.85,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.85,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[39.15,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[38.55,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[39.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.15,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-8.549999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-9.149999999999999,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-9.149999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-9.149999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.85,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[21.450000000000003,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.85,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[39.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[38.55,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[39.15,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-9.149999999999999,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-9.149999999999999,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-8.549999999999997,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-9.149999999999999,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.450000000000003,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[21.950000000000003,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.450000000000003,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[21.450000000000003,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[38.55,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[38.05,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[38.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[38.55,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-8.049999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-8.549999999999997,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-8.549999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-8.549999999999997,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.950000000000003,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[22.55,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.950000000000003,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[21.950000000000003,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[38.05,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[37.45,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[38.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[38.05,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-7.449999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-8.049999999999997,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-8.049999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-8.049999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.950000000000003,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[22.55,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.950000000000003,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[21.950000000000003,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[38.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[37.45,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[38.05,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[38.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-8.049999999999997,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-8.049999999999997,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-7.449999999999999,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-8.049999999999997,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[22.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.05,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[22.55,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[22.55,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[37.45,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.95,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[37.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[37.45,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.949999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-7.449999999999999,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-7.449999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-7.449999999999999,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.650000000000002,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.05,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.05,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[36.95,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.349999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[36.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.95,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.349999999999998,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-6.949999999999999,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.949999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-6.949999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.05,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.650000000000002,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.05,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[36.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.349999999999994,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[36.95,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-6.949999999999999,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-6.949999999999999,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-6.349999999999998,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-6.949999999999999,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.650000000000002,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.150000000000002,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.650000000000002,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.650000000000002,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[36.349999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.849999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[36.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.349999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-5.849999999999998,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-6.349999999999998,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.349999999999998,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-6.349999999999998,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.150000000000002,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.75,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.150000000000002,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.150000000000002,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[35.849999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[35.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.849999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-5.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-5.849999999999998,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-5.849999999999998,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-5.849999999999998,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.150000000000002,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.75,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.150000000000002,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.150000000000002,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[35.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.25,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[35.849999999999994,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-5.849999999999998,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-5.849999999999998,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-5.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-5.849999999999998,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.25,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[35.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[35.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.25,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-4.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-5.25,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-5.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-5.25,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[25.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.85,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[25.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[34.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.15,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[34.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.75,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-4.149999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-4.75,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-4.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-4.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[25.25,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.85,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[25.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.25,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[34.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.15,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[34.75,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-4.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-4.75,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-4.149999999999999,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-4.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[25.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[26.35,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[25.85,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.85,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[34.15,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[33.65,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[34.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.15,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.6499999999999986,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-4.149999999999999,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-4.149999999999999,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-4.149999999999999,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[26.950000000000003,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.35,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[26.35,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[33.65,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[33.05,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[33.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[33.65,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.049999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-3.6499999999999986,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.6499999999999986,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-3.6499999999999986,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.35,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[26.950000000000003,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[26.35,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[33.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[33.05,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[33.65,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[33.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-3.6499999999999986,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-3.6499999999999986,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-3.049999999999997,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-3.6499999999999986,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.950000000000003,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[27.450000000000003,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.950000000000003,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[26.950000000000003,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[33.05,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[32.55,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[33.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[33.05,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-2.549999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-3.049999999999997,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.049999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-3.049999999999997,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[27.450000000000003,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.05,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[27.450000000000003,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[27.450000000000003,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[32.55,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.95,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[32.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[32.55,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-1.9499999999999993,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.549999999999997,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-2.549999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.549999999999997,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[27.450000000000003,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.05,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[27.450000000000003,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[27.450000000000003,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[32.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.95,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[32.55,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[32.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-2.549999999999997,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.549999999999997,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-1.9499999999999993,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.549999999999997,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[28.05,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.55,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[28.05,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.05,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[31.95,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.45,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[31.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.95,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-1.4499999999999993,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-1.9499999999999993,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-1.9499999999999993,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-1.9499999999999993,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[28.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.150000000000002,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[28.55,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.55,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[31.45,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.849999999999998,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[31.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.45,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.8499999999999979,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-1.4499999999999993,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-1.4499999999999993,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-1.4499999999999993,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[28.55,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.150000000000002,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[28.55,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.55,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[31.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.849999999999998,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[31.45,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-1.4499999999999993,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-1.4499999999999993,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.8499999999999979,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-1.4499999999999993,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[29.150000000000002,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.650000000000002,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[29.150000000000002,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.150000000000002,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[30.849999999999998,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.349999999999998,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[30.849999999999998,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.849999999999998,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.34999999999999787,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.8499999999999979,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.8499999999999979,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.8499999999999979,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[29.650000000000002,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.250000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[29.650000000000002,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.650000000000002,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[30.349999999999998,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.749999999999996,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[30.349999999999998,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.349999999999998,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.25000000000000355,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.34999999999999787,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.34999999999999787,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.34999999999999787,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[29.650000000000002,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.250000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[29.650000000000002,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.650000000000002,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[30.349999999999998,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.749999999999996,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[30.349999999999998,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.349999999999998,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.34999999999999787,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.34999999999999787,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.25000000000000355,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.34999999999999787,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[30.250000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.750000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[30.250000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.250000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[29.749999999999996,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.249999999999996,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[29.749999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.749999999999996,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.7500000000000036,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.25000000000000355,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.25000000000000355,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.25000000000000355,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[30.750000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.35,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[30.750000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.750000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[29.249999999999996,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.65,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[29.249999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.249999999999996,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.3500000000000014,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.7500000000000036,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.7500000000000036,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.7500000000000036,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[30.750000000000004,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.35,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[30.750000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.750000000000004,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[29.249999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.65,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[29.249999999999996,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[29.249999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.7500000000000036,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.7500000000000036,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.3500000000000014,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.7500000000000036,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[31.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.85,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[31.35,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.35,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[28.65,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.15,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[28.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.65,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.8500000000000014,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.3500000000000014,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.3500000000000014,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.3500000000000014,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[31.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[32.45,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[31.85,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.85,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[28.15,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[27.549999999999997,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[28.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.15,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.8500000000000014,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.8500000000000014,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.8500000000000014,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[31.85,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[32.45,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[31.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[31.85,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[28.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[27.549999999999997,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[28.15,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[28.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.8500000000000014,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.8500000000000014,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.450000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.8500000000000014,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[32.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[32.95,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[32.45,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[32.45,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[27.549999999999997,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[27.049999999999997,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[27.549999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[27.549999999999997,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.450000000000003,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.450000000000003,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[32.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[33.550000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[32.95,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[32.95,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[27.049999999999997,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[26.449999999999996,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[27.049999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[27.049999999999997,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5500000000000043,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.950000000000003,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[32.95,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[33.550000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[32.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[32.95,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[27.049999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[26.449999999999996,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[27.049999999999997,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[27.049999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.950000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.950000000000003,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5500000000000043,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.950000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[33.550000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.050000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[33.550000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[33.550000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[26.449999999999996,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.949999999999996,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[26.449999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[26.449999999999996,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5500000000000043,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5500000000000043,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5500000000000043,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[34.050000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.650000000000006,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[34.050000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.050000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[25.949999999999996,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.349999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[25.949999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.949999999999996,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.050000000000004,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[34.050000000000004,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.650000000000006,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[34.050000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.050000000000004,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[25.949999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.349999999999994,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[25.949999999999996,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.949999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.050000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.050000000000004,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.650000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.050000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[34.650000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.150000000000006,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[34.650000000000006,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[34.650000000000006,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[25.349999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.849999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[25.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.349999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.650000000000006,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.650000000000006,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.552713678800501e-15,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.552713678800501e-15,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5000000000000036,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.552713678800501e-15,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[35.150000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.75,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[35.150000000000006,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.150000000000006,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[24.849999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[24.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.849999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.150000000000006,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[35.150000000000006,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.75,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[35.150000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.150000000000006,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[24.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.25,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[24.849999999999994,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.150000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.150000000000006,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.150000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[35.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.25,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[35.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[35.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[24.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[24.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.25,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.75,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.75,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[36.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.85,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[36.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[23.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.15,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[23.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.75,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.850000000000001,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.25,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[36.25,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.85,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[36.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.25,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[23.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.15,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[23.75,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.25,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.850000000000001,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[36.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[37.35,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[36.85,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[36.85,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[23.15,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[22.65,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[23.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.15,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.350000000000001,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.850000000000001,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.850000000000001,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.850000000000001,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[37.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[37.95,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[37.35,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[37.35,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[22.65,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[22.049999999999997,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[22.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[22.65,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.350000000000001,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.350000000000001,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.350000000000001,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[37.35,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[37.95,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[37.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[37.35,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[22.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[22.049999999999997,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[22.65,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[22.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.350000000000001,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.350000000000001,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.950000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.350000000000001,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[37.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[38.45,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[37.95,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[37.95,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[22.049999999999997,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[21.549999999999997,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[22.049999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[22.049999999999997,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.950000000000003,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[7.950000000000003,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[38.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.050000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[38.45,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[38.45,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[21.549999999999997,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.949999999999996,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[21.549999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[21.549999999999997,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[9.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.450000000000003,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[38.45,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.050000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[38.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[38.45,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[21.549999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.949999999999996,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[21.549999999999997,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[21.549999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.450000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.450000000000003,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.050000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.450000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[39.050000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.550000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[39.050000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.050000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.949999999999996,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.449999999999996,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.949999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.949999999999996,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[9.550000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.050000000000004,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[9.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.050000000000004,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[39.550000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.150000000000006,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[39.550000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.550000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.449999999999996,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.849999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.449999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.449999999999996,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.550000000000004,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[9.550000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.550000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[39.550000000000004,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.150000000000006,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[39.550000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[39.550000000000004,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.449999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.849999999999994,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.449999999999996,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.449999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.550000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.550000000000004,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.150000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[9.550000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[40.150000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.650000000000006,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[40.150000000000006,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.150000000000006,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[19.849999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.349999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[19.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.849999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.150000000000006,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.150000000000006,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[40.650000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.25,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[40.650000000000006,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.650000000000006,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[19.349999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[19.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.349999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.650000000000006,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[40.650000000000006,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.25,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[40.650000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[40.650000000000006,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[19.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.75,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[19.349999999999994,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.650000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.650000000000006,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.650000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[41.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.75,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[41.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[18.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[18.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.75,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.25,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.25,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[41.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.35,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[41.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[18.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.65,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[18.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.25,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[12.350000000000001,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.75,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[41.75,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.35,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[41.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[41.75,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[18.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.65,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[18.25,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.75,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.350000000000001,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[42.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.85,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[42.35,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.35,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[17.65,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.15,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[17.65,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.65,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[12.850000000000001,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.350000000000001,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[12.350000000000001,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.350000000000001,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[42.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[43.45,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[42.85,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.85,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[17.15,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[16.549999999999997,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[17.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.15,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.850000000000001,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[12.850000000000001,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.850000000000001,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[42.85,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[43.45,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[42.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[42.85,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[17.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[16.549999999999997,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[17.15,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.15,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.850000000000001,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.850000000000001,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.450000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.850000000000001,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[43.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[43.95,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[43.45,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[43.45,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[16.549999999999997,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[16.049999999999997,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[16.549999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[16.549999999999997,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.450000000000003,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.450000000000003,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[43.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[44.550000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[43.95,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[43.95,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[16.049999999999997,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.449999999999996,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[16.049999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[16.049999999999997,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.550000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.950000000000003,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[43.95,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[44.550000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[43.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[43.95,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[16.049999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.449999999999996,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[16.049999999999997,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[16.049999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.950000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.950000000000003,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.550000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.950000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[44.550000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.050000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[44.550000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[44.550000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[15.449999999999996,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.949999999999996,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[15.449999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.449999999999996,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.550000000000004,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.550000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.550000000000004,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[45.050000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.650000000000006,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[45.050000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.050000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.949999999999996,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.349999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.949999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.949999999999996,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.050000000000004,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[45.050000000000004,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.650000000000006,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[45.050000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.050000000000004,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.949999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.349999999999994,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.949999999999996,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.949999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.050000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.050000000000004,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.650000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.050000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[45.650000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.150000000000006,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[45.650000000000006,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[45.650000000000006,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.349999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.849999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.349999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[16.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.650000000000006,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.650000000000006,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[46.150000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.75,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[46.150000000000006,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.150000000000006,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.849999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.849999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[16.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[16.150000000000006,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[16.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[16.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[46.150000000000006,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.75,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[46.150000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.150000000000006,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.25,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.849999999999994,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[16.150000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[16.150000000000006,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[16.75,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[16.150000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[46.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.25,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[46.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[46.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.75,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.25,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[16.75,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[16.75,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[16.75,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[47.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.85,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[47.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.25,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[12.75,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.149999999999999,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[12.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.75,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.85,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.25,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.25,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[47.25,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.85,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[47.25,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.25,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[12.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.149999999999999,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[12.75,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.75,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.25,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.85,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.25,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[47.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[48.35,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[47.85,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[47.85,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[12.149999999999999,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.649999999999999,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[12.149999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.149999999999999,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[18.35,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.85,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.85,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.85,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[48.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[48.95,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[48.35,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[48.35,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.649999999999999,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.049999999999997,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.649999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.649999999999999,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[18.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.35,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[18.35,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.35,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[48.35,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[48.95,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[48.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[48.35,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.649999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.049999999999997,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.649999999999999,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.649999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.35,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.35,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[18.950000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.35,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[48.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[49.45,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[48.95,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[48.95,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.049999999999997,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.549999999999997,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.049999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.049999999999997,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[19.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.950000000000003,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[18.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[18.950000000000003,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[49.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.050000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[49.45,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[49.45,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.549999999999997,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.949999999999996,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.549999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.549999999999997,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[19.450000000000003,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[19.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[19.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[49.45,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.050000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[49.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[49.45,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.549999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.949999999999996,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.549999999999997,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.549999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[19.450000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[19.450000000000003,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.050000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[19.450000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[50.050000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.550000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[50.050000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.050000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[9.949999999999996,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.449999999999996,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[9.949999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.949999999999996,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.550000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.050000000000004,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.050000000000004,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[50.550000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.150000000000006,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[50.550000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.550000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[9.449999999999996,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.849999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[9.449999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.449999999999996,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[21.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.550000000000004,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.550000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.550000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[50.550000000000004,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.150000000000006,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[50.550000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[50.550000000000004,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[9.449999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.849999999999994,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[9.449999999999996,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[9.449999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.550000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.550000000000004,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[21.150000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[20.550000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[51.150000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.650000000000006,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[51.150000000000006,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.150000000000006,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.849999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.349999999999994,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.849999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.849999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[21.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.150000000000006,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[21.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.150000000000006,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[51.650000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.25000000000001,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[51.650000000000006,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.650000000000006,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.349999999999994,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.749999999999993,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.349999999999994,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[22.250000000000007,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.650000000000006,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[21.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[51.650000000000006,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.25000000000001,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[51.650000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[51.650000000000006,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.749999999999993,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.349999999999994,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.349999999999994,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[21.650000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.650000000000006,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[22.250000000000007,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[21.650000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[52.25000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.75000000000001,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[52.25000000000001,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.25000000000001,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.749999999999993,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.249999999999993,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.749999999999993,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.749999999999993,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[22.750000000000007,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[22.250000000000007,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[22.250000000000007,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[22.250000000000007,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[52.75000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.35,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[52.75000000000001,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.75000000000001,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.249999999999993,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.649999999999999,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.249999999999993,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.249999999999993,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[23.35,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[22.750000000000007,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[22.750000000000007,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[22.750000000000007,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[52.75000000000001,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.35,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[52.75000000000001,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[52.75000000000001,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.249999999999993,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.649999999999999,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.249999999999993,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[7.249999999999993,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[22.750000000000007,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[22.750000000000007,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.35,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[22.750000000000007,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[53.35,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.85,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[53.35,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.35,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.649999999999999,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.149999999999999,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.649999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.649999999999999,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[23.85,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.35,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[23.35,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.35,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[53.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[54.45,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[53.85,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.85,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.149999999999999,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.549999999999997,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.149999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.149999999999999,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[24.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.85,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[23.85,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.85,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[53.85,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[54.45,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[53.85,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[53.85,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.149999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.549999999999997,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.149999999999999,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.149999999999999,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[23.85,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.85,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.450000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[23.85,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[54.45,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[54.95,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[54.45,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[54.45,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.549999999999997,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.049999999999997,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.549999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.549999999999997,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[24.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.450000000000003,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[24.450000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.450000000000003,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[54.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[55.550000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[54.95,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[54.95,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.049999999999997,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.449999999999996,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.049999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.049999999999997,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[25.550000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.950000000000003,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[24.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.950000000000003,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[54.95,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[55.550000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[54.95,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[54.95,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.049999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.449999999999996,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.049999999999997,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.049999999999997,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[24.950000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.950000000000003,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[25.550000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[24.950000000000003,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[55.550000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.050000000000004,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[55.550000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[55.550000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.449999999999996,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.9499999999999957,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.449999999999996,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.449999999999996,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[26.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[25.550000000000004,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[25.550000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[25.550000000000004,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[56.050000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.650000000000006,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[56.050000000000004,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.050000000000004,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.9499999999999957,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.3499999999999943,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.9499999999999957,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.9499999999999957,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[26.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.050000000000004,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[26.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.050000000000004,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[56.050000000000004,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.650000000000006,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[56.050000000000004,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.050000000000004,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.9499999999999957,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.3499999999999943,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.9499999999999957,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.9499999999999957,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[26.050000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.050000000000004,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[26.650000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.050000000000004,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[56.650000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[57.150000000000006,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[56.650000000000006,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[56.650000000000006,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.3499999999999943,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.8499999999999943,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.3499999999999943,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.3499999999999943,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[27.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.650000000000006,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[26.650000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[26.650000000000006,-0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[57.150000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[60.0,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[57.150000000000006,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[57.150000000000006,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.8499999999999943,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.8499999999999943,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.8499999999999943,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.2,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[30.0,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[27.150000000000006,0.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[27.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[27.150000000000006,-0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[57.150000000000006,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[60.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[57.150000000000006,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[57.150000000000006,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.8499999999999943,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.8499999999999943,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.8499999999999943,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.2,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[27.150000000000006,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[27.150000000000006,-0.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[30.0,0.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[27.150000000000006,0.1]}],"surfaces":[{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,-0.1],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,-0.1],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-27.25,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-27.25,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-26.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-26.15,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-26.15,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-25.65,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-25.05,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-25.05,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-24.55,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-23.95,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-23.95,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-23.45,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-22.85,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-22.85,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-22.35,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-21.75,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-21.75,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-21.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-20.65,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-20.65,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-20.15,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-19.549999999999997,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-19.549999999999997,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-19.049999999999997,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-18.45,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-18.45,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-17.95,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-17.35,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-17.35,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-16.85,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-16.25,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-16.25,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-15.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-15.149999999999999,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-15.149999999999999,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-14.649999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-14.049999999999999,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-14.049999999999999,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-13.549999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-12.95,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-12.95,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-12.45,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-11.849999999999998,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-11.849999999999998,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-11.349999999999998,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-10.75,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-10.75,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-10.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-9.649999999999999,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-9.649999999999999,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-9.149999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-8.549999999999997,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-8.549999999999997,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-8.049999999999997,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-7.449999999999999,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-7.449999999999999,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-6.949999999999999,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-6.349999999999998,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-6.349999999999998,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-5.849999999999998,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-5.25,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-5.25,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-4.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-4.149999999999999,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-4.149999999999999,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-3.6499999999999986,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-3.049999999999997,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-3.049999999999997,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-2.549999999999997,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-1.9499999999999993,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-1.9499999999999993,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-1.4499999999999993,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-0.8499999999999979,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-0.8499999999999979,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-0.34999999999999787,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.25000000000000355,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.25000000000000355,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.7500000000000036,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.3500000000000014,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.3500000000000014,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.8500000000000014,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.450000000000003,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.450000000000003,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.950000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.5500000000000043,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.5500000000000043,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.050000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.650000000000002,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.650000000000002,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[5.150000000000002,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[5.75,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[5.75,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[6.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.850000000000001,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.850000000000001,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[7.350000000000001,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[7.950000000000003,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[7.950000000000003,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[8.450000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[9.050000000000004,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[9.050000000000004,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[9.550000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.150000000000006,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.150000000000006,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[10.650000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[11.25,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[11.25,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[11.75,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[12.350000000000001,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[12.350000000000001,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[12.850000000000001,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[13.450000000000003,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[13.450000000000003,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[13.950000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[14.550000000000004,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[14.550000000000004,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[15.050000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[15.650000000000006,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[15.650000000000006,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[16.150000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[16.75,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[16.75,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[17.25,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[17.85,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[17.85,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[18.35,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[18.950000000000003,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[18.950000000000003,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[19.450000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.050000000000004,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.050000000000004,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[20.550000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[21.150000000000006,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[21.150000000000006,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[21.650000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[22.250000000000007,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[22.250000000000007,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[22.750000000000007,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[23.35,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[23.35,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[23.85,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[24.450000000000003,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[24.450000000000003,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[24.950000000000003,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[25.550000000000004,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[25.550000000000004,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[26.050000000000004,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[26.650000000000006,0.0,0.2],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[26.650000000000006,2.0,0.2],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,0.1],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-30.0,0.0,0.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,-0.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[27.150000000000006,0.0,0.2],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[30.0,0.0,0.1],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"}]},"id":"batch-planar.fifty-openings","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.75,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.75,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.75,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":13},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.75,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":7,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-8,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":11,"id":9,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":12,"id":10,"tolerance":1e-8,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.75,"lo":0.0}},"halfedge":16,"id":11,"tolerance":1e-8,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":17,"id":12,"tolerance":1e-8,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":19,"id":13,"tolerance":1e-8,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":21,"id":14,"tolerance":1e-8,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":2.75,"lo":0.0}},"halfedge":22,"id":15,"tolerance":1e-8,"twin_halfedge":33},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":23,"id":16,"tolerance":1e-8,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":25,"id":17,"tolerance":1e-8,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":29,"id":18,"tolerance":1e-8,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":31,"id":19,"tolerance":1e-8,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":36,"id":20,"tolerance":1e-8,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":37,"id":21,"tolerance":1e-8,"twin_halfedge":55},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":38,"id":22,"tolerance":1e-8,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":40,"id":23,"tolerance":1e-8,"twin_halfedge":45},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":41,"id":24,"tolerance":1e-8,"twin_halfedge":59},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":42,"id":25,"tolerance":1e-8,"twin_halfedge":49},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":44,"id":26,"tolerance":1e-8,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":50,"id":27,"tolerance":1e-8,"twin_halfedge":75},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":52,"id":28,"tolerance":1e-8,"twin_halfedge":66},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":53,"id":29,"tolerance":1e-8,"twin_halfedge":87},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":54,"id":30,"tolerance":1e-8,"twin_halfedge":63},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":56,"id":31,"tolerance":1e-8,"twin_halfedge":61},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":57,"id":32,"tolerance":1e-8,"twin_halfedge":91},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":58,"id":33,"tolerance":1e-8,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":60,"id":34,"tolerance":1e-8,"twin_halfedge":94},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":64,"id":35,"tolerance":1e-8,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":65,"id":36,"tolerance":1e-8,"twin_halfedge":76},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":67,"id":37,"tolerance":1e-8,"twin_halfedge":74},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":69,"id":38,"tolerance":1e-8,"twin_halfedge":78},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":70,"id":39,"tolerance":1e-8,"twin_halfedge":81},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":71,"id":40,"tolerance":1e-8,"twin_halfedge":72},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":73,"id":41,"tolerance":1e-8,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":77,"id":42,"tolerance":1e-8,"twin_halfedge":82},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":79,"id":43,"tolerance":1e-8,"twin_halfedge":96},{"chart_seam":false,"geometry":{"curve":44,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":84,"id":44,"tolerance":1e-8,"twin_halfedge":99},{"chart_seam":false,"geometry":{"curve":45,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":85,"id":45,"tolerance":1e-8,"twin_halfedge":103},{"chart_seam":false,"geometry":{"curve":46,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":86,"id":46,"tolerance":1e-8,"twin_halfedge":95},{"chart_seam":false,"geometry":{"curve":47,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":88,"id":47,"tolerance":1e-8,"twin_halfedge":93},{"chart_seam":false,"geometry":{"curve":48,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":89,"id":48,"tolerance":1e-8,"twin_halfedge":107},{"chart_seam":false,"geometry":{"curve":49,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":90,"id":49,"tolerance":1e-8,"twin_halfedge":97},{"chart_seam":false,"geometry":{"curve":50,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":92,"id":50,"tolerance":1e-8,"twin_halfedge":110},{"chart_seam":false,"geometry":{"curve":51,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":98,"id":51,"tolerance":1e-8,"twin_halfedge":123},{"chart_seam":false,"geometry":{"curve":52,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":100,"id":52,"tolerance":1e-8,"twin_halfedge":114},{"chart_seam":false,"geometry":{"curve":53,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":101,"id":53,"tolerance":1e-8,"twin_halfedge":135},{"chart_seam":false,"geometry":{"curve":54,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":102,"id":54,"tolerance":1e-8,"twin_halfedge":111},{"chart_seam":false,"geometry":{"curve":55,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":104,"id":55,"tolerance":1e-8,"twin_halfedge":109},{"chart_seam":false,"geometry":{"curve":56,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":105,"id":56,"tolerance":1e-8,"twin_halfedge":139},{"chart_seam":false,"geometry":{"curve":57,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":106,"id":57,"tolerance":1e-8,"twin_halfedge":116},{"chart_seam":false,"geometry":{"curve":58,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":108,"id":58,"tolerance":1e-8,"twin_halfedge":142},{"chart_seam":false,"geometry":{"curve":59,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":112,"id":59,"tolerance":1e-8,"twin_halfedge":131},{"chart_seam":false,"geometry":{"curve":60,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":113,"id":60,"tolerance":1e-8,"twin_halfedge":124},{"chart_seam":false,"geometry":{"curve":61,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":115,"id":61,"tolerance":1e-8,"twin_halfedge":122},{"chart_seam":false,"geometry":{"curve":62,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":117,"id":62,"tolerance":1e-8,"twin_halfedge":126},{"chart_seam":false,"geometry":{"curve":63,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":118,"id":63,"tolerance":1e-8,"twin_halfedge":129},{"chart_seam":false,"geometry":{"curve":64,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":119,"id":64,"tolerance":1e-8,"twin_halfedge":120},{"chart_seam":false,"geometry":{"curve":65,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":121,"id":65,"tolerance":1e-8,"twin_halfedge":128},{"chart_seam":false,"geometry":{"curve":66,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":125,"id":66,"tolerance":1e-8,"twin_halfedge":130},{"chart_seam":false,"geometry":{"curve":67,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":127,"id":67,"tolerance":1e-8,"twin_halfedge":144},{"chart_seam":false,"geometry":{"curve":68,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":132,"id":68,"tolerance":1e-8,"twin_halfedge":147},{"chart_seam":false,"geometry":{"curve":69,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":133,"id":69,"tolerance":1e-8,"twin_halfedge":151},{"chart_seam":false,"geometry":{"curve":70,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":134,"id":70,"tolerance":1e-8,"twin_halfedge":143},{"chart_seam":false,"geometry":{"curve":71,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":136,"id":71,"tolerance":1e-8,"twin_halfedge":141},{"chart_seam":false,"geometry":{"curve":72,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":137,"id":72,"tolerance":1e-8,"twin_halfedge":155},{"chart_seam":false,"geometry":{"curve":73,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":138,"id":73,"tolerance":1e-8,"twin_halfedge":145},{"chart_seam":false,"geometry":{"curve":74,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":140,"id":74,"tolerance":1e-8,"twin_halfedge":158},{"chart_seam":false,"geometry":{"curve":75,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":146,"id":75,"tolerance":1e-8,"twin_halfedge":171},{"chart_seam":false,"geometry":{"curve":76,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":148,"id":76,"tolerance":1e-8,"twin_halfedge":162},{"chart_seam":false,"geometry":{"curve":77,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":149,"id":77,"tolerance":1e-8,"twin_halfedge":183},{"chart_seam":false,"geometry":{"curve":78,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":150,"id":78,"tolerance":1e-8,"twin_halfedge":159},{"chart_seam":false,"geometry":{"curve":79,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":152,"id":79,"tolerance":1e-8,"twin_halfedge":157},{"chart_seam":false,"geometry":{"curve":80,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":153,"id":80,"tolerance":1e-8,"twin_halfedge":187},{"chart_seam":false,"geometry":{"curve":81,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":154,"id":81,"tolerance":1e-8,"twin_halfedge":164},{"chart_seam":false,"geometry":{"curve":82,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":156,"id":82,"tolerance":1e-8,"twin_halfedge":190},{"chart_seam":false,"geometry":{"curve":83,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":160,"id":83,"tolerance":1e-8,"twin_halfedge":179},{"chart_seam":false,"geometry":{"curve":84,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":161,"id":84,"tolerance":1e-8,"twin_halfedge":172},{"chart_seam":false,"geometry":{"curve":85,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":163,"id":85,"tolerance":1e-8,"twin_halfedge":170},{"chart_seam":false,"geometry":{"curve":86,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":165,"id":86,"tolerance":1e-8,"twin_halfedge":174},{"chart_seam":false,"geometry":{"curve":87,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":166,"id":87,"tolerance":1e-8,"twin_halfedge":177},{"chart_seam":false,"geometry":{"curve":88,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":167,"id":88,"tolerance":1e-8,"twin_halfedge":168},{"chart_seam":false,"geometry":{"curve":89,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":169,"id":89,"tolerance":1e-8,"twin_halfedge":176},{"chart_seam":false,"geometry":{"curve":90,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":173,"id":90,"tolerance":1e-8,"twin_halfedge":178},{"chart_seam":false,"geometry":{"curve":91,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":175,"id":91,"tolerance":1e-8,"twin_halfedge":192},{"chart_seam":false,"geometry":{"curve":92,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":180,"id":92,"tolerance":1e-8,"twin_halfedge":195},{"chart_seam":false,"geometry":{"curve":93,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":181,"id":93,"tolerance":1e-8,"twin_halfedge":199},{"chart_seam":false,"geometry":{"curve":94,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":182,"id":94,"tolerance":1e-8,"twin_halfedge":191},{"chart_seam":false,"geometry":{"curve":95,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":184,"id":95,"tolerance":1e-8,"twin_halfedge":189},{"chart_seam":false,"geometry":{"curve":96,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":185,"id":96,"tolerance":1e-8,"twin_halfedge":203},{"chart_seam":false,"geometry":{"curve":97,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":186,"id":97,"tolerance":1e-8,"twin_halfedge":193},{"chart_seam":false,"geometry":{"curve":98,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":188,"id":98,"tolerance":1e-8,"twin_halfedge":206},{"chart_seam":false,"geometry":{"curve":99,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":194,"id":99,"tolerance":1e-8,"twin_halfedge":219},{"chart_seam":false,"geometry":{"curve":100,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":196,"id":100,"tolerance":1e-8,"twin_halfedge":210},{"chart_seam":false,"geometry":{"curve":101,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":197,"id":101,"tolerance":1e-8,"twin_halfedge":231},{"chart_seam":false,"geometry":{"curve":102,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":198,"id":102,"tolerance":1e-8,"twin_halfedge":207},{"chart_seam":false,"geometry":{"curve":103,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":200,"id":103,"tolerance":1e-8,"twin_halfedge":205},{"chart_seam":false,"geometry":{"curve":104,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":201,"id":104,"tolerance":1e-8,"twin_halfedge":235},{"chart_seam":false,"geometry":{"curve":105,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":202,"id":105,"tolerance":1e-8,"twin_halfedge":212},{"chart_seam":false,"geometry":{"curve":106,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":204,"id":106,"tolerance":1e-8,"twin_halfedge":238},{"chart_seam":false,"geometry":{"curve":107,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":208,"id":107,"tolerance":1e-8,"twin_halfedge":227},{"chart_seam":false,"geometry":{"curve":108,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":209,"id":108,"tolerance":1e-8,"twin_halfedge":220},{"chart_seam":false,"geometry":{"curve":109,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":211,"id":109,"tolerance":1e-8,"twin_halfedge":218},{"chart_seam":false,"geometry":{"curve":110,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":213,"id":110,"tolerance":1e-8,"twin_halfedge":222},{"chart_seam":false,"geometry":{"curve":111,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":214,"id":111,"tolerance":1e-8,"twin_halfedge":225},{"chart_seam":false,"geometry":{"curve":112,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":215,"id":112,"tolerance":1e-8,"twin_halfedge":216},{"chart_seam":false,"geometry":{"curve":113,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":217,"id":113,"tolerance":1e-8,"twin_halfedge":224},{"chart_seam":false,"geometry":{"curve":114,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":221,"id":114,"tolerance":1e-8,"twin_halfedge":226},{"chart_seam":false,"geometry":{"curve":115,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":223,"id":115,"tolerance":1e-8,"twin_halfedge":240},{"chart_seam":false,"geometry":{"curve":116,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":228,"id":116,"tolerance":1e-8,"twin_halfedge":243},{"chart_seam":false,"geometry":{"curve":117,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":229,"id":117,"tolerance":1e-8,"twin_halfedge":247},{"chart_seam":false,"geometry":{"curve":118,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":230,"id":118,"tolerance":1e-8,"twin_halfedge":239},{"chart_seam":false,"geometry":{"curve":119,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":232,"id":119,"tolerance":1e-8,"twin_halfedge":237},{"chart_seam":false,"geometry":{"curve":120,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":233,"id":120,"tolerance":1e-8,"twin_halfedge":251},{"chart_seam":false,"geometry":{"curve":121,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":234,"id":121,"tolerance":1e-8,"twin_halfedge":241},{"chart_seam":false,"geometry":{"curve":122,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":236,"id":122,"tolerance":1e-8,"twin_halfedge":254},{"chart_seam":false,"geometry":{"curve":123,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":242,"id":123,"tolerance":1e-8,"twin_halfedge":267},{"chart_seam":false,"geometry":{"curve":124,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":244,"id":124,"tolerance":1e-8,"twin_halfedge":258},{"chart_seam":false,"geometry":{"curve":125,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":245,"id":125,"tolerance":1e-8,"twin_halfedge":279},{"chart_seam":false,"geometry":{"curve":126,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":246,"id":126,"tolerance":1e-8,"twin_halfedge":255},{"chart_seam":false,"geometry":{"curve":127,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":248,"id":127,"tolerance":1e-8,"twin_halfedge":253},{"chart_seam":false,"geometry":{"curve":128,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":249,"id":128,"tolerance":1e-8,"twin_halfedge":283},{"chart_seam":false,"geometry":{"curve":129,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":250,"id":129,"tolerance":1e-8,"twin_halfedge":260},{"chart_seam":false,"geometry":{"curve":130,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":252,"id":130,"tolerance":1e-8,"twin_halfedge":286},{"chart_seam":false,"geometry":{"curve":131,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":256,"id":131,"tolerance":1e-8,"twin_halfedge":275},{"chart_seam":false,"geometry":{"curve":132,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":257,"id":132,"tolerance":1e-8,"twin_halfedge":268},{"chart_seam":false,"geometry":{"curve":133,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":259,"id":133,"tolerance":1e-8,"twin_halfedge":266},{"chart_seam":false,"geometry":{"curve":134,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":261,"id":134,"tolerance":1e-8,"twin_halfedge":270},{"chart_seam":false,"geometry":{"curve":135,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":262,"id":135,"tolerance":1e-8,"twin_halfedge":273},{"chart_seam":false,"geometry":{"curve":136,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":263,"id":136,"tolerance":1e-8,"twin_halfedge":264},{"chart_seam":false,"geometry":{"curve":137,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":265,"id":137,"tolerance":1e-8,"twin_halfedge":272},{"chart_seam":false,"geometry":{"curve":138,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":269,"id":138,"tolerance":1e-8,"twin_halfedge":274},{"chart_seam":false,"geometry":{"curve":139,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":271,"id":139,"tolerance":1e-8,"twin_halfedge":288},{"chart_seam":false,"geometry":{"curve":140,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":276,"id":140,"tolerance":1e-8,"twin_halfedge":291},{"chart_seam":false,"geometry":{"curve":141,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":277,"id":141,"tolerance":1e-8,"twin_halfedge":295},{"chart_seam":false,"geometry":{"curve":142,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":278,"id":142,"tolerance":1e-8,"twin_halfedge":287},{"chart_seam":false,"geometry":{"curve":143,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":280,"id":143,"tolerance":1e-8,"twin_halfedge":285},{"chart_seam":false,"geometry":{"curve":144,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":281,"id":144,"tolerance":1e-8,"twin_halfedge":299},{"chart_seam":false,"geometry":{"curve":145,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":282,"id":145,"tolerance":1e-8,"twin_halfedge":289},{"chart_seam":false,"geometry":{"curve":146,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":284,"id":146,"tolerance":1e-8,"twin_halfedge":302},{"chart_seam":false,"geometry":{"curve":147,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":290,"id":147,"tolerance":1e-8,"twin_halfedge":315},{"chart_seam":false,"geometry":{"curve":148,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":292,"id":148,"tolerance":1e-8,"twin_halfedge":306},{"chart_seam":false,"geometry":{"curve":149,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":293,"id":149,"tolerance":1e-8,"twin_halfedge":327},{"chart_seam":false,"geometry":{"curve":150,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":294,"id":150,"tolerance":1e-8,"twin_halfedge":303},{"chart_seam":false,"geometry":{"curve":151,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":296,"id":151,"tolerance":1e-8,"twin_halfedge":301},{"chart_seam":false,"geometry":{"curve":152,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":297,"id":152,"tolerance":1e-8,"twin_halfedge":331},{"chart_seam":false,"geometry":{"curve":153,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":298,"id":153,"tolerance":1e-8,"twin_halfedge":308},{"chart_seam":false,"geometry":{"curve":154,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":300,"id":154,"tolerance":1e-8,"twin_halfedge":334},{"chart_seam":false,"geometry":{"curve":155,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":304,"id":155,"tolerance":1e-8,"twin_halfedge":323},{"chart_seam":false,"geometry":{"curve":156,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":305,"id":156,"tolerance":1e-8,"twin_halfedge":316},{"chart_seam":false,"geometry":{"curve":157,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":307,"id":157,"tolerance":1e-8,"twin_halfedge":314},{"chart_seam":false,"geometry":{"curve":158,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":309,"id":158,"tolerance":1e-8,"twin_halfedge":318},{"chart_seam":false,"geometry":{"curve":159,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":310,"id":159,"tolerance":1e-8,"twin_halfedge":321},{"chart_seam":false,"geometry":{"curve":160,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":311,"id":160,"tolerance":1e-8,"twin_halfedge":312},{"chart_seam":false,"geometry":{"curve":161,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":313,"id":161,"tolerance":1e-8,"twin_halfedge":320},{"chart_seam":false,"geometry":{"curve":162,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":317,"id":162,"tolerance":1e-8,"twin_halfedge":322},{"chart_seam":false,"geometry":{"curve":163,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":319,"id":163,"tolerance":1e-8,"twin_halfedge":336},{"chart_seam":false,"geometry":{"curve":164,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":324,"id":164,"tolerance":1e-8,"twin_halfedge":339},{"chart_seam":false,"geometry":{"curve":165,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":325,"id":165,"tolerance":1e-8,"twin_halfedge":343},{"chart_seam":false,"geometry":{"curve":166,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":326,"id":166,"tolerance":1e-8,"twin_halfedge":335},{"chart_seam":false,"geometry":{"curve":167,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":328,"id":167,"tolerance":1e-8,"twin_halfedge":333},{"chart_seam":false,"geometry":{"curve":168,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":329,"id":168,"tolerance":1e-8,"twin_halfedge":347},{"chart_seam":false,"geometry":{"curve":169,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":330,"id":169,"tolerance":1e-8,"twin_halfedge":337},{"chart_seam":false,"geometry":{"curve":170,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":332,"id":170,"tolerance":1e-8,"twin_halfedge":350},{"chart_seam":false,"geometry":{"curve":171,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":338,"id":171,"tolerance":1e-8,"twin_halfedge":363},{"chart_seam":false,"geometry":{"curve":172,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":340,"id":172,"tolerance":1e-8,"twin_halfedge":354},{"chart_seam":false,"geometry":{"curve":173,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":341,"id":173,"tolerance":1e-8,"twin_halfedge":375},{"chart_seam":false,"geometry":{"curve":174,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":342,"id":174,"tolerance":1e-8,"twin_halfedge":351},{"chart_seam":false,"geometry":{"curve":175,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":344,"id":175,"tolerance":1e-8,"twin_halfedge":349},{"chart_seam":false,"geometry":{"curve":176,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":345,"id":176,"tolerance":1e-8,"twin_halfedge":379},{"chart_seam":false,"geometry":{"curve":177,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":346,"id":177,"tolerance":1e-8,"twin_halfedge":356},{"chart_seam":false,"geometry":{"curve":178,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":348,"id":178,"tolerance":1e-8,"twin_halfedge":382},{"chart_seam":false,"geometry":{"curve":179,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":352,"id":179,"tolerance":1e-8,"twin_halfedge":371},{"chart_seam":false,"geometry":{"curve":180,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":353,"id":180,"tolerance":1e-8,"twin_halfedge":364},{"chart_seam":false,"geometry":{"curve":181,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":355,"id":181,"tolerance":1e-8,"twin_halfedge":362},{"chart_seam":false,"geometry":{"curve":182,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":357,"id":182,"tolerance":1e-8,"twin_halfedge":366},{"chart_seam":false,"geometry":{"curve":183,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":358,"id":183,"tolerance":1e-8,"twin_halfedge":369},{"chart_seam":false,"geometry":{"curve":184,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":359,"id":184,"tolerance":1e-8,"twin_halfedge":360},{"chart_seam":false,"geometry":{"curve":185,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":361,"id":185,"tolerance":1e-8,"twin_halfedge":368},{"chart_seam":false,"geometry":{"curve":186,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":365,"id":186,"tolerance":1e-8,"twin_halfedge":370},{"chart_seam":false,"geometry":{"curve":187,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":367,"id":187,"tolerance":1e-8,"twin_halfedge":384},{"chart_seam":false,"geometry":{"curve":188,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":372,"id":188,"tolerance":1e-8,"twin_halfedge":387},{"chart_seam":false,"geometry":{"curve":189,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":373,"id":189,"tolerance":1e-8,"twin_halfedge":391},{"chart_seam":false,"geometry":{"curve":190,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":374,"id":190,"tolerance":1e-8,"twin_halfedge":383},{"chart_seam":false,"geometry":{"curve":191,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":376,"id":191,"tolerance":1e-8,"twin_halfedge":381},{"chart_seam":false,"geometry":{"curve":192,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":377,"id":192,"tolerance":1e-8,"twin_halfedge":395},{"chart_seam":false,"geometry":{"curve":193,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":378,"id":193,"tolerance":1e-8,"twin_halfedge":385},{"chart_seam":false,"geometry":{"curve":194,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":380,"id":194,"tolerance":1e-8,"twin_halfedge":398},{"chart_seam":false,"geometry":{"curve":195,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":386,"id":195,"tolerance":1e-8,"twin_halfedge":411},{"chart_seam":false,"geometry":{"curve":196,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":388,"id":196,"tolerance":1e-8,"twin_halfedge":402},{"chart_seam":false,"geometry":{"curve":197,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":389,"id":197,"tolerance":1e-8,"twin_halfedge":423},{"chart_seam":false,"geometry":{"curve":198,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":390,"id":198,"tolerance":1e-8,"twin_halfedge":399},{"chart_seam":false,"geometry":{"curve":199,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":392,"id":199,"tolerance":1e-8,"twin_halfedge":397},{"chart_seam":false,"geometry":{"curve":200,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":393,"id":200,"tolerance":1e-8,"twin_halfedge":427},{"chart_seam":false,"geometry":{"curve":201,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":394,"id":201,"tolerance":1e-8,"twin_halfedge":404},{"chart_seam":false,"geometry":{"curve":202,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":396,"id":202,"tolerance":1e-8,"twin_halfedge":430},{"chart_seam":false,"geometry":{"curve":203,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":400,"id":203,"tolerance":1e-8,"twin_halfedge":419},{"chart_seam":false,"geometry":{"curve":204,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":401,"id":204,"tolerance":1e-8,"twin_halfedge":412},{"chart_seam":false,"geometry":{"curve":205,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":403,"id":205,"tolerance":1e-8,"twin_halfedge":410},{"chart_seam":false,"geometry":{"curve":206,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":405,"id":206,"tolerance":1e-8,"twin_halfedge":414},{"chart_seam":false,"geometry":{"curve":207,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":406,"id":207,"tolerance":1e-8,"twin_halfedge":417},{"chart_seam":false,"geometry":{"curve":208,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":407,"id":208,"tolerance":1e-8,"twin_halfedge":408},{"chart_seam":false,"geometry":{"curve":209,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":409,"id":209,"tolerance":1e-8,"twin_halfedge":416},{"chart_seam":false,"geometry":{"curve":210,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":413,"id":210,"tolerance":1e-8,"twin_halfedge":418},{"chart_seam":false,"geometry":{"curve":211,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":415,"id":211,"tolerance":1e-8,"twin_halfedge":432},{"chart_seam":false,"geometry":{"curve":212,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":420,"id":212,"tolerance":1e-8,"twin_halfedge":435},{"chart_seam":false,"geometry":{"curve":213,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":421,"id":213,"tolerance":1e-8,"twin_halfedge":439},{"chart_seam":false,"geometry":{"curve":214,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":422,"id":214,"tolerance":1e-8,"twin_halfedge":431},{"chart_seam":false,"geometry":{"curve":215,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":424,"id":215,"tolerance":1e-8,"twin_halfedge":429},{"chart_seam":false,"geometry":{"curve":216,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":425,"id":216,"tolerance":1e-8,"twin_halfedge":443},{"chart_seam":false,"geometry":{"curve":217,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":426,"id":217,"tolerance":1e-8,"twin_halfedge":433},{"chart_seam":false,"geometry":{"curve":218,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":428,"id":218,"tolerance":1e-8,"twin_halfedge":446},{"chart_seam":false,"geometry":{"curve":219,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":434,"id":219,"tolerance":1e-8,"twin_halfedge":459},{"chart_seam":false,"geometry":{"curve":220,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":436,"id":220,"tolerance":1e-8,"twin_halfedge":450},{"chart_seam":false,"geometry":{"curve":221,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":437,"id":221,"tolerance":1e-8,"twin_halfedge":471},{"chart_seam":false,"geometry":{"curve":222,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":438,"id":222,"tolerance":1e-8,"twin_halfedge":447},{"chart_seam":false,"geometry":{"curve":223,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":440,"id":223,"tolerance":1e-8,"twin_halfedge":445},{"chart_seam":false,"geometry":{"curve":224,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":441,"id":224,"tolerance":1e-8,"twin_halfedge":475},{"chart_seam":false,"geometry":{"curve":225,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":442,"id":225,"tolerance":1e-8,"twin_halfedge":452},{"chart_seam":false,"geometry":{"curve":226,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":444,"id":226,"tolerance":1e-8,"twin_halfedge":478},{"chart_seam":false,"geometry":{"curve":227,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":448,"id":227,"tolerance":1e-8,"twin_halfedge":467},{"chart_seam":false,"geometry":{"curve":228,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":449,"id":228,"tolerance":1e-8,"twin_halfedge":460},{"chart_seam":false,"geometry":{"curve":229,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":451,"id":229,"tolerance":1e-8,"twin_halfedge":458},{"chart_seam":false,"geometry":{"curve":230,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":453,"id":230,"tolerance":1e-8,"twin_halfedge":462},{"chart_seam":false,"geometry":{"curve":231,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":454,"id":231,"tolerance":1e-8,"twin_halfedge":465},{"chart_seam":false,"geometry":{"curve":232,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":455,"id":232,"tolerance":1e-8,"twin_halfedge":456},{"chart_seam":false,"geometry":{"curve":233,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":457,"id":233,"tolerance":1e-8,"twin_halfedge":464},{"chart_seam":false,"geometry":{"curve":234,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":461,"id":234,"tolerance":1e-8,"twin_halfedge":466},{"chart_seam":false,"geometry":{"curve":235,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":463,"id":235,"tolerance":1e-8,"twin_halfedge":480},{"chart_seam":false,"geometry":{"curve":236,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":468,"id":236,"tolerance":1e-8,"twin_halfedge":483},{"chart_seam":false,"geometry":{"curve":237,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":469,"id":237,"tolerance":1e-8,"twin_halfedge":487},{"chart_seam":false,"geometry":{"curve":238,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":470,"id":238,"tolerance":1e-8,"twin_halfedge":479},{"chart_seam":false,"geometry":{"curve":239,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":472,"id":239,"tolerance":1e-8,"twin_halfedge":477},{"chart_seam":false,"geometry":{"curve":240,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":473,"id":240,"tolerance":1e-8,"twin_halfedge":491},{"chart_seam":false,"geometry":{"curve":241,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":474,"id":241,"tolerance":1e-8,"twin_halfedge":481},{"chart_seam":false,"geometry":{"curve":242,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":476,"id":242,"tolerance":1e-8,"twin_halfedge":494},{"chart_seam":false,"geometry":{"curve":243,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":482,"id":243,"tolerance":1e-8,"twin_halfedge":507},{"chart_seam":false,"geometry":{"curve":244,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":484,"id":244,"tolerance":1e-8,"twin_halfedge":498},{"chart_seam":false,"geometry":{"curve":245,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":485,"id":245,"tolerance":1e-8,"twin_halfedge":519},{"chart_seam":false,"geometry":{"curve":246,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":486,"id":246,"tolerance":1e-8,"twin_halfedge":495},{"chart_seam":false,"geometry":{"curve":247,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":488,"id":247,"tolerance":1e-8,"twin_halfedge":493},{"chart_seam":false,"geometry":{"curve":248,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":489,"id":248,"tolerance":1e-8,"twin_halfedge":523},{"chart_seam":false,"geometry":{"curve":249,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":490,"id":249,"tolerance":1e-8,"twin_halfedge":500},{"chart_seam":false,"geometry":{"curve":250,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":492,"id":250,"tolerance":1e-8,"twin_halfedge":526},{"chart_seam":false,"geometry":{"curve":251,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":496,"id":251,"tolerance":1e-8,"twin_halfedge":515},{"chart_seam":false,"geometry":{"curve":252,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":497,"id":252,"tolerance":1e-8,"twin_halfedge":508},{"chart_seam":false,"geometry":{"curve":253,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":499,"id":253,"tolerance":1e-8,"twin_halfedge":506},{"chart_seam":false,"geometry":{"curve":254,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":501,"id":254,"tolerance":1e-8,"twin_halfedge":510},{"chart_seam":false,"geometry":{"curve":255,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":502,"id":255,"tolerance":1e-8,"twin_halfedge":513},{"chart_seam":false,"geometry":{"curve":256,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":503,"id":256,"tolerance":1e-8,"twin_halfedge":504},{"chart_seam":false,"geometry":{"curve":257,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":505,"id":257,"tolerance":1e-8,"twin_halfedge":512},{"chart_seam":false,"geometry":{"curve":258,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":509,"id":258,"tolerance":1e-8,"twin_halfedge":514},{"chart_seam":false,"geometry":{"curve":259,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":511,"id":259,"tolerance":1e-8,"twin_halfedge":528},{"chart_seam":false,"geometry":{"curve":260,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":516,"id":260,"tolerance":1e-8,"twin_halfedge":531},{"chart_seam":false,"geometry":{"curve":261,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":517,"id":261,"tolerance":1e-8,"twin_halfedge":535},{"chart_seam":false,"geometry":{"curve":262,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":518,"id":262,"tolerance":1e-8,"twin_halfedge":527},{"chart_seam":false,"geometry":{"curve":263,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":520,"id":263,"tolerance":1e-8,"twin_halfedge":525},{"chart_seam":false,"geometry":{"curve":264,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":521,"id":264,"tolerance":1e-8,"twin_halfedge":539},{"chart_seam":false,"geometry":{"curve":265,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":522,"id":265,"tolerance":1e-8,"twin_halfedge":529},{"chart_seam":false,"geometry":{"curve":266,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":524,"id":266,"tolerance":1e-8,"twin_halfedge":542},{"chart_seam":false,"geometry":{"curve":267,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":530,"id":267,"tolerance":1e-8,"twin_halfedge":555},{"chart_seam":false,"geometry":{"curve":268,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":532,"id":268,"tolerance":1e-8,"twin_halfedge":546},{"chart_seam":false,"geometry":{"curve":269,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":533,"id":269,"tolerance":1e-8,"twin_halfedge":567},{"chart_seam":false,"geometry":{"curve":270,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":534,"id":270,"tolerance":1e-8,"twin_halfedge":543},{"chart_seam":false,"geometry":{"curve":271,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":536,"id":271,"tolerance":1e-8,"twin_halfedge":541},{"chart_seam":false,"geometry":{"curve":272,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":537,"id":272,"tolerance":1e-8,"twin_halfedge":571},{"chart_seam":false,"geometry":{"curve":273,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":538,"id":273,"tolerance":1e-8,"twin_halfedge":548},{"chart_seam":false,"geometry":{"curve":274,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":540,"id":274,"tolerance":1e-8,"twin_halfedge":574},{"chart_seam":false,"geometry":{"curve":275,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":544,"id":275,"tolerance":1e-8,"twin_halfedge":563},{"chart_seam":false,"geometry":{"curve":276,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":545,"id":276,"tolerance":1e-8,"twin_halfedge":556},{"chart_seam":false,"geometry":{"curve":277,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":547,"id":277,"tolerance":1e-8,"twin_halfedge":554},{"chart_seam":false,"geometry":{"curve":278,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":549,"id":278,"tolerance":1e-8,"twin_halfedge":558},{"chart_seam":false,"geometry":{"curve":279,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":550,"id":279,"tolerance":1e-8,"twin_halfedge":561},{"chart_seam":false,"geometry":{"curve":280,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":551,"id":280,"tolerance":1e-8,"twin_halfedge":552},{"chart_seam":false,"geometry":{"curve":281,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":553,"id":281,"tolerance":1e-8,"twin_halfedge":560},{"chart_seam":false,"geometry":{"curve":282,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":557,"id":282,"tolerance":1e-8,"twin_halfedge":562},{"chart_seam":false,"geometry":{"curve":283,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":559,"id":283,"tolerance":1e-8,"twin_halfedge":576},{"chart_seam":false,"geometry":{"curve":284,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":564,"id":284,"tolerance":1e-8,"twin_halfedge":579},{"chart_seam":false,"geometry":{"curve":285,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":565,"id":285,"tolerance":1e-8,"twin_halfedge":583},{"chart_seam":false,"geometry":{"curve":286,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":566,"id":286,"tolerance":1e-8,"twin_halfedge":575},{"chart_seam":false,"geometry":{"curve":287,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":568,"id":287,"tolerance":1e-8,"twin_halfedge":573},{"chart_seam":false,"geometry":{"curve":288,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":569,"id":288,"tolerance":1e-8,"twin_halfedge":587},{"chart_seam":false,"geometry":{"curve":289,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":570,"id":289,"tolerance":1e-8,"twin_halfedge":577},{"chart_seam":false,"geometry":{"curve":290,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":572,"id":290,"tolerance":1e-8,"twin_halfedge":590},{"chart_seam":false,"geometry":{"curve":291,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":578,"id":291,"tolerance":1e-8,"twin_halfedge":603},{"chart_seam":false,"geometry":{"curve":292,"kind":"Curve","range":{"hi":0.5999999999999996,"lo":0.0}},"halfedge":580,"id":292,"tolerance":1e-8,"twin_halfedge":594},{"chart_seam":false,"geometry":{"curve":293,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":581,"id":293,"tolerance":1e-8,"twin_halfedge":615},{"chart_seam":false,"geometry":{"curve":294,"kind":"Curve","range":{"hi":0.5999999999999996,"lo":0.0}},"halfedge":582,"id":294,"tolerance":1e-8,"twin_halfedge":591},{"chart_seam":false,"geometry":{"curve":295,"kind":"Curve","range":{"hi":0.5999999999999996,"lo":0.0}},"halfedge":584,"id":295,"tolerance":1e-8,"twin_halfedge":589},{"chart_seam":false,"geometry":{"curve":296,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":585,"id":296,"tolerance":1e-8,"twin_halfedge":619},{"chart_seam":false,"geometry":{"curve":297,"kind":"Curve","range":{"hi":0.5999999999999996,"lo":0.0}},"halfedge":586,"id":297,"tolerance":1e-8,"twin_halfedge":596},{"chart_seam":false,"geometry":{"curve":298,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":588,"id":298,"tolerance":1e-8,"twin_halfedge":622},{"chart_seam":false,"geometry":{"curve":299,"kind":"Curve","range":{"hi":0.5999999999999996,"lo":0.0}},"halfedge":592,"id":299,"tolerance":1e-8,"twin_halfedge":611},{"chart_seam":false,"geometry":{"curve":300,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":593,"id":300,"tolerance":1e-8,"twin_halfedge":604},{"chart_seam":false,"geometry":{"curve":301,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":595,"id":301,"tolerance":1e-8,"twin_halfedge":602},{"chart_seam":false,"geometry":{"curve":302,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":597,"id":302,"tolerance":1e-8,"twin_halfedge":606},{"chart_seam":false,"geometry":{"curve":303,"kind":"Curve","range":{"hi":0.5999999999999996,"lo":0.0}},"halfedge":598,"id":303,"tolerance":1e-8,"twin_halfedge":609},{"chart_seam":false,"geometry":{"curve":304,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":599,"id":304,"tolerance":1e-8,"twin_halfedge":600},{"chart_seam":false,"geometry":{"curve":305,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":601,"id":305,"tolerance":1e-8,"twin_halfedge":608},{"chart_seam":false,"geometry":{"curve":306,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":605,"id":306,"tolerance":1e-8,"twin_halfedge":610},{"chart_seam":false,"geometry":{"curve":307,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":607,"id":307,"tolerance":1e-8,"twin_halfedge":624},{"chart_seam":false,"geometry":{"curve":308,"kind":"Curve","range":{"hi":0.5000000000000018,"lo":0.0}},"halfedge":612,"id":308,"tolerance":1e-8,"twin_halfedge":627},{"chart_seam":false,"geometry":{"curve":309,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":613,"id":309,"tolerance":1e-8,"twin_halfedge":631},{"chart_seam":false,"geometry":{"curve":310,"kind":"Curve","range":{"hi":0.5000000000000018,"lo":0.0}},"halfedge":614,"id":310,"tolerance":1e-8,"twin_halfedge":623},{"chart_seam":false,"geometry":{"curve":311,"kind":"Curve","range":{"hi":0.5000000000000018,"lo":0.0}},"halfedge":616,"id":311,"tolerance":1e-8,"twin_halfedge":621},{"chart_seam":false,"geometry":{"curve":312,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":617,"id":312,"tolerance":1e-8,"twin_halfedge":635},{"chart_seam":false,"geometry":{"curve":313,"kind":"Curve","range":{"hi":0.5000000000000018,"lo":0.0}},"halfedge":618,"id":313,"tolerance":1e-8,"twin_halfedge":625},{"chart_seam":false,"geometry":{"curve":314,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":620,"id":314,"tolerance":1e-8,"twin_halfedge":638},{"chart_seam":false,"geometry":{"curve":315,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":626,"id":315,"tolerance":1e-8,"twin_halfedge":651},{"chart_seam":false,"geometry":{"curve":316,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":628,"id":316,"tolerance":1e-8,"twin_halfedge":642},{"chart_seam":false,"geometry":{"curve":317,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":629,"id":317,"tolerance":1e-8,"twin_halfedge":663},{"chart_seam":false,"geometry":{"curve":318,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":630,"id":318,"tolerance":1e-8,"twin_halfedge":639},{"chart_seam":false,"geometry":{"curve":319,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":632,"id":319,"tolerance":1e-8,"twin_halfedge":637},{"chart_seam":false,"geometry":{"curve":320,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":633,"id":320,"tolerance":1e-8,"twin_halfedge":667},{"chart_seam":false,"geometry":{"curve":321,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":634,"id":321,"tolerance":1e-8,"twin_halfedge":644},{"chart_seam":false,"geometry":{"curve":322,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":636,"id":322,"tolerance":1e-8,"twin_halfedge":670},{"chart_seam":false,"geometry":{"curve":323,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":640,"id":323,"tolerance":1e-8,"twin_halfedge":659},{"chart_seam":false,"geometry":{"curve":324,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":641,"id":324,"tolerance":1e-8,"twin_halfedge":652},{"chart_seam":false,"geometry":{"curve":325,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":643,"id":325,"tolerance":1e-8,"twin_halfedge":650},{"chart_seam":false,"geometry":{"curve":326,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":645,"id":326,"tolerance":1e-8,"twin_halfedge":654},{"chart_seam":false,"geometry":{"curve":327,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":646,"id":327,"tolerance":1e-8,"twin_halfedge":657},{"chart_seam":false,"geometry":{"curve":328,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":647,"id":328,"tolerance":1e-8,"twin_halfedge":648},{"chart_seam":false,"geometry":{"curve":329,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":649,"id":329,"tolerance":1e-8,"twin_halfedge":656},{"chart_seam":false,"geometry":{"curve":330,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":653,"id":330,"tolerance":1e-8,"twin_halfedge":658},{"chart_seam":false,"geometry":{"curve":331,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":655,"id":331,"tolerance":1e-8,"twin_halfedge":672},{"chart_seam":false,"geometry":{"curve":332,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":660,"id":332,"tolerance":1e-8,"twin_halfedge":675},{"chart_seam":false,"geometry":{"curve":333,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":661,"id":333,"tolerance":1e-8,"twin_halfedge":679},{"chart_seam":false,"geometry":{"curve":334,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":662,"id":334,"tolerance":1e-8,"twin_halfedge":671},{"chart_seam":false,"geometry":{"curve":335,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":664,"id":335,"tolerance":1e-8,"twin_halfedge":669},{"chart_seam":false,"geometry":{"curve":336,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":665,"id":336,"tolerance":1e-8,"twin_halfedge":683},{"chart_seam":false,"geometry":{"curve":337,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":666,"id":337,"tolerance":1e-8,"twin_halfedge":673},{"chart_seam":false,"geometry":{"curve":338,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":668,"id":338,"tolerance":1e-8,"twin_halfedge":686},{"chart_seam":false,"geometry":{"curve":339,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":674,"id":339,"tolerance":1e-8,"twin_halfedge":699},{"chart_seam":false,"geometry":{"curve":340,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":676,"id":340,"tolerance":1e-8,"twin_halfedge":690},{"chart_seam":false,"geometry":{"curve":341,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":677,"id":341,"tolerance":1e-8,"twin_halfedge":711},{"chart_seam":false,"geometry":{"curve":342,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":678,"id":342,"tolerance":1e-8,"twin_halfedge":687},{"chart_seam":false,"geometry":{"curve":343,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":680,"id":343,"tolerance":1e-8,"twin_halfedge":685},{"chart_seam":false,"geometry":{"curve":344,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":681,"id":344,"tolerance":1e-8,"twin_halfedge":715},{"chart_seam":false,"geometry":{"curve":345,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":682,"id":345,"tolerance":1e-8,"twin_halfedge":692},{"chart_seam":false,"geometry":{"curve":346,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":684,"id":346,"tolerance":1e-8,"twin_halfedge":718},{"chart_seam":false,"geometry":{"curve":347,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":688,"id":347,"tolerance":1e-8,"twin_halfedge":707},{"chart_seam":false,"geometry":{"curve":348,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":689,"id":348,"tolerance":1e-8,"twin_halfedge":700},{"chart_seam":false,"geometry":{"curve":349,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":691,"id":349,"tolerance":1e-8,"twin_halfedge":698},{"chart_seam":false,"geometry":{"curve":350,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":693,"id":350,"tolerance":1e-8,"twin_halfedge":702},{"chart_seam":false,"geometry":{"curve":351,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":694,"id":351,"tolerance":1e-8,"twin_halfedge":705},{"chart_seam":false,"geometry":{"curve":352,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":695,"id":352,"tolerance":1e-8,"twin_halfedge":696},{"chart_seam":false,"geometry":{"curve":353,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":697,"id":353,"tolerance":1e-8,"twin_halfedge":704},{"chart_seam":false,"geometry":{"curve":354,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":701,"id":354,"tolerance":1e-8,"twin_halfedge":706},{"chart_seam":false,"geometry":{"curve":355,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":703,"id":355,"tolerance":1e-8,"twin_halfedge":720},{"chart_seam":false,"geometry":{"curve":356,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":708,"id":356,"tolerance":1e-8,"twin_halfedge":723},{"chart_seam":false,"geometry":{"curve":357,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":709,"id":357,"tolerance":1e-8,"twin_halfedge":727},{"chart_seam":false,"geometry":{"curve":358,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":710,"id":358,"tolerance":1e-8,"twin_halfedge":719},{"chart_seam":false,"geometry":{"curve":359,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":712,"id":359,"tolerance":1e-8,"twin_halfedge":717},{"chart_seam":false,"geometry":{"curve":360,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":713,"id":360,"tolerance":1e-8,"twin_halfedge":731},{"chart_seam":false,"geometry":{"curve":361,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":714,"id":361,"tolerance":1e-8,"twin_halfedge":721},{"chart_seam":false,"geometry":{"curve":362,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":716,"id":362,"tolerance":1e-8,"twin_halfedge":734},{"chart_seam":false,"geometry":{"curve":363,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":722,"id":363,"tolerance":1e-8,"twin_halfedge":747},{"chart_seam":false,"geometry":{"curve":364,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":724,"id":364,"tolerance":1e-8,"twin_halfedge":738},{"chart_seam":false,"geometry":{"curve":365,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":725,"id":365,"tolerance":1e-8,"twin_halfedge":759},{"chart_seam":false,"geometry":{"curve":366,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":726,"id":366,"tolerance":1e-8,"twin_halfedge":735},{"chart_seam":false,"geometry":{"curve":367,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":728,"id":367,"tolerance":1e-8,"twin_halfedge":733},{"chart_seam":false,"geometry":{"curve":368,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":729,"id":368,"tolerance":1e-8,"twin_halfedge":763},{"chart_seam":false,"geometry":{"curve":369,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":730,"id":369,"tolerance":1e-8,"twin_halfedge":740},{"chart_seam":false,"geometry":{"curve":370,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":732,"id":370,"tolerance":1e-8,"twin_halfedge":766},{"chart_seam":false,"geometry":{"curve":371,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":736,"id":371,"tolerance":1e-8,"twin_halfedge":755},{"chart_seam":false,"geometry":{"curve":372,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":737,"id":372,"tolerance":1e-8,"twin_halfedge":748},{"chart_seam":false,"geometry":{"curve":373,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":739,"id":373,"tolerance":1e-8,"twin_halfedge":746},{"chart_seam":false,"geometry":{"curve":374,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":741,"id":374,"tolerance":1e-8,"twin_halfedge":750},{"chart_seam":false,"geometry":{"curve":375,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":742,"id":375,"tolerance":1e-8,"twin_halfedge":753},{"chart_seam":false,"geometry":{"curve":376,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":743,"id":376,"tolerance":1e-8,"twin_halfedge":744},{"chart_seam":false,"geometry":{"curve":377,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":745,"id":377,"tolerance":1e-8,"twin_halfedge":752},{"chart_seam":false,"geometry":{"curve":378,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":749,"id":378,"tolerance":1e-8,"twin_halfedge":754},{"chart_seam":false,"geometry":{"curve":379,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":751,"id":379,"tolerance":1e-8,"twin_halfedge":768},{"chart_seam":false,"geometry":{"curve":380,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":756,"id":380,"tolerance":1e-8,"twin_halfedge":771},{"chart_seam":false,"geometry":{"curve":381,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":757,"id":381,"tolerance":1e-8,"twin_halfedge":775},{"chart_seam":false,"geometry":{"curve":382,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":758,"id":382,"tolerance":1e-8,"twin_halfedge":767},{"chart_seam":false,"geometry":{"curve":383,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":760,"id":383,"tolerance":1e-8,"twin_halfedge":765},{"chart_seam":false,"geometry":{"curve":384,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":761,"id":384,"tolerance":1e-8,"twin_halfedge":779},{"chart_seam":false,"geometry":{"curve":385,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":762,"id":385,"tolerance":1e-8,"twin_halfedge":769},{"chart_seam":false,"geometry":{"curve":386,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":764,"id":386,"tolerance":1e-8,"twin_halfedge":782},{"chart_seam":false,"geometry":{"curve":387,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":770,"id":387,"tolerance":1e-8,"twin_halfedge":795},{"chart_seam":false,"geometry":{"curve":388,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":772,"id":388,"tolerance":1e-8,"twin_halfedge":786},{"chart_seam":false,"geometry":{"curve":389,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":773,"id":389,"tolerance":1e-8,"twin_halfedge":807},{"chart_seam":false,"geometry":{"curve":390,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":774,"id":390,"tolerance":1e-8,"twin_halfedge":783},{"chart_seam":false,"geometry":{"curve":391,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":776,"id":391,"tolerance":1e-8,"twin_halfedge":781},{"chart_seam":false,"geometry":{"curve":392,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":777,"id":392,"tolerance":1e-8,"twin_halfedge":811},{"chart_seam":false,"geometry":{"curve":393,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":778,"id":393,"tolerance":1e-8,"twin_halfedge":788},{"chart_seam":false,"geometry":{"curve":394,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":780,"id":394,"tolerance":1e-8,"twin_halfedge":814},{"chart_seam":false,"geometry":{"curve":395,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":784,"id":395,"tolerance":1e-8,"twin_halfedge":803},{"chart_seam":false,"geometry":{"curve":396,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":785,"id":396,"tolerance":1e-8,"twin_halfedge":796},{"chart_seam":false,"geometry":{"curve":397,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":787,"id":397,"tolerance":1e-8,"twin_halfedge":794},{"chart_seam":false,"geometry":{"curve":398,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":789,"id":398,"tolerance":1e-8,"twin_halfedge":798},{"chart_seam":false,"geometry":{"curve":399,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":790,"id":399,"tolerance":1e-8,"twin_halfedge":801},{"chart_seam":false,"geometry":{"curve":400,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":791,"id":400,"tolerance":1e-8,"twin_halfedge":792},{"chart_seam":false,"geometry":{"curve":401,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":793,"id":401,"tolerance":1e-8,"twin_halfedge":800},{"chart_seam":false,"geometry":{"curve":402,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":797,"id":402,"tolerance":1e-8,"twin_halfedge":802},{"chart_seam":false,"geometry":{"curve":403,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":799,"id":403,"tolerance":1e-8,"twin_halfedge":816},{"chart_seam":false,"geometry":{"curve":404,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":804,"id":404,"tolerance":1e-8,"twin_halfedge":819},{"chart_seam":false,"geometry":{"curve":405,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":805,"id":405,"tolerance":1e-8,"twin_halfedge":823},{"chart_seam":false,"geometry":{"curve":406,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":806,"id":406,"tolerance":1e-8,"twin_halfedge":815},{"chart_seam":false,"geometry":{"curve":407,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":808,"id":407,"tolerance":1e-8,"twin_halfedge":813},{"chart_seam":false,"geometry":{"curve":408,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":809,"id":408,"tolerance":1e-8,"twin_halfedge":827},{"chart_seam":false,"geometry":{"curve":409,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":810,"id":409,"tolerance":1e-8,"twin_halfedge":817},{"chart_seam":false,"geometry":{"curve":410,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":812,"id":410,"tolerance":1e-8,"twin_halfedge":830},{"chart_seam":false,"geometry":{"curve":411,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":818,"id":411,"tolerance":1e-8,"twin_halfedge":843},{"chart_seam":false,"geometry":{"curve":412,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":820,"id":412,"tolerance":1e-8,"twin_halfedge":834},{"chart_seam":false,"geometry":{"curve":413,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":821,"id":413,"tolerance":1e-8,"twin_halfedge":855},{"chart_seam":false,"geometry":{"curve":414,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":822,"id":414,"tolerance":1e-8,"twin_halfedge":831},{"chart_seam":false,"geometry":{"curve":415,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":824,"id":415,"tolerance":1e-8,"twin_halfedge":829},{"chart_seam":false,"geometry":{"curve":416,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":825,"id":416,"tolerance":1e-8,"twin_halfedge":859},{"chart_seam":false,"geometry":{"curve":417,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":826,"id":417,"tolerance":1e-8,"twin_halfedge":836},{"chart_seam":false,"geometry":{"curve":418,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":828,"id":418,"tolerance":1e-8,"twin_halfedge":862},{"chart_seam":false,"geometry":{"curve":419,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":832,"id":419,"tolerance":1e-8,"twin_halfedge":851},{"chart_seam":false,"geometry":{"curve":420,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":833,"id":420,"tolerance":1e-8,"twin_halfedge":844},{"chart_seam":false,"geometry":{"curve":421,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":835,"id":421,"tolerance":1e-8,"twin_halfedge":842},{"chart_seam":false,"geometry":{"curve":422,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":837,"id":422,"tolerance":1e-8,"twin_halfedge":846},{"chart_seam":false,"geometry":{"curve":423,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":838,"id":423,"tolerance":1e-8,"twin_halfedge":849},{"chart_seam":false,"geometry":{"curve":424,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":839,"id":424,"tolerance":1e-8,"twin_halfedge":840},{"chart_seam":false,"geometry":{"curve":425,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":841,"id":425,"tolerance":1e-8,"twin_halfedge":848},{"chart_seam":false,"geometry":{"curve":426,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":845,"id":426,"tolerance":1e-8,"twin_halfedge":850},{"chart_seam":false,"geometry":{"curve":427,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":847,"id":427,"tolerance":1e-8,"twin_halfedge":864},{"chart_seam":false,"geometry":{"curve":428,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":852,"id":428,"tolerance":1e-8,"twin_halfedge":867},{"chart_seam":false,"geometry":{"curve":429,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":853,"id":429,"tolerance":1e-8,"twin_halfedge":871},{"chart_seam":false,"geometry":{"curve":430,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":854,"id":430,"tolerance":1e-8,"twin_halfedge":863},{"chart_seam":false,"geometry":{"curve":431,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":856,"id":431,"tolerance":1e-8,"twin_halfedge":861},{"chart_seam":false,"geometry":{"curve":432,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":857,"id":432,"tolerance":1e-8,"twin_halfedge":875},{"chart_seam":false,"geometry":{"curve":433,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":858,"id":433,"tolerance":1e-8,"twin_halfedge":865},{"chart_seam":false,"geometry":{"curve":434,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":860,"id":434,"tolerance":1e-8,"twin_halfedge":878},{"chart_seam":false,"geometry":{"curve":435,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":866,"id":435,"tolerance":1e-8,"twin_halfedge":891},{"chart_seam":false,"geometry":{"curve":436,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":868,"id":436,"tolerance":1e-8,"twin_halfedge":882},{"chart_seam":false,"geometry":{"curve":437,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":869,"id":437,"tolerance":1e-8,"twin_halfedge":903},{"chart_seam":false,"geometry":{"curve":438,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":870,"id":438,"tolerance":1e-8,"twin_halfedge":879},{"chart_seam":false,"geometry":{"curve":439,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":872,"id":439,"tolerance":1e-8,"twin_halfedge":877},{"chart_seam":false,"geometry":{"curve":440,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":873,"id":440,"tolerance":1e-8,"twin_halfedge":907},{"chart_seam":false,"geometry":{"curve":441,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":874,"id":441,"tolerance":1e-8,"twin_halfedge":884},{"chart_seam":false,"geometry":{"curve":442,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":876,"id":442,"tolerance":1e-8,"twin_halfedge":910},{"chart_seam":false,"geometry":{"curve":443,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":880,"id":443,"tolerance":1e-8,"twin_halfedge":899},{"chart_seam":false,"geometry":{"curve":444,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":881,"id":444,"tolerance":1e-8,"twin_halfedge":892},{"chart_seam":false,"geometry":{"curve":445,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":883,"id":445,"tolerance":1e-8,"twin_halfedge":890},{"chart_seam":false,"geometry":{"curve":446,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":885,"id":446,"tolerance":1e-8,"twin_halfedge":894},{"chart_seam":false,"geometry":{"curve":447,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":886,"id":447,"tolerance":1e-8,"twin_halfedge":897},{"chart_seam":false,"geometry":{"curve":448,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":887,"id":448,"tolerance":1e-8,"twin_halfedge":888},{"chart_seam":false,"geometry":{"curve":449,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":889,"id":449,"tolerance":1e-8,"twin_halfedge":896},{"chart_seam":false,"geometry":{"curve":450,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":893,"id":450,"tolerance":1e-8,"twin_halfedge":898},{"chart_seam":false,"geometry":{"curve":451,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":895,"id":451,"tolerance":1e-8,"twin_halfedge":912},{"chart_seam":false,"geometry":{"curve":452,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":900,"id":452,"tolerance":1e-8,"twin_halfedge":915},{"chart_seam":false,"geometry":{"curve":453,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":901,"id":453,"tolerance":1e-8,"twin_halfedge":919},{"chart_seam":false,"geometry":{"curve":454,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":902,"id":454,"tolerance":1e-8,"twin_halfedge":911},{"chart_seam":false,"geometry":{"curve":455,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":904,"id":455,"tolerance":1e-8,"twin_halfedge":909},{"chart_seam":false,"geometry":{"curve":456,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":905,"id":456,"tolerance":1e-8,"twin_halfedge":923},{"chart_seam":false,"geometry":{"curve":457,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":906,"id":457,"tolerance":1e-8,"twin_halfedge":913},{"chart_seam":false,"geometry":{"curve":458,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":908,"id":458,"tolerance":1e-8,"twin_halfedge":926},{"chart_seam":false,"geometry":{"curve":459,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":914,"id":459,"tolerance":1e-8,"twin_halfedge":939},{"chart_seam":false,"geometry":{"curve":460,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":916,"id":460,"tolerance":1e-8,"twin_halfedge":930},{"chart_seam":false,"geometry":{"curve":461,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":917,"id":461,"tolerance":1e-8,"twin_halfedge":951},{"chart_seam":false,"geometry":{"curve":462,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":918,"id":462,"tolerance":1e-8,"twin_halfedge":927},{"chart_seam":false,"geometry":{"curve":463,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":920,"id":463,"tolerance":1e-8,"twin_halfedge":925},{"chart_seam":false,"geometry":{"curve":464,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":921,"id":464,"tolerance":1e-8,"twin_halfedge":955},{"chart_seam":false,"geometry":{"curve":465,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":922,"id":465,"tolerance":1e-8,"twin_halfedge":932},{"chart_seam":false,"geometry":{"curve":466,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":924,"id":466,"tolerance":1e-8,"twin_halfedge":958},{"chart_seam":false,"geometry":{"curve":467,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":928,"id":467,"tolerance":1e-8,"twin_halfedge":947},{"chart_seam":false,"geometry":{"curve":468,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":929,"id":468,"tolerance":1e-8,"twin_halfedge":940},{"chart_seam":false,"geometry":{"curve":469,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":931,"id":469,"tolerance":1e-8,"twin_halfedge":938},{"chart_seam":false,"geometry":{"curve":470,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":933,"id":470,"tolerance":1e-8,"twin_halfedge":942},{"chart_seam":false,"geometry":{"curve":471,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":934,"id":471,"tolerance":1e-8,"twin_halfedge":945},{"chart_seam":false,"geometry":{"curve":472,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":935,"id":472,"tolerance":1e-8,"twin_halfedge":936},{"chart_seam":false,"geometry":{"curve":473,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":937,"id":473,"tolerance":1e-8,"twin_halfedge":944},{"chart_seam":false,"geometry":{"curve":474,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":941,"id":474,"tolerance":1e-8,"twin_halfedge":946},{"chart_seam":false,"geometry":{"curve":475,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":943,"id":475,"tolerance":1e-8,"twin_halfedge":960},{"chart_seam":false,"geometry":{"curve":476,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":948,"id":476,"tolerance":1e-8,"twin_halfedge":963},{"chart_seam":false,"geometry":{"curve":477,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":949,"id":477,"tolerance":1e-8,"twin_halfedge":967},{"chart_seam":false,"geometry":{"curve":478,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":950,"id":478,"tolerance":1e-8,"twin_halfedge":959},{"chart_seam":false,"geometry":{"curve":479,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":952,"id":479,"tolerance":1e-8,"twin_halfedge":957},{"chart_seam":false,"geometry":{"curve":480,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":953,"id":480,"tolerance":1e-8,"twin_halfedge":971},{"chart_seam":false,"geometry":{"curve":481,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":954,"id":481,"tolerance":1e-8,"twin_halfedge":961},{"chart_seam":false,"geometry":{"curve":482,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":956,"id":482,"tolerance":1e-8,"twin_halfedge":974},{"chart_seam":false,"geometry":{"curve":483,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":962,"id":483,"tolerance":1e-8,"twin_halfedge":987},{"chart_seam":false,"geometry":{"curve":484,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":964,"id":484,"tolerance":1e-8,"twin_halfedge":978},{"chart_seam":false,"geometry":{"curve":485,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":965,"id":485,"tolerance":1e-8,"twin_halfedge":999},{"chart_seam":false,"geometry":{"curve":486,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":966,"id":486,"tolerance":1e-8,"twin_halfedge":975},{"chart_seam":false,"geometry":{"curve":487,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":968,"id":487,"tolerance":1e-8,"twin_halfedge":973},{"chart_seam":false,"geometry":{"curve":488,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":969,"id":488,"tolerance":1e-8,"twin_halfedge":1003},{"chart_seam":false,"geometry":{"curve":489,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":970,"id":489,"tolerance":1e-8,"twin_halfedge":980},{"chart_seam":false,"geometry":{"curve":490,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":972,"id":490,"tolerance":1e-8,"twin_halfedge":1006},{"chart_seam":false,"geometry":{"curve":491,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":976,"id":491,"tolerance":1e-8,"twin_halfedge":995},{"chart_seam":false,"geometry":{"curve":492,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":977,"id":492,"tolerance":1e-8,"twin_halfedge":988},{"chart_seam":false,"geometry":{"curve":493,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":979,"id":493,"tolerance":1e-8,"twin_halfedge":986},{"chart_seam":false,"geometry":{"curve":494,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":981,"id":494,"tolerance":1e-8,"twin_halfedge":990},{"chart_seam":false,"geometry":{"curve":495,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":982,"id":495,"tolerance":1e-8,"twin_halfedge":993},{"chart_seam":false,"geometry":{"curve":496,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":983,"id":496,"tolerance":1e-8,"twin_halfedge":984},{"chart_seam":false,"geometry":{"curve":497,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":985,"id":497,"tolerance":1e-8,"twin_halfedge":992},{"chart_seam":false,"geometry":{"curve":498,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":989,"id":498,"tolerance":1e-8,"twin_halfedge":994},{"chart_seam":false,"geometry":{"curve":499,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":991,"id":499,"tolerance":1e-8,"twin_halfedge":1008},{"chart_seam":false,"geometry":{"curve":500,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":996,"id":500,"tolerance":1e-8,"twin_halfedge":1011},{"chart_seam":false,"geometry":{"curve":501,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":997,"id":501,"tolerance":1e-8,"twin_halfedge":1015},{"chart_seam":false,"geometry":{"curve":502,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":998,"id":502,"tolerance":1e-8,"twin_halfedge":1007},{"chart_seam":false,"geometry":{"curve":503,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1000,"id":503,"tolerance":1e-8,"twin_halfedge":1005},{"chart_seam":false,"geometry":{"curve":504,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1001,"id":504,"tolerance":1e-8,"twin_halfedge":1019},{"chart_seam":false,"geometry":{"curve":505,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1002,"id":505,"tolerance":1e-8,"twin_halfedge":1009},{"chart_seam":false,"geometry":{"curve":506,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1004,"id":506,"tolerance":1e-8,"twin_halfedge":1022},{"chart_seam":false,"geometry":{"curve":507,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1010,"id":507,"tolerance":1e-8,"twin_halfedge":1035},{"chart_seam":false,"geometry":{"curve":508,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1012,"id":508,"tolerance":1e-8,"twin_halfedge":1026},{"chart_seam":false,"geometry":{"curve":509,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1013,"id":509,"tolerance":1e-8,"twin_halfedge":1047},{"chart_seam":false,"geometry":{"curve":510,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1014,"id":510,"tolerance":1e-8,"twin_halfedge":1023},{"chart_seam":false,"geometry":{"curve":511,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1016,"id":511,"tolerance":1e-8,"twin_halfedge":1021},{"chart_seam":false,"geometry":{"curve":512,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1017,"id":512,"tolerance":1e-8,"twin_halfedge":1051},{"chart_seam":false,"geometry":{"curve":513,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1018,"id":513,"tolerance":1e-8,"twin_halfedge":1028},{"chart_seam":false,"geometry":{"curve":514,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1020,"id":514,"tolerance":1e-8,"twin_halfedge":1054},{"chart_seam":false,"geometry":{"curve":515,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1024,"id":515,"tolerance":1e-8,"twin_halfedge":1043},{"chart_seam":false,"geometry":{"curve":516,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1025,"id":516,"tolerance":1e-8,"twin_halfedge":1036},{"chart_seam":false,"geometry":{"curve":517,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1027,"id":517,"tolerance":1e-8,"twin_halfedge":1034},{"chart_seam":false,"geometry":{"curve":518,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1029,"id":518,"tolerance":1e-8,"twin_halfedge":1038},{"chart_seam":false,"geometry":{"curve":519,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1030,"id":519,"tolerance":1e-8,"twin_halfedge":1041},{"chart_seam":false,"geometry":{"curve":520,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1031,"id":520,"tolerance":1e-8,"twin_halfedge":1032},{"chart_seam":false,"geometry":{"curve":521,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1033,"id":521,"tolerance":1e-8,"twin_halfedge":1040},{"chart_seam":false,"geometry":{"curve":522,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1037,"id":522,"tolerance":1e-8,"twin_halfedge":1042},{"chart_seam":false,"geometry":{"curve":523,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1039,"id":523,"tolerance":1e-8,"twin_halfedge":1056},{"chart_seam":false,"geometry":{"curve":524,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1044,"id":524,"tolerance":1e-8,"twin_halfedge":1059},{"chart_seam":false,"geometry":{"curve":525,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1045,"id":525,"tolerance":1e-8,"twin_halfedge":1063},{"chart_seam":false,"geometry":{"curve":526,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1046,"id":526,"tolerance":1e-8,"twin_halfedge":1055},{"chart_seam":false,"geometry":{"curve":527,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1048,"id":527,"tolerance":1e-8,"twin_halfedge":1053},{"chart_seam":false,"geometry":{"curve":528,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1049,"id":528,"tolerance":1e-8,"twin_halfedge":1067},{"chart_seam":false,"geometry":{"curve":529,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1050,"id":529,"tolerance":1e-8,"twin_halfedge":1057},{"chart_seam":false,"geometry":{"curve":530,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1052,"id":530,"tolerance":1e-8,"twin_halfedge":1070},{"chart_seam":false,"geometry":{"curve":531,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1058,"id":531,"tolerance":1e-8,"twin_halfedge":1083},{"chart_seam":false,"geometry":{"curve":532,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1060,"id":532,"tolerance":1e-8,"twin_halfedge":1074},{"chart_seam":false,"geometry":{"curve":533,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1061,"id":533,"tolerance":1e-8,"twin_halfedge":1095},{"chart_seam":false,"geometry":{"curve":534,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1062,"id":534,"tolerance":1e-8,"twin_halfedge":1071},{"chart_seam":false,"geometry":{"curve":535,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1064,"id":535,"tolerance":1e-8,"twin_halfedge":1069},{"chart_seam":false,"geometry":{"curve":536,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1065,"id":536,"tolerance":1e-8,"twin_halfedge":1099},{"chart_seam":false,"geometry":{"curve":537,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1066,"id":537,"tolerance":1e-8,"twin_halfedge":1076},{"chart_seam":false,"geometry":{"curve":538,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1068,"id":538,"tolerance":1e-8,"twin_halfedge":1102},{"chart_seam":false,"geometry":{"curve":539,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1072,"id":539,"tolerance":1e-8,"twin_halfedge":1091},{"chart_seam":false,"geometry":{"curve":540,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1073,"id":540,"tolerance":1e-8,"twin_halfedge":1084},{"chart_seam":false,"geometry":{"curve":541,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1075,"id":541,"tolerance":1e-8,"twin_halfedge":1082},{"chart_seam":false,"geometry":{"curve":542,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1077,"id":542,"tolerance":1e-8,"twin_halfedge":1086},{"chart_seam":false,"geometry":{"curve":543,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1078,"id":543,"tolerance":1e-8,"twin_halfedge":1089},{"chart_seam":false,"geometry":{"curve":544,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1079,"id":544,"tolerance":1e-8,"twin_halfedge":1080},{"chart_seam":false,"geometry":{"curve":545,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1081,"id":545,"tolerance":1e-8,"twin_halfedge":1088},{"chart_seam":false,"geometry":{"curve":546,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1085,"id":546,"tolerance":1e-8,"twin_halfedge":1090},{"chart_seam":false,"geometry":{"curve":547,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1087,"id":547,"tolerance":1e-8,"twin_halfedge":1104},{"chart_seam":false,"geometry":{"curve":548,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1092,"id":548,"tolerance":1e-8,"twin_halfedge":1107},{"chart_seam":false,"geometry":{"curve":549,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1093,"id":549,"tolerance":1e-8,"twin_halfedge":1111},{"chart_seam":false,"geometry":{"curve":550,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1094,"id":550,"tolerance":1e-8,"twin_halfedge":1103},{"chart_seam":false,"geometry":{"curve":551,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1096,"id":551,"tolerance":1e-8,"twin_halfedge":1101},{"chart_seam":false,"geometry":{"curve":552,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1097,"id":552,"tolerance":1e-8,"twin_halfedge":1115},{"chart_seam":false,"geometry":{"curve":553,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1098,"id":553,"tolerance":1e-8,"twin_halfedge":1105},{"chart_seam":false,"geometry":{"curve":554,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1100,"id":554,"tolerance":1e-8,"twin_halfedge":1118},{"chart_seam":false,"geometry":{"curve":555,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1106,"id":555,"tolerance":1e-8,"twin_halfedge":1131},{"chart_seam":false,"geometry":{"curve":556,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1108,"id":556,"tolerance":1e-8,"twin_halfedge":1122},{"chart_seam":false,"geometry":{"curve":557,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1109,"id":557,"tolerance":1e-8,"twin_halfedge":1143},{"chart_seam":false,"geometry":{"curve":558,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1110,"id":558,"tolerance":1e-8,"twin_halfedge":1119},{"chart_seam":false,"geometry":{"curve":559,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1112,"id":559,"tolerance":1e-8,"twin_halfedge":1117},{"chart_seam":false,"geometry":{"curve":560,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1113,"id":560,"tolerance":1e-8,"twin_halfedge":1147},{"chart_seam":false,"geometry":{"curve":561,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1114,"id":561,"tolerance":1e-8,"twin_halfedge":1124},{"chart_seam":false,"geometry":{"curve":562,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1116,"id":562,"tolerance":1e-8,"twin_halfedge":1150},{"chart_seam":false,"geometry":{"curve":563,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1120,"id":563,"tolerance":1e-8,"twin_halfedge":1139},{"chart_seam":false,"geometry":{"curve":564,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1121,"id":564,"tolerance":1e-8,"twin_halfedge":1132},{"chart_seam":false,"geometry":{"curve":565,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1123,"id":565,"tolerance":1e-8,"twin_halfedge":1130},{"chart_seam":false,"geometry":{"curve":566,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1125,"id":566,"tolerance":1e-8,"twin_halfedge":1134},{"chart_seam":false,"geometry":{"curve":567,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1126,"id":567,"tolerance":1e-8,"twin_halfedge":1137},{"chart_seam":false,"geometry":{"curve":568,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1127,"id":568,"tolerance":1e-8,"twin_halfedge":1128},{"chart_seam":false,"geometry":{"curve":569,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1129,"id":569,"tolerance":1e-8,"twin_halfedge":1136},{"chart_seam":false,"geometry":{"curve":570,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1133,"id":570,"tolerance":1e-8,"twin_halfedge":1138},{"chart_seam":false,"geometry":{"curve":571,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1135,"id":571,"tolerance":1e-8,"twin_halfedge":1152},{"chart_seam":false,"geometry":{"curve":572,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1140,"id":572,"tolerance":1e-8,"twin_halfedge":1155},{"chart_seam":false,"geometry":{"curve":573,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1141,"id":573,"tolerance":1e-8,"twin_halfedge":1159},{"chart_seam":false,"geometry":{"curve":574,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1142,"id":574,"tolerance":1e-8,"twin_halfedge":1151},{"chart_seam":false,"geometry":{"curve":575,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1144,"id":575,"tolerance":1e-8,"twin_halfedge":1149},{"chart_seam":false,"geometry":{"curve":576,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1145,"id":576,"tolerance":1e-8,"twin_halfedge":1163},{"chart_seam":false,"geometry":{"curve":577,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1146,"id":577,"tolerance":1e-8,"twin_halfedge":1153},{"chart_seam":false,"geometry":{"curve":578,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1148,"id":578,"tolerance":1e-8,"twin_halfedge":1166},{"chart_seam":false,"geometry":{"curve":579,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1154,"id":579,"tolerance":1e-8,"twin_halfedge":1179},{"chart_seam":false,"geometry":{"curve":580,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1156,"id":580,"tolerance":1e-8,"twin_halfedge":1170},{"chart_seam":false,"geometry":{"curve":581,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1157,"id":581,"tolerance":1e-8,"twin_halfedge":1191},{"chart_seam":false,"geometry":{"curve":582,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1158,"id":582,"tolerance":1e-8,"twin_halfedge":1167},{"chart_seam":false,"geometry":{"curve":583,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1160,"id":583,"tolerance":1e-8,"twin_halfedge":1165},{"chart_seam":false,"geometry":{"curve":584,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1161,"id":584,"tolerance":1e-8,"twin_halfedge":1195},{"chart_seam":false,"geometry":{"curve":585,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1162,"id":585,"tolerance":1e-8,"twin_halfedge":1172},{"chart_seam":false,"geometry":{"curve":586,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1164,"id":586,"tolerance":1e-8,"twin_halfedge":1198},{"chart_seam":false,"geometry":{"curve":587,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1168,"id":587,"tolerance":1e-8,"twin_halfedge":1187},{"chart_seam":false,"geometry":{"curve":588,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1169,"id":588,"tolerance":1e-8,"twin_halfedge":1180},{"chart_seam":false,"geometry":{"curve":589,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1171,"id":589,"tolerance":1e-8,"twin_halfedge":1178},{"chart_seam":false,"geometry":{"curve":590,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1173,"id":590,"tolerance":1e-8,"twin_halfedge":1182},{"chart_seam":false,"geometry":{"curve":591,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1174,"id":591,"tolerance":1e-8,"twin_halfedge":1185},{"chart_seam":false,"geometry":{"curve":592,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1175,"id":592,"tolerance":1e-8,"twin_halfedge":1176},{"chart_seam":false,"geometry":{"curve":593,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1177,"id":593,"tolerance":1e-8,"twin_halfedge":1184},{"chart_seam":false,"geometry":{"curve":594,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1181,"id":594,"tolerance":1e-8,"twin_halfedge":1186},{"chart_seam":false,"geometry":{"curve":595,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1183,"id":595,"tolerance":1e-8,"twin_halfedge":1200},{"chart_seam":false,"geometry":{"curve":596,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1188,"id":596,"tolerance":1e-8,"twin_halfedge":1203},{"chart_seam":false,"geometry":{"curve":597,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1189,"id":597,"tolerance":1e-8,"twin_halfedge":1207},{"chart_seam":false,"geometry":{"curve":598,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1190,"id":598,"tolerance":1e-8,"twin_halfedge":1199},{"chart_seam":false,"geometry":{"curve":599,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1192,"id":599,"tolerance":1e-8,"twin_halfedge":1197},{"chart_seam":false,"geometry":{"curve":600,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1193,"id":600,"tolerance":1e-8,"twin_halfedge":1211},{"chart_seam":false,"geometry":{"curve":601,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1194,"id":601,"tolerance":1e-8,"twin_halfedge":1201},{"chart_seam":false,"geometry":{"curve":602,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1196,"id":602,"tolerance":1e-8,"twin_halfedge":1214},{"chart_seam":false,"geometry":{"curve":603,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1202,"id":603,"tolerance":1e-8,"twin_halfedge":1227},{"chart_seam":false,"geometry":{"curve":604,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1204,"id":604,"tolerance":1e-8,"twin_halfedge":1218},{"chart_seam":false,"geometry":{"curve":605,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1205,"id":605,"tolerance":1e-8,"twin_halfedge":1239},{"chart_seam":false,"geometry":{"curve":606,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1206,"id":606,"tolerance":1e-8,"twin_halfedge":1215},{"chart_seam":false,"geometry":{"curve":607,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1208,"id":607,"tolerance":1e-8,"twin_halfedge":1213},{"chart_seam":false,"geometry":{"curve":608,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1209,"id":608,"tolerance":1e-8,"twin_halfedge":1243},{"chart_seam":false,"geometry":{"curve":609,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1210,"id":609,"tolerance":1e-8,"twin_halfedge":1220},{"chart_seam":false,"geometry":{"curve":610,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1212,"id":610,"tolerance":1e-8,"twin_halfedge":1246},{"chart_seam":false,"geometry":{"curve":611,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1216,"id":611,"tolerance":1e-8,"twin_halfedge":1235},{"chart_seam":false,"geometry":{"curve":612,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1217,"id":612,"tolerance":1e-8,"twin_halfedge":1228},{"chart_seam":false,"geometry":{"curve":613,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1219,"id":613,"tolerance":1e-8,"twin_halfedge":1226},{"chart_seam":false,"geometry":{"curve":614,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1221,"id":614,"tolerance":1e-8,"twin_halfedge":1230},{"chart_seam":false,"geometry":{"curve":615,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1222,"id":615,"tolerance":1e-8,"twin_halfedge":1233},{"chart_seam":false,"geometry":{"curve":616,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1223,"id":616,"tolerance":1e-8,"twin_halfedge":1224},{"chart_seam":false,"geometry":{"curve":617,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1225,"id":617,"tolerance":1e-8,"twin_halfedge":1232},{"chart_seam":false,"geometry":{"curve":618,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1229,"id":618,"tolerance":1e-8,"twin_halfedge":1234},{"chart_seam":false,"geometry":{"curve":619,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1231,"id":619,"tolerance":1e-8,"twin_halfedge":1248},{"chart_seam":false,"geometry":{"curve":620,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1236,"id":620,"tolerance":1e-8,"twin_halfedge":1251},{"chart_seam":false,"geometry":{"curve":621,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1237,"id":621,"tolerance":1e-8,"twin_halfedge":1255},{"chart_seam":false,"geometry":{"curve":622,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1238,"id":622,"tolerance":1e-8,"twin_halfedge":1247},{"chart_seam":false,"geometry":{"curve":623,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1240,"id":623,"tolerance":1e-8,"twin_halfedge":1245},{"chart_seam":false,"geometry":{"curve":624,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1241,"id":624,"tolerance":1e-8,"twin_halfedge":1259},{"chart_seam":false,"geometry":{"curve":625,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1242,"id":625,"tolerance":1e-8,"twin_halfedge":1249},{"chart_seam":false,"geometry":{"curve":626,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1244,"id":626,"tolerance":1e-8,"twin_halfedge":1262},{"chart_seam":false,"geometry":{"curve":627,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1250,"id":627,"tolerance":1e-8,"twin_halfedge":1275},{"chart_seam":false,"geometry":{"curve":628,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1252,"id":628,"tolerance":1e-8,"twin_halfedge":1266},{"chart_seam":false,"geometry":{"curve":629,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1253,"id":629,"tolerance":1e-8,"twin_halfedge":1287},{"chart_seam":false,"geometry":{"curve":630,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1254,"id":630,"tolerance":1e-8,"twin_halfedge":1263},{"chart_seam":false,"geometry":{"curve":631,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1256,"id":631,"tolerance":1e-8,"twin_halfedge":1261},{"chart_seam":false,"geometry":{"curve":632,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1257,"id":632,"tolerance":1e-8,"twin_halfedge":1291},{"chart_seam":false,"geometry":{"curve":633,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1258,"id":633,"tolerance":1e-8,"twin_halfedge":1268},{"chart_seam":false,"geometry":{"curve":634,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1260,"id":634,"tolerance":1e-8,"twin_halfedge":1294},{"chart_seam":false,"geometry":{"curve":635,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1264,"id":635,"tolerance":1e-8,"twin_halfedge":1283},{"chart_seam":false,"geometry":{"curve":636,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1265,"id":636,"tolerance":1e-8,"twin_halfedge":1276},{"chart_seam":false,"geometry":{"curve":637,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1267,"id":637,"tolerance":1e-8,"twin_halfedge":1274},{"chart_seam":false,"geometry":{"curve":638,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1269,"id":638,"tolerance":1e-8,"twin_halfedge":1278},{"chart_seam":false,"geometry":{"curve":639,"kind":"Curve","range":{"hi":0.5999999999999979,"lo":0.0}},"halfedge":1270,"id":639,"tolerance":1e-8,"twin_halfedge":1281},{"chart_seam":false,"geometry":{"curve":640,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1271,"id":640,"tolerance":1e-8,"twin_halfedge":1272},{"chart_seam":false,"geometry":{"curve":641,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1273,"id":641,"tolerance":1e-8,"twin_halfedge":1280},{"chart_seam":false,"geometry":{"curve":642,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1277,"id":642,"tolerance":1e-8,"twin_halfedge":1282},{"chart_seam":false,"geometry":{"curve":643,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1279,"id":643,"tolerance":1e-8,"twin_halfedge":1296},{"chart_seam":false,"geometry":{"curve":644,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1284,"id":644,"tolerance":1e-8,"twin_halfedge":1299},{"chart_seam":false,"geometry":{"curve":645,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1285,"id":645,"tolerance":1e-8,"twin_halfedge":1303},{"chart_seam":false,"geometry":{"curve":646,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1286,"id":646,"tolerance":1e-8,"twin_halfedge":1295},{"chart_seam":false,"geometry":{"curve":647,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1288,"id":647,"tolerance":1e-8,"twin_halfedge":1293},{"chart_seam":false,"geometry":{"curve":648,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1289,"id":648,"tolerance":1e-8,"twin_halfedge":1307},{"chart_seam":false,"geometry":{"curve":649,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1290,"id":649,"tolerance":1e-8,"twin_halfedge":1297},{"chart_seam":false,"geometry":{"curve":650,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1292,"id":650,"tolerance":1e-8,"twin_halfedge":1310},{"chart_seam":false,"geometry":{"curve":651,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1298,"id":651,"tolerance":1e-8,"twin_halfedge":1323},{"chart_seam":false,"geometry":{"curve":652,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1300,"id":652,"tolerance":1e-8,"twin_halfedge":1314},{"chart_seam":false,"geometry":{"curve":653,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1301,"id":653,"tolerance":1e-8,"twin_halfedge":1335},{"chart_seam":false,"geometry":{"curve":654,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1302,"id":654,"tolerance":1e-8,"twin_halfedge":1311},{"chart_seam":false,"geometry":{"curve":655,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1304,"id":655,"tolerance":1e-8,"twin_halfedge":1309},{"chart_seam":false,"geometry":{"curve":656,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1305,"id":656,"tolerance":1e-8,"twin_halfedge":1339},{"chart_seam":false,"geometry":{"curve":657,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1306,"id":657,"tolerance":1e-8,"twin_halfedge":1316},{"chart_seam":false,"geometry":{"curve":658,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1308,"id":658,"tolerance":1e-8,"twin_halfedge":1342},{"chart_seam":false,"geometry":{"curve":659,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1312,"id":659,"tolerance":1e-8,"twin_halfedge":1331},{"chart_seam":false,"geometry":{"curve":660,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1313,"id":660,"tolerance":1e-8,"twin_halfedge":1324},{"chart_seam":false,"geometry":{"curve":661,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1315,"id":661,"tolerance":1e-8,"twin_halfedge":1322},{"chart_seam":false,"geometry":{"curve":662,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1317,"id":662,"tolerance":1e-8,"twin_halfedge":1326},{"chart_seam":false,"geometry":{"curve":663,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1318,"id":663,"tolerance":1e-8,"twin_halfedge":1329},{"chart_seam":false,"geometry":{"curve":664,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1319,"id":664,"tolerance":1e-8,"twin_halfedge":1320},{"chart_seam":false,"geometry":{"curve":665,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1321,"id":665,"tolerance":1e-8,"twin_halfedge":1328},{"chart_seam":false,"geometry":{"curve":666,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1325,"id":666,"tolerance":1e-8,"twin_halfedge":1330},{"chart_seam":false,"geometry":{"curve":667,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1327,"id":667,"tolerance":1e-8,"twin_halfedge":1344},{"chart_seam":false,"geometry":{"curve":668,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1332,"id":668,"tolerance":1e-8,"twin_halfedge":1347},{"chart_seam":false,"geometry":{"curve":669,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1333,"id":669,"tolerance":1e-8,"twin_halfedge":1351},{"chart_seam":false,"geometry":{"curve":670,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1334,"id":670,"tolerance":1e-8,"twin_halfedge":1343},{"chart_seam":false,"geometry":{"curve":671,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1336,"id":671,"tolerance":1e-8,"twin_halfedge":1341},{"chart_seam":false,"geometry":{"curve":672,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1337,"id":672,"tolerance":1e-8,"twin_halfedge":1355},{"chart_seam":false,"geometry":{"curve":673,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1338,"id":673,"tolerance":1e-8,"twin_halfedge":1345},{"chart_seam":false,"geometry":{"curve":674,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1340,"id":674,"tolerance":1e-8,"twin_halfedge":1358},{"chart_seam":false,"geometry":{"curve":675,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1346,"id":675,"tolerance":1e-8,"twin_halfedge":1371},{"chart_seam":false,"geometry":{"curve":676,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1348,"id":676,"tolerance":1e-8,"twin_halfedge":1362},{"chart_seam":false,"geometry":{"curve":677,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1349,"id":677,"tolerance":1e-8,"twin_halfedge":1383},{"chart_seam":false,"geometry":{"curve":678,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1350,"id":678,"tolerance":1e-8,"twin_halfedge":1359},{"chart_seam":false,"geometry":{"curve":679,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1352,"id":679,"tolerance":1e-8,"twin_halfedge":1357},{"chart_seam":false,"geometry":{"curve":680,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1353,"id":680,"tolerance":1e-8,"twin_halfedge":1387},{"chart_seam":false,"geometry":{"curve":681,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1354,"id":681,"tolerance":1e-8,"twin_halfedge":1364},{"chart_seam":false,"geometry":{"curve":682,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1356,"id":682,"tolerance":1e-8,"twin_halfedge":1390},{"chart_seam":false,"geometry":{"curve":683,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1360,"id":683,"tolerance":1e-8,"twin_halfedge":1379},{"chart_seam":false,"geometry":{"curve":684,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1361,"id":684,"tolerance":1e-8,"twin_halfedge":1372},{"chart_seam":false,"geometry":{"curve":685,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1363,"id":685,"tolerance":1e-8,"twin_halfedge":1370},{"chart_seam":false,"geometry":{"curve":686,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1365,"id":686,"tolerance":1e-8,"twin_halfedge":1374},{"chart_seam":false,"geometry":{"curve":687,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1366,"id":687,"tolerance":1e-8,"twin_halfedge":1377},{"chart_seam":false,"geometry":{"curve":688,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1367,"id":688,"tolerance":1e-8,"twin_halfedge":1368},{"chart_seam":false,"geometry":{"curve":689,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1369,"id":689,"tolerance":1e-8,"twin_halfedge":1376},{"chart_seam":false,"geometry":{"curve":690,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1373,"id":690,"tolerance":1e-8,"twin_halfedge":1378},{"chart_seam":false,"geometry":{"curve":691,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1375,"id":691,"tolerance":1e-8,"twin_halfedge":1392},{"chart_seam":false,"geometry":{"curve":692,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1380,"id":692,"tolerance":1e-8,"twin_halfedge":1395},{"chart_seam":false,"geometry":{"curve":693,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1381,"id":693,"tolerance":1e-8,"twin_halfedge":1399},{"chart_seam":false,"geometry":{"curve":694,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1382,"id":694,"tolerance":1e-8,"twin_halfedge":1391},{"chart_seam":false,"geometry":{"curve":695,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1384,"id":695,"tolerance":1e-8,"twin_halfedge":1389},{"chart_seam":false,"geometry":{"curve":696,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1385,"id":696,"tolerance":1e-8,"twin_halfedge":1403},{"chart_seam":false,"geometry":{"curve":697,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1386,"id":697,"tolerance":1e-8,"twin_halfedge":1393},{"chart_seam":false,"geometry":{"curve":698,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1388,"id":698,"tolerance":1e-8,"twin_halfedge":1406},{"chart_seam":false,"geometry":{"curve":699,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1394,"id":699,"tolerance":1e-8,"twin_halfedge":1419},{"chart_seam":false,"geometry":{"curve":700,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1396,"id":700,"tolerance":1e-8,"twin_halfedge":1410},{"chart_seam":false,"geometry":{"curve":701,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1397,"id":701,"tolerance":1e-8,"twin_halfedge":1431},{"chart_seam":false,"geometry":{"curve":702,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1398,"id":702,"tolerance":1e-8,"twin_halfedge":1407},{"chart_seam":false,"geometry":{"curve":703,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1400,"id":703,"tolerance":1e-8,"twin_halfedge":1405},{"chart_seam":false,"geometry":{"curve":704,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1401,"id":704,"tolerance":1e-8,"twin_halfedge":1435},{"chart_seam":false,"geometry":{"curve":705,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1402,"id":705,"tolerance":1e-8,"twin_halfedge":1412},{"chart_seam":false,"geometry":{"curve":706,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1404,"id":706,"tolerance":1e-8,"twin_halfedge":1438},{"chart_seam":false,"geometry":{"curve":707,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1408,"id":707,"tolerance":1e-8,"twin_halfedge":1427},{"chart_seam":false,"geometry":{"curve":708,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1409,"id":708,"tolerance":1e-8,"twin_halfedge":1420},{"chart_seam":false,"geometry":{"curve":709,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1411,"id":709,"tolerance":1e-8,"twin_halfedge":1418},{"chart_seam":false,"geometry":{"curve":710,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1413,"id":710,"tolerance":1e-8,"twin_halfedge":1422},{"chart_seam":false,"geometry":{"curve":711,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1414,"id":711,"tolerance":1e-8,"twin_halfedge":1425},{"chart_seam":false,"geometry":{"curve":712,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1415,"id":712,"tolerance":1e-8,"twin_halfedge":1416},{"chart_seam":false,"geometry":{"curve":713,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1417,"id":713,"tolerance":1e-8,"twin_halfedge":1424},{"chart_seam":false,"geometry":{"curve":714,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1421,"id":714,"tolerance":1e-8,"twin_halfedge":1426},{"chart_seam":false,"geometry":{"curve":715,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1423,"id":715,"tolerance":1e-8,"twin_halfedge":1440},{"chart_seam":false,"geometry":{"curve":716,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1428,"id":716,"tolerance":1e-8,"twin_halfedge":1443},{"chart_seam":false,"geometry":{"curve":717,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1429,"id":717,"tolerance":1e-8,"twin_halfedge":1447},{"chart_seam":false,"geometry":{"curve":718,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1430,"id":718,"tolerance":1e-8,"twin_halfedge":1439},{"chart_seam":false,"geometry":{"curve":719,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1432,"id":719,"tolerance":1e-8,"twin_halfedge":1437},{"chart_seam":false,"geometry":{"curve":720,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1433,"id":720,"tolerance":1e-8,"twin_halfedge":1451},{"chart_seam":false,"geometry":{"curve":721,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1434,"id":721,"tolerance":1e-8,"twin_halfedge":1441},{"chart_seam":false,"geometry":{"curve":722,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1436,"id":722,"tolerance":1e-8,"twin_halfedge":1454},{"chart_seam":false,"geometry":{"curve":723,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1442,"id":723,"tolerance":1e-8,"twin_halfedge":1467},{"chart_seam":false,"geometry":{"curve":724,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1444,"id":724,"tolerance":1e-8,"twin_halfedge":1458},{"chart_seam":false,"geometry":{"curve":725,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1445,"id":725,"tolerance":1e-8,"twin_halfedge":1479},{"chart_seam":false,"geometry":{"curve":726,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1446,"id":726,"tolerance":1e-8,"twin_halfedge":1455},{"chart_seam":false,"geometry":{"curve":727,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1448,"id":727,"tolerance":1e-8,"twin_halfedge":1453},{"chart_seam":false,"geometry":{"curve":728,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1449,"id":728,"tolerance":1e-8,"twin_halfedge":1483},{"chart_seam":false,"geometry":{"curve":729,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1450,"id":729,"tolerance":1e-8,"twin_halfedge":1460},{"chart_seam":false,"geometry":{"curve":730,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1452,"id":730,"tolerance":1e-8,"twin_halfedge":1486},{"chart_seam":false,"geometry":{"curve":731,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1456,"id":731,"tolerance":1e-8,"twin_halfedge":1475},{"chart_seam":false,"geometry":{"curve":732,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1457,"id":732,"tolerance":1e-8,"twin_halfedge":1468},{"chart_seam":false,"geometry":{"curve":733,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1459,"id":733,"tolerance":1e-8,"twin_halfedge":1466},{"chart_seam":false,"geometry":{"curve":734,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1461,"id":734,"tolerance":1e-8,"twin_halfedge":1470},{"chart_seam":false,"geometry":{"curve":735,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1462,"id":735,"tolerance":1e-8,"twin_halfedge":1473},{"chart_seam":false,"geometry":{"curve":736,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1463,"id":736,"tolerance":1e-8,"twin_halfedge":1464},{"chart_seam":false,"geometry":{"curve":737,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1465,"id":737,"tolerance":1e-8,"twin_halfedge":1472},{"chart_seam":false,"geometry":{"curve":738,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1469,"id":738,"tolerance":1e-8,"twin_halfedge":1474},{"chart_seam":false,"geometry":{"curve":739,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1471,"id":739,"tolerance":1e-8,"twin_halfedge":1488},{"chart_seam":false,"geometry":{"curve":740,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1476,"id":740,"tolerance":1e-8,"twin_halfedge":1491},{"chart_seam":false,"geometry":{"curve":741,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1477,"id":741,"tolerance":1e-8,"twin_halfedge":1495},{"chart_seam":false,"geometry":{"curve":742,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1478,"id":742,"tolerance":1e-8,"twin_halfedge":1487},{"chart_seam":false,"geometry":{"curve":743,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1480,"id":743,"tolerance":1e-8,"twin_halfedge":1485},{"chart_seam":false,"geometry":{"curve":744,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1481,"id":744,"tolerance":1e-8,"twin_halfedge":1499},{"chart_seam":false,"geometry":{"curve":745,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1482,"id":745,"tolerance":1e-8,"twin_halfedge":1489},{"chart_seam":false,"geometry":{"curve":746,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1484,"id":746,"tolerance":1e-8,"twin_halfedge":1502},{"chart_seam":false,"geometry":{"curve":747,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1490,"id":747,"tolerance":1e-8,"twin_halfedge":1515},{"chart_seam":false,"geometry":{"curve":748,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1492,"id":748,"tolerance":1e-8,"twin_halfedge":1506},{"chart_seam":false,"geometry":{"curve":749,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1493,"id":749,"tolerance":1e-8,"twin_halfedge":1527},{"chart_seam":false,"geometry":{"curve":750,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1494,"id":750,"tolerance":1e-8,"twin_halfedge":1503},{"chart_seam":false,"geometry":{"curve":751,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1496,"id":751,"tolerance":1e-8,"twin_halfedge":1501},{"chart_seam":false,"geometry":{"curve":752,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1497,"id":752,"tolerance":1e-8,"twin_halfedge":1531},{"chart_seam":false,"geometry":{"curve":753,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1498,"id":753,"tolerance":1e-8,"twin_halfedge":1508},{"chart_seam":false,"geometry":{"curve":754,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1500,"id":754,"tolerance":1e-8,"twin_halfedge":1534},{"chart_seam":false,"geometry":{"curve":755,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1504,"id":755,"tolerance":1e-8,"twin_halfedge":1523},{"chart_seam":false,"geometry":{"curve":756,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1505,"id":756,"tolerance":1e-8,"twin_halfedge":1516},{"chart_seam":false,"geometry":{"curve":757,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1507,"id":757,"tolerance":1e-8,"twin_halfedge":1514},{"chart_seam":false,"geometry":{"curve":758,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1509,"id":758,"tolerance":1e-8,"twin_halfedge":1518},{"chart_seam":false,"geometry":{"curve":759,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1510,"id":759,"tolerance":1e-8,"twin_halfedge":1521},{"chart_seam":false,"geometry":{"curve":760,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1511,"id":760,"tolerance":1e-8,"twin_halfedge":1512},{"chart_seam":false,"geometry":{"curve":761,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1513,"id":761,"tolerance":1e-8,"twin_halfedge":1520},{"chart_seam":false,"geometry":{"curve":762,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1517,"id":762,"tolerance":1e-8,"twin_halfedge":1522},{"chart_seam":false,"geometry":{"curve":763,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1519,"id":763,"tolerance":1e-8,"twin_halfedge":1536},{"chart_seam":false,"geometry":{"curve":764,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1524,"id":764,"tolerance":1e-8,"twin_halfedge":1539},{"chart_seam":false,"geometry":{"curve":765,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1525,"id":765,"tolerance":1e-8,"twin_halfedge":1543},{"chart_seam":false,"geometry":{"curve":766,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1526,"id":766,"tolerance":1e-8,"twin_halfedge":1535},{"chart_seam":false,"geometry":{"curve":767,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1528,"id":767,"tolerance":1e-8,"twin_halfedge":1533},{"chart_seam":false,"geometry":{"curve":768,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1529,"id":768,"tolerance":1e-8,"twin_halfedge":1547},{"chart_seam":false,"geometry":{"curve":769,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1530,"id":769,"tolerance":1e-8,"twin_halfedge":1537},{"chart_seam":false,"geometry":{"curve":770,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1532,"id":770,"tolerance":1e-8,"twin_halfedge":1550},{"chart_seam":false,"geometry":{"curve":771,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1538,"id":771,"tolerance":1e-8,"twin_halfedge":1563},{"chart_seam":false,"geometry":{"curve":772,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1540,"id":772,"tolerance":1e-8,"twin_halfedge":1554},{"chart_seam":false,"geometry":{"curve":773,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1541,"id":773,"tolerance":1e-8,"twin_halfedge":1575},{"chart_seam":false,"geometry":{"curve":774,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1542,"id":774,"tolerance":1e-8,"twin_halfedge":1551},{"chart_seam":false,"geometry":{"curve":775,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1544,"id":775,"tolerance":1e-8,"twin_halfedge":1549},{"chart_seam":false,"geometry":{"curve":776,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1545,"id":776,"tolerance":1e-8,"twin_halfedge":1579},{"chart_seam":false,"geometry":{"curve":777,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1546,"id":777,"tolerance":1e-8,"twin_halfedge":1556},{"chart_seam":false,"geometry":{"curve":778,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1548,"id":778,"tolerance":1e-8,"twin_halfedge":1582},{"chart_seam":false,"geometry":{"curve":779,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1552,"id":779,"tolerance":1e-8,"twin_halfedge":1571},{"chart_seam":false,"geometry":{"curve":780,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1553,"id":780,"tolerance":1e-8,"twin_halfedge":1564},{"chart_seam":false,"geometry":{"curve":781,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1555,"id":781,"tolerance":1e-8,"twin_halfedge":1562},{"chart_seam":false,"geometry":{"curve":782,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1557,"id":782,"tolerance":1e-8,"twin_halfedge":1566},{"chart_seam":false,"geometry":{"curve":783,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1558,"id":783,"tolerance":1e-8,"twin_halfedge":1569},{"chart_seam":false,"geometry":{"curve":784,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1559,"id":784,"tolerance":1e-8,"twin_halfedge":1560},{"chart_seam":false,"geometry":{"curve":785,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1561,"id":785,"tolerance":1e-8,"twin_halfedge":1568},{"chart_seam":false,"geometry":{"curve":786,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1565,"id":786,"tolerance":1e-8,"twin_halfedge":1570},{"chart_seam":false,"geometry":{"curve":787,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1567,"id":787,"tolerance":1e-8,"twin_halfedge":1584},{"chart_seam":false,"geometry":{"curve":788,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1572,"id":788,"tolerance":1e-8,"twin_halfedge":1587},{"chart_seam":false,"geometry":{"curve":789,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1573,"id":789,"tolerance":1e-8,"twin_halfedge":1591},{"chart_seam":false,"geometry":{"curve":790,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1574,"id":790,"tolerance":1e-8,"twin_halfedge":1583},{"chart_seam":false,"geometry":{"curve":791,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1576,"id":791,"tolerance":1e-8,"twin_halfedge":1581},{"chart_seam":false,"geometry":{"curve":792,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1577,"id":792,"tolerance":1e-8,"twin_halfedge":1595},{"chart_seam":false,"geometry":{"curve":793,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1578,"id":793,"tolerance":1e-8,"twin_halfedge":1585},{"chart_seam":false,"geometry":{"curve":794,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1580,"id":794,"tolerance":1e-8,"twin_halfedge":1598},{"chart_seam":false,"geometry":{"curve":795,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1586,"id":795,"tolerance":1e-8,"twin_halfedge":1611},{"chart_seam":false,"geometry":{"curve":796,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1588,"id":796,"tolerance":1e-8,"twin_halfedge":1602},{"chart_seam":false,"geometry":{"curve":797,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1589,"id":797,"tolerance":1e-8,"twin_halfedge":1623},{"chart_seam":false,"geometry":{"curve":798,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1590,"id":798,"tolerance":1e-8,"twin_halfedge":1599},{"chart_seam":false,"geometry":{"curve":799,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1592,"id":799,"tolerance":1e-8,"twin_halfedge":1597},{"chart_seam":false,"geometry":{"curve":800,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1593,"id":800,"tolerance":1e-8,"twin_halfedge":1627},{"chart_seam":false,"geometry":{"curve":801,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1594,"id":801,"tolerance":1e-8,"twin_halfedge":1604},{"chart_seam":false,"geometry":{"curve":802,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1596,"id":802,"tolerance":1e-8,"twin_halfedge":1630},{"chart_seam":false,"geometry":{"curve":803,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1600,"id":803,"tolerance":1e-8,"twin_halfedge":1619},{"chart_seam":false,"geometry":{"curve":804,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1601,"id":804,"tolerance":1e-8,"twin_halfedge":1612},{"chart_seam":false,"geometry":{"curve":805,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1603,"id":805,"tolerance":1e-8,"twin_halfedge":1610},{"chart_seam":false,"geometry":{"curve":806,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1605,"id":806,"tolerance":1e-8,"twin_halfedge":1614},{"chart_seam":false,"geometry":{"curve":807,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1606,"id":807,"tolerance":1e-8,"twin_halfedge":1617},{"chart_seam":false,"geometry":{"curve":808,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1607,"id":808,"tolerance":1e-8,"twin_halfedge":1608},{"chart_seam":false,"geometry":{"curve":809,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1609,"id":809,"tolerance":1e-8,"twin_halfedge":1616},{"chart_seam":false,"geometry":{"curve":810,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1613,"id":810,"tolerance":1e-8,"twin_halfedge":1618},{"chart_seam":false,"geometry":{"curve":811,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1615,"id":811,"tolerance":1e-8,"twin_halfedge":1632},{"chart_seam":false,"geometry":{"curve":812,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1620,"id":812,"tolerance":1e-8,"twin_halfedge":1635},{"chart_seam":false,"geometry":{"curve":813,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1621,"id":813,"tolerance":1e-8,"twin_halfedge":1639},{"chart_seam":false,"geometry":{"curve":814,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1622,"id":814,"tolerance":1e-8,"twin_halfedge":1631},{"chart_seam":false,"geometry":{"curve":815,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1624,"id":815,"tolerance":1e-8,"twin_halfedge":1629},{"chart_seam":false,"geometry":{"curve":816,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1625,"id":816,"tolerance":1e-8,"twin_halfedge":1643},{"chart_seam":false,"geometry":{"curve":817,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1626,"id":817,"tolerance":1e-8,"twin_halfedge":1633},{"chart_seam":false,"geometry":{"curve":818,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1628,"id":818,"tolerance":1e-8,"twin_halfedge":1646},{"chart_seam":false,"geometry":{"curve":819,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1634,"id":819,"tolerance":1e-8,"twin_halfedge":1659},{"chart_seam":false,"geometry":{"curve":820,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1636,"id":820,"tolerance":1e-8,"twin_halfedge":1650},{"chart_seam":false,"geometry":{"curve":821,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1637,"id":821,"tolerance":1e-8,"twin_halfedge":1671},{"chart_seam":false,"geometry":{"curve":822,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1638,"id":822,"tolerance":1e-8,"twin_halfedge":1647},{"chart_seam":false,"geometry":{"curve":823,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1640,"id":823,"tolerance":1e-8,"twin_halfedge":1645},{"chart_seam":false,"geometry":{"curve":824,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1641,"id":824,"tolerance":1e-8,"twin_halfedge":1675},{"chart_seam":false,"geometry":{"curve":825,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1642,"id":825,"tolerance":1e-8,"twin_halfedge":1652},{"chart_seam":false,"geometry":{"curve":826,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1644,"id":826,"tolerance":1e-8,"twin_halfedge":1678},{"chart_seam":false,"geometry":{"curve":827,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1648,"id":827,"tolerance":1e-8,"twin_halfedge":1667},{"chart_seam":false,"geometry":{"curve":828,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1649,"id":828,"tolerance":1e-8,"twin_halfedge":1660},{"chart_seam":false,"geometry":{"curve":829,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1651,"id":829,"tolerance":1e-8,"twin_halfedge":1658},{"chart_seam":false,"geometry":{"curve":830,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1653,"id":830,"tolerance":1e-8,"twin_halfedge":1662},{"chart_seam":false,"geometry":{"curve":831,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1654,"id":831,"tolerance":1e-8,"twin_halfedge":1665},{"chart_seam":false,"geometry":{"curve":832,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1655,"id":832,"tolerance":1e-8,"twin_halfedge":1656},{"chart_seam":false,"geometry":{"curve":833,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1657,"id":833,"tolerance":1e-8,"twin_halfedge":1664},{"chart_seam":false,"geometry":{"curve":834,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1661,"id":834,"tolerance":1e-8,"twin_halfedge":1666},{"chart_seam":false,"geometry":{"curve":835,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1663,"id":835,"tolerance":1e-8,"twin_halfedge":1680},{"chart_seam":false,"geometry":{"curve":836,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1668,"id":836,"tolerance":1e-8,"twin_halfedge":1683},{"chart_seam":false,"geometry":{"curve":837,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1669,"id":837,"tolerance":1e-8,"twin_halfedge":1687},{"chart_seam":false,"geometry":{"curve":838,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1670,"id":838,"tolerance":1e-8,"twin_halfedge":1679},{"chart_seam":false,"geometry":{"curve":839,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1672,"id":839,"tolerance":1e-8,"twin_halfedge":1677},{"chart_seam":false,"geometry":{"curve":840,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1673,"id":840,"tolerance":1e-8,"twin_halfedge":1691},{"chart_seam":false,"geometry":{"curve":841,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1674,"id":841,"tolerance":1e-8,"twin_halfedge":1681},{"chart_seam":false,"geometry":{"curve":842,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1676,"id":842,"tolerance":1e-8,"twin_halfedge":1694},{"chart_seam":false,"geometry":{"curve":843,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1682,"id":843,"tolerance":1e-8,"twin_halfedge":1707},{"chart_seam":false,"geometry":{"curve":844,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1684,"id":844,"tolerance":1e-8,"twin_halfedge":1698},{"chart_seam":false,"geometry":{"curve":845,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1685,"id":845,"tolerance":1e-8,"twin_halfedge":1719},{"chart_seam":false,"geometry":{"curve":846,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1686,"id":846,"tolerance":1e-8,"twin_halfedge":1695},{"chart_seam":false,"geometry":{"curve":847,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1688,"id":847,"tolerance":1e-8,"twin_halfedge":1693},{"chart_seam":false,"geometry":{"curve":848,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1689,"id":848,"tolerance":1e-8,"twin_halfedge":1723},{"chart_seam":false,"geometry":{"curve":849,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1690,"id":849,"tolerance":1e-8,"twin_halfedge":1700},{"chart_seam":false,"geometry":{"curve":850,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1692,"id":850,"tolerance":1e-8,"twin_halfedge":1726},{"chart_seam":false,"geometry":{"curve":851,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1696,"id":851,"tolerance":1e-8,"twin_halfedge":1715},{"chart_seam":false,"geometry":{"curve":852,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1697,"id":852,"tolerance":1e-8,"twin_halfedge":1708},{"chart_seam":false,"geometry":{"curve":853,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1699,"id":853,"tolerance":1e-8,"twin_halfedge":1706},{"chart_seam":false,"geometry":{"curve":854,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1701,"id":854,"tolerance":1e-8,"twin_halfedge":1710},{"chart_seam":false,"geometry":{"curve":855,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1702,"id":855,"tolerance":1e-8,"twin_halfedge":1713},{"chart_seam":false,"geometry":{"curve":856,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1703,"id":856,"tolerance":1e-8,"twin_halfedge":1704},{"chart_seam":false,"geometry":{"curve":857,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1705,"id":857,"tolerance":1e-8,"twin_halfedge":1712},{"chart_seam":false,"geometry":{"curve":858,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1709,"id":858,"tolerance":1e-8,"twin_halfedge":1714},{"chart_seam":false,"geometry":{"curve":859,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1711,"id":859,"tolerance":1e-8,"twin_halfedge":1728},{"chart_seam":false,"geometry":{"curve":860,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1716,"id":860,"tolerance":1e-8,"twin_halfedge":1731},{"chart_seam":false,"geometry":{"curve":861,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1717,"id":861,"tolerance":1e-8,"twin_halfedge":1735},{"chart_seam":false,"geometry":{"curve":862,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1718,"id":862,"tolerance":1e-8,"twin_halfedge":1727},{"chart_seam":false,"geometry":{"curve":863,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1720,"id":863,"tolerance":1e-8,"twin_halfedge":1725},{"chart_seam":false,"geometry":{"curve":864,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1721,"id":864,"tolerance":1e-8,"twin_halfedge":1739},{"chart_seam":false,"geometry":{"curve":865,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1722,"id":865,"tolerance":1e-8,"twin_halfedge":1729},{"chart_seam":false,"geometry":{"curve":866,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1724,"id":866,"tolerance":1e-8,"twin_halfedge":1742},{"chart_seam":false,"geometry":{"curve":867,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1730,"id":867,"tolerance":1e-8,"twin_halfedge":1755},{"chart_seam":false,"geometry":{"curve":868,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1732,"id":868,"tolerance":1e-8,"twin_halfedge":1746},{"chart_seam":false,"geometry":{"curve":869,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1733,"id":869,"tolerance":1e-8,"twin_halfedge":1767},{"chart_seam":false,"geometry":{"curve":870,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1734,"id":870,"tolerance":1e-8,"twin_halfedge":1743},{"chart_seam":false,"geometry":{"curve":871,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1736,"id":871,"tolerance":1e-8,"twin_halfedge":1741},{"chart_seam":false,"geometry":{"curve":872,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1737,"id":872,"tolerance":1e-8,"twin_halfedge":1771},{"chart_seam":false,"geometry":{"curve":873,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1738,"id":873,"tolerance":1e-8,"twin_halfedge":1748},{"chart_seam":false,"geometry":{"curve":874,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1740,"id":874,"tolerance":1e-8,"twin_halfedge":1774},{"chart_seam":false,"geometry":{"curve":875,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1744,"id":875,"tolerance":1e-8,"twin_halfedge":1763},{"chart_seam":false,"geometry":{"curve":876,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1745,"id":876,"tolerance":1e-8,"twin_halfedge":1756},{"chart_seam":false,"geometry":{"curve":877,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1747,"id":877,"tolerance":1e-8,"twin_halfedge":1754},{"chart_seam":false,"geometry":{"curve":878,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1749,"id":878,"tolerance":1e-8,"twin_halfedge":1758},{"chart_seam":false,"geometry":{"curve":879,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1750,"id":879,"tolerance":1e-8,"twin_halfedge":1761},{"chart_seam":false,"geometry":{"curve":880,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1751,"id":880,"tolerance":1e-8,"twin_halfedge":1752},{"chart_seam":false,"geometry":{"curve":881,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1753,"id":881,"tolerance":1e-8,"twin_halfedge":1760},{"chart_seam":false,"geometry":{"curve":882,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1757,"id":882,"tolerance":1e-8,"twin_halfedge":1762},{"chart_seam":false,"geometry":{"curve":883,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1759,"id":883,"tolerance":1e-8,"twin_halfedge":1776},{"chart_seam":false,"geometry":{"curve":884,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1764,"id":884,"tolerance":1e-8,"twin_halfedge":1779},{"chart_seam":false,"geometry":{"curve":885,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1765,"id":885,"tolerance":1e-8,"twin_halfedge":1783},{"chart_seam":false,"geometry":{"curve":886,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1766,"id":886,"tolerance":1e-8,"twin_halfedge":1775},{"chart_seam":false,"geometry":{"curve":887,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1768,"id":887,"tolerance":1e-8,"twin_halfedge":1773},{"chart_seam":false,"geometry":{"curve":888,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1769,"id":888,"tolerance":1e-8,"twin_halfedge":1787},{"chart_seam":false,"geometry":{"curve":889,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1770,"id":889,"tolerance":1e-8,"twin_halfedge":1777},{"chart_seam":false,"geometry":{"curve":890,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1772,"id":890,"tolerance":1e-8,"twin_halfedge":1790},{"chart_seam":false,"geometry":{"curve":891,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1778,"id":891,"tolerance":1e-8,"twin_halfedge":1803},{"chart_seam":false,"geometry":{"curve":892,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1780,"id":892,"tolerance":1e-8,"twin_halfedge":1794},{"chart_seam":false,"geometry":{"curve":893,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1781,"id":893,"tolerance":1e-8,"twin_halfedge":1815},{"chart_seam":false,"geometry":{"curve":894,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1782,"id":894,"tolerance":1e-8,"twin_halfedge":1791},{"chart_seam":false,"geometry":{"curve":895,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1784,"id":895,"tolerance":1e-8,"twin_halfedge":1789},{"chart_seam":false,"geometry":{"curve":896,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1785,"id":896,"tolerance":1e-8,"twin_halfedge":1819},{"chart_seam":false,"geometry":{"curve":897,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1786,"id":897,"tolerance":1e-8,"twin_halfedge":1796},{"chart_seam":false,"geometry":{"curve":898,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1788,"id":898,"tolerance":1e-8,"twin_halfedge":1822},{"chart_seam":false,"geometry":{"curve":899,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1792,"id":899,"tolerance":1e-8,"twin_halfedge":1811},{"chart_seam":false,"geometry":{"curve":900,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1793,"id":900,"tolerance":1e-8,"twin_halfedge":1804},{"chart_seam":false,"geometry":{"curve":901,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1795,"id":901,"tolerance":1e-8,"twin_halfedge":1802},{"chart_seam":false,"geometry":{"curve":902,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1797,"id":902,"tolerance":1e-8,"twin_halfedge":1806},{"chart_seam":false,"geometry":{"curve":903,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1798,"id":903,"tolerance":1e-8,"twin_halfedge":1809},{"chart_seam":false,"geometry":{"curve":904,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1799,"id":904,"tolerance":1e-8,"twin_halfedge":1800},{"chart_seam":false,"geometry":{"curve":905,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1801,"id":905,"tolerance":1e-8,"twin_halfedge":1808},{"chart_seam":false,"geometry":{"curve":906,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1805,"id":906,"tolerance":1e-8,"twin_halfedge":1810},{"chart_seam":false,"geometry":{"curve":907,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1807,"id":907,"tolerance":1e-8,"twin_halfedge":1824},{"chart_seam":false,"geometry":{"curve":908,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1812,"id":908,"tolerance":1e-8,"twin_halfedge":1827},{"chart_seam":false,"geometry":{"curve":909,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1813,"id":909,"tolerance":1e-8,"twin_halfedge":1831},{"chart_seam":false,"geometry":{"curve":910,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1814,"id":910,"tolerance":1e-8,"twin_halfedge":1823},{"chart_seam":false,"geometry":{"curve":911,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1816,"id":911,"tolerance":1e-8,"twin_halfedge":1821},{"chart_seam":false,"geometry":{"curve":912,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1817,"id":912,"tolerance":1e-8,"twin_halfedge":1835},{"chart_seam":false,"geometry":{"curve":913,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1818,"id":913,"tolerance":1e-8,"twin_halfedge":1825},{"chart_seam":false,"geometry":{"curve":914,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1820,"id":914,"tolerance":1e-8,"twin_halfedge":1838},{"chart_seam":false,"geometry":{"curve":915,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1826,"id":915,"tolerance":1e-8,"twin_halfedge":1851},{"chart_seam":false,"geometry":{"curve":916,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1828,"id":916,"tolerance":1e-8,"twin_halfedge":1842},{"chart_seam":false,"geometry":{"curve":917,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1829,"id":917,"tolerance":1e-8,"twin_halfedge":1863},{"chart_seam":false,"geometry":{"curve":918,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1830,"id":918,"tolerance":1e-8,"twin_halfedge":1839},{"chart_seam":false,"geometry":{"curve":919,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1832,"id":919,"tolerance":1e-8,"twin_halfedge":1837},{"chart_seam":false,"geometry":{"curve":920,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1833,"id":920,"tolerance":1e-8,"twin_halfedge":1867},{"chart_seam":false,"geometry":{"curve":921,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1834,"id":921,"tolerance":1e-8,"twin_halfedge":1844},{"chart_seam":false,"geometry":{"curve":922,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1836,"id":922,"tolerance":1e-8,"twin_halfedge":1870},{"chart_seam":false,"geometry":{"curve":923,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1840,"id":923,"tolerance":1e-8,"twin_halfedge":1859},{"chart_seam":false,"geometry":{"curve":924,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1841,"id":924,"tolerance":1e-8,"twin_halfedge":1852},{"chart_seam":false,"geometry":{"curve":925,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1843,"id":925,"tolerance":1e-8,"twin_halfedge":1850},{"chart_seam":false,"geometry":{"curve":926,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1845,"id":926,"tolerance":1e-8,"twin_halfedge":1854},{"chart_seam":false,"geometry":{"curve":927,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1846,"id":927,"tolerance":1e-8,"twin_halfedge":1857},{"chart_seam":false,"geometry":{"curve":928,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1847,"id":928,"tolerance":1e-8,"twin_halfedge":1848},{"chart_seam":false,"geometry":{"curve":929,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1849,"id":929,"tolerance":1e-8,"twin_halfedge":1856},{"chart_seam":false,"geometry":{"curve":930,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1853,"id":930,"tolerance":1e-8,"twin_halfedge":1858},{"chart_seam":false,"geometry":{"curve":931,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1855,"id":931,"tolerance":1e-8,"twin_halfedge":1872},{"chart_seam":false,"geometry":{"curve":932,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1860,"id":932,"tolerance":1e-8,"twin_halfedge":1875},{"chart_seam":false,"geometry":{"curve":933,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1861,"id":933,"tolerance":1e-8,"twin_halfedge":1879},{"chart_seam":false,"geometry":{"curve":934,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1862,"id":934,"tolerance":1e-8,"twin_halfedge":1871},{"chart_seam":false,"geometry":{"curve":935,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1864,"id":935,"tolerance":1e-8,"twin_halfedge":1869},{"chart_seam":false,"geometry":{"curve":936,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1865,"id":936,"tolerance":1e-8,"twin_halfedge":1883},{"chart_seam":false,"geometry":{"curve":937,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1866,"id":937,"tolerance":1e-8,"twin_halfedge":1873},{"chart_seam":false,"geometry":{"curve":938,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1868,"id":938,"tolerance":1e-8,"twin_halfedge":1886},{"chart_seam":false,"geometry":{"curve":939,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1874,"id":939,"tolerance":1e-8,"twin_halfedge":1899},{"chart_seam":false,"geometry":{"curve":940,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1876,"id":940,"tolerance":1e-8,"twin_halfedge":1890},{"chart_seam":false,"geometry":{"curve":941,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1877,"id":941,"tolerance":1e-8,"twin_halfedge":1911},{"chart_seam":false,"geometry":{"curve":942,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1878,"id":942,"tolerance":1e-8,"twin_halfedge":1887},{"chart_seam":false,"geometry":{"curve":943,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1880,"id":943,"tolerance":1e-8,"twin_halfedge":1885},{"chart_seam":false,"geometry":{"curve":944,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1881,"id":944,"tolerance":1e-8,"twin_halfedge":1915},{"chart_seam":false,"geometry":{"curve":945,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1882,"id":945,"tolerance":1e-8,"twin_halfedge":1892},{"chart_seam":false,"geometry":{"curve":946,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1884,"id":946,"tolerance":1e-8,"twin_halfedge":1918},{"chart_seam":false,"geometry":{"curve":947,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1888,"id":947,"tolerance":1e-8,"twin_halfedge":1907},{"chart_seam":false,"geometry":{"curve":948,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1889,"id":948,"tolerance":1e-8,"twin_halfedge":1900},{"chart_seam":false,"geometry":{"curve":949,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1891,"id":949,"tolerance":1e-8,"twin_halfedge":1898},{"chart_seam":false,"geometry":{"curve":950,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1893,"id":950,"tolerance":1e-8,"twin_halfedge":1902},{"chart_seam":false,"geometry":{"curve":951,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1894,"id":951,"tolerance":1e-8,"twin_halfedge":1905},{"chart_seam":false,"geometry":{"curve":952,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1895,"id":952,"tolerance":1e-8,"twin_halfedge":1896},{"chart_seam":false,"geometry":{"curve":953,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1897,"id":953,"tolerance":1e-8,"twin_halfedge":1904},{"chart_seam":false,"geometry":{"curve":954,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1901,"id":954,"tolerance":1e-8,"twin_halfedge":1906},{"chart_seam":false,"geometry":{"curve":955,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1903,"id":955,"tolerance":1e-8,"twin_halfedge":1920},{"chart_seam":false,"geometry":{"curve":956,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1908,"id":956,"tolerance":1e-8,"twin_halfedge":1923},{"chart_seam":false,"geometry":{"curve":957,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1909,"id":957,"tolerance":1e-8,"twin_halfedge":1927},{"chart_seam":false,"geometry":{"curve":958,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1910,"id":958,"tolerance":1e-8,"twin_halfedge":1919},{"chart_seam":false,"geometry":{"curve":959,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1912,"id":959,"tolerance":1e-8,"twin_halfedge":1917},{"chart_seam":false,"geometry":{"curve":960,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1913,"id":960,"tolerance":1e-8,"twin_halfedge":1931},{"chart_seam":false,"geometry":{"curve":961,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1914,"id":961,"tolerance":1e-8,"twin_halfedge":1921},{"chart_seam":false,"geometry":{"curve":962,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1916,"id":962,"tolerance":1e-8,"twin_halfedge":1934},{"chart_seam":false,"geometry":{"curve":963,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1922,"id":963,"tolerance":1e-8,"twin_halfedge":1947},{"chart_seam":false,"geometry":{"curve":964,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1924,"id":964,"tolerance":1e-8,"twin_halfedge":1938},{"chart_seam":false,"geometry":{"curve":965,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1925,"id":965,"tolerance":1e-8,"twin_halfedge":1959},{"chart_seam":false,"geometry":{"curve":966,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1926,"id":966,"tolerance":1e-8,"twin_halfedge":1935},{"chart_seam":false,"geometry":{"curve":967,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1928,"id":967,"tolerance":1e-8,"twin_halfedge":1933},{"chart_seam":false,"geometry":{"curve":968,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1929,"id":968,"tolerance":1e-8,"twin_halfedge":1963},{"chart_seam":false,"geometry":{"curve":969,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1930,"id":969,"tolerance":1e-8,"twin_halfedge":1940},{"chart_seam":false,"geometry":{"curve":970,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1932,"id":970,"tolerance":1e-8,"twin_halfedge":1966},{"chart_seam":false,"geometry":{"curve":971,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1936,"id":971,"tolerance":1e-8,"twin_halfedge":1955},{"chart_seam":false,"geometry":{"curve":972,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1937,"id":972,"tolerance":1e-8,"twin_halfedge":1948},{"chart_seam":false,"geometry":{"curve":973,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1939,"id":973,"tolerance":1e-8,"twin_halfedge":1946},{"chart_seam":false,"geometry":{"curve":974,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1941,"id":974,"tolerance":1e-8,"twin_halfedge":1950},{"chart_seam":false,"geometry":{"curve":975,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":1942,"id":975,"tolerance":1e-8,"twin_halfedge":1953},{"chart_seam":false,"geometry":{"curve":976,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1943,"id":976,"tolerance":1e-8,"twin_halfedge":1944},{"chart_seam":false,"geometry":{"curve":977,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1945,"id":977,"tolerance":1e-8,"twin_halfedge":1952},{"chart_seam":false,"geometry":{"curve":978,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1949,"id":978,"tolerance":1e-8,"twin_halfedge":1954},{"chart_seam":false,"geometry":{"curve":979,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1951,"id":979,"tolerance":1e-8,"twin_halfedge":1968},{"chart_seam":false,"geometry":{"curve":980,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1956,"id":980,"tolerance":1e-8,"twin_halfedge":1971},{"chart_seam":false,"geometry":{"curve":981,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1957,"id":981,"tolerance":1e-8,"twin_halfedge":1975},{"chart_seam":false,"geometry":{"curve":982,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1958,"id":982,"tolerance":1e-8,"twin_halfedge":1967},{"chart_seam":false,"geometry":{"curve":983,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1960,"id":983,"tolerance":1e-8,"twin_halfedge":1965},{"chart_seam":false,"geometry":{"curve":984,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1961,"id":984,"tolerance":1e-8,"twin_halfedge":1979},{"chart_seam":false,"geometry":{"curve":985,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1962,"id":985,"tolerance":1e-8,"twin_halfedge":1969},{"chart_seam":false,"geometry":{"curve":986,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1964,"id":986,"tolerance":1e-8,"twin_halfedge":1982},{"chart_seam":false,"geometry":{"curve":987,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1970,"id":987,"tolerance":1e-8,"twin_halfedge":1995},{"chart_seam":false,"geometry":{"curve":988,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1972,"id":988,"tolerance":1e-8,"twin_halfedge":1986},{"chart_seam":false,"geometry":{"curve":989,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1973,"id":989,"tolerance":1e-8,"twin_halfedge":2007},{"chart_seam":false,"geometry":{"curve":990,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1974,"id":990,"tolerance":1e-8,"twin_halfedge":1983},{"chart_seam":false,"geometry":{"curve":991,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1976,"id":991,"tolerance":1e-8,"twin_halfedge":1981},{"chart_seam":false,"geometry":{"curve":992,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1977,"id":992,"tolerance":1e-8,"twin_halfedge":2011},{"chart_seam":false,"geometry":{"curve":993,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1978,"id":993,"tolerance":1e-8,"twin_halfedge":1988},{"chart_seam":false,"geometry":{"curve":994,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1980,"id":994,"tolerance":1e-8,"twin_halfedge":2014},{"chart_seam":false,"geometry":{"curve":995,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1984,"id":995,"tolerance":1e-8,"twin_halfedge":2003},{"chart_seam":false,"geometry":{"curve":996,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1985,"id":996,"tolerance":1e-8,"twin_halfedge":1996},{"chart_seam":false,"geometry":{"curve":997,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1987,"id":997,"tolerance":1e-8,"twin_halfedge":1994},{"chart_seam":false,"geometry":{"curve":998,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1989,"id":998,"tolerance":1e-8,"twin_halfedge":1998},{"chart_seam":false,"geometry":{"curve":999,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":1990,"id":999,"tolerance":1e-8,"twin_halfedge":2001},{"chart_seam":false,"geometry":{"curve":1000,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1991,"id":1000,"tolerance":1e-8,"twin_halfedge":1992},{"chart_seam":false,"geometry":{"curve":1001,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1993,"id":1001,"tolerance":1e-8,"twin_halfedge":2000},{"chart_seam":false,"geometry":{"curve":1002,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1997,"id":1002,"tolerance":1e-8,"twin_halfedge":2002},{"chart_seam":false,"geometry":{"curve":1003,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":1999,"id":1003,"tolerance":1e-8,"twin_halfedge":2016},{"chart_seam":false,"geometry":{"curve":1004,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2004,"id":1004,"tolerance":1e-8,"twin_halfedge":2019},{"chart_seam":false,"geometry":{"curve":1005,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2005,"id":1005,"tolerance":1e-8,"twin_halfedge":2023},{"chart_seam":false,"geometry":{"curve":1006,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2006,"id":1006,"tolerance":1e-8,"twin_halfedge":2015},{"chart_seam":false,"geometry":{"curve":1007,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2008,"id":1007,"tolerance":1e-8,"twin_halfedge":2013},{"chart_seam":false,"geometry":{"curve":1008,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2009,"id":1008,"tolerance":1e-8,"twin_halfedge":2027},{"chart_seam":false,"geometry":{"curve":1009,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2010,"id":1009,"tolerance":1e-8,"twin_halfedge":2017},{"chart_seam":false,"geometry":{"curve":1010,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2012,"id":1010,"tolerance":1e-8,"twin_halfedge":2030},{"chart_seam":false,"geometry":{"curve":1011,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2018,"id":1011,"tolerance":1e-8,"twin_halfedge":2043},{"chart_seam":false,"geometry":{"curve":1012,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2020,"id":1012,"tolerance":1e-8,"twin_halfedge":2034},{"chart_seam":false,"geometry":{"curve":1013,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2021,"id":1013,"tolerance":1e-8,"twin_halfedge":2055},{"chart_seam":false,"geometry":{"curve":1014,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2022,"id":1014,"tolerance":1e-8,"twin_halfedge":2031},{"chart_seam":false,"geometry":{"curve":1015,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2024,"id":1015,"tolerance":1e-8,"twin_halfedge":2029},{"chart_seam":false,"geometry":{"curve":1016,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2025,"id":1016,"tolerance":1e-8,"twin_halfedge":2059},{"chart_seam":false,"geometry":{"curve":1017,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2026,"id":1017,"tolerance":1e-8,"twin_halfedge":2036},{"chart_seam":false,"geometry":{"curve":1018,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2028,"id":1018,"tolerance":1e-8,"twin_halfedge":2062},{"chart_seam":false,"geometry":{"curve":1019,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2032,"id":1019,"tolerance":1e-8,"twin_halfedge":2051},{"chart_seam":false,"geometry":{"curve":1020,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2033,"id":1020,"tolerance":1e-8,"twin_halfedge":2044},{"chart_seam":false,"geometry":{"curve":1021,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2035,"id":1021,"tolerance":1e-8,"twin_halfedge":2042},{"chart_seam":false,"geometry":{"curve":1022,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2037,"id":1022,"tolerance":1e-8,"twin_halfedge":2046},{"chart_seam":false,"geometry":{"curve":1023,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2038,"id":1023,"tolerance":1e-8,"twin_halfedge":2049},{"chart_seam":false,"geometry":{"curve":1024,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2039,"id":1024,"tolerance":1e-8,"twin_halfedge":2040},{"chart_seam":false,"geometry":{"curve":1025,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2041,"id":1025,"tolerance":1e-8,"twin_halfedge":2048},{"chart_seam":false,"geometry":{"curve":1026,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2045,"id":1026,"tolerance":1e-8,"twin_halfedge":2050},{"chart_seam":false,"geometry":{"curve":1027,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2047,"id":1027,"tolerance":1e-8,"twin_halfedge":2064},{"chart_seam":false,"geometry":{"curve":1028,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2052,"id":1028,"tolerance":1e-8,"twin_halfedge":2067},{"chart_seam":false,"geometry":{"curve":1029,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2053,"id":1029,"tolerance":1e-8,"twin_halfedge":2071},{"chart_seam":false,"geometry":{"curve":1030,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2054,"id":1030,"tolerance":1e-8,"twin_halfedge":2063},{"chart_seam":false,"geometry":{"curve":1031,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2056,"id":1031,"tolerance":1e-8,"twin_halfedge":2061},{"chart_seam":false,"geometry":{"curve":1032,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2057,"id":1032,"tolerance":1e-8,"twin_halfedge":2075},{"chart_seam":false,"geometry":{"curve":1033,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2058,"id":1033,"tolerance":1e-8,"twin_halfedge":2065},{"chart_seam":false,"geometry":{"curve":1034,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2060,"id":1034,"tolerance":1e-8,"twin_halfedge":2078},{"chart_seam":false,"geometry":{"curve":1035,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2066,"id":1035,"tolerance":1e-8,"twin_halfedge":2091},{"chart_seam":false,"geometry":{"curve":1036,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2068,"id":1036,"tolerance":1e-8,"twin_halfedge":2082},{"chart_seam":false,"geometry":{"curve":1037,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2069,"id":1037,"tolerance":1e-8,"twin_halfedge":2103},{"chart_seam":false,"geometry":{"curve":1038,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2070,"id":1038,"tolerance":1e-8,"twin_halfedge":2079},{"chart_seam":false,"geometry":{"curve":1039,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2072,"id":1039,"tolerance":1e-8,"twin_halfedge":2077},{"chart_seam":false,"geometry":{"curve":1040,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2073,"id":1040,"tolerance":1e-8,"twin_halfedge":2107},{"chart_seam":false,"geometry":{"curve":1041,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2074,"id":1041,"tolerance":1e-8,"twin_halfedge":2084},{"chart_seam":false,"geometry":{"curve":1042,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2076,"id":1042,"tolerance":1e-8,"twin_halfedge":2110},{"chart_seam":false,"geometry":{"curve":1043,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2080,"id":1043,"tolerance":1e-8,"twin_halfedge":2099},{"chart_seam":false,"geometry":{"curve":1044,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2081,"id":1044,"tolerance":1e-8,"twin_halfedge":2092},{"chart_seam":false,"geometry":{"curve":1045,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2083,"id":1045,"tolerance":1e-8,"twin_halfedge":2090},{"chart_seam":false,"geometry":{"curve":1046,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2085,"id":1046,"tolerance":1e-8,"twin_halfedge":2094},{"chart_seam":false,"geometry":{"curve":1047,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2086,"id":1047,"tolerance":1e-8,"twin_halfedge":2097},{"chart_seam":false,"geometry":{"curve":1048,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2087,"id":1048,"tolerance":1e-8,"twin_halfedge":2088},{"chart_seam":false,"geometry":{"curve":1049,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2089,"id":1049,"tolerance":1e-8,"twin_halfedge":2096},{"chart_seam":false,"geometry":{"curve":1050,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2093,"id":1050,"tolerance":1e-8,"twin_halfedge":2098},{"chart_seam":false,"geometry":{"curve":1051,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2095,"id":1051,"tolerance":1e-8,"twin_halfedge":2112},{"chart_seam":false,"geometry":{"curve":1052,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2100,"id":1052,"tolerance":1e-8,"twin_halfedge":2115},{"chart_seam":false,"geometry":{"curve":1053,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2101,"id":1053,"tolerance":1e-8,"twin_halfedge":2119},{"chart_seam":false,"geometry":{"curve":1054,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2102,"id":1054,"tolerance":1e-8,"twin_halfedge":2111},{"chart_seam":false,"geometry":{"curve":1055,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2104,"id":1055,"tolerance":1e-8,"twin_halfedge":2109},{"chart_seam":false,"geometry":{"curve":1056,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2105,"id":1056,"tolerance":1e-8,"twin_halfedge":2123},{"chart_seam":false,"geometry":{"curve":1057,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2106,"id":1057,"tolerance":1e-8,"twin_halfedge":2113},{"chart_seam":false,"geometry":{"curve":1058,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2108,"id":1058,"tolerance":1e-8,"twin_halfedge":2126},{"chart_seam":false,"geometry":{"curve":1059,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2114,"id":1059,"tolerance":1e-8,"twin_halfedge":2139},{"chart_seam":false,"geometry":{"curve":1060,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2116,"id":1060,"tolerance":1e-8,"twin_halfedge":2130},{"chart_seam":false,"geometry":{"curve":1061,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2117,"id":1061,"tolerance":1e-8,"twin_halfedge":2151},{"chart_seam":false,"geometry":{"curve":1062,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2118,"id":1062,"tolerance":1e-8,"twin_halfedge":2127},{"chart_seam":false,"geometry":{"curve":1063,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2120,"id":1063,"tolerance":1e-8,"twin_halfedge":2125},{"chart_seam":false,"geometry":{"curve":1064,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2121,"id":1064,"tolerance":1e-8,"twin_halfedge":2155},{"chart_seam":false,"geometry":{"curve":1065,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2122,"id":1065,"tolerance":1e-8,"twin_halfedge":2132},{"chart_seam":false,"geometry":{"curve":1066,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2124,"id":1066,"tolerance":1e-8,"twin_halfedge":2158},{"chart_seam":false,"geometry":{"curve":1067,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2128,"id":1067,"tolerance":1e-8,"twin_halfedge":2147},{"chart_seam":false,"geometry":{"curve":1068,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2129,"id":1068,"tolerance":1e-8,"twin_halfedge":2140},{"chart_seam":false,"geometry":{"curve":1069,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2131,"id":1069,"tolerance":1e-8,"twin_halfedge":2138},{"chart_seam":false,"geometry":{"curve":1070,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2133,"id":1070,"tolerance":1e-8,"twin_halfedge":2142},{"chart_seam":false,"geometry":{"curve":1071,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2134,"id":1071,"tolerance":1e-8,"twin_halfedge":2145},{"chart_seam":false,"geometry":{"curve":1072,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2135,"id":1072,"tolerance":1e-8,"twin_halfedge":2136},{"chart_seam":false,"geometry":{"curve":1073,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2137,"id":1073,"tolerance":1e-8,"twin_halfedge":2144},{"chart_seam":false,"geometry":{"curve":1074,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2141,"id":1074,"tolerance":1e-8,"twin_halfedge":2146},{"chart_seam":false,"geometry":{"curve":1075,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2143,"id":1075,"tolerance":1e-8,"twin_halfedge":2160},{"chart_seam":false,"geometry":{"curve":1076,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2148,"id":1076,"tolerance":1e-8,"twin_halfedge":2163},{"chart_seam":false,"geometry":{"curve":1077,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2149,"id":1077,"tolerance":1e-8,"twin_halfedge":2167},{"chart_seam":false,"geometry":{"curve":1078,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2150,"id":1078,"tolerance":1e-8,"twin_halfedge":2159},{"chart_seam":false,"geometry":{"curve":1079,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2152,"id":1079,"tolerance":1e-8,"twin_halfedge":2157},{"chart_seam":false,"geometry":{"curve":1080,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2153,"id":1080,"tolerance":1e-8,"twin_halfedge":2171},{"chart_seam":false,"geometry":{"curve":1081,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2154,"id":1081,"tolerance":1e-8,"twin_halfedge":2161},{"chart_seam":false,"geometry":{"curve":1082,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2156,"id":1082,"tolerance":1e-8,"twin_halfedge":2174},{"chart_seam":false,"geometry":{"curve":1083,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2162,"id":1083,"tolerance":1e-8,"twin_halfedge":2187},{"chart_seam":false,"geometry":{"curve":1084,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2164,"id":1084,"tolerance":1e-8,"twin_halfedge":2178},{"chart_seam":false,"geometry":{"curve":1085,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2165,"id":1085,"tolerance":1e-8,"twin_halfedge":2199},{"chart_seam":false,"geometry":{"curve":1086,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2166,"id":1086,"tolerance":1e-8,"twin_halfedge":2175},{"chart_seam":false,"geometry":{"curve":1087,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2168,"id":1087,"tolerance":1e-8,"twin_halfedge":2173},{"chart_seam":false,"geometry":{"curve":1088,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2169,"id":1088,"tolerance":1e-8,"twin_halfedge":2203},{"chart_seam":false,"geometry":{"curve":1089,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2170,"id":1089,"tolerance":1e-8,"twin_halfedge":2180},{"chart_seam":false,"geometry":{"curve":1090,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2172,"id":1090,"tolerance":1e-8,"twin_halfedge":2206},{"chart_seam":false,"geometry":{"curve":1091,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2176,"id":1091,"tolerance":1e-8,"twin_halfedge":2195},{"chart_seam":false,"geometry":{"curve":1092,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2177,"id":1092,"tolerance":1e-8,"twin_halfedge":2188},{"chart_seam":false,"geometry":{"curve":1093,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2179,"id":1093,"tolerance":1e-8,"twin_halfedge":2186},{"chart_seam":false,"geometry":{"curve":1094,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2181,"id":1094,"tolerance":1e-8,"twin_halfedge":2190},{"chart_seam":false,"geometry":{"curve":1095,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2182,"id":1095,"tolerance":1e-8,"twin_halfedge":2193},{"chart_seam":false,"geometry":{"curve":1096,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2183,"id":1096,"tolerance":1e-8,"twin_halfedge":2184},{"chart_seam":false,"geometry":{"curve":1097,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2185,"id":1097,"tolerance":1e-8,"twin_halfedge":2192},{"chart_seam":false,"geometry":{"curve":1098,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2189,"id":1098,"tolerance":1e-8,"twin_halfedge":2194},{"chart_seam":false,"geometry":{"curve":1099,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2191,"id":1099,"tolerance":1e-8,"twin_halfedge":2208},{"chart_seam":false,"geometry":{"curve":1100,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2196,"id":1100,"tolerance":1e-8,"twin_halfedge":2211},{"chart_seam":false,"geometry":{"curve":1101,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2197,"id":1101,"tolerance":1e-8,"twin_halfedge":2215},{"chart_seam":false,"geometry":{"curve":1102,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2198,"id":1102,"tolerance":1e-8,"twin_halfedge":2207},{"chart_seam":false,"geometry":{"curve":1103,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2200,"id":1103,"tolerance":1e-8,"twin_halfedge":2205},{"chart_seam":false,"geometry":{"curve":1104,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2201,"id":1104,"tolerance":1e-8,"twin_halfedge":2219},{"chart_seam":false,"geometry":{"curve":1105,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2202,"id":1105,"tolerance":1e-8,"twin_halfedge":2209},{"chart_seam":false,"geometry":{"curve":1106,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2204,"id":1106,"tolerance":1e-8,"twin_halfedge":2222},{"chart_seam":false,"geometry":{"curve":1107,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2210,"id":1107,"tolerance":1e-8,"twin_halfedge":2235},{"chart_seam":false,"geometry":{"curve":1108,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":2212,"id":1108,"tolerance":1e-8,"twin_halfedge":2226},{"chart_seam":false,"geometry":{"curve":1109,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2213,"id":1109,"tolerance":1e-8,"twin_halfedge":2247},{"chart_seam":false,"geometry":{"curve":1110,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":2214,"id":1110,"tolerance":1e-8,"twin_halfedge":2223},{"chart_seam":false,"geometry":{"curve":1111,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":2216,"id":1111,"tolerance":1e-8,"twin_halfedge":2221},{"chart_seam":false,"geometry":{"curve":1112,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2217,"id":1112,"tolerance":1e-8,"twin_halfedge":2251},{"chart_seam":false,"geometry":{"curve":1113,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":2218,"id":1113,"tolerance":1e-8,"twin_halfedge":2228},{"chart_seam":false,"geometry":{"curve":1114,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2220,"id":1114,"tolerance":1e-8,"twin_halfedge":2254},{"chart_seam":false,"geometry":{"curve":1115,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":2224,"id":1115,"tolerance":1e-8,"twin_halfedge":2243},{"chart_seam":false,"geometry":{"curve":1116,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2225,"id":1116,"tolerance":1e-8,"twin_halfedge":2236},{"chart_seam":false,"geometry":{"curve":1117,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2227,"id":1117,"tolerance":1e-8,"twin_halfedge":2234},{"chart_seam":false,"geometry":{"curve":1118,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2229,"id":1118,"tolerance":1e-8,"twin_halfedge":2238},{"chart_seam":false,"geometry":{"curve":1119,"kind":"Curve","range":{"hi":0.5999999999999943,"lo":0.0}},"halfedge":2230,"id":1119,"tolerance":1e-8,"twin_halfedge":2241},{"chart_seam":false,"geometry":{"curve":1120,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2231,"id":1120,"tolerance":1e-8,"twin_halfedge":2232},{"chart_seam":false,"geometry":{"curve":1121,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2233,"id":1121,"tolerance":1e-8,"twin_halfedge":2240},{"chart_seam":false,"geometry":{"curve":1122,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2237,"id":1122,"tolerance":1e-8,"twin_halfedge":2242},{"chart_seam":false,"geometry":{"curve":1123,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2239,"id":1123,"tolerance":1e-8,"twin_halfedge":2256},{"chart_seam":false,"geometry":{"curve":1124,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2244,"id":1124,"tolerance":1e-8,"twin_halfedge":2259},{"chart_seam":false,"geometry":{"curve":1125,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2245,"id":1125,"tolerance":1e-8,"twin_halfedge":2263},{"chart_seam":false,"geometry":{"curve":1126,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2246,"id":1126,"tolerance":1e-8,"twin_halfedge":2255},{"chart_seam":false,"geometry":{"curve":1127,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2248,"id":1127,"tolerance":1e-8,"twin_halfedge":2253},{"chart_seam":false,"geometry":{"curve":1128,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2249,"id":1128,"tolerance":1e-8,"twin_halfedge":2267},{"chart_seam":false,"geometry":{"curve":1129,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2250,"id":1129,"tolerance":1e-8,"twin_halfedge":2257},{"chart_seam":false,"geometry":{"curve":1130,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2252,"id":1130,"tolerance":1e-8,"twin_halfedge":2270},{"chart_seam":false,"geometry":{"curve":1131,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2258,"id":1131,"tolerance":1e-8,"twin_halfedge":2283},{"chart_seam":false,"geometry":{"curve":1132,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2260,"id":1132,"tolerance":1e-8,"twin_halfedge":2274},{"chart_seam":false,"geometry":{"curve":1133,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2261,"id":1133,"tolerance":1e-8,"twin_halfedge":2295},{"chart_seam":false,"geometry":{"curve":1134,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2262,"id":1134,"tolerance":1e-8,"twin_halfedge":2271},{"chart_seam":false,"geometry":{"curve":1135,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2264,"id":1135,"tolerance":1e-8,"twin_halfedge":2269},{"chart_seam":false,"geometry":{"curve":1136,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2265,"id":1136,"tolerance":1e-8,"twin_halfedge":2299},{"chart_seam":false,"geometry":{"curve":1137,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2266,"id":1137,"tolerance":1e-8,"twin_halfedge":2276},{"chart_seam":false,"geometry":{"curve":1138,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2268,"id":1138,"tolerance":1e-8,"twin_halfedge":2302},{"chart_seam":false,"geometry":{"curve":1139,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2272,"id":1139,"tolerance":1e-8,"twin_halfedge":2291},{"chart_seam":false,"geometry":{"curve":1140,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2273,"id":1140,"tolerance":1e-8,"twin_halfedge":2284},{"chart_seam":false,"geometry":{"curve":1141,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2275,"id":1141,"tolerance":1e-8,"twin_halfedge":2282},{"chart_seam":false,"geometry":{"curve":1142,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2277,"id":1142,"tolerance":1e-8,"twin_halfedge":2286},{"chart_seam":false,"geometry":{"curve":1143,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2278,"id":1143,"tolerance":1e-8,"twin_halfedge":2289},{"chart_seam":false,"geometry":{"curve":1144,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2279,"id":1144,"tolerance":1e-8,"twin_halfedge":2280},{"chart_seam":false,"geometry":{"curve":1145,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2281,"id":1145,"tolerance":1e-8,"twin_halfedge":2288},{"chart_seam":false,"geometry":{"curve":1146,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2285,"id":1146,"tolerance":1e-8,"twin_halfedge":2290},{"chart_seam":false,"geometry":{"curve":1147,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2287,"id":1147,"tolerance":1e-8,"twin_halfedge":2304},{"chart_seam":false,"geometry":{"curve":1148,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2292,"id":1148,"tolerance":1e-8,"twin_halfedge":2307},{"chart_seam":false,"geometry":{"curve":1149,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2293,"id":1149,"tolerance":1e-8,"twin_halfedge":2311},{"chart_seam":false,"geometry":{"curve":1150,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2294,"id":1150,"tolerance":1e-8,"twin_halfedge":2303},{"chart_seam":false,"geometry":{"curve":1151,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2296,"id":1151,"tolerance":1e-8,"twin_halfedge":2301},{"chart_seam":false,"geometry":{"curve":1152,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2297,"id":1152,"tolerance":1e-8,"twin_halfedge":2315},{"chart_seam":false,"geometry":{"curve":1153,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2298,"id":1153,"tolerance":1e-8,"twin_halfedge":2305},{"chart_seam":false,"geometry":{"curve":1154,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2300,"id":1154,"tolerance":1e-8,"twin_halfedge":2318},{"chart_seam":false,"geometry":{"curve":1155,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2306,"id":1155,"tolerance":1e-8,"twin_halfedge":2331},{"chart_seam":false,"geometry":{"curve":1156,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2308,"id":1156,"tolerance":1e-8,"twin_halfedge":2322},{"chart_seam":false,"geometry":{"curve":1157,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2309,"id":1157,"tolerance":1e-8,"twin_halfedge":2343},{"chart_seam":false,"geometry":{"curve":1158,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2310,"id":1158,"tolerance":1e-8,"twin_halfedge":2319},{"chart_seam":false,"geometry":{"curve":1159,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2312,"id":1159,"tolerance":1e-8,"twin_halfedge":2317},{"chart_seam":false,"geometry":{"curve":1160,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2313,"id":1160,"tolerance":1e-8,"twin_halfedge":2347},{"chart_seam":false,"geometry":{"curve":1161,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2314,"id":1161,"tolerance":1e-8,"twin_halfedge":2324},{"chart_seam":false,"geometry":{"curve":1162,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2316,"id":1162,"tolerance":1e-8,"twin_halfedge":2350},{"chart_seam":false,"geometry":{"curve":1163,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2320,"id":1163,"tolerance":1e-8,"twin_halfedge":2339},{"chart_seam":false,"geometry":{"curve":1164,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2321,"id":1164,"tolerance":1e-8,"twin_halfedge":2332},{"chart_seam":false,"geometry":{"curve":1165,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2323,"id":1165,"tolerance":1e-8,"twin_halfedge":2330},{"chart_seam":false,"geometry":{"curve":1166,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2325,"id":1166,"tolerance":1e-8,"twin_halfedge":2334},{"chart_seam":false,"geometry":{"curve":1167,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2326,"id":1167,"tolerance":1e-8,"twin_halfedge":2337},{"chart_seam":false,"geometry":{"curve":1168,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2327,"id":1168,"tolerance":1e-8,"twin_halfedge":2328},{"chart_seam":false,"geometry":{"curve":1169,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2329,"id":1169,"tolerance":1e-8,"twin_halfedge":2336},{"chart_seam":false,"geometry":{"curve":1170,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2333,"id":1170,"tolerance":1e-8,"twin_halfedge":2338},{"chart_seam":false,"geometry":{"curve":1171,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2335,"id":1171,"tolerance":1e-8,"twin_halfedge":2352},{"chart_seam":false,"geometry":{"curve":1172,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2340,"id":1172,"tolerance":1e-8,"twin_halfedge":2355},{"chart_seam":false,"geometry":{"curve":1173,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2341,"id":1173,"tolerance":1e-8,"twin_halfedge":2359},{"chart_seam":false,"geometry":{"curve":1174,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2342,"id":1174,"tolerance":1e-8,"twin_halfedge":2351},{"chart_seam":false,"geometry":{"curve":1175,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2344,"id":1175,"tolerance":1e-8,"twin_halfedge":2349},{"chart_seam":false,"geometry":{"curve":1176,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2345,"id":1176,"tolerance":1e-8,"twin_halfedge":2363},{"chart_seam":false,"geometry":{"curve":1177,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2346,"id":1177,"tolerance":1e-8,"twin_halfedge":2353},{"chart_seam":false,"geometry":{"curve":1178,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2348,"id":1178,"tolerance":1e-8,"twin_halfedge":2366},{"chart_seam":false,"geometry":{"curve":1179,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2354,"id":1179,"tolerance":1e-8,"twin_halfedge":2379},{"chart_seam":false,"geometry":{"curve":1180,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2356,"id":1180,"tolerance":1e-8,"twin_halfedge":2370},{"chart_seam":false,"geometry":{"curve":1181,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2357,"id":1181,"tolerance":1e-8,"twin_halfedge":2391},{"chart_seam":false,"geometry":{"curve":1182,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2358,"id":1182,"tolerance":1e-8,"twin_halfedge":2367},{"chart_seam":false,"geometry":{"curve":1183,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2360,"id":1183,"tolerance":1e-8,"twin_halfedge":2365},{"chart_seam":false,"geometry":{"curve":1184,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2361,"id":1184,"tolerance":1e-8,"twin_halfedge":2395},{"chart_seam":false,"geometry":{"curve":1185,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2362,"id":1185,"tolerance":1e-8,"twin_halfedge":2372},{"chart_seam":false,"geometry":{"curve":1186,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2364,"id":1186,"tolerance":1e-8,"twin_halfedge":2398},{"chart_seam":false,"geometry":{"curve":1187,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2368,"id":1187,"tolerance":1e-8,"twin_halfedge":2387},{"chart_seam":false,"geometry":{"curve":1188,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2369,"id":1188,"tolerance":1e-8,"twin_halfedge":2380},{"chart_seam":false,"geometry":{"curve":1189,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2371,"id":1189,"tolerance":1e-8,"twin_halfedge":2378},{"chart_seam":false,"geometry":{"curve":1190,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2373,"id":1190,"tolerance":1e-8,"twin_halfedge":2382},{"chart_seam":false,"geometry":{"curve":1191,"kind":"Curve","range":{"hi":0.6000000000000014,"lo":0.0}},"halfedge":2374,"id":1191,"tolerance":1e-8,"twin_halfedge":2385},{"chart_seam":false,"geometry":{"curve":1192,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2375,"id":1192,"tolerance":1e-8,"twin_halfedge":2376},{"chart_seam":false,"geometry":{"curve":1193,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2377,"id":1193,"tolerance":1e-8,"twin_halfedge":2384},{"chart_seam":false,"geometry":{"curve":1194,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2381,"id":1194,"tolerance":1e-8,"twin_halfedge":2386},{"chart_seam":false,"geometry":{"curve":1195,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2383,"id":1195,"tolerance":1e-8,"twin_halfedge":2400},{"chart_seam":false,"geometry":{"curve":1196,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2388,"id":1196,"tolerance":1e-8,"twin_halfedge":2403},{"chart_seam":false,"geometry":{"curve":1197,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2389,"id":1197,"tolerance":1e-8,"twin_halfedge":2407},{"chart_seam":false,"geometry":{"curve":1198,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2390,"id":1198,"tolerance":1e-8,"twin_halfedge":2399},{"chart_seam":false,"geometry":{"curve":1199,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2392,"id":1199,"tolerance":1e-8,"twin_halfedge":2397},{"chart_seam":false,"geometry":{"curve":1200,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2393,"id":1200,"tolerance":1e-8,"twin_halfedge":2411},{"chart_seam":false,"geometry":{"curve":1201,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2394,"id":1201,"tolerance":1e-8,"twin_halfedge":2401},{"chart_seam":false,"geometry":{"curve":1202,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2396,"id":1202,"tolerance":1e-8,"twin_halfedge":2418},{"chart_seam":false,"geometry":{"curve":1203,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2402,"id":1203,"tolerance":1e-8,"twin_halfedge":2431},{"chart_seam":false,"geometry":{"curve":1204,"kind":"Curve","range":{"hi":2.8499999999999943,"lo":0.0}},"halfedge":2404,"id":1204,"tolerance":1e-8,"twin_halfedge":2422},{"chart_seam":false,"geometry":{"curve":1205,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2405,"id":1205,"tolerance":1e-8,"twin_halfedge":2412},{"chart_seam":false,"geometry":{"curve":1206,"kind":"Curve","range":{"hi":2.8499999999999943,"lo":0.0}},"halfedge":2406,"id":1206,"tolerance":1e-8,"twin_halfedge":2419},{"chart_seam":false,"geometry":{"curve":1207,"kind":"Curve","range":{"hi":2.8499999999999943,"lo":0.0}},"halfedge":2408,"id":1207,"tolerance":1e-8,"twin_halfedge":2417},{"chart_seam":false,"geometry":{"curve":1208,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2409,"id":1208,"tolerance":1e-8,"twin_halfedge":2414},{"chart_seam":false,"geometry":{"curve":1209,"kind":"Curve","range":{"hi":2.8499999999999943,"lo":0.0}},"halfedge":2410,"id":1209,"tolerance":1e-8,"twin_halfedge":2424},{"chart_seam":false,"geometry":{"curve":1210,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2413,"id":1210,"tolerance":1e-8,"twin_halfedge":2435},{"chart_seam":false,"geometry":{"curve":1211,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2415,"id":1211,"tolerance":1e-8,"twin_halfedge":2416},{"chart_seam":false,"geometry":{"curve":1212,"kind":"Curve","range":{"hi":2.8499999999999943,"lo":0.0}},"halfedge":2420,"id":1212,"tolerance":1e-8,"twin_halfedge":2439},{"chart_seam":false,"geometry":{"curve":1213,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2421,"id":1213,"tolerance":1e-8,"twin_halfedge":2432},{"chart_seam":false,"geometry":{"curve":1214,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2423,"id":1214,"tolerance":1e-8,"twin_halfedge":2430},{"chart_seam":false,"geometry":{"curve":1215,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2425,"id":1215,"tolerance":1e-8,"twin_halfedge":2434},{"chart_seam":false,"geometry":{"curve":1216,"kind":"Curve","range":{"hi":2.8499999999999943,"lo":0.0}},"halfedge":2426,"id":1216,"tolerance":1e-8,"twin_halfedge":2437},{"chart_seam":false,"geometry":{"curve":1217,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2427,"id":1217,"tolerance":1e-8,"twin_halfedge":2428},{"chart_seam":false,"geometry":{"curve":1218,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2429,"id":1218,"tolerance":1e-8,"twin_halfedge":2436},{"chart_seam":false,"geometry":{"curve":1219,"kind":"Curve","range":{"hi":0.2,"lo":0.0}},"halfedge":2433,"id":1219,"tolerance":1e-8,"twin_halfedge":2438}],"faces":[{"id":0,"key":"side-0-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.75000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.99999999}]}},{"id":1,"key":"side-0-2-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":60.00000001,"lo":57.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":2,"key":"side-0-3-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.20000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.99999999}]}},{"id":3,"key":"top-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":-27.24999999,"lo":-30.00000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":4,"key":"side-0-0-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.75000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"side-0-2-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":60.00000001,"lo":57.24999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":6,"key":"side-0-3-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":0.20000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":7,"key":"cut-0:left-7","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-0","entity":"cut-0","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":8,"key":"bottom-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":-27.24999999,"lo":-30.00000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":9,"key":"side-0-0-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":3.25000001,"lo":2.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":10,"key":"side-0-2-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":57.25000001,"lo":56.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":11,"key":"top-11","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":-26.74999999,"lo":-27.25000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":12,"key":"cut-0:top-12","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-0","entity":"cut-0","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":13,"key":"side-0-0-13","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":3.8500000100000014,"lo":3.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":14,"key":"side-0-2-14","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":56.75000001,"lo":56.14999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":15,"key":"top-15","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":-26.149999989999998,"lo":-26.75000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":16,"key":"side-0-0-16","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[],"outer":16,"uv_bounds":[{"hi":3.8500000100000014,"lo":3.24999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":17,"key":"side-0-2-17","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":17,"trim":{"chart":0,"holes":[],"outer":17,"uv_bounds":[{"hi":56.75000001,"lo":56.14999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":18,"key":"cut-0:right-18","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-0","entity":"cut-0","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":18,"trim":{"chart":0,"holes":[],"outer":18,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":19,"key":"cut-1:left-19","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-1","entity":"cut-1","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":19,"trim":{"chart":0,"holes":[],"outer":19,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":20,"key":"bottom-20","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":20,"trim":{"chart":0,"holes":[],"outer":20,"uv_bounds":[{"hi":-26.149999989999998,"lo":-26.75000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":21,"key":"side-0-0-21","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":21,"trim":{"chart":0,"holes":[],"outer":21,"uv_bounds":[{"hi":4.350000010000001,"lo":3.8499999900000015},{"hi":3.00000001,"lo":1.99999999}]}},{"id":22,"key":"side-0-2-22","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":22,"trim":{"chart":0,"holes":[],"outer":22,"uv_bounds":[{"hi":56.15000001,"lo":55.64999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":23,"key":"top-23","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":23,"trim":{"chart":0,"holes":[],"outer":23,"uv_bounds":[{"hi":-25.649999989999998,"lo":-26.15000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":24,"key":"cut-1:top-24","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-1","entity":"cut-1","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":24,"trim":{"chart":0,"holes":[],"outer":24,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":25,"key":"side-0-0-25","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":25,"trim":{"chart":0,"holes":[],"outer":25,"uv_bounds":[{"hi":4.950000009999999,"lo":4.3499999900000015},{"hi":3.00000001,"lo":1.99999999}]}},{"id":26,"key":"side-0-2-26","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":26,"trim":{"chart":0,"holes":[],"outer":26,"uv_bounds":[{"hi":55.65000001,"lo":55.049999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":27,"key":"top-27","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":27,"trim":{"chart":0,"holes":[],"outer":27,"uv_bounds":[{"hi":-25.04999999,"lo":-25.65000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":28,"key":"side-0-0-28","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":28,"trim":{"chart":0,"holes":[],"outer":28,"uv_bounds":[{"hi":4.950000009999999,"lo":4.3499999900000015},{"hi":2.00000001,"lo":-1e-8}]}},{"id":29,"key":"side-0-2-29","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":29,"trim":{"chart":0,"holes":[],"outer":29,"uv_bounds":[{"hi":55.65000001,"lo":55.049999989999996},{"hi":2.00000001,"lo":-1e-8}]}},{"id":30,"key":"cut-1:right-30","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-1","entity":"cut-1","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":30,"trim":{"chart":0,"holes":[],"outer":30,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":31,"key":"cut-2:left-31","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-2","entity":"cut-2","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":31,"trim":{"chart":0,"holes":[],"outer":31,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":32,"key":"bottom-32","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":32,"trim":{"chart":0,"holes":[],"outer":32,"uv_bounds":[{"hi":-25.04999999,"lo":-25.65000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":33,"key":"side-0-0-33","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":33,"trim":{"chart":0,"holes":[],"outer":33,"uv_bounds":[{"hi":5.450000009999999,"lo":4.949999989999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":34,"key":"side-0-2-34","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":34,"trim":{"chart":0,"holes":[],"outer":34,"uv_bounds":[{"hi":55.05000001,"lo":54.549999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":35,"key":"top-35","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":35,"trim":{"chart":0,"holes":[],"outer":35,"uv_bounds":[{"hi":-24.54999999,"lo":-25.05000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":36,"key":"cut-2:top-36","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-2","entity":"cut-2","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":36,"trim":{"chart":0,"holes":[],"outer":36,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":37,"key":"side-0-0-37","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":37,"trim":{"chart":0,"holes":[],"outer":37,"uv_bounds":[{"hi":6.050000010000001,"lo":5.449999989999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":38,"key":"side-0-2-38","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":38,"trim":{"chart":0,"holes":[],"outer":38,"uv_bounds":[{"hi":54.55000001,"lo":53.94999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":39,"key":"top-39","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":39,"trim":{"chart":0,"holes":[],"outer":39,"uv_bounds":[{"hi":-23.94999999,"lo":-24.55000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":40,"key":"side-0-0-40","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":40,"trim":{"chart":0,"holes":[],"outer":40,"uv_bounds":[{"hi":6.050000010000001,"lo":5.449999989999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":41,"key":"side-0-2-41","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":41,"trim":{"chart":0,"holes":[],"outer":41,"uv_bounds":[{"hi":54.55000001,"lo":53.94999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":42,"key":"cut-2:right-42","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-2","entity":"cut-2","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":42,"trim":{"chart":0,"holes":[],"outer":42,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":43,"key":"cut-3:left-43","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-3","entity":"cut-3","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":43,"trim":{"chart":0,"holes":[],"outer":43,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":44,"key":"bottom-44","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":44,"trim":{"chart":0,"holes":[],"outer":44,"uv_bounds":[{"hi":-23.94999999,"lo":-24.55000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":45,"key":"side-0-0-45","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":45,"trim":{"chart":0,"holes":[],"outer":45,"uv_bounds":[{"hi":6.550000010000001,"lo":6.049999990000001},{"hi":3.00000001,"lo":1.99999999}]}},{"id":46,"key":"side-0-2-46","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":46,"trim":{"chart":0,"holes":[],"outer":46,"uv_bounds":[{"hi":53.950000010000004,"lo":53.44999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":47,"key":"top-47","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":47,"trim":{"chart":0,"holes":[],"outer":47,"uv_bounds":[{"hi":-23.44999999,"lo":-23.95000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":48,"key":"cut-3:top-48","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-3","entity":"cut-3","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":48,"trim":{"chart":0,"holes":[],"outer":48,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":49,"key":"side-0-0-49","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":49,"trim":{"chart":0,"holes":[],"outer":49,"uv_bounds":[{"hi":7.1500000099999985,"lo":6.549999990000001},{"hi":3.00000001,"lo":1.99999999}]}},{"id":50,"key":"side-0-2-50","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":50,"trim":{"chart":0,"holes":[],"outer":50,"uv_bounds":[{"hi":53.450000010000004,"lo":52.84999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":51,"key":"top-51","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":51,"trim":{"chart":0,"holes":[],"outer":51,"uv_bounds":[{"hi":-22.84999999,"lo":-23.45000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":52,"key":"side-0-0-52","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":52,"trim":{"chart":0,"holes":[],"outer":52,"uv_bounds":[{"hi":7.1500000099999985,"lo":6.549999990000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":53,"key":"side-0-2-53","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":53,"trim":{"chart":0,"holes":[],"outer":53,"uv_bounds":[{"hi":53.450000010000004,"lo":52.84999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":54,"key":"cut-3:right-54","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-3","entity":"cut-3","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":54,"trim":{"chart":0,"holes":[],"outer":54,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":55,"key":"cut-4:left-55","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-4","entity":"cut-4","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":55,"trim":{"chart":0,"holes":[],"outer":55,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":56,"key":"bottom-56","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":56,"trim":{"chart":0,"holes":[],"outer":56,"uv_bounds":[{"hi":-22.84999999,"lo":-23.45000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":57,"key":"side-0-0-57","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":57,"trim":{"chart":0,"holes":[],"outer":57,"uv_bounds":[{"hi":7.6500000099999985,"lo":7.149999989999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":58,"key":"side-0-2-58","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":58,"trim":{"chart":0,"holes":[],"outer":58,"uv_bounds":[{"hi":52.85000001,"lo":52.34999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":59,"key":"top-59","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":59,"trim":{"chart":0,"holes":[],"outer":59,"uv_bounds":[{"hi":-22.34999999,"lo":-22.850000010000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":60,"key":"cut-4:top-60","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-4","entity":"cut-4","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":60,"trim":{"chart":0,"holes":[],"outer":60,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":61,"key":"side-0-0-61","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":61,"trim":{"chart":0,"holes":[],"outer":61,"uv_bounds":[{"hi":8.25000001,"lo":7.649999989999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":62,"key":"side-0-2-62","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":62,"trim":{"chart":0,"holes":[],"outer":62,"uv_bounds":[{"hi":52.35000001,"lo":51.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":63,"key":"top-63","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":63,"trim":{"chart":0,"holes":[],"outer":63,"uv_bounds":[{"hi":-21.74999999,"lo":-22.350000010000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":64,"key":"side-0-0-64","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":64,"trim":{"chart":0,"holes":[],"outer":64,"uv_bounds":[{"hi":8.25000001,"lo":7.649999989999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":65,"key":"side-0-2-65","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":65,"trim":{"chart":0,"holes":[],"outer":65,"uv_bounds":[{"hi":52.35000001,"lo":51.74999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":66,"key":"cut-4:right-66","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-4","entity":"cut-4","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":66,"trim":{"chart":0,"holes":[],"outer":66,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":67,"key":"cut-5:left-67","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-5","entity":"cut-5","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":67,"trim":{"chart":0,"holes":[],"outer":67,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":68,"key":"bottom-68","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":68,"trim":{"chart":0,"holes":[],"outer":68,"uv_bounds":[{"hi":-21.74999999,"lo":-22.350000010000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":69,"key":"side-0-0-69","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":69,"trim":{"chart":0,"holes":[],"outer":69,"uv_bounds":[{"hi":8.75000001,"lo":8.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":70,"key":"side-0-2-70","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":70,"trim":{"chart":0,"holes":[],"outer":70,"uv_bounds":[{"hi":51.75000001,"lo":51.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":71,"key":"top-71","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":71,"trim":{"chart":0,"holes":[],"outer":71,"uv_bounds":[{"hi":-21.24999999,"lo":-21.75000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":72,"key":"cut-5:top-72","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-5","entity":"cut-5","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":72,"trim":{"chart":0,"holes":[],"outer":72,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":73,"key":"side-0-0-73","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":73,"trim":{"chart":0,"holes":[],"outer":73,"uv_bounds":[{"hi":9.350000010000002,"lo":8.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":74,"key":"side-0-2-74","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":74,"trim":{"chart":0,"holes":[],"outer":74,"uv_bounds":[{"hi":51.25000001,"lo":50.64999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":75,"key":"top-75","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":75,"trim":{"chart":0,"holes":[],"outer":75,"uv_bounds":[{"hi":-20.649999989999998,"lo":-21.25000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":76,"key":"side-0-0-76","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":76,"trim":{"chart":0,"holes":[],"outer":76,"uv_bounds":[{"hi":9.350000010000002,"lo":8.74999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":77,"key":"side-0-2-77","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":77,"trim":{"chart":0,"holes":[],"outer":77,"uv_bounds":[{"hi":51.25000001,"lo":50.64999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":78,"key":"cut-5:right-78","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-5","entity":"cut-5","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":78,"trim":{"chart":0,"holes":[],"outer":78,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":79,"key":"cut-6:left-79","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-6","entity":"cut-6","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":79,"trim":{"chart":0,"holes":[],"outer":79,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":80,"key":"bottom-80","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":80,"trim":{"chart":0,"holes":[],"outer":80,"uv_bounds":[{"hi":-20.649999989999998,"lo":-21.25000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":81,"key":"side-0-0-81","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":81,"trim":{"chart":0,"holes":[],"outer":81,"uv_bounds":[{"hi":9.850000010000002,"lo":9.34999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":82,"key":"side-0-2-82","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":82,"trim":{"chart":0,"holes":[],"outer":82,"uv_bounds":[{"hi":50.65000001,"lo":50.14999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":83,"key":"top-83","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":83,"trim":{"chart":0,"holes":[],"outer":83,"uv_bounds":[{"hi":-20.149999989999998,"lo":-20.65000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":84,"key":"cut-6:top-84","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-6","entity":"cut-6","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":84,"trim":{"chart":0,"holes":[],"outer":84,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":85,"key":"side-0-0-85","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":85,"trim":{"chart":0,"holes":[],"outer":85,"uv_bounds":[{"hi":10.450000010000004,"lo":9.84999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":86,"key":"side-0-2-86","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":86,"trim":{"chart":0,"holes":[],"outer":86,"uv_bounds":[{"hi":50.15000001,"lo":49.549999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":87,"key":"top-87","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":87,"trim":{"chart":0,"holes":[],"outer":87,"uv_bounds":[{"hi":-19.549999989999996,"lo":-20.15000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":88,"key":"side-0-0-88","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":88,"trim":{"chart":0,"holes":[],"outer":88,"uv_bounds":[{"hi":10.450000010000004,"lo":9.84999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":89,"key":"side-0-2-89","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":89,"trim":{"chart":0,"holes":[],"outer":89,"uv_bounds":[{"hi":50.15000001,"lo":49.549999989999996},{"hi":2.00000001,"lo":-1e-8}]}},{"id":90,"key":"cut-6:right-90","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-6","entity":"cut-6","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":90,"trim":{"chart":0,"holes":[],"outer":90,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":91,"key":"cut-7:left-91","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-7","entity":"cut-7","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":91,"trim":{"chart":0,"holes":[],"outer":91,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":92,"key":"bottom-92","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":92,"trim":{"chart":0,"holes":[],"outer":92,"uv_bounds":[{"hi":-19.549999989999996,"lo":-20.15000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":93,"key":"side-0-0-93","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":93,"trim":{"chart":0,"holes":[],"outer":93,"uv_bounds":[{"hi":10.950000010000004,"lo":10.449999990000002},{"hi":3.00000001,"lo":1.99999999}]}},{"id":94,"key":"side-0-2-94","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":94,"trim":{"chart":0,"holes":[],"outer":94,"uv_bounds":[{"hi":49.55000001,"lo":49.049999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":95,"key":"top-95","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":95,"trim":{"chart":0,"holes":[],"outer":95,"uv_bounds":[{"hi":-19.049999989999996,"lo":-19.550000009999998},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":96,"key":"cut-7:top-96","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-7","entity":"cut-7","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":96,"trim":{"chart":0,"holes":[],"outer":96,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":97,"key":"side-0-0-97","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":97,"trim":{"chart":0,"holes":[],"outer":97,"uv_bounds":[{"hi":11.550000010000002,"lo":10.949999990000002},{"hi":3.00000001,"lo":1.99999999}]}},{"id":98,"key":"side-0-2-98","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":98,"trim":{"chart":0,"holes":[],"outer":98,"uv_bounds":[{"hi":49.05000001,"lo":48.44999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":99,"key":"top-99","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":99,"trim":{"chart":0,"holes":[],"outer":99,"uv_bounds":[{"hi":-18.44999999,"lo":-19.050000009999998},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":100,"key":"side-0-0-100","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":100,"trim":{"chart":0,"holes":[],"outer":100,"uv_bounds":[{"hi":11.550000010000002,"lo":10.949999990000002},{"hi":2.00000001,"lo":-1e-8}]}},{"id":101,"key":"side-0-2-101","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":101,"trim":{"chart":0,"holes":[],"outer":101,"uv_bounds":[{"hi":49.05000001,"lo":48.44999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":102,"key":"cut-7:right-102","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-7","entity":"cut-7","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":102,"trim":{"chart":0,"holes":[],"outer":102,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":103,"key":"cut-8:left-103","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-8","entity":"cut-8","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":103,"trim":{"chart":0,"holes":[],"outer":103,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":104,"key":"bottom-104","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":104,"trim":{"chart":0,"holes":[],"outer":104,"uv_bounds":[{"hi":-18.44999999,"lo":-19.050000009999998},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":105,"key":"side-0-0-105","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":105,"trim":{"chart":0,"holes":[],"outer":105,"uv_bounds":[{"hi":12.050000010000002,"lo":11.54999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":106,"key":"side-0-2-106","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":106,"trim":{"chart":0,"holes":[],"outer":106,"uv_bounds":[{"hi":48.450000010000004,"lo":47.94999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":107,"key":"top-107","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":107,"trim":{"chart":0,"holes":[],"outer":107,"uv_bounds":[{"hi":-17.94999999,"lo":-18.45000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":108,"key":"cut-8:top-108","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-8","entity":"cut-8","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":108,"trim":{"chart":0,"holes":[],"outer":108,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":109,"key":"side-0-0-109","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":109,"trim":{"chart":0,"holes":[],"outer":109,"uv_bounds":[{"hi":12.65000001,"lo":12.04999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":110,"key":"side-0-2-110","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":110,"trim":{"chart":0,"holes":[],"outer":110,"uv_bounds":[{"hi":47.950000010000004,"lo":47.34999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":111,"key":"top-111","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":111,"trim":{"chart":0,"holes":[],"outer":111,"uv_bounds":[{"hi":-17.34999999,"lo":-17.95000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":112,"key":"side-0-0-112","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":112,"trim":{"chart":0,"holes":[],"outer":112,"uv_bounds":[{"hi":12.65000001,"lo":12.04999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":113,"key":"side-0-2-113","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":113,"trim":{"chart":0,"holes":[],"outer":113,"uv_bounds":[{"hi":47.950000010000004,"lo":47.34999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":114,"key":"cut-8:right-114","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-8","entity":"cut-8","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":114,"trim":{"chart":0,"holes":[],"outer":114,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":115,"key":"cut-9:left-115","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-9","entity":"cut-9","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":115,"trim":{"chart":0,"holes":[],"outer":115,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":116,"key":"bottom-116","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":116,"trim":{"chart":0,"holes":[],"outer":116,"uv_bounds":[{"hi":-17.34999999,"lo":-17.95000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":117,"key":"side-0-0-117","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":117,"trim":{"chart":0,"holes":[],"outer":117,"uv_bounds":[{"hi":13.15000001,"lo":12.649999989999998},{"hi":3.00000001,"lo":1.99999999}]}},{"id":118,"key":"side-0-2-118","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":118,"trim":{"chart":0,"holes":[],"outer":118,"uv_bounds":[{"hi":47.35000001,"lo":46.84999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":119,"key":"top-119","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":119,"trim":{"chart":0,"holes":[],"outer":119,"uv_bounds":[{"hi":-16.84999999,"lo":-17.350000010000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":120,"key":"cut-9:top-120","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-9","entity":"cut-9","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":120,"trim":{"chart":0,"holes":[],"outer":120,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":121,"key":"side-0-0-121","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":121,"trim":{"chart":0,"holes":[],"outer":121,"uv_bounds":[{"hi":13.75000001,"lo":13.149999989999998},{"hi":3.00000001,"lo":1.99999999}]}},{"id":122,"key":"side-0-2-122","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":122,"trim":{"chart":0,"holes":[],"outer":122,"uv_bounds":[{"hi":46.85000001,"lo":46.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":123,"key":"top-123","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":123,"trim":{"chart":0,"holes":[],"outer":123,"uv_bounds":[{"hi":-16.24999999,"lo":-16.850000010000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":124,"key":"side-0-0-124","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":124,"trim":{"chart":0,"holes":[],"outer":124,"uv_bounds":[{"hi":13.75000001,"lo":13.149999989999998},{"hi":2.00000001,"lo":-1e-8}]}},{"id":125,"key":"side-0-2-125","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":125,"trim":{"chart":0,"holes":[],"outer":125,"uv_bounds":[{"hi":46.85000001,"lo":46.24999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":126,"key":"cut-9:right-126","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-9","entity":"cut-9","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":126,"trim":{"chart":0,"holes":[],"outer":126,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":127,"key":"cut-10:left-127","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-10","entity":"cut-10","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":127,"trim":{"chart":0,"holes":[],"outer":127,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":128,"key":"bottom-128","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":128,"trim":{"chart":0,"holes":[],"outer":128,"uv_bounds":[{"hi":-16.24999999,"lo":-16.850000010000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":129,"key":"side-0-0-129","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":129,"trim":{"chart":0,"holes":[],"outer":129,"uv_bounds":[{"hi":14.25000001,"lo":13.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":130,"key":"side-0-2-130","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":130,"trim":{"chart":0,"holes":[],"outer":130,"uv_bounds":[{"hi":46.25000001,"lo":45.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":131,"key":"top-131","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":131,"trim":{"chart":0,"holes":[],"outer":131,"uv_bounds":[{"hi":-15.74999999,"lo":-16.25000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":132,"key":"cut-10:top-132","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-10","entity":"cut-10","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":132,"trim":{"chart":0,"holes":[],"outer":132,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":133,"key":"side-0-0-133","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":133,"trim":{"chart":0,"holes":[],"outer":133,"uv_bounds":[{"hi":14.850000010000002,"lo":14.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":134,"key":"side-0-2-134","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":134,"trim":{"chart":0,"holes":[],"outer":134,"uv_bounds":[{"hi":45.75000001,"lo":45.14999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":135,"key":"top-135","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":135,"trim":{"chart":0,"holes":[],"outer":135,"uv_bounds":[{"hi":-15.149999989999998,"lo":-15.75000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":136,"key":"side-0-0-136","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":136,"trim":{"chart":0,"holes":[],"outer":136,"uv_bounds":[{"hi":14.850000010000002,"lo":14.24999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":137,"key":"side-0-2-137","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":137,"trim":{"chart":0,"holes":[],"outer":137,"uv_bounds":[{"hi":45.75000001,"lo":45.14999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":138,"key":"cut-10:right-138","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-10","entity":"cut-10","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":138,"trim":{"chart":0,"holes":[],"outer":138,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":139,"key":"cut-11:left-139","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-11","entity":"cut-11","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":139,"trim":{"chart":0,"holes":[],"outer":139,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":140,"key":"bottom-140","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":140,"trim":{"chart":0,"holes":[],"outer":140,"uv_bounds":[{"hi":-15.149999989999998,"lo":-15.75000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":141,"key":"side-0-0-141","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":141,"trim":{"chart":0,"holes":[],"outer":141,"uv_bounds":[{"hi":15.350000010000002,"lo":14.84999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":142,"key":"side-0-2-142","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":142,"trim":{"chart":0,"holes":[],"outer":142,"uv_bounds":[{"hi":45.15000001,"lo":44.64999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":143,"key":"top-143","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":143,"trim":{"chart":0,"holes":[],"outer":143,"uv_bounds":[{"hi":-14.649999989999998,"lo":-15.15000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":144,"key":"cut-11:top-144","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-11","entity":"cut-11","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":144,"trim":{"chart":0,"holes":[],"outer":144,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":145,"key":"side-0-0-145","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":145,"trim":{"chart":0,"holes":[],"outer":145,"uv_bounds":[{"hi":15.950000010000002,"lo":15.34999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":146,"key":"side-0-2-146","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":146,"trim":{"chart":0,"holes":[],"outer":146,"uv_bounds":[{"hi":44.65000001,"lo":44.049999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":147,"key":"top-147","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":147,"trim":{"chart":0,"holes":[],"outer":147,"uv_bounds":[{"hi":-14.049999989999998,"lo":-14.65000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":148,"key":"side-0-0-148","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":148,"trim":{"chart":0,"holes":[],"outer":148,"uv_bounds":[{"hi":15.950000010000002,"lo":15.34999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":149,"key":"side-0-2-149","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":149,"trim":{"chart":0,"holes":[],"outer":149,"uv_bounds":[{"hi":44.65000001,"lo":44.049999989999996},{"hi":2.00000001,"lo":-1e-8}]}},{"id":150,"key":"cut-11:right-150","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-11","entity":"cut-11","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":150,"trim":{"chart":0,"holes":[],"outer":150,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":151,"key":"cut-12:left-151","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-12","entity":"cut-12","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":151,"trim":{"chart":0,"holes":[],"outer":151,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":152,"key":"bottom-152","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":152,"trim":{"chart":0,"holes":[],"outer":152,"uv_bounds":[{"hi":-14.049999989999998,"lo":-14.65000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":153,"key":"side-0-0-153","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":153,"trim":{"chart":0,"holes":[],"outer":153,"uv_bounds":[{"hi":16.450000010000004,"lo":15.94999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":154,"key":"side-0-2-154","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":154,"trim":{"chart":0,"holes":[],"outer":154,"uv_bounds":[{"hi":44.05000001,"lo":43.549999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":155,"key":"top-155","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":155,"trim":{"chart":0,"holes":[],"outer":155,"uv_bounds":[{"hi":-13.549999989999996,"lo":-14.05000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":156,"key":"cut-12:top-156","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-12","entity":"cut-12","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":156,"trim":{"chart":0,"holes":[],"outer":156,"uv_bounds":[{"hi":0.5000000100000018,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":157,"key":"side-0-0-157","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":157,"trim":{"chart":0,"holes":[],"outer":157,"uv_bounds":[{"hi":17.05000001,"lo":16.449999990000002},{"hi":3.00000001,"lo":1.99999999}]}},{"id":158,"key":"side-0-2-158","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":158,"trim":{"chart":0,"holes":[],"outer":158,"uv_bounds":[{"hi":43.55000001,"lo":42.94999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":159,"key":"top-159","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":159,"trim":{"chart":0,"holes":[],"outer":159,"uv_bounds":[{"hi":-12.949999989999998,"lo":-13.550000009999998},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":160,"key":"side-0-0-160","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":160,"trim":{"chart":0,"holes":[],"outer":160,"uv_bounds":[{"hi":17.05000001,"lo":16.449999990000002},{"hi":2.00000001,"lo":-1e-8}]}},{"id":161,"key":"side-0-2-161","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":161,"trim":{"chart":0,"holes":[],"outer":161,"uv_bounds":[{"hi":43.55000001,"lo":42.94999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":162,"key":"cut-12:right-162","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-12","entity":"cut-12","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":162,"trim":{"chart":0,"holes":[],"outer":162,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":163,"key":"cut-13:left-163","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-13","entity":"cut-13","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":163,"trim":{"chart":0,"holes":[],"outer":163,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":164,"key":"bottom-164","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":164,"trim":{"chart":0,"holes":[],"outer":164,"uv_bounds":[{"hi":-12.949999989999998,"lo":-13.550000009999998},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":165,"key":"side-0-0-165","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":165,"trim":{"chart":0,"holes":[],"outer":165,"uv_bounds":[{"hi":17.55000001,"lo":17.04999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":166,"key":"side-0-2-166","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":166,"trim":{"chart":0,"holes":[],"outer":166,"uv_bounds":[{"hi":42.950000010000004,"lo":42.44999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":167,"key":"top-167","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":167,"trim":{"chart":0,"holes":[],"outer":167,"uv_bounds":[{"hi":-12.449999989999998,"lo":-12.95000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":168,"key":"cut-13:top-168","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-13","entity":"cut-13","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":168,"trim":{"chart":0,"holes":[],"outer":168,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":169,"key":"side-0-0-169","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":169,"trim":{"chart":0,"holes":[],"outer":169,"uv_bounds":[{"hi":18.150000010000003,"lo":17.54999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":170,"key":"side-0-2-170","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":170,"trim":{"chart":0,"holes":[],"outer":170,"uv_bounds":[{"hi":42.450000010000004,"lo":41.84999998999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":171,"key":"top-171","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":171,"trim":{"chart":0,"holes":[],"outer":171,"uv_bounds":[{"hi":-11.849999989999997,"lo":-12.45000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":172,"key":"side-0-0-172","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":172,"trim":{"chart":0,"holes":[],"outer":172,"uv_bounds":[{"hi":18.150000010000003,"lo":17.54999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":173,"key":"side-0-2-173","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":173,"trim":{"chart":0,"holes":[],"outer":173,"uv_bounds":[{"hi":42.450000010000004,"lo":41.84999998999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":174,"key":"cut-13:right-174","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-13","entity":"cut-13","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":174,"trim":{"chart":0,"holes":[],"outer":174,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":175,"key":"cut-14:left-175","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-14","entity":"cut-14","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":175,"trim":{"chart":0,"holes":[],"outer":175,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":176,"key":"bottom-176","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":176,"trim":{"chart":0,"holes":[],"outer":176,"uv_bounds":[{"hi":-11.849999989999997,"lo":-12.45000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":177,"key":"side-0-0-177","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":177,"trim":{"chart":0,"holes":[],"outer":177,"uv_bounds":[{"hi":18.650000010000003,"lo":18.14999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":178,"key":"side-0-2-178","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":178,"trim":{"chart":0,"holes":[],"outer":178,"uv_bounds":[{"hi":41.850000009999995,"lo":41.34999998999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":179,"key":"top-179","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":179,"trim":{"chart":0,"holes":[],"outer":179,"uv_bounds":[{"hi":-11.349999989999997,"lo":-11.850000009999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":180,"key":"cut-14:top-180","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-14","entity":"cut-14","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":180,"trim":{"chart":0,"holes":[],"outer":180,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":181,"key":"side-0-0-181","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":181,"trim":{"chart":0,"holes":[],"outer":181,"uv_bounds":[{"hi":19.25000001,"lo":18.64999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":182,"key":"side-0-2-182","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":182,"trim":{"chart":0,"holes":[],"outer":182,"uv_bounds":[{"hi":41.350000009999995,"lo":40.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":183,"key":"top-183","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":183,"trim":{"chart":0,"holes":[],"outer":183,"uv_bounds":[{"hi":-10.74999999,"lo":-11.350000009999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":184,"key":"side-0-0-184","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":184,"trim":{"chart":0,"holes":[],"outer":184,"uv_bounds":[{"hi":19.25000001,"lo":18.64999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":185,"key":"side-0-2-185","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":185,"trim":{"chart":0,"holes":[],"outer":185,"uv_bounds":[{"hi":41.350000009999995,"lo":40.74999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":186,"key":"cut-14:right-186","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-14","entity":"cut-14","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":186,"trim":{"chart":0,"holes":[],"outer":186,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":187,"key":"cut-15:left-187","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-15","entity":"cut-15","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":187,"trim":{"chart":0,"holes":[],"outer":187,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":188,"key":"bottom-188","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":188,"trim":{"chart":0,"holes":[],"outer":188,"uv_bounds":[{"hi":-10.74999999,"lo":-11.350000009999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":189,"key":"side-0-0-189","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":189,"trim":{"chart":0,"holes":[],"outer":189,"uv_bounds":[{"hi":19.75000001,"lo":19.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":190,"key":"side-0-2-190","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":190,"trim":{"chart":0,"holes":[],"outer":190,"uv_bounds":[{"hi":40.75000001,"lo":40.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":191,"key":"top-191","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":191,"trim":{"chart":0,"holes":[],"outer":191,"uv_bounds":[{"hi":-10.24999999,"lo":-10.75000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":192,"key":"cut-15:top-192","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-15","entity":"cut-15","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":192,"trim":{"chart":0,"holes":[],"outer":192,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":193,"key":"side-0-0-193","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":193,"trim":{"chart":0,"holes":[],"outer":193,"uv_bounds":[{"hi":20.350000010000002,"lo":19.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":194,"key":"side-0-2-194","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":194,"trim":{"chart":0,"holes":[],"outer":194,"uv_bounds":[{"hi":40.25000001,"lo":39.64999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":195,"key":"top-195","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":195,"trim":{"chart":0,"holes":[],"outer":195,"uv_bounds":[{"hi":-9.649999989999998,"lo":-10.25000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":196,"key":"side-0-0-196","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":196,"trim":{"chart":0,"holes":[],"outer":196,"uv_bounds":[{"hi":20.350000010000002,"lo":19.74999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":197,"key":"side-0-2-197","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":197,"trim":{"chart":0,"holes":[],"outer":197,"uv_bounds":[{"hi":40.25000001,"lo":39.64999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":198,"key":"cut-15:right-198","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-15","entity":"cut-15","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":198,"trim":{"chart":0,"holes":[],"outer":198,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":199,"key":"cut-16:left-199","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-16","entity":"cut-16","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":199,"trim":{"chart":0,"holes":[],"outer":199,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":200,"key":"bottom-200","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":200,"trim":{"chart":0,"holes":[],"outer":200,"uv_bounds":[{"hi":-9.649999989999998,"lo":-10.25000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":201,"key":"side-0-0-201","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":201,"trim":{"chart":0,"holes":[],"outer":201,"uv_bounds":[{"hi":20.850000010000002,"lo":20.34999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":202,"key":"side-0-2-202","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":202,"trim":{"chart":0,"holes":[],"outer":202,"uv_bounds":[{"hi":39.65000001,"lo":39.14999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":203,"key":"top-203","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":203,"trim":{"chart":0,"holes":[],"outer":203,"uv_bounds":[{"hi":-9.149999989999998,"lo":-9.65000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":204,"key":"cut-16:top-204","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-16","entity":"cut-16","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":204,"trim":{"chart":0,"holes":[],"outer":204,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":205,"key":"side-0-0-205","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":205,"trim":{"chart":0,"holes":[],"outer":205,"uv_bounds":[{"hi":21.450000010000004,"lo":20.84999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":206,"key":"side-0-2-206","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":206,"trim":{"chart":0,"holes":[],"outer":206,"uv_bounds":[{"hi":39.15000001,"lo":38.549999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":207,"key":"top-207","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":207,"trim":{"chart":0,"holes":[],"outer":207,"uv_bounds":[{"hi":-8.549999989999996,"lo":-9.15000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":208,"key":"side-0-0-208","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":208,"trim":{"chart":0,"holes":[],"outer":208,"uv_bounds":[{"hi":21.450000010000004,"lo":20.84999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":209,"key":"side-0-2-209","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":209,"trim":{"chart":0,"holes":[],"outer":209,"uv_bounds":[{"hi":39.15000001,"lo":38.549999989999996},{"hi":2.00000001,"lo":-1e-8}]}},{"id":210,"key":"cut-16:right-210","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-16","entity":"cut-16","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":210,"trim":{"chart":0,"holes":[],"outer":210,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":211,"key":"cut-17:left-211","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-17","entity":"cut-17","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":211,"trim":{"chart":0,"holes":[],"outer":211,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":212,"key":"bottom-212","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":212,"trim":{"chart":0,"holes":[],"outer":212,"uv_bounds":[{"hi":-8.549999989999996,"lo":-9.15000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":213,"key":"side-0-0-213","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":213,"trim":{"chart":0,"holes":[],"outer":213,"uv_bounds":[{"hi":21.950000010000004,"lo":21.449999990000002},{"hi":3.00000001,"lo":1.99999999}]}},{"id":214,"key":"side-0-2-214","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":214,"trim":{"chart":0,"holes":[],"outer":214,"uv_bounds":[{"hi":38.55000001,"lo":38.049999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":215,"key":"top-215","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":215,"trim":{"chart":0,"holes":[],"outer":215,"uv_bounds":[{"hi":-8.049999989999996,"lo":-8.550000009999998},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":216,"key":"cut-17:top-216","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-17","entity":"cut-17","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":216,"trim":{"chart":0,"holes":[],"outer":216,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":217,"key":"side-0-0-217","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":217,"trim":{"chart":0,"holes":[],"outer":217,"uv_bounds":[{"hi":22.55000001,"lo":21.949999990000002},{"hi":3.00000001,"lo":1.99999999}]}},{"id":218,"key":"side-0-2-218","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":218,"trim":{"chart":0,"holes":[],"outer":218,"uv_bounds":[{"hi":38.05000001,"lo":37.44999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":219,"key":"top-219","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":219,"trim":{"chart":0,"holes":[],"outer":219,"uv_bounds":[{"hi":-7.449999989999999,"lo":-8.050000009999998},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":220,"key":"side-0-0-220","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":220,"trim":{"chart":0,"holes":[],"outer":220,"uv_bounds":[{"hi":22.55000001,"lo":21.949999990000002},{"hi":2.00000001,"lo":-1e-8}]}},{"id":221,"key":"side-0-2-221","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":221,"trim":{"chart":0,"holes":[],"outer":221,"uv_bounds":[{"hi":38.05000001,"lo":37.44999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":222,"key":"cut-17:right-222","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-17","entity":"cut-17","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":222,"trim":{"chart":0,"holes":[],"outer":222,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":223,"key":"cut-18:left-223","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-18","entity":"cut-18","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":223,"trim":{"chart":0,"holes":[],"outer":223,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":224,"key":"bottom-224","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":224,"trim":{"chart":0,"holes":[],"outer":224,"uv_bounds":[{"hi":-7.449999989999999,"lo":-8.050000009999998},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":225,"key":"side-0-0-225","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":225,"trim":{"chart":0,"holes":[],"outer":225,"uv_bounds":[{"hi":23.05000001,"lo":22.54999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":226,"key":"side-0-2-226","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":226,"trim":{"chart":0,"holes":[],"outer":226,"uv_bounds":[{"hi":37.450000010000004,"lo":36.94999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":227,"key":"top-227","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":227,"trim":{"chart":0,"holes":[],"outer":227,"uv_bounds":[{"hi":-6.949999989999999,"lo":-7.450000009999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":228,"key":"cut-18:top-228","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-18","entity":"cut-18","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":228,"trim":{"chart":0,"holes":[],"outer":228,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":229,"key":"side-0-0-229","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":229,"trim":{"chart":0,"holes":[],"outer":229,"uv_bounds":[{"hi":23.650000010000003,"lo":23.04999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":230,"key":"side-0-2-230","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":230,"trim":{"chart":0,"holes":[],"outer":230,"uv_bounds":[{"hi":36.950000010000004,"lo":36.34999998999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":231,"key":"top-231","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":231,"trim":{"chart":0,"holes":[],"outer":231,"uv_bounds":[{"hi":-6.349999989999998,"lo":-6.950000009999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":232,"key":"side-0-0-232","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":232,"trim":{"chart":0,"holes":[],"outer":232,"uv_bounds":[{"hi":23.650000010000003,"lo":23.04999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":233,"key":"side-0-2-233","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":233,"trim":{"chart":0,"holes":[],"outer":233,"uv_bounds":[{"hi":36.950000010000004,"lo":36.34999998999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":234,"key":"cut-18:right-234","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-18","entity":"cut-18","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":234,"trim":{"chart":0,"holes":[],"outer":234,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":235,"key":"cut-19:left-235","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-19","entity":"cut-19","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":235,"trim":{"chart":0,"holes":[],"outer":235,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":236,"key":"bottom-236","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":236,"trim":{"chart":0,"holes":[],"outer":236,"uv_bounds":[{"hi":-6.349999989999998,"lo":-6.950000009999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":237,"key":"side-0-0-237","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":237,"trim":{"chart":0,"holes":[],"outer":237,"uv_bounds":[{"hi":24.150000010000003,"lo":23.64999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":238,"key":"side-0-2-238","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":238,"trim":{"chart":0,"holes":[],"outer":238,"uv_bounds":[{"hi":36.350000009999995,"lo":35.84999998999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":239,"key":"top-239","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":239,"trim":{"chart":0,"holes":[],"outer":239,"uv_bounds":[{"hi":-5.849999989999998,"lo":-6.350000009999998},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":240,"key":"cut-19:top-240","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-19","entity":"cut-19","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":240,"trim":{"chart":0,"holes":[],"outer":240,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":241,"key":"side-0-0-241","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":241,"trim":{"chart":0,"holes":[],"outer":241,"uv_bounds":[{"hi":24.75000001,"lo":24.14999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":242,"key":"side-0-2-242","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":242,"trim":{"chart":0,"holes":[],"outer":242,"uv_bounds":[{"hi":35.850000009999995,"lo":35.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":243,"key":"top-243","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":243,"trim":{"chart":0,"holes":[],"outer":243,"uv_bounds":[{"hi":-5.24999999,"lo":-5.850000009999998},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":244,"key":"side-0-0-244","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":244,"trim":{"chart":0,"holes":[],"outer":244,"uv_bounds":[{"hi":24.75000001,"lo":24.14999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":245,"key":"side-0-2-245","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":245,"trim":{"chart":0,"holes":[],"outer":245,"uv_bounds":[{"hi":35.850000009999995,"lo":35.24999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":246,"key":"cut-19:right-246","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-19","entity":"cut-19","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":246,"trim":{"chart":0,"holes":[],"outer":246,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":247,"key":"cut-20:left-247","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-20","entity":"cut-20","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":247,"trim":{"chart":0,"holes":[],"outer":247,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":248,"key":"bottom-248","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":248,"trim":{"chart":0,"holes":[],"outer":248,"uv_bounds":[{"hi":-5.24999999,"lo":-5.850000009999998},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":249,"key":"side-0-0-249","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":249,"trim":{"chart":0,"holes":[],"outer":249,"uv_bounds":[{"hi":25.25000001,"lo":24.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":250,"key":"side-0-2-250","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":250,"trim":{"chart":0,"holes":[],"outer":250,"uv_bounds":[{"hi":35.25000001,"lo":34.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":251,"key":"top-251","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":251,"trim":{"chart":0,"holes":[],"outer":251,"uv_bounds":[{"hi":-4.74999999,"lo":-5.25000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":252,"key":"cut-20:top-252","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-20","entity":"cut-20","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":252,"trim":{"chart":0,"holes":[],"outer":252,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":253,"key":"side-0-0-253","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":253,"trim":{"chart":0,"holes":[],"outer":253,"uv_bounds":[{"hi":25.850000010000002,"lo":25.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":254,"key":"side-0-2-254","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":254,"trim":{"chart":0,"holes":[],"outer":254,"uv_bounds":[{"hi":34.75000001,"lo":34.14999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":255,"key":"top-255","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":255,"trim":{"chart":0,"holes":[],"outer":255,"uv_bounds":[{"hi":-4.149999989999999,"lo":-4.75000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":256,"key":"side-0-0-256","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":256,"trim":{"chart":0,"holes":[],"outer":256,"uv_bounds":[{"hi":25.850000010000002,"lo":25.24999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":257,"key":"side-0-2-257","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":257,"trim":{"chart":0,"holes":[],"outer":257,"uv_bounds":[{"hi":34.75000001,"lo":34.14999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":258,"key":"cut-20:right-258","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-20","entity":"cut-20","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":258,"trim":{"chart":0,"holes":[],"outer":258,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":259,"key":"cut-21:left-259","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-21","entity":"cut-21","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":259,"trim":{"chart":0,"holes":[],"outer":259,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":260,"key":"bottom-260","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":260,"trim":{"chart":0,"holes":[],"outer":260,"uv_bounds":[{"hi":-4.149999989999999,"lo":-4.75000001},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":261,"key":"side-0-0-261","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":261,"trim":{"chart":0,"holes":[],"outer":261,"uv_bounds":[{"hi":26.350000010000002,"lo":25.84999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":262,"key":"side-0-2-262","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":262,"trim":{"chart":0,"holes":[],"outer":262,"uv_bounds":[{"hi":34.15000001,"lo":33.64999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":263,"key":"top-263","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":263,"trim":{"chart":0,"holes":[],"outer":263,"uv_bounds":[{"hi":-3.6499999899999986,"lo":-4.1500000099999985},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":264,"key":"cut-21:top-264","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-21","entity":"cut-21","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":264,"trim":{"chart":0,"holes":[],"outer":264,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":265,"key":"side-0-0-265","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":265,"trim":{"chart":0,"holes":[],"outer":265,"uv_bounds":[{"hi":26.950000010000004,"lo":26.34999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":266,"key":"side-0-2-266","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":266,"trim":{"chart":0,"holes":[],"outer":266,"uv_bounds":[{"hi":33.65000001,"lo":33.049999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":267,"key":"top-267","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":267,"trim":{"chart":0,"holes":[],"outer":267,"uv_bounds":[{"hi":-3.049999989999997,"lo":-3.6500000099999985},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":268,"key":"side-0-0-268","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":268,"trim":{"chart":0,"holes":[],"outer":268,"uv_bounds":[{"hi":26.950000010000004,"lo":26.34999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":269,"key":"side-0-2-269","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":269,"trim":{"chart":0,"holes":[],"outer":269,"uv_bounds":[{"hi":33.65000001,"lo":33.049999989999996},{"hi":2.00000001,"lo":-1e-8}]}},{"id":270,"key":"cut-21:right-270","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-21","entity":"cut-21","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":270,"trim":{"chart":0,"holes":[],"outer":270,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":271,"key":"cut-22:left-271","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-22","entity":"cut-22","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":271,"trim":{"chart":0,"holes":[],"outer":271,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":272,"key":"bottom-272","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":272,"trim":{"chart":0,"holes":[],"outer":272,"uv_bounds":[{"hi":-3.049999989999997,"lo":-3.6500000099999985},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":273,"key":"side-0-0-273","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":273,"trim":{"chart":0,"holes":[],"outer":273,"uv_bounds":[{"hi":27.450000010000004,"lo":26.949999990000002},{"hi":3.00000001,"lo":1.99999999}]}},{"id":274,"key":"side-0-2-274","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":274,"trim":{"chart":0,"holes":[],"outer":274,"uv_bounds":[{"hi":33.05000001,"lo":32.549999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":275,"key":"top-275","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":275,"trim":{"chart":0,"holes":[],"outer":275,"uv_bounds":[{"hi":-2.549999989999997,"lo":-3.050000009999997},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":276,"key":"cut-22:top-276","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-22","entity":"cut-22","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":276,"trim":{"chart":0,"holes":[],"outer":276,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":277,"key":"side-0-0-277","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":277,"trim":{"chart":0,"holes":[],"outer":277,"uv_bounds":[{"hi":28.05000001,"lo":27.449999990000002},{"hi":3.00000001,"lo":1.99999999}]}},{"id":278,"key":"side-0-2-278","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":278,"trim":{"chart":0,"holes":[],"outer":278,"uv_bounds":[{"hi":32.55000001,"lo":31.94999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":279,"key":"top-279","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":279,"trim":{"chart":0,"holes":[],"outer":279,"uv_bounds":[{"hi":-1.9499999899999994,"lo":-2.550000009999997},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":280,"key":"side-0-0-280","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":280,"trim":{"chart":0,"holes":[],"outer":280,"uv_bounds":[{"hi":28.05000001,"lo":27.449999990000002},{"hi":2.00000001,"lo":-1e-8}]}},{"id":281,"key":"side-0-2-281","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":281,"trim":{"chart":0,"holes":[],"outer":281,"uv_bounds":[{"hi":32.55000001,"lo":31.94999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":282,"key":"cut-22:right-282","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-22","entity":"cut-22","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":282,"trim":{"chart":0,"holes":[],"outer":282,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":283,"key":"cut-23:left-283","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-23","entity":"cut-23","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":283,"trim":{"chart":0,"holes":[],"outer":283,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":284,"key":"bottom-284","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":284,"trim":{"chart":0,"holes":[],"outer":284,"uv_bounds":[{"hi":-1.9499999899999994,"lo":-2.550000009999997},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":285,"key":"side-0-0-285","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":285,"trim":{"chart":0,"holes":[],"outer":285,"uv_bounds":[{"hi":28.55000001,"lo":28.04999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":286,"key":"side-0-2-286","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":286,"trim":{"chart":0,"holes":[],"outer":286,"uv_bounds":[{"hi":31.95000001,"lo":31.44999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":287,"key":"top-287","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":287,"trim":{"chart":0,"holes":[],"outer":287,"uv_bounds":[{"hi":-1.4499999899999994,"lo":-1.9500000099999992},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":288,"key":"cut-23:top-288","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-23","entity":"cut-23","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":288,"trim":{"chart":0,"holes":[],"outer":288,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":289,"key":"side-0-0-289","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":289,"trim":{"chart":0,"holes":[],"outer":289,"uv_bounds":[{"hi":29.150000010000003,"lo":28.54999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":290,"key":"side-0-2-290","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":290,"trim":{"chart":0,"holes":[],"outer":290,"uv_bounds":[{"hi":31.45000001,"lo":30.849999989999997},{"hi":3.00000001,"lo":1.99999999}]}},{"id":291,"key":"top-291","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":291,"trim":{"chart":0,"holes":[],"outer":291,"uv_bounds":[{"hi":-0.8499999899999978,"lo":-1.4500000099999992},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":292,"key":"side-0-0-292","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":292,"trim":{"chart":0,"holes":[],"outer":292,"uv_bounds":[{"hi":29.150000010000003,"lo":28.54999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":293,"key":"side-0-2-293","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":293,"trim":{"chart":0,"holes":[],"outer":293,"uv_bounds":[{"hi":31.45000001,"lo":30.849999989999997},{"hi":2.00000001,"lo":-1e-8}]}},{"id":294,"key":"cut-23:right-294","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-23","entity":"cut-23","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":294,"trim":{"chart":0,"holes":[],"outer":294,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":295,"key":"cut-24:left-295","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-24","entity":"cut-24","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":295,"trim":{"chart":0,"holes":[],"outer":295,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":296,"key":"bottom-296","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":296,"trim":{"chart":0,"holes":[],"outer":296,"uv_bounds":[{"hi":-0.8499999899999978,"lo":-1.4500000099999992},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":297,"key":"side-0-0-297","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":297,"trim":{"chart":0,"holes":[],"outer":297,"uv_bounds":[{"hi":29.650000010000003,"lo":29.14999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":298,"key":"side-0-2-298","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":298,"trim":{"chart":0,"holes":[],"outer":298,"uv_bounds":[{"hi":30.85000001,"lo":30.349999989999997},{"hi":3.00000001,"lo":1.99999999}]}},{"id":299,"key":"top-299","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":299,"trim":{"chart":0,"holes":[],"outer":299,"uv_bounds":[{"hi":-0.3499999899999979,"lo":-0.8500000099999979},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":300,"key":"cut-24:top-300","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-24","entity":"cut-24","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":300,"trim":{"chart":0,"holes":[],"outer":300,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":301,"key":"side-0-0-301","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":301,"trim":{"chart":0,"holes":[],"outer":301,"uv_bounds":[{"hi":30.250000010000004,"lo":29.64999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":302,"key":"side-0-2-302","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":302,"trim":{"chart":0,"holes":[],"outer":302,"uv_bounds":[{"hi":30.35000001,"lo":29.749999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":303,"key":"top-303","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":303,"trim":{"chart":0,"holes":[],"outer":303,"uv_bounds":[{"hi":0.25000001000000355,"lo":-0.35000000999999786},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":304,"key":"side-0-0-304","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":304,"trim":{"chart":0,"holes":[],"outer":304,"uv_bounds":[{"hi":30.250000010000004,"lo":29.64999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":305,"key":"side-0-2-305","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":305,"trim":{"chart":0,"holes":[],"outer":305,"uv_bounds":[{"hi":30.35000001,"lo":29.749999989999996},{"hi":2.00000001,"lo":-1e-8}]}},{"id":306,"key":"cut-24:right-306","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-24","entity":"cut-24","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":306,"trim":{"chart":0,"holes":[],"outer":306,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":307,"key":"cut-25:left-307","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-25","entity":"cut-25","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":307,"trim":{"chart":0,"holes":[],"outer":307,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":308,"key":"bottom-308","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":308,"trim":{"chart":0,"holes":[],"outer":308,"uv_bounds":[{"hi":0.25000001000000355,"lo":-0.35000000999999786},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":309,"key":"side-0-0-309","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":309,"trim":{"chart":0,"holes":[],"outer":309,"uv_bounds":[{"hi":30.750000010000004,"lo":30.249999990000003},{"hi":3.00000001,"lo":1.99999999}]}},{"id":310,"key":"side-0-2-310","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":310,"trim":{"chart":0,"holes":[],"outer":310,"uv_bounds":[{"hi":29.750000009999997,"lo":29.249999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":311,"key":"top-311","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":311,"trim":{"chart":0,"holes":[],"outer":311,"uv_bounds":[{"hi":0.7500000100000036,"lo":0.24999999000000356},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":312,"key":"cut-25:top-312","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-25","entity":"cut-25","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":312,"trim":{"chart":0,"holes":[],"outer":312,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":313,"key":"side-0-0-313","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":313,"trim":{"chart":0,"holes":[],"outer":313,"uv_bounds":[{"hi":31.350000010000002,"lo":30.749999990000003},{"hi":3.00000001,"lo":1.99999999}]}},{"id":314,"key":"side-0-2-314","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":314,"trim":{"chart":0,"holes":[],"outer":314,"uv_bounds":[{"hi":29.250000009999997,"lo":28.649999989999998},{"hi":3.00000001,"lo":1.99999999}]}},{"id":315,"key":"top-315","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":315,"trim":{"chart":0,"holes":[],"outer":315,"uv_bounds":[{"hi":1.3500000100000014,"lo":0.7499999900000035},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":316,"key":"side-0-0-316","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":316,"trim":{"chart":0,"holes":[],"outer":316,"uv_bounds":[{"hi":31.350000010000002,"lo":30.749999990000003},{"hi":2.00000001,"lo":-1e-8}]}},{"id":317,"key":"side-0-2-317","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":317,"trim":{"chart":0,"holes":[],"outer":317,"uv_bounds":[{"hi":29.250000009999997,"lo":28.649999989999998},{"hi":2.00000001,"lo":-1e-8}]}},{"id":318,"key":"cut-25:right-318","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-25","entity":"cut-25","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":318,"trim":{"chart":0,"holes":[],"outer":318,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":319,"key":"cut-26:left-319","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-26","entity":"cut-26","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":319,"trim":{"chart":0,"holes":[],"outer":319,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":320,"key":"bottom-320","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":320,"trim":{"chart":0,"holes":[],"outer":320,"uv_bounds":[{"hi":1.3500000100000014,"lo":0.7499999900000035},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":321,"key":"side-0-0-321","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":321,"trim":{"chart":0,"holes":[],"outer":321,"uv_bounds":[{"hi":31.850000010000002,"lo":31.34999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":322,"key":"side-0-2-322","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":322,"trim":{"chart":0,"holes":[],"outer":322,"uv_bounds":[{"hi":28.65000001,"lo":28.149999989999998},{"hi":3.00000001,"lo":1.99999999}]}},{"id":323,"key":"top-323","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":323,"trim":{"chart":0,"holes":[],"outer":323,"uv_bounds":[{"hi":1.8500000100000014,"lo":1.3499999900000015},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":324,"key":"cut-26:top-324","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-26","entity":"cut-26","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":324,"trim":{"chart":0,"holes":[],"outer":324,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":325,"key":"side-0-0-325","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":325,"trim":{"chart":0,"holes":[],"outer":325,"uv_bounds":[{"hi":32.450000010000004,"lo":31.84999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":326,"key":"side-0-2-326","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":326,"trim":{"chart":0,"holes":[],"outer":326,"uv_bounds":[{"hi":28.15000001,"lo":27.549999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":327,"key":"top-327","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":327,"trim":{"chart":0,"holes":[],"outer":327,"uv_bounds":[{"hi":2.450000010000003,"lo":1.8499999900000015},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":328,"key":"side-0-0-328","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":328,"trim":{"chart":0,"holes":[],"outer":328,"uv_bounds":[{"hi":32.450000010000004,"lo":31.84999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":329,"key":"side-0-2-329","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":329,"trim":{"chart":0,"holes":[],"outer":329,"uv_bounds":[{"hi":28.15000001,"lo":27.549999989999996},{"hi":2.00000001,"lo":-1e-8}]}},{"id":330,"key":"cut-26:right-330","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-26","entity":"cut-26","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":330,"trim":{"chart":0,"holes":[],"outer":330,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":331,"key":"cut-27:left-331","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-27","entity":"cut-27","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":331,"trim":{"chart":0,"holes":[],"outer":331,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":332,"key":"bottom-332","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":332,"trim":{"chart":0,"holes":[],"outer":332,"uv_bounds":[{"hi":2.450000010000003,"lo":1.8499999900000015},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":333,"key":"side-0-0-333","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":333,"trim":{"chart":0,"holes":[],"outer":333,"uv_bounds":[{"hi":32.950000010000004,"lo":32.44999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":334,"key":"side-0-2-334","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":334,"trim":{"chart":0,"holes":[],"outer":334,"uv_bounds":[{"hi":27.550000009999998,"lo":27.049999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":335,"key":"top-335","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":335,"trim":{"chart":0,"holes":[],"outer":335,"uv_bounds":[{"hi":2.950000010000003,"lo":2.449999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":336,"key":"cut-27:top-336","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-27","entity":"cut-27","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":336,"trim":{"chart":0,"holes":[],"outer":336,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":337,"key":"side-0-0-337","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":337,"trim":{"chart":0,"holes":[],"outer":337,"uv_bounds":[{"hi":33.550000010000005,"lo":32.94999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":338,"key":"side-0-2-338","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":338,"trim":{"chart":0,"holes":[],"outer":338,"uv_bounds":[{"hi":27.050000009999998,"lo":26.449999989999995},{"hi":3.00000001,"lo":1.99999999}]}},{"id":339,"key":"top-339","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":339,"trim":{"chart":0,"holes":[],"outer":339,"uv_bounds":[{"hi":3.550000010000004,"lo":2.949999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":340,"key":"side-0-0-340","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":340,"trim":{"chart":0,"holes":[],"outer":340,"uv_bounds":[{"hi":33.550000010000005,"lo":32.94999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":341,"key":"side-0-2-341","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":341,"trim":{"chart":0,"holes":[],"outer":341,"uv_bounds":[{"hi":27.050000009999998,"lo":26.449999989999995},{"hi":2.00000001,"lo":-1e-8}]}},{"id":342,"key":"cut-27:right-342","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-27","entity":"cut-27","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":342,"trim":{"chart":0,"holes":[],"outer":342,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":343,"key":"cut-28:left-343","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-28","entity":"cut-28","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":343,"trim":{"chart":0,"holes":[],"outer":343,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":344,"key":"bottom-344","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":344,"trim":{"chart":0,"holes":[],"outer":344,"uv_bounds":[{"hi":3.550000010000004,"lo":2.949999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":345,"key":"side-0-0-345","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":345,"trim":{"chart":0,"holes":[],"outer":345,"uv_bounds":[{"hi":34.050000010000005,"lo":33.54999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":346,"key":"side-0-2-346","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":346,"trim":{"chart":0,"holes":[],"outer":346,"uv_bounds":[{"hi":26.450000009999997,"lo":25.949999989999995},{"hi":3.00000001,"lo":1.99999999}]}},{"id":347,"key":"top-347","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":347,"trim":{"chart":0,"holes":[],"outer":347,"uv_bounds":[{"hi":4.050000010000004,"lo":3.5499999900000043},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":348,"key":"cut-28:top-348","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-28","entity":"cut-28","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":348,"trim":{"chart":0,"holes":[],"outer":348,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":349,"key":"side-0-0-349","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":349,"trim":{"chart":0,"holes":[],"outer":349,"uv_bounds":[{"hi":34.65000001000001,"lo":34.04999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":350,"key":"side-0-2-350","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":350,"trim":{"chart":0,"holes":[],"outer":350,"uv_bounds":[{"hi":25.950000009999997,"lo":25.349999989999993},{"hi":3.00000001,"lo":1.99999999}]}},{"id":351,"key":"top-351","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":351,"trim":{"chart":0,"holes":[],"outer":351,"uv_bounds":[{"hi":4.650000010000006,"lo":4.049999990000004},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":352,"key":"side-0-0-352","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":352,"trim":{"chart":0,"holes":[],"outer":352,"uv_bounds":[{"hi":34.65000001000001,"lo":34.04999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":353,"key":"side-0-2-353","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":353,"trim":{"chart":0,"holes":[],"outer":353,"uv_bounds":[{"hi":25.950000009999997,"lo":25.349999989999993},{"hi":2.00000001,"lo":-1e-8}]}},{"id":354,"key":"cut-28:right-354","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-28","entity":"cut-28","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":354,"trim":{"chart":0,"holes":[],"outer":354,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":355,"key":"cut-29:left-355","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-29","entity":"cut-29","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":355,"trim":{"chart":0,"holes":[],"outer":355,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":356,"key":"bottom-356","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":356,"trim":{"chart":0,"holes":[],"outer":356,"uv_bounds":[{"hi":4.650000010000006,"lo":4.049999990000004},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":357,"key":"side-0-0-357","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":357,"trim":{"chart":0,"holes":[],"outer":357,"uv_bounds":[{"hi":35.15000001000001,"lo":34.649999990000005},{"hi":3.00000001,"lo":1.99999999}]}},{"id":358,"key":"side-0-2-358","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":358,"trim":{"chart":0,"holes":[],"outer":358,"uv_bounds":[{"hi":25.350000009999995,"lo":24.849999989999993},{"hi":3.00000001,"lo":1.99999999}]}},{"id":359,"key":"top-359","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":359,"trim":{"chart":0,"holes":[],"outer":359,"uv_bounds":[{"hi":5.150000010000006,"lo":4.649999990000006},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":360,"key":"cut-29:top-360","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-29","entity":"cut-29","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":360,"trim":{"chart":0,"holes":[],"outer":360,"uv_bounds":[{"hi":0.5000000100000036,"lo":-9.999996447286321e-9},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":361,"key":"side-0-0-361","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":361,"trim":{"chart":0,"holes":[],"outer":361,"uv_bounds":[{"hi":35.75000001,"lo":35.149999990000005},{"hi":3.00000001,"lo":1.99999999}]}},{"id":362,"key":"side-0-2-362","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":362,"trim":{"chart":0,"holes":[],"outer":362,"uv_bounds":[{"hi":24.850000009999995,"lo":24.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":363,"key":"top-363","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":363,"trim":{"chart":0,"holes":[],"outer":363,"uv_bounds":[{"hi":5.75000001,"lo":5.149999990000006},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":364,"key":"side-0-0-364","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":364,"trim":{"chart":0,"holes":[],"outer":364,"uv_bounds":[{"hi":35.75000001,"lo":35.149999990000005},{"hi":2.00000001,"lo":-1e-8}]}},{"id":365,"key":"side-0-2-365","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":365,"trim":{"chart":0,"holes":[],"outer":365,"uv_bounds":[{"hi":24.850000009999995,"lo":24.24999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":366,"key":"cut-29:right-366","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-29","entity":"cut-29","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":366,"trim":{"chart":0,"holes":[],"outer":366,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":367,"key":"cut-30:left-367","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-30","entity":"cut-30","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":367,"trim":{"chart":0,"holes":[],"outer":367,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":368,"key":"bottom-368","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":368,"trim":{"chart":0,"holes":[],"outer":368,"uv_bounds":[{"hi":5.75000001,"lo":5.149999990000006},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":369,"key":"side-0-0-369","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":369,"trim":{"chart":0,"holes":[],"outer":369,"uv_bounds":[{"hi":36.25000001,"lo":35.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":370,"key":"side-0-2-370","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":370,"trim":{"chart":0,"holes":[],"outer":370,"uv_bounds":[{"hi":24.25000001,"lo":23.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":371,"key":"top-371","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":371,"trim":{"chart":0,"holes":[],"outer":371,"uv_bounds":[{"hi":6.25000001,"lo":5.74999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":372,"key":"cut-30:top-372","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-30","entity":"cut-30","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":372,"trim":{"chart":0,"holes":[],"outer":372,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":373,"key":"side-0-0-373","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":373,"trim":{"chart":0,"holes":[],"outer":373,"uv_bounds":[{"hi":36.85000001,"lo":36.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":374,"key":"side-0-2-374","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":374,"trim":{"chart":0,"holes":[],"outer":374,"uv_bounds":[{"hi":23.75000001,"lo":23.149999989999998},{"hi":3.00000001,"lo":1.99999999}]}},{"id":375,"key":"top-375","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":375,"trim":{"chart":0,"holes":[],"outer":375,"uv_bounds":[{"hi":6.850000010000001,"lo":6.24999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":376,"key":"side-0-0-376","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":376,"trim":{"chart":0,"holes":[],"outer":376,"uv_bounds":[{"hi":36.85000001,"lo":36.24999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":377,"key":"side-0-2-377","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":377,"trim":{"chart":0,"holes":[],"outer":377,"uv_bounds":[{"hi":23.75000001,"lo":23.149999989999998},{"hi":2.00000001,"lo":-1e-8}]}},{"id":378,"key":"cut-30:right-378","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-30","entity":"cut-30","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":378,"trim":{"chart":0,"holes":[],"outer":378,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":379,"key":"cut-31:left-379","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-31","entity":"cut-31","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":379,"trim":{"chart":0,"holes":[],"outer":379,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":380,"key":"bottom-380","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":380,"trim":{"chart":0,"holes":[],"outer":380,"uv_bounds":[{"hi":6.850000010000001,"lo":6.24999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":381,"key":"side-0-0-381","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":381,"trim":{"chart":0,"holes":[],"outer":381,"uv_bounds":[{"hi":37.35000001,"lo":36.84999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":382,"key":"side-0-2-382","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":382,"trim":{"chart":0,"holes":[],"outer":382,"uv_bounds":[{"hi":23.15000001,"lo":22.649999989999998},{"hi":3.00000001,"lo":1.99999999}]}},{"id":383,"key":"top-383","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":383,"trim":{"chart":0,"holes":[],"outer":383,"uv_bounds":[{"hi":7.350000010000001,"lo":6.8499999900000015},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":384,"key":"cut-31:top-384","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-31","entity":"cut-31","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":384,"trim":{"chart":0,"holes":[],"outer":384,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":385,"key":"side-0-0-385","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":385,"trim":{"chart":0,"holes":[],"outer":385,"uv_bounds":[{"hi":37.950000010000004,"lo":37.34999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":386,"key":"side-0-2-386","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":386,"trim":{"chart":0,"holes":[],"outer":386,"uv_bounds":[{"hi":22.65000001,"lo":22.049999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":387,"key":"top-387","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":387,"trim":{"chart":0,"holes":[],"outer":387,"uv_bounds":[{"hi":7.950000010000003,"lo":7.3499999900000015},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":388,"key":"side-0-0-388","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":388,"trim":{"chart":0,"holes":[],"outer":388,"uv_bounds":[{"hi":37.950000010000004,"lo":37.34999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":389,"key":"side-0-2-389","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":389,"trim":{"chart":0,"holes":[],"outer":389,"uv_bounds":[{"hi":22.65000001,"lo":22.049999989999996},{"hi":2.00000001,"lo":-1e-8}]}},{"id":390,"key":"cut-31:right-390","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-31","entity":"cut-31","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":390,"trim":{"chart":0,"holes":[],"outer":390,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":391,"key":"cut-32:left-391","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-32","entity":"cut-32","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":391,"trim":{"chart":0,"holes":[],"outer":391,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":392,"key":"bottom-392","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":392,"trim":{"chart":0,"holes":[],"outer":392,"uv_bounds":[{"hi":7.950000010000003,"lo":7.3499999900000015},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":393,"key":"side-0-0-393","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":393,"trim":{"chart":0,"holes":[],"outer":393,"uv_bounds":[{"hi":38.450000010000004,"lo":37.94999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":394,"key":"side-0-2-394","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":394,"trim":{"chart":0,"holes":[],"outer":394,"uv_bounds":[{"hi":22.050000009999998,"lo":21.549999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":395,"key":"top-395","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":395,"trim":{"chart":0,"holes":[],"outer":395,"uv_bounds":[{"hi":8.450000010000004,"lo":7.949999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":396,"key":"cut-32:top-396","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-32","entity":"cut-32","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":396,"trim":{"chart":0,"holes":[],"outer":396,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":397,"key":"side-0-0-397","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":397,"trim":{"chart":0,"holes":[],"outer":397,"uv_bounds":[{"hi":39.050000010000005,"lo":38.44999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":398,"key":"side-0-2-398","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":398,"trim":{"chart":0,"holes":[],"outer":398,"uv_bounds":[{"hi":21.550000009999998,"lo":20.949999989999995},{"hi":3.00000001,"lo":1.99999999}]}},{"id":399,"key":"top-399","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":399,"trim":{"chart":0,"holes":[],"outer":399,"uv_bounds":[{"hi":9.050000010000005,"lo":8.449999990000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":400,"key":"side-0-0-400","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":400,"trim":{"chart":0,"holes":[],"outer":400,"uv_bounds":[{"hi":39.050000010000005,"lo":38.44999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":401,"key":"side-0-2-401","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":401,"trim":{"chart":0,"holes":[],"outer":401,"uv_bounds":[{"hi":21.550000009999998,"lo":20.949999989999995},{"hi":2.00000001,"lo":-1e-8}]}},{"id":402,"key":"cut-32:right-402","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-32","entity":"cut-32","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":402,"trim":{"chart":0,"holes":[],"outer":402,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":403,"key":"cut-33:left-403","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-33","entity":"cut-33","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":403,"trim":{"chart":0,"holes":[],"outer":403,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":404,"key":"bottom-404","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":404,"trim":{"chart":0,"holes":[],"outer":404,"uv_bounds":[{"hi":9.050000010000005,"lo":8.449999990000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":405,"key":"side-0-0-405","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":405,"trim":{"chart":0,"holes":[],"outer":405,"uv_bounds":[{"hi":39.550000010000005,"lo":39.04999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":406,"key":"side-0-2-406","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":406,"trim":{"chart":0,"holes":[],"outer":406,"uv_bounds":[{"hi":20.950000009999997,"lo":20.449999989999995},{"hi":3.00000001,"lo":1.99999999}]}},{"id":407,"key":"top-407","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":407,"trim":{"chart":0,"holes":[],"outer":407,"uv_bounds":[{"hi":9.550000010000005,"lo":9.049999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":408,"key":"cut-33:top-408","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-33","entity":"cut-33","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":408,"trim":{"chart":0,"holes":[],"outer":408,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":409,"key":"side-0-0-409","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":409,"trim":{"chart":0,"holes":[],"outer":409,"uv_bounds":[{"hi":40.15000001000001,"lo":39.54999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":410,"key":"side-0-2-410","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":410,"trim":{"chart":0,"holes":[],"outer":410,"uv_bounds":[{"hi":20.450000009999997,"lo":19.849999989999993},{"hi":3.00000001,"lo":1.99999999}]}},{"id":411,"key":"top-411","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":411,"trim":{"chart":0,"holes":[],"outer":411,"uv_bounds":[{"hi":10.150000010000007,"lo":9.549999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":412,"key":"side-0-0-412","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":412,"trim":{"chart":0,"holes":[],"outer":412,"uv_bounds":[{"hi":40.15000001000001,"lo":39.54999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":413,"key":"side-0-2-413","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":413,"trim":{"chart":0,"holes":[],"outer":413,"uv_bounds":[{"hi":20.450000009999997,"lo":19.849999989999993},{"hi":2.00000001,"lo":-1e-8}]}},{"id":414,"key":"cut-33:right-414","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-33","entity":"cut-33","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":414,"trim":{"chart":0,"holes":[],"outer":414,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":415,"key":"cut-34:left-415","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-34","entity":"cut-34","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":415,"trim":{"chart":0,"holes":[],"outer":415,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":416,"key":"bottom-416","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":416,"trim":{"chart":0,"holes":[],"outer":416,"uv_bounds":[{"hi":10.150000010000007,"lo":9.549999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":417,"key":"side-0-0-417","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":417,"trim":{"chart":0,"holes":[],"outer":417,"uv_bounds":[{"hi":40.65000001000001,"lo":40.149999990000005},{"hi":3.00000001,"lo":1.99999999}]}},{"id":418,"key":"side-0-2-418","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":418,"trim":{"chart":0,"holes":[],"outer":418,"uv_bounds":[{"hi":19.850000009999995,"lo":19.349999989999993},{"hi":3.00000001,"lo":1.99999999}]}},{"id":419,"key":"top-419","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":419,"trim":{"chart":0,"holes":[],"outer":419,"uv_bounds":[{"hi":10.650000010000007,"lo":10.149999990000005},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":420,"key":"cut-34:top-420","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-34","entity":"cut-34","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":420,"trim":{"chart":0,"holes":[],"outer":420,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":421,"key":"side-0-0-421","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":421,"trim":{"chart":0,"holes":[],"outer":421,"uv_bounds":[{"hi":41.25000001,"lo":40.649999990000005},{"hi":3.00000001,"lo":1.99999999}]}},{"id":422,"key":"side-0-2-422","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":422,"trim":{"chart":0,"holes":[],"outer":422,"uv_bounds":[{"hi":19.350000009999995,"lo":18.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":423,"key":"top-423","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":423,"trim":{"chart":0,"holes":[],"outer":423,"uv_bounds":[{"hi":11.25000001,"lo":10.649999990000005},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":424,"key":"side-0-0-424","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":424,"trim":{"chart":0,"holes":[],"outer":424,"uv_bounds":[{"hi":41.25000001,"lo":40.649999990000005},{"hi":2.00000001,"lo":-1e-8}]}},{"id":425,"key":"side-0-2-425","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":425,"trim":{"chart":0,"holes":[],"outer":425,"uv_bounds":[{"hi":19.350000009999995,"lo":18.74999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":426,"key":"cut-34:right-426","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-34","entity":"cut-34","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":426,"trim":{"chart":0,"holes":[],"outer":426,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":427,"key":"cut-35:left-427","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-35","entity":"cut-35","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":427,"trim":{"chart":0,"holes":[],"outer":427,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":428,"key":"bottom-428","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":428,"trim":{"chart":0,"holes":[],"outer":428,"uv_bounds":[{"hi":11.25000001,"lo":10.649999990000005},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":429,"key":"side-0-0-429","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":429,"trim":{"chart":0,"holes":[],"outer":429,"uv_bounds":[{"hi":41.75000001,"lo":41.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":430,"key":"side-0-2-430","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":430,"trim":{"chart":0,"holes":[],"outer":430,"uv_bounds":[{"hi":18.75000001,"lo":18.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":431,"key":"top-431","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":431,"trim":{"chart":0,"holes":[],"outer":431,"uv_bounds":[{"hi":11.75000001,"lo":11.24999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":432,"key":"cut-35:top-432","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-35","entity":"cut-35","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":432,"trim":{"chart":0,"holes":[],"outer":432,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":433,"key":"side-0-0-433","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":433,"trim":{"chart":0,"holes":[],"outer":433,"uv_bounds":[{"hi":42.35000001,"lo":41.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":434,"key":"side-0-2-434","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":434,"trim":{"chart":0,"holes":[],"outer":434,"uv_bounds":[{"hi":18.25000001,"lo":17.649999989999998},{"hi":3.00000001,"lo":1.99999999}]}},{"id":435,"key":"top-435","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":435,"trim":{"chart":0,"holes":[],"outer":435,"uv_bounds":[{"hi":12.350000010000002,"lo":11.74999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":436,"key":"side-0-0-436","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":436,"trim":{"chart":0,"holes":[],"outer":436,"uv_bounds":[{"hi":42.35000001,"lo":41.74999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":437,"key":"side-0-2-437","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":437,"trim":{"chart":0,"holes":[],"outer":437,"uv_bounds":[{"hi":18.25000001,"lo":17.649999989999998},{"hi":2.00000001,"lo":-1e-8}]}},{"id":438,"key":"cut-35:right-438","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-35","entity":"cut-35","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":438,"trim":{"chart":0,"holes":[],"outer":438,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":439,"key":"cut-36:left-439","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-36","entity":"cut-36","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":439,"trim":{"chart":0,"holes":[],"outer":439,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":440,"key":"bottom-440","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":440,"trim":{"chart":0,"holes":[],"outer":440,"uv_bounds":[{"hi":12.350000010000002,"lo":11.74999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":441,"key":"side-0-0-441","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":441,"trim":{"chart":0,"holes":[],"outer":441,"uv_bounds":[{"hi":42.85000001,"lo":42.34999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":442,"key":"side-0-2-442","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":442,"trim":{"chart":0,"holes":[],"outer":442,"uv_bounds":[{"hi":17.65000001,"lo":17.149999989999998},{"hi":3.00000001,"lo":1.99999999}]}},{"id":443,"key":"top-443","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":443,"trim":{"chart":0,"holes":[],"outer":443,"uv_bounds":[{"hi":12.850000010000002,"lo":12.34999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":444,"key":"cut-36:top-444","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-36","entity":"cut-36","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":444,"trim":{"chart":0,"holes":[],"outer":444,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":445,"key":"side-0-0-445","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":445,"trim":{"chart":0,"holes":[],"outer":445,"uv_bounds":[{"hi":43.450000010000004,"lo":42.84999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":446,"key":"side-0-2-446","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":446,"trim":{"chart":0,"holes":[],"outer":446,"uv_bounds":[{"hi":17.15000001,"lo":16.549999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":447,"key":"top-447","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":447,"trim":{"chart":0,"holes":[],"outer":447,"uv_bounds":[{"hi":13.450000010000004,"lo":12.84999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":448,"key":"side-0-0-448","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":448,"trim":{"chart":0,"holes":[],"outer":448,"uv_bounds":[{"hi":43.450000010000004,"lo":42.84999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":449,"key":"side-0-2-449","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":449,"trim":{"chart":0,"holes":[],"outer":449,"uv_bounds":[{"hi":17.15000001,"lo":16.549999989999996},{"hi":2.00000001,"lo":-1e-8}]}},{"id":450,"key":"cut-36:right-450","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-36","entity":"cut-36","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":450,"trim":{"chart":0,"holes":[],"outer":450,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":451,"key":"cut-37:left-451","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-37","entity":"cut-37","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":451,"trim":{"chart":0,"holes":[],"outer":451,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":452,"key":"bottom-452","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":452,"trim":{"chart":0,"holes":[],"outer":452,"uv_bounds":[{"hi":13.450000010000004,"lo":12.84999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":453,"key":"side-0-0-453","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":453,"trim":{"chart":0,"holes":[],"outer":453,"uv_bounds":[{"hi":43.950000010000004,"lo":43.44999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":454,"key":"side-0-2-454","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":454,"trim":{"chart":0,"holes":[],"outer":454,"uv_bounds":[{"hi":16.550000009999998,"lo":16.049999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":455,"key":"top-455","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":455,"trim":{"chart":0,"holes":[],"outer":455,"uv_bounds":[{"hi":13.950000010000004,"lo":13.449999990000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":456,"key":"cut-37:top-456","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-37","entity":"cut-37","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":456,"trim":{"chart":0,"holes":[],"outer":456,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":457,"key":"side-0-0-457","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":457,"trim":{"chart":0,"holes":[],"outer":457,"uv_bounds":[{"hi":44.550000010000005,"lo":43.94999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":458,"key":"side-0-2-458","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":458,"trim":{"chart":0,"holes":[],"outer":458,"uv_bounds":[{"hi":16.050000009999998,"lo":15.449999989999995},{"hi":3.00000001,"lo":1.99999999}]}},{"id":459,"key":"top-459","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":459,"trim":{"chart":0,"holes":[],"outer":459,"uv_bounds":[{"hi":14.550000010000005,"lo":13.949999990000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":460,"key":"side-0-0-460","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":460,"trim":{"chart":0,"holes":[],"outer":460,"uv_bounds":[{"hi":44.550000010000005,"lo":43.94999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":461,"key":"side-0-2-461","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":461,"trim":{"chart":0,"holes":[],"outer":461,"uv_bounds":[{"hi":16.050000009999998,"lo":15.449999989999995},{"hi":2.00000001,"lo":-1e-8}]}},{"id":462,"key":"cut-37:right-462","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-37","entity":"cut-37","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":462,"trim":{"chart":0,"holes":[],"outer":462,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":463,"key":"cut-38:left-463","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-38","entity":"cut-38","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":463,"trim":{"chart":0,"holes":[],"outer":463,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":464,"key":"bottom-464","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":464,"trim":{"chart":0,"holes":[],"outer":464,"uv_bounds":[{"hi":14.550000010000005,"lo":13.949999990000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":465,"key":"side-0-0-465","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":465,"trim":{"chart":0,"holes":[],"outer":465,"uv_bounds":[{"hi":45.050000010000005,"lo":44.54999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":466,"key":"side-0-2-466","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":466,"trim":{"chart":0,"holes":[],"outer":466,"uv_bounds":[{"hi":15.450000009999997,"lo":14.949999989999995},{"hi":3.00000001,"lo":1.99999999}]}},{"id":467,"key":"top-467","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":467,"trim":{"chart":0,"holes":[],"outer":467,"uv_bounds":[{"hi":15.050000010000005,"lo":14.549999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":468,"key":"cut-38:top-468","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-38","entity":"cut-38","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":468,"trim":{"chart":0,"holes":[],"outer":468,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":469,"key":"side-0-0-469","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":469,"trim":{"chart":0,"holes":[],"outer":469,"uv_bounds":[{"hi":45.65000001000001,"lo":45.04999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":470,"key":"side-0-2-470","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":470,"trim":{"chart":0,"holes":[],"outer":470,"uv_bounds":[{"hi":14.950000009999997,"lo":14.349999989999993},{"hi":3.00000001,"lo":1.99999999}]}},{"id":471,"key":"top-471","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":471,"trim":{"chart":0,"holes":[],"outer":471,"uv_bounds":[{"hi":15.650000010000007,"lo":15.049999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":472,"key":"side-0-0-472","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":472,"trim":{"chart":0,"holes":[],"outer":472,"uv_bounds":[{"hi":45.65000001000001,"lo":45.04999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":473,"key":"side-0-2-473","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":473,"trim":{"chart":0,"holes":[],"outer":473,"uv_bounds":[{"hi":14.950000009999997,"lo":14.349999989999993},{"hi":2.00000001,"lo":-1e-8}]}},{"id":474,"key":"cut-38:right-474","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-38","entity":"cut-38","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":474,"trim":{"chart":0,"holes":[],"outer":474,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":475,"key":"cut-39:left-475","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-39","entity":"cut-39","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":475,"trim":{"chart":0,"holes":[],"outer":475,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":476,"key":"bottom-476","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":476,"trim":{"chart":0,"holes":[],"outer":476,"uv_bounds":[{"hi":15.650000010000007,"lo":15.049999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":477,"key":"side-0-0-477","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":477,"trim":{"chart":0,"holes":[],"outer":477,"uv_bounds":[{"hi":46.15000001000001,"lo":45.649999990000005},{"hi":3.00000001,"lo":1.99999999}]}},{"id":478,"key":"side-0-2-478","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":478,"trim":{"chart":0,"holes":[],"outer":478,"uv_bounds":[{"hi":14.350000009999995,"lo":13.849999989999993},{"hi":3.00000001,"lo":1.99999999}]}},{"id":479,"key":"top-479","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":479,"trim":{"chart":0,"holes":[],"outer":479,"uv_bounds":[{"hi":16.150000010000007,"lo":15.649999990000005},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":480,"key":"cut-39:top-480","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-39","entity":"cut-39","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":480,"trim":{"chart":0,"holes":[],"outer":480,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":481,"key":"side-0-0-481","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":481,"trim":{"chart":0,"holes":[],"outer":481,"uv_bounds":[{"hi":46.75000001,"lo":46.149999990000005},{"hi":3.00000001,"lo":1.99999999}]}},{"id":482,"key":"side-0-2-482","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":482,"trim":{"chart":0,"holes":[],"outer":482,"uv_bounds":[{"hi":13.850000009999995,"lo":13.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":483,"key":"top-483","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":483,"trim":{"chart":0,"holes":[],"outer":483,"uv_bounds":[{"hi":16.75000001,"lo":16.149999990000005},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":484,"key":"side-0-0-484","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":484,"trim":{"chart":0,"holes":[],"outer":484,"uv_bounds":[{"hi":46.75000001,"lo":46.149999990000005},{"hi":2.00000001,"lo":-1e-8}]}},{"id":485,"key":"side-0-2-485","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":485,"trim":{"chart":0,"holes":[],"outer":485,"uv_bounds":[{"hi":13.850000009999995,"lo":13.24999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":486,"key":"cut-39:right-486","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-39","entity":"cut-39","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":486,"trim":{"chart":0,"holes":[],"outer":486,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":487,"key":"cut-40:left-487","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-40","entity":"cut-40","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":487,"trim":{"chart":0,"holes":[],"outer":487,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":488,"key":"bottom-488","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":488,"trim":{"chart":0,"holes":[],"outer":488,"uv_bounds":[{"hi":16.75000001,"lo":16.149999990000005},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":489,"key":"side-0-0-489","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":489,"trim":{"chart":0,"holes":[],"outer":489,"uv_bounds":[{"hi":47.25000001,"lo":46.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":490,"key":"side-0-2-490","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":490,"trim":{"chart":0,"holes":[],"outer":490,"uv_bounds":[{"hi":13.25000001,"lo":12.74999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":491,"key":"top-491","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":491,"trim":{"chart":0,"holes":[],"outer":491,"uv_bounds":[{"hi":17.25000001,"lo":16.74999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":492,"key":"cut-40:top-492","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-40","entity":"cut-40","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":492,"trim":{"chart":0,"holes":[],"outer":492,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":493,"key":"side-0-0-493","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":493,"trim":{"chart":0,"holes":[],"outer":493,"uv_bounds":[{"hi":47.85000001,"lo":47.24999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":494,"key":"side-0-2-494","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":494,"trim":{"chart":0,"holes":[],"outer":494,"uv_bounds":[{"hi":12.75000001,"lo":12.149999989999998},{"hi":3.00000001,"lo":1.99999999}]}},{"id":495,"key":"top-495","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":495,"trim":{"chart":0,"holes":[],"outer":495,"uv_bounds":[{"hi":17.850000010000002,"lo":17.24999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":496,"key":"side-0-0-496","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":496,"trim":{"chart":0,"holes":[],"outer":496,"uv_bounds":[{"hi":47.85000001,"lo":47.24999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":497,"key":"side-0-2-497","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":497,"trim":{"chart":0,"holes":[],"outer":497,"uv_bounds":[{"hi":12.75000001,"lo":12.149999989999998},{"hi":2.00000001,"lo":-1e-8}]}},{"id":498,"key":"cut-40:right-498","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-40","entity":"cut-40","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":498,"trim":{"chart":0,"holes":[],"outer":498,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":499,"key":"cut-41:left-499","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-41","entity":"cut-41","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":499,"trim":{"chart":0,"holes":[],"outer":499,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":500,"key":"bottom-500","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":500,"trim":{"chart":0,"holes":[],"outer":500,"uv_bounds":[{"hi":17.850000010000002,"lo":17.24999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":501,"key":"side-0-0-501","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":501,"trim":{"chart":0,"holes":[],"outer":501,"uv_bounds":[{"hi":48.35000001,"lo":47.84999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":502,"key":"side-0-2-502","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":502,"trim":{"chart":0,"holes":[],"outer":502,"uv_bounds":[{"hi":12.15000001,"lo":11.649999989999998},{"hi":3.00000001,"lo":1.99999999}]}},{"id":503,"key":"top-503","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":503,"trim":{"chart":0,"holes":[],"outer":503,"uv_bounds":[{"hi":18.350000010000002,"lo":17.84999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":504,"key":"cut-41:top-504","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-41","entity":"cut-41","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":504,"trim":{"chart":0,"holes":[],"outer":504,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":505,"key":"side-0-0-505","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":505,"trim":{"chart":0,"holes":[],"outer":505,"uv_bounds":[{"hi":48.950000010000004,"lo":48.34999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":506,"key":"side-0-2-506","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":506,"trim":{"chart":0,"holes":[],"outer":506,"uv_bounds":[{"hi":11.65000001,"lo":11.049999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":507,"key":"top-507","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":507,"trim":{"chart":0,"holes":[],"outer":507,"uv_bounds":[{"hi":18.950000010000004,"lo":18.34999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":508,"key":"side-0-0-508","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":508,"trim":{"chart":0,"holes":[],"outer":508,"uv_bounds":[{"hi":48.950000010000004,"lo":48.34999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":509,"key":"side-0-2-509","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":509,"trim":{"chart":0,"holes":[],"outer":509,"uv_bounds":[{"hi":11.65000001,"lo":11.049999989999996},{"hi":2.00000001,"lo":-1e-8}]}},{"id":510,"key":"cut-41:right-510","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-41","entity":"cut-41","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":510,"trim":{"chart":0,"holes":[],"outer":510,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":511,"key":"cut-42:left-511","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-42","entity":"cut-42","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":511,"trim":{"chart":0,"holes":[],"outer":511,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":512,"key":"bottom-512","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":512,"trim":{"chart":0,"holes":[],"outer":512,"uv_bounds":[{"hi":18.950000010000004,"lo":18.34999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":513,"key":"side-0-0-513","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":513,"trim":{"chart":0,"holes":[],"outer":513,"uv_bounds":[{"hi":49.450000010000004,"lo":48.94999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":514,"key":"side-0-2-514","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":514,"trim":{"chart":0,"holes":[],"outer":514,"uv_bounds":[{"hi":11.050000009999998,"lo":10.549999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":515,"key":"top-515","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":515,"trim":{"chart":0,"holes":[],"outer":515,"uv_bounds":[{"hi":19.450000010000004,"lo":18.949999990000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":516,"key":"cut-42:top-516","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-42","entity":"cut-42","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":516,"trim":{"chart":0,"holes":[],"outer":516,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":517,"key":"side-0-0-517","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":517,"trim":{"chart":0,"holes":[],"outer":517,"uv_bounds":[{"hi":50.050000010000005,"lo":49.44999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":518,"key":"side-0-2-518","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":518,"trim":{"chart":0,"holes":[],"outer":518,"uv_bounds":[{"hi":10.550000009999998,"lo":9.949999989999995},{"hi":3.00000001,"lo":1.99999999}]}},{"id":519,"key":"top-519","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":519,"trim":{"chart":0,"holes":[],"outer":519,"uv_bounds":[{"hi":20.050000010000005,"lo":19.449999990000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":520,"key":"side-0-0-520","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":520,"trim":{"chart":0,"holes":[],"outer":520,"uv_bounds":[{"hi":50.050000010000005,"lo":49.44999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":521,"key":"side-0-2-521","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":521,"trim":{"chart":0,"holes":[],"outer":521,"uv_bounds":[{"hi":10.550000009999998,"lo":9.949999989999995},{"hi":2.00000001,"lo":-1e-8}]}},{"id":522,"key":"cut-42:right-522","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-42","entity":"cut-42","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":522,"trim":{"chart":0,"holes":[],"outer":522,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":523,"key":"cut-43:left-523","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-43","entity":"cut-43","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":523,"trim":{"chart":0,"holes":[],"outer":523,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":524,"key":"bottom-524","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":524,"trim":{"chart":0,"holes":[],"outer":524,"uv_bounds":[{"hi":20.050000010000005,"lo":19.449999990000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":525,"key":"side-0-0-525","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":525,"trim":{"chart":0,"holes":[],"outer":525,"uv_bounds":[{"hi":50.550000010000005,"lo":50.04999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":526,"key":"side-0-2-526","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":526,"trim":{"chart":0,"holes":[],"outer":526,"uv_bounds":[{"hi":9.950000009999997,"lo":9.449999989999995},{"hi":3.00000001,"lo":1.99999999}]}},{"id":527,"key":"top-527","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":527,"trim":{"chart":0,"holes":[],"outer":527,"uv_bounds":[{"hi":20.550000010000005,"lo":20.049999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":528,"key":"cut-43:top-528","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-43","entity":"cut-43","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":528,"trim":{"chart":0,"holes":[],"outer":528,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":529,"key":"side-0-0-529","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":529,"trim":{"chart":0,"holes":[],"outer":529,"uv_bounds":[{"hi":51.15000001000001,"lo":50.54999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":530,"key":"side-0-2-530","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":530,"trim":{"chart":0,"holes":[],"outer":530,"uv_bounds":[{"hi":9.450000009999997,"lo":8.849999989999993},{"hi":3.00000001,"lo":1.99999999}]}},{"id":531,"key":"top-531","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":531,"trim":{"chart":0,"holes":[],"outer":531,"uv_bounds":[{"hi":21.150000010000007,"lo":20.549999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":532,"key":"side-0-0-532","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":532,"trim":{"chart":0,"holes":[],"outer":532,"uv_bounds":[{"hi":51.15000001000001,"lo":50.54999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":533,"key":"side-0-2-533","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":533,"trim":{"chart":0,"holes":[],"outer":533,"uv_bounds":[{"hi":9.450000009999997,"lo":8.849999989999993},{"hi":2.00000001,"lo":-1e-8}]}},{"id":534,"key":"cut-43:right-534","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-43","entity":"cut-43","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":534,"trim":{"chart":0,"holes":[],"outer":534,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":535,"key":"cut-44:left-535","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-44","entity":"cut-44","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":535,"trim":{"chart":0,"holes":[],"outer":535,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":536,"key":"bottom-536","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":536,"trim":{"chart":0,"holes":[],"outer":536,"uv_bounds":[{"hi":21.150000010000007,"lo":20.549999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":537,"key":"side-0-0-537","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":537,"trim":{"chart":0,"holes":[],"outer":537,"uv_bounds":[{"hi":51.65000001000001,"lo":51.149999990000005},{"hi":3.00000001,"lo":1.99999999}]}},{"id":538,"key":"side-0-2-538","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":538,"trim":{"chart":0,"holes":[],"outer":538,"uv_bounds":[{"hi":8.850000009999995,"lo":8.349999989999993},{"hi":3.00000001,"lo":1.99999999}]}},{"id":539,"key":"top-539","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":539,"trim":{"chart":0,"holes":[],"outer":539,"uv_bounds":[{"hi":21.650000010000007,"lo":21.149999990000005},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":540,"key":"cut-44:top-540","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-44","entity":"cut-44","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":540,"trim":{"chart":0,"holes":[],"outer":540,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":541,"key":"side-0-0-541","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":541,"trim":{"chart":0,"holes":[],"outer":541,"uv_bounds":[{"hi":52.25000001000001,"lo":51.649999990000005},{"hi":3.00000001,"lo":1.99999999}]}},{"id":542,"key":"side-0-2-542","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":542,"trim":{"chart":0,"holes":[],"outer":542,"uv_bounds":[{"hi":8.350000009999995,"lo":7.749999989999993},{"hi":3.00000001,"lo":1.99999999}]}},{"id":543,"key":"top-543","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":543,"trim":{"chart":0,"holes":[],"outer":543,"uv_bounds":[{"hi":22.250000010000008,"lo":21.649999990000005},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":544,"key":"side-0-0-544","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":544,"trim":{"chart":0,"holes":[],"outer":544,"uv_bounds":[{"hi":52.25000001000001,"lo":51.649999990000005},{"hi":2.00000001,"lo":-1e-8}]}},{"id":545,"key":"side-0-2-545","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":545,"trim":{"chart":0,"holes":[],"outer":545,"uv_bounds":[{"hi":8.350000009999995,"lo":7.749999989999993},{"hi":2.00000001,"lo":-1e-8}]}},{"id":546,"key":"cut-44:right-546","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-44","entity":"cut-44","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":546,"trim":{"chart":0,"holes":[],"outer":546,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":547,"key":"cut-45:left-547","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-45","entity":"cut-45","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":547,"trim":{"chart":0,"holes":[],"outer":547,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":548,"key":"bottom-548","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":548,"trim":{"chart":0,"holes":[],"outer":548,"uv_bounds":[{"hi":22.250000010000008,"lo":21.649999990000005},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":549,"key":"side-0-0-549","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":549,"trim":{"chart":0,"holes":[],"outer":549,"uv_bounds":[{"hi":52.75000001000001,"lo":52.249999990000006},{"hi":3.00000001,"lo":1.99999999}]}},{"id":550,"key":"side-0-2-550","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":550,"trim":{"chart":0,"holes":[],"outer":550,"uv_bounds":[{"hi":7.750000009999993,"lo":7.249999989999993},{"hi":3.00000001,"lo":1.99999999}]}},{"id":551,"key":"top-551","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":551,"trim":{"chart":0,"holes":[],"outer":551,"uv_bounds":[{"hi":22.750000010000008,"lo":22.249999990000006},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":552,"key":"cut-45:top-552","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-45","entity":"cut-45","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":552,"trim":{"chart":0,"holes":[],"outer":552,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":553,"key":"side-0-0-553","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":553,"trim":{"chart":0,"holes":[],"outer":553,"uv_bounds":[{"hi":53.35000001,"lo":52.749999990000006},{"hi":3.00000001,"lo":1.99999999}]}},{"id":554,"key":"side-0-2-554","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":554,"trim":{"chart":0,"holes":[],"outer":554,"uv_bounds":[{"hi":7.250000009999993,"lo":6.649999989999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":555,"key":"top-555","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":555,"trim":{"chart":0,"holes":[],"outer":555,"uv_bounds":[{"hi":23.350000010000002,"lo":22.749999990000006},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":556,"key":"side-0-0-556","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":556,"trim":{"chart":0,"holes":[],"outer":556,"uv_bounds":[{"hi":53.35000001,"lo":52.749999990000006},{"hi":2.00000001,"lo":-1e-8}]}},{"id":557,"key":"side-0-2-557","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":557,"trim":{"chart":0,"holes":[],"outer":557,"uv_bounds":[{"hi":7.250000009999993,"lo":6.649999989999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":558,"key":"cut-45:right-558","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-45","entity":"cut-45","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":558,"trim":{"chart":0,"holes":[],"outer":558,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":559,"key":"cut-46:left-559","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-46","entity":"cut-46","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":559,"trim":{"chart":0,"holes":[],"outer":559,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":560,"key":"bottom-560","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":560,"trim":{"chart":0,"holes":[],"outer":560,"uv_bounds":[{"hi":23.350000010000002,"lo":22.749999990000006},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":561,"key":"side-0-0-561","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":561,"trim":{"chart":0,"holes":[],"outer":561,"uv_bounds":[{"hi":53.85000001,"lo":53.34999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":562,"key":"side-0-2-562","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":562,"trim":{"chart":0,"holes":[],"outer":562,"uv_bounds":[{"hi":6.6500000099999985,"lo":6.149999989999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":563,"key":"top-563","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":563,"trim":{"chart":0,"holes":[],"outer":563,"uv_bounds":[{"hi":23.850000010000002,"lo":23.34999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":564,"key":"cut-46:top-564","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-46","entity":"cut-46","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":564,"trim":{"chart":0,"holes":[],"outer":564,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":565,"key":"side-0-0-565","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":565,"trim":{"chart":0,"holes":[],"outer":565,"uv_bounds":[{"hi":54.450000010000004,"lo":53.84999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":566,"key":"side-0-2-566","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":566,"trim":{"chart":0,"holes":[],"outer":566,"uv_bounds":[{"hi":6.1500000099999985,"lo":5.549999989999997},{"hi":3.00000001,"lo":1.99999999}]}},{"id":567,"key":"top-567","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":567,"trim":{"chart":0,"holes":[],"outer":567,"uv_bounds":[{"hi":24.450000010000004,"lo":23.84999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":568,"key":"side-0-0-568","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":568,"trim":{"chart":0,"holes":[],"outer":568,"uv_bounds":[{"hi":54.450000010000004,"lo":53.84999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":569,"key":"side-0-2-569","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":569,"trim":{"chart":0,"holes":[],"outer":569,"uv_bounds":[{"hi":6.1500000099999985,"lo":5.549999989999997},{"hi":2.00000001,"lo":-1e-8}]}},{"id":570,"key":"cut-46:right-570","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-46","entity":"cut-46","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":570,"trim":{"chart":0,"holes":[],"outer":570,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":571,"key":"cut-47:left-571","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-47","entity":"cut-47","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":571,"trim":{"chart":0,"holes":[],"outer":571,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":572,"key":"bottom-572","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":572,"trim":{"chart":0,"holes":[],"outer":572,"uv_bounds":[{"hi":24.450000010000004,"lo":23.84999999},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":573,"key":"side-0-0-573","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":573,"trim":{"chart":0,"holes":[],"outer":573,"uv_bounds":[{"hi":54.950000010000004,"lo":54.44999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":574,"key":"side-0-2-574","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":574,"trim":{"chart":0,"holes":[],"outer":574,"uv_bounds":[{"hi":5.550000009999997,"lo":5.049999989999997},{"hi":3.00000001,"lo":1.99999999}]}},{"id":575,"key":"top-575","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":575,"trim":{"chart":0,"holes":[],"outer":575,"uv_bounds":[{"hi":24.950000010000004,"lo":24.449999990000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":576,"key":"cut-47:top-576","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-47","entity":"cut-47","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":576,"trim":{"chart":0,"holes":[],"outer":576,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":577,"key":"side-0-0-577","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":577,"trim":{"chart":0,"holes":[],"outer":577,"uv_bounds":[{"hi":55.550000010000005,"lo":54.94999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":578,"key":"side-0-2-578","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":578,"trim":{"chart":0,"holes":[],"outer":578,"uv_bounds":[{"hi":5.050000009999997,"lo":4.449999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":579,"key":"top-579","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":579,"trim":{"chart":0,"holes":[],"outer":579,"uv_bounds":[{"hi":25.550000010000005,"lo":24.949999990000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":580,"key":"side-0-0-580","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":580,"trim":{"chart":0,"holes":[],"outer":580,"uv_bounds":[{"hi":55.550000010000005,"lo":54.94999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":581,"key":"side-0-2-581","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":581,"trim":{"chart":0,"holes":[],"outer":581,"uv_bounds":[{"hi":5.050000009999997,"lo":4.449999989999996},{"hi":2.00000001,"lo":-1e-8}]}},{"id":582,"key":"cut-47:right-582","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-47","entity":"cut-47","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":582,"trim":{"chart":0,"holes":[],"outer":582,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":583,"key":"cut-48:left-583","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-48","entity":"cut-48","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":583,"trim":{"chart":0,"holes":[],"outer":583,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":584,"key":"bottom-584","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":584,"trim":{"chart":0,"holes":[],"outer":584,"uv_bounds":[{"hi":25.550000010000005,"lo":24.949999990000002},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":585,"key":"side-0-0-585","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":585,"trim":{"chart":0,"holes":[],"outer":585,"uv_bounds":[{"hi":56.050000010000005,"lo":55.54999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":586,"key":"side-0-2-586","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":586,"trim":{"chart":0,"holes":[],"outer":586,"uv_bounds":[{"hi":4.450000009999996,"lo":3.949999989999996},{"hi":3.00000001,"lo":1.99999999}]}},{"id":587,"key":"top-587","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":587,"trim":{"chart":0,"holes":[],"outer":587,"uv_bounds":[{"hi":26.050000010000005,"lo":25.549999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":588,"key":"cut-48:top-588","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-48","entity":"cut-48","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":588,"trim":{"chart":0,"holes":[],"outer":588,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":589,"key":"side-0-0-589","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":589,"trim":{"chart":0,"holes":[],"outer":589,"uv_bounds":[{"hi":56.65000001000001,"lo":56.04999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":590,"key":"side-0-2-590","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":590,"trim":{"chart":0,"holes":[],"outer":590,"uv_bounds":[{"hi":3.9500000099999957,"lo":3.3499999899999944},{"hi":3.00000001,"lo":1.99999999}]}},{"id":591,"key":"top-591","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":591,"trim":{"chart":0,"holes":[],"outer":591,"uv_bounds":[{"hi":26.650000010000007,"lo":26.049999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":592,"key":"side-0-0-592","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":592,"trim":{"chart":0,"holes":[],"outer":592,"uv_bounds":[{"hi":56.65000001000001,"lo":56.04999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":593,"key":"side-0-2-593","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":593,"trim":{"chart":0,"holes":[],"outer":593,"uv_bounds":[{"hi":3.9500000099999957,"lo":3.3499999899999944},{"hi":2.00000001,"lo":-1e-8}]}},{"id":594,"key":"cut-48:right-594","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-48","entity":"cut-48","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":594,"trim":{"chart":0,"holes":[],"outer":594,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":595,"key":"cut-49:left-595","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-49","entity":"cut-49","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":595,"trim":{"chart":0,"holes":[],"outer":595,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":596,"key":"bottom-596","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":596,"trim":{"chart":0,"holes":[],"outer":596,"uv_bounds":[{"hi":26.650000010000007,"lo":26.049999990000003},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":597,"key":"side-0-0-597","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":597,"trim":{"chart":0,"holes":[],"outer":597,"uv_bounds":[{"hi":57.15000001000001,"lo":56.649999990000005},{"hi":3.00000001,"lo":1.99999999}]}},{"id":598,"key":"side-0-2-598","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":598,"trim":{"chart":0,"holes":[],"outer":598,"uv_bounds":[{"hi":3.3500000099999943,"lo":2.8499999899999944},{"hi":3.00000001,"lo":1.99999999}]}},{"id":599,"key":"top-599","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":599,"trim":{"chart":0,"holes":[],"outer":599,"uv_bounds":[{"hi":27.150000010000007,"lo":26.649999990000005},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":600,"key":"cut-49:top-600","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-49","entity":"cut-49","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":600,"trim":{"chart":0,"holes":[],"outer":600,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.30000001000000004,"lo":0.09999999000000001}]}},{"id":601,"key":"side-0-0-601","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":601,"trim":{"chart":0,"holes":[],"outer":601,"uv_bounds":[{"hi":60.00000001,"lo":57.149999990000005},{"hi":3.00000001,"lo":1.99999999}]}},{"id":602,"key":"side-0-2-602","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":602,"trim":{"chart":0,"holes":[],"outer":602,"uv_bounds":[{"hi":2.8500000099999943,"lo":-1e-8},{"hi":3.00000001,"lo":1.99999999}]}},{"id":603,"key":"side-0-1-603","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":603,"trim":{"chart":0,"holes":[],"outer":603,"uv_bounds":[{"hi":0.20000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.99999999}]}},{"id":604,"key":"top-604","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":604,"trim":{"chart":0,"holes":[],"outer":604,"uv_bounds":[{"hi":30.00000001,"lo":27.149999990000005},{"hi":0.10000001,"lo":-0.10000001}]}},{"id":605,"key":"side-0-0-605","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":605,"trim":{"chart":0,"holes":[],"outer":605,"uv_bounds":[{"hi":60.00000001,"lo":57.149999990000005},{"hi":2.00000001,"lo":-1e-8}]}},{"id":606,"key":"side-0-2-606","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":606,"trim":{"chart":0,"holes":[],"outer":606,"uv_bounds":[{"hi":2.8500000099999943,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":607,"key":"cut-49:right-607","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"cut-49","entity":"cut-49","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":607,"trim":{"chart":0,"holes":[],"outer":607,"uv_bounds":[{"hi":0.30000001000000004,"lo":0.09999999000000001},{"hi":2.00000001,"lo":-1e-8}]}},{"id":608,"key":"side-0-1-608","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":608,"trim":{"chart":0,"holes":[],"outer":608,"uv_bounds":[{"hi":0.20000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":609,"key":"bottom-609","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wall","entity":"wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":609,"trim":{"chart":0,"holes":[],"outer":609,"uv_bounds":[{"hi":30.00000001,"lo":27.149999990000005},{"hi":0.10000001,"lo":-0.10000001}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":18,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":39,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":15,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":10,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":13,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":6,"twin":43,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":7,"twin":20,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":8,"wire_ref":null},{"edge":7,"face":2,"from":4,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":7,"twin":7,"wire_ref":null},{"edge":8,"face":2,"from":7,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":0,"twin":27,"wire_ref":null},{"edge":3,"face":2,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":3,"wire_ref":null},{"edge":9,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":4,"twin":14,"wire_ref":null},{"edge":10,"face":3,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":5,"twin":46,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":4,"twin":4,"wire_ref":null},{"edge":9,"face":3,"from":4,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":3,"twin":11,"wire_ref":null},{"edge":2,"face":3,"from":3,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":2,"twin":2,"wire_ref":null},{"edge":11,"face":4,"from":8,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":9,"twin":35,"wire_ref":null},{"edge":12,"face":4,"from":9,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":1,"twin":28,"wire_ref":null},{"edge":0,"face":4,"from":1,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":0,"twin":0,"wire_ref":null},{"edge":13,"face":4,"from":0,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":8,"twin":26,"wire_ref":null},{"edge":6,"face":5,"from":7,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":6,"wire_ref":null},{"edge":14,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":10,"twin":30,"wire_ref":null},{"edge":15,"face":5,"from":10,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":11,"twin":33,"wire_ref":null},{"edge":16,"face":5,"from":11,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":7,"twin":24,"wire_ref":null},{"edge":16,"face":6,"from":7,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":11,"twin":23,"wire_ref":null},{"edge":17,"face":6,"from":11,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":8,"twin":32,"wire_ref":null},{"edge":13,"face":6,"from":8,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":0,"twin":19,"wire_ref":null},{"edge":8,"face":6,"from":0,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":7,"twin":9,"wire_ref":null},{"edge":12,"face":7,"from":1,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":9,"twin":17,"wire_ref":null},{"edge":18,"face":7,"from":9,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":10,"twin":34,"wire_ref":null},{"edge":14,"face":7,"from":10,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":6,"twin":21,"wire_ref":null},{"edge":19,"face":7,"from":6,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":1,"twin":48,"wire_ref":null},{"edge":17,"face":8,"from":8,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":11,"twin":25,"wire_ref":null},{"edge":15,"face":8,"from":11,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":10,"twin":22,"wire_ref":null},{"edge":18,"face":8,"from":10,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":9,"twin":29,"wire_ref":null},{"edge":11,"face":8,"from":9,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":8,"twin":16,"wire_ref":null},{"edge":20,"face":9,"from":1,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":12,"twin":51,"wire_ref":null},{"edge":21,"face":9,"from":12,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":13,"twin":55,"wire_ref":null},{"edge":22,"face":9,"from":13,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":2,"twin":47,"wire_ref":null},{"edge":1,"face":9,"from":2,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":1,"twin":1,"wire_ref":null},{"edge":23,"face":10,"from":5,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":14,"twin":45,"wire_ref":null},{"edge":24,"face":10,"from":14,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":15,"twin":59,"wire_ref":null},{"edge":25,"face":10,"from":15,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":6,"twin":49,"wire_ref":null},{"edge":5,"face":10,"from":6,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":5,"twin":5,"wire_ref":null},{"edge":26,"face":11,"from":13,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":14,"twin":62,"wire_ref":null},{"edge":23,"face":11,"from":14,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Reverse"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":5,"twin":40,"wire_ref":null},{"edge":10,"face":11,"from":5,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":2,"twin":12,"wire_ref":null},{"edge":22,"face":11,"from":2,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Forward"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":13,"twin":38,"wire_ref":null},{"edge":19,"face":12,"from":1,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":12,"next":49,"prev":51,"to":6,"twin":31,"wire_ref":null},{"edge":25,"face":12,"from":6,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":12,"next":50,"prev":48,"to":15,"twin":42,"wire_ref":null},{"edge":27,"face":12,"from":15,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":12,"next":51,"prev":49,"to":12,"twin":75,"wire_ref":null},{"edge":20,"face":12,"from":12,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":12,"next":48,"prev":50,"to":1,"twin":36,"wire_ref":null},{"edge":28,"face":13,"from":12,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":13,"next":53,"prev":55,"to":16,"twin":66,"wire_ref":null},{"edge":29,"face":13,"from":16,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Reverse"},"id":53,"loop_ref":13,"next":54,"prev":52,"to":17,"twin":87,"wire_ref":null},{"edge":30,"face":13,"from":17,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":13,"next":55,"prev":53,"to":13,"twin":63,"wire_ref":null},{"edge":21,"face":13,"from":13,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Forward"},"id":55,"loop_ref":13,"next":52,"prev":54,"to":12,"twin":37,"wire_ref":null},{"edge":31,"face":14,"from":14,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":14,"next":57,"prev":59,"to":18,"twin":61,"wire_ref":null},{"edge":32,"face":14,"from":18,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":14,"next":58,"prev":56,"to":19,"twin":91,"wire_ref":null},{"edge":33,"face":14,"from":19,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":14,"next":59,"prev":57,"to":15,"twin":68,"wire_ref":null},{"edge":24,"face":14,"from":15,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":14,"next":56,"prev":58,"to":14,"twin":41,"wire_ref":null},{"edge":34,"face":15,"from":17,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Forward"},"id":60,"loop_ref":15,"next":61,"prev":63,"to":18,"twin":94,"wire_ref":null},{"edge":31,"face":15,"from":18,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Reverse"},"id":61,"loop_ref":15,"next":62,"prev":60,"to":14,"twin":56,"wire_ref":null},{"edge":26,"face":15,"from":14,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":15,"next":63,"prev":61,"to":13,"twin":44,"wire_ref":null},{"edge":30,"face":15,"from":13,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Forward"},"id":63,"loop_ref":15,"next":60,"prev":62,"to":17,"twin":54,"wire_ref":null},{"edge":35,"face":16,"from":20,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Forward"},"id":64,"loop_ref":16,"next":65,"prev":67,"to":21,"twin":83,"wire_ref":null},{"edge":36,"face":16,"from":21,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Reverse"},"id":65,"loop_ref":16,"next":66,"prev":64,"to":16,"twin":76,"wire_ref":null},{"edge":28,"face":16,"from":16,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Reverse"},"id":66,"loop_ref":16,"next":67,"prev":65,"to":12,"twin":52,"wire_ref":null},{"edge":37,"face":16,"from":12,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Forward"},"id":67,"loop_ref":16,"next":64,"prev":66,"to":20,"twin":74,"wire_ref":null},{"edge":33,"face":17,"from":15,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":17,"next":69,"prev":71,"to":19,"twin":58,"wire_ref":null},{"edge":38,"face":17,"from":19,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Forward"},"id":69,"loop_ref":17,"next":70,"prev":68,"to":22,"twin":78,"wire_ref":null},{"edge":39,"face":17,"from":22,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":17,"next":71,"prev":69,"to":23,"twin":81,"wire_ref":null},{"edge":40,"face":17,"from":23,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Reverse"},"id":71,"loop_ref":17,"next":68,"prev":70,"to":15,"twin":72,"wire_ref":null},{"edge":40,"face":18,"from":15,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Forward"},"id":72,"loop_ref":18,"next":73,"prev":75,"to":23,"twin":71,"wire_ref":null},{"edge":41,"face":18,"from":23,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Reverse"},"id":73,"loop_ref":18,"next":74,"prev":72,"to":20,"twin":80,"wire_ref":null},{"edge":37,"face":18,"from":20,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":18,"next":75,"prev":73,"to":12,"twin":67,"wire_ref":null},{"edge":27,"face":18,"from":12,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Forward"},"id":75,"loop_ref":18,"next":72,"prev":74,"to":15,"twin":50,"wire_ref":null},{"edge":36,"face":19,"from":16,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Forward"},"id":76,"loop_ref":19,"next":77,"prev":79,"to":21,"twin":65,"wire_ref":null},{"edge":42,"face":19,"from":21,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Forward"},"id":77,"loop_ref":19,"next":78,"prev":76,"to":22,"twin":82,"wire_ref":null},{"edge":38,"face":19,"from":22,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":19,"next":79,"prev":77,"to":19,"twin":69,"wire_ref":null},{"edge":43,"face":19,"from":19,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Reverse"},"id":79,"loop_ref":19,"next":76,"prev":78,"to":16,"twin":96,"wire_ref":null},{"edge":41,"face":20,"from":20,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Forward"},"id":80,"loop_ref":20,"next":81,"prev":83,"to":23,"twin":73,"wire_ref":null},{"edge":39,"face":20,"from":23,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Forward"},"id":81,"loop_ref":20,"next":82,"prev":80,"to":22,"twin":70,"wire_ref":null},{"edge":42,"face":20,"from":22,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":20,"next":83,"prev":81,"to":21,"twin":77,"wire_ref":null},{"edge":35,"face":20,"from":21,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":20,"next":80,"prev":82,"to":20,"twin":64,"wire_ref":null},{"edge":44,"face":21,"from":16,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Forward"},"id":84,"loop_ref":21,"next":85,"prev":87,"to":24,"twin":99,"wire_ref":null},{"edge":45,"face":21,"from":24,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Reverse"},"id":85,"loop_ref":21,"next":86,"prev":84,"to":25,"twin":103,"wire_ref":null},{"edge":46,"face":21,"from":25,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Reverse"},"id":86,"loop_ref":21,"next":87,"prev":85,"to":17,"twin":95,"wire_ref":null},{"edge":29,"face":21,"from":17,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Forward"},"id":87,"loop_ref":21,"next":84,"prev":86,"to":16,"twin":53,"wire_ref":null},{"edge":47,"face":22,"from":18,"geometry_use":{"pcurve":88,"periodic_lift":[0,0],"sense":"Forward"},"id":88,"loop_ref":22,"next":89,"prev":91,"to":26,"twin":93,"wire_ref":null},{"edge":48,"face":22,"from":26,"geometry_use":{"pcurve":89,"periodic_lift":[0,0],"sense":"Forward"},"id":89,"loop_ref":22,"next":90,"prev":88,"to":27,"twin":107,"wire_ref":null},{"edge":49,"face":22,"from":27,"geometry_use":{"pcurve":90,"periodic_lift":[0,0],"sense":"Reverse"},"id":90,"loop_ref":22,"next":91,"prev":89,"to":19,"twin":97,"wire_ref":null},{"edge":32,"face":22,"from":19,"geometry_use":{"pcurve":91,"periodic_lift":[0,0],"sense":"Reverse"},"id":91,"loop_ref":22,"next":88,"prev":90,"to":18,"twin":57,"wire_ref":null},{"edge":50,"face":23,"from":25,"geometry_use":{"pcurve":92,"periodic_lift":[0,0],"sense":"Forward"},"id":92,"loop_ref":23,"next":93,"prev":95,"to":26,"twin":110,"wire_ref":null},{"edge":47,"face":23,"from":26,"geometry_use":{"pcurve":93,"periodic_lift":[0,0],"sense":"Reverse"},"id":93,"loop_ref":23,"next":94,"prev":92,"to":18,"twin":88,"wire_ref":null},{"edge":34,"face":23,"from":18,"geometry_use":{"pcurve":94,"periodic_lift":[0,0],"sense":"Reverse"},"id":94,"loop_ref":23,"next":95,"prev":93,"to":17,"twin":60,"wire_ref":null},{"edge":46,"face":23,"from":17,"geometry_use":{"pcurve":95,"periodic_lift":[0,0],"sense":"Forward"},"id":95,"loop_ref":23,"next":92,"prev":94,"to":25,"twin":86,"wire_ref":null},{"edge":43,"face":24,"from":16,"geometry_use":{"pcurve":96,"periodic_lift":[0,0],"sense":"Forward"},"id":96,"loop_ref":24,"next":97,"prev":99,"to":19,"twin":79,"wire_ref":null},{"edge":49,"face":24,"from":19,"geometry_use":{"pcurve":97,"periodic_lift":[0,0],"sense":"Forward"},"id":97,"loop_ref":24,"next":98,"prev":96,"to":27,"twin":90,"wire_ref":null},{"edge":51,"face":24,"from":27,"geometry_use":{"pcurve":98,"periodic_lift":[0,0],"sense":"Reverse"},"id":98,"loop_ref":24,"next":99,"prev":97,"to":24,"twin":123,"wire_ref":null},{"edge":44,"face":24,"from":24,"geometry_use":{"pcurve":99,"periodic_lift":[0,0],"sense":"Reverse"},"id":99,"loop_ref":24,"next":96,"prev":98,"to":16,"twin":84,"wire_ref":null},{"edge":52,"face":25,"from":24,"geometry_use":{"pcurve":100,"periodic_lift":[0,0],"sense":"Forward"},"id":100,"loop_ref":25,"next":101,"prev":103,"to":28,"twin":114,"wire_ref":null},{"edge":53,"face":25,"from":28,"geometry_use":{"pcurve":101,"periodic_lift":[0,0],"sense":"Reverse"},"id":101,"loop_ref":25,"next":102,"prev":100,"to":29,"twin":135,"wire_ref":null},{"edge":54,"face":25,"from":29,"geometry_use":{"pcurve":102,"periodic_lift":[0,0],"sense":"Reverse"},"id":102,"loop_ref":25,"next":103,"prev":101,"to":25,"twin":111,"wire_ref":null},{"edge":45,"face":25,"from":25,"geometry_use":{"pcurve":103,"periodic_lift":[0,0],"sense":"Forward"},"id":103,"loop_ref":25,"next":100,"prev":102,"to":24,"twin":85,"wire_ref":null},{"edge":55,"face":26,"from":26,"geometry_use":{"pcurve":104,"periodic_lift":[0,0],"sense":"Forward"},"id":104,"loop_ref":26,"next":105,"prev":107,"to":30,"twin":109,"wire_ref":null},{"edge":56,"face":26,"from":30,"geometry_use":{"pcurve":105,"periodic_lift":[0,0],"sense":"Forward"},"id":105,"loop_ref":26,"next":106,"prev":104,"to":31,"twin":139,"wire_ref":null},{"edge":57,"face":26,"from":31,"geometry_use":{"pcurve":106,"periodic_lift":[0,0],"sense":"Reverse"},"id":106,"loop_ref":26,"next":107,"prev":105,"to":27,"twin":116,"wire_ref":null},{"edge":48,"face":26,"from":27,"geometry_use":{"pcurve":107,"periodic_lift":[0,0],"sense":"Reverse"},"id":107,"loop_ref":26,"next":104,"prev":106,"to":26,"twin":89,"wire_ref":null},{"edge":58,"face":27,"from":29,"geometry_use":{"pcurve":108,"periodic_lift":[0,0],"sense":"Forward"},"id":108,"loop_ref":27,"next":109,"prev":111,"to":30,"twin":142,"wire_ref":null},{"edge":55,"face":27,"from":30,"geometry_use":{"pcurve":109,"periodic_lift":[0,0],"sense":"Reverse"},"id":109,"loop_ref":27,"next":110,"prev":108,"to":26,"twin":104,"wire_ref":null},{"edge":50,"face":27,"from":26,"geometry_use":{"pcurve":110,"periodic_lift":[0,0],"sense":"Reverse"},"id":110,"loop_ref":27,"next":111,"prev":109,"to":25,"twin":92,"wire_ref":null},{"edge":54,"face":27,"from":25,"geometry_use":{"pcurve":111,"periodic_lift":[0,0],"sense":"Forward"},"id":111,"loop_ref":27,"next":108,"prev":110,"to":29,"twin":102,"wire_ref":null},{"edge":59,"face":28,"from":32,"geometry_use":{"pcurve":112,"periodic_lift":[0,0],"sense":"Forward"},"id":112,"loop_ref":28,"next":113,"prev":115,"to":33,"twin":131,"wire_ref":null},{"edge":60,"face":28,"from":33,"geometry_use":{"pcurve":113,"periodic_lift":[0,0],"sense":"Reverse"},"id":113,"loop_ref":28,"next":114,"prev":112,"to":28,"twin":124,"wire_ref":null},{"edge":52,"face":28,"from":28,"geometry_use":{"pcurve":114,"periodic_lift":[0,0],"sense":"Reverse"},"id":114,"loop_ref":28,"next":115,"prev":113,"to":24,"twin":100,"wire_ref":null},{"edge":61,"face":28,"from":24,"geometry_use":{"pcurve":115,"periodic_lift":[0,0],"sense":"Forward"},"id":115,"loop_ref":28,"next":112,"prev":114,"to":32,"twin":122,"wire_ref":null},{"edge":57,"face":29,"from":27,"geometry_use":{"pcurve":116,"periodic_lift":[0,0],"sense":"Forward"},"id":116,"loop_ref":29,"next":117,"prev":119,"to":31,"twin":106,"wire_ref":null},{"edge":62,"face":29,"from":31,"geometry_use":{"pcurve":117,"periodic_lift":[0,0],"sense":"Forward"},"id":117,"loop_ref":29,"next":118,"prev":116,"to":34,"twin":126,"wire_ref":null},{"edge":63,"face":29,"from":34,"geometry_use":{"pcurve":118,"periodic_lift":[0,0],"sense":"Reverse"},"id":118,"loop_ref":29,"next":119,"prev":117,"to":35,"twin":129,"wire_ref":null},{"edge":64,"face":29,"from":35,"geometry_use":{"pcurve":119,"periodic_lift":[0,0],"sense":"Reverse"},"id":119,"loop_ref":29,"next":116,"prev":118,"to":27,"twin":120,"wire_ref":null},{"edge":64,"face":30,"from":27,"geometry_use":{"pcurve":120,"periodic_lift":[0,0],"sense":"Forward"},"id":120,"loop_ref":30,"next":121,"prev":123,"to":35,"twin":119,"wire_ref":null},{"edge":65,"face":30,"from":35,"geometry_use":{"pcurve":121,"periodic_lift":[0,0],"sense":"Reverse"},"id":121,"loop_ref":30,"next":122,"prev":120,"to":32,"twin":128,"wire_ref":null},{"edge":61,"face":30,"from":32,"geometry_use":{"pcurve":122,"periodic_lift":[0,0],"sense":"Reverse"},"id":122,"loop_ref":30,"next":123,"prev":121,"to":24,"twin":115,"wire_ref":null},{"edge":51,"face":30,"from":24,"geometry_use":{"pcurve":123,"periodic_lift":[0,0],"sense":"Forward"},"id":123,"loop_ref":30,"next":120,"prev":122,"to":27,"twin":98,"wire_ref":null},{"edge":60,"face":31,"from":28,"geometry_use":{"pcurve":124,"periodic_lift":[0,0],"sense":"Forward"},"id":124,"loop_ref":31,"next":125,"prev":127,"to":33,"twin":113,"wire_ref":null},{"edge":66,"face":31,"from":33,"geometry_use":{"pcurve":125,"periodic_lift":[0,0],"sense":"Forward"},"id":125,"loop_ref":31,"next":126,"prev":124,"to":34,"twin":130,"wire_ref":null},{"edge":62,"face":31,"from":34,"geometry_use":{"pcurve":126,"periodic_lift":[0,0],"sense":"Reverse"},"id":126,"loop_ref":31,"next":127,"prev":125,"to":31,"twin":117,"wire_ref":null},{"edge":67,"face":31,"from":31,"geometry_use":{"pcurve":127,"periodic_lift":[0,0],"sense":"Reverse"},"id":127,"loop_ref":31,"next":124,"prev":126,"to":28,"twin":144,"wire_ref":null},{"edge":65,"face":32,"from":32,"geometry_use":{"pcurve":128,"periodic_lift":[0,0],"sense":"Forward"},"id":128,"loop_ref":32,"next":129,"prev":131,"to":35,"twin":121,"wire_ref":null},{"edge":63,"face":32,"from":35,"geometry_use":{"pcurve":129,"periodic_lift":[0,0],"sense":"Forward"},"id":129,"loop_ref":32,"next":130,"prev":128,"to":34,"twin":118,"wire_ref":null},{"edge":66,"face":32,"from":34,"geometry_use":{"pcurve":130,"periodic_lift":[0,0],"sense":"Reverse"},"id":130,"loop_ref":32,"next":131,"prev":129,"to":33,"twin":125,"wire_ref":null},{"edge":59,"face":32,"from":33,"geometry_use":{"pcurve":131,"periodic_lift":[0,0],"sense":"Reverse"},"id":131,"loop_ref":32,"next":128,"prev":130,"to":32,"twin":112,"wire_ref":null},{"edge":68,"face":33,"from":28,"geometry_use":{"pcurve":132,"periodic_lift":[0,0],"sense":"Forward"},"id":132,"loop_ref":33,"next":133,"prev":135,"to":36,"twin":147,"wire_ref":null},{"edge":69,"face":33,"from":36,"geometry_use":{"pcurve":133,"periodic_lift":[0,0],"sense":"Reverse"},"id":133,"loop_ref":33,"next":134,"prev":132,"to":37,"twin":151,"wire_ref":null},{"edge":70,"face":33,"from":37,"geometry_use":{"pcurve":134,"periodic_lift":[0,0],"sense":"Reverse"},"id":134,"loop_ref":33,"next":135,"prev":133,"to":29,"twin":143,"wire_ref":null},{"edge":53,"face":33,"from":29,"geometry_use":{"pcurve":135,"periodic_lift":[0,0],"sense":"Forward"},"id":135,"loop_ref":33,"next":132,"prev":134,"to":28,"twin":101,"wire_ref":null},{"edge":71,"face":34,"from":30,"geometry_use":{"pcurve":136,"periodic_lift":[0,0],"sense":"Forward"},"id":136,"loop_ref":34,"next":137,"prev":139,"to":38,"twin":141,"wire_ref":null},{"edge":72,"face":34,"from":38,"geometry_use":{"pcurve":137,"periodic_lift":[0,0],"sense":"Forward"},"id":137,"loop_ref":34,"next":138,"prev":136,"to":39,"twin":155,"wire_ref":null},{"edge":73,"face":34,"from":39,"geometry_use":{"pcurve":138,"periodic_lift":[0,0],"sense":"Reverse"},"id":138,"loop_ref":34,"next":139,"prev":137,"to":31,"twin":145,"wire_ref":null},{"edge":56,"face":34,"from":31,"geometry_use":{"pcurve":139,"periodic_lift":[0,0],"sense":"Reverse"},"id":139,"loop_ref":34,"next":136,"prev":138,"to":30,"twin":105,"wire_ref":null},{"edge":74,"face":35,"from":37,"geometry_use":{"pcurve":140,"periodic_lift":[0,0],"sense":"Forward"},"id":140,"loop_ref":35,"next":141,"prev":143,"to":38,"twin":158,"wire_ref":null},{"edge":71,"face":35,"from":38,"geometry_use":{"pcurve":141,"periodic_lift":[0,0],"sense":"Reverse"},"id":141,"loop_ref":35,"next":142,"prev":140,"to":30,"twin":136,"wire_ref":null},{"edge":58,"face":35,"from":30,"geometry_use":{"pcurve":142,"periodic_lift":[0,0],"sense":"Reverse"},"id":142,"loop_ref":35,"next":143,"prev":141,"to":29,"twin":108,"wire_ref":null},{"edge":70,"face":35,"from":29,"geometry_use":{"pcurve":143,"periodic_lift":[0,0],"sense":"Forward"},"id":143,"loop_ref":35,"next":140,"prev":142,"to":37,"twin":134,"wire_ref":null},{"edge":67,"face":36,"from":28,"geometry_use":{"pcurve":144,"periodic_lift":[0,0],"sense":"Forward"},"id":144,"loop_ref":36,"next":145,"prev":147,"to":31,"twin":127,"wire_ref":null},{"edge":73,"face":36,"from":31,"geometry_use":{"pcurve":145,"periodic_lift":[0,0],"sense":"Forward"},"id":145,"loop_ref":36,"next":146,"prev":144,"to":39,"twin":138,"wire_ref":null},{"edge":75,"face":36,"from":39,"geometry_use":{"pcurve":146,"periodic_lift":[0,0],"sense":"Reverse"},"id":146,"loop_ref":36,"next":147,"prev":145,"to":36,"twin":171,"wire_ref":null},{"edge":68,"face":36,"from":36,"geometry_use":{"pcurve":147,"periodic_lift":[0,0],"sense":"Reverse"},"id":147,"loop_ref":36,"next":144,"prev":146,"to":28,"twin":132,"wire_ref":null},{"edge":76,"face":37,"from":36,"geometry_use":{"pcurve":148,"periodic_lift":[0,0],"sense":"Forward"},"id":148,"loop_ref":37,"next":149,"prev":151,"to":40,"twin":162,"wire_ref":null},{"edge":77,"face":37,"from":40,"geometry_use":{"pcurve":149,"periodic_lift":[0,0],"sense":"Reverse"},"id":149,"loop_ref":37,"next":150,"prev":148,"to":41,"twin":183,"wire_ref":null},{"edge":78,"face":37,"from":41,"geometry_use":{"pcurve":150,"periodic_lift":[0,0],"sense":"Reverse"},"id":150,"loop_ref":37,"next":151,"prev":149,"to":37,"twin":159,"wire_ref":null},{"edge":69,"face":37,"from":37,"geometry_use":{"pcurve":151,"periodic_lift":[0,0],"sense":"Forward"},"id":151,"loop_ref":37,"next":148,"prev":150,"to":36,"twin":133,"wire_ref":null},{"edge":79,"face":38,"from":38,"geometry_use":{"pcurve":152,"periodic_lift":[0,0],"sense":"Forward"},"id":152,"loop_ref":38,"next":153,"prev":155,"to":42,"twin":157,"wire_ref":null},{"edge":80,"face":38,"from":42,"geometry_use":{"pcurve":153,"periodic_lift":[0,0],"sense":"Forward"},"id":153,"loop_ref":38,"next":154,"prev":152,"to":43,"twin":187,"wire_ref":null},{"edge":81,"face":38,"from":43,"geometry_use":{"pcurve":154,"periodic_lift":[0,0],"sense":"Reverse"},"id":154,"loop_ref":38,"next":155,"prev":153,"to":39,"twin":164,"wire_ref":null},{"edge":72,"face":38,"from":39,"geometry_use":{"pcurve":155,"periodic_lift":[0,0],"sense":"Reverse"},"id":155,"loop_ref":38,"next":152,"prev":154,"to":38,"twin":137,"wire_ref":null},{"edge":82,"face":39,"from":41,"geometry_use":{"pcurve":156,"periodic_lift":[0,0],"sense":"Forward"},"id":156,"loop_ref":39,"next":157,"prev":159,"to":42,"twin":190,"wire_ref":null},{"edge":79,"face":39,"from":42,"geometry_use":{"pcurve":157,"periodic_lift":[0,0],"sense":"Reverse"},"id":157,"loop_ref":39,"next":158,"prev":156,"to":38,"twin":152,"wire_ref":null},{"edge":74,"face":39,"from":38,"geometry_use":{"pcurve":158,"periodic_lift":[0,0],"sense":"Reverse"},"id":158,"loop_ref":39,"next":159,"prev":157,"to":37,"twin":140,"wire_ref":null},{"edge":78,"face":39,"from":37,"geometry_use":{"pcurve":159,"periodic_lift":[0,0],"sense":"Forward"},"id":159,"loop_ref":39,"next":156,"prev":158,"to":41,"twin":150,"wire_ref":null},{"edge":83,"face":40,"from":44,"geometry_use":{"pcurve":160,"periodic_lift":[0,0],"sense":"Forward"},"id":160,"loop_ref":40,"next":161,"prev":163,"to":45,"twin":179,"wire_ref":null},{"edge":84,"face":40,"from":45,"geometry_use":{"pcurve":161,"periodic_lift":[0,0],"sense":"Reverse"},"id":161,"loop_ref":40,"next":162,"prev":160,"to":40,"twin":172,"wire_ref":null},{"edge":76,"face":40,"from":40,"geometry_use":{"pcurve":162,"periodic_lift":[0,0],"sense":"Reverse"},"id":162,"loop_ref":40,"next":163,"prev":161,"to":36,"twin":148,"wire_ref":null},{"edge":85,"face":40,"from":36,"geometry_use":{"pcurve":163,"periodic_lift":[0,0],"sense":"Forward"},"id":163,"loop_ref":40,"next":160,"prev":162,"to":44,"twin":170,"wire_ref":null},{"edge":81,"face":41,"from":39,"geometry_use":{"pcurve":164,"periodic_lift":[0,0],"sense":"Forward"},"id":164,"loop_ref":41,"next":165,"prev":167,"to":43,"twin":154,"wire_ref":null},{"edge":86,"face":41,"from":43,"geometry_use":{"pcurve":165,"periodic_lift":[0,0],"sense":"Forward"},"id":165,"loop_ref":41,"next":166,"prev":164,"to":46,"twin":174,"wire_ref":null},{"edge":87,"face":41,"from":46,"geometry_use":{"pcurve":166,"periodic_lift":[0,0],"sense":"Reverse"},"id":166,"loop_ref":41,"next":167,"prev":165,"to":47,"twin":177,"wire_ref":null},{"edge":88,"face":41,"from":47,"geometry_use":{"pcurve":167,"periodic_lift":[0,0],"sense":"Reverse"},"id":167,"loop_ref":41,"next":164,"prev":166,"to":39,"twin":168,"wire_ref":null},{"edge":88,"face":42,"from":39,"geometry_use":{"pcurve":168,"periodic_lift":[0,0],"sense":"Forward"},"id":168,"loop_ref":42,"next":169,"prev":171,"to":47,"twin":167,"wire_ref":null},{"edge":89,"face":42,"from":47,"geometry_use":{"pcurve":169,"periodic_lift":[0,0],"sense":"Reverse"},"id":169,"loop_ref":42,"next":170,"prev":168,"to":44,"twin":176,"wire_ref":null},{"edge":85,"face":42,"from":44,"geometry_use":{"pcurve":170,"periodic_lift":[0,0],"sense":"Reverse"},"id":170,"loop_ref":42,"next":171,"prev":169,"to":36,"twin":163,"wire_ref":null},{"edge":75,"face":42,"from":36,"geometry_use":{"pcurve":171,"periodic_lift":[0,0],"sense":"Forward"},"id":171,"loop_ref":42,"next":168,"prev":170,"to":39,"twin":146,"wire_ref":null},{"edge":84,"face":43,"from":40,"geometry_use":{"pcurve":172,"periodic_lift":[0,0],"sense":"Forward"},"id":172,"loop_ref":43,"next":173,"prev":175,"to":45,"twin":161,"wire_ref":null},{"edge":90,"face":43,"from":45,"geometry_use":{"pcurve":173,"periodic_lift":[0,0],"sense":"Forward"},"id":173,"loop_ref":43,"next":174,"prev":172,"to":46,"twin":178,"wire_ref":null},{"edge":86,"face":43,"from":46,"geometry_use":{"pcurve":174,"periodic_lift":[0,0],"sense":"Reverse"},"id":174,"loop_ref":43,"next":175,"prev":173,"to":43,"twin":165,"wire_ref":null},{"edge":91,"face":43,"from":43,"geometry_use":{"pcurve":175,"periodic_lift":[0,0],"sense":"Reverse"},"id":175,"loop_ref":43,"next":172,"prev":174,"to":40,"twin":192,"wire_ref":null},{"edge":89,"face":44,"from":44,"geometry_use":{"pcurve":176,"periodic_lift":[0,0],"sense":"Forward"},"id":176,"loop_ref":44,"next":177,"prev":179,"to":47,"twin":169,"wire_ref":null},{"edge":87,"face":44,"from":47,"geometry_use":{"pcurve":177,"periodic_lift":[0,0],"sense":"Forward"},"id":177,"loop_ref":44,"next":178,"prev":176,"to":46,"twin":166,"wire_ref":null},{"edge":90,"face":44,"from":46,"geometry_use":{"pcurve":178,"periodic_lift":[0,0],"sense":"Reverse"},"id":178,"loop_ref":44,"next":179,"prev":177,"to":45,"twin":173,"wire_ref":null},{"edge":83,"face":44,"from":45,"geometry_use":{"pcurve":179,"periodic_lift":[0,0],"sense":"Reverse"},"id":179,"loop_ref":44,"next":176,"prev":178,"to":44,"twin":160,"wire_ref":null},{"edge":92,"face":45,"from":40,"geometry_use":{"pcurve":180,"periodic_lift":[0,0],"sense":"Forward"},"id":180,"loop_ref":45,"next":181,"prev":183,"to":48,"twin":195,"wire_ref":null},{"edge":93,"face":45,"from":48,"geometry_use":{"pcurve":181,"periodic_lift":[0,0],"sense":"Reverse"},"id":181,"loop_ref":45,"next":182,"prev":180,"to":49,"twin":199,"wire_ref":null},{"edge":94,"face":45,"from":49,"geometry_use":{"pcurve":182,"periodic_lift":[0,0],"sense":"Reverse"},"id":182,"loop_ref":45,"next":183,"prev":181,"to":41,"twin":191,"wire_ref":null},{"edge":77,"face":45,"from":41,"geometry_use":{"pcurve":183,"periodic_lift":[0,0],"sense":"Forward"},"id":183,"loop_ref":45,"next":180,"prev":182,"to":40,"twin":149,"wire_ref":null},{"edge":95,"face":46,"from":42,"geometry_use":{"pcurve":184,"periodic_lift":[0,0],"sense":"Forward"},"id":184,"loop_ref":46,"next":185,"prev":187,"to":50,"twin":189,"wire_ref":null},{"edge":96,"face":46,"from":50,"geometry_use":{"pcurve":185,"periodic_lift":[0,0],"sense":"Forward"},"id":185,"loop_ref":46,"next":186,"prev":184,"to":51,"twin":203,"wire_ref":null},{"edge":97,"face":46,"from":51,"geometry_use":{"pcurve":186,"periodic_lift":[0,0],"sense":"Reverse"},"id":186,"loop_ref":46,"next":187,"prev":185,"to":43,"twin":193,"wire_ref":null},{"edge":80,"face":46,"from":43,"geometry_use":{"pcurve":187,"periodic_lift":[0,0],"sense":"Reverse"},"id":187,"loop_ref":46,"next":184,"prev":186,"to":42,"twin":153,"wire_ref":null},{"edge":98,"face":47,"from":49,"geometry_use":{"pcurve":188,"periodic_lift":[0,0],"sense":"Forward"},"id":188,"loop_ref":47,"next":189,"prev":191,"to":50,"twin":206,"wire_ref":null},{"edge":95,"face":47,"from":50,"geometry_use":{"pcurve":189,"periodic_lift":[0,0],"sense":"Reverse"},"id":189,"loop_ref":47,"next":190,"prev":188,"to":42,"twin":184,"wire_ref":null},{"edge":82,"face":47,"from":42,"geometry_use":{"pcurve":190,"periodic_lift":[0,0],"sense":"Reverse"},"id":190,"loop_ref":47,"next":191,"prev":189,"to":41,"twin":156,"wire_ref":null},{"edge":94,"face":47,"from":41,"geometry_use":{"pcurve":191,"periodic_lift":[0,0],"sense":"Forward"},"id":191,"loop_ref":47,"next":188,"prev":190,"to":49,"twin":182,"wire_ref":null},{"edge":91,"face":48,"from":40,"geometry_use":{"pcurve":192,"periodic_lift":[0,0],"sense":"Forward"},"id":192,"loop_ref":48,"next":193,"prev":195,"to":43,"twin":175,"wire_ref":null},{"edge":97,"face":48,"from":43,"geometry_use":{"pcurve":193,"periodic_lift":[0,0],"sense":"Forward"},"id":193,"loop_ref":48,"next":194,"prev":192,"to":51,"twin":186,"wire_ref":null},{"edge":99,"face":48,"from":51,"geometry_use":{"pcurve":194,"periodic_lift":[0,0],"sense":"Reverse"},"id":194,"loop_ref":48,"next":195,"prev":193,"to":48,"twin":219,"wire_ref":null},{"edge":92,"face":48,"from":48,"geometry_use":{"pcurve":195,"periodic_lift":[0,0],"sense":"Reverse"},"id":195,"loop_ref":48,"next":192,"prev":194,"to":40,"twin":180,"wire_ref":null},{"edge":100,"face":49,"from":48,"geometry_use":{"pcurve":196,"periodic_lift":[0,0],"sense":"Forward"},"id":196,"loop_ref":49,"next":197,"prev":199,"to":52,"twin":210,"wire_ref":null},{"edge":101,"face":49,"from":52,"geometry_use":{"pcurve":197,"periodic_lift":[0,0],"sense":"Reverse"},"id":197,"loop_ref":49,"next":198,"prev":196,"to":53,"twin":231,"wire_ref":null},{"edge":102,"face":49,"from":53,"geometry_use":{"pcurve":198,"periodic_lift":[0,0],"sense":"Reverse"},"id":198,"loop_ref":49,"next":199,"prev":197,"to":49,"twin":207,"wire_ref":null},{"edge":93,"face":49,"from":49,"geometry_use":{"pcurve":199,"periodic_lift":[0,0],"sense":"Forward"},"id":199,"loop_ref":49,"next":196,"prev":198,"to":48,"twin":181,"wire_ref":null},{"edge":103,"face":50,"from":50,"geometry_use":{"pcurve":200,"periodic_lift":[0,0],"sense":"Forward"},"id":200,"loop_ref":50,"next":201,"prev":203,"to":54,"twin":205,"wire_ref":null},{"edge":104,"face":50,"from":54,"geometry_use":{"pcurve":201,"periodic_lift":[0,0],"sense":"Forward"},"id":201,"loop_ref":50,"next":202,"prev":200,"to":55,"twin":235,"wire_ref":null},{"edge":105,"face":50,"from":55,"geometry_use":{"pcurve":202,"periodic_lift":[0,0],"sense":"Reverse"},"id":202,"loop_ref":50,"next":203,"prev":201,"to":51,"twin":212,"wire_ref":null},{"edge":96,"face":50,"from":51,"geometry_use":{"pcurve":203,"periodic_lift":[0,0],"sense":"Reverse"},"id":203,"loop_ref":50,"next":200,"prev":202,"to":50,"twin":185,"wire_ref":null},{"edge":106,"face":51,"from":53,"geometry_use":{"pcurve":204,"periodic_lift":[0,0],"sense":"Forward"},"id":204,"loop_ref":51,"next":205,"prev":207,"to":54,"twin":238,"wire_ref":null},{"edge":103,"face":51,"from":54,"geometry_use":{"pcurve":205,"periodic_lift":[0,0],"sense":"Reverse"},"id":205,"loop_ref":51,"next":206,"prev":204,"to":50,"twin":200,"wire_ref":null},{"edge":98,"face":51,"from":50,"geometry_use":{"pcurve":206,"periodic_lift":[0,0],"sense":"Reverse"},"id":206,"loop_ref":51,"next":207,"prev":205,"to":49,"twin":188,"wire_ref":null},{"edge":102,"face":51,"from":49,"geometry_use":{"pcurve":207,"periodic_lift":[0,0],"sense":"Forward"},"id":207,"loop_ref":51,"next":204,"prev":206,"to":53,"twin":198,"wire_ref":null},{"edge":107,"face":52,"from":56,"geometry_use":{"pcurve":208,"periodic_lift":[0,0],"sense":"Forward"},"id":208,"loop_ref":52,"next":209,"prev":211,"to":57,"twin":227,"wire_ref":null},{"edge":108,"face":52,"from":57,"geometry_use":{"pcurve":209,"periodic_lift":[0,0],"sense":"Reverse"},"id":209,"loop_ref":52,"next":210,"prev":208,"to":52,"twin":220,"wire_ref":null},{"edge":100,"face":52,"from":52,"geometry_use":{"pcurve":210,"periodic_lift":[0,0],"sense":"Reverse"},"id":210,"loop_ref":52,"next":211,"prev":209,"to":48,"twin":196,"wire_ref":null},{"edge":109,"face":52,"from":48,"geometry_use":{"pcurve":211,"periodic_lift":[0,0],"sense":"Forward"},"id":211,"loop_ref":52,"next":208,"prev":210,"to":56,"twin":218,"wire_ref":null},{"edge":105,"face":53,"from":51,"geometry_use":{"pcurve":212,"periodic_lift":[0,0],"sense":"Forward"},"id":212,"loop_ref":53,"next":213,"prev":215,"to":55,"twin":202,"wire_ref":null},{"edge":110,"face":53,"from":55,"geometry_use":{"pcurve":213,"periodic_lift":[0,0],"sense":"Forward"},"id":213,"loop_ref":53,"next":214,"prev":212,"to":58,"twin":222,"wire_ref":null},{"edge":111,"face":53,"from":58,"geometry_use":{"pcurve":214,"periodic_lift":[0,0],"sense":"Reverse"},"id":214,"loop_ref":53,"next":215,"prev":213,"to":59,"twin":225,"wire_ref":null},{"edge":112,"face":53,"from":59,"geometry_use":{"pcurve":215,"periodic_lift":[0,0],"sense":"Reverse"},"id":215,"loop_ref":53,"next":212,"prev":214,"to":51,"twin":216,"wire_ref":null},{"edge":112,"face":54,"from":51,"geometry_use":{"pcurve":216,"periodic_lift":[0,0],"sense":"Forward"},"id":216,"loop_ref":54,"next":217,"prev":219,"to":59,"twin":215,"wire_ref":null},{"edge":113,"face":54,"from":59,"geometry_use":{"pcurve":217,"periodic_lift":[0,0],"sense":"Reverse"},"id":217,"loop_ref":54,"next":218,"prev":216,"to":56,"twin":224,"wire_ref":null},{"edge":109,"face":54,"from":56,"geometry_use":{"pcurve":218,"periodic_lift":[0,0],"sense":"Reverse"},"id":218,"loop_ref":54,"next":219,"prev":217,"to":48,"twin":211,"wire_ref":null},{"edge":99,"face":54,"from":48,"geometry_use":{"pcurve":219,"periodic_lift":[0,0],"sense":"Forward"},"id":219,"loop_ref":54,"next":216,"prev":218,"to":51,"twin":194,"wire_ref":null},{"edge":108,"face":55,"from":52,"geometry_use":{"pcurve":220,"periodic_lift":[0,0],"sense":"Forward"},"id":220,"loop_ref":55,"next":221,"prev":223,"to":57,"twin":209,"wire_ref":null},{"edge":114,"face":55,"from":57,"geometry_use":{"pcurve":221,"periodic_lift":[0,0],"sense":"Forward"},"id":221,"loop_ref":55,"next":222,"prev":220,"to":58,"twin":226,"wire_ref":null},{"edge":110,"face":55,"from":58,"geometry_use":{"pcurve":222,"periodic_lift":[0,0],"sense":"Reverse"},"id":222,"loop_ref":55,"next":223,"prev":221,"to":55,"twin":213,"wire_ref":null},{"edge":115,"face":55,"from":55,"geometry_use":{"pcurve":223,"periodic_lift":[0,0],"sense":"Reverse"},"id":223,"loop_ref":55,"next":220,"prev":222,"to":52,"twin":240,"wire_ref":null},{"edge":113,"face":56,"from":56,"geometry_use":{"pcurve":224,"periodic_lift":[0,0],"sense":"Forward"},"id":224,"loop_ref":56,"next":225,"prev":227,"to":59,"twin":217,"wire_ref":null},{"edge":111,"face":56,"from":59,"geometry_use":{"pcurve":225,"periodic_lift":[0,0],"sense":"Forward"},"id":225,"loop_ref":56,"next":226,"prev":224,"to":58,"twin":214,"wire_ref":null},{"edge":114,"face":56,"from":58,"geometry_use":{"pcurve":226,"periodic_lift":[0,0],"sense":"Reverse"},"id":226,"loop_ref":56,"next":227,"prev":225,"to":57,"twin":221,"wire_ref":null},{"edge":107,"face":56,"from":57,"geometry_use":{"pcurve":227,"periodic_lift":[0,0],"sense":"Reverse"},"id":227,"loop_ref":56,"next":224,"prev":226,"to":56,"twin":208,"wire_ref":null},{"edge":116,"face":57,"from":52,"geometry_use":{"pcurve":228,"periodic_lift":[0,0],"sense":"Forward"},"id":228,"loop_ref":57,"next":229,"prev":231,"to":60,"twin":243,"wire_ref":null},{"edge":117,"face":57,"from":60,"geometry_use":{"pcurve":229,"periodic_lift":[0,0],"sense":"Reverse"},"id":229,"loop_ref":57,"next":230,"prev":228,"to":61,"twin":247,"wire_ref":null},{"edge":118,"face":57,"from":61,"geometry_use":{"pcurve":230,"periodic_lift":[0,0],"sense":"Reverse"},"id":230,"loop_ref":57,"next":231,"prev":229,"to":53,"twin":239,"wire_ref":null},{"edge":101,"face":57,"from":53,"geometry_use":{"pcurve":231,"periodic_lift":[0,0],"sense":"Forward"},"id":231,"loop_ref":57,"next":228,"prev":230,"to":52,"twin":197,"wire_ref":null},{"edge":119,"face":58,"from":54,"geometry_use":{"pcurve":232,"periodic_lift":[0,0],"sense":"Forward"},"id":232,"loop_ref":58,"next":233,"prev":235,"to":62,"twin":237,"wire_ref":null},{"edge":120,"face":58,"from":62,"geometry_use":{"pcurve":233,"periodic_lift":[0,0],"sense":"Forward"},"id":233,"loop_ref":58,"next":234,"prev":232,"to":63,"twin":251,"wire_ref":null},{"edge":121,"face":58,"from":63,"geometry_use":{"pcurve":234,"periodic_lift":[0,0],"sense":"Reverse"},"id":234,"loop_ref":58,"next":235,"prev":233,"to":55,"twin":241,"wire_ref":null},{"edge":104,"face":58,"from":55,"geometry_use":{"pcurve":235,"periodic_lift":[0,0],"sense":"Reverse"},"id":235,"loop_ref":58,"next":232,"prev":234,"to":54,"twin":201,"wire_ref":null},{"edge":122,"face":59,"from":61,"geometry_use":{"pcurve":236,"periodic_lift":[0,0],"sense":"Forward"},"id":236,"loop_ref":59,"next":237,"prev":239,"to":62,"twin":254,"wire_ref":null},{"edge":119,"face":59,"from":62,"geometry_use":{"pcurve":237,"periodic_lift":[0,0],"sense":"Reverse"},"id":237,"loop_ref":59,"next":238,"prev":236,"to":54,"twin":232,"wire_ref":null},{"edge":106,"face":59,"from":54,"geometry_use":{"pcurve":238,"periodic_lift":[0,0],"sense":"Reverse"},"id":238,"loop_ref":59,"next":239,"prev":237,"to":53,"twin":204,"wire_ref":null},{"edge":118,"face":59,"from":53,"geometry_use":{"pcurve":239,"periodic_lift":[0,0],"sense":"Forward"},"id":239,"loop_ref":59,"next":236,"prev":238,"to":61,"twin":230,"wire_ref":null},{"edge":115,"face":60,"from":52,"geometry_use":{"pcurve":240,"periodic_lift":[0,0],"sense":"Forward"},"id":240,"loop_ref":60,"next":241,"prev":243,"to":55,"twin":223,"wire_ref":null},{"edge":121,"face":60,"from":55,"geometry_use":{"pcurve":241,"periodic_lift":[0,0],"sense":"Forward"},"id":241,"loop_ref":60,"next":242,"prev":240,"to":63,"twin":234,"wire_ref":null},{"edge":123,"face":60,"from":63,"geometry_use":{"pcurve":242,"periodic_lift":[0,0],"sense":"Reverse"},"id":242,"loop_ref":60,"next":243,"prev":241,"to":60,"twin":267,"wire_ref":null},{"edge":116,"face":60,"from":60,"geometry_use":{"pcurve":243,"periodic_lift":[0,0],"sense":"Reverse"},"id":243,"loop_ref":60,"next":240,"prev":242,"to":52,"twin":228,"wire_ref":null},{"edge":124,"face":61,"from":60,"geometry_use":{"pcurve":244,"periodic_lift":[0,0],"sense":"Forward"},"id":244,"loop_ref":61,"next":245,"prev":247,"to":64,"twin":258,"wire_ref":null},{"edge":125,"face":61,"from":64,"geometry_use":{"pcurve":245,"periodic_lift":[0,0],"sense":"Reverse"},"id":245,"loop_ref":61,"next":246,"prev":244,"to":65,"twin":279,"wire_ref":null},{"edge":126,"face":61,"from":65,"geometry_use":{"pcurve":246,"periodic_lift":[0,0],"sense":"Reverse"},"id":246,"loop_ref":61,"next":247,"prev":245,"to":61,"twin":255,"wire_ref":null},{"edge":117,"face":61,"from":61,"geometry_use":{"pcurve":247,"periodic_lift":[0,0],"sense":"Forward"},"id":247,"loop_ref":61,"next":244,"prev":246,"to":60,"twin":229,"wire_ref":null},{"edge":127,"face":62,"from":62,"geometry_use":{"pcurve":248,"periodic_lift":[0,0],"sense":"Forward"},"id":248,"loop_ref":62,"next":249,"prev":251,"to":66,"twin":253,"wire_ref":null},{"edge":128,"face":62,"from":66,"geometry_use":{"pcurve":249,"periodic_lift":[0,0],"sense":"Forward"},"id":249,"loop_ref":62,"next":250,"prev":248,"to":67,"twin":283,"wire_ref":null},{"edge":129,"face":62,"from":67,"geometry_use":{"pcurve":250,"periodic_lift":[0,0],"sense":"Reverse"},"id":250,"loop_ref":62,"next":251,"prev":249,"to":63,"twin":260,"wire_ref":null},{"edge":120,"face":62,"from":63,"geometry_use":{"pcurve":251,"periodic_lift":[0,0],"sense":"Reverse"},"id":251,"loop_ref":62,"next":248,"prev":250,"to":62,"twin":233,"wire_ref":null},{"edge":130,"face":63,"from":65,"geometry_use":{"pcurve":252,"periodic_lift":[0,0],"sense":"Forward"},"id":252,"loop_ref":63,"next":253,"prev":255,"to":66,"twin":286,"wire_ref":null},{"edge":127,"face":63,"from":66,"geometry_use":{"pcurve":253,"periodic_lift":[0,0],"sense":"Reverse"},"id":253,"loop_ref":63,"next":254,"prev":252,"to":62,"twin":248,"wire_ref":null},{"edge":122,"face":63,"from":62,"geometry_use":{"pcurve":254,"periodic_lift":[0,0],"sense":"Reverse"},"id":254,"loop_ref":63,"next":255,"prev":253,"to":61,"twin":236,"wire_ref":null},{"edge":126,"face":63,"from":61,"geometry_use":{"pcurve":255,"periodic_lift":[0,0],"sense":"Forward"},"id":255,"loop_ref":63,"next":252,"prev":254,"to":65,"twin":246,"wire_ref":null},{"edge":131,"face":64,"from":68,"geometry_use":{"pcurve":256,"periodic_lift":[0,0],"sense":"Forward"},"id":256,"loop_ref":64,"next":257,"prev":259,"to":69,"twin":275,"wire_ref":null},{"edge":132,"face":64,"from":69,"geometry_use":{"pcurve":257,"periodic_lift":[0,0],"sense":"Reverse"},"id":257,"loop_ref":64,"next":258,"prev":256,"to":64,"twin":268,"wire_ref":null},{"edge":124,"face":64,"from":64,"geometry_use":{"pcurve":258,"periodic_lift":[0,0],"sense":"Reverse"},"id":258,"loop_ref":64,"next":259,"prev":257,"to":60,"twin":244,"wire_ref":null},{"edge":133,"face":64,"from":60,"geometry_use":{"pcurve":259,"periodic_lift":[0,0],"sense":"Forward"},"id":259,"loop_ref":64,"next":256,"prev":258,"to":68,"twin":266,"wire_ref":null},{"edge":129,"face":65,"from":63,"geometry_use":{"pcurve":260,"periodic_lift":[0,0],"sense":"Forward"},"id":260,"loop_ref":65,"next":261,"prev":263,"to":67,"twin":250,"wire_ref":null},{"edge":134,"face":65,"from":67,"geometry_use":{"pcurve":261,"periodic_lift":[0,0],"sense":"Forward"},"id":261,"loop_ref":65,"next":262,"prev":260,"to":70,"twin":270,"wire_ref":null},{"edge":135,"face":65,"from":70,"geometry_use":{"pcurve":262,"periodic_lift":[0,0],"sense":"Reverse"},"id":262,"loop_ref":65,"next":263,"prev":261,"to":71,"twin":273,"wire_ref":null},{"edge":136,"face":65,"from":71,"geometry_use":{"pcurve":263,"periodic_lift":[0,0],"sense":"Reverse"},"id":263,"loop_ref":65,"next":260,"prev":262,"to":63,"twin":264,"wire_ref":null},{"edge":136,"face":66,"from":63,"geometry_use":{"pcurve":264,"periodic_lift":[0,0],"sense":"Forward"},"id":264,"loop_ref":66,"next":265,"prev":267,"to":71,"twin":263,"wire_ref":null},{"edge":137,"face":66,"from":71,"geometry_use":{"pcurve":265,"periodic_lift":[0,0],"sense":"Reverse"},"id":265,"loop_ref":66,"next":266,"prev":264,"to":68,"twin":272,"wire_ref":null},{"edge":133,"face":66,"from":68,"geometry_use":{"pcurve":266,"periodic_lift":[0,0],"sense":"Reverse"},"id":266,"loop_ref":66,"next":267,"prev":265,"to":60,"twin":259,"wire_ref":null},{"edge":123,"face":66,"from":60,"geometry_use":{"pcurve":267,"periodic_lift":[0,0],"sense":"Forward"},"id":267,"loop_ref":66,"next":264,"prev":266,"to":63,"twin":242,"wire_ref":null},{"edge":132,"face":67,"from":64,"geometry_use":{"pcurve":268,"periodic_lift":[0,0],"sense":"Forward"},"id":268,"loop_ref":67,"next":269,"prev":271,"to":69,"twin":257,"wire_ref":null},{"edge":138,"face":67,"from":69,"geometry_use":{"pcurve":269,"periodic_lift":[0,0],"sense":"Forward"},"id":269,"loop_ref":67,"next":270,"prev":268,"to":70,"twin":274,"wire_ref":null},{"edge":134,"face":67,"from":70,"geometry_use":{"pcurve":270,"periodic_lift":[0,0],"sense":"Reverse"},"id":270,"loop_ref":67,"next":271,"prev":269,"to":67,"twin":261,"wire_ref":null},{"edge":139,"face":67,"from":67,"geometry_use":{"pcurve":271,"periodic_lift":[0,0],"sense":"Reverse"},"id":271,"loop_ref":67,"next":268,"prev":270,"to":64,"twin":288,"wire_ref":null},{"edge":137,"face":68,"from":68,"geometry_use":{"pcurve":272,"periodic_lift":[0,0],"sense":"Forward"},"id":272,"loop_ref":68,"next":273,"prev":275,"to":71,"twin":265,"wire_ref":null},{"edge":135,"face":68,"from":71,"geometry_use":{"pcurve":273,"periodic_lift":[0,0],"sense":"Forward"},"id":273,"loop_ref":68,"next":274,"prev":272,"to":70,"twin":262,"wire_ref":null},{"edge":138,"face":68,"from":70,"geometry_use":{"pcurve":274,"periodic_lift":[0,0],"sense":"Reverse"},"id":274,"loop_ref":68,"next":275,"prev":273,"to":69,"twin":269,"wire_ref":null},{"edge":131,"face":68,"from":69,"geometry_use":{"pcurve":275,"periodic_lift":[0,0],"sense":"Reverse"},"id":275,"loop_ref":68,"next":272,"prev":274,"to":68,"twin":256,"wire_ref":null},{"edge":140,"face":69,"from":64,"geometry_use":{"pcurve":276,"periodic_lift":[0,0],"sense":"Forward"},"id":276,"loop_ref":69,"next":277,"prev":279,"to":72,"twin":291,"wire_ref":null},{"edge":141,"face":69,"from":72,"geometry_use":{"pcurve":277,"periodic_lift":[0,0],"sense":"Reverse"},"id":277,"loop_ref":69,"next":278,"prev":276,"to":73,"twin":295,"wire_ref":null},{"edge":142,"face":69,"from":73,"geometry_use":{"pcurve":278,"periodic_lift":[0,0],"sense":"Reverse"},"id":278,"loop_ref":69,"next":279,"prev":277,"to":65,"twin":287,"wire_ref":null},{"edge":125,"face":69,"from":65,"geometry_use":{"pcurve":279,"periodic_lift":[0,0],"sense":"Forward"},"id":279,"loop_ref":69,"next":276,"prev":278,"to":64,"twin":245,"wire_ref":null},{"edge":143,"face":70,"from":66,"geometry_use":{"pcurve":280,"periodic_lift":[0,0],"sense":"Forward"},"id":280,"loop_ref":70,"next":281,"prev":283,"to":74,"twin":285,"wire_ref":null},{"edge":144,"face":70,"from":74,"geometry_use":{"pcurve":281,"periodic_lift":[0,0],"sense":"Forward"},"id":281,"loop_ref":70,"next":282,"prev":280,"to":75,"twin":299,"wire_ref":null},{"edge":145,"face":70,"from":75,"geometry_use":{"pcurve":282,"periodic_lift":[0,0],"sense":"Reverse"},"id":282,"loop_ref":70,"next":283,"prev":281,"to":67,"twin":289,"wire_ref":null},{"edge":128,"face":70,"from":67,"geometry_use":{"pcurve":283,"periodic_lift":[0,0],"sense":"Reverse"},"id":283,"loop_ref":70,"next":280,"prev":282,"to":66,"twin":249,"wire_ref":null},{"edge":146,"face":71,"from":73,"geometry_use":{"pcurve":284,"periodic_lift":[0,0],"sense":"Forward"},"id":284,"loop_ref":71,"next":285,"prev":287,"to":74,"twin":302,"wire_ref":null},{"edge":143,"face":71,"from":74,"geometry_use":{"pcurve":285,"periodic_lift":[0,0],"sense":"Reverse"},"id":285,"loop_ref":71,"next":286,"prev":284,"to":66,"twin":280,"wire_ref":null},{"edge":130,"face":71,"from":66,"geometry_use":{"pcurve":286,"periodic_lift":[0,0],"sense":"Reverse"},"id":286,"loop_ref":71,"next":287,"prev":285,"to":65,"twin":252,"wire_ref":null},{"edge":142,"face":71,"from":65,"geometry_use":{"pcurve":287,"periodic_lift":[0,0],"sense":"Forward"},"id":287,"loop_ref":71,"next":284,"prev":286,"to":73,"twin":278,"wire_ref":null},{"edge":139,"face":72,"from":64,"geometry_use":{"pcurve":288,"periodic_lift":[0,0],"sense":"Forward"},"id":288,"loop_ref":72,"next":289,"prev":291,"to":67,"twin":271,"wire_ref":null},{"edge":145,"face":72,"from":67,"geometry_use":{"pcurve":289,"periodic_lift":[0,0],"sense":"Forward"},"id":289,"loop_ref":72,"next":290,"prev":288,"to":75,"twin":282,"wire_ref":null},{"edge":147,"face":72,"from":75,"geometry_use":{"pcurve":290,"periodic_lift":[0,0],"sense":"Reverse"},"id":290,"loop_ref":72,"next":291,"prev":289,"to":72,"twin":315,"wire_ref":null},{"edge":140,"face":72,"from":72,"geometry_use":{"pcurve":291,"periodic_lift":[0,0],"sense":"Reverse"},"id":291,"loop_ref":72,"next":288,"prev":290,"to":64,"twin":276,"wire_ref":null},{"edge":148,"face":73,"from":72,"geometry_use":{"pcurve":292,"periodic_lift":[0,0],"sense":"Forward"},"id":292,"loop_ref":73,"next":293,"prev":295,"to":76,"twin":306,"wire_ref":null},{"edge":149,"face":73,"from":76,"geometry_use":{"pcurve":293,"periodic_lift":[0,0],"sense":"Reverse"},"id":293,"loop_ref":73,"next":294,"prev":292,"to":77,"twin":327,"wire_ref":null},{"edge":150,"face":73,"from":77,"geometry_use":{"pcurve":294,"periodic_lift":[0,0],"sense":"Reverse"},"id":294,"loop_ref":73,"next":295,"prev":293,"to":73,"twin":303,"wire_ref":null},{"edge":141,"face":73,"from":73,"geometry_use":{"pcurve":295,"periodic_lift":[0,0],"sense":"Forward"},"id":295,"loop_ref":73,"next":292,"prev":294,"to":72,"twin":277,"wire_ref":null},{"edge":151,"face":74,"from":74,"geometry_use":{"pcurve":296,"periodic_lift":[0,0],"sense":"Forward"},"id":296,"loop_ref":74,"next":297,"prev":299,"to":78,"twin":301,"wire_ref":null},{"edge":152,"face":74,"from":78,"geometry_use":{"pcurve":297,"periodic_lift":[0,0],"sense":"Forward"},"id":297,"loop_ref":74,"next":298,"prev":296,"to":79,"twin":331,"wire_ref":null},{"edge":153,"face":74,"from":79,"geometry_use":{"pcurve":298,"periodic_lift":[0,0],"sense":"Reverse"},"id":298,"loop_ref":74,"next":299,"prev":297,"to":75,"twin":308,"wire_ref":null},{"edge":144,"face":74,"from":75,"geometry_use":{"pcurve":299,"periodic_lift":[0,0],"sense":"Reverse"},"id":299,"loop_ref":74,"next":296,"prev":298,"to":74,"twin":281,"wire_ref":null},{"edge":154,"face":75,"from":77,"geometry_use":{"pcurve":300,"periodic_lift":[0,0],"sense":"Forward"},"id":300,"loop_ref":75,"next":301,"prev":303,"to":78,"twin":334,"wire_ref":null},{"edge":151,"face":75,"from":78,"geometry_use":{"pcurve":301,"periodic_lift":[0,0],"sense":"Reverse"},"id":301,"loop_ref":75,"next":302,"prev":300,"to":74,"twin":296,"wire_ref":null},{"edge":146,"face":75,"from":74,"geometry_use":{"pcurve":302,"periodic_lift":[0,0],"sense":"Reverse"},"id":302,"loop_ref":75,"next":303,"prev":301,"to":73,"twin":284,"wire_ref":null},{"edge":150,"face":75,"from":73,"geometry_use":{"pcurve":303,"periodic_lift":[0,0],"sense":"Forward"},"id":303,"loop_ref":75,"next":300,"prev":302,"to":77,"twin":294,"wire_ref":null},{"edge":155,"face":76,"from":80,"geometry_use":{"pcurve":304,"periodic_lift":[0,0],"sense":"Forward"},"id":304,"loop_ref":76,"next":305,"prev":307,"to":81,"twin":323,"wire_ref":null},{"edge":156,"face":76,"from":81,"geometry_use":{"pcurve":305,"periodic_lift":[0,0],"sense":"Reverse"},"id":305,"loop_ref":76,"next":306,"prev":304,"to":76,"twin":316,"wire_ref":null},{"edge":148,"face":76,"from":76,"geometry_use":{"pcurve":306,"periodic_lift":[0,0],"sense":"Reverse"},"id":306,"loop_ref":76,"next":307,"prev":305,"to":72,"twin":292,"wire_ref":null},{"edge":157,"face":76,"from":72,"geometry_use":{"pcurve":307,"periodic_lift":[0,0],"sense":"Forward"},"id":307,"loop_ref":76,"next":304,"prev":306,"to":80,"twin":314,"wire_ref":null},{"edge":153,"face":77,"from":75,"geometry_use":{"pcurve":308,"periodic_lift":[0,0],"sense":"Forward"},"id":308,"loop_ref":77,"next":309,"prev":311,"to":79,"twin":298,"wire_ref":null},{"edge":158,"face":77,"from":79,"geometry_use":{"pcurve":309,"periodic_lift":[0,0],"sense":"Forward"},"id":309,"loop_ref":77,"next":310,"prev":308,"to":82,"twin":318,"wire_ref":null},{"edge":159,"face":77,"from":82,"geometry_use":{"pcurve":310,"periodic_lift":[0,0],"sense":"Reverse"},"id":310,"loop_ref":77,"next":311,"prev":309,"to":83,"twin":321,"wire_ref":null},{"edge":160,"face":77,"from":83,"geometry_use":{"pcurve":311,"periodic_lift":[0,0],"sense":"Reverse"},"id":311,"loop_ref":77,"next":308,"prev":310,"to":75,"twin":312,"wire_ref":null},{"edge":160,"face":78,"from":75,"geometry_use":{"pcurve":312,"periodic_lift":[0,0],"sense":"Forward"},"id":312,"loop_ref":78,"next":313,"prev":315,"to":83,"twin":311,"wire_ref":null},{"edge":161,"face":78,"from":83,"geometry_use":{"pcurve":313,"periodic_lift":[0,0],"sense":"Reverse"},"id":313,"loop_ref":78,"next":314,"prev":312,"to":80,"twin":320,"wire_ref":null},{"edge":157,"face":78,"from":80,"geometry_use":{"pcurve":314,"periodic_lift":[0,0],"sense":"Reverse"},"id":314,"loop_ref":78,"next":315,"prev":313,"to":72,"twin":307,"wire_ref":null},{"edge":147,"face":78,"from":72,"geometry_use":{"pcurve":315,"periodic_lift":[0,0],"sense":"Forward"},"id":315,"loop_ref":78,"next":312,"prev":314,"to":75,"twin":290,"wire_ref":null},{"edge":156,"face":79,"from":76,"geometry_use":{"pcurve":316,"periodic_lift":[0,0],"sense":"Forward"},"id":316,"loop_ref":79,"next":317,"prev":319,"to":81,"twin":305,"wire_ref":null},{"edge":162,"face":79,"from":81,"geometry_use":{"pcurve":317,"periodic_lift":[0,0],"sense":"Forward"},"id":317,"loop_ref":79,"next":318,"prev":316,"to":82,"twin":322,"wire_ref":null},{"edge":158,"face":79,"from":82,"geometry_use":{"pcurve":318,"periodic_lift":[0,0],"sense":"Reverse"},"id":318,"loop_ref":79,"next":319,"prev":317,"to":79,"twin":309,"wire_ref":null},{"edge":163,"face":79,"from":79,"geometry_use":{"pcurve":319,"periodic_lift":[0,0],"sense":"Reverse"},"id":319,"loop_ref":79,"next":316,"prev":318,"to":76,"twin":336,"wire_ref":null},{"edge":161,"face":80,"from":80,"geometry_use":{"pcurve":320,"periodic_lift":[0,0],"sense":"Forward"},"id":320,"loop_ref":80,"next":321,"prev":323,"to":83,"twin":313,"wire_ref":null},{"edge":159,"face":80,"from":83,"geometry_use":{"pcurve":321,"periodic_lift":[0,0],"sense":"Forward"},"id":321,"loop_ref":80,"next":322,"prev":320,"to":82,"twin":310,"wire_ref":null},{"edge":162,"face":80,"from":82,"geometry_use":{"pcurve":322,"periodic_lift":[0,0],"sense":"Reverse"},"id":322,"loop_ref":80,"next":323,"prev":321,"to":81,"twin":317,"wire_ref":null},{"edge":155,"face":80,"from":81,"geometry_use":{"pcurve":323,"periodic_lift":[0,0],"sense":"Reverse"},"id":323,"loop_ref":80,"next":320,"prev":322,"to":80,"twin":304,"wire_ref":null},{"edge":164,"face":81,"from":76,"geometry_use":{"pcurve":324,"periodic_lift":[0,0],"sense":"Forward"},"id":324,"loop_ref":81,"next":325,"prev":327,"to":84,"twin":339,"wire_ref":null},{"edge":165,"face":81,"from":84,"geometry_use":{"pcurve":325,"periodic_lift":[0,0],"sense":"Reverse"},"id":325,"loop_ref":81,"next":326,"prev":324,"to":85,"twin":343,"wire_ref":null},{"edge":166,"face":81,"from":85,"geometry_use":{"pcurve":326,"periodic_lift":[0,0],"sense":"Reverse"},"id":326,"loop_ref":81,"next":327,"prev":325,"to":77,"twin":335,"wire_ref":null},{"edge":149,"face":81,"from":77,"geometry_use":{"pcurve":327,"periodic_lift":[0,0],"sense":"Forward"},"id":327,"loop_ref":81,"next":324,"prev":326,"to":76,"twin":293,"wire_ref":null},{"edge":167,"face":82,"from":78,"geometry_use":{"pcurve":328,"periodic_lift":[0,0],"sense":"Forward"},"id":328,"loop_ref":82,"next":329,"prev":331,"to":86,"twin":333,"wire_ref":null},{"edge":168,"face":82,"from":86,"geometry_use":{"pcurve":329,"periodic_lift":[0,0],"sense":"Forward"},"id":329,"loop_ref":82,"next":330,"prev":328,"to":87,"twin":347,"wire_ref":null},{"edge":169,"face":82,"from":87,"geometry_use":{"pcurve":330,"periodic_lift":[0,0],"sense":"Reverse"},"id":330,"loop_ref":82,"next":331,"prev":329,"to":79,"twin":337,"wire_ref":null},{"edge":152,"face":82,"from":79,"geometry_use":{"pcurve":331,"periodic_lift":[0,0],"sense":"Reverse"},"id":331,"loop_ref":82,"next":328,"prev":330,"to":78,"twin":297,"wire_ref":null},{"edge":170,"face":83,"from":85,"geometry_use":{"pcurve":332,"periodic_lift":[0,0],"sense":"Forward"},"id":332,"loop_ref":83,"next":333,"prev":335,"to":86,"twin":350,"wire_ref":null},{"edge":167,"face":83,"from":86,"geometry_use":{"pcurve":333,"periodic_lift":[0,0],"sense":"Reverse"},"id":333,"loop_ref":83,"next":334,"prev":332,"to":78,"twin":328,"wire_ref":null},{"edge":154,"face":83,"from":78,"geometry_use":{"pcurve":334,"periodic_lift":[0,0],"sense":"Reverse"},"id":334,"loop_ref":83,"next":335,"prev":333,"to":77,"twin":300,"wire_ref":null},{"edge":166,"face":83,"from":77,"geometry_use":{"pcurve":335,"periodic_lift":[0,0],"sense":"Forward"},"id":335,"loop_ref":83,"next":332,"prev":334,"to":85,"twin":326,"wire_ref":null},{"edge":163,"face":84,"from":76,"geometry_use":{"pcurve":336,"periodic_lift":[0,0],"sense":"Forward"},"id":336,"loop_ref":84,"next":337,"prev":339,"to":79,"twin":319,"wire_ref":null},{"edge":169,"face":84,"from":79,"geometry_use":{"pcurve":337,"periodic_lift":[0,0],"sense":"Forward"},"id":337,"loop_ref":84,"next":338,"prev":336,"to":87,"twin":330,"wire_ref":null},{"edge":171,"face":84,"from":87,"geometry_use":{"pcurve":338,"periodic_lift":[0,0],"sense":"Reverse"},"id":338,"loop_ref":84,"next":339,"prev":337,"to":84,"twin":363,"wire_ref":null},{"edge":164,"face":84,"from":84,"geometry_use":{"pcurve":339,"periodic_lift":[0,0],"sense":"Reverse"},"id":339,"loop_ref":84,"next":336,"prev":338,"to":76,"twin":324,"wire_ref":null},{"edge":172,"face":85,"from":84,"geometry_use":{"pcurve":340,"periodic_lift":[0,0],"sense":"Forward"},"id":340,"loop_ref":85,"next":341,"prev":343,"to":88,"twin":354,"wire_ref":null},{"edge":173,"face":85,"from":88,"geometry_use":{"pcurve":341,"periodic_lift":[0,0],"sense":"Reverse"},"id":341,"loop_ref":85,"next":342,"prev":340,"to":89,"twin":375,"wire_ref":null},{"edge":174,"face":85,"from":89,"geometry_use":{"pcurve":342,"periodic_lift":[0,0],"sense":"Reverse"},"id":342,"loop_ref":85,"next":343,"prev":341,"to":85,"twin":351,"wire_ref":null},{"edge":165,"face":85,"from":85,"geometry_use":{"pcurve":343,"periodic_lift":[0,0],"sense":"Forward"},"id":343,"loop_ref":85,"next":340,"prev":342,"to":84,"twin":325,"wire_ref":null},{"edge":175,"face":86,"from":86,"geometry_use":{"pcurve":344,"periodic_lift":[0,0],"sense":"Forward"},"id":344,"loop_ref":86,"next":345,"prev":347,"to":90,"twin":349,"wire_ref":null},{"edge":176,"face":86,"from":90,"geometry_use":{"pcurve":345,"periodic_lift":[0,0],"sense":"Forward"},"id":345,"loop_ref":86,"next":346,"prev":344,"to":91,"twin":379,"wire_ref":null},{"edge":177,"face":86,"from":91,"geometry_use":{"pcurve":346,"periodic_lift":[0,0],"sense":"Reverse"},"id":346,"loop_ref":86,"next":347,"prev":345,"to":87,"twin":356,"wire_ref":null},{"edge":168,"face":86,"from":87,"geometry_use":{"pcurve":347,"periodic_lift":[0,0],"sense":"Reverse"},"id":347,"loop_ref":86,"next":344,"prev":346,"to":86,"twin":329,"wire_ref":null},{"edge":178,"face":87,"from":89,"geometry_use":{"pcurve":348,"periodic_lift":[0,0],"sense":"Forward"},"id":348,"loop_ref":87,"next":349,"prev":351,"to":90,"twin":382,"wire_ref":null},{"edge":175,"face":87,"from":90,"geometry_use":{"pcurve":349,"periodic_lift":[0,0],"sense":"Reverse"},"id":349,"loop_ref":87,"next":350,"prev":348,"to":86,"twin":344,"wire_ref":null},{"edge":170,"face":87,"from":86,"geometry_use":{"pcurve":350,"periodic_lift":[0,0],"sense":"Reverse"},"id":350,"loop_ref":87,"next":351,"prev":349,"to":85,"twin":332,"wire_ref":null},{"edge":174,"face":87,"from":85,"geometry_use":{"pcurve":351,"periodic_lift":[0,0],"sense":"Forward"},"id":351,"loop_ref":87,"next":348,"prev":350,"to":89,"twin":342,"wire_ref":null},{"edge":179,"face":88,"from":92,"geometry_use":{"pcurve":352,"periodic_lift":[0,0],"sense":"Forward"},"id":352,"loop_ref":88,"next":353,"prev":355,"to":93,"twin":371,"wire_ref":null},{"edge":180,"face":88,"from":93,"geometry_use":{"pcurve":353,"periodic_lift":[0,0],"sense":"Reverse"},"id":353,"loop_ref":88,"next":354,"prev":352,"to":88,"twin":364,"wire_ref":null},{"edge":172,"face":88,"from":88,"geometry_use":{"pcurve":354,"periodic_lift":[0,0],"sense":"Reverse"},"id":354,"loop_ref":88,"next":355,"prev":353,"to":84,"twin":340,"wire_ref":null},{"edge":181,"face":88,"from":84,"geometry_use":{"pcurve":355,"periodic_lift":[0,0],"sense":"Forward"},"id":355,"loop_ref":88,"next":352,"prev":354,"to":92,"twin":362,"wire_ref":null},{"edge":177,"face":89,"from":87,"geometry_use":{"pcurve":356,"periodic_lift":[0,0],"sense":"Forward"},"id":356,"loop_ref":89,"next":357,"prev":359,"to":91,"twin":346,"wire_ref":null},{"edge":182,"face":89,"from":91,"geometry_use":{"pcurve":357,"periodic_lift":[0,0],"sense":"Forward"},"id":357,"loop_ref":89,"next":358,"prev":356,"to":94,"twin":366,"wire_ref":null},{"edge":183,"face":89,"from":94,"geometry_use":{"pcurve":358,"periodic_lift":[0,0],"sense":"Reverse"},"id":358,"loop_ref":89,"next":359,"prev":357,"to":95,"twin":369,"wire_ref":null},{"edge":184,"face":89,"from":95,"geometry_use":{"pcurve":359,"periodic_lift":[0,0],"sense":"Reverse"},"id":359,"loop_ref":89,"next":356,"prev":358,"to":87,"twin":360,"wire_ref":null},{"edge":184,"face":90,"from":87,"geometry_use":{"pcurve":360,"periodic_lift":[0,0],"sense":"Forward"},"id":360,"loop_ref":90,"next":361,"prev":363,"to":95,"twin":359,"wire_ref":null},{"edge":185,"face":90,"from":95,"geometry_use":{"pcurve":361,"periodic_lift":[0,0],"sense":"Reverse"},"id":361,"loop_ref":90,"next":362,"prev":360,"to":92,"twin":368,"wire_ref":null},{"edge":181,"face":90,"from":92,"geometry_use":{"pcurve":362,"periodic_lift":[0,0],"sense":"Reverse"},"id":362,"loop_ref":90,"next":363,"prev":361,"to":84,"twin":355,"wire_ref":null},{"edge":171,"face":90,"from":84,"geometry_use":{"pcurve":363,"periodic_lift":[0,0],"sense":"Forward"},"id":363,"loop_ref":90,"next":360,"prev":362,"to":87,"twin":338,"wire_ref":null},{"edge":180,"face":91,"from":88,"geometry_use":{"pcurve":364,"periodic_lift":[0,0],"sense":"Forward"},"id":364,"loop_ref":91,"next":365,"prev":367,"to":93,"twin":353,"wire_ref":null},{"edge":186,"face":91,"from":93,"geometry_use":{"pcurve":365,"periodic_lift":[0,0],"sense":"Forward"},"id":365,"loop_ref":91,"next":366,"prev":364,"to":94,"twin":370,"wire_ref":null},{"edge":182,"face":91,"from":94,"geometry_use":{"pcurve":366,"periodic_lift":[0,0],"sense":"Reverse"},"id":366,"loop_ref":91,"next":367,"prev":365,"to":91,"twin":357,"wire_ref":null},{"edge":187,"face":91,"from":91,"geometry_use":{"pcurve":367,"periodic_lift":[0,0],"sense":"Reverse"},"id":367,"loop_ref":91,"next":364,"prev":366,"to":88,"twin":384,"wire_ref":null},{"edge":185,"face":92,"from":92,"geometry_use":{"pcurve":368,"periodic_lift":[0,0],"sense":"Forward"},"id":368,"loop_ref":92,"next":369,"prev":371,"to":95,"twin":361,"wire_ref":null},{"edge":183,"face":92,"from":95,"geometry_use":{"pcurve":369,"periodic_lift":[0,0],"sense":"Forward"},"id":369,"loop_ref":92,"next":370,"prev":368,"to":94,"twin":358,"wire_ref":null},{"edge":186,"face":92,"from":94,"geometry_use":{"pcurve":370,"periodic_lift":[0,0],"sense":"Reverse"},"id":370,"loop_ref":92,"next":371,"prev":369,"to":93,"twin":365,"wire_ref":null},{"edge":179,"face":92,"from":93,"geometry_use":{"pcurve":371,"periodic_lift":[0,0],"sense":"Reverse"},"id":371,"loop_ref":92,"next":368,"prev":370,"to":92,"twin":352,"wire_ref":null},{"edge":188,"face":93,"from":88,"geometry_use":{"pcurve":372,"periodic_lift":[0,0],"sense":"Forward"},"id":372,"loop_ref":93,"next":373,"prev":375,"to":96,"twin":387,"wire_ref":null},{"edge":189,"face":93,"from":96,"geometry_use":{"pcurve":373,"periodic_lift":[0,0],"sense":"Reverse"},"id":373,"loop_ref":93,"next":374,"prev":372,"to":97,"twin":391,"wire_ref":null},{"edge":190,"face":93,"from":97,"geometry_use":{"pcurve":374,"periodic_lift":[0,0],"sense":"Reverse"},"id":374,"loop_ref":93,"next":375,"prev":373,"to":89,"twin":383,"wire_ref":null},{"edge":173,"face":93,"from":89,"geometry_use":{"pcurve":375,"periodic_lift":[0,0],"sense":"Forward"},"id":375,"loop_ref":93,"next":372,"prev":374,"to":88,"twin":341,"wire_ref":null},{"edge":191,"face":94,"from":90,"geometry_use":{"pcurve":376,"periodic_lift":[0,0],"sense":"Forward"},"id":376,"loop_ref":94,"next":377,"prev":379,"to":98,"twin":381,"wire_ref":null},{"edge":192,"face":94,"from":98,"geometry_use":{"pcurve":377,"periodic_lift":[0,0],"sense":"Forward"},"id":377,"loop_ref":94,"next":378,"prev":376,"to":99,"twin":395,"wire_ref":null},{"edge":193,"face":94,"from":99,"geometry_use":{"pcurve":378,"periodic_lift":[0,0],"sense":"Reverse"},"id":378,"loop_ref":94,"next":379,"prev":377,"to":91,"twin":385,"wire_ref":null},{"edge":176,"face":94,"from":91,"geometry_use":{"pcurve":379,"periodic_lift":[0,0],"sense":"Reverse"},"id":379,"loop_ref":94,"next":376,"prev":378,"to":90,"twin":345,"wire_ref":null},{"edge":194,"face":95,"from":97,"geometry_use":{"pcurve":380,"periodic_lift":[0,0],"sense":"Forward"},"id":380,"loop_ref":95,"next":381,"prev":383,"to":98,"twin":398,"wire_ref":null},{"edge":191,"face":95,"from":98,"geometry_use":{"pcurve":381,"periodic_lift":[0,0],"sense":"Reverse"},"id":381,"loop_ref":95,"next":382,"prev":380,"to":90,"twin":376,"wire_ref":null},{"edge":178,"face":95,"from":90,"geometry_use":{"pcurve":382,"periodic_lift":[0,0],"sense":"Reverse"},"id":382,"loop_ref":95,"next":383,"prev":381,"to":89,"twin":348,"wire_ref":null},{"edge":190,"face":95,"from":89,"geometry_use":{"pcurve":383,"periodic_lift":[0,0],"sense":"Forward"},"id":383,"loop_ref":95,"next":380,"prev":382,"to":97,"twin":374,"wire_ref":null},{"edge":187,"face":96,"from":88,"geometry_use":{"pcurve":384,"periodic_lift":[0,0],"sense":"Forward"},"id":384,"loop_ref":96,"next":385,"prev":387,"to":91,"twin":367,"wire_ref":null},{"edge":193,"face":96,"from":91,"geometry_use":{"pcurve":385,"periodic_lift":[0,0],"sense":"Forward"},"id":385,"loop_ref":96,"next":386,"prev":384,"to":99,"twin":378,"wire_ref":null},{"edge":195,"face":96,"from":99,"geometry_use":{"pcurve":386,"periodic_lift":[0,0],"sense":"Reverse"},"id":386,"loop_ref":96,"next":387,"prev":385,"to":96,"twin":411,"wire_ref":null},{"edge":188,"face":96,"from":96,"geometry_use":{"pcurve":387,"periodic_lift":[0,0],"sense":"Reverse"},"id":387,"loop_ref":96,"next":384,"prev":386,"to":88,"twin":372,"wire_ref":null},{"edge":196,"face":97,"from":96,"geometry_use":{"pcurve":388,"periodic_lift":[0,0],"sense":"Forward"},"id":388,"loop_ref":97,"next":389,"prev":391,"to":100,"twin":402,"wire_ref":null},{"edge":197,"face":97,"from":100,"geometry_use":{"pcurve":389,"periodic_lift":[0,0],"sense":"Reverse"},"id":389,"loop_ref":97,"next":390,"prev":388,"to":101,"twin":423,"wire_ref":null},{"edge":198,"face":97,"from":101,"geometry_use":{"pcurve":390,"periodic_lift":[0,0],"sense":"Reverse"},"id":390,"loop_ref":97,"next":391,"prev":389,"to":97,"twin":399,"wire_ref":null},{"edge":189,"face":97,"from":97,"geometry_use":{"pcurve":391,"periodic_lift":[0,0],"sense":"Forward"},"id":391,"loop_ref":97,"next":388,"prev":390,"to":96,"twin":373,"wire_ref":null},{"edge":199,"face":98,"from":98,"geometry_use":{"pcurve":392,"periodic_lift":[0,0],"sense":"Forward"},"id":392,"loop_ref":98,"next":393,"prev":395,"to":102,"twin":397,"wire_ref":null},{"edge":200,"face":98,"from":102,"geometry_use":{"pcurve":393,"periodic_lift":[0,0],"sense":"Forward"},"id":393,"loop_ref":98,"next":394,"prev":392,"to":103,"twin":427,"wire_ref":null},{"edge":201,"face":98,"from":103,"geometry_use":{"pcurve":394,"periodic_lift":[0,0],"sense":"Reverse"},"id":394,"loop_ref":98,"next":395,"prev":393,"to":99,"twin":404,"wire_ref":null},{"edge":192,"face":98,"from":99,"geometry_use":{"pcurve":395,"periodic_lift":[0,0],"sense":"Reverse"},"id":395,"loop_ref":98,"next":392,"prev":394,"to":98,"twin":377,"wire_ref":null},{"edge":202,"face":99,"from":101,"geometry_use":{"pcurve":396,"periodic_lift":[0,0],"sense":"Forward"},"id":396,"loop_ref":99,"next":397,"prev":399,"to":102,"twin":430,"wire_ref":null},{"edge":199,"face":99,"from":102,"geometry_use":{"pcurve":397,"periodic_lift":[0,0],"sense":"Reverse"},"id":397,"loop_ref":99,"next":398,"prev":396,"to":98,"twin":392,"wire_ref":null},{"edge":194,"face":99,"from":98,"geometry_use":{"pcurve":398,"periodic_lift":[0,0],"sense":"Reverse"},"id":398,"loop_ref":99,"next":399,"prev":397,"to":97,"twin":380,"wire_ref":null},{"edge":198,"face":99,"from":97,"geometry_use":{"pcurve":399,"periodic_lift":[0,0],"sense":"Forward"},"id":399,"loop_ref":99,"next":396,"prev":398,"to":101,"twin":390,"wire_ref":null},{"edge":203,"face":100,"from":104,"geometry_use":{"pcurve":400,"periodic_lift":[0,0],"sense":"Forward"},"id":400,"loop_ref":100,"next":401,"prev":403,"to":105,"twin":419,"wire_ref":null},{"edge":204,"face":100,"from":105,"geometry_use":{"pcurve":401,"periodic_lift":[0,0],"sense":"Reverse"},"id":401,"loop_ref":100,"next":402,"prev":400,"to":100,"twin":412,"wire_ref":null},{"edge":196,"face":100,"from":100,"geometry_use":{"pcurve":402,"periodic_lift":[0,0],"sense":"Reverse"},"id":402,"loop_ref":100,"next":403,"prev":401,"to":96,"twin":388,"wire_ref":null},{"edge":205,"face":100,"from":96,"geometry_use":{"pcurve":403,"periodic_lift":[0,0],"sense":"Forward"},"id":403,"loop_ref":100,"next":400,"prev":402,"to":104,"twin":410,"wire_ref":null},{"edge":201,"face":101,"from":99,"geometry_use":{"pcurve":404,"periodic_lift":[0,0],"sense":"Forward"},"id":404,"loop_ref":101,"next":405,"prev":407,"to":103,"twin":394,"wire_ref":null},{"edge":206,"face":101,"from":103,"geometry_use":{"pcurve":405,"periodic_lift":[0,0],"sense":"Forward"},"id":405,"loop_ref":101,"next":406,"prev":404,"to":106,"twin":414,"wire_ref":null},{"edge":207,"face":101,"from":106,"geometry_use":{"pcurve":406,"periodic_lift":[0,0],"sense":"Reverse"},"id":406,"loop_ref":101,"next":407,"prev":405,"to":107,"twin":417,"wire_ref":null},{"edge":208,"face":101,"from":107,"geometry_use":{"pcurve":407,"periodic_lift":[0,0],"sense":"Reverse"},"id":407,"loop_ref":101,"next":404,"prev":406,"to":99,"twin":408,"wire_ref":null},{"edge":208,"face":102,"from":99,"geometry_use":{"pcurve":408,"periodic_lift":[0,0],"sense":"Forward"},"id":408,"loop_ref":102,"next":409,"prev":411,"to":107,"twin":407,"wire_ref":null},{"edge":209,"face":102,"from":107,"geometry_use":{"pcurve":409,"periodic_lift":[0,0],"sense":"Reverse"},"id":409,"loop_ref":102,"next":410,"prev":408,"to":104,"twin":416,"wire_ref":null},{"edge":205,"face":102,"from":104,"geometry_use":{"pcurve":410,"periodic_lift":[0,0],"sense":"Reverse"},"id":410,"loop_ref":102,"next":411,"prev":409,"to":96,"twin":403,"wire_ref":null},{"edge":195,"face":102,"from":96,"geometry_use":{"pcurve":411,"periodic_lift":[0,0],"sense":"Forward"},"id":411,"loop_ref":102,"next":408,"prev":410,"to":99,"twin":386,"wire_ref":null},{"edge":204,"face":103,"from":100,"geometry_use":{"pcurve":412,"periodic_lift":[0,0],"sense":"Forward"},"id":412,"loop_ref":103,"next":413,"prev":415,"to":105,"twin":401,"wire_ref":null},{"edge":210,"face":103,"from":105,"geometry_use":{"pcurve":413,"periodic_lift":[0,0],"sense":"Forward"},"id":413,"loop_ref":103,"next":414,"prev":412,"to":106,"twin":418,"wire_ref":null},{"edge":206,"face":103,"from":106,"geometry_use":{"pcurve":414,"periodic_lift":[0,0],"sense":"Reverse"},"id":414,"loop_ref":103,"next":415,"prev":413,"to":103,"twin":405,"wire_ref":null},{"edge":211,"face":103,"from":103,"geometry_use":{"pcurve":415,"periodic_lift":[0,0],"sense":"Reverse"},"id":415,"loop_ref":103,"next":412,"prev":414,"to":100,"twin":432,"wire_ref":null},{"edge":209,"face":104,"from":104,"geometry_use":{"pcurve":416,"periodic_lift":[0,0],"sense":"Forward"},"id":416,"loop_ref":104,"next":417,"prev":419,"to":107,"twin":409,"wire_ref":null},{"edge":207,"face":104,"from":107,"geometry_use":{"pcurve":417,"periodic_lift":[0,0],"sense":"Forward"},"id":417,"loop_ref":104,"next":418,"prev":416,"to":106,"twin":406,"wire_ref":null},{"edge":210,"face":104,"from":106,"geometry_use":{"pcurve":418,"periodic_lift":[0,0],"sense":"Reverse"},"id":418,"loop_ref":104,"next":419,"prev":417,"to":105,"twin":413,"wire_ref":null},{"edge":203,"face":104,"from":105,"geometry_use":{"pcurve":419,"periodic_lift":[0,0],"sense":"Reverse"},"id":419,"loop_ref":104,"next":416,"prev":418,"to":104,"twin":400,"wire_ref":null},{"edge":212,"face":105,"from":100,"geometry_use":{"pcurve":420,"periodic_lift":[0,0],"sense":"Forward"},"id":420,"loop_ref":105,"next":421,"prev":423,"to":108,"twin":435,"wire_ref":null},{"edge":213,"face":105,"from":108,"geometry_use":{"pcurve":421,"periodic_lift":[0,0],"sense":"Reverse"},"id":421,"loop_ref":105,"next":422,"prev":420,"to":109,"twin":439,"wire_ref":null},{"edge":214,"face":105,"from":109,"geometry_use":{"pcurve":422,"periodic_lift":[0,0],"sense":"Reverse"},"id":422,"loop_ref":105,"next":423,"prev":421,"to":101,"twin":431,"wire_ref":null},{"edge":197,"face":105,"from":101,"geometry_use":{"pcurve":423,"periodic_lift":[0,0],"sense":"Forward"},"id":423,"loop_ref":105,"next":420,"prev":422,"to":100,"twin":389,"wire_ref":null},{"edge":215,"face":106,"from":102,"geometry_use":{"pcurve":424,"periodic_lift":[0,0],"sense":"Forward"},"id":424,"loop_ref":106,"next":425,"prev":427,"to":110,"twin":429,"wire_ref":null},{"edge":216,"face":106,"from":110,"geometry_use":{"pcurve":425,"periodic_lift":[0,0],"sense":"Forward"},"id":425,"loop_ref":106,"next":426,"prev":424,"to":111,"twin":443,"wire_ref":null},{"edge":217,"face":106,"from":111,"geometry_use":{"pcurve":426,"periodic_lift":[0,0],"sense":"Reverse"},"id":426,"loop_ref":106,"next":427,"prev":425,"to":103,"twin":433,"wire_ref":null},{"edge":200,"face":106,"from":103,"geometry_use":{"pcurve":427,"periodic_lift":[0,0],"sense":"Reverse"},"id":427,"loop_ref":106,"next":424,"prev":426,"to":102,"twin":393,"wire_ref":null},{"edge":218,"face":107,"from":109,"geometry_use":{"pcurve":428,"periodic_lift":[0,0],"sense":"Forward"},"id":428,"loop_ref":107,"next":429,"prev":431,"to":110,"twin":446,"wire_ref":null},{"edge":215,"face":107,"from":110,"geometry_use":{"pcurve":429,"periodic_lift":[0,0],"sense":"Reverse"},"id":429,"loop_ref":107,"next":430,"prev":428,"to":102,"twin":424,"wire_ref":null},{"edge":202,"face":107,"from":102,"geometry_use":{"pcurve":430,"periodic_lift":[0,0],"sense":"Reverse"},"id":430,"loop_ref":107,"next":431,"prev":429,"to":101,"twin":396,"wire_ref":null},{"edge":214,"face":107,"from":101,"geometry_use":{"pcurve":431,"periodic_lift":[0,0],"sense":"Forward"},"id":431,"loop_ref":107,"next":428,"prev":430,"to":109,"twin":422,"wire_ref":null},{"edge":211,"face":108,"from":100,"geometry_use":{"pcurve":432,"periodic_lift":[0,0],"sense":"Forward"},"id":432,"loop_ref":108,"next":433,"prev":435,"to":103,"twin":415,"wire_ref":null},{"edge":217,"face":108,"from":103,"geometry_use":{"pcurve":433,"periodic_lift":[0,0],"sense":"Forward"},"id":433,"loop_ref":108,"next":434,"prev":432,"to":111,"twin":426,"wire_ref":null},{"edge":219,"face":108,"from":111,"geometry_use":{"pcurve":434,"periodic_lift":[0,0],"sense":"Reverse"},"id":434,"loop_ref":108,"next":435,"prev":433,"to":108,"twin":459,"wire_ref":null},{"edge":212,"face":108,"from":108,"geometry_use":{"pcurve":435,"periodic_lift":[0,0],"sense":"Reverse"},"id":435,"loop_ref":108,"next":432,"prev":434,"to":100,"twin":420,"wire_ref":null},{"edge":220,"face":109,"from":108,"geometry_use":{"pcurve":436,"periodic_lift":[0,0],"sense":"Forward"},"id":436,"loop_ref":109,"next":437,"prev":439,"to":112,"twin":450,"wire_ref":null},{"edge":221,"face":109,"from":112,"geometry_use":{"pcurve":437,"periodic_lift":[0,0],"sense":"Reverse"},"id":437,"loop_ref":109,"next":438,"prev":436,"to":113,"twin":471,"wire_ref":null},{"edge":222,"face":109,"from":113,"geometry_use":{"pcurve":438,"periodic_lift":[0,0],"sense":"Reverse"},"id":438,"loop_ref":109,"next":439,"prev":437,"to":109,"twin":447,"wire_ref":null},{"edge":213,"face":109,"from":109,"geometry_use":{"pcurve":439,"periodic_lift":[0,0],"sense":"Forward"},"id":439,"loop_ref":109,"next":436,"prev":438,"to":108,"twin":421,"wire_ref":null},{"edge":223,"face":110,"from":110,"geometry_use":{"pcurve":440,"periodic_lift":[0,0],"sense":"Forward"},"id":440,"loop_ref":110,"next":441,"prev":443,"to":114,"twin":445,"wire_ref":null},{"edge":224,"face":110,"from":114,"geometry_use":{"pcurve":441,"periodic_lift":[0,0],"sense":"Forward"},"id":441,"loop_ref":110,"next":442,"prev":440,"to":115,"twin":475,"wire_ref":null},{"edge":225,"face":110,"from":115,"geometry_use":{"pcurve":442,"periodic_lift":[0,0],"sense":"Reverse"},"id":442,"loop_ref":110,"next":443,"prev":441,"to":111,"twin":452,"wire_ref":null},{"edge":216,"face":110,"from":111,"geometry_use":{"pcurve":443,"periodic_lift":[0,0],"sense":"Reverse"},"id":443,"loop_ref":110,"next":440,"prev":442,"to":110,"twin":425,"wire_ref":null},{"edge":226,"face":111,"from":113,"geometry_use":{"pcurve":444,"periodic_lift":[0,0],"sense":"Forward"},"id":444,"loop_ref":111,"next":445,"prev":447,"to":114,"twin":478,"wire_ref":null},{"edge":223,"face":111,"from":114,"geometry_use":{"pcurve":445,"periodic_lift":[0,0],"sense":"Reverse"},"id":445,"loop_ref":111,"next":446,"prev":444,"to":110,"twin":440,"wire_ref":null},{"edge":218,"face":111,"from":110,"geometry_use":{"pcurve":446,"periodic_lift":[0,0],"sense":"Reverse"},"id":446,"loop_ref":111,"next":447,"prev":445,"to":109,"twin":428,"wire_ref":null},{"edge":222,"face":111,"from":109,"geometry_use":{"pcurve":447,"periodic_lift":[0,0],"sense":"Forward"},"id":447,"loop_ref":111,"next":444,"prev":446,"to":113,"twin":438,"wire_ref":null},{"edge":227,"face":112,"from":116,"geometry_use":{"pcurve":448,"periodic_lift":[0,0],"sense":"Forward"},"id":448,"loop_ref":112,"next":449,"prev":451,"to":117,"twin":467,"wire_ref":null},{"edge":228,"face":112,"from":117,"geometry_use":{"pcurve":449,"periodic_lift":[0,0],"sense":"Reverse"},"id":449,"loop_ref":112,"next":450,"prev":448,"to":112,"twin":460,"wire_ref":null},{"edge":220,"face":112,"from":112,"geometry_use":{"pcurve":450,"periodic_lift":[0,0],"sense":"Reverse"},"id":450,"loop_ref":112,"next":451,"prev":449,"to":108,"twin":436,"wire_ref":null},{"edge":229,"face":112,"from":108,"geometry_use":{"pcurve":451,"periodic_lift":[0,0],"sense":"Forward"},"id":451,"loop_ref":112,"next":448,"prev":450,"to":116,"twin":458,"wire_ref":null},{"edge":225,"face":113,"from":111,"geometry_use":{"pcurve":452,"periodic_lift":[0,0],"sense":"Forward"},"id":452,"loop_ref":113,"next":453,"prev":455,"to":115,"twin":442,"wire_ref":null},{"edge":230,"face":113,"from":115,"geometry_use":{"pcurve":453,"periodic_lift":[0,0],"sense":"Forward"},"id":453,"loop_ref":113,"next":454,"prev":452,"to":118,"twin":462,"wire_ref":null},{"edge":231,"face":113,"from":118,"geometry_use":{"pcurve":454,"periodic_lift":[0,0],"sense":"Reverse"},"id":454,"loop_ref":113,"next":455,"prev":453,"to":119,"twin":465,"wire_ref":null},{"edge":232,"face":113,"from":119,"geometry_use":{"pcurve":455,"periodic_lift":[0,0],"sense":"Reverse"},"id":455,"loop_ref":113,"next":452,"prev":454,"to":111,"twin":456,"wire_ref":null},{"edge":232,"face":114,"from":111,"geometry_use":{"pcurve":456,"periodic_lift":[0,0],"sense":"Forward"},"id":456,"loop_ref":114,"next":457,"prev":459,"to":119,"twin":455,"wire_ref":null},{"edge":233,"face":114,"from":119,"geometry_use":{"pcurve":457,"periodic_lift":[0,0],"sense":"Reverse"},"id":457,"loop_ref":114,"next":458,"prev":456,"to":116,"twin":464,"wire_ref":null},{"edge":229,"face":114,"from":116,"geometry_use":{"pcurve":458,"periodic_lift":[0,0],"sense":"Reverse"},"id":458,"loop_ref":114,"next":459,"prev":457,"to":108,"twin":451,"wire_ref":null},{"edge":219,"face":114,"from":108,"geometry_use":{"pcurve":459,"periodic_lift":[0,0],"sense":"Forward"},"id":459,"loop_ref":114,"next":456,"prev":458,"to":111,"twin":434,"wire_ref":null},{"edge":228,"face":115,"from":112,"geometry_use":{"pcurve":460,"periodic_lift":[0,0],"sense":"Forward"},"id":460,"loop_ref":115,"next":461,"prev":463,"to":117,"twin":449,"wire_ref":null},{"edge":234,"face":115,"from":117,"geometry_use":{"pcurve":461,"periodic_lift":[0,0],"sense":"Forward"},"id":461,"loop_ref":115,"next":462,"prev":460,"to":118,"twin":466,"wire_ref":null},{"edge":230,"face":115,"from":118,"geometry_use":{"pcurve":462,"periodic_lift":[0,0],"sense":"Reverse"},"id":462,"loop_ref":115,"next":463,"prev":461,"to":115,"twin":453,"wire_ref":null},{"edge":235,"face":115,"from":115,"geometry_use":{"pcurve":463,"periodic_lift":[0,0],"sense":"Reverse"},"id":463,"loop_ref":115,"next":460,"prev":462,"to":112,"twin":480,"wire_ref":null},{"edge":233,"face":116,"from":116,"geometry_use":{"pcurve":464,"periodic_lift":[0,0],"sense":"Forward"},"id":464,"loop_ref":116,"next":465,"prev":467,"to":119,"twin":457,"wire_ref":null},{"edge":231,"face":116,"from":119,"geometry_use":{"pcurve":465,"periodic_lift":[0,0],"sense":"Forward"},"id":465,"loop_ref":116,"next":466,"prev":464,"to":118,"twin":454,"wire_ref":null},{"edge":234,"face":116,"from":118,"geometry_use":{"pcurve":466,"periodic_lift":[0,0],"sense":"Reverse"},"id":466,"loop_ref":116,"next":467,"prev":465,"to":117,"twin":461,"wire_ref":null},{"edge":227,"face":116,"from":117,"geometry_use":{"pcurve":467,"periodic_lift":[0,0],"sense":"Reverse"},"id":467,"loop_ref":116,"next":464,"prev":466,"to":116,"twin":448,"wire_ref":null},{"edge":236,"face":117,"from":112,"geometry_use":{"pcurve":468,"periodic_lift":[0,0],"sense":"Forward"},"id":468,"loop_ref":117,"next":469,"prev":471,"to":120,"twin":483,"wire_ref":null},{"edge":237,"face":117,"from":120,"geometry_use":{"pcurve":469,"periodic_lift":[0,0],"sense":"Reverse"},"id":469,"loop_ref":117,"next":470,"prev":468,"to":121,"twin":487,"wire_ref":null},{"edge":238,"face":117,"from":121,"geometry_use":{"pcurve":470,"periodic_lift":[0,0],"sense":"Reverse"},"id":470,"loop_ref":117,"next":471,"prev":469,"to":113,"twin":479,"wire_ref":null},{"edge":221,"face":117,"from":113,"geometry_use":{"pcurve":471,"periodic_lift":[0,0],"sense":"Forward"},"id":471,"loop_ref":117,"next":468,"prev":470,"to":112,"twin":437,"wire_ref":null},{"edge":239,"face":118,"from":114,"geometry_use":{"pcurve":472,"periodic_lift":[0,0],"sense":"Forward"},"id":472,"loop_ref":118,"next":473,"prev":475,"to":122,"twin":477,"wire_ref":null},{"edge":240,"face":118,"from":122,"geometry_use":{"pcurve":473,"periodic_lift":[0,0],"sense":"Forward"},"id":473,"loop_ref":118,"next":474,"prev":472,"to":123,"twin":491,"wire_ref":null},{"edge":241,"face":118,"from":123,"geometry_use":{"pcurve":474,"periodic_lift":[0,0],"sense":"Reverse"},"id":474,"loop_ref":118,"next":475,"prev":473,"to":115,"twin":481,"wire_ref":null},{"edge":224,"face":118,"from":115,"geometry_use":{"pcurve":475,"periodic_lift":[0,0],"sense":"Reverse"},"id":475,"loop_ref":118,"next":472,"prev":474,"to":114,"twin":441,"wire_ref":null},{"edge":242,"face":119,"from":121,"geometry_use":{"pcurve":476,"periodic_lift":[0,0],"sense":"Forward"},"id":476,"loop_ref":119,"next":477,"prev":479,"to":122,"twin":494,"wire_ref":null},{"edge":239,"face":119,"from":122,"geometry_use":{"pcurve":477,"periodic_lift":[0,0],"sense":"Reverse"},"id":477,"loop_ref":119,"next":478,"prev":476,"to":114,"twin":472,"wire_ref":null},{"edge":226,"face":119,"from":114,"geometry_use":{"pcurve":478,"periodic_lift":[0,0],"sense":"Reverse"},"id":478,"loop_ref":119,"next":479,"prev":477,"to":113,"twin":444,"wire_ref":null},{"edge":238,"face":119,"from":113,"geometry_use":{"pcurve":479,"periodic_lift":[0,0],"sense":"Forward"},"id":479,"loop_ref":119,"next":476,"prev":478,"to":121,"twin":470,"wire_ref":null},{"edge":235,"face":120,"from":112,"geometry_use":{"pcurve":480,"periodic_lift":[0,0],"sense":"Forward"},"id":480,"loop_ref":120,"next":481,"prev":483,"to":115,"twin":463,"wire_ref":null},{"edge":241,"face":120,"from":115,"geometry_use":{"pcurve":481,"periodic_lift":[0,0],"sense":"Forward"},"id":481,"loop_ref":120,"next":482,"prev":480,"to":123,"twin":474,"wire_ref":null},{"edge":243,"face":120,"from":123,"geometry_use":{"pcurve":482,"periodic_lift":[0,0],"sense":"Reverse"},"id":482,"loop_ref":120,"next":483,"prev":481,"to":120,"twin":507,"wire_ref":null},{"edge":236,"face":120,"from":120,"geometry_use":{"pcurve":483,"periodic_lift":[0,0],"sense":"Reverse"},"id":483,"loop_ref":120,"next":480,"prev":482,"to":112,"twin":468,"wire_ref":null},{"edge":244,"face":121,"from":120,"geometry_use":{"pcurve":484,"periodic_lift":[0,0],"sense":"Forward"},"id":484,"loop_ref":121,"next":485,"prev":487,"to":124,"twin":498,"wire_ref":null},{"edge":245,"face":121,"from":124,"geometry_use":{"pcurve":485,"periodic_lift":[0,0],"sense":"Reverse"},"id":485,"loop_ref":121,"next":486,"prev":484,"to":125,"twin":519,"wire_ref":null},{"edge":246,"face":121,"from":125,"geometry_use":{"pcurve":486,"periodic_lift":[0,0],"sense":"Reverse"},"id":486,"loop_ref":121,"next":487,"prev":485,"to":121,"twin":495,"wire_ref":null},{"edge":237,"face":121,"from":121,"geometry_use":{"pcurve":487,"periodic_lift":[0,0],"sense":"Forward"},"id":487,"loop_ref":121,"next":484,"prev":486,"to":120,"twin":469,"wire_ref":null},{"edge":247,"face":122,"from":122,"geometry_use":{"pcurve":488,"periodic_lift":[0,0],"sense":"Forward"},"id":488,"loop_ref":122,"next":489,"prev":491,"to":126,"twin":493,"wire_ref":null},{"edge":248,"face":122,"from":126,"geometry_use":{"pcurve":489,"periodic_lift":[0,0],"sense":"Forward"},"id":489,"loop_ref":122,"next":490,"prev":488,"to":127,"twin":523,"wire_ref":null},{"edge":249,"face":122,"from":127,"geometry_use":{"pcurve":490,"periodic_lift":[0,0],"sense":"Reverse"},"id":490,"loop_ref":122,"next":491,"prev":489,"to":123,"twin":500,"wire_ref":null},{"edge":240,"face":122,"from":123,"geometry_use":{"pcurve":491,"periodic_lift":[0,0],"sense":"Reverse"},"id":491,"loop_ref":122,"next":488,"prev":490,"to":122,"twin":473,"wire_ref":null},{"edge":250,"face":123,"from":125,"geometry_use":{"pcurve":492,"periodic_lift":[0,0],"sense":"Forward"},"id":492,"loop_ref":123,"next":493,"prev":495,"to":126,"twin":526,"wire_ref":null},{"edge":247,"face":123,"from":126,"geometry_use":{"pcurve":493,"periodic_lift":[0,0],"sense":"Reverse"},"id":493,"loop_ref":123,"next":494,"prev":492,"to":122,"twin":488,"wire_ref":null},{"edge":242,"face":123,"from":122,"geometry_use":{"pcurve":494,"periodic_lift":[0,0],"sense":"Reverse"},"id":494,"loop_ref":123,"next":495,"prev":493,"to":121,"twin":476,"wire_ref":null},{"edge":246,"face":123,"from":121,"geometry_use":{"pcurve":495,"periodic_lift":[0,0],"sense":"Forward"},"id":495,"loop_ref":123,"next":492,"prev":494,"to":125,"twin":486,"wire_ref":null},{"edge":251,"face":124,"from":128,"geometry_use":{"pcurve":496,"periodic_lift":[0,0],"sense":"Forward"},"id":496,"loop_ref":124,"next":497,"prev":499,"to":129,"twin":515,"wire_ref":null},{"edge":252,"face":124,"from":129,"geometry_use":{"pcurve":497,"periodic_lift":[0,0],"sense":"Reverse"},"id":497,"loop_ref":124,"next":498,"prev":496,"to":124,"twin":508,"wire_ref":null},{"edge":244,"face":124,"from":124,"geometry_use":{"pcurve":498,"periodic_lift":[0,0],"sense":"Reverse"},"id":498,"loop_ref":124,"next":499,"prev":497,"to":120,"twin":484,"wire_ref":null},{"edge":253,"face":124,"from":120,"geometry_use":{"pcurve":499,"periodic_lift":[0,0],"sense":"Forward"},"id":499,"loop_ref":124,"next":496,"prev":498,"to":128,"twin":506,"wire_ref":null},{"edge":249,"face":125,"from":123,"geometry_use":{"pcurve":500,"periodic_lift":[0,0],"sense":"Forward"},"id":500,"loop_ref":125,"next":501,"prev":503,"to":127,"twin":490,"wire_ref":null},{"edge":254,"face":125,"from":127,"geometry_use":{"pcurve":501,"periodic_lift":[0,0],"sense":"Forward"},"id":501,"loop_ref":125,"next":502,"prev":500,"to":130,"twin":510,"wire_ref":null},{"edge":255,"face":125,"from":130,"geometry_use":{"pcurve":502,"periodic_lift":[0,0],"sense":"Reverse"},"id":502,"loop_ref":125,"next":503,"prev":501,"to":131,"twin":513,"wire_ref":null},{"edge":256,"face":125,"from":131,"geometry_use":{"pcurve":503,"periodic_lift":[0,0],"sense":"Reverse"},"id":503,"loop_ref":125,"next":500,"prev":502,"to":123,"twin":504,"wire_ref":null},{"edge":256,"face":126,"from":123,"geometry_use":{"pcurve":504,"periodic_lift":[0,0],"sense":"Forward"},"id":504,"loop_ref":126,"next":505,"prev":507,"to":131,"twin":503,"wire_ref":null},{"edge":257,"face":126,"from":131,"geometry_use":{"pcurve":505,"periodic_lift":[0,0],"sense":"Reverse"},"id":505,"loop_ref":126,"next":506,"prev":504,"to":128,"twin":512,"wire_ref":null},{"edge":253,"face":126,"from":128,"geometry_use":{"pcurve":506,"periodic_lift":[0,0],"sense":"Reverse"},"id":506,"loop_ref":126,"next":507,"prev":505,"to":120,"twin":499,"wire_ref":null},{"edge":243,"face":126,"from":120,"geometry_use":{"pcurve":507,"periodic_lift":[0,0],"sense":"Forward"},"id":507,"loop_ref":126,"next":504,"prev":506,"to":123,"twin":482,"wire_ref":null},{"edge":252,"face":127,"from":124,"geometry_use":{"pcurve":508,"periodic_lift":[0,0],"sense":"Forward"},"id":508,"loop_ref":127,"next":509,"prev":511,"to":129,"twin":497,"wire_ref":null},{"edge":258,"face":127,"from":129,"geometry_use":{"pcurve":509,"periodic_lift":[0,0],"sense":"Forward"},"id":509,"loop_ref":127,"next":510,"prev":508,"to":130,"twin":514,"wire_ref":null},{"edge":254,"face":127,"from":130,"geometry_use":{"pcurve":510,"periodic_lift":[0,0],"sense":"Reverse"},"id":510,"loop_ref":127,"next":511,"prev":509,"to":127,"twin":501,"wire_ref":null},{"edge":259,"face":127,"from":127,"geometry_use":{"pcurve":511,"periodic_lift":[0,0],"sense":"Reverse"},"id":511,"loop_ref":127,"next":508,"prev":510,"to":124,"twin":528,"wire_ref":null},{"edge":257,"face":128,"from":128,"geometry_use":{"pcurve":512,"periodic_lift":[0,0],"sense":"Forward"},"id":512,"loop_ref":128,"next":513,"prev":515,"to":131,"twin":505,"wire_ref":null},{"edge":255,"face":128,"from":131,"geometry_use":{"pcurve":513,"periodic_lift":[0,0],"sense":"Forward"},"id":513,"loop_ref":128,"next":514,"prev":512,"to":130,"twin":502,"wire_ref":null},{"edge":258,"face":128,"from":130,"geometry_use":{"pcurve":514,"periodic_lift":[0,0],"sense":"Reverse"},"id":514,"loop_ref":128,"next":515,"prev":513,"to":129,"twin":509,"wire_ref":null},{"edge":251,"face":128,"from":129,"geometry_use":{"pcurve":515,"periodic_lift":[0,0],"sense":"Reverse"},"id":515,"loop_ref":128,"next":512,"prev":514,"to":128,"twin":496,"wire_ref":null},{"edge":260,"face":129,"from":124,"geometry_use":{"pcurve":516,"periodic_lift":[0,0],"sense":"Forward"},"id":516,"loop_ref":129,"next":517,"prev":519,"to":132,"twin":531,"wire_ref":null},{"edge":261,"face":129,"from":132,"geometry_use":{"pcurve":517,"periodic_lift":[0,0],"sense":"Reverse"},"id":517,"loop_ref":129,"next":518,"prev":516,"to":133,"twin":535,"wire_ref":null},{"edge":262,"face":129,"from":133,"geometry_use":{"pcurve":518,"periodic_lift":[0,0],"sense":"Reverse"},"id":518,"loop_ref":129,"next":519,"prev":517,"to":125,"twin":527,"wire_ref":null},{"edge":245,"face":129,"from":125,"geometry_use":{"pcurve":519,"periodic_lift":[0,0],"sense":"Forward"},"id":519,"loop_ref":129,"next":516,"prev":518,"to":124,"twin":485,"wire_ref":null},{"edge":263,"face":130,"from":126,"geometry_use":{"pcurve":520,"periodic_lift":[0,0],"sense":"Forward"},"id":520,"loop_ref":130,"next":521,"prev":523,"to":134,"twin":525,"wire_ref":null},{"edge":264,"face":130,"from":134,"geometry_use":{"pcurve":521,"periodic_lift":[0,0],"sense":"Forward"},"id":521,"loop_ref":130,"next":522,"prev":520,"to":135,"twin":539,"wire_ref":null},{"edge":265,"face":130,"from":135,"geometry_use":{"pcurve":522,"periodic_lift":[0,0],"sense":"Reverse"},"id":522,"loop_ref":130,"next":523,"prev":521,"to":127,"twin":529,"wire_ref":null},{"edge":248,"face":130,"from":127,"geometry_use":{"pcurve":523,"periodic_lift":[0,0],"sense":"Reverse"},"id":523,"loop_ref":130,"next":520,"prev":522,"to":126,"twin":489,"wire_ref":null},{"edge":266,"face":131,"from":133,"geometry_use":{"pcurve":524,"periodic_lift":[0,0],"sense":"Forward"},"id":524,"loop_ref":131,"next":525,"prev":527,"to":134,"twin":542,"wire_ref":null},{"edge":263,"face":131,"from":134,"geometry_use":{"pcurve":525,"periodic_lift":[0,0],"sense":"Reverse"},"id":525,"loop_ref":131,"next":526,"prev":524,"to":126,"twin":520,"wire_ref":null},{"edge":250,"face":131,"from":126,"geometry_use":{"pcurve":526,"periodic_lift":[0,0],"sense":"Reverse"},"id":526,"loop_ref":131,"next":527,"prev":525,"to":125,"twin":492,"wire_ref":null},{"edge":262,"face":131,"from":125,"geometry_use":{"pcurve":527,"periodic_lift":[0,0],"sense":"Forward"},"id":527,"loop_ref":131,"next":524,"prev":526,"to":133,"twin":518,"wire_ref":null},{"edge":259,"face":132,"from":124,"geometry_use":{"pcurve":528,"periodic_lift":[0,0],"sense":"Forward"},"id":528,"loop_ref":132,"next":529,"prev":531,"to":127,"twin":511,"wire_ref":null},{"edge":265,"face":132,"from":127,"geometry_use":{"pcurve":529,"periodic_lift":[0,0],"sense":"Forward"},"id":529,"loop_ref":132,"next":530,"prev":528,"to":135,"twin":522,"wire_ref":null},{"edge":267,"face":132,"from":135,"geometry_use":{"pcurve":530,"periodic_lift":[0,0],"sense":"Reverse"},"id":530,"loop_ref":132,"next":531,"prev":529,"to":132,"twin":555,"wire_ref":null},{"edge":260,"face":132,"from":132,"geometry_use":{"pcurve":531,"periodic_lift":[0,0],"sense":"Reverse"},"id":531,"loop_ref":132,"next":528,"prev":530,"to":124,"twin":516,"wire_ref":null},{"edge":268,"face":133,"from":132,"geometry_use":{"pcurve":532,"periodic_lift":[0,0],"sense":"Forward"},"id":532,"loop_ref":133,"next":533,"prev":535,"to":136,"twin":546,"wire_ref":null},{"edge":269,"face":133,"from":136,"geometry_use":{"pcurve":533,"periodic_lift":[0,0],"sense":"Reverse"},"id":533,"loop_ref":133,"next":534,"prev":532,"to":137,"twin":567,"wire_ref":null},{"edge":270,"face":133,"from":137,"geometry_use":{"pcurve":534,"periodic_lift":[0,0],"sense":"Reverse"},"id":534,"loop_ref":133,"next":535,"prev":533,"to":133,"twin":543,"wire_ref":null},{"edge":261,"face":133,"from":133,"geometry_use":{"pcurve":535,"periodic_lift":[0,0],"sense":"Forward"},"id":535,"loop_ref":133,"next":532,"prev":534,"to":132,"twin":517,"wire_ref":null},{"edge":271,"face":134,"from":134,"geometry_use":{"pcurve":536,"periodic_lift":[0,0],"sense":"Forward"},"id":536,"loop_ref":134,"next":537,"prev":539,"to":138,"twin":541,"wire_ref":null},{"edge":272,"face":134,"from":138,"geometry_use":{"pcurve":537,"periodic_lift":[0,0],"sense":"Forward"},"id":537,"loop_ref":134,"next":538,"prev":536,"to":139,"twin":571,"wire_ref":null},{"edge":273,"face":134,"from":139,"geometry_use":{"pcurve":538,"periodic_lift":[0,0],"sense":"Reverse"},"id":538,"loop_ref":134,"next":539,"prev":537,"to":135,"twin":548,"wire_ref":null},{"edge":264,"face":134,"from":135,"geometry_use":{"pcurve":539,"periodic_lift":[0,0],"sense":"Reverse"},"id":539,"loop_ref":134,"next":536,"prev":538,"to":134,"twin":521,"wire_ref":null},{"edge":274,"face":135,"from":137,"geometry_use":{"pcurve":540,"periodic_lift":[0,0],"sense":"Forward"},"id":540,"loop_ref":135,"next":541,"prev":543,"to":138,"twin":574,"wire_ref":null},{"edge":271,"face":135,"from":138,"geometry_use":{"pcurve":541,"periodic_lift":[0,0],"sense":"Reverse"},"id":541,"loop_ref":135,"next":542,"prev":540,"to":134,"twin":536,"wire_ref":null},{"edge":266,"face":135,"from":134,"geometry_use":{"pcurve":542,"periodic_lift":[0,0],"sense":"Reverse"},"id":542,"loop_ref":135,"next":543,"prev":541,"to":133,"twin":524,"wire_ref":null},{"edge":270,"face":135,"from":133,"geometry_use":{"pcurve":543,"periodic_lift":[0,0],"sense":"Forward"},"id":543,"loop_ref":135,"next":540,"prev":542,"to":137,"twin":534,"wire_ref":null},{"edge":275,"face":136,"from":140,"geometry_use":{"pcurve":544,"periodic_lift":[0,0],"sense":"Forward"},"id":544,"loop_ref":136,"next":545,"prev":547,"to":141,"twin":563,"wire_ref":null},{"edge":276,"face":136,"from":141,"geometry_use":{"pcurve":545,"periodic_lift":[0,0],"sense":"Reverse"},"id":545,"loop_ref":136,"next":546,"prev":544,"to":136,"twin":556,"wire_ref":null},{"edge":268,"face":136,"from":136,"geometry_use":{"pcurve":546,"periodic_lift":[0,0],"sense":"Reverse"},"id":546,"loop_ref":136,"next":547,"prev":545,"to":132,"twin":532,"wire_ref":null},{"edge":277,"face":136,"from":132,"geometry_use":{"pcurve":547,"periodic_lift":[0,0],"sense":"Forward"},"id":547,"loop_ref":136,"next":544,"prev":546,"to":140,"twin":554,"wire_ref":null},{"edge":273,"face":137,"from":135,"geometry_use":{"pcurve":548,"periodic_lift":[0,0],"sense":"Forward"},"id":548,"loop_ref":137,"next":549,"prev":551,"to":139,"twin":538,"wire_ref":null},{"edge":278,"face":137,"from":139,"geometry_use":{"pcurve":549,"periodic_lift":[0,0],"sense":"Forward"},"id":549,"loop_ref":137,"next":550,"prev":548,"to":142,"twin":558,"wire_ref":null},{"edge":279,"face":137,"from":142,"geometry_use":{"pcurve":550,"periodic_lift":[0,0],"sense":"Reverse"},"id":550,"loop_ref":137,"next":551,"prev":549,"to":143,"twin":561,"wire_ref":null},{"edge":280,"face":137,"from":143,"geometry_use":{"pcurve":551,"periodic_lift":[0,0],"sense":"Reverse"},"id":551,"loop_ref":137,"next":548,"prev":550,"to":135,"twin":552,"wire_ref":null},{"edge":280,"face":138,"from":135,"geometry_use":{"pcurve":552,"periodic_lift":[0,0],"sense":"Forward"},"id":552,"loop_ref":138,"next":553,"prev":555,"to":143,"twin":551,"wire_ref":null},{"edge":281,"face":138,"from":143,"geometry_use":{"pcurve":553,"periodic_lift":[0,0],"sense":"Reverse"},"id":553,"loop_ref":138,"next":554,"prev":552,"to":140,"twin":560,"wire_ref":null},{"edge":277,"face":138,"from":140,"geometry_use":{"pcurve":554,"periodic_lift":[0,0],"sense":"Reverse"},"id":554,"loop_ref":138,"next":555,"prev":553,"to":132,"twin":547,"wire_ref":null},{"edge":267,"face":138,"from":132,"geometry_use":{"pcurve":555,"periodic_lift":[0,0],"sense":"Forward"},"id":555,"loop_ref":138,"next":552,"prev":554,"to":135,"twin":530,"wire_ref":null},{"edge":276,"face":139,"from":136,"geometry_use":{"pcurve":556,"periodic_lift":[0,0],"sense":"Forward"},"id":556,"loop_ref":139,"next":557,"prev":559,"to":141,"twin":545,"wire_ref":null},{"edge":282,"face":139,"from":141,"geometry_use":{"pcurve":557,"periodic_lift":[0,0],"sense":"Forward"},"id":557,"loop_ref":139,"next":558,"prev":556,"to":142,"twin":562,"wire_ref":null},{"edge":278,"face":139,"from":142,"geometry_use":{"pcurve":558,"periodic_lift":[0,0],"sense":"Reverse"},"id":558,"loop_ref":139,"next":559,"prev":557,"to":139,"twin":549,"wire_ref":null},{"edge":283,"face":139,"from":139,"geometry_use":{"pcurve":559,"periodic_lift":[0,0],"sense":"Reverse"},"id":559,"loop_ref":139,"next":556,"prev":558,"to":136,"twin":576,"wire_ref":null},{"edge":281,"face":140,"from":140,"geometry_use":{"pcurve":560,"periodic_lift":[0,0],"sense":"Forward"},"id":560,"loop_ref":140,"next":561,"prev":563,"to":143,"twin":553,"wire_ref":null},{"edge":279,"face":140,"from":143,"geometry_use":{"pcurve":561,"periodic_lift":[0,0],"sense":"Forward"},"id":561,"loop_ref":140,"next":562,"prev":560,"to":142,"twin":550,"wire_ref":null},{"edge":282,"face":140,"from":142,"geometry_use":{"pcurve":562,"periodic_lift":[0,0],"sense":"Reverse"},"id":562,"loop_ref":140,"next":563,"prev":561,"to":141,"twin":557,"wire_ref":null},{"edge":275,"face":140,"from":141,"geometry_use":{"pcurve":563,"periodic_lift":[0,0],"sense":"Reverse"},"id":563,"loop_ref":140,"next":560,"prev":562,"to":140,"twin":544,"wire_ref":null},{"edge":284,"face":141,"from":136,"geometry_use":{"pcurve":564,"periodic_lift":[0,0],"sense":"Forward"},"id":564,"loop_ref":141,"next":565,"prev":567,"to":144,"twin":579,"wire_ref":null},{"edge":285,"face":141,"from":144,"geometry_use":{"pcurve":565,"periodic_lift":[0,0],"sense":"Reverse"},"id":565,"loop_ref":141,"next":566,"prev":564,"to":145,"twin":583,"wire_ref":null},{"edge":286,"face":141,"from":145,"geometry_use":{"pcurve":566,"periodic_lift":[0,0],"sense":"Reverse"},"id":566,"loop_ref":141,"next":567,"prev":565,"to":137,"twin":575,"wire_ref":null},{"edge":269,"face":141,"from":137,"geometry_use":{"pcurve":567,"periodic_lift":[0,0],"sense":"Forward"},"id":567,"loop_ref":141,"next":564,"prev":566,"to":136,"twin":533,"wire_ref":null},{"edge":287,"face":142,"from":138,"geometry_use":{"pcurve":568,"periodic_lift":[0,0],"sense":"Forward"},"id":568,"loop_ref":142,"next":569,"prev":571,"to":146,"twin":573,"wire_ref":null},{"edge":288,"face":142,"from":146,"geometry_use":{"pcurve":569,"periodic_lift":[0,0],"sense":"Forward"},"id":569,"loop_ref":142,"next":570,"prev":568,"to":147,"twin":587,"wire_ref":null},{"edge":289,"face":142,"from":147,"geometry_use":{"pcurve":570,"periodic_lift":[0,0],"sense":"Reverse"},"id":570,"loop_ref":142,"next":571,"prev":569,"to":139,"twin":577,"wire_ref":null},{"edge":272,"face":142,"from":139,"geometry_use":{"pcurve":571,"periodic_lift":[0,0],"sense":"Reverse"},"id":571,"loop_ref":142,"next":568,"prev":570,"to":138,"twin":537,"wire_ref":null},{"edge":290,"face":143,"from":145,"geometry_use":{"pcurve":572,"periodic_lift":[0,0],"sense":"Forward"},"id":572,"loop_ref":143,"next":573,"prev":575,"to":146,"twin":590,"wire_ref":null},{"edge":287,"face":143,"from":146,"geometry_use":{"pcurve":573,"periodic_lift":[0,0],"sense":"Reverse"},"id":573,"loop_ref":143,"next":574,"prev":572,"to":138,"twin":568,"wire_ref":null},{"edge":274,"face":143,"from":138,"geometry_use":{"pcurve":574,"periodic_lift":[0,0],"sense":"Reverse"},"id":574,"loop_ref":143,"next":575,"prev":573,"to":137,"twin":540,"wire_ref":null},{"edge":286,"face":143,"from":137,"geometry_use":{"pcurve":575,"periodic_lift":[0,0],"sense":"Forward"},"id":575,"loop_ref":143,"next":572,"prev":574,"to":145,"twin":566,"wire_ref":null},{"edge":283,"face":144,"from":136,"geometry_use":{"pcurve":576,"periodic_lift":[0,0],"sense":"Forward"},"id":576,"loop_ref":144,"next":577,"prev":579,"to":139,"twin":559,"wire_ref":null},{"edge":289,"face":144,"from":139,"geometry_use":{"pcurve":577,"periodic_lift":[0,0],"sense":"Forward"},"id":577,"loop_ref":144,"next":578,"prev":576,"to":147,"twin":570,"wire_ref":null},{"edge":291,"face":144,"from":147,"geometry_use":{"pcurve":578,"periodic_lift":[0,0],"sense":"Reverse"},"id":578,"loop_ref":144,"next":579,"prev":577,"to":144,"twin":603,"wire_ref":null},{"edge":284,"face":144,"from":144,"geometry_use":{"pcurve":579,"periodic_lift":[0,0],"sense":"Reverse"},"id":579,"loop_ref":144,"next":576,"prev":578,"to":136,"twin":564,"wire_ref":null},{"edge":292,"face":145,"from":144,"geometry_use":{"pcurve":580,"periodic_lift":[0,0],"sense":"Forward"},"id":580,"loop_ref":145,"next":581,"prev":583,"to":148,"twin":594,"wire_ref":null},{"edge":293,"face":145,"from":148,"geometry_use":{"pcurve":581,"periodic_lift":[0,0],"sense":"Reverse"},"id":581,"loop_ref":145,"next":582,"prev":580,"to":149,"twin":615,"wire_ref":null},{"edge":294,"face":145,"from":149,"geometry_use":{"pcurve":582,"periodic_lift":[0,0],"sense":"Reverse"},"id":582,"loop_ref":145,"next":583,"prev":581,"to":145,"twin":591,"wire_ref":null},{"edge":285,"face":145,"from":145,"geometry_use":{"pcurve":583,"periodic_lift":[0,0],"sense":"Forward"},"id":583,"loop_ref":145,"next":580,"prev":582,"to":144,"twin":565,"wire_ref":null},{"edge":295,"face":146,"from":146,"geometry_use":{"pcurve":584,"periodic_lift":[0,0],"sense":"Forward"},"id":584,"loop_ref":146,"next":585,"prev":587,"to":150,"twin":589,"wire_ref":null},{"edge":296,"face":146,"from":150,"geometry_use":{"pcurve":585,"periodic_lift":[0,0],"sense":"Forward"},"id":585,"loop_ref":146,"next":586,"prev":584,"to":151,"twin":619,"wire_ref":null},{"edge":297,"face":146,"from":151,"geometry_use":{"pcurve":586,"periodic_lift":[0,0],"sense":"Reverse"},"id":586,"loop_ref":146,"next":587,"prev":585,"to":147,"twin":596,"wire_ref":null},{"edge":288,"face":146,"from":147,"geometry_use":{"pcurve":587,"periodic_lift":[0,0],"sense":"Reverse"},"id":587,"loop_ref":146,"next":584,"prev":586,"to":146,"twin":569,"wire_ref":null},{"edge":298,"face":147,"from":149,"geometry_use":{"pcurve":588,"periodic_lift":[0,0],"sense":"Forward"},"id":588,"loop_ref":147,"next":589,"prev":591,"to":150,"twin":622,"wire_ref":null},{"edge":295,"face":147,"from":150,"geometry_use":{"pcurve":589,"periodic_lift":[0,0],"sense":"Reverse"},"id":589,"loop_ref":147,"next":590,"prev":588,"to":146,"twin":584,"wire_ref":null},{"edge":290,"face":147,"from":146,"geometry_use":{"pcurve":590,"periodic_lift":[0,0],"sense":"Reverse"},"id":590,"loop_ref":147,"next":591,"prev":589,"to":145,"twin":572,"wire_ref":null},{"edge":294,"face":147,"from":145,"geometry_use":{"pcurve":591,"periodic_lift":[0,0],"sense":"Forward"},"id":591,"loop_ref":147,"next":588,"prev":590,"to":149,"twin":582,"wire_ref":null},{"edge":299,"face":148,"from":152,"geometry_use":{"pcurve":592,"periodic_lift":[0,0],"sense":"Forward"},"id":592,"loop_ref":148,"next":593,"prev":595,"to":153,"twin":611,"wire_ref":null},{"edge":300,"face":148,"from":153,"geometry_use":{"pcurve":593,"periodic_lift":[0,0],"sense":"Reverse"},"id":593,"loop_ref":148,"next":594,"prev":592,"to":148,"twin":604,"wire_ref":null},{"edge":292,"face":148,"from":148,"geometry_use":{"pcurve":594,"periodic_lift":[0,0],"sense":"Reverse"},"id":594,"loop_ref":148,"next":595,"prev":593,"to":144,"twin":580,"wire_ref":null},{"edge":301,"face":148,"from":144,"geometry_use":{"pcurve":595,"periodic_lift":[0,0],"sense":"Forward"},"id":595,"loop_ref":148,"next":592,"prev":594,"to":152,"twin":602,"wire_ref":null},{"edge":297,"face":149,"from":147,"geometry_use":{"pcurve":596,"periodic_lift":[0,0],"sense":"Forward"},"id":596,"loop_ref":149,"next":597,"prev":599,"to":151,"twin":586,"wire_ref":null},{"edge":302,"face":149,"from":151,"geometry_use":{"pcurve":597,"periodic_lift":[0,0],"sense":"Forward"},"id":597,"loop_ref":149,"next":598,"prev":596,"to":154,"twin":606,"wire_ref":null},{"edge":303,"face":149,"from":154,"geometry_use":{"pcurve":598,"periodic_lift":[0,0],"sense":"Reverse"},"id":598,"loop_ref":149,"next":599,"prev":597,"to":155,"twin":609,"wire_ref":null},{"edge":304,"face":149,"from":155,"geometry_use":{"pcurve":599,"periodic_lift":[0,0],"sense":"Reverse"},"id":599,"loop_ref":149,"next":596,"prev":598,"to":147,"twin":600,"wire_ref":null},{"edge":304,"face":150,"from":147,"geometry_use":{"pcurve":600,"periodic_lift":[0,0],"sense":"Forward"},"id":600,"loop_ref":150,"next":601,"prev":603,"to":155,"twin":599,"wire_ref":null},{"edge":305,"face":150,"from":155,"geometry_use":{"pcurve":601,"periodic_lift":[0,0],"sense":"Reverse"},"id":601,"loop_ref":150,"next":602,"prev":600,"to":152,"twin":608,"wire_ref":null},{"edge":301,"face":150,"from":152,"geometry_use":{"pcurve":602,"periodic_lift":[0,0],"sense":"Reverse"},"id":602,"loop_ref":150,"next":603,"prev":601,"to":144,"twin":595,"wire_ref":null},{"edge":291,"face":150,"from":144,"geometry_use":{"pcurve":603,"periodic_lift":[0,0],"sense":"Forward"},"id":603,"loop_ref":150,"next":600,"prev":602,"to":147,"twin":578,"wire_ref":null},{"edge":300,"face":151,"from":148,"geometry_use":{"pcurve":604,"periodic_lift":[0,0],"sense":"Forward"},"id":604,"loop_ref":151,"next":605,"prev":607,"to":153,"twin":593,"wire_ref":null},{"edge":306,"face":151,"from":153,"geometry_use":{"pcurve":605,"periodic_lift":[0,0],"sense":"Forward"},"id":605,"loop_ref":151,"next":606,"prev":604,"to":154,"twin":610,"wire_ref":null},{"edge":302,"face":151,"from":154,"geometry_use":{"pcurve":606,"periodic_lift":[0,0],"sense":"Reverse"},"id":606,"loop_ref":151,"next":607,"prev":605,"to":151,"twin":597,"wire_ref":null},{"edge":307,"face":151,"from":151,"geometry_use":{"pcurve":607,"periodic_lift":[0,0],"sense":"Reverse"},"id":607,"loop_ref":151,"next":604,"prev":606,"to":148,"twin":624,"wire_ref":null},{"edge":305,"face":152,"from":152,"geometry_use":{"pcurve":608,"periodic_lift":[0,0],"sense":"Forward"},"id":608,"loop_ref":152,"next":609,"prev":611,"to":155,"twin":601,"wire_ref":null},{"edge":303,"face":152,"from":155,"geometry_use":{"pcurve":609,"periodic_lift":[0,0],"sense":"Forward"},"id":609,"loop_ref":152,"next":610,"prev":608,"to":154,"twin":598,"wire_ref":null},{"edge":306,"face":152,"from":154,"geometry_use":{"pcurve":610,"periodic_lift":[0,0],"sense":"Reverse"},"id":610,"loop_ref":152,"next":611,"prev":609,"to":153,"twin":605,"wire_ref":null},{"edge":299,"face":152,"from":153,"geometry_use":{"pcurve":611,"periodic_lift":[0,0],"sense":"Reverse"},"id":611,"loop_ref":152,"next":608,"prev":610,"to":152,"twin":592,"wire_ref":null},{"edge":308,"face":153,"from":148,"geometry_use":{"pcurve":612,"periodic_lift":[0,0],"sense":"Forward"},"id":612,"loop_ref":153,"next":613,"prev":615,"to":156,"twin":627,"wire_ref":null},{"edge":309,"face":153,"from":156,"geometry_use":{"pcurve":613,"periodic_lift":[0,0],"sense":"Reverse"},"id":613,"loop_ref":153,"next":614,"prev":612,"to":157,"twin":631,"wire_ref":null},{"edge":310,"face":153,"from":157,"geometry_use":{"pcurve":614,"periodic_lift":[0,0],"sense":"Reverse"},"id":614,"loop_ref":153,"next":615,"prev":613,"to":149,"twin":623,"wire_ref":null},{"edge":293,"face":153,"from":149,"geometry_use":{"pcurve":615,"periodic_lift":[0,0],"sense":"Forward"},"id":615,"loop_ref":153,"next":612,"prev":614,"to":148,"twin":581,"wire_ref":null},{"edge":311,"face":154,"from":150,"geometry_use":{"pcurve":616,"periodic_lift":[0,0],"sense":"Forward"},"id":616,"loop_ref":154,"next":617,"prev":619,"to":158,"twin":621,"wire_ref":null},{"edge":312,"face":154,"from":158,"geometry_use":{"pcurve":617,"periodic_lift":[0,0],"sense":"Forward"},"id":617,"loop_ref":154,"next":618,"prev":616,"to":159,"twin":635,"wire_ref":null},{"edge":313,"face":154,"from":159,"geometry_use":{"pcurve":618,"periodic_lift":[0,0],"sense":"Reverse"},"id":618,"loop_ref":154,"next":619,"prev":617,"to":151,"twin":625,"wire_ref":null},{"edge":296,"face":154,"from":151,"geometry_use":{"pcurve":619,"periodic_lift":[0,0],"sense":"Reverse"},"id":619,"loop_ref":154,"next":616,"prev":618,"to":150,"twin":585,"wire_ref":null},{"edge":314,"face":155,"from":157,"geometry_use":{"pcurve":620,"periodic_lift":[0,0],"sense":"Forward"},"id":620,"loop_ref":155,"next":621,"prev":623,"to":158,"twin":638,"wire_ref":null},{"edge":311,"face":155,"from":158,"geometry_use":{"pcurve":621,"periodic_lift":[0,0],"sense":"Reverse"},"id":621,"loop_ref":155,"next":622,"prev":620,"to":150,"twin":616,"wire_ref":null},{"edge":298,"face":155,"from":150,"geometry_use":{"pcurve":622,"periodic_lift":[0,0],"sense":"Reverse"},"id":622,"loop_ref":155,"next":623,"prev":621,"to":149,"twin":588,"wire_ref":null},{"edge":310,"face":155,"from":149,"geometry_use":{"pcurve":623,"periodic_lift":[0,0],"sense":"Forward"},"id":623,"loop_ref":155,"next":620,"prev":622,"to":157,"twin":614,"wire_ref":null},{"edge":307,"face":156,"from":148,"geometry_use":{"pcurve":624,"periodic_lift":[0,0],"sense":"Forward"},"id":624,"loop_ref":156,"next":625,"prev":627,"to":151,"twin":607,"wire_ref":null},{"edge":313,"face":156,"from":151,"geometry_use":{"pcurve":625,"periodic_lift":[0,0],"sense":"Forward"},"id":625,"loop_ref":156,"next":626,"prev":624,"to":159,"twin":618,"wire_ref":null},{"edge":315,"face":156,"from":159,"geometry_use":{"pcurve":626,"periodic_lift":[0,0],"sense":"Reverse"},"id":626,"loop_ref":156,"next":627,"prev":625,"to":156,"twin":651,"wire_ref":null},{"edge":308,"face":156,"from":156,"geometry_use":{"pcurve":627,"periodic_lift":[0,0],"sense":"Reverse"},"id":627,"loop_ref":156,"next":624,"prev":626,"to":148,"twin":612,"wire_ref":null},{"edge":316,"face":157,"from":156,"geometry_use":{"pcurve":628,"periodic_lift":[0,0],"sense":"Forward"},"id":628,"loop_ref":157,"next":629,"prev":631,"to":160,"twin":642,"wire_ref":null},{"edge":317,"face":157,"from":160,"geometry_use":{"pcurve":629,"periodic_lift":[0,0],"sense":"Reverse"},"id":629,"loop_ref":157,"next":630,"prev":628,"to":161,"twin":663,"wire_ref":null},{"edge":318,"face":157,"from":161,"geometry_use":{"pcurve":630,"periodic_lift":[0,0],"sense":"Reverse"},"id":630,"loop_ref":157,"next":631,"prev":629,"to":157,"twin":639,"wire_ref":null},{"edge":309,"face":157,"from":157,"geometry_use":{"pcurve":631,"periodic_lift":[0,0],"sense":"Forward"},"id":631,"loop_ref":157,"next":628,"prev":630,"to":156,"twin":613,"wire_ref":null},{"edge":319,"face":158,"from":158,"geometry_use":{"pcurve":632,"periodic_lift":[0,0],"sense":"Forward"},"id":632,"loop_ref":158,"next":633,"prev":635,"to":162,"twin":637,"wire_ref":null},{"edge":320,"face":158,"from":162,"geometry_use":{"pcurve":633,"periodic_lift":[0,0],"sense":"Forward"},"id":633,"loop_ref":158,"next":634,"prev":632,"to":163,"twin":667,"wire_ref":null},{"edge":321,"face":158,"from":163,"geometry_use":{"pcurve":634,"periodic_lift":[0,0],"sense":"Reverse"},"id":634,"loop_ref":158,"next":635,"prev":633,"to":159,"twin":644,"wire_ref":null},{"edge":312,"face":158,"from":159,"geometry_use":{"pcurve":635,"periodic_lift":[0,0],"sense":"Reverse"},"id":635,"loop_ref":158,"next":632,"prev":634,"to":158,"twin":617,"wire_ref":null},{"edge":322,"face":159,"from":161,"geometry_use":{"pcurve":636,"periodic_lift":[0,0],"sense":"Forward"},"id":636,"loop_ref":159,"next":637,"prev":639,"to":162,"twin":670,"wire_ref":null},{"edge":319,"face":159,"from":162,"geometry_use":{"pcurve":637,"periodic_lift":[0,0],"sense":"Reverse"},"id":637,"loop_ref":159,"next":638,"prev":636,"to":158,"twin":632,"wire_ref":null},{"edge":314,"face":159,"from":158,"geometry_use":{"pcurve":638,"periodic_lift":[0,0],"sense":"Reverse"},"id":638,"loop_ref":159,"next":639,"prev":637,"to":157,"twin":620,"wire_ref":null},{"edge":318,"face":159,"from":157,"geometry_use":{"pcurve":639,"periodic_lift":[0,0],"sense":"Forward"},"id":639,"loop_ref":159,"next":636,"prev":638,"to":161,"twin":630,"wire_ref":null},{"edge":323,"face":160,"from":164,"geometry_use":{"pcurve":640,"periodic_lift":[0,0],"sense":"Forward"},"id":640,"loop_ref":160,"next":641,"prev":643,"to":165,"twin":659,"wire_ref":null},{"edge":324,"face":160,"from":165,"geometry_use":{"pcurve":641,"periodic_lift":[0,0],"sense":"Reverse"},"id":641,"loop_ref":160,"next":642,"prev":640,"to":160,"twin":652,"wire_ref":null},{"edge":316,"face":160,"from":160,"geometry_use":{"pcurve":642,"periodic_lift":[0,0],"sense":"Reverse"},"id":642,"loop_ref":160,"next":643,"prev":641,"to":156,"twin":628,"wire_ref":null},{"edge":325,"face":160,"from":156,"geometry_use":{"pcurve":643,"periodic_lift":[0,0],"sense":"Forward"},"id":643,"loop_ref":160,"next":640,"prev":642,"to":164,"twin":650,"wire_ref":null},{"edge":321,"face":161,"from":159,"geometry_use":{"pcurve":644,"periodic_lift":[0,0],"sense":"Forward"},"id":644,"loop_ref":161,"next":645,"prev":647,"to":163,"twin":634,"wire_ref":null},{"edge":326,"face":161,"from":163,"geometry_use":{"pcurve":645,"periodic_lift":[0,0],"sense":"Forward"},"id":645,"loop_ref":161,"next":646,"prev":644,"to":166,"twin":654,"wire_ref":null},{"edge":327,"face":161,"from":166,"geometry_use":{"pcurve":646,"periodic_lift":[0,0],"sense":"Reverse"},"id":646,"loop_ref":161,"next":647,"prev":645,"to":167,"twin":657,"wire_ref":null},{"edge":328,"face":161,"from":167,"geometry_use":{"pcurve":647,"periodic_lift":[0,0],"sense":"Reverse"},"id":647,"loop_ref":161,"next":644,"prev":646,"to":159,"twin":648,"wire_ref":null},{"edge":328,"face":162,"from":159,"geometry_use":{"pcurve":648,"periodic_lift":[0,0],"sense":"Forward"},"id":648,"loop_ref":162,"next":649,"prev":651,"to":167,"twin":647,"wire_ref":null},{"edge":329,"face":162,"from":167,"geometry_use":{"pcurve":649,"periodic_lift":[0,0],"sense":"Reverse"},"id":649,"loop_ref":162,"next":650,"prev":648,"to":164,"twin":656,"wire_ref":null},{"edge":325,"face":162,"from":164,"geometry_use":{"pcurve":650,"periodic_lift":[0,0],"sense":"Reverse"},"id":650,"loop_ref":162,"next":651,"prev":649,"to":156,"twin":643,"wire_ref":null},{"edge":315,"face":162,"from":156,"geometry_use":{"pcurve":651,"periodic_lift":[0,0],"sense":"Forward"},"id":651,"loop_ref":162,"next":648,"prev":650,"to":159,"twin":626,"wire_ref":null},{"edge":324,"face":163,"from":160,"geometry_use":{"pcurve":652,"periodic_lift":[0,0],"sense":"Forward"},"id":652,"loop_ref":163,"next":653,"prev":655,"to":165,"twin":641,"wire_ref":null},{"edge":330,"face":163,"from":165,"geometry_use":{"pcurve":653,"periodic_lift":[0,0],"sense":"Forward"},"id":653,"loop_ref":163,"next":654,"prev":652,"to":166,"twin":658,"wire_ref":null},{"edge":326,"face":163,"from":166,"geometry_use":{"pcurve":654,"periodic_lift":[0,0],"sense":"Reverse"},"id":654,"loop_ref":163,"next":655,"prev":653,"to":163,"twin":645,"wire_ref":null},{"edge":331,"face":163,"from":163,"geometry_use":{"pcurve":655,"periodic_lift":[0,0],"sense":"Reverse"},"id":655,"loop_ref":163,"next":652,"prev":654,"to":160,"twin":672,"wire_ref":null},{"edge":329,"face":164,"from":164,"geometry_use":{"pcurve":656,"periodic_lift":[0,0],"sense":"Forward"},"id":656,"loop_ref":164,"next":657,"prev":659,"to":167,"twin":649,"wire_ref":null},{"edge":327,"face":164,"from":167,"geometry_use":{"pcurve":657,"periodic_lift":[0,0],"sense":"Forward"},"id":657,"loop_ref":164,"next":658,"prev":656,"to":166,"twin":646,"wire_ref":null},{"edge":330,"face":164,"from":166,"geometry_use":{"pcurve":658,"periodic_lift":[0,0],"sense":"Reverse"},"id":658,"loop_ref":164,"next":659,"prev":657,"to":165,"twin":653,"wire_ref":null},{"edge":323,"face":164,"from":165,"geometry_use":{"pcurve":659,"periodic_lift":[0,0],"sense":"Reverse"},"id":659,"loop_ref":164,"next":656,"prev":658,"to":164,"twin":640,"wire_ref":null},{"edge":332,"face":165,"from":160,"geometry_use":{"pcurve":660,"periodic_lift":[0,0],"sense":"Forward"},"id":660,"loop_ref":165,"next":661,"prev":663,"to":168,"twin":675,"wire_ref":null},{"edge":333,"face":165,"from":168,"geometry_use":{"pcurve":661,"periodic_lift":[0,0],"sense":"Reverse"},"id":661,"loop_ref":165,"next":662,"prev":660,"to":169,"twin":679,"wire_ref":null},{"edge":334,"face":165,"from":169,"geometry_use":{"pcurve":662,"periodic_lift":[0,0],"sense":"Reverse"},"id":662,"loop_ref":165,"next":663,"prev":661,"to":161,"twin":671,"wire_ref":null},{"edge":317,"face":165,"from":161,"geometry_use":{"pcurve":663,"periodic_lift":[0,0],"sense":"Forward"},"id":663,"loop_ref":165,"next":660,"prev":662,"to":160,"twin":629,"wire_ref":null},{"edge":335,"face":166,"from":162,"geometry_use":{"pcurve":664,"periodic_lift":[0,0],"sense":"Forward"},"id":664,"loop_ref":166,"next":665,"prev":667,"to":170,"twin":669,"wire_ref":null},{"edge":336,"face":166,"from":170,"geometry_use":{"pcurve":665,"periodic_lift":[0,0],"sense":"Forward"},"id":665,"loop_ref":166,"next":666,"prev":664,"to":171,"twin":683,"wire_ref":null},{"edge":337,"face":166,"from":171,"geometry_use":{"pcurve":666,"periodic_lift":[0,0],"sense":"Reverse"},"id":666,"loop_ref":166,"next":667,"prev":665,"to":163,"twin":673,"wire_ref":null},{"edge":320,"face":166,"from":163,"geometry_use":{"pcurve":667,"periodic_lift":[0,0],"sense":"Reverse"},"id":667,"loop_ref":166,"next":664,"prev":666,"to":162,"twin":633,"wire_ref":null},{"edge":338,"face":167,"from":169,"geometry_use":{"pcurve":668,"periodic_lift":[0,0],"sense":"Forward"},"id":668,"loop_ref":167,"next":669,"prev":671,"to":170,"twin":686,"wire_ref":null},{"edge":335,"face":167,"from":170,"geometry_use":{"pcurve":669,"periodic_lift":[0,0],"sense":"Reverse"},"id":669,"loop_ref":167,"next":670,"prev":668,"to":162,"twin":664,"wire_ref":null},{"edge":322,"face":167,"from":162,"geometry_use":{"pcurve":670,"periodic_lift":[0,0],"sense":"Reverse"},"id":670,"loop_ref":167,"next":671,"prev":669,"to":161,"twin":636,"wire_ref":null},{"edge":334,"face":167,"from":161,"geometry_use":{"pcurve":671,"periodic_lift":[0,0],"sense":"Forward"},"id":671,"loop_ref":167,"next":668,"prev":670,"to":169,"twin":662,"wire_ref":null},{"edge":331,"face":168,"from":160,"geometry_use":{"pcurve":672,"periodic_lift":[0,0],"sense":"Forward"},"id":672,"loop_ref":168,"next":673,"prev":675,"to":163,"twin":655,"wire_ref":null},{"edge":337,"face":168,"from":163,"geometry_use":{"pcurve":673,"periodic_lift":[0,0],"sense":"Forward"},"id":673,"loop_ref":168,"next":674,"prev":672,"to":171,"twin":666,"wire_ref":null},{"edge":339,"face":168,"from":171,"geometry_use":{"pcurve":674,"periodic_lift":[0,0],"sense":"Reverse"},"id":674,"loop_ref":168,"next":675,"prev":673,"to":168,"twin":699,"wire_ref":null},{"edge":332,"face":168,"from":168,"geometry_use":{"pcurve":675,"periodic_lift":[0,0],"sense":"Reverse"},"id":675,"loop_ref":168,"next":672,"prev":674,"to":160,"twin":660,"wire_ref":null},{"edge":340,"face":169,"from":168,"geometry_use":{"pcurve":676,"periodic_lift":[0,0],"sense":"Forward"},"id":676,"loop_ref":169,"next":677,"prev":679,"to":172,"twin":690,"wire_ref":null},{"edge":341,"face":169,"from":172,"geometry_use":{"pcurve":677,"periodic_lift":[0,0],"sense":"Reverse"},"id":677,"loop_ref":169,"next":678,"prev":676,"to":173,"twin":711,"wire_ref":null},{"edge":342,"face":169,"from":173,"geometry_use":{"pcurve":678,"periodic_lift":[0,0],"sense":"Reverse"},"id":678,"loop_ref":169,"next":679,"prev":677,"to":169,"twin":687,"wire_ref":null},{"edge":333,"face":169,"from":169,"geometry_use":{"pcurve":679,"periodic_lift":[0,0],"sense":"Forward"},"id":679,"loop_ref":169,"next":676,"prev":678,"to":168,"twin":661,"wire_ref":null},{"edge":343,"face":170,"from":170,"geometry_use":{"pcurve":680,"periodic_lift":[0,0],"sense":"Forward"},"id":680,"loop_ref":170,"next":681,"prev":683,"to":174,"twin":685,"wire_ref":null},{"edge":344,"face":170,"from":174,"geometry_use":{"pcurve":681,"periodic_lift":[0,0],"sense":"Forward"},"id":681,"loop_ref":170,"next":682,"prev":680,"to":175,"twin":715,"wire_ref":null},{"edge":345,"face":170,"from":175,"geometry_use":{"pcurve":682,"periodic_lift":[0,0],"sense":"Reverse"},"id":682,"loop_ref":170,"next":683,"prev":681,"to":171,"twin":692,"wire_ref":null},{"edge":336,"face":170,"from":171,"geometry_use":{"pcurve":683,"periodic_lift":[0,0],"sense":"Reverse"},"id":683,"loop_ref":170,"next":680,"prev":682,"to":170,"twin":665,"wire_ref":null},{"edge":346,"face":171,"from":173,"geometry_use":{"pcurve":684,"periodic_lift":[0,0],"sense":"Forward"},"id":684,"loop_ref":171,"next":685,"prev":687,"to":174,"twin":718,"wire_ref":null},{"edge":343,"face":171,"from":174,"geometry_use":{"pcurve":685,"periodic_lift":[0,0],"sense":"Reverse"},"id":685,"loop_ref":171,"next":686,"prev":684,"to":170,"twin":680,"wire_ref":null},{"edge":338,"face":171,"from":170,"geometry_use":{"pcurve":686,"periodic_lift":[0,0],"sense":"Reverse"},"id":686,"loop_ref":171,"next":687,"prev":685,"to":169,"twin":668,"wire_ref":null},{"edge":342,"face":171,"from":169,"geometry_use":{"pcurve":687,"periodic_lift":[0,0],"sense":"Forward"},"id":687,"loop_ref":171,"next":684,"prev":686,"to":173,"twin":678,"wire_ref":null},{"edge":347,"face":172,"from":176,"geometry_use":{"pcurve":688,"periodic_lift":[0,0],"sense":"Forward"},"id":688,"loop_ref":172,"next":689,"prev":691,"to":177,"twin":707,"wire_ref":null},{"edge":348,"face":172,"from":177,"geometry_use":{"pcurve":689,"periodic_lift":[0,0],"sense":"Reverse"},"id":689,"loop_ref":172,"next":690,"prev":688,"to":172,"twin":700,"wire_ref":null},{"edge":340,"face":172,"from":172,"geometry_use":{"pcurve":690,"periodic_lift":[0,0],"sense":"Reverse"},"id":690,"loop_ref":172,"next":691,"prev":689,"to":168,"twin":676,"wire_ref":null},{"edge":349,"face":172,"from":168,"geometry_use":{"pcurve":691,"periodic_lift":[0,0],"sense":"Forward"},"id":691,"loop_ref":172,"next":688,"prev":690,"to":176,"twin":698,"wire_ref":null},{"edge":345,"face":173,"from":171,"geometry_use":{"pcurve":692,"periodic_lift":[0,0],"sense":"Forward"},"id":692,"loop_ref":173,"next":693,"prev":695,"to":175,"twin":682,"wire_ref":null},{"edge":350,"face":173,"from":175,"geometry_use":{"pcurve":693,"periodic_lift":[0,0],"sense":"Forward"},"id":693,"loop_ref":173,"next":694,"prev":692,"to":178,"twin":702,"wire_ref":null},{"edge":351,"face":173,"from":178,"geometry_use":{"pcurve":694,"periodic_lift":[0,0],"sense":"Reverse"},"id":694,"loop_ref":173,"next":695,"prev":693,"to":179,"twin":705,"wire_ref":null},{"edge":352,"face":173,"from":179,"geometry_use":{"pcurve":695,"periodic_lift":[0,0],"sense":"Reverse"},"id":695,"loop_ref":173,"next":692,"prev":694,"to":171,"twin":696,"wire_ref":null},{"edge":352,"face":174,"from":171,"geometry_use":{"pcurve":696,"periodic_lift":[0,0],"sense":"Forward"},"id":696,"loop_ref":174,"next":697,"prev":699,"to":179,"twin":695,"wire_ref":null},{"edge":353,"face":174,"from":179,"geometry_use":{"pcurve":697,"periodic_lift":[0,0],"sense":"Reverse"},"id":697,"loop_ref":174,"next":698,"prev":696,"to":176,"twin":704,"wire_ref":null},{"edge":349,"face":174,"from":176,"geometry_use":{"pcurve":698,"periodic_lift":[0,0],"sense":"Reverse"},"id":698,"loop_ref":174,"next":699,"prev":697,"to":168,"twin":691,"wire_ref":null},{"edge":339,"face":174,"from":168,"geometry_use":{"pcurve":699,"periodic_lift":[0,0],"sense":"Forward"},"id":699,"loop_ref":174,"next":696,"prev":698,"to":171,"twin":674,"wire_ref":null},{"edge":348,"face":175,"from":172,"geometry_use":{"pcurve":700,"periodic_lift":[0,0],"sense":"Forward"},"id":700,"loop_ref":175,"next":701,"prev":703,"to":177,"twin":689,"wire_ref":null},{"edge":354,"face":175,"from":177,"geometry_use":{"pcurve":701,"periodic_lift":[0,0],"sense":"Forward"},"id":701,"loop_ref":175,"next":702,"prev":700,"to":178,"twin":706,"wire_ref":null},{"edge":350,"face":175,"from":178,"geometry_use":{"pcurve":702,"periodic_lift":[0,0],"sense":"Reverse"},"id":702,"loop_ref":175,"next":703,"prev":701,"to":175,"twin":693,"wire_ref":null},{"edge":355,"face":175,"from":175,"geometry_use":{"pcurve":703,"periodic_lift":[0,0],"sense":"Reverse"},"id":703,"loop_ref":175,"next":700,"prev":702,"to":172,"twin":720,"wire_ref":null},{"edge":353,"face":176,"from":176,"geometry_use":{"pcurve":704,"periodic_lift":[0,0],"sense":"Forward"},"id":704,"loop_ref":176,"next":705,"prev":707,"to":179,"twin":697,"wire_ref":null},{"edge":351,"face":176,"from":179,"geometry_use":{"pcurve":705,"periodic_lift":[0,0],"sense":"Forward"},"id":705,"loop_ref":176,"next":706,"prev":704,"to":178,"twin":694,"wire_ref":null},{"edge":354,"face":176,"from":178,"geometry_use":{"pcurve":706,"periodic_lift":[0,0],"sense":"Reverse"},"id":706,"loop_ref":176,"next":707,"prev":705,"to":177,"twin":701,"wire_ref":null},{"edge":347,"face":176,"from":177,"geometry_use":{"pcurve":707,"periodic_lift":[0,0],"sense":"Reverse"},"id":707,"loop_ref":176,"next":704,"prev":706,"to":176,"twin":688,"wire_ref":null},{"edge":356,"face":177,"from":172,"geometry_use":{"pcurve":708,"periodic_lift":[0,0],"sense":"Forward"},"id":708,"loop_ref":177,"next":709,"prev":711,"to":180,"twin":723,"wire_ref":null},{"edge":357,"face":177,"from":180,"geometry_use":{"pcurve":709,"periodic_lift":[0,0],"sense":"Reverse"},"id":709,"loop_ref":177,"next":710,"prev":708,"to":181,"twin":727,"wire_ref":null},{"edge":358,"face":177,"from":181,"geometry_use":{"pcurve":710,"periodic_lift":[0,0],"sense":"Reverse"},"id":710,"loop_ref":177,"next":711,"prev":709,"to":173,"twin":719,"wire_ref":null},{"edge":341,"face":177,"from":173,"geometry_use":{"pcurve":711,"periodic_lift":[0,0],"sense":"Forward"},"id":711,"loop_ref":177,"next":708,"prev":710,"to":172,"twin":677,"wire_ref":null},{"edge":359,"face":178,"from":174,"geometry_use":{"pcurve":712,"periodic_lift":[0,0],"sense":"Forward"},"id":712,"loop_ref":178,"next":713,"prev":715,"to":182,"twin":717,"wire_ref":null},{"edge":360,"face":178,"from":182,"geometry_use":{"pcurve":713,"periodic_lift":[0,0],"sense":"Forward"},"id":713,"loop_ref":178,"next":714,"prev":712,"to":183,"twin":731,"wire_ref":null},{"edge":361,"face":178,"from":183,"geometry_use":{"pcurve":714,"periodic_lift":[0,0],"sense":"Reverse"},"id":714,"loop_ref":178,"next":715,"prev":713,"to":175,"twin":721,"wire_ref":null},{"edge":344,"face":178,"from":175,"geometry_use":{"pcurve":715,"periodic_lift":[0,0],"sense":"Reverse"},"id":715,"loop_ref":178,"next":712,"prev":714,"to":174,"twin":681,"wire_ref":null},{"edge":362,"face":179,"from":181,"geometry_use":{"pcurve":716,"periodic_lift":[0,0],"sense":"Forward"},"id":716,"loop_ref":179,"next":717,"prev":719,"to":182,"twin":734,"wire_ref":null},{"edge":359,"face":179,"from":182,"geometry_use":{"pcurve":717,"periodic_lift":[0,0],"sense":"Reverse"},"id":717,"loop_ref":179,"next":718,"prev":716,"to":174,"twin":712,"wire_ref":null},{"edge":346,"face":179,"from":174,"geometry_use":{"pcurve":718,"periodic_lift":[0,0],"sense":"Reverse"},"id":718,"loop_ref":179,"next":719,"prev":717,"to":173,"twin":684,"wire_ref":null},{"edge":358,"face":179,"from":173,"geometry_use":{"pcurve":719,"periodic_lift":[0,0],"sense":"Forward"},"id":719,"loop_ref":179,"next":716,"prev":718,"to":181,"twin":710,"wire_ref":null},{"edge":355,"face":180,"from":172,"geometry_use":{"pcurve":720,"periodic_lift":[0,0],"sense":"Forward"},"id":720,"loop_ref":180,"next":721,"prev":723,"to":175,"twin":703,"wire_ref":null},{"edge":361,"face":180,"from":175,"geometry_use":{"pcurve":721,"periodic_lift":[0,0],"sense":"Forward"},"id":721,"loop_ref":180,"next":722,"prev":720,"to":183,"twin":714,"wire_ref":null},{"edge":363,"face":180,"from":183,"geometry_use":{"pcurve":722,"periodic_lift":[0,0],"sense":"Reverse"},"id":722,"loop_ref":180,"next":723,"prev":721,"to":180,"twin":747,"wire_ref":null},{"edge":356,"face":180,"from":180,"geometry_use":{"pcurve":723,"periodic_lift":[0,0],"sense":"Reverse"},"id":723,"loop_ref":180,"next":720,"prev":722,"to":172,"twin":708,"wire_ref":null},{"edge":364,"face":181,"from":180,"geometry_use":{"pcurve":724,"periodic_lift":[0,0],"sense":"Forward"},"id":724,"loop_ref":181,"next":725,"prev":727,"to":184,"twin":738,"wire_ref":null},{"edge":365,"face":181,"from":184,"geometry_use":{"pcurve":725,"periodic_lift":[0,0],"sense":"Reverse"},"id":725,"loop_ref":181,"next":726,"prev":724,"to":185,"twin":759,"wire_ref":null},{"edge":366,"face":181,"from":185,"geometry_use":{"pcurve":726,"periodic_lift":[0,0],"sense":"Reverse"},"id":726,"loop_ref":181,"next":727,"prev":725,"to":181,"twin":735,"wire_ref":null},{"edge":357,"face":181,"from":181,"geometry_use":{"pcurve":727,"periodic_lift":[0,0],"sense":"Forward"},"id":727,"loop_ref":181,"next":724,"prev":726,"to":180,"twin":709,"wire_ref":null},{"edge":367,"face":182,"from":182,"geometry_use":{"pcurve":728,"periodic_lift":[0,0],"sense":"Forward"},"id":728,"loop_ref":182,"next":729,"prev":731,"to":186,"twin":733,"wire_ref":null},{"edge":368,"face":182,"from":186,"geometry_use":{"pcurve":729,"periodic_lift":[0,0],"sense":"Forward"},"id":729,"loop_ref":182,"next":730,"prev":728,"to":187,"twin":763,"wire_ref":null},{"edge":369,"face":182,"from":187,"geometry_use":{"pcurve":730,"periodic_lift":[0,0],"sense":"Reverse"},"id":730,"loop_ref":182,"next":731,"prev":729,"to":183,"twin":740,"wire_ref":null},{"edge":360,"face":182,"from":183,"geometry_use":{"pcurve":731,"periodic_lift":[0,0],"sense":"Reverse"},"id":731,"loop_ref":182,"next":728,"prev":730,"to":182,"twin":713,"wire_ref":null},{"edge":370,"face":183,"from":185,"geometry_use":{"pcurve":732,"periodic_lift":[0,0],"sense":"Forward"},"id":732,"loop_ref":183,"next":733,"prev":735,"to":186,"twin":766,"wire_ref":null},{"edge":367,"face":183,"from":186,"geometry_use":{"pcurve":733,"periodic_lift":[0,0],"sense":"Reverse"},"id":733,"loop_ref":183,"next":734,"prev":732,"to":182,"twin":728,"wire_ref":null},{"edge":362,"face":183,"from":182,"geometry_use":{"pcurve":734,"periodic_lift":[0,0],"sense":"Reverse"},"id":734,"loop_ref":183,"next":735,"prev":733,"to":181,"twin":716,"wire_ref":null},{"edge":366,"face":183,"from":181,"geometry_use":{"pcurve":735,"periodic_lift":[0,0],"sense":"Forward"},"id":735,"loop_ref":183,"next":732,"prev":734,"to":185,"twin":726,"wire_ref":null},{"edge":371,"face":184,"from":188,"geometry_use":{"pcurve":736,"periodic_lift":[0,0],"sense":"Forward"},"id":736,"loop_ref":184,"next":737,"prev":739,"to":189,"twin":755,"wire_ref":null},{"edge":372,"face":184,"from":189,"geometry_use":{"pcurve":737,"periodic_lift":[0,0],"sense":"Reverse"},"id":737,"loop_ref":184,"next":738,"prev":736,"to":184,"twin":748,"wire_ref":null},{"edge":364,"face":184,"from":184,"geometry_use":{"pcurve":738,"periodic_lift":[0,0],"sense":"Reverse"},"id":738,"loop_ref":184,"next":739,"prev":737,"to":180,"twin":724,"wire_ref":null},{"edge":373,"face":184,"from":180,"geometry_use":{"pcurve":739,"periodic_lift":[0,0],"sense":"Forward"},"id":739,"loop_ref":184,"next":736,"prev":738,"to":188,"twin":746,"wire_ref":null},{"edge":369,"face":185,"from":183,"geometry_use":{"pcurve":740,"periodic_lift":[0,0],"sense":"Forward"},"id":740,"loop_ref":185,"next":741,"prev":743,"to":187,"twin":730,"wire_ref":null},{"edge":374,"face":185,"from":187,"geometry_use":{"pcurve":741,"periodic_lift":[0,0],"sense":"Forward"},"id":741,"loop_ref":185,"next":742,"prev":740,"to":190,"twin":750,"wire_ref":null},{"edge":375,"face":185,"from":190,"geometry_use":{"pcurve":742,"periodic_lift":[0,0],"sense":"Reverse"},"id":742,"loop_ref":185,"next":743,"prev":741,"to":191,"twin":753,"wire_ref":null},{"edge":376,"face":185,"from":191,"geometry_use":{"pcurve":743,"periodic_lift":[0,0],"sense":"Reverse"},"id":743,"loop_ref":185,"next":740,"prev":742,"to":183,"twin":744,"wire_ref":null},{"edge":376,"face":186,"from":183,"geometry_use":{"pcurve":744,"periodic_lift":[0,0],"sense":"Forward"},"id":744,"loop_ref":186,"next":745,"prev":747,"to":191,"twin":743,"wire_ref":null},{"edge":377,"face":186,"from":191,"geometry_use":{"pcurve":745,"periodic_lift":[0,0],"sense":"Reverse"},"id":745,"loop_ref":186,"next":746,"prev":744,"to":188,"twin":752,"wire_ref":null},{"edge":373,"face":186,"from":188,"geometry_use":{"pcurve":746,"periodic_lift":[0,0],"sense":"Reverse"},"id":746,"loop_ref":186,"next":747,"prev":745,"to":180,"twin":739,"wire_ref":null},{"edge":363,"face":186,"from":180,"geometry_use":{"pcurve":747,"periodic_lift":[0,0],"sense":"Forward"},"id":747,"loop_ref":186,"next":744,"prev":746,"to":183,"twin":722,"wire_ref":null},{"edge":372,"face":187,"from":184,"geometry_use":{"pcurve":748,"periodic_lift":[0,0],"sense":"Forward"},"id":748,"loop_ref":187,"next":749,"prev":751,"to":189,"twin":737,"wire_ref":null},{"edge":378,"face":187,"from":189,"geometry_use":{"pcurve":749,"periodic_lift":[0,0],"sense":"Forward"},"id":749,"loop_ref":187,"next":750,"prev":748,"to":190,"twin":754,"wire_ref":null},{"edge":374,"face":187,"from":190,"geometry_use":{"pcurve":750,"periodic_lift":[0,0],"sense":"Reverse"},"id":750,"loop_ref":187,"next":751,"prev":749,"to":187,"twin":741,"wire_ref":null},{"edge":379,"face":187,"from":187,"geometry_use":{"pcurve":751,"periodic_lift":[0,0],"sense":"Reverse"},"id":751,"loop_ref":187,"next":748,"prev":750,"to":184,"twin":768,"wire_ref":null},{"edge":377,"face":188,"from":188,"geometry_use":{"pcurve":752,"periodic_lift":[0,0],"sense":"Forward"},"id":752,"loop_ref":188,"next":753,"prev":755,"to":191,"twin":745,"wire_ref":null},{"edge":375,"face":188,"from":191,"geometry_use":{"pcurve":753,"periodic_lift":[0,0],"sense":"Forward"},"id":753,"loop_ref":188,"next":754,"prev":752,"to":190,"twin":742,"wire_ref":null},{"edge":378,"face":188,"from":190,"geometry_use":{"pcurve":754,"periodic_lift":[0,0],"sense":"Reverse"},"id":754,"loop_ref":188,"next":755,"prev":753,"to":189,"twin":749,"wire_ref":null},{"edge":371,"face":188,"from":189,"geometry_use":{"pcurve":755,"periodic_lift":[0,0],"sense":"Reverse"},"id":755,"loop_ref":188,"next":752,"prev":754,"to":188,"twin":736,"wire_ref":null},{"edge":380,"face":189,"from":184,"geometry_use":{"pcurve":756,"periodic_lift":[0,0],"sense":"Forward"},"id":756,"loop_ref":189,"next":757,"prev":759,"to":192,"twin":771,"wire_ref":null},{"edge":381,"face":189,"from":192,"geometry_use":{"pcurve":757,"periodic_lift":[0,0],"sense":"Reverse"},"id":757,"loop_ref":189,"next":758,"prev":756,"to":193,"twin":775,"wire_ref":null},{"edge":382,"face":189,"from":193,"geometry_use":{"pcurve":758,"periodic_lift":[0,0],"sense":"Reverse"},"id":758,"loop_ref":189,"next":759,"prev":757,"to":185,"twin":767,"wire_ref":null},{"edge":365,"face":189,"from":185,"geometry_use":{"pcurve":759,"periodic_lift":[0,0],"sense":"Forward"},"id":759,"loop_ref":189,"next":756,"prev":758,"to":184,"twin":725,"wire_ref":null},{"edge":383,"face":190,"from":186,"geometry_use":{"pcurve":760,"periodic_lift":[0,0],"sense":"Forward"},"id":760,"loop_ref":190,"next":761,"prev":763,"to":194,"twin":765,"wire_ref":null},{"edge":384,"face":190,"from":194,"geometry_use":{"pcurve":761,"periodic_lift":[0,0],"sense":"Forward"},"id":761,"loop_ref":190,"next":762,"prev":760,"to":195,"twin":779,"wire_ref":null},{"edge":385,"face":190,"from":195,"geometry_use":{"pcurve":762,"periodic_lift":[0,0],"sense":"Reverse"},"id":762,"loop_ref":190,"next":763,"prev":761,"to":187,"twin":769,"wire_ref":null},{"edge":368,"face":190,"from":187,"geometry_use":{"pcurve":763,"periodic_lift":[0,0],"sense":"Reverse"},"id":763,"loop_ref":190,"next":760,"prev":762,"to":186,"twin":729,"wire_ref":null},{"edge":386,"face":191,"from":193,"geometry_use":{"pcurve":764,"periodic_lift":[0,0],"sense":"Forward"},"id":764,"loop_ref":191,"next":765,"prev":767,"to":194,"twin":782,"wire_ref":null},{"edge":383,"face":191,"from":194,"geometry_use":{"pcurve":765,"periodic_lift":[0,0],"sense":"Reverse"},"id":765,"loop_ref":191,"next":766,"prev":764,"to":186,"twin":760,"wire_ref":null},{"edge":370,"face":191,"from":186,"geometry_use":{"pcurve":766,"periodic_lift":[0,0],"sense":"Reverse"},"id":766,"loop_ref":191,"next":767,"prev":765,"to":185,"twin":732,"wire_ref":null},{"edge":382,"face":191,"from":185,"geometry_use":{"pcurve":767,"periodic_lift":[0,0],"sense":"Forward"},"id":767,"loop_ref":191,"next":764,"prev":766,"to":193,"twin":758,"wire_ref":null},{"edge":379,"face":192,"from":184,"geometry_use":{"pcurve":768,"periodic_lift":[0,0],"sense":"Forward"},"id":768,"loop_ref":192,"next":769,"prev":771,"to":187,"twin":751,"wire_ref":null},{"edge":385,"face":192,"from":187,"geometry_use":{"pcurve":769,"periodic_lift":[0,0],"sense":"Forward"},"id":769,"loop_ref":192,"next":770,"prev":768,"to":195,"twin":762,"wire_ref":null},{"edge":387,"face":192,"from":195,"geometry_use":{"pcurve":770,"periodic_lift":[0,0],"sense":"Reverse"},"id":770,"loop_ref":192,"next":771,"prev":769,"to":192,"twin":795,"wire_ref":null},{"edge":380,"face":192,"from":192,"geometry_use":{"pcurve":771,"periodic_lift":[0,0],"sense":"Reverse"},"id":771,"loop_ref":192,"next":768,"prev":770,"to":184,"twin":756,"wire_ref":null},{"edge":388,"face":193,"from":192,"geometry_use":{"pcurve":772,"periodic_lift":[0,0],"sense":"Forward"},"id":772,"loop_ref":193,"next":773,"prev":775,"to":196,"twin":786,"wire_ref":null},{"edge":389,"face":193,"from":196,"geometry_use":{"pcurve":773,"periodic_lift":[0,0],"sense":"Reverse"},"id":773,"loop_ref":193,"next":774,"prev":772,"to":197,"twin":807,"wire_ref":null},{"edge":390,"face":193,"from":197,"geometry_use":{"pcurve":774,"periodic_lift":[0,0],"sense":"Reverse"},"id":774,"loop_ref":193,"next":775,"prev":773,"to":193,"twin":783,"wire_ref":null},{"edge":381,"face":193,"from":193,"geometry_use":{"pcurve":775,"periodic_lift":[0,0],"sense":"Forward"},"id":775,"loop_ref":193,"next":772,"prev":774,"to":192,"twin":757,"wire_ref":null},{"edge":391,"face":194,"from":194,"geometry_use":{"pcurve":776,"periodic_lift":[0,0],"sense":"Forward"},"id":776,"loop_ref":194,"next":777,"prev":779,"to":198,"twin":781,"wire_ref":null},{"edge":392,"face":194,"from":198,"geometry_use":{"pcurve":777,"periodic_lift":[0,0],"sense":"Forward"},"id":777,"loop_ref":194,"next":778,"prev":776,"to":199,"twin":811,"wire_ref":null},{"edge":393,"face":194,"from":199,"geometry_use":{"pcurve":778,"periodic_lift":[0,0],"sense":"Reverse"},"id":778,"loop_ref":194,"next":779,"prev":777,"to":195,"twin":788,"wire_ref":null},{"edge":384,"face":194,"from":195,"geometry_use":{"pcurve":779,"periodic_lift":[0,0],"sense":"Reverse"},"id":779,"loop_ref":194,"next":776,"prev":778,"to":194,"twin":761,"wire_ref":null},{"edge":394,"face":195,"from":197,"geometry_use":{"pcurve":780,"periodic_lift":[0,0],"sense":"Forward"},"id":780,"loop_ref":195,"next":781,"prev":783,"to":198,"twin":814,"wire_ref":null},{"edge":391,"face":195,"from":198,"geometry_use":{"pcurve":781,"periodic_lift":[0,0],"sense":"Reverse"},"id":781,"loop_ref":195,"next":782,"prev":780,"to":194,"twin":776,"wire_ref":null},{"edge":386,"face":195,"from":194,"geometry_use":{"pcurve":782,"periodic_lift":[0,0],"sense":"Reverse"},"id":782,"loop_ref":195,"next":783,"prev":781,"to":193,"twin":764,"wire_ref":null},{"edge":390,"face":195,"from":193,"geometry_use":{"pcurve":783,"periodic_lift":[0,0],"sense":"Forward"},"id":783,"loop_ref":195,"next":780,"prev":782,"to":197,"twin":774,"wire_ref":null},{"edge":395,"face":196,"from":200,"geometry_use":{"pcurve":784,"periodic_lift":[0,0],"sense":"Forward"},"id":784,"loop_ref":196,"next":785,"prev":787,"to":201,"twin":803,"wire_ref":null},{"edge":396,"face":196,"from":201,"geometry_use":{"pcurve":785,"periodic_lift":[0,0],"sense":"Reverse"},"id":785,"loop_ref":196,"next":786,"prev":784,"to":196,"twin":796,"wire_ref":null},{"edge":388,"face":196,"from":196,"geometry_use":{"pcurve":786,"periodic_lift":[0,0],"sense":"Reverse"},"id":786,"loop_ref":196,"next":787,"prev":785,"to":192,"twin":772,"wire_ref":null},{"edge":397,"face":196,"from":192,"geometry_use":{"pcurve":787,"periodic_lift":[0,0],"sense":"Forward"},"id":787,"loop_ref":196,"next":784,"prev":786,"to":200,"twin":794,"wire_ref":null},{"edge":393,"face":197,"from":195,"geometry_use":{"pcurve":788,"periodic_lift":[0,0],"sense":"Forward"},"id":788,"loop_ref":197,"next":789,"prev":791,"to":199,"twin":778,"wire_ref":null},{"edge":398,"face":197,"from":199,"geometry_use":{"pcurve":789,"periodic_lift":[0,0],"sense":"Forward"},"id":789,"loop_ref":197,"next":790,"prev":788,"to":202,"twin":798,"wire_ref":null},{"edge":399,"face":197,"from":202,"geometry_use":{"pcurve":790,"periodic_lift":[0,0],"sense":"Reverse"},"id":790,"loop_ref":197,"next":791,"prev":789,"to":203,"twin":801,"wire_ref":null},{"edge":400,"face":197,"from":203,"geometry_use":{"pcurve":791,"periodic_lift":[0,0],"sense":"Reverse"},"id":791,"loop_ref":197,"next":788,"prev":790,"to":195,"twin":792,"wire_ref":null},{"edge":400,"face":198,"from":195,"geometry_use":{"pcurve":792,"periodic_lift":[0,0],"sense":"Forward"},"id":792,"loop_ref":198,"next":793,"prev":795,"to":203,"twin":791,"wire_ref":null},{"edge":401,"face":198,"from":203,"geometry_use":{"pcurve":793,"periodic_lift":[0,0],"sense":"Reverse"},"id":793,"loop_ref":198,"next":794,"prev":792,"to":200,"twin":800,"wire_ref":null},{"edge":397,"face":198,"from":200,"geometry_use":{"pcurve":794,"periodic_lift":[0,0],"sense":"Reverse"},"id":794,"loop_ref":198,"next":795,"prev":793,"to":192,"twin":787,"wire_ref":null},{"edge":387,"face":198,"from":192,"geometry_use":{"pcurve":795,"periodic_lift":[0,0],"sense":"Forward"},"id":795,"loop_ref":198,"next":792,"prev":794,"to":195,"twin":770,"wire_ref":null},{"edge":396,"face":199,"from":196,"geometry_use":{"pcurve":796,"periodic_lift":[0,0],"sense":"Forward"},"id":796,"loop_ref":199,"next":797,"prev":799,"to":201,"twin":785,"wire_ref":null},{"edge":402,"face":199,"from":201,"geometry_use":{"pcurve":797,"periodic_lift":[0,0],"sense":"Forward"},"id":797,"loop_ref":199,"next":798,"prev":796,"to":202,"twin":802,"wire_ref":null},{"edge":398,"face":199,"from":202,"geometry_use":{"pcurve":798,"periodic_lift":[0,0],"sense":"Reverse"},"id":798,"loop_ref":199,"next":799,"prev":797,"to":199,"twin":789,"wire_ref":null},{"edge":403,"face":199,"from":199,"geometry_use":{"pcurve":799,"periodic_lift":[0,0],"sense":"Reverse"},"id":799,"loop_ref":199,"next":796,"prev":798,"to":196,"twin":816,"wire_ref":null},{"edge":401,"face":200,"from":200,"geometry_use":{"pcurve":800,"periodic_lift":[0,0],"sense":"Forward"},"id":800,"loop_ref":200,"next":801,"prev":803,"to":203,"twin":793,"wire_ref":null},{"edge":399,"face":200,"from":203,"geometry_use":{"pcurve":801,"periodic_lift":[0,0],"sense":"Forward"},"id":801,"loop_ref":200,"next":802,"prev":800,"to":202,"twin":790,"wire_ref":null},{"edge":402,"face":200,"from":202,"geometry_use":{"pcurve":802,"periodic_lift":[0,0],"sense":"Reverse"},"id":802,"loop_ref":200,"next":803,"prev":801,"to":201,"twin":797,"wire_ref":null},{"edge":395,"face":200,"from":201,"geometry_use":{"pcurve":803,"periodic_lift":[0,0],"sense":"Reverse"},"id":803,"loop_ref":200,"next":800,"prev":802,"to":200,"twin":784,"wire_ref":null},{"edge":404,"face":201,"from":196,"geometry_use":{"pcurve":804,"periodic_lift":[0,0],"sense":"Forward"},"id":804,"loop_ref":201,"next":805,"prev":807,"to":204,"twin":819,"wire_ref":null},{"edge":405,"face":201,"from":204,"geometry_use":{"pcurve":805,"periodic_lift":[0,0],"sense":"Reverse"},"id":805,"loop_ref":201,"next":806,"prev":804,"to":205,"twin":823,"wire_ref":null},{"edge":406,"face":201,"from":205,"geometry_use":{"pcurve":806,"periodic_lift":[0,0],"sense":"Reverse"},"id":806,"loop_ref":201,"next":807,"prev":805,"to":197,"twin":815,"wire_ref":null},{"edge":389,"face":201,"from":197,"geometry_use":{"pcurve":807,"periodic_lift":[0,0],"sense":"Forward"},"id":807,"loop_ref":201,"next":804,"prev":806,"to":196,"twin":773,"wire_ref":null},{"edge":407,"face":202,"from":198,"geometry_use":{"pcurve":808,"periodic_lift":[0,0],"sense":"Forward"},"id":808,"loop_ref":202,"next":809,"prev":811,"to":206,"twin":813,"wire_ref":null},{"edge":408,"face":202,"from":206,"geometry_use":{"pcurve":809,"periodic_lift":[0,0],"sense":"Forward"},"id":809,"loop_ref":202,"next":810,"prev":808,"to":207,"twin":827,"wire_ref":null},{"edge":409,"face":202,"from":207,"geometry_use":{"pcurve":810,"periodic_lift":[0,0],"sense":"Reverse"},"id":810,"loop_ref":202,"next":811,"prev":809,"to":199,"twin":817,"wire_ref":null},{"edge":392,"face":202,"from":199,"geometry_use":{"pcurve":811,"periodic_lift":[0,0],"sense":"Reverse"},"id":811,"loop_ref":202,"next":808,"prev":810,"to":198,"twin":777,"wire_ref":null},{"edge":410,"face":203,"from":205,"geometry_use":{"pcurve":812,"periodic_lift":[0,0],"sense":"Forward"},"id":812,"loop_ref":203,"next":813,"prev":815,"to":206,"twin":830,"wire_ref":null},{"edge":407,"face":203,"from":206,"geometry_use":{"pcurve":813,"periodic_lift":[0,0],"sense":"Reverse"},"id":813,"loop_ref":203,"next":814,"prev":812,"to":198,"twin":808,"wire_ref":null},{"edge":394,"face":203,"from":198,"geometry_use":{"pcurve":814,"periodic_lift":[0,0],"sense":"Reverse"},"id":814,"loop_ref":203,"next":815,"prev":813,"to":197,"twin":780,"wire_ref":null},{"edge":406,"face":203,"from":197,"geometry_use":{"pcurve":815,"periodic_lift":[0,0],"sense":"Forward"},"id":815,"loop_ref":203,"next":812,"prev":814,"to":205,"twin":806,"wire_ref":null},{"edge":403,"face":204,"from":196,"geometry_use":{"pcurve":816,"periodic_lift":[0,0],"sense":"Forward"},"id":816,"loop_ref":204,"next":817,"prev":819,"to":199,"twin":799,"wire_ref":null},{"edge":409,"face":204,"from":199,"geometry_use":{"pcurve":817,"periodic_lift":[0,0],"sense":"Forward"},"id":817,"loop_ref":204,"next":818,"prev":816,"to":207,"twin":810,"wire_ref":null},{"edge":411,"face":204,"from":207,"geometry_use":{"pcurve":818,"periodic_lift":[0,0],"sense":"Reverse"},"id":818,"loop_ref":204,"next":819,"prev":817,"to":204,"twin":843,"wire_ref":null},{"edge":404,"face":204,"from":204,"geometry_use":{"pcurve":819,"periodic_lift":[0,0],"sense":"Reverse"},"id":819,"loop_ref":204,"next":816,"prev":818,"to":196,"twin":804,"wire_ref":null},{"edge":412,"face":205,"from":204,"geometry_use":{"pcurve":820,"periodic_lift":[0,0],"sense":"Forward"},"id":820,"loop_ref":205,"next":821,"prev":823,"to":208,"twin":834,"wire_ref":null},{"edge":413,"face":205,"from":208,"geometry_use":{"pcurve":821,"periodic_lift":[0,0],"sense":"Reverse"},"id":821,"loop_ref":205,"next":822,"prev":820,"to":209,"twin":855,"wire_ref":null},{"edge":414,"face":205,"from":209,"geometry_use":{"pcurve":822,"periodic_lift":[0,0],"sense":"Reverse"},"id":822,"loop_ref":205,"next":823,"prev":821,"to":205,"twin":831,"wire_ref":null},{"edge":405,"face":205,"from":205,"geometry_use":{"pcurve":823,"periodic_lift":[0,0],"sense":"Forward"},"id":823,"loop_ref":205,"next":820,"prev":822,"to":204,"twin":805,"wire_ref":null},{"edge":415,"face":206,"from":206,"geometry_use":{"pcurve":824,"periodic_lift":[0,0],"sense":"Forward"},"id":824,"loop_ref":206,"next":825,"prev":827,"to":210,"twin":829,"wire_ref":null},{"edge":416,"face":206,"from":210,"geometry_use":{"pcurve":825,"periodic_lift":[0,0],"sense":"Forward"},"id":825,"loop_ref":206,"next":826,"prev":824,"to":211,"twin":859,"wire_ref":null},{"edge":417,"face":206,"from":211,"geometry_use":{"pcurve":826,"periodic_lift":[0,0],"sense":"Reverse"},"id":826,"loop_ref":206,"next":827,"prev":825,"to":207,"twin":836,"wire_ref":null},{"edge":408,"face":206,"from":207,"geometry_use":{"pcurve":827,"periodic_lift":[0,0],"sense":"Reverse"},"id":827,"loop_ref":206,"next":824,"prev":826,"to":206,"twin":809,"wire_ref":null},{"edge":418,"face":207,"from":209,"geometry_use":{"pcurve":828,"periodic_lift":[0,0],"sense":"Forward"},"id":828,"loop_ref":207,"next":829,"prev":831,"to":210,"twin":862,"wire_ref":null},{"edge":415,"face":207,"from":210,"geometry_use":{"pcurve":829,"periodic_lift":[0,0],"sense":"Reverse"},"id":829,"loop_ref":207,"next":830,"prev":828,"to":206,"twin":824,"wire_ref":null},{"edge":410,"face":207,"from":206,"geometry_use":{"pcurve":830,"periodic_lift":[0,0],"sense":"Reverse"},"id":830,"loop_ref":207,"next":831,"prev":829,"to":205,"twin":812,"wire_ref":null},{"edge":414,"face":207,"from":205,"geometry_use":{"pcurve":831,"periodic_lift":[0,0],"sense":"Forward"},"id":831,"loop_ref":207,"next":828,"prev":830,"to":209,"twin":822,"wire_ref":null},{"edge":419,"face":208,"from":212,"geometry_use":{"pcurve":832,"periodic_lift":[0,0],"sense":"Forward"},"id":832,"loop_ref":208,"next":833,"prev":835,"to":213,"twin":851,"wire_ref":null},{"edge":420,"face":208,"from":213,"geometry_use":{"pcurve":833,"periodic_lift":[0,0],"sense":"Reverse"},"id":833,"loop_ref":208,"next":834,"prev":832,"to":208,"twin":844,"wire_ref":null},{"edge":412,"face":208,"from":208,"geometry_use":{"pcurve":834,"periodic_lift":[0,0],"sense":"Reverse"},"id":834,"loop_ref":208,"next":835,"prev":833,"to":204,"twin":820,"wire_ref":null},{"edge":421,"face":208,"from":204,"geometry_use":{"pcurve":835,"periodic_lift":[0,0],"sense":"Forward"},"id":835,"loop_ref":208,"next":832,"prev":834,"to":212,"twin":842,"wire_ref":null},{"edge":417,"face":209,"from":207,"geometry_use":{"pcurve":836,"periodic_lift":[0,0],"sense":"Forward"},"id":836,"loop_ref":209,"next":837,"prev":839,"to":211,"twin":826,"wire_ref":null},{"edge":422,"face":209,"from":211,"geometry_use":{"pcurve":837,"periodic_lift":[0,0],"sense":"Forward"},"id":837,"loop_ref":209,"next":838,"prev":836,"to":214,"twin":846,"wire_ref":null},{"edge":423,"face":209,"from":214,"geometry_use":{"pcurve":838,"periodic_lift":[0,0],"sense":"Reverse"},"id":838,"loop_ref":209,"next":839,"prev":837,"to":215,"twin":849,"wire_ref":null},{"edge":424,"face":209,"from":215,"geometry_use":{"pcurve":839,"periodic_lift":[0,0],"sense":"Reverse"},"id":839,"loop_ref":209,"next":836,"prev":838,"to":207,"twin":840,"wire_ref":null},{"edge":424,"face":210,"from":207,"geometry_use":{"pcurve":840,"periodic_lift":[0,0],"sense":"Forward"},"id":840,"loop_ref":210,"next":841,"prev":843,"to":215,"twin":839,"wire_ref":null},{"edge":425,"face":210,"from":215,"geometry_use":{"pcurve":841,"periodic_lift":[0,0],"sense":"Reverse"},"id":841,"loop_ref":210,"next":842,"prev":840,"to":212,"twin":848,"wire_ref":null},{"edge":421,"face":210,"from":212,"geometry_use":{"pcurve":842,"periodic_lift":[0,0],"sense":"Reverse"},"id":842,"loop_ref":210,"next":843,"prev":841,"to":204,"twin":835,"wire_ref":null},{"edge":411,"face":210,"from":204,"geometry_use":{"pcurve":843,"periodic_lift":[0,0],"sense":"Forward"},"id":843,"loop_ref":210,"next":840,"prev":842,"to":207,"twin":818,"wire_ref":null},{"edge":420,"face":211,"from":208,"geometry_use":{"pcurve":844,"periodic_lift":[0,0],"sense":"Forward"},"id":844,"loop_ref":211,"next":845,"prev":847,"to":213,"twin":833,"wire_ref":null},{"edge":426,"face":211,"from":213,"geometry_use":{"pcurve":845,"periodic_lift":[0,0],"sense":"Forward"},"id":845,"loop_ref":211,"next":846,"prev":844,"to":214,"twin":850,"wire_ref":null},{"edge":422,"face":211,"from":214,"geometry_use":{"pcurve":846,"periodic_lift":[0,0],"sense":"Reverse"},"id":846,"loop_ref":211,"next":847,"prev":845,"to":211,"twin":837,"wire_ref":null},{"edge":427,"face":211,"from":211,"geometry_use":{"pcurve":847,"periodic_lift":[0,0],"sense":"Reverse"},"id":847,"loop_ref":211,"next":844,"prev":846,"to":208,"twin":864,"wire_ref":null},{"edge":425,"face":212,"from":212,"geometry_use":{"pcurve":848,"periodic_lift":[0,0],"sense":"Forward"},"id":848,"loop_ref":212,"next":849,"prev":851,"to":215,"twin":841,"wire_ref":null},{"edge":423,"face":212,"from":215,"geometry_use":{"pcurve":849,"periodic_lift":[0,0],"sense":"Forward"},"id":849,"loop_ref":212,"next":850,"prev":848,"to":214,"twin":838,"wire_ref":null},{"edge":426,"face":212,"from":214,"geometry_use":{"pcurve":850,"periodic_lift":[0,0],"sense":"Reverse"},"id":850,"loop_ref":212,"next":851,"prev":849,"to":213,"twin":845,"wire_ref":null},{"edge":419,"face":212,"from":213,"geometry_use":{"pcurve":851,"periodic_lift":[0,0],"sense":"Reverse"},"id":851,"loop_ref":212,"next":848,"prev":850,"to":212,"twin":832,"wire_ref":null},{"edge":428,"face":213,"from":208,"geometry_use":{"pcurve":852,"periodic_lift":[0,0],"sense":"Forward"},"id":852,"loop_ref":213,"next":853,"prev":855,"to":216,"twin":867,"wire_ref":null},{"edge":429,"face":213,"from":216,"geometry_use":{"pcurve":853,"periodic_lift":[0,0],"sense":"Reverse"},"id":853,"loop_ref":213,"next":854,"prev":852,"to":217,"twin":871,"wire_ref":null},{"edge":430,"face":213,"from":217,"geometry_use":{"pcurve":854,"periodic_lift":[0,0],"sense":"Reverse"},"id":854,"loop_ref":213,"next":855,"prev":853,"to":209,"twin":863,"wire_ref":null},{"edge":413,"face":213,"from":209,"geometry_use":{"pcurve":855,"periodic_lift":[0,0],"sense":"Forward"},"id":855,"loop_ref":213,"next":852,"prev":854,"to":208,"twin":821,"wire_ref":null},{"edge":431,"face":214,"from":210,"geometry_use":{"pcurve":856,"periodic_lift":[0,0],"sense":"Forward"},"id":856,"loop_ref":214,"next":857,"prev":859,"to":218,"twin":861,"wire_ref":null},{"edge":432,"face":214,"from":218,"geometry_use":{"pcurve":857,"periodic_lift":[0,0],"sense":"Forward"},"id":857,"loop_ref":214,"next":858,"prev":856,"to":219,"twin":875,"wire_ref":null},{"edge":433,"face":214,"from":219,"geometry_use":{"pcurve":858,"periodic_lift":[0,0],"sense":"Reverse"},"id":858,"loop_ref":214,"next":859,"prev":857,"to":211,"twin":865,"wire_ref":null},{"edge":416,"face":214,"from":211,"geometry_use":{"pcurve":859,"periodic_lift":[0,0],"sense":"Reverse"},"id":859,"loop_ref":214,"next":856,"prev":858,"to":210,"twin":825,"wire_ref":null},{"edge":434,"face":215,"from":217,"geometry_use":{"pcurve":860,"periodic_lift":[0,0],"sense":"Forward"},"id":860,"loop_ref":215,"next":861,"prev":863,"to":218,"twin":878,"wire_ref":null},{"edge":431,"face":215,"from":218,"geometry_use":{"pcurve":861,"periodic_lift":[0,0],"sense":"Reverse"},"id":861,"loop_ref":215,"next":862,"prev":860,"to":210,"twin":856,"wire_ref":null},{"edge":418,"face":215,"from":210,"geometry_use":{"pcurve":862,"periodic_lift":[0,0],"sense":"Reverse"},"id":862,"loop_ref":215,"next":863,"prev":861,"to":209,"twin":828,"wire_ref":null},{"edge":430,"face":215,"from":209,"geometry_use":{"pcurve":863,"periodic_lift":[0,0],"sense":"Forward"},"id":863,"loop_ref":215,"next":860,"prev":862,"to":217,"twin":854,"wire_ref":null},{"edge":427,"face":216,"from":208,"geometry_use":{"pcurve":864,"periodic_lift":[0,0],"sense":"Forward"},"id":864,"loop_ref":216,"next":865,"prev":867,"to":211,"twin":847,"wire_ref":null},{"edge":433,"face":216,"from":211,"geometry_use":{"pcurve":865,"periodic_lift":[0,0],"sense":"Forward"},"id":865,"loop_ref":216,"next":866,"prev":864,"to":219,"twin":858,"wire_ref":null},{"edge":435,"face":216,"from":219,"geometry_use":{"pcurve":866,"periodic_lift":[0,0],"sense":"Reverse"},"id":866,"loop_ref":216,"next":867,"prev":865,"to":216,"twin":891,"wire_ref":null},{"edge":428,"face":216,"from":216,"geometry_use":{"pcurve":867,"periodic_lift":[0,0],"sense":"Reverse"},"id":867,"loop_ref":216,"next":864,"prev":866,"to":208,"twin":852,"wire_ref":null},{"edge":436,"face":217,"from":216,"geometry_use":{"pcurve":868,"periodic_lift":[0,0],"sense":"Forward"},"id":868,"loop_ref":217,"next":869,"prev":871,"to":220,"twin":882,"wire_ref":null},{"edge":437,"face":217,"from":220,"geometry_use":{"pcurve":869,"periodic_lift":[0,0],"sense":"Reverse"},"id":869,"loop_ref":217,"next":870,"prev":868,"to":221,"twin":903,"wire_ref":null},{"edge":438,"face":217,"from":221,"geometry_use":{"pcurve":870,"periodic_lift":[0,0],"sense":"Reverse"},"id":870,"loop_ref":217,"next":871,"prev":869,"to":217,"twin":879,"wire_ref":null},{"edge":429,"face":217,"from":217,"geometry_use":{"pcurve":871,"periodic_lift":[0,0],"sense":"Forward"},"id":871,"loop_ref":217,"next":868,"prev":870,"to":216,"twin":853,"wire_ref":null},{"edge":439,"face":218,"from":218,"geometry_use":{"pcurve":872,"periodic_lift":[0,0],"sense":"Forward"},"id":872,"loop_ref":218,"next":873,"prev":875,"to":222,"twin":877,"wire_ref":null},{"edge":440,"face":218,"from":222,"geometry_use":{"pcurve":873,"periodic_lift":[0,0],"sense":"Forward"},"id":873,"loop_ref":218,"next":874,"prev":872,"to":223,"twin":907,"wire_ref":null},{"edge":441,"face":218,"from":223,"geometry_use":{"pcurve":874,"periodic_lift":[0,0],"sense":"Reverse"},"id":874,"loop_ref":218,"next":875,"prev":873,"to":219,"twin":884,"wire_ref":null},{"edge":432,"face":218,"from":219,"geometry_use":{"pcurve":875,"periodic_lift":[0,0],"sense":"Reverse"},"id":875,"loop_ref":218,"next":872,"prev":874,"to":218,"twin":857,"wire_ref":null},{"edge":442,"face":219,"from":221,"geometry_use":{"pcurve":876,"periodic_lift":[0,0],"sense":"Forward"},"id":876,"loop_ref":219,"next":877,"prev":879,"to":222,"twin":910,"wire_ref":null},{"edge":439,"face":219,"from":222,"geometry_use":{"pcurve":877,"periodic_lift":[0,0],"sense":"Reverse"},"id":877,"loop_ref":219,"next":878,"prev":876,"to":218,"twin":872,"wire_ref":null},{"edge":434,"face":219,"from":218,"geometry_use":{"pcurve":878,"periodic_lift":[0,0],"sense":"Reverse"},"id":878,"loop_ref":219,"next":879,"prev":877,"to":217,"twin":860,"wire_ref":null},{"edge":438,"face":219,"from":217,"geometry_use":{"pcurve":879,"periodic_lift":[0,0],"sense":"Forward"},"id":879,"loop_ref":219,"next":876,"prev":878,"to":221,"twin":870,"wire_ref":null},{"edge":443,"face":220,"from":224,"geometry_use":{"pcurve":880,"periodic_lift":[0,0],"sense":"Forward"},"id":880,"loop_ref":220,"next":881,"prev":883,"to":225,"twin":899,"wire_ref":null},{"edge":444,"face":220,"from":225,"geometry_use":{"pcurve":881,"periodic_lift":[0,0],"sense":"Reverse"},"id":881,"loop_ref":220,"next":882,"prev":880,"to":220,"twin":892,"wire_ref":null},{"edge":436,"face":220,"from":220,"geometry_use":{"pcurve":882,"periodic_lift":[0,0],"sense":"Reverse"},"id":882,"loop_ref":220,"next":883,"prev":881,"to":216,"twin":868,"wire_ref":null},{"edge":445,"face":220,"from":216,"geometry_use":{"pcurve":883,"periodic_lift":[0,0],"sense":"Forward"},"id":883,"loop_ref":220,"next":880,"prev":882,"to":224,"twin":890,"wire_ref":null},{"edge":441,"face":221,"from":219,"geometry_use":{"pcurve":884,"periodic_lift":[0,0],"sense":"Forward"},"id":884,"loop_ref":221,"next":885,"prev":887,"to":223,"twin":874,"wire_ref":null},{"edge":446,"face":221,"from":223,"geometry_use":{"pcurve":885,"periodic_lift":[0,0],"sense":"Forward"},"id":885,"loop_ref":221,"next":886,"prev":884,"to":226,"twin":894,"wire_ref":null},{"edge":447,"face":221,"from":226,"geometry_use":{"pcurve":886,"periodic_lift":[0,0],"sense":"Reverse"},"id":886,"loop_ref":221,"next":887,"prev":885,"to":227,"twin":897,"wire_ref":null},{"edge":448,"face":221,"from":227,"geometry_use":{"pcurve":887,"periodic_lift":[0,0],"sense":"Reverse"},"id":887,"loop_ref":221,"next":884,"prev":886,"to":219,"twin":888,"wire_ref":null},{"edge":448,"face":222,"from":219,"geometry_use":{"pcurve":888,"periodic_lift":[0,0],"sense":"Forward"},"id":888,"loop_ref":222,"next":889,"prev":891,"to":227,"twin":887,"wire_ref":null},{"edge":449,"face":222,"from":227,"geometry_use":{"pcurve":889,"periodic_lift":[0,0],"sense":"Reverse"},"id":889,"loop_ref":222,"next":890,"prev":888,"to":224,"twin":896,"wire_ref":null},{"edge":445,"face":222,"from":224,"geometry_use":{"pcurve":890,"periodic_lift":[0,0],"sense":"Reverse"},"id":890,"loop_ref":222,"next":891,"prev":889,"to":216,"twin":883,"wire_ref":null},{"edge":435,"face":222,"from":216,"geometry_use":{"pcurve":891,"periodic_lift":[0,0],"sense":"Forward"},"id":891,"loop_ref":222,"next":888,"prev":890,"to":219,"twin":866,"wire_ref":null},{"edge":444,"face":223,"from":220,"geometry_use":{"pcurve":892,"periodic_lift":[0,0],"sense":"Forward"},"id":892,"loop_ref":223,"next":893,"prev":895,"to":225,"twin":881,"wire_ref":null},{"edge":450,"face":223,"from":225,"geometry_use":{"pcurve":893,"periodic_lift":[0,0],"sense":"Forward"},"id":893,"loop_ref":223,"next":894,"prev":892,"to":226,"twin":898,"wire_ref":null},{"edge":446,"face":223,"from":226,"geometry_use":{"pcurve":894,"periodic_lift":[0,0],"sense":"Reverse"},"id":894,"loop_ref":223,"next":895,"prev":893,"to":223,"twin":885,"wire_ref":null},{"edge":451,"face":223,"from":223,"geometry_use":{"pcurve":895,"periodic_lift":[0,0],"sense":"Reverse"},"id":895,"loop_ref":223,"next":892,"prev":894,"to":220,"twin":912,"wire_ref":null},{"edge":449,"face":224,"from":224,"geometry_use":{"pcurve":896,"periodic_lift":[0,0],"sense":"Forward"},"id":896,"loop_ref":224,"next":897,"prev":899,"to":227,"twin":889,"wire_ref":null},{"edge":447,"face":224,"from":227,"geometry_use":{"pcurve":897,"periodic_lift":[0,0],"sense":"Forward"},"id":897,"loop_ref":224,"next":898,"prev":896,"to":226,"twin":886,"wire_ref":null},{"edge":450,"face":224,"from":226,"geometry_use":{"pcurve":898,"periodic_lift":[0,0],"sense":"Reverse"},"id":898,"loop_ref":224,"next":899,"prev":897,"to":225,"twin":893,"wire_ref":null},{"edge":443,"face":224,"from":225,"geometry_use":{"pcurve":899,"periodic_lift":[0,0],"sense":"Reverse"},"id":899,"loop_ref":224,"next":896,"prev":898,"to":224,"twin":880,"wire_ref":null},{"edge":452,"face":225,"from":220,"geometry_use":{"pcurve":900,"periodic_lift":[0,0],"sense":"Forward"},"id":900,"loop_ref":225,"next":901,"prev":903,"to":228,"twin":915,"wire_ref":null},{"edge":453,"face":225,"from":228,"geometry_use":{"pcurve":901,"periodic_lift":[0,0],"sense":"Reverse"},"id":901,"loop_ref":225,"next":902,"prev":900,"to":229,"twin":919,"wire_ref":null},{"edge":454,"face":225,"from":229,"geometry_use":{"pcurve":902,"periodic_lift":[0,0],"sense":"Reverse"},"id":902,"loop_ref":225,"next":903,"prev":901,"to":221,"twin":911,"wire_ref":null},{"edge":437,"face":225,"from":221,"geometry_use":{"pcurve":903,"periodic_lift":[0,0],"sense":"Forward"},"id":903,"loop_ref":225,"next":900,"prev":902,"to":220,"twin":869,"wire_ref":null},{"edge":455,"face":226,"from":222,"geometry_use":{"pcurve":904,"periodic_lift":[0,0],"sense":"Forward"},"id":904,"loop_ref":226,"next":905,"prev":907,"to":230,"twin":909,"wire_ref":null},{"edge":456,"face":226,"from":230,"geometry_use":{"pcurve":905,"periodic_lift":[0,0],"sense":"Forward"},"id":905,"loop_ref":226,"next":906,"prev":904,"to":231,"twin":923,"wire_ref":null},{"edge":457,"face":226,"from":231,"geometry_use":{"pcurve":906,"periodic_lift":[0,0],"sense":"Reverse"},"id":906,"loop_ref":226,"next":907,"prev":905,"to":223,"twin":913,"wire_ref":null},{"edge":440,"face":226,"from":223,"geometry_use":{"pcurve":907,"periodic_lift":[0,0],"sense":"Reverse"},"id":907,"loop_ref":226,"next":904,"prev":906,"to":222,"twin":873,"wire_ref":null},{"edge":458,"face":227,"from":229,"geometry_use":{"pcurve":908,"periodic_lift":[0,0],"sense":"Forward"},"id":908,"loop_ref":227,"next":909,"prev":911,"to":230,"twin":926,"wire_ref":null},{"edge":455,"face":227,"from":230,"geometry_use":{"pcurve":909,"periodic_lift":[0,0],"sense":"Reverse"},"id":909,"loop_ref":227,"next":910,"prev":908,"to":222,"twin":904,"wire_ref":null},{"edge":442,"face":227,"from":222,"geometry_use":{"pcurve":910,"periodic_lift":[0,0],"sense":"Reverse"},"id":910,"loop_ref":227,"next":911,"prev":909,"to":221,"twin":876,"wire_ref":null},{"edge":454,"face":227,"from":221,"geometry_use":{"pcurve":911,"periodic_lift":[0,0],"sense":"Forward"},"id":911,"loop_ref":227,"next":908,"prev":910,"to":229,"twin":902,"wire_ref":null},{"edge":451,"face":228,"from":220,"geometry_use":{"pcurve":912,"periodic_lift":[0,0],"sense":"Forward"},"id":912,"loop_ref":228,"next":913,"prev":915,"to":223,"twin":895,"wire_ref":null},{"edge":457,"face":228,"from":223,"geometry_use":{"pcurve":913,"periodic_lift":[0,0],"sense":"Forward"},"id":913,"loop_ref":228,"next":914,"prev":912,"to":231,"twin":906,"wire_ref":null},{"edge":459,"face":228,"from":231,"geometry_use":{"pcurve":914,"periodic_lift":[0,0],"sense":"Reverse"},"id":914,"loop_ref":228,"next":915,"prev":913,"to":228,"twin":939,"wire_ref":null},{"edge":452,"face":228,"from":228,"geometry_use":{"pcurve":915,"periodic_lift":[0,0],"sense":"Reverse"},"id":915,"loop_ref":228,"next":912,"prev":914,"to":220,"twin":900,"wire_ref":null},{"edge":460,"face":229,"from":228,"geometry_use":{"pcurve":916,"periodic_lift":[0,0],"sense":"Forward"},"id":916,"loop_ref":229,"next":917,"prev":919,"to":232,"twin":930,"wire_ref":null},{"edge":461,"face":229,"from":232,"geometry_use":{"pcurve":917,"periodic_lift":[0,0],"sense":"Reverse"},"id":917,"loop_ref":229,"next":918,"prev":916,"to":233,"twin":951,"wire_ref":null},{"edge":462,"face":229,"from":233,"geometry_use":{"pcurve":918,"periodic_lift":[0,0],"sense":"Reverse"},"id":918,"loop_ref":229,"next":919,"prev":917,"to":229,"twin":927,"wire_ref":null},{"edge":453,"face":229,"from":229,"geometry_use":{"pcurve":919,"periodic_lift":[0,0],"sense":"Forward"},"id":919,"loop_ref":229,"next":916,"prev":918,"to":228,"twin":901,"wire_ref":null},{"edge":463,"face":230,"from":230,"geometry_use":{"pcurve":920,"periodic_lift":[0,0],"sense":"Forward"},"id":920,"loop_ref":230,"next":921,"prev":923,"to":234,"twin":925,"wire_ref":null},{"edge":464,"face":230,"from":234,"geometry_use":{"pcurve":921,"periodic_lift":[0,0],"sense":"Forward"},"id":921,"loop_ref":230,"next":922,"prev":920,"to":235,"twin":955,"wire_ref":null},{"edge":465,"face":230,"from":235,"geometry_use":{"pcurve":922,"periodic_lift":[0,0],"sense":"Reverse"},"id":922,"loop_ref":230,"next":923,"prev":921,"to":231,"twin":932,"wire_ref":null},{"edge":456,"face":230,"from":231,"geometry_use":{"pcurve":923,"periodic_lift":[0,0],"sense":"Reverse"},"id":923,"loop_ref":230,"next":920,"prev":922,"to":230,"twin":905,"wire_ref":null},{"edge":466,"face":231,"from":233,"geometry_use":{"pcurve":924,"periodic_lift":[0,0],"sense":"Forward"},"id":924,"loop_ref":231,"next":925,"prev":927,"to":234,"twin":958,"wire_ref":null},{"edge":463,"face":231,"from":234,"geometry_use":{"pcurve":925,"periodic_lift":[0,0],"sense":"Reverse"},"id":925,"loop_ref":231,"next":926,"prev":924,"to":230,"twin":920,"wire_ref":null},{"edge":458,"face":231,"from":230,"geometry_use":{"pcurve":926,"periodic_lift":[0,0],"sense":"Reverse"},"id":926,"loop_ref":231,"next":927,"prev":925,"to":229,"twin":908,"wire_ref":null},{"edge":462,"face":231,"from":229,"geometry_use":{"pcurve":927,"periodic_lift":[0,0],"sense":"Forward"},"id":927,"loop_ref":231,"next":924,"prev":926,"to":233,"twin":918,"wire_ref":null},{"edge":467,"face":232,"from":236,"geometry_use":{"pcurve":928,"periodic_lift":[0,0],"sense":"Forward"},"id":928,"loop_ref":232,"next":929,"prev":931,"to":237,"twin":947,"wire_ref":null},{"edge":468,"face":232,"from":237,"geometry_use":{"pcurve":929,"periodic_lift":[0,0],"sense":"Reverse"},"id":929,"loop_ref":232,"next":930,"prev":928,"to":232,"twin":940,"wire_ref":null},{"edge":460,"face":232,"from":232,"geometry_use":{"pcurve":930,"periodic_lift":[0,0],"sense":"Reverse"},"id":930,"loop_ref":232,"next":931,"prev":929,"to":228,"twin":916,"wire_ref":null},{"edge":469,"face":232,"from":228,"geometry_use":{"pcurve":931,"periodic_lift":[0,0],"sense":"Forward"},"id":931,"loop_ref":232,"next":928,"prev":930,"to":236,"twin":938,"wire_ref":null},{"edge":465,"face":233,"from":231,"geometry_use":{"pcurve":932,"periodic_lift":[0,0],"sense":"Forward"},"id":932,"loop_ref":233,"next":933,"prev":935,"to":235,"twin":922,"wire_ref":null},{"edge":470,"face":233,"from":235,"geometry_use":{"pcurve":933,"periodic_lift":[0,0],"sense":"Forward"},"id":933,"loop_ref":233,"next":934,"prev":932,"to":238,"twin":942,"wire_ref":null},{"edge":471,"face":233,"from":238,"geometry_use":{"pcurve":934,"periodic_lift":[0,0],"sense":"Reverse"},"id":934,"loop_ref":233,"next":935,"prev":933,"to":239,"twin":945,"wire_ref":null},{"edge":472,"face":233,"from":239,"geometry_use":{"pcurve":935,"periodic_lift":[0,0],"sense":"Reverse"},"id":935,"loop_ref":233,"next":932,"prev":934,"to":231,"twin":936,"wire_ref":null},{"edge":472,"face":234,"from":231,"geometry_use":{"pcurve":936,"periodic_lift":[0,0],"sense":"Forward"},"id":936,"loop_ref":234,"next":937,"prev":939,"to":239,"twin":935,"wire_ref":null},{"edge":473,"face":234,"from":239,"geometry_use":{"pcurve":937,"periodic_lift":[0,0],"sense":"Reverse"},"id":937,"loop_ref":234,"next":938,"prev":936,"to":236,"twin":944,"wire_ref":null},{"edge":469,"face":234,"from":236,"geometry_use":{"pcurve":938,"periodic_lift":[0,0],"sense":"Reverse"},"id":938,"loop_ref":234,"next":939,"prev":937,"to":228,"twin":931,"wire_ref":null},{"edge":459,"face":234,"from":228,"geometry_use":{"pcurve":939,"periodic_lift":[0,0],"sense":"Forward"},"id":939,"loop_ref":234,"next":936,"prev":938,"to":231,"twin":914,"wire_ref":null},{"edge":468,"face":235,"from":232,"geometry_use":{"pcurve":940,"periodic_lift":[0,0],"sense":"Forward"},"id":940,"loop_ref":235,"next":941,"prev":943,"to":237,"twin":929,"wire_ref":null},{"edge":474,"face":235,"from":237,"geometry_use":{"pcurve":941,"periodic_lift":[0,0],"sense":"Forward"},"id":941,"loop_ref":235,"next":942,"prev":940,"to":238,"twin":946,"wire_ref":null},{"edge":470,"face":235,"from":238,"geometry_use":{"pcurve":942,"periodic_lift":[0,0],"sense":"Reverse"},"id":942,"loop_ref":235,"next":943,"prev":941,"to":235,"twin":933,"wire_ref":null},{"edge":475,"face":235,"from":235,"geometry_use":{"pcurve":943,"periodic_lift":[0,0],"sense":"Reverse"},"id":943,"loop_ref":235,"next":940,"prev":942,"to":232,"twin":960,"wire_ref":null},{"edge":473,"face":236,"from":236,"geometry_use":{"pcurve":944,"periodic_lift":[0,0],"sense":"Forward"},"id":944,"loop_ref":236,"next":945,"prev":947,"to":239,"twin":937,"wire_ref":null},{"edge":471,"face":236,"from":239,"geometry_use":{"pcurve":945,"periodic_lift":[0,0],"sense":"Forward"},"id":945,"loop_ref":236,"next":946,"prev":944,"to":238,"twin":934,"wire_ref":null},{"edge":474,"face":236,"from":238,"geometry_use":{"pcurve":946,"periodic_lift":[0,0],"sense":"Reverse"},"id":946,"loop_ref":236,"next":947,"prev":945,"to":237,"twin":941,"wire_ref":null},{"edge":467,"face":236,"from":237,"geometry_use":{"pcurve":947,"periodic_lift":[0,0],"sense":"Reverse"},"id":947,"loop_ref":236,"next":944,"prev":946,"to":236,"twin":928,"wire_ref":null},{"edge":476,"face":237,"from":232,"geometry_use":{"pcurve":948,"periodic_lift":[0,0],"sense":"Forward"},"id":948,"loop_ref":237,"next":949,"prev":951,"to":240,"twin":963,"wire_ref":null},{"edge":477,"face":237,"from":240,"geometry_use":{"pcurve":949,"periodic_lift":[0,0],"sense":"Reverse"},"id":949,"loop_ref":237,"next":950,"prev":948,"to":241,"twin":967,"wire_ref":null},{"edge":478,"face":237,"from":241,"geometry_use":{"pcurve":950,"periodic_lift":[0,0],"sense":"Reverse"},"id":950,"loop_ref":237,"next":951,"prev":949,"to":233,"twin":959,"wire_ref":null},{"edge":461,"face":237,"from":233,"geometry_use":{"pcurve":951,"periodic_lift":[0,0],"sense":"Forward"},"id":951,"loop_ref":237,"next":948,"prev":950,"to":232,"twin":917,"wire_ref":null},{"edge":479,"face":238,"from":234,"geometry_use":{"pcurve":952,"periodic_lift":[0,0],"sense":"Forward"},"id":952,"loop_ref":238,"next":953,"prev":955,"to":242,"twin":957,"wire_ref":null},{"edge":480,"face":238,"from":242,"geometry_use":{"pcurve":953,"periodic_lift":[0,0],"sense":"Forward"},"id":953,"loop_ref":238,"next":954,"prev":952,"to":243,"twin":971,"wire_ref":null},{"edge":481,"face":238,"from":243,"geometry_use":{"pcurve":954,"periodic_lift":[0,0],"sense":"Reverse"},"id":954,"loop_ref":238,"next":955,"prev":953,"to":235,"twin":961,"wire_ref":null},{"edge":464,"face":238,"from":235,"geometry_use":{"pcurve":955,"periodic_lift":[0,0],"sense":"Reverse"},"id":955,"loop_ref":238,"next":952,"prev":954,"to":234,"twin":921,"wire_ref":null},{"edge":482,"face":239,"from":241,"geometry_use":{"pcurve":956,"periodic_lift":[0,0],"sense":"Forward"},"id":956,"loop_ref":239,"next":957,"prev":959,"to":242,"twin":974,"wire_ref":null},{"edge":479,"face":239,"from":242,"geometry_use":{"pcurve":957,"periodic_lift":[0,0],"sense":"Reverse"},"id":957,"loop_ref":239,"next":958,"prev":956,"to":234,"twin":952,"wire_ref":null},{"edge":466,"face":239,"from":234,"geometry_use":{"pcurve":958,"periodic_lift":[0,0],"sense":"Reverse"},"id":958,"loop_ref":239,"next":959,"prev":957,"to":233,"twin":924,"wire_ref":null},{"edge":478,"face":239,"from":233,"geometry_use":{"pcurve":959,"periodic_lift":[0,0],"sense":"Forward"},"id":959,"loop_ref":239,"next":956,"prev":958,"to":241,"twin":950,"wire_ref":null},{"edge":475,"face":240,"from":232,"geometry_use":{"pcurve":960,"periodic_lift":[0,0],"sense":"Forward"},"id":960,"loop_ref":240,"next":961,"prev":963,"to":235,"twin":943,"wire_ref":null},{"edge":481,"face":240,"from":235,"geometry_use":{"pcurve":961,"periodic_lift":[0,0],"sense":"Forward"},"id":961,"loop_ref":240,"next":962,"prev":960,"to":243,"twin":954,"wire_ref":null},{"edge":483,"face":240,"from":243,"geometry_use":{"pcurve":962,"periodic_lift":[0,0],"sense":"Reverse"},"id":962,"loop_ref":240,"next":963,"prev":961,"to":240,"twin":987,"wire_ref":null},{"edge":476,"face":240,"from":240,"geometry_use":{"pcurve":963,"periodic_lift":[0,0],"sense":"Reverse"},"id":963,"loop_ref":240,"next":960,"prev":962,"to":232,"twin":948,"wire_ref":null},{"edge":484,"face":241,"from":240,"geometry_use":{"pcurve":964,"periodic_lift":[0,0],"sense":"Forward"},"id":964,"loop_ref":241,"next":965,"prev":967,"to":244,"twin":978,"wire_ref":null},{"edge":485,"face":241,"from":244,"geometry_use":{"pcurve":965,"periodic_lift":[0,0],"sense":"Reverse"},"id":965,"loop_ref":241,"next":966,"prev":964,"to":245,"twin":999,"wire_ref":null},{"edge":486,"face":241,"from":245,"geometry_use":{"pcurve":966,"periodic_lift":[0,0],"sense":"Reverse"},"id":966,"loop_ref":241,"next":967,"prev":965,"to":241,"twin":975,"wire_ref":null},{"edge":477,"face":241,"from":241,"geometry_use":{"pcurve":967,"periodic_lift":[0,0],"sense":"Forward"},"id":967,"loop_ref":241,"next":964,"prev":966,"to":240,"twin":949,"wire_ref":null},{"edge":487,"face":242,"from":242,"geometry_use":{"pcurve":968,"periodic_lift":[0,0],"sense":"Forward"},"id":968,"loop_ref":242,"next":969,"prev":971,"to":246,"twin":973,"wire_ref":null},{"edge":488,"face":242,"from":246,"geometry_use":{"pcurve":969,"periodic_lift":[0,0],"sense":"Forward"},"id":969,"loop_ref":242,"next":970,"prev":968,"to":247,"twin":1003,"wire_ref":null},{"edge":489,"face":242,"from":247,"geometry_use":{"pcurve":970,"periodic_lift":[0,0],"sense":"Reverse"},"id":970,"loop_ref":242,"next":971,"prev":969,"to":243,"twin":980,"wire_ref":null},{"edge":480,"face":242,"from":243,"geometry_use":{"pcurve":971,"periodic_lift":[0,0],"sense":"Reverse"},"id":971,"loop_ref":242,"next":968,"prev":970,"to":242,"twin":953,"wire_ref":null},{"edge":490,"face":243,"from":245,"geometry_use":{"pcurve":972,"periodic_lift":[0,0],"sense":"Forward"},"id":972,"loop_ref":243,"next":973,"prev":975,"to":246,"twin":1006,"wire_ref":null},{"edge":487,"face":243,"from":246,"geometry_use":{"pcurve":973,"periodic_lift":[0,0],"sense":"Reverse"},"id":973,"loop_ref":243,"next":974,"prev":972,"to":242,"twin":968,"wire_ref":null},{"edge":482,"face":243,"from":242,"geometry_use":{"pcurve":974,"periodic_lift":[0,0],"sense":"Reverse"},"id":974,"loop_ref":243,"next":975,"prev":973,"to":241,"twin":956,"wire_ref":null},{"edge":486,"face":243,"from":241,"geometry_use":{"pcurve":975,"periodic_lift":[0,0],"sense":"Forward"},"id":975,"loop_ref":243,"next":972,"prev":974,"to":245,"twin":966,"wire_ref":null},{"edge":491,"face":244,"from":248,"geometry_use":{"pcurve":976,"periodic_lift":[0,0],"sense":"Forward"},"id":976,"loop_ref":244,"next":977,"prev":979,"to":249,"twin":995,"wire_ref":null},{"edge":492,"face":244,"from":249,"geometry_use":{"pcurve":977,"periodic_lift":[0,0],"sense":"Reverse"},"id":977,"loop_ref":244,"next":978,"prev":976,"to":244,"twin":988,"wire_ref":null},{"edge":484,"face":244,"from":244,"geometry_use":{"pcurve":978,"periodic_lift":[0,0],"sense":"Reverse"},"id":978,"loop_ref":244,"next":979,"prev":977,"to":240,"twin":964,"wire_ref":null},{"edge":493,"face":244,"from":240,"geometry_use":{"pcurve":979,"periodic_lift":[0,0],"sense":"Forward"},"id":979,"loop_ref":244,"next":976,"prev":978,"to":248,"twin":986,"wire_ref":null},{"edge":489,"face":245,"from":243,"geometry_use":{"pcurve":980,"periodic_lift":[0,0],"sense":"Forward"},"id":980,"loop_ref":245,"next":981,"prev":983,"to":247,"twin":970,"wire_ref":null},{"edge":494,"face":245,"from":247,"geometry_use":{"pcurve":981,"periodic_lift":[0,0],"sense":"Forward"},"id":981,"loop_ref":245,"next":982,"prev":980,"to":250,"twin":990,"wire_ref":null},{"edge":495,"face":245,"from":250,"geometry_use":{"pcurve":982,"periodic_lift":[0,0],"sense":"Reverse"},"id":982,"loop_ref":245,"next":983,"prev":981,"to":251,"twin":993,"wire_ref":null},{"edge":496,"face":245,"from":251,"geometry_use":{"pcurve":983,"periodic_lift":[0,0],"sense":"Reverse"},"id":983,"loop_ref":245,"next":980,"prev":982,"to":243,"twin":984,"wire_ref":null},{"edge":496,"face":246,"from":243,"geometry_use":{"pcurve":984,"periodic_lift":[0,0],"sense":"Forward"},"id":984,"loop_ref":246,"next":985,"prev":987,"to":251,"twin":983,"wire_ref":null},{"edge":497,"face":246,"from":251,"geometry_use":{"pcurve":985,"periodic_lift":[0,0],"sense":"Reverse"},"id":985,"loop_ref":246,"next":986,"prev":984,"to":248,"twin":992,"wire_ref":null},{"edge":493,"face":246,"from":248,"geometry_use":{"pcurve":986,"periodic_lift":[0,0],"sense":"Reverse"},"id":986,"loop_ref":246,"next":987,"prev":985,"to":240,"twin":979,"wire_ref":null},{"edge":483,"face":246,"from":240,"geometry_use":{"pcurve":987,"periodic_lift":[0,0],"sense":"Forward"},"id":987,"loop_ref":246,"next":984,"prev":986,"to":243,"twin":962,"wire_ref":null},{"edge":492,"face":247,"from":244,"geometry_use":{"pcurve":988,"periodic_lift":[0,0],"sense":"Forward"},"id":988,"loop_ref":247,"next":989,"prev":991,"to":249,"twin":977,"wire_ref":null},{"edge":498,"face":247,"from":249,"geometry_use":{"pcurve":989,"periodic_lift":[0,0],"sense":"Forward"},"id":989,"loop_ref":247,"next":990,"prev":988,"to":250,"twin":994,"wire_ref":null},{"edge":494,"face":247,"from":250,"geometry_use":{"pcurve":990,"periodic_lift":[0,0],"sense":"Reverse"},"id":990,"loop_ref":247,"next":991,"prev":989,"to":247,"twin":981,"wire_ref":null},{"edge":499,"face":247,"from":247,"geometry_use":{"pcurve":991,"periodic_lift":[0,0],"sense":"Reverse"},"id":991,"loop_ref":247,"next":988,"prev":990,"to":244,"twin":1008,"wire_ref":null},{"edge":497,"face":248,"from":248,"geometry_use":{"pcurve":992,"periodic_lift":[0,0],"sense":"Forward"},"id":992,"loop_ref":248,"next":993,"prev":995,"to":251,"twin":985,"wire_ref":null},{"edge":495,"face":248,"from":251,"geometry_use":{"pcurve":993,"periodic_lift":[0,0],"sense":"Forward"},"id":993,"loop_ref":248,"next":994,"prev":992,"to":250,"twin":982,"wire_ref":null},{"edge":498,"face":248,"from":250,"geometry_use":{"pcurve":994,"periodic_lift":[0,0],"sense":"Reverse"},"id":994,"loop_ref":248,"next":995,"prev":993,"to":249,"twin":989,"wire_ref":null},{"edge":491,"face":248,"from":249,"geometry_use":{"pcurve":995,"periodic_lift":[0,0],"sense":"Reverse"},"id":995,"loop_ref":248,"next":992,"prev":994,"to":248,"twin":976,"wire_ref":null},{"edge":500,"face":249,"from":244,"geometry_use":{"pcurve":996,"periodic_lift":[0,0],"sense":"Forward"},"id":996,"loop_ref":249,"next":997,"prev":999,"to":252,"twin":1011,"wire_ref":null},{"edge":501,"face":249,"from":252,"geometry_use":{"pcurve":997,"periodic_lift":[0,0],"sense":"Reverse"},"id":997,"loop_ref":249,"next":998,"prev":996,"to":253,"twin":1015,"wire_ref":null},{"edge":502,"face":249,"from":253,"geometry_use":{"pcurve":998,"periodic_lift":[0,0],"sense":"Reverse"},"id":998,"loop_ref":249,"next":999,"prev":997,"to":245,"twin":1007,"wire_ref":null},{"edge":485,"face":249,"from":245,"geometry_use":{"pcurve":999,"periodic_lift":[0,0],"sense":"Forward"},"id":999,"loop_ref":249,"next":996,"prev":998,"to":244,"twin":965,"wire_ref":null},{"edge":503,"face":250,"from":246,"geometry_use":{"pcurve":1000,"periodic_lift":[0,0],"sense":"Forward"},"id":1000,"loop_ref":250,"next":1001,"prev":1003,"to":254,"twin":1005,"wire_ref":null},{"edge":504,"face":250,"from":254,"geometry_use":{"pcurve":1001,"periodic_lift":[0,0],"sense":"Forward"},"id":1001,"loop_ref":250,"next":1002,"prev":1000,"to":255,"twin":1019,"wire_ref":null},{"edge":505,"face":250,"from":255,"geometry_use":{"pcurve":1002,"periodic_lift":[0,0],"sense":"Reverse"},"id":1002,"loop_ref":250,"next":1003,"prev":1001,"to":247,"twin":1009,"wire_ref":null},{"edge":488,"face":250,"from":247,"geometry_use":{"pcurve":1003,"periodic_lift":[0,0],"sense":"Reverse"},"id":1003,"loop_ref":250,"next":1000,"prev":1002,"to":246,"twin":969,"wire_ref":null},{"edge":506,"face":251,"from":253,"geometry_use":{"pcurve":1004,"periodic_lift":[0,0],"sense":"Forward"},"id":1004,"loop_ref":251,"next":1005,"prev":1007,"to":254,"twin":1022,"wire_ref":null},{"edge":503,"face":251,"from":254,"geometry_use":{"pcurve":1005,"periodic_lift":[0,0],"sense":"Reverse"},"id":1005,"loop_ref":251,"next":1006,"prev":1004,"to":246,"twin":1000,"wire_ref":null},{"edge":490,"face":251,"from":246,"geometry_use":{"pcurve":1006,"periodic_lift":[0,0],"sense":"Reverse"},"id":1006,"loop_ref":251,"next":1007,"prev":1005,"to":245,"twin":972,"wire_ref":null},{"edge":502,"face":251,"from":245,"geometry_use":{"pcurve":1007,"periodic_lift":[0,0],"sense":"Forward"},"id":1007,"loop_ref":251,"next":1004,"prev":1006,"to":253,"twin":998,"wire_ref":null},{"edge":499,"face":252,"from":244,"geometry_use":{"pcurve":1008,"periodic_lift":[0,0],"sense":"Forward"},"id":1008,"loop_ref":252,"next":1009,"prev":1011,"to":247,"twin":991,"wire_ref":null},{"edge":505,"face":252,"from":247,"geometry_use":{"pcurve":1009,"periodic_lift":[0,0],"sense":"Forward"},"id":1009,"loop_ref":252,"next":1010,"prev":1008,"to":255,"twin":1002,"wire_ref":null},{"edge":507,"face":252,"from":255,"geometry_use":{"pcurve":1010,"periodic_lift":[0,0],"sense":"Reverse"},"id":1010,"loop_ref":252,"next":1011,"prev":1009,"to":252,"twin":1035,"wire_ref":null},{"edge":500,"face":252,"from":252,"geometry_use":{"pcurve":1011,"periodic_lift":[0,0],"sense":"Reverse"},"id":1011,"loop_ref":252,"next":1008,"prev":1010,"to":244,"twin":996,"wire_ref":null},{"edge":508,"face":253,"from":252,"geometry_use":{"pcurve":1012,"periodic_lift":[0,0],"sense":"Forward"},"id":1012,"loop_ref":253,"next":1013,"prev":1015,"to":256,"twin":1026,"wire_ref":null},{"edge":509,"face":253,"from":256,"geometry_use":{"pcurve":1013,"periodic_lift":[0,0],"sense":"Reverse"},"id":1013,"loop_ref":253,"next":1014,"prev":1012,"to":257,"twin":1047,"wire_ref":null},{"edge":510,"face":253,"from":257,"geometry_use":{"pcurve":1014,"periodic_lift":[0,0],"sense":"Reverse"},"id":1014,"loop_ref":253,"next":1015,"prev":1013,"to":253,"twin":1023,"wire_ref":null},{"edge":501,"face":253,"from":253,"geometry_use":{"pcurve":1015,"periodic_lift":[0,0],"sense":"Forward"},"id":1015,"loop_ref":253,"next":1012,"prev":1014,"to":252,"twin":997,"wire_ref":null},{"edge":511,"face":254,"from":254,"geometry_use":{"pcurve":1016,"periodic_lift":[0,0],"sense":"Forward"},"id":1016,"loop_ref":254,"next":1017,"prev":1019,"to":258,"twin":1021,"wire_ref":null},{"edge":512,"face":254,"from":258,"geometry_use":{"pcurve":1017,"periodic_lift":[0,0],"sense":"Forward"},"id":1017,"loop_ref":254,"next":1018,"prev":1016,"to":259,"twin":1051,"wire_ref":null},{"edge":513,"face":254,"from":259,"geometry_use":{"pcurve":1018,"periodic_lift":[0,0],"sense":"Reverse"},"id":1018,"loop_ref":254,"next":1019,"prev":1017,"to":255,"twin":1028,"wire_ref":null},{"edge":504,"face":254,"from":255,"geometry_use":{"pcurve":1019,"periodic_lift":[0,0],"sense":"Reverse"},"id":1019,"loop_ref":254,"next":1016,"prev":1018,"to":254,"twin":1001,"wire_ref":null},{"edge":514,"face":255,"from":257,"geometry_use":{"pcurve":1020,"periodic_lift":[0,0],"sense":"Forward"},"id":1020,"loop_ref":255,"next":1021,"prev":1023,"to":258,"twin":1054,"wire_ref":null},{"edge":511,"face":255,"from":258,"geometry_use":{"pcurve":1021,"periodic_lift":[0,0],"sense":"Reverse"},"id":1021,"loop_ref":255,"next":1022,"prev":1020,"to":254,"twin":1016,"wire_ref":null},{"edge":506,"face":255,"from":254,"geometry_use":{"pcurve":1022,"periodic_lift":[0,0],"sense":"Reverse"},"id":1022,"loop_ref":255,"next":1023,"prev":1021,"to":253,"twin":1004,"wire_ref":null},{"edge":510,"face":255,"from":253,"geometry_use":{"pcurve":1023,"periodic_lift":[0,0],"sense":"Forward"},"id":1023,"loop_ref":255,"next":1020,"prev":1022,"to":257,"twin":1014,"wire_ref":null},{"edge":515,"face":256,"from":260,"geometry_use":{"pcurve":1024,"periodic_lift":[0,0],"sense":"Forward"},"id":1024,"loop_ref":256,"next":1025,"prev":1027,"to":261,"twin":1043,"wire_ref":null},{"edge":516,"face":256,"from":261,"geometry_use":{"pcurve":1025,"periodic_lift":[0,0],"sense":"Reverse"},"id":1025,"loop_ref":256,"next":1026,"prev":1024,"to":256,"twin":1036,"wire_ref":null},{"edge":508,"face":256,"from":256,"geometry_use":{"pcurve":1026,"periodic_lift":[0,0],"sense":"Reverse"},"id":1026,"loop_ref":256,"next":1027,"prev":1025,"to":252,"twin":1012,"wire_ref":null},{"edge":517,"face":256,"from":252,"geometry_use":{"pcurve":1027,"periodic_lift":[0,0],"sense":"Forward"},"id":1027,"loop_ref":256,"next":1024,"prev":1026,"to":260,"twin":1034,"wire_ref":null},{"edge":513,"face":257,"from":255,"geometry_use":{"pcurve":1028,"periodic_lift":[0,0],"sense":"Forward"},"id":1028,"loop_ref":257,"next":1029,"prev":1031,"to":259,"twin":1018,"wire_ref":null},{"edge":518,"face":257,"from":259,"geometry_use":{"pcurve":1029,"periodic_lift":[0,0],"sense":"Forward"},"id":1029,"loop_ref":257,"next":1030,"prev":1028,"to":262,"twin":1038,"wire_ref":null},{"edge":519,"face":257,"from":262,"geometry_use":{"pcurve":1030,"periodic_lift":[0,0],"sense":"Reverse"},"id":1030,"loop_ref":257,"next":1031,"prev":1029,"to":263,"twin":1041,"wire_ref":null},{"edge":520,"face":257,"from":263,"geometry_use":{"pcurve":1031,"periodic_lift":[0,0],"sense":"Reverse"},"id":1031,"loop_ref":257,"next":1028,"prev":1030,"to":255,"twin":1032,"wire_ref":null},{"edge":520,"face":258,"from":255,"geometry_use":{"pcurve":1032,"periodic_lift":[0,0],"sense":"Forward"},"id":1032,"loop_ref":258,"next":1033,"prev":1035,"to":263,"twin":1031,"wire_ref":null},{"edge":521,"face":258,"from":263,"geometry_use":{"pcurve":1033,"periodic_lift":[0,0],"sense":"Reverse"},"id":1033,"loop_ref":258,"next":1034,"prev":1032,"to":260,"twin":1040,"wire_ref":null},{"edge":517,"face":258,"from":260,"geometry_use":{"pcurve":1034,"periodic_lift":[0,0],"sense":"Reverse"},"id":1034,"loop_ref":258,"next":1035,"prev":1033,"to":252,"twin":1027,"wire_ref":null},{"edge":507,"face":258,"from":252,"geometry_use":{"pcurve":1035,"periodic_lift":[0,0],"sense":"Forward"},"id":1035,"loop_ref":258,"next":1032,"prev":1034,"to":255,"twin":1010,"wire_ref":null},{"edge":516,"face":259,"from":256,"geometry_use":{"pcurve":1036,"periodic_lift":[0,0],"sense":"Forward"},"id":1036,"loop_ref":259,"next":1037,"prev":1039,"to":261,"twin":1025,"wire_ref":null},{"edge":522,"face":259,"from":261,"geometry_use":{"pcurve":1037,"periodic_lift":[0,0],"sense":"Forward"},"id":1037,"loop_ref":259,"next":1038,"prev":1036,"to":262,"twin":1042,"wire_ref":null},{"edge":518,"face":259,"from":262,"geometry_use":{"pcurve":1038,"periodic_lift":[0,0],"sense":"Reverse"},"id":1038,"loop_ref":259,"next":1039,"prev":1037,"to":259,"twin":1029,"wire_ref":null},{"edge":523,"face":259,"from":259,"geometry_use":{"pcurve":1039,"periodic_lift":[0,0],"sense":"Reverse"},"id":1039,"loop_ref":259,"next":1036,"prev":1038,"to":256,"twin":1056,"wire_ref":null},{"edge":521,"face":260,"from":260,"geometry_use":{"pcurve":1040,"periodic_lift":[0,0],"sense":"Forward"},"id":1040,"loop_ref":260,"next":1041,"prev":1043,"to":263,"twin":1033,"wire_ref":null},{"edge":519,"face":260,"from":263,"geometry_use":{"pcurve":1041,"periodic_lift":[0,0],"sense":"Forward"},"id":1041,"loop_ref":260,"next":1042,"prev":1040,"to":262,"twin":1030,"wire_ref":null},{"edge":522,"face":260,"from":262,"geometry_use":{"pcurve":1042,"periodic_lift":[0,0],"sense":"Reverse"},"id":1042,"loop_ref":260,"next":1043,"prev":1041,"to":261,"twin":1037,"wire_ref":null},{"edge":515,"face":260,"from":261,"geometry_use":{"pcurve":1043,"periodic_lift":[0,0],"sense":"Reverse"},"id":1043,"loop_ref":260,"next":1040,"prev":1042,"to":260,"twin":1024,"wire_ref":null},{"edge":524,"face":261,"from":256,"geometry_use":{"pcurve":1044,"periodic_lift":[0,0],"sense":"Forward"},"id":1044,"loop_ref":261,"next":1045,"prev":1047,"to":264,"twin":1059,"wire_ref":null},{"edge":525,"face":261,"from":264,"geometry_use":{"pcurve":1045,"periodic_lift":[0,0],"sense":"Reverse"},"id":1045,"loop_ref":261,"next":1046,"prev":1044,"to":265,"twin":1063,"wire_ref":null},{"edge":526,"face":261,"from":265,"geometry_use":{"pcurve":1046,"periodic_lift":[0,0],"sense":"Reverse"},"id":1046,"loop_ref":261,"next":1047,"prev":1045,"to":257,"twin":1055,"wire_ref":null},{"edge":509,"face":261,"from":257,"geometry_use":{"pcurve":1047,"periodic_lift":[0,0],"sense":"Forward"},"id":1047,"loop_ref":261,"next":1044,"prev":1046,"to":256,"twin":1013,"wire_ref":null},{"edge":527,"face":262,"from":258,"geometry_use":{"pcurve":1048,"periodic_lift":[0,0],"sense":"Forward"},"id":1048,"loop_ref":262,"next":1049,"prev":1051,"to":266,"twin":1053,"wire_ref":null},{"edge":528,"face":262,"from":266,"geometry_use":{"pcurve":1049,"periodic_lift":[0,0],"sense":"Forward"},"id":1049,"loop_ref":262,"next":1050,"prev":1048,"to":267,"twin":1067,"wire_ref":null},{"edge":529,"face":262,"from":267,"geometry_use":{"pcurve":1050,"periodic_lift":[0,0],"sense":"Reverse"},"id":1050,"loop_ref":262,"next":1051,"prev":1049,"to":259,"twin":1057,"wire_ref":null},{"edge":512,"face":262,"from":259,"geometry_use":{"pcurve":1051,"periodic_lift":[0,0],"sense":"Reverse"},"id":1051,"loop_ref":262,"next":1048,"prev":1050,"to":258,"twin":1017,"wire_ref":null},{"edge":530,"face":263,"from":265,"geometry_use":{"pcurve":1052,"periodic_lift":[0,0],"sense":"Forward"},"id":1052,"loop_ref":263,"next":1053,"prev":1055,"to":266,"twin":1070,"wire_ref":null},{"edge":527,"face":263,"from":266,"geometry_use":{"pcurve":1053,"periodic_lift":[0,0],"sense":"Reverse"},"id":1053,"loop_ref":263,"next":1054,"prev":1052,"to":258,"twin":1048,"wire_ref":null},{"edge":514,"face":263,"from":258,"geometry_use":{"pcurve":1054,"periodic_lift":[0,0],"sense":"Reverse"},"id":1054,"loop_ref":263,"next":1055,"prev":1053,"to":257,"twin":1020,"wire_ref":null},{"edge":526,"face":263,"from":257,"geometry_use":{"pcurve":1055,"periodic_lift":[0,0],"sense":"Forward"},"id":1055,"loop_ref":263,"next":1052,"prev":1054,"to":265,"twin":1046,"wire_ref":null},{"edge":523,"face":264,"from":256,"geometry_use":{"pcurve":1056,"periodic_lift":[0,0],"sense":"Forward"},"id":1056,"loop_ref":264,"next":1057,"prev":1059,"to":259,"twin":1039,"wire_ref":null},{"edge":529,"face":264,"from":259,"geometry_use":{"pcurve":1057,"periodic_lift":[0,0],"sense":"Forward"},"id":1057,"loop_ref":264,"next":1058,"prev":1056,"to":267,"twin":1050,"wire_ref":null},{"edge":531,"face":264,"from":267,"geometry_use":{"pcurve":1058,"periodic_lift":[0,0],"sense":"Reverse"},"id":1058,"loop_ref":264,"next":1059,"prev":1057,"to":264,"twin":1083,"wire_ref":null},{"edge":524,"face":264,"from":264,"geometry_use":{"pcurve":1059,"periodic_lift":[0,0],"sense":"Reverse"},"id":1059,"loop_ref":264,"next":1056,"prev":1058,"to":256,"twin":1044,"wire_ref":null},{"edge":532,"face":265,"from":264,"geometry_use":{"pcurve":1060,"periodic_lift":[0,0],"sense":"Forward"},"id":1060,"loop_ref":265,"next":1061,"prev":1063,"to":268,"twin":1074,"wire_ref":null},{"edge":533,"face":265,"from":268,"geometry_use":{"pcurve":1061,"periodic_lift":[0,0],"sense":"Reverse"},"id":1061,"loop_ref":265,"next":1062,"prev":1060,"to":269,"twin":1095,"wire_ref":null},{"edge":534,"face":265,"from":269,"geometry_use":{"pcurve":1062,"periodic_lift":[0,0],"sense":"Reverse"},"id":1062,"loop_ref":265,"next":1063,"prev":1061,"to":265,"twin":1071,"wire_ref":null},{"edge":525,"face":265,"from":265,"geometry_use":{"pcurve":1063,"periodic_lift":[0,0],"sense":"Forward"},"id":1063,"loop_ref":265,"next":1060,"prev":1062,"to":264,"twin":1045,"wire_ref":null},{"edge":535,"face":266,"from":266,"geometry_use":{"pcurve":1064,"periodic_lift":[0,0],"sense":"Forward"},"id":1064,"loop_ref":266,"next":1065,"prev":1067,"to":270,"twin":1069,"wire_ref":null},{"edge":536,"face":266,"from":270,"geometry_use":{"pcurve":1065,"periodic_lift":[0,0],"sense":"Forward"},"id":1065,"loop_ref":266,"next":1066,"prev":1064,"to":271,"twin":1099,"wire_ref":null},{"edge":537,"face":266,"from":271,"geometry_use":{"pcurve":1066,"periodic_lift":[0,0],"sense":"Reverse"},"id":1066,"loop_ref":266,"next":1067,"prev":1065,"to":267,"twin":1076,"wire_ref":null},{"edge":528,"face":266,"from":267,"geometry_use":{"pcurve":1067,"periodic_lift":[0,0],"sense":"Reverse"},"id":1067,"loop_ref":266,"next":1064,"prev":1066,"to":266,"twin":1049,"wire_ref":null},{"edge":538,"face":267,"from":269,"geometry_use":{"pcurve":1068,"periodic_lift":[0,0],"sense":"Forward"},"id":1068,"loop_ref":267,"next":1069,"prev":1071,"to":270,"twin":1102,"wire_ref":null},{"edge":535,"face":267,"from":270,"geometry_use":{"pcurve":1069,"periodic_lift":[0,0],"sense":"Reverse"},"id":1069,"loop_ref":267,"next":1070,"prev":1068,"to":266,"twin":1064,"wire_ref":null},{"edge":530,"face":267,"from":266,"geometry_use":{"pcurve":1070,"periodic_lift":[0,0],"sense":"Reverse"},"id":1070,"loop_ref":267,"next":1071,"prev":1069,"to":265,"twin":1052,"wire_ref":null},{"edge":534,"face":267,"from":265,"geometry_use":{"pcurve":1071,"periodic_lift":[0,0],"sense":"Forward"},"id":1071,"loop_ref":267,"next":1068,"prev":1070,"to":269,"twin":1062,"wire_ref":null},{"edge":539,"face":268,"from":272,"geometry_use":{"pcurve":1072,"periodic_lift":[0,0],"sense":"Forward"},"id":1072,"loop_ref":268,"next":1073,"prev":1075,"to":273,"twin":1091,"wire_ref":null},{"edge":540,"face":268,"from":273,"geometry_use":{"pcurve":1073,"periodic_lift":[0,0],"sense":"Reverse"},"id":1073,"loop_ref":268,"next":1074,"prev":1072,"to":268,"twin":1084,"wire_ref":null},{"edge":532,"face":268,"from":268,"geometry_use":{"pcurve":1074,"periodic_lift":[0,0],"sense":"Reverse"},"id":1074,"loop_ref":268,"next":1075,"prev":1073,"to":264,"twin":1060,"wire_ref":null},{"edge":541,"face":268,"from":264,"geometry_use":{"pcurve":1075,"periodic_lift":[0,0],"sense":"Forward"},"id":1075,"loop_ref":268,"next":1072,"prev":1074,"to":272,"twin":1082,"wire_ref":null},{"edge":537,"face":269,"from":267,"geometry_use":{"pcurve":1076,"periodic_lift":[0,0],"sense":"Forward"},"id":1076,"loop_ref":269,"next":1077,"prev":1079,"to":271,"twin":1066,"wire_ref":null},{"edge":542,"face":269,"from":271,"geometry_use":{"pcurve":1077,"periodic_lift":[0,0],"sense":"Forward"},"id":1077,"loop_ref":269,"next":1078,"prev":1076,"to":274,"twin":1086,"wire_ref":null},{"edge":543,"face":269,"from":274,"geometry_use":{"pcurve":1078,"periodic_lift":[0,0],"sense":"Reverse"},"id":1078,"loop_ref":269,"next":1079,"prev":1077,"to":275,"twin":1089,"wire_ref":null},{"edge":544,"face":269,"from":275,"geometry_use":{"pcurve":1079,"periodic_lift":[0,0],"sense":"Reverse"},"id":1079,"loop_ref":269,"next":1076,"prev":1078,"to":267,"twin":1080,"wire_ref":null},{"edge":544,"face":270,"from":267,"geometry_use":{"pcurve":1080,"periodic_lift":[0,0],"sense":"Forward"},"id":1080,"loop_ref":270,"next":1081,"prev":1083,"to":275,"twin":1079,"wire_ref":null},{"edge":545,"face":270,"from":275,"geometry_use":{"pcurve":1081,"periodic_lift":[0,0],"sense":"Reverse"},"id":1081,"loop_ref":270,"next":1082,"prev":1080,"to":272,"twin":1088,"wire_ref":null},{"edge":541,"face":270,"from":272,"geometry_use":{"pcurve":1082,"periodic_lift":[0,0],"sense":"Reverse"},"id":1082,"loop_ref":270,"next":1083,"prev":1081,"to":264,"twin":1075,"wire_ref":null},{"edge":531,"face":270,"from":264,"geometry_use":{"pcurve":1083,"periodic_lift":[0,0],"sense":"Forward"},"id":1083,"loop_ref":270,"next":1080,"prev":1082,"to":267,"twin":1058,"wire_ref":null},{"edge":540,"face":271,"from":268,"geometry_use":{"pcurve":1084,"periodic_lift":[0,0],"sense":"Forward"},"id":1084,"loop_ref":271,"next":1085,"prev":1087,"to":273,"twin":1073,"wire_ref":null},{"edge":546,"face":271,"from":273,"geometry_use":{"pcurve":1085,"periodic_lift":[0,0],"sense":"Forward"},"id":1085,"loop_ref":271,"next":1086,"prev":1084,"to":274,"twin":1090,"wire_ref":null},{"edge":542,"face":271,"from":274,"geometry_use":{"pcurve":1086,"periodic_lift":[0,0],"sense":"Reverse"},"id":1086,"loop_ref":271,"next":1087,"prev":1085,"to":271,"twin":1077,"wire_ref":null},{"edge":547,"face":271,"from":271,"geometry_use":{"pcurve":1087,"periodic_lift":[0,0],"sense":"Reverse"},"id":1087,"loop_ref":271,"next":1084,"prev":1086,"to":268,"twin":1104,"wire_ref":null},{"edge":545,"face":272,"from":272,"geometry_use":{"pcurve":1088,"periodic_lift":[0,0],"sense":"Forward"},"id":1088,"loop_ref":272,"next":1089,"prev":1091,"to":275,"twin":1081,"wire_ref":null},{"edge":543,"face":272,"from":275,"geometry_use":{"pcurve":1089,"periodic_lift":[0,0],"sense":"Forward"},"id":1089,"loop_ref":272,"next":1090,"prev":1088,"to":274,"twin":1078,"wire_ref":null},{"edge":546,"face":272,"from":274,"geometry_use":{"pcurve":1090,"periodic_lift":[0,0],"sense":"Reverse"},"id":1090,"loop_ref":272,"next":1091,"prev":1089,"to":273,"twin":1085,"wire_ref":null},{"edge":539,"face":272,"from":273,"geometry_use":{"pcurve":1091,"periodic_lift":[0,0],"sense":"Reverse"},"id":1091,"loop_ref":272,"next":1088,"prev":1090,"to":272,"twin":1072,"wire_ref":null},{"edge":548,"face":273,"from":268,"geometry_use":{"pcurve":1092,"periodic_lift":[0,0],"sense":"Forward"},"id":1092,"loop_ref":273,"next":1093,"prev":1095,"to":276,"twin":1107,"wire_ref":null},{"edge":549,"face":273,"from":276,"geometry_use":{"pcurve":1093,"periodic_lift":[0,0],"sense":"Reverse"},"id":1093,"loop_ref":273,"next":1094,"prev":1092,"to":277,"twin":1111,"wire_ref":null},{"edge":550,"face":273,"from":277,"geometry_use":{"pcurve":1094,"periodic_lift":[0,0],"sense":"Reverse"},"id":1094,"loop_ref":273,"next":1095,"prev":1093,"to":269,"twin":1103,"wire_ref":null},{"edge":533,"face":273,"from":269,"geometry_use":{"pcurve":1095,"periodic_lift":[0,0],"sense":"Forward"},"id":1095,"loop_ref":273,"next":1092,"prev":1094,"to":268,"twin":1061,"wire_ref":null},{"edge":551,"face":274,"from":270,"geometry_use":{"pcurve":1096,"periodic_lift":[0,0],"sense":"Forward"},"id":1096,"loop_ref":274,"next":1097,"prev":1099,"to":278,"twin":1101,"wire_ref":null},{"edge":552,"face":274,"from":278,"geometry_use":{"pcurve":1097,"periodic_lift":[0,0],"sense":"Forward"},"id":1097,"loop_ref":274,"next":1098,"prev":1096,"to":279,"twin":1115,"wire_ref":null},{"edge":553,"face":274,"from":279,"geometry_use":{"pcurve":1098,"periodic_lift":[0,0],"sense":"Reverse"},"id":1098,"loop_ref":274,"next":1099,"prev":1097,"to":271,"twin":1105,"wire_ref":null},{"edge":536,"face":274,"from":271,"geometry_use":{"pcurve":1099,"periodic_lift":[0,0],"sense":"Reverse"},"id":1099,"loop_ref":274,"next":1096,"prev":1098,"to":270,"twin":1065,"wire_ref":null},{"edge":554,"face":275,"from":277,"geometry_use":{"pcurve":1100,"periodic_lift":[0,0],"sense":"Forward"},"id":1100,"loop_ref":275,"next":1101,"prev":1103,"to":278,"twin":1118,"wire_ref":null},{"edge":551,"face":275,"from":278,"geometry_use":{"pcurve":1101,"periodic_lift":[0,0],"sense":"Reverse"},"id":1101,"loop_ref":275,"next":1102,"prev":1100,"to":270,"twin":1096,"wire_ref":null},{"edge":538,"face":275,"from":270,"geometry_use":{"pcurve":1102,"periodic_lift":[0,0],"sense":"Reverse"},"id":1102,"loop_ref":275,"next":1103,"prev":1101,"to":269,"twin":1068,"wire_ref":null},{"edge":550,"face":275,"from":269,"geometry_use":{"pcurve":1103,"periodic_lift":[0,0],"sense":"Forward"},"id":1103,"loop_ref":275,"next":1100,"prev":1102,"to":277,"twin":1094,"wire_ref":null},{"edge":547,"face":276,"from":268,"geometry_use":{"pcurve":1104,"periodic_lift":[0,0],"sense":"Forward"},"id":1104,"loop_ref":276,"next":1105,"prev":1107,"to":271,"twin":1087,"wire_ref":null},{"edge":553,"face":276,"from":271,"geometry_use":{"pcurve":1105,"periodic_lift":[0,0],"sense":"Forward"},"id":1105,"loop_ref":276,"next":1106,"prev":1104,"to":279,"twin":1098,"wire_ref":null},{"edge":555,"face":276,"from":279,"geometry_use":{"pcurve":1106,"periodic_lift":[0,0],"sense":"Reverse"},"id":1106,"loop_ref":276,"next":1107,"prev":1105,"to":276,"twin":1131,"wire_ref":null},{"edge":548,"face":276,"from":276,"geometry_use":{"pcurve":1107,"periodic_lift":[0,0],"sense":"Reverse"},"id":1107,"loop_ref":276,"next":1104,"prev":1106,"to":268,"twin":1092,"wire_ref":null},{"edge":556,"face":277,"from":276,"geometry_use":{"pcurve":1108,"periodic_lift":[0,0],"sense":"Forward"},"id":1108,"loop_ref":277,"next":1109,"prev":1111,"to":280,"twin":1122,"wire_ref":null},{"edge":557,"face":277,"from":280,"geometry_use":{"pcurve":1109,"periodic_lift":[0,0],"sense":"Reverse"},"id":1109,"loop_ref":277,"next":1110,"prev":1108,"to":281,"twin":1143,"wire_ref":null},{"edge":558,"face":277,"from":281,"geometry_use":{"pcurve":1110,"periodic_lift":[0,0],"sense":"Reverse"},"id":1110,"loop_ref":277,"next":1111,"prev":1109,"to":277,"twin":1119,"wire_ref":null},{"edge":549,"face":277,"from":277,"geometry_use":{"pcurve":1111,"periodic_lift":[0,0],"sense":"Forward"},"id":1111,"loop_ref":277,"next":1108,"prev":1110,"to":276,"twin":1093,"wire_ref":null},{"edge":559,"face":278,"from":278,"geometry_use":{"pcurve":1112,"periodic_lift":[0,0],"sense":"Forward"},"id":1112,"loop_ref":278,"next":1113,"prev":1115,"to":282,"twin":1117,"wire_ref":null},{"edge":560,"face":278,"from":282,"geometry_use":{"pcurve":1113,"periodic_lift":[0,0],"sense":"Forward"},"id":1113,"loop_ref":278,"next":1114,"prev":1112,"to":283,"twin":1147,"wire_ref":null},{"edge":561,"face":278,"from":283,"geometry_use":{"pcurve":1114,"periodic_lift":[0,0],"sense":"Reverse"},"id":1114,"loop_ref":278,"next":1115,"prev":1113,"to":279,"twin":1124,"wire_ref":null},{"edge":552,"face":278,"from":279,"geometry_use":{"pcurve":1115,"periodic_lift":[0,0],"sense":"Reverse"},"id":1115,"loop_ref":278,"next":1112,"prev":1114,"to":278,"twin":1097,"wire_ref":null},{"edge":562,"face":279,"from":281,"geometry_use":{"pcurve":1116,"periodic_lift":[0,0],"sense":"Forward"},"id":1116,"loop_ref":279,"next":1117,"prev":1119,"to":282,"twin":1150,"wire_ref":null},{"edge":559,"face":279,"from":282,"geometry_use":{"pcurve":1117,"periodic_lift":[0,0],"sense":"Reverse"},"id":1117,"loop_ref":279,"next":1118,"prev":1116,"to":278,"twin":1112,"wire_ref":null},{"edge":554,"face":279,"from":278,"geometry_use":{"pcurve":1118,"periodic_lift":[0,0],"sense":"Reverse"},"id":1118,"loop_ref":279,"next":1119,"prev":1117,"to":277,"twin":1100,"wire_ref":null},{"edge":558,"face":279,"from":277,"geometry_use":{"pcurve":1119,"periodic_lift":[0,0],"sense":"Forward"},"id":1119,"loop_ref":279,"next":1116,"prev":1118,"to":281,"twin":1110,"wire_ref":null},{"edge":563,"face":280,"from":284,"geometry_use":{"pcurve":1120,"periodic_lift":[0,0],"sense":"Forward"},"id":1120,"loop_ref":280,"next":1121,"prev":1123,"to":285,"twin":1139,"wire_ref":null},{"edge":564,"face":280,"from":285,"geometry_use":{"pcurve":1121,"periodic_lift":[0,0],"sense":"Reverse"},"id":1121,"loop_ref":280,"next":1122,"prev":1120,"to":280,"twin":1132,"wire_ref":null},{"edge":556,"face":280,"from":280,"geometry_use":{"pcurve":1122,"periodic_lift":[0,0],"sense":"Reverse"},"id":1122,"loop_ref":280,"next":1123,"prev":1121,"to":276,"twin":1108,"wire_ref":null},{"edge":565,"face":280,"from":276,"geometry_use":{"pcurve":1123,"periodic_lift":[0,0],"sense":"Forward"},"id":1123,"loop_ref":280,"next":1120,"prev":1122,"to":284,"twin":1130,"wire_ref":null},{"edge":561,"face":281,"from":279,"geometry_use":{"pcurve":1124,"periodic_lift":[0,0],"sense":"Forward"},"id":1124,"loop_ref":281,"next":1125,"prev":1127,"to":283,"twin":1114,"wire_ref":null},{"edge":566,"face":281,"from":283,"geometry_use":{"pcurve":1125,"periodic_lift":[0,0],"sense":"Forward"},"id":1125,"loop_ref":281,"next":1126,"prev":1124,"to":286,"twin":1134,"wire_ref":null},{"edge":567,"face":281,"from":286,"geometry_use":{"pcurve":1126,"periodic_lift":[0,0],"sense":"Reverse"},"id":1126,"loop_ref":281,"next":1127,"prev":1125,"to":287,"twin":1137,"wire_ref":null},{"edge":568,"face":281,"from":287,"geometry_use":{"pcurve":1127,"periodic_lift":[0,0],"sense":"Reverse"},"id":1127,"loop_ref":281,"next":1124,"prev":1126,"to":279,"twin":1128,"wire_ref":null},{"edge":568,"face":282,"from":279,"geometry_use":{"pcurve":1128,"periodic_lift":[0,0],"sense":"Forward"},"id":1128,"loop_ref":282,"next":1129,"prev":1131,"to":287,"twin":1127,"wire_ref":null},{"edge":569,"face":282,"from":287,"geometry_use":{"pcurve":1129,"periodic_lift":[0,0],"sense":"Reverse"},"id":1129,"loop_ref":282,"next":1130,"prev":1128,"to":284,"twin":1136,"wire_ref":null},{"edge":565,"face":282,"from":284,"geometry_use":{"pcurve":1130,"periodic_lift":[0,0],"sense":"Reverse"},"id":1130,"loop_ref":282,"next":1131,"prev":1129,"to":276,"twin":1123,"wire_ref":null},{"edge":555,"face":282,"from":276,"geometry_use":{"pcurve":1131,"periodic_lift":[0,0],"sense":"Forward"},"id":1131,"loop_ref":282,"next":1128,"prev":1130,"to":279,"twin":1106,"wire_ref":null},{"edge":564,"face":283,"from":280,"geometry_use":{"pcurve":1132,"periodic_lift":[0,0],"sense":"Forward"},"id":1132,"loop_ref":283,"next":1133,"prev":1135,"to":285,"twin":1121,"wire_ref":null},{"edge":570,"face":283,"from":285,"geometry_use":{"pcurve":1133,"periodic_lift":[0,0],"sense":"Forward"},"id":1133,"loop_ref":283,"next":1134,"prev":1132,"to":286,"twin":1138,"wire_ref":null},{"edge":566,"face":283,"from":286,"geometry_use":{"pcurve":1134,"periodic_lift":[0,0],"sense":"Reverse"},"id":1134,"loop_ref":283,"next":1135,"prev":1133,"to":283,"twin":1125,"wire_ref":null},{"edge":571,"face":283,"from":283,"geometry_use":{"pcurve":1135,"periodic_lift":[0,0],"sense":"Reverse"},"id":1135,"loop_ref":283,"next":1132,"prev":1134,"to":280,"twin":1152,"wire_ref":null},{"edge":569,"face":284,"from":284,"geometry_use":{"pcurve":1136,"periodic_lift":[0,0],"sense":"Forward"},"id":1136,"loop_ref":284,"next":1137,"prev":1139,"to":287,"twin":1129,"wire_ref":null},{"edge":567,"face":284,"from":287,"geometry_use":{"pcurve":1137,"periodic_lift":[0,0],"sense":"Forward"},"id":1137,"loop_ref":284,"next":1138,"prev":1136,"to":286,"twin":1126,"wire_ref":null},{"edge":570,"face":284,"from":286,"geometry_use":{"pcurve":1138,"periodic_lift":[0,0],"sense":"Reverse"},"id":1138,"loop_ref":284,"next":1139,"prev":1137,"to":285,"twin":1133,"wire_ref":null},{"edge":563,"face":284,"from":285,"geometry_use":{"pcurve":1139,"periodic_lift":[0,0],"sense":"Reverse"},"id":1139,"loop_ref":284,"next":1136,"prev":1138,"to":284,"twin":1120,"wire_ref":null},{"edge":572,"face":285,"from":280,"geometry_use":{"pcurve":1140,"periodic_lift":[0,0],"sense":"Forward"},"id":1140,"loop_ref":285,"next":1141,"prev":1143,"to":288,"twin":1155,"wire_ref":null},{"edge":573,"face":285,"from":288,"geometry_use":{"pcurve":1141,"periodic_lift":[0,0],"sense":"Reverse"},"id":1141,"loop_ref":285,"next":1142,"prev":1140,"to":289,"twin":1159,"wire_ref":null},{"edge":574,"face":285,"from":289,"geometry_use":{"pcurve":1142,"periodic_lift":[0,0],"sense":"Reverse"},"id":1142,"loop_ref":285,"next":1143,"prev":1141,"to":281,"twin":1151,"wire_ref":null},{"edge":557,"face":285,"from":281,"geometry_use":{"pcurve":1143,"periodic_lift":[0,0],"sense":"Forward"},"id":1143,"loop_ref":285,"next":1140,"prev":1142,"to":280,"twin":1109,"wire_ref":null},{"edge":575,"face":286,"from":282,"geometry_use":{"pcurve":1144,"periodic_lift":[0,0],"sense":"Forward"},"id":1144,"loop_ref":286,"next":1145,"prev":1147,"to":290,"twin":1149,"wire_ref":null},{"edge":576,"face":286,"from":290,"geometry_use":{"pcurve":1145,"periodic_lift":[0,0],"sense":"Forward"},"id":1145,"loop_ref":286,"next":1146,"prev":1144,"to":291,"twin":1163,"wire_ref":null},{"edge":577,"face":286,"from":291,"geometry_use":{"pcurve":1146,"periodic_lift":[0,0],"sense":"Reverse"},"id":1146,"loop_ref":286,"next":1147,"prev":1145,"to":283,"twin":1153,"wire_ref":null},{"edge":560,"face":286,"from":283,"geometry_use":{"pcurve":1147,"periodic_lift":[0,0],"sense":"Reverse"},"id":1147,"loop_ref":286,"next":1144,"prev":1146,"to":282,"twin":1113,"wire_ref":null},{"edge":578,"face":287,"from":289,"geometry_use":{"pcurve":1148,"periodic_lift":[0,0],"sense":"Forward"},"id":1148,"loop_ref":287,"next":1149,"prev":1151,"to":290,"twin":1166,"wire_ref":null},{"edge":575,"face":287,"from":290,"geometry_use":{"pcurve":1149,"periodic_lift":[0,0],"sense":"Reverse"},"id":1149,"loop_ref":287,"next":1150,"prev":1148,"to":282,"twin":1144,"wire_ref":null},{"edge":562,"face":287,"from":282,"geometry_use":{"pcurve":1150,"periodic_lift":[0,0],"sense":"Reverse"},"id":1150,"loop_ref":287,"next":1151,"prev":1149,"to":281,"twin":1116,"wire_ref":null},{"edge":574,"face":287,"from":281,"geometry_use":{"pcurve":1151,"periodic_lift":[0,0],"sense":"Forward"},"id":1151,"loop_ref":287,"next":1148,"prev":1150,"to":289,"twin":1142,"wire_ref":null},{"edge":571,"face":288,"from":280,"geometry_use":{"pcurve":1152,"periodic_lift":[0,0],"sense":"Forward"},"id":1152,"loop_ref":288,"next":1153,"prev":1155,"to":283,"twin":1135,"wire_ref":null},{"edge":577,"face":288,"from":283,"geometry_use":{"pcurve":1153,"periodic_lift":[0,0],"sense":"Forward"},"id":1153,"loop_ref":288,"next":1154,"prev":1152,"to":291,"twin":1146,"wire_ref":null},{"edge":579,"face":288,"from":291,"geometry_use":{"pcurve":1154,"periodic_lift":[0,0],"sense":"Reverse"},"id":1154,"loop_ref":288,"next":1155,"prev":1153,"to":288,"twin":1179,"wire_ref":null},{"edge":572,"face":288,"from":288,"geometry_use":{"pcurve":1155,"periodic_lift":[0,0],"sense":"Reverse"},"id":1155,"loop_ref":288,"next":1152,"prev":1154,"to":280,"twin":1140,"wire_ref":null},{"edge":580,"face":289,"from":288,"geometry_use":{"pcurve":1156,"periodic_lift":[0,0],"sense":"Forward"},"id":1156,"loop_ref":289,"next":1157,"prev":1159,"to":292,"twin":1170,"wire_ref":null},{"edge":581,"face":289,"from":292,"geometry_use":{"pcurve":1157,"periodic_lift":[0,0],"sense":"Reverse"},"id":1157,"loop_ref":289,"next":1158,"prev":1156,"to":293,"twin":1191,"wire_ref":null},{"edge":582,"face":289,"from":293,"geometry_use":{"pcurve":1158,"periodic_lift":[0,0],"sense":"Reverse"},"id":1158,"loop_ref":289,"next":1159,"prev":1157,"to":289,"twin":1167,"wire_ref":null},{"edge":573,"face":289,"from":289,"geometry_use":{"pcurve":1159,"periodic_lift":[0,0],"sense":"Forward"},"id":1159,"loop_ref":289,"next":1156,"prev":1158,"to":288,"twin":1141,"wire_ref":null},{"edge":583,"face":290,"from":290,"geometry_use":{"pcurve":1160,"periodic_lift":[0,0],"sense":"Forward"},"id":1160,"loop_ref":290,"next":1161,"prev":1163,"to":294,"twin":1165,"wire_ref":null},{"edge":584,"face":290,"from":294,"geometry_use":{"pcurve":1161,"periodic_lift":[0,0],"sense":"Forward"},"id":1161,"loop_ref":290,"next":1162,"prev":1160,"to":295,"twin":1195,"wire_ref":null},{"edge":585,"face":290,"from":295,"geometry_use":{"pcurve":1162,"periodic_lift":[0,0],"sense":"Reverse"},"id":1162,"loop_ref":290,"next":1163,"prev":1161,"to":291,"twin":1172,"wire_ref":null},{"edge":576,"face":290,"from":291,"geometry_use":{"pcurve":1163,"periodic_lift":[0,0],"sense":"Reverse"},"id":1163,"loop_ref":290,"next":1160,"prev":1162,"to":290,"twin":1145,"wire_ref":null},{"edge":586,"face":291,"from":293,"geometry_use":{"pcurve":1164,"periodic_lift":[0,0],"sense":"Forward"},"id":1164,"loop_ref":291,"next":1165,"prev":1167,"to":294,"twin":1198,"wire_ref":null},{"edge":583,"face":291,"from":294,"geometry_use":{"pcurve":1165,"periodic_lift":[0,0],"sense":"Reverse"},"id":1165,"loop_ref":291,"next":1166,"prev":1164,"to":290,"twin":1160,"wire_ref":null},{"edge":578,"face":291,"from":290,"geometry_use":{"pcurve":1166,"periodic_lift":[0,0],"sense":"Reverse"},"id":1166,"loop_ref":291,"next":1167,"prev":1165,"to":289,"twin":1148,"wire_ref":null},{"edge":582,"face":291,"from":289,"geometry_use":{"pcurve":1167,"periodic_lift":[0,0],"sense":"Forward"},"id":1167,"loop_ref":291,"next":1164,"prev":1166,"to":293,"twin":1158,"wire_ref":null},{"edge":587,"face":292,"from":296,"geometry_use":{"pcurve":1168,"periodic_lift":[0,0],"sense":"Forward"},"id":1168,"loop_ref":292,"next":1169,"prev":1171,"to":297,"twin":1187,"wire_ref":null},{"edge":588,"face":292,"from":297,"geometry_use":{"pcurve":1169,"periodic_lift":[0,0],"sense":"Reverse"},"id":1169,"loop_ref":292,"next":1170,"prev":1168,"to":292,"twin":1180,"wire_ref":null},{"edge":580,"face":292,"from":292,"geometry_use":{"pcurve":1170,"periodic_lift":[0,0],"sense":"Reverse"},"id":1170,"loop_ref":292,"next":1171,"prev":1169,"to":288,"twin":1156,"wire_ref":null},{"edge":589,"face":292,"from":288,"geometry_use":{"pcurve":1171,"periodic_lift":[0,0],"sense":"Forward"},"id":1171,"loop_ref":292,"next":1168,"prev":1170,"to":296,"twin":1178,"wire_ref":null},{"edge":585,"face":293,"from":291,"geometry_use":{"pcurve":1172,"periodic_lift":[0,0],"sense":"Forward"},"id":1172,"loop_ref":293,"next":1173,"prev":1175,"to":295,"twin":1162,"wire_ref":null},{"edge":590,"face":293,"from":295,"geometry_use":{"pcurve":1173,"periodic_lift":[0,0],"sense":"Forward"},"id":1173,"loop_ref":293,"next":1174,"prev":1172,"to":298,"twin":1182,"wire_ref":null},{"edge":591,"face":293,"from":298,"geometry_use":{"pcurve":1174,"periodic_lift":[0,0],"sense":"Reverse"},"id":1174,"loop_ref":293,"next":1175,"prev":1173,"to":299,"twin":1185,"wire_ref":null},{"edge":592,"face":293,"from":299,"geometry_use":{"pcurve":1175,"periodic_lift":[0,0],"sense":"Reverse"},"id":1175,"loop_ref":293,"next":1172,"prev":1174,"to":291,"twin":1176,"wire_ref":null},{"edge":592,"face":294,"from":291,"geometry_use":{"pcurve":1176,"periodic_lift":[0,0],"sense":"Forward"},"id":1176,"loop_ref":294,"next":1177,"prev":1179,"to":299,"twin":1175,"wire_ref":null},{"edge":593,"face":294,"from":299,"geometry_use":{"pcurve":1177,"periodic_lift":[0,0],"sense":"Reverse"},"id":1177,"loop_ref":294,"next":1178,"prev":1176,"to":296,"twin":1184,"wire_ref":null},{"edge":589,"face":294,"from":296,"geometry_use":{"pcurve":1178,"periodic_lift":[0,0],"sense":"Reverse"},"id":1178,"loop_ref":294,"next":1179,"prev":1177,"to":288,"twin":1171,"wire_ref":null},{"edge":579,"face":294,"from":288,"geometry_use":{"pcurve":1179,"periodic_lift":[0,0],"sense":"Forward"},"id":1179,"loop_ref":294,"next":1176,"prev":1178,"to":291,"twin":1154,"wire_ref":null},{"edge":588,"face":295,"from":292,"geometry_use":{"pcurve":1180,"periodic_lift":[0,0],"sense":"Forward"},"id":1180,"loop_ref":295,"next":1181,"prev":1183,"to":297,"twin":1169,"wire_ref":null},{"edge":594,"face":295,"from":297,"geometry_use":{"pcurve":1181,"periodic_lift":[0,0],"sense":"Forward"},"id":1181,"loop_ref":295,"next":1182,"prev":1180,"to":298,"twin":1186,"wire_ref":null},{"edge":590,"face":295,"from":298,"geometry_use":{"pcurve":1182,"periodic_lift":[0,0],"sense":"Reverse"},"id":1182,"loop_ref":295,"next":1183,"prev":1181,"to":295,"twin":1173,"wire_ref":null},{"edge":595,"face":295,"from":295,"geometry_use":{"pcurve":1183,"periodic_lift":[0,0],"sense":"Reverse"},"id":1183,"loop_ref":295,"next":1180,"prev":1182,"to":292,"twin":1200,"wire_ref":null},{"edge":593,"face":296,"from":296,"geometry_use":{"pcurve":1184,"periodic_lift":[0,0],"sense":"Forward"},"id":1184,"loop_ref":296,"next":1185,"prev":1187,"to":299,"twin":1177,"wire_ref":null},{"edge":591,"face":296,"from":299,"geometry_use":{"pcurve":1185,"periodic_lift":[0,0],"sense":"Forward"},"id":1185,"loop_ref":296,"next":1186,"prev":1184,"to":298,"twin":1174,"wire_ref":null},{"edge":594,"face":296,"from":298,"geometry_use":{"pcurve":1186,"periodic_lift":[0,0],"sense":"Reverse"},"id":1186,"loop_ref":296,"next":1187,"prev":1185,"to":297,"twin":1181,"wire_ref":null},{"edge":587,"face":296,"from":297,"geometry_use":{"pcurve":1187,"periodic_lift":[0,0],"sense":"Reverse"},"id":1187,"loop_ref":296,"next":1184,"prev":1186,"to":296,"twin":1168,"wire_ref":null},{"edge":596,"face":297,"from":292,"geometry_use":{"pcurve":1188,"periodic_lift":[0,0],"sense":"Forward"},"id":1188,"loop_ref":297,"next":1189,"prev":1191,"to":300,"twin":1203,"wire_ref":null},{"edge":597,"face":297,"from":300,"geometry_use":{"pcurve":1189,"periodic_lift":[0,0],"sense":"Reverse"},"id":1189,"loop_ref":297,"next":1190,"prev":1188,"to":301,"twin":1207,"wire_ref":null},{"edge":598,"face":297,"from":301,"geometry_use":{"pcurve":1190,"periodic_lift":[0,0],"sense":"Reverse"},"id":1190,"loop_ref":297,"next":1191,"prev":1189,"to":293,"twin":1199,"wire_ref":null},{"edge":581,"face":297,"from":293,"geometry_use":{"pcurve":1191,"periodic_lift":[0,0],"sense":"Forward"},"id":1191,"loop_ref":297,"next":1188,"prev":1190,"to":292,"twin":1157,"wire_ref":null},{"edge":599,"face":298,"from":294,"geometry_use":{"pcurve":1192,"periodic_lift":[0,0],"sense":"Forward"},"id":1192,"loop_ref":298,"next":1193,"prev":1195,"to":302,"twin":1197,"wire_ref":null},{"edge":600,"face":298,"from":302,"geometry_use":{"pcurve":1193,"periodic_lift":[0,0],"sense":"Forward"},"id":1193,"loop_ref":298,"next":1194,"prev":1192,"to":303,"twin":1211,"wire_ref":null},{"edge":601,"face":298,"from":303,"geometry_use":{"pcurve":1194,"periodic_lift":[0,0],"sense":"Reverse"},"id":1194,"loop_ref":298,"next":1195,"prev":1193,"to":295,"twin":1201,"wire_ref":null},{"edge":584,"face":298,"from":295,"geometry_use":{"pcurve":1195,"periodic_lift":[0,0],"sense":"Reverse"},"id":1195,"loop_ref":298,"next":1192,"prev":1194,"to":294,"twin":1161,"wire_ref":null},{"edge":602,"face":299,"from":301,"geometry_use":{"pcurve":1196,"periodic_lift":[0,0],"sense":"Forward"},"id":1196,"loop_ref":299,"next":1197,"prev":1199,"to":302,"twin":1214,"wire_ref":null},{"edge":599,"face":299,"from":302,"geometry_use":{"pcurve":1197,"periodic_lift":[0,0],"sense":"Reverse"},"id":1197,"loop_ref":299,"next":1198,"prev":1196,"to":294,"twin":1192,"wire_ref":null},{"edge":586,"face":299,"from":294,"geometry_use":{"pcurve":1198,"periodic_lift":[0,0],"sense":"Reverse"},"id":1198,"loop_ref":299,"next":1199,"prev":1197,"to":293,"twin":1164,"wire_ref":null},{"edge":598,"face":299,"from":293,"geometry_use":{"pcurve":1199,"periodic_lift":[0,0],"sense":"Forward"},"id":1199,"loop_ref":299,"next":1196,"prev":1198,"to":301,"twin":1190,"wire_ref":null},{"edge":595,"face":300,"from":292,"geometry_use":{"pcurve":1200,"periodic_lift":[0,0],"sense":"Forward"},"id":1200,"loop_ref":300,"next":1201,"prev":1203,"to":295,"twin":1183,"wire_ref":null},{"edge":601,"face":300,"from":295,"geometry_use":{"pcurve":1201,"periodic_lift":[0,0],"sense":"Forward"},"id":1201,"loop_ref":300,"next":1202,"prev":1200,"to":303,"twin":1194,"wire_ref":null},{"edge":603,"face":300,"from":303,"geometry_use":{"pcurve":1202,"periodic_lift":[0,0],"sense":"Reverse"},"id":1202,"loop_ref":300,"next":1203,"prev":1201,"to":300,"twin":1227,"wire_ref":null},{"edge":596,"face":300,"from":300,"geometry_use":{"pcurve":1203,"periodic_lift":[0,0],"sense":"Reverse"},"id":1203,"loop_ref":300,"next":1200,"prev":1202,"to":292,"twin":1188,"wire_ref":null},{"edge":604,"face":301,"from":300,"geometry_use":{"pcurve":1204,"periodic_lift":[0,0],"sense":"Forward"},"id":1204,"loop_ref":301,"next":1205,"prev":1207,"to":304,"twin":1218,"wire_ref":null},{"edge":605,"face":301,"from":304,"geometry_use":{"pcurve":1205,"periodic_lift":[0,0],"sense":"Reverse"},"id":1205,"loop_ref":301,"next":1206,"prev":1204,"to":305,"twin":1239,"wire_ref":null},{"edge":606,"face":301,"from":305,"geometry_use":{"pcurve":1206,"periodic_lift":[0,0],"sense":"Reverse"},"id":1206,"loop_ref":301,"next":1207,"prev":1205,"to":301,"twin":1215,"wire_ref":null},{"edge":597,"face":301,"from":301,"geometry_use":{"pcurve":1207,"periodic_lift":[0,0],"sense":"Forward"},"id":1207,"loop_ref":301,"next":1204,"prev":1206,"to":300,"twin":1189,"wire_ref":null},{"edge":607,"face":302,"from":302,"geometry_use":{"pcurve":1208,"periodic_lift":[0,0],"sense":"Forward"},"id":1208,"loop_ref":302,"next":1209,"prev":1211,"to":306,"twin":1213,"wire_ref":null},{"edge":608,"face":302,"from":306,"geometry_use":{"pcurve":1209,"periodic_lift":[0,0],"sense":"Forward"},"id":1209,"loop_ref":302,"next":1210,"prev":1208,"to":307,"twin":1243,"wire_ref":null},{"edge":609,"face":302,"from":307,"geometry_use":{"pcurve":1210,"periodic_lift":[0,0],"sense":"Reverse"},"id":1210,"loop_ref":302,"next":1211,"prev":1209,"to":303,"twin":1220,"wire_ref":null},{"edge":600,"face":302,"from":303,"geometry_use":{"pcurve":1211,"periodic_lift":[0,0],"sense":"Reverse"},"id":1211,"loop_ref":302,"next":1208,"prev":1210,"to":302,"twin":1193,"wire_ref":null},{"edge":610,"face":303,"from":305,"geometry_use":{"pcurve":1212,"periodic_lift":[0,0],"sense":"Forward"},"id":1212,"loop_ref":303,"next":1213,"prev":1215,"to":306,"twin":1246,"wire_ref":null},{"edge":607,"face":303,"from":306,"geometry_use":{"pcurve":1213,"periodic_lift":[0,0],"sense":"Reverse"},"id":1213,"loop_ref":303,"next":1214,"prev":1212,"to":302,"twin":1208,"wire_ref":null},{"edge":602,"face":303,"from":302,"geometry_use":{"pcurve":1214,"periodic_lift":[0,0],"sense":"Reverse"},"id":1214,"loop_ref":303,"next":1215,"prev":1213,"to":301,"twin":1196,"wire_ref":null},{"edge":606,"face":303,"from":301,"geometry_use":{"pcurve":1215,"periodic_lift":[0,0],"sense":"Forward"},"id":1215,"loop_ref":303,"next":1212,"prev":1214,"to":305,"twin":1206,"wire_ref":null},{"edge":611,"face":304,"from":308,"geometry_use":{"pcurve":1216,"periodic_lift":[0,0],"sense":"Forward"},"id":1216,"loop_ref":304,"next":1217,"prev":1219,"to":309,"twin":1235,"wire_ref":null},{"edge":612,"face":304,"from":309,"geometry_use":{"pcurve":1217,"periodic_lift":[0,0],"sense":"Reverse"},"id":1217,"loop_ref":304,"next":1218,"prev":1216,"to":304,"twin":1228,"wire_ref":null},{"edge":604,"face":304,"from":304,"geometry_use":{"pcurve":1218,"periodic_lift":[0,0],"sense":"Reverse"},"id":1218,"loop_ref":304,"next":1219,"prev":1217,"to":300,"twin":1204,"wire_ref":null},{"edge":613,"face":304,"from":300,"geometry_use":{"pcurve":1219,"periodic_lift":[0,0],"sense":"Forward"},"id":1219,"loop_ref":304,"next":1216,"prev":1218,"to":308,"twin":1226,"wire_ref":null},{"edge":609,"face":305,"from":303,"geometry_use":{"pcurve":1220,"periodic_lift":[0,0],"sense":"Forward"},"id":1220,"loop_ref":305,"next":1221,"prev":1223,"to":307,"twin":1210,"wire_ref":null},{"edge":614,"face":305,"from":307,"geometry_use":{"pcurve":1221,"periodic_lift":[0,0],"sense":"Forward"},"id":1221,"loop_ref":305,"next":1222,"prev":1220,"to":310,"twin":1230,"wire_ref":null},{"edge":615,"face":305,"from":310,"geometry_use":{"pcurve":1222,"periodic_lift":[0,0],"sense":"Reverse"},"id":1222,"loop_ref":305,"next":1223,"prev":1221,"to":311,"twin":1233,"wire_ref":null},{"edge":616,"face":305,"from":311,"geometry_use":{"pcurve":1223,"periodic_lift":[0,0],"sense":"Reverse"},"id":1223,"loop_ref":305,"next":1220,"prev":1222,"to":303,"twin":1224,"wire_ref":null},{"edge":616,"face":306,"from":303,"geometry_use":{"pcurve":1224,"periodic_lift":[0,0],"sense":"Forward"},"id":1224,"loop_ref":306,"next":1225,"prev":1227,"to":311,"twin":1223,"wire_ref":null},{"edge":617,"face":306,"from":311,"geometry_use":{"pcurve":1225,"periodic_lift":[0,0],"sense":"Reverse"},"id":1225,"loop_ref":306,"next":1226,"prev":1224,"to":308,"twin":1232,"wire_ref":null},{"edge":613,"face":306,"from":308,"geometry_use":{"pcurve":1226,"periodic_lift":[0,0],"sense":"Reverse"},"id":1226,"loop_ref":306,"next":1227,"prev":1225,"to":300,"twin":1219,"wire_ref":null},{"edge":603,"face":306,"from":300,"geometry_use":{"pcurve":1227,"periodic_lift":[0,0],"sense":"Forward"},"id":1227,"loop_ref":306,"next":1224,"prev":1226,"to":303,"twin":1202,"wire_ref":null},{"edge":612,"face":307,"from":304,"geometry_use":{"pcurve":1228,"periodic_lift":[0,0],"sense":"Forward"},"id":1228,"loop_ref":307,"next":1229,"prev":1231,"to":309,"twin":1217,"wire_ref":null},{"edge":618,"face":307,"from":309,"geometry_use":{"pcurve":1229,"periodic_lift":[0,0],"sense":"Forward"},"id":1229,"loop_ref":307,"next":1230,"prev":1228,"to":310,"twin":1234,"wire_ref":null},{"edge":614,"face":307,"from":310,"geometry_use":{"pcurve":1230,"periodic_lift":[0,0],"sense":"Reverse"},"id":1230,"loop_ref":307,"next":1231,"prev":1229,"to":307,"twin":1221,"wire_ref":null},{"edge":619,"face":307,"from":307,"geometry_use":{"pcurve":1231,"periodic_lift":[0,0],"sense":"Reverse"},"id":1231,"loop_ref":307,"next":1228,"prev":1230,"to":304,"twin":1248,"wire_ref":null},{"edge":617,"face":308,"from":308,"geometry_use":{"pcurve":1232,"periodic_lift":[0,0],"sense":"Forward"},"id":1232,"loop_ref":308,"next":1233,"prev":1235,"to":311,"twin":1225,"wire_ref":null},{"edge":615,"face":308,"from":311,"geometry_use":{"pcurve":1233,"periodic_lift":[0,0],"sense":"Forward"},"id":1233,"loop_ref":308,"next":1234,"prev":1232,"to":310,"twin":1222,"wire_ref":null},{"edge":618,"face":308,"from":310,"geometry_use":{"pcurve":1234,"periodic_lift":[0,0],"sense":"Reverse"},"id":1234,"loop_ref":308,"next":1235,"prev":1233,"to":309,"twin":1229,"wire_ref":null},{"edge":611,"face":308,"from":309,"geometry_use":{"pcurve":1235,"periodic_lift":[0,0],"sense":"Reverse"},"id":1235,"loop_ref":308,"next":1232,"prev":1234,"to":308,"twin":1216,"wire_ref":null},{"edge":620,"face":309,"from":304,"geometry_use":{"pcurve":1236,"periodic_lift":[0,0],"sense":"Forward"},"id":1236,"loop_ref":309,"next":1237,"prev":1239,"to":312,"twin":1251,"wire_ref":null},{"edge":621,"face":309,"from":312,"geometry_use":{"pcurve":1237,"periodic_lift":[0,0],"sense":"Reverse"},"id":1237,"loop_ref":309,"next":1238,"prev":1236,"to":313,"twin":1255,"wire_ref":null},{"edge":622,"face":309,"from":313,"geometry_use":{"pcurve":1238,"periodic_lift":[0,0],"sense":"Reverse"},"id":1238,"loop_ref":309,"next":1239,"prev":1237,"to":305,"twin":1247,"wire_ref":null},{"edge":605,"face":309,"from":305,"geometry_use":{"pcurve":1239,"periodic_lift":[0,0],"sense":"Forward"},"id":1239,"loop_ref":309,"next":1236,"prev":1238,"to":304,"twin":1205,"wire_ref":null},{"edge":623,"face":310,"from":306,"geometry_use":{"pcurve":1240,"periodic_lift":[0,0],"sense":"Forward"},"id":1240,"loop_ref":310,"next":1241,"prev":1243,"to":314,"twin":1245,"wire_ref":null},{"edge":624,"face":310,"from":314,"geometry_use":{"pcurve":1241,"periodic_lift":[0,0],"sense":"Forward"},"id":1241,"loop_ref":310,"next":1242,"prev":1240,"to":315,"twin":1259,"wire_ref":null},{"edge":625,"face":310,"from":315,"geometry_use":{"pcurve":1242,"periodic_lift":[0,0],"sense":"Reverse"},"id":1242,"loop_ref":310,"next":1243,"prev":1241,"to":307,"twin":1249,"wire_ref":null},{"edge":608,"face":310,"from":307,"geometry_use":{"pcurve":1243,"periodic_lift":[0,0],"sense":"Reverse"},"id":1243,"loop_ref":310,"next":1240,"prev":1242,"to":306,"twin":1209,"wire_ref":null},{"edge":626,"face":311,"from":313,"geometry_use":{"pcurve":1244,"periodic_lift":[0,0],"sense":"Forward"},"id":1244,"loop_ref":311,"next":1245,"prev":1247,"to":314,"twin":1262,"wire_ref":null},{"edge":623,"face":311,"from":314,"geometry_use":{"pcurve":1245,"periodic_lift":[0,0],"sense":"Reverse"},"id":1245,"loop_ref":311,"next":1246,"prev":1244,"to":306,"twin":1240,"wire_ref":null},{"edge":610,"face":311,"from":306,"geometry_use":{"pcurve":1246,"periodic_lift":[0,0],"sense":"Reverse"},"id":1246,"loop_ref":311,"next":1247,"prev":1245,"to":305,"twin":1212,"wire_ref":null},{"edge":622,"face":311,"from":305,"geometry_use":{"pcurve":1247,"periodic_lift":[0,0],"sense":"Forward"},"id":1247,"loop_ref":311,"next":1244,"prev":1246,"to":313,"twin":1238,"wire_ref":null},{"edge":619,"face":312,"from":304,"geometry_use":{"pcurve":1248,"periodic_lift":[0,0],"sense":"Forward"},"id":1248,"loop_ref":312,"next":1249,"prev":1251,"to":307,"twin":1231,"wire_ref":null},{"edge":625,"face":312,"from":307,"geometry_use":{"pcurve":1249,"periodic_lift":[0,0],"sense":"Forward"},"id":1249,"loop_ref":312,"next":1250,"prev":1248,"to":315,"twin":1242,"wire_ref":null},{"edge":627,"face":312,"from":315,"geometry_use":{"pcurve":1250,"periodic_lift":[0,0],"sense":"Reverse"},"id":1250,"loop_ref":312,"next":1251,"prev":1249,"to":312,"twin":1275,"wire_ref":null},{"edge":620,"face":312,"from":312,"geometry_use":{"pcurve":1251,"periodic_lift":[0,0],"sense":"Reverse"},"id":1251,"loop_ref":312,"next":1248,"prev":1250,"to":304,"twin":1236,"wire_ref":null},{"edge":628,"face":313,"from":312,"geometry_use":{"pcurve":1252,"periodic_lift":[0,0],"sense":"Forward"},"id":1252,"loop_ref":313,"next":1253,"prev":1255,"to":316,"twin":1266,"wire_ref":null},{"edge":629,"face":313,"from":316,"geometry_use":{"pcurve":1253,"periodic_lift":[0,0],"sense":"Reverse"},"id":1253,"loop_ref":313,"next":1254,"prev":1252,"to":317,"twin":1287,"wire_ref":null},{"edge":630,"face":313,"from":317,"geometry_use":{"pcurve":1254,"periodic_lift":[0,0],"sense":"Reverse"},"id":1254,"loop_ref":313,"next":1255,"prev":1253,"to":313,"twin":1263,"wire_ref":null},{"edge":621,"face":313,"from":313,"geometry_use":{"pcurve":1255,"periodic_lift":[0,0],"sense":"Forward"},"id":1255,"loop_ref":313,"next":1252,"prev":1254,"to":312,"twin":1237,"wire_ref":null},{"edge":631,"face":314,"from":314,"geometry_use":{"pcurve":1256,"periodic_lift":[0,0],"sense":"Forward"},"id":1256,"loop_ref":314,"next":1257,"prev":1259,"to":318,"twin":1261,"wire_ref":null},{"edge":632,"face":314,"from":318,"geometry_use":{"pcurve":1257,"periodic_lift":[0,0],"sense":"Forward"},"id":1257,"loop_ref":314,"next":1258,"prev":1256,"to":319,"twin":1291,"wire_ref":null},{"edge":633,"face":314,"from":319,"geometry_use":{"pcurve":1258,"periodic_lift":[0,0],"sense":"Reverse"},"id":1258,"loop_ref":314,"next":1259,"prev":1257,"to":315,"twin":1268,"wire_ref":null},{"edge":624,"face":314,"from":315,"geometry_use":{"pcurve":1259,"periodic_lift":[0,0],"sense":"Reverse"},"id":1259,"loop_ref":314,"next":1256,"prev":1258,"to":314,"twin":1241,"wire_ref":null},{"edge":634,"face":315,"from":317,"geometry_use":{"pcurve":1260,"periodic_lift":[0,0],"sense":"Forward"},"id":1260,"loop_ref":315,"next":1261,"prev":1263,"to":318,"twin":1294,"wire_ref":null},{"edge":631,"face":315,"from":318,"geometry_use":{"pcurve":1261,"periodic_lift":[0,0],"sense":"Reverse"},"id":1261,"loop_ref":315,"next":1262,"prev":1260,"to":314,"twin":1256,"wire_ref":null},{"edge":626,"face":315,"from":314,"geometry_use":{"pcurve":1262,"periodic_lift":[0,0],"sense":"Reverse"},"id":1262,"loop_ref":315,"next":1263,"prev":1261,"to":313,"twin":1244,"wire_ref":null},{"edge":630,"face":315,"from":313,"geometry_use":{"pcurve":1263,"periodic_lift":[0,0],"sense":"Forward"},"id":1263,"loop_ref":315,"next":1260,"prev":1262,"to":317,"twin":1254,"wire_ref":null},{"edge":635,"face":316,"from":320,"geometry_use":{"pcurve":1264,"periodic_lift":[0,0],"sense":"Forward"},"id":1264,"loop_ref":316,"next":1265,"prev":1267,"to":321,"twin":1283,"wire_ref":null},{"edge":636,"face":316,"from":321,"geometry_use":{"pcurve":1265,"periodic_lift":[0,0],"sense":"Reverse"},"id":1265,"loop_ref":316,"next":1266,"prev":1264,"to":316,"twin":1276,"wire_ref":null},{"edge":628,"face":316,"from":316,"geometry_use":{"pcurve":1266,"periodic_lift":[0,0],"sense":"Reverse"},"id":1266,"loop_ref":316,"next":1267,"prev":1265,"to":312,"twin":1252,"wire_ref":null},{"edge":637,"face":316,"from":312,"geometry_use":{"pcurve":1267,"periodic_lift":[0,0],"sense":"Forward"},"id":1267,"loop_ref":316,"next":1264,"prev":1266,"to":320,"twin":1274,"wire_ref":null},{"edge":633,"face":317,"from":315,"geometry_use":{"pcurve":1268,"periodic_lift":[0,0],"sense":"Forward"},"id":1268,"loop_ref":317,"next":1269,"prev":1271,"to":319,"twin":1258,"wire_ref":null},{"edge":638,"face":317,"from":319,"geometry_use":{"pcurve":1269,"periodic_lift":[0,0],"sense":"Forward"},"id":1269,"loop_ref":317,"next":1270,"prev":1268,"to":322,"twin":1278,"wire_ref":null},{"edge":639,"face":317,"from":322,"geometry_use":{"pcurve":1270,"periodic_lift":[0,0],"sense":"Reverse"},"id":1270,"loop_ref":317,"next":1271,"prev":1269,"to":323,"twin":1281,"wire_ref":null},{"edge":640,"face":317,"from":323,"geometry_use":{"pcurve":1271,"periodic_lift":[0,0],"sense":"Reverse"},"id":1271,"loop_ref":317,"next":1268,"prev":1270,"to":315,"twin":1272,"wire_ref":null},{"edge":640,"face":318,"from":315,"geometry_use":{"pcurve":1272,"periodic_lift":[0,0],"sense":"Forward"},"id":1272,"loop_ref":318,"next":1273,"prev":1275,"to":323,"twin":1271,"wire_ref":null},{"edge":641,"face":318,"from":323,"geometry_use":{"pcurve":1273,"periodic_lift":[0,0],"sense":"Reverse"},"id":1273,"loop_ref":318,"next":1274,"prev":1272,"to":320,"twin":1280,"wire_ref":null},{"edge":637,"face":318,"from":320,"geometry_use":{"pcurve":1274,"periodic_lift":[0,0],"sense":"Reverse"},"id":1274,"loop_ref":318,"next":1275,"prev":1273,"to":312,"twin":1267,"wire_ref":null},{"edge":627,"face":318,"from":312,"geometry_use":{"pcurve":1275,"periodic_lift":[0,0],"sense":"Forward"},"id":1275,"loop_ref":318,"next":1272,"prev":1274,"to":315,"twin":1250,"wire_ref":null},{"edge":636,"face":319,"from":316,"geometry_use":{"pcurve":1276,"periodic_lift":[0,0],"sense":"Forward"},"id":1276,"loop_ref":319,"next":1277,"prev":1279,"to":321,"twin":1265,"wire_ref":null},{"edge":642,"face":319,"from":321,"geometry_use":{"pcurve":1277,"periodic_lift":[0,0],"sense":"Forward"},"id":1277,"loop_ref":319,"next":1278,"prev":1276,"to":322,"twin":1282,"wire_ref":null},{"edge":638,"face":319,"from":322,"geometry_use":{"pcurve":1278,"periodic_lift":[0,0],"sense":"Reverse"},"id":1278,"loop_ref":319,"next":1279,"prev":1277,"to":319,"twin":1269,"wire_ref":null},{"edge":643,"face":319,"from":319,"geometry_use":{"pcurve":1279,"periodic_lift":[0,0],"sense":"Reverse"},"id":1279,"loop_ref":319,"next":1276,"prev":1278,"to":316,"twin":1296,"wire_ref":null},{"edge":641,"face":320,"from":320,"geometry_use":{"pcurve":1280,"periodic_lift":[0,0],"sense":"Forward"},"id":1280,"loop_ref":320,"next":1281,"prev":1283,"to":323,"twin":1273,"wire_ref":null},{"edge":639,"face":320,"from":323,"geometry_use":{"pcurve":1281,"periodic_lift":[0,0],"sense":"Forward"},"id":1281,"loop_ref":320,"next":1282,"prev":1280,"to":322,"twin":1270,"wire_ref":null},{"edge":642,"face":320,"from":322,"geometry_use":{"pcurve":1282,"periodic_lift":[0,0],"sense":"Reverse"},"id":1282,"loop_ref":320,"next":1283,"prev":1281,"to":321,"twin":1277,"wire_ref":null},{"edge":635,"face":320,"from":321,"geometry_use":{"pcurve":1283,"periodic_lift":[0,0],"sense":"Reverse"},"id":1283,"loop_ref":320,"next":1280,"prev":1282,"to":320,"twin":1264,"wire_ref":null},{"edge":644,"face":321,"from":316,"geometry_use":{"pcurve":1284,"periodic_lift":[0,0],"sense":"Forward"},"id":1284,"loop_ref":321,"next":1285,"prev":1287,"to":324,"twin":1299,"wire_ref":null},{"edge":645,"face":321,"from":324,"geometry_use":{"pcurve":1285,"periodic_lift":[0,0],"sense":"Reverse"},"id":1285,"loop_ref":321,"next":1286,"prev":1284,"to":325,"twin":1303,"wire_ref":null},{"edge":646,"face":321,"from":325,"geometry_use":{"pcurve":1286,"periodic_lift":[0,0],"sense":"Reverse"},"id":1286,"loop_ref":321,"next":1287,"prev":1285,"to":317,"twin":1295,"wire_ref":null},{"edge":629,"face":321,"from":317,"geometry_use":{"pcurve":1287,"periodic_lift":[0,0],"sense":"Forward"},"id":1287,"loop_ref":321,"next":1284,"prev":1286,"to":316,"twin":1253,"wire_ref":null},{"edge":647,"face":322,"from":318,"geometry_use":{"pcurve":1288,"periodic_lift":[0,0],"sense":"Forward"},"id":1288,"loop_ref":322,"next":1289,"prev":1291,"to":326,"twin":1293,"wire_ref":null},{"edge":648,"face":322,"from":326,"geometry_use":{"pcurve":1289,"periodic_lift":[0,0],"sense":"Forward"},"id":1289,"loop_ref":322,"next":1290,"prev":1288,"to":327,"twin":1307,"wire_ref":null},{"edge":649,"face":322,"from":327,"geometry_use":{"pcurve":1290,"periodic_lift":[0,0],"sense":"Reverse"},"id":1290,"loop_ref":322,"next":1291,"prev":1289,"to":319,"twin":1297,"wire_ref":null},{"edge":632,"face":322,"from":319,"geometry_use":{"pcurve":1291,"periodic_lift":[0,0],"sense":"Reverse"},"id":1291,"loop_ref":322,"next":1288,"prev":1290,"to":318,"twin":1257,"wire_ref":null},{"edge":650,"face":323,"from":325,"geometry_use":{"pcurve":1292,"periodic_lift":[0,0],"sense":"Forward"},"id":1292,"loop_ref":323,"next":1293,"prev":1295,"to":326,"twin":1310,"wire_ref":null},{"edge":647,"face":323,"from":326,"geometry_use":{"pcurve":1293,"periodic_lift":[0,0],"sense":"Reverse"},"id":1293,"loop_ref":323,"next":1294,"prev":1292,"to":318,"twin":1288,"wire_ref":null},{"edge":634,"face":323,"from":318,"geometry_use":{"pcurve":1294,"periodic_lift":[0,0],"sense":"Reverse"},"id":1294,"loop_ref":323,"next":1295,"prev":1293,"to":317,"twin":1260,"wire_ref":null},{"edge":646,"face":323,"from":317,"geometry_use":{"pcurve":1295,"periodic_lift":[0,0],"sense":"Forward"},"id":1295,"loop_ref":323,"next":1292,"prev":1294,"to":325,"twin":1286,"wire_ref":null},{"edge":643,"face":324,"from":316,"geometry_use":{"pcurve":1296,"periodic_lift":[0,0],"sense":"Forward"},"id":1296,"loop_ref":324,"next":1297,"prev":1299,"to":319,"twin":1279,"wire_ref":null},{"edge":649,"face":324,"from":319,"geometry_use":{"pcurve":1297,"periodic_lift":[0,0],"sense":"Forward"},"id":1297,"loop_ref":324,"next":1298,"prev":1296,"to":327,"twin":1290,"wire_ref":null},{"edge":651,"face":324,"from":327,"geometry_use":{"pcurve":1298,"periodic_lift":[0,0],"sense":"Reverse"},"id":1298,"loop_ref":324,"next":1299,"prev":1297,"to":324,"twin":1323,"wire_ref":null},{"edge":644,"face":324,"from":324,"geometry_use":{"pcurve":1299,"periodic_lift":[0,0],"sense":"Reverse"},"id":1299,"loop_ref":324,"next":1296,"prev":1298,"to":316,"twin":1284,"wire_ref":null},{"edge":652,"face":325,"from":324,"geometry_use":{"pcurve":1300,"periodic_lift":[0,0],"sense":"Forward"},"id":1300,"loop_ref":325,"next":1301,"prev":1303,"to":328,"twin":1314,"wire_ref":null},{"edge":653,"face":325,"from":328,"geometry_use":{"pcurve":1301,"periodic_lift":[0,0],"sense":"Reverse"},"id":1301,"loop_ref":325,"next":1302,"prev":1300,"to":329,"twin":1335,"wire_ref":null},{"edge":654,"face":325,"from":329,"geometry_use":{"pcurve":1302,"periodic_lift":[0,0],"sense":"Reverse"},"id":1302,"loop_ref":325,"next":1303,"prev":1301,"to":325,"twin":1311,"wire_ref":null},{"edge":645,"face":325,"from":325,"geometry_use":{"pcurve":1303,"periodic_lift":[0,0],"sense":"Forward"},"id":1303,"loop_ref":325,"next":1300,"prev":1302,"to":324,"twin":1285,"wire_ref":null},{"edge":655,"face":326,"from":326,"geometry_use":{"pcurve":1304,"periodic_lift":[0,0],"sense":"Forward"},"id":1304,"loop_ref":326,"next":1305,"prev":1307,"to":330,"twin":1309,"wire_ref":null},{"edge":656,"face":326,"from":330,"geometry_use":{"pcurve":1305,"periodic_lift":[0,0],"sense":"Forward"},"id":1305,"loop_ref":326,"next":1306,"prev":1304,"to":331,"twin":1339,"wire_ref":null},{"edge":657,"face":326,"from":331,"geometry_use":{"pcurve":1306,"periodic_lift":[0,0],"sense":"Reverse"},"id":1306,"loop_ref":326,"next":1307,"prev":1305,"to":327,"twin":1316,"wire_ref":null},{"edge":648,"face":326,"from":327,"geometry_use":{"pcurve":1307,"periodic_lift":[0,0],"sense":"Reverse"},"id":1307,"loop_ref":326,"next":1304,"prev":1306,"to":326,"twin":1289,"wire_ref":null},{"edge":658,"face":327,"from":329,"geometry_use":{"pcurve":1308,"periodic_lift":[0,0],"sense":"Forward"},"id":1308,"loop_ref":327,"next":1309,"prev":1311,"to":330,"twin":1342,"wire_ref":null},{"edge":655,"face":327,"from":330,"geometry_use":{"pcurve":1309,"periodic_lift":[0,0],"sense":"Reverse"},"id":1309,"loop_ref":327,"next":1310,"prev":1308,"to":326,"twin":1304,"wire_ref":null},{"edge":650,"face":327,"from":326,"geometry_use":{"pcurve":1310,"periodic_lift":[0,0],"sense":"Reverse"},"id":1310,"loop_ref":327,"next":1311,"prev":1309,"to":325,"twin":1292,"wire_ref":null},{"edge":654,"face":327,"from":325,"geometry_use":{"pcurve":1311,"periodic_lift":[0,0],"sense":"Forward"},"id":1311,"loop_ref":327,"next":1308,"prev":1310,"to":329,"twin":1302,"wire_ref":null},{"edge":659,"face":328,"from":332,"geometry_use":{"pcurve":1312,"periodic_lift":[0,0],"sense":"Forward"},"id":1312,"loop_ref":328,"next":1313,"prev":1315,"to":333,"twin":1331,"wire_ref":null},{"edge":660,"face":328,"from":333,"geometry_use":{"pcurve":1313,"periodic_lift":[0,0],"sense":"Reverse"},"id":1313,"loop_ref":328,"next":1314,"prev":1312,"to":328,"twin":1324,"wire_ref":null},{"edge":652,"face":328,"from":328,"geometry_use":{"pcurve":1314,"periodic_lift":[0,0],"sense":"Reverse"},"id":1314,"loop_ref":328,"next":1315,"prev":1313,"to":324,"twin":1300,"wire_ref":null},{"edge":661,"face":328,"from":324,"geometry_use":{"pcurve":1315,"periodic_lift":[0,0],"sense":"Forward"},"id":1315,"loop_ref":328,"next":1312,"prev":1314,"to":332,"twin":1322,"wire_ref":null},{"edge":657,"face":329,"from":327,"geometry_use":{"pcurve":1316,"periodic_lift":[0,0],"sense":"Forward"},"id":1316,"loop_ref":329,"next":1317,"prev":1319,"to":331,"twin":1306,"wire_ref":null},{"edge":662,"face":329,"from":331,"geometry_use":{"pcurve":1317,"periodic_lift":[0,0],"sense":"Forward"},"id":1317,"loop_ref":329,"next":1318,"prev":1316,"to":334,"twin":1326,"wire_ref":null},{"edge":663,"face":329,"from":334,"geometry_use":{"pcurve":1318,"periodic_lift":[0,0],"sense":"Reverse"},"id":1318,"loop_ref":329,"next":1319,"prev":1317,"to":335,"twin":1329,"wire_ref":null},{"edge":664,"face":329,"from":335,"geometry_use":{"pcurve":1319,"periodic_lift":[0,0],"sense":"Reverse"},"id":1319,"loop_ref":329,"next":1316,"prev":1318,"to":327,"twin":1320,"wire_ref":null},{"edge":664,"face":330,"from":327,"geometry_use":{"pcurve":1320,"periodic_lift":[0,0],"sense":"Forward"},"id":1320,"loop_ref":330,"next":1321,"prev":1323,"to":335,"twin":1319,"wire_ref":null},{"edge":665,"face":330,"from":335,"geometry_use":{"pcurve":1321,"periodic_lift":[0,0],"sense":"Reverse"},"id":1321,"loop_ref":330,"next":1322,"prev":1320,"to":332,"twin":1328,"wire_ref":null},{"edge":661,"face":330,"from":332,"geometry_use":{"pcurve":1322,"periodic_lift":[0,0],"sense":"Reverse"},"id":1322,"loop_ref":330,"next":1323,"prev":1321,"to":324,"twin":1315,"wire_ref":null},{"edge":651,"face":330,"from":324,"geometry_use":{"pcurve":1323,"periodic_lift":[0,0],"sense":"Forward"},"id":1323,"loop_ref":330,"next":1320,"prev":1322,"to":327,"twin":1298,"wire_ref":null},{"edge":660,"face":331,"from":328,"geometry_use":{"pcurve":1324,"periodic_lift":[0,0],"sense":"Forward"},"id":1324,"loop_ref":331,"next":1325,"prev":1327,"to":333,"twin":1313,"wire_ref":null},{"edge":666,"face":331,"from":333,"geometry_use":{"pcurve":1325,"periodic_lift":[0,0],"sense":"Forward"},"id":1325,"loop_ref":331,"next":1326,"prev":1324,"to":334,"twin":1330,"wire_ref":null},{"edge":662,"face":331,"from":334,"geometry_use":{"pcurve":1326,"periodic_lift":[0,0],"sense":"Reverse"},"id":1326,"loop_ref":331,"next":1327,"prev":1325,"to":331,"twin":1317,"wire_ref":null},{"edge":667,"face":331,"from":331,"geometry_use":{"pcurve":1327,"periodic_lift":[0,0],"sense":"Reverse"},"id":1327,"loop_ref":331,"next":1324,"prev":1326,"to":328,"twin":1344,"wire_ref":null},{"edge":665,"face":332,"from":332,"geometry_use":{"pcurve":1328,"periodic_lift":[0,0],"sense":"Forward"},"id":1328,"loop_ref":332,"next":1329,"prev":1331,"to":335,"twin":1321,"wire_ref":null},{"edge":663,"face":332,"from":335,"geometry_use":{"pcurve":1329,"periodic_lift":[0,0],"sense":"Forward"},"id":1329,"loop_ref":332,"next":1330,"prev":1328,"to":334,"twin":1318,"wire_ref":null},{"edge":666,"face":332,"from":334,"geometry_use":{"pcurve":1330,"periodic_lift":[0,0],"sense":"Reverse"},"id":1330,"loop_ref":332,"next":1331,"prev":1329,"to":333,"twin":1325,"wire_ref":null},{"edge":659,"face":332,"from":333,"geometry_use":{"pcurve":1331,"periodic_lift":[0,0],"sense":"Reverse"},"id":1331,"loop_ref":332,"next":1328,"prev":1330,"to":332,"twin":1312,"wire_ref":null},{"edge":668,"face":333,"from":328,"geometry_use":{"pcurve":1332,"periodic_lift":[0,0],"sense":"Forward"},"id":1332,"loop_ref":333,"next":1333,"prev":1335,"to":336,"twin":1347,"wire_ref":null},{"edge":669,"face":333,"from":336,"geometry_use":{"pcurve":1333,"periodic_lift":[0,0],"sense":"Reverse"},"id":1333,"loop_ref":333,"next":1334,"prev":1332,"to":337,"twin":1351,"wire_ref":null},{"edge":670,"face":333,"from":337,"geometry_use":{"pcurve":1334,"periodic_lift":[0,0],"sense":"Reverse"},"id":1334,"loop_ref":333,"next":1335,"prev":1333,"to":329,"twin":1343,"wire_ref":null},{"edge":653,"face":333,"from":329,"geometry_use":{"pcurve":1335,"periodic_lift":[0,0],"sense":"Forward"},"id":1335,"loop_ref":333,"next":1332,"prev":1334,"to":328,"twin":1301,"wire_ref":null},{"edge":671,"face":334,"from":330,"geometry_use":{"pcurve":1336,"periodic_lift":[0,0],"sense":"Forward"},"id":1336,"loop_ref":334,"next":1337,"prev":1339,"to":338,"twin":1341,"wire_ref":null},{"edge":672,"face":334,"from":338,"geometry_use":{"pcurve":1337,"periodic_lift":[0,0],"sense":"Forward"},"id":1337,"loop_ref":334,"next":1338,"prev":1336,"to":339,"twin":1355,"wire_ref":null},{"edge":673,"face":334,"from":339,"geometry_use":{"pcurve":1338,"periodic_lift":[0,0],"sense":"Reverse"},"id":1338,"loop_ref":334,"next":1339,"prev":1337,"to":331,"twin":1345,"wire_ref":null},{"edge":656,"face":334,"from":331,"geometry_use":{"pcurve":1339,"periodic_lift":[0,0],"sense":"Reverse"},"id":1339,"loop_ref":334,"next":1336,"prev":1338,"to":330,"twin":1305,"wire_ref":null},{"edge":674,"face":335,"from":337,"geometry_use":{"pcurve":1340,"periodic_lift":[0,0],"sense":"Forward"},"id":1340,"loop_ref":335,"next":1341,"prev":1343,"to":338,"twin":1358,"wire_ref":null},{"edge":671,"face":335,"from":338,"geometry_use":{"pcurve":1341,"periodic_lift":[0,0],"sense":"Reverse"},"id":1341,"loop_ref":335,"next":1342,"prev":1340,"to":330,"twin":1336,"wire_ref":null},{"edge":658,"face":335,"from":330,"geometry_use":{"pcurve":1342,"periodic_lift":[0,0],"sense":"Reverse"},"id":1342,"loop_ref":335,"next":1343,"prev":1341,"to":329,"twin":1308,"wire_ref":null},{"edge":670,"face":335,"from":329,"geometry_use":{"pcurve":1343,"periodic_lift":[0,0],"sense":"Forward"},"id":1343,"loop_ref":335,"next":1340,"prev":1342,"to":337,"twin":1334,"wire_ref":null},{"edge":667,"face":336,"from":328,"geometry_use":{"pcurve":1344,"periodic_lift":[0,0],"sense":"Forward"},"id":1344,"loop_ref":336,"next":1345,"prev":1347,"to":331,"twin":1327,"wire_ref":null},{"edge":673,"face":336,"from":331,"geometry_use":{"pcurve":1345,"periodic_lift":[0,0],"sense":"Forward"},"id":1345,"loop_ref":336,"next":1346,"prev":1344,"to":339,"twin":1338,"wire_ref":null},{"edge":675,"face":336,"from":339,"geometry_use":{"pcurve":1346,"periodic_lift":[0,0],"sense":"Reverse"},"id":1346,"loop_ref":336,"next":1347,"prev":1345,"to":336,"twin":1371,"wire_ref":null},{"edge":668,"face":336,"from":336,"geometry_use":{"pcurve":1347,"periodic_lift":[0,0],"sense":"Reverse"},"id":1347,"loop_ref":336,"next":1344,"prev":1346,"to":328,"twin":1332,"wire_ref":null},{"edge":676,"face":337,"from":336,"geometry_use":{"pcurve":1348,"periodic_lift":[0,0],"sense":"Forward"},"id":1348,"loop_ref":337,"next":1349,"prev":1351,"to":340,"twin":1362,"wire_ref":null},{"edge":677,"face":337,"from":340,"geometry_use":{"pcurve":1349,"periodic_lift":[0,0],"sense":"Reverse"},"id":1349,"loop_ref":337,"next":1350,"prev":1348,"to":341,"twin":1383,"wire_ref":null},{"edge":678,"face":337,"from":341,"geometry_use":{"pcurve":1350,"periodic_lift":[0,0],"sense":"Reverse"},"id":1350,"loop_ref":337,"next":1351,"prev":1349,"to":337,"twin":1359,"wire_ref":null},{"edge":669,"face":337,"from":337,"geometry_use":{"pcurve":1351,"periodic_lift":[0,0],"sense":"Forward"},"id":1351,"loop_ref":337,"next":1348,"prev":1350,"to":336,"twin":1333,"wire_ref":null},{"edge":679,"face":338,"from":338,"geometry_use":{"pcurve":1352,"periodic_lift":[0,0],"sense":"Forward"},"id":1352,"loop_ref":338,"next":1353,"prev":1355,"to":342,"twin":1357,"wire_ref":null},{"edge":680,"face":338,"from":342,"geometry_use":{"pcurve":1353,"periodic_lift":[0,0],"sense":"Forward"},"id":1353,"loop_ref":338,"next":1354,"prev":1352,"to":343,"twin":1387,"wire_ref":null},{"edge":681,"face":338,"from":343,"geometry_use":{"pcurve":1354,"periodic_lift":[0,0],"sense":"Reverse"},"id":1354,"loop_ref":338,"next":1355,"prev":1353,"to":339,"twin":1364,"wire_ref":null},{"edge":672,"face":338,"from":339,"geometry_use":{"pcurve":1355,"periodic_lift":[0,0],"sense":"Reverse"},"id":1355,"loop_ref":338,"next":1352,"prev":1354,"to":338,"twin":1337,"wire_ref":null},{"edge":682,"face":339,"from":341,"geometry_use":{"pcurve":1356,"periodic_lift":[0,0],"sense":"Forward"},"id":1356,"loop_ref":339,"next":1357,"prev":1359,"to":342,"twin":1390,"wire_ref":null},{"edge":679,"face":339,"from":342,"geometry_use":{"pcurve":1357,"periodic_lift":[0,0],"sense":"Reverse"},"id":1357,"loop_ref":339,"next":1358,"prev":1356,"to":338,"twin":1352,"wire_ref":null},{"edge":674,"face":339,"from":338,"geometry_use":{"pcurve":1358,"periodic_lift":[0,0],"sense":"Reverse"},"id":1358,"loop_ref":339,"next":1359,"prev":1357,"to":337,"twin":1340,"wire_ref":null},{"edge":678,"face":339,"from":337,"geometry_use":{"pcurve":1359,"periodic_lift":[0,0],"sense":"Forward"},"id":1359,"loop_ref":339,"next":1356,"prev":1358,"to":341,"twin":1350,"wire_ref":null},{"edge":683,"face":340,"from":344,"geometry_use":{"pcurve":1360,"periodic_lift":[0,0],"sense":"Forward"},"id":1360,"loop_ref":340,"next":1361,"prev":1363,"to":345,"twin":1379,"wire_ref":null},{"edge":684,"face":340,"from":345,"geometry_use":{"pcurve":1361,"periodic_lift":[0,0],"sense":"Reverse"},"id":1361,"loop_ref":340,"next":1362,"prev":1360,"to":340,"twin":1372,"wire_ref":null},{"edge":676,"face":340,"from":340,"geometry_use":{"pcurve":1362,"periodic_lift":[0,0],"sense":"Reverse"},"id":1362,"loop_ref":340,"next":1363,"prev":1361,"to":336,"twin":1348,"wire_ref":null},{"edge":685,"face":340,"from":336,"geometry_use":{"pcurve":1363,"periodic_lift":[0,0],"sense":"Forward"},"id":1363,"loop_ref":340,"next":1360,"prev":1362,"to":344,"twin":1370,"wire_ref":null},{"edge":681,"face":341,"from":339,"geometry_use":{"pcurve":1364,"periodic_lift":[0,0],"sense":"Forward"},"id":1364,"loop_ref":341,"next":1365,"prev":1367,"to":343,"twin":1354,"wire_ref":null},{"edge":686,"face":341,"from":343,"geometry_use":{"pcurve":1365,"periodic_lift":[0,0],"sense":"Forward"},"id":1365,"loop_ref":341,"next":1366,"prev":1364,"to":346,"twin":1374,"wire_ref":null},{"edge":687,"face":341,"from":346,"geometry_use":{"pcurve":1366,"periodic_lift":[0,0],"sense":"Reverse"},"id":1366,"loop_ref":341,"next":1367,"prev":1365,"to":347,"twin":1377,"wire_ref":null},{"edge":688,"face":341,"from":347,"geometry_use":{"pcurve":1367,"periodic_lift":[0,0],"sense":"Reverse"},"id":1367,"loop_ref":341,"next":1364,"prev":1366,"to":339,"twin":1368,"wire_ref":null},{"edge":688,"face":342,"from":339,"geometry_use":{"pcurve":1368,"periodic_lift":[0,0],"sense":"Forward"},"id":1368,"loop_ref":342,"next":1369,"prev":1371,"to":347,"twin":1367,"wire_ref":null},{"edge":689,"face":342,"from":347,"geometry_use":{"pcurve":1369,"periodic_lift":[0,0],"sense":"Reverse"},"id":1369,"loop_ref":342,"next":1370,"prev":1368,"to":344,"twin":1376,"wire_ref":null},{"edge":685,"face":342,"from":344,"geometry_use":{"pcurve":1370,"periodic_lift":[0,0],"sense":"Reverse"},"id":1370,"loop_ref":342,"next":1371,"prev":1369,"to":336,"twin":1363,"wire_ref":null},{"edge":675,"face":342,"from":336,"geometry_use":{"pcurve":1371,"periodic_lift":[0,0],"sense":"Forward"},"id":1371,"loop_ref":342,"next":1368,"prev":1370,"to":339,"twin":1346,"wire_ref":null},{"edge":684,"face":343,"from":340,"geometry_use":{"pcurve":1372,"periodic_lift":[0,0],"sense":"Forward"},"id":1372,"loop_ref":343,"next":1373,"prev":1375,"to":345,"twin":1361,"wire_ref":null},{"edge":690,"face":343,"from":345,"geometry_use":{"pcurve":1373,"periodic_lift":[0,0],"sense":"Forward"},"id":1373,"loop_ref":343,"next":1374,"prev":1372,"to":346,"twin":1378,"wire_ref":null},{"edge":686,"face":343,"from":346,"geometry_use":{"pcurve":1374,"periodic_lift":[0,0],"sense":"Reverse"},"id":1374,"loop_ref":343,"next":1375,"prev":1373,"to":343,"twin":1365,"wire_ref":null},{"edge":691,"face":343,"from":343,"geometry_use":{"pcurve":1375,"periodic_lift":[0,0],"sense":"Reverse"},"id":1375,"loop_ref":343,"next":1372,"prev":1374,"to":340,"twin":1392,"wire_ref":null},{"edge":689,"face":344,"from":344,"geometry_use":{"pcurve":1376,"periodic_lift":[0,0],"sense":"Forward"},"id":1376,"loop_ref":344,"next":1377,"prev":1379,"to":347,"twin":1369,"wire_ref":null},{"edge":687,"face":344,"from":347,"geometry_use":{"pcurve":1377,"periodic_lift":[0,0],"sense":"Forward"},"id":1377,"loop_ref":344,"next":1378,"prev":1376,"to":346,"twin":1366,"wire_ref":null},{"edge":690,"face":344,"from":346,"geometry_use":{"pcurve":1378,"periodic_lift":[0,0],"sense":"Reverse"},"id":1378,"loop_ref":344,"next":1379,"prev":1377,"to":345,"twin":1373,"wire_ref":null},{"edge":683,"face":344,"from":345,"geometry_use":{"pcurve":1379,"periodic_lift":[0,0],"sense":"Reverse"},"id":1379,"loop_ref":344,"next":1376,"prev":1378,"to":344,"twin":1360,"wire_ref":null},{"edge":692,"face":345,"from":340,"geometry_use":{"pcurve":1380,"periodic_lift":[0,0],"sense":"Forward"},"id":1380,"loop_ref":345,"next":1381,"prev":1383,"to":348,"twin":1395,"wire_ref":null},{"edge":693,"face":345,"from":348,"geometry_use":{"pcurve":1381,"periodic_lift":[0,0],"sense":"Reverse"},"id":1381,"loop_ref":345,"next":1382,"prev":1380,"to":349,"twin":1399,"wire_ref":null},{"edge":694,"face":345,"from":349,"geometry_use":{"pcurve":1382,"periodic_lift":[0,0],"sense":"Reverse"},"id":1382,"loop_ref":345,"next":1383,"prev":1381,"to":341,"twin":1391,"wire_ref":null},{"edge":677,"face":345,"from":341,"geometry_use":{"pcurve":1383,"periodic_lift":[0,0],"sense":"Forward"},"id":1383,"loop_ref":345,"next":1380,"prev":1382,"to":340,"twin":1349,"wire_ref":null},{"edge":695,"face":346,"from":342,"geometry_use":{"pcurve":1384,"periodic_lift":[0,0],"sense":"Forward"},"id":1384,"loop_ref":346,"next":1385,"prev":1387,"to":350,"twin":1389,"wire_ref":null},{"edge":696,"face":346,"from":350,"geometry_use":{"pcurve":1385,"periodic_lift":[0,0],"sense":"Forward"},"id":1385,"loop_ref":346,"next":1386,"prev":1384,"to":351,"twin":1403,"wire_ref":null},{"edge":697,"face":346,"from":351,"geometry_use":{"pcurve":1386,"periodic_lift":[0,0],"sense":"Reverse"},"id":1386,"loop_ref":346,"next":1387,"prev":1385,"to":343,"twin":1393,"wire_ref":null},{"edge":680,"face":346,"from":343,"geometry_use":{"pcurve":1387,"periodic_lift":[0,0],"sense":"Reverse"},"id":1387,"loop_ref":346,"next":1384,"prev":1386,"to":342,"twin":1353,"wire_ref":null},{"edge":698,"face":347,"from":349,"geometry_use":{"pcurve":1388,"periodic_lift":[0,0],"sense":"Forward"},"id":1388,"loop_ref":347,"next":1389,"prev":1391,"to":350,"twin":1406,"wire_ref":null},{"edge":695,"face":347,"from":350,"geometry_use":{"pcurve":1389,"periodic_lift":[0,0],"sense":"Reverse"},"id":1389,"loop_ref":347,"next":1390,"prev":1388,"to":342,"twin":1384,"wire_ref":null},{"edge":682,"face":347,"from":342,"geometry_use":{"pcurve":1390,"periodic_lift":[0,0],"sense":"Reverse"},"id":1390,"loop_ref":347,"next":1391,"prev":1389,"to":341,"twin":1356,"wire_ref":null},{"edge":694,"face":347,"from":341,"geometry_use":{"pcurve":1391,"periodic_lift":[0,0],"sense":"Forward"},"id":1391,"loop_ref":347,"next":1388,"prev":1390,"to":349,"twin":1382,"wire_ref":null},{"edge":691,"face":348,"from":340,"geometry_use":{"pcurve":1392,"periodic_lift":[0,0],"sense":"Forward"},"id":1392,"loop_ref":348,"next":1393,"prev":1395,"to":343,"twin":1375,"wire_ref":null},{"edge":697,"face":348,"from":343,"geometry_use":{"pcurve":1393,"periodic_lift":[0,0],"sense":"Forward"},"id":1393,"loop_ref":348,"next":1394,"prev":1392,"to":351,"twin":1386,"wire_ref":null},{"edge":699,"face":348,"from":351,"geometry_use":{"pcurve":1394,"periodic_lift":[0,0],"sense":"Reverse"},"id":1394,"loop_ref":348,"next":1395,"prev":1393,"to":348,"twin":1419,"wire_ref":null},{"edge":692,"face":348,"from":348,"geometry_use":{"pcurve":1395,"periodic_lift":[0,0],"sense":"Reverse"},"id":1395,"loop_ref":348,"next":1392,"prev":1394,"to":340,"twin":1380,"wire_ref":null},{"edge":700,"face":349,"from":348,"geometry_use":{"pcurve":1396,"periodic_lift":[0,0],"sense":"Forward"},"id":1396,"loop_ref":349,"next":1397,"prev":1399,"to":352,"twin":1410,"wire_ref":null},{"edge":701,"face":349,"from":352,"geometry_use":{"pcurve":1397,"periodic_lift":[0,0],"sense":"Reverse"},"id":1397,"loop_ref":349,"next":1398,"prev":1396,"to":353,"twin":1431,"wire_ref":null},{"edge":702,"face":349,"from":353,"geometry_use":{"pcurve":1398,"periodic_lift":[0,0],"sense":"Reverse"},"id":1398,"loop_ref":349,"next":1399,"prev":1397,"to":349,"twin":1407,"wire_ref":null},{"edge":693,"face":349,"from":349,"geometry_use":{"pcurve":1399,"periodic_lift":[0,0],"sense":"Forward"},"id":1399,"loop_ref":349,"next":1396,"prev":1398,"to":348,"twin":1381,"wire_ref":null},{"edge":703,"face":350,"from":350,"geometry_use":{"pcurve":1400,"periodic_lift":[0,0],"sense":"Forward"},"id":1400,"loop_ref":350,"next":1401,"prev":1403,"to":354,"twin":1405,"wire_ref":null},{"edge":704,"face":350,"from":354,"geometry_use":{"pcurve":1401,"periodic_lift":[0,0],"sense":"Forward"},"id":1401,"loop_ref":350,"next":1402,"prev":1400,"to":355,"twin":1435,"wire_ref":null},{"edge":705,"face":350,"from":355,"geometry_use":{"pcurve":1402,"periodic_lift":[0,0],"sense":"Reverse"},"id":1402,"loop_ref":350,"next":1403,"prev":1401,"to":351,"twin":1412,"wire_ref":null},{"edge":696,"face":350,"from":351,"geometry_use":{"pcurve":1403,"periodic_lift":[0,0],"sense":"Reverse"},"id":1403,"loop_ref":350,"next":1400,"prev":1402,"to":350,"twin":1385,"wire_ref":null},{"edge":706,"face":351,"from":353,"geometry_use":{"pcurve":1404,"periodic_lift":[0,0],"sense":"Forward"},"id":1404,"loop_ref":351,"next":1405,"prev":1407,"to":354,"twin":1438,"wire_ref":null},{"edge":703,"face":351,"from":354,"geometry_use":{"pcurve":1405,"periodic_lift":[0,0],"sense":"Reverse"},"id":1405,"loop_ref":351,"next":1406,"prev":1404,"to":350,"twin":1400,"wire_ref":null},{"edge":698,"face":351,"from":350,"geometry_use":{"pcurve":1406,"periodic_lift":[0,0],"sense":"Reverse"},"id":1406,"loop_ref":351,"next":1407,"prev":1405,"to":349,"twin":1388,"wire_ref":null},{"edge":702,"face":351,"from":349,"geometry_use":{"pcurve":1407,"periodic_lift":[0,0],"sense":"Forward"},"id":1407,"loop_ref":351,"next":1404,"prev":1406,"to":353,"twin":1398,"wire_ref":null},{"edge":707,"face":352,"from":356,"geometry_use":{"pcurve":1408,"periodic_lift":[0,0],"sense":"Forward"},"id":1408,"loop_ref":352,"next":1409,"prev":1411,"to":357,"twin":1427,"wire_ref":null},{"edge":708,"face":352,"from":357,"geometry_use":{"pcurve":1409,"periodic_lift":[0,0],"sense":"Reverse"},"id":1409,"loop_ref":352,"next":1410,"prev":1408,"to":352,"twin":1420,"wire_ref":null},{"edge":700,"face":352,"from":352,"geometry_use":{"pcurve":1410,"periodic_lift":[0,0],"sense":"Reverse"},"id":1410,"loop_ref":352,"next":1411,"prev":1409,"to":348,"twin":1396,"wire_ref":null},{"edge":709,"face":352,"from":348,"geometry_use":{"pcurve":1411,"periodic_lift":[0,0],"sense":"Forward"},"id":1411,"loop_ref":352,"next":1408,"prev":1410,"to":356,"twin":1418,"wire_ref":null},{"edge":705,"face":353,"from":351,"geometry_use":{"pcurve":1412,"periodic_lift":[0,0],"sense":"Forward"},"id":1412,"loop_ref":353,"next":1413,"prev":1415,"to":355,"twin":1402,"wire_ref":null},{"edge":710,"face":353,"from":355,"geometry_use":{"pcurve":1413,"periodic_lift":[0,0],"sense":"Forward"},"id":1413,"loop_ref":353,"next":1414,"prev":1412,"to":358,"twin":1422,"wire_ref":null},{"edge":711,"face":353,"from":358,"geometry_use":{"pcurve":1414,"periodic_lift":[0,0],"sense":"Reverse"},"id":1414,"loop_ref":353,"next":1415,"prev":1413,"to":359,"twin":1425,"wire_ref":null},{"edge":712,"face":353,"from":359,"geometry_use":{"pcurve":1415,"periodic_lift":[0,0],"sense":"Reverse"},"id":1415,"loop_ref":353,"next":1412,"prev":1414,"to":351,"twin":1416,"wire_ref":null},{"edge":712,"face":354,"from":351,"geometry_use":{"pcurve":1416,"periodic_lift":[0,0],"sense":"Forward"},"id":1416,"loop_ref":354,"next":1417,"prev":1419,"to":359,"twin":1415,"wire_ref":null},{"edge":713,"face":354,"from":359,"geometry_use":{"pcurve":1417,"periodic_lift":[0,0],"sense":"Reverse"},"id":1417,"loop_ref":354,"next":1418,"prev":1416,"to":356,"twin":1424,"wire_ref":null},{"edge":709,"face":354,"from":356,"geometry_use":{"pcurve":1418,"periodic_lift":[0,0],"sense":"Reverse"},"id":1418,"loop_ref":354,"next":1419,"prev":1417,"to":348,"twin":1411,"wire_ref":null},{"edge":699,"face":354,"from":348,"geometry_use":{"pcurve":1419,"periodic_lift":[0,0],"sense":"Forward"},"id":1419,"loop_ref":354,"next":1416,"prev":1418,"to":351,"twin":1394,"wire_ref":null},{"edge":708,"face":355,"from":352,"geometry_use":{"pcurve":1420,"periodic_lift":[0,0],"sense":"Forward"},"id":1420,"loop_ref":355,"next":1421,"prev":1423,"to":357,"twin":1409,"wire_ref":null},{"edge":714,"face":355,"from":357,"geometry_use":{"pcurve":1421,"periodic_lift":[0,0],"sense":"Forward"},"id":1421,"loop_ref":355,"next":1422,"prev":1420,"to":358,"twin":1426,"wire_ref":null},{"edge":710,"face":355,"from":358,"geometry_use":{"pcurve":1422,"periodic_lift":[0,0],"sense":"Reverse"},"id":1422,"loop_ref":355,"next":1423,"prev":1421,"to":355,"twin":1413,"wire_ref":null},{"edge":715,"face":355,"from":355,"geometry_use":{"pcurve":1423,"periodic_lift":[0,0],"sense":"Reverse"},"id":1423,"loop_ref":355,"next":1420,"prev":1422,"to":352,"twin":1440,"wire_ref":null},{"edge":713,"face":356,"from":356,"geometry_use":{"pcurve":1424,"periodic_lift":[0,0],"sense":"Forward"},"id":1424,"loop_ref":356,"next":1425,"prev":1427,"to":359,"twin":1417,"wire_ref":null},{"edge":711,"face":356,"from":359,"geometry_use":{"pcurve":1425,"periodic_lift":[0,0],"sense":"Forward"},"id":1425,"loop_ref":356,"next":1426,"prev":1424,"to":358,"twin":1414,"wire_ref":null},{"edge":714,"face":356,"from":358,"geometry_use":{"pcurve":1426,"periodic_lift":[0,0],"sense":"Reverse"},"id":1426,"loop_ref":356,"next":1427,"prev":1425,"to":357,"twin":1421,"wire_ref":null},{"edge":707,"face":356,"from":357,"geometry_use":{"pcurve":1427,"periodic_lift":[0,0],"sense":"Reverse"},"id":1427,"loop_ref":356,"next":1424,"prev":1426,"to":356,"twin":1408,"wire_ref":null},{"edge":716,"face":357,"from":352,"geometry_use":{"pcurve":1428,"periodic_lift":[0,0],"sense":"Forward"},"id":1428,"loop_ref":357,"next":1429,"prev":1431,"to":360,"twin":1443,"wire_ref":null},{"edge":717,"face":357,"from":360,"geometry_use":{"pcurve":1429,"periodic_lift":[0,0],"sense":"Reverse"},"id":1429,"loop_ref":357,"next":1430,"prev":1428,"to":361,"twin":1447,"wire_ref":null},{"edge":718,"face":357,"from":361,"geometry_use":{"pcurve":1430,"periodic_lift":[0,0],"sense":"Reverse"},"id":1430,"loop_ref":357,"next":1431,"prev":1429,"to":353,"twin":1439,"wire_ref":null},{"edge":701,"face":357,"from":353,"geometry_use":{"pcurve":1431,"periodic_lift":[0,0],"sense":"Forward"},"id":1431,"loop_ref":357,"next":1428,"prev":1430,"to":352,"twin":1397,"wire_ref":null},{"edge":719,"face":358,"from":354,"geometry_use":{"pcurve":1432,"periodic_lift":[0,0],"sense":"Forward"},"id":1432,"loop_ref":358,"next":1433,"prev":1435,"to":362,"twin":1437,"wire_ref":null},{"edge":720,"face":358,"from":362,"geometry_use":{"pcurve":1433,"periodic_lift":[0,0],"sense":"Forward"},"id":1433,"loop_ref":358,"next":1434,"prev":1432,"to":363,"twin":1451,"wire_ref":null},{"edge":721,"face":358,"from":363,"geometry_use":{"pcurve":1434,"periodic_lift":[0,0],"sense":"Reverse"},"id":1434,"loop_ref":358,"next":1435,"prev":1433,"to":355,"twin":1441,"wire_ref":null},{"edge":704,"face":358,"from":355,"geometry_use":{"pcurve":1435,"periodic_lift":[0,0],"sense":"Reverse"},"id":1435,"loop_ref":358,"next":1432,"prev":1434,"to":354,"twin":1401,"wire_ref":null},{"edge":722,"face":359,"from":361,"geometry_use":{"pcurve":1436,"periodic_lift":[0,0],"sense":"Forward"},"id":1436,"loop_ref":359,"next":1437,"prev":1439,"to":362,"twin":1454,"wire_ref":null},{"edge":719,"face":359,"from":362,"geometry_use":{"pcurve":1437,"periodic_lift":[0,0],"sense":"Reverse"},"id":1437,"loop_ref":359,"next":1438,"prev":1436,"to":354,"twin":1432,"wire_ref":null},{"edge":706,"face":359,"from":354,"geometry_use":{"pcurve":1438,"periodic_lift":[0,0],"sense":"Reverse"},"id":1438,"loop_ref":359,"next":1439,"prev":1437,"to":353,"twin":1404,"wire_ref":null},{"edge":718,"face":359,"from":353,"geometry_use":{"pcurve":1439,"periodic_lift":[0,0],"sense":"Forward"},"id":1439,"loop_ref":359,"next":1436,"prev":1438,"to":361,"twin":1430,"wire_ref":null},{"edge":715,"face":360,"from":352,"geometry_use":{"pcurve":1440,"periodic_lift":[0,0],"sense":"Forward"},"id":1440,"loop_ref":360,"next":1441,"prev":1443,"to":355,"twin":1423,"wire_ref":null},{"edge":721,"face":360,"from":355,"geometry_use":{"pcurve":1441,"periodic_lift":[0,0],"sense":"Forward"},"id":1441,"loop_ref":360,"next":1442,"prev":1440,"to":363,"twin":1434,"wire_ref":null},{"edge":723,"face":360,"from":363,"geometry_use":{"pcurve":1442,"periodic_lift":[0,0],"sense":"Reverse"},"id":1442,"loop_ref":360,"next":1443,"prev":1441,"to":360,"twin":1467,"wire_ref":null},{"edge":716,"face":360,"from":360,"geometry_use":{"pcurve":1443,"periodic_lift":[0,0],"sense":"Reverse"},"id":1443,"loop_ref":360,"next":1440,"prev":1442,"to":352,"twin":1428,"wire_ref":null},{"edge":724,"face":361,"from":360,"geometry_use":{"pcurve":1444,"periodic_lift":[0,0],"sense":"Forward"},"id":1444,"loop_ref":361,"next":1445,"prev":1447,"to":364,"twin":1458,"wire_ref":null},{"edge":725,"face":361,"from":364,"geometry_use":{"pcurve":1445,"periodic_lift":[0,0],"sense":"Reverse"},"id":1445,"loop_ref":361,"next":1446,"prev":1444,"to":365,"twin":1479,"wire_ref":null},{"edge":726,"face":361,"from":365,"geometry_use":{"pcurve":1446,"periodic_lift":[0,0],"sense":"Reverse"},"id":1446,"loop_ref":361,"next":1447,"prev":1445,"to":361,"twin":1455,"wire_ref":null},{"edge":717,"face":361,"from":361,"geometry_use":{"pcurve":1447,"periodic_lift":[0,0],"sense":"Forward"},"id":1447,"loop_ref":361,"next":1444,"prev":1446,"to":360,"twin":1429,"wire_ref":null},{"edge":727,"face":362,"from":362,"geometry_use":{"pcurve":1448,"periodic_lift":[0,0],"sense":"Forward"},"id":1448,"loop_ref":362,"next":1449,"prev":1451,"to":366,"twin":1453,"wire_ref":null},{"edge":728,"face":362,"from":366,"geometry_use":{"pcurve":1449,"periodic_lift":[0,0],"sense":"Forward"},"id":1449,"loop_ref":362,"next":1450,"prev":1448,"to":367,"twin":1483,"wire_ref":null},{"edge":729,"face":362,"from":367,"geometry_use":{"pcurve":1450,"periodic_lift":[0,0],"sense":"Reverse"},"id":1450,"loop_ref":362,"next":1451,"prev":1449,"to":363,"twin":1460,"wire_ref":null},{"edge":720,"face":362,"from":363,"geometry_use":{"pcurve":1451,"periodic_lift":[0,0],"sense":"Reverse"},"id":1451,"loop_ref":362,"next":1448,"prev":1450,"to":362,"twin":1433,"wire_ref":null},{"edge":730,"face":363,"from":365,"geometry_use":{"pcurve":1452,"periodic_lift":[0,0],"sense":"Forward"},"id":1452,"loop_ref":363,"next":1453,"prev":1455,"to":366,"twin":1486,"wire_ref":null},{"edge":727,"face":363,"from":366,"geometry_use":{"pcurve":1453,"periodic_lift":[0,0],"sense":"Reverse"},"id":1453,"loop_ref":363,"next":1454,"prev":1452,"to":362,"twin":1448,"wire_ref":null},{"edge":722,"face":363,"from":362,"geometry_use":{"pcurve":1454,"periodic_lift":[0,0],"sense":"Reverse"},"id":1454,"loop_ref":363,"next":1455,"prev":1453,"to":361,"twin":1436,"wire_ref":null},{"edge":726,"face":363,"from":361,"geometry_use":{"pcurve":1455,"periodic_lift":[0,0],"sense":"Forward"},"id":1455,"loop_ref":363,"next":1452,"prev":1454,"to":365,"twin":1446,"wire_ref":null},{"edge":731,"face":364,"from":368,"geometry_use":{"pcurve":1456,"periodic_lift":[0,0],"sense":"Forward"},"id":1456,"loop_ref":364,"next":1457,"prev":1459,"to":369,"twin":1475,"wire_ref":null},{"edge":732,"face":364,"from":369,"geometry_use":{"pcurve":1457,"periodic_lift":[0,0],"sense":"Reverse"},"id":1457,"loop_ref":364,"next":1458,"prev":1456,"to":364,"twin":1468,"wire_ref":null},{"edge":724,"face":364,"from":364,"geometry_use":{"pcurve":1458,"periodic_lift":[0,0],"sense":"Reverse"},"id":1458,"loop_ref":364,"next":1459,"prev":1457,"to":360,"twin":1444,"wire_ref":null},{"edge":733,"face":364,"from":360,"geometry_use":{"pcurve":1459,"periodic_lift":[0,0],"sense":"Forward"},"id":1459,"loop_ref":364,"next":1456,"prev":1458,"to":368,"twin":1466,"wire_ref":null},{"edge":729,"face":365,"from":363,"geometry_use":{"pcurve":1460,"periodic_lift":[0,0],"sense":"Forward"},"id":1460,"loop_ref":365,"next":1461,"prev":1463,"to":367,"twin":1450,"wire_ref":null},{"edge":734,"face":365,"from":367,"geometry_use":{"pcurve":1461,"periodic_lift":[0,0],"sense":"Forward"},"id":1461,"loop_ref":365,"next":1462,"prev":1460,"to":370,"twin":1470,"wire_ref":null},{"edge":735,"face":365,"from":370,"geometry_use":{"pcurve":1462,"periodic_lift":[0,0],"sense":"Reverse"},"id":1462,"loop_ref":365,"next":1463,"prev":1461,"to":371,"twin":1473,"wire_ref":null},{"edge":736,"face":365,"from":371,"geometry_use":{"pcurve":1463,"periodic_lift":[0,0],"sense":"Reverse"},"id":1463,"loop_ref":365,"next":1460,"prev":1462,"to":363,"twin":1464,"wire_ref":null},{"edge":736,"face":366,"from":363,"geometry_use":{"pcurve":1464,"periodic_lift":[0,0],"sense":"Forward"},"id":1464,"loop_ref":366,"next":1465,"prev":1467,"to":371,"twin":1463,"wire_ref":null},{"edge":737,"face":366,"from":371,"geometry_use":{"pcurve":1465,"periodic_lift":[0,0],"sense":"Reverse"},"id":1465,"loop_ref":366,"next":1466,"prev":1464,"to":368,"twin":1472,"wire_ref":null},{"edge":733,"face":366,"from":368,"geometry_use":{"pcurve":1466,"periodic_lift":[0,0],"sense":"Reverse"},"id":1466,"loop_ref":366,"next":1467,"prev":1465,"to":360,"twin":1459,"wire_ref":null},{"edge":723,"face":366,"from":360,"geometry_use":{"pcurve":1467,"periodic_lift":[0,0],"sense":"Forward"},"id":1467,"loop_ref":366,"next":1464,"prev":1466,"to":363,"twin":1442,"wire_ref":null},{"edge":732,"face":367,"from":364,"geometry_use":{"pcurve":1468,"periodic_lift":[0,0],"sense":"Forward"},"id":1468,"loop_ref":367,"next":1469,"prev":1471,"to":369,"twin":1457,"wire_ref":null},{"edge":738,"face":367,"from":369,"geometry_use":{"pcurve":1469,"periodic_lift":[0,0],"sense":"Forward"},"id":1469,"loop_ref":367,"next":1470,"prev":1468,"to":370,"twin":1474,"wire_ref":null},{"edge":734,"face":367,"from":370,"geometry_use":{"pcurve":1470,"periodic_lift":[0,0],"sense":"Reverse"},"id":1470,"loop_ref":367,"next":1471,"prev":1469,"to":367,"twin":1461,"wire_ref":null},{"edge":739,"face":367,"from":367,"geometry_use":{"pcurve":1471,"periodic_lift":[0,0],"sense":"Reverse"},"id":1471,"loop_ref":367,"next":1468,"prev":1470,"to":364,"twin":1488,"wire_ref":null},{"edge":737,"face":368,"from":368,"geometry_use":{"pcurve":1472,"periodic_lift":[0,0],"sense":"Forward"},"id":1472,"loop_ref":368,"next":1473,"prev":1475,"to":371,"twin":1465,"wire_ref":null},{"edge":735,"face":368,"from":371,"geometry_use":{"pcurve":1473,"periodic_lift":[0,0],"sense":"Forward"},"id":1473,"loop_ref":368,"next":1474,"prev":1472,"to":370,"twin":1462,"wire_ref":null},{"edge":738,"face":368,"from":370,"geometry_use":{"pcurve":1474,"periodic_lift":[0,0],"sense":"Reverse"},"id":1474,"loop_ref":368,"next":1475,"prev":1473,"to":369,"twin":1469,"wire_ref":null},{"edge":731,"face":368,"from":369,"geometry_use":{"pcurve":1475,"periodic_lift":[0,0],"sense":"Reverse"},"id":1475,"loop_ref":368,"next":1472,"prev":1474,"to":368,"twin":1456,"wire_ref":null},{"edge":740,"face":369,"from":364,"geometry_use":{"pcurve":1476,"periodic_lift":[0,0],"sense":"Forward"},"id":1476,"loop_ref":369,"next":1477,"prev":1479,"to":372,"twin":1491,"wire_ref":null},{"edge":741,"face":369,"from":372,"geometry_use":{"pcurve":1477,"periodic_lift":[0,0],"sense":"Reverse"},"id":1477,"loop_ref":369,"next":1478,"prev":1476,"to":373,"twin":1495,"wire_ref":null},{"edge":742,"face":369,"from":373,"geometry_use":{"pcurve":1478,"periodic_lift":[0,0],"sense":"Reverse"},"id":1478,"loop_ref":369,"next":1479,"prev":1477,"to":365,"twin":1487,"wire_ref":null},{"edge":725,"face":369,"from":365,"geometry_use":{"pcurve":1479,"periodic_lift":[0,0],"sense":"Forward"},"id":1479,"loop_ref":369,"next":1476,"prev":1478,"to":364,"twin":1445,"wire_ref":null},{"edge":743,"face":370,"from":366,"geometry_use":{"pcurve":1480,"periodic_lift":[0,0],"sense":"Forward"},"id":1480,"loop_ref":370,"next":1481,"prev":1483,"to":374,"twin":1485,"wire_ref":null},{"edge":744,"face":370,"from":374,"geometry_use":{"pcurve":1481,"periodic_lift":[0,0],"sense":"Forward"},"id":1481,"loop_ref":370,"next":1482,"prev":1480,"to":375,"twin":1499,"wire_ref":null},{"edge":745,"face":370,"from":375,"geometry_use":{"pcurve":1482,"periodic_lift":[0,0],"sense":"Reverse"},"id":1482,"loop_ref":370,"next":1483,"prev":1481,"to":367,"twin":1489,"wire_ref":null},{"edge":728,"face":370,"from":367,"geometry_use":{"pcurve":1483,"periodic_lift":[0,0],"sense":"Reverse"},"id":1483,"loop_ref":370,"next":1480,"prev":1482,"to":366,"twin":1449,"wire_ref":null},{"edge":746,"face":371,"from":373,"geometry_use":{"pcurve":1484,"periodic_lift":[0,0],"sense":"Forward"},"id":1484,"loop_ref":371,"next":1485,"prev":1487,"to":374,"twin":1502,"wire_ref":null},{"edge":743,"face":371,"from":374,"geometry_use":{"pcurve":1485,"periodic_lift":[0,0],"sense":"Reverse"},"id":1485,"loop_ref":371,"next":1486,"prev":1484,"to":366,"twin":1480,"wire_ref":null},{"edge":730,"face":371,"from":366,"geometry_use":{"pcurve":1486,"periodic_lift":[0,0],"sense":"Reverse"},"id":1486,"loop_ref":371,"next":1487,"prev":1485,"to":365,"twin":1452,"wire_ref":null},{"edge":742,"face":371,"from":365,"geometry_use":{"pcurve":1487,"periodic_lift":[0,0],"sense":"Forward"},"id":1487,"loop_ref":371,"next":1484,"prev":1486,"to":373,"twin":1478,"wire_ref":null},{"edge":739,"face":372,"from":364,"geometry_use":{"pcurve":1488,"periodic_lift":[0,0],"sense":"Forward"},"id":1488,"loop_ref":372,"next":1489,"prev":1491,"to":367,"twin":1471,"wire_ref":null},{"edge":745,"face":372,"from":367,"geometry_use":{"pcurve":1489,"periodic_lift":[0,0],"sense":"Forward"},"id":1489,"loop_ref":372,"next":1490,"prev":1488,"to":375,"twin":1482,"wire_ref":null},{"edge":747,"face":372,"from":375,"geometry_use":{"pcurve":1490,"periodic_lift":[0,0],"sense":"Reverse"},"id":1490,"loop_ref":372,"next":1491,"prev":1489,"to":372,"twin":1515,"wire_ref":null},{"edge":740,"face":372,"from":372,"geometry_use":{"pcurve":1491,"periodic_lift":[0,0],"sense":"Reverse"},"id":1491,"loop_ref":372,"next":1488,"prev":1490,"to":364,"twin":1476,"wire_ref":null},{"edge":748,"face":373,"from":372,"geometry_use":{"pcurve":1492,"periodic_lift":[0,0],"sense":"Forward"},"id":1492,"loop_ref":373,"next":1493,"prev":1495,"to":376,"twin":1506,"wire_ref":null},{"edge":749,"face":373,"from":376,"geometry_use":{"pcurve":1493,"periodic_lift":[0,0],"sense":"Reverse"},"id":1493,"loop_ref":373,"next":1494,"prev":1492,"to":377,"twin":1527,"wire_ref":null},{"edge":750,"face":373,"from":377,"geometry_use":{"pcurve":1494,"periodic_lift":[0,0],"sense":"Reverse"},"id":1494,"loop_ref":373,"next":1495,"prev":1493,"to":373,"twin":1503,"wire_ref":null},{"edge":741,"face":373,"from":373,"geometry_use":{"pcurve":1495,"periodic_lift":[0,0],"sense":"Forward"},"id":1495,"loop_ref":373,"next":1492,"prev":1494,"to":372,"twin":1477,"wire_ref":null},{"edge":751,"face":374,"from":374,"geometry_use":{"pcurve":1496,"periodic_lift":[0,0],"sense":"Forward"},"id":1496,"loop_ref":374,"next":1497,"prev":1499,"to":378,"twin":1501,"wire_ref":null},{"edge":752,"face":374,"from":378,"geometry_use":{"pcurve":1497,"periodic_lift":[0,0],"sense":"Forward"},"id":1497,"loop_ref":374,"next":1498,"prev":1496,"to":379,"twin":1531,"wire_ref":null},{"edge":753,"face":374,"from":379,"geometry_use":{"pcurve":1498,"periodic_lift":[0,0],"sense":"Reverse"},"id":1498,"loop_ref":374,"next":1499,"prev":1497,"to":375,"twin":1508,"wire_ref":null},{"edge":744,"face":374,"from":375,"geometry_use":{"pcurve":1499,"periodic_lift":[0,0],"sense":"Reverse"},"id":1499,"loop_ref":374,"next":1496,"prev":1498,"to":374,"twin":1481,"wire_ref":null},{"edge":754,"face":375,"from":377,"geometry_use":{"pcurve":1500,"periodic_lift":[0,0],"sense":"Forward"},"id":1500,"loop_ref":375,"next":1501,"prev":1503,"to":378,"twin":1534,"wire_ref":null},{"edge":751,"face":375,"from":378,"geometry_use":{"pcurve":1501,"periodic_lift":[0,0],"sense":"Reverse"},"id":1501,"loop_ref":375,"next":1502,"prev":1500,"to":374,"twin":1496,"wire_ref":null},{"edge":746,"face":375,"from":374,"geometry_use":{"pcurve":1502,"periodic_lift":[0,0],"sense":"Reverse"},"id":1502,"loop_ref":375,"next":1503,"prev":1501,"to":373,"twin":1484,"wire_ref":null},{"edge":750,"face":375,"from":373,"geometry_use":{"pcurve":1503,"periodic_lift":[0,0],"sense":"Forward"},"id":1503,"loop_ref":375,"next":1500,"prev":1502,"to":377,"twin":1494,"wire_ref":null},{"edge":755,"face":376,"from":380,"geometry_use":{"pcurve":1504,"periodic_lift":[0,0],"sense":"Forward"},"id":1504,"loop_ref":376,"next":1505,"prev":1507,"to":381,"twin":1523,"wire_ref":null},{"edge":756,"face":376,"from":381,"geometry_use":{"pcurve":1505,"periodic_lift":[0,0],"sense":"Reverse"},"id":1505,"loop_ref":376,"next":1506,"prev":1504,"to":376,"twin":1516,"wire_ref":null},{"edge":748,"face":376,"from":376,"geometry_use":{"pcurve":1506,"periodic_lift":[0,0],"sense":"Reverse"},"id":1506,"loop_ref":376,"next":1507,"prev":1505,"to":372,"twin":1492,"wire_ref":null},{"edge":757,"face":376,"from":372,"geometry_use":{"pcurve":1507,"periodic_lift":[0,0],"sense":"Forward"},"id":1507,"loop_ref":376,"next":1504,"prev":1506,"to":380,"twin":1514,"wire_ref":null},{"edge":753,"face":377,"from":375,"geometry_use":{"pcurve":1508,"periodic_lift":[0,0],"sense":"Forward"},"id":1508,"loop_ref":377,"next":1509,"prev":1511,"to":379,"twin":1498,"wire_ref":null},{"edge":758,"face":377,"from":379,"geometry_use":{"pcurve":1509,"periodic_lift":[0,0],"sense":"Forward"},"id":1509,"loop_ref":377,"next":1510,"prev":1508,"to":382,"twin":1518,"wire_ref":null},{"edge":759,"face":377,"from":382,"geometry_use":{"pcurve":1510,"periodic_lift":[0,0],"sense":"Reverse"},"id":1510,"loop_ref":377,"next":1511,"prev":1509,"to":383,"twin":1521,"wire_ref":null},{"edge":760,"face":377,"from":383,"geometry_use":{"pcurve":1511,"periodic_lift":[0,0],"sense":"Reverse"},"id":1511,"loop_ref":377,"next":1508,"prev":1510,"to":375,"twin":1512,"wire_ref":null},{"edge":760,"face":378,"from":375,"geometry_use":{"pcurve":1512,"periodic_lift":[0,0],"sense":"Forward"},"id":1512,"loop_ref":378,"next":1513,"prev":1515,"to":383,"twin":1511,"wire_ref":null},{"edge":761,"face":378,"from":383,"geometry_use":{"pcurve":1513,"periodic_lift":[0,0],"sense":"Reverse"},"id":1513,"loop_ref":378,"next":1514,"prev":1512,"to":380,"twin":1520,"wire_ref":null},{"edge":757,"face":378,"from":380,"geometry_use":{"pcurve":1514,"periodic_lift":[0,0],"sense":"Reverse"},"id":1514,"loop_ref":378,"next":1515,"prev":1513,"to":372,"twin":1507,"wire_ref":null},{"edge":747,"face":378,"from":372,"geometry_use":{"pcurve":1515,"periodic_lift":[0,0],"sense":"Forward"},"id":1515,"loop_ref":378,"next":1512,"prev":1514,"to":375,"twin":1490,"wire_ref":null},{"edge":756,"face":379,"from":376,"geometry_use":{"pcurve":1516,"periodic_lift":[0,0],"sense":"Forward"},"id":1516,"loop_ref":379,"next":1517,"prev":1519,"to":381,"twin":1505,"wire_ref":null},{"edge":762,"face":379,"from":381,"geometry_use":{"pcurve":1517,"periodic_lift":[0,0],"sense":"Forward"},"id":1517,"loop_ref":379,"next":1518,"prev":1516,"to":382,"twin":1522,"wire_ref":null},{"edge":758,"face":379,"from":382,"geometry_use":{"pcurve":1518,"periodic_lift":[0,0],"sense":"Reverse"},"id":1518,"loop_ref":379,"next":1519,"prev":1517,"to":379,"twin":1509,"wire_ref":null},{"edge":763,"face":379,"from":379,"geometry_use":{"pcurve":1519,"periodic_lift":[0,0],"sense":"Reverse"},"id":1519,"loop_ref":379,"next":1516,"prev":1518,"to":376,"twin":1536,"wire_ref":null},{"edge":761,"face":380,"from":380,"geometry_use":{"pcurve":1520,"periodic_lift":[0,0],"sense":"Forward"},"id":1520,"loop_ref":380,"next":1521,"prev":1523,"to":383,"twin":1513,"wire_ref":null},{"edge":759,"face":380,"from":383,"geometry_use":{"pcurve":1521,"periodic_lift":[0,0],"sense":"Forward"},"id":1521,"loop_ref":380,"next":1522,"prev":1520,"to":382,"twin":1510,"wire_ref":null},{"edge":762,"face":380,"from":382,"geometry_use":{"pcurve":1522,"periodic_lift":[0,0],"sense":"Reverse"},"id":1522,"loop_ref":380,"next":1523,"prev":1521,"to":381,"twin":1517,"wire_ref":null},{"edge":755,"face":380,"from":381,"geometry_use":{"pcurve":1523,"periodic_lift":[0,0],"sense":"Reverse"},"id":1523,"loop_ref":380,"next":1520,"prev":1522,"to":380,"twin":1504,"wire_ref":null},{"edge":764,"face":381,"from":376,"geometry_use":{"pcurve":1524,"periodic_lift":[0,0],"sense":"Forward"},"id":1524,"loop_ref":381,"next":1525,"prev":1527,"to":384,"twin":1539,"wire_ref":null},{"edge":765,"face":381,"from":384,"geometry_use":{"pcurve":1525,"periodic_lift":[0,0],"sense":"Reverse"},"id":1525,"loop_ref":381,"next":1526,"prev":1524,"to":385,"twin":1543,"wire_ref":null},{"edge":766,"face":381,"from":385,"geometry_use":{"pcurve":1526,"periodic_lift":[0,0],"sense":"Reverse"},"id":1526,"loop_ref":381,"next":1527,"prev":1525,"to":377,"twin":1535,"wire_ref":null},{"edge":749,"face":381,"from":377,"geometry_use":{"pcurve":1527,"periodic_lift":[0,0],"sense":"Forward"},"id":1527,"loop_ref":381,"next":1524,"prev":1526,"to":376,"twin":1493,"wire_ref":null},{"edge":767,"face":382,"from":378,"geometry_use":{"pcurve":1528,"periodic_lift":[0,0],"sense":"Forward"},"id":1528,"loop_ref":382,"next":1529,"prev":1531,"to":386,"twin":1533,"wire_ref":null},{"edge":768,"face":382,"from":386,"geometry_use":{"pcurve":1529,"periodic_lift":[0,0],"sense":"Forward"},"id":1529,"loop_ref":382,"next":1530,"prev":1528,"to":387,"twin":1547,"wire_ref":null},{"edge":769,"face":382,"from":387,"geometry_use":{"pcurve":1530,"periodic_lift":[0,0],"sense":"Reverse"},"id":1530,"loop_ref":382,"next":1531,"prev":1529,"to":379,"twin":1537,"wire_ref":null},{"edge":752,"face":382,"from":379,"geometry_use":{"pcurve":1531,"periodic_lift":[0,0],"sense":"Reverse"},"id":1531,"loop_ref":382,"next":1528,"prev":1530,"to":378,"twin":1497,"wire_ref":null},{"edge":770,"face":383,"from":385,"geometry_use":{"pcurve":1532,"periodic_lift":[0,0],"sense":"Forward"},"id":1532,"loop_ref":383,"next":1533,"prev":1535,"to":386,"twin":1550,"wire_ref":null},{"edge":767,"face":383,"from":386,"geometry_use":{"pcurve":1533,"periodic_lift":[0,0],"sense":"Reverse"},"id":1533,"loop_ref":383,"next":1534,"prev":1532,"to":378,"twin":1528,"wire_ref":null},{"edge":754,"face":383,"from":378,"geometry_use":{"pcurve":1534,"periodic_lift":[0,0],"sense":"Reverse"},"id":1534,"loop_ref":383,"next":1535,"prev":1533,"to":377,"twin":1500,"wire_ref":null},{"edge":766,"face":383,"from":377,"geometry_use":{"pcurve":1535,"periodic_lift":[0,0],"sense":"Forward"},"id":1535,"loop_ref":383,"next":1532,"prev":1534,"to":385,"twin":1526,"wire_ref":null},{"edge":763,"face":384,"from":376,"geometry_use":{"pcurve":1536,"periodic_lift":[0,0],"sense":"Forward"},"id":1536,"loop_ref":384,"next":1537,"prev":1539,"to":379,"twin":1519,"wire_ref":null},{"edge":769,"face":384,"from":379,"geometry_use":{"pcurve":1537,"periodic_lift":[0,0],"sense":"Forward"},"id":1537,"loop_ref":384,"next":1538,"prev":1536,"to":387,"twin":1530,"wire_ref":null},{"edge":771,"face":384,"from":387,"geometry_use":{"pcurve":1538,"periodic_lift":[0,0],"sense":"Reverse"},"id":1538,"loop_ref":384,"next":1539,"prev":1537,"to":384,"twin":1563,"wire_ref":null},{"edge":764,"face":384,"from":384,"geometry_use":{"pcurve":1539,"periodic_lift":[0,0],"sense":"Reverse"},"id":1539,"loop_ref":384,"next":1536,"prev":1538,"to":376,"twin":1524,"wire_ref":null},{"edge":772,"face":385,"from":384,"geometry_use":{"pcurve":1540,"periodic_lift":[0,0],"sense":"Forward"},"id":1540,"loop_ref":385,"next":1541,"prev":1543,"to":388,"twin":1554,"wire_ref":null},{"edge":773,"face":385,"from":388,"geometry_use":{"pcurve":1541,"periodic_lift":[0,0],"sense":"Reverse"},"id":1541,"loop_ref":385,"next":1542,"prev":1540,"to":389,"twin":1575,"wire_ref":null},{"edge":774,"face":385,"from":389,"geometry_use":{"pcurve":1542,"periodic_lift":[0,0],"sense":"Reverse"},"id":1542,"loop_ref":385,"next":1543,"prev":1541,"to":385,"twin":1551,"wire_ref":null},{"edge":765,"face":385,"from":385,"geometry_use":{"pcurve":1543,"periodic_lift":[0,0],"sense":"Forward"},"id":1543,"loop_ref":385,"next":1540,"prev":1542,"to":384,"twin":1525,"wire_ref":null},{"edge":775,"face":386,"from":386,"geometry_use":{"pcurve":1544,"periodic_lift":[0,0],"sense":"Forward"},"id":1544,"loop_ref":386,"next":1545,"prev":1547,"to":390,"twin":1549,"wire_ref":null},{"edge":776,"face":386,"from":390,"geometry_use":{"pcurve":1545,"periodic_lift":[0,0],"sense":"Forward"},"id":1545,"loop_ref":386,"next":1546,"prev":1544,"to":391,"twin":1579,"wire_ref":null},{"edge":777,"face":386,"from":391,"geometry_use":{"pcurve":1546,"periodic_lift":[0,0],"sense":"Reverse"},"id":1546,"loop_ref":386,"next":1547,"prev":1545,"to":387,"twin":1556,"wire_ref":null},{"edge":768,"face":386,"from":387,"geometry_use":{"pcurve":1547,"periodic_lift":[0,0],"sense":"Reverse"},"id":1547,"loop_ref":386,"next":1544,"prev":1546,"to":386,"twin":1529,"wire_ref":null},{"edge":778,"face":387,"from":389,"geometry_use":{"pcurve":1548,"periodic_lift":[0,0],"sense":"Forward"},"id":1548,"loop_ref":387,"next":1549,"prev":1551,"to":390,"twin":1582,"wire_ref":null},{"edge":775,"face":387,"from":390,"geometry_use":{"pcurve":1549,"periodic_lift":[0,0],"sense":"Reverse"},"id":1549,"loop_ref":387,"next":1550,"prev":1548,"to":386,"twin":1544,"wire_ref":null},{"edge":770,"face":387,"from":386,"geometry_use":{"pcurve":1550,"periodic_lift":[0,0],"sense":"Reverse"},"id":1550,"loop_ref":387,"next":1551,"prev":1549,"to":385,"twin":1532,"wire_ref":null},{"edge":774,"face":387,"from":385,"geometry_use":{"pcurve":1551,"periodic_lift":[0,0],"sense":"Forward"},"id":1551,"loop_ref":387,"next":1548,"prev":1550,"to":389,"twin":1542,"wire_ref":null},{"edge":779,"face":388,"from":392,"geometry_use":{"pcurve":1552,"periodic_lift":[0,0],"sense":"Forward"},"id":1552,"loop_ref":388,"next":1553,"prev":1555,"to":393,"twin":1571,"wire_ref":null},{"edge":780,"face":388,"from":393,"geometry_use":{"pcurve":1553,"periodic_lift":[0,0],"sense":"Reverse"},"id":1553,"loop_ref":388,"next":1554,"prev":1552,"to":388,"twin":1564,"wire_ref":null},{"edge":772,"face":388,"from":388,"geometry_use":{"pcurve":1554,"periodic_lift":[0,0],"sense":"Reverse"},"id":1554,"loop_ref":388,"next":1555,"prev":1553,"to":384,"twin":1540,"wire_ref":null},{"edge":781,"face":388,"from":384,"geometry_use":{"pcurve":1555,"periodic_lift":[0,0],"sense":"Forward"},"id":1555,"loop_ref":388,"next":1552,"prev":1554,"to":392,"twin":1562,"wire_ref":null},{"edge":777,"face":389,"from":387,"geometry_use":{"pcurve":1556,"periodic_lift":[0,0],"sense":"Forward"},"id":1556,"loop_ref":389,"next":1557,"prev":1559,"to":391,"twin":1546,"wire_ref":null},{"edge":782,"face":389,"from":391,"geometry_use":{"pcurve":1557,"periodic_lift":[0,0],"sense":"Forward"},"id":1557,"loop_ref":389,"next":1558,"prev":1556,"to":394,"twin":1566,"wire_ref":null},{"edge":783,"face":389,"from":394,"geometry_use":{"pcurve":1558,"periodic_lift":[0,0],"sense":"Reverse"},"id":1558,"loop_ref":389,"next":1559,"prev":1557,"to":395,"twin":1569,"wire_ref":null},{"edge":784,"face":389,"from":395,"geometry_use":{"pcurve":1559,"periodic_lift":[0,0],"sense":"Reverse"},"id":1559,"loop_ref":389,"next":1556,"prev":1558,"to":387,"twin":1560,"wire_ref":null},{"edge":784,"face":390,"from":387,"geometry_use":{"pcurve":1560,"periodic_lift":[0,0],"sense":"Forward"},"id":1560,"loop_ref":390,"next":1561,"prev":1563,"to":395,"twin":1559,"wire_ref":null},{"edge":785,"face":390,"from":395,"geometry_use":{"pcurve":1561,"periodic_lift":[0,0],"sense":"Reverse"},"id":1561,"loop_ref":390,"next":1562,"prev":1560,"to":392,"twin":1568,"wire_ref":null},{"edge":781,"face":390,"from":392,"geometry_use":{"pcurve":1562,"periodic_lift":[0,0],"sense":"Reverse"},"id":1562,"loop_ref":390,"next":1563,"prev":1561,"to":384,"twin":1555,"wire_ref":null},{"edge":771,"face":390,"from":384,"geometry_use":{"pcurve":1563,"periodic_lift":[0,0],"sense":"Forward"},"id":1563,"loop_ref":390,"next":1560,"prev":1562,"to":387,"twin":1538,"wire_ref":null},{"edge":780,"face":391,"from":388,"geometry_use":{"pcurve":1564,"periodic_lift":[0,0],"sense":"Forward"},"id":1564,"loop_ref":391,"next":1565,"prev":1567,"to":393,"twin":1553,"wire_ref":null},{"edge":786,"face":391,"from":393,"geometry_use":{"pcurve":1565,"periodic_lift":[0,0],"sense":"Forward"},"id":1565,"loop_ref":391,"next":1566,"prev":1564,"to":394,"twin":1570,"wire_ref":null},{"edge":782,"face":391,"from":394,"geometry_use":{"pcurve":1566,"periodic_lift":[0,0],"sense":"Reverse"},"id":1566,"loop_ref":391,"next":1567,"prev":1565,"to":391,"twin":1557,"wire_ref":null},{"edge":787,"face":391,"from":391,"geometry_use":{"pcurve":1567,"periodic_lift":[0,0],"sense":"Reverse"},"id":1567,"loop_ref":391,"next":1564,"prev":1566,"to":388,"twin":1584,"wire_ref":null},{"edge":785,"face":392,"from":392,"geometry_use":{"pcurve":1568,"periodic_lift":[0,0],"sense":"Forward"},"id":1568,"loop_ref":392,"next":1569,"prev":1571,"to":395,"twin":1561,"wire_ref":null},{"edge":783,"face":392,"from":395,"geometry_use":{"pcurve":1569,"periodic_lift":[0,0],"sense":"Forward"},"id":1569,"loop_ref":392,"next":1570,"prev":1568,"to":394,"twin":1558,"wire_ref":null},{"edge":786,"face":392,"from":394,"geometry_use":{"pcurve":1570,"periodic_lift":[0,0],"sense":"Reverse"},"id":1570,"loop_ref":392,"next":1571,"prev":1569,"to":393,"twin":1565,"wire_ref":null},{"edge":779,"face":392,"from":393,"geometry_use":{"pcurve":1571,"periodic_lift":[0,0],"sense":"Reverse"},"id":1571,"loop_ref":392,"next":1568,"prev":1570,"to":392,"twin":1552,"wire_ref":null},{"edge":788,"face":393,"from":388,"geometry_use":{"pcurve":1572,"periodic_lift":[0,0],"sense":"Forward"},"id":1572,"loop_ref":393,"next":1573,"prev":1575,"to":396,"twin":1587,"wire_ref":null},{"edge":789,"face":393,"from":396,"geometry_use":{"pcurve":1573,"periodic_lift":[0,0],"sense":"Reverse"},"id":1573,"loop_ref":393,"next":1574,"prev":1572,"to":397,"twin":1591,"wire_ref":null},{"edge":790,"face":393,"from":397,"geometry_use":{"pcurve":1574,"periodic_lift":[0,0],"sense":"Reverse"},"id":1574,"loop_ref":393,"next":1575,"prev":1573,"to":389,"twin":1583,"wire_ref":null},{"edge":773,"face":393,"from":389,"geometry_use":{"pcurve":1575,"periodic_lift":[0,0],"sense":"Forward"},"id":1575,"loop_ref":393,"next":1572,"prev":1574,"to":388,"twin":1541,"wire_ref":null},{"edge":791,"face":394,"from":390,"geometry_use":{"pcurve":1576,"periodic_lift":[0,0],"sense":"Forward"},"id":1576,"loop_ref":394,"next":1577,"prev":1579,"to":398,"twin":1581,"wire_ref":null},{"edge":792,"face":394,"from":398,"geometry_use":{"pcurve":1577,"periodic_lift":[0,0],"sense":"Forward"},"id":1577,"loop_ref":394,"next":1578,"prev":1576,"to":399,"twin":1595,"wire_ref":null},{"edge":793,"face":394,"from":399,"geometry_use":{"pcurve":1578,"periodic_lift":[0,0],"sense":"Reverse"},"id":1578,"loop_ref":394,"next":1579,"prev":1577,"to":391,"twin":1585,"wire_ref":null},{"edge":776,"face":394,"from":391,"geometry_use":{"pcurve":1579,"periodic_lift":[0,0],"sense":"Reverse"},"id":1579,"loop_ref":394,"next":1576,"prev":1578,"to":390,"twin":1545,"wire_ref":null},{"edge":794,"face":395,"from":397,"geometry_use":{"pcurve":1580,"periodic_lift":[0,0],"sense":"Forward"},"id":1580,"loop_ref":395,"next":1581,"prev":1583,"to":398,"twin":1598,"wire_ref":null},{"edge":791,"face":395,"from":398,"geometry_use":{"pcurve":1581,"periodic_lift":[0,0],"sense":"Reverse"},"id":1581,"loop_ref":395,"next":1582,"prev":1580,"to":390,"twin":1576,"wire_ref":null},{"edge":778,"face":395,"from":390,"geometry_use":{"pcurve":1582,"periodic_lift":[0,0],"sense":"Reverse"},"id":1582,"loop_ref":395,"next":1583,"prev":1581,"to":389,"twin":1548,"wire_ref":null},{"edge":790,"face":395,"from":389,"geometry_use":{"pcurve":1583,"periodic_lift":[0,0],"sense":"Forward"},"id":1583,"loop_ref":395,"next":1580,"prev":1582,"to":397,"twin":1574,"wire_ref":null},{"edge":787,"face":396,"from":388,"geometry_use":{"pcurve":1584,"periodic_lift":[0,0],"sense":"Forward"},"id":1584,"loop_ref":396,"next":1585,"prev":1587,"to":391,"twin":1567,"wire_ref":null},{"edge":793,"face":396,"from":391,"geometry_use":{"pcurve":1585,"periodic_lift":[0,0],"sense":"Forward"},"id":1585,"loop_ref":396,"next":1586,"prev":1584,"to":399,"twin":1578,"wire_ref":null},{"edge":795,"face":396,"from":399,"geometry_use":{"pcurve":1586,"periodic_lift":[0,0],"sense":"Reverse"},"id":1586,"loop_ref":396,"next":1587,"prev":1585,"to":396,"twin":1611,"wire_ref":null},{"edge":788,"face":396,"from":396,"geometry_use":{"pcurve":1587,"periodic_lift":[0,0],"sense":"Reverse"},"id":1587,"loop_ref":396,"next":1584,"prev":1586,"to":388,"twin":1572,"wire_ref":null},{"edge":796,"face":397,"from":396,"geometry_use":{"pcurve":1588,"periodic_lift":[0,0],"sense":"Forward"},"id":1588,"loop_ref":397,"next":1589,"prev":1591,"to":400,"twin":1602,"wire_ref":null},{"edge":797,"face":397,"from":400,"geometry_use":{"pcurve":1589,"periodic_lift":[0,0],"sense":"Reverse"},"id":1589,"loop_ref":397,"next":1590,"prev":1588,"to":401,"twin":1623,"wire_ref":null},{"edge":798,"face":397,"from":401,"geometry_use":{"pcurve":1590,"periodic_lift":[0,0],"sense":"Reverse"},"id":1590,"loop_ref":397,"next":1591,"prev":1589,"to":397,"twin":1599,"wire_ref":null},{"edge":789,"face":397,"from":397,"geometry_use":{"pcurve":1591,"periodic_lift":[0,0],"sense":"Forward"},"id":1591,"loop_ref":397,"next":1588,"prev":1590,"to":396,"twin":1573,"wire_ref":null},{"edge":799,"face":398,"from":398,"geometry_use":{"pcurve":1592,"periodic_lift":[0,0],"sense":"Forward"},"id":1592,"loop_ref":398,"next":1593,"prev":1595,"to":402,"twin":1597,"wire_ref":null},{"edge":800,"face":398,"from":402,"geometry_use":{"pcurve":1593,"periodic_lift":[0,0],"sense":"Forward"},"id":1593,"loop_ref":398,"next":1594,"prev":1592,"to":403,"twin":1627,"wire_ref":null},{"edge":801,"face":398,"from":403,"geometry_use":{"pcurve":1594,"periodic_lift":[0,0],"sense":"Reverse"},"id":1594,"loop_ref":398,"next":1595,"prev":1593,"to":399,"twin":1604,"wire_ref":null},{"edge":792,"face":398,"from":399,"geometry_use":{"pcurve":1595,"periodic_lift":[0,0],"sense":"Reverse"},"id":1595,"loop_ref":398,"next":1592,"prev":1594,"to":398,"twin":1577,"wire_ref":null},{"edge":802,"face":399,"from":401,"geometry_use":{"pcurve":1596,"periodic_lift":[0,0],"sense":"Forward"},"id":1596,"loop_ref":399,"next":1597,"prev":1599,"to":402,"twin":1630,"wire_ref":null},{"edge":799,"face":399,"from":402,"geometry_use":{"pcurve":1597,"periodic_lift":[0,0],"sense":"Reverse"},"id":1597,"loop_ref":399,"next":1598,"prev":1596,"to":398,"twin":1592,"wire_ref":null},{"edge":794,"face":399,"from":398,"geometry_use":{"pcurve":1598,"periodic_lift":[0,0],"sense":"Reverse"},"id":1598,"loop_ref":399,"next":1599,"prev":1597,"to":397,"twin":1580,"wire_ref":null},{"edge":798,"face":399,"from":397,"geometry_use":{"pcurve":1599,"periodic_lift":[0,0],"sense":"Forward"},"id":1599,"loop_ref":399,"next":1596,"prev":1598,"to":401,"twin":1590,"wire_ref":null},{"edge":803,"face":400,"from":404,"geometry_use":{"pcurve":1600,"periodic_lift":[0,0],"sense":"Forward"},"id":1600,"loop_ref":400,"next":1601,"prev":1603,"to":405,"twin":1619,"wire_ref":null},{"edge":804,"face":400,"from":405,"geometry_use":{"pcurve":1601,"periodic_lift":[0,0],"sense":"Reverse"},"id":1601,"loop_ref":400,"next":1602,"prev":1600,"to":400,"twin":1612,"wire_ref":null},{"edge":796,"face":400,"from":400,"geometry_use":{"pcurve":1602,"periodic_lift":[0,0],"sense":"Reverse"},"id":1602,"loop_ref":400,"next":1603,"prev":1601,"to":396,"twin":1588,"wire_ref":null},{"edge":805,"face":400,"from":396,"geometry_use":{"pcurve":1603,"periodic_lift":[0,0],"sense":"Forward"},"id":1603,"loop_ref":400,"next":1600,"prev":1602,"to":404,"twin":1610,"wire_ref":null},{"edge":801,"face":401,"from":399,"geometry_use":{"pcurve":1604,"periodic_lift":[0,0],"sense":"Forward"},"id":1604,"loop_ref":401,"next":1605,"prev":1607,"to":403,"twin":1594,"wire_ref":null},{"edge":806,"face":401,"from":403,"geometry_use":{"pcurve":1605,"periodic_lift":[0,0],"sense":"Forward"},"id":1605,"loop_ref":401,"next":1606,"prev":1604,"to":406,"twin":1614,"wire_ref":null},{"edge":807,"face":401,"from":406,"geometry_use":{"pcurve":1606,"periodic_lift":[0,0],"sense":"Reverse"},"id":1606,"loop_ref":401,"next":1607,"prev":1605,"to":407,"twin":1617,"wire_ref":null},{"edge":808,"face":401,"from":407,"geometry_use":{"pcurve":1607,"periodic_lift":[0,0],"sense":"Reverse"},"id":1607,"loop_ref":401,"next":1604,"prev":1606,"to":399,"twin":1608,"wire_ref":null},{"edge":808,"face":402,"from":399,"geometry_use":{"pcurve":1608,"periodic_lift":[0,0],"sense":"Forward"},"id":1608,"loop_ref":402,"next":1609,"prev":1611,"to":407,"twin":1607,"wire_ref":null},{"edge":809,"face":402,"from":407,"geometry_use":{"pcurve":1609,"periodic_lift":[0,0],"sense":"Reverse"},"id":1609,"loop_ref":402,"next":1610,"prev":1608,"to":404,"twin":1616,"wire_ref":null},{"edge":805,"face":402,"from":404,"geometry_use":{"pcurve":1610,"periodic_lift":[0,0],"sense":"Reverse"},"id":1610,"loop_ref":402,"next":1611,"prev":1609,"to":396,"twin":1603,"wire_ref":null},{"edge":795,"face":402,"from":396,"geometry_use":{"pcurve":1611,"periodic_lift":[0,0],"sense":"Forward"},"id":1611,"loop_ref":402,"next":1608,"prev":1610,"to":399,"twin":1586,"wire_ref":null},{"edge":804,"face":403,"from":400,"geometry_use":{"pcurve":1612,"periodic_lift":[0,0],"sense":"Forward"},"id":1612,"loop_ref":403,"next":1613,"prev":1615,"to":405,"twin":1601,"wire_ref":null},{"edge":810,"face":403,"from":405,"geometry_use":{"pcurve":1613,"periodic_lift":[0,0],"sense":"Forward"},"id":1613,"loop_ref":403,"next":1614,"prev":1612,"to":406,"twin":1618,"wire_ref":null},{"edge":806,"face":403,"from":406,"geometry_use":{"pcurve":1614,"periodic_lift":[0,0],"sense":"Reverse"},"id":1614,"loop_ref":403,"next":1615,"prev":1613,"to":403,"twin":1605,"wire_ref":null},{"edge":811,"face":403,"from":403,"geometry_use":{"pcurve":1615,"periodic_lift":[0,0],"sense":"Reverse"},"id":1615,"loop_ref":403,"next":1612,"prev":1614,"to":400,"twin":1632,"wire_ref":null},{"edge":809,"face":404,"from":404,"geometry_use":{"pcurve":1616,"periodic_lift":[0,0],"sense":"Forward"},"id":1616,"loop_ref":404,"next":1617,"prev":1619,"to":407,"twin":1609,"wire_ref":null},{"edge":807,"face":404,"from":407,"geometry_use":{"pcurve":1617,"periodic_lift":[0,0],"sense":"Forward"},"id":1617,"loop_ref":404,"next":1618,"prev":1616,"to":406,"twin":1606,"wire_ref":null},{"edge":810,"face":404,"from":406,"geometry_use":{"pcurve":1618,"periodic_lift":[0,0],"sense":"Reverse"},"id":1618,"loop_ref":404,"next":1619,"prev":1617,"to":405,"twin":1613,"wire_ref":null},{"edge":803,"face":404,"from":405,"geometry_use":{"pcurve":1619,"periodic_lift":[0,0],"sense":"Reverse"},"id":1619,"loop_ref":404,"next":1616,"prev":1618,"to":404,"twin":1600,"wire_ref":null},{"edge":812,"face":405,"from":400,"geometry_use":{"pcurve":1620,"periodic_lift":[0,0],"sense":"Forward"},"id":1620,"loop_ref":405,"next":1621,"prev":1623,"to":408,"twin":1635,"wire_ref":null},{"edge":813,"face":405,"from":408,"geometry_use":{"pcurve":1621,"periodic_lift":[0,0],"sense":"Reverse"},"id":1621,"loop_ref":405,"next":1622,"prev":1620,"to":409,"twin":1639,"wire_ref":null},{"edge":814,"face":405,"from":409,"geometry_use":{"pcurve":1622,"periodic_lift":[0,0],"sense":"Reverse"},"id":1622,"loop_ref":405,"next":1623,"prev":1621,"to":401,"twin":1631,"wire_ref":null},{"edge":797,"face":405,"from":401,"geometry_use":{"pcurve":1623,"periodic_lift":[0,0],"sense":"Forward"},"id":1623,"loop_ref":405,"next":1620,"prev":1622,"to":400,"twin":1589,"wire_ref":null},{"edge":815,"face":406,"from":402,"geometry_use":{"pcurve":1624,"periodic_lift":[0,0],"sense":"Forward"},"id":1624,"loop_ref":406,"next":1625,"prev":1627,"to":410,"twin":1629,"wire_ref":null},{"edge":816,"face":406,"from":410,"geometry_use":{"pcurve":1625,"periodic_lift":[0,0],"sense":"Forward"},"id":1625,"loop_ref":406,"next":1626,"prev":1624,"to":411,"twin":1643,"wire_ref":null},{"edge":817,"face":406,"from":411,"geometry_use":{"pcurve":1626,"periodic_lift":[0,0],"sense":"Reverse"},"id":1626,"loop_ref":406,"next":1627,"prev":1625,"to":403,"twin":1633,"wire_ref":null},{"edge":800,"face":406,"from":403,"geometry_use":{"pcurve":1627,"periodic_lift":[0,0],"sense":"Reverse"},"id":1627,"loop_ref":406,"next":1624,"prev":1626,"to":402,"twin":1593,"wire_ref":null},{"edge":818,"face":407,"from":409,"geometry_use":{"pcurve":1628,"periodic_lift":[0,0],"sense":"Forward"},"id":1628,"loop_ref":407,"next":1629,"prev":1631,"to":410,"twin":1646,"wire_ref":null},{"edge":815,"face":407,"from":410,"geometry_use":{"pcurve":1629,"periodic_lift":[0,0],"sense":"Reverse"},"id":1629,"loop_ref":407,"next":1630,"prev":1628,"to":402,"twin":1624,"wire_ref":null},{"edge":802,"face":407,"from":402,"geometry_use":{"pcurve":1630,"periodic_lift":[0,0],"sense":"Reverse"},"id":1630,"loop_ref":407,"next":1631,"prev":1629,"to":401,"twin":1596,"wire_ref":null},{"edge":814,"face":407,"from":401,"geometry_use":{"pcurve":1631,"periodic_lift":[0,0],"sense":"Forward"},"id":1631,"loop_ref":407,"next":1628,"prev":1630,"to":409,"twin":1622,"wire_ref":null},{"edge":811,"face":408,"from":400,"geometry_use":{"pcurve":1632,"periodic_lift":[0,0],"sense":"Forward"},"id":1632,"loop_ref":408,"next":1633,"prev":1635,"to":403,"twin":1615,"wire_ref":null},{"edge":817,"face":408,"from":403,"geometry_use":{"pcurve":1633,"periodic_lift":[0,0],"sense":"Forward"},"id":1633,"loop_ref":408,"next":1634,"prev":1632,"to":411,"twin":1626,"wire_ref":null},{"edge":819,"face":408,"from":411,"geometry_use":{"pcurve":1634,"periodic_lift":[0,0],"sense":"Reverse"},"id":1634,"loop_ref":408,"next":1635,"prev":1633,"to":408,"twin":1659,"wire_ref":null},{"edge":812,"face":408,"from":408,"geometry_use":{"pcurve":1635,"periodic_lift":[0,0],"sense":"Reverse"},"id":1635,"loop_ref":408,"next":1632,"prev":1634,"to":400,"twin":1620,"wire_ref":null},{"edge":820,"face":409,"from":408,"geometry_use":{"pcurve":1636,"periodic_lift":[0,0],"sense":"Forward"},"id":1636,"loop_ref":409,"next":1637,"prev":1639,"to":412,"twin":1650,"wire_ref":null},{"edge":821,"face":409,"from":412,"geometry_use":{"pcurve":1637,"periodic_lift":[0,0],"sense":"Reverse"},"id":1637,"loop_ref":409,"next":1638,"prev":1636,"to":413,"twin":1671,"wire_ref":null},{"edge":822,"face":409,"from":413,"geometry_use":{"pcurve":1638,"periodic_lift":[0,0],"sense":"Reverse"},"id":1638,"loop_ref":409,"next":1639,"prev":1637,"to":409,"twin":1647,"wire_ref":null},{"edge":813,"face":409,"from":409,"geometry_use":{"pcurve":1639,"periodic_lift":[0,0],"sense":"Forward"},"id":1639,"loop_ref":409,"next":1636,"prev":1638,"to":408,"twin":1621,"wire_ref":null},{"edge":823,"face":410,"from":410,"geometry_use":{"pcurve":1640,"periodic_lift":[0,0],"sense":"Forward"},"id":1640,"loop_ref":410,"next":1641,"prev":1643,"to":414,"twin":1645,"wire_ref":null},{"edge":824,"face":410,"from":414,"geometry_use":{"pcurve":1641,"periodic_lift":[0,0],"sense":"Forward"},"id":1641,"loop_ref":410,"next":1642,"prev":1640,"to":415,"twin":1675,"wire_ref":null},{"edge":825,"face":410,"from":415,"geometry_use":{"pcurve":1642,"periodic_lift":[0,0],"sense":"Reverse"},"id":1642,"loop_ref":410,"next":1643,"prev":1641,"to":411,"twin":1652,"wire_ref":null},{"edge":816,"face":410,"from":411,"geometry_use":{"pcurve":1643,"periodic_lift":[0,0],"sense":"Reverse"},"id":1643,"loop_ref":410,"next":1640,"prev":1642,"to":410,"twin":1625,"wire_ref":null},{"edge":826,"face":411,"from":413,"geometry_use":{"pcurve":1644,"periodic_lift":[0,0],"sense":"Forward"},"id":1644,"loop_ref":411,"next":1645,"prev":1647,"to":414,"twin":1678,"wire_ref":null},{"edge":823,"face":411,"from":414,"geometry_use":{"pcurve":1645,"periodic_lift":[0,0],"sense":"Reverse"},"id":1645,"loop_ref":411,"next":1646,"prev":1644,"to":410,"twin":1640,"wire_ref":null},{"edge":818,"face":411,"from":410,"geometry_use":{"pcurve":1646,"periodic_lift":[0,0],"sense":"Reverse"},"id":1646,"loop_ref":411,"next":1647,"prev":1645,"to":409,"twin":1628,"wire_ref":null},{"edge":822,"face":411,"from":409,"geometry_use":{"pcurve":1647,"periodic_lift":[0,0],"sense":"Forward"},"id":1647,"loop_ref":411,"next":1644,"prev":1646,"to":413,"twin":1638,"wire_ref":null},{"edge":827,"face":412,"from":416,"geometry_use":{"pcurve":1648,"periodic_lift":[0,0],"sense":"Forward"},"id":1648,"loop_ref":412,"next":1649,"prev":1651,"to":417,"twin":1667,"wire_ref":null},{"edge":828,"face":412,"from":417,"geometry_use":{"pcurve":1649,"periodic_lift":[0,0],"sense":"Reverse"},"id":1649,"loop_ref":412,"next":1650,"prev":1648,"to":412,"twin":1660,"wire_ref":null},{"edge":820,"face":412,"from":412,"geometry_use":{"pcurve":1650,"periodic_lift":[0,0],"sense":"Reverse"},"id":1650,"loop_ref":412,"next":1651,"prev":1649,"to":408,"twin":1636,"wire_ref":null},{"edge":829,"face":412,"from":408,"geometry_use":{"pcurve":1651,"periodic_lift":[0,0],"sense":"Forward"},"id":1651,"loop_ref":412,"next":1648,"prev":1650,"to":416,"twin":1658,"wire_ref":null},{"edge":825,"face":413,"from":411,"geometry_use":{"pcurve":1652,"periodic_lift":[0,0],"sense":"Forward"},"id":1652,"loop_ref":413,"next":1653,"prev":1655,"to":415,"twin":1642,"wire_ref":null},{"edge":830,"face":413,"from":415,"geometry_use":{"pcurve":1653,"periodic_lift":[0,0],"sense":"Forward"},"id":1653,"loop_ref":413,"next":1654,"prev":1652,"to":418,"twin":1662,"wire_ref":null},{"edge":831,"face":413,"from":418,"geometry_use":{"pcurve":1654,"periodic_lift":[0,0],"sense":"Reverse"},"id":1654,"loop_ref":413,"next":1655,"prev":1653,"to":419,"twin":1665,"wire_ref":null},{"edge":832,"face":413,"from":419,"geometry_use":{"pcurve":1655,"periodic_lift":[0,0],"sense":"Reverse"},"id":1655,"loop_ref":413,"next":1652,"prev":1654,"to":411,"twin":1656,"wire_ref":null},{"edge":832,"face":414,"from":411,"geometry_use":{"pcurve":1656,"periodic_lift":[0,0],"sense":"Forward"},"id":1656,"loop_ref":414,"next":1657,"prev":1659,"to":419,"twin":1655,"wire_ref":null},{"edge":833,"face":414,"from":419,"geometry_use":{"pcurve":1657,"periodic_lift":[0,0],"sense":"Reverse"},"id":1657,"loop_ref":414,"next":1658,"prev":1656,"to":416,"twin":1664,"wire_ref":null},{"edge":829,"face":414,"from":416,"geometry_use":{"pcurve":1658,"periodic_lift":[0,0],"sense":"Reverse"},"id":1658,"loop_ref":414,"next":1659,"prev":1657,"to":408,"twin":1651,"wire_ref":null},{"edge":819,"face":414,"from":408,"geometry_use":{"pcurve":1659,"periodic_lift":[0,0],"sense":"Forward"},"id":1659,"loop_ref":414,"next":1656,"prev":1658,"to":411,"twin":1634,"wire_ref":null},{"edge":828,"face":415,"from":412,"geometry_use":{"pcurve":1660,"periodic_lift":[0,0],"sense":"Forward"},"id":1660,"loop_ref":415,"next":1661,"prev":1663,"to":417,"twin":1649,"wire_ref":null},{"edge":834,"face":415,"from":417,"geometry_use":{"pcurve":1661,"periodic_lift":[0,0],"sense":"Forward"},"id":1661,"loop_ref":415,"next":1662,"prev":1660,"to":418,"twin":1666,"wire_ref":null},{"edge":830,"face":415,"from":418,"geometry_use":{"pcurve":1662,"periodic_lift":[0,0],"sense":"Reverse"},"id":1662,"loop_ref":415,"next":1663,"prev":1661,"to":415,"twin":1653,"wire_ref":null},{"edge":835,"face":415,"from":415,"geometry_use":{"pcurve":1663,"periodic_lift":[0,0],"sense":"Reverse"},"id":1663,"loop_ref":415,"next":1660,"prev":1662,"to":412,"twin":1680,"wire_ref":null},{"edge":833,"face":416,"from":416,"geometry_use":{"pcurve":1664,"periodic_lift":[0,0],"sense":"Forward"},"id":1664,"loop_ref":416,"next":1665,"prev":1667,"to":419,"twin":1657,"wire_ref":null},{"edge":831,"face":416,"from":419,"geometry_use":{"pcurve":1665,"periodic_lift":[0,0],"sense":"Forward"},"id":1665,"loop_ref":416,"next":1666,"prev":1664,"to":418,"twin":1654,"wire_ref":null},{"edge":834,"face":416,"from":418,"geometry_use":{"pcurve":1666,"periodic_lift":[0,0],"sense":"Reverse"},"id":1666,"loop_ref":416,"next":1667,"prev":1665,"to":417,"twin":1661,"wire_ref":null},{"edge":827,"face":416,"from":417,"geometry_use":{"pcurve":1667,"periodic_lift":[0,0],"sense":"Reverse"},"id":1667,"loop_ref":416,"next":1664,"prev":1666,"to":416,"twin":1648,"wire_ref":null},{"edge":836,"face":417,"from":412,"geometry_use":{"pcurve":1668,"periodic_lift":[0,0],"sense":"Forward"},"id":1668,"loop_ref":417,"next":1669,"prev":1671,"to":420,"twin":1683,"wire_ref":null},{"edge":837,"face":417,"from":420,"geometry_use":{"pcurve":1669,"periodic_lift":[0,0],"sense":"Reverse"},"id":1669,"loop_ref":417,"next":1670,"prev":1668,"to":421,"twin":1687,"wire_ref":null},{"edge":838,"face":417,"from":421,"geometry_use":{"pcurve":1670,"periodic_lift":[0,0],"sense":"Reverse"},"id":1670,"loop_ref":417,"next":1671,"prev":1669,"to":413,"twin":1679,"wire_ref":null},{"edge":821,"face":417,"from":413,"geometry_use":{"pcurve":1671,"periodic_lift":[0,0],"sense":"Forward"},"id":1671,"loop_ref":417,"next":1668,"prev":1670,"to":412,"twin":1637,"wire_ref":null},{"edge":839,"face":418,"from":414,"geometry_use":{"pcurve":1672,"periodic_lift":[0,0],"sense":"Forward"},"id":1672,"loop_ref":418,"next":1673,"prev":1675,"to":422,"twin":1677,"wire_ref":null},{"edge":840,"face":418,"from":422,"geometry_use":{"pcurve":1673,"periodic_lift":[0,0],"sense":"Forward"},"id":1673,"loop_ref":418,"next":1674,"prev":1672,"to":423,"twin":1691,"wire_ref":null},{"edge":841,"face":418,"from":423,"geometry_use":{"pcurve":1674,"periodic_lift":[0,0],"sense":"Reverse"},"id":1674,"loop_ref":418,"next":1675,"prev":1673,"to":415,"twin":1681,"wire_ref":null},{"edge":824,"face":418,"from":415,"geometry_use":{"pcurve":1675,"periodic_lift":[0,0],"sense":"Reverse"},"id":1675,"loop_ref":418,"next":1672,"prev":1674,"to":414,"twin":1641,"wire_ref":null},{"edge":842,"face":419,"from":421,"geometry_use":{"pcurve":1676,"periodic_lift":[0,0],"sense":"Forward"},"id":1676,"loop_ref":419,"next":1677,"prev":1679,"to":422,"twin":1694,"wire_ref":null},{"edge":839,"face":419,"from":422,"geometry_use":{"pcurve":1677,"periodic_lift":[0,0],"sense":"Reverse"},"id":1677,"loop_ref":419,"next":1678,"prev":1676,"to":414,"twin":1672,"wire_ref":null},{"edge":826,"face":419,"from":414,"geometry_use":{"pcurve":1678,"periodic_lift":[0,0],"sense":"Reverse"},"id":1678,"loop_ref":419,"next":1679,"prev":1677,"to":413,"twin":1644,"wire_ref":null},{"edge":838,"face":419,"from":413,"geometry_use":{"pcurve":1679,"periodic_lift":[0,0],"sense":"Forward"},"id":1679,"loop_ref":419,"next":1676,"prev":1678,"to":421,"twin":1670,"wire_ref":null},{"edge":835,"face":420,"from":412,"geometry_use":{"pcurve":1680,"periodic_lift":[0,0],"sense":"Forward"},"id":1680,"loop_ref":420,"next":1681,"prev":1683,"to":415,"twin":1663,"wire_ref":null},{"edge":841,"face":420,"from":415,"geometry_use":{"pcurve":1681,"periodic_lift":[0,0],"sense":"Forward"},"id":1681,"loop_ref":420,"next":1682,"prev":1680,"to":423,"twin":1674,"wire_ref":null},{"edge":843,"face":420,"from":423,"geometry_use":{"pcurve":1682,"periodic_lift":[0,0],"sense":"Reverse"},"id":1682,"loop_ref":420,"next":1683,"prev":1681,"to":420,"twin":1707,"wire_ref":null},{"edge":836,"face":420,"from":420,"geometry_use":{"pcurve":1683,"periodic_lift":[0,0],"sense":"Reverse"},"id":1683,"loop_ref":420,"next":1680,"prev":1682,"to":412,"twin":1668,"wire_ref":null},{"edge":844,"face":421,"from":420,"geometry_use":{"pcurve":1684,"periodic_lift":[0,0],"sense":"Forward"},"id":1684,"loop_ref":421,"next":1685,"prev":1687,"to":424,"twin":1698,"wire_ref":null},{"edge":845,"face":421,"from":424,"geometry_use":{"pcurve":1685,"periodic_lift":[0,0],"sense":"Reverse"},"id":1685,"loop_ref":421,"next":1686,"prev":1684,"to":425,"twin":1719,"wire_ref":null},{"edge":846,"face":421,"from":425,"geometry_use":{"pcurve":1686,"periodic_lift":[0,0],"sense":"Reverse"},"id":1686,"loop_ref":421,"next":1687,"prev":1685,"to":421,"twin":1695,"wire_ref":null},{"edge":837,"face":421,"from":421,"geometry_use":{"pcurve":1687,"periodic_lift":[0,0],"sense":"Forward"},"id":1687,"loop_ref":421,"next":1684,"prev":1686,"to":420,"twin":1669,"wire_ref":null},{"edge":847,"face":422,"from":422,"geometry_use":{"pcurve":1688,"periodic_lift":[0,0],"sense":"Forward"},"id":1688,"loop_ref":422,"next":1689,"prev":1691,"to":426,"twin":1693,"wire_ref":null},{"edge":848,"face":422,"from":426,"geometry_use":{"pcurve":1689,"periodic_lift":[0,0],"sense":"Forward"},"id":1689,"loop_ref":422,"next":1690,"prev":1688,"to":427,"twin":1723,"wire_ref":null},{"edge":849,"face":422,"from":427,"geometry_use":{"pcurve":1690,"periodic_lift":[0,0],"sense":"Reverse"},"id":1690,"loop_ref":422,"next":1691,"prev":1689,"to":423,"twin":1700,"wire_ref":null},{"edge":840,"face":422,"from":423,"geometry_use":{"pcurve":1691,"periodic_lift":[0,0],"sense":"Reverse"},"id":1691,"loop_ref":422,"next":1688,"prev":1690,"to":422,"twin":1673,"wire_ref":null},{"edge":850,"face":423,"from":425,"geometry_use":{"pcurve":1692,"periodic_lift":[0,0],"sense":"Forward"},"id":1692,"loop_ref":423,"next":1693,"prev":1695,"to":426,"twin":1726,"wire_ref":null},{"edge":847,"face":423,"from":426,"geometry_use":{"pcurve":1693,"periodic_lift":[0,0],"sense":"Reverse"},"id":1693,"loop_ref":423,"next":1694,"prev":1692,"to":422,"twin":1688,"wire_ref":null},{"edge":842,"face":423,"from":422,"geometry_use":{"pcurve":1694,"periodic_lift":[0,0],"sense":"Reverse"},"id":1694,"loop_ref":423,"next":1695,"prev":1693,"to":421,"twin":1676,"wire_ref":null},{"edge":846,"face":423,"from":421,"geometry_use":{"pcurve":1695,"periodic_lift":[0,0],"sense":"Forward"},"id":1695,"loop_ref":423,"next":1692,"prev":1694,"to":425,"twin":1686,"wire_ref":null},{"edge":851,"face":424,"from":428,"geometry_use":{"pcurve":1696,"periodic_lift":[0,0],"sense":"Forward"},"id":1696,"loop_ref":424,"next":1697,"prev":1699,"to":429,"twin":1715,"wire_ref":null},{"edge":852,"face":424,"from":429,"geometry_use":{"pcurve":1697,"periodic_lift":[0,0],"sense":"Reverse"},"id":1697,"loop_ref":424,"next":1698,"prev":1696,"to":424,"twin":1708,"wire_ref":null},{"edge":844,"face":424,"from":424,"geometry_use":{"pcurve":1698,"periodic_lift":[0,0],"sense":"Reverse"},"id":1698,"loop_ref":424,"next":1699,"prev":1697,"to":420,"twin":1684,"wire_ref":null},{"edge":853,"face":424,"from":420,"geometry_use":{"pcurve":1699,"periodic_lift":[0,0],"sense":"Forward"},"id":1699,"loop_ref":424,"next":1696,"prev":1698,"to":428,"twin":1706,"wire_ref":null},{"edge":849,"face":425,"from":423,"geometry_use":{"pcurve":1700,"periodic_lift":[0,0],"sense":"Forward"},"id":1700,"loop_ref":425,"next":1701,"prev":1703,"to":427,"twin":1690,"wire_ref":null},{"edge":854,"face":425,"from":427,"geometry_use":{"pcurve":1701,"periodic_lift":[0,0],"sense":"Forward"},"id":1701,"loop_ref":425,"next":1702,"prev":1700,"to":430,"twin":1710,"wire_ref":null},{"edge":855,"face":425,"from":430,"geometry_use":{"pcurve":1702,"periodic_lift":[0,0],"sense":"Reverse"},"id":1702,"loop_ref":425,"next":1703,"prev":1701,"to":431,"twin":1713,"wire_ref":null},{"edge":856,"face":425,"from":431,"geometry_use":{"pcurve":1703,"periodic_lift":[0,0],"sense":"Reverse"},"id":1703,"loop_ref":425,"next":1700,"prev":1702,"to":423,"twin":1704,"wire_ref":null},{"edge":856,"face":426,"from":423,"geometry_use":{"pcurve":1704,"periodic_lift":[0,0],"sense":"Forward"},"id":1704,"loop_ref":426,"next":1705,"prev":1707,"to":431,"twin":1703,"wire_ref":null},{"edge":857,"face":426,"from":431,"geometry_use":{"pcurve":1705,"periodic_lift":[0,0],"sense":"Reverse"},"id":1705,"loop_ref":426,"next":1706,"prev":1704,"to":428,"twin":1712,"wire_ref":null},{"edge":853,"face":426,"from":428,"geometry_use":{"pcurve":1706,"periodic_lift":[0,0],"sense":"Reverse"},"id":1706,"loop_ref":426,"next":1707,"prev":1705,"to":420,"twin":1699,"wire_ref":null},{"edge":843,"face":426,"from":420,"geometry_use":{"pcurve":1707,"periodic_lift":[0,0],"sense":"Forward"},"id":1707,"loop_ref":426,"next":1704,"prev":1706,"to":423,"twin":1682,"wire_ref":null},{"edge":852,"face":427,"from":424,"geometry_use":{"pcurve":1708,"periodic_lift":[0,0],"sense":"Forward"},"id":1708,"loop_ref":427,"next":1709,"prev":1711,"to":429,"twin":1697,"wire_ref":null},{"edge":858,"face":427,"from":429,"geometry_use":{"pcurve":1709,"periodic_lift":[0,0],"sense":"Forward"},"id":1709,"loop_ref":427,"next":1710,"prev":1708,"to":430,"twin":1714,"wire_ref":null},{"edge":854,"face":427,"from":430,"geometry_use":{"pcurve":1710,"periodic_lift":[0,0],"sense":"Reverse"},"id":1710,"loop_ref":427,"next":1711,"prev":1709,"to":427,"twin":1701,"wire_ref":null},{"edge":859,"face":427,"from":427,"geometry_use":{"pcurve":1711,"periodic_lift":[0,0],"sense":"Reverse"},"id":1711,"loop_ref":427,"next":1708,"prev":1710,"to":424,"twin":1728,"wire_ref":null},{"edge":857,"face":428,"from":428,"geometry_use":{"pcurve":1712,"periodic_lift":[0,0],"sense":"Forward"},"id":1712,"loop_ref":428,"next":1713,"prev":1715,"to":431,"twin":1705,"wire_ref":null},{"edge":855,"face":428,"from":431,"geometry_use":{"pcurve":1713,"periodic_lift":[0,0],"sense":"Forward"},"id":1713,"loop_ref":428,"next":1714,"prev":1712,"to":430,"twin":1702,"wire_ref":null},{"edge":858,"face":428,"from":430,"geometry_use":{"pcurve":1714,"periodic_lift":[0,0],"sense":"Reverse"},"id":1714,"loop_ref":428,"next":1715,"prev":1713,"to":429,"twin":1709,"wire_ref":null},{"edge":851,"face":428,"from":429,"geometry_use":{"pcurve":1715,"periodic_lift":[0,0],"sense":"Reverse"},"id":1715,"loop_ref":428,"next":1712,"prev":1714,"to":428,"twin":1696,"wire_ref":null},{"edge":860,"face":429,"from":424,"geometry_use":{"pcurve":1716,"periodic_lift":[0,0],"sense":"Forward"},"id":1716,"loop_ref":429,"next":1717,"prev":1719,"to":432,"twin":1731,"wire_ref":null},{"edge":861,"face":429,"from":432,"geometry_use":{"pcurve":1717,"periodic_lift":[0,0],"sense":"Reverse"},"id":1717,"loop_ref":429,"next":1718,"prev":1716,"to":433,"twin":1735,"wire_ref":null},{"edge":862,"face":429,"from":433,"geometry_use":{"pcurve":1718,"periodic_lift":[0,0],"sense":"Reverse"},"id":1718,"loop_ref":429,"next":1719,"prev":1717,"to":425,"twin":1727,"wire_ref":null},{"edge":845,"face":429,"from":425,"geometry_use":{"pcurve":1719,"periodic_lift":[0,0],"sense":"Forward"},"id":1719,"loop_ref":429,"next":1716,"prev":1718,"to":424,"twin":1685,"wire_ref":null},{"edge":863,"face":430,"from":426,"geometry_use":{"pcurve":1720,"periodic_lift":[0,0],"sense":"Forward"},"id":1720,"loop_ref":430,"next":1721,"prev":1723,"to":434,"twin":1725,"wire_ref":null},{"edge":864,"face":430,"from":434,"geometry_use":{"pcurve":1721,"periodic_lift":[0,0],"sense":"Forward"},"id":1721,"loop_ref":430,"next":1722,"prev":1720,"to":435,"twin":1739,"wire_ref":null},{"edge":865,"face":430,"from":435,"geometry_use":{"pcurve":1722,"periodic_lift":[0,0],"sense":"Reverse"},"id":1722,"loop_ref":430,"next":1723,"prev":1721,"to":427,"twin":1729,"wire_ref":null},{"edge":848,"face":430,"from":427,"geometry_use":{"pcurve":1723,"periodic_lift":[0,0],"sense":"Reverse"},"id":1723,"loop_ref":430,"next":1720,"prev":1722,"to":426,"twin":1689,"wire_ref":null},{"edge":866,"face":431,"from":433,"geometry_use":{"pcurve":1724,"periodic_lift":[0,0],"sense":"Forward"},"id":1724,"loop_ref":431,"next":1725,"prev":1727,"to":434,"twin":1742,"wire_ref":null},{"edge":863,"face":431,"from":434,"geometry_use":{"pcurve":1725,"periodic_lift":[0,0],"sense":"Reverse"},"id":1725,"loop_ref":431,"next":1726,"prev":1724,"to":426,"twin":1720,"wire_ref":null},{"edge":850,"face":431,"from":426,"geometry_use":{"pcurve":1726,"periodic_lift":[0,0],"sense":"Reverse"},"id":1726,"loop_ref":431,"next":1727,"prev":1725,"to":425,"twin":1692,"wire_ref":null},{"edge":862,"face":431,"from":425,"geometry_use":{"pcurve":1727,"periodic_lift":[0,0],"sense":"Forward"},"id":1727,"loop_ref":431,"next":1724,"prev":1726,"to":433,"twin":1718,"wire_ref":null},{"edge":859,"face":432,"from":424,"geometry_use":{"pcurve":1728,"periodic_lift":[0,0],"sense":"Forward"},"id":1728,"loop_ref":432,"next":1729,"prev":1731,"to":427,"twin":1711,"wire_ref":null},{"edge":865,"face":432,"from":427,"geometry_use":{"pcurve":1729,"periodic_lift":[0,0],"sense":"Forward"},"id":1729,"loop_ref":432,"next":1730,"prev":1728,"to":435,"twin":1722,"wire_ref":null},{"edge":867,"face":432,"from":435,"geometry_use":{"pcurve":1730,"periodic_lift":[0,0],"sense":"Reverse"},"id":1730,"loop_ref":432,"next":1731,"prev":1729,"to":432,"twin":1755,"wire_ref":null},{"edge":860,"face":432,"from":432,"geometry_use":{"pcurve":1731,"periodic_lift":[0,0],"sense":"Reverse"},"id":1731,"loop_ref":432,"next":1728,"prev":1730,"to":424,"twin":1716,"wire_ref":null},{"edge":868,"face":433,"from":432,"geometry_use":{"pcurve":1732,"periodic_lift":[0,0],"sense":"Forward"},"id":1732,"loop_ref":433,"next":1733,"prev":1735,"to":436,"twin":1746,"wire_ref":null},{"edge":869,"face":433,"from":436,"geometry_use":{"pcurve":1733,"periodic_lift":[0,0],"sense":"Reverse"},"id":1733,"loop_ref":433,"next":1734,"prev":1732,"to":437,"twin":1767,"wire_ref":null},{"edge":870,"face":433,"from":437,"geometry_use":{"pcurve":1734,"periodic_lift":[0,0],"sense":"Reverse"},"id":1734,"loop_ref":433,"next":1735,"prev":1733,"to":433,"twin":1743,"wire_ref":null},{"edge":861,"face":433,"from":433,"geometry_use":{"pcurve":1735,"periodic_lift":[0,0],"sense":"Forward"},"id":1735,"loop_ref":433,"next":1732,"prev":1734,"to":432,"twin":1717,"wire_ref":null},{"edge":871,"face":434,"from":434,"geometry_use":{"pcurve":1736,"periodic_lift":[0,0],"sense":"Forward"},"id":1736,"loop_ref":434,"next":1737,"prev":1739,"to":438,"twin":1741,"wire_ref":null},{"edge":872,"face":434,"from":438,"geometry_use":{"pcurve":1737,"periodic_lift":[0,0],"sense":"Forward"},"id":1737,"loop_ref":434,"next":1738,"prev":1736,"to":439,"twin":1771,"wire_ref":null},{"edge":873,"face":434,"from":439,"geometry_use":{"pcurve":1738,"periodic_lift":[0,0],"sense":"Reverse"},"id":1738,"loop_ref":434,"next":1739,"prev":1737,"to":435,"twin":1748,"wire_ref":null},{"edge":864,"face":434,"from":435,"geometry_use":{"pcurve":1739,"periodic_lift":[0,0],"sense":"Reverse"},"id":1739,"loop_ref":434,"next":1736,"prev":1738,"to":434,"twin":1721,"wire_ref":null},{"edge":874,"face":435,"from":437,"geometry_use":{"pcurve":1740,"periodic_lift":[0,0],"sense":"Forward"},"id":1740,"loop_ref":435,"next":1741,"prev":1743,"to":438,"twin":1774,"wire_ref":null},{"edge":871,"face":435,"from":438,"geometry_use":{"pcurve":1741,"periodic_lift":[0,0],"sense":"Reverse"},"id":1741,"loop_ref":435,"next":1742,"prev":1740,"to":434,"twin":1736,"wire_ref":null},{"edge":866,"face":435,"from":434,"geometry_use":{"pcurve":1742,"periodic_lift":[0,0],"sense":"Reverse"},"id":1742,"loop_ref":435,"next":1743,"prev":1741,"to":433,"twin":1724,"wire_ref":null},{"edge":870,"face":435,"from":433,"geometry_use":{"pcurve":1743,"periodic_lift":[0,0],"sense":"Forward"},"id":1743,"loop_ref":435,"next":1740,"prev":1742,"to":437,"twin":1734,"wire_ref":null},{"edge":875,"face":436,"from":440,"geometry_use":{"pcurve":1744,"periodic_lift":[0,0],"sense":"Forward"},"id":1744,"loop_ref":436,"next":1745,"prev":1747,"to":441,"twin":1763,"wire_ref":null},{"edge":876,"face":436,"from":441,"geometry_use":{"pcurve":1745,"periodic_lift":[0,0],"sense":"Reverse"},"id":1745,"loop_ref":436,"next":1746,"prev":1744,"to":436,"twin":1756,"wire_ref":null},{"edge":868,"face":436,"from":436,"geometry_use":{"pcurve":1746,"periodic_lift":[0,0],"sense":"Reverse"},"id":1746,"loop_ref":436,"next":1747,"prev":1745,"to":432,"twin":1732,"wire_ref":null},{"edge":877,"face":436,"from":432,"geometry_use":{"pcurve":1747,"periodic_lift":[0,0],"sense":"Forward"},"id":1747,"loop_ref":436,"next":1744,"prev":1746,"to":440,"twin":1754,"wire_ref":null},{"edge":873,"face":437,"from":435,"geometry_use":{"pcurve":1748,"periodic_lift":[0,0],"sense":"Forward"},"id":1748,"loop_ref":437,"next":1749,"prev":1751,"to":439,"twin":1738,"wire_ref":null},{"edge":878,"face":437,"from":439,"geometry_use":{"pcurve":1749,"periodic_lift":[0,0],"sense":"Forward"},"id":1749,"loop_ref":437,"next":1750,"prev":1748,"to":442,"twin":1758,"wire_ref":null},{"edge":879,"face":437,"from":442,"geometry_use":{"pcurve":1750,"periodic_lift":[0,0],"sense":"Reverse"},"id":1750,"loop_ref":437,"next":1751,"prev":1749,"to":443,"twin":1761,"wire_ref":null},{"edge":880,"face":437,"from":443,"geometry_use":{"pcurve":1751,"periodic_lift":[0,0],"sense":"Reverse"},"id":1751,"loop_ref":437,"next":1748,"prev":1750,"to":435,"twin":1752,"wire_ref":null},{"edge":880,"face":438,"from":435,"geometry_use":{"pcurve":1752,"periodic_lift":[0,0],"sense":"Forward"},"id":1752,"loop_ref":438,"next":1753,"prev":1755,"to":443,"twin":1751,"wire_ref":null},{"edge":881,"face":438,"from":443,"geometry_use":{"pcurve":1753,"periodic_lift":[0,0],"sense":"Reverse"},"id":1753,"loop_ref":438,"next":1754,"prev":1752,"to":440,"twin":1760,"wire_ref":null},{"edge":877,"face":438,"from":440,"geometry_use":{"pcurve":1754,"periodic_lift":[0,0],"sense":"Reverse"},"id":1754,"loop_ref":438,"next":1755,"prev":1753,"to":432,"twin":1747,"wire_ref":null},{"edge":867,"face":438,"from":432,"geometry_use":{"pcurve":1755,"periodic_lift":[0,0],"sense":"Forward"},"id":1755,"loop_ref":438,"next":1752,"prev":1754,"to":435,"twin":1730,"wire_ref":null},{"edge":876,"face":439,"from":436,"geometry_use":{"pcurve":1756,"periodic_lift":[0,0],"sense":"Forward"},"id":1756,"loop_ref":439,"next":1757,"prev":1759,"to":441,"twin":1745,"wire_ref":null},{"edge":882,"face":439,"from":441,"geometry_use":{"pcurve":1757,"periodic_lift":[0,0],"sense":"Forward"},"id":1757,"loop_ref":439,"next":1758,"prev":1756,"to":442,"twin":1762,"wire_ref":null},{"edge":878,"face":439,"from":442,"geometry_use":{"pcurve":1758,"periodic_lift":[0,0],"sense":"Reverse"},"id":1758,"loop_ref":439,"next":1759,"prev":1757,"to":439,"twin":1749,"wire_ref":null},{"edge":883,"face":439,"from":439,"geometry_use":{"pcurve":1759,"periodic_lift":[0,0],"sense":"Reverse"},"id":1759,"loop_ref":439,"next":1756,"prev":1758,"to":436,"twin":1776,"wire_ref":null},{"edge":881,"face":440,"from":440,"geometry_use":{"pcurve":1760,"periodic_lift":[0,0],"sense":"Forward"},"id":1760,"loop_ref":440,"next":1761,"prev":1763,"to":443,"twin":1753,"wire_ref":null},{"edge":879,"face":440,"from":443,"geometry_use":{"pcurve":1761,"periodic_lift":[0,0],"sense":"Forward"},"id":1761,"loop_ref":440,"next":1762,"prev":1760,"to":442,"twin":1750,"wire_ref":null},{"edge":882,"face":440,"from":442,"geometry_use":{"pcurve":1762,"periodic_lift":[0,0],"sense":"Reverse"},"id":1762,"loop_ref":440,"next":1763,"prev":1761,"to":441,"twin":1757,"wire_ref":null},{"edge":875,"face":440,"from":441,"geometry_use":{"pcurve":1763,"periodic_lift":[0,0],"sense":"Reverse"},"id":1763,"loop_ref":440,"next":1760,"prev":1762,"to":440,"twin":1744,"wire_ref":null},{"edge":884,"face":441,"from":436,"geometry_use":{"pcurve":1764,"periodic_lift":[0,0],"sense":"Forward"},"id":1764,"loop_ref":441,"next":1765,"prev":1767,"to":444,"twin":1779,"wire_ref":null},{"edge":885,"face":441,"from":444,"geometry_use":{"pcurve":1765,"periodic_lift":[0,0],"sense":"Reverse"},"id":1765,"loop_ref":441,"next":1766,"prev":1764,"to":445,"twin":1783,"wire_ref":null},{"edge":886,"face":441,"from":445,"geometry_use":{"pcurve":1766,"periodic_lift":[0,0],"sense":"Reverse"},"id":1766,"loop_ref":441,"next":1767,"prev":1765,"to":437,"twin":1775,"wire_ref":null},{"edge":869,"face":441,"from":437,"geometry_use":{"pcurve":1767,"periodic_lift":[0,0],"sense":"Forward"},"id":1767,"loop_ref":441,"next":1764,"prev":1766,"to":436,"twin":1733,"wire_ref":null},{"edge":887,"face":442,"from":438,"geometry_use":{"pcurve":1768,"periodic_lift":[0,0],"sense":"Forward"},"id":1768,"loop_ref":442,"next":1769,"prev":1771,"to":446,"twin":1773,"wire_ref":null},{"edge":888,"face":442,"from":446,"geometry_use":{"pcurve":1769,"periodic_lift":[0,0],"sense":"Forward"},"id":1769,"loop_ref":442,"next":1770,"prev":1768,"to":447,"twin":1787,"wire_ref":null},{"edge":889,"face":442,"from":447,"geometry_use":{"pcurve":1770,"periodic_lift":[0,0],"sense":"Reverse"},"id":1770,"loop_ref":442,"next":1771,"prev":1769,"to":439,"twin":1777,"wire_ref":null},{"edge":872,"face":442,"from":439,"geometry_use":{"pcurve":1771,"periodic_lift":[0,0],"sense":"Reverse"},"id":1771,"loop_ref":442,"next":1768,"prev":1770,"to":438,"twin":1737,"wire_ref":null},{"edge":890,"face":443,"from":445,"geometry_use":{"pcurve":1772,"periodic_lift":[0,0],"sense":"Forward"},"id":1772,"loop_ref":443,"next":1773,"prev":1775,"to":446,"twin":1790,"wire_ref":null},{"edge":887,"face":443,"from":446,"geometry_use":{"pcurve":1773,"periodic_lift":[0,0],"sense":"Reverse"},"id":1773,"loop_ref":443,"next":1774,"prev":1772,"to":438,"twin":1768,"wire_ref":null},{"edge":874,"face":443,"from":438,"geometry_use":{"pcurve":1774,"periodic_lift":[0,0],"sense":"Reverse"},"id":1774,"loop_ref":443,"next":1775,"prev":1773,"to":437,"twin":1740,"wire_ref":null},{"edge":886,"face":443,"from":437,"geometry_use":{"pcurve":1775,"periodic_lift":[0,0],"sense":"Forward"},"id":1775,"loop_ref":443,"next":1772,"prev":1774,"to":445,"twin":1766,"wire_ref":null},{"edge":883,"face":444,"from":436,"geometry_use":{"pcurve":1776,"periodic_lift":[0,0],"sense":"Forward"},"id":1776,"loop_ref":444,"next":1777,"prev":1779,"to":439,"twin":1759,"wire_ref":null},{"edge":889,"face":444,"from":439,"geometry_use":{"pcurve":1777,"periodic_lift":[0,0],"sense":"Forward"},"id":1777,"loop_ref":444,"next":1778,"prev":1776,"to":447,"twin":1770,"wire_ref":null},{"edge":891,"face":444,"from":447,"geometry_use":{"pcurve":1778,"periodic_lift":[0,0],"sense":"Reverse"},"id":1778,"loop_ref":444,"next":1779,"prev":1777,"to":444,"twin":1803,"wire_ref":null},{"edge":884,"face":444,"from":444,"geometry_use":{"pcurve":1779,"periodic_lift":[0,0],"sense":"Reverse"},"id":1779,"loop_ref":444,"next":1776,"prev":1778,"to":436,"twin":1764,"wire_ref":null},{"edge":892,"face":445,"from":444,"geometry_use":{"pcurve":1780,"periodic_lift":[0,0],"sense":"Forward"},"id":1780,"loop_ref":445,"next":1781,"prev":1783,"to":448,"twin":1794,"wire_ref":null},{"edge":893,"face":445,"from":448,"geometry_use":{"pcurve":1781,"periodic_lift":[0,0],"sense":"Reverse"},"id":1781,"loop_ref":445,"next":1782,"prev":1780,"to":449,"twin":1815,"wire_ref":null},{"edge":894,"face":445,"from":449,"geometry_use":{"pcurve":1782,"periodic_lift":[0,0],"sense":"Reverse"},"id":1782,"loop_ref":445,"next":1783,"prev":1781,"to":445,"twin":1791,"wire_ref":null},{"edge":885,"face":445,"from":445,"geometry_use":{"pcurve":1783,"periodic_lift":[0,0],"sense":"Forward"},"id":1783,"loop_ref":445,"next":1780,"prev":1782,"to":444,"twin":1765,"wire_ref":null},{"edge":895,"face":446,"from":446,"geometry_use":{"pcurve":1784,"periodic_lift":[0,0],"sense":"Forward"},"id":1784,"loop_ref":446,"next":1785,"prev":1787,"to":450,"twin":1789,"wire_ref":null},{"edge":896,"face":446,"from":450,"geometry_use":{"pcurve":1785,"periodic_lift":[0,0],"sense":"Forward"},"id":1785,"loop_ref":446,"next":1786,"prev":1784,"to":451,"twin":1819,"wire_ref":null},{"edge":897,"face":446,"from":451,"geometry_use":{"pcurve":1786,"periodic_lift":[0,0],"sense":"Reverse"},"id":1786,"loop_ref":446,"next":1787,"prev":1785,"to":447,"twin":1796,"wire_ref":null},{"edge":888,"face":446,"from":447,"geometry_use":{"pcurve":1787,"periodic_lift":[0,0],"sense":"Reverse"},"id":1787,"loop_ref":446,"next":1784,"prev":1786,"to":446,"twin":1769,"wire_ref":null},{"edge":898,"face":447,"from":449,"geometry_use":{"pcurve":1788,"periodic_lift":[0,0],"sense":"Forward"},"id":1788,"loop_ref":447,"next":1789,"prev":1791,"to":450,"twin":1822,"wire_ref":null},{"edge":895,"face":447,"from":450,"geometry_use":{"pcurve":1789,"periodic_lift":[0,0],"sense":"Reverse"},"id":1789,"loop_ref":447,"next":1790,"prev":1788,"to":446,"twin":1784,"wire_ref":null},{"edge":890,"face":447,"from":446,"geometry_use":{"pcurve":1790,"periodic_lift":[0,0],"sense":"Reverse"},"id":1790,"loop_ref":447,"next":1791,"prev":1789,"to":445,"twin":1772,"wire_ref":null},{"edge":894,"face":447,"from":445,"geometry_use":{"pcurve":1791,"periodic_lift":[0,0],"sense":"Forward"},"id":1791,"loop_ref":447,"next":1788,"prev":1790,"to":449,"twin":1782,"wire_ref":null},{"edge":899,"face":448,"from":452,"geometry_use":{"pcurve":1792,"periodic_lift":[0,0],"sense":"Forward"},"id":1792,"loop_ref":448,"next":1793,"prev":1795,"to":453,"twin":1811,"wire_ref":null},{"edge":900,"face":448,"from":453,"geometry_use":{"pcurve":1793,"periodic_lift":[0,0],"sense":"Reverse"},"id":1793,"loop_ref":448,"next":1794,"prev":1792,"to":448,"twin":1804,"wire_ref":null},{"edge":892,"face":448,"from":448,"geometry_use":{"pcurve":1794,"periodic_lift":[0,0],"sense":"Reverse"},"id":1794,"loop_ref":448,"next":1795,"prev":1793,"to":444,"twin":1780,"wire_ref":null},{"edge":901,"face":448,"from":444,"geometry_use":{"pcurve":1795,"periodic_lift":[0,0],"sense":"Forward"},"id":1795,"loop_ref":448,"next":1792,"prev":1794,"to":452,"twin":1802,"wire_ref":null},{"edge":897,"face":449,"from":447,"geometry_use":{"pcurve":1796,"periodic_lift":[0,0],"sense":"Forward"},"id":1796,"loop_ref":449,"next":1797,"prev":1799,"to":451,"twin":1786,"wire_ref":null},{"edge":902,"face":449,"from":451,"geometry_use":{"pcurve":1797,"periodic_lift":[0,0],"sense":"Forward"},"id":1797,"loop_ref":449,"next":1798,"prev":1796,"to":454,"twin":1806,"wire_ref":null},{"edge":903,"face":449,"from":454,"geometry_use":{"pcurve":1798,"periodic_lift":[0,0],"sense":"Reverse"},"id":1798,"loop_ref":449,"next":1799,"prev":1797,"to":455,"twin":1809,"wire_ref":null},{"edge":904,"face":449,"from":455,"geometry_use":{"pcurve":1799,"periodic_lift":[0,0],"sense":"Reverse"},"id":1799,"loop_ref":449,"next":1796,"prev":1798,"to":447,"twin":1800,"wire_ref":null},{"edge":904,"face":450,"from":447,"geometry_use":{"pcurve":1800,"periodic_lift":[0,0],"sense":"Forward"},"id":1800,"loop_ref":450,"next":1801,"prev":1803,"to":455,"twin":1799,"wire_ref":null},{"edge":905,"face":450,"from":455,"geometry_use":{"pcurve":1801,"periodic_lift":[0,0],"sense":"Reverse"},"id":1801,"loop_ref":450,"next":1802,"prev":1800,"to":452,"twin":1808,"wire_ref":null},{"edge":901,"face":450,"from":452,"geometry_use":{"pcurve":1802,"periodic_lift":[0,0],"sense":"Reverse"},"id":1802,"loop_ref":450,"next":1803,"prev":1801,"to":444,"twin":1795,"wire_ref":null},{"edge":891,"face":450,"from":444,"geometry_use":{"pcurve":1803,"periodic_lift":[0,0],"sense":"Forward"},"id":1803,"loop_ref":450,"next":1800,"prev":1802,"to":447,"twin":1778,"wire_ref":null},{"edge":900,"face":451,"from":448,"geometry_use":{"pcurve":1804,"periodic_lift":[0,0],"sense":"Forward"},"id":1804,"loop_ref":451,"next":1805,"prev":1807,"to":453,"twin":1793,"wire_ref":null},{"edge":906,"face":451,"from":453,"geometry_use":{"pcurve":1805,"periodic_lift":[0,0],"sense":"Forward"},"id":1805,"loop_ref":451,"next":1806,"prev":1804,"to":454,"twin":1810,"wire_ref":null},{"edge":902,"face":451,"from":454,"geometry_use":{"pcurve":1806,"periodic_lift":[0,0],"sense":"Reverse"},"id":1806,"loop_ref":451,"next":1807,"prev":1805,"to":451,"twin":1797,"wire_ref":null},{"edge":907,"face":451,"from":451,"geometry_use":{"pcurve":1807,"periodic_lift":[0,0],"sense":"Reverse"},"id":1807,"loop_ref":451,"next":1804,"prev":1806,"to":448,"twin":1824,"wire_ref":null},{"edge":905,"face":452,"from":452,"geometry_use":{"pcurve":1808,"periodic_lift":[0,0],"sense":"Forward"},"id":1808,"loop_ref":452,"next":1809,"prev":1811,"to":455,"twin":1801,"wire_ref":null},{"edge":903,"face":452,"from":455,"geometry_use":{"pcurve":1809,"periodic_lift":[0,0],"sense":"Forward"},"id":1809,"loop_ref":452,"next":1810,"prev":1808,"to":454,"twin":1798,"wire_ref":null},{"edge":906,"face":452,"from":454,"geometry_use":{"pcurve":1810,"periodic_lift":[0,0],"sense":"Reverse"},"id":1810,"loop_ref":452,"next":1811,"prev":1809,"to":453,"twin":1805,"wire_ref":null},{"edge":899,"face":452,"from":453,"geometry_use":{"pcurve":1811,"periodic_lift":[0,0],"sense":"Reverse"},"id":1811,"loop_ref":452,"next":1808,"prev":1810,"to":452,"twin":1792,"wire_ref":null},{"edge":908,"face":453,"from":448,"geometry_use":{"pcurve":1812,"periodic_lift":[0,0],"sense":"Forward"},"id":1812,"loop_ref":453,"next":1813,"prev":1815,"to":456,"twin":1827,"wire_ref":null},{"edge":909,"face":453,"from":456,"geometry_use":{"pcurve":1813,"periodic_lift":[0,0],"sense":"Reverse"},"id":1813,"loop_ref":453,"next":1814,"prev":1812,"to":457,"twin":1831,"wire_ref":null},{"edge":910,"face":453,"from":457,"geometry_use":{"pcurve":1814,"periodic_lift":[0,0],"sense":"Reverse"},"id":1814,"loop_ref":453,"next":1815,"prev":1813,"to":449,"twin":1823,"wire_ref":null},{"edge":893,"face":453,"from":449,"geometry_use":{"pcurve":1815,"periodic_lift":[0,0],"sense":"Forward"},"id":1815,"loop_ref":453,"next":1812,"prev":1814,"to":448,"twin":1781,"wire_ref":null},{"edge":911,"face":454,"from":450,"geometry_use":{"pcurve":1816,"periodic_lift":[0,0],"sense":"Forward"},"id":1816,"loop_ref":454,"next":1817,"prev":1819,"to":458,"twin":1821,"wire_ref":null},{"edge":912,"face":454,"from":458,"geometry_use":{"pcurve":1817,"periodic_lift":[0,0],"sense":"Forward"},"id":1817,"loop_ref":454,"next":1818,"prev":1816,"to":459,"twin":1835,"wire_ref":null},{"edge":913,"face":454,"from":459,"geometry_use":{"pcurve":1818,"periodic_lift":[0,0],"sense":"Reverse"},"id":1818,"loop_ref":454,"next":1819,"prev":1817,"to":451,"twin":1825,"wire_ref":null},{"edge":896,"face":454,"from":451,"geometry_use":{"pcurve":1819,"periodic_lift":[0,0],"sense":"Reverse"},"id":1819,"loop_ref":454,"next":1816,"prev":1818,"to":450,"twin":1785,"wire_ref":null},{"edge":914,"face":455,"from":457,"geometry_use":{"pcurve":1820,"periodic_lift":[0,0],"sense":"Forward"},"id":1820,"loop_ref":455,"next":1821,"prev":1823,"to":458,"twin":1838,"wire_ref":null},{"edge":911,"face":455,"from":458,"geometry_use":{"pcurve":1821,"periodic_lift":[0,0],"sense":"Reverse"},"id":1821,"loop_ref":455,"next":1822,"prev":1820,"to":450,"twin":1816,"wire_ref":null},{"edge":898,"face":455,"from":450,"geometry_use":{"pcurve":1822,"periodic_lift":[0,0],"sense":"Reverse"},"id":1822,"loop_ref":455,"next":1823,"prev":1821,"to":449,"twin":1788,"wire_ref":null},{"edge":910,"face":455,"from":449,"geometry_use":{"pcurve":1823,"periodic_lift":[0,0],"sense":"Forward"},"id":1823,"loop_ref":455,"next":1820,"prev":1822,"to":457,"twin":1814,"wire_ref":null},{"edge":907,"face":456,"from":448,"geometry_use":{"pcurve":1824,"periodic_lift":[0,0],"sense":"Forward"},"id":1824,"loop_ref":456,"next":1825,"prev":1827,"to":451,"twin":1807,"wire_ref":null},{"edge":913,"face":456,"from":451,"geometry_use":{"pcurve":1825,"periodic_lift":[0,0],"sense":"Forward"},"id":1825,"loop_ref":456,"next":1826,"prev":1824,"to":459,"twin":1818,"wire_ref":null},{"edge":915,"face":456,"from":459,"geometry_use":{"pcurve":1826,"periodic_lift":[0,0],"sense":"Reverse"},"id":1826,"loop_ref":456,"next":1827,"prev":1825,"to":456,"twin":1851,"wire_ref":null},{"edge":908,"face":456,"from":456,"geometry_use":{"pcurve":1827,"periodic_lift":[0,0],"sense":"Reverse"},"id":1827,"loop_ref":456,"next":1824,"prev":1826,"to":448,"twin":1812,"wire_ref":null},{"edge":916,"face":457,"from":456,"geometry_use":{"pcurve":1828,"periodic_lift":[0,0],"sense":"Forward"},"id":1828,"loop_ref":457,"next":1829,"prev":1831,"to":460,"twin":1842,"wire_ref":null},{"edge":917,"face":457,"from":460,"geometry_use":{"pcurve":1829,"periodic_lift":[0,0],"sense":"Reverse"},"id":1829,"loop_ref":457,"next":1830,"prev":1828,"to":461,"twin":1863,"wire_ref":null},{"edge":918,"face":457,"from":461,"geometry_use":{"pcurve":1830,"periodic_lift":[0,0],"sense":"Reverse"},"id":1830,"loop_ref":457,"next":1831,"prev":1829,"to":457,"twin":1839,"wire_ref":null},{"edge":909,"face":457,"from":457,"geometry_use":{"pcurve":1831,"periodic_lift":[0,0],"sense":"Forward"},"id":1831,"loop_ref":457,"next":1828,"prev":1830,"to":456,"twin":1813,"wire_ref":null},{"edge":919,"face":458,"from":458,"geometry_use":{"pcurve":1832,"periodic_lift":[0,0],"sense":"Forward"},"id":1832,"loop_ref":458,"next":1833,"prev":1835,"to":462,"twin":1837,"wire_ref":null},{"edge":920,"face":458,"from":462,"geometry_use":{"pcurve":1833,"periodic_lift":[0,0],"sense":"Forward"},"id":1833,"loop_ref":458,"next":1834,"prev":1832,"to":463,"twin":1867,"wire_ref":null},{"edge":921,"face":458,"from":463,"geometry_use":{"pcurve":1834,"periodic_lift":[0,0],"sense":"Reverse"},"id":1834,"loop_ref":458,"next":1835,"prev":1833,"to":459,"twin":1844,"wire_ref":null},{"edge":912,"face":458,"from":459,"geometry_use":{"pcurve":1835,"periodic_lift":[0,0],"sense":"Reverse"},"id":1835,"loop_ref":458,"next":1832,"prev":1834,"to":458,"twin":1817,"wire_ref":null},{"edge":922,"face":459,"from":461,"geometry_use":{"pcurve":1836,"periodic_lift":[0,0],"sense":"Forward"},"id":1836,"loop_ref":459,"next":1837,"prev":1839,"to":462,"twin":1870,"wire_ref":null},{"edge":919,"face":459,"from":462,"geometry_use":{"pcurve":1837,"periodic_lift":[0,0],"sense":"Reverse"},"id":1837,"loop_ref":459,"next":1838,"prev":1836,"to":458,"twin":1832,"wire_ref":null},{"edge":914,"face":459,"from":458,"geometry_use":{"pcurve":1838,"periodic_lift":[0,0],"sense":"Reverse"},"id":1838,"loop_ref":459,"next":1839,"prev":1837,"to":457,"twin":1820,"wire_ref":null},{"edge":918,"face":459,"from":457,"geometry_use":{"pcurve":1839,"periodic_lift":[0,0],"sense":"Forward"},"id":1839,"loop_ref":459,"next":1836,"prev":1838,"to":461,"twin":1830,"wire_ref":null},{"edge":923,"face":460,"from":464,"geometry_use":{"pcurve":1840,"periodic_lift":[0,0],"sense":"Forward"},"id":1840,"loop_ref":460,"next":1841,"prev":1843,"to":465,"twin":1859,"wire_ref":null},{"edge":924,"face":460,"from":465,"geometry_use":{"pcurve":1841,"periodic_lift":[0,0],"sense":"Reverse"},"id":1841,"loop_ref":460,"next":1842,"prev":1840,"to":460,"twin":1852,"wire_ref":null},{"edge":916,"face":460,"from":460,"geometry_use":{"pcurve":1842,"periodic_lift":[0,0],"sense":"Reverse"},"id":1842,"loop_ref":460,"next":1843,"prev":1841,"to":456,"twin":1828,"wire_ref":null},{"edge":925,"face":460,"from":456,"geometry_use":{"pcurve":1843,"periodic_lift":[0,0],"sense":"Forward"},"id":1843,"loop_ref":460,"next":1840,"prev":1842,"to":464,"twin":1850,"wire_ref":null},{"edge":921,"face":461,"from":459,"geometry_use":{"pcurve":1844,"periodic_lift":[0,0],"sense":"Forward"},"id":1844,"loop_ref":461,"next":1845,"prev":1847,"to":463,"twin":1834,"wire_ref":null},{"edge":926,"face":461,"from":463,"geometry_use":{"pcurve":1845,"periodic_lift":[0,0],"sense":"Forward"},"id":1845,"loop_ref":461,"next":1846,"prev":1844,"to":466,"twin":1854,"wire_ref":null},{"edge":927,"face":461,"from":466,"geometry_use":{"pcurve":1846,"periodic_lift":[0,0],"sense":"Reverse"},"id":1846,"loop_ref":461,"next":1847,"prev":1845,"to":467,"twin":1857,"wire_ref":null},{"edge":928,"face":461,"from":467,"geometry_use":{"pcurve":1847,"periodic_lift":[0,0],"sense":"Reverse"},"id":1847,"loop_ref":461,"next":1844,"prev":1846,"to":459,"twin":1848,"wire_ref":null},{"edge":928,"face":462,"from":459,"geometry_use":{"pcurve":1848,"periodic_lift":[0,0],"sense":"Forward"},"id":1848,"loop_ref":462,"next":1849,"prev":1851,"to":467,"twin":1847,"wire_ref":null},{"edge":929,"face":462,"from":467,"geometry_use":{"pcurve":1849,"periodic_lift":[0,0],"sense":"Reverse"},"id":1849,"loop_ref":462,"next":1850,"prev":1848,"to":464,"twin":1856,"wire_ref":null},{"edge":925,"face":462,"from":464,"geometry_use":{"pcurve":1850,"periodic_lift":[0,0],"sense":"Reverse"},"id":1850,"loop_ref":462,"next":1851,"prev":1849,"to":456,"twin":1843,"wire_ref":null},{"edge":915,"face":462,"from":456,"geometry_use":{"pcurve":1851,"periodic_lift":[0,0],"sense":"Forward"},"id":1851,"loop_ref":462,"next":1848,"prev":1850,"to":459,"twin":1826,"wire_ref":null},{"edge":924,"face":463,"from":460,"geometry_use":{"pcurve":1852,"periodic_lift":[0,0],"sense":"Forward"},"id":1852,"loop_ref":463,"next":1853,"prev":1855,"to":465,"twin":1841,"wire_ref":null},{"edge":930,"face":463,"from":465,"geometry_use":{"pcurve":1853,"periodic_lift":[0,0],"sense":"Forward"},"id":1853,"loop_ref":463,"next":1854,"prev":1852,"to":466,"twin":1858,"wire_ref":null},{"edge":926,"face":463,"from":466,"geometry_use":{"pcurve":1854,"periodic_lift":[0,0],"sense":"Reverse"},"id":1854,"loop_ref":463,"next":1855,"prev":1853,"to":463,"twin":1845,"wire_ref":null},{"edge":931,"face":463,"from":463,"geometry_use":{"pcurve":1855,"periodic_lift":[0,0],"sense":"Reverse"},"id":1855,"loop_ref":463,"next":1852,"prev":1854,"to":460,"twin":1872,"wire_ref":null},{"edge":929,"face":464,"from":464,"geometry_use":{"pcurve":1856,"periodic_lift":[0,0],"sense":"Forward"},"id":1856,"loop_ref":464,"next":1857,"prev":1859,"to":467,"twin":1849,"wire_ref":null},{"edge":927,"face":464,"from":467,"geometry_use":{"pcurve":1857,"periodic_lift":[0,0],"sense":"Forward"},"id":1857,"loop_ref":464,"next":1858,"prev":1856,"to":466,"twin":1846,"wire_ref":null},{"edge":930,"face":464,"from":466,"geometry_use":{"pcurve":1858,"periodic_lift":[0,0],"sense":"Reverse"},"id":1858,"loop_ref":464,"next":1859,"prev":1857,"to":465,"twin":1853,"wire_ref":null},{"edge":923,"face":464,"from":465,"geometry_use":{"pcurve":1859,"periodic_lift":[0,0],"sense":"Reverse"},"id":1859,"loop_ref":464,"next":1856,"prev":1858,"to":464,"twin":1840,"wire_ref":null},{"edge":932,"face":465,"from":460,"geometry_use":{"pcurve":1860,"periodic_lift":[0,0],"sense":"Forward"},"id":1860,"loop_ref":465,"next":1861,"prev":1863,"to":468,"twin":1875,"wire_ref":null},{"edge":933,"face":465,"from":468,"geometry_use":{"pcurve":1861,"periodic_lift":[0,0],"sense":"Reverse"},"id":1861,"loop_ref":465,"next":1862,"prev":1860,"to":469,"twin":1879,"wire_ref":null},{"edge":934,"face":465,"from":469,"geometry_use":{"pcurve":1862,"periodic_lift":[0,0],"sense":"Reverse"},"id":1862,"loop_ref":465,"next":1863,"prev":1861,"to":461,"twin":1871,"wire_ref":null},{"edge":917,"face":465,"from":461,"geometry_use":{"pcurve":1863,"periodic_lift":[0,0],"sense":"Forward"},"id":1863,"loop_ref":465,"next":1860,"prev":1862,"to":460,"twin":1829,"wire_ref":null},{"edge":935,"face":466,"from":462,"geometry_use":{"pcurve":1864,"periodic_lift":[0,0],"sense":"Forward"},"id":1864,"loop_ref":466,"next":1865,"prev":1867,"to":470,"twin":1869,"wire_ref":null},{"edge":936,"face":466,"from":470,"geometry_use":{"pcurve":1865,"periodic_lift":[0,0],"sense":"Forward"},"id":1865,"loop_ref":466,"next":1866,"prev":1864,"to":471,"twin":1883,"wire_ref":null},{"edge":937,"face":466,"from":471,"geometry_use":{"pcurve":1866,"periodic_lift":[0,0],"sense":"Reverse"},"id":1866,"loop_ref":466,"next":1867,"prev":1865,"to":463,"twin":1873,"wire_ref":null},{"edge":920,"face":466,"from":463,"geometry_use":{"pcurve":1867,"periodic_lift":[0,0],"sense":"Reverse"},"id":1867,"loop_ref":466,"next":1864,"prev":1866,"to":462,"twin":1833,"wire_ref":null},{"edge":938,"face":467,"from":469,"geometry_use":{"pcurve":1868,"periodic_lift":[0,0],"sense":"Forward"},"id":1868,"loop_ref":467,"next":1869,"prev":1871,"to":470,"twin":1886,"wire_ref":null},{"edge":935,"face":467,"from":470,"geometry_use":{"pcurve":1869,"periodic_lift":[0,0],"sense":"Reverse"},"id":1869,"loop_ref":467,"next":1870,"prev":1868,"to":462,"twin":1864,"wire_ref":null},{"edge":922,"face":467,"from":462,"geometry_use":{"pcurve":1870,"periodic_lift":[0,0],"sense":"Reverse"},"id":1870,"loop_ref":467,"next":1871,"prev":1869,"to":461,"twin":1836,"wire_ref":null},{"edge":934,"face":467,"from":461,"geometry_use":{"pcurve":1871,"periodic_lift":[0,0],"sense":"Forward"},"id":1871,"loop_ref":467,"next":1868,"prev":1870,"to":469,"twin":1862,"wire_ref":null},{"edge":931,"face":468,"from":460,"geometry_use":{"pcurve":1872,"periodic_lift":[0,0],"sense":"Forward"},"id":1872,"loop_ref":468,"next":1873,"prev":1875,"to":463,"twin":1855,"wire_ref":null},{"edge":937,"face":468,"from":463,"geometry_use":{"pcurve":1873,"periodic_lift":[0,0],"sense":"Forward"},"id":1873,"loop_ref":468,"next":1874,"prev":1872,"to":471,"twin":1866,"wire_ref":null},{"edge":939,"face":468,"from":471,"geometry_use":{"pcurve":1874,"periodic_lift":[0,0],"sense":"Reverse"},"id":1874,"loop_ref":468,"next":1875,"prev":1873,"to":468,"twin":1899,"wire_ref":null},{"edge":932,"face":468,"from":468,"geometry_use":{"pcurve":1875,"periodic_lift":[0,0],"sense":"Reverse"},"id":1875,"loop_ref":468,"next":1872,"prev":1874,"to":460,"twin":1860,"wire_ref":null},{"edge":940,"face":469,"from":468,"geometry_use":{"pcurve":1876,"periodic_lift":[0,0],"sense":"Forward"},"id":1876,"loop_ref":469,"next":1877,"prev":1879,"to":472,"twin":1890,"wire_ref":null},{"edge":941,"face":469,"from":472,"geometry_use":{"pcurve":1877,"periodic_lift":[0,0],"sense":"Reverse"},"id":1877,"loop_ref":469,"next":1878,"prev":1876,"to":473,"twin":1911,"wire_ref":null},{"edge":942,"face":469,"from":473,"geometry_use":{"pcurve":1878,"periodic_lift":[0,0],"sense":"Reverse"},"id":1878,"loop_ref":469,"next":1879,"prev":1877,"to":469,"twin":1887,"wire_ref":null},{"edge":933,"face":469,"from":469,"geometry_use":{"pcurve":1879,"periodic_lift":[0,0],"sense":"Forward"},"id":1879,"loop_ref":469,"next":1876,"prev":1878,"to":468,"twin":1861,"wire_ref":null},{"edge":943,"face":470,"from":470,"geometry_use":{"pcurve":1880,"periodic_lift":[0,0],"sense":"Forward"},"id":1880,"loop_ref":470,"next":1881,"prev":1883,"to":474,"twin":1885,"wire_ref":null},{"edge":944,"face":470,"from":474,"geometry_use":{"pcurve":1881,"periodic_lift":[0,0],"sense":"Forward"},"id":1881,"loop_ref":470,"next":1882,"prev":1880,"to":475,"twin":1915,"wire_ref":null},{"edge":945,"face":470,"from":475,"geometry_use":{"pcurve":1882,"periodic_lift":[0,0],"sense":"Reverse"},"id":1882,"loop_ref":470,"next":1883,"prev":1881,"to":471,"twin":1892,"wire_ref":null},{"edge":936,"face":470,"from":471,"geometry_use":{"pcurve":1883,"periodic_lift":[0,0],"sense":"Reverse"},"id":1883,"loop_ref":470,"next":1880,"prev":1882,"to":470,"twin":1865,"wire_ref":null},{"edge":946,"face":471,"from":473,"geometry_use":{"pcurve":1884,"periodic_lift":[0,0],"sense":"Forward"},"id":1884,"loop_ref":471,"next":1885,"prev":1887,"to":474,"twin":1918,"wire_ref":null},{"edge":943,"face":471,"from":474,"geometry_use":{"pcurve":1885,"periodic_lift":[0,0],"sense":"Reverse"},"id":1885,"loop_ref":471,"next":1886,"prev":1884,"to":470,"twin":1880,"wire_ref":null},{"edge":938,"face":471,"from":470,"geometry_use":{"pcurve":1886,"periodic_lift":[0,0],"sense":"Reverse"},"id":1886,"loop_ref":471,"next":1887,"prev":1885,"to":469,"twin":1868,"wire_ref":null},{"edge":942,"face":471,"from":469,"geometry_use":{"pcurve":1887,"periodic_lift":[0,0],"sense":"Forward"},"id":1887,"loop_ref":471,"next":1884,"prev":1886,"to":473,"twin":1878,"wire_ref":null},{"edge":947,"face":472,"from":476,"geometry_use":{"pcurve":1888,"periodic_lift":[0,0],"sense":"Forward"},"id":1888,"loop_ref":472,"next":1889,"prev":1891,"to":477,"twin":1907,"wire_ref":null},{"edge":948,"face":472,"from":477,"geometry_use":{"pcurve":1889,"periodic_lift":[0,0],"sense":"Reverse"},"id":1889,"loop_ref":472,"next":1890,"prev":1888,"to":472,"twin":1900,"wire_ref":null},{"edge":940,"face":472,"from":472,"geometry_use":{"pcurve":1890,"periodic_lift":[0,0],"sense":"Reverse"},"id":1890,"loop_ref":472,"next":1891,"prev":1889,"to":468,"twin":1876,"wire_ref":null},{"edge":949,"face":472,"from":468,"geometry_use":{"pcurve":1891,"periodic_lift":[0,0],"sense":"Forward"},"id":1891,"loop_ref":472,"next":1888,"prev":1890,"to":476,"twin":1898,"wire_ref":null},{"edge":945,"face":473,"from":471,"geometry_use":{"pcurve":1892,"periodic_lift":[0,0],"sense":"Forward"},"id":1892,"loop_ref":473,"next":1893,"prev":1895,"to":475,"twin":1882,"wire_ref":null},{"edge":950,"face":473,"from":475,"geometry_use":{"pcurve":1893,"periodic_lift":[0,0],"sense":"Forward"},"id":1893,"loop_ref":473,"next":1894,"prev":1892,"to":478,"twin":1902,"wire_ref":null},{"edge":951,"face":473,"from":478,"geometry_use":{"pcurve":1894,"periodic_lift":[0,0],"sense":"Reverse"},"id":1894,"loop_ref":473,"next":1895,"prev":1893,"to":479,"twin":1905,"wire_ref":null},{"edge":952,"face":473,"from":479,"geometry_use":{"pcurve":1895,"periodic_lift":[0,0],"sense":"Reverse"},"id":1895,"loop_ref":473,"next":1892,"prev":1894,"to":471,"twin":1896,"wire_ref":null},{"edge":952,"face":474,"from":471,"geometry_use":{"pcurve":1896,"periodic_lift":[0,0],"sense":"Forward"},"id":1896,"loop_ref":474,"next":1897,"prev":1899,"to":479,"twin":1895,"wire_ref":null},{"edge":953,"face":474,"from":479,"geometry_use":{"pcurve":1897,"periodic_lift":[0,0],"sense":"Reverse"},"id":1897,"loop_ref":474,"next":1898,"prev":1896,"to":476,"twin":1904,"wire_ref":null},{"edge":949,"face":474,"from":476,"geometry_use":{"pcurve":1898,"periodic_lift":[0,0],"sense":"Reverse"},"id":1898,"loop_ref":474,"next":1899,"prev":1897,"to":468,"twin":1891,"wire_ref":null},{"edge":939,"face":474,"from":468,"geometry_use":{"pcurve":1899,"periodic_lift":[0,0],"sense":"Forward"},"id":1899,"loop_ref":474,"next":1896,"prev":1898,"to":471,"twin":1874,"wire_ref":null},{"edge":948,"face":475,"from":472,"geometry_use":{"pcurve":1900,"periodic_lift":[0,0],"sense":"Forward"},"id":1900,"loop_ref":475,"next":1901,"prev":1903,"to":477,"twin":1889,"wire_ref":null},{"edge":954,"face":475,"from":477,"geometry_use":{"pcurve":1901,"periodic_lift":[0,0],"sense":"Forward"},"id":1901,"loop_ref":475,"next":1902,"prev":1900,"to":478,"twin":1906,"wire_ref":null},{"edge":950,"face":475,"from":478,"geometry_use":{"pcurve":1902,"periodic_lift":[0,0],"sense":"Reverse"},"id":1902,"loop_ref":475,"next":1903,"prev":1901,"to":475,"twin":1893,"wire_ref":null},{"edge":955,"face":475,"from":475,"geometry_use":{"pcurve":1903,"periodic_lift":[0,0],"sense":"Reverse"},"id":1903,"loop_ref":475,"next":1900,"prev":1902,"to":472,"twin":1920,"wire_ref":null},{"edge":953,"face":476,"from":476,"geometry_use":{"pcurve":1904,"periodic_lift":[0,0],"sense":"Forward"},"id":1904,"loop_ref":476,"next":1905,"prev":1907,"to":479,"twin":1897,"wire_ref":null},{"edge":951,"face":476,"from":479,"geometry_use":{"pcurve":1905,"periodic_lift":[0,0],"sense":"Forward"},"id":1905,"loop_ref":476,"next":1906,"prev":1904,"to":478,"twin":1894,"wire_ref":null},{"edge":954,"face":476,"from":478,"geometry_use":{"pcurve":1906,"periodic_lift":[0,0],"sense":"Reverse"},"id":1906,"loop_ref":476,"next":1907,"prev":1905,"to":477,"twin":1901,"wire_ref":null},{"edge":947,"face":476,"from":477,"geometry_use":{"pcurve":1907,"periodic_lift":[0,0],"sense":"Reverse"},"id":1907,"loop_ref":476,"next":1904,"prev":1906,"to":476,"twin":1888,"wire_ref":null},{"edge":956,"face":477,"from":472,"geometry_use":{"pcurve":1908,"periodic_lift":[0,0],"sense":"Forward"},"id":1908,"loop_ref":477,"next":1909,"prev":1911,"to":480,"twin":1923,"wire_ref":null},{"edge":957,"face":477,"from":480,"geometry_use":{"pcurve":1909,"periodic_lift":[0,0],"sense":"Reverse"},"id":1909,"loop_ref":477,"next":1910,"prev":1908,"to":481,"twin":1927,"wire_ref":null},{"edge":958,"face":477,"from":481,"geometry_use":{"pcurve":1910,"periodic_lift":[0,0],"sense":"Reverse"},"id":1910,"loop_ref":477,"next":1911,"prev":1909,"to":473,"twin":1919,"wire_ref":null},{"edge":941,"face":477,"from":473,"geometry_use":{"pcurve":1911,"periodic_lift":[0,0],"sense":"Forward"},"id":1911,"loop_ref":477,"next":1908,"prev":1910,"to":472,"twin":1877,"wire_ref":null},{"edge":959,"face":478,"from":474,"geometry_use":{"pcurve":1912,"periodic_lift":[0,0],"sense":"Forward"},"id":1912,"loop_ref":478,"next":1913,"prev":1915,"to":482,"twin":1917,"wire_ref":null},{"edge":960,"face":478,"from":482,"geometry_use":{"pcurve":1913,"periodic_lift":[0,0],"sense":"Forward"},"id":1913,"loop_ref":478,"next":1914,"prev":1912,"to":483,"twin":1931,"wire_ref":null},{"edge":961,"face":478,"from":483,"geometry_use":{"pcurve":1914,"periodic_lift":[0,0],"sense":"Reverse"},"id":1914,"loop_ref":478,"next":1915,"prev":1913,"to":475,"twin":1921,"wire_ref":null},{"edge":944,"face":478,"from":475,"geometry_use":{"pcurve":1915,"periodic_lift":[0,0],"sense":"Reverse"},"id":1915,"loop_ref":478,"next":1912,"prev":1914,"to":474,"twin":1881,"wire_ref":null},{"edge":962,"face":479,"from":481,"geometry_use":{"pcurve":1916,"periodic_lift":[0,0],"sense":"Forward"},"id":1916,"loop_ref":479,"next":1917,"prev":1919,"to":482,"twin":1934,"wire_ref":null},{"edge":959,"face":479,"from":482,"geometry_use":{"pcurve":1917,"periodic_lift":[0,0],"sense":"Reverse"},"id":1917,"loop_ref":479,"next":1918,"prev":1916,"to":474,"twin":1912,"wire_ref":null},{"edge":946,"face":479,"from":474,"geometry_use":{"pcurve":1918,"periodic_lift":[0,0],"sense":"Reverse"},"id":1918,"loop_ref":479,"next":1919,"prev":1917,"to":473,"twin":1884,"wire_ref":null},{"edge":958,"face":479,"from":473,"geometry_use":{"pcurve":1919,"periodic_lift":[0,0],"sense":"Forward"},"id":1919,"loop_ref":479,"next":1916,"prev":1918,"to":481,"twin":1910,"wire_ref":null},{"edge":955,"face":480,"from":472,"geometry_use":{"pcurve":1920,"periodic_lift":[0,0],"sense":"Forward"},"id":1920,"loop_ref":480,"next":1921,"prev":1923,"to":475,"twin":1903,"wire_ref":null},{"edge":961,"face":480,"from":475,"geometry_use":{"pcurve":1921,"periodic_lift":[0,0],"sense":"Forward"},"id":1921,"loop_ref":480,"next":1922,"prev":1920,"to":483,"twin":1914,"wire_ref":null},{"edge":963,"face":480,"from":483,"geometry_use":{"pcurve":1922,"periodic_lift":[0,0],"sense":"Reverse"},"id":1922,"loop_ref":480,"next":1923,"prev":1921,"to":480,"twin":1947,"wire_ref":null},{"edge":956,"face":480,"from":480,"geometry_use":{"pcurve":1923,"periodic_lift":[0,0],"sense":"Reverse"},"id":1923,"loop_ref":480,"next":1920,"prev":1922,"to":472,"twin":1908,"wire_ref":null},{"edge":964,"face":481,"from":480,"geometry_use":{"pcurve":1924,"periodic_lift":[0,0],"sense":"Forward"},"id":1924,"loop_ref":481,"next":1925,"prev":1927,"to":484,"twin":1938,"wire_ref":null},{"edge":965,"face":481,"from":484,"geometry_use":{"pcurve":1925,"periodic_lift":[0,0],"sense":"Reverse"},"id":1925,"loop_ref":481,"next":1926,"prev":1924,"to":485,"twin":1959,"wire_ref":null},{"edge":966,"face":481,"from":485,"geometry_use":{"pcurve":1926,"periodic_lift":[0,0],"sense":"Reverse"},"id":1926,"loop_ref":481,"next":1927,"prev":1925,"to":481,"twin":1935,"wire_ref":null},{"edge":957,"face":481,"from":481,"geometry_use":{"pcurve":1927,"periodic_lift":[0,0],"sense":"Forward"},"id":1927,"loop_ref":481,"next":1924,"prev":1926,"to":480,"twin":1909,"wire_ref":null},{"edge":967,"face":482,"from":482,"geometry_use":{"pcurve":1928,"periodic_lift":[0,0],"sense":"Forward"},"id":1928,"loop_ref":482,"next":1929,"prev":1931,"to":486,"twin":1933,"wire_ref":null},{"edge":968,"face":482,"from":486,"geometry_use":{"pcurve":1929,"periodic_lift":[0,0],"sense":"Forward"},"id":1929,"loop_ref":482,"next":1930,"prev":1928,"to":487,"twin":1963,"wire_ref":null},{"edge":969,"face":482,"from":487,"geometry_use":{"pcurve":1930,"periodic_lift":[0,0],"sense":"Reverse"},"id":1930,"loop_ref":482,"next":1931,"prev":1929,"to":483,"twin":1940,"wire_ref":null},{"edge":960,"face":482,"from":483,"geometry_use":{"pcurve":1931,"periodic_lift":[0,0],"sense":"Reverse"},"id":1931,"loop_ref":482,"next":1928,"prev":1930,"to":482,"twin":1913,"wire_ref":null},{"edge":970,"face":483,"from":485,"geometry_use":{"pcurve":1932,"periodic_lift":[0,0],"sense":"Forward"},"id":1932,"loop_ref":483,"next":1933,"prev":1935,"to":486,"twin":1966,"wire_ref":null},{"edge":967,"face":483,"from":486,"geometry_use":{"pcurve":1933,"periodic_lift":[0,0],"sense":"Reverse"},"id":1933,"loop_ref":483,"next":1934,"prev":1932,"to":482,"twin":1928,"wire_ref":null},{"edge":962,"face":483,"from":482,"geometry_use":{"pcurve":1934,"periodic_lift":[0,0],"sense":"Reverse"},"id":1934,"loop_ref":483,"next":1935,"prev":1933,"to":481,"twin":1916,"wire_ref":null},{"edge":966,"face":483,"from":481,"geometry_use":{"pcurve":1935,"periodic_lift":[0,0],"sense":"Forward"},"id":1935,"loop_ref":483,"next":1932,"prev":1934,"to":485,"twin":1926,"wire_ref":null},{"edge":971,"face":484,"from":488,"geometry_use":{"pcurve":1936,"periodic_lift":[0,0],"sense":"Forward"},"id":1936,"loop_ref":484,"next":1937,"prev":1939,"to":489,"twin":1955,"wire_ref":null},{"edge":972,"face":484,"from":489,"geometry_use":{"pcurve":1937,"periodic_lift":[0,0],"sense":"Reverse"},"id":1937,"loop_ref":484,"next":1938,"prev":1936,"to":484,"twin":1948,"wire_ref":null},{"edge":964,"face":484,"from":484,"geometry_use":{"pcurve":1938,"periodic_lift":[0,0],"sense":"Reverse"},"id":1938,"loop_ref":484,"next":1939,"prev":1937,"to":480,"twin":1924,"wire_ref":null},{"edge":973,"face":484,"from":480,"geometry_use":{"pcurve":1939,"periodic_lift":[0,0],"sense":"Forward"},"id":1939,"loop_ref":484,"next":1936,"prev":1938,"to":488,"twin":1946,"wire_ref":null},{"edge":969,"face":485,"from":483,"geometry_use":{"pcurve":1940,"periodic_lift":[0,0],"sense":"Forward"},"id":1940,"loop_ref":485,"next":1941,"prev":1943,"to":487,"twin":1930,"wire_ref":null},{"edge":974,"face":485,"from":487,"geometry_use":{"pcurve":1941,"periodic_lift":[0,0],"sense":"Forward"},"id":1941,"loop_ref":485,"next":1942,"prev":1940,"to":490,"twin":1950,"wire_ref":null},{"edge":975,"face":485,"from":490,"geometry_use":{"pcurve":1942,"periodic_lift":[0,0],"sense":"Reverse"},"id":1942,"loop_ref":485,"next":1943,"prev":1941,"to":491,"twin":1953,"wire_ref":null},{"edge":976,"face":485,"from":491,"geometry_use":{"pcurve":1943,"periodic_lift":[0,0],"sense":"Reverse"},"id":1943,"loop_ref":485,"next":1940,"prev":1942,"to":483,"twin":1944,"wire_ref":null},{"edge":976,"face":486,"from":483,"geometry_use":{"pcurve":1944,"periodic_lift":[0,0],"sense":"Forward"},"id":1944,"loop_ref":486,"next":1945,"prev":1947,"to":491,"twin":1943,"wire_ref":null},{"edge":977,"face":486,"from":491,"geometry_use":{"pcurve":1945,"periodic_lift":[0,0],"sense":"Reverse"},"id":1945,"loop_ref":486,"next":1946,"prev":1944,"to":488,"twin":1952,"wire_ref":null},{"edge":973,"face":486,"from":488,"geometry_use":{"pcurve":1946,"periodic_lift":[0,0],"sense":"Reverse"},"id":1946,"loop_ref":486,"next":1947,"prev":1945,"to":480,"twin":1939,"wire_ref":null},{"edge":963,"face":486,"from":480,"geometry_use":{"pcurve":1947,"periodic_lift":[0,0],"sense":"Forward"},"id":1947,"loop_ref":486,"next":1944,"prev":1946,"to":483,"twin":1922,"wire_ref":null},{"edge":972,"face":487,"from":484,"geometry_use":{"pcurve":1948,"periodic_lift":[0,0],"sense":"Forward"},"id":1948,"loop_ref":487,"next":1949,"prev":1951,"to":489,"twin":1937,"wire_ref":null},{"edge":978,"face":487,"from":489,"geometry_use":{"pcurve":1949,"periodic_lift":[0,0],"sense":"Forward"},"id":1949,"loop_ref":487,"next":1950,"prev":1948,"to":490,"twin":1954,"wire_ref":null},{"edge":974,"face":487,"from":490,"geometry_use":{"pcurve":1950,"periodic_lift":[0,0],"sense":"Reverse"},"id":1950,"loop_ref":487,"next":1951,"prev":1949,"to":487,"twin":1941,"wire_ref":null},{"edge":979,"face":487,"from":487,"geometry_use":{"pcurve":1951,"periodic_lift":[0,0],"sense":"Reverse"},"id":1951,"loop_ref":487,"next":1948,"prev":1950,"to":484,"twin":1968,"wire_ref":null},{"edge":977,"face":488,"from":488,"geometry_use":{"pcurve":1952,"periodic_lift":[0,0],"sense":"Forward"},"id":1952,"loop_ref":488,"next":1953,"prev":1955,"to":491,"twin":1945,"wire_ref":null},{"edge":975,"face":488,"from":491,"geometry_use":{"pcurve":1953,"periodic_lift":[0,0],"sense":"Forward"},"id":1953,"loop_ref":488,"next":1954,"prev":1952,"to":490,"twin":1942,"wire_ref":null},{"edge":978,"face":488,"from":490,"geometry_use":{"pcurve":1954,"periodic_lift":[0,0],"sense":"Reverse"},"id":1954,"loop_ref":488,"next":1955,"prev":1953,"to":489,"twin":1949,"wire_ref":null},{"edge":971,"face":488,"from":489,"geometry_use":{"pcurve":1955,"periodic_lift":[0,0],"sense":"Reverse"},"id":1955,"loop_ref":488,"next":1952,"prev":1954,"to":488,"twin":1936,"wire_ref":null},{"edge":980,"face":489,"from":484,"geometry_use":{"pcurve":1956,"periodic_lift":[0,0],"sense":"Forward"},"id":1956,"loop_ref":489,"next":1957,"prev":1959,"to":492,"twin":1971,"wire_ref":null},{"edge":981,"face":489,"from":492,"geometry_use":{"pcurve":1957,"periodic_lift":[0,0],"sense":"Reverse"},"id":1957,"loop_ref":489,"next":1958,"prev":1956,"to":493,"twin":1975,"wire_ref":null},{"edge":982,"face":489,"from":493,"geometry_use":{"pcurve":1958,"periodic_lift":[0,0],"sense":"Reverse"},"id":1958,"loop_ref":489,"next":1959,"prev":1957,"to":485,"twin":1967,"wire_ref":null},{"edge":965,"face":489,"from":485,"geometry_use":{"pcurve":1959,"periodic_lift":[0,0],"sense":"Forward"},"id":1959,"loop_ref":489,"next":1956,"prev":1958,"to":484,"twin":1925,"wire_ref":null},{"edge":983,"face":490,"from":486,"geometry_use":{"pcurve":1960,"periodic_lift":[0,0],"sense":"Forward"},"id":1960,"loop_ref":490,"next":1961,"prev":1963,"to":494,"twin":1965,"wire_ref":null},{"edge":984,"face":490,"from":494,"geometry_use":{"pcurve":1961,"periodic_lift":[0,0],"sense":"Forward"},"id":1961,"loop_ref":490,"next":1962,"prev":1960,"to":495,"twin":1979,"wire_ref":null},{"edge":985,"face":490,"from":495,"geometry_use":{"pcurve":1962,"periodic_lift":[0,0],"sense":"Reverse"},"id":1962,"loop_ref":490,"next":1963,"prev":1961,"to":487,"twin":1969,"wire_ref":null},{"edge":968,"face":490,"from":487,"geometry_use":{"pcurve":1963,"periodic_lift":[0,0],"sense":"Reverse"},"id":1963,"loop_ref":490,"next":1960,"prev":1962,"to":486,"twin":1929,"wire_ref":null},{"edge":986,"face":491,"from":493,"geometry_use":{"pcurve":1964,"periodic_lift":[0,0],"sense":"Forward"},"id":1964,"loop_ref":491,"next":1965,"prev":1967,"to":494,"twin":1982,"wire_ref":null},{"edge":983,"face":491,"from":494,"geometry_use":{"pcurve":1965,"periodic_lift":[0,0],"sense":"Reverse"},"id":1965,"loop_ref":491,"next":1966,"prev":1964,"to":486,"twin":1960,"wire_ref":null},{"edge":970,"face":491,"from":486,"geometry_use":{"pcurve":1966,"periodic_lift":[0,0],"sense":"Reverse"},"id":1966,"loop_ref":491,"next":1967,"prev":1965,"to":485,"twin":1932,"wire_ref":null},{"edge":982,"face":491,"from":485,"geometry_use":{"pcurve":1967,"periodic_lift":[0,0],"sense":"Forward"},"id":1967,"loop_ref":491,"next":1964,"prev":1966,"to":493,"twin":1958,"wire_ref":null},{"edge":979,"face":492,"from":484,"geometry_use":{"pcurve":1968,"periodic_lift":[0,0],"sense":"Forward"},"id":1968,"loop_ref":492,"next":1969,"prev":1971,"to":487,"twin":1951,"wire_ref":null},{"edge":985,"face":492,"from":487,"geometry_use":{"pcurve":1969,"periodic_lift":[0,0],"sense":"Forward"},"id":1969,"loop_ref":492,"next":1970,"prev":1968,"to":495,"twin":1962,"wire_ref":null},{"edge":987,"face":492,"from":495,"geometry_use":{"pcurve":1970,"periodic_lift":[0,0],"sense":"Reverse"},"id":1970,"loop_ref":492,"next":1971,"prev":1969,"to":492,"twin":1995,"wire_ref":null},{"edge":980,"face":492,"from":492,"geometry_use":{"pcurve":1971,"periodic_lift":[0,0],"sense":"Reverse"},"id":1971,"loop_ref":492,"next":1968,"prev":1970,"to":484,"twin":1956,"wire_ref":null},{"edge":988,"face":493,"from":492,"geometry_use":{"pcurve":1972,"periodic_lift":[0,0],"sense":"Forward"},"id":1972,"loop_ref":493,"next":1973,"prev":1975,"to":496,"twin":1986,"wire_ref":null},{"edge":989,"face":493,"from":496,"geometry_use":{"pcurve":1973,"periodic_lift":[0,0],"sense":"Reverse"},"id":1973,"loop_ref":493,"next":1974,"prev":1972,"to":497,"twin":2007,"wire_ref":null},{"edge":990,"face":493,"from":497,"geometry_use":{"pcurve":1974,"periodic_lift":[0,0],"sense":"Reverse"},"id":1974,"loop_ref":493,"next":1975,"prev":1973,"to":493,"twin":1983,"wire_ref":null},{"edge":981,"face":493,"from":493,"geometry_use":{"pcurve":1975,"periodic_lift":[0,0],"sense":"Forward"},"id":1975,"loop_ref":493,"next":1972,"prev":1974,"to":492,"twin":1957,"wire_ref":null},{"edge":991,"face":494,"from":494,"geometry_use":{"pcurve":1976,"periodic_lift":[0,0],"sense":"Forward"},"id":1976,"loop_ref":494,"next":1977,"prev":1979,"to":498,"twin":1981,"wire_ref":null},{"edge":992,"face":494,"from":498,"geometry_use":{"pcurve":1977,"periodic_lift":[0,0],"sense":"Forward"},"id":1977,"loop_ref":494,"next":1978,"prev":1976,"to":499,"twin":2011,"wire_ref":null},{"edge":993,"face":494,"from":499,"geometry_use":{"pcurve":1978,"periodic_lift":[0,0],"sense":"Reverse"},"id":1978,"loop_ref":494,"next":1979,"prev":1977,"to":495,"twin":1988,"wire_ref":null},{"edge":984,"face":494,"from":495,"geometry_use":{"pcurve":1979,"periodic_lift":[0,0],"sense":"Reverse"},"id":1979,"loop_ref":494,"next":1976,"prev":1978,"to":494,"twin":1961,"wire_ref":null},{"edge":994,"face":495,"from":497,"geometry_use":{"pcurve":1980,"periodic_lift":[0,0],"sense":"Forward"},"id":1980,"loop_ref":495,"next":1981,"prev":1983,"to":498,"twin":2014,"wire_ref":null},{"edge":991,"face":495,"from":498,"geometry_use":{"pcurve":1981,"periodic_lift":[0,0],"sense":"Reverse"},"id":1981,"loop_ref":495,"next":1982,"prev":1980,"to":494,"twin":1976,"wire_ref":null},{"edge":986,"face":495,"from":494,"geometry_use":{"pcurve":1982,"periodic_lift":[0,0],"sense":"Reverse"},"id":1982,"loop_ref":495,"next":1983,"prev":1981,"to":493,"twin":1964,"wire_ref":null},{"edge":990,"face":495,"from":493,"geometry_use":{"pcurve":1983,"periodic_lift":[0,0],"sense":"Forward"},"id":1983,"loop_ref":495,"next":1980,"prev":1982,"to":497,"twin":1974,"wire_ref":null},{"edge":995,"face":496,"from":500,"geometry_use":{"pcurve":1984,"periodic_lift":[0,0],"sense":"Forward"},"id":1984,"loop_ref":496,"next":1985,"prev":1987,"to":501,"twin":2003,"wire_ref":null},{"edge":996,"face":496,"from":501,"geometry_use":{"pcurve":1985,"periodic_lift":[0,0],"sense":"Reverse"},"id":1985,"loop_ref":496,"next":1986,"prev":1984,"to":496,"twin":1996,"wire_ref":null},{"edge":988,"face":496,"from":496,"geometry_use":{"pcurve":1986,"periodic_lift":[0,0],"sense":"Reverse"},"id":1986,"loop_ref":496,"next":1987,"prev":1985,"to":492,"twin":1972,"wire_ref":null},{"edge":997,"face":496,"from":492,"geometry_use":{"pcurve":1987,"periodic_lift":[0,0],"sense":"Forward"},"id":1987,"loop_ref":496,"next":1984,"prev":1986,"to":500,"twin":1994,"wire_ref":null},{"edge":993,"face":497,"from":495,"geometry_use":{"pcurve":1988,"periodic_lift":[0,0],"sense":"Forward"},"id":1988,"loop_ref":497,"next":1989,"prev":1991,"to":499,"twin":1978,"wire_ref":null},{"edge":998,"face":497,"from":499,"geometry_use":{"pcurve":1989,"periodic_lift":[0,0],"sense":"Forward"},"id":1989,"loop_ref":497,"next":1990,"prev":1988,"to":502,"twin":1998,"wire_ref":null},{"edge":999,"face":497,"from":502,"geometry_use":{"pcurve":1990,"periodic_lift":[0,0],"sense":"Reverse"},"id":1990,"loop_ref":497,"next":1991,"prev":1989,"to":503,"twin":2001,"wire_ref":null},{"edge":1000,"face":497,"from":503,"geometry_use":{"pcurve":1991,"periodic_lift":[0,0],"sense":"Reverse"},"id":1991,"loop_ref":497,"next":1988,"prev":1990,"to":495,"twin":1992,"wire_ref":null},{"edge":1000,"face":498,"from":495,"geometry_use":{"pcurve":1992,"periodic_lift":[0,0],"sense":"Forward"},"id":1992,"loop_ref":498,"next":1993,"prev":1995,"to":503,"twin":1991,"wire_ref":null},{"edge":1001,"face":498,"from":503,"geometry_use":{"pcurve":1993,"periodic_lift":[0,0],"sense":"Reverse"},"id":1993,"loop_ref":498,"next":1994,"prev":1992,"to":500,"twin":2000,"wire_ref":null},{"edge":997,"face":498,"from":500,"geometry_use":{"pcurve":1994,"periodic_lift":[0,0],"sense":"Reverse"},"id":1994,"loop_ref":498,"next":1995,"prev":1993,"to":492,"twin":1987,"wire_ref":null},{"edge":987,"face":498,"from":492,"geometry_use":{"pcurve":1995,"periodic_lift":[0,0],"sense":"Forward"},"id":1995,"loop_ref":498,"next":1992,"prev":1994,"to":495,"twin":1970,"wire_ref":null},{"edge":996,"face":499,"from":496,"geometry_use":{"pcurve":1996,"periodic_lift":[0,0],"sense":"Forward"},"id":1996,"loop_ref":499,"next":1997,"prev":1999,"to":501,"twin":1985,"wire_ref":null},{"edge":1002,"face":499,"from":501,"geometry_use":{"pcurve":1997,"periodic_lift":[0,0],"sense":"Forward"},"id":1997,"loop_ref":499,"next":1998,"prev":1996,"to":502,"twin":2002,"wire_ref":null},{"edge":998,"face":499,"from":502,"geometry_use":{"pcurve":1998,"periodic_lift":[0,0],"sense":"Reverse"},"id":1998,"loop_ref":499,"next":1999,"prev":1997,"to":499,"twin":1989,"wire_ref":null},{"edge":1003,"face":499,"from":499,"geometry_use":{"pcurve":1999,"periodic_lift":[0,0],"sense":"Reverse"},"id":1999,"loop_ref":499,"next":1996,"prev":1998,"to":496,"twin":2016,"wire_ref":null},{"edge":1001,"face":500,"from":500,"geometry_use":{"pcurve":2000,"periodic_lift":[0,0],"sense":"Forward"},"id":2000,"loop_ref":500,"next":2001,"prev":2003,"to":503,"twin":1993,"wire_ref":null},{"edge":999,"face":500,"from":503,"geometry_use":{"pcurve":2001,"periodic_lift":[0,0],"sense":"Forward"},"id":2001,"loop_ref":500,"next":2002,"prev":2000,"to":502,"twin":1990,"wire_ref":null},{"edge":1002,"face":500,"from":502,"geometry_use":{"pcurve":2002,"periodic_lift":[0,0],"sense":"Reverse"},"id":2002,"loop_ref":500,"next":2003,"prev":2001,"to":501,"twin":1997,"wire_ref":null},{"edge":995,"face":500,"from":501,"geometry_use":{"pcurve":2003,"periodic_lift":[0,0],"sense":"Reverse"},"id":2003,"loop_ref":500,"next":2000,"prev":2002,"to":500,"twin":1984,"wire_ref":null},{"edge":1004,"face":501,"from":496,"geometry_use":{"pcurve":2004,"periodic_lift":[0,0],"sense":"Forward"},"id":2004,"loop_ref":501,"next":2005,"prev":2007,"to":504,"twin":2019,"wire_ref":null},{"edge":1005,"face":501,"from":504,"geometry_use":{"pcurve":2005,"periodic_lift":[0,0],"sense":"Reverse"},"id":2005,"loop_ref":501,"next":2006,"prev":2004,"to":505,"twin":2023,"wire_ref":null},{"edge":1006,"face":501,"from":505,"geometry_use":{"pcurve":2006,"periodic_lift":[0,0],"sense":"Reverse"},"id":2006,"loop_ref":501,"next":2007,"prev":2005,"to":497,"twin":2015,"wire_ref":null},{"edge":989,"face":501,"from":497,"geometry_use":{"pcurve":2007,"periodic_lift":[0,0],"sense":"Forward"},"id":2007,"loop_ref":501,"next":2004,"prev":2006,"to":496,"twin":1973,"wire_ref":null},{"edge":1007,"face":502,"from":498,"geometry_use":{"pcurve":2008,"periodic_lift":[0,0],"sense":"Forward"},"id":2008,"loop_ref":502,"next":2009,"prev":2011,"to":506,"twin":2013,"wire_ref":null},{"edge":1008,"face":502,"from":506,"geometry_use":{"pcurve":2009,"periodic_lift":[0,0],"sense":"Forward"},"id":2009,"loop_ref":502,"next":2010,"prev":2008,"to":507,"twin":2027,"wire_ref":null},{"edge":1009,"face":502,"from":507,"geometry_use":{"pcurve":2010,"periodic_lift":[0,0],"sense":"Reverse"},"id":2010,"loop_ref":502,"next":2011,"prev":2009,"to":499,"twin":2017,"wire_ref":null},{"edge":992,"face":502,"from":499,"geometry_use":{"pcurve":2011,"periodic_lift":[0,0],"sense":"Reverse"},"id":2011,"loop_ref":502,"next":2008,"prev":2010,"to":498,"twin":1977,"wire_ref":null},{"edge":1010,"face":503,"from":505,"geometry_use":{"pcurve":2012,"periodic_lift":[0,0],"sense":"Forward"},"id":2012,"loop_ref":503,"next":2013,"prev":2015,"to":506,"twin":2030,"wire_ref":null},{"edge":1007,"face":503,"from":506,"geometry_use":{"pcurve":2013,"periodic_lift":[0,0],"sense":"Reverse"},"id":2013,"loop_ref":503,"next":2014,"prev":2012,"to":498,"twin":2008,"wire_ref":null},{"edge":994,"face":503,"from":498,"geometry_use":{"pcurve":2014,"periodic_lift":[0,0],"sense":"Reverse"},"id":2014,"loop_ref":503,"next":2015,"prev":2013,"to":497,"twin":1980,"wire_ref":null},{"edge":1006,"face":503,"from":497,"geometry_use":{"pcurve":2015,"periodic_lift":[0,0],"sense":"Forward"},"id":2015,"loop_ref":503,"next":2012,"prev":2014,"to":505,"twin":2006,"wire_ref":null},{"edge":1003,"face":504,"from":496,"geometry_use":{"pcurve":2016,"periodic_lift":[0,0],"sense":"Forward"},"id":2016,"loop_ref":504,"next":2017,"prev":2019,"to":499,"twin":1999,"wire_ref":null},{"edge":1009,"face":504,"from":499,"geometry_use":{"pcurve":2017,"periodic_lift":[0,0],"sense":"Forward"},"id":2017,"loop_ref":504,"next":2018,"prev":2016,"to":507,"twin":2010,"wire_ref":null},{"edge":1011,"face":504,"from":507,"geometry_use":{"pcurve":2018,"periodic_lift":[0,0],"sense":"Reverse"},"id":2018,"loop_ref":504,"next":2019,"prev":2017,"to":504,"twin":2043,"wire_ref":null},{"edge":1004,"face":504,"from":504,"geometry_use":{"pcurve":2019,"periodic_lift":[0,0],"sense":"Reverse"},"id":2019,"loop_ref":504,"next":2016,"prev":2018,"to":496,"twin":2004,"wire_ref":null},{"edge":1012,"face":505,"from":504,"geometry_use":{"pcurve":2020,"periodic_lift":[0,0],"sense":"Forward"},"id":2020,"loop_ref":505,"next":2021,"prev":2023,"to":508,"twin":2034,"wire_ref":null},{"edge":1013,"face":505,"from":508,"geometry_use":{"pcurve":2021,"periodic_lift":[0,0],"sense":"Reverse"},"id":2021,"loop_ref":505,"next":2022,"prev":2020,"to":509,"twin":2055,"wire_ref":null},{"edge":1014,"face":505,"from":509,"geometry_use":{"pcurve":2022,"periodic_lift":[0,0],"sense":"Reverse"},"id":2022,"loop_ref":505,"next":2023,"prev":2021,"to":505,"twin":2031,"wire_ref":null},{"edge":1005,"face":505,"from":505,"geometry_use":{"pcurve":2023,"periodic_lift":[0,0],"sense":"Forward"},"id":2023,"loop_ref":505,"next":2020,"prev":2022,"to":504,"twin":2005,"wire_ref":null},{"edge":1015,"face":506,"from":506,"geometry_use":{"pcurve":2024,"periodic_lift":[0,0],"sense":"Forward"},"id":2024,"loop_ref":506,"next":2025,"prev":2027,"to":510,"twin":2029,"wire_ref":null},{"edge":1016,"face":506,"from":510,"geometry_use":{"pcurve":2025,"periodic_lift":[0,0],"sense":"Forward"},"id":2025,"loop_ref":506,"next":2026,"prev":2024,"to":511,"twin":2059,"wire_ref":null},{"edge":1017,"face":506,"from":511,"geometry_use":{"pcurve":2026,"periodic_lift":[0,0],"sense":"Reverse"},"id":2026,"loop_ref":506,"next":2027,"prev":2025,"to":507,"twin":2036,"wire_ref":null},{"edge":1008,"face":506,"from":507,"geometry_use":{"pcurve":2027,"periodic_lift":[0,0],"sense":"Reverse"},"id":2027,"loop_ref":506,"next":2024,"prev":2026,"to":506,"twin":2009,"wire_ref":null},{"edge":1018,"face":507,"from":509,"geometry_use":{"pcurve":2028,"periodic_lift":[0,0],"sense":"Forward"},"id":2028,"loop_ref":507,"next":2029,"prev":2031,"to":510,"twin":2062,"wire_ref":null},{"edge":1015,"face":507,"from":510,"geometry_use":{"pcurve":2029,"periodic_lift":[0,0],"sense":"Reverse"},"id":2029,"loop_ref":507,"next":2030,"prev":2028,"to":506,"twin":2024,"wire_ref":null},{"edge":1010,"face":507,"from":506,"geometry_use":{"pcurve":2030,"periodic_lift":[0,0],"sense":"Reverse"},"id":2030,"loop_ref":507,"next":2031,"prev":2029,"to":505,"twin":2012,"wire_ref":null},{"edge":1014,"face":507,"from":505,"geometry_use":{"pcurve":2031,"periodic_lift":[0,0],"sense":"Forward"},"id":2031,"loop_ref":507,"next":2028,"prev":2030,"to":509,"twin":2022,"wire_ref":null},{"edge":1019,"face":508,"from":512,"geometry_use":{"pcurve":2032,"periodic_lift":[0,0],"sense":"Forward"},"id":2032,"loop_ref":508,"next":2033,"prev":2035,"to":513,"twin":2051,"wire_ref":null},{"edge":1020,"face":508,"from":513,"geometry_use":{"pcurve":2033,"periodic_lift":[0,0],"sense":"Reverse"},"id":2033,"loop_ref":508,"next":2034,"prev":2032,"to":508,"twin":2044,"wire_ref":null},{"edge":1012,"face":508,"from":508,"geometry_use":{"pcurve":2034,"periodic_lift":[0,0],"sense":"Reverse"},"id":2034,"loop_ref":508,"next":2035,"prev":2033,"to":504,"twin":2020,"wire_ref":null},{"edge":1021,"face":508,"from":504,"geometry_use":{"pcurve":2035,"periodic_lift":[0,0],"sense":"Forward"},"id":2035,"loop_ref":508,"next":2032,"prev":2034,"to":512,"twin":2042,"wire_ref":null},{"edge":1017,"face":509,"from":507,"geometry_use":{"pcurve":2036,"periodic_lift":[0,0],"sense":"Forward"},"id":2036,"loop_ref":509,"next":2037,"prev":2039,"to":511,"twin":2026,"wire_ref":null},{"edge":1022,"face":509,"from":511,"geometry_use":{"pcurve":2037,"periodic_lift":[0,0],"sense":"Forward"},"id":2037,"loop_ref":509,"next":2038,"prev":2036,"to":514,"twin":2046,"wire_ref":null},{"edge":1023,"face":509,"from":514,"geometry_use":{"pcurve":2038,"periodic_lift":[0,0],"sense":"Reverse"},"id":2038,"loop_ref":509,"next":2039,"prev":2037,"to":515,"twin":2049,"wire_ref":null},{"edge":1024,"face":509,"from":515,"geometry_use":{"pcurve":2039,"periodic_lift":[0,0],"sense":"Reverse"},"id":2039,"loop_ref":509,"next":2036,"prev":2038,"to":507,"twin":2040,"wire_ref":null},{"edge":1024,"face":510,"from":507,"geometry_use":{"pcurve":2040,"periodic_lift":[0,0],"sense":"Forward"},"id":2040,"loop_ref":510,"next":2041,"prev":2043,"to":515,"twin":2039,"wire_ref":null},{"edge":1025,"face":510,"from":515,"geometry_use":{"pcurve":2041,"periodic_lift":[0,0],"sense":"Reverse"},"id":2041,"loop_ref":510,"next":2042,"prev":2040,"to":512,"twin":2048,"wire_ref":null},{"edge":1021,"face":510,"from":512,"geometry_use":{"pcurve":2042,"periodic_lift":[0,0],"sense":"Reverse"},"id":2042,"loop_ref":510,"next":2043,"prev":2041,"to":504,"twin":2035,"wire_ref":null},{"edge":1011,"face":510,"from":504,"geometry_use":{"pcurve":2043,"periodic_lift":[0,0],"sense":"Forward"},"id":2043,"loop_ref":510,"next":2040,"prev":2042,"to":507,"twin":2018,"wire_ref":null},{"edge":1020,"face":511,"from":508,"geometry_use":{"pcurve":2044,"periodic_lift":[0,0],"sense":"Forward"},"id":2044,"loop_ref":511,"next":2045,"prev":2047,"to":513,"twin":2033,"wire_ref":null},{"edge":1026,"face":511,"from":513,"geometry_use":{"pcurve":2045,"periodic_lift":[0,0],"sense":"Forward"},"id":2045,"loop_ref":511,"next":2046,"prev":2044,"to":514,"twin":2050,"wire_ref":null},{"edge":1022,"face":511,"from":514,"geometry_use":{"pcurve":2046,"periodic_lift":[0,0],"sense":"Reverse"},"id":2046,"loop_ref":511,"next":2047,"prev":2045,"to":511,"twin":2037,"wire_ref":null},{"edge":1027,"face":511,"from":511,"geometry_use":{"pcurve":2047,"periodic_lift":[0,0],"sense":"Reverse"},"id":2047,"loop_ref":511,"next":2044,"prev":2046,"to":508,"twin":2064,"wire_ref":null},{"edge":1025,"face":512,"from":512,"geometry_use":{"pcurve":2048,"periodic_lift":[0,0],"sense":"Forward"},"id":2048,"loop_ref":512,"next":2049,"prev":2051,"to":515,"twin":2041,"wire_ref":null},{"edge":1023,"face":512,"from":515,"geometry_use":{"pcurve":2049,"periodic_lift":[0,0],"sense":"Forward"},"id":2049,"loop_ref":512,"next":2050,"prev":2048,"to":514,"twin":2038,"wire_ref":null},{"edge":1026,"face":512,"from":514,"geometry_use":{"pcurve":2050,"periodic_lift":[0,0],"sense":"Reverse"},"id":2050,"loop_ref":512,"next":2051,"prev":2049,"to":513,"twin":2045,"wire_ref":null},{"edge":1019,"face":512,"from":513,"geometry_use":{"pcurve":2051,"periodic_lift":[0,0],"sense":"Reverse"},"id":2051,"loop_ref":512,"next":2048,"prev":2050,"to":512,"twin":2032,"wire_ref":null},{"edge":1028,"face":513,"from":508,"geometry_use":{"pcurve":2052,"periodic_lift":[0,0],"sense":"Forward"},"id":2052,"loop_ref":513,"next":2053,"prev":2055,"to":516,"twin":2067,"wire_ref":null},{"edge":1029,"face":513,"from":516,"geometry_use":{"pcurve":2053,"periodic_lift":[0,0],"sense":"Reverse"},"id":2053,"loop_ref":513,"next":2054,"prev":2052,"to":517,"twin":2071,"wire_ref":null},{"edge":1030,"face":513,"from":517,"geometry_use":{"pcurve":2054,"periodic_lift":[0,0],"sense":"Reverse"},"id":2054,"loop_ref":513,"next":2055,"prev":2053,"to":509,"twin":2063,"wire_ref":null},{"edge":1013,"face":513,"from":509,"geometry_use":{"pcurve":2055,"periodic_lift":[0,0],"sense":"Forward"},"id":2055,"loop_ref":513,"next":2052,"prev":2054,"to":508,"twin":2021,"wire_ref":null},{"edge":1031,"face":514,"from":510,"geometry_use":{"pcurve":2056,"periodic_lift":[0,0],"sense":"Forward"},"id":2056,"loop_ref":514,"next":2057,"prev":2059,"to":518,"twin":2061,"wire_ref":null},{"edge":1032,"face":514,"from":518,"geometry_use":{"pcurve":2057,"periodic_lift":[0,0],"sense":"Forward"},"id":2057,"loop_ref":514,"next":2058,"prev":2056,"to":519,"twin":2075,"wire_ref":null},{"edge":1033,"face":514,"from":519,"geometry_use":{"pcurve":2058,"periodic_lift":[0,0],"sense":"Reverse"},"id":2058,"loop_ref":514,"next":2059,"prev":2057,"to":511,"twin":2065,"wire_ref":null},{"edge":1016,"face":514,"from":511,"geometry_use":{"pcurve":2059,"periodic_lift":[0,0],"sense":"Reverse"},"id":2059,"loop_ref":514,"next":2056,"prev":2058,"to":510,"twin":2025,"wire_ref":null},{"edge":1034,"face":515,"from":517,"geometry_use":{"pcurve":2060,"periodic_lift":[0,0],"sense":"Forward"},"id":2060,"loop_ref":515,"next":2061,"prev":2063,"to":518,"twin":2078,"wire_ref":null},{"edge":1031,"face":515,"from":518,"geometry_use":{"pcurve":2061,"periodic_lift":[0,0],"sense":"Reverse"},"id":2061,"loop_ref":515,"next":2062,"prev":2060,"to":510,"twin":2056,"wire_ref":null},{"edge":1018,"face":515,"from":510,"geometry_use":{"pcurve":2062,"periodic_lift":[0,0],"sense":"Reverse"},"id":2062,"loop_ref":515,"next":2063,"prev":2061,"to":509,"twin":2028,"wire_ref":null},{"edge":1030,"face":515,"from":509,"geometry_use":{"pcurve":2063,"periodic_lift":[0,0],"sense":"Forward"},"id":2063,"loop_ref":515,"next":2060,"prev":2062,"to":517,"twin":2054,"wire_ref":null},{"edge":1027,"face":516,"from":508,"geometry_use":{"pcurve":2064,"periodic_lift":[0,0],"sense":"Forward"},"id":2064,"loop_ref":516,"next":2065,"prev":2067,"to":511,"twin":2047,"wire_ref":null},{"edge":1033,"face":516,"from":511,"geometry_use":{"pcurve":2065,"periodic_lift":[0,0],"sense":"Forward"},"id":2065,"loop_ref":516,"next":2066,"prev":2064,"to":519,"twin":2058,"wire_ref":null},{"edge":1035,"face":516,"from":519,"geometry_use":{"pcurve":2066,"periodic_lift":[0,0],"sense":"Reverse"},"id":2066,"loop_ref":516,"next":2067,"prev":2065,"to":516,"twin":2091,"wire_ref":null},{"edge":1028,"face":516,"from":516,"geometry_use":{"pcurve":2067,"periodic_lift":[0,0],"sense":"Reverse"},"id":2067,"loop_ref":516,"next":2064,"prev":2066,"to":508,"twin":2052,"wire_ref":null},{"edge":1036,"face":517,"from":516,"geometry_use":{"pcurve":2068,"periodic_lift":[0,0],"sense":"Forward"},"id":2068,"loop_ref":517,"next":2069,"prev":2071,"to":520,"twin":2082,"wire_ref":null},{"edge":1037,"face":517,"from":520,"geometry_use":{"pcurve":2069,"periodic_lift":[0,0],"sense":"Reverse"},"id":2069,"loop_ref":517,"next":2070,"prev":2068,"to":521,"twin":2103,"wire_ref":null},{"edge":1038,"face":517,"from":521,"geometry_use":{"pcurve":2070,"periodic_lift":[0,0],"sense":"Reverse"},"id":2070,"loop_ref":517,"next":2071,"prev":2069,"to":517,"twin":2079,"wire_ref":null},{"edge":1029,"face":517,"from":517,"geometry_use":{"pcurve":2071,"periodic_lift":[0,0],"sense":"Forward"},"id":2071,"loop_ref":517,"next":2068,"prev":2070,"to":516,"twin":2053,"wire_ref":null},{"edge":1039,"face":518,"from":518,"geometry_use":{"pcurve":2072,"periodic_lift":[0,0],"sense":"Forward"},"id":2072,"loop_ref":518,"next":2073,"prev":2075,"to":522,"twin":2077,"wire_ref":null},{"edge":1040,"face":518,"from":522,"geometry_use":{"pcurve":2073,"periodic_lift":[0,0],"sense":"Forward"},"id":2073,"loop_ref":518,"next":2074,"prev":2072,"to":523,"twin":2107,"wire_ref":null},{"edge":1041,"face":518,"from":523,"geometry_use":{"pcurve":2074,"periodic_lift":[0,0],"sense":"Reverse"},"id":2074,"loop_ref":518,"next":2075,"prev":2073,"to":519,"twin":2084,"wire_ref":null},{"edge":1032,"face":518,"from":519,"geometry_use":{"pcurve":2075,"periodic_lift":[0,0],"sense":"Reverse"},"id":2075,"loop_ref":518,"next":2072,"prev":2074,"to":518,"twin":2057,"wire_ref":null},{"edge":1042,"face":519,"from":521,"geometry_use":{"pcurve":2076,"periodic_lift":[0,0],"sense":"Forward"},"id":2076,"loop_ref":519,"next":2077,"prev":2079,"to":522,"twin":2110,"wire_ref":null},{"edge":1039,"face":519,"from":522,"geometry_use":{"pcurve":2077,"periodic_lift":[0,0],"sense":"Reverse"},"id":2077,"loop_ref":519,"next":2078,"prev":2076,"to":518,"twin":2072,"wire_ref":null},{"edge":1034,"face":519,"from":518,"geometry_use":{"pcurve":2078,"periodic_lift":[0,0],"sense":"Reverse"},"id":2078,"loop_ref":519,"next":2079,"prev":2077,"to":517,"twin":2060,"wire_ref":null},{"edge":1038,"face":519,"from":517,"geometry_use":{"pcurve":2079,"periodic_lift":[0,0],"sense":"Forward"},"id":2079,"loop_ref":519,"next":2076,"prev":2078,"to":521,"twin":2070,"wire_ref":null},{"edge":1043,"face":520,"from":524,"geometry_use":{"pcurve":2080,"periodic_lift":[0,0],"sense":"Forward"},"id":2080,"loop_ref":520,"next":2081,"prev":2083,"to":525,"twin":2099,"wire_ref":null},{"edge":1044,"face":520,"from":525,"geometry_use":{"pcurve":2081,"periodic_lift":[0,0],"sense":"Reverse"},"id":2081,"loop_ref":520,"next":2082,"prev":2080,"to":520,"twin":2092,"wire_ref":null},{"edge":1036,"face":520,"from":520,"geometry_use":{"pcurve":2082,"periodic_lift":[0,0],"sense":"Reverse"},"id":2082,"loop_ref":520,"next":2083,"prev":2081,"to":516,"twin":2068,"wire_ref":null},{"edge":1045,"face":520,"from":516,"geometry_use":{"pcurve":2083,"periodic_lift":[0,0],"sense":"Forward"},"id":2083,"loop_ref":520,"next":2080,"prev":2082,"to":524,"twin":2090,"wire_ref":null},{"edge":1041,"face":521,"from":519,"geometry_use":{"pcurve":2084,"periodic_lift":[0,0],"sense":"Forward"},"id":2084,"loop_ref":521,"next":2085,"prev":2087,"to":523,"twin":2074,"wire_ref":null},{"edge":1046,"face":521,"from":523,"geometry_use":{"pcurve":2085,"periodic_lift":[0,0],"sense":"Forward"},"id":2085,"loop_ref":521,"next":2086,"prev":2084,"to":526,"twin":2094,"wire_ref":null},{"edge":1047,"face":521,"from":526,"geometry_use":{"pcurve":2086,"periodic_lift":[0,0],"sense":"Reverse"},"id":2086,"loop_ref":521,"next":2087,"prev":2085,"to":527,"twin":2097,"wire_ref":null},{"edge":1048,"face":521,"from":527,"geometry_use":{"pcurve":2087,"periodic_lift":[0,0],"sense":"Reverse"},"id":2087,"loop_ref":521,"next":2084,"prev":2086,"to":519,"twin":2088,"wire_ref":null},{"edge":1048,"face":522,"from":519,"geometry_use":{"pcurve":2088,"periodic_lift":[0,0],"sense":"Forward"},"id":2088,"loop_ref":522,"next":2089,"prev":2091,"to":527,"twin":2087,"wire_ref":null},{"edge":1049,"face":522,"from":527,"geometry_use":{"pcurve":2089,"periodic_lift":[0,0],"sense":"Reverse"},"id":2089,"loop_ref":522,"next":2090,"prev":2088,"to":524,"twin":2096,"wire_ref":null},{"edge":1045,"face":522,"from":524,"geometry_use":{"pcurve":2090,"periodic_lift":[0,0],"sense":"Reverse"},"id":2090,"loop_ref":522,"next":2091,"prev":2089,"to":516,"twin":2083,"wire_ref":null},{"edge":1035,"face":522,"from":516,"geometry_use":{"pcurve":2091,"periodic_lift":[0,0],"sense":"Forward"},"id":2091,"loop_ref":522,"next":2088,"prev":2090,"to":519,"twin":2066,"wire_ref":null},{"edge":1044,"face":523,"from":520,"geometry_use":{"pcurve":2092,"periodic_lift":[0,0],"sense":"Forward"},"id":2092,"loop_ref":523,"next":2093,"prev":2095,"to":525,"twin":2081,"wire_ref":null},{"edge":1050,"face":523,"from":525,"geometry_use":{"pcurve":2093,"periodic_lift":[0,0],"sense":"Forward"},"id":2093,"loop_ref":523,"next":2094,"prev":2092,"to":526,"twin":2098,"wire_ref":null},{"edge":1046,"face":523,"from":526,"geometry_use":{"pcurve":2094,"periodic_lift":[0,0],"sense":"Reverse"},"id":2094,"loop_ref":523,"next":2095,"prev":2093,"to":523,"twin":2085,"wire_ref":null},{"edge":1051,"face":523,"from":523,"geometry_use":{"pcurve":2095,"periodic_lift":[0,0],"sense":"Reverse"},"id":2095,"loop_ref":523,"next":2092,"prev":2094,"to":520,"twin":2112,"wire_ref":null},{"edge":1049,"face":524,"from":524,"geometry_use":{"pcurve":2096,"periodic_lift":[0,0],"sense":"Forward"},"id":2096,"loop_ref":524,"next":2097,"prev":2099,"to":527,"twin":2089,"wire_ref":null},{"edge":1047,"face":524,"from":527,"geometry_use":{"pcurve":2097,"periodic_lift":[0,0],"sense":"Forward"},"id":2097,"loop_ref":524,"next":2098,"prev":2096,"to":526,"twin":2086,"wire_ref":null},{"edge":1050,"face":524,"from":526,"geometry_use":{"pcurve":2098,"periodic_lift":[0,0],"sense":"Reverse"},"id":2098,"loop_ref":524,"next":2099,"prev":2097,"to":525,"twin":2093,"wire_ref":null},{"edge":1043,"face":524,"from":525,"geometry_use":{"pcurve":2099,"periodic_lift":[0,0],"sense":"Reverse"},"id":2099,"loop_ref":524,"next":2096,"prev":2098,"to":524,"twin":2080,"wire_ref":null},{"edge":1052,"face":525,"from":520,"geometry_use":{"pcurve":2100,"periodic_lift":[0,0],"sense":"Forward"},"id":2100,"loop_ref":525,"next":2101,"prev":2103,"to":528,"twin":2115,"wire_ref":null},{"edge":1053,"face":525,"from":528,"geometry_use":{"pcurve":2101,"periodic_lift":[0,0],"sense":"Reverse"},"id":2101,"loop_ref":525,"next":2102,"prev":2100,"to":529,"twin":2119,"wire_ref":null},{"edge":1054,"face":525,"from":529,"geometry_use":{"pcurve":2102,"periodic_lift":[0,0],"sense":"Reverse"},"id":2102,"loop_ref":525,"next":2103,"prev":2101,"to":521,"twin":2111,"wire_ref":null},{"edge":1037,"face":525,"from":521,"geometry_use":{"pcurve":2103,"periodic_lift":[0,0],"sense":"Forward"},"id":2103,"loop_ref":525,"next":2100,"prev":2102,"to":520,"twin":2069,"wire_ref":null},{"edge":1055,"face":526,"from":522,"geometry_use":{"pcurve":2104,"periodic_lift":[0,0],"sense":"Forward"},"id":2104,"loop_ref":526,"next":2105,"prev":2107,"to":530,"twin":2109,"wire_ref":null},{"edge":1056,"face":526,"from":530,"geometry_use":{"pcurve":2105,"periodic_lift":[0,0],"sense":"Forward"},"id":2105,"loop_ref":526,"next":2106,"prev":2104,"to":531,"twin":2123,"wire_ref":null},{"edge":1057,"face":526,"from":531,"geometry_use":{"pcurve":2106,"periodic_lift":[0,0],"sense":"Reverse"},"id":2106,"loop_ref":526,"next":2107,"prev":2105,"to":523,"twin":2113,"wire_ref":null},{"edge":1040,"face":526,"from":523,"geometry_use":{"pcurve":2107,"periodic_lift":[0,0],"sense":"Reverse"},"id":2107,"loop_ref":526,"next":2104,"prev":2106,"to":522,"twin":2073,"wire_ref":null},{"edge":1058,"face":527,"from":529,"geometry_use":{"pcurve":2108,"periodic_lift":[0,0],"sense":"Forward"},"id":2108,"loop_ref":527,"next":2109,"prev":2111,"to":530,"twin":2126,"wire_ref":null},{"edge":1055,"face":527,"from":530,"geometry_use":{"pcurve":2109,"periodic_lift":[0,0],"sense":"Reverse"},"id":2109,"loop_ref":527,"next":2110,"prev":2108,"to":522,"twin":2104,"wire_ref":null},{"edge":1042,"face":527,"from":522,"geometry_use":{"pcurve":2110,"periodic_lift":[0,0],"sense":"Reverse"},"id":2110,"loop_ref":527,"next":2111,"prev":2109,"to":521,"twin":2076,"wire_ref":null},{"edge":1054,"face":527,"from":521,"geometry_use":{"pcurve":2111,"periodic_lift":[0,0],"sense":"Forward"},"id":2111,"loop_ref":527,"next":2108,"prev":2110,"to":529,"twin":2102,"wire_ref":null},{"edge":1051,"face":528,"from":520,"geometry_use":{"pcurve":2112,"periodic_lift":[0,0],"sense":"Forward"},"id":2112,"loop_ref":528,"next":2113,"prev":2115,"to":523,"twin":2095,"wire_ref":null},{"edge":1057,"face":528,"from":523,"geometry_use":{"pcurve":2113,"periodic_lift":[0,0],"sense":"Forward"},"id":2113,"loop_ref":528,"next":2114,"prev":2112,"to":531,"twin":2106,"wire_ref":null},{"edge":1059,"face":528,"from":531,"geometry_use":{"pcurve":2114,"periodic_lift":[0,0],"sense":"Reverse"},"id":2114,"loop_ref":528,"next":2115,"prev":2113,"to":528,"twin":2139,"wire_ref":null},{"edge":1052,"face":528,"from":528,"geometry_use":{"pcurve":2115,"periodic_lift":[0,0],"sense":"Reverse"},"id":2115,"loop_ref":528,"next":2112,"prev":2114,"to":520,"twin":2100,"wire_ref":null},{"edge":1060,"face":529,"from":528,"geometry_use":{"pcurve":2116,"periodic_lift":[0,0],"sense":"Forward"},"id":2116,"loop_ref":529,"next":2117,"prev":2119,"to":532,"twin":2130,"wire_ref":null},{"edge":1061,"face":529,"from":532,"geometry_use":{"pcurve":2117,"periodic_lift":[0,0],"sense":"Reverse"},"id":2117,"loop_ref":529,"next":2118,"prev":2116,"to":533,"twin":2151,"wire_ref":null},{"edge":1062,"face":529,"from":533,"geometry_use":{"pcurve":2118,"periodic_lift":[0,0],"sense":"Reverse"},"id":2118,"loop_ref":529,"next":2119,"prev":2117,"to":529,"twin":2127,"wire_ref":null},{"edge":1053,"face":529,"from":529,"geometry_use":{"pcurve":2119,"periodic_lift":[0,0],"sense":"Forward"},"id":2119,"loop_ref":529,"next":2116,"prev":2118,"to":528,"twin":2101,"wire_ref":null},{"edge":1063,"face":530,"from":530,"geometry_use":{"pcurve":2120,"periodic_lift":[0,0],"sense":"Forward"},"id":2120,"loop_ref":530,"next":2121,"prev":2123,"to":534,"twin":2125,"wire_ref":null},{"edge":1064,"face":530,"from":534,"geometry_use":{"pcurve":2121,"periodic_lift":[0,0],"sense":"Forward"},"id":2121,"loop_ref":530,"next":2122,"prev":2120,"to":535,"twin":2155,"wire_ref":null},{"edge":1065,"face":530,"from":535,"geometry_use":{"pcurve":2122,"periodic_lift":[0,0],"sense":"Reverse"},"id":2122,"loop_ref":530,"next":2123,"prev":2121,"to":531,"twin":2132,"wire_ref":null},{"edge":1056,"face":530,"from":531,"geometry_use":{"pcurve":2123,"periodic_lift":[0,0],"sense":"Reverse"},"id":2123,"loop_ref":530,"next":2120,"prev":2122,"to":530,"twin":2105,"wire_ref":null},{"edge":1066,"face":531,"from":533,"geometry_use":{"pcurve":2124,"periodic_lift":[0,0],"sense":"Forward"},"id":2124,"loop_ref":531,"next":2125,"prev":2127,"to":534,"twin":2158,"wire_ref":null},{"edge":1063,"face":531,"from":534,"geometry_use":{"pcurve":2125,"periodic_lift":[0,0],"sense":"Reverse"},"id":2125,"loop_ref":531,"next":2126,"prev":2124,"to":530,"twin":2120,"wire_ref":null},{"edge":1058,"face":531,"from":530,"geometry_use":{"pcurve":2126,"periodic_lift":[0,0],"sense":"Reverse"},"id":2126,"loop_ref":531,"next":2127,"prev":2125,"to":529,"twin":2108,"wire_ref":null},{"edge":1062,"face":531,"from":529,"geometry_use":{"pcurve":2127,"periodic_lift":[0,0],"sense":"Forward"},"id":2127,"loop_ref":531,"next":2124,"prev":2126,"to":533,"twin":2118,"wire_ref":null},{"edge":1067,"face":532,"from":536,"geometry_use":{"pcurve":2128,"periodic_lift":[0,0],"sense":"Forward"},"id":2128,"loop_ref":532,"next":2129,"prev":2131,"to":537,"twin":2147,"wire_ref":null},{"edge":1068,"face":532,"from":537,"geometry_use":{"pcurve":2129,"periodic_lift":[0,0],"sense":"Reverse"},"id":2129,"loop_ref":532,"next":2130,"prev":2128,"to":532,"twin":2140,"wire_ref":null},{"edge":1060,"face":532,"from":532,"geometry_use":{"pcurve":2130,"periodic_lift":[0,0],"sense":"Reverse"},"id":2130,"loop_ref":532,"next":2131,"prev":2129,"to":528,"twin":2116,"wire_ref":null},{"edge":1069,"face":532,"from":528,"geometry_use":{"pcurve":2131,"periodic_lift":[0,0],"sense":"Forward"},"id":2131,"loop_ref":532,"next":2128,"prev":2130,"to":536,"twin":2138,"wire_ref":null},{"edge":1065,"face":533,"from":531,"geometry_use":{"pcurve":2132,"periodic_lift":[0,0],"sense":"Forward"},"id":2132,"loop_ref":533,"next":2133,"prev":2135,"to":535,"twin":2122,"wire_ref":null},{"edge":1070,"face":533,"from":535,"geometry_use":{"pcurve":2133,"periodic_lift":[0,0],"sense":"Forward"},"id":2133,"loop_ref":533,"next":2134,"prev":2132,"to":538,"twin":2142,"wire_ref":null},{"edge":1071,"face":533,"from":538,"geometry_use":{"pcurve":2134,"periodic_lift":[0,0],"sense":"Reverse"},"id":2134,"loop_ref":533,"next":2135,"prev":2133,"to":539,"twin":2145,"wire_ref":null},{"edge":1072,"face":533,"from":539,"geometry_use":{"pcurve":2135,"periodic_lift":[0,0],"sense":"Reverse"},"id":2135,"loop_ref":533,"next":2132,"prev":2134,"to":531,"twin":2136,"wire_ref":null},{"edge":1072,"face":534,"from":531,"geometry_use":{"pcurve":2136,"periodic_lift":[0,0],"sense":"Forward"},"id":2136,"loop_ref":534,"next":2137,"prev":2139,"to":539,"twin":2135,"wire_ref":null},{"edge":1073,"face":534,"from":539,"geometry_use":{"pcurve":2137,"periodic_lift":[0,0],"sense":"Reverse"},"id":2137,"loop_ref":534,"next":2138,"prev":2136,"to":536,"twin":2144,"wire_ref":null},{"edge":1069,"face":534,"from":536,"geometry_use":{"pcurve":2138,"periodic_lift":[0,0],"sense":"Reverse"},"id":2138,"loop_ref":534,"next":2139,"prev":2137,"to":528,"twin":2131,"wire_ref":null},{"edge":1059,"face":534,"from":528,"geometry_use":{"pcurve":2139,"periodic_lift":[0,0],"sense":"Forward"},"id":2139,"loop_ref":534,"next":2136,"prev":2138,"to":531,"twin":2114,"wire_ref":null},{"edge":1068,"face":535,"from":532,"geometry_use":{"pcurve":2140,"periodic_lift":[0,0],"sense":"Forward"},"id":2140,"loop_ref":535,"next":2141,"prev":2143,"to":537,"twin":2129,"wire_ref":null},{"edge":1074,"face":535,"from":537,"geometry_use":{"pcurve":2141,"periodic_lift":[0,0],"sense":"Forward"},"id":2141,"loop_ref":535,"next":2142,"prev":2140,"to":538,"twin":2146,"wire_ref":null},{"edge":1070,"face":535,"from":538,"geometry_use":{"pcurve":2142,"periodic_lift":[0,0],"sense":"Reverse"},"id":2142,"loop_ref":535,"next":2143,"prev":2141,"to":535,"twin":2133,"wire_ref":null},{"edge":1075,"face":535,"from":535,"geometry_use":{"pcurve":2143,"periodic_lift":[0,0],"sense":"Reverse"},"id":2143,"loop_ref":535,"next":2140,"prev":2142,"to":532,"twin":2160,"wire_ref":null},{"edge":1073,"face":536,"from":536,"geometry_use":{"pcurve":2144,"periodic_lift":[0,0],"sense":"Forward"},"id":2144,"loop_ref":536,"next":2145,"prev":2147,"to":539,"twin":2137,"wire_ref":null},{"edge":1071,"face":536,"from":539,"geometry_use":{"pcurve":2145,"periodic_lift":[0,0],"sense":"Forward"},"id":2145,"loop_ref":536,"next":2146,"prev":2144,"to":538,"twin":2134,"wire_ref":null},{"edge":1074,"face":536,"from":538,"geometry_use":{"pcurve":2146,"periodic_lift":[0,0],"sense":"Reverse"},"id":2146,"loop_ref":536,"next":2147,"prev":2145,"to":537,"twin":2141,"wire_ref":null},{"edge":1067,"face":536,"from":537,"geometry_use":{"pcurve":2147,"periodic_lift":[0,0],"sense":"Reverse"},"id":2147,"loop_ref":536,"next":2144,"prev":2146,"to":536,"twin":2128,"wire_ref":null},{"edge":1076,"face":537,"from":532,"geometry_use":{"pcurve":2148,"periodic_lift":[0,0],"sense":"Forward"},"id":2148,"loop_ref":537,"next":2149,"prev":2151,"to":540,"twin":2163,"wire_ref":null},{"edge":1077,"face":537,"from":540,"geometry_use":{"pcurve":2149,"periodic_lift":[0,0],"sense":"Reverse"},"id":2149,"loop_ref":537,"next":2150,"prev":2148,"to":541,"twin":2167,"wire_ref":null},{"edge":1078,"face":537,"from":541,"geometry_use":{"pcurve":2150,"periodic_lift":[0,0],"sense":"Reverse"},"id":2150,"loop_ref":537,"next":2151,"prev":2149,"to":533,"twin":2159,"wire_ref":null},{"edge":1061,"face":537,"from":533,"geometry_use":{"pcurve":2151,"periodic_lift":[0,0],"sense":"Forward"},"id":2151,"loop_ref":537,"next":2148,"prev":2150,"to":532,"twin":2117,"wire_ref":null},{"edge":1079,"face":538,"from":534,"geometry_use":{"pcurve":2152,"periodic_lift":[0,0],"sense":"Forward"},"id":2152,"loop_ref":538,"next":2153,"prev":2155,"to":542,"twin":2157,"wire_ref":null},{"edge":1080,"face":538,"from":542,"geometry_use":{"pcurve":2153,"periodic_lift":[0,0],"sense":"Forward"},"id":2153,"loop_ref":538,"next":2154,"prev":2152,"to":543,"twin":2171,"wire_ref":null},{"edge":1081,"face":538,"from":543,"geometry_use":{"pcurve":2154,"periodic_lift":[0,0],"sense":"Reverse"},"id":2154,"loop_ref":538,"next":2155,"prev":2153,"to":535,"twin":2161,"wire_ref":null},{"edge":1064,"face":538,"from":535,"geometry_use":{"pcurve":2155,"periodic_lift":[0,0],"sense":"Reverse"},"id":2155,"loop_ref":538,"next":2152,"prev":2154,"to":534,"twin":2121,"wire_ref":null},{"edge":1082,"face":539,"from":541,"geometry_use":{"pcurve":2156,"periodic_lift":[0,0],"sense":"Forward"},"id":2156,"loop_ref":539,"next":2157,"prev":2159,"to":542,"twin":2174,"wire_ref":null},{"edge":1079,"face":539,"from":542,"geometry_use":{"pcurve":2157,"periodic_lift":[0,0],"sense":"Reverse"},"id":2157,"loop_ref":539,"next":2158,"prev":2156,"to":534,"twin":2152,"wire_ref":null},{"edge":1066,"face":539,"from":534,"geometry_use":{"pcurve":2158,"periodic_lift":[0,0],"sense":"Reverse"},"id":2158,"loop_ref":539,"next":2159,"prev":2157,"to":533,"twin":2124,"wire_ref":null},{"edge":1078,"face":539,"from":533,"geometry_use":{"pcurve":2159,"periodic_lift":[0,0],"sense":"Forward"},"id":2159,"loop_ref":539,"next":2156,"prev":2158,"to":541,"twin":2150,"wire_ref":null},{"edge":1075,"face":540,"from":532,"geometry_use":{"pcurve":2160,"periodic_lift":[0,0],"sense":"Forward"},"id":2160,"loop_ref":540,"next":2161,"prev":2163,"to":535,"twin":2143,"wire_ref":null},{"edge":1081,"face":540,"from":535,"geometry_use":{"pcurve":2161,"periodic_lift":[0,0],"sense":"Forward"},"id":2161,"loop_ref":540,"next":2162,"prev":2160,"to":543,"twin":2154,"wire_ref":null},{"edge":1083,"face":540,"from":543,"geometry_use":{"pcurve":2162,"periodic_lift":[0,0],"sense":"Reverse"},"id":2162,"loop_ref":540,"next":2163,"prev":2161,"to":540,"twin":2187,"wire_ref":null},{"edge":1076,"face":540,"from":540,"geometry_use":{"pcurve":2163,"periodic_lift":[0,0],"sense":"Reverse"},"id":2163,"loop_ref":540,"next":2160,"prev":2162,"to":532,"twin":2148,"wire_ref":null},{"edge":1084,"face":541,"from":540,"geometry_use":{"pcurve":2164,"periodic_lift":[0,0],"sense":"Forward"},"id":2164,"loop_ref":541,"next":2165,"prev":2167,"to":544,"twin":2178,"wire_ref":null},{"edge":1085,"face":541,"from":544,"geometry_use":{"pcurve":2165,"periodic_lift":[0,0],"sense":"Reverse"},"id":2165,"loop_ref":541,"next":2166,"prev":2164,"to":545,"twin":2199,"wire_ref":null},{"edge":1086,"face":541,"from":545,"geometry_use":{"pcurve":2166,"periodic_lift":[0,0],"sense":"Reverse"},"id":2166,"loop_ref":541,"next":2167,"prev":2165,"to":541,"twin":2175,"wire_ref":null},{"edge":1077,"face":541,"from":541,"geometry_use":{"pcurve":2167,"periodic_lift":[0,0],"sense":"Forward"},"id":2167,"loop_ref":541,"next":2164,"prev":2166,"to":540,"twin":2149,"wire_ref":null},{"edge":1087,"face":542,"from":542,"geometry_use":{"pcurve":2168,"periodic_lift":[0,0],"sense":"Forward"},"id":2168,"loop_ref":542,"next":2169,"prev":2171,"to":546,"twin":2173,"wire_ref":null},{"edge":1088,"face":542,"from":546,"geometry_use":{"pcurve":2169,"periodic_lift":[0,0],"sense":"Forward"},"id":2169,"loop_ref":542,"next":2170,"prev":2168,"to":547,"twin":2203,"wire_ref":null},{"edge":1089,"face":542,"from":547,"geometry_use":{"pcurve":2170,"periodic_lift":[0,0],"sense":"Reverse"},"id":2170,"loop_ref":542,"next":2171,"prev":2169,"to":543,"twin":2180,"wire_ref":null},{"edge":1080,"face":542,"from":543,"geometry_use":{"pcurve":2171,"periodic_lift":[0,0],"sense":"Reverse"},"id":2171,"loop_ref":542,"next":2168,"prev":2170,"to":542,"twin":2153,"wire_ref":null},{"edge":1090,"face":543,"from":545,"geometry_use":{"pcurve":2172,"periodic_lift":[0,0],"sense":"Forward"},"id":2172,"loop_ref":543,"next":2173,"prev":2175,"to":546,"twin":2206,"wire_ref":null},{"edge":1087,"face":543,"from":546,"geometry_use":{"pcurve":2173,"periodic_lift":[0,0],"sense":"Reverse"},"id":2173,"loop_ref":543,"next":2174,"prev":2172,"to":542,"twin":2168,"wire_ref":null},{"edge":1082,"face":543,"from":542,"geometry_use":{"pcurve":2174,"periodic_lift":[0,0],"sense":"Reverse"},"id":2174,"loop_ref":543,"next":2175,"prev":2173,"to":541,"twin":2156,"wire_ref":null},{"edge":1086,"face":543,"from":541,"geometry_use":{"pcurve":2175,"periodic_lift":[0,0],"sense":"Forward"},"id":2175,"loop_ref":543,"next":2172,"prev":2174,"to":545,"twin":2166,"wire_ref":null},{"edge":1091,"face":544,"from":548,"geometry_use":{"pcurve":2176,"periodic_lift":[0,0],"sense":"Forward"},"id":2176,"loop_ref":544,"next":2177,"prev":2179,"to":549,"twin":2195,"wire_ref":null},{"edge":1092,"face":544,"from":549,"geometry_use":{"pcurve":2177,"periodic_lift":[0,0],"sense":"Reverse"},"id":2177,"loop_ref":544,"next":2178,"prev":2176,"to":544,"twin":2188,"wire_ref":null},{"edge":1084,"face":544,"from":544,"geometry_use":{"pcurve":2178,"periodic_lift":[0,0],"sense":"Reverse"},"id":2178,"loop_ref":544,"next":2179,"prev":2177,"to":540,"twin":2164,"wire_ref":null},{"edge":1093,"face":544,"from":540,"geometry_use":{"pcurve":2179,"periodic_lift":[0,0],"sense":"Forward"},"id":2179,"loop_ref":544,"next":2176,"prev":2178,"to":548,"twin":2186,"wire_ref":null},{"edge":1089,"face":545,"from":543,"geometry_use":{"pcurve":2180,"periodic_lift":[0,0],"sense":"Forward"},"id":2180,"loop_ref":545,"next":2181,"prev":2183,"to":547,"twin":2170,"wire_ref":null},{"edge":1094,"face":545,"from":547,"geometry_use":{"pcurve":2181,"periodic_lift":[0,0],"sense":"Forward"},"id":2181,"loop_ref":545,"next":2182,"prev":2180,"to":550,"twin":2190,"wire_ref":null},{"edge":1095,"face":545,"from":550,"geometry_use":{"pcurve":2182,"periodic_lift":[0,0],"sense":"Reverse"},"id":2182,"loop_ref":545,"next":2183,"prev":2181,"to":551,"twin":2193,"wire_ref":null},{"edge":1096,"face":545,"from":551,"geometry_use":{"pcurve":2183,"periodic_lift":[0,0],"sense":"Reverse"},"id":2183,"loop_ref":545,"next":2180,"prev":2182,"to":543,"twin":2184,"wire_ref":null},{"edge":1096,"face":546,"from":543,"geometry_use":{"pcurve":2184,"periodic_lift":[0,0],"sense":"Forward"},"id":2184,"loop_ref":546,"next":2185,"prev":2187,"to":551,"twin":2183,"wire_ref":null},{"edge":1097,"face":546,"from":551,"geometry_use":{"pcurve":2185,"periodic_lift":[0,0],"sense":"Reverse"},"id":2185,"loop_ref":546,"next":2186,"prev":2184,"to":548,"twin":2192,"wire_ref":null},{"edge":1093,"face":546,"from":548,"geometry_use":{"pcurve":2186,"periodic_lift":[0,0],"sense":"Reverse"},"id":2186,"loop_ref":546,"next":2187,"prev":2185,"to":540,"twin":2179,"wire_ref":null},{"edge":1083,"face":546,"from":540,"geometry_use":{"pcurve":2187,"periodic_lift":[0,0],"sense":"Forward"},"id":2187,"loop_ref":546,"next":2184,"prev":2186,"to":543,"twin":2162,"wire_ref":null},{"edge":1092,"face":547,"from":544,"geometry_use":{"pcurve":2188,"periodic_lift":[0,0],"sense":"Forward"},"id":2188,"loop_ref":547,"next":2189,"prev":2191,"to":549,"twin":2177,"wire_ref":null},{"edge":1098,"face":547,"from":549,"geometry_use":{"pcurve":2189,"periodic_lift":[0,0],"sense":"Forward"},"id":2189,"loop_ref":547,"next":2190,"prev":2188,"to":550,"twin":2194,"wire_ref":null},{"edge":1094,"face":547,"from":550,"geometry_use":{"pcurve":2190,"periodic_lift":[0,0],"sense":"Reverse"},"id":2190,"loop_ref":547,"next":2191,"prev":2189,"to":547,"twin":2181,"wire_ref":null},{"edge":1099,"face":547,"from":547,"geometry_use":{"pcurve":2191,"periodic_lift":[0,0],"sense":"Reverse"},"id":2191,"loop_ref":547,"next":2188,"prev":2190,"to":544,"twin":2208,"wire_ref":null},{"edge":1097,"face":548,"from":548,"geometry_use":{"pcurve":2192,"periodic_lift":[0,0],"sense":"Forward"},"id":2192,"loop_ref":548,"next":2193,"prev":2195,"to":551,"twin":2185,"wire_ref":null},{"edge":1095,"face":548,"from":551,"geometry_use":{"pcurve":2193,"periodic_lift":[0,0],"sense":"Forward"},"id":2193,"loop_ref":548,"next":2194,"prev":2192,"to":550,"twin":2182,"wire_ref":null},{"edge":1098,"face":548,"from":550,"geometry_use":{"pcurve":2194,"periodic_lift":[0,0],"sense":"Reverse"},"id":2194,"loop_ref":548,"next":2195,"prev":2193,"to":549,"twin":2189,"wire_ref":null},{"edge":1091,"face":548,"from":549,"geometry_use":{"pcurve":2195,"periodic_lift":[0,0],"sense":"Reverse"},"id":2195,"loop_ref":548,"next":2192,"prev":2194,"to":548,"twin":2176,"wire_ref":null},{"edge":1100,"face":549,"from":544,"geometry_use":{"pcurve":2196,"periodic_lift":[0,0],"sense":"Forward"},"id":2196,"loop_ref":549,"next":2197,"prev":2199,"to":552,"twin":2211,"wire_ref":null},{"edge":1101,"face":549,"from":552,"geometry_use":{"pcurve":2197,"periodic_lift":[0,0],"sense":"Reverse"},"id":2197,"loop_ref":549,"next":2198,"prev":2196,"to":553,"twin":2215,"wire_ref":null},{"edge":1102,"face":549,"from":553,"geometry_use":{"pcurve":2198,"periodic_lift":[0,0],"sense":"Reverse"},"id":2198,"loop_ref":549,"next":2199,"prev":2197,"to":545,"twin":2207,"wire_ref":null},{"edge":1085,"face":549,"from":545,"geometry_use":{"pcurve":2199,"periodic_lift":[0,0],"sense":"Forward"},"id":2199,"loop_ref":549,"next":2196,"prev":2198,"to":544,"twin":2165,"wire_ref":null},{"edge":1103,"face":550,"from":546,"geometry_use":{"pcurve":2200,"periodic_lift":[0,0],"sense":"Forward"},"id":2200,"loop_ref":550,"next":2201,"prev":2203,"to":554,"twin":2205,"wire_ref":null},{"edge":1104,"face":550,"from":554,"geometry_use":{"pcurve":2201,"periodic_lift":[0,0],"sense":"Forward"},"id":2201,"loop_ref":550,"next":2202,"prev":2200,"to":555,"twin":2219,"wire_ref":null},{"edge":1105,"face":550,"from":555,"geometry_use":{"pcurve":2202,"periodic_lift":[0,0],"sense":"Reverse"},"id":2202,"loop_ref":550,"next":2203,"prev":2201,"to":547,"twin":2209,"wire_ref":null},{"edge":1088,"face":550,"from":547,"geometry_use":{"pcurve":2203,"periodic_lift":[0,0],"sense":"Reverse"},"id":2203,"loop_ref":550,"next":2200,"prev":2202,"to":546,"twin":2169,"wire_ref":null},{"edge":1106,"face":551,"from":553,"geometry_use":{"pcurve":2204,"periodic_lift":[0,0],"sense":"Forward"},"id":2204,"loop_ref":551,"next":2205,"prev":2207,"to":554,"twin":2222,"wire_ref":null},{"edge":1103,"face":551,"from":554,"geometry_use":{"pcurve":2205,"periodic_lift":[0,0],"sense":"Reverse"},"id":2205,"loop_ref":551,"next":2206,"prev":2204,"to":546,"twin":2200,"wire_ref":null},{"edge":1090,"face":551,"from":546,"geometry_use":{"pcurve":2206,"periodic_lift":[0,0],"sense":"Reverse"},"id":2206,"loop_ref":551,"next":2207,"prev":2205,"to":545,"twin":2172,"wire_ref":null},{"edge":1102,"face":551,"from":545,"geometry_use":{"pcurve":2207,"periodic_lift":[0,0],"sense":"Forward"},"id":2207,"loop_ref":551,"next":2204,"prev":2206,"to":553,"twin":2198,"wire_ref":null},{"edge":1099,"face":552,"from":544,"geometry_use":{"pcurve":2208,"periodic_lift":[0,0],"sense":"Forward"},"id":2208,"loop_ref":552,"next":2209,"prev":2211,"to":547,"twin":2191,"wire_ref":null},{"edge":1105,"face":552,"from":547,"geometry_use":{"pcurve":2209,"periodic_lift":[0,0],"sense":"Forward"},"id":2209,"loop_ref":552,"next":2210,"prev":2208,"to":555,"twin":2202,"wire_ref":null},{"edge":1107,"face":552,"from":555,"geometry_use":{"pcurve":2210,"periodic_lift":[0,0],"sense":"Reverse"},"id":2210,"loop_ref":552,"next":2211,"prev":2209,"to":552,"twin":2235,"wire_ref":null},{"edge":1100,"face":552,"from":552,"geometry_use":{"pcurve":2211,"periodic_lift":[0,0],"sense":"Reverse"},"id":2211,"loop_ref":552,"next":2208,"prev":2210,"to":544,"twin":2196,"wire_ref":null},{"edge":1108,"face":553,"from":552,"geometry_use":{"pcurve":2212,"periodic_lift":[0,0],"sense":"Forward"},"id":2212,"loop_ref":553,"next":2213,"prev":2215,"to":556,"twin":2226,"wire_ref":null},{"edge":1109,"face":553,"from":556,"geometry_use":{"pcurve":2213,"periodic_lift":[0,0],"sense":"Reverse"},"id":2213,"loop_ref":553,"next":2214,"prev":2212,"to":557,"twin":2247,"wire_ref":null},{"edge":1110,"face":553,"from":557,"geometry_use":{"pcurve":2214,"periodic_lift":[0,0],"sense":"Reverse"},"id":2214,"loop_ref":553,"next":2215,"prev":2213,"to":553,"twin":2223,"wire_ref":null},{"edge":1101,"face":553,"from":553,"geometry_use":{"pcurve":2215,"periodic_lift":[0,0],"sense":"Forward"},"id":2215,"loop_ref":553,"next":2212,"prev":2214,"to":552,"twin":2197,"wire_ref":null},{"edge":1111,"face":554,"from":554,"geometry_use":{"pcurve":2216,"periodic_lift":[0,0],"sense":"Forward"},"id":2216,"loop_ref":554,"next":2217,"prev":2219,"to":558,"twin":2221,"wire_ref":null},{"edge":1112,"face":554,"from":558,"geometry_use":{"pcurve":2217,"periodic_lift":[0,0],"sense":"Forward"},"id":2217,"loop_ref":554,"next":2218,"prev":2216,"to":559,"twin":2251,"wire_ref":null},{"edge":1113,"face":554,"from":559,"geometry_use":{"pcurve":2218,"periodic_lift":[0,0],"sense":"Reverse"},"id":2218,"loop_ref":554,"next":2219,"prev":2217,"to":555,"twin":2228,"wire_ref":null},{"edge":1104,"face":554,"from":555,"geometry_use":{"pcurve":2219,"periodic_lift":[0,0],"sense":"Reverse"},"id":2219,"loop_ref":554,"next":2216,"prev":2218,"to":554,"twin":2201,"wire_ref":null},{"edge":1114,"face":555,"from":557,"geometry_use":{"pcurve":2220,"periodic_lift":[0,0],"sense":"Forward"},"id":2220,"loop_ref":555,"next":2221,"prev":2223,"to":558,"twin":2254,"wire_ref":null},{"edge":1111,"face":555,"from":558,"geometry_use":{"pcurve":2221,"periodic_lift":[0,0],"sense":"Reverse"},"id":2221,"loop_ref":555,"next":2222,"prev":2220,"to":554,"twin":2216,"wire_ref":null},{"edge":1106,"face":555,"from":554,"geometry_use":{"pcurve":2222,"periodic_lift":[0,0],"sense":"Reverse"},"id":2222,"loop_ref":555,"next":2223,"prev":2221,"to":553,"twin":2204,"wire_ref":null},{"edge":1110,"face":555,"from":553,"geometry_use":{"pcurve":2223,"periodic_lift":[0,0],"sense":"Forward"},"id":2223,"loop_ref":555,"next":2220,"prev":2222,"to":557,"twin":2214,"wire_ref":null},{"edge":1115,"face":556,"from":560,"geometry_use":{"pcurve":2224,"periodic_lift":[0,0],"sense":"Forward"},"id":2224,"loop_ref":556,"next":2225,"prev":2227,"to":561,"twin":2243,"wire_ref":null},{"edge":1116,"face":556,"from":561,"geometry_use":{"pcurve":2225,"periodic_lift":[0,0],"sense":"Reverse"},"id":2225,"loop_ref":556,"next":2226,"prev":2224,"to":556,"twin":2236,"wire_ref":null},{"edge":1108,"face":556,"from":556,"geometry_use":{"pcurve":2226,"periodic_lift":[0,0],"sense":"Reverse"},"id":2226,"loop_ref":556,"next":2227,"prev":2225,"to":552,"twin":2212,"wire_ref":null},{"edge":1117,"face":556,"from":552,"geometry_use":{"pcurve":2227,"periodic_lift":[0,0],"sense":"Forward"},"id":2227,"loop_ref":556,"next":2224,"prev":2226,"to":560,"twin":2234,"wire_ref":null},{"edge":1113,"face":557,"from":555,"geometry_use":{"pcurve":2228,"periodic_lift":[0,0],"sense":"Forward"},"id":2228,"loop_ref":557,"next":2229,"prev":2231,"to":559,"twin":2218,"wire_ref":null},{"edge":1118,"face":557,"from":559,"geometry_use":{"pcurve":2229,"periodic_lift":[0,0],"sense":"Forward"},"id":2229,"loop_ref":557,"next":2230,"prev":2228,"to":562,"twin":2238,"wire_ref":null},{"edge":1119,"face":557,"from":562,"geometry_use":{"pcurve":2230,"periodic_lift":[0,0],"sense":"Reverse"},"id":2230,"loop_ref":557,"next":2231,"prev":2229,"to":563,"twin":2241,"wire_ref":null},{"edge":1120,"face":557,"from":563,"geometry_use":{"pcurve":2231,"periodic_lift":[0,0],"sense":"Reverse"},"id":2231,"loop_ref":557,"next":2228,"prev":2230,"to":555,"twin":2232,"wire_ref":null},{"edge":1120,"face":558,"from":555,"geometry_use":{"pcurve":2232,"periodic_lift":[0,0],"sense":"Forward"},"id":2232,"loop_ref":558,"next":2233,"prev":2235,"to":563,"twin":2231,"wire_ref":null},{"edge":1121,"face":558,"from":563,"geometry_use":{"pcurve":2233,"periodic_lift":[0,0],"sense":"Reverse"},"id":2233,"loop_ref":558,"next":2234,"prev":2232,"to":560,"twin":2240,"wire_ref":null},{"edge":1117,"face":558,"from":560,"geometry_use":{"pcurve":2234,"periodic_lift":[0,0],"sense":"Reverse"},"id":2234,"loop_ref":558,"next":2235,"prev":2233,"to":552,"twin":2227,"wire_ref":null},{"edge":1107,"face":558,"from":552,"geometry_use":{"pcurve":2235,"periodic_lift":[0,0],"sense":"Forward"},"id":2235,"loop_ref":558,"next":2232,"prev":2234,"to":555,"twin":2210,"wire_ref":null},{"edge":1116,"face":559,"from":556,"geometry_use":{"pcurve":2236,"periodic_lift":[0,0],"sense":"Forward"},"id":2236,"loop_ref":559,"next":2237,"prev":2239,"to":561,"twin":2225,"wire_ref":null},{"edge":1122,"face":559,"from":561,"geometry_use":{"pcurve":2237,"periodic_lift":[0,0],"sense":"Forward"},"id":2237,"loop_ref":559,"next":2238,"prev":2236,"to":562,"twin":2242,"wire_ref":null},{"edge":1118,"face":559,"from":562,"geometry_use":{"pcurve":2238,"periodic_lift":[0,0],"sense":"Reverse"},"id":2238,"loop_ref":559,"next":2239,"prev":2237,"to":559,"twin":2229,"wire_ref":null},{"edge":1123,"face":559,"from":559,"geometry_use":{"pcurve":2239,"periodic_lift":[0,0],"sense":"Reverse"},"id":2239,"loop_ref":559,"next":2236,"prev":2238,"to":556,"twin":2256,"wire_ref":null},{"edge":1121,"face":560,"from":560,"geometry_use":{"pcurve":2240,"periodic_lift":[0,0],"sense":"Forward"},"id":2240,"loop_ref":560,"next":2241,"prev":2243,"to":563,"twin":2233,"wire_ref":null},{"edge":1119,"face":560,"from":563,"geometry_use":{"pcurve":2241,"periodic_lift":[0,0],"sense":"Forward"},"id":2241,"loop_ref":560,"next":2242,"prev":2240,"to":562,"twin":2230,"wire_ref":null},{"edge":1122,"face":560,"from":562,"geometry_use":{"pcurve":2242,"periodic_lift":[0,0],"sense":"Reverse"},"id":2242,"loop_ref":560,"next":2243,"prev":2241,"to":561,"twin":2237,"wire_ref":null},{"edge":1115,"face":560,"from":561,"geometry_use":{"pcurve":2243,"periodic_lift":[0,0],"sense":"Reverse"},"id":2243,"loop_ref":560,"next":2240,"prev":2242,"to":560,"twin":2224,"wire_ref":null},{"edge":1124,"face":561,"from":556,"geometry_use":{"pcurve":2244,"periodic_lift":[0,0],"sense":"Forward"},"id":2244,"loop_ref":561,"next":2245,"prev":2247,"to":564,"twin":2259,"wire_ref":null},{"edge":1125,"face":561,"from":564,"geometry_use":{"pcurve":2245,"periodic_lift":[0,0],"sense":"Reverse"},"id":2245,"loop_ref":561,"next":2246,"prev":2244,"to":565,"twin":2263,"wire_ref":null},{"edge":1126,"face":561,"from":565,"geometry_use":{"pcurve":2246,"periodic_lift":[0,0],"sense":"Reverse"},"id":2246,"loop_ref":561,"next":2247,"prev":2245,"to":557,"twin":2255,"wire_ref":null},{"edge":1109,"face":561,"from":557,"geometry_use":{"pcurve":2247,"periodic_lift":[0,0],"sense":"Forward"},"id":2247,"loop_ref":561,"next":2244,"prev":2246,"to":556,"twin":2213,"wire_ref":null},{"edge":1127,"face":562,"from":558,"geometry_use":{"pcurve":2248,"periodic_lift":[0,0],"sense":"Forward"},"id":2248,"loop_ref":562,"next":2249,"prev":2251,"to":566,"twin":2253,"wire_ref":null},{"edge":1128,"face":562,"from":566,"geometry_use":{"pcurve":2249,"periodic_lift":[0,0],"sense":"Forward"},"id":2249,"loop_ref":562,"next":2250,"prev":2248,"to":567,"twin":2267,"wire_ref":null},{"edge":1129,"face":562,"from":567,"geometry_use":{"pcurve":2250,"periodic_lift":[0,0],"sense":"Reverse"},"id":2250,"loop_ref":562,"next":2251,"prev":2249,"to":559,"twin":2257,"wire_ref":null},{"edge":1112,"face":562,"from":559,"geometry_use":{"pcurve":2251,"periodic_lift":[0,0],"sense":"Reverse"},"id":2251,"loop_ref":562,"next":2248,"prev":2250,"to":558,"twin":2217,"wire_ref":null},{"edge":1130,"face":563,"from":565,"geometry_use":{"pcurve":2252,"periodic_lift":[0,0],"sense":"Forward"},"id":2252,"loop_ref":563,"next":2253,"prev":2255,"to":566,"twin":2270,"wire_ref":null},{"edge":1127,"face":563,"from":566,"geometry_use":{"pcurve":2253,"periodic_lift":[0,0],"sense":"Reverse"},"id":2253,"loop_ref":563,"next":2254,"prev":2252,"to":558,"twin":2248,"wire_ref":null},{"edge":1114,"face":563,"from":558,"geometry_use":{"pcurve":2254,"periodic_lift":[0,0],"sense":"Reverse"},"id":2254,"loop_ref":563,"next":2255,"prev":2253,"to":557,"twin":2220,"wire_ref":null},{"edge":1126,"face":563,"from":557,"geometry_use":{"pcurve":2255,"periodic_lift":[0,0],"sense":"Forward"},"id":2255,"loop_ref":563,"next":2252,"prev":2254,"to":565,"twin":2246,"wire_ref":null},{"edge":1123,"face":564,"from":556,"geometry_use":{"pcurve":2256,"periodic_lift":[0,0],"sense":"Forward"},"id":2256,"loop_ref":564,"next":2257,"prev":2259,"to":559,"twin":2239,"wire_ref":null},{"edge":1129,"face":564,"from":559,"geometry_use":{"pcurve":2257,"periodic_lift":[0,0],"sense":"Forward"},"id":2257,"loop_ref":564,"next":2258,"prev":2256,"to":567,"twin":2250,"wire_ref":null},{"edge":1131,"face":564,"from":567,"geometry_use":{"pcurve":2258,"periodic_lift":[0,0],"sense":"Reverse"},"id":2258,"loop_ref":564,"next":2259,"prev":2257,"to":564,"twin":2283,"wire_ref":null},{"edge":1124,"face":564,"from":564,"geometry_use":{"pcurve":2259,"periodic_lift":[0,0],"sense":"Reverse"},"id":2259,"loop_ref":564,"next":2256,"prev":2258,"to":556,"twin":2244,"wire_ref":null},{"edge":1132,"face":565,"from":564,"geometry_use":{"pcurve":2260,"periodic_lift":[0,0],"sense":"Forward"},"id":2260,"loop_ref":565,"next":2261,"prev":2263,"to":568,"twin":2274,"wire_ref":null},{"edge":1133,"face":565,"from":568,"geometry_use":{"pcurve":2261,"periodic_lift":[0,0],"sense":"Reverse"},"id":2261,"loop_ref":565,"next":2262,"prev":2260,"to":569,"twin":2295,"wire_ref":null},{"edge":1134,"face":565,"from":569,"geometry_use":{"pcurve":2262,"periodic_lift":[0,0],"sense":"Reverse"},"id":2262,"loop_ref":565,"next":2263,"prev":2261,"to":565,"twin":2271,"wire_ref":null},{"edge":1125,"face":565,"from":565,"geometry_use":{"pcurve":2263,"periodic_lift":[0,0],"sense":"Forward"},"id":2263,"loop_ref":565,"next":2260,"prev":2262,"to":564,"twin":2245,"wire_ref":null},{"edge":1135,"face":566,"from":566,"geometry_use":{"pcurve":2264,"periodic_lift":[0,0],"sense":"Forward"},"id":2264,"loop_ref":566,"next":2265,"prev":2267,"to":570,"twin":2269,"wire_ref":null},{"edge":1136,"face":566,"from":570,"geometry_use":{"pcurve":2265,"periodic_lift":[0,0],"sense":"Forward"},"id":2265,"loop_ref":566,"next":2266,"prev":2264,"to":571,"twin":2299,"wire_ref":null},{"edge":1137,"face":566,"from":571,"geometry_use":{"pcurve":2266,"periodic_lift":[0,0],"sense":"Reverse"},"id":2266,"loop_ref":566,"next":2267,"prev":2265,"to":567,"twin":2276,"wire_ref":null},{"edge":1128,"face":566,"from":567,"geometry_use":{"pcurve":2267,"periodic_lift":[0,0],"sense":"Reverse"},"id":2267,"loop_ref":566,"next":2264,"prev":2266,"to":566,"twin":2249,"wire_ref":null},{"edge":1138,"face":567,"from":569,"geometry_use":{"pcurve":2268,"periodic_lift":[0,0],"sense":"Forward"},"id":2268,"loop_ref":567,"next":2269,"prev":2271,"to":570,"twin":2302,"wire_ref":null},{"edge":1135,"face":567,"from":570,"geometry_use":{"pcurve":2269,"periodic_lift":[0,0],"sense":"Reverse"},"id":2269,"loop_ref":567,"next":2270,"prev":2268,"to":566,"twin":2264,"wire_ref":null},{"edge":1130,"face":567,"from":566,"geometry_use":{"pcurve":2270,"periodic_lift":[0,0],"sense":"Reverse"},"id":2270,"loop_ref":567,"next":2271,"prev":2269,"to":565,"twin":2252,"wire_ref":null},{"edge":1134,"face":567,"from":565,"geometry_use":{"pcurve":2271,"periodic_lift":[0,0],"sense":"Forward"},"id":2271,"loop_ref":567,"next":2268,"prev":2270,"to":569,"twin":2262,"wire_ref":null},{"edge":1139,"face":568,"from":572,"geometry_use":{"pcurve":2272,"periodic_lift":[0,0],"sense":"Forward"},"id":2272,"loop_ref":568,"next":2273,"prev":2275,"to":573,"twin":2291,"wire_ref":null},{"edge":1140,"face":568,"from":573,"geometry_use":{"pcurve":2273,"periodic_lift":[0,0],"sense":"Reverse"},"id":2273,"loop_ref":568,"next":2274,"prev":2272,"to":568,"twin":2284,"wire_ref":null},{"edge":1132,"face":568,"from":568,"geometry_use":{"pcurve":2274,"periodic_lift":[0,0],"sense":"Reverse"},"id":2274,"loop_ref":568,"next":2275,"prev":2273,"to":564,"twin":2260,"wire_ref":null},{"edge":1141,"face":568,"from":564,"geometry_use":{"pcurve":2275,"periodic_lift":[0,0],"sense":"Forward"},"id":2275,"loop_ref":568,"next":2272,"prev":2274,"to":572,"twin":2282,"wire_ref":null},{"edge":1137,"face":569,"from":567,"geometry_use":{"pcurve":2276,"periodic_lift":[0,0],"sense":"Forward"},"id":2276,"loop_ref":569,"next":2277,"prev":2279,"to":571,"twin":2266,"wire_ref":null},{"edge":1142,"face":569,"from":571,"geometry_use":{"pcurve":2277,"periodic_lift":[0,0],"sense":"Forward"},"id":2277,"loop_ref":569,"next":2278,"prev":2276,"to":574,"twin":2286,"wire_ref":null},{"edge":1143,"face":569,"from":574,"geometry_use":{"pcurve":2278,"periodic_lift":[0,0],"sense":"Reverse"},"id":2278,"loop_ref":569,"next":2279,"prev":2277,"to":575,"twin":2289,"wire_ref":null},{"edge":1144,"face":569,"from":575,"geometry_use":{"pcurve":2279,"periodic_lift":[0,0],"sense":"Reverse"},"id":2279,"loop_ref":569,"next":2276,"prev":2278,"to":567,"twin":2280,"wire_ref":null},{"edge":1144,"face":570,"from":567,"geometry_use":{"pcurve":2280,"periodic_lift":[0,0],"sense":"Forward"},"id":2280,"loop_ref":570,"next":2281,"prev":2283,"to":575,"twin":2279,"wire_ref":null},{"edge":1145,"face":570,"from":575,"geometry_use":{"pcurve":2281,"periodic_lift":[0,0],"sense":"Reverse"},"id":2281,"loop_ref":570,"next":2282,"prev":2280,"to":572,"twin":2288,"wire_ref":null},{"edge":1141,"face":570,"from":572,"geometry_use":{"pcurve":2282,"periodic_lift":[0,0],"sense":"Reverse"},"id":2282,"loop_ref":570,"next":2283,"prev":2281,"to":564,"twin":2275,"wire_ref":null},{"edge":1131,"face":570,"from":564,"geometry_use":{"pcurve":2283,"periodic_lift":[0,0],"sense":"Forward"},"id":2283,"loop_ref":570,"next":2280,"prev":2282,"to":567,"twin":2258,"wire_ref":null},{"edge":1140,"face":571,"from":568,"geometry_use":{"pcurve":2284,"periodic_lift":[0,0],"sense":"Forward"},"id":2284,"loop_ref":571,"next":2285,"prev":2287,"to":573,"twin":2273,"wire_ref":null},{"edge":1146,"face":571,"from":573,"geometry_use":{"pcurve":2285,"periodic_lift":[0,0],"sense":"Forward"},"id":2285,"loop_ref":571,"next":2286,"prev":2284,"to":574,"twin":2290,"wire_ref":null},{"edge":1142,"face":571,"from":574,"geometry_use":{"pcurve":2286,"periodic_lift":[0,0],"sense":"Reverse"},"id":2286,"loop_ref":571,"next":2287,"prev":2285,"to":571,"twin":2277,"wire_ref":null},{"edge":1147,"face":571,"from":571,"geometry_use":{"pcurve":2287,"periodic_lift":[0,0],"sense":"Reverse"},"id":2287,"loop_ref":571,"next":2284,"prev":2286,"to":568,"twin":2304,"wire_ref":null},{"edge":1145,"face":572,"from":572,"geometry_use":{"pcurve":2288,"periodic_lift":[0,0],"sense":"Forward"},"id":2288,"loop_ref":572,"next":2289,"prev":2291,"to":575,"twin":2281,"wire_ref":null},{"edge":1143,"face":572,"from":575,"geometry_use":{"pcurve":2289,"periodic_lift":[0,0],"sense":"Forward"},"id":2289,"loop_ref":572,"next":2290,"prev":2288,"to":574,"twin":2278,"wire_ref":null},{"edge":1146,"face":572,"from":574,"geometry_use":{"pcurve":2290,"periodic_lift":[0,0],"sense":"Reverse"},"id":2290,"loop_ref":572,"next":2291,"prev":2289,"to":573,"twin":2285,"wire_ref":null},{"edge":1139,"face":572,"from":573,"geometry_use":{"pcurve":2291,"periodic_lift":[0,0],"sense":"Reverse"},"id":2291,"loop_ref":572,"next":2288,"prev":2290,"to":572,"twin":2272,"wire_ref":null},{"edge":1148,"face":573,"from":568,"geometry_use":{"pcurve":2292,"periodic_lift":[0,0],"sense":"Forward"},"id":2292,"loop_ref":573,"next":2293,"prev":2295,"to":576,"twin":2307,"wire_ref":null},{"edge":1149,"face":573,"from":576,"geometry_use":{"pcurve":2293,"periodic_lift":[0,0],"sense":"Reverse"},"id":2293,"loop_ref":573,"next":2294,"prev":2292,"to":577,"twin":2311,"wire_ref":null},{"edge":1150,"face":573,"from":577,"geometry_use":{"pcurve":2294,"periodic_lift":[0,0],"sense":"Reverse"},"id":2294,"loop_ref":573,"next":2295,"prev":2293,"to":569,"twin":2303,"wire_ref":null},{"edge":1133,"face":573,"from":569,"geometry_use":{"pcurve":2295,"periodic_lift":[0,0],"sense":"Forward"},"id":2295,"loop_ref":573,"next":2292,"prev":2294,"to":568,"twin":2261,"wire_ref":null},{"edge":1151,"face":574,"from":570,"geometry_use":{"pcurve":2296,"periodic_lift":[0,0],"sense":"Forward"},"id":2296,"loop_ref":574,"next":2297,"prev":2299,"to":578,"twin":2301,"wire_ref":null},{"edge":1152,"face":574,"from":578,"geometry_use":{"pcurve":2297,"periodic_lift":[0,0],"sense":"Forward"},"id":2297,"loop_ref":574,"next":2298,"prev":2296,"to":579,"twin":2315,"wire_ref":null},{"edge":1153,"face":574,"from":579,"geometry_use":{"pcurve":2298,"periodic_lift":[0,0],"sense":"Reverse"},"id":2298,"loop_ref":574,"next":2299,"prev":2297,"to":571,"twin":2305,"wire_ref":null},{"edge":1136,"face":574,"from":571,"geometry_use":{"pcurve":2299,"periodic_lift":[0,0],"sense":"Reverse"},"id":2299,"loop_ref":574,"next":2296,"prev":2298,"to":570,"twin":2265,"wire_ref":null},{"edge":1154,"face":575,"from":577,"geometry_use":{"pcurve":2300,"periodic_lift":[0,0],"sense":"Forward"},"id":2300,"loop_ref":575,"next":2301,"prev":2303,"to":578,"twin":2318,"wire_ref":null},{"edge":1151,"face":575,"from":578,"geometry_use":{"pcurve":2301,"periodic_lift":[0,0],"sense":"Reverse"},"id":2301,"loop_ref":575,"next":2302,"prev":2300,"to":570,"twin":2296,"wire_ref":null},{"edge":1138,"face":575,"from":570,"geometry_use":{"pcurve":2302,"periodic_lift":[0,0],"sense":"Reverse"},"id":2302,"loop_ref":575,"next":2303,"prev":2301,"to":569,"twin":2268,"wire_ref":null},{"edge":1150,"face":575,"from":569,"geometry_use":{"pcurve":2303,"periodic_lift":[0,0],"sense":"Forward"},"id":2303,"loop_ref":575,"next":2300,"prev":2302,"to":577,"twin":2294,"wire_ref":null},{"edge":1147,"face":576,"from":568,"geometry_use":{"pcurve":2304,"periodic_lift":[0,0],"sense":"Forward"},"id":2304,"loop_ref":576,"next":2305,"prev":2307,"to":571,"twin":2287,"wire_ref":null},{"edge":1153,"face":576,"from":571,"geometry_use":{"pcurve":2305,"periodic_lift":[0,0],"sense":"Forward"},"id":2305,"loop_ref":576,"next":2306,"prev":2304,"to":579,"twin":2298,"wire_ref":null},{"edge":1155,"face":576,"from":579,"geometry_use":{"pcurve":2306,"periodic_lift":[0,0],"sense":"Reverse"},"id":2306,"loop_ref":576,"next":2307,"prev":2305,"to":576,"twin":2331,"wire_ref":null},{"edge":1148,"face":576,"from":576,"geometry_use":{"pcurve":2307,"periodic_lift":[0,0],"sense":"Reverse"},"id":2307,"loop_ref":576,"next":2304,"prev":2306,"to":568,"twin":2292,"wire_ref":null},{"edge":1156,"face":577,"from":576,"geometry_use":{"pcurve":2308,"periodic_lift":[0,0],"sense":"Forward"},"id":2308,"loop_ref":577,"next":2309,"prev":2311,"to":580,"twin":2322,"wire_ref":null},{"edge":1157,"face":577,"from":580,"geometry_use":{"pcurve":2309,"periodic_lift":[0,0],"sense":"Reverse"},"id":2309,"loop_ref":577,"next":2310,"prev":2308,"to":581,"twin":2343,"wire_ref":null},{"edge":1158,"face":577,"from":581,"geometry_use":{"pcurve":2310,"periodic_lift":[0,0],"sense":"Reverse"},"id":2310,"loop_ref":577,"next":2311,"prev":2309,"to":577,"twin":2319,"wire_ref":null},{"edge":1149,"face":577,"from":577,"geometry_use":{"pcurve":2311,"periodic_lift":[0,0],"sense":"Forward"},"id":2311,"loop_ref":577,"next":2308,"prev":2310,"to":576,"twin":2293,"wire_ref":null},{"edge":1159,"face":578,"from":578,"geometry_use":{"pcurve":2312,"periodic_lift":[0,0],"sense":"Forward"},"id":2312,"loop_ref":578,"next":2313,"prev":2315,"to":582,"twin":2317,"wire_ref":null},{"edge":1160,"face":578,"from":582,"geometry_use":{"pcurve":2313,"periodic_lift":[0,0],"sense":"Forward"},"id":2313,"loop_ref":578,"next":2314,"prev":2312,"to":583,"twin":2347,"wire_ref":null},{"edge":1161,"face":578,"from":583,"geometry_use":{"pcurve":2314,"periodic_lift":[0,0],"sense":"Reverse"},"id":2314,"loop_ref":578,"next":2315,"prev":2313,"to":579,"twin":2324,"wire_ref":null},{"edge":1152,"face":578,"from":579,"geometry_use":{"pcurve":2315,"periodic_lift":[0,0],"sense":"Reverse"},"id":2315,"loop_ref":578,"next":2312,"prev":2314,"to":578,"twin":2297,"wire_ref":null},{"edge":1162,"face":579,"from":581,"geometry_use":{"pcurve":2316,"periodic_lift":[0,0],"sense":"Forward"},"id":2316,"loop_ref":579,"next":2317,"prev":2319,"to":582,"twin":2350,"wire_ref":null},{"edge":1159,"face":579,"from":582,"geometry_use":{"pcurve":2317,"periodic_lift":[0,0],"sense":"Reverse"},"id":2317,"loop_ref":579,"next":2318,"prev":2316,"to":578,"twin":2312,"wire_ref":null},{"edge":1154,"face":579,"from":578,"geometry_use":{"pcurve":2318,"periodic_lift":[0,0],"sense":"Reverse"},"id":2318,"loop_ref":579,"next":2319,"prev":2317,"to":577,"twin":2300,"wire_ref":null},{"edge":1158,"face":579,"from":577,"geometry_use":{"pcurve":2319,"periodic_lift":[0,0],"sense":"Forward"},"id":2319,"loop_ref":579,"next":2316,"prev":2318,"to":581,"twin":2310,"wire_ref":null},{"edge":1163,"face":580,"from":584,"geometry_use":{"pcurve":2320,"periodic_lift":[0,0],"sense":"Forward"},"id":2320,"loop_ref":580,"next":2321,"prev":2323,"to":585,"twin":2339,"wire_ref":null},{"edge":1164,"face":580,"from":585,"geometry_use":{"pcurve":2321,"periodic_lift":[0,0],"sense":"Reverse"},"id":2321,"loop_ref":580,"next":2322,"prev":2320,"to":580,"twin":2332,"wire_ref":null},{"edge":1156,"face":580,"from":580,"geometry_use":{"pcurve":2322,"periodic_lift":[0,0],"sense":"Reverse"},"id":2322,"loop_ref":580,"next":2323,"prev":2321,"to":576,"twin":2308,"wire_ref":null},{"edge":1165,"face":580,"from":576,"geometry_use":{"pcurve":2323,"periodic_lift":[0,0],"sense":"Forward"},"id":2323,"loop_ref":580,"next":2320,"prev":2322,"to":584,"twin":2330,"wire_ref":null},{"edge":1161,"face":581,"from":579,"geometry_use":{"pcurve":2324,"periodic_lift":[0,0],"sense":"Forward"},"id":2324,"loop_ref":581,"next":2325,"prev":2327,"to":583,"twin":2314,"wire_ref":null},{"edge":1166,"face":581,"from":583,"geometry_use":{"pcurve":2325,"periodic_lift":[0,0],"sense":"Forward"},"id":2325,"loop_ref":581,"next":2326,"prev":2324,"to":586,"twin":2334,"wire_ref":null},{"edge":1167,"face":581,"from":586,"geometry_use":{"pcurve":2326,"periodic_lift":[0,0],"sense":"Reverse"},"id":2326,"loop_ref":581,"next":2327,"prev":2325,"to":587,"twin":2337,"wire_ref":null},{"edge":1168,"face":581,"from":587,"geometry_use":{"pcurve":2327,"periodic_lift":[0,0],"sense":"Reverse"},"id":2327,"loop_ref":581,"next":2324,"prev":2326,"to":579,"twin":2328,"wire_ref":null},{"edge":1168,"face":582,"from":579,"geometry_use":{"pcurve":2328,"periodic_lift":[0,0],"sense":"Forward"},"id":2328,"loop_ref":582,"next":2329,"prev":2331,"to":587,"twin":2327,"wire_ref":null},{"edge":1169,"face":582,"from":587,"geometry_use":{"pcurve":2329,"periodic_lift":[0,0],"sense":"Reverse"},"id":2329,"loop_ref":582,"next":2330,"prev":2328,"to":584,"twin":2336,"wire_ref":null},{"edge":1165,"face":582,"from":584,"geometry_use":{"pcurve":2330,"periodic_lift":[0,0],"sense":"Reverse"},"id":2330,"loop_ref":582,"next":2331,"prev":2329,"to":576,"twin":2323,"wire_ref":null},{"edge":1155,"face":582,"from":576,"geometry_use":{"pcurve":2331,"periodic_lift":[0,0],"sense":"Forward"},"id":2331,"loop_ref":582,"next":2328,"prev":2330,"to":579,"twin":2306,"wire_ref":null},{"edge":1164,"face":583,"from":580,"geometry_use":{"pcurve":2332,"periodic_lift":[0,0],"sense":"Forward"},"id":2332,"loop_ref":583,"next":2333,"prev":2335,"to":585,"twin":2321,"wire_ref":null},{"edge":1170,"face":583,"from":585,"geometry_use":{"pcurve":2333,"periodic_lift":[0,0],"sense":"Forward"},"id":2333,"loop_ref":583,"next":2334,"prev":2332,"to":586,"twin":2338,"wire_ref":null},{"edge":1166,"face":583,"from":586,"geometry_use":{"pcurve":2334,"periodic_lift":[0,0],"sense":"Reverse"},"id":2334,"loop_ref":583,"next":2335,"prev":2333,"to":583,"twin":2325,"wire_ref":null},{"edge":1171,"face":583,"from":583,"geometry_use":{"pcurve":2335,"periodic_lift":[0,0],"sense":"Reverse"},"id":2335,"loop_ref":583,"next":2332,"prev":2334,"to":580,"twin":2352,"wire_ref":null},{"edge":1169,"face":584,"from":584,"geometry_use":{"pcurve":2336,"periodic_lift":[0,0],"sense":"Forward"},"id":2336,"loop_ref":584,"next":2337,"prev":2339,"to":587,"twin":2329,"wire_ref":null},{"edge":1167,"face":584,"from":587,"geometry_use":{"pcurve":2337,"periodic_lift":[0,0],"sense":"Forward"},"id":2337,"loop_ref":584,"next":2338,"prev":2336,"to":586,"twin":2326,"wire_ref":null},{"edge":1170,"face":584,"from":586,"geometry_use":{"pcurve":2338,"periodic_lift":[0,0],"sense":"Reverse"},"id":2338,"loop_ref":584,"next":2339,"prev":2337,"to":585,"twin":2333,"wire_ref":null},{"edge":1163,"face":584,"from":585,"geometry_use":{"pcurve":2339,"periodic_lift":[0,0],"sense":"Reverse"},"id":2339,"loop_ref":584,"next":2336,"prev":2338,"to":584,"twin":2320,"wire_ref":null},{"edge":1172,"face":585,"from":580,"geometry_use":{"pcurve":2340,"periodic_lift":[0,0],"sense":"Forward"},"id":2340,"loop_ref":585,"next":2341,"prev":2343,"to":588,"twin":2355,"wire_ref":null},{"edge":1173,"face":585,"from":588,"geometry_use":{"pcurve":2341,"periodic_lift":[0,0],"sense":"Reverse"},"id":2341,"loop_ref":585,"next":2342,"prev":2340,"to":589,"twin":2359,"wire_ref":null},{"edge":1174,"face":585,"from":589,"geometry_use":{"pcurve":2342,"periodic_lift":[0,0],"sense":"Reverse"},"id":2342,"loop_ref":585,"next":2343,"prev":2341,"to":581,"twin":2351,"wire_ref":null},{"edge":1157,"face":585,"from":581,"geometry_use":{"pcurve":2343,"periodic_lift":[0,0],"sense":"Forward"},"id":2343,"loop_ref":585,"next":2340,"prev":2342,"to":580,"twin":2309,"wire_ref":null},{"edge":1175,"face":586,"from":582,"geometry_use":{"pcurve":2344,"periodic_lift":[0,0],"sense":"Forward"},"id":2344,"loop_ref":586,"next":2345,"prev":2347,"to":590,"twin":2349,"wire_ref":null},{"edge":1176,"face":586,"from":590,"geometry_use":{"pcurve":2345,"periodic_lift":[0,0],"sense":"Forward"},"id":2345,"loop_ref":586,"next":2346,"prev":2344,"to":591,"twin":2363,"wire_ref":null},{"edge":1177,"face":586,"from":591,"geometry_use":{"pcurve":2346,"periodic_lift":[0,0],"sense":"Reverse"},"id":2346,"loop_ref":586,"next":2347,"prev":2345,"to":583,"twin":2353,"wire_ref":null},{"edge":1160,"face":586,"from":583,"geometry_use":{"pcurve":2347,"periodic_lift":[0,0],"sense":"Reverse"},"id":2347,"loop_ref":586,"next":2344,"prev":2346,"to":582,"twin":2313,"wire_ref":null},{"edge":1178,"face":587,"from":589,"geometry_use":{"pcurve":2348,"periodic_lift":[0,0],"sense":"Forward"},"id":2348,"loop_ref":587,"next":2349,"prev":2351,"to":590,"twin":2366,"wire_ref":null},{"edge":1175,"face":587,"from":590,"geometry_use":{"pcurve":2349,"periodic_lift":[0,0],"sense":"Reverse"},"id":2349,"loop_ref":587,"next":2350,"prev":2348,"to":582,"twin":2344,"wire_ref":null},{"edge":1162,"face":587,"from":582,"geometry_use":{"pcurve":2350,"periodic_lift":[0,0],"sense":"Reverse"},"id":2350,"loop_ref":587,"next":2351,"prev":2349,"to":581,"twin":2316,"wire_ref":null},{"edge":1174,"face":587,"from":581,"geometry_use":{"pcurve":2351,"periodic_lift":[0,0],"sense":"Forward"},"id":2351,"loop_ref":587,"next":2348,"prev":2350,"to":589,"twin":2342,"wire_ref":null},{"edge":1171,"face":588,"from":580,"geometry_use":{"pcurve":2352,"periodic_lift":[0,0],"sense":"Forward"},"id":2352,"loop_ref":588,"next":2353,"prev":2355,"to":583,"twin":2335,"wire_ref":null},{"edge":1177,"face":588,"from":583,"geometry_use":{"pcurve":2353,"periodic_lift":[0,0],"sense":"Forward"},"id":2353,"loop_ref":588,"next":2354,"prev":2352,"to":591,"twin":2346,"wire_ref":null},{"edge":1179,"face":588,"from":591,"geometry_use":{"pcurve":2354,"periodic_lift":[0,0],"sense":"Reverse"},"id":2354,"loop_ref":588,"next":2355,"prev":2353,"to":588,"twin":2379,"wire_ref":null},{"edge":1172,"face":588,"from":588,"geometry_use":{"pcurve":2355,"periodic_lift":[0,0],"sense":"Reverse"},"id":2355,"loop_ref":588,"next":2352,"prev":2354,"to":580,"twin":2340,"wire_ref":null},{"edge":1180,"face":589,"from":588,"geometry_use":{"pcurve":2356,"periodic_lift":[0,0],"sense":"Forward"},"id":2356,"loop_ref":589,"next":2357,"prev":2359,"to":592,"twin":2370,"wire_ref":null},{"edge":1181,"face":589,"from":592,"geometry_use":{"pcurve":2357,"periodic_lift":[0,0],"sense":"Reverse"},"id":2357,"loop_ref":589,"next":2358,"prev":2356,"to":593,"twin":2391,"wire_ref":null},{"edge":1182,"face":589,"from":593,"geometry_use":{"pcurve":2358,"periodic_lift":[0,0],"sense":"Reverse"},"id":2358,"loop_ref":589,"next":2359,"prev":2357,"to":589,"twin":2367,"wire_ref":null},{"edge":1173,"face":589,"from":589,"geometry_use":{"pcurve":2359,"periodic_lift":[0,0],"sense":"Forward"},"id":2359,"loop_ref":589,"next":2356,"prev":2358,"to":588,"twin":2341,"wire_ref":null},{"edge":1183,"face":590,"from":590,"geometry_use":{"pcurve":2360,"periodic_lift":[0,0],"sense":"Forward"},"id":2360,"loop_ref":590,"next":2361,"prev":2363,"to":594,"twin":2365,"wire_ref":null},{"edge":1184,"face":590,"from":594,"geometry_use":{"pcurve":2361,"periodic_lift":[0,0],"sense":"Forward"},"id":2361,"loop_ref":590,"next":2362,"prev":2360,"to":595,"twin":2395,"wire_ref":null},{"edge":1185,"face":590,"from":595,"geometry_use":{"pcurve":2362,"periodic_lift":[0,0],"sense":"Reverse"},"id":2362,"loop_ref":590,"next":2363,"prev":2361,"to":591,"twin":2372,"wire_ref":null},{"edge":1176,"face":590,"from":591,"geometry_use":{"pcurve":2363,"periodic_lift":[0,0],"sense":"Reverse"},"id":2363,"loop_ref":590,"next":2360,"prev":2362,"to":590,"twin":2345,"wire_ref":null},{"edge":1186,"face":591,"from":593,"geometry_use":{"pcurve":2364,"periodic_lift":[0,0],"sense":"Forward"},"id":2364,"loop_ref":591,"next":2365,"prev":2367,"to":594,"twin":2398,"wire_ref":null},{"edge":1183,"face":591,"from":594,"geometry_use":{"pcurve":2365,"periodic_lift":[0,0],"sense":"Reverse"},"id":2365,"loop_ref":591,"next":2366,"prev":2364,"to":590,"twin":2360,"wire_ref":null},{"edge":1178,"face":591,"from":590,"geometry_use":{"pcurve":2366,"periodic_lift":[0,0],"sense":"Reverse"},"id":2366,"loop_ref":591,"next":2367,"prev":2365,"to":589,"twin":2348,"wire_ref":null},{"edge":1182,"face":591,"from":589,"geometry_use":{"pcurve":2367,"periodic_lift":[0,0],"sense":"Forward"},"id":2367,"loop_ref":591,"next":2364,"prev":2366,"to":593,"twin":2358,"wire_ref":null},{"edge":1187,"face":592,"from":596,"geometry_use":{"pcurve":2368,"periodic_lift":[0,0],"sense":"Forward"},"id":2368,"loop_ref":592,"next":2369,"prev":2371,"to":597,"twin":2387,"wire_ref":null},{"edge":1188,"face":592,"from":597,"geometry_use":{"pcurve":2369,"periodic_lift":[0,0],"sense":"Reverse"},"id":2369,"loop_ref":592,"next":2370,"prev":2368,"to":592,"twin":2380,"wire_ref":null},{"edge":1180,"face":592,"from":592,"geometry_use":{"pcurve":2370,"periodic_lift":[0,0],"sense":"Reverse"},"id":2370,"loop_ref":592,"next":2371,"prev":2369,"to":588,"twin":2356,"wire_ref":null},{"edge":1189,"face":592,"from":588,"geometry_use":{"pcurve":2371,"periodic_lift":[0,0],"sense":"Forward"},"id":2371,"loop_ref":592,"next":2368,"prev":2370,"to":596,"twin":2378,"wire_ref":null},{"edge":1185,"face":593,"from":591,"geometry_use":{"pcurve":2372,"periodic_lift":[0,0],"sense":"Forward"},"id":2372,"loop_ref":593,"next":2373,"prev":2375,"to":595,"twin":2362,"wire_ref":null},{"edge":1190,"face":593,"from":595,"geometry_use":{"pcurve":2373,"periodic_lift":[0,0],"sense":"Forward"},"id":2373,"loop_ref":593,"next":2374,"prev":2372,"to":598,"twin":2382,"wire_ref":null},{"edge":1191,"face":593,"from":598,"geometry_use":{"pcurve":2374,"periodic_lift":[0,0],"sense":"Reverse"},"id":2374,"loop_ref":593,"next":2375,"prev":2373,"to":599,"twin":2385,"wire_ref":null},{"edge":1192,"face":593,"from":599,"geometry_use":{"pcurve":2375,"periodic_lift":[0,0],"sense":"Reverse"},"id":2375,"loop_ref":593,"next":2372,"prev":2374,"to":591,"twin":2376,"wire_ref":null},{"edge":1192,"face":594,"from":591,"geometry_use":{"pcurve":2376,"periodic_lift":[0,0],"sense":"Forward"},"id":2376,"loop_ref":594,"next":2377,"prev":2379,"to":599,"twin":2375,"wire_ref":null},{"edge":1193,"face":594,"from":599,"geometry_use":{"pcurve":2377,"periodic_lift":[0,0],"sense":"Reverse"},"id":2377,"loop_ref":594,"next":2378,"prev":2376,"to":596,"twin":2384,"wire_ref":null},{"edge":1189,"face":594,"from":596,"geometry_use":{"pcurve":2378,"periodic_lift":[0,0],"sense":"Reverse"},"id":2378,"loop_ref":594,"next":2379,"prev":2377,"to":588,"twin":2371,"wire_ref":null},{"edge":1179,"face":594,"from":588,"geometry_use":{"pcurve":2379,"periodic_lift":[0,0],"sense":"Forward"},"id":2379,"loop_ref":594,"next":2376,"prev":2378,"to":591,"twin":2354,"wire_ref":null},{"edge":1188,"face":595,"from":592,"geometry_use":{"pcurve":2380,"periodic_lift":[0,0],"sense":"Forward"},"id":2380,"loop_ref":595,"next":2381,"prev":2383,"to":597,"twin":2369,"wire_ref":null},{"edge":1194,"face":595,"from":597,"geometry_use":{"pcurve":2381,"periodic_lift":[0,0],"sense":"Forward"},"id":2381,"loop_ref":595,"next":2382,"prev":2380,"to":598,"twin":2386,"wire_ref":null},{"edge":1190,"face":595,"from":598,"geometry_use":{"pcurve":2382,"periodic_lift":[0,0],"sense":"Reverse"},"id":2382,"loop_ref":595,"next":2383,"prev":2381,"to":595,"twin":2373,"wire_ref":null},{"edge":1195,"face":595,"from":595,"geometry_use":{"pcurve":2383,"periodic_lift":[0,0],"sense":"Reverse"},"id":2383,"loop_ref":595,"next":2380,"prev":2382,"to":592,"twin":2400,"wire_ref":null},{"edge":1193,"face":596,"from":596,"geometry_use":{"pcurve":2384,"periodic_lift":[0,0],"sense":"Forward"},"id":2384,"loop_ref":596,"next":2385,"prev":2387,"to":599,"twin":2377,"wire_ref":null},{"edge":1191,"face":596,"from":599,"geometry_use":{"pcurve":2385,"periodic_lift":[0,0],"sense":"Forward"},"id":2385,"loop_ref":596,"next":2386,"prev":2384,"to":598,"twin":2374,"wire_ref":null},{"edge":1194,"face":596,"from":598,"geometry_use":{"pcurve":2386,"periodic_lift":[0,0],"sense":"Reverse"},"id":2386,"loop_ref":596,"next":2387,"prev":2385,"to":597,"twin":2381,"wire_ref":null},{"edge":1187,"face":596,"from":597,"geometry_use":{"pcurve":2387,"periodic_lift":[0,0],"sense":"Reverse"},"id":2387,"loop_ref":596,"next":2384,"prev":2386,"to":596,"twin":2368,"wire_ref":null},{"edge":1196,"face":597,"from":592,"geometry_use":{"pcurve":2388,"periodic_lift":[0,0],"sense":"Forward"},"id":2388,"loop_ref":597,"next":2389,"prev":2391,"to":600,"twin":2403,"wire_ref":null},{"edge":1197,"face":597,"from":600,"geometry_use":{"pcurve":2389,"periodic_lift":[0,0],"sense":"Reverse"},"id":2389,"loop_ref":597,"next":2390,"prev":2388,"to":601,"twin":2407,"wire_ref":null},{"edge":1198,"face":597,"from":601,"geometry_use":{"pcurve":2390,"periodic_lift":[0,0],"sense":"Reverse"},"id":2390,"loop_ref":597,"next":2391,"prev":2389,"to":593,"twin":2399,"wire_ref":null},{"edge":1181,"face":597,"from":593,"geometry_use":{"pcurve":2391,"periodic_lift":[0,0],"sense":"Forward"},"id":2391,"loop_ref":597,"next":2388,"prev":2390,"to":592,"twin":2357,"wire_ref":null},{"edge":1199,"face":598,"from":594,"geometry_use":{"pcurve":2392,"periodic_lift":[0,0],"sense":"Forward"},"id":2392,"loop_ref":598,"next":2393,"prev":2395,"to":602,"twin":2397,"wire_ref":null},{"edge":1200,"face":598,"from":602,"geometry_use":{"pcurve":2393,"periodic_lift":[0,0],"sense":"Forward"},"id":2393,"loop_ref":598,"next":2394,"prev":2392,"to":603,"twin":2411,"wire_ref":null},{"edge":1201,"face":598,"from":603,"geometry_use":{"pcurve":2394,"periodic_lift":[0,0],"sense":"Reverse"},"id":2394,"loop_ref":598,"next":2395,"prev":2393,"to":595,"twin":2401,"wire_ref":null},{"edge":1184,"face":598,"from":595,"geometry_use":{"pcurve":2395,"periodic_lift":[0,0],"sense":"Reverse"},"id":2395,"loop_ref":598,"next":2392,"prev":2394,"to":594,"twin":2361,"wire_ref":null},{"edge":1202,"face":599,"from":601,"geometry_use":{"pcurve":2396,"periodic_lift":[0,0],"sense":"Forward"},"id":2396,"loop_ref":599,"next":2397,"prev":2399,"to":602,"twin":2418,"wire_ref":null},{"edge":1199,"face":599,"from":602,"geometry_use":{"pcurve":2397,"periodic_lift":[0,0],"sense":"Reverse"},"id":2397,"loop_ref":599,"next":2398,"prev":2396,"to":594,"twin":2392,"wire_ref":null},{"edge":1186,"face":599,"from":594,"geometry_use":{"pcurve":2398,"periodic_lift":[0,0],"sense":"Reverse"},"id":2398,"loop_ref":599,"next":2399,"prev":2397,"to":593,"twin":2364,"wire_ref":null},{"edge":1198,"face":599,"from":593,"geometry_use":{"pcurve":2399,"periodic_lift":[0,0],"sense":"Forward"},"id":2399,"loop_ref":599,"next":2396,"prev":2398,"to":601,"twin":2390,"wire_ref":null},{"edge":1195,"face":600,"from":592,"geometry_use":{"pcurve":2400,"periodic_lift":[0,0],"sense":"Forward"},"id":2400,"loop_ref":600,"next":2401,"prev":2403,"to":595,"twin":2383,"wire_ref":null},{"edge":1201,"face":600,"from":595,"geometry_use":{"pcurve":2401,"periodic_lift":[0,0],"sense":"Forward"},"id":2401,"loop_ref":600,"next":2402,"prev":2400,"to":603,"twin":2394,"wire_ref":null},{"edge":1203,"face":600,"from":603,"geometry_use":{"pcurve":2402,"periodic_lift":[0,0],"sense":"Reverse"},"id":2402,"loop_ref":600,"next":2403,"prev":2401,"to":600,"twin":2431,"wire_ref":null},{"edge":1196,"face":600,"from":600,"geometry_use":{"pcurve":2403,"periodic_lift":[0,0],"sense":"Reverse"},"id":2403,"loop_ref":600,"next":2400,"prev":2402,"to":592,"twin":2388,"wire_ref":null},{"edge":1204,"face":601,"from":600,"geometry_use":{"pcurve":2404,"periodic_lift":[0,0],"sense":"Forward"},"id":2404,"loop_ref":601,"next":2405,"prev":2407,"to":604,"twin":2422,"wire_ref":null},{"edge":1205,"face":601,"from":604,"geometry_use":{"pcurve":2405,"periodic_lift":[0,0],"sense":"Reverse"},"id":2405,"loop_ref":601,"next":2406,"prev":2404,"to":605,"twin":2412,"wire_ref":null},{"edge":1206,"face":601,"from":605,"geometry_use":{"pcurve":2406,"periodic_lift":[0,0],"sense":"Reverse"},"id":2406,"loop_ref":601,"next":2407,"prev":2405,"to":601,"twin":2419,"wire_ref":null},{"edge":1197,"face":601,"from":601,"geometry_use":{"pcurve":2407,"periodic_lift":[0,0],"sense":"Forward"},"id":2407,"loop_ref":601,"next":2404,"prev":2406,"to":600,"twin":2389,"wire_ref":null},{"edge":1207,"face":602,"from":602,"geometry_use":{"pcurve":2408,"periodic_lift":[0,0],"sense":"Forward"},"id":2408,"loop_ref":602,"next":2409,"prev":2411,"to":606,"twin":2417,"wire_ref":null},{"edge":1208,"face":602,"from":606,"geometry_use":{"pcurve":2409,"periodic_lift":[0,0],"sense":"Forward"},"id":2409,"loop_ref":602,"next":2410,"prev":2408,"to":607,"twin":2414,"wire_ref":null},{"edge":1209,"face":602,"from":607,"geometry_use":{"pcurve":2410,"periodic_lift":[0,0],"sense":"Reverse"},"id":2410,"loop_ref":602,"next":2411,"prev":2409,"to":603,"twin":2424,"wire_ref":null},{"edge":1200,"face":602,"from":603,"geometry_use":{"pcurve":2411,"periodic_lift":[0,0],"sense":"Reverse"},"id":2411,"loop_ref":602,"next":2408,"prev":2410,"to":602,"twin":2393,"wire_ref":null},{"edge":1205,"face":603,"from":605,"geometry_use":{"pcurve":2412,"periodic_lift":[0,0],"sense":"Forward"},"id":2412,"loop_ref":603,"next":2413,"prev":2415,"to":604,"twin":2405,"wire_ref":null},{"edge":1210,"face":603,"from":604,"geometry_use":{"pcurve":2413,"periodic_lift":[0,0],"sense":"Forward"},"id":2413,"loop_ref":603,"next":2414,"prev":2412,"to":607,"twin":2435,"wire_ref":null},{"edge":1208,"face":603,"from":607,"geometry_use":{"pcurve":2414,"periodic_lift":[0,0],"sense":"Reverse"},"id":2414,"loop_ref":603,"next":2415,"prev":2413,"to":606,"twin":2409,"wire_ref":null},{"edge":1211,"face":603,"from":606,"geometry_use":{"pcurve":2415,"periodic_lift":[0,0],"sense":"Reverse"},"id":2415,"loop_ref":603,"next":2412,"prev":2414,"to":605,"twin":2416,"wire_ref":null},{"edge":1211,"face":604,"from":605,"geometry_use":{"pcurve":2416,"periodic_lift":[0,0],"sense":"Forward"},"id":2416,"loop_ref":604,"next":2417,"prev":2419,"to":606,"twin":2415,"wire_ref":null},{"edge":1207,"face":604,"from":606,"geometry_use":{"pcurve":2417,"periodic_lift":[0,0],"sense":"Reverse"},"id":2417,"loop_ref":604,"next":2418,"prev":2416,"to":602,"twin":2408,"wire_ref":null},{"edge":1202,"face":604,"from":602,"geometry_use":{"pcurve":2418,"periodic_lift":[0,0],"sense":"Reverse"},"id":2418,"loop_ref":604,"next":2419,"prev":2417,"to":601,"twin":2396,"wire_ref":null},{"edge":1206,"face":604,"from":601,"geometry_use":{"pcurve":2419,"periodic_lift":[0,0],"sense":"Forward"},"id":2419,"loop_ref":604,"next":2416,"prev":2418,"to":605,"twin":2406,"wire_ref":null},{"edge":1212,"face":605,"from":608,"geometry_use":{"pcurve":2420,"periodic_lift":[0,0],"sense":"Forward"},"id":2420,"loop_ref":605,"next":2421,"prev":2423,"to":609,"twin":2439,"wire_ref":null},{"edge":1213,"face":605,"from":609,"geometry_use":{"pcurve":2421,"periodic_lift":[0,0],"sense":"Reverse"},"id":2421,"loop_ref":605,"next":2422,"prev":2420,"to":604,"twin":2432,"wire_ref":null},{"edge":1204,"face":605,"from":604,"geometry_use":{"pcurve":2422,"periodic_lift":[0,0],"sense":"Reverse"},"id":2422,"loop_ref":605,"next":2423,"prev":2421,"to":600,"twin":2404,"wire_ref":null},{"edge":1214,"face":605,"from":600,"geometry_use":{"pcurve":2423,"periodic_lift":[0,0],"sense":"Forward"},"id":2423,"loop_ref":605,"next":2420,"prev":2422,"to":608,"twin":2430,"wire_ref":null},{"edge":1209,"face":606,"from":603,"geometry_use":{"pcurve":2424,"periodic_lift":[0,0],"sense":"Forward"},"id":2424,"loop_ref":606,"next":2425,"prev":2427,"to":607,"twin":2410,"wire_ref":null},{"edge":1215,"face":606,"from":607,"geometry_use":{"pcurve":2425,"periodic_lift":[0,0],"sense":"Forward"},"id":2425,"loop_ref":606,"next":2426,"prev":2424,"to":610,"twin":2434,"wire_ref":null},{"edge":1216,"face":606,"from":610,"geometry_use":{"pcurve":2426,"periodic_lift":[0,0],"sense":"Reverse"},"id":2426,"loop_ref":606,"next":2427,"prev":2425,"to":611,"twin":2437,"wire_ref":null},{"edge":1217,"face":606,"from":611,"geometry_use":{"pcurve":2427,"periodic_lift":[0,0],"sense":"Reverse"},"id":2427,"loop_ref":606,"next":2424,"prev":2426,"to":603,"twin":2428,"wire_ref":null},{"edge":1217,"face":607,"from":603,"geometry_use":{"pcurve":2428,"periodic_lift":[0,0],"sense":"Forward"},"id":2428,"loop_ref":607,"next":2429,"prev":2431,"to":611,"twin":2427,"wire_ref":null},{"edge":1218,"face":607,"from":611,"geometry_use":{"pcurve":2429,"periodic_lift":[0,0],"sense":"Reverse"},"id":2429,"loop_ref":607,"next":2430,"prev":2428,"to":608,"twin":2436,"wire_ref":null},{"edge":1214,"face":607,"from":608,"geometry_use":{"pcurve":2430,"periodic_lift":[0,0],"sense":"Reverse"},"id":2430,"loop_ref":607,"next":2431,"prev":2429,"to":600,"twin":2423,"wire_ref":null},{"edge":1203,"face":607,"from":600,"geometry_use":{"pcurve":2431,"periodic_lift":[0,0],"sense":"Forward"},"id":2431,"loop_ref":607,"next":2428,"prev":2430,"to":603,"twin":2402,"wire_ref":null},{"edge":1213,"face":608,"from":604,"geometry_use":{"pcurve":2432,"periodic_lift":[0,0],"sense":"Forward"},"id":2432,"loop_ref":608,"next":2433,"prev":2435,"to":609,"twin":2421,"wire_ref":null},{"edge":1219,"face":608,"from":609,"geometry_use":{"pcurve":2433,"periodic_lift":[0,0],"sense":"Forward"},"id":2433,"loop_ref":608,"next":2434,"prev":2432,"to":610,"twin":2438,"wire_ref":null},{"edge":1215,"face":608,"from":610,"geometry_use":{"pcurve":2434,"periodic_lift":[0,0],"sense":"Reverse"},"id":2434,"loop_ref":608,"next":2435,"prev":2433,"to":607,"twin":2425,"wire_ref":null},{"edge":1210,"face":608,"from":607,"geometry_use":{"pcurve":2435,"periodic_lift":[0,0],"sense":"Reverse"},"id":2435,"loop_ref":608,"next":2432,"prev":2434,"to":604,"twin":2413,"wire_ref":null},{"edge":1218,"face":609,"from":608,"geometry_use":{"pcurve":2436,"periodic_lift":[0,0],"sense":"Forward"},"id":2436,"loop_ref":609,"next":2437,"prev":2439,"to":611,"twin":2429,"wire_ref":null},{"edge":1216,"face":609,"from":611,"geometry_use":{"pcurve":2437,"periodic_lift":[0,0],"sense":"Forward"},"id":2437,"loop_ref":609,"next":2438,"prev":2436,"to":610,"twin":2426,"wire_ref":null},{"edge":1219,"face":609,"from":610,"geometry_use":{"pcurve":2438,"periodic_lift":[0,0],"sense":"Reverse"},"id":2438,"loop_ref":609,"next":2439,"prev":2437,"to":609,"twin":2433,"wire_ref":null},{"edge":1212,"face":609,"from":609,"geometry_use":{"pcurve":2439,"periodic_lift":[0,0],"sense":"Reverse"},"id":2439,"loop_ref":609,"next":2436,"prev":2438,"to":608,"twin":2420,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":48},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":52},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":56},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":60},{"face_ref":16,"id":16,"is_hole":false,"start_halfedge":64},{"face_ref":17,"id":17,"is_hole":false,"start_halfedge":68},{"face_ref":18,"id":18,"is_hole":false,"start_halfedge":72},{"face_ref":19,"id":19,"is_hole":false,"start_halfedge":76},{"face_ref":20,"id":20,"is_hole":false,"start_halfedge":80},{"face_ref":21,"id":21,"is_hole":false,"start_halfedge":84},{"face_ref":22,"id":22,"is_hole":false,"start_halfedge":88},{"face_ref":23,"id":23,"is_hole":false,"start_halfedge":92},{"face_ref":24,"id":24,"is_hole":false,"start_halfedge":96},{"face_ref":25,"id":25,"is_hole":false,"start_halfedge":100},{"face_ref":26,"id":26,"is_hole":false,"start_halfedge":104},{"face_ref":27,"id":27,"is_hole":false,"start_halfedge":108},{"face_ref":28,"id":28,"is_hole":false,"start_halfedge":112},{"face_ref":29,"id":29,"is_hole":false,"start_halfedge":116},{"face_ref":30,"id":30,"is_hole":false,"start_halfedge":120},{"face_ref":31,"id":31,"is_hole":false,"start_halfedge":124},{"face_ref":32,"id":32,"is_hole":false,"start_halfedge":128},{"face_ref":33,"id":33,"is_hole":false,"start_halfedge":132},{"face_ref":34,"id":34,"is_hole":false,"start_halfedge":136},{"face_ref":35,"id":35,"is_hole":false,"start_halfedge":140},{"face_ref":36,"id":36,"is_hole":false,"start_halfedge":144},{"face_ref":37,"id":37,"is_hole":false,"start_halfedge":148},{"face_ref":38,"id":38,"is_hole":false,"start_halfedge":152},{"face_ref":39,"id":39,"is_hole":false,"start_halfedge":156},{"face_ref":40,"id":40,"is_hole":false,"start_halfedge":160},{"face_ref":41,"id":41,"is_hole":false,"start_halfedge":164},{"face_ref":42,"id":42,"is_hole":false,"start_halfedge":168},{"face_ref":43,"id":43,"is_hole":false,"start_halfedge":172},{"face_ref":44,"id":44,"is_hole":false,"start_halfedge":176},{"face_ref":45,"id":45,"is_hole":false,"start_halfedge":180},{"face_ref":46,"id":46,"is_hole":false,"start_halfedge":184},{"face_ref":47,"id":47,"is_hole":false,"start_halfedge":188},{"face_ref":48,"id":48,"is_hole":false,"start_halfedge":192},{"face_ref":49,"id":49,"is_hole":false,"start_halfedge":196},{"face_ref":50,"id":50,"is_hole":false,"start_halfedge":200},{"face_ref":51,"id":51,"is_hole":false,"start_halfedge":204},{"face_ref":52,"id":52,"is_hole":false,"start_halfedge":208},{"face_ref":53,"id":53,"is_hole":false,"start_halfedge":212},{"face_ref":54,"id":54,"is_hole":false,"start_halfedge":216},{"face_ref":55,"id":55,"is_hole":false,"start_halfedge":220},{"face_ref":56,"id":56,"is_hole":false,"start_halfedge":224},{"face_ref":57,"id":57,"is_hole":false,"start_halfedge":228},{"face_ref":58,"id":58,"is_hole":false,"start_halfedge":232},{"face_ref":59,"id":59,"is_hole":false,"start_halfedge":236},{"face_ref":60,"id":60,"is_hole":false,"start_halfedge":240},{"face_ref":61,"id":61,"is_hole":false,"start_halfedge":244},{"face_ref":62,"id":62,"is_hole":false,"start_halfedge":248},{"face_ref":63,"id":63,"is_hole":false,"start_halfedge":252},{"face_ref":64,"id":64,"is_hole":false,"start_halfedge":256},{"face_ref":65,"id":65,"is_hole":false,"start_halfedge":260},{"face_ref":66,"id":66,"is_hole":false,"start_halfedge":264},{"face_ref":67,"id":67,"is_hole":false,"start_halfedge":268},{"face_ref":68,"id":68,"is_hole":false,"start_halfedge":272},{"face_ref":69,"id":69,"is_hole":false,"start_halfedge":276},{"face_ref":70,"id":70,"is_hole":false,"start_halfedge":280},{"face_ref":71,"id":71,"is_hole":false,"start_halfedge":284},{"face_ref":72,"id":72,"is_hole":false,"start_halfedge":288},{"face_ref":73,"id":73,"is_hole":false,"start_halfedge":292},{"face_ref":74,"id":74,"is_hole":false,"start_halfedge":296},{"face_ref":75,"id":75,"is_hole":false,"start_halfedge":300},{"face_ref":76,"id":76,"is_hole":false,"start_halfedge":304},{"face_ref":77,"id":77,"is_hole":false,"start_halfedge":308},{"face_ref":78,"id":78,"is_hole":false,"start_halfedge":312},{"face_ref":79,"id":79,"is_hole":false,"start_halfedge":316},{"face_ref":80,"id":80,"is_hole":false,"start_halfedge":320},{"face_ref":81,"id":81,"is_hole":false,"start_halfedge":324},{"face_ref":82,"id":82,"is_hole":false,"start_halfedge":328},{"face_ref":83,"id":83,"is_hole":false,"start_halfedge":332},{"face_ref":84,"id":84,"is_hole":false,"start_halfedge":336},{"face_ref":85,"id":85,"is_hole":false,"start_halfedge":340},{"face_ref":86,"id":86,"is_hole":false,"start_halfedge":344},{"face_ref":87,"id":87,"is_hole":false,"start_halfedge":348},{"face_ref":88,"id":88,"is_hole":false,"start_halfedge":352},{"face_ref":89,"id":89,"is_hole":false,"start_halfedge":356},{"face_ref":90,"id":90,"is_hole":false,"start_halfedge":360},{"face_ref":91,"id":91,"is_hole":false,"start_halfedge":364},{"face_ref":92,"id":92,"is_hole":false,"start_halfedge":368},{"face_ref":93,"id":93,"is_hole":false,"start_halfedge":372},{"face_ref":94,"id":94,"is_hole":false,"start_halfedge":376},{"face_ref":95,"id":95,"is_hole":false,"start_halfedge":380},{"face_ref":96,"id":96,"is_hole":false,"start_halfedge":384},{"face_ref":97,"id":97,"is_hole":false,"start_halfedge":388},{"face_ref":98,"id":98,"is_hole":false,"start_halfedge":392},{"face_ref":99,"id":99,"is_hole":false,"start_halfedge":396},{"face_ref":100,"id":100,"is_hole":false,"start_halfedge":400},{"face_ref":101,"id":101,"is_hole":false,"start_halfedge":404},{"face_ref":102,"id":102,"is_hole":false,"start_halfedge":408},{"face_ref":103,"id":103,"is_hole":false,"start_halfedge":412},{"face_ref":104,"id":104,"is_hole":false,"start_halfedge":416},{"face_ref":105,"id":105,"is_hole":false,"start_halfedge":420},{"face_ref":106,"id":106,"is_hole":false,"start_halfedge":424},{"face_ref":107,"id":107,"is_hole":false,"start_halfedge":428},{"face_ref":108,"id":108,"is_hole":false,"start_halfedge":432},{"face_ref":109,"id":109,"is_hole":false,"start_halfedge":436},{"face_ref":110,"id":110,"is_hole":false,"start_halfedge":440},{"face_ref":111,"id":111,"is_hole":false,"start_halfedge":444},{"face_ref":112,"id":112,"is_hole":false,"start_halfedge":448},{"face_ref":113,"id":113,"is_hole":false,"start_halfedge":452},{"face_ref":114,"id":114,"is_hole":false,"start_halfedge":456},{"face_ref":115,"id":115,"is_hole":false,"start_halfedge":460},{"face_ref":116,"id":116,"is_hole":false,"start_halfedge":464},{"face_ref":117,"id":117,"is_hole":false,"start_halfedge":468},{"face_ref":118,"id":118,"is_hole":false,"start_halfedge":472},{"face_ref":119,"id":119,"is_hole":false,"start_halfedge":476},{"face_ref":120,"id":120,"is_hole":false,"start_halfedge":480},{"face_ref":121,"id":121,"is_hole":false,"start_halfedge":484},{"face_ref":122,"id":122,"is_hole":false,"start_halfedge":488},{"face_ref":123,"id":123,"is_hole":false,"start_halfedge":492},{"face_ref":124,"id":124,"is_hole":false,"start_halfedge":496},{"face_ref":125,"id":125,"is_hole":false,"start_halfedge":500},{"face_ref":126,"id":126,"is_hole":false,"start_halfedge":504},{"face_ref":127,"id":127,"is_hole":false,"start_halfedge":508},{"face_ref":128,"id":128,"is_hole":false,"start_halfedge":512},{"face_ref":129,"id":129,"is_hole":false,"start_halfedge":516},{"face_ref":130,"id":130,"is_hole":false,"start_halfedge":520},{"face_ref":131,"id":131,"is_hole":false,"start_halfedge":524},{"face_ref":132,"id":132,"is_hole":false,"start_halfedge":528},{"face_ref":133,"id":133,"is_hole":false,"start_halfedge":532},{"face_ref":134,"id":134,"is_hole":false,"start_halfedge":536},{"face_ref":135,"id":135,"is_hole":false,"start_halfedge":540},{"face_ref":136,"id":136,"is_hole":false,"start_halfedge":544},{"face_ref":137,"id":137,"is_hole":false,"start_halfedge":548},{"face_ref":138,"id":138,"is_hole":false,"start_halfedge":552},{"face_ref":139,"id":139,"is_hole":false,"start_halfedge":556},{"face_ref":140,"id":140,"is_hole":false,"start_halfedge":560},{"face_ref":141,"id":141,"is_hole":false,"start_halfedge":564},{"face_ref":142,"id":142,"is_hole":false,"start_halfedge":568},{"face_ref":143,"id":143,"is_hole":false,"start_halfedge":572},{"face_ref":144,"id":144,"is_hole":false,"start_halfedge":576},{"face_ref":145,"id":145,"is_hole":false,"start_halfedge":580},{"face_ref":146,"id":146,"is_hole":false,"start_halfedge":584},{"face_ref":147,"id":147,"is_hole":false,"start_halfedge":588},{"face_ref":148,"id":148,"is_hole":false,"start_halfedge":592},{"face_ref":149,"id":149,"is_hole":false,"start_halfedge":596},{"face_ref":150,"id":150,"is_hole":false,"start_halfedge":600},{"face_ref":151,"id":151,"is_hole":false,"start_halfedge":604},{"face_ref":152,"id":152,"is_hole":false,"start_halfedge":608},{"face_ref":153,"id":153,"is_hole":false,"start_halfedge":612},{"face_ref":154,"id":154,"is_hole":false,"start_halfedge":616},{"face_ref":155,"id":155,"is_hole":false,"start_halfedge":620},{"face_ref":156,"id":156,"is_hole":false,"start_halfedge":624},{"face_ref":157,"id":157,"is_hole":false,"start_halfedge":628},{"face_ref":158,"id":158,"is_hole":false,"start_halfedge":632},{"face_ref":159,"id":159,"is_hole":false,"start_halfedge":636},{"face_ref":160,"id":160,"is_hole":false,"start_halfedge":640},{"face_ref":161,"id":161,"is_hole":false,"start_halfedge":644},{"face_ref":162,"id":162,"is_hole":false,"start_halfedge":648},{"face_ref":163,"id":163,"is_hole":false,"start_halfedge":652},{"face_ref":164,"id":164,"is_hole":false,"start_halfedge":656},{"face_ref":165,"id":165,"is_hole":false,"start_halfedge":660},{"face_ref":166,"id":166,"is_hole":false,"start_halfedge":664},{"face_ref":167,"id":167,"is_hole":false,"start_halfedge":668},{"face_ref":168,"id":168,"is_hole":false,"start_halfedge":672},{"face_ref":169,"id":169,"is_hole":false,"start_halfedge":676},{"face_ref":170,"id":170,"is_hole":false,"start_halfedge":680},{"face_ref":171,"id":171,"is_hole":false,"start_halfedge":684},{"face_ref":172,"id":172,"is_hole":false,"start_halfedge":688},{"face_ref":173,"id":173,"is_hole":false,"start_halfedge":692},{"face_ref":174,"id":174,"is_hole":false,"start_halfedge":696},{"face_ref":175,"id":175,"is_hole":false,"start_halfedge":700},{"face_ref":176,"id":176,"is_hole":false,"start_halfedge":704},{"face_ref":177,"id":177,"is_hole":false,"start_halfedge":708},{"face_ref":178,"id":178,"is_hole":false,"start_halfedge":712},{"face_ref":179,"id":179,"is_hole":false,"start_halfedge":716},{"face_ref":180,"id":180,"is_hole":false,"start_halfedge":720},{"face_ref":181,"id":181,"is_hole":false,"start_halfedge":724},{"face_ref":182,"id":182,"is_hole":false,"start_halfedge":728},{"face_ref":183,"id":183,"is_hole":false,"start_halfedge":732},{"face_ref":184,"id":184,"is_hole":false,"start_halfedge":736},{"face_ref":185,"id":185,"is_hole":false,"start_halfedge":740},{"face_ref":186,"id":186,"is_hole":false,"start_halfedge":744},{"face_ref":187,"id":187,"is_hole":false,"start_halfedge":748},{"face_ref":188,"id":188,"is_hole":false,"start_halfedge":752},{"face_ref":189,"id":189,"is_hole":false,"start_halfedge":756},{"face_ref":190,"id":190,"is_hole":false,"start_halfedge":760},{"face_ref":191,"id":191,"is_hole":false,"start_halfedge":764},{"face_ref":192,"id":192,"is_hole":false,"start_halfedge":768},{"face_ref":193,"id":193,"is_hole":false,"start_halfedge":772},{"face_ref":194,"id":194,"is_hole":false,"start_halfedge":776},{"face_ref":195,"id":195,"is_hole":false,"start_halfedge":780},{"face_ref":196,"id":196,"is_hole":false,"start_halfedge":784},{"face_ref":197,"id":197,"is_hole":false,"start_halfedge":788},{"face_ref":198,"id":198,"is_hole":false,"start_halfedge":792},{"face_ref":199,"id":199,"is_hole":false,"start_halfedge":796},{"face_ref":200,"id":200,"is_hole":false,"start_halfedge":800},{"face_ref":201,"id":201,"is_hole":false,"start_halfedge":804},{"face_ref":202,"id":202,"is_hole":false,"start_halfedge":808},{"face_ref":203,"id":203,"is_hole":false,"start_halfedge":812},{"face_ref":204,"id":204,"is_hole":false,"start_halfedge":816},{"face_ref":205,"id":205,"is_hole":false,"start_halfedge":820},{"face_ref":206,"id":206,"is_hole":false,"start_halfedge":824},{"face_ref":207,"id":207,"is_hole":false,"start_halfedge":828},{"face_ref":208,"id":208,"is_hole":false,"start_halfedge":832},{"face_ref":209,"id":209,"is_hole":false,"start_halfedge":836},{"face_ref":210,"id":210,"is_hole":false,"start_halfedge":840},{"face_ref":211,"id":211,"is_hole":false,"start_halfedge":844},{"face_ref":212,"id":212,"is_hole":false,"start_halfedge":848},{"face_ref":213,"id":213,"is_hole":false,"start_halfedge":852},{"face_ref":214,"id":214,"is_hole":false,"start_halfedge":856},{"face_ref":215,"id":215,"is_hole":false,"start_halfedge":860},{"face_ref":216,"id":216,"is_hole":false,"start_halfedge":864},{"face_ref":217,"id":217,"is_hole":false,"start_halfedge":868},{"face_ref":218,"id":218,"is_hole":false,"start_halfedge":872},{"face_ref":219,"id":219,"is_hole":false,"start_halfedge":876},{"face_ref":220,"id":220,"is_hole":false,"start_halfedge":880},{"face_ref":221,"id":221,"is_hole":false,"start_halfedge":884},{"face_ref":222,"id":222,"is_hole":false,"start_halfedge":888},{"face_ref":223,"id":223,"is_hole":false,"start_halfedge":892},{"face_ref":224,"id":224,"is_hole":false,"start_halfedge":896},{"face_ref":225,"id":225,"is_hole":false,"start_halfedge":900},{"face_ref":226,"id":226,"is_hole":false,"start_halfedge":904},{"face_ref":227,"id":227,"is_hole":false,"start_halfedge":908},{"face_ref":228,"id":228,"is_hole":false,"start_halfedge":912},{"face_ref":229,"id":229,"is_hole":false,"start_halfedge":916},{"face_ref":230,"id":230,"is_hole":false,"start_halfedge":920},{"face_ref":231,"id":231,"is_hole":false,"start_halfedge":924},{"face_ref":232,"id":232,"is_hole":false,"start_halfedge":928},{"face_ref":233,"id":233,"is_hole":false,"start_halfedge":932},{"face_ref":234,"id":234,"is_hole":false,"start_halfedge":936},{"face_ref":235,"id":235,"is_hole":false,"start_halfedge":940},{"face_ref":236,"id":236,"is_hole":false,"start_halfedge":944},{"face_ref":237,"id":237,"is_hole":false,"start_halfedge":948},{"face_ref":238,"id":238,"is_hole":false,"start_halfedge":952},{"face_ref":239,"id":239,"is_hole":false,"start_halfedge":956},{"face_ref":240,"id":240,"is_hole":false,"start_halfedge":960},{"face_ref":241,"id":241,"is_hole":false,"start_halfedge":964},{"face_ref":242,"id":242,"is_hole":false,"start_halfedge":968},{"face_ref":243,"id":243,"is_hole":false,"start_halfedge":972},{"face_ref":244,"id":244,"is_hole":false,"start_halfedge":976},{"face_ref":245,"id":245,"is_hole":false,"start_halfedge":980},{"face_ref":246,"id":246,"is_hole":false,"start_halfedge":984},{"face_ref":247,"id":247,"is_hole":false,"start_halfedge":988},{"face_ref":248,"id":248,"is_hole":false,"start_halfedge":992},{"face_ref":249,"id":249,"is_hole":false,"start_halfedge":996},{"face_ref":250,"id":250,"is_hole":false,"start_halfedge":1000},{"face_ref":251,"id":251,"is_hole":false,"start_halfedge":1004},{"face_ref":252,"id":252,"is_hole":false,"start_halfedge":1008},{"face_ref":253,"id":253,"is_hole":false,"start_halfedge":1012},{"face_ref":254,"id":254,"is_hole":false,"start_halfedge":1016},{"face_ref":255,"id":255,"is_hole":false,"start_halfedge":1020},{"face_ref":256,"id":256,"is_hole":false,"start_halfedge":1024},{"face_ref":257,"id":257,"is_hole":false,"start_halfedge":1028},{"face_ref":258,"id":258,"is_hole":false,"start_halfedge":1032},{"face_ref":259,"id":259,"is_hole":false,"start_halfedge":1036},{"face_ref":260,"id":260,"is_hole":false,"start_halfedge":1040},{"face_ref":261,"id":261,"is_hole":false,"start_halfedge":1044},{"face_ref":262,"id":262,"is_hole":false,"start_halfedge":1048},{"face_ref":263,"id":263,"is_hole":false,"start_halfedge":1052},{"face_ref":264,"id":264,"is_hole":false,"start_halfedge":1056},{"face_ref":265,"id":265,"is_hole":false,"start_halfedge":1060},{"face_ref":266,"id":266,"is_hole":false,"start_halfedge":1064},{"face_ref":267,"id":267,"is_hole":false,"start_halfedge":1068},{"face_ref":268,"id":268,"is_hole":false,"start_halfedge":1072},{"face_ref":269,"id":269,"is_hole":false,"start_halfedge":1076},{"face_ref":270,"id":270,"is_hole":false,"start_halfedge":1080},{"face_ref":271,"id":271,"is_hole":false,"start_halfedge":1084},{"face_ref":272,"id":272,"is_hole":false,"start_halfedge":1088},{"face_ref":273,"id":273,"is_hole":false,"start_halfedge":1092},{"face_ref":274,"id":274,"is_hole":false,"start_halfedge":1096},{"face_ref":275,"id":275,"is_hole":false,"start_halfedge":1100},{"face_ref":276,"id":276,"is_hole":false,"start_halfedge":1104},{"face_ref":277,"id":277,"is_hole":false,"start_halfedge":1108},{"face_ref":278,"id":278,"is_hole":false,"start_halfedge":1112},{"face_ref":279,"id":279,"is_hole":false,"start_halfedge":1116},{"face_ref":280,"id":280,"is_hole":false,"start_halfedge":1120},{"face_ref":281,"id":281,"is_hole":false,"start_halfedge":1124},{"face_ref":282,"id":282,"is_hole":false,"start_halfedge":1128},{"face_ref":283,"id":283,"is_hole":false,"start_halfedge":1132},{"face_ref":284,"id":284,"is_hole":false,"start_halfedge":1136},{"face_ref":285,"id":285,"is_hole":false,"start_halfedge":1140},{"face_ref":286,"id":286,"is_hole":false,"start_halfedge":1144},{"face_ref":287,"id":287,"is_hole":false,"start_halfedge":1148},{"face_ref":288,"id":288,"is_hole":false,"start_halfedge":1152},{"face_ref":289,"id":289,"is_hole":false,"start_halfedge":1156},{"face_ref":290,"id":290,"is_hole":false,"start_halfedge":1160},{"face_ref":291,"id":291,"is_hole":false,"start_halfedge":1164},{"face_ref":292,"id":292,"is_hole":false,"start_halfedge":1168},{"face_ref":293,"id":293,"is_hole":false,"start_halfedge":1172},{"face_ref":294,"id":294,"is_hole":false,"start_halfedge":1176},{"face_ref":295,"id":295,"is_hole":false,"start_halfedge":1180},{"face_ref":296,"id":296,"is_hole":false,"start_halfedge":1184},{"face_ref":297,"id":297,"is_hole":false,"start_halfedge":1188},{"face_ref":298,"id":298,"is_hole":false,"start_halfedge":1192},{"face_ref":299,"id":299,"is_hole":false,"start_halfedge":1196},{"face_ref":300,"id":300,"is_hole":false,"start_halfedge":1200},{"face_ref":301,"id":301,"is_hole":false,"start_halfedge":1204},{"face_ref":302,"id":302,"is_hole":false,"start_halfedge":1208},{"face_ref":303,"id":303,"is_hole":false,"start_halfedge":1212},{"face_ref":304,"id":304,"is_hole":false,"start_halfedge":1216},{"face_ref":305,"id":305,"is_hole":false,"start_halfedge":1220},{"face_ref":306,"id":306,"is_hole":false,"start_halfedge":1224},{"face_ref":307,"id":307,"is_hole":false,"start_halfedge":1228},{"face_ref":308,"id":308,"is_hole":false,"start_halfedge":1232},{"face_ref":309,"id":309,"is_hole":false,"start_halfedge":1236},{"face_ref":310,"id":310,"is_hole":false,"start_halfedge":1240},{"face_ref":311,"id":311,"is_hole":false,"start_halfedge":1244},{"face_ref":312,"id":312,"is_hole":false,"start_halfedge":1248},{"face_ref":313,"id":313,"is_hole":false,"start_halfedge":1252},{"face_ref":314,"id":314,"is_hole":false,"start_halfedge":1256},{"face_ref":315,"id":315,"is_hole":false,"start_halfedge":1260},{"face_ref":316,"id":316,"is_hole":false,"start_halfedge":1264},{"face_ref":317,"id":317,"is_hole":false,"start_halfedge":1268},{"face_ref":318,"id":318,"is_hole":false,"start_halfedge":1272},{"face_ref":319,"id":319,"is_hole":false,"start_halfedge":1276},{"face_ref":320,"id":320,"is_hole":false,"start_halfedge":1280},{"face_ref":321,"id":321,"is_hole":false,"start_halfedge":1284},{"face_ref":322,"id":322,"is_hole":false,"start_halfedge":1288},{"face_ref":323,"id":323,"is_hole":false,"start_halfedge":1292},{"face_ref":324,"id":324,"is_hole":false,"start_halfedge":1296},{"face_ref":325,"id":325,"is_hole":false,"start_halfedge":1300},{"face_ref":326,"id":326,"is_hole":false,"start_halfedge":1304},{"face_ref":327,"id":327,"is_hole":false,"start_halfedge":1308},{"face_ref":328,"id":328,"is_hole":false,"start_halfedge":1312},{"face_ref":329,"id":329,"is_hole":false,"start_halfedge":1316},{"face_ref":330,"id":330,"is_hole":false,"start_halfedge":1320},{"face_ref":331,"id":331,"is_hole":false,"start_halfedge":1324},{"face_ref":332,"id":332,"is_hole":false,"start_halfedge":1328},{"face_ref":333,"id":333,"is_hole":false,"start_halfedge":1332},{"face_ref":334,"id":334,"is_hole":false,"start_halfedge":1336},{"face_ref":335,"id":335,"is_hole":false,"start_halfedge":1340},{"face_ref":336,"id":336,"is_hole":false,"start_halfedge":1344},{"face_ref":337,"id":337,"is_hole":false,"start_halfedge":1348},{"face_ref":338,"id":338,"is_hole":false,"start_halfedge":1352},{"face_ref":339,"id":339,"is_hole":false,"start_halfedge":1356},{"face_ref":340,"id":340,"is_hole":false,"start_halfedge":1360},{"face_ref":341,"id":341,"is_hole":false,"start_halfedge":1364},{"face_ref":342,"id":342,"is_hole":false,"start_halfedge":1368},{"face_ref":343,"id":343,"is_hole":false,"start_halfedge":1372},{"face_ref":344,"id":344,"is_hole":false,"start_halfedge":1376},{"face_ref":345,"id":345,"is_hole":false,"start_halfedge":1380},{"face_ref":346,"id":346,"is_hole":false,"start_halfedge":1384},{"face_ref":347,"id":347,"is_hole":false,"start_halfedge":1388},{"face_ref":348,"id":348,"is_hole":false,"start_halfedge":1392},{"face_ref":349,"id":349,"is_hole":false,"start_halfedge":1396},{"face_ref":350,"id":350,"is_hole":false,"start_halfedge":1400},{"face_ref":351,"id":351,"is_hole":false,"start_halfedge":1404},{"face_ref":352,"id":352,"is_hole":false,"start_halfedge":1408},{"face_ref":353,"id":353,"is_hole":false,"start_halfedge":1412},{"face_ref":354,"id":354,"is_hole":false,"start_halfedge":1416},{"face_ref":355,"id":355,"is_hole":false,"start_halfedge":1420},{"face_ref":356,"id":356,"is_hole":false,"start_halfedge":1424},{"face_ref":357,"id":357,"is_hole":false,"start_halfedge":1428},{"face_ref":358,"id":358,"is_hole":false,"start_halfedge":1432},{"face_ref":359,"id":359,"is_hole":false,"start_halfedge":1436},{"face_ref":360,"id":360,"is_hole":false,"start_halfedge":1440},{"face_ref":361,"id":361,"is_hole":false,"start_halfedge":1444},{"face_ref":362,"id":362,"is_hole":false,"start_halfedge":1448},{"face_ref":363,"id":363,"is_hole":false,"start_halfedge":1452},{"face_ref":364,"id":364,"is_hole":false,"start_halfedge":1456},{"face_ref":365,"id":365,"is_hole":false,"start_halfedge":1460},{"face_ref":366,"id":366,"is_hole":false,"start_halfedge":1464},{"face_ref":367,"id":367,"is_hole":false,"start_halfedge":1468},{"face_ref":368,"id":368,"is_hole":false,"start_halfedge":1472},{"face_ref":369,"id":369,"is_hole":false,"start_halfedge":1476},{"face_ref":370,"id":370,"is_hole":false,"start_halfedge":1480},{"face_ref":371,"id":371,"is_hole":false,"start_halfedge":1484},{"face_ref":372,"id":372,"is_hole":false,"start_halfedge":1488},{"face_ref":373,"id":373,"is_hole":false,"start_halfedge":1492},{"face_ref":374,"id":374,"is_hole":false,"start_halfedge":1496},{"face_ref":375,"id":375,"is_hole":false,"start_halfedge":1500},{"face_ref":376,"id":376,"is_hole":false,"start_halfedge":1504},{"face_ref":377,"id":377,"is_hole":false,"start_halfedge":1508},{"face_ref":378,"id":378,"is_hole":false,"start_halfedge":1512},{"face_ref":379,"id":379,"is_hole":false,"start_halfedge":1516},{"face_ref":380,"id":380,"is_hole":false,"start_halfedge":1520},{"face_ref":381,"id":381,"is_hole":false,"start_halfedge":1524},{"face_ref":382,"id":382,"is_hole":false,"start_halfedge":1528},{"face_ref":383,"id":383,"is_hole":false,"start_halfedge":1532},{"face_ref":384,"id":384,"is_hole":false,"start_halfedge":1536},{"face_ref":385,"id":385,"is_hole":false,"start_halfedge":1540},{"face_ref":386,"id":386,"is_hole":false,"start_halfedge":1544},{"face_ref":387,"id":387,"is_hole":false,"start_halfedge":1548},{"face_ref":388,"id":388,"is_hole":false,"start_halfedge":1552},{"face_ref":389,"id":389,"is_hole":false,"start_halfedge":1556},{"face_ref":390,"id":390,"is_hole":false,"start_halfedge":1560},{"face_ref":391,"id":391,"is_hole":false,"start_halfedge":1564},{"face_ref":392,"id":392,"is_hole":false,"start_halfedge":1568},{"face_ref":393,"id":393,"is_hole":false,"start_halfedge":1572},{"face_ref":394,"id":394,"is_hole":false,"start_halfedge":1576},{"face_ref":395,"id":395,"is_hole":false,"start_halfedge":1580},{"face_ref":396,"id":396,"is_hole":false,"start_halfedge":1584},{"face_ref":397,"id":397,"is_hole":false,"start_halfedge":1588},{"face_ref":398,"id":398,"is_hole":false,"start_halfedge":1592},{"face_ref":399,"id":399,"is_hole":false,"start_halfedge":1596},{"face_ref":400,"id":400,"is_hole":false,"start_halfedge":1600},{"face_ref":401,"id":401,"is_hole":false,"start_halfedge":1604},{"face_ref":402,"id":402,"is_hole":false,"start_halfedge":1608},{"face_ref":403,"id":403,"is_hole":false,"start_halfedge":1612},{"face_ref":404,"id":404,"is_hole":false,"start_halfedge":1616},{"face_ref":405,"id":405,"is_hole":false,"start_halfedge":1620},{"face_ref":406,"id":406,"is_hole":false,"start_halfedge":1624},{"face_ref":407,"id":407,"is_hole":false,"start_halfedge":1628},{"face_ref":408,"id":408,"is_hole":false,"start_halfedge":1632},{"face_ref":409,"id":409,"is_hole":false,"start_halfedge":1636},{"face_ref":410,"id":410,"is_hole":false,"start_halfedge":1640},{"face_ref":411,"id":411,"is_hole":false,"start_halfedge":1644},{"face_ref":412,"id":412,"is_hole":false,"start_halfedge":1648},{"face_ref":413,"id":413,"is_hole":false,"start_halfedge":1652},{"face_ref":414,"id":414,"is_hole":false,"start_halfedge":1656},{"face_ref":415,"id":415,"is_hole":false,"start_halfedge":1660},{"face_ref":416,"id":416,"is_hole":false,"start_halfedge":1664},{"face_ref":417,"id":417,"is_hole":false,"start_halfedge":1668},{"face_ref":418,"id":418,"is_hole":false,"start_halfedge":1672},{"face_ref":419,"id":419,"is_hole":false,"start_halfedge":1676},{"face_ref":420,"id":420,"is_hole":false,"start_halfedge":1680},{"face_ref":421,"id":421,"is_hole":false,"start_halfedge":1684},{"face_ref":422,"id":422,"is_hole":false,"start_halfedge":1688},{"face_ref":423,"id":423,"is_hole":false,"start_halfedge":1692},{"face_ref":424,"id":424,"is_hole":false,"start_halfedge":1696},{"face_ref":425,"id":425,"is_hole":false,"start_halfedge":1700},{"face_ref":426,"id":426,"is_hole":false,"start_halfedge":1704},{"face_ref":427,"id":427,"is_hole":false,"start_halfedge":1708},{"face_ref":428,"id":428,"is_hole":false,"start_halfedge":1712},{"face_ref":429,"id":429,"is_hole":false,"start_halfedge":1716},{"face_ref":430,"id":430,"is_hole":false,"start_halfedge":1720},{"face_ref":431,"id":431,"is_hole":false,"start_halfedge":1724},{"face_ref":432,"id":432,"is_hole":false,"start_halfedge":1728},{"face_ref":433,"id":433,"is_hole":false,"start_halfedge":1732},{"face_ref":434,"id":434,"is_hole":false,"start_halfedge":1736},{"face_ref":435,"id":435,"is_hole":false,"start_halfedge":1740},{"face_ref":436,"id":436,"is_hole":false,"start_halfedge":1744},{"face_ref":437,"id":437,"is_hole":false,"start_halfedge":1748},{"face_ref":438,"id":438,"is_hole":false,"start_halfedge":1752},{"face_ref":439,"id":439,"is_hole":false,"start_halfedge":1756},{"face_ref":440,"id":440,"is_hole":false,"start_halfedge":1760},{"face_ref":441,"id":441,"is_hole":false,"start_halfedge":1764},{"face_ref":442,"id":442,"is_hole":false,"start_halfedge":1768},{"face_ref":443,"id":443,"is_hole":false,"start_halfedge":1772},{"face_ref":444,"id":444,"is_hole":false,"start_halfedge":1776},{"face_ref":445,"id":445,"is_hole":false,"start_halfedge":1780},{"face_ref":446,"id":446,"is_hole":false,"start_halfedge":1784},{"face_ref":447,"id":447,"is_hole":false,"start_halfedge":1788},{"face_ref":448,"id":448,"is_hole":false,"start_halfedge":1792},{"face_ref":449,"id":449,"is_hole":false,"start_halfedge":1796},{"face_ref":450,"id":450,"is_hole":false,"start_halfedge":1800},{"face_ref":451,"id":451,"is_hole":false,"start_halfedge":1804},{"face_ref":452,"id":452,"is_hole":false,"start_halfedge":1808},{"face_ref":453,"id":453,"is_hole":false,"start_halfedge":1812},{"face_ref":454,"id":454,"is_hole":false,"start_halfedge":1816},{"face_ref":455,"id":455,"is_hole":false,"start_halfedge":1820},{"face_ref":456,"id":456,"is_hole":false,"start_halfedge":1824},{"face_ref":457,"id":457,"is_hole":false,"start_halfedge":1828},{"face_ref":458,"id":458,"is_hole":false,"start_halfedge":1832},{"face_ref":459,"id":459,"is_hole":false,"start_halfedge":1836},{"face_ref":460,"id":460,"is_hole":false,"start_halfedge":1840},{"face_ref":461,"id":461,"is_hole":false,"start_halfedge":1844},{"face_ref":462,"id":462,"is_hole":false,"start_halfedge":1848},{"face_ref":463,"id":463,"is_hole":false,"start_halfedge":1852},{"face_ref":464,"id":464,"is_hole":false,"start_halfedge":1856},{"face_ref":465,"id":465,"is_hole":false,"start_halfedge":1860},{"face_ref":466,"id":466,"is_hole":false,"start_halfedge":1864},{"face_ref":467,"id":467,"is_hole":false,"start_halfedge":1868},{"face_ref":468,"id":468,"is_hole":false,"start_halfedge":1872},{"face_ref":469,"id":469,"is_hole":false,"start_halfedge":1876},{"face_ref":470,"id":470,"is_hole":false,"start_halfedge":1880},{"face_ref":471,"id":471,"is_hole":false,"start_halfedge":1884},{"face_ref":472,"id":472,"is_hole":false,"start_halfedge":1888},{"face_ref":473,"id":473,"is_hole":false,"start_halfedge":1892},{"face_ref":474,"id":474,"is_hole":false,"start_halfedge":1896},{"face_ref":475,"id":475,"is_hole":false,"start_halfedge":1900},{"face_ref":476,"id":476,"is_hole":false,"start_halfedge":1904},{"face_ref":477,"id":477,"is_hole":false,"start_halfedge":1908},{"face_ref":478,"id":478,"is_hole":false,"start_halfedge":1912},{"face_ref":479,"id":479,"is_hole":false,"start_halfedge":1916},{"face_ref":480,"id":480,"is_hole":false,"start_halfedge":1920},{"face_ref":481,"id":481,"is_hole":false,"start_halfedge":1924},{"face_ref":482,"id":482,"is_hole":false,"start_halfedge":1928},{"face_ref":483,"id":483,"is_hole":false,"start_halfedge":1932},{"face_ref":484,"id":484,"is_hole":false,"start_halfedge":1936},{"face_ref":485,"id":485,"is_hole":false,"start_halfedge":1940},{"face_ref":486,"id":486,"is_hole":false,"start_halfedge":1944},{"face_ref":487,"id":487,"is_hole":false,"start_halfedge":1948},{"face_ref":488,"id":488,"is_hole":false,"start_halfedge":1952},{"face_ref":489,"id":489,"is_hole":false,"start_halfedge":1956},{"face_ref":490,"id":490,"is_hole":false,"start_halfedge":1960},{"face_ref":491,"id":491,"is_hole":false,"start_halfedge":1964},{"face_ref":492,"id":492,"is_hole":false,"start_halfedge":1968},{"face_ref":493,"id":493,"is_hole":false,"start_halfedge":1972},{"face_ref":494,"id":494,"is_hole":false,"start_halfedge":1976},{"face_ref":495,"id":495,"is_hole":false,"start_halfedge":1980},{"face_ref":496,"id":496,"is_hole":false,"start_halfedge":1984},{"face_ref":497,"id":497,"is_hole":false,"start_halfedge":1988},{"face_ref":498,"id":498,"is_hole":false,"start_halfedge":1992},{"face_ref":499,"id":499,"is_hole":false,"start_halfedge":1996},{"face_ref":500,"id":500,"is_hole":false,"start_halfedge":2000},{"face_ref":501,"id":501,"is_hole":false,"start_halfedge":2004},{"face_ref":502,"id":502,"is_hole":false,"start_halfedge":2008},{"face_ref":503,"id":503,"is_hole":false,"start_halfedge":2012},{"face_ref":504,"id":504,"is_hole":false,"start_halfedge":2016},{"face_ref":505,"id":505,"is_hole":false,"start_halfedge":2020},{"face_ref":506,"id":506,"is_hole":false,"start_halfedge":2024},{"face_ref":507,"id":507,"is_hole":false,"start_halfedge":2028},{"face_ref":508,"id":508,"is_hole":false,"start_halfedge":2032},{"face_ref":509,"id":509,"is_hole":false,"start_halfedge":2036},{"face_ref":510,"id":510,"is_hole":false,"start_halfedge":2040},{"face_ref":511,"id":511,"is_hole":false,"start_halfedge":2044},{"face_ref":512,"id":512,"is_hole":false,"start_halfedge":2048},{"face_ref":513,"id":513,"is_hole":false,"start_halfedge":2052},{"face_ref":514,"id":514,"is_hole":false,"start_halfedge":2056},{"face_ref":515,"id":515,"is_hole":false,"start_halfedge":2060},{"face_ref":516,"id":516,"is_hole":false,"start_halfedge":2064},{"face_ref":517,"id":517,"is_hole":false,"start_halfedge":2068},{"face_ref":518,"id":518,"is_hole":false,"start_halfedge":2072},{"face_ref":519,"id":519,"is_hole":false,"start_halfedge":2076},{"face_ref":520,"id":520,"is_hole":false,"start_halfedge":2080},{"face_ref":521,"id":521,"is_hole":false,"start_halfedge":2084},{"face_ref":522,"id":522,"is_hole":false,"start_halfedge":2088},{"face_ref":523,"id":523,"is_hole":false,"start_halfedge":2092},{"face_ref":524,"id":524,"is_hole":false,"start_halfedge":2096},{"face_ref":525,"id":525,"is_hole":false,"start_halfedge":2100},{"face_ref":526,"id":526,"is_hole":false,"start_halfedge":2104},{"face_ref":527,"id":527,"is_hole":false,"start_halfedge":2108},{"face_ref":528,"id":528,"is_hole":false,"start_halfedge":2112},{"face_ref":529,"id":529,"is_hole":false,"start_halfedge":2116},{"face_ref":530,"id":530,"is_hole":false,"start_halfedge":2120},{"face_ref":531,"id":531,"is_hole":false,"start_halfedge":2124},{"face_ref":532,"id":532,"is_hole":false,"start_halfedge":2128},{"face_ref":533,"id":533,"is_hole":false,"start_halfedge":2132},{"face_ref":534,"id":534,"is_hole":false,"start_halfedge":2136},{"face_ref":535,"id":535,"is_hole":false,"start_halfedge":2140},{"face_ref":536,"id":536,"is_hole":false,"start_halfedge":2144},{"face_ref":537,"id":537,"is_hole":false,"start_halfedge":2148},{"face_ref":538,"id":538,"is_hole":false,"start_halfedge":2152},{"face_ref":539,"id":539,"is_hole":false,"start_halfedge":2156},{"face_ref":540,"id":540,"is_hole":false,"start_halfedge":2160},{"face_ref":541,"id":541,"is_hole":false,"start_halfedge":2164},{"face_ref":542,"id":542,"is_hole":false,"start_halfedge":2168},{"face_ref":543,"id":543,"is_hole":false,"start_halfedge":2172},{"face_ref":544,"id":544,"is_hole":false,"start_halfedge":2176},{"face_ref":545,"id":545,"is_hole":false,"start_halfedge":2180},{"face_ref":546,"id":546,"is_hole":false,"start_halfedge":2184},{"face_ref":547,"id":547,"is_hole":false,"start_halfedge":2188},{"face_ref":548,"id":548,"is_hole":false,"start_halfedge":2192},{"face_ref":549,"id":549,"is_hole":false,"start_halfedge":2196},{"face_ref":550,"id":550,"is_hole":false,"start_halfedge":2200},{"face_ref":551,"id":551,"is_hole":false,"start_halfedge":2204},{"face_ref":552,"id":552,"is_hole":false,"start_halfedge":2208},{"face_ref":553,"id":553,"is_hole":false,"start_halfedge":2212},{"face_ref":554,"id":554,"is_hole":false,"start_halfedge":2216},{"face_ref":555,"id":555,"is_hole":false,"start_halfedge":2220},{"face_ref":556,"id":556,"is_hole":false,"start_halfedge":2224},{"face_ref":557,"id":557,"is_hole":false,"start_halfedge":2228},{"face_ref":558,"id":558,"is_hole":false,"start_halfedge":2232},{"face_ref":559,"id":559,"is_hole":false,"start_halfedge":2236},{"face_ref":560,"id":560,"is_hole":false,"start_halfedge":2240},{"face_ref":561,"id":561,"is_hole":false,"start_halfedge":2244},{"face_ref":562,"id":562,"is_hole":false,"start_halfedge":2248},{"face_ref":563,"id":563,"is_hole":false,"start_halfedge":2252},{"face_ref":564,"id":564,"is_hole":false,"start_halfedge":2256},{"face_ref":565,"id":565,"is_hole":false,"start_halfedge":2260},{"face_ref":566,"id":566,"is_hole":false,"start_halfedge":2264},{"face_ref":567,"id":567,"is_hole":false,"start_halfedge":2268},{"face_ref":568,"id":568,"is_hole":false,"start_halfedge":2272},{"face_ref":569,"id":569,"is_hole":false,"start_halfedge":2276},{"face_ref":570,"id":570,"is_hole":false,"start_halfedge":2280},{"face_ref":571,"id":571,"is_hole":false,"start_halfedge":2284},{"face_ref":572,"id":572,"is_hole":false,"start_halfedge":2288},{"face_ref":573,"id":573,"is_hole":false,"start_halfedge":2292},{"face_ref":574,"id":574,"is_hole":false,"start_halfedge":2296},{"face_ref":575,"id":575,"is_hole":false,"start_halfedge":2300},{"face_ref":576,"id":576,"is_hole":false,"start_halfedge":2304},{"face_ref":577,"id":577,"is_hole":false,"start_halfedge":2308},{"face_ref":578,"id":578,"is_hole":false,"start_halfedge":2312},{"face_ref":579,"id":579,"is_hole":false,"start_halfedge":2316},{"face_ref":580,"id":580,"is_hole":false,"start_halfedge":2320},{"face_ref":581,"id":581,"is_hole":false,"start_halfedge":2324},{"face_ref":582,"id":582,"is_hole":false,"start_halfedge":2328},{"face_ref":583,"id":583,"is_hole":false,"start_halfedge":2332},{"face_ref":584,"id":584,"is_hole":false,"start_halfedge":2336},{"face_ref":585,"id":585,"is_hole":false,"start_halfedge":2340},{"face_ref":586,"id":586,"is_hole":false,"start_halfedge":2344},{"face_ref":587,"id":587,"is_hole":false,"start_halfedge":2348},{"face_ref":588,"id":588,"is_hole":false,"start_halfedge":2352},{"face_ref":589,"id":589,"is_hole":false,"start_halfedge":2356},{"face_ref":590,"id":590,"is_hole":false,"start_halfedge":2360},{"face_ref":591,"id":591,"is_hole":false,"start_halfedge":2364},{"face_ref":592,"id":592,"is_hole":false,"start_halfedge":2368},{"face_ref":593,"id":593,"is_hole":false,"start_halfedge":2372},{"face_ref":594,"id":594,"is_hole":false,"start_halfedge":2376},{"face_ref":595,"id":595,"is_hole":false,"start_halfedge":2380},{"face_ref":596,"id":596,"is_hole":false,"start_halfedge":2384},{"face_ref":597,"id":597,"is_hole":false,"start_halfedge":2388},{"face_ref":598,"id":598,"is_hole":false,"start_halfedge":2392},{"face_ref":599,"id":599,"is_hole":false,"start_halfedge":2396},{"face_ref":600,"id":600,"is_hole":false,"start_halfedge":2400},{"face_ref":601,"id":601,"is_hole":false,"start_halfedge":2404},{"face_ref":602,"id":602,"is_hole":false,"start_halfedge":2408},{"face_ref":603,"id":603,"is_hole":false,"start_halfedge":2412},{"face_ref":604,"id":604,"is_hole":false,"start_halfedge":2416},{"face_ref":605,"id":605,"is_hole":false,"start_halfedge":2420},{"face_ref":606,"id":606,"is_hole":false,"start_halfedge":2424},{"face_ref":607,"id":607,"is_hole":false,"start_halfedge":2428},{"face_ref":608,"id":608,"is_hole":false,"start_halfedge":2432},{"face_ref":609,"id":609,"is_hole":false,"start_halfedge":2436}],"shells":[{"faces":[0,4,9,3,2,8,7,6,12,13,11,1,5,10,18,16,21,15,14,17,20,19,24,25,23,22,30,28,33,27,26,29,32,31,36,37,35,34,42,40,45,39,38,41,44,43,48,49,47,46,54,52,57,51,50,53,56,55,60,61,59,58,66,64,69,63,62,65,68,67,72,73,71,70,78,76,81,75,74,77,80,79,84,85,83,82,90,88,93,87,86,89,92,91,96,97,95,94,102,100,105,99,98,101,104,103,108,109,107,106,114,112,117,111,110,113,116,115,120,121,119,118,126,124,129,123,122,125,128,127,132,133,131,130,138,136,141,135,134,137,140,139,144,145,143,142,150,148,153,147,146,149,152,151,156,157,155,154,162,160,165,159,158,161,164,163,168,169,167,166,174,172,177,171,170,173,176,175,180,181,179,178,186,184,189,183,182,185,188,187,192,193,191,190,198,196,201,195,194,197,200,199,204,205,203,202,210,208,213,207,206,209,212,211,216,217,215,214,222,220,225,219,218,221,224,223,228,229,227,226,234,232,237,231,230,233,236,235,240,241,239,238,246,244,249,243,242,245,248,247,252,253,251,250,258,256,261,255,254,257,260,259,264,265,263,262,270,268,273,267,266,269,272,271,276,277,275,274,282,280,285,279,278,281,284,283,288,289,287,286,294,292,297,291,290,293,296,295,300,301,299,298,306,304,309,303,302,305,308,307,312,313,311,310,318,316,321,315,314,317,320,319,324,325,323,322,330,328,333,327,326,329,332,331,336,337,335,334,342,340,345,339,338,341,344,343,348,349,347,346,354,352,357,351,350,353,356,355,360,361,359,358,366,364,369,363,362,365,368,367,372,373,371,370,378,376,381,375,374,377,380,379,384,385,383,382,390,388,393,387,386,389,392,391,396,397,395,394,402,400,405,399,398,401,404,403,408,409,407,406,414,412,417,411,410,413,416,415,420,421,419,418,426,424,429,423,422,425,428,427,432,433,431,430,438,436,441,435,434,437,440,439,444,445,443,442,450,448,453,447,446,449,452,451,456,457,455,454,462,460,465,459,458,461,464,463,468,469,467,466,474,472,477,471,470,473,476,475,480,481,479,478,486,484,489,483,482,485,488,487,492,493,491,490,498,496,501,495,494,497,500,499,504,505,503,502,510,508,513,507,506,509,512,511,516,517,515,514,522,520,525,519,518,521,524,523,528,529,527,526,534,532,537,531,530,533,536,535,540,541,539,538,546,544,549,543,542,545,548,547,552,553,551,550,558,556,561,555,554,557,560,559,564,565,563,562,570,568,573,567,566,569,572,571,576,577,575,574,582,580,585,579,578,581,584,583,588,589,587,586,594,592,597,591,590,593,596,595,600,601,599,598,607,605,603,604,602,606,609,608],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-30.0,2.0,0.1],"tolerance":1e-8},{"id":1,"outgoing_halfedge":1,"position":[-27.25,2.0,0.1],"tolerance":1e-8},{"id":2,"outgoing_halfedge":2,"position":[-27.25,3.0,0.1],"tolerance":1e-8},{"id":3,"outgoing_halfedge":3,"position":[-30.0,3.0,0.1],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-30.0,3.0,-0.1],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-27.25,3.0,-0.1],"tolerance":1e-8},{"id":6,"outgoing_halfedge":6,"position":[-27.25,2.0,-0.1],"tolerance":1e-8},{"id":7,"outgoing_halfedge":7,"position":[-30.0,2.0,-0.1],"tolerance":1e-8},{"id":8,"outgoing_halfedge":16,"position":[-30.0,0.0,0.1],"tolerance":1e-8},{"id":9,"outgoing_halfedge":17,"position":[-27.25,0.0,0.1],"tolerance":1e-8},{"id":10,"outgoing_halfedge":22,"position":[-27.25,0.0,-0.1],"tolerance":1e-8},{"id":11,"outgoing_halfedge":23,"position":[-30.0,0.0,-0.1],"tolerance":1e-8},{"id":12,"outgoing_halfedge":37,"position":[-26.75,2.0,0.1],"tolerance":1e-8},{"id":13,"outgoing_halfedge":38,"position":[-26.75,3.0,0.1],"tolerance":1e-8},{"id":14,"outgoing_halfedge":41,"position":[-26.75,3.0,-0.1],"tolerance":1e-8},{"id":15,"outgoing_halfedge":42,"position":[-26.75,2.0,-0.1],"tolerance":1e-8},{"id":16,"outgoing_halfedge":53,"position":[-26.15,2.0,0.1],"tolerance":1e-8},{"id":17,"outgoing_halfedge":54,"position":[-26.15,3.0,0.1],"tolerance":1e-8},{"id":18,"outgoing_halfedge":57,"position":[-26.15,3.0,-0.1],"tolerance":1e-8},{"id":19,"outgoing_halfedge":58,"position":[-26.15,2.0,-0.1],"tolerance":1e-8},{"id":20,"outgoing_halfedge":64,"position":[-26.75,0.0,0.1],"tolerance":1e-8},{"id":21,"outgoing_halfedge":65,"position":[-26.15,0.0,0.1],"tolerance":1e-8},{"id":22,"outgoing_halfedge":70,"position":[-26.15,0.0,-0.1],"tolerance":1e-8},{"id":23,"outgoing_halfedge":71,"position":[-26.75,0.0,-0.1],"tolerance":1e-8},{"id":24,"outgoing_halfedge":85,"position":[-25.65,2.0,0.1],"tolerance":1e-8},{"id":25,"outgoing_halfedge":86,"position":[-25.65,3.0,0.1],"tolerance":1e-8},{"id":26,"outgoing_halfedge":89,"position":[-25.65,3.0,-0.1],"tolerance":1e-8},{"id":27,"outgoing_halfedge":90,"position":[-25.65,2.0,-0.1],"tolerance":1e-8},{"id":28,"outgoing_halfedge":101,"position":[-25.05,2.0,0.1],"tolerance":1e-8},{"id":29,"outgoing_halfedge":102,"position":[-25.05,3.0,0.1],"tolerance":1e-8},{"id":30,"outgoing_halfedge":105,"position":[-25.05,3.0,-0.1],"tolerance":1e-8},{"id":31,"outgoing_halfedge":106,"position":[-25.05,2.0,-0.1],"tolerance":1e-8},{"id":32,"outgoing_halfedge":112,"position":[-25.65,0.0,0.1],"tolerance":1e-8},{"id":33,"outgoing_halfedge":113,"position":[-25.05,0.0,0.1],"tolerance":1e-8},{"id":34,"outgoing_halfedge":118,"position":[-25.05,0.0,-0.1],"tolerance":1e-8},{"id":35,"outgoing_halfedge":119,"position":[-25.65,0.0,-0.1],"tolerance":1e-8},{"id":36,"outgoing_halfedge":133,"position":[-24.55,2.0,0.1],"tolerance":1e-8},{"id":37,"outgoing_halfedge":134,"position":[-24.55,3.0,0.1],"tolerance":1e-8},{"id":38,"outgoing_halfedge":137,"position":[-24.55,3.0,-0.1],"tolerance":1e-8},{"id":39,"outgoing_halfedge":138,"position":[-24.55,2.0,-0.1],"tolerance":1e-8},{"id":40,"outgoing_halfedge":149,"position":[-23.95,2.0,0.1],"tolerance":1e-8},{"id":41,"outgoing_halfedge":150,"position":[-23.95,3.0,0.1],"tolerance":1e-8},{"id":42,"outgoing_halfedge":153,"position":[-23.95,3.0,-0.1],"tolerance":1e-8},{"id":43,"outgoing_halfedge":154,"position":[-23.95,2.0,-0.1],"tolerance":1e-8},{"id":44,"outgoing_halfedge":160,"position":[-24.55,0.0,0.1],"tolerance":1e-8},{"id":45,"outgoing_halfedge":161,"position":[-23.95,0.0,0.1],"tolerance":1e-8},{"id":46,"outgoing_halfedge":166,"position":[-23.95,0.0,-0.1],"tolerance":1e-8},{"id":47,"outgoing_halfedge":167,"position":[-24.55,0.0,-0.1],"tolerance":1e-8},{"id":48,"outgoing_halfedge":181,"position":[-23.45,2.0,0.1],"tolerance":1e-8},{"id":49,"outgoing_halfedge":182,"position":[-23.45,3.0,0.1],"tolerance":1e-8},{"id":50,"outgoing_halfedge":185,"position":[-23.45,3.0,-0.1],"tolerance":1e-8},{"id":51,"outgoing_halfedge":186,"position":[-23.45,2.0,-0.1],"tolerance":1e-8},{"id":52,"outgoing_halfedge":197,"position":[-22.85,2.0,0.1],"tolerance":1e-8},{"id":53,"outgoing_halfedge":198,"position":[-22.85,3.0,0.1],"tolerance":1e-8},{"id":54,"outgoing_halfedge":201,"position":[-22.85,3.0,-0.1],"tolerance":1e-8},{"id":55,"outgoing_halfedge":202,"position":[-22.85,2.0,-0.1],"tolerance":1e-8},{"id":56,"outgoing_halfedge":208,"position":[-23.45,0.0,0.1],"tolerance":1e-8},{"id":57,"outgoing_halfedge":209,"position":[-22.85,0.0,0.1],"tolerance":1e-8},{"id":58,"outgoing_halfedge":214,"position":[-22.85,0.0,-0.1],"tolerance":1e-8},{"id":59,"outgoing_halfedge":215,"position":[-23.45,0.0,-0.1],"tolerance":1e-8},{"id":60,"outgoing_halfedge":229,"position":[-22.35,2.0,0.1],"tolerance":1e-8},{"id":61,"outgoing_halfedge":230,"position":[-22.35,3.0,0.1],"tolerance":1e-8},{"id":62,"outgoing_halfedge":233,"position":[-22.35,3.0,-0.1],"tolerance":1e-8},{"id":63,"outgoing_halfedge":234,"position":[-22.35,2.0,-0.1],"tolerance":1e-8},{"id":64,"outgoing_halfedge":245,"position":[-21.75,2.0,0.1],"tolerance":1e-8},{"id":65,"outgoing_halfedge":246,"position":[-21.75,3.0,0.1],"tolerance":1e-8},{"id":66,"outgoing_halfedge":249,"position":[-21.75,3.0,-0.1],"tolerance":1e-8},{"id":67,"outgoing_halfedge":250,"position":[-21.75,2.0,-0.1],"tolerance":1e-8},{"id":68,"outgoing_halfedge":256,"position":[-22.35,0.0,0.1],"tolerance":1e-8},{"id":69,"outgoing_halfedge":257,"position":[-21.75,0.0,0.1],"tolerance":1e-8},{"id":70,"outgoing_halfedge":262,"position":[-21.75,0.0,-0.1],"tolerance":1e-8},{"id":71,"outgoing_halfedge":263,"position":[-22.35,0.0,-0.1],"tolerance":1e-8},{"id":72,"outgoing_halfedge":277,"position":[-21.25,2.0,0.1],"tolerance":1e-8},{"id":73,"outgoing_halfedge":278,"position":[-21.25,3.0,0.1],"tolerance":1e-8},{"id":74,"outgoing_halfedge":281,"position":[-21.25,3.0,-0.1],"tolerance":1e-8},{"id":75,"outgoing_halfedge":282,"position":[-21.25,2.0,-0.1],"tolerance":1e-8},{"id":76,"outgoing_halfedge":293,"position":[-20.65,2.0,0.1],"tolerance":1e-8},{"id":77,"outgoing_halfedge":294,"position":[-20.65,3.0,0.1],"tolerance":1e-8},{"id":78,"outgoing_halfedge":297,"position":[-20.65,3.0,-0.1],"tolerance":1e-8},{"id":79,"outgoing_halfedge":298,"position":[-20.65,2.0,-0.1],"tolerance":1e-8},{"id":80,"outgoing_halfedge":304,"position":[-21.25,0.0,0.1],"tolerance":1e-8},{"id":81,"outgoing_halfedge":305,"position":[-20.65,0.0,0.1],"tolerance":1e-8},{"id":82,"outgoing_halfedge":310,"position":[-20.65,0.0,-0.1],"tolerance":1e-8},{"id":83,"outgoing_halfedge":311,"position":[-21.25,0.0,-0.1],"tolerance":1e-8},{"id":84,"outgoing_halfedge":325,"position":[-20.15,2.0,0.1],"tolerance":1e-8},{"id":85,"outgoing_halfedge":326,"position":[-20.15,3.0,0.1],"tolerance":1e-8},{"id":86,"outgoing_halfedge":329,"position":[-20.15,3.0,-0.1],"tolerance":1e-8},{"id":87,"outgoing_halfedge":330,"position":[-20.15,2.0,-0.1],"tolerance":1e-8},{"id":88,"outgoing_halfedge":341,"position":[-19.549999999999997,2.0,0.1],"tolerance":1e-8},{"id":89,"outgoing_halfedge":342,"position":[-19.549999999999997,3.0,0.1],"tolerance":1e-8},{"id":90,"outgoing_halfedge":345,"position":[-19.549999999999997,3.0,-0.1],"tolerance":1e-8},{"id":91,"outgoing_halfedge":346,"position":[-19.549999999999997,2.0,-0.1],"tolerance":1e-8},{"id":92,"outgoing_halfedge":352,"position":[-20.15,0.0,0.1],"tolerance":1e-8},{"id":93,"outgoing_halfedge":353,"position":[-19.549999999999997,0.0,0.1],"tolerance":1e-8},{"id":94,"outgoing_halfedge":358,"position":[-19.549999999999997,0.0,-0.1],"tolerance":1e-8},{"id":95,"outgoing_halfedge":359,"position":[-20.15,0.0,-0.1],"tolerance":1e-8},{"id":96,"outgoing_halfedge":373,"position":[-19.049999999999997,2.0,0.1],"tolerance":1e-8},{"id":97,"outgoing_halfedge":374,"position":[-19.049999999999997,3.0,0.1],"tolerance":1e-8},{"id":98,"outgoing_halfedge":377,"position":[-19.049999999999997,3.0,-0.1],"tolerance":1e-8},{"id":99,"outgoing_halfedge":378,"position":[-19.049999999999997,2.0,-0.1],"tolerance":1e-8},{"id":100,"outgoing_halfedge":389,"position":[-18.45,2.0,0.1],"tolerance":1e-8},{"id":101,"outgoing_halfedge":390,"position":[-18.45,3.0,0.1],"tolerance":1e-8},{"id":102,"outgoing_halfedge":393,"position":[-18.45,3.0,-0.1],"tolerance":1e-8},{"id":103,"outgoing_halfedge":394,"position":[-18.45,2.0,-0.1],"tolerance":1e-8},{"id":104,"outgoing_halfedge":400,"position":[-19.049999999999997,0.0,0.1],"tolerance":1e-8},{"id":105,"outgoing_halfedge":401,"position":[-18.45,0.0,0.1],"tolerance":1e-8},{"id":106,"outgoing_halfedge":406,"position":[-18.45,0.0,-0.1],"tolerance":1e-8},{"id":107,"outgoing_halfedge":407,"position":[-19.049999999999997,0.0,-0.1],"tolerance":1e-8},{"id":108,"outgoing_halfedge":421,"position":[-17.95,2.0,0.1],"tolerance":1e-8},{"id":109,"outgoing_halfedge":422,"position":[-17.95,3.0,0.1],"tolerance":1e-8},{"id":110,"outgoing_halfedge":425,"position":[-17.95,3.0,-0.1],"tolerance":1e-8},{"id":111,"outgoing_halfedge":426,"position":[-17.95,2.0,-0.1],"tolerance":1e-8},{"id":112,"outgoing_halfedge":437,"position":[-17.35,2.0,0.1],"tolerance":1e-8},{"id":113,"outgoing_halfedge":438,"position":[-17.35,3.0,0.1],"tolerance":1e-8},{"id":114,"outgoing_halfedge":441,"position":[-17.35,3.0,-0.1],"tolerance":1e-8},{"id":115,"outgoing_halfedge":442,"position":[-17.35,2.0,-0.1],"tolerance":1e-8},{"id":116,"outgoing_halfedge":448,"position":[-17.95,0.0,0.1],"tolerance":1e-8},{"id":117,"outgoing_halfedge":449,"position":[-17.35,0.0,0.1],"tolerance":1e-8},{"id":118,"outgoing_halfedge":454,"position":[-17.35,0.0,-0.1],"tolerance":1e-8},{"id":119,"outgoing_halfedge":455,"position":[-17.95,0.0,-0.1],"tolerance":1e-8},{"id":120,"outgoing_halfedge":469,"position":[-16.85,2.0,0.1],"tolerance":1e-8},{"id":121,"outgoing_halfedge":470,"position":[-16.85,3.0,0.1],"tolerance":1e-8},{"id":122,"outgoing_halfedge":473,"position":[-16.85,3.0,-0.1],"tolerance":1e-8},{"id":123,"outgoing_halfedge":474,"position":[-16.85,2.0,-0.1],"tolerance":1e-8},{"id":124,"outgoing_halfedge":485,"position":[-16.25,2.0,0.1],"tolerance":1e-8},{"id":125,"outgoing_halfedge":486,"position":[-16.25,3.0,0.1],"tolerance":1e-8},{"id":126,"outgoing_halfedge":489,"position":[-16.25,3.0,-0.1],"tolerance":1e-8},{"id":127,"outgoing_halfedge":490,"position":[-16.25,2.0,-0.1],"tolerance":1e-8},{"id":128,"outgoing_halfedge":496,"position":[-16.85,0.0,0.1],"tolerance":1e-8},{"id":129,"outgoing_halfedge":497,"position":[-16.25,0.0,0.1],"tolerance":1e-8},{"id":130,"outgoing_halfedge":502,"position":[-16.25,0.0,-0.1],"tolerance":1e-8},{"id":131,"outgoing_halfedge":503,"position":[-16.85,0.0,-0.1],"tolerance":1e-8},{"id":132,"outgoing_halfedge":517,"position":[-15.75,2.0,0.1],"tolerance":1e-8},{"id":133,"outgoing_halfedge":518,"position":[-15.75,3.0,0.1],"tolerance":1e-8},{"id":134,"outgoing_halfedge":521,"position":[-15.75,3.0,-0.1],"tolerance":1e-8},{"id":135,"outgoing_halfedge":522,"position":[-15.75,2.0,-0.1],"tolerance":1e-8},{"id":136,"outgoing_halfedge":533,"position":[-15.149999999999999,2.0,0.1],"tolerance":1e-8},{"id":137,"outgoing_halfedge":534,"position":[-15.149999999999999,3.0,0.1],"tolerance":1e-8},{"id":138,"outgoing_halfedge":537,"position":[-15.149999999999999,3.0,-0.1],"tolerance":1e-8},{"id":139,"outgoing_halfedge":538,"position":[-15.149999999999999,2.0,-0.1],"tolerance":1e-8},{"id":140,"outgoing_halfedge":544,"position":[-15.75,0.0,0.1],"tolerance":1e-8},{"id":141,"outgoing_halfedge":545,"position":[-15.149999999999999,0.0,0.1],"tolerance":1e-8},{"id":142,"outgoing_halfedge":550,"position":[-15.149999999999999,0.0,-0.1],"tolerance":1e-8},{"id":143,"outgoing_halfedge":551,"position":[-15.75,0.0,-0.1],"tolerance":1e-8},{"id":144,"outgoing_halfedge":565,"position":[-14.649999999999999,2.0,0.1],"tolerance":1e-8},{"id":145,"outgoing_halfedge":566,"position":[-14.649999999999999,3.0,0.1],"tolerance":1e-8},{"id":146,"outgoing_halfedge":569,"position":[-14.649999999999999,3.0,-0.1],"tolerance":1e-8},{"id":147,"outgoing_halfedge":570,"position":[-14.649999999999999,2.0,-0.1],"tolerance":1e-8},{"id":148,"outgoing_halfedge":581,"position":[-14.049999999999999,2.0,0.1],"tolerance":1e-8},{"id":149,"outgoing_halfedge":582,"position":[-14.049999999999999,3.0,0.1],"tolerance":1e-8},{"id":150,"outgoing_halfedge":585,"position":[-14.049999999999999,3.0,-0.1],"tolerance":1e-8},{"id":151,"outgoing_halfedge":586,"position":[-14.049999999999999,2.0,-0.1],"tolerance":1e-8},{"id":152,"outgoing_halfedge":592,"position":[-14.649999999999999,0.0,0.1],"tolerance":1e-8},{"id":153,"outgoing_halfedge":593,"position":[-14.049999999999999,0.0,0.1],"tolerance":1e-8},{"id":154,"outgoing_halfedge":598,"position":[-14.049999999999999,0.0,-0.1],"tolerance":1e-8},{"id":155,"outgoing_halfedge":599,"position":[-14.649999999999999,0.0,-0.1],"tolerance":1e-8},{"id":156,"outgoing_halfedge":613,"position":[-13.549999999999997,2.0,0.1],"tolerance":1e-8},{"id":157,"outgoing_halfedge":614,"position":[-13.549999999999997,3.0,0.1],"tolerance":1e-8},{"id":158,"outgoing_halfedge":617,"position":[-13.549999999999997,3.0,-0.1],"tolerance":1e-8},{"id":159,"outgoing_halfedge":618,"position":[-13.549999999999997,2.0,-0.1],"tolerance":1e-8},{"id":160,"outgoing_halfedge":629,"position":[-12.95,2.0,0.1],"tolerance":1e-8},{"id":161,"outgoing_halfedge":630,"position":[-12.95,3.0,0.1],"tolerance":1e-8},{"id":162,"outgoing_halfedge":633,"position":[-12.95,3.0,-0.1],"tolerance":1e-8},{"id":163,"outgoing_halfedge":634,"position":[-12.95,2.0,-0.1],"tolerance":1e-8},{"id":164,"outgoing_halfedge":640,"position":[-13.549999999999997,0.0,0.1],"tolerance":1e-8},{"id":165,"outgoing_halfedge":641,"position":[-12.95,0.0,0.1],"tolerance":1e-8},{"id":166,"outgoing_halfedge":646,"position":[-12.95,0.0,-0.1],"tolerance":1e-8},{"id":167,"outgoing_halfedge":647,"position":[-13.549999999999997,0.0,-0.1],"tolerance":1e-8},{"id":168,"outgoing_halfedge":661,"position":[-12.45,2.0,0.1],"tolerance":1e-8},{"id":169,"outgoing_halfedge":662,"position":[-12.45,3.0,0.1],"tolerance":1e-8},{"id":170,"outgoing_halfedge":665,"position":[-12.45,3.0,-0.1],"tolerance":1e-8},{"id":171,"outgoing_halfedge":666,"position":[-12.45,2.0,-0.1],"tolerance":1e-8},{"id":172,"outgoing_halfedge":677,"position":[-11.849999999999998,2.0,0.1],"tolerance":1e-8},{"id":173,"outgoing_halfedge":678,"position":[-11.849999999999998,3.0,0.1],"tolerance":1e-8},{"id":174,"outgoing_halfedge":681,"position":[-11.849999999999998,3.0,-0.1],"tolerance":1e-8},{"id":175,"outgoing_halfedge":682,"position":[-11.849999999999998,2.0,-0.1],"tolerance":1e-8},{"id":176,"outgoing_halfedge":688,"position":[-12.45,0.0,0.1],"tolerance":1e-8},{"id":177,"outgoing_halfedge":689,"position":[-11.849999999999998,0.0,0.1],"tolerance":1e-8},{"id":178,"outgoing_halfedge":694,"position":[-11.849999999999998,0.0,-0.1],"tolerance":1e-8},{"id":179,"outgoing_halfedge":695,"position":[-12.45,0.0,-0.1],"tolerance":1e-8},{"id":180,"outgoing_halfedge":709,"position":[-11.349999999999998,2.0,0.1],"tolerance":1e-8},{"id":181,"outgoing_halfedge":710,"position":[-11.349999999999998,3.0,0.1],"tolerance":1e-8},{"id":182,"outgoing_halfedge":713,"position":[-11.349999999999998,3.0,-0.1],"tolerance":1e-8},{"id":183,"outgoing_halfedge":714,"position":[-11.349999999999998,2.0,-0.1],"tolerance":1e-8},{"id":184,"outgoing_halfedge":725,"position":[-10.75,2.0,0.1],"tolerance":1e-8},{"id":185,"outgoing_halfedge":726,"position":[-10.75,3.0,0.1],"tolerance":1e-8},{"id":186,"outgoing_halfedge":729,"position":[-10.75,3.0,-0.1],"tolerance":1e-8},{"id":187,"outgoing_halfedge":730,"position":[-10.75,2.0,-0.1],"tolerance":1e-8},{"id":188,"outgoing_halfedge":736,"position":[-11.349999999999998,0.0,0.1],"tolerance":1e-8},{"id":189,"outgoing_halfedge":737,"position":[-10.75,0.0,0.1],"tolerance":1e-8},{"id":190,"outgoing_halfedge":742,"position":[-10.75,0.0,-0.1],"tolerance":1e-8},{"id":191,"outgoing_halfedge":743,"position":[-11.349999999999998,0.0,-0.1],"tolerance":1e-8},{"id":192,"outgoing_halfedge":757,"position":[-10.25,2.0,0.1],"tolerance":1e-8},{"id":193,"outgoing_halfedge":758,"position":[-10.25,3.0,0.1],"tolerance":1e-8},{"id":194,"outgoing_halfedge":761,"position":[-10.25,3.0,-0.1],"tolerance":1e-8},{"id":195,"outgoing_halfedge":762,"position":[-10.25,2.0,-0.1],"tolerance":1e-8},{"id":196,"outgoing_halfedge":773,"position":[-9.649999999999999,2.0,0.1],"tolerance":1e-8},{"id":197,"outgoing_halfedge":774,"position":[-9.649999999999999,3.0,0.1],"tolerance":1e-8},{"id":198,"outgoing_halfedge":777,"position":[-9.649999999999999,3.0,-0.1],"tolerance":1e-8},{"id":199,"outgoing_halfedge":778,"position":[-9.649999999999999,2.0,-0.1],"tolerance":1e-8},{"id":200,"outgoing_halfedge":784,"position":[-10.25,0.0,0.1],"tolerance":1e-8},{"id":201,"outgoing_halfedge":785,"position":[-9.649999999999999,0.0,0.1],"tolerance":1e-8},{"id":202,"outgoing_halfedge":790,"position":[-9.649999999999999,0.0,-0.1],"tolerance":1e-8},{"id":203,"outgoing_halfedge":791,"position":[-10.25,0.0,-0.1],"tolerance":1e-8},{"id":204,"outgoing_halfedge":805,"position":[-9.149999999999999,2.0,0.1],"tolerance":1e-8},{"id":205,"outgoing_halfedge":806,"position":[-9.149999999999999,3.0,0.1],"tolerance":1e-8},{"id":206,"outgoing_halfedge":809,"position":[-9.149999999999999,3.0,-0.1],"tolerance":1e-8},{"id":207,"outgoing_halfedge":810,"position":[-9.149999999999999,2.0,-0.1],"tolerance":1e-8},{"id":208,"outgoing_halfedge":821,"position":[-8.549999999999997,2.0,0.1],"tolerance":1e-8},{"id":209,"outgoing_halfedge":822,"position":[-8.549999999999997,3.0,0.1],"tolerance":1e-8},{"id":210,"outgoing_halfedge":825,"position":[-8.549999999999997,3.0,-0.1],"tolerance":1e-8},{"id":211,"outgoing_halfedge":826,"position":[-8.549999999999997,2.0,-0.1],"tolerance":1e-8},{"id":212,"outgoing_halfedge":832,"position":[-9.149999999999999,0.0,0.1],"tolerance":1e-8},{"id":213,"outgoing_halfedge":833,"position":[-8.549999999999997,0.0,0.1],"tolerance":1e-8},{"id":214,"outgoing_halfedge":838,"position":[-8.549999999999997,0.0,-0.1],"tolerance":1e-8},{"id":215,"outgoing_halfedge":839,"position":[-9.149999999999999,0.0,-0.1],"tolerance":1e-8},{"id":216,"outgoing_halfedge":853,"position":[-8.049999999999997,2.0,0.1],"tolerance":1e-8},{"id":217,"outgoing_halfedge":854,"position":[-8.049999999999997,3.0,0.1],"tolerance":1e-8},{"id":218,"outgoing_halfedge":857,"position":[-8.049999999999997,3.0,-0.1],"tolerance":1e-8},{"id":219,"outgoing_halfedge":858,"position":[-8.049999999999997,2.0,-0.1],"tolerance":1e-8},{"id":220,"outgoing_halfedge":869,"position":[-7.449999999999999,2.0,0.1],"tolerance":1e-8},{"id":221,"outgoing_halfedge":870,"position":[-7.449999999999999,3.0,0.1],"tolerance":1e-8},{"id":222,"outgoing_halfedge":873,"position":[-7.449999999999999,3.0,-0.1],"tolerance":1e-8},{"id":223,"outgoing_halfedge":874,"position":[-7.449999999999999,2.0,-0.1],"tolerance":1e-8},{"id":224,"outgoing_halfedge":880,"position":[-8.049999999999997,0.0,0.1],"tolerance":1e-8},{"id":225,"outgoing_halfedge":881,"position":[-7.449999999999999,0.0,0.1],"tolerance":1e-8},{"id":226,"outgoing_halfedge":886,"position":[-7.449999999999999,0.0,-0.1],"tolerance":1e-8},{"id":227,"outgoing_halfedge":887,"position":[-8.049999999999997,0.0,-0.1],"tolerance":1e-8},{"id":228,"outgoing_halfedge":901,"position":[-6.949999999999999,2.0,0.1],"tolerance":1e-8},{"id":229,"outgoing_halfedge":902,"position":[-6.949999999999999,3.0,0.1],"tolerance":1e-8},{"id":230,"outgoing_halfedge":905,"position":[-6.949999999999999,3.0,-0.1],"tolerance":1e-8},{"id":231,"outgoing_halfedge":906,"position":[-6.949999999999999,2.0,-0.1],"tolerance":1e-8},{"id":232,"outgoing_halfedge":917,"position":[-6.349999999999998,2.0,0.1],"tolerance":1e-8},{"id":233,"outgoing_halfedge":918,"position":[-6.349999999999998,3.0,0.1],"tolerance":1e-8},{"id":234,"outgoing_halfedge":921,"position":[-6.349999999999998,3.0,-0.1],"tolerance":1e-8},{"id":235,"outgoing_halfedge":922,"position":[-6.349999999999998,2.0,-0.1],"tolerance":1e-8},{"id":236,"outgoing_halfedge":928,"position":[-6.949999999999999,0.0,0.1],"tolerance":1e-8},{"id":237,"outgoing_halfedge":929,"position":[-6.349999999999998,0.0,0.1],"tolerance":1e-8},{"id":238,"outgoing_halfedge":934,"position":[-6.349999999999998,0.0,-0.1],"tolerance":1e-8},{"id":239,"outgoing_halfedge":935,"position":[-6.949999999999999,0.0,-0.1],"tolerance":1e-8},{"id":240,"outgoing_halfedge":949,"position":[-5.849999999999998,2.0,0.1],"tolerance":1e-8},{"id":241,"outgoing_halfedge":950,"position":[-5.849999999999998,3.0,0.1],"tolerance":1e-8},{"id":242,"outgoing_halfedge":953,"position":[-5.849999999999998,3.0,-0.1],"tolerance":1e-8},{"id":243,"outgoing_halfedge":954,"position":[-5.849999999999998,2.0,-0.1],"tolerance":1e-8},{"id":244,"outgoing_halfedge":965,"position":[-5.25,2.0,0.1],"tolerance":1e-8},{"id":245,"outgoing_halfedge":966,"position":[-5.25,3.0,0.1],"tolerance":1e-8},{"id":246,"outgoing_halfedge":969,"position":[-5.25,3.0,-0.1],"tolerance":1e-8},{"id":247,"outgoing_halfedge":970,"position":[-5.25,2.0,-0.1],"tolerance":1e-8},{"id":248,"outgoing_halfedge":976,"position":[-5.849999999999998,0.0,0.1],"tolerance":1e-8},{"id":249,"outgoing_halfedge":977,"position":[-5.25,0.0,0.1],"tolerance":1e-8},{"id":250,"outgoing_halfedge":982,"position":[-5.25,0.0,-0.1],"tolerance":1e-8},{"id":251,"outgoing_halfedge":983,"position":[-5.849999999999998,0.0,-0.1],"tolerance":1e-8},{"id":252,"outgoing_halfedge":997,"position":[-4.75,2.0,0.1],"tolerance":1e-8},{"id":253,"outgoing_halfedge":998,"position":[-4.75,3.0,0.1],"tolerance":1e-8},{"id":254,"outgoing_halfedge":1001,"position":[-4.75,3.0,-0.1],"tolerance":1e-8},{"id":255,"outgoing_halfedge":1002,"position":[-4.75,2.0,-0.1],"tolerance":1e-8},{"id":256,"outgoing_halfedge":1013,"position":[-4.149999999999999,2.0,0.1],"tolerance":1e-8},{"id":257,"outgoing_halfedge":1014,"position":[-4.149999999999999,3.0,0.1],"tolerance":1e-8},{"id":258,"outgoing_halfedge":1017,"position":[-4.149999999999999,3.0,-0.1],"tolerance":1e-8},{"id":259,"outgoing_halfedge":1018,"position":[-4.149999999999999,2.0,-0.1],"tolerance":1e-8},{"id":260,"outgoing_halfedge":1024,"position":[-4.75,0.0,0.1],"tolerance":1e-8},{"id":261,"outgoing_halfedge":1025,"position":[-4.149999999999999,0.0,0.1],"tolerance":1e-8},{"id":262,"outgoing_halfedge":1030,"position":[-4.149999999999999,0.0,-0.1],"tolerance":1e-8},{"id":263,"outgoing_halfedge":1031,"position":[-4.75,0.0,-0.1],"tolerance":1e-8},{"id":264,"outgoing_halfedge":1045,"position":[-3.6499999999999986,2.0,0.1],"tolerance":1e-8},{"id":265,"outgoing_halfedge":1046,"position":[-3.6499999999999986,3.0,0.1],"tolerance":1e-8},{"id":266,"outgoing_halfedge":1049,"position":[-3.6499999999999986,3.0,-0.1],"tolerance":1e-8},{"id":267,"outgoing_halfedge":1050,"position":[-3.6499999999999986,2.0,-0.1],"tolerance":1e-8},{"id":268,"outgoing_halfedge":1061,"position":[-3.049999999999997,2.0,0.1],"tolerance":1e-8},{"id":269,"outgoing_halfedge":1062,"position":[-3.049999999999997,3.0,0.1],"tolerance":1e-8},{"id":270,"outgoing_halfedge":1065,"position":[-3.049999999999997,3.0,-0.1],"tolerance":1e-8},{"id":271,"outgoing_halfedge":1066,"position":[-3.049999999999997,2.0,-0.1],"tolerance":1e-8},{"id":272,"outgoing_halfedge":1072,"position":[-3.6499999999999986,0.0,0.1],"tolerance":1e-8},{"id":273,"outgoing_halfedge":1073,"position":[-3.049999999999997,0.0,0.1],"tolerance":1e-8},{"id":274,"outgoing_halfedge":1078,"position":[-3.049999999999997,0.0,-0.1],"tolerance":1e-8},{"id":275,"outgoing_halfedge":1079,"position":[-3.6499999999999986,0.0,-0.1],"tolerance":1e-8},{"id":276,"outgoing_halfedge":1093,"position":[-2.549999999999997,2.0,0.1],"tolerance":1e-8},{"id":277,"outgoing_halfedge":1094,"position":[-2.549999999999997,3.0,0.1],"tolerance":1e-8},{"id":278,"outgoing_halfedge":1097,"position":[-2.549999999999997,3.0,-0.1],"tolerance":1e-8},{"id":279,"outgoing_halfedge":1098,"position":[-2.549999999999997,2.0,-0.1],"tolerance":1e-8},{"id":280,"outgoing_halfedge":1109,"position":[-1.9499999999999993,2.0,0.1],"tolerance":1e-8},{"id":281,"outgoing_halfedge":1110,"position":[-1.9499999999999993,3.0,0.1],"tolerance":1e-8},{"id":282,"outgoing_halfedge":1113,"position":[-1.9499999999999993,3.0,-0.1],"tolerance":1e-8},{"id":283,"outgoing_halfedge":1114,"position":[-1.9499999999999993,2.0,-0.1],"tolerance":1e-8},{"id":284,"outgoing_halfedge":1120,"position":[-2.549999999999997,0.0,0.1],"tolerance":1e-8},{"id":285,"outgoing_halfedge":1121,"position":[-1.9499999999999993,0.0,0.1],"tolerance":1e-8},{"id":286,"outgoing_halfedge":1126,"position":[-1.9499999999999993,0.0,-0.1],"tolerance":1e-8},{"id":287,"outgoing_halfedge":1127,"position":[-2.549999999999997,0.0,-0.1],"tolerance":1e-8},{"id":288,"outgoing_halfedge":1141,"position":[-1.4499999999999993,2.0,0.1],"tolerance":1e-8},{"id":289,"outgoing_halfedge":1142,"position":[-1.4499999999999993,3.0,0.1],"tolerance":1e-8},{"id":290,"outgoing_halfedge":1145,"position":[-1.4499999999999993,3.0,-0.1],"tolerance":1e-8},{"id":291,"outgoing_halfedge":1146,"position":[-1.4499999999999993,2.0,-0.1],"tolerance":1e-8},{"id":292,"outgoing_halfedge":1157,"position":[-0.8499999999999979,2.0,0.1],"tolerance":1e-8},{"id":293,"outgoing_halfedge":1158,"position":[-0.8499999999999979,3.0,0.1],"tolerance":1e-8},{"id":294,"outgoing_halfedge":1161,"position":[-0.8499999999999979,3.0,-0.1],"tolerance":1e-8},{"id":295,"outgoing_halfedge":1162,"position":[-0.8499999999999979,2.0,-0.1],"tolerance":1e-8},{"id":296,"outgoing_halfedge":1168,"position":[-1.4499999999999993,0.0,0.1],"tolerance":1e-8},{"id":297,"outgoing_halfedge":1169,"position":[-0.8499999999999979,0.0,0.1],"tolerance":1e-8},{"id":298,"outgoing_halfedge":1174,"position":[-0.8499999999999979,0.0,-0.1],"tolerance":1e-8},{"id":299,"outgoing_halfedge":1175,"position":[-1.4499999999999993,0.0,-0.1],"tolerance":1e-8},{"id":300,"outgoing_halfedge":1189,"position":[-0.34999999999999787,2.0,0.1],"tolerance":1e-8},{"id":301,"outgoing_halfedge":1190,"position":[-0.34999999999999787,3.0,0.1],"tolerance":1e-8},{"id":302,"outgoing_halfedge":1193,"position":[-0.34999999999999787,3.0,-0.1],"tolerance":1e-8},{"id":303,"outgoing_halfedge":1194,"position":[-0.34999999999999787,2.0,-0.1],"tolerance":1e-8},{"id":304,"outgoing_halfedge":1205,"position":[0.25000000000000355,2.0,0.1],"tolerance":1e-8},{"id":305,"outgoing_halfedge":1206,"position":[0.25000000000000355,3.0,0.1],"tolerance":1e-8},{"id":306,"outgoing_halfedge":1209,"position":[0.25000000000000355,3.0,-0.1],"tolerance":1e-8},{"id":307,"outgoing_halfedge":1210,"position":[0.25000000000000355,2.0,-0.1],"tolerance":1e-8},{"id":308,"outgoing_halfedge":1216,"position":[-0.34999999999999787,0.0,0.1],"tolerance":1e-8},{"id":309,"outgoing_halfedge":1217,"position":[0.25000000000000355,0.0,0.1],"tolerance":1e-8},{"id":310,"outgoing_halfedge":1222,"position":[0.25000000000000355,0.0,-0.1],"tolerance":1e-8},{"id":311,"outgoing_halfedge":1223,"position":[-0.34999999999999787,0.0,-0.1],"tolerance":1e-8},{"id":312,"outgoing_halfedge":1237,"position":[0.7500000000000036,2.0,0.1],"tolerance":1e-8},{"id":313,"outgoing_halfedge":1238,"position":[0.7500000000000036,3.0,0.1],"tolerance":1e-8},{"id":314,"outgoing_halfedge":1241,"position":[0.7500000000000036,3.0,-0.1],"tolerance":1e-8},{"id":315,"outgoing_halfedge":1242,"position":[0.7500000000000036,2.0,-0.1],"tolerance":1e-8},{"id":316,"outgoing_halfedge":1253,"position":[1.3500000000000014,2.0,0.1],"tolerance":1e-8},{"id":317,"outgoing_halfedge":1254,"position":[1.3500000000000014,3.0,0.1],"tolerance":1e-8},{"id":318,"outgoing_halfedge":1257,"position":[1.3500000000000014,3.0,-0.1],"tolerance":1e-8},{"id":319,"outgoing_halfedge":1258,"position":[1.3500000000000014,2.0,-0.1],"tolerance":1e-8},{"id":320,"outgoing_halfedge":1264,"position":[0.7500000000000036,0.0,0.1],"tolerance":1e-8},{"id":321,"outgoing_halfedge":1265,"position":[1.3500000000000014,0.0,0.1],"tolerance":1e-8},{"id":322,"outgoing_halfedge":1270,"position":[1.3500000000000014,0.0,-0.1],"tolerance":1e-8},{"id":323,"outgoing_halfedge":1271,"position":[0.7500000000000036,0.0,-0.1],"tolerance":1e-8},{"id":324,"outgoing_halfedge":1285,"position":[1.8500000000000014,2.0,0.1],"tolerance":1e-8},{"id":325,"outgoing_halfedge":1286,"position":[1.8500000000000014,3.0,0.1],"tolerance":1e-8},{"id":326,"outgoing_halfedge":1289,"position":[1.8500000000000014,3.0,-0.1],"tolerance":1e-8},{"id":327,"outgoing_halfedge":1290,"position":[1.8500000000000014,2.0,-0.1],"tolerance":1e-8},{"id":328,"outgoing_halfedge":1301,"position":[2.450000000000003,2.0,0.1],"tolerance":1e-8},{"id":329,"outgoing_halfedge":1302,"position":[2.450000000000003,3.0,0.1],"tolerance":1e-8},{"id":330,"outgoing_halfedge":1305,"position":[2.450000000000003,3.0,-0.1],"tolerance":1e-8},{"id":331,"outgoing_halfedge":1306,"position":[2.450000000000003,2.0,-0.1],"tolerance":1e-8},{"id":332,"outgoing_halfedge":1312,"position":[1.8500000000000014,0.0,0.1],"tolerance":1e-8},{"id":333,"outgoing_halfedge":1313,"position":[2.450000000000003,0.0,0.1],"tolerance":1e-8},{"id":334,"outgoing_halfedge":1318,"position":[2.450000000000003,0.0,-0.1],"tolerance":1e-8},{"id":335,"outgoing_halfedge":1319,"position":[1.8500000000000014,0.0,-0.1],"tolerance":1e-8},{"id":336,"outgoing_halfedge":1333,"position":[2.950000000000003,2.0,0.1],"tolerance":1e-8},{"id":337,"outgoing_halfedge":1334,"position":[2.950000000000003,3.0,0.1],"tolerance":1e-8},{"id":338,"outgoing_halfedge":1337,"position":[2.950000000000003,3.0,-0.1],"tolerance":1e-8},{"id":339,"outgoing_halfedge":1338,"position":[2.950000000000003,2.0,-0.1],"tolerance":1e-8},{"id":340,"outgoing_halfedge":1349,"position":[3.5500000000000043,2.0,0.1],"tolerance":1e-8},{"id":341,"outgoing_halfedge":1350,"position":[3.5500000000000043,3.0,0.1],"tolerance":1e-8},{"id":342,"outgoing_halfedge":1353,"position":[3.5500000000000043,3.0,-0.1],"tolerance":1e-8},{"id":343,"outgoing_halfedge":1354,"position":[3.5500000000000043,2.0,-0.1],"tolerance":1e-8},{"id":344,"outgoing_halfedge":1360,"position":[2.950000000000003,0.0,0.1],"tolerance":1e-8},{"id":345,"outgoing_halfedge":1361,"position":[3.5500000000000043,0.0,0.1],"tolerance":1e-8},{"id":346,"outgoing_halfedge":1366,"position":[3.5500000000000043,0.0,-0.1],"tolerance":1e-8},{"id":347,"outgoing_halfedge":1367,"position":[2.950000000000003,0.0,-0.1],"tolerance":1e-8},{"id":348,"outgoing_halfedge":1381,"position":[4.050000000000004,2.0,0.1],"tolerance":1e-8},{"id":349,"outgoing_halfedge":1382,"position":[4.050000000000004,3.0,0.1],"tolerance":1e-8},{"id":350,"outgoing_halfedge":1385,"position":[4.050000000000004,3.0,-0.1],"tolerance":1e-8},{"id":351,"outgoing_halfedge":1386,"position":[4.050000000000004,2.0,-0.1],"tolerance":1e-8},{"id":352,"outgoing_halfedge":1397,"position":[4.650000000000006,2.0,0.1],"tolerance":1e-8},{"id":353,"outgoing_halfedge":1398,"position":[4.650000000000006,3.0,0.1],"tolerance":1e-8},{"id":354,"outgoing_halfedge":1401,"position":[4.650000000000006,3.0,-0.1],"tolerance":1e-8},{"id":355,"outgoing_halfedge":1402,"position":[4.650000000000006,2.0,-0.1],"tolerance":1e-8},{"id":356,"outgoing_halfedge":1408,"position":[4.050000000000004,0.0,0.1],"tolerance":1e-8},{"id":357,"outgoing_halfedge":1409,"position":[4.650000000000006,0.0,0.1],"tolerance":1e-8},{"id":358,"outgoing_halfedge":1414,"position":[4.650000000000006,0.0,-0.1],"tolerance":1e-8},{"id":359,"outgoing_halfedge":1415,"position":[4.050000000000004,0.0,-0.1],"tolerance":1e-8},{"id":360,"outgoing_halfedge":1429,"position":[5.150000000000006,2.0,0.1],"tolerance":1e-8},{"id":361,"outgoing_halfedge":1430,"position":[5.150000000000006,3.0,0.1],"tolerance":1e-8},{"id":362,"outgoing_halfedge":1433,"position":[5.150000000000006,3.0,-0.1],"tolerance":1e-8},{"id":363,"outgoing_halfedge":1434,"position":[5.150000000000006,2.0,-0.1],"tolerance":1e-8},{"id":364,"outgoing_halfedge":1445,"position":[5.75,2.0,0.1],"tolerance":1e-8},{"id":365,"outgoing_halfedge":1446,"position":[5.75,3.0,0.1],"tolerance":1e-8},{"id":366,"outgoing_halfedge":1449,"position":[5.75,3.0,-0.1],"tolerance":1e-8},{"id":367,"outgoing_halfedge":1450,"position":[5.75,2.0,-0.1],"tolerance":1e-8},{"id":368,"outgoing_halfedge":1456,"position":[5.150000000000006,0.0,0.1],"tolerance":1e-8},{"id":369,"outgoing_halfedge":1457,"position":[5.75,0.0,0.1],"tolerance":1e-8},{"id":370,"outgoing_halfedge":1462,"position":[5.75,0.0,-0.1],"tolerance":1e-8},{"id":371,"outgoing_halfedge":1463,"position":[5.150000000000006,0.0,-0.1],"tolerance":1e-8},{"id":372,"outgoing_halfedge":1477,"position":[6.25,2.0,0.1],"tolerance":1e-8},{"id":373,"outgoing_halfedge":1478,"position":[6.25,3.0,0.1],"tolerance":1e-8},{"id":374,"outgoing_halfedge":1481,"position":[6.25,3.0,-0.1],"tolerance":1e-8},{"id":375,"outgoing_halfedge":1482,"position":[6.25,2.0,-0.1],"tolerance":1e-8},{"id":376,"outgoing_halfedge":1493,"position":[6.850000000000001,2.0,0.1],"tolerance":1e-8},{"id":377,"outgoing_halfedge":1494,"position":[6.850000000000001,3.0,0.1],"tolerance":1e-8},{"id":378,"outgoing_halfedge":1497,"position":[6.850000000000001,3.0,-0.1],"tolerance":1e-8},{"id":379,"outgoing_halfedge":1498,"position":[6.850000000000001,2.0,-0.1],"tolerance":1e-8},{"id":380,"outgoing_halfedge":1504,"position":[6.25,0.0,0.1],"tolerance":1e-8},{"id":381,"outgoing_halfedge":1505,"position":[6.850000000000001,0.0,0.1],"tolerance":1e-8},{"id":382,"outgoing_halfedge":1510,"position":[6.850000000000001,0.0,-0.1],"tolerance":1e-8},{"id":383,"outgoing_halfedge":1511,"position":[6.25,0.0,-0.1],"tolerance":1e-8},{"id":384,"outgoing_halfedge":1525,"position":[7.350000000000001,2.0,0.1],"tolerance":1e-8},{"id":385,"outgoing_halfedge":1526,"position":[7.350000000000001,3.0,0.1],"tolerance":1e-8},{"id":386,"outgoing_halfedge":1529,"position":[7.350000000000001,3.0,-0.1],"tolerance":1e-8},{"id":387,"outgoing_halfedge":1530,"position":[7.350000000000001,2.0,-0.1],"tolerance":1e-8},{"id":388,"outgoing_halfedge":1541,"position":[7.950000000000003,2.0,0.1],"tolerance":1e-8},{"id":389,"outgoing_halfedge":1542,"position":[7.950000000000003,3.0,0.1],"tolerance":1e-8},{"id":390,"outgoing_halfedge":1545,"position":[7.950000000000003,3.0,-0.1],"tolerance":1e-8},{"id":391,"outgoing_halfedge":1546,"position":[7.950000000000003,2.0,-0.1],"tolerance":1e-8},{"id":392,"outgoing_halfedge":1552,"position":[7.350000000000001,0.0,0.1],"tolerance":1e-8},{"id":393,"outgoing_halfedge":1553,"position":[7.950000000000003,0.0,0.1],"tolerance":1e-8},{"id":394,"outgoing_halfedge":1558,"position":[7.950000000000003,0.0,-0.1],"tolerance":1e-8},{"id":395,"outgoing_halfedge":1559,"position":[7.350000000000001,0.0,-0.1],"tolerance":1e-8},{"id":396,"outgoing_halfedge":1573,"position":[8.450000000000003,2.0,0.1],"tolerance":1e-8},{"id":397,"outgoing_halfedge":1574,"position":[8.450000000000003,3.0,0.1],"tolerance":1e-8},{"id":398,"outgoing_halfedge":1577,"position":[8.450000000000003,3.0,-0.1],"tolerance":1e-8},{"id":399,"outgoing_halfedge":1578,"position":[8.450000000000003,2.0,-0.1],"tolerance":1e-8},{"id":400,"outgoing_halfedge":1589,"position":[9.050000000000004,2.0,0.1],"tolerance":1e-8},{"id":401,"outgoing_halfedge":1590,"position":[9.050000000000004,3.0,0.1],"tolerance":1e-8},{"id":402,"outgoing_halfedge":1593,"position":[9.050000000000004,3.0,-0.1],"tolerance":1e-8},{"id":403,"outgoing_halfedge":1594,"position":[9.050000000000004,2.0,-0.1],"tolerance":1e-8},{"id":404,"outgoing_halfedge":1600,"position":[8.450000000000003,0.0,0.1],"tolerance":1e-8},{"id":405,"outgoing_halfedge":1601,"position":[9.050000000000004,0.0,0.1],"tolerance":1e-8},{"id":406,"outgoing_halfedge":1606,"position":[9.050000000000004,0.0,-0.1],"tolerance":1e-8},{"id":407,"outgoing_halfedge":1607,"position":[8.450000000000003,0.0,-0.1],"tolerance":1e-8},{"id":408,"outgoing_halfedge":1621,"position":[9.550000000000004,2.0,0.1],"tolerance":1e-8},{"id":409,"outgoing_halfedge":1622,"position":[9.550000000000004,3.0,0.1],"tolerance":1e-8},{"id":410,"outgoing_halfedge":1625,"position":[9.550000000000004,3.0,-0.1],"tolerance":1e-8},{"id":411,"outgoing_halfedge":1626,"position":[9.550000000000004,2.0,-0.1],"tolerance":1e-8},{"id":412,"outgoing_halfedge":1637,"position":[10.150000000000006,2.0,0.1],"tolerance":1e-8},{"id":413,"outgoing_halfedge":1638,"position":[10.150000000000006,3.0,0.1],"tolerance":1e-8},{"id":414,"outgoing_halfedge":1641,"position":[10.150000000000006,3.0,-0.1],"tolerance":1e-8},{"id":415,"outgoing_halfedge":1642,"position":[10.150000000000006,2.0,-0.1],"tolerance":1e-8},{"id":416,"outgoing_halfedge":1648,"position":[9.550000000000004,0.0,0.1],"tolerance":1e-8},{"id":417,"outgoing_halfedge":1649,"position":[10.150000000000006,0.0,0.1],"tolerance":1e-8},{"id":418,"outgoing_halfedge":1654,"position":[10.150000000000006,0.0,-0.1],"tolerance":1e-8},{"id":419,"outgoing_halfedge":1655,"position":[9.550000000000004,0.0,-0.1],"tolerance":1e-8},{"id":420,"outgoing_halfedge":1669,"position":[10.650000000000006,2.0,0.1],"tolerance":1e-8},{"id":421,"outgoing_halfedge":1670,"position":[10.650000000000006,3.0,0.1],"tolerance":1e-8},{"id":422,"outgoing_halfedge":1673,"position":[10.650000000000006,3.0,-0.1],"tolerance":1e-8},{"id":423,"outgoing_halfedge":1674,"position":[10.650000000000006,2.0,-0.1],"tolerance":1e-8},{"id":424,"outgoing_halfedge":1685,"position":[11.25,2.0,0.1],"tolerance":1e-8},{"id":425,"outgoing_halfedge":1686,"position":[11.25,3.0,0.1],"tolerance":1e-8},{"id":426,"outgoing_halfedge":1689,"position":[11.25,3.0,-0.1],"tolerance":1e-8},{"id":427,"outgoing_halfedge":1690,"position":[11.25,2.0,-0.1],"tolerance":1e-8},{"id":428,"outgoing_halfedge":1696,"position":[10.650000000000006,0.0,0.1],"tolerance":1e-8},{"id":429,"outgoing_halfedge":1697,"position":[11.25,0.0,0.1],"tolerance":1e-8},{"id":430,"outgoing_halfedge":1702,"position":[11.25,0.0,-0.1],"tolerance":1e-8},{"id":431,"outgoing_halfedge":1703,"position":[10.650000000000006,0.0,-0.1],"tolerance":1e-8},{"id":432,"outgoing_halfedge":1717,"position":[11.75,2.0,0.1],"tolerance":1e-8},{"id":433,"outgoing_halfedge":1718,"position":[11.75,3.0,0.1],"tolerance":1e-8},{"id":434,"outgoing_halfedge":1721,"position":[11.75,3.0,-0.1],"tolerance":1e-8},{"id":435,"outgoing_halfedge":1722,"position":[11.75,2.0,-0.1],"tolerance":1e-8},{"id":436,"outgoing_halfedge":1733,"position":[12.350000000000001,2.0,0.1],"tolerance":1e-8},{"id":437,"outgoing_halfedge":1734,"position":[12.350000000000001,3.0,0.1],"tolerance":1e-8},{"id":438,"outgoing_halfedge":1737,"position":[12.350000000000001,3.0,-0.1],"tolerance":1e-8},{"id":439,"outgoing_halfedge":1738,"position":[12.350000000000001,2.0,-0.1],"tolerance":1e-8},{"id":440,"outgoing_halfedge":1744,"position":[11.75,0.0,0.1],"tolerance":1e-8},{"id":441,"outgoing_halfedge":1745,"position":[12.350000000000001,0.0,0.1],"tolerance":1e-8},{"id":442,"outgoing_halfedge":1750,"position":[12.350000000000001,0.0,-0.1],"tolerance":1e-8},{"id":443,"outgoing_halfedge":1751,"position":[11.75,0.0,-0.1],"tolerance":1e-8},{"id":444,"outgoing_halfedge":1765,"position":[12.850000000000001,2.0,0.1],"tolerance":1e-8},{"id":445,"outgoing_halfedge":1766,"position":[12.850000000000001,3.0,0.1],"tolerance":1e-8},{"id":446,"outgoing_halfedge":1769,"position":[12.850000000000001,3.0,-0.1],"tolerance":1e-8},{"id":447,"outgoing_halfedge":1770,"position":[12.850000000000001,2.0,-0.1],"tolerance":1e-8},{"id":448,"outgoing_halfedge":1781,"position":[13.450000000000003,2.0,0.1],"tolerance":1e-8},{"id":449,"outgoing_halfedge":1782,"position":[13.450000000000003,3.0,0.1],"tolerance":1e-8},{"id":450,"outgoing_halfedge":1785,"position":[13.450000000000003,3.0,-0.1],"tolerance":1e-8},{"id":451,"outgoing_halfedge":1786,"position":[13.450000000000003,2.0,-0.1],"tolerance":1e-8},{"id":452,"outgoing_halfedge":1792,"position":[12.850000000000001,0.0,0.1],"tolerance":1e-8},{"id":453,"outgoing_halfedge":1793,"position":[13.450000000000003,0.0,0.1],"tolerance":1e-8},{"id":454,"outgoing_halfedge":1798,"position":[13.450000000000003,0.0,-0.1],"tolerance":1e-8},{"id":455,"outgoing_halfedge":1799,"position":[12.850000000000001,0.0,-0.1],"tolerance":1e-8},{"id":456,"outgoing_halfedge":1813,"position":[13.950000000000003,2.0,0.1],"tolerance":1e-8},{"id":457,"outgoing_halfedge":1814,"position":[13.950000000000003,3.0,0.1],"tolerance":1e-8},{"id":458,"outgoing_halfedge":1817,"position":[13.950000000000003,3.0,-0.1],"tolerance":1e-8},{"id":459,"outgoing_halfedge":1818,"position":[13.950000000000003,2.0,-0.1],"tolerance":1e-8},{"id":460,"outgoing_halfedge":1829,"position":[14.550000000000004,2.0,0.1],"tolerance":1e-8},{"id":461,"outgoing_halfedge":1830,"position":[14.550000000000004,3.0,0.1],"tolerance":1e-8},{"id":462,"outgoing_halfedge":1833,"position":[14.550000000000004,3.0,-0.1],"tolerance":1e-8},{"id":463,"outgoing_halfedge":1834,"position":[14.550000000000004,2.0,-0.1],"tolerance":1e-8},{"id":464,"outgoing_halfedge":1840,"position":[13.950000000000003,0.0,0.1],"tolerance":1e-8},{"id":465,"outgoing_halfedge":1841,"position":[14.550000000000004,0.0,0.1],"tolerance":1e-8},{"id":466,"outgoing_halfedge":1846,"position":[14.550000000000004,0.0,-0.1],"tolerance":1e-8},{"id":467,"outgoing_halfedge":1847,"position":[13.950000000000003,0.0,-0.1],"tolerance":1e-8},{"id":468,"outgoing_halfedge":1861,"position":[15.050000000000004,2.0,0.1],"tolerance":1e-8},{"id":469,"outgoing_halfedge":1862,"position":[15.050000000000004,3.0,0.1],"tolerance":1e-8},{"id":470,"outgoing_halfedge":1865,"position":[15.050000000000004,3.0,-0.1],"tolerance":1e-8},{"id":471,"outgoing_halfedge":1866,"position":[15.050000000000004,2.0,-0.1],"tolerance":1e-8},{"id":472,"outgoing_halfedge":1877,"position":[15.650000000000006,2.0,0.1],"tolerance":1e-8},{"id":473,"outgoing_halfedge":1878,"position":[15.650000000000006,3.0,0.1],"tolerance":1e-8},{"id":474,"outgoing_halfedge":1881,"position":[15.650000000000006,3.0,-0.1],"tolerance":1e-8},{"id":475,"outgoing_halfedge":1882,"position":[15.650000000000006,2.0,-0.1],"tolerance":1e-8},{"id":476,"outgoing_halfedge":1888,"position":[15.050000000000004,0.0,0.1],"tolerance":1e-8},{"id":477,"outgoing_halfedge":1889,"position":[15.650000000000006,0.0,0.1],"tolerance":1e-8},{"id":478,"outgoing_halfedge":1894,"position":[15.650000000000006,0.0,-0.1],"tolerance":1e-8},{"id":479,"outgoing_halfedge":1895,"position":[15.050000000000004,0.0,-0.1],"tolerance":1e-8},{"id":480,"outgoing_halfedge":1909,"position":[16.150000000000006,2.0,0.1],"tolerance":1e-8},{"id":481,"outgoing_halfedge":1910,"position":[16.150000000000006,3.0,0.1],"tolerance":1e-8},{"id":482,"outgoing_halfedge":1913,"position":[16.150000000000006,3.0,-0.1],"tolerance":1e-8},{"id":483,"outgoing_halfedge":1914,"position":[16.150000000000006,2.0,-0.1],"tolerance":1e-8},{"id":484,"outgoing_halfedge":1925,"position":[16.75,2.0,0.1],"tolerance":1e-8},{"id":485,"outgoing_halfedge":1926,"position":[16.75,3.0,0.1],"tolerance":1e-8},{"id":486,"outgoing_halfedge":1929,"position":[16.75,3.0,-0.1],"tolerance":1e-8},{"id":487,"outgoing_halfedge":1930,"position":[16.75,2.0,-0.1],"tolerance":1e-8},{"id":488,"outgoing_halfedge":1936,"position":[16.150000000000006,0.0,0.1],"tolerance":1e-8},{"id":489,"outgoing_halfedge":1937,"position":[16.75,0.0,0.1],"tolerance":1e-8},{"id":490,"outgoing_halfedge":1942,"position":[16.75,0.0,-0.1],"tolerance":1e-8},{"id":491,"outgoing_halfedge":1943,"position":[16.150000000000006,0.0,-0.1],"tolerance":1e-8},{"id":492,"outgoing_halfedge":1957,"position":[17.25,2.0,0.1],"tolerance":1e-8},{"id":493,"outgoing_halfedge":1958,"position":[17.25,3.0,0.1],"tolerance":1e-8},{"id":494,"outgoing_halfedge":1961,"position":[17.25,3.0,-0.1],"tolerance":1e-8},{"id":495,"outgoing_halfedge":1962,"position":[17.25,2.0,-0.1],"tolerance":1e-8},{"id":496,"outgoing_halfedge":1973,"position":[17.85,2.0,0.1],"tolerance":1e-8},{"id":497,"outgoing_halfedge":1974,"position":[17.85,3.0,0.1],"tolerance":1e-8},{"id":498,"outgoing_halfedge":1977,"position":[17.85,3.0,-0.1],"tolerance":1e-8},{"id":499,"outgoing_halfedge":1978,"position":[17.85,2.0,-0.1],"tolerance":1e-8},{"id":500,"outgoing_halfedge":1984,"position":[17.25,0.0,0.1],"tolerance":1e-8},{"id":501,"outgoing_halfedge":1985,"position":[17.85,0.0,0.1],"tolerance":1e-8},{"id":502,"outgoing_halfedge":1990,"position":[17.85,0.0,-0.1],"tolerance":1e-8},{"id":503,"outgoing_halfedge":1991,"position":[17.25,0.0,-0.1],"tolerance":1e-8},{"id":504,"outgoing_halfedge":2005,"position":[18.35,2.0,0.1],"tolerance":1e-8},{"id":505,"outgoing_halfedge":2006,"position":[18.35,3.0,0.1],"tolerance":1e-8},{"id":506,"outgoing_halfedge":2009,"position":[18.35,3.0,-0.1],"tolerance":1e-8},{"id":507,"outgoing_halfedge":2010,"position":[18.35,2.0,-0.1],"tolerance":1e-8},{"id":508,"outgoing_halfedge":2021,"position":[18.950000000000003,2.0,0.1],"tolerance":1e-8},{"id":509,"outgoing_halfedge":2022,"position":[18.950000000000003,3.0,0.1],"tolerance":1e-8},{"id":510,"outgoing_halfedge":2025,"position":[18.950000000000003,3.0,-0.1],"tolerance":1e-8},{"id":511,"outgoing_halfedge":2026,"position":[18.950000000000003,2.0,-0.1],"tolerance":1e-8},{"id":512,"outgoing_halfedge":2032,"position":[18.35,0.0,0.1],"tolerance":1e-8},{"id":513,"outgoing_halfedge":2033,"position":[18.950000000000003,0.0,0.1],"tolerance":1e-8},{"id":514,"outgoing_halfedge":2038,"position":[18.950000000000003,0.0,-0.1],"tolerance":1e-8},{"id":515,"outgoing_halfedge":2039,"position":[18.35,0.0,-0.1],"tolerance":1e-8},{"id":516,"outgoing_halfedge":2053,"position":[19.450000000000003,2.0,0.1],"tolerance":1e-8},{"id":517,"outgoing_halfedge":2054,"position":[19.450000000000003,3.0,0.1],"tolerance":1e-8},{"id":518,"outgoing_halfedge":2057,"position":[19.450000000000003,3.0,-0.1],"tolerance":1e-8},{"id":519,"outgoing_halfedge":2058,"position":[19.450000000000003,2.0,-0.1],"tolerance":1e-8},{"id":520,"outgoing_halfedge":2069,"position":[20.050000000000004,2.0,0.1],"tolerance":1e-8},{"id":521,"outgoing_halfedge":2070,"position":[20.050000000000004,3.0,0.1],"tolerance":1e-8},{"id":522,"outgoing_halfedge":2073,"position":[20.050000000000004,3.0,-0.1],"tolerance":1e-8},{"id":523,"outgoing_halfedge":2074,"position":[20.050000000000004,2.0,-0.1],"tolerance":1e-8},{"id":524,"outgoing_halfedge":2080,"position":[19.450000000000003,0.0,0.1],"tolerance":1e-8},{"id":525,"outgoing_halfedge":2081,"position":[20.050000000000004,0.0,0.1],"tolerance":1e-8},{"id":526,"outgoing_halfedge":2086,"position":[20.050000000000004,0.0,-0.1],"tolerance":1e-8},{"id":527,"outgoing_halfedge":2087,"position":[19.450000000000003,0.0,-0.1],"tolerance":1e-8},{"id":528,"outgoing_halfedge":2101,"position":[20.550000000000004,2.0,0.1],"tolerance":1e-8},{"id":529,"outgoing_halfedge":2102,"position":[20.550000000000004,3.0,0.1],"tolerance":1e-8},{"id":530,"outgoing_halfedge":2105,"position":[20.550000000000004,3.0,-0.1],"tolerance":1e-8},{"id":531,"outgoing_halfedge":2106,"position":[20.550000000000004,2.0,-0.1],"tolerance":1e-8},{"id":532,"outgoing_halfedge":2117,"position":[21.150000000000006,2.0,0.1],"tolerance":1e-8},{"id":533,"outgoing_halfedge":2118,"position":[21.150000000000006,3.0,0.1],"tolerance":1e-8},{"id":534,"outgoing_halfedge":2121,"position":[21.150000000000006,3.0,-0.1],"tolerance":1e-8},{"id":535,"outgoing_halfedge":2122,"position":[21.150000000000006,2.0,-0.1],"tolerance":1e-8},{"id":536,"outgoing_halfedge":2128,"position":[20.550000000000004,0.0,0.1],"tolerance":1e-8},{"id":537,"outgoing_halfedge":2129,"position":[21.150000000000006,0.0,0.1],"tolerance":1e-8},{"id":538,"outgoing_halfedge":2134,"position":[21.150000000000006,0.0,-0.1],"tolerance":1e-8},{"id":539,"outgoing_halfedge":2135,"position":[20.550000000000004,0.0,-0.1],"tolerance":1e-8},{"id":540,"outgoing_halfedge":2149,"position":[21.650000000000006,2.0,0.1],"tolerance":1e-8},{"id":541,"outgoing_halfedge":2150,"position":[21.650000000000006,3.0,0.1],"tolerance":1e-8},{"id":542,"outgoing_halfedge":2153,"position":[21.650000000000006,3.0,-0.1],"tolerance":1e-8},{"id":543,"outgoing_halfedge":2154,"position":[21.650000000000006,2.0,-0.1],"tolerance":1e-8},{"id":544,"outgoing_halfedge":2165,"position":[22.250000000000007,2.0,0.1],"tolerance":1e-8},{"id":545,"outgoing_halfedge":2166,"position":[22.250000000000007,3.0,0.1],"tolerance":1e-8},{"id":546,"outgoing_halfedge":2169,"position":[22.250000000000007,3.0,-0.1],"tolerance":1e-8},{"id":547,"outgoing_halfedge":2170,"position":[22.250000000000007,2.0,-0.1],"tolerance":1e-8},{"id":548,"outgoing_halfedge":2176,"position":[21.650000000000006,0.0,0.1],"tolerance":1e-8},{"id":549,"outgoing_halfedge":2177,"position":[22.250000000000007,0.0,0.1],"tolerance":1e-8},{"id":550,"outgoing_halfedge":2182,"position":[22.250000000000007,0.0,-0.1],"tolerance":1e-8},{"id":551,"outgoing_halfedge":2183,"position":[21.650000000000006,0.0,-0.1],"tolerance":1e-8},{"id":552,"outgoing_halfedge":2197,"position":[22.750000000000007,2.0,0.1],"tolerance":1e-8},{"id":553,"outgoing_halfedge":2198,"position":[22.750000000000007,3.0,0.1],"tolerance":1e-8},{"id":554,"outgoing_halfedge":2201,"position":[22.750000000000007,3.0,-0.1],"tolerance":1e-8},{"id":555,"outgoing_halfedge":2202,"position":[22.750000000000007,2.0,-0.1],"tolerance":1e-8},{"id":556,"outgoing_halfedge":2213,"position":[23.35,2.0,0.1],"tolerance":1e-8},{"id":557,"outgoing_halfedge":2214,"position":[23.35,3.0,0.1],"tolerance":1e-8},{"id":558,"outgoing_halfedge":2217,"position":[23.35,3.0,-0.1],"tolerance":1e-8},{"id":559,"outgoing_halfedge":2218,"position":[23.35,2.0,-0.1],"tolerance":1e-8},{"id":560,"outgoing_halfedge":2224,"position":[22.750000000000007,0.0,0.1],"tolerance":1e-8},{"id":561,"outgoing_halfedge":2225,"position":[23.35,0.0,0.1],"tolerance":1e-8},{"id":562,"outgoing_halfedge":2230,"position":[23.35,0.0,-0.1],"tolerance":1e-8},{"id":563,"outgoing_halfedge":2231,"position":[22.750000000000007,0.0,-0.1],"tolerance":1e-8},{"id":564,"outgoing_halfedge":2245,"position":[23.85,2.0,0.1],"tolerance":1e-8},{"id":565,"outgoing_halfedge":2246,"position":[23.85,3.0,0.1],"tolerance":1e-8},{"id":566,"outgoing_halfedge":2249,"position":[23.85,3.0,-0.1],"tolerance":1e-8},{"id":567,"outgoing_halfedge":2250,"position":[23.85,2.0,-0.1],"tolerance":1e-8},{"id":568,"outgoing_halfedge":2261,"position":[24.450000000000003,2.0,0.1],"tolerance":1e-8},{"id":569,"outgoing_halfedge":2262,"position":[24.450000000000003,3.0,0.1],"tolerance":1e-8},{"id":570,"outgoing_halfedge":2265,"position":[24.450000000000003,3.0,-0.1],"tolerance":1e-8},{"id":571,"outgoing_halfedge":2266,"position":[24.450000000000003,2.0,-0.1],"tolerance":1e-8},{"id":572,"outgoing_halfedge":2272,"position":[23.85,0.0,0.1],"tolerance":1e-8},{"id":573,"outgoing_halfedge":2273,"position":[24.450000000000003,0.0,0.1],"tolerance":1e-8},{"id":574,"outgoing_halfedge":2278,"position":[24.450000000000003,0.0,-0.1],"tolerance":1e-8},{"id":575,"outgoing_halfedge":2279,"position":[23.85,0.0,-0.1],"tolerance":1e-8},{"id":576,"outgoing_halfedge":2293,"position":[24.950000000000003,2.0,0.1],"tolerance":1e-8},{"id":577,"outgoing_halfedge":2294,"position":[24.950000000000003,3.0,0.1],"tolerance":1e-8},{"id":578,"outgoing_halfedge":2297,"position":[24.950000000000003,3.0,-0.1],"tolerance":1e-8},{"id":579,"outgoing_halfedge":2298,"position":[24.950000000000003,2.0,-0.1],"tolerance":1e-8},{"id":580,"outgoing_halfedge":2309,"position":[25.550000000000004,2.0,0.1],"tolerance":1e-8},{"id":581,"outgoing_halfedge":2310,"position":[25.550000000000004,3.0,0.1],"tolerance":1e-8},{"id":582,"outgoing_halfedge":2313,"position":[25.550000000000004,3.0,-0.1],"tolerance":1e-8},{"id":583,"outgoing_halfedge":2314,"position":[25.550000000000004,2.0,-0.1],"tolerance":1e-8},{"id":584,"outgoing_halfedge":2320,"position":[24.950000000000003,0.0,0.1],"tolerance":1e-8},{"id":585,"outgoing_halfedge":2321,"position":[25.550000000000004,0.0,0.1],"tolerance":1e-8},{"id":586,"outgoing_halfedge":2326,"position":[25.550000000000004,0.0,-0.1],"tolerance":1e-8},{"id":587,"outgoing_halfedge":2327,"position":[24.950000000000003,0.0,-0.1],"tolerance":1e-8},{"id":588,"outgoing_halfedge":2341,"position":[26.050000000000004,2.0,0.1],"tolerance":1e-8},{"id":589,"outgoing_halfedge":2342,"position":[26.050000000000004,3.0,0.1],"tolerance":1e-8},{"id":590,"outgoing_halfedge":2345,"position":[26.050000000000004,3.0,-0.1],"tolerance":1e-8},{"id":591,"outgoing_halfedge":2346,"position":[26.050000000000004,2.0,-0.1],"tolerance":1e-8},{"id":592,"outgoing_halfedge":2357,"position":[26.650000000000006,2.0,0.1],"tolerance":1e-8},{"id":593,"outgoing_halfedge":2358,"position":[26.650000000000006,3.0,0.1],"tolerance":1e-8},{"id":594,"outgoing_halfedge":2361,"position":[26.650000000000006,3.0,-0.1],"tolerance":1e-8},{"id":595,"outgoing_halfedge":2362,"position":[26.650000000000006,2.0,-0.1],"tolerance":1e-8},{"id":596,"outgoing_halfedge":2368,"position":[26.050000000000004,0.0,0.1],"tolerance":1e-8},{"id":597,"outgoing_halfedge":2369,"position":[26.650000000000006,0.0,0.1],"tolerance":1e-8},{"id":598,"outgoing_halfedge":2374,"position":[26.650000000000006,0.0,-0.1],"tolerance":1e-8},{"id":599,"outgoing_halfedge":2375,"position":[26.050000000000004,0.0,-0.1],"tolerance":1e-8},{"id":600,"outgoing_halfedge":2389,"position":[27.150000000000006,2.0,0.1],"tolerance":1e-8},{"id":601,"outgoing_halfedge":2390,"position":[27.150000000000006,3.0,0.1],"tolerance":1e-8},{"id":602,"outgoing_halfedge":2393,"position":[27.150000000000006,3.0,-0.1],"tolerance":1e-8},{"id":603,"outgoing_halfedge":2394,"position":[27.150000000000006,2.0,-0.1],"tolerance":1e-8},{"id":604,"outgoing_halfedge":2405,"position":[30.0,2.0,0.1],"tolerance":1e-8},{"id":605,"outgoing_halfedge":2406,"position":[30.0,3.0,0.1],"tolerance":1e-8},{"id":606,"outgoing_halfedge":2409,"position":[30.0,3.0,-0.1],"tolerance":1e-8},{"id":607,"outgoing_halfedge":2410,"position":[30.0,2.0,-0.1],"tolerance":1e-8},{"id":608,"outgoing_halfedge":2420,"position":[27.150000000000006,0.0,0.1],"tolerance":1e-8},{"id":609,"outgoing_halfedge":2421,"position":[30.0,0.0,0.1],"tolerance":1e-8},{"id":610,"outgoing_halfedge":2426,"position":[30.0,0.0,-0.1],"tolerance":1e-8},{"id":611,"outgoing_halfedge":2427,"position":[27.150000000000006,0.0,-0.1],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.no-cutters.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.no-cutters.json new file mode 100644 index 0000000..05ca61c --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.no-cutters.json @@ -0,0 +1 @@ +{"cutters":[],"handlers":[],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[10.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"error":{"InvalidGeometry":"planar batch subtraction requires between one and 100 cutters"}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.oblique-cutters.fallback.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.oblique-cutters.fallback.json new file mode 100644 index 0000000..6aef395 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.oblique-cutters.fallback.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.8660254037844386,0.0,-0.5000000000000001],"kind":"Line","origin":[2.066987298107781,-0.35,0.24999999999999997]},{"direction":[0.8660254037844386,0.0,-0.5000000000000001],"kind":"Line","origin":[3.11698729810778,-0.35,2.068653347947321]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.066987298107781,-0.35,0.24999999999999997]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.933012701892219,-0.35,-0.24999999999999997]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.9830127018922195,-0.35,1.5686533479473213]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.933012701892219,-0.35,-0.24999999999999997]},{"direction":[-0.8660254037844386,0.0,0.5000000000000001],"kind":"Line","origin":[2.933012701892219,0.65,-0.24999999999999997]},{"direction":[-0.8660254037844386,0.0,0.5000000000000001],"kind":"Line","origin":[3.9830127018922195,0.65,1.5686533479473213]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.933012701892219,0.65,-0.24999999999999997]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.066987298107781,0.65,0.24999999999999997]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.11698729810778,0.65,2.068653347947321]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.066987298107781,0.65,0.24999999999999997]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.5000000000000002,-0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5000000000000003,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5000000000000003,0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.5000000000000002,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.4999999999999999,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.4999999999999999,-0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.4999999999999999,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.4999999999999999,0.5]},{"direction":[1.0,-1.4874168143337463e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.9999999999999997,4.063698308792034e-17]},{"direction":[1.0,-1.4874168143337463e-17],"kind":"Line2","origin":[-7.493719268661776e-16,2.0999999999999996]},{"direction":[-2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,1.4874168143337463e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.9999999999999997,-4.063698308792034e-17]},{"direction":[1.0,1.4874168143337463e-17],"kind":"Line2","origin":[9.120915477526774e-17,2.1]},{"direction":[2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0999999999999996]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[3.55,0.15,1.8186533479473213],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[0.0,1.0,0.0],"z":[0.49999999999999994,0.0,0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.5,0.15,0.0],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[-0.0,-1.0,-0.0],"z":[-0.49999999999999994,-0.0,-0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.066987298107781,-0.35,0.24999999999999997],"x":[0.8660254037844386,0.0,-0.5000000000000001],"y":[0.5000000000000001,0.0,0.8660254037844386],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.933012701892219,-0.35,-0.24999999999999997],"x":[0.0,1.0,0.0],"y":[0.49999999999999994,-0.0,0.8660254037844387],"z":[0.8660254037844387,0.0,-0.49999999999999994]},"kind":"Plane"},{"frame":{"origin":[2.933012701892219,0.65,-0.24999999999999997],"x":[-0.8660254037844386,0.0,0.5000000000000001],"y":[0.5000000000000001,0.0,0.8660254037844386],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.066987298107781,0.65,0.24999999999999997],"x":[0.0,-1.0,0.0],"y":[0.49999999999999994,0.0,0.8660254037844387],"z":[-0.8660254037844387,0.0,0.49999999999999994]},"kind":"Plane"}]},"id":"first-oblique","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.500000001,"lo":-0.500000001},{"hi":0.500000001,"lo":-0.500000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.500000001,"lo":-0.500000001},{"hi":0.500000001,"lo":-0.500000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0000000009999999,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.0000000009999999,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.066987298107781,-0.35,0.24999999999999997],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.933012701892219,-0.35,-0.24999999999999997],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.933012701892219,0.65,-0.24999999999999997],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.066987298107781,0.65,0.24999999999999997],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.11698729810778,-0.35,2.068653347947321],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.9830127018922195,-0.35,1.5686533479473213],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.9830127018922195,0.65,1.5686533479473213],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.11698729810778,0.65,2.068653347947321],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.8660254037844389,0.0,-0.4999999999999997],"kind":"Line","origin":[5.56698729810778,-0.35,0.24999999999999997]},{"direction":[0.8660254037844389,0.0,-0.4999999999999997],"kind":"Line","origin":[6.61698729810778,-0.35,2.068653347947321]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[5.56698729810778,-0.35,0.24999999999999997]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.43301270189222,-0.35,-0.24999999999999997]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[7.4830127018922195,-0.35,1.5686533479473213]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[6.43301270189222,-0.35,-0.24999999999999997]},{"direction":[-0.8660254037844389,0.0,0.4999999999999997],"kind":"Line","origin":[6.43301270189222,0.65,-0.24999999999999997]},{"direction":[-0.8660254037844389,0.0,0.4999999999999997],"kind":"Line","origin":[7.4830127018922195,0.65,1.5686533479473213]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[6.43301270189222,0.65,-0.24999999999999997]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.56698729810778,0.65,0.24999999999999997]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.61698729810778,0.65,2.068653347947321]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[5.56698729810778,0.65,0.24999999999999997]}],"intersections":[],"pcurves":[{"direction":[1.0000000000000002,0.0],"kind":"Line2","origin":[-0.5000000000000002,-0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5000000000000003,-0.5]},{"direction":[-1.0000000000000002,0.0],"kind":"Line2","origin":[0.5000000000000003,0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.5000000000000002,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.5000000000000002,-0.5]},{"direction":[-1.0000000000000002,0.0],"kind":"Line2","origin":[0.5000000000000002,-0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.5000000000000002,0.5]},{"direction":[1.0000000000000002,0.0],"kind":"Line2","origin":[-0.5000000000000002,0.5]},{"direction":[1.0000000000000002,-1.832573087291426e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[2.8499284022795783e-16,1.0000000000000002],"kind":"Line2","origin":[1.0000000000000007,-1.8325730872914354e-17]},{"direction":[1.0000000000000002,-1.832573087291426e-17],"kind":"Line2","origin":[7.62096980405069e-16,2.1]},{"direction":[2.8499284022795783e-16,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0000000000000002,1.832573087291426e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-2.8499284022795783e-16,1.0000000000000002],"kind":"Line2","origin":[1.0000000000000007,1.8325730872914354e-17]},{"direction":[1.0000000000000002,1.832573087291426e-17],"kind":"Line2","origin":[-6.510746779425533e-16,2.1]},{"direction":[-2.8499284022795783e-16,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0999999999999996]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[7.05,0.15,1.8186533479473213],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[0.0,1.0,0.0],"z":[0.49999999999999994,0.0,0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[6.0,0.15,0.0],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[-0.0,-1.0,-0.0],"z":[-0.49999999999999994,-0.0,-0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[5.56698729810778,-0.35,0.24999999999999997],"x":[0.8660254037844389,0.0,-0.4999999999999997],"y":[0.4999999999999997,0.0,0.8660254037844389],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.43301270189222,-0.35,-0.24999999999999997],"x":[0.0,1.0,0.0],"y":[0.49999999999999994,-0.0,0.8660254037844387],"z":[0.8660254037844387,0.0,-0.49999999999999994]},"kind":"Plane"},{"frame":{"origin":[6.43301270189222,0.65,-0.24999999999999997],"x":[-0.8660254037844389,0.0,0.4999999999999997],"y":[0.4999999999999997,0.0,0.8660254037844389],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[5.56698729810778,0.65,0.24999999999999997],"x":[0.0,-1.0,0.0],"y":[0.49999999999999994,0.0,0.8660254037844387],"z":[-0.8660254037844387,0.0,0.49999999999999994]},"kind":"Plane"}]},"id":"second-oblique","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0000000000000004,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0000000000000004,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0000000000000004,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0000000000000004,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.500000001,"lo":-0.500000001},{"hi":0.500000001,"lo":-0.500000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.500000001,"lo":-0.500000001},{"hi":0.500000001,"lo":-0.500000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0000000010000005,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.0000000010000005,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[5.56698729810778,-0.35,0.24999999999999997],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.43301270189222,-0.35,-0.24999999999999997],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.43301270189222,0.65,-0.24999999999999997],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[5.56698729810778,0.65,0.24999999999999997],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[6.61698729810778,-0.35,2.068653347947321],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[7.4830127018922195,-0.35,1.5686533479473213],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[7.4830127018922195,0.65,1.5686533479473213],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[6.61698729810778,0.65,2.068653347947321],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_polyhedra","subtract_planar_polyhedra"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"oblique-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[10.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,2.2204460492503135e-17,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[1.4802973661668753e-15,-1.0,0.0],"kind":"Line","origin":[2.4999999999999996,0.30000000000000004,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[1.0,1.899343279013665e-17,0.0],"kind":"Line","origin":[3.0773502691896253,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.0,0.30000000000000004,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0773502691896253,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.577350269189626,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.577350269189626,0.0,0.0]},{"direction":[-0.8660254037844389,0.0,0.49999999999999944],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[0.4999999999999999,0.0,0.8660254037844387],"kind":"Line","origin":[2.066987298107781,0.0,0.24999999999999956]},{"direction":[0.8660254037844387,0.0,-0.49999999999999994],"kind":"Line","origin":[3.1169872981077806,0.0,2.068653347947321]},{"direction":[-0.5000000000000001,0.0,-0.8660254037844386],"kind":"Line","origin":[3.983012701892219,0.0,1.5686533479473213]},{"direction":[-0.8660254037844389,0.0,0.49999999999999967],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.5000000000000004,0.0,0.8660254037844385],"kind":"Line","origin":[5.5669872981077795,0.0,0.25000000000000044]},{"direction":[0.8660254037844387,0.0,-0.4999999999999999],"kind":"Line","origin":[6.61698729810778,0.0,2.068653347947321]},{"direction":[-0.49999999999999994,0.0,-0.8660254037844386],"kind":"Line","origin":[7.4830127018922195,0.0,1.5686533479473208]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.5,0.0,0.8660254037844386],"kind":"Line","origin":[6.577350269189626,0.3,0.0]},{"direction":[-0.8660254037844387,0.0,0.5000000000000001],"kind":"Line","origin":[7.4830127018922195,0.3,1.5686533479473206]},{"direction":[-0.5000000000000001,0.0,-0.8660254037844387],"kind":"Line","origin":[6.61698729810778,0.3,2.068653347947321]},{"direction":[0.8660254037844385,0.0,-0.5000000000000002],"kind":"Line","origin":[5.56698729810778,0.3,0.25000000000000033]},{"direction":[0.5,0.0,0.8660254037844386],"kind":"Line","origin":[3.0773502691896253,0.3,0.0]},{"direction":[-0.8660254037844387,0.0,0.4999999999999998],"kind":"Line","origin":[3.9830127018922186,0.3,1.5686533479473206]},{"direction":[-0.4999999999999999,0.0,-0.8660254037844387],"kind":"Line","origin":[3.11698729810778,0.3,2.06865334794732]},{"direction":[0.8660254037844383,0.0,-0.5000000000000008],"kind":"Line","origin":[2.0669872981077813,0.3,0.25000000000000017]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,-7.401486830834377e-16],"kind":"Line","origin":[3.983012701892219,0.3,1.5686533479473215]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.1169872981077806,0.0,2.068653347947321]},{"direction":[-1.4802973661668755e-15,-1.0,1.8503717077085943e-16],"kind":"Line","origin":[2.0669872981077813,0.3,0.24999999999999975]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[7.4830127018922195,0.3,1.568653347947321]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.61698729810778,0.0,2.0686533479473215]},{"direction":[0.0,-1.0,2.775557561562891e-16],"kind":"Line","origin":[5.56698729810778,0.3,0.24999999999999997]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,-2.2204460492503135e-17],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[1.4802973661668753e-15,1.0],"kind":"Line2","origin":[2.4999999999999996,-0.30000000000000004]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,-0.0]},{"direction":[1.0,-1.899343279013665e-17],"kind":"Line2","origin":[3.0773502691896253,-0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,-0.30000000000000004]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.0773502691896253,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[6.577350269189626,-0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.577350269189626,-0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[-0.8660254037844389,0.49999999999999944],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.4999999999999999,0.8660254037844387],"kind":"Line2","origin":[2.066987298107781,0.24999999999999956]},{"direction":[0.8660254037844387,-0.49999999999999994],"kind":"Line2","origin":[3.1169872981077806,2.068653347947321]},{"direction":[-0.5000000000000001,-0.8660254037844386],"kind":"Line2","origin":[3.983012701892219,1.5686533479473213]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-0.8660254037844389,0.49999999999999967],"kind":"Line2","origin":[6.0,0.0]},{"direction":[0.5000000000000004,0.8660254037844385],"kind":"Line2","origin":[5.5669872981077795,0.25000000000000044]},{"direction":[0.8660254037844387,-0.4999999999999999],"kind":"Line2","origin":[6.61698729810778,2.068653347947321]},{"direction":[-0.49999999999999994,-0.8660254037844386],"kind":"Line2","origin":[7.4830127018922195,1.5686533479473208]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.422649730810374,0.0]},{"direction":[-0.5,0.8660254037844386],"kind":"Line2","origin":[3.422649730810374,0.0]},{"direction":[0.8660254037844387,0.5000000000000001],"kind":"Line2","origin":[2.5169872981077805,1.5686533479473206]},{"direction":[0.5000000000000001,-0.8660254037844387],"kind":"Line2","origin":[3.38301270189222,2.068653347947321]},{"direction":[-0.8660254037844385,-0.5000000000000002],"kind":"Line2","origin":[4.43301270189222,0.25000000000000033]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.922649730810375,0.0]},{"direction":[-0.5,0.8660254037844386],"kind":"Line2","origin":[6.922649730810375,0.0]},{"direction":[0.8660254037844387,0.4999999999999998],"kind":"Line2","origin":[6.016987298107781,1.5686533479473206]},{"direction":[0.4999999999999999,-0.8660254037844387],"kind":"Line2","origin":[6.88301270189222,2.06865334794732]},{"direction":[-0.8660254037844383,-0.5000000000000008],"kind":"Line2","origin":[7.933012701892219,0.25000000000000017]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.3,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.49999999999999983,-0.15]},{"direction":[3.700743415417188e-16,1.0],"kind":"Line2","origin":[0.4999999999999998,-0.15]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.4999999999999998,0.15]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.4999999999999998,0.15]},{"direction":[-1.3744937096411392e-15,-1.0],"kind":"Line2","origin":[-0.4999999999999994,0.15]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.0,-0.15]},{"direction":[1.2819751242557092e-15,-1.0],"kind":"Line2","origin":[-3.845925372767128e-16,0.15000000000000005]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.49999999999999956,0.15]},{"direction":[-1.0,6.409875621278547e-16],"kind":"Line2","origin":[0.6499999999999999,-2.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.6499999999999999,-0.2886751345948127]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.35,-0.2886751345948127]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.35,-2.1]},{"direction":[-0.0,1.0],"kind":"Line2","origin":[0.35000000000000003,-2.0999999999999988]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[0.65,-2.0999999999999996]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.65,3.6055550369691825e-16]},{"direction":[1.0,5.79901792551474e-16],"kind":"Line2","origin":[0.35000000000000003,-2.974833628667488e-17]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5000000000000006,-0.15]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5000000000000003,-0.15]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.5000000000000002,0.15]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.5000000000000003,0.15]},{"direction":[-1.3877787807814452e-16,-1.0],"kind":"Line2","origin":[-0.5000000000000002,0.15]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.0,-0.15]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.15000000000000005]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.5000000000000004,0.15]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[0.6499999999999999,-2.0999999999999996]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.6499999999999999,-0.2886751345948129]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.35,-0.2886751345948129]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.35,-2.0999999999999996]},{"direction":[-0.0,1.0000000000000002],"kind":"Line2","origin":[0.35000000000000003,-2.0999999999999996]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[0.65,-2.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.65,3.545963899355519e-17]},{"direction":[1.0,-2.4037033579794546e-16],"kind":"Line2","origin":[0.35000000000000003,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.55,0.15,1.8186533479473213],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[-0.0,-1.0,-0.0],"z":[-0.49999999999999994,-0.0,-0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.5,0.15,0.0],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[0.0,1.0,0.0],"z":[0.49999999999999994,0.0,0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.933012701892219,-0.35,-0.24999999999999997],"x":[0.0,1.0,0.0],"y":[-0.49999999999999994,0.0,-0.8660254037844387],"z":[-0.8660254037844387,-0.0,0.49999999999999994]},"kind":"Plane"},{"frame":{"origin":[2.066987298107781,0.65,0.24999999999999997],"x":[0.0,-1.0,0.0],"y":[-0.49999999999999994,-0.0,-0.8660254037844387],"z":[0.8660254037844387,-0.0,-0.49999999999999994]},"kind":"Plane"},{"frame":{"origin":[7.05,0.15,1.8186533479473213],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[-0.0,-1.0,-0.0],"z":[-0.49999999999999994,-0.0,-0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[6.0,0.15,0.0],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[0.0,1.0,0.0],"z":[0.49999999999999994,0.0,0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[6.43301270189222,-0.35,-0.24999999999999997],"x":[0.0,1.0,0.0],"y":[-0.49999999999999994,0.0,-0.8660254037844387],"z":[-0.8660254037844387,-0.0,0.49999999999999994]},"kind":"Plane"},{"frame":{"origin":[5.56698729810778,0.65,0.24999999999999997],"x":[0.0,-1.0,0.0],"y":[-0.49999999999999994,-0.0,-0.8660254037844387],"z":[0.8660254037844387,-0.0,-0.49999999999999994]},"kind":"Plane"}]},"id":"batch-planar.oblique-cutters:part:1","quality":{"kind":"Analytic"},"revision":2,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.4999999999999996,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-9,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":7,"id":7,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.9226497308103747,"lo":0.0}},"halfedge":8,"id":8,"tolerance":1e-9,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":9,"id":9,"tolerance":1e-9,"twin_halfedge":74},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.9226497308103747,"lo":0.0}},"halfedge":10,"id":10,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":11,"id":11,"tolerance":1e-9,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":3.422649730810374,"lo":0.0}},"halfedge":12,"id":12,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":13,"id":13,"tolerance":1e-9,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":3.422649730810374,"lo":0.0}},"halfedge":14,"id":14,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":15,"id":15,"tolerance":1e-9,"twin_halfedge":78},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":0.49999999999999967,"lo":0.0}},"halfedge":17,"id":16,"tolerance":1e-9,"twin_halfedge":57},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":18,"id":17,"tolerance":1e-9,"twin_halfedge":66},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":0.9999999999999997,"lo":0.0}},"halfedge":19,"id":18,"tolerance":1e-9,"twin_halfedge":54},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":1.8113248654051872,"lo":0.0}},"halfedge":20,"id":19,"tolerance":1e-9,"twin_halfedge":63},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.5000000000000012,"lo":0.0}},"halfedge":22,"id":20,"tolerance":1e-9,"twin_halfedge":73},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":2.0999999999999996,"lo":0.0}},"halfedge":23,"id":21,"tolerance":1e-9,"twin_halfedge":82},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":1.0000000000000007,"lo":0.0}},"halfedge":24,"id":22,"tolerance":1e-9,"twin_halfedge":70},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":1.8113248654051868,"lo":0.0}},"halfedge":25,"id":23,"tolerance":1e-9,"twin_halfedge":79},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":27,"id":24,"tolerance":1e-9,"twin_halfedge":33},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":29,"id":25,"tolerance":1e-9,"twin_halfedge":49},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":31,"id":26,"tolerance":1e-9,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":1.8113248654051866,"lo":0.0}},"halfedge":35,"id":27,"tolerance":1e-9,"twin_halfedge":77},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":1.0000000000000007,"lo":0.0}},"halfedge":36,"id":28,"tolerance":1e-9,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":2.099999999999999,"lo":0.0}},"halfedge":37,"id":29,"tolerance":1e-9,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":0.5000000000000004,"lo":0.0}},"halfedge":38,"id":30,"tolerance":1e-9,"twin_halfedge":75},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":1.8113248654051866,"lo":0.0}},"halfedge":40,"id":31,"tolerance":1e-9,"twin_halfedge":61},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":0.9999999999999996,"lo":0.0}},"halfedge":41,"id":32,"tolerance":1e-9,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":2.0999999999999983,"lo":0.0}},"halfedge":42,"id":33,"tolerance":1e-9,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":0.49999999999999956,"lo":0.0}},"halfedge":43,"id":34,"tolerance":1e-9,"twin_halfedge":59},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":45,"id":35,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":53,"id":36,"tolerance":1e-9,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":55,"id":37,"tolerance":1e-9,"twin_halfedge":65},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":56,"id":38,"tolerance":1e-9,"twin_halfedge":67},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":69,"id":39,"tolerance":1e-9,"twin_halfedge":76},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":71,"id":40,"tolerance":1e-9,"twin_halfedge":81},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":72,"id":41,"tolerance":1e-9,"twin_halfedge":83}],"faces":[{"id":0,"key":"planar-host-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":0,"key":"planar-host-0-0"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"planar-host-1-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":1,"key":"planar-host-1-1"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.30000000100000007}]}},{"id":2,"key":"planar-host-2-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":2,"key":"planar-host-1-2"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.000000001,"lo":3.077350268189625},{"hi":1e-9,"lo":-0.30000000100000007}]}},{"id":3,"key":"planar-host-2-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":2,"key":"planar-host-1-2"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":10.000000001,"lo":6.577350268189626},{"hi":1e-9,"lo":-0.300000001}]}},{"id":4,"key":"planar-host-3-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":3,"key":"planar-host-2-3"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"planar-host-4-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":4,"key":"planar-host-3-4"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":6,"key":"planar-host-5-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":5,"key":"planar-host-4-5"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":7,"key":"planar-host-6-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":6,"key":"planar-host-5-6"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":8,"key":"planar-host-7-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":7,"key":"planar-cut-0-7"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":0.5000000009999999,"lo":-0.5000000009999997},{"hi":0.150000001,"lo":-0.150000001}]}},{"id":9,"key":"planar-host-8-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":8,"key":"planar-cut-1-8"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":1e-9,"lo":-0.5000000009999997},{"hi":0.15000000100000005,"lo":-0.150000001}]}},{"id":10,"key":"planar-host-9-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":9,"key":"planar-cut-3-9"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":0.6500000009999999,"lo":0.34999999899999995},{"hi":-0.28867513359481256,"lo":-2.100000001}]}},{"id":11,"key":"planar-host-10-11","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.oblique-cutters:part:0","entity":"batch-planar.oblique-cutters:part:0","face":10,"key":"planar-cut-5-10"}]},"sense":"Forward","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":0.650000001,"lo":0.349999999},{"hi":1.0000002644073695e-9,"lo":-2.1000000009999997}]}},{"id":12,"key":"planar-cut-0-12","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"second-oblique","entity":"second-oblique","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":0.5000000010000003,"lo":-0.5000000010000003},{"hi":0.15000000100000002,"lo":-0.15000000100000005}]}},{"id":13,"key":"planar-cut-1-13","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"second-oblique","entity":"second-oblique","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":1e-9,"lo":-0.5000000010000003},{"hi":0.150000001,"lo":-0.15000000100000002}]}},{"id":14,"key":"planar-cut-3-14","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"second-oblique","entity":"second-oblique","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":0.6500000009999999,"lo":0.34999999899999995},{"hi":-0.28867513359481295,"lo":-2.100000001}]}},{"id":15,"key":"planar-cut-5-15","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"second-oblique","entity":"second-oblique","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":0.650000001,"lo":0.349999999},{"hi":1e-9,"lo":-2.1000000009999997}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":28,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":32,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":46,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":50,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":48,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":6,"twin":44,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":7,"twin":58,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":16,"wire_ref":null},{"edge":8,"face":2,"from":8,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":9,"twin":39,"wire_ref":null},{"edge":9,"face":2,"from":9,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":10,"twin":74,"wire_ref":null},{"edge":10,"face":2,"from":10,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":11,"twin":21,"wire_ref":null},{"edge":11,"face":2,"from":11,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":8,"twin":62,"wire_ref":null},{"edge":12,"face":3,"from":12,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":13,"twin":34,"wire_ref":null},{"edge":13,"face":3,"from":13,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":14,"twin":30,"wire_ref":null},{"edge":14,"face":3,"from":14,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":15,"twin":26,"wire_ref":null},{"edge":15,"face":3,"from":15,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":12,"twin":78,"wire_ref":null},{"edge":7,"face":4,"from":4,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":4,"next":17,"prev":29,"to":7,"twin":7,"wire_ref":null},{"edge":16,"face":4,"from":7,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":16,"twin":57,"wire_ref":null},{"edge":17,"face":4,"from":16,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":17,"twin":66,"wire_ref":null},{"edge":18,"face":4,"from":17,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":20,"prev":18,"to":18,"twin":54,"wire_ref":null},{"edge":19,"face":4,"from":18,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":4,"next":21,"prev":19,"to":11,"twin":63,"wire_ref":null},{"edge":10,"face":4,"from":11,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Reverse"},"id":21,"loop_ref":4,"next":22,"prev":20,"to":10,"twin":10,"wire_ref":null},{"edge":20,"face":4,"from":10,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Forward"},"id":22,"loop_ref":4,"next":23,"prev":21,"to":19,"twin":73,"wire_ref":null},{"edge":21,"face":4,"from":19,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":4,"next":24,"prev":22,"to":20,"twin":82,"wire_ref":null},{"edge":22,"face":4,"from":20,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":4,"next":25,"prev":23,"to":21,"twin":70,"wire_ref":null},{"edge":23,"face":4,"from":21,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":4,"next":26,"prev":24,"to":15,"twin":79,"wire_ref":null},{"edge":14,"face":4,"from":15,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":4,"next":27,"prev":25,"to":14,"twin":14,"wire_ref":null},{"edge":24,"face":4,"from":14,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":4,"next":28,"prev":26,"to":1,"twin":33,"wire_ref":null},{"edge":0,"face":4,"from":1,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Reverse"},"id":28,"loop_ref":4,"next":29,"prev":27,"to":0,"twin":0,"wire_ref":null},{"edge":25,"face":4,"from":0,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":4,"next":16,"prev":28,"to":4,"twin":49,"wire_ref":null},{"edge":13,"face":5,"from":14,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":5,"next":31,"prev":33,"to":13,"twin":13,"wire_ref":null},{"edge":26,"face":5,"from":13,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Forward"},"id":31,"loop_ref":5,"next":32,"prev":30,"to":2,"twin":47,"wire_ref":null},{"edge":1,"face":5,"from":2,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Reverse"},"id":32,"loop_ref":5,"next":33,"prev":31,"to":1,"twin":1,"wire_ref":null},{"edge":24,"face":5,"from":1,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":5,"next":30,"prev":32,"to":14,"twin":27,"wire_ref":null},{"edge":12,"face":6,"from":13,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":6,"next":35,"prev":47,"to":12,"twin":12,"wire_ref":null},{"edge":27,"face":6,"from":12,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Forward"},"id":35,"loop_ref":6,"next":36,"prev":34,"to":22,"twin":77,"wire_ref":null},{"edge":28,"face":6,"from":22,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":6,"next":37,"prev":35,"to":23,"twin":68,"wire_ref":null},{"edge":29,"face":6,"from":23,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":6,"next":38,"prev":36,"to":24,"twin":80,"wire_ref":null},{"edge":30,"face":6,"from":24,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Forward"},"id":38,"loop_ref":6,"next":39,"prev":37,"to":9,"twin":75,"wire_ref":null},{"edge":8,"face":6,"from":9,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":6,"next":40,"prev":38,"to":8,"twin":8,"wire_ref":null},{"edge":31,"face":6,"from":8,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":6,"next":41,"prev":39,"to":25,"twin":61,"wire_ref":null},{"edge":32,"face":6,"from":25,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":6,"next":42,"prev":40,"to":26,"twin":52,"wire_ref":null},{"edge":33,"face":6,"from":26,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Forward"},"id":42,"loop_ref":6,"next":43,"prev":41,"to":27,"twin":64,"wire_ref":null},{"edge":34,"face":6,"from":27,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Forward"},"id":43,"loop_ref":6,"next":44,"prev":42,"to":6,"twin":59,"wire_ref":null},{"edge":5,"face":6,"from":6,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Reverse"},"id":44,"loop_ref":6,"next":45,"prev":43,"to":5,"twin":5,"wire_ref":null},{"edge":35,"face":6,"from":5,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":6,"next":46,"prev":44,"to":3,"twin":51,"wire_ref":null},{"edge":2,"face":6,"from":3,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":6,"next":47,"prev":45,"to":2,"twin":2,"wire_ref":null},{"edge":26,"face":6,"from":2,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":6,"next":34,"prev":46,"to":13,"twin":31,"wire_ref":null},{"edge":4,"face":7,"from":5,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Reverse"},"id":48,"loop_ref":7,"next":49,"prev":51,"to":4,"twin":4,"wire_ref":null},{"edge":25,"face":7,"from":4,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Reverse"},"id":49,"loop_ref":7,"next":50,"prev":48,"to":0,"twin":29,"wire_ref":null},{"edge":3,"face":7,"from":0,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":7,"next":51,"prev":49,"to":3,"twin":3,"wire_ref":null},{"edge":35,"face":7,"from":3,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":7,"next":48,"prev":50,"to":5,"twin":45,"wire_ref":null},{"edge":32,"face":8,"from":26,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Reverse"},"id":52,"loop_ref":8,"next":53,"prev":55,"to":25,"twin":41,"wire_ref":null},{"edge":36,"face":8,"from":25,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":8,"next":54,"prev":52,"to":18,"twin":60,"wire_ref":null},{"edge":18,"face":8,"from":18,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":8,"next":55,"prev":53,"to":17,"twin":19,"wire_ref":null},{"edge":37,"face":8,"from":17,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Forward"},"id":55,"loop_ref":8,"next":52,"prev":54,"to":26,"twin":65,"wire_ref":null},{"edge":38,"face":9,"from":27,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":9,"next":57,"prev":59,"to":16,"twin":67,"wire_ref":null},{"edge":16,"face":9,"from":16,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Reverse"},"id":57,"loop_ref":9,"next":58,"prev":56,"to":7,"twin":17,"wire_ref":null},{"edge":6,"face":9,"from":7,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":9,"next":59,"prev":57,"to":6,"twin":6,"wire_ref":null},{"edge":34,"face":9,"from":6,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":9,"next":56,"prev":58,"to":27,"twin":43,"wire_ref":null},{"edge":36,"face":10,"from":18,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Reverse"},"id":60,"loop_ref":10,"next":61,"prev":63,"to":25,"twin":53,"wire_ref":null},{"edge":31,"face":10,"from":25,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Reverse"},"id":61,"loop_ref":10,"next":62,"prev":60,"to":8,"twin":40,"wire_ref":null},{"edge":11,"face":10,"from":8,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":10,"next":63,"prev":61,"to":11,"twin":11,"wire_ref":null},{"edge":19,"face":10,"from":11,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Reverse"},"id":63,"loop_ref":10,"next":60,"prev":62,"to":18,"twin":20,"wire_ref":null},{"edge":33,"face":11,"from":27,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Reverse"},"id":64,"loop_ref":11,"next":65,"prev":67,"to":26,"twin":42,"wire_ref":null},{"edge":37,"face":11,"from":26,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Reverse"},"id":65,"loop_ref":11,"next":66,"prev":64,"to":17,"twin":55,"wire_ref":null},{"edge":17,"face":11,"from":17,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Reverse"},"id":66,"loop_ref":11,"next":67,"prev":65,"to":16,"twin":18,"wire_ref":null},{"edge":38,"face":11,"from":16,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Reverse"},"id":67,"loop_ref":11,"next":64,"prev":66,"to":27,"twin":56,"wire_ref":null},{"edge":28,"face":12,"from":23,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Reverse"},"id":68,"loop_ref":12,"next":69,"prev":71,"to":22,"twin":36,"wire_ref":null},{"edge":39,"face":12,"from":22,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Forward"},"id":69,"loop_ref":12,"next":70,"prev":68,"to":21,"twin":76,"wire_ref":null},{"edge":22,"face":12,"from":21,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":12,"next":71,"prev":69,"to":20,"twin":24,"wire_ref":null},{"edge":40,"face":12,"from":20,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Forward"},"id":71,"loop_ref":12,"next":68,"prev":70,"to":23,"twin":81,"wire_ref":null},{"edge":41,"face":13,"from":24,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Forward"},"id":72,"loop_ref":13,"next":73,"prev":75,"to":19,"twin":83,"wire_ref":null},{"edge":20,"face":13,"from":19,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Reverse"},"id":73,"loop_ref":13,"next":74,"prev":72,"to":10,"twin":22,"wire_ref":null},{"edge":9,"face":13,"from":10,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":13,"next":75,"prev":73,"to":9,"twin":9,"wire_ref":null},{"edge":30,"face":13,"from":9,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Reverse"},"id":75,"loop_ref":13,"next":72,"prev":74,"to":24,"twin":38,"wire_ref":null},{"edge":39,"face":14,"from":21,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Reverse"},"id":76,"loop_ref":14,"next":77,"prev":79,"to":22,"twin":69,"wire_ref":null},{"edge":27,"face":14,"from":22,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Reverse"},"id":77,"loop_ref":14,"next":78,"prev":76,"to":12,"twin":35,"wire_ref":null},{"edge":15,"face":14,"from":12,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":14,"next":79,"prev":77,"to":15,"twin":15,"wire_ref":null},{"edge":23,"face":14,"from":15,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Reverse"},"id":79,"loop_ref":14,"next":76,"prev":78,"to":21,"twin":25,"wire_ref":null},{"edge":29,"face":15,"from":24,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Reverse"},"id":80,"loop_ref":15,"next":81,"prev":83,"to":23,"twin":37,"wire_ref":null},{"edge":40,"face":15,"from":23,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Reverse"},"id":81,"loop_ref":15,"next":82,"prev":80,"to":20,"twin":71,"wire_ref":null},{"edge":21,"face":15,"from":20,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":15,"next":83,"prev":81,"to":19,"twin":23,"wire_ref":null},{"edge":41,"face":15,"from":19,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":15,"next":80,"prev":82,"to":24,"twin":72,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":30},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":34},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":48},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":52},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":56},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":60},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":64},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":68},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":72},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":76},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":80}],"shells":[{"faces":[0,4,5,6,7,1,9,11,8,10,2,13,15,12,14,3],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":1,"position":[10.0,0.0,3.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":2,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":6,"position":[2.4999999999999996,0.30000000000000004,0.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":7,"position":[2.5,0.0,0.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":8,"position":[3.0773502691896253,0.3,0.0],"tolerance":1e-9},{"id":9,"outgoing_halfedge":9,"position":[6.0,0.30000000000000004,0.0],"tolerance":1e-9},{"id":10,"outgoing_halfedge":10,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":11,"outgoing_halfedge":11,"position":[3.0773502691896253,0.0,0.0],"tolerance":1e-9},{"id":12,"outgoing_halfedge":12,"position":[6.577350269189626,0.3,0.0],"tolerance":1e-9},{"id":13,"outgoing_halfedge":13,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":14,"outgoing_halfedge":14,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":15,"outgoing_halfedge":15,"position":[6.577350269189626,0.0,0.0],"tolerance":1e-9},{"id":16,"outgoing_halfedge":18,"position":[2.066987298107781,0.0,0.24999999999999956],"tolerance":1e-9},{"id":17,"outgoing_halfedge":19,"position":[3.1169872981077806,0.0,2.068653347947321],"tolerance":1e-9},{"id":18,"outgoing_halfedge":20,"position":[3.983012701892219,0.0,1.5686533479473213],"tolerance":1e-9},{"id":19,"outgoing_halfedge":23,"position":[5.5669872981077795,0.0,0.25000000000000044],"tolerance":1e-9},{"id":20,"outgoing_halfedge":24,"position":[6.61698729810778,0.0,2.068653347947321],"tolerance":1e-9},{"id":21,"outgoing_halfedge":25,"position":[7.4830127018922195,0.0,1.5686533479473208],"tolerance":1e-9},{"id":22,"outgoing_halfedge":36,"position":[7.4830127018922195,0.3,1.5686533479473206],"tolerance":1e-9},{"id":23,"outgoing_halfedge":37,"position":[6.61698729810778,0.3,2.068653347947321],"tolerance":1e-9},{"id":24,"outgoing_halfedge":38,"position":[5.56698729810778,0.3,0.25000000000000033],"tolerance":1e-9},{"id":25,"outgoing_halfedge":41,"position":[3.9830127018922186,0.3,1.5686533479473206],"tolerance":1e-9},{"id":26,"outgoing_halfedge":42,"position":[3.11698729810778,0.3,2.06865334794732],"tolerance":1e-9},{"id":27,"outgoing_halfedge":43,"position":[2.0669872981077813,0.3,0.25000000000000017],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.oblique-cutters.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.oblique-cutters.json new file mode 100644 index 0000000..3d8a27e --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.oblique-cutters.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.8660254037844386,0.0,-0.5000000000000001],"kind":"Line","origin":[2.066987298107781,-0.35,0.24999999999999997]},{"direction":[0.8660254037844386,0.0,-0.5000000000000001],"kind":"Line","origin":[3.11698729810778,-0.35,2.068653347947321]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.066987298107781,-0.35,0.24999999999999997]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.933012701892219,-0.35,-0.24999999999999997]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.9830127018922195,-0.35,1.5686533479473213]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.933012701892219,-0.35,-0.24999999999999997]},{"direction":[-0.8660254037844386,0.0,0.5000000000000001],"kind":"Line","origin":[2.933012701892219,0.65,-0.24999999999999997]},{"direction":[-0.8660254037844386,0.0,0.5000000000000001],"kind":"Line","origin":[3.9830127018922195,0.65,1.5686533479473213]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.933012701892219,0.65,-0.24999999999999997]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.066987298107781,0.65,0.24999999999999997]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.11698729810778,0.65,2.068653347947321]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.066987298107781,0.65,0.24999999999999997]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.5000000000000002,-0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5000000000000003,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5000000000000003,0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.5000000000000002,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.4999999999999999,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.4999999999999999,-0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.4999999999999999,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.4999999999999999,0.5]},{"direction":[1.0,-1.4874168143337463e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.9999999999999997,4.063698308792034e-17]},{"direction":[1.0,-1.4874168143337463e-17],"kind":"Line2","origin":[-7.493719268661776e-16,2.0999999999999996]},{"direction":[-2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,1.4874168143337463e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.9999999999999997,-4.063698308792034e-17]},{"direction":[1.0,1.4874168143337463e-17],"kind":"Line2","origin":[9.120915477526774e-17,2.1]},{"direction":[2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0999999999999996]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[3.55,0.15,1.8186533479473213],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[0.0,1.0,0.0],"z":[0.49999999999999994,0.0,0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.5,0.15,0.0],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[-0.0,-1.0,-0.0],"z":[-0.49999999999999994,-0.0,-0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.066987298107781,-0.35,0.24999999999999997],"x":[0.8660254037844386,0.0,-0.5000000000000001],"y":[0.5000000000000001,0.0,0.8660254037844386],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.933012701892219,-0.35,-0.24999999999999997],"x":[0.0,1.0,0.0],"y":[0.49999999999999994,-0.0,0.8660254037844387],"z":[0.8660254037844387,0.0,-0.49999999999999994]},"kind":"Plane"},{"frame":{"origin":[2.933012701892219,0.65,-0.24999999999999997],"x":[-0.8660254037844386,0.0,0.5000000000000001],"y":[0.5000000000000001,0.0,0.8660254037844386],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.066987298107781,0.65,0.24999999999999997],"x":[0.0,-1.0,0.0],"y":[0.49999999999999994,0.0,0.8660254037844387],"z":[-0.8660254037844387,0.0,0.49999999999999994]},"kind":"Plane"}]},"id":"first-oblique","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.500000001,"lo":-0.500000001},{"hi":0.500000001,"lo":-0.500000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.500000001,"lo":-0.500000001},{"hi":0.500000001,"lo":-0.500000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0000000009999999,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.0000000009999999,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.066987298107781,-0.35,0.24999999999999997],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.933012701892219,-0.35,-0.24999999999999997],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.933012701892219,0.65,-0.24999999999999997],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.066987298107781,0.65,0.24999999999999997],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.11698729810778,-0.35,2.068653347947321],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.9830127018922195,-0.35,1.5686533479473213],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.9830127018922195,0.65,1.5686533479473213],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.11698729810778,0.65,2.068653347947321],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.8660254037844389,0.0,-0.4999999999999997],"kind":"Line","origin":[5.56698729810778,-0.35,0.24999999999999997]},{"direction":[0.8660254037844389,0.0,-0.4999999999999997],"kind":"Line","origin":[6.61698729810778,-0.35,2.068653347947321]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[5.56698729810778,-0.35,0.24999999999999997]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.43301270189222,-0.35,-0.24999999999999997]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[7.4830127018922195,-0.35,1.5686533479473213]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[6.43301270189222,-0.35,-0.24999999999999997]},{"direction":[-0.8660254037844389,0.0,0.4999999999999997],"kind":"Line","origin":[6.43301270189222,0.65,-0.24999999999999997]},{"direction":[-0.8660254037844389,0.0,0.4999999999999997],"kind":"Line","origin":[7.4830127018922195,0.65,1.5686533479473213]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[6.43301270189222,0.65,-0.24999999999999997]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.56698729810778,0.65,0.24999999999999997]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.61698729810778,0.65,2.068653347947321]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[5.56698729810778,0.65,0.24999999999999997]}],"intersections":[],"pcurves":[{"direction":[1.0000000000000002,0.0],"kind":"Line2","origin":[-0.5000000000000002,-0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5000000000000003,-0.5]},{"direction":[-1.0000000000000002,0.0],"kind":"Line2","origin":[0.5000000000000003,0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.5000000000000002,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.5000000000000002,-0.5]},{"direction":[-1.0000000000000002,0.0],"kind":"Line2","origin":[0.5000000000000002,-0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.5000000000000002,0.5]},{"direction":[1.0000000000000002,0.0],"kind":"Line2","origin":[-0.5000000000000002,0.5]},{"direction":[1.0000000000000002,-1.832573087291426e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[2.8499284022795783e-16,1.0000000000000002],"kind":"Line2","origin":[1.0000000000000007,-1.8325730872914354e-17]},{"direction":[1.0000000000000002,-1.832573087291426e-17],"kind":"Line2","origin":[7.62096980405069e-16,2.1]},{"direction":[2.8499284022795783e-16,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0000000000000002,1.832573087291426e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-2.8499284022795783e-16,1.0000000000000002],"kind":"Line2","origin":[1.0000000000000007,1.8325730872914354e-17]},{"direction":[1.0000000000000002,1.832573087291426e-17],"kind":"Line2","origin":[-6.510746779425533e-16,2.1]},{"direction":[-2.8499284022795783e-16,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0999999999999996]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[7.05,0.15,1.8186533479473213],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[0.0,1.0,0.0],"z":[0.49999999999999994,0.0,0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[6.0,0.15,0.0],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[-0.0,-1.0,-0.0],"z":[-0.49999999999999994,-0.0,-0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[5.56698729810778,-0.35,0.24999999999999997],"x":[0.8660254037844389,0.0,-0.4999999999999997],"y":[0.4999999999999997,0.0,0.8660254037844389],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.43301270189222,-0.35,-0.24999999999999997],"x":[0.0,1.0,0.0],"y":[0.49999999999999994,-0.0,0.8660254037844387],"z":[0.8660254037844387,0.0,-0.49999999999999994]},"kind":"Plane"},{"frame":{"origin":[6.43301270189222,0.65,-0.24999999999999997],"x":[-0.8660254037844389,0.0,0.4999999999999997],"y":[0.4999999999999997,0.0,0.8660254037844389],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[5.56698729810778,0.65,0.24999999999999997],"x":[0.0,-1.0,0.0],"y":[0.49999999999999994,0.0,0.8660254037844387],"z":[-0.8660254037844387,0.0,0.49999999999999994]},"kind":"Plane"}]},"id":"second-oblique","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0000000000000004,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0000000000000004,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0000000000000004,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0000000000000004,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.500000001,"lo":-0.500000001},{"hi":0.500000001,"lo":-0.500000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.500000001,"lo":-0.500000001},{"hi":0.500000001,"lo":-0.500000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0000000010000005,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.0000000010000005,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-oblique","entity":"second-oblique","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[5.56698729810778,-0.35,0.24999999999999997],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.43301270189222,-0.35,-0.24999999999999997],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.43301270189222,0.65,-0.24999999999999997],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[5.56698729810778,0.65,0.24999999999999997],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[6.61698729810778,-0.35,2.068653347947321],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[7.4830127018922195,-0.35,1.5686533479473213],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[7.4830127018922195,0.65,1.5686533479473213],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[6.61698729810778,0.65,2.068653347947321],"tolerance":1e-9}],"wires":[]}}],"handlers":[],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"oblique-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[10.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"error":{"CoverageGap":{"families":["canonical aligned cuboid","canonical aligned cuboid"]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.overlapping-cutters.fallback.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.overlapping-cutters.fallback.json new file mode 100644 index 0000000..9fd59d7 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.overlapping-cutters.fallback.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"first","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.000000001,"lo":1.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.000000001,"lo":1.999999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.0,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.0,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.0,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.0,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.5,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.5,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.5,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"second","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.500000001,"lo":2.499999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.500000001,"lo":2.499999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.5,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.5,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.5,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.5,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.5,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.5,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.5,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.5,0.3,2.1],"tolerance":1e-9}],"wires":[]}}],"handlers":["boolean_rectilinear","boolean_rectilinear"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[10.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.5,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.5,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,3.0]}],"intersections":[],"pcurves":[{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.5,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.5,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.5,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.5,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.5,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.5,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.5,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.5,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.5,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"}]},"id":"batch-planar.overlapping-cutters:part:1","quality":{"kind":"Analytic"},"revision":2,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-9,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-9,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":7,"id":7,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-9,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":11,"id":9,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-9,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":20,"id":12,"tolerance":1e-9,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":21,"id":13,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":23,"id":14,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":24,"id":15,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":25,"id":16,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":29,"id":17,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":30,"id":18,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":33,"id":19,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":36,"id":20,"tolerance":1e-9,"twin_halfedge":54},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":37,"id":21,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":39,"id":22,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":41,"id":23,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":42,"id":24,"tolerance":1e-9,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":43,"id":25,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":45,"id":26,"tolerance":1e-9,"twin_halfedge":63},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":49,"id":27,"tolerance":1e-9,"twin_halfedge":67},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":52,"id":28,"tolerance":1e-9,"twin_halfedge":70},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":53,"id":29,"tolerance":1e-9,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":55,"id":30,"tolerance":1e-9,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":57,"id":31,"tolerance":1e-9,"twin_halfedge":66},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":58,"id":32,"tolerance":1e-9,"twin_halfedge":72},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":59,"id":33,"tolerance":1e-9,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":61,"id":34,"tolerance":1e-9,"twin_halfedge":79},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":65,"id":35,"tolerance":1e-9,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":68,"id":36,"tolerance":1e-9,"twin_halfedge":110},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":69,"id":37,"tolerance":1e-9,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":71,"id":38,"tolerance":1e-9,"twin_halfedge":78},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":73,"id":39,"tolerance":1e-9,"twin_halfedge":82},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":74,"id":40,"tolerance":1e-9,"twin_halfedge":112},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":75,"id":41,"tolerance":1e-9,"twin_halfedge":76},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":77,"id":42,"tolerance":1e-9,"twin_halfedge":84},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":81,"id":43,"tolerance":1e-9,"twin_halfedge":119},{"chart_seam":false,"geometry":{"curve":44,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":85,"id":44,"tolerance":1e-9,"twin_halfedge":96},{"chart_seam":false,"geometry":{"curve":45,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":86,"id":45,"tolerance":1e-9,"twin_halfedge":103},{"chart_seam":false,"geometry":{"curve":46,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":87,"id":46,"tolerance":1e-9,"twin_halfedge":94},{"chart_seam":false,"geometry":{"curve":47,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":88,"id":47,"tolerance":1e-9,"twin_halfedge":101},{"chart_seam":false,"geometry":{"curve":48,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":89,"id":48,"tolerance":1e-9,"twin_halfedge":98},{"chart_seam":false,"geometry":{"curve":49,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":90,"id":49,"tolerance":1e-9,"twin_halfedge":104},{"chart_seam":false,"geometry":{"curve":50,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":91,"id":50,"tolerance":1e-9,"twin_halfedge":92},{"chart_seam":false,"geometry":{"curve":51,"kind":"Curve","range":{"hi":6.5,"lo":0.0}},"halfedge":93,"id":51,"tolerance":1e-9,"twin_halfedge":111},{"chart_seam":false,"geometry":{"curve":52,"kind":"Curve","range":{"hi":6.5,"lo":0.0}},"halfedge":95,"id":52,"tolerance":1e-9,"twin_halfedge":102},{"chart_seam":false,"geometry":{"curve":53,"kind":"Curve","range":{"hi":6.5,"lo":0.0}},"halfedge":97,"id":53,"tolerance":1e-9,"twin_halfedge":115},{"chart_seam":false,"geometry":{"curve":54,"kind":"Curve","range":{"hi":6.5,"lo":0.0}},"halfedge":99,"id":54,"tolerance":1e-9,"twin_halfedge":100},{"chart_seam":false,"geometry":{"curve":55,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":105,"id":55,"tolerance":1e-9,"twin_halfedge":114},{"chart_seam":false,"geometry":{"curve":56,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":106,"id":56,"tolerance":1e-9,"twin_halfedge":117},{"chart_seam":false,"geometry":{"curve":57,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":107,"id":57,"tolerance":1e-9,"twin_halfedge":108},{"chart_seam":false,"geometry":{"curve":58,"kind":"Curve","range":{"hi":6.5,"lo":0.0}},"halfedge":109,"id":58,"tolerance":1e-9,"twin_halfedge":116},{"chart_seam":false,"geometry":{"curve":59,"kind":"Curve","range":{"hi":6.5,"lo":0.0}},"halfedge":113,"id":59,"tolerance":1e-9,"twin_halfedge":118}],"faces":[{"id":0,"key":"side-0-3-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":0,"key":"side-0-3-0"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":1,"key":"side-0-3-1-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":1,"key":"side-0-3-1"}]},"sense":"Reverse","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":2,"key":"side-0-0-2-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":2,"key":"side-0-0-2"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-2-3-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":3,"key":"side-0-2-3"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":10.000000001,"lo":7.999999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"bottom-4-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":4,"key":"bottom-4"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":5,"key":"side-0-3-5-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":5,"key":"side-0-3-5"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":6,"key":"side-0-0-6-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":6,"key":"side-0-0-6"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":2.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":7,"key":"side-0-2-7-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":7,"key":"side-0-2-7"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":10.000000001,"lo":7.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":8,"key":"top-8-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":8,"key":"top-8"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":2.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":9,"key":"side-0-0-9-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":9,"key":"side-0-0-9"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":2.500000001,"lo":1.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":10,"key":"side-0-2-10-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":10,"key":"side-0-2-10"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":8.000000001,"lo":7.499999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":11,"key":"top-11-11","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":11,"key":"top-11"}]},"sense":"Reverse","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":2.500000001,"lo":1.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":12,"key":"top-12-12","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":12,"key":"top-12"}]},"sense":"Forward","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":2.500000001,"lo":1.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":13,"key":"side-0-0-9-13","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":9,"key":"side-0-0-9"}]},"sense":"Forward","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":3.000000001,"lo":2.499999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":14,"key":"side-0-2-10-14","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":10,"key":"side-0-2-10"}]},"sense":"Forward","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":7.500000001,"lo":6.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":15,"key":"top-11-15","provenance":{"reversed":false,"role":"Coincident","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":11,"key":"top-11"},{"body":"second","entity":"second","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":3.000000001,"lo":2.499999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":16,"key":"top-12-16","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":12,"key":"top-12"}]},"sense":"Forward","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[],"outer":16,"uv_bounds":[{"hi":3.000000001,"lo":2.499999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":17,"key":"side-0-0-19-17","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":19,"key":"side-0-0-19"}]},"sense":"Forward","shell_ref":0,"surface":17,"trim":{"chart":0,"holes":[],"outer":17,"uv_bounds":[{"hi":3.500000001,"lo":2.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":18,"key":"side-0-2-20-18","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":20,"key":"side-0-2-20"}]},"sense":"Forward","shell_ref":0,"surface":18,"trim":{"chart":0,"holes":[],"outer":18,"uv_bounds":[{"hi":7.000000001,"lo":6.499999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":19,"key":"top-19","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"second","entity":"second","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":19,"trim":{"chart":0,"holes":[],"outer":19,"uv_bounds":[{"hi":3.500000001,"lo":2.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":20,"key":"top-21-20","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":21,"key":"top-21"}]},"sense":"Forward","shell_ref":0,"surface":20,"trim":{"chart":0,"holes":[],"outer":20,"uv_bounds":[{"hi":3.500000001,"lo":2.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":21,"key":"side-0-1-21","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"second","entity":"second","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":21,"trim":{"chart":0,"holes":[],"outer":21,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":22,"key":"side-0-1-14-22","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":14,"key":"side-0-1-14"}]},"sense":"Forward","shell_ref":0,"surface":22,"trim":{"chart":0,"holes":[],"outer":22,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":23,"key":"side-0-0-15-23","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":15,"key":"side-0-0-15"}]},"sense":"Forward","shell_ref":0,"surface":23,"trim":{"chart":0,"holes":[],"outer":23,"uv_bounds":[{"hi":10.000000001,"lo":3.499999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":24,"key":"side-0-2-16-24","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":16,"key":"side-0-2-16"}]},"sense":"Forward","shell_ref":0,"surface":24,"trim":{"chart":0,"holes":[],"outer":24,"uv_bounds":[{"hi":6.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":25,"key":"bottom-17-25","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":17,"key":"bottom-17"}]},"sense":"Forward","shell_ref":0,"surface":25,"trim":{"chart":0,"holes":[],"outer":25,"uv_bounds":[{"hi":10.000000001,"lo":3.499999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":26,"key":"side-0-1-18-26","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":18,"key":"side-0-1-18"}]},"sense":"Forward","shell_ref":0,"surface":26,"trim":{"chart":0,"holes":[],"outer":26,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":27,"key":"side-0-0-19-27","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":19,"key":"side-0-0-19"}]},"sense":"Forward","shell_ref":0,"surface":27,"trim":{"chart":0,"holes":[],"outer":27,"uv_bounds":[{"hi":10.000000001,"lo":3.499999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":28,"key":"side-0-2-20-28","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":20,"key":"side-0-2-20"}]},"sense":"Forward","shell_ref":0,"surface":28,"trim":{"chart":0,"holes":[],"outer":28,"uv_bounds":[{"hi":6.500000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":29,"key":"top-21-29","provenance":{"reversed":false,"role":"Split","sources":[{"body":"batch-planar.overlapping-cutters:part:0","entity":"batch-planar.overlapping-cutters:part:0","face":21,"key":"top-21"}]},"sense":"Forward","shell_ref":0,"surface":29,"trim":{"chart":0,"holes":[],"outer":29,"uv_bounds":[{"hi":10.000000001,"lo":3.499999999},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":22,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":12,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":19,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":10,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":17,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":6,"twin":14,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":7,"twin":47,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":8,"wire_ref":null},{"edge":7,"face":2,"from":4,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":7,"twin":7,"wire_ref":null},{"edge":8,"face":2,"from":7,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":0,"twin":27,"wire_ref":null},{"edge":3,"face":2,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":3,"wire_ref":null},{"edge":9,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":4,"twin":18,"wire_ref":null},{"edge":1,"face":3,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":1,"twin":1,"wire_ref":null},{"edge":10,"face":3,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":31,"wire_ref":null},{"edge":5,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":11,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":2,"twin":16,"wire_ref":null},{"edge":11,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":5,"twin":15,"wire_ref":null},{"edge":4,"face":4,"from":5,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":4,"twin":4,"wire_ref":null},{"edge":9,"face":4,"from":4,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":3,"twin":11,"wire_ref":null},{"edge":2,"face":4,"from":3,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":2,"wire_ref":null},{"edge":12,"face":5,"from":8,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":9,"twin":35,"wire_ref":null},{"edge":13,"face":5,"from":9,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Reverse"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":1,"twin":28,"wire_ref":null},{"edge":0,"face":5,"from":1,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":0,"twin":0,"wire_ref":null},{"edge":14,"face":5,"from":0,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":8,"twin":26,"wire_ref":null},{"edge":15,"face":6,"from":7,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":10,"twin":38,"wire_ref":null},{"edge":16,"face":6,"from":10,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":8,"twin":32,"wire_ref":null},{"edge":14,"face":6,"from":8,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":0,"twin":23,"wire_ref":null},{"edge":8,"face":6,"from":0,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":7,"twin":9,"wire_ref":null},{"edge":13,"face":7,"from":1,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":9,"twin":21,"wire_ref":null},{"edge":17,"face":7,"from":9,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":11,"twin":34,"wire_ref":null},{"edge":18,"face":7,"from":11,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":6,"twin":40,"wire_ref":null},{"edge":10,"face":7,"from":6,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":1,"twin":13,"wire_ref":null},{"edge":16,"face":8,"from":8,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":10,"twin":25,"wire_ref":null},{"edge":19,"face":8,"from":10,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":11,"twin":51,"wire_ref":null},{"edge":17,"face":8,"from":11,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":9,"twin":29,"wire_ref":null},{"edge":12,"face":8,"from":9,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":8,"twin":20,"wire_ref":null},{"edge":20,"face":9,"from":12,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":13,"twin":54,"wire_ref":null},{"edge":21,"face":9,"from":13,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":10,"twin":48,"wire_ref":null},{"edge":15,"face":9,"from":10,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":7,"twin":24,"wire_ref":null},{"edge":22,"face":9,"from":7,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":12,"twin":46,"wire_ref":null},{"edge":18,"face":10,"from":6,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":11,"twin":30,"wire_ref":null},{"edge":23,"face":10,"from":11,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":14,"twin":50,"wire_ref":null},{"edge":24,"face":10,"from":14,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":15,"twin":56,"wire_ref":null},{"edge":25,"face":10,"from":15,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":6,"twin":44,"wire_ref":null},{"edge":25,"face":11,"from":6,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":15,"twin":43,"wire_ref":null},{"edge":26,"face":11,"from":15,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Reverse"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":12,"twin":63,"wire_ref":null},{"edge":22,"face":11,"from":12,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":7,"twin":39,"wire_ref":null},{"edge":6,"face":11,"from":7,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Forward"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":6,"twin":6,"wire_ref":null},{"edge":21,"face":12,"from":10,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":12,"next":49,"prev":51,"to":13,"twin":37,"wire_ref":null},{"edge":27,"face":12,"from":13,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":12,"next":50,"prev":48,"to":14,"twin":67,"wire_ref":null},{"edge":23,"face":12,"from":14,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":12,"next":51,"prev":49,"to":11,"twin":41,"wire_ref":null},{"edge":19,"face":12,"from":11,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":12,"next":48,"prev":50,"to":10,"twin":33,"wire_ref":null},{"edge":28,"face":13,"from":16,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":13,"next":53,"prev":55,"to":17,"twin":70,"wire_ref":null},{"edge":29,"face":13,"from":17,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Reverse"},"id":53,"loop_ref":13,"next":54,"prev":52,"to":13,"twin":64,"wire_ref":null},{"edge":20,"face":13,"from":13,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":13,"next":55,"prev":53,"to":12,"twin":36,"wire_ref":null},{"edge":30,"face":13,"from":12,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Forward"},"id":55,"loop_ref":13,"next":52,"prev":54,"to":16,"twin":62,"wire_ref":null},{"edge":24,"face":14,"from":15,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":14,"next":57,"prev":59,"to":14,"twin":42,"wire_ref":null},{"edge":31,"face":14,"from":14,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":14,"next":58,"prev":56,"to":18,"twin":66,"wire_ref":null},{"edge":32,"face":14,"from":18,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":14,"next":59,"prev":57,"to":19,"twin":72,"wire_ref":null},{"edge":33,"face":14,"from":19,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":14,"next":56,"prev":58,"to":15,"twin":60,"wire_ref":null},{"edge":33,"face":15,"from":15,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Forward"},"id":60,"loop_ref":15,"next":61,"prev":63,"to":19,"twin":59,"wire_ref":null},{"edge":34,"face":15,"from":19,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Reverse"},"id":61,"loop_ref":15,"next":62,"prev":60,"to":16,"twin":79,"wire_ref":null},{"edge":30,"face":15,"from":16,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":15,"next":63,"prev":61,"to":12,"twin":55,"wire_ref":null},{"edge":26,"face":15,"from":12,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Forward"},"id":63,"loop_ref":15,"next":60,"prev":62,"to":15,"twin":45,"wire_ref":null},{"edge":29,"face":16,"from":13,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Forward"},"id":64,"loop_ref":16,"next":65,"prev":67,"to":17,"twin":53,"wire_ref":null},{"edge":35,"face":16,"from":17,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Forward"},"id":65,"loop_ref":16,"next":66,"prev":64,"to":18,"twin":83,"wire_ref":null},{"edge":31,"face":16,"from":18,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Reverse"},"id":66,"loop_ref":16,"next":67,"prev":65,"to":14,"twin":57,"wire_ref":null},{"edge":27,"face":16,"from":14,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Reverse"},"id":67,"loop_ref":16,"next":64,"prev":66,"to":13,"twin":49,"wire_ref":null},{"edge":36,"face":17,"from":20,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":17,"next":69,"prev":71,"to":21,"twin":110,"wire_ref":null},{"edge":37,"face":17,"from":21,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Reverse"},"id":69,"loop_ref":17,"next":70,"prev":68,"to":17,"twin":80,"wire_ref":null},{"edge":28,"face":17,"from":17,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":17,"next":71,"prev":69,"to":16,"twin":52,"wire_ref":null},{"edge":38,"face":17,"from":16,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Forward"},"id":71,"loop_ref":17,"next":68,"prev":70,"to":20,"twin":78,"wire_ref":null},{"edge":32,"face":18,"from":19,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Forward"},"id":72,"loop_ref":18,"next":73,"prev":75,"to":18,"twin":58,"wire_ref":null},{"edge":39,"face":18,"from":18,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Forward"},"id":73,"loop_ref":18,"next":74,"prev":72,"to":22,"twin":82,"wire_ref":null},{"edge":40,"face":18,"from":22,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":18,"next":75,"prev":73,"to":23,"twin":112,"wire_ref":null},{"edge":41,"face":18,"from":23,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Reverse"},"id":75,"loop_ref":18,"next":72,"prev":74,"to":19,"twin":76,"wire_ref":null},{"edge":41,"face":19,"from":19,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Forward"},"id":76,"loop_ref":19,"next":77,"prev":79,"to":23,"twin":75,"wire_ref":null},{"edge":42,"face":19,"from":23,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Reverse"},"id":77,"loop_ref":19,"next":78,"prev":76,"to":20,"twin":84,"wire_ref":null},{"edge":38,"face":19,"from":20,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":19,"next":79,"prev":77,"to":16,"twin":71,"wire_ref":null},{"edge":34,"face":19,"from":16,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Forward"},"id":79,"loop_ref":19,"next":76,"prev":78,"to":19,"twin":61,"wire_ref":null},{"edge":37,"face":20,"from":17,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Forward"},"id":80,"loop_ref":20,"next":81,"prev":83,"to":21,"twin":69,"wire_ref":null},{"edge":43,"face":20,"from":21,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Forward"},"id":81,"loop_ref":20,"next":82,"prev":80,"to":22,"twin":119,"wire_ref":null},{"edge":39,"face":20,"from":22,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":20,"next":83,"prev":81,"to":18,"twin":73,"wire_ref":null},{"edge":35,"face":20,"from":18,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":20,"next":80,"prev":82,"to":17,"twin":65,"wire_ref":null},{"edge":42,"face":21,"from":20,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Forward"},"id":84,"loop_ref":21,"next":85,"prev":87,"to":23,"twin":77,"wire_ref":null},{"edge":44,"face":21,"from":23,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Reverse"},"id":85,"loop_ref":21,"next":86,"prev":84,"to":24,"twin":96,"wire_ref":null},{"edge":45,"face":21,"from":24,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Reverse"},"id":86,"loop_ref":21,"next":87,"prev":85,"to":25,"twin":103,"wire_ref":null},{"edge":46,"face":21,"from":25,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Forward"},"id":87,"loop_ref":21,"next":84,"prev":86,"to":20,"twin":94,"wire_ref":null},{"edge":47,"face":22,"from":26,"geometry_use":{"pcurve":88,"periodic_lift":[0,0],"sense":"Forward"},"id":88,"loop_ref":22,"next":89,"prev":91,"to":27,"twin":101,"wire_ref":null},{"edge":48,"face":22,"from":27,"geometry_use":{"pcurve":89,"periodic_lift":[0,0],"sense":"Forward"},"id":89,"loop_ref":22,"next":90,"prev":88,"to":28,"twin":98,"wire_ref":null},{"edge":49,"face":22,"from":28,"geometry_use":{"pcurve":90,"periodic_lift":[0,0],"sense":"Reverse"},"id":90,"loop_ref":22,"next":91,"prev":89,"to":29,"twin":104,"wire_ref":null},{"edge":50,"face":22,"from":29,"geometry_use":{"pcurve":91,"periodic_lift":[0,0],"sense":"Reverse"},"id":91,"loop_ref":22,"next":88,"prev":90,"to":26,"twin":92,"wire_ref":null},{"edge":50,"face":23,"from":26,"geometry_use":{"pcurve":92,"periodic_lift":[0,0],"sense":"Forward"},"id":92,"loop_ref":23,"next":93,"prev":95,"to":29,"twin":91,"wire_ref":null},{"edge":51,"face":23,"from":29,"geometry_use":{"pcurve":93,"periodic_lift":[0,0],"sense":"Reverse"},"id":93,"loop_ref":23,"next":94,"prev":92,"to":20,"twin":111,"wire_ref":null},{"edge":46,"face":23,"from":20,"geometry_use":{"pcurve":94,"periodic_lift":[0,0],"sense":"Reverse"},"id":94,"loop_ref":23,"next":95,"prev":93,"to":25,"twin":87,"wire_ref":null},{"edge":52,"face":23,"from":25,"geometry_use":{"pcurve":95,"periodic_lift":[0,0],"sense":"Forward"},"id":95,"loop_ref":23,"next":92,"prev":94,"to":26,"twin":102,"wire_ref":null},{"edge":44,"face":24,"from":24,"geometry_use":{"pcurve":96,"periodic_lift":[0,0],"sense":"Forward"},"id":96,"loop_ref":24,"next":97,"prev":99,"to":23,"twin":85,"wire_ref":null},{"edge":53,"face":24,"from":23,"geometry_use":{"pcurve":97,"periodic_lift":[0,0],"sense":"Forward"},"id":97,"loop_ref":24,"next":98,"prev":96,"to":28,"twin":115,"wire_ref":null},{"edge":48,"face":24,"from":28,"geometry_use":{"pcurve":98,"periodic_lift":[0,0],"sense":"Reverse"},"id":98,"loop_ref":24,"next":99,"prev":97,"to":27,"twin":89,"wire_ref":null},{"edge":54,"face":24,"from":27,"geometry_use":{"pcurve":99,"periodic_lift":[0,0],"sense":"Reverse"},"id":99,"loop_ref":24,"next":96,"prev":98,"to":24,"twin":100,"wire_ref":null},{"edge":54,"face":25,"from":24,"geometry_use":{"pcurve":100,"periodic_lift":[0,0],"sense":"Forward"},"id":100,"loop_ref":25,"next":101,"prev":103,"to":27,"twin":99,"wire_ref":null},{"edge":47,"face":25,"from":27,"geometry_use":{"pcurve":101,"periodic_lift":[0,0],"sense":"Reverse"},"id":101,"loop_ref":25,"next":102,"prev":100,"to":26,"twin":88,"wire_ref":null},{"edge":52,"face":25,"from":26,"geometry_use":{"pcurve":102,"periodic_lift":[0,0],"sense":"Reverse"},"id":102,"loop_ref":25,"next":103,"prev":101,"to":25,"twin":95,"wire_ref":null},{"edge":45,"face":25,"from":25,"geometry_use":{"pcurve":103,"periodic_lift":[0,0],"sense":"Forward"},"id":103,"loop_ref":25,"next":100,"prev":102,"to":24,"twin":86,"wire_ref":null},{"edge":49,"face":26,"from":29,"geometry_use":{"pcurve":104,"periodic_lift":[0,0],"sense":"Forward"},"id":104,"loop_ref":26,"next":105,"prev":107,"to":28,"twin":90,"wire_ref":null},{"edge":55,"face":26,"from":28,"geometry_use":{"pcurve":105,"periodic_lift":[0,0],"sense":"Forward"},"id":105,"loop_ref":26,"next":106,"prev":104,"to":30,"twin":114,"wire_ref":null},{"edge":56,"face":26,"from":30,"geometry_use":{"pcurve":106,"periodic_lift":[0,0],"sense":"Reverse"},"id":106,"loop_ref":26,"next":107,"prev":105,"to":31,"twin":117,"wire_ref":null},{"edge":57,"face":26,"from":31,"geometry_use":{"pcurve":107,"periodic_lift":[0,0],"sense":"Reverse"},"id":107,"loop_ref":26,"next":104,"prev":106,"to":29,"twin":108,"wire_ref":null},{"edge":57,"face":27,"from":29,"geometry_use":{"pcurve":108,"periodic_lift":[0,0],"sense":"Forward"},"id":108,"loop_ref":27,"next":109,"prev":111,"to":31,"twin":107,"wire_ref":null},{"edge":58,"face":27,"from":31,"geometry_use":{"pcurve":109,"periodic_lift":[0,0],"sense":"Reverse"},"id":109,"loop_ref":27,"next":110,"prev":108,"to":21,"twin":116,"wire_ref":null},{"edge":36,"face":27,"from":21,"geometry_use":{"pcurve":110,"periodic_lift":[0,0],"sense":"Reverse"},"id":110,"loop_ref":27,"next":111,"prev":109,"to":20,"twin":68,"wire_ref":null},{"edge":51,"face":27,"from":20,"geometry_use":{"pcurve":111,"periodic_lift":[0,0],"sense":"Forward"},"id":111,"loop_ref":27,"next":108,"prev":110,"to":29,"twin":93,"wire_ref":null},{"edge":40,"face":28,"from":23,"geometry_use":{"pcurve":112,"periodic_lift":[0,0],"sense":"Forward"},"id":112,"loop_ref":28,"next":113,"prev":115,"to":22,"twin":74,"wire_ref":null},{"edge":59,"face":28,"from":22,"geometry_use":{"pcurve":113,"periodic_lift":[0,0],"sense":"Forward"},"id":113,"loop_ref":28,"next":114,"prev":112,"to":30,"twin":118,"wire_ref":null},{"edge":55,"face":28,"from":30,"geometry_use":{"pcurve":114,"periodic_lift":[0,0],"sense":"Reverse"},"id":114,"loop_ref":28,"next":115,"prev":113,"to":28,"twin":105,"wire_ref":null},{"edge":53,"face":28,"from":28,"geometry_use":{"pcurve":115,"periodic_lift":[0,0],"sense":"Reverse"},"id":115,"loop_ref":28,"next":112,"prev":114,"to":23,"twin":97,"wire_ref":null},{"edge":58,"face":29,"from":21,"geometry_use":{"pcurve":116,"periodic_lift":[0,0],"sense":"Forward"},"id":116,"loop_ref":29,"next":117,"prev":119,"to":31,"twin":109,"wire_ref":null},{"edge":56,"face":29,"from":31,"geometry_use":{"pcurve":117,"periodic_lift":[0,0],"sense":"Forward"},"id":117,"loop_ref":29,"next":118,"prev":116,"to":30,"twin":106,"wire_ref":null},{"edge":59,"face":29,"from":30,"geometry_use":{"pcurve":118,"periodic_lift":[0,0],"sense":"Reverse"},"id":118,"loop_ref":29,"next":119,"prev":117,"to":22,"twin":113,"wire_ref":null},{"edge":43,"face":29,"from":22,"geometry_use":{"pcurve":119,"periodic_lift":[0,0],"sense":"Reverse"},"id":119,"loop_ref":29,"next":116,"prev":118,"to":21,"twin":81,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":48},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":52},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":56},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":60},{"face_ref":16,"id":16,"is_hole":false,"start_halfedge":64},{"face_ref":17,"id":17,"is_hole":false,"start_halfedge":68},{"face_ref":18,"id":18,"is_hole":false,"start_halfedge":72},{"face_ref":19,"id":19,"is_hole":false,"start_halfedge":76},{"face_ref":20,"id":20,"is_hole":false,"start_halfedge":80},{"face_ref":21,"id":21,"is_hole":false,"start_halfedge":84},{"face_ref":22,"id":22,"is_hole":false,"start_halfedge":88},{"face_ref":23,"id":23,"is_hole":false,"start_halfedge":92},{"face_ref":24,"id":24,"is_hole":false,"start_halfedge":96},{"face_ref":25,"id":25,"is_hole":false,"start_halfedge":100},{"face_ref":26,"id":26,"is_hole":false,"start_halfedge":104},{"face_ref":27,"id":27,"is_hole":false,"start_halfedge":108},{"face_ref":28,"id":28,"is_hole":false,"start_halfedge":112},{"face_ref":29,"id":29,"is_hole":false,"start_halfedge":116}],"shells":[{"faces":[0,5,3,4,2,8,7,6,1,12,10,9,11,16,14,13,15,20,18,17,19,29,28,27,21,26,24,23,25,22],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,2.1],"tolerance":1e-9},{"id":1,"outgoing_halfedge":1,"position":[0.0,0.3,2.1],"tolerance":1e-9},{"id":2,"outgoing_halfedge":2,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":3,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":4,"position":[2.0,0.0,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":5,"position":[2.0,0.3,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":6,"position":[2.0,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":7,"position":[2.0,0.0,2.1],"tolerance":1e-9},{"id":8,"outgoing_halfedge":20,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":9,"outgoing_halfedge":21,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":10,"outgoing_halfedge":25,"position":[2.0,0.0,3.0],"tolerance":1e-9},{"id":11,"outgoing_halfedge":30,"position":[2.0,0.3,3.0],"tolerance":1e-9},{"id":12,"outgoing_halfedge":36,"position":[2.5,0.0,2.1],"tolerance":1e-9},{"id":13,"outgoing_halfedge":37,"position":[2.5,0.0,3.0],"tolerance":1e-9},{"id":14,"outgoing_halfedge":42,"position":[2.5,0.3,3.0],"tolerance":1e-9},{"id":15,"outgoing_halfedge":43,"position":[2.5,0.3,2.1],"tolerance":1e-9},{"id":16,"outgoing_halfedge":52,"position":[3.0,0.0,2.1],"tolerance":1e-9},{"id":17,"outgoing_halfedge":53,"position":[3.0,0.0,3.0],"tolerance":1e-9},{"id":18,"outgoing_halfedge":58,"position":[3.0,0.3,3.0],"tolerance":1e-9},{"id":19,"outgoing_halfedge":59,"position":[3.0,0.3,2.1],"tolerance":1e-9},{"id":20,"outgoing_halfedge":68,"position":[3.5,0.0,2.1],"tolerance":1e-9},{"id":21,"outgoing_halfedge":69,"position":[3.5,0.0,3.0],"tolerance":1e-9},{"id":22,"outgoing_halfedge":74,"position":[3.5,0.3,3.0],"tolerance":1e-9},{"id":23,"outgoing_halfedge":75,"position":[3.5,0.3,2.1],"tolerance":1e-9},{"id":24,"outgoing_halfedge":86,"position":[3.5,0.3,0.0],"tolerance":1e-9},{"id":25,"outgoing_halfedge":87,"position":[3.5,0.0,0.0],"tolerance":1e-9},{"id":26,"outgoing_halfedge":88,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":27,"outgoing_halfedge":89,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":28,"outgoing_halfedge":90,"position":[10.0,0.3,2.1],"tolerance":1e-9},{"id":29,"outgoing_halfedge":91,"position":[10.0,0.0,2.1],"tolerance":1e-9},{"id":30,"outgoing_halfedge":106,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":31,"outgoing_halfedge":107,"position":[10.0,0.0,3.0],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.overlapping-cutters.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.overlapping-cutters.json new file mode 100644 index 0000000..866daf5 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.overlapping-cutters.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"first","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.000000001,"lo":1.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.000000001,"lo":1.999999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first","entity":"first","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.0,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.0,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.0,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.0,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.5,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.5,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.5,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"second","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.500000001,"lo":2.499999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.500000001,"lo":2.499999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second","entity":"second","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.5,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.5,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.5,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.5,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.5,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.5,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.5,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.5,0.3,2.1],"tolerance":1e-9}],"wires":[]}}],"handlers":[],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[10.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"error":{"CoverageGap":{"families":["overlapping planar batch cutters","overlapping planar batch cutters"]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.single-cutter.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.single-cutter.json new file mode 100644 index 0000000..ea8bd2a --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.single-cutter.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"lower_cutout","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"lower_cutout","entity":"lower_cutout","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.000000001,"lo":1.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"lower_cutout","entity":"lower_cutout","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.000000001,"lo":1.999999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"lower_cutout","entity":"lower_cutout","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"lower_cutout","entity":"lower_cutout","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"lower_cutout","entity":"lower_cutout","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"lower_cutout","entity":"lower_cutout","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.0,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.0,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.0,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.0,0.3,2.1],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","boolean_rectilinear"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[10.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.3,3.0]}],"intersections":[],"pcurves":[{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.0,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.0,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"}]},"id":"batch-planar.single-cutter","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-9,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-9,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":7,"id":7,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-9,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":11,"id":9,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-9,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":20,"id":12,"tolerance":1e-9,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":21,"id":13,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":23,"id":14,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":24,"id":15,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":25,"id":16,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":29,"id":17,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":30,"id":18,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":33,"id":19,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":36,"id":20,"tolerance":1e-9,"twin_halfedge":78},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":37,"id":21,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":39,"id":22,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":41,"id":23,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":42,"id":24,"tolerance":1e-9,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":43,"id":25,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":45,"id":26,"tolerance":1e-9,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":49,"id":27,"tolerance":1e-9,"twin_halfedge":87},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":53,"id":28,"tolerance":1e-9,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":54,"id":29,"tolerance":1e-9,"twin_halfedge":71},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":55,"id":30,"tolerance":1e-9,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":56,"id":31,"tolerance":1e-9,"twin_halfedge":69},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":57,"id":32,"tolerance":1e-9,"twin_halfedge":66},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":58,"id":33,"tolerance":1e-9,"twin_halfedge":72},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":59,"id":34,"tolerance":1e-9,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":7.0,"lo":0.0}},"halfedge":61,"id":35,"tolerance":1e-9,"twin_halfedge":79},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":7.0,"lo":0.0}},"halfedge":63,"id":36,"tolerance":1e-9,"twin_halfedge":70},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":7.0,"lo":0.0}},"halfedge":65,"id":37,"tolerance":1e-9,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":7.0,"lo":0.0}},"halfedge":67,"id":38,"tolerance":1e-9,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":73,"id":39,"tolerance":1e-9,"twin_halfedge":82},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":74,"id":40,"tolerance":1e-9,"twin_halfedge":85},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":75,"id":41,"tolerance":1e-9,"twin_halfedge":76},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":7.0,"lo":0.0}},"halfedge":77,"id":42,"tolerance":1e-9,"twin_halfedge":84},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":7.0,"lo":0.0}},"halfedge":81,"id":43,"tolerance":1e-9,"twin_halfedge":86}],"faces":[{"id":0,"key":"side-0-3-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":1,"key":"side-0-3-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"lower_cutout","entity":"lower_cutout","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":2,"key":"side-0-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-2-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":10.000000001,"lo":7.999999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"bottom-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":5,"key":"side-0-3-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":6,"key":"side-0-0-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":2.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":7,"key":"side-0-2-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":10.000000001,"lo":7.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":8,"key":"top-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":2.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":9,"key":"side-0-0-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":3.000000001,"lo":1.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":10,"key":"side-0-2-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":8.000000001,"lo":6.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":11,"key":"top-11","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"lower_cutout","entity":"lower_cutout","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":3.000000001,"lo":1.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":12,"key":"top-12","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":3.000000001,"lo":1.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":13,"key":"side-0-1-13","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"lower_cutout","entity":"lower_cutout","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":14,"key":"side-0-1-14","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":15,"key":"side-0-0-15","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":10.000000001,"lo":2.999999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":16,"key":"side-0-2-16","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[],"outer":16,"uv_bounds":[{"hi":7.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":17,"key":"bottom-17","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":17,"trim":{"chart":0,"holes":[],"outer":17,"uv_bounds":[{"hi":10.000000001,"lo":2.999999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":18,"key":"side-0-1-18","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":18,"trim":{"chart":0,"holes":[],"outer":18,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":19,"key":"side-0-0-19","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":19,"trim":{"chart":0,"holes":[],"outer":19,"uv_bounds":[{"hi":10.000000001,"lo":2.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":20,"key":"side-0-2-20","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":20,"trim":{"chart":0,"holes":[],"outer":20,"uv_bounds":[{"hi":7.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":21,"key":"top-21","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":21,"trim":{"chart":0,"holes":[],"outer":21,"uv_bounds":[{"hi":10.000000001,"lo":2.999999999},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":22,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":12,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":19,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":10,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":17,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":6,"twin":14,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":7,"twin":47,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":8,"wire_ref":null},{"edge":7,"face":2,"from":4,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":7,"twin":7,"wire_ref":null},{"edge":8,"face":2,"from":7,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":0,"twin":27,"wire_ref":null},{"edge":3,"face":2,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":3,"wire_ref":null},{"edge":9,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":4,"twin":18,"wire_ref":null},{"edge":1,"face":3,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":1,"twin":1,"wire_ref":null},{"edge":10,"face":3,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":31,"wire_ref":null},{"edge":5,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":11,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":2,"twin":16,"wire_ref":null},{"edge":11,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":5,"twin":15,"wire_ref":null},{"edge":4,"face":4,"from":5,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":4,"twin":4,"wire_ref":null},{"edge":9,"face":4,"from":4,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":3,"twin":11,"wire_ref":null},{"edge":2,"face":4,"from":3,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":2,"wire_ref":null},{"edge":12,"face":5,"from":8,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":9,"twin":35,"wire_ref":null},{"edge":13,"face":5,"from":9,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Reverse"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":1,"twin":28,"wire_ref":null},{"edge":0,"face":5,"from":1,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":0,"twin":0,"wire_ref":null},{"edge":14,"face":5,"from":0,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":8,"twin":26,"wire_ref":null},{"edge":15,"face":6,"from":7,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":10,"twin":38,"wire_ref":null},{"edge":16,"face":6,"from":10,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":8,"twin":32,"wire_ref":null},{"edge":14,"face":6,"from":8,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":0,"twin":23,"wire_ref":null},{"edge":8,"face":6,"from":0,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":7,"twin":9,"wire_ref":null},{"edge":13,"face":7,"from":1,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":9,"twin":21,"wire_ref":null},{"edge":17,"face":7,"from":9,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":11,"twin":34,"wire_ref":null},{"edge":18,"face":7,"from":11,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":6,"twin":40,"wire_ref":null},{"edge":10,"face":7,"from":6,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":1,"twin":13,"wire_ref":null},{"edge":16,"face":8,"from":8,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":10,"twin":25,"wire_ref":null},{"edge":19,"face":8,"from":10,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":11,"twin":51,"wire_ref":null},{"edge":17,"face":8,"from":11,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":9,"twin":29,"wire_ref":null},{"edge":12,"face":8,"from":9,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":8,"twin":20,"wire_ref":null},{"edge":20,"face":9,"from":12,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":13,"twin":78,"wire_ref":null},{"edge":21,"face":9,"from":13,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":10,"twin":48,"wire_ref":null},{"edge":15,"face":9,"from":10,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":7,"twin":24,"wire_ref":null},{"edge":22,"face":9,"from":7,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":12,"twin":46,"wire_ref":null},{"edge":18,"face":10,"from":6,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":11,"twin":30,"wire_ref":null},{"edge":23,"face":10,"from":11,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":14,"twin":50,"wire_ref":null},{"edge":24,"face":10,"from":14,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":15,"twin":80,"wire_ref":null},{"edge":25,"face":10,"from":15,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":6,"twin":44,"wire_ref":null},{"edge":25,"face":11,"from":6,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":15,"twin":43,"wire_ref":null},{"edge":26,"face":11,"from":15,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Reverse"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":12,"twin":52,"wire_ref":null},{"edge":22,"face":11,"from":12,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":7,"twin":39,"wire_ref":null},{"edge":6,"face":11,"from":7,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Forward"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":6,"twin":6,"wire_ref":null},{"edge":21,"face":12,"from":10,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":12,"next":49,"prev":51,"to":13,"twin":37,"wire_ref":null},{"edge":27,"face":12,"from":13,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":12,"next":50,"prev":48,"to":14,"twin":87,"wire_ref":null},{"edge":23,"face":12,"from":14,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":12,"next":51,"prev":49,"to":11,"twin":41,"wire_ref":null},{"edge":19,"face":12,"from":11,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":12,"next":48,"prev":50,"to":10,"twin":33,"wire_ref":null},{"edge":26,"face":13,"from":12,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":13,"next":53,"prev":55,"to":15,"twin":45,"wire_ref":null},{"edge":28,"face":13,"from":15,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Reverse"},"id":53,"loop_ref":13,"next":54,"prev":52,"to":16,"twin":64,"wire_ref":null},{"edge":29,"face":13,"from":16,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":13,"next":55,"prev":53,"to":17,"twin":71,"wire_ref":null},{"edge":30,"face":13,"from":17,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Forward"},"id":55,"loop_ref":13,"next":52,"prev":54,"to":12,"twin":62,"wire_ref":null},{"edge":31,"face":14,"from":18,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":14,"next":57,"prev":59,"to":19,"twin":69,"wire_ref":null},{"edge":32,"face":14,"from":19,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":14,"next":58,"prev":56,"to":20,"twin":66,"wire_ref":null},{"edge":33,"face":14,"from":20,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":14,"next":59,"prev":57,"to":21,"twin":72,"wire_ref":null},{"edge":34,"face":14,"from":21,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":14,"next":56,"prev":58,"to":18,"twin":60,"wire_ref":null},{"edge":34,"face":15,"from":18,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Forward"},"id":60,"loop_ref":15,"next":61,"prev":63,"to":21,"twin":59,"wire_ref":null},{"edge":35,"face":15,"from":21,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Reverse"},"id":61,"loop_ref":15,"next":62,"prev":60,"to":12,"twin":79,"wire_ref":null},{"edge":30,"face":15,"from":12,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":15,"next":63,"prev":61,"to":17,"twin":55,"wire_ref":null},{"edge":36,"face":15,"from":17,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Forward"},"id":63,"loop_ref":15,"next":60,"prev":62,"to":18,"twin":70,"wire_ref":null},{"edge":28,"face":16,"from":16,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Forward"},"id":64,"loop_ref":16,"next":65,"prev":67,"to":15,"twin":53,"wire_ref":null},{"edge":37,"face":16,"from":15,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Forward"},"id":65,"loop_ref":16,"next":66,"prev":64,"to":20,"twin":83,"wire_ref":null},{"edge":32,"face":16,"from":20,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Reverse"},"id":66,"loop_ref":16,"next":67,"prev":65,"to":19,"twin":57,"wire_ref":null},{"edge":38,"face":16,"from":19,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Reverse"},"id":67,"loop_ref":16,"next":64,"prev":66,"to":16,"twin":68,"wire_ref":null},{"edge":38,"face":17,"from":16,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":17,"next":69,"prev":71,"to":19,"twin":67,"wire_ref":null},{"edge":31,"face":17,"from":19,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Reverse"},"id":69,"loop_ref":17,"next":70,"prev":68,"to":18,"twin":56,"wire_ref":null},{"edge":36,"face":17,"from":18,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":17,"next":71,"prev":69,"to":17,"twin":63,"wire_ref":null},{"edge":29,"face":17,"from":17,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Forward"},"id":71,"loop_ref":17,"next":68,"prev":70,"to":16,"twin":54,"wire_ref":null},{"edge":33,"face":18,"from":21,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Forward"},"id":72,"loop_ref":18,"next":73,"prev":75,"to":20,"twin":58,"wire_ref":null},{"edge":39,"face":18,"from":20,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Forward"},"id":73,"loop_ref":18,"next":74,"prev":72,"to":22,"twin":82,"wire_ref":null},{"edge":40,"face":18,"from":22,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":18,"next":75,"prev":73,"to":23,"twin":85,"wire_ref":null},{"edge":41,"face":18,"from":23,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Reverse"},"id":75,"loop_ref":18,"next":72,"prev":74,"to":21,"twin":76,"wire_ref":null},{"edge":41,"face":19,"from":21,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Forward"},"id":76,"loop_ref":19,"next":77,"prev":79,"to":23,"twin":75,"wire_ref":null},{"edge":42,"face":19,"from":23,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Reverse"},"id":77,"loop_ref":19,"next":78,"prev":76,"to":13,"twin":84,"wire_ref":null},{"edge":20,"face":19,"from":13,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":19,"next":79,"prev":77,"to":12,"twin":36,"wire_ref":null},{"edge":35,"face":19,"from":12,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Forward"},"id":79,"loop_ref":19,"next":76,"prev":78,"to":21,"twin":61,"wire_ref":null},{"edge":24,"face":20,"from":15,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Forward"},"id":80,"loop_ref":20,"next":81,"prev":83,"to":14,"twin":42,"wire_ref":null},{"edge":43,"face":20,"from":14,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Forward"},"id":81,"loop_ref":20,"next":82,"prev":80,"to":22,"twin":86,"wire_ref":null},{"edge":39,"face":20,"from":22,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":20,"next":83,"prev":81,"to":20,"twin":73,"wire_ref":null},{"edge":37,"face":20,"from":20,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":20,"next":80,"prev":82,"to":15,"twin":65,"wire_ref":null},{"edge":42,"face":21,"from":13,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Forward"},"id":84,"loop_ref":21,"next":85,"prev":87,"to":23,"twin":77,"wire_ref":null},{"edge":40,"face":21,"from":23,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Forward"},"id":85,"loop_ref":21,"next":86,"prev":84,"to":22,"twin":74,"wire_ref":null},{"edge":43,"face":21,"from":22,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Reverse"},"id":86,"loop_ref":21,"next":87,"prev":85,"to":14,"twin":81,"wire_ref":null},{"edge":27,"face":21,"from":14,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Reverse"},"id":87,"loop_ref":21,"next":84,"prev":86,"to":13,"twin":49,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":48},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":52},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":56},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":60},{"face_ref":16,"id":16,"is_hole":false,"start_halfedge":64},{"face_ref":17,"id":17,"is_hole":false,"start_halfedge":68},{"face_ref":18,"id":18,"is_hole":false,"start_halfedge":72},{"face_ref":19,"id":19,"is_hole":false,"start_halfedge":76},{"face_ref":20,"id":20,"is_hole":false,"start_halfedge":80},{"face_ref":21,"id":21,"is_hole":false,"start_halfedge":84}],"shells":[{"faces":[0,5,3,4,2,8,7,6,1,12,10,9,11,21,20,19,13,18,16,15,17,14],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,2.1],"tolerance":1e-9},{"id":1,"outgoing_halfedge":1,"position":[0.0,0.3,2.1],"tolerance":1e-9},{"id":2,"outgoing_halfedge":2,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":3,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":4,"position":[2.0,0.0,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":5,"position":[2.0,0.3,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":6,"position":[2.0,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":7,"position":[2.0,0.0,2.1],"tolerance":1e-9},{"id":8,"outgoing_halfedge":20,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":9,"outgoing_halfedge":21,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":10,"outgoing_halfedge":25,"position":[2.0,0.0,3.0],"tolerance":1e-9},{"id":11,"outgoing_halfedge":30,"position":[2.0,0.3,3.0],"tolerance":1e-9},{"id":12,"outgoing_halfedge":36,"position":[3.0,0.0,2.1],"tolerance":1e-9},{"id":13,"outgoing_halfedge":37,"position":[3.0,0.0,3.0],"tolerance":1e-9},{"id":14,"outgoing_halfedge":42,"position":[3.0,0.3,3.0],"tolerance":1e-9},{"id":15,"outgoing_halfedge":43,"position":[3.0,0.3,2.1],"tolerance":1e-9},{"id":16,"outgoing_halfedge":54,"position":[3.0,0.3,0.0],"tolerance":1e-9},{"id":17,"outgoing_halfedge":55,"position":[3.0,0.0,0.0],"tolerance":1e-9},{"id":18,"outgoing_halfedge":56,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":19,"outgoing_halfedge":57,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":20,"outgoing_halfedge":58,"position":[10.0,0.3,2.1],"tolerance":1e-9},{"id":21,"outgoing_halfedge":59,"position":[10.0,0.0,2.1],"tolerance":1e-9},{"id":22,"outgoing_halfedge":74,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":23,"outgoing_halfedge":75,"position":[10.0,0.0,3.0],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.single-oblique.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.single-oblique.json new file mode 100644 index 0000000..889ee45 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.single-oblique.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.8660254037844386,0.0,-0.5000000000000001],"kind":"Line","origin":[2.066987298107781,-0.35,0.24999999999999997]},{"direction":[0.8660254037844386,0.0,-0.5000000000000001],"kind":"Line","origin":[3.11698729810778,-0.35,2.068653347947321]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.066987298107781,-0.35,0.24999999999999997]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.933012701892219,-0.35,-0.24999999999999997]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.9830127018922195,-0.35,1.5686533479473213]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.933012701892219,-0.35,-0.24999999999999997]},{"direction":[-0.8660254037844386,0.0,0.5000000000000001],"kind":"Line","origin":[2.933012701892219,0.65,-0.24999999999999997]},{"direction":[-0.8660254037844386,0.0,0.5000000000000001],"kind":"Line","origin":[3.9830127018922195,0.65,1.5686533479473213]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.933012701892219,0.65,-0.24999999999999997]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.066987298107781,0.65,0.24999999999999997]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.11698729810778,0.65,2.068653347947321]},{"direction":[0.49999999999999994,0.0,0.8660254037844387],"kind":"Line","origin":[2.066987298107781,0.65,0.24999999999999997]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.5000000000000002,-0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5000000000000003,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5000000000000003,0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.5000000000000002,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.4999999999999999,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.4999999999999999,-0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.4999999999999999,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.4999999999999999,0.5]},{"direction":[1.0,-1.4874168143337463e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.9999999999999997,4.063698308792034e-17]},{"direction":[1.0,-1.4874168143337463e-17],"kind":"Line2","origin":[-7.493719268661776e-16,2.0999999999999996]},{"direction":[-2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,1.4874168143337463e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.9999999999999997,-4.063698308792034e-17]},{"direction":[1.0,1.4874168143337463e-17],"kind":"Line2","origin":[9.120915477526774e-17,2.1]},{"direction":[2.1460752085336256e-16,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0999999999999996]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[3.55,0.15,1.8186533479473213],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[0.0,1.0,0.0],"z":[0.49999999999999994,0.0,0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.5,0.15,0.0],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[-0.0,-1.0,-0.0],"z":[-0.49999999999999994,-0.0,-0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.066987298107781,-0.35,0.24999999999999997],"x":[0.8660254037844386,0.0,-0.5000000000000001],"y":[0.5000000000000001,0.0,0.8660254037844386],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.933012701892219,-0.35,-0.24999999999999997],"x":[0.0,1.0,0.0],"y":[0.49999999999999994,-0.0,0.8660254037844387],"z":[0.8660254037844387,0.0,-0.49999999999999994]},"kind":"Plane"},{"frame":{"origin":[2.933012701892219,0.65,-0.24999999999999997],"x":[-0.8660254037844386,0.0,0.5000000000000001],"y":[0.5000000000000001,0.0,0.8660254037844386],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.066987298107781,0.65,0.24999999999999997],"x":[0.0,-1.0,0.0],"y":[0.49999999999999994,0.0,0.8660254037844387],"z":[-0.8660254037844387,0.0,0.49999999999999994]},"kind":"Plane"}]},"id":"first-oblique","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.9999999999999998,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.500000001,"lo":-0.500000001},{"hi":0.500000001,"lo":-0.500000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.500000001,"lo":-0.500000001},{"hi":0.500000001,"lo":-0.500000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0000000009999999,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.0000000009999999,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"first-oblique","entity":"first-oblique","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.066987298107781,-0.35,0.24999999999999997],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.933012701892219,-0.35,-0.24999999999999997],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.933012701892219,0.65,-0.24999999999999997],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.066987298107781,0.65,0.24999999999999997],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.11698729810778,-0.35,2.068653347947321],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.9830127018922195,-0.35,1.5686533479473213],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.9830127018922195,0.65,1.5686533479473213],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.11698729810778,0.65,2.068653347947321],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_planar_polyhedra"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"oblique-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"oblique-host","entity":"oblique-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[10.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,2.2204460492503135e-17,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[1.4802973661668753e-15,-1.0,0.0],"kind":"Line","origin":[2.4999999999999996,0.30000000000000004,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0773502691896253,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0773502691896253,0.0,0.0]},{"direction":[-0.8660254037844387,0.0,0.49999999999999983],"kind":"Line","origin":[2.4999999999999996,0.0,0.0]},{"direction":[0.4999999999999999,0.0,0.8660254037844387],"kind":"Line","origin":[2.066987298107781,0.0,0.24999999999999956]},{"direction":[0.8660254037844387,0.0,-0.49999999999999994],"kind":"Line","origin":[3.1169872981077806,0.0,2.068653347947321]},{"direction":[-0.5000000000000001,0.0,-0.8660254037844386],"kind":"Line","origin":[3.983012701892219,0.0,1.5686533479473213]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.49999999999999967,0.0,0.8660254037844388],"kind":"Line","origin":[3.077350269189626,0.3,2.220446049250313e-16]},{"direction":[-0.8660254037844387,0.0,0.4999999999999998],"kind":"Line","origin":[3.9830127018922186,0.3,1.5686533479473206]},{"direction":[-0.4999999999999999,0.0,-0.8660254037844387],"kind":"Line","origin":[3.11698729810778,0.3,2.06865334794732]},{"direction":[0.8660254037844383,0.0,-0.5000000000000008],"kind":"Line","origin":[2.0669872981077813,0.3,0.25000000000000017]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.9830127018922195,0.3,1.568653347947321]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.11698729810778,0.0,2.0686533479473215]},{"direction":[0.0,-1.0,-1.850371707708594e-16],"kind":"Line","origin":[2.066987298107781,0.3,0.24999999999999997]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,-2.2204460492503135e-17],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[1.4802973661668753e-15,1.0],"kind":"Line2","origin":[2.4999999999999996,-0.30000000000000004]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.0773502691896253,-0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.0773502691896253,-0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[-0.8660254037844387,0.49999999999999983],"kind":"Line2","origin":[2.4999999999999996,0.0]},{"direction":[0.4999999999999999,0.8660254037844387],"kind":"Line2","origin":[2.066987298107781,0.24999999999999956]},{"direction":[0.8660254037844387,-0.49999999999999994],"kind":"Line2","origin":[3.1169872981077806,2.068653347947321]},{"direction":[-0.5000000000000001,-0.8660254037844386],"kind":"Line2","origin":[3.983012701892219,1.5686533479473213]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.922649730810375,0.0]},{"direction":[-0.49999999999999967,0.8660254037844388],"kind":"Line2","origin":[6.922649730810374,2.220446049250313e-16]},{"direction":[0.8660254037844387,0.4999999999999998],"kind":"Line2","origin":[6.016987298107781,1.5686533479473206]},{"direction":[0.4999999999999999,-0.8660254037844387],"kind":"Line2","origin":[6.88301270189222,2.06865334794732]},{"direction":[-0.8660254037844383,-0.5000000000000008],"kind":"Line2","origin":[7.933012701892219,0.25000000000000017]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.3,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.49999999999999983,-0.15]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5000000000000003,-0.15]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.4999999999999998,0.15]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.5000000000000003,0.15]},{"direction":[9.251858538542969e-17,-1.0],"kind":"Line2","origin":[-0.4999999999999999,0.15]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[-3.845925372767128e-16,-0.15]},{"direction":[1.2819751242557092e-15,-1.0],"kind":"Line2","origin":[-3.845925372767128e-16,0.15000000000000005]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.49999999999999956,0.15]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[0.6499999999999999,-2.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.6499999999999999,-0.28867513459481337]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.35,-0.2886751345948127]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.35,-2.1]},{"direction":[-0.0,1.0],"kind":"Line2","origin":[0.35000000000000003,-2.0999999999999988]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[0.65,-2.1]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.65,3.6055550369691825e-16]},{"direction":[1.0,1.6024689053196365e-16],"kind":"Line2","origin":[0.35000000000000003,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.55,0.15,1.8186533479473213],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[-0.0,-1.0,-0.0],"z":[-0.49999999999999994,-0.0,-0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.5,0.15,0.0],"x":[0.8660254037844387,0.0,-0.49999999999999994],"y":[0.0,1.0,0.0],"z":[0.49999999999999994,0.0,0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.933012701892219,-0.35,-0.24999999999999997],"x":[0.0,1.0,0.0],"y":[-0.49999999999999994,0.0,-0.8660254037844387],"z":[-0.8660254037844387,-0.0,0.49999999999999994]},"kind":"Plane"},{"frame":{"origin":[2.066987298107781,0.65,0.24999999999999997],"x":[0.0,-1.0,0.0],"y":[-0.49999999999999994,-0.0,-0.8660254037844387],"z":[0.8660254037844387,-0.0,-0.49999999999999994]},"kind":"Plane"}]},"id":"batch-planar.single-oblique","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-9,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-9,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.4999999999999996,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-9,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":7,"id":7,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":6.922649730810375,"lo":0.0}},"halfedge":8,"id":8,"tolerance":1e-9,"twin_halfedge":25},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":9,"id":9,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":6.922649730810375,"lo":0.0}},"halfedge":10,"id":10,"tolerance":1e-9,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":11,"id":11,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":0.4999999999999993,"lo":0.0}},"halfedge":13,"id":12,"tolerance":1e-9,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":14,"id":13,"tolerance":1e-9,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.9999999999999997,"lo":0.0}},"halfedge":15,"id":14,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":1.8113248654051872,"lo":0.0}},"halfedge":16,"id":15,"tolerance":1e-9,"twin_halfedge":49},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":18,"id":16,"tolerance":1e-9,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":20,"id":17,"tolerance":1e-9,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":22,"id":18,"tolerance":1e-9,"twin_halfedge":33},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":1.811324865405186,"lo":0.0}},"halfedge":26,"id":19,"tolerance":1e-9,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.9999999999999996,"lo":0.0}},"halfedge":27,"id":20,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":2.0999999999999983,"lo":0.0}},"halfedge":28,"id":21,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":0.49999999999999956,"lo":0.0}},"halfedge":29,"id":22,"tolerance":1e-9,"twin_halfedge":45},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":31,"id":23,"tolerance":1e-9,"twin_halfedge":37},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":39,"id":24,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":41,"id":25,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":42,"id":26,"tolerance":1e-9,"twin_halfedge":53}],"faces":[{"id":0,"key":"planar-host-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"oblique-host","entity":"oblique-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"planar-host-1-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"oblique-host","entity":"oblique-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.30000000100000007}]}},{"id":2,"key":"planar-host-1-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"oblique-host","entity":"oblique-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":10.000000001,"lo":3.077350268189625},{"hi":1e-9,"lo":-0.300000001}]}},{"id":3,"key":"planar-host-2-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"oblique-host","entity":"oblique-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"planar-host-3-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"oblique-host","entity":"oblique-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"planar-host-4-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"oblique-host","entity":"oblique-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":6,"key":"planar-host-5-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"oblique-host","entity":"oblique-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":7,"key":"planar-cut-0-7","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"first-oblique","entity":"first-oblique","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":0.5000000010000003,"lo":-0.5000000010000003},{"hi":0.15000000100000002,"lo":-0.15000000100000005}]}},{"id":8,"key":"planar-cut-1-8","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"first-oblique","entity":"first-oblique","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":1e-9,"lo":-0.5000000009999999},{"hi":0.150000001,"lo":-0.15000000100000002}]}},{"id":9,"key":"planar-cut-3-9","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"first-oblique","entity":"first-oblique","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":0.6500000009999999,"lo":0.34999999899999995},{"hi":-0.2886751335948122,"lo":-2.100000001}]}},{"id":10,"key":"planar-cut-5-10","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"first-oblique","entity":"first-oblique","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":0.6500000010000001,"lo":0.349999999},{"hi":1e-9,"lo":-2.1000000009999997}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":19,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":23,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":32,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":36,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":34,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":6,"twin":30,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":7,"twin":44,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":12,"wire_ref":null},{"edge":8,"face":2,"from":8,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":9,"twin":25,"wire_ref":null},{"edge":9,"face":2,"from":9,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":10,"twin":21,"wire_ref":null},{"edge":10,"face":2,"from":10,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":11,"twin":17,"wire_ref":null},{"edge":11,"face":2,"from":11,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":8,"twin":48,"wire_ref":null},{"edge":7,"face":3,"from":4,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":20,"to":7,"twin":7,"wire_ref":null},{"edge":12,"face":3,"from":7,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":12,"twin":43,"wire_ref":null},{"edge":13,"face":3,"from":12,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":13,"twin":52,"wire_ref":null},{"edge":14,"face":3,"from":13,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":3,"next":16,"prev":14,"to":14,"twin":40,"wire_ref":null},{"edge":15,"face":3,"from":14,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":3,"next":17,"prev":15,"to":11,"twin":49,"wire_ref":null},{"edge":10,"face":3,"from":11,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":3,"next":18,"prev":16,"to":10,"twin":10,"wire_ref":null},{"edge":16,"face":3,"from":10,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":3,"next":19,"prev":17,"to":1,"twin":24,"wire_ref":null},{"edge":0,"face":3,"from":1,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":3,"next":20,"prev":18,"to":0,"twin":0,"wire_ref":null},{"edge":17,"face":3,"from":0,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":3,"next":12,"prev":19,"to":4,"twin":35,"wire_ref":null},{"edge":9,"face":4,"from":10,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Reverse"},"id":21,"loop_ref":4,"next":22,"prev":24,"to":9,"twin":9,"wire_ref":null},{"edge":18,"face":4,"from":9,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Forward"},"id":22,"loop_ref":4,"next":23,"prev":21,"to":2,"twin":33,"wire_ref":null},{"edge":1,"face":4,"from":2,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":4,"next":24,"prev":22,"to":1,"twin":1,"wire_ref":null},{"edge":16,"face":4,"from":1,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Reverse"},"id":24,"loop_ref":4,"next":21,"prev":23,"to":10,"twin":18,"wire_ref":null},{"edge":8,"face":5,"from":9,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":5,"next":26,"prev":33,"to":8,"twin":8,"wire_ref":null},{"edge":19,"face":5,"from":8,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Forward"},"id":26,"loop_ref":5,"next":27,"prev":25,"to":15,"twin":47,"wire_ref":null},{"edge":20,"face":5,"from":15,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":5,"next":28,"prev":26,"to":16,"twin":38,"wire_ref":null},{"edge":21,"face":5,"from":16,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":5,"next":29,"prev":27,"to":17,"twin":50,"wire_ref":null},{"edge":22,"face":5,"from":17,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":5,"next":30,"prev":28,"to":6,"twin":45,"wire_ref":null},{"edge":5,"face":5,"from":6,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":5,"next":31,"prev":29,"to":5,"twin":5,"wire_ref":null},{"edge":23,"face":5,"from":5,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Forward"},"id":31,"loop_ref":5,"next":32,"prev":30,"to":3,"twin":37,"wire_ref":null},{"edge":2,"face":5,"from":3,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Reverse"},"id":32,"loop_ref":5,"next":33,"prev":31,"to":2,"twin":2,"wire_ref":null},{"edge":18,"face":5,"from":2,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":5,"next":25,"prev":32,"to":9,"twin":22,"wire_ref":null},{"edge":4,"face":6,"from":5,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":6,"next":35,"prev":37,"to":4,"twin":4,"wire_ref":null},{"edge":17,"face":6,"from":4,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":6,"next":36,"prev":34,"to":0,"twin":20,"wire_ref":null},{"edge":3,"face":6,"from":0,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Reverse"},"id":36,"loop_ref":6,"next":37,"prev":35,"to":3,"twin":3,"wire_ref":null},{"edge":23,"face":6,"from":3,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":6,"next":34,"prev":36,"to":5,"twin":31,"wire_ref":null},{"edge":20,"face":7,"from":16,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":7,"next":39,"prev":41,"to":15,"twin":27,"wire_ref":null},{"edge":24,"face":7,"from":15,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":7,"next":40,"prev":38,"to":14,"twin":46,"wire_ref":null},{"edge":14,"face":7,"from":14,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Reverse"},"id":40,"loop_ref":7,"next":41,"prev":39,"to":13,"twin":15,"wire_ref":null},{"edge":25,"face":7,"from":13,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":7,"next":38,"prev":40,"to":16,"twin":51,"wire_ref":null},{"edge":26,"face":8,"from":17,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Forward"},"id":42,"loop_ref":8,"next":43,"prev":45,"to":12,"twin":53,"wire_ref":null},{"edge":12,"face":8,"from":12,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":8,"next":44,"prev":42,"to":7,"twin":13,"wire_ref":null},{"edge":6,"face":8,"from":7,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Reverse"},"id":44,"loop_ref":8,"next":45,"prev":43,"to":6,"twin":6,"wire_ref":null},{"edge":22,"face":8,"from":6,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Reverse"},"id":45,"loop_ref":8,"next":42,"prev":44,"to":17,"twin":29,"wire_ref":null},{"edge":24,"face":9,"from":14,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":9,"next":47,"prev":49,"to":15,"twin":39,"wire_ref":null},{"edge":19,"face":9,"from":15,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":9,"next":48,"prev":46,"to":8,"twin":26,"wire_ref":null},{"edge":11,"face":9,"from":8,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Reverse"},"id":48,"loop_ref":9,"next":49,"prev":47,"to":11,"twin":11,"wire_ref":null},{"edge":15,"face":9,"from":11,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Reverse"},"id":49,"loop_ref":9,"next":46,"prev":48,"to":14,"twin":16,"wire_ref":null},{"edge":21,"face":10,"from":17,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":10,"next":51,"prev":53,"to":16,"twin":28,"wire_ref":null},{"edge":25,"face":10,"from":16,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":10,"next":52,"prev":50,"to":13,"twin":41,"wire_ref":null},{"edge":13,"face":10,"from":13,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Reverse"},"id":52,"loop_ref":10,"next":53,"prev":51,"to":12,"twin":14,"wire_ref":null},{"edge":26,"face":10,"from":12,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Reverse"},"id":53,"loop_ref":10,"next":50,"prev":52,"to":17,"twin":42,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":21},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":25},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":34},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":38},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":42},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":46},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":50}],"shells":[{"faces":[0,3,4,5,6,1,8,10,7,9,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":1,"position":[10.0,0.0,3.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":2,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":6,"position":[2.4999999999999996,0.30000000000000004,0.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":7,"position":[2.5,0.0,0.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":8,"position":[3.0773502691896253,0.3,0.0],"tolerance":1e-9},{"id":9,"outgoing_halfedge":9,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":10,"outgoing_halfedge":10,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":11,"outgoing_halfedge":11,"position":[3.0773502691896253,0.0,0.0],"tolerance":1e-9},{"id":12,"outgoing_halfedge":14,"position":[2.066987298107781,0.0,0.24999999999999956],"tolerance":1e-9},{"id":13,"outgoing_halfedge":15,"position":[3.1169872981077806,0.0,2.068653347947321],"tolerance":1e-9},{"id":14,"outgoing_halfedge":16,"position":[3.983012701892219,0.0,1.5686533479473213],"tolerance":1e-9},{"id":15,"outgoing_halfedge":27,"position":[3.9830127018922186,0.3,1.5686533479473206],"tolerance":1e-9},{"id":16,"outgoing_halfedge":28,"position":[3.11698729810778,0.3,2.06865334794732],"tolerance":1e-9},{"id":17,"outgoing_halfedge":29,"position":[2.0669872981077813,0.3,0.25000000000000017],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.ten-flush-openings.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.ten-flush-openings.json new file mode 100644 index 0000000..c4a9b85 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.ten-flush-openings.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.5,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.5,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.5,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"opening-0","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-0","entity":"opening-0","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.500000001,"lo":0.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-0","entity":"opening-0","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.500000001,"lo":0.999999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-0","entity":"opening-0","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-0","entity":"opening-0","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-0","entity":"opening-0","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-0","entity":"opening-0","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.5,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.5,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.0,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.5,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.5,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.0,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.8,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.8,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.8,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.3,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.3,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.3,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.3,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.3,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.3,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.8,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.8,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.8,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.8,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.3,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.3,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.8,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.8,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.3,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.3,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.8,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.8,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.3,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.3,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.8,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"opening-1","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-1","entity":"opening-1","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.300000001,"lo":2.7999999989999997},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-1","entity":"opening-1","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.300000001,"lo":2.7999999989999997},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-1","entity":"opening-1","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-1","entity":"opening-1","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-1","entity":"opening-1","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-1","entity":"opening-1","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.8,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.3,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.3,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.8,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.8,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.3,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.3,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.8,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.6,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.6,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.6,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.1,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.1,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.1,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[5.1,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[5.1,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.1,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.6,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.6,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.6,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.6,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.1,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.1,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.6,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.6,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.1,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.1,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.6,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.6,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[5.1,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[5.1,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.6,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"opening-2","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-2","entity":"opening-2","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":5.100000001,"lo":4.599999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-2","entity":"opening-2","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":5.100000001,"lo":4.599999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-2","entity":"opening-2","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-2","entity":"opening-2","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-2","entity":"opening-2","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-2","entity":"opening-2","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.6,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[5.1,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[5.1,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.6,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.6,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[5.1,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[5.1,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.6,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.4,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.4,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.4,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.9,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.9,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.9,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.9,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.9,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.9,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.4,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.4,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.4,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[6.4,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.9,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.9,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.4,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.4,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.9,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.9,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[6.4,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[6.4,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.9,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.9,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.4,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"opening-3","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-3","entity":"opening-3","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.900000001,"lo":6.399999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-3","entity":"opening-3","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.900000001,"lo":6.399999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-3","entity":"opening-3","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-3","entity":"opening-3","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-3","entity":"opening-3","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-3","entity":"opening-3","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[6.4,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.9,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.9,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[6.4,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[6.4,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.9,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.9,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[6.4,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.2,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.2,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.2,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[8.7,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[8.7,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.7,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[8.7,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[8.7,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.7,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[8.2,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[8.2,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.2,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[8.2,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.7,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.7,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.2,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.2,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.7,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.7,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[8.2,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[8.2,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[8.7,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[8.7,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[8.2,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"opening-4","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-4","entity":"opening-4","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":8.700000001,"lo":8.199999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-4","entity":"opening-4","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":8.700000001,"lo":8.199999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-4","entity":"opening-4","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-4","entity":"opening-4","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-4","entity":"opening-4","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-4","entity":"opening-4","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[8.2,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[8.7,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[8.7,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[8.2,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[8.2,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[8.7,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[8.7,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[8.2,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.5,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.5,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.5,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.5,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.5,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[10.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.5,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.5,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.5,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.5,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.5,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.5,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"opening-5","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-5","entity":"opening-5","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":10.500000001,"lo":9.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-5","entity":"opening-5","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":10.500000001,"lo":9.999999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-5","entity":"opening-5","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-5","entity":"opening-5","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-5","entity":"opening-5","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-5","entity":"opening-5","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[10.5,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[10.5,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[10.0,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[10.5,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[10.5,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[10.0,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.8,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.8,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[11.8,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[12.3,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[12.3,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[12.3,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[12.3,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[12.3,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[12.3,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[11.8,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[11.8,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[11.8,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[11.8,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[12.3,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[12.3,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.8,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.8,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[12.3,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.3,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[11.8,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[11.8,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[12.3,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[12.3,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[11.8,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"opening-6","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-6","entity":"opening-6","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":12.300000001,"lo":11.799999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-6","entity":"opening-6","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":12.300000001,"lo":11.799999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-6","entity":"opening-6","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-6","entity":"opening-6","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-6","entity":"opening-6","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-6","entity":"opening-6","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[11.8,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[12.3,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[12.3,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[11.8,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[11.8,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[12.3,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[12.3,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[11.8,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.6,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.6,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[13.6,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[14.1,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[14.1,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[14.1,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[14.1,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[14.1,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[14.1,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[13.6,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[13.6,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[13.6,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[13.6,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.1,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.1,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.6,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.6,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.1,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.1,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[13.6,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[13.6,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[14.1,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[14.1,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[13.6,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"opening-7","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-7","entity":"opening-7","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":14.100000001,"lo":13.599999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-7","entity":"opening-7","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":14.100000001,"lo":13.599999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-7","entity":"opening-7","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-7","entity":"opening-7","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-7","entity":"opening-7","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-7","entity":"opening-7","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[13.6,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[14.1,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[14.1,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[13.6,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[13.6,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[14.1,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[14.1,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[13.6,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.4,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.4,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.4,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.9,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.9,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.9,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[15.9,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[15.9,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.9,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[15.4,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[15.4,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.4,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[15.4,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.9,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[15.9,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.4,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.4,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[15.9,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.9,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[15.4,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[15.4,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[15.9,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[15.9,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[15.4,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"opening-8","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-8","entity":"opening-8","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":15.900000001,"lo":15.399999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-8","entity":"opening-8","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":15.900000001,"lo":15.399999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-8","entity":"opening-8","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-8","entity":"opening-8","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-8","entity":"opening-8","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-8","entity":"opening-8","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[15.4,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[15.9,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[15.9,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[15.4,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[15.4,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[15.9,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[15.9,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[15.4,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.2,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.2,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.2,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.7,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.7,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.7,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[17.7,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[17.7,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.7,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[17.2,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[17.2,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.2,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[17.2,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.7,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[17.7,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.2,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.2,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[17.7,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.7,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[17.2,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[17.2,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[17.7,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[17.7,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[17.2,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"opening-9","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-9","entity":"opening-9","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":17.700000001,"lo":17.199999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-9","entity":"opening-9","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":17.700000001,"lo":17.199999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-9","entity":"opening-9","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-9","entity":"opening-9","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-9","entity":"opening-9","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening-9","entity":"opening-9","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[17.2,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[17.7,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[17.7,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[17.2,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[17.2,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[17.7,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[17.7,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[17.2,0.3,2.1],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","boolean_rectilinear"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[20.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[20.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[20.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[20.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[20.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[20.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":20.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":20.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":20.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":20.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":20.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":20.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":20.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":20.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[20.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[20.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[20.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[20.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.8,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.8,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.8,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.8,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.8,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.8,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.8,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.3,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.8,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.8,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.8,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.3,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.8,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.3,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.3,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.3,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.3,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.3,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.6,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.6,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.6,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.6,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.3,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.3,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.3,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.3,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.6,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.3,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.3,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.6,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.6,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.1,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.6,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.6,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.6,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.1,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.6,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.1,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.1,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.1,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.1,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.1,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.4,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.4,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.4,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.4,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.1,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.1,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.1,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.1,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.4,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.1,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.1,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.4,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.4,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.9,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.4,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.4,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.4,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.9,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.4,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.9,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.9,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.9,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.9,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.9,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[8.2,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.2,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[8.2,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.2,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.9,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.9,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.9,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.9,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.2,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.9,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[6.9,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.2,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[8.2,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.7,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.2,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.2,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.2,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.7,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.2,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[8.7,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[8.7,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.7,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[8.7,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[8.7,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.7,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.7,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.7,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.7,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.7,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[8.7,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.5,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.5,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.5,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.5,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.5,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.5,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[11.8,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[11.8,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[11.8,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[11.8,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.5,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.5,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.5,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[11.8,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.5,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[10.5,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[11.8,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[11.8,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[12.3,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.8,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.8,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.8,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[12.3,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[11.8,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[12.3,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[12.3,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[12.3,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[12.3,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[12.3,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[13.6,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[13.6,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[13.6,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[13.6,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.3,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.3,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.3,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.3,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[13.6,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.3,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[12.3,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[13.6,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[13.6,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[14.1,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.6,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.6,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.6,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[14.1,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[13.6,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[14.1,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[14.1,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[14.1,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[14.1,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[14.1,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.4,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.4,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.4,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.4,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.1,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.1,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.1,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.1,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.4,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.1,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[14.1,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.4,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.4,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.9,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.4,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.4,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.4,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.9,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.4,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.9,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.9,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.9,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[15.9,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[15.9,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.2,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.2,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.2,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.2,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.9,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.9,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.9,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.9,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.2,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.9,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[15.9,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.2,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.2,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.7,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.2,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.2,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.2,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.7,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.2,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.7,0.0,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.7,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.7,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[17.7,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[17.7,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[20.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[20.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[20.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[20.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.7,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.7,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.7,0.3,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.7,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[20.0,0.3,2.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[20.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[20.0,0.0,2.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.7,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[17.7,0.3,3.0]}],"intersections":[],"pcurves":[{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[19.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.0,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[19.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[20.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[19.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[19.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[18.5,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[19.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[18.5,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[18.5,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.2,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[18.5,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.5,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.8,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.5,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.5,-0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.8,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[18.5,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[18.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.2,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[18.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.3,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.8,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.8,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.8,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.2,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[17.2,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[16.7,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[17.2,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.8,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.8,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.8,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.8,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[16.7,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[16.7,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.4,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[16.7,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.3,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.6,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.3,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.3,-0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.6,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.3,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.3,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[16.7,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[16.7,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.4,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[16.7,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.3,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.1,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.6,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.6,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.6,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.4,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[15.4,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.9,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[15.4,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.6,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.1,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.6,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.6,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.1,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[4.6,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.1,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.1,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.1,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.9,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.9,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.6,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.9,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[5.1,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.4,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[5.1,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.1,-0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.4,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.1,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.1,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.1,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.9,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.9,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.6,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[14.9,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.1,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.1,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.1,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.9,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.4,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.4,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.4,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.6,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.6,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.1,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.6,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.4,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.9,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.4,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.4,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.9,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.4,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.9,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.9,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.9,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.1,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.1,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.8,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.1,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[6.9,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.2,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[6.9,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.9,-0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.2,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.9,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.9,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.9,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.1,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.1,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.8,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[13.1,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.9,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[6.9,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.9,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.7,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.2,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.2,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.2,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.8,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.8,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.3,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.8,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.2,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.7,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.2,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.2,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.7,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.2,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.7,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.7,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.7,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.3,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.3,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[8.7,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[8.7,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.7,-0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.7,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.7,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.7,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.3,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.3,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[11.3,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.7,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[8.7,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.7,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[9.5,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.5,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[9.5,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[9.5,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.2,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[9.5,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[10.5,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[11.8,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[10.5,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.5,-0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.8,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.5,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[9.5,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[9.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.2,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[9.5,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[10.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[12.3,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.8,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.8,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.8,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.2,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.2,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.699999999999999,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[8.2,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.8,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[12.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.8,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.8,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[12.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[11.8,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[11.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[12.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.699999999999999,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.699999999999999,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.4,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.699999999999999,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[12.3,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[13.6,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[12.3,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[12.3,-0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.6,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.3,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[12.3,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.699999999999999,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.699999999999999,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.4,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[7.699999999999999,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[12.3,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[12.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.1,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.6,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.6,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.6,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.4,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.4,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.9,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.4,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.6,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.1,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.6,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.6,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.1,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[13.6,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[13.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.1,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.1,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.1,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.9,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.9,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.6,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.9,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[14.1,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.4,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[14.1,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[14.1,-0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.4,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.1,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.1,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.1,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.9,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.9,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.6,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.9,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.1,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[14.1,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[14.1,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.9,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.4,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.4,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.4,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.6,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.6,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.1,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.6,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.4,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.9,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.4,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.4,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.9,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.4,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.4,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.9,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.9,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.9,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.1,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.1,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.8000000000000007,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.1,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[15.9,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.2,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[15.9,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[15.9,-0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.2,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.9,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.9,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.9,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.1,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.1,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.8000000000000007,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.1,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.9,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[15.9,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[15.9,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.7,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.2,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.2,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.2,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.8000000000000007,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.8000000000000007,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.3000000000000007,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.8000000000000007,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.2,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.7,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.2,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.2,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.7,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.2,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.7,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.7,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.7,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.3000000000000007,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.3000000000000007,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.3000000000000007,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[17.7,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[20.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[17.7,-0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[17.7,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.0,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.7,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.7,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.7,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.3000000000000007,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.3000000000000007,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.3000000000000007,2.1]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.7,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[20.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[17.7,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[17.7,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.8,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.3,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.6,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[5.1,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.4,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[6.9,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[8.2,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[8.7,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[10.5,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[11.8,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[12.3,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[13.6,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[14.1,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[15.4,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[15.9,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[17.2,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[17.7,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[20.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"}]},"id":"batch-planar.ten-flush-openings","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-9,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-9,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":7,"id":7,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-9,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":11,"id":9,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-9,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":20,"id":12,"tolerance":1e-9,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":21,"id":13,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":23,"id":14,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":24,"id":15,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":25,"id":16,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":29,"id":17,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":30,"id":18,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":33,"id":19,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":36,"id":20,"tolerance":1e-9,"twin_halfedge":74},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":37,"id":21,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":39,"id":22,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":41,"id":23,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":42,"id":24,"tolerance":1e-9,"twin_halfedge":76},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":43,"id":25,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":45,"id":26,"tolerance":1e-9,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":49,"id":27,"tolerance":1e-9,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":53,"id":28,"tolerance":1e-9,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":54,"id":29,"tolerance":1e-9,"twin_halfedge":71},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":55,"id":30,"tolerance":1e-9,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":56,"id":31,"tolerance":1e-9,"twin_halfedge":69},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":57,"id":32,"tolerance":1e-9,"twin_halfedge":66},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":58,"id":33,"tolerance":1e-9,"twin_halfedge":95},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":59,"id":34,"tolerance":1e-9,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":61,"id":35,"tolerance":1e-9,"twin_halfedge":75},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":63,"id":36,"tolerance":1e-9,"twin_halfedge":70},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":65,"id":37,"tolerance":1e-9,"twin_halfedge":79},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":67,"id":38,"tolerance":1e-9,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":72,"id":39,"tolerance":1e-9,"twin_halfedge":86},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":73,"id":40,"tolerance":1e-9,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":77,"id":41,"tolerance":1e-9,"twin_halfedge":82},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":78,"id":42,"tolerance":1e-9,"twin_halfedge":88},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":81,"id":43,"tolerance":1e-9,"twin_halfedge":99},{"chart_seam":false,"geometry":{"curve":44,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":84,"id":44,"tolerance":1e-9,"twin_halfedge":122},{"chart_seam":false,"geometry":{"curve":45,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":85,"id":45,"tolerance":1e-9,"twin_halfedge":96},{"chart_seam":false,"geometry":{"curve":46,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":87,"id":46,"tolerance":1e-9,"twin_halfedge":94},{"chart_seam":false,"geometry":{"curve":47,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":89,"id":47,"tolerance":1e-9,"twin_halfedge":98},{"chart_seam":false,"geometry":{"curve":48,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":90,"id":48,"tolerance":1e-9,"twin_halfedge":124},{"chart_seam":false,"geometry":{"curve":49,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":91,"id":49,"tolerance":1e-9,"twin_halfedge":92},{"chart_seam":false,"geometry":{"curve":50,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":93,"id":50,"tolerance":1e-9,"twin_halfedge":100},{"chart_seam":false,"geometry":{"curve":51,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":97,"id":51,"tolerance":1e-9,"twin_halfedge":131},{"chart_seam":false,"geometry":{"curve":52,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":101,"id":52,"tolerance":1e-9,"twin_halfedge":112},{"chart_seam":false,"geometry":{"curve":53,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":102,"id":53,"tolerance":1e-9,"twin_halfedge":119},{"chart_seam":false,"geometry":{"curve":54,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":103,"id":54,"tolerance":1e-9,"twin_halfedge":110},{"chart_seam":false,"geometry":{"curve":55,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":104,"id":55,"tolerance":1e-9,"twin_halfedge":117},{"chart_seam":false,"geometry":{"curve":56,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":105,"id":56,"tolerance":1e-9,"twin_halfedge":114},{"chart_seam":false,"geometry":{"curve":57,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":106,"id":57,"tolerance":1e-9,"twin_halfedge":143},{"chart_seam":false,"geometry":{"curve":58,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":107,"id":58,"tolerance":1e-9,"twin_halfedge":108},{"chart_seam":false,"geometry":{"curve":59,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":109,"id":59,"tolerance":1e-9,"twin_halfedge":123},{"chart_seam":false,"geometry":{"curve":60,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":111,"id":60,"tolerance":1e-9,"twin_halfedge":118},{"chart_seam":false,"geometry":{"curve":61,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":113,"id":61,"tolerance":1e-9,"twin_halfedge":127},{"chart_seam":false,"geometry":{"curve":62,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":115,"id":62,"tolerance":1e-9,"twin_halfedge":116},{"chart_seam":false,"geometry":{"curve":63,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":120,"id":63,"tolerance":1e-9,"twin_halfedge":134},{"chart_seam":false,"geometry":{"curve":64,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":121,"id":64,"tolerance":1e-9,"twin_halfedge":128},{"chart_seam":false,"geometry":{"curve":65,"kind":"Curve","range":{"hi":1.2999999999999998,"lo":0.0}},"halfedge":125,"id":65,"tolerance":1e-9,"twin_halfedge":130},{"chart_seam":false,"geometry":{"curve":66,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":126,"id":66,"tolerance":1e-9,"twin_halfedge":136},{"chart_seam":false,"geometry":{"curve":67,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":129,"id":67,"tolerance":1e-9,"twin_halfedge":147},{"chart_seam":false,"geometry":{"curve":68,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":132,"id":68,"tolerance":1e-9,"twin_halfedge":170},{"chart_seam":false,"geometry":{"curve":69,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":133,"id":69,"tolerance":1e-9,"twin_halfedge":144},{"chart_seam":false,"geometry":{"curve":70,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":135,"id":70,"tolerance":1e-9,"twin_halfedge":142},{"chart_seam":false,"geometry":{"curve":71,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":137,"id":71,"tolerance":1e-9,"twin_halfedge":146},{"chart_seam":false,"geometry":{"curve":72,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":138,"id":72,"tolerance":1e-9,"twin_halfedge":172},{"chart_seam":false,"geometry":{"curve":73,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":139,"id":73,"tolerance":1e-9,"twin_halfedge":140},{"chart_seam":false,"geometry":{"curve":74,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":141,"id":74,"tolerance":1e-9,"twin_halfedge":148},{"chart_seam":false,"geometry":{"curve":75,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":145,"id":75,"tolerance":1e-9,"twin_halfedge":179},{"chart_seam":false,"geometry":{"curve":76,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":149,"id":76,"tolerance":1e-9,"twin_halfedge":160},{"chart_seam":false,"geometry":{"curve":77,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":150,"id":77,"tolerance":1e-9,"twin_halfedge":167},{"chart_seam":false,"geometry":{"curve":78,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":151,"id":78,"tolerance":1e-9,"twin_halfedge":158},{"chart_seam":false,"geometry":{"curve":79,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":152,"id":79,"tolerance":1e-9,"twin_halfedge":165},{"chart_seam":false,"geometry":{"curve":80,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":153,"id":80,"tolerance":1e-9,"twin_halfedge":162},{"chart_seam":false,"geometry":{"curve":81,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":154,"id":81,"tolerance":1e-9,"twin_halfedge":191},{"chart_seam":false,"geometry":{"curve":82,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":155,"id":82,"tolerance":1e-9,"twin_halfedge":156},{"chart_seam":false,"geometry":{"curve":83,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":157,"id":83,"tolerance":1e-9,"twin_halfedge":171},{"chart_seam":false,"geometry":{"curve":84,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":159,"id":84,"tolerance":1e-9,"twin_halfedge":166},{"chart_seam":false,"geometry":{"curve":85,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":161,"id":85,"tolerance":1e-9,"twin_halfedge":175},{"chart_seam":false,"geometry":{"curve":86,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":163,"id":86,"tolerance":1e-9,"twin_halfedge":164},{"chart_seam":false,"geometry":{"curve":87,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":168,"id":87,"tolerance":1e-9,"twin_halfedge":182},{"chart_seam":false,"geometry":{"curve":88,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":169,"id":88,"tolerance":1e-9,"twin_halfedge":176},{"chart_seam":false,"geometry":{"curve":89,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":173,"id":89,"tolerance":1e-9,"twin_halfedge":178},{"chart_seam":false,"geometry":{"curve":90,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":174,"id":90,"tolerance":1e-9,"twin_halfedge":184},{"chart_seam":false,"geometry":{"curve":91,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":177,"id":91,"tolerance":1e-9,"twin_halfedge":195},{"chart_seam":false,"geometry":{"curve":92,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":180,"id":92,"tolerance":1e-9,"twin_halfedge":218},{"chart_seam":false,"geometry":{"curve":93,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":181,"id":93,"tolerance":1e-9,"twin_halfedge":192},{"chart_seam":false,"geometry":{"curve":94,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":183,"id":94,"tolerance":1e-9,"twin_halfedge":190},{"chart_seam":false,"geometry":{"curve":95,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":185,"id":95,"tolerance":1e-9,"twin_halfedge":194},{"chart_seam":false,"geometry":{"curve":96,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":186,"id":96,"tolerance":1e-9,"twin_halfedge":220},{"chart_seam":false,"geometry":{"curve":97,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":187,"id":97,"tolerance":1e-9,"twin_halfedge":188},{"chart_seam":false,"geometry":{"curve":98,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":189,"id":98,"tolerance":1e-9,"twin_halfedge":196},{"chart_seam":false,"geometry":{"curve":99,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":193,"id":99,"tolerance":1e-9,"twin_halfedge":227},{"chart_seam":false,"geometry":{"curve":100,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":197,"id":100,"tolerance":1e-9,"twin_halfedge":208},{"chart_seam":false,"geometry":{"curve":101,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":198,"id":101,"tolerance":1e-9,"twin_halfedge":215},{"chart_seam":false,"geometry":{"curve":102,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":199,"id":102,"tolerance":1e-9,"twin_halfedge":206},{"chart_seam":false,"geometry":{"curve":103,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":200,"id":103,"tolerance":1e-9,"twin_halfedge":213},{"chart_seam":false,"geometry":{"curve":104,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":201,"id":104,"tolerance":1e-9,"twin_halfedge":210},{"chart_seam":false,"geometry":{"curve":105,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":202,"id":105,"tolerance":1e-9,"twin_halfedge":239},{"chart_seam":false,"geometry":{"curve":106,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":203,"id":106,"tolerance":1e-9,"twin_halfedge":204},{"chart_seam":false,"geometry":{"curve":107,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":205,"id":107,"tolerance":1e-9,"twin_halfedge":219},{"chart_seam":false,"geometry":{"curve":108,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":207,"id":108,"tolerance":1e-9,"twin_halfedge":214},{"chart_seam":false,"geometry":{"curve":109,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":209,"id":109,"tolerance":1e-9,"twin_halfedge":223},{"chart_seam":false,"geometry":{"curve":110,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":211,"id":110,"tolerance":1e-9,"twin_halfedge":212},{"chart_seam":false,"geometry":{"curve":111,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":216,"id":111,"tolerance":1e-9,"twin_halfedge":230},{"chart_seam":false,"geometry":{"curve":112,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":217,"id":112,"tolerance":1e-9,"twin_halfedge":224},{"chart_seam":false,"geometry":{"curve":113,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":221,"id":113,"tolerance":1e-9,"twin_halfedge":226},{"chart_seam":false,"geometry":{"curve":114,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":222,"id":114,"tolerance":1e-9,"twin_halfedge":232},{"chart_seam":false,"geometry":{"curve":115,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":225,"id":115,"tolerance":1e-9,"twin_halfedge":243},{"chart_seam":false,"geometry":{"curve":116,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":228,"id":116,"tolerance":1e-9,"twin_halfedge":266},{"chart_seam":false,"geometry":{"curve":117,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":229,"id":117,"tolerance":1e-9,"twin_halfedge":240},{"chart_seam":false,"geometry":{"curve":118,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":231,"id":118,"tolerance":1e-9,"twin_halfedge":238},{"chart_seam":false,"geometry":{"curve":119,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":233,"id":119,"tolerance":1e-9,"twin_halfedge":242},{"chart_seam":false,"geometry":{"curve":120,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":234,"id":120,"tolerance":1e-9,"twin_halfedge":268},{"chart_seam":false,"geometry":{"curve":121,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":235,"id":121,"tolerance":1e-9,"twin_halfedge":236},{"chart_seam":false,"geometry":{"curve":122,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":237,"id":122,"tolerance":1e-9,"twin_halfedge":244},{"chart_seam":false,"geometry":{"curve":123,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":241,"id":123,"tolerance":1e-9,"twin_halfedge":275},{"chart_seam":false,"geometry":{"curve":124,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":245,"id":124,"tolerance":1e-9,"twin_halfedge":256},{"chart_seam":false,"geometry":{"curve":125,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":246,"id":125,"tolerance":1e-9,"twin_halfedge":263},{"chart_seam":false,"geometry":{"curve":126,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":247,"id":126,"tolerance":1e-9,"twin_halfedge":254},{"chart_seam":false,"geometry":{"curve":127,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":248,"id":127,"tolerance":1e-9,"twin_halfedge":261},{"chart_seam":false,"geometry":{"curve":128,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":249,"id":128,"tolerance":1e-9,"twin_halfedge":258},{"chart_seam":false,"geometry":{"curve":129,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":250,"id":129,"tolerance":1e-9,"twin_halfedge":287},{"chart_seam":false,"geometry":{"curve":130,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":251,"id":130,"tolerance":1e-9,"twin_halfedge":252},{"chart_seam":false,"geometry":{"curve":131,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":253,"id":131,"tolerance":1e-9,"twin_halfedge":267},{"chart_seam":false,"geometry":{"curve":132,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":255,"id":132,"tolerance":1e-9,"twin_halfedge":262},{"chart_seam":false,"geometry":{"curve":133,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":257,"id":133,"tolerance":1e-9,"twin_halfedge":271},{"chart_seam":false,"geometry":{"curve":134,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":259,"id":134,"tolerance":1e-9,"twin_halfedge":260},{"chart_seam":false,"geometry":{"curve":135,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":264,"id":135,"tolerance":1e-9,"twin_halfedge":278},{"chart_seam":false,"geometry":{"curve":136,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":265,"id":136,"tolerance":1e-9,"twin_halfedge":272},{"chart_seam":false,"geometry":{"curve":137,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":269,"id":137,"tolerance":1e-9,"twin_halfedge":274},{"chart_seam":false,"geometry":{"curve":138,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":270,"id":138,"tolerance":1e-9,"twin_halfedge":280},{"chart_seam":false,"geometry":{"curve":139,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":273,"id":139,"tolerance":1e-9,"twin_halfedge":291},{"chart_seam":false,"geometry":{"curve":140,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":276,"id":140,"tolerance":1e-9,"twin_halfedge":314},{"chart_seam":false,"geometry":{"curve":141,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":277,"id":141,"tolerance":1e-9,"twin_halfedge":288},{"chart_seam":false,"geometry":{"curve":142,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":279,"id":142,"tolerance":1e-9,"twin_halfedge":286},{"chart_seam":false,"geometry":{"curve":143,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":281,"id":143,"tolerance":1e-9,"twin_halfedge":290},{"chart_seam":false,"geometry":{"curve":144,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":282,"id":144,"tolerance":1e-9,"twin_halfedge":316},{"chart_seam":false,"geometry":{"curve":145,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":283,"id":145,"tolerance":1e-9,"twin_halfedge":284},{"chart_seam":false,"geometry":{"curve":146,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":285,"id":146,"tolerance":1e-9,"twin_halfedge":292},{"chart_seam":false,"geometry":{"curve":147,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":289,"id":147,"tolerance":1e-9,"twin_halfedge":323},{"chart_seam":false,"geometry":{"curve":148,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":293,"id":148,"tolerance":1e-9,"twin_halfedge":304},{"chart_seam":false,"geometry":{"curve":149,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":294,"id":149,"tolerance":1e-9,"twin_halfedge":311},{"chart_seam":false,"geometry":{"curve":150,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":295,"id":150,"tolerance":1e-9,"twin_halfedge":302},{"chart_seam":false,"geometry":{"curve":151,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":296,"id":151,"tolerance":1e-9,"twin_halfedge":309},{"chart_seam":false,"geometry":{"curve":152,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":297,"id":152,"tolerance":1e-9,"twin_halfedge":306},{"chart_seam":false,"geometry":{"curve":153,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":298,"id":153,"tolerance":1e-9,"twin_halfedge":335},{"chart_seam":false,"geometry":{"curve":154,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":299,"id":154,"tolerance":1e-9,"twin_halfedge":300},{"chart_seam":false,"geometry":{"curve":155,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":301,"id":155,"tolerance":1e-9,"twin_halfedge":315},{"chart_seam":false,"geometry":{"curve":156,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":303,"id":156,"tolerance":1e-9,"twin_halfedge":310},{"chart_seam":false,"geometry":{"curve":157,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":305,"id":157,"tolerance":1e-9,"twin_halfedge":319},{"chart_seam":false,"geometry":{"curve":158,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":307,"id":158,"tolerance":1e-9,"twin_halfedge":308},{"chart_seam":false,"geometry":{"curve":159,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":312,"id":159,"tolerance":1e-9,"twin_halfedge":326},{"chart_seam":false,"geometry":{"curve":160,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":313,"id":160,"tolerance":1e-9,"twin_halfedge":320},{"chart_seam":false,"geometry":{"curve":161,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":317,"id":161,"tolerance":1e-9,"twin_halfedge":322},{"chart_seam":false,"geometry":{"curve":162,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":318,"id":162,"tolerance":1e-9,"twin_halfedge":328},{"chart_seam":false,"geometry":{"curve":163,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":321,"id":163,"tolerance":1e-9,"twin_halfedge":339},{"chart_seam":false,"geometry":{"curve":164,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":324,"id":164,"tolerance":1e-9,"twin_halfedge":362},{"chart_seam":false,"geometry":{"curve":165,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":325,"id":165,"tolerance":1e-9,"twin_halfedge":336},{"chart_seam":false,"geometry":{"curve":166,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":327,"id":166,"tolerance":1e-9,"twin_halfedge":334},{"chart_seam":false,"geometry":{"curve":167,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":329,"id":167,"tolerance":1e-9,"twin_halfedge":338},{"chart_seam":false,"geometry":{"curve":168,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":330,"id":168,"tolerance":1e-9,"twin_halfedge":364},{"chart_seam":false,"geometry":{"curve":169,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":331,"id":169,"tolerance":1e-9,"twin_halfedge":332},{"chart_seam":false,"geometry":{"curve":170,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":333,"id":170,"tolerance":1e-9,"twin_halfedge":340},{"chart_seam":false,"geometry":{"curve":171,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":337,"id":171,"tolerance":1e-9,"twin_halfedge":371},{"chart_seam":false,"geometry":{"curve":172,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":341,"id":172,"tolerance":1e-9,"twin_halfedge":352},{"chart_seam":false,"geometry":{"curve":173,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":342,"id":173,"tolerance":1e-9,"twin_halfedge":359},{"chart_seam":false,"geometry":{"curve":174,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":343,"id":174,"tolerance":1e-9,"twin_halfedge":350},{"chart_seam":false,"geometry":{"curve":175,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":344,"id":175,"tolerance":1e-9,"twin_halfedge":357},{"chart_seam":false,"geometry":{"curve":176,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":345,"id":176,"tolerance":1e-9,"twin_halfedge":354},{"chart_seam":false,"geometry":{"curve":177,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":346,"id":177,"tolerance":1e-9,"twin_halfedge":383},{"chart_seam":false,"geometry":{"curve":178,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":347,"id":178,"tolerance":1e-9,"twin_halfedge":348},{"chart_seam":false,"geometry":{"curve":179,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":349,"id":179,"tolerance":1e-9,"twin_halfedge":363},{"chart_seam":false,"geometry":{"curve":180,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":351,"id":180,"tolerance":1e-9,"twin_halfedge":358},{"chart_seam":false,"geometry":{"curve":181,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":353,"id":181,"tolerance":1e-9,"twin_halfedge":367},{"chart_seam":false,"geometry":{"curve":182,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":355,"id":182,"tolerance":1e-9,"twin_halfedge":356},{"chart_seam":false,"geometry":{"curve":183,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":360,"id":183,"tolerance":1e-9,"twin_halfedge":374},{"chart_seam":false,"geometry":{"curve":184,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":361,"id":184,"tolerance":1e-9,"twin_halfedge":368},{"chart_seam":false,"geometry":{"curve":185,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":365,"id":185,"tolerance":1e-9,"twin_halfedge":370},{"chart_seam":false,"geometry":{"curve":186,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":366,"id":186,"tolerance":1e-9,"twin_halfedge":376},{"chart_seam":false,"geometry":{"curve":187,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":369,"id":187,"tolerance":1e-9,"twin_halfedge":387},{"chart_seam":false,"geometry":{"curve":188,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":372,"id":188,"tolerance":1e-9,"twin_halfedge":410},{"chart_seam":false,"geometry":{"curve":189,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":373,"id":189,"tolerance":1e-9,"twin_halfedge":384},{"chart_seam":false,"geometry":{"curve":190,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":375,"id":190,"tolerance":1e-9,"twin_halfedge":382},{"chart_seam":false,"geometry":{"curve":191,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":377,"id":191,"tolerance":1e-9,"twin_halfedge":386},{"chart_seam":false,"geometry":{"curve":192,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":378,"id":192,"tolerance":1e-9,"twin_halfedge":412},{"chart_seam":false,"geometry":{"curve":193,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":379,"id":193,"tolerance":1e-9,"twin_halfedge":380},{"chart_seam":false,"geometry":{"curve":194,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":381,"id":194,"tolerance":1e-9,"twin_halfedge":388},{"chart_seam":false,"geometry":{"curve":195,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":385,"id":195,"tolerance":1e-9,"twin_halfedge":419},{"chart_seam":false,"geometry":{"curve":196,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":389,"id":196,"tolerance":1e-9,"twin_halfedge":400},{"chart_seam":false,"geometry":{"curve":197,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":390,"id":197,"tolerance":1e-9,"twin_halfedge":407},{"chart_seam":false,"geometry":{"curve":198,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":391,"id":198,"tolerance":1e-9,"twin_halfedge":398},{"chart_seam":false,"geometry":{"curve":199,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":392,"id":199,"tolerance":1e-9,"twin_halfedge":405},{"chart_seam":false,"geometry":{"curve":200,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":393,"id":200,"tolerance":1e-9,"twin_halfedge":402},{"chart_seam":false,"geometry":{"curve":201,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":394,"id":201,"tolerance":1e-9,"twin_halfedge":431},{"chart_seam":false,"geometry":{"curve":202,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":395,"id":202,"tolerance":1e-9,"twin_halfedge":396},{"chart_seam":false,"geometry":{"curve":203,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":397,"id":203,"tolerance":1e-9,"twin_halfedge":411},{"chart_seam":false,"geometry":{"curve":204,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":399,"id":204,"tolerance":1e-9,"twin_halfedge":406},{"chart_seam":false,"geometry":{"curve":205,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":401,"id":205,"tolerance":1e-9,"twin_halfedge":415},{"chart_seam":false,"geometry":{"curve":206,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":403,"id":206,"tolerance":1e-9,"twin_halfedge":404},{"chart_seam":false,"geometry":{"curve":207,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":408,"id":207,"tolerance":1e-9,"twin_halfedge":422},{"chart_seam":false,"geometry":{"curve":208,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":409,"id":208,"tolerance":1e-9,"twin_halfedge":416},{"chart_seam":false,"geometry":{"curve":209,"kind":"Curve","range":{"hi":1.3000000000000007,"lo":0.0}},"halfedge":413,"id":209,"tolerance":1e-9,"twin_halfedge":418},{"chart_seam":false,"geometry":{"curve":210,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":414,"id":210,"tolerance":1e-9,"twin_halfedge":424},{"chart_seam":false,"geometry":{"curve":211,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":417,"id":211,"tolerance":1e-9,"twin_halfedge":435},{"chart_seam":false,"geometry":{"curve":212,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":420,"id":212,"tolerance":1e-9,"twin_halfedge":458},{"chart_seam":false,"geometry":{"curve":213,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":421,"id":213,"tolerance":1e-9,"twin_halfedge":432},{"chart_seam":false,"geometry":{"curve":214,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":423,"id":214,"tolerance":1e-9,"twin_halfedge":430},{"chart_seam":false,"geometry":{"curve":215,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":425,"id":215,"tolerance":1e-9,"twin_halfedge":434},{"chart_seam":false,"geometry":{"curve":216,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":426,"id":216,"tolerance":1e-9,"twin_halfedge":460},{"chart_seam":false,"geometry":{"curve":217,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":427,"id":217,"tolerance":1e-9,"twin_halfedge":428},{"chart_seam":false,"geometry":{"curve":218,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":429,"id":218,"tolerance":1e-9,"twin_halfedge":436},{"chart_seam":false,"geometry":{"curve":219,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":433,"id":219,"tolerance":1e-9,"twin_halfedge":467},{"chart_seam":false,"geometry":{"curve":220,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":437,"id":220,"tolerance":1e-9,"twin_halfedge":448},{"chart_seam":false,"geometry":{"curve":221,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":438,"id":221,"tolerance":1e-9,"twin_halfedge":455},{"chart_seam":false,"geometry":{"curve":222,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":439,"id":222,"tolerance":1e-9,"twin_halfedge":446},{"chart_seam":false,"geometry":{"curve":223,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":440,"id":223,"tolerance":1e-9,"twin_halfedge":453},{"chart_seam":false,"geometry":{"curve":224,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":441,"id":224,"tolerance":1e-9,"twin_halfedge":450},{"chart_seam":false,"geometry":{"curve":225,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":442,"id":225,"tolerance":1e-9,"twin_halfedge":479},{"chart_seam":false,"geometry":{"curve":226,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":443,"id":226,"tolerance":1e-9,"twin_halfedge":444},{"chart_seam":false,"geometry":{"curve":227,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":445,"id":227,"tolerance":1e-9,"twin_halfedge":459},{"chart_seam":false,"geometry":{"curve":228,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":447,"id":228,"tolerance":1e-9,"twin_halfedge":454},{"chart_seam":false,"geometry":{"curve":229,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":449,"id":229,"tolerance":1e-9,"twin_halfedge":463},{"chart_seam":false,"geometry":{"curve":230,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":451,"id":230,"tolerance":1e-9,"twin_halfedge":452},{"chart_seam":false,"geometry":{"curve":231,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":456,"id":231,"tolerance":1e-9,"twin_halfedge":470},{"chart_seam":false,"geometry":{"curve":232,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":457,"id":232,"tolerance":1e-9,"twin_halfedge":464},{"chart_seam":false,"geometry":{"curve":233,"kind":"Curve","range":{"hi":1.299999999999999,"lo":0.0}},"halfedge":461,"id":233,"tolerance":1e-9,"twin_halfedge":466},{"chart_seam":false,"geometry":{"curve":234,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":462,"id":234,"tolerance":1e-9,"twin_halfedge":472},{"chart_seam":false,"geometry":{"curve":235,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":465,"id":235,"tolerance":1e-9,"twin_halfedge":483},{"chart_seam":false,"geometry":{"curve":236,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":468,"id":236,"tolerance":1e-9,"twin_halfedge":510},{"chart_seam":false,"geometry":{"curve":237,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":469,"id":237,"tolerance":1e-9,"twin_halfedge":480},{"chart_seam":false,"geometry":{"curve":238,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":471,"id":238,"tolerance":1e-9,"twin_halfedge":478},{"chart_seam":false,"geometry":{"curve":239,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":473,"id":239,"tolerance":1e-9,"twin_halfedge":482},{"chart_seam":false,"geometry":{"curve":240,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":474,"id":240,"tolerance":1e-9,"twin_halfedge":512},{"chart_seam":false,"geometry":{"curve":241,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":475,"id":241,"tolerance":1e-9,"twin_halfedge":476},{"chart_seam":false,"geometry":{"curve":242,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":477,"id":242,"tolerance":1e-9,"twin_halfedge":484},{"chart_seam":false,"geometry":{"curve":243,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":481,"id":243,"tolerance":1e-9,"twin_halfedge":519},{"chart_seam":false,"geometry":{"curve":244,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":485,"id":244,"tolerance":1e-9,"twin_halfedge":496},{"chart_seam":false,"geometry":{"curve":245,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":486,"id":245,"tolerance":1e-9,"twin_halfedge":503},{"chart_seam":false,"geometry":{"curve":246,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":487,"id":246,"tolerance":1e-9,"twin_halfedge":494},{"chart_seam":false,"geometry":{"curve":247,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":488,"id":247,"tolerance":1e-9,"twin_halfedge":501},{"chart_seam":false,"geometry":{"curve":248,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":489,"id":248,"tolerance":1e-9,"twin_halfedge":498},{"chart_seam":false,"geometry":{"curve":249,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":490,"id":249,"tolerance":1e-9,"twin_halfedge":504},{"chart_seam":false,"geometry":{"curve":250,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":491,"id":250,"tolerance":1e-9,"twin_halfedge":492},{"chart_seam":false,"geometry":{"curve":251,"kind":"Curve","range":{"hi":2.3000000000000007,"lo":0.0}},"halfedge":493,"id":251,"tolerance":1e-9,"twin_halfedge":511},{"chart_seam":false,"geometry":{"curve":252,"kind":"Curve","range":{"hi":2.3000000000000007,"lo":0.0}},"halfedge":495,"id":252,"tolerance":1e-9,"twin_halfedge":502},{"chart_seam":false,"geometry":{"curve":253,"kind":"Curve","range":{"hi":2.3000000000000007,"lo":0.0}},"halfedge":497,"id":253,"tolerance":1e-9,"twin_halfedge":515},{"chart_seam":false,"geometry":{"curve":254,"kind":"Curve","range":{"hi":2.3000000000000007,"lo":0.0}},"halfedge":499,"id":254,"tolerance":1e-9,"twin_halfedge":500},{"chart_seam":false,"geometry":{"curve":255,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":505,"id":255,"tolerance":1e-9,"twin_halfedge":514},{"chart_seam":false,"geometry":{"curve":256,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":506,"id":256,"tolerance":1e-9,"twin_halfedge":517},{"chart_seam":false,"geometry":{"curve":257,"kind":"Curve","range":{"hi":0.8999999999999999,"lo":0.0}},"halfedge":507,"id":257,"tolerance":1e-9,"twin_halfedge":508},{"chart_seam":false,"geometry":{"curve":258,"kind":"Curve","range":{"hi":2.3000000000000007,"lo":0.0}},"halfedge":509,"id":258,"tolerance":1e-9,"twin_halfedge":516},{"chart_seam":false,"geometry":{"curve":259,"kind":"Curve","range":{"hi":2.3000000000000007,"lo":0.0}},"halfedge":513,"id":259,"tolerance":1e-9,"twin_halfedge":518}],"faces":[{"id":0,"key":"side-0-3-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":1,"key":"opening-0:side-0-3-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-0","entity":"opening-0","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":2,"key":"side-0-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-2-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":20.000000001,"lo":18.999999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"bottom-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":5,"key":"side-0-3-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":6,"key":"side-0-0-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":7,"key":"side-0-2-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":20.000000001,"lo":18.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":8,"key":"top-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":9,"key":"side-0-0-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":1.500000001,"lo":0.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":10,"key":"side-0-2-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":19.000000001,"lo":18.499999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":11,"key":"opening-0:top-11","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-0","entity":"opening-0","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":1.500000001,"lo":0.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":12,"key":"top-12","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":1.500000001,"lo":0.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":13,"key":"opening-0:side-0-1-13","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-0","entity":"opening-0","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":14,"key":"opening-1:side-0-3-14","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-1","entity":"opening-1","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":15,"key":"side-0-0-15","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":2.800000001,"lo":1.499999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":16,"key":"side-0-2-16","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[],"outer":16,"uv_bounds":[{"hi":18.500000001,"lo":17.199999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":17,"key":"bottom-17","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":17,"trim":{"chart":0,"holes":[],"outer":17,"uv_bounds":[{"hi":2.800000001,"lo":1.499999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":18,"key":"side-0-0-18","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":18,"trim":{"chart":0,"holes":[],"outer":18,"uv_bounds":[{"hi":2.800000001,"lo":1.499999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":19,"key":"side-0-2-19","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":19,"trim":{"chart":0,"holes":[],"outer":19,"uv_bounds":[{"hi":18.500000001,"lo":17.199999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":20,"key":"top-20","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":20,"trim":{"chart":0,"holes":[],"outer":20,"uv_bounds":[{"hi":2.800000001,"lo":1.499999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":21,"key":"side-0-0-21","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":21,"trim":{"chart":0,"holes":[],"outer":21,"uv_bounds":[{"hi":3.300000001,"lo":2.7999999989999997},{"hi":3.000000001,"lo":2.099999999}]}},{"id":22,"key":"side-0-2-22","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":22,"trim":{"chart":0,"holes":[],"outer":22,"uv_bounds":[{"hi":17.200000001,"lo":16.699999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":23,"key":"opening-1:top-23","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-1","entity":"opening-1","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":23,"trim":{"chart":0,"holes":[],"outer":23,"uv_bounds":[{"hi":3.300000001,"lo":2.7999999989999997},{"hi":0.300000001,"lo":-1e-9}]}},{"id":24,"key":"top-24","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":24,"trim":{"chart":0,"holes":[],"outer":24,"uv_bounds":[{"hi":3.300000001,"lo":2.7999999989999997},{"hi":0.300000001,"lo":-1e-9}]}},{"id":25,"key":"opening-1:side-0-1-25","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-1","entity":"opening-1","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":25,"trim":{"chart":0,"holes":[],"outer":25,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":26,"key":"opening-2:side-0-3-26","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-2","entity":"opening-2","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":26,"trim":{"chart":0,"holes":[],"outer":26,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":27,"key":"side-0-0-27","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":27,"trim":{"chart":0,"holes":[],"outer":27,"uv_bounds":[{"hi":4.600000001,"lo":3.2999999989999997},{"hi":2.100000001,"lo":-1e-9}]}},{"id":28,"key":"side-0-2-28","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":28,"trim":{"chart":0,"holes":[],"outer":28,"uv_bounds":[{"hi":16.700000001,"lo":15.399999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":29,"key":"bottom-29","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":29,"trim":{"chart":0,"holes":[],"outer":29,"uv_bounds":[{"hi":4.600000001,"lo":3.2999999989999997},{"hi":1e-9,"lo":-0.300000001}]}},{"id":30,"key":"side-0-0-30","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":30,"trim":{"chart":0,"holes":[],"outer":30,"uv_bounds":[{"hi":4.600000001,"lo":3.2999999989999997},{"hi":3.000000001,"lo":2.099999999}]}},{"id":31,"key":"side-0-2-31","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":31,"trim":{"chart":0,"holes":[],"outer":31,"uv_bounds":[{"hi":16.700000001,"lo":15.399999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":32,"key":"top-32","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":32,"trim":{"chart":0,"holes":[],"outer":32,"uv_bounds":[{"hi":4.600000001,"lo":3.2999999989999997},{"hi":0.300000001,"lo":-1e-9}]}},{"id":33,"key":"side-0-0-33","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":33,"trim":{"chart":0,"holes":[],"outer":33,"uv_bounds":[{"hi":5.100000001,"lo":4.599999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":34,"key":"side-0-2-34","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":34,"trim":{"chart":0,"holes":[],"outer":34,"uv_bounds":[{"hi":15.400000001,"lo":14.899999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":35,"key":"opening-2:top-35","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-2","entity":"opening-2","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":35,"trim":{"chart":0,"holes":[],"outer":35,"uv_bounds":[{"hi":5.100000001,"lo":4.599999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":36,"key":"top-36","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":36,"trim":{"chart":0,"holes":[],"outer":36,"uv_bounds":[{"hi":5.100000001,"lo":4.599999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":37,"key":"opening-2:side-0-1-37","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-2","entity":"opening-2","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":37,"trim":{"chart":0,"holes":[],"outer":37,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":38,"key":"opening-3:side-0-3-38","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-3","entity":"opening-3","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":38,"trim":{"chart":0,"holes":[],"outer":38,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":39,"key":"side-0-0-39","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":39,"trim":{"chart":0,"holes":[],"outer":39,"uv_bounds":[{"hi":6.400000001,"lo":5.099999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":40,"key":"side-0-2-40","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":40,"trim":{"chart":0,"holes":[],"outer":40,"uv_bounds":[{"hi":14.900000001,"lo":13.599999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":41,"key":"bottom-41","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":41,"trim":{"chart":0,"holes":[],"outer":41,"uv_bounds":[{"hi":6.400000001,"lo":5.099999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":42,"key":"side-0-0-42","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":42,"trim":{"chart":0,"holes":[],"outer":42,"uv_bounds":[{"hi":6.400000001,"lo":5.099999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":43,"key":"side-0-2-43","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":43,"trim":{"chart":0,"holes":[],"outer":43,"uv_bounds":[{"hi":14.900000001,"lo":13.599999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":44,"key":"top-44","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":44,"trim":{"chart":0,"holes":[],"outer":44,"uv_bounds":[{"hi":6.400000001,"lo":5.099999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":45,"key":"side-0-0-45","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":45,"trim":{"chart":0,"holes":[],"outer":45,"uv_bounds":[{"hi":6.900000001,"lo":6.399999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":46,"key":"side-0-2-46","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":46,"trim":{"chart":0,"holes":[],"outer":46,"uv_bounds":[{"hi":13.600000001,"lo":13.099999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":47,"key":"opening-3:top-47","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-3","entity":"opening-3","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":47,"trim":{"chart":0,"holes":[],"outer":47,"uv_bounds":[{"hi":6.900000001,"lo":6.399999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":48,"key":"top-48","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":48,"trim":{"chart":0,"holes":[],"outer":48,"uv_bounds":[{"hi":6.900000001,"lo":6.399999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":49,"key":"opening-3:side-0-1-49","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-3","entity":"opening-3","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":49,"trim":{"chart":0,"holes":[],"outer":49,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":50,"key":"opening-4:side-0-3-50","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-4","entity":"opening-4","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":50,"trim":{"chart":0,"holes":[],"outer":50,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":51,"key":"side-0-0-51","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":51,"trim":{"chart":0,"holes":[],"outer":51,"uv_bounds":[{"hi":8.200000001,"lo":6.899999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":52,"key":"side-0-2-52","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":52,"trim":{"chart":0,"holes":[],"outer":52,"uv_bounds":[{"hi":13.100000001,"lo":11.799999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":53,"key":"bottom-53","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":53,"trim":{"chart":0,"holes":[],"outer":53,"uv_bounds":[{"hi":8.200000001,"lo":6.899999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":54,"key":"side-0-0-54","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":54,"trim":{"chart":0,"holes":[],"outer":54,"uv_bounds":[{"hi":8.200000001,"lo":6.899999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":55,"key":"side-0-2-55","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":55,"trim":{"chart":0,"holes":[],"outer":55,"uv_bounds":[{"hi":13.100000001,"lo":11.799999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":56,"key":"top-56","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":56,"trim":{"chart":0,"holes":[],"outer":56,"uv_bounds":[{"hi":8.200000001,"lo":6.899999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":57,"key":"side-0-0-57","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":57,"trim":{"chart":0,"holes":[],"outer":57,"uv_bounds":[{"hi":8.700000001,"lo":8.199999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":58,"key":"side-0-2-58","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":58,"trim":{"chart":0,"holes":[],"outer":58,"uv_bounds":[{"hi":11.800000001,"lo":11.299999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":59,"key":"opening-4:top-59","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-4","entity":"opening-4","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":59,"trim":{"chart":0,"holes":[],"outer":59,"uv_bounds":[{"hi":8.700000001,"lo":8.199999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":60,"key":"top-60","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":60,"trim":{"chart":0,"holes":[],"outer":60,"uv_bounds":[{"hi":8.700000001,"lo":8.199999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":61,"key":"opening-4:side-0-1-61","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-4","entity":"opening-4","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":61,"trim":{"chart":0,"holes":[],"outer":61,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":62,"key":"opening-5:side-0-3-62","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-5","entity":"opening-5","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":62,"trim":{"chart":0,"holes":[],"outer":62,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":63,"key":"side-0-0-63","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":63,"trim":{"chart":0,"holes":[],"outer":63,"uv_bounds":[{"hi":10.000000001,"lo":8.699999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":64,"key":"side-0-2-64","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":64,"trim":{"chart":0,"holes":[],"outer":64,"uv_bounds":[{"hi":11.300000001,"lo":9.999999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":65,"key":"bottom-65","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":65,"trim":{"chart":0,"holes":[],"outer":65,"uv_bounds":[{"hi":10.000000001,"lo":8.699999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":66,"key":"side-0-0-66","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":66,"trim":{"chart":0,"holes":[],"outer":66,"uv_bounds":[{"hi":10.000000001,"lo":8.699999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":67,"key":"side-0-2-67","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":67,"trim":{"chart":0,"holes":[],"outer":67,"uv_bounds":[{"hi":11.300000001,"lo":9.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":68,"key":"top-68","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":68,"trim":{"chart":0,"holes":[],"outer":68,"uv_bounds":[{"hi":10.000000001,"lo":8.699999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":69,"key":"side-0-0-69","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":69,"trim":{"chart":0,"holes":[],"outer":69,"uv_bounds":[{"hi":10.500000001,"lo":9.999999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":70,"key":"side-0-2-70","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":70,"trim":{"chart":0,"holes":[],"outer":70,"uv_bounds":[{"hi":10.000000001,"lo":9.499999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":71,"key":"opening-5:top-71","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-5","entity":"opening-5","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":71,"trim":{"chart":0,"holes":[],"outer":71,"uv_bounds":[{"hi":10.500000001,"lo":9.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":72,"key":"top-72","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":72,"trim":{"chart":0,"holes":[],"outer":72,"uv_bounds":[{"hi":10.500000001,"lo":9.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":73,"key":"opening-5:side-0-1-73","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-5","entity":"opening-5","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":73,"trim":{"chart":0,"holes":[],"outer":73,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":74,"key":"opening-6:side-0-3-74","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-6","entity":"opening-6","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":74,"trim":{"chart":0,"holes":[],"outer":74,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":75,"key":"side-0-0-75","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":75,"trim":{"chart":0,"holes":[],"outer":75,"uv_bounds":[{"hi":11.800000001,"lo":10.499999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":76,"key":"side-0-2-76","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":76,"trim":{"chart":0,"holes":[],"outer":76,"uv_bounds":[{"hi":9.500000001,"lo":8.199999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":77,"key":"bottom-77","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":77,"trim":{"chart":0,"holes":[],"outer":77,"uv_bounds":[{"hi":11.800000001,"lo":10.499999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":78,"key":"side-0-0-78","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":78,"trim":{"chart":0,"holes":[],"outer":78,"uv_bounds":[{"hi":11.800000001,"lo":10.499999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":79,"key":"side-0-2-79","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":79,"trim":{"chart":0,"holes":[],"outer":79,"uv_bounds":[{"hi":9.500000001,"lo":8.199999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":80,"key":"top-80","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":80,"trim":{"chart":0,"holes":[],"outer":80,"uv_bounds":[{"hi":11.800000001,"lo":10.499999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":81,"key":"side-0-0-81","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":81,"trim":{"chart":0,"holes":[],"outer":81,"uv_bounds":[{"hi":12.300000001,"lo":11.799999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":82,"key":"side-0-2-82","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":82,"trim":{"chart":0,"holes":[],"outer":82,"uv_bounds":[{"hi":8.200000001,"lo":7.699999998999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":83,"key":"opening-6:top-83","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-6","entity":"opening-6","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":83,"trim":{"chart":0,"holes":[],"outer":83,"uv_bounds":[{"hi":12.300000001,"lo":11.799999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":84,"key":"top-84","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":84,"trim":{"chart":0,"holes":[],"outer":84,"uv_bounds":[{"hi":12.300000001,"lo":11.799999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":85,"key":"opening-6:side-0-1-85","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-6","entity":"opening-6","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":85,"trim":{"chart":0,"holes":[],"outer":85,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":86,"key":"opening-7:side-0-3-86","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-7","entity":"opening-7","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":86,"trim":{"chart":0,"holes":[],"outer":86,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":87,"key":"side-0-0-87","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":87,"trim":{"chart":0,"holes":[],"outer":87,"uv_bounds":[{"hi":13.600000001,"lo":12.299999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":88,"key":"side-0-2-88","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":88,"trim":{"chart":0,"holes":[],"outer":88,"uv_bounds":[{"hi":7.700000000999999,"lo":6.399999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":89,"key":"bottom-89","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":89,"trim":{"chart":0,"holes":[],"outer":89,"uv_bounds":[{"hi":13.600000001,"lo":12.299999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":90,"key":"side-0-0-90","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":90,"trim":{"chart":0,"holes":[],"outer":90,"uv_bounds":[{"hi":13.600000001,"lo":12.299999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":91,"key":"side-0-2-91","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":91,"trim":{"chart":0,"holes":[],"outer":91,"uv_bounds":[{"hi":7.700000000999999,"lo":6.399999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":92,"key":"top-92","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":92,"trim":{"chart":0,"holes":[],"outer":92,"uv_bounds":[{"hi":13.600000001,"lo":12.299999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":93,"key":"side-0-0-93","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":93,"trim":{"chart":0,"holes":[],"outer":93,"uv_bounds":[{"hi":14.100000001,"lo":13.599999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":94,"key":"side-0-2-94","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":94,"trim":{"chart":0,"holes":[],"outer":94,"uv_bounds":[{"hi":6.400000001,"lo":5.899999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":95,"key":"opening-7:top-95","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-7","entity":"opening-7","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":95,"trim":{"chart":0,"holes":[],"outer":95,"uv_bounds":[{"hi":14.100000001,"lo":13.599999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":96,"key":"top-96","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":96,"trim":{"chart":0,"holes":[],"outer":96,"uv_bounds":[{"hi":14.100000001,"lo":13.599999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":97,"key":"opening-7:side-0-1-97","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-7","entity":"opening-7","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":97,"trim":{"chart":0,"holes":[],"outer":97,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":98,"key":"opening-8:side-0-3-98","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-8","entity":"opening-8","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":98,"trim":{"chart":0,"holes":[],"outer":98,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":99,"key":"side-0-0-99","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":99,"trim":{"chart":0,"holes":[],"outer":99,"uv_bounds":[{"hi":15.400000001,"lo":14.099999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":100,"key":"side-0-2-100","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":100,"trim":{"chart":0,"holes":[],"outer":100,"uv_bounds":[{"hi":5.900000001,"lo":4.599999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":101,"key":"bottom-101","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":101,"trim":{"chart":0,"holes":[],"outer":101,"uv_bounds":[{"hi":15.400000001,"lo":14.099999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":102,"key":"side-0-0-102","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":102,"trim":{"chart":0,"holes":[],"outer":102,"uv_bounds":[{"hi":15.400000001,"lo":14.099999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":103,"key":"side-0-2-103","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":103,"trim":{"chart":0,"holes":[],"outer":103,"uv_bounds":[{"hi":5.900000001,"lo":4.599999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":104,"key":"top-104","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":104,"trim":{"chart":0,"holes":[],"outer":104,"uv_bounds":[{"hi":15.400000001,"lo":14.099999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":105,"key":"side-0-0-105","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":105,"trim":{"chart":0,"holes":[],"outer":105,"uv_bounds":[{"hi":15.900000001,"lo":15.399999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":106,"key":"side-0-2-106","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":106,"trim":{"chart":0,"holes":[],"outer":106,"uv_bounds":[{"hi":4.600000001,"lo":4.099999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":107,"key":"opening-8:top-107","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-8","entity":"opening-8","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":107,"trim":{"chart":0,"holes":[],"outer":107,"uv_bounds":[{"hi":15.900000001,"lo":15.399999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":108,"key":"top-108","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":108,"trim":{"chart":0,"holes":[],"outer":108,"uv_bounds":[{"hi":15.900000001,"lo":15.399999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":109,"key":"opening-8:side-0-1-109","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-8","entity":"opening-8","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":109,"trim":{"chart":0,"holes":[],"outer":109,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":110,"key":"opening-9:side-0-3-110","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-9","entity":"opening-9","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":110,"trim":{"chart":0,"holes":[],"outer":110,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":111,"key":"side-0-0-111","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":111,"trim":{"chart":0,"holes":[],"outer":111,"uv_bounds":[{"hi":17.200000001,"lo":15.899999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":112,"key":"side-0-2-112","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":112,"trim":{"chart":0,"holes":[],"outer":112,"uv_bounds":[{"hi":4.100000001,"lo":2.7999999990000006},{"hi":2.100000001,"lo":-1e-9}]}},{"id":113,"key":"bottom-113","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":113,"trim":{"chart":0,"holes":[],"outer":113,"uv_bounds":[{"hi":17.200000001,"lo":15.899999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":114,"key":"side-0-0-114","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":114,"trim":{"chart":0,"holes":[],"outer":114,"uv_bounds":[{"hi":17.200000001,"lo":15.899999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":115,"key":"side-0-2-115","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":115,"trim":{"chart":0,"holes":[],"outer":115,"uv_bounds":[{"hi":4.100000001,"lo":2.7999999990000006},{"hi":3.000000001,"lo":2.099999999}]}},{"id":116,"key":"top-116","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":116,"trim":{"chart":0,"holes":[],"outer":116,"uv_bounds":[{"hi":17.200000001,"lo":15.899999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":117,"key":"side-0-0-117","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":117,"trim":{"chart":0,"holes":[],"outer":117,"uv_bounds":[{"hi":17.700000001,"lo":17.199999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":118,"key":"side-0-2-118","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":118,"trim":{"chart":0,"holes":[],"outer":118,"uv_bounds":[{"hi":2.800000001000001,"lo":2.2999999990000006},{"hi":3.000000001,"lo":2.099999999}]}},{"id":119,"key":"opening-9:top-119","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-9","entity":"opening-9","face":0,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":119,"trim":{"chart":0,"holes":[],"outer":119,"uv_bounds":[{"hi":17.700000001,"lo":17.199999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":120,"key":"top-120","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":120,"trim":{"chart":0,"holes":[],"outer":120,"uv_bounds":[{"hi":17.700000001,"lo":17.199999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":121,"key":"opening-9:side-0-1-121","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening-9","entity":"opening-9","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":121,"trim":{"chart":0,"holes":[],"outer":121,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":122,"key":"side-0-1-122","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":122,"trim":{"chart":0,"holes":[],"outer":122,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":123,"key":"side-0-0-123","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":123,"trim":{"chart":0,"holes":[],"outer":123,"uv_bounds":[{"hi":20.000000001,"lo":17.699999999},{"hi":2.100000001,"lo":-1e-9}]}},{"id":124,"key":"side-0-2-124","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":124,"trim":{"chart":0,"holes":[],"outer":124,"uv_bounds":[{"hi":2.300000001000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":125,"key":"bottom-125","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":125,"trim":{"chart":0,"holes":[],"outer":125,"uv_bounds":[{"hi":20.000000001,"lo":17.699999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":126,"key":"side-0-1-126","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":126,"trim":{"chart":0,"holes":[],"outer":126,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":127,"key":"side-0-0-127","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":127,"trim":{"chart":0,"holes":[],"outer":127,"uv_bounds":[{"hi":20.000000001,"lo":17.699999999},{"hi":3.000000001,"lo":2.099999999}]}},{"id":128,"key":"side-0-2-128","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":128,"trim":{"chart":0,"holes":[],"outer":128,"uv_bounds":[{"hi":2.300000001000001,"lo":-1e-9},{"hi":3.000000001,"lo":2.099999999}]}},{"id":129,"key":"top-129","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":129,"trim":{"chart":0,"holes":[],"outer":129,"uv_bounds":[{"hi":20.000000001,"lo":17.699999999},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":22,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":12,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":19,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":10,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":17,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":6,"twin":14,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":7,"twin":47,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":8,"wire_ref":null},{"edge":7,"face":2,"from":4,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":7,"twin":7,"wire_ref":null},{"edge":8,"face":2,"from":7,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":0,"twin":27,"wire_ref":null},{"edge":3,"face":2,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":3,"wire_ref":null},{"edge":9,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":4,"twin":18,"wire_ref":null},{"edge":1,"face":3,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":1,"twin":1,"wire_ref":null},{"edge":10,"face":3,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":31,"wire_ref":null},{"edge":5,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":11,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":2,"twin":16,"wire_ref":null},{"edge":11,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":5,"twin":15,"wire_ref":null},{"edge":4,"face":4,"from":5,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":4,"twin":4,"wire_ref":null},{"edge":9,"face":4,"from":4,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":3,"twin":11,"wire_ref":null},{"edge":2,"face":4,"from":3,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":2,"wire_ref":null},{"edge":12,"face":5,"from":8,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":9,"twin":35,"wire_ref":null},{"edge":13,"face":5,"from":9,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Reverse"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":1,"twin":28,"wire_ref":null},{"edge":0,"face":5,"from":1,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":0,"twin":0,"wire_ref":null},{"edge":14,"face":5,"from":0,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":8,"twin":26,"wire_ref":null},{"edge":15,"face":6,"from":7,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":10,"twin":38,"wire_ref":null},{"edge":16,"face":6,"from":10,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":8,"twin":32,"wire_ref":null},{"edge":14,"face":6,"from":8,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":0,"twin":23,"wire_ref":null},{"edge":8,"face":6,"from":0,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":7,"twin":9,"wire_ref":null},{"edge":13,"face":7,"from":1,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":9,"twin":21,"wire_ref":null},{"edge":17,"face":7,"from":9,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":11,"twin":34,"wire_ref":null},{"edge":18,"face":7,"from":11,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":6,"twin":40,"wire_ref":null},{"edge":10,"face":7,"from":6,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":1,"twin":13,"wire_ref":null},{"edge":16,"face":8,"from":8,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":10,"twin":25,"wire_ref":null},{"edge":19,"face":8,"from":10,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":11,"twin":51,"wire_ref":null},{"edge":17,"face":8,"from":11,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":9,"twin":29,"wire_ref":null},{"edge":12,"face":8,"from":9,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":8,"twin":20,"wire_ref":null},{"edge":20,"face":9,"from":12,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":13,"twin":74,"wire_ref":null},{"edge":21,"face":9,"from":13,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":10,"twin":48,"wire_ref":null},{"edge":15,"face":9,"from":10,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":7,"twin":24,"wire_ref":null},{"edge":22,"face":9,"from":7,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":12,"twin":46,"wire_ref":null},{"edge":18,"face":10,"from":6,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":11,"twin":30,"wire_ref":null},{"edge":23,"face":10,"from":11,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":14,"twin":50,"wire_ref":null},{"edge":24,"face":10,"from":14,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":15,"twin":76,"wire_ref":null},{"edge":25,"face":10,"from":15,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":6,"twin":44,"wire_ref":null},{"edge":25,"face":11,"from":6,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":15,"twin":43,"wire_ref":null},{"edge":26,"face":11,"from":15,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Reverse"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":12,"twin":52,"wire_ref":null},{"edge":22,"face":11,"from":12,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":7,"twin":39,"wire_ref":null},{"edge":6,"face":11,"from":7,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Forward"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":6,"twin":6,"wire_ref":null},{"edge":21,"face":12,"from":10,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":12,"next":49,"prev":51,"to":13,"twin":37,"wire_ref":null},{"edge":27,"face":12,"from":13,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":12,"next":50,"prev":48,"to":14,"twin":83,"wire_ref":null},{"edge":23,"face":12,"from":14,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":12,"next":51,"prev":49,"to":11,"twin":41,"wire_ref":null},{"edge":19,"face":12,"from":11,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":12,"next":48,"prev":50,"to":10,"twin":33,"wire_ref":null},{"edge":26,"face":13,"from":12,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":13,"next":53,"prev":55,"to":15,"twin":45,"wire_ref":null},{"edge":28,"face":13,"from":15,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Reverse"},"id":53,"loop_ref":13,"next":54,"prev":52,"to":16,"twin":64,"wire_ref":null},{"edge":29,"face":13,"from":16,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":13,"next":55,"prev":53,"to":17,"twin":71,"wire_ref":null},{"edge":30,"face":13,"from":17,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Forward"},"id":55,"loop_ref":13,"next":52,"prev":54,"to":12,"twin":62,"wire_ref":null},{"edge":31,"face":14,"from":18,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":14,"next":57,"prev":59,"to":19,"twin":69,"wire_ref":null},{"edge":32,"face":14,"from":19,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":14,"next":58,"prev":56,"to":20,"twin":66,"wire_ref":null},{"edge":33,"face":14,"from":20,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":14,"next":59,"prev":57,"to":21,"twin":95,"wire_ref":null},{"edge":34,"face":14,"from":21,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":14,"next":56,"prev":58,"to":18,"twin":60,"wire_ref":null},{"edge":34,"face":15,"from":18,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Forward"},"id":60,"loop_ref":15,"next":61,"prev":63,"to":21,"twin":59,"wire_ref":null},{"edge":35,"face":15,"from":21,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Reverse"},"id":61,"loop_ref":15,"next":62,"prev":60,"to":12,"twin":75,"wire_ref":null},{"edge":30,"face":15,"from":12,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":15,"next":63,"prev":61,"to":17,"twin":55,"wire_ref":null},{"edge":36,"face":15,"from":17,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Forward"},"id":63,"loop_ref":15,"next":60,"prev":62,"to":18,"twin":70,"wire_ref":null},{"edge":28,"face":16,"from":16,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Forward"},"id":64,"loop_ref":16,"next":65,"prev":67,"to":15,"twin":53,"wire_ref":null},{"edge":37,"face":16,"from":15,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Forward"},"id":65,"loop_ref":16,"next":66,"prev":64,"to":20,"twin":79,"wire_ref":null},{"edge":32,"face":16,"from":20,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Reverse"},"id":66,"loop_ref":16,"next":67,"prev":65,"to":19,"twin":57,"wire_ref":null},{"edge":38,"face":16,"from":19,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Reverse"},"id":67,"loop_ref":16,"next":64,"prev":66,"to":16,"twin":68,"wire_ref":null},{"edge":38,"face":17,"from":16,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":17,"next":69,"prev":71,"to":19,"twin":67,"wire_ref":null},{"edge":31,"face":17,"from":19,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Reverse"},"id":69,"loop_ref":17,"next":70,"prev":68,"to":18,"twin":56,"wire_ref":null},{"edge":36,"face":17,"from":18,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":17,"next":71,"prev":69,"to":17,"twin":63,"wire_ref":null},{"edge":29,"face":17,"from":17,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Forward"},"id":71,"loop_ref":17,"next":68,"prev":70,"to":16,"twin":54,"wire_ref":null},{"edge":39,"face":18,"from":21,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Forward"},"id":72,"loop_ref":18,"next":73,"prev":75,"to":22,"twin":86,"wire_ref":null},{"edge":40,"face":18,"from":22,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Reverse"},"id":73,"loop_ref":18,"next":74,"prev":72,"to":13,"twin":80,"wire_ref":null},{"edge":20,"face":18,"from":13,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":18,"next":75,"prev":73,"to":12,"twin":36,"wire_ref":null},{"edge":35,"face":18,"from":12,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Forward"},"id":75,"loop_ref":18,"next":72,"prev":74,"to":21,"twin":61,"wire_ref":null},{"edge":24,"face":19,"from":15,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Forward"},"id":76,"loop_ref":19,"next":77,"prev":79,"to":14,"twin":42,"wire_ref":null},{"edge":41,"face":19,"from":14,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Forward"},"id":77,"loop_ref":19,"next":78,"prev":76,"to":23,"twin":82,"wire_ref":null},{"edge":42,"face":19,"from":23,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":19,"next":79,"prev":77,"to":20,"twin":88,"wire_ref":null},{"edge":37,"face":19,"from":20,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Reverse"},"id":79,"loop_ref":19,"next":76,"prev":78,"to":15,"twin":65,"wire_ref":null},{"edge":40,"face":20,"from":13,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Forward"},"id":80,"loop_ref":20,"next":81,"prev":83,"to":22,"twin":73,"wire_ref":null},{"edge":43,"face":20,"from":22,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Forward"},"id":81,"loop_ref":20,"next":82,"prev":80,"to":23,"twin":99,"wire_ref":null},{"edge":41,"face":20,"from":23,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":20,"next":83,"prev":81,"to":14,"twin":77,"wire_ref":null},{"edge":27,"face":20,"from":14,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":20,"next":80,"prev":82,"to":13,"twin":49,"wire_ref":null},{"edge":44,"face":21,"from":24,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Forward"},"id":84,"loop_ref":21,"next":85,"prev":87,"to":25,"twin":122,"wire_ref":null},{"edge":45,"face":21,"from":25,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Reverse"},"id":85,"loop_ref":21,"next":86,"prev":84,"to":22,"twin":96,"wire_ref":null},{"edge":39,"face":21,"from":22,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Reverse"},"id":86,"loop_ref":21,"next":87,"prev":85,"to":21,"twin":72,"wire_ref":null},{"edge":46,"face":21,"from":21,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Forward"},"id":87,"loop_ref":21,"next":84,"prev":86,"to":24,"twin":94,"wire_ref":null},{"edge":42,"face":22,"from":20,"geometry_use":{"pcurve":88,"periodic_lift":[0,0],"sense":"Forward"},"id":88,"loop_ref":22,"next":89,"prev":91,"to":23,"twin":78,"wire_ref":null},{"edge":47,"face":22,"from":23,"geometry_use":{"pcurve":89,"periodic_lift":[0,0],"sense":"Forward"},"id":89,"loop_ref":22,"next":90,"prev":88,"to":26,"twin":98,"wire_ref":null},{"edge":48,"face":22,"from":26,"geometry_use":{"pcurve":90,"periodic_lift":[0,0],"sense":"Reverse"},"id":90,"loop_ref":22,"next":91,"prev":89,"to":27,"twin":124,"wire_ref":null},{"edge":49,"face":22,"from":27,"geometry_use":{"pcurve":91,"periodic_lift":[0,0],"sense":"Reverse"},"id":91,"loop_ref":22,"next":88,"prev":90,"to":20,"twin":92,"wire_ref":null},{"edge":49,"face":23,"from":20,"geometry_use":{"pcurve":92,"periodic_lift":[0,0],"sense":"Forward"},"id":92,"loop_ref":23,"next":93,"prev":95,"to":27,"twin":91,"wire_ref":null},{"edge":50,"face":23,"from":27,"geometry_use":{"pcurve":93,"periodic_lift":[0,0],"sense":"Reverse"},"id":93,"loop_ref":23,"next":94,"prev":92,"to":24,"twin":100,"wire_ref":null},{"edge":46,"face":23,"from":24,"geometry_use":{"pcurve":94,"periodic_lift":[0,0],"sense":"Reverse"},"id":94,"loop_ref":23,"next":95,"prev":93,"to":21,"twin":87,"wire_ref":null},{"edge":33,"face":23,"from":21,"geometry_use":{"pcurve":95,"periodic_lift":[0,0],"sense":"Forward"},"id":95,"loop_ref":23,"next":92,"prev":94,"to":20,"twin":58,"wire_ref":null},{"edge":45,"face":24,"from":22,"geometry_use":{"pcurve":96,"periodic_lift":[0,0],"sense":"Forward"},"id":96,"loop_ref":24,"next":97,"prev":99,"to":25,"twin":85,"wire_ref":null},{"edge":51,"face":24,"from":25,"geometry_use":{"pcurve":97,"periodic_lift":[0,0],"sense":"Forward"},"id":97,"loop_ref":24,"next":98,"prev":96,"to":26,"twin":131,"wire_ref":null},{"edge":47,"face":24,"from":26,"geometry_use":{"pcurve":98,"periodic_lift":[0,0],"sense":"Reverse"},"id":98,"loop_ref":24,"next":99,"prev":97,"to":23,"twin":89,"wire_ref":null},{"edge":43,"face":24,"from":23,"geometry_use":{"pcurve":99,"periodic_lift":[0,0],"sense":"Reverse"},"id":99,"loop_ref":24,"next":96,"prev":98,"to":22,"twin":81,"wire_ref":null},{"edge":50,"face":25,"from":24,"geometry_use":{"pcurve":100,"periodic_lift":[0,0],"sense":"Forward"},"id":100,"loop_ref":25,"next":101,"prev":103,"to":27,"twin":93,"wire_ref":null},{"edge":52,"face":25,"from":27,"geometry_use":{"pcurve":101,"periodic_lift":[0,0],"sense":"Reverse"},"id":101,"loop_ref":25,"next":102,"prev":100,"to":28,"twin":112,"wire_ref":null},{"edge":53,"face":25,"from":28,"geometry_use":{"pcurve":102,"periodic_lift":[0,0],"sense":"Reverse"},"id":102,"loop_ref":25,"next":103,"prev":101,"to":29,"twin":119,"wire_ref":null},{"edge":54,"face":25,"from":29,"geometry_use":{"pcurve":103,"periodic_lift":[0,0],"sense":"Forward"},"id":103,"loop_ref":25,"next":100,"prev":102,"to":24,"twin":110,"wire_ref":null},{"edge":55,"face":26,"from":30,"geometry_use":{"pcurve":104,"periodic_lift":[0,0],"sense":"Forward"},"id":104,"loop_ref":26,"next":105,"prev":107,"to":31,"twin":117,"wire_ref":null},{"edge":56,"face":26,"from":31,"geometry_use":{"pcurve":105,"periodic_lift":[0,0],"sense":"Forward"},"id":105,"loop_ref":26,"next":106,"prev":104,"to":32,"twin":114,"wire_ref":null},{"edge":57,"face":26,"from":32,"geometry_use":{"pcurve":106,"periodic_lift":[0,0],"sense":"Reverse"},"id":106,"loop_ref":26,"next":107,"prev":105,"to":33,"twin":143,"wire_ref":null},{"edge":58,"face":26,"from":33,"geometry_use":{"pcurve":107,"periodic_lift":[0,0],"sense":"Reverse"},"id":107,"loop_ref":26,"next":104,"prev":106,"to":30,"twin":108,"wire_ref":null},{"edge":58,"face":27,"from":30,"geometry_use":{"pcurve":108,"periodic_lift":[0,0],"sense":"Forward"},"id":108,"loop_ref":27,"next":109,"prev":111,"to":33,"twin":107,"wire_ref":null},{"edge":59,"face":27,"from":33,"geometry_use":{"pcurve":109,"periodic_lift":[0,0],"sense":"Reverse"},"id":109,"loop_ref":27,"next":110,"prev":108,"to":24,"twin":123,"wire_ref":null},{"edge":54,"face":27,"from":24,"geometry_use":{"pcurve":110,"periodic_lift":[0,0],"sense":"Reverse"},"id":110,"loop_ref":27,"next":111,"prev":109,"to":29,"twin":103,"wire_ref":null},{"edge":60,"face":27,"from":29,"geometry_use":{"pcurve":111,"periodic_lift":[0,0],"sense":"Forward"},"id":111,"loop_ref":27,"next":108,"prev":110,"to":30,"twin":118,"wire_ref":null},{"edge":52,"face":28,"from":28,"geometry_use":{"pcurve":112,"periodic_lift":[0,0],"sense":"Forward"},"id":112,"loop_ref":28,"next":113,"prev":115,"to":27,"twin":101,"wire_ref":null},{"edge":61,"face":28,"from":27,"geometry_use":{"pcurve":113,"periodic_lift":[0,0],"sense":"Forward"},"id":113,"loop_ref":28,"next":114,"prev":112,"to":32,"twin":127,"wire_ref":null},{"edge":56,"face":28,"from":32,"geometry_use":{"pcurve":114,"periodic_lift":[0,0],"sense":"Reverse"},"id":114,"loop_ref":28,"next":115,"prev":113,"to":31,"twin":105,"wire_ref":null},{"edge":62,"face":28,"from":31,"geometry_use":{"pcurve":115,"periodic_lift":[0,0],"sense":"Reverse"},"id":115,"loop_ref":28,"next":112,"prev":114,"to":28,"twin":116,"wire_ref":null},{"edge":62,"face":29,"from":28,"geometry_use":{"pcurve":116,"periodic_lift":[0,0],"sense":"Forward"},"id":116,"loop_ref":29,"next":117,"prev":119,"to":31,"twin":115,"wire_ref":null},{"edge":55,"face":29,"from":31,"geometry_use":{"pcurve":117,"periodic_lift":[0,0],"sense":"Reverse"},"id":117,"loop_ref":29,"next":118,"prev":116,"to":30,"twin":104,"wire_ref":null},{"edge":60,"face":29,"from":30,"geometry_use":{"pcurve":118,"periodic_lift":[0,0],"sense":"Reverse"},"id":118,"loop_ref":29,"next":119,"prev":117,"to":29,"twin":111,"wire_ref":null},{"edge":53,"face":29,"from":29,"geometry_use":{"pcurve":119,"periodic_lift":[0,0],"sense":"Forward"},"id":119,"loop_ref":29,"next":116,"prev":118,"to":28,"twin":102,"wire_ref":null},{"edge":63,"face":30,"from":33,"geometry_use":{"pcurve":120,"periodic_lift":[0,0],"sense":"Forward"},"id":120,"loop_ref":30,"next":121,"prev":123,"to":34,"twin":134,"wire_ref":null},{"edge":64,"face":30,"from":34,"geometry_use":{"pcurve":121,"periodic_lift":[0,0],"sense":"Reverse"},"id":121,"loop_ref":30,"next":122,"prev":120,"to":25,"twin":128,"wire_ref":null},{"edge":44,"face":30,"from":25,"geometry_use":{"pcurve":122,"periodic_lift":[0,0],"sense":"Reverse"},"id":122,"loop_ref":30,"next":123,"prev":121,"to":24,"twin":84,"wire_ref":null},{"edge":59,"face":30,"from":24,"geometry_use":{"pcurve":123,"periodic_lift":[0,0],"sense":"Forward"},"id":123,"loop_ref":30,"next":120,"prev":122,"to":33,"twin":109,"wire_ref":null},{"edge":48,"face":31,"from":27,"geometry_use":{"pcurve":124,"periodic_lift":[0,0],"sense":"Forward"},"id":124,"loop_ref":31,"next":125,"prev":127,"to":26,"twin":90,"wire_ref":null},{"edge":65,"face":31,"from":26,"geometry_use":{"pcurve":125,"periodic_lift":[0,0],"sense":"Forward"},"id":125,"loop_ref":31,"next":126,"prev":124,"to":35,"twin":130,"wire_ref":null},{"edge":66,"face":31,"from":35,"geometry_use":{"pcurve":126,"periodic_lift":[0,0],"sense":"Reverse"},"id":126,"loop_ref":31,"next":127,"prev":125,"to":32,"twin":136,"wire_ref":null},{"edge":61,"face":31,"from":32,"geometry_use":{"pcurve":127,"periodic_lift":[0,0],"sense":"Reverse"},"id":127,"loop_ref":31,"next":124,"prev":126,"to":27,"twin":113,"wire_ref":null},{"edge":64,"face":32,"from":25,"geometry_use":{"pcurve":128,"periodic_lift":[0,0],"sense":"Forward"},"id":128,"loop_ref":32,"next":129,"prev":131,"to":34,"twin":121,"wire_ref":null},{"edge":67,"face":32,"from":34,"geometry_use":{"pcurve":129,"periodic_lift":[0,0],"sense":"Forward"},"id":129,"loop_ref":32,"next":130,"prev":128,"to":35,"twin":147,"wire_ref":null},{"edge":65,"face":32,"from":35,"geometry_use":{"pcurve":130,"periodic_lift":[0,0],"sense":"Reverse"},"id":130,"loop_ref":32,"next":131,"prev":129,"to":26,"twin":125,"wire_ref":null},{"edge":51,"face":32,"from":26,"geometry_use":{"pcurve":131,"periodic_lift":[0,0],"sense":"Reverse"},"id":131,"loop_ref":32,"next":128,"prev":130,"to":25,"twin":97,"wire_ref":null},{"edge":68,"face":33,"from":36,"geometry_use":{"pcurve":132,"periodic_lift":[0,0],"sense":"Forward"},"id":132,"loop_ref":33,"next":133,"prev":135,"to":37,"twin":170,"wire_ref":null},{"edge":69,"face":33,"from":37,"geometry_use":{"pcurve":133,"periodic_lift":[0,0],"sense":"Reverse"},"id":133,"loop_ref":33,"next":134,"prev":132,"to":34,"twin":144,"wire_ref":null},{"edge":63,"face":33,"from":34,"geometry_use":{"pcurve":134,"periodic_lift":[0,0],"sense":"Reverse"},"id":134,"loop_ref":33,"next":135,"prev":133,"to":33,"twin":120,"wire_ref":null},{"edge":70,"face":33,"from":33,"geometry_use":{"pcurve":135,"periodic_lift":[0,0],"sense":"Forward"},"id":135,"loop_ref":33,"next":132,"prev":134,"to":36,"twin":142,"wire_ref":null},{"edge":66,"face":34,"from":32,"geometry_use":{"pcurve":136,"periodic_lift":[0,0],"sense":"Forward"},"id":136,"loop_ref":34,"next":137,"prev":139,"to":35,"twin":126,"wire_ref":null},{"edge":71,"face":34,"from":35,"geometry_use":{"pcurve":137,"periodic_lift":[0,0],"sense":"Forward"},"id":137,"loop_ref":34,"next":138,"prev":136,"to":38,"twin":146,"wire_ref":null},{"edge":72,"face":34,"from":38,"geometry_use":{"pcurve":138,"periodic_lift":[0,0],"sense":"Reverse"},"id":138,"loop_ref":34,"next":139,"prev":137,"to":39,"twin":172,"wire_ref":null},{"edge":73,"face":34,"from":39,"geometry_use":{"pcurve":139,"periodic_lift":[0,0],"sense":"Reverse"},"id":139,"loop_ref":34,"next":136,"prev":138,"to":32,"twin":140,"wire_ref":null},{"edge":73,"face":35,"from":32,"geometry_use":{"pcurve":140,"periodic_lift":[0,0],"sense":"Forward"},"id":140,"loop_ref":35,"next":141,"prev":143,"to":39,"twin":139,"wire_ref":null},{"edge":74,"face":35,"from":39,"geometry_use":{"pcurve":141,"periodic_lift":[0,0],"sense":"Reverse"},"id":141,"loop_ref":35,"next":142,"prev":140,"to":36,"twin":148,"wire_ref":null},{"edge":70,"face":35,"from":36,"geometry_use":{"pcurve":142,"periodic_lift":[0,0],"sense":"Reverse"},"id":142,"loop_ref":35,"next":143,"prev":141,"to":33,"twin":135,"wire_ref":null},{"edge":57,"face":35,"from":33,"geometry_use":{"pcurve":143,"periodic_lift":[0,0],"sense":"Forward"},"id":143,"loop_ref":35,"next":140,"prev":142,"to":32,"twin":106,"wire_ref":null},{"edge":69,"face":36,"from":34,"geometry_use":{"pcurve":144,"periodic_lift":[0,0],"sense":"Forward"},"id":144,"loop_ref":36,"next":145,"prev":147,"to":37,"twin":133,"wire_ref":null},{"edge":75,"face":36,"from":37,"geometry_use":{"pcurve":145,"periodic_lift":[0,0],"sense":"Forward"},"id":145,"loop_ref":36,"next":146,"prev":144,"to":38,"twin":179,"wire_ref":null},{"edge":71,"face":36,"from":38,"geometry_use":{"pcurve":146,"periodic_lift":[0,0],"sense":"Reverse"},"id":146,"loop_ref":36,"next":147,"prev":145,"to":35,"twin":137,"wire_ref":null},{"edge":67,"face":36,"from":35,"geometry_use":{"pcurve":147,"periodic_lift":[0,0],"sense":"Reverse"},"id":147,"loop_ref":36,"next":144,"prev":146,"to":34,"twin":129,"wire_ref":null},{"edge":74,"face":37,"from":36,"geometry_use":{"pcurve":148,"periodic_lift":[0,0],"sense":"Forward"},"id":148,"loop_ref":37,"next":149,"prev":151,"to":39,"twin":141,"wire_ref":null},{"edge":76,"face":37,"from":39,"geometry_use":{"pcurve":149,"periodic_lift":[0,0],"sense":"Reverse"},"id":149,"loop_ref":37,"next":150,"prev":148,"to":40,"twin":160,"wire_ref":null},{"edge":77,"face":37,"from":40,"geometry_use":{"pcurve":150,"periodic_lift":[0,0],"sense":"Reverse"},"id":150,"loop_ref":37,"next":151,"prev":149,"to":41,"twin":167,"wire_ref":null},{"edge":78,"face":37,"from":41,"geometry_use":{"pcurve":151,"periodic_lift":[0,0],"sense":"Forward"},"id":151,"loop_ref":37,"next":148,"prev":150,"to":36,"twin":158,"wire_ref":null},{"edge":79,"face":38,"from":42,"geometry_use":{"pcurve":152,"periodic_lift":[0,0],"sense":"Forward"},"id":152,"loop_ref":38,"next":153,"prev":155,"to":43,"twin":165,"wire_ref":null},{"edge":80,"face":38,"from":43,"geometry_use":{"pcurve":153,"periodic_lift":[0,0],"sense":"Forward"},"id":153,"loop_ref":38,"next":154,"prev":152,"to":44,"twin":162,"wire_ref":null},{"edge":81,"face":38,"from":44,"geometry_use":{"pcurve":154,"periodic_lift":[0,0],"sense":"Reverse"},"id":154,"loop_ref":38,"next":155,"prev":153,"to":45,"twin":191,"wire_ref":null},{"edge":82,"face":38,"from":45,"geometry_use":{"pcurve":155,"periodic_lift":[0,0],"sense":"Reverse"},"id":155,"loop_ref":38,"next":152,"prev":154,"to":42,"twin":156,"wire_ref":null},{"edge":82,"face":39,"from":42,"geometry_use":{"pcurve":156,"periodic_lift":[0,0],"sense":"Forward"},"id":156,"loop_ref":39,"next":157,"prev":159,"to":45,"twin":155,"wire_ref":null},{"edge":83,"face":39,"from":45,"geometry_use":{"pcurve":157,"periodic_lift":[0,0],"sense":"Reverse"},"id":157,"loop_ref":39,"next":158,"prev":156,"to":36,"twin":171,"wire_ref":null},{"edge":78,"face":39,"from":36,"geometry_use":{"pcurve":158,"periodic_lift":[0,0],"sense":"Reverse"},"id":158,"loop_ref":39,"next":159,"prev":157,"to":41,"twin":151,"wire_ref":null},{"edge":84,"face":39,"from":41,"geometry_use":{"pcurve":159,"periodic_lift":[0,0],"sense":"Forward"},"id":159,"loop_ref":39,"next":156,"prev":158,"to":42,"twin":166,"wire_ref":null},{"edge":76,"face":40,"from":40,"geometry_use":{"pcurve":160,"periodic_lift":[0,0],"sense":"Forward"},"id":160,"loop_ref":40,"next":161,"prev":163,"to":39,"twin":149,"wire_ref":null},{"edge":85,"face":40,"from":39,"geometry_use":{"pcurve":161,"periodic_lift":[0,0],"sense":"Forward"},"id":161,"loop_ref":40,"next":162,"prev":160,"to":44,"twin":175,"wire_ref":null},{"edge":80,"face":40,"from":44,"geometry_use":{"pcurve":162,"periodic_lift":[0,0],"sense":"Reverse"},"id":162,"loop_ref":40,"next":163,"prev":161,"to":43,"twin":153,"wire_ref":null},{"edge":86,"face":40,"from":43,"geometry_use":{"pcurve":163,"periodic_lift":[0,0],"sense":"Reverse"},"id":163,"loop_ref":40,"next":160,"prev":162,"to":40,"twin":164,"wire_ref":null},{"edge":86,"face":41,"from":40,"geometry_use":{"pcurve":164,"periodic_lift":[0,0],"sense":"Forward"},"id":164,"loop_ref":41,"next":165,"prev":167,"to":43,"twin":163,"wire_ref":null},{"edge":79,"face":41,"from":43,"geometry_use":{"pcurve":165,"periodic_lift":[0,0],"sense":"Reverse"},"id":165,"loop_ref":41,"next":166,"prev":164,"to":42,"twin":152,"wire_ref":null},{"edge":84,"face":41,"from":42,"geometry_use":{"pcurve":166,"periodic_lift":[0,0],"sense":"Reverse"},"id":166,"loop_ref":41,"next":167,"prev":165,"to":41,"twin":159,"wire_ref":null},{"edge":77,"face":41,"from":41,"geometry_use":{"pcurve":167,"periodic_lift":[0,0],"sense":"Forward"},"id":167,"loop_ref":41,"next":164,"prev":166,"to":40,"twin":150,"wire_ref":null},{"edge":87,"face":42,"from":45,"geometry_use":{"pcurve":168,"periodic_lift":[0,0],"sense":"Forward"},"id":168,"loop_ref":42,"next":169,"prev":171,"to":46,"twin":182,"wire_ref":null},{"edge":88,"face":42,"from":46,"geometry_use":{"pcurve":169,"periodic_lift":[0,0],"sense":"Reverse"},"id":169,"loop_ref":42,"next":170,"prev":168,"to":37,"twin":176,"wire_ref":null},{"edge":68,"face":42,"from":37,"geometry_use":{"pcurve":170,"periodic_lift":[0,0],"sense":"Reverse"},"id":170,"loop_ref":42,"next":171,"prev":169,"to":36,"twin":132,"wire_ref":null},{"edge":83,"face":42,"from":36,"geometry_use":{"pcurve":171,"periodic_lift":[0,0],"sense":"Forward"},"id":171,"loop_ref":42,"next":168,"prev":170,"to":45,"twin":157,"wire_ref":null},{"edge":72,"face":43,"from":39,"geometry_use":{"pcurve":172,"periodic_lift":[0,0],"sense":"Forward"},"id":172,"loop_ref":43,"next":173,"prev":175,"to":38,"twin":138,"wire_ref":null},{"edge":89,"face":43,"from":38,"geometry_use":{"pcurve":173,"periodic_lift":[0,0],"sense":"Forward"},"id":173,"loop_ref":43,"next":174,"prev":172,"to":47,"twin":178,"wire_ref":null},{"edge":90,"face":43,"from":47,"geometry_use":{"pcurve":174,"periodic_lift":[0,0],"sense":"Reverse"},"id":174,"loop_ref":43,"next":175,"prev":173,"to":44,"twin":184,"wire_ref":null},{"edge":85,"face":43,"from":44,"geometry_use":{"pcurve":175,"periodic_lift":[0,0],"sense":"Reverse"},"id":175,"loop_ref":43,"next":172,"prev":174,"to":39,"twin":161,"wire_ref":null},{"edge":88,"face":44,"from":37,"geometry_use":{"pcurve":176,"periodic_lift":[0,0],"sense":"Forward"},"id":176,"loop_ref":44,"next":177,"prev":179,"to":46,"twin":169,"wire_ref":null},{"edge":91,"face":44,"from":46,"geometry_use":{"pcurve":177,"periodic_lift":[0,0],"sense":"Forward"},"id":177,"loop_ref":44,"next":178,"prev":176,"to":47,"twin":195,"wire_ref":null},{"edge":89,"face":44,"from":47,"geometry_use":{"pcurve":178,"periodic_lift":[0,0],"sense":"Reverse"},"id":178,"loop_ref":44,"next":179,"prev":177,"to":38,"twin":173,"wire_ref":null},{"edge":75,"face":44,"from":38,"geometry_use":{"pcurve":179,"periodic_lift":[0,0],"sense":"Reverse"},"id":179,"loop_ref":44,"next":176,"prev":178,"to":37,"twin":145,"wire_ref":null},{"edge":92,"face":45,"from":48,"geometry_use":{"pcurve":180,"periodic_lift":[0,0],"sense":"Forward"},"id":180,"loop_ref":45,"next":181,"prev":183,"to":49,"twin":218,"wire_ref":null},{"edge":93,"face":45,"from":49,"geometry_use":{"pcurve":181,"periodic_lift":[0,0],"sense":"Reverse"},"id":181,"loop_ref":45,"next":182,"prev":180,"to":46,"twin":192,"wire_ref":null},{"edge":87,"face":45,"from":46,"geometry_use":{"pcurve":182,"periodic_lift":[0,0],"sense":"Reverse"},"id":182,"loop_ref":45,"next":183,"prev":181,"to":45,"twin":168,"wire_ref":null},{"edge":94,"face":45,"from":45,"geometry_use":{"pcurve":183,"periodic_lift":[0,0],"sense":"Forward"},"id":183,"loop_ref":45,"next":180,"prev":182,"to":48,"twin":190,"wire_ref":null},{"edge":90,"face":46,"from":44,"geometry_use":{"pcurve":184,"periodic_lift":[0,0],"sense":"Forward"},"id":184,"loop_ref":46,"next":185,"prev":187,"to":47,"twin":174,"wire_ref":null},{"edge":95,"face":46,"from":47,"geometry_use":{"pcurve":185,"periodic_lift":[0,0],"sense":"Forward"},"id":185,"loop_ref":46,"next":186,"prev":184,"to":50,"twin":194,"wire_ref":null},{"edge":96,"face":46,"from":50,"geometry_use":{"pcurve":186,"periodic_lift":[0,0],"sense":"Reverse"},"id":186,"loop_ref":46,"next":187,"prev":185,"to":51,"twin":220,"wire_ref":null},{"edge":97,"face":46,"from":51,"geometry_use":{"pcurve":187,"periodic_lift":[0,0],"sense":"Reverse"},"id":187,"loop_ref":46,"next":184,"prev":186,"to":44,"twin":188,"wire_ref":null},{"edge":97,"face":47,"from":44,"geometry_use":{"pcurve":188,"periodic_lift":[0,0],"sense":"Forward"},"id":188,"loop_ref":47,"next":189,"prev":191,"to":51,"twin":187,"wire_ref":null},{"edge":98,"face":47,"from":51,"geometry_use":{"pcurve":189,"periodic_lift":[0,0],"sense":"Reverse"},"id":189,"loop_ref":47,"next":190,"prev":188,"to":48,"twin":196,"wire_ref":null},{"edge":94,"face":47,"from":48,"geometry_use":{"pcurve":190,"periodic_lift":[0,0],"sense":"Reverse"},"id":190,"loop_ref":47,"next":191,"prev":189,"to":45,"twin":183,"wire_ref":null},{"edge":81,"face":47,"from":45,"geometry_use":{"pcurve":191,"periodic_lift":[0,0],"sense":"Forward"},"id":191,"loop_ref":47,"next":188,"prev":190,"to":44,"twin":154,"wire_ref":null},{"edge":93,"face":48,"from":46,"geometry_use":{"pcurve":192,"periodic_lift":[0,0],"sense":"Forward"},"id":192,"loop_ref":48,"next":193,"prev":195,"to":49,"twin":181,"wire_ref":null},{"edge":99,"face":48,"from":49,"geometry_use":{"pcurve":193,"periodic_lift":[0,0],"sense":"Forward"},"id":193,"loop_ref":48,"next":194,"prev":192,"to":50,"twin":227,"wire_ref":null},{"edge":95,"face":48,"from":50,"geometry_use":{"pcurve":194,"periodic_lift":[0,0],"sense":"Reverse"},"id":194,"loop_ref":48,"next":195,"prev":193,"to":47,"twin":185,"wire_ref":null},{"edge":91,"face":48,"from":47,"geometry_use":{"pcurve":195,"periodic_lift":[0,0],"sense":"Reverse"},"id":195,"loop_ref":48,"next":192,"prev":194,"to":46,"twin":177,"wire_ref":null},{"edge":98,"face":49,"from":48,"geometry_use":{"pcurve":196,"periodic_lift":[0,0],"sense":"Forward"},"id":196,"loop_ref":49,"next":197,"prev":199,"to":51,"twin":189,"wire_ref":null},{"edge":100,"face":49,"from":51,"geometry_use":{"pcurve":197,"periodic_lift":[0,0],"sense":"Reverse"},"id":197,"loop_ref":49,"next":198,"prev":196,"to":52,"twin":208,"wire_ref":null},{"edge":101,"face":49,"from":52,"geometry_use":{"pcurve":198,"periodic_lift":[0,0],"sense":"Reverse"},"id":198,"loop_ref":49,"next":199,"prev":197,"to":53,"twin":215,"wire_ref":null},{"edge":102,"face":49,"from":53,"geometry_use":{"pcurve":199,"periodic_lift":[0,0],"sense":"Forward"},"id":199,"loop_ref":49,"next":196,"prev":198,"to":48,"twin":206,"wire_ref":null},{"edge":103,"face":50,"from":54,"geometry_use":{"pcurve":200,"periodic_lift":[0,0],"sense":"Forward"},"id":200,"loop_ref":50,"next":201,"prev":203,"to":55,"twin":213,"wire_ref":null},{"edge":104,"face":50,"from":55,"geometry_use":{"pcurve":201,"periodic_lift":[0,0],"sense":"Forward"},"id":201,"loop_ref":50,"next":202,"prev":200,"to":56,"twin":210,"wire_ref":null},{"edge":105,"face":50,"from":56,"geometry_use":{"pcurve":202,"periodic_lift":[0,0],"sense":"Reverse"},"id":202,"loop_ref":50,"next":203,"prev":201,"to":57,"twin":239,"wire_ref":null},{"edge":106,"face":50,"from":57,"geometry_use":{"pcurve":203,"periodic_lift":[0,0],"sense":"Reverse"},"id":203,"loop_ref":50,"next":200,"prev":202,"to":54,"twin":204,"wire_ref":null},{"edge":106,"face":51,"from":54,"geometry_use":{"pcurve":204,"periodic_lift":[0,0],"sense":"Forward"},"id":204,"loop_ref":51,"next":205,"prev":207,"to":57,"twin":203,"wire_ref":null},{"edge":107,"face":51,"from":57,"geometry_use":{"pcurve":205,"periodic_lift":[0,0],"sense":"Reverse"},"id":205,"loop_ref":51,"next":206,"prev":204,"to":48,"twin":219,"wire_ref":null},{"edge":102,"face":51,"from":48,"geometry_use":{"pcurve":206,"periodic_lift":[0,0],"sense":"Reverse"},"id":206,"loop_ref":51,"next":207,"prev":205,"to":53,"twin":199,"wire_ref":null},{"edge":108,"face":51,"from":53,"geometry_use":{"pcurve":207,"periodic_lift":[0,0],"sense":"Forward"},"id":207,"loop_ref":51,"next":204,"prev":206,"to":54,"twin":214,"wire_ref":null},{"edge":100,"face":52,"from":52,"geometry_use":{"pcurve":208,"periodic_lift":[0,0],"sense":"Forward"},"id":208,"loop_ref":52,"next":209,"prev":211,"to":51,"twin":197,"wire_ref":null},{"edge":109,"face":52,"from":51,"geometry_use":{"pcurve":209,"periodic_lift":[0,0],"sense":"Forward"},"id":209,"loop_ref":52,"next":210,"prev":208,"to":56,"twin":223,"wire_ref":null},{"edge":104,"face":52,"from":56,"geometry_use":{"pcurve":210,"periodic_lift":[0,0],"sense":"Reverse"},"id":210,"loop_ref":52,"next":211,"prev":209,"to":55,"twin":201,"wire_ref":null},{"edge":110,"face":52,"from":55,"geometry_use":{"pcurve":211,"periodic_lift":[0,0],"sense":"Reverse"},"id":211,"loop_ref":52,"next":208,"prev":210,"to":52,"twin":212,"wire_ref":null},{"edge":110,"face":53,"from":52,"geometry_use":{"pcurve":212,"periodic_lift":[0,0],"sense":"Forward"},"id":212,"loop_ref":53,"next":213,"prev":215,"to":55,"twin":211,"wire_ref":null},{"edge":103,"face":53,"from":55,"geometry_use":{"pcurve":213,"periodic_lift":[0,0],"sense":"Reverse"},"id":213,"loop_ref":53,"next":214,"prev":212,"to":54,"twin":200,"wire_ref":null},{"edge":108,"face":53,"from":54,"geometry_use":{"pcurve":214,"periodic_lift":[0,0],"sense":"Reverse"},"id":214,"loop_ref":53,"next":215,"prev":213,"to":53,"twin":207,"wire_ref":null},{"edge":101,"face":53,"from":53,"geometry_use":{"pcurve":215,"periodic_lift":[0,0],"sense":"Forward"},"id":215,"loop_ref":53,"next":212,"prev":214,"to":52,"twin":198,"wire_ref":null},{"edge":111,"face":54,"from":57,"geometry_use":{"pcurve":216,"periodic_lift":[0,0],"sense":"Forward"},"id":216,"loop_ref":54,"next":217,"prev":219,"to":58,"twin":230,"wire_ref":null},{"edge":112,"face":54,"from":58,"geometry_use":{"pcurve":217,"periodic_lift":[0,0],"sense":"Reverse"},"id":217,"loop_ref":54,"next":218,"prev":216,"to":49,"twin":224,"wire_ref":null},{"edge":92,"face":54,"from":49,"geometry_use":{"pcurve":218,"periodic_lift":[0,0],"sense":"Reverse"},"id":218,"loop_ref":54,"next":219,"prev":217,"to":48,"twin":180,"wire_ref":null},{"edge":107,"face":54,"from":48,"geometry_use":{"pcurve":219,"periodic_lift":[0,0],"sense":"Forward"},"id":219,"loop_ref":54,"next":216,"prev":218,"to":57,"twin":205,"wire_ref":null},{"edge":96,"face":55,"from":51,"geometry_use":{"pcurve":220,"periodic_lift":[0,0],"sense":"Forward"},"id":220,"loop_ref":55,"next":221,"prev":223,"to":50,"twin":186,"wire_ref":null},{"edge":113,"face":55,"from":50,"geometry_use":{"pcurve":221,"periodic_lift":[0,0],"sense":"Forward"},"id":221,"loop_ref":55,"next":222,"prev":220,"to":59,"twin":226,"wire_ref":null},{"edge":114,"face":55,"from":59,"geometry_use":{"pcurve":222,"periodic_lift":[0,0],"sense":"Reverse"},"id":222,"loop_ref":55,"next":223,"prev":221,"to":56,"twin":232,"wire_ref":null},{"edge":109,"face":55,"from":56,"geometry_use":{"pcurve":223,"periodic_lift":[0,0],"sense":"Reverse"},"id":223,"loop_ref":55,"next":220,"prev":222,"to":51,"twin":209,"wire_ref":null},{"edge":112,"face":56,"from":49,"geometry_use":{"pcurve":224,"periodic_lift":[0,0],"sense":"Forward"},"id":224,"loop_ref":56,"next":225,"prev":227,"to":58,"twin":217,"wire_ref":null},{"edge":115,"face":56,"from":58,"geometry_use":{"pcurve":225,"periodic_lift":[0,0],"sense":"Forward"},"id":225,"loop_ref":56,"next":226,"prev":224,"to":59,"twin":243,"wire_ref":null},{"edge":113,"face":56,"from":59,"geometry_use":{"pcurve":226,"periodic_lift":[0,0],"sense":"Reverse"},"id":226,"loop_ref":56,"next":227,"prev":225,"to":50,"twin":221,"wire_ref":null},{"edge":99,"face":56,"from":50,"geometry_use":{"pcurve":227,"periodic_lift":[0,0],"sense":"Reverse"},"id":227,"loop_ref":56,"next":224,"prev":226,"to":49,"twin":193,"wire_ref":null},{"edge":116,"face":57,"from":60,"geometry_use":{"pcurve":228,"periodic_lift":[0,0],"sense":"Forward"},"id":228,"loop_ref":57,"next":229,"prev":231,"to":61,"twin":266,"wire_ref":null},{"edge":117,"face":57,"from":61,"geometry_use":{"pcurve":229,"periodic_lift":[0,0],"sense":"Reverse"},"id":229,"loop_ref":57,"next":230,"prev":228,"to":58,"twin":240,"wire_ref":null},{"edge":111,"face":57,"from":58,"geometry_use":{"pcurve":230,"periodic_lift":[0,0],"sense":"Reverse"},"id":230,"loop_ref":57,"next":231,"prev":229,"to":57,"twin":216,"wire_ref":null},{"edge":118,"face":57,"from":57,"geometry_use":{"pcurve":231,"periodic_lift":[0,0],"sense":"Forward"},"id":231,"loop_ref":57,"next":228,"prev":230,"to":60,"twin":238,"wire_ref":null},{"edge":114,"face":58,"from":56,"geometry_use":{"pcurve":232,"periodic_lift":[0,0],"sense":"Forward"},"id":232,"loop_ref":58,"next":233,"prev":235,"to":59,"twin":222,"wire_ref":null},{"edge":119,"face":58,"from":59,"geometry_use":{"pcurve":233,"periodic_lift":[0,0],"sense":"Forward"},"id":233,"loop_ref":58,"next":234,"prev":232,"to":62,"twin":242,"wire_ref":null},{"edge":120,"face":58,"from":62,"geometry_use":{"pcurve":234,"periodic_lift":[0,0],"sense":"Reverse"},"id":234,"loop_ref":58,"next":235,"prev":233,"to":63,"twin":268,"wire_ref":null},{"edge":121,"face":58,"from":63,"geometry_use":{"pcurve":235,"periodic_lift":[0,0],"sense":"Reverse"},"id":235,"loop_ref":58,"next":232,"prev":234,"to":56,"twin":236,"wire_ref":null},{"edge":121,"face":59,"from":56,"geometry_use":{"pcurve":236,"periodic_lift":[0,0],"sense":"Forward"},"id":236,"loop_ref":59,"next":237,"prev":239,"to":63,"twin":235,"wire_ref":null},{"edge":122,"face":59,"from":63,"geometry_use":{"pcurve":237,"periodic_lift":[0,0],"sense":"Reverse"},"id":237,"loop_ref":59,"next":238,"prev":236,"to":60,"twin":244,"wire_ref":null},{"edge":118,"face":59,"from":60,"geometry_use":{"pcurve":238,"periodic_lift":[0,0],"sense":"Reverse"},"id":238,"loop_ref":59,"next":239,"prev":237,"to":57,"twin":231,"wire_ref":null},{"edge":105,"face":59,"from":57,"geometry_use":{"pcurve":239,"periodic_lift":[0,0],"sense":"Forward"},"id":239,"loop_ref":59,"next":236,"prev":238,"to":56,"twin":202,"wire_ref":null},{"edge":117,"face":60,"from":58,"geometry_use":{"pcurve":240,"periodic_lift":[0,0],"sense":"Forward"},"id":240,"loop_ref":60,"next":241,"prev":243,"to":61,"twin":229,"wire_ref":null},{"edge":123,"face":60,"from":61,"geometry_use":{"pcurve":241,"periodic_lift":[0,0],"sense":"Forward"},"id":241,"loop_ref":60,"next":242,"prev":240,"to":62,"twin":275,"wire_ref":null},{"edge":119,"face":60,"from":62,"geometry_use":{"pcurve":242,"periodic_lift":[0,0],"sense":"Reverse"},"id":242,"loop_ref":60,"next":243,"prev":241,"to":59,"twin":233,"wire_ref":null},{"edge":115,"face":60,"from":59,"geometry_use":{"pcurve":243,"periodic_lift":[0,0],"sense":"Reverse"},"id":243,"loop_ref":60,"next":240,"prev":242,"to":58,"twin":225,"wire_ref":null},{"edge":122,"face":61,"from":60,"geometry_use":{"pcurve":244,"periodic_lift":[0,0],"sense":"Forward"},"id":244,"loop_ref":61,"next":245,"prev":247,"to":63,"twin":237,"wire_ref":null},{"edge":124,"face":61,"from":63,"geometry_use":{"pcurve":245,"periodic_lift":[0,0],"sense":"Reverse"},"id":245,"loop_ref":61,"next":246,"prev":244,"to":64,"twin":256,"wire_ref":null},{"edge":125,"face":61,"from":64,"geometry_use":{"pcurve":246,"periodic_lift":[0,0],"sense":"Reverse"},"id":246,"loop_ref":61,"next":247,"prev":245,"to":65,"twin":263,"wire_ref":null},{"edge":126,"face":61,"from":65,"geometry_use":{"pcurve":247,"periodic_lift":[0,0],"sense":"Forward"},"id":247,"loop_ref":61,"next":244,"prev":246,"to":60,"twin":254,"wire_ref":null},{"edge":127,"face":62,"from":66,"geometry_use":{"pcurve":248,"periodic_lift":[0,0],"sense":"Forward"},"id":248,"loop_ref":62,"next":249,"prev":251,"to":67,"twin":261,"wire_ref":null},{"edge":128,"face":62,"from":67,"geometry_use":{"pcurve":249,"periodic_lift":[0,0],"sense":"Forward"},"id":249,"loop_ref":62,"next":250,"prev":248,"to":68,"twin":258,"wire_ref":null},{"edge":129,"face":62,"from":68,"geometry_use":{"pcurve":250,"periodic_lift":[0,0],"sense":"Reverse"},"id":250,"loop_ref":62,"next":251,"prev":249,"to":69,"twin":287,"wire_ref":null},{"edge":130,"face":62,"from":69,"geometry_use":{"pcurve":251,"periodic_lift":[0,0],"sense":"Reverse"},"id":251,"loop_ref":62,"next":248,"prev":250,"to":66,"twin":252,"wire_ref":null},{"edge":130,"face":63,"from":66,"geometry_use":{"pcurve":252,"periodic_lift":[0,0],"sense":"Forward"},"id":252,"loop_ref":63,"next":253,"prev":255,"to":69,"twin":251,"wire_ref":null},{"edge":131,"face":63,"from":69,"geometry_use":{"pcurve":253,"periodic_lift":[0,0],"sense":"Reverse"},"id":253,"loop_ref":63,"next":254,"prev":252,"to":60,"twin":267,"wire_ref":null},{"edge":126,"face":63,"from":60,"geometry_use":{"pcurve":254,"periodic_lift":[0,0],"sense":"Reverse"},"id":254,"loop_ref":63,"next":255,"prev":253,"to":65,"twin":247,"wire_ref":null},{"edge":132,"face":63,"from":65,"geometry_use":{"pcurve":255,"periodic_lift":[0,0],"sense":"Forward"},"id":255,"loop_ref":63,"next":252,"prev":254,"to":66,"twin":262,"wire_ref":null},{"edge":124,"face":64,"from":64,"geometry_use":{"pcurve":256,"periodic_lift":[0,0],"sense":"Forward"},"id":256,"loop_ref":64,"next":257,"prev":259,"to":63,"twin":245,"wire_ref":null},{"edge":133,"face":64,"from":63,"geometry_use":{"pcurve":257,"periodic_lift":[0,0],"sense":"Forward"},"id":257,"loop_ref":64,"next":258,"prev":256,"to":68,"twin":271,"wire_ref":null},{"edge":128,"face":64,"from":68,"geometry_use":{"pcurve":258,"periodic_lift":[0,0],"sense":"Reverse"},"id":258,"loop_ref":64,"next":259,"prev":257,"to":67,"twin":249,"wire_ref":null},{"edge":134,"face":64,"from":67,"geometry_use":{"pcurve":259,"periodic_lift":[0,0],"sense":"Reverse"},"id":259,"loop_ref":64,"next":256,"prev":258,"to":64,"twin":260,"wire_ref":null},{"edge":134,"face":65,"from":64,"geometry_use":{"pcurve":260,"periodic_lift":[0,0],"sense":"Forward"},"id":260,"loop_ref":65,"next":261,"prev":263,"to":67,"twin":259,"wire_ref":null},{"edge":127,"face":65,"from":67,"geometry_use":{"pcurve":261,"periodic_lift":[0,0],"sense":"Reverse"},"id":261,"loop_ref":65,"next":262,"prev":260,"to":66,"twin":248,"wire_ref":null},{"edge":132,"face":65,"from":66,"geometry_use":{"pcurve":262,"periodic_lift":[0,0],"sense":"Reverse"},"id":262,"loop_ref":65,"next":263,"prev":261,"to":65,"twin":255,"wire_ref":null},{"edge":125,"face":65,"from":65,"geometry_use":{"pcurve":263,"periodic_lift":[0,0],"sense":"Forward"},"id":263,"loop_ref":65,"next":260,"prev":262,"to":64,"twin":246,"wire_ref":null},{"edge":135,"face":66,"from":69,"geometry_use":{"pcurve":264,"periodic_lift":[0,0],"sense":"Forward"},"id":264,"loop_ref":66,"next":265,"prev":267,"to":70,"twin":278,"wire_ref":null},{"edge":136,"face":66,"from":70,"geometry_use":{"pcurve":265,"periodic_lift":[0,0],"sense":"Reverse"},"id":265,"loop_ref":66,"next":266,"prev":264,"to":61,"twin":272,"wire_ref":null},{"edge":116,"face":66,"from":61,"geometry_use":{"pcurve":266,"periodic_lift":[0,0],"sense":"Reverse"},"id":266,"loop_ref":66,"next":267,"prev":265,"to":60,"twin":228,"wire_ref":null},{"edge":131,"face":66,"from":60,"geometry_use":{"pcurve":267,"periodic_lift":[0,0],"sense":"Forward"},"id":267,"loop_ref":66,"next":264,"prev":266,"to":69,"twin":253,"wire_ref":null},{"edge":120,"face":67,"from":63,"geometry_use":{"pcurve":268,"periodic_lift":[0,0],"sense":"Forward"},"id":268,"loop_ref":67,"next":269,"prev":271,"to":62,"twin":234,"wire_ref":null},{"edge":137,"face":67,"from":62,"geometry_use":{"pcurve":269,"periodic_lift":[0,0],"sense":"Forward"},"id":269,"loop_ref":67,"next":270,"prev":268,"to":71,"twin":274,"wire_ref":null},{"edge":138,"face":67,"from":71,"geometry_use":{"pcurve":270,"periodic_lift":[0,0],"sense":"Reverse"},"id":270,"loop_ref":67,"next":271,"prev":269,"to":68,"twin":280,"wire_ref":null},{"edge":133,"face":67,"from":68,"geometry_use":{"pcurve":271,"periodic_lift":[0,0],"sense":"Reverse"},"id":271,"loop_ref":67,"next":268,"prev":270,"to":63,"twin":257,"wire_ref":null},{"edge":136,"face":68,"from":61,"geometry_use":{"pcurve":272,"periodic_lift":[0,0],"sense":"Forward"},"id":272,"loop_ref":68,"next":273,"prev":275,"to":70,"twin":265,"wire_ref":null},{"edge":139,"face":68,"from":70,"geometry_use":{"pcurve":273,"periodic_lift":[0,0],"sense":"Forward"},"id":273,"loop_ref":68,"next":274,"prev":272,"to":71,"twin":291,"wire_ref":null},{"edge":137,"face":68,"from":71,"geometry_use":{"pcurve":274,"periodic_lift":[0,0],"sense":"Reverse"},"id":274,"loop_ref":68,"next":275,"prev":273,"to":62,"twin":269,"wire_ref":null},{"edge":123,"face":68,"from":62,"geometry_use":{"pcurve":275,"periodic_lift":[0,0],"sense":"Reverse"},"id":275,"loop_ref":68,"next":272,"prev":274,"to":61,"twin":241,"wire_ref":null},{"edge":140,"face":69,"from":72,"geometry_use":{"pcurve":276,"periodic_lift":[0,0],"sense":"Forward"},"id":276,"loop_ref":69,"next":277,"prev":279,"to":73,"twin":314,"wire_ref":null},{"edge":141,"face":69,"from":73,"geometry_use":{"pcurve":277,"periodic_lift":[0,0],"sense":"Reverse"},"id":277,"loop_ref":69,"next":278,"prev":276,"to":70,"twin":288,"wire_ref":null},{"edge":135,"face":69,"from":70,"geometry_use":{"pcurve":278,"periodic_lift":[0,0],"sense":"Reverse"},"id":278,"loop_ref":69,"next":279,"prev":277,"to":69,"twin":264,"wire_ref":null},{"edge":142,"face":69,"from":69,"geometry_use":{"pcurve":279,"periodic_lift":[0,0],"sense":"Forward"},"id":279,"loop_ref":69,"next":276,"prev":278,"to":72,"twin":286,"wire_ref":null},{"edge":138,"face":70,"from":68,"geometry_use":{"pcurve":280,"periodic_lift":[0,0],"sense":"Forward"},"id":280,"loop_ref":70,"next":281,"prev":283,"to":71,"twin":270,"wire_ref":null},{"edge":143,"face":70,"from":71,"geometry_use":{"pcurve":281,"periodic_lift":[0,0],"sense":"Forward"},"id":281,"loop_ref":70,"next":282,"prev":280,"to":74,"twin":290,"wire_ref":null},{"edge":144,"face":70,"from":74,"geometry_use":{"pcurve":282,"periodic_lift":[0,0],"sense":"Reverse"},"id":282,"loop_ref":70,"next":283,"prev":281,"to":75,"twin":316,"wire_ref":null},{"edge":145,"face":70,"from":75,"geometry_use":{"pcurve":283,"periodic_lift":[0,0],"sense":"Reverse"},"id":283,"loop_ref":70,"next":280,"prev":282,"to":68,"twin":284,"wire_ref":null},{"edge":145,"face":71,"from":68,"geometry_use":{"pcurve":284,"periodic_lift":[0,0],"sense":"Forward"},"id":284,"loop_ref":71,"next":285,"prev":287,"to":75,"twin":283,"wire_ref":null},{"edge":146,"face":71,"from":75,"geometry_use":{"pcurve":285,"periodic_lift":[0,0],"sense":"Reverse"},"id":285,"loop_ref":71,"next":286,"prev":284,"to":72,"twin":292,"wire_ref":null},{"edge":142,"face":71,"from":72,"geometry_use":{"pcurve":286,"periodic_lift":[0,0],"sense":"Reverse"},"id":286,"loop_ref":71,"next":287,"prev":285,"to":69,"twin":279,"wire_ref":null},{"edge":129,"face":71,"from":69,"geometry_use":{"pcurve":287,"periodic_lift":[0,0],"sense":"Forward"},"id":287,"loop_ref":71,"next":284,"prev":286,"to":68,"twin":250,"wire_ref":null},{"edge":141,"face":72,"from":70,"geometry_use":{"pcurve":288,"periodic_lift":[0,0],"sense":"Forward"},"id":288,"loop_ref":72,"next":289,"prev":291,"to":73,"twin":277,"wire_ref":null},{"edge":147,"face":72,"from":73,"geometry_use":{"pcurve":289,"periodic_lift":[0,0],"sense":"Forward"},"id":289,"loop_ref":72,"next":290,"prev":288,"to":74,"twin":323,"wire_ref":null},{"edge":143,"face":72,"from":74,"geometry_use":{"pcurve":290,"periodic_lift":[0,0],"sense":"Reverse"},"id":290,"loop_ref":72,"next":291,"prev":289,"to":71,"twin":281,"wire_ref":null},{"edge":139,"face":72,"from":71,"geometry_use":{"pcurve":291,"periodic_lift":[0,0],"sense":"Reverse"},"id":291,"loop_ref":72,"next":288,"prev":290,"to":70,"twin":273,"wire_ref":null},{"edge":146,"face":73,"from":72,"geometry_use":{"pcurve":292,"periodic_lift":[0,0],"sense":"Forward"},"id":292,"loop_ref":73,"next":293,"prev":295,"to":75,"twin":285,"wire_ref":null},{"edge":148,"face":73,"from":75,"geometry_use":{"pcurve":293,"periodic_lift":[0,0],"sense":"Reverse"},"id":293,"loop_ref":73,"next":294,"prev":292,"to":76,"twin":304,"wire_ref":null},{"edge":149,"face":73,"from":76,"geometry_use":{"pcurve":294,"periodic_lift":[0,0],"sense":"Reverse"},"id":294,"loop_ref":73,"next":295,"prev":293,"to":77,"twin":311,"wire_ref":null},{"edge":150,"face":73,"from":77,"geometry_use":{"pcurve":295,"periodic_lift":[0,0],"sense":"Forward"},"id":295,"loop_ref":73,"next":292,"prev":294,"to":72,"twin":302,"wire_ref":null},{"edge":151,"face":74,"from":78,"geometry_use":{"pcurve":296,"periodic_lift":[0,0],"sense":"Forward"},"id":296,"loop_ref":74,"next":297,"prev":299,"to":79,"twin":309,"wire_ref":null},{"edge":152,"face":74,"from":79,"geometry_use":{"pcurve":297,"periodic_lift":[0,0],"sense":"Forward"},"id":297,"loop_ref":74,"next":298,"prev":296,"to":80,"twin":306,"wire_ref":null},{"edge":153,"face":74,"from":80,"geometry_use":{"pcurve":298,"periodic_lift":[0,0],"sense":"Reverse"},"id":298,"loop_ref":74,"next":299,"prev":297,"to":81,"twin":335,"wire_ref":null},{"edge":154,"face":74,"from":81,"geometry_use":{"pcurve":299,"periodic_lift":[0,0],"sense":"Reverse"},"id":299,"loop_ref":74,"next":296,"prev":298,"to":78,"twin":300,"wire_ref":null},{"edge":154,"face":75,"from":78,"geometry_use":{"pcurve":300,"periodic_lift":[0,0],"sense":"Forward"},"id":300,"loop_ref":75,"next":301,"prev":303,"to":81,"twin":299,"wire_ref":null},{"edge":155,"face":75,"from":81,"geometry_use":{"pcurve":301,"periodic_lift":[0,0],"sense":"Reverse"},"id":301,"loop_ref":75,"next":302,"prev":300,"to":72,"twin":315,"wire_ref":null},{"edge":150,"face":75,"from":72,"geometry_use":{"pcurve":302,"periodic_lift":[0,0],"sense":"Reverse"},"id":302,"loop_ref":75,"next":303,"prev":301,"to":77,"twin":295,"wire_ref":null},{"edge":156,"face":75,"from":77,"geometry_use":{"pcurve":303,"periodic_lift":[0,0],"sense":"Forward"},"id":303,"loop_ref":75,"next":300,"prev":302,"to":78,"twin":310,"wire_ref":null},{"edge":148,"face":76,"from":76,"geometry_use":{"pcurve":304,"periodic_lift":[0,0],"sense":"Forward"},"id":304,"loop_ref":76,"next":305,"prev":307,"to":75,"twin":293,"wire_ref":null},{"edge":157,"face":76,"from":75,"geometry_use":{"pcurve":305,"periodic_lift":[0,0],"sense":"Forward"},"id":305,"loop_ref":76,"next":306,"prev":304,"to":80,"twin":319,"wire_ref":null},{"edge":152,"face":76,"from":80,"geometry_use":{"pcurve":306,"periodic_lift":[0,0],"sense":"Reverse"},"id":306,"loop_ref":76,"next":307,"prev":305,"to":79,"twin":297,"wire_ref":null},{"edge":158,"face":76,"from":79,"geometry_use":{"pcurve":307,"periodic_lift":[0,0],"sense":"Reverse"},"id":307,"loop_ref":76,"next":304,"prev":306,"to":76,"twin":308,"wire_ref":null},{"edge":158,"face":77,"from":76,"geometry_use":{"pcurve":308,"periodic_lift":[0,0],"sense":"Forward"},"id":308,"loop_ref":77,"next":309,"prev":311,"to":79,"twin":307,"wire_ref":null},{"edge":151,"face":77,"from":79,"geometry_use":{"pcurve":309,"periodic_lift":[0,0],"sense":"Reverse"},"id":309,"loop_ref":77,"next":310,"prev":308,"to":78,"twin":296,"wire_ref":null},{"edge":156,"face":77,"from":78,"geometry_use":{"pcurve":310,"periodic_lift":[0,0],"sense":"Reverse"},"id":310,"loop_ref":77,"next":311,"prev":309,"to":77,"twin":303,"wire_ref":null},{"edge":149,"face":77,"from":77,"geometry_use":{"pcurve":311,"periodic_lift":[0,0],"sense":"Forward"},"id":311,"loop_ref":77,"next":308,"prev":310,"to":76,"twin":294,"wire_ref":null},{"edge":159,"face":78,"from":81,"geometry_use":{"pcurve":312,"periodic_lift":[0,0],"sense":"Forward"},"id":312,"loop_ref":78,"next":313,"prev":315,"to":82,"twin":326,"wire_ref":null},{"edge":160,"face":78,"from":82,"geometry_use":{"pcurve":313,"periodic_lift":[0,0],"sense":"Reverse"},"id":313,"loop_ref":78,"next":314,"prev":312,"to":73,"twin":320,"wire_ref":null},{"edge":140,"face":78,"from":73,"geometry_use":{"pcurve":314,"periodic_lift":[0,0],"sense":"Reverse"},"id":314,"loop_ref":78,"next":315,"prev":313,"to":72,"twin":276,"wire_ref":null},{"edge":155,"face":78,"from":72,"geometry_use":{"pcurve":315,"periodic_lift":[0,0],"sense":"Forward"},"id":315,"loop_ref":78,"next":312,"prev":314,"to":81,"twin":301,"wire_ref":null},{"edge":144,"face":79,"from":75,"geometry_use":{"pcurve":316,"periodic_lift":[0,0],"sense":"Forward"},"id":316,"loop_ref":79,"next":317,"prev":319,"to":74,"twin":282,"wire_ref":null},{"edge":161,"face":79,"from":74,"geometry_use":{"pcurve":317,"periodic_lift":[0,0],"sense":"Forward"},"id":317,"loop_ref":79,"next":318,"prev":316,"to":83,"twin":322,"wire_ref":null},{"edge":162,"face":79,"from":83,"geometry_use":{"pcurve":318,"periodic_lift":[0,0],"sense":"Reverse"},"id":318,"loop_ref":79,"next":319,"prev":317,"to":80,"twin":328,"wire_ref":null},{"edge":157,"face":79,"from":80,"geometry_use":{"pcurve":319,"periodic_lift":[0,0],"sense":"Reverse"},"id":319,"loop_ref":79,"next":316,"prev":318,"to":75,"twin":305,"wire_ref":null},{"edge":160,"face":80,"from":73,"geometry_use":{"pcurve":320,"periodic_lift":[0,0],"sense":"Forward"},"id":320,"loop_ref":80,"next":321,"prev":323,"to":82,"twin":313,"wire_ref":null},{"edge":163,"face":80,"from":82,"geometry_use":{"pcurve":321,"periodic_lift":[0,0],"sense":"Forward"},"id":321,"loop_ref":80,"next":322,"prev":320,"to":83,"twin":339,"wire_ref":null},{"edge":161,"face":80,"from":83,"geometry_use":{"pcurve":322,"periodic_lift":[0,0],"sense":"Reverse"},"id":322,"loop_ref":80,"next":323,"prev":321,"to":74,"twin":317,"wire_ref":null},{"edge":147,"face":80,"from":74,"geometry_use":{"pcurve":323,"periodic_lift":[0,0],"sense":"Reverse"},"id":323,"loop_ref":80,"next":320,"prev":322,"to":73,"twin":289,"wire_ref":null},{"edge":164,"face":81,"from":84,"geometry_use":{"pcurve":324,"periodic_lift":[0,0],"sense":"Forward"},"id":324,"loop_ref":81,"next":325,"prev":327,"to":85,"twin":362,"wire_ref":null},{"edge":165,"face":81,"from":85,"geometry_use":{"pcurve":325,"periodic_lift":[0,0],"sense":"Reverse"},"id":325,"loop_ref":81,"next":326,"prev":324,"to":82,"twin":336,"wire_ref":null},{"edge":159,"face":81,"from":82,"geometry_use":{"pcurve":326,"periodic_lift":[0,0],"sense":"Reverse"},"id":326,"loop_ref":81,"next":327,"prev":325,"to":81,"twin":312,"wire_ref":null},{"edge":166,"face":81,"from":81,"geometry_use":{"pcurve":327,"periodic_lift":[0,0],"sense":"Forward"},"id":327,"loop_ref":81,"next":324,"prev":326,"to":84,"twin":334,"wire_ref":null},{"edge":162,"face":82,"from":80,"geometry_use":{"pcurve":328,"periodic_lift":[0,0],"sense":"Forward"},"id":328,"loop_ref":82,"next":329,"prev":331,"to":83,"twin":318,"wire_ref":null},{"edge":167,"face":82,"from":83,"geometry_use":{"pcurve":329,"periodic_lift":[0,0],"sense":"Forward"},"id":329,"loop_ref":82,"next":330,"prev":328,"to":86,"twin":338,"wire_ref":null},{"edge":168,"face":82,"from":86,"geometry_use":{"pcurve":330,"periodic_lift":[0,0],"sense":"Reverse"},"id":330,"loop_ref":82,"next":331,"prev":329,"to":87,"twin":364,"wire_ref":null},{"edge":169,"face":82,"from":87,"geometry_use":{"pcurve":331,"periodic_lift":[0,0],"sense":"Reverse"},"id":331,"loop_ref":82,"next":328,"prev":330,"to":80,"twin":332,"wire_ref":null},{"edge":169,"face":83,"from":80,"geometry_use":{"pcurve":332,"periodic_lift":[0,0],"sense":"Forward"},"id":332,"loop_ref":83,"next":333,"prev":335,"to":87,"twin":331,"wire_ref":null},{"edge":170,"face":83,"from":87,"geometry_use":{"pcurve":333,"periodic_lift":[0,0],"sense":"Reverse"},"id":333,"loop_ref":83,"next":334,"prev":332,"to":84,"twin":340,"wire_ref":null},{"edge":166,"face":83,"from":84,"geometry_use":{"pcurve":334,"periodic_lift":[0,0],"sense":"Reverse"},"id":334,"loop_ref":83,"next":335,"prev":333,"to":81,"twin":327,"wire_ref":null},{"edge":153,"face":83,"from":81,"geometry_use":{"pcurve":335,"periodic_lift":[0,0],"sense":"Forward"},"id":335,"loop_ref":83,"next":332,"prev":334,"to":80,"twin":298,"wire_ref":null},{"edge":165,"face":84,"from":82,"geometry_use":{"pcurve":336,"periodic_lift":[0,0],"sense":"Forward"},"id":336,"loop_ref":84,"next":337,"prev":339,"to":85,"twin":325,"wire_ref":null},{"edge":171,"face":84,"from":85,"geometry_use":{"pcurve":337,"periodic_lift":[0,0],"sense":"Forward"},"id":337,"loop_ref":84,"next":338,"prev":336,"to":86,"twin":371,"wire_ref":null},{"edge":167,"face":84,"from":86,"geometry_use":{"pcurve":338,"periodic_lift":[0,0],"sense":"Reverse"},"id":338,"loop_ref":84,"next":339,"prev":337,"to":83,"twin":329,"wire_ref":null},{"edge":163,"face":84,"from":83,"geometry_use":{"pcurve":339,"periodic_lift":[0,0],"sense":"Reverse"},"id":339,"loop_ref":84,"next":336,"prev":338,"to":82,"twin":321,"wire_ref":null},{"edge":170,"face":85,"from":84,"geometry_use":{"pcurve":340,"periodic_lift":[0,0],"sense":"Forward"},"id":340,"loop_ref":85,"next":341,"prev":343,"to":87,"twin":333,"wire_ref":null},{"edge":172,"face":85,"from":87,"geometry_use":{"pcurve":341,"periodic_lift":[0,0],"sense":"Reverse"},"id":341,"loop_ref":85,"next":342,"prev":340,"to":88,"twin":352,"wire_ref":null},{"edge":173,"face":85,"from":88,"geometry_use":{"pcurve":342,"periodic_lift":[0,0],"sense":"Reverse"},"id":342,"loop_ref":85,"next":343,"prev":341,"to":89,"twin":359,"wire_ref":null},{"edge":174,"face":85,"from":89,"geometry_use":{"pcurve":343,"periodic_lift":[0,0],"sense":"Forward"},"id":343,"loop_ref":85,"next":340,"prev":342,"to":84,"twin":350,"wire_ref":null},{"edge":175,"face":86,"from":90,"geometry_use":{"pcurve":344,"periodic_lift":[0,0],"sense":"Forward"},"id":344,"loop_ref":86,"next":345,"prev":347,"to":91,"twin":357,"wire_ref":null},{"edge":176,"face":86,"from":91,"geometry_use":{"pcurve":345,"periodic_lift":[0,0],"sense":"Forward"},"id":345,"loop_ref":86,"next":346,"prev":344,"to":92,"twin":354,"wire_ref":null},{"edge":177,"face":86,"from":92,"geometry_use":{"pcurve":346,"periodic_lift":[0,0],"sense":"Reverse"},"id":346,"loop_ref":86,"next":347,"prev":345,"to":93,"twin":383,"wire_ref":null},{"edge":178,"face":86,"from":93,"geometry_use":{"pcurve":347,"periodic_lift":[0,0],"sense":"Reverse"},"id":347,"loop_ref":86,"next":344,"prev":346,"to":90,"twin":348,"wire_ref":null},{"edge":178,"face":87,"from":90,"geometry_use":{"pcurve":348,"periodic_lift":[0,0],"sense":"Forward"},"id":348,"loop_ref":87,"next":349,"prev":351,"to":93,"twin":347,"wire_ref":null},{"edge":179,"face":87,"from":93,"geometry_use":{"pcurve":349,"periodic_lift":[0,0],"sense":"Reverse"},"id":349,"loop_ref":87,"next":350,"prev":348,"to":84,"twin":363,"wire_ref":null},{"edge":174,"face":87,"from":84,"geometry_use":{"pcurve":350,"periodic_lift":[0,0],"sense":"Reverse"},"id":350,"loop_ref":87,"next":351,"prev":349,"to":89,"twin":343,"wire_ref":null},{"edge":180,"face":87,"from":89,"geometry_use":{"pcurve":351,"periodic_lift":[0,0],"sense":"Forward"},"id":351,"loop_ref":87,"next":348,"prev":350,"to":90,"twin":358,"wire_ref":null},{"edge":172,"face":88,"from":88,"geometry_use":{"pcurve":352,"periodic_lift":[0,0],"sense":"Forward"},"id":352,"loop_ref":88,"next":353,"prev":355,"to":87,"twin":341,"wire_ref":null},{"edge":181,"face":88,"from":87,"geometry_use":{"pcurve":353,"periodic_lift":[0,0],"sense":"Forward"},"id":353,"loop_ref":88,"next":354,"prev":352,"to":92,"twin":367,"wire_ref":null},{"edge":176,"face":88,"from":92,"geometry_use":{"pcurve":354,"periodic_lift":[0,0],"sense":"Reverse"},"id":354,"loop_ref":88,"next":355,"prev":353,"to":91,"twin":345,"wire_ref":null},{"edge":182,"face":88,"from":91,"geometry_use":{"pcurve":355,"periodic_lift":[0,0],"sense":"Reverse"},"id":355,"loop_ref":88,"next":352,"prev":354,"to":88,"twin":356,"wire_ref":null},{"edge":182,"face":89,"from":88,"geometry_use":{"pcurve":356,"periodic_lift":[0,0],"sense":"Forward"},"id":356,"loop_ref":89,"next":357,"prev":359,"to":91,"twin":355,"wire_ref":null},{"edge":175,"face":89,"from":91,"geometry_use":{"pcurve":357,"periodic_lift":[0,0],"sense":"Reverse"},"id":357,"loop_ref":89,"next":358,"prev":356,"to":90,"twin":344,"wire_ref":null},{"edge":180,"face":89,"from":90,"geometry_use":{"pcurve":358,"periodic_lift":[0,0],"sense":"Reverse"},"id":358,"loop_ref":89,"next":359,"prev":357,"to":89,"twin":351,"wire_ref":null},{"edge":173,"face":89,"from":89,"geometry_use":{"pcurve":359,"periodic_lift":[0,0],"sense":"Forward"},"id":359,"loop_ref":89,"next":356,"prev":358,"to":88,"twin":342,"wire_ref":null},{"edge":183,"face":90,"from":93,"geometry_use":{"pcurve":360,"periodic_lift":[0,0],"sense":"Forward"},"id":360,"loop_ref":90,"next":361,"prev":363,"to":94,"twin":374,"wire_ref":null},{"edge":184,"face":90,"from":94,"geometry_use":{"pcurve":361,"periodic_lift":[0,0],"sense":"Reverse"},"id":361,"loop_ref":90,"next":362,"prev":360,"to":85,"twin":368,"wire_ref":null},{"edge":164,"face":90,"from":85,"geometry_use":{"pcurve":362,"periodic_lift":[0,0],"sense":"Reverse"},"id":362,"loop_ref":90,"next":363,"prev":361,"to":84,"twin":324,"wire_ref":null},{"edge":179,"face":90,"from":84,"geometry_use":{"pcurve":363,"periodic_lift":[0,0],"sense":"Forward"},"id":363,"loop_ref":90,"next":360,"prev":362,"to":93,"twin":349,"wire_ref":null},{"edge":168,"face":91,"from":87,"geometry_use":{"pcurve":364,"periodic_lift":[0,0],"sense":"Forward"},"id":364,"loop_ref":91,"next":365,"prev":367,"to":86,"twin":330,"wire_ref":null},{"edge":185,"face":91,"from":86,"geometry_use":{"pcurve":365,"periodic_lift":[0,0],"sense":"Forward"},"id":365,"loop_ref":91,"next":366,"prev":364,"to":95,"twin":370,"wire_ref":null},{"edge":186,"face":91,"from":95,"geometry_use":{"pcurve":366,"periodic_lift":[0,0],"sense":"Reverse"},"id":366,"loop_ref":91,"next":367,"prev":365,"to":92,"twin":376,"wire_ref":null},{"edge":181,"face":91,"from":92,"geometry_use":{"pcurve":367,"periodic_lift":[0,0],"sense":"Reverse"},"id":367,"loop_ref":91,"next":364,"prev":366,"to":87,"twin":353,"wire_ref":null},{"edge":184,"face":92,"from":85,"geometry_use":{"pcurve":368,"periodic_lift":[0,0],"sense":"Forward"},"id":368,"loop_ref":92,"next":369,"prev":371,"to":94,"twin":361,"wire_ref":null},{"edge":187,"face":92,"from":94,"geometry_use":{"pcurve":369,"periodic_lift":[0,0],"sense":"Forward"},"id":369,"loop_ref":92,"next":370,"prev":368,"to":95,"twin":387,"wire_ref":null},{"edge":185,"face":92,"from":95,"geometry_use":{"pcurve":370,"periodic_lift":[0,0],"sense":"Reverse"},"id":370,"loop_ref":92,"next":371,"prev":369,"to":86,"twin":365,"wire_ref":null},{"edge":171,"face":92,"from":86,"geometry_use":{"pcurve":371,"periodic_lift":[0,0],"sense":"Reverse"},"id":371,"loop_ref":92,"next":368,"prev":370,"to":85,"twin":337,"wire_ref":null},{"edge":188,"face":93,"from":96,"geometry_use":{"pcurve":372,"periodic_lift":[0,0],"sense":"Forward"},"id":372,"loop_ref":93,"next":373,"prev":375,"to":97,"twin":410,"wire_ref":null},{"edge":189,"face":93,"from":97,"geometry_use":{"pcurve":373,"periodic_lift":[0,0],"sense":"Reverse"},"id":373,"loop_ref":93,"next":374,"prev":372,"to":94,"twin":384,"wire_ref":null},{"edge":183,"face":93,"from":94,"geometry_use":{"pcurve":374,"periodic_lift":[0,0],"sense":"Reverse"},"id":374,"loop_ref":93,"next":375,"prev":373,"to":93,"twin":360,"wire_ref":null},{"edge":190,"face":93,"from":93,"geometry_use":{"pcurve":375,"periodic_lift":[0,0],"sense":"Forward"},"id":375,"loop_ref":93,"next":372,"prev":374,"to":96,"twin":382,"wire_ref":null},{"edge":186,"face":94,"from":92,"geometry_use":{"pcurve":376,"periodic_lift":[0,0],"sense":"Forward"},"id":376,"loop_ref":94,"next":377,"prev":379,"to":95,"twin":366,"wire_ref":null},{"edge":191,"face":94,"from":95,"geometry_use":{"pcurve":377,"periodic_lift":[0,0],"sense":"Forward"},"id":377,"loop_ref":94,"next":378,"prev":376,"to":98,"twin":386,"wire_ref":null},{"edge":192,"face":94,"from":98,"geometry_use":{"pcurve":378,"periodic_lift":[0,0],"sense":"Reverse"},"id":378,"loop_ref":94,"next":379,"prev":377,"to":99,"twin":412,"wire_ref":null},{"edge":193,"face":94,"from":99,"geometry_use":{"pcurve":379,"periodic_lift":[0,0],"sense":"Reverse"},"id":379,"loop_ref":94,"next":376,"prev":378,"to":92,"twin":380,"wire_ref":null},{"edge":193,"face":95,"from":92,"geometry_use":{"pcurve":380,"periodic_lift":[0,0],"sense":"Forward"},"id":380,"loop_ref":95,"next":381,"prev":383,"to":99,"twin":379,"wire_ref":null},{"edge":194,"face":95,"from":99,"geometry_use":{"pcurve":381,"periodic_lift":[0,0],"sense":"Reverse"},"id":381,"loop_ref":95,"next":382,"prev":380,"to":96,"twin":388,"wire_ref":null},{"edge":190,"face":95,"from":96,"geometry_use":{"pcurve":382,"periodic_lift":[0,0],"sense":"Reverse"},"id":382,"loop_ref":95,"next":383,"prev":381,"to":93,"twin":375,"wire_ref":null},{"edge":177,"face":95,"from":93,"geometry_use":{"pcurve":383,"periodic_lift":[0,0],"sense":"Forward"},"id":383,"loop_ref":95,"next":380,"prev":382,"to":92,"twin":346,"wire_ref":null},{"edge":189,"face":96,"from":94,"geometry_use":{"pcurve":384,"periodic_lift":[0,0],"sense":"Forward"},"id":384,"loop_ref":96,"next":385,"prev":387,"to":97,"twin":373,"wire_ref":null},{"edge":195,"face":96,"from":97,"geometry_use":{"pcurve":385,"periodic_lift":[0,0],"sense":"Forward"},"id":385,"loop_ref":96,"next":386,"prev":384,"to":98,"twin":419,"wire_ref":null},{"edge":191,"face":96,"from":98,"geometry_use":{"pcurve":386,"periodic_lift":[0,0],"sense":"Reverse"},"id":386,"loop_ref":96,"next":387,"prev":385,"to":95,"twin":377,"wire_ref":null},{"edge":187,"face":96,"from":95,"geometry_use":{"pcurve":387,"periodic_lift":[0,0],"sense":"Reverse"},"id":387,"loop_ref":96,"next":384,"prev":386,"to":94,"twin":369,"wire_ref":null},{"edge":194,"face":97,"from":96,"geometry_use":{"pcurve":388,"periodic_lift":[0,0],"sense":"Forward"},"id":388,"loop_ref":97,"next":389,"prev":391,"to":99,"twin":381,"wire_ref":null},{"edge":196,"face":97,"from":99,"geometry_use":{"pcurve":389,"periodic_lift":[0,0],"sense":"Reverse"},"id":389,"loop_ref":97,"next":390,"prev":388,"to":100,"twin":400,"wire_ref":null},{"edge":197,"face":97,"from":100,"geometry_use":{"pcurve":390,"periodic_lift":[0,0],"sense":"Reverse"},"id":390,"loop_ref":97,"next":391,"prev":389,"to":101,"twin":407,"wire_ref":null},{"edge":198,"face":97,"from":101,"geometry_use":{"pcurve":391,"periodic_lift":[0,0],"sense":"Forward"},"id":391,"loop_ref":97,"next":388,"prev":390,"to":96,"twin":398,"wire_ref":null},{"edge":199,"face":98,"from":102,"geometry_use":{"pcurve":392,"periodic_lift":[0,0],"sense":"Forward"},"id":392,"loop_ref":98,"next":393,"prev":395,"to":103,"twin":405,"wire_ref":null},{"edge":200,"face":98,"from":103,"geometry_use":{"pcurve":393,"periodic_lift":[0,0],"sense":"Forward"},"id":393,"loop_ref":98,"next":394,"prev":392,"to":104,"twin":402,"wire_ref":null},{"edge":201,"face":98,"from":104,"geometry_use":{"pcurve":394,"periodic_lift":[0,0],"sense":"Reverse"},"id":394,"loop_ref":98,"next":395,"prev":393,"to":105,"twin":431,"wire_ref":null},{"edge":202,"face":98,"from":105,"geometry_use":{"pcurve":395,"periodic_lift":[0,0],"sense":"Reverse"},"id":395,"loop_ref":98,"next":392,"prev":394,"to":102,"twin":396,"wire_ref":null},{"edge":202,"face":99,"from":102,"geometry_use":{"pcurve":396,"periodic_lift":[0,0],"sense":"Forward"},"id":396,"loop_ref":99,"next":397,"prev":399,"to":105,"twin":395,"wire_ref":null},{"edge":203,"face":99,"from":105,"geometry_use":{"pcurve":397,"periodic_lift":[0,0],"sense":"Reverse"},"id":397,"loop_ref":99,"next":398,"prev":396,"to":96,"twin":411,"wire_ref":null},{"edge":198,"face":99,"from":96,"geometry_use":{"pcurve":398,"periodic_lift":[0,0],"sense":"Reverse"},"id":398,"loop_ref":99,"next":399,"prev":397,"to":101,"twin":391,"wire_ref":null},{"edge":204,"face":99,"from":101,"geometry_use":{"pcurve":399,"periodic_lift":[0,0],"sense":"Forward"},"id":399,"loop_ref":99,"next":396,"prev":398,"to":102,"twin":406,"wire_ref":null},{"edge":196,"face":100,"from":100,"geometry_use":{"pcurve":400,"periodic_lift":[0,0],"sense":"Forward"},"id":400,"loop_ref":100,"next":401,"prev":403,"to":99,"twin":389,"wire_ref":null},{"edge":205,"face":100,"from":99,"geometry_use":{"pcurve":401,"periodic_lift":[0,0],"sense":"Forward"},"id":401,"loop_ref":100,"next":402,"prev":400,"to":104,"twin":415,"wire_ref":null},{"edge":200,"face":100,"from":104,"geometry_use":{"pcurve":402,"periodic_lift":[0,0],"sense":"Reverse"},"id":402,"loop_ref":100,"next":403,"prev":401,"to":103,"twin":393,"wire_ref":null},{"edge":206,"face":100,"from":103,"geometry_use":{"pcurve":403,"periodic_lift":[0,0],"sense":"Reverse"},"id":403,"loop_ref":100,"next":400,"prev":402,"to":100,"twin":404,"wire_ref":null},{"edge":206,"face":101,"from":100,"geometry_use":{"pcurve":404,"periodic_lift":[0,0],"sense":"Forward"},"id":404,"loop_ref":101,"next":405,"prev":407,"to":103,"twin":403,"wire_ref":null},{"edge":199,"face":101,"from":103,"geometry_use":{"pcurve":405,"periodic_lift":[0,0],"sense":"Reverse"},"id":405,"loop_ref":101,"next":406,"prev":404,"to":102,"twin":392,"wire_ref":null},{"edge":204,"face":101,"from":102,"geometry_use":{"pcurve":406,"periodic_lift":[0,0],"sense":"Reverse"},"id":406,"loop_ref":101,"next":407,"prev":405,"to":101,"twin":399,"wire_ref":null},{"edge":197,"face":101,"from":101,"geometry_use":{"pcurve":407,"periodic_lift":[0,0],"sense":"Forward"},"id":407,"loop_ref":101,"next":404,"prev":406,"to":100,"twin":390,"wire_ref":null},{"edge":207,"face":102,"from":105,"geometry_use":{"pcurve":408,"periodic_lift":[0,0],"sense":"Forward"},"id":408,"loop_ref":102,"next":409,"prev":411,"to":106,"twin":422,"wire_ref":null},{"edge":208,"face":102,"from":106,"geometry_use":{"pcurve":409,"periodic_lift":[0,0],"sense":"Reverse"},"id":409,"loop_ref":102,"next":410,"prev":408,"to":97,"twin":416,"wire_ref":null},{"edge":188,"face":102,"from":97,"geometry_use":{"pcurve":410,"periodic_lift":[0,0],"sense":"Reverse"},"id":410,"loop_ref":102,"next":411,"prev":409,"to":96,"twin":372,"wire_ref":null},{"edge":203,"face":102,"from":96,"geometry_use":{"pcurve":411,"periodic_lift":[0,0],"sense":"Forward"},"id":411,"loop_ref":102,"next":408,"prev":410,"to":105,"twin":397,"wire_ref":null},{"edge":192,"face":103,"from":99,"geometry_use":{"pcurve":412,"periodic_lift":[0,0],"sense":"Forward"},"id":412,"loop_ref":103,"next":413,"prev":415,"to":98,"twin":378,"wire_ref":null},{"edge":209,"face":103,"from":98,"geometry_use":{"pcurve":413,"periodic_lift":[0,0],"sense":"Forward"},"id":413,"loop_ref":103,"next":414,"prev":412,"to":107,"twin":418,"wire_ref":null},{"edge":210,"face":103,"from":107,"geometry_use":{"pcurve":414,"periodic_lift":[0,0],"sense":"Reverse"},"id":414,"loop_ref":103,"next":415,"prev":413,"to":104,"twin":424,"wire_ref":null},{"edge":205,"face":103,"from":104,"geometry_use":{"pcurve":415,"periodic_lift":[0,0],"sense":"Reverse"},"id":415,"loop_ref":103,"next":412,"prev":414,"to":99,"twin":401,"wire_ref":null},{"edge":208,"face":104,"from":97,"geometry_use":{"pcurve":416,"periodic_lift":[0,0],"sense":"Forward"},"id":416,"loop_ref":104,"next":417,"prev":419,"to":106,"twin":409,"wire_ref":null},{"edge":211,"face":104,"from":106,"geometry_use":{"pcurve":417,"periodic_lift":[0,0],"sense":"Forward"},"id":417,"loop_ref":104,"next":418,"prev":416,"to":107,"twin":435,"wire_ref":null},{"edge":209,"face":104,"from":107,"geometry_use":{"pcurve":418,"periodic_lift":[0,0],"sense":"Reverse"},"id":418,"loop_ref":104,"next":419,"prev":417,"to":98,"twin":413,"wire_ref":null},{"edge":195,"face":104,"from":98,"geometry_use":{"pcurve":419,"periodic_lift":[0,0],"sense":"Reverse"},"id":419,"loop_ref":104,"next":416,"prev":418,"to":97,"twin":385,"wire_ref":null},{"edge":212,"face":105,"from":108,"geometry_use":{"pcurve":420,"periodic_lift":[0,0],"sense":"Forward"},"id":420,"loop_ref":105,"next":421,"prev":423,"to":109,"twin":458,"wire_ref":null},{"edge":213,"face":105,"from":109,"geometry_use":{"pcurve":421,"periodic_lift":[0,0],"sense":"Reverse"},"id":421,"loop_ref":105,"next":422,"prev":420,"to":106,"twin":432,"wire_ref":null},{"edge":207,"face":105,"from":106,"geometry_use":{"pcurve":422,"periodic_lift":[0,0],"sense":"Reverse"},"id":422,"loop_ref":105,"next":423,"prev":421,"to":105,"twin":408,"wire_ref":null},{"edge":214,"face":105,"from":105,"geometry_use":{"pcurve":423,"periodic_lift":[0,0],"sense":"Forward"},"id":423,"loop_ref":105,"next":420,"prev":422,"to":108,"twin":430,"wire_ref":null},{"edge":210,"face":106,"from":104,"geometry_use":{"pcurve":424,"periodic_lift":[0,0],"sense":"Forward"},"id":424,"loop_ref":106,"next":425,"prev":427,"to":107,"twin":414,"wire_ref":null},{"edge":215,"face":106,"from":107,"geometry_use":{"pcurve":425,"periodic_lift":[0,0],"sense":"Forward"},"id":425,"loop_ref":106,"next":426,"prev":424,"to":110,"twin":434,"wire_ref":null},{"edge":216,"face":106,"from":110,"geometry_use":{"pcurve":426,"periodic_lift":[0,0],"sense":"Reverse"},"id":426,"loop_ref":106,"next":427,"prev":425,"to":111,"twin":460,"wire_ref":null},{"edge":217,"face":106,"from":111,"geometry_use":{"pcurve":427,"periodic_lift":[0,0],"sense":"Reverse"},"id":427,"loop_ref":106,"next":424,"prev":426,"to":104,"twin":428,"wire_ref":null},{"edge":217,"face":107,"from":104,"geometry_use":{"pcurve":428,"periodic_lift":[0,0],"sense":"Forward"},"id":428,"loop_ref":107,"next":429,"prev":431,"to":111,"twin":427,"wire_ref":null},{"edge":218,"face":107,"from":111,"geometry_use":{"pcurve":429,"periodic_lift":[0,0],"sense":"Reverse"},"id":429,"loop_ref":107,"next":430,"prev":428,"to":108,"twin":436,"wire_ref":null},{"edge":214,"face":107,"from":108,"geometry_use":{"pcurve":430,"periodic_lift":[0,0],"sense":"Reverse"},"id":430,"loop_ref":107,"next":431,"prev":429,"to":105,"twin":423,"wire_ref":null},{"edge":201,"face":107,"from":105,"geometry_use":{"pcurve":431,"periodic_lift":[0,0],"sense":"Forward"},"id":431,"loop_ref":107,"next":428,"prev":430,"to":104,"twin":394,"wire_ref":null},{"edge":213,"face":108,"from":106,"geometry_use":{"pcurve":432,"periodic_lift":[0,0],"sense":"Forward"},"id":432,"loop_ref":108,"next":433,"prev":435,"to":109,"twin":421,"wire_ref":null},{"edge":219,"face":108,"from":109,"geometry_use":{"pcurve":433,"periodic_lift":[0,0],"sense":"Forward"},"id":433,"loop_ref":108,"next":434,"prev":432,"to":110,"twin":467,"wire_ref":null},{"edge":215,"face":108,"from":110,"geometry_use":{"pcurve":434,"periodic_lift":[0,0],"sense":"Reverse"},"id":434,"loop_ref":108,"next":435,"prev":433,"to":107,"twin":425,"wire_ref":null},{"edge":211,"face":108,"from":107,"geometry_use":{"pcurve":435,"periodic_lift":[0,0],"sense":"Reverse"},"id":435,"loop_ref":108,"next":432,"prev":434,"to":106,"twin":417,"wire_ref":null},{"edge":218,"face":109,"from":108,"geometry_use":{"pcurve":436,"periodic_lift":[0,0],"sense":"Forward"},"id":436,"loop_ref":109,"next":437,"prev":439,"to":111,"twin":429,"wire_ref":null},{"edge":220,"face":109,"from":111,"geometry_use":{"pcurve":437,"periodic_lift":[0,0],"sense":"Reverse"},"id":437,"loop_ref":109,"next":438,"prev":436,"to":112,"twin":448,"wire_ref":null},{"edge":221,"face":109,"from":112,"geometry_use":{"pcurve":438,"periodic_lift":[0,0],"sense":"Reverse"},"id":438,"loop_ref":109,"next":439,"prev":437,"to":113,"twin":455,"wire_ref":null},{"edge":222,"face":109,"from":113,"geometry_use":{"pcurve":439,"periodic_lift":[0,0],"sense":"Forward"},"id":439,"loop_ref":109,"next":436,"prev":438,"to":108,"twin":446,"wire_ref":null},{"edge":223,"face":110,"from":114,"geometry_use":{"pcurve":440,"periodic_lift":[0,0],"sense":"Forward"},"id":440,"loop_ref":110,"next":441,"prev":443,"to":115,"twin":453,"wire_ref":null},{"edge":224,"face":110,"from":115,"geometry_use":{"pcurve":441,"periodic_lift":[0,0],"sense":"Forward"},"id":441,"loop_ref":110,"next":442,"prev":440,"to":116,"twin":450,"wire_ref":null},{"edge":225,"face":110,"from":116,"geometry_use":{"pcurve":442,"periodic_lift":[0,0],"sense":"Reverse"},"id":442,"loop_ref":110,"next":443,"prev":441,"to":117,"twin":479,"wire_ref":null},{"edge":226,"face":110,"from":117,"geometry_use":{"pcurve":443,"periodic_lift":[0,0],"sense":"Reverse"},"id":443,"loop_ref":110,"next":440,"prev":442,"to":114,"twin":444,"wire_ref":null},{"edge":226,"face":111,"from":114,"geometry_use":{"pcurve":444,"periodic_lift":[0,0],"sense":"Forward"},"id":444,"loop_ref":111,"next":445,"prev":447,"to":117,"twin":443,"wire_ref":null},{"edge":227,"face":111,"from":117,"geometry_use":{"pcurve":445,"periodic_lift":[0,0],"sense":"Reverse"},"id":445,"loop_ref":111,"next":446,"prev":444,"to":108,"twin":459,"wire_ref":null},{"edge":222,"face":111,"from":108,"geometry_use":{"pcurve":446,"periodic_lift":[0,0],"sense":"Reverse"},"id":446,"loop_ref":111,"next":447,"prev":445,"to":113,"twin":439,"wire_ref":null},{"edge":228,"face":111,"from":113,"geometry_use":{"pcurve":447,"periodic_lift":[0,0],"sense":"Forward"},"id":447,"loop_ref":111,"next":444,"prev":446,"to":114,"twin":454,"wire_ref":null},{"edge":220,"face":112,"from":112,"geometry_use":{"pcurve":448,"periodic_lift":[0,0],"sense":"Forward"},"id":448,"loop_ref":112,"next":449,"prev":451,"to":111,"twin":437,"wire_ref":null},{"edge":229,"face":112,"from":111,"geometry_use":{"pcurve":449,"periodic_lift":[0,0],"sense":"Forward"},"id":449,"loop_ref":112,"next":450,"prev":448,"to":116,"twin":463,"wire_ref":null},{"edge":224,"face":112,"from":116,"geometry_use":{"pcurve":450,"periodic_lift":[0,0],"sense":"Reverse"},"id":450,"loop_ref":112,"next":451,"prev":449,"to":115,"twin":441,"wire_ref":null},{"edge":230,"face":112,"from":115,"geometry_use":{"pcurve":451,"periodic_lift":[0,0],"sense":"Reverse"},"id":451,"loop_ref":112,"next":448,"prev":450,"to":112,"twin":452,"wire_ref":null},{"edge":230,"face":113,"from":112,"geometry_use":{"pcurve":452,"periodic_lift":[0,0],"sense":"Forward"},"id":452,"loop_ref":113,"next":453,"prev":455,"to":115,"twin":451,"wire_ref":null},{"edge":223,"face":113,"from":115,"geometry_use":{"pcurve":453,"periodic_lift":[0,0],"sense":"Reverse"},"id":453,"loop_ref":113,"next":454,"prev":452,"to":114,"twin":440,"wire_ref":null},{"edge":228,"face":113,"from":114,"geometry_use":{"pcurve":454,"periodic_lift":[0,0],"sense":"Reverse"},"id":454,"loop_ref":113,"next":455,"prev":453,"to":113,"twin":447,"wire_ref":null},{"edge":221,"face":113,"from":113,"geometry_use":{"pcurve":455,"periodic_lift":[0,0],"sense":"Forward"},"id":455,"loop_ref":113,"next":452,"prev":454,"to":112,"twin":438,"wire_ref":null},{"edge":231,"face":114,"from":117,"geometry_use":{"pcurve":456,"periodic_lift":[0,0],"sense":"Forward"},"id":456,"loop_ref":114,"next":457,"prev":459,"to":118,"twin":470,"wire_ref":null},{"edge":232,"face":114,"from":118,"geometry_use":{"pcurve":457,"periodic_lift":[0,0],"sense":"Reverse"},"id":457,"loop_ref":114,"next":458,"prev":456,"to":109,"twin":464,"wire_ref":null},{"edge":212,"face":114,"from":109,"geometry_use":{"pcurve":458,"periodic_lift":[0,0],"sense":"Reverse"},"id":458,"loop_ref":114,"next":459,"prev":457,"to":108,"twin":420,"wire_ref":null},{"edge":227,"face":114,"from":108,"geometry_use":{"pcurve":459,"periodic_lift":[0,0],"sense":"Forward"},"id":459,"loop_ref":114,"next":456,"prev":458,"to":117,"twin":445,"wire_ref":null},{"edge":216,"face":115,"from":111,"geometry_use":{"pcurve":460,"periodic_lift":[0,0],"sense":"Forward"},"id":460,"loop_ref":115,"next":461,"prev":463,"to":110,"twin":426,"wire_ref":null},{"edge":233,"face":115,"from":110,"geometry_use":{"pcurve":461,"periodic_lift":[0,0],"sense":"Forward"},"id":461,"loop_ref":115,"next":462,"prev":460,"to":119,"twin":466,"wire_ref":null},{"edge":234,"face":115,"from":119,"geometry_use":{"pcurve":462,"periodic_lift":[0,0],"sense":"Reverse"},"id":462,"loop_ref":115,"next":463,"prev":461,"to":116,"twin":472,"wire_ref":null},{"edge":229,"face":115,"from":116,"geometry_use":{"pcurve":463,"periodic_lift":[0,0],"sense":"Reverse"},"id":463,"loop_ref":115,"next":460,"prev":462,"to":111,"twin":449,"wire_ref":null},{"edge":232,"face":116,"from":109,"geometry_use":{"pcurve":464,"periodic_lift":[0,0],"sense":"Forward"},"id":464,"loop_ref":116,"next":465,"prev":467,"to":118,"twin":457,"wire_ref":null},{"edge":235,"face":116,"from":118,"geometry_use":{"pcurve":465,"periodic_lift":[0,0],"sense":"Forward"},"id":465,"loop_ref":116,"next":466,"prev":464,"to":119,"twin":483,"wire_ref":null},{"edge":233,"face":116,"from":119,"geometry_use":{"pcurve":466,"periodic_lift":[0,0],"sense":"Reverse"},"id":466,"loop_ref":116,"next":467,"prev":465,"to":110,"twin":461,"wire_ref":null},{"edge":219,"face":116,"from":110,"geometry_use":{"pcurve":467,"periodic_lift":[0,0],"sense":"Reverse"},"id":467,"loop_ref":116,"next":464,"prev":466,"to":109,"twin":433,"wire_ref":null},{"edge":236,"face":117,"from":120,"geometry_use":{"pcurve":468,"periodic_lift":[0,0],"sense":"Forward"},"id":468,"loop_ref":117,"next":469,"prev":471,"to":121,"twin":510,"wire_ref":null},{"edge":237,"face":117,"from":121,"geometry_use":{"pcurve":469,"periodic_lift":[0,0],"sense":"Reverse"},"id":469,"loop_ref":117,"next":470,"prev":468,"to":118,"twin":480,"wire_ref":null},{"edge":231,"face":117,"from":118,"geometry_use":{"pcurve":470,"periodic_lift":[0,0],"sense":"Reverse"},"id":470,"loop_ref":117,"next":471,"prev":469,"to":117,"twin":456,"wire_ref":null},{"edge":238,"face":117,"from":117,"geometry_use":{"pcurve":471,"periodic_lift":[0,0],"sense":"Forward"},"id":471,"loop_ref":117,"next":468,"prev":470,"to":120,"twin":478,"wire_ref":null},{"edge":234,"face":118,"from":116,"geometry_use":{"pcurve":472,"periodic_lift":[0,0],"sense":"Forward"},"id":472,"loop_ref":118,"next":473,"prev":475,"to":119,"twin":462,"wire_ref":null},{"edge":239,"face":118,"from":119,"geometry_use":{"pcurve":473,"periodic_lift":[0,0],"sense":"Forward"},"id":473,"loop_ref":118,"next":474,"prev":472,"to":122,"twin":482,"wire_ref":null},{"edge":240,"face":118,"from":122,"geometry_use":{"pcurve":474,"periodic_lift":[0,0],"sense":"Reverse"},"id":474,"loop_ref":118,"next":475,"prev":473,"to":123,"twin":512,"wire_ref":null},{"edge":241,"face":118,"from":123,"geometry_use":{"pcurve":475,"periodic_lift":[0,0],"sense":"Reverse"},"id":475,"loop_ref":118,"next":472,"prev":474,"to":116,"twin":476,"wire_ref":null},{"edge":241,"face":119,"from":116,"geometry_use":{"pcurve":476,"periodic_lift":[0,0],"sense":"Forward"},"id":476,"loop_ref":119,"next":477,"prev":479,"to":123,"twin":475,"wire_ref":null},{"edge":242,"face":119,"from":123,"geometry_use":{"pcurve":477,"periodic_lift":[0,0],"sense":"Reverse"},"id":477,"loop_ref":119,"next":478,"prev":476,"to":120,"twin":484,"wire_ref":null},{"edge":238,"face":119,"from":120,"geometry_use":{"pcurve":478,"periodic_lift":[0,0],"sense":"Reverse"},"id":478,"loop_ref":119,"next":479,"prev":477,"to":117,"twin":471,"wire_ref":null},{"edge":225,"face":119,"from":117,"geometry_use":{"pcurve":479,"periodic_lift":[0,0],"sense":"Forward"},"id":479,"loop_ref":119,"next":476,"prev":478,"to":116,"twin":442,"wire_ref":null},{"edge":237,"face":120,"from":118,"geometry_use":{"pcurve":480,"periodic_lift":[0,0],"sense":"Forward"},"id":480,"loop_ref":120,"next":481,"prev":483,"to":121,"twin":469,"wire_ref":null},{"edge":243,"face":120,"from":121,"geometry_use":{"pcurve":481,"periodic_lift":[0,0],"sense":"Forward"},"id":481,"loop_ref":120,"next":482,"prev":480,"to":122,"twin":519,"wire_ref":null},{"edge":239,"face":120,"from":122,"geometry_use":{"pcurve":482,"periodic_lift":[0,0],"sense":"Reverse"},"id":482,"loop_ref":120,"next":483,"prev":481,"to":119,"twin":473,"wire_ref":null},{"edge":235,"face":120,"from":119,"geometry_use":{"pcurve":483,"periodic_lift":[0,0],"sense":"Reverse"},"id":483,"loop_ref":120,"next":480,"prev":482,"to":118,"twin":465,"wire_ref":null},{"edge":242,"face":121,"from":120,"geometry_use":{"pcurve":484,"periodic_lift":[0,0],"sense":"Forward"},"id":484,"loop_ref":121,"next":485,"prev":487,"to":123,"twin":477,"wire_ref":null},{"edge":244,"face":121,"from":123,"geometry_use":{"pcurve":485,"periodic_lift":[0,0],"sense":"Reverse"},"id":485,"loop_ref":121,"next":486,"prev":484,"to":124,"twin":496,"wire_ref":null},{"edge":245,"face":121,"from":124,"geometry_use":{"pcurve":486,"periodic_lift":[0,0],"sense":"Reverse"},"id":486,"loop_ref":121,"next":487,"prev":485,"to":125,"twin":503,"wire_ref":null},{"edge":246,"face":121,"from":125,"geometry_use":{"pcurve":487,"periodic_lift":[0,0],"sense":"Forward"},"id":487,"loop_ref":121,"next":484,"prev":486,"to":120,"twin":494,"wire_ref":null},{"edge":247,"face":122,"from":126,"geometry_use":{"pcurve":488,"periodic_lift":[0,0],"sense":"Forward"},"id":488,"loop_ref":122,"next":489,"prev":491,"to":127,"twin":501,"wire_ref":null},{"edge":248,"face":122,"from":127,"geometry_use":{"pcurve":489,"periodic_lift":[0,0],"sense":"Forward"},"id":489,"loop_ref":122,"next":490,"prev":488,"to":128,"twin":498,"wire_ref":null},{"edge":249,"face":122,"from":128,"geometry_use":{"pcurve":490,"periodic_lift":[0,0],"sense":"Reverse"},"id":490,"loop_ref":122,"next":491,"prev":489,"to":129,"twin":504,"wire_ref":null},{"edge":250,"face":122,"from":129,"geometry_use":{"pcurve":491,"periodic_lift":[0,0],"sense":"Reverse"},"id":491,"loop_ref":122,"next":488,"prev":490,"to":126,"twin":492,"wire_ref":null},{"edge":250,"face":123,"from":126,"geometry_use":{"pcurve":492,"periodic_lift":[0,0],"sense":"Forward"},"id":492,"loop_ref":123,"next":493,"prev":495,"to":129,"twin":491,"wire_ref":null},{"edge":251,"face":123,"from":129,"geometry_use":{"pcurve":493,"periodic_lift":[0,0],"sense":"Reverse"},"id":493,"loop_ref":123,"next":494,"prev":492,"to":120,"twin":511,"wire_ref":null},{"edge":246,"face":123,"from":120,"geometry_use":{"pcurve":494,"periodic_lift":[0,0],"sense":"Reverse"},"id":494,"loop_ref":123,"next":495,"prev":493,"to":125,"twin":487,"wire_ref":null},{"edge":252,"face":123,"from":125,"geometry_use":{"pcurve":495,"periodic_lift":[0,0],"sense":"Forward"},"id":495,"loop_ref":123,"next":492,"prev":494,"to":126,"twin":502,"wire_ref":null},{"edge":244,"face":124,"from":124,"geometry_use":{"pcurve":496,"periodic_lift":[0,0],"sense":"Forward"},"id":496,"loop_ref":124,"next":497,"prev":499,"to":123,"twin":485,"wire_ref":null},{"edge":253,"face":124,"from":123,"geometry_use":{"pcurve":497,"periodic_lift":[0,0],"sense":"Forward"},"id":497,"loop_ref":124,"next":498,"prev":496,"to":128,"twin":515,"wire_ref":null},{"edge":248,"face":124,"from":128,"geometry_use":{"pcurve":498,"periodic_lift":[0,0],"sense":"Reverse"},"id":498,"loop_ref":124,"next":499,"prev":497,"to":127,"twin":489,"wire_ref":null},{"edge":254,"face":124,"from":127,"geometry_use":{"pcurve":499,"periodic_lift":[0,0],"sense":"Reverse"},"id":499,"loop_ref":124,"next":496,"prev":498,"to":124,"twin":500,"wire_ref":null},{"edge":254,"face":125,"from":124,"geometry_use":{"pcurve":500,"periodic_lift":[0,0],"sense":"Forward"},"id":500,"loop_ref":125,"next":501,"prev":503,"to":127,"twin":499,"wire_ref":null},{"edge":247,"face":125,"from":127,"geometry_use":{"pcurve":501,"periodic_lift":[0,0],"sense":"Reverse"},"id":501,"loop_ref":125,"next":502,"prev":500,"to":126,"twin":488,"wire_ref":null},{"edge":252,"face":125,"from":126,"geometry_use":{"pcurve":502,"periodic_lift":[0,0],"sense":"Reverse"},"id":502,"loop_ref":125,"next":503,"prev":501,"to":125,"twin":495,"wire_ref":null},{"edge":245,"face":125,"from":125,"geometry_use":{"pcurve":503,"periodic_lift":[0,0],"sense":"Forward"},"id":503,"loop_ref":125,"next":500,"prev":502,"to":124,"twin":486,"wire_ref":null},{"edge":249,"face":126,"from":129,"geometry_use":{"pcurve":504,"periodic_lift":[0,0],"sense":"Forward"},"id":504,"loop_ref":126,"next":505,"prev":507,"to":128,"twin":490,"wire_ref":null},{"edge":255,"face":126,"from":128,"geometry_use":{"pcurve":505,"periodic_lift":[0,0],"sense":"Forward"},"id":505,"loop_ref":126,"next":506,"prev":504,"to":130,"twin":514,"wire_ref":null},{"edge":256,"face":126,"from":130,"geometry_use":{"pcurve":506,"periodic_lift":[0,0],"sense":"Reverse"},"id":506,"loop_ref":126,"next":507,"prev":505,"to":131,"twin":517,"wire_ref":null},{"edge":257,"face":126,"from":131,"geometry_use":{"pcurve":507,"periodic_lift":[0,0],"sense":"Reverse"},"id":507,"loop_ref":126,"next":504,"prev":506,"to":129,"twin":508,"wire_ref":null},{"edge":257,"face":127,"from":129,"geometry_use":{"pcurve":508,"periodic_lift":[0,0],"sense":"Forward"},"id":508,"loop_ref":127,"next":509,"prev":511,"to":131,"twin":507,"wire_ref":null},{"edge":258,"face":127,"from":131,"geometry_use":{"pcurve":509,"periodic_lift":[0,0],"sense":"Reverse"},"id":509,"loop_ref":127,"next":510,"prev":508,"to":121,"twin":516,"wire_ref":null},{"edge":236,"face":127,"from":121,"geometry_use":{"pcurve":510,"periodic_lift":[0,0],"sense":"Reverse"},"id":510,"loop_ref":127,"next":511,"prev":509,"to":120,"twin":468,"wire_ref":null},{"edge":251,"face":127,"from":120,"geometry_use":{"pcurve":511,"periodic_lift":[0,0],"sense":"Forward"},"id":511,"loop_ref":127,"next":508,"prev":510,"to":129,"twin":493,"wire_ref":null},{"edge":240,"face":128,"from":123,"geometry_use":{"pcurve":512,"periodic_lift":[0,0],"sense":"Forward"},"id":512,"loop_ref":128,"next":513,"prev":515,"to":122,"twin":474,"wire_ref":null},{"edge":259,"face":128,"from":122,"geometry_use":{"pcurve":513,"periodic_lift":[0,0],"sense":"Forward"},"id":513,"loop_ref":128,"next":514,"prev":512,"to":130,"twin":518,"wire_ref":null},{"edge":255,"face":128,"from":130,"geometry_use":{"pcurve":514,"periodic_lift":[0,0],"sense":"Reverse"},"id":514,"loop_ref":128,"next":515,"prev":513,"to":128,"twin":505,"wire_ref":null},{"edge":253,"face":128,"from":128,"geometry_use":{"pcurve":515,"periodic_lift":[0,0],"sense":"Reverse"},"id":515,"loop_ref":128,"next":512,"prev":514,"to":123,"twin":497,"wire_ref":null},{"edge":258,"face":129,"from":121,"geometry_use":{"pcurve":516,"periodic_lift":[0,0],"sense":"Forward"},"id":516,"loop_ref":129,"next":517,"prev":519,"to":131,"twin":509,"wire_ref":null},{"edge":256,"face":129,"from":131,"geometry_use":{"pcurve":517,"periodic_lift":[0,0],"sense":"Forward"},"id":517,"loop_ref":129,"next":518,"prev":516,"to":130,"twin":506,"wire_ref":null},{"edge":259,"face":129,"from":130,"geometry_use":{"pcurve":518,"periodic_lift":[0,0],"sense":"Reverse"},"id":518,"loop_ref":129,"next":519,"prev":517,"to":122,"twin":513,"wire_ref":null},{"edge":243,"face":129,"from":122,"geometry_use":{"pcurve":519,"periodic_lift":[0,0],"sense":"Reverse"},"id":519,"loop_ref":129,"next":516,"prev":518,"to":121,"twin":481,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":48},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":52},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":56},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":60},{"face_ref":16,"id":16,"is_hole":false,"start_halfedge":64},{"face_ref":17,"id":17,"is_hole":false,"start_halfedge":68},{"face_ref":18,"id":18,"is_hole":false,"start_halfedge":72},{"face_ref":19,"id":19,"is_hole":false,"start_halfedge":76},{"face_ref":20,"id":20,"is_hole":false,"start_halfedge":80},{"face_ref":21,"id":21,"is_hole":false,"start_halfedge":84},{"face_ref":22,"id":22,"is_hole":false,"start_halfedge":88},{"face_ref":23,"id":23,"is_hole":false,"start_halfedge":92},{"face_ref":24,"id":24,"is_hole":false,"start_halfedge":96},{"face_ref":25,"id":25,"is_hole":false,"start_halfedge":100},{"face_ref":26,"id":26,"is_hole":false,"start_halfedge":104},{"face_ref":27,"id":27,"is_hole":false,"start_halfedge":108},{"face_ref":28,"id":28,"is_hole":false,"start_halfedge":112},{"face_ref":29,"id":29,"is_hole":false,"start_halfedge":116},{"face_ref":30,"id":30,"is_hole":false,"start_halfedge":120},{"face_ref":31,"id":31,"is_hole":false,"start_halfedge":124},{"face_ref":32,"id":32,"is_hole":false,"start_halfedge":128},{"face_ref":33,"id":33,"is_hole":false,"start_halfedge":132},{"face_ref":34,"id":34,"is_hole":false,"start_halfedge":136},{"face_ref":35,"id":35,"is_hole":false,"start_halfedge":140},{"face_ref":36,"id":36,"is_hole":false,"start_halfedge":144},{"face_ref":37,"id":37,"is_hole":false,"start_halfedge":148},{"face_ref":38,"id":38,"is_hole":false,"start_halfedge":152},{"face_ref":39,"id":39,"is_hole":false,"start_halfedge":156},{"face_ref":40,"id":40,"is_hole":false,"start_halfedge":160},{"face_ref":41,"id":41,"is_hole":false,"start_halfedge":164},{"face_ref":42,"id":42,"is_hole":false,"start_halfedge":168},{"face_ref":43,"id":43,"is_hole":false,"start_halfedge":172},{"face_ref":44,"id":44,"is_hole":false,"start_halfedge":176},{"face_ref":45,"id":45,"is_hole":false,"start_halfedge":180},{"face_ref":46,"id":46,"is_hole":false,"start_halfedge":184},{"face_ref":47,"id":47,"is_hole":false,"start_halfedge":188},{"face_ref":48,"id":48,"is_hole":false,"start_halfedge":192},{"face_ref":49,"id":49,"is_hole":false,"start_halfedge":196},{"face_ref":50,"id":50,"is_hole":false,"start_halfedge":200},{"face_ref":51,"id":51,"is_hole":false,"start_halfedge":204},{"face_ref":52,"id":52,"is_hole":false,"start_halfedge":208},{"face_ref":53,"id":53,"is_hole":false,"start_halfedge":212},{"face_ref":54,"id":54,"is_hole":false,"start_halfedge":216},{"face_ref":55,"id":55,"is_hole":false,"start_halfedge":220},{"face_ref":56,"id":56,"is_hole":false,"start_halfedge":224},{"face_ref":57,"id":57,"is_hole":false,"start_halfedge":228},{"face_ref":58,"id":58,"is_hole":false,"start_halfedge":232},{"face_ref":59,"id":59,"is_hole":false,"start_halfedge":236},{"face_ref":60,"id":60,"is_hole":false,"start_halfedge":240},{"face_ref":61,"id":61,"is_hole":false,"start_halfedge":244},{"face_ref":62,"id":62,"is_hole":false,"start_halfedge":248},{"face_ref":63,"id":63,"is_hole":false,"start_halfedge":252},{"face_ref":64,"id":64,"is_hole":false,"start_halfedge":256},{"face_ref":65,"id":65,"is_hole":false,"start_halfedge":260},{"face_ref":66,"id":66,"is_hole":false,"start_halfedge":264},{"face_ref":67,"id":67,"is_hole":false,"start_halfedge":268},{"face_ref":68,"id":68,"is_hole":false,"start_halfedge":272},{"face_ref":69,"id":69,"is_hole":false,"start_halfedge":276},{"face_ref":70,"id":70,"is_hole":false,"start_halfedge":280},{"face_ref":71,"id":71,"is_hole":false,"start_halfedge":284},{"face_ref":72,"id":72,"is_hole":false,"start_halfedge":288},{"face_ref":73,"id":73,"is_hole":false,"start_halfedge":292},{"face_ref":74,"id":74,"is_hole":false,"start_halfedge":296},{"face_ref":75,"id":75,"is_hole":false,"start_halfedge":300},{"face_ref":76,"id":76,"is_hole":false,"start_halfedge":304},{"face_ref":77,"id":77,"is_hole":false,"start_halfedge":308},{"face_ref":78,"id":78,"is_hole":false,"start_halfedge":312},{"face_ref":79,"id":79,"is_hole":false,"start_halfedge":316},{"face_ref":80,"id":80,"is_hole":false,"start_halfedge":320},{"face_ref":81,"id":81,"is_hole":false,"start_halfedge":324},{"face_ref":82,"id":82,"is_hole":false,"start_halfedge":328},{"face_ref":83,"id":83,"is_hole":false,"start_halfedge":332},{"face_ref":84,"id":84,"is_hole":false,"start_halfedge":336},{"face_ref":85,"id":85,"is_hole":false,"start_halfedge":340},{"face_ref":86,"id":86,"is_hole":false,"start_halfedge":344},{"face_ref":87,"id":87,"is_hole":false,"start_halfedge":348},{"face_ref":88,"id":88,"is_hole":false,"start_halfedge":352},{"face_ref":89,"id":89,"is_hole":false,"start_halfedge":356},{"face_ref":90,"id":90,"is_hole":false,"start_halfedge":360},{"face_ref":91,"id":91,"is_hole":false,"start_halfedge":364},{"face_ref":92,"id":92,"is_hole":false,"start_halfedge":368},{"face_ref":93,"id":93,"is_hole":false,"start_halfedge":372},{"face_ref":94,"id":94,"is_hole":false,"start_halfedge":376},{"face_ref":95,"id":95,"is_hole":false,"start_halfedge":380},{"face_ref":96,"id":96,"is_hole":false,"start_halfedge":384},{"face_ref":97,"id":97,"is_hole":false,"start_halfedge":388},{"face_ref":98,"id":98,"is_hole":false,"start_halfedge":392},{"face_ref":99,"id":99,"is_hole":false,"start_halfedge":396},{"face_ref":100,"id":100,"is_hole":false,"start_halfedge":400},{"face_ref":101,"id":101,"is_hole":false,"start_halfedge":404},{"face_ref":102,"id":102,"is_hole":false,"start_halfedge":408},{"face_ref":103,"id":103,"is_hole":false,"start_halfedge":412},{"face_ref":104,"id":104,"is_hole":false,"start_halfedge":416},{"face_ref":105,"id":105,"is_hole":false,"start_halfedge":420},{"face_ref":106,"id":106,"is_hole":false,"start_halfedge":424},{"face_ref":107,"id":107,"is_hole":false,"start_halfedge":428},{"face_ref":108,"id":108,"is_hole":false,"start_halfedge":432},{"face_ref":109,"id":109,"is_hole":false,"start_halfedge":436},{"face_ref":110,"id":110,"is_hole":false,"start_halfedge":440},{"face_ref":111,"id":111,"is_hole":false,"start_halfedge":444},{"face_ref":112,"id":112,"is_hole":false,"start_halfedge":448},{"face_ref":113,"id":113,"is_hole":false,"start_halfedge":452},{"face_ref":114,"id":114,"is_hole":false,"start_halfedge":456},{"face_ref":115,"id":115,"is_hole":false,"start_halfedge":460},{"face_ref":116,"id":116,"is_hole":false,"start_halfedge":464},{"face_ref":117,"id":117,"is_hole":false,"start_halfedge":468},{"face_ref":118,"id":118,"is_hole":false,"start_halfedge":472},{"face_ref":119,"id":119,"is_hole":false,"start_halfedge":476},{"face_ref":120,"id":120,"is_hole":false,"start_halfedge":480},{"face_ref":121,"id":121,"is_hole":false,"start_halfedge":484},{"face_ref":122,"id":122,"is_hole":false,"start_halfedge":488},{"face_ref":123,"id":123,"is_hole":false,"start_halfedge":492},{"face_ref":124,"id":124,"is_hole":false,"start_halfedge":496},{"face_ref":125,"id":125,"is_hole":false,"start_halfedge":500},{"face_ref":126,"id":126,"is_hole":false,"start_halfedge":504},{"face_ref":127,"id":127,"is_hole":false,"start_halfedge":508},{"face_ref":128,"id":128,"is_hole":false,"start_halfedge":512},{"face_ref":129,"id":129,"is_hole":false,"start_halfedge":516}],"shells":[{"faces":[0,5,3,4,2,8,7,6,1,12,10,9,11,20,19,18,13,24,22,16,21,15,17,32,31,23,14,30,36,34,28,25,33,27,44,43,35,26,29,42,48,46,40,37,45,39,56,55,47,38,41,54,60,58,52,49,57,51,68,67,59,50,53,66,72,70,64,61,69,63,80,79,71,62,65,78,84,82,76,73,81,75,92,91,83,74,77,90,96,94,88,85,93,87,104,103,95,86,89,102,108,106,100,97,105,99,116,115,107,98,101,114,120,118,112,109,117,111,129,128,119,110,113,127,126,124,121,123,122,125],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,2.1],"tolerance":1e-9},{"id":1,"outgoing_halfedge":1,"position":[0.0,0.3,2.1],"tolerance":1e-9},{"id":2,"outgoing_halfedge":2,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":3,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":4,"position":[1.0,0.0,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":5,"position":[1.0,0.3,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":6,"position":[1.0,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":7,"position":[1.0,0.0,2.1],"tolerance":1e-9},{"id":8,"outgoing_halfedge":20,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":9,"outgoing_halfedge":21,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":10,"outgoing_halfedge":25,"position":[1.0,0.0,3.0],"tolerance":1e-9},{"id":11,"outgoing_halfedge":30,"position":[1.0,0.3,3.0],"tolerance":1e-9},{"id":12,"outgoing_halfedge":36,"position":[1.5,0.0,2.1],"tolerance":1e-9},{"id":13,"outgoing_halfedge":37,"position":[1.5,0.0,3.0],"tolerance":1e-9},{"id":14,"outgoing_halfedge":42,"position":[1.5,0.3,3.0],"tolerance":1e-9},{"id":15,"outgoing_halfedge":43,"position":[1.5,0.3,2.1],"tolerance":1e-9},{"id":16,"outgoing_halfedge":54,"position":[1.5,0.3,0.0],"tolerance":1e-9},{"id":17,"outgoing_halfedge":55,"position":[1.5,0.0,0.0],"tolerance":1e-9},{"id":18,"outgoing_halfedge":56,"position":[2.8,0.0,0.0],"tolerance":1e-9},{"id":19,"outgoing_halfedge":57,"position":[2.8,0.3,0.0],"tolerance":1e-9},{"id":20,"outgoing_halfedge":58,"position":[2.8,0.3,2.1],"tolerance":1e-9},{"id":21,"outgoing_halfedge":59,"position":[2.8,0.0,2.1],"tolerance":1e-9},{"id":22,"outgoing_halfedge":73,"position":[2.8,0.0,3.0],"tolerance":1e-9},{"id":23,"outgoing_halfedge":78,"position":[2.8,0.3,3.0],"tolerance":1e-9},{"id":24,"outgoing_halfedge":84,"position":[3.3,0.0,2.1],"tolerance":1e-9},{"id":25,"outgoing_halfedge":85,"position":[3.3,0.0,3.0],"tolerance":1e-9},{"id":26,"outgoing_halfedge":90,"position":[3.3,0.3,3.0],"tolerance":1e-9},{"id":27,"outgoing_halfedge":91,"position":[3.3,0.3,2.1],"tolerance":1e-9},{"id":28,"outgoing_halfedge":102,"position":[3.3,0.3,0.0],"tolerance":1e-9},{"id":29,"outgoing_halfedge":103,"position":[3.3,0.0,0.0],"tolerance":1e-9},{"id":30,"outgoing_halfedge":104,"position":[4.6,0.0,0.0],"tolerance":1e-9},{"id":31,"outgoing_halfedge":105,"position":[4.6,0.3,0.0],"tolerance":1e-9},{"id":32,"outgoing_halfedge":106,"position":[4.6,0.3,2.1],"tolerance":1e-9},{"id":33,"outgoing_halfedge":107,"position":[4.6,0.0,2.1],"tolerance":1e-9},{"id":34,"outgoing_halfedge":121,"position":[4.6,0.0,3.0],"tolerance":1e-9},{"id":35,"outgoing_halfedge":126,"position":[4.6,0.3,3.0],"tolerance":1e-9},{"id":36,"outgoing_halfedge":132,"position":[5.1,0.0,2.1],"tolerance":1e-9},{"id":37,"outgoing_halfedge":133,"position":[5.1,0.0,3.0],"tolerance":1e-9},{"id":38,"outgoing_halfedge":138,"position":[5.1,0.3,3.0],"tolerance":1e-9},{"id":39,"outgoing_halfedge":139,"position":[5.1,0.3,2.1],"tolerance":1e-9},{"id":40,"outgoing_halfedge":150,"position":[5.1,0.3,0.0],"tolerance":1e-9},{"id":41,"outgoing_halfedge":151,"position":[5.1,0.0,0.0],"tolerance":1e-9},{"id":42,"outgoing_halfedge":152,"position":[6.4,0.0,0.0],"tolerance":1e-9},{"id":43,"outgoing_halfedge":153,"position":[6.4,0.3,0.0],"tolerance":1e-9},{"id":44,"outgoing_halfedge":154,"position":[6.4,0.3,2.1],"tolerance":1e-9},{"id":45,"outgoing_halfedge":155,"position":[6.4,0.0,2.1],"tolerance":1e-9},{"id":46,"outgoing_halfedge":169,"position":[6.4,0.0,3.0],"tolerance":1e-9},{"id":47,"outgoing_halfedge":174,"position":[6.4,0.3,3.0],"tolerance":1e-9},{"id":48,"outgoing_halfedge":180,"position":[6.9,0.0,2.1],"tolerance":1e-9},{"id":49,"outgoing_halfedge":181,"position":[6.9,0.0,3.0],"tolerance":1e-9},{"id":50,"outgoing_halfedge":186,"position":[6.9,0.3,3.0],"tolerance":1e-9},{"id":51,"outgoing_halfedge":187,"position":[6.9,0.3,2.1],"tolerance":1e-9},{"id":52,"outgoing_halfedge":198,"position":[6.9,0.3,0.0],"tolerance":1e-9},{"id":53,"outgoing_halfedge":199,"position":[6.9,0.0,0.0],"tolerance":1e-9},{"id":54,"outgoing_halfedge":200,"position":[8.2,0.0,0.0],"tolerance":1e-9},{"id":55,"outgoing_halfedge":201,"position":[8.2,0.3,0.0],"tolerance":1e-9},{"id":56,"outgoing_halfedge":202,"position":[8.2,0.3,2.1],"tolerance":1e-9},{"id":57,"outgoing_halfedge":203,"position":[8.2,0.0,2.1],"tolerance":1e-9},{"id":58,"outgoing_halfedge":217,"position":[8.2,0.0,3.0],"tolerance":1e-9},{"id":59,"outgoing_halfedge":222,"position":[8.2,0.3,3.0],"tolerance":1e-9},{"id":60,"outgoing_halfedge":228,"position":[8.7,0.0,2.1],"tolerance":1e-9},{"id":61,"outgoing_halfedge":229,"position":[8.7,0.0,3.0],"tolerance":1e-9},{"id":62,"outgoing_halfedge":234,"position":[8.7,0.3,3.0],"tolerance":1e-9},{"id":63,"outgoing_halfedge":235,"position":[8.7,0.3,2.1],"tolerance":1e-9},{"id":64,"outgoing_halfedge":246,"position":[8.7,0.3,0.0],"tolerance":1e-9},{"id":65,"outgoing_halfedge":247,"position":[8.7,0.0,0.0],"tolerance":1e-9},{"id":66,"outgoing_halfedge":248,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":67,"outgoing_halfedge":249,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":68,"outgoing_halfedge":250,"position":[10.0,0.3,2.1],"tolerance":1e-9},{"id":69,"outgoing_halfedge":251,"position":[10.0,0.0,2.1],"tolerance":1e-9},{"id":70,"outgoing_halfedge":265,"position":[10.0,0.0,3.0],"tolerance":1e-9},{"id":71,"outgoing_halfedge":270,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":72,"outgoing_halfedge":276,"position":[10.5,0.0,2.1],"tolerance":1e-9},{"id":73,"outgoing_halfedge":277,"position":[10.5,0.0,3.0],"tolerance":1e-9},{"id":74,"outgoing_halfedge":282,"position":[10.5,0.3,3.0],"tolerance":1e-9},{"id":75,"outgoing_halfedge":283,"position":[10.5,0.3,2.1],"tolerance":1e-9},{"id":76,"outgoing_halfedge":294,"position":[10.5,0.3,0.0],"tolerance":1e-9},{"id":77,"outgoing_halfedge":295,"position":[10.5,0.0,0.0],"tolerance":1e-9},{"id":78,"outgoing_halfedge":296,"position":[11.8,0.0,0.0],"tolerance":1e-9},{"id":79,"outgoing_halfedge":297,"position":[11.8,0.3,0.0],"tolerance":1e-9},{"id":80,"outgoing_halfedge":298,"position":[11.8,0.3,2.1],"tolerance":1e-9},{"id":81,"outgoing_halfedge":299,"position":[11.8,0.0,2.1],"tolerance":1e-9},{"id":82,"outgoing_halfedge":313,"position":[11.8,0.0,3.0],"tolerance":1e-9},{"id":83,"outgoing_halfedge":318,"position":[11.8,0.3,3.0],"tolerance":1e-9},{"id":84,"outgoing_halfedge":324,"position":[12.3,0.0,2.1],"tolerance":1e-9},{"id":85,"outgoing_halfedge":325,"position":[12.3,0.0,3.0],"tolerance":1e-9},{"id":86,"outgoing_halfedge":330,"position":[12.3,0.3,3.0],"tolerance":1e-9},{"id":87,"outgoing_halfedge":331,"position":[12.3,0.3,2.1],"tolerance":1e-9},{"id":88,"outgoing_halfedge":342,"position":[12.3,0.3,0.0],"tolerance":1e-9},{"id":89,"outgoing_halfedge":343,"position":[12.3,0.0,0.0],"tolerance":1e-9},{"id":90,"outgoing_halfedge":344,"position":[13.6,0.0,0.0],"tolerance":1e-9},{"id":91,"outgoing_halfedge":345,"position":[13.6,0.3,0.0],"tolerance":1e-9},{"id":92,"outgoing_halfedge":346,"position":[13.6,0.3,2.1],"tolerance":1e-9},{"id":93,"outgoing_halfedge":347,"position":[13.6,0.0,2.1],"tolerance":1e-9},{"id":94,"outgoing_halfedge":361,"position":[13.6,0.0,3.0],"tolerance":1e-9},{"id":95,"outgoing_halfedge":366,"position":[13.6,0.3,3.0],"tolerance":1e-9},{"id":96,"outgoing_halfedge":372,"position":[14.1,0.0,2.1],"tolerance":1e-9},{"id":97,"outgoing_halfedge":373,"position":[14.1,0.0,3.0],"tolerance":1e-9},{"id":98,"outgoing_halfedge":378,"position":[14.1,0.3,3.0],"tolerance":1e-9},{"id":99,"outgoing_halfedge":379,"position":[14.1,0.3,2.1],"tolerance":1e-9},{"id":100,"outgoing_halfedge":390,"position":[14.1,0.3,0.0],"tolerance":1e-9},{"id":101,"outgoing_halfedge":391,"position":[14.1,0.0,0.0],"tolerance":1e-9},{"id":102,"outgoing_halfedge":392,"position":[15.4,0.0,0.0],"tolerance":1e-9},{"id":103,"outgoing_halfedge":393,"position":[15.4,0.3,0.0],"tolerance":1e-9},{"id":104,"outgoing_halfedge":394,"position":[15.4,0.3,2.1],"tolerance":1e-9},{"id":105,"outgoing_halfedge":395,"position":[15.4,0.0,2.1],"tolerance":1e-9},{"id":106,"outgoing_halfedge":409,"position":[15.4,0.0,3.0],"tolerance":1e-9},{"id":107,"outgoing_halfedge":414,"position":[15.4,0.3,3.0],"tolerance":1e-9},{"id":108,"outgoing_halfedge":420,"position":[15.9,0.0,2.1],"tolerance":1e-9},{"id":109,"outgoing_halfedge":421,"position":[15.9,0.0,3.0],"tolerance":1e-9},{"id":110,"outgoing_halfedge":426,"position":[15.9,0.3,3.0],"tolerance":1e-9},{"id":111,"outgoing_halfedge":427,"position":[15.9,0.3,2.1],"tolerance":1e-9},{"id":112,"outgoing_halfedge":438,"position":[15.9,0.3,0.0],"tolerance":1e-9},{"id":113,"outgoing_halfedge":439,"position":[15.9,0.0,0.0],"tolerance":1e-9},{"id":114,"outgoing_halfedge":440,"position":[17.2,0.0,0.0],"tolerance":1e-9},{"id":115,"outgoing_halfedge":441,"position":[17.2,0.3,0.0],"tolerance":1e-9},{"id":116,"outgoing_halfedge":442,"position":[17.2,0.3,2.1],"tolerance":1e-9},{"id":117,"outgoing_halfedge":443,"position":[17.2,0.0,2.1],"tolerance":1e-9},{"id":118,"outgoing_halfedge":457,"position":[17.2,0.0,3.0],"tolerance":1e-9},{"id":119,"outgoing_halfedge":462,"position":[17.2,0.3,3.0],"tolerance":1e-9},{"id":120,"outgoing_halfedge":468,"position":[17.7,0.0,2.1],"tolerance":1e-9},{"id":121,"outgoing_halfedge":469,"position":[17.7,0.0,3.0],"tolerance":1e-9},{"id":122,"outgoing_halfedge":474,"position":[17.7,0.3,3.0],"tolerance":1e-9},{"id":123,"outgoing_halfedge":475,"position":[17.7,0.3,2.1],"tolerance":1e-9},{"id":124,"outgoing_halfedge":486,"position":[17.7,0.3,0.0],"tolerance":1e-9},{"id":125,"outgoing_halfedge":487,"position":[17.7,0.0,0.0],"tolerance":1e-9},{"id":126,"outgoing_halfedge":488,"position":[20.0,0.0,0.0],"tolerance":1e-9},{"id":127,"outgoing_halfedge":489,"position":[20.0,0.3,0.0],"tolerance":1e-9},{"id":128,"outgoing_halfedge":490,"position":[20.0,0.3,2.1],"tolerance":1e-9},{"id":129,"outgoing_halfedge":491,"position":[20.0,0.0,2.1],"tolerance":1e-9},{"id":130,"outgoing_halfedge":506,"position":[20.0,0.3,3.0],"tolerance":1e-9},{"id":131,"outgoing_halfedge":507,"position":[20.0,0.0,3.0],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.too-many-cutters.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.too-many-cutters.json new file mode 100644 index 0000000..8a2ac5a --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.too-many-cutters.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.01,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.01,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.01,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.01,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.01,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.01,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.01,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.01,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.01,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.01,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.01,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.01,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.01,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.01,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-0","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.01,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.01,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.01,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.01,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-0","entity":"crowded-0","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.010000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-0","entity":"crowded-0","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.010000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-0","entity":"crowded-0","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-0","entity":"crowded-0","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-0","entity":"crowded-0","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-0","entity":"crowded-0","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.01,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.01,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.01,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.01,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.05,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.05,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.05,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.060000000000000005,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.060000000000000005,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.060000000000000005,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.060000000000000005,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.060000000000000005,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.060000000000000005,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.05,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.05,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.05,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.05,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.060000000000000005,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.060000000000000005,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.05,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.05,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.060000000000000005,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.060000000000000005,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.05,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000002,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000002,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.05,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.060000000000000005,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.060000000000000005,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.05,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-1","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000002,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000002,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000002,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000002,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-1","entity":"crowded-1","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.060000001000000004,"lo":0.049999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-1","entity":"crowded-1","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.060000001000000004,"lo":0.049999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-1","entity":"crowded-1","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-1","entity":"crowded-1","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-1","entity":"crowded-1","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-1","entity":"crowded-1","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.05,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.060000000000000005,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.060000000000000005,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.05,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.05,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.060000000000000005,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.060000000000000005,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.05,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.1,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.1,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.1,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.11,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.11,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.11,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.11,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.11,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.11,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.1,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.1,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.1,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.1,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.11,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.11,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.1,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.1,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.11,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.11,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.1,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999995,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999995,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.1,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.11,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.11,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.1,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-2","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999995,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999995,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999995,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999995,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-2","entity":"crowded-2","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.110000001,"lo":0.099999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-2","entity":"crowded-2","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.110000001,"lo":0.099999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-2","entity":"crowded-2","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999994,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-2","entity":"crowded-2","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-2","entity":"crowded-2","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999994,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-2","entity":"crowded-2","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.1,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.11,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.11,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.1,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.1,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.11,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.11,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.1,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.15000000000000002,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.15000000000000002,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.15000000000000002,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.16000000000000003,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.16000000000000003,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.16000000000000003,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.16000000000000003,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.16000000000000003,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.16000000000000003,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.15000000000000002,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.15000000000000002,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.15000000000000002,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.15000000000000002,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.16000000000000003,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.16000000000000003,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.15000000000000002,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.15000000000000002,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.16000000000000003,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.16000000000000003,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.15000000000000002,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.15000000000000002,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.16000000000000003,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.16000000000000003,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.15000000000000002,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-3","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-3","entity":"crowded-3","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.16000000100000003,"lo":0.14999999900000002},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-3","entity":"crowded-3","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.16000000100000003,"lo":0.14999999900000002},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-3","entity":"crowded-3","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-3","entity":"crowded-3","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-3","entity":"crowded-3","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-3","entity":"crowded-3","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.15000000000000002,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.16000000000000003,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.16000000000000003,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.15000000000000002,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.15000000000000002,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.16000000000000003,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.16000000000000003,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.15000000000000002,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.2,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.2,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.2,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.21000000000000002,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.21000000000000002,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.21000000000000002,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.21000000000000002,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.21000000000000002,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.21000000000000002,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.2,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.2,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.2,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.2,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.21000000000000002,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.21000000000000002,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.2,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.2,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.21000000000000002,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.21000000000000002,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.2,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.2,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.21000000000000002,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.21000000000000002,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.2,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-4","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-4","entity":"crowded-4","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.21000000100000002,"lo":0.199999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-4","entity":"crowded-4","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.21000000100000002,"lo":0.199999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-4","entity":"crowded-4","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-4","entity":"crowded-4","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-4","entity":"crowded-4","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-4","entity":"crowded-4","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.2,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.21000000000000002,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.21000000000000002,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.2,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.2,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.21000000000000002,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.21000000000000002,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.2,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.25,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.25,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.25,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.26,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.26,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.26,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.26,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.26,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.26,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.25,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.25,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.25,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.25,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.26,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.26,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.25,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.25,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.26,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.26,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.25,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.25,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.26,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.26,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.25,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-5","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-5","entity":"crowded-5","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.26000000100000004,"lo":0.249999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-5","entity":"crowded-5","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.26000000100000004,"lo":0.249999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-5","entity":"crowded-5","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-5","entity":"crowded-5","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-5","entity":"crowded-5","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-5","entity":"crowded-5","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.25,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.26,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.26,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.25,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.25,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.26,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.26,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.25,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.30000000000000004,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.30000000000000004,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.30000000000000004,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.31000000000000005,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.31000000000000005,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.31000000000000005,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.31000000000000005,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.31000000000000005,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.31000000000000005,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.30000000000000004,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.30000000000000004,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.30000000000000004,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.30000000000000004,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.31000000000000005,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.31000000000000005,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.30000000000000004,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.30000000000000004,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.31000000000000005,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.31000000000000005,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.30000000000000004,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.30000000000000004,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.31000000000000005,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.31000000000000005,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.30000000000000004,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-6","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-6","entity":"crowded-6","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.3100000010000001,"lo":0.299999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-6","entity":"crowded-6","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.3100000010000001,"lo":0.299999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-6","entity":"crowded-6","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-6","entity":"crowded-6","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-6","entity":"crowded-6","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-6","entity":"crowded-6","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.30000000000000004,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.31000000000000005,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.31000000000000005,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.30000000000000004,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.30000000000000004,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.31000000000000005,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.31000000000000005,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.30000000000000004,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.35000000000000003,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.35000000000000003,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.35000000000000003,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.36000000000000004,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.36000000000000004,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.36000000000000004,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.36000000000000004,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.36000000000000004,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.36000000000000004,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.35000000000000003,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.35000000000000003,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.35000000000000003,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.35000000000000003,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.36000000000000004,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.36000000000000004,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.35000000000000003,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.35000000000000003,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.36000000000000004,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.36000000000000004,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.35000000000000003,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.35000000000000003,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.36000000000000004,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.36000000000000004,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.35000000000000003,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-7","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-7","entity":"crowded-7","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.36000000100000007,"lo":0.349999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-7","entity":"crowded-7","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.36000000100000007,"lo":0.349999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-7","entity":"crowded-7","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-7","entity":"crowded-7","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-7","entity":"crowded-7","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-7","entity":"crowded-7","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.35000000000000003,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.36000000000000004,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.36000000000000004,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.35000000000000003,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.35000000000000003,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.36000000000000004,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.36000000000000004,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.35000000000000003,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.4,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.4,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.4,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.41000000000000003,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.41000000000000003,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.41000000000000003,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.41000000000000003,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.41000000000000003,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.41000000000000003,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.4,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.4,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.4,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.4,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.41000000000000003,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.41000000000000003,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.4,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.41000000000000003,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.41000000000000003,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.4,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.4,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.41000000000000003,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.41000000000000003,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.4,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-8","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-8","entity":"crowded-8","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.41000000100000006,"lo":0.399999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-8","entity":"crowded-8","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.41000000100000006,"lo":0.399999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-8","entity":"crowded-8","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-8","entity":"crowded-8","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-8","entity":"crowded-8","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-8","entity":"crowded-8","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.4,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.41000000000000003,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.41000000000000003,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.4,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.4,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.41000000000000003,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.41000000000000003,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.4,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.45,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.45,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.45,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.46,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.46,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.46,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.46,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.46,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.46,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.45,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.45,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.45,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.45,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.46,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.46,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.45,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.45,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.46,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.46,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.45,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.45,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.46,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.46,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.45,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-9","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-9","entity":"crowded-9","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.46000000100000005,"lo":0.449999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-9","entity":"crowded-9","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.46000000100000005,"lo":0.449999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-9","entity":"crowded-9","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-9","entity":"crowded-9","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-9","entity":"crowded-9","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-9","entity":"crowded-9","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.45,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.46,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.46,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.45,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.45,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.46,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.46,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.45,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.5,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.51,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.51,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.51,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.51,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.51,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.51,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.5,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.5,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.5,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.51,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.51,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.51,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.51,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.5,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.5,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.51,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.51,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.5,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-10","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-10","entity":"crowded-10","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.510000001,"lo":0.499999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-10","entity":"crowded-10","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.510000001,"lo":0.499999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-10","entity":"crowded-10","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-10","entity":"crowded-10","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-10","entity":"crowded-10","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-10","entity":"crowded-10","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.5,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.51,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.51,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.5,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.5,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.51,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.51,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.5,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.55,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.55,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.55,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.56,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.56,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.56,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.56,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.56,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.56,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.55,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.55,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.55,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.55,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.56,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.56,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.55,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.55,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.56,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.56,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.55,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.55,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.56,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.56,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.55,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-11","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-11","entity":"crowded-11","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.560000001,"lo":0.5499999990000001},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-11","entity":"crowded-11","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.560000001,"lo":0.5499999990000001},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-11","entity":"crowded-11","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-11","entity":"crowded-11","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-11","entity":"crowded-11","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-11","entity":"crowded-11","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.55,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.56,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.56,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.55,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.55,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.56,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.56,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.55,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.6000000000000001,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.6000000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.6000000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.6100000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.6100000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.6100000000000001,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.6100000000000001,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.6100000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.6100000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.6000000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.6000000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.6000000000000001,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.6000000000000001,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6100000000000001,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.6100000000000001,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.6000000000000001,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6000000000000001,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.6100000000000001,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.6100000000000001,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.6000000000000001,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.6000000000000001,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.6100000000000001,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.6100000000000001,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.6000000000000001,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-12","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-12","entity":"crowded-12","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.6100000010000001,"lo":0.5999999990000001},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-12","entity":"crowded-12","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.6100000010000001,"lo":0.5999999990000001},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-12","entity":"crowded-12","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-12","entity":"crowded-12","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-12","entity":"crowded-12","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-12","entity":"crowded-12","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.6000000000000001,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.6100000000000001,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.6100000000000001,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.6000000000000001,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.6000000000000001,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.6100000000000001,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.6100000000000001,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.6000000000000001,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.65,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.65,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.65,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.66,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.66,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.66,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.66,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.66,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.66,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.65,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.65,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.65,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.65,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.66,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.66,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.65,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.65,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.66,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.66,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.65,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.65,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.66,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.66,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.65,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-13","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-13","entity":"crowded-13","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.660000001,"lo":0.649999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-13","entity":"crowded-13","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.660000001,"lo":0.649999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-13","entity":"crowded-13","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-13","entity":"crowded-13","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-13","entity":"crowded-13","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-13","entity":"crowded-13","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.65,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.66,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.66,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.65,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.65,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.66,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.66,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.65,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.7000000000000001,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.7000000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.7000000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.7100000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.7100000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.7100000000000001,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.7100000000000001,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.7100000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.7100000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.7000000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.7000000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.7000000000000001,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.7000000000000001,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.7100000000000001,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.7100000000000001,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.7000000000000001,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.7000000000000001,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.7100000000000001,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.7100000000000001,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.7000000000000001,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.7000000000000001,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.7100000000000001,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.7100000000000001,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.7000000000000001,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-14","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-14","entity":"crowded-14","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.710000001,"lo":0.6999999990000001},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-14","entity":"crowded-14","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.710000001,"lo":0.6999999990000001},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-14","entity":"crowded-14","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-14","entity":"crowded-14","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-14","entity":"crowded-14","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-14","entity":"crowded-14","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.7000000000000001,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.7100000000000001,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.7100000000000001,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.7000000000000001,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.7000000000000001,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.7100000000000001,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.7100000000000001,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.7000000000000001,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.75,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.75,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.75,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.76,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.76,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.76,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.76,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.76,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.76,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.75,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.75,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.75,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.75,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.76,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.76,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.75,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.75,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.76,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.76,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.75,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.75,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.76,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.76,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.75,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-15","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-15","entity":"crowded-15","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.760000001,"lo":0.749999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-15","entity":"crowded-15","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.760000001,"lo":0.749999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-15","entity":"crowded-15","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-15","entity":"crowded-15","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-15","entity":"crowded-15","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-15","entity":"crowded-15","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.75,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.76,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.76,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.75,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.75,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.76,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.76,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.75,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.8,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.8,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.8,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.81,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.81,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.81,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.81,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.81,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.81,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.8,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.8,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.8,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.81,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.81,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.8,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.81,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.81,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.8,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.8,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.81,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.81,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.8,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-16","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-16","entity":"crowded-16","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.810000001,"lo":0.7999999990000001},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-16","entity":"crowded-16","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.810000001,"lo":0.7999999990000001},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-16","entity":"crowded-16","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-16","entity":"crowded-16","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-16","entity":"crowded-16","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-16","entity":"crowded-16","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.8,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.81,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.81,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.8,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.8,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.81,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.81,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.8,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.8500000000000001,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.8500000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.8500000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.8600000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.8600000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.8600000000000001,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.8600000000000001,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.8600000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.8600000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.8500000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.8500000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.8500000000000001,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.8500000000000001,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8600000000000001,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.8600000000000001,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.8500000000000001,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8500000000000001,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.8600000000000001,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.8600000000000001,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.8500000000000001,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.8500000000000001,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.8600000000000001,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.8600000000000001,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.8500000000000001,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-17","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-17","entity":"crowded-17","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.8600000010000001,"lo":0.8499999990000001},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-17","entity":"crowded-17","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.8600000010000001,"lo":0.8499999990000001},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-17","entity":"crowded-17","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-17","entity":"crowded-17","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-17","entity":"crowded-17","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-17","entity":"crowded-17","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.8500000000000001,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.8600000000000001,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.8600000000000001,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.8500000000000001,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.8500000000000001,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.8600000000000001,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.8600000000000001,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.8500000000000001,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.9,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.9,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.9,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.91,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.91,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.91,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.91,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.91,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.91,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.9,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.9,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.9,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.9,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.91,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.91,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.9,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.9,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.91,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.91,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.9,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.9,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.91,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.91,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.9,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-18","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-18","entity":"crowded-18","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.910000001,"lo":0.899999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-18","entity":"crowded-18","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.910000001,"lo":0.899999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-18","entity":"crowded-18","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-18","entity":"crowded-18","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-18","entity":"crowded-18","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-18","entity":"crowded-18","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.9,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.91,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.91,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.9,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.9,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.91,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.91,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.9,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.9500000000000001,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.9500000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.9500000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.9600000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.9600000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.9600000000000001,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.9600000000000001,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.9600000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.9600000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.9500000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.9500000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.9500000000000001,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.9500000000000001,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.9600000000000001,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.9600000000000001,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.9500000000000001,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.9500000000000001,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.9600000000000001,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.9600000000000001,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.9500000000000001,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.9500000000000001,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.9600000000000001,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.9600000000000001,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.9500000000000001,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-19","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-19","entity":"crowded-19","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.960000001,"lo":0.9499999990000001},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-19","entity":"crowded-19","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.960000001,"lo":0.9499999990000001},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-19","entity":"crowded-19","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-19","entity":"crowded-19","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-19","entity":"crowded-19","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-19","entity":"crowded-19","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.9500000000000001,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.9600000000000001,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[0.9600000000000001,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.9500000000000001,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.9500000000000001,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.9600000000000001,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.9600000000000001,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.9500000000000001,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.01,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.01,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.01,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.01,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.01,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.01,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.01,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.01,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.01,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.01,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.01,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.01,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-20","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-20","entity":"crowded-20","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.010000001,"lo":0.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-20","entity":"crowded-20","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.010000001,"lo":0.999999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-20","entity":"crowded-20","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-20","entity":"crowded-20","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-20","entity":"crowded-20","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-20","entity":"crowded-20","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.01,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.01,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.0,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.01,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.01,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.0,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.05,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.05,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.05,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.06,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.06,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.06,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.06,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.06,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.06,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.05,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.05,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.05,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.05,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.06,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.06,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.05,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.05,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.06,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.06,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.05,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.05,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.06,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.06,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.05,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-21","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-21","entity":"crowded-21","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.0600000010000001,"lo":1.049999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-21","entity":"crowded-21","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.0600000010000001,"lo":1.049999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-21","entity":"crowded-21","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-21","entity":"crowded-21","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-21","entity":"crowded-21","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-21","entity":"crowded-21","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.05,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.06,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.06,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.05,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.05,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.06,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.06,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.05,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.1,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.1,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.11,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.11,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.11,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.11,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.11,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.11,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.1,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.1,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.1,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.11,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.11,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.1,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.1,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.11,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.11,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.1,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.1,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.11,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.11,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.1,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-22","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-22","entity":"crowded-22","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.1100000010000002,"lo":1.099999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-22","entity":"crowded-22","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.1100000010000002,"lo":1.099999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-22","entity":"crowded-22","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-22","entity":"crowded-22","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-22","entity":"crowded-22","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-22","entity":"crowded-22","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.1,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.11,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.11,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.1,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.1,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.11,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.11,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.1,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.1500000000000001,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.1500000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1500000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.1600000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.1600000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1600000000000001,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.1600000000000001,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.1600000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1600000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.1500000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.1500000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1500000000000001,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.1500000000000001,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.1600000000000001,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.1600000000000001,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.1500000000000001,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.1500000000000001,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.1600000000000001,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.1600000000000001,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.1500000000000001,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.1500000000000001,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.1600000000000001,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.1600000000000001,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.1500000000000001,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-23","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-23","entity":"crowded-23","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.1600000010000002,"lo":1.149999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-23","entity":"crowded-23","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.1600000010000002,"lo":1.149999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-23","entity":"crowded-23","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-23","entity":"crowded-23","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-23","entity":"crowded-23","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-23","entity":"crowded-23","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.1500000000000001,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.1600000000000001,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.1600000000000001,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.1500000000000001,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.1500000000000001,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.1600000000000001,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.1600000000000001,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.1500000000000001,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.2000000000000002,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.2000000000000002,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.2000000000000002,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.2100000000000002,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.2100000000000002,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.2100000000000002,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.2100000000000002,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.2100000000000002,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.2100000000000002,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.2000000000000002,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.2000000000000002,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.2000000000000002,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.2000000000000002,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.2100000000000002,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.2100000000000002,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.2000000000000002,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.2000000000000002,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.2100000000000002,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.2100000000000002,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.2000000000000002,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.2000000000000002,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.2100000000000002,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.2100000000000002,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.2000000000000002,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-24","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-24","entity":"crowded-24","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.2100000010000003,"lo":1.199999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-24","entity":"crowded-24","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.2100000010000003,"lo":1.199999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-24","entity":"crowded-24","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-24","entity":"crowded-24","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-24","entity":"crowded-24","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-24","entity":"crowded-24","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.2000000000000002,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.2100000000000002,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.2100000000000002,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.2000000000000002,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.2000000000000002,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.2100000000000002,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.2100000000000002,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.2000000000000002,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.25,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.25,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.25,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.26,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.26,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.26,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.26,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.26,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.26,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.25,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.25,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.25,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.25,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.26,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.26,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.25,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.25,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.26,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.26,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.25,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.25,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.26,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.26,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.25,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-25","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-25","entity":"crowded-25","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.260000001,"lo":1.249999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-25","entity":"crowded-25","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.260000001,"lo":1.249999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-25","entity":"crowded-25","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-25","entity":"crowded-25","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-25","entity":"crowded-25","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-25","entity":"crowded-25","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.25,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.26,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.26,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.25,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.25,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.26,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.26,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.25,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.3,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.3,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.3,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.31,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.31,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.31,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.31,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.31,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.31,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.3,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.3,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.3,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.31,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.31,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.3,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.3,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.31,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.31,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.3,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.3,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.31,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.31,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.3,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-26","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-26","entity":"crowded-26","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.3100000010000001,"lo":1.299999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-26","entity":"crowded-26","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.3100000010000001,"lo":1.299999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-26","entity":"crowded-26","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-26","entity":"crowded-26","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-26","entity":"crowded-26","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-26","entity":"crowded-26","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.3,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.31,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.31,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.3,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.3,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.31,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.31,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.3,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.35,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.35,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.35,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.36,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.36,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.36,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.36,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.36,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.36,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.35,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.35,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.35,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.35,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.36,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.36,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.35,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.35,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.36,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.36,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.35,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.35,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.36,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.36,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.35,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-27","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-27","entity":"crowded-27","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.3600000010000002,"lo":1.349999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-27","entity":"crowded-27","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.3600000010000002,"lo":1.349999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-27","entity":"crowded-27","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-27","entity":"crowded-27","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-27","entity":"crowded-27","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-27","entity":"crowded-27","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.35,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.36,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.36,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.35,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.35,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.36,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.36,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.35,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.4000000000000001,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.4000000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.4000000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.4100000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.4100000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.4100000000000001,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.4100000000000001,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.4100000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.4100000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.4000000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.4000000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.4000000000000001,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.4000000000000001,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.4100000000000001,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.4100000000000001,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.4000000000000001,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.4000000000000001,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.4100000000000001,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.4100000000000001,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.4000000000000001,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.4000000000000001,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.4100000000000001,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.4100000000000001,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.4000000000000001,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-28","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-28","entity":"crowded-28","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.4100000010000002,"lo":1.399999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-28","entity":"crowded-28","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.4100000010000002,"lo":1.399999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-28","entity":"crowded-28","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-28","entity":"crowded-28","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-28","entity":"crowded-28","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-28","entity":"crowded-28","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.4000000000000001,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.4100000000000001,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.4100000000000001,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.4000000000000001,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.4000000000000001,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.4100000000000001,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.4100000000000001,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.4000000000000001,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.4500000000000002,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.4500000000000002,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.4500000000000002,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.4600000000000002,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.4600000000000002,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.4600000000000002,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.4600000000000002,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.4600000000000002,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.4600000000000002,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.4500000000000002,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.4500000000000002,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.4500000000000002,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.4500000000000002,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.4600000000000002,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.4600000000000002,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.4500000000000002,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.4500000000000002,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.4600000000000002,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.4600000000000002,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.4500000000000002,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.4500000000000002,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.4600000000000002,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.4600000000000002,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.4500000000000002,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-29","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-29","entity":"crowded-29","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.4600000010000003,"lo":1.449999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-29","entity":"crowded-29","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.4600000010000003,"lo":1.449999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-29","entity":"crowded-29","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-29","entity":"crowded-29","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-29","entity":"crowded-29","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-29","entity":"crowded-29","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.4500000000000002,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.4600000000000002,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.4600000000000002,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.4500000000000002,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.4500000000000002,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.4600000000000002,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.4600000000000002,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.4500000000000002,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.51,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.51,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.51,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.51,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.51,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.51,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.5,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.5,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.51,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.51,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.51,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.51,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.5,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.51,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.51,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-30","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-30","entity":"crowded-30","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.510000001,"lo":1.499999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-30","entity":"crowded-30","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.510000001,"lo":1.499999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-30","entity":"crowded-30","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-30","entity":"crowded-30","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-30","entity":"crowded-30","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-30","entity":"crowded-30","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.5,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.51,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.51,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.5,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.5,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.51,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.51,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.5,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.55,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.55,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.55,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.56,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.56,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.56,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.56,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.56,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.56,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.55,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.55,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.55,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.55,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.56,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.56,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.55,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.55,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.56,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.56,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.55,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.55,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.56,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.56,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.55,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-31","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-31","entity":"crowded-31","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.5600000010000001,"lo":1.549999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-31","entity":"crowded-31","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.5600000010000001,"lo":1.549999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-31","entity":"crowded-31","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-31","entity":"crowded-31","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-31","entity":"crowded-31","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-31","entity":"crowded-31","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.55,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.56,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.56,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.55,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.55,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.56,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.56,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.55,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.6,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.6,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.6,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.61,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.61,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.61,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.61,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.61,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.61,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.6,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.6,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.6,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.6,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.61,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.61,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.6,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.6,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.61,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.61,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.6,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.6,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.61,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.61,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.6,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-32","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-32","entity":"crowded-32","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.6100000010000002,"lo":1.599999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-32","entity":"crowded-32","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.6100000010000002,"lo":1.599999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-32","entity":"crowded-32","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-32","entity":"crowded-32","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-32","entity":"crowded-32","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-32","entity":"crowded-32","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.6,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.61,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.61,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.6,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.6,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.61,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.61,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.6,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.6500000000000001,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.6500000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.6500000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.6600000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.6600000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.6600000000000001,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.6600000000000001,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.6600000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.6600000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.6500000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.6500000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.6500000000000001,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.6500000000000001,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.6600000000000001,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.6600000000000001,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.6500000000000001,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.6500000000000001,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.6600000000000001,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.6600000000000001,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.6500000000000001,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.6500000000000001,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.6600000000000001,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.6600000000000001,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.6500000000000001,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-33","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-33","entity":"crowded-33","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.6600000010000002,"lo":1.649999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-33","entity":"crowded-33","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.6600000010000002,"lo":1.649999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-33","entity":"crowded-33","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-33","entity":"crowded-33","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-33","entity":"crowded-33","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-33","entity":"crowded-33","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.6500000000000001,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.6600000000000001,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.6600000000000001,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.6500000000000001,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.6500000000000001,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.6600000000000001,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.6600000000000001,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.6500000000000001,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.7000000000000002,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.7000000000000002,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.7000000000000002,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.7100000000000002,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.7100000000000002,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.7100000000000002,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.7100000000000002,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.7100000000000002,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.7100000000000002,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.7000000000000002,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.7000000000000002,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.7000000000000002,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.7000000000000002,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.7100000000000002,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.7100000000000002,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.7000000000000002,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.7000000000000002,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.7100000000000002,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.7100000000000002,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.7000000000000002,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.7000000000000002,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.7100000000000002,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.7100000000000002,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.7000000000000002,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-34","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-34","entity":"crowded-34","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.7100000010000003,"lo":1.699999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-34","entity":"crowded-34","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.7100000010000003,"lo":1.699999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-34","entity":"crowded-34","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-34","entity":"crowded-34","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-34","entity":"crowded-34","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-34","entity":"crowded-34","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.7000000000000002,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.7100000000000002,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.7100000000000002,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.7000000000000002,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.7000000000000002,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.7100000000000002,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.7100000000000002,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.7000000000000002,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.75,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.75,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.75,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.76,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.76,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.76,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.76,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.76,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.76,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.75,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.75,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.75,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.75,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.76,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.76,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.75,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.75,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.76,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.76,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.75,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.75,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.76,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.76,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.75,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-35","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-35","entity":"crowded-35","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.760000001,"lo":1.749999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-35","entity":"crowded-35","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.760000001,"lo":1.749999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-35","entity":"crowded-35","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-35","entity":"crowded-35","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-35","entity":"crowded-35","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-35","entity":"crowded-35","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.75,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.76,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.76,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.75,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.75,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.76,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.76,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.75,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.8,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.8,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.8,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.81,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.81,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.81,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.81,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.81,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.81,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.8,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.8,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.81,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.81,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.8,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.8,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.81,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.81,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.8,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.8,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.81,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.81,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.8,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-36","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-36","entity":"crowded-36","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.8100000010000001,"lo":1.799999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-36","entity":"crowded-36","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.8100000010000001,"lo":1.799999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-36","entity":"crowded-36","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-36","entity":"crowded-36","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-36","entity":"crowded-36","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-36","entity":"crowded-36","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.8,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.81,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.81,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.8,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.8,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.81,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.81,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.8,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.85,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.85,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.85,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.86,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.86,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.86,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.86,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.86,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.86,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.85,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.85,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.85,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.85,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.86,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.86,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.85,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.85,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.86,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.86,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.85,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.85,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.86,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.86,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.85,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-37","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-37","entity":"crowded-37","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.8600000010000002,"lo":1.849999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-37","entity":"crowded-37","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.8600000010000002,"lo":1.849999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-37","entity":"crowded-37","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-37","entity":"crowded-37","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-37","entity":"crowded-37","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-37","entity":"crowded-37","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.85,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.86,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.86,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.85,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.85,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.86,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.86,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.85,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.9000000000000001,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.9000000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.9000000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9100000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9100000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.9100000000000001,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.9100000000000001,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.9100000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.9100000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.9000000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.9000000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.9000000000000001,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.9000000000000001,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.9100000000000001,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.9100000000000001,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.9000000000000001,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.9000000000000001,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.9100000000000001,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.9100000000000001,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.9000000000000001,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.9000000000000001,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.9100000000000001,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.9100000000000001,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.9000000000000001,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-38","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-38","entity":"crowded-38","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.9100000010000002,"lo":1.899999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-38","entity":"crowded-38","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.9100000010000002,"lo":1.899999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-38","entity":"crowded-38","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-38","entity":"crowded-38","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-38","entity":"crowded-38","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-38","entity":"crowded-38","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.9000000000000001,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.9100000000000001,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.9100000000000001,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.9000000000000001,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.9000000000000001,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.9100000000000001,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.9100000000000001,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.9000000000000001,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.9500000000000002,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.9500000000000002,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.9500000000000002,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9600000000000002,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9600000000000002,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.9600000000000002,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.9600000000000002,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.9600000000000002,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.9600000000000002,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.9500000000000002,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.9500000000000002,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.9500000000000002,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.9500000000000002,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.9600000000000002,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.9600000000000002,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.9500000000000002,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.9500000000000002,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.9600000000000002,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.9600000000000002,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.9500000000000002,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.010000000000000009,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.9500000000000002,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.9600000000000002,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.9600000000000002,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.9500000000000002,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-39","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.010000000000000009,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-39","entity":"crowded-39","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.9600000010000003,"lo":1.949999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-39","entity":"crowded-39","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.9600000010000003,"lo":1.949999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-39","entity":"crowded-39","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-39","entity":"crowded-39","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-39","entity":"crowded-39","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000001000000008,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-39","entity":"crowded-39","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.9500000000000002,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[1.9600000000000002,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[1.9600000000000002,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.9500000000000002,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.9500000000000002,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[1.9600000000000002,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[1.9600000000000002,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.9500000000000002,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.01,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.01,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.01,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.01,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.01,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.01,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.01,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.01,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.01,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.01,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.01,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.01,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-40","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-40","entity":"crowded-40","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.010000001,"lo":1.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-40","entity":"crowded-40","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.010000001,"lo":1.999999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-40","entity":"crowded-40","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-40","entity":"crowded-40","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-40","entity":"crowded-40","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-40","entity":"crowded-40","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.01,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.01,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.0,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.01,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.01,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.0,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0500000000000003,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0500000000000003,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0500000000000003,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.06,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.06,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.06,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.06,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.06,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.06,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0500000000000003,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0500000000000003,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0500000000000003,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0500000000000003,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.06,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.06,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0500000000000003,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0500000000000003,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.06,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.06,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.0500000000000003,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.0500000000000003,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.06,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.06,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0500000000000003,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-41","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-41","entity":"crowded-41","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.060000001,"lo":2.049999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-41","entity":"crowded-41","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.060000001,"lo":2.049999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-41","entity":"crowded-41","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-41","entity":"crowded-41","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-41","entity":"crowded-41","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-41","entity":"crowded-41","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.0500000000000003,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.06,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.06,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.0500000000000003,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.0500000000000003,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.06,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.06,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.0500000000000003,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.1,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.1,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.1,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.11,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.11,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.11,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.11,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.11,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.11,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.1,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.1,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.1,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.1,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.11,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.11,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.1,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.1,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.11,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.11,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.1,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.1,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.11,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.11,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.1,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-42","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-42","entity":"crowded-42","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.110000001,"lo":2.099999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-42","entity":"crowded-42","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.110000001,"lo":2.099999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-42","entity":"crowded-42","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-42","entity":"crowded-42","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-42","entity":"crowded-42","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-42","entity":"crowded-42","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.1,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.11,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.11,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.1,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.1,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.11,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.11,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.1,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.15,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.15,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.15,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.1599999999999997,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.1599999999999997,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.1599999999999997,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.1599999999999997,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.1599999999999997,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.1599999999999997,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.15,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.15,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.15,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.15,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.1599999999999997,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.1599999999999997,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.15,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.15,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.1599999999999997,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.1599999999999997,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.15,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.15,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.1599999999999997,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.1599999999999997,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.15,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-43","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-43","entity":"crowded-43","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.160000001,"lo":2.149999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-43","entity":"crowded-43","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.160000001,"lo":2.149999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-43","entity":"crowded-43","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-43","entity":"crowded-43","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-43","entity":"crowded-43","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-43","entity":"crowded-43","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.15,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.1599999999999997,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.1599999999999997,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.15,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.15,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.1599999999999997,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.1599999999999997,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.15,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.2,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.2,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.2,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.21,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.21,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.21,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.21,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.21,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.21,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.2,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.2,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.2,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.2,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.21,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.21,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.2,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.2,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.21,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.21,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.2,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.2,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.21,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.21,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.2,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-44","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-44","entity":"crowded-44","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.210000001,"lo":2.199999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-44","entity":"crowded-44","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.210000001,"lo":2.199999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-44","entity":"crowded-44","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-44","entity":"crowded-44","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-44","entity":"crowded-44","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-44","entity":"crowded-44","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.2,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.21,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.21,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.2,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.2,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.21,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.21,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.2,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.25,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.25,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.25,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.26,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.26,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.26,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.26,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.26,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.26,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.25,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.25,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.25,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.25,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.26,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.26,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.25,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.25,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.26,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.26,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.25,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.25,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.26,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.26,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.25,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-45","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-45","entity":"crowded-45","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.260000001,"lo":2.249999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-45","entity":"crowded-45","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.260000001,"lo":2.249999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-45","entity":"crowded-45","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-45","entity":"crowded-45","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-45","entity":"crowded-45","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-45","entity":"crowded-45","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.25,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.26,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.26,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.25,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.25,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.26,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.26,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.25,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.3000000000000003,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.3000000000000003,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.3000000000000003,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.31,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.31,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.31,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.31,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.31,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.31,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.3000000000000003,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.3000000000000003,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.3000000000000003,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.3000000000000003,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.31,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.31,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.3000000000000003,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.3000000000000003,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.31,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.31,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.3000000000000003,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.3000000000000003,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.31,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.31,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.3000000000000003,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-46","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-46","entity":"crowded-46","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.310000001,"lo":2.299999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-46","entity":"crowded-46","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.310000001,"lo":2.299999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-46","entity":"crowded-46","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-46","entity":"crowded-46","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-46","entity":"crowded-46","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-46","entity":"crowded-46","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.3000000000000003,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.31,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.31,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.3000000000000003,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.3000000000000003,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.31,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.31,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.3000000000000003,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.35,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.35,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.35,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.36,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.36,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.36,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.36,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.36,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.36,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.35,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.35,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.35,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.35,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.36,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.36,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.35,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.35,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.36,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.36,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.35,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.35,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.36,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.36,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.35,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-47","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-47","entity":"crowded-47","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.360000001,"lo":2.349999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-47","entity":"crowded-47","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.360000001,"lo":2.349999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-47","entity":"crowded-47","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-47","entity":"crowded-47","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-47","entity":"crowded-47","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-47","entity":"crowded-47","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.35,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.36,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.36,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.35,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.35,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.36,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.36,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.35,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.4000000000000004,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.4000000000000004,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.4000000000000004,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.41,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.41,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.41,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.41,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.41,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.41,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.4000000000000004,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.4000000000000004,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.4000000000000004,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.4000000000000004,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.41,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.41,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.4000000000000004,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.4000000000000004,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.41,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.41,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.4000000000000004,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.4000000000000004,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.41,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.41,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.4000000000000004,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-48","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-48","entity":"crowded-48","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.4100000010000002,"lo":2.3999999990000003},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-48","entity":"crowded-48","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.4100000010000002,"lo":2.3999999990000003},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-48","entity":"crowded-48","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-48","entity":"crowded-48","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-48","entity":"crowded-48","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-48","entity":"crowded-48","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.4000000000000004,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.41,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.41,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.4000000000000004,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.4000000000000004,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.41,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.41,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.4000000000000004,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.45,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.45,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.45,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.46,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.46,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.46,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.46,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.46,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.46,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.45,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.45,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.45,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.45,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.46,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.46,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.45,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.45,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.46,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.46,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.45,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.45,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.46,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.46,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.45,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-49","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-49","entity":"crowded-49","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.460000001,"lo":2.449999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-49","entity":"crowded-49","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.460000001,"lo":2.449999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-49","entity":"crowded-49","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-49","entity":"crowded-49","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-49","entity":"crowded-49","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-49","entity":"crowded-49","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.45,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.46,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.46,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.45,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.45,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.46,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.46,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.45,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.51,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.51,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.51,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.51,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.51,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.51,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.51,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.51,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.51,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.51,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.5,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.51,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.51,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-50","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-50","entity":"crowded-50","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.510000001,"lo":2.499999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-50","entity":"crowded-50","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.510000001,"lo":2.499999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-50","entity":"crowded-50","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-50","entity":"crowded-50","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-50","entity":"crowded-50","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-50","entity":"crowded-50","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.5,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.51,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.51,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.5,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.5,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.51,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.51,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.5,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5500000000000003,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5500000000000003,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5500000000000003,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.56,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.56,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.56,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.56,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.56,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.56,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5500000000000003,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5500000000000003,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5500000000000003,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5500000000000003,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.56,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.56,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5500000000000003,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5500000000000003,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.56,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.56,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.5500000000000003,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.5500000000000003,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.56,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.56,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.5500000000000003,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-51","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-51","entity":"crowded-51","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.560000001,"lo":2.549999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-51","entity":"crowded-51","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.560000001,"lo":2.549999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-51","entity":"crowded-51","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-51","entity":"crowded-51","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-51","entity":"crowded-51","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-51","entity":"crowded-51","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.5500000000000003,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.56,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.56,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.5500000000000003,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.5500000000000003,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.56,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.56,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.5500000000000003,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.6,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.6,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.6,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.61,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.61,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.61,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.61,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.61,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.61,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.6,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.6,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.6,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.6,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.61,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.61,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.6,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.6,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.61,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.61,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.6,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.6,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.61,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.61,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.6,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-52","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-52","entity":"crowded-52","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.610000001,"lo":2.599999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-52","entity":"crowded-52","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.610000001,"lo":2.599999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-52","entity":"crowded-52","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-52","entity":"crowded-52","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-52","entity":"crowded-52","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-52","entity":"crowded-52","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.6,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.61,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.61,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.6,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.6,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.61,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.61,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.6,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.6500000000000004,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.6500000000000004,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.6500000000000004,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.66,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.66,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.66,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.66,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.66,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.66,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.6500000000000004,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.6500000000000004,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.6500000000000004,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.6500000000000004,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.66,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.66,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.6500000000000004,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.6500000000000004,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.66,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.66,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.6500000000000004,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.6500000000000004,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.66,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.66,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.6500000000000004,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-53","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-53","entity":"crowded-53","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.6600000010000002,"lo":2.6499999990000003},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-53","entity":"crowded-53","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.6600000010000002,"lo":2.6499999990000003},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-53","entity":"crowded-53","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-53","entity":"crowded-53","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-53","entity":"crowded-53","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-53","entity":"crowded-53","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.6500000000000004,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.66,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.66,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.6500000000000004,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.6500000000000004,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.66,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.66,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.6500000000000004,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.7,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.7,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.7,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.71,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.71,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.71,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.71,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.71,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.71,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.7,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.7,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.7,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.7,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.71,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.71,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.7,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.7,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.71,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.71,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.7,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.7,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.71,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.71,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.7,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-54","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-54","entity":"crowded-54","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.710000001,"lo":2.699999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-54","entity":"crowded-54","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.710000001,"lo":2.699999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-54","entity":"crowded-54","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-54","entity":"crowded-54","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-54","entity":"crowded-54","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-54","entity":"crowded-54","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.7,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.71,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.71,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.7,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.7,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.71,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.71,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.7,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.75,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.75,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.75,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.76,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.76,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.76,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.76,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.76,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.76,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.75,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.75,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.75,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.75,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.76,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.76,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.75,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.75,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.76,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.76,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.75,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.75,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.76,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.76,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.75,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-55","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-55","entity":"crowded-55","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.760000001,"lo":2.749999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-55","entity":"crowded-55","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.760000001,"lo":2.749999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-55","entity":"crowded-55","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-55","entity":"crowded-55","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-55","entity":"crowded-55","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-55","entity":"crowded-55","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.75,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.76,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.76,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.75,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.75,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.76,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.76,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.75,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.8000000000000003,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.8000000000000003,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.8000000000000003,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.81,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.81,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.81,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.81,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.81,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.81,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.8000000000000003,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.8000000000000003,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.8000000000000003,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.8000000000000003,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.81,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.81,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.8000000000000003,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.8000000000000003,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.81,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.81,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.8000000000000003,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.8000000000000003,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.81,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.81,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.8000000000000003,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-56","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-56","entity":"crowded-56","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.810000001,"lo":2.799999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-56","entity":"crowded-56","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.810000001,"lo":2.799999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-56","entity":"crowded-56","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-56","entity":"crowded-56","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-56","entity":"crowded-56","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-56","entity":"crowded-56","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.8000000000000003,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.81,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.81,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.8000000000000003,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.8000000000000003,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.81,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.81,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.8000000000000003,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.85,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.85,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.85,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.86,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.86,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.86,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.86,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.86,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.86,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.85,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.85,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.85,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.85,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.86,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.86,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.85,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.85,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.86,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.86,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.85,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.85,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.86,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.86,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.85,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-57","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-57","entity":"crowded-57","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.860000001,"lo":2.849999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-57","entity":"crowded-57","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.860000001,"lo":2.849999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-57","entity":"crowded-57","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-57","entity":"crowded-57","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-57","entity":"crowded-57","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-57","entity":"crowded-57","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.85,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.86,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.86,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.85,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.85,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.86,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.86,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.85,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.9000000000000004,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.9000000000000004,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.9000000000000004,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.91,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.91,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.91,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.91,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.91,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.91,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.9000000000000004,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.9000000000000004,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.9000000000000004,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.9000000000000004,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.91,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.91,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.9000000000000004,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.9000000000000004,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.91,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.91,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.9000000000000004,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.9000000000000004,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.91,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.91,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.9000000000000004,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-58","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-58","entity":"crowded-58","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.9100000010000002,"lo":2.8999999990000003},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-58","entity":"crowded-58","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.9100000010000002,"lo":2.8999999990000003},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-58","entity":"crowded-58","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-58","entity":"crowded-58","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-58","entity":"crowded-58","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-58","entity":"crowded-58","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.9000000000000004,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.91,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.91,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.9000000000000004,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.9000000000000004,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.91,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.91,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.9000000000000004,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.95,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.95,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.95,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.96,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.96,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.96,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.96,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.96,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.96,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.95,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.95,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.95,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.95,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.96,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.96,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.95,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.95,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.96,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.96,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.95,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.95,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.96,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.96,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.95,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-59","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-59","entity":"crowded-59","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.960000001,"lo":2.949999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-59","entity":"crowded-59","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.960000001,"lo":2.949999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-59","entity":"crowded-59","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-59","entity":"crowded-59","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-59","entity":"crowded-59","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-59","entity":"crowded-59","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.95,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.96,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.96,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.95,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.95,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.96,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.96,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.95,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.01,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.01,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.01,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.01,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.01,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.01,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.01,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.01,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.01,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.01,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.01,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.01,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-60","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-60","entity":"crowded-60","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.010000001,"lo":2.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-60","entity":"crowded-60","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.010000001,"lo":2.999999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-60","entity":"crowded-60","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-60","entity":"crowded-60","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-60","entity":"crowded-60","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-60","entity":"crowded-60","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.01,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.01,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.0,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.01,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.01,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.0,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0500000000000003,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0500000000000003,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0500000000000003,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.06,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.06,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.06,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.06,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.06,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.06,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.0500000000000003,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.0500000000000003,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.0500000000000003,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.0500000000000003,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.06,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.06,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.0500000000000003,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0500000000000003,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.06,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.06,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.0500000000000003,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.0500000000000003,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.06,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.06,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0500000000000003,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-61","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-61","entity":"crowded-61","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.060000001,"lo":3.049999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-61","entity":"crowded-61","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.060000001,"lo":3.049999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-61","entity":"crowded-61","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-61","entity":"crowded-61","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-61","entity":"crowded-61","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-61","entity":"crowded-61","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.0500000000000003,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.06,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.06,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.0500000000000003,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.0500000000000003,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.06,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.06,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.0500000000000003,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.1,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.1,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.1,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.11,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.11,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.11,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.11,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.11,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.11,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.1,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.1,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.1,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.1,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.11,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.11,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.1,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.1,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.11,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.11,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.1,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.1,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.11,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.11,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.1,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-62","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-62","entity":"crowded-62","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.110000001,"lo":3.099999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-62","entity":"crowded-62","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.110000001,"lo":3.099999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-62","entity":"crowded-62","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-62","entity":"crowded-62","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-62","entity":"crowded-62","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-62","entity":"crowded-62","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.1,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.11,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.11,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.1,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.1,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.11,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.11,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.1,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.1500000000000004,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.1500000000000004,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.1500000000000004,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.16,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.16,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.16,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.16,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.16,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.16,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.1500000000000004,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.1500000000000004,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.1500000000000004,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.1500000000000004,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.16,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.16,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.1500000000000004,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.1500000000000004,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.16,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.16,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.1500000000000004,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.1500000000000004,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.16,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.16,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.1500000000000004,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-63","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-63","entity":"crowded-63","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.1600000010000002,"lo":3.1499999990000003},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-63","entity":"crowded-63","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.1600000010000002,"lo":3.1499999990000003},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-63","entity":"crowded-63","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-63","entity":"crowded-63","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-63","entity":"crowded-63","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-63","entity":"crowded-63","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.1500000000000004,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.16,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.16,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.1500000000000004,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.1500000000000004,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.16,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.16,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.1500000000000004,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.2,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.2,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.2,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.21,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.21,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.21,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.21,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.21,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.21,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.2,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.2,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.2,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.2,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.21,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.21,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.2,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.2,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.21,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.21,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.2,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.2,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.21,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.21,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.2,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-64","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-64","entity":"crowded-64","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.210000001,"lo":3.199999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-64","entity":"crowded-64","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.210000001,"lo":3.199999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-64","entity":"crowded-64","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-64","entity":"crowded-64","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-64","entity":"crowded-64","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-64","entity":"crowded-64","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.2,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.21,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.21,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.2,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.2,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.21,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.21,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.2,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.25,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.25,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.25,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.26,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.26,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.26,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.26,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.26,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.26,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.25,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.25,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.25,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.25,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.26,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.26,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.25,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.25,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.26,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.26,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.25,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.25,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.26,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.26,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.25,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-65","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-65","entity":"crowded-65","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.260000001,"lo":3.249999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-65","entity":"crowded-65","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.260000001,"lo":3.249999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-65","entity":"crowded-65","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-65","entity":"crowded-65","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-65","entity":"crowded-65","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-65","entity":"crowded-65","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.25,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.26,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.26,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.25,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.25,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.26,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.26,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.25,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.3000000000000003,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.3000000000000003,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.3000000000000003,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.31,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.31,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.31,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.31,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.31,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.31,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.3000000000000003,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.3000000000000003,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.3000000000000003,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.3000000000000003,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.31,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.31,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.3000000000000003,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.3000000000000003,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.31,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.31,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.3000000000000003,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.3000000000000003,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.31,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.31,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.3000000000000003,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-66","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-66","entity":"crowded-66","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.310000001,"lo":3.299999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-66","entity":"crowded-66","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.310000001,"lo":3.299999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-66","entity":"crowded-66","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-66","entity":"crowded-66","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-66","entity":"crowded-66","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-66","entity":"crowded-66","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.3000000000000003,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.31,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.31,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.3000000000000003,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.3000000000000003,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.31,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.31,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.3000000000000003,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.35,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.35,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.35,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.36,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.36,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.36,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.36,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.36,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.36,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.35,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.35,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.35,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.35,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.36,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.36,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.35,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.35,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.36,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.36,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.35,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.35,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.36,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.36,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.35,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-67","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-67","entity":"crowded-67","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.360000001,"lo":3.349999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-67","entity":"crowded-67","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.360000001,"lo":3.349999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-67","entity":"crowded-67","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-67","entity":"crowded-67","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-67","entity":"crowded-67","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-67","entity":"crowded-67","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.35,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.36,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.36,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.35,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.35,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.36,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.36,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.35,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.4000000000000004,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.4000000000000004,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.4000000000000004,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.41,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.41,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.41,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.41,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.41,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.41,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.4000000000000004,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.4000000000000004,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.4000000000000004,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.4000000000000004,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.41,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.41,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.4000000000000004,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.4000000000000004,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.41,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.41,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.4000000000000004,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.4000000000000004,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.41,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.41,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.4000000000000004,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-68","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-68","entity":"crowded-68","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.4100000010000002,"lo":3.3999999990000003},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-68","entity":"crowded-68","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.4100000010000002,"lo":3.3999999990000003},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-68","entity":"crowded-68","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-68","entity":"crowded-68","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-68","entity":"crowded-68","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-68","entity":"crowded-68","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.4000000000000004,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.41,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.41,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.4000000000000004,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.4000000000000004,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.41,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.41,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.4000000000000004,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.45,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.45,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.45,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.46,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.46,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.46,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.46,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.46,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.46,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.45,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.45,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.45,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.45,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.46,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.46,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.45,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.45,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.46,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.46,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.45,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.45,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.46,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.46,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.45,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-69","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-69","entity":"crowded-69","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.460000001,"lo":3.449999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-69","entity":"crowded-69","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.460000001,"lo":3.449999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-69","entity":"crowded-69","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-69","entity":"crowded-69","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-69","entity":"crowded-69","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-69","entity":"crowded-69","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.45,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.46,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.46,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.45,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.45,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.46,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.46,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.45,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.51,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.51,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.51,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.51,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.51,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.51,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.51,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.51,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.51,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.51,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.5,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.51,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.51,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-70","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-70","entity":"crowded-70","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.510000001,"lo":3.499999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-70","entity":"crowded-70","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.510000001,"lo":3.499999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-70","entity":"crowded-70","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-70","entity":"crowded-70","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-70","entity":"crowded-70","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-70","entity":"crowded-70","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.5,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.51,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.51,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.5,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.5,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.51,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.51,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.5,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5500000000000003,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5500000000000003,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5500000000000003,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.56,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.56,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.56,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.56,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.56,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.56,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5500000000000003,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5500000000000003,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5500000000000003,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5500000000000003,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.56,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.56,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5500000000000003,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5500000000000003,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.56,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.56,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.5500000000000003,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.5500000000000003,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.56,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.56,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5500000000000003,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-71","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-71","entity":"crowded-71","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.560000001,"lo":3.549999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-71","entity":"crowded-71","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.560000001,"lo":3.549999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-71","entity":"crowded-71","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-71","entity":"crowded-71","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-71","entity":"crowded-71","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-71","entity":"crowded-71","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.5500000000000003,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.56,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.56,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.5500000000000003,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.5500000000000003,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.56,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.56,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.5500000000000003,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.6,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.6,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.6,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.61,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.61,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.61,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.61,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.61,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.61,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.6,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.6,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.6,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.6,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.61,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.61,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.6,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.6,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.61,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.61,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.6,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.6,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.61,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.61,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.6,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-72","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-72","entity":"crowded-72","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.610000001,"lo":3.599999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-72","entity":"crowded-72","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.610000001,"lo":3.599999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-72","entity":"crowded-72","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-72","entity":"crowded-72","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-72","entity":"crowded-72","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-72","entity":"crowded-72","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.6,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.61,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.61,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.6,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.6,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.61,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.61,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.6,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.6500000000000004,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.6500000000000004,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.6500000000000004,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.66,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.66,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.66,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.66,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.66,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.66,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.6500000000000004,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.6500000000000004,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.6500000000000004,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.6500000000000004,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.66,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.66,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.6500000000000004,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.6500000000000004,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.66,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.66,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.6500000000000004,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.6500000000000004,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.66,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.66,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.6500000000000004,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-73","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-73","entity":"crowded-73","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.6600000010000002,"lo":3.6499999990000003},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-73","entity":"crowded-73","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.6600000010000002,"lo":3.6499999990000003},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-73","entity":"crowded-73","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-73","entity":"crowded-73","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-73","entity":"crowded-73","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-73","entity":"crowded-73","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.6500000000000004,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.66,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.66,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.6500000000000004,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.6500000000000004,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.66,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.66,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.6500000000000004,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.7,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.7,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.7,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.71,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.71,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.71,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.71,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.71,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.71,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.7,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.7,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.7,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.7,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.71,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.71,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.7,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.7,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.71,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.71,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.7,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.7,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.71,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.71,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.7,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-74","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-74","entity":"crowded-74","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.710000001,"lo":3.699999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-74","entity":"crowded-74","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.710000001,"lo":3.699999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-74","entity":"crowded-74","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-74","entity":"crowded-74","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-74","entity":"crowded-74","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-74","entity":"crowded-74","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.7,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.71,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.71,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.7,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.7,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.71,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.71,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.7,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.75,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.75,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.75,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.76,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.76,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.76,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.76,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.76,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.76,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.75,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.75,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.75,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.75,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.76,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.76,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.75,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.75,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.76,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.76,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.75,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.75,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.76,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.76,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.75,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-75","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-75","entity":"crowded-75","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.760000001,"lo":3.749999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-75","entity":"crowded-75","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.760000001,"lo":3.749999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-75","entity":"crowded-75","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-75","entity":"crowded-75","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-75","entity":"crowded-75","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-75","entity":"crowded-75","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.75,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.76,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.76,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.75,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.75,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.76,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.76,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.75,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.8000000000000003,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.8000000000000003,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.8000000000000003,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.81,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.81,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.81,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.81,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.81,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.81,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.8000000000000003,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.8000000000000003,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.8000000000000003,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.8000000000000003,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.81,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.81,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.8000000000000003,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.8000000000000003,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.81,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.81,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.8000000000000003,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.8000000000000003,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.81,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.81,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.8000000000000003,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-76","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-76","entity":"crowded-76","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.810000001,"lo":3.799999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-76","entity":"crowded-76","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.810000001,"lo":3.799999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-76","entity":"crowded-76","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-76","entity":"crowded-76","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-76","entity":"crowded-76","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-76","entity":"crowded-76","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.8000000000000003,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.81,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.81,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.8000000000000003,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.8000000000000003,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.81,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.81,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.8000000000000003,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.85,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.85,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.85,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.86,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.86,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.86,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.86,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.86,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.86,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.85,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.85,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.85,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.85,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.86,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.86,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.85,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.85,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.86,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.86,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.85,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.85,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.86,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.86,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.85,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-77","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-77","entity":"crowded-77","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.860000001,"lo":3.849999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-77","entity":"crowded-77","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.860000001,"lo":3.849999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-77","entity":"crowded-77","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-77","entity":"crowded-77","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-77","entity":"crowded-77","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-77","entity":"crowded-77","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.85,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.86,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.86,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.85,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.85,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.86,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.86,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.85,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.9000000000000004,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.9000000000000004,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.9000000000000004,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.91,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.91,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.91,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.91,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.91,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.91,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.9000000000000004,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.9000000000000004,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.9000000000000004,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.9000000000000004,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.91,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.91,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.9000000000000004,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.9000000000000004,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.91,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.91,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.9000000000000004,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.9000000000000004,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.91,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.91,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.9000000000000004,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-78","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-78","entity":"crowded-78","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.9100000010000002,"lo":3.8999999990000003},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-78","entity":"crowded-78","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.9100000010000002,"lo":3.8999999990000003},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-78","entity":"crowded-78","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-78","entity":"crowded-78","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-78","entity":"crowded-78","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-78","entity":"crowded-78","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.9000000000000004,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.91,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.91,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.9000000000000004,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.9000000000000004,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.91,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.91,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.9000000000000004,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.95,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.95,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.95,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.96,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.96,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.96,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.96,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.96,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.96,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.95,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.95,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.95,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.95,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.96,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.96,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.95,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.95,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.96,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.96,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.95,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.95,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.96,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.96,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.95,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-79","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-79","entity":"crowded-79","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.960000001,"lo":3.949999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-79","entity":"crowded-79","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.960000001,"lo":3.949999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-79","entity":"crowded-79","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-79","entity":"crowded-79","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-79","entity":"crowded-79","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-79","entity":"crowded-79","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.95,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.96,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.96,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.95,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.95,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.96,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.96,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.95,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.01,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.01,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.01,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.01,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.01,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.01,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.01,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.01,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.01,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.01,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.01,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.01,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-80","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-80","entity":"crowded-80","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.010000001,"lo":3.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-80","entity":"crowded-80","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.010000001,"lo":3.999999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-80","entity":"crowded-80","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-80","entity":"crowded-80","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-80","entity":"crowded-80","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-80","entity":"crowded-80","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.01,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.01,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.0,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.01,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.01,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.0,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.05,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.05,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.05,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.06,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.06,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.06,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.06,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.06,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.06,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.05,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.05,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.05,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.05,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.06,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.06,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.05,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.05,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.06,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.06,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.05,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.05,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.06,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.06,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.05,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-81","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-81","entity":"crowded-81","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.060000001,"lo":4.049999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-81","entity":"crowded-81","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.060000001,"lo":4.049999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-81","entity":"crowded-81","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-81","entity":"crowded-81","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-81","entity":"crowded-81","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-81","entity":"crowded-81","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.05,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.06,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.06,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.05,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.05,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.06,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.06,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.05,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.1000000000000005,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.1000000000000005,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.1000000000000005,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.11,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.11,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.11,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.11,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.11,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.11,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.1000000000000005,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.1000000000000005,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.1000000000000005,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.1000000000000005,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.11,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.11,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.1000000000000005,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.1000000000000005,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.11,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.11,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.1000000000000005,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.1000000000000005,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.11,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.11,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.1000000000000005,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-82","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-82","entity":"crowded-82","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.110000001,"lo":4.0999999990000005},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-82","entity":"crowded-82","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.110000001,"lo":4.0999999990000005},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-82","entity":"crowded-82","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-82","entity":"crowded-82","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-82","entity":"crowded-82","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-82","entity":"crowded-82","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.1000000000000005,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.11,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.11,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.1000000000000005,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.1000000000000005,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.11,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.11,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.1000000000000005,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.15,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.15,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.15,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.16,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.16,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.16,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.16,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.16,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.16,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.15,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.15,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.15,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.15,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.16,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.16,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.15,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.15,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.16,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.16,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.15,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.15,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.16,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.16,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.15,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-83","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-83","entity":"crowded-83","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.160000001,"lo":4.149999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-83","entity":"crowded-83","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.160000001,"lo":4.149999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-83","entity":"crowded-83","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-83","entity":"crowded-83","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-83","entity":"crowded-83","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-83","entity":"crowded-83","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.15,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.16,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.16,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.15,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.15,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.16,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.16,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.15,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.2,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.2,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.2,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.21,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.21,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.21,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.21,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.21,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.21,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.2,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.2,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.2,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.2,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.21,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.21,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.2,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.2,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.21,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.21,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.2,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.2,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.21,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.21,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.2,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-84","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-84","entity":"crowded-84","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.210000001,"lo":4.199999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-84","entity":"crowded-84","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.210000001,"lo":4.199999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-84","entity":"crowded-84","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-84","entity":"crowded-84","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-84","entity":"crowded-84","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-84","entity":"crowded-84","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.2,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.21,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.21,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.2,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.2,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.21,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.21,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.2,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.25,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.25,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.25,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.26,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.26,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.26,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.26,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.26,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.26,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.25,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.25,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.25,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.25,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.26,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.26,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.25,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.25,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.26,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.26,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.25,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.25,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.26,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.26,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.25,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-85","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-85","entity":"crowded-85","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.260000001,"lo":4.249999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-85","entity":"crowded-85","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.260000001,"lo":4.249999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-85","entity":"crowded-85","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-85","entity":"crowded-85","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-85","entity":"crowded-85","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-85","entity":"crowded-85","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.25,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.26,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.26,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.25,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.25,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.26,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.26,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.25,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.3,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.3,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.3,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.31,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.31,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.31,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.31,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.31,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.31,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.3,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.3,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.3,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.3,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.31,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.31,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.3,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.3,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.31,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.31,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.3,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.3,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.31,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.31,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.3,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-86","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-86","entity":"crowded-86","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.310000001,"lo":4.299999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-86","entity":"crowded-86","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.310000001,"lo":4.299999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-86","entity":"crowded-86","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-86","entity":"crowded-86","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-86","entity":"crowded-86","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-86","entity":"crowded-86","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.3,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.31,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.31,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.3,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.3,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.31,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.31,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.3,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.3500000000000005,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.3500000000000005,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.3500000000000005,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.36,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.36,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.36,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.36,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.36,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.36,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.3500000000000005,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.3500000000000005,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.3500000000000005,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.3500000000000005,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.36,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.36,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.3500000000000005,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.3500000000000005,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.36,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.36,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.3500000000000005,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.3500000000000005,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.36,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.36,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.3500000000000005,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-87","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-87","entity":"crowded-87","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.360000001,"lo":4.3499999990000005},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-87","entity":"crowded-87","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.360000001,"lo":4.3499999990000005},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-87","entity":"crowded-87","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-87","entity":"crowded-87","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-87","entity":"crowded-87","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-87","entity":"crowded-87","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.3500000000000005,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.36,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.36,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.3500000000000005,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.3500000000000005,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.36,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.36,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.3500000000000005,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.4,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.4,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.4,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.41,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.41,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.41,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.41,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.41,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.41,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.4,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.4,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.4,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.4,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.41,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.41,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.4,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.4,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.41,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.41,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.4,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.4,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.41,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.41,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.4,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-88","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-88","entity":"crowded-88","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.410000001,"lo":4.399999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-88","entity":"crowded-88","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.410000001,"lo":4.399999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-88","entity":"crowded-88","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-88","entity":"crowded-88","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-88","entity":"crowded-88","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-88","entity":"crowded-88","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.4,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.41,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.41,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.4,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.4,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.41,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.41,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.4,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.45,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.45,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.45,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.46,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.46,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.46,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.46,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.46,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.46,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.45,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.45,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.45,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.45,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.46,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.46,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.45,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.45,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.46,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.46,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.45,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.45,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.46,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.46,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.45,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-89","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-89","entity":"crowded-89","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.460000001,"lo":4.449999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-89","entity":"crowded-89","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.460000001,"lo":4.449999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-89","entity":"crowded-89","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-89","entity":"crowded-89","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-89","entity":"crowded-89","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-89","entity":"crowded-89","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.45,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.46,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.46,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.45,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.45,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.46,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.46,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.45,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.51,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.51,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.51,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.51,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.51,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.51,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.51,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.51,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.5,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.5,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.51,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.51,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.5,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.51,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.51,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-90","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-90","entity":"crowded-90","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.510000001,"lo":4.499999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-90","entity":"crowded-90","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.510000001,"lo":4.499999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-90","entity":"crowded-90","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-90","entity":"crowded-90","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-90","entity":"crowded-90","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-90","entity":"crowded-90","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.5,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.51,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.51,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.5,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.5,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.51,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.51,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.5,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.55,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.55,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.55,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.56,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.56,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.56,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.56,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.56,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.56,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.55,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.55,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.55,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.55,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.56,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.56,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.55,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.55,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.56,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.56,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.55,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.55,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.56,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.56,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.55,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-91","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-91","entity":"crowded-91","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.560000001,"lo":4.549999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-91","entity":"crowded-91","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.560000001,"lo":4.549999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-91","entity":"crowded-91","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-91","entity":"crowded-91","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-91","entity":"crowded-91","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-91","entity":"crowded-91","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.55,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.56,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.56,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.55,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.55,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.56,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.56,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.55,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.6000000000000005,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.6000000000000005,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.6000000000000005,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.61,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.61,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.61,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.61,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.61,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.61,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.6000000000000005,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.6000000000000005,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.6000000000000005,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.6000000000000005,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.61,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.61,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.6000000000000005,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.6000000000000005,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.61,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.61,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.6000000000000005,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.6000000000000005,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.61,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.61,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.6000000000000005,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-92","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-92","entity":"crowded-92","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.610000001,"lo":4.5999999990000005},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-92","entity":"crowded-92","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.610000001,"lo":4.5999999990000005},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-92","entity":"crowded-92","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-92","entity":"crowded-92","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-92","entity":"crowded-92","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-92","entity":"crowded-92","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.6000000000000005,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.61,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.61,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.6000000000000005,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.6000000000000005,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.61,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.61,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.6000000000000005,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.65,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.65,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.65,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.66,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.66,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.66,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.66,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.66,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.66,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.65,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.65,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.65,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.65,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.66,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.66,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.65,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.65,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.66,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.66,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.65,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.65,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.66,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.66,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.65,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-93","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-93","entity":"crowded-93","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.660000001,"lo":4.649999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-93","entity":"crowded-93","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.660000001,"lo":4.649999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-93","entity":"crowded-93","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-93","entity":"crowded-93","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-93","entity":"crowded-93","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-93","entity":"crowded-93","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.65,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.66,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.66,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.65,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.65,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.66,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.66,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.65,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.7,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.7,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.7,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.71,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.71,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.71,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.71,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.71,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.71,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.7,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.7,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.7,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.7,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.71,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.71,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.7,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.7,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.71,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.71,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.7,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.7,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.71,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.71,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.7,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-94","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-94","entity":"crowded-94","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.710000001,"lo":4.699999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-94","entity":"crowded-94","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.710000001,"lo":4.699999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-94","entity":"crowded-94","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-94","entity":"crowded-94","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-94","entity":"crowded-94","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-94","entity":"crowded-94","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.7,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.71,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.71,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.7,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.7,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.71,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.71,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.7,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.75,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.75,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.75,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.76,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.76,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.76,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.76,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.76,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.76,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.75,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.75,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.75,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.75,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.76,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.76,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.75,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.75,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.76,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.76,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.75,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.75,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.76,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.76,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.75,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-95","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-95","entity":"crowded-95","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.760000001,"lo":4.749999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-95","entity":"crowded-95","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.760000001,"lo":4.749999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-95","entity":"crowded-95","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-95","entity":"crowded-95","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-95","entity":"crowded-95","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-95","entity":"crowded-95","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.75,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.76,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.76,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.75,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.75,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.76,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.76,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.75,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.800000000000001,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.800000000000001,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.800000000000001,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.8100000000000005,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.8100000000000005,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.8100000000000005,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.8100000000000005,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.8100000000000005,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.8100000000000005,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.800000000000001,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.800000000000001,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.800000000000001,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.800000000000001,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.8100000000000005,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.8100000000000005,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.800000000000001,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.800000000000001,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.8100000000000005,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.8100000000000005,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.800000000000001,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.800000000000001,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.8100000000000005,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.8100000000000005,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.800000000000001,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-96","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-96","entity":"crowded-96","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.810000001000001,"lo":4.799999999000001},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-96","entity":"crowded-96","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.810000001000001,"lo":4.799999999000001},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-96","entity":"crowded-96","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-96","entity":"crowded-96","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-96","entity":"crowded-96","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-96","entity":"crowded-96","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.800000000000001,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.8100000000000005,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.8100000000000005,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.800000000000001,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.800000000000001,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.8100000000000005,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.8100000000000005,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.800000000000001,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.8500000000000005,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.8500000000000005,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.8500000000000005,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.86,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.86,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.86,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.86,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.86,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.86,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.8500000000000005,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.8500000000000005,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.8500000000000005,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.8500000000000005,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.86,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.86,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.8500000000000005,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.8500000000000005,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.86,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.86,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.8500000000000005,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.8500000000000005,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.86,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.86,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.8500000000000005,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-97","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-97","entity":"crowded-97","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.860000001,"lo":4.8499999990000005},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-97","entity":"crowded-97","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.860000001,"lo":4.8499999990000005},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-97","entity":"crowded-97","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-97","entity":"crowded-97","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-97","entity":"crowded-97","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-97","entity":"crowded-97","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.8500000000000005,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.86,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.86,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.8500000000000005,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.8500000000000005,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.86,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.86,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.8500000000000005,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.9,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.9,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.9,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.91,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.91,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.91,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.91,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.91,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.91,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.9,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.9,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.9,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.9,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.91,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.91,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.9,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.9,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.91,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.91,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.9,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.9,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.91,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.91,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.9,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-98","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-98","entity":"crowded-98","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.910000001,"lo":4.899999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-98","entity":"crowded-98","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.910000001,"lo":4.899999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-98","entity":"crowded-98","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-98","entity":"crowded-98","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-98","entity":"crowded-98","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-98","entity":"crowded-98","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.9,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.91,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.91,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.9,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.9,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.91,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.91,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.9,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.95,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.95,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.95,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.96,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.96,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.96,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.96,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.96,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.96,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.95,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.95,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.95,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[4.95,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.96,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.96,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.95,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.95,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.96,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.96,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[4.95,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.95,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.96,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.96,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.95,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-99","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-99","entity":"crowded-99","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.960000001,"lo":4.949999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-99","entity":"crowded-99","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.960000001,"lo":4.949999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-99","entity":"crowded-99","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-99","entity":"crowded-99","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-99","entity":"crowded-99","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-99","entity":"crowded-99","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[4.95,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.96,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.96,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[4.95,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.95,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.96,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.96,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.95,0.3,2.1],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.0,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.01,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.01,0.0,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.01,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[5.01,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[5.01,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.01,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[5.0,0.3,2.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[5.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.01,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.01,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[5.01,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.01,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[5.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.009999999999999787,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.1]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.1],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[5.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[5.01,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[5.01,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[5.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"crowded-100","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.009999999999999787,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.1,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-100","entity":"crowded-100","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":5.010000001,"lo":4.999999999},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-100","entity":"crowded-100","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":5.010000001,"lo":4.999999999},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-100","entity":"crowded-100","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-100","entity":"crowded-100","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-100","entity":"crowded-100","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.010000000999999786,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"crowded-100","entity":"crowded-100","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":2.100000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[5.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[5.01,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[5.01,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[5.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[5.0,0.0,2.1],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[5.01,0.0,2.1],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[5.01,0.3,2.1],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[5.0,0.3,2.1],"tolerance":1e-9}],"wires":[]}}],"handlers":[],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[10.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[10.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[10.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[10.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[10.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[10.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[10.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":10.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":10.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[10.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[10.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[10.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[10.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"error":{"InvalidGeometry":"planar batch subtraction requires between one and 100 cutters"}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.two-cuboids.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.two-cuboids.json new file mode 100644 index 0000000..5087ed5 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/planar.two-cuboids.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0,2.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.0,2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[1.0,0.0,0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,2.0,0.0],"x":[0.0,0.0,1.0],"y":[1.0,0.0,0.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"left","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"left","entity":"left","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"left","entity":"left","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"left","entity":"left","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"left","entity":"left","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"left","entity":"left","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"left","entity":"left","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[2.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[1.0,2.0,0.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[2.0,2.0,0.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.0,0.0,2.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[2.0,0.0,2.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[1.0,2.0,2.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[2.0,2.0,2.0],"tolerance":1e-8}],"wires":[]}},{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.0,2.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.0,2.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[5.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.0,0.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.0,0.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[4.0,2.0,2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[4.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[1.0,0.0,0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[4.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[5.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.0,2.0,0.0],"x":[0.0,0.0,1.0],"y":[1.0,0.0,0.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"right","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"right","entity":"right","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"right","entity":"right","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"right","entity":"right","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"right","entity":"right","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"right","entity":"right","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"right","entity":"right","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[4.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[5.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[4.0,2.0,0.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[5.0,2.0,0.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[4.0,0.0,2.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[5.0,0.0,2.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[4.0,2.0,2.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[5.0,2.0,2.0],"tolerance":1e-8}],"wires":[]}}],"handlers":["subtract_planar_cutters","boolean_rectilinear"],"host":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,6.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,6.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[1.0,0.0,0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[0.0,0.0,1.0],"y":[1.0,0.0,0.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":6.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":6.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"host","entity":"host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":6.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[6.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[6.0,2.0,0.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,0.0,2.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[6.0,0.0,2.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,2.0,2.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[6.0,2.0,2.0],"tolerance":1e-8}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,2.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,2.0,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.0,2.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,2.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.0,0.0,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.0,2.0,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,2.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[5.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[5.0,2.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[5.0,0.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,4.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,4.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,5.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,6.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,5.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,5.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[5.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,5.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,5.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,6.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,5.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[0.0,0.0,1.0],"y":[1.0,0.0,0.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[1.0,0.0,0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[0.0,0.0,1.0],"y":[1.0,0.0,0.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[1.0,0.0,0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[0.0,0.0,1.0],"y":[1.0,0.0,0.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[5.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[1.0,0.0,0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"}]},"id":"batch-planar.two-cuboids","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0},{"cavity_shells":[],"outer_shell":1},{"cavity_shells":[],"outer_shell":2}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":7,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":11,"id":9,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":24,"id":12,"tolerance":1e-8,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":25,"id":13,"tolerance":1e-8,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":26,"id":14,"tolerance":1e-8,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":27,"id":15,"tolerance":1e-8,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":28,"id":16,"tolerance":1e-8,"twin_halfedge":41},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":29,"id":17,"tolerance":1e-8,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":30,"id":18,"tolerance":1e-8,"twin_halfedge":45},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":31,"id":19,"tolerance":1e-8,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":33,"id":20,"tolerance":1e-8,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":35,"id":21,"tolerance":1e-8,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":37,"id":22,"tolerance":1e-8,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":39,"id":23,"tolerance":1e-8,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":48,"id":24,"tolerance":1e-8,"twin_halfedge":71},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":49,"id":25,"tolerance":1e-8,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":50,"id":26,"tolerance":1e-8,"twin_halfedge":67},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":51,"id":27,"tolerance":1e-8,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":52,"id":28,"tolerance":1e-8,"twin_halfedge":65},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":53,"id":29,"tolerance":1e-8,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":54,"id":30,"tolerance":1e-8,"twin_halfedge":69},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":55,"id":31,"tolerance":1e-8,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":57,"id":32,"tolerance":1e-8,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":59,"id":33,"tolerance":1e-8,"twin_halfedge":66},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":61,"id":34,"tolerance":1e-8,"twin_halfedge":70},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":63,"id":35,"tolerance":1e-8,"twin_halfedge":64}],"faces":[{"id":0,"key":"front-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"back-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":2,"key":"left-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"left:left-3","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"left","entity":"left","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"top-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"bottom-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":6,"key":"front-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":1,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":4.00000001,"lo":1.99999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":7,"key":"back-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":1,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":4.00000001,"lo":1.99999999}]}},{"id":8,"key":"left:right-8","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"left","entity":"left","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":1,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":9,"key":"right:left-9","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"right","entity":"right","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":1,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":10,"key":"top-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":1,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":4.00000001,"lo":1.99999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":11,"key":"bottom-11","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":1,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":4.00000001,"lo":1.99999999}]}},{"id":12,"key":"front-12","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":2,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":6.00000001,"lo":4.99999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":13,"key":"back-13","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":2,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":6.00000001,"lo":4.99999999}]}},{"id":14,"key":"right:right-14","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"right","entity":"right","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":2,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":15,"key":"right-15","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":2,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":16,"key":"top-16","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":2,"surface":16,"trim":{"chart":0,"holes":[],"outer":16,"uv_bounds":[{"hi":6.00000001,"lo":4.99999999},{"hi":2.00000001,"lo":-1e-8}]}},{"id":17,"key":"bottom-17","provenance":{"reversed":false,"role":"Split","sources":[{"body":"host","entity":"host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":2,"surface":17,"trim":{"chart":0,"holes":[],"outer":17,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":6.00000001,"lo":4.99999999}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":23,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":12,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":19,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":10,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":17,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":6,"twin":14,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":7,"twin":21,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":8,"wire_ref":null},{"edge":7,"face":2,"from":4,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":7,"twin":7,"wire_ref":null},{"edge":8,"face":2,"from":7,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":0,"twin":20,"wire_ref":null},{"edge":3,"face":2,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":3,"wire_ref":null},{"edge":9,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":4,"twin":18,"wire_ref":null},{"edge":1,"face":3,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":1,"twin":1,"wire_ref":null},{"edge":10,"face":3,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":22,"wire_ref":null},{"edge":5,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":11,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":2,"twin":16,"wire_ref":null},{"edge":11,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":5,"twin":15,"wire_ref":null},{"edge":4,"face":4,"from":5,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":4,"twin":4,"wire_ref":null},{"edge":9,"face":4,"from":4,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":3,"twin":11,"wire_ref":null},{"edge":2,"face":4,"from":3,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":2,"wire_ref":null},{"edge":8,"face":5,"from":0,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":7,"twin":9,"wire_ref":null},{"edge":6,"face":5,"from":7,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":6,"twin":6,"wire_ref":null},{"edge":10,"face":5,"from":6,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":1,"twin":13,"wire_ref":null},{"edge":0,"face":5,"from":1,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":0,"twin":0,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":9,"twin":47,"wire_ref":null},{"edge":13,"face":6,"from":9,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":10,"twin":36,"wire_ref":null},{"edge":14,"face":6,"from":10,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":11,"twin":43,"wire_ref":null},{"edge":15,"face":6,"from":11,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":8,"twin":34,"wire_ref":null},{"edge":16,"face":7,"from":12,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":13,"twin":41,"wire_ref":null},{"edge":17,"face":7,"from":13,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":14,"twin":38,"wire_ref":null},{"edge":18,"face":7,"from":14,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":15,"twin":45,"wire_ref":null},{"edge":19,"face":7,"from":15,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":12,"twin":32,"wire_ref":null},{"edge":19,"face":8,"from":12,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":15,"twin":31,"wire_ref":null},{"edge":20,"face":8,"from":15,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":8,"twin":44,"wire_ref":null},{"edge":15,"face":8,"from":8,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":11,"twin":27,"wire_ref":null},{"edge":21,"face":8,"from":11,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Forward"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":12,"twin":42,"wire_ref":null},{"edge":13,"face":9,"from":10,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":9,"twin":25,"wire_ref":null},{"edge":22,"face":9,"from":9,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":14,"twin":46,"wire_ref":null},{"edge":17,"face":9,"from":14,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":13,"twin":29,"wire_ref":null},{"edge":23,"face":9,"from":13,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":10,"twin":40,"wire_ref":null},{"edge":23,"face":10,"from":10,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":13,"twin":39,"wire_ref":null},{"edge":16,"face":10,"from":13,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Reverse"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":12,"twin":28,"wire_ref":null},{"edge":21,"face":10,"from":12,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":11,"twin":35,"wire_ref":null},{"edge":14,"face":10,"from":11,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Forward"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":10,"twin":26,"wire_ref":null},{"edge":20,"face":11,"from":8,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":15,"twin":33,"wire_ref":null},{"edge":18,"face":11,"from":15,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":14,"twin":30,"wire_ref":null},{"edge":22,"face":11,"from":14,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":9,"twin":37,"wire_ref":null},{"edge":12,"face":11,"from":9,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":8,"twin":24,"wire_ref":null},{"edge":24,"face":12,"from":16,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":12,"next":49,"prev":51,"to":17,"twin":71,"wire_ref":null},{"edge":25,"face":12,"from":17,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Reverse"},"id":49,"loop_ref":12,"next":50,"prev":48,"to":18,"twin":60,"wire_ref":null},{"edge":26,"face":12,"from":18,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":12,"next":51,"prev":49,"to":19,"twin":67,"wire_ref":null},{"edge":27,"face":12,"from":19,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Forward"},"id":51,"loop_ref":12,"next":48,"prev":50,"to":16,"twin":58,"wire_ref":null},{"edge":28,"face":13,"from":20,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":13,"next":53,"prev":55,"to":21,"twin":65,"wire_ref":null},{"edge":29,"face":13,"from":21,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":13,"next":54,"prev":52,"to":22,"twin":62,"wire_ref":null},{"edge":30,"face":13,"from":22,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":13,"next":55,"prev":53,"to":23,"twin":69,"wire_ref":null},{"edge":31,"face":13,"from":23,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Reverse"},"id":55,"loop_ref":13,"next":52,"prev":54,"to":20,"twin":56,"wire_ref":null},{"edge":31,"face":14,"from":20,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":14,"next":57,"prev":59,"to":23,"twin":55,"wire_ref":null},{"edge":32,"face":14,"from":23,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Reverse"},"id":57,"loop_ref":14,"next":58,"prev":56,"to":16,"twin":68,"wire_ref":null},{"edge":27,"face":14,"from":16,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":14,"next":59,"prev":57,"to":19,"twin":51,"wire_ref":null},{"edge":33,"face":14,"from":19,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Forward"},"id":59,"loop_ref":14,"next":56,"prev":58,"to":20,"twin":66,"wire_ref":null},{"edge":25,"face":15,"from":18,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Forward"},"id":60,"loop_ref":15,"next":61,"prev":63,"to":17,"twin":49,"wire_ref":null},{"edge":34,"face":15,"from":17,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Forward"},"id":61,"loop_ref":15,"next":62,"prev":60,"to":22,"twin":70,"wire_ref":null},{"edge":29,"face":15,"from":22,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":15,"next":63,"prev":61,"to":21,"twin":53,"wire_ref":null},{"edge":35,"face":15,"from":21,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Reverse"},"id":63,"loop_ref":15,"next":60,"prev":62,"to":18,"twin":64,"wire_ref":null},{"edge":35,"face":16,"from":18,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Forward"},"id":64,"loop_ref":16,"next":65,"prev":67,"to":21,"twin":63,"wire_ref":null},{"edge":28,"face":16,"from":21,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Reverse"},"id":65,"loop_ref":16,"next":66,"prev":64,"to":20,"twin":52,"wire_ref":null},{"edge":33,"face":16,"from":20,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Reverse"},"id":66,"loop_ref":16,"next":67,"prev":65,"to":19,"twin":59,"wire_ref":null},{"edge":26,"face":16,"from":19,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Forward"},"id":67,"loop_ref":16,"next":64,"prev":66,"to":18,"twin":50,"wire_ref":null},{"edge":32,"face":17,"from":16,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":17,"next":69,"prev":71,"to":23,"twin":57,"wire_ref":null},{"edge":30,"face":17,"from":23,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Forward"},"id":69,"loop_ref":17,"next":70,"prev":68,"to":22,"twin":54,"wire_ref":null},{"edge":34,"face":17,"from":22,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":17,"next":71,"prev":69,"to":17,"twin":61,"wire_ref":null},{"edge":24,"face":17,"from":17,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Reverse"},"id":71,"loop_ref":17,"next":68,"prev":70,"to":16,"twin":48,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":48},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":52},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":56},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":60},{"face_ref":16,"id":16,"is_hole":false,"start_halfedge":64},{"face_ref":17,"id":17,"is_hole":false,"start_halfedge":68}],"shells":[{"faces":[0,5,3,4,2,1],"id":0,"is_closed":true},{"faces":[6,11,9,10,8,7],"id":1,"is_closed":true},{"faces":[12,17,15,16,14,13],"id":2,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":1,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":2,"position":[1.0,0.0,2.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":3,"position":[0.0,0.0,2.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,2.0,2.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[1.0,2.0,2.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":6,"position":[1.0,2.0,0.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":7,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":8,"outgoing_halfedge":24,"position":[2.0,0.0,0.0],"tolerance":1e-8},{"id":9,"outgoing_halfedge":25,"position":[4.0,0.0,0.0],"tolerance":1e-8},{"id":10,"outgoing_halfedge":26,"position":[4.0,0.0,2.0],"tolerance":1e-8},{"id":11,"outgoing_halfedge":27,"position":[2.0,0.0,2.0],"tolerance":1e-8},{"id":12,"outgoing_halfedge":28,"position":[2.0,2.0,2.0],"tolerance":1e-8},{"id":13,"outgoing_halfedge":29,"position":[4.0,2.0,2.0],"tolerance":1e-8},{"id":14,"outgoing_halfedge":30,"position":[4.0,2.0,0.0],"tolerance":1e-8},{"id":15,"outgoing_halfedge":31,"position":[2.0,2.0,0.0],"tolerance":1e-8},{"id":16,"outgoing_halfedge":48,"position":[5.0,0.0,0.0],"tolerance":1e-8},{"id":17,"outgoing_halfedge":49,"position":[6.0,0.0,0.0],"tolerance":1e-8},{"id":18,"outgoing_halfedge":50,"position":[6.0,0.0,2.0],"tolerance":1e-8},{"id":19,"outgoing_halfedge":51,"position":[5.0,0.0,2.0],"tolerance":1e-8},{"id":20,"outgoing_halfedge":52,"position":[5.0,2.0,2.0],"tolerance":1e-8},{"id":21,"outgoing_halfedge":53,"position":[6.0,2.0,2.0],"tolerance":1e-8},{"id":22,"outgoing_halfedge":54,"position":[6.0,2.0,0.0],"tolerance":1e-8},{"id":23,"outgoing_halfedge":55,"position":[5.0,2.0,0.0],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.arched.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.arched.json new file mode 100644 index 0000000..c12fe23 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.arched.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[2.0,-0.5,2.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.5},{"frame":{"origin":[2.0,0.8,2.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.5},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.5,-0.5,2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.5,0.0],"axis_b":[0.0,-0.5],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.5,0.0],"axis_b":[0.0,0.5],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[2.0,-0.5,2.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.5},{"frame":{"origin":[2.0,-0.5,2.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.8,2.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"arched-cap","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-cap","entity":"arched-cap","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-cap","entity":"arched-cap","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.5,"lo":-0.5},{"hi":0.5,"lo":-0.5}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-cap","entity":"arched-cap","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.5,"lo":-0.5},{"hi":0.5,"lo":-0.5}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[2.5,-0.5,2.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[2.5,0.8,2.0],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.8,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,-0.5,0.5]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.5,0.8,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.8,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,-0.5,0.5]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.8,0.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.8,2.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.5,-0.5,2.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.8,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,0.8,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,-0.5,2.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.5,0.8,2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.5,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,-2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,-2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5,-0.5]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.5,-0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,-0.5,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.8,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.8,0.5],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.8,0.5],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.8,2.0],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.8,2.0],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"}]},"id":"arched-lower","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-lower","entity":"arched-lower","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.500000001,"lo":1.499999999},{"hi":2.000000001,"lo":0.499999999}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-lower","entity":"arched-lower","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.500000001,"lo":1.499999999},{"hi":-0.499999999,"lo":-2.000000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-lower","entity":"arched-lower","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-lower","entity":"arched-lower","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.500000001,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-lower","entity":"arched-lower","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-lower","entity":"arched-lower","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.500000001,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.5,0.8,0.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[2.5,0.8,0.5],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[2.5,0.8,2.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.5,0.8,2.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.5,-0.5,0.5],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.5,-0.5,0.5],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.5,-0.5,2.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.5,-0.5,2.0],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_prismatic_profile_batch"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"arched-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-host","entity":"arched-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-host","entity":"arched-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-host","entity":"arched-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":4.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-host","entity":"arched-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-host","entity":"arched-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":4.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arched-host","entity":"arched-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.0,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.3,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.5,0.0,0.5]},{"frame":{"origin":[2.0,0.0,2.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.5},{"frame":{"origin":[2.0,0.3,2.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.5},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.5,0.0,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,0.0,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,0.3,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.3,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.0,0.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.5]},{"axis_a":[0.5,0.0],"axis_b":[0.0,0.5],"kind":"Conic2","origin":[2.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-3.0]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[4.0,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[2.5,-0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,-2.0]},{"axis_a":[0.5,0.0],"axis_b":[0.0,-0.5],"kind":"Conic2","origin":[2.0,-2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.5,-0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[4.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[4.0,0.0,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.0,0.5],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,2.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.5},{"frame":{"origin":[1.5,0.0,2.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.0,0.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"}]},"id":"batch-prismatic.arched","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":11,"id":0,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":19,"id":2,"tolerance":1e-9,"twin_halfedge":29},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":3,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":17,"id":5,"tolerance":1e-9,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":9,"id":6,"tolerance":1e-9,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":21,"id":8,"tolerance":1e-9,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":9,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":25,"id":11,"tolerance":1e-9,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":15,"id":12,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":4,"id":13,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":35,"id":14,"tolerance":1e-9,"twin_halfedge":45},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":14,"id":15,"tolerance":1e-9,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":5,"id":16,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":33,"id":17,"tolerance":1e-9,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":13,"id":18,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":6,"id":19,"tolerance":1e-9,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":37,"id":20,"tolerance":1e-9,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":12,"id":21,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":22,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":41,"id":23,"tolerance":1e-9,"twin_halfedge":47}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arched-host","entity":"arched-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1],"outer":0,"uv_bounds":[{"hi":4.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arched-host","entity":"arched-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[3],"outer":2,"uv_bounds":[{"hi":4.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-3.000000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arched-host","entity":"arched-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":4.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arched-host","entity":"arched-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arched-host","entity":"arched-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":4.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arched-host","entity":"arched-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":6,"key":"side-1-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"arched-lower","entity":"arched-lower","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":1.500000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":7,"key":"side-1-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"arched-cap","entity":"arched-cap","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":-3.141592651589793,"lo":-6.283185309179586},{"hi":0.300000001,"lo":-1e-9}]}},{"id":8,"key":"side-1-2","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"arched-lower","entity":"arched-lower","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":1.500000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":9,"key":"side-1-3","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"arched-lower","entity":"arched-lower","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":18,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":22,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":26,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":30,"wire_ref":null},{"edge":13,"face":0,"from":12,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":13,"twin":34,"wire_ref":null},{"edge":16,"face":0,"from":13,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":14,"twin":38,"wire_ref":null},{"edge":19,"face":0,"from":14,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":15,"twin":42,"wire_ref":null},{"edge":22,"face":0,"from":15,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":12,"twin":46,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":3,"twin":28,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":2,"twin":24,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":1,"twin":20,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":16,"wire_ref":null},{"edge":21,"face":1,"from":8,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":11,"twin":44,"wire_ref":null},{"edge":18,"face":1,"from":11,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":10,"twin":40,"wire_ref":null},{"edge":15,"face":1,"from":10,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":9,"twin":36,"wire_ref":null},{"edge":12,"face":1,"from":9,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":8,"twin":32,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":11,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":23,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":29,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":2,"twin":10,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":6,"twin":27,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":1,"twin":17,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":3,"twin":9,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":7,"twin":31,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":2,"twin":21,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":0,"twin":8,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":4,"twin":19,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":3,"twin":25,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":9,"twin":15,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":13,"twin":39,"wire_ref":null},{"edge":13,"face":6,"from":13,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":12,"twin":4,"wire_ref":null},{"edge":14,"face":6,"from":12,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":8,"twin":45,"wire_ref":null},{"edge":15,"face":7,"from":9,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Reverse"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":10,"twin":14,"wire_ref":null},{"edge":20,"face":7,"from":10,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":14,"twin":43,"wire_ref":null},{"edge":16,"face":7,"from":14,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Forward"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":13,"twin":5,"wire_ref":null},{"edge":17,"face":7,"from":13,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":9,"twin":33,"wire_ref":null},{"edge":18,"face":8,"from":10,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":11,"twin":13,"wire_ref":null},{"edge":23,"face":8,"from":11,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":15,"twin":47,"wire_ref":null},{"edge":19,"face":8,"from":15,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":14,"twin":6,"wire_ref":null},{"edge":20,"face":8,"from":14,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":10,"twin":37,"wire_ref":null},{"edge":21,"face":9,"from":11,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":8,"twin":12,"wire_ref":null},{"edge":14,"face":9,"from":8,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":12,"twin":35,"wire_ref":null},{"edge":22,"face":9,"from":12,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":15,"twin":7,"wire_ref":null},{"edge":23,"face":9,"from":15,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":11,"twin":41,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":4},{"face_ref":1,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":1,"id":3,"is_hole":true,"start_halfedge":12},{"face_ref":2,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":3,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":4,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":5,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":6,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":7,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":8,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":9,"id":11,"is_hole":false,"start_halfedge":44}],"shells":[{"faces":[0,1,2,3,4,5,6,7,8,9],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":8,"position":[4.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":11,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":10,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":9,"position":[4.0,0.0,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[4.0,0.3,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[4.0,0.3,3.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":12,"position":[2.5,0.0,0.5],"tolerance":1e-9},{"id":9,"outgoing_halfedge":15,"position":[2.5,0.0,2.0],"tolerance":1e-9},{"id":10,"outgoing_halfedge":14,"position":[1.5,0.0,2.0],"tolerance":1e-9},{"id":11,"outgoing_halfedge":13,"position":[1.5,0.0,0.5],"tolerance":1e-9},{"id":12,"outgoing_halfedge":4,"position":[2.5,0.3,0.5],"tolerance":1e-9},{"id":13,"outgoing_halfedge":5,"position":[2.5,0.3,2.0],"tolerance":1e-9},{"id":14,"outgoing_halfedge":6,"position":[1.5,0.3,2.0],"tolerance":1e-9},{"id":15,"outgoing_halfedge":7,"position":[1.5,0.3,0.5],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.disjoint-mixed.forward.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.disjoint-mixed.forward.json new file mode 100644 index 0000000..b11c0e6 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.disjoint-mixed.forward.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.8,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,-0.5,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.8,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"round","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round","entity":"round","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round","entity":"round","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round","entity":"round","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.9,-0.5,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[1.9,0.8,1.5],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.8,0.7]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,-0.5,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.8,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.8,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,-0.5,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.8,0.7]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.8,2.2]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,-0.5,2.2]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.8,2.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.8,2.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,-0.5,2.2]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.8,2.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,0.7]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.5,0.7]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,2.2]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,2.2]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,-2.2]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,-2.2]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.5,-0.7]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.5,-0.7]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5000000000000002,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5000000000000002,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,-0.5,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.8,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.8,0.7],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.8,0.7],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.8,2.2],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.8,2.2],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"}]},"id":"rectangle","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.500000001,"lo":3.499999999},{"hi":2.2000000010000003,"lo":0.699999999}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.500000001,"lo":3.499999999},{"hi":-0.699999999,"lo":-2.2000000010000003}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.5000000010000003,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.5000000010000003,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.5,0.8,0.7],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.5,0.8,0.7],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.5,0.8,2.2],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.5,0.8,2.2],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.5,-0.5,0.7],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.5,-0.5,0.7],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.5,-0.5,2.2],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.5,-0.5,2.2],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_prismatic_profile_batch"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"mixed-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.0,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.3,0.7]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.5,0.0,0.7]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.0,2.2]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.3,2.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.5,0.0,2.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.0,2.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.3,2.2]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,2.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,0.7]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,0.7]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,0.7]},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9000000000000004,0.0,1.5]},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0999999999999996,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.7]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,2.2]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5,2.2]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,0.7]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[4.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[4.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-3.0]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[6.0,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[2.5,-0.7]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,-2.2]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,-2.2]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.5,-0.7]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[4.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[4.5,-1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5000000000000002,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5000000000000002,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.0,0.7],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.0,2.2],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.0,2.2],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.0,0.7],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4}]},"id":"batch-prismatic.disjoint-mixed.forward","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":13,"id":0,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":23,"id":2,"tolerance":1e-9,"twin_halfedge":33},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":12,"id":3,"tolerance":1e-9,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":21,"id":5,"tolerance":1e-9,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":11,"id":6,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":25,"id":8,"tolerance":1e-9,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":9,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":29,"id":11,"tolerance":1e-9,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":17,"id":12,"tolerance":1e-9,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":4,"id":13,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":39,"id":14,"tolerance":1e-9,"twin_halfedge":49},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":16,"id":15,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":16,"tolerance":1e-9,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":37,"id":17,"tolerance":1e-9,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":15,"id":18,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":6,"id":19,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":41,"id":20,"tolerance":1e-9,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":14,"id":21,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":22,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":45,"id":23,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":19,"id":24,"tolerance":1e-9,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":8,"id":25,"tolerance":1e-9,"twin_halfedge":54},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":55,"id":26,"tolerance":1e-9,"twin_halfedge":57},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":18,"id":27,"tolerance":1e-9,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":9,"id":28,"tolerance":1e-9,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":53,"id":29,"tolerance":1e-9,"twin_halfedge":59}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1,2],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[4,5],"outer":3,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-3.000000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":6,"key":"side-1-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"rectangle","entity":"rectangle","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":1.5000000010000003,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":7,"key":"side-1-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"rectangle","entity":"rectangle","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":8,"key":"side-1-2","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"rectangle","entity":"rectangle","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":1.5000000010000003,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":9,"key":"side-1-3","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"rectangle","entity":"rectangle","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":10,"key":"side-2-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round","entity":"round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":-3.141592651089793,"lo":-6.283185309679586},{"hi":0.300000001,"lo":-1e-9}]}},{"id":11,"key":"side-2-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round","entity":"round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":2.5e-9,"lo":-3.1415926560897933},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":22,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":26,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":30,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":34,"wire_ref":null},{"edge":13,"face":0,"from":12,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":13,"twin":38,"wire_ref":null},{"edge":16,"face":0,"from":13,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":14,"twin":42,"wire_ref":null},{"edge":19,"face":0,"from":14,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":15,"twin":46,"wire_ref":null},{"edge":22,"face":0,"from":15,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":12,"twin":50,"wire_ref":null},{"edge":25,"face":0,"from":18,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":9,"prev":9,"to":19,"twin":54,"wire_ref":null},{"edge":28,"face":0,"from":19,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":8,"prev":8,"to":18,"twin":58,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":3,"next":11,"prev":13,"to":3,"twin":32,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":3,"next":12,"prev":10,"to":2,"twin":28,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":11,"to":1,"twin":24,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":10,"prev":12,"to":0,"twin":20,"wire_ref":null},{"edge":21,"face":1,"from":8,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":4,"next":15,"prev":17,"to":11,"twin":48,"wire_ref":null},{"edge":18,"face":1,"from":11,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":4,"next":16,"prev":14,"to":10,"twin":44,"wire_ref":null},{"edge":15,"face":1,"from":10,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":4,"next":17,"prev":15,"to":9,"twin":40,"wire_ref":null},{"edge":12,"face":1,"from":9,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":14,"prev":16,"to":8,"twin":36,"wire_ref":null},{"edge":27,"face":1,"from":16,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":5,"next":19,"prev":19,"to":17,"twin":56,"wire_ref":null},{"edge":24,"face":1,"from":17,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":5,"next":18,"prev":18,"to":16,"twin":52,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":6,"next":21,"prev":23,"to":1,"twin":13,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":6,"next":22,"prev":20,"to":5,"twin":27,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":6,"next":23,"prev":21,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":6,"next":20,"prev":22,"to":0,"twin":33,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":7,"next":25,"prev":27,"to":2,"twin":12,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":7,"next":26,"prev":24,"to":6,"twin":31,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":7,"next":27,"prev":25,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":7,"next":24,"prev":26,"to":1,"twin":21,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":8,"next":29,"prev":31,"to":3,"twin":11,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":8,"next":30,"prev":28,"to":7,"twin":35,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":8,"next":31,"prev":29,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":8,"next":28,"prev":30,"to":2,"twin":25,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":9,"next":33,"prev":35,"to":0,"twin":10,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":9,"next":34,"prev":32,"to":4,"twin":23,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":9,"next":35,"prev":33,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":9,"next":32,"prev":34,"to":3,"twin":29,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":10,"next":37,"prev":39,"to":9,"twin":17,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":10,"next":38,"prev":36,"to":13,"twin":43,"wire_ref":null},{"edge":13,"face":6,"from":13,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":10,"next":39,"prev":37,"to":12,"twin":4,"wire_ref":null},{"edge":14,"face":6,"from":12,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":10,"next":36,"prev":38,"to":8,"twin":49,"wire_ref":null},{"edge":15,"face":7,"from":9,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":11,"next":41,"prev":43,"to":10,"twin":16,"wire_ref":null},{"edge":20,"face":7,"from":10,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":11,"next":42,"prev":40,"to":14,"twin":47,"wire_ref":null},{"edge":16,"face":7,"from":14,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":11,"next":43,"prev":41,"to":13,"twin":5,"wire_ref":null},{"edge":17,"face":7,"from":13,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":11,"next":40,"prev":42,"to":9,"twin":37,"wire_ref":null},{"edge":18,"face":8,"from":10,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":12,"next":45,"prev":47,"to":11,"twin":15,"wire_ref":null},{"edge":23,"face":8,"from":11,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":12,"next":46,"prev":44,"to":15,"twin":51,"wire_ref":null},{"edge":19,"face":8,"from":15,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":12,"next":47,"prev":45,"to":14,"twin":6,"wire_ref":null},{"edge":20,"face":8,"from":14,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":12,"next":44,"prev":46,"to":10,"twin":41,"wire_ref":null},{"edge":21,"face":9,"from":11,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":13,"next":49,"prev":51,"to":8,"twin":14,"wire_ref":null},{"edge":14,"face":9,"from":8,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":13,"next":50,"prev":48,"to":12,"twin":39,"wire_ref":null},{"edge":22,"face":9,"from":12,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":13,"next":51,"prev":49,"to":15,"twin":7,"wire_ref":null},{"edge":23,"face":9,"from":15,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":13,"next":48,"prev":50,"to":11,"twin":45,"wire_ref":null},{"edge":24,"face":10,"from":16,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Reverse"},"id":52,"loop_ref":14,"next":53,"prev":55,"to":17,"twin":19,"wire_ref":null},{"edge":29,"face":10,"from":17,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":14,"next":54,"prev":52,"to":19,"twin":59,"wire_ref":null},{"edge":25,"face":10,"from":19,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Forward"},"id":54,"loop_ref":14,"next":55,"prev":53,"to":18,"twin":8,"wire_ref":null},{"edge":26,"face":10,"from":18,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Reverse"},"id":55,"loop_ref":14,"next":52,"prev":54,"to":16,"twin":57,"wire_ref":null},{"edge":27,"face":11,"from":17,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Reverse"},"id":56,"loop_ref":15,"next":57,"prev":59,"to":16,"twin":18,"wire_ref":null},{"edge":26,"face":11,"from":16,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":15,"next":58,"prev":56,"to":18,"twin":55,"wire_ref":null},{"edge":28,"face":11,"from":18,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Forward"},"id":58,"loop_ref":15,"next":59,"prev":57,"to":19,"twin":9,"wire_ref":null},{"edge":29,"face":11,"from":19,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":15,"next":56,"prev":58,"to":17,"twin":53,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":4},{"face_ref":0,"id":2,"is_hole":true,"start_halfedge":8},{"face_ref":1,"id":3,"is_hole":false,"start_halfedge":10},{"face_ref":1,"id":4,"is_hole":true,"start_halfedge":14},{"face_ref":1,"id":5,"is_hole":true,"start_halfedge":18},{"face_ref":2,"id":6,"is_hole":false,"start_halfedge":20},{"face_ref":3,"id":7,"is_hole":false,"start_halfedge":24},{"face_ref":4,"id":8,"is_hole":false,"start_halfedge":28},{"face_ref":5,"id":9,"is_hole":false,"start_halfedge":32},{"face_ref":6,"id":10,"is_hole":false,"start_halfedge":36},{"face_ref":7,"id":11,"is_hole":false,"start_halfedge":40},{"face_ref":8,"id":12,"is_hole":false,"start_halfedge":44},{"face_ref":9,"id":13,"is_hole":false,"start_halfedge":48},{"face_ref":10,"id":14,"is_hole":false,"start_halfedge":52},{"face_ref":11,"id":15,"is_hole":false,"start_halfedge":56}],"shells":[{"faces":[0,1,2,3,4,5,6,7,8,9,10,11],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":10,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":13,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":12,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":11,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":14,"position":[4.5,0.0,0.7],"tolerance":1e-9},{"id":9,"outgoing_halfedge":17,"position":[4.5,0.0,2.2],"tolerance":1e-9},{"id":10,"outgoing_halfedge":16,"position":[3.5,0.0,2.2],"tolerance":1e-9},{"id":11,"outgoing_halfedge":15,"position":[3.5,0.0,0.7],"tolerance":1e-9},{"id":12,"outgoing_halfedge":4,"position":[4.5,0.3,0.7],"tolerance":1e-9},{"id":13,"outgoing_halfedge":5,"position":[4.5,0.3,2.2],"tolerance":1e-9},{"id":14,"outgoing_halfedge":6,"position":[3.5,0.3,2.2],"tolerance":1e-9},{"id":15,"outgoing_halfedge":7,"position":[3.5,0.3,0.7],"tolerance":1e-9},{"id":16,"outgoing_halfedge":18,"position":[1.9000000000000004,0.0,1.5],"tolerance":1e-9},{"id":17,"outgoing_halfedge":19,"position":[1.0999999999999996,0.0,1.5],"tolerance":1e-9},{"id":18,"outgoing_halfedge":8,"position":[1.9000000000000004,0.3,1.5],"tolerance":1e-9},{"id":19,"outgoing_halfedge":9,"position":[1.0999999999999996,0.3,1.5],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.disjoint-mixed.reverse.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.disjoint-mixed.reverse.json new file mode 100644 index 0000000..a837e10 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.disjoint-mixed.reverse.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.8,0.7]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,-0.5,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.8,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.8,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,-0.5,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.8,0.7]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.8,2.2]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,-0.5,2.2]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.8,2.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.8,2.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,-0.5,2.2]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.8,2.2]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,0.7]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.5,0.7]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,2.2]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,2.2]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,-2.2]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,-2.2]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.5,-0.7]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.5,-0.7]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5000000000000002,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5000000000000002,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,-0.5,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.8,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.8,0.7],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.8,0.7],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.8,2.2],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.8,2.2],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"}]},"id":"rectangle","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.500000001,"lo":3.499999999},{"hi":2.2000000010000003,"lo":0.699999999}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.500000001,"lo":3.499999999},{"hi":-0.699999999,"lo":-2.2000000010000003}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.5000000010000003,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangle","entity":"rectangle","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.5000000010000003,"lo":-1e-9},{"hi":1.3000000010000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.5,0.8,0.7],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.5,0.8,0.7],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.5,0.8,2.2],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.5,0.8,2.2],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.5,-0.5,0.7],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.5,-0.5,0.7],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.5,-0.5,2.2],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.5,-0.5,2.2],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.8,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,-0.5,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.8,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"round","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round","entity":"round","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round","entity":"round","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round","entity":"round","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.9,-0.5,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[1.9,0.8,1.5],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_prismatic_profile_batch"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"mixed-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-host","entity":"mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.1,0.3,1.5]},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.9,0.3,1.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.3,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.0,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,0.7]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,2.2]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,2.2]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,2.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.5,0.3,2.2]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.5,0.0,2.2]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.3,2.2]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.3,0.7]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.0,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.3,0.7]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[1.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[1.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,0.7]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,2.2]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.5,2.2]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,0.7]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[1.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[1.5,-1.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,-0.7]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.5,-2.2]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.5,-2.2]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,-0.7]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5000000000000002,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5000000000000002,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[1.0,0.0,-0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,3.0],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[3.5,0.3,0.7],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,2.2],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.3,2.2],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.3,0.7],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"}]},"id":"batch-prismatic.disjoint-mixed.reverse","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":13,"id":0,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":23,"id":2,"tolerance":1e-9,"twin_halfedge":33},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":12,"id":3,"tolerance":1e-9,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":21,"id":5,"tolerance":1e-9,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":11,"id":6,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":25,"id":8,"tolerance":1e-9,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":9,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":29,"id":11,"tolerance":1e-9,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":15,"id":12,"tolerance":1e-9,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":4,"id":13,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":39,"id":14,"tolerance":1e-9,"twin_halfedge":41},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":14,"id":15,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":5,"id":16,"tolerance":1e-9,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":37,"id":17,"tolerance":1e-9,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":19,"id":18,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":6,"id":19,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":47,"id":20,"tolerance":1e-9,"twin_halfedge":57},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":18,"id":21,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":22,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":45,"id":23,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":17,"id":24,"tolerance":1e-9,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":1.5000000000000002,"lo":0.0}},"halfedge":8,"id":25,"tolerance":1e-9,"twin_halfedge":54},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":49,"id":26,"tolerance":1e-9,"twin_halfedge":55},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":16,"id":27,"tolerance":1e-9,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":9,"id":28,"tolerance":1e-9,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":53,"id":29,"tolerance":1e-9,"twin_halfedge":59}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1,2],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[4,5],"outer":3,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-3.000000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-host","entity":"mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":6,"key":"side-1-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round","entity":"round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":-3.141592651089793,"lo":-6.283185309679586},{"hi":0.300000001,"lo":-1e-9}]}},{"id":7,"key":"side-1-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round","entity":"round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":2.5e-9,"lo":-3.1415926560897933},{"hi":0.300000001,"lo":-1e-9}]}},{"id":8,"key":"side-2-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"rectangle","entity":"rectangle","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":1.5000000010000003,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":9,"key":"side-2-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"rectangle","entity":"rectangle","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":10,"key":"side-2-2","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"rectangle","entity":"rectangle","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":1.5000000010000003,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":11,"key":"side-2-3","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"rectangle","entity":"rectangle","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":22,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":26,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":30,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":34,"wire_ref":null},{"edge":13,"face":0,"from":10,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":5,"to":11,"twin":38,"wire_ref":null},{"edge":16,"face":0,"from":11,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":4,"prev":4,"to":10,"twin":42,"wire_ref":null},{"edge":19,"face":0,"from":16,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":2,"next":7,"prev":9,"to":17,"twin":46,"wire_ref":null},{"edge":22,"face":0,"from":17,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":2,"next":8,"prev":6,"to":18,"twin":50,"wire_ref":null},{"edge":25,"face":0,"from":18,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":7,"to":19,"twin":54,"wire_ref":null},{"edge":28,"face":0,"from":19,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":6,"prev":8,"to":16,"twin":58,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":3,"next":11,"prev":13,"to":3,"twin":32,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":3,"next":12,"prev":10,"to":2,"twin":28,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":11,"to":1,"twin":24,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":10,"prev":12,"to":0,"twin":20,"wire_ref":null},{"edge":15,"face":1,"from":8,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":4,"next":15,"prev":15,"to":9,"twin":40,"wire_ref":null},{"edge":12,"face":1,"from":9,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":4,"next":14,"prev":14,"to":8,"twin":36,"wire_ref":null},{"edge":27,"face":1,"from":12,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":5,"next":17,"prev":19,"to":15,"twin":56,"wire_ref":null},{"edge":24,"face":1,"from":15,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":5,"next":18,"prev":16,"to":14,"twin":52,"wire_ref":null},{"edge":21,"face":1,"from":14,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":5,"next":19,"prev":17,"to":13,"twin":48,"wire_ref":null},{"edge":18,"face":1,"from":13,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":5,"next":16,"prev":18,"to":12,"twin":44,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":6,"next":21,"prev":23,"to":1,"twin":13,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":6,"next":22,"prev":20,"to":5,"twin":27,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":6,"next":23,"prev":21,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":6,"next":20,"prev":22,"to":0,"twin":33,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":7,"next":25,"prev":27,"to":2,"twin":12,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":7,"next":26,"prev":24,"to":6,"twin":31,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":7,"next":27,"prev":25,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":7,"next":24,"prev":26,"to":1,"twin":21,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":8,"next":29,"prev":31,"to":3,"twin":11,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":8,"next":30,"prev":28,"to":7,"twin":35,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":8,"next":31,"prev":29,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":8,"next":28,"prev":30,"to":2,"twin":25,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":9,"next":33,"prev":35,"to":0,"twin":10,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":9,"next":34,"prev":32,"to":4,"twin":23,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":9,"next":35,"prev":33,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":9,"next":32,"prev":34,"to":3,"twin":29,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Reverse"},"id":36,"loop_ref":10,"next":37,"prev":39,"to":9,"twin":15,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":10,"next":38,"prev":36,"to":11,"twin":43,"wire_ref":null},{"edge":13,"face":6,"from":11,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Forward"},"id":38,"loop_ref":10,"next":39,"prev":37,"to":10,"twin":4,"wire_ref":null},{"edge":14,"face":6,"from":10,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":10,"next":36,"prev":38,"to":8,"twin":41,"wire_ref":null},{"edge":15,"face":7,"from":9,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Reverse"},"id":40,"loop_ref":11,"next":41,"prev":43,"to":8,"twin":14,"wire_ref":null},{"edge":14,"face":7,"from":8,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":11,"next":42,"prev":40,"to":10,"twin":39,"wire_ref":null},{"edge":16,"face":7,"from":10,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Forward"},"id":42,"loop_ref":11,"next":43,"prev":41,"to":11,"twin":5,"wire_ref":null},{"edge":17,"face":7,"from":11,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":11,"next":40,"prev":42,"to":9,"twin":37,"wire_ref":null},{"edge":18,"face":8,"from":12,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":12,"next":45,"prev":47,"to":13,"twin":19,"wire_ref":null},{"edge":23,"face":8,"from":13,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":12,"next":46,"prev":44,"to":17,"twin":51,"wire_ref":null},{"edge":19,"face":8,"from":17,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":12,"next":47,"prev":45,"to":16,"twin":6,"wire_ref":null},{"edge":20,"face":8,"from":16,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":12,"next":44,"prev":46,"to":12,"twin":57,"wire_ref":null},{"edge":21,"face":9,"from":13,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":13,"next":49,"prev":51,"to":14,"twin":18,"wire_ref":null},{"edge":26,"face":9,"from":14,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":13,"next":50,"prev":48,"to":18,"twin":55,"wire_ref":null},{"edge":22,"face":9,"from":18,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":13,"next":51,"prev":49,"to":17,"twin":7,"wire_ref":null},{"edge":23,"face":9,"from":17,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":13,"next":48,"prev":50,"to":13,"twin":45,"wire_ref":null},{"edge":24,"face":10,"from":14,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":14,"next":53,"prev":55,"to":15,"twin":17,"wire_ref":null},{"edge":29,"face":10,"from":15,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":14,"next":54,"prev":52,"to":19,"twin":59,"wire_ref":null},{"edge":25,"face":10,"from":19,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":14,"next":55,"prev":53,"to":18,"twin":8,"wire_ref":null},{"edge":26,"face":10,"from":18,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Reverse"},"id":55,"loop_ref":14,"next":52,"prev":54,"to":14,"twin":49,"wire_ref":null},{"edge":27,"face":11,"from":15,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":15,"next":57,"prev":59,"to":12,"twin":16,"wire_ref":null},{"edge":20,"face":11,"from":12,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":15,"next":58,"prev":56,"to":16,"twin":47,"wire_ref":null},{"edge":28,"face":11,"from":16,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":15,"next":59,"prev":57,"to":19,"twin":9,"wire_ref":null},{"edge":29,"face":11,"from":19,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":15,"next":56,"prev":58,"to":15,"twin":53,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":4},{"face_ref":0,"id":2,"is_hole":true,"start_halfedge":6},{"face_ref":1,"id":3,"is_hole":false,"start_halfedge":10},{"face_ref":1,"id":4,"is_hole":true,"start_halfedge":14},{"face_ref":1,"id":5,"is_hole":true,"start_halfedge":16},{"face_ref":2,"id":6,"is_hole":false,"start_halfedge":20},{"face_ref":3,"id":7,"is_hole":false,"start_halfedge":24},{"face_ref":4,"id":8,"is_hole":false,"start_halfedge":28},{"face_ref":5,"id":9,"is_hole":false,"start_halfedge":32},{"face_ref":6,"id":10,"is_hole":false,"start_halfedge":36},{"face_ref":7,"id":11,"is_hole":false,"start_halfedge":40},{"face_ref":8,"id":12,"is_hole":false,"start_halfedge":44},{"face_ref":9,"id":13,"is_hole":false,"start_halfedge":48},{"face_ref":10,"id":14,"is_hole":false,"start_halfedge":52},{"face_ref":11,"id":15,"is_hole":false,"start_halfedge":56}],"shells":[{"faces":[0,1,2,3,4,5,6,7,8,9,10,11],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":10,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":13,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":12,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":11,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":14,"position":[1.1,0.3,1.5],"tolerance":1e-9},{"id":9,"outgoing_halfedge":15,"position":[1.9,0.3,1.5],"tolerance":1e-9},{"id":10,"outgoing_halfedge":4,"position":[1.1,0.0,1.5],"tolerance":1e-9},{"id":11,"outgoing_halfedge":5,"position":[1.9,0.0,1.5],"tolerance":1e-9},{"id":12,"outgoing_halfedge":16,"position":[3.5,0.3,0.7],"tolerance":1e-9},{"id":13,"outgoing_halfedge":19,"position":[3.5,0.3,2.2],"tolerance":1e-9},{"id":14,"outgoing_halfedge":18,"position":[4.5,0.3,2.2],"tolerance":1e-9},{"id":15,"outgoing_halfedge":17,"position":[4.5,0.3,0.7],"tolerance":1e-9},{"id":16,"outgoing_halfedge":6,"position":[3.5,0.0,0.7],"tolerance":1e-9},{"id":17,"outgoing_halfedge":7,"position":[3.5,0.0,2.2],"tolerance":1e-9},{"id":18,"outgoing_halfedge":8,"position":[4.5,0.0,2.2],"tolerance":1e-9},{"id":19,"outgoing_halfedge":9,"position":[4.5,0.0,0.7],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.flush-mixed.forward.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.flush-mixed.forward.json new file mode 100644 index 0000000..6177d85 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.flush-mixed.forward.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"flush-round","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-round","entity":"flush-round","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":0.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-round","entity":"flush-round","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-round","entity":"flush-round","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.9,0.0,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[1.9,0.3,1.5],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,0.7]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.3,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.0,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.3,0.7]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.3,1.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.0,1.5]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.3,1.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.3,1.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.0,1.5]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,0.7]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.5,0.7]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,-1.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,-1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.5,-0.7]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.5,-0.7]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,0.7],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.3,0.7],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,1.5],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"}]},"id":"flush-rectangle","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.500000001,"lo":3.499999999},{"hi":1.500000001,"lo":0.699999999}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.500000001,"lo":3.499999999},{"hi":-0.699999999,"lo":-1.500000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.800000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.800000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.5,0.3,0.7],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.5,0.3,0.7],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.5,0.3,1.5],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.5,0.3,1.5],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.5,0.0,0.7],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.5,0.0,0.7],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.5,0.0,1.5],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.5,0.0,1.5],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_prismatic_profile_batch"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"flush-mixed-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.0,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.3,0.7]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.5,0.0,0.7]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.0,1.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.3,1.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.5,0.0,1.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.0,1.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.3,1.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,1.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,0.7]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,0.7]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,0.7]},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9000000000000004,0.0,1.5]},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0999999999999996,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.7]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5,1.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,0.7]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[4.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[4.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-3.0]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[6.0,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[2.5,-0.7]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,-1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,-1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.5,-0.7]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[4.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[4.5,-1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.0,0.7],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.0,1.5],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.0,0.7],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4}]},"id":"batch-prismatic.flush-mixed.forward","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":13,"id":0,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":23,"id":2,"tolerance":1e-9,"twin_halfedge":33},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":12,"id":3,"tolerance":1e-9,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":21,"id":5,"tolerance":1e-9,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":11,"id":6,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":25,"id":8,"tolerance":1e-9,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":9,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":29,"id":11,"tolerance":1e-9,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":17,"id":12,"tolerance":1e-9,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":4,"id":13,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":39,"id":14,"tolerance":1e-9,"twin_halfedge":49},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":16,"id":15,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":16,"tolerance":1e-9,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":37,"id":17,"tolerance":1e-9,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":15,"id":18,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":6,"id":19,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":41,"id":20,"tolerance":1e-9,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":14,"id":21,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":22,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":45,"id":23,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":19,"id":24,"tolerance":1e-9,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":8,"id":25,"tolerance":1e-9,"twin_halfedge":54},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":55,"id":26,"tolerance":1e-9,"twin_halfedge":57},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":18,"id":27,"tolerance":1e-9,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":9,"id":28,"tolerance":1e-9,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":53,"id":29,"tolerance":1e-9,"twin_halfedge":59}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1,2],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[4,5],"outer":3,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-3.000000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":6,"key":"side-1-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":0.800000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":7,"key":"side-1-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":8,"key":"side-1-2","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":0.800000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":9,"key":"side-1-3","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":10,"key":"side-2-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-round","entity":"flush-round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":-3.141592651089793,"lo":-6.283185309679586},{"hi":0.300000001,"lo":-1e-9}]}},{"id":11,"key":"side-2-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-round","entity":"flush-round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":2.5e-9,"lo":-3.1415926560897933},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":22,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":26,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":30,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":34,"wire_ref":null},{"edge":13,"face":0,"from":12,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":13,"twin":38,"wire_ref":null},{"edge":16,"face":0,"from":13,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":14,"twin":42,"wire_ref":null},{"edge":19,"face":0,"from":14,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":15,"twin":46,"wire_ref":null},{"edge":22,"face":0,"from":15,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":12,"twin":50,"wire_ref":null},{"edge":25,"face":0,"from":18,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":9,"prev":9,"to":19,"twin":54,"wire_ref":null},{"edge":28,"face":0,"from":19,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":8,"prev":8,"to":18,"twin":58,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":3,"next":11,"prev":13,"to":3,"twin":32,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":3,"next":12,"prev":10,"to":2,"twin":28,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":11,"to":1,"twin":24,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":10,"prev":12,"to":0,"twin":20,"wire_ref":null},{"edge":21,"face":1,"from":8,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":4,"next":15,"prev":17,"to":11,"twin":48,"wire_ref":null},{"edge":18,"face":1,"from":11,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":4,"next":16,"prev":14,"to":10,"twin":44,"wire_ref":null},{"edge":15,"face":1,"from":10,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":4,"next":17,"prev":15,"to":9,"twin":40,"wire_ref":null},{"edge":12,"face":1,"from":9,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":14,"prev":16,"to":8,"twin":36,"wire_ref":null},{"edge":27,"face":1,"from":16,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":5,"next":19,"prev":19,"to":17,"twin":56,"wire_ref":null},{"edge":24,"face":1,"from":17,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":5,"next":18,"prev":18,"to":16,"twin":52,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":6,"next":21,"prev":23,"to":1,"twin":13,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":6,"next":22,"prev":20,"to":5,"twin":27,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":6,"next":23,"prev":21,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":6,"next":20,"prev":22,"to":0,"twin":33,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":7,"next":25,"prev":27,"to":2,"twin":12,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":7,"next":26,"prev":24,"to":6,"twin":31,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":7,"next":27,"prev":25,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":7,"next":24,"prev":26,"to":1,"twin":21,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":8,"next":29,"prev":31,"to":3,"twin":11,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":8,"next":30,"prev":28,"to":7,"twin":35,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":8,"next":31,"prev":29,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":8,"next":28,"prev":30,"to":2,"twin":25,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":9,"next":33,"prev":35,"to":0,"twin":10,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":9,"next":34,"prev":32,"to":4,"twin":23,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":9,"next":35,"prev":33,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":9,"next":32,"prev":34,"to":3,"twin":29,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":10,"next":37,"prev":39,"to":9,"twin":17,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":10,"next":38,"prev":36,"to":13,"twin":43,"wire_ref":null},{"edge":13,"face":6,"from":13,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":10,"next":39,"prev":37,"to":12,"twin":4,"wire_ref":null},{"edge":14,"face":6,"from":12,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":10,"next":36,"prev":38,"to":8,"twin":49,"wire_ref":null},{"edge":15,"face":7,"from":9,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":11,"next":41,"prev":43,"to":10,"twin":16,"wire_ref":null},{"edge":20,"face":7,"from":10,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":11,"next":42,"prev":40,"to":14,"twin":47,"wire_ref":null},{"edge":16,"face":7,"from":14,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":11,"next":43,"prev":41,"to":13,"twin":5,"wire_ref":null},{"edge":17,"face":7,"from":13,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":11,"next":40,"prev":42,"to":9,"twin":37,"wire_ref":null},{"edge":18,"face":8,"from":10,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":12,"next":45,"prev":47,"to":11,"twin":15,"wire_ref":null},{"edge":23,"face":8,"from":11,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":12,"next":46,"prev":44,"to":15,"twin":51,"wire_ref":null},{"edge":19,"face":8,"from":15,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":12,"next":47,"prev":45,"to":14,"twin":6,"wire_ref":null},{"edge":20,"face":8,"from":14,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":12,"next":44,"prev":46,"to":10,"twin":41,"wire_ref":null},{"edge":21,"face":9,"from":11,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":13,"next":49,"prev":51,"to":8,"twin":14,"wire_ref":null},{"edge":14,"face":9,"from":8,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":13,"next":50,"prev":48,"to":12,"twin":39,"wire_ref":null},{"edge":22,"face":9,"from":12,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":13,"next":51,"prev":49,"to":15,"twin":7,"wire_ref":null},{"edge":23,"face":9,"from":15,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":13,"next":48,"prev":50,"to":11,"twin":45,"wire_ref":null},{"edge":24,"face":10,"from":16,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Reverse"},"id":52,"loop_ref":14,"next":53,"prev":55,"to":17,"twin":19,"wire_ref":null},{"edge":29,"face":10,"from":17,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":14,"next":54,"prev":52,"to":19,"twin":59,"wire_ref":null},{"edge":25,"face":10,"from":19,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Forward"},"id":54,"loop_ref":14,"next":55,"prev":53,"to":18,"twin":8,"wire_ref":null},{"edge":26,"face":10,"from":18,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Reverse"},"id":55,"loop_ref":14,"next":52,"prev":54,"to":16,"twin":57,"wire_ref":null},{"edge":27,"face":11,"from":17,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Reverse"},"id":56,"loop_ref":15,"next":57,"prev":59,"to":16,"twin":18,"wire_ref":null},{"edge":26,"face":11,"from":16,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":15,"next":58,"prev":56,"to":18,"twin":55,"wire_ref":null},{"edge":28,"face":11,"from":18,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Forward"},"id":58,"loop_ref":15,"next":59,"prev":57,"to":19,"twin":9,"wire_ref":null},{"edge":29,"face":11,"from":19,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":15,"next":56,"prev":58,"to":17,"twin":53,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":4},{"face_ref":0,"id":2,"is_hole":true,"start_halfedge":8},{"face_ref":1,"id":3,"is_hole":false,"start_halfedge":10},{"face_ref":1,"id":4,"is_hole":true,"start_halfedge":14},{"face_ref":1,"id":5,"is_hole":true,"start_halfedge":18},{"face_ref":2,"id":6,"is_hole":false,"start_halfedge":20},{"face_ref":3,"id":7,"is_hole":false,"start_halfedge":24},{"face_ref":4,"id":8,"is_hole":false,"start_halfedge":28},{"face_ref":5,"id":9,"is_hole":false,"start_halfedge":32},{"face_ref":6,"id":10,"is_hole":false,"start_halfedge":36},{"face_ref":7,"id":11,"is_hole":false,"start_halfedge":40},{"face_ref":8,"id":12,"is_hole":false,"start_halfedge":44},{"face_ref":9,"id":13,"is_hole":false,"start_halfedge":48},{"face_ref":10,"id":14,"is_hole":false,"start_halfedge":52},{"face_ref":11,"id":15,"is_hole":false,"start_halfedge":56}],"shells":[{"faces":[0,1,2,3,4,5,6,7,8,9,10,11],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":10,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":13,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":12,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":11,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":14,"position":[4.5,0.0,0.7],"tolerance":1e-9},{"id":9,"outgoing_halfedge":17,"position":[4.5,0.0,1.5],"tolerance":1e-9},{"id":10,"outgoing_halfedge":16,"position":[3.5,0.0,1.5],"tolerance":1e-9},{"id":11,"outgoing_halfedge":15,"position":[3.5,0.0,0.7],"tolerance":1e-9},{"id":12,"outgoing_halfedge":4,"position":[4.5,0.3,0.7],"tolerance":1e-9},{"id":13,"outgoing_halfedge":5,"position":[4.5,0.3,1.5],"tolerance":1e-9},{"id":14,"outgoing_halfedge":6,"position":[3.5,0.3,1.5],"tolerance":1e-9},{"id":15,"outgoing_halfedge":7,"position":[3.5,0.3,0.7],"tolerance":1e-9},{"id":16,"outgoing_halfedge":18,"position":[1.9000000000000004,0.0,1.5],"tolerance":1e-9},{"id":17,"outgoing_halfedge":19,"position":[1.0999999999999996,0.0,1.5],"tolerance":1e-9},{"id":18,"outgoing_halfedge":8,"position":[1.9000000000000004,0.3,1.5],"tolerance":1e-9},{"id":19,"outgoing_halfedge":9,"position":[1.0999999999999996,0.3,1.5],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.flush-mixed.reverse.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.flush-mixed.reverse.json new file mode 100644 index 0000000..2d97273 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.flush-mixed.reverse.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,0.7]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.3,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[4.5,0.0,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.3,0.7]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.3,1.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.0,1.5]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.3,1.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.3,1.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.0,1.5]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,0.7]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.5,0.7]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,-1.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,-1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.5,-0.7]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.5,-0.7]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,0.7],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.3,0.7],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,1.5],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"}]},"id":"flush-rectangle","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.500000001,"lo":3.499999999},{"hi":1.500000001,"lo":0.699999999}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.500000001,"lo":3.499999999},{"hi":-0.699999999,"lo":-1.500000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.800000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.800000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[3.5,0.3,0.7],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[4.5,0.3,0.7],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[4.5,0.3,1.5],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[3.5,0.3,1.5],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[3.5,0.0,0.7],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[4.5,0.0,0.7],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[4.5,0.0,1.5],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[3.5,0.0,1.5],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"flush-round","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-round","entity":"flush-round","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":0.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-round","entity":"flush-round","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-round","entity":"flush-round","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.9,0.0,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[1.9,0.3,1.5],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_prismatic_profile_batch"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"flush-mixed-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.1,0.3,1.5]},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.9,0.3,1.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.3,0.7]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.0,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,0.7]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,1.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,1.5]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,1.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.5,0.3,1.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.5,0.0,1.5]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.3,1.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.3,0.7]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[4.5,0.0,0.7]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[4.5,0.3,0.7]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[1.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[1.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,0.7]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.5,1.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,0.7]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[1.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[1.5,-1.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.5,-0.7]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.5,-1.5]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.5,-1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,-0.7]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[1.0,0.0,-0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,3.0],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[3.5,0.3,0.7],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.3,1.5],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.3,0.7],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"}]},"id":"batch-prismatic.flush-mixed.reverse","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":13,"id":0,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":23,"id":2,"tolerance":1e-9,"twin_halfedge":33},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":12,"id":3,"tolerance":1e-9,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":21,"id":5,"tolerance":1e-9,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":11,"id":6,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":25,"id":8,"tolerance":1e-9,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":9,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":29,"id":11,"tolerance":1e-9,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":15,"id":12,"tolerance":1e-9,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":4,"id":13,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":39,"id":14,"tolerance":1e-9,"twin_halfedge":41},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":14,"id":15,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":5,"id":16,"tolerance":1e-9,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":37,"id":17,"tolerance":1e-9,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":19,"id":18,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":6,"id":19,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":47,"id":20,"tolerance":1e-9,"twin_halfedge":57},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":18,"id":21,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":22,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":45,"id":23,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":17,"id":24,"tolerance":1e-9,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":8,"id":25,"tolerance":1e-9,"twin_halfedge":54},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":49,"id":26,"tolerance":1e-9,"twin_halfedge":55},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":16,"id":27,"tolerance":1e-9,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":9,"id":28,"tolerance":1e-9,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":53,"id":29,"tolerance":1e-9,"twin_halfedge":59}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1,2],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[4,5],"outer":3,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-3.000000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"flush-mixed-host","entity":"flush-mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":6,"key":"side-1-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-round","entity":"flush-round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":-3.141592651089793,"lo":-6.283185309679586},{"hi":0.300000001,"lo":-1e-9}]}},{"id":7,"key":"side-1-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-round","entity":"flush-round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":2.5e-9,"lo":-3.1415926560897933},{"hi":0.300000001,"lo":-1e-9}]}},{"id":8,"key":"side-2-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":0.800000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":9,"key":"side-2-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":10,"key":"side-2-2","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":0.800000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":11,"key":"side-2-3","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"flush-rectangle","entity":"flush-rectangle","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":22,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":26,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":30,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":34,"wire_ref":null},{"edge":13,"face":0,"from":10,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":5,"to":11,"twin":38,"wire_ref":null},{"edge":16,"face":0,"from":11,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":4,"prev":4,"to":10,"twin":42,"wire_ref":null},{"edge":19,"face":0,"from":16,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":2,"next":7,"prev":9,"to":17,"twin":46,"wire_ref":null},{"edge":22,"face":0,"from":17,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":2,"next":8,"prev":6,"to":18,"twin":50,"wire_ref":null},{"edge":25,"face":0,"from":18,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":7,"to":19,"twin":54,"wire_ref":null},{"edge":28,"face":0,"from":19,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":6,"prev":8,"to":16,"twin":58,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":3,"next":11,"prev":13,"to":3,"twin":32,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":3,"next":12,"prev":10,"to":2,"twin":28,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":11,"to":1,"twin":24,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":10,"prev":12,"to":0,"twin":20,"wire_ref":null},{"edge":15,"face":1,"from":8,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":4,"next":15,"prev":15,"to":9,"twin":40,"wire_ref":null},{"edge":12,"face":1,"from":9,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":4,"next":14,"prev":14,"to":8,"twin":36,"wire_ref":null},{"edge":27,"face":1,"from":12,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":5,"next":17,"prev":19,"to":15,"twin":56,"wire_ref":null},{"edge":24,"face":1,"from":15,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":5,"next":18,"prev":16,"to":14,"twin":52,"wire_ref":null},{"edge":21,"face":1,"from":14,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":5,"next":19,"prev":17,"to":13,"twin":48,"wire_ref":null},{"edge":18,"face":1,"from":13,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":5,"next":16,"prev":18,"to":12,"twin":44,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":6,"next":21,"prev":23,"to":1,"twin":13,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":6,"next":22,"prev":20,"to":5,"twin":27,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":6,"next":23,"prev":21,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":6,"next":20,"prev":22,"to":0,"twin":33,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":7,"next":25,"prev":27,"to":2,"twin":12,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":7,"next":26,"prev":24,"to":6,"twin":31,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":7,"next":27,"prev":25,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":7,"next":24,"prev":26,"to":1,"twin":21,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":8,"next":29,"prev":31,"to":3,"twin":11,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":8,"next":30,"prev":28,"to":7,"twin":35,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":8,"next":31,"prev":29,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":8,"next":28,"prev":30,"to":2,"twin":25,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":9,"next":33,"prev":35,"to":0,"twin":10,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":9,"next":34,"prev":32,"to":4,"twin":23,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":9,"next":35,"prev":33,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":9,"next":32,"prev":34,"to":3,"twin":29,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Reverse"},"id":36,"loop_ref":10,"next":37,"prev":39,"to":9,"twin":15,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":10,"next":38,"prev":36,"to":11,"twin":43,"wire_ref":null},{"edge":13,"face":6,"from":11,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Forward"},"id":38,"loop_ref":10,"next":39,"prev":37,"to":10,"twin":4,"wire_ref":null},{"edge":14,"face":6,"from":10,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":10,"next":36,"prev":38,"to":8,"twin":41,"wire_ref":null},{"edge":15,"face":7,"from":9,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Reverse"},"id":40,"loop_ref":11,"next":41,"prev":43,"to":8,"twin":14,"wire_ref":null},{"edge":14,"face":7,"from":8,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":11,"next":42,"prev":40,"to":10,"twin":39,"wire_ref":null},{"edge":16,"face":7,"from":10,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Forward"},"id":42,"loop_ref":11,"next":43,"prev":41,"to":11,"twin":5,"wire_ref":null},{"edge":17,"face":7,"from":11,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":11,"next":40,"prev":42,"to":9,"twin":37,"wire_ref":null},{"edge":18,"face":8,"from":12,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":12,"next":45,"prev":47,"to":13,"twin":19,"wire_ref":null},{"edge":23,"face":8,"from":13,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":12,"next":46,"prev":44,"to":17,"twin":51,"wire_ref":null},{"edge":19,"face":8,"from":17,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":12,"next":47,"prev":45,"to":16,"twin":6,"wire_ref":null},{"edge":20,"face":8,"from":16,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":12,"next":44,"prev":46,"to":12,"twin":57,"wire_ref":null},{"edge":21,"face":9,"from":13,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":13,"next":49,"prev":51,"to":14,"twin":18,"wire_ref":null},{"edge":26,"face":9,"from":14,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":13,"next":50,"prev":48,"to":18,"twin":55,"wire_ref":null},{"edge":22,"face":9,"from":18,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":13,"next":51,"prev":49,"to":17,"twin":7,"wire_ref":null},{"edge":23,"face":9,"from":17,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":13,"next":48,"prev":50,"to":13,"twin":45,"wire_ref":null},{"edge":24,"face":10,"from":14,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":14,"next":53,"prev":55,"to":15,"twin":17,"wire_ref":null},{"edge":29,"face":10,"from":15,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":14,"next":54,"prev":52,"to":19,"twin":59,"wire_ref":null},{"edge":25,"face":10,"from":19,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":14,"next":55,"prev":53,"to":18,"twin":8,"wire_ref":null},{"edge":26,"face":10,"from":18,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Reverse"},"id":55,"loop_ref":14,"next":52,"prev":54,"to":14,"twin":49,"wire_ref":null},{"edge":27,"face":11,"from":15,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":15,"next":57,"prev":59,"to":12,"twin":16,"wire_ref":null},{"edge":20,"face":11,"from":12,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":15,"next":58,"prev":56,"to":16,"twin":47,"wire_ref":null},{"edge":28,"face":11,"from":16,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":15,"next":59,"prev":57,"to":19,"twin":9,"wire_ref":null},{"edge":29,"face":11,"from":19,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":15,"next":56,"prev":58,"to":15,"twin":53,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":4},{"face_ref":0,"id":2,"is_hole":true,"start_halfedge":6},{"face_ref":1,"id":3,"is_hole":false,"start_halfedge":10},{"face_ref":1,"id":4,"is_hole":true,"start_halfedge":14},{"face_ref":1,"id":5,"is_hole":true,"start_halfedge":16},{"face_ref":2,"id":6,"is_hole":false,"start_halfedge":20},{"face_ref":3,"id":7,"is_hole":false,"start_halfedge":24},{"face_ref":4,"id":8,"is_hole":false,"start_halfedge":28},{"face_ref":5,"id":9,"is_hole":false,"start_halfedge":32},{"face_ref":6,"id":10,"is_hole":false,"start_halfedge":36},{"face_ref":7,"id":11,"is_hole":false,"start_halfedge":40},{"face_ref":8,"id":12,"is_hole":false,"start_halfedge":44},{"face_ref":9,"id":13,"is_hole":false,"start_halfedge":48},{"face_ref":10,"id":14,"is_hole":false,"start_halfedge":52},{"face_ref":11,"id":15,"is_hole":false,"start_halfedge":56}],"shells":[{"faces":[0,1,2,3,4,5,6,7,8,9,10,11],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":10,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":13,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":12,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":11,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":14,"position":[1.1,0.3,1.5],"tolerance":1e-9},{"id":9,"outgoing_halfedge":15,"position":[1.9,0.3,1.5],"tolerance":1e-9},{"id":10,"outgoing_halfedge":4,"position":[1.1,0.0,1.5],"tolerance":1e-9},{"id":11,"outgoing_halfedge":5,"position":[1.9,0.0,1.5],"tolerance":1e-9},{"id":12,"outgoing_halfedge":16,"position":[3.5,0.3,0.7],"tolerance":1e-9},{"id":13,"outgoing_halfedge":19,"position":[3.5,0.3,1.5],"tolerance":1e-9},{"id":14,"outgoing_halfedge":18,"position":[4.5,0.3,1.5],"tolerance":1e-9},{"id":15,"outgoing_halfedge":17,"position":[4.5,0.3,0.7],"tolerance":1e-9},{"id":16,"outgoing_halfedge":6,"position":[3.5,0.0,0.7],"tolerance":1e-9},{"id":17,"outgoing_halfedge":7,"position":[3.5,0.0,1.5],"tolerance":1e-9},{"id":18,"outgoing_halfedge":8,"position":[4.5,0.0,1.5],"tolerance":1e-9},{"id":19,"outgoing_halfedge":9,"position":[4.5,0.0,0.7],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.single-round.fallback.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.single-round.fallback.json new file mode 100644 index 0000000..3abd334 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.single-round.fallback.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"round-a","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":0.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.9,0.0,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[1.9,0.3,1.5],"tolerance":1e-9}],"wires":[]}}],"handlers":[],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"two-round-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"error":{"CoverageGap":{"families":["generic analytic solid","intersecting analytic solid"]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.single-round.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.single-round.json new file mode 100644 index 0000000..3abd334 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.single-round.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"round-a","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":0.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.9,0.0,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[1.9,0.3,1.5],"tolerance":1e-9}],"wires":[]}}],"handlers":[],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"two-round-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"error":{"CoverageGap":{"families":["generic analytic solid","intersecting analytic solid"]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.split-mixed.forward.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.split-mixed.forward.json new file mode 100644 index 0000000..82b32fb --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.split-mixed.forward.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.8,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,-0.5,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.8,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"split-round","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-round","entity":"split-round","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-round","entity":"split-round","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-round","entity":"split-round","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.9,-0.5,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[1.9,0.8,1.5],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.3,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,3.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.5,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.5,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,3.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.5,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,-3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.5,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.5,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.3,0.0],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,0.0],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,3.0],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.3,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"}]},"id":"full-height","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.500000001,"lo":2.499999999},{"hi":3.000000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.500000001,"lo":2.499999999},{"hi":1e-9,"lo":-3.000000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.5,0.3,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.5,0.3,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.5,0.3,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.5,0.3,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.5,0.0,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.5,0.0,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.5,0.0,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.5,0.0,3.0],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_prismatic_profile_batch"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"split-mixed-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.5,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.5,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.5,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.5,0.0,3.0]},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9000000000000004,0.0,1.5]},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0999999999999996,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-3.0]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[2.5,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,3.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[4.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[4.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,-3.0]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[6.0,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[4.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[4.5,-1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.0,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4}]},"id":"batch-prismatic.split-mixed.forward","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0},{"cavity_shells":[],"outer_shell":1}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":33,"id":12,"tolerance":1e-9,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":24,"id":13,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":39,"id":14,"tolerance":1e-9,"twin_halfedge":49},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":32,"id":15,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":25,"id":16,"tolerance":1e-9,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":37,"id":17,"tolerance":1e-9,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":31,"id":18,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":26,"id":19,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":41,"id":20,"tolerance":1e-9,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":30,"id":21,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":27,"id":22,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":45,"id":23,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":35,"id":24,"tolerance":1e-9,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":28,"id":25,"tolerance":1e-9,"twin_halfedge":54},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":55,"id":26,"tolerance":1e-9,"twin_halfedge":57},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":34,"id":27,"tolerance":1e-9,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":29,"id":28,"tolerance":1e-9,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":53,"id":29,"tolerance":1e-9,"twin_halfedge":59}],"faces":[{"id":0,"key":"batch-prismatic.split-mixed.forward:part:0:top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":1,"key":"batch-prismatic.split-mixed.forward:part:0:bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":1e-9,"lo":-3.000000001}]}},{"id":2,"key":"batch-prismatic.split-mixed.forward:part:0:side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"batch-prismatic.split-mixed.forward:part:0:side-0-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"full-height","entity":"full-height","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"batch-prismatic.split-mixed.forward:part:0:side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"batch-prismatic.split-mixed.forward:part:0:side-0-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":6,"key":"batch-prismatic.split-mixed.forward:part:1:top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":1,"surface":6,"trim":{"chart":0,"holes":[7],"outer":6,"uv_bounds":[{"hi":6.000000001,"lo":3.499999999},{"hi":3.000000001,"lo":-1e-9}]}},{"id":7,"key":"batch-prismatic.split-mixed.forward:part:1:bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":1,"surface":7,"trim":{"chart":0,"holes":[9],"outer":8,"uv_bounds":[{"hi":6.000000001,"lo":3.499999999},{"hi":1e-9,"lo":-3.000000001}]}},{"id":8,"key":"batch-prismatic.split-mixed.forward:part:1:side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":1,"surface":8,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":9,"key":"batch-prismatic.split-mixed.forward:part:1:side-0-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":1,"surface":9,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":10,"key":"batch-prismatic.split-mixed.forward:part:1:side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":1,"surface":10,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":11,"key":"batch-prismatic.split-mixed.forward:part:1:side-0-3","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"full-height","entity":"full-height","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":1,"surface":11,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":12,"key":"batch-prismatic.split-mixed.forward:part:1:side-1-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"split-round","entity":"split-round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":1,"surface":12,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":-3.141592651089793,"lo":-6.283185309679586},{"hi":0.300000001,"lo":-1e-9}]}},{"id":13,"key":"batch-prismatic.split-mixed.forward:part:1:side-1-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"split-round","entity":"split-round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":1,"surface":13,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":2.5e-9,"lo":-3.1415926560897933},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null},{"edge":13,"face":6,"from":12,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":13,"twin":38,"wire_ref":null},{"edge":16,"face":6,"from":13,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":14,"twin":42,"wire_ref":null},{"edge":19,"face":6,"from":14,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Forward"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":15,"twin":46,"wire_ref":null},{"edge":22,"face":6,"from":15,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":12,"twin":50,"wire_ref":null},{"edge":25,"face":6,"from":18,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Reverse"},"id":28,"loop_ref":7,"next":29,"prev":29,"to":19,"twin":54,"wire_ref":null},{"edge":28,"face":6,"from":19,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Reverse"},"id":29,"loop_ref":7,"next":28,"prev":28,"to":18,"twin":58,"wire_ref":null},{"edge":21,"face":7,"from":8,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":8,"next":31,"prev":33,"to":11,"twin":48,"wire_ref":null},{"edge":18,"face":7,"from":11,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":8,"next":32,"prev":30,"to":10,"twin":44,"wire_ref":null},{"edge":15,"face":7,"from":10,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Reverse"},"id":32,"loop_ref":8,"next":33,"prev":31,"to":9,"twin":40,"wire_ref":null},{"edge":12,"face":7,"from":9,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":8,"next":30,"prev":32,"to":8,"twin":36,"wire_ref":null},{"edge":27,"face":7,"from":16,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Forward"},"id":34,"loop_ref":9,"next":35,"prev":35,"to":17,"twin":56,"wire_ref":null},{"edge":24,"face":7,"from":17,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Forward"},"id":35,"loop_ref":9,"next":34,"prev":34,"to":16,"twin":52,"wire_ref":null},{"edge":12,"face":8,"from":8,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":10,"next":37,"prev":39,"to":9,"twin":33,"wire_ref":null},{"edge":17,"face":8,"from":9,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":10,"next":38,"prev":36,"to":13,"twin":43,"wire_ref":null},{"edge":13,"face":8,"from":13,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":10,"next":39,"prev":37,"to":12,"twin":24,"wire_ref":null},{"edge":14,"face":8,"from":12,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":10,"next":36,"prev":38,"to":8,"twin":49,"wire_ref":null},{"edge":15,"face":9,"from":9,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":11,"next":41,"prev":43,"to":10,"twin":32,"wire_ref":null},{"edge":20,"face":9,"from":10,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":11,"next":42,"prev":40,"to":14,"twin":47,"wire_ref":null},{"edge":16,"face":9,"from":14,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":11,"next":43,"prev":41,"to":13,"twin":25,"wire_ref":null},{"edge":17,"face":9,"from":13,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":11,"next":40,"prev":42,"to":9,"twin":37,"wire_ref":null},{"edge":18,"face":10,"from":10,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":12,"next":45,"prev":47,"to":11,"twin":31,"wire_ref":null},{"edge":23,"face":10,"from":11,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":12,"next":46,"prev":44,"to":15,"twin":51,"wire_ref":null},{"edge":19,"face":10,"from":15,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":12,"next":47,"prev":45,"to":14,"twin":26,"wire_ref":null},{"edge":20,"face":10,"from":14,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":12,"next":44,"prev":46,"to":10,"twin":41,"wire_ref":null},{"edge":21,"face":11,"from":11,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":13,"next":49,"prev":51,"to":8,"twin":30,"wire_ref":null},{"edge":14,"face":11,"from":8,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":13,"next":50,"prev":48,"to":12,"twin":39,"wire_ref":null},{"edge":22,"face":11,"from":12,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":13,"next":51,"prev":49,"to":15,"twin":27,"wire_ref":null},{"edge":23,"face":11,"from":15,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":13,"next":48,"prev":50,"to":11,"twin":45,"wire_ref":null},{"edge":24,"face":12,"from":16,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Reverse"},"id":52,"loop_ref":14,"next":53,"prev":55,"to":17,"twin":35,"wire_ref":null},{"edge":29,"face":12,"from":17,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":14,"next":54,"prev":52,"to":19,"twin":59,"wire_ref":null},{"edge":25,"face":12,"from":19,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Forward"},"id":54,"loop_ref":14,"next":55,"prev":53,"to":18,"twin":28,"wire_ref":null},{"edge":26,"face":12,"from":18,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Reverse"},"id":55,"loop_ref":14,"next":52,"prev":54,"to":16,"twin":57,"wire_ref":null},{"edge":27,"face":13,"from":17,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Reverse"},"id":56,"loop_ref":15,"next":57,"prev":59,"to":16,"twin":34,"wire_ref":null},{"edge":26,"face":13,"from":16,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":15,"next":58,"prev":56,"to":18,"twin":55,"wire_ref":null},{"edge":28,"face":13,"from":18,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Forward"},"id":58,"loop_ref":15,"next":59,"prev":57,"to":19,"twin":29,"wire_ref":null},{"edge":29,"face":13,"from":19,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":15,"next":56,"prev":58,"to":17,"twin":53,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":6,"id":7,"is_hole":true,"start_halfedge":28},{"face_ref":7,"id":8,"is_hole":false,"start_halfedge":30},{"face_ref":7,"id":9,"is_hole":true,"start_halfedge":34},{"face_ref":8,"id":10,"is_hole":false,"start_halfedge":36},{"face_ref":9,"id":11,"is_hole":false,"start_halfedge":40},{"face_ref":10,"id":12,"is_hole":false,"start_halfedge":44},{"face_ref":11,"id":13,"is_hole":false,"start_halfedge":48},{"face_ref":12,"id":14,"is_hole":false,"start_halfedge":52},{"face_ref":13,"id":15,"is_hole":false,"start_halfedge":56}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true},{"faces":[6,7,8,9,10,11,12,13],"id":1,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.5,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.5,0.0,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.5,0.3,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.5,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":30,"position":[2.5,0.0,0.0],"tolerance":1e-9},{"id":9,"outgoing_halfedge":33,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":10,"outgoing_halfedge":32,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":11,"outgoing_halfedge":31,"position":[2.5,0.0,3.0],"tolerance":1e-9},{"id":12,"outgoing_halfedge":24,"position":[2.5,0.3,0.0],"tolerance":1e-9},{"id":13,"outgoing_halfedge":25,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":14,"outgoing_halfedge":26,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":15,"outgoing_halfedge":27,"position":[2.5,0.3,3.0],"tolerance":1e-9},{"id":16,"outgoing_halfedge":34,"position":[1.9000000000000004,0.0,1.5],"tolerance":1e-9},{"id":17,"outgoing_halfedge":35,"position":[1.0999999999999996,0.0,1.5],"tolerance":1e-9},{"id":18,"outgoing_halfedge":28,"position":[1.9000000000000004,0.3,1.5],"tolerance":1e-9},{"id":19,"outgoing_halfedge":29,"position":[1.0999999999999996,0.3,1.5],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.split-mixed.reverse.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.split-mixed.reverse.json new file mode 100644 index 0000000..bb4f5a5 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.split-mixed.reverse.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.3,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,3.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.5,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.5,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,3.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.5,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,-3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.5,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[2.5,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.3,0.0],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,0.0],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,3.0],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.3,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"}]},"id":"full-height","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.500000001,"lo":2.499999999},{"hi":3.000000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.500000001,"lo":2.499999999},{"hi":1e-9,"lo":-3.000000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"full-height","entity":"full-height","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.5,0.3,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[3.5,0.3,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[3.5,0.3,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[2.5,0.3,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.5,0.0,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[3.5,0.0,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[3.5,0.0,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[2.5,0.0,3.0],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.8,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,-0.5,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,-0.5,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.8,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"split-round","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-round","entity":"split-round","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-round","entity":"split-round","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-round","entity":"split-round","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.9,-0.5,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[1.9,0.8,1.5],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_prismatic_profile_batch"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"split-mixed-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.5,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.3,3.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.5,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.5,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.1,0.3,1.5]},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.9,0.3,1.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.5,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.3,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.5,0.0,3.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[3.5,0.3,3.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[1.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[1.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.5,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.5,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[1.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[1.5,-1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[3.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.5,-3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[3.5,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[1.0,0.0,-0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.3,0.0],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.5,0.3,3.0],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,-0.0],"y":[0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[1.0,0.0,-0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,0.0],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[0.0,0.0,1.0],"y":[0.0,-1.0,0.0],"z":[1.0,0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,3.0],"x":[-1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.5,0.3,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,-1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"}]},"id":"batch-prismatic.split-mixed.reverse","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0},{"cavity_shells":[],"outer_shell":1}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":9,"id":0,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":15,"id":2,"tolerance":1e-9,"twin_halfedge":25},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":3,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":13,"id":5,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":7,"id":6,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":17,"id":8,"tolerance":1e-9,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":6,"id":9,"tolerance":1e-9,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":21,"id":11,"tolerance":1e-9,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":11,"id":12,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":4,"id":13,"tolerance":1e-9,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":31,"id":14,"tolerance":1e-9,"twin_halfedge":33},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":10,"id":15,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":5,"id":16,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":29,"id":17,"tolerance":1e-9,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":43,"id":18,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":36,"id":19,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":47,"id":20,"tolerance":1e-9,"twin_halfedge":57},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":42,"id":21,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":37,"id":22,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":45,"id":23,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":41,"id":24,"tolerance":1e-9,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":38,"id":25,"tolerance":1e-9,"twin_halfedge":54},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":49,"id":26,"tolerance":1e-9,"twin_halfedge":55},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":40,"id":27,"tolerance":1e-9,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":39,"id":28,"tolerance":1e-9,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":53,"id":29,"tolerance":1e-9,"twin_halfedge":59}],"faces":[{"id":0,"key":"batch-prismatic.split-mixed.reverse:part:0:top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1],"outer":0,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":1,"key":"batch-prismatic.split-mixed.reverse:part:0:bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[3],"outer":2,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":1e-9,"lo":-3.000000001}]}},{"id":2,"key":"batch-prismatic.split-mixed.reverse:part:0:side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"batch-prismatic.split-mixed.reverse:part:0:side-0-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"full-height","entity":"full-height","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"batch-prismatic.split-mixed.reverse:part:0:side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"batch-prismatic.split-mixed.reverse:part:0:side-0-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":6,"key":"batch-prismatic.split-mixed.reverse:part:0:side-1-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"split-round","entity":"split-round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":-3.141592651089793,"lo":-6.283185309679586},{"hi":0.300000001,"lo":-1e-9}]}},{"id":7,"key":"batch-prismatic.split-mixed.reverse:part:0:side-1-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"split-round","entity":"split-round","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":2.5e-9,"lo":-3.1415926560897933},{"hi":0.300000001,"lo":-1e-9}]}},{"id":8,"key":"batch-prismatic.split-mixed.reverse:part:1:top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":1,"surface":8,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":6.000000001,"lo":3.499999999},{"hi":3.000000001,"lo":-1e-9}]}},{"id":9,"key":"batch-prismatic.split-mixed.reverse:part:1:bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":1,"surface":9,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":6.000000001,"lo":3.499999999},{"hi":1e-9,"lo":-3.000000001}]}},{"id":10,"key":"batch-prismatic.split-mixed.reverse:part:1:side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":1,"surface":10,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":11,"key":"batch-prismatic.split-mixed.reverse:part:1:side-0-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":1,"surface":11,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":12,"key":"batch-prismatic.split-mixed.reverse:part:1:side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"split-mixed-host","entity":"split-mixed-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":1,"surface":12,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":2.500000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":13,"key":"batch-prismatic.split-mixed.reverse:part:1:side-0-3","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"full-height","entity":"full-height","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":1,"surface":13,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":14,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":18,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":26,"wire_ref":null},{"edge":13,"face":0,"from":10,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":5,"to":11,"twin":30,"wire_ref":null},{"edge":16,"face":0,"from":11,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":4,"prev":4,"to":10,"twin":34,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":2,"next":7,"prev":9,"to":3,"twin":24,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":2,"next":8,"prev":6,"to":2,"twin":20,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":9,"prev":7,"to":1,"twin":16,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":6,"prev":8,"to":0,"twin":12,"wire_ref":null},{"edge":15,"face":1,"from":8,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":3,"next":11,"prev":11,"to":9,"twin":32,"wire_ref":null},{"edge":12,"face":1,"from":9,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":3,"next":10,"prev":10,"to":8,"twin":28,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":4,"next":13,"prev":15,"to":1,"twin":9,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":4,"next":14,"prev":12,"to":5,"twin":19,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":4,"next":15,"prev":13,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":4,"next":12,"prev":14,"to":0,"twin":25,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":5,"next":17,"prev":19,"to":2,"twin":8,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":5,"next":18,"prev":16,"to":6,"twin":23,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":5,"next":19,"prev":17,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":5,"next":16,"prev":18,"to":1,"twin":13,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":6,"next":21,"prev":23,"to":3,"twin":7,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":6,"next":22,"prev":20,"to":7,"twin":27,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":6,"next":23,"prev":21,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":6,"next":20,"prev":22,"to":2,"twin":17,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":7,"next":25,"prev":27,"to":0,"twin":6,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":7,"next":26,"prev":24,"to":4,"twin":15,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":7,"next":27,"prev":25,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":7,"next":24,"prev":26,"to":3,"twin":21,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Reverse"},"id":28,"loop_ref":8,"next":29,"prev":31,"to":9,"twin":11,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":8,"next":30,"prev":28,"to":11,"twin":35,"wire_ref":null},{"edge":13,"face":6,"from":11,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Forward"},"id":30,"loop_ref":8,"next":31,"prev":29,"to":10,"twin":4,"wire_ref":null},{"edge":14,"face":6,"from":10,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":8,"next":28,"prev":30,"to":8,"twin":33,"wire_ref":null},{"edge":15,"face":7,"from":9,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Reverse"},"id":32,"loop_ref":9,"next":33,"prev":35,"to":8,"twin":10,"wire_ref":null},{"edge":14,"face":7,"from":8,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":9,"next":34,"prev":32,"to":10,"twin":31,"wire_ref":null},{"edge":16,"face":7,"from":10,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Forward"},"id":34,"loop_ref":9,"next":35,"prev":33,"to":11,"twin":5,"wire_ref":null},{"edge":17,"face":7,"from":11,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":9,"next":32,"prev":34,"to":9,"twin":29,"wire_ref":null},{"edge":19,"face":8,"from":16,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":10,"next":37,"prev":39,"to":17,"twin":46,"wire_ref":null},{"edge":22,"face":8,"from":17,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":10,"next":38,"prev":36,"to":18,"twin":50,"wire_ref":null},{"edge":25,"face":8,"from":18,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Forward"},"id":38,"loop_ref":10,"next":39,"prev":37,"to":19,"twin":54,"wire_ref":null},{"edge":28,"face":8,"from":19,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":10,"next":36,"prev":38,"to":16,"twin":58,"wire_ref":null},{"edge":27,"face":9,"from":12,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Reverse"},"id":40,"loop_ref":11,"next":41,"prev":43,"to":15,"twin":56,"wire_ref":null},{"edge":24,"face":9,"from":15,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Reverse"},"id":41,"loop_ref":11,"next":42,"prev":40,"to":14,"twin":52,"wire_ref":null},{"edge":21,"face":9,"from":14,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":11,"next":43,"prev":41,"to":13,"twin":48,"wire_ref":null},{"edge":18,"face":9,"from":13,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":11,"next":40,"prev":42,"to":12,"twin":44,"wire_ref":null},{"edge":18,"face":10,"from":12,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":12,"next":45,"prev":47,"to":13,"twin":43,"wire_ref":null},{"edge":23,"face":10,"from":13,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":12,"next":46,"prev":44,"to":17,"twin":51,"wire_ref":null},{"edge":19,"face":10,"from":17,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":12,"next":47,"prev":45,"to":16,"twin":36,"wire_ref":null},{"edge":20,"face":10,"from":16,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":12,"next":44,"prev":46,"to":12,"twin":57,"wire_ref":null},{"edge":21,"face":11,"from":13,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":13,"next":49,"prev":51,"to":14,"twin":42,"wire_ref":null},{"edge":26,"face":11,"from":14,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":13,"next":50,"prev":48,"to":18,"twin":55,"wire_ref":null},{"edge":22,"face":11,"from":18,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":13,"next":51,"prev":49,"to":17,"twin":37,"wire_ref":null},{"edge":23,"face":11,"from":17,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":13,"next":48,"prev":50,"to":13,"twin":45,"wire_ref":null},{"edge":24,"face":12,"from":14,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":14,"next":53,"prev":55,"to":15,"twin":41,"wire_ref":null},{"edge":29,"face":12,"from":15,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":14,"next":54,"prev":52,"to":19,"twin":59,"wire_ref":null},{"edge":25,"face":12,"from":19,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":14,"next":55,"prev":53,"to":18,"twin":38,"wire_ref":null},{"edge":26,"face":12,"from":18,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Reverse"},"id":55,"loop_ref":14,"next":52,"prev":54,"to":14,"twin":49,"wire_ref":null},{"edge":27,"face":13,"from":15,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":15,"next":57,"prev":59,"to":12,"twin":40,"wire_ref":null},{"edge":20,"face":13,"from":12,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":15,"next":58,"prev":56,"to":16,"twin":47,"wire_ref":null},{"edge":28,"face":13,"from":16,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":15,"next":59,"prev":57,"to":19,"twin":39,"wire_ref":null},{"edge":29,"face":13,"from":19,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":15,"next":56,"prev":58,"to":15,"twin":53,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":4},{"face_ref":1,"id":2,"is_hole":false,"start_halfedge":6},{"face_ref":1,"id":3,"is_hole":true,"start_halfedge":10},{"face_ref":2,"id":4,"is_hole":false,"start_halfedge":12},{"face_ref":3,"id":5,"is_hole":false,"start_halfedge":16},{"face_ref":4,"id":6,"is_hole":false,"start_halfedge":20},{"face_ref":5,"id":7,"is_hole":false,"start_halfedge":24},{"face_ref":6,"id":8,"is_hole":false,"start_halfedge":28},{"face_ref":7,"id":9,"is_hole":false,"start_halfedge":32},{"face_ref":8,"id":10,"is_hole":false,"start_halfedge":36},{"face_ref":9,"id":11,"is_hole":false,"start_halfedge":40},{"face_ref":10,"id":12,"is_hole":false,"start_halfedge":44},{"face_ref":11,"id":13,"is_hole":false,"start_halfedge":48},{"face_ref":12,"id":14,"is_hole":false,"start_halfedge":52},{"face_ref":13,"id":15,"is_hole":false,"start_halfedge":56}],"shells":[{"faces":[0,1,2,3,4,5,6,7],"id":0,"is_closed":true},{"faces":[8,9,10,11,12,13],"id":1,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":6,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":9,"position":[2.5,0.3,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":8,"position":[2.5,0.3,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":7,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[2.5,0.0,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[2.5,0.0,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":10,"position":[1.1,0.3,1.5],"tolerance":1e-9},{"id":9,"outgoing_halfedge":11,"position":[1.9,0.3,1.5],"tolerance":1e-9},{"id":10,"outgoing_halfedge":4,"position":[1.1,0.0,1.5],"tolerance":1e-9},{"id":11,"outgoing_halfedge":5,"position":[1.9,0.0,1.5],"tolerance":1e-9},{"id":12,"outgoing_halfedge":40,"position":[3.5,0.3,0.0],"tolerance":1e-9},{"id":13,"outgoing_halfedge":43,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":14,"outgoing_halfedge":42,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":15,"outgoing_halfedge":41,"position":[3.5,0.3,3.0],"tolerance":1e-9},{"id":16,"outgoing_halfedge":36,"position":[3.5,0.0,0.0],"tolerance":1e-9},{"id":17,"outgoing_halfedge":37,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":18,"outgoing_halfedge":38,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":19,"outgoing_halfedge":39,"position":[3.5,0.0,3.0],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.two-round.forward.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.two-round.forward.json new file mode 100644 index 0000000..fc0b570 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.two-round.forward.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"round-a","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":0.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.9,0.0,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[1.9,0.3,1.5],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[4.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[4.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.9,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[4.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[4.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"round-b","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-b","entity":"round-b","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":0.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-b","entity":"round-b","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-b","entity":"round-b","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[4.9,0.0,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[4.9,0.3,1.5],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_prismatic_profile_batch"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"two-round-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"frame":{"origin":[4.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[4.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.9,0.0,1.5]},{"frame":{"origin":[4.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[4.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.1,0.0,1.5]},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9000000000000004,0.0,1.5]},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0999999999999996,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[1.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[1.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[4.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[4.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-3.0]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[6.0,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,-0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[1.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[1.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[4.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[4.5,-1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[4.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4}]},"id":"batch-prismatic.two-round.forward","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":11,"id":0,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":19,"id":2,"tolerance":1e-9,"twin_halfedge":29},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":3,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":17,"id":5,"tolerance":1e-9,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":9,"id":6,"tolerance":1e-9,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":21,"id":8,"tolerance":1e-9,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":9,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":25,"id":11,"tolerance":1e-9,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":13,"id":12,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":4,"id":13,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":35,"id":14,"tolerance":1e-9,"twin_halfedge":37},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":12,"id":15,"tolerance":1e-9,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":5,"id":16,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":33,"id":17,"tolerance":1e-9,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":15,"id":18,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":6,"id":19,"tolerance":1e-9,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":43,"id":20,"tolerance":1e-9,"twin_halfedge":45},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":14,"id":21,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":7,"id":22,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":41,"id":23,"tolerance":1e-9,"twin_halfedge":47}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1,2],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[4,5],"outer":3,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-3.000000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":6,"key":"side-1-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round-b","entity":"round-b","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":-3.141592651089793,"lo":-6.283185309679586},{"hi":0.300000001,"lo":-1e-9}]}},{"id":7,"key":"side-1-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round-b","entity":"round-b","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":2.5e-9,"lo":-3.1415926560897933},{"hi":0.300000001,"lo":-1e-9}]}},{"id":8,"key":"side-2-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round-a","entity":"round-a","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":-3.141592651089793,"lo":-6.283185309679586},{"hi":0.300000001,"lo":-1e-9}]}},{"id":9,"key":"side-2-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round-a","entity":"round-a","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":2.5e-9,"lo":-3.1415926560897933},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":18,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":22,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":26,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":30,"wire_ref":null},{"edge":13,"face":0,"from":10,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":5,"to":11,"twin":34,"wire_ref":null},{"edge":16,"face":0,"from":11,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":4,"prev":4,"to":10,"twin":38,"wire_ref":null},{"edge":19,"face":0,"from":14,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":2,"next":7,"prev":7,"to":15,"twin":42,"wire_ref":null},{"edge":22,"face":0,"from":15,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":2,"next":6,"prev":6,"to":14,"twin":46,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":3,"next":9,"prev":11,"to":3,"twin":28,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":3,"next":10,"prev":8,"to":2,"twin":24,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":3,"next":11,"prev":9,"to":1,"twin":20,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":3,"next":8,"prev":10,"to":0,"twin":16,"wire_ref":null},{"edge":15,"face":1,"from":8,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":4,"next":13,"prev":13,"to":9,"twin":36,"wire_ref":null},{"edge":12,"face":1,"from":9,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":4,"next":12,"prev":12,"to":8,"twin":32,"wire_ref":null},{"edge":21,"face":1,"from":12,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":5,"next":15,"prev":15,"to":13,"twin":44,"wire_ref":null},{"edge":18,"face":1,"from":13,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":5,"next":14,"prev":14,"to":12,"twin":40,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":6,"next":17,"prev":19,"to":1,"twin":11,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":6,"next":18,"prev":16,"to":5,"twin":23,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":6,"next":19,"prev":17,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":6,"next":16,"prev":18,"to":0,"twin":29,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":7,"next":21,"prev":23,"to":2,"twin":10,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":7,"next":22,"prev":20,"to":6,"twin":27,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":7,"next":23,"prev":21,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":7,"next":20,"prev":22,"to":1,"twin":17,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":8,"next":25,"prev":27,"to":3,"twin":9,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":8,"next":26,"prev":24,"to":7,"twin":31,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":8,"next":27,"prev":25,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":8,"next":24,"prev":26,"to":2,"twin":21,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":9,"next":29,"prev":31,"to":0,"twin":8,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":9,"next":30,"prev":28,"to":4,"twin":19,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":9,"next":31,"prev":29,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":9,"next":28,"prev":30,"to":3,"twin":25,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Reverse"},"id":32,"loop_ref":10,"next":33,"prev":35,"to":9,"twin":13,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":10,"next":34,"prev":32,"to":11,"twin":39,"wire_ref":null},{"edge":13,"face":6,"from":11,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Forward"},"id":34,"loop_ref":10,"next":35,"prev":33,"to":10,"twin":4,"wire_ref":null},{"edge":14,"face":6,"from":10,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":10,"next":32,"prev":34,"to":8,"twin":37,"wire_ref":null},{"edge":15,"face":7,"from":9,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Reverse"},"id":36,"loop_ref":11,"next":37,"prev":39,"to":8,"twin":12,"wire_ref":null},{"edge":14,"face":7,"from":8,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":11,"next":38,"prev":36,"to":10,"twin":35,"wire_ref":null},{"edge":16,"face":7,"from":10,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Forward"},"id":38,"loop_ref":11,"next":39,"prev":37,"to":11,"twin":5,"wire_ref":null},{"edge":17,"face":7,"from":11,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":11,"next":36,"prev":38,"to":9,"twin":33,"wire_ref":null},{"edge":18,"face":8,"from":12,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Reverse"},"id":40,"loop_ref":12,"next":41,"prev":43,"to":13,"twin":15,"wire_ref":null},{"edge":23,"face":8,"from":13,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":12,"next":42,"prev":40,"to":15,"twin":47,"wire_ref":null},{"edge":19,"face":8,"from":15,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Forward"},"id":42,"loop_ref":12,"next":43,"prev":41,"to":14,"twin":6,"wire_ref":null},{"edge":20,"face":8,"from":14,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":12,"next":40,"prev":42,"to":12,"twin":45,"wire_ref":null},{"edge":21,"face":9,"from":13,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Reverse"},"id":44,"loop_ref":13,"next":45,"prev":47,"to":12,"twin":14,"wire_ref":null},{"edge":20,"face":9,"from":12,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":13,"next":46,"prev":44,"to":14,"twin":43,"wire_ref":null},{"edge":22,"face":9,"from":14,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Forward"},"id":46,"loop_ref":13,"next":47,"prev":45,"to":15,"twin":7,"wire_ref":null},{"edge":23,"face":9,"from":15,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":13,"next":44,"prev":46,"to":13,"twin":41,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":4},{"face_ref":0,"id":2,"is_hole":true,"start_halfedge":6},{"face_ref":1,"id":3,"is_hole":false,"start_halfedge":8},{"face_ref":1,"id":4,"is_hole":true,"start_halfedge":12},{"face_ref":1,"id":5,"is_hole":true,"start_halfedge":14},{"face_ref":2,"id":6,"is_hole":false,"start_halfedge":16},{"face_ref":3,"id":7,"is_hole":false,"start_halfedge":20},{"face_ref":4,"id":8,"is_hole":false,"start_halfedge":24},{"face_ref":5,"id":9,"is_hole":false,"start_halfedge":28},{"face_ref":6,"id":10,"is_hole":false,"start_halfedge":32},{"face_ref":7,"id":11,"is_hole":false,"start_halfedge":36},{"face_ref":8,"id":12,"is_hole":false,"start_halfedge":40},{"face_ref":9,"id":13,"is_hole":false,"start_halfedge":44}],"shells":[{"faces":[0,1,2,3,4,5,6,7,8,9],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":8,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":11,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":10,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":9,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":12,"position":[4.9,0.0,1.5],"tolerance":1e-9},{"id":9,"outgoing_halfedge":13,"position":[4.1,0.0,1.5],"tolerance":1e-9},{"id":10,"outgoing_halfedge":4,"position":[4.9,0.3,1.5],"tolerance":1e-9},{"id":11,"outgoing_halfedge":5,"position":[4.1,0.3,1.5],"tolerance":1e-9},{"id":12,"outgoing_halfedge":14,"position":[1.9000000000000004,0.0,1.5],"tolerance":1e-9},{"id":13,"outgoing_halfedge":15,"position":[1.0999999999999996,0.0,1.5],"tolerance":1e-9},{"id":14,"outgoing_halfedge":6,"position":[1.9000000000000004,0.3,1.5],"tolerance":1e-9},{"id":15,"outgoing_halfedge":7,"position":[1.0999999999999996,0.3,1.5],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.two-round.reverse.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.two-round.reverse.json new file mode 100644 index 0000000..7e6c6d6 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/prismatic.two-round.reverse.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[4.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[4.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.9,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[4.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[4.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"round-b","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-b","entity":"round-b","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":0.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-b","entity":"round-b","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-b","entity":"round-b","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[4.9,0.0,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[4.9,0.3,1.5],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.3,1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"round-a","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-9,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-9,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":0.3,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-a","entity":"round-a","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.9,0.0,1.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":2,"position":[1.9,0.3,1.5],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_prismatic_profile_batch"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-0.3]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,-0.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.3,0.0],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"two-round-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-0.300000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"two-round-host","entity":"two-round-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.300000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[0.0,0.3,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[6.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.3,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[6.0,0.3,3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[6.0,0.0,3.0]},{"frame":{"origin":[4.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[4.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.9,0.0,1.5]},{"frame":{"origin":[4.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[4.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.1,0.0,1.5]},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9000000000000004,0.0,1.5]},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,0.3,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0999999999999996,0.0,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[6.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[1.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[1.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[4.5,1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[4.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-3.0]},{"direction":[-1.0,-0.0],"kind":"Line2","origin":[6.0,-3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,-0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[1.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[1.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[4.5,-1.5]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[4.5,-1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[6.0,0.3,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[6.0,0.0,3.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[4.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,0.0,1.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4}]},"id":"batch-prismatic.two-round.reverse","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":11,"id":0,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":19,"id":2,"tolerance":1e-9,"twin_halfedge":29},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":3,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":17,"id":5,"tolerance":1e-9,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":9,"id":6,"tolerance":1e-9,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":21,"id":8,"tolerance":1e-9,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":9,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":25,"id":11,"tolerance":1e-9,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":13,"id":12,"tolerance":1e-9,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":4,"id":13,"tolerance":1e-9,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":35,"id":14,"tolerance":1e-9,"twin_halfedge":37},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":12,"id":15,"tolerance":1e-9,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":5,"id":16,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":33,"id":17,"tolerance":1e-9,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":15,"id":18,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":-3.141592653589793,"lo":-6.283185307179586}},"halfedge":6,"id":19,"tolerance":1e-9,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":43,"id":20,"tolerance":1e-9,"twin_halfedge":45},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":14,"id":21,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":0.0,"lo":-3.141592653589793}},"halfedge":7,"id":22,"tolerance":1e-9,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.3,"lo":0.0}},"halfedge":41,"id":23,"tolerance":1e-9,"twin_halfedge":47}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1,2],"outer":0,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[4,5],"outer":3,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-3.000000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":6.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"two-round-host","entity":"two-round-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":3.000000001,"lo":-1e-9},{"hi":0.300000001,"lo":-1e-9}]}},{"id":6,"key":"side-1-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round-b","entity":"round-b","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":-3.141592651089793,"lo":-6.283185309679586},{"hi":0.300000001,"lo":-1e-9}]}},{"id":7,"key":"side-1-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round-b","entity":"round-b","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":2.5e-9,"lo":-3.1415926560897933},{"hi":0.300000001,"lo":-1e-9}]}},{"id":8,"key":"side-2-0","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round-a","entity":"round-a","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":-3.141592651089793,"lo":-6.283185309679586},{"hi":0.300000001,"lo":-1e-9}]}},{"id":9,"key":"side-2-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round-a","entity":"round-a","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":2.5e-9,"lo":-3.1415926560897933},{"hi":0.300000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":18,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":22,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":26,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":30,"wire_ref":null},{"edge":13,"face":0,"from":10,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":5,"to":11,"twin":34,"wire_ref":null},{"edge":16,"face":0,"from":11,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":4,"prev":4,"to":10,"twin":38,"wire_ref":null},{"edge":19,"face":0,"from":14,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":2,"next":7,"prev":7,"to":15,"twin":42,"wire_ref":null},{"edge":22,"face":0,"from":15,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":2,"next":6,"prev":6,"to":14,"twin":46,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":3,"next":9,"prev":11,"to":3,"twin":28,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":3,"next":10,"prev":8,"to":2,"twin":24,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":3,"next":11,"prev":9,"to":1,"twin":20,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":3,"next":8,"prev":10,"to":0,"twin":16,"wire_ref":null},{"edge":15,"face":1,"from":8,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":4,"next":13,"prev":13,"to":9,"twin":36,"wire_ref":null},{"edge":12,"face":1,"from":9,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":4,"next":12,"prev":12,"to":8,"twin":32,"wire_ref":null},{"edge":21,"face":1,"from":12,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":5,"next":15,"prev":15,"to":13,"twin":44,"wire_ref":null},{"edge":18,"face":1,"from":13,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":5,"next":14,"prev":14,"to":12,"twin":40,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":6,"next":17,"prev":19,"to":1,"twin":11,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":6,"next":18,"prev":16,"to":5,"twin":23,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":6,"next":19,"prev":17,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":6,"next":16,"prev":18,"to":0,"twin":29,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":7,"next":21,"prev":23,"to":2,"twin":10,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":7,"next":22,"prev":20,"to":6,"twin":27,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":7,"next":23,"prev":21,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":7,"next":20,"prev":22,"to":1,"twin":17,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":8,"next":25,"prev":27,"to":3,"twin":9,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":8,"next":26,"prev":24,"to":7,"twin":31,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":8,"next":27,"prev":25,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":8,"next":24,"prev":26,"to":2,"twin":21,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":9,"next":29,"prev":31,"to":0,"twin":8,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":9,"next":30,"prev":28,"to":4,"twin":19,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":9,"next":31,"prev":29,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":9,"next":28,"prev":30,"to":3,"twin":25,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Reverse"},"id":32,"loop_ref":10,"next":33,"prev":35,"to":9,"twin":13,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":10,"next":34,"prev":32,"to":11,"twin":39,"wire_ref":null},{"edge":13,"face":6,"from":11,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Forward"},"id":34,"loop_ref":10,"next":35,"prev":33,"to":10,"twin":4,"wire_ref":null},{"edge":14,"face":6,"from":10,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":10,"next":32,"prev":34,"to":8,"twin":37,"wire_ref":null},{"edge":15,"face":7,"from":9,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Reverse"},"id":36,"loop_ref":11,"next":37,"prev":39,"to":8,"twin":12,"wire_ref":null},{"edge":14,"face":7,"from":8,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":11,"next":38,"prev":36,"to":10,"twin":35,"wire_ref":null},{"edge":16,"face":7,"from":10,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Forward"},"id":38,"loop_ref":11,"next":39,"prev":37,"to":11,"twin":5,"wire_ref":null},{"edge":17,"face":7,"from":11,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":11,"next":36,"prev":38,"to":9,"twin":33,"wire_ref":null},{"edge":18,"face":8,"from":12,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Reverse"},"id":40,"loop_ref":12,"next":41,"prev":43,"to":13,"twin":15,"wire_ref":null},{"edge":23,"face":8,"from":13,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":12,"next":42,"prev":40,"to":15,"twin":47,"wire_ref":null},{"edge":19,"face":8,"from":15,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Forward"},"id":42,"loop_ref":12,"next":43,"prev":41,"to":14,"twin":6,"wire_ref":null},{"edge":20,"face":8,"from":14,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":12,"next":40,"prev":42,"to":12,"twin":45,"wire_ref":null},{"edge":21,"face":9,"from":13,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Reverse"},"id":44,"loop_ref":13,"next":45,"prev":47,"to":12,"twin":14,"wire_ref":null},{"edge":20,"face":9,"from":12,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":13,"next":46,"prev":44,"to":14,"twin":43,"wire_ref":null},{"edge":22,"face":9,"from":14,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Forward"},"id":46,"loop_ref":13,"next":47,"prev":45,"to":15,"twin":7,"wire_ref":null},{"edge":23,"face":9,"from":15,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":13,"next":44,"prev":46,"to":13,"twin":41,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":4},{"face_ref":0,"id":2,"is_hole":true,"start_halfedge":6},{"face_ref":1,"id":3,"is_hole":false,"start_halfedge":8},{"face_ref":1,"id":4,"is_hole":true,"start_halfedge":12},{"face_ref":1,"id":5,"is_hole":true,"start_halfedge":14},{"face_ref":2,"id":6,"is_hole":false,"start_halfedge":16},{"face_ref":3,"id":7,"is_hole":false,"start_halfedge":20},{"face_ref":4,"id":8,"is_hole":false,"start_halfedge":24},{"face_ref":5,"id":9,"is_hole":false,"start_halfedge":28},{"face_ref":6,"id":10,"is_hole":false,"start_halfedge":32},{"face_ref":7,"id":11,"is_hole":false,"start_halfedge":36},{"face_ref":8,"id":12,"is_hole":false,"start_halfedge":40},{"face_ref":9,"id":13,"is_hole":false,"start_halfedge":44}],"shells":[{"faces":[0,1,2,3,4,5,6,7,8,9],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":8,"position":[6.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":11,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":10,"position":[0.0,0.0,3.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":9,"position":[6.0,0.0,3.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[6.0,0.3,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.0,0.3,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[0.0,0.3,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[6.0,0.3,3.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":12,"position":[4.9,0.0,1.5],"tolerance":1e-9},{"id":9,"outgoing_halfedge":13,"position":[4.1,0.0,1.5],"tolerance":1e-9},{"id":10,"outgoing_halfedge":4,"position":[4.9,0.3,1.5],"tolerance":1e-9},{"id":11,"outgoing_halfedge":5,"position":[4.1,0.3,1.5],"tolerance":1e-9},{"id":12,"outgoing_halfedge":14,"position":[1.9000000000000004,0.0,1.5],"tolerance":1e-9},{"id":13,"outgoing_halfedge":15,"position":[1.0999999999999996,0.0,1.5],"tolerance":1e-9},{"id":14,"outgoing_halfedge":6,"position":[1.9000000000000004,0.3,1.5],"tolerance":1e-9},{"id":15,"outgoing_halfedge":7,"position":[1.0999999999999996,0.3,1.5],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/staged.mixed-curved-openings.forward.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/staged.mixed-curved-openings.forward.json new file mode 100644 index 0000000..08df006 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/staged.mixed-curved-openings.forward.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":6.3e-8,"intersection":1.575e-8,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.2,1.0,1.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.2,1.0,1.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.2,1.0,1.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.2,1.0,1.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.2,1.0,1.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.2,1.5,1.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.2,1.5,1.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.2,1.5,1.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.2,1.0,1.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.2,1.0,1.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.2,1.0,1.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.2,1.5,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3999999999999999,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3999999999999999]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3999999999999999]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3999999999999999,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[2.2,1.0,1.1],"x":[0.0,1.0,0.0],"y":[1.0,0.0,0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.2,1.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.2,1.0,1.1],"x":[0.0,0.0,1.0],"y":[0.0,1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.2,1.0,1.1],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.2,1.0,1.1],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.2,1.5,1.1],"x":[0.0,0.0,1.0],"y":[1.0,0.0,0.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"rectangular-opening","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":6.3e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":6.3e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":8,"id":2,"tolerance":6.3e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":3,"tolerance":6.3e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":15,"id":4,"tolerance":6.3e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":6.3e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":10,"id":6,"tolerance":6.3e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":13,"id":7,"tolerance":6.3e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":6.3e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":9,"tolerance":6.3e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":10,"tolerance":6.3e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":6.3e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.500000063,"lo":-6.3e-8},{"hi":1.000000063,"lo":-6.3e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.000000063,"lo":-6.3e-8},{"hi":0.500000063,"lo":-6.3e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4000000629999999,"lo":-6.3e-8},{"hi":0.500000063,"lo":-6.3e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.500000063,"lo":-6.3e-8},{"hi":0.4000000629999999,"lo":-6.3e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000063,"lo":-6.3e-8},{"hi":0.4000000629999999,"lo":-6.3e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.4000000629999999,"lo":-6.3e-8},{"hi":1.000000063,"lo":-6.3e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[2.2,1.0,1.1],"tolerance":6.3e-8},{"id":1,"outgoing_halfedge":3,"position":[3.2,1.0,1.1],"tolerance":6.3e-8},{"id":2,"outgoing_halfedge":1,"position":[2.2,1.5,1.1],"tolerance":6.3e-8},{"id":3,"outgoing_halfedge":2,"position":[3.2,1.5,1.1],"tolerance":6.3e-8},{"id":4,"outgoing_halfedge":4,"position":[2.2,1.0,1.5],"tolerance":6.3e-8},{"id":5,"outgoing_halfedge":5,"position":[3.2,1.0,1.5],"tolerance":6.3e-8},{"id":6,"outgoing_halfedge":7,"position":[2.2,1.5,1.5],"tolerance":6.3e-8},{"id":7,"outgoing_halfedge":6,"position":[3.2,1.5,1.5],"tolerance":6.3e-8}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":6.3e-8,"intersection":1.575e-8,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[2.65,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Circle","radius":0.5},{"frame":{"origin":[3.05,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Circle","radius":0.5},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.65,1.5,-0.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.5,0.0],"axis_b":[0.0,-0.5],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.5,0.0],"axis_b":[0.0,0.5],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[2.65,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Cylinder","radius":0.5},{"frame":{"origin":[2.65,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,-1.0,-0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.05,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"}]},"id":"round-opening","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":6.3e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":6.3e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3999999999999999,"lo":0.0}},"halfedge":1,"id":2,"tolerance":6.3e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-opening","entity":"round-opening","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":0.4,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-opening","entity":"round-opening","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.5,"lo":-0.5},{"hi":0.5,"lo":-0.5}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-opening","entity":"round-opening","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.5,"lo":-0.5},{"hi":0.5,"lo":-0.5}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[2.65,1.5,-0.5],"tolerance":6.3e-8},{"id":1,"outgoing_halfedge":2,"position":[3.05,1.5,-0.5],"tolerance":6.3e-8}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_planar_cutters","subtract_vertical_arc_extrusion","generic_two_sided_cutter_band_subtraction"],"host":{"accuracy":{"exchange":0.00001,"geometric":6.3e-8,"intersection":1.575e-8,"tessellation":0.01},"geometry":{"curves":[{"direction":[-0.6967067093471652,0.0,0.717356090899523],"kind":"Line","origin":[2.0901201280414963,0.0,-2.1520682726985685]},{"direction":[-0.6967067093471652,0.0,0.717356090899523],"kind":"Line","origin":[2.0901201280414963,3.0,-2.1520682726985685]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,0.0,-2.1520682726985685]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8811081152373468,0.0,-1.9368614454287116]},{"direction":[0.6967067093471652,0.0,0.717356090899523],"kind":"Line","origin":[1.8811081152373468,0.0,1.9368614454287116]},{"direction":[0.6967067093471652,0.0,0.717356090899523],"kind":"Line","origin":[1.8811081152373468,3.0,1.9368614454287116]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8811081152373468,0.0,1.9368614454287116]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,0.0,2.1520682726985685]}],"intersections":[],"pcurves":[{"direction":[-0.6967067093471652,-0.717356090899523],"kind":"Line2","origin":[2.0901201280414963,2.1520682726985685]},{"axis_a":[2.7,0.0],"axis_b":[0.0,2.7],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[0.6967067093471652,-0.717356090899523],"kind":"Line2","origin":[1.8811081152373468,-1.9368614454287116]},{"axis_a":[3.0,0.0],"axis_b":[0.0,3.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[3.0,0.0],"axis_b":[0.0,-3.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[0.6967067093471652,0.717356090899523],"kind":"Line2","origin":[1.8811081152373468,1.9368614454287116]},{"axis_a":[2.7,0.0],"axis_b":[0.0,-2.7],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-0.6967067093471652,0.717356090899523],"kind":"Line2","origin":[2.0901201280414963,-2.1520682726985685]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.29999999999999993,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.29999999999999993,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.8,0.0]}],"surfaces":[{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0901201280414963,0.0,-2.1520682726985685],"x":[-0.6967067093471652,0.0,0.717356090899523],"y":[0.0,1.0,0.0],"z":[-0.717356090899523,0.0,-0.6967067093471652]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.7},{"frame":{"origin":[1.8811081152373468,0.0,1.9368614454287116],"x":[0.6967067093471652,0.0,0.717356090899523],"y":[0.0,1.0,-0.0],"z":[-0.717356090899523,0.0,0.6967067093471652]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":3.0}]},"id":"mixed-curved-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":7,"id":0,"tolerance":6.3e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":0,"id":1,"tolerance":6.3e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":6.3e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.8,"lo":-0.8}},"halfedge":6,"id":3,"tolerance":6.3e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.8,"lo":-0.8}},"halfedge":1,"id":4,"tolerance":6.3e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":6.3e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":5,"id":6,"tolerance":6.3e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":2,"id":7,"tolerance":6.3e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":6.3e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.8,"lo":-0.8}},"halfedge":4,"id":9,"tolerance":6.3e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.8,"lo":-0.8}},"halfedge":3,"id":10,"tolerance":6.3e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":6.3e-8,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.000000063,"lo":-3.000000063},{"hi":3.000000063,"lo":-3.000000063}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.000000063,"lo":-3.000000063},{"hi":3.000000063,"lo":-3.000000063}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.30000006299999993,"lo":-6.3e-8},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.8000000233333334,"lo":-0.8000000233333334},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.30000006299999993,"lo":-6.3e-8},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.800000021,"lo":-0.800000021},{"hi":3.000000063,"lo":-6.3e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.0901201280414963,0.0,-2.1520682726985685],"tolerance":6.3e-8},{"id":1,"outgoing_halfedge":7,"position":[1.8811081152373468,0.0,-1.9368614454287116],"tolerance":6.3e-8},{"id":2,"outgoing_halfedge":6,"position":[1.8811081152373468,0.0,1.9368614454287116],"tolerance":6.3e-8},{"id":3,"outgoing_halfedge":5,"position":[2.0901201280414963,0.0,2.1520682726985685],"tolerance":6.3e-8},{"id":4,"outgoing_halfedge":0,"position":[2.0901201280414963,3.0,-2.1520682726985685],"tolerance":6.3e-8},{"id":5,"outgoing_halfedge":1,"position":[1.8811081152373468,3.0,-1.9368614454287116],"tolerance":6.3e-8},{"id":6,"outgoing_halfedge":2,"position":[1.8811081152373468,3.0,1.9368614454287116],"tolerance":6.3e-8},{"id":7,"outgoing_halfedge":3,"position":[2.0901201280414963,3.0,2.1520682726985685],"tolerance":6.3e-8}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":6.3e-8,"intersection":1.575e-8,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.6967067093471652,0.0,0.717356090899523],"kind":"Line","origin":[1.8811081152373468,0.0,1.9368614454287116]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"direction":[-0.6967067093471659,0.0,0.7173560908995223],"kind":"Line","origin":[2.0901201280414963,0.0,-2.152068272698568]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,1.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.465765601187591,1.0,1.1]},{"frame":{"origin":[0.0,1.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.598076211353316,1.0,1.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.244994432064365,1.5,1.5]},{"frame":{"origin":[0.0,1.5,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.7910571473905725,1.5,1.1]},{"frame":{"origin":[0.0,1.5,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"direction":[0.6967067093471659,0.0,-0.7173560908995223],"kind":"Line","origin":[1.8811081152373468,3.0,-1.9368614454287116]},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"direction":[-0.6967067093471652,0.0,-0.717356090899523],"kind":"Line","origin":[2.0901201280414963,3.0,2.1520682726985685]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.244994432064365,0.0,1.4999999999999998]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.244994432064365,1.0,1.4999999999999998]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.244994432064365,1.5,1.4999999999999998]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8811081152373468,3.0,1.9368614454287116]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8811081152373468,1.5,1.9368614454287116]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8811081152373468,1.0,1.9368614454287116]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.4657656011875906,0.0,1.1000000000000005]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8811081152373468,0.0,-1.9368614454287116]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8811081152373468,1.0,-1.9368614454287116]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8811081152373468,1.5,-1.9368614454287116]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.4657656011875906,3.0,1.1000000000000005]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.4657656011875906,1.5,1.1000000000000005]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,0.0,-2.152068272698568]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,1.0,-2.152068272698568]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,1.5,-2.152068272698568]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.791057147390573,0.0,1.0999999999999992]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.791057147390573,1.0,1.0999999999999992]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.791057147390573,1.5,1.0999999999999992]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.598076211353316,0.0,1.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,0.0,2.1520682726985685]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,1.0,2.1520682726985685]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,1.5,2.1520682726985685]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.598076211353316,3.0,1.5]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.598076211353316,1.5,1.5]},{"definition":0,"kind":"Intersection"},{"definition":1,"kind":"Intersection"}],"intersections":[{"anchors":[{"parameter":0.0,"point":[2.682255636269859,1.1069411007860208,0.309038349905685],"uv_a":[6.168475257835536,1.1069411008145305],"uv_b":[-2.2370900898141954,0.03225563626985917]},{"parameter":0.028334801114291642,"point":[2.679668824997465,1.1249999843059526,0.3307188962004861],"uv_a":[6.160388449313543,1.12499998425],"uv_b":[-2.2935305273231577,0.029668824997465038]},{"parameter":0.06261555364766803,"point":[2.676489529532718,1.148437485266038,0.35553311839962887],"uv_a":[6.151122836532611,1.148437485234375],"uv_b":[-2.361809801256655,0.02648952953271788]},{"parameter":0.06917300359337525,"point":[2.6758798017644945,1.1531099403349856,0.3600934413561505],"uv_a":[6.1494187979880905,1.15310994033352],"uv_b":[-2.3748679768681527,0.02587980176449456]},{"parameter":0.09472350578879225,"point":[2.673511927131303,1.1718749862698106,0.37727175277559466],"uv_a":[6.142996291169094,1.17187498621875],"uv_b":[-2.4257545571066568,0.0235119271313029]},{"parameter":0.12512878456108087,"point":[2.670736692480739,1.1953124871700322,0.3964410668323627],"uv_a":[6.135822511238574,1.195312487203125],"uv_b":[-2.4863205326425137,0.020736692480739283]},{"parameter":0.14995049847172123,"point":[2.66853225449655,1.2152819057274709,0.4110177693783217],"uv_a":[6.130362338140644,1.2152819057554232],"uv_b":[-2.5357728337336125,0.01853225449655005]},{"parameter":0.15417322142588208,"point":[2.668164456906109,1.2187499882008486,0.41339863435515534],"uv_a":[6.129470076501057,1.2187499881875001],"uv_b":[-2.5441862084349074,0.018164456906109017]},{"parameter":0.18210498285059248,"point":[2.6657958081649715,1.242187489148561,0.4284071768811314],"uv_a":[6.123842549443157,1.2421874891718752],"uv_b":[-2.5998556927074383,0.015795808164971614]},{"parameter":0.2091194781015368,"point":[2.6636312892736353,1.2656249901660308,0.4416654330895639],"uv_a":[6.118867069463013,1.2656249901562502],"uv_b":[-2.6537174838059925,0.013631289273635439]},{"parameter":0.23537113950850322,"point":[2.66167139832315,1.2890624911389978,0.4533269982602597],"uv_a":[6.1144873948445735,1.289062491140625],"uv_b":[-2.7060802629751683,0.011671398323150228]},{"parameter":0.256443011692048,"point":[2.660215662637239,1.3083039602163389,0.46179284131799114],"uv_a":[6.111305878293199,1.308303960231183],"uv_b":[-2.7481264156909653,0.010215662637238943]},{"parameter":0.2609875710109817,"point":[2.6599165875972526,1.3124999921208964,0.4635124022444197],"uv_a":[6.1106594428997605,1.312499992125],"uv_b":[-2.7571958620866104,0.009916587597252668]},{"parameter":0.28607223779480606,"point":[2.6583672632416975,1.33593749309167,0.47231715379922057],"uv_a":[6.107348320847363,1.335937493109375],"uv_b":[-2.8072746448894756,0.008367263241697565]},{"parameter":0.3107171775011209,"point":[2.657023784686102,1.3593749940794733,0.4798172648614164],"uv_a":[6.104526287239927,1.3593749940937498],"uv_b":[-2.8564962009681647,0.007023784686102097]},{"parameter":0.33500186015188027,"point":[2.6558864643186353,1.3828124950733056,0.4860731309880169],"uv_a":[6.102171320575353,1.3828124950781249],"uv_b":[-2.9050170325986295,0.005886464318635376]},{"parameter":0.3589972392979255,"point":[2.654955567088826,1.4062499960645936,0.4911323006820845],"uv_a":[6.100266097951611,1.4062499960625],"uv_b":[-2.9529762594057267,0.004955567088825941]},{"parameter":0.38276790371366365,"point":[2.654231310219106,1.429687497041538,0.4950314655552374],"uv_a":[6.098797261305364,1.429687497046875],"uv_b":[-3.000499988146808,0.004231310219106277]},{"parameter":0.40637377755047316,"point":[2.6537138628622183,1.4531249980248027,0.49779788480974135],"uv_a":[6.097754892221692,1.45312499803125],"uv_b":[-3.0477047745014545,0.00371386286221842]},{"parameter":0.4298715140080698,"point":[2.6534033459790627,1.4765624990156134,0.4994503814668287],"uv_a":[6.0971321448404385,1.476562499015625],"uv_b":[-3.0947004684855486,0.0034033459790627774]},{"parameter":0.45331568701695135,"point":[2.653299832265033,1.5,0.5000000000511745],"uv_a":[6.096925003564456,1.5],"uv_b":[-3.141592653589793,0.0032998322650330536]},{"parameter":0.4767598600258329,"point":[2.6534033459790627,1.5234375009843866,0.4994503814668287],"uv_a":[6.0971321448404385,1.523437500984375],"uv_b":[-3.1884848386940376,0.0034033459790627774]},{"parameter":0.5002575964834295,"point":[2.6537138628622183,1.5468750019751973,0.49779788480974135],"uv_a":[6.097754892221692,1.54687500196875],"uv_b":[-3.2354805326781317,0.00371386286221842]},{"parameter":0.523863470320239,"point":[2.654231310219106,1.570312502958462,0.4950314655552374],"uv_a":[6.098797261305364,1.570312502953125],"uv_b":[-3.2826853190327783,0.004231310219106277]},{"parameter":0.5476341347359772,"point":[2.654955567088826,1.5937500039354064,0.4911323006820845],"uv_a":[6.100266097951611,1.5937500039375],"uv_b":[-3.3302090477738595,0.004955567088825941]},{"parameter":0.5716295138820224,"point":[2.6558864643186353,1.6171875049266944,0.4860731309880169],"uv_a":[6.102171320575353,1.6171875049218751],"uv_b":[-3.3781682745809567,0.005886464318635376]},{"parameter":0.5959141965327819,"point":[2.657023784686102,1.6406250059205267,0.4798172648614164],"uv_a":[6.104526287239927,1.6406250059062502],"uv_b":[-3.4266891062114215,0.007023784686102097]},{"parameter":0.6205591362390966,"point":[2.6583672632416975,1.66406250690833,0.47231715379922057],"uv_a":[6.107348320847363,1.664062506890625],"uv_b":[-3.4759106622901106,0.008367263241697565]},{"parameter":0.645643803022921,"point":[2.6599165875972526,1.6875000078791036,0.4635124022444197],"uv_a":[6.1106594428997605,1.687500007875],"uv_b":[-3.525989445092976,0.009916587597252668]},{"parameter":0.6501883623418547,"point":[2.660215662637239,1.6916960397836611,0.46179284131799114],"uv_a":[6.111305878293199,1.691696039768817],"uv_b":[-3.535058891488621,0.010215662637238943]},{"parameter":0.6712602345253995,"point":[2.66167139832315,1.7109375088610022,0.4533269982602597],"uv_a":[6.1144873948445735,1.710937508859375],"uv_b":[-3.577105044204418,0.011671398323150228]},{"parameter":0.697511895932366,"point":[2.6636312892736353,1.7343750098339692,0.4416654330895639],"uv_a":[6.118867069463013,1.7343750098437498],"uv_b":[-3.6294678233735937,0.013631289273635439]},{"parameter":0.7245263911833103,"point":[2.6657958081649715,1.757812510851439,0.4284071768811314],"uv_a":[6.123842549443157,1.7578125108281248],"uv_b":[-3.683329614472148,0.015795808164971614]},{"parameter":0.7524581526080207,"point":[2.668164456906109,1.7812500117991514,0.41339863435515534],"uv_a":[6.129470076501057,1.7812500118124999],"uv_b":[-3.738999098744679,0.018164456906109017]},{"parameter":0.7566808755621816,"point":[2.66853225449655,1.7847180942725291,0.4110177693783217],"uv_a":[6.130362338140644,1.7847180942445768],"uv_b":[-3.7474124734459737,0.01853225449655005]},{"parameter":0.7815025894728219,"point":[2.670736692480739,1.8046875128299678,0.3964410668323627],"uv_a":[6.135822511238574,1.804687512796875],"uv_b":[-3.7968647745370725,0.020736692480739283]},{"parameter":0.8119078682451105,"point":[2.673511927131303,1.8281250137301894,0.37727175277559466],"uv_a":[6.142996291169094,1.82812501378125],"uv_b":[-3.8574307500729295,0.0235119271313029]},{"parameter":0.8374583704405275,"point":[2.6758798017644945,1.8468900596650144,0.3600934413561505],"uv_a":[6.1494187979880905,1.84689005966648],"uv_b":[-3.9083173303114336,0.02587980176449456]},{"parameter":0.8440158203862347,"point":[2.676489529532718,1.851562514733962,0.35553311839962887],"uv_a":[6.151122836532611,1.851562514765625],"uv_b":[-3.9213755059229314,0.02648952953271788]},{"parameter":0.878296572919611,"point":[2.679668824997465,1.8750000156940474,0.3307188962004861],"uv_a":[6.160388449313543,1.87500001575],"uv_b":[-3.9896547798564286,0.029668824997465038]},{"parameter":0.9066313740339027,"point":[2.682255636269859,1.8930588992139792,0.309038349905685],"uv_a":[6.168475257835536,1.8930588991854695],"uv_b":[-4.046095217365391,0.03225563626985917]},{"parameter":0.9154681118685938,"point":[2.6830490965935136,1.8984375167303162,0.3020720862093555],"uv_a":[6.171072038485683,1.898437516734375],"uv_b":[-4.063697530428579,0.03304909659351374]},{"parameter":0.9566760112783708,"point":[2.686629585655327,1.921875017694837,0.2683681603195926],"uv_a":[6.183625297356484,1.92187501771875],"uv_b":[-4.145824715609436,0.036629585655326924]},{"parameter":0.9690637587096833,"point":[2.6876574427069433,1.9283719520199973,0.2578710349665264],"uv_a":[6.187531717682981,1.9283719519789853],"uv_b":[-4.170515405258614,0.03765744270694338]},{"parameter":1.004062580127355,"point":[2.6904094928695583,1.9453125187070932,0.22736921664616086],"uv_a":[6.198874637593571,1.9453125187031248],"uv_b":[-4.240310477201132,0.04040949286955842]},{"parameter":1.0271667910844,"point":[2.6920832594763313,1.9553155784149696,0.20661007731490855],"uv_a":[6.206588177530428,1.9553155784108087],"uv_b":[-4.286401564583248,0.0420832594763314]},{"parameter":1.0625178429302367,"point":[2.6943879789281096,1.9687500197040324,0.1739925832035218],"uv_a":[6.218698924623721,1.9687500196874999],"uv_b":[-4.35696789203586,0.04438797892810964]},{"parameter":1.0823752028069469,"point":[2.6955314793972236,1.975278819574291,0.15527409180585766],"uv_a":[6.225644637377873,1.9752788195002098],"uv_b":[-4.396619305311677,0.045531479397223684]},{"parameter":1.1356481782814714,"point":[2.6980008502910633,1.9890895502642703,0.10388172037675547],"uv_a":[6.2447010972253185,1.9890895502702768],"uv_b":[-4.503101029766185,0.04800085029106338]},{"parameter":1.1517975554941897,"point":[2.698564165559826,1.9921875207400295,0.08804228733141027],"uv_a":[6.250571271133383,1.9921875206718749],"uv_b":[-4.535381530439569,0.04856416555982612]},{"parameter":1.1877571590697558,"point":[2.6994904754362534,1.99724121612939,0.05245162560341963],"uv_a":[6.263757557072764,1.9972412161754107],"uv_b":[-4.6072923654263125,0.04949047543625351]},{"parameter":1.2392826767807936,"point":[2.6999998138937857,1.9999989950868042,0.0010024836771621234],"uv_a":[6.282814016920209,1.999998995148141],"uv_b":[-4.710384011687316,0.04999981389378583]},{"parameter":1.2907975115920034,"point":[2.6995286807036782,1.9974485882753887,-0.05044702228975368],"uv_a":[6.301870476767655,1.9974485883102902],"uv_b":[-4.813454990847523,0.04952868070367833]},{"parameter":1.3287713593431787,"point":[2.6985641655431207,1.9921875206956527,-0.08804228783551027],"uv_a":[6.315799343415534,1.9921875206718749],"uv_b":[-4.889396431337886,0.04856416554312082]},{"parameter":1.3428722316668074,"point":[2.698077246952141,1.9895108073171726,-0.10187820905755031],"uv_a":[6.3209269366151,1.989510807279073],"uv_b":[-4.917582290131343,0.048077246952141106]},{"parameter":1.3960865449589484,"point":[2.6956460397094473,1.9759281157955804,-0.153272399989274],"uv_a":[6.339983396462546,1.9759281157986295],"uv_b":[-5.023949920612639,0.04564603970944736]},{"parameter":1.4180509198041897,"point":[2.694387978944502,1.9687500197454413,-0.17399258296503345],"uv_a":[6.347671689641234,1.9687500196874999],"uv_b":[-5.0678100682575336,0.044387978944502304]},{"parameter":1.45120479941553,"point":[2.6922359418385375,1.956217455224096,-0.20461093197499977],"uv_a":[6.359039856309991,1.956217455164631],"uv_b":[-5.133990066703966,0.04223594183853763]},{"parameter":1.4765068365529088,"point":[2.690409492858698,1.945312518681071,-0.227369216761379],"uv_a":[6.367495976813366,1.9453125187031248],"uv_b":[-5.184467483797146,0.040409492858698215]},{"parameter":1.5091728293897624,"point":[2.687848191674414,1.929567109430189,-0.2558751619620383],"uv_a":[6.378096316157436,1.9295671094416216],"uv_b":[-5.24960984875082,0.037848191674414]},{"parameter":1.5238945235008194,"point":[2.6866295856806124,1.921875017752155,-0.2683681601029221],"uv_a":[6.38274531690847,1.92187501771875],"uv_b":[-5.278953244732782,0.036629585680612475]},{"parameter":1.5651024229164958,"point":[2.683049096621975,1.898437516791168,-0.3020720860026923],"uv_a":[6.39529857577927,1.898437516734375],"uv_b":[-5.361080429937905,0.03304909662197497]},{"parameter":1.5714001120926695,"point":[2.682484382574458,1.8946168557621426,-0.3070464740359601],"uv_a":[6.397152776004882,1.8946168558170062],"uv_b":[-5.373625150412406,0.03248438257445807]},{"parameter":1.6022729373260531,"point":[2.679668825028625,1.8750000157566713,-0.3307188960032387],"uv_a":[6.405982164951411,1.87500001575],"uv_b":[-5.4351231805342355,0.029668825028625]},{"parameter":1.636553689865318,"point":[2.676489529566215,1.851562514797001,-0.3555331182112076],"uv_a":[6.415247777732344,1.851562514765625],"uv_b":[-5.50340245449183,0.026489529566215086]},{"parameter":1.6402429620635373,"point":[2.676146462339821,1.8489410950732177,-0.3581062860764056],"uv_a":[6.416209235852327,1.8489410950441387],"uv_b":[-5.5107490441552915,0.026146462339820875]},{"parameter":1.6686606523699568,"point":[2.6735119271668495,1.8281250137925558,-0.37727175259540324],"uv_a":[6.423374323095858,1.82812501378125],"uv_b":[-5.567347210365832,0.023511927166849578]},{"parameter":1.6990659313877927,"point":[2.6707366924618046,1.8046875127991515,-0.39644106691982683],"uv_a":[6.430548103168361,1.804687512796875],"uv_b":[-5.6279131863877705,0.02073669246180465]},{"parameter":1.720412352146336,"point":[2.6688367325081037,1.7875578286620981,-0.40903605611156013],"uv_a":[6.435265695699773,1.7875578286759928],"uv_b":[-5.670439788281108,0.018836732508103804]},{"parameter":1.7281112220715473,"point":[2.668164456886363,1.781250011769516,-0.41339863443903974],"uv_a":[6.436900537905881,1.7812500118124999],"uv_b":[-5.685778862168076,0.018164456886363034]},{"parameter":1.7560429834932776,"point":[2.665795808144511,1.7578125108233147,-0.42840717696171626],"uv_a":[6.442528064963776,1.7578125108281248],"uv_b":[-5.7414483464285295,0.015795808144511092]},{"parameter":1.7830574787412532,"point":[2.6636312892525402,1.7343750098076303,-0.4416654331671527],"uv_a":[6.447503544943923,1.7343750098437498],"uv_b":[-5.795310137515057,0.013631289252540313]},{"parameter":1.8093091400611427,"point":[2.661671398333681,1.7109375088728267,-0.4533269982238437],"uv_a":[6.4518832194913704,1.710937508859375],"uv_b":[-5.847672916512794,0.011671398333680916]},{"parameter":1.8252799228484844,"point":[2.6605578475175835,1.6963875252620246,-0.45981728976936054],"uv_a":[6.454322155547218,1.6963875252814207],"uv_b":[-5.879538094132449,0.010557847517583596]},{"parameter":1.834926323041138,"point":[2.6599165875751147,1.6875000078570217,-0.46351240231687274],"uv_a":[6.455711171507174,1.687500007875],"uv_b":[-5.898788515771685,0.009916587575114821]},{"parameter":1.8600109898219923,"point":[2.658367263219139,1.6640625068886525,-0.4723171538695391],"uv_a":[6.459022293559571,1.664062506890625],"uv_b":[-5.948867298562591,0.008367263219139165]},{"parameter":1.884655929525338,"point":[2.657023784663185,1.640625005903401,-0.4798172649298883],"uv_a":[6.461844327167008,1.6406250059062502],"uv_b":[-5.998088854629342,0.007023784663184873]},{"parameter":1.908940612173128,"point":[2.6558864642954187,1.617187504912243,-0.48607313105492644],"uv_a":[6.464199293831582,1.6171875049218751],"uv_b":[-6.046609686247884,0.005886464295418836]},{"parameter":1.9329359913162028,"point":[2.654955567065369,1.5937500039237296,-0.49113230074771436],"uv_a":[6.466104516455323,1.5937500039375],"uv_b":[-6.09456891304307,0.004955567065369149]},{"parameter":1.956706655728973,"point":[2.654231310195462,1.5703125029496365,-0.4950314656198781],"uv_a":[6.467573353101572,1.570312502953125],"uv_b":[-6.142092641772257,0.004231310195462079]},{"parameter":1.980312529562813,"point":[2.653713862838442,1.546875001969282,-0.49779788487367427],"uv_a":[6.468615722185245,1.54687500196875],"uv_b":[-6.1892974281150135,0.0037138628384418837]},{"parameter":2.0038102660059347,"point":[2.6534033459906636,1.5234375009858294,-0.49945038143594533],"uv_a":[6.469238469495507,1.523437500984375],"uv_b":[-6.236293122069564,0.00340334599066372]},{"parameter":2.0272544390138956,"point":[2.6532998323121433,1.5,-0.49999999992617605],"uv_a":[6.469445610700496,1.5],"uv_b":[-6.283185307179586,0.0032998323121433693]},{"parameter":2.0506986120218564,"point":[2.6534033459906636,1.4765624990141706,-0.49945038143594533],"uv_a":[6.469238469495507,1.476562499015625],"uv_b":[-6.330077492289608,0.00340334599066372]},{"parameter":2.0741963484649784,"point":[2.653713862838442,1.453124998030718,-0.49779788487367427],"uv_a":[6.468615722185245,1.45312499803125],"uv_b":[-6.377073186244159,0.0037138628384418837]},{"parameter":2.0978022222988186,"point":[2.654231310195462,1.4296874970503635,-0.4950314656198781],"uv_a":[6.467573353101572,1.429687497046875],"uv_b":[-6.424277972586916,0.004231310195462079]},{"parameter":2.1215728867115886,"point":[2.654955567065369,1.4062499960762704,-0.49113230074771436],"uv_a":[6.466104516455323,1.4062499960625],"uv_b":[-6.471801701316102,0.004955567065369149]},{"parameter":2.1455682658546635,"point":[2.6558864642954187,1.382812495087757,-0.48607313105492644],"uv_a":[6.464199293831582,1.3828124950781249],"uv_b":[-6.519760928111288,0.005886464295418836]},{"parameter":2.1698529485024536,"point":[2.657023784663185,1.359374994096599,-0.4798172649298883],"uv_a":[6.461844327167008,1.3593749940937498],"uv_b":[-6.568281759729831,0.007023784663184873]},{"parameter":2.1944978882057993,"point":[2.658367263219139,1.3359374931113475,-0.4723171538695391],"uv_a":[6.459022293559571,1.335937493109375],"uv_b":[-6.617503315796581,0.008367263219139165]},{"parameter":2.219582554986654,"point":[2.6599165875751147,1.3124999921429783,-0.46351240231687274],"uv_a":[6.455711171507174,1.312499992125],"uv_b":[-6.667582098587488,0.009916587575114821]},{"parameter":2.2292289551793076,"point":[2.6605578475175835,1.3036124747379754,-0.45981728976936054],"uv_a":[6.454322155547218,1.3036124747185793],"uv_b":[-6.686832520226724,0.010557847517583596]},{"parameter":2.245199737966649,"point":[2.661671398333681,1.2890624911271733,-0.4533269982238437],"uv_a":[6.4518832194913704,1.289062491140625],"uv_b":[-6.718697697846379,0.011671398333680916]},{"parameter":2.2714513992865384,"point":[2.6636312892525402,1.2656249901923697,-0.4416654331671527],"uv_a":[6.447503544943923,1.2656249901562502],"uv_b":[-6.771060476844116,0.013631289252540313]},{"parameter":2.298465894534514,"point":[2.665795808144511,1.2421874891766853,-0.42840717696171626],"uv_a":[6.442528064963776,1.2421874891718752],"uv_b":[-6.824922267930643,0.015795808144511092]},{"parameter":2.3263976559562445,"point":[2.668164456886363,1.218749988230484,-0.41339863443903974],"uv_a":[6.436900537905881,1.2187499881875001],"uv_b":[-6.880591752191097,0.018164456886363034]},{"parameter":2.334096525881456,"point":[2.6688367325081037,1.2124421713379019,-0.40903605611156013],"uv_a":[6.435265695699773,1.2124421713240072],"uv_b":[-6.8959308260780645,0.018836732508103804]},{"parameter":2.3554429466399993,"point":[2.6707366924618046,1.1953124872008485,-0.39644106691982683],"uv_a":[6.430548103168361,1.195312487203125],"uv_b":[-6.938457427971402,0.02073669246180465]},{"parameter":2.3858482256578353,"point":[2.6735119271668495,1.1718749862074442,-0.37727175259540324],"uv_a":[6.423374323095858,1.17187498621875],"uv_b":[-6.99902340399334,0.023511927166849578]},{"parameter":2.4142659159642545,"point":[2.676146462339821,1.1510589049267823,-0.3581062860764056],"uv_a":[6.416209235852327,1.1510589049558613],"uv_b":[-7.055621570203881,0.026146462339820875]},{"parameter":2.4179551881624737,"point":[2.676489529566215,1.148437485202999,-0.3555331182112076],"uv_a":[6.415247777732344,1.148437485234375],"uv_b":[-7.062968159867342,0.026489529566215086]},{"parameter":2.4522359407017382,"point":[2.679668825028625,1.1249999842433287,-0.3307188960032387],"uv_a":[6.405982164951411,1.12499998425],"uv_b":[-7.131247433824937,0.029668825028625]},{"parameter":2.483108765935122,"point":[2.682484382574458,1.1053831442378574,-0.3070464740359601],"uv_a":[6.397152776004882,1.1053831441829938],"uv_b":[-7.192745463946767,0.03248438257445807]},{"parameter":2.4894064551112955,"point":[2.683049096621975,1.101562483208832,-0.3020720860026923],"uv_a":[6.39529857577927,1.101562483265625],"uv_b":[-7.205290184421267,0.03304909662197497]},{"parameter":2.5306143545269717,"point":[2.6866295856806124,1.078124982247845,-0.2683681601029221],"uv_a":[6.38274531690847,1.07812498228125],"uv_b":[-7.28741736962639,0.036629585680612475]},{"parameter":2.545336048638029,"point":[2.687848191674414,1.070432890569811,-0.2558751619620383],"uv_a":[6.378096316157436,1.0704328905583784],"uv_b":[-7.316760765608352,0.037848191674414]},{"parameter":2.5780020414748828,"point":[2.690409492858698,1.0546874813189289,-0.22736921676137894],"uv_a":[6.367495976813366,1.054687481296875],"uv_b":[-7.381903130562026,0.040409492858698215]},{"parameter":2.6033040786122617,"point":[2.6922359418385375,1.043782544775904,-0.20461093197499977],"uv_a":[6.359039856309991,1.043782544835369],"uv_b":[-7.432380547655207,0.04223594183853763]},{"parameter":2.636457958223602,"point":[2.694387978944502,1.0312499802545585,-0.17399258296503345],"uv_a":[6.347671689641234,1.0312499803125],"uv_b":[-7.498560546101639,0.044387978944502304]},{"parameter":2.658422333068843,"point":[2.6956460397094473,1.0240718842044194,-0.15327239998927394],"uv_a":[6.339983396462546,1.02407188420137],"uv_b":[-7.542420693746534,0.04564603970944736]},{"parameter":2.711636646360984,"point":[2.698077246952141,1.0104891926828272,-0.10187820905755025],"uv_a":[6.3209269366151,1.0104891927209265],"uv_b":[-7.648788324227829,0.048077246952141106]},{"parameter":2.7257375186846122,"point":[2.6985641655431207,1.007812479304347,-0.08804228783551021],"uv_a":[6.315799343415534,1.007812479328125],"uv_b":[-7.6769741830212865,0.04856416554312082]},{"parameter":2.7637113664357873,"point":[2.6995286807036782,1.0025514117246113,-0.05044702228975368],"uv_a":[6.301870476767655,1.0025514116897098],"uv_b":[-7.752915623511649,0.04952868070367833]},{"parameter":2.8152262012469973,"point":[2.6999998138937857,1.0000010049131958,0.0010024836771621234],"uv_a":[6.282814016920209,1.000001004851859],"uv_b":[-7.855986602671856,0.04999981389378583]},{"parameter":2.866751718958035,"point":[2.6994904754362534,1.00275878387061,0.05245162560341963],"uv_a":[6.263757557072764,1.0027587838245893],"uv_b":[-7.95907824893286,0.04949047543625351]},{"parameter":2.902711322533601,"point":[2.698564165559826,1.0078124792599703,0.08804228733141027],"uv_a":[6.250571271133383,1.007812479328125],"uv_b":[-8.030989083919604,0.04856416555982612]},{"parameter":2.9188606997463196,"point":[2.6980008502910633,1.0109104497357295,0.10388172037675542],"uv_a":[6.2447010972253185,1.0109104497297228],"uv_b":[-8.063269584592987,0.04800085029106338]},{"parameter":2.972133675220844,"point":[2.6955314793972236,1.0247211804257086,0.1552740918058576],"uv_a":[6.225644637377873,1.0247211804997898],"uv_b":[-8.169751309047495,0.045531479397223684]},{"parameter":2.9919910350975543,"point":[2.6943879789281096,1.0312499802959674,0.17399258320352173],"uv_a":[6.218698924623721,1.0312499803125],"uv_b":[-8.209402722323311,0.04438797892810964]},{"parameter":3.027342086943391,"point":[2.6920832594763313,1.0446844215850304,0.20661007731490855],"uv_a":[6.206588177530428,1.0446844215891913],"uv_b":[-8.279969049775923,0.0420832594763314]},{"parameter":3.050446297900436,"point":[2.6904094928695583,1.0546874812929066,0.22736921664616086],"uv_a":[6.198874637593571,1.054687481296875],"uv_b":[-8.326060137158041,0.04040949286955842]},{"parameter":3.085445119318108,"point":[2.6876574427069433,1.0716280479800027,0.2578710349665264],"uv_a":[6.187531717682981,1.0716280480210147],"uv_b":[-8.395855209100558,0.03765744270694338]},{"parameter":3.09783286674942,"point":[2.686629585655327,1.078124982305163,0.2683681603195926],"uv_a":[6.183625297356484,1.07812498228125],"uv_b":[-8.420545898749737,0.036629585655326924]},{"parameter":3.139040766159197,"point":[2.6830490965935136,1.1015624832696838,0.3020720862093555],"uv_a":[6.171072038485683,1.101562483265625],"uv_b":[-8.502673083930594,0.03304909659351374]},{"parameter":3.147877503993888,"point":[2.682255636269859,1.1069411007860208,0.309038349905685],"uv_a":[6.168475257835536,1.1069411008145305],"uv_b":[-8.520275396993782,0.03225563626985917]}],"residual_tolerance":1.575e-8,"surfaces":[6,16],"uv_tubes":[[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-2.124209214796271,"lo":-2.406411402341082},{"hi":0.037429258814647426,"lo":0.02449520245267678}],[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.149609360283598,"lo":1.1238281092007771},{"hi":-2.1569719794561633,"lo":-2.4983683491236492},{"hi":0.03602741592695935,"lo":0.02013093860322357}],[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-2.3356934500336592,"lo":-2.4009843280911483},{"hi":0.027708985069164527,"lo":0.024660346228047914}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-2.2730948163911444,"lo":-2.527527717583665},{"hi":0.030615551030877874,"lo":0.018776177864919585}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.196484362252348,"lo":1.1707031111695272},{"hi":-2.304622606034943,"lo":-2.6074524837142277},{"hi":0.029062396432430138,"lo":0.015186223179612046}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-2.387415930460316,"lo":-2.63467743591581},{"hi":0.02514556844911775,"lo":0.014123378528171582}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-2.5189460843310227,"lo":-2.561012957837497},{"hi":0.019267849677432114,"lo":0.017428861725226952}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.2433593642210983,"lo":1.217578113138277},{"hi":-2.4328472398898455,"lo":-2.7111946612525},{"hi":0.02290175438838382,"lo":0.01105851068269681}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.2667968652054735,"lo":1.2410156141226518},{"hi":-2.49213211051033,"lo":-2.761441066003101},{"hi":0.020124845947643966,"lo":0.009302251490963087}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.2902343661898483,"lo":1.2644531151070268},{"hi":-2.5489919254676408,"lo":-2.81080582131352},{"hi":0.01755107117460586,"lo":0.007751616422179808}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-2.6219879575435745,"lo":-2.832218721122559},{"hi":0.0145828696949728,"lo":0.007304191265416371}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-2.729987522899675,"lo":-2.7753347548779006},{"hi":0.01081381271721149,"lo":0.00931843751728012}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.3371093681585986,"lo":1.3113281170757765},{"hi":-2.65703829648088,"lo":-2.907432210495206},{"hi":0.013015236308362876,"lo":0.005268614530587357}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.3605468691429734,"lo":1.3347656180601515},{"hi":-2.7088315327320975,"lo":-2.954939313125543},{"hi":0.0110542203528885,"lo":0.004336827574911162}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.3839843701273484,"lo":1.3582031190445263},{"hi":-2.759454537707235,"lo":-3.002058695859559},{"hi":0.009298425421035539,"lo":0.003611823583701934}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.4074218711117237,"lo":1.381640620028901},{"hi":-2.809098578984435,"lo":-3.048894713019921},{"hi":0.007748258778254247,"lo":0.00309377262920707}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.4308593720960987,"lo":1.4050781210132761},{"hi":-2.8579288019235642,"lo":-3.0955474456289704},{"hi":0.006404080828265268,"lo":0.002782796479666949}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.454296873080474,"lo":1.428515621997651},{"hi":-2.906090415437515,"lo":-3.1421143472107476},{"hi":0.00526620493288199,"lo":0.0026789681484427064}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.477734374064849,"lo":1.451953122982026},{"hi":-2.9537133865332663,"lo":-3.1886918564537368},{"hi":0.004334896628529705,"lo":0.002782312212751492}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.501171875049224,"lo":1.475390623966401},{"hi":-3.0009160982770595,"lo":-3.235377023798282},{"hi":0.003610373407122225,"lo":0.003092804836973606}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.5246093760335993,"lo":1.4988281249507758},{"hi":-3.047808283381304,"lo":-3.2822692089025267},{"hi":0.003610373407122225,"lo":0.003092804836973606}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.5480468770179743,"lo":1.5222656259351508},{"hi":-3.0944934507258495,"lo":-3.32947192064632},{"hi":0.004334896628529705,"lo":0.002782312212751492}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.5714843780023493,"lo":1.5457031269195258},{"hi":-3.1410709599688387,"lo":-3.3770948917420713},{"hi":0.00526620493288199,"lo":0.0026789681484427064}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.5949218789867246,"lo":1.5691406279039006},{"hi":-3.187637861550616,"lo":-3.425256505256022},{"hi":0.006404080828265268,"lo":0.002782796479666949}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.6183593799710996,"lo":1.5925781288882757},{"hi":-3.234290594159665,"lo":-3.474086728195151},{"hi":0.007748258778254247,"lo":0.00309377262920707}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.6417968809554748,"lo":1.6160156298726505},{"hi":-3.281126611320027,"lo":-3.523730769472351},{"hi":0.009298425421035539,"lo":0.003611823583701934}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.6652343819398496,"lo":1.6394531308570255},{"hi":-3.3282459940540434,"lo":-3.5743537744474887},{"hi":0.0110542203528885,"lo":0.004336827574911162}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.6886718829242247,"lo":1.6628906318414003},{"hi":-3.37575309668438,"lo":-3.6261470106987064},{"hi":0.013015236308362876,"lo":0.005268614530587357}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-3.5078505523016856,"lo":-3.553197784279911},{"hi":0.01081381271721149,"lo":0.00931843751728012}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-3.450966586057027,"lo":-3.6611973496360117},{"hi":0.0145828696949728,"lo":0.007304191265416371}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.7355468848929747,"lo":1.7097656338101501},{"hi":-3.472379485866066,"lo":-3.7341933817119455},{"hi":0.01755107117460586,"lo":0.007751616422179808}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.7589843858773497,"lo":1.733203134794525},{"hi":-3.5217442411764854,"lo":-3.791053196669256},{"hi":0.020124845947643966,"lo":0.009302251490963087}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.782421886861725,"lo":1.7566406357788997},{"hi":-3.571990645927086,"lo":-3.8503380672897407},{"hi":0.02290175438838382,"lo":0.01105851068269681}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-3.722172349342089,"lo":-3.7642392228485635},{"hi":0.019267849677432114,"lo":0.017428861725226952}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-3.648507871263776,"lo":-3.89576937671927},{"hi":0.02514556844911775,"lo":0.014123378528171582}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.8292968888304753,"lo":1.8035156377476496},{"hi":-3.6757328234653586,"lo":-3.9785627011446434},{"hi":0.029062396432430138,"lo":0.015186223179612046}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-3.755657589595921,"lo":-4.010090490788442},{"hi":0.030615551030877874,"lo":0.018776177864919585}],[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-3.882200979088438,"lo":-3.947491857145927},{"hi":0.027708985069164527,"lo":0.024660346228047914}],[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.8761718907992253,"lo":1.8503906397163996},{"hi":-3.784816958055937,"lo":-4.126213327723423},{"hi":0.03602741592695935,"lo":0.02013093860322357}],[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-3.876773904838504,"lo":-4.158976092383315},{"hi":0.037429258814647426,"lo":0.02449520245267678}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-4.010890591239015,"lo":-4.098902156554955},{"hi":0.03463601724082288,"lo":0.030668715622550025}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.9230468927679756,"lo":1.8972656416851494},{"hi":-3.8994431600668644,"lo":-4.31007908597115},{"hi":0.043790563778953295,"lo":0.025888118469887367}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-4.096443336311079,"lo":-4.219896784556971},{"hi":0.03971315681017629,"lo":0.034573871552094015}],[{"hi":6.207541000522822,"lo":6.186578894690587},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-4.030925261373579,"lo":-4.3799006210861675},{"hi":0.04591359319478849,"lo":0.0321533423817133}],[{"hi":6.207541000522822,"lo":6.186578894690587},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.148128302436899,"lo":-4.378583739347481},{"hi":0.045430792689877375,"lo":0.037061959656012444}],[{"hi":6.226597460370267,"lo":6.205635354538034},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.145268909678024,"lo":-4.498100546941084},{"hi":0.04899741783166611,"lo":0.03747382057277493}],[{"hi":6.226597460370267,"lo":6.205635354538034},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.277665065484228,"lo":-4.475922131863309},{"hi":0.047818480335451774,"lo":0.04210097798988155}],[{"hi":6.245653920217713,"lo":6.224691814385479},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.18365585640266,"lo":-4.716064478675202},{"hi":0.05293959207874277,"lo":0.040592737609544294}],[{"hi":6.264710380065159,"lo":6.243748274232924},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.438540028419418,"lo":-4.599942531786336},{"hi":0.04969079609735161,"lo":0.046874219753537893}],[{"hi":6.264710380065159,"lo":6.243748274232924},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.3915598604660815,"lo":-4.7511140353998},{"hi":0.05134309518910829,"lo":0.046711545806971344}],[{"hi":6.283766839912604,"lo":6.262804734080369},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.401109072904305,"lo":-4.916567304209324},{"hi":0.05101849080885046,"lo":0.04847179852118888}],[{"hi":6.30282329976005,"lo":6.281861193927814},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.5042420533669025,"lo":-5.019596949167937},{"hi":0.05094208027400082,"lo":0.048586414323463334}],[{"hi":6.321879759607495,"lo":6.30091765377526},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.661572109866798,"lo":-5.041279312318611},{"hi":0.05145771102479335,"lo":0.0466351352220058}],[{"hi":6.321879759607495,"lo":6.30091765377526},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.833024713750971,"lo":-4.973954007718258},{"hi":0.049538002725080243,"lo":0.04710340977018168}],[{"hi":6.340936219454941,"lo":6.319974113622705},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.704847029168753,"lo":-5.236685181575229},{"hi":0.052939661437528596,"lo":0.04078362522405987}],[{"hi":6.359992679302386,"lo":6.339030573470151},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.936229625322849,"lo":-5.1555303635473235},{"hi":0.048162161239337475,"lo":0.04187185741461219}],[{"hi":6.359992679302386,"lo":6.339030573470151},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.935450071364669,"lo":-5.26635006359683},{"hi":0.04869205315643166,"lo":0.03793186762660827}],[{"hi":6.379049139149831,"lo":6.358087033317596},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-5.033035232517605,"lo":-5.285422317983507},{"hi":0.04588883979821645,"lo":0.03675659489901939}],[{"hi":6.379049139149831,"lo":6.358087033317596},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-5.054182753889798,"lo":-5.379894578658169},{"hi":0.045532095227266645,"lo":0.03272558930584557}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-5.190923056786897,"lo":-5.337640036696706},{"hi":0.04028540366201705,"lo":0.034192373693009426}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.9230468927679756,"lo":1.8972656416851494},{"hi":-5.114698874322536,"lo":-5.525334800348151},{"hi":0.04379056379788748,"lo":0.025888118504699964}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-5.335990988988904,"lo":-5.398714591361407},{"hi":0.034178524717008774,"lo":0.03135495447942427}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-5.250629090168746,"lo":-5.558119240777895},{"hi":0.03811549766612421,"lo":0.024037709936958862}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.8761718907992253,"lo":1.8503906397163996},{"hi":-5.298564632619046,"lo":-5.63996100240702},{"hi":0.03602741595344483,"lo":0.020130938641395257}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-5.488709275164908,"lo":-5.525442223482214},{"hi":0.02717566401900351,"lo":0.025460327887032452}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-5.39755271173421,"lo":-5.680543542786913},{"hi":0.03141553268576347,"lo":0.018242856820906983}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.8292968888304753,"lo":1.8035156377476496},{"hi":-5.446215258321955,"lo":-5.749045138431647},{"hi":0.02906239657693943,"lo":0.015186223051714798}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-5.542859982601096,"lo":-5.755492992067783},{"hi":0.024536612369206345,"lo":0.01503681260070211}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-5.639761640507173,"lo":-5.716457009942011},{"hi":0.020181283751585344,"lo":0.016819905642881494}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.782421886861725,"lo":1.7566406357788997},{"hi":-5.574439893647168,"lo":-5.852787314949437},{"hi":0.022901754370066918,"lo":0.011058510660807208}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.7589843858773497,"lo":1.733203134794525},{"hi":-5.633724764255475,"lo":-5.9030337196881115},{"hi":0.02012484592845265,"lo":0.009302251468598755}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.7355468848929747,"lo":1.7097656338101501},{"hi":-5.690584579519583,"lo":-5.952398474508268},{"hi":0.017551071090259107,"lo":0.007751616495962121}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-5.783942561273484,"lo":-5.943268449371758},{"hi":0.013898499965875555,"lo":0.008330745885388957}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-5.841037250853977,"lo":-5.9372893590501565},{"hi":0.011840367402521146,"lo":0.008634067690177272}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.6886718829242247,"lo":1.6628906318414003},{"hi":-5.798630950189871,"lo":-6.049024864144405},{"hi":0.013015236287066134,"lo":0.005268614507187852}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.6652343819398496,"lo":1.6394531308570255},{"hi":-5.850424186429091,"lo":-6.096531966762842},{"hi":0.011054220331047748,"lo":0.00433682755127629}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.6417968809554748,"lo":1.6160156298726505},{"hi":-5.901047191392256,"lo":-6.14365134948497},{"hi":0.009298425398716947,"lo":0.003611823559886762}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.6183593799710996,"lo":1.5925781288882757},{"hi":-5.950691232657513,"lo":-6.190487366633442},{"hi":0.007748258755518211,"lo":0.0030937726052697734}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.5949218789867246,"lo":1.5691406279039006},{"hi":-5.999521455584698,"lo":-6.237140099230629},{"hi":0.006404080805183288,"lo":0.00278279645564794}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.5714843780023493,"lo":1.5457031269195258},{"hi":-6.047683069086743,"lo":-6.283707000800527},{"hi":0.00526620490950247,"lo":0.002678968124401493}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.5480468770179743,"lo":1.5222656259351508},{"hi":-6.095306040205912,"lo":-6.330284509978665},{"hi":0.0043348965339982115,"lo":0.002782312295107392}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.5246093760335993,"lo":1.4988281249507758},{"hi":-6.14250875184952,"lo":-6.3769696773996305},{"hi":0.003610373347704421,"lo":0.003092804955102668}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.501171875049224,"lo":1.475390623966401},{"hi":-6.189400936959542,"lo":-6.423861862509653},{"hi":0.003610373347704421,"lo":0.003092804955102668}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.477734374064849,"lo":1.451953122982026},{"hi":-6.236086104380507,"lo":-6.47106457415326},{"hi":0.0043348965339982115,"lo":0.002782312295107392}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.454296873080474,"lo":1.428515621997651},{"hi":-6.282663613558645,"lo":-6.51868754527243},{"hi":0.00526620490950247,"lo":0.002678968124401493}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.4308593720960987,"lo":1.4050781210132761},{"hi":-6.329230515128543,"lo":-6.566849158774475},{"hi":0.006404080805183288,"lo":0.00278279645564794}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.4074218711117237,"lo":1.381640620028901},{"hi":-6.375883247725731,"lo":-6.61567938170166},{"hi":0.007748258755518211,"lo":0.0030937726052697734}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.3839843701273484,"lo":1.3582031190445263},{"hi":-6.422719264874202,"lo":-6.6653234229669165},{"hi":0.009298425398716947,"lo":0.003611823559886762}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.3605468691429734,"lo":1.3347656180601515},{"hi":-6.4698386475963305,"lo":-6.715946427930081},{"hi":0.011054220331047748,"lo":0.00433682755127629}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.3371093681585986,"lo":1.3113281170757765},{"hi":-6.517345750214767,"lo":-6.7677396641693015},{"hi":0.013015236287066134,"lo":0.005268614507187852}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-6.629081255309016,"lo":-6.725333363505196},{"hi":0.011840367402521146,"lo":0.008634067690177272}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-6.623102164987414,"lo":-6.782428053085688},{"hi":0.013898499965875555,"lo":0.008330745885388957}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.2902343661898483,"lo":1.2644531151070268},{"hi":-6.613972139850905,"lo":-6.87578603483959},{"hi":0.017551071090259107,"lo":0.007751616495962121}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.2667968652054735,"lo":1.2410156141226518},{"hi":-6.663336894671061,"lo":-6.932645850103698},{"hi":0.02012484592845265,"lo":0.009302251468598755}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.2433593642210983,"lo":1.217578113138277},{"hi":-6.713583299409735,"lo":-6.991930720712005},{"hi":0.022901754370066918,"lo":0.011058510660807208}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-6.849913604417162,"lo":-6.926608973852},{"hi":0.020181283751585344,"lo":0.016819905642881494}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-6.81087762229139,"lo":-7.023510631758077},{"hi":0.024536612369206345,"lo":0.01503681260070211}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.196484362252348,"lo":1.1707031111695272},{"hi":-6.817325475927525,"lo":-7.120155356037217},{"hi":0.02906239657693943,"lo":0.015186223051714798}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-6.885827071572259,"lo":-7.168817902624962},{"hi":0.03141553268576347,"lo":0.018242856820906983}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-7.040928390876958,"lo":-7.077661339194265},{"hi":0.02717566401900351,"lo":0.025460327887032452}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.149609360283598,"lo":1.1238281092007771},{"hi":-6.9264096119521525,"lo":-7.267805981740127},{"hi":0.03602741595344483,"lo":0.020130938641395257}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-7.0082513735812775,"lo":-7.315741524190426},{"hi":0.03811549766612421,"lo":0.024037709936958862}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-7.1676560229977655,"lo":-7.230379625370269},{"hi":0.034178524717008774,"lo":0.03135495447942427}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.1027343583148477,"lo":1.0769531072320273},{"hi":-7.041035814011021,"lo":-7.451671740036637},{"hi":0.04379056379788748,"lo":0.025888118504699964}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-7.228730577662467,"lo":-7.375447557572276},{"hi":0.04028540366201705,"lo":0.034192373693009426}],[{"hi":6.379049139149831,"lo":6.358087033317596},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-7.186476035701004,"lo":-7.512187860469375},{"hi":0.045532095227266645,"lo":0.03272558930584557}],[{"hi":6.379049139149831,"lo":6.358087033317596},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-7.280948296375666,"lo":-7.533335381841567},{"hi":0.04588883979821645,"lo":0.03675659489901939}],[{"hi":6.359992679302386,"lo":6.339030573470151},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-7.300020550762342,"lo":-7.630920542994503},{"hi":0.04869205315643166,"lo":0.03793186762660827}],[{"hi":6.359992679302386,"lo":6.339030573470151},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.410840250811849,"lo":-7.630140989036324},{"hi":0.048162161239337475,"lo":0.04187185741461219}],[{"hi":6.340936219454941,"lo":6.319974113622705},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.329685432783943,"lo":-7.86152358519042},{"hi":0.052939661437528596,"lo":0.04078362522405987}],[{"hi":6.321879759607495,"lo":6.30091765377526},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.5924166066409144,"lo":-7.733345900608201},{"hi":0.049538002725080243,"lo":0.04710340977018168}],[{"hi":6.321879759607495,"lo":6.30091765377526},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.525091302040561,"lo":-7.9047985044923745},{"hi":0.05145771102479335,"lo":0.0466351352220058}],[{"hi":6.30282329976005,"lo":6.281861193927814},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.546773665191235,"lo":-8.06212856099227},{"hi":0.05094208027400082,"lo":0.048586414323463334}],[{"hi":6.283766839912604,"lo":6.262804734080369},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.649803310149848,"lo":-8.165261541454868},{"hi":0.05101849080885046,"lo":0.04847179852118888}],[{"hi":6.264710380065159,"lo":6.243748274232924},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.815256578959373,"lo":-8.174810753893091},{"hi":0.05134309518910829,"lo":0.046711545806971344}],[{"hi":6.264710380065159,"lo":6.243748274232924},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.966428082572836,"lo":-8.127830585939755},{"hi":0.04969079609735161,"lo":0.046874219753537893}],[{"hi":6.245653920217713,"lo":6.224691814385479},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.850306135683972,"lo":-8.38271475795651},{"hi":0.05293959207874277,"lo":0.040592737609544294}],[{"hi":6.226597460370267,"lo":6.205635354538034},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-8.090448482495862,"lo":-8.288705548874944},{"hi":0.047818480335451774,"lo":0.04210097798988155}],[{"hi":6.226597460370267,"lo":6.205635354538034},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-8.068270067418087,"lo":-8.421101704681147},{"hi":0.04899741783166611,"lo":0.03747382057277493}],[{"hi":6.207541000522822,"lo":6.186578894690587},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-8.187786875011687,"lo":-8.418242311922278},{"hi":0.045430792689877375,"lo":0.037061959656012444}],[{"hi":6.207541000522822,"lo":6.186578894690587},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-8.186469993273008,"lo":-8.535445352985592},{"hi":0.04591359319478849,"lo":0.0321533423817133}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-8.346473829802202,"lo":-8.469927278048093},{"hi":0.03971315681017629,"lo":0.034573871552094015}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.1027343583148477,"lo":1.0769531072320273},{"hi":-8.256291528388022,"lo":-8.666927454292308},{"hi":0.043790563778953295,"lo":0.025888118469887367}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-8.467468457804218,"lo":-8.555480023120158},{"hi":0.03463601724082288,"lo":0.030668715622550025}]]},{"anchors":[{"parameter":0.0,"point":[2.990025593501361,1.06381993428954,0.24443189280520244],"uv_a":[6.2016175906938065,1.0638199343388153],"uv_b":[-2.081583143507084,0.3400255935013612]},{"parameter":0.027960601948876673,"point":[2.9879723108865743,1.078124982260299,0.2683681601500001],"uv_a":[6.193609511793907,1.07812498228125],"uv_b":[-2.1373605912358022,0.3379723108865744]},{"parameter":0.06566350641898697,"point":[2.985032342842705,1.0994777312845825,0.2993023758744895],"uv_a":[6.183251596162853,1.0994777313317967],"uv_b":[-2.2125545136462232,0.3350323428427049]},{"parameter":0.06914134111203746,"point":[2.984753332289419,1.1015624833168893,0.30207208636967364],"uv_a":[6.182323686679556,1.101562483265625],"uv_b":[-2.2194877770635526,0.33475333228941917]},{"parameter":0.10627874720988245,"point":[2.9817151124528634,1.1249999842435154,0.33071889600382687],"uv_a":[6.172721158909179,1.12499998425],"uv_b":[-2.2935305269455717,0.33171511245286345]},{"parameter":0.138451119986034,"point":[2.9790322399577196,1.1469658845454735,0.3540719041429702],"uv_a":[6.1648856016319,1.1469658845608737],"uv_b":[-2.3576621455573745,0.3290322399577197]},{"parameter":0.14053223592357528,"point":[2.9788582043878766,1.148437485213058,0.3555331182412742],"uv_a":[6.1643950877238165,1.148437485234375],"uv_b":[-2.3618098009586244,0.3288582043878767]},{"parameter":0.1726109003025741,"point":[2.9761831302065995,1.1718749862655384,0.3772717527632513],"uv_a":[6.157094202032266,1.17187498621875],"uv_b":[-2.425754557084009,0.3261831302065996]},{"parameter":0.20299169788098603,"point":[2.973690380710923,1.1953124872401966,0.39644106703150683],"uv_a":[6.150650620365512,1.195312487203125],"uv_b":[-2.486320532996485,0.32369038071092326]},{"parameter":0.22377451989690716,"point":[2.972027308682751,1.2119959662445097,0.4087220039129997],"uv_a":[6.146519607100946,1.2119959662778652],"uv_b":[-2.527755845914946,0.3220273086827512]},{"parameter":0.23201503912643257,"point":[2.971380414731811,1.2187499882135842,0.4133986343912045],"uv_a":[6.1449458871691025,1.2187499881875001],"uv_b":[-2.5441862084965225,0.3213804147318111]},{"parameter":0.2599273283225378,"point":[2.9692536588896807,1.242187489148606,0.42840717688126084],"uv_a":[6.139893056008326,1.2421874891718752],"uv_b":[-2.5998556927076493,0.3192536588896808]},{"parameter":0.2869249884216469,"point":[2.9673105070407413,1.2656249901667684,0.44166543309173684],"uv_a":[6.135426420474502,1.2656249901562502],"uv_b":[-2.6537174838093325,0.3173105070407414]},{"parameter":0.3131624290936812,"point":[2.9655513201809924,1.2890624911441202,0.4533269982760353],"uv_a":[6.131495248472412,1.289062491140625],"uv_b":[-2.7060802629977676,0.3155513201809925]},{"parameter":0.3380244834888475,"point":[2.9640199117830357,1.3118138222699471,0.46323424153891385],"uv_a":[6.128153612569992,1.3118138222613736],"uv_b":[-2.7557150480637147,0.3140199117830358]},{"parameter":0.3387661663716,"point":[2.963976425838612,1.3124999921298923,0.46351240227393675],"uv_a":[6.128059766113292,1.312499992125],"uv_b":[-2.7571958621254273,0.3139764258386122]},{"parameter":0.3638415140017471,"point":[2.9625861179326485,1.3359374931093746,0.47231715386248857],"uv_a":[6.125088483845136,1.335937493109375],"uv_b":[-2.8072746449644437,0.31258611793264857]},{"parameter":0.38847931536185726,"point":[2.961380656429395,1.3593749940954285,0.4798172649252091],"uv_a":[6.122556360574773,1.3593749940937498],"uv_b":[-2.8564962010346706,0.31138065642939505]},{"parameter":0.41275880271272114,"point":[2.9603602671622977,1.3828124950683494,0.48607313096507004],"uv_a":[6.120443514455005,1.3828124950781249],"uv_b":[-2.905017032578237,0.3103602671622978]},{"parameter":0.4367506572602511,"point":[2.9595251415313575,1.4062499960514447,0.4911323006081817],"uv_a":[6.118734302858896,1.4062499960625],"uv_b":[-2.952976259352182,0.3095251415313576]},{"parameter":0.46051916706439316,"point":[2.9588754363665952,1.4296874970565727,0.49503146566535783],"uv_a":[6.117416661523907,1.429687497046875],"uv_b":[-3.0004999882075496,0.3088754363665953]},{"parameter":0.4841239329795401,"point":[2.9584112739474304,1.4531249980307959,0.49779788487451726],"uv_a":[6.1164816319842075,1.45312499803125],"uv_b":[-3.047704774525534,0.3084112739474305]},{"parameter":0.5076212685256527,"point":[2.958132741516926,1.4765624990160457,0.4994503814760793],"uv_a":[6.115923030013848,1.476562499015625],"uv_b":[-3.094700468487279,0.30813274151692616]},{"parameter":0.5310653968754574,"point":[2.958039891529144,1.5,0.5000000000611257],"uv_a":[6.115737227918569,1.5],"uv_b":[-3.141592653589793,0.30803989152914424]},{"parameter":0.5545095252252621,"point":[2.958132741516926,1.5234375009839543,0.4994503814760793],"uv_a":[6.115923030013848,1.523437500984375],"uv_b":[-3.188484838692307,0.30813274151692616]},{"parameter":0.5780068607713746,"point":[2.9584112739474304,1.5468750019692041,0.49779788487451726],"uv_a":[6.1164816319842075,1.54687500196875],"uv_b":[-3.2354805326540523,0.3084112739474305]},{"parameter":0.6016116266865216,"point":[2.9588754363665952,1.5703125029434273,0.49503146566535783],"uv_a":[6.117416661523907,1.570312502953125],"uv_b":[-3.2826853189720366,0.3088754363665953]},{"parameter":0.6253801364906636,"point":[2.9595251415313575,1.5937500039485553,0.4911323006081817],"uv_a":[6.118734302858896,1.5937500039375],"uv_b":[-3.3302090478274042,0.3095251415313576]},{"parameter":0.6493719910381935,"point":[2.9603602671622977,1.6171875049316506,0.48607313096507004],"uv_a":[6.120443514455005,1.6171875049218751],"uv_b":[-3.3781682746013493,0.3103602671622978]},{"parameter":0.6736514783890574,"point":[2.961380656429395,1.6406250059045715,0.4798172649252091],"uv_a":[6.122556360574773,1.6406250059062502],"uv_b":[-3.4266891061449156,0.31138065642939505]},{"parameter":0.6982892797491675,"point":[2.9625861179326485,1.6640625068906254,0.47231715386248857],"uv_a":[6.125088483845136,1.664062506890625],"uv_b":[-3.4759106622151426,0.31258611793264857]},{"parameter":0.7233646273793146,"point":[2.963976425838612,1.6875000078701077,0.46351240227393675],"uv_a":[6.128059766113292,1.687500007875],"uv_b":[-3.525989445054159,0.3139764258386122]},{"parameter":0.7241063102620672,"point":[2.9640199117830357,1.6881861777300529,0.46323424153891385],"uv_a":[6.128153612569992,1.6881861777386264],"uv_b":[-3.5274702591158715,0.3140199117830358]},{"parameter":0.7489683646572335,"point":[2.9655513201809924,1.7109375088558798,0.4533269982760353],"uv_a":[6.131495248472412,1.710937508859375],"uv_b":[-3.5771050441818186,0.3155513201809925]},{"parameter":0.7752058053292677,"point":[2.9673105070407413,1.7343750098332316,0.44166543309173684],"uv_a":[6.135426420474502,1.7343750098437498],"uv_b":[-3.6294678233702538,0.3173105070407414]},{"parameter":0.8022034654283768,"point":[2.9692536588896807,1.757812510851394,0.42840717688126084],"uv_a":[6.139893056008326,1.7578125108281248],"uv_b":[-3.683329614471937,0.3192536588896808]},{"parameter":0.8301157546244821,"point":[2.971380414731811,1.7812500117864158,0.4133986343912045],"uv_a":[6.1449458871691025,1.7812500118124999],"uv_b":[-3.7389990986830637,0.3213804147318111]},{"parameter":0.8383562738540075,"point":[2.972027308682751,1.7880040337554903,0.4087220039129997],"uv_a":[6.146519607100946,1.7880040337221348],"uv_b":[-3.75542946126464,0.3220273086827512]},{"parameter":0.8591390958699285,"point":[2.973690380710923,1.8046875127598034,0.39644106703150683],"uv_a":[6.150650620365512,1.804687512796875],"uv_b":[-3.7968647741831014,0.32369038071092326]},{"parameter":0.8895198934483405,"point":[2.9761831302065995,1.8281250137344616,0.3772717527632513],"uv_a":[6.157094202032266,1.82812501378125],"uv_b":[-3.857430750095577,0.3261831302065996]},{"parameter":0.9215985578273392,"point":[2.9788582043878766,1.851562514786942,0.3555331182412742],"uv_a":[6.1643950877238165,1.851562514765625],"uv_b":[-3.921375506220962,0.3288582043878767]},{"parameter":0.9236796737648805,"point":[2.9790322399577196,1.8530341154545265,0.3540719041429702],"uv_a":[6.1648856016319,1.8530341154391263],"uv_b":[-3.9255231616222117,0.3290322399577197]},{"parameter":0.9558520465410321,"point":[2.9817151124528634,1.8750000157564846,0.33071889600382687],"uv_a":[6.172721158909179,1.87500001575],"uv_b":[-3.9896547802340145,0.33171511245286345]},{"parameter":0.9929894526388771,"point":[2.984753332289419,1.8984375166831107,0.30207208636967364],"uv_a":[6.182323686679556,1.898437516734375],"uv_b":[-4.063697530116034,0.33475333228941917]},{"parameter":0.9964672873319276,"point":[2.985032342842705,1.9005222687154175,0.2993023758744895],"uv_a":[6.183251596162853,1.9005222686682033],"uv_b":[-4.070630793533363,0.3350323428427049]},{"parameter":1.0341701918020378,"point":[2.9879723108865743,1.921875017739701,0.2683681601500001],"uv_a":[6.193609511793907,1.92187501771875],"uv_b":[-4.1458247159437835,0.3379723108865744]},{"parameter":1.0621307937509141,"point":[2.990025593501361,1.9361800657104595,0.24443189280520253],"uv_a":[6.2016175906938065,1.9361800656611843],"uv_b":[-4.201602163672502,0.3400255935013612]},{"parameter":1.081530484277304,"point":[2.99137146463429,1.945312518754148,0.2273692164378188],"uv_a":[6.207322823072767,1.9453125187031248],"uv_b":[-4.240310477615036,0.34137146463429024]},{"parameter":1.1233060883448023,"point":[2.994010307708874,1.962706951180748,0.18947896277030496],"uv_a":[6.21998358522476,1.9627069511839812],"uv_b":[-4.323719007011324,0.3440103077088743]},{"parameter":1.1399563130081112,"point":[2.9949501800369926,1.968750019662461,0.17399258344294688],"uv_a":[6.225155215538505,1.9687500196874999],"uv_b":[-4.356967891558006,0.34495018003699274]},{"parameter":1.1815666960635112,"point":[2.9969851414200517,1.9815806660152426,0.13446212146289366],"uv_a":[6.238349579755713,1.9815806660036719],"uv_b":[-4.440113025616254,0.34698514142005177]},{"parameter":1.229214087928075,"point":[2.998707814310851,1.9921875207229818,0.08804228752506559],"uv_a":[6.253833663689513,1.9921875206718749],"uv_b":[-4.535381530052305,0.3487078143108513]},{"parameter":1.2379835399870236,"point":[2.998949091222668,1.9936553977252123,0.07939992604383014],"uv_a":[6.256715574286667,1.9936553976847358],"uv_b":[-4.552914019133949,0.34894909122266826]},{"parameter":1.293380309955671,"point":[2.999901494675918,1.99940862801749,0.024310948999184546],"uv_a":[6.275081568817621,1.999408627997067],"uv_b":[-4.663747904226072,0.349901494675918]},{"parameter":1.348478677818362,"point":[2.9998420305338556,1.9990513081448087,-0.030786228130472493],"uv_a":[6.2934475633485745,1.9990513081483483],"uv_b":[-4.774000408416548,0.34984203053385565]},{"parameter":1.403955715116119,"point":[2.998770718853752,1.9925706287723952,-0.08587302104395379],"uv_a":[6.311813557879528,1.992570628843767],"uv_b":[-4.884990754064252,0.3487707188537521]},{"parameter":1.4061594509690132,"point":[2.9987078142710573,1.9921875206055155,-0.08804228885943374],"uv_a":[6.312536951121648,1.9921875206718749],"uv_b":[-4.889396433385479,0.34870781427105735]},{"parameter":1.4605334458072101,"point":[2.9966879209893302,1.9797275224690436,-0.1409308489857392],"uv_a":[6.330179552410482,1.9797275224787612],"uv_b":[-4.998122908279489,0.3466879209893303]},{"parameter":1.4954132906747397,"point":[2.9949501800535847,1.9687500197090473,-0.17399258317463864],"uv_a":[6.341215398725307,1.9687500196874999],"uv_b":[-5.067810068675873,0.3449501800535848]},{"parameter":1.519077735033578,"point":[2.9935943394688804,1.9600076838869414,-0.19594114092896384],"uv_a":[6.348545546941436,1.9600076838350269],"uv_b":[-5.115065608224227,0.3435943394688805]},{"parameter":1.5538428470629704,"point":[2.9913714645937475,1.9453125186461344,-0.22736921691606637],"uv_a":[6.359047791464718,1.9453125187031248],"uv_b":[-5.184467484104458,0.34137146459374756]},{"parameter":1.580688630748231,"point":[2.989491017758296,1.932500341259705,-0.25088534185181033],"uv_a":[6.36691154147239,1.932500341212875],"uv_b":[-5.238033575112214,0.33949101775829593]},{"parameter":1.601203330610856,"point":[2.987972310875444,1.92187501771164,-0.26836816025607485],"uv_a":[6.372761102606741,1.92187501771875],"uv_b":[-5.278953245034719,0.3379723108754442]},{"parameter":1.6423813938726104,"point":[2.9847533323182245,1.8984375167516232,-0.3020720861369931],"uv_a":[6.384046927584258,1.898437516734375],"uv_b":[-5.361080430199728,0.3347533323182246]},{"parameter":1.6470218030402053,"point":[2.9843793399091125,1.8956261424212553,-0.30574491900756773],"uv_a":[6.385277536003343,1.8956261424735315],"uv_b":[-5.370331090604577,0.3343793399091126]},{"parameter":1.679521568305571,"point":[2.9817151124391463,1.8750000157258093,-0.330718896100445],"uv_a":[6.39364945549147,1.87500001575],"uv_b":[-5.435123180720872,0.3317151124391464]},{"parameter":1.7137739258950988,"point":[2.9788582043731306,1.8515625147560555,-0.3555331183335919],"uv_a":[6.401975526676833,1.851562514765625],"uv_b":[-5.503402454722163,0.32885820437313074]},{"parameter":1.72091629919211,"point":[2.978261030091669,1.8464661071564765,-0.3605013684048319],"uv_a":[6.403643530534297,1.8464661071772088],"uv_b":[-5.517637304153507,0.32826103009166907]},{"parameter":1.745855408194644,"point":[2.9761831302425756,1.8281250138047276,-0.37727175256023615],"uv_a":[6.409276412231548,1.82812501378125],"uv_b":[-5.567347210301307,0.3261831302425757]},{"parameter":1.776236205779676,"point":[2.973690380748728,1.8046875128283095,-0.3964410668370696],"uv_a":[6.415719993898301,1.804687512796875],"uv_b":[-5.627913186240673,0.32369038074872813]},{"parameter":1.8052570859497412,"point":[2.971380414771232,1.7812500118523045,-0.41339863420470424],"uv_a":[6.421424727094712,1.7812500118124999],"uv_b":[-5.68577886176755,0.321380414771232]},{"parameter":1.808369739931587,"point":[2.971138152013541,1.778678880365249,-0.4151362205890914],"uv_a":[6.422009525065251,1.7786788803689406],"uv_b":[-5.691985295358635,0.32113815201354123]},{"parameter":1.8331704450667723,"point":[2.9692536588719127,1.75781251082419,-0.4284071769592079],"uv_a":[6.426477558392323,1.7578125108281248],"uv_b":[-5.741448346424442,0.3192536588719128]},{"parameter":1.8601681051630032,"point":[2.967310507022423,1.7343750098077526,-0.44166543316679185],"uv_a":[6.430944193926146,1.7343750098437498],"uv_b":[-5.795310137514503,0.31731050702242314]},{"parameter":1.8864055457423028,"point":[2.965551320193207,1.7109375088711614,-0.4533269982289724],"uv_a":[6.434875365859817,1.710937508859375],"uv_b":[-5.847672916520141,0.3155513201932072]},{"parameter":1.9120090455704917,"point":[2.9639764258193875,1.6875000078487399,-0.4635124023440469],"uv_a":[6.438310848287356,1.687500007875],"uv_b":[-5.89878851580742,0.31397642581938756]},{"parameter":1.9290726021117979,"point":[2.963013108223454,1.6716003485187807,-0.4696310471481647],"uv_a":[6.440375519596205,1.6716003485384285],"uv_b":[-5.932862850106976,0.31301310822345396]},{"parameter":1.9370861248056854,"point":[2.9625861179453747,1.6640625069029968,-0.472317153818279],"uv_a":[6.441282130487092,1.664062506890625],"uv_b":[-5.948867298501852,0.31258611794537483]},{"parameter":1.961723926228562,"point":[2.9613806564094936,1.6406250058879954,-0.47981726499148236],"uv_a":[6.443814253825877,1.6406250059062502],"uv_b":[-5.998088854693556,0.31138065640949364]},{"parameter":1.98600341357655,"point":[2.9603602671421374,1.6171875049176623,-0.48607313102983524],"uv_a":[6.445927099945645,1.6171875049218751],"uv_b":[-6.046609686225586,0.3103602671421375]},{"parameter":2.009995268121205,"point":[2.959525141510987,1.5937500039372514,-0.49113230067171487],"uv_a":[6.447636311541753,1.5937500039375],"uv_b":[-6.094568912988007,0.3095251415109872]},{"parameter":2.033763777858961,"point":[2.958875436413802,1.5703125029630696,-0.4950314655214878],"uv_a":[6.448953952739905,1.570312502953125],"uv_b":[-6.142092641717985,0.308875436413802]},{"parameter":2.0573685438247455,"point":[2.9584112739267834,1.5468750019634778,-0.49779788493640886],"uv_a":[6.449888982416442,1.54687500196875],"uv_b":[-6.1892974281383335,0.30841127392678347]},{"parameter":2.0808658793679835,"point":[2.9581327414962093,1.5234375009810819,-0.49945038153756327],"uv_a":[6.450447584386803,1.523437500984375],"uv_b":[-6.236293122088576,0.3081327414962094]},{"parameter":2.1043100077124057,"point":[2.958039891542615,1.5,-0.5000000000212772],"uv_a":[6.450633386413662,1.5],"uv_b":[-6.283185307179586,0.30803989154261524]},{"parameter":2.127754136056828,"point":[2.9581327414962093,1.4765624990189181,-0.49945038153756327],"uv_a":[6.450447584386803,1.476562499015625],"uv_b":[-6.330077492270597,0.3081327414962094]},{"parameter":2.151251471600066,"point":[2.9584112739267834,1.4531249980365222,-0.49779788493640886],"uv_a":[6.449888982416442,1.45312499803125],"uv_b":[-6.377073186220839,0.30841127392678347]},{"parameter":2.1748562375658507,"point":[2.958875436413802,1.4296874970369304,-0.4950314655214878],"uv_a":[6.448953952739905,1.429687497046875],"uv_b":[-6.424277972641187,0.308875436413802]},{"parameter":2.1986247473036062,"point":[2.959525141510987,1.4062499960627486,-0.49113230067171487],"uv_a":[6.447636311541753,1.4062499960625],"uv_b":[-6.471801701371166,0.3095251415109872]},{"parameter":2.2226166018482614,"point":[2.9603602671421374,1.3828124950823377,-0.48607313102983524],"uv_a":[6.445927099945645,1.3828124950781249],"uv_b":[-6.519760928133587,0.3103602671421375]},{"parameter":2.2468960891962495,"point":[2.9613806564094936,1.3593749941120046,-0.47981726499148236],"uv_a":[6.443814253825877,1.3593749940937498],"uv_b":[-6.5682817596656164,0.31138065640949364]},{"parameter":2.271533890619126,"point":[2.9625861179453747,1.3359374930970032,-0.472317153818279],"uv_a":[6.441282130487092,1.335937493109375],"uv_b":[-6.617503315857321,0.31258611794537483]},{"parameter":2.279547413313013,"point":[2.963013108223454,1.3283996514812193,-0.4696310471481647],"uv_a":[6.440375519596205,1.3283996514615715],"uv_b":[-6.6335077642521965,0.31301310822345396]},{"parameter":2.2966109698543193,"point":[2.9639764258193875,1.3124999921512601,-0.4635124023440469],"uv_a":[6.438310848287356,1.312499992125],"uv_b":[-6.667582098551753,0.31397642581938756]},{"parameter":2.322214469682508,"point":[2.965551320193207,1.2890624911288386,-0.4533269982289724],"uv_a":[6.434875365859817,1.289062491140625],"uv_b":[-6.718697697839032,0.3155513201932072]},{"parameter":2.3484519102618076,"point":[2.967310507022423,1.2656249901922474,-0.44166543316679185],"uv_a":[6.430944193926146,1.2656249901562502],"uv_b":[-6.77106047684467,0.31731050702242314]},{"parameter":2.3754495703580383,"point":[2.9692536588719127,1.24218748917581,-0.4284071769592079],"uv_a":[6.426477558392323,1.2421874891718752],"uv_b":[-6.82492226793473,0.3192536588719128]},{"parameter":2.4002502754932236,"point":[2.971138152013541,1.221321119634751,-0.4151362205890914],"uv_a":[6.422009525065251,1.2213211196310594],"uv_b":[-6.874385319000537,0.32113815201354123]},{"parameter":2.4033629294750694,"point":[2.971380414771232,1.2187499881476955,-0.41339863420470424],"uv_a":[6.421424727094712,1.2187499881875001],"uv_b":[-6.880591752591623,0.321380414771232]},{"parameter":2.4323838096451347,"point":[2.973690380748728,1.1953124871716905,-0.3964410668370696],"uv_a":[6.415719993898301,1.195312487203125],"uv_b":[-6.938457428118499,0.32369038074872813]},{"parameter":2.4627646072301665,"point":[2.9761831302425756,1.1718749861952724,-0.37727175256023615],"uv_a":[6.409276412231548,1.17187498621875],"uv_b":[-6.999023404057866,0.3261831302425757]},{"parameter":2.4877037162327005,"point":[2.978261030091669,1.1535338928435235,-0.3605013684048319],"uv_a":[6.403643530534297,1.1535338928227912],"uv_b":[-7.048733310205665,0.32826103009166907]},{"parameter":2.494846089529712,"point":[2.9788582043731306,1.1484374852439445,-0.3555331183335919],"uv_a":[6.401975526676833,1.148437485234375],"uv_b":[-7.062968159637009,0.32885820437313074]},{"parameter":2.5290984471192397,"point":[2.9817151124391463,1.1249999842741907,-0.330718896100445],"uv_a":[6.39364945549147,1.12499998425],"uv_b":[-7.1312474336383005,0.3317151124391464]},{"parameter":2.5615982123846055,"point":[2.9843793399091125,1.1043738575787447,-0.30574491900756773],"uv_a":[6.385277536003343,1.1043738575264685],"uv_b":[-7.196039523754595,0.3343793399091126]},{"parameter":2.5662386215522,"point":[2.9847533323182245,1.1015624832483768,-0.3020720861369931],"uv_a":[6.384046927584258,1.101562483265625],"uv_b":[-7.205290184159445,0.3347533323182246]},{"parameter":2.6074166848139546,"point":[2.987972310875444,1.07812498228836,-0.26836816025607485],"uv_a":[6.372761102606741,1.07812498228125],"uv_b":[-7.287417369324453,0.3379723108754442]},{"parameter":2.6279313846765793,"point":[2.989491017758296,1.067499658740295,-0.25088534185181033],"uv_a":[6.36691154147239,1.067499658787125],"uv_b":[-7.328337039246959,0.33949101775829593]},{"parameter":2.65477716836184,"point":[2.9913714645937475,1.0546874813538654,-0.22736921691606635],"uv_a":[6.359047791464718,1.054687481296875],"uv_b":[-7.381903130254716,0.34137146459374756]},{"parameter":2.6895422803912323,"point":[2.9935943394688804,1.0399923161130586,-0.19594114092896384],"uv_a":[6.348545546941436,1.0399923161649731],"uv_b":[-7.4513050061349455,0.3435943394688805]},{"parameter":2.7132067247500706,"point":[2.9949501800535847,1.0312499802909525,-0.1739925831746386],"uv_a":[6.341215398725307,1.0312499803125],"uv_b":[-7.498560545683301,0.3449501800535848]},{"parameter":2.7480865696176004,"point":[2.9966879209893302,1.020272477530956,-0.14093084898573915],"uv_a":[6.330179552410482,1.0202724775212384],"uv_b":[-7.568247706079684,0.3466879209893303]},{"parameter":2.802460564455797,"point":[2.9987078142710573,1.0078124793944845,-0.08804228885943374],"uv_a":[6.312536951121648,1.007812479328125],"uv_b":[-7.676974180973693,0.34870781427105735]},{"parameter":2.8046643003086915,"point":[2.998770718853752,1.0074293712276043,-0.08587302104395379],"uv_a":[6.311813557879528,1.0074293711562325],"uv_b":[-7.68137986029492,0.3487707188537521]},{"parameter":2.8601413376064486,"point":[2.9998420305338556,1.0009486918551913,-0.030786228130472493],"uv_a":[6.2934475633485745,1.0009486918516517],"uv_b":[-7.792370205942625,0.34984203053385565]},{"parameter":2.9152397054691397,"point":[2.999901494675918,1.00059137198251,0.024310948999184546],"uv_a":[6.275081568817621,1.000591372002933],"uv_b":[-7.902622710133101,0.349901494675918]},{"parameter":2.970636475437787,"point":[2.998949091222668,1.0063446022747877,0.07939992604383014],"uv_a":[6.256715574286667,1.0063446023152642],"uv_b":[-8.013456595225223,0.34894909122266826]},{"parameter":2.9794059274967357,"point":[2.998707814310851,1.007812479277018,0.08804228752506559],"uv_a":[6.253833663689513,1.007812479328125],"uv_b":[-8.030989084306867,0.3487078143108513]},{"parameter":3.0270533193612996,"point":[2.9969851414200517,1.0184193339847571,0.13446212146289355],"uv_a":[6.238349579755713,1.0184193339963277],"uv_b":[-8.126257588742918,0.34698514142005177]},{"parameter":3.0686637024167,"point":[2.9949501800369926,1.031249980337539,0.17399258344294682],"uv_a":[6.225155215538505,1.0312499803125],"uv_b":[-8.209402722801165,0.34495018003699274]},{"parameter":3.085313927080009,"point":[2.994010307708874,1.037293048819252,0.18947896277030496],"uv_a":[6.21998358522476,1.0372930488160188],"uv_b":[-8.242651607347849,0.3440103077088743]},{"parameter":3.127089531147507,"point":[2.99137146463429,1.054687481245852,0.2273692164378188],"uv_a":[6.207322823072767,1.054687481296875],"uv_b":[-8.326060136744136,0.34137146463429024]},{"parameter":3.146489221673897,"point":[2.990025593501361,1.06381993428954,0.24443189280520244],"uv_a":[6.2016175906938065,1.0638199343388153],"uv_b":[-8.36476845068667,0.3400255935013612]}],"residual_tolerance":1.575e-8,"surfaces":[8,16],"uv_tubes":[[{"hi":6.202535890420376,"lo":6.182333296436283},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-1.970028248049648,"lo":-2.2489154866932384},{"hi":0.3441321587309347,"lo":0.3338657456570009}],[{"hi":6.202535890420376,"lo":6.182333296436283},{"hi":1.1027343583148477,"lo":1.0769531072320273},{"hi":-1.9869727464149602,"lo":-2.3629423584670652},{"hi":0.3438522469743135,"lo":0.3291524067549658}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.1027343583148477,"lo":1.0769531072320273},{"hi":-2.1986879868115645,"lo":-2.2333543038982113},{"hi":0.3355903639492763,"lo":0.33419531118284773}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-2.0714022772995144,"lo":-2.44161602670961},{"hi":0.3408297719625306,"lo":0.325638672779752}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.149609360283598,"lo":1.1238281092007771},{"hi":-2.165267289721966,"lo":-2.48592538278098},{"hi":0.33708085744315097,"lo":0.32366649496743216}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.149609360283598,"lo":1.1238281092007771},{"hi":-2.349366834754875,"lo":-2.370105111761124},{"hi":0.3293803110974056,"lo":0.3285101332481908}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-2.233920288707855,"lo":-2.5536440693347786},{"hi":0.3342083527504309,"lo":0.3208329818440454}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.196484362252348,"lo":1.1707031111695272},{"hi":-2.3046226052590577,"lo":-2.607452484821436},{"hi":0.3311686291979523,"lo":0.31870488171957057}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-2.4034499071595623,"lo":-2.6106264717518686},{"hi":0.3270165247672674,"lo":0.3187011646264071}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-2.494895120751793,"lo":-2.5770469336596755},{"hi":0.3233210965846314,"lo":0.32008662682993094}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.2433593642210983,"lo":1.217578113138277},{"hi":-2.432847240074269,"lo":-2.7111946611299027},{"hi":0.3256339264160717,"lo":0.31500014720542024}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.2667968652054735,"lo":1.2410156141226518},{"hi":-2.492132110504283,"lo":-2.761441066012699},{"hi":0.3231399625875597,"lo":0.3134242033428625}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.2902343661898483,"lo":1.2644531151070268},{"hi":-2.5489919254324622,"lo":-2.810805821374638},{"hi":0.3208288807602391,"lo":0.3120329464614948}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-2.6068106928658734,"lo":-2.854984618195609},{"hi":0.318614136976906,"lo":0.3109570949871223}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-2.7527534199402894,"lo":-2.7601574902488526},{"hi":0.314106883671883,"lo":0.313889453949765}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.3371093681585986,"lo":1.3113281170757765},{"hi":-2.6570382964473946,"lo":-2.9074322106424764},{"hi":0.3167570416505394,"lo":0.30980550212072133}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.3605468691429734,"lo":1.3347656180601515},{"hi":-2.70883153282399,"lo":-2.9549393131751245},{"hi":0.3149970409391556,"lo":0.308969733422888}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.3839843701273484,"lo":1.3582031190445263},{"hi":-2.759454537947538,"lo":-3.0020586956653696},{"hi":0.31342143496358954,"lo":0.3083194886281033}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.4074218711117237,"lo":1.381640620028901},{"hi":-2.809098579030347,"lo":-3.048894712900072},{"hi":0.3120305184241783,"lo":0.3078548902694771}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.4308593720960987,"lo":1.4050781210132761},{"hi":-2.857928801641447,"lo":-3.095547445918285},{"hi":0.3108245518608821,"lo":0.3075760260370708}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.454296873080474,"lo":1.428515621997651},{"hi":-2.906090415571581,"lo":-3.1421143471615025},{"hi":0.3098037612049249,"lo":0.3074829491091009}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.477734374064849,"lo":1.451953122982026},{"hi":-2.9537133866020433,"lo":-3.18869185641077},{"hi":0.3089683388084392,"lo":0.30757567665591745}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.501171875049224,"lo":1.475390623966401},{"hi":-3.0009160982822514,"lo":-3.235377023794821},{"hi":0.30831844149249,"lo":0.3078541915535804}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.5246093760335993,"lo":1.4988281249507758},{"hi":-3.0478082833847653,"lo":-3.282269208897335},{"hi":0.30831844149249,"lo":0.3078541915535804}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.5480468770179743,"lo":1.5222656259351508},{"hi":-3.0944934507688164,"lo":-3.329471920577543},{"hi":0.3089683388084392,"lo":0.30757567665591745}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.5714843780023493,"lo":1.5457031269195258},{"hi":-3.1410709600180837,"lo":-3.3770948916080052},{"hi":0.3098037612049249,"lo":0.3074829491091009}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.5949218789867246,"lo":1.5691406279039006},{"hi":-3.1876378612613014,"lo":-3.4252565055381394},{"hi":0.3108245518608821,"lo":0.3075760260370708}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.6183593799710996,"lo":1.5925781288882757},{"hi":-3.234290594279514,"lo":-3.4740867281492394},{"hi":0.3120305184241783,"lo":0.3078548902694771}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.6417968809554748,"lo":1.6160156298726505},{"hi":-3.2811266115142166,"lo":-3.5237307692320483},{"hi":0.31342143496358954,"lo":0.3083194886281033}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.6652343819398496,"lo":1.6394531308570255},{"hi":-3.3282459940044617,"lo":-3.5743537743555964},{"hi":0.3149970409391556,"lo":0.308969733422888}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.6886718829242247,"lo":1.6628906318414003},{"hi":-3.37575309653711,"lo":-3.6261470107321916},{"hi":0.3167570416505394,"lo":0.30980550212072133}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-3.5230278169307336,"lo":-3.530431887239297},{"hi":0.314106883671883,"lo":0.313889453949765}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-3.4282006889839773,"lo":-3.676374614313713},{"hi":0.318614136976906,"lo":0.3109570949871223}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.7355468848929747,"lo":1.7097656338101501},{"hi":-3.4723794858049484,"lo":-3.734193381747124},{"hi":0.3208288807602391,"lo":0.3120329464614948}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.7589843858773497,"lo":1.733203134794525},{"hi":-3.5217442411668873,"lo":-3.7910531966753034},{"hi":0.3231399625875597,"lo":0.3134242033428625}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.782421886861725,"lo":1.7566406357788997},{"hi":-3.5719906460496835,"lo":-3.850338067105317},{"hi":0.3256339264160717,"lo":0.31500014720542024}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-3.7061383735199107,"lo":-3.788290186427793},{"hi":0.3233210965846314,"lo":0.32008662682993094}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-3.6725588354277177,"lo":-3.879735400020024},{"hi":0.3270165247672674,"lo":0.3187011646264071}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.8292968888304753,"lo":1.8035156377476496},{"hi":-3.67573282235815,"lo":-3.9785627019205285},{"hi":0.3311686291979523,"lo":0.31870488171957057}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-3.7295412378448076,"lo":-4.049265018471731},{"hi":0.3342083527504309,"lo":0.3208329818440454}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.8761718907992253,"lo":1.8503906397163996},{"hi":-3.913080195418462,"lo":-3.9338184724247114},{"hi":0.3293803110974056,"lo":0.3285101332481908}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.8761718907992253,"lo":1.8503906397163996},{"hi":-3.797259924398606,"lo":-4.117918017457621},{"hi":0.33708085744315097,"lo":0.32366649496743216}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-3.8415692804699755,"lo":-4.211783029880073},{"hi":0.3408297719625306,"lo":0.325638672779752}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.9230468927679756,"lo":1.8972656416851494},{"hi":-4.049831003281376,"lo":-4.084497320368021},{"hi":0.3355903639492763,"lo":0.33419531118284773}],[{"hi":6.202535890420376,"lo":6.182333296436283},{"hi":1.9230468927679756,"lo":1.8972656416851494},{"hi":-3.920242948712522,"lo":-4.296212560764625},{"hi":0.3438522469743135,"lo":0.3291524067549658}],[{"hi":6.202535890420376,"lo":6.182333296436283},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-4.034269820486347,"lo":-4.313157059129938},{"hi":0.3441321587309347,"lo":0.3338657456570009}],[{"hi":6.2209018849513305,"lo":6.200699290967236},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-4.124185535787432,"lo":-4.317727105500106},{"hi":0.34406320690014836,"lo":0.3373338512355031}],[{"hi":6.2209018849513305,"lo":6.200699290967236},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.073493418822462,"lo":-4.490536065803898},{"hi":0.34928799385804243,"lo":0.3360937784851221}],[{"hi":6.239267879482283,"lo":6.21906528549819},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.257221237917959,"lo":-4.423465660651371},{"hi":0.3468299246932296,"lo":0.34213056305263745}],[{"hi":6.239267879482283,"lo":6.21906528549819},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.19067762344151,"lo":-4.60640329373275},{"hi":0.35105506418616983,"lo":0.3408802572708747}],[{"hi":6.257633874013237,"lo":6.237431280029143},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.249576016744152,"lo":-4.725918538924407},{"hi":0.35215316009245035,"lo":0.3435397956384527}],[{"hi":6.257633874013237,"lo":6.237431280029143},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.500316551889018,"lo":-4.5879789972972365},{"hi":0.3494316450463022,"lo":0.34822526048721736}],[{"hi":6.275999868544191,"lo":6.255797274560097},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.331246248949704,"lo":-4.885415674410317},{"hi":0.35180630158241755,"lo":0.34704428431616874}],[{"hi":6.294365863075145,"lo":6.2741632690910505},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.4432428958451196,"lo":-4.9945054167975},{"hi":0.35002042296004277,"lo":0.3497231022497309}],[{"hi":6.3127318576060985,"lo":6.292529263622004},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.552019717121139,"lo":-5.106971445359661},{"hi":0.35198465389406275,"lo":0.346628095493545}],[{"hi":6.331097852137052,"lo":6.310895258152958},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.876179395421798,"lo":-4.898207792027933},{"hi":0.3488965280191416,"lo":0.34858200510566784}],[{"hi":6.331097852137052,"lo":6.310895258152958},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.671943483597459,"lo":-5.21557585806751},{"hi":0.3527476008345114,"lo":0.34264813442587627}],[{"hi":6.349463846668006,"lo":6.329261252683912},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.858748587486723,"lo":-5.207184389468639},{"hi":0.35016340286082137,"lo":0.34147469818209375}],[{"hi":6.349463846668006,"lo":6.329261252683912},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.973298989579164,"lo":-5.2095766873209355},{"hi":0.34766186122299336,"lo":0.34088265829947195}],[{"hi":6.36782984119896,"lo":6.347627247214866},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.976261856463766,"lo":-5.323271235864919},{"hi":0.3480400892191464,"lo":0.33692571484348166}],[{"hi":6.36782984119896,"lo":6.347627247214866},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-5.077335302088946,"lo":-5.345165757127726},{"hi":0.34513235826465083,"lo":0.33573012408739267}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-5.156194235267202,"lo":-5.36079258487973},{"hi":0.34252843152399937,"lo":0.3349348971097408}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.9230468927679756,"lo":1.8972656416851494},{"hi":-5.114698874704702,"lo":-5.525334800529745},{"hi":0.3444102679898835,"lo":0.3283153752037853}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-5.342579109390028,"lo":-5.388832411414277},{"hi":0.3355013171364485,"lo":0.3336313550908887}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-5.240746910371988,"lo":-5.564707360953461},{"hi":0.339707794849045,"lo":0.32638665749921403}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.8761718907992253,"lo":1.8503906397163996},{"hi":-5.298564632718289,"lo":-5.639961002724746},{"hi":0.3374289285711778,"lo":0.32314438824109937}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-5.474932755859475,"lo":-5.546107003016195},{"hi":0.33005255293605407,"lo":0.32706668152874574}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-5.4182174918579085,"lo":-5.666767022596906},{"hi":0.33241682978985576,"lo":0.32202733054438903}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.8292968888304753,"lo":1.8035156377476496},{"hi":-5.4462152584225745,"lo":-5.749045138119405},{"hi":0.3311686292302709,"lo":0.31870488176103295}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-5.51218183518692,"lo":-5.801510212821303},{"hi":0.32831031270372035,"lo":0.3167604828162398}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.782421886861725,"lo":1.7566406357788997},{"hi":-5.673365994585379,"lo":-5.704398162540806},{"hi":0.3218649402866136,"lo":0.32065362649815965}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.782421886861725,"lo":1.7566406357788997},{"hi":-5.593059193227021,"lo":-5.840374448556056},{"hi":0.32490713829679807,"lo":0.315484672588656}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.7589843858773497,"lo":1.733203134794525},{"hi":-5.633724764244321,"lo":-5.903033719694624},{"hi":0.32313996257089217,"lo":0.3134242033234438}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.7355468848929747,"lo":1.7097656338101501},{"hi":-5.690584579503226,"lo":-5.952398474531417},{"hi":0.320828880680855,"lo":0.3120329465347753}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-5.7454417179455834,"lo":-6.001019714381977},{"hi":0.31870110894084647,"lo":0.3108266370717483}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.6886718829242247,"lo":1.6628906318414003},{"hi":-5.830639847208307,"lo":-6.001011518706089},{"hi":0.31590306101125476,"lo":0.31108647303158676}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.6886718829242247,"lo":1.6628906318414003},{"hi":-5.900853953317225,"lo":-5.980876195291603},{"hi":0.3138670887796122,"lo":0.31173213738921657}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.6652343819398496,"lo":1.6394531308570255},{"hi":-5.850424186118443,"lo":-6.096531967076965},{"hi":0.3149970410171372,"lo":0.30896973333773126}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.6417968809554748,"lo":1.6160156298726505},{"hi":-5.901047191629496,"lo":-6.143651349289645},{"hi":0.31342143494420593,"lo":0.3083194886074252}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.6183593799710996,"lo":1.5925781288882757},{"hi":-5.950691232700744,"lo":-6.190487366512849},{"hi":0.3120305184044381,"lo":0.3078548902486866}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.5949218789867246,"lo":1.5691406279039006},{"hi":-5.99952145552805,"lo":-6.237140099177942},{"hi":0.3108245517053576,"lo":0.3075760262194316}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.5714843780023493,"lo":1.5457031269195258},{"hi":-6.047683068877289,"lo":-6.28370700097903},{"hi":0.30980376138783905,"lo":0.3074829489527464}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.5480468770179743,"lo":1.5222656259351508},{"hi":-6.095306040237849,"lo":-6.33028450998906},{"hi":0.3089683387879316,"lo":0.30757567663506125}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.5246093760335993,"lo":1.4988281249507758},{"hi":-6.142508751906554,"lo":-6.376969677361608},{"hi":0.3083184414033977,"lo":0.30785419163542693}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.501171875049224,"lo":1.475390623966401},{"hi":-6.189400936997565,"lo":-6.423861862452618},{"hi":0.3083184414033977,"lo":0.30785419163542693}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.477734374064849,"lo":1.451953122982026},{"hi":-6.236086104370113,"lo":-6.471064574121323},{"hi":0.3089683387879316,"lo":0.30757567663506125}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.454296873080474,"lo":1.428515621997651},{"hi":-6.282663613380143,"lo":-6.518687545481884},{"hi":0.30980376138783905,"lo":0.3074829489527464}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.4308593720960987,"lo":1.4050781210132761},{"hi":-6.32923051518123,"lo":-6.566849158831123},{"hi":0.3108245517053576,"lo":0.3075760262194316}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.4074218711117237,"lo":1.381640620028901},{"hi":-6.375883247846324,"lo":-6.6156793816584285},{"hi":0.3120305184044381,"lo":0.3078548902486866}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.3839843701273484,"lo":1.3582031190445263},{"hi":-6.422719265069527,"lo":-6.665323422729676},{"hi":0.31342143494420593,"lo":0.3083194886074252}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.3605468691429734,"lo":1.3347656180601515},{"hi":-6.469838647282208,"lo":-6.71594642824073},{"hi":0.3149970410171372,"lo":0.30896973333773126}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.3371093681585986,"lo":1.3113281170757765},{"hi":-6.58549441906757,"lo":-6.665516661041948},{"hi":0.3138670887796122,"lo":0.31173213738921657}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.3371093681585986,"lo":1.3113281170757765},{"hi":-6.565359095653084,"lo":-6.735730767150866},{"hi":0.31590306101125476,"lo":0.31108647303158676}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-6.5653508999771955,"lo":-6.820928896413589},{"hi":0.31870110894084647,"lo":0.3108266370717483}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.2902343661898483,"lo":1.2644531151070268},{"hi":-6.613972139827755,"lo":-6.875786034855946},{"hi":0.320828880680855,"lo":0.3120329465347753}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.2667968652054735,"lo":1.2410156141226518},{"hi":-6.663336894664549,"lo":-6.932645850114851},{"hi":0.32313996257089217,"lo":0.3134242033234438}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.2433593642210983,"lo":1.217578113138277},{"hi":-6.725996165803116,"lo":-6.973311421132151},{"hi":0.32490713829679807,"lo":0.315484672588656}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.2433593642210983,"lo":1.217578113138277},{"hi":-6.8619724518183665,"lo":-6.8930046197737935},{"hi":0.3218649402866136,"lo":0.32065362649815965}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-6.764860401537869,"lo":-7.054188779172253},{"hi":0.32831031270372035,"lo":0.3167604828162398}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.196484362252348,"lo":1.1707031111695272},{"hi":-6.817325476239767,"lo":-7.120155355936598},{"hi":0.3311686292302709,"lo":0.31870488176103295}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-6.899603591762267,"lo":-7.148153122501264},{"hi":0.33241682978985576,"lo":0.32202733054438903}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-7.020263611342977,"lo":-7.091437858499697},{"hi":0.33005255293605407,"lo":0.32706668152874574}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.149609360283598,"lo":1.1238281092007771},{"hi":-6.926409611634426,"lo":-7.267805981640883},{"hi":0.3374289285711778,"lo":0.32314438824109937}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-7.001663253405711,"lo":-7.325623703987184},{"hi":0.339707794849045,"lo":0.32638665749921403}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-7.177538202944896,"lo":-7.223791504969144},{"hi":0.3355013171364485,"lo":0.3336313550908887}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.1027343583148477,"lo":1.0769531072320273},{"hi":-7.041035813829428,"lo":-7.45167173965447},{"hi":0.3444102679898835,"lo":0.3283153752037853}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-7.205578029479442,"lo":-7.41017637909197},{"hi":0.34252843152399937,"lo":0.3349348971097408}],[{"hi":6.36782984119896,"lo":6.347627247214866},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-7.221204857231445,"lo":-7.48903531227023},{"hi":0.34513235826465083,"lo":0.33573012408739267}],[{"hi":6.36782984119896,"lo":6.347627247214866},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-7.243099378494256,"lo":-7.590108757895405},{"hi":0.3480400892191464,"lo":0.33692571484348166}],[{"hi":6.349463846668006,"lo":6.329261252683912},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-7.356793927038235,"lo":-7.593071624780011},{"hi":0.34766186122299336,"lo":0.34088265829947195}],[{"hi":6.349463846668006,"lo":6.329261252683912},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.359186224890534,"lo":-7.707622026872451},{"hi":0.35016340286082137,"lo":0.34147469818209375}],[{"hi":6.331097852137052,"lo":6.310895258152958},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.350794756291665,"lo":-7.894427130761712},{"hi":0.3527476008345114,"lo":0.34264813442587627}],[{"hi":6.331097852137052,"lo":6.310895258152958},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.668162822331239,"lo":-7.6901912189373745},{"hi":0.3488965280191416,"lo":0.34858200510566784}],[{"hi":6.3127318576060985,"lo":6.292529263622004},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.459399168999512,"lo":-8.014350897238034},{"hi":0.35198465389406275,"lo":0.346628095493545}],[{"hi":6.294365863075145,"lo":6.2741632690910505},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.571865197561673,"lo":-8.123127718514052},{"hi":0.35002042296004277,"lo":0.3497231022497309}],[{"hi":6.275999868544191,"lo":6.255797274560097},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.6809549399488555,"lo":-8.235124365409469},{"hi":0.35180630158241755,"lo":0.34704428431616874}],[{"hi":6.257633874013237,"lo":6.237431280029143},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.978391617061936,"lo":-8.066054062470155},{"hi":0.3494316450463022,"lo":0.34822526048721736}],[{"hi":6.257633874013237,"lo":6.237431280029143},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.840452075434765,"lo":-8.31679459761502},{"hi":0.35215316009245035,"lo":0.3435397956384527}],[{"hi":6.239267879482283,"lo":6.21906528549819},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.959967320626424,"lo":-8.37569299091766},{"hi":0.35105506418616983,"lo":0.3408802572708747}],[{"hi":6.239267879482283,"lo":6.21906528549819},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-8.142904953707799,"lo":-8.309149376441216},{"hi":0.3468299246932296,"lo":0.34213056305263745}],[{"hi":6.2209018849513305,"lo":6.200699290967236},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-8.075834548555274,"lo":-8.49287719553671},{"hi":0.34928799385804243,"lo":0.3360937784851221}],[{"hi":6.2209018849513305,"lo":6.200699290967236},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-8.248643508859068,"lo":-8.442185078571738},{"hi":0.34406320690014836,"lo":0.3373338512355031}]]}],"pcurves":[{"direction":[0.6967067093471652,0.717356090899523],"kind":"Line2","origin":[1.8811081152373468,1.9368614454287116]},{"axis_a":[3.0,0.0],"axis_b":[0.0,-3.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[3.0,0.0],"axis_b":[0.0,-3.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[3.0,0.0],"axis_b":[0.0,-3.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-0.6967067093471659,0.7173560908995223],"kind":"Line2","origin":[2.0901201280414963,-2.152068272698568]},{"axis_a":[2.7,0.0],"axis_b":[0.0,-2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,-2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,-2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,2.7],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.465765601187591,-1.1]},{"axis_a":[3.0,0.0],"axis_b":[0.0,3.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.598076211353316,-1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.244994432064365,1.5]},{"axis_a":[3.0,0.0],"axis_b":[0.0,-3.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.7910571473905725,1.1]},{"axis_a":[2.7,0.0],"axis_b":[0.0,-2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,2.7],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[0.6967067093471659,0.7173560908995223],"kind":"Line2","origin":[1.8811081152373468,1.9368614454287116]},{"axis_a":[3.0,0.0],"axis_b":[0.0,3.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[3.0,0.0],"axis_b":[0.0,3.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[3.0,0.0],"axis_b":[0.0,3.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-0.6967067093471652,0.717356090899523],"kind":"Line2","origin":[2.0901201280414963,-2.1520682726985685]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.6941543369633125,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.6941543369633125,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.6941543369633125,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.483185307179586,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.483185307179586,1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.483185307179586,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.863571923609742,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.6941543369633125,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.863571923609742,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.863571923609742,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.863571923609742,0.0]},{"direction":[-1.0000000000000002,0.0],"kind":"Line2","origin":[0.29999999999999966,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.29999999999999966,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.29999999999999966,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.29999999999999966,1.5]},{"direction":[1.0000000000000002,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.907761699198547,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.907761699198547,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.907761699198547,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.759586531581287,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.907761699198547,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.483185307179586,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.483185307179586,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.483185307179586,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.759586531581287,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.759586531581287,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.759586531581287,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.29999999999999993,0.0]},{"direction":[-0.0,-1.0],"kind":"Line2","origin":[0.29999999999999993,1.0]},{"direction":[-0.0,-1.0],"kind":"Line2","origin":[0.29999999999999993,1.5]},{"direction":[-0.0,-1.0],"kind":"Line2","origin":[0.29999999999999993,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3530817792889511,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.3530817792889511,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3252915462029815,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.32529154620298195,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-4.440892098500626e-16,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.863571923609742,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.6941543369633125,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.759586531581287,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.907761699198547,1.5]},{"definition":0,"kind":"IntersectionSide","side":"A"},{"definition":0,"kind":"IntersectionSide","side":"B"},{"definition":1,"kind":"IntersectionSide","side":"A"},{"definition":1,"kind":"IntersectionSide","side":"B"}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,1.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,1.5,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.7},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.7},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.7},{"frame":{"origin":[1.8811081152373468,0.0,-1.9368614454287116],"x":[0.6967067093471659,0.0,-0.7173560908995223],"y":[0.0,1.0,0.0],"z":[0.7173560908995223,-0.0,0.6967067093471659]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":3.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":3.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":3.0},{"frame":{"origin":[2.0901201280414963,0.0,2.1520682726985685],"x":[-0.6967067093471652,0.0,-0.717356090899523],"y":[0.0,1.0,0.0],"z":[0.717356090899523,0.0,-0.6967067093471652]},"kind":"Plane"},{"frame":{"origin":[2.244994432064365,1.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.7910571473905725,1.0,1.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.7},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":3.0},{"frame":{"origin":[2.65,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Cylinder","radius":0.5}]},"id":"batch-staged.mixed-curved-openings.forward","quality":{"kind":"Analytic"},"revision":2,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":0,"id":0,"tolerance":6.3e-8,"twin_halfedge":72},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":5.759586531581287,"lo":5.483185307179586}},"halfedge":1,"id":1,"tolerance":6.3e-8,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":5.907761699198547,"lo":5.759586531581287}},"halfedge":2,"id":2,"tolerance":6.3e-8,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":7.083185307179586,"lo":5.907761699198547}},"halfedge":3,"id":3,"tolerance":6.3e-8,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.2999999999999996,"lo":0.0}},"halfedge":4,"id":4,"tolerance":6.3e-8,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":7.083185307179586,"lo":5.863571923609742}},"halfedge":5,"id":5,"tolerance":6.3e-8,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":5.863571923609742,"lo":5.6941543369633125}},"halfedge":6,"id":6,"tolerance":6.3e-8,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":5.6941543369633125,"lo":5.483185307179586}},"halfedge":7,"id":7,"tolerance":6.3e-8,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":5.863571923609742,"lo":5.6941543369633125}},"halfedge":8,"id":8,"tolerance":6.3e-8,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3252915462029815,"lo":0.0}},"halfedge":9,"id":9,"tolerance":6.3e-8,"twin_halfedge":84},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":5.907761699198547,"lo":5.759586531581287}},"halfedge":10,"id":10,"tolerance":6.3e-8,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3530817792889511,"lo":0.0}},"halfedge":11,"id":11,"tolerance":6.3e-8,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":0.3530817792889511,"lo":0.0}},"halfedge":12,"id":12,"tolerance":6.3e-8,"twin_halfedge":82},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":5.907761699198547,"lo":5.759586531581287}},"halfedge":13,"id":13,"tolerance":6.3e-8,"twin_halfedge":92},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.3252915462029815,"lo":0.0}},"halfedge":14,"id":14,"tolerance":6.3e-8,"twin_halfedge":86},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":5.863571923609742,"lo":5.6941543369633125}},"halfedge":15,"id":15,"tolerance":6.3e-8,"twin_halfedge":88},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":5.6941543369633125,"lo":5.483185307179586}},"halfedge":16,"id":16,"tolerance":6.3e-8,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":5.863571923609742,"lo":5.6941543369633125}},"halfedge":17,"id":17,"tolerance":6.3e-8,"twin_halfedge":90},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":7.083185307179586,"lo":5.863571923609742}},"halfedge":18,"id":18,"tolerance":6.3e-8,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.2999999999999996,"lo":0.0}},"halfedge":19,"id":19,"tolerance":6.3e-8,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":7.083185307179586,"lo":5.907761699198547}},"halfedge":20,"id":20,"tolerance":6.3e-8,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":5.907761699198547,"lo":5.759586531581287}},"halfedge":21,"id":21,"tolerance":6.3e-8,"twin_halfedge":94},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":5.759586531581287,"lo":5.483185307179586}},"halfedge":22,"id":22,"tolerance":6.3e-8,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":23,"id":23,"tolerance":6.3e-8,"twin_halfedge":76},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":25,"id":24,"tolerance":6.3e-8,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":26,"id":25,"tolerance":6.3e-8,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":27,"id":26,"tolerance":6.3e-8,"twin_halfedge":91},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":29,"id":27,"tolerance":6.3e-8,"twin_halfedge":75},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":30,"id":28,"tolerance":6.3e-8,"twin_halfedge":74},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":31,"id":29,"tolerance":6.3e-8,"twin_halfedge":73},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":33,"id":30,"tolerance":6.3e-8,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":37,"id":31,"tolerance":6.3e-8,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":38,"id":32,"tolerance":6.3e-8,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":39,"id":33,"tolerance":6.3e-8,"twin_halfedge":49},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":41,"id":34,"tolerance":6.3e-8,"twin_halfedge":89},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":42,"id":35,"tolerance":6.3e-8,"twin_halfedge":85},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":45,"id":36,"tolerance":6.3e-8,"twin_halfedge":59},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":46,"id":37,"tolerance":6.3e-8,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":47,"id":38,"tolerance":6.3e-8,"twin_halfedge":57},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":53,"id":39,"tolerance":6.3e-8,"twin_halfedge":63},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":54,"id":40,"tolerance":6.3e-8,"twin_halfedge":87},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":55,"id":41,"tolerance":6.3e-8,"twin_halfedge":95},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":61,"id":42,"tolerance":6.3e-8,"twin_halfedge":71},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":65,"id":43,"tolerance":6.3e-8,"twin_halfedge":79},{"chart_seam":false,"geometry":{"curve":44,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":66,"id":44,"tolerance":6.3e-8,"twin_halfedge":78},{"chart_seam":false,"geometry":{"curve":45,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":67,"id":45,"tolerance":6.3e-8,"twin_halfedge":77},{"chart_seam":false,"geometry":{"curve":46,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":69,"id":46,"tolerance":6.3e-8,"twin_halfedge":93},{"chart_seam":false,"geometry":{"curve":47,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":70,"id":47,"tolerance":6.3e-8,"twin_halfedge":81},{"chart_seam":false,"geometry":{"curve":48,"kind":"Curve","range":{"hi":3.147877503993888,"lo":0.0}},"halfedge":96,"id":48,"tolerance":6.3e-8,"twin_halfedge":97},{"chart_seam":false,"geometry":{"curve":49,"kind":"Curve","range":{"hi":3.146489221673897,"lo":0.0}},"halfedge":98,"id":49,"tolerance":6.3e-8,"twin_halfedge":99}],"faces":[{"id":0,"key":"batch-staged.mixed-curved-openings.forward:planar:lower-cap-0","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.000000063,"lo":1.8811080522373467},{"hi":2.1520683356985684,"lo":-2.1520683356985684}]}},{"id":1,"key":"batch-staged.mixed-curved-openings.forward:planar:upper-cap-1","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.791057210390573,"lo":2.244994369064365},{"hi":-1.0999999369999995,"lo":-1.500000063}]}},{"id":2,"key":"batch-staged.mixed-curved-openings.forward:planar:lower-cap-2","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.791057210390573,"lo":2.244994369064365},{"hi":1.500000063,"lo":1.0999999369999995}]}},{"id":3,"key":"batch-staged.mixed-curved-openings.forward:planar:upper-cap-3","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.000000063,"lo":1.8811080522373467},{"hi":2.1520683356985684,"lo":-2.1520683356985684}]}},{"id":4,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-cylinder-4","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":5.694154360296646,"lo":5.483185283846253},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":5,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-cylinder-5","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":5.863571946943075,"lo":5.694154313629979},{"hi":1.000000063,"lo":-6.3e-8}]}},{"id":6,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-cylinder-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[16],"outer":6,"uv_bounds":[{"hi":7.0831853305129195,"lo":5.863571900276408},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":7,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-plane-7","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":2,"key":"side-0-0"}]},"sense":"Reverse","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":0.3000000629999996,"lo":-6.3e-8},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":8,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-cylinder-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[18],"outer":8,"uv_bounds":[{"hi":7.083185328179586,"lo":5.907761678198547},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":9,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-cylinder-9","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":5.907761720198547,"lo":5.759586510581287},{"hi":1.000000063,"lo":-6.3e-8}]}},{"id":10,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-cylinder-10","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":5.759586552581287,"lo":5.4831852861795864},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":11,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-plane-11","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":0.30000006299999993,"lo":-6.3e-8},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":12,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-plane-12","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":0.3530818422889511,"lo":-6.3e-8},{"hi":0.500000063,"lo":-6.3e-8}]}},{"id":13,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-plane-13","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":0,"key":"bottom"}]},"sense":"Reverse","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":0.32529160920298195,"lo":-6.3e-8},{"hi":0.500000063,"lo":-6.3e-8}]}},{"id":14,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-cylinder-14","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":5.863571946943075,"lo":5.694154313629979},{"hi":3.000000063,"lo":1.499999937}]}},{"id":15,"key":"batch-staged.mixed-curved-openings.forward:planar:vertical-cylinder-15","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":5.907761720198547,"lo":5.759586510581287},{"hi":3.000000063,"lo":1.499999937}]}},{"id":16,"key":"lateral:through-cut","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round-opening","entity":"round-opening","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[19],"outer":17,"uv_bounds":[{"hi":-2.081576844571777,"lo":-8.52028169592909},{"hi":0.3499989716450176,"lo":0.0033002839911298975}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":7,"to":1,"twin":72,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":64,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":60,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":4,"prev":2,"to":4,"twin":52,"wire_ref":null},{"edge":4,"face":0,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":0,"next":5,"prev":3,"to":5,"twin":44,"wire_ref":null},{"edge":5,"face":0,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":0,"next":6,"prev":4,"to":6,"twin":36,"wire_ref":null},{"edge":6,"face":0,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":0,"next":7,"prev":5,"to":7,"twin":32,"wire_ref":null},{"edge":7,"face":0,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":0,"next":0,"prev":6,"to":0,"twin":24,"wire_ref":null},{"edge":8,"face":1,"from":8,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":1,"next":9,"prev":11,"to":9,"twin":34,"wire_ref":null},{"edge":9,"face":1,"from":9,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":1,"next":10,"prev":8,"to":10,"twin":84,"wire_ref":null},{"edge":10,"face":1,"from":10,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":1,"next":11,"prev":9,"to":11,"twin":62,"wire_ref":null},{"edge":11,"face":1,"from":11,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":1,"next":8,"prev":10,"to":8,"twin":80,"wire_ref":null},{"edge":12,"face":2,"from":12,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":2,"next":13,"prev":15,"to":13,"twin":82,"wire_ref":null},{"edge":13,"face":2,"from":13,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":2,"next":14,"prev":12,"to":14,"twin":92,"wire_ref":null},{"edge":14,"face":2,"from":14,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":2,"next":15,"prev":13,"to":15,"twin":86,"wire_ref":null},{"edge":15,"face":2,"from":15,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":2,"next":12,"prev":14,"to":12,"twin":88,"wire_ref":null},{"edge":16,"face":3,"from":16,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":3,"next":17,"prev":23,"to":17,"twin":28,"wire_ref":null},{"edge":17,"face":3,"from":17,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":3,"next":18,"prev":16,"to":18,"twin":90,"wire_ref":null},{"edge":18,"face":3,"from":18,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":3,"next":19,"prev":17,"to":19,"twin":40,"wire_ref":null},{"edge":19,"face":3,"from":19,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":3,"next":20,"prev":18,"to":20,"twin":48,"wire_ref":null},{"edge":20,"face":3,"from":20,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Reverse"},"id":20,"loop_ref":3,"next":21,"prev":19,"to":21,"twin":56,"wire_ref":null},{"edge":21,"face":3,"from":21,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Reverse"},"id":21,"loop_ref":3,"next":22,"prev":20,"to":22,"twin":94,"wire_ref":null},{"edge":22,"face":3,"from":22,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":3,"next":23,"prev":21,"to":23,"twin":68,"wire_ref":null},{"edge":23,"face":3,"from":23,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":3,"next":16,"prev":22,"to":16,"twin":76,"wire_ref":null},{"edge":7,"face":4,"from":0,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":4,"next":25,"prev":31,"to":7,"twin":7,"wire_ref":null},{"edge":24,"face":4,"from":7,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":4,"next":26,"prev":24,"to":8,"twin":35,"wire_ref":null},{"edge":25,"face":4,"from":8,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Forward"},"id":26,"loop_ref":4,"next":27,"prev":25,"to":12,"twin":83,"wire_ref":null},{"edge":26,"face":4,"from":12,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":4,"next":28,"prev":26,"to":17,"twin":91,"wire_ref":null},{"edge":16,"face":4,"from":17,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Reverse"},"id":28,"loop_ref":4,"next":29,"prev":27,"to":16,"twin":16,"wire_ref":null},{"edge":27,"face":4,"from":16,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":4,"next":30,"prev":28,"to":24,"twin":75,"wire_ref":null},{"edge":28,"face":4,"from":24,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Forward"},"id":30,"loop_ref":4,"next":31,"prev":29,"to":25,"twin":74,"wire_ref":null},{"edge":29,"face":4,"from":25,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Forward"},"id":31,"loop_ref":4,"next":24,"prev":30,"to":0,"twin":73,"wire_ref":null},{"edge":6,"face":5,"from":7,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":5,"next":33,"prev":35,"to":6,"twin":6,"wire_ref":null},{"edge":30,"face":5,"from":6,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":5,"next":34,"prev":32,"to":9,"twin":43,"wire_ref":null},{"edge":8,"face":5,"from":9,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":5,"next":35,"prev":33,"to":8,"twin":8,"wire_ref":null},{"edge":24,"face":5,"from":8,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":5,"next":32,"prev":34,"to":7,"twin":25,"wire_ref":null},{"edge":5,"face":6,"from":6,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":6,"next":37,"prev":43,"to":5,"twin":5,"wire_ref":null},{"edge":31,"face":6,"from":5,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":6,"next":38,"prev":36,"to":26,"twin":51,"wire_ref":null},{"edge":32,"face":6,"from":26,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Forward"},"id":38,"loop_ref":6,"next":39,"prev":37,"to":27,"twin":50,"wire_ref":null},{"edge":33,"face":6,"from":27,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":6,"next":40,"prev":38,"to":19,"twin":49,"wire_ref":null},{"edge":18,"face":6,"from":19,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Reverse"},"id":40,"loop_ref":6,"next":41,"prev":39,"to":18,"twin":18,"wire_ref":null},{"edge":34,"face":6,"from":18,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":6,"next":42,"prev":40,"to":15,"twin":89,"wire_ref":null},{"edge":35,"face":6,"from":15,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Forward"},"id":42,"loop_ref":6,"next":43,"prev":41,"to":9,"twin":85,"wire_ref":null},{"edge":30,"face":6,"from":9,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":6,"next":36,"prev":42,"to":6,"twin":33,"wire_ref":null},{"edge":4,"face":7,"from":5,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Reverse"},"id":44,"loop_ref":7,"next":45,"prev":51,"to":4,"twin":4,"wire_ref":null},{"edge":36,"face":7,"from":4,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":7,"next":46,"prev":44,"to":28,"twin":59,"wire_ref":null},{"edge":37,"face":7,"from":28,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Forward"},"id":46,"loop_ref":7,"next":47,"prev":45,"to":29,"twin":58,"wire_ref":null},{"edge":38,"face":7,"from":29,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Forward"},"id":47,"loop_ref":7,"next":48,"prev":46,"to":20,"twin":57,"wire_ref":null},{"edge":19,"face":7,"from":20,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Reverse"},"id":48,"loop_ref":7,"next":49,"prev":47,"to":19,"twin":19,"wire_ref":null},{"edge":33,"face":7,"from":19,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Reverse"},"id":49,"loop_ref":7,"next":50,"prev":48,"to":27,"twin":39,"wire_ref":null},{"edge":32,"face":7,"from":27,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":7,"next":51,"prev":49,"to":26,"twin":38,"wire_ref":null},{"edge":31,"face":7,"from":26,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":7,"next":44,"prev":50,"to":5,"twin":37,"wire_ref":null},{"edge":3,"face":8,"from":4,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Reverse"},"id":52,"loop_ref":8,"next":53,"prev":59,"to":3,"twin":3,"wire_ref":null},{"edge":39,"face":8,"from":3,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":8,"next":54,"prev":52,"to":10,"twin":63,"wire_ref":null},{"edge":40,"face":8,"from":10,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Forward"},"id":54,"loop_ref":8,"next":55,"prev":53,"to":14,"twin":87,"wire_ref":null},{"edge":41,"face":8,"from":14,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Forward"},"id":55,"loop_ref":8,"next":56,"prev":54,"to":21,"twin":95,"wire_ref":null},{"edge":20,"face":8,"from":21,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":8,"next":57,"prev":55,"to":20,"twin":20,"wire_ref":null},{"edge":38,"face":8,"from":20,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Reverse"},"id":57,"loop_ref":8,"next":58,"prev":56,"to":29,"twin":47,"wire_ref":null},{"edge":37,"face":8,"from":29,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":8,"next":59,"prev":57,"to":28,"twin":46,"wire_ref":null},{"edge":36,"face":8,"from":28,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":8,"next":52,"prev":58,"to":4,"twin":45,"wire_ref":null},{"edge":2,"face":9,"from":3,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Reverse"},"id":60,"loop_ref":9,"next":61,"prev":63,"to":2,"twin":2,"wire_ref":null},{"edge":42,"face":9,"from":2,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Forward"},"id":61,"loop_ref":9,"next":62,"prev":60,"to":11,"twin":71,"wire_ref":null},{"edge":10,"face":9,"from":11,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Forward"},"id":62,"loop_ref":9,"next":63,"prev":61,"to":10,"twin":10,"wire_ref":null},{"edge":39,"face":9,"from":10,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Reverse"},"id":63,"loop_ref":9,"next":60,"prev":62,"to":3,"twin":53,"wire_ref":null},{"edge":1,"face":10,"from":2,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Reverse"},"id":64,"loop_ref":10,"next":65,"prev":71,"to":1,"twin":1,"wire_ref":null},{"edge":43,"face":10,"from":1,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Forward"},"id":65,"loop_ref":10,"next":66,"prev":64,"to":30,"twin":79,"wire_ref":null},{"edge":44,"face":10,"from":30,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Forward"},"id":66,"loop_ref":10,"next":67,"prev":65,"to":31,"twin":78,"wire_ref":null},{"edge":45,"face":10,"from":31,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Forward"},"id":67,"loop_ref":10,"next":68,"prev":66,"to":23,"twin":77,"wire_ref":null},{"edge":22,"face":10,"from":23,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":10,"next":69,"prev":67,"to":22,"twin":22,"wire_ref":null},{"edge":46,"face":10,"from":22,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Forward"},"id":69,"loop_ref":10,"next":70,"prev":68,"to":13,"twin":93,"wire_ref":null},{"edge":47,"face":10,"from":13,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Forward"},"id":70,"loop_ref":10,"next":71,"prev":69,"to":11,"twin":81,"wire_ref":null},{"edge":42,"face":10,"from":11,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Reverse"},"id":71,"loop_ref":10,"next":64,"prev":70,"to":2,"twin":61,"wire_ref":null},{"edge":0,"face":11,"from":1,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Reverse"},"id":72,"loop_ref":11,"next":73,"prev":79,"to":0,"twin":0,"wire_ref":null},{"edge":29,"face":11,"from":0,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Reverse"},"id":73,"loop_ref":11,"next":74,"prev":72,"to":25,"twin":31,"wire_ref":null},{"edge":28,"face":11,"from":25,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":11,"next":75,"prev":73,"to":24,"twin":30,"wire_ref":null},{"edge":27,"face":11,"from":24,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Reverse"},"id":75,"loop_ref":11,"next":76,"prev":74,"to":16,"twin":29,"wire_ref":null},{"edge":23,"face":11,"from":16,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Reverse"},"id":76,"loop_ref":11,"next":77,"prev":75,"to":23,"twin":23,"wire_ref":null},{"edge":45,"face":11,"from":23,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Reverse"},"id":77,"loop_ref":11,"next":78,"prev":76,"to":31,"twin":67,"wire_ref":null},{"edge":44,"face":11,"from":31,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":11,"next":79,"prev":77,"to":30,"twin":66,"wire_ref":null},{"edge":43,"face":11,"from":30,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Reverse"},"id":79,"loop_ref":11,"next":72,"prev":78,"to":1,"twin":65,"wire_ref":null},{"edge":11,"face":12,"from":8,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Reverse"},"id":80,"loop_ref":12,"next":81,"prev":83,"to":11,"twin":11,"wire_ref":null},{"edge":47,"face":12,"from":11,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Reverse"},"id":81,"loop_ref":12,"next":82,"prev":80,"to":13,"twin":70,"wire_ref":null},{"edge":12,"face":12,"from":13,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":12,"next":83,"prev":81,"to":12,"twin":12,"wire_ref":null},{"edge":25,"face":12,"from":12,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":12,"next":80,"prev":82,"to":8,"twin":26,"wire_ref":null},{"edge":9,"face":13,"from":10,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Reverse"},"id":84,"loop_ref":13,"next":85,"prev":87,"to":9,"twin":9,"wire_ref":null},{"edge":35,"face":13,"from":9,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Reverse"},"id":85,"loop_ref":13,"next":86,"prev":84,"to":15,"twin":42,"wire_ref":null},{"edge":14,"face":13,"from":15,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Reverse"},"id":86,"loop_ref":13,"next":87,"prev":85,"to":14,"twin":14,"wire_ref":null},{"edge":40,"face":13,"from":14,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Reverse"},"id":87,"loop_ref":13,"next":84,"prev":86,"to":10,"twin":54,"wire_ref":null},{"edge":15,"face":14,"from":12,"geometry_use":{"pcurve":88,"periodic_lift":[0,0],"sense":"Forward"},"id":88,"loop_ref":14,"next":89,"prev":91,"to":15,"twin":15,"wire_ref":null},{"edge":34,"face":14,"from":15,"geometry_use":{"pcurve":89,"periodic_lift":[0,0],"sense":"Reverse"},"id":89,"loop_ref":14,"next":90,"prev":88,"to":18,"twin":41,"wire_ref":null},{"edge":17,"face":14,"from":18,"geometry_use":{"pcurve":90,"periodic_lift":[0,0],"sense":"Reverse"},"id":90,"loop_ref":14,"next":91,"prev":89,"to":17,"twin":17,"wire_ref":null},{"edge":26,"face":14,"from":17,"geometry_use":{"pcurve":91,"periodic_lift":[0,0],"sense":"Reverse"},"id":91,"loop_ref":14,"next":88,"prev":90,"to":12,"twin":27,"wire_ref":null},{"edge":13,"face":15,"from":14,"geometry_use":{"pcurve":92,"periodic_lift":[0,0],"sense":"Reverse"},"id":92,"loop_ref":15,"next":93,"prev":95,"to":13,"twin":13,"wire_ref":null},{"edge":46,"face":15,"from":13,"geometry_use":{"pcurve":93,"periodic_lift":[0,0],"sense":"Reverse"},"id":93,"loop_ref":15,"next":94,"prev":92,"to":22,"twin":69,"wire_ref":null},{"edge":21,"face":15,"from":22,"geometry_use":{"pcurve":94,"periodic_lift":[0,0],"sense":"Forward"},"id":94,"loop_ref":15,"next":95,"prev":93,"to":21,"twin":21,"wire_ref":null},{"edge":41,"face":15,"from":21,"geometry_use":{"pcurve":95,"periodic_lift":[0,0],"sense":"Reverse"},"id":95,"loop_ref":15,"next":92,"prev":94,"to":14,"twin":55,"wire_ref":null},{"edge":48,"face":6,"from":32,"geometry_use":{"pcurve":96,"periodic_lift":[0,0],"sense":"Forward"},"id":96,"loop_ref":16,"next":96,"prev":96,"to":32,"twin":97,"wire_ref":null},{"edge":48,"face":16,"from":32,"geometry_use":{"pcurve":97,"periodic_lift":[0,0],"sense":"Reverse"},"id":97,"loop_ref":17,"next":97,"prev":97,"to":32,"twin":96,"wire_ref":null},{"edge":49,"face":8,"from":33,"geometry_use":{"pcurve":98,"periodic_lift":[0,0],"sense":"Forward"},"id":98,"loop_ref":18,"next":98,"prev":98,"to":33,"twin":99,"wire_ref":null},{"edge":49,"face":16,"from":33,"geometry_use":{"pcurve":99,"periodic_lift":[0,0],"sense":"Reverse"},"id":99,"loop_ref":19,"next":99,"prev":99,"to":33,"twin":98,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":8},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":12},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":16},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":24},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":32},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":36},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":44},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":52},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":60},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":64},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":72},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":80},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":84},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":88},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":92},{"face_ref":6,"id":16,"is_hole":true,"start_halfedge":96},{"face_ref":16,"id":17,"is_hole":false,"start_halfedge":97},{"face_ref":8,"id":18,"is_hole":true,"start_halfedge":98},{"face_ref":16,"id":19,"is_hole":true,"start_halfedge":99}],"shells":[{"faces":[0,11,10,9,8,7,6,5,4,3,15,12,1,13,14,2,16],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.8811081152373468,0.0,1.9368614454287116],"tolerance":6.3e-8},{"id":1,"outgoing_halfedge":1,"position":[2.0901201280414963,0.0,2.1520682726985685],"tolerance":6.3e-8},{"id":2,"outgoing_halfedge":2,"position":[2.598076211353316,0.0,1.5],"tolerance":6.3e-8},{"id":3,"outgoing_halfedge":3,"position":[2.791057147390573,0.0,1.0999999999999992],"tolerance":6.3e-8},{"id":4,"outgoing_halfedge":4,"position":[2.0901201280414963,0.0,-2.1520682726985685],"tolerance":6.3e-8},{"id":5,"outgoing_halfedge":5,"position":[1.8811081152373468,0.0,-1.9368614454287116],"tolerance":6.3e-8},{"id":6,"outgoing_halfedge":6,"position":[2.4657656011875906,0.0,1.1000000000000003],"tolerance":6.3e-8},{"id":7,"outgoing_halfedge":7,"position":[2.244994432064365,0.0,1.4999999999999998],"tolerance":6.3e-8},{"id":8,"outgoing_halfedge":8,"position":[2.244994432064365,1.0,1.4999999999999998],"tolerance":6.3e-8},{"id":9,"outgoing_halfedge":9,"position":[2.4657656011875906,1.0,1.1000000000000005],"tolerance":6.3e-8},{"id":10,"outgoing_halfedge":10,"position":[2.7910571473905725,1.0,1.1],"tolerance":6.3e-8},{"id":11,"outgoing_halfedge":11,"position":[2.598076211353316,1.0,1.5],"tolerance":6.3e-8},{"id":12,"outgoing_halfedge":12,"position":[2.244994432064365,1.5,1.5],"tolerance":6.3e-8},{"id":13,"outgoing_halfedge":13,"position":[2.598076211353316,1.5,1.5],"tolerance":6.3e-8},{"id":14,"outgoing_halfedge":14,"position":[2.791057147390573,1.5,1.0999999999999996],"tolerance":6.3e-8},{"id":15,"outgoing_halfedge":15,"position":[2.465765601187591,1.5,1.1],"tolerance":6.3e-8},{"id":16,"outgoing_halfedge":16,"position":[1.8811081152373468,3.0,1.9368614454287116],"tolerance":6.3e-8},{"id":17,"outgoing_halfedge":17,"position":[2.244994432064365,3.0,1.4999999999999998],"tolerance":6.3e-8},{"id":18,"outgoing_halfedge":18,"position":[2.4657656011875906,3.0,1.1000000000000005],"tolerance":6.3e-8},{"id":19,"outgoing_halfedge":19,"position":[1.8811081152373468,3.0,-1.9368614454287116],"tolerance":6.3e-8},{"id":20,"outgoing_halfedge":20,"position":[2.0901201280414963,3.0,-2.152068272698568],"tolerance":6.3e-8},{"id":21,"outgoing_halfedge":21,"position":[2.791057147390573,3.0,1.0999999999999992],"tolerance":6.3e-8},{"id":22,"outgoing_halfedge":22,"position":[2.598076211353316,3.0,1.5],"tolerance":6.3e-8},{"id":23,"outgoing_halfedge":23,"position":[2.0901201280414963,3.0,2.1520682726985685],"tolerance":6.3e-8},{"id":24,"outgoing_halfedge":30,"position":[1.8811081152373468,1.5,1.9368614454287116],"tolerance":6.3e-8},{"id":25,"outgoing_halfedge":31,"position":[1.8811081152373468,1.0,1.9368614454287116],"tolerance":6.3e-8},{"id":26,"outgoing_halfedge":38,"position":[1.8811081152373468,1.0,-1.9368614454287116],"tolerance":6.3e-8},{"id":27,"outgoing_halfedge":39,"position":[1.8811081152373468,1.5,-1.9368614454287116],"tolerance":6.3e-8},{"id":28,"outgoing_halfedge":46,"position":[2.0901201280414963,1.0,-2.152068272698568],"tolerance":6.3e-8},{"id":29,"outgoing_halfedge":47,"position":[2.0901201280414963,1.5,-2.152068272698568],"tolerance":6.3e-8},{"id":30,"outgoing_halfedge":66,"position":[2.0901201280414963,1.0,2.1520682726985685],"tolerance":6.3e-8},{"id":31,"outgoing_halfedge":67,"position":[2.0901201280414963,1.5,2.1520682726985685],"tolerance":6.3e-8},{"id":32,"outgoing_halfedge":96,"position":[2.682255636269859,1.1069411008145305,0.30903834988326967],"tolerance":6.3e-8},{"id":33,"outgoing_halfedge":98,"position":[2.990025593501361,1.0638199343388153,0.244431892777589],"tolerance":6.3e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/staged.mixed-curved-openings.reverse.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/staged.mixed-curved-openings.reverse.json new file mode 100644 index 0000000..ec4e2a3 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/staged.mixed-curved-openings.reverse.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":6.3e-8,"intersection":1.575e-8,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[2.65,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Circle","radius":0.5},{"frame":{"origin":[3.05,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Circle","radius":0.5},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.65,1.5,-0.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.4]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.5,0.0],"axis_b":[0.0,-0.5],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.5,0.0],"axis_b":[0.0,0.5],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[2.65,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Cylinder","radius":0.5},{"frame":{"origin":[2.65,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,-1.0,-0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.05,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"}]},"id":"round-opening","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":6.3e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":6.3e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.3999999999999999,"lo":0.0}},"halfedge":1,"id":2,"tolerance":6.3e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-opening","entity":"round-opening","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":0.4,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-opening","entity":"round-opening","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.5,"lo":-0.5},{"hi":0.5,"lo":-0.5}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"round-opening","entity":"round-opening","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.5,"lo":-0.5},{"hi":0.5,"lo":-0.5}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[2.65,1.5,-0.5],"tolerance":6.3e-8},{"id":1,"outgoing_halfedge":2,"position":[3.05,1.5,-0.5],"tolerance":6.3e-8}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":6.3e-8,"intersection":1.575e-8,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.2,1.0,1.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.2,1.0,1.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.2,1.0,1.1]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.2,1.0,1.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.2,1.0,1.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.2,1.5,1.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.2,1.5,1.1]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[3.2,1.5,1.1]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.2,1.0,1.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.2,1.0,1.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.2,1.0,1.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.2,1.5,1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3999999999999999,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3999999999999999]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.3999999999999999]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3999999999999999,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[2.2,1.0,1.1],"x":[0.0,1.0,0.0],"y":[1.0,0.0,0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[2.2,1.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.2,1.0,1.1],"x":[0.0,0.0,1.0],"y":[0.0,1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.2,1.0,1.1],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.2,1.0,1.1],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.2,1.5,1.1],"x":[0.0,0.0,1.0],"y":[1.0,0.0,0.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"rectangular-opening","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":6.3e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":1,"tolerance":6.3e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":8,"id":2,"tolerance":6.3e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":3,"tolerance":6.3e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":15,"id":4,"tolerance":6.3e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":6.3e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":10,"id":6,"tolerance":6.3e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.4,"lo":0.0}},"halfedge":13,"id":7,"tolerance":6.3e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":6.3e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":7,"id":9,"tolerance":6.3e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":10,"tolerance":6.3e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":6.3e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.500000063,"lo":-6.3e-8},{"hi":1.000000063,"lo":-6.3e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.000000063,"lo":-6.3e-8},{"hi":0.500000063,"lo":-6.3e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4000000629999999,"lo":-6.3e-8},{"hi":0.500000063,"lo":-6.3e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.500000063,"lo":-6.3e-8},{"hi":0.4000000629999999,"lo":-6.3e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000063,"lo":-6.3e-8},{"hi":0.4000000629999999,"lo":-6.3e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.4000000629999999,"lo":-6.3e-8},{"hi":1.000000063,"lo":-6.3e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[2.2,1.0,1.1],"tolerance":6.3e-8},{"id":1,"outgoing_halfedge":3,"position":[3.2,1.0,1.1],"tolerance":6.3e-8},{"id":2,"outgoing_halfedge":1,"position":[2.2,1.5,1.1],"tolerance":6.3e-8},{"id":3,"outgoing_halfedge":2,"position":[3.2,1.5,1.1],"tolerance":6.3e-8},{"id":4,"outgoing_halfedge":4,"position":[2.2,1.0,1.5],"tolerance":6.3e-8},{"id":5,"outgoing_halfedge":5,"position":[3.2,1.0,1.5],"tolerance":6.3e-8},{"id":6,"outgoing_halfedge":7,"position":[2.2,1.5,1.5],"tolerance":6.3e-8},{"id":7,"outgoing_halfedge":6,"position":[3.2,1.5,1.5],"tolerance":6.3e-8}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_planar_cutters","subtract_vertical_arc_extrusion","generic_two_sided_cutter_band_subtraction"],"host":{"accuracy":{"exchange":0.00001,"geometric":6.3e-8,"intersection":1.575e-8,"tessellation":0.01},"geometry":{"curves":[{"direction":[-0.6967067093471652,0.0,0.717356090899523],"kind":"Line","origin":[2.0901201280414963,0.0,-2.1520682726985685]},{"direction":[-0.6967067093471652,0.0,0.717356090899523],"kind":"Line","origin":[2.0901201280414963,3.0,-2.1520682726985685]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,0.0,-2.1520682726985685]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8811081152373468,0.0,-1.9368614454287116]},{"direction":[0.6967067093471652,0.0,0.717356090899523],"kind":"Line","origin":[1.8811081152373468,0.0,1.9368614454287116]},{"direction":[0.6967067093471652,0.0,0.717356090899523],"kind":"Line","origin":[1.8811081152373468,3.0,1.9368614454287116]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8811081152373468,0.0,1.9368614454287116]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,0.0,2.1520682726985685]}],"intersections":[],"pcurves":[{"direction":[-0.6967067093471652,-0.717356090899523],"kind":"Line2","origin":[2.0901201280414963,2.1520682726985685]},{"axis_a":[2.7,0.0],"axis_b":[0.0,2.7],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[0.6967067093471652,-0.717356090899523],"kind":"Line2","origin":[1.8811081152373468,-1.9368614454287116]},{"axis_a":[3.0,0.0],"axis_b":[0.0,3.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[3.0,0.0],"axis_b":[0.0,-3.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[0.6967067093471652,0.717356090899523],"kind":"Line2","origin":[1.8811081152373468,1.9368614454287116]},{"axis_a":[2.7,0.0],"axis_b":[0.0,-2.7],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-0.6967067093471652,0.717356090899523],"kind":"Line2","origin":[2.0901201280414963,-2.1520682726985685]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.29999999999999993,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.29999999999999993,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.8,0.0]}],"surfaces":[{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0901201280414963,0.0,-2.1520682726985685],"x":[-0.6967067093471652,0.0,0.717356090899523],"y":[0.0,1.0,0.0],"z":[-0.717356090899523,0.0,-0.6967067093471652]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.7},{"frame":{"origin":[1.8811081152373468,0.0,1.9368614454287116],"x":[0.6967067093471652,0.0,0.717356090899523],"y":[0.0,1.0,-0.0],"z":[-0.717356090899523,0.0,0.6967067093471652]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":3.0}]},"id":"mixed-curved-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":7,"id":0,"tolerance":6.3e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":0,"id":1,"tolerance":6.3e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":6.3e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.8,"lo":-0.8}},"halfedge":6,"id":3,"tolerance":6.3e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.8,"lo":-0.8}},"halfedge":1,"id":4,"tolerance":6.3e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":6.3e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":5,"id":6,"tolerance":6.3e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":2,"id":7,"tolerance":6.3e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":6.3e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.8,"lo":-0.8}},"halfedge":4,"id":9,"tolerance":6.3e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.8,"lo":-0.8}},"halfedge":3,"id":10,"tolerance":6.3e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":6.3e-8,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.000000063,"lo":-3.000000063},{"hi":3.000000063,"lo":-3.000000063}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.000000063,"lo":-3.000000063},{"hi":3.000000063,"lo":-3.000000063}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.30000006299999993,"lo":-6.3e-8},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.8000000233333334,"lo":-0.8000000233333334},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.30000006299999993,"lo":-6.3e-8},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.800000021,"lo":-0.800000021},{"hi":3.000000063,"lo":-6.3e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.0901201280414963,0.0,-2.1520682726985685],"tolerance":6.3e-8},{"id":1,"outgoing_halfedge":7,"position":[1.8811081152373468,0.0,-1.9368614454287116],"tolerance":6.3e-8},{"id":2,"outgoing_halfedge":6,"position":[1.8811081152373468,0.0,1.9368614454287116],"tolerance":6.3e-8},{"id":3,"outgoing_halfedge":5,"position":[2.0901201280414963,0.0,2.1520682726985685],"tolerance":6.3e-8},{"id":4,"outgoing_halfedge":0,"position":[2.0901201280414963,3.0,-2.1520682726985685],"tolerance":6.3e-8},{"id":5,"outgoing_halfedge":1,"position":[1.8811081152373468,3.0,-1.9368614454287116],"tolerance":6.3e-8},{"id":6,"outgoing_halfedge":2,"position":[1.8811081152373468,3.0,1.9368614454287116],"tolerance":6.3e-8},{"id":7,"outgoing_halfedge":3,"position":[2.0901201280414963,3.0,2.1520682726985685],"tolerance":6.3e-8}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":6.3e-8,"intersection":1.575e-8,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.6967067093471652,0.0,0.717356090899523],"kind":"Line","origin":[1.8811081152373468,0.0,1.9368614454287116]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"direction":[-0.6967067093471659,0.0,0.7173560908995223],"kind":"Line","origin":[2.0901201280414963,0.0,-2.152068272698568]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,1.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.465765601187591,1.0,1.1]},{"frame":{"origin":[0.0,1.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.598076211353316,1.0,1.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.244994432064365,1.5,1.5]},{"frame":{"origin":[0.0,1.5,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.7910571473905725,1.5,1.1]},{"frame":{"origin":[0.0,1.5,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.7},{"direction":[0.6967067093471659,0.0,-0.7173560908995223],"kind":"Line","origin":[1.8811081152373468,3.0,-1.9368614454287116]},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":3.0},{"direction":[-0.6967067093471652,0.0,-0.717356090899523],"kind":"Line","origin":[2.0901201280414963,3.0,2.1520682726985685]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.244994432064365,0.0,1.4999999999999998]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.244994432064365,1.0,1.4999999999999998]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.244994432064365,1.5,1.4999999999999998]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8811081152373468,3.0,1.9368614454287116]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8811081152373468,1.5,1.9368614454287116]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.8811081152373468,1.0,1.9368614454287116]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.4657656011875906,0.0,1.1000000000000005]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8811081152373468,0.0,-1.9368614454287116]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8811081152373468,1.0,-1.9368614454287116]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.8811081152373468,1.5,-1.9368614454287116]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.4657656011875906,3.0,1.1000000000000005]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.4657656011875906,1.5,1.1000000000000005]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,0.0,-2.152068272698568]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,1.0,-2.152068272698568]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,1.5,-2.152068272698568]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.791057147390573,0.0,1.0999999999999992]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.791057147390573,1.0,1.0999999999999992]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.791057147390573,1.5,1.0999999999999992]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.598076211353316,0.0,1.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,0.0,2.1520682726985685]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,1.0,2.1520682726985685]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0901201280414963,1.5,2.1520682726985685]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.598076211353316,3.0,1.5]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.598076211353316,1.5,1.5]},{"definition":0,"kind":"Intersection"},{"definition":1,"kind":"Intersection"}],"intersections":[{"anchors":[{"parameter":0.0,"point":[2.682255636269859,1.1069411007860208,0.309038349905685],"uv_a":[6.168475257835536,1.1069411008145305],"uv_b":[-2.2370900898141954,0.03225563626985917]},{"parameter":0.028334801114291642,"point":[2.679668824997465,1.1249999843059526,0.3307188962004861],"uv_a":[6.160388449313543,1.12499998425],"uv_b":[-2.2935305273231577,0.029668824997465038]},{"parameter":0.06261555364766803,"point":[2.676489529532718,1.148437485266038,0.35553311839962887],"uv_a":[6.151122836532611,1.148437485234375],"uv_b":[-2.361809801256655,0.02648952953271788]},{"parameter":0.06917300359337525,"point":[2.6758798017644945,1.1531099403349856,0.3600934413561505],"uv_a":[6.1494187979880905,1.15310994033352],"uv_b":[-2.3748679768681527,0.02587980176449456]},{"parameter":0.09472350578879225,"point":[2.673511927131303,1.1718749862698106,0.37727175277559466],"uv_a":[6.142996291169094,1.17187498621875],"uv_b":[-2.4257545571066568,0.0235119271313029]},{"parameter":0.12512878456108087,"point":[2.670736692480739,1.1953124871700322,0.3964410668323627],"uv_a":[6.135822511238574,1.195312487203125],"uv_b":[-2.4863205326425137,0.020736692480739283]},{"parameter":0.14995049847172123,"point":[2.66853225449655,1.2152819057274709,0.4110177693783217],"uv_a":[6.130362338140644,1.2152819057554232],"uv_b":[-2.5357728337336125,0.01853225449655005]},{"parameter":0.15417322142588208,"point":[2.668164456906109,1.2187499882008486,0.41339863435515534],"uv_a":[6.129470076501057,1.2187499881875001],"uv_b":[-2.5441862084349074,0.018164456906109017]},{"parameter":0.18210498285059248,"point":[2.6657958081649715,1.242187489148561,0.4284071768811314],"uv_a":[6.123842549443157,1.2421874891718752],"uv_b":[-2.5998556927074383,0.015795808164971614]},{"parameter":0.2091194781015368,"point":[2.6636312892736353,1.2656249901660308,0.4416654330895639],"uv_a":[6.118867069463013,1.2656249901562502],"uv_b":[-2.6537174838059925,0.013631289273635439]},{"parameter":0.23537113950850322,"point":[2.66167139832315,1.2890624911389978,0.4533269982602597],"uv_a":[6.1144873948445735,1.289062491140625],"uv_b":[-2.7060802629751683,0.011671398323150228]},{"parameter":0.256443011692048,"point":[2.660215662637239,1.3083039602163389,0.46179284131799114],"uv_a":[6.111305878293199,1.308303960231183],"uv_b":[-2.7481264156909653,0.010215662637238943]},{"parameter":0.2609875710109817,"point":[2.6599165875972526,1.3124999921208964,0.4635124022444197],"uv_a":[6.1106594428997605,1.312499992125],"uv_b":[-2.7571958620866104,0.009916587597252668]},{"parameter":0.28607223779480606,"point":[2.6583672632416975,1.33593749309167,0.47231715379922057],"uv_a":[6.107348320847363,1.335937493109375],"uv_b":[-2.8072746448894756,0.008367263241697565]},{"parameter":0.3107171775011209,"point":[2.657023784686102,1.3593749940794733,0.4798172648614164],"uv_a":[6.104526287239927,1.3593749940937498],"uv_b":[-2.8564962009681647,0.007023784686102097]},{"parameter":0.33500186015188027,"point":[2.6558864643186353,1.3828124950733056,0.4860731309880169],"uv_a":[6.102171320575353,1.3828124950781249],"uv_b":[-2.9050170325986295,0.005886464318635376]},{"parameter":0.3589972392979255,"point":[2.654955567088826,1.4062499960645936,0.4911323006820845],"uv_a":[6.100266097951611,1.4062499960625],"uv_b":[-2.9529762594057267,0.004955567088825941]},{"parameter":0.38276790371366365,"point":[2.654231310219106,1.429687497041538,0.4950314655552374],"uv_a":[6.098797261305364,1.429687497046875],"uv_b":[-3.000499988146808,0.004231310219106277]},{"parameter":0.40637377755047316,"point":[2.6537138628622183,1.4531249980248027,0.49779788480974135],"uv_a":[6.097754892221692,1.45312499803125],"uv_b":[-3.0477047745014545,0.00371386286221842]},{"parameter":0.4298715140080698,"point":[2.6534033459790627,1.4765624990156134,0.4994503814668287],"uv_a":[6.0971321448404385,1.476562499015625],"uv_b":[-3.0947004684855486,0.0034033459790627774]},{"parameter":0.45331568701695135,"point":[2.653299832265033,1.5,0.5000000000511745],"uv_a":[6.096925003564456,1.5],"uv_b":[-3.141592653589793,0.0032998322650330536]},{"parameter":0.4767598600258329,"point":[2.6534033459790627,1.5234375009843866,0.4994503814668287],"uv_a":[6.0971321448404385,1.523437500984375],"uv_b":[-3.1884848386940376,0.0034033459790627774]},{"parameter":0.5002575964834295,"point":[2.6537138628622183,1.5468750019751973,0.49779788480974135],"uv_a":[6.097754892221692,1.54687500196875],"uv_b":[-3.2354805326781317,0.00371386286221842]},{"parameter":0.523863470320239,"point":[2.654231310219106,1.570312502958462,0.4950314655552374],"uv_a":[6.098797261305364,1.570312502953125],"uv_b":[-3.2826853190327783,0.004231310219106277]},{"parameter":0.5476341347359772,"point":[2.654955567088826,1.5937500039354064,0.4911323006820845],"uv_a":[6.100266097951611,1.5937500039375],"uv_b":[-3.3302090477738595,0.004955567088825941]},{"parameter":0.5716295138820224,"point":[2.6558864643186353,1.6171875049266944,0.4860731309880169],"uv_a":[6.102171320575353,1.6171875049218751],"uv_b":[-3.3781682745809567,0.005886464318635376]},{"parameter":0.5959141965327819,"point":[2.657023784686102,1.6406250059205267,0.4798172648614164],"uv_a":[6.104526287239927,1.6406250059062502],"uv_b":[-3.4266891062114215,0.007023784686102097]},{"parameter":0.6205591362390966,"point":[2.6583672632416975,1.66406250690833,0.47231715379922057],"uv_a":[6.107348320847363,1.664062506890625],"uv_b":[-3.4759106622901106,0.008367263241697565]},{"parameter":0.645643803022921,"point":[2.6599165875972526,1.6875000078791036,0.4635124022444197],"uv_a":[6.1106594428997605,1.687500007875],"uv_b":[-3.525989445092976,0.009916587597252668]},{"parameter":0.6501883623418547,"point":[2.660215662637239,1.6916960397836611,0.46179284131799114],"uv_a":[6.111305878293199,1.691696039768817],"uv_b":[-3.535058891488621,0.010215662637238943]},{"parameter":0.6712602345253995,"point":[2.66167139832315,1.7109375088610022,0.4533269982602597],"uv_a":[6.1144873948445735,1.710937508859375],"uv_b":[-3.577105044204418,0.011671398323150228]},{"parameter":0.697511895932366,"point":[2.6636312892736353,1.7343750098339692,0.4416654330895639],"uv_a":[6.118867069463013,1.7343750098437498],"uv_b":[-3.6294678233735937,0.013631289273635439]},{"parameter":0.7245263911833103,"point":[2.6657958081649715,1.757812510851439,0.4284071768811314],"uv_a":[6.123842549443157,1.7578125108281248],"uv_b":[-3.683329614472148,0.015795808164971614]},{"parameter":0.7524581526080207,"point":[2.668164456906109,1.7812500117991514,0.41339863435515534],"uv_a":[6.129470076501057,1.7812500118124999],"uv_b":[-3.738999098744679,0.018164456906109017]},{"parameter":0.7566808755621816,"point":[2.66853225449655,1.7847180942725291,0.4110177693783217],"uv_a":[6.130362338140644,1.7847180942445768],"uv_b":[-3.7474124734459737,0.01853225449655005]},{"parameter":0.7815025894728219,"point":[2.670736692480739,1.8046875128299678,0.3964410668323627],"uv_a":[6.135822511238574,1.804687512796875],"uv_b":[-3.7968647745370725,0.020736692480739283]},{"parameter":0.8119078682451105,"point":[2.673511927131303,1.8281250137301894,0.37727175277559466],"uv_a":[6.142996291169094,1.82812501378125],"uv_b":[-3.8574307500729295,0.0235119271313029]},{"parameter":0.8374583704405275,"point":[2.6758798017644945,1.8468900596650144,0.3600934413561505],"uv_a":[6.1494187979880905,1.84689005966648],"uv_b":[-3.9083173303114336,0.02587980176449456]},{"parameter":0.8440158203862347,"point":[2.676489529532718,1.851562514733962,0.35553311839962887],"uv_a":[6.151122836532611,1.851562514765625],"uv_b":[-3.9213755059229314,0.02648952953271788]},{"parameter":0.878296572919611,"point":[2.679668824997465,1.8750000156940474,0.3307188962004861],"uv_a":[6.160388449313543,1.87500001575],"uv_b":[-3.9896547798564286,0.029668824997465038]},{"parameter":0.9066313740339027,"point":[2.682255636269859,1.8930588992139792,0.309038349905685],"uv_a":[6.168475257835536,1.8930588991854695],"uv_b":[-4.046095217365391,0.03225563626985917]},{"parameter":0.9154681118685938,"point":[2.6830490965935136,1.8984375167303162,0.3020720862093555],"uv_a":[6.171072038485683,1.898437516734375],"uv_b":[-4.063697530428579,0.03304909659351374]},{"parameter":0.9566760112783708,"point":[2.686629585655327,1.921875017694837,0.2683681603195926],"uv_a":[6.183625297356484,1.92187501771875],"uv_b":[-4.145824715609436,0.036629585655326924]},{"parameter":0.9690637587096833,"point":[2.6876574427069433,1.9283719520199973,0.2578710349665264],"uv_a":[6.187531717682981,1.9283719519789853],"uv_b":[-4.170515405258614,0.03765744270694338]},{"parameter":1.004062580127355,"point":[2.6904094928695583,1.9453125187070932,0.22736921664616086],"uv_a":[6.198874637593571,1.9453125187031248],"uv_b":[-4.240310477201132,0.04040949286955842]},{"parameter":1.0271667910844,"point":[2.6920832594763313,1.9553155784149696,0.20661007731490855],"uv_a":[6.206588177530428,1.9553155784108087],"uv_b":[-4.286401564583248,0.0420832594763314]},{"parameter":1.0625178429302367,"point":[2.6943879789281096,1.9687500197040324,0.1739925832035218],"uv_a":[6.218698924623721,1.9687500196874999],"uv_b":[-4.35696789203586,0.04438797892810964]},{"parameter":1.0823752028069469,"point":[2.6955314793972236,1.975278819574291,0.15527409180585766],"uv_a":[6.225644637377873,1.9752788195002098],"uv_b":[-4.396619305311677,0.045531479397223684]},{"parameter":1.1356481782814714,"point":[2.6980008502910633,1.9890895502642703,0.10388172037675547],"uv_a":[6.2447010972253185,1.9890895502702768],"uv_b":[-4.503101029766185,0.04800085029106338]},{"parameter":1.1517975554941897,"point":[2.698564165559826,1.9921875207400295,0.08804228733141027],"uv_a":[6.250571271133383,1.9921875206718749],"uv_b":[-4.535381530439569,0.04856416555982612]},{"parameter":1.1877571590697558,"point":[2.6994904754362534,1.99724121612939,0.05245162560341963],"uv_a":[6.263757557072764,1.9972412161754107],"uv_b":[-4.6072923654263125,0.04949047543625351]},{"parameter":1.2392826767807936,"point":[2.6999998138937857,1.9999989950868042,0.0010024836771621234],"uv_a":[6.282814016920209,1.999998995148141],"uv_b":[-4.710384011687316,0.04999981389378583]},{"parameter":1.2907975115920034,"point":[2.6995286807036782,1.9974485882753887,-0.05044702228975368],"uv_a":[6.301870476767655,1.9974485883102902],"uv_b":[-4.813454990847523,0.04952868070367833]},{"parameter":1.3287713593431787,"point":[2.6985641655431207,1.9921875206956527,-0.08804228783551027],"uv_a":[6.315799343415534,1.9921875206718749],"uv_b":[-4.889396431337886,0.04856416554312082]},{"parameter":1.3428722316668074,"point":[2.698077246952141,1.9895108073171726,-0.10187820905755031],"uv_a":[6.3209269366151,1.989510807279073],"uv_b":[-4.917582290131343,0.048077246952141106]},{"parameter":1.3960865449589484,"point":[2.6956460397094473,1.9759281157955804,-0.153272399989274],"uv_a":[6.339983396462546,1.9759281157986295],"uv_b":[-5.023949920612639,0.04564603970944736]},{"parameter":1.4180509198041897,"point":[2.694387978944502,1.9687500197454413,-0.17399258296503345],"uv_a":[6.347671689641234,1.9687500196874999],"uv_b":[-5.0678100682575336,0.044387978944502304]},{"parameter":1.45120479941553,"point":[2.6922359418385375,1.956217455224096,-0.20461093197499977],"uv_a":[6.359039856309991,1.956217455164631],"uv_b":[-5.133990066703966,0.04223594183853763]},{"parameter":1.4765068365529088,"point":[2.690409492858698,1.945312518681071,-0.227369216761379],"uv_a":[6.367495976813366,1.9453125187031248],"uv_b":[-5.184467483797146,0.040409492858698215]},{"parameter":1.5091728293897624,"point":[2.687848191674414,1.929567109430189,-0.2558751619620383],"uv_a":[6.378096316157436,1.9295671094416216],"uv_b":[-5.24960984875082,0.037848191674414]},{"parameter":1.5238945235008194,"point":[2.6866295856806124,1.921875017752155,-0.2683681601029221],"uv_a":[6.38274531690847,1.92187501771875],"uv_b":[-5.278953244732782,0.036629585680612475]},{"parameter":1.5651024229164958,"point":[2.683049096621975,1.898437516791168,-0.3020720860026923],"uv_a":[6.39529857577927,1.898437516734375],"uv_b":[-5.361080429937905,0.03304909662197497]},{"parameter":1.5714001120926695,"point":[2.682484382574458,1.8946168557621426,-0.3070464740359601],"uv_a":[6.397152776004882,1.8946168558170062],"uv_b":[-5.373625150412406,0.03248438257445807]},{"parameter":1.6022729373260531,"point":[2.679668825028625,1.8750000157566713,-0.3307188960032387],"uv_a":[6.405982164951411,1.87500001575],"uv_b":[-5.4351231805342355,0.029668825028625]},{"parameter":1.636553689865318,"point":[2.676489529566215,1.851562514797001,-0.3555331182112076],"uv_a":[6.415247777732344,1.851562514765625],"uv_b":[-5.50340245449183,0.026489529566215086]},{"parameter":1.6402429620635373,"point":[2.676146462339821,1.8489410950732177,-0.3581062860764056],"uv_a":[6.416209235852327,1.8489410950441387],"uv_b":[-5.5107490441552915,0.026146462339820875]},{"parameter":1.6686606523699568,"point":[2.6735119271668495,1.8281250137925558,-0.37727175259540324],"uv_a":[6.423374323095858,1.82812501378125],"uv_b":[-5.567347210365832,0.023511927166849578]},{"parameter":1.6990659313877927,"point":[2.6707366924618046,1.8046875127991515,-0.39644106691982683],"uv_a":[6.430548103168361,1.804687512796875],"uv_b":[-5.6279131863877705,0.02073669246180465]},{"parameter":1.720412352146336,"point":[2.6688367325081037,1.7875578286620981,-0.40903605611156013],"uv_a":[6.435265695699773,1.7875578286759928],"uv_b":[-5.670439788281108,0.018836732508103804]},{"parameter":1.7281112220715473,"point":[2.668164456886363,1.781250011769516,-0.41339863443903974],"uv_a":[6.436900537905881,1.7812500118124999],"uv_b":[-5.685778862168076,0.018164456886363034]},{"parameter":1.7560429834932776,"point":[2.665795808144511,1.7578125108233147,-0.42840717696171626],"uv_a":[6.442528064963776,1.7578125108281248],"uv_b":[-5.7414483464285295,0.015795808144511092]},{"parameter":1.7830574787412532,"point":[2.6636312892525402,1.7343750098076303,-0.4416654331671527],"uv_a":[6.447503544943923,1.7343750098437498],"uv_b":[-5.795310137515057,0.013631289252540313]},{"parameter":1.8093091400611427,"point":[2.661671398333681,1.7109375088728267,-0.4533269982238437],"uv_a":[6.4518832194913704,1.710937508859375],"uv_b":[-5.847672916512794,0.011671398333680916]},{"parameter":1.8252799228484844,"point":[2.6605578475175835,1.6963875252620246,-0.45981728976936054],"uv_a":[6.454322155547218,1.6963875252814207],"uv_b":[-5.879538094132449,0.010557847517583596]},{"parameter":1.834926323041138,"point":[2.6599165875751147,1.6875000078570217,-0.46351240231687274],"uv_a":[6.455711171507174,1.687500007875],"uv_b":[-5.898788515771685,0.009916587575114821]},{"parameter":1.8600109898219923,"point":[2.658367263219139,1.6640625068886525,-0.4723171538695391],"uv_a":[6.459022293559571,1.664062506890625],"uv_b":[-5.948867298562591,0.008367263219139165]},{"parameter":1.884655929525338,"point":[2.657023784663185,1.640625005903401,-0.4798172649298883],"uv_a":[6.461844327167008,1.6406250059062502],"uv_b":[-5.998088854629342,0.007023784663184873]},{"parameter":1.908940612173128,"point":[2.6558864642954187,1.617187504912243,-0.48607313105492644],"uv_a":[6.464199293831582,1.6171875049218751],"uv_b":[-6.046609686247884,0.005886464295418836]},{"parameter":1.9329359913162028,"point":[2.654955567065369,1.5937500039237296,-0.49113230074771436],"uv_a":[6.466104516455323,1.5937500039375],"uv_b":[-6.09456891304307,0.004955567065369149]},{"parameter":1.956706655728973,"point":[2.654231310195462,1.5703125029496365,-0.4950314656198781],"uv_a":[6.467573353101572,1.570312502953125],"uv_b":[-6.142092641772257,0.004231310195462079]},{"parameter":1.980312529562813,"point":[2.653713862838442,1.546875001969282,-0.49779788487367427],"uv_a":[6.468615722185245,1.54687500196875],"uv_b":[-6.1892974281150135,0.0037138628384418837]},{"parameter":2.0038102660059347,"point":[2.6534033459906636,1.5234375009858294,-0.49945038143594533],"uv_a":[6.469238469495507,1.523437500984375],"uv_b":[-6.236293122069564,0.00340334599066372]},{"parameter":2.0272544390138956,"point":[2.6532998323121433,1.5,-0.49999999992617605],"uv_a":[6.469445610700496,1.5],"uv_b":[-6.283185307179586,0.0032998323121433693]},{"parameter":2.0506986120218564,"point":[2.6534033459906636,1.4765624990141706,-0.49945038143594533],"uv_a":[6.469238469495507,1.476562499015625],"uv_b":[-6.330077492289608,0.00340334599066372]},{"parameter":2.0741963484649784,"point":[2.653713862838442,1.453124998030718,-0.49779788487367427],"uv_a":[6.468615722185245,1.45312499803125],"uv_b":[-6.377073186244159,0.0037138628384418837]},{"parameter":2.0978022222988186,"point":[2.654231310195462,1.4296874970503635,-0.4950314656198781],"uv_a":[6.467573353101572,1.429687497046875],"uv_b":[-6.424277972586916,0.004231310195462079]},{"parameter":2.1215728867115886,"point":[2.654955567065369,1.4062499960762704,-0.49113230074771436],"uv_a":[6.466104516455323,1.4062499960625],"uv_b":[-6.471801701316102,0.004955567065369149]},{"parameter":2.1455682658546635,"point":[2.6558864642954187,1.382812495087757,-0.48607313105492644],"uv_a":[6.464199293831582,1.3828124950781249],"uv_b":[-6.519760928111288,0.005886464295418836]},{"parameter":2.1698529485024536,"point":[2.657023784663185,1.359374994096599,-0.4798172649298883],"uv_a":[6.461844327167008,1.3593749940937498],"uv_b":[-6.568281759729831,0.007023784663184873]},{"parameter":2.1944978882057993,"point":[2.658367263219139,1.3359374931113475,-0.4723171538695391],"uv_a":[6.459022293559571,1.335937493109375],"uv_b":[-6.617503315796581,0.008367263219139165]},{"parameter":2.219582554986654,"point":[2.6599165875751147,1.3124999921429783,-0.46351240231687274],"uv_a":[6.455711171507174,1.312499992125],"uv_b":[-6.667582098587488,0.009916587575114821]},{"parameter":2.2292289551793076,"point":[2.6605578475175835,1.3036124747379754,-0.45981728976936054],"uv_a":[6.454322155547218,1.3036124747185793],"uv_b":[-6.686832520226724,0.010557847517583596]},{"parameter":2.245199737966649,"point":[2.661671398333681,1.2890624911271733,-0.4533269982238437],"uv_a":[6.4518832194913704,1.289062491140625],"uv_b":[-6.718697697846379,0.011671398333680916]},{"parameter":2.2714513992865384,"point":[2.6636312892525402,1.2656249901923697,-0.4416654331671527],"uv_a":[6.447503544943923,1.2656249901562502],"uv_b":[-6.771060476844116,0.013631289252540313]},{"parameter":2.298465894534514,"point":[2.665795808144511,1.2421874891766853,-0.42840717696171626],"uv_a":[6.442528064963776,1.2421874891718752],"uv_b":[-6.824922267930643,0.015795808144511092]},{"parameter":2.3263976559562445,"point":[2.668164456886363,1.218749988230484,-0.41339863443903974],"uv_a":[6.436900537905881,1.2187499881875001],"uv_b":[-6.880591752191097,0.018164456886363034]},{"parameter":2.334096525881456,"point":[2.6688367325081037,1.2124421713379019,-0.40903605611156013],"uv_a":[6.435265695699773,1.2124421713240072],"uv_b":[-6.8959308260780645,0.018836732508103804]},{"parameter":2.3554429466399993,"point":[2.6707366924618046,1.1953124872008485,-0.39644106691982683],"uv_a":[6.430548103168361,1.195312487203125],"uv_b":[-6.938457427971402,0.02073669246180465]},{"parameter":2.3858482256578353,"point":[2.6735119271668495,1.1718749862074442,-0.37727175259540324],"uv_a":[6.423374323095858,1.17187498621875],"uv_b":[-6.99902340399334,0.023511927166849578]},{"parameter":2.4142659159642545,"point":[2.676146462339821,1.1510589049267823,-0.3581062860764056],"uv_a":[6.416209235852327,1.1510589049558613],"uv_b":[-7.055621570203881,0.026146462339820875]},{"parameter":2.4179551881624737,"point":[2.676489529566215,1.148437485202999,-0.3555331182112076],"uv_a":[6.415247777732344,1.148437485234375],"uv_b":[-7.062968159867342,0.026489529566215086]},{"parameter":2.4522359407017382,"point":[2.679668825028625,1.1249999842433287,-0.3307188960032387],"uv_a":[6.405982164951411,1.12499998425],"uv_b":[-7.131247433824937,0.029668825028625]},{"parameter":2.483108765935122,"point":[2.682484382574458,1.1053831442378574,-0.3070464740359601],"uv_a":[6.397152776004882,1.1053831441829938],"uv_b":[-7.192745463946767,0.03248438257445807]},{"parameter":2.4894064551112955,"point":[2.683049096621975,1.101562483208832,-0.3020720860026923],"uv_a":[6.39529857577927,1.101562483265625],"uv_b":[-7.205290184421267,0.03304909662197497]},{"parameter":2.5306143545269717,"point":[2.6866295856806124,1.078124982247845,-0.2683681601029221],"uv_a":[6.38274531690847,1.07812498228125],"uv_b":[-7.28741736962639,0.036629585680612475]},{"parameter":2.545336048638029,"point":[2.687848191674414,1.070432890569811,-0.2558751619620383],"uv_a":[6.378096316157436,1.0704328905583784],"uv_b":[-7.316760765608352,0.037848191674414]},{"parameter":2.5780020414748828,"point":[2.690409492858698,1.0546874813189289,-0.22736921676137894],"uv_a":[6.367495976813366,1.054687481296875],"uv_b":[-7.381903130562026,0.040409492858698215]},{"parameter":2.6033040786122617,"point":[2.6922359418385375,1.043782544775904,-0.20461093197499977],"uv_a":[6.359039856309991,1.043782544835369],"uv_b":[-7.432380547655207,0.04223594183853763]},{"parameter":2.636457958223602,"point":[2.694387978944502,1.0312499802545585,-0.17399258296503345],"uv_a":[6.347671689641234,1.0312499803125],"uv_b":[-7.498560546101639,0.044387978944502304]},{"parameter":2.658422333068843,"point":[2.6956460397094473,1.0240718842044194,-0.15327239998927394],"uv_a":[6.339983396462546,1.02407188420137],"uv_b":[-7.542420693746534,0.04564603970944736]},{"parameter":2.711636646360984,"point":[2.698077246952141,1.0104891926828272,-0.10187820905755025],"uv_a":[6.3209269366151,1.0104891927209265],"uv_b":[-7.648788324227829,0.048077246952141106]},{"parameter":2.7257375186846122,"point":[2.6985641655431207,1.007812479304347,-0.08804228783551021],"uv_a":[6.315799343415534,1.007812479328125],"uv_b":[-7.6769741830212865,0.04856416554312082]},{"parameter":2.7637113664357873,"point":[2.6995286807036782,1.0025514117246113,-0.05044702228975368],"uv_a":[6.301870476767655,1.0025514116897098],"uv_b":[-7.752915623511649,0.04952868070367833]},{"parameter":2.8152262012469973,"point":[2.6999998138937857,1.0000010049131958,0.0010024836771621234],"uv_a":[6.282814016920209,1.000001004851859],"uv_b":[-7.855986602671856,0.04999981389378583]},{"parameter":2.866751718958035,"point":[2.6994904754362534,1.00275878387061,0.05245162560341963],"uv_a":[6.263757557072764,1.0027587838245893],"uv_b":[-7.95907824893286,0.04949047543625351]},{"parameter":2.902711322533601,"point":[2.698564165559826,1.0078124792599703,0.08804228733141027],"uv_a":[6.250571271133383,1.007812479328125],"uv_b":[-8.030989083919604,0.04856416555982612]},{"parameter":2.9188606997463196,"point":[2.6980008502910633,1.0109104497357295,0.10388172037675542],"uv_a":[6.2447010972253185,1.0109104497297228],"uv_b":[-8.063269584592987,0.04800085029106338]},{"parameter":2.972133675220844,"point":[2.6955314793972236,1.0247211804257086,0.1552740918058576],"uv_a":[6.225644637377873,1.0247211804997898],"uv_b":[-8.169751309047495,0.045531479397223684]},{"parameter":2.9919910350975543,"point":[2.6943879789281096,1.0312499802959674,0.17399258320352173],"uv_a":[6.218698924623721,1.0312499803125],"uv_b":[-8.209402722323311,0.04438797892810964]},{"parameter":3.027342086943391,"point":[2.6920832594763313,1.0446844215850304,0.20661007731490855],"uv_a":[6.206588177530428,1.0446844215891913],"uv_b":[-8.279969049775923,0.0420832594763314]},{"parameter":3.050446297900436,"point":[2.6904094928695583,1.0546874812929066,0.22736921664616086],"uv_a":[6.198874637593571,1.054687481296875],"uv_b":[-8.326060137158041,0.04040949286955842]},{"parameter":3.085445119318108,"point":[2.6876574427069433,1.0716280479800027,0.2578710349665264],"uv_a":[6.187531717682981,1.0716280480210147],"uv_b":[-8.395855209100558,0.03765744270694338]},{"parameter":3.09783286674942,"point":[2.686629585655327,1.078124982305163,0.2683681603195926],"uv_a":[6.183625297356484,1.07812498228125],"uv_b":[-8.420545898749737,0.036629585655326924]},{"parameter":3.139040766159197,"point":[2.6830490965935136,1.1015624832696838,0.3020720862093555],"uv_a":[6.171072038485683,1.101562483265625],"uv_b":[-8.502673083930594,0.03304909659351374]},{"parameter":3.147877503993888,"point":[2.682255636269859,1.1069411007860208,0.309038349905685],"uv_a":[6.168475257835536,1.1069411008145305],"uv_b":[-8.520275396993782,0.03225563626985917]}],"residual_tolerance":1.575e-8,"surfaces":[6,16],"uv_tubes":[[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-2.124209214796271,"lo":-2.406411402341082},{"hi":0.037429258814647426,"lo":0.02449520245267678}],[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.149609360283598,"lo":1.1238281092007771},{"hi":-2.1569719794561633,"lo":-2.4983683491236492},{"hi":0.03602741592695935,"lo":0.02013093860322357}],[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-2.3356934500336592,"lo":-2.4009843280911483},{"hi":0.027708985069164527,"lo":0.024660346228047914}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-2.2730948163911444,"lo":-2.527527717583665},{"hi":0.030615551030877874,"lo":0.018776177864919585}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.196484362252348,"lo":1.1707031111695272},{"hi":-2.304622606034943,"lo":-2.6074524837142277},{"hi":0.029062396432430138,"lo":0.015186223179612046}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-2.387415930460316,"lo":-2.63467743591581},{"hi":0.02514556844911775,"lo":0.014123378528171582}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-2.5189460843310227,"lo":-2.561012957837497},{"hi":0.019267849677432114,"lo":0.017428861725226952}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.2433593642210983,"lo":1.217578113138277},{"hi":-2.4328472398898455,"lo":-2.7111946612525},{"hi":0.02290175438838382,"lo":0.01105851068269681}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.2667968652054735,"lo":1.2410156141226518},{"hi":-2.49213211051033,"lo":-2.761441066003101},{"hi":0.020124845947643966,"lo":0.009302251490963087}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.2902343661898483,"lo":1.2644531151070268},{"hi":-2.5489919254676408,"lo":-2.81080582131352},{"hi":0.01755107117460586,"lo":0.007751616422179808}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-2.6219879575435745,"lo":-2.832218721122559},{"hi":0.0145828696949728,"lo":0.007304191265416371}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-2.729987522899675,"lo":-2.7753347548779006},{"hi":0.01081381271721149,"lo":0.00931843751728012}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.3371093681585986,"lo":1.3113281170757765},{"hi":-2.65703829648088,"lo":-2.907432210495206},{"hi":0.013015236308362876,"lo":0.005268614530587357}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.3605468691429734,"lo":1.3347656180601515},{"hi":-2.7088315327320975,"lo":-2.954939313125543},{"hi":0.0110542203528885,"lo":0.004336827574911162}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.3839843701273484,"lo":1.3582031190445263},{"hi":-2.759454537707235,"lo":-3.002058695859559},{"hi":0.009298425421035539,"lo":0.003611823583701934}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.4074218711117237,"lo":1.381640620028901},{"hi":-2.809098578984435,"lo":-3.048894713019921},{"hi":0.007748258778254247,"lo":0.00309377262920707}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.4308593720960987,"lo":1.4050781210132761},{"hi":-2.8579288019235642,"lo":-3.0955474456289704},{"hi":0.006404080828265268,"lo":0.002782796479666949}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.454296873080474,"lo":1.428515621997651},{"hi":-2.906090415437515,"lo":-3.1421143472107476},{"hi":0.00526620493288199,"lo":0.0026789681484427064}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.477734374064849,"lo":1.451953122982026},{"hi":-2.9537133865332663,"lo":-3.1886918564537368},{"hi":0.004334896628529705,"lo":0.002782312212751492}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.501171875049224,"lo":1.475390623966401},{"hi":-3.0009160982770595,"lo":-3.235377023798282},{"hi":0.003610373407122225,"lo":0.003092804836973606}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.5246093760335993,"lo":1.4988281249507758},{"hi":-3.047808283381304,"lo":-3.2822692089025267},{"hi":0.003610373407122225,"lo":0.003092804836973606}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.5480468770179743,"lo":1.5222656259351508},{"hi":-3.0944934507258495,"lo":-3.32947192064632},{"hi":0.004334896628529705,"lo":0.002782312212751492}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.5714843780023493,"lo":1.5457031269195258},{"hi":-3.1410709599688387,"lo":-3.3770948917420713},{"hi":0.00526620493288199,"lo":0.0026789681484427064}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.5949218789867246,"lo":1.5691406279039006},{"hi":-3.187637861550616,"lo":-3.425256505256022},{"hi":0.006404080828265268,"lo":0.002782796479666949}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.6183593799710996,"lo":1.5925781288882757},{"hi":-3.234290594159665,"lo":-3.474086728195151},{"hi":0.007748258778254247,"lo":0.00309377262920707}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.6417968809554748,"lo":1.6160156298726505},{"hi":-3.281126611320027,"lo":-3.523730769472351},{"hi":0.009298425421035539,"lo":0.003611823583701934}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.6652343819398496,"lo":1.6394531308570255},{"hi":-3.3282459940540434,"lo":-3.5743537744474887},{"hi":0.0110542203528885,"lo":0.004336827574911162}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.6886718829242247,"lo":1.6628906318414003},{"hi":-3.37575309668438,"lo":-3.6261470106987064},{"hi":0.013015236308362876,"lo":0.005268614530587357}],[{"hi":6.112258701285593,"lo":6.091296595453359},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-3.5078505523016856,"lo":-3.553197784279911},{"hi":0.01081381271721149,"lo":0.00931843751728012}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-3.450966586057027,"lo":-3.6611973496360117},{"hi":0.0145828696949728,"lo":0.007304191265416371}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.7355468848929747,"lo":1.7097656338101501},{"hi":-3.472379485866066,"lo":-3.7341933817119455},{"hi":0.01755107117460586,"lo":0.007751616422179808}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.7589843858773497,"lo":1.733203134794525},{"hi":-3.5217442411764854,"lo":-3.791053196669256},{"hi":0.020124845947643966,"lo":0.009302251490963087}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.782421886861725,"lo":1.7566406357788997},{"hi":-3.571990645927086,"lo":-3.8503380672897407},{"hi":0.02290175438838382,"lo":0.01105851068269681}],[{"hi":6.131315161133038,"lo":6.110353055300805},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-3.722172349342089,"lo":-3.7642392228485635},{"hi":0.019267849677432114,"lo":0.017428861725226952}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-3.648507871263776,"lo":-3.89576937671927},{"hi":0.02514556844911775,"lo":0.014123378528171582}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.8292968888304753,"lo":1.8035156377476496},{"hi":-3.6757328234653586,"lo":-3.9785627011446434},{"hi":0.029062396432430138,"lo":0.015186223179612046}],[{"hi":6.150371620980485,"lo":6.12940951514825},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-3.755657589595921,"lo":-4.010090490788442},{"hi":0.030615551030877874,"lo":0.018776177864919585}],[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-3.882200979088438,"lo":-3.947491857145927},{"hi":0.027708985069164527,"lo":0.024660346228047914}],[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.8761718907992253,"lo":1.8503906397163996},{"hi":-3.784816958055937,"lo":-4.126213327723423},{"hi":0.03602741592695935,"lo":0.02013093860322357}],[{"hi":6.16942808082793,"lo":6.1484659749956965},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-3.876773904838504,"lo":-4.158976092383315},{"hi":0.037429258814647426,"lo":0.02449520245267678}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-4.010890591239015,"lo":-4.098902156554955},{"hi":0.03463601724082288,"lo":0.030668715622550025}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.9230468927679756,"lo":1.8972656416851494},{"hi":-3.8994431600668644,"lo":-4.31007908597115},{"hi":0.043790563778953295,"lo":0.025888118469887367}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-4.096443336311079,"lo":-4.219896784556971},{"hi":0.03971315681017629,"lo":0.034573871552094015}],[{"hi":6.207541000522822,"lo":6.186578894690587},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-4.030925261373579,"lo":-4.3799006210861675},{"hi":0.04591359319478849,"lo":0.0321533423817133}],[{"hi":6.207541000522822,"lo":6.186578894690587},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.148128302436899,"lo":-4.378583739347481},{"hi":0.045430792689877375,"lo":0.037061959656012444}],[{"hi":6.226597460370267,"lo":6.205635354538034},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.145268909678024,"lo":-4.498100546941084},{"hi":0.04899741783166611,"lo":0.03747382057277493}],[{"hi":6.226597460370267,"lo":6.205635354538034},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.277665065484228,"lo":-4.475922131863309},{"hi":0.047818480335451774,"lo":0.04210097798988155}],[{"hi":6.245653920217713,"lo":6.224691814385479},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.18365585640266,"lo":-4.716064478675202},{"hi":0.05293959207874277,"lo":0.040592737609544294}],[{"hi":6.264710380065159,"lo":6.243748274232924},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.438540028419418,"lo":-4.599942531786336},{"hi":0.04969079609735161,"lo":0.046874219753537893}],[{"hi":6.264710380065159,"lo":6.243748274232924},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.3915598604660815,"lo":-4.7511140353998},{"hi":0.05134309518910829,"lo":0.046711545806971344}],[{"hi":6.283766839912604,"lo":6.262804734080369},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.401109072904305,"lo":-4.916567304209324},{"hi":0.05101849080885046,"lo":0.04847179852118888}],[{"hi":6.30282329976005,"lo":6.281861193927814},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.5042420533669025,"lo":-5.019596949167937},{"hi":0.05094208027400082,"lo":0.048586414323463334}],[{"hi":6.321879759607495,"lo":6.30091765377526},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.661572109866798,"lo":-5.041279312318611},{"hi":0.05145771102479335,"lo":0.0466351352220058}],[{"hi":6.321879759607495,"lo":6.30091765377526},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.833024713750971,"lo":-4.973954007718258},{"hi":0.049538002725080243,"lo":0.04710340977018168}],[{"hi":6.340936219454941,"lo":6.319974113622705},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.704847029168753,"lo":-5.236685181575229},{"hi":0.052939661437528596,"lo":0.04078362522405987}],[{"hi":6.359992679302386,"lo":6.339030573470151},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.936229625322849,"lo":-5.1555303635473235},{"hi":0.048162161239337475,"lo":0.04187185741461219}],[{"hi":6.359992679302386,"lo":6.339030573470151},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.935450071364669,"lo":-5.26635006359683},{"hi":0.04869205315643166,"lo":0.03793186762660827}],[{"hi":6.379049139149831,"lo":6.358087033317596},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-5.033035232517605,"lo":-5.285422317983507},{"hi":0.04588883979821645,"lo":0.03675659489901939}],[{"hi":6.379049139149831,"lo":6.358087033317596},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-5.054182753889798,"lo":-5.379894578658169},{"hi":0.045532095227266645,"lo":0.03272558930584557}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-5.190923056786897,"lo":-5.337640036696706},{"hi":0.04028540366201705,"lo":0.034192373693009426}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.9230468927679756,"lo":1.8972656416851494},{"hi":-5.114698874322536,"lo":-5.525334800348151},{"hi":0.04379056379788748,"lo":0.025888118504699964}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-5.335990988988904,"lo":-5.398714591361407},{"hi":0.034178524717008774,"lo":0.03135495447942427}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-5.250629090168746,"lo":-5.558119240777895},{"hi":0.03811549766612421,"lo":0.024037709936958862}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.8761718907992253,"lo":1.8503906397163996},{"hi":-5.298564632619046,"lo":-5.63996100240702},{"hi":0.03602741595344483,"lo":0.020130938641395257}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-5.488709275164908,"lo":-5.525442223482214},{"hi":0.02717566401900351,"lo":0.025460327887032452}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-5.39755271173421,"lo":-5.680543542786913},{"hi":0.03141553268576347,"lo":0.018242856820906983}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.8292968888304753,"lo":1.8035156377476496},{"hi":-5.446215258321955,"lo":-5.749045138431647},{"hi":0.02906239657693943,"lo":0.015186223051714798}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-5.542859982601096,"lo":-5.755492992067783},{"hi":0.024536612369206345,"lo":0.01503681260070211}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-5.639761640507173,"lo":-5.716457009942011},{"hi":0.020181283751585344,"lo":0.016819905642881494}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.782421886861725,"lo":1.7566406357788997},{"hi":-5.574439893647168,"lo":-5.852787314949437},{"hi":0.022901754370066918,"lo":0.011058510660807208}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.7589843858773497,"lo":1.733203134794525},{"hi":-5.633724764255475,"lo":-5.9030337196881115},{"hi":0.02012484592845265,"lo":0.009302251468598755}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.7355468848929747,"lo":1.7097656338101501},{"hi":-5.690584579519583,"lo":-5.952398474508268},{"hi":0.017551071090259107,"lo":0.007751616495962121}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-5.783942561273484,"lo":-5.943268449371758},{"hi":0.013898499965875555,"lo":0.008330745885388957}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-5.841037250853977,"lo":-5.9372893590501565},{"hi":0.011840367402521146,"lo":0.008634067690177272}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.6886718829242247,"lo":1.6628906318414003},{"hi":-5.798630950189871,"lo":-6.049024864144405},{"hi":0.013015236287066134,"lo":0.005268614507187852}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.6652343819398496,"lo":1.6394531308570255},{"hi":-5.850424186429091,"lo":-6.096531966762842},{"hi":0.011054220331047748,"lo":0.00433682755127629}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.6417968809554748,"lo":1.6160156298726505},{"hi":-5.901047191392256,"lo":-6.14365134948497},{"hi":0.009298425398716947,"lo":0.003611823559886762}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.6183593799710996,"lo":1.5925781288882757},{"hi":-5.950691232657513,"lo":-6.190487366633442},{"hi":0.007748258755518211,"lo":0.0030937726052697734}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.5949218789867246,"lo":1.5691406279039006},{"hi":-5.999521455584698,"lo":-6.237140099230629},{"hi":0.006404080805183288,"lo":0.00278279645564794}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.5714843780023493,"lo":1.5457031269195258},{"hi":-6.047683069086743,"lo":-6.283707000800527},{"hi":0.00526620490950247,"lo":0.002678968124401493}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.5480468770179743,"lo":1.5222656259351508},{"hi":-6.095306040205912,"lo":-6.330284509978665},{"hi":0.0043348965339982115,"lo":0.002782312295107392}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.5246093760335993,"lo":1.4988281249507758},{"hi":-6.14250875184952,"lo":-6.3769696773996305},{"hi":0.003610373347704421,"lo":0.003092804955102668}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.501171875049224,"lo":1.475390623966401},{"hi":-6.189400936959542,"lo":-6.423861862509653},{"hi":0.003610373347704421,"lo":0.003092804955102668}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.477734374064849,"lo":1.451953122982026},{"hi":-6.236086104380507,"lo":-6.47106457415326},{"hi":0.0043348965339982115,"lo":0.002782312295107392}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.454296873080474,"lo":1.428515621997651},{"hi":-6.282663613558645,"lo":-6.51868754527243},{"hi":0.00526620490950247,"lo":0.002678968124401493}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.4308593720960987,"lo":1.4050781210132761},{"hi":-6.329230515128543,"lo":-6.566849158774475},{"hi":0.006404080805183288,"lo":0.00278279645564794}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.4074218711117237,"lo":1.381640620028901},{"hi":-6.375883247725731,"lo":-6.61567938170166},{"hi":0.007748258755518211,"lo":0.0030937726052697734}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.3839843701273484,"lo":1.3582031190445263},{"hi":-6.422719264874202,"lo":-6.6653234229669165},{"hi":0.009298425398716947,"lo":0.003611823559886762}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.3605468691429734,"lo":1.3347656180601515},{"hi":-6.4698386475963305,"lo":-6.715946427930081},{"hi":0.011054220331047748,"lo":0.00433682755127629}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.3371093681585986,"lo":1.3113281170757765},{"hi":-6.517345750214767,"lo":-6.7677396641693015},{"hi":0.013015236287066134,"lo":0.005268614507187852}],[{"hi":6.4743314383870585,"lo":6.453369332554823},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-6.629081255309016,"lo":-6.725333363505196},{"hi":0.011840367402521146,"lo":0.008634067690177272}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-6.623102164987414,"lo":-6.782428053085688},{"hi":0.013898499965875555,"lo":0.008330745885388957}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.2902343661898483,"lo":1.2644531151070268},{"hi":-6.613972139850905,"lo":-6.87578603483959},{"hi":0.017551071090259107,"lo":0.007751616495962121}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.2667968652054735,"lo":1.2410156141226518},{"hi":-6.663336894671061,"lo":-6.932645850103698},{"hi":0.02012484592845265,"lo":0.009302251468598755}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.2433593642210983,"lo":1.217578113138277},{"hi":-6.713583299409735,"lo":-6.991930720712005},{"hi":0.022901754370066918,"lo":0.011058510660807208}],[{"hi":6.455274978539613,"lo":6.434312872707378},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-6.849913604417162,"lo":-6.926608973852},{"hi":0.020181283751585344,"lo":0.016819905642881494}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-6.81087762229139,"lo":-7.023510631758077},{"hi":0.024536612369206345,"lo":0.01503681260070211}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.196484362252348,"lo":1.1707031111695272},{"hi":-6.817325475927525,"lo":-7.120155356037217},{"hi":0.02906239657693943,"lo":0.015186223051714798}],[{"hi":6.436218518692168,"lo":6.415256412859932},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-6.885827071572259,"lo":-7.168817902624962},{"hi":0.03141553268576347,"lo":0.018242856820906983}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-7.040928390876958,"lo":-7.077661339194265},{"hi":0.02717566401900351,"lo":0.025460327887032452}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.149609360283598,"lo":1.1238281092007771},{"hi":-6.9264096119521525,"lo":-7.267805981740127},{"hi":0.03602741595344483,"lo":0.020130938641395257}],[{"hi":6.417162058844722,"lo":6.396199953012487},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-7.0082513735812775,"lo":-7.315741524190426},{"hi":0.03811549766612421,"lo":0.024037709936958862}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-7.1676560229977655,"lo":-7.230379625370269},{"hi":0.034178524717008774,"lo":0.03135495447942427}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.1027343583148477,"lo":1.0769531072320273},{"hi":-7.041035814011021,"lo":-7.451671740036637},{"hi":0.04379056379788748,"lo":0.025888118504699964}],[{"hi":6.398105598997277,"lo":6.3771434931650415},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-7.228730577662467,"lo":-7.375447557572276},{"hi":0.04028540366201705,"lo":0.034192373693009426}],[{"hi":6.379049139149831,"lo":6.358087033317596},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-7.186476035701004,"lo":-7.512187860469375},{"hi":0.045532095227266645,"lo":0.03272558930584557}],[{"hi":6.379049139149831,"lo":6.358087033317596},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-7.280948296375666,"lo":-7.533335381841567},{"hi":0.04588883979821645,"lo":0.03675659489901939}],[{"hi":6.359992679302386,"lo":6.339030573470151},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-7.300020550762342,"lo":-7.630920542994503},{"hi":0.04869205315643166,"lo":0.03793186762660827}],[{"hi":6.359992679302386,"lo":6.339030573470151},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.410840250811849,"lo":-7.630140989036324},{"hi":0.048162161239337475,"lo":0.04187185741461219}],[{"hi":6.340936219454941,"lo":6.319974113622705},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.329685432783943,"lo":-7.86152358519042},{"hi":0.052939661437528596,"lo":0.04078362522405987}],[{"hi":6.321879759607495,"lo":6.30091765377526},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.5924166066409144,"lo":-7.733345900608201},{"hi":0.049538002725080243,"lo":0.04710340977018168}],[{"hi":6.321879759607495,"lo":6.30091765377526},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.525091302040561,"lo":-7.9047985044923745},{"hi":0.05145771102479335,"lo":0.0466351352220058}],[{"hi":6.30282329976005,"lo":6.281861193927814},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.546773665191235,"lo":-8.06212856099227},{"hi":0.05094208027400082,"lo":0.048586414323463334}],[{"hi":6.283766839912604,"lo":6.262804734080369},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.649803310149848,"lo":-8.165261541454868},{"hi":0.05101849080885046,"lo":0.04847179852118888}],[{"hi":6.264710380065159,"lo":6.243748274232924},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.815256578959373,"lo":-8.174810753893091},{"hi":0.05134309518910829,"lo":0.046711545806971344}],[{"hi":6.264710380065159,"lo":6.243748274232924},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.966428082572836,"lo":-8.127830585939755},{"hi":0.04969079609735161,"lo":0.046874219753537893}],[{"hi":6.245653920217713,"lo":6.224691814385479},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.850306135683972,"lo":-8.38271475795651},{"hi":0.05293959207874277,"lo":0.040592737609544294}],[{"hi":6.226597460370267,"lo":6.205635354538034},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-8.090448482495862,"lo":-8.288705548874944},{"hi":0.047818480335451774,"lo":0.04210097798988155}],[{"hi":6.226597460370267,"lo":6.205635354538034},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-8.068270067418087,"lo":-8.421101704681147},{"hi":0.04899741783166611,"lo":0.03747382057277493}],[{"hi":6.207541000522822,"lo":6.186578894690587},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-8.187786875011687,"lo":-8.418242311922278},{"hi":0.045430792689877375,"lo":0.037061959656012444}],[{"hi":6.207541000522822,"lo":6.186578894690587},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-8.186469993273008,"lo":-8.535445352985592},{"hi":0.04591359319478849,"lo":0.0321533423817133}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-8.346473829802202,"lo":-8.469927278048093},{"hi":0.03971315681017629,"lo":0.034573871552094015}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.1027343583148477,"lo":1.0769531072320273},{"hi":-8.256291528388022,"lo":-8.666927454292308},{"hi":0.043790563778953295,"lo":0.025888118469887367}],[{"hi":6.1884845406753755,"lo":6.167522434843142},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-8.467468457804218,"lo":-8.555480023120158},{"hi":0.03463601724082288,"lo":0.030668715622550025}]]},{"anchors":[{"parameter":0.0,"point":[2.990025593501361,1.06381993428954,0.24443189280520244],"uv_a":[6.2016175906938065,1.0638199343388153],"uv_b":[-2.081583143507084,0.3400255935013612]},{"parameter":0.027960601948876673,"point":[2.9879723108865743,1.078124982260299,0.2683681601500001],"uv_a":[6.193609511793907,1.07812498228125],"uv_b":[-2.1373605912358022,0.3379723108865744]},{"parameter":0.06566350641898697,"point":[2.985032342842705,1.0994777312845825,0.2993023758744895],"uv_a":[6.183251596162853,1.0994777313317967],"uv_b":[-2.2125545136462232,0.3350323428427049]},{"parameter":0.06914134111203746,"point":[2.984753332289419,1.1015624833168893,0.30207208636967364],"uv_a":[6.182323686679556,1.101562483265625],"uv_b":[-2.2194877770635526,0.33475333228941917]},{"parameter":0.10627874720988245,"point":[2.9817151124528634,1.1249999842435154,0.33071889600382687],"uv_a":[6.172721158909179,1.12499998425],"uv_b":[-2.2935305269455717,0.33171511245286345]},{"parameter":0.138451119986034,"point":[2.9790322399577196,1.1469658845454735,0.3540719041429702],"uv_a":[6.1648856016319,1.1469658845608737],"uv_b":[-2.3576621455573745,0.3290322399577197]},{"parameter":0.14053223592357528,"point":[2.9788582043878766,1.148437485213058,0.3555331182412742],"uv_a":[6.1643950877238165,1.148437485234375],"uv_b":[-2.3618098009586244,0.3288582043878767]},{"parameter":0.1726109003025741,"point":[2.9761831302065995,1.1718749862655384,0.3772717527632513],"uv_a":[6.157094202032266,1.17187498621875],"uv_b":[-2.425754557084009,0.3261831302065996]},{"parameter":0.20299169788098603,"point":[2.973690380710923,1.1953124872401966,0.39644106703150683],"uv_a":[6.150650620365512,1.195312487203125],"uv_b":[-2.486320532996485,0.32369038071092326]},{"parameter":0.22377451989690716,"point":[2.972027308682751,1.2119959662445097,0.4087220039129997],"uv_a":[6.146519607100946,1.2119959662778652],"uv_b":[-2.527755845914946,0.3220273086827512]},{"parameter":0.23201503912643257,"point":[2.971380414731811,1.2187499882135842,0.4133986343912045],"uv_a":[6.1449458871691025,1.2187499881875001],"uv_b":[-2.5441862084965225,0.3213804147318111]},{"parameter":0.2599273283225378,"point":[2.9692536588896807,1.242187489148606,0.42840717688126084],"uv_a":[6.139893056008326,1.2421874891718752],"uv_b":[-2.5998556927076493,0.3192536588896808]},{"parameter":0.2869249884216469,"point":[2.9673105070407413,1.2656249901667684,0.44166543309173684],"uv_a":[6.135426420474502,1.2656249901562502],"uv_b":[-2.6537174838093325,0.3173105070407414]},{"parameter":0.3131624290936812,"point":[2.9655513201809924,1.2890624911441202,0.4533269982760353],"uv_a":[6.131495248472412,1.289062491140625],"uv_b":[-2.7060802629977676,0.3155513201809925]},{"parameter":0.3380244834888475,"point":[2.9640199117830357,1.3118138222699471,0.46323424153891385],"uv_a":[6.128153612569992,1.3118138222613736],"uv_b":[-2.7557150480637147,0.3140199117830358]},{"parameter":0.3387661663716,"point":[2.963976425838612,1.3124999921298923,0.46351240227393675],"uv_a":[6.128059766113292,1.312499992125],"uv_b":[-2.7571958621254273,0.3139764258386122]},{"parameter":0.3638415140017471,"point":[2.9625861179326485,1.3359374931093746,0.47231715386248857],"uv_a":[6.125088483845136,1.335937493109375],"uv_b":[-2.8072746449644437,0.31258611793264857]},{"parameter":0.38847931536185726,"point":[2.961380656429395,1.3593749940954285,0.4798172649252091],"uv_a":[6.122556360574773,1.3593749940937498],"uv_b":[-2.8564962010346706,0.31138065642939505]},{"parameter":0.41275880271272114,"point":[2.9603602671622977,1.3828124950683494,0.48607313096507004],"uv_a":[6.120443514455005,1.3828124950781249],"uv_b":[-2.905017032578237,0.3103602671622978]},{"parameter":0.4367506572602511,"point":[2.9595251415313575,1.4062499960514447,0.4911323006081817],"uv_a":[6.118734302858896,1.4062499960625],"uv_b":[-2.952976259352182,0.3095251415313576]},{"parameter":0.46051916706439316,"point":[2.9588754363665952,1.4296874970565727,0.49503146566535783],"uv_a":[6.117416661523907,1.429687497046875],"uv_b":[-3.0004999882075496,0.3088754363665953]},{"parameter":0.4841239329795401,"point":[2.9584112739474304,1.4531249980307959,0.49779788487451726],"uv_a":[6.1164816319842075,1.45312499803125],"uv_b":[-3.047704774525534,0.3084112739474305]},{"parameter":0.5076212685256527,"point":[2.958132741516926,1.4765624990160457,0.4994503814760793],"uv_a":[6.115923030013848,1.476562499015625],"uv_b":[-3.094700468487279,0.30813274151692616]},{"parameter":0.5310653968754574,"point":[2.958039891529144,1.5,0.5000000000611257],"uv_a":[6.115737227918569,1.5],"uv_b":[-3.141592653589793,0.30803989152914424]},{"parameter":0.5545095252252621,"point":[2.958132741516926,1.5234375009839543,0.4994503814760793],"uv_a":[6.115923030013848,1.523437500984375],"uv_b":[-3.188484838692307,0.30813274151692616]},{"parameter":0.5780068607713746,"point":[2.9584112739474304,1.5468750019692041,0.49779788487451726],"uv_a":[6.1164816319842075,1.54687500196875],"uv_b":[-3.2354805326540523,0.3084112739474305]},{"parameter":0.6016116266865216,"point":[2.9588754363665952,1.5703125029434273,0.49503146566535783],"uv_a":[6.117416661523907,1.570312502953125],"uv_b":[-3.2826853189720366,0.3088754363665953]},{"parameter":0.6253801364906636,"point":[2.9595251415313575,1.5937500039485553,0.4911323006081817],"uv_a":[6.118734302858896,1.5937500039375],"uv_b":[-3.3302090478274042,0.3095251415313576]},{"parameter":0.6493719910381935,"point":[2.9603602671622977,1.6171875049316506,0.48607313096507004],"uv_a":[6.120443514455005,1.6171875049218751],"uv_b":[-3.3781682746013493,0.3103602671622978]},{"parameter":0.6736514783890574,"point":[2.961380656429395,1.6406250059045715,0.4798172649252091],"uv_a":[6.122556360574773,1.6406250059062502],"uv_b":[-3.4266891061449156,0.31138065642939505]},{"parameter":0.6982892797491675,"point":[2.9625861179326485,1.6640625068906254,0.47231715386248857],"uv_a":[6.125088483845136,1.664062506890625],"uv_b":[-3.4759106622151426,0.31258611793264857]},{"parameter":0.7233646273793146,"point":[2.963976425838612,1.6875000078701077,0.46351240227393675],"uv_a":[6.128059766113292,1.687500007875],"uv_b":[-3.525989445054159,0.3139764258386122]},{"parameter":0.7241063102620672,"point":[2.9640199117830357,1.6881861777300529,0.46323424153891385],"uv_a":[6.128153612569992,1.6881861777386264],"uv_b":[-3.5274702591158715,0.3140199117830358]},{"parameter":0.7489683646572335,"point":[2.9655513201809924,1.7109375088558798,0.4533269982760353],"uv_a":[6.131495248472412,1.710937508859375],"uv_b":[-3.5771050441818186,0.3155513201809925]},{"parameter":0.7752058053292677,"point":[2.9673105070407413,1.7343750098332316,0.44166543309173684],"uv_a":[6.135426420474502,1.7343750098437498],"uv_b":[-3.6294678233702538,0.3173105070407414]},{"parameter":0.8022034654283768,"point":[2.9692536588896807,1.757812510851394,0.42840717688126084],"uv_a":[6.139893056008326,1.7578125108281248],"uv_b":[-3.683329614471937,0.3192536588896808]},{"parameter":0.8301157546244821,"point":[2.971380414731811,1.7812500117864158,0.4133986343912045],"uv_a":[6.1449458871691025,1.7812500118124999],"uv_b":[-3.7389990986830637,0.3213804147318111]},{"parameter":0.8383562738540075,"point":[2.972027308682751,1.7880040337554903,0.4087220039129997],"uv_a":[6.146519607100946,1.7880040337221348],"uv_b":[-3.75542946126464,0.3220273086827512]},{"parameter":0.8591390958699285,"point":[2.973690380710923,1.8046875127598034,0.39644106703150683],"uv_a":[6.150650620365512,1.804687512796875],"uv_b":[-3.7968647741831014,0.32369038071092326]},{"parameter":0.8895198934483405,"point":[2.9761831302065995,1.8281250137344616,0.3772717527632513],"uv_a":[6.157094202032266,1.82812501378125],"uv_b":[-3.857430750095577,0.3261831302065996]},{"parameter":0.9215985578273392,"point":[2.9788582043878766,1.851562514786942,0.3555331182412742],"uv_a":[6.1643950877238165,1.851562514765625],"uv_b":[-3.921375506220962,0.3288582043878767]},{"parameter":0.9236796737648805,"point":[2.9790322399577196,1.8530341154545265,0.3540719041429702],"uv_a":[6.1648856016319,1.8530341154391263],"uv_b":[-3.9255231616222117,0.3290322399577197]},{"parameter":0.9558520465410321,"point":[2.9817151124528634,1.8750000157564846,0.33071889600382687],"uv_a":[6.172721158909179,1.87500001575],"uv_b":[-3.9896547802340145,0.33171511245286345]},{"parameter":0.9929894526388771,"point":[2.984753332289419,1.8984375166831107,0.30207208636967364],"uv_a":[6.182323686679556,1.898437516734375],"uv_b":[-4.063697530116034,0.33475333228941917]},{"parameter":0.9964672873319276,"point":[2.985032342842705,1.9005222687154175,0.2993023758744895],"uv_a":[6.183251596162853,1.9005222686682033],"uv_b":[-4.070630793533363,0.3350323428427049]},{"parameter":1.0341701918020378,"point":[2.9879723108865743,1.921875017739701,0.2683681601500001],"uv_a":[6.193609511793907,1.92187501771875],"uv_b":[-4.1458247159437835,0.3379723108865744]},{"parameter":1.0621307937509141,"point":[2.990025593501361,1.9361800657104595,0.24443189280520253],"uv_a":[6.2016175906938065,1.9361800656611843],"uv_b":[-4.201602163672502,0.3400255935013612]},{"parameter":1.081530484277304,"point":[2.99137146463429,1.945312518754148,0.2273692164378188],"uv_a":[6.207322823072767,1.9453125187031248],"uv_b":[-4.240310477615036,0.34137146463429024]},{"parameter":1.1233060883448023,"point":[2.994010307708874,1.962706951180748,0.18947896277030496],"uv_a":[6.21998358522476,1.9627069511839812],"uv_b":[-4.323719007011324,0.3440103077088743]},{"parameter":1.1399563130081112,"point":[2.9949501800369926,1.968750019662461,0.17399258344294688],"uv_a":[6.225155215538505,1.9687500196874999],"uv_b":[-4.356967891558006,0.34495018003699274]},{"parameter":1.1815666960635112,"point":[2.9969851414200517,1.9815806660152426,0.13446212146289366],"uv_a":[6.238349579755713,1.9815806660036719],"uv_b":[-4.440113025616254,0.34698514142005177]},{"parameter":1.229214087928075,"point":[2.998707814310851,1.9921875207229818,0.08804228752506559],"uv_a":[6.253833663689513,1.9921875206718749],"uv_b":[-4.535381530052305,0.3487078143108513]},{"parameter":1.2379835399870236,"point":[2.998949091222668,1.9936553977252123,0.07939992604383014],"uv_a":[6.256715574286667,1.9936553976847358],"uv_b":[-4.552914019133949,0.34894909122266826]},{"parameter":1.293380309955671,"point":[2.999901494675918,1.99940862801749,0.024310948999184546],"uv_a":[6.275081568817621,1.999408627997067],"uv_b":[-4.663747904226072,0.349901494675918]},{"parameter":1.348478677818362,"point":[2.9998420305338556,1.9990513081448087,-0.030786228130472493],"uv_a":[6.2934475633485745,1.9990513081483483],"uv_b":[-4.774000408416548,0.34984203053385565]},{"parameter":1.403955715116119,"point":[2.998770718853752,1.9925706287723952,-0.08587302104395379],"uv_a":[6.311813557879528,1.992570628843767],"uv_b":[-4.884990754064252,0.3487707188537521]},{"parameter":1.4061594509690132,"point":[2.9987078142710573,1.9921875206055155,-0.08804228885943374],"uv_a":[6.312536951121648,1.9921875206718749],"uv_b":[-4.889396433385479,0.34870781427105735]},{"parameter":1.4605334458072101,"point":[2.9966879209893302,1.9797275224690436,-0.1409308489857392],"uv_a":[6.330179552410482,1.9797275224787612],"uv_b":[-4.998122908279489,0.3466879209893303]},{"parameter":1.4954132906747397,"point":[2.9949501800535847,1.9687500197090473,-0.17399258317463864],"uv_a":[6.341215398725307,1.9687500196874999],"uv_b":[-5.067810068675873,0.3449501800535848]},{"parameter":1.519077735033578,"point":[2.9935943394688804,1.9600076838869414,-0.19594114092896384],"uv_a":[6.348545546941436,1.9600076838350269],"uv_b":[-5.115065608224227,0.3435943394688805]},{"parameter":1.5538428470629704,"point":[2.9913714645937475,1.9453125186461344,-0.22736921691606637],"uv_a":[6.359047791464718,1.9453125187031248],"uv_b":[-5.184467484104458,0.34137146459374756]},{"parameter":1.580688630748231,"point":[2.989491017758296,1.932500341259705,-0.25088534185181033],"uv_a":[6.36691154147239,1.932500341212875],"uv_b":[-5.238033575112214,0.33949101775829593]},{"parameter":1.601203330610856,"point":[2.987972310875444,1.92187501771164,-0.26836816025607485],"uv_a":[6.372761102606741,1.92187501771875],"uv_b":[-5.278953245034719,0.3379723108754442]},{"parameter":1.6423813938726104,"point":[2.9847533323182245,1.8984375167516232,-0.3020720861369931],"uv_a":[6.384046927584258,1.898437516734375],"uv_b":[-5.361080430199728,0.3347533323182246]},{"parameter":1.6470218030402053,"point":[2.9843793399091125,1.8956261424212553,-0.30574491900756773],"uv_a":[6.385277536003343,1.8956261424735315],"uv_b":[-5.370331090604577,0.3343793399091126]},{"parameter":1.679521568305571,"point":[2.9817151124391463,1.8750000157258093,-0.330718896100445],"uv_a":[6.39364945549147,1.87500001575],"uv_b":[-5.435123180720872,0.3317151124391464]},{"parameter":1.7137739258950988,"point":[2.9788582043731306,1.8515625147560555,-0.3555331183335919],"uv_a":[6.401975526676833,1.851562514765625],"uv_b":[-5.503402454722163,0.32885820437313074]},{"parameter":1.72091629919211,"point":[2.978261030091669,1.8464661071564765,-0.3605013684048319],"uv_a":[6.403643530534297,1.8464661071772088],"uv_b":[-5.517637304153507,0.32826103009166907]},{"parameter":1.745855408194644,"point":[2.9761831302425756,1.8281250138047276,-0.37727175256023615],"uv_a":[6.409276412231548,1.82812501378125],"uv_b":[-5.567347210301307,0.3261831302425757]},{"parameter":1.776236205779676,"point":[2.973690380748728,1.8046875128283095,-0.3964410668370696],"uv_a":[6.415719993898301,1.804687512796875],"uv_b":[-5.627913186240673,0.32369038074872813]},{"parameter":1.8052570859497412,"point":[2.971380414771232,1.7812500118523045,-0.41339863420470424],"uv_a":[6.421424727094712,1.7812500118124999],"uv_b":[-5.68577886176755,0.321380414771232]},{"parameter":1.808369739931587,"point":[2.971138152013541,1.778678880365249,-0.4151362205890914],"uv_a":[6.422009525065251,1.7786788803689406],"uv_b":[-5.691985295358635,0.32113815201354123]},{"parameter":1.8331704450667723,"point":[2.9692536588719127,1.75781251082419,-0.4284071769592079],"uv_a":[6.426477558392323,1.7578125108281248],"uv_b":[-5.741448346424442,0.3192536588719128]},{"parameter":1.8601681051630032,"point":[2.967310507022423,1.7343750098077526,-0.44166543316679185],"uv_a":[6.430944193926146,1.7343750098437498],"uv_b":[-5.795310137514503,0.31731050702242314]},{"parameter":1.8864055457423028,"point":[2.965551320193207,1.7109375088711614,-0.4533269982289724],"uv_a":[6.434875365859817,1.710937508859375],"uv_b":[-5.847672916520141,0.3155513201932072]},{"parameter":1.9120090455704917,"point":[2.9639764258193875,1.6875000078487399,-0.4635124023440469],"uv_a":[6.438310848287356,1.687500007875],"uv_b":[-5.89878851580742,0.31397642581938756]},{"parameter":1.9290726021117979,"point":[2.963013108223454,1.6716003485187807,-0.4696310471481647],"uv_a":[6.440375519596205,1.6716003485384285],"uv_b":[-5.932862850106976,0.31301310822345396]},{"parameter":1.9370861248056854,"point":[2.9625861179453747,1.6640625069029968,-0.472317153818279],"uv_a":[6.441282130487092,1.664062506890625],"uv_b":[-5.948867298501852,0.31258611794537483]},{"parameter":1.961723926228562,"point":[2.9613806564094936,1.6406250058879954,-0.47981726499148236],"uv_a":[6.443814253825877,1.6406250059062502],"uv_b":[-5.998088854693556,0.31138065640949364]},{"parameter":1.98600341357655,"point":[2.9603602671421374,1.6171875049176623,-0.48607313102983524],"uv_a":[6.445927099945645,1.6171875049218751],"uv_b":[-6.046609686225586,0.3103602671421375]},{"parameter":2.009995268121205,"point":[2.959525141510987,1.5937500039372514,-0.49113230067171487],"uv_a":[6.447636311541753,1.5937500039375],"uv_b":[-6.094568912988007,0.3095251415109872]},{"parameter":2.033763777858961,"point":[2.958875436413802,1.5703125029630696,-0.4950314655214878],"uv_a":[6.448953952739905,1.570312502953125],"uv_b":[-6.142092641717985,0.308875436413802]},{"parameter":2.0573685438247455,"point":[2.9584112739267834,1.5468750019634778,-0.49779788493640886],"uv_a":[6.449888982416442,1.54687500196875],"uv_b":[-6.1892974281383335,0.30841127392678347]},{"parameter":2.0808658793679835,"point":[2.9581327414962093,1.5234375009810819,-0.49945038153756327],"uv_a":[6.450447584386803,1.523437500984375],"uv_b":[-6.236293122088576,0.3081327414962094]},{"parameter":2.1043100077124057,"point":[2.958039891542615,1.5,-0.5000000000212772],"uv_a":[6.450633386413662,1.5],"uv_b":[-6.283185307179586,0.30803989154261524]},{"parameter":2.127754136056828,"point":[2.9581327414962093,1.4765624990189181,-0.49945038153756327],"uv_a":[6.450447584386803,1.476562499015625],"uv_b":[-6.330077492270597,0.3081327414962094]},{"parameter":2.151251471600066,"point":[2.9584112739267834,1.4531249980365222,-0.49779788493640886],"uv_a":[6.449888982416442,1.45312499803125],"uv_b":[-6.377073186220839,0.30841127392678347]},{"parameter":2.1748562375658507,"point":[2.958875436413802,1.4296874970369304,-0.4950314655214878],"uv_a":[6.448953952739905,1.429687497046875],"uv_b":[-6.424277972641187,0.308875436413802]},{"parameter":2.1986247473036062,"point":[2.959525141510987,1.4062499960627486,-0.49113230067171487],"uv_a":[6.447636311541753,1.4062499960625],"uv_b":[-6.471801701371166,0.3095251415109872]},{"parameter":2.2226166018482614,"point":[2.9603602671421374,1.3828124950823377,-0.48607313102983524],"uv_a":[6.445927099945645,1.3828124950781249],"uv_b":[-6.519760928133587,0.3103602671421375]},{"parameter":2.2468960891962495,"point":[2.9613806564094936,1.3593749941120046,-0.47981726499148236],"uv_a":[6.443814253825877,1.3593749940937498],"uv_b":[-6.5682817596656164,0.31138065640949364]},{"parameter":2.271533890619126,"point":[2.9625861179453747,1.3359374930970032,-0.472317153818279],"uv_a":[6.441282130487092,1.335937493109375],"uv_b":[-6.617503315857321,0.31258611794537483]},{"parameter":2.279547413313013,"point":[2.963013108223454,1.3283996514812193,-0.4696310471481647],"uv_a":[6.440375519596205,1.3283996514615715],"uv_b":[-6.6335077642521965,0.31301310822345396]},{"parameter":2.2966109698543193,"point":[2.9639764258193875,1.3124999921512601,-0.4635124023440469],"uv_a":[6.438310848287356,1.312499992125],"uv_b":[-6.667582098551753,0.31397642581938756]},{"parameter":2.322214469682508,"point":[2.965551320193207,1.2890624911288386,-0.4533269982289724],"uv_a":[6.434875365859817,1.289062491140625],"uv_b":[-6.718697697839032,0.3155513201932072]},{"parameter":2.3484519102618076,"point":[2.967310507022423,1.2656249901922474,-0.44166543316679185],"uv_a":[6.430944193926146,1.2656249901562502],"uv_b":[-6.77106047684467,0.31731050702242314]},{"parameter":2.3754495703580383,"point":[2.9692536588719127,1.24218748917581,-0.4284071769592079],"uv_a":[6.426477558392323,1.2421874891718752],"uv_b":[-6.82492226793473,0.3192536588719128]},{"parameter":2.4002502754932236,"point":[2.971138152013541,1.221321119634751,-0.4151362205890914],"uv_a":[6.422009525065251,1.2213211196310594],"uv_b":[-6.874385319000537,0.32113815201354123]},{"parameter":2.4033629294750694,"point":[2.971380414771232,1.2187499881476955,-0.41339863420470424],"uv_a":[6.421424727094712,1.2187499881875001],"uv_b":[-6.880591752591623,0.321380414771232]},{"parameter":2.4323838096451347,"point":[2.973690380748728,1.1953124871716905,-0.3964410668370696],"uv_a":[6.415719993898301,1.195312487203125],"uv_b":[-6.938457428118499,0.32369038074872813]},{"parameter":2.4627646072301665,"point":[2.9761831302425756,1.1718749861952724,-0.37727175256023615],"uv_a":[6.409276412231548,1.17187498621875],"uv_b":[-6.999023404057866,0.3261831302425757]},{"parameter":2.4877037162327005,"point":[2.978261030091669,1.1535338928435235,-0.3605013684048319],"uv_a":[6.403643530534297,1.1535338928227912],"uv_b":[-7.048733310205665,0.32826103009166907]},{"parameter":2.494846089529712,"point":[2.9788582043731306,1.1484374852439445,-0.3555331183335919],"uv_a":[6.401975526676833,1.148437485234375],"uv_b":[-7.062968159637009,0.32885820437313074]},{"parameter":2.5290984471192397,"point":[2.9817151124391463,1.1249999842741907,-0.330718896100445],"uv_a":[6.39364945549147,1.12499998425],"uv_b":[-7.1312474336383005,0.3317151124391464]},{"parameter":2.5615982123846055,"point":[2.9843793399091125,1.1043738575787447,-0.30574491900756773],"uv_a":[6.385277536003343,1.1043738575264685],"uv_b":[-7.196039523754595,0.3343793399091126]},{"parameter":2.5662386215522,"point":[2.9847533323182245,1.1015624832483768,-0.3020720861369931],"uv_a":[6.384046927584258,1.101562483265625],"uv_b":[-7.205290184159445,0.3347533323182246]},{"parameter":2.6074166848139546,"point":[2.987972310875444,1.07812498228836,-0.26836816025607485],"uv_a":[6.372761102606741,1.07812498228125],"uv_b":[-7.287417369324453,0.3379723108754442]},{"parameter":2.6279313846765793,"point":[2.989491017758296,1.067499658740295,-0.25088534185181033],"uv_a":[6.36691154147239,1.067499658787125],"uv_b":[-7.328337039246959,0.33949101775829593]},{"parameter":2.65477716836184,"point":[2.9913714645937475,1.0546874813538654,-0.22736921691606635],"uv_a":[6.359047791464718,1.054687481296875],"uv_b":[-7.381903130254716,0.34137146459374756]},{"parameter":2.6895422803912323,"point":[2.9935943394688804,1.0399923161130586,-0.19594114092896384],"uv_a":[6.348545546941436,1.0399923161649731],"uv_b":[-7.4513050061349455,0.3435943394688805]},{"parameter":2.7132067247500706,"point":[2.9949501800535847,1.0312499802909525,-0.1739925831746386],"uv_a":[6.341215398725307,1.0312499803125],"uv_b":[-7.498560545683301,0.3449501800535848]},{"parameter":2.7480865696176004,"point":[2.9966879209893302,1.020272477530956,-0.14093084898573915],"uv_a":[6.330179552410482,1.0202724775212384],"uv_b":[-7.568247706079684,0.3466879209893303]},{"parameter":2.802460564455797,"point":[2.9987078142710573,1.0078124793944845,-0.08804228885943374],"uv_a":[6.312536951121648,1.007812479328125],"uv_b":[-7.676974180973693,0.34870781427105735]},{"parameter":2.8046643003086915,"point":[2.998770718853752,1.0074293712276043,-0.08587302104395379],"uv_a":[6.311813557879528,1.0074293711562325],"uv_b":[-7.68137986029492,0.3487707188537521]},{"parameter":2.8601413376064486,"point":[2.9998420305338556,1.0009486918551913,-0.030786228130472493],"uv_a":[6.2934475633485745,1.0009486918516517],"uv_b":[-7.792370205942625,0.34984203053385565]},{"parameter":2.9152397054691397,"point":[2.999901494675918,1.00059137198251,0.024310948999184546],"uv_a":[6.275081568817621,1.000591372002933],"uv_b":[-7.902622710133101,0.349901494675918]},{"parameter":2.970636475437787,"point":[2.998949091222668,1.0063446022747877,0.07939992604383014],"uv_a":[6.256715574286667,1.0063446023152642],"uv_b":[-8.013456595225223,0.34894909122266826]},{"parameter":2.9794059274967357,"point":[2.998707814310851,1.007812479277018,0.08804228752506559],"uv_a":[6.253833663689513,1.007812479328125],"uv_b":[-8.030989084306867,0.3487078143108513]},{"parameter":3.0270533193612996,"point":[2.9969851414200517,1.0184193339847571,0.13446212146289355],"uv_a":[6.238349579755713,1.0184193339963277],"uv_b":[-8.126257588742918,0.34698514142005177]},{"parameter":3.0686637024167,"point":[2.9949501800369926,1.031249980337539,0.17399258344294682],"uv_a":[6.225155215538505,1.0312499803125],"uv_b":[-8.209402722801165,0.34495018003699274]},{"parameter":3.085313927080009,"point":[2.994010307708874,1.037293048819252,0.18947896277030496],"uv_a":[6.21998358522476,1.0372930488160188],"uv_b":[-8.242651607347849,0.3440103077088743]},{"parameter":3.127089531147507,"point":[2.99137146463429,1.054687481245852,0.2273692164378188],"uv_a":[6.207322823072767,1.054687481296875],"uv_b":[-8.326060136744136,0.34137146463429024]},{"parameter":3.146489221673897,"point":[2.990025593501361,1.06381993428954,0.24443189280520244],"uv_a":[6.2016175906938065,1.0638199343388153],"uv_b":[-8.36476845068667,0.3400255935013612]}],"residual_tolerance":1.575e-8,"surfaces":[8,16],"uv_tubes":[[{"hi":6.202535890420376,"lo":6.182333296436283},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-1.970028248049648,"lo":-2.2489154866932384},{"hi":0.3441321587309347,"lo":0.3338657456570009}],[{"hi":6.202535890420376,"lo":6.182333296436283},{"hi":1.1027343583148477,"lo":1.0769531072320273},{"hi":-1.9869727464149602,"lo":-2.3629423584670652},{"hi":0.3438522469743135,"lo":0.3291524067549658}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.1027343583148477,"lo":1.0769531072320273},{"hi":-2.1986879868115645,"lo":-2.2333543038982113},{"hi":0.3355903639492763,"lo":0.33419531118284773}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-2.0714022772995144,"lo":-2.44161602670961},{"hi":0.3408297719625306,"lo":0.325638672779752}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.149609360283598,"lo":1.1238281092007771},{"hi":-2.165267289721966,"lo":-2.48592538278098},{"hi":0.33708085744315097,"lo":0.32366649496743216}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.149609360283598,"lo":1.1238281092007771},{"hi":-2.349366834754875,"lo":-2.370105111761124},{"hi":0.3293803110974056,"lo":0.3285101332481908}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-2.233920288707855,"lo":-2.5536440693347786},{"hi":0.3342083527504309,"lo":0.3208329818440454}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.196484362252348,"lo":1.1707031111695272},{"hi":-2.3046226052590577,"lo":-2.607452484821436},{"hi":0.3311686291979523,"lo":0.31870488171957057}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-2.4034499071595623,"lo":-2.6106264717518686},{"hi":0.3270165247672674,"lo":0.3187011646264071}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-2.494895120751793,"lo":-2.5770469336596755},{"hi":0.3233210965846314,"lo":0.32008662682993094}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.2433593642210983,"lo":1.217578113138277},{"hi":-2.432847240074269,"lo":-2.7111946611299027},{"hi":0.3256339264160717,"lo":0.31500014720542024}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.2667968652054735,"lo":1.2410156141226518},{"hi":-2.492132110504283,"lo":-2.761441066012699},{"hi":0.3231399625875597,"lo":0.3134242033428625}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.2902343661898483,"lo":1.2644531151070268},{"hi":-2.5489919254324622,"lo":-2.810805821374638},{"hi":0.3208288807602391,"lo":0.3120329464614948}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-2.6068106928658734,"lo":-2.854984618195609},{"hi":0.318614136976906,"lo":0.3109570949871223}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-2.7527534199402894,"lo":-2.7601574902488526},{"hi":0.314106883671883,"lo":0.313889453949765}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.3371093681585986,"lo":1.3113281170757765},{"hi":-2.6570382964473946,"lo":-2.9074322106424764},{"hi":0.3167570416505394,"lo":0.30980550212072133}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.3605468691429734,"lo":1.3347656180601515},{"hi":-2.70883153282399,"lo":-2.9549393131751245},{"hi":0.3149970409391556,"lo":0.308969733422888}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.3839843701273484,"lo":1.3582031190445263},{"hi":-2.759454537947538,"lo":-3.0020586956653696},{"hi":0.31342143496358954,"lo":0.3083194886281033}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.4074218711117237,"lo":1.381640620028901},{"hi":-2.809098579030347,"lo":-3.048894712900072},{"hi":0.3120305184241783,"lo":0.3078548902694771}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.4308593720960987,"lo":1.4050781210132761},{"hi":-2.857928801641447,"lo":-3.095547445918285},{"hi":0.3108245518608821,"lo":0.3075760260370708}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.454296873080474,"lo":1.428515621997651},{"hi":-2.906090415571581,"lo":-3.1421143471615025},{"hi":0.3098037612049249,"lo":0.3074829491091009}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.477734374064849,"lo":1.451953122982026},{"hi":-2.9537133866020433,"lo":-3.18869185641077},{"hi":0.3089683388084392,"lo":0.30757567665591745}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.501171875049224,"lo":1.475390623966401},{"hi":-3.0009160982822514,"lo":-3.235377023794821},{"hi":0.30831844149249,"lo":0.3078541915535804}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.5246093760335993,"lo":1.4988281249507758},{"hi":-3.0478082833847653,"lo":-3.282269208897335},{"hi":0.30831844149249,"lo":0.3078541915535804}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.5480468770179743,"lo":1.5222656259351508},{"hi":-3.0944934507688164,"lo":-3.329471920577543},{"hi":0.3089683388084392,"lo":0.30757567665591745}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.5714843780023493,"lo":1.5457031269195258},{"hi":-3.1410709600180837,"lo":-3.3770948916080052},{"hi":0.3098037612049249,"lo":0.3074829491091009}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.5949218789867246,"lo":1.5691406279039006},{"hi":-3.1876378612613014,"lo":-3.4252565055381394},{"hi":0.3108245518608821,"lo":0.3075760260370708}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.6183593799710996,"lo":1.5925781288882757},{"hi":-3.234290594279514,"lo":-3.4740867281492394},{"hi":0.3120305184241783,"lo":0.3078548902694771}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.6417968809554748,"lo":1.6160156298726505},{"hi":-3.2811266115142166,"lo":-3.5237307692320483},{"hi":0.31342143496358954,"lo":0.3083194886281033}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.6652343819398496,"lo":1.6394531308570255},{"hi":-3.3282459940044617,"lo":-3.5743537743555964},{"hi":0.3149970409391556,"lo":0.308969733422888}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.6886718829242247,"lo":1.6628906318414003},{"hi":-3.37575309653711,"lo":-3.6261470107321916},{"hi":0.3167570416505394,"lo":0.30980550212072133}],[{"hi":6.129071912296562,"lo":6.108869318312469},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-3.5230278169307336,"lo":-3.530431887239297},{"hi":0.314106883671883,"lo":0.313889453949765}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-3.4282006889839773,"lo":-3.676374614313713},{"hi":0.318614136976906,"lo":0.3109570949871223}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.7355468848929747,"lo":1.7097656338101501},{"hi":-3.4723794858049484,"lo":-3.734193381747124},{"hi":0.3208288807602391,"lo":0.3120329464614948}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.7589843858773497,"lo":1.733203134794525},{"hi":-3.5217442411668873,"lo":-3.7910531966753034},{"hi":0.3231399625875597,"lo":0.3134242033428625}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.782421886861725,"lo":1.7566406357788997},{"hi":-3.5719906460496835,"lo":-3.850338067105317},{"hi":0.3256339264160717,"lo":0.31500014720542024}],[{"hi":6.147437906827515,"lo":6.127235312843423},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-3.7061383735199107,"lo":-3.788290186427793},{"hi":0.3233210965846314,"lo":0.32008662682993094}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-3.6725588354277177,"lo":-3.879735400020024},{"hi":0.3270165247672674,"lo":0.3187011646264071}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.8292968888304753,"lo":1.8035156377476496},{"hi":-3.67573282235815,"lo":-3.9785627019205285},{"hi":0.3311686291979523,"lo":0.31870488171957057}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-3.7295412378448076,"lo":-4.049265018471731},{"hi":0.3342083527504309,"lo":0.3208329818440454}],[{"hi":6.165803901358469,"lo":6.145601307374377},{"hi":1.8761718907992253,"lo":1.8503906397163996},{"hi":-3.913080195418462,"lo":-3.9338184724247114},{"hi":0.3293803110974056,"lo":0.3285101332481908}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.8761718907992253,"lo":1.8503906397163996},{"hi":-3.797259924398606,"lo":-4.117918017457621},{"hi":0.33708085744315097,"lo":0.32366649496743216}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-3.8415692804699755,"lo":-4.211783029880073},{"hi":0.3408297719625306,"lo":0.325638672779752}],[{"hi":6.184169895889422,"lo":6.1639673019053305},{"hi":1.9230468927679756,"lo":1.8972656416851494},{"hi":-4.049831003281376,"lo":-4.084497320368021},{"hi":0.3355903639492763,"lo":0.33419531118284773}],[{"hi":6.202535890420376,"lo":6.182333296436283},{"hi":1.9230468927679756,"lo":1.8972656416851494},{"hi":-3.920242948712522,"lo":-4.296212560764625},{"hi":0.3438522469743135,"lo":0.3291524067549658}],[{"hi":6.202535890420376,"lo":6.182333296436283},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-4.034269820486347,"lo":-4.313157059129938},{"hi":0.3441321587309347,"lo":0.3338657456570009}],[{"hi":6.2209018849513305,"lo":6.200699290967236},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-4.124185535787432,"lo":-4.317727105500106},{"hi":0.34406320690014836,"lo":0.3373338512355031}],[{"hi":6.2209018849513305,"lo":6.200699290967236},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.073493418822462,"lo":-4.490536065803898},{"hi":0.34928799385804243,"lo":0.3360937784851221}],[{"hi":6.239267879482283,"lo":6.21906528549819},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.257221237917959,"lo":-4.423465660651371},{"hi":0.3468299246932296,"lo":0.34213056305263745}],[{"hi":6.239267879482283,"lo":6.21906528549819},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.19067762344151,"lo":-4.60640329373275},{"hi":0.35105506418616983,"lo":0.3408802572708747}],[{"hi":6.257633874013237,"lo":6.237431280029143},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.249576016744152,"lo":-4.725918538924407},{"hi":0.35215316009245035,"lo":0.3435397956384527}],[{"hi":6.257633874013237,"lo":6.237431280029143},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.500316551889018,"lo":-4.5879789972972365},{"hi":0.3494316450463022,"lo":0.34822526048721736}],[{"hi":6.275999868544191,"lo":6.255797274560097},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.331246248949704,"lo":-4.885415674410317},{"hi":0.35180630158241755,"lo":0.34704428431616874}],[{"hi":6.294365863075145,"lo":6.2741632690910505},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.4432428958451196,"lo":-4.9945054167975},{"hi":0.35002042296004277,"lo":0.3497231022497309}],[{"hi":6.3127318576060985,"lo":6.292529263622004},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.552019717121139,"lo":-5.106971445359661},{"hi":0.35198465389406275,"lo":0.346628095493545}],[{"hi":6.331097852137052,"lo":6.310895258152958},{"hi":2.0167968967054755,"lo":1.9910156456226489},{"hi":-4.876179395421798,"lo":-4.898207792027933},{"hi":0.3488965280191416,"lo":0.34858200510566784}],[{"hi":6.331097852137052,"lo":6.310895258152958},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.671943483597459,"lo":-5.21557585806751},{"hi":0.3527476008345114,"lo":0.34264813442587627}],[{"hi":6.349463846668006,"lo":6.329261252683912},{"hi":1.9933593957211007,"lo":1.967578144638274},{"hi":-4.858748587486723,"lo":-5.207184389468639},{"hi":0.35016340286082137,"lo":0.34147469818209375}],[{"hi":6.349463846668006,"lo":6.329261252683912},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.973298989579164,"lo":-5.2095766873209355},{"hi":0.34766186122299336,"lo":0.34088265829947195}],[{"hi":6.36782984119896,"lo":6.347627247214866},{"hi":1.9699218947367256,"lo":1.944140643653899},{"hi":-4.976261856463766,"lo":-5.323271235864919},{"hi":0.3480400892191464,"lo":0.33692571484348166}],[{"hi":6.36782984119896,"lo":6.347627247214866},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-5.077335302088946,"lo":-5.345165757127726},{"hi":0.34513235826465083,"lo":0.33573012408739267}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.9464843937523504,"lo":1.9207031426695245},{"hi":-5.156194235267202,"lo":-5.36079258487973},{"hi":0.34252843152399937,"lo":0.3349348971097408}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.9230468927679756,"lo":1.8972656416851494},{"hi":-5.114698874704702,"lo":-5.525334800529745},{"hi":0.3444102679898835,"lo":0.3283153752037853}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-5.342579109390028,"lo":-5.388832411414277},{"hi":0.3355013171364485,"lo":0.3336313550908887}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.8996093917836006,"lo":1.8738281407007744},{"hi":-5.240746910371988,"lo":-5.564707360953461},{"hi":0.339707794849045,"lo":0.32638665749921403}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.8761718907992253,"lo":1.8503906397163996},{"hi":-5.298564632718289,"lo":-5.639961002724746},{"hi":0.3374289285711778,"lo":0.32314438824109937}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-5.474932755859475,"lo":-5.546107003016195},{"hi":0.33005255293605407,"lo":0.32706668152874574}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.8527343898148503,"lo":1.8269531387320246},{"hi":-5.4182174918579085,"lo":-5.666767022596906},{"hi":0.33241682978985576,"lo":0.32202733054438903}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.8292968888304753,"lo":1.8035156377476496},{"hi":-5.4462152584225745,"lo":-5.749045138119405},{"hi":0.3311686292302709,"lo":0.31870488176103295}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.8058593878461,"lo":1.7800781367632748},{"hi":-5.51218183518692,"lo":-5.801510212821303},{"hi":0.32831031270372035,"lo":0.3167604828162398}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.782421886861725,"lo":1.7566406357788997},{"hi":-5.673365994585379,"lo":-5.704398162540806},{"hi":0.3218649402866136,"lo":0.32065362649815965}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.782421886861725,"lo":1.7566406357788997},{"hi":-5.593059193227021,"lo":-5.840374448556056},{"hi":0.32490713829679807,"lo":0.315484672588656}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.7589843858773497,"lo":1.733203134794525},{"hi":-5.633724764244321,"lo":-5.903033719694624},{"hi":0.32313996257089217,"lo":0.3134242033234438}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.7355468848929747,"lo":1.7097656338101501},{"hi":-5.690584579503226,"lo":-5.952398474531417},{"hi":0.320828880680855,"lo":0.3120329465347753}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.7121093839086,"lo":1.686328132825775},{"hi":-5.7454417179455834,"lo":-6.001019714381977},{"hi":0.31870110894084647,"lo":0.3108266370717483}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.6886718829242247,"lo":1.6628906318414003},{"hi":-5.830639847208307,"lo":-6.001011518706089},{"hi":0.31590306101125476,"lo":0.31108647303158676}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.6886718829242247,"lo":1.6628906318414003},{"hi":-5.900853953317225,"lo":-5.980876195291603},{"hi":0.3138670887796122,"lo":0.31173213738921657}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.6652343819398496,"lo":1.6394531308570255},{"hi":-5.850424186118443,"lo":-6.096531967076965},{"hi":0.3149970410171372,"lo":0.30896973333773126}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.6417968809554748,"lo":1.6160156298726505},{"hi":-5.901047191629496,"lo":-6.143651349289645},{"hi":0.31342143494420593,"lo":0.3083194886074252}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.6183593799710996,"lo":1.5925781288882757},{"hi":-5.950691232700744,"lo":-6.190487366512849},{"hi":0.3120305184044381,"lo":0.3078548902486866}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.5949218789867246,"lo":1.5691406279039006},{"hi":-5.99952145552805,"lo":-6.237140099177942},{"hi":0.3108245517053576,"lo":0.3075760262194316}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.5714843780023493,"lo":1.5457031269195258},{"hi":-6.047683068877289,"lo":-6.28370700097903},{"hi":0.30980376138783905,"lo":0.3074829489527464}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.5480468770179743,"lo":1.5222656259351508},{"hi":-6.095306040237849,"lo":-6.33028450998906},{"hi":0.3089683387879316,"lo":0.30757567663506125}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.5246093760335993,"lo":1.4988281249507758},{"hi":-6.142508751906554,"lo":-6.376969677361608},{"hi":0.3083184414033977,"lo":0.30785419163542693}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.501171875049224,"lo":1.475390623966401},{"hi":-6.189400936997565,"lo":-6.423861862452618},{"hi":0.3083184414033977,"lo":0.30785419163542693}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.477734374064849,"lo":1.451953122982026},{"hi":-6.236086104370113,"lo":-6.471064574121323},{"hi":0.3089683387879316,"lo":0.30757567663506125}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.454296873080474,"lo":1.428515621997651},{"hi":-6.282663613380143,"lo":-6.518687545481884},{"hi":0.30980376138783905,"lo":0.3074829489527464}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.4308593720960987,"lo":1.4050781210132761},{"hi":-6.32923051518123,"lo":-6.566849158831123},{"hi":0.3108245517053576,"lo":0.3075760262194316}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.4074218711117237,"lo":1.381640620028901},{"hi":-6.375883247846324,"lo":-6.6156793816584285},{"hi":0.3120305184044381,"lo":0.3078548902486866}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.3839843701273484,"lo":1.3582031190445263},{"hi":-6.422719265069527,"lo":-6.665323422729676},{"hi":0.31342143494420593,"lo":0.3083194886074252}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.3605468691429734,"lo":1.3347656180601515},{"hi":-6.469838647282208,"lo":-6.71594642824073},{"hi":0.3149970410171372,"lo":0.30896973333773126}],[{"hi":6.459659813853729,"lo":6.4394572198696345},{"hi":1.3371093681585986,"lo":1.3113281170757765},{"hi":-6.58549441906757,"lo":-6.665516661041948},{"hi":0.3138670887796122,"lo":0.31173213738921657}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.3371093681585986,"lo":1.3113281170757765},{"hi":-6.565359095653084,"lo":-6.735730767150866},{"hi":0.31590306101125476,"lo":0.31108647303158676}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.3136718671742234,"lo":1.2878906160914017},{"hi":-6.5653508999771955,"lo":-6.820928896413589},{"hi":0.31870110894084647,"lo":0.3108266370717483}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.2902343661898483,"lo":1.2644531151070268},{"hi":-6.613972139827755,"lo":-6.875786034855946},{"hi":0.320828880680855,"lo":0.3120329465347753}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.2667968652054735,"lo":1.2410156141226518},{"hi":-6.663336894664549,"lo":-6.932645850114851},{"hi":0.32313996257089217,"lo":0.3134242033234438}],[{"hi":6.441293819322775,"lo":6.421091225338681},{"hi":1.2433593642210983,"lo":1.217578113138277},{"hi":-6.725996165803116,"lo":-6.973311421132151},{"hi":0.32490713829679807,"lo":0.315484672588656}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.2433593642210983,"lo":1.217578113138277},{"hi":-6.8619724518183665,"lo":-6.8930046197737935},{"hi":0.3218649402866136,"lo":0.32065362649815965}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.2199218632367232,"lo":1.194140612153902},{"hi":-6.764860401537869,"lo":-7.054188779172253},{"hi":0.32831031270372035,"lo":0.3167604828162398}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.196484362252348,"lo":1.1707031111695272},{"hi":-6.817325476239767,"lo":-7.120155355936598},{"hi":0.3311686292302709,"lo":0.31870488176103295}],[{"hi":6.422927824791821,"lo":6.402725230807727},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-6.899603591762267,"lo":-7.148153122501264},{"hi":0.33241682978985576,"lo":0.32202733054438903}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.173046861267973,"lo":1.1472656101851522},{"hi":-7.020263611342977,"lo":-7.091437858499697},{"hi":0.33005255293605407,"lo":0.32706668152874574}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.149609360283598,"lo":1.1238281092007771},{"hi":-6.926409611634426,"lo":-7.267805981640883},{"hi":0.3374289285711778,"lo":0.32314438824109937}],[{"hi":6.404561830260867,"lo":6.384359236276773},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-7.001663253405711,"lo":-7.325623703987184},{"hi":0.339707794849045,"lo":0.32638665749921403}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.1261718592992227,"lo":1.1003906082164023},{"hi":-7.177538202944896,"lo":-7.223791504969144},{"hi":0.3355013171364485,"lo":0.3336313550908887}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.1027343583148477,"lo":1.0769531072320273},{"hi":-7.041035813829428,"lo":-7.45167173965447},{"hi":0.3444102679898835,"lo":0.3283153752037853}],[{"hi":6.386195835729914,"lo":6.365993241745819},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-7.205578029479442,"lo":-7.41017637909197},{"hi":0.34252843152399937,"lo":0.3349348971097408}],[{"hi":6.36782984119896,"lo":6.347627247214866},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-7.221204857231445,"lo":-7.48903531227023},{"hi":0.34513235826465083,"lo":0.33573012408739267}],[{"hi":6.36782984119896,"lo":6.347627247214866},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-7.243099378494256,"lo":-7.590108757895405},{"hi":0.3480400892191464,"lo":0.33692571484348166}],[{"hi":6.349463846668006,"lo":6.329261252683912},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-7.356793927038235,"lo":-7.593071624780011},{"hi":0.34766186122299336,"lo":0.34088265829947195}],[{"hi":6.349463846668006,"lo":6.329261252683912},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.359186224890534,"lo":-7.707622026872451},{"hi":0.35016340286082137,"lo":0.34147469818209375}],[{"hi":6.331097852137052,"lo":6.310895258152958},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.350794756291665,"lo":-7.894427130761712},{"hi":0.3527476008345114,"lo":0.34264813442587627}],[{"hi":6.331097852137052,"lo":6.310895258152958},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.668162822331239,"lo":-7.6901912189373745},{"hi":0.3488965280191416,"lo":0.34858200510566784}],[{"hi":6.3127318576060985,"lo":6.292529263622004},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.459399168999512,"lo":-8.014350897238034},{"hi":0.35198465389406275,"lo":0.346628095493545}],[{"hi":6.294365863075145,"lo":6.2741632690910505},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.571865197561673,"lo":-8.123127718514052},{"hi":0.35002042296004277,"lo":0.3497231022497309}],[{"hi":6.275999868544191,"lo":6.255797274560097},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.6809549399488555,"lo":-8.235124365409469},{"hi":0.35180630158241755,"lo":0.34704428431616874}],[{"hi":6.257633874013237,"lo":6.237431280029143},{"hi":1.0089843543773471,"lo":0.9832031032945276},{"hi":-7.978391617061936,"lo":-8.066054062470155},{"hi":0.3494316450463022,"lo":0.34822526048721736}],[{"hi":6.257633874013237,"lo":6.237431280029143},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.840452075434765,"lo":-8.31679459761502},{"hi":0.35215316009245035,"lo":0.3435397956384527}],[{"hi":6.239267879482283,"lo":6.21906528549819},{"hi":1.0324218553617224,"lo":1.0066406042789025},{"hi":-7.959967320626424,"lo":-8.37569299091766},{"hi":0.35105506418616983,"lo":0.3408802572708747}],[{"hi":6.239267879482283,"lo":6.21906528549819},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-8.142904953707799,"lo":-8.309149376441216},{"hi":0.3468299246932296,"lo":0.34213056305263745}],[{"hi":6.2209018849513305,"lo":6.200699290967236},{"hi":1.0558593563460974,"lo":1.0300781052632775},{"hi":-8.075834548555274,"lo":-8.49287719553671},{"hi":0.34928799385804243,"lo":0.3360937784851221}],[{"hi":6.2209018849513305,"lo":6.200699290967236},{"hi":1.0792968573304726,"lo":1.0535156062476523},{"hi":-8.248643508859068,"lo":-8.442185078571738},{"hi":0.34406320690014836,"lo":0.3373338512355031}]]}],"pcurves":[{"direction":[0.6967067093471652,0.717356090899523],"kind":"Line2","origin":[1.8811081152373468,1.9368614454287116]},{"axis_a":[3.0,0.0],"axis_b":[0.0,-3.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[3.0,0.0],"axis_b":[0.0,-3.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[3.0,0.0],"axis_b":[0.0,-3.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-0.6967067093471659,0.7173560908995223],"kind":"Line2","origin":[2.0901201280414963,-2.152068272698568]},{"axis_a":[2.7,0.0],"axis_b":[0.0,-2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,-2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,-2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,2.7],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.465765601187591,-1.1]},{"axis_a":[3.0,0.0],"axis_b":[0.0,3.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.598076211353316,-1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.244994432064365,1.5]},{"axis_a":[3.0,0.0],"axis_b":[0.0,-3.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.7910571473905725,1.1]},{"axis_a":[2.7,0.0],"axis_b":[0.0,-2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,2.7],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.7,0.0],"axis_b":[0.0,2.7],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[0.6967067093471659,0.7173560908995223],"kind":"Line2","origin":[1.8811081152373468,1.9368614454287116]},{"axis_a":[3.0,0.0],"axis_b":[0.0,3.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[3.0,0.0],"axis_b":[0.0,3.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[3.0,0.0],"axis_b":[0.0,3.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-0.6967067093471652,0.717356090899523],"kind":"Line2","origin":[2.0901201280414963,-2.1520682726985685]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.6941543369633125,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.6941543369633125,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.6941543369633125,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.483185307179586,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.483185307179586,1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.483185307179586,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.863571923609742,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.6941543369633125,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.863571923609742,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.863571923609742,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.863571923609742,0.0]},{"direction":[-1.0000000000000002,0.0],"kind":"Line2","origin":[0.29999999999999966,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.29999999999999966,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.29999999999999966,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.29999999999999966,1.5]},{"direction":[1.0000000000000002,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.907761699198547,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.907761699198547,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.907761699198547,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[7.083185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.759586531581287,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.907761699198547,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.483185307179586,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.483185307179586,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.483185307179586,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.759586531581287,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.759586531581287,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.759586531581287,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.29999999999999993,0.0]},{"direction":[-0.0,-1.0],"kind":"Line2","origin":[0.29999999999999993,1.0]},{"direction":[-0.0,-1.0],"kind":"Line2","origin":[0.29999999999999993,1.5]},{"direction":[-0.0,-1.0],"kind":"Line2","origin":[0.29999999999999993,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3530817792889511,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.3530817792889511,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.3252915462029815,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.32529154620298195,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-4.440892098500626e-16,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.863571923609742,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.6941543369633125,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.759586531581287,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.907761699198547,1.5]},{"definition":0,"kind":"IntersectionSide","side":"A"},{"definition":0,"kind":"IntersectionSide","side":"B"},{"definition":1,"kind":"IntersectionSide","side":"A"},{"definition":1,"kind":"IntersectionSide","side":"B"}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,1.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,1.5,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.7},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.7},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.7},{"frame":{"origin":[1.8811081152373468,0.0,-1.9368614454287116],"x":[0.6967067093471659,0.0,-0.7173560908995223],"y":[0.0,1.0,0.0],"z":[0.7173560908995223,-0.0,0.6967067093471659]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":3.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":3.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":3.0},{"frame":{"origin":[2.0901201280414963,0.0,2.1520682726985685],"x":[-0.6967067093471652,0.0,-0.717356090899523],"y":[0.0,1.0,0.0],"z":[0.717356090899523,0.0,-0.6967067093471652]},"kind":"Plane"},{"frame":{"origin":[2.244994432064365,1.0,1.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.7910571473905725,1.0,1.1],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.7},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":3.0},{"frame":{"origin":[2.65,1.5,0.0],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Cylinder","radius":0.5}]},"id":"batch-staged.mixed-curved-openings.reverse","quality":{"kind":"Analytic"},"revision":2,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":0,"id":0,"tolerance":6.3e-8,"twin_halfedge":72},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":5.759586531581287,"lo":5.483185307179586}},"halfedge":1,"id":1,"tolerance":6.3e-8,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":5.907761699198547,"lo":5.759586531581287}},"halfedge":2,"id":2,"tolerance":6.3e-8,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":7.083185307179586,"lo":5.907761699198547}},"halfedge":3,"id":3,"tolerance":6.3e-8,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.2999999999999996,"lo":0.0}},"halfedge":4,"id":4,"tolerance":6.3e-8,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":7.083185307179586,"lo":5.863571923609742}},"halfedge":5,"id":5,"tolerance":6.3e-8,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":5.863571923609742,"lo":5.6941543369633125}},"halfedge":6,"id":6,"tolerance":6.3e-8,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":5.6941543369633125,"lo":5.483185307179586}},"halfedge":7,"id":7,"tolerance":6.3e-8,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":5.863571923609742,"lo":5.6941543369633125}},"halfedge":8,"id":8,"tolerance":6.3e-8,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.3252915462029815,"lo":0.0}},"halfedge":9,"id":9,"tolerance":6.3e-8,"twin_halfedge":84},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":5.907761699198547,"lo":5.759586531581287}},"halfedge":10,"id":10,"tolerance":6.3e-8,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.3530817792889511,"lo":0.0}},"halfedge":11,"id":11,"tolerance":6.3e-8,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":0.3530817792889511,"lo":0.0}},"halfedge":12,"id":12,"tolerance":6.3e-8,"twin_halfedge":82},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":5.907761699198547,"lo":5.759586531581287}},"halfedge":13,"id":13,"tolerance":6.3e-8,"twin_halfedge":92},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.3252915462029815,"lo":0.0}},"halfedge":14,"id":14,"tolerance":6.3e-8,"twin_halfedge":86},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":5.863571923609742,"lo":5.6941543369633125}},"halfedge":15,"id":15,"tolerance":6.3e-8,"twin_halfedge":88},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":5.6941543369633125,"lo":5.483185307179586}},"halfedge":16,"id":16,"tolerance":6.3e-8,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":5.863571923609742,"lo":5.6941543369633125}},"halfedge":17,"id":17,"tolerance":6.3e-8,"twin_halfedge":90},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":7.083185307179586,"lo":5.863571923609742}},"halfedge":18,"id":18,"tolerance":6.3e-8,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.2999999999999996,"lo":0.0}},"halfedge":19,"id":19,"tolerance":6.3e-8,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":7.083185307179586,"lo":5.907761699198547}},"halfedge":20,"id":20,"tolerance":6.3e-8,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":5.907761699198547,"lo":5.759586531581287}},"halfedge":21,"id":21,"tolerance":6.3e-8,"twin_halfedge":94},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":5.759586531581287,"lo":5.483185307179586}},"halfedge":22,"id":22,"tolerance":6.3e-8,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.29999999999999993,"lo":0.0}},"halfedge":23,"id":23,"tolerance":6.3e-8,"twin_halfedge":76},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":25,"id":24,"tolerance":6.3e-8,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":26,"id":25,"tolerance":6.3e-8,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":27,"id":26,"tolerance":6.3e-8,"twin_halfedge":91},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":29,"id":27,"tolerance":6.3e-8,"twin_halfedge":75},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":30,"id":28,"tolerance":6.3e-8,"twin_halfedge":74},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":31,"id":29,"tolerance":6.3e-8,"twin_halfedge":73},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":33,"id":30,"tolerance":6.3e-8,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":37,"id":31,"tolerance":6.3e-8,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":38,"id":32,"tolerance":6.3e-8,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":39,"id":33,"tolerance":6.3e-8,"twin_halfedge":49},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":41,"id":34,"tolerance":6.3e-8,"twin_halfedge":89},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":42,"id":35,"tolerance":6.3e-8,"twin_halfedge":85},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":45,"id":36,"tolerance":6.3e-8,"twin_halfedge":59},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":46,"id":37,"tolerance":6.3e-8,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":47,"id":38,"tolerance":6.3e-8,"twin_halfedge":57},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":53,"id":39,"tolerance":6.3e-8,"twin_halfedge":63},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":54,"id":40,"tolerance":6.3e-8,"twin_halfedge":87},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":55,"id":41,"tolerance":6.3e-8,"twin_halfedge":95},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":61,"id":42,"tolerance":6.3e-8,"twin_halfedge":71},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":65,"id":43,"tolerance":6.3e-8,"twin_halfedge":79},{"chart_seam":false,"geometry":{"curve":44,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":66,"id":44,"tolerance":6.3e-8,"twin_halfedge":78},{"chart_seam":false,"geometry":{"curve":45,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":67,"id":45,"tolerance":6.3e-8,"twin_halfedge":77},{"chart_seam":false,"geometry":{"curve":46,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":69,"id":46,"tolerance":6.3e-8,"twin_halfedge":93},{"chart_seam":false,"geometry":{"curve":47,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":70,"id":47,"tolerance":6.3e-8,"twin_halfedge":81},{"chart_seam":false,"geometry":{"curve":48,"kind":"Curve","range":{"hi":3.147877503993888,"lo":0.0}},"halfedge":96,"id":48,"tolerance":6.3e-8,"twin_halfedge":97},{"chart_seam":false,"geometry":{"curve":49,"kind":"Curve","range":{"hi":3.146489221673897,"lo":0.0}},"halfedge":98,"id":49,"tolerance":6.3e-8,"twin_halfedge":99}],"faces":[{"id":0,"key":"batch-staged.mixed-curved-openings.reverse:planar:lower-cap-0","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.000000063,"lo":1.8811080522373467},{"hi":2.1520683356985684,"lo":-2.1520683356985684}]}},{"id":1,"key":"batch-staged.mixed-curved-openings.reverse:planar:upper-cap-1","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.791057210390573,"lo":2.244994369064365},{"hi":-1.0999999369999995,"lo":-1.500000063}]}},{"id":2,"key":"batch-staged.mixed-curved-openings.reverse:planar:lower-cap-2","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.791057210390573,"lo":2.244994369064365},{"hi":1.500000063,"lo":1.0999999369999995}]}},{"id":3,"key":"batch-staged.mixed-curved-openings.reverse:planar:upper-cap-3","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.000000063,"lo":1.8811080522373467},{"hi":2.1520683356985684,"lo":-2.1520683356985684}]}},{"id":4,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-cylinder-4","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":5.694154360296646,"lo":5.483185283846253},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":5,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-cylinder-5","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":5.863571946943075,"lo":5.694154313629979},{"hi":1.000000063,"lo":-6.3e-8}]}},{"id":6,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-cylinder-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[16],"outer":6,"uv_bounds":[{"hi":7.0831853305129195,"lo":5.863571900276408},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":7,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-plane-7","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":2,"key":"side-0-0"}]},"sense":"Reverse","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":0.3000000629999996,"lo":-6.3e-8},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":8,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-cylinder-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[18],"outer":8,"uv_bounds":[{"hi":7.083185328179586,"lo":5.907761678198547},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":9,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-cylinder-9","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":5.907761720198547,"lo":5.759586510581287},{"hi":1.000000063,"lo":-6.3e-8}]}},{"id":10,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-cylinder-10","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":5.759586552581287,"lo":5.4831852861795864},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":11,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-plane-11","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":0.30000006299999993,"lo":-6.3e-8},{"hi":3.000000063,"lo":-6.3e-8}]}},{"id":12,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-plane-12","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":0.3530818422889511,"lo":-6.3e-8},{"hi":0.500000063,"lo":-6.3e-8}]}},{"id":13,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-plane-13","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"rectangular-opening","entity":"rectangular-opening","face":0,"key":"bottom"}]},"sense":"Reverse","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":0.32529160920298195,"lo":-6.3e-8},{"hi":0.500000063,"lo":-6.3e-8}]}},{"id":14,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-cylinder-14","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":5.863571946943075,"lo":5.694154313629979},{"hi":3.000000063,"lo":1.499999937}]}},{"id":15,"key":"batch-staged.mixed-curved-openings.reverse:planar:vertical-cylinder-15","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"mixed-curved-host","entity":"mixed-curved-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":5.907761720198547,"lo":5.759586510581287},{"hi":3.000000063,"lo":1.499999937}]}},{"id":16,"key":"lateral:through-cut","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"round-opening","entity":"round-opening","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[19],"outer":17,"uv_bounds":[{"hi":-2.081576844571777,"lo":-8.52028169592909},{"hi":0.3499989716450176,"lo":0.0033002839911298975}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":7,"to":1,"twin":72,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":64,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":60,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":4,"prev":2,"to":4,"twin":52,"wire_ref":null},{"edge":4,"face":0,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":0,"next":5,"prev":3,"to":5,"twin":44,"wire_ref":null},{"edge":5,"face":0,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":0,"next":6,"prev":4,"to":6,"twin":36,"wire_ref":null},{"edge":6,"face":0,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":0,"next":7,"prev":5,"to":7,"twin":32,"wire_ref":null},{"edge":7,"face":0,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":0,"next":0,"prev":6,"to":0,"twin":24,"wire_ref":null},{"edge":8,"face":1,"from":8,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":1,"next":9,"prev":11,"to":9,"twin":34,"wire_ref":null},{"edge":9,"face":1,"from":9,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":1,"next":10,"prev":8,"to":10,"twin":84,"wire_ref":null},{"edge":10,"face":1,"from":10,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":1,"next":11,"prev":9,"to":11,"twin":62,"wire_ref":null},{"edge":11,"face":1,"from":11,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":1,"next":8,"prev":10,"to":8,"twin":80,"wire_ref":null},{"edge":12,"face":2,"from":12,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":2,"next":13,"prev":15,"to":13,"twin":82,"wire_ref":null},{"edge":13,"face":2,"from":13,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":2,"next":14,"prev":12,"to":14,"twin":92,"wire_ref":null},{"edge":14,"face":2,"from":14,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":2,"next":15,"prev":13,"to":15,"twin":86,"wire_ref":null},{"edge":15,"face":2,"from":15,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":2,"next":12,"prev":14,"to":12,"twin":88,"wire_ref":null},{"edge":16,"face":3,"from":16,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":3,"next":17,"prev":23,"to":17,"twin":28,"wire_ref":null},{"edge":17,"face":3,"from":17,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":3,"next":18,"prev":16,"to":18,"twin":90,"wire_ref":null},{"edge":18,"face":3,"from":18,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":3,"next":19,"prev":17,"to":19,"twin":40,"wire_ref":null},{"edge":19,"face":3,"from":19,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":3,"next":20,"prev":18,"to":20,"twin":48,"wire_ref":null},{"edge":20,"face":3,"from":20,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Reverse"},"id":20,"loop_ref":3,"next":21,"prev":19,"to":21,"twin":56,"wire_ref":null},{"edge":21,"face":3,"from":21,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Reverse"},"id":21,"loop_ref":3,"next":22,"prev":20,"to":22,"twin":94,"wire_ref":null},{"edge":22,"face":3,"from":22,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":3,"next":23,"prev":21,"to":23,"twin":68,"wire_ref":null},{"edge":23,"face":3,"from":23,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":3,"next":16,"prev":22,"to":16,"twin":76,"wire_ref":null},{"edge":7,"face":4,"from":0,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":4,"next":25,"prev":31,"to":7,"twin":7,"wire_ref":null},{"edge":24,"face":4,"from":7,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":4,"next":26,"prev":24,"to":8,"twin":35,"wire_ref":null},{"edge":25,"face":4,"from":8,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Forward"},"id":26,"loop_ref":4,"next":27,"prev":25,"to":12,"twin":83,"wire_ref":null},{"edge":26,"face":4,"from":12,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":4,"next":28,"prev":26,"to":17,"twin":91,"wire_ref":null},{"edge":16,"face":4,"from":17,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Reverse"},"id":28,"loop_ref":4,"next":29,"prev":27,"to":16,"twin":16,"wire_ref":null},{"edge":27,"face":4,"from":16,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":4,"next":30,"prev":28,"to":24,"twin":75,"wire_ref":null},{"edge":28,"face":4,"from":24,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Forward"},"id":30,"loop_ref":4,"next":31,"prev":29,"to":25,"twin":74,"wire_ref":null},{"edge":29,"face":4,"from":25,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Forward"},"id":31,"loop_ref":4,"next":24,"prev":30,"to":0,"twin":73,"wire_ref":null},{"edge":6,"face":5,"from":7,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":5,"next":33,"prev":35,"to":6,"twin":6,"wire_ref":null},{"edge":30,"face":5,"from":6,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":5,"next":34,"prev":32,"to":9,"twin":43,"wire_ref":null},{"edge":8,"face":5,"from":9,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":5,"next":35,"prev":33,"to":8,"twin":8,"wire_ref":null},{"edge":24,"face":5,"from":8,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":5,"next":32,"prev":34,"to":7,"twin":25,"wire_ref":null},{"edge":5,"face":6,"from":6,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":6,"next":37,"prev":43,"to":5,"twin":5,"wire_ref":null},{"edge":31,"face":6,"from":5,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":6,"next":38,"prev":36,"to":26,"twin":51,"wire_ref":null},{"edge":32,"face":6,"from":26,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Forward"},"id":38,"loop_ref":6,"next":39,"prev":37,"to":27,"twin":50,"wire_ref":null},{"edge":33,"face":6,"from":27,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":6,"next":40,"prev":38,"to":19,"twin":49,"wire_ref":null},{"edge":18,"face":6,"from":19,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Reverse"},"id":40,"loop_ref":6,"next":41,"prev":39,"to":18,"twin":18,"wire_ref":null},{"edge":34,"face":6,"from":18,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":6,"next":42,"prev":40,"to":15,"twin":89,"wire_ref":null},{"edge":35,"face":6,"from":15,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Forward"},"id":42,"loop_ref":6,"next":43,"prev":41,"to":9,"twin":85,"wire_ref":null},{"edge":30,"face":6,"from":9,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":6,"next":36,"prev":42,"to":6,"twin":33,"wire_ref":null},{"edge":4,"face":7,"from":5,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Reverse"},"id":44,"loop_ref":7,"next":45,"prev":51,"to":4,"twin":4,"wire_ref":null},{"edge":36,"face":7,"from":4,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":7,"next":46,"prev":44,"to":28,"twin":59,"wire_ref":null},{"edge":37,"face":7,"from":28,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Forward"},"id":46,"loop_ref":7,"next":47,"prev":45,"to":29,"twin":58,"wire_ref":null},{"edge":38,"face":7,"from":29,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Forward"},"id":47,"loop_ref":7,"next":48,"prev":46,"to":20,"twin":57,"wire_ref":null},{"edge":19,"face":7,"from":20,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Reverse"},"id":48,"loop_ref":7,"next":49,"prev":47,"to":19,"twin":19,"wire_ref":null},{"edge":33,"face":7,"from":19,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Reverse"},"id":49,"loop_ref":7,"next":50,"prev":48,"to":27,"twin":39,"wire_ref":null},{"edge":32,"face":7,"from":27,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":7,"next":51,"prev":49,"to":26,"twin":38,"wire_ref":null},{"edge":31,"face":7,"from":26,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":7,"next":44,"prev":50,"to":5,"twin":37,"wire_ref":null},{"edge":3,"face":8,"from":4,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Reverse"},"id":52,"loop_ref":8,"next":53,"prev":59,"to":3,"twin":3,"wire_ref":null},{"edge":39,"face":8,"from":3,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":8,"next":54,"prev":52,"to":10,"twin":63,"wire_ref":null},{"edge":40,"face":8,"from":10,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Forward"},"id":54,"loop_ref":8,"next":55,"prev":53,"to":14,"twin":87,"wire_ref":null},{"edge":41,"face":8,"from":14,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Forward"},"id":55,"loop_ref":8,"next":56,"prev":54,"to":21,"twin":95,"wire_ref":null},{"edge":20,"face":8,"from":21,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":8,"next":57,"prev":55,"to":20,"twin":20,"wire_ref":null},{"edge":38,"face":8,"from":20,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Reverse"},"id":57,"loop_ref":8,"next":58,"prev":56,"to":29,"twin":47,"wire_ref":null},{"edge":37,"face":8,"from":29,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":8,"next":59,"prev":57,"to":28,"twin":46,"wire_ref":null},{"edge":36,"face":8,"from":28,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":8,"next":52,"prev":58,"to":4,"twin":45,"wire_ref":null},{"edge":2,"face":9,"from":3,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Reverse"},"id":60,"loop_ref":9,"next":61,"prev":63,"to":2,"twin":2,"wire_ref":null},{"edge":42,"face":9,"from":2,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Forward"},"id":61,"loop_ref":9,"next":62,"prev":60,"to":11,"twin":71,"wire_ref":null},{"edge":10,"face":9,"from":11,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Forward"},"id":62,"loop_ref":9,"next":63,"prev":61,"to":10,"twin":10,"wire_ref":null},{"edge":39,"face":9,"from":10,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Reverse"},"id":63,"loop_ref":9,"next":60,"prev":62,"to":3,"twin":53,"wire_ref":null},{"edge":1,"face":10,"from":2,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Reverse"},"id":64,"loop_ref":10,"next":65,"prev":71,"to":1,"twin":1,"wire_ref":null},{"edge":43,"face":10,"from":1,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Forward"},"id":65,"loop_ref":10,"next":66,"prev":64,"to":30,"twin":79,"wire_ref":null},{"edge":44,"face":10,"from":30,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Forward"},"id":66,"loop_ref":10,"next":67,"prev":65,"to":31,"twin":78,"wire_ref":null},{"edge":45,"face":10,"from":31,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Forward"},"id":67,"loop_ref":10,"next":68,"prev":66,"to":23,"twin":77,"wire_ref":null},{"edge":22,"face":10,"from":23,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":10,"next":69,"prev":67,"to":22,"twin":22,"wire_ref":null},{"edge":46,"face":10,"from":22,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Forward"},"id":69,"loop_ref":10,"next":70,"prev":68,"to":13,"twin":93,"wire_ref":null},{"edge":47,"face":10,"from":13,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Forward"},"id":70,"loop_ref":10,"next":71,"prev":69,"to":11,"twin":81,"wire_ref":null},{"edge":42,"face":10,"from":11,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Reverse"},"id":71,"loop_ref":10,"next":64,"prev":70,"to":2,"twin":61,"wire_ref":null},{"edge":0,"face":11,"from":1,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Reverse"},"id":72,"loop_ref":11,"next":73,"prev":79,"to":0,"twin":0,"wire_ref":null},{"edge":29,"face":11,"from":0,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Reverse"},"id":73,"loop_ref":11,"next":74,"prev":72,"to":25,"twin":31,"wire_ref":null},{"edge":28,"face":11,"from":25,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":11,"next":75,"prev":73,"to":24,"twin":30,"wire_ref":null},{"edge":27,"face":11,"from":24,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Reverse"},"id":75,"loop_ref":11,"next":76,"prev":74,"to":16,"twin":29,"wire_ref":null},{"edge":23,"face":11,"from":16,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Reverse"},"id":76,"loop_ref":11,"next":77,"prev":75,"to":23,"twin":23,"wire_ref":null},{"edge":45,"face":11,"from":23,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Reverse"},"id":77,"loop_ref":11,"next":78,"prev":76,"to":31,"twin":67,"wire_ref":null},{"edge":44,"face":11,"from":31,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":11,"next":79,"prev":77,"to":30,"twin":66,"wire_ref":null},{"edge":43,"face":11,"from":30,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Reverse"},"id":79,"loop_ref":11,"next":72,"prev":78,"to":1,"twin":65,"wire_ref":null},{"edge":11,"face":12,"from":8,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Reverse"},"id":80,"loop_ref":12,"next":81,"prev":83,"to":11,"twin":11,"wire_ref":null},{"edge":47,"face":12,"from":11,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Reverse"},"id":81,"loop_ref":12,"next":82,"prev":80,"to":13,"twin":70,"wire_ref":null},{"edge":12,"face":12,"from":13,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":12,"next":83,"prev":81,"to":12,"twin":12,"wire_ref":null},{"edge":25,"face":12,"from":12,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":12,"next":80,"prev":82,"to":8,"twin":26,"wire_ref":null},{"edge":9,"face":13,"from":10,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Reverse"},"id":84,"loop_ref":13,"next":85,"prev":87,"to":9,"twin":9,"wire_ref":null},{"edge":35,"face":13,"from":9,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Reverse"},"id":85,"loop_ref":13,"next":86,"prev":84,"to":15,"twin":42,"wire_ref":null},{"edge":14,"face":13,"from":15,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Reverse"},"id":86,"loop_ref":13,"next":87,"prev":85,"to":14,"twin":14,"wire_ref":null},{"edge":40,"face":13,"from":14,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Reverse"},"id":87,"loop_ref":13,"next":84,"prev":86,"to":10,"twin":54,"wire_ref":null},{"edge":15,"face":14,"from":12,"geometry_use":{"pcurve":88,"periodic_lift":[0,0],"sense":"Forward"},"id":88,"loop_ref":14,"next":89,"prev":91,"to":15,"twin":15,"wire_ref":null},{"edge":34,"face":14,"from":15,"geometry_use":{"pcurve":89,"periodic_lift":[0,0],"sense":"Reverse"},"id":89,"loop_ref":14,"next":90,"prev":88,"to":18,"twin":41,"wire_ref":null},{"edge":17,"face":14,"from":18,"geometry_use":{"pcurve":90,"periodic_lift":[0,0],"sense":"Reverse"},"id":90,"loop_ref":14,"next":91,"prev":89,"to":17,"twin":17,"wire_ref":null},{"edge":26,"face":14,"from":17,"geometry_use":{"pcurve":91,"periodic_lift":[0,0],"sense":"Reverse"},"id":91,"loop_ref":14,"next":88,"prev":90,"to":12,"twin":27,"wire_ref":null},{"edge":13,"face":15,"from":14,"geometry_use":{"pcurve":92,"periodic_lift":[0,0],"sense":"Reverse"},"id":92,"loop_ref":15,"next":93,"prev":95,"to":13,"twin":13,"wire_ref":null},{"edge":46,"face":15,"from":13,"geometry_use":{"pcurve":93,"periodic_lift":[0,0],"sense":"Reverse"},"id":93,"loop_ref":15,"next":94,"prev":92,"to":22,"twin":69,"wire_ref":null},{"edge":21,"face":15,"from":22,"geometry_use":{"pcurve":94,"periodic_lift":[0,0],"sense":"Forward"},"id":94,"loop_ref":15,"next":95,"prev":93,"to":21,"twin":21,"wire_ref":null},{"edge":41,"face":15,"from":21,"geometry_use":{"pcurve":95,"periodic_lift":[0,0],"sense":"Reverse"},"id":95,"loop_ref":15,"next":92,"prev":94,"to":14,"twin":55,"wire_ref":null},{"edge":48,"face":6,"from":32,"geometry_use":{"pcurve":96,"periodic_lift":[0,0],"sense":"Forward"},"id":96,"loop_ref":16,"next":96,"prev":96,"to":32,"twin":97,"wire_ref":null},{"edge":48,"face":16,"from":32,"geometry_use":{"pcurve":97,"periodic_lift":[0,0],"sense":"Reverse"},"id":97,"loop_ref":17,"next":97,"prev":97,"to":32,"twin":96,"wire_ref":null},{"edge":49,"face":8,"from":33,"geometry_use":{"pcurve":98,"periodic_lift":[0,0],"sense":"Forward"},"id":98,"loop_ref":18,"next":98,"prev":98,"to":33,"twin":99,"wire_ref":null},{"edge":49,"face":16,"from":33,"geometry_use":{"pcurve":99,"periodic_lift":[0,0],"sense":"Reverse"},"id":99,"loop_ref":19,"next":99,"prev":99,"to":33,"twin":98,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":8},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":12},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":16},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":24},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":32},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":36},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":44},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":52},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":60},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":64},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":72},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":80},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":84},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":88},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":92},{"face_ref":6,"id":16,"is_hole":true,"start_halfedge":96},{"face_ref":16,"id":17,"is_hole":false,"start_halfedge":97},{"face_ref":8,"id":18,"is_hole":true,"start_halfedge":98},{"face_ref":16,"id":19,"is_hole":true,"start_halfedge":99}],"shells":[{"faces":[0,11,10,9,8,7,6,5,4,3,15,12,1,13,14,2,16],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.8811081152373468,0.0,1.9368614454287116],"tolerance":6.3e-8},{"id":1,"outgoing_halfedge":1,"position":[2.0901201280414963,0.0,2.1520682726985685],"tolerance":6.3e-8},{"id":2,"outgoing_halfedge":2,"position":[2.598076211353316,0.0,1.5],"tolerance":6.3e-8},{"id":3,"outgoing_halfedge":3,"position":[2.791057147390573,0.0,1.0999999999999992],"tolerance":6.3e-8},{"id":4,"outgoing_halfedge":4,"position":[2.0901201280414963,0.0,-2.1520682726985685],"tolerance":6.3e-8},{"id":5,"outgoing_halfedge":5,"position":[1.8811081152373468,0.0,-1.9368614454287116],"tolerance":6.3e-8},{"id":6,"outgoing_halfedge":6,"position":[2.4657656011875906,0.0,1.1000000000000003],"tolerance":6.3e-8},{"id":7,"outgoing_halfedge":7,"position":[2.244994432064365,0.0,1.4999999999999998],"tolerance":6.3e-8},{"id":8,"outgoing_halfedge":8,"position":[2.244994432064365,1.0,1.4999999999999998],"tolerance":6.3e-8},{"id":9,"outgoing_halfedge":9,"position":[2.4657656011875906,1.0,1.1000000000000005],"tolerance":6.3e-8},{"id":10,"outgoing_halfedge":10,"position":[2.7910571473905725,1.0,1.1],"tolerance":6.3e-8},{"id":11,"outgoing_halfedge":11,"position":[2.598076211353316,1.0,1.5],"tolerance":6.3e-8},{"id":12,"outgoing_halfedge":12,"position":[2.244994432064365,1.5,1.5],"tolerance":6.3e-8},{"id":13,"outgoing_halfedge":13,"position":[2.598076211353316,1.5,1.5],"tolerance":6.3e-8},{"id":14,"outgoing_halfedge":14,"position":[2.791057147390573,1.5,1.0999999999999996],"tolerance":6.3e-8},{"id":15,"outgoing_halfedge":15,"position":[2.465765601187591,1.5,1.1],"tolerance":6.3e-8},{"id":16,"outgoing_halfedge":16,"position":[1.8811081152373468,3.0,1.9368614454287116],"tolerance":6.3e-8},{"id":17,"outgoing_halfedge":17,"position":[2.244994432064365,3.0,1.4999999999999998],"tolerance":6.3e-8},{"id":18,"outgoing_halfedge":18,"position":[2.4657656011875906,3.0,1.1000000000000005],"tolerance":6.3e-8},{"id":19,"outgoing_halfedge":19,"position":[1.8811081152373468,3.0,-1.9368614454287116],"tolerance":6.3e-8},{"id":20,"outgoing_halfedge":20,"position":[2.0901201280414963,3.0,-2.152068272698568],"tolerance":6.3e-8},{"id":21,"outgoing_halfedge":21,"position":[2.791057147390573,3.0,1.0999999999999992],"tolerance":6.3e-8},{"id":22,"outgoing_halfedge":22,"position":[2.598076211353316,3.0,1.5],"tolerance":6.3e-8},{"id":23,"outgoing_halfedge":23,"position":[2.0901201280414963,3.0,2.1520682726985685],"tolerance":6.3e-8},{"id":24,"outgoing_halfedge":30,"position":[1.8811081152373468,1.5,1.9368614454287116],"tolerance":6.3e-8},{"id":25,"outgoing_halfedge":31,"position":[1.8811081152373468,1.0,1.9368614454287116],"tolerance":6.3e-8},{"id":26,"outgoing_halfedge":38,"position":[1.8811081152373468,1.0,-1.9368614454287116],"tolerance":6.3e-8},{"id":27,"outgoing_halfedge":39,"position":[1.8811081152373468,1.5,-1.9368614454287116],"tolerance":6.3e-8},{"id":28,"outgoing_halfedge":46,"position":[2.0901201280414963,1.0,-2.152068272698568],"tolerance":6.3e-8},{"id":29,"outgoing_halfedge":47,"position":[2.0901201280414963,1.5,-2.152068272698568],"tolerance":6.3e-8},{"id":30,"outgoing_halfedge":66,"position":[2.0901201280414963,1.0,2.1520682726985685],"tolerance":6.3e-8},{"id":31,"outgoing_halfedge":67,"position":[2.0901201280414963,1.5,2.1520682726985685],"tolerance":6.3e-8},{"id":32,"outgoing_halfedge":96,"position":[2.682255636269859,1.1069411008145305,0.30903834988326967],"tolerance":6.3e-8},{"id":33,"outgoing_halfedge":98,"position":[2.990025593501361,1.0638199343388153,0.244431892777589],"tolerance":6.3e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/vertical-arc.arc-host.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/vertical-arc.arc-host.json new file mode 100644 index 0000000..65a406e --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/vertical-arc.arc-host.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.1,0.9,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.1,0.9,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1,0.9,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.1,0.9,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.1,0.9,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.1,1.25,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1,1.25,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.1,1.25,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.1,0.9,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.1,0.9,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.1,0.9,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.1,1.25,2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.35,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.35]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.35]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.35,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.1,0.9,0.5],"x":[0.0,1.0,0.0],"y":[1.0,0.0,0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.1,0.9,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.1,0.9,0.5],"x":[0.0,0.0,1.0],"y":[0.0,1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.1,0.9,0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.1,0.9,0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.1,1.25,0.5],"x":[0.0,0.0,1.0],"y":[1.0,0.0,0.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"opening","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.35,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.35,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-9,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-9,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.35,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-9,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.35,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-9,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening","entity":"opening","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.350000001,"lo":-1e-9},{"hi":1.000000001,"lo":-1e-9}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening","entity":"opening","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":0.350000001,"lo":-1e-9}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening","entity":"opening","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.500000001,"lo":-1e-9},{"hi":0.350000001,"lo":-1e-9}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening","entity":"opening","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.350000001,"lo":-1e-9},{"hi":1.500000001,"lo":-1e-9}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening","entity":"opening","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.000000001,"lo":-1e-9},{"hi":1.500000001,"lo":-1e-9}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"opening","entity":"opening","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.500000001,"lo":-1e-9},{"hi":1.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.1,0.9,0.5],"tolerance":1e-9},{"id":1,"outgoing_halfedge":3,"position":[2.1,0.9,0.5],"tolerance":1e-9},{"id":2,"outgoing_halfedge":1,"position":[1.1,1.25,0.5],"tolerance":1e-9},{"id":3,"outgoing_halfedge":2,"position":[2.1,1.25,0.5],"tolerance":1e-9},{"id":4,"outgoing_halfedge":4,"position":[1.1,0.9,2.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":5,"position":[2.1,0.9,2.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":7,"position":[1.1,1.25,2.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":6,"position":[2.1,1.25,2.0],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.6,1.3,0.8]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.6,1.3,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.6,1.3,0.8]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.05,1.3,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.05,1.3,0.8]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.6,2.1,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.6,2.1,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.05,2.1,0.8]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.6,1.3,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.6,1.3,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.05,1.3,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.6,2.1,2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.45000000000000007]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.45000000000000007,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.2]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.45000000000000007,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.2]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.2,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.45000000000000007]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.6,1.3,0.8],"x":[0.0,1.0,0.0],"y":[1.0,0.0,0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.6,1.3,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.6,1.3,0.8],"x":[0.0,0.0,1.0],"y":[0.0,1.0,0.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.05,1.3,0.8],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.6,1.3,0.8],"x":[1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.6,2.1,0.8],"x":[0.0,0.0,1.0],"y":[1.0,0.0,0.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"second-opening","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.45,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-9,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.45,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-9,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.45,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-9,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.45,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-9,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-opening","entity":"second-opening","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.800000001,"lo":-1e-9},{"hi":0.4500000010000001,"lo":-1e-9}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-opening","entity":"second-opening","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4500000010000001,"lo":-1e-9},{"hi":0.800000001,"lo":-1e-9}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-opening","entity":"second-opening","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.200000001,"lo":-1e-9},{"hi":0.800000001,"lo":-1e-9}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-opening","entity":"second-opening","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.800000001,"lo":-1e-9},{"hi":1.200000001,"lo":-1e-9}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-opening","entity":"second-opening","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.4500000010000001,"lo":-1e-9},{"hi":1.200000001,"lo":-1e-9}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"second-opening","entity":"second-opening","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.200000001,"lo":-1e-9},{"hi":0.4500000010000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.6,1.3,0.8],"tolerance":1e-9},{"id":1,"outgoing_halfedge":3,"position":[1.05,1.3,0.8],"tolerance":1e-9},{"id":2,"outgoing_halfedge":1,"position":[0.6,2.1,0.8],"tolerance":1e-9},{"id":3,"outgoing_halfedge":2,"position":[1.05,2.1,0.8],"tolerance":1e-9},{"id":4,"outgoing_halfedge":4,"position":[0.6,1.3,2.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":5,"position":[1.05,1.3,2.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":7,"position":[0.6,2.1,2.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":6,"position":[1.05,2.1,2.0],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_vertical_arc_extrusion_batch"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.0,2.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[9.184850993605148e-17,1.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.0,0.0]}],"intersections":[],"pcurves":[{"axis_a":[2.0,0.0],"axis_b":[0.0,2.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,2.0]},{"axis_a":[1.5,0.0],"axis_b":[0.0,1.5],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.5,-0.0]},{"axis_a":[1.5,-0.0],"axis_b":[0.0,-1.5],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,-2.0]},{"axis_a":[2.0,-0.0],"axis_b":[0.0,-2.0],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5707963267948966,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5707963267948966,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.0},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.8369701987210297e-16,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,-1.8369701987210297e-16,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.5},{"frame":{"origin":[1.5,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"}]},"id":"arc-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-host","entity":"arc-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.000000001,"lo":-2.000000001},{"hi":2.000000001,"lo":-2.000000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-host","entity":"arc-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.000000001,"lo":-2.000000001},{"hi":2.000000001,"lo":-2.000000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-host","entity":"arc-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.5707963272948966,"lo":-5e-10},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-host","entity":"arc-host","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-host","entity":"arc-host","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.5707963274615633,"lo":-6.666666666666667e-10},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-host","entity":"arc-host","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[2.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[0.0,2.0,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[9.184850993605148e-17,1.5,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.5,0.0,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[2.0,0.0,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[0.0,2.0,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[9.184850993605148e-17,1.5,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.5,0.0,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"direction":[1.8369701987210297e-16,-1.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.1,1.019803902718557,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.1,1.25,0.5]},{"frame":{"origin":[0.0,0.0,0.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.786057109949175,0.9,0.5]},{"frame":{"origin":[0.0,0.0,0.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"frame":{"origin":[0.0,0.0,0.8],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.05,1.70220445305492,0.8]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.05,1.3,0.8]},{"frame":{"origin":[0.0,0.0,0.8],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.6,1.374772708486752,0.8]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.6,1.9078784028338913,2.0]},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.748331477354788,1.3,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.05,1.3,2.0]},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.1999999999999997,0.9,2.0]},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.5612494995995996,1.25,2.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[1.1,1.25,2.0]},{"direction":[-1.8369701987210297e-16,1.0,0.0],"kind":"Line","origin":[9.184850993605148e-17,1.5,3.0]},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.0},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,3.0]},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.5},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,2.0,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,2.0,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,2.0,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.184850993605148e-17,1.5,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.184850993605148e-17,1.5,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.184850993605148e-17,1.5,0.8]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[9.184850993605148e-17,1.5,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.5999999999999999,1.9078784028338913,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.5999999999999999,1.9078784028338913,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.5999999999999999,1.9078784028338913,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.5999999999999999,1.9078784028338913,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.05,1.7022044530549203,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.05,1.7022044530549203,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5612494995995996,1.2499999999999998,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5612494995995996,1.2499999999999998,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5612494995995996,1.2499999999999998,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5612494995995996,1.2499999999999998,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.05,1.7022044530549203,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.05,1.7022044530549203,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.7860571099491753,0.8999999999999998,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,1.1102230246251565e-16,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,1.1102230246251565e-16,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,1.1102230246251565e-16,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.0,1.1102230246251565e-16,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.786057109949175,0.8999999999999999,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.786057109949175,0.8999999999999999,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.786057109949175,0.8999999999999999,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.0,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.0,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5,0.0,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.2000000000000002,0.8999999999999998,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.2000000000000002,0.8999999999999998,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.2000000000000002,0.8999999999999998,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.2000000000000002,0.8999999999999998,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1,1.0198039027185568,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.7483314773547882,1.3,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.7483314773547882,1.3,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.7483314773547882,1.3,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.7483314773547882,1.3,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.1,1.0198039027185568,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.1,1.0198039027185568,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.1,1.0198039027185568,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.5999999999999999,1.374772708486752,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.5999999999999999,1.374772708486752,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.5999999999999999,1.374772708486752,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.5999999999999999,1.374772708486752,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1,1.25,0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1,1.25,0.8]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.05,1.3,0.8]}],"intersections":[],"pcurves":[{"axis_a":[1.5,-0.0],"axis_b":[0.0,-1.5],"kind":"Conic2","origin":[0.0,-0.0]},{"axis_a":[1.5,-0.0],"axis_b":[0.0,-1.5],"kind":"Conic2","origin":[0.0,-0.0]},{"axis_a":[1.5,-0.0],"axis_b":[0.0,-1.5],"kind":"Conic2","origin":[0.0,-0.0]},{"axis_a":[1.5,-0.0],"axis_b":[0.0,-1.5],"kind":"Conic2","origin":[0.0,-0.0]},{"axis_a":[1.5,-0.0],"axis_b":[0.0,-1.5],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.5,-0.0]},{"axis_a":[2.0,-0.0],"axis_b":[0.0,-2.0],"kind":"Conic2","origin":[0.0,-0.0]},{"axis_a":[2.0,-0.0],"axis_b":[0.0,-2.0],"kind":"Conic2","origin":[0.0,-0.0]},{"axis_a":[2.0,-0.0],"axis_b":[0.0,-2.0],"kind":"Conic2","origin":[0.0,-0.0]},{"axis_a":[2.0,-0.0],"axis_b":[0.0,-2.0],"kind":"Conic2","origin":[0.0,-0.0]},{"axis_a":[2.0,-0.0],"axis_b":[0.0,-2.0],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[1.8369701987210297e-16,1.0],"kind":"Line2","origin":[0.0,-2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.1,1.019803902718557]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.1,1.25]},{"axis_a":[2.0,0.0],"axis_b":[0.0,2.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.786057109949175,0.9]},{"axis_a":[1.5,0.0],"axis_b":[0.0,1.5],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.0,0.0],"axis_b":[0.0,2.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.05,1.70220445305492]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.05,1.3]},{"axis_a":[1.5,0.0],"axis_b":[0.0,1.5],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6,1.374772708486752]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6,-1.9078784028338913]},{"axis_a":[1.5,-0.0],"axis_b":[0.0,-1.5],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[0.748331477354788,-1.3]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.05,-1.3]},{"axis_a":[2.0,-0.0],"axis_b":[0.0,-2.0],"kind":"Conic2","origin":[0.0,-0.0]},{"axis_a":[1.5,-0.0],"axis_b":[0.0,-1.5],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[1.0,-0.0],"kind":"Line2","origin":[1.1999999999999997,-0.9]},{"axis_a":[2.0,-0.0],"axis_b":[0.0,-2.0],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.5612494995995996,-1.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.1,-1.25]},{"direction":[-1.8369701987210297e-16,1.0],"kind":"Line2","origin":[9.184850993605148e-17,1.5]},{"axis_a":[2.0,0.0],"axis_b":[0.0,2.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.0,0.0],"axis_b":[0.0,2.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.0,0.0],"axis_b":[0.0,2.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.0,0.0],"axis_b":[0.0,2.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.0,0.0],"axis_b":[0.0,2.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"axis_a":[1.5,0.0],"axis_b":[0.0,1.5],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.5,0.0],"axis_b":[0.0,1.5],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.5,0.0],"axis_b":[0.0,1.5],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.5,0.0],"axis_b":[0.0,1.5],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.5,0.0],"axis_b":[0.0,1.5],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.8]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.266103672779499,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.266103672779499,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.266103672779499,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.266103672779499,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5707963267948963,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5707963267948963,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5707963267948963,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5707963267948963,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0180812136981137,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0180812136981137,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.2661036727794994,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.2661036727794994,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6751315329370315,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6751315329370315,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6751315329370315,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6751315329370315,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.0180812136981134,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.0180812136981134,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0180812136981134,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0180812136981134,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.46676533904729633,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6751315329370317,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.551115123125783e-17,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.551115123125783e-17,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.551115123125783e-17,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.551115123125783e-17,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.46676533904729633,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.46676533904729633,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.46676533904729633,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.46676533904729633,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6435011087932843,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6435011087932843,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6435011087932843,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6435011087932843,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.7475843496690207,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6435011087932843,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.048481504988848,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.048481504988848,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.048481504988848,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.048481504988848,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.7475843496690207,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.7475843496690207,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.7475843496690207,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.7475843496690207,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.1592794807274087,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.1592794807274087,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.048481504988848,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.048481504988848,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.5707963267948966,0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.5707963267948966,0.8]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.5707963267948966,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.5707963267948966,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.1592794807274087,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.1592794807274087,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.1592794807274087,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.1592794807274087,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.4612494995995995,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4612494995995995,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4612494995995995,0.30000000000000004]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.23019609728144297,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.2301960972814432,0.30000000000000004]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.2301960972814432,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5860571099491754,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.5860571099491754,0.30000000000000004]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.5860571099491754,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.440892098500626e-16,0.30000000000000004]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.440892098500626e-16,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5331056943471393,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.5331056943471393,1.2]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.2]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.30166852264521204,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.30166852264521204,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.2]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.220446049250313e-16,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.40220445305492003,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.40220445305492025,1.2]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.2]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.0180812136981137,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.2661036727794994,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.46676533904729633,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6751315329370317,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.7475843496690207,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.6435011087932843,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.1592794807274087,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.048481504988848,2.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.8],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[9.184850993605148e-17,1.5,0.0],"x":[-1.8369701987210297e-16,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,1.8369701987210297e-16,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.0},{"frame":{"origin":[2.0,0.0,0.0],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.5},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.5},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.5},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.5},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.5},{"frame":{"origin":[1.5612494995995996,1.25,0.5],"x":[-1.0,0.0,0.0],"y":[0.0,0.0,1.0],"z":[0.0,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.1,1.25,0.5],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.1999999999999997,0.9,0.5],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.6,1.9078784028338913,0.8],"x":[0.0,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.748331477354788,1.3,0.8],"x":[1.0,0.0,0.0],"y":[-0.0,0.0,1.0],"z":[0.0,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.05,1.3,0.8],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.5},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.5}]},"id":"batch-vertical-arc.arc-host","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":1.1592794807274087}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":134},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.1592794807274085,"lo":1.0484815049888478}},"halfedge":1,"id":1,"tolerance":1e-9,"twin_halfedge":128},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.048481504988848,"lo":0.7475843496690207}},"halfedge":2,"id":2,"tolerance":1e-9,"twin_halfedge":118},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.7475843496690207,"lo":0.6435011087932843}},"halfedge":3,"id":3,"tolerance":1e-9,"twin_halfedge":114},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.6435011087932843,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-9,"twin_halfedge":104},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-9,"twin_halfedge":94},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.46676533904729633,"lo":5.551115123125783e-17}},"halfedge":6,"id":6,"tolerance":1e-9,"twin_halfedge":84},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.6751315329370315,"lo":0.4667653390472962}},"halfedge":7,"id":7,"tolerance":1e-9,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.0180812136981134,"lo":0.6751315329370315}},"halfedge":8,"id":8,"tolerance":1e-9,"twin_halfedge":70},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.2661036727794992,"lo":1.0180812136981134}},"halfedge":9,"id":9,"tolerance":1e-9,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":1.2661036727794992}},"halfedge":10,"id":10,"tolerance":1e-9,"twin_halfedge":54},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":11,"id":11,"tolerance":1e-9,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":0.23019609728144297,"lo":0.0}},"halfedge":12,"id":12,"tolerance":1e-9,"twin_halfedge":150},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":0.4612494995995995,"lo":0.0}},"halfedge":13,"id":13,"tolerance":1e-9,"twin_halfedge":144},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.6751315329370317,"lo":0.46676533904729633}},"halfedge":14,"id":14,"tolerance":1e-9,"twin_halfedge":82},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.5860571099491754,"lo":0.0}},"halfedge":15,"id":15,"tolerance":1e-9,"twin_halfedge":156},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":0.7475843496690207,"lo":0.6435011087932844}},"halfedge":16,"id":16,"tolerance":1e-9,"twin_halfedge":116},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":1.2661036727794994,"lo":1.0180812136981137}},"halfedge":17,"id":17,"tolerance":1e-9,"twin_halfedge":67},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":0.40220445305492003,"lo":0.0}},"halfedge":18,"id":18,"tolerance":1e-9,"twin_halfedge":170},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.30166852264521204,"lo":0.0}},"halfedge":19,"id":19,"tolerance":1e-9,"twin_halfedge":166},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":1.1592794807274085,"lo":1.048481504988848}},"halfedge":20,"id":20,"tolerance":1e-9,"twin_halfedge":131},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":0.5331056943471393,"lo":0.0}},"halfedge":21,"id":21,"tolerance":1e-9,"twin_halfedge":162},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":0.5331056943471393,"lo":0.0}},"halfedge":22,"id":22,"tolerance":1e-9,"twin_halfedge":164},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":1.1592794807274085,"lo":1.048481504988848}},"halfedge":23,"id":23,"tolerance":1e-9,"twin_halfedge":186},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":0.30166852264521204,"lo":0.0}},"halfedge":24,"id":24,"tolerance":1e-9,"twin_halfedge":168},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":0.40220445305492003,"lo":0.0}},"halfedge":25,"id":25,"tolerance":1e-9,"twin_halfedge":172},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":1.2661036727794992,"lo":1.0180812136981134}},"halfedge":26,"id":26,"tolerance":1e-9,"twin_halfedge":174},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":0.7475843496690205,"lo":0.6435011087932843}},"halfedge":27,"id":27,"tolerance":1e-9,"twin_halfedge":182},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":0.5860571099491754,"lo":0.0}},"halfedge":28,"id":28,"tolerance":1e-9,"twin_halfedge":159},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.6751315329370315,"lo":0.4667653390472962}},"halfedge":29,"id":29,"tolerance":1e-9,"twin_halfedge":178},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":0.4612494995995995,"lo":0.0}},"halfedge":30,"id":30,"tolerance":1e-9,"twin_halfedge":147},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":0.23019609728144297,"lo":0.0}},"halfedge":31,"id":31,"tolerance":1e-9,"twin_halfedge":153},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":32,"id":32,"tolerance":1e-9,"twin_halfedge":49},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":1.5707963267948963,"lo":1.266103672779499}},"halfedge":33,"id":33,"tolerance":1e-9,"twin_halfedge":59},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":1.2661036727794994,"lo":1.0180812136981137}},"halfedge":34,"id":34,"tolerance":1e-9,"twin_halfedge":176},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":1.0180812136981134,"lo":0.6751315329370315}},"halfedge":35,"id":35,"tolerance":1e-9,"twin_halfedge":75},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":0.6751315329370317,"lo":0.46676533904729633}},"halfedge":36,"id":36,"tolerance":1e-9,"twin_halfedge":180},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":0.46676533904729633,"lo":5.551115123125783e-17}},"halfedge":37,"id":37,"tolerance":1e-9,"twin_halfedge":89},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":38,"id":38,"tolerance":1e-9,"twin_halfedge":99},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":0.6435011087932843,"lo":0.0}},"halfedge":39,"id":39,"tolerance":1e-9,"twin_halfedge":109},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":0.7475843496690207,"lo":0.6435011087932843}},"halfedge":40,"id":40,"tolerance":1e-9,"twin_halfedge":184},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":1.048481504988848,"lo":0.7475843496690207}},"halfedge":41,"id":41,"tolerance":1e-9,"twin_halfedge":123},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":1.1592794807274087,"lo":1.048481504988848}},"halfedge":42,"id":42,"tolerance":1e-9,"twin_halfedge":188},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":1.1592794807274087}},"halfedge":43,"id":43,"tolerance":1e-9,"twin_halfedge":139},{"chart_seam":false,"geometry":{"curve":44,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":45,"id":44,"tolerance":1e-9,"twin_halfedge":63},{"chart_seam":false,"geometry":{"curve":45,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":46,"id":45,"tolerance":1e-9,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":46,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":47,"id":46,"tolerance":1e-9,"twin_halfedge":61},{"chart_seam":false,"geometry":{"curve":47,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":48,"id":47,"tolerance":1e-9,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":48,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":50,"id":48,"tolerance":1e-9,"twin_halfedge":138},{"chart_seam":false,"geometry":{"curve":49,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":51,"id":49,"tolerance":1e-9,"twin_halfedge":137},{"chart_seam":false,"geometry":{"curve":50,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":52,"id":50,"tolerance":1e-9,"twin_halfedge":136},{"chart_seam":false,"geometry":{"curve":51,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":53,"id":51,"tolerance":1e-9,"twin_halfedge":135},{"chart_seam":false,"geometry":{"curve":52,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":55,"id":52,"tolerance":1e-9,"twin_halfedge":69},{"chart_seam":false,"geometry":{"curve":53,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":56,"id":53,"tolerance":1e-9,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":54,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":57,"id":54,"tolerance":1e-9,"twin_halfedge":165},{"chart_seam":false,"geometry":{"curve":55,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":58,"id":55,"tolerance":1e-9,"twin_halfedge":177},{"chart_seam":false,"geometry":{"curve":56,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":65,"id":56,"tolerance":1e-9,"twin_halfedge":79},{"chart_seam":false,"geometry":{"curve":57,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":66,"id":57,"tolerance":1e-9,"twin_halfedge":78},{"chart_seam":false,"geometry":{"curve":58,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":71,"id":58,"tolerance":1e-9,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":59,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":72,"id":59,"tolerance":1e-9,"twin_halfedge":149},{"chart_seam":false,"geometry":{"curve":60,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":73,"id":60,"tolerance":1e-9,"twin_halfedge":148},{"chart_seam":false,"geometry":{"curve":61,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":74,"id":61,"tolerance":1e-9,"twin_halfedge":181},{"chart_seam":false,"geometry":{"curve":62,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":76,"id":62,"tolerance":1e-9,"twin_halfedge":175},{"chart_seam":false,"geometry":{"curve":63,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":77,"id":63,"tolerance":1e-9,"twin_halfedge":171},{"chart_seam":false,"geometry":{"curve":64,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":81,"id":64,"tolerance":1e-9,"twin_halfedge":93},{"chart_seam":false,"geometry":{"curve":65,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":85,"id":65,"tolerance":1e-9,"twin_halfedge":103},{"chart_seam":false,"geometry":{"curve":66,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":86,"id":66,"tolerance":1e-9,"twin_halfedge":102},{"chart_seam":false,"geometry":{"curve":67,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":87,"id":67,"tolerance":1e-9,"twin_halfedge":101},{"chart_seam":false,"geometry":{"curve":68,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":88,"id":68,"tolerance":1e-9,"twin_halfedge":100},{"chart_seam":false,"geometry":{"curve":69,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":90,"id":69,"tolerance":1e-9,"twin_halfedge":179},{"chart_seam":false,"geometry":{"curve":70,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":91,"id":70,"tolerance":1e-9,"twin_halfedge":158},{"chart_seam":false,"geometry":{"curve":71,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":92,"id":71,"tolerance":1e-9,"twin_halfedge":157},{"chart_seam":false,"geometry":{"curve":72,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":95,"id":72,"tolerance":1e-9,"twin_halfedge":113},{"chart_seam":false,"geometry":{"curve":73,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":96,"id":73,"tolerance":1e-9,"twin_halfedge":112},{"chart_seam":false,"geometry":{"curve":74,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":97,"id":74,"tolerance":1e-9,"twin_halfedge":111},{"chart_seam":false,"geometry":{"curve":75,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":98,"id":75,"tolerance":1e-9,"twin_halfedge":110},{"chart_seam":false,"geometry":{"curve":76,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":105,"id":76,"tolerance":1e-9,"twin_halfedge":117},{"chart_seam":false,"geometry":{"curve":77,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":106,"id":77,"tolerance":1e-9,"twin_halfedge":161},{"chart_seam":false,"geometry":{"curve":78,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":107,"id":78,"tolerance":1e-9,"twin_halfedge":160},{"chart_seam":false,"geometry":{"curve":79,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":108,"id":79,"tolerance":1e-9,"twin_halfedge":185},{"chart_seam":false,"geometry":{"curve":80,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":115,"id":80,"tolerance":1e-9,"twin_halfedge":127},{"chart_seam":false,"geometry":{"curve":81,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":119,"id":81,"tolerance":1e-9,"twin_halfedge":133},{"chart_seam":false,"geometry":{"curve":82,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":120,"id":82,"tolerance":1e-9,"twin_halfedge":132},{"chart_seam":false,"geometry":{"curve":83,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":121,"id":83,"tolerance":1e-9,"twin_halfedge":169},{"chart_seam":false,"geometry":{"curve":84,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":122,"id":84,"tolerance":1e-9,"twin_halfedge":189},{"chart_seam":false,"geometry":{"curve":85,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":124,"id":85,"tolerance":1e-9,"twin_halfedge":183},{"chart_seam":false,"geometry":{"curve":86,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":125,"id":86,"tolerance":1e-9,"twin_halfedge":152},{"chart_seam":false,"geometry":{"curve":87,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":126,"id":87,"tolerance":1e-9,"twin_halfedge":151},{"chart_seam":false,"geometry":{"curve":88,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":129,"id":88,"tolerance":1e-9,"twin_halfedge":143},{"chart_seam":false,"geometry":{"curve":89,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":130,"id":89,"tolerance":1e-9,"twin_halfedge":142},{"chart_seam":false,"geometry":{"curve":90,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":140,"id":90,"tolerance":1e-9,"twin_halfedge":187},{"chart_seam":false,"geometry":{"curve":91,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":141,"id":91,"tolerance":1e-9,"twin_halfedge":163},{"chart_seam":false,"geometry":{"curve":92,"kind":"Curve","range":{"hi":0.30000000000000004,"lo":0.0}},"halfedge":145,"id":92,"tolerance":1e-9,"twin_halfedge":155},{"chart_seam":false,"geometry":{"curve":93,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":146,"id":93,"tolerance":1e-9,"twin_halfedge":154},{"chart_seam":false,"geometry":{"curve":94,"kind":"Curve","range":{"hi":1.2,"lo":0.0}},"halfedge":167,"id":94,"tolerance":1e-9,"twin_halfedge":173}],"faces":[{"id":0,"key":"lower-cap-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.000000001,"lo":-1e-9},{"hi":1e-9,"lo":-2.000000001}]}},{"id":1,"key":"upper-cap-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening","entity":"opening","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.7860571109491754,"lo":1.099999999},{"hi":1.250000001,"lo":0.8999999989999998}]}},{"id":2,"key":"upper-cap-2","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"second-opening","entity":"second-opening","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0500000010000001,"lo":0.5999999989999999},{"hi":1.9078784038338914,"lo":1.299999999}]}},{"id":3,"key":"lower-cap-3","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"second-opening","entity":"second-opening","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.0500000010000001,"lo":0.5999999989999999},{"hi":-1.299999999,"lo":-1.9078784038338914}]}},{"id":4,"key":"lower-cap-4","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening","entity":"opening","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.7860571109491754,"lo":1.099999999},{"hi":-0.8999999989999998,"lo":-1.250000001}]}},{"id":5,"key":"upper-cap-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.000000001,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}},{"id":6,"key":"vertical-plane-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":7,"key":"vertical-cylinder-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":1.5707963272948964,"lo":1.266103672279499},{"hi":3.000000001,"lo":-1e-9}]}},{"id":8,"key":"vertical-cylinder-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":1.2661036732794995,"lo":1.0180812131981136},{"hi":0.800000001,"lo":-1e-9}]}},{"id":9,"key":"vertical-cylinder-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":1.0180812141981135,"lo":0.6751315324370315},{"hi":3.000000001,"lo":-1e-9}]}},{"id":10,"key":"vertical-cylinder-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":0.6751315334370317,"lo":0.46676533854729635},{"hi":0.500000001,"lo":-1e-9}]}},{"id":11,"key":"vertical-cylinder-11","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":0.4667653395472963,"lo":-4.999999444888488e-10},{"hi":3.000000001,"lo":-1e-9}]}},{"id":12,"key":"vertical-plane-12","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":0.500000001,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":13,"key":"vertical-cylinder-13","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":0.643501109459951,"lo":-6.666666666666667e-10},{"hi":3.000000001,"lo":-1e-9}]}},{"id":14,"key":"vertical-cylinder-14","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":0.7475843503356874,"lo":0.6435011081266175},{"hi":0.500000001,"lo":-1e-9}]}},{"id":15,"key":"vertical-cylinder-15","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":1.0484815056555148,"lo":0.7475843490023539},{"hi":3.000000001,"lo":-1e-9}]}},{"id":16,"key":"vertical-cylinder-16","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[],"outer":16,"uv_bounds":[{"hi":1.1592794813940754,"lo":1.0484815043221813},{"hi":0.800000001,"lo":-1e-9}]}},{"id":17,"key":"vertical-cylinder-17","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":17,"trim":{"chart":0,"holes":[],"outer":17,"uv_bounds":[{"hi":1.5707963274615633,"lo":1.159279480060742},{"hi":3.000000001,"lo":-1e-9}]}},{"id":18,"key":"vertical-plane-18","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening","entity":"opening","face":5,"key":"back"}]},"sense":"Reverse","shell_ref":0,"surface":18,"trim":{"chart":0,"holes":[],"outer":18,"uv_bounds":[{"hi":0.4612495005995995,"lo":-1e-9},{"hi":1.500000001,"lo":-1e-9}]}},{"id":19,"key":"vertical-plane-19","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening","entity":"opening","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":19,"trim":{"chart":0,"holes":[],"outer":19,"uv_bounds":[{"hi":0.2301960982814432,"lo":-1e-9},{"hi":1.500000001,"lo":-1e-9}]}},{"id":20,"key":"vertical-plane-20","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"opening","entity":"opening","face":4,"key":"front"}]},"sense":"Reverse","shell_ref":0,"surface":20,"trim":{"chart":0,"holes":[],"outer":20,"uv_bounds":[{"hi":0.5860571109491753,"lo":-1e-9},{"hi":1.500000001,"lo":-1e-9}]}},{"id":21,"key":"vertical-plane-21","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"second-opening","entity":"second-opening","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":21,"trim":{"chart":0,"holes":[],"outer":21,"uv_bounds":[{"hi":0.5331056953471393,"lo":-1e-9},{"hi":1.200000001,"lo":-1e-9}]}},{"id":22,"key":"vertical-plane-22","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"second-opening","entity":"second-opening","face":4,"key":"front"}]},"sense":"Reverse","shell_ref":0,"surface":22,"trim":{"chart":0,"holes":[],"outer":22,"uv_bounds":[{"hi":0.30166852364521207,"lo":-1e-9},{"hi":1.200000001,"lo":-1e-9}]}},{"id":23,"key":"vertical-plane-23","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"second-opening","entity":"second-opening","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":23,"trim":{"chart":0,"holes":[],"outer":23,"uv_bounds":[{"hi":0.40220445405492006,"lo":-1e-9},{"hi":1.200000001,"lo":-1e-9}]}},{"id":24,"key":"vertical-cylinder-24","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":24,"trim":{"chart":0,"holes":[],"outer":24,"uv_bounds":[{"hi":1.2661036732794995,"lo":1.0180812131981136},{"hi":3.000000001,"lo":1.999999999}]}},{"id":25,"key":"vertical-cylinder-25","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":25,"trim":{"chart":0,"holes":[],"outer":25,"uv_bounds":[{"hi":0.6751315334370317,"lo":0.46676533854729635},{"hi":3.000000001,"lo":1.999999999}]}},{"id":26,"key":"vertical-cylinder-26","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":26,"trim":{"chart":0,"holes":[],"outer":26,"uv_bounds":[{"hi":0.7475843503356874,"lo":0.6435011081266175},{"hi":3.000000001,"lo":1.999999999}]}},{"id":27,"key":"vertical-cylinder-27","provenance":{"reversed":false,"role":"Split","sources":[{"body":"arc-host","entity":"arc-host","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":27,"trim":{"chart":0,"holes":[],"outer":27,"uv_bounds":[{"hi":1.1592794813940754,"lo":1.0484815043221813},{"hi":3.000000001,"lo":1.999999999}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Reverse"},"id":0,"loop_ref":0,"next":1,"prev":11,"to":1,"twin":134,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":128,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":118,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":4,"prev":2,"to":4,"twin":114,"wire_ref":null},{"edge":4,"face":0,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":0,"next":5,"prev":3,"to":5,"twin":104,"wire_ref":null},{"edge":5,"face":0,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":0,"next":6,"prev":4,"to":6,"twin":94,"wire_ref":null},{"edge":6,"face":0,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":0,"next":7,"prev":5,"to":7,"twin":84,"wire_ref":null},{"edge":7,"face":0,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":0,"next":8,"prev":6,"to":8,"twin":80,"wire_ref":null},{"edge":8,"face":0,"from":8,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":0,"next":9,"prev":7,"to":9,"twin":70,"wire_ref":null},{"edge":9,"face":0,"from":9,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":0,"next":10,"prev":8,"to":10,"twin":64,"wire_ref":null},{"edge":10,"face":0,"from":10,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":0,"next":11,"prev":9,"to":11,"twin":54,"wire_ref":null},{"edge":11,"face":0,"from":11,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":0,"next":0,"prev":10,"to":0,"twin":44,"wire_ref":null},{"edge":12,"face":1,"from":12,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":1,"next":13,"prev":16,"to":13,"twin":150,"wire_ref":null},{"edge":13,"face":1,"from":13,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":1,"next":14,"prev":12,"to":14,"twin":144,"wire_ref":null},{"edge":14,"face":1,"from":14,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":1,"next":15,"prev":13,"to":15,"twin":82,"wire_ref":null},{"edge":15,"face":1,"from":15,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":1,"next":16,"prev":14,"to":16,"twin":156,"wire_ref":null},{"edge":16,"face":1,"from":16,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":1,"next":12,"prev":15,"to":12,"twin":116,"wire_ref":null},{"edge":17,"face":2,"from":17,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":2,"next":18,"prev":21,"to":18,"twin":67,"wire_ref":null},{"edge":18,"face":2,"from":18,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":2,"next":19,"prev":17,"to":19,"twin":170,"wire_ref":null},{"edge":19,"face":2,"from":19,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":2,"next":20,"prev":18,"to":20,"twin":166,"wire_ref":null},{"edge":20,"face":2,"from":20,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":2,"next":21,"prev":19,"to":21,"twin":131,"wire_ref":null},{"edge":21,"face":2,"from":21,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":2,"next":17,"prev":20,"to":17,"twin":162,"wire_ref":null},{"edge":22,"face":3,"from":22,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Forward"},"id":22,"loop_ref":3,"next":23,"prev":26,"to":23,"twin":164,"wire_ref":null},{"edge":23,"face":3,"from":23,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":3,"next":24,"prev":22,"to":24,"twin":186,"wire_ref":null},{"edge":24,"face":3,"from":24,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":3,"next":25,"prev":23,"to":25,"twin":168,"wire_ref":null},{"edge":25,"face":3,"from":25,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":3,"next":26,"prev":24,"to":26,"twin":172,"wire_ref":null},{"edge":26,"face":3,"from":26,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Forward"},"id":26,"loop_ref":3,"next":22,"prev":25,"to":22,"twin":174,"wire_ref":null},{"edge":27,"face":4,"from":27,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":4,"next":28,"prev":31,"to":28,"twin":182,"wire_ref":null},{"edge":28,"face":4,"from":28,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":4,"next":29,"prev":27,"to":29,"twin":159,"wire_ref":null},{"edge":29,"face":4,"from":29,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":4,"next":30,"prev":28,"to":30,"twin":178,"wire_ref":null},{"edge":30,"face":4,"from":30,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Forward"},"id":30,"loop_ref":4,"next":31,"prev":29,"to":31,"twin":147,"wire_ref":null},{"edge":31,"face":4,"from":31,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Forward"},"id":31,"loop_ref":4,"next":27,"prev":30,"to":27,"twin":153,"wire_ref":null},{"edge":32,"face":5,"from":32,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":5,"next":33,"prev":43,"to":33,"twin":49,"wire_ref":null},{"edge":33,"face":5,"from":33,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":5,"next":34,"prev":32,"to":34,"twin":59,"wire_ref":null},{"edge":34,"face":5,"from":34,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":5,"next":35,"prev":33,"to":35,"twin":176,"wire_ref":null},{"edge":35,"face":5,"from":35,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":5,"next":36,"prev":34,"to":36,"twin":75,"wire_ref":null},{"edge":36,"face":5,"from":36,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Reverse"},"id":36,"loop_ref":5,"next":37,"prev":35,"to":37,"twin":180,"wire_ref":null},{"edge":37,"face":5,"from":37,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":5,"next":38,"prev":36,"to":38,"twin":89,"wire_ref":null},{"edge":38,"face":5,"from":38,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Forward"},"id":38,"loop_ref":5,"next":39,"prev":37,"to":39,"twin":99,"wire_ref":null},{"edge":39,"face":5,"from":39,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":5,"next":40,"prev":38,"to":40,"twin":109,"wire_ref":null},{"edge":40,"face":5,"from":40,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":5,"next":41,"prev":39,"to":41,"twin":184,"wire_ref":null},{"edge":41,"face":5,"from":41,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":5,"next":42,"prev":40,"to":42,"twin":123,"wire_ref":null},{"edge":42,"face":5,"from":42,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Forward"},"id":42,"loop_ref":5,"next":43,"prev":41,"to":43,"twin":188,"wire_ref":null},{"edge":43,"face":5,"from":43,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Forward"},"id":43,"loop_ref":5,"next":32,"prev":42,"to":32,"twin":139,"wire_ref":null},{"edge":11,"face":6,"from":0,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Reverse"},"id":44,"loop_ref":6,"next":45,"prev":53,"to":11,"twin":11,"wire_ref":null},{"edge":44,"face":6,"from":11,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":6,"next":46,"prev":44,"to":44,"twin":63,"wire_ref":null},{"edge":45,"face":6,"from":44,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Forward"},"id":46,"loop_ref":6,"next":47,"prev":45,"to":45,"twin":62,"wire_ref":null},{"edge":46,"face":6,"from":45,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Forward"},"id":47,"loop_ref":6,"next":48,"prev":46,"to":46,"twin":61,"wire_ref":null},{"edge":47,"face":6,"from":46,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":6,"next":49,"prev":47,"to":33,"twin":60,"wire_ref":null},{"edge":32,"face":6,"from":33,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Reverse"},"id":49,"loop_ref":6,"next":50,"prev":48,"to":32,"twin":32,"wire_ref":null},{"edge":48,"face":6,"from":32,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Forward"},"id":50,"loop_ref":6,"next":51,"prev":49,"to":47,"twin":138,"wire_ref":null},{"edge":49,"face":6,"from":47,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Forward"},"id":51,"loop_ref":6,"next":52,"prev":50,"to":48,"twin":137,"wire_ref":null},{"edge":50,"face":6,"from":48,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":6,"next":53,"prev":51,"to":49,"twin":136,"wire_ref":null},{"edge":51,"face":6,"from":49,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":6,"next":44,"prev":52,"to":0,"twin":135,"wire_ref":null},{"edge":10,"face":7,"from":11,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":7,"next":55,"prev":63,"to":10,"twin":10,"wire_ref":null},{"edge":52,"face":7,"from":10,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Forward"},"id":55,"loop_ref":7,"next":56,"prev":54,"to":50,"twin":69,"wire_ref":null},{"edge":53,"face":7,"from":50,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":7,"next":57,"prev":55,"to":17,"twin":68,"wire_ref":null},{"edge":54,"face":7,"from":17,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":7,"next":58,"prev":56,"to":22,"twin":165,"wire_ref":null},{"edge":55,"face":7,"from":22,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Forward"},"id":58,"loop_ref":7,"next":59,"prev":57,"to":34,"twin":177,"wire_ref":null},{"edge":33,"face":7,"from":34,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Forward"},"id":59,"loop_ref":7,"next":60,"prev":58,"to":33,"twin":33,"wire_ref":null},{"edge":47,"face":7,"from":33,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Reverse"},"id":60,"loop_ref":7,"next":61,"prev":59,"to":46,"twin":48,"wire_ref":null},{"edge":46,"face":7,"from":46,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Reverse"},"id":61,"loop_ref":7,"next":62,"prev":60,"to":45,"twin":47,"wire_ref":null},{"edge":45,"face":7,"from":45,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":7,"next":63,"prev":61,"to":44,"twin":46,"wire_ref":null},{"edge":44,"face":7,"from":44,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Reverse"},"id":63,"loop_ref":7,"next":54,"prev":62,"to":11,"twin":45,"wire_ref":null},{"edge":9,"face":8,"from":10,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Reverse"},"id":64,"loop_ref":8,"next":65,"prev":69,"to":9,"twin":9,"wire_ref":null},{"edge":56,"face":8,"from":9,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Forward"},"id":65,"loop_ref":8,"next":66,"prev":64,"to":51,"twin":79,"wire_ref":null},{"edge":57,"face":8,"from":51,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Forward"},"id":66,"loop_ref":8,"next":67,"prev":65,"to":18,"twin":78,"wire_ref":null},{"edge":17,"face":8,"from":18,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Forward"},"id":67,"loop_ref":8,"next":68,"prev":66,"to":17,"twin":17,"wire_ref":null},{"edge":53,"face":8,"from":17,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Reverse"},"id":68,"loop_ref":8,"next":69,"prev":67,"to":50,"twin":56,"wire_ref":null},{"edge":52,"face":8,"from":50,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Reverse"},"id":69,"loop_ref":8,"next":64,"prev":68,"to":10,"twin":55,"wire_ref":null},{"edge":8,"face":9,"from":9,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":9,"next":71,"prev":79,"to":8,"twin":8,"wire_ref":null},{"edge":58,"face":9,"from":8,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Forward"},"id":71,"loop_ref":9,"next":72,"prev":70,"to":14,"twin":83,"wire_ref":null},{"edge":59,"face":9,"from":14,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Forward"},"id":72,"loop_ref":9,"next":73,"prev":71,"to":52,"twin":149,"wire_ref":null},{"edge":60,"face":9,"from":52,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Forward"},"id":73,"loop_ref":9,"next":74,"prev":72,"to":30,"twin":148,"wire_ref":null},{"edge":61,"face":9,"from":30,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Forward"},"id":74,"loop_ref":9,"next":75,"prev":73,"to":36,"twin":181,"wire_ref":null},{"edge":35,"face":9,"from":36,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Forward"},"id":75,"loop_ref":9,"next":76,"prev":74,"to":35,"twin":35,"wire_ref":null},{"edge":62,"face":9,"from":35,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Forward"},"id":76,"loop_ref":9,"next":77,"prev":75,"to":26,"twin":175,"wire_ref":null},{"edge":63,"face":9,"from":26,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Forward"},"id":77,"loop_ref":9,"next":78,"prev":76,"to":18,"twin":171,"wire_ref":null},{"edge":57,"face":9,"from":18,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":9,"next":79,"prev":77,"to":51,"twin":66,"wire_ref":null},{"edge":56,"face":9,"from":51,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Reverse"},"id":79,"loop_ref":9,"next":70,"prev":78,"to":9,"twin":65,"wire_ref":null},{"edge":7,"face":10,"from":8,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Reverse"},"id":80,"loop_ref":10,"next":81,"prev":83,"to":7,"twin":7,"wire_ref":null},{"edge":64,"face":10,"from":7,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Forward"},"id":81,"loop_ref":10,"next":82,"prev":80,"to":15,"twin":93,"wire_ref":null},{"edge":14,"face":10,"from":15,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Forward"},"id":82,"loop_ref":10,"next":83,"prev":81,"to":14,"twin":14,"wire_ref":null},{"edge":58,"face":10,"from":14,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":10,"next":80,"prev":82,"to":8,"twin":71,"wire_ref":null},{"edge":6,"face":11,"from":7,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Reverse"},"id":84,"loop_ref":11,"next":85,"prev":93,"to":6,"twin":6,"wire_ref":null},{"edge":65,"face":11,"from":6,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Forward"},"id":85,"loop_ref":11,"next":86,"prev":84,"to":53,"twin":103,"wire_ref":null},{"edge":66,"face":11,"from":53,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Forward"},"id":86,"loop_ref":11,"next":87,"prev":85,"to":54,"twin":102,"wire_ref":null},{"edge":67,"face":11,"from":54,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Forward"},"id":87,"loop_ref":11,"next":88,"prev":86,"to":55,"twin":101,"wire_ref":null},{"edge":68,"face":11,"from":55,"geometry_use":{"pcurve":88,"periodic_lift":[0,0],"sense":"Forward"},"id":88,"loop_ref":11,"next":89,"prev":87,"to":38,"twin":100,"wire_ref":null},{"edge":37,"face":11,"from":38,"geometry_use":{"pcurve":89,"periodic_lift":[0,0],"sense":"Forward"},"id":89,"loop_ref":11,"next":90,"prev":88,"to":37,"twin":37,"wire_ref":null},{"edge":69,"face":11,"from":37,"geometry_use":{"pcurve":90,"periodic_lift":[0,0],"sense":"Forward"},"id":90,"loop_ref":11,"next":91,"prev":89,"to":29,"twin":179,"wire_ref":null},{"edge":70,"face":11,"from":29,"geometry_use":{"pcurve":91,"periodic_lift":[0,0],"sense":"Forward"},"id":91,"loop_ref":11,"next":92,"prev":90,"to":56,"twin":158,"wire_ref":null},{"edge":71,"face":11,"from":56,"geometry_use":{"pcurve":92,"periodic_lift":[0,0],"sense":"Forward"},"id":92,"loop_ref":11,"next":93,"prev":91,"to":15,"twin":157,"wire_ref":null},{"edge":64,"face":11,"from":15,"geometry_use":{"pcurve":93,"periodic_lift":[0,0],"sense":"Reverse"},"id":93,"loop_ref":11,"next":84,"prev":92,"to":7,"twin":81,"wire_ref":null},{"edge":5,"face":12,"from":6,"geometry_use":{"pcurve":94,"periodic_lift":[0,0],"sense":"Reverse"},"id":94,"loop_ref":12,"next":95,"prev":103,"to":5,"twin":5,"wire_ref":null},{"edge":72,"face":12,"from":5,"geometry_use":{"pcurve":95,"periodic_lift":[0,0],"sense":"Forward"},"id":95,"loop_ref":12,"next":96,"prev":94,"to":57,"twin":113,"wire_ref":null},{"edge":73,"face":12,"from":57,"geometry_use":{"pcurve":96,"periodic_lift":[0,0],"sense":"Forward"},"id":96,"loop_ref":12,"next":97,"prev":95,"to":58,"twin":112,"wire_ref":null},{"edge":74,"face":12,"from":58,"geometry_use":{"pcurve":97,"periodic_lift":[0,0],"sense":"Forward"},"id":97,"loop_ref":12,"next":98,"prev":96,"to":59,"twin":111,"wire_ref":null},{"edge":75,"face":12,"from":59,"geometry_use":{"pcurve":98,"periodic_lift":[0,0],"sense":"Forward"},"id":98,"loop_ref":12,"next":99,"prev":97,"to":39,"twin":110,"wire_ref":null},{"edge":38,"face":12,"from":39,"geometry_use":{"pcurve":99,"periodic_lift":[0,0],"sense":"Reverse"},"id":99,"loop_ref":12,"next":100,"prev":98,"to":38,"twin":38,"wire_ref":null},{"edge":68,"face":12,"from":38,"geometry_use":{"pcurve":100,"periodic_lift":[0,0],"sense":"Reverse"},"id":100,"loop_ref":12,"next":101,"prev":99,"to":55,"twin":88,"wire_ref":null},{"edge":67,"face":12,"from":55,"geometry_use":{"pcurve":101,"periodic_lift":[0,0],"sense":"Reverse"},"id":101,"loop_ref":12,"next":102,"prev":100,"to":54,"twin":87,"wire_ref":null},{"edge":66,"face":12,"from":54,"geometry_use":{"pcurve":102,"periodic_lift":[0,0],"sense":"Reverse"},"id":102,"loop_ref":12,"next":103,"prev":101,"to":53,"twin":86,"wire_ref":null},{"edge":65,"face":12,"from":53,"geometry_use":{"pcurve":103,"periodic_lift":[0,0],"sense":"Reverse"},"id":103,"loop_ref":12,"next":94,"prev":102,"to":6,"twin":85,"wire_ref":null},{"edge":4,"face":13,"from":5,"geometry_use":{"pcurve":104,"periodic_lift":[0,0],"sense":"Forward"},"id":104,"loop_ref":13,"next":105,"prev":113,"to":4,"twin":4,"wire_ref":null},{"edge":76,"face":13,"from":4,"geometry_use":{"pcurve":105,"periodic_lift":[0,0],"sense":"Forward"},"id":105,"loop_ref":13,"next":106,"prev":104,"to":16,"twin":117,"wire_ref":null},{"edge":77,"face":13,"from":16,"geometry_use":{"pcurve":106,"periodic_lift":[0,0],"sense":"Forward"},"id":106,"loop_ref":13,"next":107,"prev":105,"to":60,"twin":161,"wire_ref":null},{"edge":78,"face":13,"from":60,"geometry_use":{"pcurve":107,"periodic_lift":[0,0],"sense":"Forward"},"id":107,"loop_ref":13,"next":108,"prev":106,"to":28,"twin":160,"wire_ref":null},{"edge":79,"face":13,"from":28,"geometry_use":{"pcurve":108,"periodic_lift":[0,0],"sense":"Forward"},"id":108,"loop_ref":13,"next":109,"prev":107,"to":40,"twin":185,"wire_ref":null},{"edge":39,"face":13,"from":40,"geometry_use":{"pcurve":109,"periodic_lift":[0,0],"sense":"Reverse"},"id":109,"loop_ref":13,"next":110,"prev":108,"to":39,"twin":39,"wire_ref":null},{"edge":75,"face":13,"from":39,"geometry_use":{"pcurve":110,"periodic_lift":[0,0],"sense":"Reverse"},"id":110,"loop_ref":13,"next":111,"prev":109,"to":59,"twin":98,"wire_ref":null},{"edge":74,"face":13,"from":59,"geometry_use":{"pcurve":111,"periodic_lift":[0,0],"sense":"Reverse"},"id":111,"loop_ref":13,"next":112,"prev":110,"to":58,"twin":97,"wire_ref":null},{"edge":73,"face":13,"from":58,"geometry_use":{"pcurve":112,"periodic_lift":[0,0],"sense":"Reverse"},"id":112,"loop_ref":13,"next":113,"prev":111,"to":57,"twin":96,"wire_ref":null},{"edge":72,"face":13,"from":57,"geometry_use":{"pcurve":113,"periodic_lift":[0,0],"sense":"Reverse"},"id":113,"loop_ref":13,"next":104,"prev":112,"to":5,"twin":95,"wire_ref":null},{"edge":3,"face":14,"from":4,"geometry_use":{"pcurve":114,"periodic_lift":[0,0],"sense":"Forward"},"id":114,"loop_ref":14,"next":115,"prev":117,"to":3,"twin":3,"wire_ref":null},{"edge":80,"face":14,"from":3,"geometry_use":{"pcurve":115,"periodic_lift":[0,0],"sense":"Forward"},"id":115,"loop_ref":14,"next":116,"prev":114,"to":12,"twin":127,"wire_ref":null},{"edge":16,"face":14,"from":12,"geometry_use":{"pcurve":116,"periodic_lift":[0,0],"sense":"Reverse"},"id":116,"loop_ref":14,"next":117,"prev":115,"to":16,"twin":16,"wire_ref":null},{"edge":76,"face":14,"from":16,"geometry_use":{"pcurve":117,"periodic_lift":[0,0],"sense":"Reverse"},"id":117,"loop_ref":14,"next":114,"prev":116,"to":4,"twin":105,"wire_ref":null},{"edge":2,"face":15,"from":3,"geometry_use":{"pcurve":118,"periodic_lift":[0,0],"sense":"Forward"},"id":118,"loop_ref":15,"next":119,"prev":127,"to":2,"twin":2,"wire_ref":null},{"edge":81,"face":15,"from":2,"geometry_use":{"pcurve":119,"periodic_lift":[0,0],"sense":"Forward"},"id":119,"loop_ref":15,"next":120,"prev":118,"to":61,"twin":133,"wire_ref":null},{"edge":82,"face":15,"from":61,"geometry_use":{"pcurve":120,"periodic_lift":[0,0],"sense":"Forward"},"id":120,"loop_ref":15,"next":121,"prev":119,"to":20,"twin":132,"wire_ref":null},{"edge":83,"face":15,"from":20,"geometry_use":{"pcurve":121,"periodic_lift":[0,0],"sense":"Forward"},"id":121,"loop_ref":15,"next":122,"prev":120,"to":24,"twin":169,"wire_ref":null},{"edge":84,"face":15,"from":24,"geometry_use":{"pcurve":122,"periodic_lift":[0,0],"sense":"Forward"},"id":122,"loop_ref":15,"next":123,"prev":121,"to":42,"twin":189,"wire_ref":null},{"edge":41,"face":15,"from":42,"geometry_use":{"pcurve":123,"periodic_lift":[0,0],"sense":"Reverse"},"id":123,"loop_ref":15,"next":124,"prev":122,"to":41,"twin":41,"wire_ref":null},{"edge":85,"face":15,"from":41,"geometry_use":{"pcurve":124,"periodic_lift":[0,0],"sense":"Forward"},"id":124,"loop_ref":15,"next":125,"prev":123,"to":27,"twin":183,"wire_ref":null},{"edge":86,"face":15,"from":27,"geometry_use":{"pcurve":125,"periodic_lift":[0,0],"sense":"Forward"},"id":125,"loop_ref":15,"next":126,"prev":124,"to":62,"twin":152,"wire_ref":null},{"edge":87,"face":15,"from":62,"geometry_use":{"pcurve":126,"periodic_lift":[0,0],"sense":"Forward"},"id":126,"loop_ref":15,"next":127,"prev":125,"to":12,"twin":151,"wire_ref":null},{"edge":80,"face":15,"from":12,"geometry_use":{"pcurve":127,"periodic_lift":[0,0],"sense":"Reverse"},"id":127,"loop_ref":15,"next":118,"prev":126,"to":3,"twin":115,"wire_ref":null},{"edge":1,"face":16,"from":2,"geometry_use":{"pcurve":128,"periodic_lift":[0,0],"sense":"Forward"},"id":128,"loop_ref":16,"next":129,"prev":133,"to":1,"twin":1,"wire_ref":null},{"edge":88,"face":16,"from":1,"geometry_use":{"pcurve":129,"periodic_lift":[0,0],"sense":"Forward"},"id":129,"loop_ref":16,"next":130,"prev":128,"to":63,"twin":143,"wire_ref":null},{"edge":89,"face":16,"from":63,"geometry_use":{"pcurve":130,"periodic_lift":[0,0],"sense":"Forward"},"id":130,"loop_ref":16,"next":131,"prev":129,"to":21,"twin":142,"wire_ref":null},{"edge":20,"face":16,"from":21,"geometry_use":{"pcurve":131,"periodic_lift":[0,0],"sense":"Reverse"},"id":131,"loop_ref":16,"next":132,"prev":130,"to":20,"twin":20,"wire_ref":null},{"edge":82,"face":16,"from":20,"geometry_use":{"pcurve":132,"periodic_lift":[0,0],"sense":"Reverse"},"id":132,"loop_ref":16,"next":133,"prev":131,"to":61,"twin":120,"wire_ref":null},{"edge":81,"face":16,"from":61,"geometry_use":{"pcurve":133,"periodic_lift":[0,0],"sense":"Reverse"},"id":133,"loop_ref":16,"next":128,"prev":132,"to":2,"twin":119,"wire_ref":null},{"edge":0,"face":17,"from":1,"geometry_use":{"pcurve":134,"periodic_lift":[0,0],"sense":"Forward"},"id":134,"loop_ref":17,"next":135,"prev":143,"to":0,"twin":0,"wire_ref":null},{"edge":51,"face":17,"from":0,"geometry_use":{"pcurve":135,"periodic_lift":[0,0],"sense":"Reverse"},"id":135,"loop_ref":17,"next":136,"prev":134,"to":49,"twin":53,"wire_ref":null},{"edge":50,"face":17,"from":49,"geometry_use":{"pcurve":136,"periodic_lift":[0,0],"sense":"Reverse"},"id":136,"loop_ref":17,"next":137,"prev":135,"to":48,"twin":52,"wire_ref":null},{"edge":49,"face":17,"from":48,"geometry_use":{"pcurve":137,"periodic_lift":[0,0],"sense":"Reverse"},"id":137,"loop_ref":17,"next":138,"prev":136,"to":47,"twin":51,"wire_ref":null},{"edge":48,"face":17,"from":47,"geometry_use":{"pcurve":138,"periodic_lift":[0,0],"sense":"Reverse"},"id":138,"loop_ref":17,"next":139,"prev":137,"to":32,"twin":50,"wire_ref":null},{"edge":43,"face":17,"from":32,"geometry_use":{"pcurve":139,"periodic_lift":[0,0],"sense":"Reverse"},"id":139,"loop_ref":17,"next":140,"prev":138,"to":43,"twin":43,"wire_ref":null},{"edge":90,"face":17,"from":43,"geometry_use":{"pcurve":140,"periodic_lift":[0,0],"sense":"Forward"},"id":140,"loop_ref":17,"next":141,"prev":139,"to":23,"twin":187,"wire_ref":null},{"edge":91,"face":17,"from":23,"geometry_use":{"pcurve":141,"periodic_lift":[0,0],"sense":"Forward"},"id":141,"loop_ref":17,"next":142,"prev":140,"to":21,"twin":163,"wire_ref":null},{"edge":89,"face":17,"from":21,"geometry_use":{"pcurve":142,"periodic_lift":[0,0],"sense":"Reverse"},"id":142,"loop_ref":17,"next":143,"prev":141,"to":63,"twin":130,"wire_ref":null},{"edge":88,"face":17,"from":63,"geometry_use":{"pcurve":143,"periodic_lift":[0,0],"sense":"Reverse"},"id":143,"loop_ref":17,"next":134,"prev":142,"to":1,"twin":129,"wire_ref":null},{"edge":13,"face":18,"from":14,"geometry_use":{"pcurve":144,"periodic_lift":[0,0],"sense":"Reverse"},"id":144,"loop_ref":18,"next":145,"prev":149,"to":13,"twin":13,"wire_ref":null},{"edge":92,"face":18,"from":13,"geometry_use":{"pcurve":145,"periodic_lift":[0,0],"sense":"Forward"},"id":145,"loop_ref":18,"next":146,"prev":144,"to":64,"twin":155,"wire_ref":null},{"edge":93,"face":18,"from":64,"geometry_use":{"pcurve":146,"periodic_lift":[0,0],"sense":"Forward"},"id":146,"loop_ref":18,"next":147,"prev":145,"to":31,"twin":154,"wire_ref":null},{"edge":30,"face":18,"from":31,"geometry_use":{"pcurve":147,"periodic_lift":[0,0],"sense":"Reverse"},"id":147,"loop_ref":18,"next":148,"prev":146,"to":30,"twin":30,"wire_ref":null},{"edge":60,"face":18,"from":30,"geometry_use":{"pcurve":148,"periodic_lift":[0,0],"sense":"Reverse"},"id":148,"loop_ref":18,"next":149,"prev":147,"to":52,"twin":73,"wire_ref":null},{"edge":59,"face":18,"from":52,"geometry_use":{"pcurve":149,"periodic_lift":[0,0],"sense":"Reverse"},"id":149,"loop_ref":18,"next":144,"prev":148,"to":14,"twin":72,"wire_ref":null},{"edge":12,"face":19,"from":13,"geometry_use":{"pcurve":150,"periodic_lift":[0,0],"sense":"Reverse"},"id":150,"loop_ref":19,"next":151,"prev":155,"to":12,"twin":12,"wire_ref":null},{"edge":87,"face":19,"from":12,"geometry_use":{"pcurve":151,"periodic_lift":[0,0],"sense":"Reverse"},"id":151,"loop_ref":19,"next":152,"prev":150,"to":62,"twin":126,"wire_ref":null},{"edge":86,"face":19,"from":62,"geometry_use":{"pcurve":152,"periodic_lift":[0,0],"sense":"Reverse"},"id":152,"loop_ref":19,"next":153,"prev":151,"to":27,"twin":125,"wire_ref":null},{"edge":31,"face":19,"from":27,"geometry_use":{"pcurve":153,"periodic_lift":[0,0],"sense":"Reverse"},"id":153,"loop_ref":19,"next":154,"prev":152,"to":31,"twin":31,"wire_ref":null},{"edge":93,"face":19,"from":31,"geometry_use":{"pcurve":154,"periodic_lift":[0,0],"sense":"Reverse"},"id":154,"loop_ref":19,"next":155,"prev":153,"to":64,"twin":146,"wire_ref":null},{"edge":92,"face":19,"from":64,"geometry_use":{"pcurve":155,"periodic_lift":[0,0],"sense":"Reverse"},"id":155,"loop_ref":19,"next":150,"prev":154,"to":13,"twin":145,"wire_ref":null},{"edge":15,"face":20,"from":16,"geometry_use":{"pcurve":156,"periodic_lift":[0,0],"sense":"Reverse"},"id":156,"loop_ref":20,"next":157,"prev":161,"to":15,"twin":15,"wire_ref":null},{"edge":71,"face":20,"from":15,"geometry_use":{"pcurve":157,"periodic_lift":[0,0],"sense":"Reverse"},"id":157,"loop_ref":20,"next":158,"prev":156,"to":56,"twin":92,"wire_ref":null},{"edge":70,"face":20,"from":56,"geometry_use":{"pcurve":158,"periodic_lift":[0,0],"sense":"Reverse"},"id":158,"loop_ref":20,"next":159,"prev":157,"to":29,"twin":91,"wire_ref":null},{"edge":28,"face":20,"from":29,"geometry_use":{"pcurve":159,"periodic_lift":[0,0],"sense":"Reverse"},"id":159,"loop_ref":20,"next":160,"prev":158,"to":28,"twin":28,"wire_ref":null},{"edge":78,"face":20,"from":28,"geometry_use":{"pcurve":160,"periodic_lift":[0,0],"sense":"Reverse"},"id":160,"loop_ref":20,"next":161,"prev":159,"to":60,"twin":107,"wire_ref":null},{"edge":77,"face":20,"from":60,"geometry_use":{"pcurve":161,"periodic_lift":[0,0],"sense":"Reverse"},"id":161,"loop_ref":20,"next":156,"prev":160,"to":16,"twin":106,"wire_ref":null},{"edge":21,"face":21,"from":17,"geometry_use":{"pcurve":162,"periodic_lift":[0,0],"sense":"Reverse"},"id":162,"loop_ref":21,"next":163,"prev":165,"to":21,"twin":21,"wire_ref":null},{"edge":91,"face":21,"from":21,"geometry_use":{"pcurve":163,"periodic_lift":[0,0],"sense":"Reverse"},"id":163,"loop_ref":21,"next":164,"prev":162,"to":23,"twin":141,"wire_ref":null},{"edge":22,"face":21,"from":23,"geometry_use":{"pcurve":164,"periodic_lift":[0,0],"sense":"Reverse"},"id":164,"loop_ref":21,"next":165,"prev":163,"to":22,"twin":22,"wire_ref":null},{"edge":54,"face":21,"from":22,"geometry_use":{"pcurve":165,"periodic_lift":[0,0],"sense":"Reverse"},"id":165,"loop_ref":21,"next":162,"prev":164,"to":17,"twin":57,"wire_ref":null},{"edge":19,"face":22,"from":20,"geometry_use":{"pcurve":166,"periodic_lift":[0,0],"sense":"Reverse"},"id":166,"loop_ref":22,"next":167,"prev":169,"to":19,"twin":19,"wire_ref":null},{"edge":94,"face":22,"from":19,"geometry_use":{"pcurve":167,"periodic_lift":[0,0],"sense":"Forward"},"id":167,"loop_ref":22,"next":168,"prev":166,"to":25,"twin":173,"wire_ref":null},{"edge":24,"face":22,"from":25,"geometry_use":{"pcurve":168,"periodic_lift":[0,0],"sense":"Reverse"},"id":168,"loop_ref":22,"next":169,"prev":167,"to":24,"twin":24,"wire_ref":null},{"edge":83,"face":22,"from":24,"geometry_use":{"pcurve":169,"periodic_lift":[0,0],"sense":"Reverse"},"id":169,"loop_ref":22,"next":166,"prev":168,"to":20,"twin":121,"wire_ref":null},{"edge":18,"face":23,"from":19,"geometry_use":{"pcurve":170,"periodic_lift":[0,0],"sense":"Reverse"},"id":170,"loop_ref":23,"next":171,"prev":173,"to":18,"twin":18,"wire_ref":null},{"edge":63,"face":23,"from":18,"geometry_use":{"pcurve":171,"periodic_lift":[0,0],"sense":"Reverse"},"id":171,"loop_ref":23,"next":172,"prev":170,"to":26,"twin":77,"wire_ref":null},{"edge":25,"face":23,"from":26,"geometry_use":{"pcurve":172,"periodic_lift":[0,0],"sense":"Reverse"},"id":172,"loop_ref":23,"next":173,"prev":171,"to":25,"twin":25,"wire_ref":null},{"edge":94,"face":23,"from":25,"geometry_use":{"pcurve":173,"periodic_lift":[0,0],"sense":"Reverse"},"id":173,"loop_ref":23,"next":170,"prev":172,"to":19,"twin":167,"wire_ref":null},{"edge":26,"face":24,"from":22,"geometry_use":{"pcurve":174,"periodic_lift":[0,0],"sense":"Reverse"},"id":174,"loop_ref":24,"next":175,"prev":177,"to":26,"twin":26,"wire_ref":null},{"edge":62,"face":24,"from":26,"geometry_use":{"pcurve":175,"periodic_lift":[0,0],"sense":"Reverse"},"id":175,"loop_ref":24,"next":176,"prev":174,"to":35,"twin":76,"wire_ref":null},{"edge":34,"face":24,"from":35,"geometry_use":{"pcurve":176,"periodic_lift":[0,0],"sense":"Forward"},"id":176,"loop_ref":24,"next":177,"prev":175,"to":34,"twin":34,"wire_ref":null},{"edge":55,"face":24,"from":34,"geometry_use":{"pcurve":177,"periodic_lift":[0,0],"sense":"Reverse"},"id":177,"loop_ref":24,"next":174,"prev":176,"to":22,"twin":58,"wire_ref":null},{"edge":29,"face":25,"from":30,"geometry_use":{"pcurve":178,"periodic_lift":[0,0],"sense":"Reverse"},"id":178,"loop_ref":25,"next":179,"prev":181,"to":29,"twin":29,"wire_ref":null},{"edge":69,"face":25,"from":29,"geometry_use":{"pcurve":179,"periodic_lift":[0,0],"sense":"Reverse"},"id":179,"loop_ref":25,"next":180,"prev":178,"to":37,"twin":90,"wire_ref":null},{"edge":36,"face":25,"from":37,"geometry_use":{"pcurve":180,"periodic_lift":[0,0],"sense":"Forward"},"id":180,"loop_ref":25,"next":181,"prev":179,"to":36,"twin":36,"wire_ref":null},{"edge":61,"face":25,"from":36,"geometry_use":{"pcurve":181,"periodic_lift":[0,0],"sense":"Reverse"},"id":181,"loop_ref":25,"next":178,"prev":180,"to":30,"twin":74,"wire_ref":null},{"edge":27,"face":26,"from":28,"geometry_use":{"pcurve":182,"periodic_lift":[0,0],"sense":"Forward"},"id":182,"loop_ref":26,"next":183,"prev":185,"to":27,"twin":27,"wire_ref":null},{"edge":85,"face":26,"from":27,"geometry_use":{"pcurve":183,"periodic_lift":[0,0],"sense":"Reverse"},"id":183,"loop_ref":26,"next":184,"prev":182,"to":41,"twin":124,"wire_ref":null},{"edge":40,"face":26,"from":41,"geometry_use":{"pcurve":184,"periodic_lift":[0,0],"sense":"Reverse"},"id":184,"loop_ref":26,"next":185,"prev":183,"to":40,"twin":40,"wire_ref":null},{"edge":79,"face":26,"from":40,"geometry_use":{"pcurve":185,"periodic_lift":[0,0],"sense":"Reverse"},"id":185,"loop_ref":26,"next":182,"prev":184,"to":28,"twin":108,"wire_ref":null},{"edge":23,"face":27,"from":24,"geometry_use":{"pcurve":186,"periodic_lift":[0,0],"sense":"Forward"},"id":186,"loop_ref":27,"next":187,"prev":189,"to":23,"twin":23,"wire_ref":null},{"edge":90,"face":27,"from":23,"geometry_use":{"pcurve":187,"periodic_lift":[0,0],"sense":"Reverse"},"id":187,"loop_ref":27,"next":188,"prev":186,"to":43,"twin":140,"wire_ref":null},{"edge":42,"face":27,"from":43,"geometry_use":{"pcurve":188,"periodic_lift":[0,0],"sense":"Reverse"},"id":188,"loop_ref":27,"next":189,"prev":187,"to":42,"twin":42,"wire_ref":null},{"edge":84,"face":27,"from":42,"geometry_use":{"pcurve":189,"periodic_lift":[0,0],"sense":"Reverse"},"id":189,"loop_ref":27,"next":186,"prev":188,"to":24,"twin":122,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":12},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":17},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":22},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":27},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":32},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":44},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":54},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":64},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":70},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":80},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":84},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":94},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":104},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":114},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":118},{"face_ref":16,"id":16,"is_hole":false,"start_halfedge":128},{"face_ref":17,"id":17,"is_hole":false,"start_halfedge":134},{"face_ref":18,"id":18,"is_hole":false,"start_halfedge":144},{"face_ref":19,"id":19,"is_hole":false,"start_halfedge":150},{"face_ref":20,"id":20,"is_hole":false,"start_halfedge":156},{"face_ref":21,"id":21,"is_hole":false,"start_halfedge":162},{"face_ref":22,"id":22,"is_hole":false,"start_halfedge":166},{"face_ref":23,"id":23,"is_hole":false,"start_halfedge":170},{"face_ref":24,"id":24,"is_hole":false,"start_halfedge":174},{"face_ref":25,"id":25,"is_hole":false,"start_halfedge":178},{"face_ref":26,"id":26,"is_hole":false,"start_halfedge":182},{"face_ref":27,"id":27,"is_hole":false,"start_halfedge":186}],"shells":[{"faces":[0,17,16,15,14,13,12,11,10,9,8,7,6,5,27,21,2,22,26,19,1,20,25,18,24,23,3,4],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[9.184850993605148e-17,1.5,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":1,"position":[0.5999999999999999,1.374772708486752,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":2,"position":[0.7483314773547882,1.3,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":3,"position":[1.1,1.0198039027185568,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":4,"position":[1.2000000000000002,0.8999999999999998,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":5,"position":[1.5,0.0,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":6,"position":[2.0,0.0,0.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":7,"position":[1.786057109949175,0.8999999999999999,0.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":8,"position":[1.5612494995995996,1.2499999999999998,0.0],"tolerance":1e-9},{"id":9,"outgoing_halfedge":9,"position":[1.05,1.7022044530549203,0.0],"tolerance":1e-9},{"id":10,"outgoing_halfedge":10,"position":[0.5999999999999999,1.9078784028338913,0.0],"tolerance":1e-9},{"id":11,"outgoing_halfedge":11,"position":[1.2246467991473532e-16,2.0,0.0],"tolerance":1e-9},{"id":12,"outgoing_halfedge":12,"position":[1.1,1.019803902718557,0.5],"tolerance":1e-9},{"id":13,"outgoing_halfedge":13,"position":[1.1,1.25,0.5],"tolerance":1e-9},{"id":14,"outgoing_halfedge":14,"position":[1.5612494995995996,1.25,0.5],"tolerance":1e-9},{"id":15,"outgoing_halfedge":15,"position":[1.7860571099491753,0.8999999999999998,0.5],"tolerance":1e-9},{"id":16,"outgoing_halfedge":16,"position":[1.1999999999999997,0.9,0.5],"tolerance":1e-9},{"id":17,"outgoing_halfedge":17,"position":[0.5999999999999999,1.9078784028338913,0.8],"tolerance":1e-9},{"id":18,"outgoing_halfedge":18,"position":[1.05,1.7022044530549203,0.8],"tolerance":1e-9},{"id":19,"outgoing_halfedge":19,"position":[1.05,1.3,0.8],"tolerance":1e-9},{"id":20,"outgoing_halfedge":20,"position":[0.748331477354788,1.3,0.8],"tolerance":1e-9},{"id":21,"outgoing_halfedge":21,"position":[0.6000000000000001,1.374772708486752,0.8],"tolerance":1e-9},{"id":22,"outgoing_halfedge":22,"position":[0.6,1.9078784028338913,2.0],"tolerance":1e-9},{"id":23,"outgoing_halfedge":23,"position":[0.6,1.374772708486752,2.0],"tolerance":1e-9},{"id":24,"outgoing_halfedge":24,"position":[0.7483314773547882,1.3,2.0],"tolerance":1e-9},{"id":25,"outgoing_halfedge":25,"position":[1.05,1.3,2.0],"tolerance":1e-9},{"id":26,"outgoing_halfedge":26,"position":[1.05,1.70220445305492,2.0],"tolerance":1e-9},{"id":27,"outgoing_halfedge":27,"position":[1.1,1.0198039027185568,2.0],"tolerance":1e-9},{"id":28,"outgoing_halfedge":28,"position":[1.2000000000000002,0.8999999999999999,2.0],"tolerance":1e-9},{"id":29,"outgoing_halfedge":29,"position":[1.786057109949175,0.9,2.0],"tolerance":1e-9},{"id":30,"outgoing_halfedge":30,"position":[1.5612494995995996,1.2499999999999998,2.0],"tolerance":1e-9},{"id":31,"outgoing_halfedge":31,"position":[1.1,1.25,2.0],"tolerance":1e-9},{"id":32,"outgoing_halfedge":32,"position":[9.184850993605148e-17,1.5,3.0],"tolerance":1e-9},{"id":33,"outgoing_halfedge":33,"position":[0.0,2.0,3.0],"tolerance":1e-9},{"id":34,"outgoing_halfedge":34,"position":[0.5999999999999999,1.9078784028338913,3.0],"tolerance":1e-9},{"id":35,"outgoing_halfedge":35,"position":[1.05,1.7022044530549203,3.0],"tolerance":1e-9},{"id":36,"outgoing_halfedge":36,"position":[1.5612494995995996,1.2499999999999998,3.0],"tolerance":1e-9},{"id":37,"outgoing_halfedge":37,"position":[1.7860571099491753,0.8999999999999998,3.0],"tolerance":1e-9},{"id":38,"outgoing_halfedge":38,"position":[2.0,1.1102230246251565e-16,3.0],"tolerance":1e-9},{"id":39,"outgoing_halfedge":39,"position":[1.5,0.0,3.0],"tolerance":1e-9},{"id":40,"outgoing_halfedge":40,"position":[1.2000000000000002,0.8999999999999998,3.0],"tolerance":1e-9},{"id":41,"outgoing_halfedge":41,"position":[1.1,1.0198039027185568,3.0],"tolerance":1e-9},{"id":42,"outgoing_halfedge":42,"position":[0.7483314773547882,1.3,3.0],"tolerance":1e-9},{"id":43,"outgoing_halfedge":43,"position":[0.5999999999999999,1.374772708486752,3.0],"tolerance":1e-9},{"id":44,"outgoing_halfedge":46,"position":[0.0,2.0,0.5],"tolerance":1e-9},{"id":45,"outgoing_halfedge":47,"position":[0.0,2.0,0.8],"tolerance":1e-9},{"id":46,"outgoing_halfedge":48,"position":[0.0,2.0,2.0],"tolerance":1e-9},{"id":47,"outgoing_halfedge":51,"position":[9.184850993605148e-17,1.5,2.0],"tolerance":1e-9},{"id":48,"outgoing_halfedge":52,"position":[9.184850993605148e-17,1.5,0.8],"tolerance":1e-9},{"id":49,"outgoing_halfedge":53,"position":[9.184850993605148e-17,1.5,0.5],"tolerance":1e-9},{"id":50,"outgoing_halfedge":56,"position":[0.5999999999999999,1.9078784028338913,0.5],"tolerance":1e-9},{"id":51,"outgoing_halfedge":66,"position":[1.05,1.7022044530549203,0.5],"tolerance":1e-9},{"id":52,"outgoing_halfedge":73,"position":[1.5612494995995996,1.2499999999999998,0.8],"tolerance":1e-9},{"id":53,"outgoing_halfedge":86,"position":[2.0,1.1102230246251565e-16,0.5],"tolerance":1e-9},{"id":54,"outgoing_halfedge":87,"position":[2.0,1.1102230246251565e-16,0.8],"tolerance":1e-9},{"id":55,"outgoing_halfedge":88,"position":[2.0,1.1102230246251565e-16,2.0],"tolerance":1e-9},{"id":56,"outgoing_halfedge":92,"position":[1.786057109949175,0.8999999999999999,0.8],"tolerance":1e-9},{"id":57,"outgoing_halfedge":96,"position":[1.5,0.0,0.5],"tolerance":1e-9},{"id":58,"outgoing_halfedge":97,"position":[1.5,0.0,0.8],"tolerance":1e-9},{"id":59,"outgoing_halfedge":98,"position":[1.5,0.0,2.0],"tolerance":1e-9},{"id":60,"outgoing_halfedge":107,"position":[1.2000000000000002,0.8999999999999998,0.8],"tolerance":1e-9},{"id":61,"outgoing_halfedge":120,"position":[0.7483314773547882,1.3,0.5],"tolerance":1e-9},{"id":62,"outgoing_halfedge":126,"position":[1.1,1.0198039027185568,0.8],"tolerance":1e-9},{"id":63,"outgoing_halfedge":130,"position":[0.5999999999999999,1.374772708486752,0.5],"tolerance":1e-9},{"id":64,"outgoing_halfedge":146,"position":[1.1,1.25,0.8],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/batch-matrix/vertical-arc.wide-opening.json b/main/opengeometry/tests/fixtures/cases/batch-matrix/vertical-arc.wide-opening.json new file mode 100644 index 0000000..208c5dd --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/batch-matrix/vertical-arc.wide-opening.json @@ -0,0 +1 @@ +{"cutters":[{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.5646424733950354,-0.8253356149096783,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.5646424733950354,-0.8253356149096783,0.0],"kind":"Line","origin":[0.0,0.0,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.3778406937858983,0.9258706227758774,0.0],"kind":"Line","origin":[1.5336147919652325,-2.2416785257946072,0.0]},{"direction":[0.3778406937858983,0.9258706227758774,0.0],"kind":"Line","origin":[1.5336147919652325,-2.2416785257946072,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.5336147919652325,-2.2416785257946072,0.0]},{"direction":[-0.9808786552620674,-0.19462030636929495,0.0],"kind":"Line","origin":[2.664146049424873,0.5286045501857572,0.0]},{"direction":[-0.9808786552620674,-0.19462030636929495,0.0],"kind":"Line","origin":[2.664146049424873,0.5286045501857572,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.664146049424873,0.5286045501857572,0.0]}],"intersections":[],"pcurves":[{"direction":[0.5646424733950354,-0.8253356149096783],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.3778406937858983,0.9258706227758774],"kind":"Line2","origin":[1.5336147919652325,-2.2416785257946072]},{"direction":[-0.9808786552620674,-0.19462030636929495],"kind":"Line2","origin":[2.664146049424873,0.5286045501857572]},{"direction":[-0.9808786552620674,0.19462030636929495],"kind":"Line2","origin":[2.664146049424873,-0.5286045501857572]},{"direction":[0.3778406937858983,-0.9258706227758774],"kind":"Line2","origin":[1.5336147919652325,2.2416785257946072]},{"direction":[0.5646424733950354,0.8253356149096783],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.716081173886975,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.992084431488608,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.7160811738869746,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.5646424733950354,-0.8253356149096783,0.0],"y":[0.0,0.0,1.0],"z":[-0.8253356149096783,-0.5646424733950354,0.0]},"kind":"Plane"},{"frame":{"origin":[1.5336147919652325,-2.2416785257946072,0.0],"x":[0.3778406937858983,0.9258706227758774,0.0],"y":[-0.0,0.0,1.0],"z":[0.9258706227758774,-0.3778406937858983,0.0]},"kind":"Plane"},{"frame":{"origin":[2.664146049424873,0.5286045501857572,0.0],"x":[-0.9808786552620675,-0.194620306369295,0.0],"y":[0.0,0.0,1.0],"z":[-0.19462030636929498,0.9808786552620675,0.0]},"kind":"Plane"}]},"id":"sector-0","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":5,"id":0,"tolerance":1e-9,"twin_halfedge":6},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":9,"id":2,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.9920844314886077,"lo":0.0}},"halfedge":4,"id":3,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.9920844314886077,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":5,"tolerance":1e-9,"twin_halfedge":13},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":3,"id":6,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":11,"id":8,"tolerance":1e-9,"twin_halfedge":17}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-0","entity":"sector-0","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.6641460504248733,"lo":-1e-9},{"hi":0.5286045511857572,"lo":-2.2416785267946073}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-0","entity":"sector-0","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.6641460504248733,"lo":-1e-9},{"hi":2.2416785267946073,"lo":-0.5286045511857572}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-0","entity":"sector-0","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.7160811748869746,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-0","entity":"sector-0","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.992084432488608,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-0","entity":"sector-0","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.7160811748869746,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":3,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":2,"to":4,"twin":8,"wire_ref":null},{"edge":4,"face":0,"from":4,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":5,"twin":12,"wire_ref":null},{"edge":7,"face":0,"from":5,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":0,"prev":1,"to":3,"twin":16,"wire_ref":null},{"edge":6,"face":1,"from":0,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":1,"next":4,"prev":5,"to":2,"twin":14,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":3,"to":1,"twin":10,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":3,"prev":4,"to":0,"twin":6,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":2,"next":7,"prev":9,"to":1,"twin":5,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":2,"next":8,"prev":6,"to":4,"twin":13,"wire_ref":null},{"edge":1,"face":2,"from":4,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":9,"prev":7,"to":3,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":3,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":6,"prev":8,"to":0,"twin":15,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":3,"next":11,"prev":13,"to":2,"twin":4,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":3,"next":12,"prev":10,"to":5,"twin":17,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":11,"to":4,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":4,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":10,"prev":12,"to":1,"twin":7,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":4,"next":15,"prev":17,"to":0,"twin":3,"wire_ref":null},{"edge":2,"face":4,"from":0,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":4,"next":16,"prev":14,"to":3,"twin":9,"wire_ref":null},{"edge":7,"face":4,"from":3,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":4,"next":17,"prev":15,"to":5,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":14,"prev":16,"to":2,"twin":11,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":3},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":6},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":10},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":14}],"shells":[{"faces":[0,1,2,3,4],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":3,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":5,"position":[1.5336147919652325,-2.2416785257946072,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":4,"position":[2.664146049424873,0.5286045501857572,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":0,"position":[0.0,0.0,2.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":1,"position":[1.5336147919652325,-2.2416785257946072,2.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":2,"position":[2.664146049424873,0.5286045501857572,2.0],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.9808786552620674,0.19462030636929495,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.9808786552620674,0.19462030636929495,0.0],"kind":"Line","origin":[0.0,0.0,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[-0.7027130767735538,0.7114733527908446,0.0],"kind":"Line","origin":[2.664146049424873,0.5286045501857572,0.0]},{"direction":[-0.7027130767735538,0.7114733527908446,0.0],"kind":"Line","origin":[2.664146049424873,0.5286045501857572,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[2.664146049424873,0.5286045501857572,0.0]},{"direction":[-0.20675714629088357,-0.9783922947660872,0.0],"kind":"Line","origin":[0.561569192607264,2.657392892490245,0.0]},{"direction":[-0.20675714629088357,-0.9783922947660872,0.0],"kind":"Line","origin":[0.561569192607264,2.657392892490245,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.561569192607264,2.657392892490245,0.0]}],"intersections":[],"pcurves":[{"direction":[0.9808786552620674,0.19462030636929495],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-0.7027130767735538,0.7114733527908446],"kind":"Line2","origin":[2.664146049424873,0.5286045501857572]},{"direction":[-0.20675714629088357,-0.9783922947660872],"kind":"Line2","origin":[0.561569192607264,2.657392892490245]},{"direction":[-0.20675714629088357,0.9783922947660872],"kind":"Line2","origin":[0.561569192607264,-2.657392892490245]},{"direction":[-0.7027130767735538,-0.7114733527908446],"kind":"Line2","origin":[2.664146049424873,-0.5286045501857572]},{"direction":[0.9808786552620674,-0.19462030636929495],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.7160811738869746,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.992084431488608,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.716081173886974,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.9808786552620675,0.194620306369295,0.0],"y":[-0.0,0.0,1.0],"z":[0.19462030636929498,-0.9808786552620675,0.0]},"kind":"Plane"},{"frame":{"origin":[2.664146049424873,0.5286045501857572,0.0],"x":[-0.7027130767735538,0.7114733527908446,0.0],"y":[0.0,0.0,1.0],"z":[0.7114733527908446,0.7027130767735538,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.561569192607264,2.657392892490245,0.0],"x":[-0.20675714629088357,-0.9783922947660872,0.0],"y":[0.0,0.0,1.0],"z":[-0.9783922947660872,0.20675714629088357,0.0]},"kind":"Plane"}]},"id":"sector-1","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":5,"id":0,"tolerance":1e-9,"twin_halfedge":6},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":9,"id":2,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.992084431488608,"lo":0.0}},"halfedge":4,"id":3,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.992084431488608,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":5,"tolerance":1e-9,"twin_halfedge":13},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":3,"id":6,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":11,"id":8,"tolerance":1e-9,"twin_halfedge":17}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-1","entity":"sector-1","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.6641460504248733,"lo":-1e-9},{"hi":2.657392893490245,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-1","entity":"sector-1","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.6641460504248733,"lo":-1e-9},{"hi":1e-9,"lo":-2.657392893490245}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-1","entity":"sector-1","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.7160811748869746,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-1","entity":"sector-1","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.9920844324886082,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-1","entity":"sector-1","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.7160811748869746,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":3,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":2,"to":4,"twin":8,"wire_ref":null},{"edge":4,"face":0,"from":4,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":5,"twin":12,"wire_ref":null},{"edge":7,"face":0,"from":5,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":0,"prev":1,"to":3,"twin":16,"wire_ref":null},{"edge":6,"face":1,"from":0,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":1,"next":4,"prev":5,"to":2,"twin":14,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":3,"to":1,"twin":10,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":3,"prev":4,"to":0,"twin":6,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":2,"next":7,"prev":9,"to":1,"twin":5,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":2,"next":8,"prev":6,"to":4,"twin":13,"wire_ref":null},{"edge":1,"face":2,"from":4,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":9,"prev":7,"to":3,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":3,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":6,"prev":8,"to":0,"twin":15,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":3,"next":11,"prev":13,"to":2,"twin":4,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":3,"next":12,"prev":10,"to":5,"twin":17,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":11,"to":4,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":4,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":10,"prev":12,"to":1,"twin":7,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":4,"next":15,"prev":17,"to":0,"twin":3,"wire_ref":null},{"edge":2,"face":4,"from":0,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":4,"next":16,"prev":14,"to":3,"twin":9,"wire_ref":null},{"edge":7,"face":4,"from":3,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":4,"next":17,"prev":15,"to":5,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":14,"prev":16,"to":2,"twin":11,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":3},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":6},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":10},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":14}],"shells":[{"faces":[0,1,2,3,4],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":3,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":5,"position":[2.664146049424873,0.5286045501857572,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":4,"position":[0.561569192607264,2.657392892490245,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":0,"position":[0.0,0.0,2.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":1,"position":[2.664146049424873,0.5286045501857572,2.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":2,"position":[0.561569192607264,2.657392892490245,2.0],"tolerance":1e-9}],"wires":[]}},{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.20675714629088357,0.9783922947660872,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.20675714629088357,0.9783922947660872,0.0],"kind":"Line","origin":[0.0,0.0,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[-0.9304805104068569,-0.3663413978149277,0.0],"kind":"Line","origin":[0.561569192607264,2.657392892490245,0.0]},{"direction":[-0.9304805104068569,-0.3663413978149277,0.0],"kind":"Line","origin":[0.561569192607264,2.657392892490245,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.561569192607264,2.657392892490245,0.0]},{"direction":[0.8182771110644104,-0.5748239465332692,0.0],"kind":"Line","origin":[-2.222507056384666,1.5612684994784252,0.0]},{"direction":[0.8182771110644104,-0.5748239465332692,0.0],"kind":"Line","origin":[-2.222507056384666,1.5612684994784252,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.222507056384666,1.5612684994784252,0.0]}],"intersections":[],"pcurves":[{"direction":[0.20675714629088357,0.9783922947660872],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-0.9304805104068569,-0.3663413978149277],"kind":"Line2","origin":[0.561569192607264,2.657392892490245]},{"direction":[0.8182771110644104,-0.5748239465332692],"kind":"Line2","origin":[-2.222507056384666,1.5612684994784252]},{"direction":[0.8182771110644104,0.5748239465332692],"kind":"Line2","origin":[-2.222507056384666,-1.5612684994784252]},{"direction":[-0.9304805104068569,0.3663413978149277],"kind":"Line2","origin":[0.561569192607264,-2.657392892490245]},{"direction":[0.20675714629088357,-0.9783922947660872],"kind":"Line2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.716081173886974,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.992084431488608,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.716081173886975,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.20675714629088357,0.9783922947660872,0.0],"y":[-0.0,0.0,1.0],"z":[0.9783922947660872,-0.20675714629088357,0.0]},"kind":"Plane"},{"frame":{"origin":[0.561569192607264,2.657392892490245,0.0],"x":[-0.930480510406857,-0.36634139781492775,0.0],"y":[0.0,0.0,1.0],"z":[-0.36634139781492775,0.930480510406857,0.0]},"kind":"Plane"},{"frame":{"origin":[-2.222507056384666,1.5612684994784252,0.0],"x":[0.8182771110644104,-0.5748239465332692,0.0],"y":[0.0,0.0,1.0],"z":[-0.5748239465332692,-0.8182771110644104,0.0]},"kind":"Plane"}]},"id":"sector-2","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":5,"id":0,"tolerance":1e-9,"twin_halfedge":6},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":9,"id":2,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.9920844314886077,"lo":0.0}},"halfedge":4,"id":3,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.9920844314886077,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":5,"tolerance":1e-9,"twin_halfedge":13},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":3,"id":6,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.7160811738869746,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":11,"id":8,"tolerance":1e-9,"twin_halfedge":17}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-2","entity":"sector-2","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.561569193607264,"lo":-2.222507057384666},{"hi":2.657392893490245,"lo":-1e-9}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-2","entity":"sector-2","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.561569193607264,"lo":-2.222507057384666},{"hi":1e-9,"lo":-2.657392893490245}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-2","entity":"sector-2","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.7160811748869746,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-2","entity":"sector-2","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.992084432488608,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sector-2","entity":"sector-2","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.7160811748869746,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":3,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":2,"to":4,"twin":8,"wire_ref":null},{"edge":4,"face":0,"from":4,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":5,"twin":12,"wire_ref":null},{"edge":7,"face":0,"from":5,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":0,"prev":1,"to":3,"twin":16,"wire_ref":null},{"edge":6,"face":1,"from":0,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":1,"next":4,"prev":5,"to":2,"twin":14,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":3,"to":1,"twin":10,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":3,"prev":4,"to":0,"twin":6,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":2,"next":7,"prev":9,"to":1,"twin":5,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":2,"next":8,"prev":6,"to":4,"twin":13,"wire_ref":null},{"edge":1,"face":2,"from":4,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":9,"prev":7,"to":3,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":3,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":6,"prev":8,"to":0,"twin":15,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":3,"next":11,"prev":13,"to":2,"twin":4,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":3,"next":12,"prev":10,"to":5,"twin":17,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":11,"to":4,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":4,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":10,"prev":12,"to":1,"twin":7,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":4,"next":15,"prev":17,"to":0,"twin":3,"wire_ref":null},{"edge":2,"face":4,"from":0,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":4,"next":16,"prev":14,"to":3,"twin":9,"wire_ref":null},{"edge":7,"face":4,"from":3,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":4,"next":17,"prev":15,"to":5,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":14,"prev":16,"to":2,"twin":11,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":3},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":6},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":10},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":14}],"shells":[{"faces":[0,1,2,3,4],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":3,"position":[0.0,0.0,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":5,"position":[0.561569192607264,2.657392892490245,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":4,"position":[-2.222507056384666,1.5612684994784252,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":0,"position":[0.0,0.0,2.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":1,"position":[0.561569192607264,2.657392892490245,2.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":2,"position":[-2.222507056384666,1.5612684994784252,2.0],"tolerance":1e-9}],"wires":[]}}],"handlers":["subtract_planar_cutters","subtract_vertical_arc_extrusion_batch"],"host":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.1},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.1},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.285879139104721e-16,-2.1,0.0]},{"direction":[1.0,-1.2246467991473544e-16,0.0],"kind":"Line","origin":[-2.1,2.571758278209442e-16,0.0]},{"direction":[1.0,-1.2246467991473544e-16,0.0],"kind":"Line","origin":[-2.1,2.571758278209442e-16,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.1,2.571758278209442e-16,0.0]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.9},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.9},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-1.9,2.326828918379971e-16,0.0]},{"direction":[6.123233995736772e-17,-1.0,0.0],"kind":"Line","origin":[1.1634144591899854e-16,-1.9,0.0]},{"direction":[6.123233995736772e-17,-1.0,0.0],"kind":"Line","origin":[1.1634144591899854e-16,-1.9,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1634144591899854e-16,-1.9,0.0]}],"intersections":[],"pcurves":[{"axis_a":[2.1,0.0],"axis_b":[0.0,2.1],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[1.0,-1.2246467991473544e-16],"kind":"Line2","origin":[-2.1,2.571758278209442e-16]},{"axis_a":[1.9,0.0],"axis_b":[0.0,1.9],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[6.123233995736772e-17,-1.0],"kind":"Line2","origin":[1.1634144591899854e-16,-1.9]},{"direction":[6.123233995736772e-17,1.0],"kind":"Line2","origin":[1.1634144591899854e-16,1.9]},{"axis_a":[1.9,-0.0],"axis_b":[0.0,-1.9],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[1.0,1.2246467991473544e-16],"kind":"Line2","origin":[-2.1,-2.571758278209442e-16]},{"axis_a":[2.1,-0.0],"axis_b":[0.0,-2.1],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-1.5707963267948966,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.20000000000000018,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-1.5707963267948966,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.141592653589793,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.20000000000000018,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.1},{"frame":{"origin":[-2.1,2.571758278209442e-16,0.0],"x":[1.0,-1.2246467991473544e-16,0.0],"y":[0.0,0.0,1.0],"z":[-1.2246467991473544e-16,-1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.9},{"frame":{"origin":[1.1634144591899854e-16,-1.9,0.0],"x":[6.123233995736772e-17,-1.0,0.0],"y":[0.0,0.0,1.0],"z":[-1.0,-6.123233995736772e-17,0.0]},"kind":"Plane"}]},"id":"wide-arc","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":3.141592653589793,"lo":-1.5707963267948966}},"halfedge":7,"id":0,"tolerance":1e-9,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.141592653589793,"lo":-1.5707963267948966}},"halfedge":0,"id":1,"tolerance":1e-9,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-9,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.20000000000000018,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-9,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.20000000000000018,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-9,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-9,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.141592653589793,"lo":-1.5707963267948966}},"halfedge":5,"id":6,"tolerance":1e-9,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.141592653589793,"lo":-1.5707963267948966}},"halfedge":2,"id":7,"tolerance":1e-9,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-9,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.20000000000000018,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.20000000000000018,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-9,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-9,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wide-arc","entity":"wide-arc","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.100000001,"lo":-2.100000001},{"hi":2.100000001,"lo":-2.100000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wide-arc","entity":"wide-arc","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.100000001,"lo":-2.100000001},{"hi":2.100000001,"lo":-2.100000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wide-arc","entity":"wide-arc","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":3.1415926540659838,"lo":-1.570796327271087},{"hi":3.000000001,"lo":-1e-9}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wide-arc","entity":"wide-arc","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.20000000100000018,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wide-arc","entity":"wide-arc","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":3.141592654116109,"lo":-1.5707963273212124},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"wide-arc","entity":"wide-arc","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.20000000100000018,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[1.285879139104721e-16,-2.1,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":7,"position":[-2.1,2.571758278209442e-16,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":6,"position":[-1.9,2.326828918379971e-16,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":5,"position":[1.1634144591899854e-16,-1.9,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":0,"position":[1.285879139104721e-16,-2.1,3.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":1,"position":[-2.1,2.571758278209442e-16,3.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":2,"position":[-1.9,2.326828918379971e-16,3.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":3,"position":[1.1634144591899854e-16,-1.9,3.0],"tolerance":1e-9}],"wires":[]}},"result":{"brep":{"accuracy":{"exchange":0.00001,"geometric":1e-9,"intersection":1e-10,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,-1.2246467991473544e-16,0.0],"kind":"Line","origin":[-2.1,2.571758278209442e-16,0.0]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.9},{"direction":[-0.8182771110644103,0.5748239465332692,0.0],"kind":"Line","origin":[-1.5547265110223796,1.0921654984132112,0.0]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.1},{"direction":[6.123233995736766e-17,-1.0,0.0],"kind":"Line","origin":[1.1634144591899857e-16,-1.9000000000000001,0.0]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.1},{"direction":[-0.5646424733950348,0.8253356149096787,0.0],"kind":"Line","origin":[1.1857491941295741,-1.7332047913103246,0.0]},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.9},{"direction":[0.8182771110644099,-0.5748239465332697,0.0],"kind":"Line","origin":[-1.7183819332352617,1.207130287719865,2.0]},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.9},{"direction":[0.5646424733950345,-0.8253356149096789,0.0],"kind":"Line","origin":[1.0728206994505671,-1.5681376683283887,2.0]},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.1},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.1},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.1},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":2.1},{"direction":[-6.123233995736766e-17,1.0,0.0],"kind":"Line","origin":[1.285879139104721e-16,-2.1,3.0]},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.9},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.9},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":1.9},{"direction":[-1.0,1.2246467991473544e-16,0.0],"kind":"Line","origin":[-1.9,2.326828918379971e-16,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-1.718381933235262,1.2071302877198653,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-1.718381933235262,1.2071302877198653,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-2.1,2.571758278209442e-16,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-2.1,2.571758278209442e-16,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-1.5547265110223796,1.0921654984132112,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-1.9,2.326828918379971e-16,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-1.9,2.326828918379971e-16,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-1.5547265110223796,1.0921654984132114,3.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0728206994505671,-1.5681376683283887,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.0728206994505671,-1.5681376683283887,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.1634144591899854e-16,-1.9,3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.1634144591899854e-16,-1.9,2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.1857491941295741,-1.7332047913103246,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.285879139104721e-16,-2.1,0.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[1.285879139104721e-16,-2.1,2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.185749194129574,-1.7332047913103248,3.0]}],"intersections":[],"pcurves":[{"direction":[1.0,1.2246467991473544e-16],"kind":"Line2","origin":[-2.1,-2.571758278209442e-16]},{"axis_a":[1.9,-0.0],"axis_b":[0.0,-1.9],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[-0.8182771110644103,-0.5748239465332692],"kind":"Line2","origin":[-1.5547265110223796,-1.0921654984132112]},{"axis_a":[2.1,-0.0],"axis_b":[0.0,-2.1],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[6.123233995736766e-17,1.0],"kind":"Line2","origin":[1.1634144591899857e-16,1.9000000000000001]},{"axis_a":[2.1,-0.0],"axis_b":[0.0,-2.1],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[-0.5646424733950348,-0.8253356149096787],"kind":"Line2","origin":[1.1857491941295741,1.7332047913103246]},{"axis_a":[1.9,-0.0],"axis_b":[0.0,-1.9],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[0.8182771110644099,0.5748239465332697],"kind":"Line2","origin":[-1.7183819332352617,-1.207130287719865]},{"axis_a":[1.9,-0.0],"axis_b":[0.0,-1.9],"kind":"Conic2","origin":[0.0,-0.0]},{"direction":[0.5646424733950345,0.8253356149096789],"kind":"Line2","origin":[1.0728206994505671,1.5681376683283887]},{"axis_a":[2.1,-0.0],"axis_b":[0.0,-2.1],"kind":"Conic2","origin":[0.0,-0.0]},{"axis_a":[2.1,0.0],"axis_b":[0.0,2.1],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.1,0.0],"axis_b":[0.0,2.1],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[2.1,0.0],"axis_b":[0.0,2.1],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-6.123233995736766e-17,1.0],"kind":"Line2","origin":[1.285879139104721e-16,-2.1]},{"axis_a":[1.9,0.0],"axis_b":[0.0,1.9],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.9,0.0],"axis_b":[0.0,1.9],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.9,0.0],"axis_b":[0.0,1.9],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[-1.0,1.2246467991473544e-16],"kind":"Line2","origin":[-1.9,2.326828918379971e-16]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.529203673205103,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.529203673205103,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.141592653589793,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.141592653589793,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.20000000000000007,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.20000000000000007,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-3.0933057395492006e-16,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.141592653589793,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.141592653589793,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.529203673205103,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.529203673205103,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.20000000000000018,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.20000000000000018,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.20000000000000018,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.312388980384689,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.312388980384689,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.71238898038469,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.71238898038469,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.2000000000000001,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.2000000000000001,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.71238898038469,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.71238898038469,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.312388980384689,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.312388980384689,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.19999999999999996,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.20000000000000018,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.20000000000000018,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[5.312388980384689,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[8.81238898038469,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[8.81238898038469,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[5.312388980384689,2.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[-0.0,-1.0,-0.0],"z":[-0.0,-0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,3.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.1},{"frame":{"origin":[-1.7183819332352617,1.207130287719865,0.0],"x":[0.8182771110644104,-0.5748239465332693,0.0],"y":[0.0,0.0,1.0],"z":[-0.5748239465332693,-0.8182771110644104,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.9},{"frame":{"origin":[-1.9,2.326828918379971e-16,0.0],"x":[-1.0,1.2246467991473544e-16,0.0],"y":[0.0,0.0,1.0],"z":[1.2246467991473544e-16,1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.9},{"frame":{"origin":[1.0728206994505671,-1.5681376683283887,0.0],"x":[0.5646424733950348,-0.8253356149096787,0.0],"y":[0.0,0.0,1.0],"z":[-0.8253356149096787,-0.5646424733950348,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.1},{"frame":{"origin":[1.285879139104721e-16,-2.1,0.0],"x":[-6.123233995736766e-17,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,6.123233995736766e-17,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":2.1},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Cylinder","radius":1.9}]},"id":"batch-vertical-arc.wide-opening","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.20000000000000018,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-9,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.141592653589793,"lo":2.529203673205103}},"halfedge":1,"id":1,"tolerance":1e-9,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.20000000000000007,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-9,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.141592653589793,"lo":2.529203673205103}},"halfedge":3,"id":3,"tolerance":1e-9,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.19999999999999996,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-9,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":5.312388980384689,"lo":4.71238898038469}},"halfedge":5,"id":5,"tolerance":1e-9,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.2000000000000001,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-9,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":5.312388980384689,"lo":4.71238898038469}},"halfedge":7,"id":7,"tolerance":1e-9,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.19999999999999987,"lo":0.0}},"halfedge":8,"id":8,"tolerance":1e-9,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":8.81238898038469,"lo":5.31238898038469}},"halfedge":9,"id":9,"tolerance":1e-9,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.1999999999999998,"lo":0.0}},"halfedge":10,"id":10,"tolerance":1e-9,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":8.81238898038469,"lo":5.312388980384689}},"halfedge":11,"id":11,"tolerance":1e-9,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":3.141592653589793,"lo":2.529203673205103}},"halfedge":12,"id":12,"tolerance":1e-9,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":8.81238898038469,"lo":5.312388980384689}},"halfedge":13,"id":13,"tolerance":1e-9,"twin_halfedge":66},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":5.312388980384689,"lo":4.71238898038469}},"halfedge":14,"id":14,"tolerance":1e-9,"twin_halfedge":55},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.19999999999999996,"lo":0.0}},"halfedge":15,"id":15,"tolerance":1e-9,"twin_halfedge":61},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":5.312388980384689,"lo":4.71238898038469}},"halfedge":16,"id":16,"tolerance":1e-9,"twin_halfedge":45},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":8.81238898038469,"lo":5.312388980384689}},"halfedge":17,"id":17,"tolerance":1e-9,"twin_halfedge":70},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":3.141592653589793,"lo":2.529203673205103}},"halfedge":18,"id":18,"tolerance":1e-9,"twin_halfedge":33},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.20000000000000018,"lo":0.0}},"halfedge":19,"id":19,"tolerance":1e-9,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":21,"id":20,"tolerance":1e-9,"twin_halfedge":29},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":22,"id":21,"tolerance":1e-9,"twin_halfedge":67},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":24,"id":22,"tolerance":1e-9,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":25,"id":23,"tolerance":1e-9,"twin_halfedge":37},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":27,"id":24,"tolerance":1e-9,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":31,"id":25,"tolerance":1e-9,"twin_halfedge":41},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":32,"id":26,"tolerance":1e-9,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":34,"id":27,"tolerance":1e-9,"twin_halfedge":69},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":43,"id":28,"tolerance":1e-9,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":44,"id":29,"tolerance":1e-9,"twin_halfedge":71},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":46,"id":30,"tolerance":1e-9,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":47,"id":31,"tolerance":1e-9,"twin_halfedge":59},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":49,"id":32,"tolerance":1e-9,"twin_halfedge":57},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":53,"id":33,"tolerance":1e-9,"twin_halfedge":63},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":54,"id":34,"tolerance":1e-9,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":56,"id":35,"tolerance":1e-9,"twin_halfedge":65}],"faces":[{"id":0,"key":"lower-cap-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wide-arc","entity":"wide-arc","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":-1.5547265100223795,"lo":-2.100000001},{"hi":9.999997673171082e-10,"lo":-1.2071302887198654}]}},{"id":1,"key":"lower-cap-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wide-arc","entity":"wide-arc","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.1857491951295742,"lo":-9.999998836585542e-10},{"hi":2.100000001,"lo":1.5681376673283887}]}},{"id":2,"key":"lower-cap-2","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"sector-0","entity":"sector-0","face":0,"key":"top"},{"body":"sector-1","entity":"sector-1","face":0,"key":"top"},{"body":"sector-2","entity":"sector-2","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.100000001,"lo":-1.718381934235262},{"hi":1.7332047923103255,"lo":-2.100000001}]}},{"id":3,"key":"upper-cap-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wide-arc","entity":"wide-arc","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.100000001,"lo":-2.100000001},{"hi":2.100000001,"lo":-2.100000001}]}},{"id":4,"key":"vertical-cylinder-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wide-arc","entity":"wide-arc","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":3.1415926540659838,"lo":2.5292036727289124},{"hi":3.000000001,"lo":-1e-9}]}},{"id":5,"key":"vertical-plane-5","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"sector-2","entity":"sector-2","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.20000000100000007,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}},{"id":6,"key":"vertical-cylinder-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wide-arc","entity":"wide-arc","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":3.141592654116109,"lo":2.529203672678787},{"hi":3.000000001,"lo":-1e-9}]}},{"id":7,"key":"vertical-plane-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wide-arc","entity":"wide-arc","face":3,"key":"side-0-1"}]},"sense":"Reverse","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":0.20000000100000018,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":8,"key":"vertical-cylinder-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wide-arc","entity":"wide-arc","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":5.312388980911005,"lo":4.712388979858374},{"hi":3.000000001,"lo":-1e-9}]}},{"id":9,"key":"vertical-plane-9","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"sector-0","entity":"sector-0","face":2,"key":"side-0-0"}]},"sense":"Reverse","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":0.2000000010000001,"lo":-1e-9},{"hi":2.000000001,"lo":-1e-9}]}},{"id":10,"key":"vertical-cylinder-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wide-arc","entity":"wide-arc","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":5.31238898086088,"lo":4.712388979908499},{"hi":3.000000001,"lo":-1e-9}]}},{"id":11,"key":"vertical-plane-11","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wide-arc","entity":"wide-arc","face":5,"key":"side-0-3"}]},"sense":"Reverse","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":0.20000000099999996,"lo":-1e-9},{"hi":3.000000001,"lo":-1e-9}]}},{"id":12,"key":"vertical-cylinder-12","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wide-arc","entity":"wide-arc","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":8.812388980860879,"lo":5.312388979908499},{"hi":3.000000001,"lo":1.999999999}]}},{"id":13,"key":"vertical-cylinder-13","provenance":{"reversed":false,"role":"Split","sources":[{"body":"wide-arc","entity":"wide-arc","face":4,"key":"side-0-2"}]},"sense":"Reverse","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":8.812388980911004,"lo":5.312388979858373},{"hi":3.000000001,"lo":1.999999999}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":36,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":30,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":26,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":20,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":58,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":6,"twin":52,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":7,"twin":48,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":42,"wire_ref":null},{"edge":8,"face":2,"from":8,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":9,"twin":28,"wire_ref":null},{"edge":9,"face":2,"from":9,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":10,"twin":68,"wire_ref":null},{"edge":10,"face":2,"from":10,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":11,"twin":50,"wire_ref":null},{"edge":11,"face":2,"from":11,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":8,"twin":64,"wire_ref":null},{"edge":12,"face":3,"from":12,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":19,"to":13,"twin":23,"wire_ref":null},{"edge":13,"face":3,"from":13,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":14,"twin":66,"wire_ref":null},{"edge":14,"face":3,"from":14,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":15,"twin":55,"wire_ref":null},{"edge":15,"face":3,"from":15,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":3,"next":16,"prev":14,"to":16,"twin":61,"wire_ref":null},{"edge":16,"face":3,"from":16,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":3,"next":17,"prev":15,"to":17,"twin":45,"wire_ref":null},{"edge":17,"face":3,"from":17,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":3,"next":18,"prev":16,"to":18,"twin":70,"wire_ref":null},{"edge":18,"face":3,"from":18,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":3,"next":19,"prev":17,"to":19,"twin":33,"wire_ref":null},{"edge":19,"face":3,"from":19,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":3,"next":12,"prev":18,"to":12,"twin":39,"wire_ref":null},{"edge":3,"face":4,"from":0,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Reverse"},"id":20,"loop_ref":4,"next":21,"prev":25,"to":3,"twin":3,"wire_ref":null},{"edge":20,"face":4,"from":3,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":4,"next":22,"prev":20,"to":8,"twin":29,"wire_ref":null},{"edge":21,"face":4,"from":8,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Forward"},"id":22,"loop_ref":4,"next":23,"prev":21,"to":13,"twin":67,"wire_ref":null},{"edge":12,"face":4,"from":13,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":4,"next":24,"prev":22,"to":12,"twin":12,"wire_ref":null},{"edge":22,"face":4,"from":12,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":4,"next":25,"prev":23,"to":20,"twin":38,"wire_ref":null},{"edge":23,"face":4,"from":20,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":4,"next":20,"prev":24,"to":0,"twin":37,"wire_ref":null},{"edge":2,"face":5,"from":3,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":5,"next":27,"prev":29,"to":2,"twin":2,"wire_ref":null},{"edge":24,"face":5,"from":2,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":5,"next":28,"prev":26,"to":9,"twin":35,"wire_ref":null},{"edge":8,"face":5,"from":9,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Reverse"},"id":28,"loop_ref":5,"next":29,"prev":27,"to":8,"twin":8,"wire_ref":null},{"edge":20,"face":5,"from":8,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Reverse"},"id":29,"loop_ref":5,"next":26,"prev":28,"to":3,"twin":21,"wire_ref":null},{"edge":1,"face":6,"from":2,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Forward"},"id":30,"loop_ref":6,"next":31,"prev":35,"to":1,"twin":1,"wire_ref":null},{"edge":25,"face":6,"from":1,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Forward"},"id":31,"loop_ref":6,"next":32,"prev":30,"to":21,"twin":41,"wire_ref":null},{"edge":26,"face":6,"from":21,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":6,"next":33,"prev":31,"to":19,"twin":40,"wire_ref":null},{"edge":18,"face":6,"from":19,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":6,"next":34,"prev":32,"to":18,"twin":18,"wire_ref":null},{"edge":27,"face":6,"from":18,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Forward"},"id":34,"loop_ref":6,"next":35,"prev":33,"to":9,"twin":69,"wire_ref":null},{"edge":24,"face":6,"from":9,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":6,"next":30,"prev":34,"to":2,"twin":27,"wire_ref":null},{"edge":0,"face":7,"from":1,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Reverse"},"id":36,"loop_ref":7,"next":37,"prev":41,"to":0,"twin":0,"wire_ref":null},{"edge":23,"face":7,"from":0,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":7,"next":38,"prev":36,"to":20,"twin":25,"wire_ref":null},{"edge":22,"face":7,"from":20,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":7,"next":39,"prev":37,"to":12,"twin":24,"wire_ref":null},{"edge":19,"face":7,"from":12,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":7,"next":40,"prev":38,"to":19,"twin":19,"wire_ref":null},{"edge":26,"face":7,"from":19,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Reverse"},"id":40,"loop_ref":7,"next":41,"prev":39,"to":21,"twin":32,"wire_ref":null},{"edge":25,"face":7,"from":21,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Reverse"},"id":41,"loop_ref":7,"next":36,"prev":40,"to":1,"twin":31,"wire_ref":null},{"edge":7,"face":8,"from":4,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Forward"},"id":42,"loop_ref":8,"next":43,"prev":47,"to":7,"twin":7,"wire_ref":null},{"edge":28,"face":8,"from":7,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Forward"},"id":43,"loop_ref":8,"next":44,"prev":42,"to":10,"twin":51,"wire_ref":null},{"edge":29,"face":8,"from":10,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":8,"next":45,"prev":43,"to":17,"twin":71,"wire_ref":null},{"edge":16,"face":8,"from":17,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Reverse"},"id":45,"loop_ref":8,"next":46,"prev":44,"to":16,"twin":16,"wire_ref":null},{"edge":30,"face":8,"from":16,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Forward"},"id":46,"loop_ref":8,"next":47,"prev":45,"to":22,"twin":60,"wire_ref":null},{"edge":31,"face":8,"from":22,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Forward"},"id":47,"loop_ref":8,"next":42,"prev":46,"to":4,"twin":59,"wire_ref":null},{"edge":6,"face":9,"from":7,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Reverse"},"id":48,"loop_ref":9,"next":49,"prev":51,"to":6,"twin":6,"wire_ref":null},{"edge":32,"face":9,"from":6,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":9,"next":50,"prev":48,"to":11,"twin":57,"wire_ref":null},{"edge":10,"face":9,"from":11,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":9,"next":51,"prev":49,"to":10,"twin":10,"wire_ref":null},{"edge":28,"face":9,"from":10,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":9,"next":48,"prev":50,"to":7,"twin":43,"wire_ref":null},{"edge":5,"face":10,"from":6,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Reverse"},"id":52,"loop_ref":10,"next":53,"prev":57,"to":5,"twin":5,"wire_ref":null},{"edge":33,"face":10,"from":5,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":10,"next":54,"prev":52,"to":23,"twin":63,"wire_ref":null},{"edge":34,"face":10,"from":23,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Forward"},"id":54,"loop_ref":10,"next":55,"prev":53,"to":15,"twin":62,"wire_ref":null},{"edge":14,"face":10,"from":15,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Forward"},"id":55,"loop_ref":10,"next":56,"prev":54,"to":14,"twin":14,"wire_ref":null},{"edge":35,"face":10,"from":14,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":10,"next":57,"prev":55,"to":11,"twin":65,"wire_ref":null},{"edge":32,"face":10,"from":11,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Reverse"},"id":57,"loop_ref":10,"next":52,"prev":56,"to":6,"twin":49,"wire_ref":null},{"edge":4,"face":11,"from":5,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":11,"next":59,"prev":63,"to":4,"twin":4,"wire_ref":null},{"edge":31,"face":11,"from":4,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":11,"next":60,"prev":58,"to":22,"twin":47,"wire_ref":null},{"edge":30,"face":11,"from":22,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Reverse"},"id":60,"loop_ref":11,"next":61,"prev":59,"to":16,"twin":46,"wire_ref":null},{"edge":15,"face":11,"from":16,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Reverse"},"id":61,"loop_ref":11,"next":62,"prev":60,"to":15,"twin":15,"wire_ref":null},{"edge":34,"face":11,"from":15,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":11,"next":63,"prev":61,"to":23,"twin":54,"wire_ref":null},{"edge":33,"face":11,"from":23,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Reverse"},"id":63,"loop_ref":11,"next":58,"prev":62,"to":5,"twin":53,"wire_ref":null},{"edge":11,"face":12,"from":8,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Reverse"},"id":64,"loop_ref":12,"next":65,"prev":67,"to":11,"twin":11,"wire_ref":null},{"edge":35,"face":12,"from":11,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Reverse"},"id":65,"loop_ref":12,"next":66,"prev":64,"to":14,"twin":56,"wire_ref":null},{"edge":13,"face":12,"from":14,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Forward"},"id":66,"loop_ref":12,"next":67,"prev":65,"to":13,"twin":13,"wire_ref":null},{"edge":21,"face":12,"from":13,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Reverse"},"id":67,"loop_ref":12,"next":64,"prev":66,"to":8,"twin":22,"wire_ref":null},{"edge":9,"face":13,"from":10,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":13,"next":69,"prev":71,"to":9,"twin":9,"wire_ref":null},{"edge":27,"face":13,"from":9,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Reverse"},"id":69,"loop_ref":13,"next":70,"prev":68,"to":18,"twin":34,"wire_ref":null},{"edge":17,"face":13,"from":18,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":13,"next":71,"prev":69,"to":17,"twin":17,"wire_ref":null},{"edge":29,"face":13,"from":17,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Reverse"},"id":71,"loop_ref":13,"next":68,"prev":70,"to":10,"twin":44,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":20},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":26},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":30},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":36},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":42},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":48},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":52},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":58},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":64},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":68}],"shells":[{"faces":[0,7,6,5,4,3,13,2,12,10,11,8,9,1],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-2.1,2.571758278209442e-16,0.0],"tolerance":1e-9},{"id":1,"outgoing_halfedge":1,"position":[-1.9,2.326828918379971e-16,0.0],"tolerance":1e-9},{"id":2,"outgoing_halfedge":2,"position":[-1.5547265110223796,1.0921654984132114,0.0],"tolerance":1e-9},{"id":3,"outgoing_halfedge":3,"position":[-1.7183819332352617,1.207130287719865,0.0],"tolerance":1e-9},{"id":4,"outgoing_halfedge":4,"position":[1.1634144591899857e-16,-1.9000000000000001,0.0],"tolerance":1e-9},{"id":5,"outgoing_halfedge":5,"position":[1.285879139104721e-16,-2.1,0.0],"tolerance":1e-9},{"id":6,"outgoing_halfedge":6,"position":[1.185749194129574,-1.7332047913103248,0.0],"tolerance":1e-9},{"id":7,"outgoing_halfedge":7,"position":[1.0728206994505671,-1.5681376683283887,0.0],"tolerance":1e-9},{"id":8,"outgoing_halfedge":8,"position":[-1.7183819332352617,1.207130287719865,2.0],"tolerance":1e-9},{"id":9,"outgoing_halfedge":9,"position":[-1.5547265110223798,1.0921654984132112,2.0],"tolerance":1e-9},{"id":10,"outgoing_halfedge":10,"position":[1.0728206994505667,-1.568137668328389,2.0],"tolerance":1e-9},{"id":11,"outgoing_halfedge":11,"position":[1.185749194129574,-1.7332047913103243,2.0],"tolerance":1e-9},{"id":12,"outgoing_halfedge":12,"position":[-2.1,2.571758278209442e-16,3.0],"tolerance":1e-9},{"id":13,"outgoing_halfedge":13,"position":[-1.718381933235262,1.2071302877198653,3.0],"tolerance":1e-9},{"id":14,"outgoing_halfedge":14,"position":[1.185749194129573,-1.7332047913103255,3.0],"tolerance":1e-9},{"id":15,"outgoing_halfedge":15,"position":[1.285879139104721e-16,-2.1,3.0],"tolerance":1e-9},{"id":16,"outgoing_halfedge":16,"position":[1.1634144591899857e-16,-1.9000000000000001,3.0],"tolerance":1e-9},{"id":17,"outgoing_halfedge":17,"position":[1.0728206994505667,-1.568137668328389,3.0],"tolerance":1e-9},{"id":18,"outgoing_halfedge":18,"position":[-1.5547265110223796,1.0921654984132114,3.0],"tolerance":1e-9},{"id":19,"outgoing_halfedge":19,"position":[-1.9,2.326828918379971e-16,3.0],"tolerance":1e-9},{"id":20,"outgoing_halfedge":25,"position":[-2.1,2.571758278209442e-16,2.0],"tolerance":1e-9},{"id":21,"outgoing_halfedge":32,"position":[-1.9,2.326828918379971e-16,2.0],"tolerance":1e-9},{"id":22,"outgoing_halfedge":47,"position":[1.1634144591899854e-16,-1.9,2.0],"tolerance":1e-9},{"id":23,"outgoing_halfedge":54,"position":[1.285879139104721e-16,-2.1,2.0],"tolerance":1e-9}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/annular-first.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/annular-first.json new file mode 100644 index 0000000..7e17a07 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/annular-first.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.3},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.3},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.3,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.3,0.0],"axis_b":[0.0,-0.3],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.3,0.0],"axis_b":[0.0,0.3],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.3},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-annular-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":4,"id":2,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":true,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":3},{"chart_seam":true,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":7}],"faces":[{"id":0,"key":"outer","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-annular-host","entity":"matrix-annular-host","face":0,"key":"outer"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"inner","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-annular-host","entity":"matrix-annular-host","face":1,"key":"inner"}]},"sense":"Reverse","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":2,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-annular-host","entity":"matrix-annular-host","face":2,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[3],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":3,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-annular-host","entity":"matrix-annular-host","face":3,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[5],"outer":4,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":8,"wire_ref":null},{"edge":4,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":2,"face":1,"from":2,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":2,"twin":9,"wire_ref":null},{"edge":5,"face":1,"from":2,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":3,"twin":7,"wire_ref":null},{"edge":3,"face":1,"from":3,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":3,"twin":11,"wire_ref":null},{"edge":5,"face":1,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":2,"twin":5,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":8,"prev":8,"to":0,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":2,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":3,"next":9,"prev":9,"to":2,"twin":4,"wire_ref":null},{"edge":1,"face":3,"from":1,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":4,"next":10,"prev":10,"to":1,"twin":2,"wire_ref":null},{"edge":3,"face":3,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":5,"next":11,"prev":11,"to":3,"twin":6,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":2,"id":3,"is_hole":true,"start_halfedge":9},{"face_ref":3,"id":4,"is_hole":false,"start_halfedge":10},{"face_ref":3,"id":5,"is_hole":true,"start_halfedge":11}],"shells":[{"faces":[0,1,2,3],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,3.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":4,"position":[0.3,0.0,0.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":6,"position":[0.3,3.0,0.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,0.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.4,0.0,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.4,0.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.4,0.0,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.4,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,3.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.4,3.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.4,3.0,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,3.0,-0.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-0.4,0.0,0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,3.0,0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,0.0,0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.4,0.0,0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,0.0,0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,0.0,-0.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-wall-cutter","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":0.8000000100000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.8000000100000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.8000000100000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":0.8000000100000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-0.4,0.0,0.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[0.4,0.0,0.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-0.4,0.0,-0.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[0.4,0.0,-0.5],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-0.4,3.0,0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[0.4,3.0,0.5],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-0.4,3.0,-0.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[0.4,3.0,-0.5],"tolerance":1e-8}],"wires":[]}},"handlers":["subtract_vertical_arc_extrusion"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.4,0.0,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,0.0,-0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.4,0.0,-0.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.4,0.0,0.5]},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,3.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.4,3.0,0.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.4,3.0,-0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-0.4,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-1.0,3.0,-1.2246467991473532e-16]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,0.5]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-0.4,3.0,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.4,0.0,-0.5]}],"intersections":[],"pcurves":[{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.4,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.4,-0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.4,0.5]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.4,-0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.4,0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.4,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.141592653589793,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[3.141592653589793,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[-0.4,0.0,0.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.4,0.0,0.5],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.4,0.0,-0.5],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,0.0,-0.5],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"matrix-annular-first","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":3.141592653589793}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.141592653589793,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-8,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-8,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.141592653589793,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":6.283185307179586,"lo":3.141592653589793}},"halfedge":7,"id":7,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":8,"id":8,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":9,"id":9,"tolerance":1e-8,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":10,"id":10,"tolerance":1e-8,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":11,"id":11,"tolerance":1e-8,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":12,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":15,"id":13,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":21,"id":14,"tolerance":1e-8,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":23,"id":15,"tolerance":1e-8,"twin_halfedge":33},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":25,"id":16,"tolerance":1e-8,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":29,"id":17,"tolerance":1e-8,"twin_halfedge":35}],"faces":[{"id":0,"key":"lower-cap-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-annular-host","entity":"matrix-annular-host","face":2,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1],"outer":0,"uv_bounds":[{"hi":1.00000001,"lo":-1.00000001},{"hi":1.00000001,"lo":-1.00000001}]}},{"id":1,"key":"upper-cap-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-annular-host","entity":"matrix-annular-host","face":3,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[3],"outer":2,"uv_bounds":[{"hi":1.00000001,"lo":-1.00000001},{"hi":1.00000001,"lo":-1.00000001}]}},{"id":2,"key":"vertical-cylinder-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-annular-host","entity":"matrix-annular-host","face":0,"key":"outer"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":3.141592663589793,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"vertical-cylinder-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-annular-host","entity":"matrix-annular-host","face":0,"key":"outer"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":6.283185317179586,"lo":3.141592643589793},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"vertical-plane-4","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":4,"key":"front"}]},"sense":"Reverse","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":0.8000000100000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"vertical-plane-5","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":6,"key":"vertical-plane-6","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":5,"key":"back"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":0.8000000100000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":7,"key":"vertical-plane-7","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":1,"to":1,"twin":16,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":0,"prev":0,"to":0,"twin":12,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":1,"next":3,"prev":5,"to":3,"twin":32,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":1,"next":4,"prev":2,"to":4,"twin":28,"wire_ref":null},{"edge":4,"face":0,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":3,"to":5,"twin":24,"wire_ref":null},{"edge":5,"face":0,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":2,"prev":4,"to":2,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":2,"next":7,"prev":7,"to":7,"twin":14,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":2,"next":6,"prev":6,"to":6,"twin":18,"wire_ref":null},{"edge":8,"face":1,"from":8,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":3,"next":9,"prev":11,"to":9,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":9,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":3,"next":10,"prev":8,"to":10,"twin":26,"wire_ref":null},{"edge":10,"face":1,"from":10,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":3,"next":11,"prev":9,"to":11,"twin":30,"wire_ref":null},{"edge":11,"face":1,"from":11,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":3,"next":8,"prev":10,"to":8,"twin":34,"wire_ref":null},{"edge":1,"face":2,"from":0,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":4,"next":13,"prev":15,"to":1,"twin":1,"wire_ref":null},{"edge":12,"face":2,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":4,"next":14,"prev":12,"to":7,"twin":19,"wire_ref":null},{"edge":6,"face":2,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":4,"next":15,"prev":13,"to":6,"twin":6,"wire_ref":null},{"edge":13,"face":2,"from":6,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":4,"next":12,"prev":14,"to":0,"twin":17,"wire_ref":null},{"edge":0,"face":3,"from":1,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":5,"next":17,"prev":19,"to":0,"twin":0,"wire_ref":null},{"edge":13,"face":3,"from":0,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":5,"next":18,"prev":16,"to":6,"twin":15,"wire_ref":null},{"edge":7,"face":3,"from":6,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":5,"next":19,"prev":17,"to":7,"twin":7,"wire_ref":null},{"edge":12,"face":3,"from":7,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":5,"next":16,"prev":18,"to":1,"twin":13,"wire_ref":null},{"edge":5,"face":4,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Reverse"},"id":20,"loop_ref":6,"next":21,"prev":23,"to":5,"twin":5,"wire_ref":null},{"edge":14,"face":4,"from":5,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":6,"next":22,"prev":20,"to":9,"twin":27,"wire_ref":null},{"edge":8,"face":4,"from":9,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":6,"next":23,"prev":21,"to":8,"twin":8,"wire_ref":null},{"edge":15,"face":4,"from":8,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":6,"next":20,"prev":22,"to":2,"twin":33,"wire_ref":null},{"edge":4,"face":5,"from":5,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Reverse"},"id":24,"loop_ref":7,"next":25,"prev":27,"to":4,"twin":4,"wire_ref":null},{"edge":16,"face":5,"from":4,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":7,"next":26,"prev":24,"to":10,"twin":31,"wire_ref":null},{"edge":9,"face":5,"from":10,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":7,"next":27,"prev":25,"to":9,"twin":9,"wire_ref":null},{"edge":14,"face":5,"from":9,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":7,"next":24,"prev":26,"to":5,"twin":21,"wire_ref":null},{"edge":3,"face":6,"from":4,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Reverse"},"id":28,"loop_ref":8,"next":29,"prev":31,"to":3,"twin":3,"wire_ref":null},{"edge":17,"face":6,"from":3,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":8,"next":30,"prev":28,"to":11,"twin":35,"wire_ref":null},{"edge":10,"face":6,"from":11,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":8,"next":31,"prev":29,"to":10,"twin":10,"wire_ref":null},{"edge":16,"face":6,"from":10,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":8,"next":28,"prev":30,"to":4,"twin":25,"wire_ref":null},{"edge":2,"face":7,"from":3,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Reverse"},"id":32,"loop_ref":9,"next":33,"prev":35,"to":2,"twin":2,"wire_ref":null},{"edge":15,"face":7,"from":2,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":9,"next":34,"prev":32,"to":8,"twin":23,"wire_ref":null},{"edge":11,"face":7,"from":8,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":9,"next":35,"prev":33,"to":11,"twin":11,"wire_ref":null},{"edge":17,"face":7,"from":11,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":9,"next":32,"prev":34,"to":3,"twin":29,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":2},{"face_ref":1,"id":2,"is_hole":false,"start_halfedge":6},{"face_ref":1,"id":3,"is_hole":true,"start_halfedge":8},{"face_ref":2,"id":4,"is_hole":false,"start_halfedge":12},{"face_ref":3,"id":5,"is_hole":false,"start_halfedge":16},{"face_ref":4,"id":6,"is_hole":false,"start_halfedge":20},{"face_ref":5,"id":7,"is_hole":false,"start_halfedge":24},{"face_ref":6,"id":8,"is_hole":false,"start_halfedge":28},{"face_ref":7,"id":9,"is_hole":false,"start_halfedge":32}],"shells":[{"faces":[0,3,2,7,6,5,4,1],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-1.0,0.0,-1.2246467991473532e-16],"tolerance":1e-8},{"id":1,"outgoing_halfedge":1,"position":[1.0,0.0,2.4492935982947064e-16],"tolerance":1e-8},{"id":2,"outgoing_halfedge":2,"position":[-0.4,0.0,0.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":3,"position":[-0.4,0.0,-0.5],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.4,0.0,-0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[0.4,0.0,0.5],"tolerance":1e-8},{"id":6,"outgoing_halfedge":6,"position":[-1.0,3.0,-1.2246467991473532e-16],"tolerance":1e-8},{"id":7,"outgoing_halfedge":7,"position":[1.0,3.0,0.0],"tolerance":1e-8},{"id":8,"outgoing_halfedge":8,"position":[-0.4,3.0,0.5],"tolerance":1e-8},{"id":9,"outgoing_halfedge":9,"position":[0.4,3.0,0.5],"tolerance":1e-8},{"id":10,"outgoing_halfedge":10,"position":[0.4,3.0,-0.5],"tolerance":1e-8},{"id":11,"outgoing_halfedge":11,"position":[-0.4,3.0,-0.5],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-intersection.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-intersection.json new file mode 100644 index 0000000..9915538 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-intersection.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-box-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[3.0,3.0,-3.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-2.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,2.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-2.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-box-cutter","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.5,-0.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[3.0,0.5,-0.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[1.0,0.5,-2.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.0,0.5,-2.5],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.0,2.5,-0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[3.0,2.5,-0.5],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[1.0,2.5,-2.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[3.0,2.5,-2.5],"tolerance":1e-8}],"wires":[]}},"handlers":["boolean_boxes"],"operation":"intersection","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-2.5]}],"intersections":[],"pcurves":[{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-2.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,2.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-box-intersection","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":7,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":11,"id":9,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-8,"twin_halfedge":16}],"faces":[{"id":0,"key":"left-0","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"right-1","provenance":{"reversed":false,"role":"Coincident","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"},{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":2,"key":"front-2","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"back-3","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"bottom-4","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"top-5","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":23,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":12,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":19,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":10,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":17,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":6,"twin":14,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":7,"twin":21,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":8,"wire_ref":null},{"edge":7,"face":2,"from":4,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":7,"twin":7,"wire_ref":null},{"edge":8,"face":2,"from":7,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":0,"twin":20,"wire_ref":null},{"edge":3,"face":2,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":3,"wire_ref":null},{"edge":9,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":4,"twin":18,"wire_ref":null},{"edge":1,"face":3,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":1,"twin":1,"wire_ref":null},{"edge":10,"face":3,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":22,"wire_ref":null},{"edge":5,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":11,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":2,"twin":16,"wire_ref":null},{"edge":11,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":5,"twin":15,"wire_ref":null},{"edge":4,"face":4,"from":5,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":4,"twin":4,"wire_ref":null},{"edge":9,"face":4,"from":4,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":3,"twin":11,"wire_ref":null},{"edge":2,"face":4,"from":3,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":2,"wire_ref":null},{"edge":8,"face":5,"from":0,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":7,"twin":9,"wire_ref":null},{"edge":6,"face":5,"from":7,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":6,"twin":6,"wire_ref":null},{"edge":10,"face":5,"from":6,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":1,"twin":13,"wire_ref":null},{"edge":0,"face":5,"from":1,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":0,"twin":0,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,5,3,4,2,1],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,2.5,-0.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":1,"position":[1.0,2.5,-2.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":2,"position":[1.0,0.5,-2.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":3,"position":[1.0,0.5,-0.5],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[3.0,0.5,-0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[3.0,0.5,-2.5],"tolerance":1e-8},{"id":6,"outgoing_halfedge":6,"position":[3.0,2.5,-2.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":7,"position":[3.0,2.5,-0.5],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-round.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-round.json new file mode 100644 index 0000000..f9c60dc --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-round.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-box-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[3.0,3.0,-3.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[1.5,-1.0,-1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,4.0,-1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,-1.0,-1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,5.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.5,-1.0,-1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[1.5,-1.0,-1.5],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,4.0,-1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-box-round-cutter","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":5.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-round-cutter","entity":"matrix-box-round-cutter","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":5.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-round-cutter","entity":"matrix-box-round-cutter","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-round-cutter","entity":"matrix-box-round-cutter","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.9,-1.0,-1.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.9,4.0,-1.5],"tolerance":1e-8}],"wires":[]}},"handlers":["cylinder_box_boolean"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]},{"frame":{"origin":[1.5,0.0,-1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[1.5,3.0,-1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.9,0.0,-1.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.0,0.4],"axis_b":[0.4,0.0],"kind":"Conic2","origin":[1.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[1.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.0,-1.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4}]},"id":"matrix-box-round","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":13},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":2,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":4,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":25},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":12,"id":6,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":15,"id":7,"tolerance":1e-8,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":5,"id":8,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":9,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":6,"id":10,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":7,"id":11,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":4,"id":12,"tolerance":1e-8,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":9,"id":13,"tolerance":1e-8,"twin_halfedge":28},{"chart_seam":true,"geometry":{"curve":14,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":27,"id":14,"tolerance":1e-8,"twin_halfedge":29}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1],"outer":0,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[3],"outer":2,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":6,"key":"cutter:tunnel","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-box-round-cutter","entity":"matrix-box-round-cutter","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":13,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":25,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":14,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":18,"wire_ref":null},{"edge":12,"face":0,"from":8,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":8,"twin":26,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":6,"prev":8,"to":5,"twin":20,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":2,"next":7,"prev":5,"to":7,"twin":16,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":2,"next":8,"prev":6,"to":6,"twin":23,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":5,"prev":7,"to":4,"twin":11,"wire_ref":null},{"edge":13,"face":1,"from":9,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":3,"next":9,"prev":9,"to":9,"twin":28,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":4,"next":11,"prev":13,"to":4,"twin":21,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":4,"next":12,"prev":10,"to":6,"twin":8,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":4,"next":13,"prev":11,"to":2,"twin":22,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":4,"next":10,"prev":12,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Forward"},"id":14,"loop_ref":5,"next":15,"prev":17,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":5,"next":16,"prev":14,"to":7,"twin":24,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Reverse"},"id":16,"loop_ref":5,"next":17,"prev":15,"to":5,"twin":6,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":5,"next":14,"prev":16,"to":1,"twin":19,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Forward"},"id":18,"loop_ref":6,"next":19,"prev":21,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":6,"next":20,"prev":18,"to":5,"twin":17,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Reverse"},"id":20,"loop_ref":6,"next":21,"prev":19,"to":4,"twin":5,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Reverse"},"id":21,"loop_ref":6,"next":18,"prev":20,"to":0,"twin":10,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Forward"},"id":22,"loop_ref":7,"next":23,"prev":25,"to":6,"twin":12,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":7,"next":24,"prev":22,"to":7,"twin":7,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Reverse"},"id":24,"loop_ref":7,"next":25,"prev":23,"to":3,"twin":15,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":7,"next":22,"prev":24,"to":2,"twin":1,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":8,"next":29,"prev":27,"to":8,"twin":4,"wire_ref":null},{"edge":14,"face":6,"from":9,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":8,"next":26,"prev":28,"to":8,"twin":29,"wire_ref":null},{"edge":13,"face":6,"from":9,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":8,"next":27,"prev":29,"to":9,"twin":9,"wire_ref":null},{"edge":14,"face":6,"from":8,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":8,"next":28,"prev":26,"to":9,"twin":27,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":4},{"face_ref":1,"id":2,"is_hole":false,"start_halfedge":5},{"face_ref":1,"id":3,"is_hole":true,"start_halfedge":9},{"face_ref":2,"id":4,"is_hole":false,"start_halfedge":10},{"face_ref":3,"id":5,"is_hole":false,"start_halfedge":14},{"face_ref":4,"id":6,"is_hole":false,"start_halfedge":18},{"face_ref":5,"id":7,"is_hole":false,"start_halfedge":22},{"face_ref":6,"id":8,"is_hole":false,"start_halfedge":26}],"shells":[{"faces":[0,1,2,3,4,5,6],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":5,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":6,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":8,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":7,"position":[3.0,3.0,-3.0],"tolerance":1e-8},{"id":8,"outgoing_halfedge":4,"position":[1.9,0.0,-1.5],"tolerance":1e-8},{"id":9,"outgoing_halfedge":9,"position":[1.9,3.0,-1.5],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-subtract.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-subtract.json new file mode 100644 index 0000000..7721510 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-subtract.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-box-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[3.0,3.0,-3.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-2.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,2.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-2.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-box-cutter","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.5,-0.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[3.0,0.5,-0.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[1.0,0.5,-2.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.0,0.5,-2.5],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.0,2.5,-0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[3.0,2.5,-0.5],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[1.0,2.5,-2.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[3.0,2.5,-2.5],"tolerance":1e-8}],"wires":[]}},"handlers":["boolean_boxes"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.5,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,2.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.5,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,3.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.5,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.5,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.5,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.5,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.5,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,2.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,3.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.5,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.5,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,-3.0]}],"intersections":[],"pcurves":[{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.5]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,2.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-2.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-box-subtract","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-8,"twin_halfedge":6},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":94},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":6,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":8,"id":7,"tolerance":1e-8,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-8,"twin_halfedge":99},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":12,"id":9,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-8,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":16,"id":12,"tolerance":1e-8,"twin_halfedge":106},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":17,"id":13,"tolerance":1e-8,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":20,"id":14,"tolerance":1e-8,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":21,"id":15,"tolerance":1e-8,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":23,"id":16,"tolerance":1e-8,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":24,"id":17,"tolerance":1e-8,"twin_halfedge":118},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":25,"id":18,"tolerance":1e-8,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":29,"id":19,"tolerance":1e-8,"twin_halfedge":123},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":30,"id":20,"tolerance":1e-8,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":32,"id":21,"tolerance":1e-8,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":33,"id":22,"tolerance":1e-8,"twin_halfedge":59},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":34,"id":23,"tolerance":1e-8,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":36,"id":24,"tolerance":1e-8,"twin_halfedge":66},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":37,"id":25,"tolerance":1e-8,"twin_halfedge":131},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":40,"id":26,"tolerance":1e-8,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":41,"id":27,"tolerance":1e-8,"twin_halfedge":71},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":44,"id":28,"tolerance":1e-8,"twin_halfedge":135},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":45,"id":29,"tolerance":1e-8,"twin_halfedge":166},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":46,"id":30,"tolerance":1e-8,"twin_halfedge":143},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":47,"id":31,"tolerance":1e-8,"twin_halfedge":108},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":48,"id":32,"tolerance":1e-8,"twin_halfedge":55},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":49,"id":33,"tolerance":1e-8,"twin_halfedge":79},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":53,"id":34,"tolerance":1e-8,"twin_halfedge":147},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":54,"id":35,"tolerance":1e-8,"twin_halfedge":84},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":56,"id":36,"tolerance":1e-8,"twin_halfedge":70},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":57,"id":37,"tolerance":1e-8,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":58,"id":38,"tolerance":1e-8,"twin_halfedge":67},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":61,"id":39,"tolerance":1e-8,"twin_halfedge":75},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":62,"id":40,"tolerance":1e-8,"twin_halfedge":152},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":63,"id":41,"tolerance":1e-8,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":65,"id":42,"tolerance":1e-8,"twin_halfedge":159},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":68,"id":43,"tolerance":1e-8,"twin_halfedge":78},{"chart_seam":false,"geometry":{"curve":44,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":69,"id":44,"tolerance":1e-8,"twin_halfedge":72},{"chart_seam":false,"geometry":{"curve":45,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":73,"id":45,"tolerance":1e-8,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":46,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":74,"id":46,"tolerance":1e-8,"twin_halfedge":168},{"chart_seam":false,"geometry":{"curve":47,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":76,"id":47,"tolerance":1e-8,"twin_halfedge":87},{"chart_seam":false,"geometry":{"curve":48,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":77,"id":48,"tolerance":1e-8,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":49,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":81,"id":49,"tolerance":1e-8,"twin_halfedge":86},{"chart_seam":false,"geometry":{"curve":50,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":82,"id":50,"tolerance":1e-8,"twin_halfedge":176},{"chart_seam":false,"geometry":{"curve":51,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":85,"id":51,"tolerance":1e-8,"twin_halfedge":183},{"chart_seam":false,"geometry":{"curve":52,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":88,"id":52,"tolerance":1e-8,"twin_halfedge":97},{"chart_seam":false,"geometry":{"curve":53,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":89,"id":53,"tolerance":1e-8,"twin_halfedge":127},{"chart_seam":false,"geometry":{"curve":54,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":90,"id":54,"tolerance":1e-8,"twin_halfedge":100},{"chart_seam":false,"geometry":{"curve":55,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":91,"id":55,"tolerance":1e-8,"twin_halfedge":92},{"chart_seam":false,"geometry":{"curve":56,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":93,"id":56,"tolerance":1e-8,"twin_halfedge":107},{"chart_seam":false,"geometry":{"curve":57,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":95,"id":57,"tolerance":1e-8,"twin_halfedge":98},{"chart_seam":false,"geometry":{"curve":58,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":96,"id":58,"tolerance":1e-8,"twin_halfedge":130},{"chart_seam":false,"geometry":{"curve":59,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":101,"id":59,"tolerance":1e-8,"twin_halfedge":110},{"chart_seam":false,"geometry":{"curve":60,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":102,"id":60,"tolerance":1e-8,"twin_halfedge":112},{"chart_seam":false,"geometry":{"curve":61,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":103,"id":61,"tolerance":1e-8,"twin_halfedge":104},{"chart_seam":false,"geometry":{"curve":62,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":105,"id":62,"tolerance":1e-8,"twin_halfedge":119},{"chart_seam":false,"geometry":{"curve":63,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":109,"id":63,"tolerance":1e-8,"twin_halfedge":142},{"chart_seam":false,"geometry":{"curve":64,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":111,"id":64,"tolerance":1e-8,"twin_halfedge":132},{"chart_seam":false,"geometry":{"curve":65,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":113,"id":65,"tolerance":1e-8,"twin_halfedge":139},{"chart_seam":false,"geometry":{"curve":66,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":114,"id":66,"tolerance":1e-8,"twin_halfedge":121},{"chart_seam":false,"geometry":{"curve":67,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":115,"id":67,"tolerance":1e-8,"twin_halfedge":116},{"chart_seam":false,"geometry":{"curve":68,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":117,"id":68,"tolerance":1e-8,"twin_halfedge":120},{"chart_seam":false,"geometry":{"curve":69,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":122,"id":69,"tolerance":1e-8,"twin_halfedge":144},{"chart_seam":false,"geometry":{"curve":70,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":124,"id":70,"tolerance":1e-8,"twin_halfedge":129},{"chart_seam":false,"geometry":{"curve":71,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":125,"id":71,"tolerance":1e-8,"twin_halfedge":151},{"chart_seam":false,"geometry":{"curve":72,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":126,"id":72,"tolerance":1e-8,"twin_halfedge":133},{"chart_seam":false,"geometry":{"curve":73,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":128,"id":73,"tolerance":1e-8,"twin_halfedge":158},{"chart_seam":false,"geometry":{"curve":74,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":134,"id":74,"tolerance":1e-8,"twin_halfedge":167},{"chart_seam":false,"geometry":{"curve":75,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":136,"id":75,"tolerance":1e-8,"twin_halfedge":141},{"chart_seam":false,"geometry":{"curve":76,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":137,"id":76,"tolerance":1e-8,"twin_halfedge":175},{"chart_seam":false,"geometry":{"curve":77,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":138,"id":77,"tolerance":1e-8,"twin_halfedge":145},{"chart_seam":false,"geometry":{"curve":78,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":140,"id":78,"tolerance":1e-8,"twin_halfedge":165},{"chart_seam":false,"geometry":{"curve":79,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":146,"id":79,"tolerance":1e-8,"twin_halfedge":180},{"chart_seam":false,"geometry":{"curve":80,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":148,"id":80,"tolerance":1e-8,"twin_halfedge":157},{"chart_seam":false,"geometry":{"curve":81,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":149,"id":81,"tolerance":1e-8,"twin_halfedge":154},{"chart_seam":false,"geometry":{"curve":82,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":150,"id":82,"tolerance":1e-8,"twin_halfedge":160},{"chart_seam":false,"geometry":{"curve":83,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":153,"id":83,"tolerance":1e-8,"twin_halfedge":171},{"chart_seam":false,"geometry":{"curve":84,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":155,"id":84,"tolerance":1e-8,"twin_halfedge":156},{"chart_seam":false,"geometry":{"curve":85,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":161,"id":85,"tolerance":1e-8,"twin_halfedge":170},{"chart_seam":false,"geometry":{"curve":86,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":162,"id":86,"tolerance":1e-8,"twin_halfedge":172},{"chart_seam":false,"geometry":{"curve":87,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":163,"id":87,"tolerance":1e-8,"twin_halfedge":164},{"chart_seam":false,"geometry":{"curve":88,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":169,"id":88,"tolerance":1e-8,"twin_halfedge":179},{"chart_seam":false,"geometry":{"curve":89,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":173,"id":89,"tolerance":1e-8,"twin_halfedge":178},{"chart_seam":false,"geometry":{"curve":90,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":174,"id":90,"tolerance":1e-8,"twin_halfedge":181},{"chart_seam":false,"geometry":{"curve":91,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":177,"id":91,"tolerance":1e-8,"twin_halfedge":182}],"faces":[{"id":0,"key":"left-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"front-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":2,"key":"bottom-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":3,"key":"left-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":4,"key":"front-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.50000001,"lo":0.49999999}]}},{"id":5,"key":"left-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":6,"key":"front-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":7,"key":"top-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":8,"key":"left-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.50000001,"lo":0.49999999}]}},{"id":9,"key":"bottom-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":10,"key":"left-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":11,"key":"left-11","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":12,"key":"left-12","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":13,"key":"top-13","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.50000001,"lo":0.49999999}]}},{"id":14,"key":"left-14","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":15,"key":"back-15","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":16,"key":"bottom-16","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[],"outer":16,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":17,"key":"left-17","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":17,"trim":{"chart":0,"holes":[],"outer":17,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":18,"key":"back-18","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":18,"trim":{"chart":0,"holes":[],"outer":18,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":19,"key":"left-19","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":19,"trim":{"chart":0,"holes":[],"outer":19,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":20,"key":"back-20","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":20,"trim":{"chart":0,"holes":[],"outer":20,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":21,"key":"top-21","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":21,"trim":{"chart":0,"holes":[],"outer":21,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":22,"key":"right-22","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":22,"trim":{"chart":0,"holes":[],"outer":22,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":23,"key":"front-23","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":23,"trim":{"chart":0,"holes":[],"outer":23,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":24,"key":"bottom-24","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":24,"trim":{"chart":0,"holes":[],"outer":24,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.99999999}]}},{"id":25,"key":"right-25","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":25,"trim":{"chart":0,"holes":[],"outer":25,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.50000001,"lo":0.49999999}]}},{"id":26,"key":"front-26","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":26,"trim":{"chart":0,"holes":[],"outer":26,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":27,"key":"front-27","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":4,"key":"front"}]},"sense":"Reverse","shell_ref":0,"surface":27,"trim":{"chart":0,"holes":[],"outer":27,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":28,"key":"right-28","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":28,"trim":{"chart":0,"holes":[],"outer":28,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":29,"key":"front-29","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":29,"trim":{"chart":0,"holes":[],"outer":29,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":30,"key":"top-30","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":30,"trim":{"chart":0,"holes":[],"outer":30,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":31,"key":"right-31","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":31,"trim":{"chart":0,"holes":[],"outer":31,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":32,"key":"bottom-32","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":32,"trim":{"chart":0,"holes":[],"outer":32,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":3.00000001,"lo":0.99999999}]}},{"id":33,"key":"bottom-33","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":0,"key":"bottom"}]},"sense":"Reverse","shell_ref":0,"surface":33,"trim":{"chart":0,"holes":[],"outer":33,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":34,"key":"right-34","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":34,"trim":{"chart":0,"holes":[],"outer":34,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":35,"key":"top-35","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":0,"surface":35,"trim":{"chart":0,"holes":[],"outer":35,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":36,"key":"top-36","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":36,"trim":{"chart":0,"holes":[],"outer":36,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":37,"key":"right-37","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":37,"trim":{"chart":0,"holes":[],"outer":37,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":38,"key":"back-38","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":38,"trim":{"chart":0,"holes":[],"outer":38,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.99999999}]}},{"id":39,"key":"bottom-39","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":39,"trim":{"chart":0,"holes":[],"outer":39,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":0.99999999}]}},{"id":40,"key":"right-40","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":40,"trim":{"chart":0,"holes":[],"outer":40,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":41,"key":"back-41","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":5,"key":"back"}]},"sense":"Reverse","shell_ref":0,"surface":41,"trim":{"chart":0,"holes":[],"outer":41,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":42,"key":"back-42","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":42,"trim":{"chart":0,"holes":[],"outer":42,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":3.00000001,"lo":0.99999999}]}},{"id":43,"key":"right-43","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":43,"trim":{"chart":0,"holes":[],"outer":43,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":44,"key":"back-44","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":44,"trim":{"chart":0,"holes":[],"outer":44,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":0.99999999}]}},{"id":45,"key":"top-45","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":45,"trim":{"chart":0,"holes":[],"outer":45,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":3.00000001,"lo":2.49999999}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":14,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":35,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":11,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":6,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":94,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":0,"twin":19,"wire_ref":null},{"edge":3,"face":1,"from":0,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":3,"twin":3,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":10,"wire_ref":null},{"edge":7,"face":2,"from":2,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":6,"twin":38,"wire_ref":null},{"edge":8,"face":2,"from":6,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":4,"twin":99,"wire_ref":null},{"edge":6,"face":2,"from":4,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":7,"wire_ref":null},{"edge":2,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":2,"twin":2,"wire_ref":null},{"edge":9,"face":3,"from":7,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":8,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":8,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":1,"twin":43,"wire_ref":null},{"edge":0,"face":3,"from":1,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":0,"twin":0,"wire_ref":null},{"edge":11,"face":3,"from":0,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":7,"twin":18,"wire_ref":null},{"edge":12,"face":4,"from":5,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":9,"twin":106,"wire_ref":null},{"edge":13,"face":4,"from":9,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":27,"wire_ref":null},{"edge":11,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":0,"twin":15,"wire_ref":null},{"edge":5,"face":4,"from":0,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":5,"twin":5,"wire_ref":null},{"edge":14,"face":5,"from":10,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":11,"twin":31,"wire_ref":null},{"edge":15,"face":5,"from":11,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Reverse"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":8,"twin":51,"wire_ref":null},{"edge":9,"face":5,"from":8,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":12,"wire_ref":null},{"edge":16,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":10,"twin":26,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":12,"twin":118,"wire_ref":null},{"edge":18,"face":6,"from":12,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":10,"twin":28,"wire_ref":null},{"edge":16,"face":6,"from":10,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":7,"twin":23,"wire_ref":null},{"edge":13,"face":6,"from":7,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":9,"twin":17,"wire_ref":null},{"edge":18,"face":7,"from":10,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":12,"twin":25,"wire_ref":null},{"edge":19,"face":7,"from":12,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":13,"twin":123,"wire_ref":null},{"edge":20,"face":7,"from":13,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":11,"twin":52,"wire_ref":null},{"edge":14,"face":7,"from":11,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":10,"twin":20,"wire_ref":null},{"edge":21,"face":8,"from":1,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":14,"twin":42,"wire_ref":null},{"edge":22,"face":8,"from":14,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":15,"twin":59,"wire_ref":null},{"edge":23,"face":8,"from":15,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":2,"twin":39,"wire_ref":null},{"edge":1,"face":8,"from":2,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Forward"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":1,"twin":1,"wire_ref":null},{"edge":24,"face":9,"from":15,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":16,"twin":66,"wire_ref":null},{"edge":25,"face":9,"from":16,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":6,"twin":131,"wire_ref":null},{"edge":7,"face":9,"from":6,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":2,"twin":8,"wire_ref":null},{"edge":23,"face":9,"from":2,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":15,"twin":34,"wire_ref":null},{"edge":26,"face":10,"from":8,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":17,"twin":50,"wire_ref":null},{"edge":27,"face":10,"from":17,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Reverse"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":14,"twin":71,"wire_ref":null},{"edge":21,"face":10,"from":14,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":1,"twin":32,"wire_ref":null},{"edge":10,"face":10,"from":1,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Forward"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":8,"twin":13,"wire_ref":null},{"edge":28,"face":11,"from":18,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":19,"twin":135,"wire_ref":null},{"edge":29,"face":11,"from":19,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":20,"twin":166,"wire_ref":null},{"edge":30,"face":11,"from":20,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":21,"twin":143,"wire_ref":null},{"edge":31,"face":11,"from":21,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":18,"twin":108,"wire_ref":null},{"edge":32,"face":12,"from":11,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":12,"next":49,"prev":51,"to":22,"twin":55,"wire_ref":null},{"edge":33,"face":12,"from":22,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Reverse"},"id":49,"loop_ref":12,"next":50,"prev":48,"to":17,"twin":79,"wire_ref":null},{"edge":26,"face":12,"from":17,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":12,"next":51,"prev":49,"to":8,"twin":40,"wire_ref":null},{"edge":15,"face":12,"from":8,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Forward"},"id":51,"loop_ref":12,"next":48,"prev":50,"to":11,"twin":21,"wire_ref":null},{"edge":20,"face":13,"from":11,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":13,"next":53,"prev":55,"to":13,"twin":30,"wire_ref":null},{"edge":34,"face":13,"from":13,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":13,"next":54,"prev":52,"to":23,"twin":147,"wire_ref":null},{"edge":35,"face":13,"from":23,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":13,"next":55,"prev":53,"to":22,"twin":84,"wire_ref":null},{"edge":32,"face":13,"from":22,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Reverse"},"id":55,"loop_ref":13,"next":52,"prev":54,"to":11,"twin":48,"wire_ref":null},{"edge":36,"face":14,"from":14,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":14,"next":57,"prev":59,"to":24,"twin":70,"wire_ref":null},{"edge":37,"face":14,"from":24,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Reverse"},"id":57,"loop_ref":14,"next":58,"prev":56,"to":25,"twin":60,"wire_ref":null},{"edge":38,"face":14,"from":25,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":14,"next":59,"prev":57,"to":15,"twin":67,"wire_ref":null},{"edge":22,"face":14,"from":15,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Forward"},"id":59,"loop_ref":14,"next":56,"prev":58,"to":14,"twin":33,"wire_ref":null},{"edge":37,"face":15,"from":25,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Forward"},"id":60,"loop_ref":15,"next":61,"prev":63,"to":24,"twin":57,"wire_ref":null},{"edge":39,"face":15,"from":24,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Forward"},"id":61,"loop_ref":15,"next":62,"prev":60,"to":26,"twin":75,"wire_ref":null},{"edge":40,"face":15,"from":26,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":15,"next":63,"prev":61,"to":27,"twin":152,"wire_ref":null},{"edge":41,"face":15,"from":27,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Reverse"},"id":63,"loop_ref":15,"next":60,"prev":62,"to":25,"twin":64,"wire_ref":null},{"edge":41,"face":16,"from":25,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Forward"},"id":64,"loop_ref":16,"next":65,"prev":67,"to":27,"twin":63,"wire_ref":null},{"edge":42,"face":16,"from":27,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Reverse"},"id":65,"loop_ref":16,"next":66,"prev":64,"to":16,"twin":159,"wire_ref":null},{"edge":24,"face":16,"from":16,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Reverse"},"id":66,"loop_ref":16,"next":67,"prev":65,"to":15,"twin":36,"wire_ref":null},{"edge":38,"face":16,"from":15,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Forward"},"id":67,"loop_ref":16,"next":64,"prev":66,"to":25,"twin":58,"wire_ref":null},{"edge":43,"face":17,"from":17,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":17,"next":69,"prev":71,"to":28,"twin":78,"wire_ref":null},{"edge":44,"face":17,"from":28,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Reverse"},"id":69,"loop_ref":17,"next":70,"prev":68,"to":24,"twin":72,"wire_ref":null},{"edge":36,"face":17,"from":24,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":17,"next":71,"prev":69,"to":14,"twin":56,"wire_ref":null},{"edge":27,"face":17,"from":14,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Forward"},"id":71,"loop_ref":17,"next":68,"prev":70,"to":17,"twin":41,"wire_ref":null},{"edge":44,"face":18,"from":24,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Forward"},"id":72,"loop_ref":18,"next":73,"prev":75,"to":28,"twin":69,"wire_ref":null},{"edge":45,"face":18,"from":28,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Forward"},"id":73,"loop_ref":18,"next":74,"prev":72,"to":29,"twin":83,"wire_ref":null},{"edge":46,"face":18,"from":29,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":18,"next":75,"prev":73,"to":26,"twin":168,"wire_ref":null},{"edge":39,"face":18,"from":26,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Reverse"},"id":75,"loop_ref":18,"next":72,"prev":74,"to":24,"twin":61,"wire_ref":null},{"edge":47,"face":19,"from":22,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Forward"},"id":76,"loop_ref":19,"next":77,"prev":79,"to":30,"twin":87,"wire_ref":null},{"edge":48,"face":19,"from":30,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Reverse"},"id":77,"loop_ref":19,"next":78,"prev":76,"to":28,"twin":80,"wire_ref":null},{"edge":43,"face":19,"from":28,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":19,"next":79,"prev":77,"to":17,"twin":68,"wire_ref":null},{"edge":33,"face":19,"from":17,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Forward"},"id":79,"loop_ref":19,"next":76,"prev":78,"to":22,"twin":49,"wire_ref":null},{"edge":48,"face":20,"from":28,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Forward"},"id":80,"loop_ref":20,"next":81,"prev":83,"to":30,"twin":77,"wire_ref":null},{"edge":49,"face":20,"from":30,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Forward"},"id":81,"loop_ref":20,"next":82,"prev":80,"to":31,"twin":86,"wire_ref":null},{"edge":50,"face":20,"from":31,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":20,"next":83,"prev":81,"to":29,"twin":176,"wire_ref":null},{"edge":45,"face":20,"from":29,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":20,"next":80,"prev":82,"to":28,"twin":73,"wire_ref":null},{"edge":35,"face":21,"from":22,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Forward"},"id":84,"loop_ref":21,"next":85,"prev":87,"to":23,"twin":54,"wire_ref":null},{"edge":51,"face":21,"from":23,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Forward"},"id":85,"loop_ref":21,"next":86,"prev":84,"to":31,"twin":183,"wire_ref":null},{"edge":49,"face":21,"from":31,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Reverse"},"id":86,"loop_ref":21,"next":87,"prev":85,"to":30,"twin":81,"wire_ref":null},{"edge":47,"face":21,"from":30,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Reverse"},"id":87,"loop_ref":21,"next":84,"prev":86,"to":22,"twin":76,"wire_ref":null},{"edge":52,"face":22,"from":32,"geometry_use":{"pcurve":88,"periodic_lift":[0,0],"sense":"Forward"},"id":88,"loop_ref":22,"next":89,"prev":91,"to":33,"twin":97,"wire_ref":null},{"edge":53,"face":22,"from":33,"geometry_use":{"pcurve":89,"periodic_lift":[0,0],"sense":"Forward"},"id":89,"loop_ref":22,"next":90,"prev":88,"to":34,"twin":127,"wire_ref":null},{"edge":54,"face":22,"from":34,"geometry_use":{"pcurve":90,"periodic_lift":[0,0],"sense":"Reverse"},"id":90,"loop_ref":22,"next":91,"prev":89,"to":35,"twin":100,"wire_ref":null},{"edge":55,"face":22,"from":35,"geometry_use":{"pcurve":91,"periodic_lift":[0,0],"sense":"Reverse"},"id":91,"loop_ref":22,"next":88,"prev":90,"to":32,"twin":92,"wire_ref":null},{"edge":55,"face":23,"from":32,"geometry_use":{"pcurve":92,"periodic_lift":[0,0],"sense":"Forward"},"id":92,"loop_ref":23,"next":93,"prev":95,"to":35,"twin":91,"wire_ref":null},{"edge":56,"face":23,"from":35,"geometry_use":{"pcurve":93,"periodic_lift":[0,0],"sense":"Reverse"},"id":93,"loop_ref":23,"next":94,"prev":92,"to":5,"twin":107,"wire_ref":null},{"edge":4,"face":23,"from":5,"geometry_use":{"pcurve":94,"periodic_lift":[0,0],"sense":"Reverse"},"id":94,"loop_ref":23,"next":95,"prev":93,"to":4,"twin":4,"wire_ref":null},{"edge":57,"face":23,"from":4,"geometry_use":{"pcurve":95,"periodic_lift":[0,0],"sense":"Forward"},"id":95,"loop_ref":23,"next":92,"prev":94,"to":32,"twin":98,"wire_ref":null},{"edge":58,"face":24,"from":6,"geometry_use":{"pcurve":96,"periodic_lift":[0,0],"sense":"Forward"},"id":96,"loop_ref":24,"next":97,"prev":99,"to":33,"twin":130,"wire_ref":null},{"edge":52,"face":24,"from":33,"geometry_use":{"pcurve":97,"periodic_lift":[0,0],"sense":"Reverse"},"id":97,"loop_ref":24,"next":98,"prev":96,"to":32,"twin":88,"wire_ref":null},{"edge":57,"face":24,"from":32,"geometry_use":{"pcurve":98,"periodic_lift":[0,0],"sense":"Reverse"},"id":98,"loop_ref":24,"next":99,"prev":97,"to":4,"twin":95,"wire_ref":null},{"edge":8,"face":24,"from":4,"geometry_use":{"pcurve":99,"periodic_lift":[0,0],"sense":"Forward"},"id":99,"loop_ref":24,"next":96,"prev":98,"to":6,"twin":9,"wire_ref":null},{"edge":54,"face":25,"from":35,"geometry_use":{"pcurve":100,"periodic_lift":[0,0],"sense":"Forward"},"id":100,"loop_ref":25,"next":101,"prev":103,"to":34,"twin":90,"wire_ref":null},{"edge":59,"face":25,"from":34,"geometry_use":{"pcurve":101,"periodic_lift":[0,0],"sense":"Forward"},"id":101,"loop_ref":25,"next":102,"prev":100,"to":36,"twin":110,"wire_ref":null},{"edge":60,"face":25,"from":36,"geometry_use":{"pcurve":102,"periodic_lift":[0,0],"sense":"Reverse"},"id":102,"loop_ref":25,"next":103,"prev":101,"to":37,"twin":112,"wire_ref":null},{"edge":61,"face":25,"from":37,"geometry_use":{"pcurve":103,"periodic_lift":[0,0],"sense":"Reverse"},"id":103,"loop_ref":25,"next":100,"prev":102,"to":35,"twin":104,"wire_ref":null},{"edge":61,"face":26,"from":35,"geometry_use":{"pcurve":104,"periodic_lift":[0,0],"sense":"Forward"},"id":104,"loop_ref":26,"next":105,"prev":107,"to":37,"twin":103,"wire_ref":null},{"edge":62,"face":26,"from":37,"geometry_use":{"pcurve":105,"periodic_lift":[0,0],"sense":"Reverse"},"id":105,"loop_ref":26,"next":106,"prev":104,"to":9,"twin":119,"wire_ref":null},{"edge":12,"face":26,"from":9,"geometry_use":{"pcurve":106,"periodic_lift":[0,0],"sense":"Reverse"},"id":106,"loop_ref":26,"next":107,"prev":105,"to":5,"twin":16,"wire_ref":null},{"edge":56,"face":26,"from":5,"geometry_use":{"pcurve":107,"periodic_lift":[0,0],"sense":"Forward"},"id":107,"loop_ref":26,"next":104,"prev":106,"to":35,"twin":93,"wire_ref":null},{"edge":31,"face":27,"from":18,"geometry_use":{"pcurve":108,"periodic_lift":[0,0],"sense":"Forward"},"id":108,"loop_ref":27,"next":109,"prev":111,"to":21,"twin":47,"wire_ref":null},{"edge":63,"face":27,"from":21,"geometry_use":{"pcurve":109,"periodic_lift":[0,0],"sense":"Forward"},"id":109,"loop_ref":27,"next":110,"prev":108,"to":36,"twin":142,"wire_ref":null},{"edge":59,"face":27,"from":36,"geometry_use":{"pcurve":110,"periodic_lift":[0,0],"sense":"Reverse"},"id":110,"loop_ref":27,"next":111,"prev":109,"to":34,"twin":101,"wire_ref":null},{"edge":64,"face":27,"from":34,"geometry_use":{"pcurve":111,"periodic_lift":[0,0],"sense":"Reverse"},"id":111,"loop_ref":27,"next":108,"prev":110,"to":18,"twin":132,"wire_ref":null},{"edge":60,"face":28,"from":37,"geometry_use":{"pcurve":112,"periodic_lift":[0,0],"sense":"Forward"},"id":112,"loop_ref":28,"next":113,"prev":115,"to":36,"twin":102,"wire_ref":null},{"edge":65,"face":28,"from":36,"geometry_use":{"pcurve":113,"periodic_lift":[0,0],"sense":"Forward"},"id":113,"loop_ref":28,"next":114,"prev":112,"to":38,"twin":139,"wire_ref":null},{"edge":66,"face":28,"from":38,"geometry_use":{"pcurve":114,"periodic_lift":[0,0],"sense":"Reverse"},"id":114,"loop_ref":28,"next":115,"prev":113,"to":39,"twin":121,"wire_ref":null},{"edge":67,"face":28,"from":39,"geometry_use":{"pcurve":115,"periodic_lift":[0,0],"sense":"Reverse"},"id":115,"loop_ref":28,"next":112,"prev":114,"to":37,"twin":116,"wire_ref":null},{"edge":67,"face":29,"from":37,"geometry_use":{"pcurve":116,"periodic_lift":[0,0],"sense":"Forward"},"id":116,"loop_ref":29,"next":117,"prev":119,"to":39,"twin":115,"wire_ref":null},{"edge":68,"face":29,"from":39,"geometry_use":{"pcurve":117,"periodic_lift":[0,0],"sense":"Reverse"},"id":117,"loop_ref":29,"next":118,"prev":116,"to":12,"twin":120,"wire_ref":null},{"edge":17,"face":29,"from":12,"geometry_use":{"pcurve":118,"periodic_lift":[0,0],"sense":"Reverse"},"id":118,"loop_ref":29,"next":119,"prev":117,"to":9,"twin":24,"wire_ref":null},{"edge":62,"face":29,"from":9,"geometry_use":{"pcurve":119,"periodic_lift":[0,0],"sense":"Forward"},"id":119,"loop_ref":29,"next":116,"prev":118,"to":37,"twin":105,"wire_ref":null},{"edge":68,"face":30,"from":12,"geometry_use":{"pcurve":120,"periodic_lift":[0,0],"sense":"Forward"},"id":120,"loop_ref":30,"next":121,"prev":123,"to":39,"twin":117,"wire_ref":null},{"edge":66,"face":30,"from":39,"geometry_use":{"pcurve":121,"periodic_lift":[0,0],"sense":"Forward"},"id":121,"loop_ref":30,"next":122,"prev":120,"to":38,"twin":114,"wire_ref":null},{"edge":69,"face":30,"from":38,"geometry_use":{"pcurve":122,"periodic_lift":[0,0],"sense":"Reverse"},"id":122,"loop_ref":30,"next":123,"prev":121,"to":13,"twin":144,"wire_ref":null},{"edge":19,"face":30,"from":13,"geometry_use":{"pcurve":123,"periodic_lift":[0,0],"sense":"Reverse"},"id":123,"loop_ref":30,"next":120,"prev":122,"to":12,"twin":29,"wire_ref":null},{"edge":70,"face":31,"from":33,"geometry_use":{"pcurve":124,"periodic_lift":[0,0],"sense":"Forward"},"id":124,"loop_ref":31,"next":125,"prev":127,"to":40,"twin":129,"wire_ref":null},{"edge":71,"face":31,"from":40,"geometry_use":{"pcurve":125,"periodic_lift":[0,0],"sense":"Forward"},"id":125,"loop_ref":31,"next":126,"prev":124,"to":41,"twin":151,"wire_ref":null},{"edge":72,"face":31,"from":41,"geometry_use":{"pcurve":126,"periodic_lift":[0,0],"sense":"Reverse"},"id":126,"loop_ref":31,"next":127,"prev":125,"to":34,"twin":133,"wire_ref":null},{"edge":53,"face":31,"from":34,"geometry_use":{"pcurve":127,"periodic_lift":[0,0],"sense":"Reverse"},"id":127,"loop_ref":31,"next":124,"prev":126,"to":33,"twin":89,"wire_ref":null},{"edge":73,"face":32,"from":16,"geometry_use":{"pcurve":128,"periodic_lift":[0,0],"sense":"Forward"},"id":128,"loop_ref":32,"next":129,"prev":131,"to":40,"twin":158,"wire_ref":null},{"edge":70,"face":32,"from":40,"geometry_use":{"pcurve":129,"periodic_lift":[0,0],"sense":"Reverse"},"id":129,"loop_ref":32,"next":130,"prev":128,"to":33,"twin":124,"wire_ref":null},{"edge":58,"face":32,"from":33,"geometry_use":{"pcurve":130,"periodic_lift":[0,0],"sense":"Reverse"},"id":130,"loop_ref":32,"next":131,"prev":129,"to":6,"twin":96,"wire_ref":null},{"edge":25,"face":32,"from":6,"geometry_use":{"pcurve":131,"periodic_lift":[0,0],"sense":"Forward"},"id":131,"loop_ref":32,"next":128,"prev":130,"to":16,"twin":37,"wire_ref":null},{"edge":64,"face":33,"from":18,"geometry_use":{"pcurve":132,"periodic_lift":[0,0],"sense":"Forward"},"id":132,"loop_ref":33,"next":133,"prev":135,"to":34,"twin":111,"wire_ref":null},{"edge":72,"face":33,"from":34,"geometry_use":{"pcurve":133,"periodic_lift":[0,0],"sense":"Forward"},"id":133,"loop_ref":33,"next":134,"prev":132,"to":41,"twin":126,"wire_ref":null},{"edge":74,"face":33,"from":41,"geometry_use":{"pcurve":134,"periodic_lift":[0,0],"sense":"Reverse"},"id":134,"loop_ref":33,"next":135,"prev":133,"to":19,"twin":167,"wire_ref":null},{"edge":28,"face":33,"from":19,"geometry_use":{"pcurve":135,"periodic_lift":[0,0],"sense":"Reverse"},"id":135,"loop_ref":33,"next":132,"prev":134,"to":18,"twin":44,"wire_ref":null},{"edge":75,"face":34,"from":36,"geometry_use":{"pcurve":136,"periodic_lift":[0,0],"sense":"Forward"},"id":136,"loop_ref":34,"next":137,"prev":139,"to":42,"twin":141,"wire_ref":null},{"edge":76,"face":34,"from":42,"geometry_use":{"pcurve":137,"periodic_lift":[0,0],"sense":"Forward"},"id":137,"loop_ref":34,"next":138,"prev":136,"to":43,"twin":175,"wire_ref":null},{"edge":77,"face":34,"from":43,"geometry_use":{"pcurve":138,"periodic_lift":[0,0],"sense":"Reverse"},"id":138,"loop_ref":34,"next":139,"prev":137,"to":38,"twin":145,"wire_ref":null},{"edge":65,"face":34,"from":38,"geometry_use":{"pcurve":139,"periodic_lift":[0,0],"sense":"Reverse"},"id":139,"loop_ref":34,"next":136,"prev":138,"to":36,"twin":113,"wire_ref":null},{"edge":78,"face":35,"from":20,"geometry_use":{"pcurve":140,"periodic_lift":[0,0],"sense":"Forward"},"id":140,"loop_ref":35,"next":141,"prev":143,"to":42,"twin":165,"wire_ref":null},{"edge":75,"face":35,"from":42,"geometry_use":{"pcurve":141,"periodic_lift":[0,0],"sense":"Reverse"},"id":141,"loop_ref":35,"next":142,"prev":140,"to":36,"twin":136,"wire_ref":null},{"edge":63,"face":35,"from":36,"geometry_use":{"pcurve":142,"periodic_lift":[0,0],"sense":"Reverse"},"id":142,"loop_ref":35,"next":143,"prev":141,"to":21,"twin":109,"wire_ref":null},{"edge":30,"face":35,"from":21,"geometry_use":{"pcurve":143,"periodic_lift":[0,0],"sense":"Forward"},"id":143,"loop_ref":35,"next":140,"prev":142,"to":20,"twin":46,"wire_ref":null},{"edge":69,"face":36,"from":13,"geometry_use":{"pcurve":144,"periodic_lift":[0,0],"sense":"Forward"},"id":144,"loop_ref":36,"next":145,"prev":147,"to":38,"twin":122,"wire_ref":null},{"edge":77,"face":36,"from":38,"geometry_use":{"pcurve":145,"periodic_lift":[0,0],"sense":"Forward"},"id":145,"loop_ref":36,"next":146,"prev":144,"to":43,"twin":138,"wire_ref":null},{"edge":79,"face":36,"from":43,"geometry_use":{"pcurve":146,"periodic_lift":[0,0],"sense":"Reverse"},"id":146,"loop_ref":36,"next":147,"prev":145,"to":23,"twin":180,"wire_ref":null},{"edge":34,"face":36,"from":23,"geometry_use":{"pcurve":147,"periodic_lift":[0,0],"sense":"Reverse"},"id":147,"loop_ref":36,"next":144,"prev":146,"to":13,"twin":53,"wire_ref":null},{"edge":80,"face":37,"from":40,"geometry_use":{"pcurve":148,"periodic_lift":[0,0],"sense":"Forward"},"id":148,"loop_ref":37,"next":149,"prev":151,"to":44,"twin":157,"wire_ref":null},{"edge":81,"face":37,"from":44,"geometry_use":{"pcurve":149,"periodic_lift":[0,0],"sense":"Forward"},"id":149,"loop_ref":37,"next":150,"prev":148,"to":45,"twin":154,"wire_ref":null},{"edge":82,"face":37,"from":45,"geometry_use":{"pcurve":150,"periodic_lift":[0,0],"sense":"Reverse"},"id":150,"loop_ref":37,"next":151,"prev":149,"to":41,"twin":160,"wire_ref":null},{"edge":71,"face":37,"from":41,"geometry_use":{"pcurve":151,"periodic_lift":[0,0],"sense":"Reverse"},"id":151,"loop_ref":37,"next":148,"prev":150,"to":40,"twin":125,"wire_ref":null},{"edge":40,"face":38,"from":27,"geometry_use":{"pcurve":152,"periodic_lift":[0,0],"sense":"Forward"},"id":152,"loop_ref":38,"next":153,"prev":155,"to":26,"twin":62,"wire_ref":null},{"edge":83,"face":38,"from":26,"geometry_use":{"pcurve":153,"periodic_lift":[0,0],"sense":"Forward"},"id":153,"loop_ref":38,"next":154,"prev":152,"to":45,"twin":171,"wire_ref":null},{"edge":81,"face":38,"from":45,"geometry_use":{"pcurve":154,"periodic_lift":[0,0],"sense":"Reverse"},"id":154,"loop_ref":38,"next":155,"prev":153,"to":44,"twin":149,"wire_ref":null},{"edge":84,"face":38,"from":44,"geometry_use":{"pcurve":155,"periodic_lift":[0,0],"sense":"Reverse"},"id":155,"loop_ref":38,"next":152,"prev":154,"to":27,"twin":156,"wire_ref":null},{"edge":84,"face":39,"from":27,"geometry_use":{"pcurve":156,"periodic_lift":[0,0],"sense":"Forward"},"id":156,"loop_ref":39,"next":157,"prev":159,"to":44,"twin":155,"wire_ref":null},{"edge":80,"face":39,"from":44,"geometry_use":{"pcurve":157,"periodic_lift":[0,0],"sense":"Reverse"},"id":157,"loop_ref":39,"next":158,"prev":156,"to":40,"twin":148,"wire_ref":null},{"edge":73,"face":39,"from":40,"geometry_use":{"pcurve":158,"periodic_lift":[0,0],"sense":"Reverse"},"id":158,"loop_ref":39,"next":159,"prev":157,"to":16,"twin":128,"wire_ref":null},{"edge":42,"face":39,"from":16,"geometry_use":{"pcurve":159,"periodic_lift":[0,0],"sense":"Forward"},"id":159,"loop_ref":39,"next":156,"prev":158,"to":27,"twin":65,"wire_ref":null},{"edge":82,"face":40,"from":41,"geometry_use":{"pcurve":160,"periodic_lift":[0,0],"sense":"Forward"},"id":160,"loop_ref":40,"next":161,"prev":163,"to":45,"twin":150,"wire_ref":null},{"edge":85,"face":40,"from":45,"geometry_use":{"pcurve":161,"periodic_lift":[0,0],"sense":"Forward"},"id":161,"loop_ref":40,"next":162,"prev":160,"to":46,"twin":170,"wire_ref":null},{"edge":86,"face":40,"from":46,"geometry_use":{"pcurve":162,"periodic_lift":[0,0],"sense":"Reverse"},"id":162,"loop_ref":40,"next":163,"prev":161,"to":42,"twin":172,"wire_ref":null},{"edge":87,"face":40,"from":42,"geometry_use":{"pcurve":163,"periodic_lift":[0,0],"sense":"Reverse"},"id":163,"loop_ref":40,"next":160,"prev":162,"to":41,"twin":164,"wire_ref":null},{"edge":87,"face":41,"from":41,"geometry_use":{"pcurve":164,"periodic_lift":[0,0],"sense":"Forward"},"id":164,"loop_ref":41,"next":165,"prev":167,"to":42,"twin":163,"wire_ref":null},{"edge":78,"face":41,"from":42,"geometry_use":{"pcurve":165,"periodic_lift":[0,0],"sense":"Reverse"},"id":165,"loop_ref":41,"next":166,"prev":164,"to":20,"twin":140,"wire_ref":null},{"edge":29,"face":41,"from":20,"geometry_use":{"pcurve":166,"periodic_lift":[0,0],"sense":"Reverse"},"id":166,"loop_ref":41,"next":167,"prev":165,"to":19,"twin":45,"wire_ref":null},{"edge":74,"face":41,"from":19,"geometry_use":{"pcurve":167,"periodic_lift":[0,0],"sense":"Forward"},"id":167,"loop_ref":41,"next":164,"prev":166,"to":41,"twin":134,"wire_ref":null},{"edge":46,"face":42,"from":26,"geometry_use":{"pcurve":168,"periodic_lift":[0,0],"sense":"Forward"},"id":168,"loop_ref":42,"next":169,"prev":171,"to":29,"twin":74,"wire_ref":null},{"edge":88,"face":42,"from":29,"geometry_use":{"pcurve":169,"periodic_lift":[0,0],"sense":"Forward"},"id":169,"loop_ref":42,"next":170,"prev":168,"to":46,"twin":179,"wire_ref":null},{"edge":85,"face":42,"from":46,"geometry_use":{"pcurve":170,"periodic_lift":[0,0],"sense":"Reverse"},"id":170,"loop_ref":42,"next":171,"prev":169,"to":45,"twin":161,"wire_ref":null},{"edge":83,"face":42,"from":45,"geometry_use":{"pcurve":171,"periodic_lift":[0,0],"sense":"Reverse"},"id":171,"loop_ref":42,"next":168,"prev":170,"to":26,"twin":153,"wire_ref":null},{"edge":86,"face":43,"from":42,"geometry_use":{"pcurve":172,"periodic_lift":[0,0],"sense":"Forward"},"id":172,"loop_ref":43,"next":173,"prev":175,"to":46,"twin":162,"wire_ref":null},{"edge":89,"face":43,"from":46,"geometry_use":{"pcurve":173,"periodic_lift":[0,0],"sense":"Forward"},"id":173,"loop_ref":43,"next":174,"prev":172,"to":47,"twin":178,"wire_ref":null},{"edge":90,"face":43,"from":47,"geometry_use":{"pcurve":174,"periodic_lift":[0,0],"sense":"Reverse"},"id":174,"loop_ref":43,"next":175,"prev":173,"to":43,"twin":181,"wire_ref":null},{"edge":76,"face":43,"from":43,"geometry_use":{"pcurve":175,"periodic_lift":[0,0],"sense":"Reverse"},"id":175,"loop_ref":43,"next":172,"prev":174,"to":42,"twin":137,"wire_ref":null},{"edge":50,"face":44,"from":29,"geometry_use":{"pcurve":176,"periodic_lift":[0,0],"sense":"Forward"},"id":176,"loop_ref":44,"next":177,"prev":179,"to":31,"twin":82,"wire_ref":null},{"edge":91,"face":44,"from":31,"geometry_use":{"pcurve":177,"periodic_lift":[0,0],"sense":"Forward"},"id":177,"loop_ref":44,"next":178,"prev":176,"to":47,"twin":182,"wire_ref":null},{"edge":89,"face":44,"from":47,"geometry_use":{"pcurve":178,"periodic_lift":[0,0],"sense":"Reverse"},"id":178,"loop_ref":44,"next":179,"prev":177,"to":46,"twin":173,"wire_ref":null},{"edge":88,"face":44,"from":46,"geometry_use":{"pcurve":179,"periodic_lift":[0,0],"sense":"Reverse"},"id":179,"loop_ref":44,"next":176,"prev":178,"to":29,"twin":169,"wire_ref":null},{"edge":79,"face":45,"from":23,"geometry_use":{"pcurve":180,"periodic_lift":[0,0],"sense":"Forward"},"id":180,"loop_ref":45,"next":181,"prev":183,"to":43,"twin":146,"wire_ref":null},{"edge":90,"face":45,"from":43,"geometry_use":{"pcurve":181,"periodic_lift":[0,0],"sense":"Forward"},"id":181,"loop_ref":45,"next":182,"prev":180,"to":47,"twin":174,"wire_ref":null},{"edge":91,"face":45,"from":47,"geometry_use":{"pcurve":182,"periodic_lift":[0,0],"sense":"Reverse"},"id":182,"loop_ref":45,"next":183,"prev":181,"to":31,"twin":177,"wire_ref":null},{"edge":51,"face":45,"from":31,"geometry_use":{"pcurve":183,"periodic_lift":[0,0],"sense":"Reverse"},"id":183,"loop_ref":45,"next":180,"prev":182,"to":23,"twin":85,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":48},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":52},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":56},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":60},{"face_ref":16,"id":16,"is_hole":false,"start_halfedge":64},{"face_ref":17,"id":17,"is_hole":false,"start_halfedge":68},{"face_ref":18,"id":18,"is_hole":false,"start_halfedge":72},{"face_ref":19,"id":19,"is_hole":false,"start_halfedge":76},{"face_ref":20,"id":20,"is_hole":false,"start_halfedge":80},{"face_ref":21,"id":21,"is_hole":false,"start_halfedge":84},{"face_ref":22,"id":22,"is_hole":false,"start_halfedge":88},{"face_ref":23,"id":23,"is_hole":false,"start_halfedge":92},{"face_ref":24,"id":24,"is_hole":false,"start_halfedge":96},{"face_ref":25,"id":25,"is_hole":false,"start_halfedge":100},{"face_ref":26,"id":26,"is_hole":false,"start_halfedge":104},{"face_ref":27,"id":27,"is_hole":false,"start_halfedge":108},{"face_ref":28,"id":28,"is_hole":false,"start_halfedge":112},{"face_ref":29,"id":29,"is_hole":false,"start_halfedge":116},{"face_ref":30,"id":30,"is_hole":false,"start_halfedge":120},{"face_ref":31,"id":31,"is_hole":false,"start_halfedge":124},{"face_ref":32,"id":32,"is_hole":false,"start_halfedge":128},{"face_ref":33,"id":33,"is_hole":false,"start_halfedge":132},{"face_ref":34,"id":34,"is_hole":false,"start_halfedge":136},{"face_ref":35,"id":35,"is_hole":false,"start_halfedge":140},{"face_ref":36,"id":36,"is_hole":false,"start_halfedge":144},{"face_ref":37,"id":37,"is_hole":false,"start_halfedge":148},{"face_ref":38,"id":38,"is_hole":false,"start_halfedge":152},{"face_ref":39,"id":39,"is_hole":false,"start_halfedge":156},{"face_ref":40,"id":40,"is_hole":false,"start_halfedge":160},{"face_ref":41,"id":41,"is_hole":false,"start_halfedge":164},{"face_ref":42,"id":42,"is_hole":false,"start_halfedge":168},{"face_ref":43,"id":43,"is_hole":false,"start_halfedge":172},{"face_ref":44,"id":44,"is_hole":false,"start_halfedge":176},{"face_ref":45,"id":45,"is_hole":false,"start_halfedge":180}],"shells":[{"faces":[0,3,8,2,1,5,10,4,14,9,24,23,7,12,6,17,26,15,16,32,22,30,13,19,29,18,25,38,39,31,28,36,21,20,42,27,37,33,34,45,44,40,11,35,41,43],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.5,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":1,"position":[0.0,0.5,-0.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":2,"position":[0.0,0.0,-0.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":3,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[1.0,0.5,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":9,"position":[1.0,0.0,-0.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":12,"position":[0.0,2.5,0.0],"tolerance":1e-8},{"id":8,"outgoing_halfedge":13,"position":[0.0,2.5,-0.5],"tolerance":1e-8},{"id":9,"outgoing_halfedge":17,"position":[1.0,2.5,0.0],"tolerance":1e-8},{"id":10,"outgoing_halfedge":20,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":11,"outgoing_halfedge":21,"position":[0.0,3.0,-0.5],"tolerance":1e-8},{"id":12,"outgoing_halfedge":25,"position":[1.0,3.0,0.0],"tolerance":1e-8},{"id":13,"outgoing_halfedge":30,"position":[1.0,3.0,-0.5],"tolerance":1e-8},{"id":14,"outgoing_halfedge":33,"position":[0.0,0.5,-2.5],"tolerance":1e-8},{"id":15,"outgoing_halfedge":34,"position":[0.0,0.0,-2.5],"tolerance":1e-8},{"id":16,"outgoing_halfedge":37,"position":[1.0,0.0,-2.5],"tolerance":1e-8},{"id":17,"outgoing_halfedge":41,"position":[0.0,2.5,-2.5],"tolerance":1e-8},{"id":18,"outgoing_halfedge":44,"position":[1.0,0.5,-0.5],"tolerance":1e-8},{"id":19,"outgoing_halfedge":45,"position":[1.0,0.5,-2.5],"tolerance":1e-8},{"id":20,"outgoing_halfedge":46,"position":[1.0,2.5,-2.5],"tolerance":1e-8},{"id":21,"outgoing_halfedge":47,"position":[1.0,2.5,-0.5],"tolerance":1e-8},{"id":22,"outgoing_halfedge":49,"position":[0.0,3.0,-2.5],"tolerance":1e-8},{"id":23,"outgoing_halfedge":54,"position":[1.0,3.0,-2.5],"tolerance":1e-8},{"id":24,"outgoing_halfedge":57,"position":[0.0,0.5,-3.0],"tolerance":1e-8},{"id":25,"outgoing_halfedge":58,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":26,"outgoing_halfedge":62,"position":[1.0,0.5,-3.0],"tolerance":1e-8},{"id":27,"outgoing_halfedge":63,"position":[1.0,0.0,-3.0],"tolerance":1e-8},{"id":28,"outgoing_halfedge":69,"position":[0.0,2.5,-3.0],"tolerance":1e-8},{"id":29,"outgoing_halfedge":74,"position":[1.0,2.5,-3.0],"tolerance":1e-8},{"id":30,"outgoing_halfedge":77,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":31,"outgoing_halfedge":82,"position":[1.0,3.0,-3.0],"tolerance":1e-8},{"id":32,"outgoing_halfedge":88,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":33,"outgoing_halfedge":89,"position":[3.0,0.0,-0.5],"tolerance":1e-8},{"id":34,"outgoing_halfedge":90,"position":[3.0,0.5,-0.5],"tolerance":1e-8},{"id":35,"outgoing_halfedge":91,"position":[3.0,0.5,0.0],"tolerance":1e-8},{"id":36,"outgoing_halfedge":102,"position":[3.0,2.5,-0.5],"tolerance":1e-8},{"id":37,"outgoing_halfedge":103,"position":[3.0,2.5,0.0],"tolerance":1e-8},{"id":38,"outgoing_halfedge":114,"position":[3.0,3.0,-0.5],"tolerance":1e-8},{"id":39,"outgoing_halfedge":115,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":40,"outgoing_halfedge":125,"position":[3.0,0.0,-2.5],"tolerance":1e-8},{"id":41,"outgoing_halfedge":126,"position":[3.0,0.5,-2.5],"tolerance":1e-8},{"id":42,"outgoing_halfedge":137,"position":[3.0,2.5,-2.5],"tolerance":1e-8},{"id":43,"outgoing_halfedge":138,"position":[3.0,3.0,-2.5],"tolerance":1e-8},{"id":44,"outgoing_halfedge":149,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":45,"outgoing_halfedge":150,"position":[3.0,0.5,-3.0],"tolerance":1e-8},{"id":46,"outgoing_halfedge":162,"position":[3.0,2.5,-3.0],"tolerance":1e-8},{"id":47,"outgoing_halfedge":174,"position":[3.0,3.0,-3.0],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-union.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-union.json new file mode 100644 index 0000000..8f9a9ab --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/box-union.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-box-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[3.0,3.0,-3.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-2.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,2.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-2.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-box-cutter","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.5,-0.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[3.0,0.5,-0.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[1.0,0.5,-2.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.0,0.5,-2.5],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.0,2.5,-0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[3.0,2.5,-0.5],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[1.0,2.5,-2.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[3.0,2.5,-2.5],"tolerance":1e-8}],"wires":[]}},"handlers":["boolean_boxes"],"operation":"union","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.5,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,2.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.5,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,3.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.5,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.5,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.5,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.5,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.5,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,2.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,3.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.5,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,-2.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,-2.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.5,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.5,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.5,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,-2.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,-3.0]}],"intersections":[],"pcurves":[{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.5]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-box-union","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-8,"twin_halfedge":6},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":90},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":6,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":8,"id":7,"tolerance":1e-8,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-8,"twin_halfedge":95},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":12,"id":9,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-8,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":16,"id":12,"tolerance":1e-8,"twin_halfedge":102},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":17,"id":13,"tolerance":1e-8,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":20,"id":14,"tolerance":1e-8,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":21,"id":15,"tolerance":1e-8,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":23,"id":16,"tolerance":1e-8,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":24,"id":17,"tolerance":1e-8,"twin_halfedge":110},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":25,"id":18,"tolerance":1e-8,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":29,"id":19,"tolerance":1e-8,"twin_halfedge":115},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":30,"id":20,"tolerance":1e-8,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":32,"id":21,"tolerance":1e-8,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":33,"id":22,"tolerance":1e-8,"twin_halfedge":55},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":34,"id":23,"tolerance":1e-8,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":36,"id":24,"tolerance":1e-8,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":37,"id":25,"tolerance":1e-8,"twin_halfedge":123},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":40,"id":26,"tolerance":1e-8,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":41,"id":27,"tolerance":1e-8,"twin_halfedge":67},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":44,"id":28,"tolerance":1e-8,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":45,"id":29,"tolerance":1e-8,"twin_halfedge":75},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":49,"id":30,"tolerance":1e-8,"twin_halfedge":135},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":50,"id":31,"tolerance":1e-8,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":52,"id":32,"tolerance":1e-8,"twin_halfedge":66},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":53,"id":33,"tolerance":1e-8,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":54,"id":34,"tolerance":1e-8,"twin_halfedge":63},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":57,"id":35,"tolerance":1e-8,"twin_halfedge":71},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":58,"id":36,"tolerance":1e-8,"twin_halfedge":140},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":59,"id":37,"tolerance":1e-8,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":61,"id":38,"tolerance":1e-8,"twin_halfedge":147},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":64,"id":39,"tolerance":1e-8,"twin_halfedge":74},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":65,"id":40,"tolerance":1e-8,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":69,"id":41,"tolerance":1e-8,"twin_halfedge":79},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":70,"id":42,"tolerance":1e-8,"twin_halfedge":152},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":72,"id":43,"tolerance":1e-8,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":44,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":73,"id":44,"tolerance":1e-8,"twin_halfedge":76},{"chart_seam":false,"geometry":{"curve":45,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":77,"id":45,"tolerance":1e-8,"twin_halfedge":82},{"chart_seam":false,"geometry":{"curve":46,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":78,"id":46,"tolerance":1e-8,"twin_halfedge":160},{"chart_seam":false,"geometry":{"curve":47,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":81,"id":47,"tolerance":1e-8,"twin_halfedge":167},{"chart_seam":false,"geometry":{"curve":48,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":84,"id":48,"tolerance":1e-8,"twin_halfedge":93},{"chart_seam":false,"geometry":{"curve":49,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":85,"id":49,"tolerance":1e-8,"twin_halfedge":119},{"chart_seam":false,"geometry":{"curve":50,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":86,"id":50,"tolerance":1e-8,"twin_halfedge":96},{"chart_seam":false,"geometry":{"curve":51,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":87,"id":51,"tolerance":1e-8,"twin_halfedge":88},{"chart_seam":false,"geometry":{"curve":52,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":89,"id":52,"tolerance":1e-8,"twin_halfedge":103},{"chart_seam":false,"geometry":{"curve":53,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":91,"id":53,"tolerance":1e-8,"twin_halfedge":94},{"chart_seam":false,"geometry":{"curve":54,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":92,"id":54,"tolerance":1e-8,"twin_halfedge":122},{"chart_seam":false,"geometry":{"curve":55,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":97,"id":55,"tolerance":1e-8,"twin_halfedge":127},{"chart_seam":false,"geometry":{"curve":56,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":98,"id":56,"tolerance":1e-8,"twin_halfedge":104},{"chart_seam":false,"geometry":{"curve":57,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":99,"id":57,"tolerance":1e-8,"twin_halfedge":100},{"chart_seam":false,"geometry":{"curve":58,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":101,"id":58,"tolerance":1e-8,"twin_halfedge":111},{"chart_seam":false,"geometry":{"curve":59,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":105,"id":59,"tolerance":1e-8,"twin_halfedge":131},{"chart_seam":false,"geometry":{"curve":60,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":106,"id":60,"tolerance":1e-8,"twin_halfedge":113},{"chart_seam":false,"geometry":{"curve":61,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":107,"id":61,"tolerance":1e-8,"twin_halfedge":108},{"chart_seam":false,"geometry":{"curve":62,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":109,"id":62,"tolerance":1e-8,"twin_halfedge":112},{"chart_seam":false,"geometry":{"curve":63,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":114,"id":63,"tolerance":1e-8,"twin_halfedge":132},{"chart_seam":false,"geometry":{"curve":64,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":116,"id":64,"tolerance":1e-8,"twin_halfedge":121},{"chart_seam":false,"geometry":{"curve":65,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":117,"id":65,"tolerance":1e-8,"twin_halfedge":139},{"chart_seam":false,"geometry":{"curve":66,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":118,"id":66,"tolerance":1e-8,"twin_halfedge":124},{"chart_seam":false,"geometry":{"curve":67,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":120,"id":67,"tolerance":1e-8,"twin_halfedge":146},{"chart_seam":false,"geometry":{"curve":68,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":125,"id":68,"tolerance":1e-8,"twin_halfedge":151},{"chart_seam":false,"geometry":{"curve":69,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":126,"id":69,"tolerance":1e-8,"twin_halfedge":128},{"chart_seam":false,"geometry":{"curve":70,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":129,"id":70,"tolerance":1e-8,"twin_halfedge":159},{"chart_seam":false,"geometry":{"curve":71,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":130,"id":71,"tolerance":1e-8,"twin_halfedge":133},{"chart_seam":false,"geometry":{"curve":72,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":134,"id":72,"tolerance":1e-8,"twin_halfedge":164},{"chart_seam":false,"geometry":{"curve":73,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":136,"id":73,"tolerance":1e-8,"twin_halfedge":145},{"chart_seam":false,"geometry":{"curve":74,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":137,"id":74,"tolerance":1e-8,"twin_halfedge":142},{"chart_seam":false,"geometry":{"curve":75,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":138,"id":75,"tolerance":1e-8,"twin_halfedge":148},{"chart_seam":false,"geometry":{"curve":76,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":141,"id":76,"tolerance":1e-8,"twin_halfedge":155},{"chart_seam":false,"geometry":{"curve":77,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":143,"id":77,"tolerance":1e-8,"twin_halfedge":144},{"chart_seam":false,"geometry":{"curve":78,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":149,"id":78,"tolerance":1e-8,"twin_halfedge":154},{"chart_seam":false,"geometry":{"curve":79,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":150,"id":79,"tolerance":1e-8,"twin_halfedge":156},{"chart_seam":false,"geometry":{"curve":80,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":153,"id":80,"tolerance":1e-8,"twin_halfedge":163},{"chart_seam":false,"geometry":{"curve":81,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":157,"id":81,"tolerance":1e-8,"twin_halfedge":162},{"chart_seam":false,"geometry":{"curve":82,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":158,"id":82,"tolerance":1e-8,"twin_halfedge":165},{"chart_seam":false,"geometry":{"curve":83,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":161,"id":83,"tolerance":1e-8,"twin_halfedge":166}],"faces":[{"id":0,"key":"left-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"front-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":2,"key":"bottom-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":3,"key":"left-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":4,"key":"front-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.50000001,"lo":0.49999999}]}},{"id":5,"key":"left-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":6,"key":"front-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":7,"key":"top-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":8,"key":"left-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.50000001,"lo":0.49999999}]}},{"id":9,"key":"bottom-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":10,"key":"left-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":11,"key":"left-11","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":12,"key":"top-12","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.50000001,"lo":0.49999999}]}},{"id":13,"key":"left-13","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":14,"key":"back-14","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":15,"key":"bottom-15","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":16,"key":"left-16","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[],"outer":16,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":17,"key":"back-17","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":17,"trim":{"chart":0,"holes":[],"outer":17,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":18,"key":"left-18","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":18,"trim":{"chart":0,"holes":[],"outer":18,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":19,"key":"back-19","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":19,"trim":{"chart":0,"holes":[],"outer":19,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":20,"key":"top-20","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":20,"trim":{"chart":0,"holes":[],"outer":20,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":21,"key":"right-21","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":21,"trim":{"chart":0,"holes":[],"outer":21,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":22,"key":"front-22","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":22,"trim":{"chart":0,"holes":[],"outer":22,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":23,"key":"bottom-23","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":23,"trim":{"chart":0,"holes":[],"outer":23,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.99999999}]}},{"id":24,"key":"right-24","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":24,"trim":{"chart":0,"holes":[],"outer":24,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":2.50000001,"lo":0.49999999}]}},{"id":25,"key":"front-25","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":25,"trim":{"chart":0,"holes":[],"outer":25,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":26,"key":"right-26","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":26,"trim":{"chart":0,"holes":[],"outer":26,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":27,"key":"front-27","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":27,"trim":{"chart":0,"holes":[],"outer":27,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":28,"key":"top-28","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":28,"trim":{"chart":0,"holes":[],"outer":28,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":29,"key":"right-29","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":29,"trim":{"chart":0,"holes":[],"outer":29,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":30,"key":"bottom-30","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":30,"trim":{"chart":0,"holes":[],"outer":30,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":3.00000001,"lo":0.99999999}]}},{"id":31,"key":"right-31","provenance":{"reversed":false,"role":"Coincident","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"},{"body":"matrix-box-cutter","entity":"matrix-box-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":31,"trim":{"chart":0,"holes":[],"outer":31,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":32,"key":"right-32","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":32,"trim":{"chart":0,"holes":[],"outer":32,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":33,"key":"top-33","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":33,"trim":{"chart":0,"holes":[],"outer":33,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":34,"key":"right-34","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":34,"trim":{"chart":0,"holes":[],"outer":34,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":35,"key":"back-35","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":35,"trim":{"chart":0,"holes":[],"outer":35,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.99999999}]}},{"id":36,"key":"bottom-36","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":36,"trim":{"chart":0,"holes":[],"outer":36,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":0.99999999}]}},{"id":37,"key":"right-37","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":37,"trim":{"chart":0,"holes":[],"outer":37,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":2.50000001,"lo":0.49999999}]}},{"id":38,"key":"back-38","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":38,"trim":{"chart":0,"holes":[],"outer":38,"uv_bounds":[{"hi":2.50000001,"lo":0.49999999},{"hi":3.00000001,"lo":0.99999999}]}},{"id":39,"key":"right-39","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":39,"trim":{"chart":0,"holes":[],"outer":39,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":40,"key":"back-40","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":40,"trim":{"chart":0,"holes":[],"outer":40,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":0.99999999}]}},{"id":41,"key":"top-41","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":41,"trim":{"chart":0,"holes":[],"outer":41,"uv_bounds":[{"hi":3.00000001,"lo":0.99999999},{"hi":3.00000001,"lo":2.49999999}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":14,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":35,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":11,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":6,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":90,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":0,"twin":19,"wire_ref":null},{"edge":3,"face":1,"from":0,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":3,"twin":3,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":10,"wire_ref":null},{"edge":7,"face":2,"from":2,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":6,"twin":38,"wire_ref":null},{"edge":8,"face":2,"from":6,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":4,"twin":95,"wire_ref":null},{"edge":6,"face":2,"from":4,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":7,"wire_ref":null},{"edge":2,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":2,"twin":2,"wire_ref":null},{"edge":9,"face":3,"from":7,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":8,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":8,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":1,"twin":43,"wire_ref":null},{"edge":0,"face":3,"from":1,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":0,"twin":0,"wire_ref":null},{"edge":11,"face":3,"from":0,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":7,"twin":18,"wire_ref":null},{"edge":12,"face":4,"from":5,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":9,"twin":102,"wire_ref":null},{"edge":13,"face":4,"from":9,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":27,"wire_ref":null},{"edge":11,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":0,"twin":15,"wire_ref":null},{"edge":5,"face":4,"from":0,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":5,"twin":5,"wire_ref":null},{"edge":14,"face":5,"from":10,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":11,"twin":31,"wire_ref":null},{"edge":15,"face":5,"from":11,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Reverse"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":8,"twin":47,"wire_ref":null},{"edge":9,"face":5,"from":8,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":12,"wire_ref":null},{"edge":16,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":10,"twin":26,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":12,"twin":110,"wire_ref":null},{"edge":18,"face":6,"from":12,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":10,"twin":28,"wire_ref":null},{"edge":16,"face":6,"from":10,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":7,"twin":23,"wire_ref":null},{"edge":13,"face":6,"from":7,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":9,"twin":17,"wire_ref":null},{"edge":18,"face":7,"from":10,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":12,"twin":25,"wire_ref":null},{"edge":19,"face":7,"from":12,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":13,"twin":115,"wire_ref":null},{"edge":20,"face":7,"from":13,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":11,"twin":48,"wire_ref":null},{"edge":14,"face":7,"from":11,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":10,"twin":20,"wire_ref":null},{"edge":21,"face":8,"from":1,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":14,"twin":42,"wire_ref":null},{"edge":22,"face":8,"from":14,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":15,"twin":55,"wire_ref":null},{"edge":23,"face":8,"from":15,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":2,"twin":39,"wire_ref":null},{"edge":1,"face":8,"from":2,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Forward"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":1,"twin":1,"wire_ref":null},{"edge":24,"face":9,"from":15,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":16,"twin":62,"wire_ref":null},{"edge":25,"face":9,"from":16,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":6,"twin":123,"wire_ref":null},{"edge":7,"face":9,"from":6,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":2,"twin":8,"wire_ref":null},{"edge":23,"face":9,"from":2,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":15,"twin":34,"wire_ref":null},{"edge":26,"face":10,"from":8,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":17,"twin":46,"wire_ref":null},{"edge":27,"face":10,"from":17,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Reverse"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":14,"twin":67,"wire_ref":null},{"edge":21,"face":10,"from":14,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":1,"twin":32,"wire_ref":null},{"edge":10,"face":10,"from":1,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Forward"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":8,"twin":13,"wire_ref":null},{"edge":28,"face":11,"from":11,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":18,"twin":51,"wire_ref":null},{"edge":29,"face":11,"from":18,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Reverse"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":17,"twin":75,"wire_ref":null},{"edge":26,"face":11,"from":17,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":8,"twin":40,"wire_ref":null},{"edge":15,"face":11,"from":8,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Forward"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":11,"twin":21,"wire_ref":null},{"edge":20,"face":12,"from":11,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":12,"next":49,"prev":51,"to":13,"twin":30,"wire_ref":null},{"edge":30,"face":12,"from":13,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":12,"next":50,"prev":48,"to":19,"twin":135,"wire_ref":null},{"edge":31,"face":12,"from":19,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":12,"next":51,"prev":49,"to":18,"twin":80,"wire_ref":null},{"edge":28,"face":12,"from":18,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":12,"next":48,"prev":50,"to":11,"twin":44,"wire_ref":null},{"edge":32,"face":13,"from":14,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":13,"next":53,"prev":55,"to":20,"twin":66,"wire_ref":null},{"edge":33,"face":13,"from":20,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Reverse"},"id":53,"loop_ref":13,"next":54,"prev":52,"to":21,"twin":56,"wire_ref":null},{"edge":34,"face":13,"from":21,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":13,"next":55,"prev":53,"to":15,"twin":63,"wire_ref":null},{"edge":22,"face":13,"from":15,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Forward"},"id":55,"loop_ref":13,"next":52,"prev":54,"to":14,"twin":33,"wire_ref":null},{"edge":33,"face":14,"from":21,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":14,"next":57,"prev":59,"to":20,"twin":53,"wire_ref":null},{"edge":35,"face":14,"from":20,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Forward"},"id":57,"loop_ref":14,"next":58,"prev":56,"to":22,"twin":71,"wire_ref":null},{"edge":36,"face":14,"from":22,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":14,"next":59,"prev":57,"to":23,"twin":140,"wire_ref":null},{"edge":37,"face":14,"from":23,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Reverse"},"id":59,"loop_ref":14,"next":56,"prev":58,"to":21,"twin":60,"wire_ref":null},{"edge":37,"face":15,"from":21,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Forward"},"id":60,"loop_ref":15,"next":61,"prev":63,"to":23,"twin":59,"wire_ref":null},{"edge":38,"face":15,"from":23,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Reverse"},"id":61,"loop_ref":15,"next":62,"prev":60,"to":16,"twin":147,"wire_ref":null},{"edge":24,"face":15,"from":16,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":15,"next":63,"prev":61,"to":15,"twin":36,"wire_ref":null},{"edge":34,"face":15,"from":15,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Forward"},"id":63,"loop_ref":15,"next":60,"prev":62,"to":21,"twin":54,"wire_ref":null},{"edge":39,"face":16,"from":17,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Forward"},"id":64,"loop_ref":16,"next":65,"prev":67,"to":24,"twin":74,"wire_ref":null},{"edge":40,"face":16,"from":24,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Reverse"},"id":65,"loop_ref":16,"next":66,"prev":64,"to":20,"twin":68,"wire_ref":null},{"edge":32,"face":16,"from":20,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Reverse"},"id":66,"loop_ref":16,"next":67,"prev":65,"to":14,"twin":52,"wire_ref":null},{"edge":27,"face":16,"from":14,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Forward"},"id":67,"loop_ref":16,"next":64,"prev":66,"to":17,"twin":41,"wire_ref":null},{"edge":40,"face":17,"from":20,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":17,"next":69,"prev":71,"to":24,"twin":65,"wire_ref":null},{"edge":41,"face":17,"from":24,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Forward"},"id":69,"loop_ref":17,"next":70,"prev":68,"to":25,"twin":79,"wire_ref":null},{"edge":42,"face":17,"from":25,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":17,"next":71,"prev":69,"to":22,"twin":152,"wire_ref":null},{"edge":35,"face":17,"from":22,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Reverse"},"id":71,"loop_ref":17,"next":68,"prev":70,"to":20,"twin":57,"wire_ref":null},{"edge":43,"face":18,"from":18,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Forward"},"id":72,"loop_ref":18,"next":73,"prev":75,"to":26,"twin":83,"wire_ref":null},{"edge":44,"face":18,"from":26,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Reverse"},"id":73,"loop_ref":18,"next":74,"prev":72,"to":24,"twin":76,"wire_ref":null},{"edge":39,"face":18,"from":24,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":18,"next":75,"prev":73,"to":17,"twin":64,"wire_ref":null},{"edge":29,"face":18,"from":17,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Forward"},"id":75,"loop_ref":18,"next":72,"prev":74,"to":18,"twin":45,"wire_ref":null},{"edge":44,"face":19,"from":24,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Forward"},"id":76,"loop_ref":19,"next":77,"prev":79,"to":26,"twin":73,"wire_ref":null},{"edge":45,"face":19,"from":26,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Forward"},"id":77,"loop_ref":19,"next":78,"prev":76,"to":27,"twin":82,"wire_ref":null},{"edge":46,"face":19,"from":27,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":19,"next":79,"prev":77,"to":25,"twin":160,"wire_ref":null},{"edge":41,"face":19,"from":25,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Reverse"},"id":79,"loop_ref":19,"next":76,"prev":78,"to":24,"twin":69,"wire_ref":null},{"edge":31,"face":20,"from":18,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Forward"},"id":80,"loop_ref":20,"next":81,"prev":83,"to":19,"twin":50,"wire_ref":null},{"edge":47,"face":20,"from":19,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Forward"},"id":81,"loop_ref":20,"next":82,"prev":80,"to":27,"twin":167,"wire_ref":null},{"edge":45,"face":20,"from":27,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":20,"next":83,"prev":81,"to":26,"twin":77,"wire_ref":null},{"edge":43,"face":20,"from":26,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":20,"next":80,"prev":82,"to":18,"twin":72,"wire_ref":null},{"edge":48,"face":21,"from":28,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Forward"},"id":84,"loop_ref":21,"next":85,"prev":87,"to":29,"twin":93,"wire_ref":null},{"edge":49,"face":21,"from":29,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Forward"},"id":85,"loop_ref":21,"next":86,"prev":84,"to":30,"twin":119,"wire_ref":null},{"edge":50,"face":21,"from":30,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Reverse"},"id":86,"loop_ref":21,"next":87,"prev":85,"to":31,"twin":96,"wire_ref":null},{"edge":51,"face":21,"from":31,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Reverse"},"id":87,"loop_ref":21,"next":84,"prev":86,"to":28,"twin":88,"wire_ref":null},{"edge":51,"face":22,"from":28,"geometry_use":{"pcurve":88,"periodic_lift":[0,0],"sense":"Forward"},"id":88,"loop_ref":22,"next":89,"prev":91,"to":31,"twin":87,"wire_ref":null},{"edge":52,"face":22,"from":31,"geometry_use":{"pcurve":89,"periodic_lift":[0,0],"sense":"Reverse"},"id":89,"loop_ref":22,"next":90,"prev":88,"to":5,"twin":103,"wire_ref":null},{"edge":4,"face":22,"from":5,"geometry_use":{"pcurve":90,"periodic_lift":[0,0],"sense":"Reverse"},"id":90,"loop_ref":22,"next":91,"prev":89,"to":4,"twin":4,"wire_ref":null},{"edge":53,"face":22,"from":4,"geometry_use":{"pcurve":91,"periodic_lift":[0,0],"sense":"Forward"},"id":91,"loop_ref":22,"next":88,"prev":90,"to":28,"twin":94,"wire_ref":null},{"edge":54,"face":23,"from":6,"geometry_use":{"pcurve":92,"periodic_lift":[0,0],"sense":"Forward"},"id":92,"loop_ref":23,"next":93,"prev":95,"to":29,"twin":122,"wire_ref":null},{"edge":48,"face":23,"from":29,"geometry_use":{"pcurve":93,"periodic_lift":[0,0],"sense":"Reverse"},"id":93,"loop_ref":23,"next":94,"prev":92,"to":28,"twin":84,"wire_ref":null},{"edge":53,"face":23,"from":28,"geometry_use":{"pcurve":94,"periodic_lift":[0,0],"sense":"Reverse"},"id":94,"loop_ref":23,"next":95,"prev":93,"to":4,"twin":91,"wire_ref":null},{"edge":8,"face":23,"from":4,"geometry_use":{"pcurve":95,"periodic_lift":[0,0],"sense":"Forward"},"id":95,"loop_ref":23,"next":92,"prev":94,"to":6,"twin":9,"wire_ref":null},{"edge":50,"face":24,"from":31,"geometry_use":{"pcurve":96,"periodic_lift":[0,0],"sense":"Forward"},"id":96,"loop_ref":24,"next":97,"prev":99,"to":30,"twin":86,"wire_ref":null},{"edge":55,"face":24,"from":30,"geometry_use":{"pcurve":97,"periodic_lift":[0,0],"sense":"Forward"},"id":97,"loop_ref":24,"next":98,"prev":96,"to":32,"twin":127,"wire_ref":null},{"edge":56,"face":24,"from":32,"geometry_use":{"pcurve":98,"periodic_lift":[0,0],"sense":"Reverse"},"id":98,"loop_ref":24,"next":99,"prev":97,"to":33,"twin":104,"wire_ref":null},{"edge":57,"face":24,"from":33,"geometry_use":{"pcurve":99,"periodic_lift":[0,0],"sense":"Reverse"},"id":99,"loop_ref":24,"next":96,"prev":98,"to":31,"twin":100,"wire_ref":null},{"edge":57,"face":25,"from":31,"geometry_use":{"pcurve":100,"periodic_lift":[0,0],"sense":"Forward"},"id":100,"loop_ref":25,"next":101,"prev":103,"to":33,"twin":99,"wire_ref":null},{"edge":58,"face":25,"from":33,"geometry_use":{"pcurve":101,"periodic_lift":[0,0],"sense":"Reverse"},"id":101,"loop_ref":25,"next":102,"prev":100,"to":9,"twin":111,"wire_ref":null},{"edge":12,"face":25,"from":9,"geometry_use":{"pcurve":102,"periodic_lift":[0,0],"sense":"Reverse"},"id":102,"loop_ref":25,"next":103,"prev":101,"to":5,"twin":16,"wire_ref":null},{"edge":52,"face":25,"from":5,"geometry_use":{"pcurve":103,"periodic_lift":[0,0],"sense":"Forward"},"id":103,"loop_ref":25,"next":100,"prev":102,"to":31,"twin":89,"wire_ref":null},{"edge":56,"face":26,"from":33,"geometry_use":{"pcurve":104,"periodic_lift":[0,0],"sense":"Forward"},"id":104,"loop_ref":26,"next":105,"prev":107,"to":32,"twin":98,"wire_ref":null},{"edge":59,"face":26,"from":32,"geometry_use":{"pcurve":105,"periodic_lift":[0,0],"sense":"Forward"},"id":105,"loop_ref":26,"next":106,"prev":104,"to":34,"twin":131,"wire_ref":null},{"edge":60,"face":26,"from":34,"geometry_use":{"pcurve":106,"periodic_lift":[0,0],"sense":"Reverse"},"id":106,"loop_ref":26,"next":107,"prev":105,"to":35,"twin":113,"wire_ref":null},{"edge":61,"face":26,"from":35,"geometry_use":{"pcurve":107,"periodic_lift":[0,0],"sense":"Reverse"},"id":107,"loop_ref":26,"next":104,"prev":106,"to":33,"twin":108,"wire_ref":null},{"edge":61,"face":27,"from":33,"geometry_use":{"pcurve":108,"periodic_lift":[0,0],"sense":"Forward"},"id":108,"loop_ref":27,"next":109,"prev":111,"to":35,"twin":107,"wire_ref":null},{"edge":62,"face":27,"from":35,"geometry_use":{"pcurve":109,"periodic_lift":[0,0],"sense":"Reverse"},"id":109,"loop_ref":27,"next":110,"prev":108,"to":12,"twin":112,"wire_ref":null},{"edge":17,"face":27,"from":12,"geometry_use":{"pcurve":110,"periodic_lift":[0,0],"sense":"Reverse"},"id":110,"loop_ref":27,"next":111,"prev":109,"to":9,"twin":24,"wire_ref":null},{"edge":58,"face":27,"from":9,"geometry_use":{"pcurve":111,"periodic_lift":[0,0],"sense":"Forward"},"id":111,"loop_ref":27,"next":108,"prev":110,"to":33,"twin":101,"wire_ref":null},{"edge":62,"face":28,"from":12,"geometry_use":{"pcurve":112,"periodic_lift":[0,0],"sense":"Forward"},"id":112,"loop_ref":28,"next":113,"prev":115,"to":35,"twin":109,"wire_ref":null},{"edge":60,"face":28,"from":35,"geometry_use":{"pcurve":113,"periodic_lift":[0,0],"sense":"Forward"},"id":113,"loop_ref":28,"next":114,"prev":112,"to":34,"twin":106,"wire_ref":null},{"edge":63,"face":28,"from":34,"geometry_use":{"pcurve":114,"periodic_lift":[0,0],"sense":"Reverse"},"id":114,"loop_ref":28,"next":115,"prev":113,"to":13,"twin":132,"wire_ref":null},{"edge":19,"face":28,"from":13,"geometry_use":{"pcurve":115,"periodic_lift":[0,0],"sense":"Reverse"},"id":115,"loop_ref":28,"next":112,"prev":114,"to":12,"twin":29,"wire_ref":null},{"edge":64,"face":29,"from":29,"geometry_use":{"pcurve":116,"periodic_lift":[0,0],"sense":"Forward"},"id":116,"loop_ref":29,"next":117,"prev":119,"to":36,"twin":121,"wire_ref":null},{"edge":65,"face":29,"from":36,"geometry_use":{"pcurve":117,"periodic_lift":[0,0],"sense":"Forward"},"id":117,"loop_ref":29,"next":118,"prev":116,"to":37,"twin":139,"wire_ref":null},{"edge":66,"face":29,"from":37,"geometry_use":{"pcurve":118,"periodic_lift":[0,0],"sense":"Reverse"},"id":118,"loop_ref":29,"next":119,"prev":117,"to":30,"twin":124,"wire_ref":null},{"edge":49,"face":29,"from":30,"geometry_use":{"pcurve":119,"periodic_lift":[0,0],"sense":"Reverse"},"id":119,"loop_ref":29,"next":116,"prev":118,"to":29,"twin":85,"wire_ref":null},{"edge":67,"face":30,"from":16,"geometry_use":{"pcurve":120,"periodic_lift":[0,0],"sense":"Forward"},"id":120,"loop_ref":30,"next":121,"prev":123,"to":36,"twin":146,"wire_ref":null},{"edge":64,"face":30,"from":36,"geometry_use":{"pcurve":121,"periodic_lift":[0,0],"sense":"Reverse"},"id":121,"loop_ref":30,"next":122,"prev":120,"to":29,"twin":116,"wire_ref":null},{"edge":54,"face":30,"from":29,"geometry_use":{"pcurve":122,"periodic_lift":[0,0],"sense":"Reverse"},"id":122,"loop_ref":30,"next":123,"prev":121,"to":6,"twin":92,"wire_ref":null},{"edge":25,"face":30,"from":6,"geometry_use":{"pcurve":123,"periodic_lift":[0,0],"sense":"Forward"},"id":123,"loop_ref":30,"next":120,"prev":122,"to":16,"twin":37,"wire_ref":null},{"edge":66,"face":31,"from":30,"geometry_use":{"pcurve":124,"periodic_lift":[0,0],"sense":"Forward"},"id":124,"loop_ref":31,"next":125,"prev":127,"to":37,"twin":118,"wire_ref":null},{"edge":68,"face":31,"from":37,"geometry_use":{"pcurve":125,"periodic_lift":[0,0],"sense":"Forward"},"id":125,"loop_ref":31,"next":126,"prev":124,"to":38,"twin":151,"wire_ref":null},{"edge":69,"face":31,"from":38,"geometry_use":{"pcurve":126,"periodic_lift":[0,0],"sense":"Reverse"},"id":126,"loop_ref":31,"next":127,"prev":125,"to":32,"twin":128,"wire_ref":null},{"edge":55,"face":31,"from":32,"geometry_use":{"pcurve":127,"periodic_lift":[0,0],"sense":"Reverse"},"id":127,"loop_ref":31,"next":124,"prev":126,"to":30,"twin":97,"wire_ref":null},{"edge":69,"face":32,"from":32,"geometry_use":{"pcurve":128,"periodic_lift":[0,0],"sense":"Forward"},"id":128,"loop_ref":32,"next":129,"prev":131,"to":38,"twin":126,"wire_ref":null},{"edge":70,"face":32,"from":38,"geometry_use":{"pcurve":129,"periodic_lift":[0,0],"sense":"Forward"},"id":129,"loop_ref":32,"next":130,"prev":128,"to":39,"twin":159,"wire_ref":null},{"edge":71,"face":32,"from":39,"geometry_use":{"pcurve":130,"periodic_lift":[0,0],"sense":"Reverse"},"id":130,"loop_ref":32,"next":131,"prev":129,"to":34,"twin":133,"wire_ref":null},{"edge":59,"face":32,"from":34,"geometry_use":{"pcurve":131,"periodic_lift":[0,0],"sense":"Reverse"},"id":131,"loop_ref":32,"next":128,"prev":130,"to":32,"twin":105,"wire_ref":null},{"edge":63,"face":33,"from":13,"geometry_use":{"pcurve":132,"periodic_lift":[0,0],"sense":"Forward"},"id":132,"loop_ref":33,"next":133,"prev":135,"to":34,"twin":114,"wire_ref":null},{"edge":71,"face":33,"from":34,"geometry_use":{"pcurve":133,"periodic_lift":[0,0],"sense":"Forward"},"id":133,"loop_ref":33,"next":134,"prev":132,"to":39,"twin":130,"wire_ref":null},{"edge":72,"face":33,"from":39,"geometry_use":{"pcurve":134,"periodic_lift":[0,0],"sense":"Reverse"},"id":134,"loop_ref":33,"next":135,"prev":133,"to":19,"twin":164,"wire_ref":null},{"edge":30,"face":33,"from":19,"geometry_use":{"pcurve":135,"periodic_lift":[0,0],"sense":"Reverse"},"id":135,"loop_ref":33,"next":132,"prev":134,"to":13,"twin":49,"wire_ref":null},{"edge":73,"face":34,"from":36,"geometry_use":{"pcurve":136,"periodic_lift":[0,0],"sense":"Forward"},"id":136,"loop_ref":34,"next":137,"prev":139,"to":40,"twin":145,"wire_ref":null},{"edge":74,"face":34,"from":40,"geometry_use":{"pcurve":137,"periodic_lift":[0,0],"sense":"Forward"},"id":137,"loop_ref":34,"next":138,"prev":136,"to":41,"twin":142,"wire_ref":null},{"edge":75,"face":34,"from":41,"geometry_use":{"pcurve":138,"periodic_lift":[0,0],"sense":"Reverse"},"id":138,"loop_ref":34,"next":139,"prev":137,"to":37,"twin":148,"wire_ref":null},{"edge":65,"face":34,"from":37,"geometry_use":{"pcurve":139,"periodic_lift":[0,0],"sense":"Reverse"},"id":139,"loop_ref":34,"next":136,"prev":138,"to":36,"twin":117,"wire_ref":null},{"edge":36,"face":35,"from":23,"geometry_use":{"pcurve":140,"periodic_lift":[0,0],"sense":"Forward"},"id":140,"loop_ref":35,"next":141,"prev":143,"to":22,"twin":58,"wire_ref":null},{"edge":76,"face":35,"from":22,"geometry_use":{"pcurve":141,"periodic_lift":[0,0],"sense":"Forward"},"id":141,"loop_ref":35,"next":142,"prev":140,"to":41,"twin":155,"wire_ref":null},{"edge":74,"face":35,"from":41,"geometry_use":{"pcurve":142,"periodic_lift":[0,0],"sense":"Reverse"},"id":142,"loop_ref":35,"next":143,"prev":141,"to":40,"twin":137,"wire_ref":null},{"edge":77,"face":35,"from":40,"geometry_use":{"pcurve":143,"periodic_lift":[0,0],"sense":"Reverse"},"id":143,"loop_ref":35,"next":140,"prev":142,"to":23,"twin":144,"wire_ref":null},{"edge":77,"face":36,"from":23,"geometry_use":{"pcurve":144,"periodic_lift":[0,0],"sense":"Forward"},"id":144,"loop_ref":36,"next":145,"prev":147,"to":40,"twin":143,"wire_ref":null},{"edge":73,"face":36,"from":40,"geometry_use":{"pcurve":145,"periodic_lift":[0,0],"sense":"Reverse"},"id":145,"loop_ref":36,"next":146,"prev":144,"to":36,"twin":136,"wire_ref":null},{"edge":67,"face":36,"from":36,"geometry_use":{"pcurve":146,"periodic_lift":[0,0],"sense":"Reverse"},"id":146,"loop_ref":36,"next":147,"prev":145,"to":16,"twin":120,"wire_ref":null},{"edge":38,"face":36,"from":16,"geometry_use":{"pcurve":147,"periodic_lift":[0,0],"sense":"Forward"},"id":147,"loop_ref":36,"next":144,"prev":146,"to":23,"twin":61,"wire_ref":null},{"edge":75,"face":37,"from":37,"geometry_use":{"pcurve":148,"periodic_lift":[0,0],"sense":"Forward"},"id":148,"loop_ref":37,"next":149,"prev":151,"to":41,"twin":138,"wire_ref":null},{"edge":78,"face":37,"from":41,"geometry_use":{"pcurve":149,"periodic_lift":[0,0],"sense":"Forward"},"id":149,"loop_ref":37,"next":150,"prev":148,"to":42,"twin":154,"wire_ref":null},{"edge":79,"face":37,"from":42,"geometry_use":{"pcurve":150,"periodic_lift":[0,0],"sense":"Reverse"},"id":150,"loop_ref":37,"next":151,"prev":149,"to":38,"twin":156,"wire_ref":null},{"edge":68,"face":37,"from":38,"geometry_use":{"pcurve":151,"periodic_lift":[0,0],"sense":"Reverse"},"id":151,"loop_ref":37,"next":148,"prev":150,"to":37,"twin":125,"wire_ref":null},{"edge":42,"face":38,"from":22,"geometry_use":{"pcurve":152,"periodic_lift":[0,0],"sense":"Forward"},"id":152,"loop_ref":38,"next":153,"prev":155,"to":25,"twin":70,"wire_ref":null},{"edge":80,"face":38,"from":25,"geometry_use":{"pcurve":153,"periodic_lift":[0,0],"sense":"Forward"},"id":153,"loop_ref":38,"next":154,"prev":152,"to":42,"twin":163,"wire_ref":null},{"edge":78,"face":38,"from":42,"geometry_use":{"pcurve":154,"periodic_lift":[0,0],"sense":"Reverse"},"id":154,"loop_ref":38,"next":155,"prev":153,"to":41,"twin":149,"wire_ref":null},{"edge":76,"face":38,"from":41,"geometry_use":{"pcurve":155,"periodic_lift":[0,0],"sense":"Reverse"},"id":155,"loop_ref":38,"next":152,"prev":154,"to":22,"twin":141,"wire_ref":null},{"edge":79,"face":39,"from":38,"geometry_use":{"pcurve":156,"periodic_lift":[0,0],"sense":"Forward"},"id":156,"loop_ref":39,"next":157,"prev":159,"to":42,"twin":150,"wire_ref":null},{"edge":81,"face":39,"from":42,"geometry_use":{"pcurve":157,"periodic_lift":[0,0],"sense":"Forward"},"id":157,"loop_ref":39,"next":158,"prev":156,"to":43,"twin":162,"wire_ref":null},{"edge":82,"face":39,"from":43,"geometry_use":{"pcurve":158,"periodic_lift":[0,0],"sense":"Reverse"},"id":158,"loop_ref":39,"next":159,"prev":157,"to":39,"twin":165,"wire_ref":null},{"edge":70,"face":39,"from":39,"geometry_use":{"pcurve":159,"periodic_lift":[0,0],"sense":"Reverse"},"id":159,"loop_ref":39,"next":156,"prev":158,"to":38,"twin":129,"wire_ref":null},{"edge":46,"face":40,"from":25,"geometry_use":{"pcurve":160,"periodic_lift":[0,0],"sense":"Forward"},"id":160,"loop_ref":40,"next":161,"prev":163,"to":27,"twin":78,"wire_ref":null},{"edge":83,"face":40,"from":27,"geometry_use":{"pcurve":161,"periodic_lift":[0,0],"sense":"Forward"},"id":161,"loop_ref":40,"next":162,"prev":160,"to":43,"twin":166,"wire_ref":null},{"edge":81,"face":40,"from":43,"geometry_use":{"pcurve":162,"periodic_lift":[0,0],"sense":"Reverse"},"id":162,"loop_ref":40,"next":163,"prev":161,"to":42,"twin":157,"wire_ref":null},{"edge":80,"face":40,"from":42,"geometry_use":{"pcurve":163,"periodic_lift":[0,0],"sense":"Reverse"},"id":163,"loop_ref":40,"next":160,"prev":162,"to":25,"twin":153,"wire_ref":null},{"edge":72,"face":41,"from":19,"geometry_use":{"pcurve":164,"periodic_lift":[0,0],"sense":"Forward"},"id":164,"loop_ref":41,"next":165,"prev":167,"to":39,"twin":134,"wire_ref":null},{"edge":82,"face":41,"from":39,"geometry_use":{"pcurve":165,"periodic_lift":[0,0],"sense":"Forward"},"id":165,"loop_ref":41,"next":166,"prev":164,"to":43,"twin":158,"wire_ref":null},{"edge":83,"face":41,"from":43,"geometry_use":{"pcurve":166,"periodic_lift":[0,0],"sense":"Reverse"},"id":166,"loop_ref":41,"next":167,"prev":165,"to":27,"twin":161,"wire_ref":null},{"edge":47,"face":41,"from":27,"geometry_use":{"pcurve":167,"periodic_lift":[0,0],"sense":"Reverse"},"id":167,"loop_ref":41,"next":164,"prev":166,"to":19,"twin":81,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":48},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":52},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":56},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":60},{"face_ref":16,"id":16,"is_hole":false,"start_halfedge":64},{"face_ref":17,"id":17,"is_hole":false,"start_halfedge":68},{"face_ref":18,"id":18,"is_hole":false,"start_halfedge":72},{"face_ref":19,"id":19,"is_hole":false,"start_halfedge":76},{"face_ref":20,"id":20,"is_hole":false,"start_halfedge":80},{"face_ref":21,"id":21,"is_hole":false,"start_halfedge":84},{"face_ref":22,"id":22,"is_hole":false,"start_halfedge":88},{"face_ref":23,"id":23,"is_hole":false,"start_halfedge":92},{"face_ref":24,"id":24,"is_hole":false,"start_halfedge":96},{"face_ref":25,"id":25,"is_hole":false,"start_halfedge":100},{"face_ref":26,"id":26,"is_hole":false,"start_halfedge":104},{"face_ref":27,"id":27,"is_hole":false,"start_halfedge":108},{"face_ref":28,"id":28,"is_hole":false,"start_halfedge":112},{"face_ref":29,"id":29,"is_hole":false,"start_halfedge":116},{"face_ref":30,"id":30,"is_hole":false,"start_halfedge":120},{"face_ref":31,"id":31,"is_hole":false,"start_halfedge":124},{"face_ref":32,"id":32,"is_hole":false,"start_halfedge":128},{"face_ref":33,"id":33,"is_hole":false,"start_halfedge":132},{"face_ref":34,"id":34,"is_hole":false,"start_halfedge":136},{"face_ref":35,"id":35,"is_hole":false,"start_halfedge":140},{"face_ref":36,"id":36,"is_hole":false,"start_halfedge":144},{"face_ref":37,"id":37,"is_hole":false,"start_halfedge":148},{"face_ref":38,"id":38,"is_hole":false,"start_halfedge":152},{"face_ref":39,"id":39,"is_hole":false,"start_halfedge":156},{"face_ref":40,"id":40,"is_hole":false,"start_halfedge":160},{"face_ref":41,"id":41,"is_hole":false,"start_halfedge":164}],"shells":[{"faces":[0,3,8,2,1,5,10,4,13,9,23,22,7,11,6,16,25,14,15,30,21,28,12,18,27,17,24,35,36,29,26,33,20,19,38,31,34,32,41,40,37,39],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.5,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":1,"position":[0.0,0.5,-0.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":2,"position":[0.0,0.0,-0.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":3,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[1.0,0.5,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":9,"position":[1.0,0.0,-0.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":12,"position":[0.0,2.5,0.0],"tolerance":1e-8},{"id":8,"outgoing_halfedge":13,"position":[0.0,2.5,-0.5],"tolerance":1e-8},{"id":9,"outgoing_halfedge":17,"position":[1.0,2.5,0.0],"tolerance":1e-8},{"id":10,"outgoing_halfedge":20,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":11,"outgoing_halfedge":21,"position":[0.0,3.0,-0.5],"tolerance":1e-8},{"id":12,"outgoing_halfedge":25,"position":[1.0,3.0,0.0],"tolerance":1e-8},{"id":13,"outgoing_halfedge":30,"position":[1.0,3.0,-0.5],"tolerance":1e-8},{"id":14,"outgoing_halfedge":33,"position":[0.0,0.5,-2.5],"tolerance":1e-8},{"id":15,"outgoing_halfedge":34,"position":[0.0,0.0,-2.5],"tolerance":1e-8},{"id":16,"outgoing_halfedge":37,"position":[1.0,0.0,-2.5],"tolerance":1e-8},{"id":17,"outgoing_halfedge":41,"position":[0.0,2.5,-2.5],"tolerance":1e-8},{"id":18,"outgoing_halfedge":45,"position":[0.0,3.0,-2.5],"tolerance":1e-8},{"id":19,"outgoing_halfedge":50,"position":[1.0,3.0,-2.5],"tolerance":1e-8},{"id":20,"outgoing_halfedge":53,"position":[0.0,0.5,-3.0],"tolerance":1e-8},{"id":21,"outgoing_halfedge":54,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":22,"outgoing_halfedge":58,"position":[1.0,0.5,-3.0],"tolerance":1e-8},{"id":23,"outgoing_halfedge":59,"position":[1.0,0.0,-3.0],"tolerance":1e-8},{"id":24,"outgoing_halfedge":65,"position":[0.0,2.5,-3.0],"tolerance":1e-8},{"id":25,"outgoing_halfedge":70,"position":[1.0,2.5,-3.0],"tolerance":1e-8},{"id":26,"outgoing_halfedge":73,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":27,"outgoing_halfedge":78,"position":[1.0,3.0,-3.0],"tolerance":1e-8},{"id":28,"outgoing_halfedge":84,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":29,"outgoing_halfedge":85,"position":[3.0,0.0,-0.5],"tolerance":1e-8},{"id":30,"outgoing_halfedge":86,"position":[3.0,0.5,-0.5],"tolerance":1e-8},{"id":31,"outgoing_halfedge":87,"position":[3.0,0.5,0.0],"tolerance":1e-8},{"id":32,"outgoing_halfedge":98,"position":[3.0,2.5,-0.5],"tolerance":1e-8},{"id":33,"outgoing_halfedge":99,"position":[3.0,2.5,0.0],"tolerance":1e-8},{"id":34,"outgoing_halfedge":106,"position":[3.0,3.0,-0.5],"tolerance":1e-8},{"id":35,"outgoing_halfedge":107,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":36,"outgoing_halfedge":117,"position":[3.0,0.0,-2.5],"tolerance":1e-8},{"id":37,"outgoing_halfedge":118,"position":[3.0,0.5,-2.5],"tolerance":1e-8},{"id":38,"outgoing_halfedge":126,"position":[3.0,2.5,-2.5],"tolerance":1e-8},{"id":39,"outgoing_halfedge":130,"position":[3.0,3.0,-2.5],"tolerance":1e-8},{"id":40,"outgoing_halfedge":137,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":41,"outgoing_halfedge":138,"position":[3.0,0.5,-3.0],"tolerance":1e-8},{"id":42,"outgoing_halfedge":150,"position":[3.0,2.5,-3.0],"tolerance":1e-8},{"id":43,"outgoing_halfedge":158,"position":[3.0,3.0,-3.0],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/cone-contained.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cone-contained.json new file mode 100644 index 0000000..3af03ed --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cone-contained.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Circle","radius":1.0},{"direction":[0.31622776601683794,-0.9486832980505138,0.0],"kind":"Line","origin":[0.0,3.0,0.0]}],"intersections":[],"pcurves":[{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,0.9486832980505138],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,0.9486832980505138],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Cone","semi_angle":0.3217505543966422},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"}]},"id":"matrix-cone-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":true,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.1622776601683795,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cone-host","entity":"matrix-cone-host","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cone-host","entity":"matrix-cone-host","face":1,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":1,"face":1,"from":1,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4}],"shells":[{"faces":[0,1],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,0.0,0.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.5,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Circle","radius":0.2},{"direction":[0.19611613513818402,-0.9805806756909201,0.0],"kind":"Line","origin":[0.0,1.5,0.0]}],"intersections":[],"pcurves":[{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,0.9805806756909201],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,0.9805806756909201],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.2,0.0],"axis_b":[0.0,0.2],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,1.5,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Cone","semi_angle":0.19739555984988078},{"frame":{"origin":[0.0,0.5,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"}]},"id":"matrix-cone-inner","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":true,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.019803902718557,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cone-inner","entity":"matrix-cone-inner","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.0,"lo":0.0}]}},{"id":1,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cone-inner","entity":"matrix-cone-inner","face":1,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.2,"lo":-0.2},{"hi":0.2,"lo":-0.2}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":1,"face":1,"from":1,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4}],"shells":[{"faces":[0,1],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,1.5,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[0.2,0.5,0.0],"tolerance":1e-8}],"wires":[]}},"handlers":["conic_containment_boolean"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Circle","radius":1.0},{"direction":[0.31622776601683794,-0.9486832980505138,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"frame":{"origin":[0.0,0.5,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Circle","radius":0.2},{"direction":[0.19611613513818402,-0.9805806756909201,0.0],"kind":"Line","origin":[0.0,1.5,0.0]}],"intersections":[],"pcurves":[{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,0.9486832980505138],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,0.9486832980505138],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]},{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,0.9805806756909201],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,0.9805806756909201],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.2,0.0],"axis_b":[0.0,0.2],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Cone","semi_angle":0.3217505543966422},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,1.5,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Cone","semi_angle":0.19739555984988078},{"frame":{"origin":[0.0,0.5,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"}]},"id":"matrix-cone-contained","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[1],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":true,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.1622776601683795,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3},{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":2},"halfedge":5,"id":3,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":7,"id":4,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":true,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.019803902718557,"lo":0.0}},"halfedge":6,"id":5,"tolerance":1e-8,"twin_halfedge":8}],"faces":[{"id":0,"key":"matrix-cone-host:lateral","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-cone-host","entity":"matrix-cone-host","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"matrix-cone-host:upper_cap","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-cone-host","entity":"matrix-cone-host","face":1,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":2,"key":"matrix-cone-inner:lateral","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-cone-inner","entity":"matrix-cone-inner","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":1,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.0,"lo":0.0}]}},{"id":3,"key":"matrix-cone-inner:upper_cap","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-cone-inner","entity":"matrix-cone-inner","face":1,"key":"upper_cap"}]},"sense":"Reverse","shell_ref":1,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.2,"lo":-0.2},{"hi":0.2,"lo":-0.2}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":1,"face":1,"from":1,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":1,"twin":2,"wire_ref":null},{"edge":3,"face":2,"from":2,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":2,"next":8,"prev":6,"to":2,"twin":null,"wire_ref":null},{"edge":5,"face":2,"from":3,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":2,"next":5,"prev":7,"to":2,"twin":8,"wire_ref":null},{"edge":4,"face":2,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":2,"next":6,"prev":8,"to":3,"twin":9,"wire_ref":null},{"edge":5,"face":2,"from":2,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":7,"prev":5,"to":3,"twin":6,"wire_ref":null},{"edge":4,"face":3,"from":3,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":3,"next":9,"prev":9,"to":3,"twin":7,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":9}],"shells":[{"faces":[0,1],"id":0,"is_closed":true},{"faces":[2,3],"id":1,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":5,"position":[0.0,1.5,0.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":6,"position":[0.2,0.5,0.0],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-coaxial.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-coaxial.json new file mode 100644 index 0000000..b2f1c0e --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-coaxial.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,3.0,0.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-inner","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-inner","entity":"matrix-cylinder-inner","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-inner","entity":"matrix-cylinder-inner","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-inner","entity":"matrix-cylinder-inner","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.4,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[0.4,3.0,0.0],"tolerance":1e-8}],"wires":[]}},"handlers":["boolean_cylinders"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-coaxial","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":4,"id":2,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":true,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":3},{"chart_seam":true,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":7}],"faces":[{"id":0,"key":"matrix-cylinder-coaxial:annular:outer","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"matrix-cylinder-coaxial:annular:inner","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-cylinder-inner","entity":"matrix-cylinder-inner","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":2,"key":"matrix-cylinder-coaxial:annular:lower_cap","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[3],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":3,"key":"matrix-cylinder-coaxial:annular:upper_cap","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[5],"outer":4,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":8,"wire_ref":null},{"edge":4,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":2,"face":1,"from":2,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":2,"twin":9,"wire_ref":null},{"edge":5,"face":1,"from":2,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":3,"twin":7,"wire_ref":null},{"edge":3,"face":1,"from":3,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":3,"twin":11,"wire_ref":null},{"edge":5,"face":1,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":2,"twin":5,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":8,"prev":8,"to":0,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":2,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":3,"next":9,"prev":9,"to":2,"twin":4,"wire_ref":null},{"edge":1,"face":3,"from":1,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":4,"next":10,"prev":10,"to":1,"twin":2,"wire_ref":null},{"edge":3,"face":3,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":5,"next":11,"prev":11,"to":3,"twin":6,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":2,"id":3,"is_hole":true,"start_halfedge":9},{"face_ref":3,"id":4,"is_hole":false,"start_halfedge":10},{"face_ref":3,"id":5,"is_hole":true,"start_halfedge":11}],"shells":[{"faces":[0,1,2,3],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,3.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":4,"position":[0.4,0.0,0.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":6,"position":[0.4,3.0,0.0],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-cross.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-cross.json new file mode 100644 index 0000000..5a4d7fa --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-cross.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,3.0,0.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[-2.0,1.5,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Circle","radius":0.35},{"frame":{"origin":[2.0,1.5,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Circle","radius":0.35},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,1.85,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,4.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.35,0.0],"axis_b":[0.0,-0.35],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.35,0.0],"axis_b":[0.0,0.35],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-2.0,1.5,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Cylinder","radius":0.35},{"frame":{"origin":[-2.0,1.5,0.0],"x":[0.0,1.0,0.0],"y":[-0.0,-0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,1.5,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-cross","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-cross","entity":"matrix-cylinder-cross","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":4.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-cross","entity":"matrix-cylinder-cross","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.35,"lo":-0.35},{"hi":0.35,"lo":-0.35}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-cross","entity":"matrix-cylinder-cross","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.35,"lo":-0.35},{"hi":0.35,"lo":-0.35}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-2.0,1.85,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[2.0,1.85,0.0],"tolerance":1e-8}],"wires":[]}},"handlers":["boolean_cylinders"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[-3.8285686989269494e-16,0.0,-1.0],"y":[-1.0,-0.0,3.8285686989269494e-16],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[-3.8285686989269494e-16,0.0,-1.0],"y":[-1.0,-0.0,3.8285686989269494e-16],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-3.8285686989269494e-16,0.0,-1.0]},{"definition":0,"kind":"Intersection"},{"definition":1,"kind":"Intersection"},{"frame":{"origin":[-0.9569403357322088,1.5,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Circle","radius":0.35},{"direction":[1.0,3.0842315809709978e-12,2.1675364304853405e-13],"kind":"Line","origin":[-0.9569403357322088,1.1508614485586484,-0.024541228523336397]}],"intersections":[{"anchors":[{"parameter":0.0,"point":[0.9996988186962041,1.1508614485616657,-0.024541228523124386],"uv_a":[4.736932672990859,1.150861448564683],"uv_b":[3.211768032612946,2.999698818696204]},{"parameter":0.024558190104960534,"point":[1.0,1.1499999999970896,2.143131898507868e-17],"uv_a":[4.71238898038469,1.1499999999941792],"uv_b":[3.141592653589793,3.0]},{"parameter":0.04911638020992118,"point":[0.9996988186962041,1.1508614485616657,0.024541228523124542],"uv_a":[4.687845287778519,1.150861448564683],"uv_b":[3.07141727456664,2.999698818696204]},{"parameter":0.07379627394027383,"point":[0.9987954562051725,1.1534565491339372,0.04906767432701331],"uv_a":[4.663301595172349,1.1534565491310786],"uv_b":[3.000935957765032,2.9987954562051726]},{"parameter":0.09872393435240052,"point":[0.9972904566786901,1.157818388887034,0.0735645635992434],"uv_a":[4.638757902566178,1.1578183888850617],"uv_b":[2.9298290153999136,2.99729045667869]},{"parameter":0.12403473760749835,"point":[0.995184726672197,1.1640049997369677,0.0980171403295086],"uv_a":[4.614214209960009,1.1640049997367896],"uv_b":[2.8577475311166456,2.995184726672197]},{"parameter":0.14921866244604126,"point":[0.9925552960028878,1.1718750000010847,0.12179484544315389],"uv_a":[4.590290989827579,1.171875],"uv_b":[2.7861714518817577,2.992555296002888]},{"parameter":0.14988012977841397,"point":[0.9924795345987101,1.172104244311274,0.12241067519858889],"uv_a":[4.589670517353838,1.172104244309594],"uv_b":[2.78429398209447,2.99247953459871]},{"parameter":0.17643452712788407,"point":[0.9891765099647811,1.182241966485856,0.14673047445705703],"uv_a":[4.565126824747669,1.1822419664895278],"uv_b":[2.708995709797488,2.989176509964781]},{"parameter":0.20391119692860835,"point":[0.9852776423889411,1.194595297040236,0.17096188876118545],"uv_a":[4.540583132141498,1.1945952970418148],"uv_b":[2.631265732563743,2.985277642388941]},{"parameter":0.23258138838563674,"point":[0.9807852804032304,1.20941478659058,0.19509032201822168],"uv_a":[4.5160394395353265,1.2094147865936975],"uv_b":[2.5503406587258515,2.9807852804032304]},{"parameter":0.24900315329041633,"point":[0.9780601016817911,1.2187499999989695,0.20832291640269104],"uv_a":[4.50252903873862,1.21875],"uv_b":[2.5040677714515702,2.978060101681791]},{"parameter":0.2628175307928904,"point":[0.9757021300385285,1.22706292563281,0.21910124015532842],"uv_a":[4.491495746929157,1.2270629256308894],"uv_b":[2.46517489373359,2.9757021300385285]},{"parameter":0.2951337011890277,"point":[0.9700312531945436,1.2480860619690073,0.24298017990319415],"uv_a":[4.466952054322986,1.2480860619689338],"uv_b":[2.374244387292552,2.9700312531945436]},{"parameter":0.3199255532655499,"point":[0.9656249999997297,1.2656250000002496,0.25993914552329],"uv_a":[4.449429798827856,1.265625],"uv_b":[2.3045241281462254,2.9656249999997297]},{"parameter":0.3303743745597719,"point":[0.9637760657954395,1.2733630546545385,0.2667127574780769],"uv_a":[4.442408361716815,1.2733630546572385],"uv_b":[2.275140401081142,2.9637760657954395]},{"parameter":0.3699919873848242,"point":[0.9569403357322086,1.3044627755307163,0.29028467725314433],"uv_a":[4.417864669110646,1.304462775529828],"uv_b":[2.1635870564670467,2.9569403357322086]},{"parameter":0.3797287548133279,"point":[0.955330440216331,1.3124999999984968,0.2955397604341453],"uv_a":[4.412368512379372,1.3125],"uv_b":[2.1361497570424026,2.955330440216331]},{"parameter":0.41718019299945497,"point":[0.9495281805930365,1.3447461248599197,0.3136817403948525],"uv_a":[4.393320976504475,1.3447461248579202],"uv_b":[2.030388348874558,2.9495281805930365]},{"parameter":0.43348334630504237,"point":[0.9472462143608684,1.359375000001149,0.32050680082850824],"uv_a":[4.386124516034441,1.359375],"uv_b":[1.984262377205745,2.9472462143608684]},{"parameter":0.48239215925865175,"point":[0.9415440651830204,1.4051041271316915,0.3368898533878881],"uv_a":[4.368777283898305,1.4051041271304712],"uv_b":[1.8453642451252028,2.9415440651830203]},{"parameter":0.4835875810143991,"point":[0.9414292657975234,1.4062499999999878,0.33721052400535423],"uv_a":[4.368436683630938,1.40625],"uv_b":[1.841964538488531,2.9414292657975234]},{"parameter":0.5315711904729243,"point":[0.9379217801206198,1.453125000000035,0.34684684570453755],"uv_a":[4.358181827549307,1.453125],"uv_b":[1.7051285412163706,2.93792178012062]},{"parameter":0.578566741106693,"point":[0.9367496997605852,1.5,0.34999999999889553],"uv_a":[4.354817876741537,1.5],"uv_b":[1.5707963267948966,2.9367496997605853]},{"parameter":0.6255622917404617,"point":[0.9379217801206198,1.546874999999965,0.34684684570453755],"uv_a":[4.358181827549307,1.546875],"uv_b":[1.4364641123734225,2.93792178012062]},{"parameter":0.6735459011989872,"point":[0.9414292657975234,1.5937500000000124,0.33721052400535423],"uv_a":[4.368436683630938,1.59375],"uv_b":[1.299628115101262,2.9414292657975234]},{"parameter":0.6747413229547343,"point":[0.9415440651830204,1.5948958728683085,0.3368898533878881],"uv_a":[4.368777283898305,1.5948958728695288],"uv_b":[1.2962284084645903,2.9415440651830203]},{"parameter":0.7236501359083437,"point":[0.9472462143608684,1.640624999998851,0.32050680082850824],"uv_a":[4.386124516034441,1.640625],"uv_b":[1.157330276384048,2.9472462143608684]},{"parameter":0.7399532892139311,"point":[0.9495281805930365,1.6552538751400803,0.3136817403948525],"uv_a":[4.393320976504475,1.6552538751420798],"uv_b":[1.111204304715236,2.9495281805930365]},{"parameter":0.7774047274000582,"point":[0.955330440216331,1.6875000000015032,0.2955397604341453],"uv_a":[4.412368512379372,1.6875],"uv_b":[1.0054428965473905,2.955330440216331]},{"parameter":0.7871414948285617,"point":[0.9569403357322086,1.6955372244692835,0.29028467725314433],"uv_a":[4.417864669110646,1.695537224470172],"uv_b":[0.9780055971227464,2.9569403357322086]},{"parameter":0.8267591076536142,"point":[0.9637760657954395,1.7266369453454615,0.2667127574780769],"uv_a":[4.442408361716815,1.7266369453427615],"uv_b":[0.8664522525086511,2.9637760657954395]},{"parameter":0.837207928947836,"point":[0.9656249999997297,1.7343749999997502,0.25993914552329],"uv_a":[4.449429798827856,1.734375],"uv_b":[0.8370685254435681,2.9656249999997297]},{"parameter":0.8619997810243584,"point":[0.9700312531945436,1.7519139380309927,0.24298017990319415],"uv_a":[4.466952054322986,1.7519139380310662],"uv_b":[0.7673482662972413,2.9700312531945436]},{"parameter":0.8943159514204957,"point":[0.9757021300385285,1.77293707436719,0.21910124015532842],"uv_a":[4.491495746929157,1.7729370743691106],"uv_b":[0.6764177598562036,2.9757021300385285]},{"parameter":0.9081303289229697,"point":[0.9780601016817911,1.7812500000010305,0.20832291640269104],"uv_a":[4.50252903873862,1.78125],"uv_b":[0.6375248821382229,2.978060101681791]},{"parameter":0.9245520938277493,"point":[0.9807852804032304,1.79058521340942,0.19509032201822168],"uv_a":[4.5160394395353265,1.7905852134063025],"uv_b":[0.5912519948639421,2.9807852804032304]},{"parameter":0.9532222852847777,"point":[0.9852776423889411,1.805404702959764,0.17096188876118548],"uv_a":[4.540583132141498,1.8054047029581852],"uv_b":[0.5103269210260502,2.985277642388941]},{"parameter":0.980698955085502,"point":[0.9891765099647811,1.817758033514144,0.14673047445705703],"uv_a":[4.565126824747669,1.8177580335104722],"uv_b":[0.43259694379230496,2.989176509964781]},{"parameter":1.007253352434972,"point":[0.9924795345987101,1.827895755688726,0.12241067519858889],"uv_a":[4.589670517353838,1.827895755690406],"uv_b":[0.35729867149532346,2.99247953459871]},{"parameter":1.0079148197673446,"point":[0.9925552960028878,1.8281249999989153,0.12179484544315389],"uv_a":[4.590290989827579,1.828125],"uv_b":[0.3554212017080358,2.992555296002888]},{"parameter":1.0330987446058875,"point":[0.995184726672197,1.8359950002630323,0.09801714032950859],"uv_a":[4.614214209960009,1.8359950002632104],"uv_b":[0.2838451224731475,2.995184726672197]},{"parameter":1.0584095478609854,"point":[0.9972904566786901,1.842181611112966,0.07356456359924338],"uv_a":[4.638757902566178,1.8421816111149383],"uv_b":[0.21176363818987998,2.99729045667869]},{"parameter":1.083337208273112,"point":[0.9987954562051725,1.8465434508660628,0.049067674327013325],"uv_a":[4.663301595172349,1.8465434508689214],"uv_b":[0.14065669582476126,2.9987954562051726]},{"parameter":1.1080171020034648,"point":[0.9996988186962041,1.8491385514383343,0.024541228523124542],"uv_a":[4.687845287778519,1.849138551435317],"uv_b":[0.07017537902315318,2.999698818696204]},{"parameter":1.1325752921084253,"point":[1.0,1.8500000000029104,2.4492935982743395e-16],"uv_a":[4.71238898038469,1.8500000000058208],"uv_b":[8.881784197001252e-16,3.0]},{"parameter":1.157133482213386,"point":[0.9996988186962041,1.8491385514383343,-0.024541228523124382],"uv_a":[4.736932672990859,1.849138551435317],"uv_b":[-0.07017537902315318,2.999698818696204]},{"parameter":1.1818133759437388,"point":[0.9987954562051725,1.8465434508660628,-0.049067674327013255],"uv_a":[4.76147636559703,1.8465434508689214],"uv_b":[-0.14065669582476126,2.9987954562051726]},{"parameter":1.2067410363558655,"point":[0.9972904566786901,1.842181611112966,-0.0735645635992434],"uv_a":[4.7860200582032,1.8421816111149383],"uv_b":[-0.21176363818987998,2.99729045667869]},{"parameter":1.2320518396109634,"point":[0.995184726672197,1.8359950002630323,-0.0980171403295087],"uv_a":[4.81056375080937,1.8359950002632104],"uv_b":[-0.2838451224731475,2.995184726672197]},{"parameter":1.2572357644495067,"point":[0.9925552960028878,1.828124999998915,-0.12179484544315419],"uv_a":[4.8344869709418,1.828125],"uv_b":[-0.3554212017080367,2.992555296002888]},{"parameter":1.257897231781879,"point":[0.9924795345987101,1.827895755688726,-0.12241067519858903],"uv_a":[4.8351074434155406,1.827895755690406],"uv_b":[-0.35729867149532346,2.99247953459871]},{"parameter":1.2844516291313492,"point":[0.9891765099647811,1.817758033514144,-0.14673047445705725],"uv_a":[4.859651136021711,1.8177580335104722],"uv_b":[-0.43259694379230584,2.989176509964781]},{"parameter":1.3119282989320729,"point":[0.9852776423889411,1.805404702959764,-0.170961888761185],"uv_a":[4.884194828627881,1.8054047029581852],"uv_b":[-0.5103269210260493,2.985277642388941]},{"parameter":1.3405984903891013,"point":[0.9807852804032304,1.79058521340942,-0.1950903220182213],"uv_a":[4.908738521234051,1.7905852134063025],"uv_b":[-0.5912519948639403,2.9807852804032304]},{"parameter":1.357020255293882,"point":[0.9780601016817907,1.7812500000010303,-0.20832291640269177],"uv_a":[4.92224892203076,1.78125],"uv_b":[-0.6375248821382247,2.9780601016817907]},{"parameter":1.3708346327963548,"point":[0.9757021300385285,1.7729370743671902,-0.21910124015532814],"uv_a":[4.933282213840221,1.7729370743691106],"uv_b":[-0.6764177598562027,2.9757021300385285]},{"parameter":1.4031508031924915,"point":[0.9700312531945441,1.751913938030993,-0.24298017990319326],"uv_a":[4.957825906446391,1.7519139380310662],"uv_b":[-0.7673482662972386,2.970031253194544]},{"parameter":1.4279426552690144,"point":[0.9656249999997297,1.7343749999997504,-0.25993914552328967],"uv_a":[4.975348161941523,1.734375],"uv_b":[-0.8370685254435672,2.9656249999997297]},{"parameter":1.438391476563236,"point":[0.96377606579544,1.7266369453454617,-0.2667127574780761],"uv_a":[4.9823695990525625,1.7266369453427615],"uv_b":[-0.8664522525086493,2.96377606579544]},{"parameter":1.4780090893882885,"point":[0.9569403357322087,1.695537224469284,-0.29028467725314366],"uv_a":[5.006913291658732,1.695537224470172],"uv_b":[-0.9780055971227446,2.9569403357322086]},{"parameter":1.4877458568167925,"point":[0.955330440216331,1.6875000000015032,-0.295539760434145],"uv_a":[5.012409448390007,1.6875],"uv_b":[-1.0054428965473896,2.955330440216331]},{"parameter":1.5251972950029193,"point":[0.9495281805930366,1.6552538751400805,-0.313681740394852],"uv_a":[5.031456984264903,1.6552538751420798],"uv_b":[-1.1112043047152351,2.9495281805930365]},{"parameter":1.5415004483085073,"point":[0.9472462143608684,1.640624999998851,-0.3205068008285083],"uv_a":[5.038653444734939,1.640625],"uv_b":[-1.157330276384048,2.9472462143608684]},{"parameter":1.5904092612621166,"point":[0.9415440651830207,1.5948958728683085,-0.3368898533878877],"uv_a":[5.056000676871073,1.5948958728695288],"uv_b":[-1.2962284084645894,2.9415440651830207]},{"parameter":1.5916046830178638,"point":[0.9414292657975237,1.5937500000000124,-0.33721052400535406],"uv_a":[5.056341277138441,1.59375],"uv_b":[-1.299628115101262,2.941429265797524]},{"parameter":1.6395882924763898,"point":[0.937921780120619,1.5468749999999647,-0.3468468457045383],"uv_a":[5.066596133220074,1.546875],"uv_b":[-1.4364641123734234,2.937921780120619]},{"parameter":1.6865838431101583,"point":[0.9367496997605852,1.5,-0.3499999999988954],"uv_a":[5.069960084027841,1.5],"uv_b":[-1.5707963267948966,2.9367496997605853]},{"parameter":1.7335793937439268,"point":[0.937921780120619,1.4531250000000353,-0.3468468457045383],"uv_a":[5.066596133220074,1.453125],"uv_b":[-1.7051285412163697,2.937921780120619]},{"parameter":1.7815630032024528,"point":[0.9414292657975237,1.4062499999999876,-0.33721052400535406],"uv_a":[5.056341277138441,1.40625],"uv_b":[-1.841964538488531,2.941429265797524]},{"parameter":1.7827584249582,"point":[0.9415440651830207,1.4051041271316915,-0.3368898533878877],"uv_a":[5.056000676871073,1.4051041271304712],"uv_b":[-1.8453642451252037,2.9415440651830207]},{"parameter":1.8316672379118093,"point":[0.9472462143608684,1.359375000001149,-0.3205068008285083],"uv_a":[5.038653444734939,1.359375],"uv_b":[-1.984262377205745,2.9472462143608684]},{"parameter":1.8479703912173973,"point":[0.9495281805930366,1.3447461248599195,-0.313681740394852],"uv_a":[5.031456984264903,1.3447461248579202],"uv_b":[-2.030388348874558,2.9495281805930365]},{"parameter":1.885421829403524,"point":[0.955330440216331,1.3124999999984968,-0.295539760434145],"uv_a":[5.012409448390007,1.3125],"uv_b":[-2.1361497570424035,2.955330440216331]},{"parameter":1.8951585968320281,"point":[0.9569403357322087,1.304462775530716,-0.2902846772531437],"uv_a":[5.006913291658732,1.304462775529828],"uv_b":[-2.1635870564670476,2.9569403357322086]},{"parameter":1.9347762096570806,"point":[0.96377606579544,1.2733630546545383,-0.26671275747807616],"uv_a":[4.9823695990525625,1.2733630546572385],"uv_b":[-2.275140401081144,2.96377606579544]},{"parameter":1.9452250309513022,"point":[0.9656249999997297,1.2656250000002496,-0.25993914552328967],"uv_a":[4.975348161941523,1.265625],"uv_b":[-2.3045241281462268,2.9656249999997297]},{"parameter":1.970016883027825,"point":[0.9700312531945441,1.248086061969007,-0.2429801799031933],"uv_a":[4.957825906446391,1.2480860619689338],"uv_b":[-2.3742443872925545,2.970031253194544]},{"parameter":2.002333053423962,"point":[0.9757021300385285,1.2270629256328098,-0.21910124015532811],"uv_a":[4.933282213840221,1.2270629256308894],"uv_b":[-2.4651748937335913,2.9757021300385285]},{"parameter":2.0161474309264347,"point":[0.9780601016817907,1.2187499999989697,-0.2083229164026918],"uv_a":[4.92224892203076,1.21875],"uv_b":[-2.5040677714515684,2.9780601016817907]},{"parameter":2.0325691958312153,"point":[0.9807852804032304,1.20941478659058,-0.19509032201822127],"uv_a":[4.908738521234051,1.2094147865936975],"uv_b":[-2.550340658725853,2.9807852804032304]},{"parameter":2.0612393872882437,"point":[0.9852776423889411,1.194595297040236,-0.170961888761185],"uv_a":[4.884194828627881,1.1945952970418148],"uv_b":[-2.631265732563744,2.985277642388941]},{"parameter":2.0887160570889676,"point":[0.9891765099647811,1.182241966485856,-0.14673047445705723],"uv_a":[4.859651136021711,1.1822419664895278],"uv_b":[-2.708995709797488,2.989176509964781]},{"parameter":2.1152704544384378,"point":[0.9924795345987101,1.172104244311274,-0.12241067519858906],"uv_a":[4.8351074434155406,1.172104244309594],"uv_b":[-2.7842939820944697,2.99247953459871]},{"parameter":2.11593192177081,"point":[0.9925552960028878,1.171875000001085,-0.1217948454431542],"uv_a":[4.8344869709418,1.171875],"uv_b":[-2.7861714518817564,2.992555296002888]},{"parameter":2.1411158466093534,"point":[0.995184726672197,1.1640049997369677,-0.09801714032950865],"uv_a":[4.81056375080937,1.1640049997367896],"uv_b":[-2.8577475311166456,2.995184726672197]},{"parameter":2.166426649864451,"point":[0.9972904566786901,1.157818388887034,-0.0735645635992434],"uv_a":[4.7860200582032,1.1578183888850617],"uv_b":[-2.929829015399914,2.99729045667869]},{"parameter":2.191354310276578,"point":[0.9987954562051725,1.1534565491339372,-0.04906767432701327],"uv_a":[4.76147636559703,1.1534565491310786],"uv_b":[-3.000935957765032,2.9987954562051726]},{"parameter":2.2160342040069305,"point":[0.9996988186962041,1.1508614485616657,-0.024541228523124386],"uv_a":[4.736932672990859,1.150861448564683],"uv_b":[-3.07141727456664,2.999698818696204]}],"residual_tolerance":1e-9,"surfaces":[0,3],"uv_tubes":[[{"hi":4.738159857621167,"lo":4.71116179575438},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.3521187906592513,"lo":3.0012418955434876},{"hi":3.0006023626075917,"lo":2.9990964560886124}],[{"hi":4.713616165014997,"lo":4.68661810314821},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.2819434116360995,"lo":2.9310665165203336},{"hi":3.0006023626075917,"lo":2.9990964560886124}],[{"hi":4.689072472408827,"lo":4.66207441054204},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.212379908169856,"lo":2.8599733241618157},{"hi":3.001505543678267,"lo":2.9969887312231096}],[{"hi":4.664528779802657,"lo":4.63753071793587},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.1431498424952684,"lo":2.787615130669677},{"hi":3.001805455258138,"lo":2.9942804576257247}],[{"hi":4.639985087196487,"lo":4.6129870253297},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.0739919839664496,"lo":2.7135845625501096},{"hi":3.0015019166916757,"lo":2.9909732666592115}],[{"hi":4.615441394590317,"lo":4.588443332723529},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.0008996895864213,"lo":2.643019293411982},{"hi":3.000443588010816,"lo":2.987296434664269}],[{"hi":4.615441394590317,"lo":4.588443332723529},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":2.789926391456333,"lo":2.780539042519895},{"hi":2.9927068188112433,"lo":2.9923280117903546}],[{"hi":4.590897701984147,"lo":4.563899640117358},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":2.934890526688434,"lo":2.5583991652035243},{"hi":2.999085583866568,"lo":2.982570460696923}],[{"hi":4.566354009377977,"lo":4.539355947511187},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":2.8644556642649786,"lo":2.4758057780962526},{"hi":2.996974245116461,"lo":2.977479907237261}],[{"hi":4.541810316771806,"lo":4.514812254905017},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":2.793115880239526,"lo":2.3884905110500685},{"hi":2.9942623663603625,"lo":2.971800556431809}],[{"hi":4.517266624165636,"lo":4.490268562298847},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":2.642886433274414,"lo":2.4115219969030077},{"hi":2.986235637846109,"lo":2.9726097442389126}],[{"hi":4.517266624165636,"lo":4.490268562298847},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":2.5818535268875307,"lo":2.3873891382976296},{"hi":2.9827760449683165,"lo":2.970986186752003}],[{"hi":4.492722931559466,"lo":4.465724869692676},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":2.6470359066156663,"lo":2.1923833744104755},{"hi":2.987043883726498,"lo":2.958689499506574}],[{"hi":4.468179238953295,"lo":4.441181177086506},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":2.5136849055852046,"lo":2.165083609853573},{"hi":2.9788437595841715,"lo":2.956812493610102}],[{"hi":4.468179238953295,"lo":4.441181177086506},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":2.3632915822763922,"lo":2.216372946950975},{"hi":2.96932286840831,"lo":2.960078197386859}],[{"hi":4.443635546347124,"lo":4.4166374844803356},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":2.4982470903093326,"lo":1.9404803672388562},{"hi":2.9774475259219013,"lo":2.943268875605747}],[{"hi":4.419091853740954,"lo":4.392093791874165},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":2.218461655316335,"lo":2.0812751581931144},{"hi":2.960160126763964,"lo":2.9521106491845757}],[{"hi":4.419091853740954,"lo":4.392093791874165},{"hi":1.3617187500000048,"lo":1.3101562499999952},{"hi":2.347672573378092,"lo":1.8188655325388687},{"hi":2.96693495946292,"lo":2.9379236613464474}],[{"hi":4.394548161134784,"lo":4.367550099267995},{"hi":1.3617187500000048,"lo":1.3101562499999952},{"hi":2.122640292212184,"lo":1.8920104338681192},{"hi":2.9540921130573725,"lo":2.9426822818965324}],[{"hi":4.394548161134784,"lo":4.367550099267995},{"hi":1.408593750000005,"lo":1.357031249999995},{"hi":2.2620586413668295,"lo":1.5675679809641183},{"hi":2.9586505127165648,"lo":2.930139766827324}],[{"hi":4.370004468528615,"lo":4.343006406661825},{"hi":1.408593750000005,"lo":1.357031249999995},{"hi":1.8521636583985464,"lo":1.8351651252151875},{"hi":2.941773663954014,"lo":2.9411996670265297}],[{"hi":4.370004468528615,"lo":4.343006406661825},{"hi":1.4554687500000052,"lo":1.4039062499999948},{"hi":2.115636533032852,"lo":1.4314565466720497},{"hi":2.9484442371513304,"lo":2.930906808766813}],[{"hi":4.370004468528615,"lo":4.343006406661825},{"hi":1.5023437500000054,"lo":1.4507812499999946},{"hi":1.9737929700593186,"lo":1.3021318979519485},{"hi":2.940265940840689,"lo":2.934405539040516}],[{"hi":4.370004468528615,"lo":4.343006406661825},{"hi":1.5492187500000054,"lo":1.4976562499999946},{"hi":1.8394607556378446,"lo":1.1677996835304745},{"hi":2.940265940840689,"lo":2.934405539040516}],[{"hi":4.370004468528615,"lo":4.343006406661825},{"hi":1.5960937500000056,"lo":1.5445312499999944},{"hi":1.7101361069177434,"lo":1.0259561205569412},{"hi":2.9484442371513304,"lo":2.930906808766813}],[{"hi":4.370004468528615,"lo":4.343006406661825},{"hi":1.6429687500000059,"lo":1.5914062499999941},{"hi":1.3064275283746056,"lo":1.2894289951912468},{"hi":2.941773663954014,"lo":2.9411996670265297}],[{"hi":4.394548161134784,"lo":4.367550099267995},{"hi":1.6429687500000059,"lo":1.5914062499999941},{"hi":1.5740246726256748,"lo":0.8795340122229636},{"hi":2.9586505127165648,"lo":2.930139766827324}],[{"hi":4.394548161134784,"lo":4.367550099267995},{"hi":1.689843750000006,"lo":1.638281249999994},{"hi":1.2495822197216722,"lo":1.018952361377612},{"hi":2.9540921130573725,"lo":2.9426822818965324}],[{"hi":4.419091853740954,"lo":4.392093791874165},{"hi":1.689843750000006,"lo":1.638281249999994},{"hi":1.322727121050927,"lo":0.7939200802116995},{"hi":2.96693495946292,"lo":2.9379236613464474}],[{"hi":4.419091853740954,"lo":4.392093791874165},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":1.0603174953966787,"lo":0.9231309982734581},{"hi":2.960160126763964,"lo":2.9521106491845757}],[{"hi":4.443635546347124,"lo":4.4166374844803356},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":1.201112286350937,"lo":0.6433455632804606},{"hi":2.9774475259219013,"lo":2.943268875605747}],[{"hi":4.468179238953295,"lo":4.441181177086506},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":0.925219706638817,"lo":0.7783010713134022},{"hi":2.96932286840831,"lo":2.960078197386859}],[{"hi":4.468179238953295,"lo":4.441181177086506},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":0.9765090437362218,"lo":0.6279077480045876},{"hi":2.9788437595841715,"lo":2.956812493610102}],[{"hi":4.492722931559466,"lo":4.465724869692676},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":0.9492092791793167,"lo":0.4945567469741281},{"hi":2.987043883726498,"lo":2.958689499506574}],[{"hi":4.517266624165636,"lo":4.490268562298847},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":0.7542035152921649,"lo":0.5597391267022616},{"hi":2.9827760449683165,"lo":2.970986186752003}],[{"hi":4.517266624165636,"lo":4.490268562298847},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":0.7300706566867845,"lo":0.4987062203153805},{"hi":2.986235637846109,"lo":2.9726097442389126}],[{"hi":4.541810316771806,"lo":4.514812254905017},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":0.753102142539726,"lo":0.3484767733502663},{"hi":2.9942623663603625,"lo":2.971800556431809}],[{"hi":4.566354009377977,"lo":4.539355947511187},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":0.6657868754935405,"lo":0.27713698932481456},{"hi":2.996974245116461,"lo":2.977479907237261}],[{"hi":4.590897701984147,"lo":4.563899640117358},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":0.5831934883862679,"lo":0.20670212690136047},{"hi":2.999085583866568,"lo":2.982570460696923}],[{"hi":4.615441394590317,"lo":4.588443332723529},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":0.36105361106989875,"lo":0.3516662621334605},{"hi":2.9927068188112433,"lo":2.9923280117903546}],[{"hi":4.615441394590317,"lo":4.588443332723529},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.4985733601778124,"lo":0.14069296400337095},{"hi":3.000443588010816,"lo":2.987296434664269}],[{"hi":4.639985087196487,"lo":4.6129870253297},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.4280080910396826,"lo":0.06760066962334488},{"hi":3.0015019166916757,"lo":2.9909732666592115}],[{"hi":4.664528779802657,"lo":4.63753071793587},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.3539775229201174,"lo":-0.0015571889054761812},{"hi":3.001805455258138,"lo":2.9942804576257247}],[{"hi":4.689072472408827,"lo":4.66207441054204},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.2816193294279774,"lo":-0.07078725458006296},{"hi":3.001505543678267,"lo":2.9969887312231096}],[{"hi":4.713616165014997,"lo":4.68661810314821},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.21052613706945777,"lo":-0.1403507580463037},{"hi":3.0006023626075917,"lo":2.9990964560886124}],[{"hi":4.738159857621167,"lo":4.71116179575438},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.14035075804630903,"lo":-0.21052613706946133},{"hi":3.0006023626075917,"lo":2.9990964560886124}],[{"hi":4.762703550227338,"lo":4.7357054883605505},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.07078725458006296,"lo":-0.2816193294279774},{"hi":3.001505543678267,"lo":2.9969887312231096}],[{"hi":4.787247242833509,"lo":4.760249180966721},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.0015571889054761812,"lo":-0.3539775229201174},{"hi":3.001805455258138,"lo":2.9942804576257247}],[{"hi":4.811790935439679,"lo":4.784792873572891},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":-0.06760066962334488,"lo":-0.4280080910396826},{"hi":3.0015019166916757,"lo":2.9909732666592115}],[{"hi":4.836334628045849,"lo":4.809336566179061},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":-0.14069296400336917,"lo":-0.49857336017781506},{"hi":3.000443588010816,"lo":2.987296434664269}],[{"hi":4.836334628045849,"lo":4.809336566179061},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":-0.3516662621334632,"lo":-0.361053611069897},{"hi":2.9927068188112433,"lo":2.9923280117903546}],[{"hi":4.860878320652019,"lo":4.833880258785231},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":-0.2067021269013587,"lo":-0.5831934883862706},{"hi":2.999085583866568,"lo":2.982570460696923}],[{"hi":4.885422013258189,"lo":4.858423951391401},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":-0.277136989324819,"lo":-0.6657868754935361},{"hi":2.996974245116461,"lo":2.977479907237261}],[{"hi":4.90996570586436,"lo":4.8829676439975715},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":-0.34847677335026717,"lo":-0.7531021425397224},{"hi":2.9942623663603625,"lo":2.971800556431809}],[{"hi":4.934509398470531,"lo":4.907511336603742},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":-0.4987062203153716,"lo":-0.7300706566867934},{"hi":2.98623563784611,"lo":2.9726097442389112}],[{"hi":4.934509398470531,"lo":4.907511336603742},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":-0.5597391267022687,"lo":-0.7542035152921587},{"hi":2.982776044968315,"lo":2.970986186752004}],[{"hi":4.959053091076701,"lo":4.932055029209912},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":-0.49455674697413077,"lo":-0.9492092791793105},{"hi":2.987043883726497,"lo":2.9586894995065753}],[{"hi":4.983596783682871,"lo":4.956598721816082},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":-0.6279077480045814,"lo":-0.9765090437362245},{"hi":2.978843759584173,"lo":2.956812493610101}],[{"hi":4.983596783682871,"lo":4.956598721816082},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":-0.7783010713134031,"lo":-0.9252197066388135},{"hi":2.9693228684083093,"lo":2.9600781973868604}],[{"hi":5.008140476289041,"lo":4.981142414422252},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":-0.6433455632804588,"lo":-1.2011122863509351},{"hi":2.9774475259219026,"lo":2.943268875605746}],[{"hi":5.032684168895212,"lo":5.005686107028422},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":-0.9231309982734546,"lo":-1.0603174953966796},{"hi":2.960160126763964,"lo":2.9521106491845757}],[{"hi":5.032684168895212,"lo":5.005686107028422},{"hi":1.689843750000006,"lo":1.638281249999994},{"hi":-0.7939200802116986,"lo":-1.3227271210509262},{"hi":2.96693495946292,"lo":2.9379236613464474}],[{"hi":5.057227861501382,"lo":5.030229799634593},{"hi":1.689843750000006,"lo":1.638281249999994},{"hi":-1.0189523613776093,"lo":-1.249582219721674},{"hi":2.9540921130573725,"lo":2.9426822818965324}],[{"hi":5.057227861501382,"lo":5.030229799634593},{"hi":1.6429687500000059,"lo":1.5914062499999941},{"hi":-0.8795340122229653,"lo":-1.5740246726256721},{"hi":2.958650512716564,"lo":2.9301397668273252}],[{"hi":5.0817715541075525,"lo":5.054773492240763},{"hi":1.6429687500000059,"lo":1.5914062499999941},{"hi":-1.289428995191244,"lo":-1.3064275283746074},{"hi":2.9417736639540144,"lo":2.94119966702653}],[{"hi":5.0817715541075525,"lo":5.054773492240763},{"hi":1.5960937500000056,"lo":1.5445312499999944},{"hi":-1.0259561205569394,"lo":-1.710136106917746},{"hi":2.9484442371513335,"lo":2.9309068087668093}],[{"hi":5.0817715541075525,"lo":5.054773492240763},{"hi":1.5492187500000054,"lo":1.4976562499999946},{"hi":-1.1677996835304771,"lo":-1.8394607556378428},{"hi":2.9402659408406864,"lo":2.934405539040518}],[{"hi":5.0817715541075525,"lo":5.054773492240763},{"hi":1.5023437500000054,"lo":1.4507812499999946},{"hi":-1.3021318979519503,"lo":-1.973792970059316},{"hi":2.9402659408406864,"lo":2.934405539040518}],[{"hi":5.0817715541075525,"lo":5.054773492240763},{"hi":1.4554687500000052,"lo":1.4039062499999948},{"hi":-1.431456546672047,"lo":-2.1156365330328537},{"hi":2.9484442371513335,"lo":2.9309068087668093}],[{"hi":5.0817715541075525,"lo":5.054773492240763},{"hi":1.408593750000005,"lo":1.357031249999995},{"hi":-1.8351651252151857,"lo":-1.852163658398549},{"hi":2.9417736639540144,"lo":2.94119966702653}],[{"hi":5.057227861501382,"lo":5.030229799634593},{"hi":1.408593750000005,"lo":1.357031249999995},{"hi":-1.567567980964121,"lo":-2.2620586413668278},{"hi":2.958650512716564,"lo":2.9301397668273252}],[{"hi":5.057227861501382,"lo":5.030229799634593},{"hi":1.3617187500000048,"lo":1.3101562499999952},{"hi":-1.8920104338681192,"lo":-2.122640292212184},{"hi":2.9540921130573725,"lo":2.9426822818965324}],[{"hi":5.032684168895212,"lo":5.005686107028422},{"hi":1.3617187500000048,"lo":1.3101562499999952},{"hi":-1.818865532538867,"lo":-2.3476725733780945},{"hi":2.96693495946292,"lo":2.9379236613464474}],[{"hi":5.032684168895212,"lo":5.005686107028422},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":-2.0812751581931153,"lo":-2.218461655316336},{"hi":2.960160126763964,"lo":2.9521106491845757}],[{"hi":5.008140476289041,"lo":4.981142414422252},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":-1.9404803672388553,"lo":-2.498247090309336},{"hi":2.9774475259219026,"lo":2.943268875605746}],[{"hi":4.983596783682871,"lo":4.956598721816082},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":-2.216372946950978,"lo":-2.3632915822763927},{"hi":2.9693228684083093,"lo":2.9600781973868604}],[{"hi":4.983596783682871,"lo":4.956598721816082},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":-2.1650836098535713,"lo":-2.51368490558521},{"hi":2.978843759584173,"lo":2.956812493610101}],[{"hi":4.959053091076701,"lo":4.932055029209912},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":-2.192383374410481,"lo":-2.647035906615665},{"hi":2.987043883726497,"lo":2.9586894995065753}],[{"hi":4.934509398470531,"lo":4.907511336603742},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":-2.387389138297637,"lo":-2.5818535268875227},{"hi":2.982776044968315,"lo":2.970986186752004}],[{"hi":4.934509398470531,"lo":4.907511336603742},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":-2.4115219969029997,"lo":-2.6428864332744215},{"hi":2.98623563784611,"lo":2.9726097442389112}],[{"hi":4.90996570586436,"lo":4.8829676439975715},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":-2.3884905110500707,"lo":-2.793115880239526},{"hi":2.9942623663603625,"lo":2.971800556431809}],[{"hi":4.885422013258189,"lo":4.858423951391401},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":-2.4758057780962552,"lo":-2.8644556642649768},{"hi":2.996974245116461,"lo":2.977479907237261}],[{"hi":4.860878320652019,"lo":4.833880258785231},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":-2.558399165203525,"lo":-2.9348905266884326},{"hi":2.999085583866568,"lo":2.982570460696923}],[{"hi":4.836334628045849,"lo":4.809336566179061},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":-2.780539042519896,"lo":-2.78992639145633},{"hi":2.9927068188112433,"lo":2.9923280117903546}],[{"hi":4.836334628045849,"lo":4.809336566179061},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":-2.643019293411978,"lo":-3.000899689586424},{"hi":3.000443588010816,"lo":2.987296434664269}],[{"hi":4.811790935439679,"lo":4.784792873572891},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":-2.7135845625501087,"lo":-3.073991983966451},{"hi":3.0015019166916757,"lo":2.9909732666592115}],[{"hi":4.787247242833509,"lo":4.760249180966721},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":-2.7876151306696784,"lo":-3.1431498424952675},{"hi":3.001805455258138,"lo":2.9942804576257247}],[{"hi":4.762703550227338,"lo":4.7357054883605505},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":-2.8599733241618157,"lo":-3.212379908169856},{"hi":3.001505543678267,"lo":2.9969887312231096}]]},{"anchors":[{"parameter":0.0,"point":[-0.9569403357322088,1.304462775530716,-0.2902846772531438],"uv_a":[1.2762720155208531,1.304462775529828],"uv_b":[-2.163587056467048,1.0430596642677912]},{"parameter":0.009736767428503688,"point":[-0.9553304402163312,1.3124999999984965,-0.2955397604341447],"uv_a":[1.270775858789579,1.3125],"uv_b":[-2.136149757042404,1.0446695597836688]},{"parameter":0.04718820561463079,"point":[-0.9495281805930367,1.3447461248599195,-0.31368174039485197],"uv_a":[1.251728322914683,1.3447461248579202],"uv_b":[-2.0303883488745584,1.0504718194069633]},{"parameter":0.06349135892021843,"point":[-0.9472462143608684,1.3593750000011489,-0.32050680082850813],"uv_a":[1.244531862444647,1.359375],"uv_b":[-1.984262377205745,1.0527537856391316]},{"parameter":0.11240017187382786,"point":[-0.9415440651830207,1.4051041271316915,-0.33688985338788763],"uv_a":[1.2271846303085128,1.4051041271304712],"uv_b":[-1.8453642451252037,1.0584559348169793]},{"parameter":0.113595593629575,"point":[-0.9414292657975238,1.4062499999999876,-0.3372105240053538],"uv_a":[1.226844030041145,1.40625],"uv_b":[-1.841964538488532,1.0585707342024762]},{"parameter":0.16157920308810098,"point":[-0.937921780120619,1.4531250000000353,-0.3468468457045384],"uv_a":[1.216589173959512,1.453125],"uv_b":[-1.70512854121637,1.062078219879381]},{"parameter":0.20857475372186934,"point":[-0.9367496997605853,1.5,-0.34999999999889503],"uv_a":[1.213225223151745,1.5],"uv_b":[-1.5707963267948966,1.0632503002394147]},{"parameter":0.2555703043556377,"point":[-0.937921780120619,1.5468749999999647,-0.3468468457045384],"uv_a":[1.216589173959512,1.546875],"uv_b":[-1.4364641123734232,1.062078219879381]},{"parameter":0.3035539138141637,"point":[-0.9414292657975238,1.5937500000000124,-0.3372105240053538],"uv_a":[1.226844030041145,1.59375],"uv_b":[-1.2996281151012614,1.0585707342024762]},{"parameter":0.30474933556991085,"point":[-0.9415440651830207,1.5948958728683085,-0.33688985338788763],"uv_a":[1.2271846303085128,1.5948958728695288],"uv_b":[-1.2962284084645894,1.0584559348169793]},{"parameter":0.35365814852352007,"point":[-0.9472462143608684,1.640624999998851,-0.3205068008285082],"uv_a":[1.244531862444647,1.640625],"uv_b":[-1.1573302763840483,1.0527537856391316]},{"parameter":0.3699613018291079,"point":[-0.9495281805930367,1.6552538751400805,-0.31368174039485197],"uv_a":[1.251728322914683,1.6552538751420798],"uv_b":[-1.1112043047152347,1.0504718194069633]},{"parameter":0.40741274001523503,"point":[-0.9553304402163312,1.6875000000015035,-0.29553976043414465],"uv_a":[1.270775858789579,1.6875],"uv_b":[-1.0054428965473892,1.0446695597836688]},{"parameter":0.41714950744373874,"point":[-0.9569403357322088,1.695537224469284,-0.2902846772531437],"uv_a":[1.2762720155208531,1.695537224470172],"uv_b":[-0.978005597122745,1.0430596642677912]},{"parameter":0.45676712026879107,"point":[-0.96377606579544,1.7266369453454617,-0.2667127574780762],"uv_a":[1.3008157081270233,1.7266369453427615],"uv_b":[-0.8664522525086491,1.03622393420456]},{"parameter":0.4672159415630129,"point":[-0.96562499999973,1.7343749999997504,-0.25993914552328934],"uv_a":[1.307837145238063,1.734375],"uv_b":[-0.8370685254435661,1.03437500000027]},{"parameter":0.49200779363953534,"point":[-0.9700312531945441,1.751913938030993,-0.24298017990319343],"uv_a":[1.3253594007331935,1.7519139380310662],"uv_b":[-0.7673482662972395,1.029968746805456]},{"parameter":0.524323964035672,"point":[-0.9757021300385285,1.7729370743671902,-0.21910124015532834],"uv_a":[1.3499030933393636,1.7729370743691106],"uv_b":[-0.676417759856203,1.0242978699614715]},{"parameter":0.5381383415381447,"point":[-0.9780601016817907,1.7812500000010303,-0.20832291640269235],"uv_a":[1.360936385148825,1.78125],"uv_b":[-0.6375248821382267,1.0219398983182093]},{"parameter":0.5545601064429255,"point":[-0.9807852804032304,1.79058521340942,-0.1950903220182216],"uv_a":[1.3744467859455338,1.7905852134063025],"uv_b":[-0.5912519948639414,1.0192147195967696]},{"parameter":0.583230297899954,"point":[-0.9852776423889411,1.805404702959764,-0.17096188876118537],"uv_a":[1.398990478551704,1.8054047029581852],"uv_b":[-0.51032692102605,1.014722357611059]},{"parameter":0.6107069677006774,"point":[-0.9891765099647809,1.817758033514144,-0.14673047445705772],"uv_a":[1.4235341711578742,1.8177580335104722],"uv_b":[-0.43259694379230695,1.010823490035219]},{"parameter":0.6372613650501475,"point":[-0.9924795345987101,1.827895755688726,-0.12241067519858959],"uv_a":[1.4480778637640443,1.827895755690406],"uv_b":[-0.357298671495325,1.00752046540129]},{"parameter":0.6379228323825193,"point":[-0.9925552960028876,1.828124999998915,-0.12179484544315543],"uv_a":[1.448698336237784,1.828125],"uv_b":[-0.3554212017080398,1.0074447039971124]},{"parameter":0.6631067572210635,"point":[-0.9951847266721967,1.8359950002630323,-0.09801714032950891],"uv_a":[1.472621556370215,1.8359950002632104],"uv_b":[-0.28384512247314847,1.0048152733278033]},{"parameter":0.6884175604761618,"point":[-0.9972904566786902,1.842181611112966,-0.07356456359924327],"uv_a":[1.4971652489763856,1.8421816111149383],"uv_b":[-0.2117636381898792,1.0027095433213098]},{"parameter":0.7133452208882884,"point":[-0.9987954562051724,1.8465434508660628,-0.0490676743270132],"uv_a":[1.5217089415825558,1.8465434508689214],"uv_b":[-0.14065669582476112,1.0012045437948276]},{"parameter":0.7380251146186411,"point":[-0.9996988186962041,1.8491385514383343,-0.02454122852312442],"uv_a":[1.546252634188726,1.849138551435317],"uv_b":[-0.07017537902315295,1.0003011813037959]},{"parameter":0.7625833047236015,"point":[-1.0,1.8500000000029104,-1.2246467991371698e-16],"uv_a":[1.5707963267948961,1.8500000000058208],"uv_b":[-3.4989908546485324e-16,1.0]},{"parameter":0.7871414948285619,"point":[-0.9996988186962041,1.8491385514383343,0.024541228523124174],"uv_a":[1.5953400194010663,1.849138551435317],"uv_b":[0.07017537902315224,1.0003011813037959]},{"parameter":0.8118213885589145,"point":[-0.9987954562051724,1.8465434508660628,0.049067674327012964],"uv_a":[1.6198837120072365,1.8465434508689214],"uv_b":[0.14065669582476042,1.0012045437948276]},{"parameter":0.8367490489710412,"point":[-0.9972904566786902,1.842181611112966,0.07356456359924303],"uv_a":[1.6444274046134066,1.8421816111149383],"uv_b":[0.21176363818987856,1.0027095433213098]},{"parameter":0.8620598522261395,"point":[-0.9951847266721969,1.8359950002630323,0.09801714032950867],"uv_a":[1.6689710972195773,1.8359950002632104],"uv_b":[0.2838451224731478,1.004815273327803]},{"parameter":0.8872437770646836,"point":[-0.9925552960028876,1.828124999998915,0.12179484544315519],"uv_a":[1.6928943173520081,1.828125],"uv_b":[0.35542120170803915,1.0074447039971124]},{"parameter":0.8879052443970554,"point":[-0.9924795345987101,1.827895755688726,0.12241067519858935],"uv_a":[1.6935147898257479,1.827895755690406],"uv_b":[0.35729867149532435,1.00752046540129]},{"parameter":0.9144596417465256,"point":[-0.9891765099647809,1.817758033514144,0.1467304744570575],"uv_a":[1.718058482431918,1.8177580335104722],"uv_b":[0.43259694379230634,1.010823490035219]},{"parameter":0.9419363115472492,"point":[-0.9852776423889411,1.805404702959764,0.17096188876118518],"uv_a":[1.7426021750380882,1.8054047029581852],"uv_b":[0.5103269210260495,1.014722357611059]},{"parameter":0.9706065030042776,"point":[-0.9807852804032304,1.79058521340942,0.19509032201822138],"uv_a":[1.7671458676442584,1.7905852134063025],"uv_b":[0.5912519948639408,1.0192147195967696]},{"parameter":0.9870282679090586,"point":[-0.9780601016817907,1.7812500000010303,0.20832291640269218],"uv_a":[1.7806562684409672,1.78125],"uv_b":[0.6375248821382261,1.0219398983182093]},{"parameter":1.0008426454115313,"point":[-0.9757021300385285,1.7729370743671902,0.21910124015532814],"uv_a":[1.7916895602504286,1.7729370743691106],"uv_b":[0.6764177598562024,1.0242978699614715]},{"parameter":1.033158815807668,"point":[-0.9700312531945441,1.751913938030993,0.24298017990319326],"uv_a":[1.8162332528565988,1.7519139380310662],"uv_b":[0.767348266297239,1.029968746805456]},{"parameter":1.0579506678841906,"point":[-0.96562499999973,1.7343749999997504,0.2599391455232891],"uv_a":[1.8337555083517292,1.734375],"uv_b":[0.8370685254435657,1.03437500000027]},{"parameter":1.0683994891784125,"point":[-0.96377606579544,1.7266369453454617,0.2667127574780761],"uv_a":[1.840776945462769,1.7266369453427615],"uv_b":[0.8664522525086488,1.03622393420456]},{"parameter":1.1080171020034648,"point":[-0.956940335732209,1.695537224469284,0.29028467725314355],"uv_a":[1.865320638068939,1.695537224470172],"uv_b":[0.9780055971227446,1.043059664267791]},{"parameter":1.1177538694319684,"point":[-0.9553304402163314,1.6875000000015037,0.29553976043414454],"uv_a":[1.8708167948002132,1.6875],"uv_b":[1.0054428965473887,1.0446695597836686]},{"parameter":1.1552053076180957,"point":[-0.9495281805930369,1.6552538751400805,0.3136817403948518],"uv_a":[1.8898643306751093,1.6552538751420798],"uv_b":[1.1112043047152345,1.050471819406963]},{"parameter":1.1715084609236834,"point":[-0.9472462143608684,1.6406249999988511,0.320506800828508],"uv_a":[1.8970607911451451,1.640625],"uv_b":[1.157330276384048,1.0527537856391316]},{"parameter":1.220417273877293,"point":[-0.9415440651830209,1.5948958728683085,0.3368898533878875],"uv_a":[1.9144080232812795,1.5948958728695288],"uv_b":[1.2962284084645894,1.058455934816979]},{"parameter":1.22161269563304,"point":[-0.9414292657975238,1.5937500000000124,0.3372105240053536],"uv_a":[1.9147486235486473,1.59375],"uv_b":[1.2996281151012612,1.0585707342024762]},{"parameter":1.269596305091566,"point":[-0.937921780120619,1.5468749999999647,0.3468468457045383],"uv_a":[1.9250034796302802,1.546875],"uv_b":[1.4364641123734232,1.062078219879381]},{"parameter":1.3165918557253344,"point":[-0.9367496997605855,1.5,0.3499999999988949],"uv_a":[1.9283674304380471,1.5],"uv_b":[1.5707963267948966,1.0632503002394145]},{"parameter":1.3635874063591027,"point":[-0.937921780120619,1.4531250000000353,0.3468468457045383],"uv_a":[1.9250034796302802,1.453125],"uv_b":[1.7051285412163701,1.062078219879381]},{"parameter":1.4115710158176287,"point":[-0.9414292657975238,1.4062499999999876,0.3372105240053537],"uv_a":[1.9147486235486473,1.40625],"uv_b":[1.841964538488532,1.0585707342024762]},{"parameter":1.412766437573376,"point":[-0.9415440651830209,1.4051041271316915,0.33688985338788746],"uv_a":[1.9144080232812795,1.4051041271304712],"uv_b":[1.845364245125204,1.058455934816979]},{"parameter":1.4616752505269854,"point":[-0.9472462143608684,1.3593750000011489,0.320506800828508],"uv_a":[1.8970607911451451,1.359375],"uv_b":[1.9842623772057453,1.0527537856391316]},{"parameter":1.4779784038325732,"point":[-0.9495281805930369,1.3447461248599195,0.3136817403948518],"uv_a":[1.8898643306751093,1.3447461248579202],"uv_b":[2.030388348874559,1.050471819406963]},{"parameter":1.5154298420187002,"point":[-0.9553304402163314,1.3124999999984965,0.29553976043414454],"uv_a":[1.8708167948002132,1.3125],"uv_b":[2.1361497570424044,1.0446695597836686]},{"parameter":1.525166609447204,"point":[-0.956940335732209,1.304462775530716,0.29028467725314355],"uv_a":[1.865320638068939,1.304462775529828],"uv_b":[2.1635870564670485,1.043059664267791]},{"parameter":1.5647842222722566,"point":[-0.96377606579544,1.273363054654538,0.2667127574780761],"uv_a":[1.840776945462769,1.2733630546572385],"uv_b":[2.2751404010811447,1.03622393420456]},{"parameter":1.5752330435664783,"point":[-0.96562499999973,1.2656250000002496,0.2599391455232891],"uv_a":[1.8337555083517292,1.265625],"uv_b":[2.3045241281462276,1.03437500000027]},{"parameter":1.6000248956430008,"point":[-0.9700312531945441,1.248086061969007,0.2429801799031932],"uv_a":[1.8162332528565988,1.2480860619689338],"uv_b":[2.3742443872925545,1.029968746805456]},{"parameter":1.6323410660391375,"point":[-0.9757021300385285,1.2270629256328098,0.21910124015532811],"uv_a":[1.7916895602504286,1.2270629256308894],"uv_b":[2.465174893733591,1.0242978699614715]},{"parameter":1.6461554435416101,"point":[-0.9780601016817907,1.2187499999989697,0.20832291640269218],"uv_a":[1.7806562684409672,1.21875],"uv_b":[2.504067771451567,1.0219398983182093]},{"parameter":1.662577208446391,"point":[-0.9807852804032304,1.20941478659058,0.1950903220182214],"uv_a":[1.7671458676442584,1.2094147865936975],"uv_b":[2.5503406587258524,1.0192147195967696]},{"parameter":1.6912473999034194,"point":[-0.9852776423889411,1.194595297040236,0.17096188876118518],"uv_a":[1.7426021750380882,1.1945952970418148],"uv_b":[2.631265732563744,1.014722357611059]},{"parameter":1.718724069704143,"point":[-0.9891765099647809,1.182241966485856,0.1467304744570575],"uv_a":[1.718058482431918,1.1822419664895278],"uv_b":[2.708995709797487,1.010823490035219]},{"parameter":1.7452784670536132,"point":[-0.9924795345987101,1.172104244311274,0.12241067519858936],"uv_a":[1.6935147898257479,1.172104244309594],"uv_b":[2.7842939820944688,1.00752046540129]},{"parameter":1.7459399343859852,"point":[-0.9925552960028876,1.171875000001085,0.12179484544315518],"uv_a":[1.6928943173520081,1.171875],"uv_b":[2.786171451881754,1.0074447039971124]},{"parameter":1.7711238592245293,"point":[-0.9951847266721969,1.1640049997369677,0.09801714032950865],"uv_a":[1.6689710972195773,1.1640049997367896],"uv_b":[2.8577475311166456,1.004815273327803]},{"parameter":1.7964346624796275,"point":[-0.9972904566786902,1.157818388887034,0.07356456359924307],"uv_a":[1.6444274046134066,1.1578183888850617],"uv_b":[2.9298290153999145,1.0027095433213098]},{"parameter":1.8213623228917541,"point":[-0.9987954562051724,1.1534565491339372,0.04906767432701298],"uv_a":[1.6198837120072365,1.1534565491310786],"uv_b":[3.0009359577650327,1.0012045437948276]},{"parameter":1.8460422166221069,"point":[-0.9996988186962041,1.1508614485616657,0.024541228523124205],"uv_a":[1.5953400194010663,1.150861448564683],"uv_b":[3.071417274566641,1.0003011813037959]},{"parameter":1.8706004067270674,"point":[-1.0,1.1499999999970896,-1.603792706662073e-16],"uv_a":[1.5707963267948961,1.1499999999941792],"uv_b":[3.1415926535897936,1.0]},{"parameter":1.8951585968320277,"point":[-0.9996988186962041,1.1508614485616657,-0.024541228523124403],"uv_a":[1.546252634188726,1.150861448564683],"uv_b":[3.211768032612946,1.0003011813037959]},{"parameter":1.9198384905623804,"point":[-0.9987954562051724,1.1534565491339372,-0.04906767432701317],"uv_a":[1.5217089415825558,1.1534565491310786],"uv_b":[3.282249349414554,1.0012045437948276]},{"parameter":1.9447661509745071,"point":[-0.9972904566786902,1.157818388887034,-0.07356456359924327],"uv_a":[1.4971652489763856,1.1578183888850617],"uv_b":[3.353356291779672,1.0027095433213098]},{"parameter":1.9700769542296055,"point":[-0.9951847266721967,1.1640049997369677,-0.09801714032950892],"uv_a":[1.472621556370215,1.1640049997367896],"uv_b":[3.4254377760629415,1.0048152733278033]},{"parameter":1.9952608790681496,"point":[-0.9925552960028876,1.171875000001085,-0.12179484544315544],"uv_a":[1.448698336237784,1.171875],"uv_b":[3.497013855297833,1.0074447039971124]},{"parameter":1.9959223464005214,"point":[-0.9924795345987101,1.172104244311274,-0.12241067519858956],"uv_a":[1.4480778637640443,1.172104244309594],"uv_b":[3.498891325085118,1.00752046540129]},{"parameter":2.0224767437499915,"point":[-0.9891765099647809,1.182241966485856,-0.1467304744570577],"uv_a":[1.4235341711578742,1.1822419664895278],"uv_b":[3.5741895973821,1.010823490035219]},{"parameter":2.049953413550715,"point":[-0.9852776423889411,1.194595297040236,-0.17096188876118534],"uv_a":[1.398990478551704,1.1945952970418148],"uv_b":[3.651919574615843,1.014722357611059]},{"parameter":2.0786236050077433,"point":[-0.9807852804032304,1.20941478659058,-0.19509032201822157],"uv_a":[1.3744467859455338,1.2094147865936975],"uv_b":[3.7328446484537343,1.0192147195967696]},{"parameter":2.0950453699125244,"point":[-0.9780601016817907,1.2187499999989697,-0.20832291640269235],"uv_a":[1.360936385148825,1.21875],"uv_b":[3.7791175357280196,1.0219398983182093]},{"parameter":2.1088597474149973,"point":[-0.9757021300385285,1.22706292563281,-0.21910124015532836],"uv_a":[1.3499030933393636,1.2270629256308894],"uv_b":[3.8180104134459962,1.0242978699614715]},{"parameter":2.141175917811134,"point":[-0.9700312531945441,1.248086061969007,-0.24298017990319343],"uv_a":[1.3253594007331935,1.2480860619689338],"uv_b":[3.9089409198870326,1.029968746805456]},{"parameter":2.165967769887656,"point":[-0.96562499999973,1.2656250000002496,-0.2599391455232893],"uv_a":[1.307837145238063,1.265625],"uv_b":[3.978661179033359,1.03437500000027]},{"parameter":2.176416591181878,"point":[-0.96377606579544,1.2733630546545383,-0.2667127574780762],"uv_a":[1.3008157081270233,1.2733630546572385],"uv_b":[4.008044906098442,1.03622393420456]},{"parameter":2.21603420400693,"point":[-0.9569403357322088,1.304462775530716,-0.2902846772531438],"uv_a":[1.2762720155208531,1.304462775529828],"uv_b":[4.119598250712539,1.0430596642677912]}],"residual_tolerance":1e-9,"surfaces":[0,3],"uv_tubes":[[{"hi":1.2774992001511718,"lo":1.2505011382843643},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":-2.0812751581931157,"lo":-2.2184616553163363},{"hi":1.0478893508154241,"lo":1.0398398732360359}],[{"hi":1.2774992001511718,"lo":1.2505011382843643},{"hi":1.3617187500000048,"lo":1.3101562499999952},{"hi":-1.8188655325388674,"lo":-2.347672573378095},{"hi":1.0620763386535523,"lo":1.0330650405370798}],[{"hi":1.2529555075450016,"lo":1.225957445678194},{"hi":1.3617187500000048,"lo":1.3101562499999952},{"hi":-1.8920104338681183,"lo":-2.122640292212185},{"hi":1.057317718103468,"lo":1.0459078869426268}],[{"hi":1.2529555075450016,"lo":1.225957445678194},{"hi":1.408593750000005,"lo":1.357031249999995},{"hi":-1.567567980964121,"lo":-2.2620586413668278},{"hi":1.0698602331726748,"lo":1.0413494872834361}],[{"hi":1.228411814938831,"lo":1.2014137530720244},{"hi":1.408593750000005,"lo":1.357031249999995},{"hi":-1.8351651252151884,"lo":-1.8521636583985472},{"hi":1.0588003329734699,"lo":1.0582263360459856}],[{"hi":1.228411814938831,"lo":1.2014137530720244},{"hi":1.4554687500000052,"lo":1.4039062499999948},{"hi":-1.431456546672046,"lo":-2.115636533032856},{"hi":1.0690931912331907,"lo":1.0515557628486665}],[{"hi":1.228411814938831,"lo":1.2014137530720244},{"hi":1.5023437500000054,"lo":1.4507812499999946},{"hi":-1.3021318979519498,"lo":-1.9737929700593166},{"hi":1.065594460959482,"lo":1.0597340591593136}],[{"hi":1.228411814938831,"lo":1.2014137530720244},{"hi":1.5492187500000054,"lo":1.4976562499999946},{"hi":-1.1677996835304765,"lo":-1.8394607556378433},{"hi":1.065594460959482,"lo":1.0597340591593136}],[{"hi":1.228411814938831,"lo":1.2014137530720244},{"hi":1.5960937500000056,"lo":1.5445312499999944},{"hi":-1.0259561205569379,"lo":-1.7101361069177468},{"hi":1.0690931912331907,"lo":1.0515557628486665}],[{"hi":1.228411814938831,"lo":1.2014137530720244},{"hi":1.6429687500000059,"lo":1.5914062499999941},{"hi":-1.2894289951912454,"lo":-1.3064275283746054},{"hi":1.0588003329734699,"lo":1.0582263360459856}],[{"hi":1.2529555075450016,"lo":1.225957445678194},{"hi":1.6429687500000059,"lo":1.5914062499999941},{"hi":-0.879534012222966,"lo":-1.5740246726256717},{"hi":1.0698602331726748,"lo":1.0413494872834361}],[{"hi":1.2529555075450016,"lo":1.225957445678194},{"hi":1.689843750000006,"lo":1.638281249999994},{"hi":-1.0189523613776075,"lo":-1.2495822197216755},{"hi":1.057317718103468,"lo":1.0459078869426268}],[{"hi":1.2774992001511718,"lo":1.2505011382843643},{"hi":1.689843750000006,"lo":1.638281249999994},{"hi":-0.7939200802116981,"lo":-1.3227271210509257},{"hi":1.0620763386535523,"lo":1.0330650405370798}],[{"hi":1.2774992001511718,"lo":1.2505011382843643},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":-0.9231309982734568,"lo":-1.0603174953966774},{"hi":1.0478893508154241,"lo":1.0398398732360359}],[{"hi":1.302042892757342,"lo":1.2750448308905344},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":-0.6433455632804572,"lo":-1.201112286350937},{"hi":1.0567311243942534,"lo":1.0225524740780978}],[{"hi":1.3265865853635121,"lo":1.2995885234967046},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":-0.7783010713134002,"lo":-0.925219706638815},{"hi":1.03992180261314,"lo":1.03067713159169}],[{"hi":1.3265865853635121,"lo":1.2995885234967046},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":-0.6279077480045863,"lo":-0.9765090437362194},{"hi":1.0431875063898983,"lo":1.0211562404158276}],[{"hi":1.3511302779696823,"lo":1.3241322161028748},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":-0.49455674697413,"lo":-0.9492092791793125},{"hi":1.0413105004934247,"lo":1.0129561162735028}],[{"hi":1.375673970575853,"lo":1.3486759087090445},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":-0.559739126702274,"lo":-0.7542035152921557},{"hi":1.029013813247996,"lo":1.0172239550316848}],[{"hi":1.375673970575853,"lo":1.3486759087090445},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":-0.49870622031537093,"lo":-0.7300706566867972},{"hi":1.0273902557610888,"lo":1.01376436215389}],[{"hi":1.4002176631820231,"lo":1.3732196013152147},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":-0.3484767733502673,"lo":-0.7531021425397242},{"hi":1.028199443568191,"lo":1.0057376336396375}],[{"hi":1.4247613557881933,"lo":1.3977632939213849},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":-0.2771369893248208,"lo":-0.6657868754935362},{"hi":1.0225200927627385,"lo":1.0030257548835395}],[{"hi":1.4493050483943635,"lo":1.422306986527555},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":-0.20670212690136114,"lo":-0.5831934883862708},{"hi":1.0174295393030774,"lo":1.0009144161334316}],[{"hi":1.473848741000534,"lo":1.4468506791337252},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":-0.3516662621334694,"lo":-0.3610536110698954},{"hi":1.007671988209645,"lo":1.0072931811887573}],[{"hi":1.473848741000534,"lo":1.4468506791337252},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":-0.14069296400336578,"lo":-0.4985733601778225},{"hi":1.0127035653357306,"lo":0.9995564119891851}],[{"hi":1.4983924336067052,"lo":1.4713943717398954},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":-0.06760066962334066,"lo":-0.428008091039687},{"hi":1.0090267333407903,"lo":0.9984980833083228}],[{"hi":1.5229361262128753,"lo":1.495938064346066},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.0015571889054750432,"lo":-0.35397752292011536},{"hi":1.0057195423742742,"lo":0.9981945447418632}],[{"hi":1.5474798188190455,"lo":1.5204817569522362},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.0707872545800634,"lo":-0.28161932942797746},{"hi":1.003011268776891,"lo":0.9984944563217324}],[{"hi":1.5720235114252157,"lo":1.5450254495584064},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.14035075804630484,"lo":-0.21052613706945816},{"hi":1.0009035439113876,"lo":0.9993976373924083}],[{"hi":1.5965672040313859,"lo":1.5695691421645765},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.21052613706945741,"lo":-0.14035075804630553},{"hi":1.0009035439113876,"lo":0.9993976373924083}],[{"hi":1.6211108966375565,"lo":1.5941128347707463},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.2816193294279768,"lo":-0.07078725458006413},{"hi":1.003011268776891,"lo":0.9984944563217324}],[{"hi":1.6456545892437267,"lo":1.6186565273769165},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.35397752292011486,"lo":-0.001557188905475848},{"hi":1.0057195423742742,"lo":0.9981945447418632}],[{"hi":1.6701982818498973,"lo":1.6432002199830866},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.4280080910396863,"lo":0.06760066962334008},{"hi":1.0090267333407896,"lo":0.9984980833083232}],[{"hi":1.694741974456068,"lo":1.6677439125892572},{"hi":1.8773437500000068,"lo":1.8257812499999932},{"hi":0.49857336017782183,"lo":0.14069296400336512},{"hi":1.012703565335731,"lo":0.9995564119891844}],[{"hi":1.694741974456068,"lo":1.6677439125892572},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":0.36105361106989475,"lo":0.35166626213346874},{"hi":1.007671988209645,"lo":1.0072931811887573}],[{"hi":1.719285667062238,"lo":1.6922876051954279},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":0.5831934883862704,"lo":0.20670212690136036},{"hi":1.0174295393030774,"lo":1.0009144161334316}],[{"hi":1.7438293596684087,"lo":1.7168312978015976},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":0.6657868754935358,"lo":0.27713698932482006},{"hi":1.0225200927627385,"lo":1.0030257548835395}],[{"hi":1.7683730522745789,"lo":1.7413749904077678},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":0.7531021425397233,"lo":0.34847677335026694},{"hi":1.028199443568191,"lo":1.0057376336396375}],[{"hi":1.792916744880749,"lo":1.765918683013938},{"hi":1.8304687500000065,"lo":1.7789062499999935},{"hi":0.7300706566867968,"lo":0.49870622031537004},{"hi":1.0273902557610888,"lo":1.01376436215389}],[{"hi":1.792916744880749,"lo":1.765918683013938},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":0.7542035152921551,"lo":0.5597391267022734},{"hi":1.029013813247996,"lo":1.0172239550316848}],[{"hi":1.8174604374869192,"lo":1.7904623756201081},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":0.949209279179312,"lo":0.49455674697412944},{"hi":1.0413105004934247,"lo":1.0129561162735028}],[{"hi":1.8420041300930894,"lo":1.8150060682262783},{"hi":1.7835937500000063,"lo":1.7320312499999937},{"hi":0.9765090437362192,"lo":0.6279077480045855},{"hi":1.0431875063898983,"lo":1.0211562404158276}],[{"hi":1.8420041300930894,"lo":1.8150060682262783},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":0.9252197066388149,"lo":0.7783010713133995},{"hi":1.03992180261314,"lo":1.03067713159169}],[{"hi":1.86654782269926,"lo":1.839549760832448},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":1.2011122863509363,"lo":0.6433455632804571},{"hi":1.0567311243942528,"lo":1.0225524740780982}],[{"hi":1.8910915153054302,"lo":1.8640934534386182},{"hi":1.736718750000006,"lo":1.685156249999994},{"hi":1.060317495396677,"lo":0.9231309982734563},{"hi":1.047889350815424,"lo":1.0398398732360357}],[{"hi":1.8910915153054302,"lo":1.8640934534386182},{"hi":1.689843750000006,"lo":1.638281249999994},{"hi":1.322727121050926,"lo":0.7939200802116972},{"hi":1.062076338653552,"lo":1.0330650405370796}],[{"hi":1.9156352079116004,"lo":1.8886371460447884},{"hi":1.689843750000006,"lo":1.638281249999994},{"hi":1.2495822197216753,"lo":1.0189523613776073},{"hi":1.0573177181034685,"lo":1.0459078869426262}],[{"hi":1.9156352079116004,"lo":1.8886371460447884},{"hi":1.6429687500000059,"lo":1.5914062499999941},{"hi":1.5740246726256721,"lo":0.8795340122229653},{"hi":1.069860233172674,"lo":1.0413494872834366}],[{"hi":1.9401789005177705,"lo":1.9131808386509586},{"hi":1.6429687500000059,"lo":1.5914062499999941},{"hi":1.3064275283746047,"lo":1.2894289951912459},{"hi":1.0588003329734703,"lo":1.058226336045985}],[{"hi":1.9401789005177705,"lo":1.9131808386509586},{"hi":1.5960937500000056,"lo":1.5445312499999944},{"hi":1.7101361069177472,"lo":1.0259561205569372},{"hi":1.0690931912331907,"lo":1.0515557628486665}],[{"hi":1.9401789005177705,"lo":1.9131808386509586},{"hi":1.5492187500000054,"lo":1.4976562499999946},{"hi":1.8394607556378433,"lo":1.1677996835304765},{"hi":1.0655944609594814,"lo":1.059734059159314}],[{"hi":1.9401789005177705,"lo":1.9131808386509586},{"hi":1.5023437500000054,"lo":1.4507812499999946},{"hi":1.9737929700593173,"lo":1.3021318979519494},{"hi":1.0655944609594814,"lo":1.059734059159314}],[{"hi":1.9401789005177705,"lo":1.9131808386509586},{"hi":1.4554687500000052,"lo":1.4039062499999948},{"hi":2.1156365330328555,"lo":1.4314565466720466},{"hi":1.0690931912331907,"lo":1.0515557628486665}],[{"hi":1.9401789005177705,"lo":1.9131808386509586},{"hi":1.408593750000005,"lo":1.357031249999995},{"hi":1.852163658398548,"lo":1.835165125215188},{"hi":1.0588003329734703,"lo":1.058226336045985}],[{"hi":1.9156352079116004,"lo":1.8886371460447884},{"hi":1.408593750000005,"lo":1.357031249999995},{"hi":2.2620586413668278,"lo":1.5675679809641212},{"hi":1.069860233172674,"lo":1.0413494872834366}],[{"hi":1.9156352079116004,"lo":1.8886371460447884},{"hi":1.3617187500000048,"lo":1.3101562499999952},{"hi":2.122640292212186,"lo":1.892010433868118},{"hi":1.0573177181034685,"lo":1.0459078869426262}],[{"hi":1.8910915153054302,"lo":1.8640934534386182},{"hi":1.3617187500000048,"lo":1.3101562499999952},{"hi":2.3476725733780954,"lo":1.8188655325388678},{"hi":1.062076338653552,"lo":1.0330650405370796}],[{"hi":1.8910915153054302,"lo":1.8640934534386182},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":2.2184616553163368,"lo":2.081275158193116},{"hi":1.047889350815424,"lo":1.0398398732360357}],[{"hi":1.86654782269926,"lo":1.839549760832448},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":2.498247090309337,"lo":1.9404803672388562},{"hi":1.0567311243942528,"lo":1.0225524740780982}],[{"hi":1.8420041300930894,"lo":1.8150060682262783},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":2.3632915822763936,"lo":2.2163729469509788},{"hi":1.03992180261314,"lo":1.03067713159169}],[{"hi":1.8420041300930894,"lo":1.8150060682262783},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":2.513684905585208,"lo":2.165083609853574},{"hi":1.0431875063898983,"lo":1.0211562404158276}],[{"hi":1.8174604374869192,"lo":1.7904623756201081},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":2.6470359066156637,"lo":2.1923833744104817},{"hi":1.0413105004934247,"lo":1.0129561162735028}],[{"hi":1.792916744880749,"lo":1.765918683013938},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":2.5818535268875196,"lo":2.3873891382976384},{"hi":1.029013813247996,"lo":1.0172239550316848}],[{"hi":1.792916744880749,"lo":1.765918683013938},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":2.642886433274423,"lo":2.4115219969029966},{"hi":1.0273902557610888,"lo":1.01376436215389}],[{"hi":1.7683730522745789,"lo":1.7413749904077678},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":2.793115880239527,"lo":2.3884905110500694},{"hi":1.028199443568191,"lo":1.0057376336396375}],[{"hi":1.7438293596684087,"lo":1.7168312978015976},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":2.864455664264973,"lo":2.475805778096258},{"hi":1.0225200927627385,"lo":1.0030257548835395}],[{"hi":1.719285667062238,"lo":1.6922876051954279},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":2.9348905266884326,"lo":2.558399165203523},{"hi":1.0174295393030774,"lo":1.0009144161334316}],[{"hi":1.694741974456068,"lo":1.6677439125892572},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":2.789926391456325,"lo":2.780539042519898},{"hi":1.007671988209645,"lo":1.0072931811887573}],[{"hi":1.694741974456068,"lo":1.6677439125892572},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.0008996895864284,"lo":2.6430192934119714},{"hi":1.012703565335731,"lo":0.9995564119891844}],[{"hi":1.6701982818498973,"lo":1.6432002199830866},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.0739919839664522,"lo":2.713584562550108},{"hi":1.0090267333407896,"lo":0.9984980833083232}],[{"hi":1.6456545892437267,"lo":1.6186565273769165},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.1431498424952693,"lo":2.787615130669678},{"hi":1.0057195423742742,"lo":0.9981945447418632}],[{"hi":1.6211108966375565,"lo":1.5941128347707463},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.212379908169857,"lo":2.8599733241618166},{"hi":1.003011268776891,"lo":0.9984944563217324}],[{"hi":1.5965672040313859,"lo":1.5695691421645765},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.281943411636099,"lo":2.9310665165203353},{"hi":1.0009035439113876,"lo":0.9993976373924083}],[{"hi":1.5720235114252157,"lo":1.5450254495584064},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.3521187906592504,"lo":3.001241895543489},{"hi":1.0009035439113876,"lo":0.9993976373924083}],[{"hi":1.5474798188190455,"lo":1.5204817569522362},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.42321198301777,"lo":3.0708053990097297},{"hi":1.003011268776891,"lo":0.9984944563217324}],[{"hi":1.5229361262128753,"lo":1.495938064346066},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.4955701765099088,"lo":3.1400354646843174},{"hi":1.0057195423742742,"lo":0.9981945447418632}],[{"hi":1.4983924336067052,"lo":1.4713943717398954},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.56960074462948,"lo":3.2091933232131336},{"hi":1.0090267333407903,"lo":0.9984980833083228}],[{"hi":1.473848741000534,"lo":1.4468506791337252},{"hi":1.174218750000004,"lo":1.122656249999996},{"hi":3.6401660137676157,"lo":3.2822856175931587},{"hi":1.0127035653357306,"lo":0.9995564119891851}],[{"hi":1.473848741000534,"lo":1.4468506791337252},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":3.502646264659688,"lo":3.493258915723263},{"hi":1.007671988209645,"lo":1.0072931811887573}],[{"hi":1.4493050483943635,"lo":1.422306986527555},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":3.7247861419760637,"lo":3.348294780491154},{"hi":1.0174295393030774,"lo":1.0009144161334316}],[{"hi":1.4247613557881933,"lo":1.3977632939213849},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":3.8073795290833288,"lo":3.418729642914614},{"hi":1.0225200927627385,"lo":1.0030257548835395}],[{"hi":1.4002176631820231,"lo":1.3732196013152147},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":3.8946947961295173,"lo":3.49006942694006},{"hi":1.028199443568191,"lo":1.0057376336396375}],[{"hi":1.375673970575853,"lo":1.3486759087090445},{"hi":1.2210937500000043,"lo":1.1695312499999957},{"hi":3.87166331027659,"lo":3.640298873905164},{"hi":1.0273902557610888,"lo":1.01376436215389}],[{"hi":1.375673970575853,"lo":1.3486759087090445},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":3.8957961688819496,"lo":3.701331780292066},{"hi":1.029013813247996,"lo":1.0172239550316848}],[{"hi":1.3511302779696823,"lo":1.3241322161028748},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":4.090801932769105,"lo":3.6361494005639234},{"hi":1.0413105004934247,"lo":1.0129561162735028}],[{"hi":1.3265865853635121,"lo":1.2995885234967046},{"hi":1.2679687500000045,"lo":1.2164062499999955},{"hi":4.118101697326011,"lo":3.76950040159438},{"hi":1.0431875063898983,"lo":1.0211562404158276}],[{"hi":1.3265865853635121,"lo":1.2995885234967046},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":4.066812360228609,"lo":3.919893724903192},{"hi":1.03992180261314,"lo":1.03067713159169}],[{"hi":1.302042892757342,"lo":1.2750448308905344},{"hi":1.3148437500000048,"lo":1.2632812499999952},{"hi":4.342704939940731,"lo":3.78493821687025},{"hi":1.0567311243942534,"lo":1.0225524740780978}]]}],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"definition":1,"kind":"IntersectionSide","side":"A"},{"definition":0,"kind":"IntersectionSide","side":"A"},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]},{"definition":1,"kind":"IntersectionSide","side":"B"},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0430596642677912]},{"direction":[0.0,0.9999999999999999],"kind":"Line2","origin":[3.211768032612946,1.0430596642677912]},{"definition":0,"kind":"IntersectionSide","side":"B"},{"direction":[0.0,0.9999999999999999],"kind":"Line2","origin":[-3.07141727456664,1.0430596642677912]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-6.283185307179586,1.0430596642677912]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[-3.8285686989269494e-16,0.0,-1.0],"y":[-1.0,-0.0,3.8285686989269494e-16],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[-3.8285686989269494e-16,0.0,-1.0],"y":[1.0,0.0,-3.8285686989269494e-16],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[-3.8285686989269494e-16,0.0,-1.0],"y":[-1.0,-0.0,3.8285686989269494e-16],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,1.5,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,1.0],"z":[1.0,0.0,0.0]},"kind":"Cylinder","radius":0.35}]},"id":"matrix-cylinder-cross","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":6},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":7},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.2160342040069305,"lo":0.0}},"halfedge":5,"id":3,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.21603420400693,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":true,"geometry":{"curve":5,"kind":"Curve","range":{"hi":4.119598250712539,"lo":3.211768032612946}},"halfedge":9,"id":5,"tolerance":1e-8,"twin_halfedge":13},{"chart_seam":true,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.9566391544284132,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":12}],"faces":[{"id":0,"key":"host:lateral","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1,2],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"host:lower_cap","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":2,"key":"host:upper_cap","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":3,"key":"cutter:tunnel","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-cylinder-cross","entity":"matrix-cylinder-cross","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":4.119598250712539,"lo":-3.07141727456664},{"hi":3.0,"lo":1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":6,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":7,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":4,"face":0,"from":3,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":3,"twin":8,"wire_ref":null},{"edge":3,"face":0,"from":2,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":2,"twin":11,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":3,"next":6,"prev":6,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":4,"next":7,"prev":7,"to":1,"twin":2,"wire_ref":null},{"edge":4,"face":3,"from":3,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":5,"next":13,"prev":9,"to":3,"twin":4,"wire_ref":null},{"edge":5,"face":3,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":5,"next":8,"prev":10,"to":3,"twin":13,"wire_ref":null},{"edge":6,"face":3,"from":2,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":5,"next":9,"prev":11,"to":4,"twin":12,"wire_ref":null},{"edge":3,"face":3,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":5,"next":10,"prev":12,"to":2,"twin":5,"wire_ref":null},{"edge":6,"face":3,"from":4,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":5,"next":11,"prev":13,"to":2,"twin":10,"wire_ref":null},{"edge":5,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":5,"next":12,"prev":8,"to":4,"twin":9,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":4},{"face_ref":0,"id":2,"is_hole":true,"start_halfedge":5},{"face_ref":1,"id":3,"is_hole":false,"start_halfedge":6},{"face_ref":2,"id":4,"is_hole":false,"start_halfedge":7},{"face_ref":3,"id":5,"is_hole":false,"start_halfedge":8}],"shells":[{"faces":[0,1,2,3],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-3.8285686989269494e-16,0.0,-1.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[-3.8285686989269494e-16,3.0,-1.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":5,"position":[0.9996988186962042,1.150861448564683,-0.024541228522912288],"tolerance":1e-8},{"id":3,"outgoing_halfedge":4,"position":[-0.9569403357322088,1.304462775529828,-0.2902846772544624],"tolerance":1e-8},{"id":4,"outgoing_halfedge":9,"position":[-0.9569403357322088,1.1508614485586484,-0.024541228523336397],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-empty.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-empty.json new file mode 100644 index 0000000..a432b7b --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-empty.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-inner","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-inner","entity":"matrix-cylinder-inner","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-inner","entity":"matrix-cylinder-inner","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-inner","entity":"matrix-cylinder-inner","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.4,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[0.4,3.0,0.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,3.0,0.0],"tolerance":1e-8}],"wires":[]}},"handlers":["boolean_cylinders"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[],"intersections":[],"pcurves":[],"surfaces":[]},"id":"matrix-cylinder-empty","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[],"topology":{"edges":[],"faces":[],"halfedges":[],"loops":[],"shells":[],"vertices":[],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-offset.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-offset.json new file mode 100644 index 0000000..52910c8 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-offset.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,3.0,0.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.35,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[0.35,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.75,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.35,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[0.35,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.35,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-offset","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-offset","entity":"matrix-cylinder-offset","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-offset","entity":"matrix-cylinder-offset","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-offset","entity":"matrix-cylinder-offset","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.75,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[0.75,3.0,0.0],"tolerance":1e-8}],"wires":[]}},"handlers":["boolean_cylinders"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.35,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[0.35,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.75,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.35,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.35,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.35,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-offset","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":4,"id":2,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":true,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":3},{"chart_seam":true,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":7}],"faces":[{"id":0,"key":"outer","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"inner","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-cylinder-offset","entity":"matrix-cylinder-offset","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":2,"key":"lower_cap","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[3],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":3,"key":"upper_cap","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[5],"outer":4,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":8,"wire_ref":null},{"edge":4,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":2,"face":1,"from":2,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":2,"twin":9,"wire_ref":null},{"edge":5,"face":1,"from":2,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":3,"twin":7,"wire_ref":null},{"edge":3,"face":1,"from":3,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":3,"twin":11,"wire_ref":null},{"edge":5,"face":1,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":2,"twin":5,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":8,"prev":8,"to":0,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":2,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":3,"next":9,"prev":9,"to":2,"twin":4,"wire_ref":null},{"edge":1,"face":3,"from":1,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":4,"next":10,"prev":10,"to":1,"twin":2,"wire_ref":null},{"edge":3,"face":3,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":5,"next":11,"prev":11,"to":3,"twin":6,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":2,"id":3,"is_hole":true,"start_halfedge":9},{"face_ref":3,"id":4,"is_hole":false,"start_halfedge":10},{"face_ref":3,"id":5,"is_hole":true,"start_halfedge":11}],"shells":[{"faces":[0,1,2,3],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,3.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":4,"position":[0.75,0.0,0.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":6,"position":[0.75,3.0,0.0],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-sliced.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-sliced.json new file mode 100644 index 0000000..802ff49 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/cylinder-sliced.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-host","entity":"matrix-cylinder-host","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,3.0,0.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.5,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[0.0,3.5,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.5,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.5,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[0.0,0.5,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.5,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"matrix-cylinder-shifted","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-shifted","entity":"matrix-cylinder-shifted","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-shifted","entity":"matrix-cylinder-shifted","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-cylinder-shifted","entity":"matrix-cylinder-shifted","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.4,0.5,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[0.4,3.5,0.0],"tolerance":1e-8}],"wires":[]}},"handlers":[],"operation":"subtraction","result":{"error":{"CoverageGap":{"families":["generic analytic solid","intersecting analytic solid"]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/planar-extrusions.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/planar-extrusions.json new file mode 100644 index 0000000..8dcff46 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/planar-extrusions.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,-0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-0.25]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,0.0,-0.25]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,-0.25]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,-0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,0.25],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.25],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,-0.25],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,-0.25],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"matrix-wall","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-8,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[-2.0,0.0,0.25],"tolerance":1e-8},{"id":1,"outgoing_halfedge":7,"position":[2.0,0.0,0.25],"tolerance":1e-8},{"id":2,"outgoing_halfedge":6,"position":[2.0,0.0,-0.25],"tolerance":1e-8},{"id":3,"outgoing_halfedge":5,"position":[-2.0,0.0,-0.25],"tolerance":1e-8},{"id":4,"outgoing_halfedge":0,"position":[-2.0,3.0,0.25],"tolerance":1e-8},{"id":5,"outgoing_halfedge":1,"position":[2.0,3.0,0.25],"tolerance":1e-8},{"id":6,"outgoing_halfedge":2,"position":[2.0,3.0,-0.25],"tolerance":1e-8},{"id":7,"outgoing_halfedge":3,"position":[-2.0,3.0,-0.25],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,0.0,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,3.0,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.4,0.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.4,0.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.4,3.0,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,0.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.4,0.0,-0.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.4,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,-0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-0.4,0.0,-0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-0.4,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.4,0.0,-0.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.4,-0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.4,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.4,0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.4,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.4,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.4,-0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.4,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.4,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,0.0,0.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.4,0.0,0.5],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.4,0.0,-0.5],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,0.0,-0.5],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"matrix-profile-cutter","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-8,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-profile-cutter","entity":"matrix-profile-cutter","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.40000001,"lo":-0.40000001},{"hi":0.50000001,"lo":-0.50000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-profile-cutter","entity":"matrix-profile-cutter","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.40000001,"lo":-0.40000001},{"hi":0.50000001,"lo":-0.50000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-profile-cutter","entity":"matrix-profile-cutter","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.8000000100000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-profile-cutter","entity":"matrix-profile-cutter","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-profile-cutter","entity":"matrix-profile-cutter","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.8000000100000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-profile-cutter","entity":"matrix-profile-cutter","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[-0.4,0.0,0.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":7,"position":[0.4,0.0,0.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":6,"position":[0.4,0.0,-0.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":5,"position":[-0.4,0.0,-0.5],"tolerance":1e-8},{"id":4,"outgoing_halfedge":0,"position":[-0.4,3.0,0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":1,"position":[0.4,3.0,0.5],"tolerance":1e-8},{"id":6,"outgoing_halfedge":2,"position":[0.4,3.0,-0.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":3,"position":[-0.4,3.0,-0.5],"tolerance":1e-8}],"wires":[]}},"handlers":["boolean_planar_extrusions"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[2.220446049250313e-16,0.0,-1.0],"kind":"Line","origin":[-0.4,0.0,0.25]},{"direction":[2.220446049250313e-16,0.0,-1.0],"kind":"Line","origin":[-0.4,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.4,0.0,0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[-0.3999999999999999,0.0,-0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[-0.3999999999999999,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.3999999999999999,0.0,-0.25]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,0.0,-0.25]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,-0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.3999999999999999,0.0,0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.3999999999999999,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.3999999999999999,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,-0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-0.25]},{"direction":[-2.220446049250313e-16,0.0,1.0],"kind":"Line","origin":[0.4,0.0,-0.25]},{"direction":[-2.220446049250313e-16,0.0,1.0],"kind":"Line","origin":[0.4,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,-0.25]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[2.220446049250313e-16,1.0],"kind":"Line2","origin":[-0.4,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[-0.3999999999999999,0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[-0.3999999999999999,-0.25]},{"direction":[2.220446049250313e-16,-1.0],"kind":"Line2","origin":[-0.4,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.3999999999999999,-0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.25]},{"direction":[-2.220446049250313e-16,-1.0],"kind":"Line2","origin":[0.4,0.25]},{"direction":[-2.220446049250313e-16,1.0],"kind":"Line2","origin":[0.4,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,-0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.3999999999999999,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.6,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,0.25],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,0.0,0.25],"x":[2.220446049250313e-16,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,2.220446049250313e-16]},"kind":"Plane"},{"frame":{"origin":[-0.3999999999999999,0.0,-0.25],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,-0.25],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.3999999999999999,0.0,0.25],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.25],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,-0.25],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.4,0.0,-0.25],"x":[-2.220446049250313e-16,0.0,1.0],"y":[0.0,1.0,0.0],"z":[-1.0,0.0,-2.220446049250313e-16]},"kind":"Plane"}]},"id":"matrix-planar-extrusions","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0},{"cavity_shells":[],"outer_shell":1}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":31,"id":12,"tolerance":1e-8,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":24,"id":13,"tolerance":1e-8,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":35,"id":14,"tolerance":1e-8,"twin_halfedge":45},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":30,"id":15,"tolerance":1e-8,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":25,"id":16,"tolerance":1e-8,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":33,"id":17,"tolerance":1e-8,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":29,"id":18,"tolerance":1e-8,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":26,"id":19,"tolerance":1e-8,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":37,"id":20,"tolerance":1e-8,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":28,"id":21,"tolerance":1e-8,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":27,"id":22,"tolerance":1e-8,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":41,"id":23,"tolerance":1e-8,"twin_halfedge":47}],"faces":[{"id":0,"key":"matrix-planar-extrusions:region-0:top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":-0.3999999899999999,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":1,"key":"matrix-planar-extrusions:region-0:bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":-0.3999999899999999,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":2,"key":"matrix-planar-extrusions:region-0:side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.60000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"matrix-planar-extrusions:region-0:side-0-1","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-profile-cutter","entity":"matrix-profile-cutter","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"matrix-planar-extrusions:region-0:side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.60000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"matrix-planar-extrusions:region-0:side-0-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":6,"key":"matrix-planar-extrusions:region-1:top","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":1,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":2.00000001,"lo":0.3999999899999999},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":7,"key":"matrix-planar-extrusions:region-1:bottom","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":1,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":2.00000001,"lo":0.3999999899999999},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":8,"key":"matrix-planar-extrusions:region-1:side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":1,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":1.60000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":9,"key":"matrix-planar-extrusions:region-1:side-0-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":1,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":10,"key":"matrix-planar-extrusions:region-1:side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":1,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":1.60000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":11,"key":"matrix-planar-extrusions:region-1:side-0-3","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-profile-cutter","entity":"matrix-profile-cutter","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":1,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null},{"edge":13,"face":6,"from":12,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":13,"twin":34,"wire_ref":null},{"edge":16,"face":6,"from":13,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":14,"twin":38,"wire_ref":null},{"edge":19,"face":6,"from":14,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Forward"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":15,"twin":42,"wire_ref":null},{"edge":22,"face":6,"from":15,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":12,"twin":46,"wire_ref":null},{"edge":21,"face":7,"from":8,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Reverse"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":11,"twin":44,"wire_ref":null},{"edge":18,"face":7,"from":11,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Reverse"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":10,"twin":40,"wire_ref":null},{"edge":15,"face":7,"from":10,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":9,"twin":36,"wire_ref":null},{"edge":12,"face":7,"from":9,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":8,"twin":32,"wire_ref":null},{"edge":12,"face":8,"from":8,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":9,"twin":31,"wire_ref":null},{"edge":17,"face":8,"from":9,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":13,"twin":39,"wire_ref":null},{"edge":13,"face":8,"from":13,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":12,"twin":24,"wire_ref":null},{"edge":14,"face":8,"from":12,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":8,"twin":45,"wire_ref":null},{"edge":15,"face":9,"from":9,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":10,"twin":30,"wire_ref":null},{"edge":20,"face":9,"from":10,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":14,"twin":43,"wire_ref":null},{"edge":16,"face":9,"from":14,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":13,"twin":25,"wire_ref":null},{"edge":17,"face":9,"from":13,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":9,"twin":33,"wire_ref":null},{"edge":18,"face":10,"from":10,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":11,"twin":29,"wire_ref":null},{"edge":23,"face":10,"from":11,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":15,"twin":47,"wire_ref":null},{"edge":19,"face":10,"from":15,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":14,"twin":26,"wire_ref":null},{"edge":20,"face":10,"from":14,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":10,"twin":37,"wire_ref":null},{"edge":21,"face":11,"from":11,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":8,"twin":28,"wire_ref":null},{"edge":14,"face":11,"from":8,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":12,"twin":35,"wire_ref":null},{"edge":22,"face":11,"from":12,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":15,"twin":27,"wire_ref":null},{"edge":23,"face":11,"from":15,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":11,"twin":41,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true},{"faces":[6,7,8,9,10,11],"id":1,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[-2.0,0.0,0.25],"tolerance":1e-8},{"id":1,"outgoing_halfedge":7,"position":[-0.4,0.0,0.25],"tolerance":1e-8},{"id":2,"outgoing_halfedge":6,"position":[-0.3999999999999999,0.0,-0.25],"tolerance":1e-8},{"id":3,"outgoing_halfedge":5,"position":[-2.0,0.0,-0.25],"tolerance":1e-8},{"id":4,"outgoing_halfedge":0,"position":[-2.0,3.0,0.25],"tolerance":1e-8},{"id":5,"outgoing_halfedge":1,"position":[-0.4,3.0,0.25],"tolerance":1e-8},{"id":6,"outgoing_halfedge":2,"position":[-0.3999999999999999,3.0,-0.25],"tolerance":1e-8},{"id":7,"outgoing_halfedge":3,"position":[-2.0,3.0,-0.25],"tolerance":1e-8},{"id":8,"outgoing_halfedge":28,"position":[0.3999999999999999,0.0,0.25],"tolerance":1e-8},{"id":9,"outgoing_halfedge":31,"position":[2.0,0.0,0.25],"tolerance":1e-8},{"id":10,"outgoing_halfedge":30,"position":[2.0,0.0,-0.25],"tolerance":1e-8},{"id":11,"outgoing_halfedge":29,"position":[0.4,0.0,-0.25],"tolerance":1e-8},{"id":12,"outgoing_halfedge":24,"position":[0.3999999999999999,3.0,0.25],"tolerance":1e-8},{"id":13,"outgoing_halfedge":25,"position":[2.0,3.0,0.25],"tolerance":1e-8},{"id":14,"outgoing_halfedge":26,"position":[2.0,3.0,-0.25],"tolerance":1e-8},{"id":15,"outgoing_halfedge":27,"position":[0.4,3.0,-0.25],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/rotated-box-intersection.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/rotated-box-intersection.json new file mode 100644 index 0000000..6947f53 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/rotated-box-intersection.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-box-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[3.0,3.0,-3.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.8660254037844387,0.0,0.5000000000000001],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.5000000000000001,0.0,-0.8660254037844387],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.5000000000000001,0.0,-0.8660254037844387],"kind":"Line","origin":[2.7320508075688776,0.5,0.5000000000000002]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.7320508075688776,0.5,0.5000000000000002]},{"direction":[0.8660254037844387,0.0,0.5000000000000001],"kind":"Line","origin":[2.0,0.5,-2.2320508075688776]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.5,-2.2320508075688776]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.7320508075688776,0.5,-1.2320508075688772]},{"direction":[0.8660254037844387,0.0,0.5000000000000001],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.5000000000000001,0.0,-0.8660254037844387],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.5000000000000001,0.0,-0.8660254037844387],"kind":"Line","origin":[2.7320508075688776,2.5,0.5000000000000002]},{"direction":[0.8660254037844387,0.0,0.5000000000000001],"kind":"Line","origin":[2.0,2.5,-2.2320508075688776]}],"intersections":[],"pcurves":[{"direction":[1.0,1.5165928555043604e-16],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.5165928555043604e-16,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,1.5165928555043604e-16],"kind":"Line2","origin":[-1.9229626863835643e-16,2.0000000000000004]},{"direction":[-1.5165928555043604e-16,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,-1.487416814333745e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[4.0636983087920364e-17,1.0000000000000002],"kind":"Line2","origin":[2.0000000000000004,8.127396617584078e-17]},{"direction":[1.0,-1.487416814333745e-17],"kind":"Line2","origin":[-2.220446049250313e-16,2.0000000000000004]},{"direction":[4.0636983087920364e-17,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0000000000000004]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0000000000000002,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0000000000000002,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0000000000000004,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.000000000000001]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.4999999999999999,0.0,-0.8660254037844386],"y":[0.8660254037844387,-0.0,0.49999999999999994],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,2.5,-0.5],"x":[0.8660254037844386,0.0,0.5],"y":[0.5000000000000001,0.0,-0.8660254037844387],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.5000000000000001,0.0,-0.8660254037844387],"z":[-0.8660254037844387,-0.0,-0.5000000000000001]},"kind":"Plane"},{"frame":{"origin":[2.7320508075688776,0.5,0.5000000000000002],"x":[0.5000000000000001,0.0,-0.8660254037844386],"y":[-0.0,1.0,0.0],"z":[0.8660254037844387,0.0,0.5000000000000001]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.8660254037844386,0.0,0.5],"y":[-0.0,1.0,0.0],"z":[-0.5000000000000001,-0.0,0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.0,0.5,-2.2320508075688776],"x":[0.0,1.0,0.0],"y":[0.8660254037844387,-0.0,0.5000000000000001],"z":[0.5000000000000001,0.0,-0.8660254037844387]},"kind":"Plane"}]},"id":"matrix-rotated-cutter","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1.0000000192296269e-8},{"hi":2.0000000100000004,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.0000000100000004,"lo":-1.0000000222044605e-8},{"hi":2.0000000100000004,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.0000000100000004,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.0000000100000004,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.000000010000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.5,-0.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[2.7320508075688776,0.5,0.5000000000000002],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[2.0,0.5,-2.2320508075688776],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.7320508075688776,0.5,-1.2320508075688772],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.0,2.5,-0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[2.7320508075688776,2.5,0.5000000000000002],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[2.0,2.5,-2.2320508075688776],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[3.7320508075688776,2.5,-1.2320508075688772],"tolerance":1e-8}],"wires":[]}},"handlers":[],"operation":"intersection","result":{"error":{"CoverageGap":{"families":["generic analytic solid","intersecting analytic solid"]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/rotated-box-union.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/rotated-box-union.json new file mode 100644 index 0000000..ffd4e10 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/rotated-box-union.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-box-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-box-host","entity":"matrix-box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[3.0,3.0,-3.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.8660254037844387,0.0,0.5000000000000001],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.5000000000000001,0.0,-0.8660254037844387],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.5,-0.5]},{"direction":[0.5000000000000001,0.0,-0.8660254037844387],"kind":"Line","origin":[2.7320508075688776,0.5,0.5000000000000002]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.7320508075688776,0.5,0.5000000000000002]},{"direction":[0.8660254037844387,0.0,0.5000000000000001],"kind":"Line","origin":[2.0,0.5,-2.2320508075688776]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.5,-2.2320508075688776]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.7320508075688776,0.5,-1.2320508075688772]},{"direction":[0.8660254037844387,0.0,0.5000000000000001],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.5000000000000001,0.0,-0.8660254037844387],"kind":"Line","origin":[1.0,2.5,-0.5]},{"direction":[0.5000000000000001,0.0,-0.8660254037844387],"kind":"Line","origin":[2.7320508075688776,2.5,0.5000000000000002]},{"direction":[0.8660254037844387,0.0,0.5000000000000001],"kind":"Line","origin":[2.0,2.5,-2.2320508075688776]}],"intersections":[],"pcurves":[{"direction":[1.0,1.5165928555043604e-16],"kind":"Line2","origin":[0.0,0.0]},{"direction":[-1.5165928555043604e-16,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,1.5165928555043604e-16],"kind":"Line2","origin":[-1.9229626863835643e-16,2.0000000000000004]},{"direction":[-1.5165928555043604e-16,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,-1.487416814333745e-17],"kind":"Line2","origin":[0.0,0.0]},{"direction":[4.0636983087920364e-17,1.0000000000000002],"kind":"Line2","origin":[2.0000000000000004,8.127396617584078e-17]},{"direction":[1.0,-1.487416814333745e-17],"kind":"Line2","origin":[-2.220446049250313e-16,2.0000000000000004]},{"direction":[4.0636983087920364e-17,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0000000000000004]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0000000000000002,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0000000000000002,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0000000000000004,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.000000000000001]},{"direction":[0.0,1.0000000000000002],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.4999999999999999,0.0,-0.8660254037844386],"y":[0.8660254037844387,-0.0,0.49999999999999994],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,2.5,-0.5],"x":[0.8660254037844386,0.0,0.5],"y":[0.5000000000000001,0.0,-0.8660254037844387],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.5000000000000001,0.0,-0.8660254037844387],"z":[-0.8660254037844387,-0.0,-0.5000000000000001]},"kind":"Plane"},{"frame":{"origin":[2.7320508075688776,0.5,0.5000000000000002],"x":[0.5000000000000001,0.0,-0.8660254037844386],"y":[-0.0,1.0,0.0],"z":[0.8660254037844387,0.0,0.5000000000000001]},"kind":"Plane"},{"frame":{"origin":[1.0,0.5,-0.5],"x":[0.8660254037844386,0.0,0.5],"y":[-0.0,1.0,0.0],"z":[-0.5000000000000001,-0.0,0.8660254037844387]},"kind":"Plane"},{"frame":{"origin":[2.0,0.5,-2.2320508075688776],"x":[0.0,1.0,0.0],"y":[0.8660254037844387,-0.0,0.5000000000000001],"z":[0.5000000000000001,0.0,-0.8660254037844387]},"kind":"Plane"}]},"id":"matrix-rotated-cutter","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1.0000000192296269e-8},{"hi":2.0000000100000004,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.0000000100000004,"lo":-1.0000000222044605e-8},{"hi":2.0000000100000004,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.0000000100000004,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.0000000100000004,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-rotated-cutter","entity":"matrix-rotated-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.000000010000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.5,-0.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[2.7320508075688776,0.5,0.5000000000000002],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[2.0,0.5,-2.2320508075688776],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[3.7320508075688776,0.5,-1.2320508075688772],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.0,2.5,-0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[2.7320508075688776,2.5,0.5000000000000002],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[2.0,2.5,-2.2320508075688776],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[3.7320508075688776,2.5,-1.2320508075688772],"tolerance":1e-8}],"wires":[]}},"handlers":[],"operation":"union","result":{"error":{"CoverageGap":{"families":["generic analytic solid","intersecting analytic solid"]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/sphere-contained.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/sphere-contained.json new file mode 100644 index 0000000..c6ac940 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/sphere-contained.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":2.0}],"intersections":[],"pcurves":[{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,-1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[-6.283185307179586,0.0],"kind":"Line2","origin":[6.283185307179586,1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Sphere","radius":2.0}]},"id":"matrix-sphere-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":1},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":-1.5707963267948966}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"skin","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-sphere-host","entity":"matrix-sphere-host","face":0,"key":"skin"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.5707963267948966,"lo":-1.5707963267948966}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0}],"shells":[{"faces":[0],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,-2.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[0.0,2.0,0.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":0.5}],"intersections":[],"pcurves":[{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,-1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[-6.283185307179586,0.0],"kind":"Line2","origin":[6.283185307179586,1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Sphere","radius":0.5}]},"id":"matrix-sphere-inner","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":1},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":-1.5707963267948966}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"skin","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-sphere-inner","entity":"matrix-sphere-inner","face":0,"key":"skin"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.5707963267948966,"lo":-1.5707963267948966}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0}],"shells":[{"faces":[0],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,-0.5,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[0.0,0.5,0.0],"tolerance":1e-8}],"wires":[]}},"handlers":["boolean_spheres"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":0.5}],"intersections":[],"pcurves":[{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,-1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[-6.283185307179586,0.0],"kind":"Line2","origin":[6.283185307179586,1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,-1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[-6.283185307179586,0.0],"kind":"Line2","origin":[6.283185307179586,1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Sphere","radius":2.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Sphere","radius":0.5}]},"id":"matrix-sphere-contained","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[1],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":1},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":-1.5707963267948966}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3},{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":2},"halfedge":4,"id":3,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":3},"halfedge":6,"id":4,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":-1.5707963267948966}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":7}],"faces":[{"id":0,"key":"0:skin","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-sphere-host","entity":"matrix-sphere-host","face":0,"key":"skin"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.5707963267948966,"lo":-1.5707963267948966}]}},{"id":1,"key":"1:skin","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-sphere-inner","entity":"matrix-sphere-inner","face":0,"key":"skin"}]},"sense":"Reverse","shell_ref":1,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.5707963267948966,"lo":-1.5707963267948966}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":7,"prev":5,"to":2,"twin":null,"wire_ref":null},{"edge":5,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":4,"prev":6,"to":2,"twin":7,"wire_ref":null},{"edge":4,"face":1,"from":3,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":null,"wire_ref":null},{"edge":5,"face":1,"from":2,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":6,"prev":4,"to":3,"twin":5,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4}],"shells":[{"faces":[0],"id":0,"is_closed":true},{"faces":[1],"id":1,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,-2.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":4,"position":[0.0,-0.5,0.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":5,"position":[0.0,0.5,0.0],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/torus-contained.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/torus-contained.json new file mode 100644 index 0000000..277ef7c --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/torus-contained.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.6},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":0.6}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,6.283185307179586]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Torus","major_radius":2.0,"minor_radius":0.6}]},"id":"matrix-torus-host","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":2},{"chart_seam":true,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"skin","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-torus-host","entity":"matrix-torus-host","face":0,"key":"skin"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":6.283185307179586,"lo":0.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":2,"wire_ref":null},{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":0,"twin":3,"wire_ref":null},{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0}],"shells":[{"faces":[0],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[2.6,0.0,0.0],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.2},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":0.2}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,6.283185307179586]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Torus","major_radius":2.0,"minor_radius":0.2}]},"id":"matrix-torus-inner","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":2},{"chart_seam":true,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"skin","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-torus-inner","entity":"matrix-torus-inner","face":0,"key":"skin"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":6.283185307179586,"lo":0.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":2,"wire_ref":null},{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":0,"twin":3,"wire_ref":null},{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0}],"shells":[{"faces":[0],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[2.2,0.0,0.0],"tolerance":1e-8}],"wires":[]}},"handlers":["torus_containment_boolean"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.6},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":0.6},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.2},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":0.2}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,6.283185307179586]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,6.283185307179586]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Torus","major_radius":2.0,"minor_radius":0.6},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Torus","major_radius":2.0,"minor_radius":0.2}]},"id":"matrix-torus-contained","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[1],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":2},{"chart_seam":true,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":3},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":4,"id":2,"tolerance":1e-8,"twin_halfedge":6},{"chart_seam":true,"geometry":{"curve":3,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":5,"id":3,"tolerance":1e-8,"twin_halfedge":7}],"faces":[{"id":0,"key":"matrix-torus-host:skin","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-torus-host","entity":"matrix-torus-host","face":0,"key":"skin"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":6.283185307179586,"lo":0.0}]}},{"id":1,"key":"matrix-torus-inner:skin","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-torus-inner","entity":"matrix-torus-inner","face":0,"key":"skin"}]},"sense":"Reverse","shell_ref":1,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":6.283185307179586,"lo":0.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":2,"wire_ref":null},{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":0,"twin":3,"wire_ref":null},{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":2,"face":1,"from":1,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":6,"wire_ref":null},{"edge":3,"face":1,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":4,"prev":6,"to":1,"twin":7,"wire_ref":null},{"edge":2,"face":1,"from":1,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":1,"next":5,"prev":7,"to":1,"twin":4,"wire_ref":null},{"edge":3,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":6,"prev":4,"to":1,"twin":5,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4}],"shells":[{"faces":[0],"id":0,"is_closed":true},{"faces":[1],"id":1,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[2.6,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":4,"position":[2.2,0.0,0.0],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/wall-rectilinear.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/wall-rectilinear.json new file mode 100644 index 0000000..2a8b3ee --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/wall-rectilinear.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,-0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-0.25]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,0.0,-0.25]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,-0.25]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,-0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,0.25],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.25],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,-0.25],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,-0.25],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"matrix-wall","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-8,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[-2.0,0.0,0.25],"tolerance":1e-8},{"id":1,"outgoing_halfedge":7,"position":[2.0,0.0,0.25],"tolerance":1e-8},{"id":2,"outgoing_halfedge":6,"position":[2.0,0.0,-0.25],"tolerance":1e-8},{"id":3,"outgoing_halfedge":5,"position":[-2.0,0.0,-0.25],"tolerance":1e-8},{"id":4,"outgoing_halfedge":0,"position":[-2.0,3.0,0.25],"tolerance":1e-8},{"id":5,"outgoing_halfedge":1,"position":[2.0,3.0,0.25],"tolerance":1e-8},{"id":6,"outgoing_halfedge":2,"position":[2.0,3.0,-0.25],"tolerance":1e-8},{"id":7,"outgoing_halfedge":3,"position":[-2.0,3.0,-0.25],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,0.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.4,0.0,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.4,0.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.4,0.0,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.4,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,3.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.4,3.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.4,3.0,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.4,3.0,-0.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.8,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.8]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[-0.4,0.0,0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,3.0,0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,0.0,0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.4,0.0,0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,0.0,0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,0.0,-0.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-wall-cutter","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.8,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":0.8000000100000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.8000000100000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":0.8000000100000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":0.8000000100000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-0.4,0.0,0.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[0.4,0.0,0.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[-0.4,0.0,-0.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[0.4,0.0,-0.5],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-0.4,3.0,0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[0.4,3.0,0.5],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[-0.4,3.0,-0.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[0.4,3.0,-0.5],"tolerance":1e-8}],"wires":[]}},"handlers":["boolean_rectilinear"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-0.3999999999999999,3.0,0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,3.0,0.25]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-2.0,3.0,0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,3.0,-0.25]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-0.3999999999999999,3.0,-0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,0.0,-0.25]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[-2.0,3.0,-0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-2.0,3.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.3999999999999999,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.3999999999999999,3.0,0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.3999999999999999,0.0,0.25]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0,3.0,0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.3999999999999999,3.0,0.25]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.3999999999999999,3.0,0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.3999999999999999,3.0,-0.25]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[2.0,3.0,-0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.3999999999999999,0.0,-0.25]},{"direction":[0.0,-1.0,0.0],"kind":"Line","origin":[0.3999999999999999,3.0,-0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.3999999999999999,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.3999999999999999,3.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,3.0,0.25]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.6,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.4,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.0,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.0,0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[3.0,0.75]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.3999999999999999,-0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.3999999999999999,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.4,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[4.0,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.4,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.4,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.6,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.6,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.6,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.75,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.25,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.25,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.25,3.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,-0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.3999999999999999,0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.3999999999999999,-0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.3999999999999999,-0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.3999999999999999,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.3999999999999999,-0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.3999999999999999,0.25]}],"surfaces":[{"frame":{"origin":[-2.0,0.0,0.25],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,-0.25],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,-0.25],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-0.4,0.0,0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,0.25],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,-0.25],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.4,0.0,0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.25],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"}]},"id":"matrix-wall-rectilinear","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0},{"cavity_shells":[],"outer_shell":1}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":7,"id":7,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":11,"id":9,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":24,"id":12,"tolerance":1e-8,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":25,"id":13,"tolerance":1e-8,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":26,"id":14,"tolerance":1e-8,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":27,"id":15,"tolerance":1e-8,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":28,"id":16,"tolerance":1e-8,"twin_halfedge":41},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":29,"id":17,"tolerance":1e-8,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":1.6,"lo":0.0}},"halfedge":30,"id":18,"tolerance":1e-8,"twin_halfedge":45},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":31,"id":19,"tolerance":1e-8,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":33,"id":20,"tolerance":1e-8,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":35,"id":21,"tolerance":1e-8,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":37,"id":22,"tolerance":1e-8,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":39,"id":23,"tolerance":1e-8,"twin_halfedge":40}],"faces":[{"id":0,"key":"side-0-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.60000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":1,"key":"side-0-2-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.00000001,"lo":2.39999999},{"hi":3.00000001,"lo":-1e-8}]}},{"id":2,"key":"side-0-3-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"left-3","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":0.75000001,"lo":0.24999999}]}},{"id":4,"key":"top-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":-0.3999999899999999,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":5,"key":"bottom-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":-0.3999999899999999,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":6,"key":"side-0-0-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":1,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":4.00000001,"lo":2.39999999},{"hi":3.00000001,"lo":-1e-8}]}},{"id":7,"key":"side-0-2-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":1,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":1.60000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":8,"key":"right-8","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-wall-cutter","entity":"matrix-wall-cutter","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":1,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":0.75000001,"lo":0.24999999},{"hi":3.00000001,"lo":-1e-8}]}},{"id":9,"key":"side-0-1-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":1,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":10,"key":"top-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":1,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":2.00000001,"lo":0.3999999899999999},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":11,"key":"bottom-11","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":1,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":2.00000001,"lo":0.3999999899999999},{"hi":0.25000001,"lo":-0.25000001}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":23,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":12,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":19,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":10,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":17,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":6,"twin":14,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":7,"twin":21,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":8,"wire_ref":null},{"edge":7,"face":2,"from":4,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":7,"twin":7,"wire_ref":null},{"edge":8,"face":2,"from":7,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":0,"twin":20,"wire_ref":null},{"edge":3,"face":2,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":3,"wire_ref":null},{"edge":9,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":4,"twin":18,"wire_ref":null},{"edge":1,"face":3,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":1,"twin":1,"wire_ref":null},{"edge":10,"face":3,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":22,"wire_ref":null},{"edge":5,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":11,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":2,"twin":16,"wire_ref":null},{"edge":11,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":5,"twin":15,"wire_ref":null},{"edge":4,"face":4,"from":5,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":4,"twin":4,"wire_ref":null},{"edge":9,"face":4,"from":4,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":3,"twin":11,"wire_ref":null},{"edge":2,"face":4,"from":3,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":2,"wire_ref":null},{"edge":8,"face":5,"from":0,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":7,"twin":9,"wire_ref":null},{"edge":6,"face":5,"from":7,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":6,"twin":6,"wire_ref":null},{"edge":10,"face":5,"from":6,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":1,"twin":13,"wire_ref":null},{"edge":0,"face":5,"from":1,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":0,"twin":0,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":9,"twin":47,"wire_ref":null},{"edge":13,"face":6,"from":9,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":10,"twin":36,"wire_ref":null},{"edge":14,"face":6,"from":10,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":11,"twin":43,"wire_ref":null},{"edge":15,"face":6,"from":11,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":8,"twin":34,"wire_ref":null},{"edge":16,"face":7,"from":12,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":13,"twin":41,"wire_ref":null},{"edge":17,"face":7,"from":13,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":14,"twin":38,"wire_ref":null},{"edge":18,"face":7,"from":14,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":15,"twin":45,"wire_ref":null},{"edge":19,"face":7,"from":15,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":12,"twin":32,"wire_ref":null},{"edge":19,"face":8,"from":12,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":15,"twin":31,"wire_ref":null},{"edge":20,"face":8,"from":15,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":8,"twin":44,"wire_ref":null},{"edge":15,"face":8,"from":8,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":11,"twin":27,"wire_ref":null},{"edge":21,"face":8,"from":11,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Forward"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":12,"twin":42,"wire_ref":null},{"edge":13,"face":9,"from":10,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":9,"twin":25,"wire_ref":null},{"edge":22,"face":9,"from":9,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":14,"twin":46,"wire_ref":null},{"edge":17,"face":9,"from":14,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":13,"twin":29,"wire_ref":null},{"edge":23,"face":9,"from":13,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":10,"twin":40,"wire_ref":null},{"edge":23,"face":10,"from":10,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":13,"twin":39,"wire_ref":null},{"edge":16,"face":10,"from":13,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Reverse"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":12,"twin":28,"wire_ref":null},{"edge":21,"face":10,"from":12,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":11,"twin":35,"wire_ref":null},{"edge":14,"face":10,"from":11,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Forward"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":10,"twin":26,"wire_ref":null},{"edge":20,"face":11,"from":8,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":15,"twin":33,"wire_ref":null},{"edge":18,"face":11,"from":15,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":14,"twin":30,"wire_ref":null},{"edge":22,"face":11,"from":14,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":9,"twin":37,"wire_ref":null},{"edge":12,"face":11,"from":9,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":8,"twin":24,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44}],"shells":[{"faces":[0,5,3,4,2,1],"id":0,"is_closed":true},{"faces":[6,11,9,10,8,7],"id":1,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[-2.0,0.0,0.25],"tolerance":1e-8},{"id":1,"outgoing_halfedge":1,"position":[-0.3999999999999999,0.0,0.25],"tolerance":1e-8},{"id":2,"outgoing_halfedge":2,"position":[-0.3999999999999999,3.0,0.25],"tolerance":1e-8},{"id":3,"outgoing_halfedge":3,"position":[-2.0,3.0,0.25],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[-2.0,3.0,-0.25],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[-0.3999999999999999,3.0,-0.25],"tolerance":1e-8},{"id":6,"outgoing_halfedge":6,"position":[-0.3999999999999999,0.0,-0.25],"tolerance":1e-8},{"id":7,"outgoing_halfedge":7,"position":[-2.0,0.0,-0.25],"tolerance":1e-8},{"id":8,"outgoing_halfedge":24,"position":[0.3999999999999999,0.0,0.25],"tolerance":1e-8},{"id":9,"outgoing_halfedge":25,"position":[2.0,0.0,0.25],"tolerance":1e-8},{"id":10,"outgoing_halfedge":26,"position":[2.0,3.0,0.25],"tolerance":1e-8},{"id":11,"outgoing_halfedge":27,"position":[0.3999999999999999,3.0,0.25],"tolerance":1e-8},{"id":12,"outgoing_halfedge":28,"position":[0.3999999999999999,3.0,-0.25],"tolerance":1e-8},{"id":13,"outgoing_halfedge":29,"position":[2.0,3.0,-0.25],"tolerance":1e-8},{"id":14,"outgoing_halfedge":30,"position":[2.0,0.0,-0.25],"tolerance":1e-8},{"id":15,"outgoing_halfedge":31,"position":[0.3999999999999999,0.0,-0.25],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/boolean-matrix/wall-round.json b/main/opengeometry/tests/fixtures/cases/boolean-matrix/wall-round.json new file mode 100644 index 0000000..d8d3624 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/boolean-matrix/wall-round.json @@ -0,0 +1 @@ +{"a":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,-0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-0.25]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,0.0,-0.25]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,-0.25]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,-0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,0.25],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.25],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,-0.25],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,-0.25],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"matrix-wall","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-8,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[-2.0,0.0,0.25],"tolerance":1e-8},{"id":1,"outgoing_halfedge":7,"position":[2.0,0.0,0.25],"tolerance":1e-8},{"id":2,"outgoing_halfedge":6,"position":[2.0,0.0,-0.25],"tolerance":1e-8},{"id":3,"outgoing_halfedge":5,"position":[-2.0,0.0,-0.25],"tolerance":1e-8},{"id":4,"outgoing_halfedge":0,"position":[-2.0,3.0,0.25],"tolerance":1e-8},{"id":5,"outgoing_halfedge":1,"position":[2.0,3.0,0.25],"tolerance":1e-8},{"id":6,"outgoing_halfedge":2,"position":[2.0,3.0,-0.25],"tolerance":1e-8},{"id":7,"outgoing_halfedge":3,"position":[-2.0,3.0,-0.25],"tolerance":1e-8}],"wires":[]}},"b":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,1.5,1.0],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Circle","radius":0.5},{"frame":{"origin":[0.0,1.5,-1.0],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Circle","radius":0.5},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.5,1.5,1.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.5,0.0],"axis_b":[0.0,-0.5],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.5,0.0],"axis_b":[0.0,0.5],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,1.5,1.0],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Cylinder","radius":0.5},{"frame":{"origin":[0.0,1.5,1.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,-0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,1.5,-1.0],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"matrix-round-cutter","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-round-cutter","entity":"matrix-round-cutter","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":2.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-round-cutter","entity":"matrix-round-cutter","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.5,"lo":-0.5},{"hi":0.5,"lo":-0.5}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"matrix-round-cutter","entity":"matrix-round-cutter","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.5,"lo":-0.5},{"hi":0.5,"lo":-0.5}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.5,1.5,1.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[0.5,1.5,-1.0],"tolerance":1e-8}],"wires":[]}},"handlers":["generic_two_sided_cutter_band_subtraction"],"operation":"subtraction","result":{"brep":{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.25]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,3.0,0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,-0.25]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-0.25]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,0.0,-0.25]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,3.0,-0.25]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,-0.25]},{"frame":{"origin":[0.0,1.5,0.25],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Circle","radius":0.5},{"frame":{"origin":[0.0,1.5,-0.25],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Circle","radius":0.5}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-2.0,-0.25]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,-0.25]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,0.25]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[0.5,0.0],"axis_b":[0.0,0.5],"kind":"Conic2","origin":[2.0,1.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.0,0.75]},{"axis_a":[-0.5,0.0],"axis_b":[0.0,-0.5],"kind":"Conic2","origin":[2.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.25]}],"surfaces":[{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,0.25],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.25],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,-0.25],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,-0.25],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,1.5,1.0],"x":[1.0,0.0,0.0],"y":[0.0,-1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Cylinder","radius":0.5}]},"id":"matrix-wall-round","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":24,"id":12,"tolerance":1e-8,"twin_halfedge":25},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":26,"id":13,"tolerance":1e-8,"twin_halfedge":27}],"faces":[{"id":0,"key":"matrix-wall:top","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":1,"key":"matrix-wall:bottom","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-2.00000001},{"hi":0.25000001,"lo":-0.25000001}]}},{"id":2,"key":"matrix-wall:side-0-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[6],"outer":2,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":3,"key":"matrix-wall:side-0-1","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"matrix-wall:side-0-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[8],"outer":4,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"matrix-wall:side-0-3","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"matrix-wall","entity":"matrix-wall","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":6,"key":"lateral:through-cut","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"matrix-round-cutter","entity":"matrix-round-cutter","face":0,"key":"lateral"}]},"sense":"Reverse","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[9],"outer":7,"uv_bounds":[{"hi":6.2831915913648935,"lo":-6.2831915913648935},{"hi":1.250000001000001,"lo":0.749999998999999}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null},{"edge":12,"face":2,"from":8,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":24,"prev":24,"to":8,"twin":25,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":7,"next":25,"prev":25,"to":8,"twin":24,"wire_ref":null},{"edge":13,"face":4,"from":9,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Forward"},"id":26,"loop_ref":8,"next":26,"prev":26,"to":9,"twin":27,"wire_ref":null},{"edge":13,"face":6,"from":9,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":9,"next":27,"prev":27,"to":9,"twin":26,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":2,"id":6,"is_hole":true,"start_halfedge":24},{"face_ref":6,"id":7,"is_hole":false,"start_halfedge":25},{"face_ref":4,"id":8,"is_hole":true,"start_halfedge":26},{"face_ref":6,"id":9,"is_hole":true,"start_halfedge":27}],"shells":[{"faces":[0,1,2,3,4,5,6],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[-2.0,0.0,0.25],"tolerance":1e-8},{"id":1,"outgoing_halfedge":7,"position":[2.0,0.0,0.25],"tolerance":1e-8},{"id":2,"outgoing_halfedge":6,"position":[2.0,0.0,-0.25],"tolerance":1e-8},{"id":3,"outgoing_halfedge":5,"position":[-2.0,0.0,-0.25],"tolerance":1e-8},{"id":4,"outgoing_halfedge":0,"position":[-2.0,3.0,0.25],"tolerance":1e-8},{"id":5,"outgoing_halfedge":1,"position":[2.0,3.0,0.25],"tolerance":1e-8},{"id":6,"outgoing_halfedge":2,"position":[2.0,3.0,-0.25],"tolerance":1e-8},{"id":7,"outgoing_halfedge":3,"position":[-2.0,3.0,-0.25],"tolerance":1e-8},{"id":8,"outgoing_halfedge":24,"position":[0.5,1.5,0.25],"tolerance":1e-8},{"id":9,"outgoing_halfedge":26,"position":[0.5,1.5,-0.25],"tolerance":1e-8}],"wires":[]}}}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-cavity.brep.json b/main/opengeometry/tests/fixtures/cases/box-cavity.brep.json new file mode 100644 index 0000000..839908a --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cavity.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"box-cavity","revision":1,"geometry":{"surfaces":[{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[1.0,1.0,-1.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[1.0,1.0,-1.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[1.0,1.0,-1.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[1.0,2.0,-1.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[1.0,1.0,-2.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[2.0,1.0,-1.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}}],"curves":[{"kind":"Line","origin":[0.0,1.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,-1.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,1.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-1.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,2.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,1.0,-1.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,1.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,1.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,2.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,2.0,-1.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,2.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,2.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,3.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,3.0,-1.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,1.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,-2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,-2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,0.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,2.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,1.0,-2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,1.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.0,1.0,-2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,2.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.0,1.0,-1.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,3.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,2.0,-2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,3.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,3.0,-2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,1.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,1.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,0.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,0.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,2.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,1.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,2.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,1.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,3.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,2.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,3.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,2.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,3.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,1.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,0.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,0.0,-1.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,1.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,2.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,2.0,-1.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,1.0,-1.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,1.0,-1.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,2.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,3.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.0,3.0,-1.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,0.0,-2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,0.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,1.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.0,1.0,-2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,2.0,-2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,2.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,3.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.0,3.0,-2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,1.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,0.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,0.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,0.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,1.0,-2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,2.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,1.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,3.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,2.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[2.0,3.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,-1.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,1.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[2.0,1.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,0.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,0.0,-1.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,1.0,-1.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,2.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,1.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[2.0,2.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,2.0,-1.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,2.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[2.0,3.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,3.0,-1.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,-2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,1.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,0.0,-2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,1.0,-2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,2.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,2.0,-2.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,3.0,-1.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,3.0,-2.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,1.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,1.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[2.0,0.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,1.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,2.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,2.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,2.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,3.0,-2.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,3.0,-3.0],"direction":[1.0,0.0,0.0]}],"pcurves":[{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,1.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,2.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,2.0],"direction":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[0.0,1.0,0.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[0.0,1.0,-1.0],"outgoing_halfedge":1,"tolerance":1e-8},{"id":2,"position":[0.0,0.0,-1.0],"outgoing_halfedge":2,"tolerance":1e-8},{"id":3,"position":[0.0,0.0,0.0],"outgoing_halfedge":3,"tolerance":1e-8},{"id":4,"position":[1.0,0.0,0.0],"outgoing_halfedge":4,"tolerance":1e-8},{"id":5,"position":[1.0,1.0,0.0],"outgoing_halfedge":5,"tolerance":1e-8},{"id":6,"position":[1.0,0.0,-1.0],"outgoing_halfedge":9,"tolerance":1e-8},{"id":7,"position":[0.0,2.0,0.0],"outgoing_halfedge":12,"tolerance":1e-8},{"id":8,"position":[0.0,2.0,-1.0],"outgoing_halfedge":13,"tolerance":1e-8},{"id":9,"position":[1.0,2.0,0.0],"outgoing_halfedge":17,"tolerance":1e-8},{"id":10,"position":[0.0,3.0,0.0],"outgoing_halfedge":20,"tolerance":1e-8},{"id":11,"position":[0.0,3.0,-1.0],"outgoing_halfedge":21,"tolerance":1e-8},{"id":12,"position":[1.0,3.0,0.0],"outgoing_halfedge":25,"tolerance":1e-8},{"id":13,"position":[1.0,3.0,-1.0],"outgoing_halfedge":30,"tolerance":1e-8},{"id":14,"position":[0.0,1.0,-2.0],"outgoing_halfedge":33,"tolerance":1e-8},{"id":15,"position":[0.0,0.0,-2.0],"outgoing_halfedge":34,"tolerance":1e-8},{"id":16,"position":[1.0,0.0,-2.0],"outgoing_halfedge":37,"tolerance":1e-8},{"id":17,"position":[0.0,2.0,-2.0],"outgoing_halfedge":41,"tolerance":1e-8},{"id":18,"position":[1.0,1.0,-1.0],"outgoing_halfedge":44,"tolerance":1e-8},{"id":19,"position":[1.0,1.0,-2.0],"outgoing_halfedge":45,"tolerance":1e-8},{"id":20,"position":[1.0,2.0,-2.0],"outgoing_halfedge":46,"tolerance":1e-8},{"id":21,"position":[1.0,2.0,-1.0],"outgoing_halfedge":47,"tolerance":1e-8},{"id":22,"position":[0.0,3.0,-2.0],"outgoing_halfedge":49,"tolerance":1e-8},{"id":23,"position":[1.0,3.0,-2.0],"outgoing_halfedge":54,"tolerance":1e-8},{"id":24,"position":[0.0,1.0,-3.0],"outgoing_halfedge":57,"tolerance":1e-8},{"id":25,"position":[0.0,0.0,-3.0],"outgoing_halfedge":58,"tolerance":1e-8},{"id":26,"position":[1.0,1.0,-3.0],"outgoing_halfedge":62,"tolerance":1e-8},{"id":27,"position":[1.0,0.0,-3.0],"outgoing_halfedge":63,"tolerance":1e-8},{"id":28,"position":[0.0,2.0,-3.0],"outgoing_halfedge":69,"tolerance":1e-8},{"id":29,"position":[1.0,2.0,-3.0],"outgoing_halfedge":74,"tolerance":1e-8},{"id":30,"position":[0.0,3.0,-3.0],"outgoing_halfedge":77,"tolerance":1e-8},{"id":31,"position":[1.0,3.0,-3.0],"outgoing_halfedge":82,"tolerance":1e-8},{"id":32,"position":[2.0,0.0,0.0],"outgoing_halfedge":88,"tolerance":1e-8},{"id":33,"position":[2.0,1.0,0.0],"outgoing_halfedge":89,"tolerance":1e-8},{"id":34,"position":[2.0,0.0,-1.0],"outgoing_halfedge":93,"tolerance":1e-8},{"id":35,"position":[2.0,2.0,0.0],"outgoing_halfedge":97,"tolerance":1e-8},{"id":36,"position":[2.0,2.0,-1.0],"outgoing_halfedge":102,"tolerance":1e-8},{"id":37,"position":[2.0,1.0,-1.0],"outgoing_halfedge":103,"tolerance":1e-8},{"id":38,"position":[2.0,3.0,0.0],"outgoing_halfedge":105,"tolerance":1e-8},{"id":39,"position":[2.0,3.0,-1.0],"outgoing_halfedge":110,"tolerance":1e-8},{"id":40,"position":[2.0,0.0,-2.0],"outgoing_halfedge":113,"tolerance":1e-8},{"id":41,"position":[2.0,1.0,-2.0],"outgoing_halfedge":118,"tolerance":1e-8},{"id":42,"position":[2.0,2.0,-2.0],"outgoing_halfedge":121,"tolerance":1e-8},{"id":43,"position":[2.0,3.0,-2.0],"outgoing_halfedge":126,"tolerance":1e-8},{"id":44,"position":[2.0,1.0,-3.0],"outgoing_halfedge":130,"tolerance":1e-8},{"id":45,"position":[2.0,0.0,-3.0],"outgoing_halfedge":131,"tolerance":1e-8},{"id":46,"position":[2.0,2.0,-3.0],"outgoing_halfedge":142,"tolerance":1e-8},{"id":47,"position":[2.0,3.0,-3.0],"outgoing_halfedge":146,"tolerance":1e-8},{"id":48,"position":[3.0,0.0,0.0],"outgoing_halfedge":152,"tolerance":1e-8},{"id":49,"position":[3.0,0.0,-1.0],"outgoing_halfedge":153,"tolerance":1e-8},{"id":50,"position":[3.0,1.0,-1.0],"outgoing_halfedge":154,"tolerance":1e-8},{"id":51,"position":[3.0,1.0,0.0],"outgoing_halfedge":155,"tolerance":1e-8},{"id":52,"position":[3.0,2.0,-1.0],"outgoing_halfedge":166,"tolerance":1e-8},{"id":53,"position":[3.0,2.0,0.0],"outgoing_halfedge":167,"tolerance":1e-8},{"id":54,"position":[3.0,3.0,-1.0],"outgoing_halfedge":174,"tolerance":1e-8},{"id":55,"position":[3.0,3.0,0.0],"outgoing_halfedge":175,"tolerance":1e-8},{"id":56,"position":[3.0,0.0,-2.0],"outgoing_halfedge":185,"tolerance":1e-8},{"id":57,"position":[3.0,1.0,-2.0],"outgoing_halfedge":186,"tolerance":1e-8},{"id":58,"position":[3.0,2.0,-2.0],"outgoing_halfedge":198,"tolerance":1e-8},{"id":59,"position":[3.0,3.0,-2.0],"outgoing_halfedge":202,"tolerance":1e-8},{"id":60,"position":[3.0,0.0,-3.0],"outgoing_halfedge":209,"tolerance":1e-8},{"id":61,"position":[3.0,1.0,-3.0],"outgoing_halfedge":210,"tolerance":1e-8},{"id":62,"position":[3.0,2.0,-3.0],"outgoing_halfedge":222,"tolerance":1e-8},{"id":63,"position":[3.0,3.0,-3.0],"outgoing_halfedge":230,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":1.0}},"halfedge":0,"twin_halfedge":14,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":1.0}},"halfedge":1,"twin_halfedge":35,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":1.0}},"halfedge":2,"twin_halfedge":11,"tolerance":1e-8,"chart_seam":false},{"id":3,"geometry":{"kind":"Curve","curve":3,"range":{"lo":0.0,"hi":1.0}},"halfedge":3,"twin_halfedge":6,"tolerance":1e-8,"chart_seam":false},{"id":4,"geometry":{"kind":"Curve","curve":4,"range":{"lo":0.0,"hi":1.0}},"halfedge":4,"twin_halfedge":90,"tolerance":1e-8,"chart_seam":false},{"id":5,"geometry":{"kind":"Curve","curve":5,"range":{"lo":0.0,"hi":1.0}},"halfedge":5,"twin_halfedge":19,"tolerance":1e-8,"chart_seam":false},{"id":6,"geometry":{"kind":"Curve","curve":6,"range":{"lo":0.0,"hi":1.0}},"halfedge":7,"twin_halfedge":10,"tolerance":1e-8,"chart_seam":false},{"id":7,"geometry":{"kind":"Curve","curve":7,"range":{"lo":0.0,"hi":1.0}},"halfedge":8,"twin_halfedge":38,"tolerance":1e-8,"chart_seam":false},{"id":8,"geometry":{"kind":"Curve","curve":8,"range":{"lo":0.0,"hi":1.0}},"halfedge":9,"twin_halfedge":95,"tolerance":1e-8,"chart_seam":false},{"id":9,"geometry":{"kind":"Curve","curve":9,"range":{"lo":0.0,"hi":1.0}},"halfedge":12,"twin_halfedge":22,"tolerance":1e-8,"chart_seam":false},{"id":10,"geometry":{"kind":"Curve","curve":10,"range":{"lo":0.0,"hi":1.0}},"halfedge":13,"twin_halfedge":43,"tolerance":1e-8,"chart_seam":false},{"id":11,"geometry":{"kind":"Curve","curve":11,"range":{"lo":0.0,"hi":1.0}},"halfedge":15,"twin_halfedge":18,"tolerance":1e-8,"chart_seam":false},{"id":12,"geometry":{"kind":"Curve","curve":12,"range":{"lo":0.0,"hi":1.0}},"halfedge":16,"twin_halfedge":98,"tolerance":1e-8,"chart_seam":false},{"id":13,"geometry":{"kind":"Curve","curve":13,"range":{"lo":0.0,"hi":1.0}},"halfedge":17,"twin_halfedge":27,"tolerance":1e-8,"chart_seam":false},{"id":14,"geometry":{"kind":"Curve","curve":14,"range":{"lo":0.0,"hi":1.0}},"halfedge":20,"twin_halfedge":31,"tolerance":1e-8,"chart_seam":false},{"id":15,"geometry":{"kind":"Curve","curve":15,"range":{"lo":0.0,"hi":1.0}},"halfedge":21,"twin_halfedge":51,"tolerance":1e-8,"chart_seam":false},{"id":16,"geometry":{"kind":"Curve","curve":16,"range":{"lo":0.0,"hi":1.0}},"halfedge":23,"twin_halfedge":26,"tolerance":1e-8,"chart_seam":false},{"id":17,"geometry":{"kind":"Curve","curve":17,"range":{"lo":0.0,"hi":1.0}},"halfedge":24,"twin_halfedge":106,"tolerance":1e-8,"chart_seam":false},{"id":18,"geometry":{"kind":"Curve","curve":18,"range":{"lo":0.0,"hi":1.0}},"halfedge":25,"twin_halfedge":28,"tolerance":1e-8,"chart_seam":false},{"id":19,"geometry":{"kind":"Curve","curve":19,"range":{"lo":0.0,"hi":1.0}},"halfedge":29,"twin_halfedge":111,"tolerance":1e-8,"chart_seam":false},{"id":20,"geometry":{"kind":"Curve","curve":20,"range":{"lo":0.0,"hi":1.0}},"halfedge":30,"twin_halfedge":52,"tolerance":1e-8,"chart_seam":false},{"id":21,"geometry":{"kind":"Curve","curve":21,"range":{"lo":0.0,"hi":1.0}},"halfedge":32,"twin_halfedge":42,"tolerance":1e-8,"chart_seam":false},{"id":22,"geometry":{"kind":"Curve","curve":22,"range":{"lo":0.0,"hi":1.0}},"halfedge":33,"twin_halfedge":59,"tolerance":1e-8,"chart_seam":false},{"id":23,"geometry":{"kind":"Curve","curve":23,"range":{"lo":0.0,"hi":1.0}},"halfedge":34,"twin_halfedge":39,"tolerance":1e-8,"chart_seam":false},{"id":24,"geometry":{"kind":"Curve","curve":24,"range":{"lo":0.0,"hi":1.0}},"halfedge":36,"twin_halfedge":66,"tolerance":1e-8,"chart_seam":false},{"id":25,"geometry":{"kind":"Curve","curve":25,"range":{"lo":0.0,"hi":1.0}},"halfedge":37,"twin_halfedge":115,"tolerance":1e-8,"chart_seam":false},{"id":26,"geometry":{"kind":"Curve","curve":26,"range":{"lo":0.0,"hi":1.0}},"halfedge":40,"twin_halfedge":50,"tolerance":1e-8,"chart_seam":false},{"id":27,"geometry":{"kind":"Curve","curve":27,"range":{"lo":0.0,"hi":1.0}},"halfedge":41,"twin_halfedge":71,"tolerance":1e-8,"chart_seam":false},{"id":28,"geometry":{"kind":"Curve","curve":28,"range":{"lo":0.0,"hi":1.0}},"halfedge":44,"twin_halfedge":119,"tolerance":1e-8,"chart_seam":false},{"id":29,"geometry":{"kind":"Curve","curve":29,"range":{"lo":0.0,"hi":1.0}},"halfedge":45,"twin_halfedge":138,"tolerance":1e-8,"chart_seam":false},{"id":30,"geometry":{"kind":"Curve","curve":30,"range":{"lo":0.0,"hi":1.0}},"halfedge":46,"twin_halfedge":123,"tolerance":1e-8,"chart_seam":false},{"id":31,"geometry":{"kind":"Curve","curve":31,"range":{"lo":0.0,"hi":1.0}},"halfedge":47,"twin_halfedge":100,"tolerance":1e-8,"chart_seam":false},{"id":32,"geometry":{"kind":"Curve","curve":32,"range":{"lo":0.0,"hi":1.0}},"halfedge":48,"twin_halfedge":55,"tolerance":1e-8,"chart_seam":false},{"id":33,"geometry":{"kind":"Curve","curve":33,"range":{"lo":0.0,"hi":1.0}},"halfedge":49,"twin_halfedge":79,"tolerance":1e-8,"chart_seam":false},{"id":34,"geometry":{"kind":"Curve","curve":34,"range":{"lo":0.0,"hi":1.0}},"halfedge":53,"twin_halfedge":127,"tolerance":1e-8,"chart_seam":false},{"id":35,"geometry":{"kind":"Curve","curve":35,"range":{"lo":0.0,"hi":1.0}},"halfedge":54,"twin_halfedge":84,"tolerance":1e-8,"chart_seam":false},{"id":36,"geometry":{"kind":"Curve","curve":36,"range":{"lo":0.0,"hi":1.0}},"halfedge":56,"twin_halfedge":70,"tolerance":1e-8,"chart_seam":false},{"id":37,"geometry":{"kind":"Curve","curve":37,"range":{"lo":0.0,"hi":1.0}},"halfedge":57,"twin_halfedge":60,"tolerance":1e-8,"chart_seam":false},{"id":38,"geometry":{"kind":"Curve","curve":38,"range":{"lo":0.0,"hi":1.0}},"halfedge":58,"twin_halfedge":67,"tolerance":1e-8,"chart_seam":false},{"id":39,"geometry":{"kind":"Curve","curve":39,"range":{"lo":0.0,"hi":1.0}},"halfedge":61,"twin_halfedge":75,"tolerance":1e-8,"chart_seam":false},{"id":40,"geometry":{"kind":"Curve","curve":40,"range":{"lo":0.0,"hi":1.0}},"halfedge":62,"twin_halfedge":128,"tolerance":1e-8,"chart_seam":false},{"id":41,"geometry":{"kind":"Curve","curve":41,"range":{"lo":0.0,"hi":1.0}},"halfedge":63,"twin_halfedge":64,"tolerance":1e-8,"chart_seam":false},{"id":42,"geometry":{"kind":"Curve","curve":42,"range":{"lo":0.0,"hi":1.0}},"halfedge":65,"twin_halfedge":135,"tolerance":1e-8,"chart_seam":false},{"id":43,"geometry":{"kind":"Curve","curve":43,"range":{"lo":0.0,"hi":1.0}},"halfedge":68,"twin_halfedge":78,"tolerance":1e-8,"chart_seam":false},{"id":44,"geometry":{"kind":"Curve","curve":44,"range":{"lo":0.0,"hi":1.0}},"halfedge":69,"twin_halfedge":72,"tolerance":1e-8,"chart_seam":false},{"id":45,"geometry":{"kind":"Curve","curve":45,"range":{"lo":0.0,"hi":1.0}},"halfedge":73,"twin_halfedge":83,"tolerance":1e-8,"chart_seam":false},{"id":46,"geometry":{"kind":"Curve","curve":46,"range":{"lo":0.0,"hi":1.0}},"halfedge":74,"twin_halfedge":140,"tolerance":1e-8,"chart_seam":false},{"id":47,"geometry":{"kind":"Curve","curve":47,"range":{"lo":0.0,"hi":1.0}},"halfedge":76,"twin_halfedge":87,"tolerance":1e-8,"chart_seam":false},{"id":48,"geometry":{"kind":"Curve","curve":48,"range":{"lo":0.0,"hi":1.0}},"halfedge":77,"twin_halfedge":80,"tolerance":1e-8,"chart_seam":false},{"id":49,"geometry":{"kind":"Curve","curve":49,"range":{"lo":0.0,"hi":1.0}},"halfedge":81,"twin_halfedge":86,"tolerance":1e-8,"chart_seam":false},{"id":50,"geometry":{"kind":"Curve","curve":50,"range":{"lo":0.0,"hi":1.0}},"halfedge":82,"twin_halfedge":144,"tolerance":1e-8,"chart_seam":false},{"id":51,"geometry":{"kind":"Curve","curve":51,"range":{"lo":0.0,"hi":1.0}},"halfedge":85,"twin_halfedge":151,"tolerance":1e-8,"chart_seam":false},{"id":52,"geometry":{"kind":"Curve","curve":52,"range":{"lo":0.0,"hi":1.0}},"halfedge":88,"twin_halfedge":158,"tolerance":1e-8,"chart_seam":false},{"id":53,"geometry":{"kind":"Curve","curve":53,"range":{"lo":0.0,"hi":1.0}},"halfedge":89,"twin_halfedge":99,"tolerance":1e-8,"chart_seam":false},{"id":54,"geometry":{"kind":"Curve","curve":54,"range":{"lo":0.0,"hi":1.0}},"halfedge":91,"twin_halfedge":94,"tolerance":1e-8,"chart_seam":false},{"id":55,"geometry":{"kind":"Curve","curve":55,"range":{"lo":0.0,"hi":1.0}},"halfedge":92,"twin_halfedge":114,"tolerance":1e-8,"chart_seam":false},{"id":56,"geometry":{"kind":"Curve","curve":56,"range":{"lo":0.0,"hi":1.0}},"halfedge":93,"twin_halfedge":163,"tolerance":1e-8,"chart_seam":false},{"id":57,"geometry":{"kind":"Curve","curve":57,"range":{"lo":0.0,"hi":1.0}},"halfedge":96,"twin_halfedge":170,"tolerance":1e-8,"chart_seam":false},{"id":58,"geometry":{"kind":"Curve","curve":58,"range":{"lo":0.0,"hi":1.0}},"halfedge":97,"twin_halfedge":107,"tolerance":1e-8,"chart_seam":false},{"id":59,"geometry":{"kind":"Curve","curve":59,"range":{"lo":0.0,"hi":1.0}},"halfedge":101,"twin_halfedge":122,"tolerance":1e-8,"chart_seam":false},{"id":60,"geometry":{"kind":"Curve","curve":60,"range":{"lo":0.0,"hi":1.0}},"halfedge":102,"twin_halfedge":195,"tolerance":1e-8,"chart_seam":false},{"id":61,"geometry":{"kind":"Curve","curve":61,"range":{"lo":0.0,"hi":1.0}},"halfedge":103,"twin_halfedge":116,"tolerance":1e-8,"chart_seam":false},{"id":62,"geometry":{"kind":"Curve","curve":62,"range":{"lo":0.0,"hi":1.0}},"halfedge":104,"twin_halfedge":178,"tolerance":1e-8,"chart_seam":false},{"id":63,"geometry":{"kind":"Curve","curve":63,"range":{"lo":0.0,"hi":1.0}},"halfedge":105,"twin_halfedge":108,"tolerance":1e-8,"chart_seam":false},{"id":64,"geometry":{"kind":"Curve","curve":64,"range":{"lo":0.0,"hi":1.0}},"halfedge":109,"twin_halfedge":183,"tolerance":1e-8,"chart_seam":false},{"id":65,"geometry":{"kind":"Curve","curve":65,"range":{"lo":0.0,"hi":1.0}},"halfedge":110,"twin_halfedge":124,"tolerance":1e-8,"chart_seam":false},{"id":66,"geometry":{"kind":"Curve","curve":66,"range":{"lo":0.0,"hi":1.0}},"halfedge":112,"twin_halfedge":134,"tolerance":1e-8,"chart_seam":false},{"id":67,"geometry":{"kind":"Curve","curve":67,"range":{"lo":0.0,"hi":1.0}},"halfedge":113,"twin_halfedge":191,"tolerance":1e-8,"chart_seam":false},{"id":68,"geometry":{"kind":"Curve","curve":68,"range":{"lo":0.0,"hi":1.0}},"halfedge":117,"twin_halfedge":194,"tolerance":1e-8,"chart_seam":false},{"id":69,"geometry":{"kind":"Curve","curve":69,"range":{"lo":0.0,"hi":1.0}},"halfedge":118,"twin_halfedge":139,"tolerance":1e-8,"chart_seam":false},{"id":70,"geometry":{"kind":"Curve","curve":70,"range":{"lo":0.0,"hi":1.0}},"halfedge":120,"twin_halfedge":137,"tolerance":1e-8,"chart_seam":false},{"id":71,"geometry":{"kind":"Curve","curve":71,"range":{"lo":0.0,"hi":1.0}},"halfedge":121,"twin_halfedge":192,"tolerance":1e-8,"chart_seam":false},{"id":72,"geometry":{"kind":"Curve","curve":72,"range":{"lo":0.0,"hi":1.0}},"halfedge":125,"twin_halfedge":207,"tolerance":1e-8,"chart_seam":false},{"id":73,"geometry":{"kind":"Curve","curve":73,"range":{"lo":0.0,"hi":1.0}},"halfedge":126,"twin_halfedge":148,"tolerance":1e-8,"chart_seam":false},{"id":74,"geometry":{"kind":"Curve","curve":74,"range":{"lo":0.0,"hi":1.0}},"halfedge":129,"twin_halfedge":143,"tolerance":1e-8,"chart_seam":false},{"id":75,"geometry":{"kind":"Curve","curve":75,"range":{"lo":0.0,"hi":1.0}},"halfedge":130,"twin_halfedge":212,"tolerance":1e-8,"chart_seam":false},{"id":76,"geometry":{"kind":"Curve","curve":76,"range":{"lo":0.0,"hi":1.0}},"halfedge":131,"twin_halfedge":132,"tolerance":1e-8,"chart_seam":false},{"id":77,"geometry":{"kind":"Curve","curve":77,"range":{"lo":0.0,"hi":1.0}},"halfedge":133,"twin_halfedge":219,"tolerance":1e-8,"chart_seam":false},{"id":78,"geometry":{"kind":"Curve","curve":78,"range":{"lo":0.0,"hi":1.0}},"halfedge":136,"twin_halfedge":193,"tolerance":1e-8,"chart_seam":false},{"id":79,"geometry":{"kind":"Curve","curve":79,"range":{"lo":0.0,"hi":1.0}},"halfedge":141,"twin_halfedge":147,"tolerance":1e-8,"chart_seam":false},{"id":80,"geometry":{"kind":"Curve","curve":80,"range":{"lo":0.0,"hi":1.0}},"halfedge":142,"twin_halfedge":224,"tolerance":1e-8,"chart_seam":false},{"id":81,"geometry":{"kind":"Curve","curve":81,"range":{"lo":0.0,"hi":1.0}},"halfedge":145,"twin_halfedge":150,"tolerance":1e-8,"chart_seam":false},{"id":82,"geometry":{"kind":"Curve","curve":82,"range":{"lo":0.0,"hi":1.0}},"halfedge":146,"twin_halfedge":232,"tolerance":1e-8,"chart_seam":false},{"id":83,"geometry":{"kind":"Curve","curve":83,"range":{"lo":0.0,"hi":1.0}},"halfedge":149,"twin_halfedge":239,"tolerance":1e-8,"chart_seam":false},{"id":84,"geometry":{"kind":"Curve","curve":84,"range":{"lo":0.0,"hi":1.0}},"halfedge":152,"twin_halfedge":161,"tolerance":1e-8,"chart_seam":false},{"id":85,"geometry":{"kind":"Curve","curve":85,"range":{"lo":0.0,"hi":1.0}},"halfedge":153,"twin_halfedge":187,"tolerance":1e-8,"chart_seam":false},{"id":86,"geometry":{"kind":"Curve","curve":86,"range":{"lo":0.0,"hi":1.0}},"halfedge":154,"twin_halfedge":164,"tolerance":1e-8,"chart_seam":false},{"id":87,"geometry":{"kind":"Curve","curve":87,"range":{"lo":0.0,"hi":1.0}},"halfedge":155,"twin_halfedge":156,"tolerance":1e-8,"chart_seam":false},{"id":88,"geometry":{"kind":"Curve","curve":88,"range":{"lo":0.0,"hi":1.0}},"halfedge":157,"twin_halfedge":171,"tolerance":1e-8,"chart_seam":false},{"id":89,"geometry":{"kind":"Curve","curve":89,"range":{"lo":0.0,"hi":1.0}},"halfedge":159,"twin_halfedge":162,"tolerance":1e-8,"chart_seam":false},{"id":90,"geometry":{"kind":"Curve","curve":90,"range":{"lo":0.0,"hi":1.0}},"halfedge":160,"twin_halfedge":190,"tolerance":1e-8,"chart_seam":false},{"id":91,"geometry":{"kind":"Curve","curve":91,"range":{"lo":0.0,"hi":1.0}},"halfedge":165,"twin_halfedge":199,"tolerance":1e-8,"chart_seam":false},{"id":92,"geometry":{"kind":"Curve","curve":92,"range":{"lo":0.0,"hi":1.0}},"halfedge":166,"twin_halfedge":172,"tolerance":1e-8,"chart_seam":false},{"id":93,"geometry":{"kind":"Curve","curve":93,"range":{"lo":0.0,"hi":1.0}},"halfedge":167,"twin_halfedge":168,"tolerance":1e-8,"chart_seam":false},{"id":94,"geometry":{"kind":"Curve","curve":94,"range":{"lo":0.0,"hi":1.0}},"halfedge":169,"twin_halfedge":179,"tolerance":1e-8,"chart_seam":false},{"id":95,"geometry":{"kind":"Curve","curve":95,"range":{"lo":0.0,"hi":1.0}},"halfedge":173,"twin_halfedge":203,"tolerance":1e-8,"chart_seam":false},{"id":96,"geometry":{"kind":"Curve","curve":96,"range":{"lo":0.0,"hi":1.0}},"halfedge":174,"twin_halfedge":181,"tolerance":1e-8,"chart_seam":false},{"id":97,"geometry":{"kind":"Curve","curve":97,"range":{"lo":0.0,"hi":1.0}},"halfedge":175,"twin_halfedge":176,"tolerance":1e-8,"chart_seam":false},{"id":98,"geometry":{"kind":"Curve","curve":98,"range":{"lo":0.0,"hi":1.0}},"halfedge":177,"twin_halfedge":180,"tolerance":1e-8,"chart_seam":false},{"id":99,"geometry":{"kind":"Curve","curve":99,"range":{"lo":0.0,"hi":1.0}},"halfedge":182,"twin_halfedge":204,"tolerance":1e-8,"chart_seam":false},{"id":100,"geometry":{"kind":"Curve","curve":100,"range":{"lo":0.0,"hi":1.0}},"halfedge":184,"twin_halfedge":189,"tolerance":1e-8,"chart_seam":false},{"id":101,"geometry":{"kind":"Curve","curve":101,"range":{"lo":0.0,"hi":1.0}},"halfedge":185,"twin_halfedge":211,"tolerance":1e-8,"chart_seam":false},{"id":102,"geometry":{"kind":"Curve","curve":102,"range":{"lo":0.0,"hi":1.0}},"halfedge":186,"twin_halfedge":196,"tolerance":1e-8,"chart_seam":false},{"id":103,"geometry":{"kind":"Curve","curve":103,"range":{"lo":0.0,"hi":1.0}},"halfedge":188,"twin_halfedge":218,"tolerance":1e-8,"chart_seam":false},{"id":104,"geometry":{"kind":"Curve","curve":104,"range":{"lo":0.0,"hi":1.0}},"halfedge":197,"twin_halfedge":223,"tolerance":1e-8,"chart_seam":false},{"id":105,"geometry":{"kind":"Curve","curve":105,"range":{"lo":0.0,"hi":1.0}},"halfedge":198,"twin_halfedge":200,"tolerance":1e-8,"chart_seam":false},{"id":106,"geometry":{"kind":"Curve","curve":106,"range":{"lo":0.0,"hi":1.0}},"halfedge":201,"twin_halfedge":231,"tolerance":1e-8,"chart_seam":false},{"id":107,"geometry":{"kind":"Curve","curve":107,"range":{"lo":0.0,"hi":1.0}},"halfedge":202,"twin_halfedge":205,"tolerance":1e-8,"chart_seam":false},{"id":108,"geometry":{"kind":"Curve","curve":108,"range":{"lo":0.0,"hi":1.0}},"halfedge":206,"twin_halfedge":236,"tolerance":1e-8,"chart_seam":false},{"id":109,"geometry":{"kind":"Curve","curve":109,"range":{"lo":0.0,"hi":1.0}},"halfedge":208,"twin_halfedge":217,"tolerance":1e-8,"chart_seam":false},{"id":110,"geometry":{"kind":"Curve","curve":110,"range":{"lo":0.0,"hi":1.0}},"halfedge":209,"twin_halfedge":214,"tolerance":1e-8,"chart_seam":false},{"id":111,"geometry":{"kind":"Curve","curve":111,"range":{"lo":0.0,"hi":1.0}},"halfedge":210,"twin_halfedge":220,"tolerance":1e-8,"chart_seam":false},{"id":112,"geometry":{"kind":"Curve","curve":112,"range":{"lo":0.0,"hi":1.0}},"halfedge":213,"twin_halfedge":227,"tolerance":1e-8,"chart_seam":false},{"id":113,"geometry":{"kind":"Curve","curve":113,"range":{"lo":0.0,"hi":1.0}},"halfedge":215,"twin_halfedge":216,"tolerance":1e-8,"chart_seam":false},{"id":114,"geometry":{"kind":"Curve","curve":114,"range":{"lo":0.0,"hi":1.0}},"halfedge":221,"twin_halfedge":226,"tolerance":1e-8,"chart_seam":false},{"id":115,"geometry":{"kind":"Curve","curve":115,"range":{"lo":0.0,"hi":1.0}},"halfedge":222,"twin_halfedge":228,"tolerance":1e-8,"chart_seam":false},{"id":116,"geometry":{"kind":"Curve","curve":116,"range":{"lo":0.0,"hi":1.0}},"halfedge":225,"twin_halfedge":235,"tolerance":1e-8,"chart_seam":false},{"id":117,"geometry":{"kind":"Curve","curve":117,"range":{"lo":0.0,"hi":1.0}},"halfedge":229,"twin_halfedge":234,"tolerance":1e-8,"chart_seam":false},{"id":118,"geometry":{"kind":"Curve","curve":118,"range":{"lo":0.0,"hi":1.0}},"halfedge":230,"twin_halfedge":237,"tolerance":1e-8,"chart_seam":false},{"id":119,"geometry":{"kind":"Curve","curve":119,"range":{"lo":0.0,"hi":1.0}},"halfedge":233,"twin_halfedge":238,"tolerance":1e-8,"chart_seam":false}],"halfedges":[{"id":0,"from":0,"to":1,"twin":14,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":1,"to":2,"twin":35,"next":2,"prev":0,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":2,"to":3,"twin":11,"next":3,"prev":1,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":3,"to":0,"twin":6,"next":0,"prev":2,"edge":3,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":4,"to":5,"twin":90,"next":5,"prev":7,"edge":4,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":5,"to":0,"twin":19,"next":6,"prev":4,"edge":5,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":5,"periodic_lift":[0,0]}},{"id":6,"from":0,"to":3,"twin":3,"next":7,"prev":5,"edge":3,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":6,"periodic_lift":[0,0]}},{"id":7,"from":3,"to":4,"twin":10,"next":4,"prev":6,"edge":6,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":7,"periodic_lift":[0,0]}},{"id":8,"from":2,"to":6,"twin":38,"next":9,"prev":11,"edge":7,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":8,"periodic_lift":[0,0]}},{"id":9,"from":6,"to":4,"twin":95,"next":10,"prev":8,"edge":8,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":9,"periodic_lift":[0,0]}},{"id":10,"from":4,"to":3,"twin":7,"next":11,"prev":9,"edge":6,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":10,"periodic_lift":[0,0]}},{"id":11,"from":3,"to":2,"twin":2,"next":8,"prev":10,"edge":2,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":11,"periodic_lift":[0,0]}},{"id":12,"from":7,"to":8,"twin":22,"next":13,"prev":15,"edge":9,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":12,"periodic_lift":[0,0]}},{"id":13,"from":8,"to":1,"twin":43,"next":14,"prev":12,"edge":10,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":13,"periodic_lift":[0,0]}},{"id":14,"from":1,"to":0,"twin":0,"next":15,"prev":13,"edge":0,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":14,"periodic_lift":[0,0]}},{"id":15,"from":0,"to":7,"twin":18,"next":12,"prev":14,"edge":11,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":15,"periodic_lift":[0,0]}},{"id":16,"from":5,"to":9,"twin":98,"next":17,"prev":19,"edge":12,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":16,"periodic_lift":[0,0]}},{"id":17,"from":9,"to":7,"twin":27,"next":18,"prev":16,"edge":13,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":17,"periodic_lift":[0,0]}},{"id":18,"from":7,"to":0,"twin":15,"next":19,"prev":17,"edge":11,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":18,"periodic_lift":[0,0]}},{"id":19,"from":0,"to":5,"twin":5,"next":16,"prev":18,"edge":5,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":19,"periodic_lift":[0,0]}},{"id":20,"from":10,"to":11,"twin":31,"next":21,"prev":23,"edge":14,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":20,"periodic_lift":[0,0]}},{"id":21,"from":11,"to":8,"twin":51,"next":22,"prev":20,"edge":15,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":21,"periodic_lift":[0,0]}},{"id":22,"from":8,"to":7,"twin":12,"next":23,"prev":21,"edge":9,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":22,"periodic_lift":[0,0]}},{"id":23,"from":7,"to":10,"twin":26,"next":20,"prev":22,"edge":16,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":23,"periodic_lift":[0,0]}},{"id":24,"from":9,"to":12,"twin":106,"next":25,"prev":27,"edge":17,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":24,"periodic_lift":[0,0]}},{"id":25,"from":12,"to":10,"twin":28,"next":26,"prev":24,"edge":18,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":25,"periodic_lift":[0,0]}},{"id":26,"from":10,"to":7,"twin":23,"next":27,"prev":25,"edge":16,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":26,"periodic_lift":[0,0]}},{"id":27,"from":7,"to":9,"twin":17,"next":24,"prev":26,"edge":13,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":27,"periodic_lift":[0,0]}},{"id":28,"from":10,"to":12,"twin":25,"next":29,"prev":31,"edge":18,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":28,"periodic_lift":[0,0]}},{"id":29,"from":12,"to":13,"twin":111,"next":30,"prev":28,"edge":19,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":29,"periodic_lift":[0,0]}},{"id":30,"from":13,"to":11,"twin":52,"next":31,"prev":29,"edge":20,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":30,"periodic_lift":[0,0]}},{"id":31,"from":11,"to":10,"twin":20,"next":28,"prev":30,"edge":14,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":31,"periodic_lift":[0,0]}},{"id":32,"from":1,"to":14,"twin":42,"next":33,"prev":35,"edge":21,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":32,"periodic_lift":[0,0]}},{"id":33,"from":14,"to":15,"twin":59,"next":34,"prev":32,"edge":22,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":33,"periodic_lift":[0,0]}},{"id":34,"from":15,"to":2,"twin":39,"next":35,"prev":33,"edge":23,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":34,"periodic_lift":[0,0]}},{"id":35,"from":2,"to":1,"twin":1,"next":32,"prev":34,"edge":1,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":35,"periodic_lift":[0,0]}},{"id":36,"from":15,"to":16,"twin":66,"next":37,"prev":39,"edge":24,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":36,"periodic_lift":[0,0]}},{"id":37,"from":16,"to":6,"twin":115,"next":38,"prev":36,"edge":25,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":37,"periodic_lift":[0,0]}},{"id":38,"from":6,"to":2,"twin":8,"next":39,"prev":37,"edge":7,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":38,"periodic_lift":[0,0]}},{"id":39,"from":2,"to":15,"twin":34,"next":36,"prev":38,"edge":23,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":39,"periodic_lift":[0,0]}},{"id":40,"from":8,"to":17,"twin":50,"next":41,"prev":43,"edge":26,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":40,"periodic_lift":[0,0]}},{"id":41,"from":17,"to":14,"twin":71,"next":42,"prev":40,"edge":27,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":41,"periodic_lift":[0,0]}},{"id":42,"from":14,"to":1,"twin":32,"next":43,"prev":41,"edge":21,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":42,"periodic_lift":[0,0]}},{"id":43,"from":1,"to":8,"twin":13,"next":40,"prev":42,"edge":10,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":43,"periodic_lift":[0,0]}},{"id":44,"from":18,"to":19,"twin":119,"next":45,"prev":47,"edge":28,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":44,"periodic_lift":[0,0]}},{"id":45,"from":19,"to":20,"twin":138,"next":46,"prev":44,"edge":29,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":45,"periodic_lift":[0,0]}},{"id":46,"from":20,"to":21,"twin":123,"next":47,"prev":45,"edge":30,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":46,"periodic_lift":[0,0]}},{"id":47,"from":21,"to":18,"twin":100,"next":44,"prev":46,"edge":31,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":47,"periodic_lift":[0,0]}},{"id":48,"from":11,"to":22,"twin":55,"next":49,"prev":51,"edge":32,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":48,"periodic_lift":[0,0]}},{"id":49,"from":22,"to":17,"twin":79,"next":50,"prev":48,"edge":33,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":49,"periodic_lift":[0,0]}},{"id":50,"from":17,"to":8,"twin":40,"next":51,"prev":49,"edge":26,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":50,"periodic_lift":[0,0]}},{"id":51,"from":8,"to":11,"twin":21,"next":48,"prev":50,"edge":15,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":51,"periodic_lift":[0,0]}},{"id":52,"from":11,"to":13,"twin":30,"next":53,"prev":55,"edge":20,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":52,"periodic_lift":[0,0]}},{"id":53,"from":13,"to":23,"twin":127,"next":54,"prev":52,"edge":34,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":53,"periodic_lift":[0,0]}},{"id":54,"from":23,"to":22,"twin":84,"next":55,"prev":53,"edge":35,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":54,"periodic_lift":[0,0]}},{"id":55,"from":22,"to":11,"twin":48,"next":52,"prev":54,"edge":32,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":55,"periodic_lift":[0,0]}},{"id":56,"from":14,"to":24,"twin":70,"next":57,"prev":59,"edge":36,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":56,"periodic_lift":[0,0]}},{"id":57,"from":24,"to":25,"twin":60,"next":58,"prev":56,"edge":37,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":57,"periodic_lift":[0,0]}},{"id":58,"from":25,"to":15,"twin":67,"next":59,"prev":57,"edge":38,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":58,"periodic_lift":[0,0]}},{"id":59,"from":15,"to":14,"twin":33,"next":56,"prev":58,"edge":22,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":59,"periodic_lift":[0,0]}},{"id":60,"from":25,"to":24,"twin":57,"next":61,"prev":63,"edge":37,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":60,"periodic_lift":[0,0]}},{"id":61,"from":24,"to":26,"twin":75,"next":62,"prev":60,"edge":39,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":61,"periodic_lift":[0,0]}},{"id":62,"from":26,"to":27,"twin":128,"next":63,"prev":61,"edge":40,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":62,"periodic_lift":[0,0]}},{"id":63,"from":27,"to":25,"twin":64,"next":60,"prev":62,"edge":41,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":63,"periodic_lift":[0,0]}},{"id":64,"from":25,"to":27,"twin":63,"next":65,"prev":67,"edge":41,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":64,"periodic_lift":[0,0]}},{"id":65,"from":27,"to":16,"twin":135,"next":66,"prev":64,"edge":42,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":65,"periodic_lift":[0,0]}},{"id":66,"from":16,"to":15,"twin":36,"next":67,"prev":65,"edge":24,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":66,"periodic_lift":[0,0]}},{"id":67,"from":15,"to":25,"twin":58,"next":64,"prev":66,"edge":38,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":67,"periodic_lift":[0,0]}},{"id":68,"from":17,"to":28,"twin":78,"next":69,"prev":71,"edge":43,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":68,"periodic_lift":[0,0]}},{"id":69,"from":28,"to":24,"twin":72,"next":70,"prev":68,"edge":44,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":69,"periodic_lift":[0,0]}},{"id":70,"from":24,"to":14,"twin":56,"next":71,"prev":69,"edge":36,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":70,"periodic_lift":[0,0]}},{"id":71,"from":14,"to":17,"twin":41,"next":68,"prev":70,"edge":27,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":71,"periodic_lift":[0,0]}},{"id":72,"from":24,"to":28,"twin":69,"next":73,"prev":75,"edge":44,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":72,"periodic_lift":[0,0]}},{"id":73,"from":28,"to":29,"twin":83,"next":74,"prev":72,"edge":45,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":73,"periodic_lift":[0,0]}},{"id":74,"from":29,"to":26,"twin":140,"next":75,"prev":73,"edge":46,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":74,"periodic_lift":[0,0]}},{"id":75,"from":26,"to":24,"twin":61,"next":72,"prev":74,"edge":39,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":75,"periodic_lift":[0,0]}},{"id":76,"from":22,"to":30,"twin":87,"next":77,"prev":79,"edge":47,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":76,"periodic_lift":[0,0]}},{"id":77,"from":30,"to":28,"twin":80,"next":78,"prev":76,"edge":48,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":77,"periodic_lift":[0,0]}},{"id":78,"from":28,"to":17,"twin":68,"next":79,"prev":77,"edge":43,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":78,"periodic_lift":[0,0]}},{"id":79,"from":17,"to":22,"twin":49,"next":76,"prev":78,"edge":33,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":79,"periodic_lift":[0,0]}},{"id":80,"from":28,"to":30,"twin":77,"next":81,"prev":83,"edge":48,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":80,"periodic_lift":[0,0]}},{"id":81,"from":30,"to":31,"twin":86,"next":82,"prev":80,"edge":49,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":81,"periodic_lift":[0,0]}},{"id":82,"from":31,"to":29,"twin":144,"next":83,"prev":81,"edge":50,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":82,"periodic_lift":[0,0]}},{"id":83,"from":29,"to":28,"twin":73,"next":80,"prev":82,"edge":45,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":83,"periodic_lift":[0,0]}},{"id":84,"from":22,"to":23,"twin":54,"next":85,"prev":87,"edge":35,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":84,"periodic_lift":[0,0]}},{"id":85,"from":23,"to":31,"twin":151,"next":86,"prev":84,"edge":51,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":85,"periodic_lift":[0,0]}},{"id":86,"from":31,"to":30,"twin":81,"next":87,"prev":85,"edge":49,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":86,"periodic_lift":[0,0]}},{"id":87,"from":30,"to":22,"twin":76,"next":84,"prev":86,"edge":47,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":87,"periodic_lift":[0,0]}},{"id":88,"from":32,"to":33,"twin":158,"next":89,"prev":91,"edge":52,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":88,"periodic_lift":[0,0]}},{"id":89,"from":33,"to":5,"twin":99,"next":90,"prev":88,"edge":53,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":89,"periodic_lift":[0,0]}},{"id":90,"from":5,"to":4,"twin":4,"next":91,"prev":89,"edge":4,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":90,"periodic_lift":[0,0]}},{"id":91,"from":4,"to":32,"twin":94,"next":88,"prev":90,"edge":54,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":91,"periodic_lift":[0,0]}},{"id":92,"from":6,"to":34,"twin":114,"next":93,"prev":95,"edge":55,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":92,"periodic_lift":[0,0]}},{"id":93,"from":34,"to":32,"twin":163,"next":94,"prev":92,"edge":56,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":93,"periodic_lift":[0,0]}},{"id":94,"from":32,"to":4,"twin":91,"next":95,"prev":93,"edge":54,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":94,"periodic_lift":[0,0]}},{"id":95,"from":4,"to":6,"twin":9,"next":92,"prev":94,"edge":8,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":95,"periodic_lift":[0,0]}},{"id":96,"from":33,"to":35,"twin":170,"next":97,"prev":99,"edge":57,"face":24,"loop_ref":24,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":96,"periodic_lift":[0,0]}},{"id":97,"from":35,"to":9,"twin":107,"next":98,"prev":96,"edge":58,"face":24,"loop_ref":24,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":97,"periodic_lift":[0,0]}},{"id":98,"from":9,"to":5,"twin":16,"next":99,"prev":97,"edge":12,"face":24,"loop_ref":24,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":98,"periodic_lift":[0,0]}},{"id":99,"from":5,"to":33,"twin":89,"next":96,"prev":98,"edge":53,"face":24,"loop_ref":24,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":99,"periodic_lift":[0,0]}},{"id":100,"from":18,"to":21,"twin":47,"next":101,"prev":103,"edge":31,"face":25,"loop_ref":25,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":100,"periodic_lift":[0,0]}},{"id":101,"from":21,"to":36,"twin":122,"next":102,"prev":100,"edge":59,"face":25,"loop_ref":25,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":101,"periodic_lift":[0,0]}},{"id":102,"from":36,"to":37,"twin":195,"next":103,"prev":101,"edge":60,"face":25,"loop_ref":25,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":102,"periodic_lift":[0,0]}},{"id":103,"from":37,"to":18,"twin":116,"next":100,"prev":102,"edge":61,"face":25,"loop_ref":25,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":103,"periodic_lift":[0,0]}},{"id":104,"from":35,"to":38,"twin":178,"next":105,"prev":107,"edge":62,"face":26,"loop_ref":26,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":104,"periodic_lift":[0,0]}},{"id":105,"from":38,"to":12,"twin":108,"next":106,"prev":104,"edge":63,"face":26,"loop_ref":26,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":105,"periodic_lift":[0,0]}},{"id":106,"from":12,"to":9,"twin":24,"next":107,"prev":105,"edge":17,"face":26,"loop_ref":26,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":106,"periodic_lift":[0,0]}},{"id":107,"from":9,"to":35,"twin":97,"next":104,"prev":106,"edge":58,"face":26,"loop_ref":26,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":107,"periodic_lift":[0,0]}},{"id":108,"from":12,"to":38,"twin":105,"next":109,"prev":111,"edge":63,"face":27,"loop_ref":27,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":108,"periodic_lift":[0,0]}},{"id":109,"from":38,"to":39,"twin":183,"next":110,"prev":108,"edge":64,"face":27,"loop_ref":27,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":109,"periodic_lift":[0,0]}},{"id":110,"from":39,"to":13,"twin":124,"next":111,"prev":109,"edge":65,"face":27,"loop_ref":27,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":110,"periodic_lift":[0,0]}},{"id":111,"from":13,"to":12,"twin":29,"next":108,"prev":110,"edge":19,"face":27,"loop_ref":27,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":111,"periodic_lift":[0,0]}},{"id":112,"from":16,"to":40,"twin":134,"next":113,"prev":115,"edge":66,"face":28,"loop_ref":28,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":112,"periodic_lift":[0,0]}},{"id":113,"from":40,"to":34,"twin":191,"next":114,"prev":112,"edge":67,"face":28,"loop_ref":28,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":113,"periodic_lift":[0,0]}},{"id":114,"from":34,"to":6,"twin":92,"next":115,"prev":113,"edge":55,"face":28,"loop_ref":28,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":114,"periodic_lift":[0,0]}},{"id":115,"from":6,"to":16,"twin":37,"next":112,"prev":114,"edge":25,"face":28,"loop_ref":28,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":115,"periodic_lift":[0,0]}},{"id":116,"from":18,"to":37,"twin":103,"next":117,"prev":119,"edge":61,"face":29,"loop_ref":29,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":116,"periodic_lift":[0,0]}},{"id":117,"from":37,"to":41,"twin":194,"next":118,"prev":116,"edge":68,"face":29,"loop_ref":29,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":117,"periodic_lift":[0,0]}},{"id":118,"from":41,"to":19,"twin":139,"next":119,"prev":117,"edge":69,"face":29,"loop_ref":29,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":118,"periodic_lift":[0,0]}},{"id":119,"from":19,"to":18,"twin":44,"next":116,"prev":118,"edge":28,"face":29,"loop_ref":29,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":119,"periodic_lift":[0,0]}},{"id":120,"from":20,"to":42,"twin":137,"next":121,"prev":123,"edge":70,"face":30,"loop_ref":30,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":120,"periodic_lift":[0,0]}},{"id":121,"from":42,"to":36,"twin":192,"next":122,"prev":120,"edge":71,"face":30,"loop_ref":30,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":121,"periodic_lift":[0,0]}},{"id":122,"from":36,"to":21,"twin":101,"next":123,"prev":121,"edge":59,"face":30,"loop_ref":30,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":122,"periodic_lift":[0,0]}},{"id":123,"from":21,"to":20,"twin":46,"next":120,"prev":122,"edge":30,"face":30,"loop_ref":30,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":123,"periodic_lift":[0,0]}},{"id":124,"from":13,"to":39,"twin":110,"next":125,"prev":127,"edge":65,"face":31,"loop_ref":31,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":124,"periodic_lift":[0,0]}},{"id":125,"from":39,"to":43,"twin":207,"next":126,"prev":124,"edge":72,"face":31,"loop_ref":31,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":125,"periodic_lift":[0,0]}},{"id":126,"from":43,"to":23,"twin":148,"next":127,"prev":125,"edge":73,"face":31,"loop_ref":31,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":126,"periodic_lift":[0,0]}},{"id":127,"from":23,"to":13,"twin":53,"next":124,"prev":126,"edge":34,"face":31,"loop_ref":31,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":127,"periodic_lift":[0,0]}},{"id":128,"from":27,"to":26,"twin":62,"next":129,"prev":131,"edge":40,"face":32,"loop_ref":32,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":128,"periodic_lift":[0,0]}},{"id":129,"from":26,"to":44,"twin":143,"next":130,"prev":128,"edge":74,"face":32,"loop_ref":32,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":129,"periodic_lift":[0,0]}},{"id":130,"from":44,"to":45,"twin":212,"next":131,"prev":129,"edge":75,"face":32,"loop_ref":32,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":130,"periodic_lift":[0,0]}},{"id":131,"from":45,"to":27,"twin":132,"next":128,"prev":130,"edge":76,"face":32,"loop_ref":32,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":131,"periodic_lift":[0,0]}},{"id":132,"from":27,"to":45,"twin":131,"next":133,"prev":135,"edge":76,"face":33,"loop_ref":33,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":132,"periodic_lift":[0,0]}},{"id":133,"from":45,"to":40,"twin":219,"next":134,"prev":132,"edge":77,"face":33,"loop_ref":33,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":133,"periodic_lift":[0,0]}},{"id":134,"from":40,"to":16,"twin":112,"next":135,"prev":133,"edge":66,"face":33,"loop_ref":33,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":134,"periodic_lift":[0,0]}},{"id":135,"from":16,"to":27,"twin":65,"next":132,"prev":134,"edge":42,"face":33,"loop_ref":33,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":135,"periodic_lift":[0,0]}},{"id":136,"from":41,"to":42,"twin":193,"next":137,"prev":139,"edge":78,"face":34,"loop_ref":34,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":136,"periodic_lift":[0,0]}},{"id":137,"from":42,"to":20,"twin":120,"next":138,"prev":136,"edge":70,"face":34,"loop_ref":34,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":137,"periodic_lift":[0,0]}},{"id":138,"from":20,"to":19,"twin":45,"next":139,"prev":137,"edge":29,"face":34,"loop_ref":34,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":138,"periodic_lift":[0,0]}},{"id":139,"from":19,"to":41,"twin":118,"next":136,"prev":138,"edge":69,"face":34,"loop_ref":34,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":139,"periodic_lift":[0,0]}},{"id":140,"from":26,"to":29,"twin":74,"next":141,"prev":143,"edge":46,"face":35,"loop_ref":35,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":140,"periodic_lift":[0,0]}},{"id":141,"from":29,"to":46,"twin":147,"next":142,"prev":140,"edge":79,"face":35,"loop_ref":35,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":141,"periodic_lift":[0,0]}},{"id":142,"from":46,"to":44,"twin":224,"next":143,"prev":141,"edge":80,"face":35,"loop_ref":35,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":142,"periodic_lift":[0,0]}},{"id":143,"from":44,"to":26,"twin":129,"next":140,"prev":142,"edge":74,"face":35,"loop_ref":35,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":143,"periodic_lift":[0,0]}},{"id":144,"from":29,"to":31,"twin":82,"next":145,"prev":147,"edge":50,"face":36,"loop_ref":36,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":144,"periodic_lift":[0,0]}},{"id":145,"from":31,"to":47,"twin":150,"next":146,"prev":144,"edge":81,"face":36,"loop_ref":36,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":145,"periodic_lift":[0,0]}},{"id":146,"from":47,"to":46,"twin":232,"next":147,"prev":145,"edge":82,"face":36,"loop_ref":36,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":146,"periodic_lift":[0,0]}},{"id":147,"from":46,"to":29,"twin":141,"next":144,"prev":146,"edge":79,"face":36,"loop_ref":36,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":147,"periodic_lift":[0,0]}},{"id":148,"from":23,"to":43,"twin":126,"next":149,"prev":151,"edge":73,"face":37,"loop_ref":37,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":148,"periodic_lift":[0,0]}},{"id":149,"from":43,"to":47,"twin":239,"next":150,"prev":148,"edge":83,"face":37,"loop_ref":37,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":149,"periodic_lift":[0,0]}},{"id":150,"from":47,"to":31,"twin":145,"next":151,"prev":149,"edge":81,"face":37,"loop_ref":37,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":150,"periodic_lift":[0,0]}},{"id":151,"from":31,"to":23,"twin":85,"next":148,"prev":150,"edge":51,"face":37,"loop_ref":37,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":151,"periodic_lift":[0,0]}},{"id":152,"from":48,"to":49,"twin":161,"next":153,"prev":155,"edge":84,"face":38,"loop_ref":38,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":152,"periodic_lift":[0,0]}},{"id":153,"from":49,"to":50,"twin":187,"next":154,"prev":152,"edge":85,"face":38,"loop_ref":38,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":153,"periodic_lift":[0,0]}},{"id":154,"from":50,"to":51,"twin":164,"next":155,"prev":153,"edge":86,"face":38,"loop_ref":38,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":154,"periodic_lift":[0,0]}},{"id":155,"from":51,"to":48,"twin":156,"next":152,"prev":154,"edge":87,"face":38,"loop_ref":38,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":155,"periodic_lift":[0,0]}},{"id":156,"from":48,"to":51,"twin":155,"next":157,"prev":159,"edge":87,"face":39,"loop_ref":39,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":156,"periodic_lift":[0,0]}},{"id":157,"from":51,"to":33,"twin":171,"next":158,"prev":156,"edge":88,"face":39,"loop_ref":39,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":157,"periodic_lift":[0,0]}},{"id":158,"from":33,"to":32,"twin":88,"next":159,"prev":157,"edge":52,"face":39,"loop_ref":39,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":158,"periodic_lift":[0,0]}},{"id":159,"from":32,"to":48,"twin":162,"next":156,"prev":158,"edge":89,"face":39,"loop_ref":39,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":159,"periodic_lift":[0,0]}},{"id":160,"from":34,"to":49,"twin":190,"next":161,"prev":163,"edge":90,"face":40,"loop_ref":40,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":160,"periodic_lift":[0,0]}},{"id":161,"from":49,"to":48,"twin":152,"next":162,"prev":160,"edge":84,"face":40,"loop_ref":40,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":161,"periodic_lift":[0,0]}},{"id":162,"from":48,"to":32,"twin":159,"next":163,"prev":161,"edge":89,"face":40,"loop_ref":40,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":162,"periodic_lift":[0,0]}},{"id":163,"from":32,"to":34,"twin":93,"next":160,"prev":162,"edge":56,"face":40,"loop_ref":40,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":163,"periodic_lift":[0,0]}},{"id":164,"from":51,"to":50,"twin":154,"next":165,"prev":167,"edge":86,"face":41,"loop_ref":41,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":164,"periodic_lift":[0,0]}},{"id":165,"from":50,"to":52,"twin":199,"next":166,"prev":164,"edge":91,"face":41,"loop_ref":41,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":165,"periodic_lift":[0,0]}},{"id":166,"from":52,"to":53,"twin":172,"next":167,"prev":165,"edge":92,"face":41,"loop_ref":41,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":166,"periodic_lift":[0,0]}},{"id":167,"from":53,"to":51,"twin":168,"next":164,"prev":166,"edge":93,"face":41,"loop_ref":41,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":167,"periodic_lift":[0,0]}},{"id":168,"from":51,"to":53,"twin":167,"next":169,"prev":171,"edge":93,"face":42,"loop_ref":42,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":168,"periodic_lift":[0,0]}},{"id":169,"from":53,"to":35,"twin":179,"next":170,"prev":168,"edge":94,"face":42,"loop_ref":42,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":169,"periodic_lift":[0,0]}},{"id":170,"from":35,"to":33,"twin":96,"next":171,"prev":169,"edge":57,"face":42,"loop_ref":42,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":170,"periodic_lift":[0,0]}},{"id":171,"from":33,"to":51,"twin":157,"next":168,"prev":170,"edge":88,"face":42,"loop_ref":42,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":171,"periodic_lift":[0,0]}},{"id":172,"from":53,"to":52,"twin":166,"next":173,"prev":175,"edge":92,"face":43,"loop_ref":43,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":172,"periodic_lift":[0,0]}},{"id":173,"from":52,"to":54,"twin":203,"next":174,"prev":172,"edge":95,"face":43,"loop_ref":43,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":173,"periodic_lift":[0,0]}},{"id":174,"from":54,"to":55,"twin":181,"next":175,"prev":173,"edge":96,"face":43,"loop_ref":43,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":174,"periodic_lift":[0,0]}},{"id":175,"from":55,"to":53,"twin":176,"next":172,"prev":174,"edge":97,"face":43,"loop_ref":43,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":175,"periodic_lift":[0,0]}},{"id":176,"from":53,"to":55,"twin":175,"next":177,"prev":179,"edge":97,"face":44,"loop_ref":44,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":176,"periodic_lift":[0,0]}},{"id":177,"from":55,"to":38,"twin":180,"next":178,"prev":176,"edge":98,"face":44,"loop_ref":44,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":177,"periodic_lift":[0,0]}},{"id":178,"from":38,"to":35,"twin":104,"next":179,"prev":177,"edge":62,"face":44,"loop_ref":44,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":178,"periodic_lift":[0,0]}},{"id":179,"from":35,"to":53,"twin":169,"next":176,"prev":178,"edge":94,"face":44,"loop_ref":44,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":179,"periodic_lift":[0,0]}},{"id":180,"from":38,"to":55,"twin":177,"next":181,"prev":183,"edge":98,"face":45,"loop_ref":45,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":180,"periodic_lift":[0,0]}},{"id":181,"from":55,"to":54,"twin":174,"next":182,"prev":180,"edge":96,"face":45,"loop_ref":45,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":181,"periodic_lift":[0,0]}},{"id":182,"from":54,"to":39,"twin":204,"next":183,"prev":181,"edge":99,"face":45,"loop_ref":45,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":182,"periodic_lift":[0,0]}},{"id":183,"from":39,"to":38,"twin":109,"next":180,"prev":182,"edge":64,"face":45,"loop_ref":45,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":183,"periodic_lift":[0,0]}},{"id":184,"from":49,"to":56,"twin":189,"next":185,"prev":187,"edge":100,"face":46,"loop_ref":46,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":184,"periodic_lift":[0,0]}},{"id":185,"from":56,"to":57,"twin":211,"next":186,"prev":184,"edge":101,"face":46,"loop_ref":46,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":185,"periodic_lift":[0,0]}},{"id":186,"from":57,"to":50,"twin":196,"next":187,"prev":185,"edge":102,"face":46,"loop_ref":46,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":186,"periodic_lift":[0,0]}},{"id":187,"from":50,"to":49,"twin":153,"next":184,"prev":186,"edge":85,"face":46,"loop_ref":46,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":187,"periodic_lift":[0,0]}},{"id":188,"from":40,"to":56,"twin":218,"next":189,"prev":191,"edge":103,"face":47,"loop_ref":47,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":188,"periodic_lift":[0,0]}},{"id":189,"from":56,"to":49,"twin":184,"next":190,"prev":188,"edge":100,"face":47,"loop_ref":47,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":189,"periodic_lift":[0,0]}},{"id":190,"from":49,"to":34,"twin":160,"next":191,"prev":189,"edge":90,"face":47,"loop_ref":47,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":190,"periodic_lift":[0,0]}},{"id":191,"from":34,"to":40,"twin":113,"next":188,"prev":190,"edge":67,"face":47,"loop_ref":47,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":191,"periodic_lift":[0,0]}},{"id":192,"from":36,"to":42,"twin":121,"next":193,"prev":195,"edge":71,"face":48,"loop_ref":48,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":192,"periodic_lift":[0,0]}},{"id":193,"from":42,"to":41,"twin":136,"next":194,"prev":192,"edge":78,"face":48,"loop_ref":48,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":193,"periodic_lift":[0,0]}},{"id":194,"from":41,"to":37,"twin":117,"next":195,"prev":193,"edge":68,"face":48,"loop_ref":48,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":194,"periodic_lift":[0,0]}},{"id":195,"from":37,"to":36,"twin":102,"next":192,"prev":194,"edge":60,"face":48,"loop_ref":48,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":195,"periodic_lift":[0,0]}},{"id":196,"from":50,"to":57,"twin":186,"next":197,"prev":199,"edge":102,"face":49,"loop_ref":49,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":196,"periodic_lift":[0,0]}},{"id":197,"from":57,"to":58,"twin":223,"next":198,"prev":196,"edge":104,"face":49,"loop_ref":49,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":197,"periodic_lift":[0,0]}},{"id":198,"from":58,"to":52,"twin":200,"next":199,"prev":197,"edge":105,"face":49,"loop_ref":49,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":198,"periodic_lift":[0,0]}},{"id":199,"from":52,"to":50,"twin":165,"next":196,"prev":198,"edge":91,"face":49,"loop_ref":49,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":199,"periodic_lift":[0,0]}},{"id":200,"from":52,"to":58,"twin":198,"next":201,"prev":203,"edge":105,"face":50,"loop_ref":50,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":200,"periodic_lift":[0,0]}},{"id":201,"from":58,"to":59,"twin":231,"next":202,"prev":200,"edge":106,"face":50,"loop_ref":50,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":201,"periodic_lift":[0,0]}},{"id":202,"from":59,"to":54,"twin":205,"next":203,"prev":201,"edge":107,"face":50,"loop_ref":50,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":202,"periodic_lift":[0,0]}},{"id":203,"from":54,"to":52,"twin":173,"next":200,"prev":202,"edge":95,"face":50,"loop_ref":50,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":203,"periodic_lift":[0,0]}},{"id":204,"from":39,"to":54,"twin":182,"next":205,"prev":207,"edge":99,"face":51,"loop_ref":51,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":204,"periodic_lift":[0,0]}},{"id":205,"from":54,"to":59,"twin":202,"next":206,"prev":204,"edge":107,"face":51,"loop_ref":51,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":205,"periodic_lift":[0,0]}},{"id":206,"from":59,"to":43,"twin":236,"next":207,"prev":205,"edge":108,"face":51,"loop_ref":51,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":206,"periodic_lift":[0,0]}},{"id":207,"from":43,"to":39,"twin":125,"next":204,"prev":206,"edge":72,"face":51,"loop_ref":51,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":207,"periodic_lift":[0,0]}},{"id":208,"from":56,"to":60,"twin":217,"next":209,"prev":211,"edge":109,"face":52,"loop_ref":52,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":208,"periodic_lift":[0,0]}},{"id":209,"from":60,"to":61,"twin":214,"next":210,"prev":208,"edge":110,"face":52,"loop_ref":52,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":209,"periodic_lift":[0,0]}},{"id":210,"from":61,"to":57,"twin":220,"next":211,"prev":209,"edge":111,"face":52,"loop_ref":52,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":210,"periodic_lift":[0,0]}},{"id":211,"from":57,"to":56,"twin":185,"next":208,"prev":210,"edge":101,"face":52,"loop_ref":52,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":211,"periodic_lift":[0,0]}},{"id":212,"from":45,"to":44,"twin":130,"next":213,"prev":215,"edge":75,"face":53,"loop_ref":53,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":212,"periodic_lift":[0,0]}},{"id":213,"from":44,"to":61,"twin":227,"next":214,"prev":212,"edge":112,"face":53,"loop_ref":53,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":213,"periodic_lift":[0,0]}},{"id":214,"from":61,"to":60,"twin":209,"next":215,"prev":213,"edge":110,"face":53,"loop_ref":53,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":214,"periodic_lift":[0,0]}},{"id":215,"from":60,"to":45,"twin":216,"next":212,"prev":214,"edge":113,"face":53,"loop_ref":53,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":215,"periodic_lift":[0,0]}},{"id":216,"from":45,"to":60,"twin":215,"next":217,"prev":219,"edge":113,"face":54,"loop_ref":54,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":216,"periodic_lift":[0,0]}},{"id":217,"from":60,"to":56,"twin":208,"next":218,"prev":216,"edge":109,"face":54,"loop_ref":54,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":217,"periodic_lift":[0,0]}},{"id":218,"from":56,"to":40,"twin":188,"next":219,"prev":217,"edge":103,"face":54,"loop_ref":54,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":218,"periodic_lift":[0,0]}},{"id":219,"from":40,"to":45,"twin":133,"next":216,"prev":218,"edge":77,"face":54,"loop_ref":54,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":219,"periodic_lift":[0,0]}},{"id":220,"from":57,"to":61,"twin":210,"next":221,"prev":223,"edge":111,"face":55,"loop_ref":55,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":220,"periodic_lift":[0,0]}},{"id":221,"from":61,"to":62,"twin":226,"next":222,"prev":220,"edge":114,"face":55,"loop_ref":55,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":221,"periodic_lift":[0,0]}},{"id":222,"from":62,"to":58,"twin":228,"next":223,"prev":221,"edge":115,"face":55,"loop_ref":55,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":222,"periodic_lift":[0,0]}},{"id":223,"from":58,"to":57,"twin":197,"next":220,"prev":222,"edge":104,"face":55,"loop_ref":55,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":223,"periodic_lift":[0,0]}},{"id":224,"from":44,"to":46,"twin":142,"next":225,"prev":227,"edge":80,"face":56,"loop_ref":56,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":224,"periodic_lift":[0,0]}},{"id":225,"from":46,"to":62,"twin":235,"next":226,"prev":224,"edge":116,"face":56,"loop_ref":56,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":225,"periodic_lift":[0,0]}},{"id":226,"from":62,"to":61,"twin":221,"next":227,"prev":225,"edge":114,"face":56,"loop_ref":56,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":226,"periodic_lift":[0,0]}},{"id":227,"from":61,"to":44,"twin":213,"next":224,"prev":226,"edge":112,"face":56,"loop_ref":56,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":227,"periodic_lift":[0,0]}},{"id":228,"from":58,"to":62,"twin":222,"next":229,"prev":231,"edge":115,"face":57,"loop_ref":57,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":228,"periodic_lift":[0,0]}},{"id":229,"from":62,"to":63,"twin":234,"next":230,"prev":228,"edge":117,"face":57,"loop_ref":57,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":229,"periodic_lift":[0,0]}},{"id":230,"from":63,"to":59,"twin":237,"next":231,"prev":229,"edge":118,"face":57,"loop_ref":57,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":230,"periodic_lift":[0,0]}},{"id":231,"from":59,"to":58,"twin":201,"next":228,"prev":230,"edge":106,"face":57,"loop_ref":57,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":231,"periodic_lift":[0,0]}},{"id":232,"from":46,"to":47,"twin":146,"next":233,"prev":235,"edge":82,"face":58,"loop_ref":58,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":232,"periodic_lift":[0,0]}},{"id":233,"from":47,"to":63,"twin":238,"next":234,"prev":232,"edge":119,"face":58,"loop_ref":58,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":233,"periodic_lift":[0,0]}},{"id":234,"from":63,"to":62,"twin":229,"next":235,"prev":233,"edge":117,"face":58,"loop_ref":58,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":234,"periodic_lift":[0,0]}},{"id":235,"from":62,"to":46,"twin":225,"next":232,"prev":234,"edge":116,"face":58,"loop_ref":58,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":235,"periodic_lift":[0,0]}},{"id":236,"from":43,"to":59,"twin":206,"next":237,"prev":239,"edge":108,"face":59,"loop_ref":59,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":236,"periodic_lift":[0,0]}},{"id":237,"from":59,"to":63,"twin":230,"next":238,"prev":236,"edge":118,"face":59,"loop_ref":59,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":237,"periodic_lift":[0,0]}},{"id":238,"from":63,"to":47,"twin":233,"next":239,"prev":237,"edge":119,"face":59,"loop_ref":59,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":238,"periodic_lift":[0,0]}},{"id":239,"from":47,"to":43,"twin":149,"next":236,"prev":238,"edge":83,"face":59,"loop_ref":59,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":239,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false},{"id":2,"start_halfedge":8,"face_ref":2,"is_hole":false},{"id":3,"start_halfedge":12,"face_ref":3,"is_hole":false},{"id":4,"start_halfedge":16,"face_ref":4,"is_hole":false},{"id":5,"start_halfedge":20,"face_ref":5,"is_hole":false},{"id":6,"start_halfedge":24,"face_ref":6,"is_hole":false},{"id":7,"start_halfedge":28,"face_ref":7,"is_hole":false},{"id":8,"start_halfedge":32,"face_ref":8,"is_hole":false},{"id":9,"start_halfedge":36,"face_ref":9,"is_hole":false},{"id":10,"start_halfedge":40,"face_ref":10,"is_hole":false},{"id":11,"start_halfedge":44,"face_ref":11,"is_hole":false},{"id":12,"start_halfedge":48,"face_ref":12,"is_hole":false},{"id":13,"start_halfedge":52,"face_ref":13,"is_hole":false},{"id":14,"start_halfedge":56,"face_ref":14,"is_hole":false},{"id":15,"start_halfedge":60,"face_ref":15,"is_hole":false},{"id":16,"start_halfedge":64,"face_ref":16,"is_hole":false},{"id":17,"start_halfedge":68,"face_ref":17,"is_hole":false},{"id":18,"start_halfedge":72,"face_ref":18,"is_hole":false},{"id":19,"start_halfedge":76,"face_ref":19,"is_hole":false},{"id":20,"start_halfedge":80,"face_ref":20,"is_hole":false},{"id":21,"start_halfedge":84,"face_ref":21,"is_hole":false},{"id":22,"start_halfedge":88,"face_ref":22,"is_hole":false},{"id":23,"start_halfedge":92,"face_ref":23,"is_hole":false},{"id":24,"start_halfedge":96,"face_ref":24,"is_hole":false},{"id":25,"start_halfedge":100,"face_ref":25,"is_hole":false},{"id":26,"start_halfedge":104,"face_ref":26,"is_hole":false},{"id":27,"start_halfedge":108,"face_ref":27,"is_hole":false},{"id":28,"start_halfedge":112,"face_ref":28,"is_hole":false},{"id":29,"start_halfedge":116,"face_ref":29,"is_hole":false},{"id":30,"start_halfedge":120,"face_ref":30,"is_hole":false},{"id":31,"start_halfedge":124,"face_ref":31,"is_hole":false},{"id":32,"start_halfedge":128,"face_ref":32,"is_hole":false},{"id":33,"start_halfedge":132,"face_ref":33,"is_hole":false},{"id":34,"start_halfedge":136,"face_ref":34,"is_hole":false},{"id":35,"start_halfedge":140,"face_ref":35,"is_hole":false},{"id":36,"start_halfedge":144,"face_ref":36,"is_hole":false},{"id":37,"start_halfedge":148,"face_ref":37,"is_hole":false},{"id":38,"start_halfedge":152,"face_ref":38,"is_hole":false},{"id":39,"start_halfedge":156,"face_ref":39,"is_hole":false},{"id":40,"start_halfedge":160,"face_ref":40,"is_hole":false},{"id":41,"start_halfedge":164,"face_ref":41,"is_hole":false},{"id":42,"start_halfedge":168,"face_ref":42,"is_hole":false},{"id":43,"start_halfedge":172,"face_ref":43,"is_hole":false},{"id":44,"start_halfedge":176,"face_ref":44,"is_hole":false},{"id":45,"start_halfedge":180,"face_ref":45,"is_hole":false},{"id":46,"start_halfedge":184,"face_ref":46,"is_hole":false},{"id":47,"start_halfedge":188,"face_ref":47,"is_hole":false},{"id":48,"start_halfedge":192,"face_ref":48,"is_hole":false},{"id":49,"start_halfedge":196,"face_ref":49,"is_hole":false},{"id":50,"start_halfedge":200,"face_ref":50,"is_hole":false},{"id":51,"start_halfedge":204,"face_ref":51,"is_hole":false},{"id":52,"start_halfedge":208,"face_ref":52,"is_hole":false},{"id":53,"start_halfedge":212,"face_ref":53,"is_hole":false},{"id":54,"start_halfedge":216,"face_ref":54,"is_hole":false},{"id":55,"start_halfedge":220,"face_ref":55,"is_hole":false},{"id":56,"start_halfedge":224,"face_ref":56,"is_hole":false},{"id":57,"start_halfedge":228,"face_ref":57,"is_hole":false},{"id":58,"start_halfedge":232,"face_ref":58,"is_hole":false},{"id":59,"start_halfedge":236,"face_ref":59,"is_hole":false}],"faces":[{"id":0,"key":"left-0","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":1,"key":"front-1","surface":1,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":2,"key":"bottom-2","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":2,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":3,"key":"left-3","surface":3,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":3,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":4,"key":"front-4","surface":4,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":4,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":5,"key":"left-5","surface":5,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":5,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":6,"key":"front-6","surface":6,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":6,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":7,"key":"top-7","surface":7,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":7,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":8,"key":"left-8","surface":8,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":8,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":9,"key":"bottom-9","surface":9,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":9,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":10,"key":"left-10","surface":10,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":10,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":11,"key":"left-11","surface":11,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":11,"holes":[]},"shell_ref":1,"provenance":{"sources":[{"entity":"box-inner","body":"box-inner","key":"left","face":2}],"role":"Cut","reversed":true}},{"id":12,"key":"left-12","surface":12,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":12,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":13,"key":"top-13","surface":13,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":13,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":14,"key":"left-14","surface":14,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":14,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":15,"key":"back-15","surface":15,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":15,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":16,"key":"bottom-16","surface":16,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":16,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":17,"key":"left-17","surface":17,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":17,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":18,"key":"back-18","surface":18,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":18,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":19,"key":"left-19","surface":19,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":19,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":20,"key":"back-20","surface":20,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":20,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":21,"key":"top-21","surface":21,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":21,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":22,"key":"front-22","surface":22,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":22,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":23,"key":"bottom-23","surface":23,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":23,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":24,"key":"front-24","surface":24,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":24,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":25,"key":"front-25","surface":25,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":25,"holes":[]},"shell_ref":1,"provenance":{"sources":[{"entity":"box-inner","body":"box-inner","key":"front","face":4}],"role":"Cut","reversed":true}},{"id":26,"key":"front-26","surface":26,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":26,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":27,"key":"top-27","surface":27,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":27,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":28,"key":"bottom-28","surface":28,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":28,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":29,"key":"bottom-29","surface":29,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":29,"holes":[]},"shell_ref":1,"provenance":{"sources":[{"entity":"box-inner","body":"box-inner","key":"bottom","face":0}],"role":"Cut","reversed":true}},{"id":30,"key":"top-30","surface":30,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":30,"holes":[]},"shell_ref":1,"provenance":{"sources":[{"entity":"box-inner","body":"box-inner","key":"top","face":1}],"role":"Cut","reversed":true}},{"id":31,"key":"top-31","surface":31,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":31,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":32,"key":"back-32","surface":32,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":32,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":33,"key":"bottom-33","surface":33,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":33,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":34,"key":"back-34","surface":34,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":34,"holes":[]},"shell_ref":1,"provenance":{"sources":[{"entity":"box-inner","body":"box-inner","key":"back","face":5}],"role":"Cut","reversed":true}},{"id":35,"key":"back-35","surface":35,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":35,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":36,"key":"back-36","surface":36,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":36,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":37,"key":"top-37","surface":37,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":37,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":38,"key":"right-38","surface":38,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":38,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":39,"key":"front-39","surface":39,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":39,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":40,"key":"bottom-40","surface":40,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":40,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":41,"key":"right-41","surface":41,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":41,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":42,"key":"front-42","surface":42,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":42,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":43,"key":"right-43","surface":43,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":43,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":44,"key":"front-44","surface":44,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":44,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":45,"key":"top-45","surface":45,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":45,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":46,"key":"right-46","surface":46,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":46,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":47,"key":"bottom-47","surface":47,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":47,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":48,"key":"right-48","surface":48,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":48,"holes":[]},"shell_ref":1,"provenance":{"sources":[{"entity":"box-inner","body":"box-inner","key":"right","face":3}],"role":"Cut","reversed":true}},{"id":49,"key":"right-49","surface":49,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":49,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":50,"key":"right-50","surface":50,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":50,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":51,"key":"top-51","surface":51,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":51,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":52,"key":"right-52","surface":52,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":52,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":53,"key":"back-53","surface":53,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":53,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":54,"key":"bottom-54","surface":54,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":54,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":55,"key":"right-55","surface":55,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":0.99999999,"hi":2.00000001}],"outer":55,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":56,"key":"back-56","surface":56,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.99999999,"hi":2.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":56,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":57,"key":"right-57","surface":57,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":57,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":58,"key":"back-58","surface":58,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":58,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":59,"key":"top-59","surface":59,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.99999999,"hi":3.00000001},{"lo":1.99999999,"hi":3.00000001}],"outer":59,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,3,8,2,1,5,10,4,14,9,23,22,7,12,6,17,24,15,16,28,40,39,27,13,19,26,18,42,32,33,47,38,45,31,21,20,44,35,41,53,54,46,43,51,37,36,56,49,52,50,59,58,55,57],"is_closed":true},{"id":1,"faces":[11,29,34,30,25,48],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[1]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-cavity.handlers.json b/main/opengeometry/tests/fixtures/cases/box-cavity.handlers.json new file mode 100644 index 0000000..4b8564a --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cavity.handlers.json @@ -0,0 +1 @@ +["boolean_boxes"] \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-cavity.step.m b/main/opengeometry/tests/fixtures/cases/box-cavity.step.m new file mode 100644 index 0000000..f227c2e --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cavity.step.m @@ -0,0 +1,3037 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('box-cavity','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#24=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#28=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#29=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#34=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#35=AXIS2_PLACEMENT_3D('',#32,#33,#34); +#36=PLANE('',#35); +#37=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#38=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#39=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#40=AXIS2_PLACEMENT_3D('',#37,#38,#39); +#41=PLANE('',#40); +#42=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#43=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#44=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#45=AXIS2_PLACEMENT_3D('',#42,#43,#44); +#46=PLANE('',#45); +#47=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#48=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#49=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#50=AXIS2_PLACEMENT_3D('',#47,#48,#49); +#51=PLANE('',#50); +#52=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#53=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#54=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#55=AXIS2_PLACEMENT_3D('',#52,#53,#54); +#56=PLANE('',#55); +#57=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#58=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#59=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#60=AXIS2_PLACEMENT_3D('',#57,#58,#59); +#61=PLANE('',#60); +#62=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#63=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#64=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#65=AXIS2_PLACEMENT_3D('',#62,#63,#64); +#66=PLANE('',#65); +#67=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#68=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#69=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#70=AXIS2_PLACEMENT_3D('',#67,#68,#69); +#71=PLANE('',#70); +#72=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#73=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#74=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#75=AXIS2_PLACEMENT_3D('',#72,#73,#74); +#76=PLANE('',#75); +#77=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#78=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#79=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#80=AXIS2_PLACEMENT_3D('',#77,#78,#79); +#81=PLANE('',#80); +#82=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#83=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#84=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#85=AXIS2_PLACEMENT_3D('',#82,#83,#84); +#86=PLANE('',#85); +#87=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#88=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#89=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#90=AXIS2_PLACEMENT_3D('',#87,#88,#89); +#91=PLANE('',#90); +#92=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#93=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#94=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#95=AXIS2_PLACEMENT_3D('',#92,#93,#94); +#96=PLANE('',#95); +#97=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#98=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#99=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#100=AXIS2_PLACEMENT_3D('',#97,#98,#99); +#101=PLANE('',#100); +#102=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#103=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#104=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#105=AXIS2_PLACEMENT_3D('',#102,#103,#104); +#106=PLANE('',#105); +#107=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#108=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#109=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#110=AXIS2_PLACEMENT_3D('',#107,#108,#109); +#111=PLANE('',#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#113=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#114=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#115=AXIS2_PLACEMENT_3D('',#112,#113,#114); +#116=PLANE('',#115); +#117=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#118=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#119=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#120=AXIS2_PLACEMENT_3D('',#117,#118,#119); +#121=PLANE('',#120); +#122=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#123=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#124=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#125=AXIS2_PLACEMENT_3D('',#122,#123,#124); +#126=PLANE('',#125); +#127=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#128=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#129=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#130=AXIS2_PLACEMENT_3D('',#127,#128,#129); +#131=PLANE('',#130); +#132=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#133=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#134=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#135=AXIS2_PLACEMENT_3D('',#132,#133,#134); +#136=PLANE('',#135); +#137=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#138=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#139=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#140=AXIS2_PLACEMENT_3D('',#137,#138,#139); +#141=PLANE('',#140); +#142=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#143=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#144=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#145=AXIS2_PLACEMENT_3D('',#142,#143,#144); +#146=PLANE('',#145); +#147=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#148=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#149=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#150=AXIS2_PLACEMENT_3D('',#147,#148,#149); +#151=PLANE('',#150); +#152=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#153=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#154=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#155=AXIS2_PLACEMENT_3D('',#152,#153,#154); +#156=PLANE('',#155); +#157=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#158=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#159=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#160=AXIS2_PLACEMENT_3D('',#157,#158,#159); +#161=PLANE('',#160); +#162=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#163=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#164=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#165=AXIS2_PLACEMENT_3D('',#162,#163,#164); +#166=PLANE('',#165); +#167=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#168=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#169=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#170=AXIS2_PLACEMENT_3D('',#167,#168,#169); +#171=PLANE('',#170); +#172=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#173=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#174=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#175=AXIS2_PLACEMENT_3D('',#172,#173,#174); +#176=PLANE('',#175); +#177=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#178=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#179=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#180=AXIS2_PLACEMENT_3D('',#177,#178,#179); +#181=PLANE('',#180); +#182=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#183=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#184=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#185=AXIS2_PLACEMENT_3D('',#182,#183,#184); +#186=PLANE('',#185); +#187=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#188=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#189=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#190=AXIS2_PLACEMENT_3D('',#187,#188,#189); +#191=PLANE('',#190); +#192=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#193=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#194=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#195=AXIS2_PLACEMENT_3D('',#192,#193,#194); +#196=PLANE('',#195); +#197=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#198=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#199=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#200=AXIS2_PLACEMENT_3D('',#197,#198,#199); +#201=PLANE('',#200); +#202=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#203=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#204=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#205=AXIS2_PLACEMENT_3D('',#202,#203,#204); +#206=PLANE('',#205); +#207=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#208=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#209=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#210=AXIS2_PLACEMENT_3D('',#207,#208,#209); +#211=PLANE('',#210); +#212=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#213=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#214=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#215=AXIS2_PLACEMENT_3D('',#212,#213,#214); +#216=PLANE('',#215); +#217=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#218=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#219=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#220=AXIS2_PLACEMENT_3D('',#217,#218,#219); +#221=PLANE('',#220); +#222=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#223=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#224=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#225=AXIS2_PLACEMENT_3D('',#222,#223,#224); +#226=PLANE('',#225); +#227=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#228=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#229=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#230=AXIS2_PLACEMENT_3D('',#227,#228,#229); +#231=PLANE('',#230); +#232=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#233=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#234=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#235=AXIS2_PLACEMENT_3D('',#232,#233,#234); +#236=PLANE('',#235); +#237=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#238=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#239=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#240=AXIS2_PLACEMENT_3D('',#237,#238,#239); +#241=PLANE('',#240); +#242=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#243=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#244=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#245=AXIS2_PLACEMENT_3D('',#242,#243,#244); +#246=PLANE('',#245); +#247=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#248=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#249=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#250=AXIS2_PLACEMENT_3D('',#247,#248,#249); +#251=PLANE('',#250); +#252=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#253=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#254=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#255=AXIS2_PLACEMENT_3D('',#252,#253,#254); +#256=PLANE('',#255); +#257=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#258=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#259=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#260=AXIS2_PLACEMENT_3D('',#257,#258,#259); +#261=PLANE('',#260); +#262=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#263=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#264=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#265=AXIS2_PLACEMENT_3D('',#262,#263,#264); +#266=PLANE('',#265); +#267=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#268=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#269=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#270=AXIS2_PLACEMENT_3D('',#267,#268,#269); +#271=PLANE('',#270); +#272=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#273=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#274=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#275=AXIS2_PLACEMENT_3D('',#272,#273,#274); +#276=PLANE('',#275); +#277=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#278=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#279=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#280=AXIS2_PLACEMENT_3D('',#277,#278,#279); +#281=PLANE('',#280); +#282=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#283=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#284=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#285=AXIS2_PLACEMENT_3D('',#282,#283,#284); +#286=PLANE('',#285); +#287=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#288=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#289=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#290=AXIS2_PLACEMENT_3D('',#287,#288,#289); +#291=PLANE('',#290); +#292=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#293=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#294=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#295=AXIS2_PLACEMENT_3D('',#292,#293,#294); +#296=PLANE('',#295); +#297=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#298=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#299=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#300=AXIS2_PLACEMENT_3D('',#297,#298,#299); +#301=PLANE('',#300); +#302=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#303=VERTEX_POINT('',#302); +#304=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#305=VERTEX_POINT('',#304); +#306=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#307=VERTEX_POINT('',#306); +#308=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#309=VERTEX_POINT('',#308); +#310=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#311=VERTEX_POINT('',#310); +#312=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#313=VERTEX_POINT('',#312); +#314=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#315=VERTEX_POINT('',#314); +#316=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#317=VERTEX_POINT('',#316); +#318=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#319=VERTEX_POINT('',#318); +#320=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#321=VERTEX_POINT('',#320); +#322=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#323=VERTEX_POINT('',#322); +#324=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#325=VERTEX_POINT('',#324); +#326=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#327=VERTEX_POINT('',#326); +#328=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#329=VERTEX_POINT('',#328); +#330=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#331=VERTEX_POINT('',#330); +#332=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#333=VERTEX_POINT('',#332); +#334=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#335=VERTEX_POINT('',#334); +#336=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#337=VERTEX_POINT('',#336); +#338=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#339=VERTEX_POINT('',#338); +#340=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#341=VERTEX_POINT('',#340); +#342=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#343=VERTEX_POINT('',#342); +#344=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#345=VERTEX_POINT('',#344); +#346=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-2.00000000000000000E0)); +#347=VERTEX_POINT('',#346); +#348=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0,-2.00000000000000000E0)); +#349=VERTEX_POINT('',#348); +#350=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,-3.00000000000000000E0)); +#351=VERTEX_POINT('',#350); +#352=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#353=VERTEX_POINT('',#352); +#354=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-3.00000000000000000E0)); +#355=VERTEX_POINT('',#354); +#356=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#357=VERTEX_POINT('',#356); +#358=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,-3.00000000000000000E0)); +#359=VERTEX_POINT('',#358); +#360=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,-3.00000000000000000E0)); +#361=VERTEX_POINT('',#360); +#362=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#363=VERTEX_POINT('',#362); +#364=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#365=VERTEX_POINT('',#364); +#366=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#367=VERTEX_POINT('',#366); +#368=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#369=VERTEX_POINT('',#368); +#370=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#371=VERTEX_POINT('',#370); +#372=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#373=VERTEX_POINT('',#372); +#374=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#375=VERTEX_POINT('',#374); +#376=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#377=VERTEX_POINT('',#376); +#378=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#379=VERTEX_POINT('',#378); +#380=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#381=VERTEX_POINT('',#380); +#382=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#383=VERTEX_POINT('',#382); +#384=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#385=VERTEX_POINT('',#384); +#386=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#387=VERTEX_POINT('',#386); +#388=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,-2.00000000000000000E0)); +#389=VERTEX_POINT('',#388); +#390=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,-3.00000000000000000E0)); +#391=VERTEX_POINT('',#390); +#392=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#393=VERTEX_POINT('',#392); +#394=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,-3.00000000000000000E0)); +#395=VERTEX_POINT('',#394); +#396=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#397=VERTEX_POINT('',#396); +#398=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#399=VERTEX_POINT('',#398); +#400=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#401=VERTEX_POINT('',#400); +#402=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#403=VERTEX_POINT('',#402); +#404=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#405=VERTEX_POINT('',#404); +#406=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#407=VERTEX_POINT('',#406); +#408=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#409=VERTEX_POINT('',#408); +#410=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#411=VERTEX_POINT('',#410); +#412=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#413=VERTEX_POINT('',#412); +#414=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#415=VERTEX_POINT('',#414); +#416=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#417=VERTEX_POINT('',#416); +#418=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#419=VERTEX_POINT('',#418); +#420=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-2.00000000000000000E0)); +#421=VERTEX_POINT('',#420); +#422=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#423=VERTEX_POINT('',#422); +#424=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0,-3.00000000000000000E0)); +#425=VERTEX_POINT('',#424); +#426=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0,-3.00000000000000000E0)); +#427=VERTEX_POINT('',#426); +#428=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#429=VERTEX_POINT('',#428); +#430=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#431=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#432=VECTOR('',#431,1.00000000000000000E0); +#433=LINE('',#430,#432); +#434=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#435=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#436=VECTOR('',#435,1.00000000000000000E0); +#437=LINE('',#434,#436); +#438=DEFINITIONAL_REPRESENTATION('',(#437),#1); +#439=PCURVE('',#6,#438); +#440=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#441=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#442=VECTOR('',#441,1.00000000000000000E0); +#443=LINE('',#440,#442); +#444=DEFINITIONAL_REPRESENTATION('',(#443),#1); +#445=PCURVE('',#21,#444); +#446=SURFACE_CURVE('',#433,(#439,#445),.CURVE_3D.); +#447=EDGE_CURVE('',#303,#305,#446,.T.); +#448=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#449=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#450=VECTOR('',#449,1.00000000000000000E0); +#451=LINE('',#448,#450); +#452=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#453=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#454=VECTOR('',#453,1.00000000000000000E0); +#455=LINE('',#452,#454); +#456=DEFINITIONAL_REPRESENTATION('',(#455),#1); +#457=PCURVE('',#6,#456); +#458=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#459=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#460=VECTOR('',#459,1.00000000000000000E0); +#461=LINE('',#458,#460); +#462=DEFINITIONAL_REPRESENTATION('',(#461),#1); +#463=PCURVE('',#46,#462); +#464=SURFACE_CURVE('',#451,(#457,#463),.CURVE_3D.); +#465=EDGE_CURVE('',#307,#305,#464,.T.); +#466=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#467=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#468=VECTOR('',#467,1.00000000000000000E0); +#469=LINE('',#466,#468); +#470=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#471=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#472=VECTOR('',#471,1.00000000000000000E0); +#473=LINE('',#470,#472); +#474=DEFINITIONAL_REPRESENTATION('',(#473),#1); +#475=PCURVE('',#6,#474); +#476=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#477=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#478=VECTOR('',#477,1.00000000000000000E0); +#479=LINE('',#476,#478); +#480=DEFINITIONAL_REPRESENTATION('',(#479),#1); +#481=PCURVE('',#16,#480); +#482=SURFACE_CURVE('',#469,(#475,#481),.CURVE_3D.); +#483=EDGE_CURVE('',#309,#307,#482,.T.); +#484=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#485=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#486=VECTOR('',#485,1.00000000000000000E0); +#487=LINE('',#484,#486); +#488=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#489=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#490=VECTOR('',#489,1.00000000000000000E0); +#491=LINE('',#488,#490); +#492=DEFINITIONAL_REPRESENTATION('',(#491),#1); +#493=PCURVE('',#6,#492); +#494=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#495=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#496=VECTOR('',#495,1.00000000000000000E0); +#497=LINE('',#494,#496); +#498=DEFINITIONAL_REPRESENTATION('',(#497),#1); +#499=PCURVE('',#11,#498); +#500=SURFACE_CURVE('',#487,(#493,#499),.CURVE_3D.); +#501=EDGE_CURVE('',#309,#303,#500,.T.); +#502=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#503=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#504=VECTOR('',#503,1.00000000000000000E0); +#505=LINE('',#502,#504); +#506=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#507=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#508=VECTOR('',#507,1.00000000000000000E0); +#509=LINE('',#506,#508); +#510=DEFINITIONAL_REPRESENTATION('',(#509),#1); +#511=PCURVE('',#11,#510); +#512=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#513=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#514=VECTOR('',#513,1.00000000000000000E0); +#515=LINE('',#512,#514); +#516=DEFINITIONAL_REPRESENTATION('',(#515),#1); +#517=PCURVE('',#116,#516); +#518=SURFACE_CURVE('',#505,(#511,#517),.CURVE_3D.); +#519=EDGE_CURVE('',#311,#313,#518,.T.); +#520=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#521=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#522=VECTOR('',#521,1.00000000000000000E0); +#523=LINE('',#520,#522); +#524=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#525=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#526=VECTOR('',#525,1.00000000000000000E0); +#527=LINE('',#524,#526); +#528=DEFINITIONAL_REPRESENTATION('',(#527),#1); +#529=PCURVE('',#11,#528); +#530=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#531=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#532=VECTOR('',#531,1.00000000000000000E0); +#533=LINE('',#530,#532); +#534=DEFINITIONAL_REPRESENTATION('',(#533),#1); +#535=PCURVE('',#26,#534); +#536=SURFACE_CURVE('',#523,(#529,#535),.CURVE_3D.); +#537=EDGE_CURVE('',#303,#313,#536,.T.); +#538=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#539=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#540=VECTOR('',#539,1.00000000000000000E0); +#541=LINE('',#538,#540); +#542=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#543=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#544=VECTOR('',#543,1.00000000000000000E0); +#545=LINE('',#542,#544); +#546=DEFINITIONAL_REPRESENTATION('',(#545),#1); +#547=PCURVE('',#11,#546); +#548=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#549=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#550=VECTOR('',#549,1.00000000000000000E0); +#551=LINE('',#548,#550); +#552=DEFINITIONAL_REPRESENTATION('',(#551),#1); +#553=PCURVE('',#16,#552); +#554=SURFACE_CURVE('',#541,(#547,#553),.CURVE_3D.); +#555=EDGE_CURVE('',#309,#311,#554,.T.); +#556=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#557=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#558=VECTOR('',#557,1.00000000000000000E0); +#559=LINE('',#556,#558); +#560=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#561=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#562=VECTOR('',#561,1.00000000000000000E0); +#563=LINE('',#560,#562); +#564=DEFINITIONAL_REPRESENTATION('',(#563),#1); +#565=PCURVE('',#16,#564); +#566=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#567=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#568=VECTOR('',#567,1.00000000000000000E0); +#569=LINE('',#566,#568); +#570=DEFINITIONAL_REPRESENTATION('',(#569),#1); +#571=PCURVE('',#51,#570); +#572=SURFACE_CURVE('',#559,(#565,#571),.CURVE_3D.); +#573=EDGE_CURVE('',#307,#315,#572,.T.); +#574=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#575=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#576=VECTOR('',#575,1.00000000000000000E0); +#577=LINE('',#574,#576); +#578=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#579=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#580=VECTOR('',#579,1.00000000000000000E0); +#581=LINE('',#578,#580); +#582=DEFINITIONAL_REPRESENTATION('',(#581),#1); +#583=PCURVE('',#16,#582); +#584=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#585=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#586=VECTOR('',#585,1.00000000000000000E0); +#587=LINE('',#584,#586); +#588=DEFINITIONAL_REPRESENTATION('',(#587),#1); +#589=PCURVE('',#121,#588); +#590=SURFACE_CURVE('',#577,(#583,#589),.CURVE_3D.); +#591=EDGE_CURVE('',#311,#315,#590,.T.); +#592=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#593=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#594=VECTOR('',#593,1.00000000000000000E0); +#595=LINE('',#592,#594); +#596=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#597=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#598=VECTOR('',#597,1.00000000000000000E0); +#599=LINE('',#596,#598); +#600=DEFINITIONAL_REPRESENTATION('',(#599),#1); +#601=PCURVE('',#21,#600); +#602=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#603=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#604=VECTOR('',#603,1.00000000000000000E0); +#605=LINE('',#602,#604); +#606=DEFINITIONAL_REPRESENTATION('',(#605),#1); +#607=PCURVE('',#31,#606); +#608=SURFACE_CURVE('',#595,(#601,#607),.CURVE_3D.); +#609=EDGE_CURVE('',#317,#319,#608,.T.); +#610=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#611=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#612=VECTOR('',#611,1.00000000000000000E0); +#613=LINE('',#610,#612); +#614=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#615=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#616=VECTOR('',#615,1.00000000000000000E0); +#617=LINE('',#614,#616); +#618=DEFINITIONAL_REPRESENTATION('',(#617),#1); +#619=PCURVE('',#21,#618); +#620=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#621=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#622=VECTOR('',#621,1.00000000000000000E0); +#623=LINE('',#620,#622); +#624=DEFINITIONAL_REPRESENTATION('',(#623),#1); +#625=PCURVE('',#56,#624); +#626=SURFACE_CURVE('',#613,(#619,#625),.CURVE_3D.); +#627=EDGE_CURVE('',#305,#319,#626,.T.); +#628=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#629=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#630=VECTOR('',#629,1.00000000000000000E0); +#631=LINE('',#628,#630); +#632=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#633=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#634=VECTOR('',#633,1.00000000000000000E0); +#635=LINE('',#632,#634); +#636=DEFINITIONAL_REPRESENTATION('',(#635),#1); +#637=PCURVE('',#21,#636); +#638=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#639=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#640=VECTOR('',#639,1.00000000000000000E0); +#641=LINE('',#638,#640); +#642=DEFINITIONAL_REPRESENTATION('',(#641),#1); +#643=PCURVE('',#26,#642); +#644=SURFACE_CURVE('',#631,(#637,#643),.CURVE_3D.); +#645=EDGE_CURVE('',#303,#317,#644,.T.); +#646=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#647=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#648=VECTOR('',#647,1.00000000000000000E0); +#649=LINE('',#646,#648); +#650=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#651=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#652=VECTOR('',#651,1.00000000000000000E0); +#653=LINE('',#650,#652); +#654=DEFINITIONAL_REPRESENTATION('',(#653),#1); +#655=PCURVE('',#26,#654); +#656=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#657=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#658=VECTOR('',#657,1.00000000000000000E0); +#659=LINE('',#656,#658); +#660=DEFINITIONAL_REPRESENTATION('',(#659),#1); +#661=PCURVE('',#126,#660); +#662=SURFACE_CURVE('',#649,(#655,#661),.CURVE_3D.); +#663=EDGE_CURVE('',#313,#321,#662,.T.); +#664=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#665=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#666=VECTOR('',#665,1.00000000000000000E0); +#667=LINE('',#664,#666); +#668=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#669=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#670=VECTOR('',#669,1.00000000000000000E0); +#671=LINE('',#668,#670); +#672=DEFINITIONAL_REPRESENTATION('',(#671),#1); +#673=PCURVE('',#26,#672); +#674=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#675=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#676=VECTOR('',#675,1.00000000000000000E0); +#677=LINE('',#674,#676); +#678=DEFINITIONAL_REPRESENTATION('',(#677),#1); +#679=PCURVE('',#36,#678); +#680=SURFACE_CURVE('',#667,(#673,#679),.CURVE_3D.); +#681=EDGE_CURVE('',#317,#321,#680,.T.); +#682=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#683=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#684=VECTOR('',#683,1.00000000000000000E0); +#685=LINE('',#682,#684); +#686=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#687=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#688=VECTOR('',#687,1.00000000000000000E0); +#689=LINE('',#686,#688); +#690=DEFINITIONAL_REPRESENTATION('',(#689),#1); +#691=PCURVE('',#31,#690); +#692=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#693=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#694=VECTOR('',#693,1.00000000000000000E0); +#695=LINE('',#692,#694); +#696=DEFINITIONAL_REPRESENTATION('',(#695),#1); +#697=PCURVE('',#41,#696); +#698=SURFACE_CURVE('',#685,(#691,#697),.CURVE_3D.); +#699=EDGE_CURVE('',#323,#325,#698,.T.); +#700=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#701=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#702=VECTOR('',#701,1.00000000000000000E0); +#703=LINE('',#700,#702); +#704=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#705=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#706=VECTOR('',#705,1.00000000000000000E0); +#707=LINE('',#704,#706); +#708=DEFINITIONAL_REPRESENTATION('',(#707),#1); +#709=PCURVE('',#31,#708); +#710=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#711=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#712=VECTOR('',#711,1.00000000000000000E0); +#713=LINE('',#710,#712); +#714=DEFINITIONAL_REPRESENTATION('',(#713),#1); +#715=PCURVE('',#66,#714); +#716=SURFACE_CURVE('',#703,(#709,#715),.CURVE_3D.); +#717=EDGE_CURVE('',#319,#325,#716,.T.); +#718=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#719=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#720=VECTOR('',#719,1.00000000000000000E0); +#721=LINE('',#718,#720); +#722=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#723=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#724=VECTOR('',#723,1.00000000000000000E0); +#725=LINE('',#722,#724); +#726=DEFINITIONAL_REPRESENTATION('',(#725),#1); +#727=PCURVE('',#31,#726); +#728=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#729=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#730=VECTOR('',#729,1.00000000000000000E0); +#731=LINE('',#728,#730); +#732=DEFINITIONAL_REPRESENTATION('',(#731),#1); +#733=PCURVE('',#36,#732); +#734=SURFACE_CURVE('',#721,(#727,#733),.CURVE_3D.); +#735=EDGE_CURVE('',#317,#323,#734,.T.); +#736=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#737=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#738=VECTOR('',#737,1.00000000000000000E0); +#739=LINE('',#736,#738); +#740=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#741=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#742=VECTOR('',#741,1.00000000000000000E0); +#743=LINE('',#740,#742); +#744=DEFINITIONAL_REPRESENTATION('',(#743),#1); +#745=PCURVE('',#36,#744); +#746=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#747=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#748=VECTOR('',#747,1.00000000000000000E0); +#749=LINE('',#746,#748); +#750=DEFINITIONAL_REPRESENTATION('',(#749),#1); +#751=PCURVE('',#136,#750); +#752=SURFACE_CURVE('',#739,(#745,#751),.CURVE_3D.); +#753=EDGE_CURVE('',#321,#327,#752,.T.); +#754=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#755=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#756=VECTOR('',#755,1.00000000000000000E0); +#757=LINE('',#754,#756); +#758=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#759=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#760=VECTOR('',#759,1.00000000000000000E0); +#761=LINE('',#758,#760); +#762=DEFINITIONAL_REPRESENTATION('',(#761),#1); +#763=PCURVE('',#36,#762); +#764=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#765=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#766=VECTOR('',#765,1.00000000000000000E0); +#767=LINE('',#764,#766); +#768=DEFINITIONAL_REPRESENTATION('',(#767),#1); +#769=PCURVE('',#41,#768); +#770=SURFACE_CURVE('',#757,(#763,#769),.CURVE_3D.); +#771=EDGE_CURVE('',#323,#327,#770,.T.); +#772=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#773=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#774=VECTOR('',#773,1.00000000000000000E0); +#775=LINE('',#772,#774); +#776=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#777=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#778=VECTOR('',#777,1.00000000000000000E0); +#779=LINE('',#776,#778); +#780=DEFINITIONAL_REPRESENTATION('',(#779),#1); +#781=PCURVE('',#41,#780); +#782=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#783=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#784=VECTOR('',#783,1.00000000000000000E0); +#785=LINE('',#782,#784); +#786=DEFINITIONAL_REPRESENTATION('',(#785),#1); +#787=PCURVE('',#141,#786); +#788=SURFACE_CURVE('',#775,(#781,#787),.CURVE_3D.); +#789=EDGE_CURVE('',#327,#329,#788,.T.); +#790=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#791=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#792=VECTOR('',#791,1.00000000000000000E0); +#793=LINE('',#790,#792); +#794=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#795=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#796=VECTOR('',#795,1.00000000000000000E0); +#797=LINE('',#794,#796); +#798=DEFINITIONAL_REPRESENTATION('',(#797),#1); +#799=PCURVE('',#41,#798); +#800=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#801=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#802=VECTOR('',#801,1.00000000000000000E0); +#803=LINE('',#800,#802); +#804=DEFINITIONAL_REPRESENTATION('',(#803),#1); +#805=PCURVE('',#71,#804); +#806=SURFACE_CURVE('',#793,(#799,#805),.CURVE_3D.); +#807=EDGE_CURVE('',#325,#329,#806,.T.); +#808=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#809=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#810=VECTOR('',#809,1.00000000000000000E0); +#811=LINE('',#808,#810); +#812=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#813=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#814=VECTOR('',#813,1.00000000000000000E0); +#815=LINE('',#812,#814); +#816=DEFINITIONAL_REPRESENTATION('',(#815),#1); +#817=PCURVE('',#46,#816); +#818=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#819=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#820=VECTOR('',#819,1.00000000000000000E0); +#821=LINE('',#818,#820); +#822=DEFINITIONAL_REPRESENTATION('',(#821),#1); +#823=PCURVE('',#56,#822); +#824=SURFACE_CURVE('',#811,(#817,#823),.CURVE_3D.); +#825=EDGE_CURVE('',#305,#331,#824,.T.); +#826=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#827=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#828=VECTOR('',#827,1.00000000000000000E0); +#829=LINE('',#826,#828); +#830=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#831=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#832=VECTOR('',#831,1.00000000000000000E0); +#833=LINE('',#830,#832); +#834=DEFINITIONAL_REPRESENTATION('',(#833),#1); +#835=PCURVE('',#46,#834); +#836=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#837=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#838=VECTOR('',#837,1.00000000000000000E0); +#839=LINE('',#836,#838); +#840=DEFINITIONAL_REPRESENTATION('',(#839),#1); +#841=PCURVE('',#76,#840); +#842=SURFACE_CURVE('',#829,(#835,#841),.CURVE_3D.); +#843=EDGE_CURVE('',#333,#331,#842,.T.); +#844=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#845=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#846=VECTOR('',#845,1.00000000000000000E0); +#847=LINE('',#844,#846); +#848=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#849=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#850=VECTOR('',#849,1.00000000000000000E0); +#851=LINE('',#848,#850); +#852=DEFINITIONAL_REPRESENTATION('',(#851),#1); +#853=PCURVE('',#46,#852); +#854=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#855=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#856=VECTOR('',#855,1.00000000000000000E0); +#857=LINE('',#854,#856); +#858=DEFINITIONAL_REPRESENTATION('',(#857),#1); +#859=PCURVE('',#51,#858); +#860=SURFACE_CURVE('',#847,(#853,#859),.CURVE_3D.); +#861=EDGE_CURVE('',#307,#333,#860,.T.); +#862=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#863=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#864=VECTOR('',#863,1.00000000000000000E0); +#865=LINE('',#862,#864); +#866=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#867=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#868=VECTOR('',#867,1.00000000000000000E0); +#869=LINE('',#866,#868); +#870=DEFINITIONAL_REPRESENTATION('',(#869),#1); +#871=PCURVE('',#51,#870); +#872=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#873=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#874=VECTOR('',#873,1.00000000000000000E0); +#875=LINE('',#872,#874); +#876=DEFINITIONAL_REPRESENTATION('',(#875),#1); +#877=PCURVE('',#86,#876); +#878=SURFACE_CURVE('',#865,(#871,#877),.CURVE_3D.); +#879=EDGE_CURVE('',#333,#335,#878,.T.); +#880=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#881=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#882=VECTOR('',#881,1.00000000000000000E0); +#883=LINE('',#880,#882); +#884=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#885=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#886=VECTOR('',#885,1.00000000000000000E0); +#887=LINE('',#884,#886); +#888=DEFINITIONAL_REPRESENTATION('',(#887),#1); +#889=PCURVE('',#51,#888); +#890=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#891=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#892=VECTOR('',#891,1.00000000000000000E0); +#893=LINE('',#890,#892); +#894=DEFINITIONAL_REPRESENTATION('',(#893),#1); +#895=PCURVE('',#146,#894); +#896=SURFACE_CURVE('',#883,(#889,#895),.CURVE_3D.); +#897=EDGE_CURVE('',#315,#335,#896,.T.); +#898=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#899=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#900=VECTOR('',#899,1.00000000000000000E0); +#901=LINE('',#898,#900); +#902=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#903=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#904=VECTOR('',#903,1.00000000000000000E0); +#905=LINE('',#902,#904); +#906=DEFINITIONAL_REPRESENTATION('',(#905),#1); +#907=PCURVE('',#56,#906); +#908=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#909=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#910=VECTOR('',#909,1.00000000000000000E0); +#911=LINE('',#908,#910); +#912=DEFINITIONAL_REPRESENTATION('',(#911),#1); +#913=PCURVE('',#66,#912); +#914=SURFACE_CURVE('',#901,(#907,#913),.CURVE_3D.); +#915=EDGE_CURVE('',#319,#337,#914,.T.); +#916=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#917=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#918=VECTOR('',#917,1.00000000000000000E0); +#919=LINE('',#916,#918); +#920=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#921=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#922=VECTOR('',#921,1.00000000000000000E0); +#923=LINE('',#920,#922); +#924=DEFINITIONAL_REPRESENTATION('',(#923),#1); +#925=PCURVE('',#56,#924); +#926=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#927=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#928=VECTOR('',#927,1.00000000000000000E0); +#929=LINE('',#926,#928); +#930=DEFINITIONAL_REPRESENTATION('',(#929),#1); +#931=PCURVE('',#91,#930); +#932=SURFACE_CURVE('',#919,(#925,#931),.CURVE_3D.); +#933=EDGE_CURVE('',#331,#337,#932,.T.); +#934=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#935=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#936=VECTOR('',#935,1.00000000000000000E0); +#937=LINE('',#934,#936); +#938=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#939=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#940=VECTOR('',#939,1.00000000000000000E0); +#941=LINE('',#938,#940); +#942=DEFINITIONAL_REPRESENTATION('',(#941),#1); +#943=PCURVE('',#61,#942); +#944=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#945=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#946=VECTOR('',#945,1.00000000000000000E0); +#947=LINE('',#944,#946); +#948=DEFINITIONAL_REPRESENTATION('',(#947),#1); +#949=PCURVE('',#151,#948); +#950=SURFACE_CURVE('',#937,(#943,#949),.CURVE_3D.); +#951=EDGE_CURVE('',#339,#341,#950,.T.); +#952=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#953=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#954=VECTOR('',#953,1.00000000000000000E0); +#955=LINE('',#952,#954); +#956=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#957=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#958=VECTOR('',#957,1.00000000000000000E0); +#959=LINE('',#956,#958); +#960=DEFINITIONAL_REPRESENTATION('',(#959),#1); +#961=PCURVE('',#61,#960); +#962=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#963=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#964=VECTOR('',#963,1.00000000000000000E0); +#965=LINE('',#962,#964); +#966=DEFINITIONAL_REPRESENTATION('',(#965),#1); +#967=PCURVE('',#176,#966); +#968=SURFACE_CURVE('',#955,(#961,#967),.CURVE_3D.); +#969=EDGE_CURVE('',#341,#343,#968,.T.); +#970=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#971=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#972=VECTOR('',#971,1.00000000000000000E0); +#973=LINE('',#970,#972); +#974=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#975=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#976=VECTOR('',#975,1.00000000000000000E0); +#977=LINE('',#974,#976); +#978=DEFINITIONAL_REPRESENTATION('',(#977),#1); +#979=PCURVE('',#61,#978); +#980=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#981=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#982=VECTOR('',#981,1.00000000000000000E0); +#983=LINE('',#980,#982); +#984=DEFINITIONAL_REPRESENTATION('',(#983),#1); +#985=PCURVE('',#156,#984); +#986=SURFACE_CURVE('',#973,(#979,#985),.CURVE_3D.); +#987=EDGE_CURVE('',#345,#343,#986,.T.); +#988=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#989=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#990=VECTOR('',#989,1.00000000000000000E0); +#991=LINE('',#988,#990); +#992=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#993=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#994=VECTOR('',#993,1.00000000000000000E0); +#995=LINE('',#992,#994); +#996=DEFINITIONAL_REPRESENTATION('',(#995),#1); +#997=PCURVE('',#61,#996); +#998=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#999=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1000=VECTOR('',#999,1.00000000000000000E0); +#1001=LINE('',#998,#1000); +#1002=DEFINITIONAL_REPRESENTATION('',(#1001),#1); +#1003=PCURVE('',#131,#1002); +#1004=SURFACE_CURVE('',#991,(#997,#1003),.CURVE_3D.); +#1005=EDGE_CURVE('',#339,#345,#1004,.T.); +#1006=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#1007=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1008=VECTOR('',#1007,1.00000000000000000E0); +#1009=LINE('',#1006,#1008); +#1010=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0)); +#1011=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1012=VECTOR('',#1011,1.00000000000000000E0); +#1013=LINE('',#1010,#1012); +#1014=DEFINITIONAL_REPRESENTATION('',(#1013),#1); +#1015=PCURVE('',#66,#1014); +#1016=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1017=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1018=VECTOR('',#1017,1.00000000000000000E0); +#1019=LINE('',#1016,#1018); +#1020=DEFINITIONAL_REPRESENTATION('',(#1019),#1); +#1021=PCURVE('',#71,#1020); +#1022=SURFACE_CURVE('',#1009,(#1015,#1021),.CURVE_3D.); +#1023=EDGE_CURVE('',#325,#347,#1022,.T.); +#1024=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#1025=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1026=VECTOR('',#1025,1.00000000000000000E0); +#1027=LINE('',#1024,#1026); +#1028=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1029=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1030=VECTOR('',#1029,1.00000000000000000E0); +#1031=LINE('',#1028,#1030); +#1032=DEFINITIONAL_REPRESENTATION('',(#1031),#1); +#1033=PCURVE('',#66,#1032); +#1034=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1035=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1036=VECTOR('',#1035,1.00000000000000000E0); +#1037=LINE('',#1034,#1036); +#1038=DEFINITIONAL_REPRESENTATION('',(#1037),#1); +#1039=PCURVE('',#101,#1038); +#1040=SURFACE_CURVE('',#1027,(#1033,#1039),.CURVE_3D.); +#1041=EDGE_CURVE('',#337,#347,#1040,.T.); +#1042=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#1043=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1044=VECTOR('',#1043,1.00000000000000000E0); +#1045=LINE('',#1042,#1044); +#1046=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1047=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1048=VECTOR('',#1047,1.00000000000000000E0); +#1049=LINE('',#1046,#1048); +#1050=DEFINITIONAL_REPRESENTATION('',(#1049),#1); +#1051=PCURVE('',#71,#1050); +#1052=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1053=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1054=VECTOR('',#1053,1.00000000000000000E0); +#1055=LINE('',#1052,#1054); +#1056=DEFINITIONAL_REPRESENTATION('',(#1055),#1); +#1057=PCURVE('',#161,#1056); +#1058=SURFACE_CURVE('',#1045,(#1051,#1057),.CURVE_3D.); +#1059=EDGE_CURVE('',#329,#349,#1058,.T.); +#1060=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-2.00000000000000000E0)); +#1061=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1062=VECTOR('',#1061,1.00000000000000000E0); +#1063=LINE('',#1060,#1062); +#1064=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#1065=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1066=VECTOR('',#1065,1.00000000000000000E0); +#1067=LINE('',#1064,#1066); +#1068=DEFINITIONAL_REPRESENTATION('',(#1067),#1); +#1069=PCURVE('',#71,#1068); +#1070=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#1071=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1072=VECTOR('',#1071,1.00000000000000000E0); +#1073=LINE('',#1070,#1072); +#1074=DEFINITIONAL_REPRESENTATION('',(#1073),#1); +#1075=PCURVE('',#111,#1074); +#1076=SURFACE_CURVE('',#1063,(#1069,#1075),.CURVE_3D.); +#1077=EDGE_CURVE('',#347,#349,#1076,.T.); +#1078=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#1079=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1080=VECTOR('',#1079,1.00000000000000000E0); +#1081=LINE('',#1078,#1080); +#1082=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1083=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1084=VECTOR('',#1083,1.00000000000000000E0); +#1085=LINE('',#1082,#1084); +#1086=DEFINITIONAL_REPRESENTATION('',(#1085),#1); +#1087=PCURVE('',#76,#1086); +#1088=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1089=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1090=VECTOR('',#1089,1.00000000000000000E0); +#1091=LINE('',#1088,#1090); +#1092=DEFINITIONAL_REPRESENTATION('',(#1091),#1); +#1093=PCURVE('',#91,#1092); +#1094=SURFACE_CURVE('',#1081,(#1087,#1093),.CURVE_3D.); +#1095=EDGE_CURVE('',#331,#351,#1094,.T.); +#1096=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#1097=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1098=VECTOR('',#1097,1.00000000000000000E0); +#1099=LINE('',#1096,#1098); +#1100=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#1101=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1102=VECTOR('',#1101,1.00000000000000000E0); +#1103=LINE('',#1100,#1102); +#1104=DEFINITIONAL_REPRESENTATION('',(#1103),#1); +#1105=PCURVE('',#76,#1104); +#1106=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1107=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1108=VECTOR('',#1107,1.00000000000000000E0); +#1109=LINE('',#1106,#1108); +#1110=DEFINITIONAL_REPRESENTATION('',(#1109),#1); +#1111=PCURVE('',#81,#1110); +#1112=SURFACE_CURVE('',#1099,(#1105,#1111),.CURVE_3D.); +#1113=EDGE_CURVE('',#353,#351,#1112,.T.); +#1114=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#1115=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1116=VECTOR('',#1115,1.00000000000000000E0); +#1117=LINE('',#1114,#1116); +#1118=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#1119=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1120=VECTOR('',#1119,1.00000000000000000E0); +#1121=LINE('',#1118,#1120); +#1122=DEFINITIONAL_REPRESENTATION('',(#1121),#1); +#1123=PCURVE('',#76,#1122); +#1124=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#1125=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1126=VECTOR('',#1125,1.00000000000000000E0); +#1127=LINE('',#1124,#1126); +#1128=DEFINITIONAL_REPRESENTATION('',(#1127),#1); +#1129=PCURVE('',#86,#1128); +#1130=SURFACE_CURVE('',#1117,(#1123,#1129),.CURVE_3D.); +#1131=EDGE_CURVE('',#333,#353,#1130,.T.); +#1132=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,-3.00000000000000000E0)); +#1133=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1134=VECTOR('',#1133,1.00000000000000000E0); +#1135=LINE('',#1132,#1134); +#1136=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1137=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1138=VECTOR('',#1137,1.00000000000000000E0); +#1139=LINE('',#1136,#1138); +#1140=DEFINITIONAL_REPRESENTATION('',(#1139),#1); +#1141=PCURVE('',#81,#1140); +#1142=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1143=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1144=VECTOR('',#1143,1.00000000000000000E0); +#1145=LINE('',#1142,#1144); +#1146=DEFINITIONAL_REPRESENTATION('',(#1145),#1); +#1147=PCURVE('',#96,#1146); +#1148=SURFACE_CURVE('',#1135,(#1141,#1147),.CURVE_3D.); +#1149=EDGE_CURVE('',#351,#355,#1148,.T.); +#1150=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#1151=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1152=VECTOR('',#1151,1.00000000000000000E0); +#1153=LINE('',#1150,#1152); +#1154=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1155=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1156=VECTOR('',#1155,1.00000000000000000E0); +#1157=LINE('',#1154,#1156); +#1158=DEFINITIONAL_REPRESENTATION('',(#1157),#1); +#1159=PCURVE('',#81,#1158); +#1160=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1161=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1162=VECTOR('',#1161,1.00000000000000000E0); +#1163=LINE('',#1160,#1162); +#1164=DEFINITIONAL_REPRESENTATION('',(#1163),#1); +#1165=PCURVE('',#166,#1164); +#1166=SURFACE_CURVE('',#1153,(#1159,#1165),.CURVE_3D.); +#1167=EDGE_CURVE('',#357,#355,#1166,.T.); +#1168=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#1169=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1170=VECTOR('',#1169,1.00000000000000000E0); +#1171=LINE('',#1168,#1170); +#1172=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1173=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1174=VECTOR('',#1173,1.00000000000000000E0); +#1175=LINE('',#1172,#1174); +#1176=DEFINITIONAL_REPRESENTATION('',(#1175),#1); +#1177=PCURVE('',#81,#1176); +#1178=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#1179=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1180=VECTOR('',#1179,1.00000000000000000E0); +#1181=LINE('',#1178,#1180); +#1182=DEFINITIONAL_REPRESENTATION('',(#1181),#1); +#1183=PCURVE('',#86,#1182); +#1184=SURFACE_CURVE('',#1171,(#1177,#1183),.CURVE_3D.); +#1185=EDGE_CURVE('',#353,#357,#1184,.T.); +#1186=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#1187=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1188=VECTOR('',#1187,1.00000000000000000E0); +#1189=LINE('',#1186,#1188); +#1190=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1191=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1192=VECTOR('',#1191,1.00000000000000000E0); +#1193=LINE('',#1190,#1192); +#1194=DEFINITIONAL_REPRESENTATION('',(#1193),#1); +#1195=PCURVE('',#86,#1194); +#1196=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1197=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1198=VECTOR('',#1197,1.00000000000000000E0); +#1199=LINE('',#1196,#1198); +#1200=DEFINITIONAL_REPRESENTATION('',(#1199),#1); +#1201=PCURVE('',#171,#1200); +#1202=SURFACE_CURVE('',#1189,(#1195,#1201),.CURVE_3D.); +#1203=EDGE_CURVE('',#335,#357,#1202,.T.); +#1204=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#1205=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1206=VECTOR('',#1205,1.00000000000000000E0); +#1207=LINE('',#1204,#1206); +#1208=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1209=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1210=VECTOR('',#1209,1.00000000000000000E0); +#1211=LINE('',#1208,#1210); +#1212=DEFINITIONAL_REPRESENTATION('',(#1211),#1); +#1213=PCURVE('',#91,#1212); +#1214=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1215=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1216=VECTOR('',#1215,1.00000000000000000E0); +#1217=LINE('',#1214,#1216); +#1218=DEFINITIONAL_REPRESENTATION('',(#1217),#1); +#1219=PCURVE('',#101,#1218); +#1220=SURFACE_CURVE('',#1207,(#1213,#1219),.CURVE_3D.); +#1221=EDGE_CURVE('',#337,#359,#1220,.T.); +#1222=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,-3.00000000000000000E0)); +#1223=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1224=VECTOR('',#1223,1.00000000000000000E0); +#1225=LINE('',#1222,#1224); +#1226=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0)); +#1227=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1228=VECTOR('',#1227,1.00000000000000000E0); +#1229=LINE('',#1226,#1228); +#1230=DEFINITIONAL_REPRESENTATION('',(#1229),#1); +#1231=PCURVE('',#91,#1230); +#1232=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1233=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1234=VECTOR('',#1233,1.00000000000000000E0); +#1235=LINE('',#1232,#1234); +#1236=DEFINITIONAL_REPRESENTATION('',(#1235),#1); +#1237=PCURVE('',#96,#1236); +#1238=SURFACE_CURVE('',#1225,(#1231,#1237),.CURVE_3D.); +#1239=EDGE_CURVE('',#351,#359,#1238,.T.); +#1240=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,-3.00000000000000000E0)); +#1241=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1242=VECTOR('',#1241,1.00000000000000000E0); +#1243=LINE('',#1240,#1242); +#1244=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#1245=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1246=VECTOR('',#1245,1.00000000000000000E0); +#1247=LINE('',#1244,#1246); +#1248=DEFINITIONAL_REPRESENTATION('',(#1247),#1); +#1249=PCURVE('',#96,#1248); +#1250=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#1251=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1252=VECTOR('',#1251,1.00000000000000000E0); +#1253=LINE('',#1250,#1252); +#1254=DEFINITIONAL_REPRESENTATION('',(#1253),#1); +#1255=PCURVE('',#106,#1254); +#1256=SURFACE_CURVE('',#1243,(#1249,#1255),.CURVE_3D.); +#1257=EDGE_CURVE('',#359,#361,#1256,.T.); +#1258=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-3.00000000000000000E0)); +#1259=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1260=VECTOR('',#1259,1.00000000000000000E0); +#1261=LINE('',#1258,#1260); +#1262=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1263=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1264=VECTOR('',#1263,1.00000000000000000E0); +#1265=LINE('',#1262,#1264); +#1266=DEFINITIONAL_REPRESENTATION('',(#1265),#1); +#1267=PCURVE('',#96,#1266); +#1268=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1269=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1270=VECTOR('',#1269,1.00000000000000000E0); +#1271=LINE('',#1268,#1270); +#1272=DEFINITIONAL_REPRESENTATION('',(#1271),#1); +#1273=PCURVE('',#181,#1272); +#1274=SURFACE_CURVE('',#1261,(#1267,#1273),.CURVE_3D.); +#1275=EDGE_CURVE('',#355,#361,#1274,.T.); +#1276=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-2.00000000000000000E0)); +#1277=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1278=VECTOR('',#1277,1.00000000000000000E0); +#1279=LINE('',#1276,#1278); +#1280=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0)); +#1281=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1282=VECTOR('',#1281,1.00000000000000000E0); +#1283=LINE('',#1280,#1282); +#1284=DEFINITIONAL_REPRESENTATION('',(#1283),#1); +#1285=PCURVE('',#101,#1284); +#1286=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#1287=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1288=VECTOR('',#1287,1.00000000000000000E0); +#1289=LINE('',#1286,#1288); +#1290=DEFINITIONAL_REPRESENTATION('',(#1289),#1); +#1291=PCURVE('',#111,#1290); +#1292=SURFACE_CURVE('',#1279,(#1285,#1291),.CURVE_3D.); +#1293=EDGE_CURVE('',#347,#363,#1292,.T.); +#1294=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,-3.00000000000000000E0)); +#1295=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1296=VECTOR('',#1295,1.00000000000000000E0); +#1297=LINE('',#1294,#1296); +#1298=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0)); +#1299=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1300=VECTOR('',#1299,1.00000000000000000E0); +#1301=LINE('',#1298,#1300); +#1302=DEFINITIONAL_REPRESENTATION('',(#1301),#1); +#1303=PCURVE('',#101,#1302); +#1304=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#1305=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1306=VECTOR('',#1305,1.00000000000000000E0); +#1307=LINE('',#1304,#1306); +#1308=DEFINITIONAL_REPRESENTATION('',(#1307),#1); +#1309=PCURVE('',#106,#1308); +#1310=SURFACE_CURVE('',#1297,(#1303,#1309),.CURVE_3D.); +#1311=EDGE_CURVE('',#359,#363,#1310,.T.); +#1312=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#1313=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1314=VECTOR('',#1313,1.00000000000000000E0); +#1315=LINE('',#1312,#1314); +#1316=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#1317=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1318=VECTOR('',#1317,1.00000000000000000E0); +#1319=LINE('',#1316,#1318); +#1320=DEFINITIONAL_REPRESENTATION('',(#1319),#1); +#1321=PCURVE('',#106,#1320); +#1322=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#1323=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1324=VECTOR('',#1323,1.00000000000000000E0); +#1325=LINE('',#1322,#1324); +#1326=DEFINITIONAL_REPRESENTATION('',(#1325),#1); +#1327=PCURVE('',#111,#1326); +#1328=SURFACE_CURVE('',#1315,(#1321,#1327),.CURVE_3D.); +#1329=EDGE_CURVE('',#363,#365,#1328,.T.); +#1330=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,-3.00000000000000000E0)); +#1331=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1332=VECTOR('',#1331,1.00000000000000000E0); +#1333=LINE('',#1330,#1332); +#1334=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1335=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1336=VECTOR('',#1335,1.00000000000000000E0); +#1337=LINE('',#1334,#1336); +#1338=DEFINITIONAL_REPRESENTATION('',(#1337),#1); +#1339=PCURVE('',#106,#1338); +#1340=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1341=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1342=VECTOR('',#1341,1.00000000000000000E0); +#1343=LINE('',#1340,#1342); +#1344=DEFINITIONAL_REPRESENTATION('',(#1343),#1); +#1345=PCURVE('',#186,#1344); +#1346=SURFACE_CURVE('',#1333,(#1339,#1345),.CURVE_3D.); +#1347=EDGE_CURVE('',#361,#365,#1346,.T.); +#1348=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0,-2.00000000000000000E0)); +#1349=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1350=VECTOR('',#1349,1.00000000000000000E0); +#1351=LINE('',#1348,#1350); +#1352=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1353=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1354=VECTOR('',#1353,1.00000000000000000E0); +#1355=LINE('',#1352,#1354); +#1356=DEFINITIONAL_REPRESENTATION('',(#1355),#1); +#1357=PCURVE('',#111,#1356); +#1358=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1359=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1360=VECTOR('',#1359,1.00000000000000000E0); +#1361=LINE('',#1358,#1360); +#1362=DEFINITIONAL_REPRESENTATION('',(#1361),#1); +#1363=PCURVE('',#191,#1362); +#1364=SURFACE_CURVE('',#1351,(#1357,#1363),.CURVE_3D.); +#1365=EDGE_CURVE('',#349,#365,#1364,.T.); +#1366=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1367=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1368=VECTOR('',#1367,1.00000000000000000E0); +#1369=LINE('',#1366,#1368); +#1370=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#1371=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1372=VECTOR('',#1371,1.00000000000000000E0); +#1373=LINE('',#1370,#1372); +#1374=DEFINITIONAL_REPRESENTATION('',(#1373),#1); +#1375=PCURVE('',#116,#1374); +#1376=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#1377=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1378=VECTOR('',#1377,1.00000000000000000E0); +#1379=LINE('',#1376,#1378); +#1380=DEFINITIONAL_REPRESENTATION('',(#1379),#1); +#1381=PCURVE('',#201,#1380); +#1382=SURFACE_CURVE('',#1369,(#1375,#1381),.CURVE_3D.); +#1383=EDGE_CURVE('',#367,#369,#1382,.T.); +#1384=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1385=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1386=VECTOR('',#1385,1.00000000000000000E0); +#1387=LINE('',#1384,#1386); +#1388=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1389=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1390=VECTOR('',#1389,1.00000000000000000E0); +#1391=LINE('',#1388,#1390); +#1392=DEFINITIONAL_REPRESENTATION('',(#1391),#1); +#1393=PCURVE('',#116,#1392); +#1394=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1395=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1396=VECTOR('',#1395,1.00000000000000000E0); +#1397=LINE('',#1394,#1396); +#1398=DEFINITIONAL_REPRESENTATION('',(#1397),#1); +#1399=PCURVE('',#126,#1398); +#1400=SURFACE_CURVE('',#1387,(#1393,#1399),.CURVE_3D.); +#1401=EDGE_CURVE('',#313,#369,#1400,.T.); +#1402=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1403=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1404=VECTOR('',#1403,1.00000000000000000E0); +#1405=LINE('',#1402,#1404); +#1406=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1407=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1408=VECTOR('',#1407,1.00000000000000000E0); +#1409=LINE('',#1406,#1408); +#1410=DEFINITIONAL_REPRESENTATION('',(#1409),#1); +#1411=PCURVE('',#116,#1410); +#1412=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1413=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1414=VECTOR('',#1413,1.00000000000000000E0); +#1415=LINE('',#1412,#1414); +#1416=DEFINITIONAL_REPRESENTATION('',(#1415),#1); +#1417=PCURVE('',#121,#1416); +#1418=SURFACE_CURVE('',#1405,(#1411,#1417),.CURVE_3D.); +#1419=EDGE_CURVE('',#311,#367,#1418,.T.); +#1420=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1421=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1422=VECTOR('',#1421,1.00000000000000000E0); +#1423=LINE('',#1420,#1422); +#1424=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1425=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1426=VECTOR('',#1425,1.00000000000000000E0); +#1427=LINE('',#1424,#1426); +#1428=DEFINITIONAL_REPRESENTATION('',(#1427),#1); +#1429=PCURVE('',#121,#1428); +#1430=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1431=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1432=VECTOR('',#1431,1.00000000000000000E0); +#1433=LINE('',#1430,#1432); +#1434=DEFINITIONAL_REPRESENTATION('',(#1433),#1); +#1435=PCURVE('',#146,#1434); +#1436=SURFACE_CURVE('',#1423,(#1429,#1435),.CURVE_3D.); +#1437=EDGE_CURVE('',#315,#371,#1436,.T.); +#1438=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1439=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1440=VECTOR('',#1439,1.00000000000000000E0); +#1441=LINE('',#1438,#1440); +#1442=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#1443=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1444=VECTOR('',#1443,1.00000000000000000E0); +#1445=LINE('',#1442,#1444); +#1446=DEFINITIONAL_REPRESENTATION('',(#1445),#1); +#1447=PCURVE('',#121,#1446); +#1448=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#1449=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1450=VECTOR('',#1449,1.00000000000000000E0); +#1451=LINE('',#1448,#1450); +#1452=DEFINITIONAL_REPRESENTATION('',(#1451),#1); +#1453=PCURVE('',#206,#1452); +#1454=SURFACE_CURVE('',#1441,(#1447,#1453),.CURVE_3D.); +#1455=EDGE_CURVE('',#367,#371,#1454,.T.); +#1456=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1457=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1458=VECTOR('',#1457,1.00000000000000000E0); +#1459=LINE('',#1456,#1458); +#1460=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1461=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1462=VECTOR('',#1461,1.00000000000000000E0); +#1463=LINE('',#1460,#1462); +#1464=DEFINITIONAL_REPRESENTATION('',(#1463),#1); +#1465=PCURVE('',#126,#1464); +#1466=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1467=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1468=VECTOR('',#1467,1.00000000000000000E0); +#1469=LINE('',#1466,#1468); +#1470=DEFINITIONAL_REPRESENTATION('',(#1469),#1); +#1471=PCURVE('',#216,#1470); +#1472=SURFACE_CURVE('',#1459,(#1465,#1471),.CURVE_3D.); +#1473=EDGE_CURVE('',#369,#373,#1472,.T.); +#1474=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#1475=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1476=VECTOR('',#1475,1.00000000000000000E0); +#1477=LINE('',#1474,#1476); +#1478=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1479=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1480=VECTOR('',#1479,1.00000000000000000E0); +#1481=LINE('',#1478,#1480); +#1482=DEFINITIONAL_REPRESENTATION('',(#1481),#1); +#1483=PCURVE('',#126,#1482); +#1484=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1485=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1486=VECTOR('',#1485,1.00000000000000000E0); +#1487=LINE('',#1484,#1486); +#1488=DEFINITIONAL_REPRESENTATION('',(#1487),#1); +#1489=PCURVE('',#136,#1488); +#1490=SURFACE_CURVE('',#1477,(#1483,#1489),.CURVE_3D.); +#1491=EDGE_CURVE('',#321,#373,#1490,.T.); +#1492=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#1493=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1494=VECTOR('',#1493,1.00000000000000000E0); +#1495=LINE('',#1492,#1494); +#1496=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1497=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1498=VECTOR('',#1497,1.00000000000000000E0); +#1499=LINE('',#1496,#1498); +#1500=DEFINITIONAL_REPRESENTATION('',(#1499),#1); +#1501=PCURVE('',#131,#1500); +#1502=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1503=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1504=VECTOR('',#1503,1.00000000000000000E0); +#1505=LINE('',#1502,#1504); +#1506=DEFINITIONAL_REPRESENTATION('',(#1505),#1); +#1507=PCURVE('',#156,#1506); +#1508=SURFACE_CURVE('',#1495,(#1501,#1507),.CURVE_3D.); +#1509=EDGE_CURVE('',#345,#375,#1508,.T.); +#1510=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#1511=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1512=VECTOR('',#1511,1.00000000000000000E0); +#1513=LINE('',#1510,#1512); +#1514=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1515=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1516=VECTOR('',#1515,1.00000000000000000E0); +#1517=LINE('',#1514,#1516); +#1518=DEFINITIONAL_REPRESENTATION('',(#1517),#1); +#1519=PCURVE('',#131,#1518); +#1520=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1521=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1522=VECTOR('',#1521,1.00000000000000000E0); +#1523=LINE('',#1520,#1522); +#1524=DEFINITIONAL_REPRESENTATION('',(#1523),#1); +#1525=PCURVE('',#246,#1524); +#1526=SURFACE_CURVE('',#1513,(#1519,#1525),.CURVE_3D.); +#1527=EDGE_CURVE('',#377,#375,#1526,.T.); +#1528=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#1529=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1530=VECTOR('',#1529,1.00000000000000000E0); +#1531=LINE('',#1528,#1530); +#1532=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1533=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1534=VECTOR('',#1533,1.00000000000000000E0); +#1535=LINE('',#1532,#1534); +#1536=DEFINITIONAL_REPRESENTATION('',(#1535),#1); +#1537=PCURVE('',#131,#1536); +#1538=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1539=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1540=VECTOR('',#1539,1.00000000000000000E0); +#1541=LINE('',#1538,#1540); +#1542=DEFINITIONAL_REPRESENTATION('',(#1541),#1); +#1543=PCURVE('',#151,#1542); +#1544=SURFACE_CURVE('',#1531,(#1537,#1543),.CURVE_3D.); +#1545=EDGE_CURVE('',#339,#377,#1544,.T.); +#1546=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#1547=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1548=VECTOR('',#1547,1.00000000000000000E0); +#1549=LINE('',#1546,#1548); +#1550=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1551=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1552=VECTOR('',#1551,1.00000000000000000E0); +#1553=LINE('',#1550,#1552); +#1554=DEFINITIONAL_REPRESENTATION('',(#1553),#1); +#1555=PCURVE('',#136,#1554); +#1556=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1557=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1558=VECTOR('',#1557,1.00000000000000000E0); +#1559=LINE('',#1556,#1558); +#1560=DEFINITIONAL_REPRESENTATION('',(#1559),#1); +#1561=PCURVE('',#226,#1560); +#1562=SURFACE_CURVE('',#1549,(#1555,#1561),.CURVE_3D.); +#1563=EDGE_CURVE('',#373,#379,#1562,.T.); +#1564=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#1565=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1566=VECTOR('',#1565,1.00000000000000000E0); +#1567=LINE('',#1564,#1566); +#1568=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0)); +#1569=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1570=VECTOR('',#1569,1.00000000000000000E0); +#1571=LINE('',#1568,#1570); +#1572=DEFINITIONAL_REPRESENTATION('',(#1571),#1); +#1573=PCURVE('',#136,#1572); +#1574=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1575=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1576=VECTOR('',#1575,1.00000000000000000E0); +#1577=LINE('',#1574,#1576); +#1578=DEFINITIONAL_REPRESENTATION('',(#1577),#1); +#1579=PCURVE('',#141,#1578); +#1580=SURFACE_CURVE('',#1567,(#1573,#1579),.CURVE_3D.); +#1581=EDGE_CURVE('',#327,#379,#1580,.T.); +#1582=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#1583=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1584=VECTOR('',#1583,1.00000000000000000E0); +#1585=LINE('',#1582,#1584); +#1586=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#1587=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1588=VECTOR('',#1587,1.00000000000000000E0); +#1589=LINE('',#1586,#1588); +#1590=DEFINITIONAL_REPRESENTATION('',(#1589),#1); +#1591=PCURVE('',#141,#1590); +#1592=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#1593=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1594=VECTOR('',#1593,1.00000000000000000E0); +#1595=LINE('',#1592,#1594); +#1596=DEFINITIONAL_REPRESENTATION('',(#1595),#1); +#1597=PCURVE('',#231,#1596); +#1598=SURFACE_CURVE('',#1585,(#1591,#1597),.CURVE_3D.); +#1599=EDGE_CURVE('',#379,#381,#1598,.T.); +#1600=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#1601=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1602=VECTOR('',#1601,1.00000000000000000E0); +#1603=LINE('',#1600,#1602); +#1604=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1605=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1606=VECTOR('',#1605,1.00000000000000000E0); +#1607=LINE('',#1604,#1606); +#1608=DEFINITIONAL_REPRESENTATION('',(#1607),#1); +#1609=PCURVE('',#141,#1608); +#1610=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1611=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1612=VECTOR('',#1611,1.00000000000000000E0); +#1613=LINE('',#1610,#1612); +#1614=DEFINITIONAL_REPRESENTATION('',(#1613),#1); +#1615=PCURVE('',#161,#1614); +#1616=SURFACE_CURVE('',#1603,(#1609,#1615),.CURVE_3D.); +#1617=EDGE_CURVE('',#329,#381,#1616,.T.); +#1618=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#1619=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1620=VECTOR('',#1619,1.00000000000000000E0); +#1621=LINE('',#1618,#1620); +#1622=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1623=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1624=VECTOR('',#1623,1.00000000000000000E0); +#1625=LINE('',#1622,#1624); +#1626=DEFINITIONAL_REPRESENTATION('',(#1625),#1); +#1627=PCURVE('',#146,#1626); +#1628=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1629=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1630=VECTOR('',#1629,1.00000000000000000E0); +#1631=LINE('',#1628,#1630); +#1632=DEFINITIONAL_REPRESENTATION('',(#1631),#1); +#1633=PCURVE('',#171,#1632); +#1634=SURFACE_CURVE('',#1621,(#1627,#1633),.CURVE_3D.); +#1635=EDGE_CURVE('',#335,#383,#1634,.T.); +#1636=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1637=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1638=VECTOR('',#1637,1.00000000000000000E0); +#1639=LINE('',#1636,#1638); +#1640=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1641=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1642=VECTOR('',#1641,1.00000000000000000E0); +#1643=LINE('',#1640,#1642); +#1644=DEFINITIONAL_REPRESENTATION('',(#1643),#1); +#1645=PCURVE('',#146,#1644); +#1646=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1647=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1648=VECTOR('',#1647,1.00000000000000000E0); +#1649=LINE('',#1646,#1648); +#1650=DEFINITIONAL_REPRESENTATION('',(#1649),#1); +#1651=PCURVE('',#241,#1650); +#1652=SURFACE_CURVE('',#1639,(#1645,#1651),.CURVE_3D.); +#1653=EDGE_CURVE('',#371,#383,#1652,.T.); +#1654=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#1655=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1656=VECTOR('',#1655,1.00000000000000000E0); +#1657=LINE('',#1654,#1656); +#1658=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1659=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1660=VECTOR('',#1659,1.00000000000000000E0); +#1661=LINE('',#1658,#1660); +#1662=DEFINITIONAL_REPRESENTATION('',(#1661),#1); +#1663=PCURVE('',#151,#1662); +#1664=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1665=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1666=VECTOR('',#1665,1.00000000000000000E0); +#1667=LINE('',#1664,#1666); +#1668=DEFINITIONAL_REPRESENTATION('',(#1667),#1); +#1669=PCURVE('',#246,#1668); +#1670=SURFACE_CURVE('',#1657,(#1663,#1669),.CURVE_3D.); +#1671=EDGE_CURVE('',#377,#385,#1670,.T.); +#1672=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#1673=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1674=VECTOR('',#1673,1.00000000000000000E0); +#1675=LINE('',#1672,#1674); +#1676=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1677=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1678=VECTOR('',#1677,1.00000000000000000E0); +#1679=LINE('',#1676,#1678); +#1680=DEFINITIONAL_REPRESENTATION('',(#1679),#1); +#1681=PCURVE('',#151,#1680); +#1682=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1683=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1684=VECTOR('',#1683,1.00000000000000000E0); +#1685=LINE('',#1682,#1684); +#1686=DEFINITIONAL_REPRESENTATION('',(#1685),#1); +#1687=PCURVE('',#176,#1686); +#1688=SURFACE_CURVE('',#1675,(#1681,#1687),.CURVE_3D.); +#1689=EDGE_CURVE('',#341,#385,#1688,.T.); +#1690=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#1691=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1692=VECTOR('',#1691,1.00000000000000000E0); +#1693=LINE('',#1690,#1692); +#1694=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1695=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1696=VECTOR('',#1695,1.00000000000000000E0); +#1697=LINE('',#1694,#1696); +#1698=DEFINITIONAL_REPRESENTATION('',(#1697),#1); +#1699=PCURVE('',#156,#1698); +#1700=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1701=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1702=VECTOR('',#1701,1.00000000000000000E0); +#1703=LINE('',#1700,#1702); +#1704=DEFINITIONAL_REPRESENTATION('',(#1703),#1); +#1705=PCURVE('',#176,#1704); +#1706=SURFACE_CURVE('',#1693,(#1699,#1705),.CURVE_3D.); +#1707=EDGE_CURVE('',#343,#387,#1706,.T.); +#1708=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#1709=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1710=VECTOR('',#1709,1.00000000000000000E0); +#1711=LINE('',#1708,#1710); +#1712=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1713=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1714=VECTOR('',#1713,1.00000000000000000E0); +#1715=LINE('',#1712,#1714); +#1716=DEFINITIONAL_REPRESENTATION('',(#1715),#1); +#1717=PCURVE('',#156,#1716); +#1718=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1719=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1720=VECTOR('',#1719,1.00000000000000000E0); +#1721=LINE('',#1718,#1720); +#1722=DEFINITIONAL_REPRESENTATION('',(#1721),#1); +#1723=PCURVE('',#246,#1722); +#1724=SURFACE_CURVE('',#1711,(#1717,#1723),.CURVE_3D.); +#1725=EDGE_CURVE('',#375,#387,#1724,.T.); +#1726=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#1727=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1728=VECTOR('',#1727,1.00000000000000000E0); +#1729=LINE('',#1726,#1728); +#1730=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1731=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1732=VECTOR('',#1731,1.00000000000000000E0); +#1733=LINE('',#1730,#1732); +#1734=DEFINITIONAL_REPRESENTATION('',(#1733),#1); +#1735=PCURVE('',#161,#1734); +#1736=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1737=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1738=VECTOR('',#1737,1.00000000000000000E0); +#1739=LINE('',#1736,#1738); +#1740=DEFINITIONAL_REPRESENTATION('',(#1739),#1); +#1741=PCURVE('',#261,#1740); +#1742=SURFACE_CURVE('',#1729,(#1735,#1741),.CURVE_3D.); +#1743=EDGE_CURVE('',#381,#389,#1742,.T.); +#1744=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0,-2.00000000000000000E0)); +#1745=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1746=VECTOR('',#1745,1.00000000000000000E0); +#1747=LINE('',#1744,#1746); +#1748=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1749=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1750=VECTOR('',#1749,1.00000000000000000E0); +#1751=LINE('',#1748,#1750); +#1752=DEFINITIONAL_REPRESENTATION('',(#1751),#1); +#1753=PCURVE('',#161,#1752); +#1754=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1755=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1756=VECTOR('',#1755,1.00000000000000000E0); +#1757=LINE('',#1754,#1756); +#1758=DEFINITIONAL_REPRESENTATION('',(#1757),#1); +#1759=PCURVE('',#191,#1758); +#1760=SURFACE_CURVE('',#1747,(#1753,#1759),.CURVE_3D.); +#1761=EDGE_CURVE('',#349,#389,#1760,.T.); +#1762=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0,-3.00000000000000000E0)); +#1763=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1764=VECTOR('',#1763,1.00000000000000000E0); +#1765=LINE('',#1762,#1764); +#1766=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1767=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1768=VECTOR('',#1767,1.00000000000000000E0); +#1769=LINE('',#1766,#1768); +#1770=DEFINITIONAL_REPRESENTATION('',(#1769),#1); +#1771=PCURVE('',#166,#1770); +#1772=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#1773=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1774=VECTOR('',#1773,1.00000000000000000E0); +#1775=LINE('',#1772,#1774); +#1776=DEFINITIONAL_REPRESENTATION('',(#1775),#1); +#1777=PCURVE('',#181,#1776); +#1778=SURFACE_CURVE('',#1765,(#1771,#1777),.CURVE_3D.); +#1779=EDGE_CURVE('',#355,#391,#1778,.T.); +#1780=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#1781=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1782=VECTOR('',#1781,1.00000000000000000E0); +#1783=LINE('',#1780,#1782); +#1784=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#1785=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1786=VECTOR('',#1785,1.00000000000000000E0); +#1787=LINE('',#1784,#1786); +#1788=DEFINITIONAL_REPRESENTATION('',(#1787),#1); +#1789=PCURVE('',#166,#1788); +#1790=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#1791=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1792=VECTOR('',#1791,1.00000000000000000E0); +#1793=LINE('',#1790,#1792); +#1794=DEFINITIONAL_REPRESENTATION('',(#1793),#1); +#1795=PCURVE('',#271,#1794); +#1796=SURFACE_CURVE('',#1783,(#1789,#1795),.CURVE_3D.); +#1797=EDGE_CURVE('',#393,#391,#1796,.T.); +#1798=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#1799=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1800=VECTOR('',#1799,1.00000000000000000E0); +#1801=LINE('',#1798,#1800); +#1802=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1803=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1804=VECTOR('',#1803,1.00000000000000000E0); +#1805=LINE('',#1802,#1804); +#1806=DEFINITIONAL_REPRESENTATION('',(#1805),#1); +#1807=PCURVE('',#166,#1806); +#1808=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0)); +#1809=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1810=VECTOR('',#1809,1.00000000000000000E0); +#1811=LINE('',#1808,#1810); +#1812=DEFINITIONAL_REPRESENTATION('',(#1811),#1); +#1813=PCURVE('',#171,#1812); +#1814=SURFACE_CURVE('',#1801,(#1807,#1813),.CURVE_3D.); +#1815=EDGE_CURVE('',#357,#393,#1814,.T.); +#1816=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#1817=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1818=VECTOR('',#1817,1.00000000000000000E0); +#1819=LINE('',#1816,#1818); +#1820=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1821=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1822=VECTOR('',#1821,1.00000000000000000E0); +#1823=LINE('',#1820,#1822); +#1824=DEFINITIONAL_REPRESENTATION('',(#1823),#1); +#1825=PCURVE('',#171,#1824); +#1826=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1827=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1828=VECTOR('',#1827,1.00000000000000000E0); +#1829=LINE('',#1826,#1828); +#1830=DEFINITIONAL_REPRESENTATION('',(#1829),#1); +#1831=PCURVE('',#276,#1830); +#1832=SURFACE_CURVE('',#1819,(#1825,#1831),.CURVE_3D.); +#1833=EDGE_CURVE('',#383,#393,#1832,.T.); +#1834=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#1835=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1836=VECTOR('',#1835,1.00000000000000000E0); +#1837=LINE('',#1834,#1836); +#1838=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1839=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1840=VECTOR('',#1839,1.00000000000000000E0); +#1841=LINE('',#1838,#1840); +#1842=DEFINITIONAL_REPRESENTATION('',(#1841),#1); +#1843=PCURVE('',#176,#1842); +#1844=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1845=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1846=VECTOR('',#1845,1.00000000000000000E0); +#1847=LINE('',#1844,#1846); +#1848=DEFINITIONAL_REPRESENTATION('',(#1847),#1); +#1849=PCURVE('',#246,#1848); +#1850=SURFACE_CURVE('',#1837,(#1843,#1849),.CURVE_3D.); +#1851=EDGE_CURVE('',#385,#387,#1850,.T.); +#1852=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,-3.00000000000000000E0)); +#1853=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1854=VECTOR('',#1853,1.00000000000000000E0); +#1855=LINE('',#1852,#1854); +#1856=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1857=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1858=VECTOR('',#1857,1.00000000000000000E0); +#1859=LINE('',#1856,#1858); +#1860=DEFINITIONAL_REPRESENTATION('',(#1859),#1); +#1861=PCURVE('',#181,#1860); +#1862=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#1863=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1864=VECTOR('',#1863,1.00000000000000000E0); +#1865=LINE('',#1862,#1864); +#1866=DEFINITIONAL_REPRESENTATION('',(#1865),#1); +#1867=PCURVE('',#186,#1866); +#1868=SURFACE_CURVE('',#1855,(#1861,#1867),.CURVE_3D.); +#1869=EDGE_CURVE('',#361,#395,#1868,.T.); +#1870=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,-3.00000000000000000E0)); +#1871=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1872=VECTOR('',#1871,1.00000000000000000E0); +#1873=LINE('',#1870,#1872); +#1874=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1875=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1876=VECTOR('',#1875,1.00000000000000000E0); +#1877=LINE('',#1874,#1876); +#1878=DEFINITIONAL_REPRESENTATION('',(#1877),#1); +#1879=PCURVE('',#181,#1878); +#1880=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#1881=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1882=VECTOR('',#1881,1.00000000000000000E0); +#1883=LINE('',#1880,#1882); +#1884=DEFINITIONAL_REPRESENTATION('',(#1883),#1); +#1885=PCURVE('',#286,#1884); +#1886=SURFACE_CURVE('',#1873,(#1879,#1885),.CURVE_3D.); +#1887=EDGE_CURVE('',#391,#395,#1886,.T.); +#1888=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#1889=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1890=VECTOR('',#1889,1.00000000000000000E0); +#1891=LINE('',#1888,#1890); +#1892=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0)); +#1893=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1894=VECTOR('',#1893,1.00000000000000000E0); +#1895=LINE('',#1892,#1894); +#1896=DEFINITIONAL_REPRESENTATION('',(#1895),#1); +#1897=PCURVE('',#186,#1896); +#1898=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0)); +#1899=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1900=VECTOR('',#1899,1.00000000000000000E0); +#1901=LINE('',#1898,#1900); +#1902=DEFINITIONAL_REPRESENTATION('',(#1901),#1); +#1903=PCURVE('',#191,#1902); +#1904=SURFACE_CURVE('',#1891,(#1897,#1903),.CURVE_3D.); +#1905=EDGE_CURVE('',#365,#397,#1904,.T.); +#1906=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,-3.00000000000000000E0)); +#1907=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1908=VECTOR('',#1907,1.00000000000000000E0); +#1909=LINE('',#1906,#1908); +#1910=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1911=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1912=VECTOR('',#1911,1.00000000000000000E0); +#1913=LINE('',#1910,#1912); +#1914=DEFINITIONAL_REPRESENTATION('',(#1913),#1); +#1915=PCURVE('',#186,#1914); +#1916=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1917=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1918=VECTOR('',#1917,1.00000000000000000E0); +#1919=LINE('',#1916,#1918); +#1920=DEFINITIONAL_REPRESENTATION('',(#1919),#1); +#1921=PCURVE('',#296,#1920); +#1922=SURFACE_CURVE('',#1909,(#1915,#1921),.CURVE_3D.); +#1923=EDGE_CURVE('',#395,#397,#1922,.T.); +#1924=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,-2.00000000000000000E0)); +#1925=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1926=VECTOR('',#1925,1.00000000000000000E0); +#1927=LINE('',#1924,#1926); +#1928=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1929=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1930=VECTOR('',#1929,1.00000000000000000E0); +#1931=LINE('',#1928,#1930); +#1932=DEFINITIONAL_REPRESENTATION('',(#1931),#1); +#1933=PCURVE('',#191,#1932); +#1934=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#1935=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1936=VECTOR('',#1935,1.00000000000000000E0); +#1937=LINE('',#1934,#1936); +#1938=DEFINITIONAL_REPRESENTATION('',(#1937),#1); +#1939=PCURVE('',#301,#1938); +#1940=SURFACE_CURVE('',#1927,(#1933,#1939),.CURVE_3D.); +#1941=EDGE_CURVE('',#389,#397,#1940,.T.); +#1942=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1943=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1944=VECTOR('',#1943,1.00000000000000000E0); +#1945=LINE('',#1942,#1944); +#1946=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1947=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1948=VECTOR('',#1947,1.00000000000000000E0); +#1949=LINE('',#1946,#1948); +#1950=DEFINITIONAL_REPRESENTATION('',(#1949),#1); +#1951=PCURVE('',#196,#1950); +#1952=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#1953=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1954=VECTOR('',#1953,1.00000000000000000E0); +#1955=LINE('',#1952,#1954); +#1956=DEFINITIONAL_REPRESENTATION('',(#1955),#1); +#1957=PCURVE('',#206,#1956); +#1958=SURFACE_CURVE('',#1945,(#1951,#1957),.CURVE_3D.); +#1959=EDGE_CURVE('',#399,#401,#1958,.T.); +#1960=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1961=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1962=VECTOR('',#1961,1.00000000000000000E0); +#1963=LINE('',#1960,#1962); +#1964=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1965=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1966=VECTOR('',#1965,1.00000000000000000E0); +#1967=LINE('',#1964,#1966); +#1968=DEFINITIONAL_REPRESENTATION('',(#1967),#1); +#1969=PCURVE('',#196,#1968); +#1970=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#1971=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1972=VECTOR('',#1971,1.00000000000000000E0); +#1973=LINE('',#1970,#1972); +#1974=DEFINITIONAL_REPRESENTATION('',(#1973),#1); +#1975=PCURVE('',#236,#1974); +#1976=SURFACE_CURVE('',#1963,(#1969,#1975),.CURVE_3D.); +#1977=EDGE_CURVE('',#401,#403,#1976,.T.); +#1978=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1979=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1980=VECTOR('',#1979,1.00000000000000000E0); +#1981=LINE('',#1978,#1980); +#1982=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1983=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1984=VECTOR('',#1983,1.00000000000000000E0); +#1985=LINE('',#1982,#1984); +#1986=DEFINITIONAL_REPRESENTATION('',(#1985),#1); +#1987=PCURVE('',#196,#1986); +#1988=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#1989=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1990=VECTOR('',#1989,1.00000000000000000E0); +#1991=LINE('',#1988,#1990); +#1992=DEFINITIONAL_REPRESENTATION('',(#1991),#1); +#1993=PCURVE('',#211,#1992); +#1994=SURFACE_CURVE('',#1981,(#1987,#1993),.CURVE_3D.); +#1995=EDGE_CURVE('',#405,#403,#1994,.T.); +#1996=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1997=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1998=VECTOR('',#1997,1.00000000000000000E0); +#1999=LINE('',#1996,#1998); +#2000=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#2001=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2002=VECTOR('',#2001,1.00000000000000000E0); +#2003=LINE('',#2000,#2002); +#2004=DEFINITIONAL_REPRESENTATION('',(#2003),#1); +#2005=PCURVE('',#196,#2004); +#2006=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#2007=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2008=VECTOR('',#2007,1.00000000000000000E0); +#2009=LINE('',#2006,#2008); +#2010=DEFINITIONAL_REPRESENTATION('',(#2009),#1); +#2011=PCURVE('',#201,#2010); +#2012=SURFACE_CURVE('',#1999,(#2005,#2011),.CURVE_3D.); +#2013=EDGE_CURVE('',#399,#405,#2012,.T.); +#2014=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2015=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2016=VECTOR('',#2015,1.00000000000000000E0); +#2017=LINE('',#2014,#2016); +#2018=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#2019=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2020=VECTOR('',#2019,1.00000000000000000E0); +#2021=LINE('',#2018,#2020); +#2022=DEFINITIONAL_REPRESENTATION('',(#2021),#1); +#2023=PCURVE('',#201,#2022); +#2024=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#2025=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2026=VECTOR('',#2025,1.00000000000000000E0); +#2027=LINE('',#2024,#2026); +#2028=DEFINITIONAL_REPRESENTATION('',(#2027),#1); +#2029=PCURVE('',#216,#2028); +#2030=SURFACE_CURVE('',#2017,(#2023,#2029),.CURVE_3D.); +#2031=EDGE_CURVE('',#369,#405,#2030,.T.); +#2032=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2033=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2034=VECTOR('',#2033,1.00000000000000000E0); +#2035=LINE('',#2032,#2034); +#2036=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#2037=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2038=VECTOR('',#2037,1.00000000000000000E0); +#2039=LINE('',#2036,#2038); +#2040=DEFINITIONAL_REPRESENTATION('',(#2039),#1); +#2041=PCURVE('',#201,#2040); +#2042=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#2043=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2044=VECTOR('',#2043,1.00000000000000000E0); +#2045=LINE('',#2042,#2044); +#2046=DEFINITIONAL_REPRESENTATION('',(#2045),#1); +#2047=PCURVE('',#206,#2046); +#2048=SURFACE_CURVE('',#2035,(#2041,#2047),.CURVE_3D.); +#2049=EDGE_CURVE('',#367,#399,#2048,.T.); +#2050=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2051=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2052=VECTOR('',#2051,1.00000000000000000E0); +#2053=LINE('',#2050,#2052); +#2054=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#2055=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2056=VECTOR('',#2055,1.00000000000000000E0); +#2057=LINE('',#2054,#2056); +#2058=DEFINITIONAL_REPRESENTATION('',(#2057),#1); +#2059=PCURVE('',#206,#2058); +#2060=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#2061=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2062=VECTOR('',#2061,1.00000000000000000E0); +#2063=LINE('',#2060,#2062); +#2064=DEFINITIONAL_REPRESENTATION('',(#2063),#1); +#2065=PCURVE('',#241,#2064); +#2066=SURFACE_CURVE('',#2053,(#2059,#2065),.CURVE_3D.); +#2067=EDGE_CURVE('',#371,#401,#2066,.T.); +#2068=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#2069=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2070=VECTOR('',#2069,1.00000000000000000E0); +#2071=LINE('',#2068,#2070); +#2072=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#2073=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2074=VECTOR('',#2073,1.00000000000000000E0); +#2075=LINE('',#2072,#2074); +#2076=DEFINITIONAL_REPRESENTATION('',(#2075),#1); +#2077=PCURVE('',#211,#2076); +#2078=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#2079=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2080=VECTOR('',#2079,1.00000000000000000E0); +#2081=LINE('',#2078,#2080); +#2082=DEFINITIONAL_REPRESENTATION('',(#2081),#1); +#2083=PCURVE('',#251,#2082); +#2084=SURFACE_CURVE('',#2071,(#2077,#2083),.CURVE_3D.); +#2085=EDGE_CURVE('',#403,#407,#2084,.T.); +#2086=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#2087=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2088=VECTOR('',#2087,1.00000000000000000E0); +#2089=LINE('',#2086,#2088); +#2090=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#2091=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2092=VECTOR('',#2091,1.00000000000000000E0); +#2093=LINE('',#2090,#2092); +#2094=DEFINITIONAL_REPRESENTATION('',(#2093),#1); +#2095=PCURVE('',#211,#2094); +#2096=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#2097=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2098=VECTOR('',#2097,1.00000000000000000E0); +#2099=LINE('',#2096,#2098); +#2100=DEFINITIONAL_REPRESENTATION('',(#2099),#1); +#2101=PCURVE('',#221,#2100); +#2102=SURFACE_CURVE('',#2089,(#2095,#2101),.CURVE_3D.); +#2103=EDGE_CURVE('',#409,#407,#2102,.T.); +#2104=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2105=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2106=VECTOR('',#2105,1.00000000000000000E0); +#2107=LINE('',#2104,#2106); +#2108=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#2109=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2110=VECTOR('',#2109,1.00000000000000000E0); +#2111=LINE('',#2108,#2110); +#2112=DEFINITIONAL_REPRESENTATION('',(#2111),#1); +#2113=PCURVE('',#211,#2112); +#2114=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0)); +#2115=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2116=VECTOR('',#2115,1.00000000000000000E0); +#2117=LINE('',#2114,#2116); +#2118=DEFINITIONAL_REPRESENTATION('',(#2117),#1); +#2119=PCURVE('',#216,#2118); +#2120=SURFACE_CURVE('',#2107,(#2113,#2119),.CURVE_3D.); +#2121=EDGE_CURVE('',#405,#409,#2120,.T.); +#2122=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#2123=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2124=VECTOR('',#2123,1.00000000000000000E0); +#2125=LINE('',#2122,#2124); +#2126=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2127=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2128=VECTOR('',#2127,1.00000000000000000E0); +#2129=LINE('',#2126,#2128); +#2130=DEFINITIONAL_REPRESENTATION('',(#2129),#1); +#2131=PCURVE('',#216,#2130); +#2132=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2133=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2134=VECTOR('',#2133,1.00000000000000000E0); +#2135=LINE('',#2132,#2134); +#2136=DEFINITIONAL_REPRESENTATION('',(#2135),#1); +#2137=PCURVE('',#226,#2136); +#2138=SURFACE_CURVE('',#2125,(#2131,#2137),.CURVE_3D.); +#2139=EDGE_CURVE('',#373,#409,#2138,.T.); +#2140=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#2141=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2142=VECTOR('',#2141,1.00000000000000000E0); +#2143=LINE('',#2140,#2142); +#2144=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#2145=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2146=VECTOR('',#2145,1.00000000000000000E0); +#2147=LINE('',#2144,#2146); +#2148=DEFINITIONAL_REPRESENTATION('',(#2147),#1); +#2149=PCURVE('',#221,#2148); +#2150=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#2151=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2152=VECTOR('',#2151,1.00000000000000000E0); +#2153=LINE('',#2150,#2152); +#2154=DEFINITIONAL_REPRESENTATION('',(#2153),#1); +#2155=PCURVE('',#256,#2154); +#2156=SURFACE_CURVE('',#2143,(#2149,#2155),.CURVE_3D.); +#2157=EDGE_CURVE('',#407,#411,#2156,.T.); +#2158=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#2159=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2160=VECTOR('',#2159,1.00000000000000000E0); +#2161=LINE('',#2158,#2160); +#2162=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#2163=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2164=VECTOR('',#2163,1.00000000000000000E0); +#2165=LINE('',#2162,#2164); +#2166=DEFINITIONAL_REPRESENTATION('',(#2165),#1); +#2167=PCURVE('',#221,#2166); +#2168=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#2169=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2170=VECTOR('',#2169,1.00000000000000000E0); +#2171=LINE('',#2168,#2170); +#2172=DEFINITIONAL_REPRESENTATION('',(#2171),#1); +#2173=PCURVE('',#231,#2172); +#2174=SURFACE_CURVE('',#2161,(#2167,#2173),.CURVE_3D.); +#2175=EDGE_CURVE('',#413,#411,#2174,.T.); +#2176=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#2177=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2178=VECTOR('',#2177,1.00000000000000000E0); +#2179=LINE('',#2176,#2178); +#2180=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#2181=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2182=VECTOR('',#2181,1.00000000000000000E0); +#2183=LINE('',#2180,#2182); +#2184=DEFINITIONAL_REPRESENTATION('',(#2183),#1); +#2185=PCURVE('',#221,#2184); +#2186=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0)); +#2187=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2188=VECTOR('',#2187,1.00000000000000000E0); +#2189=LINE('',#2186,#2188); +#2190=DEFINITIONAL_REPRESENTATION('',(#2189),#1); +#2191=PCURVE('',#226,#2190); +#2192=SURFACE_CURVE('',#2179,(#2185,#2191),.CURVE_3D.); +#2193=EDGE_CURVE('',#409,#413,#2192,.T.); +#2194=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#2195=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2196=VECTOR('',#2195,1.00000000000000000E0); +#2197=LINE('',#2194,#2196); +#2198=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0)); +#2199=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2200=VECTOR('',#2199,1.00000000000000000E0); +#2201=LINE('',#2198,#2200); +#2202=DEFINITIONAL_REPRESENTATION('',(#2201),#1); +#2203=PCURVE('',#226,#2202); +#2204=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#2205=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2206=VECTOR('',#2205,1.00000000000000000E0); +#2207=LINE('',#2204,#2206); +#2208=DEFINITIONAL_REPRESENTATION('',(#2207),#1); +#2209=PCURVE('',#231,#2208); +#2210=SURFACE_CURVE('',#2197,(#2203,#2209),.CURVE_3D.); +#2211=EDGE_CURVE('',#379,#413,#2210,.T.); +#2212=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#2213=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2214=VECTOR('',#2213,1.00000000000000000E0); +#2215=LINE('',#2212,#2214); +#2216=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#2217=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2218=VECTOR('',#2217,1.00000000000000000E0); +#2219=LINE('',#2216,#2218); +#2220=DEFINITIONAL_REPRESENTATION('',(#2219),#1); +#2221=PCURVE('',#231,#2220); +#2222=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#2223=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2224=VECTOR('',#2223,1.00000000000000000E0); +#2225=LINE('',#2222,#2224); +#2226=DEFINITIONAL_REPRESENTATION('',(#2225),#1); +#2227=PCURVE('',#261,#2226); +#2228=SURFACE_CURVE('',#2215,(#2221,#2227),.CURVE_3D.); +#2229=EDGE_CURVE('',#381,#411,#2228,.T.); +#2230=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2231=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2232=VECTOR('',#2231,1.00000000000000000E0); +#2233=LINE('',#2230,#2232); +#2234=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#2235=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2236=VECTOR('',#2235,1.00000000000000000E0); +#2237=LINE('',#2234,#2236); +#2238=DEFINITIONAL_REPRESENTATION('',(#2237),#1); +#2239=PCURVE('',#236,#2238); +#2240=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0)); +#2241=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2242=VECTOR('',#2241,1.00000000000000000E0); +#2243=LINE('',#2240,#2242); +#2244=DEFINITIONAL_REPRESENTATION('',(#2243),#1); +#2245=PCURVE('',#241,#2244); +#2246=SURFACE_CURVE('',#2233,(#2239,#2245),.CURVE_3D.); +#2247=EDGE_CURVE('',#401,#415,#2246,.T.); +#2248=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#2249=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2250=VECTOR('',#2249,1.00000000000000000E0); +#2251=LINE('',#2248,#2250); +#2252=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#2253=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2254=VECTOR('',#2253,1.00000000000000000E0); +#2255=LINE('',#2252,#2254); +#2256=DEFINITIONAL_REPRESENTATION('',(#2255),#1); +#2257=PCURVE('',#236,#2256); +#2258=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#2259=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2260=VECTOR('',#2259,1.00000000000000000E0); +#2261=LINE('',#2258,#2260); +#2262=DEFINITIONAL_REPRESENTATION('',(#2261),#1); +#2263=PCURVE('',#266,#2262); +#2264=SURFACE_CURVE('',#2251,(#2257,#2263),.CURVE_3D.); +#2265=EDGE_CURVE('',#415,#417,#2264,.T.); +#2266=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0,-1.00000000000000000E0)); +#2267=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2268=VECTOR('',#2267,1.00000000000000000E0); +#2269=LINE('',#2266,#2268); +#2270=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#2271=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2272=VECTOR('',#2271,1.00000000000000000E0); +#2273=LINE('',#2270,#2272); +#2274=DEFINITIONAL_REPRESENTATION('',(#2273),#1); +#2275=PCURVE('',#236,#2274); +#2276=CARTESIAN_POINT('',(1.00000000000000000E0,1.00000000000000000E0)); +#2277=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2278=VECTOR('',#2277,1.00000000000000000E0); +#2279=LINE('',#2276,#2278); +#2280=DEFINITIONAL_REPRESENTATION('',(#2279),#1); +#2281=PCURVE('',#251,#2280); +#2282=SURFACE_CURVE('',#2269,(#2275,#2281),.CURVE_3D.); +#2283=EDGE_CURVE('',#403,#417,#2282,.T.); +#2284=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#2285=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2286=VECTOR('',#2285,1.00000000000000000E0); +#2287=LINE('',#2284,#2286); +#2288=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2289=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2290=VECTOR('',#2289,1.00000000000000000E0); +#2291=LINE('',#2288,#2290); +#2292=DEFINITIONAL_REPRESENTATION('',(#2291),#1); +#2293=PCURVE('',#241,#2292); +#2294=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2295=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2296=VECTOR('',#2295,1.00000000000000000E0); +#2297=LINE('',#2294,#2296); +#2298=DEFINITIONAL_REPRESENTATION('',(#2297),#1); +#2299=PCURVE('',#276,#2298); +#2300=SURFACE_CURVE('',#2287,(#2293,#2299),.CURVE_3D.); +#2301=EDGE_CURVE('',#383,#415,#2300,.T.); +#2302=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#2303=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2304=VECTOR('',#2303,1.00000000000000000E0); +#2305=LINE('',#2302,#2304); +#2306=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#2307=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2308=VECTOR('',#2307,1.00000000000000000E0); +#2309=LINE('',#2306,#2308); +#2310=DEFINITIONAL_REPRESENTATION('',(#2309),#1); +#2311=PCURVE('',#251,#2310); +#2312=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#2313=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2314=VECTOR('',#2313,1.00000000000000000E0); +#2315=LINE('',#2312,#2314); +#2316=DEFINITIONAL_REPRESENTATION('',(#2315),#1); +#2317=PCURVE('',#281,#2316); +#2318=SURFACE_CURVE('',#2305,(#2311,#2317),.CURVE_3D.); +#2319=EDGE_CURVE('',#417,#419,#2318,.T.); +#2320=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0,-1.00000000000000000E0)); +#2321=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2322=VECTOR('',#2321,1.00000000000000000E0); +#2323=LINE('',#2320,#2322); +#2324=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#2325=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2326=VECTOR('',#2325,1.00000000000000000E0); +#2327=LINE('',#2324,#2326); +#2328=DEFINITIONAL_REPRESENTATION('',(#2327),#1); +#2329=PCURVE('',#251,#2328); +#2330=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#2331=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2332=VECTOR('',#2331,1.00000000000000000E0); +#2333=LINE('',#2330,#2332); +#2334=DEFINITIONAL_REPRESENTATION('',(#2333),#1); +#2335=PCURVE('',#256,#2334); +#2336=SURFACE_CURVE('',#2323,(#2329,#2335),.CURVE_3D.); +#2337=EDGE_CURVE('',#407,#419,#2336,.T.); +#2338=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#2339=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2340=VECTOR('',#2339,1.00000000000000000E0); +#2341=LINE('',#2338,#2340); +#2342=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2343=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2344=VECTOR('',#2343,1.00000000000000000E0); +#2345=LINE('',#2342,#2344); +#2346=DEFINITIONAL_REPRESENTATION('',(#2345),#1); +#2347=PCURVE('',#256,#2346); +#2348=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2349=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2350=VECTOR('',#2349,1.00000000000000000E0); +#2351=LINE('',#2348,#2350); +#2352=DEFINITIONAL_REPRESENTATION('',(#2351),#1); +#2353=PCURVE('',#291,#2352); +#2354=SURFACE_CURVE('',#2341,(#2347,#2353),.CURVE_3D.); +#2355=EDGE_CURVE('',#419,#421,#2354,.T.); +#2356=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#2357=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2358=VECTOR('',#2357,1.00000000000000000E0); +#2359=LINE('',#2356,#2358); +#2360=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0)); +#2361=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2362=VECTOR('',#2361,1.00000000000000000E0); +#2363=LINE('',#2360,#2362); +#2364=DEFINITIONAL_REPRESENTATION('',(#2363),#1); +#2365=PCURVE('',#256,#2364); +#2366=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0)); +#2367=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2368=VECTOR('',#2367,1.00000000000000000E0); +#2369=LINE('',#2366,#2368); +#2370=DEFINITIONAL_REPRESENTATION('',(#2369),#1); +#2371=PCURVE('',#261,#2370); +#2372=SURFACE_CURVE('',#2359,(#2365,#2371),.CURVE_3D.); +#2373=EDGE_CURVE('',#411,#421,#2372,.T.); +#2374=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,-2.00000000000000000E0)); +#2375=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2376=VECTOR('',#2375,1.00000000000000000E0); +#2377=LINE('',#2374,#2376); +#2378=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2379=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2380=VECTOR('',#2379,1.00000000000000000E0); +#2381=LINE('',#2378,#2380); +#2382=DEFINITIONAL_REPRESENTATION('',(#2381),#1); +#2383=PCURVE('',#261,#2382); +#2384=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2385=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2386=VECTOR('',#2385,1.00000000000000000E0); +#2387=LINE('',#2384,#2386); +#2388=DEFINITIONAL_REPRESENTATION('',(#2387),#1); +#2389=PCURVE('',#301,#2388); +#2390=SURFACE_CURVE('',#2377,(#2383,#2389),.CURVE_3D.); +#2391=EDGE_CURVE('',#389,#421,#2390,.T.); +#2392=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E0)); +#2393=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2394=VECTOR('',#2393,1.00000000000000000E0); +#2395=LINE('',#2392,#2394); +#2396=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#2397=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2398=VECTOR('',#2397,1.00000000000000000E0); +#2399=LINE('',#2396,#2398); +#2400=DEFINITIONAL_REPRESENTATION('',(#2399),#1); +#2401=PCURVE('',#266,#2400); +#2402=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0)); +#2403=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2404=VECTOR('',#2403,1.00000000000000000E0); +#2405=LINE('',#2402,#2404); +#2406=DEFINITIONAL_REPRESENTATION('',(#2405),#1); +#2407=PCURVE('',#276,#2406); +#2408=SURFACE_CURVE('',#2395,(#2401,#2407),.CURVE_3D.); +#2409=EDGE_CURVE('',#415,#423,#2408,.T.); +#2410=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#2411=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2412=VECTOR('',#2411,1.00000000000000000E0); +#2413=LINE('',#2410,#2412); +#2414=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#2415=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2416=VECTOR('',#2415,1.00000000000000000E0); +#2417=LINE('',#2414,#2416); +#2418=DEFINITIONAL_REPRESENTATION('',(#2417),#1); +#2419=PCURVE('',#266,#2418); +#2420=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#2421=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2422=VECTOR('',#2421,1.00000000000000000E0); +#2423=LINE('',#2420,#2422); +#2424=DEFINITIONAL_REPRESENTATION('',(#2423),#1); +#2425=PCURVE('',#271,#2424); +#2426=SURFACE_CURVE('',#2413,(#2419,#2425),.CURVE_3D.); +#2427=EDGE_CURVE('',#423,#425,#2426,.T.); +#2428=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0,-2.00000000000000000E0)); +#2429=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2430=VECTOR('',#2429,1.00000000000000000E0); +#2431=LINE('',#2428,#2430); +#2432=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#2433=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2434=VECTOR('',#2433,1.00000000000000000E0); +#2435=LINE('',#2432,#2434); +#2436=DEFINITIONAL_REPRESENTATION('',(#2435),#1); +#2437=PCURVE('',#266,#2436); +#2438=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0)); +#2439=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2440=VECTOR('',#2439,1.00000000000000000E0); +#2441=LINE('',#2438,#2440); +#2442=DEFINITIONAL_REPRESENTATION('',(#2441),#1); +#2443=PCURVE('',#281,#2442); +#2444=SURFACE_CURVE('',#2431,(#2437,#2443),.CURVE_3D.); +#2445=EDGE_CURVE('',#417,#425,#2444,.T.); +#2446=CARTESIAN_POINT('',(2.00000000000000000E0,1.00000000000000000E0,-3.00000000000000000E0)); +#2447=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2448=VECTOR('',#2447,1.00000000000000000E0); +#2449=LINE('',#2446,#2448); +#2450=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#2451=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2452=VECTOR('',#2451,1.00000000000000000E0); +#2453=LINE('',#2450,#2452); +#2454=DEFINITIONAL_REPRESENTATION('',(#2453),#1); +#2455=PCURVE('',#271,#2454); +#2456=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0)); +#2457=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2458=VECTOR('',#2457,1.00000000000000000E0); +#2459=LINE('',#2456,#2458); +#2460=DEFINITIONAL_REPRESENTATION('',(#2459),#1); +#2461=PCURVE('',#286,#2460); +#2462=SURFACE_CURVE('',#2449,(#2455,#2461),.CURVE_3D.); +#2463=EDGE_CURVE('',#391,#425,#2462,.T.); +#2464=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#2465=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2466=VECTOR('',#2465,1.00000000000000000E0); +#2467=LINE('',#2464,#2466); +#2468=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#2469=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2470=VECTOR('',#2469,1.00000000000000000E0); +#2471=LINE('',#2468,#2470); +#2472=DEFINITIONAL_REPRESENTATION('',(#2471),#1); +#2473=PCURVE('',#271,#2472); +#2474=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0)); +#2475=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2476=VECTOR('',#2475,1.00000000000000000E0); +#2477=LINE('',#2474,#2476); +#2478=DEFINITIONAL_REPRESENTATION('',(#2477),#1); +#2479=PCURVE('',#276,#2478); +#2480=SURFACE_CURVE('',#2467,(#2473,#2479),.CURVE_3D.); +#2481=EDGE_CURVE('',#393,#423,#2480,.T.); +#2482=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0,-3.00000000000000000E0)); +#2483=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2484=VECTOR('',#2483,1.00000000000000000E0); +#2485=LINE('',#2482,#2484); +#2486=CARTESIAN_POINT('',(3.00000000000000000E0,1.00000000000000000E0)); +#2487=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2488=VECTOR('',#2487,1.00000000000000000E0); +#2489=LINE('',#2486,#2488); +#2490=DEFINITIONAL_REPRESENTATION('',(#2489),#1); +#2491=PCURVE('',#281,#2490); +#2492=CARTESIAN_POINT('',(1.00000000000000000E0,3.00000000000000000E0)); +#2493=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2494=VECTOR('',#2493,1.00000000000000000E0); +#2495=LINE('',#2492,#2494); +#2496=DEFINITIONAL_REPRESENTATION('',(#2495),#1); +#2497=PCURVE('',#286,#2496); +#2498=SURFACE_CURVE('',#2485,(#2491,#2497),.CURVE_3D.); +#2499=EDGE_CURVE('',#425,#427,#2498,.T.); +#2500=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0,-2.00000000000000000E0)); +#2501=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2502=VECTOR('',#2501,1.00000000000000000E0); +#2503=LINE('',#2500,#2502); +#2504=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2505=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2506=VECTOR('',#2505,1.00000000000000000E0); +#2507=LINE('',#2504,#2506); +#2508=DEFINITIONAL_REPRESENTATION('',(#2507),#1); +#2509=PCURVE('',#281,#2508); +#2510=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2511=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2512=VECTOR('',#2511,1.00000000000000000E0); +#2513=LINE('',#2510,#2512); +#2514=DEFINITIONAL_REPRESENTATION('',(#2513),#1); +#2515=PCURVE('',#291,#2514); +#2516=SURFACE_CURVE('',#2503,(#2509,#2515),.CURVE_3D.); +#2517=EDGE_CURVE('',#419,#427,#2516,.T.); +#2518=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0,-3.00000000000000000E0)); +#2519=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2520=VECTOR('',#2519,1.00000000000000000E0); +#2521=LINE('',#2518,#2520); +#2522=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2523=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2524=VECTOR('',#2523,1.00000000000000000E0); +#2525=LINE('',#2522,#2524); +#2526=DEFINITIONAL_REPRESENTATION('',(#2525),#1); +#2527=PCURVE('',#286,#2526); +#2528=CARTESIAN_POINT('',(2.00000000000000000E0,2.00000000000000000E0)); +#2529=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2530=VECTOR('',#2529,1.00000000000000000E0); +#2531=LINE('',#2528,#2530); +#2532=DEFINITIONAL_REPRESENTATION('',(#2531),#1); +#2533=PCURVE('',#296,#2532); +#2534=SURFACE_CURVE('',#2521,(#2527,#2533),.CURVE_3D.); +#2535=EDGE_CURVE('',#395,#427,#2534,.T.); +#2536=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0,-3.00000000000000000E0)); +#2537=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2538=VECTOR('',#2537,1.00000000000000000E0); +#2539=LINE('',#2536,#2538); +#2540=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0)); +#2541=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2542=VECTOR('',#2541,1.00000000000000000E0); +#2543=LINE('',#2540,#2542); +#2544=DEFINITIONAL_REPRESENTATION('',(#2543),#1); +#2545=PCURVE('',#291,#2544); +#2546=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0)); +#2547=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2548=VECTOR('',#2547,1.00000000000000000E0); +#2549=LINE('',#2546,#2548); +#2550=DEFINITIONAL_REPRESENTATION('',(#2549),#1); +#2551=PCURVE('',#296,#2550); +#2552=SURFACE_CURVE('',#2539,(#2545,#2551),.CURVE_3D.); +#2553=EDGE_CURVE('',#427,#429,#2552,.T.); +#2554=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-2.00000000000000000E0)); +#2555=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2556=VECTOR('',#2555,1.00000000000000000E0); +#2557=LINE('',#2554,#2556); +#2558=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0)); +#2559=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2560=VECTOR('',#2559,1.00000000000000000E0); +#2561=LINE('',#2558,#2560); +#2562=DEFINITIONAL_REPRESENTATION('',(#2561),#1); +#2563=PCURVE('',#291,#2562); +#2564=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0)); +#2565=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2566=VECTOR('',#2565,1.00000000000000000E0); +#2567=LINE('',#2564,#2566); +#2568=DEFINITIONAL_REPRESENTATION('',(#2567),#1); +#2569=PCURVE('',#301,#2568); +#2570=SURFACE_CURVE('',#2557,(#2563,#2569),.CURVE_3D.); +#2571=EDGE_CURVE('',#421,#429,#2570,.T.); +#2572=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#2573=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2574=VECTOR('',#2573,1.00000000000000000E0); +#2575=LINE('',#2572,#2574); +#2576=CARTESIAN_POINT('',(3.00000000000000000E0,2.00000000000000000E0)); +#2577=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2578=VECTOR('',#2577,1.00000000000000000E0); +#2579=LINE('',#2576,#2578); +#2580=DEFINITIONAL_REPRESENTATION('',(#2579),#1); +#2581=PCURVE('',#296,#2580); +#2582=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0)); +#2583=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2584=VECTOR('',#2583,1.00000000000000000E0); +#2585=LINE('',#2582,#2584); +#2586=DEFINITIONAL_REPRESENTATION('',(#2585),#1); +#2587=PCURVE('',#301,#2586); +#2588=SURFACE_CURVE('',#2575,(#2581,#2587),.CURVE_3D.); +#2589=EDGE_CURVE('',#397,#429,#2588,.T.); +#2590=ORIENTED_EDGE('',*,*,#447,.T.); +#2591=ORIENTED_EDGE('',*,*,#465,.F.); +#2592=ORIENTED_EDGE('',*,*,#483,.F.); +#2593=ORIENTED_EDGE('',*,*,#501,.T.); +#2594=EDGE_LOOP('',(#2590,#2591,#2592,#2593)); +#2595=FACE_OUTER_BOUND('',#2594,.T.); +#2596=ADVANCED_FACE('left-0',(#2595),#6,.T.); +#2597=ORIENTED_EDGE('',*,*,#519,.T.); +#2598=ORIENTED_EDGE('',*,*,#537,.F.); +#2599=ORIENTED_EDGE('',*,*,#501,.F.); +#2600=ORIENTED_EDGE('',*,*,#555,.T.); +#2601=EDGE_LOOP('',(#2597,#2598,#2599,#2600)); +#2602=FACE_OUTER_BOUND('',#2601,.T.); +#2603=ADVANCED_FACE('front-1',(#2602),#11,.T.); +#2604=ORIENTED_EDGE('',*,*,#573,.T.); +#2605=ORIENTED_EDGE('',*,*,#591,.F.); +#2606=ORIENTED_EDGE('',*,*,#555,.F.); +#2607=ORIENTED_EDGE('',*,*,#483,.T.); +#2608=EDGE_LOOP('',(#2604,#2605,#2606,#2607)); +#2609=FACE_OUTER_BOUND('',#2608,.T.); +#2610=ADVANCED_FACE('bottom-2',(#2609),#16,.T.); +#2611=ORIENTED_EDGE('',*,*,#609,.T.); +#2612=ORIENTED_EDGE('',*,*,#627,.F.); +#2613=ORIENTED_EDGE('',*,*,#447,.F.); +#2614=ORIENTED_EDGE('',*,*,#645,.T.); +#2615=EDGE_LOOP('',(#2611,#2612,#2613,#2614)); +#2616=FACE_OUTER_BOUND('',#2615,.T.); +#2617=ADVANCED_FACE('left-3',(#2616),#21,.T.); +#2618=ORIENTED_EDGE('',*,*,#663,.T.); +#2619=ORIENTED_EDGE('',*,*,#681,.F.); +#2620=ORIENTED_EDGE('',*,*,#645,.F.); +#2621=ORIENTED_EDGE('',*,*,#537,.T.); +#2622=EDGE_LOOP('',(#2618,#2619,#2620,#2621)); +#2623=FACE_OUTER_BOUND('',#2622,.T.); +#2624=ADVANCED_FACE('front-4',(#2623),#26,.T.); +#2625=ORIENTED_EDGE('',*,*,#699,.T.); +#2626=ORIENTED_EDGE('',*,*,#717,.F.); +#2627=ORIENTED_EDGE('',*,*,#609,.F.); +#2628=ORIENTED_EDGE('',*,*,#735,.T.); +#2629=EDGE_LOOP('',(#2625,#2626,#2627,#2628)); +#2630=FACE_OUTER_BOUND('',#2629,.T.); +#2631=ADVANCED_FACE('left-5',(#2630),#31,.T.); +#2632=ORIENTED_EDGE('',*,*,#753,.T.); +#2633=ORIENTED_EDGE('',*,*,#771,.F.); +#2634=ORIENTED_EDGE('',*,*,#735,.F.); +#2635=ORIENTED_EDGE('',*,*,#681,.T.); +#2636=EDGE_LOOP('',(#2632,#2633,#2634,#2635)); +#2637=FACE_OUTER_BOUND('',#2636,.T.); +#2638=ADVANCED_FACE('front-6',(#2637),#36,.T.); +#2639=ORIENTED_EDGE('',*,*,#771,.T.); +#2640=ORIENTED_EDGE('',*,*,#789,.T.); +#2641=ORIENTED_EDGE('',*,*,#807,.F.); +#2642=ORIENTED_EDGE('',*,*,#699,.F.); +#2643=EDGE_LOOP('',(#2639,#2640,#2641,#2642)); +#2644=FACE_OUTER_BOUND('',#2643,.T.); +#2645=ADVANCED_FACE('top-7',(#2644),#41,.T.); +#2646=ORIENTED_EDGE('',*,*,#825,.T.); +#2647=ORIENTED_EDGE('',*,*,#843,.F.); +#2648=ORIENTED_EDGE('',*,*,#861,.F.); +#2649=ORIENTED_EDGE('',*,*,#465,.T.); +#2650=EDGE_LOOP('',(#2646,#2647,#2648,#2649)); +#2651=FACE_OUTER_BOUND('',#2650,.T.); +#2652=ADVANCED_FACE('left-8',(#2651),#46,.T.); +#2653=ORIENTED_EDGE('',*,*,#879,.T.); +#2654=ORIENTED_EDGE('',*,*,#897,.F.); +#2655=ORIENTED_EDGE('',*,*,#573,.F.); +#2656=ORIENTED_EDGE('',*,*,#861,.T.); +#2657=EDGE_LOOP('',(#2653,#2654,#2655,#2656)); +#2658=FACE_OUTER_BOUND('',#2657,.T.); +#2659=ADVANCED_FACE('bottom-9',(#2658),#51,.T.); +#2660=ORIENTED_EDGE('',*,*,#915,.T.); +#2661=ORIENTED_EDGE('',*,*,#933,.F.); +#2662=ORIENTED_EDGE('',*,*,#825,.F.); +#2663=ORIENTED_EDGE('',*,*,#627,.T.); +#2664=EDGE_LOOP('',(#2660,#2661,#2662,#2663)); +#2665=FACE_OUTER_BOUND('',#2664,.T.); +#2666=ADVANCED_FACE('left-10',(#2665),#56,.T.); +#2667=ORIENTED_EDGE('',*,*,#951,.T.); +#2668=ORIENTED_EDGE('',*,*,#969,.T.); +#2669=ORIENTED_EDGE('',*,*,#987,.F.); +#2670=ORIENTED_EDGE('',*,*,#1005,.F.); +#2671=EDGE_LOOP('',(#2667,#2668,#2669,#2670)); +#2672=FACE_OUTER_BOUND('',#2671,.T.); +#2673=ADVANCED_FACE('left-11',(#2672),#61,.F.); +#2674=ORIENTED_EDGE('',*,*,#1023,.T.); +#2675=ORIENTED_EDGE('',*,*,#1041,.F.); +#2676=ORIENTED_EDGE('',*,*,#915,.F.); +#2677=ORIENTED_EDGE('',*,*,#717,.T.); +#2678=EDGE_LOOP('',(#2674,#2675,#2676,#2677)); +#2679=FACE_OUTER_BOUND('',#2678,.T.); +#2680=ADVANCED_FACE('left-12',(#2679),#66,.T.); +#2681=ORIENTED_EDGE('',*,*,#807,.T.); +#2682=ORIENTED_EDGE('',*,*,#1059,.T.); +#2683=ORIENTED_EDGE('',*,*,#1077,.F.); +#2684=ORIENTED_EDGE('',*,*,#1023,.F.); +#2685=EDGE_LOOP('',(#2681,#2682,#2683,#2684)); +#2686=FACE_OUTER_BOUND('',#2685,.T.); +#2687=ADVANCED_FACE('top-13',(#2686),#71,.T.); +#2688=ORIENTED_EDGE('',*,*,#1095,.T.); +#2689=ORIENTED_EDGE('',*,*,#1113,.F.); +#2690=ORIENTED_EDGE('',*,*,#1131,.F.); +#2691=ORIENTED_EDGE('',*,*,#843,.T.); +#2692=EDGE_LOOP('',(#2688,#2689,#2690,#2691)); +#2693=FACE_OUTER_BOUND('',#2692,.T.); +#2694=ADVANCED_FACE('left-14',(#2693),#76,.T.); +#2695=ORIENTED_EDGE('',*,*,#1113,.T.); +#2696=ORIENTED_EDGE('',*,*,#1149,.T.); +#2697=ORIENTED_EDGE('',*,*,#1167,.F.); +#2698=ORIENTED_EDGE('',*,*,#1185,.F.); +#2699=EDGE_LOOP('',(#2695,#2696,#2697,#2698)); +#2700=FACE_OUTER_BOUND('',#2699,.T.); +#2701=ADVANCED_FACE('back-15',(#2700),#81,.T.); +#2702=ORIENTED_EDGE('',*,*,#1185,.T.); +#2703=ORIENTED_EDGE('',*,*,#1203,.F.); +#2704=ORIENTED_EDGE('',*,*,#879,.F.); +#2705=ORIENTED_EDGE('',*,*,#1131,.T.); +#2706=EDGE_LOOP('',(#2702,#2703,#2704,#2705)); +#2707=FACE_OUTER_BOUND('',#2706,.T.); +#2708=ADVANCED_FACE('bottom-16',(#2707),#86,.T.); +#2709=ORIENTED_EDGE('',*,*,#1221,.T.); +#2710=ORIENTED_EDGE('',*,*,#1239,.F.); +#2711=ORIENTED_EDGE('',*,*,#1095,.F.); +#2712=ORIENTED_EDGE('',*,*,#933,.T.); +#2713=EDGE_LOOP('',(#2709,#2710,#2711,#2712)); +#2714=FACE_OUTER_BOUND('',#2713,.T.); +#2715=ADVANCED_FACE('left-17',(#2714),#91,.T.); +#2716=ORIENTED_EDGE('',*,*,#1239,.T.); +#2717=ORIENTED_EDGE('',*,*,#1257,.T.); +#2718=ORIENTED_EDGE('',*,*,#1275,.F.); +#2719=ORIENTED_EDGE('',*,*,#1149,.F.); +#2720=EDGE_LOOP('',(#2716,#2717,#2718,#2719)); +#2721=FACE_OUTER_BOUND('',#2720,.T.); +#2722=ADVANCED_FACE('back-18',(#2721),#96,.T.); +#2723=ORIENTED_EDGE('',*,*,#1293,.T.); +#2724=ORIENTED_EDGE('',*,*,#1311,.F.); +#2725=ORIENTED_EDGE('',*,*,#1221,.F.); +#2726=ORIENTED_EDGE('',*,*,#1041,.T.); +#2727=EDGE_LOOP('',(#2723,#2724,#2725,#2726)); +#2728=FACE_OUTER_BOUND('',#2727,.T.); +#2729=ADVANCED_FACE('left-19',(#2728),#101,.T.); +#2730=ORIENTED_EDGE('',*,*,#1311,.T.); +#2731=ORIENTED_EDGE('',*,*,#1329,.T.); +#2732=ORIENTED_EDGE('',*,*,#1347,.F.); +#2733=ORIENTED_EDGE('',*,*,#1257,.F.); +#2734=EDGE_LOOP('',(#2730,#2731,#2732,#2733)); +#2735=FACE_OUTER_BOUND('',#2734,.T.); +#2736=ADVANCED_FACE('back-20',(#2735),#106,.T.); +#2737=ORIENTED_EDGE('',*,*,#1077,.T.); +#2738=ORIENTED_EDGE('',*,*,#1365,.T.); +#2739=ORIENTED_EDGE('',*,*,#1329,.F.); +#2740=ORIENTED_EDGE('',*,*,#1293,.F.); +#2741=EDGE_LOOP('',(#2737,#2738,#2739,#2740)); +#2742=FACE_OUTER_BOUND('',#2741,.T.); +#2743=ADVANCED_FACE('top-21',(#2742),#111,.T.); +#2744=ORIENTED_EDGE('',*,*,#1383,.T.); +#2745=ORIENTED_EDGE('',*,*,#1401,.F.); +#2746=ORIENTED_EDGE('',*,*,#519,.F.); +#2747=ORIENTED_EDGE('',*,*,#1419,.T.); +#2748=EDGE_LOOP('',(#2744,#2745,#2746,#2747)); +#2749=FACE_OUTER_BOUND('',#2748,.T.); +#2750=ADVANCED_FACE('front-22',(#2749),#116,.T.); +#2751=ORIENTED_EDGE('',*,*,#1437,.T.); +#2752=ORIENTED_EDGE('',*,*,#1455,.F.); +#2753=ORIENTED_EDGE('',*,*,#1419,.F.); +#2754=ORIENTED_EDGE('',*,*,#591,.T.); +#2755=EDGE_LOOP('',(#2751,#2752,#2753,#2754)); +#2756=FACE_OUTER_BOUND('',#2755,.T.); +#2757=ADVANCED_FACE('bottom-23',(#2756),#121,.T.); +#2758=ORIENTED_EDGE('',*,*,#1473,.T.); +#2759=ORIENTED_EDGE('',*,*,#1491,.F.); +#2760=ORIENTED_EDGE('',*,*,#663,.F.); +#2761=ORIENTED_EDGE('',*,*,#1401,.T.); +#2762=EDGE_LOOP('',(#2758,#2759,#2760,#2761)); +#2763=FACE_OUTER_BOUND('',#2762,.T.); +#2764=ADVANCED_FACE('front-24',(#2763),#126,.T.); +#2765=ORIENTED_EDGE('',*,*,#1005,.T.); +#2766=ORIENTED_EDGE('',*,*,#1509,.T.); +#2767=ORIENTED_EDGE('',*,*,#1527,.F.); +#2768=ORIENTED_EDGE('',*,*,#1545,.F.); +#2769=EDGE_LOOP('',(#2765,#2766,#2767,#2768)); +#2770=FACE_OUTER_BOUND('',#2769,.T.); +#2771=ADVANCED_FACE('front-25',(#2770),#131,.F.); +#2772=ORIENTED_EDGE('',*,*,#1563,.T.); +#2773=ORIENTED_EDGE('',*,*,#1581,.F.); +#2774=ORIENTED_EDGE('',*,*,#753,.F.); +#2775=ORIENTED_EDGE('',*,*,#1491,.T.); +#2776=EDGE_LOOP('',(#2772,#2773,#2774,#2775)); +#2777=FACE_OUTER_BOUND('',#2776,.T.); +#2778=ADVANCED_FACE('front-26',(#2777),#136,.T.); +#2779=ORIENTED_EDGE('',*,*,#1581,.T.); +#2780=ORIENTED_EDGE('',*,*,#1599,.T.); +#2781=ORIENTED_EDGE('',*,*,#1617,.F.); +#2782=ORIENTED_EDGE('',*,*,#789,.F.); +#2783=EDGE_LOOP('',(#2779,#2780,#2781,#2782)); +#2784=FACE_OUTER_BOUND('',#2783,.T.); +#2785=ADVANCED_FACE('top-27',(#2784),#141,.T.); +#2786=ORIENTED_EDGE('',*,*,#1635,.T.); +#2787=ORIENTED_EDGE('',*,*,#1653,.F.); +#2788=ORIENTED_EDGE('',*,*,#1437,.F.); +#2789=ORIENTED_EDGE('',*,*,#897,.T.); +#2790=EDGE_LOOP('',(#2786,#2787,#2788,#2789)); +#2791=FACE_OUTER_BOUND('',#2790,.T.); +#2792=ADVANCED_FACE('bottom-28',(#2791),#146,.T.); +#2793=ORIENTED_EDGE('',*,*,#1545,.T.); +#2794=ORIENTED_EDGE('',*,*,#1671,.T.); +#2795=ORIENTED_EDGE('',*,*,#1689,.F.); +#2796=ORIENTED_EDGE('',*,*,#951,.F.); +#2797=EDGE_LOOP('',(#2793,#2794,#2795,#2796)); +#2798=FACE_OUTER_BOUND('',#2797,.T.); +#2799=ADVANCED_FACE('bottom-29',(#2798),#151,.F.); +#2800=ORIENTED_EDGE('',*,*,#1707,.T.); +#2801=ORIENTED_EDGE('',*,*,#1725,.F.); +#2802=ORIENTED_EDGE('',*,*,#1509,.F.); +#2803=ORIENTED_EDGE('',*,*,#987,.T.); +#2804=EDGE_LOOP('',(#2800,#2801,#2802,#2803)); +#2805=FACE_OUTER_BOUND('',#2804,.T.); +#2806=ADVANCED_FACE('top-30',(#2805),#156,.F.); +#2807=ORIENTED_EDGE('',*,*,#1617,.T.); +#2808=ORIENTED_EDGE('',*,*,#1743,.T.); +#2809=ORIENTED_EDGE('',*,*,#1761,.F.); +#2810=ORIENTED_EDGE('',*,*,#1059,.F.); +#2811=EDGE_LOOP('',(#2807,#2808,#2809,#2810)); +#2812=FACE_OUTER_BOUND('',#2811,.T.); +#2813=ADVANCED_FACE('top-31',(#2812),#161,.T.); +#2814=ORIENTED_EDGE('',*,*,#1167,.T.); +#2815=ORIENTED_EDGE('',*,*,#1779,.T.); +#2816=ORIENTED_EDGE('',*,*,#1797,.F.); +#2817=ORIENTED_EDGE('',*,*,#1815,.F.); +#2818=EDGE_LOOP('',(#2814,#2815,#2816,#2817)); +#2819=FACE_OUTER_BOUND('',#2818,.T.); +#2820=ADVANCED_FACE('back-32',(#2819),#166,.T.); +#2821=ORIENTED_EDGE('',*,*,#1815,.T.); +#2822=ORIENTED_EDGE('',*,*,#1833,.F.); +#2823=ORIENTED_EDGE('',*,*,#1635,.F.); +#2824=ORIENTED_EDGE('',*,*,#1203,.T.); +#2825=EDGE_LOOP('',(#2821,#2822,#2823,#2824)); +#2826=FACE_OUTER_BOUND('',#2825,.T.); +#2827=ADVANCED_FACE('bottom-33',(#2826),#171,.T.); +#2828=ORIENTED_EDGE('',*,*,#1851,.T.); +#2829=ORIENTED_EDGE('',*,*,#1707,.F.); +#2830=ORIENTED_EDGE('',*,*,#969,.F.); +#2831=ORIENTED_EDGE('',*,*,#1689,.T.); +#2832=EDGE_LOOP('',(#2828,#2829,#2830,#2831)); +#2833=FACE_OUTER_BOUND('',#2832,.T.); +#2834=ADVANCED_FACE('back-34',(#2833),#176,.F.); +#2835=ORIENTED_EDGE('',*,*,#1275,.T.); +#2836=ORIENTED_EDGE('',*,*,#1869,.T.); +#2837=ORIENTED_EDGE('',*,*,#1887,.F.); +#2838=ORIENTED_EDGE('',*,*,#1779,.F.); +#2839=EDGE_LOOP('',(#2835,#2836,#2837,#2838)); +#2840=FACE_OUTER_BOUND('',#2839,.T.); +#2841=ADVANCED_FACE('back-35',(#2840),#181,.T.); +#2842=ORIENTED_EDGE('',*,*,#1347,.T.); +#2843=ORIENTED_EDGE('',*,*,#1905,.T.); +#2844=ORIENTED_EDGE('',*,*,#1923,.F.); +#2845=ORIENTED_EDGE('',*,*,#1869,.F.); +#2846=EDGE_LOOP('',(#2842,#2843,#2844,#2845)); +#2847=FACE_OUTER_BOUND('',#2846,.T.); +#2848=ADVANCED_FACE('back-36',(#2847),#186,.T.); +#2849=ORIENTED_EDGE('',*,*,#1761,.T.); +#2850=ORIENTED_EDGE('',*,*,#1941,.T.); +#2851=ORIENTED_EDGE('',*,*,#1905,.F.); +#2852=ORIENTED_EDGE('',*,*,#1365,.F.); +#2853=EDGE_LOOP('',(#2849,#2850,#2851,#2852)); +#2854=FACE_OUTER_BOUND('',#2853,.T.); +#2855=ADVANCED_FACE('top-37',(#2854),#191,.T.); +#2856=ORIENTED_EDGE('',*,*,#1959,.T.); +#2857=ORIENTED_EDGE('',*,*,#1977,.T.); +#2858=ORIENTED_EDGE('',*,*,#1995,.F.); +#2859=ORIENTED_EDGE('',*,*,#2013,.F.); +#2860=EDGE_LOOP('',(#2856,#2857,#2858,#2859)); +#2861=FACE_OUTER_BOUND('',#2860,.T.); +#2862=ADVANCED_FACE('right-38',(#2861),#196,.T.); +#2863=ORIENTED_EDGE('',*,*,#2013,.T.); +#2864=ORIENTED_EDGE('',*,*,#2031,.F.); +#2865=ORIENTED_EDGE('',*,*,#1383,.F.); +#2866=ORIENTED_EDGE('',*,*,#2049,.T.); +#2867=EDGE_LOOP('',(#2863,#2864,#2865,#2866)); +#2868=FACE_OUTER_BOUND('',#2867,.T.); +#2869=ADVANCED_FACE('front-39',(#2868),#201,.T.); +#2870=ORIENTED_EDGE('',*,*,#2067,.T.); +#2871=ORIENTED_EDGE('',*,*,#1959,.F.); +#2872=ORIENTED_EDGE('',*,*,#2049,.F.); +#2873=ORIENTED_EDGE('',*,*,#1455,.T.); +#2874=EDGE_LOOP('',(#2870,#2871,#2872,#2873)); +#2875=FACE_OUTER_BOUND('',#2874,.T.); +#2876=ADVANCED_FACE('bottom-40',(#2875),#206,.T.); +#2877=ORIENTED_EDGE('',*,*,#1995,.T.); +#2878=ORIENTED_EDGE('',*,*,#2085,.T.); +#2879=ORIENTED_EDGE('',*,*,#2103,.F.); +#2880=ORIENTED_EDGE('',*,*,#2121,.F.); +#2881=EDGE_LOOP('',(#2877,#2878,#2879,#2880)); +#2882=FACE_OUTER_BOUND('',#2881,.T.); +#2883=ADVANCED_FACE('right-41',(#2882),#211,.T.); +#2884=ORIENTED_EDGE('',*,*,#2121,.T.); +#2885=ORIENTED_EDGE('',*,*,#2139,.F.); +#2886=ORIENTED_EDGE('',*,*,#1473,.F.); +#2887=ORIENTED_EDGE('',*,*,#2031,.T.); +#2888=EDGE_LOOP('',(#2884,#2885,#2886,#2887)); +#2889=FACE_OUTER_BOUND('',#2888,.T.); +#2890=ADVANCED_FACE('front-42',(#2889),#216,.T.); +#2891=ORIENTED_EDGE('',*,*,#2103,.T.); +#2892=ORIENTED_EDGE('',*,*,#2157,.T.); +#2893=ORIENTED_EDGE('',*,*,#2175,.F.); +#2894=ORIENTED_EDGE('',*,*,#2193,.F.); +#2895=EDGE_LOOP('',(#2891,#2892,#2893,#2894)); +#2896=FACE_OUTER_BOUND('',#2895,.T.); +#2897=ADVANCED_FACE('right-43',(#2896),#221,.T.); +#2898=ORIENTED_EDGE('',*,*,#2193,.T.); +#2899=ORIENTED_EDGE('',*,*,#2211,.F.); +#2900=ORIENTED_EDGE('',*,*,#1563,.F.); +#2901=ORIENTED_EDGE('',*,*,#2139,.T.); +#2902=EDGE_LOOP('',(#2898,#2899,#2900,#2901)); +#2903=FACE_OUTER_BOUND('',#2902,.T.); +#2904=ADVANCED_FACE('front-44',(#2903),#226,.T.); +#2905=ORIENTED_EDGE('',*,*,#2211,.T.); +#2906=ORIENTED_EDGE('',*,*,#2175,.T.); +#2907=ORIENTED_EDGE('',*,*,#2229,.F.); +#2908=ORIENTED_EDGE('',*,*,#1599,.F.); +#2909=EDGE_LOOP('',(#2905,#2906,#2907,#2908)); +#2910=FACE_OUTER_BOUND('',#2909,.T.); +#2911=ADVANCED_FACE('top-45',(#2910),#231,.T.); +#2912=ORIENTED_EDGE('',*,*,#2247,.T.); +#2913=ORIENTED_EDGE('',*,*,#2265,.T.); +#2914=ORIENTED_EDGE('',*,*,#2283,.F.); +#2915=ORIENTED_EDGE('',*,*,#1977,.F.); +#2916=EDGE_LOOP('',(#2912,#2913,#2914,#2915)); +#2917=FACE_OUTER_BOUND('',#2916,.T.); +#2918=ADVANCED_FACE('right-46',(#2917),#236,.T.); +#2919=ORIENTED_EDGE('',*,*,#2301,.T.); +#2920=ORIENTED_EDGE('',*,*,#2247,.F.); +#2921=ORIENTED_EDGE('',*,*,#2067,.F.); +#2922=ORIENTED_EDGE('',*,*,#1653,.T.); +#2923=EDGE_LOOP('',(#2919,#2920,#2921,#2922)); +#2924=FACE_OUTER_BOUND('',#2923,.T.); +#2925=ADVANCED_FACE('bottom-47',(#2924),#241,.T.); +#2926=ORIENTED_EDGE('',*,*,#1725,.T.); +#2927=ORIENTED_EDGE('',*,*,#1851,.F.); +#2928=ORIENTED_EDGE('',*,*,#1671,.F.); +#2929=ORIENTED_EDGE('',*,*,#1527,.T.); +#2930=EDGE_LOOP('',(#2926,#2927,#2928,#2929)); +#2931=FACE_OUTER_BOUND('',#2930,.T.); +#2932=ADVANCED_FACE('right-48',(#2931),#246,.F.); +#2933=ORIENTED_EDGE('',*,*,#2283,.T.); +#2934=ORIENTED_EDGE('',*,*,#2319,.T.); +#2935=ORIENTED_EDGE('',*,*,#2337,.F.); +#2936=ORIENTED_EDGE('',*,*,#2085,.F.); +#2937=EDGE_LOOP('',(#2933,#2934,#2935,#2936)); +#2938=FACE_OUTER_BOUND('',#2937,.T.); +#2939=ADVANCED_FACE('right-49',(#2938),#251,.T.); +#2940=ORIENTED_EDGE('',*,*,#2337,.T.); +#2941=ORIENTED_EDGE('',*,*,#2355,.T.); +#2942=ORIENTED_EDGE('',*,*,#2373,.F.); +#2943=ORIENTED_EDGE('',*,*,#2157,.F.); +#2944=EDGE_LOOP('',(#2940,#2941,#2942,#2943)); +#2945=FACE_OUTER_BOUND('',#2944,.T.); +#2946=ADVANCED_FACE('right-50',(#2945),#256,.T.); +#2947=ORIENTED_EDGE('',*,*,#2229,.T.); +#2948=ORIENTED_EDGE('',*,*,#2373,.T.); +#2949=ORIENTED_EDGE('',*,*,#2391,.F.); +#2950=ORIENTED_EDGE('',*,*,#1743,.F.); +#2951=EDGE_LOOP('',(#2947,#2948,#2949,#2950)); +#2952=FACE_OUTER_BOUND('',#2951,.T.); +#2953=ADVANCED_FACE('top-51',(#2952),#261,.T.); +#2954=ORIENTED_EDGE('',*,*,#2409,.T.); +#2955=ORIENTED_EDGE('',*,*,#2427,.T.); +#2956=ORIENTED_EDGE('',*,*,#2445,.F.); +#2957=ORIENTED_EDGE('',*,*,#2265,.F.); +#2958=EDGE_LOOP('',(#2954,#2955,#2956,#2957)); +#2959=FACE_OUTER_BOUND('',#2958,.T.); +#2960=ADVANCED_FACE('right-52',(#2959),#266,.T.); +#2961=ORIENTED_EDGE('',*,*,#1797,.T.); +#2962=ORIENTED_EDGE('',*,*,#2463,.T.); +#2963=ORIENTED_EDGE('',*,*,#2427,.F.); +#2964=ORIENTED_EDGE('',*,*,#2481,.F.); +#2965=EDGE_LOOP('',(#2961,#2962,#2963,#2964)); +#2966=FACE_OUTER_BOUND('',#2965,.T.); +#2967=ADVANCED_FACE('back-53',(#2966),#271,.T.); +#2968=ORIENTED_EDGE('',*,*,#2481,.T.); +#2969=ORIENTED_EDGE('',*,*,#2409,.F.); +#2970=ORIENTED_EDGE('',*,*,#2301,.F.); +#2971=ORIENTED_EDGE('',*,*,#1833,.T.); +#2972=EDGE_LOOP('',(#2968,#2969,#2970,#2971)); +#2973=FACE_OUTER_BOUND('',#2972,.T.); +#2974=ADVANCED_FACE('bottom-54',(#2973),#276,.T.); +#2975=ORIENTED_EDGE('',*,*,#2445,.T.); +#2976=ORIENTED_EDGE('',*,*,#2499,.T.); +#2977=ORIENTED_EDGE('',*,*,#2517,.F.); +#2978=ORIENTED_EDGE('',*,*,#2319,.F.); +#2979=EDGE_LOOP('',(#2975,#2976,#2977,#2978)); +#2980=FACE_OUTER_BOUND('',#2979,.T.); +#2981=ADVANCED_FACE('right-55',(#2980),#281,.T.); +#2982=ORIENTED_EDGE('',*,*,#1887,.T.); +#2983=ORIENTED_EDGE('',*,*,#2535,.T.); +#2984=ORIENTED_EDGE('',*,*,#2499,.F.); +#2985=ORIENTED_EDGE('',*,*,#2463,.F.); +#2986=EDGE_LOOP('',(#2982,#2983,#2984,#2985)); +#2987=FACE_OUTER_BOUND('',#2986,.T.); +#2988=ADVANCED_FACE('back-56',(#2987),#286,.T.); +#2989=ORIENTED_EDGE('',*,*,#2517,.T.); +#2990=ORIENTED_EDGE('',*,*,#2553,.T.); +#2991=ORIENTED_EDGE('',*,*,#2571,.F.); +#2992=ORIENTED_EDGE('',*,*,#2355,.F.); +#2993=EDGE_LOOP('',(#2989,#2990,#2991,#2992)); +#2994=FACE_OUTER_BOUND('',#2993,.T.); +#2995=ADVANCED_FACE('right-57',(#2994),#291,.T.); +#2996=ORIENTED_EDGE('',*,*,#1923,.T.); +#2997=ORIENTED_EDGE('',*,*,#2589,.T.); +#2998=ORIENTED_EDGE('',*,*,#2553,.F.); +#2999=ORIENTED_EDGE('',*,*,#2535,.F.); +#3000=EDGE_LOOP('',(#2996,#2997,#2998,#2999)); +#3001=FACE_OUTER_BOUND('',#3000,.T.); +#3002=ADVANCED_FACE('back-58',(#3001),#296,.T.); +#3003=ORIENTED_EDGE('',*,*,#2391,.T.); +#3004=ORIENTED_EDGE('',*,*,#2571,.T.); +#3005=ORIENTED_EDGE('',*,*,#2589,.F.); +#3006=ORIENTED_EDGE('',*,*,#1941,.F.); +#3007=EDGE_LOOP('',(#3003,#3004,#3005,#3006)); +#3008=FACE_OUTER_BOUND('',#3007,.T.); +#3009=ADVANCED_FACE('top-59',(#3008),#301,.T.); +#3010=CLOSED_SHELL('',(#2596,#2617,#2652,#2610,#2603,#2631,#2666,#2624,#2694,#2659,#2757,#2750,#2645,#2680,#2638,#2715,#2764,#2701,#2708,#2792,#2876,#2869,#2785,#2687,#2729,#2778,#2722,#2890,#2820,#2827,#2925,#2862,#2911,#2813,#2743,#2736,#2904,#2841,#2883,#2967,#2974,#2918,#2897,#2953,#2855,#2848,#2988,#2939,#2960,#2946,#3009,#3002,#2981,#2995)); +#3011=CLOSED_SHELL('',(#2673,#2799,#2834,#2806,#2771,#2932)); +#3012=ORIENTED_CLOSED_SHELL('',*,#3011,.T.); +#3013=BREP_WITH_VOIDS('box-cavity-0',#3010,(#3012)); +#3014=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#3015=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#3016=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#3017=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000426325642881E-8),#3014,'distance_accuracy_value','exchange error bound'); +#3018=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#3017)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#3014,#3015,#3016)) REPRESENTATION_CONTEXT('','')); +#3019=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#3013),#3018); +#3020=APPLICATION_CONTEXT('automotive design'); +#3021=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#3020); +#3022=PRODUCT_CONTEXT('',#3020,'mechanical'); +#3023=PRODUCT('box-cavity','box-cavity','',(#3022)); +#3024=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#3023,.NOT_KNOWN.); +#3025=PRODUCT_DEFINITION_CONTEXT('part definition',#3020,'design'); +#3026=PRODUCT_DEFINITION('','',#3024,#3025); +#3027=PRODUCT_DEFINITION_SHAPE('','',#3026); +#3028=SHAPE_DEFINITION_REPRESENTATION(#3027,#3019); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/box-cavity.step.m.report.json b/main/opengeometry/tests/fixtures/cases/box-cavity.step.m.report.json new file mode 100644 index 0000000..0c3a55e --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cavity.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":1,"collapsed_chart_uses":0,"edges":120,"exchange_error_bound":1.0000042632564288e-8,"faces":60,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"1","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-cavity.step.mm b/main/opengeometry/tests/fixtures/cases/box-cavity.step.mm new file mode 100644 index 0000000..231a04c --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cavity.step.mm @@ -0,0 +1,3037 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('box-cavity','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#24=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#28=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#29=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#34=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#35=AXIS2_PLACEMENT_3D('',#32,#33,#34); +#36=PLANE('',#35); +#37=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#38=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#39=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#40=AXIS2_PLACEMENT_3D('',#37,#38,#39); +#41=PLANE('',#40); +#42=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#43=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#44=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#45=AXIS2_PLACEMENT_3D('',#42,#43,#44); +#46=PLANE('',#45); +#47=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#48=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#49=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#50=AXIS2_PLACEMENT_3D('',#47,#48,#49); +#51=PLANE('',#50); +#52=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#53=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#54=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#55=AXIS2_PLACEMENT_3D('',#52,#53,#54); +#56=PLANE('',#55); +#57=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#58=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#59=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#60=AXIS2_PLACEMENT_3D('',#57,#58,#59); +#61=PLANE('',#60); +#62=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#63=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#64=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#65=AXIS2_PLACEMENT_3D('',#62,#63,#64); +#66=PLANE('',#65); +#67=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#68=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#69=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#70=AXIS2_PLACEMENT_3D('',#67,#68,#69); +#71=PLANE('',#70); +#72=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#73=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#74=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#75=AXIS2_PLACEMENT_3D('',#72,#73,#74); +#76=PLANE('',#75); +#77=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#78=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#79=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#80=AXIS2_PLACEMENT_3D('',#77,#78,#79); +#81=PLANE('',#80); +#82=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#83=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#84=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#85=AXIS2_PLACEMENT_3D('',#82,#83,#84); +#86=PLANE('',#85); +#87=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#88=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#89=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#90=AXIS2_PLACEMENT_3D('',#87,#88,#89); +#91=PLANE('',#90); +#92=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#93=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#94=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#95=AXIS2_PLACEMENT_3D('',#92,#93,#94); +#96=PLANE('',#95); +#97=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#98=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#99=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#100=AXIS2_PLACEMENT_3D('',#97,#98,#99); +#101=PLANE('',#100); +#102=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#103=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#104=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#105=AXIS2_PLACEMENT_3D('',#102,#103,#104); +#106=PLANE('',#105); +#107=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#108=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#109=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#110=AXIS2_PLACEMENT_3D('',#107,#108,#109); +#111=PLANE('',#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#113=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#114=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#115=AXIS2_PLACEMENT_3D('',#112,#113,#114); +#116=PLANE('',#115); +#117=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#118=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#119=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#120=AXIS2_PLACEMENT_3D('',#117,#118,#119); +#121=PLANE('',#120); +#122=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#123=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#124=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#125=AXIS2_PLACEMENT_3D('',#122,#123,#124); +#126=PLANE('',#125); +#127=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#128=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#129=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#130=AXIS2_PLACEMENT_3D('',#127,#128,#129); +#131=PLANE('',#130); +#132=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#133=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#134=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#135=AXIS2_PLACEMENT_3D('',#132,#133,#134); +#136=PLANE('',#135); +#137=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#138=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#139=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#140=AXIS2_PLACEMENT_3D('',#137,#138,#139); +#141=PLANE('',#140); +#142=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#143=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#144=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#145=AXIS2_PLACEMENT_3D('',#142,#143,#144); +#146=PLANE('',#145); +#147=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#148=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#149=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#150=AXIS2_PLACEMENT_3D('',#147,#148,#149); +#151=PLANE('',#150); +#152=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,-1.00000000000000000E3)); +#153=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#154=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#155=AXIS2_PLACEMENT_3D('',#152,#153,#154); +#156=PLANE('',#155); +#157=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#158=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#159=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#160=AXIS2_PLACEMENT_3D('',#157,#158,#159); +#161=PLANE('',#160); +#162=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#163=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#164=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#165=AXIS2_PLACEMENT_3D('',#162,#163,#164); +#166=PLANE('',#165); +#167=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#168=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#169=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#170=AXIS2_PLACEMENT_3D('',#167,#168,#169); +#171=PLANE('',#170); +#172=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-2.00000000000000000E3)); +#173=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#174=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#175=AXIS2_PLACEMENT_3D('',#172,#173,#174); +#176=PLANE('',#175); +#177=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#178=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#179=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#180=AXIS2_PLACEMENT_3D('',#177,#178,#179); +#181=PLANE('',#180); +#182=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#183=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#184=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#185=AXIS2_PLACEMENT_3D('',#182,#183,#184); +#186=PLANE('',#185); +#187=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#188=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#189=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#190=AXIS2_PLACEMENT_3D('',#187,#188,#189); +#191=PLANE('',#190); +#192=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#193=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#194=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#195=AXIS2_PLACEMENT_3D('',#192,#193,#194); +#196=PLANE('',#195); +#197=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#198=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#199=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#200=AXIS2_PLACEMENT_3D('',#197,#198,#199); +#201=PLANE('',#200); +#202=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#203=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#204=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#205=AXIS2_PLACEMENT_3D('',#202,#203,#204); +#206=PLANE('',#205); +#207=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#208=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#209=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#210=AXIS2_PLACEMENT_3D('',#207,#208,#209); +#211=PLANE('',#210); +#212=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#213=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#214=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#215=AXIS2_PLACEMENT_3D('',#212,#213,#214); +#216=PLANE('',#215); +#217=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#218=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#219=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#220=AXIS2_PLACEMENT_3D('',#217,#218,#219); +#221=PLANE('',#220); +#222=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#223=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#224=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#225=AXIS2_PLACEMENT_3D('',#222,#223,#224); +#226=PLANE('',#225); +#227=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#228=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#229=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#230=AXIS2_PLACEMENT_3D('',#227,#228,#229); +#231=PLANE('',#230); +#232=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#233=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#234=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#235=AXIS2_PLACEMENT_3D('',#232,#233,#234); +#236=PLANE('',#235); +#237=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#238=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#239=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#240=AXIS2_PLACEMENT_3D('',#237,#238,#239); +#241=PLANE('',#240); +#242=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#243=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#244=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#245=AXIS2_PLACEMENT_3D('',#242,#243,#244); +#246=PLANE('',#245); +#247=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#248=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#249=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#250=AXIS2_PLACEMENT_3D('',#247,#248,#249); +#251=PLANE('',#250); +#252=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#253=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#254=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#255=AXIS2_PLACEMENT_3D('',#252,#253,#254); +#256=PLANE('',#255); +#257=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#258=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#259=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#260=AXIS2_PLACEMENT_3D('',#257,#258,#259); +#261=PLANE('',#260); +#262=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#263=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#264=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#265=AXIS2_PLACEMENT_3D('',#262,#263,#264); +#266=PLANE('',#265); +#267=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#268=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#269=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#270=AXIS2_PLACEMENT_3D('',#267,#268,#269); +#271=PLANE('',#270); +#272=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#273=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#274=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#275=AXIS2_PLACEMENT_3D('',#272,#273,#274); +#276=PLANE('',#275); +#277=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#278=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#279=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#280=AXIS2_PLACEMENT_3D('',#277,#278,#279); +#281=PLANE('',#280); +#282=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#283=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#284=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#285=AXIS2_PLACEMENT_3D('',#282,#283,#284); +#286=PLANE('',#285); +#287=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#288=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#289=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#290=AXIS2_PLACEMENT_3D('',#287,#288,#289); +#291=PLANE('',#290); +#292=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#293=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#294=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#295=AXIS2_PLACEMENT_3D('',#292,#293,#294); +#296=PLANE('',#295); +#297=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#298=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#299=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#300=AXIS2_PLACEMENT_3D('',#297,#298,#299); +#301=PLANE('',#300); +#302=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,0.00000000000000000E0)); +#303=VERTEX_POINT('',#302); +#304=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,-1.00000000000000000E3)); +#305=VERTEX_POINT('',#304); +#306=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E3)); +#307=VERTEX_POINT('',#306); +#308=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#309=VERTEX_POINT('',#308); +#310=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#311=VERTEX_POINT('',#310); +#312=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,0.00000000000000000E0)); +#313=VERTEX_POINT('',#312); +#314=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-1.00000000000000000E3)); +#315=VERTEX_POINT('',#314); +#316=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#317=VERTEX_POINT('',#316); +#318=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,-1.00000000000000000E3)); +#319=VERTEX_POINT('',#318); +#320=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#321=VERTEX_POINT('',#320); +#322=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#323=VERTEX_POINT('',#322); +#324=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-1.00000000000000000E3)); +#325=VERTEX_POINT('',#324); +#326=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#327=VERTEX_POINT('',#326); +#328=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3,-1.00000000000000000E3)); +#329=VERTEX_POINT('',#328); +#330=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,-2.00000000000000000E3)); +#331=VERTEX_POINT('',#330); +#332=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E3)); +#333=VERTEX_POINT('',#332); +#334=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#335=VERTEX_POINT('',#334); +#336=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,-2.00000000000000000E3)); +#337=VERTEX_POINT('',#336); +#338=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#339=VERTEX_POINT('',#338); +#340=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-2.00000000000000000E3)); +#341=VERTEX_POINT('',#340); +#342=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#343=VERTEX_POINT('',#342); +#344=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,-1.00000000000000000E3)); +#345=VERTEX_POINT('',#344); +#346=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-2.00000000000000000E3)); +#347=VERTEX_POINT('',#346); +#348=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3,-2.00000000000000000E3)); +#349=VERTEX_POINT('',#348); +#350=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,-3.00000000000000000E3)); +#351=VERTEX_POINT('',#350); +#352=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#353=VERTEX_POINT('',#352); +#354=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-3.00000000000000000E3)); +#355=VERTEX_POINT('',#354); +#356=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#357=VERTEX_POINT('',#356); +#358=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,-3.00000000000000000E3)); +#359=VERTEX_POINT('',#358); +#360=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,-3.00000000000000000E3)); +#361=VERTEX_POINT('',#360); +#362=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-3.00000000000000000E3)); +#363=VERTEX_POINT('',#362); +#364=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#365=VERTEX_POINT('',#364); +#366=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#367=VERTEX_POINT('',#366); +#368=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,0.00000000000000000E0)); +#369=VERTEX_POINT('',#368); +#370=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-1.00000000000000000E3)); +#371=VERTEX_POINT('',#370); +#372=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#373=VERTEX_POINT('',#372); +#374=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,-1.00000000000000000E3)); +#375=VERTEX_POINT('',#374); +#376=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#377=VERTEX_POINT('',#376); +#378=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#379=VERTEX_POINT('',#378); +#380=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,-1.00000000000000000E3)); +#381=VERTEX_POINT('',#380); +#382=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#383=VERTEX_POINT('',#382); +#384=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,-2.00000000000000000E3)); +#385=VERTEX_POINT('',#384); +#386=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#387=VERTEX_POINT('',#386); +#388=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,-2.00000000000000000E3)); +#389=VERTEX_POINT('',#388); +#390=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,-3.00000000000000000E3)); +#391=VERTEX_POINT('',#390); +#392=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#393=VERTEX_POINT('',#392); +#394=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,-3.00000000000000000E3)); +#395=VERTEX_POINT('',#394); +#396=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#397=VERTEX_POINT('',#396); +#398=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#399=VERTEX_POINT('',#398); +#400=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-1.00000000000000000E3)); +#401=VERTEX_POINT('',#400); +#402=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#403=VERTEX_POINT('',#402); +#404=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3,0.00000000000000000E0)); +#405=VERTEX_POINT('',#404); +#406=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3,-1.00000000000000000E3)); +#407=VERTEX_POINT('',#406); +#408=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#409=VERTEX_POINT('',#408); +#410=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-1.00000000000000000E3)); +#411=VERTEX_POINT('',#410); +#412=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#413=VERTEX_POINT('',#412); +#414=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#415=VERTEX_POINT('',#414); +#416=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3,-2.00000000000000000E3)); +#417=VERTEX_POINT('',#416); +#418=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#419=VERTEX_POINT('',#418); +#420=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-2.00000000000000000E3)); +#421=VERTEX_POINT('',#420); +#422=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#423=VERTEX_POINT('',#422); +#424=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3,-3.00000000000000000E3)); +#425=VERTEX_POINT('',#424); +#426=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3,-3.00000000000000000E3)); +#427=VERTEX_POINT('',#426); +#428=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#429=VERTEX_POINT('',#428); +#430=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,0.00000000000000000E0)); +#431=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#432=VECTOR('',#431,1.00000000000000000E3); +#433=LINE('',#430,#432); +#434=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#435=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#436=VECTOR('',#435,1.00000000000000000E3); +#437=LINE('',#434,#436); +#438=DEFINITIONAL_REPRESENTATION('',(#437),#1); +#439=PCURVE('',#6,#438); +#440=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#441=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#442=VECTOR('',#441,1.00000000000000000E3); +#443=LINE('',#440,#442); +#444=DEFINITIONAL_REPRESENTATION('',(#443),#1); +#445=PCURVE('',#21,#444); +#446=SURFACE_CURVE('',#433,(#439,#445),.CURVE_3D.); +#447=EDGE_CURVE('',#303,#305,#446,.T.); +#448=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E3)); +#449=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#450=VECTOR('',#449,1.00000000000000000E3); +#451=LINE('',#448,#450); +#452=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#453=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#454=VECTOR('',#453,1.00000000000000000E3); +#455=LINE('',#452,#454); +#456=DEFINITIONAL_REPRESENTATION('',(#455),#1); +#457=PCURVE('',#6,#456); +#458=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#459=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#460=VECTOR('',#459,1.00000000000000000E3); +#461=LINE('',#458,#460); +#462=DEFINITIONAL_REPRESENTATION('',(#461),#1); +#463=PCURVE('',#46,#462); +#464=SURFACE_CURVE('',#451,(#457,#463),.CURVE_3D.); +#465=EDGE_CURVE('',#307,#305,#464,.T.); +#466=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#467=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#468=VECTOR('',#467,1.00000000000000000E3); +#469=LINE('',#466,#468); +#470=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#471=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#472=VECTOR('',#471,1.00000000000000000E3); +#473=LINE('',#470,#472); +#474=DEFINITIONAL_REPRESENTATION('',(#473),#1); +#475=PCURVE('',#6,#474); +#476=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#477=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#478=VECTOR('',#477,1.00000000000000000E3); +#479=LINE('',#476,#478); +#480=DEFINITIONAL_REPRESENTATION('',(#479),#1); +#481=PCURVE('',#16,#480); +#482=SURFACE_CURVE('',#469,(#475,#481),.CURVE_3D.); +#483=EDGE_CURVE('',#309,#307,#482,.T.); +#484=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#485=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#486=VECTOR('',#485,1.00000000000000000E3); +#487=LINE('',#484,#486); +#488=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#489=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#490=VECTOR('',#489,1.00000000000000000E3); +#491=LINE('',#488,#490); +#492=DEFINITIONAL_REPRESENTATION('',(#491),#1); +#493=PCURVE('',#6,#492); +#494=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#495=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#496=VECTOR('',#495,1.00000000000000000E3); +#497=LINE('',#494,#496); +#498=DEFINITIONAL_REPRESENTATION('',(#497),#1); +#499=PCURVE('',#11,#498); +#500=SURFACE_CURVE('',#487,(#493,#499),.CURVE_3D.); +#501=EDGE_CURVE('',#309,#303,#500,.T.); +#502=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#503=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#504=VECTOR('',#503,1.00000000000000000E3); +#505=LINE('',#502,#504); +#506=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#507=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#508=VECTOR('',#507,1.00000000000000000E3); +#509=LINE('',#506,#508); +#510=DEFINITIONAL_REPRESENTATION('',(#509),#1); +#511=PCURVE('',#11,#510); +#512=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#513=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#514=VECTOR('',#513,1.00000000000000000E3); +#515=LINE('',#512,#514); +#516=DEFINITIONAL_REPRESENTATION('',(#515),#1); +#517=PCURVE('',#116,#516); +#518=SURFACE_CURVE('',#505,(#511,#517),.CURVE_3D.); +#519=EDGE_CURVE('',#311,#313,#518,.T.); +#520=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,0.00000000000000000E0)); +#521=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#522=VECTOR('',#521,1.00000000000000000E3); +#523=LINE('',#520,#522); +#524=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#525=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#526=VECTOR('',#525,1.00000000000000000E3); +#527=LINE('',#524,#526); +#528=DEFINITIONAL_REPRESENTATION('',(#527),#1); +#529=PCURVE('',#11,#528); +#530=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#531=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#532=VECTOR('',#531,1.00000000000000000E3); +#533=LINE('',#530,#532); +#534=DEFINITIONAL_REPRESENTATION('',(#533),#1); +#535=PCURVE('',#26,#534); +#536=SURFACE_CURVE('',#523,(#529,#535),.CURVE_3D.); +#537=EDGE_CURVE('',#303,#313,#536,.T.); +#538=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#539=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#540=VECTOR('',#539,1.00000000000000000E3); +#541=LINE('',#538,#540); +#542=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#543=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#544=VECTOR('',#543,1.00000000000000000E3); +#545=LINE('',#542,#544); +#546=DEFINITIONAL_REPRESENTATION('',(#545),#1); +#547=PCURVE('',#11,#546); +#548=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#549=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#550=VECTOR('',#549,1.00000000000000000E3); +#551=LINE('',#548,#550); +#552=DEFINITIONAL_REPRESENTATION('',(#551),#1); +#553=PCURVE('',#16,#552); +#554=SURFACE_CURVE('',#541,(#547,#553),.CURVE_3D.); +#555=EDGE_CURVE('',#309,#311,#554,.T.); +#556=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E3)); +#557=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#558=VECTOR('',#557,1.00000000000000000E3); +#559=LINE('',#556,#558); +#560=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#561=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#562=VECTOR('',#561,1.00000000000000000E3); +#563=LINE('',#560,#562); +#564=DEFINITIONAL_REPRESENTATION('',(#563),#1); +#565=PCURVE('',#16,#564); +#566=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#567=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#568=VECTOR('',#567,1.00000000000000000E3); +#569=LINE('',#566,#568); +#570=DEFINITIONAL_REPRESENTATION('',(#569),#1); +#571=PCURVE('',#51,#570); +#572=SURFACE_CURVE('',#559,(#565,#571),.CURVE_3D.); +#573=EDGE_CURVE('',#307,#315,#572,.T.); +#574=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#575=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#576=VECTOR('',#575,1.00000000000000000E3); +#577=LINE('',#574,#576); +#578=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#579=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#580=VECTOR('',#579,1.00000000000000000E3); +#581=LINE('',#578,#580); +#582=DEFINITIONAL_REPRESENTATION('',(#581),#1); +#583=PCURVE('',#16,#582); +#584=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#585=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#586=VECTOR('',#585,1.00000000000000000E3); +#587=LINE('',#584,#586); +#588=DEFINITIONAL_REPRESENTATION('',(#587),#1); +#589=PCURVE('',#121,#588); +#590=SURFACE_CURVE('',#577,(#583,#589),.CURVE_3D.); +#591=EDGE_CURVE('',#311,#315,#590,.T.); +#592=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#593=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#594=VECTOR('',#593,1.00000000000000000E3); +#595=LINE('',#592,#594); +#596=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#597=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#598=VECTOR('',#597,1.00000000000000000E3); +#599=LINE('',#596,#598); +#600=DEFINITIONAL_REPRESENTATION('',(#599),#1); +#601=PCURVE('',#21,#600); +#602=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#603=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#604=VECTOR('',#603,1.00000000000000000E3); +#605=LINE('',#602,#604); +#606=DEFINITIONAL_REPRESENTATION('',(#605),#1); +#607=PCURVE('',#31,#606); +#608=SURFACE_CURVE('',#595,(#601,#607),.CURVE_3D.); +#609=EDGE_CURVE('',#317,#319,#608,.T.); +#610=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,-1.00000000000000000E3)); +#611=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#612=VECTOR('',#611,1.00000000000000000E3); +#613=LINE('',#610,#612); +#614=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#615=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#616=VECTOR('',#615,1.00000000000000000E3); +#617=LINE('',#614,#616); +#618=DEFINITIONAL_REPRESENTATION('',(#617),#1); +#619=PCURVE('',#21,#618); +#620=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#621=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#622=VECTOR('',#621,1.00000000000000000E3); +#623=LINE('',#620,#622); +#624=DEFINITIONAL_REPRESENTATION('',(#623),#1); +#625=PCURVE('',#56,#624); +#626=SURFACE_CURVE('',#613,(#619,#625),.CURVE_3D.); +#627=EDGE_CURVE('',#305,#319,#626,.T.); +#628=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,0.00000000000000000E0)); +#629=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#630=VECTOR('',#629,1.00000000000000000E3); +#631=LINE('',#628,#630); +#632=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#633=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#634=VECTOR('',#633,1.00000000000000000E3); +#635=LINE('',#632,#634); +#636=DEFINITIONAL_REPRESENTATION('',(#635),#1); +#637=PCURVE('',#21,#636); +#638=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#639=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#640=VECTOR('',#639,1.00000000000000000E3); +#641=LINE('',#638,#640); +#642=DEFINITIONAL_REPRESENTATION('',(#641),#1); +#643=PCURVE('',#26,#642); +#644=SURFACE_CURVE('',#631,(#637,#643),.CURVE_3D.); +#645=EDGE_CURVE('',#303,#317,#644,.T.); +#646=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,0.00000000000000000E0)); +#647=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#648=VECTOR('',#647,1.00000000000000000E3); +#649=LINE('',#646,#648); +#650=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#651=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#652=VECTOR('',#651,1.00000000000000000E3); +#653=LINE('',#650,#652); +#654=DEFINITIONAL_REPRESENTATION('',(#653),#1); +#655=PCURVE('',#26,#654); +#656=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#657=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#658=VECTOR('',#657,1.00000000000000000E3); +#659=LINE('',#656,#658); +#660=DEFINITIONAL_REPRESENTATION('',(#659),#1); +#661=PCURVE('',#126,#660); +#662=SURFACE_CURVE('',#649,(#655,#661),.CURVE_3D.); +#663=EDGE_CURVE('',#313,#321,#662,.T.); +#664=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#665=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#666=VECTOR('',#665,1.00000000000000000E3); +#667=LINE('',#664,#666); +#668=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#669=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#670=VECTOR('',#669,1.00000000000000000E3); +#671=LINE('',#668,#670); +#672=DEFINITIONAL_REPRESENTATION('',(#671),#1); +#673=PCURVE('',#26,#672); +#674=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#675=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#676=VECTOR('',#675,1.00000000000000000E3); +#677=LINE('',#674,#676); +#678=DEFINITIONAL_REPRESENTATION('',(#677),#1); +#679=PCURVE('',#36,#678); +#680=SURFACE_CURVE('',#667,(#673,#679),.CURVE_3D.); +#681=EDGE_CURVE('',#317,#321,#680,.T.); +#682=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#683=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#684=VECTOR('',#683,1.00000000000000000E3); +#685=LINE('',#682,#684); +#686=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#687=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#688=VECTOR('',#687,1.00000000000000000E3); +#689=LINE('',#686,#688); +#690=DEFINITIONAL_REPRESENTATION('',(#689),#1); +#691=PCURVE('',#31,#690); +#692=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#693=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#694=VECTOR('',#693,1.00000000000000000E3); +#695=LINE('',#692,#694); +#696=DEFINITIONAL_REPRESENTATION('',(#695),#1); +#697=PCURVE('',#41,#696); +#698=SURFACE_CURVE('',#685,(#691,#697),.CURVE_3D.); +#699=EDGE_CURVE('',#323,#325,#698,.T.); +#700=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,-1.00000000000000000E3)); +#701=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#702=VECTOR('',#701,1.00000000000000000E3); +#703=LINE('',#700,#702); +#704=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#705=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#706=VECTOR('',#705,1.00000000000000000E3); +#707=LINE('',#704,#706); +#708=DEFINITIONAL_REPRESENTATION('',(#707),#1); +#709=PCURVE('',#31,#708); +#710=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#711=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#712=VECTOR('',#711,1.00000000000000000E3); +#713=LINE('',#710,#712); +#714=DEFINITIONAL_REPRESENTATION('',(#713),#1); +#715=PCURVE('',#66,#714); +#716=SURFACE_CURVE('',#703,(#709,#715),.CURVE_3D.); +#717=EDGE_CURVE('',#319,#325,#716,.T.); +#718=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#719=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#720=VECTOR('',#719,1.00000000000000000E3); +#721=LINE('',#718,#720); +#722=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#723=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#724=VECTOR('',#723,1.00000000000000000E3); +#725=LINE('',#722,#724); +#726=DEFINITIONAL_REPRESENTATION('',(#725),#1); +#727=PCURVE('',#31,#726); +#728=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#729=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#730=VECTOR('',#729,1.00000000000000000E3); +#731=LINE('',#728,#730); +#732=DEFINITIONAL_REPRESENTATION('',(#731),#1); +#733=PCURVE('',#36,#732); +#734=SURFACE_CURVE('',#721,(#727,#733),.CURVE_3D.); +#735=EDGE_CURVE('',#317,#323,#734,.T.); +#736=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#737=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#738=VECTOR('',#737,1.00000000000000000E3); +#739=LINE('',#736,#738); +#740=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#741=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#742=VECTOR('',#741,1.00000000000000000E3); +#743=LINE('',#740,#742); +#744=DEFINITIONAL_REPRESENTATION('',(#743),#1); +#745=PCURVE('',#36,#744); +#746=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#747=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#748=VECTOR('',#747,1.00000000000000000E3); +#749=LINE('',#746,#748); +#750=DEFINITIONAL_REPRESENTATION('',(#749),#1); +#751=PCURVE('',#136,#750); +#752=SURFACE_CURVE('',#739,(#745,#751),.CURVE_3D.); +#753=EDGE_CURVE('',#321,#327,#752,.T.); +#754=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#755=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#756=VECTOR('',#755,1.00000000000000000E3); +#757=LINE('',#754,#756); +#758=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#759=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#760=VECTOR('',#759,1.00000000000000000E3); +#761=LINE('',#758,#760); +#762=DEFINITIONAL_REPRESENTATION('',(#761),#1); +#763=PCURVE('',#36,#762); +#764=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#765=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#766=VECTOR('',#765,1.00000000000000000E3); +#767=LINE('',#764,#766); +#768=DEFINITIONAL_REPRESENTATION('',(#767),#1); +#769=PCURVE('',#41,#768); +#770=SURFACE_CURVE('',#757,(#763,#769),.CURVE_3D.); +#771=EDGE_CURVE('',#323,#327,#770,.T.); +#772=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#773=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#774=VECTOR('',#773,1.00000000000000000E3); +#775=LINE('',#772,#774); +#776=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#777=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#778=VECTOR('',#777,1.00000000000000000E3); +#779=LINE('',#776,#778); +#780=DEFINITIONAL_REPRESENTATION('',(#779),#1); +#781=PCURVE('',#41,#780); +#782=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#783=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#784=VECTOR('',#783,1.00000000000000000E3); +#785=LINE('',#782,#784); +#786=DEFINITIONAL_REPRESENTATION('',(#785),#1); +#787=PCURVE('',#141,#786); +#788=SURFACE_CURVE('',#775,(#781,#787),.CURVE_3D.); +#789=EDGE_CURVE('',#327,#329,#788,.T.); +#790=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-1.00000000000000000E3)); +#791=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#792=VECTOR('',#791,1.00000000000000000E3); +#793=LINE('',#790,#792); +#794=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#795=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#796=VECTOR('',#795,1.00000000000000000E3); +#797=LINE('',#794,#796); +#798=DEFINITIONAL_REPRESENTATION('',(#797),#1); +#799=PCURVE('',#41,#798); +#800=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#801=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#802=VECTOR('',#801,1.00000000000000000E3); +#803=LINE('',#800,#802); +#804=DEFINITIONAL_REPRESENTATION('',(#803),#1); +#805=PCURVE('',#71,#804); +#806=SURFACE_CURVE('',#793,(#799,#805),.CURVE_3D.); +#807=EDGE_CURVE('',#325,#329,#806,.T.); +#808=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,-1.00000000000000000E3)); +#809=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#810=VECTOR('',#809,1.00000000000000000E3); +#811=LINE('',#808,#810); +#812=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#813=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#814=VECTOR('',#813,1.00000000000000000E3); +#815=LINE('',#812,#814); +#816=DEFINITIONAL_REPRESENTATION('',(#815),#1); +#817=PCURVE('',#46,#816); +#818=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#819=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#820=VECTOR('',#819,1.00000000000000000E3); +#821=LINE('',#818,#820); +#822=DEFINITIONAL_REPRESENTATION('',(#821),#1); +#823=PCURVE('',#56,#822); +#824=SURFACE_CURVE('',#811,(#817,#823),.CURVE_3D.); +#825=EDGE_CURVE('',#305,#331,#824,.T.); +#826=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E3)); +#827=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#828=VECTOR('',#827,1.00000000000000000E3); +#829=LINE('',#826,#828); +#830=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#831=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#832=VECTOR('',#831,1.00000000000000000E3); +#833=LINE('',#830,#832); +#834=DEFINITIONAL_REPRESENTATION('',(#833),#1); +#835=PCURVE('',#46,#834); +#836=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#837=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#838=VECTOR('',#837,1.00000000000000000E3); +#839=LINE('',#836,#838); +#840=DEFINITIONAL_REPRESENTATION('',(#839),#1); +#841=PCURVE('',#76,#840); +#842=SURFACE_CURVE('',#829,(#835,#841),.CURVE_3D.); +#843=EDGE_CURVE('',#333,#331,#842,.T.); +#844=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E3)); +#845=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#846=VECTOR('',#845,1.00000000000000000E3); +#847=LINE('',#844,#846); +#848=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#849=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#850=VECTOR('',#849,1.00000000000000000E3); +#851=LINE('',#848,#850); +#852=DEFINITIONAL_REPRESENTATION('',(#851),#1); +#853=PCURVE('',#46,#852); +#854=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#855=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#856=VECTOR('',#855,1.00000000000000000E3); +#857=LINE('',#854,#856); +#858=DEFINITIONAL_REPRESENTATION('',(#857),#1); +#859=PCURVE('',#51,#858); +#860=SURFACE_CURVE('',#847,(#853,#859),.CURVE_3D.); +#861=EDGE_CURVE('',#307,#333,#860,.T.); +#862=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E3)); +#863=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#864=VECTOR('',#863,1.00000000000000000E3); +#865=LINE('',#862,#864); +#866=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#867=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#868=VECTOR('',#867,1.00000000000000000E3); +#869=LINE('',#866,#868); +#870=DEFINITIONAL_REPRESENTATION('',(#869),#1); +#871=PCURVE('',#51,#870); +#872=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#873=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#874=VECTOR('',#873,1.00000000000000000E3); +#875=LINE('',#872,#874); +#876=DEFINITIONAL_REPRESENTATION('',(#875),#1); +#877=PCURVE('',#86,#876); +#878=SURFACE_CURVE('',#865,(#871,#877),.CURVE_3D.); +#879=EDGE_CURVE('',#333,#335,#878,.T.); +#880=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-1.00000000000000000E3)); +#881=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#882=VECTOR('',#881,1.00000000000000000E3); +#883=LINE('',#880,#882); +#884=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#885=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#886=VECTOR('',#885,1.00000000000000000E3); +#887=LINE('',#884,#886); +#888=DEFINITIONAL_REPRESENTATION('',(#887),#1); +#889=PCURVE('',#51,#888); +#890=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#891=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#892=VECTOR('',#891,1.00000000000000000E3); +#893=LINE('',#890,#892); +#894=DEFINITIONAL_REPRESENTATION('',(#893),#1); +#895=PCURVE('',#146,#894); +#896=SURFACE_CURVE('',#883,(#889,#895),.CURVE_3D.); +#897=EDGE_CURVE('',#315,#335,#896,.T.); +#898=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,-1.00000000000000000E3)); +#899=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#900=VECTOR('',#899,1.00000000000000000E3); +#901=LINE('',#898,#900); +#902=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#903=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#904=VECTOR('',#903,1.00000000000000000E3); +#905=LINE('',#902,#904); +#906=DEFINITIONAL_REPRESENTATION('',(#905),#1); +#907=PCURVE('',#56,#906); +#908=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#909=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#910=VECTOR('',#909,1.00000000000000000E3); +#911=LINE('',#908,#910); +#912=DEFINITIONAL_REPRESENTATION('',(#911),#1); +#913=PCURVE('',#66,#912); +#914=SURFACE_CURVE('',#901,(#907,#913),.CURVE_3D.); +#915=EDGE_CURVE('',#319,#337,#914,.T.); +#916=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,-2.00000000000000000E3)); +#917=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#918=VECTOR('',#917,1.00000000000000000E3); +#919=LINE('',#916,#918); +#920=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#921=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#922=VECTOR('',#921,1.00000000000000000E3); +#923=LINE('',#920,#922); +#924=DEFINITIONAL_REPRESENTATION('',(#923),#1); +#925=PCURVE('',#56,#924); +#926=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#927=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#928=VECTOR('',#927,1.00000000000000000E3); +#929=LINE('',#926,#928); +#930=DEFINITIONAL_REPRESENTATION('',(#929),#1); +#931=PCURVE('',#91,#930); +#932=SURFACE_CURVE('',#919,(#925,#931),.CURVE_3D.); +#933=EDGE_CURVE('',#331,#337,#932,.T.); +#934=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#935=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#936=VECTOR('',#935,1.00000000000000000E3); +#937=LINE('',#934,#936); +#938=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#939=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#940=VECTOR('',#939,1.00000000000000000E3); +#941=LINE('',#938,#940); +#942=DEFINITIONAL_REPRESENTATION('',(#941),#1); +#943=PCURVE('',#61,#942); +#944=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#945=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#946=VECTOR('',#945,1.00000000000000000E3); +#947=LINE('',#944,#946); +#948=DEFINITIONAL_REPRESENTATION('',(#947),#1); +#949=PCURVE('',#151,#948); +#950=SURFACE_CURVE('',#937,(#943,#949),.CURVE_3D.); +#951=EDGE_CURVE('',#339,#341,#950,.T.); +#952=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-2.00000000000000000E3)); +#953=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#954=VECTOR('',#953,1.00000000000000000E3); +#955=LINE('',#952,#954); +#956=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#957=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#958=VECTOR('',#957,1.00000000000000000E3); +#959=LINE('',#956,#958); +#960=DEFINITIONAL_REPRESENTATION('',(#959),#1); +#961=PCURVE('',#61,#960); +#962=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#963=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#964=VECTOR('',#963,1.00000000000000000E3); +#965=LINE('',#962,#964); +#966=DEFINITIONAL_REPRESENTATION('',(#965),#1); +#967=PCURVE('',#176,#966); +#968=SURFACE_CURVE('',#955,(#961,#967),.CURVE_3D.); +#969=EDGE_CURVE('',#341,#343,#968,.T.); +#970=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,-1.00000000000000000E3)); +#971=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#972=VECTOR('',#971,1.00000000000000000E3); +#973=LINE('',#970,#972); +#974=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#975=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#976=VECTOR('',#975,1.00000000000000000E3); +#977=LINE('',#974,#976); +#978=DEFINITIONAL_REPRESENTATION('',(#977),#1); +#979=PCURVE('',#61,#978); +#980=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#981=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#982=VECTOR('',#981,1.00000000000000000E3); +#983=LINE('',#980,#982); +#984=DEFINITIONAL_REPRESENTATION('',(#983),#1); +#985=PCURVE('',#156,#984); +#986=SURFACE_CURVE('',#973,(#979,#985),.CURVE_3D.); +#987=EDGE_CURVE('',#345,#343,#986,.T.); +#988=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#989=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#990=VECTOR('',#989,1.00000000000000000E3); +#991=LINE('',#988,#990); +#992=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#993=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#994=VECTOR('',#993,1.00000000000000000E3); +#995=LINE('',#992,#994); +#996=DEFINITIONAL_REPRESENTATION('',(#995),#1); +#997=PCURVE('',#61,#996); +#998=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#999=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1000=VECTOR('',#999,1.00000000000000000E3); +#1001=LINE('',#998,#1000); +#1002=DEFINITIONAL_REPRESENTATION('',(#1001),#1); +#1003=PCURVE('',#131,#1002); +#1004=SURFACE_CURVE('',#991,(#997,#1003),.CURVE_3D.); +#1005=EDGE_CURVE('',#339,#345,#1004,.T.); +#1006=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-1.00000000000000000E3)); +#1007=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1008=VECTOR('',#1007,1.00000000000000000E3); +#1009=LINE('',#1006,#1008); +#1010=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3)); +#1011=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1012=VECTOR('',#1011,1.00000000000000000E3); +#1013=LINE('',#1010,#1012); +#1014=DEFINITIONAL_REPRESENTATION('',(#1013),#1); +#1015=PCURVE('',#66,#1014); +#1016=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1017=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1018=VECTOR('',#1017,1.00000000000000000E3); +#1019=LINE('',#1016,#1018); +#1020=DEFINITIONAL_REPRESENTATION('',(#1019),#1); +#1021=PCURVE('',#71,#1020); +#1022=SURFACE_CURVE('',#1009,(#1015,#1021),.CURVE_3D.); +#1023=EDGE_CURVE('',#325,#347,#1022,.T.); +#1024=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,-2.00000000000000000E3)); +#1025=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1026=VECTOR('',#1025,1.00000000000000000E3); +#1027=LINE('',#1024,#1026); +#1028=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1029=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1030=VECTOR('',#1029,1.00000000000000000E3); +#1031=LINE('',#1028,#1030); +#1032=DEFINITIONAL_REPRESENTATION('',(#1031),#1); +#1033=PCURVE('',#66,#1032); +#1034=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1035=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1036=VECTOR('',#1035,1.00000000000000000E3); +#1037=LINE('',#1034,#1036); +#1038=DEFINITIONAL_REPRESENTATION('',(#1037),#1); +#1039=PCURVE('',#101,#1038); +#1040=SURFACE_CURVE('',#1027,(#1033,#1039),.CURVE_3D.); +#1041=EDGE_CURVE('',#337,#347,#1040,.T.); +#1042=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3,-1.00000000000000000E3)); +#1043=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1044=VECTOR('',#1043,1.00000000000000000E3); +#1045=LINE('',#1042,#1044); +#1046=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1047=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1048=VECTOR('',#1047,1.00000000000000000E3); +#1049=LINE('',#1046,#1048); +#1050=DEFINITIONAL_REPRESENTATION('',(#1049),#1); +#1051=PCURVE('',#71,#1050); +#1052=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1053=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1054=VECTOR('',#1053,1.00000000000000000E3); +#1055=LINE('',#1052,#1054); +#1056=DEFINITIONAL_REPRESENTATION('',(#1055),#1); +#1057=PCURVE('',#161,#1056); +#1058=SURFACE_CURVE('',#1045,(#1051,#1057),.CURVE_3D.); +#1059=EDGE_CURVE('',#329,#349,#1058,.T.); +#1060=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-2.00000000000000000E3)); +#1061=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1062=VECTOR('',#1061,1.00000000000000000E3); +#1063=LINE('',#1060,#1062); +#1064=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#1065=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1066=VECTOR('',#1065,1.00000000000000000E3); +#1067=LINE('',#1064,#1066); +#1068=DEFINITIONAL_REPRESENTATION('',(#1067),#1); +#1069=PCURVE('',#71,#1068); +#1070=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#1071=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1072=VECTOR('',#1071,1.00000000000000000E3); +#1073=LINE('',#1070,#1072); +#1074=DEFINITIONAL_REPRESENTATION('',(#1073),#1); +#1075=PCURVE('',#111,#1074); +#1076=SURFACE_CURVE('',#1063,(#1069,#1075),.CURVE_3D.); +#1077=EDGE_CURVE('',#347,#349,#1076,.T.); +#1078=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,-2.00000000000000000E3)); +#1079=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1080=VECTOR('',#1079,1.00000000000000000E3); +#1081=LINE('',#1078,#1080); +#1082=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1083=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1084=VECTOR('',#1083,1.00000000000000000E3); +#1085=LINE('',#1082,#1084); +#1086=DEFINITIONAL_REPRESENTATION('',(#1085),#1); +#1087=PCURVE('',#76,#1086); +#1088=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1089=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1090=VECTOR('',#1089,1.00000000000000000E3); +#1091=LINE('',#1088,#1090); +#1092=DEFINITIONAL_REPRESENTATION('',(#1091),#1); +#1093=PCURVE('',#91,#1092); +#1094=SURFACE_CURVE('',#1081,(#1087,#1093),.CURVE_3D.); +#1095=EDGE_CURVE('',#331,#351,#1094,.T.); +#1096=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#1097=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1098=VECTOR('',#1097,1.00000000000000000E3); +#1099=LINE('',#1096,#1098); +#1100=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#1101=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1102=VECTOR('',#1101,1.00000000000000000E3); +#1103=LINE('',#1100,#1102); +#1104=DEFINITIONAL_REPRESENTATION('',(#1103),#1); +#1105=PCURVE('',#76,#1104); +#1106=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1107=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1108=VECTOR('',#1107,1.00000000000000000E3); +#1109=LINE('',#1106,#1108); +#1110=DEFINITIONAL_REPRESENTATION('',(#1109),#1); +#1111=PCURVE('',#81,#1110); +#1112=SURFACE_CURVE('',#1099,(#1105,#1111),.CURVE_3D.); +#1113=EDGE_CURVE('',#353,#351,#1112,.T.); +#1114=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-2.00000000000000000E3)); +#1115=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1116=VECTOR('',#1115,1.00000000000000000E3); +#1117=LINE('',#1114,#1116); +#1118=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#1119=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1120=VECTOR('',#1119,1.00000000000000000E3); +#1121=LINE('',#1118,#1120); +#1122=DEFINITIONAL_REPRESENTATION('',(#1121),#1); +#1123=PCURVE('',#76,#1122); +#1124=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#1125=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1126=VECTOR('',#1125,1.00000000000000000E3); +#1127=LINE('',#1124,#1126); +#1128=DEFINITIONAL_REPRESENTATION('',(#1127),#1); +#1129=PCURVE('',#86,#1128); +#1130=SURFACE_CURVE('',#1117,(#1123,#1129),.CURVE_3D.); +#1131=EDGE_CURVE('',#333,#353,#1130,.T.); +#1132=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,-3.00000000000000000E3)); +#1133=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1134=VECTOR('',#1133,1.00000000000000000E3); +#1135=LINE('',#1132,#1134); +#1136=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1137=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1138=VECTOR('',#1137,1.00000000000000000E3); +#1139=LINE('',#1136,#1138); +#1140=DEFINITIONAL_REPRESENTATION('',(#1139),#1); +#1141=PCURVE('',#81,#1140); +#1142=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1143=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1144=VECTOR('',#1143,1.00000000000000000E3); +#1145=LINE('',#1142,#1144); +#1146=DEFINITIONAL_REPRESENTATION('',(#1145),#1); +#1147=PCURVE('',#96,#1146); +#1148=SURFACE_CURVE('',#1135,(#1141,#1147),.CURVE_3D.); +#1149=EDGE_CURVE('',#351,#355,#1148,.T.); +#1150=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#1151=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1152=VECTOR('',#1151,1.00000000000000000E3); +#1153=LINE('',#1150,#1152); +#1154=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1155=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1156=VECTOR('',#1155,1.00000000000000000E3); +#1157=LINE('',#1154,#1156); +#1158=DEFINITIONAL_REPRESENTATION('',(#1157),#1); +#1159=PCURVE('',#81,#1158); +#1160=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1161=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1162=VECTOR('',#1161,1.00000000000000000E3); +#1163=LINE('',#1160,#1162); +#1164=DEFINITIONAL_REPRESENTATION('',(#1163),#1); +#1165=PCURVE('',#166,#1164); +#1166=SURFACE_CURVE('',#1153,(#1159,#1165),.CURVE_3D.); +#1167=EDGE_CURVE('',#357,#355,#1166,.T.); +#1168=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#1169=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1170=VECTOR('',#1169,1.00000000000000000E3); +#1171=LINE('',#1168,#1170); +#1172=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1173=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1174=VECTOR('',#1173,1.00000000000000000E3); +#1175=LINE('',#1172,#1174); +#1176=DEFINITIONAL_REPRESENTATION('',(#1175),#1); +#1177=PCURVE('',#81,#1176); +#1178=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#1179=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1180=VECTOR('',#1179,1.00000000000000000E3); +#1181=LINE('',#1178,#1180); +#1182=DEFINITIONAL_REPRESENTATION('',(#1181),#1); +#1183=PCURVE('',#86,#1182); +#1184=SURFACE_CURVE('',#1171,(#1177,#1183),.CURVE_3D.); +#1185=EDGE_CURVE('',#353,#357,#1184,.T.); +#1186=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#1187=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1188=VECTOR('',#1187,1.00000000000000000E3); +#1189=LINE('',#1186,#1188); +#1190=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1191=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1192=VECTOR('',#1191,1.00000000000000000E3); +#1193=LINE('',#1190,#1192); +#1194=DEFINITIONAL_REPRESENTATION('',(#1193),#1); +#1195=PCURVE('',#86,#1194); +#1196=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1197=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1198=VECTOR('',#1197,1.00000000000000000E3); +#1199=LINE('',#1196,#1198); +#1200=DEFINITIONAL_REPRESENTATION('',(#1199),#1); +#1201=PCURVE('',#171,#1200); +#1202=SURFACE_CURVE('',#1189,(#1195,#1201),.CURVE_3D.); +#1203=EDGE_CURVE('',#335,#357,#1202,.T.); +#1204=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,-2.00000000000000000E3)); +#1205=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1206=VECTOR('',#1205,1.00000000000000000E3); +#1207=LINE('',#1204,#1206); +#1208=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1209=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1210=VECTOR('',#1209,1.00000000000000000E3); +#1211=LINE('',#1208,#1210); +#1212=DEFINITIONAL_REPRESENTATION('',(#1211),#1); +#1213=PCURVE('',#91,#1212); +#1214=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1215=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1216=VECTOR('',#1215,1.00000000000000000E3); +#1217=LINE('',#1214,#1216); +#1218=DEFINITIONAL_REPRESENTATION('',(#1217),#1); +#1219=PCURVE('',#101,#1218); +#1220=SURFACE_CURVE('',#1207,(#1213,#1219),.CURVE_3D.); +#1221=EDGE_CURVE('',#337,#359,#1220,.T.); +#1222=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,-3.00000000000000000E3)); +#1223=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1224=VECTOR('',#1223,1.00000000000000000E3); +#1225=LINE('',#1222,#1224); +#1226=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3)); +#1227=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1228=VECTOR('',#1227,1.00000000000000000E3); +#1229=LINE('',#1226,#1228); +#1230=DEFINITIONAL_REPRESENTATION('',(#1229),#1); +#1231=PCURVE('',#91,#1230); +#1232=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1233=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1234=VECTOR('',#1233,1.00000000000000000E3); +#1235=LINE('',#1232,#1234); +#1236=DEFINITIONAL_REPRESENTATION('',(#1235),#1); +#1237=PCURVE('',#96,#1236); +#1238=SURFACE_CURVE('',#1225,(#1231,#1237),.CURVE_3D.); +#1239=EDGE_CURVE('',#351,#359,#1238,.T.); +#1240=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,-3.00000000000000000E3)); +#1241=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1242=VECTOR('',#1241,1.00000000000000000E3); +#1243=LINE('',#1240,#1242); +#1244=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#1245=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1246=VECTOR('',#1245,1.00000000000000000E3); +#1247=LINE('',#1244,#1246); +#1248=DEFINITIONAL_REPRESENTATION('',(#1247),#1); +#1249=PCURVE('',#96,#1248); +#1250=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#1251=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1252=VECTOR('',#1251,1.00000000000000000E3); +#1253=LINE('',#1250,#1252); +#1254=DEFINITIONAL_REPRESENTATION('',(#1253),#1); +#1255=PCURVE('',#106,#1254); +#1256=SURFACE_CURVE('',#1243,(#1249,#1255),.CURVE_3D.); +#1257=EDGE_CURVE('',#359,#361,#1256,.T.); +#1258=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-3.00000000000000000E3)); +#1259=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1260=VECTOR('',#1259,1.00000000000000000E3); +#1261=LINE('',#1258,#1260); +#1262=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1263=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1264=VECTOR('',#1263,1.00000000000000000E3); +#1265=LINE('',#1262,#1264); +#1266=DEFINITIONAL_REPRESENTATION('',(#1265),#1); +#1267=PCURVE('',#96,#1266); +#1268=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1269=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1270=VECTOR('',#1269,1.00000000000000000E3); +#1271=LINE('',#1268,#1270); +#1272=DEFINITIONAL_REPRESENTATION('',(#1271),#1); +#1273=PCURVE('',#181,#1272); +#1274=SURFACE_CURVE('',#1261,(#1267,#1273),.CURVE_3D.); +#1275=EDGE_CURVE('',#355,#361,#1274,.T.); +#1276=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-2.00000000000000000E3)); +#1277=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1278=VECTOR('',#1277,1.00000000000000000E3); +#1279=LINE('',#1276,#1278); +#1280=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3)); +#1281=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1282=VECTOR('',#1281,1.00000000000000000E3); +#1283=LINE('',#1280,#1282); +#1284=DEFINITIONAL_REPRESENTATION('',(#1283),#1); +#1285=PCURVE('',#101,#1284); +#1286=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#1287=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1288=VECTOR('',#1287,1.00000000000000000E3); +#1289=LINE('',#1286,#1288); +#1290=DEFINITIONAL_REPRESENTATION('',(#1289),#1); +#1291=PCURVE('',#111,#1290); +#1292=SURFACE_CURVE('',#1279,(#1285,#1291),.CURVE_3D.); +#1293=EDGE_CURVE('',#347,#363,#1292,.T.); +#1294=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,-3.00000000000000000E3)); +#1295=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1296=VECTOR('',#1295,1.00000000000000000E3); +#1297=LINE('',#1294,#1296); +#1298=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3)); +#1299=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1300=VECTOR('',#1299,1.00000000000000000E3); +#1301=LINE('',#1298,#1300); +#1302=DEFINITIONAL_REPRESENTATION('',(#1301),#1); +#1303=PCURVE('',#101,#1302); +#1304=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#1305=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1306=VECTOR('',#1305,1.00000000000000000E3); +#1307=LINE('',#1304,#1306); +#1308=DEFINITIONAL_REPRESENTATION('',(#1307),#1); +#1309=PCURVE('',#106,#1308); +#1310=SURFACE_CURVE('',#1297,(#1303,#1309),.CURVE_3D.); +#1311=EDGE_CURVE('',#359,#363,#1310,.T.); +#1312=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-3.00000000000000000E3)); +#1313=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1314=VECTOR('',#1313,1.00000000000000000E3); +#1315=LINE('',#1312,#1314); +#1316=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#1317=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1318=VECTOR('',#1317,1.00000000000000000E3); +#1319=LINE('',#1316,#1318); +#1320=DEFINITIONAL_REPRESENTATION('',(#1319),#1); +#1321=PCURVE('',#106,#1320); +#1322=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#1323=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1324=VECTOR('',#1323,1.00000000000000000E3); +#1325=LINE('',#1322,#1324); +#1326=DEFINITIONAL_REPRESENTATION('',(#1325),#1); +#1327=PCURVE('',#111,#1326); +#1328=SURFACE_CURVE('',#1315,(#1321,#1327),.CURVE_3D.); +#1329=EDGE_CURVE('',#363,#365,#1328,.T.); +#1330=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,-3.00000000000000000E3)); +#1331=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1332=VECTOR('',#1331,1.00000000000000000E3); +#1333=LINE('',#1330,#1332); +#1334=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1335=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1336=VECTOR('',#1335,1.00000000000000000E3); +#1337=LINE('',#1334,#1336); +#1338=DEFINITIONAL_REPRESENTATION('',(#1337),#1); +#1339=PCURVE('',#106,#1338); +#1340=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1341=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1342=VECTOR('',#1341,1.00000000000000000E3); +#1343=LINE('',#1340,#1342); +#1344=DEFINITIONAL_REPRESENTATION('',(#1343),#1); +#1345=PCURVE('',#186,#1344); +#1346=SURFACE_CURVE('',#1333,(#1339,#1345),.CURVE_3D.); +#1347=EDGE_CURVE('',#361,#365,#1346,.T.); +#1348=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3,-2.00000000000000000E3)); +#1349=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1350=VECTOR('',#1349,1.00000000000000000E3); +#1351=LINE('',#1348,#1350); +#1352=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1353=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1354=VECTOR('',#1353,1.00000000000000000E3); +#1355=LINE('',#1352,#1354); +#1356=DEFINITIONAL_REPRESENTATION('',(#1355),#1); +#1357=PCURVE('',#111,#1356); +#1358=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1359=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1360=VECTOR('',#1359,1.00000000000000000E3); +#1361=LINE('',#1358,#1360); +#1362=DEFINITIONAL_REPRESENTATION('',(#1361),#1); +#1363=PCURVE('',#191,#1362); +#1364=SURFACE_CURVE('',#1351,(#1357,#1363),.CURVE_3D.); +#1365=EDGE_CURVE('',#349,#365,#1364,.T.); +#1366=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#1367=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1368=VECTOR('',#1367,1.00000000000000000E3); +#1369=LINE('',#1366,#1368); +#1370=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#1371=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1372=VECTOR('',#1371,1.00000000000000000E3); +#1373=LINE('',#1370,#1372); +#1374=DEFINITIONAL_REPRESENTATION('',(#1373),#1); +#1375=PCURVE('',#116,#1374); +#1376=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#1377=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1378=VECTOR('',#1377,1.00000000000000000E3); +#1379=LINE('',#1376,#1378); +#1380=DEFINITIONAL_REPRESENTATION('',(#1379),#1); +#1381=PCURVE('',#201,#1380); +#1382=SURFACE_CURVE('',#1369,(#1375,#1381),.CURVE_3D.); +#1383=EDGE_CURVE('',#367,#369,#1382,.T.); +#1384=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,0.00000000000000000E0)); +#1385=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1386=VECTOR('',#1385,1.00000000000000000E3); +#1387=LINE('',#1384,#1386); +#1388=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1389=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1390=VECTOR('',#1389,1.00000000000000000E3); +#1391=LINE('',#1388,#1390); +#1392=DEFINITIONAL_REPRESENTATION('',(#1391),#1); +#1393=PCURVE('',#116,#1392); +#1394=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1395=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1396=VECTOR('',#1395,1.00000000000000000E3); +#1397=LINE('',#1394,#1396); +#1398=DEFINITIONAL_REPRESENTATION('',(#1397),#1); +#1399=PCURVE('',#126,#1398); +#1400=SURFACE_CURVE('',#1387,(#1393,#1399),.CURVE_3D.); +#1401=EDGE_CURVE('',#313,#369,#1400,.T.); +#1402=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#1403=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1404=VECTOR('',#1403,1.00000000000000000E3); +#1405=LINE('',#1402,#1404); +#1406=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1407=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1408=VECTOR('',#1407,1.00000000000000000E3); +#1409=LINE('',#1406,#1408); +#1410=DEFINITIONAL_REPRESENTATION('',(#1409),#1); +#1411=PCURVE('',#116,#1410); +#1412=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1413=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1414=VECTOR('',#1413,1.00000000000000000E3); +#1415=LINE('',#1412,#1414); +#1416=DEFINITIONAL_REPRESENTATION('',(#1415),#1); +#1417=PCURVE('',#121,#1416); +#1418=SURFACE_CURVE('',#1405,(#1411,#1417),.CURVE_3D.); +#1419=EDGE_CURVE('',#311,#367,#1418,.T.); +#1420=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-1.00000000000000000E3)); +#1421=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1422=VECTOR('',#1421,1.00000000000000000E3); +#1423=LINE('',#1420,#1422); +#1424=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1425=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1426=VECTOR('',#1425,1.00000000000000000E3); +#1427=LINE('',#1424,#1426); +#1428=DEFINITIONAL_REPRESENTATION('',(#1427),#1); +#1429=PCURVE('',#121,#1428); +#1430=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1431=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1432=VECTOR('',#1431,1.00000000000000000E3); +#1433=LINE('',#1430,#1432); +#1434=DEFINITIONAL_REPRESENTATION('',(#1433),#1); +#1435=PCURVE('',#146,#1434); +#1436=SURFACE_CURVE('',#1423,(#1429,#1435),.CURVE_3D.); +#1437=EDGE_CURVE('',#315,#371,#1436,.T.); +#1438=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#1439=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1440=VECTOR('',#1439,1.00000000000000000E3); +#1441=LINE('',#1438,#1440); +#1442=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#1443=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1444=VECTOR('',#1443,1.00000000000000000E3); +#1445=LINE('',#1442,#1444); +#1446=DEFINITIONAL_REPRESENTATION('',(#1445),#1); +#1447=PCURVE('',#121,#1446); +#1448=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#1449=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1450=VECTOR('',#1449,1.00000000000000000E3); +#1451=LINE('',#1448,#1450); +#1452=DEFINITIONAL_REPRESENTATION('',(#1451),#1); +#1453=PCURVE('',#206,#1452); +#1454=SURFACE_CURVE('',#1441,(#1447,#1453),.CURVE_3D.); +#1455=EDGE_CURVE('',#367,#371,#1454,.T.); +#1456=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,0.00000000000000000E0)); +#1457=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1458=VECTOR('',#1457,1.00000000000000000E3); +#1459=LINE('',#1456,#1458); +#1460=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1461=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1462=VECTOR('',#1461,1.00000000000000000E3); +#1463=LINE('',#1460,#1462); +#1464=DEFINITIONAL_REPRESENTATION('',(#1463),#1); +#1465=PCURVE('',#126,#1464); +#1466=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1467=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1468=VECTOR('',#1467,1.00000000000000000E3); +#1469=LINE('',#1466,#1468); +#1470=DEFINITIONAL_REPRESENTATION('',(#1469),#1); +#1471=PCURVE('',#216,#1470); +#1472=SURFACE_CURVE('',#1459,(#1465,#1471),.CURVE_3D.); +#1473=EDGE_CURVE('',#369,#373,#1472,.T.); +#1474=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#1475=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1476=VECTOR('',#1475,1.00000000000000000E3); +#1477=LINE('',#1474,#1476); +#1478=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1479=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1480=VECTOR('',#1479,1.00000000000000000E3); +#1481=LINE('',#1478,#1480); +#1482=DEFINITIONAL_REPRESENTATION('',(#1481),#1); +#1483=PCURVE('',#126,#1482); +#1484=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1485=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1486=VECTOR('',#1485,1.00000000000000000E3); +#1487=LINE('',#1484,#1486); +#1488=DEFINITIONAL_REPRESENTATION('',(#1487),#1); +#1489=PCURVE('',#136,#1488); +#1490=SURFACE_CURVE('',#1477,(#1483,#1489),.CURVE_3D.); +#1491=EDGE_CURVE('',#321,#373,#1490,.T.); +#1492=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,-1.00000000000000000E3)); +#1493=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1494=VECTOR('',#1493,1.00000000000000000E3); +#1495=LINE('',#1492,#1494); +#1496=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1497=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1498=VECTOR('',#1497,1.00000000000000000E3); +#1499=LINE('',#1496,#1498); +#1500=DEFINITIONAL_REPRESENTATION('',(#1499),#1); +#1501=PCURVE('',#131,#1500); +#1502=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1503=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1504=VECTOR('',#1503,1.00000000000000000E3); +#1505=LINE('',#1502,#1504); +#1506=DEFINITIONAL_REPRESENTATION('',(#1505),#1); +#1507=PCURVE('',#156,#1506); +#1508=SURFACE_CURVE('',#1495,(#1501,#1507),.CURVE_3D.); +#1509=EDGE_CURVE('',#345,#375,#1508,.T.); +#1510=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#1511=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1512=VECTOR('',#1511,1.00000000000000000E3); +#1513=LINE('',#1510,#1512); +#1514=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1515=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1516=VECTOR('',#1515,1.00000000000000000E3); +#1517=LINE('',#1514,#1516); +#1518=DEFINITIONAL_REPRESENTATION('',(#1517),#1); +#1519=PCURVE('',#131,#1518); +#1520=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1521=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1522=VECTOR('',#1521,1.00000000000000000E3); +#1523=LINE('',#1520,#1522); +#1524=DEFINITIONAL_REPRESENTATION('',(#1523),#1); +#1525=PCURVE('',#246,#1524); +#1526=SURFACE_CURVE('',#1513,(#1519,#1525),.CURVE_3D.); +#1527=EDGE_CURVE('',#377,#375,#1526,.T.); +#1528=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#1529=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1530=VECTOR('',#1529,1.00000000000000000E3); +#1531=LINE('',#1528,#1530); +#1532=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1533=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1534=VECTOR('',#1533,1.00000000000000000E3); +#1535=LINE('',#1532,#1534); +#1536=DEFINITIONAL_REPRESENTATION('',(#1535),#1); +#1537=PCURVE('',#131,#1536); +#1538=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1539=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1540=VECTOR('',#1539,1.00000000000000000E3); +#1541=LINE('',#1538,#1540); +#1542=DEFINITIONAL_REPRESENTATION('',(#1541),#1); +#1543=PCURVE('',#151,#1542); +#1544=SURFACE_CURVE('',#1531,(#1537,#1543),.CURVE_3D.); +#1545=EDGE_CURVE('',#339,#377,#1544,.T.); +#1546=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#1547=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1548=VECTOR('',#1547,1.00000000000000000E3); +#1549=LINE('',#1546,#1548); +#1550=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1551=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1552=VECTOR('',#1551,1.00000000000000000E3); +#1553=LINE('',#1550,#1552); +#1554=DEFINITIONAL_REPRESENTATION('',(#1553),#1); +#1555=PCURVE('',#136,#1554); +#1556=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1557=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1558=VECTOR('',#1557,1.00000000000000000E3); +#1559=LINE('',#1556,#1558); +#1560=DEFINITIONAL_REPRESENTATION('',(#1559),#1); +#1561=PCURVE('',#226,#1560); +#1562=SURFACE_CURVE('',#1549,(#1555,#1561),.CURVE_3D.); +#1563=EDGE_CURVE('',#373,#379,#1562,.T.); +#1564=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#1565=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1566=VECTOR('',#1565,1.00000000000000000E3); +#1567=LINE('',#1564,#1566); +#1568=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3)); +#1569=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1570=VECTOR('',#1569,1.00000000000000000E3); +#1571=LINE('',#1568,#1570); +#1572=DEFINITIONAL_REPRESENTATION('',(#1571),#1); +#1573=PCURVE('',#136,#1572); +#1574=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1575=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1576=VECTOR('',#1575,1.00000000000000000E3); +#1577=LINE('',#1574,#1576); +#1578=DEFINITIONAL_REPRESENTATION('',(#1577),#1); +#1579=PCURVE('',#141,#1578); +#1580=SURFACE_CURVE('',#1567,(#1573,#1579),.CURVE_3D.); +#1581=EDGE_CURVE('',#327,#379,#1580,.T.); +#1582=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#1583=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1584=VECTOR('',#1583,1.00000000000000000E3); +#1585=LINE('',#1582,#1584); +#1586=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#1587=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1588=VECTOR('',#1587,1.00000000000000000E3); +#1589=LINE('',#1586,#1588); +#1590=DEFINITIONAL_REPRESENTATION('',(#1589),#1); +#1591=PCURVE('',#141,#1590); +#1592=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#1593=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1594=VECTOR('',#1593,1.00000000000000000E3); +#1595=LINE('',#1592,#1594); +#1596=DEFINITIONAL_REPRESENTATION('',(#1595),#1); +#1597=PCURVE('',#231,#1596); +#1598=SURFACE_CURVE('',#1585,(#1591,#1597),.CURVE_3D.); +#1599=EDGE_CURVE('',#379,#381,#1598,.T.); +#1600=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3,-1.00000000000000000E3)); +#1601=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1602=VECTOR('',#1601,1.00000000000000000E3); +#1603=LINE('',#1600,#1602); +#1604=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1605=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1606=VECTOR('',#1605,1.00000000000000000E3); +#1607=LINE('',#1604,#1606); +#1608=DEFINITIONAL_REPRESENTATION('',(#1607),#1); +#1609=PCURVE('',#141,#1608); +#1610=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1611=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1612=VECTOR('',#1611,1.00000000000000000E3); +#1613=LINE('',#1610,#1612); +#1614=DEFINITIONAL_REPRESENTATION('',(#1613),#1); +#1615=PCURVE('',#161,#1614); +#1616=SURFACE_CURVE('',#1603,(#1609,#1615),.CURVE_3D.); +#1617=EDGE_CURVE('',#329,#381,#1616,.T.); +#1618=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#1619=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1620=VECTOR('',#1619,1.00000000000000000E3); +#1621=LINE('',#1618,#1620); +#1622=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1623=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1624=VECTOR('',#1623,1.00000000000000000E3); +#1625=LINE('',#1622,#1624); +#1626=DEFINITIONAL_REPRESENTATION('',(#1625),#1); +#1627=PCURVE('',#146,#1626); +#1628=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1629=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1630=VECTOR('',#1629,1.00000000000000000E3); +#1631=LINE('',#1628,#1630); +#1632=DEFINITIONAL_REPRESENTATION('',(#1631),#1); +#1633=PCURVE('',#171,#1632); +#1634=SURFACE_CURVE('',#1621,(#1627,#1633),.CURVE_3D.); +#1635=EDGE_CURVE('',#335,#383,#1634,.T.); +#1636=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-1.00000000000000000E3)); +#1637=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1638=VECTOR('',#1637,1.00000000000000000E3); +#1639=LINE('',#1636,#1638); +#1640=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1641=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1642=VECTOR('',#1641,1.00000000000000000E3); +#1643=LINE('',#1640,#1642); +#1644=DEFINITIONAL_REPRESENTATION('',(#1643),#1); +#1645=PCURVE('',#146,#1644); +#1646=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1647=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1648=VECTOR('',#1647,1.00000000000000000E3); +#1649=LINE('',#1646,#1648); +#1650=DEFINITIONAL_REPRESENTATION('',(#1649),#1); +#1651=PCURVE('',#241,#1650); +#1652=SURFACE_CURVE('',#1639,(#1645,#1651),.CURVE_3D.); +#1653=EDGE_CURVE('',#371,#383,#1652,.T.); +#1654=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#1655=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1656=VECTOR('',#1655,1.00000000000000000E3); +#1657=LINE('',#1654,#1656); +#1658=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1659=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1660=VECTOR('',#1659,1.00000000000000000E3); +#1661=LINE('',#1658,#1660); +#1662=DEFINITIONAL_REPRESENTATION('',(#1661),#1); +#1663=PCURVE('',#151,#1662); +#1664=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1665=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1666=VECTOR('',#1665,1.00000000000000000E3); +#1667=LINE('',#1664,#1666); +#1668=DEFINITIONAL_REPRESENTATION('',(#1667),#1); +#1669=PCURVE('',#246,#1668); +#1670=SURFACE_CURVE('',#1657,(#1663,#1669),.CURVE_3D.); +#1671=EDGE_CURVE('',#377,#385,#1670,.T.); +#1672=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-2.00000000000000000E3)); +#1673=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1674=VECTOR('',#1673,1.00000000000000000E3); +#1675=LINE('',#1672,#1674); +#1676=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1677=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1678=VECTOR('',#1677,1.00000000000000000E3); +#1679=LINE('',#1676,#1678); +#1680=DEFINITIONAL_REPRESENTATION('',(#1679),#1); +#1681=PCURVE('',#151,#1680); +#1682=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1683=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1684=VECTOR('',#1683,1.00000000000000000E3); +#1685=LINE('',#1682,#1684); +#1686=DEFINITIONAL_REPRESENTATION('',(#1685),#1); +#1687=PCURVE('',#176,#1686); +#1688=SURFACE_CURVE('',#1675,(#1681,#1687),.CURVE_3D.); +#1689=EDGE_CURVE('',#341,#385,#1688,.T.); +#1690=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#1691=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1692=VECTOR('',#1691,1.00000000000000000E3); +#1693=LINE('',#1690,#1692); +#1694=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1695=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1696=VECTOR('',#1695,1.00000000000000000E3); +#1697=LINE('',#1694,#1696); +#1698=DEFINITIONAL_REPRESENTATION('',(#1697),#1); +#1699=PCURVE('',#156,#1698); +#1700=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1701=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1702=VECTOR('',#1701,1.00000000000000000E3); +#1703=LINE('',#1700,#1702); +#1704=DEFINITIONAL_REPRESENTATION('',(#1703),#1); +#1705=PCURVE('',#176,#1704); +#1706=SURFACE_CURVE('',#1693,(#1699,#1705),.CURVE_3D.); +#1707=EDGE_CURVE('',#343,#387,#1706,.T.); +#1708=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,-1.00000000000000000E3)); +#1709=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1710=VECTOR('',#1709,1.00000000000000000E3); +#1711=LINE('',#1708,#1710); +#1712=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1713=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1714=VECTOR('',#1713,1.00000000000000000E3); +#1715=LINE('',#1712,#1714); +#1716=DEFINITIONAL_REPRESENTATION('',(#1715),#1); +#1717=PCURVE('',#156,#1716); +#1718=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1719=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1720=VECTOR('',#1719,1.00000000000000000E3); +#1721=LINE('',#1718,#1720); +#1722=DEFINITIONAL_REPRESENTATION('',(#1721),#1); +#1723=PCURVE('',#246,#1722); +#1724=SURFACE_CURVE('',#1711,(#1717,#1723),.CURVE_3D.); +#1725=EDGE_CURVE('',#375,#387,#1724,.T.); +#1726=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,-1.00000000000000000E3)); +#1727=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1728=VECTOR('',#1727,1.00000000000000000E3); +#1729=LINE('',#1726,#1728); +#1730=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1731=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1732=VECTOR('',#1731,1.00000000000000000E3); +#1733=LINE('',#1730,#1732); +#1734=DEFINITIONAL_REPRESENTATION('',(#1733),#1); +#1735=PCURVE('',#161,#1734); +#1736=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1737=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1738=VECTOR('',#1737,1.00000000000000000E3); +#1739=LINE('',#1736,#1738); +#1740=DEFINITIONAL_REPRESENTATION('',(#1739),#1); +#1741=PCURVE('',#261,#1740); +#1742=SURFACE_CURVE('',#1729,(#1735,#1741),.CURVE_3D.); +#1743=EDGE_CURVE('',#381,#389,#1742,.T.); +#1744=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3,-2.00000000000000000E3)); +#1745=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1746=VECTOR('',#1745,1.00000000000000000E3); +#1747=LINE('',#1744,#1746); +#1748=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1749=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1750=VECTOR('',#1749,1.00000000000000000E3); +#1751=LINE('',#1748,#1750); +#1752=DEFINITIONAL_REPRESENTATION('',(#1751),#1); +#1753=PCURVE('',#161,#1752); +#1754=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1755=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1756=VECTOR('',#1755,1.00000000000000000E3); +#1757=LINE('',#1754,#1756); +#1758=DEFINITIONAL_REPRESENTATION('',(#1757),#1); +#1759=PCURVE('',#191,#1758); +#1760=SURFACE_CURVE('',#1747,(#1753,#1759),.CURVE_3D.); +#1761=EDGE_CURVE('',#349,#389,#1760,.T.); +#1762=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3,-3.00000000000000000E3)); +#1763=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1764=VECTOR('',#1763,1.00000000000000000E3); +#1765=LINE('',#1762,#1764); +#1766=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1767=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1768=VECTOR('',#1767,1.00000000000000000E3); +#1769=LINE('',#1766,#1768); +#1770=DEFINITIONAL_REPRESENTATION('',(#1769),#1); +#1771=PCURVE('',#166,#1770); +#1772=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#1773=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1774=VECTOR('',#1773,1.00000000000000000E3); +#1775=LINE('',#1772,#1774); +#1776=DEFINITIONAL_REPRESENTATION('',(#1775),#1); +#1777=PCURVE('',#181,#1776); +#1778=SURFACE_CURVE('',#1765,(#1771,#1777),.CURVE_3D.); +#1779=EDGE_CURVE('',#355,#391,#1778,.T.); +#1780=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#1781=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1782=VECTOR('',#1781,1.00000000000000000E3); +#1783=LINE('',#1780,#1782); +#1784=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#1785=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1786=VECTOR('',#1785,1.00000000000000000E3); +#1787=LINE('',#1784,#1786); +#1788=DEFINITIONAL_REPRESENTATION('',(#1787),#1); +#1789=PCURVE('',#166,#1788); +#1790=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#1791=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1792=VECTOR('',#1791,1.00000000000000000E3); +#1793=LINE('',#1790,#1792); +#1794=DEFINITIONAL_REPRESENTATION('',(#1793),#1); +#1795=PCURVE('',#271,#1794); +#1796=SURFACE_CURVE('',#1783,(#1789,#1795),.CURVE_3D.); +#1797=EDGE_CURVE('',#393,#391,#1796,.T.); +#1798=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#1799=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1800=VECTOR('',#1799,1.00000000000000000E3); +#1801=LINE('',#1798,#1800); +#1802=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1803=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1804=VECTOR('',#1803,1.00000000000000000E3); +#1805=LINE('',#1802,#1804); +#1806=DEFINITIONAL_REPRESENTATION('',(#1805),#1); +#1807=PCURVE('',#166,#1806); +#1808=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3)); +#1809=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1810=VECTOR('',#1809,1.00000000000000000E3); +#1811=LINE('',#1808,#1810); +#1812=DEFINITIONAL_REPRESENTATION('',(#1811),#1); +#1813=PCURVE('',#171,#1812); +#1814=SURFACE_CURVE('',#1801,(#1807,#1813),.CURVE_3D.); +#1815=EDGE_CURVE('',#357,#393,#1814,.T.); +#1816=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#1817=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1818=VECTOR('',#1817,1.00000000000000000E3); +#1819=LINE('',#1816,#1818); +#1820=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1821=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1822=VECTOR('',#1821,1.00000000000000000E3); +#1823=LINE('',#1820,#1822); +#1824=DEFINITIONAL_REPRESENTATION('',(#1823),#1); +#1825=PCURVE('',#171,#1824); +#1826=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1827=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1828=VECTOR('',#1827,1.00000000000000000E3); +#1829=LINE('',#1826,#1828); +#1830=DEFINITIONAL_REPRESENTATION('',(#1829),#1); +#1831=PCURVE('',#276,#1830); +#1832=SURFACE_CURVE('',#1819,(#1825,#1831),.CURVE_3D.); +#1833=EDGE_CURVE('',#383,#393,#1832,.T.); +#1834=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,-2.00000000000000000E3)); +#1835=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1836=VECTOR('',#1835,1.00000000000000000E3); +#1837=LINE('',#1834,#1836); +#1838=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1839=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1840=VECTOR('',#1839,1.00000000000000000E3); +#1841=LINE('',#1838,#1840); +#1842=DEFINITIONAL_REPRESENTATION('',(#1841),#1); +#1843=PCURVE('',#176,#1842); +#1844=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1845=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1846=VECTOR('',#1845,1.00000000000000000E3); +#1847=LINE('',#1844,#1846); +#1848=DEFINITIONAL_REPRESENTATION('',(#1847),#1); +#1849=PCURVE('',#246,#1848); +#1850=SURFACE_CURVE('',#1837,(#1843,#1849),.CURVE_3D.); +#1851=EDGE_CURVE('',#385,#387,#1850,.T.); +#1852=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,-3.00000000000000000E3)); +#1853=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1854=VECTOR('',#1853,1.00000000000000000E3); +#1855=LINE('',#1852,#1854); +#1856=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1857=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1858=VECTOR('',#1857,1.00000000000000000E3); +#1859=LINE('',#1856,#1858); +#1860=DEFINITIONAL_REPRESENTATION('',(#1859),#1); +#1861=PCURVE('',#181,#1860); +#1862=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#1863=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1864=VECTOR('',#1863,1.00000000000000000E3); +#1865=LINE('',#1862,#1864); +#1866=DEFINITIONAL_REPRESENTATION('',(#1865),#1); +#1867=PCURVE('',#186,#1866); +#1868=SURFACE_CURVE('',#1855,(#1861,#1867),.CURVE_3D.); +#1869=EDGE_CURVE('',#361,#395,#1868,.T.); +#1870=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,-3.00000000000000000E3)); +#1871=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1872=VECTOR('',#1871,1.00000000000000000E3); +#1873=LINE('',#1870,#1872); +#1874=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1875=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1876=VECTOR('',#1875,1.00000000000000000E3); +#1877=LINE('',#1874,#1876); +#1878=DEFINITIONAL_REPRESENTATION('',(#1877),#1); +#1879=PCURVE('',#181,#1878); +#1880=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#1881=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1882=VECTOR('',#1881,1.00000000000000000E3); +#1883=LINE('',#1880,#1882); +#1884=DEFINITIONAL_REPRESENTATION('',(#1883),#1); +#1885=PCURVE('',#286,#1884); +#1886=SURFACE_CURVE('',#1873,(#1879,#1885),.CURVE_3D.); +#1887=EDGE_CURVE('',#391,#395,#1886,.T.); +#1888=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#1889=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1890=VECTOR('',#1889,1.00000000000000000E3); +#1891=LINE('',#1888,#1890); +#1892=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3)); +#1893=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1894=VECTOR('',#1893,1.00000000000000000E3); +#1895=LINE('',#1892,#1894); +#1896=DEFINITIONAL_REPRESENTATION('',(#1895),#1); +#1897=PCURVE('',#186,#1896); +#1898=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3)); +#1899=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1900=VECTOR('',#1899,1.00000000000000000E3); +#1901=LINE('',#1898,#1900); +#1902=DEFINITIONAL_REPRESENTATION('',(#1901),#1); +#1903=PCURVE('',#191,#1902); +#1904=SURFACE_CURVE('',#1891,(#1897,#1903),.CURVE_3D.); +#1905=EDGE_CURVE('',#365,#397,#1904,.T.); +#1906=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,-3.00000000000000000E3)); +#1907=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1908=VECTOR('',#1907,1.00000000000000000E3); +#1909=LINE('',#1906,#1908); +#1910=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1911=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1912=VECTOR('',#1911,1.00000000000000000E3); +#1913=LINE('',#1910,#1912); +#1914=DEFINITIONAL_REPRESENTATION('',(#1913),#1); +#1915=PCURVE('',#186,#1914); +#1916=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1917=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1918=VECTOR('',#1917,1.00000000000000000E3); +#1919=LINE('',#1916,#1918); +#1920=DEFINITIONAL_REPRESENTATION('',(#1919),#1); +#1921=PCURVE('',#296,#1920); +#1922=SURFACE_CURVE('',#1909,(#1915,#1921),.CURVE_3D.); +#1923=EDGE_CURVE('',#395,#397,#1922,.T.); +#1924=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,-2.00000000000000000E3)); +#1925=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1926=VECTOR('',#1925,1.00000000000000000E3); +#1927=LINE('',#1924,#1926); +#1928=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1929=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1930=VECTOR('',#1929,1.00000000000000000E3); +#1931=LINE('',#1928,#1930); +#1932=DEFINITIONAL_REPRESENTATION('',(#1931),#1); +#1933=PCURVE('',#191,#1932); +#1934=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#1935=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1936=VECTOR('',#1935,1.00000000000000000E3); +#1937=LINE('',#1934,#1936); +#1938=DEFINITIONAL_REPRESENTATION('',(#1937),#1); +#1939=PCURVE('',#301,#1938); +#1940=SURFACE_CURVE('',#1927,(#1933,#1939),.CURVE_3D.); +#1941=EDGE_CURVE('',#389,#397,#1940,.T.); +#1942=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#1943=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1944=VECTOR('',#1943,1.00000000000000000E3); +#1945=LINE('',#1942,#1944); +#1946=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1947=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1948=VECTOR('',#1947,1.00000000000000000E3); +#1949=LINE('',#1946,#1948); +#1950=DEFINITIONAL_REPRESENTATION('',(#1949),#1); +#1951=PCURVE('',#196,#1950); +#1952=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#1953=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1954=VECTOR('',#1953,1.00000000000000000E3); +#1955=LINE('',#1952,#1954); +#1956=DEFINITIONAL_REPRESENTATION('',(#1955),#1); +#1957=PCURVE('',#206,#1956); +#1958=SURFACE_CURVE('',#1945,(#1951,#1957),.CURVE_3D.); +#1959=EDGE_CURVE('',#399,#401,#1958,.T.); +#1960=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-1.00000000000000000E3)); +#1961=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1962=VECTOR('',#1961,1.00000000000000000E3); +#1963=LINE('',#1960,#1962); +#1964=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1965=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1966=VECTOR('',#1965,1.00000000000000000E3); +#1967=LINE('',#1964,#1966); +#1968=DEFINITIONAL_REPRESENTATION('',(#1967),#1); +#1969=PCURVE('',#196,#1968); +#1970=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#1971=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1972=VECTOR('',#1971,1.00000000000000000E3); +#1973=LINE('',#1970,#1972); +#1974=DEFINITIONAL_REPRESENTATION('',(#1973),#1); +#1975=PCURVE('',#236,#1974); +#1976=SURFACE_CURVE('',#1963,(#1969,#1975),.CURVE_3D.); +#1977=EDGE_CURVE('',#401,#403,#1976,.T.); +#1978=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3,0.00000000000000000E0)); +#1979=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1980=VECTOR('',#1979,1.00000000000000000E3); +#1981=LINE('',#1978,#1980); +#1982=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1983=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1984=VECTOR('',#1983,1.00000000000000000E3); +#1985=LINE('',#1982,#1984); +#1986=DEFINITIONAL_REPRESENTATION('',(#1985),#1); +#1987=PCURVE('',#196,#1986); +#1988=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#1989=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1990=VECTOR('',#1989,1.00000000000000000E3); +#1991=LINE('',#1988,#1990); +#1992=DEFINITIONAL_REPRESENTATION('',(#1991),#1); +#1993=PCURVE('',#211,#1992); +#1994=SURFACE_CURVE('',#1981,(#1987,#1993),.CURVE_3D.); +#1995=EDGE_CURVE('',#405,#403,#1994,.T.); +#1996=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#1997=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1998=VECTOR('',#1997,1.00000000000000000E3); +#1999=LINE('',#1996,#1998); +#2000=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#2001=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2002=VECTOR('',#2001,1.00000000000000000E3); +#2003=LINE('',#2000,#2002); +#2004=DEFINITIONAL_REPRESENTATION('',(#2003),#1); +#2005=PCURVE('',#196,#2004); +#2006=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#2007=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2008=VECTOR('',#2007,1.00000000000000000E3); +#2009=LINE('',#2006,#2008); +#2010=DEFINITIONAL_REPRESENTATION('',(#2009),#1); +#2011=PCURVE('',#201,#2010); +#2012=SURFACE_CURVE('',#1999,(#2005,#2011),.CURVE_3D.); +#2013=EDGE_CURVE('',#399,#405,#2012,.T.); +#2014=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,0.00000000000000000E0)); +#2015=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2016=VECTOR('',#2015,1.00000000000000000E3); +#2017=LINE('',#2014,#2016); +#2018=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#2019=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2020=VECTOR('',#2019,1.00000000000000000E3); +#2021=LINE('',#2018,#2020); +#2022=DEFINITIONAL_REPRESENTATION('',(#2021),#1); +#2023=PCURVE('',#201,#2022); +#2024=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#2025=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2026=VECTOR('',#2025,1.00000000000000000E3); +#2027=LINE('',#2024,#2026); +#2028=DEFINITIONAL_REPRESENTATION('',(#2027),#1); +#2029=PCURVE('',#216,#2028); +#2030=SURFACE_CURVE('',#2017,(#2023,#2029),.CURVE_3D.); +#2031=EDGE_CURVE('',#369,#405,#2030,.T.); +#2032=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#2033=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2034=VECTOR('',#2033,1.00000000000000000E3); +#2035=LINE('',#2032,#2034); +#2036=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#2037=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2038=VECTOR('',#2037,1.00000000000000000E3); +#2039=LINE('',#2036,#2038); +#2040=DEFINITIONAL_REPRESENTATION('',(#2039),#1); +#2041=PCURVE('',#201,#2040); +#2042=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#2043=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2044=VECTOR('',#2043,1.00000000000000000E3); +#2045=LINE('',#2042,#2044); +#2046=DEFINITIONAL_REPRESENTATION('',(#2045),#1); +#2047=PCURVE('',#206,#2046); +#2048=SURFACE_CURVE('',#2035,(#2041,#2047),.CURVE_3D.); +#2049=EDGE_CURVE('',#367,#399,#2048,.T.); +#2050=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-1.00000000000000000E3)); +#2051=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2052=VECTOR('',#2051,1.00000000000000000E3); +#2053=LINE('',#2050,#2052); +#2054=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#2055=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2056=VECTOR('',#2055,1.00000000000000000E3); +#2057=LINE('',#2054,#2056); +#2058=DEFINITIONAL_REPRESENTATION('',(#2057),#1); +#2059=PCURVE('',#206,#2058); +#2060=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#2061=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2062=VECTOR('',#2061,1.00000000000000000E3); +#2063=LINE('',#2060,#2062); +#2064=DEFINITIONAL_REPRESENTATION('',(#2063),#1); +#2065=PCURVE('',#241,#2064); +#2066=SURFACE_CURVE('',#2053,(#2059,#2065),.CURVE_3D.); +#2067=EDGE_CURVE('',#371,#401,#2066,.T.); +#2068=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#2069=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2070=VECTOR('',#2069,1.00000000000000000E3); +#2071=LINE('',#2068,#2070); +#2072=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#2073=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2074=VECTOR('',#2073,1.00000000000000000E3); +#2075=LINE('',#2072,#2074); +#2076=DEFINITIONAL_REPRESENTATION('',(#2075),#1); +#2077=PCURVE('',#211,#2076); +#2078=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#2079=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2080=VECTOR('',#2079,1.00000000000000000E3); +#2081=LINE('',#2078,#2080); +#2082=DEFINITIONAL_REPRESENTATION('',(#2081),#1); +#2083=PCURVE('',#251,#2082); +#2084=SURFACE_CURVE('',#2071,(#2077,#2083),.CURVE_3D.); +#2085=EDGE_CURVE('',#403,#407,#2084,.T.); +#2086=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#2087=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2088=VECTOR('',#2087,1.00000000000000000E3); +#2089=LINE('',#2086,#2088); +#2090=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#2091=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2092=VECTOR('',#2091,1.00000000000000000E3); +#2093=LINE('',#2090,#2092); +#2094=DEFINITIONAL_REPRESENTATION('',(#2093),#1); +#2095=PCURVE('',#211,#2094); +#2096=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#2097=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2098=VECTOR('',#2097,1.00000000000000000E3); +#2099=LINE('',#2096,#2098); +#2100=DEFINITIONAL_REPRESENTATION('',(#2099),#1); +#2101=PCURVE('',#221,#2100); +#2102=SURFACE_CURVE('',#2089,(#2095,#2101),.CURVE_3D.); +#2103=EDGE_CURVE('',#409,#407,#2102,.T.); +#2104=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3,0.00000000000000000E0)); +#2105=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2106=VECTOR('',#2105,1.00000000000000000E3); +#2107=LINE('',#2104,#2106); +#2108=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#2109=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2110=VECTOR('',#2109,1.00000000000000000E3); +#2111=LINE('',#2108,#2110); +#2112=DEFINITIONAL_REPRESENTATION('',(#2111),#1); +#2113=PCURVE('',#211,#2112); +#2114=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3)); +#2115=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2116=VECTOR('',#2115,1.00000000000000000E3); +#2117=LINE('',#2114,#2116); +#2118=DEFINITIONAL_REPRESENTATION('',(#2117),#1); +#2119=PCURVE('',#216,#2118); +#2120=SURFACE_CURVE('',#2107,(#2113,#2119),.CURVE_3D.); +#2121=EDGE_CURVE('',#405,#409,#2120,.T.); +#2122=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#2123=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2124=VECTOR('',#2123,1.00000000000000000E3); +#2125=LINE('',#2122,#2124); +#2126=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2127=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2128=VECTOR('',#2127,1.00000000000000000E3); +#2129=LINE('',#2126,#2128); +#2130=DEFINITIONAL_REPRESENTATION('',(#2129),#1); +#2131=PCURVE('',#216,#2130); +#2132=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2133=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2134=VECTOR('',#2133,1.00000000000000000E3); +#2135=LINE('',#2132,#2134); +#2136=DEFINITIONAL_REPRESENTATION('',(#2135),#1); +#2137=PCURVE('',#226,#2136); +#2138=SURFACE_CURVE('',#2125,(#2131,#2137),.CURVE_3D.); +#2139=EDGE_CURVE('',#373,#409,#2138,.T.); +#2140=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3,-1.00000000000000000E3)); +#2141=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2142=VECTOR('',#2141,1.00000000000000000E3); +#2143=LINE('',#2140,#2142); +#2144=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#2145=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2146=VECTOR('',#2145,1.00000000000000000E3); +#2147=LINE('',#2144,#2146); +#2148=DEFINITIONAL_REPRESENTATION('',(#2147),#1); +#2149=PCURVE('',#221,#2148); +#2150=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#2151=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2152=VECTOR('',#2151,1.00000000000000000E3); +#2153=LINE('',#2150,#2152); +#2154=DEFINITIONAL_REPRESENTATION('',(#2153),#1); +#2155=PCURVE('',#256,#2154); +#2156=SURFACE_CURVE('',#2143,(#2149,#2155),.CURVE_3D.); +#2157=EDGE_CURVE('',#407,#411,#2156,.T.); +#2158=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#2159=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2160=VECTOR('',#2159,1.00000000000000000E3); +#2161=LINE('',#2158,#2160); +#2162=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#2163=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2164=VECTOR('',#2163,1.00000000000000000E3); +#2165=LINE('',#2162,#2164); +#2166=DEFINITIONAL_REPRESENTATION('',(#2165),#1); +#2167=PCURVE('',#221,#2166); +#2168=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#2169=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2170=VECTOR('',#2169,1.00000000000000000E3); +#2171=LINE('',#2168,#2170); +#2172=DEFINITIONAL_REPRESENTATION('',(#2171),#1); +#2173=PCURVE('',#231,#2172); +#2174=SURFACE_CURVE('',#2161,(#2167,#2173),.CURVE_3D.); +#2175=EDGE_CURVE('',#413,#411,#2174,.T.); +#2176=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#2177=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2178=VECTOR('',#2177,1.00000000000000000E3); +#2179=LINE('',#2176,#2178); +#2180=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#2181=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2182=VECTOR('',#2181,1.00000000000000000E3); +#2183=LINE('',#2180,#2182); +#2184=DEFINITIONAL_REPRESENTATION('',(#2183),#1); +#2185=PCURVE('',#221,#2184); +#2186=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3)); +#2187=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2188=VECTOR('',#2187,1.00000000000000000E3); +#2189=LINE('',#2186,#2188); +#2190=DEFINITIONAL_REPRESENTATION('',(#2189),#1); +#2191=PCURVE('',#226,#2190); +#2192=SURFACE_CURVE('',#2179,(#2185,#2191),.CURVE_3D.); +#2193=EDGE_CURVE('',#409,#413,#2192,.T.); +#2194=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#2195=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2196=VECTOR('',#2195,1.00000000000000000E3); +#2197=LINE('',#2194,#2196); +#2198=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3)); +#2199=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2200=VECTOR('',#2199,1.00000000000000000E3); +#2201=LINE('',#2198,#2200); +#2202=DEFINITIONAL_REPRESENTATION('',(#2201),#1); +#2203=PCURVE('',#226,#2202); +#2204=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#2205=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2206=VECTOR('',#2205,1.00000000000000000E3); +#2207=LINE('',#2204,#2206); +#2208=DEFINITIONAL_REPRESENTATION('',(#2207),#1); +#2209=PCURVE('',#231,#2208); +#2210=SURFACE_CURVE('',#2197,(#2203,#2209),.CURVE_3D.); +#2211=EDGE_CURVE('',#379,#413,#2210,.T.); +#2212=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,-1.00000000000000000E3)); +#2213=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2214=VECTOR('',#2213,1.00000000000000000E3); +#2215=LINE('',#2212,#2214); +#2216=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#2217=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2218=VECTOR('',#2217,1.00000000000000000E3); +#2219=LINE('',#2216,#2218); +#2220=DEFINITIONAL_REPRESENTATION('',(#2219),#1); +#2221=PCURVE('',#231,#2220); +#2222=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#2223=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2224=VECTOR('',#2223,1.00000000000000000E3); +#2225=LINE('',#2222,#2224); +#2226=DEFINITIONAL_REPRESENTATION('',(#2225),#1); +#2227=PCURVE('',#261,#2226); +#2228=SURFACE_CURVE('',#2215,(#2221,#2227),.CURVE_3D.); +#2229=EDGE_CURVE('',#381,#411,#2228,.T.); +#2230=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-1.00000000000000000E3)); +#2231=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2232=VECTOR('',#2231,1.00000000000000000E3); +#2233=LINE('',#2230,#2232); +#2234=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#2235=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2236=VECTOR('',#2235,1.00000000000000000E3); +#2237=LINE('',#2234,#2236); +#2238=DEFINITIONAL_REPRESENTATION('',(#2237),#1); +#2239=PCURVE('',#236,#2238); +#2240=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3)); +#2241=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2242=VECTOR('',#2241,1.00000000000000000E3); +#2243=LINE('',#2240,#2242); +#2244=DEFINITIONAL_REPRESENTATION('',(#2243),#1); +#2245=PCURVE('',#241,#2244); +#2246=SURFACE_CURVE('',#2233,(#2239,#2245),.CURVE_3D.); +#2247=EDGE_CURVE('',#401,#415,#2246,.T.); +#2248=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#2249=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2250=VECTOR('',#2249,1.00000000000000000E3); +#2251=LINE('',#2248,#2250); +#2252=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#2253=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2254=VECTOR('',#2253,1.00000000000000000E3); +#2255=LINE('',#2252,#2254); +#2256=DEFINITIONAL_REPRESENTATION('',(#2255),#1); +#2257=PCURVE('',#236,#2256); +#2258=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#2259=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2260=VECTOR('',#2259,1.00000000000000000E3); +#2261=LINE('',#2258,#2260); +#2262=DEFINITIONAL_REPRESENTATION('',(#2261),#1); +#2263=PCURVE('',#266,#2262); +#2264=SURFACE_CURVE('',#2251,(#2257,#2263),.CURVE_3D.); +#2265=EDGE_CURVE('',#415,#417,#2264,.T.); +#2266=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3,-1.00000000000000000E3)); +#2267=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2268=VECTOR('',#2267,1.00000000000000000E3); +#2269=LINE('',#2266,#2268); +#2270=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#2271=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2272=VECTOR('',#2271,1.00000000000000000E3); +#2273=LINE('',#2270,#2272); +#2274=DEFINITIONAL_REPRESENTATION('',(#2273),#1); +#2275=PCURVE('',#236,#2274); +#2276=CARTESIAN_POINT('',(1.00000000000000000E3,1.00000000000000000E3)); +#2277=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2278=VECTOR('',#2277,1.00000000000000000E3); +#2279=LINE('',#2276,#2278); +#2280=DEFINITIONAL_REPRESENTATION('',(#2279),#1); +#2281=PCURVE('',#251,#2280); +#2282=SURFACE_CURVE('',#2269,(#2275,#2281),.CURVE_3D.); +#2283=EDGE_CURVE('',#403,#417,#2282,.T.); +#2284=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#2285=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2286=VECTOR('',#2285,1.00000000000000000E3); +#2287=LINE('',#2284,#2286); +#2288=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2289=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2290=VECTOR('',#2289,1.00000000000000000E3); +#2291=LINE('',#2288,#2290); +#2292=DEFINITIONAL_REPRESENTATION('',(#2291),#1); +#2293=PCURVE('',#241,#2292); +#2294=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2295=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2296=VECTOR('',#2295,1.00000000000000000E3); +#2297=LINE('',#2294,#2296); +#2298=DEFINITIONAL_REPRESENTATION('',(#2297),#1); +#2299=PCURVE('',#276,#2298); +#2300=SURFACE_CURVE('',#2287,(#2293,#2299),.CURVE_3D.); +#2301=EDGE_CURVE('',#383,#415,#2300,.T.); +#2302=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3,-2.00000000000000000E3)); +#2303=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2304=VECTOR('',#2303,1.00000000000000000E3); +#2305=LINE('',#2302,#2304); +#2306=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#2307=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2308=VECTOR('',#2307,1.00000000000000000E3); +#2309=LINE('',#2306,#2308); +#2310=DEFINITIONAL_REPRESENTATION('',(#2309),#1); +#2311=PCURVE('',#251,#2310); +#2312=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#2313=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2314=VECTOR('',#2313,1.00000000000000000E3); +#2315=LINE('',#2312,#2314); +#2316=DEFINITIONAL_REPRESENTATION('',(#2315),#1); +#2317=PCURVE('',#281,#2316); +#2318=SURFACE_CURVE('',#2305,(#2311,#2317),.CURVE_3D.); +#2319=EDGE_CURVE('',#417,#419,#2318,.T.); +#2320=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3,-1.00000000000000000E3)); +#2321=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2322=VECTOR('',#2321,1.00000000000000000E3); +#2323=LINE('',#2320,#2322); +#2324=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#2325=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2326=VECTOR('',#2325,1.00000000000000000E3); +#2327=LINE('',#2324,#2326); +#2328=DEFINITIONAL_REPRESENTATION('',(#2327),#1); +#2329=PCURVE('',#251,#2328); +#2330=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#2331=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2332=VECTOR('',#2331,1.00000000000000000E3); +#2333=LINE('',#2330,#2332); +#2334=DEFINITIONAL_REPRESENTATION('',(#2333),#1); +#2335=PCURVE('',#256,#2334); +#2336=SURFACE_CURVE('',#2323,(#2329,#2335),.CURVE_3D.); +#2337=EDGE_CURVE('',#407,#419,#2336,.T.); +#2338=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#2339=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2340=VECTOR('',#2339,1.00000000000000000E3); +#2341=LINE('',#2338,#2340); +#2342=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2343=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2344=VECTOR('',#2343,1.00000000000000000E3); +#2345=LINE('',#2342,#2344); +#2346=DEFINITIONAL_REPRESENTATION('',(#2345),#1); +#2347=PCURVE('',#256,#2346); +#2348=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2349=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2350=VECTOR('',#2349,1.00000000000000000E3); +#2351=LINE('',#2348,#2350); +#2352=DEFINITIONAL_REPRESENTATION('',(#2351),#1); +#2353=PCURVE('',#291,#2352); +#2354=SURFACE_CURVE('',#2341,(#2347,#2353),.CURVE_3D.); +#2355=EDGE_CURVE('',#419,#421,#2354,.T.); +#2356=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-1.00000000000000000E3)); +#2357=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2358=VECTOR('',#2357,1.00000000000000000E3); +#2359=LINE('',#2356,#2358); +#2360=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3)); +#2361=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2362=VECTOR('',#2361,1.00000000000000000E3); +#2363=LINE('',#2360,#2362); +#2364=DEFINITIONAL_REPRESENTATION('',(#2363),#1); +#2365=PCURVE('',#256,#2364); +#2366=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3)); +#2367=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2368=VECTOR('',#2367,1.00000000000000000E3); +#2369=LINE('',#2366,#2368); +#2370=DEFINITIONAL_REPRESENTATION('',(#2369),#1); +#2371=PCURVE('',#261,#2370); +#2372=SURFACE_CURVE('',#2359,(#2365,#2371),.CURVE_3D.); +#2373=EDGE_CURVE('',#411,#421,#2372,.T.); +#2374=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,-2.00000000000000000E3)); +#2375=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2376=VECTOR('',#2375,1.00000000000000000E3); +#2377=LINE('',#2374,#2376); +#2378=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2379=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2380=VECTOR('',#2379,1.00000000000000000E3); +#2381=LINE('',#2378,#2380); +#2382=DEFINITIONAL_REPRESENTATION('',(#2381),#1); +#2383=PCURVE('',#261,#2382); +#2384=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2385=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2386=VECTOR('',#2385,1.00000000000000000E3); +#2387=LINE('',#2384,#2386); +#2388=DEFINITIONAL_REPRESENTATION('',(#2387),#1); +#2389=PCURVE('',#301,#2388); +#2390=SURFACE_CURVE('',#2377,(#2383,#2389),.CURVE_3D.); +#2391=EDGE_CURVE('',#389,#421,#2390,.T.); +#2392=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-2.00000000000000000E3)); +#2393=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2394=VECTOR('',#2393,1.00000000000000000E3); +#2395=LINE('',#2392,#2394); +#2396=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#2397=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2398=VECTOR('',#2397,1.00000000000000000E3); +#2399=LINE('',#2396,#2398); +#2400=DEFINITIONAL_REPRESENTATION('',(#2399),#1); +#2401=PCURVE('',#266,#2400); +#2402=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3)); +#2403=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2404=VECTOR('',#2403,1.00000000000000000E3); +#2405=LINE('',#2402,#2404); +#2406=DEFINITIONAL_REPRESENTATION('',(#2405),#1); +#2407=PCURVE('',#276,#2406); +#2408=SURFACE_CURVE('',#2395,(#2401,#2407),.CURVE_3D.); +#2409=EDGE_CURVE('',#415,#423,#2408,.T.); +#2410=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#2411=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2412=VECTOR('',#2411,1.00000000000000000E3); +#2413=LINE('',#2410,#2412); +#2414=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#2415=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2416=VECTOR('',#2415,1.00000000000000000E3); +#2417=LINE('',#2414,#2416); +#2418=DEFINITIONAL_REPRESENTATION('',(#2417),#1); +#2419=PCURVE('',#266,#2418); +#2420=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#2421=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2422=VECTOR('',#2421,1.00000000000000000E3); +#2423=LINE('',#2420,#2422); +#2424=DEFINITIONAL_REPRESENTATION('',(#2423),#1); +#2425=PCURVE('',#271,#2424); +#2426=SURFACE_CURVE('',#2413,(#2419,#2425),.CURVE_3D.); +#2427=EDGE_CURVE('',#423,#425,#2426,.T.); +#2428=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3,-2.00000000000000000E3)); +#2429=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2430=VECTOR('',#2429,1.00000000000000000E3); +#2431=LINE('',#2428,#2430); +#2432=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#2433=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2434=VECTOR('',#2433,1.00000000000000000E3); +#2435=LINE('',#2432,#2434); +#2436=DEFINITIONAL_REPRESENTATION('',(#2435),#1); +#2437=PCURVE('',#266,#2436); +#2438=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3)); +#2439=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2440=VECTOR('',#2439,1.00000000000000000E3); +#2441=LINE('',#2438,#2440); +#2442=DEFINITIONAL_REPRESENTATION('',(#2441),#1); +#2443=PCURVE('',#281,#2442); +#2444=SURFACE_CURVE('',#2431,(#2437,#2443),.CURVE_3D.); +#2445=EDGE_CURVE('',#417,#425,#2444,.T.); +#2446=CARTESIAN_POINT('',(2.00000000000000000E3,1.00000000000000000E3,-3.00000000000000000E3)); +#2447=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2448=VECTOR('',#2447,1.00000000000000000E3); +#2449=LINE('',#2446,#2448); +#2450=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#2451=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2452=VECTOR('',#2451,1.00000000000000000E3); +#2453=LINE('',#2450,#2452); +#2454=DEFINITIONAL_REPRESENTATION('',(#2453),#1); +#2455=PCURVE('',#271,#2454); +#2456=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3)); +#2457=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2458=VECTOR('',#2457,1.00000000000000000E3); +#2459=LINE('',#2456,#2458); +#2460=DEFINITIONAL_REPRESENTATION('',(#2459),#1); +#2461=PCURVE('',#286,#2460); +#2462=SURFACE_CURVE('',#2449,(#2455,#2461),.CURVE_3D.); +#2463=EDGE_CURVE('',#391,#425,#2462,.T.); +#2464=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#2465=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2466=VECTOR('',#2465,1.00000000000000000E3); +#2467=LINE('',#2464,#2466); +#2468=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#2469=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2470=VECTOR('',#2469,1.00000000000000000E3); +#2471=LINE('',#2468,#2470); +#2472=DEFINITIONAL_REPRESENTATION('',(#2471),#1); +#2473=PCURVE('',#271,#2472); +#2474=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3)); +#2475=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2476=VECTOR('',#2475,1.00000000000000000E3); +#2477=LINE('',#2474,#2476); +#2478=DEFINITIONAL_REPRESENTATION('',(#2477),#1); +#2479=PCURVE('',#276,#2478); +#2480=SURFACE_CURVE('',#2467,(#2473,#2479),.CURVE_3D.); +#2481=EDGE_CURVE('',#393,#423,#2480,.T.); +#2482=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3,-3.00000000000000000E3)); +#2483=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2484=VECTOR('',#2483,1.00000000000000000E3); +#2485=LINE('',#2482,#2484); +#2486=CARTESIAN_POINT('',(3.00000000000000000E3,1.00000000000000000E3)); +#2487=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2488=VECTOR('',#2487,1.00000000000000000E3); +#2489=LINE('',#2486,#2488); +#2490=DEFINITIONAL_REPRESENTATION('',(#2489),#1); +#2491=PCURVE('',#281,#2490); +#2492=CARTESIAN_POINT('',(1.00000000000000000E3,3.00000000000000000E3)); +#2493=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2494=VECTOR('',#2493,1.00000000000000000E3); +#2495=LINE('',#2492,#2494); +#2496=DEFINITIONAL_REPRESENTATION('',(#2495),#1); +#2497=PCURVE('',#286,#2496); +#2498=SURFACE_CURVE('',#2485,(#2491,#2497),.CURVE_3D.); +#2499=EDGE_CURVE('',#425,#427,#2498,.T.); +#2500=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3,-2.00000000000000000E3)); +#2501=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2502=VECTOR('',#2501,1.00000000000000000E3); +#2503=LINE('',#2500,#2502); +#2504=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2505=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2506=VECTOR('',#2505,1.00000000000000000E3); +#2507=LINE('',#2504,#2506); +#2508=DEFINITIONAL_REPRESENTATION('',(#2507),#1); +#2509=PCURVE('',#281,#2508); +#2510=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2511=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2512=VECTOR('',#2511,1.00000000000000000E3); +#2513=LINE('',#2510,#2512); +#2514=DEFINITIONAL_REPRESENTATION('',(#2513),#1); +#2515=PCURVE('',#291,#2514); +#2516=SURFACE_CURVE('',#2503,(#2509,#2515),.CURVE_3D.); +#2517=EDGE_CURVE('',#419,#427,#2516,.T.); +#2518=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3,-3.00000000000000000E3)); +#2519=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2520=VECTOR('',#2519,1.00000000000000000E3); +#2521=LINE('',#2518,#2520); +#2522=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2523=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2524=VECTOR('',#2523,1.00000000000000000E3); +#2525=LINE('',#2522,#2524); +#2526=DEFINITIONAL_REPRESENTATION('',(#2525),#1); +#2527=PCURVE('',#286,#2526); +#2528=CARTESIAN_POINT('',(2.00000000000000000E3,2.00000000000000000E3)); +#2529=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2530=VECTOR('',#2529,1.00000000000000000E3); +#2531=LINE('',#2528,#2530); +#2532=DEFINITIONAL_REPRESENTATION('',(#2531),#1); +#2533=PCURVE('',#296,#2532); +#2534=SURFACE_CURVE('',#2521,(#2527,#2533),.CURVE_3D.); +#2535=EDGE_CURVE('',#395,#427,#2534,.T.); +#2536=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3,-3.00000000000000000E3)); +#2537=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#2538=VECTOR('',#2537,1.00000000000000000E3); +#2539=LINE('',#2536,#2538); +#2540=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3)); +#2541=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2542=VECTOR('',#2541,1.00000000000000000E3); +#2543=LINE('',#2540,#2542); +#2544=DEFINITIONAL_REPRESENTATION('',(#2543),#1); +#2545=PCURVE('',#291,#2544); +#2546=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3)); +#2547=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2548=VECTOR('',#2547,1.00000000000000000E3); +#2549=LINE('',#2546,#2548); +#2550=DEFINITIONAL_REPRESENTATION('',(#2549),#1); +#2551=PCURVE('',#296,#2550); +#2552=SURFACE_CURVE('',#2539,(#2545,#2551),.CURVE_3D.); +#2553=EDGE_CURVE('',#427,#429,#2552,.T.); +#2554=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-2.00000000000000000E3)); +#2555=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#2556=VECTOR('',#2555,1.00000000000000000E3); +#2557=LINE('',#2554,#2556); +#2558=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3)); +#2559=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2560=VECTOR('',#2559,1.00000000000000000E3); +#2561=LINE('',#2558,#2560); +#2562=DEFINITIONAL_REPRESENTATION('',(#2561),#1); +#2563=PCURVE('',#291,#2562); +#2564=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3)); +#2565=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2566=VECTOR('',#2565,1.00000000000000000E3); +#2567=LINE('',#2564,#2566); +#2568=DEFINITIONAL_REPRESENTATION('',(#2567),#1); +#2569=PCURVE('',#301,#2568); +#2570=SURFACE_CURVE('',#2557,(#2563,#2569),.CURVE_3D.); +#2571=EDGE_CURVE('',#421,#429,#2570,.T.); +#2572=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#2573=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#2574=VECTOR('',#2573,1.00000000000000000E3); +#2575=LINE('',#2572,#2574); +#2576=CARTESIAN_POINT('',(3.00000000000000000E3,2.00000000000000000E3)); +#2577=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#2578=VECTOR('',#2577,1.00000000000000000E3); +#2579=LINE('',#2576,#2578); +#2580=DEFINITIONAL_REPRESENTATION('',(#2579),#1); +#2581=PCURVE('',#296,#2580); +#2582=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3)); +#2583=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#2584=VECTOR('',#2583,1.00000000000000000E3); +#2585=LINE('',#2582,#2584); +#2586=DEFINITIONAL_REPRESENTATION('',(#2585),#1); +#2587=PCURVE('',#301,#2586); +#2588=SURFACE_CURVE('',#2575,(#2581,#2587),.CURVE_3D.); +#2589=EDGE_CURVE('',#397,#429,#2588,.T.); +#2590=ORIENTED_EDGE('',*,*,#447,.T.); +#2591=ORIENTED_EDGE('',*,*,#465,.F.); +#2592=ORIENTED_EDGE('',*,*,#483,.F.); +#2593=ORIENTED_EDGE('',*,*,#501,.T.); +#2594=EDGE_LOOP('',(#2590,#2591,#2592,#2593)); +#2595=FACE_OUTER_BOUND('',#2594,.T.); +#2596=ADVANCED_FACE('left-0',(#2595),#6,.T.); +#2597=ORIENTED_EDGE('',*,*,#519,.T.); +#2598=ORIENTED_EDGE('',*,*,#537,.F.); +#2599=ORIENTED_EDGE('',*,*,#501,.F.); +#2600=ORIENTED_EDGE('',*,*,#555,.T.); +#2601=EDGE_LOOP('',(#2597,#2598,#2599,#2600)); +#2602=FACE_OUTER_BOUND('',#2601,.T.); +#2603=ADVANCED_FACE('front-1',(#2602),#11,.T.); +#2604=ORIENTED_EDGE('',*,*,#573,.T.); +#2605=ORIENTED_EDGE('',*,*,#591,.F.); +#2606=ORIENTED_EDGE('',*,*,#555,.F.); +#2607=ORIENTED_EDGE('',*,*,#483,.T.); +#2608=EDGE_LOOP('',(#2604,#2605,#2606,#2607)); +#2609=FACE_OUTER_BOUND('',#2608,.T.); +#2610=ADVANCED_FACE('bottom-2',(#2609),#16,.T.); +#2611=ORIENTED_EDGE('',*,*,#609,.T.); +#2612=ORIENTED_EDGE('',*,*,#627,.F.); +#2613=ORIENTED_EDGE('',*,*,#447,.F.); +#2614=ORIENTED_EDGE('',*,*,#645,.T.); +#2615=EDGE_LOOP('',(#2611,#2612,#2613,#2614)); +#2616=FACE_OUTER_BOUND('',#2615,.T.); +#2617=ADVANCED_FACE('left-3',(#2616),#21,.T.); +#2618=ORIENTED_EDGE('',*,*,#663,.T.); +#2619=ORIENTED_EDGE('',*,*,#681,.F.); +#2620=ORIENTED_EDGE('',*,*,#645,.F.); +#2621=ORIENTED_EDGE('',*,*,#537,.T.); +#2622=EDGE_LOOP('',(#2618,#2619,#2620,#2621)); +#2623=FACE_OUTER_BOUND('',#2622,.T.); +#2624=ADVANCED_FACE('front-4',(#2623),#26,.T.); +#2625=ORIENTED_EDGE('',*,*,#699,.T.); +#2626=ORIENTED_EDGE('',*,*,#717,.F.); +#2627=ORIENTED_EDGE('',*,*,#609,.F.); +#2628=ORIENTED_EDGE('',*,*,#735,.T.); +#2629=EDGE_LOOP('',(#2625,#2626,#2627,#2628)); +#2630=FACE_OUTER_BOUND('',#2629,.T.); +#2631=ADVANCED_FACE('left-5',(#2630),#31,.T.); +#2632=ORIENTED_EDGE('',*,*,#753,.T.); +#2633=ORIENTED_EDGE('',*,*,#771,.F.); +#2634=ORIENTED_EDGE('',*,*,#735,.F.); +#2635=ORIENTED_EDGE('',*,*,#681,.T.); +#2636=EDGE_LOOP('',(#2632,#2633,#2634,#2635)); +#2637=FACE_OUTER_BOUND('',#2636,.T.); +#2638=ADVANCED_FACE('front-6',(#2637),#36,.T.); +#2639=ORIENTED_EDGE('',*,*,#771,.T.); +#2640=ORIENTED_EDGE('',*,*,#789,.T.); +#2641=ORIENTED_EDGE('',*,*,#807,.F.); +#2642=ORIENTED_EDGE('',*,*,#699,.F.); +#2643=EDGE_LOOP('',(#2639,#2640,#2641,#2642)); +#2644=FACE_OUTER_BOUND('',#2643,.T.); +#2645=ADVANCED_FACE('top-7',(#2644),#41,.T.); +#2646=ORIENTED_EDGE('',*,*,#825,.T.); +#2647=ORIENTED_EDGE('',*,*,#843,.F.); +#2648=ORIENTED_EDGE('',*,*,#861,.F.); +#2649=ORIENTED_EDGE('',*,*,#465,.T.); +#2650=EDGE_LOOP('',(#2646,#2647,#2648,#2649)); +#2651=FACE_OUTER_BOUND('',#2650,.T.); +#2652=ADVANCED_FACE('left-8',(#2651),#46,.T.); +#2653=ORIENTED_EDGE('',*,*,#879,.T.); +#2654=ORIENTED_EDGE('',*,*,#897,.F.); +#2655=ORIENTED_EDGE('',*,*,#573,.F.); +#2656=ORIENTED_EDGE('',*,*,#861,.T.); +#2657=EDGE_LOOP('',(#2653,#2654,#2655,#2656)); +#2658=FACE_OUTER_BOUND('',#2657,.T.); +#2659=ADVANCED_FACE('bottom-9',(#2658),#51,.T.); +#2660=ORIENTED_EDGE('',*,*,#915,.T.); +#2661=ORIENTED_EDGE('',*,*,#933,.F.); +#2662=ORIENTED_EDGE('',*,*,#825,.F.); +#2663=ORIENTED_EDGE('',*,*,#627,.T.); +#2664=EDGE_LOOP('',(#2660,#2661,#2662,#2663)); +#2665=FACE_OUTER_BOUND('',#2664,.T.); +#2666=ADVANCED_FACE('left-10',(#2665),#56,.T.); +#2667=ORIENTED_EDGE('',*,*,#951,.T.); +#2668=ORIENTED_EDGE('',*,*,#969,.T.); +#2669=ORIENTED_EDGE('',*,*,#987,.F.); +#2670=ORIENTED_EDGE('',*,*,#1005,.F.); +#2671=EDGE_LOOP('',(#2667,#2668,#2669,#2670)); +#2672=FACE_OUTER_BOUND('',#2671,.T.); +#2673=ADVANCED_FACE('left-11',(#2672),#61,.F.); +#2674=ORIENTED_EDGE('',*,*,#1023,.T.); +#2675=ORIENTED_EDGE('',*,*,#1041,.F.); +#2676=ORIENTED_EDGE('',*,*,#915,.F.); +#2677=ORIENTED_EDGE('',*,*,#717,.T.); +#2678=EDGE_LOOP('',(#2674,#2675,#2676,#2677)); +#2679=FACE_OUTER_BOUND('',#2678,.T.); +#2680=ADVANCED_FACE('left-12',(#2679),#66,.T.); +#2681=ORIENTED_EDGE('',*,*,#807,.T.); +#2682=ORIENTED_EDGE('',*,*,#1059,.T.); +#2683=ORIENTED_EDGE('',*,*,#1077,.F.); +#2684=ORIENTED_EDGE('',*,*,#1023,.F.); +#2685=EDGE_LOOP('',(#2681,#2682,#2683,#2684)); +#2686=FACE_OUTER_BOUND('',#2685,.T.); +#2687=ADVANCED_FACE('top-13',(#2686),#71,.T.); +#2688=ORIENTED_EDGE('',*,*,#1095,.T.); +#2689=ORIENTED_EDGE('',*,*,#1113,.F.); +#2690=ORIENTED_EDGE('',*,*,#1131,.F.); +#2691=ORIENTED_EDGE('',*,*,#843,.T.); +#2692=EDGE_LOOP('',(#2688,#2689,#2690,#2691)); +#2693=FACE_OUTER_BOUND('',#2692,.T.); +#2694=ADVANCED_FACE('left-14',(#2693),#76,.T.); +#2695=ORIENTED_EDGE('',*,*,#1113,.T.); +#2696=ORIENTED_EDGE('',*,*,#1149,.T.); +#2697=ORIENTED_EDGE('',*,*,#1167,.F.); +#2698=ORIENTED_EDGE('',*,*,#1185,.F.); +#2699=EDGE_LOOP('',(#2695,#2696,#2697,#2698)); +#2700=FACE_OUTER_BOUND('',#2699,.T.); +#2701=ADVANCED_FACE('back-15',(#2700),#81,.T.); +#2702=ORIENTED_EDGE('',*,*,#1185,.T.); +#2703=ORIENTED_EDGE('',*,*,#1203,.F.); +#2704=ORIENTED_EDGE('',*,*,#879,.F.); +#2705=ORIENTED_EDGE('',*,*,#1131,.T.); +#2706=EDGE_LOOP('',(#2702,#2703,#2704,#2705)); +#2707=FACE_OUTER_BOUND('',#2706,.T.); +#2708=ADVANCED_FACE('bottom-16',(#2707),#86,.T.); +#2709=ORIENTED_EDGE('',*,*,#1221,.T.); +#2710=ORIENTED_EDGE('',*,*,#1239,.F.); +#2711=ORIENTED_EDGE('',*,*,#1095,.F.); +#2712=ORIENTED_EDGE('',*,*,#933,.T.); +#2713=EDGE_LOOP('',(#2709,#2710,#2711,#2712)); +#2714=FACE_OUTER_BOUND('',#2713,.T.); +#2715=ADVANCED_FACE('left-17',(#2714),#91,.T.); +#2716=ORIENTED_EDGE('',*,*,#1239,.T.); +#2717=ORIENTED_EDGE('',*,*,#1257,.T.); +#2718=ORIENTED_EDGE('',*,*,#1275,.F.); +#2719=ORIENTED_EDGE('',*,*,#1149,.F.); +#2720=EDGE_LOOP('',(#2716,#2717,#2718,#2719)); +#2721=FACE_OUTER_BOUND('',#2720,.T.); +#2722=ADVANCED_FACE('back-18',(#2721),#96,.T.); +#2723=ORIENTED_EDGE('',*,*,#1293,.T.); +#2724=ORIENTED_EDGE('',*,*,#1311,.F.); +#2725=ORIENTED_EDGE('',*,*,#1221,.F.); +#2726=ORIENTED_EDGE('',*,*,#1041,.T.); +#2727=EDGE_LOOP('',(#2723,#2724,#2725,#2726)); +#2728=FACE_OUTER_BOUND('',#2727,.T.); +#2729=ADVANCED_FACE('left-19',(#2728),#101,.T.); +#2730=ORIENTED_EDGE('',*,*,#1311,.T.); +#2731=ORIENTED_EDGE('',*,*,#1329,.T.); +#2732=ORIENTED_EDGE('',*,*,#1347,.F.); +#2733=ORIENTED_EDGE('',*,*,#1257,.F.); +#2734=EDGE_LOOP('',(#2730,#2731,#2732,#2733)); +#2735=FACE_OUTER_BOUND('',#2734,.T.); +#2736=ADVANCED_FACE('back-20',(#2735),#106,.T.); +#2737=ORIENTED_EDGE('',*,*,#1077,.T.); +#2738=ORIENTED_EDGE('',*,*,#1365,.T.); +#2739=ORIENTED_EDGE('',*,*,#1329,.F.); +#2740=ORIENTED_EDGE('',*,*,#1293,.F.); +#2741=EDGE_LOOP('',(#2737,#2738,#2739,#2740)); +#2742=FACE_OUTER_BOUND('',#2741,.T.); +#2743=ADVANCED_FACE('top-21',(#2742),#111,.T.); +#2744=ORIENTED_EDGE('',*,*,#1383,.T.); +#2745=ORIENTED_EDGE('',*,*,#1401,.F.); +#2746=ORIENTED_EDGE('',*,*,#519,.F.); +#2747=ORIENTED_EDGE('',*,*,#1419,.T.); +#2748=EDGE_LOOP('',(#2744,#2745,#2746,#2747)); +#2749=FACE_OUTER_BOUND('',#2748,.T.); +#2750=ADVANCED_FACE('front-22',(#2749),#116,.T.); +#2751=ORIENTED_EDGE('',*,*,#1437,.T.); +#2752=ORIENTED_EDGE('',*,*,#1455,.F.); +#2753=ORIENTED_EDGE('',*,*,#1419,.F.); +#2754=ORIENTED_EDGE('',*,*,#591,.T.); +#2755=EDGE_LOOP('',(#2751,#2752,#2753,#2754)); +#2756=FACE_OUTER_BOUND('',#2755,.T.); +#2757=ADVANCED_FACE('bottom-23',(#2756),#121,.T.); +#2758=ORIENTED_EDGE('',*,*,#1473,.T.); +#2759=ORIENTED_EDGE('',*,*,#1491,.F.); +#2760=ORIENTED_EDGE('',*,*,#663,.F.); +#2761=ORIENTED_EDGE('',*,*,#1401,.T.); +#2762=EDGE_LOOP('',(#2758,#2759,#2760,#2761)); +#2763=FACE_OUTER_BOUND('',#2762,.T.); +#2764=ADVANCED_FACE('front-24',(#2763),#126,.T.); +#2765=ORIENTED_EDGE('',*,*,#1005,.T.); +#2766=ORIENTED_EDGE('',*,*,#1509,.T.); +#2767=ORIENTED_EDGE('',*,*,#1527,.F.); +#2768=ORIENTED_EDGE('',*,*,#1545,.F.); +#2769=EDGE_LOOP('',(#2765,#2766,#2767,#2768)); +#2770=FACE_OUTER_BOUND('',#2769,.T.); +#2771=ADVANCED_FACE('front-25',(#2770),#131,.F.); +#2772=ORIENTED_EDGE('',*,*,#1563,.T.); +#2773=ORIENTED_EDGE('',*,*,#1581,.F.); +#2774=ORIENTED_EDGE('',*,*,#753,.F.); +#2775=ORIENTED_EDGE('',*,*,#1491,.T.); +#2776=EDGE_LOOP('',(#2772,#2773,#2774,#2775)); +#2777=FACE_OUTER_BOUND('',#2776,.T.); +#2778=ADVANCED_FACE('front-26',(#2777),#136,.T.); +#2779=ORIENTED_EDGE('',*,*,#1581,.T.); +#2780=ORIENTED_EDGE('',*,*,#1599,.T.); +#2781=ORIENTED_EDGE('',*,*,#1617,.F.); +#2782=ORIENTED_EDGE('',*,*,#789,.F.); +#2783=EDGE_LOOP('',(#2779,#2780,#2781,#2782)); +#2784=FACE_OUTER_BOUND('',#2783,.T.); +#2785=ADVANCED_FACE('top-27',(#2784),#141,.T.); +#2786=ORIENTED_EDGE('',*,*,#1635,.T.); +#2787=ORIENTED_EDGE('',*,*,#1653,.F.); +#2788=ORIENTED_EDGE('',*,*,#1437,.F.); +#2789=ORIENTED_EDGE('',*,*,#897,.T.); +#2790=EDGE_LOOP('',(#2786,#2787,#2788,#2789)); +#2791=FACE_OUTER_BOUND('',#2790,.T.); +#2792=ADVANCED_FACE('bottom-28',(#2791),#146,.T.); +#2793=ORIENTED_EDGE('',*,*,#1545,.T.); +#2794=ORIENTED_EDGE('',*,*,#1671,.T.); +#2795=ORIENTED_EDGE('',*,*,#1689,.F.); +#2796=ORIENTED_EDGE('',*,*,#951,.F.); +#2797=EDGE_LOOP('',(#2793,#2794,#2795,#2796)); +#2798=FACE_OUTER_BOUND('',#2797,.T.); +#2799=ADVANCED_FACE('bottom-29',(#2798),#151,.F.); +#2800=ORIENTED_EDGE('',*,*,#1707,.T.); +#2801=ORIENTED_EDGE('',*,*,#1725,.F.); +#2802=ORIENTED_EDGE('',*,*,#1509,.F.); +#2803=ORIENTED_EDGE('',*,*,#987,.T.); +#2804=EDGE_LOOP('',(#2800,#2801,#2802,#2803)); +#2805=FACE_OUTER_BOUND('',#2804,.T.); +#2806=ADVANCED_FACE('top-30',(#2805),#156,.F.); +#2807=ORIENTED_EDGE('',*,*,#1617,.T.); +#2808=ORIENTED_EDGE('',*,*,#1743,.T.); +#2809=ORIENTED_EDGE('',*,*,#1761,.F.); +#2810=ORIENTED_EDGE('',*,*,#1059,.F.); +#2811=EDGE_LOOP('',(#2807,#2808,#2809,#2810)); +#2812=FACE_OUTER_BOUND('',#2811,.T.); +#2813=ADVANCED_FACE('top-31',(#2812),#161,.T.); +#2814=ORIENTED_EDGE('',*,*,#1167,.T.); +#2815=ORIENTED_EDGE('',*,*,#1779,.T.); +#2816=ORIENTED_EDGE('',*,*,#1797,.F.); +#2817=ORIENTED_EDGE('',*,*,#1815,.F.); +#2818=EDGE_LOOP('',(#2814,#2815,#2816,#2817)); +#2819=FACE_OUTER_BOUND('',#2818,.T.); +#2820=ADVANCED_FACE('back-32',(#2819),#166,.T.); +#2821=ORIENTED_EDGE('',*,*,#1815,.T.); +#2822=ORIENTED_EDGE('',*,*,#1833,.F.); +#2823=ORIENTED_EDGE('',*,*,#1635,.F.); +#2824=ORIENTED_EDGE('',*,*,#1203,.T.); +#2825=EDGE_LOOP('',(#2821,#2822,#2823,#2824)); +#2826=FACE_OUTER_BOUND('',#2825,.T.); +#2827=ADVANCED_FACE('bottom-33',(#2826),#171,.T.); +#2828=ORIENTED_EDGE('',*,*,#1851,.T.); +#2829=ORIENTED_EDGE('',*,*,#1707,.F.); +#2830=ORIENTED_EDGE('',*,*,#969,.F.); +#2831=ORIENTED_EDGE('',*,*,#1689,.T.); +#2832=EDGE_LOOP('',(#2828,#2829,#2830,#2831)); +#2833=FACE_OUTER_BOUND('',#2832,.T.); +#2834=ADVANCED_FACE('back-34',(#2833),#176,.F.); +#2835=ORIENTED_EDGE('',*,*,#1275,.T.); +#2836=ORIENTED_EDGE('',*,*,#1869,.T.); +#2837=ORIENTED_EDGE('',*,*,#1887,.F.); +#2838=ORIENTED_EDGE('',*,*,#1779,.F.); +#2839=EDGE_LOOP('',(#2835,#2836,#2837,#2838)); +#2840=FACE_OUTER_BOUND('',#2839,.T.); +#2841=ADVANCED_FACE('back-35',(#2840),#181,.T.); +#2842=ORIENTED_EDGE('',*,*,#1347,.T.); +#2843=ORIENTED_EDGE('',*,*,#1905,.T.); +#2844=ORIENTED_EDGE('',*,*,#1923,.F.); +#2845=ORIENTED_EDGE('',*,*,#1869,.F.); +#2846=EDGE_LOOP('',(#2842,#2843,#2844,#2845)); +#2847=FACE_OUTER_BOUND('',#2846,.T.); +#2848=ADVANCED_FACE('back-36',(#2847),#186,.T.); +#2849=ORIENTED_EDGE('',*,*,#1761,.T.); +#2850=ORIENTED_EDGE('',*,*,#1941,.T.); +#2851=ORIENTED_EDGE('',*,*,#1905,.F.); +#2852=ORIENTED_EDGE('',*,*,#1365,.F.); +#2853=EDGE_LOOP('',(#2849,#2850,#2851,#2852)); +#2854=FACE_OUTER_BOUND('',#2853,.T.); +#2855=ADVANCED_FACE('top-37',(#2854),#191,.T.); +#2856=ORIENTED_EDGE('',*,*,#1959,.T.); +#2857=ORIENTED_EDGE('',*,*,#1977,.T.); +#2858=ORIENTED_EDGE('',*,*,#1995,.F.); +#2859=ORIENTED_EDGE('',*,*,#2013,.F.); +#2860=EDGE_LOOP('',(#2856,#2857,#2858,#2859)); +#2861=FACE_OUTER_BOUND('',#2860,.T.); +#2862=ADVANCED_FACE('right-38',(#2861),#196,.T.); +#2863=ORIENTED_EDGE('',*,*,#2013,.T.); +#2864=ORIENTED_EDGE('',*,*,#2031,.F.); +#2865=ORIENTED_EDGE('',*,*,#1383,.F.); +#2866=ORIENTED_EDGE('',*,*,#2049,.T.); +#2867=EDGE_LOOP('',(#2863,#2864,#2865,#2866)); +#2868=FACE_OUTER_BOUND('',#2867,.T.); +#2869=ADVANCED_FACE('front-39',(#2868),#201,.T.); +#2870=ORIENTED_EDGE('',*,*,#2067,.T.); +#2871=ORIENTED_EDGE('',*,*,#1959,.F.); +#2872=ORIENTED_EDGE('',*,*,#2049,.F.); +#2873=ORIENTED_EDGE('',*,*,#1455,.T.); +#2874=EDGE_LOOP('',(#2870,#2871,#2872,#2873)); +#2875=FACE_OUTER_BOUND('',#2874,.T.); +#2876=ADVANCED_FACE('bottom-40',(#2875),#206,.T.); +#2877=ORIENTED_EDGE('',*,*,#1995,.T.); +#2878=ORIENTED_EDGE('',*,*,#2085,.T.); +#2879=ORIENTED_EDGE('',*,*,#2103,.F.); +#2880=ORIENTED_EDGE('',*,*,#2121,.F.); +#2881=EDGE_LOOP('',(#2877,#2878,#2879,#2880)); +#2882=FACE_OUTER_BOUND('',#2881,.T.); +#2883=ADVANCED_FACE('right-41',(#2882),#211,.T.); +#2884=ORIENTED_EDGE('',*,*,#2121,.T.); +#2885=ORIENTED_EDGE('',*,*,#2139,.F.); +#2886=ORIENTED_EDGE('',*,*,#1473,.F.); +#2887=ORIENTED_EDGE('',*,*,#2031,.T.); +#2888=EDGE_LOOP('',(#2884,#2885,#2886,#2887)); +#2889=FACE_OUTER_BOUND('',#2888,.T.); +#2890=ADVANCED_FACE('front-42',(#2889),#216,.T.); +#2891=ORIENTED_EDGE('',*,*,#2103,.T.); +#2892=ORIENTED_EDGE('',*,*,#2157,.T.); +#2893=ORIENTED_EDGE('',*,*,#2175,.F.); +#2894=ORIENTED_EDGE('',*,*,#2193,.F.); +#2895=EDGE_LOOP('',(#2891,#2892,#2893,#2894)); +#2896=FACE_OUTER_BOUND('',#2895,.T.); +#2897=ADVANCED_FACE('right-43',(#2896),#221,.T.); +#2898=ORIENTED_EDGE('',*,*,#2193,.T.); +#2899=ORIENTED_EDGE('',*,*,#2211,.F.); +#2900=ORIENTED_EDGE('',*,*,#1563,.F.); +#2901=ORIENTED_EDGE('',*,*,#2139,.T.); +#2902=EDGE_LOOP('',(#2898,#2899,#2900,#2901)); +#2903=FACE_OUTER_BOUND('',#2902,.T.); +#2904=ADVANCED_FACE('front-44',(#2903),#226,.T.); +#2905=ORIENTED_EDGE('',*,*,#2211,.T.); +#2906=ORIENTED_EDGE('',*,*,#2175,.T.); +#2907=ORIENTED_EDGE('',*,*,#2229,.F.); +#2908=ORIENTED_EDGE('',*,*,#1599,.F.); +#2909=EDGE_LOOP('',(#2905,#2906,#2907,#2908)); +#2910=FACE_OUTER_BOUND('',#2909,.T.); +#2911=ADVANCED_FACE('top-45',(#2910),#231,.T.); +#2912=ORIENTED_EDGE('',*,*,#2247,.T.); +#2913=ORIENTED_EDGE('',*,*,#2265,.T.); +#2914=ORIENTED_EDGE('',*,*,#2283,.F.); +#2915=ORIENTED_EDGE('',*,*,#1977,.F.); +#2916=EDGE_LOOP('',(#2912,#2913,#2914,#2915)); +#2917=FACE_OUTER_BOUND('',#2916,.T.); +#2918=ADVANCED_FACE('right-46',(#2917),#236,.T.); +#2919=ORIENTED_EDGE('',*,*,#2301,.T.); +#2920=ORIENTED_EDGE('',*,*,#2247,.F.); +#2921=ORIENTED_EDGE('',*,*,#2067,.F.); +#2922=ORIENTED_EDGE('',*,*,#1653,.T.); +#2923=EDGE_LOOP('',(#2919,#2920,#2921,#2922)); +#2924=FACE_OUTER_BOUND('',#2923,.T.); +#2925=ADVANCED_FACE('bottom-47',(#2924),#241,.T.); +#2926=ORIENTED_EDGE('',*,*,#1725,.T.); +#2927=ORIENTED_EDGE('',*,*,#1851,.F.); +#2928=ORIENTED_EDGE('',*,*,#1671,.F.); +#2929=ORIENTED_EDGE('',*,*,#1527,.T.); +#2930=EDGE_LOOP('',(#2926,#2927,#2928,#2929)); +#2931=FACE_OUTER_BOUND('',#2930,.T.); +#2932=ADVANCED_FACE('right-48',(#2931),#246,.F.); +#2933=ORIENTED_EDGE('',*,*,#2283,.T.); +#2934=ORIENTED_EDGE('',*,*,#2319,.T.); +#2935=ORIENTED_EDGE('',*,*,#2337,.F.); +#2936=ORIENTED_EDGE('',*,*,#2085,.F.); +#2937=EDGE_LOOP('',(#2933,#2934,#2935,#2936)); +#2938=FACE_OUTER_BOUND('',#2937,.T.); +#2939=ADVANCED_FACE('right-49',(#2938),#251,.T.); +#2940=ORIENTED_EDGE('',*,*,#2337,.T.); +#2941=ORIENTED_EDGE('',*,*,#2355,.T.); +#2942=ORIENTED_EDGE('',*,*,#2373,.F.); +#2943=ORIENTED_EDGE('',*,*,#2157,.F.); +#2944=EDGE_LOOP('',(#2940,#2941,#2942,#2943)); +#2945=FACE_OUTER_BOUND('',#2944,.T.); +#2946=ADVANCED_FACE('right-50',(#2945),#256,.T.); +#2947=ORIENTED_EDGE('',*,*,#2229,.T.); +#2948=ORIENTED_EDGE('',*,*,#2373,.T.); +#2949=ORIENTED_EDGE('',*,*,#2391,.F.); +#2950=ORIENTED_EDGE('',*,*,#1743,.F.); +#2951=EDGE_LOOP('',(#2947,#2948,#2949,#2950)); +#2952=FACE_OUTER_BOUND('',#2951,.T.); +#2953=ADVANCED_FACE('top-51',(#2952),#261,.T.); +#2954=ORIENTED_EDGE('',*,*,#2409,.T.); +#2955=ORIENTED_EDGE('',*,*,#2427,.T.); +#2956=ORIENTED_EDGE('',*,*,#2445,.F.); +#2957=ORIENTED_EDGE('',*,*,#2265,.F.); +#2958=EDGE_LOOP('',(#2954,#2955,#2956,#2957)); +#2959=FACE_OUTER_BOUND('',#2958,.T.); +#2960=ADVANCED_FACE('right-52',(#2959),#266,.T.); +#2961=ORIENTED_EDGE('',*,*,#1797,.T.); +#2962=ORIENTED_EDGE('',*,*,#2463,.T.); +#2963=ORIENTED_EDGE('',*,*,#2427,.F.); +#2964=ORIENTED_EDGE('',*,*,#2481,.F.); +#2965=EDGE_LOOP('',(#2961,#2962,#2963,#2964)); +#2966=FACE_OUTER_BOUND('',#2965,.T.); +#2967=ADVANCED_FACE('back-53',(#2966),#271,.T.); +#2968=ORIENTED_EDGE('',*,*,#2481,.T.); +#2969=ORIENTED_EDGE('',*,*,#2409,.F.); +#2970=ORIENTED_EDGE('',*,*,#2301,.F.); +#2971=ORIENTED_EDGE('',*,*,#1833,.T.); +#2972=EDGE_LOOP('',(#2968,#2969,#2970,#2971)); +#2973=FACE_OUTER_BOUND('',#2972,.T.); +#2974=ADVANCED_FACE('bottom-54',(#2973),#276,.T.); +#2975=ORIENTED_EDGE('',*,*,#2445,.T.); +#2976=ORIENTED_EDGE('',*,*,#2499,.T.); +#2977=ORIENTED_EDGE('',*,*,#2517,.F.); +#2978=ORIENTED_EDGE('',*,*,#2319,.F.); +#2979=EDGE_LOOP('',(#2975,#2976,#2977,#2978)); +#2980=FACE_OUTER_BOUND('',#2979,.T.); +#2981=ADVANCED_FACE('right-55',(#2980),#281,.T.); +#2982=ORIENTED_EDGE('',*,*,#1887,.T.); +#2983=ORIENTED_EDGE('',*,*,#2535,.T.); +#2984=ORIENTED_EDGE('',*,*,#2499,.F.); +#2985=ORIENTED_EDGE('',*,*,#2463,.F.); +#2986=EDGE_LOOP('',(#2982,#2983,#2984,#2985)); +#2987=FACE_OUTER_BOUND('',#2986,.T.); +#2988=ADVANCED_FACE('back-56',(#2987),#286,.T.); +#2989=ORIENTED_EDGE('',*,*,#2517,.T.); +#2990=ORIENTED_EDGE('',*,*,#2553,.T.); +#2991=ORIENTED_EDGE('',*,*,#2571,.F.); +#2992=ORIENTED_EDGE('',*,*,#2355,.F.); +#2993=EDGE_LOOP('',(#2989,#2990,#2991,#2992)); +#2994=FACE_OUTER_BOUND('',#2993,.T.); +#2995=ADVANCED_FACE('right-57',(#2994),#291,.T.); +#2996=ORIENTED_EDGE('',*,*,#1923,.T.); +#2997=ORIENTED_EDGE('',*,*,#2589,.T.); +#2998=ORIENTED_EDGE('',*,*,#2553,.F.); +#2999=ORIENTED_EDGE('',*,*,#2535,.F.); +#3000=EDGE_LOOP('',(#2996,#2997,#2998,#2999)); +#3001=FACE_OUTER_BOUND('',#3000,.T.); +#3002=ADVANCED_FACE('back-58',(#3001),#296,.T.); +#3003=ORIENTED_EDGE('',*,*,#2391,.T.); +#3004=ORIENTED_EDGE('',*,*,#2571,.T.); +#3005=ORIENTED_EDGE('',*,*,#2589,.F.); +#3006=ORIENTED_EDGE('',*,*,#1941,.F.); +#3007=EDGE_LOOP('',(#3003,#3004,#3005,#3006)); +#3008=FACE_OUTER_BOUND('',#3007,.T.); +#3009=ADVANCED_FACE('top-59',(#3008),#301,.T.); +#3010=CLOSED_SHELL('',(#2596,#2617,#2652,#2610,#2603,#2631,#2666,#2624,#2694,#2659,#2757,#2750,#2645,#2680,#2638,#2715,#2764,#2701,#2708,#2792,#2876,#2869,#2785,#2687,#2729,#2778,#2722,#2890,#2820,#2827,#2925,#2862,#2911,#2813,#2743,#2736,#2904,#2841,#2883,#2967,#2974,#2918,#2897,#2953,#2855,#2848,#2988,#2939,#2960,#2946,#3009,#3002,#2981,#2995)); +#3011=CLOSED_SHELL('',(#2673,#2799,#2834,#2806,#2771,#2932)); +#3012=ORIENTED_CLOSED_SHELL('',*,#3011,.T.); +#3013=BREP_WITH_VOIDS('box-cavity-0',#3010,(#3012)); +#3014=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#3015=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#3016=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#3017=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000426325642887E-5),#3014,'distance_accuracy_value','exchange error bound'); +#3018=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#3017)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#3014,#3015,#3016)) REPRESENTATION_CONTEXT('','')); +#3019=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#3013),#3018); +#3020=APPLICATION_CONTEXT('automotive design'); +#3021=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#3020); +#3022=PRODUCT_CONTEXT('',#3020,'mechanical'); +#3023=PRODUCT('box-cavity','box-cavity','',(#3022)); +#3024=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#3023,.NOT_KNOWN.); +#3025=PRODUCT_DEFINITION_CONTEXT('part definition',#3020,'design'); +#3026=PRODUCT_DEFINITION('','',#3024,#3025); +#3027=PRODUCT_DEFINITION_SHAPE('','',#3026); +#3028=SHAPE_DEFINITION_REPRESENTATION(#3027,#3019); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/box-cavity.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/box-cavity.step.mm.report.json new file mode 100644 index 0000000..1a57271 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cavity.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":1,"collapsed_chart_uses":0,"edges":120,"exchange_error_bound":0.000010000042632564289,"faces":60,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"1","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-cut.brep.json b/main/opengeometry/tests/fixtures/cases/box-cut.brep.json new file mode 100644 index 0000000..3d4fbe9 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cut.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"box-cut","revision":1,"geometry":{"surfaces":[{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}}],"curves":[{"kind":"Line","origin":[0.0,0.5,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.5,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.5,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.5,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,0.5,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,3.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,3.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,0.5,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.5,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,3.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.5,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,0.5,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,0.5,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,3.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,0.5,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,3.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,0.5,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,0.5,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.5,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.5,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,3.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,3.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.5,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,0.5,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,0.5,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,-3.0],"direction":[1.0,0.0,0.0]}],"pcurves":[{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[0.0,0.5,0.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[0.0,0.5,-0.5],"outgoing_halfedge":1,"tolerance":1e-8},{"id":2,"position":[0.0,0.0,-0.5],"outgoing_halfedge":2,"tolerance":1e-8},{"id":3,"position":[0.0,0.0,0.0],"outgoing_halfedge":3,"tolerance":1e-8},{"id":4,"position":[1.5,0.0,0.0],"outgoing_halfedge":4,"tolerance":1e-8},{"id":5,"position":[1.5,0.5,0.0],"outgoing_halfedge":5,"tolerance":1e-8},{"id":6,"position":[1.5,0.0,-0.5],"outgoing_halfedge":9,"tolerance":1e-8},{"id":7,"position":[0.0,3.0,0.0],"outgoing_halfedge":12,"tolerance":1e-8},{"id":8,"position":[0.0,3.0,-0.5],"outgoing_halfedge":13,"tolerance":1e-8},{"id":9,"position":[1.5,3.0,0.0],"outgoing_halfedge":17,"tolerance":1e-8},{"id":10,"position":[1.5,3.0,-0.5],"outgoing_halfedge":22,"tolerance":1e-8},{"id":11,"position":[0.0,0.5,-3.0],"outgoing_halfedge":25,"tolerance":1e-8},{"id":12,"position":[0.0,0.0,-3.0],"outgoing_halfedge":26,"tolerance":1e-8},{"id":13,"position":[1.5,0.5,-3.0],"outgoing_halfedge":30,"tolerance":1e-8},{"id":14,"position":[1.5,0.0,-3.0],"outgoing_halfedge":31,"tolerance":1e-8},{"id":15,"position":[0.0,3.0,-3.0],"outgoing_halfedge":37,"tolerance":1e-8},{"id":16,"position":[1.5,0.5,-0.5],"outgoing_halfedge":40,"tolerance":1e-8},{"id":17,"position":[1.5,3.0,-3.0],"outgoing_halfedge":42,"tolerance":1e-8},{"id":18,"position":[3.0,0.0,0.0],"outgoing_halfedge":52,"tolerance":1e-8},{"id":19,"position":[3.0,0.0,-0.5],"outgoing_halfedge":53,"tolerance":1e-8},{"id":20,"position":[3.0,0.5,-0.5],"outgoing_halfedge":54,"tolerance":1e-8},{"id":21,"position":[3.0,0.5,0.0],"outgoing_halfedge":55,"tolerance":1e-8},{"id":22,"position":[3.0,3.0,-0.5],"outgoing_halfedge":66,"tolerance":1e-8},{"id":23,"position":[3.0,3.0,0.0],"outgoing_halfedge":67,"tolerance":1e-8},{"id":24,"position":[3.0,0.0,-3.0],"outgoing_halfedge":81,"tolerance":1e-8},{"id":25,"position":[3.0,0.5,-3.0],"outgoing_halfedge":82,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":0.5}},"halfedge":0,"twin_halfedge":14,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":0.5}},"halfedge":1,"twin_halfedge":27,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":0.5}},"halfedge":2,"twin_halfedge":11,"tolerance":1e-8,"chart_seam":false},{"id":3,"geometry":{"kind":"Curve","curve":3,"range":{"lo":0.0,"hi":0.5}},"halfedge":3,"twin_halfedge":6,"tolerance":1e-8,"chart_seam":false},{"id":4,"geometry":{"kind":"Curve","curve":4,"range":{"lo":0.0,"hi":0.5}},"halfedge":4,"twin_halfedge":58,"tolerance":1e-8,"chart_seam":false},{"id":5,"geometry":{"kind":"Curve","curve":5,"range":{"lo":0.0,"hi":1.5}},"halfedge":5,"twin_halfedge":19,"tolerance":1e-8,"chart_seam":false},{"id":6,"geometry":{"kind":"Curve","curve":6,"range":{"lo":0.0,"hi":1.5}},"halfedge":7,"twin_halfedge":10,"tolerance":1e-8,"chart_seam":false},{"id":7,"geometry":{"kind":"Curve","curve":7,"range":{"lo":0.0,"hi":1.5}},"halfedge":8,"twin_halfedge":34,"tolerance":1e-8,"chart_seam":false},{"id":8,"geometry":{"kind":"Curve","curve":8,"range":{"lo":0.0,"hi":0.5}},"halfedge":9,"twin_halfedge":63,"tolerance":1e-8,"chart_seam":false},{"id":9,"geometry":{"kind":"Curve","curve":9,"range":{"lo":0.0,"hi":0.5}},"halfedge":12,"twin_halfedge":23,"tolerance":1e-8,"chart_seam":false},{"id":10,"geometry":{"kind":"Curve","curve":10,"range":{"lo":0.0,"hi":2.5}},"halfedge":13,"twin_halfedge":39,"tolerance":1e-8,"chart_seam":false},{"id":11,"geometry":{"kind":"Curve","curve":11,"range":{"lo":0.0,"hi":2.5}},"halfedge":15,"twin_halfedge":18,"tolerance":1e-8,"chart_seam":false},{"id":12,"geometry":{"kind":"Curve","curve":12,"range":{"lo":0.0,"hi":2.5}},"halfedge":16,"twin_halfedge":70,"tolerance":1e-8,"chart_seam":false},{"id":13,"geometry":{"kind":"Curve","curve":13,"range":{"lo":0.0,"hi":1.5}},"halfedge":17,"twin_halfedge":20,"tolerance":1e-8,"chart_seam":false},{"id":14,"geometry":{"kind":"Curve","curve":14,"range":{"lo":0.0,"hi":0.5}},"halfedge":21,"twin_halfedge":79,"tolerance":1e-8,"chart_seam":false},{"id":15,"geometry":{"kind":"Curve","curve":15,"range":{"lo":0.0,"hi":1.5}},"halfedge":22,"twin_halfedge":48,"tolerance":1e-8,"chart_seam":false},{"id":16,"geometry":{"kind":"Curve","curve":16,"range":{"lo":0.0,"hi":2.5}},"halfedge":24,"twin_halfedge":38,"tolerance":1e-8,"chart_seam":false},{"id":17,"geometry":{"kind":"Curve","curve":17,"range":{"lo":0.0,"hi":0.5}},"halfedge":25,"twin_halfedge":28,"tolerance":1e-8,"chart_seam":false},{"id":18,"geometry":{"kind":"Curve","curve":18,"range":{"lo":0.0,"hi":2.5}},"halfedge":26,"twin_halfedge":35,"tolerance":1e-8,"chart_seam":false},{"id":19,"geometry":{"kind":"Curve","curve":19,"range":{"lo":0.0,"hi":1.5}},"halfedge":29,"twin_halfedge":47,"tolerance":1e-8,"chart_seam":false},{"id":20,"geometry":{"kind":"Curve","curve":20,"range":{"lo":0.0,"hi":0.5}},"halfedge":30,"twin_halfedge":84,"tolerance":1e-8,"chart_seam":false},{"id":21,"geometry":{"kind":"Curve","curve":21,"range":{"lo":0.0,"hi":1.5}},"halfedge":31,"twin_halfedge":32,"tolerance":1e-8,"chart_seam":false},{"id":22,"geometry":{"kind":"Curve","curve":22,"range":{"lo":0.0,"hi":2.5}},"halfedge":33,"twin_halfedge":91,"tolerance":1e-8,"chart_seam":false},{"id":23,"geometry":{"kind":"Curve","curve":23,"range":{"lo":0.0,"hi":2.5}},"halfedge":36,"twin_halfedge":51,"tolerance":1e-8,"chart_seam":false},{"id":24,"geometry":{"kind":"Curve","curve":24,"range":{"lo":0.0,"hi":2.5}},"halfedge":37,"twin_halfedge":44,"tolerance":1e-8,"chart_seam":false},{"id":25,"geometry":{"kind":"Curve","curve":25,"range":{"lo":0.0,"hi":2.5}},"halfedge":40,"twin_halfedge":95,"tolerance":1e-8,"chart_seam":false},{"id":26,"geometry":{"kind":"Curve","curve":26,"range":{"lo":0.0,"hi":2.5}},"halfedge":41,"twin_halfedge":46,"tolerance":1e-8,"chart_seam":false},{"id":27,"geometry":{"kind":"Curve","curve":27,"range":{"lo":0.0,"hi":2.5}},"halfedge":42,"twin_halfedge":49,"tolerance":1e-8,"chart_seam":false},{"id":28,"geometry":{"kind":"Curve","curve":28,"range":{"lo":0.0,"hi":2.5}},"halfedge":43,"twin_halfedge":72,"tolerance":1e-8,"chart_seam":false},{"id":29,"geometry":{"kind":"Curve","curve":29,"range":{"lo":0.0,"hi":1.5}},"halfedge":45,"twin_halfedge":50,"tolerance":1e-8,"chart_seam":false},{"id":30,"geometry":{"kind":"Curve","curve":30,"range":{"lo":0.0,"hi":0.5}},"halfedge":52,"twin_halfedge":61,"tolerance":1e-8,"chart_seam":false},{"id":31,"geometry":{"kind":"Curve","curve":31,"range":{"lo":0.0,"hi":0.5}},"halfedge":53,"twin_halfedge":83,"tolerance":1e-8,"chart_seam":false},{"id":32,"geometry":{"kind":"Curve","curve":32,"range":{"lo":0.0,"hi":0.5}},"halfedge":54,"twin_halfedge":64,"tolerance":1e-8,"chart_seam":false},{"id":33,"geometry":{"kind":"Curve","curve":33,"range":{"lo":0.0,"hi":0.5}},"halfedge":55,"twin_halfedge":56,"tolerance":1e-8,"chart_seam":false},{"id":34,"geometry":{"kind":"Curve","curve":34,"range":{"lo":0.0,"hi":1.5}},"halfedge":57,"twin_halfedge":71,"tolerance":1e-8,"chart_seam":false},{"id":35,"geometry":{"kind":"Curve","curve":35,"range":{"lo":0.0,"hi":1.5}},"halfedge":59,"twin_halfedge":62,"tolerance":1e-8,"chart_seam":false},{"id":36,"geometry":{"kind":"Curve","curve":36,"range":{"lo":0.0,"hi":1.5}},"halfedge":60,"twin_halfedge":90,"tolerance":1e-8,"chart_seam":false},{"id":37,"geometry":{"kind":"Curve","curve":37,"range":{"lo":0.0,"hi":2.5}},"halfedge":65,"twin_halfedge":74,"tolerance":1e-8,"chart_seam":false},{"id":38,"geometry":{"kind":"Curve","curve":38,"range":{"lo":0.0,"hi":0.5}},"halfedge":66,"twin_halfedge":77,"tolerance":1e-8,"chart_seam":false},{"id":39,"geometry":{"kind":"Curve","curve":39,"range":{"lo":0.0,"hi":2.5}},"halfedge":67,"twin_halfedge":68,"tolerance":1e-8,"chart_seam":false},{"id":40,"geometry":{"kind":"Curve","curve":40,"range":{"lo":0.0,"hi":1.5}},"halfedge":69,"twin_halfedge":76,"tolerance":1e-8,"chart_seam":false},{"id":41,"geometry":{"kind":"Curve","curve":41,"range":{"lo":0.0,"hi":1.5}},"halfedge":73,"twin_halfedge":78,"tolerance":1e-8,"chart_seam":false},{"id":42,"geometry":{"kind":"Curve","curve":42,"range":{"lo":0.0,"hi":1.5}},"halfedge":75,"twin_halfedge":92,"tolerance":1e-8,"chart_seam":false},{"id":43,"geometry":{"kind":"Curve","curve":43,"range":{"lo":0.0,"hi":2.5}},"halfedge":80,"twin_halfedge":89,"tolerance":1e-8,"chart_seam":false},{"id":44,"geometry":{"kind":"Curve","curve":44,"range":{"lo":0.0,"hi":0.5}},"halfedge":81,"twin_halfedge":86,"tolerance":1e-8,"chart_seam":false},{"id":45,"geometry":{"kind":"Curve","curve":45,"range":{"lo":0.0,"hi":2.5}},"halfedge":82,"twin_halfedge":93,"tolerance":1e-8,"chart_seam":false},{"id":46,"geometry":{"kind":"Curve","curve":46,"range":{"lo":0.0,"hi":1.5}},"halfedge":85,"twin_halfedge":94,"tolerance":1e-8,"chart_seam":false},{"id":47,"geometry":{"kind":"Curve","curve":47,"range":{"lo":0.0,"hi":1.5}},"halfedge":87,"twin_halfedge":88,"tolerance":1e-8,"chart_seam":false}],"halfedges":[{"id":0,"from":0,"to":1,"twin":14,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":1,"to":2,"twin":27,"next":2,"prev":0,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":2,"to":3,"twin":11,"next":3,"prev":1,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":3,"to":0,"twin":6,"next":0,"prev":2,"edge":3,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":4,"to":5,"twin":58,"next":5,"prev":7,"edge":4,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":5,"to":0,"twin":19,"next":6,"prev":4,"edge":5,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":5,"periodic_lift":[0,0]}},{"id":6,"from":0,"to":3,"twin":3,"next":7,"prev":5,"edge":3,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":6,"periodic_lift":[0,0]}},{"id":7,"from":3,"to":4,"twin":10,"next":4,"prev":6,"edge":6,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":7,"periodic_lift":[0,0]}},{"id":8,"from":2,"to":6,"twin":34,"next":9,"prev":11,"edge":7,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":8,"periodic_lift":[0,0]}},{"id":9,"from":6,"to":4,"twin":63,"next":10,"prev":8,"edge":8,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":9,"periodic_lift":[0,0]}},{"id":10,"from":4,"to":3,"twin":7,"next":11,"prev":9,"edge":6,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":10,"periodic_lift":[0,0]}},{"id":11,"from":3,"to":2,"twin":2,"next":8,"prev":10,"edge":2,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":11,"periodic_lift":[0,0]}},{"id":12,"from":7,"to":8,"twin":23,"next":13,"prev":15,"edge":9,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":12,"periodic_lift":[0,0]}},{"id":13,"from":8,"to":1,"twin":39,"next":14,"prev":12,"edge":10,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":13,"periodic_lift":[0,0]}},{"id":14,"from":1,"to":0,"twin":0,"next":15,"prev":13,"edge":0,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":14,"periodic_lift":[0,0]}},{"id":15,"from":0,"to":7,"twin":18,"next":12,"prev":14,"edge":11,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":15,"periodic_lift":[0,0]}},{"id":16,"from":5,"to":9,"twin":70,"next":17,"prev":19,"edge":12,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":16,"periodic_lift":[0,0]}},{"id":17,"from":9,"to":7,"twin":20,"next":18,"prev":16,"edge":13,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":17,"periodic_lift":[0,0]}},{"id":18,"from":7,"to":0,"twin":15,"next":19,"prev":17,"edge":11,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":18,"periodic_lift":[0,0]}},{"id":19,"from":0,"to":5,"twin":5,"next":16,"prev":18,"edge":5,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":19,"periodic_lift":[0,0]}},{"id":20,"from":7,"to":9,"twin":17,"next":21,"prev":23,"edge":13,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":20,"periodic_lift":[0,0]}},{"id":21,"from":9,"to":10,"twin":79,"next":22,"prev":20,"edge":14,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":21,"periodic_lift":[0,0]}},{"id":22,"from":10,"to":8,"twin":48,"next":23,"prev":21,"edge":15,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":22,"periodic_lift":[0,0]}},{"id":23,"from":8,"to":7,"twin":12,"next":20,"prev":22,"edge":9,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":23,"periodic_lift":[0,0]}},{"id":24,"from":1,"to":11,"twin":38,"next":25,"prev":27,"edge":16,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":24,"periodic_lift":[0,0]}},{"id":25,"from":11,"to":12,"twin":28,"next":26,"prev":24,"edge":17,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":25,"periodic_lift":[0,0]}},{"id":26,"from":12,"to":2,"twin":35,"next":27,"prev":25,"edge":18,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":26,"periodic_lift":[0,0]}},{"id":27,"from":2,"to":1,"twin":1,"next":24,"prev":26,"edge":1,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":27,"periodic_lift":[0,0]}},{"id":28,"from":12,"to":11,"twin":25,"next":29,"prev":31,"edge":17,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":28,"periodic_lift":[0,0]}},{"id":29,"from":11,"to":13,"twin":47,"next":30,"prev":28,"edge":19,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":29,"periodic_lift":[0,0]}},{"id":30,"from":13,"to":14,"twin":84,"next":31,"prev":29,"edge":20,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":30,"periodic_lift":[0,0]}},{"id":31,"from":14,"to":12,"twin":32,"next":28,"prev":30,"edge":21,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":31,"periodic_lift":[0,0]}},{"id":32,"from":12,"to":14,"twin":31,"next":33,"prev":35,"edge":21,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":32,"periodic_lift":[0,0]}},{"id":33,"from":14,"to":6,"twin":91,"next":34,"prev":32,"edge":22,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":33,"periodic_lift":[0,0]}},{"id":34,"from":6,"to":2,"twin":8,"next":35,"prev":33,"edge":7,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":34,"periodic_lift":[0,0]}},{"id":35,"from":2,"to":12,"twin":26,"next":32,"prev":34,"edge":18,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":35,"periodic_lift":[0,0]}},{"id":36,"from":8,"to":15,"twin":51,"next":37,"prev":39,"edge":23,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":36,"periodic_lift":[0,0]}},{"id":37,"from":15,"to":11,"twin":44,"next":38,"prev":36,"edge":24,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":37,"periodic_lift":[0,0]}},{"id":38,"from":11,"to":1,"twin":24,"next":39,"prev":37,"edge":16,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":38,"periodic_lift":[0,0]}},{"id":39,"from":1,"to":8,"twin":13,"next":36,"prev":38,"edge":10,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":39,"periodic_lift":[0,0]}},{"id":40,"from":16,"to":13,"twin":95,"next":41,"prev":43,"edge":25,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":40,"periodic_lift":[0,0]}},{"id":41,"from":13,"to":17,"twin":46,"next":42,"prev":40,"edge":26,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":41,"periodic_lift":[0,0]}},{"id":42,"from":17,"to":10,"twin":49,"next":43,"prev":41,"edge":27,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":42,"periodic_lift":[0,0]}},{"id":43,"from":10,"to":16,"twin":72,"next":40,"prev":42,"edge":28,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":43,"periodic_lift":[0,0]}},{"id":44,"from":11,"to":15,"twin":37,"next":45,"prev":47,"edge":24,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":44,"periodic_lift":[0,0]}},{"id":45,"from":15,"to":17,"twin":50,"next":46,"prev":44,"edge":29,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":45,"periodic_lift":[0,0]}},{"id":46,"from":17,"to":13,"twin":41,"next":47,"prev":45,"edge":26,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":46,"periodic_lift":[0,0]}},{"id":47,"from":13,"to":11,"twin":29,"next":44,"prev":46,"edge":19,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":47,"periodic_lift":[0,0]}},{"id":48,"from":8,"to":10,"twin":22,"next":49,"prev":51,"edge":15,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":48,"periodic_lift":[0,0]}},{"id":49,"from":10,"to":17,"twin":42,"next":50,"prev":48,"edge":27,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":49,"periodic_lift":[0,0]}},{"id":50,"from":17,"to":15,"twin":45,"next":51,"prev":49,"edge":29,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":50,"periodic_lift":[0,0]}},{"id":51,"from":15,"to":8,"twin":36,"next":48,"prev":50,"edge":23,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":51,"periodic_lift":[0,0]}},{"id":52,"from":18,"to":19,"twin":61,"next":53,"prev":55,"edge":30,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":52,"periodic_lift":[0,0]}},{"id":53,"from":19,"to":20,"twin":83,"next":54,"prev":52,"edge":31,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":53,"periodic_lift":[0,0]}},{"id":54,"from":20,"to":21,"twin":64,"next":55,"prev":53,"edge":32,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":54,"periodic_lift":[0,0]}},{"id":55,"from":21,"to":18,"twin":56,"next":52,"prev":54,"edge":33,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":55,"periodic_lift":[0,0]}},{"id":56,"from":18,"to":21,"twin":55,"next":57,"prev":59,"edge":33,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":56,"periodic_lift":[0,0]}},{"id":57,"from":21,"to":5,"twin":71,"next":58,"prev":56,"edge":34,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":57,"periodic_lift":[0,0]}},{"id":58,"from":5,"to":4,"twin":4,"next":59,"prev":57,"edge":4,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":58,"periodic_lift":[0,0]}},{"id":59,"from":4,"to":18,"twin":62,"next":56,"prev":58,"edge":35,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":59,"periodic_lift":[0,0]}},{"id":60,"from":6,"to":19,"twin":90,"next":61,"prev":63,"edge":36,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":60,"periodic_lift":[0,0]}},{"id":61,"from":19,"to":18,"twin":52,"next":62,"prev":60,"edge":30,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":61,"periodic_lift":[0,0]}},{"id":62,"from":18,"to":4,"twin":59,"next":63,"prev":61,"edge":35,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":62,"periodic_lift":[0,0]}},{"id":63,"from":4,"to":6,"twin":9,"next":60,"prev":62,"edge":8,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":63,"periodic_lift":[0,0]}},{"id":64,"from":21,"to":20,"twin":54,"next":65,"prev":67,"edge":32,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":64,"periodic_lift":[0,0]}},{"id":65,"from":20,"to":22,"twin":74,"next":66,"prev":64,"edge":37,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":65,"periodic_lift":[0,0]}},{"id":66,"from":22,"to":23,"twin":77,"next":67,"prev":65,"edge":38,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":66,"periodic_lift":[0,0]}},{"id":67,"from":23,"to":21,"twin":68,"next":64,"prev":66,"edge":39,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":67,"periodic_lift":[0,0]}},{"id":68,"from":21,"to":23,"twin":67,"next":69,"prev":71,"edge":39,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":68,"periodic_lift":[0,0]}},{"id":69,"from":23,"to":9,"twin":76,"next":70,"prev":68,"edge":40,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":69,"periodic_lift":[0,0]}},{"id":70,"from":9,"to":5,"twin":16,"next":71,"prev":69,"edge":12,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":70,"periodic_lift":[0,0]}},{"id":71,"from":5,"to":21,"twin":57,"next":68,"prev":70,"edge":34,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":71,"periodic_lift":[0,0]}},{"id":72,"from":16,"to":10,"twin":43,"next":73,"prev":75,"edge":28,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":72,"periodic_lift":[0,0]}},{"id":73,"from":10,"to":22,"twin":78,"next":74,"prev":72,"edge":41,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":73,"periodic_lift":[0,0]}},{"id":74,"from":22,"to":20,"twin":65,"next":75,"prev":73,"edge":37,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":74,"periodic_lift":[0,0]}},{"id":75,"from":20,"to":16,"twin":92,"next":72,"prev":74,"edge":42,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":75,"periodic_lift":[0,0]}},{"id":76,"from":9,"to":23,"twin":69,"next":77,"prev":79,"edge":40,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":76,"periodic_lift":[0,0]}},{"id":77,"from":23,"to":22,"twin":66,"next":78,"prev":76,"edge":38,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":77,"periodic_lift":[0,0]}},{"id":78,"from":22,"to":10,"twin":73,"next":79,"prev":77,"edge":41,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":78,"periodic_lift":[0,0]}},{"id":79,"from":10,"to":9,"twin":21,"next":76,"prev":78,"edge":14,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":79,"periodic_lift":[0,0]}},{"id":80,"from":19,"to":24,"twin":89,"next":81,"prev":83,"edge":43,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":80,"periodic_lift":[0,0]}},{"id":81,"from":24,"to":25,"twin":86,"next":82,"prev":80,"edge":44,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":81,"periodic_lift":[0,0]}},{"id":82,"from":25,"to":20,"twin":93,"next":83,"prev":81,"edge":45,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":82,"periodic_lift":[0,0]}},{"id":83,"from":20,"to":19,"twin":53,"next":80,"prev":82,"edge":31,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":83,"periodic_lift":[0,0]}},{"id":84,"from":14,"to":13,"twin":30,"next":85,"prev":87,"edge":20,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":84,"periodic_lift":[0,0]}},{"id":85,"from":13,"to":25,"twin":94,"next":86,"prev":84,"edge":46,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":85,"periodic_lift":[0,0]}},{"id":86,"from":25,"to":24,"twin":81,"next":87,"prev":85,"edge":44,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":86,"periodic_lift":[0,0]}},{"id":87,"from":24,"to":14,"twin":88,"next":84,"prev":86,"edge":47,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":87,"periodic_lift":[0,0]}},{"id":88,"from":14,"to":24,"twin":87,"next":89,"prev":91,"edge":47,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":88,"periodic_lift":[0,0]}},{"id":89,"from":24,"to":19,"twin":80,"next":90,"prev":88,"edge":43,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":89,"periodic_lift":[0,0]}},{"id":90,"from":19,"to":6,"twin":60,"next":91,"prev":89,"edge":36,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":90,"periodic_lift":[0,0]}},{"id":91,"from":6,"to":14,"twin":33,"next":88,"prev":90,"edge":22,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":91,"periodic_lift":[0,0]}},{"id":92,"from":16,"to":20,"twin":75,"next":93,"prev":95,"edge":42,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":92,"periodic_lift":[0,0]}},{"id":93,"from":20,"to":25,"twin":82,"next":94,"prev":92,"edge":45,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":93,"periodic_lift":[0,0]}},{"id":94,"from":25,"to":13,"twin":85,"next":95,"prev":93,"edge":46,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":94,"periodic_lift":[0,0]}},{"id":95,"from":13,"to":16,"twin":40,"next":92,"prev":94,"edge":25,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":95,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false},{"id":2,"start_halfedge":8,"face_ref":2,"is_hole":false},{"id":3,"start_halfedge":12,"face_ref":3,"is_hole":false},{"id":4,"start_halfedge":16,"face_ref":4,"is_hole":false},{"id":5,"start_halfedge":20,"face_ref":5,"is_hole":false},{"id":6,"start_halfedge":24,"face_ref":6,"is_hole":false},{"id":7,"start_halfedge":28,"face_ref":7,"is_hole":false},{"id":8,"start_halfedge":32,"face_ref":8,"is_hole":false},{"id":9,"start_halfedge":36,"face_ref":9,"is_hole":false},{"id":10,"start_halfedge":40,"face_ref":10,"is_hole":false},{"id":11,"start_halfedge":44,"face_ref":11,"is_hole":false},{"id":12,"start_halfedge":48,"face_ref":12,"is_hole":false},{"id":13,"start_halfedge":52,"face_ref":13,"is_hole":false},{"id":14,"start_halfedge":56,"face_ref":14,"is_hole":false},{"id":15,"start_halfedge":60,"face_ref":15,"is_hole":false},{"id":16,"start_halfedge":64,"face_ref":16,"is_hole":false},{"id":17,"start_halfedge":68,"face_ref":17,"is_hole":false},{"id":18,"start_halfedge":72,"face_ref":18,"is_hole":false},{"id":19,"start_halfedge":76,"face_ref":19,"is_hole":false},{"id":20,"start_halfedge":80,"face_ref":20,"is_hole":false},{"id":21,"start_halfedge":84,"face_ref":21,"is_hole":false},{"id":22,"start_halfedge":88,"face_ref":22,"is_hole":false},{"id":23,"start_halfedge":92,"face_ref":23,"is_hole":false}],"faces":[{"id":0,"key":"left-0","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":-1e-8,"hi":0.50000001}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":1,"key":"front-1","surface":1,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":-1e-8,"hi":0.50000001}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":2,"key":"bottom-2","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":-1e-8,"hi":1.50000001}],"outer":2,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":3,"key":"left-3","surface":3,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":0.50000001}],"outer":3,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":4,"key":"front-4","surface":4,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":0.49999999,"hi":3.00000001}],"outer":4,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":5,"key":"top-5","surface":5,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":-1e-8,"hi":0.50000001}],"outer":5,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":6,"key":"left-6","surface":6,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":0.49999999,"hi":3.00000001}],"outer":6,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":7,"key":"back-7","surface":7,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":-1e-8,"hi":1.50000001}],"outer":7,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":8,"key":"bottom-8","surface":8,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.50000001}],"outer":8,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":9,"key":"left-9","surface":9,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":0.49999999,"hi":3.00000001}],"outer":9,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":10,"key":"left-10","surface":10,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.50000001},{"lo":-1e-8,"hi":2.50000001}],"outer":10,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"left","face":2}],"role":"Cut","reversed":true}},{"id":11,"key":"back-11","surface":11,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.50000001}],"outer":11,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":12,"key":"top-12","surface":12,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":0.49999999,"hi":3.00000001}],"outer":12,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":13,"key":"right-13","surface":13,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":-1e-8,"hi":0.50000001}],"outer":13,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":14,"key":"front-14","surface":14,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":0.50000001}],"outer":14,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":15,"key":"bottom-15","surface":15,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":1.49999999,"hi":3.00000001}],"outer":15,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":16,"key":"right-16","surface":16,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":0.49999999,"hi":3.00000001}],"outer":16,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":17,"key":"front-17","surface":17,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.49999999,"hi":3.00000001},{"lo":0.49999999,"hi":3.00000001}],"outer":17,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":18,"key":"front-18","surface":18,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":-1e-8,"hi":2.50000001}],"outer":18,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"front","face":4}],"role":"Cut","reversed":true}},{"id":19,"key":"top-19","surface":19,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":0.50000001}],"outer":19,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":20,"key":"right-20","surface":20,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":0.50000001}],"outer":20,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":21,"key":"back-21","surface":21,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":1.49999999,"hi":3.00000001}],"outer":21,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":22,"key":"bottom-22","surface":22,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":1.49999999,"hi":3.00000001}],"outer":22,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":23,"key":"bottom-23","surface":23,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.50000001},{"lo":-1e-8,"hi":1.50000001}],"outer":23,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"bottom","face":0}],"role":"Cut","reversed":true}}],"wires":[],"shells":[{"id":0,"faces":[0,3,6,2,1,5,9,4,7,8,15,14,19,12,11,17,21,22,13,16,18,10,23,20],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-cut.handlers.json b/main/opengeometry/tests/fixtures/cases/box-cut.handlers.json new file mode 100644 index 0000000..4b8564a --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cut.handlers.json @@ -0,0 +1 @@ +["boolean_boxes"] \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-cut.step.m b/main/opengeometry/tests/fixtures/cases/box-cut.step.m new file mode 100644 index 0000000..e758de4 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cut.step.m @@ -0,0 +1,1231 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('box-cut','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#24=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#28=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#34=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#35=AXIS2_PLACEMENT_3D('',#32,#33,#34); +#36=PLANE('',#35); +#37=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#38=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#39=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#40=AXIS2_PLACEMENT_3D('',#37,#38,#39); +#41=PLANE('',#40); +#42=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#43=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#44=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#45=AXIS2_PLACEMENT_3D('',#42,#43,#44); +#46=PLANE('',#45); +#47=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#48=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#49=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#50=AXIS2_PLACEMENT_3D('',#47,#48,#49); +#51=PLANE('',#50); +#52=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#53=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#54=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#55=AXIS2_PLACEMENT_3D('',#52,#53,#54); +#56=PLANE('',#55); +#57=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#58=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#59=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#60=AXIS2_PLACEMENT_3D('',#57,#58,#59); +#61=PLANE('',#60); +#62=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#63=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#64=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#65=AXIS2_PLACEMENT_3D('',#62,#63,#64); +#66=PLANE('',#65); +#67=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#68=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#69=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#70=AXIS2_PLACEMENT_3D('',#67,#68,#69); +#71=PLANE('',#70); +#72=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#73=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#74=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#75=AXIS2_PLACEMENT_3D('',#72,#73,#74); +#76=PLANE('',#75); +#77=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#78=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#79=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#80=AXIS2_PLACEMENT_3D('',#77,#78,#79); +#81=PLANE('',#80); +#82=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#83=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#84=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#85=AXIS2_PLACEMENT_3D('',#82,#83,#84); +#86=PLANE('',#85); +#87=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#88=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#90=AXIS2_PLACEMENT_3D('',#87,#88,#89); +#91=PLANE('',#90); +#92=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#93=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#94=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#95=AXIS2_PLACEMENT_3D('',#92,#93,#94); +#96=PLANE('',#95); +#97=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#98=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#99=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#100=AXIS2_PLACEMENT_3D('',#97,#98,#99); +#101=PLANE('',#100); +#102=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#103=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#104=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#105=AXIS2_PLACEMENT_3D('',#102,#103,#104); +#106=PLANE('',#105); +#107=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#108=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#109=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#110=AXIS2_PLACEMENT_3D('',#107,#108,#109); +#111=PLANE('',#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#113=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#114=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#115=AXIS2_PLACEMENT_3D('',#112,#113,#114); +#116=PLANE('',#115); +#117=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#118=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#119=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#120=AXIS2_PLACEMENT_3D('',#117,#118,#119); +#121=PLANE('',#120); +#122=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#123=VERTEX_POINT('',#122); +#124=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#125=VERTEX_POINT('',#124); +#126=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#127=VERTEX_POINT('',#126); +#128=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#129=VERTEX_POINT('',#128); +#130=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#131=VERTEX_POINT('',#130); +#132=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#133=VERTEX_POINT('',#132); +#134=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#135=VERTEX_POINT('',#134); +#136=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#137=VERTEX_POINT('',#136); +#138=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#139=VERTEX_POINT('',#138); +#140=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#141=VERTEX_POINT('',#140); +#142=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#143=VERTEX_POINT('',#142); +#144=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#145=VERTEX_POINT('',#144); +#146=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#147=VERTEX_POINT('',#146); +#148=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#149=VERTEX_POINT('',#148); +#150=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#151=VERTEX_POINT('',#150); +#152=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#153=VERTEX_POINT('',#152); +#154=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#155=VERTEX_POINT('',#154); +#156=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#157=VERTEX_POINT('',#156); +#158=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#159=VERTEX_POINT('',#158); +#160=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#161=VERTEX_POINT('',#160); +#162=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#163=VERTEX_POINT('',#162); +#164=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#165=VERTEX_POINT('',#164); +#166=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#167=VERTEX_POINT('',#166); +#168=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#169=VERTEX_POINT('',#168); +#170=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#171=VERTEX_POINT('',#170); +#172=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#173=VERTEX_POINT('',#172); +#174=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#175=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#176=VECTOR('',#175,1.00000000000000000E0); +#177=LINE('',#174,#176); +#178=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#179=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#180=VECTOR('',#179,1.00000000000000000E0); +#181=LINE('',#178,#180); +#182=DEFINITIONAL_REPRESENTATION('',(#181),#1); +#183=PCURVE('',#6,#182); +#184=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#185=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#186=VECTOR('',#185,1.00000000000000000E0); +#187=LINE('',#184,#186); +#188=DEFINITIONAL_REPRESENTATION('',(#187),#1); +#189=PCURVE('',#21,#188); +#190=SURFACE_CURVE('',#177,(#183,#189),.CURVE_3D.); +#191=EDGE_CURVE('',#123,#125,#190,.T.); +#192=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#193=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#194=VECTOR('',#193,1.00000000000000000E0); +#195=LINE('',#192,#194); +#196=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#197=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#198=VECTOR('',#197,1.00000000000000000E0); +#199=LINE('',#196,#198); +#200=DEFINITIONAL_REPRESENTATION('',(#199),#1); +#201=PCURVE('',#6,#200); +#202=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#203=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#204=VECTOR('',#203,1.00000000000000000E0); +#205=LINE('',#202,#204); +#206=DEFINITIONAL_REPRESENTATION('',(#205),#1); +#207=PCURVE('',#36,#206); +#208=SURFACE_CURVE('',#195,(#201,#207),.CURVE_3D.); +#209=EDGE_CURVE('',#127,#125,#208,.T.); +#210=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#211=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#212=VECTOR('',#211,1.00000000000000000E0); +#213=LINE('',#210,#212); +#214=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#215=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#216=VECTOR('',#215,1.00000000000000000E0); +#217=LINE('',#214,#216); +#218=DEFINITIONAL_REPRESENTATION('',(#217),#1); +#219=PCURVE('',#6,#218); +#220=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#221=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#222=VECTOR('',#221,1.00000000000000000E0); +#223=LINE('',#220,#222); +#224=DEFINITIONAL_REPRESENTATION('',(#223),#1); +#225=PCURVE('',#16,#224); +#226=SURFACE_CURVE('',#213,(#219,#225),.CURVE_3D.); +#227=EDGE_CURVE('',#129,#127,#226,.T.); +#228=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#229=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#230=VECTOR('',#229,1.00000000000000000E0); +#231=LINE('',#228,#230); +#232=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#233=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#234=VECTOR('',#233,1.00000000000000000E0); +#235=LINE('',#232,#234); +#236=DEFINITIONAL_REPRESENTATION('',(#235),#1); +#237=PCURVE('',#6,#236); +#238=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#239=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#240=VECTOR('',#239,1.00000000000000000E0); +#241=LINE('',#238,#240); +#242=DEFINITIONAL_REPRESENTATION('',(#241),#1); +#243=PCURVE('',#11,#242); +#244=SURFACE_CURVE('',#231,(#237,#243),.CURVE_3D.); +#245=EDGE_CURVE('',#129,#123,#244,.T.); +#246=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#247=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#248=VECTOR('',#247,1.00000000000000000E0); +#249=LINE('',#246,#248); +#250=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#251=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#252=VECTOR('',#251,1.00000000000000000E0); +#253=LINE('',#250,#252); +#254=DEFINITIONAL_REPRESENTATION('',(#253),#1); +#255=PCURVE('',#11,#254); +#256=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#257=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#258=VECTOR('',#257,1.00000000000000000E0); +#259=LINE('',#256,#258); +#260=DEFINITIONAL_REPRESENTATION('',(#259),#1); +#261=PCURVE('',#76,#260); +#262=SURFACE_CURVE('',#249,(#255,#261),.CURVE_3D.); +#263=EDGE_CURVE('',#131,#133,#262,.T.); +#264=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#265=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#266=VECTOR('',#265,1.00000000000000000E0); +#267=LINE('',#264,#266); +#268=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#269=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#270=VECTOR('',#269,1.00000000000000000E0); +#271=LINE('',#268,#270); +#272=DEFINITIONAL_REPRESENTATION('',(#271),#1); +#273=PCURVE('',#11,#272); +#274=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#275=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#276=VECTOR('',#275,1.00000000000000000E0); +#277=LINE('',#274,#276); +#278=DEFINITIONAL_REPRESENTATION('',(#277),#1); +#279=PCURVE('',#26,#278); +#280=SURFACE_CURVE('',#267,(#273,#279),.CURVE_3D.); +#281=EDGE_CURVE('',#123,#133,#280,.T.); +#282=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#283=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#284=VECTOR('',#283,1.00000000000000000E0); +#285=LINE('',#282,#284); +#286=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#287=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#288=VECTOR('',#287,1.00000000000000000E0); +#289=LINE('',#286,#288); +#290=DEFINITIONAL_REPRESENTATION('',(#289),#1); +#291=PCURVE('',#11,#290); +#292=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#293=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#294=VECTOR('',#293,1.00000000000000000E0); +#295=LINE('',#292,#294); +#296=DEFINITIONAL_REPRESENTATION('',(#295),#1); +#297=PCURVE('',#16,#296); +#298=SURFACE_CURVE('',#285,(#291,#297),.CURVE_3D.); +#299=EDGE_CURVE('',#129,#131,#298,.T.); +#300=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#301=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#302=VECTOR('',#301,1.00000000000000000E0); +#303=LINE('',#300,#302); +#304=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#305=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#306=VECTOR('',#305,1.00000000000000000E0); +#307=LINE('',#304,#306); +#308=DEFINITIONAL_REPRESENTATION('',(#307),#1); +#309=PCURVE('',#16,#308); +#310=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#311=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#312=VECTOR('',#311,1.00000000000000000E0); +#313=LINE('',#310,#312); +#314=DEFINITIONAL_REPRESENTATION('',(#313),#1); +#315=PCURVE('',#46,#314); +#316=SURFACE_CURVE('',#303,(#309,#315),.CURVE_3D.); +#317=EDGE_CURVE('',#127,#135,#316,.T.); +#318=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#319=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#320=VECTOR('',#319,1.00000000000000000E0); +#321=LINE('',#318,#320); +#322=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#323=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#324=VECTOR('',#323,1.00000000000000000E0); +#325=LINE('',#322,#324); +#326=DEFINITIONAL_REPRESENTATION('',(#325),#1); +#327=PCURVE('',#16,#326); +#328=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#329=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#330=VECTOR('',#329,1.00000000000000000E0); +#331=LINE('',#328,#330); +#332=DEFINITIONAL_REPRESENTATION('',(#331),#1); +#333=PCURVE('',#81,#332); +#334=SURFACE_CURVE('',#321,(#327,#333),.CURVE_3D.); +#335=EDGE_CURVE('',#131,#135,#334,.T.); +#336=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#337=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#338=VECTOR('',#337,1.00000000000000000E0); +#339=LINE('',#336,#338); +#340=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#341=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#342=VECTOR('',#341,1.00000000000000000E0); +#343=LINE('',#340,#342); +#344=DEFINITIONAL_REPRESENTATION('',(#343),#1); +#345=PCURVE('',#21,#344); +#346=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#347=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#348=VECTOR('',#347,1.00000000000000000E0); +#349=LINE('',#346,#348); +#350=DEFINITIONAL_REPRESENTATION('',(#349),#1); +#351=PCURVE('',#31,#350); +#352=SURFACE_CURVE('',#339,(#345,#351),.CURVE_3D.); +#353=EDGE_CURVE('',#137,#139,#352,.T.); +#354=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#355=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#356=VECTOR('',#355,1.00000000000000000E0); +#357=LINE('',#354,#356); +#358=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#359=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#360=VECTOR('',#359,1.00000000000000000E0); +#361=LINE('',#358,#360); +#362=DEFINITIONAL_REPRESENTATION('',(#361),#1); +#363=PCURVE('',#21,#362); +#364=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#365=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#366=VECTOR('',#365,1.00000000000000000E0); +#367=LINE('',#364,#366); +#368=DEFINITIONAL_REPRESENTATION('',(#367),#1); +#369=PCURVE('',#51,#368); +#370=SURFACE_CURVE('',#357,(#363,#369),.CURVE_3D.); +#371=EDGE_CURVE('',#125,#139,#370,.T.); +#372=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#373=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#374=VECTOR('',#373,1.00000000000000000E0); +#375=LINE('',#372,#374); +#376=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#377=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#378=VECTOR('',#377,1.00000000000000000E0); +#379=LINE('',#376,#378); +#380=DEFINITIONAL_REPRESENTATION('',(#379),#1); +#381=PCURVE('',#21,#380); +#382=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#383=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#384=VECTOR('',#383,1.00000000000000000E0); +#385=LINE('',#382,#384); +#386=DEFINITIONAL_REPRESENTATION('',(#385),#1); +#387=PCURVE('',#26,#386); +#388=SURFACE_CURVE('',#375,(#381,#387),.CURVE_3D.); +#389=EDGE_CURVE('',#123,#137,#388,.T.); +#390=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#391=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#392=VECTOR('',#391,1.00000000000000000E0); +#393=LINE('',#390,#392); +#394=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#395=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#396=VECTOR('',#395,1.00000000000000000E0); +#397=LINE('',#394,#396); +#398=DEFINITIONAL_REPRESENTATION('',(#397),#1); +#399=PCURVE('',#26,#398); +#400=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#401=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#402=VECTOR('',#401,1.00000000000000000E0); +#403=LINE('',#400,#402); +#404=DEFINITIONAL_REPRESENTATION('',(#403),#1); +#405=PCURVE('',#91,#404); +#406=SURFACE_CURVE('',#393,(#399,#405),.CURVE_3D.); +#407=EDGE_CURVE('',#133,#141,#406,.T.); +#408=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#409=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#410=VECTOR('',#409,1.00000000000000000E0); +#411=LINE('',#408,#410); +#412=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#413=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#414=VECTOR('',#413,1.00000000000000000E0); +#415=LINE('',#412,#414); +#416=DEFINITIONAL_REPRESENTATION('',(#415),#1); +#417=PCURVE('',#26,#416); +#418=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#419=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#420=VECTOR('',#419,1.00000000000000000E0); +#421=LINE('',#418,#420); +#422=DEFINITIONAL_REPRESENTATION('',(#421),#1); +#423=PCURVE('',#31,#422); +#424=SURFACE_CURVE('',#411,(#417,#423),.CURVE_3D.); +#425=EDGE_CURVE('',#137,#141,#424,.T.); +#426=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#427=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#428=VECTOR('',#427,1.00000000000000000E0); +#429=LINE('',#426,#428); +#430=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#431=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#432=VECTOR('',#431,1.00000000000000000E0); +#433=LINE('',#430,#432); +#434=DEFINITIONAL_REPRESENTATION('',(#433),#1); +#435=PCURVE('',#31,#434); +#436=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#437=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#438=VECTOR('',#437,1.00000000000000000E0); +#439=LINE('',#436,#438); +#440=DEFINITIONAL_REPRESENTATION('',(#439),#1); +#441=PCURVE('',#101,#440); +#442=SURFACE_CURVE('',#429,(#435,#441),.CURVE_3D.); +#443=EDGE_CURVE('',#141,#143,#442,.T.); +#444=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#445=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#446=VECTOR('',#445,1.00000000000000000E0); +#447=LINE('',#444,#446); +#448=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#449=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#450=VECTOR('',#449,1.00000000000000000E0); +#451=LINE('',#448,#450); +#452=DEFINITIONAL_REPRESENTATION('',(#451),#1); +#453=PCURVE('',#31,#452); +#454=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#455=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#456=VECTOR('',#455,1.00000000000000000E0); +#457=LINE('',#454,#456); +#458=DEFINITIONAL_REPRESENTATION('',(#457),#1); +#459=PCURVE('',#66,#458); +#460=SURFACE_CURVE('',#447,(#453,#459),.CURVE_3D.); +#461=EDGE_CURVE('',#139,#143,#460,.T.); +#462=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#463=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#464=VECTOR('',#463,1.00000000000000000E0); +#465=LINE('',#462,#464); +#466=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#467=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#468=VECTOR('',#467,1.00000000000000000E0); +#469=LINE('',#466,#468); +#470=DEFINITIONAL_REPRESENTATION('',(#469),#1); +#471=PCURVE('',#36,#470); +#472=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#473=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#474=VECTOR('',#473,1.00000000000000000E0); +#475=LINE('',#472,#474); +#476=DEFINITIONAL_REPRESENTATION('',(#475),#1); +#477=PCURVE('',#51,#476); +#478=SURFACE_CURVE('',#465,(#471,#477),.CURVE_3D.); +#479=EDGE_CURVE('',#125,#145,#478,.T.); +#480=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#481=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#482=VECTOR('',#481,1.00000000000000000E0); +#483=LINE('',#480,#482); +#484=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#485=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#486=VECTOR('',#485,1.00000000000000000E0); +#487=LINE('',#484,#486); +#488=DEFINITIONAL_REPRESENTATION('',(#487),#1); +#489=PCURVE('',#36,#488); +#490=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#491=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#492=VECTOR('',#491,1.00000000000000000E0); +#493=LINE('',#490,#492); +#494=DEFINITIONAL_REPRESENTATION('',(#493),#1); +#495=PCURVE('',#41,#494); +#496=SURFACE_CURVE('',#483,(#489,#495),.CURVE_3D.); +#497=EDGE_CURVE('',#147,#145,#496,.T.); +#498=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#499=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#500=VECTOR('',#499,1.00000000000000000E0); +#501=LINE('',#498,#500); +#502=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#503=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#504=VECTOR('',#503,1.00000000000000000E0); +#505=LINE('',#502,#504); +#506=DEFINITIONAL_REPRESENTATION('',(#505),#1); +#507=PCURVE('',#36,#506); +#508=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#509=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#510=VECTOR('',#509,1.00000000000000000E0); +#511=LINE('',#508,#510); +#512=DEFINITIONAL_REPRESENTATION('',(#511),#1); +#513=PCURVE('',#46,#512); +#514=SURFACE_CURVE('',#501,(#507,#513),.CURVE_3D.); +#515=EDGE_CURVE('',#127,#147,#514,.T.); +#516=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#517=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#518=VECTOR('',#517,1.00000000000000000E0); +#519=LINE('',#516,#518); +#520=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#521=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#522=VECTOR('',#521,1.00000000000000000E0); +#523=LINE('',#520,#522); +#524=DEFINITIONAL_REPRESENTATION('',(#523),#1); +#525=PCURVE('',#41,#524); +#526=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#527=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#528=VECTOR('',#527,1.00000000000000000E0); +#529=LINE('',#526,#528); +#530=DEFINITIONAL_REPRESENTATION('',(#529),#1); +#531=PCURVE('',#61,#530); +#532=SURFACE_CURVE('',#519,(#525,#531),.CURVE_3D.); +#533=EDGE_CURVE('',#145,#149,#532,.T.); +#534=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#535=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#536=VECTOR('',#535,1.00000000000000000E0); +#537=LINE('',#534,#536); +#538=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#539=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#540=VECTOR('',#539,1.00000000000000000E0); +#541=LINE('',#538,#540); +#542=DEFINITIONAL_REPRESENTATION('',(#541),#1); +#543=PCURVE('',#41,#542); +#544=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#545=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#546=VECTOR('',#545,1.00000000000000000E0); +#547=LINE('',#544,#546); +#548=DEFINITIONAL_REPRESENTATION('',(#547),#1); +#549=PCURVE('',#111,#548); +#550=SURFACE_CURVE('',#537,(#543,#549),.CURVE_3D.); +#551=EDGE_CURVE('',#151,#149,#550,.T.); +#552=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#553=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#554=VECTOR('',#553,1.00000000000000000E0); +#555=LINE('',#552,#554); +#556=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#557=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#558=VECTOR('',#557,1.00000000000000000E0); +#559=LINE('',#556,#558); +#560=DEFINITIONAL_REPRESENTATION('',(#559),#1); +#561=PCURVE('',#41,#560); +#562=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#563=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#564=VECTOR('',#563,1.00000000000000000E0); +#565=LINE('',#562,#564); +#566=DEFINITIONAL_REPRESENTATION('',(#565),#1); +#567=PCURVE('',#46,#566); +#568=SURFACE_CURVE('',#555,(#561,#567),.CURVE_3D.); +#569=EDGE_CURVE('',#147,#151,#568,.T.); +#570=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#571=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#572=VECTOR('',#571,1.00000000000000000E0); +#573=LINE('',#570,#572); +#574=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#575=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#576=VECTOR('',#575,1.00000000000000000E0); +#577=LINE('',#574,#576); +#578=DEFINITIONAL_REPRESENTATION('',(#577),#1); +#579=PCURVE('',#46,#578); +#580=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#581=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#582=VECTOR('',#581,1.00000000000000000E0); +#583=LINE('',#580,#582); +#584=DEFINITIONAL_REPRESENTATION('',(#583),#1); +#585=PCURVE('',#116,#584); +#586=SURFACE_CURVE('',#573,(#579,#585),.CURVE_3D.); +#587=EDGE_CURVE('',#135,#151,#586,.T.); +#588=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#589=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#590=VECTOR('',#589,1.00000000000000000E0); +#591=LINE('',#588,#590); +#592=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1)); +#593=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#594=VECTOR('',#593,1.00000000000000000E0); +#595=LINE('',#592,#594); +#596=DEFINITIONAL_REPRESENTATION('',(#595),#1); +#597=PCURVE('',#51,#596); +#598=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#599=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#600=VECTOR('',#599,1.00000000000000000E0); +#601=LINE('',#598,#600); +#602=DEFINITIONAL_REPRESENTATION('',(#601),#1); +#603=PCURVE('',#66,#602); +#604=SURFACE_CURVE('',#591,(#597,#603),.CURVE_3D.); +#605=EDGE_CURVE('',#139,#153,#604,.T.); +#606=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#607=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#608=VECTOR('',#607,1.00000000000000000E0); +#609=LINE('',#606,#608); +#610=CARTESIAN_POINT('',(5.00000000000000000E-1,3.00000000000000000E0)); +#611=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#612=VECTOR('',#611,1.00000000000000000E0); +#613=LINE('',#610,#612); +#614=DEFINITIONAL_REPRESENTATION('',(#613),#1); +#615=PCURVE('',#51,#614); +#616=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#617=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#618=VECTOR('',#617,1.00000000000000000E0); +#619=LINE('',#616,#618); +#620=DEFINITIONAL_REPRESENTATION('',(#619),#1); +#621=PCURVE('',#61,#620); +#622=SURFACE_CURVE('',#609,(#615,#621),.CURVE_3D.); +#623=EDGE_CURVE('',#145,#153,#622,.T.); +#624=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#625=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#626=VECTOR('',#625,1.00000000000000000E0); +#627=LINE('',#624,#626); +#628=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#629=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#630=VECTOR('',#629,1.00000000000000000E0); +#631=LINE('',#628,#630); +#632=DEFINITIONAL_REPRESENTATION('',(#631),#1); +#633=PCURVE('',#56,#632); +#634=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#635=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#636=VECTOR('',#635,1.00000000000000000E0); +#637=LINE('',#634,#636); +#638=DEFINITIONAL_REPRESENTATION('',(#637),#1); +#639=PCURVE('',#121,#638); +#640=SURFACE_CURVE('',#627,(#633,#639),.CURVE_3D.); +#641=EDGE_CURVE('',#155,#149,#640,.T.); +#642=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#643=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#644=VECTOR('',#643,1.00000000000000000E0); +#645=LINE('',#642,#644); +#646=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#647=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#648=VECTOR('',#647,1.00000000000000000E0); +#649=LINE('',#646,#648); +#650=DEFINITIONAL_REPRESENTATION('',(#649),#1); +#651=PCURVE('',#56,#650); +#652=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#653=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#654=VECTOR('',#653,1.00000000000000000E0); +#655=LINE('',#652,#654); +#656=DEFINITIONAL_REPRESENTATION('',(#655),#1); +#657=PCURVE('',#61,#656); +#658=SURFACE_CURVE('',#645,(#651,#657),.CURVE_3D.); +#659=EDGE_CURVE('',#149,#157,#658,.T.); +#660=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#661=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#662=VECTOR('',#661,1.00000000000000000E0); +#663=LINE('',#660,#662); +#664=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#665=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#666=VECTOR('',#665,1.00000000000000000E0); +#667=LINE('',#664,#666); +#668=DEFINITIONAL_REPRESENTATION('',(#667),#1); +#669=PCURVE('',#56,#668); +#670=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#671=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#672=VECTOR('',#671,1.00000000000000000E0); +#673=LINE('',#670,#672); +#674=DEFINITIONAL_REPRESENTATION('',(#673),#1); +#675=PCURVE('',#66,#674); +#676=SURFACE_CURVE('',#663,(#669,#675),.CURVE_3D.); +#677=EDGE_CURVE('',#143,#157,#676,.T.); +#678=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#679=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#680=VECTOR('',#679,1.00000000000000000E0); +#681=LINE('',#678,#680); +#682=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#683=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#684=VECTOR('',#683,1.00000000000000000E0); +#685=LINE('',#682,#684); +#686=DEFINITIONAL_REPRESENTATION('',(#685),#1); +#687=PCURVE('',#56,#686); +#688=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#689=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#690=VECTOR('',#689,1.00000000000000000E0); +#691=LINE('',#688,#690); +#692=DEFINITIONAL_REPRESENTATION('',(#691),#1); +#693=PCURVE('',#96,#692); +#694=SURFACE_CURVE('',#681,(#687,#693),.CURVE_3D.); +#695=EDGE_CURVE('',#155,#143,#694,.T.); +#696=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#697=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#698=VECTOR('',#697,1.00000000000000000E0); +#699=LINE('',#696,#698); +#700=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#701=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#702=VECTOR('',#701,1.00000000000000000E0); +#703=LINE('',#700,#702); +#704=DEFINITIONAL_REPRESENTATION('',(#703),#1); +#705=PCURVE('',#61,#704); +#706=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#707=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#708=VECTOR('',#707,1.00000000000000000E0); +#709=LINE('',#706,#708); +#710=DEFINITIONAL_REPRESENTATION('',(#709),#1); +#711=PCURVE('',#66,#710); +#712=SURFACE_CURVE('',#699,(#705,#711),.CURVE_3D.); +#713=EDGE_CURVE('',#153,#157,#712,.T.); +#714=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#715=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#716=VECTOR('',#715,1.00000000000000000E0); +#717=LINE('',#714,#716); +#718=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#719=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#720=VECTOR('',#719,1.00000000000000000E0); +#721=LINE('',#718,#720); +#722=DEFINITIONAL_REPRESENTATION('',(#721),#1); +#723=PCURVE('',#71,#722); +#724=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#725=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#726=VECTOR('',#725,1.00000000000000000E0); +#727=LINE('',#724,#726); +#728=DEFINITIONAL_REPRESENTATION('',(#727),#1); +#729=PCURVE('',#81,#728); +#730=SURFACE_CURVE('',#717,(#723,#729),.CURVE_3D.); +#731=EDGE_CURVE('',#159,#161,#730,.T.); +#732=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#733=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#734=VECTOR('',#733,1.00000000000000000E0); +#735=LINE('',#732,#734); +#736=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#737=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#738=VECTOR('',#737,1.00000000000000000E0); +#739=LINE('',#736,#738); +#740=DEFINITIONAL_REPRESENTATION('',(#739),#1); +#741=PCURVE('',#71,#740); +#742=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#743=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#744=VECTOR('',#743,1.00000000000000000E0); +#745=LINE('',#742,#744); +#746=DEFINITIONAL_REPRESENTATION('',(#745),#1); +#747=PCURVE('',#106,#746); +#748=SURFACE_CURVE('',#735,(#741,#747),.CURVE_3D.); +#749=EDGE_CURVE('',#161,#163,#748,.T.); +#750=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#751=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#752=VECTOR('',#751,1.00000000000000000E0); +#753=LINE('',#750,#752); +#754=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#755=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#756=VECTOR('',#755,1.00000000000000000E0); +#757=LINE('',#754,#756); +#758=DEFINITIONAL_REPRESENTATION('',(#757),#1); +#759=PCURVE('',#71,#758); +#760=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#761=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#762=VECTOR('',#761,1.00000000000000000E0); +#763=LINE('',#760,#762); +#764=DEFINITIONAL_REPRESENTATION('',(#763),#1); +#765=PCURVE('',#86,#764); +#766=SURFACE_CURVE('',#753,(#759,#765),.CURVE_3D.); +#767=EDGE_CURVE('',#165,#163,#766,.T.); +#768=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#769=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#770=VECTOR('',#769,1.00000000000000000E0); +#771=LINE('',#768,#770); +#772=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#773=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#774=VECTOR('',#773,1.00000000000000000E0); +#775=LINE('',#772,#774); +#776=DEFINITIONAL_REPRESENTATION('',(#775),#1); +#777=PCURVE('',#71,#776); +#778=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#779=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#780=VECTOR('',#779,1.00000000000000000E0); +#781=LINE('',#778,#780); +#782=DEFINITIONAL_REPRESENTATION('',(#781),#1); +#783=PCURVE('',#76,#782); +#784=SURFACE_CURVE('',#771,(#777,#783),.CURVE_3D.); +#785=EDGE_CURVE('',#159,#165,#784,.T.); +#786=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#787=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#788=VECTOR('',#787,1.00000000000000000E0); +#789=LINE('',#786,#788); +#790=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#791=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#792=VECTOR('',#791,1.00000000000000000E0); +#793=LINE('',#790,#792); +#794=DEFINITIONAL_REPRESENTATION('',(#793),#1); +#795=PCURVE('',#76,#794); +#796=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#797=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#798=VECTOR('',#797,1.00000000000000000E0); +#799=LINE('',#796,#798); +#800=DEFINITIONAL_REPRESENTATION('',(#799),#1); +#801=PCURVE('',#91,#800); +#802=SURFACE_CURVE('',#789,(#795,#801),.CURVE_3D.); +#803=EDGE_CURVE('',#133,#165,#802,.T.); +#804=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#805=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#806=VECTOR('',#805,1.00000000000000000E0); +#807=LINE('',#804,#806); +#808=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#809=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#810=VECTOR('',#809,1.00000000000000000E0); +#811=LINE('',#808,#810); +#812=DEFINITIONAL_REPRESENTATION('',(#811),#1); +#813=PCURVE('',#76,#812); +#814=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#815=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#816=VECTOR('',#815,1.00000000000000000E0); +#817=LINE('',#814,#816); +#818=DEFINITIONAL_REPRESENTATION('',(#817),#1); +#819=PCURVE('',#81,#818); +#820=SURFACE_CURVE('',#807,(#813,#819),.CURVE_3D.); +#821=EDGE_CURVE('',#131,#159,#820,.T.); +#822=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#823=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#824=VECTOR('',#823,1.00000000000000000E0); +#825=LINE('',#822,#824); +#826=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#827=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#828=VECTOR('',#827,1.00000000000000000E0); +#829=LINE('',#826,#828); +#830=DEFINITIONAL_REPRESENTATION('',(#829),#1); +#831=PCURVE('',#81,#830); +#832=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#833=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#834=VECTOR('',#833,1.00000000000000000E0); +#835=LINE('',#832,#834); +#836=DEFINITIONAL_REPRESENTATION('',(#835),#1); +#837=PCURVE('',#116,#836); +#838=SURFACE_CURVE('',#825,(#831,#837),.CURVE_3D.); +#839=EDGE_CURVE('',#135,#161,#838,.T.); +#840=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#841=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#842=VECTOR('',#841,1.00000000000000000E0); +#843=LINE('',#840,#842); +#844=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#845=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#846=VECTOR('',#845,1.00000000000000000E0); +#847=LINE('',#844,#846); +#848=DEFINITIONAL_REPRESENTATION('',(#847),#1); +#849=PCURVE('',#86,#848); +#850=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#851=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#852=VECTOR('',#851,1.00000000000000000E0); +#853=LINE('',#850,#852); +#854=DEFINITIONAL_REPRESENTATION('',(#853),#1); +#855=PCURVE('',#96,#854); +#856=SURFACE_CURVE('',#843,(#849,#855),.CURVE_3D.); +#857=EDGE_CURVE('',#163,#167,#856,.T.); +#858=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#859=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#860=VECTOR('',#859,1.00000000000000000E0); +#861=LINE('',#858,#860); +#862=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#863=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#864=VECTOR('',#863,1.00000000000000000E0); +#865=LINE('',#862,#864); +#866=DEFINITIONAL_REPRESENTATION('',(#865),#1); +#867=PCURVE('',#86,#866); +#868=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#869=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#870=VECTOR('',#869,1.00000000000000000E0); +#871=LINE('',#868,#870); +#872=DEFINITIONAL_REPRESENTATION('',(#871),#1); +#873=PCURVE('',#101,#872); +#874=SURFACE_CURVE('',#861,(#867,#873),.CURVE_3D.); +#875=EDGE_CURVE('',#169,#167,#874,.T.); +#876=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#877=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#878=VECTOR('',#877,1.00000000000000000E0); +#879=LINE('',#876,#878); +#880=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#881=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#882=VECTOR('',#881,1.00000000000000000E0); +#883=LINE('',#880,#882); +#884=DEFINITIONAL_REPRESENTATION('',(#883),#1); +#885=PCURVE('',#86,#884); +#886=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1)); +#887=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#888=VECTOR('',#887,1.00000000000000000E0); +#889=LINE('',#886,#888); +#890=DEFINITIONAL_REPRESENTATION('',(#889),#1); +#891=PCURVE('',#91,#890); +#892=SURFACE_CURVE('',#879,(#885,#891),.CURVE_3D.); +#893=EDGE_CURVE('',#165,#169,#892,.T.); +#894=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#895=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#896=VECTOR('',#895,1.00000000000000000E0); +#897=LINE('',#894,#896); +#898=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0)); +#899=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#900=VECTOR('',#899,1.00000000000000000E0); +#901=LINE('',#898,#900); +#902=DEFINITIONAL_REPRESENTATION('',(#901),#1); +#903=PCURVE('',#91,#902); +#904=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#905=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#906=VECTOR('',#905,1.00000000000000000E0); +#907=LINE('',#904,#906); +#908=DEFINITIONAL_REPRESENTATION('',(#907),#1); +#909=PCURVE('',#101,#908); +#910=SURFACE_CURVE('',#897,(#903,#909),.CURVE_3D.); +#911=EDGE_CURVE('',#141,#169,#910,.T.); +#912=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#913=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#914=VECTOR('',#913,1.00000000000000000E0); +#915=LINE('',#912,#914); +#916=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#917=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#918=VECTOR('',#917,1.00000000000000000E0); +#919=LINE('',#916,#918); +#920=DEFINITIONAL_REPRESENTATION('',(#919),#1); +#921=PCURVE('',#96,#920); +#922=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#923=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#924=VECTOR('',#923,1.00000000000000000E0); +#925=LINE('',#922,#924); +#926=DEFINITIONAL_REPRESENTATION('',(#925),#1); +#927=PCURVE('',#101,#926); +#928=SURFACE_CURVE('',#915,(#921,#927),.CURVE_3D.); +#929=EDGE_CURVE('',#143,#167,#928,.T.); +#930=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#931=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#932=VECTOR('',#931,1.00000000000000000E0); +#933=LINE('',#930,#932); +#934=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#935=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#936=VECTOR('',#935,1.00000000000000000E0); +#937=LINE('',#934,#936); +#938=DEFINITIONAL_REPRESENTATION('',(#937),#1); +#939=PCURVE('',#96,#938); +#940=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#941=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#942=VECTOR('',#941,1.00000000000000000E0); +#943=LINE('',#940,#942); +#944=DEFINITIONAL_REPRESENTATION('',(#943),#1); +#945=PCURVE('',#121,#944); +#946=SURFACE_CURVE('',#933,(#939,#945),.CURVE_3D.); +#947=EDGE_CURVE('',#155,#163,#946,.T.); +#948=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#949=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#950=VECTOR('',#949,1.00000000000000000E0); +#951=LINE('',#948,#950); +#952=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#953=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#954=VECTOR('',#953,1.00000000000000000E0); +#955=LINE('',#952,#954); +#956=DEFINITIONAL_REPRESENTATION('',(#955),#1); +#957=PCURVE('',#106,#956); +#958=CARTESIAN_POINT('',(5.00000000000000000E-1,3.00000000000000000E0)); +#959=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#960=VECTOR('',#959,1.00000000000000000E0); +#961=LINE('',#958,#960); +#962=DEFINITIONAL_REPRESENTATION('',(#961),#1); +#963=PCURVE('',#116,#962); +#964=SURFACE_CURVE('',#951,(#957,#963),.CURVE_3D.); +#965=EDGE_CURVE('',#161,#171,#964,.T.); +#966=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#967=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#968=VECTOR('',#967,1.00000000000000000E0); +#969=LINE('',#966,#968); +#970=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#971=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#972=VECTOR('',#971,1.00000000000000000E0); +#973=LINE('',#970,#972); +#974=DEFINITIONAL_REPRESENTATION('',(#973),#1); +#975=PCURVE('',#106,#974); +#976=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#977=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#978=VECTOR('',#977,1.00000000000000000E0); +#979=LINE('',#976,#978); +#980=DEFINITIONAL_REPRESENTATION('',(#979),#1); +#981=PCURVE('',#111,#980); +#982=SURFACE_CURVE('',#969,(#975,#981),.CURVE_3D.); +#983=EDGE_CURVE('',#171,#173,#982,.T.); +#984=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#985=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#986=VECTOR('',#985,1.00000000000000000E0); +#987=LINE('',#984,#986); +#988=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#989=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#990=VECTOR('',#989,1.00000000000000000E0); +#991=LINE('',#988,#990); +#992=DEFINITIONAL_REPRESENTATION('',(#991),#1); +#993=PCURVE('',#106,#992); +#994=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#995=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#996=VECTOR('',#995,1.00000000000000000E0); +#997=LINE('',#994,#996); +#998=DEFINITIONAL_REPRESENTATION('',(#997),#1); +#999=PCURVE('',#121,#998); +#1000=SURFACE_CURVE('',#987,(#993,#999),.CURVE_3D.); +#1001=EDGE_CURVE('',#163,#173,#1000,.T.); +#1002=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#1003=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1004=VECTOR('',#1003,1.00000000000000000E0); +#1005=LINE('',#1002,#1004); +#1006=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#1007=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1008=VECTOR('',#1007,1.00000000000000000E0); +#1009=LINE('',#1006,#1008); +#1010=DEFINITIONAL_REPRESENTATION('',(#1009),#1); +#1011=PCURVE('',#111,#1010); +#1012=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#1013=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1014=VECTOR('',#1013,1.00000000000000000E0); +#1015=LINE('',#1012,#1014); +#1016=DEFINITIONAL_REPRESENTATION('',(#1015),#1); +#1017=PCURVE('',#121,#1016); +#1018=SURFACE_CURVE('',#1005,(#1011,#1017),.CURVE_3D.); +#1019=EDGE_CURVE('',#149,#173,#1018,.T.); +#1020=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#1021=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1022=VECTOR('',#1021,1.00000000000000000E0); +#1023=LINE('',#1020,#1022); +#1024=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#1025=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1026=VECTOR('',#1025,1.00000000000000000E0); +#1027=LINE('',#1024,#1026); +#1028=DEFINITIONAL_REPRESENTATION('',(#1027),#1); +#1029=PCURVE('',#111,#1028); +#1030=CARTESIAN_POINT('',(3.00000000000000000E0,1.50000000000000000E0)); +#1031=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1032=VECTOR('',#1031,1.00000000000000000E0); +#1033=LINE('',#1030,#1032); +#1034=DEFINITIONAL_REPRESENTATION('',(#1033),#1); +#1035=PCURVE('',#116,#1034); +#1036=SURFACE_CURVE('',#1023,(#1029,#1035),.CURVE_3D.); +#1037=EDGE_CURVE('',#151,#171,#1036,.T.); +#1038=ORIENTED_EDGE('',*,*,#191,.T.); +#1039=ORIENTED_EDGE('',*,*,#209,.F.); +#1040=ORIENTED_EDGE('',*,*,#227,.F.); +#1041=ORIENTED_EDGE('',*,*,#245,.T.); +#1042=EDGE_LOOP('',(#1038,#1039,#1040,#1041)); +#1043=FACE_OUTER_BOUND('',#1042,.T.); +#1044=ADVANCED_FACE('left-0',(#1043),#6,.T.); +#1045=ORIENTED_EDGE('',*,*,#263,.T.); +#1046=ORIENTED_EDGE('',*,*,#281,.F.); +#1047=ORIENTED_EDGE('',*,*,#245,.F.); +#1048=ORIENTED_EDGE('',*,*,#299,.T.); +#1049=EDGE_LOOP('',(#1045,#1046,#1047,#1048)); +#1050=FACE_OUTER_BOUND('',#1049,.T.); +#1051=ADVANCED_FACE('front-1',(#1050),#11,.T.); +#1052=ORIENTED_EDGE('',*,*,#317,.T.); +#1053=ORIENTED_EDGE('',*,*,#335,.F.); +#1054=ORIENTED_EDGE('',*,*,#299,.F.); +#1055=ORIENTED_EDGE('',*,*,#227,.T.); +#1056=EDGE_LOOP('',(#1052,#1053,#1054,#1055)); +#1057=FACE_OUTER_BOUND('',#1056,.T.); +#1058=ADVANCED_FACE('bottom-2',(#1057),#16,.T.); +#1059=ORIENTED_EDGE('',*,*,#353,.T.); +#1060=ORIENTED_EDGE('',*,*,#371,.F.); +#1061=ORIENTED_EDGE('',*,*,#191,.F.); +#1062=ORIENTED_EDGE('',*,*,#389,.T.); +#1063=EDGE_LOOP('',(#1059,#1060,#1061,#1062)); +#1064=FACE_OUTER_BOUND('',#1063,.T.); +#1065=ADVANCED_FACE('left-3',(#1064),#21,.T.); +#1066=ORIENTED_EDGE('',*,*,#407,.T.); +#1067=ORIENTED_EDGE('',*,*,#425,.F.); +#1068=ORIENTED_EDGE('',*,*,#389,.F.); +#1069=ORIENTED_EDGE('',*,*,#281,.T.); +#1070=EDGE_LOOP('',(#1066,#1067,#1068,#1069)); +#1071=FACE_OUTER_BOUND('',#1070,.T.); +#1072=ADVANCED_FACE('front-4',(#1071),#26,.T.); +#1073=ORIENTED_EDGE('',*,*,#425,.T.); +#1074=ORIENTED_EDGE('',*,*,#443,.T.); +#1075=ORIENTED_EDGE('',*,*,#461,.F.); +#1076=ORIENTED_EDGE('',*,*,#353,.F.); +#1077=EDGE_LOOP('',(#1073,#1074,#1075,#1076)); +#1078=FACE_OUTER_BOUND('',#1077,.T.); +#1079=ADVANCED_FACE('top-5',(#1078),#31,.T.); +#1080=ORIENTED_EDGE('',*,*,#479,.T.); +#1081=ORIENTED_EDGE('',*,*,#497,.F.); +#1082=ORIENTED_EDGE('',*,*,#515,.F.); +#1083=ORIENTED_EDGE('',*,*,#209,.T.); +#1084=EDGE_LOOP('',(#1080,#1081,#1082,#1083)); +#1085=FACE_OUTER_BOUND('',#1084,.T.); +#1086=ADVANCED_FACE('left-6',(#1085),#36,.T.); +#1087=ORIENTED_EDGE('',*,*,#497,.T.); +#1088=ORIENTED_EDGE('',*,*,#533,.T.); +#1089=ORIENTED_EDGE('',*,*,#551,.F.); +#1090=ORIENTED_EDGE('',*,*,#569,.F.); +#1091=EDGE_LOOP('',(#1087,#1088,#1089,#1090)); +#1092=FACE_OUTER_BOUND('',#1091,.T.); +#1093=ADVANCED_FACE('back-7',(#1092),#41,.T.); +#1094=ORIENTED_EDGE('',*,*,#569,.T.); +#1095=ORIENTED_EDGE('',*,*,#587,.F.); +#1096=ORIENTED_EDGE('',*,*,#317,.F.); +#1097=ORIENTED_EDGE('',*,*,#515,.T.); +#1098=EDGE_LOOP('',(#1094,#1095,#1096,#1097)); +#1099=FACE_OUTER_BOUND('',#1098,.T.); +#1100=ADVANCED_FACE('bottom-8',(#1099),#46,.T.); +#1101=ORIENTED_EDGE('',*,*,#605,.T.); +#1102=ORIENTED_EDGE('',*,*,#623,.F.); +#1103=ORIENTED_EDGE('',*,*,#479,.F.); +#1104=ORIENTED_EDGE('',*,*,#371,.T.); +#1105=EDGE_LOOP('',(#1101,#1102,#1103,#1104)); +#1106=FACE_OUTER_BOUND('',#1105,.T.); +#1107=ADVANCED_FACE('left-9',(#1106),#51,.T.); +#1108=ORIENTED_EDGE('',*,*,#641,.T.); +#1109=ORIENTED_EDGE('',*,*,#659,.T.); +#1110=ORIENTED_EDGE('',*,*,#677,.F.); +#1111=ORIENTED_EDGE('',*,*,#695,.F.); +#1112=EDGE_LOOP('',(#1108,#1109,#1110,#1111)); +#1113=FACE_OUTER_BOUND('',#1112,.T.); +#1114=ADVANCED_FACE('left-10',(#1113),#56,.F.); +#1115=ORIENTED_EDGE('',*,*,#623,.T.); +#1116=ORIENTED_EDGE('',*,*,#713,.T.); +#1117=ORIENTED_EDGE('',*,*,#659,.F.); +#1118=ORIENTED_EDGE('',*,*,#533,.F.); +#1119=EDGE_LOOP('',(#1115,#1116,#1117,#1118)); +#1120=FACE_OUTER_BOUND('',#1119,.T.); +#1121=ADVANCED_FACE('back-11',(#1120),#61,.T.); +#1122=ORIENTED_EDGE('',*,*,#461,.T.); +#1123=ORIENTED_EDGE('',*,*,#677,.T.); +#1124=ORIENTED_EDGE('',*,*,#713,.F.); +#1125=ORIENTED_EDGE('',*,*,#605,.F.); +#1126=EDGE_LOOP('',(#1122,#1123,#1124,#1125)); +#1127=FACE_OUTER_BOUND('',#1126,.T.); +#1128=ADVANCED_FACE('top-12',(#1127),#66,.T.); +#1129=ORIENTED_EDGE('',*,*,#731,.T.); +#1130=ORIENTED_EDGE('',*,*,#749,.T.); +#1131=ORIENTED_EDGE('',*,*,#767,.F.); +#1132=ORIENTED_EDGE('',*,*,#785,.F.); +#1133=EDGE_LOOP('',(#1129,#1130,#1131,#1132)); +#1134=FACE_OUTER_BOUND('',#1133,.T.); +#1135=ADVANCED_FACE('right-13',(#1134),#71,.T.); +#1136=ORIENTED_EDGE('',*,*,#785,.T.); +#1137=ORIENTED_EDGE('',*,*,#803,.F.); +#1138=ORIENTED_EDGE('',*,*,#263,.F.); +#1139=ORIENTED_EDGE('',*,*,#821,.T.); +#1140=EDGE_LOOP('',(#1136,#1137,#1138,#1139)); +#1141=FACE_OUTER_BOUND('',#1140,.T.); +#1142=ADVANCED_FACE('front-14',(#1141),#76,.T.); +#1143=ORIENTED_EDGE('',*,*,#839,.T.); +#1144=ORIENTED_EDGE('',*,*,#731,.F.); +#1145=ORIENTED_EDGE('',*,*,#821,.F.); +#1146=ORIENTED_EDGE('',*,*,#335,.T.); +#1147=EDGE_LOOP('',(#1143,#1144,#1145,#1146)); +#1148=FACE_OUTER_BOUND('',#1147,.T.); +#1149=ADVANCED_FACE('bottom-15',(#1148),#81,.T.); +#1150=ORIENTED_EDGE('',*,*,#767,.T.); +#1151=ORIENTED_EDGE('',*,*,#857,.T.); +#1152=ORIENTED_EDGE('',*,*,#875,.F.); +#1153=ORIENTED_EDGE('',*,*,#893,.F.); +#1154=EDGE_LOOP('',(#1150,#1151,#1152,#1153)); +#1155=FACE_OUTER_BOUND('',#1154,.T.); +#1156=ADVANCED_FACE('right-16',(#1155),#86,.T.); +#1157=ORIENTED_EDGE('',*,*,#893,.T.); +#1158=ORIENTED_EDGE('',*,*,#911,.F.); +#1159=ORIENTED_EDGE('',*,*,#407,.F.); +#1160=ORIENTED_EDGE('',*,*,#803,.T.); +#1161=EDGE_LOOP('',(#1157,#1158,#1159,#1160)); +#1162=FACE_OUTER_BOUND('',#1161,.T.); +#1163=ADVANCED_FACE('front-17',(#1162),#91,.T.); +#1164=ORIENTED_EDGE('',*,*,#695,.T.); +#1165=ORIENTED_EDGE('',*,*,#929,.T.); +#1166=ORIENTED_EDGE('',*,*,#857,.F.); +#1167=ORIENTED_EDGE('',*,*,#947,.F.); +#1168=EDGE_LOOP('',(#1164,#1165,#1166,#1167)); +#1169=FACE_OUTER_BOUND('',#1168,.T.); +#1170=ADVANCED_FACE('front-18',(#1169),#96,.F.); +#1171=ORIENTED_EDGE('',*,*,#911,.T.); +#1172=ORIENTED_EDGE('',*,*,#875,.T.); +#1173=ORIENTED_EDGE('',*,*,#929,.F.); +#1174=ORIENTED_EDGE('',*,*,#443,.F.); +#1175=EDGE_LOOP('',(#1171,#1172,#1173,#1174)); +#1176=FACE_OUTER_BOUND('',#1175,.T.); +#1177=ADVANCED_FACE('top-19',(#1176),#101,.T.); +#1178=ORIENTED_EDGE('',*,*,#965,.T.); +#1179=ORIENTED_EDGE('',*,*,#983,.T.); +#1180=ORIENTED_EDGE('',*,*,#1001,.F.); +#1181=ORIENTED_EDGE('',*,*,#749,.F.); +#1182=EDGE_LOOP('',(#1178,#1179,#1180,#1181)); +#1183=FACE_OUTER_BOUND('',#1182,.T.); +#1184=ADVANCED_FACE('right-20',(#1183),#106,.T.); +#1185=ORIENTED_EDGE('',*,*,#551,.T.); +#1186=ORIENTED_EDGE('',*,*,#1019,.T.); +#1187=ORIENTED_EDGE('',*,*,#983,.F.); +#1188=ORIENTED_EDGE('',*,*,#1037,.F.); +#1189=EDGE_LOOP('',(#1185,#1186,#1187,#1188)); +#1190=FACE_OUTER_BOUND('',#1189,.T.); +#1191=ADVANCED_FACE('back-21',(#1190),#111,.T.); +#1192=ORIENTED_EDGE('',*,*,#1037,.T.); +#1193=ORIENTED_EDGE('',*,*,#965,.F.); +#1194=ORIENTED_EDGE('',*,*,#839,.F.); +#1195=ORIENTED_EDGE('',*,*,#587,.T.); +#1196=EDGE_LOOP('',(#1192,#1193,#1194,#1195)); +#1197=FACE_OUTER_BOUND('',#1196,.T.); +#1198=ADVANCED_FACE('bottom-22',(#1197),#116,.T.); +#1199=ORIENTED_EDGE('',*,*,#947,.T.); +#1200=ORIENTED_EDGE('',*,*,#1001,.T.); +#1201=ORIENTED_EDGE('',*,*,#1019,.F.); +#1202=ORIENTED_EDGE('',*,*,#641,.F.); +#1203=EDGE_LOOP('',(#1199,#1200,#1201,#1202)); +#1204=FACE_OUTER_BOUND('',#1203,.T.); +#1205=ADVANCED_FACE('bottom-23',(#1204),#121,.F.); +#1206=CLOSED_SHELL('',(#1044,#1065,#1086,#1058,#1051,#1079,#1107,#1072,#1093,#1100,#1149,#1142,#1177,#1128,#1121,#1163,#1191,#1198,#1135,#1156,#1170,#1114,#1205,#1184)); +#1207=MANIFOLD_SOLID_BREP('box-cut-0',#1206); +#1208=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#1209=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#1210=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#1211=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000426325642881E-8),#1208,'distance_accuracy_value','exchange error bound'); +#1212=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#1211)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#1208,#1209,#1210)) REPRESENTATION_CONTEXT('','')); +#1213=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#1207),#1212); +#1214=APPLICATION_CONTEXT('automotive design'); +#1215=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#1214); +#1216=PRODUCT_CONTEXT('',#1214,'mechanical'); +#1217=PRODUCT('box-cut','box-cut','',(#1216)); +#1218=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#1217,.NOT_KNOWN.); +#1219=PRODUCT_DEFINITION_CONTEXT('part definition',#1214,'design'); +#1220=PRODUCT_DEFINITION('','',#1218,#1219); +#1221=PRODUCT_DEFINITION_SHAPE('','',#1220); +#1222=SHAPE_DEFINITION_REPRESENTATION(#1221,#1213); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/box-cut.step.m.report.json b/main/opengeometry/tests/fixtures/cases/box-cut.step.m.report.json new file mode 100644 index 0000000..76edd71 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cut.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":48,"exchange_error_bound":1.0000042632564288e-8,"faces":24,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"1","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-cut.step.mm b/main/opengeometry/tests/fixtures/cases/box-cut.step.mm new file mode 100644 index 0000000..a7acde5 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cut.step.mm @@ -0,0 +1,1231 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('box-cut','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#24=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#28=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#34=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#35=AXIS2_PLACEMENT_3D('',#32,#33,#34); +#36=PLANE('',#35); +#37=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#38=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#39=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#40=AXIS2_PLACEMENT_3D('',#37,#38,#39); +#41=PLANE('',#40); +#42=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#43=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#44=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#45=AXIS2_PLACEMENT_3D('',#42,#43,#44); +#46=PLANE('',#45); +#47=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#48=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#49=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#50=AXIS2_PLACEMENT_3D('',#47,#48,#49); +#51=PLANE('',#50); +#52=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#53=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#54=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#55=AXIS2_PLACEMENT_3D('',#52,#53,#54); +#56=PLANE('',#55); +#57=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#58=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#59=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#60=AXIS2_PLACEMENT_3D('',#57,#58,#59); +#61=PLANE('',#60); +#62=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#63=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#64=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#65=AXIS2_PLACEMENT_3D('',#62,#63,#64); +#66=PLANE('',#65); +#67=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#68=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#69=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#70=AXIS2_PLACEMENT_3D('',#67,#68,#69); +#71=PLANE('',#70); +#72=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#73=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#74=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#75=AXIS2_PLACEMENT_3D('',#72,#73,#74); +#76=PLANE('',#75); +#77=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#78=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#79=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#80=AXIS2_PLACEMENT_3D('',#77,#78,#79); +#81=PLANE('',#80); +#82=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#83=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#84=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#85=AXIS2_PLACEMENT_3D('',#82,#83,#84); +#86=PLANE('',#85); +#87=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#88=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#90=AXIS2_PLACEMENT_3D('',#87,#88,#89); +#91=PLANE('',#90); +#92=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#93=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#94=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#95=AXIS2_PLACEMENT_3D('',#92,#93,#94); +#96=PLANE('',#95); +#97=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#98=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#99=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#100=AXIS2_PLACEMENT_3D('',#97,#98,#99); +#101=PLANE('',#100); +#102=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#103=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#104=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#105=AXIS2_PLACEMENT_3D('',#102,#103,#104); +#106=PLANE('',#105); +#107=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#108=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#109=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#110=AXIS2_PLACEMENT_3D('',#107,#108,#109); +#111=PLANE('',#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#113=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#114=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#115=AXIS2_PLACEMENT_3D('',#112,#113,#114); +#116=PLANE('',#115); +#117=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#118=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#119=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#120=AXIS2_PLACEMENT_3D('',#117,#118,#119); +#121=PLANE('',#120); +#122=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,0.00000000000000000E0)); +#123=VERTEX_POINT('',#122); +#124=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-5.00000000000000000E2)); +#125=VERTEX_POINT('',#124); +#126=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E2)); +#127=VERTEX_POINT('',#126); +#128=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#129=VERTEX_POINT('',#128); +#130=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#131=VERTEX_POINT('',#130); +#132=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#133=VERTEX_POINT('',#132); +#134=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#135=VERTEX_POINT('',#134); +#136=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#137=VERTEX_POINT('',#136); +#138=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-5.00000000000000000E2)); +#139=VERTEX_POINT('',#138); +#140=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#141=VERTEX_POINT('',#140); +#142=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#143=VERTEX_POINT('',#142); +#144=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-3.00000000000000000E3)); +#145=VERTEX_POINT('',#144); +#146=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#147=VERTEX_POINT('',#146); +#148=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#149=VERTEX_POINT('',#148); +#150=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#151=VERTEX_POINT('',#150); +#152=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-3.00000000000000000E3)); +#153=VERTEX_POINT('',#152); +#154=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#155=VERTEX_POINT('',#154); +#156=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#157=VERTEX_POINT('',#156); +#158=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#159=VERTEX_POINT('',#158); +#160=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#161=VERTEX_POINT('',#160); +#162=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#163=VERTEX_POINT('',#162); +#164=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#165=VERTEX_POINT('',#164); +#166=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#167=VERTEX_POINT('',#166); +#168=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#169=VERTEX_POINT('',#168); +#170=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#171=VERTEX_POINT('',#170); +#172=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#173=VERTEX_POINT('',#172); +#174=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,0.00000000000000000E0)); +#175=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#176=VECTOR('',#175,1.00000000000000000E3); +#177=LINE('',#174,#176); +#178=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#179=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#180=VECTOR('',#179,1.00000000000000000E3); +#181=LINE('',#178,#180); +#182=DEFINITIONAL_REPRESENTATION('',(#181),#1); +#183=PCURVE('',#6,#182); +#184=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#185=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#186=VECTOR('',#185,1.00000000000000000E3); +#187=LINE('',#184,#186); +#188=DEFINITIONAL_REPRESENTATION('',(#187),#1); +#189=PCURVE('',#21,#188); +#190=SURFACE_CURVE('',#177,(#183,#189),.CURVE_3D.); +#191=EDGE_CURVE('',#123,#125,#190,.T.); +#192=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E2)); +#193=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#194=VECTOR('',#193,1.00000000000000000E3); +#195=LINE('',#192,#194); +#196=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#197=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#198=VECTOR('',#197,1.00000000000000000E3); +#199=LINE('',#196,#198); +#200=DEFINITIONAL_REPRESENTATION('',(#199),#1); +#201=PCURVE('',#6,#200); +#202=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#203=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#204=VECTOR('',#203,1.00000000000000000E3); +#205=LINE('',#202,#204); +#206=DEFINITIONAL_REPRESENTATION('',(#205),#1); +#207=PCURVE('',#36,#206); +#208=SURFACE_CURVE('',#195,(#201,#207),.CURVE_3D.); +#209=EDGE_CURVE('',#127,#125,#208,.T.); +#210=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#211=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#212=VECTOR('',#211,1.00000000000000000E3); +#213=LINE('',#210,#212); +#214=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#215=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#216=VECTOR('',#215,1.00000000000000000E3); +#217=LINE('',#214,#216); +#218=DEFINITIONAL_REPRESENTATION('',(#217),#1); +#219=PCURVE('',#6,#218); +#220=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#221=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#222=VECTOR('',#221,1.00000000000000000E3); +#223=LINE('',#220,#222); +#224=DEFINITIONAL_REPRESENTATION('',(#223),#1); +#225=PCURVE('',#16,#224); +#226=SURFACE_CURVE('',#213,(#219,#225),.CURVE_3D.); +#227=EDGE_CURVE('',#129,#127,#226,.T.); +#228=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#229=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#230=VECTOR('',#229,1.00000000000000000E3); +#231=LINE('',#228,#230); +#232=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#233=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#234=VECTOR('',#233,1.00000000000000000E3); +#235=LINE('',#232,#234); +#236=DEFINITIONAL_REPRESENTATION('',(#235),#1); +#237=PCURVE('',#6,#236); +#238=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#239=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#240=VECTOR('',#239,1.00000000000000000E3); +#241=LINE('',#238,#240); +#242=DEFINITIONAL_REPRESENTATION('',(#241),#1); +#243=PCURVE('',#11,#242); +#244=SURFACE_CURVE('',#231,(#237,#243),.CURVE_3D.); +#245=EDGE_CURVE('',#129,#123,#244,.T.); +#246=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#247=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#248=VECTOR('',#247,1.00000000000000000E3); +#249=LINE('',#246,#248); +#250=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#251=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#252=VECTOR('',#251,1.00000000000000000E3); +#253=LINE('',#250,#252); +#254=DEFINITIONAL_REPRESENTATION('',(#253),#1); +#255=PCURVE('',#11,#254); +#256=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#257=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#258=VECTOR('',#257,1.00000000000000000E3); +#259=LINE('',#256,#258); +#260=DEFINITIONAL_REPRESENTATION('',(#259),#1); +#261=PCURVE('',#76,#260); +#262=SURFACE_CURVE('',#249,(#255,#261),.CURVE_3D.); +#263=EDGE_CURVE('',#131,#133,#262,.T.); +#264=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,0.00000000000000000E0)); +#265=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#266=VECTOR('',#265,1.00000000000000000E3); +#267=LINE('',#264,#266); +#268=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#269=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#270=VECTOR('',#269,1.00000000000000000E3); +#271=LINE('',#268,#270); +#272=DEFINITIONAL_REPRESENTATION('',(#271),#1); +#273=PCURVE('',#11,#272); +#274=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#275=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#276=VECTOR('',#275,1.00000000000000000E3); +#277=LINE('',#274,#276); +#278=DEFINITIONAL_REPRESENTATION('',(#277),#1); +#279=PCURVE('',#26,#278); +#280=SURFACE_CURVE('',#267,(#273,#279),.CURVE_3D.); +#281=EDGE_CURVE('',#123,#133,#280,.T.); +#282=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#283=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#284=VECTOR('',#283,1.00000000000000000E3); +#285=LINE('',#282,#284); +#286=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#287=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#288=VECTOR('',#287,1.00000000000000000E3); +#289=LINE('',#286,#288); +#290=DEFINITIONAL_REPRESENTATION('',(#289),#1); +#291=PCURVE('',#11,#290); +#292=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#293=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#294=VECTOR('',#293,1.00000000000000000E3); +#295=LINE('',#292,#294); +#296=DEFINITIONAL_REPRESENTATION('',(#295),#1); +#297=PCURVE('',#16,#296); +#298=SURFACE_CURVE('',#285,(#291,#297),.CURVE_3D.); +#299=EDGE_CURVE('',#129,#131,#298,.T.); +#300=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E2)); +#301=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#302=VECTOR('',#301,1.00000000000000000E3); +#303=LINE('',#300,#302); +#304=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#305=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#306=VECTOR('',#305,1.00000000000000000E3); +#307=LINE('',#304,#306); +#308=DEFINITIONAL_REPRESENTATION('',(#307),#1); +#309=PCURVE('',#16,#308); +#310=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#311=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#312=VECTOR('',#311,1.00000000000000000E3); +#313=LINE('',#310,#312); +#314=DEFINITIONAL_REPRESENTATION('',(#313),#1); +#315=PCURVE('',#46,#314); +#316=SURFACE_CURVE('',#303,(#309,#315),.CURVE_3D.); +#317=EDGE_CURVE('',#127,#135,#316,.T.); +#318=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#319=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#320=VECTOR('',#319,1.00000000000000000E3); +#321=LINE('',#318,#320); +#322=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#323=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#324=VECTOR('',#323,1.00000000000000000E3); +#325=LINE('',#322,#324); +#326=DEFINITIONAL_REPRESENTATION('',(#325),#1); +#327=PCURVE('',#16,#326); +#328=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#329=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#330=VECTOR('',#329,1.00000000000000000E3); +#331=LINE('',#328,#330); +#332=DEFINITIONAL_REPRESENTATION('',(#331),#1); +#333=PCURVE('',#81,#332); +#334=SURFACE_CURVE('',#321,(#327,#333),.CURVE_3D.); +#335=EDGE_CURVE('',#131,#135,#334,.T.); +#336=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#337=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#338=VECTOR('',#337,1.00000000000000000E3); +#339=LINE('',#336,#338); +#340=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#341=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#342=VECTOR('',#341,1.00000000000000000E3); +#343=LINE('',#340,#342); +#344=DEFINITIONAL_REPRESENTATION('',(#343),#1); +#345=PCURVE('',#21,#344); +#346=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#347=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#348=VECTOR('',#347,1.00000000000000000E3); +#349=LINE('',#346,#348); +#350=DEFINITIONAL_REPRESENTATION('',(#349),#1); +#351=PCURVE('',#31,#350); +#352=SURFACE_CURVE('',#339,(#345,#351),.CURVE_3D.); +#353=EDGE_CURVE('',#137,#139,#352,.T.); +#354=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-5.00000000000000000E2)); +#355=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#356=VECTOR('',#355,1.00000000000000000E3); +#357=LINE('',#354,#356); +#358=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#359=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#360=VECTOR('',#359,1.00000000000000000E3); +#361=LINE('',#358,#360); +#362=DEFINITIONAL_REPRESENTATION('',(#361),#1); +#363=PCURVE('',#21,#362); +#364=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#365=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#366=VECTOR('',#365,1.00000000000000000E3); +#367=LINE('',#364,#366); +#368=DEFINITIONAL_REPRESENTATION('',(#367),#1); +#369=PCURVE('',#51,#368); +#370=SURFACE_CURVE('',#357,(#363,#369),.CURVE_3D.); +#371=EDGE_CURVE('',#125,#139,#370,.T.); +#372=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,0.00000000000000000E0)); +#373=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#374=VECTOR('',#373,1.00000000000000000E3); +#375=LINE('',#372,#374); +#376=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#377=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#378=VECTOR('',#377,1.00000000000000000E3); +#379=LINE('',#376,#378); +#380=DEFINITIONAL_REPRESENTATION('',(#379),#1); +#381=PCURVE('',#21,#380); +#382=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#383=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#384=VECTOR('',#383,1.00000000000000000E3); +#385=LINE('',#382,#384); +#386=DEFINITIONAL_REPRESENTATION('',(#385),#1); +#387=PCURVE('',#26,#386); +#388=SURFACE_CURVE('',#375,(#381,#387),.CURVE_3D.); +#389=EDGE_CURVE('',#123,#137,#388,.T.); +#390=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#391=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#392=VECTOR('',#391,1.00000000000000000E3); +#393=LINE('',#390,#392); +#394=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#395=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#396=VECTOR('',#395,1.00000000000000000E3); +#397=LINE('',#394,#396); +#398=DEFINITIONAL_REPRESENTATION('',(#397),#1); +#399=PCURVE('',#26,#398); +#400=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#401=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#402=VECTOR('',#401,1.00000000000000000E3); +#403=LINE('',#400,#402); +#404=DEFINITIONAL_REPRESENTATION('',(#403),#1); +#405=PCURVE('',#91,#404); +#406=SURFACE_CURVE('',#393,(#399,#405),.CURVE_3D.); +#407=EDGE_CURVE('',#133,#141,#406,.T.); +#408=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#409=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#410=VECTOR('',#409,1.00000000000000000E3); +#411=LINE('',#408,#410); +#412=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#413=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#414=VECTOR('',#413,1.00000000000000000E3); +#415=LINE('',#412,#414); +#416=DEFINITIONAL_REPRESENTATION('',(#415),#1); +#417=PCURVE('',#26,#416); +#418=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#419=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#420=VECTOR('',#419,1.00000000000000000E3); +#421=LINE('',#418,#420); +#422=DEFINITIONAL_REPRESENTATION('',(#421),#1); +#423=PCURVE('',#31,#422); +#424=SURFACE_CURVE('',#411,(#417,#423),.CURVE_3D.); +#425=EDGE_CURVE('',#137,#141,#424,.T.); +#426=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#427=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#428=VECTOR('',#427,1.00000000000000000E3); +#429=LINE('',#426,#428); +#430=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#431=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#432=VECTOR('',#431,1.00000000000000000E3); +#433=LINE('',#430,#432); +#434=DEFINITIONAL_REPRESENTATION('',(#433),#1); +#435=PCURVE('',#31,#434); +#436=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#437=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#438=VECTOR('',#437,1.00000000000000000E3); +#439=LINE('',#436,#438); +#440=DEFINITIONAL_REPRESENTATION('',(#439),#1); +#441=PCURVE('',#101,#440); +#442=SURFACE_CURVE('',#429,(#435,#441),.CURVE_3D.); +#443=EDGE_CURVE('',#141,#143,#442,.T.); +#444=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-5.00000000000000000E2)); +#445=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#446=VECTOR('',#445,1.00000000000000000E3); +#447=LINE('',#444,#446); +#448=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#449=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#450=VECTOR('',#449,1.00000000000000000E3); +#451=LINE('',#448,#450); +#452=DEFINITIONAL_REPRESENTATION('',(#451),#1); +#453=PCURVE('',#31,#452); +#454=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#455=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#456=VECTOR('',#455,1.00000000000000000E3); +#457=LINE('',#454,#456); +#458=DEFINITIONAL_REPRESENTATION('',(#457),#1); +#459=PCURVE('',#66,#458); +#460=SURFACE_CURVE('',#447,(#453,#459),.CURVE_3D.); +#461=EDGE_CURVE('',#139,#143,#460,.T.); +#462=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-5.00000000000000000E2)); +#463=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#464=VECTOR('',#463,1.00000000000000000E3); +#465=LINE('',#462,#464); +#466=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#467=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#468=VECTOR('',#467,1.00000000000000000E3); +#469=LINE('',#466,#468); +#470=DEFINITIONAL_REPRESENTATION('',(#469),#1); +#471=PCURVE('',#36,#470); +#472=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#473=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#474=VECTOR('',#473,1.00000000000000000E3); +#475=LINE('',#472,#474); +#476=DEFINITIONAL_REPRESENTATION('',(#475),#1); +#477=PCURVE('',#51,#476); +#478=SURFACE_CURVE('',#465,(#471,#477),.CURVE_3D.); +#479=EDGE_CURVE('',#125,#145,#478,.T.); +#480=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#481=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#482=VECTOR('',#481,1.00000000000000000E3); +#483=LINE('',#480,#482); +#484=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#485=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#486=VECTOR('',#485,1.00000000000000000E3); +#487=LINE('',#484,#486); +#488=DEFINITIONAL_REPRESENTATION('',(#487),#1); +#489=PCURVE('',#36,#488); +#490=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#491=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#492=VECTOR('',#491,1.00000000000000000E3); +#493=LINE('',#490,#492); +#494=DEFINITIONAL_REPRESENTATION('',(#493),#1); +#495=PCURVE('',#41,#494); +#496=SURFACE_CURVE('',#483,(#489,#495),.CURVE_3D.); +#497=EDGE_CURVE('',#147,#145,#496,.T.); +#498=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E2)); +#499=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#500=VECTOR('',#499,1.00000000000000000E3); +#501=LINE('',#498,#500); +#502=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#503=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#504=VECTOR('',#503,1.00000000000000000E3); +#505=LINE('',#502,#504); +#506=DEFINITIONAL_REPRESENTATION('',(#505),#1); +#507=PCURVE('',#36,#506); +#508=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#509=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#510=VECTOR('',#509,1.00000000000000000E3); +#511=LINE('',#508,#510); +#512=DEFINITIONAL_REPRESENTATION('',(#511),#1); +#513=PCURVE('',#46,#512); +#514=SURFACE_CURVE('',#501,(#507,#513),.CURVE_3D.); +#515=EDGE_CURVE('',#127,#147,#514,.T.); +#516=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-3.00000000000000000E3)); +#517=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#518=VECTOR('',#517,1.00000000000000000E3); +#519=LINE('',#516,#518); +#520=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#521=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#522=VECTOR('',#521,1.00000000000000000E3); +#523=LINE('',#520,#522); +#524=DEFINITIONAL_REPRESENTATION('',(#523),#1); +#525=PCURVE('',#41,#524); +#526=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#527=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#528=VECTOR('',#527,1.00000000000000000E3); +#529=LINE('',#526,#528); +#530=DEFINITIONAL_REPRESENTATION('',(#529),#1); +#531=PCURVE('',#61,#530); +#532=SURFACE_CURVE('',#519,(#525,#531),.CURVE_3D.); +#533=EDGE_CURVE('',#145,#149,#532,.T.); +#534=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#535=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#536=VECTOR('',#535,1.00000000000000000E3); +#537=LINE('',#534,#536); +#538=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#539=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#540=VECTOR('',#539,1.00000000000000000E3); +#541=LINE('',#538,#540); +#542=DEFINITIONAL_REPRESENTATION('',(#541),#1); +#543=PCURVE('',#41,#542); +#544=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#545=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#546=VECTOR('',#545,1.00000000000000000E3); +#547=LINE('',#544,#546); +#548=DEFINITIONAL_REPRESENTATION('',(#547),#1); +#549=PCURVE('',#111,#548); +#550=SURFACE_CURVE('',#537,(#543,#549),.CURVE_3D.); +#551=EDGE_CURVE('',#151,#149,#550,.T.); +#552=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#553=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#554=VECTOR('',#553,1.00000000000000000E3); +#555=LINE('',#552,#554); +#556=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#557=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#558=VECTOR('',#557,1.00000000000000000E3); +#559=LINE('',#556,#558); +#560=DEFINITIONAL_REPRESENTATION('',(#559),#1); +#561=PCURVE('',#41,#560); +#562=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#563=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#564=VECTOR('',#563,1.00000000000000000E3); +#565=LINE('',#562,#564); +#566=DEFINITIONAL_REPRESENTATION('',(#565),#1); +#567=PCURVE('',#46,#566); +#568=SURFACE_CURVE('',#555,(#561,#567),.CURVE_3D.); +#569=EDGE_CURVE('',#147,#151,#568,.T.); +#570=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#571=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#572=VECTOR('',#571,1.00000000000000000E3); +#573=LINE('',#570,#572); +#574=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#575=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#576=VECTOR('',#575,1.00000000000000000E3); +#577=LINE('',#574,#576); +#578=DEFINITIONAL_REPRESENTATION('',(#577),#1); +#579=PCURVE('',#46,#578); +#580=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#581=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#582=VECTOR('',#581,1.00000000000000000E3); +#583=LINE('',#580,#582); +#584=DEFINITIONAL_REPRESENTATION('',(#583),#1); +#585=PCURVE('',#116,#584); +#586=SURFACE_CURVE('',#573,(#579,#585),.CURVE_3D.); +#587=EDGE_CURVE('',#135,#151,#586,.T.); +#588=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-5.00000000000000000E2)); +#589=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#590=VECTOR('',#589,1.00000000000000000E3); +#591=LINE('',#588,#590); +#592=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2)); +#593=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#594=VECTOR('',#593,1.00000000000000000E3); +#595=LINE('',#592,#594); +#596=DEFINITIONAL_REPRESENTATION('',(#595),#1); +#597=PCURVE('',#51,#596); +#598=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#599=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#600=VECTOR('',#599,1.00000000000000000E3); +#601=LINE('',#598,#600); +#602=DEFINITIONAL_REPRESENTATION('',(#601),#1); +#603=PCURVE('',#66,#602); +#604=SURFACE_CURVE('',#591,(#597,#603),.CURVE_3D.); +#605=EDGE_CURVE('',#139,#153,#604,.T.); +#606=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-3.00000000000000000E3)); +#607=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#608=VECTOR('',#607,1.00000000000000000E3); +#609=LINE('',#606,#608); +#610=CARTESIAN_POINT('',(5.00000000000000000E2,3.00000000000000000E3)); +#611=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#612=VECTOR('',#611,1.00000000000000000E3); +#613=LINE('',#610,#612); +#614=DEFINITIONAL_REPRESENTATION('',(#613),#1); +#615=PCURVE('',#51,#614); +#616=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#617=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#618=VECTOR('',#617,1.00000000000000000E3); +#619=LINE('',#616,#618); +#620=DEFINITIONAL_REPRESENTATION('',(#619),#1); +#621=PCURVE('',#61,#620); +#622=SURFACE_CURVE('',#609,(#615,#621),.CURVE_3D.); +#623=EDGE_CURVE('',#145,#153,#622,.T.); +#624=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#625=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#626=VECTOR('',#625,1.00000000000000000E3); +#627=LINE('',#624,#626); +#628=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#629=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#630=VECTOR('',#629,1.00000000000000000E3); +#631=LINE('',#628,#630); +#632=DEFINITIONAL_REPRESENTATION('',(#631),#1); +#633=PCURVE('',#56,#632); +#634=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#635=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#636=VECTOR('',#635,1.00000000000000000E3); +#637=LINE('',#634,#636); +#638=DEFINITIONAL_REPRESENTATION('',(#637),#1); +#639=PCURVE('',#121,#638); +#640=SURFACE_CURVE('',#627,(#633,#639),.CURVE_3D.); +#641=EDGE_CURVE('',#155,#149,#640,.T.); +#642=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#643=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#644=VECTOR('',#643,1.00000000000000000E3); +#645=LINE('',#642,#644); +#646=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#647=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#648=VECTOR('',#647,1.00000000000000000E3); +#649=LINE('',#646,#648); +#650=DEFINITIONAL_REPRESENTATION('',(#649),#1); +#651=PCURVE('',#56,#650); +#652=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#653=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#654=VECTOR('',#653,1.00000000000000000E3); +#655=LINE('',#652,#654); +#656=DEFINITIONAL_REPRESENTATION('',(#655),#1); +#657=PCURVE('',#61,#656); +#658=SURFACE_CURVE('',#645,(#651,#657),.CURVE_3D.); +#659=EDGE_CURVE('',#149,#157,#658,.T.); +#660=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#661=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#662=VECTOR('',#661,1.00000000000000000E3); +#663=LINE('',#660,#662); +#664=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#665=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#666=VECTOR('',#665,1.00000000000000000E3); +#667=LINE('',#664,#666); +#668=DEFINITIONAL_REPRESENTATION('',(#667),#1); +#669=PCURVE('',#56,#668); +#670=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#671=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#672=VECTOR('',#671,1.00000000000000000E3); +#673=LINE('',#670,#672); +#674=DEFINITIONAL_REPRESENTATION('',(#673),#1); +#675=PCURVE('',#66,#674); +#676=SURFACE_CURVE('',#663,(#669,#675),.CURVE_3D.); +#677=EDGE_CURVE('',#143,#157,#676,.T.); +#678=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#679=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#680=VECTOR('',#679,1.00000000000000000E3); +#681=LINE('',#678,#680); +#682=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#683=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#684=VECTOR('',#683,1.00000000000000000E3); +#685=LINE('',#682,#684); +#686=DEFINITIONAL_REPRESENTATION('',(#685),#1); +#687=PCURVE('',#56,#686); +#688=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#689=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#690=VECTOR('',#689,1.00000000000000000E3); +#691=LINE('',#688,#690); +#692=DEFINITIONAL_REPRESENTATION('',(#691),#1); +#693=PCURVE('',#96,#692); +#694=SURFACE_CURVE('',#681,(#687,#693),.CURVE_3D.); +#695=EDGE_CURVE('',#155,#143,#694,.T.); +#696=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-3.00000000000000000E3)); +#697=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#698=VECTOR('',#697,1.00000000000000000E3); +#699=LINE('',#696,#698); +#700=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#701=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#702=VECTOR('',#701,1.00000000000000000E3); +#703=LINE('',#700,#702); +#704=DEFINITIONAL_REPRESENTATION('',(#703),#1); +#705=PCURVE('',#61,#704); +#706=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#707=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#708=VECTOR('',#707,1.00000000000000000E3); +#709=LINE('',#706,#708); +#710=DEFINITIONAL_REPRESENTATION('',(#709),#1); +#711=PCURVE('',#66,#710); +#712=SURFACE_CURVE('',#699,(#705,#711),.CURVE_3D.); +#713=EDGE_CURVE('',#153,#157,#712,.T.); +#714=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#715=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#716=VECTOR('',#715,1.00000000000000000E3); +#717=LINE('',#714,#716); +#718=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#719=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#720=VECTOR('',#719,1.00000000000000000E3); +#721=LINE('',#718,#720); +#722=DEFINITIONAL_REPRESENTATION('',(#721),#1); +#723=PCURVE('',#71,#722); +#724=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#725=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#726=VECTOR('',#725,1.00000000000000000E3); +#727=LINE('',#724,#726); +#728=DEFINITIONAL_REPRESENTATION('',(#727),#1); +#729=PCURVE('',#81,#728); +#730=SURFACE_CURVE('',#717,(#723,#729),.CURVE_3D.); +#731=EDGE_CURVE('',#159,#161,#730,.T.); +#732=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#733=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#734=VECTOR('',#733,1.00000000000000000E3); +#735=LINE('',#732,#734); +#736=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#737=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#738=VECTOR('',#737,1.00000000000000000E3); +#739=LINE('',#736,#738); +#740=DEFINITIONAL_REPRESENTATION('',(#739),#1); +#741=PCURVE('',#71,#740); +#742=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#743=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#744=VECTOR('',#743,1.00000000000000000E3); +#745=LINE('',#742,#744); +#746=DEFINITIONAL_REPRESENTATION('',(#745),#1); +#747=PCURVE('',#106,#746); +#748=SURFACE_CURVE('',#735,(#741,#747),.CURVE_3D.); +#749=EDGE_CURVE('',#161,#163,#748,.T.); +#750=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#751=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#752=VECTOR('',#751,1.00000000000000000E3); +#753=LINE('',#750,#752); +#754=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#755=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#756=VECTOR('',#755,1.00000000000000000E3); +#757=LINE('',#754,#756); +#758=DEFINITIONAL_REPRESENTATION('',(#757),#1); +#759=PCURVE('',#71,#758); +#760=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#761=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#762=VECTOR('',#761,1.00000000000000000E3); +#763=LINE('',#760,#762); +#764=DEFINITIONAL_REPRESENTATION('',(#763),#1); +#765=PCURVE('',#86,#764); +#766=SURFACE_CURVE('',#753,(#759,#765),.CURVE_3D.); +#767=EDGE_CURVE('',#165,#163,#766,.T.); +#768=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#769=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#770=VECTOR('',#769,1.00000000000000000E3); +#771=LINE('',#768,#770); +#772=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#773=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#774=VECTOR('',#773,1.00000000000000000E3); +#775=LINE('',#772,#774); +#776=DEFINITIONAL_REPRESENTATION('',(#775),#1); +#777=PCURVE('',#71,#776); +#778=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#779=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#780=VECTOR('',#779,1.00000000000000000E3); +#781=LINE('',#778,#780); +#782=DEFINITIONAL_REPRESENTATION('',(#781),#1); +#783=PCURVE('',#76,#782); +#784=SURFACE_CURVE('',#771,(#777,#783),.CURVE_3D.); +#785=EDGE_CURVE('',#159,#165,#784,.T.); +#786=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#787=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#788=VECTOR('',#787,1.00000000000000000E3); +#789=LINE('',#786,#788); +#790=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#791=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#792=VECTOR('',#791,1.00000000000000000E3); +#793=LINE('',#790,#792); +#794=DEFINITIONAL_REPRESENTATION('',(#793),#1); +#795=PCURVE('',#76,#794); +#796=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#797=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#798=VECTOR('',#797,1.00000000000000000E3); +#799=LINE('',#796,#798); +#800=DEFINITIONAL_REPRESENTATION('',(#799),#1); +#801=PCURVE('',#91,#800); +#802=SURFACE_CURVE('',#789,(#795,#801),.CURVE_3D.); +#803=EDGE_CURVE('',#133,#165,#802,.T.); +#804=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#805=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#806=VECTOR('',#805,1.00000000000000000E3); +#807=LINE('',#804,#806); +#808=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#809=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#810=VECTOR('',#809,1.00000000000000000E3); +#811=LINE('',#808,#810); +#812=DEFINITIONAL_REPRESENTATION('',(#811),#1); +#813=PCURVE('',#76,#812); +#814=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#815=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#816=VECTOR('',#815,1.00000000000000000E3); +#817=LINE('',#814,#816); +#818=DEFINITIONAL_REPRESENTATION('',(#817),#1); +#819=PCURVE('',#81,#818); +#820=SURFACE_CURVE('',#807,(#813,#819),.CURVE_3D.); +#821=EDGE_CURVE('',#131,#159,#820,.T.); +#822=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#823=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#824=VECTOR('',#823,1.00000000000000000E3); +#825=LINE('',#822,#824); +#826=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#827=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#828=VECTOR('',#827,1.00000000000000000E3); +#829=LINE('',#826,#828); +#830=DEFINITIONAL_REPRESENTATION('',(#829),#1); +#831=PCURVE('',#81,#830); +#832=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#833=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#834=VECTOR('',#833,1.00000000000000000E3); +#835=LINE('',#832,#834); +#836=DEFINITIONAL_REPRESENTATION('',(#835),#1); +#837=PCURVE('',#116,#836); +#838=SURFACE_CURVE('',#825,(#831,#837),.CURVE_3D.); +#839=EDGE_CURVE('',#135,#161,#838,.T.); +#840=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#841=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#842=VECTOR('',#841,1.00000000000000000E3); +#843=LINE('',#840,#842); +#844=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#845=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#846=VECTOR('',#845,1.00000000000000000E3); +#847=LINE('',#844,#846); +#848=DEFINITIONAL_REPRESENTATION('',(#847),#1); +#849=PCURVE('',#86,#848); +#850=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#851=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#852=VECTOR('',#851,1.00000000000000000E3); +#853=LINE('',#850,#852); +#854=DEFINITIONAL_REPRESENTATION('',(#853),#1); +#855=PCURVE('',#96,#854); +#856=SURFACE_CURVE('',#843,(#849,#855),.CURVE_3D.); +#857=EDGE_CURVE('',#163,#167,#856,.T.); +#858=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#859=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#860=VECTOR('',#859,1.00000000000000000E3); +#861=LINE('',#858,#860); +#862=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#863=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#864=VECTOR('',#863,1.00000000000000000E3); +#865=LINE('',#862,#864); +#866=DEFINITIONAL_REPRESENTATION('',(#865),#1); +#867=PCURVE('',#86,#866); +#868=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#869=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#870=VECTOR('',#869,1.00000000000000000E3); +#871=LINE('',#868,#870); +#872=DEFINITIONAL_REPRESENTATION('',(#871),#1); +#873=PCURVE('',#101,#872); +#874=SURFACE_CURVE('',#861,(#867,#873),.CURVE_3D.); +#875=EDGE_CURVE('',#169,#167,#874,.T.); +#876=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#877=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#878=VECTOR('',#877,1.00000000000000000E3); +#879=LINE('',#876,#878); +#880=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#881=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#882=VECTOR('',#881,1.00000000000000000E3); +#883=LINE('',#880,#882); +#884=DEFINITIONAL_REPRESENTATION('',(#883),#1); +#885=PCURVE('',#86,#884); +#886=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2)); +#887=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#888=VECTOR('',#887,1.00000000000000000E3); +#889=LINE('',#886,#888); +#890=DEFINITIONAL_REPRESENTATION('',(#889),#1); +#891=PCURVE('',#91,#890); +#892=SURFACE_CURVE('',#879,(#885,#891),.CURVE_3D.); +#893=EDGE_CURVE('',#165,#169,#892,.T.); +#894=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#895=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#896=VECTOR('',#895,1.00000000000000000E3); +#897=LINE('',#894,#896); +#898=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3)); +#899=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#900=VECTOR('',#899,1.00000000000000000E3); +#901=LINE('',#898,#900); +#902=DEFINITIONAL_REPRESENTATION('',(#901),#1); +#903=PCURVE('',#91,#902); +#904=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#905=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#906=VECTOR('',#905,1.00000000000000000E3); +#907=LINE('',#904,#906); +#908=DEFINITIONAL_REPRESENTATION('',(#907),#1); +#909=PCURVE('',#101,#908); +#910=SURFACE_CURVE('',#897,(#903,#909),.CURVE_3D.); +#911=EDGE_CURVE('',#141,#169,#910,.T.); +#912=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#913=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#914=VECTOR('',#913,1.00000000000000000E3); +#915=LINE('',#912,#914); +#916=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#917=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#918=VECTOR('',#917,1.00000000000000000E3); +#919=LINE('',#916,#918); +#920=DEFINITIONAL_REPRESENTATION('',(#919),#1); +#921=PCURVE('',#96,#920); +#922=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#923=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#924=VECTOR('',#923,1.00000000000000000E3); +#925=LINE('',#922,#924); +#926=DEFINITIONAL_REPRESENTATION('',(#925),#1); +#927=PCURVE('',#101,#926); +#928=SURFACE_CURVE('',#915,(#921,#927),.CURVE_3D.); +#929=EDGE_CURVE('',#143,#167,#928,.T.); +#930=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#931=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#932=VECTOR('',#931,1.00000000000000000E3); +#933=LINE('',#930,#932); +#934=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#935=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#936=VECTOR('',#935,1.00000000000000000E3); +#937=LINE('',#934,#936); +#938=DEFINITIONAL_REPRESENTATION('',(#937),#1); +#939=PCURVE('',#96,#938); +#940=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#941=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#942=VECTOR('',#941,1.00000000000000000E3); +#943=LINE('',#940,#942); +#944=DEFINITIONAL_REPRESENTATION('',(#943),#1); +#945=PCURVE('',#121,#944); +#946=SURFACE_CURVE('',#933,(#939,#945),.CURVE_3D.); +#947=EDGE_CURVE('',#155,#163,#946,.T.); +#948=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#949=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#950=VECTOR('',#949,1.00000000000000000E3); +#951=LINE('',#948,#950); +#952=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#953=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#954=VECTOR('',#953,1.00000000000000000E3); +#955=LINE('',#952,#954); +#956=DEFINITIONAL_REPRESENTATION('',(#955),#1); +#957=PCURVE('',#106,#956); +#958=CARTESIAN_POINT('',(5.00000000000000000E2,3.00000000000000000E3)); +#959=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#960=VECTOR('',#959,1.00000000000000000E3); +#961=LINE('',#958,#960); +#962=DEFINITIONAL_REPRESENTATION('',(#961),#1); +#963=PCURVE('',#116,#962); +#964=SURFACE_CURVE('',#951,(#957,#963),.CURVE_3D.); +#965=EDGE_CURVE('',#161,#171,#964,.T.); +#966=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#967=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#968=VECTOR('',#967,1.00000000000000000E3); +#969=LINE('',#966,#968); +#970=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#971=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#972=VECTOR('',#971,1.00000000000000000E3); +#973=LINE('',#970,#972); +#974=DEFINITIONAL_REPRESENTATION('',(#973),#1); +#975=PCURVE('',#106,#974); +#976=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#977=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#978=VECTOR('',#977,1.00000000000000000E3); +#979=LINE('',#976,#978); +#980=DEFINITIONAL_REPRESENTATION('',(#979),#1); +#981=PCURVE('',#111,#980); +#982=SURFACE_CURVE('',#969,(#975,#981),.CURVE_3D.); +#983=EDGE_CURVE('',#171,#173,#982,.T.); +#984=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#985=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#986=VECTOR('',#985,1.00000000000000000E3); +#987=LINE('',#984,#986); +#988=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#989=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#990=VECTOR('',#989,1.00000000000000000E3); +#991=LINE('',#988,#990); +#992=DEFINITIONAL_REPRESENTATION('',(#991),#1); +#993=PCURVE('',#106,#992); +#994=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#995=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#996=VECTOR('',#995,1.00000000000000000E3); +#997=LINE('',#994,#996); +#998=DEFINITIONAL_REPRESENTATION('',(#997),#1); +#999=PCURVE('',#121,#998); +#1000=SURFACE_CURVE('',#987,(#993,#999),.CURVE_3D.); +#1001=EDGE_CURVE('',#163,#173,#1000,.T.); +#1002=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#1003=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1004=VECTOR('',#1003,1.00000000000000000E3); +#1005=LINE('',#1002,#1004); +#1006=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#1007=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1008=VECTOR('',#1007,1.00000000000000000E3); +#1009=LINE('',#1006,#1008); +#1010=DEFINITIONAL_REPRESENTATION('',(#1009),#1); +#1011=PCURVE('',#111,#1010); +#1012=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#1013=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1014=VECTOR('',#1013,1.00000000000000000E3); +#1015=LINE('',#1012,#1014); +#1016=DEFINITIONAL_REPRESENTATION('',(#1015),#1); +#1017=PCURVE('',#121,#1016); +#1018=SURFACE_CURVE('',#1005,(#1011,#1017),.CURVE_3D.); +#1019=EDGE_CURVE('',#149,#173,#1018,.T.); +#1020=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#1021=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1022=VECTOR('',#1021,1.00000000000000000E3); +#1023=LINE('',#1020,#1022); +#1024=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#1025=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1026=VECTOR('',#1025,1.00000000000000000E3); +#1027=LINE('',#1024,#1026); +#1028=DEFINITIONAL_REPRESENTATION('',(#1027),#1); +#1029=PCURVE('',#111,#1028); +#1030=CARTESIAN_POINT('',(3.00000000000000000E3,1.50000000000000000E3)); +#1031=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1032=VECTOR('',#1031,1.00000000000000000E3); +#1033=LINE('',#1030,#1032); +#1034=DEFINITIONAL_REPRESENTATION('',(#1033),#1); +#1035=PCURVE('',#116,#1034); +#1036=SURFACE_CURVE('',#1023,(#1029,#1035),.CURVE_3D.); +#1037=EDGE_CURVE('',#151,#171,#1036,.T.); +#1038=ORIENTED_EDGE('',*,*,#191,.T.); +#1039=ORIENTED_EDGE('',*,*,#209,.F.); +#1040=ORIENTED_EDGE('',*,*,#227,.F.); +#1041=ORIENTED_EDGE('',*,*,#245,.T.); +#1042=EDGE_LOOP('',(#1038,#1039,#1040,#1041)); +#1043=FACE_OUTER_BOUND('',#1042,.T.); +#1044=ADVANCED_FACE('left-0',(#1043),#6,.T.); +#1045=ORIENTED_EDGE('',*,*,#263,.T.); +#1046=ORIENTED_EDGE('',*,*,#281,.F.); +#1047=ORIENTED_EDGE('',*,*,#245,.F.); +#1048=ORIENTED_EDGE('',*,*,#299,.T.); +#1049=EDGE_LOOP('',(#1045,#1046,#1047,#1048)); +#1050=FACE_OUTER_BOUND('',#1049,.T.); +#1051=ADVANCED_FACE('front-1',(#1050),#11,.T.); +#1052=ORIENTED_EDGE('',*,*,#317,.T.); +#1053=ORIENTED_EDGE('',*,*,#335,.F.); +#1054=ORIENTED_EDGE('',*,*,#299,.F.); +#1055=ORIENTED_EDGE('',*,*,#227,.T.); +#1056=EDGE_LOOP('',(#1052,#1053,#1054,#1055)); +#1057=FACE_OUTER_BOUND('',#1056,.T.); +#1058=ADVANCED_FACE('bottom-2',(#1057),#16,.T.); +#1059=ORIENTED_EDGE('',*,*,#353,.T.); +#1060=ORIENTED_EDGE('',*,*,#371,.F.); +#1061=ORIENTED_EDGE('',*,*,#191,.F.); +#1062=ORIENTED_EDGE('',*,*,#389,.T.); +#1063=EDGE_LOOP('',(#1059,#1060,#1061,#1062)); +#1064=FACE_OUTER_BOUND('',#1063,.T.); +#1065=ADVANCED_FACE('left-3',(#1064),#21,.T.); +#1066=ORIENTED_EDGE('',*,*,#407,.T.); +#1067=ORIENTED_EDGE('',*,*,#425,.F.); +#1068=ORIENTED_EDGE('',*,*,#389,.F.); +#1069=ORIENTED_EDGE('',*,*,#281,.T.); +#1070=EDGE_LOOP('',(#1066,#1067,#1068,#1069)); +#1071=FACE_OUTER_BOUND('',#1070,.T.); +#1072=ADVANCED_FACE('front-4',(#1071),#26,.T.); +#1073=ORIENTED_EDGE('',*,*,#425,.T.); +#1074=ORIENTED_EDGE('',*,*,#443,.T.); +#1075=ORIENTED_EDGE('',*,*,#461,.F.); +#1076=ORIENTED_EDGE('',*,*,#353,.F.); +#1077=EDGE_LOOP('',(#1073,#1074,#1075,#1076)); +#1078=FACE_OUTER_BOUND('',#1077,.T.); +#1079=ADVANCED_FACE('top-5',(#1078),#31,.T.); +#1080=ORIENTED_EDGE('',*,*,#479,.T.); +#1081=ORIENTED_EDGE('',*,*,#497,.F.); +#1082=ORIENTED_EDGE('',*,*,#515,.F.); +#1083=ORIENTED_EDGE('',*,*,#209,.T.); +#1084=EDGE_LOOP('',(#1080,#1081,#1082,#1083)); +#1085=FACE_OUTER_BOUND('',#1084,.T.); +#1086=ADVANCED_FACE('left-6',(#1085),#36,.T.); +#1087=ORIENTED_EDGE('',*,*,#497,.T.); +#1088=ORIENTED_EDGE('',*,*,#533,.T.); +#1089=ORIENTED_EDGE('',*,*,#551,.F.); +#1090=ORIENTED_EDGE('',*,*,#569,.F.); +#1091=EDGE_LOOP('',(#1087,#1088,#1089,#1090)); +#1092=FACE_OUTER_BOUND('',#1091,.T.); +#1093=ADVANCED_FACE('back-7',(#1092),#41,.T.); +#1094=ORIENTED_EDGE('',*,*,#569,.T.); +#1095=ORIENTED_EDGE('',*,*,#587,.F.); +#1096=ORIENTED_EDGE('',*,*,#317,.F.); +#1097=ORIENTED_EDGE('',*,*,#515,.T.); +#1098=EDGE_LOOP('',(#1094,#1095,#1096,#1097)); +#1099=FACE_OUTER_BOUND('',#1098,.T.); +#1100=ADVANCED_FACE('bottom-8',(#1099),#46,.T.); +#1101=ORIENTED_EDGE('',*,*,#605,.T.); +#1102=ORIENTED_EDGE('',*,*,#623,.F.); +#1103=ORIENTED_EDGE('',*,*,#479,.F.); +#1104=ORIENTED_EDGE('',*,*,#371,.T.); +#1105=EDGE_LOOP('',(#1101,#1102,#1103,#1104)); +#1106=FACE_OUTER_BOUND('',#1105,.T.); +#1107=ADVANCED_FACE('left-9',(#1106),#51,.T.); +#1108=ORIENTED_EDGE('',*,*,#641,.T.); +#1109=ORIENTED_EDGE('',*,*,#659,.T.); +#1110=ORIENTED_EDGE('',*,*,#677,.F.); +#1111=ORIENTED_EDGE('',*,*,#695,.F.); +#1112=EDGE_LOOP('',(#1108,#1109,#1110,#1111)); +#1113=FACE_OUTER_BOUND('',#1112,.T.); +#1114=ADVANCED_FACE('left-10',(#1113),#56,.F.); +#1115=ORIENTED_EDGE('',*,*,#623,.T.); +#1116=ORIENTED_EDGE('',*,*,#713,.T.); +#1117=ORIENTED_EDGE('',*,*,#659,.F.); +#1118=ORIENTED_EDGE('',*,*,#533,.F.); +#1119=EDGE_LOOP('',(#1115,#1116,#1117,#1118)); +#1120=FACE_OUTER_BOUND('',#1119,.T.); +#1121=ADVANCED_FACE('back-11',(#1120),#61,.T.); +#1122=ORIENTED_EDGE('',*,*,#461,.T.); +#1123=ORIENTED_EDGE('',*,*,#677,.T.); +#1124=ORIENTED_EDGE('',*,*,#713,.F.); +#1125=ORIENTED_EDGE('',*,*,#605,.F.); +#1126=EDGE_LOOP('',(#1122,#1123,#1124,#1125)); +#1127=FACE_OUTER_BOUND('',#1126,.T.); +#1128=ADVANCED_FACE('top-12',(#1127),#66,.T.); +#1129=ORIENTED_EDGE('',*,*,#731,.T.); +#1130=ORIENTED_EDGE('',*,*,#749,.T.); +#1131=ORIENTED_EDGE('',*,*,#767,.F.); +#1132=ORIENTED_EDGE('',*,*,#785,.F.); +#1133=EDGE_LOOP('',(#1129,#1130,#1131,#1132)); +#1134=FACE_OUTER_BOUND('',#1133,.T.); +#1135=ADVANCED_FACE('right-13',(#1134),#71,.T.); +#1136=ORIENTED_EDGE('',*,*,#785,.T.); +#1137=ORIENTED_EDGE('',*,*,#803,.F.); +#1138=ORIENTED_EDGE('',*,*,#263,.F.); +#1139=ORIENTED_EDGE('',*,*,#821,.T.); +#1140=EDGE_LOOP('',(#1136,#1137,#1138,#1139)); +#1141=FACE_OUTER_BOUND('',#1140,.T.); +#1142=ADVANCED_FACE('front-14',(#1141),#76,.T.); +#1143=ORIENTED_EDGE('',*,*,#839,.T.); +#1144=ORIENTED_EDGE('',*,*,#731,.F.); +#1145=ORIENTED_EDGE('',*,*,#821,.F.); +#1146=ORIENTED_EDGE('',*,*,#335,.T.); +#1147=EDGE_LOOP('',(#1143,#1144,#1145,#1146)); +#1148=FACE_OUTER_BOUND('',#1147,.T.); +#1149=ADVANCED_FACE('bottom-15',(#1148),#81,.T.); +#1150=ORIENTED_EDGE('',*,*,#767,.T.); +#1151=ORIENTED_EDGE('',*,*,#857,.T.); +#1152=ORIENTED_EDGE('',*,*,#875,.F.); +#1153=ORIENTED_EDGE('',*,*,#893,.F.); +#1154=EDGE_LOOP('',(#1150,#1151,#1152,#1153)); +#1155=FACE_OUTER_BOUND('',#1154,.T.); +#1156=ADVANCED_FACE('right-16',(#1155),#86,.T.); +#1157=ORIENTED_EDGE('',*,*,#893,.T.); +#1158=ORIENTED_EDGE('',*,*,#911,.F.); +#1159=ORIENTED_EDGE('',*,*,#407,.F.); +#1160=ORIENTED_EDGE('',*,*,#803,.T.); +#1161=EDGE_LOOP('',(#1157,#1158,#1159,#1160)); +#1162=FACE_OUTER_BOUND('',#1161,.T.); +#1163=ADVANCED_FACE('front-17',(#1162),#91,.T.); +#1164=ORIENTED_EDGE('',*,*,#695,.T.); +#1165=ORIENTED_EDGE('',*,*,#929,.T.); +#1166=ORIENTED_EDGE('',*,*,#857,.F.); +#1167=ORIENTED_EDGE('',*,*,#947,.F.); +#1168=EDGE_LOOP('',(#1164,#1165,#1166,#1167)); +#1169=FACE_OUTER_BOUND('',#1168,.T.); +#1170=ADVANCED_FACE('front-18',(#1169),#96,.F.); +#1171=ORIENTED_EDGE('',*,*,#911,.T.); +#1172=ORIENTED_EDGE('',*,*,#875,.T.); +#1173=ORIENTED_EDGE('',*,*,#929,.F.); +#1174=ORIENTED_EDGE('',*,*,#443,.F.); +#1175=EDGE_LOOP('',(#1171,#1172,#1173,#1174)); +#1176=FACE_OUTER_BOUND('',#1175,.T.); +#1177=ADVANCED_FACE('top-19',(#1176),#101,.T.); +#1178=ORIENTED_EDGE('',*,*,#965,.T.); +#1179=ORIENTED_EDGE('',*,*,#983,.T.); +#1180=ORIENTED_EDGE('',*,*,#1001,.F.); +#1181=ORIENTED_EDGE('',*,*,#749,.F.); +#1182=EDGE_LOOP('',(#1178,#1179,#1180,#1181)); +#1183=FACE_OUTER_BOUND('',#1182,.T.); +#1184=ADVANCED_FACE('right-20',(#1183),#106,.T.); +#1185=ORIENTED_EDGE('',*,*,#551,.T.); +#1186=ORIENTED_EDGE('',*,*,#1019,.T.); +#1187=ORIENTED_EDGE('',*,*,#983,.F.); +#1188=ORIENTED_EDGE('',*,*,#1037,.F.); +#1189=EDGE_LOOP('',(#1185,#1186,#1187,#1188)); +#1190=FACE_OUTER_BOUND('',#1189,.T.); +#1191=ADVANCED_FACE('back-21',(#1190),#111,.T.); +#1192=ORIENTED_EDGE('',*,*,#1037,.T.); +#1193=ORIENTED_EDGE('',*,*,#965,.F.); +#1194=ORIENTED_EDGE('',*,*,#839,.F.); +#1195=ORIENTED_EDGE('',*,*,#587,.T.); +#1196=EDGE_LOOP('',(#1192,#1193,#1194,#1195)); +#1197=FACE_OUTER_BOUND('',#1196,.T.); +#1198=ADVANCED_FACE('bottom-22',(#1197),#116,.T.); +#1199=ORIENTED_EDGE('',*,*,#947,.T.); +#1200=ORIENTED_EDGE('',*,*,#1001,.T.); +#1201=ORIENTED_EDGE('',*,*,#1019,.F.); +#1202=ORIENTED_EDGE('',*,*,#641,.F.); +#1203=EDGE_LOOP('',(#1199,#1200,#1201,#1202)); +#1204=FACE_OUTER_BOUND('',#1203,.T.); +#1205=ADVANCED_FACE('bottom-23',(#1204),#121,.F.); +#1206=CLOSED_SHELL('',(#1044,#1065,#1086,#1058,#1051,#1079,#1107,#1072,#1093,#1100,#1149,#1142,#1177,#1128,#1121,#1163,#1191,#1198,#1135,#1156,#1170,#1114,#1205,#1184)); +#1207=MANIFOLD_SOLID_BREP('box-cut-0',#1206); +#1208=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#1209=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#1210=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#1211=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000426325642887E-5),#1208,'distance_accuracy_value','exchange error bound'); +#1212=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#1211)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#1208,#1209,#1210)) REPRESENTATION_CONTEXT('','')); +#1213=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#1207),#1212); +#1214=APPLICATION_CONTEXT('automotive design'); +#1215=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#1214); +#1216=PRODUCT_CONTEXT('',#1214,'mechanical'); +#1217=PRODUCT('box-cut','box-cut','',(#1216)); +#1218=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#1217,.NOT_KNOWN.); +#1219=PRODUCT_DEFINITION_CONTEXT('part definition',#1214,'design'); +#1220=PRODUCT_DEFINITION('','',#1218,#1219); +#1221=PRODUCT_DEFINITION_SHAPE('','',#1220); +#1222=SHAPE_DEFINITION_REPRESENTATION(#1221,#1213); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/box-cut.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/box-cut.step.mm.report.json new file mode 100644 index 0000000..d75fee3 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-cut.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":48,"exchange_error_bound":0.000010000042632564289,"faces":24,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"1","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-intersection.brep.json b/main/opengeometry/tests/fixtures/cases/box-intersection.brep.json new file mode 100644 index 0000000..12504a6 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-intersection.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"box-intersection","revision":1,"geometry":{"surfaces":[{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}}],"curves":[{"kind":"Line","origin":[1.5,3.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,0.5,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,0.5,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,0.5,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,0.5,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.5,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,3.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.5,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,3.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.5,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,3.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.5,-3.0],"direction":[1.0,0.0,0.0]}],"pcurves":[{"kind":"Line2","origin":[2.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[1.5,3.0,-0.5],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[1.5,3.0,-3.0],"outgoing_halfedge":1,"tolerance":1e-8},{"id":2,"position":[1.5,0.5,-3.0],"outgoing_halfedge":2,"tolerance":1e-8},{"id":3,"position":[1.5,0.5,-0.5],"outgoing_halfedge":3,"tolerance":1e-8},{"id":4,"position":[3.0,0.5,-0.5],"outgoing_halfedge":4,"tolerance":1e-8},{"id":5,"position":[3.0,0.5,-3.0],"outgoing_halfedge":5,"tolerance":1e-8},{"id":6,"position":[3.0,3.0,-3.0],"outgoing_halfedge":6,"tolerance":1e-8},{"id":7,"position":[3.0,3.0,-0.5],"outgoing_halfedge":7,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":2.5}},"halfedge":0,"twin_halfedge":23,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":2.5}},"halfedge":1,"twin_halfedge":12,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":2.5}},"halfedge":2,"twin_halfedge":19,"tolerance":1e-8,"chart_seam":false},{"id":3,"geometry":{"kind":"Curve","curve":3,"range":{"lo":0.0,"hi":2.5}},"halfedge":3,"twin_halfedge":10,"tolerance":1e-8,"chart_seam":false},{"id":4,"geometry":{"kind":"Curve","curve":4,"range":{"lo":0.0,"hi":2.5}},"halfedge":4,"twin_halfedge":17,"tolerance":1e-8,"chart_seam":false},{"id":5,"geometry":{"kind":"Curve","curve":5,"range":{"lo":0.0,"hi":2.5}},"halfedge":5,"twin_halfedge":14,"tolerance":1e-8,"chart_seam":false},{"id":6,"geometry":{"kind":"Curve","curve":6,"range":{"lo":0.0,"hi":2.5}},"halfedge":6,"twin_halfedge":21,"tolerance":1e-8,"chart_seam":false},{"id":7,"geometry":{"kind":"Curve","curve":7,"range":{"lo":0.0,"hi":2.5}},"halfedge":7,"twin_halfedge":8,"tolerance":1e-8,"chart_seam":false},{"id":8,"geometry":{"kind":"Curve","curve":8,"range":{"lo":0.0,"hi":1.5}},"halfedge":9,"twin_halfedge":20,"tolerance":1e-8,"chart_seam":false},{"id":9,"geometry":{"kind":"Curve","curve":9,"range":{"lo":0.0,"hi":1.5}},"halfedge":11,"twin_halfedge":18,"tolerance":1e-8,"chart_seam":false},{"id":10,"geometry":{"kind":"Curve","curve":10,"range":{"lo":0.0,"hi":1.5}},"halfedge":13,"twin_halfedge":22,"tolerance":1e-8,"chart_seam":false},{"id":11,"geometry":{"kind":"Curve","curve":11,"range":{"lo":0.0,"hi":1.5}},"halfedge":15,"twin_halfedge":16,"tolerance":1e-8,"chart_seam":false}],"halfedges":[{"id":0,"from":0,"to":1,"twin":23,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":1,"to":2,"twin":12,"next":2,"prev":0,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":2,"to":3,"twin":19,"next":3,"prev":1,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":3,"to":0,"twin":10,"next":0,"prev":2,"edge":3,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":4,"to":5,"twin":17,"next":5,"prev":7,"edge":4,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":5,"to":6,"twin":14,"next":6,"prev":4,"edge":5,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":5,"periodic_lift":[0,0]}},{"id":6,"from":6,"to":7,"twin":21,"next":7,"prev":5,"edge":6,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":6,"periodic_lift":[0,0]}},{"id":7,"from":7,"to":4,"twin":8,"next":4,"prev":6,"edge":7,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":7,"periodic_lift":[0,0]}},{"id":8,"from":4,"to":7,"twin":7,"next":9,"prev":11,"edge":7,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":8,"periodic_lift":[0,0]}},{"id":9,"from":7,"to":0,"twin":20,"next":10,"prev":8,"edge":8,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":9,"periodic_lift":[0,0]}},{"id":10,"from":0,"to":3,"twin":3,"next":11,"prev":9,"edge":3,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":10,"periodic_lift":[0,0]}},{"id":11,"from":3,"to":4,"twin":18,"next":8,"prev":10,"edge":9,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":11,"periodic_lift":[0,0]}},{"id":12,"from":2,"to":1,"twin":1,"next":13,"prev":15,"edge":1,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":12,"periodic_lift":[0,0]}},{"id":13,"from":1,"to":6,"twin":22,"next":14,"prev":12,"edge":10,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":13,"periodic_lift":[0,0]}},{"id":14,"from":6,"to":5,"twin":5,"next":15,"prev":13,"edge":5,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":14,"periodic_lift":[0,0]}},{"id":15,"from":5,"to":2,"twin":16,"next":12,"prev":14,"edge":11,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":15,"periodic_lift":[0,0]}},{"id":16,"from":2,"to":5,"twin":15,"next":17,"prev":19,"edge":11,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":16,"periodic_lift":[0,0]}},{"id":17,"from":5,"to":4,"twin":4,"next":18,"prev":16,"edge":4,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":17,"periodic_lift":[0,0]}},{"id":18,"from":4,"to":3,"twin":11,"next":19,"prev":17,"edge":9,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":18,"periodic_lift":[0,0]}},{"id":19,"from":3,"to":2,"twin":2,"next":16,"prev":18,"edge":2,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":19,"periodic_lift":[0,0]}},{"id":20,"from":0,"to":7,"twin":9,"next":21,"prev":23,"edge":8,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":20,"periodic_lift":[0,0]}},{"id":21,"from":7,"to":6,"twin":6,"next":22,"prev":20,"edge":6,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":21,"periodic_lift":[0,0]}},{"id":22,"from":6,"to":1,"twin":13,"next":23,"prev":21,"edge":10,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":22,"periodic_lift":[0,0]}},{"id":23,"from":1,"to":0,"twin":0,"next":20,"prev":22,"edge":0,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":23,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false},{"id":2,"start_halfedge":8,"face_ref":2,"is_hole":false},{"id":3,"start_halfedge":12,"face_ref":3,"is_hole":false},{"id":4,"start_halfedge":16,"face_ref":4,"is_hole":false},{"id":5,"start_halfedge":20,"face_ref":5,"is_hole":false}],"faces":[{"id":0,"key":"left-0","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.50000001},{"lo":-1e-8,"hi":2.50000001}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"left","face":2}],"role":"Split","reversed":false}},{"id":1,"key":"right-1","surface":1,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":0.49999999,"hi":3.00000001}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":2,"key":"front-2","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":-1e-8,"hi":2.50000001}],"outer":2,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"front","face":4}],"role":"Split","reversed":false}},{"id":3,"key":"back-3","surface":3,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":1.49999999,"hi":3.00000001}],"outer":3,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":4,"key":"bottom-4","surface":4,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.50000001},{"lo":-1e-8,"hi":1.50000001}],"outer":4,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":5,"key":"top-5","surface":5,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.49999999,"hi":3.00000001},{"lo":0.49999999,"hi":3.00000001}],"outer":5,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,5,3,4,2,1],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-intersection.handlers.json b/main/opengeometry/tests/fixtures/cases/box-intersection.handlers.json new file mode 100644 index 0000000..4b8564a --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-intersection.handlers.json @@ -0,0 +1 @@ +["boolean_boxes"] \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-intersection.step.m b/main/opengeometry/tests/fixtures/cases/box-intersection.step.m new file mode 100644 index 0000000..78fd76d --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-intersection.step.m @@ -0,0 +1,331 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('box-intersection','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#3=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#13=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#18=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#23=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#24=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#28=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#33=VERTEX_POINT('',#32); +#34=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#35=VERTEX_POINT('',#34); +#36=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#37=VERTEX_POINT('',#36); +#38=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#39=VERTEX_POINT('',#38); +#40=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#41=VERTEX_POINT('',#40); +#42=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#43=VERTEX_POINT('',#42); +#44=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#45=VERTEX_POINT('',#44); +#46=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#47=VERTEX_POINT('',#46); +#48=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#49=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#50=VECTOR('',#49,1.00000000000000000E0); +#51=LINE('',#48,#50); +#52=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#53=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#54=VECTOR('',#53,1.00000000000000000E0); +#55=LINE('',#52,#54); +#56=DEFINITIONAL_REPRESENTATION('',(#55),#1); +#57=PCURVE('',#6,#56); +#58=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#59=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#60=VECTOR('',#59,1.00000000000000000E0); +#61=LINE('',#58,#60); +#62=DEFINITIONAL_REPRESENTATION('',(#61),#1); +#63=PCURVE('',#31,#62); +#64=SURFACE_CURVE('',#51,(#57,#63),.CURVE_3D.); +#65=EDGE_CURVE('',#33,#35,#64,.T.); +#66=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#67=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#68=VECTOR('',#67,1.00000000000000000E0); +#69=LINE('',#66,#68); +#70=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#71=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#72=VECTOR('',#71,1.00000000000000000E0); +#73=LINE('',#70,#72); +#74=DEFINITIONAL_REPRESENTATION('',(#73),#1); +#75=PCURVE('',#6,#74); +#76=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#77=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#78=VECTOR('',#77,1.00000000000000000E0); +#79=LINE('',#76,#78); +#80=DEFINITIONAL_REPRESENTATION('',(#79),#1); +#81=PCURVE('',#21,#80); +#82=SURFACE_CURVE('',#69,(#75,#81),.CURVE_3D.); +#83=EDGE_CURVE('',#37,#35,#82,.T.); +#84=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#85=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#86=VECTOR('',#85,1.00000000000000000E0); +#87=LINE('',#84,#86); +#88=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#90=VECTOR('',#89,1.00000000000000000E0); +#91=LINE('',#88,#90); +#92=DEFINITIONAL_REPRESENTATION('',(#91),#1); +#93=PCURVE('',#6,#92); +#94=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#95=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#96=VECTOR('',#95,1.00000000000000000E0); +#97=LINE('',#94,#96); +#98=DEFINITIONAL_REPRESENTATION('',(#97),#1); +#99=PCURVE('',#26,#98); +#100=SURFACE_CURVE('',#87,(#93,#99),.CURVE_3D.); +#101=EDGE_CURVE('',#39,#37,#100,.T.); +#102=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#103=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#104=VECTOR('',#103,1.00000000000000000E0); +#105=LINE('',#102,#104); +#106=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#107=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E0); +#109=LINE('',#106,#108); +#110=DEFINITIONAL_REPRESENTATION('',(#109),#1); +#111=PCURVE('',#6,#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#113=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#114=VECTOR('',#113,1.00000000000000000E0); +#115=LINE('',#112,#114); +#116=DEFINITIONAL_REPRESENTATION('',(#115),#1); +#117=PCURVE('',#16,#116); +#118=SURFACE_CURVE('',#105,(#111,#117),.CURVE_3D.); +#119=EDGE_CURVE('',#39,#33,#118,.T.); +#120=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#121=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#122=VECTOR('',#121,1.00000000000000000E0); +#123=LINE('',#120,#122); +#124=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#125=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E0); +#127=LINE('',#124,#126); +#128=DEFINITIONAL_REPRESENTATION('',(#127),#1); +#129=PCURVE('',#11,#128); +#130=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#131=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#132=VECTOR('',#131,1.00000000000000000E0); +#133=LINE('',#130,#132); +#134=DEFINITIONAL_REPRESENTATION('',(#133),#1); +#135=PCURVE('',#26,#134); +#136=SURFACE_CURVE('',#123,(#129,#135),.CURVE_3D.); +#137=EDGE_CURVE('',#41,#43,#136,.T.); +#138=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#139=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#140=VECTOR('',#139,1.00000000000000000E0); +#141=LINE('',#138,#140); +#142=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1)); +#143=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#144=VECTOR('',#143,1.00000000000000000E0); +#145=LINE('',#142,#144); +#146=DEFINITIONAL_REPRESENTATION('',(#145),#1); +#147=PCURVE('',#11,#146); +#148=CARTESIAN_POINT('',(5.00000000000000000E-1,3.00000000000000000E0)); +#149=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#150=VECTOR('',#149,1.00000000000000000E0); +#151=LINE('',#148,#150); +#152=DEFINITIONAL_REPRESENTATION('',(#151),#1); +#153=PCURVE('',#21,#152); +#154=SURFACE_CURVE('',#141,(#147,#153),.CURVE_3D.); +#155=EDGE_CURVE('',#43,#45,#154,.T.); +#156=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#157=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#158=VECTOR('',#157,1.00000000000000000E0); +#159=LINE('',#156,#158); +#160=CARTESIAN_POINT('',(5.00000000000000000E-1,3.00000000000000000E0)); +#161=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#162=VECTOR('',#161,1.00000000000000000E0); +#163=LINE('',#160,#162); +#164=DEFINITIONAL_REPRESENTATION('',(#163),#1); +#165=PCURVE('',#11,#164); +#166=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1)); +#167=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#168=VECTOR('',#167,1.00000000000000000E0); +#169=LINE('',#166,#168); +#170=DEFINITIONAL_REPRESENTATION('',(#169),#1); +#171=PCURVE('',#31,#170); +#172=SURFACE_CURVE('',#159,(#165,#171),.CURVE_3D.); +#173=EDGE_CURVE('',#47,#45,#172,.T.); +#174=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#175=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#176=VECTOR('',#175,1.00000000000000000E0); +#177=LINE('',#174,#176); +#178=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#179=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#180=VECTOR('',#179,1.00000000000000000E0); +#181=LINE('',#178,#180); +#182=DEFINITIONAL_REPRESENTATION('',(#181),#1); +#183=PCURVE('',#11,#182); +#184=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#185=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#186=VECTOR('',#185,1.00000000000000000E0); +#187=LINE('',#184,#186); +#188=DEFINITIONAL_REPRESENTATION('',(#187),#1); +#189=PCURVE('',#16,#188); +#190=SURFACE_CURVE('',#177,(#183,#189),.CURVE_3D.); +#191=EDGE_CURVE('',#41,#47,#190,.T.); +#192=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#193=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#194=VECTOR('',#193,1.00000000000000000E0); +#195=LINE('',#192,#194); +#196=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#197=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#198=VECTOR('',#197,1.00000000000000000E0); +#199=LINE('',#196,#198); +#200=DEFINITIONAL_REPRESENTATION('',(#199),#1); +#201=PCURVE('',#16,#200); +#202=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#203=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#204=VECTOR('',#203,1.00000000000000000E0); +#205=LINE('',#202,#204); +#206=DEFINITIONAL_REPRESENTATION('',(#205),#1); +#207=PCURVE('',#31,#206); +#208=SURFACE_CURVE('',#195,(#201,#207),.CURVE_3D.); +#209=EDGE_CURVE('',#33,#47,#208,.T.); +#210=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#211=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#212=VECTOR('',#211,1.00000000000000000E0); +#213=LINE('',#210,#212); +#214=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#215=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#216=VECTOR('',#215,1.00000000000000000E0); +#217=LINE('',#214,#216); +#218=DEFINITIONAL_REPRESENTATION('',(#217),#1); +#219=PCURVE('',#16,#218); +#220=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#221=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#222=VECTOR('',#221,1.00000000000000000E0); +#223=LINE('',#220,#222); +#224=DEFINITIONAL_REPRESENTATION('',(#223),#1); +#225=PCURVE('',#26,#224); +#226=SURFACE_CURVE('',#213,(#219,#225),.CURVE_3D.); +#227=EDGE_CURVE('',#39,#41,#226,.T.); +#228=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#229=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#230=VECTOR('',#229,1.00000000000000000E0); +#231=LINE('',#228,#230); +#232=CARTESIAN_POINT('',(3.00000000000000000E0,1.50000000000000000E0)); +#233=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#234=VECTOR('',#233,1.00000000000000000E0); +#235=LINE('',#232,#234); +#236=DEFINITIONAL_REPRESENTATION('',(#235),#1); +#237=PCURVE('',#21,#236); +#238=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0)); +#239=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#240=VECTOR('',#239,1.00000000000000000E0); +#241=LINE('',#238,#240); +#242=DEFINITIONAL_REPRESENTATION('',(#241),#1); +#243=PCURVE('',#31,#242); +#244=SURFACE_CURVE('',#231,(#237,#243),.CURVE_3D.); +#245=EDGE_CURVE('',#35,#45,#244,.T.); +#246=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#247=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#248=VECTOR('',#247,1.00000000000000000E0); +#249=LINE('',#246,#248); +#250=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#251=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#252=VECTOR('',#251,1.00000000000000000E0); +#253=LINE('',#250,#252); +#254=DEFINITIONAL_REPRESENTATION('',(#253),#1); +#255=PCURVE('',#21,#254); +#256=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#257=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#258=VECTOR('',#257,1.00000000000000000E0); +#259=LINE('',#256,#258); +#260=DEFINITIONAL_REPRESENTATION('',(#259),#1); +#261=PCURVE('',#26,#260); +#262=SURFACE_CURVE('',#249,(#255,#261),.CURVE_3D.); +#263=EDGE_CURVE('',#37,#43,#262,.T.); +#264=ORIENTED_EDGE('',*,*,#65,.T.); +#265=ORIENTED_EDGE('',*,*,#83,.F.); +#266=ORIENTED_EDGE('',*,*,#101,.F.); +#267=ORIENTED_EDGE('',*,*,#119,.T.); +#268=EDGE_LOOP('',(#264,#265,#266,#267)); +#269=FACE_OUTER_BOUND('',#268,.T.); +#270=ADVANCED_FACE('left-0',(#269),#6,.T.); +#271=ORIENTED_EDGE('',*,*,#137,.T.); +#272=ORIENTED_EDGE('',*,*,#155,.T.); +#273=ORIENTED_EDGE('',*,*,#173,.F.); +#274=ORIENTED_EDGE('',*,*,#191,.F.); +#275=EDGE_LOOP('',(#271,#272,#273,#274)); +#276=FACE_OUTER_BOUND('',#275,.T.); +#277=ADVANCED_FACE('right-1',(#276),#11,.T.); +#278=ORIENTED_EDGE('',*,*,#191,.T.); +#279=ORIENTED_EDGE('',*,*,#209,.F.); +#280=ORIENTED_EDGE('',*,*,#119,.F.); +#281=ORIENTED_EDGE('',*,*,#227,.T.); +#282=EDGE_LOOP('',(#278,#279,#280,#281)); +#283=FACE_OUTER_BOUND('',#282,.T.); +#284=ADVANCED_FACE('front-2',(#283),#16,.T.); +#285=ORIENTED_EDGE('',*,*,#83,.T.); +#286=ORIENTED_EDGE('',*,*,#245,.T.); +#287=ORIENTED_EDGE('',*,*,#155,.F.); +#288=ORIENTED_EDGE('',*,*,#263,.F.); +#289=EDGE_LOOP('',(#285,#286,#287,#288)); +#290=FACE_OUTER_BOUND('',#289,.T.); +#291=ADVANCED_FACE('back-3',(#290),#21,.T.); +#292=ORIENTED_EDGE('',*,*,#263,.T.); +#293=ORIENTED_EDGE('',*,*,#137,.F.); +#294=ORIENTED_EDGE('',*,*,#227,.F.); +#295=ORIENTED_EDGE('',*,*,#101,.T.); +#296=EDGE_LOOP('',(#292,#293,#294,#295)); +#297=FACE_OUTER_BOUND('',#296,.T.); +#298=ADVANCED_FACE('bottom-4',(#297),#26,.T.); +#299=ORIENTED_EDGE('',*,*,#209,.T.); +#300=ORIENTED_EDGE('',*,*,#173,.T.); +#301=ORIENTED_EDGE('',*,*,#245,.F.); +#302=ORIENTED_EDGE('',*,*,#65,.F.); +#303=EDGE_LOOP('',(#299,#300,#301,#302)); +#304=FACE_OUTER_BOUND('',#303,.T.); +#305=ADVANCED_FACE('top-5',(#304),#31,.T.); +#306=CLOSED_SHELL('',(#270,#305,#291,#298,#284,#277)); +#307=MANIFOLD_SOLID_BREP('box-intersection-0',#306); +#308=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#309=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#310=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#311=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000426325642881E-8),#308,'distance_accuracy_value','exchange error bound'); +#312=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#311)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#308,#309,#310)) REPRESENTATION_CONTEXT('','')); +#313=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#307),#312); +#314=APPLICATION_CONTEXT('automotive design'); +#315=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#314); +#316=PRODUCT_CONTEXT('',#314,'mechanical'); +#317=PRODUCT('box-intersection','box-intersection','',(#316)); +#318=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#317,.NOT_KNOWN.); +#319=PRODUCT_DEFINITION_CONTEXT('part definition',#314,'design'); +#320=PRODUCT_DEFINITION('','',#318,#319); +#321=PRODUCT_DEFINITION_SHAPE('','',#320); +#322=SHAPE_DEFINITION_REPRESENTATION(#321,#313); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/box-intersection.step.m.report.json b/main/opengeometry/tests/fixtures/cases/box-intersection.step.m.report.json new file mode 100644 index 0000000..705b5fd --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-intersection.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":12,"exchange_error_bound":1.0000042632564288e-8,"faces":6,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"1","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-intersection.step.mm b/main/opengeometry/tests/fixtures/cases/box-intersection.step.mm new file mode 100644 index 0000000..8e5c2f0 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-intersection.step.mm @@ -0,0 +1,331 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('box-intersection','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#3=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#13=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#18=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#23=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#24=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#28=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#33=VERTEX_POINT('',#32); +#34=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#35=VERTEX_POINT('',#34); +#36=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#37=VERTEX_POINT('',#36); +#38=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#39=VERTEX_POINT('',#38); +#40=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#41=VERTEX_POINT('',#40); +#42=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#43=VERTEX_POINT('',#42); +#44=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#45=VERTEX_POINT('',#44); +#46=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#47=VERTEX_POINT('',#46); +#48=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#49=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#50=VECTOR('',#49,1.00000000000000000E3); +#51=LINE('',#48,#50); +#52=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#53=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#54=VECTOR('',#53,1.00000000000000000E3); +#55=LINE('',#52,#54); +#56=DEFINITIONAL_REPRESENTATION('',(#55),#1); +#57=PCURVE('',#6,#56); +#58=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#59=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#60=VECTOR('',#59,1.00000000000000000E3); +#61=LINE('',#58,#60); +#62=DEFINITIONAL_REPRESENTATION('',(#61),#1); +#63=PCURVE('',#31,#62); +#64=SURFACE_CURVE('',#51,(#57,#63),.CURVE_3D.); +#65=EDGE_CURVE('',#33,#35,#64,.T.); +#66=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#67=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#68=VECTOR('',#67,1.00000000000000000E3); +#69=LINE('',#66,#68); +#70=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#71=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#72=VECTOR('',#71,1.00000000000000000E3); +#73=LINE('',#70,#72); +#74=DEFINITIONAL_REPRESENTATION('',(#73),#1); +#75=PCURVE('',#6,#74); +#76=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#77=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#78=VECTOR('',#77,1.00000000000000000E3); +#79=LINE('',#76,#78); +#80=DEFINITIONAL_REPRESENTATION('',(#79),#1); +#81=PCURVE('',#21,#80); +#82=SURFACE_CURVE('',#69,(#75,#81),.CURVE_3D.); +#83=EDGE_CURVE('',#37,#35,#82,.T.); +#84=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#85=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#86=VECTOR('',#85,1.00000000000000000E3); +#87=LINE('',#84,#86); +#88=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#90=VECTOR('',#89,1.00000000000000000E3); +#91=LINE('',#88,#90); +#92=DEFINITIONAL_REPRESENTATION('',(#91),#1); +#93=PCURVE('',#6,#92); +#94=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#95=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#96=VECTOR('',#95,1.00000000000000000E3); +#97=LINE('',#94,#96); +#98=DEFINITIONAL_REPRESENTATION('',(#97),#1); +#99=PCURVE('',#26,#98); +#100=SURFACE_CURVE('',#87,(#93,#99),.CURVE_3D.); +#101=EDGE_CURVE('',#39,#37,#100,.T.); +#102=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#103=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#104=VECTOR('',#103,1.00000000000000000E3); +#105=LINE('',#102,#104); +#106=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#107=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E3); +#109=LINE('',#106,#108); +#110=DEFINITIONAL_REPRESENTATION('',(#109),#1); +#111=PCURVE('',#6,#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#113=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#114=VECTOR('',#113,1.00000000000000000E3); +#115=LINE('',#112,#114); +#116=DEFINITIONAL_REPRESENTATION('',(#115),#1); +#117=PCURVE('',#16,#116); +#118=SURFACE_CURVE('',#105,(#111,#117),.CURVE_3D.); +#119=EDGE_CURVE('',#39,#33,#118,.T.); +#120=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#121=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#122=VECTOR('',#121,1.00000000000000000E3); +#123=LINE('',#120,#122); +#124=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#125=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E3); +#127=LINE('',#124,#126); +#128=DEFINITIONAL_REPRESENTATION('',(#127),#1); +#129=PCURVE('',#11,#128); +#130=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#131=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#132=VECTOR('',#131,1.00000000000000000E3); +#133=LINE('',#130,#132); +#134=DEFINITIONAL_REPRESENTATION('',(#133),#1); +#135=PCURVE('',#26,#134); +#136=SURFACE_CURVE('',#123,(#129,#135),.CURVE_3D.); +#137=EDGE_CURVE('',#41,#43,#136,.T.); +#138=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#139=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#140=VECTOR('',#139,1.00000000000000000E3); +#141=LINE('',#138,#140); +#142=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2)); +#143=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#144=VECTOR('',#143,1.00000000000000000E3); +#145=LINE('',#142,#144); +#146=DEFINITIONAL_REPRESENTATION('',(#145),#1); +#147=PCURVE('',#11,#146); +#148=CARTESIAN_POINT('',(5.00000000000000000E2,3.00000000000000000E3)); +#149=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#150=VECTOR('',#149,1.00000000000000000E3); +#151=LINE('',#148,#150); +#152=DEFINITIONAL_REPRESENTATION('',(#151),#1); +#153=PCURVE('',#21,#152); +#154=SURFACE_CURVE('',#141,(#147,#153),.CURVE_3D.); +#155=EDGE_CURVE('',#43,#45,#154,.T.); +#156=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#157=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#158=VECTOR('',#157,1.00000000000000000E3); +#159=LINE('',#156,#158); +#160=CARTESIAN_POINT('',(5.00000000000000000E2,3.00000000000000000E3)); +#161=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#162=VECTOR('',#161,1.00000000000000000E3); +#163=LINE('',#160,#162); +#164=DEFINITIONAL_REPRESENTATION('',(#163),#1); +#165=PCURVE('',#11,#164); +#166=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2)); +#167=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#168=VECTOR('',#167,1.00000000000000000E3); +#169=LINE('',#166,#168); +#170=DEFINITIONAL_REPRESENTATION('',(#169),#1); +#171=PCURVE('',#31,#170); +#172=SURFACE_CURVE('',#159,(#165,#171),.CURVE_3D.); +#173=EDGE_CURVE('',#47,#45,#172,.T.); +#174=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#175=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#176=VECTOR('',#175,1.00000000000000000E3); +#177=LINE('',#174,#176); +#178=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#179=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#180=VECTOR('',#179,1.00000000000000000E3); +#181=LINE('',#178,#180); +#182=DEFINITIONAL_REPRESENTATION('',(#181),#1); +#183=PCURVE('',#11,#182); +#184=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#185=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#186=VECTOR('',#185,1.00000000000000000E3); +#187=LINE('',#184,#186); +#188=DEFINITIONAL_REPRESENTATION('',(#187),#1); +#189=PCURVE('',#16,#188); +#190=SURFACE_CURVE('',#177,(#183,#189),.CURVE_3D.); +#191=EDGE_CURVE('',#41,#47,#190,.T.); +#192=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#193=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#194=VECTOR('',#193,1.00000000000000000E3); +#195=LINE('',#192,#194); +#196=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#197=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#198=VECTOR('',#197,1.00000000000000000E3); +#199=LINE('',#196,#198); +#200=DEFINITIONAL_REPRESENTATION('',(#199),#1); +#201=PCURVE('',#16,#200); +#202=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#203=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#204=VECTOR('',#203,1.00000000000000000E3); +#205=LINE('',#202,#204); +#206=DEFINITIONAL_REPRESENTATION('',(#205),#1); +#207=PCURVE('',#31,#206); +#208=SURFACE_CURVE('',#195,(#201,#207),.CURVE_3D.); +#209=EDGE_CURVE('',#33,#47,#208,.T.); +#210=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#211=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#212=VECTOR('',#211,1.00000000000000000E3); +#213=LINE('',#210,#212); +#214=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#215=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#216=VECTOR('',#215,1.00000000000000000E3); +#217=LINE('',#214,#216); +#218=DEFINITIONAL_REPRESENTATION('',(#217),#1); +#219=PCURVE('',#16,#218); +#220=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#221=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#222=VECTOR('',#221,1.00000000000000000E3); +#223=LINE('',#220,#222); +#224=DEFINITIONAL_REPRESENTATION('',(#223),#1); +#225=PCURVE('',#26,#224); +#226=SURFACE_CURVE('',#213,(#219,#225),.CURVE_3D.); +#227=EDGE_CURVE('',#39,#41,#226,.T.); +#228=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#229=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#230=VECTOR('',#229,1.00000000000000000E3); +#231=LINE('',#228,#230); +#232=CARTESIAN_POINT('',(3.00000000000000000E3,1.50000000000000000E3)); +#233=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#234=VECTOR('',#233,1.00000000000000000E3); +#235=LINE('',#232,#234); +#236=DEFINITIONAL_REPRESENTATION('',(#235),#1); +#237=PCURVE('',#21,#236); +#238=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3)); +#239=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#240=VECTOR('',#239,1.00000000000000000E3); +#241=LINE('',#238,#240); +#242=DEFINITIONAL_REPRESENTATION('',(#241),#1); +#243=PCURVE('',#31,#242); +#244=SURFACE_CURVE('',#231,(#237,#243),.CURVE_3D.); +#245=EDGE_CURVE('',#35,#45,#244,.T.); +#246=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#247=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#248=VECTOR('',#247,1.00000000000000000E3); +#249=LINE('',#246,#248); +#250=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#251=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#252=VECTOR('',#251,1.00000000000000000E3); +#253=LINE('',#250,#252); +#254=DEFINITIONAL_REPRESENTATION('',(#253),#1); +#255=PCURVE('',#21,#254); +#256=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#257=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#258=VECTOR('',#257,1.00000000000000000E3); +#259=LINE('',#256,#258); +#260=DEFINITIONAL_REPRESENTATION('',(#259),#1); +#261=PCURVE('',#26,#260); +#262=SURFACE_CURVE('',#249,(#255,#261),.CURVE_3D.); +#263=EDGE_CURVE('',#37,#43,#262,.T.); +#264=ORIENTED_EDGE('',*,*,#65,.T.); +#265=ORIENTED_EDGE('',*,*,#83,.F.); +#266=ORIENTED_EDGE('',*,*,#101,.F.); +#267=ORIENTED_EDGE('',*,*,#119,.T.); +#268=EDGE_LOOP('',(#264,#265,#266,#267)); +#269=FACE_OUTER_BOUND('',#268,.T.); +#270=ADVANCED_FACE('left-0',(#269),#6,.T.); +#271=ORIENTED_EDGE('',*,*,#137,.T.); +#272=ORIENTED_EDGE('',*,*,#155,.T.); +#273=ORIENTED_EDGE('',*,*,#173,.F.); +#274=ORIENTED_EDGE('',*,*,#191,.F.); +#275=EDGE_LOOP('',(#271,#272,#273,#274)); +#276=FACE_OUTER_BOUND('',#275,.T.); +#277=ADVANCED_FACE('right-1',(#276),#11,.T.); +#278=ORIENTED_EDGE('',*,*,#191,.T.); +#279=ORIENTED_EDGE('',*,*,#209,.F.); +#280=ORIENTED_EDGE('',*,*,#119,.F.); +#281=ORIENTED_EDGE('',*,*,#227,.T.); +#282=EDGE_LOOP('',(#278,#279,#280,#281)); +#283=FACE_OUTER_BOUND('',#282,.T.); +#284=ADVANCED_FACE('front-2',(#283),#16,.T.); +#285=ORIENTED_EDGE('',*,*,#83,.T.); +#286=ORIENTED_EDGE('',*,*,#245,.T.); +#287=ORIENTED_EDGE('',*,*,#155,.F.); +#288=ORIENTED_EDGE('',*,*,#263,.F.); +#289=EDGE_LOOP('',(#285,#286,#287,#288)); +#290=FACE_OUTER_BOUND('',#289,.T.); +#291=ADVANCED_FACE('back-3',(#290),#21,.T.); +#292=ORIENTED_EDGE('',*,*,#263,.T.); +#293=ORIENTED_EDGE('',*,*,#137,.F.); +#294=ORIENTED_EDGE('',*,*,#227,.F.); +#295=ORIENTED_EDGE('',*,*,#101,.T.); +#296=EDGE_LOOP('',(#292,#293,#294,#295)); +#297=FACE_OUTER_BOUND('',#296,.T.); +#298=ADVANCED_FACE('bottom-4',(#297),#26,.T.); +#299=ORIENTED_EDGE('',*,*,#209,.T.); +#300=ORIENTED_EDGE('',*,*,#173,.T.); +#301=ORIENTED_EDGE('',*,*,#245,.F.); +#302=ORIENTED_EDGE('',*,*,#65,.F.); +#303=EDGE_LOOP('',(#299,#300,#301,#302)); +#304=FACE_OUTER_BOUND('',#303,.T.); +#305=ADVANCED_FACE('top-5',(#304),#31,.T.); +#306=CLOSED_SHELL('',(#270,#305,#291,#298,#284,#277)); +#307=MANIFOLD_SOLID_BREP('box-intersection-0',#306); +#308=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#309=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#310=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#311=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000426325642887E-5),#308,'distance_accuracy_value','exchange error bound'); +#312=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#311)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#308,#309,#310)) REPRESENTATION_CONTEXT('','')); +#313=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#307),#312); +#314=APPLICATION_CONTEXT('automotive design'); +#315=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#314); +#316=PRODUCT_CONTEXT('',#314,'mechanical'); +#317=PRODUCT('box-intersection','box-intersection','',(#316)); +#318=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#317,.NOT_KNOWN.); +#319=PRODUCT_DEFINITION_CONTEXT('part definition',#314,'design'); +#320=PRODUCT_DEFINITION('','',#318,#319); +#321=PRODUCT_DEFINITION_SHAPE('','',#320); +#322=SHAPE_DEFINITION_REPRESENTATION(#321,#313); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/box-intersection.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/box-intersection.step.mm.report.json new file mode 100644 index 0000000..e3c93f3 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-intersection.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":12,"exchange_error_bound":0.000010000042632564289,"faces":6,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"1","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-union.brep.json b/main/opengeometry/tests/fixtures/cases/box-union.brep.json new file mode 100644 index 0000000..2a07fba --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-union.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"box-union","revision":1,"geometry":{"surfaces":[{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[1.5,3.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-3.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-3.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[1.5,3.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[4.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[4.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[1.5,3.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[4.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-3.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[4.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[1.5,0.5,-3.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[1.5,3.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}}],"curves":[{"kind":"Line","origin":[0.0,0.5,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.5,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.5,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.5,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,0.5,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,3.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,3.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,0.5,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.5,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,3.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.5,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,3.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.5,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,3.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,0.5,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,0.5,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.5,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.5,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,3.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,3.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,0.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,0.5,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,0.5,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,0.0,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,3.5,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,3.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,3.0,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,3.0,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,3.5,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,3.5,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,3.5,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[1.5,3.0,-3.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,0.5,-3.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,0.5,-3.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,3.0,-3.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.5,-3.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,0.5,-3.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.5,-3.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,3.5,-3.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.5,3.0,-3.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.5,3.5,-3.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,3.0,-3.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,3.5,-3.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[4.5,0.5,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[4.5,0.5,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[4.5,3.0,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[4.5,0.5,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,3.0,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.5,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.5,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[4.5,3.0,-3.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[4.5,3.5,-0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[4.5,3.0,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[3.0,3.5,-0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,3.5,-3.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[4.5,0.5,-3.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[4.5,0.5,-3.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[4.5,3.0,-3.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,3.0,-3.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[3.0,0.5,-3.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[4.5,3.0,-3.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[4.5,3.5,-3.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[3.0,3.5,-3.5],"direction":[1.0,0.0,0.0]}],"pcurves":[{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.5,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[3.0,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,1.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[2.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.5,1.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,2.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,2.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.5,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.5,2.5],"direction":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[0.0,0.5,0.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[0.0,0.5,-0.5],"outgoing_halfedge":1,"tolerance":1e-8},{"id":2,"position":[0.0,0.0,-0.5],"outgoing_halfedge":2,"tolerance":1e-8},{"id":3,"position":[0.0,0.0,0.0],"outgoing_halfedge":3,"tolerance":1e-8},{"id":4,"position":[1.5,0.0,0.0],"outgoing_halfedge":4,"tolerance":1e-8},{"id":5,"position":[1.5,0.5,0.0],"outgoing_halfedge":5,"tolerance":1e-8},{"id":6,"position":[1.5,0.0,-0.5],"outgoing_halfedge":9,"tolerance":1e-8},{"id":7,"position":[0.0,3.0,0.0],"outgoing_halfedge":12,"tolerance":1e-8},{"id":8,"position":[0.0,3.0,-0.5],"outgoing_halfedge":13,"tolerance":1e-8},{"id":9,"position":[1.5,3.0,0.0],"outgoing_halfedge":17,"tolerance":1e-8},{"id":10,"position":[1.5,3.0,-0.5],"outgoing_halfedge":22,"tolerance":1e-8},{"id":11,"position":[0.0,0.5,-3.0],"outgoing_halfedge":25,"tolerance":1e-8},{"id":12,"position":[0.0,0.0,-3.0],"outgoing_halfedge":26,"tolerance":1e-8},{"id":13,"position":[1.5,0.5,-3.0],"outgoing_halfedge":30,"tolerance":1e-8},{"id":14,"position":[1.5,0.0,-3.0],"outgoing_halfedge":31,"tolerance":1e-8},{"id":15,"position":[0.0,3.0,-3.0],"outgoing_halfedge":37,"tolerance":1e-8},{"id":16,"position":[1.5,3.0,-3.0],"outgoing_halfedge":42,"tolerance":1e-8},{"id":17,"position":[3.0,0.0,0.0],"outgoing_halfedge":48,"tolerance":1e-8},{"id":18,"position":[3.0,0.0,-0.5],"outgoing_halfedge":49,"tolerance":1e-8},{"id":19,"position":[3.0,0.5,-0.5],"outgoing_halfedge":50,"tolerance":1e-8},{"id":20,"position":[3.0,0.5,0.0],"outgoing_halfedge":51,"tolerance":1e-8},{"id":21,"position":[3.0,3.0,-0.5],"outgoing_halfedge":62,"tolerance":1e-8},{"id":22,"position":[3.0,3.0,0.0],"outgoing_halfedge":63,"tolerance":1e-8},{"id":23,"position":[3.0,0.0,-3.0],"outgoing_halfedge":73,"tolerance":1e-8},{"id":24,"position":[3.0,0.5,-3.0],"outgoing_halfedge":74,"tolerance":1e-8},{"id":25,"position":[1.5,3.5,-0.5],"outgoing_halfedge":84,"tolerance":1e-8},{"id":26,"position":[1.5,3.5,-3.0],"outgoing_halfedge":85,"tolerance":1e-8},{"id":27,"position":[3.0,3.5,-0.5],"outgoing_halfedge":89,"tolerance":1e-8},{"id":28,"position":[3.0,3.5,-3.0],"outgoing_halfedge":94,"tolerance":1e-8},{"id":29,"position":[1.5,3.0,-3.5],"outgoing_halfedge":97,"tolerance":1e-8},{"id":30,"position":[1.5,0.5,-3.5],"outgoing_halfedge":98,"tolerance":1e-8},{"id":31,"position":[3.0,3.0,-3.5],"outgoing_halfedge":102,"tolerance":1e-8},{"id":32,"position":[3.0,0.5,-3.5],"outgoing_halfedge":103,"tolerance":1e-8},{"id":33,"position":[1.5,3.5,-3.5],"outgoing_halfedge":109,"tolerance":1e-8},{"id":34,"position":[3.0,3.5,-3.5],"outgoing_halfedge":114,"tolerance":1e-8},{"id":35,"position":[4.5,0.5,-0.5],"outgoing_halfedge":120,"tolerance":1e-8},{"id":36,"position":[4.5,0.5,-3.0],"outgoing_halfedge":121,"tolerance":1e-8},{"id":37,"position":[4.5,3.0,-3.0],"outgoing_halfedge":122,"tolerance":1e-8},{"id":38,"position":[4.5,3.0,-0.5],"outgoing_halfedge":123,"tolerance":1e-8},{"id":39,"position":[4.5,3.5,-3.0],"outgoing_halfedge":134,"tolerance":1e-8},{"id":40,"position":[4.5,3.5,-0.5],"outgoing_halfedge":135,"tolerance":1e-8},{"id":41,"position":[4.5,0.5,-3.5],"outgoing_halfedge":145,"tolerance":1e-8},{"id":42,"position":[4.5,3.0,-3.5],"outgoing_halfedge":146,"tolerance":1e-8},{"id":43,"position":[4.5,3.5,-3.5],"outgoing_halfedge":158,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":0.5}},"halfedge":0,"twin_halfedge":14,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":0.5}},"halfedge":1,"twin_halfedge":27,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":0.5}},"halfedge":2,"twin_halfedge":11,"tolerance":1e-8,"chart_seam":false},{"id":3,"geometry":{"kind":"Curve","curve":3,"range":{"lo":0.0,"hi":0.5}},"halfedge":3,"twin_halfedge":6,"tolerance":1e-8,"chart_seam":false},{"id":4,"geometry":{"kind":"Curve","curve":4,"range":{"lo":0.0,"hi":0.5}},"halfedge":4,"twin_halfedge":54,"tolerance":1e-8,"chart_seam":false},{"id":5,"geometry":{"kind":"Curve","curve":5,"range":{"lo":0.0,"hi":1.5}},"halfedge":5,"twin_halfedge":19,"tolerance":1e-8,"chart_seam":false},{"id":6,"geometry":{"kind":"Curve","curve":6,"range":{"lo":0.0,"hi":1.5}},"halfedge":7,"twin_halfedge":10,"tolerance":1e-8,"chart_seam":false},{"id":7,"geometry":{"kind":"Curve","curve":7,"range":{"lo":0.0,"hi":1.5}},"halfedge":8,"twin_halfedge":34,"tolerance":1e-8,"chart_seam":false},{"id":8,"geometry":{"kind":"Curve","curve":8,"range":{"lo":0.0,"hi":0.5}},"halfedge":9,"twin_halfedge":59,"tolerance":1e-8,"chart_seam":false},{"id":9,"geometry":{"kind":"Curve","curve":9,"range":{"lo":0.0,"hi":0.5}},"halfedge":12,"twin_halfedge":23,"tolerance":1e-8,"chart_seam":false},{"id":10,"geometry":{"kind":"Curve","curve":10,"range":{"lo":0.0,"hi":2.5}},"halfedge":13,"twin_halfedge":39,"tolerance":1e-8,"chart_seam":false},{"id":11,"geometry":{"kind":"Curve","curve":11,"range":{"lo":0.0,"hi":2.5}},"halfedge":15,"twin_halfedge":18,"tolerance":1e-8,"chart_seam":false},{"id":12,"geometry":{"kind":"Curve","curve":12,"range":{"lo":0.0,"hi":2.5}},"halfedge":16,"twin_halfedge":66,"tolerance":1e-8,"chart_seam":false},{"id":13,"geometry":{"kind":"Curve","curve":13,"range":{"lo":0.0,"hi":1.5}},"halfedge":17,"twin_halfedge":20,"tolerance":1e-8,"chart_seam":false},{"id":14,"geometry":{"kind":"Curve","curve":14,"range":{"lo":0.0,"hi":0.5}},"halfedge":21,"twin_halfedge":71,"tolerance":1e-8,"chart_seam":false},{"id":15,"geometry":{"kind":"Curve","curve":15,"range":{"lo":0.0,"hi":1.5}},"halfedge":22,"twin_halfedge":44,"tolerance":1e-8,"chart_seam":false},{"id":16,"geometry":{"kind":"Curve","curve":16,"range":{"lo":0.0,"hi":2.5}},"halfedge":24,"twin_halfedge":38,"tolerance":1e-8,"chart_seam":false},{"id":17,"geometry":{"kind":"Curve","curve":17,"range":{"lo":0.0,"hi":0.5}},"halfedge":25,"twin_halfedge":28,"tolerance":1e-8,"chart_seam":false},{"id":18,"geometry":{"kind":"Curve","curve":18,"range":{"lo":0.0,"hi":2.5}},"halfedge":26,"twin_halfedge":35,"tolerance":1e-8,"chart_seam":false},{"id":19,"geometry":{"kind":"Curve","curve":19,"range":{"lo":0.0,"hi":1.5}},"halfedge":29,"twin_halfedge":43,"tolerance":1e-8,"chart_seam":false},{"id":20,"geometry":{"kind":"Curve","curve":20,"range":{"lo":0.0,"hi":0.5}},"halfedge":30,"twin_halfedge":76,"tolerance":1e-8,"chart_seam":false},{"id":21,"geometry":{"kind":"Curve","curve":21,"range":{"lo":0.0,"hi":1.5}},"halfedge":31,"twin_halfedge":32,"tolerance":1e-8,"chart_seam":false},{"id":22,"geometry":{"kind":"Curve","curve":22,"range":{"lo":0.0,"hi":2.5}},"halfedge":33,"twin_halfedge":83,"tolerance":1e-8,"chart_seam":false},{"id":23,"geometry":{"kind":"Curve","curve":23,"range":{"lo":0.0,"hi":2.5}},"halfedge":36,"twin_halfedge":47,"tolerance":1e-8,"chart_seam":false},{"id":24,"geometry":{"kind":"Curve","curve":24,"range":{"lo":0.0,"hi":2.5}},"halfedge":37,"twin_halfedge":40,"tolerance":1e-8,"chart_seam":false},{"id":25,"geometry":{"kind":"Curve","curve":25,"range":{"lo":0.0,"hi":1.5}},"halfedge":41,"twin_halfedge":46,"tolerance":1e-8,"chart_seam":false},{"id":26,"geometry":{"kind":"Curve","curve":26,"range":{"lo":0.0,"hi":2.5}},"halfedge":42,"twin_halfedge":99,"tolerance":1e-8,"chart_seam":false},{"id":27,"geometry":{"kind":"Curve","curve":27,"range":{"lo":0.0,"hi":2.5}},"halfedge":45,"twin_halfedge":86,"tolerance":1e-8,"chart_seam":false},{"id":28,"geometry":{"kind":"Curve","curve":28,"range":{"lo":0.0,"hi":0.5}},"halfedge":48,"twin_halfedge":57,"tolerance":1e-8,"chart_seam":false},{"id":29,"geometry":{"kind":"Curve","curve":29,"range":{"lo":0.0,"hi":0.5}},"halfedge":49,"twin_halfedge":75,"tolerance":1e-8,"chart_seam":false},{"id":30,"geometry":{"kind":"Curve","curve":30,"range":{"lo":0.0,"hi":0.5}},"halfedge":50,"twin_halfedge":60,"tolerance":1e-8,"chart_seam":false},{"id":31,"geometry":{"kind":"Curve","curve":31,"range":{"lo":0.0,"hi":0.5}},"halfedge":51,"twin_halfedge":52,"tolerance":1e-8,"chart_seam":false},{"id":32,"geometry":{"kind":"Curve","curve":32,"range":{"lo":0.0,"hi":1.5}},"halfedge":53,"twin_halfedge":67,"tolerance":1e-8,"chart_seam":false},{"id":33,"geometry":{"kind":"Curve","curve":33,"range":{"lo":0.0,"hi":1.5}},"halfedge":55,"twin_halfedge":58,"tolerance":1e-8,"chart_seam":false},{"id":34,"geometry":{"kind":"Curve","curve":34,"range":{"lo":0.0,"hi":1.5}},"halfedge":56,"twin_halfedge":82,"tolerance":1e-8,"chart_seam":false},{"id":35,"geometry":{"kind":"Curve","curve":35,"range":{"lo":0.0,"hi":2.5}},"halfedge":61,"twin_halfedge":126,"tolerance":1e-8,"chart_seam":false},{"id":36,"geometry":{"kind":"Curve","curve":36,"range":{"lo":0.0,"hi":0.5}},"halfedge":62,"twin_halfedge":69,"tolerance":1e-8,"chart_seam":false},{"id":37,"geometry":{"kind":"Curve","curve":37,"range":{"lo":0.0,"hi":2.5}},"halfedge":63,"twin_halfedge":64,"tolerance":1e-8,"chart_seam":false},{"id":38,"geometry":{"kind":"Curve","curve":38,"range":{"lo":0.0,"hi":1.5}},"halfedge":65,"twin_halfedge":68,"tolerance":1e-8,"chart_seam":false},{"id":39,"geometry":{"kind":"Curve","curve":39,"range":{"lo":0.0,"hi":1.5}},"halfedge":70,"twin_halfedge":91,"tolerance":1e-8,"chart_seam":false},{"id":40,"geometry":{"kind":"Curve","curve":40,"range":{"lo":0.0,"hi":2.5}},"halfedge":72,"twin_halfedge":81,"tolerance":1e-8,"chart_seam":false},{"id":41,"geometry":{"kind":"Curve","curve":41,"range":{"lo":0.0,"hi":0.5}},"halfedge":73,"twin_halfedge":78,"tolerance":1e-8,"chart_seam":false},{"id":42,"geometry":{"kind":"Curve","curve":42,"range":{"lo":0.0,"hi":2.5}},"halfedge":74,"twin_halfedge":131,"tolerance":1e-8,"chart_seam":false},{"id":43,"geometry":{"kind":"Curve","curve":43,"range":{"lo":0.0,"hi":1.5}},"halfedge":77,"twin_halfedge":106,"tolerance":1e-8,"chart_seam":false},{"id":44,"geometry":{"kind":"Curve","curve":44,"range":{"lo":0.0,"hi":1.5}},"halfedge":79,"twin_halfedge":80,"tolerance":1e-8,"chart_seam":false},{"id":45,"geometry":{"kind":"Curve","curve":45,"range":{"lo":0.0,"hi":2.5}},"halfedge":84,"twin_halfedge":95,"tolerance":1e-8,"chart_seam":false},{"id":46,"geometry":{"kind":"Curve","curve":46,"range":{"lo":0.0,"hi":0.5}},"halfedge":85,"twin_halfedge":111,"tolerance":1e-8,"chart_seam":false},{"id":47,"geometry":{"kind":"Curve","curve":47,"range":{"lo":0.0,"hi":0.5}},"halfedge":87,"twin_halfedge":90,"tolerance":1e-8,"chart_seam":false},{"id":48,"geometry":{"kind":"Curve","curve":48,"range":{"lo":0.0,"hi":0.5}},"halfedge":88,"twin_halfedge":138,"tolerance":1e-8,"chart_seam":false},{"id":49,"geometry":{"kind":"Curve","curve":49,"range":{"lo":0.0,"hi":1.5}},"halfedge":89,"twin_halfedge":92,"tolerance":1e-8,"chart_seam":false},{"id":50,"geometry":{"kind":"Curve","curve":50,"range":{"lo":0.0,"hi":2.5}},"halfedge":93,"twin_halfedge":143,"tolerance":1e-8,"chart_seam":false},{"id":51,"geometry":{"kind":"Curve","curve":51,"range":{"lo":0.0,"hi":1.5}},"halfedge":94,"twin_halfedge":116,"tolerance":1e-8,"chart_seam":false},{"id":52,"geometry":{"kind":"Curve","curve":52,"range":{"lo":0.0,"hi":0.5}},"halfedge":96,"twin_halfedge":110,"tolerance":1e-8,"chart_seam":false},{"id":53,"geometry":{"kind":"Curve","curve":53,"range":{"lo":0.0,"hi":2.5}},"halfedge":97,"twin_halfedge":100,"tolerance":1e-8,"chart_seam":false},{"id":54,"geometry":{"kind":"Curve","curve":54,"range":{"lo":0.0,"hi":0.5}},"halfedge":98,"twin_halfedge":107,"tolerance":1e-8,"chart_seam":false},{"id":55,"geometry":{"kind":"Curve","curve":55,"range":{"lo":0.0,"hi":1.5}},"halfedge":101,"twin_halfedge":115,"tolerance":1e-8,"chart_seam":false},{"id":56,"geometry":{"kind":"Curve","curve":56,"range":{"lo":0.0,"hi":2.5}},"halfedge":102,"twin_halfedge":148,"tolerance":1e-8,"chart_seam":false},{"id":57,"geometry":{"kind":"Curve","curve":57,"range":{"lo":0.0,"hi":1.5}},"halfedge":103,"twin_halfedge":104,"tolerance":1e-8,"chart_seam":false},{"id":58,"geometry":{"kind":"Curve","curve":58,"range":{"lo":0.0,"hi":0.5}},"halfedge":105,"twin_halfedge":155,"tolerance":1e-8,"chart_seam":false},{"id":59,"geometry":{"kind":"Curve","curve":59,"range":{"lo":0.0,"hi":0.5}},"halfedge":108,"twin_halfedge":119,"tolerance":1e-8,"chart_seam":false},{"id":60,"geometry":{"kind":"Curve","curve":60,"range":{"lo":0.0,"hi":0.5}},"halfedge":109,"twin_halfedge":112,"tolerance":1e-8,"chart_seam":false},{"id":61,"geometry":{"kind":"Curve","curve":61,"range":{"lo":0.0,"hi":1.5}},"halfedge":113,"twin_halfedge":118,"tolerance":1e-8,"chart_seam":false},{"id":62,"geometry":{"kind":"Curve","curve":62,"range":{"lo":0.0,"hi":0.5}},"halfedge":114,"twin_halfedge":160,"tolerance":1e-8,"chart_seam":false},{"id":63,"geometry":{"kind":"Curve","curve":63,"range":{"lo":0.0,"hi":0.5}},"halfedge":117,"twin_halfedge":167,"tolerance":1e-8,"chart_seam":false},{"id":64,"geometry":{"kind":"Curve","curve":64,"range":{"lo":0.0,"hi":2.5}},"halfedge":120,"twin_halfedge":129,"tolerance":1e-8,"chart_seam":false},{"id":65,"geometry":{"kind":"Curve","curve":65,"range":{"lo":0.0,"hi":2.5}},"halfedge":121,"twin_halfedge":147,"tolerance":1e-8,"chart_seam":false},{"id":66,"geometry":{"kind":"Curve","curve":66,"range":{"lo":0.0,"hi":2.5}},"halfedge":122,"twin_halfedge":132,"tolerance":1e-8,"chart_seam":false},{"id":67,"geometry":{"kind":"Curve","curve":67,"range":{"lo":0.0,"hi":2.5}},"halfedge":123,"twin_halfedge":124,"tolerance":1e-8,"chart_seam":false},{"id":68,"geometry":{"kind":"Curve","curve":68,"range":{"lo":0.0,"hi":1.5}},"halfedge":125,"twin_halfedge":139,"tolerance":1e-8,"chart_seam":false},{"id":69,"geometry":{"kind":"Curve","curve":69,"range":{"lo":0.0,"hi":1.5}},"halfedge":127,"twin_halfedge":130,"tolerance":1e-8,"chart_seam":false},{"id":70,"geometry":{"kind":"Curve","curve":70,"range":{"lo":0.0,"hi":1.5}},"halfedge":128,"twin_halfedge":154,"tolerance":1e-8,"chart_seam":false},{"id":71,"geometry":{"kind":"Curve","curve":71,"range":{"lo":0.0,"hi":0.5}},"halfedge":133,"twin_halfedge":159,"tolerance":1e-8,"chart_seam":false},{"id":72,"geometry":{"kind":"Curve","curve":72,"range":{"lo":0.0,"hi":2.5}},"halfedge":134,"twin_halfedge":141,"tolerance":1e-8,"chart_seam":false},{"id":73,"geometry":{"kind":"Curve","curve":73,"range":{"lo":0.0,"hi":0.5}},"halfedge":135,"twin_halfedge":136,"tolerance":1e-8,"chart_seam":false},{"id":74,"geometry":{"kind":"Curve","curve":74,"range":{"lo":0.0,"hi":1.5}},"halfedge":137,"twin_halfedge":140,"tolerance":1e-8,"chart_seam":false},{"id":75,"geometry":{"kind":"Curve","curve":75,"range":{"lo":0.0,"hi":1.5}},"halfedge":142,"twin_halfedge":164,"tolerance":1e-8,"chart_seam":false},{"id":76,"geometry":{"kind":"Curve","curve":76,"range":{"lo":0.0,"hi":0.5}},"halfedge":144,"twin_halfedge":153,"tolerance":1e-8,"chart_seam":false},{"id":77,"geometry":{"kind":"Curve","curve":77,"range":{"lo":0.0,"hi":2.5}},"halfedge":145,"twin_halfedge":150,"tolerance":1e-8,"chart_seam":false},{"id":78,"geometry":{"kind":"Curve","curve":78,"range":{"lo":0.0,"hi":0.5}},"halfedge":146,"twin_halfedge":156,"tolerance":1e-8,"chart_seam":false},{"id":79,"geometry":{"kind":"Curve","curve":79,"range":{"lo":0.0,"hi":1.5}},"halfedge":149,"twin_halfedge":163,"tolerance":1e-8,"chart_seam":false},{"id":80,"geometry":{"kind":"Curve","curve":80,"range":{"lo":0.0,"hi":1.5}},"halfedge":151,"twin_halfedge":152,"tolerance":1e-8,"chart_seam":false},{"id":81,"geometry":{"kind":"Curve","curve":81,"range":{"lo":0.0,"hi":0.5}},"halfedge":157,"twin_halfedge":162,"tolerance":1e-8,"chart_seam":false},{"id":82,"geometry":{"kind":"Curve","curve":82,"range":{"lo":0.0,"hi":0.5}},"halfedge":158,"twin_halfedge":165,"tolerance":1e-8,"chart_seam":false},{"id":83,"geometry":{"kind":"Curve","curve":83,"range":{"lo":0.0,"hi":1.5}},"halfedge":161,"twin_halfedge":166,"tolerance":1e-8,"chart_seam":false}],"halfedges":[{"id":0,"from":0,"to":1,"twin":14,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":1,"to":2,"twin":27,"next":2,"prev":0,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":2,"to":3,"twin":11,"next":3,"prev":1,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":3,"to":0,"twin":6,"next":0,"prev":2,"edge":3,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":4,"to":5,"twin":54,"next":5,"prev":7,"edge":4,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":5,"to":0,"twin":19,"next":6,"prev":4,"edge":5,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":5,"periodic_lift":[0,0]}},{"id":6,"from":0,"to":3,"twin":3,"next":7,"prev":5,"edge":3,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":6,"periodic_lift":[0,0]}},{"id":7,"from":3,"to":4,"twin":10,"next":4,"prev":6,"edge":6,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":7,"periodic_lift":[0,0]}},{"id":8,"from":2,"to":6,"twin":34,"next":9,"prev":11,"edge":7,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":8,"periodic_lift":[0,0]}},{"id":9,"from":6,"to":4,"twin":59,"next":10,"prev":8,"edge":8,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":9,"periodic_lift":[0,0]}},{"id":10,"from":4,"to":3,"twin":7,"next":11,"prev":9,"edge":6,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":10,"periodic_lift":[0,0]}},{"id":11,"from":3,"to":2,"twin":2,"next":8,"prev":10,"edge":2,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":11,"periodic_lift":[0,0]}},{"id":12,"from":7,"to":8,"twin":23,"next":13,"prev":15,"edge":9,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":12,"periodic_lift":[0,0]}},{"id":13,"from":8,"to":1,"twin":39,"next":14,"prev":12,"edge":10,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":13,"periodic_lift":[0,0]}},{"id":14,"from":1,"to":0,"twin":0,"next":15,"prev":13,"edge":0,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":14,"periodic_lift":[0,0]}},{"id":15,"from":0,"to":7,"twin":18,"next":12,"prev":14,"edge":11,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":15,"periodic_lift":[0,0]}},{"id":16,"from":5,"to":9,"twin":66,"next":17,"prev":19,"edge":12,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":16,"periodic_lift":[0,0]}},{"id":17,"from":9,"to":7,"twin":20,"next":18,"prev":16,"edge":13,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":17,"periodic_lift":[0,0]}},{"id":18,"from":7,"to":0,"twin":15,"next":19,"prev":17,"edge":11,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":18,"periodic_lift":[0,0]}},{"id":19,"from":0,"to":5,"twin":5,"next":16,"prev":18,"edge":5,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":19,"periodic_lift":[0,0]}},{"id":20,"from":7,"to":9,"twin":17,"next":21,"prev":23,"edge":13,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":20,"periodic_lift":[0,0]}},{"id":21,"from":9,"to":10,"twin":71,"next":22,"prev":20,"edge":14,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":21,"periodic_lift":[0,0]}},{"id":22,"from":10,"to":8,"twin":44,"next":23,"prev":21,"edge":15,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":22,"periodic_lift":[0,0]}},{"id":23,"from":8,"to":7,"twin":12,"next":20,"prev":22,"edge":9,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":23,"periodic_lift":[0,0]}},{"id":24,"from":1,"to":11,"twin":38,"next":25,"prev":27,"edge":16,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":24,"periodic_lift":[0,0]}},{"id":25,"from":11,"to":12,"twin":28,"next":26,"prev":24,"edge":17,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":25,"periodic_lift":[0,0]}},{"id":26,"from":12,"to":2,"twin":35,"next":27,"prev":25,"edge":18,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":26,"periodic_lift":[0,0]}},{"id":27,"from":2,"to":1,"twin":1,"next":24,"prev":26,"edge":1,"face":6,"loop_ref":6,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":27,"periodic_lift":[0,0]}},{"id":28,"from":12,"to":11,"twin":25,"next":29,"prev":31,"edge":17,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":28,"periodic_lift":[0,0]}},{"id":29,"from":11,"to":13,"twin":43,"next":30,"prev":28,"edge":19,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":29,"periodic_lift":[0,0]}},{"id":30,"from":13,"to":14,"twin":76,"next":31,"prev":29,"edge":20,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":30,"periodic_lift":[0,0]}},{"id":31,"from":14,"to":12,"twin":32,"next":28,"prev":30,"edge":21,"face":7,"loop_ref":7,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":31,"periodic_lift":[0,0]}},{"id":32,"from":12,"to":14,"twin":31,"next":33,"prev":35,"edge":21,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":32,"periodic_lift":[0,0]}},{"id":33,"from":14,"to":6,"twin":83,"next":34,"prev":32,"edge":22,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":33,"periodic_lift":[0,0]}},{"id":34,"from":6,"to":2,"twin":8,"next":35,"prev":33,"edge":7,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":34,"periodic_lift":[0,0]}},{"id":35,"from":2,"to":12,"twin":26,"next":32,"prev":34,"edge":18,"face":8,"loop_ref":8,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":35,"periodic_lift":[0,0]}},{"id":36,"from":8,"to":15,"twin":47,"next":37,"prev":39,"edge":23,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":36,"periodic_lift":[0,0]}},{"id":37,"from":15,"to":11,"twin":40,"next":38,"prev":36,"edge":24,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":37,"periodic_lift":[0,0]}},{"id":38,"from":11,"to":1,"twin":24,"next":39,"prev":37,"edge":16,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":38,"periodic_lift":[0,0]}},{"id":39,"from":1,"to":8,"twin":13,"next":36,"prev":38,"edge":10,"face":9,"loop_ref":9,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":39,"periodic_lift":[0,0]}},{"id":40,"from":11,"to":15,"twin":37,"next":41,"prev":43,"edge":24,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":40,"periodic_lift":[0,0]}},{"id":41,"from":15,"to":16,"twin":46,"next":42,"prev":40,"edge":25,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":41,"periodic_lift":[0,0]}},{"id":42,"from":16,"to":13,"twin":99,"next":43,"prev":41,"edge":26,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":42,"periodic_lift":[0,0]}},{"id":43,"from":13,"to":11,"twin":29,"next":40,"prev":42,"edge":19,"face":10,"loop_ref":10,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":43,"periodic_lift":[0,0]}},{"id":44,"from":8,"to":10,"twin":22,"next":45,"prev":47,"edge":15,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":44,"periodic_lift":[0,0]}},{"id":45,"from":10,"to":16,"twin":86,"next":46,"prev":44,"edge":27,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":45,"periodic_lift":[0,0]}},{"id":46,"from":16,"to":15,"twin":41,"next":47,"prev":45,"edge":25,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":46,"periodic_lift":[0,0]}},{"id":47,"from":15,"to":8,"twin":36,"next":44,"prev":46,"edge":23,"face":11,"loop_ref":11,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":47,"periodic_lift":[0,0]}},{"id":48,"from":17,"to":18,"twin":57,"next":49,"prev":51,"edge":28,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":48,"periodic_lift":[0,0]}},{"id":49,"from":18,"to":19,"twin":75,"next":50,"prev":48,"edge":29,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":49,"periodic_lift":[0,0]}},{"id":50,"from":19,"to":20,"twin":60,"next":51,"prev":49,"edge":30,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":50,"periodic_lift":[0,0]}},{"id":51,"from":20,"to":17,"twin":52,"next":48,"prev":50,"edge":31,"face":12,"loop_ref":12,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":51,"periodic_lift":[0,0]}},{"id":52,"from":17,"to":20,"twin":51,"next":53,"prev":55,"edge":31,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":52,"periodic_lift":[0,0]}},{"id":53,"from":20,"to":5,"twin":67,"next":54,"prev":52,"edge":32,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":53,"periodic_lift":[0,0]}},{"id":54,"from":5,"to":4,"twin":4,"next":55,"prev":53,"edge":4,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":54,"periodic_lift":[0,0]}},{"id":55,"from":4,"to":17,"twin":58,"next":52,"prev":54,"edge":33,"face":13,"loop_ref":13,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":55,"periodic_lift":[0,0]}},{"id":56,"from":6,"to":18,"twin":82,"next":57,"prev":59,"edge":34,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":56,"periodic_lift":[0,0]}},{"id":57,"from":18,"to":17,"twin":48,"next":58,"prev":56,"edge":28,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":57,"periodic_lift":[0,0]}},{"id":58,"from":17,"to":4,"twin":55,"next":59,"prev":57,"edge":33,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":58,"periodic_lift":[0,0]}},{"id":59,"from":4,"to":6,"twin":9,"next":56,"prev":58,"edge":8,"face":14,"loop_ref":14,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":59,"periodic_lift":[0,0]}},{"id":60,"from":20,"to":19,"twin":50,"next":61,"prev":63,"edge":30,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":60,"periodic_lift":[0,0]}},{"id":61,"from":19,"to":21,"twin":126,"next":62,"prev":60,"edge":35,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":61,"periodic_lift":[0,0]}},{"id":62,"from":21,"to":22,"twin":69,"next":63,"prev":61,"edge":36,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":62,"periodic_lift":[0,0]}},{"id":63,"from":22,"to":20,"twin":64,"next":60,"prev":62,"edge":37,"face":15,"loop_ref":15,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":63,"periodic_lift":[0,0]}},{"id":64,"from":20,"to":22,"twin":63,"next":65,"prev":67,"edge":37,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":64,"periodic_lift":[0,0]}},{"id":65,"from":22,"to":9,"twin":68,"next":66,"prev":64,"edge":38,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":65,"periodic_lift":[0,0]}},{"id":66,"from":9,"to":5,"twin":16,"next":67,"prev":65,"edge":12,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":66,"periodic_lift":[0,0]}},{"id":67,"from":5,"to":20,"twin":53,"next":64,"prev":66,"edge":32,"face":16,"loop_ref":16,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":67,"periodic_lift":[0,0]}},{"id":68,"from":9,"to":22,"twin":65,"next":69,"prev":71,"edge":38,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":68,"periodic_lift":[0,0]}},{"id":69,"from":22,"to":21,"twin":62,"next":70,"prev":68,"edge":36,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":69,"periodic_lift":[0,0]}},{"id":70,"from":21,"to":10,"twin":91,"next":71,"prev":69,"edge":39,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":70,"periodic_lift":[0,0]}},{"id":71,"from":10,"to":9,"twin":21,"next":68,"prev":70,"edge":14,"face":17,"loop_ref":17,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":71,"periodic_lift":[0,0]}},{"id":72,"from":18,"to":23,"twin":81,"next":73,"prev":75,"edge":40,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":72,"periodic_lift":[0,0]}},{"id":73,"from":23,"to":24,"twin":78,"next":74,"prev":72,"edge":41,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":73,"periodic_lift":[0,0]}},{"id":74,"from":24,"to":19,"twin":131,"next":75,"prev":73,"edge":42,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":74,"periodic_lift":[0,0]}},{"id":75,"from":19,"to":18,"twin":49,"next":72,"prev":74,"edge":29,"face":18,"loop_ref":18,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":75,"periodic_lift":[0,0]}},{"id":76,"from":14,"to":13,"twin":30,"next":77,"prev":79,"edge":20,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":76,"periodic_lift":[0,0]}},{"id":77,"from":13,"to":24,"twin":106,"next":78,"prev":76,"edge":43,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":77,"periodic_lift":[0,0]}},{"id":78,"from":24,"to":23,"twin":73,"next":79,"prev":77,"edge":41,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":78,"periodic_lift":[0,0]}},{"id":79,"from":23,"to":14,"twin":80,"next":76,"prev":78,"edge":44,"face":19,"loop_ref":19,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":79,"periodic_lift":[0,0]}},{"id":80,"from":14,"to":23,"twin":79,"next":81,"prev":83,"edge":44,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":80,"periodic_lift":[0,0]}},{"id":81,"from":23,"to":18,"twin":72,"next":82,"prev":80,"edge":40,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":81,"periodic_lift":[0,0]}},{"id":82,"from":18,"to":6,"twin":56,"next":83,"prev":81,"edge":34,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":82,"periodic_lift":[0,0]}},{"id":83,"from":6,"to":14,"twin":33,"next":80,"prev":82,"edge":22,"face":20,"loop_ref":20,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":83,"periodic_lift":[0,0]}},{"id":84,"from":25,"to":26,"twin":95,"next":85,"prev":87,"edge":45,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":84,"periodic_lift":[0,0]}},{"id":85,"from":26,"to":16,"twin":111,"next":86,"prev":84,"edge":46,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":85,"periodic_lift":[0,0]}},{"id":86,"from":16,"to":10,"twin":45,"next":87,"prev":85,"edge":27,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":86,"periodic_lift":[0,0]}},{"id":87,"from":10,"to":25,"twin":90,"next":84,"prev":86,"edge":47,"face":21,"loop_ref":21,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":87,"periodic_lift":[0,0]}},{"id":88,"from":21,"to":27,"twin":138,"next":89,"prev":91,"edge":48,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":88,"periodic_lift":[0,0]}},{"id":89,"from":27,"to":25,"twin":92,"next":90,"prev":88,"edge":49,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":89,"periodic_lift":[0,0]}},{"id":90,"from":25,"to":10,"twin":87,"next":91,"prev":89,"edge":47,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":90,"periodic_lift":[0,0]}},{"id":91,"from":10,"to":21,"twin":70,"next":88,"prev":90,"edge":39,"face":22,"loop_ref":22,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":91,"periodic_lift":[0,0]}},{"id":92,"from":25,"to":27,"twin":89,"next":93,"prev":95,"edge":49,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":92,"periodic_lift":[0,0]}},{"id":93,"from":27,"to":28,"twin":143,"next":94,"prev":92,"edge":50,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":93,"periodic_lift":[0,0]}},{"id":94,"from":28,"to":26,"twin":116,"next":95,"prev":93,"edge":51,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":94,"periodic_lift":[0,0]}},{"id":95,"from":26,"to":25,"twin":84,"next":92,"prev":94,"edge":45,"face":23,"loop_ref":23,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":95,"periodic_lift":[0,0]}},{"id":96,"from":16,"to":29,"twin":110,"next":97,"prev":99,"edge":52,"face":24,"loop_ref":24,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":96,"periodic_lift":[0,0]}},{"id":97,"from":29,"to":30,"twin":100,"next":98,"prev":96,"edge":53,"face":24,"loop_ref":24,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":97,"periodic_lift":[0,0]}},{"id":98,"from":30,"to":13,"twin":107,"next":99,"prev":97,"edge":54,"face":24,"loop_ref":24,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":98,"periodic_lift":[0,0]}},{"id":99,"from":13,"to":16,"twin":42,"next":96,"prev":98,"edge":26,"face":24,"loop_ref":24,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":99,"periodic_lift":[0,0]}},{"id":100,"from":30,"to":29,"twin":97,"next":101,"prev":103,"edge":53,"face":25,"loop_ref":25,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":100,"periodic_lift":[0,0]}},{"id":101,"from":29,"to":31,"twin":115,"next":102,"prev":100,"edge":55,"face":25,"loop_ref":25,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":101,"periodic_lift":[0,0]}},{"id":102,"from":31,"to":32,"twin":148,"next":103,"prev":101,"edge":56,"face":25,"loop_ref":25,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":102,"periodic_lift":[0,0]}},{"id":103,"from":32,"to":30,"twin":104,"next":100,"prev":102,"edge":57,"face":25,"loop_ref":25,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":103,"periodic_lift":[0,0]}},{"id":104,"from":30,"to":32,"twin":103,"next":105,"prev":107,"edge":57,"face":26,"loop_ref":26,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":104,"periodic_lift":[0,0]}},{"id":105,"from":32,"to":24,"twin":155,"next":106,"prev":104,"edge":58,"face":26,"loop_ref":26,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":105,"periodic_lift":[0,0]}},{"id":106,"from":24,"to":13,"twin":77,"next":107,"prev":105,"edge":43,"face":26,"loop_ref":26,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":106,"periodic_lift":[0,0]}},{"id":107,"from":13,"to":30,"twin":98,"next":104,"prev":106,"edge":54,"face":26,"loop_ref":26,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":107,"periodic_lift":[0,0]}},{"id":108,"from":26,"to":33,"twin":119,"next":109,"prev":111,"edge":59,"face":27,"loop_ref":27,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":108,"periodic_lift":[0,0]}},{"id":109,"from":33,"to":29,"twin":112,"next":110,"prev":108,"edge":60,"face":27,"loop_ref":27,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":109,"periodic_lift":[0,0]}},{"id":110,"from":29,"to":16,"twin":96,"next":111,"prev":109,"edge":52,"face":27,"loop_ref":27,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":110,"periodic_lift":[0,0]}},{"id":111,"from":16,"to":26,"twin":85,"next":108,"prev":110,"edge":46,"face":27,"loop_ref":27,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":111,"periodic_lift":[0,0]}},{"id":112,"from":29,"to":33,"twin":109,"next":113,"prev":115,"edge":60,"face":28,"loop_ref":28,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":112,"periodic_lift":[0,0]}},{"id":113,"from":33,"to":34,"twin":118,"next":114,"prev":112,"edge":61,"face":28,"loop_ref":28,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":113,"periodic_lift":[0,0]}},{"id":114,"from":34,"to":31,"twin":160,"next":115,"prev":113,"edge":62,"face":28,"loop_ref":28,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":114,"periodic_lift":[0,0]}},{"id":115,"from":31,"to":29,"twin":101,"next":112,"prev":114,"edge":55,"face":28,"loop_ref":28,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":115,"periodic_lift":[0,0]}},{"id":116,"from":26,"to":28,"twin":94,"next":117,"prev":119,"edge":51,"face":29,"loop_ref":29,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":116,"periodic_lift":[0,0]}},{"id":117,"from":28,"to":34,"twin":167,"next":118,"prev":116,"edge":63,"face":29,"loop_ref":29,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":117,"periodic_lift":[0,0]}},{"id":118,"from":34,"to":33,"twin":113,"next":119,"prev":117,"edge":61,"face":29,"loop_ref":29,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":118,"periodic_lift":[0,0]}},{"id":119,"from":33,"to":26,"twin":108,"next":116,"prev":118,"edge":59,"face":29,"loop_ref":29,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":119,"periodic_lift":[0,0]}},{"id":120,"from":35,"to":36,"twin":129,"next":121,"prev":123,"edge":64,"face":30,"loop_ref":30,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":120,"periodic_lift":[0,0]}},{"id":121,"from":36,"to":37,"twin":147,"next":122,"prev":120,"edge":65,"face":30,"loop_ref":30,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":121,"periodic_lift":[0,0]}},{"id":122,"from":37,"to":38,"twin":132,"next":123,"prev":121,"edge":66,"face":30,"loop_ref":30,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":122,"periodic_lift":[0,0]}},{"id":123,"from":38,"to":35,"twin":124,"next":120,"prev":122,"edge":67,"face":30,"loop_ref":30,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":123,"periodic_lift":[0,0]}},{"id":124,"from":35,"to":38,"twin":123,"next":125,"prev":127,"edge":67,"face":31,"loop_ref":31,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":124,"periodic_lift":[0,0]}},{"id":125,"from":38,"to":21,"twin":139,"next":126,"prev":124,"edge":68,"face":31,"loop_ref":31,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":125,"periodic_lift":[0,0]}},{"id":126,"from":21,"to":19,"twin":61,"next":127,"prev":125,"edge":35,"face":31,"loop_ref":31,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":126,"periodic_lift":[0,0]}},{"id":127,"from":19,"to":35,"twin":130,"next":124,"prev":126,"edge":69,"face":31,"loop_ref":31,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":127,"periodic_lift":[0,0]}},{"id":128,"from":24,"to":36,"twin":154,"next":129,"prev":131,"edge":70,"face":32,"loop_ref":32,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":128,"periodic_lift":[0,0]}},{"id":129,"from":36,"to":35,"twin":120,"next":130,"prev":128,"edge":64,"face":32,"loop_ref":32,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":129,"periodic_lift":[0,0]}},{"id":130,"from":35,"to":19,"twin":127,"next":131,"prev":129,"edge":69,"face":32,"loop_ref":32,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":130,"periodic_lift":[0,0]}},{"id":131,"from":19,"to":24,"twin":74,"next":128,"prev":130,"edge":42,"face":32,"loop_ref":32,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":131,"periodic_lift":[0,0]}},{"id":132,"from":38,"to":37,"twin":122,"next":133,"prev":135,"edge":66,"face":33,"loop_ref":33,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":132,"periodic_lift":[0,0]}},{"id":133,"from":37,"to":39,"twin":159,"next":134,"prev":132,"edge":71,"face":33,"loop_ref":33,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":133,"periodic_lift":[0,0]}},{"id":134,"from":39,"to":40,"twin":141,"next":135,"prev":133,"edge":72,"face":33,"loop_ref":33,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":134,"periodic_lift":[0,0]}},{"id":135,"from":40,"to":38,"twin":136,"next":132,"prev":134,"edge":73,"face":33,"loop_ref":33,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":135,"periodic_lift":[0,0]}},{"id":136,"from":38,"to":40,"twin":135,"next":137,"prev":139,"edge":73,"face":34,"loop_ref":34,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":136,"periodic_lift":[0,0]}},{"id":137,"from":40,"to":27,"twin":140,"next":138,"prev":136,"edge":74,"face":34,"loop_ref":34,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":137,"periodic_lift":[0,0]}},{"id":138,"from":27,"to":21,"twin":88,"next":139,"prev":137,"edge":48,"face":34,"loop_ref":34,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":138,"periodic_lift":[0,0]}},{"id":139,"from":21,"to":38,"twin":125,"next":136,"prev":138,"edge":68,"face":34,"loop_ref":34,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":139,"periodic_lift":[0,0]}},{"id":140,"from":27,"to":40,"twin":137,"next":141,"prev":143,"edge":74,"face":35,"loop_ref":35,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":140,"periodic_lift":[0,0]}},{"id":141,"from":40,"to":39,"twin":134,"next":142,"prev":140,"edge":72,"face":35,"loop_ref":35,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":141,"periodic_lift":[0,0]}},{"id":142,"from":39,"to":28,"twin":164,"next":143,"prev":141,"edge":75,"face":35,"loop_ref":35,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":142,"periodic_lift":[0,0]}},{"id":143,"from":28,"to":27,"twin":93,"next":140,"prev":142,"edge":50,"face":35,"loop_ref":35,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":143,"periodic_lift":[0,0]}},{"id":144,"from":36,"to":41,"twin":153,"next":145,"prev":147,"edge":76,"face":36,"loop_ref":36,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":144,"periodic_lift":[0,0]}},{"id":145,"from":41,"to":42,"twin":150,"next":146,"prev":144,"edge":77,"face":36,"loop_ref":36,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":145,"periodic_lift":[0,0]}},{"id":146,"from":42,"to":37,"twin":156,"next":147,"prev":145,"edge":78,"face":36,"loop_ref":36,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":146,"periodic_lift":[0,0]}},{"id":147,"from":37,"to":36,"twin":121,"next":144,"prev":146,"edge":65,"face":36,"loop_ref":36,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":147,"periodic_lift":[0,0]}},{"id":148,"from":32,"to":31,"twin":102,"next":149,"prev":151,"edge":56,"face":37,"loop_ref":37,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":148,"periodic_lift":[0,0]}},{"id":149,"from":31,"to":42,"twin":163,"next":150,"prev":148,"edge":79,"face":37,"loop_ref":37,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":149,"periodic_lift":[0,0]}},{"id":150,"from":42,"to":41,"twin":145,"next":151,"prev":149,"edge":77,"face":37,"loop_ref":37,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":150,"periodic_lift":[0,0]}},{"id":151,"from":41,"to":32,"twin":152,"next":148,"prev":150,"edge":80,"face":37,"loop_ref":37,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":151,"periodic_lift":[0,0]}},{"id":152,"from":32,"to":41,"twin":151,"next":153,"prev":155,"edge":80,"face":38,"loop_ref":38,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":152,"periodic_lift":[0,0]}},{"id":153,"from":41,"to":36,"twin":144,"next":154,"prev":152,"edge":76,"face":38,"loop_ref":38,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":153,"periodic_lift":[0,0]}},{"id":154,"from":36,"to":24,"twin":128,"next":155,"prev":153,"edge":70,"face":38,"loop_ref":38,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":154,"periodic_lift":[0,0]}},{"id":155,"from":24,"to":32,"twin":105,"next":152,"prev":154,"edge":58,"face":38,"loop_ref":38,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":155,"periodic_lift":[0,0]}},{"id":156,"from":37,"to":42,"twin":146,"next":157,"prev":159,"edge":78,"face":39,"loop_ref":39,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":156,"periodic_lift":[0,0]}},{"id":157,"from":42,"to":43,"twin":162,"next":158,"prev":156,"edge":81,"face":39,"loop_ref":39,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":157,"periodic_lift":[0,0]}},{"id":158,"from":43,"to":39,"twin":165,"next":159,"prev":157,"edge":82,"face":39,"loop_ref":39,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":158,"periodic_lift":[0,0]}},{"id":159,"from":39,"to":37,"twin":133,"next":156,"prev":158,"edge":71,"face":39,"loop_ref":39,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":159,"periodic_lift":[0,0]}},{"id":160,"from":31,"to":34,"twin":114,"next":161,"prev":163,"edge":62,"face":40,"loop_ref":40,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":160,"periodic_lift":[0,0]}},{"id":161,"from":34,"to":43,"twin":166,"next":162,"prev":160,"edge":83,"face":40,"loop_ref":40,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":161,"periodic_lift":[0,0]}},{"id":162,"from":43,"to":42,"twin":157,"next":163,"prev":161,"edge":81,"face":40,"loop_ref":40,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":162,"periodic_lift":[0,0]}},{"id":163,"from":42,"to":31,"twin":149,"next":160,"prev":162,"edge":79,"face":40,"loop_ref":40,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":163,"periodic_lift":[0,0]}},{"id":164,"from":28,"to":39,"twin":142,"next":165,"prev":167,"edge":75,"face":41,"loop_ref":41,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":164,"periodic_lift":[0,0]}},{"id":165,"from":39,"to":43,"twin":158,"next":166,"prev":164,"edge":82,"face":41,"loop_ref":41,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":165,"periodic_lift":[0,0]}},{"id":166,"from":43,"to":34,"twin":161,"next":167,"prev":165,"edge":83,"face":41,"loop_ref":41,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":166,"periodic_lift":[0,0]}},{"id":167,"from":34,"to":28,"twin":117,"next":164,"prev":166,"edge":63,"face":41,"loop_ref":41,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":167,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false},{"id":2,"start_halfedge":8,"face_ref":2,"is_hole":false},{"id":3,"start_halfedge":12,"face_ref":3,"is_hole":false},{"id":4,"start_halfedge":16,"face_ref":4,"is_hole":false},{"id":5,"start_halfedge":20,"face_ref":5,"is_hole":false},{"id":6,"start_halfedge":24,"face_ref":6,"is_hole":false},{"id":7,"start_halfedge":28,"face_ref":7,"is_hole":false},{"id":8,"start_halfedge":32,"face_ref":8,"is_hole":false},{"id":9,"start_halfedge":36,"face_ref":9,"is_hole":false},{"id":10,"start_halfedge":40,"face_ref":10,"is_hole":false},{"id":11,"start_halfedge":44,"face_ref":11,"is_hole":false},{"id":12,"start_halfedge":48,"face_ref":12,"is_hole":false},{"id":13,"start_halfedge":52,"face_ref":13,"is_hole":false},{"id":14,"start_halfedge":56,"face_ref":14,"is_hole":false},{"id":15,"start_halfedge":60,"face_ref":15,"is_hole":false},{"id":16,"start_halfedge":64,"face_ref":16,"is_hole":false},{"id":17,"start_halfedge":68,"face_ref":17,"is_hole":false},{"id":18,"start_halfedge":72,"face_ref":18,"is_hole":false},{"id":19,"start_halfedge":76,"face_ref":19,"is_hole":false},{"id":20,"start_halfedge":80,"face_ref":20,"is_hole":false},{"id":21,"start_halfedge":84,"face_ref":21,"is_hole":false},{"id":22,"start_halfedge":88,"face_ref":22,"is_hole":false},{"id":23,"start_halfedge":92,"face_ref":23,"is_hole":false},{"id":24,"start_halfedge":96,"face_ref":24,"is_hole":false},{"id":25,"start_halfedge":100,"face_ref":25,"is_hole":false},{"id":26,"start_halfedge":104,"face_ref":26,"is_hole":false},{"id":27,"start_halfedge":108,"face_ref":27,"is_hole":false},{"id":28,"start_halfedge":112,"face_ref":28,"is_hole":false},{"id":29,"start_halfedge":116,"face_ref":29,"is_hole":false},{"id":30,"start_halfedge":120,"face_ref":30,"is_hole":false},{"id":31,"start_halfedge":124,"face_ref":31,"is_hole":false},{"id":32,"start_halfedge":128,"face_ref":32,"is_hole":false},{"id":33,"start_halfedge":132,"face_ref":33,"is_hole":false},{"id":34,"start_halfedge":136,"face_ref":34,"is_hole":false},{"id":35,"start_halfedge":140,"face_ref":35,"is_hole":false},{"id":36,"start_halfedge":144,"face_ref":36,"is_hole":false},{"id":37,"start_halfedge":148,"face_ref":37,"is_hole":false},{"id":38,"start_halfedge":152,"face_ref":38,"is_hole":false},{"id":39,"start_halfedge":156,"face_ref":39,"is_hole":false},{"id":40,"start_halfedge":160,"face_ref":40,"is_hole":false},{"id":41,"start_halfedge":164,"face_ref":41,"is_hole":false}],"faces":[{"id":0,"key":"left-0","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":-1e-8,"hi":0.50000001}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":1,"key":"front-1","surface":1,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":-1e-8,"hi":0.50000001}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":2,"key":"bottom-2","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":-1e-8,"hi":1.50000001}],"outer":2,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":3,"key":"left-3","surface":3,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":0.50000001}],"outer":3,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":4,"key":"front-4","surface":4,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":0.49999999,"hi":3.00000001}],"outer":4,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":5,"key":"top-5","surface":5,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":-1e-8,"hi":0.50000001}],"outer":5,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":6,"key":"left-6","surface":6,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":0.49999999,"hi":3.00000001}],"outer":6,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":7,"key":"back-7","surface":7,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":-1e-8,"hi":1.50000001}],"outer":7,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":8,"key":"bottom-8","surface":8,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.50000001}],"outer":8,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":9,"key":"left-9","surface":9,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":0.49999999,"hi":3.00000001}],"outer":9,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"left","face":2}],"role":"Split","reversed":false}},{"id":10,"key":"back-10","surface":10,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.50000001}],"outer":10,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":11,"key":"top-11","surface":11,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":0.49999999,"hi":3.00000001}],"outer":11,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":12,"key":"right-12","surface":12,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":-1e-8,"hi":0.50000001}],"outer":12,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":13,"key":"front-13","surface":13,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":0.50000001}],"outer":13,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":14,"key":"bottom-14","surface":14,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":1.49999999,"hi":3.00000001}],"outer":14,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":15,"key":"right-15","surface":15,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":0.49999999,"hi":3.00000001}],"outer":15,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":16,"key":"front-16","surface":16,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.49999999,"hi":3.00000001},{"lo":0.49999999,"hi":3.00000001}],"outer":16,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"front","face":4}],"role":"Split","reversed":false}},{"id":17,"key":"top-17","surface":17,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":0.50000001}],"outer":17,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"top","face":1}],"role":"Split","reversed":false}},{"id":18,"key":"right-18","surface":18,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":0.50000001}],"outer":18,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"right","face":3}],"role":"Split","reversed":false}},{"id":19,"key":"back-19","surface":19,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":0.50000001},{"lo":1.49999999,"hi":3.00000001}],"outer":19,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"back","face":5}],"role":"Split","reversed":false}},{"id":20,"key":"bottom-20","surface":20,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.49999999,"hi":3.00000001},{"lo":1.49999999,"hi":3.00000001}],"outer":20,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-host","body":"box-host","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":21,"key":"left-21","surface":21,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":2.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":2.50000001}],"outer":21,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"left","face":2}],"role":"Split","reversed":false}},{"id":22,"key":"front-22","surface":22,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":2.49999999,"hi":3.00000001}],"outer":22,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"front","face":4}],"role":"Split","reversed":false}},{"id":23,"key":"top-23","surface":23,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":-1e-8,"hi":2.50000001}],"outer":23,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"top","face":1}],"role":"Split","reversed":false}},{"id":24,"key":"left-24","surface":24,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.50000001},{"lo":2.49999999,"hi":3.00000001}],"outer":24,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"left","face":2}],"role":"Split","reversed":false}},{"id":25,"key":"back-25","surface":25,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.50000001},{"lo":-1e-8,"hi":1.50000001}],"outer":25,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"back","face":5}],"role":"Split","reversed":false}},{"id":26,"key":"bottom-26","surface":26,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":2.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.50000001}],"outer":26,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":27,"key":"left-27","surface":27,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":2.49999999,"hi":3.00000001},{"lo":2.49999999,"hi":3.00000001}],"outer":27,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"left","face":2}],"role":"Split","reversed":false}},{"id":28,"key":"back-28","surface":28,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":2.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":1.50000001}],"outer":28,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"back","face":5}],"role":"Split","reversed":false}},{"id":29,"key":"top-29","surface":29,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.50000001},{"lo":2.49999999,"hi":3.00000001}],"outer":29,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"top","face":1}],"role":"Split","reversed":false}},{"id":30,"key":"right-30","surface":30,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.50000001},{"lo":-1e-8,"hi":2.50000001}],"outer":30,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"right","face":3}],"role":"Split","reversed":false}},{"id":31,"key":"front-31","surface":31,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":2.50000001}],"outer":31,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"front","face":4}],"role":"Split","reversed":false}},{"id":32,"key":"bottom-32","surface":32,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.50000001},{"lo":1.49999999,"hi":3.00000001}],"outer":32,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":33,"key":"right-33","surface":33,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.50000001},{"lo":2.49999999,"hi":3.00000001}],"outer":33,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"right","face":3}],"role":"Split","reversed":false}},{"id":34,"key":"front-34","surface":34,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.49999999,"hi":3.00000001},{"lo":2.49999999,"hi":3.00000001}],"outer":34,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"front","face":4}],"role":"Split","reversed":false}},{"id":35,"key":"top-35","surface":35,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":2.50000001}],"outer":35,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"top","face":1}],"role":"Split","reversed":false}},{"id":36,"key":"right-36","surface":36,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":2.49999999,"hi":3.00000001},{"lo":-1e-8,"hi":2.50000001}],"outer":36,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"right","face":3}],"role":"Split","reversed":false}},{"id":37,"key":"back-37","surface":37,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.50000001},{"lo":1.49999999,"hi":3.00000001}],"outer":37,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"back","face":5}],"role":"Split","reversed":false}},{"id":38,"key":"bottom-38","surface":38,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":2.49999999,"hi":3.00000001},{"lo":1.49999999,"hi":3.00000001}],"outer":38,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"bottom","face":0}],"role":"Split","reversed":false}},{"id":39,"key":"right-39","surface":39,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":2.49999999,"hi":3.00000001},{"lo":2.49999999,"hi":3.00000001}],"outer":39,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"right","face":3}],"role":"Split","reversed":false}},{"id":40,"key":"back-40","surface":40,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":2.49999999,"hi":3.00000001},{"lo":1.49999999,"hi":3.00000001}],"outer":40,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"back","face":5}],"role":"Split","reversed":false}},{"id":41,"key":"top-41","surface":41,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":1.49999999,"hi":3.00000001},{"lo":2.49999999,"hi":3.00000001}],"outer":41,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"box-cutter","body":"box-cutter","key":"top","face":1}],"role":"Split","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,3,6,2,1,5,9,4,7,8,14,13,17,11,10,16,19,20,12,15,22,21,24,26,18,31,34,23,27,25,38,32,30,33,35,29,28,37,36,39,41,40],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-union.handlers.json b/main/opengeometry/tests/fixtures/cases/box-union.handlers.json new file mode 100644 index 0000000..4b8564a --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-union.handlers.json @@ -0,0 +1 @@ +["boolean_boxes"] \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-union.step.m b/main/opengeometry/tests/fixtures/cases/box-union.step.m new file mode 100644 index 0000000..62f9af9 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-union.step.m @@ -0,0 +1,2131 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('box-union','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#24=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#28=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#34=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#35=AXIS2_PLACEMENT_3D('',#32,#33,#34); +#36=PLANE('',#35); +#37=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#38=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#39=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#40=AXIS2_PLACEMENT_3D('',#37,#38,#39); +#41=PLANE('',#40); +#42=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#43=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#44=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#45=AXIS2_PLACEMENT_3D('',#42,#43,#44); +#46=PLANE('',#45); +#47=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#48=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#49=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#50=AXIS2_PLACEMENT_3D('',#47,#48,#49); +#51=PLANE('',#50); +#52=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#53=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#54=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#55=AXIS2_PLACEMENT_3D('',#52,#53,#54); +#56=PLANE('',#55); +#57=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#58=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#59=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#60=AXIS2_PLACEMENT_3D('',#57,#58,#59); +#61=PLANE('',#60); +#62=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#63=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#64=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#65=AXIS2_PLACEMENT_3D('',#62,#63,#64); +#66=PLANE('',#65); +#67=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#68=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#69=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#70=AXIS2_PLACEMENT_3D('',#67,#68,#69); +#71=PLANE('',#70); +#72=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#73=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#74=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#75=AXIS2_PLACEMENT_3D('',#72,#73,#74); +#76=PLANE('',#75); +#77=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#78=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#79=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#80=AXIS2_PLACEMENT_3D('',#77,#78,#79); +#81=PLANE('',#80); +#82=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#83=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#84=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#85=AXIS2_PLACEMENT_3D('',#82,#83,#84); +#86=PLANE('',#85); +#87=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#88=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#90=AXIS2_PLACEMENT_3D('',#87,#88,#89); +#91=PLANE('',#90); +#92=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#93=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#94=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#95=AXIS2_PLACEMENT_3D('',#92,#93,#94); +#96=PLANE('',#95); +#97=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#98=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#99=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#100=AXIS2_PLACEMENT_3D('',#97,#98,#99); +#101=PLANE('',#100); +#102=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#103=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#104=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#105=AXIS2_PLACEMENT_3D('',#102,#103,#104); +#106=PLANE('',#105); +#107=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#108=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#109=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#110=AXIS2_PLACEMENT_3D('',#107,#108,#109); +#111=PLANE('',#110); +#112=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#113=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#114=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#115=AXIS2_PLACEMENT_3D('',#112,#113,#114); +#116=PLANE('',#115); +#117=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#118=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#119=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#120=AXIS2_PLACEMENT_3D('',#117,#118,#119); +#121=PLANE('',#120); +#122=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#123=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#124=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#125=AXIS2_PLACEMENT_3D('',#122,#123,#124); +#126=PLANE('',#125); +#127=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#128=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#129=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#130=AXIS2_PLACEMENT_3D('',#127,#128,#129); +#131=PLANE('',#130); +#132=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#133=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#134=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#135=AXIS2_PLACEMENT_3D('',#132,#133,#134); +#136=PLANE('',#135); +#137=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#138=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#139=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#140=AXIS2_PLACEMENT_3D('',#137,#138,#139); +#141=PLANE('',#140); +#142=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#143=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#144=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#145=AXIS2_PLACEMENT_3D('',#142,#143,#144); +#146=PLANE('',#145); +#147=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#148=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#149=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#150=AXIS2_PLACEMENT_3D('',#147,#148,#149); +#151=PLANE('',#150); +#152=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#153=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#154=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#155=AXIS2_PLACEMENT_3D('',#152,#153,#154); +#156=PLANE('',#155); +#157=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#158=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#159=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#160=AXIS2_PLACEMENT_3D('',#157,#158,#159); +#161=PLANE('',#160); +#162=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#163=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#164=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#165=AXIS2_PLACEMENT_3D('',#162,#163,#164); +#166=PLANE('',#165); +#167=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#168=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#169=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#170=AXIS2_PLACEMENT_3D('',#167,#168,#169); +#171=PLANE('',#170); +#172=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#173=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#174=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#175=AXIS2_PLACEMENT_3D('',#172,#173,#174); +#176=PLANE('',#175); +#177=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#178=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#179=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#180=AXIS2_PLACEMENT_3D('',#177,#178,#179); +#181=PLANE('',#180); +#182=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#183=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#184=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#185=AXIS2_PLACEMENT_3D('',#182,#183,#184); +#186=PLANE('',#185); +#187=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#188=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#189=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#190=AXIS2_PLACEMENT_3D('',#187,#188,#189); +#191=PLANE('',#190); +#192=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#193=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#194=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#195=AXIS2_PLACEMENT_3D('',#192,#193,#194); +#196=PLANE('',#195); +#197=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#198=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#199=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#200=AXIS2_PLACEMENT_3D('',#197,#198,#199); +#201=PLANE('',#200); +#202=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#203=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#204=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#205=AXIS2_PLACEMENT_3D('',#202,#203,#204); +#206=PLANE('',#205); +#207=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#208=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#209=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#210=AXIS2_PLACEMENT_3D('',#207,#208,#209); +#211=PLANE('',#210); +#212=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#213=VERTEX_POINT('',#212); +#214=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#215=VERTEX_POINT('',#214); +#216=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#217=VERTEX_POINT('',#216); +#218=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#219=VERTEX_POINT('',#218); +#220=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#221=VERTEX_POINT('',#220); +#222=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#223=VERTEX_POINT('',#222); +#224=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#225=VERTEX_POINT('',#224); +#226=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#227=VERTEX_POINT('',#226); +#228=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#229=VERTEX_POINT('',#228); +#230=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#231=VERTEX_POINT('',#230); +#232=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#233=VERTEX_POINT('',#232); +#234=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#235=VERTEX_POINT('',#234); +#236=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#237=VERTEX_POINT('',#236); +#238=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#239=VERTEX_POINT('',#238); +#240=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#241=VERTEX_POINT('',#240); +#242=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#243=VERTEX_POINT('',#242); +#244=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#245=VERTEX_POINT('',#244); +#246=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#247=VERTEX_POINT('',#246); +#248=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#249=VERTEX_POINT('',#248); +#250=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#251=VERTEX_POINT('',#250); +#252=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#253=VERTEX_POINT('',#252); +#254=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#255=VERTEX_POINT('',#254); +#256=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#257=VERTEX_POINT('',#256); +#258=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#259=VERTEX_POINT('',#258); +#260=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#261=VERTEX_POINT('',#260); +#262=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#263=VERTEX_POINT('',#262); +#264=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-3.00000000000000000E0)); +#265=VERTEX_POINT('',#264); +#266=CARTESIAN_POINT('',(3.00000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#267=VERTEX_POINT('',#266); +#268=CARTESIAN_POINT('',(3.00000000000000000E0,3.50000000000000000E0,-3.00000000000000000E0)); +#269=VERTEX_POINT('',#268); +#270=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-3.50000000000000000E0)); +#271=VERTEX_POINT('',#270); +#272=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#273=VERTEX_POINT('',#272); +#274=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-3.50000000000000000E0)); +#275=VERTEX_POINT('',#274); +#276=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#277=VERTEX_POINT('',#276); +#278=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-3.50000000000000000E0)); +#279=VERTEX_POINT('',#278); +#280=CARTESIAN_POINT('',(3.00000000000000000E0,3.50000000000000000E0,-3.50000000000000000E0)); +#281=VERTEX_POINT('',#280); +#282=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#283=VERTEX_POINT('',#282); +#284=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#285=VERTEX_POINT('',#284); +#286=CARTESIAN_POINT('',(4.50000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#287=VERTEX_POINT('',#286); +#288=CARTESIAN_POINT('',(4.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#289=VERTEX_POINT('',#288); +#290=CARTESIAN_POINT('',(4.50000000000000000E0,3.50000000000000000E0,-3.00000000000000000E0)); +#291=VERTEX_POINT('',#290); +#292=CARTESIAN_POINT('',(4.50000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#293=VERTEX_POINT('',#292); +#294=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#295=VERTEX_POINT('',#294); +#296=CARTESIAN_POINT('',(4.50000000000000000E0,3.00000000000000000E0,-3.50000000000000000E0)); +#297=VERTEX_POINT('',#296); +#298=CARTESIAN_POINT('',(4.50000000000000000E0,3.50000000000000000E0,-3.50000000000000000E0)); +#299=VERTEX_POINT('',#298); +#300=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#301=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#302=VECTOR('',#301,1.00000000000000000E0); +#303=LINE('',#300,#302); +#304=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#305=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#306=VECTOR('',#305,1.00000000000000000E0); +#307=LINE('',#304,#306); +#308=DEFINITIONAL_REPRESENTATION('',(#307),#1); +#309=PCURVE('',#6,#308); +#310=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#311=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#312=VECTOR('',#311,1.00000000000000000E0); +#313=LINE('',#310,#312); +#314=DEFINITIONAL_REPRESENTATION('',(#313),#1); +#315=PCURVE('',#21,#314); +#316=SURFACE_CURVE('',#303,(#309,#315),.CURVE_3D.); +#317=EDGE_CURVE('',#213,#215,#316,.T.); +#318=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#319=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#320=VECTOR('',#319,1.00000000000000000E0); +#321=LINE('',#318,#320); +#322=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#323=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#324=VECTOR('',#323,1.00000000000000000E0); +#325=LINE('',#322,#324); +#326=DEFINITIONAL_REPRESENTATION('',(#325),#1); +#327=PCURVE('',#6,#326); +#328=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#329=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#330=VECTOR('',#329,1.00000000000000000E0); +#331=LINE('',#328,#330); +#332=DEFINITIONAL_REPRESENTATION('',(#331),#1); +#333=PCURVE('',#36,#332); +#334=SURFACE_CURVE('',#321,(#327,#333),.CURVE_3D.); +#335=EDGE_CURVE('',#217,#215,#334,.T.); +#336=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#337=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#338=VECTOR('',#337,1.00000000000000000E0); +#339=LINE('',#336,#338); +#340=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#341=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#342=VECTOR('',#341,1.00000000000000000E0); +#343=LINE('',#340,#342); +#344=DEFINITIONAL_REPRESENTATION('',(#343),#1); +#345=PCURVE('',#6,#344); +#346=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#347=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#348=VECTOR('',#347,1.00000000000000000E0); +#349=LINE('',#346,#348); +#350=DEFINITIONAL_REPRESENTATION('',(#349),#1); +#351=PCURVE('',#16,#350); +#352=SURFACE_CURVE('',#339,(#345,#351),.CURVE_3D.); +#353=EDGE_CURVE('',#219,#217,#352,.T.); +#354=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#355=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#356=VECTOR('',#355,1.00000000000000000E0); +#357=LINE('',#354,#356); +#358=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#359=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#360=VECTOR('',#359,1.00000000000000000E0); +#361=LINE('',#358,#360); +#362=DEFINITIONAL_REPRESENTATION('',(#361),#1); +#363=PCURVE('',#6,#362); +#364=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#365=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#366=VECTOR('',#365,1.00000000000000000E0); +#367=LINE('',#364,#366); +#368=DEFINITIONAL_REPRESENTATION('',(#367),#1); +#369=PCURVE('',#11,#368); +#370=SURFACE_CURVE('',#357,(#363,#369),.CURVE_3D.); +#371=EDGE_CURVE('',#219,#213,#370,.T.); +#372=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#373=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#374=VECTOR('',#373,1.00000000000000000E0); +#375=LINE('',#372,#374); +#376=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#377=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#378=VECTOR('',#377,1.00000000000000000E0); +#379=LINE('',#376,#378); +#380=DEFINITIONAL_REPRESENTATION('',(#379),#1); +#381=PCURVE('',#11,#380); +#382=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#383=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#384=VECTOR('',#383,1.00000000000000000E0); +#385=LINE('',#382,#384); +#386=DEFINITIONAL_REPRESENTATION('',(#385),#1); +#387=PCURVE('',#71,#386); +#388=SURFACE_CURVE('',#375,(#381,#387),.CURVE_3D.); +#389=EDGE_CURVE('',#221,#223,#388,.T.); +#390=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#391=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#392=VECTOR('',#391,1.00000000000000000E0); +#393=LINE('',#390,#392); +#394=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#395=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#396=VECTOR('',#395,1.00000000000000000E0); +#397=LINE('',#394,#396); +#398=DEFINITIONAL_REPRESENTATION('',(#397),#1); +#399=PCURVE('',#11,#398); +#400=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#401=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#402=VECTOR('',#401,1.00000000000000000E0); +#403=LINE('',#400,#402); +#404=DEFINITIONAL_REPRESENTATION('',(#403),#1); +#405=PCURVE('',#26,#404); +#406=SURFACE_CURVE('',#393,(#399,#405),.CURVE_3D.); +#407=EDGE_CURVE('',#213,#223,#406,.T.); +#408=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#409=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#410=VECTOR('',#409,1.00000000000000000E0); +#411=LINE('',#408,#410); +#412=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#413=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#414=VECTOR('',#413,1.00000000000000000E0); +#415=LINE('',#412,#414); +#416=DEFINITIONAL_REPRESENTATION('',(#415),#1); +#417=PCURVE('',#11,#416); +#418=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#419=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#420=VECTOR('',#419,1.00000000000000000E0); +#421=LINE('',#418,#420); +#422=DEFINITIONAL_REPRESENTATION('',(#421),#1); +#423=PCURVE('',#16,#422); +#424=SURFACE_CURVE('',#411,(#417,#423),.CURVE_3D.); +#425=EDGE_CURVE('',#219,#221,#424,.T.); +#426=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#427=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#428=VECTOR('',#427,1.00000000000000000E0); +#429=LINE('',#426,#428); +#430=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#431=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#432=VECTOR('',#431,1.00000000000000000E0); +#433=LINE('',#430,#432); +#434=DEFINITIONAL_REPRESENTATION('',(#433),#1); +#435=PCURVE('',#16,#434); +#436=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#437=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#438=VECTOR('',#437,1.00000000000000000E0); +#439=LINE('',#436,#438); +#440=DEFINITIONAL_REPRESENTATION('',(#439),#1); +#441=PCURVE('',#46,#440); +#442=SURFACE_CURVE('',#429,(#435,#441),.CURVE_3D.); +#443=EDGE_CURVE('',#217,#225,#442,.T.); +#444=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#445=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#446=VECTOR('',#445,1.00000000000000000E0); +#447=LINE('',#444,#446); +#448=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#449=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#450=VECTOR('',#449,1.00000000000000000E0); +#451=LINE('',#448,#450); +#452=DEFINITIONAL_REPRESENTATION('',(#451),#1); +#453=PCURVE('',#16,#452); +#454=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#455=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#456=VECTOR('',#455,1.00000000000000000E0); +#457=LINE('',#454,#456); +#458=DEFINITIONAL_REPRESENTATION('',(#457),#1); +#459=PCURVE('',#76,#458); +#460=SURFACE_CURVE('',#447,(#453,#459),.CURVE_3D.); +#461=EDGE_CURVE('',#221,#225,#460,.T.); +#462=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#463=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#464=VECTOR('',#463,1.00000000000000000E0); +#465=LINE('',#462,#464); +#466=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#467=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#468=VECTOR('',#467,1.00000000000000000E0); +#469=LINE('',#466,#468); +#470=DEFINITIONAL_REPRESENTATION('',(#469),#1); +#471=PCURVE('',#21,#470); +#472=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#473=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#474=VECTOR('',#473,1.00000000000000000E0); +#475=LINE('',#472,#474); +#476=DEFINITIONAL_REPRESENTATION('',(#475),#1); +#477=PCURVE('',#31,#476); +#478=SURFACE_CURVE('',#465,(#471,#477),.CURVE_3D.); +#479=EDGE_CURVE('',#227,#229,#478,.T.); +#480=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#481=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#482=VECTOR('',#481,1.00000000000000000E0); +#483=LINE('',#480,#482); +#484=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#485=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#486=VECTOR('',#485,1.00000000000000000E0); +#487=LINE('',#484,#486); +#488=DEFINITIONAL_REPRESENTATION('',(#487),#1); +#489=PCURVE('',#21,#488); +#490=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#491=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#492=VECTOR('',#491,1.00000000000000000E0); +#493=LINE('',#490,#492); +#494=DEFINITIONAL_REPRESENTATION('',(#493),#1); +#495=PCURVE('',#51,#494); +#496=SURFACE_CURVE('',#483,(#489,#495),.CURVE_3D.); +#497=EDGE_CURVE('',#215,#229,#496,.T.); +#498=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#499=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#500=VECTOR('',#499,1.00000000000000000E0); +#501=LINE('',#498,#500); +#502=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#503=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#504=VECTOR('',#503,1.00000000000000000E0); +#505=LINE('',#502,#504); +#506=DEFINITIONAL_REPRESENTATION('',(#505),#1); +#507=PCURVE('',#21,#506); +#508=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#509=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#510=VECTOR('',#509,1.00000000000000000E0); +#511=LINE('',#508,#510); +#512=DEFINITIONAL_REPRESENTATION('',(#511),#1); +#513=PCURVE('',#26,#512); +#514=SURFACE_CURVE('',#501,(#507,#513),.CURVE_3D.); +#515=EDGE_CURVE('',#213,#227,#514,.T.); +#516=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#517=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#518=VECTOR('',#517,1.00000000000000000E0); +#519=LINE('',#516,#518); +#520=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#521=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#522=VECTOR('',#521,1.00000000000000000E0); +#523=LINE('',#520,#522); +#524=DEFINITIONAL_REPRESENTATION('',(#523),#1); +#525=PCURVE('',#26,#524); +#526=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#527=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#528=VECTOR('',#527,1.00000000000000000E0); +#529=LINE('',#526,#528); +#530=DEFINITIONAL_REPRESENTATION('',(#529),#1); +#531=PCURVE('',#86,#530); +#532=SURFACE_CURVE('',#519,(#525,#531),.CURVE_3D.); +#533=EDGE_CURVE('',#223,#231,#532,.T.); +#534=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#535=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#536=VECTOR('',#535,1.00000000000000000E0); +#537=LINE('',#534,#536); +#538=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#539=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#540=VECTOR('',#539,1.00000000000000000E0); +#541=LINE('',#538,#540); +#542=DEFINITIONAL_REPRESENTATION('',(#541),#1); +#543=PCURVE('',#26,#542); +#544=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#545=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#546=VECTOR('',#545,1.00000000000000000E0); +#547=LINE('',#544,#546); +#548=DEFINITIONAL_REPRESENTATION('',(#547),#1); +#549=PCURVE('',#31,#548); +#550=SURFACE_CURVE('',#537,(#543,#549),.CURVE_3D.); +#551=EDGE_CURVE('',#227,#231,#550,.T.); +#552=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#553=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#554=VECTOR('',#553,1.00000000000000000E0); +#555=LINE('',#552,#554); +#556=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#557=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#558=VECTOR('',#557,1.00000000000000000E0); +#559=LINE('',#556,#558); +#560=DEFINITIONAL_REPRESENTATION('',(#559),#1); +#561=PCURVE('',#31,#560); +#562=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#563=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#564=VECTOR('',#563,1.00000000000000000E0); +#565=LINE('',#562,#564); +#566=DEFINITIONAL_REPRESENTATION('',(#565),#1); +#567=PCURVE('',#91,#566); +#568=SURFACE_CURVE('',#555,(#561,#567),.CURVE_3D.); +#569=EDGE_CURVE('',#231,#233,#568,.T.); +#570=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#571=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#572=VECTOR('',#571,1.00000000000000000E0); +#573=LINE('',#570,#572); +#574=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#575=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#576=VECTOR('',#575,1.00000000000000000E0); +#577=LINE('',#574,#576); +#578=DEFINITIONAL_REPRESENTATION('',(#577),#1); +#579=PCURVE('',#31,#578); +#580=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#581=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#582=VECTOR('',#581,1.00000000000000000E0); +#583=LINE('',#580,#582); +#584=DEFINITIONAL_REPRESENTATION('',(#583),#1); +#585=PCURVE('',#61,#584); +#586=SURFACE_CURVE('',#573,(#579,#585),.CURVE_3D.); +#587=EDGE_CURVE('',#229,#233,#586,.T.); +#588=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#589=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#590=VECTOR('',#589,1.00000000000000000E0); +#591=LINE('',#588,#590); +#592=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#593=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#594=VECTOR('',#593,1.00000000000000000E0); +#595=LINE('',#592,#594); +#596=DEFINITIONAL_REPRESENTATION('',(#595),#1); +#597=PCURVE('',#36,#596); +#598=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#599=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#600=VECTOR('',#599,1.00000000000000000E0); +#601=LINE('',#598,#600); +#602=DEFINITIONAL_REPRESENTATION('',(#601),#1); +#603=PCURVE('',#51,#602); +#604=SURFACE_CURVE('',#591,(#597,#603),.CURVE_3D.); +#605=EDGE_CURVE('',#215,#235,#604,.T.); +#606=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#607=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#608=VECTOR('',#607,1.00000000000000000E0); +#609=LINE('',#606,#608); +#610=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#611=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#612=VECTOR('',#611,1.00000000000000000E0); +#613=LINE('',#610,#612); +#614=DEFINITIONAL_REPRESENTATION('',(#613),#1); +#615=PCURVE('',#36,#614); +#616=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#617=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#618=VECTOR('',#617,1.00000000000000000E0); +#619=LINE('',#616,#618); +#620=DEFINITIONAL_REPRESENTATION('',(#619),#1); +#621=PCURVE('',#41,#620); +#622=SURFACE_CURVE('',#609,(#615,#621),.CURVE_3D.); +#623=EDGE_CURVE('',#237,#235,#622,.T.); +#624=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#625=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#626=VECTOR('',#625,1.00000000000000000E0); +#627=LINE('',#624,#626); +#628=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#629=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#630=VECTOR('',#629,1.00000000000000000E0); +#631=LINE('',#628,#630); +#632=DEFINITIONAL_REPRESENTATION('',(#631),#1); +#633=PCURVE('',#36,#632); +#634=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#635=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#636=VECTOR('',#635,1.00000000000000000E0); +#637=LINE('',#634,#636); +#638=DEFINITIONAL_REPRESENTATION('',(#637),#1); +#639=PCURVE('',#46,#638); +#640=SURFACE_CURVE('',#627,(#633,#639),.CURVE_3D.); +#641=EDGE_CURVE('',#217,#237,#640,.T.); +#642=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#643=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#644=VECTOR('',#643,1.00000000000000000E0); +#645=LINE('',#642,#644); +#646=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#647=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#648=VECTOR('',#647,1.00000000000000000E0); +#649=LINE('',#646,#648); +#650=DEFINITIONAL_REPRESENTATION('',(#649),#1); +#651=PCURVE('',#41,#650); +#652=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#653=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#654=VECTOR('',#653,1.00000000000000000E0); +#655=LINE('',#652,#654); +#656=DEFINITIONAL_REPRESENTATION('',(#655),#1); +#657=PCURVE('',#56,#656); +#658=SURFACE_CURVE('',#645,(#651,#657),.CURVE_3D.); +#659=EDGE_CURVE('',#235,#239,#658,.T.); +#660=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#661=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#662=VECTOR('',#661,1.00000000000000000E0); +#663=LINE('',#660,#662); +#664=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#665=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#666=VECTOR('',#665,1.00000000000000000E0); +#667=LINE('',#664,#666); +#668=DEFINITIONAL_REPRESENTATION('',(#667),#1); +#669=PCURVE('',#41,#668); +#670=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#671=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#672=VECTOR('',#671,1.00000000000000000E0); +#673=LINE('',#670,#672); +#674=DEFINITIONAL_REPRESENTATION('',(#673),#1); +#675=PCURVE('',#101,#674); +#676=SURFACE_CURVE('',#663,(#669,#675),.CURVE_3D.); +#677=EDGE_CURVE('',#241,#239,#676,.T.); +#678=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#679=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#680=VECTOR('',#679,1.00000000000000000E0); +#681=LINE('',#678,#680); +#682=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#683=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#684=VECTOR('',#683,1.00000000000000000E0); +#685=LINE('',#682,#684); +#686=DEFINITIONAL_REPRESENTATION('',(#685),#1); +#687=PCURVE('',#41,#686); +#688=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#689=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#690=VECTOR('',#689,1.00000000000000000E0); +#691=LINE('',#688,#690); +#692=DEFINITIONAL_REPRESENTATION('',(#691),#1); +#693=PCURVE('',#46,#692); +#694=SURFACE_CURVE('',#681,(#687,#693),.CURVE_3D.); +#695=EDGE_CURVE('',#237,#241,#694,.T.); +#696=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#697=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#698=VECTOR('',#697,1.00000000000000000E0); +#699=LINE('',#696,#698); +#700=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#701=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#702=VECTOR('',#701,1.00000000000000000E0); +#703=LINE('',#700,#702); +#704=DEFINITIONAL_REPRESENTATION('',(#703),#1); +#705=PCURVE('',#46,#704); +#706=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#707=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#708=VECTOR('',#707,1.00000000000000000E0); +#709=LINE('',#706,#708); +#710=DEFINITIONAL_REPRESENTATION('',(#709),#1); +#711=PCURVE('',#106,#710); +#712=SURFACE_CURVE('',#699,(#705,#711),.CURVE_3D.); +#713=EDGE_CURVE('',#225,#241,#712,.T.); +#714=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#715=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#716=VECTOR('',#715,1.00000000000000000E0); +#717=LINE('',#714,#716); +#718=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1)); +#719=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#720=VECTOR('',#719,1.00000000000000000E0); +#721=LINE('',#718,#720); +#722=DEFINITIONAL_REPRESENTATION('',(#721),#1); +#723=PCURVE('',#51,#722); +#724=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#725=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#726=VECTOR('',#725,1.00000000000000000E0); +#727=LINE('',#724,#726); +#728=DEFINITIONAL_REPRESENTATION('',(#727),#1); +#729=PCURVE('',#61,#728); +#730=SURFACE_CURVE('',#717,(#723,#729),.CURVE_3D.); +#731=EDGE_CURVE('',#229,#243,#730,.T.); +#732=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#733=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#734=VECTOR('',#733,1.00000000000000000E0); +#735=LINE('',#732,#734); +#736=CARTESIAN_POINT('',(5.00000000000000000E-1,3.00000000000000000E0)); +#737=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#738=VECTOR('',#737,1.00000000000000000E0); +#739=LINE('',#736,#738); +#740=DEFINITIONAL_REPRESENTATION('',(#739),#1); +#741=PCURVE('',#51,#740); +#742=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#743=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#744=VECTOR('',#743,1.00000000000000000E0); +#745=LINE('',#742,#744); +#746=DEFINITIONAL_REPRESENTATION('',(#745),#1); +#747=PCURVE('',#56,#746); +#748=SURFACE_CURVE('',#735,(#741,#747),.CURVE_3D.); +#749=EDGE_CURVE('',#235,#243,#748,.T.); +#750=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#751=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#752=VECTOR('',#751,1.00000000000000000E0); +#753=LINE('',#750,#752); +#754=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#755=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#756=VECTOR('',#755,1.00000000000000000E0); +#757=LINE('',#754,#756); +#758=DEFINITIONAL_REPRESENTATION('',(#757),#1); +#759=PCURVE('',#56,#758); +#760=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#761=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#762=VECTOR('',#761,1.00000000000000000E0); +#763=LINE('',#760,#762); +#764=DEFINITIONAL_REPRESENTATION('',(#763),#1); +#765=PCURVE('',#61,#764); +#766=SURFACE_CURVE('',#753,(#759,#765),.CURVE_3D.); +#767=EDGE_CURVE('',#243,#245,#766,.T.); +#768=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#769=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#770=VECTOR('',#769,1.00000000000000000E0); +#771=LINE('',#768,#770); +#772=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#773=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#774=VECTOR('',#773,1.00000000000000000E0); +#775=LINE('',#772,#774); +#776=DEFINITIONAL_REPRESENTATION('',(#775),#1); +#777=PCURVE('',#56,#776); +#778=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#779=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#780=VECTOR('',#779,1.00000000000000000E0); +#781=LINE('',#778,#780); +#782=DEFINITIONAL_REPRESENTATION('',(#781),#1); +#783=PCURVE('',#126,#782); +#784=SURFACE_CURVE('',#771,(#777,#783),.CURVE_3D.); +#785=EDGE_CURVE('',#239,#245,#784,.T.); +#786=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#787=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#788=VECTOR('',#787,1.00000000000000000E0); +#789=LINE('',#786,#788); +#790=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#791=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#792=VECTOR('',#791,1.00000000000000000E0); +#793=LINE('',#790,#792); +#794=DEFINITIONAL_REPRESENTATION('',(#793),#1); +#795=PCURVE('',#61,#794); +#796=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#797=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#798=VECTOR('',#797,1.00000000000000000E0); +#799=LINE('',#796,#798); +#800=DEFINITIONAL_REPRESENTATION('',(#799),#1); +#801=PCURVE('',#111,#800); +#802=SURFACE_CURVE('',#789,(#795,#801),.CURVE_3D.); +#803=EDGE_CURVE('',#233,#245,#802,.T.); +#804=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#805=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#806=VECTOR('',#805,1.00000000000000000E0); +#807=LINE('',#804,#806); +#808=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#809=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#810=VECTOR('',#809,1.00000000000000000E0); +#811=LINE('',#808,#810); +#812=DEFINITIONAL_REPRESENTATION('',(#811),#1); +#813=PCURVE('',#66,#812); +#814=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#815=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#816=VECTOR('',#815,1.00000000000000000E0); +#817=LINE('',#814,#816); +#818=DEFINITIONAL_REPRESENTATION('',(#817),#1); +#819=PCURVE('',#76,#818); +#820=SURFACE_CURVE('',#807,(#813,#819),.CURVE_3D.); +#821=EDGE_CURVE('',#247,#249,#820,.T.); +#822=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#823=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#824=VECTOR('',#823,1.00000000000000000E0); +#825=LINE('',#822,#824); +#826=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#827=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#828=VECTOR('',#827,1.00000000000000000E0); +#829=LINE('',#826,#828); +#830=DEFINITIONAL_REPRESENTATION('',(#829),#1); +#831=PCURVE('',#66,#830); +#832=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#833=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#834=VECTOR('',#833,1.00000000000000000E0); +#835=LINE('',#832,#834); +#836=DEFINITIONAL_REPRESENTATION('',(#835),#1); +#837=PCURVE('',#96,#836); +#838=SURFACE_CURVE('',#825,(#831,#837),.CURVE_3D.); +#839=EDGE_CURVE('',#249,#251,#838,.T.); +#840=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#841=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#842=VECTOR('',#841,1.00000000000000000E0); +#843=LINE('',#840,#842); +#844=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#845=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#846=VECTOR('',#845,1.00000000000000000E0); +#847=LINE('',#844,#846); +#848=DEFINITIONAL_REPRESENTATION('',(#847),#1); +#849=PCURVE('',#66,#848); +#850=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#851=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#852=VECTOR('',#851,1.00000000000000000E0); +#853=LINE('',#850,#852); +#854=DEFINITIONAL_REPRESENTATION('',(#853),#1); +#855=PCURVE('',#81,#854); +#856=SURFACE_CURVE('',#843,(#849,#855),.CURVE_3D.); +#857=EDGE_CURVE('',#253,#251,#856,.T.); +#858=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#859=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#860=VECTOR('',#859,1.00000000000000000E0); +#861=LINE('',#858,#860); +#862=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#863=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#864=VECTOR('',#863,1.00000000000000000E0); +#865=LINE('',#862,#864); +#866=DEFINITIONAL_REPRESENTATION('',(#865),#1); +#867=PCURVE('',#66,#866); +#868=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#869=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#870=VECTOR('',#869,1.00000000000000000E0); +#871=LINE('',#868,#870); +#872=DEFINITIONAL_REPRESENTATION('',(#871),#1); +#873=PCURVE('',#71,#872); +#874=SURFACE_CURVE('',#861,(#867,#873),.CURVE_3D.); +#875=EDGE_CURVE('',#247,#253,#874,.T.); +#876=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#877=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#878=VECTOR('',#877,1.00000000000000000E0); +#879=LINE('',#876,#878); +#880=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#881=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#882=VECTOR('',#881,1.00000000000000000E0); +#883=LINE('',#880,#882); +#884=DEFINITIONAL_REPRESENTATION('',(#883),#1); +#885=PCURVE('',#71,#884); +#886=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#887=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#888=VECTOR('',#887,1.00000000000000000E0); +#889=LINE('',#886,#888); +#890=DEFINITIONAL_REPRESENTATION('',(#889),#1); +#891=PCURVE('',#86,#890); +#892=SURFACE_CURVE('',#879,(#885,#891),.CURVE_3D.); +#893=EDGE_CURVE('',#223,#253,#892,.T.); +#894=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#895=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#896=VECTOR('',#895,1.00000000000000000E0); +#897=LINE('',#894,#896); +#898=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#899=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#900=VECTOR('',#899,1.00000000000000000E0); +#901=LINE('',#898,#900); +#902=DEFINITIONAL_REPRESENTATION('',(#901),#1); +#903=PCURVE('',#71,#902); +#904=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#905=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#906=VECTOR('',#905,1.00000000000000000E0); +#907=LINE('',#904,#906); +#908=DEFINITIONAL_REPRESENTATION('',(#907),#1); +#909=PCURVE('',#76,#908); +#910=SURFACE_CURVE('',#897,(#903,#909),.CURVE_3D.); +#911=EDGE_CURVE('',#221,#247,#910,.T.); +#912=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#913=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#914=VECTOR('',#913,1.00000000000000000E0); +#915=LINE('',#912,#914); +#916=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#917=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#918=VECTOR('',#917,1.00000000000000000E0); +#919=LINE('',#916,#918); +#920=DEFINITIONAL_REPRESENTATION('',(#919),#1); +#921=PCURVE('',#76,#920); +#922=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#923=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#924=VECTOR('',#923,1.00000000000000000E0); +#925=LINE('',#922,#924); +#926=DEFINITIONAL_REPRESENTATION('',(#925),#1); +#927=PCURVE('',#106,#926); +#928=SURFACE_CURVE('',#915,(#921,#927),.CURVE_3D.); +#929=EDGE_CURVE('',#225,#249,#928,.T.); +#930=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#931=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#932=VECTOR('',#931,1.00000000000000000E0); +#933=LINE('',#930,#932); +#934=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#935=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#936=VECTOR('',#935,1.00000000000000000E0); +#937=LINE('',#934,#936); +#938=DEFINITIONAL_REPRESENTATION('',(#937),#1); +#939=PCURVE('',#81,#938); +#940=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#941=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#942=VECTOR('',#941,1.00000000000000000E0); +#943=LINE('',#940,#942); +#944=DEFINITIONAL_REPRESENTATION('',(#943),#1); +#945=PCURVE('',#161,#944); +#946=SURFACE_CURVE('',#933,(#939,#945),.CURVE_3D.); +#947=EDGE_CURVE('',#251,#255,#946,.T.); +#948=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#949=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#950=VECTOR('',#949,1.00000000000000000E0); +#951=LINE('',#948,#950); +#952=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#953=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#954=VECTOR('',#953,1.00000000000000000E0); +#955=LINE('',#952,#954); +#956=DEFINITIONAL_REPRESENTATION('',(#955),#1); +#957=PCURVE('',#81,#956); +#958=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#959=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#960=VECTOR('',#959,1.00000000000000000E0); +#961=LINE('',#958,#960); +#962=DEFINITIONAL_REPRESENTATION('',(#961),#1); +#963=PCURVE('',#91,#962); +#964=SURFACE_CURVE('',#951,(#957,#963),.CURVE_3D.); +#965=EDGE_CURVE('',#257,#255,#964,.T.); +#966=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,0.00000000000000000E0)); +#967=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#968=VECTOR('',#967,1.00000000000000000E0); +#969=LINE('',#966,#968); +#970=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E-1)); +#971=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#972=VECTOR('',#971,1.00000000000000000E0); +#973=LINE('',#970,#972); +#974=DEFINITIONAL_REPRESENTATION('',(#973),#1); +#975=PCURVE('',#81,#974); +#976=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1)); +#977=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#978=VECTOR('',#977,1.00000000000000000E0); +#979=LINE('',#976,#978); +#980=DEFINITIONAL_REPRESENTATION('',(#979),#1); +#981=PCURVE('',#86,#980); +#982=SURFACE_CURVE('',#969,(#975,#981),.CURVE_3D.); +#983=EDGE_CURVE('',#253,#257,#982,.T.); +#984=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#985=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#986=VECTOR('',#985,1.00000000000000000E0); +#987=LINE('',#984,#986); +#988=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0)); +#989=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#990=VECTOR('',#989,1.00000000000000000E0); +#991=LINE('',#988,#990); +#992=DEFINITIONAL_REPRESENTATION('',(#991),#1); +#993=PCURVE('',#86,#992); +#994=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#995=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#996=VECTOR('',#995,1.00000000000000000E0); +#997=LINE('',#994,#996); +#998=DEFINITIONAL_REPRESENTATION('',(#997),#1); +#999=PCURVE('',#91,#998); +#1000=SURFACE_CURVE('',#987,(#993,#999),.CURVE_3D.); +#1001=EDGE_CURVE('',#231,#257,#1000,.T.); +#1002=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#1003=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1004=VECTOR('',#1003,1.00000000000000000E0); +#1005=LINE('',#1002,#1004); +#1006=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1)); +#1007=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1008=VECTOR('',#1007,1.00000000000000000E0); +#1009=LINE('',#1006,#1008); +#1010=DEFINITIONAL_REPRESENTATION('',(#1009),#1); +#1011=PCURVE('',#91,#1010); +#1012=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#1013=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1014=VECTOR('',#1013,1.00000000000000000E0); +#1015=LINE('',#1012,#1014); +#1016=DEFINITIONAL_REPRESENTATION('',(#1015),#1); +#1017=PCURVE('',#116,#1016); +#1018=SURFACE_CURVE('',#1005,(#1011,#1017),.CURVE_3D.); +#1019=EDGE_CURVE('',#233,#255,#1018,.T.); +#1020=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#1021=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1022=VECTOR('',#1021,1.00000000000000000E0); +#1023=LINE('',#1020,#1022); +#1024=CARTESIAN_POINT('',(5.00000000000000000E-1,0.00000000000000000E0)); +#1025=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1026=VECTOR('',#1025,1.00000000000000000E0); +#1027=LINE('',#1024,#1026); +#1028=DEFINITIONAL_REPRESENTATION('',(#1027),#1); +#1029=PCURVE('',#96,#1028); +#1030=CARTESIAN_POINT('',(5.00000000000000000E-1,3.00000000000000000E0)); +#1031=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1032=VECTOR('',#1031,1.00000000000000000E0); +#1033=LINE('',#1030,#1032); +#1034=DEFINITIONAL_REPRESENTATION('',(#1033),#1); +#1035=PCURVE('',#106,#1034); +#1036=SURFACE_CURVE('',#1023,(#1029,#1035),.CURVE_3D.); +#1037=EDGE_CURVE('',#249,#259,#1036,.T.); +#1038=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#1039=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1040=VECTOR('',#1039,1.00000000000000000E0); +#1041=LINE('',#1038,#1040); +#1042=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#1043=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1044=VECTOR('',#1043,1.00000000000000000E0); +#1045=LINE('',#1042,#1044); +#1046=DEFINITIONAL_REPRESENTATION('',(#1045),#1); +#1047=PCURVE('',#96,#1046); +#1048=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#1049=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1050=VECTOR('',#1049,1.00000000000000000E0); +#1051=LINE('',#1048,#1050); +#1052=DEFINITIONAL_REPRESENTATION('',(#1051),#1); +#1053=PCURVE('',#101,#1052); +#1054=SURFACE_CURVE('',#1041,(#1047,#1053),.CURVE_3D.); +#1055=EDGE_CURVE('',#259,#261,#1054,.T.); +#1056=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#1057=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1058=VECTOR('',#1057,1.00000000000000000E0); +#1059=LINE('',#1056,#1058); +#1060=CARTESIAN_POINT('',(5.00000000000000000E-1,5.00000000000000000E-1)); +#1061=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1062=VECTOR('',#1061,1.00000000000000000E0); +#1063=LINE('',#1060,#1062); +#1064=DEFINITIONAL_REPRESENTATION('',(#1063),#1); +#1065=PCURVE('',#96,#1064); +#1066=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#1067=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1068=VECTOR('',#1067,1.00000000000000000E0); +#1069=LINE('',#1066,#1068); +#1070=DEFINITIONAL_REPRESENTATION('',(#1069),#1); +#1071=PCURVE('',#166,#1070); +#1072=SURFACE_CURVE('',#1059,(#1065,#1071),.CURVE_3D.); +#1073=EDGE_CURVE('',#251,#261,#1072,.T.); +#1074=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#1075=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1076=VECTOR('',#1075,1.00000000000000000E0); +#1077=LINE('',#1074,#1076); +#1078=CARTESIAN_POINT('',(5.00000000000000000E-1,1.50000000000000000E0)); +#1079=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1080=VECTOR('',#1079,1.00000000000000000E0); +#1081=LINE('',#1078,#1080); +#1082=DEFINITIONAL_REPRESENTATION('',(#1081),#1); +#1083=PCURVE('',#101,#1082); +#1084=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#1085=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1086=VECTOR('',#1085,1.00000000000000000E0); +#1087=LINE('',#1084,#1086); +#1088=DEFINITIONAL_REPRESENTATION('',(#1087),#1); +#1089=PCURVE('',#136,#1088); +#1090=SURFACE_CURVE('',#1077,(#1083,#1089),.CURVE_3D.); +#1091=EDGE_CURVE('',#239,#261,#1090,.T.); +#1092=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0,-3.00000000000000000E0)); +#1093=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1094=VECTOR('',#1093,1.00000000000000000E0); +#1095=LINE('',#1092,#1094); +#1096=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#1097=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1098=VECTOR('',#1097,1.00000000000000000E0); +#1099=LINE('',#1096,#1098); +#1100=DEFINITIONAL_REPRESENTATION('',(#1099),#1); +#1101=PCURVE('',#101,#1100); +#1102=CARTESIAN_POINT('',(3.00000000000000000E0,1.50000000000000000E0)); +#1103=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1104=VECTOR('',#1103,1.00000000000000000E0); +#1105=LINE('',#1102,#1104); +#1106=DEFINITIONAL_REPRESENTATION('',(#1105),#1); +#1107=PCURVE('',#106,#1106); +#1108=SURFACE_CURVE('',#1095,(#1101,#1107),.CURVE_3D.); +#1109=EDGE_CURVE('',#241,#259,#1108,.T.); +#1110=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#1111=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1112=VECTOR('',#1111,1.00000000000000000E0); +#1113=LINE('',#1110,#1112); +#1114=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#1115=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1116=VECTOR('',#1115,1.00000000000000000E0); +#1117=LINE('',#1114,#1116); +#1118=DEFINITIONAL_REPRESENTATION('',(#1117),#1); +#1119=PCURVE('',#111,#1118); +#1120=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1121=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1122=VECTOR('',#1121,1.00000000000000000E0); +#1123=LINE('',#1120,#1122); +#1124=DEFINITIONAL_REPRESENTATION('',(#1123),#1); +#1125=PCURVE('',#121,#1124); +#1126=SURFACE_CURVE('',#1113,(#1119,#1125),.CURVE_3D.); +#1127=EDGE_CURVE('',#263,#265,#1126,.T.); +#1128=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#1129=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1130=VECTOR('',#1129,1.00000000000000000E0); +#1131=LINE('',#1128,#1130); +#1132=CARTESIAN_POINT('',(2.50000000000000000E0,2.50000000000000000E0)); +#1133=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1134=VECTOR('',#1133,1.00000000000000000E0); +#1135=LINE('',#1132,#1134); +#1136=DEFINITIONAL_REPRESENTATION('',(#1135),#1); +#1137=PCURVE('',#111,#1136); +#1138=CARTESIAN_POINT('',(2.50000000000000000E0,2.50000000000000000E0)); +#1139=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1140=VECTOR('',#1139,1.00000000000000000E0); +#1141=LINE('',#1138,#1140); +#1142=DEFINITIONAL_REPRESENTATION('',(#1141),#1); +#1143=PCURVE('',#141,#1142); +#1144=SURFACE_CURVE('',#1131,(#1137,#1143),.CURVE_3D.); +#1145=EDGE_CURVE('',#245,#265,#1144,.T.); +#1146=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#1147=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1148=VECTOR('',#1147,1.00000000000000000E0); +#1149=LINE('',#1146,#1148); +#1150=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#1151=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1152=VECTOR('',#1151,1.00000000000000000E0); +#1153=LINE('',#1150,#1152); +#1154=DEFINITIONAL_REPRESENTATION('',(#1153),#1); +#1155=PCURVE('',#111,#1154); +#1156=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#1157=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1158=VECTOR('',#1157,1.00000000000000000E0); +#1159=LINE('',#1156,#1158); +#1160=DEFINITIONAL_REPRESENTATION('',(#1159),#1); +#1161=PCURVE('',#116,#1160); +#1162=SURFACE_CURVE('',#1149,(#1155,#1161),.CURVE_3D.); +#1163=EDGE_CURVE('',#233,#263,#1162,.T.); +#1164=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#1165=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1166=VECTOR('',#1165,1.00000000000000000E0); +#1167=LINE('',#1164,#1166); +#1168=CARTESIAN_POINT('',(1.50000000000000000E0,2.50000000000000000E0)); +#1169=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1170=VECTOR('',#1169,1.00000000000000000E0); +#1171=LINE('',#1168,#1170); +#1172=DEFINITIONAL_REPRESENTATION('',(#1171),#1); +#1173=PCURVE('',#116,#1172); +#1174=CARTESIAN_POINT('',(1.50000000000000000E0,2.50000000000000000E0)); +#1175=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1176=VECTOR('',#1175,1.00000000000000000E0); +#1177=LINE('',#1174,#1176); +#1178=DEFINITIONAL_REPRESENTATION('',(#1177),#1); +#1179=PCURVE('',#176,#1178); +#1180=SURFACE_CURVE('',#1167,(#1173,#1179),.CURVE_3D.); +#1181=EDGE_CURVE('',#255,#267,#1180,.T.); +#1182=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#1183=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1184=VECTOR('',#1183,1.00000000000000000E0); +#1185=LINE('',#1182,#1184); +#1186=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#1187=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1188=VECTOR('',#1187,1.00000000000000000E0); +#1189=LINE('',#1186,#1188); +#1190=DEFINITIONAL_REPRESENTATION('',(#1189),#1); +#1191=PCURVE('',#116,#1190); +#1192=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1193=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1194=VECTOR('',#1193,1.00000000000000000E0); +#1195=LINE('',#1192,#1194); +#1196=DEFINITIONAL_REPRESENTATION('',(#1195),#1); +#1197=PCURVE('',#121,#1196); +#1198=SURFACE_CURVE('',#1185,(#1191,#1197),.CURVE_3D.); +#1199=EDGE_CURVE('',#263,#267,#1198,.T.); +#1200=CARTESIAN_POINT('',(3.00000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#1201=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1202=VECTOR('',#1201,1.00000000000000000E0); +#1203=LINE('',#1200,#1202); +#1204=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#1205=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1206=VECTOR('',#1205,1.00000000000000000E0); +#1207=LINE('',#1204,#1206); +#1208=DEFINITIONAL_REPRESENTATION('',(#1207),#1); +#1209=PCURVE('',#121,#1208); +#1210=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#1211=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1212=VECTOR('',#1211,1.00000000000000000E0); +#1213=LINE('',#1210,#1212); +#1214=DEFINITIONAL_REPRESENTATION('',(#1213),#1); +#1215=PCURVE('',#181,#1214); +#1216=SURFACE_CURVE('',#1203,(#1209,#1215),.CURVE_3D.); +#1217=EDGE_CURVE('',#267,#269,#1216,.T.); +#1218=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-3.00000000000000000E0)); +#1219=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1220=VECTOR('',#1219,1.00000000000000000E0); +#1221=LINE('',#1218,#1220); +#1222=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#1223=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1224=VECTOR('',#1223,1.00000000000000000E0); +#1225=LINE('',#1222,#1224); +#1226=DEFINITIONAL_REPRESENTATION('',(#1225),#1); +#1227=PCURVE('',#121,#1226); +#1228=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#1229=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1230=VECTOR('',#1229,1.00000000000000000E0); +#1231=LINE('',#1228,#1230); +#1232=DEFINITIONAL_REPRESENTATION('',(#1231),#1); +#1233=PCURVE('',#151,#1232); +#1234=SURFACE_CURVE('',#1221,(#1227,#1233),.CURVE_3D.); +#1235=EDGE_CURVE('',#265,#269,#1234,.T.); +#1236=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#1237=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1238=VECTOR('',#1237,1.00000000000000000E0); +#1239=LINE('',#1236,#1238); +#1240=CARTESIAN_POINT('',(2.50000000000000000E0,2.50000000000000000E0)); +#1241=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1242=VECTOR('',#1241,1.00000000000000000E0); +#1243=LINE('',#1240,#1242); +#1244=DEFINITIONAL_REPRESENTATION('',(#1243),#1); +#1245=PCURVE('',#126,#1244); +#1246=CARTESIAN_POINT('',(2.50000000000000000E0,2.50000000000000000E0)); +#1247=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1248=VECTOR('',#1247,1.00000000000000000E0); +#1249=LINE('',#1246,#1248); +#1250=DEFINITIONAL_REPRESENTATION('',(#1249),#1); +#1251=PCURVE('',#141,#1250); +#1252=SURFACE_CURVE('',#1239,(#1245,#1251),.CURVE_3D.); +#1253=EDGE_CURVE('',#245,#271,#1252,.T.); +#1254=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#1255=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1256=VECTOR('',#1255,1.00000000000000000E0); +#1257=LINE('',#1254,#1256); +#1258=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#1259=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1260=VECTOR('',#1259,1.00000000000000000E0); +#1261=LINE('',#1258,#1260); +#1262=DEFINITIONAL_REPRESENTATION('',(#1261),#1); +#1263=PCURVE('',#126,#1262); +#1264=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1265=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1266=VECTOR('',#1265,1.00000000000000000E0); +#1267=LINE('',#1264,#1266); +#1268=DEFINITIONAL_REPRESENTATION('',(#1267),#1); +#1269=PCURVE('',#131,#1268); +#1270=SURFACE_CURVE('',#1257,(#1263,#1269),.CURVE_3D.); +#1271=EDGE_CURVE('',#273,#271,#1270,.T.); +#1272=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#1273=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1274=VECTOR('',#1273,1.00000000000000000E0); +#1275=LINE('',#1272,#1274); +#1276=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#1277=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1278=VECTOR('',#1277,1.00000000000000000E0); +#1279=LINE('',#1276,#1278); +#1280=DEFINITIONAL_REPRESENTATION('',(#1279),#1); +#1281=PCURVE('',#126,#1280); +#1282=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#1283=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1284=VECTOR('',#1283,1.00000000000000000E0); +#1285=LINE('',#1282,#1284); +#1286=DEFINITIONAL_REPRESENTATION('',(#1285),#1); +#1287=PCURVE('',#136,#1286); +#1288=SURFACE_CURVE('',#1275,(#1281,#1287),.CURVE_3D.); +#1289=EDGE_CURVE('',#239,#273,#1288,.T.); +#1290=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-3.50000000000000000E0)); +#1291=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1292=VECTOR('',#1291,1.00000000000000000E0); +#1293=LINE('',#1290,#1292); +#1294=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#1295=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1296=VECTOR('',#1295,1.00000000000000000E0); +#1297=LINE('',#1294,#1296); +#1298=DEFINITIONAL_REPRESENTATION('',(#1297),#1); +#1299=PCURVE('',#131,#1298); +#1300=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#1301=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1302=VECTOR('',#1301,1.00000000000000000E0); +#1303=LINE('',#1300,#1302); +#1304=DEFINITIONAL_REPRESENTATION('',(#1303),#1); +#1305=PCURVE('',#146,#1304); +#1306=SURFACE_CURVE('',#1293,(#1299,#1305),.CURVE_3D.); +#1307=EDGE_CURVE('',#271,#275,#1306,.T.); +#1308=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#1309=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1310=VECTOR('',#1309,1.00000000000000000E0); +#1311=LINE('',#1308,#1310); +#1312=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#1313=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1314=VECTOR('',#1313,1.00000000000000000E0); +#1315=LINE('',#1312,#1314); +#1316=DEFINITIONAL_REPRESENTATION('',(#1315),#1); +#1317=PCURVE('',#131,#1316); +#1318=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#1319=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1320=VECTOR('',#1319,1.00000000000000000E0); +#1321=LINE('',#1318,#1320); +#1322=DEFINITIONAL_REPRESENTATION('',(#1321),#1); +#1323=PCURVE('',#191,#1322); +#1324=SURFACE_CURVE('',#1311,(#1317,#1323),.CURVE_3D.); +#1325=EDGE_CURVE('',#277,#275,#1324,.T.); +#1326=CARTESIAN_POINT('',(1.50000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#1327=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1328=VECTOR('',#1327,1.00000000000000000E0); +#1329=LINE('',#1326,#1328); +#1330=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1331=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1332=VECTOR('',#1331,1.00000000000000000E0); +#1333=LINE('',#1330,#1332); +#1334=DEFINITIONAL_REPRESENTATION('',(#1333),#1); +#1335=PCURVE('',#131,#1334); +#1336=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#1337=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1338=VECTOR('',#1337,1.00000000000000000E0); +#1339=LINE('',#1336,#1338); +#1340=DEFINITIONAL_REPRESENTATION('',(#1339),#1); +#1341=PCURVE('',#136,#1340); +#1342=SURFACE_CURVE('',#1329,(#1335,#1341),.CURVE_3D.); +#1343=EDGE_CURVE('',#273,#277,#1342,.T.); +#1344=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#1345=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1346=VECTOR('',#1345,1.00000000000000000E0); +#1347=LINE('',#1344,#1346); +#1348=CARTESIAN_POINT('',(2.50000000000000000E0,1.50000000000000000E0)); +#1349=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1350=VECTOR('',#1349,1.00000000000000000E0); +#1351=LINE('',#1348,#1350); +#1352=DEFINITIONAL_REPRESENTATION('',(#1351),#1); +#1353=PCURVE('',#136,#1352); +#1354=CARTESIAN_POINT('',(2.50000000000000000E0,1.50000000000000000E0)); +#1355=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1356=VECTOR('',#1355,1.00000000000000000E0); +#1357=LINE('',#1354,#1356); +#1358=DEFINITIONAL_REPRESENTATION('',(#1357),#1); +#1359=PCURVE('',#196,#1358); +#1360=SURFACE_CURVE('',#1347,(#1353,#1359),.CURVE_3D.); +#1361=EDGE_CURVE('',#261,#277,#1360,.T.); +#1362=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-3.00000000000000000E0)); +#1363=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1364=VECTOR('',#1363,1.00000000000000000E0); +#1365=LINE('',#1362,#1364); +#1366=CARTESIAN_POINT('',(3.00000000000000000E0,2.50000000000000000E0)); +#1367=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1368=VECTOR('',#1367,1.00000000000000000E0); +#1369=LINE('',#1366,#1368); +#1370=DEFINITIONAL_REPRESENTATION('',(#1369),#1); +#1371=PCURVE('',#141,#1370); +#1372=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#1373=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1374=VECTOR('',#1373,1.00000000000000000E0); +#1375=LINE('',#1372,#1374); +#1376=DEFINITIONAL_REPRESENTATION('',(#1375),#1); +#1377=PCURVE('',#151,#1376); +#1378=SURFACE_CURVE('',#1365,(#1371,#1377),.CURVE_3D.); +#1379=EDGE_CURVE('',#265,#279,#1378,.T.); +#1380=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0,-3.50000000000000000E0)); +#1381=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1382=VECTOR('',#1381,1.00000000000000000E0); +#1383=LINE('',#1380,#1382); +#1384=CARTESIAN_POINT('',(2.50000000000000000E0,3.00000000000000000E0)); +#1385=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1386=VECTOR('',#1385,1.00000000000000000E0); +#1387=LINE('',#1384,#1386); +#1388=DEFINITIONAL_REPRESENTATION('',(#1387),#1); +#1389=PCURVE('',#141,#1388); +#1390=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#1391=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1392=VECTOR('',#1391,1.00000000000000000E0); +#1393=LINE('',#1390,#1392); +#1394=DEFINITIONAL_REPRESENTATION('',(#1393),#1); +#1395=PCURVE('',#146,#1394); +#1396=SURFACE_CURVE('',#1383,(#1389,#1395),.CURVE_3D.); +#1397=EDGE_CURVE('',#271,#279,#1396,.T.); +#1398=CARTESIAN_POINT('',(1.50000000000000000E0,3.50000000000000000E0,-3.50000000000000000E0)); +#1399=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1400=VECTOR('',#1399,1.00000000000000000E0); +#1401=LINE('',#1398,#1400); +#1402=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#1403=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1404=VECTOR('',#1403,1.00000000000000000E0); +#1405=LINE('',#1402,#1404); +#1406=DEFINITIONAL_REPRESENTATION('',(#1405),#1); +#1407=PCURVE('',#146,#1406); +#1408=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#1409=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1410=VECTOR('',#1409,1.00000000000000000E0); +#1411=LINE('',#1408,#1410); +#1412=DEFINITIONAL_REPRESENTATION('',(#1411),#1); +#1413=PCURVE('',#151,#1412); +#1414=SURFACE_CURVE('',#1401,(#1407,#1413),.CURVE_3D.); +#1415=EDGE_CURVE('',#279,#281,#1414,.T.); +#1416=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-3.50000000000000000E0)); +#1417=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1418=VECTOR('',#1417,1.00000000000000000E0); +#1419=LINE('',#1416,#1418); +#1420=CARTESIAN_POINT('',(2.50000000000000000E0,1.50000000000000000E0)); +#1421=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1422=VECTOR('',#1421,1.00000000000000000E0); +#1423=LINE('',#1420,#1422); +#1424=DEFINITIONAL_REPRESENTATION('',(#1423),#1); +#1425=PCURVE('',#146,#1424); +#1426=CARTESIAN_POINT('',(2.50000000000000000E0,1.50000000000000000E0)); +#1427=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1428=VECTOR('',#1427,1.00000000000000000E0); +#1429=LINE('',#1426,#1428); +#1430=DEFINITIONAL_REPRESENTATION('',(#1429),#1); +#1431=PCURVE('',#206,#1430); +#1432=SURFACE_CURVE('',#1419,(#1425,#1431),.CURVE_3D.); +#1433=EDGE_CURVE('',#275,#281,#1432,.T.); +#1434=CARTESIAN_POINT('',(3.00000000000000000E0,3.50000000000000000E0,-3.00000000000000000E0)); +#1435=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1436=VECTOR('',#1435,1.00000000000000000E0); +#1437=LINE('',#1434,#1436); +#1438=CARTESIAN_POINT('',(1.50000000000000000E0,2.50000000000000000E0)); +#1439=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1440=VECTOR('',#1439,1.00000000000000000E0); +#1441=LINE('',#1438,#1440); +#1442=DEFINITIONAL_REPRESENTATION('',(#1441),#1); +#1443=PCURVE('',#151,#1442); +#1444=CARTESIAN_POINT('',(1.50000000000000000E0,2.50000000000000000E0)); +#1445=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1446=VECTOR('',#1445,1.00000000000000000E0); +#1447=LINE('',#1444,#1446); +#1448=DEFINITIONAL_REPRESENTATION('',(#1447),#1); +#1449=PCURVE('',#211,#1448); +#1450=SURFACE_CURVE('',#1437,(#1443,#1449),.CURVE_3D.); +#1451=EDGE_CURVE('',#269,#281,#1450,.T.); +#1452=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#1453=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1454=VECTOR('',#1453,1.00000000000000000E0); +#1455=LINE('',#1452,#1454); +#1456=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1457=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1458=VECTOR('',#1457,1.00000000000000000E0); +#1459=LINE('',#1456,#1458); +#1460=DEFINITIONAL_REPRESENTATION('',(#1459),#1); +#1461=PCURVE('',#156,#1460); +#1462=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#1463=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1464=VECTOR('',#1463,1.00000000000000000E0); +#1465=LINE('',#1462,#1464); +#1466=DEFINITIONAL_REPRESENTATION('',(#1465),#1); +#1467=PCURVE('',#166,#1466); +#1468=SURFACE_CURVE('',#1455,(#1461,#1467),.CURVE_3D.); +#1469=EDGE_CURVE('',#283,#285,#1468,.T.); +#1470=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#1471=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1472=VECTOR('',#1471,1.00000000000000000E0); +#1473=LINE('',#1470,#1472); +#1474=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#1475=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1476=VECTOR('',#1475,1.00000000000000000E0); +#1477=LINE('',#1474,#1476); +#1478=DEFINITIONAL_REPRESENTATION('',(#1477),#1); +#1479=PCURVE('',#156,#1478); +#1480=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#1481=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1482=VECTOR('',#1481,1.00000000000000000E0); +#1483=LINE('',#1480,#1482); +#1484=DEFINITIONAL_REPRESENTATION('',(#1483),#1); +#1485=PCURVE('',#186,#1484); +#1486=SURFACE_CURVE('',#1473,(#1479,#1485),.CURVE_3D.); +#1487=EDGE_CURVE('',#285,#287,#1486,.T.); +#1488=CARTESIAN_POINT('',(4.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#1489=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1490=VECTOR('',#1489,1.00000000000000000E0); +#1491=LINE('',#1488,#1490); +#1492=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#1493=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1494=VECTOR('',#1493,1.00000000000000000E0); +#1495=LINE('',#1492,#1494); +#1496=DEFINITIONAL_REPRESENTATION('',(#1495),#1); +#1497=PCURVE('',#156,#1496); +#1498=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#1499=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1500=VECTOR('',#1499,1.00000000000000000E0); +#1501=LINE('',#1498,#1500); +#1502=DEFINITIONAL_REPRESENTATION('',(#1501),#1); +#1503=PCURVE('',#171,#1502); +#1504=SURFACE_CURVE('',#1491,(#1497,#1503),.CURVE_3D.); +#1505=EDGE_CURVE('',#289,#287,#1504,.T.); +#1506=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#1507=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1508=VECTOR('',#1507,1.00000000000000000E0); +#1509=LINE('',#1506,#1508); +#1510=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1511=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1512=VECTOR('',#1511,1.00000000000000000E0); +#1513=LINE('',#1510,#1512); +#1514=DEFINITIONAL_REPRESENTATION('',(#1513),#1); +#1515=PCURVE('',#156,#1514); +#1516=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#1517=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1518=VECTOR('',#1517,1.00000000000000000E0); +#1519=LINE('',#1516,#1518); +#1520=DEFINITIONAL_REPRESENTATION('',(#1519),#1); +#1521=PCURVE('',#161,#1520); +#1522=SURFACE_CURVE('',#1509,(#1515,#1521),.CURVE_3D.); +#1523=EDGE_CURVE('',#283,#289,#1522,.T.); +#1524=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#1525=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1526=VECTOR('',#1525,1.00000000000000000E0); +#1527=LINE('',#1524,#1526); +#1528=CARTESIAN_POINT('',(1.50000000000000000E0,2.50000000000000000E0)); +#1529=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1530=VECTOR('',#1529,1.00000000000000000E0); +#1531=LINE('',#1528,#1530); +#1532=DEFINITIONAL_REPRESENTATION('',(#1531),#1); +#1533=PCURVE('',#161,#1532); +#1534=CARTESIAN_POINT('',(1.50000000000000000E0,2.50000000000000000E0)); +#1535=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1536=VECTOR('',#1535,1.00000000000000000E0); +#1537=LINE('',#1534,#1536); +#1538=DEFINITIONAL_REPRESENTATION('',(#1537),#1); +#1539=PCURVE('',#176,#1538); +#1540=SURFACE_CURVE('',#1527,(#1533,#1539),.CURVE_3D.); +#1541=EDGE_CURVE('',#255,#289,#1540,.T.); +#1542=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-5.00000000000000000E-1)); +#1543=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1544=VECTOR('',#1543,1.00000000000000000E0); +#1545=LINE('',#1542,#1544); +#1546=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#1547=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1548=VECTOR('',#1547,1.00000000000000000E0); +#1549=LINE('',#1546,#1548); +#1550=DEFINITIONAL_REPRESENTATION('',(#1549),#1); +#1551=PCURVE('',#161,#1550); +#1552=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#1553=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1554=VECTOR('',#1553,1.00000000000000000E0); +#1555=LINE('',#1552,#1554); +#1556=DEFINITIONAL_REPRESENTATION('',(#1555),#1); +#1557=PCURVE('',#166,#1556); +#1558=SURFACE_CURVE('',#1545,(#1551,#1557),.CURVE_3D.); +#1559=EDGE_CURVE('',#251,#283,#1558,.T.); +#1560=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#1561=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1562=VECTOR('',#1561,1.00000000000000000E0); +#1563=LINE('',#1560,#1562); +#1564=CARTESIAN_POINT('',(2.50000000000000000E0,1.50000000000000000E0)); +#1565=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1566=VECTOR('',#1565,1.00000000000000000E0); +#1567=LINE('',#1564,#1566); +#1568=DEFINITIONAL_REPRESENTATION('',(#1567),#1); +#1569=PCURVE('',#166,#1568); +#1570=CARTESIAN_POINT('',(2.50000000000000000E0,1.50000000000000000E0)); +#1571=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1572=VECTOR('',#1571,1.00000000000000000E0); +#1573=LINE('',#1570,#1572); +#1574=DEFINITIONAL_REPRESENTATION('',(#1573),#1); +#1575=PCURVE('',#196,#1574); +#1576=SURFACE_CURVE('',#1563,(#1569,#1575),.CURVE_3D.); +#1577=EDGE_CURVE('',#261,#285,#1576,.T.); +#1578=CARTESIAN_POINT('',(4.50000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#1579=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1580=VECTOR('',#1579,1.00000000000000000E0); +#1581=LINE('',#1578,#1580); +#1582=CARTESIAN_POINT('',(2.50000000000000000E0,2.50000000000000000E0)); +#1583=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1584=VECTOR('',#1583,1.00000000000000000E0); +#1585=LINE('',#1582,#1584); +#1586=DEFINITIONAL_REPRESENTATION('',(#1585),#1); +#1587=PCURVE('',#171,#1586); +#1588=CARTESIAN_POINT('',(2.50000000000000000E0,2.50000000000000000E0)); +#1589=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1590=VECTOR('',#1589,1.00000000000000000E0); +#1591=LINE('',#1588,#1590); +#1592=DEFINITIONAL_REPRESENTATION('',(#1591),#1); +#1593=PCURVE('',#201,#1592); +#1594=SURFACE_CURVE('',#1581,(#1587,#1593),.CURVE_3D.); +#1595=EDGE_CURVE('',#287,#291,#1594,.T.); +#1596=CARTESIAN_POINT('',(4.50000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#1597=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1598=VECTOR('',#1597,1.00000000000000000E0); +#1599=LINE('',#1596,#1598); +#1600=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#1601=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1602=VECTOR('',#1601,1.00000000000000000E0); +#1603=LINE('',#1600,#1602); +#1604=DEFINITIONAL_REPRESENTATION('',(#1603),#1); +#1605=PCURVE('',#171,#1604); +#1606=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#1607=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1608=VECTOR('',#1607,1.00000000000000000E0); +#1609=LINE('',#1606,#1608); +#1610=DEFINITIONAL_REPRESENTATION('',(#1609),#1); +#1611=PCURVE('',#181,#1610); +#1612=SURFACE_CURVE('',#1599,(#1605,#1611),.CURVE_3D.); +#1613=EDGE_CURVE('',#293,#291,#1612,.T.); +#1614=CARTESIAN_POINT('',(4.50000000000000000E0,3.00000000000000000E0,-5.00000000000000000E-1)); +#1615=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1616=VECTOR('',#1615,1.00000000000000000E0); +#1617=LINE('',#1614,#1616); +#1618=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E0)); +#1619=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1620=VECTOR('',#1619,1.00000000000000000E0); +#1621=LINE('',#1618,#1620); +#1622=DEFINITIONAL_REPRESENTATION('',(#1621),#1); +#1623=PCURVE('',#171,#1622); +#1624=CARTESIAN_POINT('',(3.00000000000000000E0,2.50000000000000000E0)); +#1625=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1626=VECTOR('',#1625,1.00000000000000000E0); +#1627=LINE('',#1624,#1626); +#1628=DEFINITIONAL_REPRESENTATION('',(#1627),#1); +#1629=PCURVE('',#176,#1628); +#1630=SURFACE_CURVE('',#1617,(#1623,#1629),.CURVE_3D.); +#1631=EDGE_CURVE('',#289,#293,#1630,.T.); +#1632=CARTESIAN_POINT('',(3.00000000000000000E0,3.50000000000000000E0,-5.00000000000000000E-1)); +#1633=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1634=VECTOR('',#1633,1.00000000000000000E0); +#1635=LINE('',#1632,#1634); +#1636=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0)); +#1637=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1638=VECTOR('',#1637,1.00000000000000000E0); +#1639=LINE('',#1636,#1638); +#1640=DEFINITIONAL_REPRESENTATION('',(#1639),#1); +#1641=PCURVE('',#176,#1640); +#1642=CARTESIAN_POINT('',(1.50000000000000000E0,0.00000000000000000E0)); +#1643=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1644=VECTOR('',#1643,1.00000000000000000E0); +#1645=LINE('',#1642,#1644); +#1646=DEFINITIONAL_REPRESENTATION('',(#1645),#1); +#1647=PCURVE('',#181,#1646); +#1648=SURFACE_CURVE('',#1635,(#1641,#1647),.CURVE_3D.); +#1649=EDGE_CURVE('',#267,#293,#1648,.T.); +#1650=CARTESIAN_POINT('',(3.00000000000000000E0,3.50000000000000000E0,-3.00000000000000000E0)); +#1651=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1652=VECTOR('',#1651,1.00000000000000000E0); +#1653=LINE('',#1650,#1652); +#1654=CARTESIAN_POINT('',(1.50000000000000000E0,2.50000000000000000E0)); +#1655=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1656=VECTOR('',#1655,1.00000000000000000E0); +#1657=LINE('',#1654,#1656); +#1658=DEFINITIONAL_REPRESENTATION('',(#1657),#1); +#1659=PCURVE('',#181,#1658); +#1660=CARTESIAN_POINT('',(1.50000000000000000E0,2.50000000000000000E0)); +#1661=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1662=VECTOR('',#1661,1.00000000000000000E0); +#1663=LINE('',#1660,#1662); +#1664=DEFINITIONAL_REPRESENTATION('',(#1663),#1); +#1665=PCURVE('',#211,#1664); +#1666=SURFACE_CURVE('',#1653,(#1659,#1665),.CURVE_3D.); +#1667=EDGE_CURVE('',#269,#291,#1666,.T.); +#1668=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-3.00000000000000000E0)); +#1669=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1670=VECTOR('',#1669,1.00000000000000000E0); +#1671=LINE('',#1668,#1670); +#1672=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0)); +#1673=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1674=VECTOR('',#1673,1.00000000000000000E0); +#1675=LINE('',#1672,#1674); +#1676=DEFINITIONAL_REPRESENTATION('',(#1675),#1); +#1677=PCURVE('',#186,#1676); +#1678=CARTESIAN_POINT('',(2.50000000000000000E0,3.00000000000000000E0)); +#1679=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1680=VECTOR('',#1679,1.00000000000000000E0); +#1681=LINE('',#1678,#1680); +#1682=DEFINITIONAL_REPRESENTATION('',(#1681),#1); +#1683=PCURVE('',#196,#1682); +#1684=SURFACE_CURVE('',#1671,(#1677,#1683),.CURVE_3D.); +#1685=EDGE_CURVE('',#285,#295,#1684,.T.); +#1686=CARTESIAN_POINT('',(4.50000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#1687=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1688=VECTOR('',#1687,1.00000000000000000E0); +#1689=LINE('',#1686,#1688); +#1690=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#1691=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1692=VECTOR('',#1691,1.00000000000000000E0); +#1693=LINE('',#1690,#1692); +#1694=DEFINITIONAL_REPRESENTATION('',(#1693),#1); +#1695=PCURVE('',#186,#1694); +#1696=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#1697=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1698=VECTOR('',#1697,1.00000000000000000E0); +#1699=LINE('',#1696,#1698); +#1700=DEFINITIONAL_REPRESENTATION('',(#1699),#1); +#1701=PCURVE('',#191,#1700); +#1702=SURFACE_CURVE('',#1689,(#1695,#1701),.CURVE_3D.); +#1703=EDGE_CURVE('',#295,#297,#1702,.T.); +#1704=CARTESIAN_POINT('',(4.50000000000000000E0,3.00000000000000000E0,-3.00000000000000000E0)); +#1705=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1706=VECTOR('',#1705,1.00000000000000000E0); +#1707=LINE('',#1704,#1706); +#1708=CARTESIAN_POINT('',(2.50000000000000000E0,2.50000000000000000E0)); +#1709=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1710=VECTOR('',#1709,1.00000000000000000E0); +#1711=LINE('',#1708,#1710); +#1712=DEFINITIONAL_REPRESENTATION('',(#1711),#1); +#1713=PCURVE('',#186,#1712); +#1714=CARTESIAN_POINT('',(2.50000000000000000E0,2.50000000000000000E0)); +#1715=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1716=VECTOR('',#1715,1.00000000000000000E0); +#1717=LINE('',#1714,#1716); +#1718=DEFINITIONAL_REPRESENTATION('',(#1717),#1); +#1719=PCURVE('',#201,#1718); +#1720=SURFACE_CURVE('',#1707,(#1713,#1719),.CURVE_3D.); +#1721=EDGE_CURVE('',#287,#297,#1720,.T.); +#1722=CARTESIAN_POINT('',(3.00000000000000000E0,3.00000000000000000E0,-3.50000000000000000E0)); +#1723=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1724=VECTOR('',#1723,1.00000000000000000E0); +#1725=LINE('',#1722,#1724); +#1726=CARTESIAN_POINT('',(2.50000000000000000E0,1.50000000000000000E0)); +#1727=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1728=VECTOR('',#1727,1.00000000000000000E0); +#1729=LINE('',#1726,#1728); +#1730=DEFINITIONAL_REPRESENTATION('',(#1729),#1); +#1731=PCURVE('',#191,#1730); +#1732=CARTESIAN_POINT('',(2.50000000000000000E0,1.50000000000000000E0)); +#1733=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1734=VECTOR('',#1733,1.00000000000000000E0); +#1735=LINE('',#1732,#1734); +#1736=DEFINITIONAL_REPRESENTATION('',(#1735),#1); +#1737=PCURVE('',#206,#1736); +#1738=SURFACE_CURVE('',#1725,(#1731,#1737),.CURVE_3D.); +#1739=EDGE_CURVE('',#275,#297,#1738,.T.); +#1740=CARTESIAN_POINT('',(3.00000000000000000E0,5.00000000000000000E-1,-3.50000000000000000E0)); +#1741=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1742=VECTOR('',#1741,1.00000000000000000E0); +#1743=LINE('',#1740,#1742); +#1744=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E0)); +#1745=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1746=VECTOR('',#1745,1.00000000000000000E0); +#1747=LINE('',#1744,#1746); +#1748=DEFINITIONAL_REPRESENTATION('',(#1747),#1); +#1749=PCURVE('',#191,#1748); +#1750=CARTESIAN_POINT('',(3.00000000000000000E0,1.50000000000000000E0)); +#1751=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1752=VECTOR('',#1751,1.00000000000000000E0); +#1753=LINE('',#1750,#1752); +#1754=DEFINITIONAL_REPRESENTATION('',(#1753),#1); +#1755=PCURVE('',#196,#1754); +#1756=SURFACE_CURVE('',#1743,(#1749,#1755),.CURVE_3D.); +#1757=EDGE_CURVE('',#277,#295,#1756,.T.); +#1758=CARTESIAN_POINT('',(4.50000000000000000E0,3.00000000000000000E0,-3.50000000000000000E0)); +#1759=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1760=VECTOR('',#1759,1.00000000000000000E0); +#1761=LINE('',#1758,#1760); +#1762=CARTESIAN_POINT('',(3.00000000000000000E0,2.50000000000000000E0)); +#1763=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1764=VECTOR('',#1763,1.00000000000000000E0); +#1765=LINE('',#1762,#1764); +#1766=DEFINITIONAL_REPRESENTATION('',(#1765),#1); +#1767=PCURVE('',#201,#1766); +#1768=CARTESIAN_POINT('',(2.50000000000000000E0,3.00000000000000000E0)); +#1769=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1770=VECTOR('',#1769,1.00000000000000000E0); +#1771=LINE('',#1768,#1770); +#1772=DEFINITIONAL_REPRESENTATION('',(#1771),#1); +#1773=PCURVE('',#206,#1772); +#1774=SURFACE_CURVE('',#1761,(#1767,#1773),.CURVE_3D.); +#1775=EDGE_CURVE('',#297,#299,#1774,.T.); +#1776=CARTESIAN_POINT('',(4.50000000000000000E0,3.50000000000000000E0,-3.00000000000000000E0)); +#1777=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1778=VECTOR('',#1777,1.00000000000000000E0); +#1779=LINE('',#1776,#1778); +#1780=CARTESIAN_POINT('',(2.50000000000000000E0,3.00000000000000000E0)); +#1781=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1782=VECTOR('',#1781,1.00000000000000000E0); +#1783=LINE('',#1780,#1782); +#1784=DEFINITIONAL_REPRESENTATION('',(#1783),#1); +#1785=PCURVE('',#201,#1784); +#1786=CARTESIAN_POINT('',(3.00000000000000000E0,2.50000000000000000E0)); +#1787=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1788=VECTOR('',#1787,1.00000000000000000E0); +#1789=LINE('',#1786,#1788); +#1790=DEFINITIONAL_REPRESENTATION('',(#1789),#1); +#1791=PCURVE('',#211,#1790); +#1792=SURFACE_CURVE('',#1779,(#1785,#1791),.CURVE_3D.); +#1793=EDGE_CURVE('',#291,#299,#1792,.T.); +#1794=CARTESIAN_POINT('',(3.00000000000000000E0,3.50000000000000000E0,-3.50000000000000000E0)); +#1795=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1796=VECTOR('',#1795,1.00000000000000000E0); +#1797=LINE('',#1794,#1796); +#1798=CARTESIAN_POINT('',(3.00000000000000000E0,1.50000000000000000E0)); +#1799=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1800=VECTOR('',#1799,1.00000000000000000E0); +#1801=LINE('',#1798,#1800); +#1802=DEFINITIONAL_REPRESENTATION('',(#1801),#1); +#1803=PCURVE('',#206,#1802); +#1804=CARTESIAN_POINT('',(1.50000000000000000E0,3.00000000000000000E0)); +#1805=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1806=VECTOR('',#1805,1.00000000000000000E0); +#1807=LINE('',#1804,#1806); +#1808=DEFINITIONAL_REPRESENTATION('',(#1807),#1); +#1809=PCURVE('',#211,#1808); +#1810=SURFACE_CURVE('',#1797,(#1803,#1809),.CURVE_3D.); +#1811=EDGE_CURVE('',#281,#299,#1810,.T.); +#1812=ORIENTED_EDGE('',*,*,#317,.T.); +#1813=ORIENTED_EDGE('',*,*,#335,.F.); +#1814=ORIENTED_EDGE('',*,*,#353,.F.); +#1815=ORIENTED_EDGE('',*,*,#371,.T.); +#1816=EDGE_LOOP('',(#1812,#1813,#1814,#1815)); +#1817=FACE_OUTER_BOUND('',#1816,.T.); +#1818=ADVANCED_FACE('left-0',(#1817),#6,.T.); +#1819=ORIENTED_EDGE('',*,*,#389,.T.); +#1820=ORIENTED_EDGE('',*,*,#407,.F.); +#1821=ORIENTED_EDGE('',*,*,#371,.F.); +#1822=ORIENTED_EDGE('',*,*,#425,.T.); +#1823=EDGE_LOOP('',(#1819,#1820,#1821,#1822)); +#1824=FACE_OUTER_BOUND('',#1823,.T.); +#1825=ADVANCED_FACE('front-1',(#1824),#11,.T.); +#1826=ORIENTED_EDGE('',*,*,#443,.T.); +#1827=ORIENTED_EDGE('',*,*,#461,.F.); +#1828=ORIENTED_EDGE('',*,*,#425,.F.); +#1829=ORIENTED_EDGE('',*,*,#353,.T.); +#1830=EDGE_LOOP('',(#1826,#1827,#1828,#1829)); +#1831=FACE_OUTER_BOUND('',#1830,.T.); +#1832=ADVANCED_FACE('bottom-2',(#1831),#16,.T.); +#1833=ORIENTED_EDGE('',*,*,#479,.T.); +#1834=ORIENTED_EDGE('',*,*,#497,.F.); +#1835=ORIENTED_EDGE('',*,*,#317,.F.); +#1836=ORIENTED_EDGE('',*,*,#515,.T.); +#1837=EDGE_LOOP('',(#1833,#1834,#1835,#1836)); +#1838=FACE_OUTER_BOUND('',#1837,.T.); +#1839=ADVANCED_FACE('left-3',(#1838),#21,.T.); +#1840=ORIENTED_EDGE('',*,*,#533,.T.); +#1841=ORIENTED_EDGE('',*,*,#551,.F.); +#1842=ORIENTED_EDGE('',*,*,#515,.F.); +#1843=ORIENTED_EDGE('',*,*,#407,.T.); +#1844=EDGE_LOOP('',(#1840,#1841,#1842,#1843)); +#1845=FACE_OUTER_BOUND('',#1844,.T.); +#1846=ADVANCED_FACE('front-4',(#1845),#26,.T.); +#1847=ORIENTED_EDGE('',*,*,#551,.T.); +#1848=ORIENTED_EDGE('',*,*,#569,.T.); +#1849=ORIENTED_EDGE('',*,*,#587,.F.); +#1850=ORIENTED_EDGE('',*,*,#479,.F.); +#1851=EDGE_LOOP('',(#1847,#1848,#1849,#1850)); +#1852=FACE_OUTER_BOUND('',#1851,.T.); +#1853=ADVANCED_FACE('top-5',(#1852),#31,.T.); +#1854=ORIENTED_EDGE('',*,*,#605,.T.); +#1855=ORIENTED_EDGE('',*,*,#623,.F.); +#1856=ORIENTED_EDGE('',*,*,#641,.F.); +#1857=ORIENTED_EDGE('',*,*,#335,.T.); +#1858=EDGE_LOOP('',(#1854,#1855,#1856,#1857)); +#1859=FACE_OUTER_BOUND('',#1858,.T.); +#1860=ADVANCED_FACE('left-6',(#1859),#36,.T.); +#1861=ORIENTED_EDGE('',*,*,#623,.T.); +#1862=ORIENTED_EDGE('',*,*,#659,.T.); +#1863=ORIENTED_EDGE('',*,*,#677,.F.); +#1864=ORIENTED_EDGE('',*,*,#695,.F.); +#1865=EDGE_LOOP('',(#1861,#1862,#1863,#1864)); +#1866=FACE_OUTER_BOUND('',#1865,.T.); +#1867=ADVANCED_FACE('back-7',(#1866),#41,.T.); +#1868=ORIENTED_EDGE('',*,*,#695,.T.); +#1869=ORIENTED_EDGE('',*,*,#713,.F.); +#1870=ORIENTED_EDGE('',*,*,#443,.F.); +#1871=ORIENTED_EDGE('',*,*,#641,.T.); +#1872=EDGE_LOOP('',(#1868,#1869,#1870,#1871)); +#1873=FACE_OUTER_BOUND('',#1872,.T.); +#1874=ADVANCED_FACE('bottom-8',(#1873),#46,.T.); +#1875=ORIENTED_EDGE('',*,*,#731,.T.); +#1876=ORIENTED_EDGE('',*,*,#749,.F.); +#1877=ORIENTED_EDGE('',*,*,#605,.F.); +#1878=ORIENTED_EDGE('',*,*,#497,.T.); +#1879=EDGE_LOOP('',(#1875,#1876,#1877,#1878)); +#1880=FACE_OUTER_BOUND('',#1879,.T.); +#1881=ADVANCED_FACE('left-9',(#1880),#51,.T.); +#1882=ORIENTED_EDGE('',*,*,#749,.T.); +#1883=ORIENTED_EDGE('',*,*,#767,.T.); +#1884=ORIENTED_EDGE('',*,*,#785,.F.); +#1885=ORIENTED_EDGE('',*,*,#659,.F.); +#1886=EDGE_LOOP('',(#1882,#1883,#1884,#1885)); +#1887=FACE_OUTER_BOUND('',#1886,.T.); +#1888=ADVANCED_FACE('back-10',(#1887),#56,.T.); +#1889=ORIENTED_EDGE('',*,*,#587,.T.); +#1890=ORIENTED_EDGE('',*,*,#803,.T.); +#1891=ORIENTED_EDGE('',*,*,#767,.F.); +#1892=ORIENTED_EDGE('',*,*,#731,.F.); +#1893=EDGE_LOOP('',(#1889,#1890,#1891,#1892)); +#1894=FACE_OUTER_BOUND('',#1893,.T.); +#1895=ADVANCED_FACE('top-11',(#1894),#61,.T.); +#1896=ORIENTED_EDGE('',*,*,#821,.T.); +#1897=ORIENTED_EDGE('',*,*,#839,.T.); +#1898=ORIENTED_EDGE('',*,*,#857,.F.); +#1899=ORIENTED_EDGE('',*,*,#875,.F.); +#1900=EDGE_LOOP('',(#1896,#1897,#1898,#1899)); +#1901=FACE_OUTER_BOUND('',#1900,.T.); +#1902=ADVANCED_FACE('right-12',(#1901),#66,.T.); +#1903=ORIENTED_EDGE('',*,*,#875,.T.); +#1904=ORIENTED_EDGE('',*,*,#893,.F.); +#1905=ORIENTED_EDGE('',*,*,#389,.F.); +#1906=ORIENTED_EDGE('',*,*,#911,.T.); +#1907=EDGE_LOOP('',(#1903,#1904,#1905,#1906)); +#1908=FACE_OUTER_BOUND('',#1907,.T.); +#1909=ADVANCED_FACE('front-13',(#1908),#71,.T.); +#1910=ORIENTED_EDGE('',*,*,#929,.T.); +#1911=ORIENTED_EDGE('',*,*,#821,.F.); +#1912=ORIENTED_EDGE('',*,*,#911,.F.); +#1913=ORIENTED_EDGE('',*,*,#461,.T.); +#1914=EDGE_LOOP('',(#1910,#1911,#1912,#1913)); +#1915=FACE_OUTER_BOUND('',#1914,.T.); +#1916=ADVANCED_FACE('bottom-14',(#1915),#76,.T.); +#1917=ORIENTED_EDGE('',*,*,#857,.T.); +#1918=ORIENTED_EDGE('',*,*,#947,.T.); +#1919=ORIENTED_EDGE('',*,*,#965,.F.); +#1920=ORIENTED_EDGE('',*,*,#983,.F.); +#1921=EDGE_LOOP('',(#1917,#1918,#1919,#1920)); +#1922=FACE_OUTER_BOUND('',#1921,.T.); +#1923=ADVANCED_FACE('right-15',(#1922),#81,.T.); +#1924=ORIENTED_EDGE('',*,*,#983,.T.); +#1925=ORIENTED_EDGE('',*,*,#1001,.F.); +#1926=ORIENTED_EDGE('',*,*,#533,.F.); +#1927=ORIENTED_EDGE('',*,*,#893,.T.); +#1928=EDGE_LOOP('',(#1924,#1925,#1926,#1927)); +#1929=FACE_OUTER_BOUND('',#1928,.T.); +#1930=ADVANCED_FACE('front-16',(#1929),#86,.T.); +#1931=ORIENTED_EDGE('',*,*,#1001,.T.); +#1932=ORIENTED_EDGE('',*,*,#965,.T.); +#1933=ORIENTED_EDGE('',*,*,#1019,.F.); +#1934=ORIENTED_EDGE('',*,*,#569,.F.); +#1935=EDGE_LOOP('',(#1931,#1932,#1933,#1934)); +#1936=FACE_OUTER_BOUND('',#1935,.T.); +#1937=ADVANCED_FACE('top-17',(#1936),#91,.T.); +#1938=ORIENTED_EDGE('',*,*,#1037,.T.); +#1939=ORIENTED_EDGE('',*,*,#1055,.T.); +#1940=ORIENTED_EDGE('',*,*,#1073,.F.); +#1941=ORIENTED_EDGE('',*,*,#839,.F.); +#1942=EDGE_LOOP('',(#1938,#1939,#1940,#1941)); +#1943=FACE_OUTER_BOUND('',#1942,.T.); +#1944=ADVANCED_FACE('right-18',(#1943),#96,.T.); +#1945=ORIENTED_EDGE('',*,*,#677,.T.); +#1946=ORIENTED_EDGE('',*,*,#1091,.T.); +#1947=ORIENTED_EDGE('',*,*,#1055,.F.); +#1948=ORIENTED_EDGE('',*,*,#1109,.F.); +#1949=EDGE_LOOP('',(#1945,#1946,#1947,#1948)); +#1950=FACE_OUTER_BOUND('',#1949,.T.); +#1951=ADVANCED_FACE('back-19',(#1950),#101,.T.); +#1952=ORIENTED_EDGE('',*,*,#1109,.T.); +#1953=ORIENTED_EDGE('',*,*,#1037,.F.); +#1954=ORIENTED_EDGE('',*,*,#929,.F.); +#1955=ORIENTED_EDGE('',*,*,#713,.T.); +#1956=EDGE_LOOP('',(#1952,#1953,#1954,#1955)); +#1957=FACE_OUTER_BOUND('',#1956,.T.); +#1958=ADVANCED_FACE('bottom-20',(#1957),#106,.T.); +#1959=ORIENTED_EDGE('',*,*,#1127,.T.); +#1960=ORIENTED_EDGE('',*,*,#1145,.F.); +#1961=ORIENTED_EDGE('',*,*,#803,.F.); +#1962=ORIENTED_EDGE('',*,*,#1163,.T.); +#1963=EDGE_LOOP('',(#1959,#1960,#1961,#1962)); +#1964=FACE_OUTER_BOUND('',#1963,.T.); +#1965=ADVANCED_FACE('left-21',(#1964),#111,.T.); +#1966=ORIENTED_EDGE('',*,*,#1181,.T.); +#1967=ORIENTED_EDGE('',*,*,#1199,.F.); +#1968=ORIENTED_EDGE('',*,*,#1163,.F.); +#1969=ORIENTED_EDGE('',*,*,#1019,.T.); +#1970=EDGE_LOOP('',(#1966,#1967,#1968,#1969)); +#1971=FACE_OUTER_BOUND('',#1970,.T.); +#1972=ADVANCED_FACE('front-22',(#1971),#116,.T.); +#1973=ORIENTED_EDGE('',*,*,#1199,.T.); +#1974=ORIENTED_EDGE('',*,*,#1217,.T.); +#1975=ORIENTED_EDGE('',*,*,#1235,.F.); +#1976=ORIENTED_EDGE('',*,*,#1127,.F.); +#1977=EDGE_LOOP('',(#1973,#1974,#1975,#1976)); +#1978=FACE_OUTER_BOUND('',#1977,.T.); +#1979=ADVANCED_FACE('top-23',(#1978),#121,.T.); +#1980=ORIENTED_EDGE('',*,*,#1253,.T.); +#1981=ORIENTED_EDGE('',*,*,#1271,.F.); +#1982=ORIENTED_EDGE('',*,*,#1289,.F.); +#1983=ORIENTED_EDGE('',*,*,#785,.T.); +#1984=EDGE_LOOP('',(#1980,#1981,#1982,#1983)); +#1985=FACE_OUTER_BOUND('',#1984,.T.); +#1986=ADVANCED_FACE('left-24',(#1985),#126,.T.); +#1987=ORIENTED_EDGE('',*,*,#1271,.T.); +#1988=ORIENTED_EDGE('',*,*,#1307,.T.); +#1989=ORIENTED_EDGE('',*,*,#1325,.F.); +#1990=ORIENTED_EDGE('',*,*,#1343,.F.); +#1991=EDGE_LOOP('',(#1987,#1988,#1989,#1990)); +#1992=FACE_OUTER_BOUND('',#1991,.T.); +#1993=ADVANCED_FACE('back-25',(#1992),#131,.T.); +#1994=ORIENTED_EDGE('',*,*,#1343,.T.); +#1995=ORIENTED_EDGE('',*,*,#1361,.F.); +#1996=ORIENTED_EDGE('',*,*,#1091,.F.); +#1997=ORIENTED_EDGE('',*,*,#1289,.T.); +#1998=EDGE_LOOP('',(#1994,#1995,#1996,#1997)); +#1999=FACE_OUTER_BOUND('',#1998,.T.); +#2000=ADVANCED_FACE('bottom-26',(#1999),#136,.T.); +#2001=ORIENTED_EDGE('',*,*,#1379,.T.); +#2002=ORIENTED_EDGE('',*,*,#1397,.F.); +#2003=ORIENTED_EDGE('',*,*,#1253,.F.); +#2004=ORIENTED_EDGE('',*,*,#1145,.T.); +#2005=EDGE_LOOP('',(#2001,#2002,#2003,#2004)); +#2006=FACE_OUTER_BOUND('',#2005,.T.); +#2007=ADVANCED_FACE('left-27',(#2006),#141,.T.); +#2008=ORIENTED_EDGE('',*,*,#1397,.T.); +#2009=ORIENTED_EDGE('',*,*,#1415,.T.); +#2010=ORIENTED_EDGE('',*,*,#1433,.F.); +#2011=ORIENTED_EDGE('',*,*,#1307,.F.); +#2012=EDGE_LOOP('',(#2008,#2009,#2010,#2011)); +#2013=FACE_OUTER_BOUND('',#2012,.T.); +#2014=ADVANCED_FACE('back-28',(#2013),#146,.T.); +#2015=ORIENTED_EDGE('',*,*,#1235,.T.); +#2016=ORIENTED_EDGE('',*,*,#1451,.T.); +#2017=ORIENTED_EDGE('',*,*,#1415,.F.); +#2018=ORIENTED_EDGE('',*,*,#1379,.F.); +#2019=EDGE_LOOP('',(#2015,#2016,#2017,#2018)); +#2020=FACE_OUTER_BOUND('',#2019,.T.); +#2021=ADVANCED_FACE('top-29',(#2020),#151,.T.); +#2022=ORIENTED_EDGE('',*,*,#1469,.T.); +#2023=ORIENTED_EDGE('',*,*,#1487,.T.); +#2024=ORIENTED_EDGE('',*,*,#1505,.F.); +#2025=ORIENTED_EDGE('',*,*,#1523,.F.); +#2026=EDGE_LOOP('',(#2022,#2023,#2024,#2025)); +#2027=FACE_OUTER_BOUND('',#2026,.T.); +#2028=ADVANCED_FACE('right-30',(#2027),#156,.T.); +#2029=ORIENTED_EDGE('',*,*,#1523,.T.); +#2030=ORIENTED_EDGE('',*,*,#1541,.F.); +#2031=ORIENTED_EDGE('',*,*,#947,.F.); +#2032=ORIENTED_EDGE('',*,*,#1559,.T.); +#2033=EDGE_LOOP('',(#2029,#2030,#2031,#2032)); +#2034=FACE_OUTER_BOUND('',#2033,.T.); +#2035=ADVANCED_FACE('front-31',(#2034),#161,.T.); +#2036=ORIENTED_EDGE('',*,*,#1577,.T.); +#2037=ORIENTED_EDGE('',*,*,#1469,.F.); +#2038=ORIENTED_EDGE('',*,*,#1559,.F.); +#2039=ORIENTED_EDGE('',*,*,#1073,.T.); +#2040=EDGE_LOOP('',(#2036,#2037,#2038,#2039)); +#2041=FACE_OUTER_BOUND('',#2040,.T.); +#2042=ADVANCED_FACE('bottom-32',(#2041),#166,.T.); +#2043=ORIENTED_EDGE('',*,*,#1505,.T.); +#2044=ORIENTED_EDGE('',*,*,#1595,.T.); +#2045=ORIENTED_EDGE('',*,*,#1613,.F.); +#2046=ORIENTED_EDGE('',*,*,#1631,.F.); +#2047=EDGE_LOOP('',(#2043,#2044,#2045,#2046)); +#2048=FACE_OUTER_BOUND('',#2047,.T.); +#2049=ADVANCED_FACE('right-33',(#2048),#171,.T.); +#2050=ORIENTED_EDGE('',*,*,#1631,.T.); +#2051=ORIENTED_EDGE('',*,*,#1649,.F.); +#2052=ORIENTED_EDGE('',*,*,#1181,.F.); +#2053=ORIENTED_EDGE('',*,*,#1541,.T.); +#2054=EDGE_LOOP('',(#2050,#2051,#2052,#2053)); +#2055=FACE_OUTER_BOUND('',#2054,.T.); +#2056=ADVANCED_FACE('front-34',(#2055),#176,.T.); +#2057=ORIENTED_EDGE('',*,*,#1649,.T.); +#2058=ORIENTED_EDGE('',*,*,#1613,.T.); +#2059=ORIENTED_EDGE('',*,*,#1667,.F.); +#2060=ORIENTED_EDGE('',*,*,#1217,.F.); +#2061=EDGE_LOOP('',(#2057,#2058,#2059,#2060)); +#2062=FACE_OUTER_BOUND('',#2061,.T.); +#2063=ADVANCED_FACE('top-35',(#2062),#181,.T.); +#2064=ORIENTED_EDGE('',*,*,#1685,.T.); +#2065=ORIENTED_EDGE('',*,*,#1703,.T.); +#2066=ORIENTED_EDGE('',*,*,#1721,.F.); +#2067=ORIENTED_EDGE('',*,*,#1487,.F.); +#2068=EDGE_LOOP('',(#2064,#2065,#2066,#2067)); +#2069=FACE_OUTER_BOUND('',#2068,.T.); +#2070=ADVANCED_FACE('right-36',(#2069),#186,.T.); +#2071=ORIENTED_EDGE('',*,*,#1325,.T.); +#2072=ORIENTED_EDGE('',*,*,#1739,.T.); +#2073=ORIENTED_EDGE('',*,*,#1703,.F.); +#2074=ORIENTED_EDGE('',*,*,#1757,.F.); +#2075=EDGE_LOOP('',(#2071,#2072,#2073,#2074)); +#2076=FACE_OUTER_BOUND('',#2075,.T.); +#2077=ADVANCED_FACE('back-37',(#2076),#191,.T.); +#2078=ORIENTED_EDGE('',*,*,#1757,.T.); +#2079=ORIENTED_EDGE('',*,*,#1685,.F.); +#2080=ORIENTED_EDGE('',*,*,#1577,.F.); +#2081=ORIENTED_EDGE('',*,*,#1361,.T.); +#2082=EDGE_LOOP('',(#2078,#2079,#2080,#2081)); +#2083=FACE_OUTER_BOUND('',#2082,.T.); +#2084=ADVANCED_FACE('bottom-38',(#2083),#196,.T.); +#2085=ORIENTED_EDGE('',*,*,#1721,.T.); +#2086=ORIENTED_EDGE('',*,*,#1775,.T.); +#2087=ORIENTED_EDGE('',*,*,#1793,.F.); +#2088=ORIENTED_EDGE('',*,*,#1595,.F.); +#2089=EDGE_LOOP('',(#2085,#2086,#2087,#2088)); +#2090=FACE_OUTER_BOUND('',#2089,.T.); +#2091=ADVANCED_FACE('right-39',(#2090),#201,.T.); +#2092=ORIENTED_EDGE('',*,*,#1433,.T.); +#2093=ORIENTED_EDGE('',*,*,#1811,.T.); +#2094=ORIENTED_EDGE('',*,*,#1775,.F.); +#2095=ORIENTED_EDGE('',*,*,#1739,.F.); +#2096=EDGE_LOOP('',(#2092,#2093,#2094,#2095)); +#2097=FACE_OUTER_BOUND('',#2096,.T.); +#2098=ADVANCED_FACE('back-40',(#2097),#206,.T.); +#2099=ORIENTED_EDGE('',*,*,#1667,.T.); +#2100=ORIENTED_EDGE('',*,*,#1793,.T.); +#2101=ORIENTED_EDGE('',*,*,#1811,.F.); +#2102=ORIENTED_EDGE('',*,*,#1451,.F.); +#2103=EDGE_LOOP('',(#2099,#2100,#2101,#2102)); +#2104=FACE_OUTER_BOUND('',#2103,.T.); +#2105=ADVANCED_FACE('top-41',(#2104),#211,.T.); +#2106=CLOSED_SHELL('',(#1818,#1839,#1860,#1832,#1825,#1853,#1881,#1846,#1867,#1874,#1916,#1909,#1937,#1895,#1888,#1930,#1951,#1958,#1902,#1923,#1972,#1965,#1986,#2000,#1944,#2035,#2056,#1979,#2007,#1993,#2084,#2042,#2028,#2049,#2063,#2021,#2014,#2077,#2070,#2091,#2105,#2098)); +#2107=MANIFOLD_SOLID_BREP('box-union-0',#2106); +#2108=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#2109=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#2110=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#2111=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000639488463609E-8),#2108,'distance_accuracy_value','exchange error bound'); +#2112=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#2111)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#2108,#2109,#2110)) REPRESENTATION_CONTEXT('','')); +#2113=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#2107),#2112); +#2114=APPLICATION_CONTEXT('automotive design'); +#2115=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#2114); +#2116=PRODUCT_CONTEXT('',#2114,'mechanical'); +#2117=PRODUCT('box-union','box-union','',(#2116)); +#2118=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#2117,.NOT_KNOWN.); +#2119=PRODUCT_DEFINITION_CONTEXT('part definition',#2114,'design'); +#2120=PRODUCT_DEFINITION('','',#2118,#2119); +#2121=PRODUCT_DEFINITION_SHAPE('','',#2120); +#2122=SHAPE_DEFINITION_REPRESENTATION(#2121,#2113); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/box-union.step.m.report.json b/main/opengeometry/tests/fixtures/cases/box-union.step.m.report.json new file mode 100644 index 0000000..77cab9b --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-union.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":84,"exchange_error_bound":1.0000063948846361e-8,"faces":42,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"1","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/box-union.step.mm b/main/opengeometry/tests/fixtures/cases/box-union.step.mm new file mode 100644 index 0000000..e69bfae --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-union.step.mm @@ -0,0 +1,2131 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('box-union','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#24=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#28=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#34=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#35=AXIS2_PLACEMENT_3D('',#32,#33,#34); +#36=PLANE('',#35); +#37=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#38=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#39=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#40=AXIS2_PLACEMENT_3D('',#37,#38,#39); +#41=PLANE('',#40); +#42=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#43=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#44=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#45=AXIS2_PLACEMENT_3D('',#42,#43,#44); +#46=PLANE('',#45); +#47=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#48=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#49=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#50=AXIS2_PLACEMENT_3D('',#47,#48,#49); +#51=PLANE('',#50); +#52=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#53=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#54=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#55=AXIS2_PLACEMENT_3D('',#52,#53,#54); +#56=PLANE('',#55); +#57=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#58=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#59=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#60=AXIS2_PLACEMENT_3D('',#57,#58,#59); +#61=PLANE('',#60); +#62=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#63=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#64=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#65=AXIS2_PLACEMENT_3D('',#62,#63,#64); +#66=PLANE('',#65); +#67=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#68=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#69=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#70=AXIS2_PLACEMENT_3D('',#67,#68,#69); +#71=PLANE('',#70); +#72=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#73=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#74=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#75=AXIS2_PLACEMENT_3D('',#72,#73,#74); +#76=PLANE('',#75); +#77=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#78=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#79=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#80=AXIS2_PLACEMENT_3D('',#77,#78,#79); +#81=PLANE('',#80); +#82=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#83=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#84=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#85=AXIS2_PLACEMENT_3D('',#82,#83,#84); +#86=PLANE('',#85); +#87=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#88=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#90=AXIS2_PLACEMENT_3D('',#87,#88,#89); +#91=PLANE('',#90); +#92=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#93=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#94=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#95=AXIS2_PLACEMENT_3D('',#92,#93,#94); +#96=PLANE('',#95); +#97=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#98=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#99=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#100=AXIS2_PLACEMENT_3D('',#97,#98,#99); +#101=PLANE('',#100); +#102=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#103=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#104=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#105=AXIS2_PLACEMENT_3D('',#102,#103,#104); +#106=PLANE('',#105); +#107=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#108=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#109=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#110=AXIS2_PLACEMENT_3D('',#107,#108,#109); +#111=PLANE('',#110); +#112=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#113=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#114=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#115=AXIS2_PLACEMENT_3D('',#112,#113,#114); +#116=PLANE('',#115); +#117=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#118=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#119=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#120=AXIS2_PLACEMENT_3D('',#117,#118,#119); +#121=PLANE('',#120); +#122=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#123=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#124=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#125=AXIS2_PLACEMENT_3D('',#122,#123,#124); +#126=PLANE('',#125); +#127=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#128=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#129=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#130=AXIS2_PLACEMENT_3D('',#127,#128,#129); +#131=PLANE('',#130); +#132=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#133=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#134=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#135=AXIS2_PLACEMENT_3D('',#132,#133,#134); +#136=PLANE('',#135); +#137=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#138=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#139=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#140=AXIS2_PLACEMENT_3D('',#137,#138,#139); +#141=PLANE('',#140); +#142=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#143=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#144=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#145=AXIS2_PLACEMENT_3D('',#142,#143,#144); +#146=PLANE('',#145); +#147=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#148=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#149=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#150=AXIS2_PLACEMENT_3D('',#147,#148,#149); +#151=PLANE('',#150); +#152=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#153=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#154=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#155=AXIS2_PLACEMENT_3D('',#152,#153,#154); +#156=PLANE('',#155); +#157=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#158=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#159=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#160=AXIS2_PLACEMENT_3D('',#157,#158,#159); +#161=PLANE('',#160); +#162=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#163=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#164=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#165=AXIS2_PLACEMENT_3D('',#162,#163,#164); +#166=PLANE('',#165); +#167=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#168=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#169=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#170=AXIS2_PLACEMENT_3D('',#167,#168,#169); +#171=PLANE('',#170); +#172=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#173=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#174=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#175=AXIS2_PLACEMENT_3D('',#172,#173,#174); +#176=PLANE('',#175); +#177=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#178=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#179=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#180=AXIS2_PLACEMENT_3D('',#177,#178,#179); +#181=PLANE('',#180); +#182=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#183=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#184=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#185=AXIS2_PLACEMENT_3D('',#182,#183,#184); +#186=PLANE('',#185); +#187=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#188=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#189=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#190=AXIS2_PLACEMENT_3D('',#187,#188,#189); +#191=PLANE('',#190); +#192=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#193=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#194=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#195=AXIS2_PLACEMENT_3D('',#192,#193,#194); +#196=PLANE('',#195); +#197=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#198=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#199=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#200=AXIS2_PLACEMENT_3D('',#197,#198,#199); +#201=PLANE('',#200); +#202=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#203=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#204=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#205=AXIS2_PLACEMENT_3D('',#202,#203,#204); +#206=PLANE('',#205); +#207=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#208=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#209=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#210=AXIS2_PLACEMENT_3D('',#207,#208,#209); +#211=PLANE('',#210); +#212=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,0.00000000000000000E0)); +#213=VERTEX_POINT('',#212); +#214=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-5.00000000000000000E2)); +#215=VERTEX_POINT('',#214); +#216=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E2)); +#217=VERTEX_POINT('',#216); +#218=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#219=VERTEX_POINT('',#218); +#220=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#221=VERTEX_POINT('',#220); +#222=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#223=VERTEX_POINT('',#222); +#224=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#225=VERTEX_POINT('',#224); +#226=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#227=VERTEX_POINT('',#226); +#228=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-5.00000000000000000E2)); +#229=VERTEX_POINT('',#228); +#230=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#231=VERTEX_POINT('',#230); +#232=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#233=VERTEX_POINT('',#232); +#234=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-3.00000000000000000E3)); +#235=VERTEX_POINT('',#234); +#236=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#237=VERTEX_POINT('',#236); +#238=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#239=VERTEX_POINT('',#238); +#240=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#241=VERTEX_POINT('',#240); +#242=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-3.00000000000000000E3)); +#243=VERTEX_POINT('',#242); +#244=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#245=VERTEX_POINT('',#244); +#246=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#247=VERTEX_POINT('',#246); +#248=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#249=VERTEX_POINT('',#248); +#250=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#251=VERTEX_POINT('',#250); +#252=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#253=VERTEX_POINT('',#252); +#254=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#255=VERTEX_POINT('',#254); +#256=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#257=VERTEX_POINT('',#256); +#258=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#259=VERTEX_POINT('',#258); +#260=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#261=VERTEX_POINT('',#260); +#262=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#263=VERTEX_POINT('',#262); +#264=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-3.00000000000000000E3)); +#265=VERTEX_POINT('',#264); +#266=CARTESIAN_POINT('',(3.00000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#267=VERTEX_POINT('',#266); +#268=CARTESIAN_POINT('',(3.00000000000000000E3,3.50000000000000000E3,-3.00000000000000000E3)); +#269=VERTEX_POINT('',#268); +#270=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-3.50000000000000000E3)); +#271=VERTEX_POINT('',#270); +#272=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#273=VERTEX_POINT('',#272); +#274=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-3.50000000000000000E3)); +#275=VERTEX_POINT('',#274); +#276=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#277=VERTEX_POINT('',#276); +#278=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-3.50000000000000000E3)); +#279=VERTEX_POINT('',#278); +#280=CARTESIAN_POINT('',(3.00000000000000000E3,3.50000000000000000E3,-3.50000000000000000E3)); +#281=VERTEX_POINT('',#280); +#282=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#283=VERTEX_POINT('',#282); +#284=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#285=VERTEX_POINT('',#284); +#286=CARTESIAN_POINT('',(4.50000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#287=VERTEX_POINT('',#286); +#288=CARTESIAN_POINT('',(4.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#289=VERTEX_POINT('',#288); +#290=CARTESIAN_POINT('',(4.50000000000000000E3,3.50000000000000000E3,-3.00000000000000000E3)); +#291=VERTEX_POINT('',#290); +#292=CARTESIAN_POINT('',(4.50000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#293=VERTEX_POINT('',#292); +#294=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#295=VERTEX_POINT('',#294); +#296=CARTESIAN_POINT('',(4.50000000000000000E3,3.00000000000000000E3,-3.50000000000000000E3)); +#297=VERTEX_POINT('',#296); +#298=CARTESIAN_POINT('',(4.50000000000000000E3,3.50000000000000000E3,-3.50000000000000000E3)); +#299=VERTEX_POINT('',#298); +#300=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,0.00000000000000000E0)); +#301=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#302=VECTOR('',#301,1.00000000000000000E3); +#303=LINE('',#300,#302); +#304=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#305=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#306=VECTOR('',#305,1.00000000000000000E3); +#307=LINE('',#304,#306); +#308=DEFINITIONAL_REPRESENTATION('',(#307),#1); +#309=PCURVE('',#6,#308); +#310=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#311=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#312=VECTOR('',#311,1.00000000000000000E3); +#313=LINE('',#310,#312); +#314=DEFINITIONAL_REPRESENTATION('',(#313),#1); +#315=PCURVE('',#21,#314); +#316=SURFACE_CURVE('',#303,(#309,#315),.CURVE_3D.); +#317=EDGE_CURVE('',#213,#215,#316,.T.); +#318=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E2)); +#319=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#320=VECTOR('',#319,1.00000000000000000E3); +#321=LINE('',#318,#320); +#322=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#323=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#324=VECTOR('',#323,1.00000000000000000E3); +#325=LINE('',#322,#324); +#326=DEFINITIONAL_REPRESENTATION('',(#325),#1); +#327=PCURVE('',#6,#326); +#328=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#329=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#330=VECTOR('',#329,1.00000000000000000E3); +#331=LINE('',#328,#330); +#332=DEFINITIONAL_REPRESENTATION('',(#331),#1); +#333=PCURVE('',#36,#332); +#334=SURFACE_CURVE('',#321,(#327,#333),.CURVE_3D.); +#335=EDGE_CURVE('',#217,#215,#334,.T.); +#336=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#337=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#338=VECTOR('',#337,1.00000000000000000E3); +#339=LINE('',#336,#338); +#340=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#341=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#342=VECTOR('',#341,1.00000000000000000E3); +#343=LINE('',#340,#342); +#344=DEFINITIONAL_REPRESENTATION('',(#343),#1); +#345=PCURVE('',#6,#344); +#346=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#347=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#348=VECTOR('',#347,1.00000000000000000E3); +#349=LINE('',#346,#348); +#350=DEFINITIONAL_REPRESENTATION('',(#349),#1); +#351=PCURVE('',#16,#350); +#352=SURFACE_CURVE('',#339,(#345,#351),.CURVE_3D.); +#353=EDGE_CURVE('',#219,#217,#352,.T.); +#354=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#355=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#356=VECTOR('',#355,1.00000000000000000E3); +#357=LINE('',#354,#356); +#358=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#359=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#360=VECTOR('',#359,1.00000000000000000E3); +#361=LINE('',#358,#360); +#362=DEFINITIONAL_REPRESENTATION('',(#361),#1); +#363=PCURVE('',#6,#362); +#364=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#365=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#366=VECTOR('',#365,1.00000000000000000E3); +#367=LINE('',#364,#366); +#368=DEFINITIONAL_REPRESENTATION('',(#367),#1); +#369=PCURVE('',#11,#368); +#370=SURFACE_CURVE('',#357,(#363,#369),.CURVE_3D.); +#371=EDGE_CURVE('',#219,#213,#370,.T.); +#372=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#373=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#374=VECTOR('',#373,1.00000000000000000E3); +#375=LINE('',#372,#374); +#376=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#377=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#378=VECTOR('',#377,1.00000000000000000E3); +#379=LINE('',#376,#378); +#380=DEFINITIONAL_REPRESENTATION('',(#379),#1); +#381=PCURVE('',#11,#380); +#382=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#383=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#384=VECTOR('',#383,1.00000000000000000E3); +#385=LINE('',#382,#384); +#386=DEFINITIONAL_REPRESENTATION('',(#385),#1); +#387=PCURVE('',#71,#386); +#388=SURFACE_CURVE('',#375,(#381,#387),.CURVE_3D.); +#389=EDGE_CURVE('',#221,#223,#388,.T.); +#390=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,0.00000000000000000E0)); +#391=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#392=VECTOR('',#391,1.00000000000000000E3); +#393=LINE('',#390,#392); +#394=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#395=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#396=VECTOR('',#395,1.00000000000000000E3); +#397=LINE('',#394,#396); +#398=DEFINITIONAL_REPRESENTATION('',(#397),#1); +#399=PCURVE('',#11,#398); +#400=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#401=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#402=VECTOR('',#401,1.00000000000000000E3); +#403=LINE('',#400,#402); +#404=DEFINITIONAL_REPRESENTATION('',(#403),#1); +#405=PCURVE('',#26,#404); +#406=SURFACE_CURVE('',#393,(#399,#405),.CURVE_3D.); +#407=EDGE_CURVE('',#213,#223,#406,.T.); +#408=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#409=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#410=VECTOR('',#409,1.00000000000000000E3); +#411=LINE('',#408,#410); +#412=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#413=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#414=VECTOR('',#413,1.00000000000000000E3); +#415=LINE('',#412,#414); +#416=DEFINITIONAL_REPRESENTATION('',(#415),#1); +#417=PCURVE('',#11,#416); +#418=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#419=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#420=VECTOR('',#419,1.00000000000000000E3); +#421=LINE('',#418,#420); +#422=DEFINITIONAL_REPRESENTATION('',(#421),#1); +#423=PCURVE('',#16,#422); +#424=SURFACE_CURVE('',#411,(#417,#423),.CURVE_3D.); +#425=EDGE_CURVE('',#219,#221,#424,.T.); +#426=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E2)); +#427=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#428=VECTOR('',#427,1.00000000000000000E3); +#429=LINE('',#426,#428); +#430=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#431=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#432=VECTOR('',#431,1.00000000000000000E3); +#433=LINE('',#430,#432); +#434=DEFINITIONAL_REPRESENTATION('',(#433),#1); +#435=PCURVE('',#16,#434); +#436=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#437=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#438=VECTOR('',#437,1.00000000000000000E3); +#439=LINE('',#436,#438); +#440=DEFINITIONAL_REPRESENTATION('',(#439),#1); +#441=PCURVE('',#46,#440); +#442=SURFACE_CURVE('',#429,(#435,#441),.CURVE_3D.); +#443=EDGE_CURVE('',#217,#225,#442,.T.); +#444=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#445=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#446=VECTOR('',#445,1.00000000000000000E3); +#447=LINE('',#444,#446); +#448=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#449=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#450=VECTOR('',#449,1.00000000000000000E3); +#451=LINE('',#448,#450); +#452=DEFINITIONAL_REPRESENTATION('',(#451),#1); +#453=PCURVE('',#16,#452); +#454=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#455=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#456=VECTOR('',#455,1.00000000000000000E3); +#457=LINE('',#454,#456); +#458=DEFINITIONAL_REPRESENTATION('',(#457),#1); +#459=PCURVE('',#76,#458); +#460=SURFACE_CURVE('',#447,(#453,#459),.CURVE_3D.); +#461=EDGE_CURVE('',#221,#225,#460,.T.); +#462=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#463=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#464=VECTOR('',#463,1.00000000000000000E3); +#465=LINE('',#462,#464); +#466=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#467=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#468=VECTOR('',#467,1.00000000000000000E3); +#469=LINE('',#466,#468); +#470=DEFINITIONAL_REPRESENTATION('',(#469),#1); +#471=PCURVE('',#21,#470); +#472=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#473=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#474=VECTOR('',#473,1.00000000000000000E3); +#475=LINE('',#472,#474); +#476=DEFINITIONAL_REPRESENTATION('',(#475),#1); +#477=PCURVE('',#31,#476); +#478=SURFACE_CURVE('',#465,(#471,#477),.CURVE_3D.); +#479=EDGE_CURVE('',#227,#229,#478,.T.); +#480=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-5.00000000000000000E2)); +#481=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#482=VECTOR('',#481,1.00000000000000000E3); +#483=LINE('',#480,#482); +#484=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#485=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#486=VECTOR('',#485,1.00000000000000000E3); +#487=LINE('',#484,#486); +#488=DEFINITIONAL_REPRESENTATION('',(#487),#1); +#489=PCURVE('',#21,#488); +#490=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#491=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#492=VECTOR('',#491,1.00000000000000000E3); +#493=LINE('',#490,#492); +#494=DEFINITIONAL_REPRESENTATION('',(#493),#1); +#495=PCURVE('',#51,#494); +#496=SURFACE_CURVE('',#483,(#489,#495),.CURVE_3D.); +#497=EDGE_CURVE('',#215,#229,#496,.T.); +#498=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,0.00000000000000000E0)); +#499=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#500=VECTOR('',#499,1.00000000000000000E3); +#501=LINE('',#498,#500); +#502=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#503=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#504=VECTOR('',#503,1.00000000000000000E3); +#505=LINE('',#502,#504); +#506=DEFINITIONAL_REPRESENTATION('',(#505),#1); +#507=PCURVE('',#21,#506); +#508=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#509=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#510=VECTOR('',#509,1.00000000000000000E3); +#511=LINE('',#508,#510); +#512=DEFINITIONAL_REPRESENTATION('',(#511),#1); +#513=PCURVE('',#26,#512); +#514=SURFACE_CURVE('',#501,(#507,#513),.CURVE_3D.); +#515=EDGE_CURVE('',#213,#227,#514,.T.); +#516=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#517=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#518=VECTOR('',#517,1.00000000000000000E3); +#519=LINE('',#516,#518); +#520=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#521=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#522=VECTOR('',#521,1.00000000000000000E3); +#523=LINE('',#520,#522); +#524=DEFINITIONAL_REPRESENTATION('',(#523),#1); +#525=PCURVE('',#26,#524); +#526=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#527=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#528=VECTOR('',#527,1.00000000000000000E3); +#529=LINE('',#526,#528); +#530=DEFINITIONAL_REPRESENTATION('',(#529),#1); +#531=PCURVE('',#86,#530); +#532=SURFACE_CURVE('',#519,(#525,#531),.CURVE_3D.); +#533=EDGE_CURVE('',#223,#231,#532,.T.); +#534=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#535=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#536=VECTOR('',#535,1.00000000000000000E3); +#537=LINE('',#534,#536); +#538=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#539=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#540=VECTOR('',#539,1.00000000000000000E3); +#541=LINE('',#538,#540); +#542=DEFINITIONAL_REPRESENTATION('',(#541),#1); +#543=PCURVE('',#26,#542); +#544=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#545=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#546=VECTOR('',#545,1.00000000000000000E3); +#547=LINE('',#544,#546); +#548=DEFINITIONAL_REPRESENTATION('',(#547),#1); +#549=PCURVE('',#31,#548); +#550=SURFACE_CURVE('',#537,(#543,#549),.CURVE_3D.); +#551=EDGE_CURVE('',#227,#231,#550,.T.); +#552=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#553=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#554=VECTOR('',#553,1.00000000000000000E3); +#555=LINE('',#552,#554); +#556=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#557=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#558=VECTOR('',#557,1.00000000000000000E3); +#559=LINE('',#556,#558); +#560=DEFINITIONAL_REPRESENTATION('',(#559),#1); +#561=PCURVE('',#31,#560); +#562=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#563=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#564=VECTOR('',#563,1.00000000000000000E3); +#565=LINE('',#562,#564); +#566=DEFINITIONAL_REPRESENTATION('',(#565),#1); +#567=PCURVE('',#91,#566); +#568=SURFACE_CURVE('',#555,(#561,#567),.CURVE_3D.); +#569=EDGE_CURVE('',#231,#233,#568,.T.); +#570=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-5.00000000000000000E2)); +#571=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#572=VECTOR('',#571,1.00000000000000000E3); +#573=LINE('',#570,#572); +#574=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#575=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#576=VECTOR('',#575,1.00000000000000000E3); +#577=LINE('',#574,#576); +#578=DEFINITIONAL_REPRESENTATION('',(#577),#1); +#579=PCURVE('',#31,#578); +#580=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#581=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#582=VECTOR('',#581,1.00000000000000000E3); +#583=LINE('',#580,#582); +#584=DEFINITIONAL_REPRESENTATION('',(#583),#1); +#585=PCURVE('',#61,#584); +#586=SURFACE_CURVE('',#573,(#579,#585),.CURVE_3D.); +#587=EDGE_CURVE('',#229,#233,#586,.T.); +#588=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-5.00000000000000000E2)); +#589=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#590=VECTOR('',#589,1.00000000000000000E3); +#591=LINE('',#588,#590); +#592=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#593=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#594=VECTOR('',#593,1.00000000000000000E3); +#595=LINE('',#592,#594); +#596=DEFINITIONAL_REPRESENTATION('',(#595),#1); +#597=PCURVE('',#36,#596); +#598=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#599=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#600=VECTOR('',#599,1.00000000000000000E3); +#601=LINE('',#598,#600); +#602=DEFINITIONAL_REPRESENTATION('',(#601),#1); +#603=PCURVE('',#51,#602); +#604=SURFACE_CURVE('',#591,(#597,#603),.CURVE_3D.); +#605=EDGE_CURVE('',#215,#235,#604,.T.); +#606=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#607=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#608=VECTOR('',#607,1.00000000000000000E3); +#609=LINE('',#606,#608); +#610=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#611=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#612=VECTOR('',#611,1.00000000000000000E3); +#613=LINE('',#610,#612); +#614=DEFINITIONAL_REPRESENTATION('',(#613),#1); +#615=PCURVE('',#36,#614); +#616=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#617=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#618=VECTOR('',#617,1.00000000000000000E3); +#619=LINE('',#616,#618); +#620=DEFINITIONAL_REPRESENTATION('',(#619),#1); +#621=PCURVE('',#41,#620); +#622=SURFACE_CURVE('',#609,(#615,#621),.CURVE_3D.); +#623=EDGE_CURVE('',#237,#235,#622,.T.); +#624=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E2)); +#625=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#626=VECTOR('',#625,1.00000000000000000E3); +#627=LINE('',#624,#626); +#628=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#629=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#630=VECTOR('',#629,1.00000000000000000E3); +#631=LINE('',#628,#630); +#632=DEFINITIONAL_REPRESENTATION('',(#631),#1); +#633=PCURVE('',#36,#632); +#634=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#635=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#636=VECTOR('',#635,1.00000000000000000E3); +#637=LINE('',#634,#636); +#638=DEFINITIONAL_REPRESENTATION('',(#637),#1); +#639=PCURVE('',#46,#638); +#640=SURFACE_CURVE('',#627,(#633,#639),.CURVE_3D.); +#641=EDGE_CURVE('',#217,#237,#640,.T.); +#642=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-3.00000000000000000E3)); +#643=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#644=VECTOR('',#643,1.00000000000000000E3); +#645=LINE('',#642,#644); +#646=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#647=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#648=VECTOR('',#647,1.00000000000000000E3); +#649=LINE('',#646,#648); +#650=DEFINITIONAL_REPRESENTATION('',(#649),#1); +#651=PCURVE('',#41,#650); +#652=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#653=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#654=VECTOR('',#653,1.00000000000000000E3); +#655=LINE('',#652,#654); +#656=DEFINITIONAL_REPRESENTATION('',(#655),#1); +#657=PCURVE('',#56,#656); +#658=SURFACE_CURVE('',#645,(#651,#657),.CURVE_3D.); +#659=EDGE_CURVE('',#235,#239,#658,.T.); +#660=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#661=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#662=VECTOR('',#661,1.00000000000000000E3); +#663=LINE('',#660,#662); +#664=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#665=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#666=VECTOR('',#665,1.00000000000000000E3); +#667=LINE('',#664,#666); +#668=DEFINITIONAL_REPRESENTATION('',(#667),#1); +#669=PCURVE('',#41,#668); +#670=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#671=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#672=VECTOR('',#671,1.00000000000000000E3); +#673=LINE('',#670,#672); +#674=DEFINITIONAL_REPRESENTATION('',(#673),#1); +#675=PCURVE('',#101,#674); +#676=SURFACE_CURVE('',#663,(#669,#675),.CURVE_3D.); +#677=EDGE_CURVE('',#241,#239,#676,.T.); +#678=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-3.00000000000000000E3)); +#679=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#680=VECTOR('',#679,1.00000000000000000E3); +#681=LINE('',#678,#680); +#682=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#683=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#684=VECTOR('',#683,1.00000000000000000E3); +#685=LINE('',#682,#684); +#686=DEFINITIONAL_REPRESENTATION('',(#685),#1); +#687=PCURVE('',#41,#686); +#688=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#689=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#690=VECTOR('',#689,1.00000000000000000E3); +#691=LINE('',#688,#690); +#692=DEFINITIONAL_REPRESENTATION('',(#691),#1); +#693=PCURVE('',#46,#692); +#694=SURFACE_CURVE('',#681,(#687,#693),.CURVE_3D.); +#695=EDGE_CURVE('',#237,#241,#694,.T.); +#696=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#697=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#698=VECTOR('',#697,1.00000000000000000E3); +#699=LINE('',#696,#698); +#700=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#701=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#702=VECTOR('',#701,1.00000000000000000E3); +#703=LINE('',#700,#702); +#704=DEFINITIONAL_REPRESENTATION('',(#703),#1); +#705=PCURVE('',#46,#704); +#706=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#707=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#708=VECTOR('',#707,1.00000000000000000E3); +#709=LINE('',#706,#708); +#710=DEFINITIONAL_REPRESENTATION('',(#709),#1); +#711=PCURVE('',#106,#710); +#712=SURFACE_CURVE('',#699,(#705,#711),.CURVE_3D.); +#713=EDGE_CURVE('',#225,#241,#712,.T.); +#714=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-5.00000000000000000E2)); +#715=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#716=VECTOR('',#715,1.00000000000000000E3); +#717=LINE('',#714,#716); +#718=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2)); +#719=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#720=VECTOR('',#719,1.00000000000000000E3); +#721=LINE('',#718,#720); +#722=DEFINITIONAL_REPRESENTATION('',(#721),#1); +#723=PCURVE('',#51,#722); +#724=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#725=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#726=VECTOR('',#725,1.00000000000000000E3); +#727=LINE('',#724,#726); +#728=DEFINITIONAL_REPRESENTATION('',(#727),#1); +#729=PCURVE('',#61,#728); +#730=SURFACE_CURVE('',#717,(#723,#729),.CURVE_3D.); +#731=EDGE_CURVE('',#229,#243,#730,.T.); +#732=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2,-3.00000000000000000E3)); +#733=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#734=VECTOR('',#733,1.00000000000000000E3); +#735=LINE('',#732,#734); +#736=CARTESIAN_POINT('',(5.00000000000000000E2,3.00000000000000000E3)); +#737=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#738=VECTOR('',#737,1.00000000000000000E3); +#739=LINE('',#736,#738); +#740=DEFINITIONAL_REPRESENTATION('',(#739),#1); +#741=PCURVE('',#51,#740); +#742=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#743=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#744=VECTOR('',#743,1.00000000000000000E3); +#745=LINE('',#742,#744); +#746=DEFINITIONAL_REPRESENTATION('',(#745),#1); +#747=PCURVE('',#56,#746); +#748=SURFACE_CURVE('',#735,(#741,#747),.CURVE_3D.); +#749=EDGE_CURVE('',#235,#243,#748,.T.); +#750=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-3.00000000000000000E3)); +#751=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#752=VECTOR('',#751,1.00000000000000000E3); +#753=LINE('',#750,#752); +#754=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#755=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#756=VECTOR('',#755,1.00000000000000000E3); +#757=LINE('',#754,#756); +#758=DEFINITIONAL_REPRESENTATION('',(#757),#1); +#759=PCURVE('',#56,#758); +#760=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#761=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#762=VECTOR('',#761,1.00000000000000000E3); +#763=LINE('',#760,#762); +#764=DEFINITIONAL_REPRESENTATION('',(#763),#1); +#765=PCURVE('',#61,#764); +#766=SURFACE_CURVE('',#753,(#759,#765),.CURVE_3D.); +#767=EDGE_CURVE('',#243,#245,#766,.T.); +#768=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#769=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#770=VECTOR('',#769,1.00000000000000000E3); +#771=LINE('',#768,#770); +#772=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#773=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#774=VECTOR('',#773,1.00000000000000000E3); +#775=LINE('',#772,#774); +#776=DEFINITIONAL_REPRESENTATION('',(#775),#1); +#777=PCURVE('',#56,#776); +#778=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#779=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#780=VECTOR('',#779,1.00000000000000000E3); +#781=LINE('',#778,#780); +#782=DEFINITIONAL_REPRESENTATION('',(#781),#1); +#783=PCURVE('',#126,#782); +#784=SURFACE_CURVE('',#771,(#777,#783),.CURVE_3D.); +#785=EDGE_CURVE('',#239,#245,#784,.T.); +#786=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#787=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#788=VECTOR('',#787,1.00000000000000000E3); +#789=LINE('',#786,#788); +#790=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#791=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#792=VECTOR('',#791,1.00000000000000000E3); +#793=LINE('',#790,#792); +#794=DEFINITIONAL_REPRESENTATION('',(#793),#1); +#795=PCURVE('',#61,#794); +#796=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#797=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#798=VECTOR('',#797,1.00000000000000000E3); +#799=LINE('',#796,#798); +#800=DEFINITIONAL_REPRESENTATION('',(#799),#1); +#801=PCURVE('',#111,#800); +#802=SURFACE_CURVE('',#789,(#795,#801),.CURVE_3D.); +#803=EDGE_CURVE('',#233,#245,#802,.T.); +#804=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#805=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#806=VECTOR('',#805,1.00000000000000000E3); +#807=LINE('',#804,#806); +#808=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#809=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#810=VECTOR('',#809,1.00000000000000000E3); +#811=LINE('',#808,#810); +#812=DEFINITIONAL_REPRESENTATION('',(#811),#1); +#813=PCURVE('',#66,#812); +#814=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#815=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#816=VECTOR('',#815,1.00000000000000000E3); +#817=LINE('',#814,#816); +#818=DEFINITIONAL_REPRESENTATION('',(#817),#1); +#819=PCURVE('',#76,#818); +#820=SURFACE_CURVE('',#807,(#813,#819),.CURVE_3D.); +#821=EDGE_CURVE('',#247,#249,#820,.T.); +#822=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#823=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#824=VECTOR('',#823,1.00000000000000000E3); +#825=LINE('',#822,#824); +#826=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#827=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#828=VECTOR('',#827,1.00000000000000000E3); +#829=LINE('',#826,#828); +#830=DEFINITIONAL_REPRESENTATION('',(#829),#1); +#831=PCURVE('',#66,#830); +#832=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#833=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#834=VECTOR('',#833,1.00000000000000000E3); +#835=LINE('',#832,#834); +#836=DEFINITIONAL_REPRESENTATION('',(#835),#1); +#837=PCURVE('',#96,#836); +#838=SURFACE_CURVE('',#825,(#831,#837),.CURVE_3D.); +#839=EDGE_CURVE('',#249,#251,#838,.T.); +#840=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#841=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#842=VECTOR('',#841,1.00000000000000000E3); +#843=LINE('',#840,#842); +#844=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#845=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#846=VECTOR('',#845,1.00000000000000000E3); +#847=LINE('',#844,#846); +#848=DEFINITIONAL_REPRESENTATION('',(#847),#1); +#849=PCURVE('',#66,#848); +#850=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#851=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#852=VECTOR('',#851,1.00000000000000000E3); +#853=LINE('',#850,#852); +#854=DEFINITIONAL_REPRESENTATION('',(#853),#1); +#855=PCURVE('',#81,#854); +#856=SURFACE_CURVE('',#843,(#849,#855),.CURVE_3D.); +#857=EDGE_CURVE('',#253,#251,#856,.T.); +#858=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#859=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#860=VECTOR('',#859,1.00000000000000000E3); +#861=LINE('',#858,#860); +#862=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#863=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#864=VECTOR('',#863,1.00000000000000000E3); +#865=LINE('',#862,#864); +#866=DEFINITIONAL_REPRESENTATION('',(#865),#1); +#867=PCURVE('',#66,#866); +#868=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#869=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#870=VECTOR('',#869,1.00000000000000000E3); +#871=LINE('',#868,#870); +#872=DEFINITIONAL_REPRESENTATION('',(#871),#1); +#873=PCURVE('',#71,#872); +#874=SURFACE_CURVE('',#861,(#867,#873),.CURVE_3D.); +#875=EDGE_CURVE('',#247,#253,#874,.T.); +#876=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#877=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#878=VECTOR('',#877,1.00000000000000000E3); +#879=LINE('',#876,#878); +#880=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#881=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#882=VECTOR('',#881,1.00000000000000000E3); +#883=LINE('',#880,#882); +#884=DEFINITIONAL_REPRESENTATION('',(#883),#1); +#885=PCURVE('',#71,#884); +#886=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#887=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#888=VECTOR('',#887,1.00000000000000000E3); +#889=LINE('',#886,#888); +#890=DEFINITIONAL_REPRESENTATION('',(#889),#1); +#891=PCURVE('',#86,#890); +#892=SURFACE_CURVE('',#879,(#885,#891),.CURVE_3D.); +#893=EDGE_CURVE('',#223,#253,#892,.T.); +#894=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#895=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#896=VECTOR('',#895,1.00000000000000000E3); +#897=LINE('',#894,#896); +#898=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#899=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#900=VECTOR('',#899,1.00000000000000000E3); +#901=LINE('',#898,#900); +#902=DEFINITIONAL_REPRESENTATION('',(#901),#1); +#903=PCURVE('',#71,#902); +#904=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#905=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#906=VECTOR('',#905,1.00000000000000000E3); +#907=LINE('',#904,#906); +#908=DEFINITIONAL_REPRESENTATION('',(#907),#1); +#909=PCURVE('',#76,#908); +#910=SURFACE_CURVE('',#897,(#903,#909),.CURVE_3D.); +#911=EDGE_CURVE('',#221,#247,#910,.T.); +#912=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#913=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#914=VECTOR('',#913,1.00000000000000000E3); +#915=LINE('',#912,#914); +#916=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#917=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#918=VECTOR('',#917,1.00000000000000000E3); +#919=LINE('',#916,#918); +#920=DEFINITIONAL_REPRESENTATION('',(#919),#1); +#921=PCURVE('',#76,#920); +#922=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#923=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#924=VECTOR('',#923,1.00000000000000000E3); +#925=LINE('',#922,#924); +#926=DEFINITIONAL_REPRESENTATION('',(#925),#1); +#927=PCURVE('',#106,#926); +#928=SURFACE_CURVE('',#915,(#921,#927),.CURVE_3D.); +#929=EDGE_CURVE('',#225,#249,#928,.T.); +#930=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#931=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#932=VECTOR('',#931,1.00000000000000000E3); +#933=LINE('',#930,#932); +#934=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#935=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#936=VECTOR('',#935,1.00000000000000000E3); +#937=LINE('',#934,#936); +#938=DEFINITIONAL_REPRESENTATION('',(#937),#1); +#939=PCURVE('',#81,#938); +#940=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#941=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#942=VECTOR('',#941,1.00000000000000000E3); +#943=LINE('',#940,#942); +#944=DEFINITIONAL_REPRESENTATION('',(#943),#1); +#945=PCURVE('',#161,#944); +#946=SURFACE_CURVE('',#933,(#939,#945),.CURVE_3D.); +#947=EDGE_CURVE('',#251,#255,#946,.T.); +#948=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#949=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#950=VECTOR('',#949,1.00000000000000000E3); +#951=LINE('',#948,#950); +#952=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#953=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#954=VECTOR('',#953,1.00000000000000000E3); +#955=LINE('',#952,#954); +#956=DEFINITIONAL_REPRESENTATION('',(#955),#1); +#957=PCURVE('',#81,#956); +#958=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#959=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#960=VECTOR('',#959,1.00000000000000000E3); +#961=LINE('',#958,#960); +#962=DEFINITIONAL_REPRESENTATION('',(#961),#1); +#963=PCURVE('',#91,#962); +#964=SURFACE_CURVE('',#951,(#957,#963),.CURVE_3D.); +#965=EDGE_CURVE('',#257,#255,#964,.T.); +#966=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,0.00000000000000000E0)); +#967=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#968=VECTOR('',#967,1.00000000000000000E3); +#969=LINE('',#966,#968); +#970=CARTESIAN_POINT('',(0.00000000000000000E0,5.00000000000000000E2)); +#971=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#972=VECTOR('',#971,1.00000000000000000E3); +#973=LINE('',#970,#972); +#974=DEFINITIONAL_REPRESENTATION('',(#973),#1); +#975=PCURVE('',#81,#974); +#976=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2)); +#977=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#978=VECTOR('',#977,1.00000000000000000E3); +#979=LINE('',#976,#978); +#980=DEFINITIONAL_REPRESENTATION('',(#979),#1); +#981=PCURVE('',#86,#980); +#982=SURFACE_CURVE('',#969,(#975,#981),.CURVE_3D.); +#983=EDGE_CURVE('',#253,#257,#982,.T.); +#984=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#985=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#986=VECTOR('',#985,1.00000000000000000E3); +#987=LINE('',#984,#986); +#988=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3)); +#989=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#990=VECTOR('',#989,1.00000000000000000E3); +#991=LINE('',#988,#990); +#992=DEFINITIONAL_REPRESENTATION('',(#991),#1); +#993=PCURVE('',#86,#992); +#994=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#995=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#996=VECTOR('',#995,1.00000000000000000E3); +#997=LINE('',#994,#996); +#998=DEFINITIONAL_REPRESENTATION('',(#997),#1); +#999=PCURVE('',#91,#998); +#1000=SURFACE_CURVE('',#987,(#993,#999),.CURVE_3D.); +#1001=EDGE_CURVE('',#231,#257,#1000,.T.); +#1002=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#1003=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1004=VECTOR('',#1003,1.00000000000000000E3); +#1005=LINE('',#1002,#1004); +#1006=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2)); +#1007=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1008=VECTOR('',#1007,1.00000000000000000E3); +#1009=LINE('',#1006,#1008); +#1010=DEFINITIONAL_REPRESENTATION('',(#1009),#1); +#1011=PCURVE('',#91,#1010); +#1012=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#1013=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1014=VECTOR('',#1013,1.00000000000000000E3); +#1015=LINE('',#1012,#1014); +#1016=DEFINITIONAL_REPRESENTATION('',(#1015),#1); +#1017=PCURVE('',#116,#1016); +#1018=SURFACE_CURVE('',#1005,(#1011,#1017),.CURVE_3D.); +#1019=EDGE_CURVE('',#233,#255,#1018,.T.); +#1020=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#1021=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1022=VECTOR('',#1021,1.00000000000000000E3); +#1023=LINE('',#1020,#1022); +#1024=CARTESIAN_POINT('',(5.00000000000000000E2,0.00000000000000000E0)); +#1025=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1026=VECTOR('',#1025,1.00000000000000000E3); +#1027=LINE('',#1024,#1026); +#1028=DEFINITIONAL_REPRESENTATION('',(#1027),#1); +#1029=PCURVE('',#96,#1028); +#1030=CARTESIAN_POINT('',(5.00000000000000000E2,3.00000000000000000E3)); +#1031=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1032=VECTOR('',#1031,1.00000000000000000E3); +#1033=LINE('',#1030,#1032); +#1034=DEFINITIONAL_REPRESENTATION('',(#1033),#1); +#1035=PCURVE('',#106,#1034); +#1036=SURFACE_CURVE('',#1023,(#1029,#1035),.CURVE_3D.); +#1037=EDGE_CURVE('',#249,#259,#1036,.T.); +#1038=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#1039=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1040=VECTOR('',#1039,1.00000000000000000E3); +#1041=LINE('',#1038,#1040); +#1042=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#1043=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1044=VECTOR('',#1043,1.00000000000000000E3); +#1045=LINE('',#1042,#1044); +#1046=DEFINITIONAL_REPRESENTATION('',(#1045),#1); +#1047=PCURVE('',#96,#1046); +#1048=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#1049=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1050=VECTOR('',#1049,1.00000000000000000E3); +#1051=LINE('',#1048,#1050); +#1052=DEFINITIONAL_REPRESENTATION('',(#1051),#1); +#1053=PCURVE('',#101,#1052); +#1054=SURFACE_CURVE('',#1041,(#1047,#1053),.CURVE_3D.); +#1055=EDGE_CURVE('',#259,#261,#1054,.T.); +#1056=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#1057=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1058=VECTOR('',#1057,1.00000000000000000E3); +#1059=LINE('',#1056,#1058); +#1060=CARTESIAN_POINT('',(5.00000000000000000E2,5.00000000000000000E2)); +#1061=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1062=VECTOR('',#1061,1.00000000000000000E3); +#1063=LINE('',#1060,#1062); +#1064=DEFINITIONAL_REPRESENTATION('',(#1063),#1); +#1065=PCURVE('',#96,#1064); +#1066=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#1067=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1068=VECTOR('',#1067,1.00000000000000000E3); +#1069=LINE('',#1066,#1068); +#1070=DEFINITIONAL_REPRESENTATION('',(#1069),#1); +#1071=PCURVE('',#166,#1070); +#1072=SURFACE_CURVE('',#1059,(#1065,#1071),.CURVE_3D.); +#1073=EDGE_CURVE('',#251,#261,#1072,.T.); +#1074=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#1075=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1076=VECTOR('',#1075,1.00000000000000000E3); +#1077=LINE('',#1074,#1076); +#1078=CARTESIAN_POINT('',(5.00000000000000000E2,1.50000000000000000E3)); +#1079=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1080=VECTOR('',#1079,1.00000000000000000E3); +#1081=LINE('',#1078,#1080); +#1082=DEFINITIONAL_REPRESENTATION('',(#1081),#1); +#1083=PCURVE('',#101,#1082); +#1084=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#1085=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1086=VECTOR('',#1085,1.00000000000000000E3); +#1087=LINE('',#1084,#1086); +#1088=DEFINITIONAL_REPRESENTATION('',(#1087),#1); +#1089=PCURVE('',#136,#1088); +#1090=SURFACE_CURVE('',#1077,(#1083,#1089),.CURVE_3D.); +#1091=EDGE_CURVE('',#239,#261,#1090,.T.); +#1092=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0,-3.00000000000000000E3)); +#1093=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1094=VECTOR('',#1093,1.00000000000000000E3); +#1095=LINE('',#1092,#1094); +#1096=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#1097=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1098=VECTOR('',#1097,1.00000000000000000E3); +#1099=LINE('',#1096,#1098); +#1100=DEFINITIONAL_REPRESENTATION('',(#1099),#1); +#1101=PCURVE('',#101,#1100); +#1102=CARTESIAN_POINT('',(3.00000000000000000E3,1.50000000000000000E3)); +#1103=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1104=VECTOR('',#1103,1.00000000000000000E3); +#1105=LINE('',#1102,#1104); +#1106=DEFINITIONAL_REPRESENTATION('',(#1105),#1); +#1107=PCURVE('',#106,#1106); +#1108=SURFACE_CURVE('',#1095,(#1101,#1107),.CURVE_3D.); +#1109=EDGE_CURVE('',#241,#259,#1108,.T.); +#1110=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#1111=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1112=VECTOR('',#1111,1.00000000000000000E3); +#1113=LINE('',#1110,#1112); +#1114=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#1115=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1116=VECTOR('',#1115,1.00000000000000000E3); +#1117=LINE('',#1114,#1116); +#1118=DEFINITIONAL_REPRESENTATION('',(#1117),#1); +#1119=PCURVE('',#111,#1118); +#1120=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1121=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1122=VECTOR('',#1121,1.00000000000000000E3); +#1123=LINE('',#1120,#1122); +#1124=DEFINITIONAL_REPRESENTATION('',(#1123),#1); +#1125=PCURVE('',#121,#1124); +#1126=SURFACE_CURVE('',#1113,(#1119,#1125),.CURVE_3D.); +#1127=EDGE_CURVE('',#263,#265,#1126,.T.); +#1128=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#1129=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1130=VECTOR('',#1129,1.00000000000000000E3); +#1131=LINE('',#1128,#1130); +#1132=CARTESIAN_POINT('',(2.50000000000000000E3,2.50000000000000000E3)); +#1133=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1134=VECTOR('',#1133,1.00000000000000000E3); +#1135=LINE('',#1132,#1134); +#1136=DEFINITIONAL_REPRESENTATION('',(#1135),#1); +#1137=PCURVE('',#111,#1136); +#1138=CARTESIAN_POINT('',(2.50000000000000000E3,2.50000000000000000E3)); +#1139=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1140=VECTOR('',#1139,1.00000000000000000E3); +#1141=LINE('',#1138,#1140); +#1142=DEFINITIONAL_REPRESENTATION('',(#1141),#1); +#1143=PCURVE('',#141,#1142); +#1144=SURFACE_CURVE('',#1131,(#1137,#1143),.CURVE_3D.); +#1145=EDGE_CURVE('',#245,#265,#1144,.T.); +#1146=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#1147=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1148=VECTOR('',#1147,1.00000000000000000E3); +#1149=LINE('',#1146,#1148); +#1150=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#1151=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1152=VECTOR('',#1151,1.00000000000000000E3); +#1153=LINE('',#1150,#1152); +#1154=DEFINITIONAL_REPRESENTATION('',(#1153),#1); +#1155=PCURVE('',#111,#1154); +#1156=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#1157=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1158=VECTOR('',#1157,1.00000000000000000E3); +#1159=LINE('',#1156,#1158); +#1160=DEFINITIONAL_REPRESENTATION('',(#1159),#1); +#1161=PCURVE('',#116,#1160); +#1162=SURFACE_CURVE('',#1149,(#1155,#1161),.CURVE_3D.); +#1163=EDGE_CURVE('',#233,#263,#1162,.T.); +#1164=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#1165=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1166=VECTOR('',#1165,1.00000000000000000E3); +#1167=LINE('',#1164,#1166); +#1168=CARTESIAN_POINT('',(1.50000000000000000E3,2.50000000000000000E3)); +#1169=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1170=VECTOR('',#1169,1.00000000000000000E3); +#1171=LINE('',#1168,#1170); +#1172=DEFINITIONAL_REPRESENTATION('',(#1171),#1); +#1173=PCURVE('',#116,#1172); +#1174=CARTESIAN_POINT('',(1.50000000000000000E3,2.50000000000000000E3)); +#1175=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1176=VECTOR('',#1175,1.00000000000000000E3); +#1177=LINE('',#1174,#1176); +#1178=DEFINITIONAL_REPRESENTATION('',(#1177),#1); +#1179=PCURVE('',#176,#1178); +#1180=SURFACE_CURVE('',#1167,(#1173,#1179),.CURVE_3D.); +#1181=EDGE_CURVE('',#255,#267,#1180,.T.); +#1182=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#1183=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1184=VECTOR('',#1183,1.00000000000000000E3); +#1185=LINE('',#1182,#1184); +#1186=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#1187=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1188=VECTOR('',#1187,1.00000000000000000E3); +#1189=LINE('',#1186,#1188); +#1190=DEFINITIONAL_REPRESENTATION('',(#1189),#1); +#1191=PCURVE('',#116,#1190); +#1192=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1193=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1194=VECTOR('',#1193,1.00000000000000000E3); +#1195=LINE('',#1192,#1194); +#1196=DEFINITIONAL_REPRESENTATION('',(#1195),#1); +#1197=PCURVE('',#121,#1196); +#1198=SURFACE_CURVE('',#1185,(#1191,#1197),.CURVE_3D.); +#1199=EDGE_CURVE('',#263,#267,#1198,.T.); +#1200=CARTESIAN_POINT('',(3.00000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#1201=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1202=VECTOR('',#1201,1.00000000000000000E3); +#1203=LINE('',#1200,#1202); +#1204=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#1205=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1206=VECTOR('',#1205,1.00000000000000000E3); +#1207=LINE('',#1204,#1206); +#1208=DEFINITIONAL_REPRESENTATION('',(#1207),#1); +#1209=PCURVE('',#121,#1208); +#1210=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#1211=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1212=VECTOR('',#1211,1.00000000000000000E3); +#1213=LINE('',#1210,#1212); +#1214=DEFINITIONAL_REPRESENTATION('',(#1213),#1); +#1215=PCURVE('',#181,#1214); +#1216=SURFACE_CURVE('',#1203,(#1209,#1215),.CURVE_3D.); +#1217=EDGE_CURVE('',#267,#269,#1216,.T.); +#1218=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-3.00000000000000000E3)); +#1219=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1220=VECTOR('',#1219,1.00000000000000000E3); +#1221=LINE('',#1218,#1220); +#1222=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#1223=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1224=VECTOR('',#1223,1.00000000000000000E3); +#1225=LINE('',#1222,#1224); +#1226=DEFINITIONAL_REPRESENTATION('',(#1225),#1); +#1227=PCURVE('',#121,#1226); +#1228=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#1229=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1230=VECTOR('',#1229,1.00000000000000000E3); +#1231=LINE('',#1228,#1230); +#1232=DEFINITIONAL_REPRESENTATION('',(#1231),#1); +#1233=PCURVE('',#151,#1232); +#1234=SURFACE_CURVE('',#1221,(#1227,#1233),.CURVE_3D.); +#1235=EDGE_CURVE('',#265,#269,#1234,.T.); +#1236=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#1237=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1238=VECTOR('',#1237,1.00000000000000000E3); +#1239=LINE('',#1236,#1238); +#1240=CARTESIAN_POINT('',(2.50000000000000000E3,2.50000000000000000E3)); +#1241=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1242=VECTOR('',#1241,1.00000000000000000E3); +#1243=LINE('',#1240,#1242); +#1244=DEFINITIONAL_REPRESENTATION('',(#1243),#1); +#1245=PCURVE('',#126,#1244); +#1246=CARTESIAN_POINT('',(2.50000000000000000E3,2.50000000000000000E3)); +#1247=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1248=VECTOR('',#1247,1.00000000000000000E3); +#1249=LINE('',#1246,#1248); +#1250=DEFINITIONAL_REPRESENTATION('',(#1249),#1); +#1251=PCURVE('',#141,#1250); +#1252=SURFACE_CURVE('',#1239,(#1245,#1251),.CURVE_3D.); +#1253=EDGE_CURVE('',#245,#271,#1252,.T.); +#1254=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#1255=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1256=VECTOR('',#1255,1.00000000000000000E3); +#1257=LINE('',#1254,#1256); +#1258=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#1259=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1260=VECTOR('',#1259,1.00000000000000000E3); +#1261=LINE('',#1258,#1260); +#1262=DEFINITIONAL_REPRESENTATION('',(#1261),#1); +#1263=PCURVE('',#126,#1262); +#1264=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1265=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1266=VECTOR('',#1265,1.00000000000000000E3); +#1267=LINE('',#1264,#1266); +#1268=DEFINITIONAL_REPRESENTATION('',(#1267),#1); +#1269=PCURVE('',#131,#1268); +#1270=SURFACE_CURVE('',#1257,(#1263,#1269),.CURVE_3D.); +#1271=EDGE_CURVE('',#273,#271,#1270,.T.); +#1272=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#1273=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1274=VECTOR('',#1273,1.00000000000000000E3); +#1275=LINE('',#1272,#1274); +#1276=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#1277=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1278=VECTOR('',#1277,1.00000000000000000E3); +#1279=LINE('',#1276,#1278); +#1280=DEFINITIONAL_REPRESENTATION('',(#1279),#1); +#1281=PCURVE('',#126,#1280); +#1282=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#1283=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1284=VECTOR('',#1283,1.00000000000000000E3); +#1285=LINE('',#1282,#1284); +#1286=DEFINITIONAL_REPRESENTATION('',(#1285),#1); +#1287=PCURVE('',#136,#1286); +#1288=SURFACE_CURVE('',#1275,(#1281,#1287),.CURVE_3D.); +#1289=EDGE_CURVE('',#239,#273,#1288,.T.); +#1290=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-3.50000000000000000E3)); +#1291=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1292=VECTOR('',#1291,1.00000000000000000E3); +#1293=LINE('',#1290,#1292); +#1294=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#1295=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1296=VECTOR('',#1295,1.00000000000000000E3); +#1297=LINE('',#1294,#1296); +#1298=DEFINITIONAL_REPRESENTATION('',(#1297),#1); +#1299=PCURVE('',#131,#1298); +#1300=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#1301=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1302=VECTOR('',#1301,1.00000000000000000E3); +#1303=LINE('',#1300,#1302); +#1304=DEFINITIONAL_REPRESENTATION('',(#1303),#1); +#1305=PCURVE('',#146,#1304); +#1306=SURFACE_CURVE('',#1293,(#1299,#1305),.CURVE_3D.); +#1307=EDGE_CURVE('',#271,#275,#1306,.T.); +#1308=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#1309=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1310=VECTOR('',#1309,1.00000000000000000E3); +#1311=LINE('',#1308,#1310); +#1312=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#1313=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1314=VECTOR('',#1313,1.00000000000000000E3); +#1315=LINE('',#1312,#1314); +#1316=DEFINITIONAL_REPRESENTATION('',(#1315),#1); +#1317=PCURVE('',#131,#1316); +#1318=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#1319=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1320=VECTOR('',#1319,1.00000000000000000E3); +#1321=LINE('',#1318,#1320); +#1322=DEFINITIONAL_REPRESENTATION('',(#1321),#1); +#1323=PCURVE('',#191,#1322); +#1324=SURFACE_CURVE('',#1311,(#1317,#1323),.CURVE_3D.); +#1325=EDGE_CURVE('',#277,#275,#1324,.T.); +#1326=CARTESIAN_POINT('',(1.50000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#1327=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1328=VECTOR('',#1327,1.00000000000000000E3); +#1329=LINE('',#1326,#1328); +#1330=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1331=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1332=VECTOR('',#1331,1.00000000000000000E3); +#1333=LINE('',#1330,#1332); +#1334=DEFINITIONAL_REPRESENTATION('',(#1333),#1); +#1335=PCURVE('',#131,#1334); +#1336=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#1337=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1338=VECTOR('',#1337,1.00000000000000000E3); +#1339=LINE('',#1336,#1338); +#1340=DEFINITIONAL_REPRESENTATION('',(#1339),#1); +#1341=PCURVE('',#136,#1340); +#1342=SURFACE_CURVE('',#1329,(#1335,#1341),.CURVE_3D.); +#1343=EDGE_CURVE('',#273,#277,#1342,.T.); +#1344=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#1345=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1346=VECTOR('',#1345,1.00000000000000000E3); +#1347=LINE('',#1344,#1346); +#1348=CARTESIAN_POINT('',(2.50000000000000000E3,1.50000000000000000E3)); +#1349=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1350=VECTOR('',#1349,1.00000000000000000E3); +#1351=LINE('',#1348,#1350); +#1352=DEFINITIONAL_REPRESENTATION('',(#1351),#1); +#1353=PCURVE('',#136,#1352); +#1354=CARTESIAN_POINT('',(2.50000000000000000E3,1.50000000000000000E3)); +#1355=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1356=VECTOR('',#1355,1.00000000000000000E3); +#1357=LINE('',#1354,#1356); +#1358=DEFINITIONAL_REPRESENTATION('',(#1357),#1); +#1359=PCURVE('',#196,#1358); +#1360=SURFACE_CURVE('',#1347,(#1353,#1359),.CURVE_3D.); +#1361=EDGE_CURVE('',#261,#277,#1360,.T.); +#1362=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-3.00000000000000000E3)); +#1363=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1364=VECTOR('',#1363,1.00000000000000000E3); +#1365=LINE('',#1362,#1364); +#1366=CARTESIAN_POINT('',(3.00000000000000000E3,2.50000000000000000E3)); +#1367=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1368=VECTOR('',#1367,1.00000000000000000E3); +#1369=LINE('',#1366,#1368); +#1370=DEFINITIONAL_REPRESENTATION('',(#1369),#1); +#1371=PCURVE('',#141,#1370); +#1372=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#1373=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1374=VECTOR('',#1373,1.00000000000000000E3); +#1375=LINE('',#1372,#1374); +#1376=DEFINITIONAL_REPRESENTATION('',(#1375),#1); +#1377=PCURVE('',#151,#1376); +#1378=SURFACE_CURVE('',#1365,(#1371,#1377),.CURVE_3D.); +#1379=EDGE_CURVE('',#265,#279,#1378,.T.); +#1380=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3,-3.50000000000000000E3)); +#1381=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1382=VECTOR('',#1381,1.00000000000000000E3); +#1383=LINE('',#1380,#1382); +#1384=CARTESIAN_POINT('',(2.50000000000000000E3,3.00000000000000000E3)); +#1385=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1386=VECTOR('',#1385,1.00000000000000000E3); +#1387=LINE('',#1384,#1386); +#1388=DEFINITIONAL_REPRESENTATION('',(#1387),#1); +#1389=PCURVE('',#141,#1388); +#1390=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#1391=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1392=VECTOR('',#1391,1.00000000000000000E3); +#1393=LINE('',#1390,#1392); +#1394=DEFINITIONAL_REPRESENTATION('',(#1393),#1); +#1395=PCURVE('',#146,#1394); +#1396=SURFACE_CURVE('',#1383,(#1389,#1395),.CURVE_3D.); +#1397=EDGE_CURVE('',#271,#279,#1396,.T.); +#1398=CARTESIAN_POINT('',(1.50000000000000000E3,3.50000000000000000E3,-3.50000000000000000E3)); +#1399=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1400=VECTOR('',#1399,1.00000000000000000E3); +#1401=LINE('',#1398,#1400); +#1402=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#1403=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1404=VECTOR('',#1403,1.00000000000000000E3); +#1405=LINE('',#1402,#1404); +#1406=DEFINITIONAL_REPRESENTATION('',(#1405),#1); +#1407=PCURVE('',#146,#1406); +#1408=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#1409=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1410=VECTOR('',#1409,1.00000000000000000E3); +#1411=LINE('',#1408,#1410); +#1412=DEFINITIONAL_REPRESENTATION('',(#1411),#1); +#1413=PCURVE('',#151,#1412); +#1414=SURFACE_CURVE('',#1401,(#1407,#1413),.CURVE_3D.); +#1415=EDGE_CURVE('',#279,#281,#1414,.T.); +#1416=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-3.50000000000000000E3)); +#1417=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1418=VECTOR('',#1417,1.00000000000000000E3); +#1419=LINE('',#1416,#1418); +#1420=CARTESIAN_POINT('',(2.50000000000000000E3,1.50000000000000000E3)); +#1421=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1422=VECTOR('',#1421,1.00000000000000000E3); +#1423=LINE('',#1420,#1422); +#1424=DEFINITIONAL_REPRESENTATION('',(#1423),#1); +#1425=PCURVE('',#146,#1424); +#1426=CARTESIAN_POINT('',(2.50000000000000000E3,1.50000000000000000E3)); +#1427=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1428=VECTOR('',#1427,1.00000000000000000E3); +#1429=LINE('',#1426,#1428); +#1430=DEFINITIONAL_REPRESENTATION('',(#1429),#1); +#1431=PCURVE('',#206,#1430); +#1432=SURFACE_CURVE('',#1419,(#1425,#1431),.CURVE_3D.); +#1433=EDGE_CURVE('',#275,#281,#1432,.T.); +#1434=CARTESIAN_POINT('',(3.00000000000000000E3,3.50000000000000000E3,-3.00000000000000000E3)); +#1435=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1436=VECTOR('',#1435,1.00000000000000000E3); +#1437=LINE('',#1434,#1436); +#1438=CARTESIAN_POINT('',(1.50000000000000000E3,2.50000000000000000E3)); +#1439=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1440=VECTOR('',#1439,1.00000000000000000E3); +#1441=LINE('',#1438,#1440); +#1442=DEFINITIONAL_REPRESENTATION('',(#1441),#1); +#1443=PCURVE('',#151,#1442); +#1444=CARTESIAN_POINT('',(1.50000000000000000E3,2.50000000000000000E3)); +#1445=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1446=VECTOR('',#1445,1.00000000000000000E3); +#1447=LINE('',#1444,#1446); +#1448=DEFINITIONAL_REPRESENTATION('',(#1447),#1); +#1449=PCURVE('',#211,#1448); +#1450=SURFACE_CURVE('',#1437,(#1443,#1449),.CURVE_3D.); +#1451=EDGE_CURVE('',#269,#281,#1450,.T.); +#1452=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#1453=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1454=VECTOR('',#1453,1.00000000000000000E3); +#1455=LINE('',#1452,#1454); +#1456=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1457=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1458=VECTOR('',#1457,1.00000000000000000E3); +#1459=LINE('',#1456,#1458); +#1460=DEFINITIONAL_REPRESENTATION('',(#1459),#1); +#1461=PCURVE('',#156,#1460); +#1462=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#1463=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1464=VECTOR('',#1463,1.00000000000000000E3); +#1465=LINE('',#1462,#1464); +#1466=DEFINITIONAL_REPRESENTATION('',(#1465),#1); +#1467=PCURVE('',#166,#1466); +#1468=SURFACE_CURVE('',#1455,(#1461,#1467),.CURVE_3D.); +#1469=EDGE_CURVE('',#283,#285,#1468,.T.); +#1470=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#1471=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1472=VECTOR('',#1471,1.00000000000000000E3); +#1473=LINE('',#1470,#1472); +#1474=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#1475=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1476=VECTOR('',#1475,1.00000000000000000E3); +#1477=LINE('',#1474,#1476); +#1478=DEFINITIONAL_REPRESENTATION('',(#1477),#1); +#1479=PCURVE('',#156,#1478); +#1480=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#1481=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1482=VECTOR('',#1481,1.00000000000000000E3); +#1483=LINE('',#1480,#1482); +#1484=DEFINITIONAL_REPRESENTATION('',(#1483),#1); +#1485=PCURVE('',#186,#1484); +#1486=SURFACE_CURVE('',#1473,(#1479,#1485),.CURVE_3D.); +#1487=EDGE_CURVE('',#285,#287,#1486,.T.); +#1488=CARTESIAN_POINT('',(4.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#1489=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1490=VECTOR('',#1489,1.00000000000000000E3); +#1491=LINE('',#1488,#1490); +#1492=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#1493=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1494=VECTOR('',#1493,1.00000000000000000E3); +#1495=LINE('',#1492,#1494); +#1496=DEFINITIONAL_REPRESENTATION('',(#1495),#1); +#1497=PCURVE('',#156,#1496); +#1498=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#1499=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1500=VECTOR('',#1499,1.00000000000000000E3); +#1501=LINE('',#1498,#1500); +#1502=DEFINITIONAL_REPRESENTATION('',(#1501),#1); +#1503=PCURVE('',#171,#1502); +#1504=SURFACE_CURVE('',#1491,(#1497,#1503),.CURVE_3D.); +#1505=EDGE_CURVE('',#289,#287,#1504,.T.); +#1506=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#1507=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1508=VECTOR('',#1507,1.00000000000000000E3); +#1509=LINE('',#1506,#1508); +#1510=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#1511=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1512=VECTOR('',#1511,1.00000000000000000E3); +#1513=LINE('',#1510,#1512); +#1514=DEFINITIONAL_REPRESENTATION('',(#1513),#1); +#1515=PCURVE('',#156,#1514); +#1516=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#1517=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1518=VECTOR('',#1517,1.00000000000000000E3); +#1519=LINE('',#1516,#1518); +#1520=DEFINITIONAL_REPRESENTATION('',(#1519),#1); +#1521=PCURVE('',#161,#1520); +#1522=SURFACE_CURVE('',#1509,(#1515,#1521),.CURVE_3D.); +#1523=EDGE_CURVE('',#283,#289,#1522,.T.); +#1524=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#1525=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1526=VECTOR('',#1525,1.00000000000000000E3); +#1527=LINE('',#1524,#1526); +#1528=CARTESIAN_POINT('',(1.50000000000000000E3,2.50000000000000000E3)); +#1529=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1530=VECTOR('',#1529,1.00000000000000000E3); +#1531=LINE('',#1528,#1530); +#1532=DEFINITIONAL_REPRESENTATION('',(#1531),#1); +#1533=PCURVE('',#161,#1532); +#1534=CARTESIAN_POINT('',(1.50000000000000000E3,2.50000000000000000E3)); +#1535=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1536=VECTOR('',#1535,1.00000000000000000E3); +#1537=LINE('',#1534,#1536); +#1538=DEFINITIONAL_REPRESENTATION('',(#1537),#1); +#1539=PCURVE('',#176,#1538); +#1540=SURFACE_CURVE('',#1527,(#1533,#1539),.CURVE_3D.); +#1541=EDGE_CURVE('',#255,#289,#1540,.T.); +#1542=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-5.00000000000000000E2)); +#1543=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1544=VECTOR('',#1543,1.00000000000000000E3); +#1545=LINE('',#1542,#1544); +#1546=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#1547=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1548=VECTOR('',#1547,1.00000000000000000E3); +#1549=LINE('',#1546,#1548); +#1550=DEFINITIONAL_REPRESENTATION('',(#1549),#1); +#1551=PCURVE('',#161,#1550); +#1552=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#1553=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1554=VECTOR('',#1553,1.00000000000000000E3); +#1555=LINE('',#1552,#1554); +#1556=DEFINITIONAL_REPRESENTATION('',(#1555),#1); +#1557=PCURVE('',#166,#1556); +#1558=SURFACE_CURVE('',#1545,(#1551,#1557),.CURVE_3D.); +#1559=EDGE_CURVE('',#251,#283,#1558,.T.); +#1560=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#1561=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1562=VECTOR('',#1561,1.00000000000000000E3); +#1563=LINE('',#1560,#1562); +#1564=CARTESIAN_POINT('',(2.50000000000000000E3,1.50000000000000000E3)); +#1565=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1566=VECTOR('',#1565,1.00000000000000000E3); +#1567=LINE('',#1564,#1566); +#1568=DEFINITIONAL_REPRESENTATION('',(#1567),#1); +#1569=PCURVE('',#166,#1568); +#1570=CARTESIAN_POINT('',(2.50000000000000000E3,1.50000000000000000E3)); +#1571=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1572=VECTOR('',#1571,1.00000000000000000E3); +#1573=LINE('',#1570,#1572); +#1574=DEFINITIONAL_REPRESENTATION('',(#1573),#1); +#1575=PCURVE('',#196,#1574); +#1576=SURFACE_CURVE('',#1563,(#1569,#1575),.CURVE_3D.); +#1577=EDGE_CURVE('',#261,#285,#1576,.T.); +#1578=CARTESIAN_POINT('',(4.50000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#1579=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1580=VECTOR('',#1579,1.00000000000000000E3); +#1581=LINE('',#1578,#1580); +#1582=CARTESIAN_POINT('',(2.50000000000000000E3,2.50000000000000000E3)); +#1583=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1584=VECTOR('',#1583,1.00000000000000000E3); +#1585=LINE('',#1582,#1584); +#1586=DEFINITIONAL_REPRESENTATION('',(#1585),#1); +#1587=PCURVE('',#171,#1586); +#1588=CARTESIAN_POINT('',(2.50000000000000000E3,2.50000000000000000E3)); +#1589=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1590=VECTOR('',#1589,1.00000000000000000E3); +#1591=LINE('',#1588,#1590); +#1592=DEFINITIONAL_REPRESENTATION('',(#1591),#1); +#1593=PCURVE('',#201,#1592); +#1594=SURFACE_CURVE('',#1581,(#1587,#1593),.CURVE_3D.); +#1595=EDGE_CURVE('',#287,#291,#1594,.T.); +#1596=CARTESIAN_POINT('',(4.50000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#1597=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1598=VECTOR('',#1597,1.00000000000000000E3); +#1599=LINE('',#1596,#1598); +#1600=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#1601=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1602=VECTOR('',#1601,1.00000000000000000E3); +#1603=LINE('',#1600,#1602); +#1604=DEFINITIONAL_REPRESENTATION('',(#1603),#1); +#1605=PCURVE('',#171,#1604); +#1606=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#1607=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1608=VECTOR('',#1607,1.00000000000000000E3); +#1609=LINE('',#1606,#1608); +#1610=DEFINITIONAL_REPRESENTATION('',(#1609),#1); +#1611=PCURVE('',#181,#1610); +#1612=SURFACE_CURVE('',#1599,(#1605,#1611),.CURVE_3D.); +#1613=EDGE_CURVE('',#293,#291,#1612,.T.); +#1614=CARTESIAN_POINT('',(4.50000000000000000E3,3.00000000000000000E3,-5.00000000000000000E2)); +#1615=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1616=VECTOR('',#1615,1.00000000000000000E3); +#1617=LINE('',#1614,#1616); +#1618=CARTESIAN_POINT('',(0.00000000000000000E0,2.50000000000000000E3)); +#1619=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1620=VECTOR('',#1619,1.00000000000000000E3); +#1621=LINE('',#1618,#1620); +#1622=DEFINITIONAL_REPRESENTATION('',(#1621),#1); +#1623=PCURVE('',#171,#1622); +#1624=CARTESIAN_POINT('',(3.00000000000000000E3,2.50000000000000000E3)); +#1625=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1626=VECTOR('',#1625,1.00000000000000000E3); +#1627=LINE('',#1624,#1626); +#1628=DEFINITIONAL_REPRESENTATION('',(#1627),#1); +#1629=PCURVE('',#176,#1628); +#1630=SURFACE_CURVE('',#1617,(#1623,#1629),.CURVE_3D.); +#1631=EDGE_CURVE('',#289,#293,#1630,.T.); +#1632=CARTESIAN_POINT('',(3.00000000000000000E3,3.50000000000000000E3,-5.00000000000000000E2)); +#1633=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1634=VECTOR('',#1633,1.00000000000000000E3); +#1635=LINE('',#1632,#1634); +#1636=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3)); +#1637=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1638=VECTOR('',#1637,1.00000000000000000E3); +#1639=LINE('',#1636,#1638); +#1640=DEFINITIONAL_REPRESENTATION('',(#1639),#1); +#1641=PCURVE('',#176,#1640); +#1642=CARTESIAN_POINT('',(1.50000000000000000E3,0.00000000000000000E0)); +#1643=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1644=VECTOR('',#1643,1.00000000000000000E3); +#1645=LINE('',#1642,#1644); +#1646=DEFINITIONAL_REPRESENTATION('',(#1645),#1); +#1647=PCURVE('',#181,#1646); +#1648=SURFACE_CURVE('',#1635,(#1641,#1647),.CURVE_3D.); +#1649=EDGE_CURVE('',#267,#293,#1648,.T.); +#1650=CARTESIAN_POINT('',(3.00000000000000000E3,3.50000000000000000E3,-3.00000000000000000E3)); +#1651=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1652=VECTOR('',#1651,1.00000000000000000E3); +#1653=LINE('',#1650,#1652); +#1654=CARTESIAN_POINT('',(1.50000000000000000E3,2.50000000000000000E3)); +#1655=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1656=VECTOR('',#1655,1.00000000000000000E3); +#1657=LINE('',#1654,#1656); +#1658=DEFINITIONAL_REPRESENTATION('',(#1657),#1); +#1659=PCURVE('',#181,#1658); +#1660=CARTESIAN_POINT('',(1.50000000000000000E3,2.50000000000000000E3)); +#1661=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1662=VECTOR('',#1661,1.00000000000000000E3); +#1663=LINE('',#1660,#1662); +#1664=DEFINITIONAL_REPRESENTATION('',(#1663),#1); +#1665=PCURVE('',#211,#1664); +#1666=SURFACE_CURVE('',#1653,(#1659,#1665),.CURVE_3D.); +#1667=EDGE_CURVE('',#269,#291,#1666,.T.); +#1668=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-3.00000000000000000E3)); +#1669=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1670=VECTOR('',#1669,1.00000000000000000E3); +#1671=LINE('',#1668,#1670); +#1672=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0)); +#1673=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1674=VECTOR('',#1673,1.00000000000000000E3); +#1675=LINE('',#1672,#1674); +#1676=DEFINITIONAL_REPRESENTATION('',(#1675),#1); +#1677=PCURVE('',#186,#1676); +#1678=CARTESIAN_POINT('',(2.50000000000000000E3,3.00000000000000000E3)); +#1679=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1680=VECTOR('',#1679,1.00000000000000000E3); +#1681=LINE('',#1678,#1680); +#1682=DEFINITIONAL_REPRESENTATION('',(#1681),#1); +#1683=PCURVE('',#196,#1682); +#1684=SURFACE_CURVE('',#1671,(#1677,#1683),.CURVE_3D.); +#1685=EDGE_CURVE('',#285,#295,#1684,.T.); +#1686=CARTESIAN_POINT('',(4.50000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#1687=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1688=VECTOR('',#1687,1.00000000000000000E3); +#1689=LINE('',#1686,#1688); +#1690=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#1691=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1692=VECTOR('',#1691,1.00000000000000000E3); +#1693=LINE('',#1690,#1692); +#1694=DEFINITIONAL_REPRESENTATION('',(#1693),#1); +#1695=PCURVE('',#186,#1694); +#1696=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#1697=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1698=VECTOR('',#1697,1.00000000000000000E3); +#1699=LINE('',#1696,#1698); +#1700=DEFINITIONAL_REPRESENTATION('',(#1699),#1); +#1701=PCURVE('',#191,#1700); +#1702=SURFACE_CURVE('',#1689,(#1695,#1701),.CURVE_3D.); +#1703=EDGE_CURVE('',#295,#297,#1702,.T.); +#1704=CARTESIAN_POINT('',(4.50000000000000000E3,3.00000000000000000E3,-3.00000000000000000E3)); +#1705=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1706=VECTOR('',#1705,1.00000000000000000E3); +#1707=LINE('',#1704,#1706); +#1708=CARTESIAN_POINT('',(2.50000000000000000E3,2.50000000000000000E3)); +#1709=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1710=VECTOR('',#1709,1.00000000000000000E3); +#1711=LINE('',#1708,#1710); +#1712=DEFINITIONAL_REPRESENTATION('',(#1711),#1); +#1713=PCURVE('',#186,#1712); +#1714=CARTESIAN_POINT('',(2.50000000000000000E3,2.50000000000000000E3)); +#1715=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1716=VECTOR('',#1715,1.00000000000000000E3); +#1717=LINE('',#1714,#1716); +#1718=DEFINITIONAL_REPRESENTATION('',(#1717),#1); +#1719=PCURVE('',#201,#1718); +#1720=SURFACE_CURVE('',#1707,(#1713,#1719),.CURVE_3D.); +#1721=EDGE_CURVE('',#287,#297,#1720,.T.); +#1722=CARTESIAN_POINT('',(3.00000000000000000E3,3.00000000000000000E3,-3.50000000000000000E3)); +#1723=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1724=VECTOR('',#1723,1.00000000000000000E3); +#1725=LINE('',#1722,#1724); +#1726=CARTESIAN_POINT('',(2.50000000000000000E3,1.50000000000000000E3)); +#1727=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1728=VECTOR('',#1727,1.00000000000000000E3); +#1729=LINE('',#1726,#1728); +#1730=DEFINITIONAL_REPRESENTATION('',(#1729),#1); +#1731=PCURVE('',#191,#1730); +#1732=CARTESIAN_POINT('',(2.50000000000000000E3,1.50000000000000000E3)); +#1733=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1734=VECTOR('',#1733,1.00000000000000000E3); +#1735=LINE('',#1732,#1734); +#1736=DEFINITIONAL_REPRESENTATION('',(#1735),#1); +#1737=PCURVE('',#206,#1736); +#1738=SURFACE_CURVE('',#1725,(#1731,#1737),.CURVE_3D.); +#1739=EDGE_CURVE('',#275,#297,#1738,.T.); +#1740=CARTESIAN_POINT('',(3.00000000000000000E3,5.00000000000000000E2,-3.50000000000000000E3)); +#1741=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1742=VECTOR('',#1741,1.00000000000000000E3); +#1743=LINE('',#1740,#1742); +#1744=CARTESIAN_POINT('',(0.00000000000000000E0,1.50000000000000000E3)); +#1745=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1746=VECTOR('',#1745,1.00000000000000000E3); +#1747=LINE('',#1744,#1746); +#1748=DEFINITIONAL_REPRESENTATION('',(#1747),#1); +#1749=PCURVE('',#191,#1748); +#1750=CARTESIAN_POINT('',(3.00000000000000000E3,1.50000000000000000E3)); +#1751=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1752=VECTOR('',#1751,1.00000000000000000E3); +#1753=LINE('',#1750,#1752); +#1754=DEFINITIONAL_REPRESENTATION('',(#1753),#1); +#1755=PCURVE('',#196,#1754); +#1756=SURFACE_CURVE('',#1743,(#1749,#1755),.CURVE_3D.); +#1757=EDGE_CURVE('',#277,#295,#1756,.T.); +#1758=CARTESIAN_POINT('',(4.50000000000000000E3,3.00000000000000000E3,-3.50000000000000000E3)); +#1759=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#1760=VECTOR('',#1759,1.00000000000000000E3); +#1761=LINE('',#1758,#1760); +#1762=CARTESIAN_POINT('',(3.00000000000000000E3,2.50000000000000000E3)); +#1763=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1764=VECTOR('',#1763,1.00000000000000000E3); +#1765=LINE('',#1762,#1764); +#1766=DEFINITIONAL_REPRESENTATION('',(#1765),#1); +#1767=PCURVE('',#201,#1766); +#1768=CARTESIAN_POINT('',(2.50000000000000000E3,3.00000000000000000E3)); +#1769=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1770=VECTOR('',#1769,1.00000000000000000E3); +#1771=LINE('',#1768,#1770); +#1772=DEFINITIONAL_REPRESENTATION('',(#1771),#1); +#1773=PCURVE('',#206,#1772); +#1774=SURFACE_CURVE('',#1761,(#1767,#1773),.CURVE_3D.); +#1775=EDGE_CURVE('',#297,#299,#1774,.T.); +#1776=CARTESIAN_POINT('',(4.50000000000000000E3,3.50000000000000000E3,-3.00000000000000000E3)); +#1777=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#1778=VECTOR('',#1777,1.00000000000000000E3); +#1779=LINE('',#1776,#1778); +#1780=CARTESIAN_POINT('',(2.50000000000000000E3,3.00000000000000000E3)); +#1781=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1782=VECTOR('',#1781,1.00000000000000000E3); +#1783=LINE('',#1780,#1782); +#1784=DEFINITIONAL_REPRESENTATION('',(#1783),#1); +#1785=PCURVE('',#201,#1784); +#1786=CARTESIAN_POINT('',(3.00000000000000000E3,2.50000000000000000E3)); +#1787=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1788=VECTOR('',#1787,1.00000000000000000E3); +#1789=LINE('',#1786,#1788); +#1790=DEFINITIONAL_REPRESENTATION('',(#1789),#1); +#1791=PCURVE('',#211,#1790); +#1792=SURFACE_CURVE('',#1779,(#1785,#1791),.CURVE_3D.); +#1793=EDGE_CURVE('',#291,#299,#1792,.T.); +#1794=CARTESIAN_POINT('',(3.00000000000000000E3,3.50000000000000000E3,-3.50000000000000000E3)); +#1795=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#1796=VECTOR('',#1795,1.00000000000000000E3); +#1797=LINE('',#1794,#1796); +#1798=CARTESIAN_POINT('',(3.00000000000000000E3,1.50000000000000000E3)); +#1799=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#1800=VECTOR('',#1799,1.00000000000000000E3); +#1801=LINE('',#1798,#1800); +#1802=DEFINITIONAL_REPRESENTATION('',(#1801),#1); +#1803=PCURVE('',#206,#1802); +#1804=CARTESIAN_POINT('',(1.50000000000000000E3,3.00000000000000000E3)); +#1805=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#1806=VECTOR('',#1805,1.00000000000000000E3); +#1807=LINE('',#1804,#1806); +#1808=DEFINITIONAL_REPRESENTATION('',(#1807),#1); +#1809=PCURVE('',#211,#1808); +#1810=SURFACE_CURVE('',#1797,(#1803,#1809),.CURVE_3D.); +#1811=EDGE_CURVE('',#281,#299,#1810,.T.); +#1812=ORIENTED_EDGE('',*,*,#317,.T.); +#1813=ORIENTED_EDGE('',*,*,#335,.F.); +#1814=ORIENTED_EDGE('',*,*,#353,.F.); +#1815=ORIENTED_EDGE('',*,*,#371,.T.); +#1816=EDGE_LOOP('',(#1812,#1813,#1814,#1815)); +#1817=FACE_OUTER_BOUND('',#1816,.T.); +#1818=ADVANCED_FACE('left-0',(#1817),#6,.T.); +#1819=ORIENTED_EDGE('',*,*,#389,.T.); +#1820=ORIENTED_EDGE('',*,*,#407,.F.); +#1821=ORIENTED_EDGE('',*,*,#371,.F.); +#1822=ORIENTED_EDGE('',*,*,#425,.T.); +#1823=EDGE_LOOP('',(#1819,#1820,#1821,#1822)); +#1824=FACE_OUTER_BOUND('',#1823,.T.); +#1825=ADVANCED_FACE('front-1',(#1824),#11,.T.); +#1826=ORIENTED_EDGE('',*,*,#443,.T.); +#1827=ORIENTED_EDGE('',*,*,#461,.F.); +#1828=ORIENTED_EDGE('',*,*,#425,.F.); +#1829=ORIENTED_EDGE('',*,*,#353,.T.); +#1830=EDGE_LOOP('',(#1826,#1827,#1828,#1829)); +#1831=FACE_OUTER_BOUND('',#1830,.T.); +#1832=ADVANCED_FACE('bottom-2',(#1831),#16,.T.); +#1833=ORIENTED_EDGE('',*,*,#479,.T.); +#1834=ORIENTED_EDGE('',*,*,#497,.F.); +#1835=ORIENTED_EDGE('',*,*,#317,.F.); +#1836=ORIENTED_EDGE('',*,*,#515,.T.); +#1837=EDGE_LOOP('',(#1833,#1834,#1835,#1836)); +#1838=FACE_OUTER_BOUND('',#1837,.T.); +#1839=ADVANCED_FACE('left-3',(#1838),#21,.T.); +#1840=ORIENTED_EDGE('',*,*,#533,.T.); +#1841=ORIENTED_EDGE('',*,*,#551,.F.); +#1842=ORIENTED_EDGE('',*,*,#515,.F.); +#1843=ORIENTED_EDGE('',*,*,#407,.T.); +#1844=EDGE_LOOP('',(#1840,#1841,#1842,#1843)); +#1845=FACE_OUTER_BOUND('',#1844,.T.); +#1846=ADVANCED_FACE('front-4',(#1845),#26,.T.); +#1847=ORIENTED_EDGE('',*,*,#551,.T.); +#1848=ORIENTED_EDGE('',*,*,#569,.T.); +#1849=ORIENTED_EDGE('',*,*,#587,.F.); +#1850=ORIENTED_EDGE('',*,*,#479,.F.); +#1851=EDGE_LOOP('',(#1847,#1848,#1849,#1850)); +#1852=FACE_OUTER_BOUND('',#1851,.T.); +#1853=ADVANCED_FACE('top-5',(#1852),#31,.T.); +#1854=ORIENTED_EDGE('',*,*,#605,.T.); +#1855=ORIENTED_EDGE('',*,*,#623,.F.); +#1856=ORIENTED_EDGE('',*,*,#641,.F.); +#1857=ORIENTED_EDGE('',*,*,#335,.T.); +#1858=EDGE_LOOP('',(#1854,#1855,#1856,#1857)); +#1859=FACE_OUTER_BOUND('',#1858,.T.); +#1860=ADVANCED_FACE('left-6',(#1859),#36,.T.); +#1861=ORIENTED_EDGE('',*,*,#623,.T.); +#1862=ORIENTED_EDGE('',*,*,#659,.T.); +#1863=ORIENTED_EDGE('',*,*,#677,.F.); +#1864=ORIENTED_EDGE('',*,*,#695,.F.); +#1865=EDGE_LOOP('',(#1861,#1862,#1863,#1864)); +#1866=FACE_OUTER_BOUND('',#1865,.T.); +#1867=ADVANCED_FACE('back-7',(#1866),#41,.T.); +#1868=ORIENTED_EDGE('',*,*,#695,.T.); +#1869=ORIENTED_EDGE('',*,*,#713,.F.); +#1870=ORIENTED_EDGE('',*,*,#443,.F.); +#1871=ORIENTED_EDGE('',*,*,#641,.T.); +#1872=EDGE_LOOP('',(#1868,#1869,#1870,#1871)); +#1873=FACE_OUTER_BOUND('',#1872,.T.); +#1874=ADVANCED_FACE('bottom-8',(#1873),#46,.T.); +#1875=ORIENTED_EDGE('',*,*,#731,.T.); +#1876=ORIENTED_EDGE('',*,*,#749,.F.); +#1877=ORIENTED_EDGE('',*,*,#605,.F.); +#1878=ORIENTED_EDGE('',*,*,#497,.T.); +#1879=EDGE_LOOP('',(#1875,#1876,#1877,#1878)); +#1880=FACE_OUTER_BOUND('',#1879,.T.); +#1881=ADVANCED_FACE('left-9',(#1880),#51,.T.); +#1882=ORIENTED_EDGE('',*,*,#749,.T.); +#1883=ORIENTED_EDGE('',*,*,#767,.T.); +#1884=ORIENTED_EDGE('',*,*,#785,.F.); +#1885=ORIENTED_EDGE('',*,*,#659,.F.); +#1886=EDGE_LOOP('',(#1882,#1883,#1884,#1885)); +#1887=FACE_OUTER_BOUND('',#1886,.T.); +#1888=ADVANCED_FACE('back-10',(#1887),#56,.T.); +#1889=ORIENTED_EDGE('',*,*,#587,.T.); +#1890=ORIENTED_EDGE('',*,*,#803,.T.); +#1891=ORIENTED_EDGE('',*,*,#767,.F.); +#1892=ORIENTED_EDGE('',*,*,#731,.F.); +#1893=EDGE_LOOP('',(#1889,#1890,#1891,#1892)); +#1894=FACE_OUTER_BOUND('',#1893,.T.); +#1895=ADVANCED_FACE('top-11',(#1894),#61,.T.); +#1896=ORIENTED_EDGE('',*,*,#821,.T.); +#1897=ORIENTED_EDGE('',*,*,#839,.T.); +#1898=ORIENTED_EDGE('',*,*,#857,.F.); +#1899=ORIENTED_EDGE('',*,*,#875,.F.); +#1900=EDGE_LOOP('',(#1896,#1897,#1898,#1899)); +#1901=FACE_OUTER_BOUND('',#1900,.T.); +#1902=ADVANCED_FACE('right-12',(#1901),#66,.T.); +#1903=ORIENTED_EDGE('',*,*,#875,.T.); +#1904=ORIENTED_EDGE('',*,*,#893,.F.); +#1905=ORIENTED_EDGE('',*,*,#389,.F.); +#1906=ORIENTED_EDGE('',*,*,#911,.T.); +#1907=EDGE_LOOP('',(#1903,#1904,#1905,#1906)); +#1908=FACE_OUTER_BOUND('',#1907,.T.); +#1909=ADVANCED_FACE('front-13',(#1908),#71,.T.); +#1910=ORIENTED_EDGE('',*,*,#929,.T.); +#1911=ORIENTED_EDGE('',*,*,#821,.F.); +#1912=ORIENTED_EDGE('',*,*,#911,.F.); +#1913=ORIENTED_EDGE('',*,*,#461,.T.); +#1914=EDGE_LOOP('',(#1910,#1911,#1912,#1913)); +#1915=FACE_OUTER_BOUND('',#1914,.T.); +#1916=ADVANCED_FACE('bottom-14',(#1915),#76,.T.); +#1917=ORIENTED_EDGE('',*,*,#857,.T.); +#1918=ORIENTED_EDGE('',*,*,#947,.T.); +#1919=ORIENTED_EDGE('',*,*,#965,.F.); +#1920=ORIENTED_EDGE('',*,*,#983,.F.); +#1921=EDGE_LOOP('',(#1917,#1918,#1919,#1920)); +#1922=FACE_OUTER_BOUND('',#1921,.T.); +#1923=ADVANCED_FACE('right-15',(#1922),#81,.T.); +#1924=ORIENTED_EDGE('',*,*,#983,.T.); +#1925=ORIENTED_EDGE('',*,*,#1001,.F.); +#1926=ORIENTED_EDGE('',*,*,#533,.F.); +#1927=ORIENTED_EDGE('',*,*,#893,.T.); +#1928=EDGE_LOOP('',(#1924,#1925,#1926,#1927)); +#1929=FACE_OUTER_BOUND('',#1928,.T.); +#1930=ADVANCED_FACE('front-16',(#1929),#86,.T.); +#1931=ORIENTED_EDGE('',*,*,#1001,.T.); +#1932=ORIENTED_EDGE('',*,*,#965,.T.); +#1933=ORIENTED_EDGE('',*,*,#1019,.F.); +#1934=ORIENTED_EDGE('',*,*,#569,.F.); +#1935=EDGE_LOOP('',(#1931,#1932,#1933,#1934)); +#1936=FACE_OUTER_BOUND('',#1935,.T.); +#1937=ADVANCED_FACE('top-17',(#1936),#91,.T.); +#1938=ORIENTED_EDGE('',*,*,#1037,.T.); +#1939=ORIENTED_EDGE('',*,*,#1055,.T.); +#1940=ORIENTED_EDGE('',*,*,#1073,.F.); +#1941=ORIENTED_EDGE('',*,*,#839,.F.); +#1942=EDGE_LOOP('',(#1938,#1939,#1940,#1941)); +#1943=FACE_OUTER_BOUND('',#1942,.T.); +#1944=ADVANCED_FACE('right-18',(#1943),#96,.T.); +#1945=ORIENTED_EDGE('',*,*,#677,.T.); +#1946=ORIENTED_EDGE('',*,*,#1091,.T.); +#1947=ORIENTED_EDGE('',*,*,#1055,.F.); +#1948=ORIENTED_EDGE('',*,*,#1109,.F.); +#1949=EDGE_LOOP('',(#1945,#1946,#1947,#1948)); +#1950=FACE_OUTER_BOUND('',#1949,.T.); +#1951=ADVANCED_FACE('back-19',(#1950),#101,.T.); +#1952=ORIENTED_EDGE('',*,*,#1109,.T.); +#1953=ORIENTED_EDGE('',*,*,#1037,.F.); +#1954=ORIENTED_EDGE('',*,*,#929,.F.); +#1955=ORIENTED_EDGE('',*,*,#713,.T.); +#1956=EDGE_LOOP('',(#1952,#1953,#1954,#1955)); +#1957=FACE_OUTER_BOUND('',#1956,.T.); +#1958=ADVANCED_FACE('bottom-20',(#1957),#106,.T.); +#1959=ORIENTED_EDGE('',*,*,#1127,.T.); +#1960=ORIENTED_EDGE('',*,*,#1145,.F.); +#1961=ORIENTED_EDGE('',*,*,#803,.F.); +#1962=ORIENTED_EDGE('',*,*,#1163,.T.); +#1963=EDGE_LOOP('',(#1959,#1960,#1961,#1962)); +#1964=FACE_OUTER_BOUND('',#1963,.T.); +#1965=ADVANCED_FACE('left-21',(#1964),#111,.T.); +#1966=ORIENTED_EDGE('',*,*,#1181,.T.); +#1967=ORIENTED_EDGE('',*,*,#1199,.F.); +#1968=ORIENTED_EDGE('',*,*,#1163,.F.); +#1969=ORIENTED_EDGE('',*,*,#1019,.T.); +#1970=EDGE_LOOP('',(#1966,#1967,#1968,#1969)); +#1971=FACE_OUTER_BOUND('',#1970,.T.); +#1972=ADVANCED_FACE('front-22',(#1971),#116,.T.); +#1973=ORIENTED_EDGE('',*,*,#1199,.T.); +#1974=ORIENTED_EDGE('',*,*,#1217,.T.); +#1975=ORIENTED_EDGE('',*,*,#1235,.F.); +#1976=ORIENTED_EDGE('',*,*,#1127,.F.); +#1977=EDGE_LOOP('',(#1973,#1974,#1975,#1976)); +#1978=FACE_OUTER_BOUND('',#1977,.T.); +#1979=ADVANCED_FACE('top-23',(#1978),#121,.T.); +#1980=ORIENTED_EDGE('',*,*,#1253,.T.); +#1981=ORIENTED_EDGE('',*,*,#1271,.F.); +#1982=ORIENTED_EDGE('',*,*,#1289,.F.); +#1983=ORIENTED_EDGE('',*,*,#785,.T.); +#1984=EDGE_LOOP('',(#1980,#1981,#1982,#1983)); +#1985=FACE_OUTER_BOUND('',#1984,.T.); +#1986=ADVANCED_FACE('left-24',(#1985),#126,.T.); +#1987=ORIENTED_EDGE('',*,*,#1271,.T.); +#1988=ORIENTED_EDGE('',*,*,#1307,.T.); +#1989=ORIENTED_EDGE('',*,*,#1325,.F.); +#1990=ORIENTED_EDGE('',*,*,#1343,.F.); +#1991=EDGE_LOOP('',(#1987,#1988,#1989,#1990)); +#1992=FACE_OUTER_BOUND('',#1991,.T.); +#1993=ADVANCED_FACE('back-25',(#1992),#131,.T.); +#1994=ORIENTED_EDGE('',*,*,#1343,.T.); +#1995=ORIENTED_EDGE('',*,*,#1361,.F.); +#1996=ORIENTED_EDGE('',*,*,#1091,.F.); +#1997=ORIENTED_EDGE('',*,*,#1289,.T.); +#1998=EDGE_LOOP('',(#1994,#1995,#1996,#1997)); +#1999=FACE_OUTER_BOUND('',#1998,.T.); +#2000=ADVANCED_FACE('bottom-26',(#1999),#136,.T.); +#2001=ORIENTED_EDGE('',*,*,#1379,.T.); +#2002=ORIENTED_EDGE('',*,*,#1397,.F.); +#2003=ORIENTED_EDGE('',*,*,#1253,.F.); +#2004=ORIENTED_EDGE('',*,*,#1145,.T.); +#2005=EDGE_LOOP('',(#2001,#2002,#2003,#2004)); +#2006=FACE_OUTER_BOUND('',#2005,.T.); +#2007=ADVANCED_FACE('left-27',(#2006),#141,.T.); +#2008=ORIENTED_EDGE('',*,*,#1397,.T.); +#2009=ORIENTED_EDGE('',*,*,#1415,.T.); +#2010=ORIENTED_EDGE('',*,*,#1433,.F.); +#2011=ORIENTED_EDGE('',*,*,#1307,.F.); +#2012=EDGE_LOOP('',(#2008,#2009,#2010,#2011)); +#2013=FACE_OUTER_BOUND('',#2012,.T.); +#2014=ADVANCED_FACE('back-28',(#2013),#146,.T.); +#2015=ORIENTED_EDGE('',*,*,#1235,.T.); +#2016=ORIENTED_EDGE('',*,*,#1451,.T.); +#2017=ORIENTED_EDGE('',*,*,#1415,.F.); +#2018=ORIENTED_EDGE('',*,*,#1379,.F.); +#2019=EDGE_LOOP('',(#2015,#2016,#2017,#2018)); +#2020=FACE_OUTER_BOUND('',#2019,.T.); +#2021=ADVANCED_FACE('top-29',(#2020),#151,.T.); +#2022=ORIENTED_EDGE('',*,*,#1469,.T.); +#2023=ORIENTED_EDGE('',*,*,#1487,.T.); +#2024=ORIENTED_EDGE('',*,*,#1505,.F.); +#2025=ORIENTED_EDGE('',*,*,#1523,.F.); +#2026=EDGE_LOOP('',(#2022,#2023,#2024,#2025)); +#2027=FACE_OUTER_BOUND('',#2026,.T.); +#2028=ADVANCED_FACE('right-30',(#2027),#156,.T.); +#2029=ORIENTED_EDGE('',*,*,#1523,.T.); +#2030=ORIENTED_EDGE('',*,*,#1541,.F.); +#2031=ORIENTED_EDGE('',*,*,#947,.F.); +#2032=ORIENTED_EDGE('',*,*,#1559,.T.); +#2033=EDGE_LOOP('',(#2029,#2030,#2031,#2032)); +#2034=FACE_OUTER_BOUND('',#2033,.T.); +#2035=ADVANCED_FACE('front-31',(#2034),#161,.T.); +#2036=ORIENTED_EDGE('',*,*,#1577,.T.); +#2037=ORIENTED_EDGE('',*,*,#1469,.F.); +#2038=ORIENTED_EDGE('',*,*,#1559,.F.); +#2039=ORIENTED_EDGE('',*,*,#1073,.T.); +#2040=EDGE_LOOP('',(#2036,#2037,#2038,#2039)); +#2041=FACE_OUTER_BOUND('',#2040,.T.); +#2042=ADVANCED_FACE('bottom-32',(#2041),#166,.T.); +#2043=ORIENTED_EDGE('',*,*,#1505,.T.); +#2044=ORIENTED_EDGE('',*,*,#1595,.T.); +#2045=ORIENTED_EDGE('',*,*,#1613,.F.); +#2046=ORIENTED_EDGE('',*,*,#1631,.F.); +#2047=EDGE_LOOP('',(#2043,#2044,#2045,#2046)); +#2048=FACE_OUTER_BOUND('',#2047,.T.); +#2049=ADVANCED_FACE('right-33',(#2048),#171,.T.); +#2050=ORIENTED_EDGE('',*,*,#1631,.T.); +#2051=ORIENTED_EDGE('',*,*,#1649,.F.); +#2052=ORIENTED_EDGE('',*,*,#1181,.F.); +#2053=ORIENTED_EDGE('',*,*,#1541,.T.); +#2054=EDGE_LOOP('',(#2050,#2051,#2052,#2053)); +#2055=FACE_OUTER_BOUND('',#2054,.T.); +#2056=ADVANCED_FACE('front-34',(#2055),#176,.T.); +#2057=ORIENTED_EDGE('',*,*,#1649,.T.); +#2058=ORIENTED_EDGE('',*,*,#1613,.T.); +#2059=ORIENTED_EDGE('',*,*,#1667,.F.); +#2060=ORIENTED_EDGE('',*,*,#1217,.F.); +#2061=EDGE_LOOP('',(#2057,#2058,#2059,#2060)); +#2062=FACE_OUTER_BOUND('',#2061,.T.); +#2063=ADVANCED_FACE('top-35',(#2062),#181,.T.); +#2064=ORIENTED_EDGE('',*,*,#1685,.T.); +#2065=ORIENTED_EDGE('',*,*,#1703,.T.); +#2066=ORIENTED_EDGE('',*,*,#1721,.F.); +#2067=ORIENTED_EDGE('',*,*,#1487,.F.); +#2068=EDGE_LOOP('',(#2064,#2065,#2066,#2067)); +#2069=FACE_OUTER_BOUND('',#2068,.T.); +#2070=ADVANCED_FACE('right-36',(#2069),#186,.T.); +#2071=ORIENTED_EDGE('',*,*,#1325,.T.); +#2072=ORIENTED_EDGE('',*,*,#1739,.T.); +#2073=ORIENTED_EDGE('',*,*,#1703,.F.); +#2074=ORIENTED_EDGE('',*,*,#1757,.F.); +#2075=EDGE_LOOP('',(#2071,#2072,#2073,#2074)); +#2076=FACE_OUTER_BOUND('',#2075,.T.); +#2077=ADVANCED_FACE('back-37',(#2076),#191,.T.); +#2078=ORIENTED_EDGE('',*,*,#1757,.T.); +#2079=ORIENTED_EDGE('',*,*,#1685,.F.); +#2080=ORIENTED_EDGE('',*,*,#1577,.F.); +#2081=ORIENTED_EDGE('',*,*,#1361,.T.); +#2082=EDGE_LOOP('',(#2078,#2079,#2080,#2081)); +#2083=FACE_OUTER_BOUND('',#2082,.T.); +#2084=ADVANCED_FACE('bottom-38',(#2083),#196,.T.); +#2085=ORIENTED_EDGE('',*,*,#1721,.T.); +#2086=ORIENTED_EDGE('',*,*,#1775,.T.); +#2087=ORIENTED_EDGE('',*,*,#1793,.F.); +#2088=ORIENTED_EDGE('',*,*,#1595,.F.); +#2089=EDGE_LOOP('',(#2085,#2086,#2087,#2088)); +#2090=FACE_OUTER_BOUND('',#2089,.T.); +#2091=ADVANCED_FACE('right-39',(#2090),#201,.T.); +#2092=ORIENTED_EDGE('',*,*,#1433,.T.); +#2093=ORIENTED_EDGE('',*,*,#1811,.T.); +#2094=ORIENTED_EDGE('',*,*,#1775,.F.); +#2095=ORIENTED_EDGE('',*,*,#1739,.F.); +#2096=EDGE_LOOP('',(#2092,#2093,#2094,#2095)); +#2097=FACE_OUTER_BOUND('',#2096,.T.); +#2098=ADVANCED_FACE('back-40',(#2097),#206,.T.); +#2099=ORIENTED_EDGE('',*,*,#1667,.T.); +#2100=ORIENTED_EDGE('',*,*,#1793,.T.); +#2101=ORIENTED_EDGE('',*,*,#1811,.F.); +#2102=ORIENTED_EDGE('',*,*,#1451,.F.); +#2103=EDGE_LOOP('',(#2099,#2100,#2101,#2102)); +#2104=FACE_OUTER_BOUND('',#2103,.T.); +#2105=ADVANCED_FACE('top-41',(#2104),#211,.T.); +#2106=CLOSED_SHELL('',(#1818,#1839,#1860,#1832,#1825,#1853,#1881,#1846,#1867,#1874,#1916,#1909,#1937,#1895,#1888,#1930,#1951,#1958,#1902,#1923,#1972,#1965,#1986,#2000,#1944,#2035,#2056,#1979,#2007,#1993,#2084,#2042,#2028,#2049,#2063,#2021,#2014,#2077,#2070,#2091,#2105,#2098)); +#2107=MANIFOLD_SOLID_BREP('box-union-0',#2106); +#2108=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#2109=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#2110=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#2111=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000639488463615E-5),#2108,'distance_accuracy_value','exchange error bound'); +#2112=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#2111)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#2108,#2109,#2110)) REPRESENTATION_CONTEXT('','')); +#2113=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#2107),#2112); +#2114=APPLICATION_CONTEXT('automotive design'); +#2115=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#2114); +#2116=PRODUCT_CONTEXT('',#2114,'mechanical'); +#2117=PRODUCT('box-union','box-union','',(#2116)); +#2118=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#2117,.NOT_KNOWN.); +#2119=PRODUCT_DEFINITION_CONTEXT('part definition',#2114,'design'); +#2120=PRODUCT_DEFINITION('','',#2118,#2119); +#2121=PRODUCT_DEFINITION_SHAPE('','',#2120); +#2122=SHAPE_DEFINITION_REPRESENTATION(#2121,#2113); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/box-union.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/box-union.step.mm.report.json new file mode 100644 index 0000000..c01f2f4 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/box-union.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":84,"exchange_error_bound":0.000010000063948846362,"faces":42,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"1","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/circle.brep.json b/main/opengeometry/tests/fixtures/cases/circle.brep.json new file mode 100644 index 0000000..6ae0a19 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/circle.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"circle","revision":0,"geometry":{"surfaces":[],"curves":[{"kind":"Circle","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0}],"pcurves":[],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[1.0,0.0,0.0],"outgoing_halfedge":0,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":0,"twin_halfedge":null,"tolerance":1e-8,"chart_seam":false}],"halfedges":[{"id":0,"from":0,"to":0,"twin":null,"next":0,"prev":0,"edge":0,"face":null,"loop_ref":null,"wire_ref":0,"geometry_use":{"sense":"Forward","pcurve":null,"periodic_lift":[0,0]}}],"loops":[],"faces":[],"wires":[{"id":0,"start_halfedge":0,"is_closed":true}],"shells":[]},"solids":[],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/circle.step.m.error.json b/main/opengeometry/tests/fixtures/cases/circle.step.m.error.json new file mode 100644 index 0000000..347ecde --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/circle.step.m.error.json @@ -0,0 +1 @@ +{"UnsupportedGeometry":"analytic analytic exchange export requires nonempty closed solid regions without wires"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/circle.step.mm.error.json b/main/opengeometry/tests/fixtures/cases/circle.step.mm.error.json new file mode 100644 index 0000000..347ecde --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/circle.step.mm.error.json @@ -0,0 +1 @@ +{"UnsupportedGeometry":"analytic analytic exchange export requires nonempty closed solid regions without wires"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/circle.tess.json b/main/opengeometry/tests/fixtures/cases/circle.tess.json new file mode 100644 index 0000000..15498eb --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/circle.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[],"normals":[],"outlineEdgeIds":[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],"outlinePositions":[1.0,0.0,0.0,0.9807852804032304,0.0,-0.19509032201612825,0.9807852804032304,0.0,-0.19509032201612825,0.9238795325112867,0.0,-0.3826834323650898,0.9238795325112867,0.0,-0.3826834323650898,0.8314696123025452,0.0,-0.5555702330196022,0.8314696123025452,0.0,-0.5555702330196022,0.7071067811865476,0.0,-0.7071067811865475,0.7071067811865476,0.0,-0.7071067811865475,0.5555702330196023,0.0,-0.8314696123025452,0.5555702330196023,0.0,-0.8314696123025452,0.38268343236508984,0.0,-0.9238795325112867,0.38268343236508984,0.0,-0.9238795325112867,0.19509032201612833,0.0,-0.9807852804032304,0.19509032201612833,0.0,-0.9807852804032304,6.123233995736766e-17,0.0,-1.0,6.123233995736766e-17,0.0,-1.0,-0.1950903220161282,0.0,-0.9807852804032304,-0.1950903220161282,0.0,-0.9807852804032304,-0.3826834323650897,0.0,-0.9238795325112867,-0.3826834323650897,0.0,-0.9238795325112867,-0.555570233019602,0.0,-0.8314696123025453,-0.555570233019602,0.0,-0.8314696123025453,-0.7071067811865475,0.0,-0.7071067811865476,-0.7071067811865475,0.0,-0.7071067811865476,-0.8314696123025453,0.0,-0.5555702330196022,-0.8314696123025453,0.0,-0.5555702330196022,-0.9238795325112867,0.0,-0.3826834323650899,-0.9238795325112867,0.0,-0.3826834323650899,-0.9807852804032304,0.0,-0.1950903220161286,-0.9807852804032304,0.0,-0.1950903220161286,-1.0,0.0,-1.2246467991473532e-16,-1.0,0.0,-1.2246467991473532e-16,-0.9807852804032304,0.0,0.19509032201612836,-0.9807852804032304,0.0,0.19509032201612836,-0.9238795325112868,0.0,0.38268343236508967,-0.9238795325112868,0.0,0.38268343236508967,-0.8314696123025455,0.0,0.555570233019602,-0.8314696123025455,0.0,0.555570233019602,-0.7071067811865477,0.0,0.7071067811865475,-0.7071067811865477,0.0,0.7071067811865475,-0.5555702330196022,0.0,0.8314696123025452,-0.5555702330196022,0.0,0.8314696123025452,-0.38268343236509034,0.0,0.9238795325112865,-0.38268343236509034,0.0,0.9238795325112865,-0.19509032201612866,0.0,0.9807852804032303,-0.19509032201612866,0.0,0.9807852804032303,-1.8369701987210297e-16,0.0,1.0,-1.8369701987210297e-16,0.0,1.0,0.1950903220161283,0.0,0.9807852804032304,0.1950903220161283,0.0,0.9807852804032304,0.38268343236509,0.0,0.9238795325112866,0.38268343236509,0.0,0.9238795325112866,0.5555702330196018,0.0,0.8314696123025455,0.5555702330196018,0.0,0.8314696123025455,0.7071067811865474,0.0,0.7071067811865477,0.7071067811865474,0.0,0.7071067811865477,0.8314696123025452,0.0,0.5555702330196022,0.8314696123025452,0.0,0.5555702330196022,0.9238795325112865,0.0,0.3826834323650904,0.9238795325112865,0.0,0.3826834323650904,0.9807852804032303,0.0,0.19509032201612872,0.9807852804032303,0.0,0.19509032201612872,1.0,0.0,0.0],"positions":[],"revision":0,"triangleFaceIds":[]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cone.brep.json b/main/opengeometry/tests/fixtures/cases/cone.brep.json new file mode 100644 index 0000000..b8d3179 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cone.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"cone","revision":0,"geometry":{"surfaces":[{"kind":"Cone","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"semi_angle":0.4636476090008061},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]}}],"curves":[{"kind":"Circle","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"radius":1.0},{"kind":"Line","origin":[0.0,2.0,0.0],"direction":[0.4472135954999579,-0.8944271909999159,0.0]}],"pcurves":[{"kind":"Line2","origin":[0.0,0.0],"direction":[6.283185307179586,0.0]},{"kind":"Line2","origin":[6.283185307179586,0.0],"direction":[0.0,0.8944271909999159]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,0.8944271909999159]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[1.0,0.0],"axis_b":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[0.0,2.0,0.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[1.0,0.0,0.0],"outgoing_halfedge":2,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"twin_halfedge":null,"tolerance":1e-8,"chart_seam":true},{"id":1,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":2,"twin_halfedge":4,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":2.23606797749979}},"halfedge":1,"twin_halfedge":3,"tolerance":1e-8,"chart_seam":true}],"halfedges":[{"id":0,"from":0,"to":0,"twin":null,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":0,"to":1,"twin":3,"next":2,"prev":0,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":1,"to":1,"twin":4,"next":3,"prev":1,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":1,"to":0,"twin":1,"next":0,"prev":2,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":1,"to":1,"twin":2,"next":4,"prev":4,"edge":1,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":4,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false}],"faces":[{"id":0,"key":"lateral","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.0,"hi":6.283185307179586},{"lo":0.0,"hi":2.0}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cone","body":"cone","key":"lateral","face":0}],"role":"Authored","reversed":false}},{"id":1,"key":"upper_cap","surface":1,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.0,"hi":1.0},{"lo":-1.0,"hi":1.0}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cone","body":"cone","key":"upper_cap","face":1}],"role":"Authored","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,1],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cone.step.m b/main/opengeometry/tests/fixtures/cases/cone.step.m new file mode 100644 index 0000000..959f5c7 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cone.step.m @@ -0,0 +1,88 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('cone','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=CONICAL_SURFACE('',#5,0.,4.63647609000806094E-1); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#13=VERTEX_POINT('',#12); +#14=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=VERTEX_POINT('',#14); +#16=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#17=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#18=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#19=AXIS2_PLACEMENT_3D('',#16,#17,#18); +#20=CIRCLE('',#19,1.00000000000000000E0); +#21=CARTESIAN_POINT('',(0.00000000000000000E0,2.23606797749978981E0)); +#22=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#23=VECTOR('',#22,1.00000000000000000E0); +#24=LINE('',#21,#23); +#25=DEFINITIONAL_REPRESENTATION('',(#24),#1); +#26=PCURVE('',#6,#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#28=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#29=AXIS2_PLACEMENT_2D('',#27,#28); +#30=CIRCLE('',#29,1.00000000000000000E0); +#31=DEFINITIONAL_REPRESENTATION('',(#30),#1); +#32=PCURVE('',#11,#31); +#33=SURFACE_CURVE('',#20,(#26,#32),.CURVE_3D.); +#34=EDGE_CURVE('',#15,#15,#33,.T.); +#35=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#36=DIRECTION('',(4.47213595499957983E-1,-8.94427190999915966E-1,0.00000000000000000E0)); +#37=VECTOR('',#36,9.99999999999999889E-1); +#38=LINE('',#35,#37); +#39=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#40=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#41=VECTOR('',#40,1.00000000000000000E0); +#42=LINE('',#39,#41); +#43=DEFINITIONAL_REPRESENTATION('',(#42),#1); +#44=PCURVE('',#6,#43); +#45=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#46=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#47=VECTOR('',#46,1.00000000000000000E0); +#48=LINE('',#45,#47); +#49=DEFINITIONAL_REPRESENTATION('',(#48),#1); +#50=PCURVE('',#6,#49); +#51=SEAM_CURVE('',#38,(#44,#50),.CURVE_3D.); +#52=EDGE_CURVE('',#13,#15,#51,.T.); +#53=ORIENTED_EDGE('',*,*,#52,.T.); +#54=ORIENTED_EDGE('',*,*,#34,.F.); +#55=ORIENTED_EDGE('',*,*,#52,.F.); +#56=EDGE_LOOP('',(#53,#54,#55)); +#57=FACE_OUTER_BOUND('',#56,.T.); +#58=ADVANCED_FACE('lateral',(#57),#6,.T.); +#59=ORIENTED_EDGE('',*,*,#34,.T.); +#60=EDGE_LOOP('',(#59)); +#61=FACE_OUTER_BOUND('',#60,.T.); +#62=ADVANCED_FACE('upper_cap',(#61),#11,.T.); +#63=CLOSED_SHELL('',(#58,#62)); +#64=MANIFOLD_SOLID_BREP('cone-0',#63); +#65=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#66=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#67=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#68=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217094306E-8),#65,'distance_accuracy_value','exchange error bound'); +#69=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#68)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#65,#66,#67)) REPRESENTATION_CONTEXT('','')); +#70=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#64),#69); +#71=APPLICATION_CONTEXT('automotive design'); +#72=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#71); +#73=PRODUCT_CONTEXT('',#71,'mechanical'); +#74=PRODUCT('cone','cone','',(#73)); +#75=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#74,.NOT_KNOWN.); +#76=PRODUCT_DEFINITION_CONTEXT('part definition',#71,'design'); +#77=PRODUCT_DEFINITION('','',#75,#76); +#78=PRODUCT_DEFINITION_SHAPE('','',#77); +#79=SHAPE_DEFINITION_REPRESENTATION(#78,#70); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/cone.step.m.report.json b/main/opengeometry/tests/fixtures/cases/cone.step.m.report.json new file mode 100644 index 0000000..7146963 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cone.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":1,"edges":2,"exchange_error_bound":1.000002842170943e-8,"faces":2,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cone.step.mm b/main/opengeometry/tests/fixtures/cases/cone.step.mm new file mode 100644 index 0000000..0555174 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cone.step.mm @@ -0,0 +1,88 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('cone','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#3=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=CONICAL_SURFACE('',#5,0.,4.63647609000806094E-1); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#13=VERTEX_POINT('',#12); +#14=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#15=VERTEX_POINT('',#14); +#16=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#17=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#18=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#19=AXIS2_PLACEMENT_3D('',#16,#17,#18); +#20=CIRCLE('',#19,1.00000000000000000E3); +#21=CARTESIAN_POINT('',(0.00000000000000000E0,2.23606797749978978E3)); +#22=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#23=VECTOR('',#22,1.00000000000000000E0); +#24=LINE('',#21,#23); +#25=DEFINITIONAL_REPRESENTATION('',(#24),#1); +#26=PCURVE('',#6,#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#28=DIRECTION('',(1.00000000000000000E3,0.00000000000000000E0)); +#29=AXIS2_PLACEMENT_2D('',#27,#28); +#30=CIRCLE('',#29,1.00000000000000000E3); +#31=DEFINITIONAL_REPRESENTATION('',(#30),#1); +#32=PCURVE('',#11,#31); +#33=SURFACE_CURVE('',#20,(#26,#32),.CURVE_3D.); +#34=EDGE_CURVE('',#15,#15,#33,.T.); +#35=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#36=DIRECTION('',(4.47213595499957928E-1,-8.94427190999915855E-1,0.00000000000000000E0)); +#37=VECTOR('',#36,1.00000000000000000E3); +#38=LINE('',#35,#37); +#39=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#40=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#41=VECTOR('',#40,1.00000000000000000E3); +#42=LINE('',#39,#41); +#43=DEFINITIONAL_REPRESENTATION('',(#42),#1); +#44=PCURVE('',#6,#43); +#45=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#46=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#47=VECTOR('',#46,1.00000000000000000E3); +#48=LINE('',#45,#47); +#49=DEFINITIONAL_REPRESENTATION('',(#48),#1); +#50=PCURVE('',#6,#49); +#51=SEAM_CURVE('',#38,(#44,#50),.CURVE_3D.); +#52=EDGE_CURVE('',#13,#15,#51,.T.); +#53=ORIENTED_EDGE('',*,*,#52,.T.); +#54=ORIENTED_EDGE('',*,*,#34,.F.); +#55=ORIENTED_EDGE('',*,*,#52,.F.); +#56=EDGE_LOOP('',(#53,#54,#55)); +#57=FACE_OUTER_BOUND('',#56,.T.); +#58=ADVANCED_FACE('lateral',(#57),#6,.T.); +#59=ORIENTED_EDGE('',*,*,#34,.T.); +#60=EDGE_LOOP('',(#59)); +#61=FACE_OUTER_BOUND('',#60,.T.); +#62=ADVANCED_FACE('upper_cap',(#61),#11,.T.); +#63=CLOSED_SHELL('',(#58,#62)); +#64=MANIFOLD_SOLID_BREP('cone-0',#63); +#65=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#66=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#67=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#68=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217094312E-5),#65,'distance_accuracy_value','exchange error bound'); +#69=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#68)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#65,#66,#67)) REPRESENTATION_CONTEXT('','')); +#70=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#64),#69); +#71=APPLICATION_CONTEXT('automotive design'); +#72=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#71); +#73=PRODUCT_CONTEXT('',#71,'mechanical'); +#74=PRODUCT('cone','cone','',(#73)); +#75=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#74,.NOT_KNOWN.); +#76=PRODUCT_DEFINITION_CONTEXT('part definition',#71,'design'); +#77=PRODUCT_DEFINITION('','',#75,#76); +#78=PRODUCT_DEFINITION_SHAPE('','',#77); +#79=SHAPE_DEFINITION_REPRESENTATION(#78,#70); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/cone.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/cone.step.mm.report.json new file mode 100644 index 0000000..2c90896 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cone.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":1,"edges":2,"exchange_error_bound":0.000010000028421709431,"faces":2,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cone.tess.json b/main/opengeometry/tests/fixtures/cases/cone.tess.json new file mode 100644 index 0000000..a0e6c1f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cone.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[0,34,33,1,35,34,2,36,35,3,37,36,4,38,37,5,39,38,6,40,39,7,41,40,8,42,41,9,43,42,10,44,43,11,45,44,12,46,45,13,47,46,14,48,47,15,49,48,16,50,49,17,51,50,18,52,51,19,53,52,20,54,53,21,55,54,22,56,55,23,57,56,24,58,57,25,59,58,26,60,59,27,61,60,28,62,61,29,63,62,30,64,63,31,65,64,96,97,66,66,67,68,68,69,70,70,71,72,72,73,74,74,75,76,76,77,78,78,79,80,80,81,82,82,83,84,84,85,86,86,87,88,88,89,90,90,91,92,92,93,94,94,95,96,96,66,68,68,70,72,72,74,76,76,78,80,80,82,84,84,86,88,88,90,92,92,94,96,96,68,72,72,76,80,80,84,88,88,92,96,96,72,80,80,88,96],"normals":[0.8944271802902222,0.4472135901451111,0.0,0.8772410154342651,0.4472135901451111,0.17449408769607544,0.8263429999351501,0.4472135901451111,0.3422824740409851,0.7436890006065369,0.4472135901451111,0.49691712856292725,0.6324555277824402,0.4472135901451111,0.6324555277824402,0.49691712856292725,0.4472135901451111,0.7436890006065369,0.3422824740409851,0.4472135901451111,0.8263429999351501,0.17449408769607544,0.4472135901451111,0.8772410154342651,5.476786989387483e-17,0.4472135901451111,0.8944271802902222,-0.17449408769607544,0.4472135901451111,0.8772410154342651,-0.3422824740409851,0.4472135901451111,0.8263429999351501,-0.49691712856292725,0.4472135901451111,0.7436890006065369,-0.6324555277824402,0.4472135901451111,0.6324555277824402,-0.7436890006065369,0.4472135901451111,0.49691712856292725,-0.8263429999351501,0.4472135901451111,0.3422824740409851,-0.8772410154342651,0.4472135901451111,0.17449408769607544,-0.8944271802902222,0.4472135901451111,1.0953573978774966e-16,-0.8772410154342651,0.4472135901451111,-0.17449408769607544,-0.8263429999351501,0.4472135901451111,-0.3422824740409851,-0.7436890006065369,0.4472135901451111,-0.49691712856292725,-0.6324555277824402,0.4472135901451111,-0.6324555277824402,-0.49691712856292725,0.4472135901451111,-0.7436890006065369,-0.3422824740409851,0.4472135901451111,-0.8263429999351501,-0.17449408769607544,0.4472135901451111,-0.8772410154342651,-1.6430361299034693e-16,0.4472135901451111,-0.8944271802902222,0.17449408769607544,0.4472135901451111,-0.8772410154342651,0.3422824740409851,0.4472135901451111,-0.8263429999351501,0.49691712856292725,0.4472135901451111,-0.7436890006065369,0.6324555277824402,0.4472135901451111,-0.6324555277824402,0.7436890006065369,0.4472135901451111,-0.49691712856292725,0.8263429999351501,0.4472135901451111,-0.3422824740409851,0.8772410154342651,0.4472135901451111,-0.17449408769607544,0.8944271802902222,0.4472135901451111,-2.190714795754993e-16,0.8944271802902222,0.4472135901451111,0.0,0.8772410154342651,0.4472135901451111,0.17449408769607544,0.8263429999351501,0.4472135901451111,0.3422824740409851,0.7436890006065369,0.4472135901451111,0.49691712856292725,0.6324555277824402,0.4472135901451111,0.6324555277824402,0.49691712856292725,0.4472135901451111,0.7436890006065369,0.3422824740409851,0.4472135901451111,0.8263429999351501,0.17449408769607544,0.4472135901451111,0.8772410154342651,5.476786989387483e-17,0.4472135901451111,0.8944271802902222,-0.17449408769607544,0.4472135901451111,0.8772410154342651,-0.3422824740409851,0.4472135901451111,0.8263429999351501,-0.49691712856292725,0.4472135901451111,0.7436890006065369,-0.6324555277824402,0.4472135901451111,0.6324555277824402,-0.7436890006065369,0.4472135901451111,0.49691712856292725,-0.8263429999351501,0.4472135901451111,0.3422824740409851,-0.8772410154342651,0.4472135901451111,0.17449408769607544,-0.8944271802902222,0.4472135901451111,1.0953573978774966e-16,-0.8772410154342651,0.4472135901451111,-0.17449408769607544,-0.8263429999351501,0.4472135901451111,-0.3422824740409851,-0.7436890006065369,0.4472135901451111,-0.49691712856292725,-0.6324555277824402,0.4472135901451111,-0.6324555277824402,-0.49691712856292725,0.4472135901451111,-0.7436890006065369,-0.3422824740409851,0.4472135901451111,-0.8263429999351501,-0.17449408769607544,0.4472135901451111,-0.8772410154342651,-1.6430361299034693e-16,0.4472135901451111,-0.8944271802902222,0.17449408769607544,0.4472135901451111,-0.8772410154342651,0.3422824740409851,0.4472135901451111,-0.8263429999351501,0.49691712856292725,0.4472135901451111,-0.7436890006065369,0.6324555277824402,0.4472135901451111,-0.6324555277824402,0.7436890006065369,0.4472135901451111,-0.49691712856292725,0.8263429999351501,0.4472135901451111,-0.3422824740409851,0.8772410154342651,0.4472135901451111,-0.17449408769607544,0.8944271802902222,0.4472135901451111,-2.190714795754993e-16,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0],"outlineEdgeIds":[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],"outlinePositions":[1.0,0.0,0.0,0.9807852804032304,0.0,0.19509032201612825,0.9807852804032304,0.0,0.19509032201612825,0.9238795325112867,0.0,0.3826834323650898,0.9238795325112867,0.0,0.3826834323650898,0.8314696123025452,0.0,0.5555702330196022,0.8314696123025452,0.0,0.5555702330196022,0.7071067811865476,0.0,0.7071067811865475,0.7071067811865476,0.0,0.7071067811865475,0.5555702330196023,0.0,0.8314696123025452,0.5555702330196023,0.0,0.8314696123025452,0.38268343236508984,0.0,0.9238795325112867,0.38268343236508984,0.0,0.9238795325112867,0.19509032201612833,0.0,0.9807852804032304,0.19509032201612833,0.0,0.9807852804032304,6.123233995736766e-17,0.0,1.0,6.123233995736766e-17,0.0,1.0,-0.1950903220161282,0.0,0.9807852804032304,-0.1950903220161282,0.0,0.9807852804032304,-0.3826834323650897,0.0,0.9238795325112867,-0.3826834323650897,0.0,0.9238795325112867,-0.555570233019602,0.0,0.8314696123025453,-0.555570233019602,0.0,0.8314696123025453,-0.7071067811865475,0.0,0.7071067811865476,-0.7071067811865475,0.0,0.7071067811865476,-0.8314696123025453,0.0,0.5555702330196022,-0.8314696123025453,0.0,0.5555702330196022,-0.9238795325112867,0.0,0.3826834323650899,-0.9238795325112867,0.0,0.3826834323650899,-0.9807852804032304,0.0,0.1950903220161286,-0.9807852804032304,0.0,0.1950903220161286,-1.0,0.0,1.2246467991473532e-16,-1.0,0.0,1.2246467991473532e-16,-0.9807852804032304,0.0,-0.19509032201612836,-0.9807852804032304,0.0,-0.19509032201612836,-0.9238795325112868,0.0,-0.38268343236508967,-0.9238795325112868,0.0,-0.38268343236508967,-0.8314696123025455,0.0,-0.555570233019602,-0.8314696123025455,0.0,-0.555570233019602,-0.7071067811865477,0.0,-0.7071067811865475,-0.7071067811865477,0.0,-0.7071067811865475,-0.5555702330196022,0.0,-0.8314696123025452,-0.5555702330196022,0.0,-0.8314696123025452,-0.38268343236509034,0.0,-0.9238795325112865,-0.38268343236509034,0.0,-0.9238795325112865,-0.19509032201612866,0.0,-0.9807852804032303,-0.19509032201612866,0.0,-0.9807852804032303,-1.8369701987210297e-16,0.0,-1.0,-1.8369701987210297e-16,0.0,-1.0,0.1950903220161283,0.0,-0.9807852804032304,0.1950903220161283,0.0,-0.9807852804032304,0.38268343236509,0.0,-0.9238795325112866,0.38268343236509,0.0,-0.9238795325112866,0.5555702330196018,0.0,-0.8314696123025455,0.5555702330196018,0.0,-0.8314696123025455,0.7071067811865474,0.0,-0.7071067811865477,0.7071067811865474,0.0,-0.7071067811865477,0.8314696123025452,0.0,-0.5555702330196022,0.8314696123025452,0.0,-0.5555702330196022,0.9238795325112865,0.0,-0.3826834323650904,0.9238795325112865,0.0,-0.3826834323650904,0.9807852804032303,0.0,-0.19509032201612872,0.9807852804032303,0.0,-0.19509032201612872,1.0,0.0,0.0],"positions":[0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,0.0,2.0,0.0,1.0,0.0,0.0,0.9807852804032304,0.0,0.19509032201612825,0.9238795325112867,0.0,0.3826834323650898,0.8314696123025452,0.0,0.5555702330196022,0.7071067811865476,0.0,0.7071067811865475,0.5555702330196023,0.0,0.8314696123025452,0.38268343236508984,0.0,0.9238795325112867,0.19509032201612833,0.0,0.9807852804032304,6.123233995736766e-17,0.0,1.0,-0.1950903220161282,0.0,0.9807852804032304,-0.3826834323650897,0.0,0.9238795325112867,-0.555570233019602,0.0,0.8314696123025453,-0.7071067811865475,0.0,0.7071067811865476,-0.8314696123025453,0.0,0.5555702330196022,-0.9238795325112867,0.0,0.3826834323650899,-0.9807852804032304,0.0,0.1950903220161286,-1.0,0.0,1.2246467991473532e-16,-0.9807852804032304,0.0,-0.19509032201612836,-0.9238795325112868,0.0,-0.38268343236508967,-0.8314696123025455,0.0,-0.555570233019602,-0.7071067811865477,0.0,-0.7071067811865475,-0.5555702330196022,0.0,-0.8314696123025452,-0.38268343236509034,0.0,-0.9238795325112865,-0.19509032201612866,0.0,-0.9807852804032303,-1.8369701987210297e-16,0.0,-1.0,0.1950903220161283,0.0,-0.9807852804032304,0.38268343236509,0.0,-0.9238795325112866,0.5555702330196018,0.0,-0.8314696123025455,0.7071067811865474,0.0,-0.7071067811865477,0.8314696123025452,0.0,-0.5555702330196022,0.9238795325112865,0.0,-0.3826834323650904,0.9807852804032303,0.0,-0.19509032201612872,1.0,0.0,0.0,1.0,0.0,0.0,0.9807852804032304,0.0,0.19509032201612825,0.9238795325112867,0.0,0.3826834323650898,0.8314696123025452,0.0,0.5555702330196022,0.7071067811865476,0.0,0.7071067811865475,0.5555702330196023,0.0,0.8314696123025452,0.38268343236508984,0.0,0.9238795325112867,0.19509032201612833,0.0,0.9807852804032304,6.123233995736766e-17,0.0,1.0,-0.1950903220161282,0.0,0.9807852804032304,-0.3826834323650897,0.0,0.9238795325112867,-0.555570233019602,0.0,0.8314696123025453,-0.7071067811865475,0.0,0.7071067811865476,-0.8314696123025453,0.0,0.5555702330196022,-0.9238795325112867,0.0,0.3826834323650899,-0.9807852804032304,0.0,0.1950903220161286,-1.0,0.0,1.2246467991473532e-16,-0.9807852804032304,0.0,-0.19509032201612836,-0.9238795325112868,0.0,-0.38268343236508967,-0.8314696123025455,0.0,-0.555570233019602,-0.7071067811865477,0.0,-0.7071067811865475,-0.5555702330196022,0.0,-0.8314696123025452,-0.38268343236509034,0.0,-0.9238795325112865,-0.19509032201612866,0.0,-0.9807852804032303,-1.8369701987210297e-16,0.0,-1.0,0.1950903220161283,0.0,-0.9807852804032304,0.38268343236509,0.0,-0.9238795325112866,0.5555702330196018,0.0,-0.8314696123025455,0.7071067811865474,0.0,-0.7071067811865477,0.8314696123025452,0.0,-0.5555702330196022,0.9238795325112865,0.0,-0.3826834323650904,0.9807852804032303,0.0,-0.19509032201612872],"revision":0,"triangleFaceIds":[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,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]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cuboid.brep.json b/main/opengeometry/tests/fixtures/cases/cuboid.brep.json new file mode 100644 index 0000000..6ead1c9 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cuboid.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"cuboid","revision":0,"geometry":{"surfaces":[{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]}},{"kind":"Plane","frame":{"origin":[2.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,-1.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]}}],"curves":[{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[2.0,0.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-1.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,0.0,-1.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[2.0,0.0,-1.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.0,3.0,0.0],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[0.0,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[2.0,3.0,0.0],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[0.0,3.0,-1.0],"direction":[1.0,0.0,0.0]}],"pcurves":[{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,1.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,3.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[3.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[0.0,0.0,0.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[2.0,0.0,0.0],"outgoing_halfedge":3,"tolerance":1e-8},{"id":2,"position":[0.0,0.0,-1.0],"outgoing_halfedge":1,"tolerance":1e-8},{"id":3,"position":[2.0,0.0,-1.0],"outgoing_halfedge":2,"tolerance":1e-8},{"id":4,"position":[0.0,3.0,0.0],"outgoing_halfedge":4,"tolerance":1e-8},{"id":5,"position":[2.0,3.0,0.0],"outgoing_halfedge":5,"tolerance":1e-8},{"id":6,"position":[0.0,3.0,-1.0],"outgoing_halfedge":7,"tolerance":1e-8},{"id":7,"position":[2.0,3.0,-1.0],"outgoing_halfedge":6,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":2.0}},"halfedge":3,"twin_halfedge":16,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":1.0}},"halfedge":0,"twin_halfedge":11,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":3.0}},"halfedge":8,"twin_halfedge":19,"tolerance":1e-8,"chart_seam":false},{"id":3,"geometry":{"kind":"Curve","curve":3,"range":{"lo":0.0,"hi":1.0}},"halfedge":2,"twin_halfedge":12,"tolerance":1e-8,"chart_seam":false},{"id":4,"geometry":{"kind":"Curve","curve":4,"range":{"lo":0.0,"hi":3.0}},"halfedge":15,"twin_halfedge":17,"tolerance":1e-8,"chart_seam":false},{"id":5,"geometry":{"kind":"Curve","curve":5,"range":{"lo":0.0,"hi":2.0}},"halfedge":1,"twin_halfedge":23,"tolerance":1e-8,"chart_seam":false},{"id":6,"geometry":{"kind":"Curve","curve":6,"range":{"lo":0.0,"hi":3.0}},"halfedge":10,"twin_halfedge":20,"tolerance":1e-8,"chart_seam":false},{"id":7,"geometry":{"kind":"Curve","curve":7,"range":{"lo":0.0,"hi":3.0}},"halfedge":13,"twin_halfedge":22,"tolerance":1e-8,"chart_seam":false},{"id":8,"geometry":{"kind":"Curve","curve":8,"range":{"lo":0.0,"hi":2.0}},"halfedge":4,"twin_halfedge":18,"tolerance":1e-8,"chart_seam":false},{"id":9,"geometry":{"kind":"Curve","curve":9,"range":{"lo":0.0,"hi":1.0}},"halfedge":7,"twin_halfedge":9,"tolerance":1e-8,"chart_seam":false},{"id":10,"geometry":{"kind":"Curve","curve":10,"range":{"lo":0.0,"hi":1.0}},"halfedge":5,"twin_halfedge":14,"tolerance":1e-8,"chart_seam":false},{"id":11,"geometry":{"kind":"Curve","curve":11,"range":{"lo":0.0,"hi":2.0}},"halfedge":6,"twin_halfedge":21,"tolerance":1e-8,"chart_seam":false}],"halfedges":[{"id":0,"from":0,"to":2,"twin":11,"next":1,"prev":3,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":2,"to":3,"twin":23,"next":2,"prev":0,"edge":5,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":3,"to":1,"twin":12,"next":3,"prev":1,"edge":3,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":1,"to":0,"twin":16,"next":0,"prev":2,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":4,"to":5,"twin":18,"next":5,"prev":7,"edge":8,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":5,"to":7,"twin":14,"next":6,"prev":4,"edge":10,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":5,"periodic_lift":[0,0]}},{"id":6,"from":7,"to":6,"twin":21,"next":7,"prev":5,"edge":11,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":6,"periodic_lift":[0,0]}},{"id":7,"from":6,"to":4,"twin":9,"next":4,"prev":6,"edge":9,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":7,"periodic_lift":[0,0]}},{"id":8,"from":0,"to":4,"twin":19,"next":9,"prev":11,"edge":2,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":8,"periodic_lift":[0,0]}},{"id":9,"from":4,"to":6,"twin":7,"next":10,"prev":8,"edge":9,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":9,"periodic_lift":[0,0]}},{"id":10,"from":6,"to":2,"twin":20,"next":11,"prev":9,"edge":6,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":10,"periodic_lift":[0,0]}},{"id":11,"from":2,"to":0,"twin":0,"next":8,"prev":10,"edge":1,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":11,"periodic_lift":[0,0]}},{"id":12,"from":1,"to":3,"twin":2,"next":13,"prev":15,"edge":3,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":12,"periodic_lift":[0,0]}},{"id":13,"from":3,"to":7,"twin":22,"next":14,"prev":12,"edge":7,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":13,"periodic_lift":[0,0]}},{"id":14,"from":7,"to":5,"twin":5,"next":15,"prev":13,"edge":10,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":14,"periodic_lift":[0,0]}},{"id":15,"from":5,"to":1,"twin":17,"next":12,"prev":14,"edge":4,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":15,"periodic_lift":[0,0]}},{"id":16,"from":0,"to":1,"twin":3,"next":17,"prev":19,"edge":0,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":16,"periodic_lift":[0,0]}},{"id":17,"from":1,"to":5,"twin":15,"next":18,"prev":16,"edge":4,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":17,"periodic_lift":[0,0]}},{"id":18,"from":5,"to":4,"twin":4,"next":19,"prev":17,"edge":8,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":18,"periodic_lift":[0,0]}},{"id":19,"from":4,"to":0,"twin":8,"next":16,"prev":18,"edge":2,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":19,"periodic_lift":[0,0]}},{"id":20,"from":2,"to":6,"twin":10,"next":21,"prev":23,"edge":6,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":20,"periodic_lift":[0,0]}},{"id":21,"from":6,"to":7,"twin":6,"next":22,"prev":20,"edge":11,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":21,"periodic_lift":[0,0]}},{"id":22,"from":7,"to":3,"twin":13,"next":23,"prev":21,"edge":7,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":22,"periodic_lift":[0,0]}},{"id":23,"from":3,"to":2,"twin":1,"next":20,"prev":22,"edge":5,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":23,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false},{"id":2,"start_halfedge":8,"face_ref":2,"is_hole":false},{"id":3,"start_halfedge":12,"face_ref":3,"is_hole":false},{"id":4,"start_halfedge":16,"face_ref":4,"is_hole":false},{"id":5,"start_halfedge":20,"face_ref":5,"is_hole":false}],"faces":[{"id":0,"key":"bottom","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cuboid","body":"cuboid","key":"bottom","face":0}],"role":"Authored","reversed":false}},{"id":1,"key":"top","surface":1,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cuboid","body":"cuboid","key":"top","face":1}],"role":"Authored","reversed":false}},{"id":2,"key":"left","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":3.00000001},{"lo":-1e-8,"hi":1.00000001}],"outer":2,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cuboid","body":"cuboid","key":"left","face":2}],"role":"Authored","reversed":false}},{"id":3,"key":"right","surface":3,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":3.00000001}],"outer":3,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cuboid","body":"cuboid","key":"right","face":3}],"role":"Authored","reversed":false}},{"id":4,"key":"front","surface":4,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.00000001},{"lo":-1e-8,"hi":3.00000001}],"outer":4,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cuboid","body":"cuboid","key":"front","face":4}],"role":"Authored","reversed":false}},{"id":5,"key":"back","surface":5,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":3.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":5,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cuboid","body":"cuboid","key":"back","face":5}],"role":"Authored","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,1,2,3,4,5],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cuboid.step.m b/main/opengeometry/tests/fixtures/cases/cuboid.step.m new file mode 100644 index 0000000..e5312ac --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cuboid.step.m @@ -0,0 +1,331 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('cuboid','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#24=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#28=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#29=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=VERTEX_POINT('',#32); +#34=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#35=VERTEX_POINT('',#34); +#36=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#37=VERTEX_POINT('',#36); +#38=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#39=VERTEX_POINT('',#38); +#40=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#41=VERTEX_POINT('',#40); +#42=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#43=VERTEX_POINT('',#42); +#44=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#45=VERTEX_POINT('',#44); +#46=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#47=VERTEX_POINT('',#46); +#48=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#49=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#50=VECTOR('',#49,1.00000000000000000E0); +#51=LINE('',#48,#50); +#52=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#53=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#54=VECTOR('',#53,1.00000000000000000E0); +#55=LINE('',#52,#54); +#56=DEFINITIONAL_REPRESENTATION('',(#55),#1); +#57=PCURVE('',#6,#56); +#58=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#59=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#60=VECTOR('',#59,1.00000000000000000E0); +#61=LINE('',#58,#60); +#62=DEFINITIONAL_REPRESENTATION('',(#61),#1); +#63=PCURVE('',#26,#62); +#64=SURFACE_CURVE('',#51,(#57,#63),.CURVE_3D.); +#65=EDGE_CURVE('',#33,#35,#64,.T.); +#66=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#67=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#68=VECTOR('',#67,1.00000000000000000E0); +#69=LINE('',#66,#68); +#70=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#71=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#72=VECTOR('',#71,1.00000000000000000E0); +#73=LINE('',#70,#72); +#74=DEFINITIONAL_REPRESENTATION('',(#73),#1); +#75=PCURVE('',#6,#74); +#76=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#77=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#78=VECTOR('',#77,1.00000000000000000E0); +#79=LINE('',#76,#78); +#80=DEFINITIONAL_REPRESENTATION('',(#79),#1); +#81=PCURVE('',#16,#80); +#82=SURFACE_CURVE('',#69,(#75,#81),.CURVE_3D.); +#83=EDGE_CURVE('',#33,#37,#82,.T.); +#84=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#85=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#86=VECTOR('',#85,1.00000000000000000E0); +#87=LINE('',#84,#86); +#88=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#90=VECTOR('',#89,1.00000000000000000E0); +#91=LINE('',#88,#90); +#92=DEFINITIONAL_REPRESENTATION('',(#91),#1); +#93=PCURVE('',#16,#92); +#94=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#95=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#96=VECTOR('',#95,1.00000000000000000E0); +#97=LINE('',#94,#96); +#98=DEFINITIONAL_REPRESENTATION('',(#97),#1); +#99=PCURVE('',#26,#98); +#100=SURFACE_CURVE('',#87,(#93,#99),.CURVE_3D.); +#101=EDGE_CURVE('',#33,#41,#100,.T.); +#102=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#103=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#104=VECTOR('',#103,1.00000000000000000E0); +#105=LINE('',#102,#104); +#106=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#107=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E0); +#109=LINE('',#106,#108); +#110=DEFINITIONAL_REPRESENTATION('',(#109),#1); +#111=PCURVE('',#6,#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#113=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#114=VECTOR('',#113,1.00000000000000000E0); +#115=LINE('',#112,#114); +#116=DEFINITIONAL_REPRESENTATION('',(#115),#1); +#117=PCURVE('',#21,#116); +#118=SURFACE_CURVE('',#105,(#111,#117),.CURVE_3D.); +#119=EDGE_CURVE('',#35,#39,#118,.T.); +#120=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#121=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#122=VECTOR('',#121,1.00000000000000000E0); +#123=LINE('',#120,#122); +#124=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#125=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E0); +#127=LINE('',#124,#126); +#128=DEFINITIONAL_REPRESENTATION('',(#127),#1); +#129=PCURVE('',#21,#128); +#130=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#131=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#132=VECTOR('',#131,1.00000000000000000E0); +#133=LINE('',#130,#132); +#134=DEFINITIONAL_REPRESENTATION('',(#133),#1); +#135=PCURVE('',#26,#134); +#136=SURFACE_CURVE('',#123,(#129,#135),.CURVE_3D.); +#137=EDGE_CURVE('',#35,#43,#136,.T.); +#138=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#139=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#140=VECTOR('',#139,1.00000000000000000E0); +#141=LINE('',#138,#140); +#142=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#143=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#144=VECTOR('',#143,1.00000000000000000E0); +#145=LINE('',#142,#144); +#146=DEFINITIONAL_REPRESENTATION('',(#145),#1); +#147=PCURVE('',#6,#146); +#148=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#149=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#150=VECTOR('',#149,1.00000000000000000E0); +#151=LINE('',#148,#150); +#152=DEFINITIONAL_REPRESENTATION('',(#151),#1); +#153=PCURVE('',#31,#152); +#154=SURFACE_CURVE('',#141,(#147,#153),.CURVE_3D.); +#155=EDGE_CURVE('',#37,#39,#154,.T.); +#156=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#157=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#158=VECTOR('',#157,1.00000000000000000E0); +#159=LINE('',#156,#158); +#160=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#161=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#162=VECTOR('',#161,1.00000000000000000E0); +#163=LINE('',#160,#162); +#164=DEFINITIONAL_REPRESENTATION('',(#163),#1); +#165=PCURVE('',#16,#164); +#166=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#167=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#168=VECTOR('',#167,1.00000000000000000E0); +#169=LINE('',#166,#168); +#170=DEFINITIONAL_REPRESENTATION('',(#169),#1); +#171=PCURVE('',#31,#170); +#172=SURFACE_CURVE('',#159,(#165,#171),.CURVE_3D.); +#173=EDGE_CURVE('',#37,#45,#172,.T.); +#174=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#175=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#176=VECTOR('',#175,1.00000000000000000E0); +#177=LINE('',#174,#176); +#178=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#179=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#180=VECTOR('',#179,1.00000000000000000E0); +#181=LINE('',#178,#180); +#182=DEFINITIONAL_REPRESENTATION('',(#181),#1); +#183=PCURVE('',#21,#182); +#184=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#185=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#186=VECTOR('',#185,1.00000000000000000E0); +#187=LINE('',#184,#186); +#188=DEFINITIONAL_REPRESENTATION('',(#187),#1); +#189=PCURVE('',#31,#188); +#190=SURFACE_CURVE('',#177,(#183,#189),.CURVE_3D.); +#191=EDGE_CURVE('',#39,#47,#190,.T.); +#192=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#193=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#194=VECTOR('',#193,1.00000000000000000E0); +#195=LINE('',#192,#194); +#196=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#197=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#198=VECTOR('',#197,1.00000000000000000E0); +#199=LINE('',#196,#198); +#200=DEFINITIONAL_REPRESENTATION('',(#199),#1); +#201=PCURVE('',#11,#200); +#202=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#203=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#204=VECTOR('',#203,1.00000000000000000E0); +#205=LINE('',#202,#204); +#206=DEFINITIONAL_REPRESENTATION('',(#205),#1); +#207=PCURVE('',#26,#206); +#208=SURFACE_CURVE('',#195,(#201,#207),.CURVE_3D.); +#209=EDGE_CURVE('',#41,#43,#208,.T.); +#210=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#211=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#212=VECTOR('',#211,1.00000000000000000E0); +#213=LINE('',#210,#212); +#214=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#215=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#216=VECTOR('',#215,1.00000000000000000E0); +#217=LINE('',#214,#216); +#218=DEFINITIONAL_REPRESENTATION('',(#217),#1); +#219=PCURVE('',#11,#218); +#220=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#221=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#222=VECTOR('',#221,1.00000000000000000E0); +#223=LINE('',#220,#222); +#224=DEFINITIONAL_REPRESENTATION('',(#223),#1); +#225=PCURVE('',#16,#224); +#226=SURFACE_CURVE('',#213,(#219,#225),.CURVE_3D.); +#227=EDGE_CURVE('',#41,#45,#226,.T.); +#228=CARTESIAN_POINT('',(2.00000000000000000E0,3.00000000000000000E0,0.00000000000000000E0)); +#229=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#230=VECTOR('',#229,1.00000000000000000E0); +#231=LINE('',#228,#230); +#232=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#233=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#234=VECTOR('',#233,1.00000000000000000E0); +#235=LINE('',#232,#234); +#236=DEFINITIONAL_REPRESENTATION('',(#235),#1); +#237=PCURVE('',#11,#236); +#238=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0)); +#239=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#240=VECTOR('',#239,1.00000000000000000E0); +#241=LINE('',#238,#240); +#242=DEFINITIONAL_REPRESENTATION('',(#241),#1); +#243=PCURVE('',#21,#242); +#244=SURFACE_CURVE('',#231,(#237,#243),.CURVE_3D.); +#245=EDGE_CURVE('',#43,#47,#244,.T.); +#246=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E0,-1.00000000000000000E0)); +#247=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#248=VECTOR('',#247,1.00000000000000000E0); +#249=LINE('',#246,#248); +#250=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0)); +#251=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#252=VECTOR('',#251,1.00000000000000000E0); +#253=LINE('',#250,#252); +#254=DEFINITIONAL_REPRESENTATION('',(#253),#1); +#255=PCURVE('',#11,#254); +#256=CARTESIAN_POINT('',(3.00000000000000000E0,0.00000000000000000E0)); +#257=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#258=VECTOR('',#257,1.00000000000000000E0); +#259=LINE('',#256,#258); +#260=DEFINITIONAL_REPRESENTATION('',(#259),#1); +#261=PCURVE('',#31,#260); +#262=SURFACE_CURVE('',#249,(#255,#261),.CURVE_3D.); +#263=EDGE_CURVE('',#45,#47,#262,.T.); +#264=ORIENTED_EDGE('',*,*,#83,.T.); +#265=ORIENTED_EDGE('',*,*,#155,.T.); +#266=ORIENTED_EDGE('',*,*,#119,.F.); +#267=ORIENTED_EDGE('',*,*,#65,.F.); +#268=EDGE_LOOP('',(#264,#265,#266,#267)); +#269=FACE_OUTER_BOUND('',#268,.T.); +#270=ADVANCED_FACE('bottom',(#269),#6,.T.); +#271=ORIENTED_EDGE('',*,*,#209,.T.); +#272=ORIENTED_EDGE('',*,*,#245,.T.); +#273=ORIENTED_EDGE('',*,*,#263,.F.); +#274=ORIENTED_EDGE('',*,*,#227,.F.); +#275=EDGE_LOOP('',(#271,#272,#273,#274)); +#276=FACE_OUTER_BOUND('',#275,.T.); +#277=ADVANCED_FACE('top',(#276),#11,.T.); +#278=ORIENTED_EDGE('',*,*,#101,.T.); +#279=ORIENTED_EDGE('',*,*,#227,.T.); +#280=ORIENTED_EDGE('',*,*,#173,.F.); +#281=ORIENTED_EDGE('',*,*,#83,.F.); +#282=EDGE_LOOP('',(#278,#279,#280,#281)); +#283=FACE_OUTER_BOUND('',#282,.T.); +#284=ADVANCED_FACE('left',(#283),#16,.T.); +#285=ORIENTED_EDGE('',*,*,#119,.T.); +#286=ORIENTED_EDGE('',*,*,#191,.T.); +#287=ORIENTED_EDGE('',*,*,#245,.F.); +#288=ORIENTED_EDGE('',*,*,#137,.F.); +#289=EDGE_LOOP('',(#285,#286,#287,#288)); +#290=FACE_OUTER_BOUND('',#289,.T.); +#291=ADVANCED_FACE('right',(#290),#21,.T.); +#292=ORIENTED_EDGE('',*,*,#65,.T.); +#293=ORIENTED_EDGE('',*,*,#137,.T.); +#294=ORIENTED_EDGE('',*,*,#209,.F.); +#295=ORIENTED_EDGE('',*,*,#101,.F.); +#296=EDGE_LOOP('',(#292,#293,#294,#295)); +#297=FACE_OUTER_BOUND('',#296,.T.); +#298=ADVANCED_FACE('front',(#297),#26,.T.); +#299=ORIENTED_EDGE('',*,*,#173,.T.); +#300=ORIENTED_EDGE('',*,*,#263,.T.); +#301=ORIENTED_EDGE('',*,*,#191,.F.); +#302=ORIENTED_EDGE('',*,*,#155,.F.); +#303=EDGE_LOOP('',(#299,#300,#301,#302)); +#304=FACE_OUTER_BOUND('',#303,.T.); +#305=ADVANCED_FACE('back',(#304),#31,.T.); +#306=CLOSED_SHELL('',(#270,#277,#284,#291,#298,#305)); +#307=MANIFOLD_SOLID_BREP('cuboid-0',#306); +#308=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#309=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#310=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#311=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000426325642881E-8),#308,'distance_accuracy_value','exchange error bound'); +#312=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#311)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#308,#309,#310)) REPRESENTATION_CONTEXT('','')); +#313=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#307),#312); +#314=APPLICATION_CONTEXT('automotive design'); +#315=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#314); +#316=PRODUCT_CONTEXT('',#314,'mechanical'); +#317=PRODUCT('cuboid','cuboid','',(#316)); +#318=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#317,.NOT_KNOWN.); +#319=PRODUCT_DEFINITION_CONTEXT('part definition',#314,'design'); +#320=PRODUCT_DEFINITION('','',#318,#319); +#321=PRODUCT_DEFINITION_SHAPE('','',#320); +#322=SHAPE_DEFINITION_REPRESENTATION(#321,#313); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/cuboid.step.m.report.json b/main/opengeometry/tests/fixtures/cases/cuboid.step.m.report.json new file mode 100644 index 0000000..57d1b65 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cuboid.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":12,"exchange_error_bound":1.0000042632564288e-8,"faces":6,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cuboid.step.mm b/main/opengeometry/tests/fixtures/cases/cuboid.step.mm new file mode 100644 index 0000000..df15d7e --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cuboid.step.mm @@ -0,0 +1,331 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('cuboid','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#8=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-1.00000000000000000E0,-0.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#24=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E3)); +#28=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#29=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=VERTEX_POINT('',#32); +#34=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#35=VERTEX_POINT('',#34); +#36=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E3)); +#37=VERTEX_POINT('',#36); +#38=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-1.00000000000000000E3)); +#39=VERTEX_POINT('',#38); +#40=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#41=VERTEX_POINT('',#40); +#42=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#43=VERTEX_POINT('',#42); +#44=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-1.00000000000000000E3)); +#45=VERTEX_POINT('',#44); +#46=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,-1.00000000000000000E3)); +#47=VERTEX_POINT('',#46); +#48=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#49=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#50=VECTOR('',#49,1.00000000000000000E3); +#51=LINE('',#48,#50); +#52=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#53=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#54=VECTOR('',#53,1.00000000000000000E3); +#55=LINE('',#52,#54); +#56=DEFINITIONAL_REPRESENTATION('',(#55),#1); +#57=PCURVE('',#6,#56); +#58=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#59=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#60=VECTOR('',#59,1.00000000000000000E3); +#61=LINE('',#58,#60); +#62=DEFINITIONAL_REPRESENTATION('',(#61),#1); +#63=PCURVE('',#26,#62); +#64=SURFACE_CURVE('',#51,(#57,#63),.CURVE_3D.); +#65=EDGE_CURVE('',#33,#35,#64,.T.); +#66=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#67=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#68=VECTOR('',#67,1.00000000000000000E3); +#69=LINE('',#66,#68); +#70=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#71=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#72=VECTOR('',#71,1.00000000000000000E3); +#73=LINE('',#70,#72); +#74=DEFINITIONAL_REPRESENTATION('',(#73),#1); +#75=PCURVE('',#6,#74); +#76=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#77=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#78=VECTOR('',#77,1.00000000000000000E3); +#79=LINE('',#76,#78); +#80=DEFINITIONAL_REPRESENTATION('',(#79),#1); +#81=PCURVE('',#16,#80); +#82=SURFACE_CURVE('',#69,(#75,#81),.CURVE_3D.); +#83=EDGE_CURVE('',#33,#37,#82,.T.); +#84=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#85=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#86=VECTOR('',#85,1.00000000000000000E3); +#87=LINE('',#84,#86); +#88=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#90=VECTOR('',#89,1.00000000000000000E3); +#91=LINE('',#88,#90); +#92=DEFINITIONAL_REPRESENTATION('',(#91),#1); +#93=PCURVE('',#16,#92); +#94=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#95=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#96=VECTOR('',#95,1.00000000000000000E3); +#97=LINE('',#94,#96); +#98=DEFINITIONAL_REPRESENTATION('',(#97),#1); +#99=PCURVE('',#26,#98); +#100=SURFACE_CURVE('',#87,(#93,#99),.CURVE_3D.); +#101=EDGE_CURVE('',#33,#41,#100,.T.); +#102=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#103=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#104=VECTOR('',#103,1.00000000000000000E3); +#105=LINE('',#102,#104); +#106=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#107=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E3); +#109=LINE('',#106,#108); +#110=DEFINITIONAL_REPRESENTATION('',(#109),#1); +#111=PCURVE('',#6,#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#113=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#114=VECTOR('',#113,1.00000000000000000E3); +#115=LINE('',#112,#114); +#116=DEFINITIONAL_REPRESENTATION('',(#115),#1); +#117=PCURVE('',#21,#116); +#118=SURFACE_CURVE('',#105,(#111,#117),.CURVE_3D.); +#119=EDGE_CURVE('',#35,#39,#118,.T.); +#120=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#121=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#122=VECTOR('',#121,1.00000000000000000E3); +#123=LINE('',#120,#122); +#124=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#125=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E3); +#127=LINE('',#124,#126); +#128=DEFINITIONAL_REPRESENTATION('',(#127),#1); +#129=PCURVE('',#21,#128); +#130=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#131=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#132=VECTOR('',#131,1.00000000000000000E3); +#133=LINE('',#130,#132); +#134=DEFINITIONAL_REPRESENTATION('',(#133),#1); +#135=PCURVE('',#26,#134); +#136=SURFACE_CURVE('',#123,(#129,#135),.CURVE_3D.); +#137=EDGE_CURVE('',#35,#43,#136,.T.); +#138=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E3)); +#139=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#140=VECTOR('',#139,1.00000000000000000E3); +#141=LINE('',#138,#140); +#142=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#143=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#144=VECTOR('',#143,1.00000000000000000E3); +#145=LINE('',#142,#144); +#146=DEFINITIONAL_REPRESENTATION('',(#145),#1); +#147=PCURVE('',#6,#146); +#148=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#149=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#150=VECTOR('',#149,1.00000000000000000E3); +#151=LINE('',#148,#150); +#152=DEFINITIONAL_REPRESENTATION('',(#151),#1); +#153=PCURVE('',#31,#152); +#154=SURFACE_CURVE('',#141,(#147,#153),.CURVE_3D.); +#155=EDGE_CURVE('',#37,#39,#154,.T.); +#156=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E3)); +#157=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#158=VECTOR('',#157,1.00000000000000000E3); +#159=LINE('',#156,#158); +#160=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#161=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#162=VECTOR('',#161,1.00000000000000000E3); +#163=LINE('',#160,#162); +#164=DEFINITIONAL_REPRESENTATION('',(#163),#1); +#165=PCURVE('',#16,#164); +#166=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#167=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#168=VECTOR('',#167,1.00000000000000000E3); +#169=LINE('',#166,#168); +#170=DEFINITIONAL_REPRESENTATION('',(#169),#1); +#171=PCURVE('',#31,#170); +#172=SURFACE_CURVE('',#159,(#165,#171),.CURVE_3D.); +#173=EDGE_CURVE('',#37,#45,#172,.T.); +#174=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,-1.00000000000000000E3)); +#175=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#176=VECTOR('',#175,1.00000000000000000E3); +#177=LINE('',#174,#176); +#178=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#179=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#180=VECTOR('',#179,1.00000000000000000E3); +#181=LINE('',#178,#180); +#182=DEFINITIONAL_REPRESENTATION('',(#181),#1); +#183=PCURVE('',#21,#182); +#184=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#185=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#186=VECTOR('',#185,1.00000000000000000E3); +#187=LINE('',#184,#186); +#188=DEFINITIONAL_REPRESENTATION('',(#187),#1); +#189=PCURVE('',#31,#188); +#190=SURFACE_CURVE('',#177,(#183,#189),.CURVE_3D.); +#191=EDGE_CURVE('',#39,#47,#190,.T.); +#192=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#193=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#194=VECTOR('',#193,1.00000000000000000E3); +#195=LINE('',#192,#194); +#196=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#197=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#198=VECTOR('',#197,1.00000000000000000E3); +#199=LINE('',#196,#198); +#200=DEFINITIONAL_REPRESENTATION('',(#199),#1); +#201=PCURVE('',#11,#200); +#202=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#203=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#204=VECTOR('',#203,1.00000000000000000E3); +#205=LINE('',#202,#204); +#206=DEFINITIONAL_REPRESENTATION('',(#205),#1); +#207=PCURVE('',#26,#206); +#208=SURFACE_CURVE('',#195,(#201,#207),.CURVE_3D.); +#209=EDGE_CURVE('',#41,#43,#208,.T.); +#210=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,0.00000000000000000E0)); +#211=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#212=VECTOR('',#211,1.00000000000000000E3); +#213=LINE('',#210,#212); +#214=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#215=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#216=VECTOR('',#215,1.00000000000000000E3); +#217=LINE('',#214,#216); +#218=DEFINITIONAL_REPRESENTATION('',(#217),#1); +#219=PCURVE('',#11,#218); +#220=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#221=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#222=VECTOR('',#221,1.00000000000000000E3); +#223=LINE('',#220,#222); +#224=DEFINITIONAL_REPRESENTATION('',(#223),#1); +#225=PCURVE('',#16,#224); +#226=SURFACE_CURVE('',#213,(#219,#225),.CURVE_3D.); +#227=EDGE_CURVE('',#41,#45,#226,.T.); +#228=CARTESIAN_POINT('',(2.00000000000000000E3,3.00000000000000000E3,0.00000000000000000E0)); +#229=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#230=VECTOR('',#229,1.00000000000000000E3); +#231=LINE('',#228,#230); +#232=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#233=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#234=VECTOR('',#233,1.00000000000000000E3); +#235=LINE('',#232,#234); +#236=DEFINITIONAL_REPRESENTATION('',(#235),#1); +#237=PCURVE('',#11,#236); +#238=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3)); +#239=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#240=VECTOR('',#239,1.00000000000000000E3); +#241=LINE('',#238,#240); +#242=DEFINITIONAL_REPRESENTATION('',(#241),#1); +#243=PCURVE('',#21,#242); +#244=SURFACE_CURVE('',#231,(#237,#243),.CURVE_3D.); +#245=EDGE_CURVE('',#43,#47,#244,.T.); +#246=CARTESIAN_POINT('',(0.00000000000000000E0,3.00000000000000000E3,-1.00000000000000000E3)); +#247=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#248=VECTOR('',#247,1.00000000000000000E3); +#249=LINE('',#246,#248); +#250=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3)); +#251=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#252=VECTOR('',#251,1.00000000000000000E3); +#253=LINE('',#250,#252); +#254=DEFINITIONAL_REPRESENTATION('',(#253),#1); +#255=PCURVE('',#11,#254); +#256=CARTESIAN_POINT('',(3.00000000000000000E3,0.00000000000000000E0)); +#257=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#258=VECTOR('',#257,1.00000000000000000E3); +#259=LINE('',#256,#258); +#260=DEFINITIONAL_REPRESENTATION('',(#259),#1); +#261=PCURVE('',#31,#260); +#262=SURFACE_CURVE('',#249,(#255,#261),.CURVE_3D.); +#263=EDGE_CURVE('',#45,#47,#262,.T.); +#264=ORIENTED_EDGE('',*,*,#83,.T.); +#265=ORIENTED_EDGE('',*,*,#155,.T.); +#266=ORIENTED_EDGE('',*,*,#119,.F.); +#267=ORIENTED_EDGE('',*,*,#65,.F.); +#268=EDGE_LOOP('',(#264,#265,#266,#267)); +#269=FACE_OUTER_BOUND('',#268,.T.); +#270=ADVANCED_FACE('bottom',(#269),#6,.T.); +#271=ORIENTED_EDGE('',*,*,#209,.T.); +#272=ORIENTED_EDGE('',*,*,#245,.T.); +#273=ORIENTED_EDGE('',*,*,#263,.F.); +#274=ORIENTED_EDGE('',*,*,#227,.F.); +#275=EDGE_LOOP('',(#271,#272,#273,#274)); +#276=FACE_OUTER_BOUND('',#275,.T.); +#277=ADVANCED_FACE('top',(#276),#11,.T.); +#278=ORIENTED_EDGE('',*,*,#101,.T.); +#279=ORIENTED_EDGE('',*,*,#227,.T.); +#280=ORIENTED_EDGE('',*,*,#173,.F.); +#281=ORIENTED_EDGE('',*,*,#83,.F.); +#282=EDGE_LOOP('',(#278,#279,#280,#281)); +#283=FACE_OUTER_BOUND('',#282,.T.); +#284=ADVANCED_FACE('left',(#283),#16,.T.); +#285=ORIENTED_EDGE('',*,*,#119,.T.); +#286=ORIENTED_EDGE('',*,*,#191,.T.); +#287=ORIENTED_EDGE('',*,*,#245,.F.); +#288=ORIENTED_EDGE('',*,*,#137,.F.); +#289=EDGE_LOOP('',(#285,#286,#287,#288)); +#290=FACE_OUTER_BOUND('',#289,.T.); +#291=ADVANCED_FACE('right',(#290),#21,.T.); +#292=ORIENTED_EDGE('',*,*,#65,.T.); +#293=ORIENTED_EDGE('',*,*,#137,.T.); +#294=ORIENTED_EDGE('',*,*,#209,.F.); +#295=ORIENTED_EDGE('',*,*,#101,.F.); +#296=EDGE_LOOP('',(#292,#293,#294,#295)); +#297=FACE_OUTER_BOUND('',#296,.T.); +#298=ADVANCED_FACE('front',(#297),#26,.T.); +#299=ORIENTED_EDGE('',*,*,#173,.T.); +#300=ORIENTED_EDGE('',*,*,#263,.T.); +#301=ORIENTED_EDGE('',*,*,#191,.F.); +#302=ORIENTED_EDGE('',*,*,#155,.F.); +#303=EDGE_LOOP('',(#299,#300,#301,#302)); +#304=FACE_OUTER_BOUND('',#303,.T.); +#305=ADVANCED_FACE('back',(#304),#31,.T.); +#306=CLOSED_SHELL('',(#270,#277,#284,#291,#298,#305)); +#307=MANIFOLD_SOLID_BREP('cuboid-0',#306); +#308=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#309=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#310=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#311=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000426325642887E-5),#308,'distance_accuracy_value','exchange error bound'); +#312=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#311)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#308,#309,#310)) REPRESENTATION_CONTEXT('','')); +#313=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#307),#312); +#314=APPLICATION_CONTEXT('automotive design'); +#315=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#314); +#316=PRODUCT_CONTEXT('',#314,'mechanical'); +#317=PRODUCT('cuboid','cuboid','',(#316)); +#318=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#317,.NOT_KNOWN.); +#319=PRODUCT_DEFINITION_CONTEXT('part definition',#314,'design'); +#320=PRODUCT_DEFINITION('','',#318,#319); +#321=PRODUCT_DEFINITION_SHAPE('','',#320); +#322=SHAPE_DEFINITION_REPRESENTATION(#321,#313); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/cuboid.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/cuboid.step.mm.report.json new file mode 100644 index 0000000..583fea1 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cuboid.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":12,"exchange_error_bound":0.000010000042632564289,"faces":6,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cuboid.tess.json b/main/opengeometry/tests/fixtures/cases/cuboid.tess.json new file mode 100644 index 0000000..6ffa672 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cuboid.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[2,3,0,0,1,2,6,7,4,4,5,6,10,11,8,8,9,10,14,15,12,12,13,14,18,19,16,16,17,18,22,23,20,20,21,22],"normals":[0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0],"outlineEdgeIds":[0,1,2,3,4,5,6,7,8,9,10,11],"outlinePositions":[0.0,0.0,0.0,2.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,3.0,0.0,2.0,0.0,0.0,2.0,0.0,-1.0,2.0,0.0,0.0,2.0,3.0,0.0,0.0,0.0,-1.0,2.0,0.0,-1.0,0.0,0.0,-1.0,0.0,3.0,-1.0,2.0,0.0,-1.0,2.0,3.0,-1.0,0.0,3.0,0.0,2.0,3.0,0.0,0.0,3.0,0.0,0.0,3.0,-1.0,2.0,3.0,0.0,2.0,3.0,-1.0,0.0,3.0,-1.0,2.0,3.0,-1.0],"positions":[0.0,0.0,0.0,0.0,0.0,-1.0,2.0,0.0,-1.0,2.0,0.0,0.0,0.0,3.0,0.0,2.0,3.0,0.0,2.0,3.0,-1.0,0.0,3.0,-1.0,0.0,0.0,0.0,0.0,3.0,0.0,0.0,3.0,-1.0,0.0,0.0,-1.0,2.0,0.0,0.0,2.0,0.0,-1.0,2.0,3.0,-1.0,2.0,3.0,0.0,0.0,0.0,0.0,2.0,0.0,0.0,2.0,3.0,0.0,0.0,3.0,0.0,0.0,0.0,-1.0,0.0,3.0,-1.0,2.0,3.0,-1.0,2.0,0.0,-1.0],"revision":0,"triangleFaceIds":[0,0,1,1,2,2,3,3,4,4,5,5]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.brep.json b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.brep.json new file mode 100644 index 0000000..200eedb --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"cylinder-with-hole","revision":0,"geometry":{"surfaces":[{"kind":"Cylinder","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0},{"kind":"Cylinder","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":0.4},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}}],"curves":[{"kind":"Circle","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0},{"kind":"Circle","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0},{"kind":"Circle","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":0.4},{"kind":"Circle","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":0.4},{"kind":"Line","origin":[1.0,0.0,0.0],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[0.4,0.0,0.0],"direction":[0.0,1.0,0.0]}],"pcurves":[{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[6.283185307179586,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[6.283185307179586,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[1.0,0.0],"axis_b":[0.0,1.0]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[0.4,0.0],"axis_b":[0.0,0.4]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[1.0,0.0,0.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[1.0,2.0,0.0],"outgoing_halfedge":2,"tolerance":1e-8},{"id":2,"position":[0.4,0.0,0.0],"outgoing_halfedge":4,"tolerance":1e-8},{"id":3,"position":[0.4,2.0,0.0],"outgoing_halfedge":6,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":0,"twin_halfedge":8,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":2,"twin_halfedge":10,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":4,"twin_halfedge":9,"tolerance":1e-8,"chart_seam":false},{"id":3,"geometry":{"kind":"Curve","curve":3,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":6,"twin_halfedge":11,"tolerance":1e-8,"chart_seam":false},{"id":4,"geometry":{"kind":"Curve","curve":4,"range":{"lo":0.0,"hi":2.0}},"halfedge":1,"twin_halfedge":3,"tolerance":1e-8,"chart_seam":true},{"id":5,"geometry":{"kind":"Curve","curve":5,"range":{"lo":0.0,"hi":2.0}},"halfedge":5,"twin_halfedge":7,"tolerance":1e-8,"chart_seam":true}],"halfedges":[{"id":0,"from":0,"to":0,"twin":8,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":0,"to":1,"twin":3,"next":2,"prev":0,"edge":4,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":1,"to":1,"twin":10,"next":3,"prev":1,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":1,"to":0,"twin":1,"next":0,"prev":2,"edge":4,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":2,"to":2,"twin":9,"next":5,"prev":7,"edge":2,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":2,"to":3,"twin":7,"next":6,"prev":4,"edge":5,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":5,"periodic_lift":[0,0]}},{"id":6,"from":3,"to":3,"twin":11,"next":7,"prev":5,"edge":3,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":6,"periodic_lift":[0,0]}},{"id":7,"from":3,"to":2,"twin":5,"next":4,"prev":6,"edge":5,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":7,"periodic_lift":[0,0]}},{"id":8,"from":0,"to":0,"twin":0,"next":8,"prev":8,"edge":0,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":8,"periodic_lift":[0,0]}},{"id":9,"from":2,"to":2,"twin":4,"next":9,"prev":9,"edge":2,"face":2,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":9,"periodic_lift":[0,0]}},{"id":10,"from":1,"to":1,"twin":2,"next":10,"prev":10,"edge":1,"face":3,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":10,"periodic_lift":[0,0]}},{"id":11,"from":3,"to":3,"twin":6,"next":11,"prev":11,"edge":3,"face":3,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":11,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false},{"id":2,"start_halfedge":8,"face_ref":2,"is_hole":false},{"id":3,"start_halfedge":9,"face_ref":2,"is_hole":true},{"id":4,"start_halfedge":10,"face_ref":3,"is_hole":false},{"id":5,"start_halfedge":11,"face_ref":3,"is_hole":true}],"faces":[{"id":0,"key":"outer","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.0,"hi":6.283185307179586},{"lo":0.0,"hi":2.0}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cylinder-with-hole","body":"cylinder-with-hole","key":"outer","face":0}],"role":"Authored","reversed":false}},{"id":1,"key":"inner","surface":1,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":0.0,"hi":6.283185307179586},{"lo":0.0,"hi":2.0}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cylinder-with-hole","body":"cylinder-with-hole","key":"inner","face":1}],"role":"Authored","reversed":false}},{"id":2,"key":"lower_cap","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.0,"hi":1.0},{"lo":-1.0,"hi":1.0}],"outer":2,"holes":[3]},"shell_ref":0,"provenance":{"sources":[{"entity":"cylinder-with-hole","body":"cylinder-with-hole","key":"lower_cap","face":2}],"role":"Authored","reversed":false}},{"id":3,"key":"upper_cap","surface":3,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.0,"hi":1.0},{"lo":-1.0,"hi":1.0}],"outer":4,"holes":[5]},"shell_ref":0,"provenance":{"sources":[{"entity":"cylinder-with-hole","body":"cylinder-with-hole","key":"upper_cap","face":3}],"role":"Authored","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,1,2,3],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.m b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.m new file mode 100644 index 0000000..8121b61 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.m @@ -0,0 +1,195 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('cylinder-with-hole','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=CYLINDRICAL_SURFACE('',#5,1.00000000000000000E0); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=CYLINDRICAL_SURFACE('',#10,4.00000000000000022E-1); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#18=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=VERTEX_POINT('',#22); +#24=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#25=VERTEX_POINT('',#24); +#26=CARTESIAN_POINT('',(4.00000000000000022E-1,0.00000000000000000E0,0.00000000000000000E0)); +#27=VERTEX_POINT('',#26); +#28=CARTESIAN_POINT('',(4.00000000000000022E-1,2.00000000000000000E0,0.00000000000000000E0)); +#29=VERTEX_POINT('',#28); +#30=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#31=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#32=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=AXIS2_PLACEMENT_3D('',#30,#31,#32); +#34=CIRCLE('',#33,1.00000000000000000E0); +#35=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#36=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#37=VECTOR('',#36,1.00000000000000000E0); +#38=LINE('',#35,#37); +#39=DEFINITIONAL_REPRESENTATION('',(#38),#1); +#40=PCURVE('',#6,#39); +#41=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#42=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#43=AXIS2_PLACEMENT_2D('',#41,#42); +#44=CIRCLE('',#43,1.00000000000000000E0); +#45=DEFINITIONAL_REPRESENTATION('',(#44),#1); +#46=PCURVE('',#16,#45); +#47=SURFACE_CURVE('',#34,(#40,#46),.CURVE_3D.); +#48=EDGE_CURVE('',#23,#23,#47,.T.); +#49=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#50=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#51=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#52=AXIS2_PLACEMENT_3D('',#49,#50,#51); +#53=CIRCLE('',#52,1.00000000000000000E0); +#54=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#55=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#56=VECTOR('',#55,1.00000000000000000E0); +#57=LINE('',#54,#56); +#58=DEFINITIONAL_REPRESENTATION('',(#57),#1); +#59=PCURVE('',#6,#58); +#60=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#61=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#62=AXIS2_PLACEMENT_2D('',#60,#61); +#63=CIRCLE('',#62,1.00000000000000000E0); +#64=DEFINITIONAL_REPRESENTATION('',(#63),#1); +#65=PCURVE('',#21,#64); +#66=SURFACE_CURVE('',#53,(#59,#65),.CURVE_3D.); +#67=EDGE_CURVE('',#25,#25,#66,.T.); +#68=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#69=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#70=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#71=AXIS2_PLACEMENT_3D('',#68,#69,#70); +#72=CIRCLE('',#71,4.00000000000000022E-1); +#73=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#74=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#75=VECTOR('',#74,1.00000000000000000E0); +#76=LINE('',#73,#75); +#77=DEFINITIONAL_REPRESENTATION('',(#76),#1); +#78=PCURVE('',#11,#77); +#79=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#80=DIRECTION('',(4.00000000000000022E-1,0.00000000000000000E0)); +#81=AXIS2_PLACEMENT_2D('',#79,#80); +#82=CIRCLE('',#81,4.00000000000000022E-1); +#83=DEFINITIONAL_REPRESENTATION('',(#82),#1); +#84=PCURVE('',#16,#83); +#85=SURFACE_CURVE('',#72,(#78,#84),.CURVE_3D.); +#86=EDGE_CURVE('',#27,#27,#85,.T.); +#87=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#88=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#90=AXIS2_PLACEMENT_3D('',#87,#88,#89); +#91=CIRCLE('',#90,4.00000000000000022E-1); +#92=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#93=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#94=VECTOR('',#93,1.00000000000000000E0); +#95=LINE('',#92,#94); +#96=DEFINITIONAL_REPRESENTATION('',(#95),#1); +#97=PCURVE('',#11,#96); +#98=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#99=DIRECTION('',(4.00000000000000022E-1,0.00000000000000000E0)); +#100=AXIS2_PLACEMENT_2D('',#98,#99); +#101=CIRCLE('',#100,4.00000000000000022E-1); +#102=DEFINITIONAL_REPRESENTATION('',(#101),#1); +#103=PCURVE('',#21,#102); +#104=SURFACE_CURVE('',#91,(#97,#103),.CURVE_3D.); +#105=EDGE_CURVE('',#29,#29,#104,.T.); +#106=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#107=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E0); +#109=LINE('',#106,#108); +#110=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#111=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#112=VECTOR('',#111,1.00000000000000000E0); +#113=LINE('',#110,#112); +#114=DEFINITIONAL_REPRESENTATION('',(#113),#1); +#115=PCURVE('',#6,#114); +#116=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#117=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#118=VECTOR('',#117,1.00000000000000000E0); +#119=LINE('',#116,#118); +#120=DEFINITIONAL_REPRESENTATION('',(#119),#1); +#121=PCURVE('',#6,#120); +#122=SEAM_CURVE('',#109,(#115,#121),.CURVE_3D.); +#123=EDGE_CURVE('',#23,#25,#122,.T.); +#124=CARTESIAN_POINT('',(4.00000000000000022E-1,0.00000000000000000E0,0.00000000000000000E0)); +#125=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E0); +#127=LINE('',#124,#126); +#128=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#129=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#130=VECTOR('',#129,1.00000000000000000E0); +#131=LINE('',#128,#130); +#132=DEFINITIONAL_REPRESENTATION('',(#131),#1); +#133=PCURVE('',#11,#132); +#134=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#135=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#136=VECTOR('',#135,1.00000000000000000E0); +#137=LINE('',#134,#136); +#138=DEFINITIONAL_REPRESENTATION('',(#137),#1); +#139=PCURVE('',#11,#138); +#140=SEAM_CURVE('',#127,(#133,#139),.CURVE_3D.); +#141=EDGE_CURVE('',#27,#29,#140,.T.); +#142=ORIENTED_EDGE('',*,*,#48,.T.); +#143=ORIENTED_EDGE('',*,*,#123,.T.); +#144=ORIENTED_EDGE('',*,*,#67,.F.); +#145=ORIENTED_EDGE('',*,*,#123,.F.); +#146=EDGE_LOOP('',(#142,#143,#144,#145)); +#147=FACE_OUTER_BOUND('',#146,.T.); +#148=ADVANCED_FACE('outer',(#147),#6,.T.); +#149=ORIENTED_EDGE('',*,*,#86,.T.); +#150=ORIENTED_EDGE('',*,*,#141,.T.); +#151=ORIENTED_EDGE('',*,*,#105,.F.); +#152=ORIENTED_EDGE('',*,*,#141,.F.); +#153=EDGE_LOOP('',(#149,#150,#151,#152)); +#154=FACE_OUTER_BOUND('',#153,.T.); +#155=ADVANCED_FACE('inner',(#154),#11,.F.); +#156=ORIENTED_EDGE('',*,*,#48,.F.); +#157=EDGE_LOOP('',(#156)); +#158=FACE_OUTER_BOUND('',#157,.T.); +#159=ORIENTED_EDGE('',*,*,#86,.F.); +#160=EDGE_LOOP('',(#159)); +#161=FACE_BOUND('',#160,.T.); +#162=ADVANCED_FACE('lower_cap',(#158,#161),#16,.T.); +#163=ORIENTED_EDGE('',*,*,#67,.T.); +#164=EDGE_LOOP('',(#163)); +#165=FACE_OUTER_BOUND('',#164,.T.); +#166=ORIENTED_EDGE('',*,*,#105,.T.); +#167=EDGE_LOOP('',(#166)); +#168=FACE_BOUND('',#167,.T.); +#169=ADVANCED_FACE('upper_cap',(#165,#168),#21,.T.); +#170=CLOSED_SHELL('',(#148,#155,#162,#169)); +#171=MANIFOLD_SOLID_BREP('cylinder-with-hole-0',#170); +#172=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#173=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#174=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#175=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217094306E-8),#172,'distance_accuracy_value','exchange error bound'); +#176=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#175)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#172,#173,#174)) REPRESENTATION_CONTEXT('','')); +#177=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#171),#176); +#178=APPLICATION_CONTEXT('automotive design'); +#179=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#178); +#180=PRODUCT_CONTEXT('',#178,'mechanical'); +#181=PRODUCT('cylinder-with-hole','cylinder-with-hole','',(#180)); +#182=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#181,.NOT_KNOWN.); +#183=PRODUCT_DEFINITION_CONTEXT('part definition',#178,'design'); +#184=PRODUCT_DEFINITION('','',#182,#183); +#185=PRODUCT_DEFINITION_SHAPE('','',#184); +#186=SHAPE_DEFINITION_REPRESENTATION(#185,#177); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.m.report.json b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.m.report.json new file mode 100644 index 0000000..3b49082 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":6,"exchange_error_bound":1.000002842170943e-8,"faces":4,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.mm b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.mm new file mode 100644 index 0000000..858075e --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.mm @@ -0,0 +1,195 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('cylinder-with-hole','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=CYLINDRICAL_SURFACE('',#5,1.00000000000000000E3); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=CYLINDRICAL_SURFACE('',#10,4.00000000000000000E2); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#18=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#23=VERTEX_POINT('',#22); +#24=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#25=VERTEX_POINT('',#24); +#26=CARTESIAN_POINT('',(4.00000000000000000E2,0.00000000000000000E0,0.00000000000000000E0)); +#27=VERTEX_POINT('',#26); +#28=CARTESIAN_POINT('',(4.00000000000000000E2,2.00000000000000000E3,0.00000000000000000E0)); +#29=VERTEX_POINT('',#28); +#30=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#31=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#32=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#33=AXIS2_PLACEMENT_3D('',#30,#31,#32); +#34=CIRCLE('',#33,1.00000000000000000E3); +#35=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#36=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#37=VECTOR('',#36,1.00000000000000000E0); +#38=LINE('',#35,#37); +#39=DEFINITIONAL_REPRESENTATION('',(#38),#1); +#40=PCURVE('',#6,#39); +#41=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#42=DIRECTION('',(1.00000000000000000E3,0.00000000000000000E0)); +#43=AXIS2_PLACEMENT_2D('',#41,#42); +#44=CIRCLE('',#43,1.00000000000000000E3); +#45=DEFINITIONAL_REPRESENTATION('',(#44),#1); +#46=PCURVE('',#16,#45); +#47=SURFACE_CURVE('',#34,(#40,#46),.CURVE_3D.); +#48=EDGE_CURVE('',#23,#23,#47,.T.); +#49=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#50=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#51=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#52=AXIS2_PLACEMENT_3D('',#49,#50,#51); +#53=CIRCLE('',#52,1.00000000000000000E3); +#54=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#55=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#56=VECTOR('',#55,1.00000000000000000E0); +#57=LINE('',#54,#56); +#58=DEFINITIONAL_REPRESENTATION('',(#57),#1); +#59=PCURVE('',#6,#58); +#60=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#61=DIRECTION('',(1.00000000000000000E3,0.00000000000000000E0)); +#62=AXIS2_PLACEMENT_2D('',#60,#61); +#63=CIRCLE('',#62,1.00000000000000000E3); +#64=DEFINITIONAL_REPRESENTATION('',(#63),#1); +#65=PCURVE('',#21,#64); +#66=SURFACE_CURVE('',#53,(#59,#65),.CURVE_3D.); +#67=EDGE_CURVE('',#25,#25,#66,.T.); +#68=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#69=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#70=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#71=AXIS2_PLACEMENT_3D('',#68,#69,#70); +#72=CIRCLE('',#71,4.00000000000000000E2); +#73=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#74=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#75=VECTOR('',#74,1.00000000000000000E0); +#76=LINE('',#73,#75); +#77=DEFINITIONAL_REPRESENTATION('',(#76),#1); +#78=PCURVE('',#11,#77); +#79=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#80=DIRECTION('',(4.00000000000000000E2,0.00000000000000000E0)); +#81=AXIS2_PLACEMENT_2D('',#79,#80); +#82=CIRCLE('',#81,4.00000000000000000E2); +#83=DEFINITIONAL_REPRESENTATION('',(#82),#1); +#84=PCURVE('',#16,#83); +#85=SURFACE_CURVE('',#72,(#78,#84),.CURVE_3D.); +#86=EDGE_CURVE('',#27,#27,#85,.T.); +#87=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#88=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#90=AXIS2_PLACEMENT_3D('',#87,#88,#89); +#91=CIRCLE('',#90,4.00000000000000000E2); +#92=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#93=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#94=VECTOR('',#93,1.00000000000000000E0); +#95=LINE('',#92,#94); +#96=DEFINITIONAL_REPRESENTATION('',(#95),#1); +#97=PCURVE('',#11,#96); +#98=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#99=DIRECTION('',(4.00000000000000000E2,0.00000000000000000E0)); +#100=AXIS2_PLACEMENT_2D('',#98,#99); +#101=CIRCLE('',#100,4.00000000000000000E2); +#102=DEFINITIONAL_REPRESENTATION('',(#101),#1); +#103=PCURVE('',#21,#102); +#104=SURFACE_CURVE('',#91,(#97,#103),.CURVE_3D.); +#105=EDGE_CURVE('',#29,#29,#104,.T.); +#106=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#107=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E3); +#109=LINE('',#106,#108); +#110=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#111=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#112=VECTOR('',#111,1.00000000000000000E3); +#113=LINE('',#110,#112); +#114=DEFINITIONAL_REPRESENTATION('',(#113),#1); +#115=PCURVE('',#6,#114); +#116=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#117=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#118=VECTOR('',#117,1.00000000000000000E3); +#119=LINE('',#116,#118); +#120=DEFINITIONAL_REPRESENTATION('',(#119),#1); +#121=PCURVE('',#6,#120); +#122=SEAM_CURVE('',#109,(#115,#121),.CURVE_3D.); +#123=EDGE_CURVE('',#23,#25,#122,.T.); +#124=CARTESIAN_POINT('',(4.00000000000000000E2,0.00000000000000000E0,0.00000000000000000E0)); +#125=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E3); +#127=LINE('',#124,#126); +#128=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#129=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#130=VECTOR('',#129,1.00000000000000000E3); +#131=LINE('',#128,#130); +#132=DEFINITIONAL_REPRESENTATION('',(#131),#1); +#133=PCURVE('',#11,#132); +#134=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#135=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#136=VECTOR('',#135,1.00000000000000000E3); +#137=LINE('',#134,#136); +#138=DEFINITIONAL_REPRESENTATION('',(#137),#1); +#139=PCURVE('',#11,#138); +#140=SEAM_CURVE('',#127,(#133,#139),.CURVE_3D.); +#141=EDGE_CURVE('',#27,#29,#140,.T.); +#142=ORIENTED_EDGE('',*,*,#48,.T.); +#143=ORIENTED_EDGE('',*,*,#123,.T.); +#144=ORIENTED_EDGE('',*,*,#67,.F.); +#145=ORIENTED_EDGE('',*,*,#123,.F.); +#146=EDGE_LOOP('',(#142,#143,#144,#145)); +#147=FACE_OUTER_BOUND('',#146,.T.); +#148=ADVANCED_FACE('outer',(#147),#6,.T.); +#149=ORIENTED_EDGE('',*,*,#86,.T.); +#150=ORIENTED_EDGE('',*,*,#141,.T.); +#151=ORIENTED_EDGE('',*,*,#105,.F.); +#152=ORIENTED_EDGE('',*,*,#141,.F.); +#153=EDGE_LOOP('',(#149,#150,#151,#152)); +#154=FACE_OUTER_BOUND('',#153,.T.); +#155=ADVANCED_FACE('inner',(#154),#11,.F.); +#156=ORIENTED_EDGE('',*,*,#48,.F.); +#157=EDGE_LOOP('',(#156)); +#158=FACE_OUTER_BOUND('',#157,.T.); +#159=ORIENTED_EDGE('',*,*,#86,.F.); +#160=EDGE_LOOP('',(#159)); +#161=FACE_BOUND('',#160,.T.); +#162=ADVANCED_FACE('lower_cap',(#158,#161),#16,.T.); +#163=ORIENTED_EDGE('',*,*,#67,.T.); +#164=EDGE_LOOP('',(#163)); +#165=FACE_OUTER_BOUND('',#164,.T.); +#166=ORIENTED_EDGE('',*,*,#105,.T.); +#167=EDGE_LOOP('',(#166)); +#168=FACE_BOUND('',#167,.T.); +#169=ADVANCED_FACE('upper_cap',(#165,#168),#21,.T.); +#170=CLOSED_SHELL('',(#148,#155,#162,#169)); +#171=MANIFOLD_SOLID_BREP('cylinder-with-hole-0',#170); +#172=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#173=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#174=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#175=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217094312E-5),#172,'distance_accuracy_value','exchange error bound'); +#176=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#175)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#172,#173,#174)) REPRESENTATION_CONTEXT('','')); +#177=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#171),#176); +#178=APPLICATION_CONTEXT('automotive design'); +#179=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#178); +#180=PRODUCT_CONTEXT('',#178,'mechanical'); +#181=PRODUCT('cylinder-with-hole','cylinder-with-hole','',(#180)); +#182=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#181,.NOT_KNOWN.); +#183=PRODUCT_DEFINITION_CONTEXT('part definition',#178,'design'); +#184=PRODUCT_DEFINITION('','',#182,#183); +#185=PRODUCT_DEFINITION_SHAPE('','',#184); +#186=SHAPE_DEFINITION_REPRESENTATION(#185,#177); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.mm.report.json new file mode 100644 index 0000000..5eec7ae --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":6,"exchange_error_bound":0.000010000028421709431,"faces":4,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.tess.json b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.tess.json new file mode 100644 index 0000000..a8b25c8 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder-with-hole.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[0,1,34,0,34,33,1,2,35,1,35,34,2,3,36,2,36,35,3,4,37,3,37,36,4,5,38,4,38,37,5,6,39,5,39,38,6,7,40,6,40,39,7,8,41,7,41,40,8,9,42,8,42,41,9,10,43,9,43,42,10,11,44,10,44,43,11,12,45,11,45,44,12,13,46,12,46,45,13,14,47,13,47,46,14,15,48,14,48,47,15,16,49,15,49,48,16,17,50,16,50,49,17,18,51,17,51,50,18,19,52,18,52,51,19,20,53,19,53,52,20,21,54,20,54,53,21,22,55,21,55,54,22,23,56,22,56,55,23,24,57,23,57,56,24,25,58,24,58,57,25,26,59,25,59,58,26,27,60,26,60,59,27,28,61,27,61,60,28,29,62,28,62,61,29,30,63,29,63,62,30,31,64,30,64,63,31,32,65,31,65,64,66,100,67,66,99,100,67,101,68,67,100,101,68,102,69,68,101,102,69,103,70,69,102,103,70,104,71,70,103,104,71,105,72,71,104,105,72,106,73,72,105,106,73,107,74,73,106,107,74,108,75,74,107,108,75,109,76,75,108,109,76,110,77,76,109,110,77,111,78,77,110,111,78,112,79,78,111,112,79,113,80,79,112,113,80,114,81,80,113,114,81,115,82,81,114,115,82,116,83,82,115,116,83,117,84,83,116,117,84,118,85,84,117,118,85,119,86,85,118,119,86,120,87,86,119,120,87,121,88,87,120,121,88,122,89,88,121,122,89,123,90,89,122,123,90,124,91,90,123,124,91,125,92,91,124,125,92,126,93,92,125,126,93,127,94,93,126,127,94,128,95,94,127,128,95,129,96,95,128,129,96,130,97,96,129,130,97,131,98,97,130,131,163,132,133,133,134,135,135,136,137,137,138,139,139,140,141,141,142,143,143,144,145,145,146,147,147,148,180,181,180,148,148,149,150,150,151,152,152,153,154,154,155,156,156,157,158,158,159,160,160,161,162,162,163,133,133,135,137,137,139,141,141,143,145,145,147,180,182,181,148,148,150,152,152,154,156,156,158,160,160,162,133,133,137,141,145,180,179,183,182,148,148,152,156,156,160,133,141,145,179,184,183,148,141,179,178,185,184,148,141,178,177,186,185,148,141,177,176,186,148,156,141,176,175,187,186,156,141,175,174,188,187,156,141,174,173,189,188,156,141,173,172,190,189,156,141,172,171,191,190,156,133,141,171,192,191,156,133,171,170,193,192,156,133,170,169,194,193,156,133,169,168,194,156,133,133,168,167,195,194,133,133,167,166,164,195,133,133,166,165,165,164,133,227,196,197,197,198,199,199,200,201,201,202,203,203,204,205,205,206,207,207,208,209,209,210,211,211,212,244,245,244,212,212,213,214,214,215,216,216,217,218,218,219,220,220,221,222,222,223,224,224,225,226,226,227,197,197,199,201,201,203,205,205,207,209,209,211,244,246,245,212,212,214,216,216,218,220,220,222,224,224,226,197,197,201,205,209,244,243,247,246,212,212,216,220,220,224,197,205,209,243,248,247,212,205,243,242,249,248,212,205,242,241,250,249,212,205,241,240,250,212,220,205,240,239,251,250,220,205,239,238,252,251,220,205,238,237,253,252,220,205,237,236,254,253,220,205,236,235,255,254,220,197,205,235,256,255,220,197,235,234,257,256,220,197,234,233,258,257,220,197,233,232,258,220,197,197,232,231,259,258,197,197,231,230,228,259,197,197,230,229,229,228,197],"normals":[1.0,0.0,0.0,0.9807852506637573,0.0,-0.19509032368659973,0.9238795042037964,0.0,-0.3826834261417389,0.8314695954322815,0.0,-0.5555702447891235,0.7071067690849304,0.0,-0.7071067690849304,0.5555702447891235,0.0,-0.8314695954322815,0.3826834261417389,0.0,-0.9238795042037964,0.19509032368659973,0.0,-0.9807852506637573,6.123234262925839e-17,0.0,-1.0,-0.19509032368659973,0.0,-0.9807852506637573,-0.3826834261417389,0.0,-0.9238795042037964,-0.5555702447891235,0.0,-0.8314695954322815,-0.7071067690849304,0.0,-0.7071067690849304,-0.8314695954322815,0.0,-0.5555702447891235,-0.9238795042037964,0.0,-0.3826834261417389,-0.9807852506637573,0.0,-0.19509032368659973,-1.0,0.0,-1.2246468525851679e-16,-0.9807852506637573,0.0,0.19509032368659973,-0.9238795042037964,0.0,0.3826834261417389,-0.8314695954322815,0.0,0.5555702447891235,-0.7071067690849304,0.0,0.7071067690849304,-0.5555702447891235,0.0,0.8314695954322815,-0.3826834261417389,0.0,0.9238795042037964,-0.19509032368659973,0.0,0.9807852506637573,-1.8369701465288538e-16,0.0,1.0,0.19509032368659973,0.0,0.9807852506637573,0.3826834261417389,0.0,0.9238795042037964,0.5555702447891235,0.0,0.8314695954322815,0.7071067690849304,0.0,0.7071067690849304,0.8314695954322815,0.0,0.5555702447891235,0.9238795042037964,0.0,0.3826834261417389,0.9807852506637573,0.0,0.19509032368659973,1.0,0.0,2.4492937051703357e-16,1.0,0.0,0.0,0.9807852506637573,0.0,-0.19509032368659973,0.9238795042037964,0.0,-0.3826834261417389,0.8314695954322815,0.0,-0.5555702447891235,0.7071067690849304,0.0,-0.7071067690849304,0.5555702447891235,0.0,-0.8314695954322815,0.3826834261417389,0.0,-0.9238795042037964,0.19509032368659973,0.0,-0.9807852506637573,6.123234262925839e-17,0.0,-1.0,-0.19509032368659973,0.0,-0.9807852506637573,-0.3826834261417389,0.0,-0.9238795042037964,-0.5555702447891235,0.0,-0.8314695954322815,-0.7071067690849304,0.0,-0.7071067690849304,-0.8314695954322815,0.0,-0.5555702447891235,-0.9238795042037964,0.0,-0.3826834261417389,-0.9807852506637573,0.0,-0.19509032368659973,-1.0,0.0,-1.2246468525851679e-16,-0.9807852506637573,0.0,0.19509032368659973,-0.9238795042037964,0.0,0.3826834261417389,-0.8314695954322815,0.0,0.5555702447891235,-0.7071067690849304,0.0,0.7071067690849304,-0.5555702447891235,0.0,0.8314695954322815,-0.3826834261417389,0.0,0.9238795042037964,-0.19509032368659973,0.0,0.9807852506637573,-1.8369701465288538e-16,0.0,1.0,0.19509032368659973,0.0,0.9807852506637573,0.3826834261417389,0.0,0.9238795042037964,0.5555702447891235,0.0,0.8314695954322815,0.7071067690849304,0.0,0.7071067690849304,0.8314695954322815,0.0,0.5555702447891235,0.9238795042037964,0.0,0.3826834261417389,0.9807852506637573,0.0,0.19509032368659973,1.0,0.0,2.4492937051703357e-16,-1.0,-0.0,-0.0,-0.9807852506637573,-0.0,0.19509032368659973,-0.9238795042037964,-0.0,0.3826834261417389,-0.8314695954322815,-0.0,0.5555702447891235,-0.7071067690849304,-0.0,0.7071067690849304,-0.5555702447891235,-0.0,0.8314695954322815,-0.3826834261417389,-0.0,0.9238795042037964,-0.19509032368659973,-0.0,0.9807852506637573,-6.123234262925839e-17,-0.0,1.0,0.19509032368659973,-0.0,0.9807852506637573,0.3826834261417389,-0.0,0.9238795042037964,0.5555702447891235,-0.0,0.8314695954322815,0.7071067690849304,-0.0,0.7071067690849304,0.8314695954322815,-0.0,0.5555702447891235,0.9238795042037964,-0.0,0.3826834261417389,0.9807852506637573,-0.0,0.19509032368659973,1.0,-0.0,1.2246468525851679e-16,0.9807852506637573,-0.0,-0.19509032368659973,0.9238795042037964,-0.0,-0.3826834261417389,0.8314695954322815,-0.0,-0.5555702447891235,0.7071067690849304,-0.0,-0.7071067690849304,0.5555702447891235,-0.0,-0.8314695954322815,0.3826834261417389,-0.0,-0.9238795042037964,0.19509032368659973,-0.0,-0.9807852506637573,1.8369701465288538e-16,-0.0,-1.0,-0.19509032368659973,-0.0,-0.9807852506637573,-0.3826834261417389,-0.0,-0.9238795042037964,-0.5555702447891235,-0.0,-0.8314695954322815,-0.7071067690849304,-0.0,-0.7071067690849304,-0.8314695954322815,-0.0,-0.5555702447891235,-0.9238795042037964,-0.0,-0.3826834261417389,-0.9807852506637573,-0.0,-0.19509032368659973,-1.0,-0.0,-2.4492937051703357e-16,-1.0,-0.0,-0.0,-0.9807852506637573,-0.0,0.19509032368659973,-0.9238795042037964,-0.0,0.3826834261417389,-0.8314695954322815,-0.0,0.5555702447891235,-0.7071067690849304,-0.0,0.7071067690849304,-0.5555702447891235,-0.0,0.8314695954322815,-0.3826834261417389,-0.0,0.9238795042037964,-0.19509032368659973,-0.0,0.9807852506637573,-6.123234262925839e-17,-0.0,1.0,0.19509032368659973,-0.0,0.9807852506637573,0.3826834261417389,-0.0,0.9238795042037964,0.5555702447891235,-0.0,0.8314695954322815,0.7071067690849304,-0.0,0.7071067690849304,0.8314695954322815,-0.0,0.5555702447891235,0.9238795042037964,-0.0,0.3826834261417389,0.9807852506637573,-0.0,0.19509032368659973,1.0,-0.0,1.2246468525851679e-16,0.9807852506637573,-0.0,-0.19509032368659973,0.9238795042037964,-0.0,-0.3826834261417389,0.8314695954322815,-0.0,-0.5555702447891235,0.7071067690849304,-0.0,-0.7071067690849304,0.5555702447891235,-0.0,-0.8314695954322815,0.3826834261417389,-0.0,-0.9238795042037964,0.19509032368659973,-0.0,-0.9807852506637573,1.8369701465288538e-16,-0.0,-1.0,-0.19509032368659973,-0.0,-0.9807852506637573,-0.3826834261417389,-0.0,-0.9238795042037964,-0.5555702447891235,-0.0,-0.8314695954322815,-0.7071067690849304,-0.0,-0.7071067690849304,-0.8314695954322815,-0.0,-0.5555702447891235,-0.9238795042037964,-0.0,-0.3826834261417389,-0.9807852506637573,-0.0,-0.19509032368659973,-1.0,-0.0,-2.4492937051703357e-16,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0],"outlineEdgeIds":[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,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,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],"outlinePositions":[1.0,0.0,0.0,0.9807852804032304,0.0,-0.19509032201612825,0.9807852804032304,0.0,-0.19509032201612825,0.9238795325112867,0.0,-0.3826834323650898,0.9238795325112867,0.0,-0.3826834323650898,0.8314696123025452,0.0,-0.5555702330196022,0.8314696123025452,0.0,-0.5555702330196022,0.7071067811865476,0.0,-0.7071067811865475,0.7071067811865476,0.0,-0.7071067811865475,0.5555702330196023,0.0,-0.8314696123025452,0.5555702330196023,0.0,-0.8314696123025452,0.38268343236508984,0.0,-0.9238795325112867,0.38268343236508984,0.0,-0.9238795325112867,0.19509032201612833,0.0,-0.9807852804032304,0.19509032201612833,0.0,-0.9807852804032304,6.123233995736766e-17,0.0,-1.0,6.123233995736766e-17,0.0,-1.0,-0.1950903220161282,0.0,-0.9807852804032304,-0.1950903220161282,0.0,-0.9807852804032304,-0.3826834323650897,0.0,-0.9238795325112867,-0.3826834323650897,0.0,-0.9238795325112867,-0.555570233019602,0.0,-0.8314696123025453,-0.555570233019602,0.0,-0.8314696123025453,-0.7071067811865475,0.0,-0.7071067811865476,-0.7071067811865475,0.0,-0.7071067811865476,-0.8314696123025453,0.0,-0.5555702330196022,-0.8314696123025453,0.0,-0.5555702330196022,-0.9238795325112867,0.0,-0.3826834323650899,-0.9238795325112867,0.0,-0.3826834323650899,-0.9807852804032304,0.0,-0.1950903220161286,-0.9807852804032304,0.0,-0.1950903220161286,-1.0,0.0,-1.2246467991473532e-16,-1.0,0.0,-1.2246467991473532e-16,-0.9807852804032304,0.0,0.19509032201612836,-0.9807852804032304,0.0,0.19509032201612836,-0.9238795325112868,0.0,0.38268343236508967,-0.9238795325112868,0.0,0.38268343236508967,-0.8314696123025455,0.0,0.555570233019602,-0.8314696123025455,0.0,0.555570233019602,-0.7071067811865477,0.0,0.7071067811865475,-0.7071067811865477,0.0,0.7071067811865475,-0.5555702330196022,0.0,0.8314696123025452,-0.5555702330196022,0.0,0.8314696123025452,-0.38268343236509034,0.0,0.9238795325112865,-0.38268343236509034,0.0,0.9238795325112865,-0.19509032201612866,0.0,0.9807852804032303,-0.19509032201612866,0.0,0.9807852804032303,-1.8369701987210297e-16,0.0,1.0,-1.8369701987210297e-16,0.0,1.0,0.1950903220161283,0.0,0.9807852804032304,0.1950903220161283,0.0,0.9807852804032304,0.38268343236509,0.0,0.9238795325112866,0.38268343236509,0.0,0.9238795325112866,0.5555702330196018,0.0,0.8314696123025455,0.5555702330196018,0.0,0.8314696123025455,0.7071067811865474,0.0,0.7071067811865477,0.7071067811865474,0.0,0.7071067811865477,0.8314696123025452,0.0,0.5555702330196022,0.8314696123025452,0.0,0.5555702330196022,0.9238795325112865,0.0,0.3826834323650904,0.9238795325112865,0.0,0.3826834323650904,0.9807852804032303,0.0,0.19509032201612872,0.9807852804032303,0.0,0.19509032201612872,1.0,0.0,0.0,1.0,2.0,0.0,0.9807852804032304,2.0,-0.19509032201612825,0.9807852804032304,2.0,-0.19509032201612825,0.9238795325112867,2.0,-0.3826834323650898,0.9238795325112867,2.0,-0.3826834323650898,0.8314696123025452,2.0,-0.5555702330196022,0.8314696123025452,2.0,-0.5555702330196022,0.7071067811865476,2.0,-0.7071067811865475,0.7071067811865476,2.0,-0.7071067811865475,0.5555702330196023,2.0,-0.8314696123025452,0.5555702330196023,2.0,-0.8314696123025452,0.38268343236508984,2.0,-0.9238795325112867,0.38268343236508984,2.0,-0.9238795325112867,0.19509032201612833,2.0,-0.9807852804032304,0.19509032201612833,2.0,-0.9807852804032304,6.123233995736766e-17,2.0,-1.0,6.123233995736766e-17,2.0,-1.0,-0.1950903220161282,2.0,-0.9807852804032304,-0.1950903220161282,2.0,-0.9807852804032304,-0.3826834323650897,2.0,-0.9238795325112867,-0.3826834323650897,2.0,-0.9238795325112867,-0.555570233019602,2.0,-0.8314696123025453,-0.555570233019602,2.0,-0.8314696123025453,-0.7071067811865475,2.0,-0.7071067811865476,-0.7071067811865475,2.0,-0.7071067811865476,-0.8314696123025453,2.0,-0.5555702330196022,-0.8314696123025453,2.0,-0.5555702330196022,-0.9238795325112867,2.0,-0.3826834323650899,-0.9238795325112867,2.0,-0.3826834323650899,-0.9807852804032304,2.0,-0.1950903220161286,-0.9807852804032304,2.0,-0.1950903220161286,-1.0,2.0,-1.2246467991473532e-16,-1.0,2.0,-1.2246467991473532e-16,-0.9807852804032304,2.0,0.19509032201612836,-0.9807852804032304,2.0,0.19509032201612836,-0.9238795325112868,2.0,0.38268343236508967,-0.9238795325112868,2.0,0.38268343236508967,-0.8314696123025455,2.0,0.555570233019602,-0.8314696123025455,2.0,0.555570233019602,-0.7071067811865477,2.0,0.7071067811865475,-0.7071067811865477,2.0,0.7071067811865475,-0.5555702330196022,2.0,0.8314696123025452,-0.5555702330196022,2.0,0.8314696123025452,-0.38268343236509034,2.0,0.9238795325112865,-0.38268343236509034,2.0,0.9238795325112865,-0.19509032201612866,2.0,0.9807852804032303,-0.19509032201612866,2.0,0.9807852804032303,-1.8369701987210297e-16,2.0,1.0,-1.8369701987210297e-16,2.0,1.0,0.1950903220161283,2.0,0.9807852804032304,0.1950903220161283,2.0,0.9807852804032304,0.38268343236509,2.0,0.9238795325112866,0.38268343236509,2.0,0.9238795325112866,0.5555702330196018,2.0,0.8314696123025455,0.5555702330196018,2.0,0.8314696123025455,0.7071067811865474,2.0,0.7071067811865477,0.7071067811865474,2.0,0.7071067811865477,0.8314696123025452,2.0,0.5555702330196022,0.8314696123025452,2.0,0.5555702330196022,0.9238795325112865,2.0,0.3826834323650904,0.9238795325112865,2.0,0.3826834323650904,0.9807852804032303,2.0,0.19509032201612872,0.9807852804032303,2.0,0.19509032201612872,1.0,2.0,0.0,0.4,0.0,0.0,0.3923141121612922,0.0,-0.0780361288064513,0.3923141121612922,0.0,-0.0780361288064513,0.36955181300451473,0.0,-0.15307337294603593,0.36955181300451473,0.0,-0.15307337294603593,0.3325878449210181,0.0,-0.22222809320784087,0.3325878449210181,0.0,-0.22222809320784087,0.28284271247461906,0.0,-0.282842712474619,0.28284271247461906,0.0,-0.282842712474619,0.22222809320784093,0.0,-0.3325878449210181,0.22222809320784093,0.0,-0.3325878449210181,0.15307337294603596,0.0,-0.36955181300451473,0.15307337294603596,0.0,-0.36955181300451473,0.07803612880645133,0.0,-0.3923141121612922,0.07803612880645133,0.0,-0.3923141121612922,2.4492935982947065e-17,0.0,-0.4,2.4492935982947065e-17,0.0,-0.4,-0.07803612880645128,0.0,-0.3923141121612922,-0.07803612880645128,0.0,-0.3923141121612922,-0.1530733729460359,0.0,-0.36955181300451473,-0.1530733729460359,0.0,-0.36955181300451473,-0.2222280932078408,0.0,-0.33258784492101817,-0.2222280932078408,0.0,-0.33258784492101817,-0.282842712474619,0.0,-0.28284271247461906,-0.282842712474619,0.0,-0.28284271247461906,-0.33258784492101817,0.0,-0.22222809320784087,-0.33258784492101817,0.0,-0.22222809320784087,-0.36955181300451473,0.0,-0.15307337294603596,-0.36955181300451473,0.0,-0.15307337294603596,-0.3923141121612922,0.0,-0.07803612880645144,-0.3923141121612922,0.0,-0.07803612880645144,-0.4,0.0,-4.898587196589413e-17,-0.4,0.0,-4.898587196589413e-17,-0.3923141121612922,0.0,0.07803612880645135,-0.3923141121612922,0.0,0.07803612880645135,-0.3695518130045148,0.0,0.15307337294603587,-0.3695518130045148,0.0,0.15307337294603587,-0.3325878449210182,0.0,0.2222280932078408,-0.3325878449210182,0.0,0.2222280932078408,-0.28284271247461906,0.0,0.282842712474619,-0.28284271247461906,0.0,0.282842712474619,-0.22222809320784087,0.0,0.3325878449210181,-0.22222809320784087,0.0,0.3325878449210181,-0.15307337294603615,0.0,0.3695518130045146,-0.15307337294603615,0.0,0.3695518130045146,-0.07803612880645147,0.0,0.39231411216129214,-0.07803612880645147,0.0,0.39231411216129214,-7.347880794884119e-17,0.0,0.4,-7.347880794884119e-17,0.0,0.4,0.07803612880645133,0.0,0.3923141121612922,0.07803612880645133,0.0,0.3923141121612922,0.153073372946036,0.0,0.3695518130045147,0.153073372946036,0.0,0.3695518130045147,0.22222809320784076,0.0,0.3325878449210182,0.22222809320784076,0.0,0.3325878449210182,0.28284271247461895,0.0,0.28284271247461906,0.28284271247461895,0.0,0.28284271247461906,0.3325878449210181,0.0,0.22222809320784087,0.3325878449210181,0.0,0.22222809320784087,0.3695518130045146,0.0,0.15307337294603618,0.3695518130045146,0.0,0.15307337294603618,0.39231411216129214,0.0,0.0780361288064515,0.39231411216129214,0.0,0.0780361288064515,0.4,0.0,0.0,0.4,2.0,0.0,0.3923141121612922,2.0,-0.0780361288064513,0.3923141121612922,2.0,-0.0780361288064513,0.36955181300451473,2.0,-0.15307337294603593,0.36955181300451473,2.0,-0.15307337294603593,0.3325878449210181,2.0,-0.22222809320784087,0.3325878449210181,2.0,-0.22222809320784087,0.28284271247461906,2.0,-0.282842712474619,0.28284271247461906,2.0,-0.282842712474619,0.22222809320784093,2.0,-0.3325878449210181,0.22222809320784093,2.0,-0.3325878449210181,0.15307337294603596,2.0,-0.36955181300451473,0.15307337294603596,2.0,-0.36955181300451473,0.07803612880645133,2.0,-0.3923141121612922,0.07803612880645133,2.0,-0.3923141121612922,2.4492935982947065e-17,2.0,-0.4,2.4492935982947065e-17,2.0,-0.4,-0.07803612880645128,2.0,-0.3923141121612922,-0.07803612880645128,2.0,-0.3923141121612922,-0.1530733729460359,2.0,-0.36955181300451473,-0.1530733729460359,2.0,-0.36955181300451473,-0.2222280932078408,2.0,-0.33258784492101817,-0.2222280932078408,2.0,-0.33258784492101817,-0.282842712474619,2.0,-0.28284271247461906,-0.282842712474619,2.0,-0.28284271247461906,-0.33258784492101817,2.0,-0.22222809320784087,-0.33258784492101817,2.0,-0.22222809320784087,-0.36955181300451473,2.0,-0.15307337294603596,-0.36955181300451473,2.0,-0.15307337294603596,-0.3923141121612922,2.0,-0.07803612880645144,-0.3923141121612922,2.0,-0.07803612880645144,-0.4,2.0,-4.898587196589413e-17,-0.4,2.0,-4.898587196589413e-17,-0.3923141121612922,2.0,0.07803612880645135,-0.3923141121612922,2.0,0.07803612880645135,-0.3695518130045148,2.0,0.15307337294603587,-0.3695518130045148,2.0,0.15307337294603587,-0.3325878449210182,2.0,0.2222280932078408,-0.3325878449210182,2.0,0.2222280932078408,-0.28284271247461906,2.0,0.282842712474619,-0.28284271247461906,2.0,0.282842712474619,-0.22222809320784087,2.0,0.3325878449210181,-0.22222809320784087,2.0,0.3325878449210181,-0.15307337294603615,2.0,0.3695518130045146,-0.15307337294603615,2.0,0.3695518130045146,-0.07803612880645147,2.0,0.39231411216129214,-0.07803612880645147,2.0,0.39231411216129214,-7.347880794884119e-17,2.0,0.4,-7.347880794884119e-17,2.0,0.4,0.07803612880645133,2.0,0.3923141121612922,0.07803612880645133,2.0,0.3923141121612922,0.153073372946036,2.0,0.3695518130045147,0.153073372946036,2.0,0.3695518130045147,0.22222809320784076,2.0,0.3325878449210182,0.22222809320784076,2.0,0.3325878449210182,0.28284271247461895,2.0,0.28284271247461906,0.28284271247461895,2.0,0.28284271247461906,0.3325878449210181,2.0,0.22222809320784087,0.3325878449210181,2.0,0.22222809320784087,0.3695518130045146,2.0,0.15307337294603618,0.3695518130045146,2.0,0.15307337294603618,0.39231411216129214,2.0,0.0780361288064515,0.39231411216129214,2.0,0.0780361288064515,0.4,2.0,0.0],"positions":[1.0,0.0,0.0,0.9807852804032304,0.0,-0.19509032201612825,0.9238795325112867,0.0,-0.3826834323650898,0.8314696123025452,0.0,-0.5555702330196022,0.7071067811865476,0.0,-0.7071067811865475,0.5555702330196023,0.0,-0.8314696123025452,0.38268343236508984,0.0,-0.9238795325112867,0.19509032201612833,0.0,-0.9807852804032304,6.123233995736766e-17,0.0,-1.0,-0.1950903220161282,0.0,-0.9807852804032304,-0.3826834323650897,0.0,-0.9238795325112867,-0.555570233019602,0.0,-0.8314696123025453,-0.7071067811865475,0.0,-0.7071067811865476,-0.8314696123025453,0.0,-0.5555702330196022,-0.9238795325112867,0.0,-0.3826834323650899,-0.9807852804032304,0.0,-0.1950903220161286,-1.0,0.0,-1.2246467991473532e-16,-0.9807852804032304,0.0,0.19509032201612836,-0.9238795325112868,0.0,0.38268343236508967,-0.8314696123025455,0.0,0.555570233019602,-0.7071067811865477,0.0,0.7071067811865475,-0.5555702330196022,0.0,0.8314696123025452,-0.38268343236509034,0.0,0.9238795325112865,-0.19509032201612866,0.0,0.9807852804032303,-1.8369701987210297e-16,0.0,1.0,0.1950903220161283,0.0,0.9807852804032304,0.38268343236509,0.0,0.9238795325112866,0.5555702330196018,0.0,0.8314696123025455,0.7071067811865474,0.0,0.7071067811865477,0.8314696123025452,0.0,0.5555702330196022,0.9238795325112865,0.0,0.3826834323650904,0.9807852804032303,0.0,0.19509032201612872,1.0,0.0,0.0,1.0,2.0,0.0,0.9807852804032304,2.0,-0.19509032201612825,0.9238795325112867,2.0,-0.3826834323650898,0.8314696123025452,2.0,-0.5555702330196022,0.7071067811865476,2.0,-0.7071067811865475,0.5555702330196023,2.0,-0.8314696123025452,0.38268343236508984,2.0,-0.9238795325112867,0.19509032201612833,2.0,-0.9807852804032304,6.123233995736766e-17,2.0,-1.0,-0.1950903220161282,2.0,-0.9807852804032304,-0.3826834323650897,2.0,-0.9238795325112867,-0.555570233019602,2.0,-0.8314696123025453,-0.7071067811865475,2.0,-0.7071067811865476,-0.8314696123025453,2.0,-0.5555702330196022,-0.9238795325112867,2.0,-0.3826834323650899,-0.9807852804032304,2.0,-0.1950903220161286,-1.0,2.0,-1.2246467991473532e-16,-0.9807852804032304,2.0,0.19509032201612836,-0.9238795325112868,2.0,0.38268343236508967,-0.8314696123025455,2.0,0.555570233019602,-0.7071067811865477,2.0,0.7071067811865475,-0.5555702330196022,2.0,0.8314696123025452,-0.38268343236509034,2.0,0.9238795325112865,-0.19509032201612866,2.0,0.9807852804032303,-1.8369701987210297e-16,2.0,1.0,0.1950903220161283,2.0,0.9807852804032304,0.38268343236509,2.0,0.9238795325112866,0.5555702330196018,2.0,0.8314696123025455,0.7071067811865474,2.0,0.7071067811865477,0.8314696123025452,2.0,0.5555702330196022,0.9238795325112865,2.0,0.3826834323650904,0.9807852804032303,2.0,0.19509032201612872,1.0,2.0,0.0,0.4,0.0,0.0,0.3923141121612922,0.0,-0.0780361288064513,0.36955181300451473,0.0,-0.15307337294603593,0.3325878449210181,0.0,-0.22222809320784087,0.28284271247461906,0.0,-0.282842712474619,0.22222809320784093,0.0,-0.3325878449210181,0.15307337294603596,0.0,-0.36955181300451473,0.07803612880645133,0.0,-0.3923141121612922,2.4492935982947065e-17,0.0,-0.4,-0.07803612880645128,0.0,-0.3923141121612922,-0.1530733729460359,0.0,-0.36955181300451473,-0.2222280932078408,0.0,-0.33258784492101817,-0.282842712474619,0.0,-0.28284271247461906,-0.33258784492101817,0.0,-0.22222809320784087,-0.36955181300451473,0.0,-0.15307337294603596,-0.3923141121612922,0.0,-0.07803612880645144,-0.4,0.0,-4.898587196589413e-17,-0.3923141121612922,0.0,0.07803612880645135,-0.3695518130045148,0.0,0.15307337294603587,-0.3325878449210182,0.0,0.2222280932078408,-0.28284271247461906,0.0,0.282842712474619,-0.22222809320784087,0.0,0.3325878449210181,-0.15307337294603615,0.0,0.3695518130045146,-0.07803612880645147,0.0,0.39231411216129214,-7.347880794884119e-17,0.0,0.4,0.07803612880645133,0.0,0.3923141121612922,0.153073372946036,0.0,0.3695518130045147,0.22222809320784076,0.0,0.3325878449210182,0.28284271247461895,0.0,0.28284271247461906,0.3325878449210181,0.0,0.22222809320784087,0.3695518130045146,0.0,0.15307337294603618,0.39231411216129214,0.0,0.0780361288064515,0.4,0.0,0.0,0.4,2.0,0.0,0.3923141121612922,2.0,-0.0780361288064513,0.36955181300451473,2.0,-0.15307337294603593,0.3325878449210181,2.0,-0.22222809320784087,0.28284271247461906,2.0,-0.282842712474619,0.22222809320784093,2.0,-0.3325878449210181,0.15307337294603596,2.0,-0.36955181300451473,0.07803612880645133,2.0,-0.3923141121612922,2.4492935982947065e-17,2.0,-0.4,-0.07803612880645128,2.0,-0.3923141121612922,-0.1530733729460359,2.0,-0.36955181300451473,-0.2222280932078408,2.0,-0.33258784492101817,-0.282842712474619,2.0,-0.28284271247461906,-0.33258784492101817,2.0,-0.22222809320784087,-0.36955181300451473,2.0,-0.15307337294603596,-0.3923141121612922,2.0,-0.07803612880645144,-0.4,2.0,-4.898587196589413e-17,-0.3923141121612922,2.0,0.07803612880645135,-0.3695518130045148,2.0,0.15307337294603587,-0.3325878449210182,2.0,0.2222280932078408,-0.28284271247461906,2.0,0.282842712474619,-0.22222809320784087,2.0,0.3325878449210181,-0.15307337294603615,2.0,0.3695518130045146,-0.07803612880645147,2.0,0.39231411216129214,-7.347880794884119e-17,2.0,0.4,0.07803612880645133,2.0,0.3923141121612922,0.153073372946036,2.0,0.3695518130045147,0.22222809320784076,2.0,0.3325878449210182,0.28284271247461895,2.0,0.28284271247461906,0.3325878449210181,2.0,0.22222809320784087,0.3695518130045146,2.0,0.15307337294603618,0.39231411216129214,2.0,0.0780361288064515,0.4,2.0,0.0,1.0,0.0,0.0,0.9807852804032303,0.0,0.19509032201612872,0.9238795325112865,0.0,0.3826834323650904,0.8314696123025452,0.0,0.5555702330196022,0.7071067811865474,0.0,0.7071067811865477,0.5555702330196018,0.0,0.8314696123025455,0.38268343236509,0.0,0.9238795325112866,0.1950903220161283,0.0,0.9807852804032304,-1.8369701987210297e-16,0.0,1.0,-0.19509032201612866,0.0,0.9807852804032303,-0.38268343236509034,0.0,0.9238795325112865,-0.5555702330196022,0.0,0.8314696123025452,-0.7071067811865477,0.0,0.7071067811865475,-0.8314696123025455,0.0,0.555570233019602,-0.9238795325112868,0.0,0.38268343236508967,-0.9807852804032304,0.0,0.19509032201612836,-1.0,0.0,-1.2246467991473532e-16,-0.9807852804032304,0.0,-0.1950903220161286,-0.9238795325112867,0.0,-0.3826834323650899,-0.8314696123025453,0.0,-0.5555702330196022,-0.7071067811865475,0.0,-0.7071067811865476,-0.555570233019602,0.0,-0.8314696123025453,-0.3826834323650897,0.0,-0.9238795325112867,-0.1950903220161282,0.0,-0.9807852804032304,6.123233995736766e-17,0.0,-1.0,0.19509032201612833,0.0,-0.9807852804032304,0.38268343236508984,0.0,-0.9238795325112867,0.5555702330196023,0.0,-0.8314696123025452,0.7071067811865476,0.0,-0.7071067811865475,0.8314696123025452,0.0,-0.5555702330196022,0.9238795325112867,0.0,-0.3826834323650898,0.9807852804032304,0.0,-0.19509032201612825,0.4,0.0,0.0,0.39231411216129214,0.0,0.0780361288064515,0.3695518130045146,0.0,0.15307337294603618,0.3325878449210181,0.0,0.22222809320784087,0.28284271247461895,0.0,0.28284271247461906,0.22222809320784076,0.0,0.3325878449210182,0.153073372946036,0.0,0.3695518130045147,0.07803612880645133,0.0,0.3923141121612922,-7.347880794884119e-17,0.0,0.4,-0.07803612880645147,0.0,0.39231411216129214,-0.15307337294603615,0.0,0.3695518130045146,-0.22222809320784087,0.0,0.3325878449210181,-0.28284271247461906,0.0,0.282842712474619,-0.3325878449210182,0.0,0.2222280932078408,-0.3695518130045148,0.0,0.15307337294603587,-0.3923141121612922,0.0,0.07803612880645135,-0.4,0.0,-4.898587196589413e-17,-0.3923141121612922,0.0,-0.07803612880645144,-0.36955181300451473,0.0,-0.15307337294603596,-0.33258784492101817,0.0,-0.22222809320784087,-0.282842712474619,0.0,-0.28284271247461906,-0.2222280932078408,0.0,-0.33258784492101817,-0.1530733729460359,0.0,-0.36955181300451473,-0.07803612880645128,0.0,-0.3923141121612922,2.4492935982947065e-17,0.0,-0.4,0.07803612880645133,0.0,-0.3923141121612922,0.15307337294603596,0.0,-0.36955181300451473,0.22222809320784093,0.0,-0.3325878449210181,0.28284271247461906,0.0,-0.282842712474619,0.3325878449210181,0.0,-0.22222809320784087,0.36955181300451473,0.0,-0.15307337294603593,0.3923141121612922,0.0,-0.0780361288064513,1.0,2.0,0.0,0.9807852804032304,2.0,-0.19509032201612825,0.9238795325112867,2.0,-0.3826834323650898,0.8314696123025452,2.0,-0.5555702330196022,0.7071067811865476,2.0,-0.7071067811865475,0.5555702330196023,2.0,-0.8314696123025452,0.38268343236508984,2.0,-0.9238795325112867,0.19509032201612833,2.0,-0.9807852804032304,6.123233995736766e-17,2.0,-1.0,-0.1950903220161282,2.0,-0.9807852804032304,-0.3826834323650897,2.0,-0.9238795325112867,-0.555570233019602,2.0,-0.8314696123025453,-0.7071067811865475,2.0,-0.7071067811865476,-0.8314696123025453,2.0,-0.5555702330196022,-0.9238795325112867,2.0,-0.3826834323650899,-0.9807852804032304,2.0,-0.1950903220161286,-1.0,2.0,-1.2246467991473532e-16,-0.9807852804032304,2.0,0.19509032201612836,-0.9238795325112868,2.0,0.38268343236508967,-0.8314696123025455,2.0,0.555570233019602,-0.7071067811865477,2.0,0.7071067811865475,-0.5555702330196022,2.0,0.8314696123025452,-0.38268343236509034,2.0,0.9238795325112865,-0.19509032201612866,2.0,0.9807852804032303,-1.8369701987210297e-16,2.0,1.0,0.1950903220161283,2.0,0.9807852804032304,0.38268343236509,2.0,0.9238795325112866,0.5555702330196018,2.0,0.8314696123025455,0.7071067811865474,2.0,0.7071067811865477,0.8314696123025452,2.0,0.5555702330196022,0.9238795325112865,2.0,0.3826834323650904,0.9807852804032303,2.0,0.19509032201612872,0.4,2.0,0.0,0.3923141121612922,2.0,-0.0780361288064513,0.36955181300451473,2.0,-0.15307337294603593,0.3325878449210181,2.0,-0.22222809320784087,0.28284271247461906,2.0,-0.282842712474619,0.22222809320784093,2.0,-0.3325878449210181,0.15307337294603596,2.0,-0.36955181300451473,0.07803612880645133,2.0,-0.3923141121612922,2.4492935982947065e-17,2.0,-0.4,-0.07803612880645128,2.0,-0.3923141121612922,-0.1530733729460359,2.0,-0.36955181300451473,-0.2222280932078408,2.0,-0.33258784492101817,-0.282842712474619,2.0,-0.28284271247461906,-0.33258784492101817,2.0,-0.22222809320784087,-0.36955181300451473,2.0,-0.15307337294603596,-0.3923141121612922,2.0,-0.07803612880645144,-0.4,2.0,-4.898587196589413e-17,-0.3923141121612922,2.0,0.07803612880645135,-0.3695518130045148,2.0,0.15307337294603587,-0.3325878449210182,2.0,0.2222280932078408,-0.28284271247461906,2.0,0.282842712474619,-0.22222809320784087,2.0,0.3325878449210181,-0.15307337294603615,2.0,0.3695518130045146,-0.07803612880645147,2.0,0.39231411216129214,-7.347880794884119e-17,2.0,0.4,0.07803612880645133,2.0,0.3923141121612922,0.153073372946036,2.0,0.3695518130045147,0.22222809320784076,2.0,0.3325878449210182,0.28284271247461895,2.0,0.28284271247461906,0.3325878449210181,2.0,0.22222809320784087,0.3695518130045146,2.0,0.15307337294603618,0.39231411216129214,2.0,0.0780361288064515],"revision":0,"triangleFaceIds":[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,0,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,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cylinder.brep.json b/main/opengeometry/tests/fixtures/cases/cylinder.brep.json new file mode 100644 index 0000000..ad6612f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"cylinder","revision":0,"geometry":{"surfaces":[{"kind":"Cylinder","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}}],"curves":[{"kind":"Circle","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0},{"kind":"Circle","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0},{"kind":"Line","origin":[1.0,0.0,0.0],"direction":[0.0,1.0,0.0]}],"pcurves":[{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[6.283185307179586,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[1.0,0.0],"axis_b":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[1.0,0.0,0.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[1.0,2.0,0.0],"outgoing_halfedge":2,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":0,"twin_halfedge":4,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":2,"twin_halfedge":5,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":2.0}},"halfedge":1,"twin_halfedge":3,"tolerance":1e-8,"chart_seam":true}],"halfedges":[{"id":0,"from":0,"to":0,"twin":4,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":0,"to":1,"twin":3,"next":2,"prev":0,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":1,"to":1,"twin":5,"next":3,"prev":1,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":1,"to":0,"twin":1,"next":0,"prev":2,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":0,"to":0,"twin":0,"next":4,"prev":4,"edge":0,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":1,"to":1,"twin":2,"next":5,"prev":5,"edge":1,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":5,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false},{"id":2,"start_halfedge":5,"face_ref":2,"is_hole":false}],"faces":[{"id":0,"key":"lateral","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.0,"hi":6.283185307179586},{"lo":0.0,"hi":2.0}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cylinder","body":"cylinder","key":"lateral","face":0}],"role":"Authored","reversed":false}},{"id":1,"key":"lower_cap","surface":1,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.0,"hi":1.0},{"lo":-1.0,"hi":1.0}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cylinder","body":"cylinder","key":"lower_cap","face":1}],"role":"Authored","reversed":false}},{"id":2,"key":"upper_cap","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.0,"hi":1.0},{"lo":-1.0,"hi":1.0}],"outer":2,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"cylinder","body":"cylinder","key":"upper_cap","face":2}],"role":"Authored","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,1,2],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cylinder.step.m b/main/opengeometry/tests/fixtures/cases/cylinder.step.m new file mode 100644 index 0000000..ed60d1b --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder.step.m @@ -0,0 +1,117 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('cylinder','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=CYLINDRICAL_SURFACE('',#5,1.00000000000000000E0); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#18=VERTEX_POINT('',#17); +#19=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#20=VERTEX_POINT('',#19); +#21=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#22=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#24=AXIS2_PLACEMENT_3D('',#21,#22,#23); +#25=CIRCLE('',#24,1.00000000000000000E0); +#26=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#27=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#28=VECTOR('',#27,1.00000000000000000E0); +#29=LINE('',#26,#28); +#30=DEFINITIONAL_REPRESENTATION('',(#29),#1); +#31=PCURVE('',#6,#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#33=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#34=AXIS2_PLACEMENT_2D('',#32,#33); +#35=CIRCLE('',#34,1.00000000000000000E0); +#36=DEFINITIONAL_REPRESENTATION('',(#35),#1); +#37=PCURVE('',#11,#36); +#38=SURFACE_CURVE('',#25,(#31,#37),.CURVE_3D.); +#39=EDGE_CURVE('',#18,#18,#38,.T.); +#40=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#41=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#42=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#43=AXIS2_PLACEMENT_3D('',#40,#41,#42); +#44=CIRCLE('',#43,1.00000000000000000E0); +#45=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#46=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#47=VECTOR('',#46,1.00000000000000000E0); +#48=LINE('',#45,#47); +#49=DEFINITIONAL_REPRESENTATION('',(#48),#1); +#50=PCURVE('',#6,#49); +#51=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#52=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#53=AXIS2_PLACEMENT_2D('',#51,#52); +#54=CIRCLE('',#53,1.00000000000000000E0); +#55=DEFINITIONAL_REPRESENTATION('',(#54),#1); +#56=PCURVE('',#16,#55); +#57=SURFACE_CURVE('',#44,(#50,#56),.CURVE_3D.); +#58=EDGE_CURVE('',#20,#20,#57,.T.); +#59=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#60=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#61=VECTOR('',#60,1.00000000000000000E0); +#62=LINE('',#59,#61); +#63=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#64=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#65=VECTOR('',#64,1.00000000000000000E0); +#66=LINE('',#63,#65); +#67=DEFINITIONAL_REPRESENTATION('',(#66),#1); +#68=PCURVE('',#6,#67); +#69=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#70=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#71=VECTOR('',#70,1.00000000000000000E0); +#72=LINE('',#69,#71); +#73=DEFINITIONAL_REPRESENTATION('',(#72),#1); +#74=PCURVE('',#6,#73); +#75=SEAM_CURVE('',#62,(#68,#74),.CURVE_3D.); +#76=EDGE_CURVE('',#18,#20,#75,.T.); +#77=ORIENTED_EDGE('',*,*,#39,.T.); +#78=ORIENTED_EDGE('',*,*,#76,.T.); +#79=ORIENTED_EDGE('',*,*,#58,.F.); +#80=ORIENTED_EDGE('',*,*,#76,.F.); +#81=EDGE_LOOP('',(#77,#78,#79,#80)); +#82=FACE_OUTER_BOUND('',#81,.T.); +#83=ADVANCED_FACE('lateral',(#82),#6,.T.); +#84=ORIENTED_EDGE('',*,*,#39,.F.); +#85=EDGE_LOOP('',(#84)); +#86=FACE_OUTER_BOUND('',#85,.T.); +#87=ADVANCED_FACE('lower_cap',(#86),#11,.T.); +#88=ORIENTED_EDGE('',*,*,#58,.T.); +#89=EDGE_LOOP('',(#88)); +#90=FACE_OUTER_BOUND('',#89,.T.); +#91=ADVANCED_FACE('upper_cap',(#90),#16,.T.); +#92=CLOSED_SHELL('',(#83,#87,#91)); +#93=MANIFOLD_SOLID_BREP('cylinder-0',#92); +#94=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#95=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#96=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#97=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217094306E-8),#94,'distance_accuracy_value','exchange error bound'); +#98=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#97)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#94,#95,#96)) REPRESENTATION_CONTEXT('','')); +#99=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#93),#98); +#100=APPLICATION_CONTEXT('automotive design'); +#101=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#100); +#102=PRODUCT_CONTEXT('',#100,'mechanical'); +#103=PRODUCT('cylinder','cylinder','',(#102)); +#104=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#103,.NOT_KNOWN.); +#105=PRODUCT_DEFINITION_CONTEXT('part definition',#100,'design'); +#106=PRODUCT_DEFINITION('','',#104,#105); +#107=PRODUCT_DEFINITION_SHAPE('','',#106); +#108=SHAPE_DEFINITION_REPRESENTATION(#107,#99); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/cylinder.step.m.report.json b/main/opengeometry/tests/fixtures/cases/cylinder.step.m.report.json new file mode 100644 index 0000000..4cd759c --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":3,"exchange_error_bound":1.000002842170943e-8,"faces":3,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cylinder.step.mm b/main/opengeometry/tests/fixtures/cases/cylinder.step.mm new file mode 100644 index 0000000..dd0acbf --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder.step.mm @@ -0,0 +1,117 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('cylinder','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=CYLINDRICAL_SURFACE('',#5,1.00000000000000000E3); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#13=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#18=VERTEX_POINT('',#17); +#19=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,0.00000000000000000E0)); +#20=VERTEX_POINT('',#19); +#21=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#22=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#24=AXIS2_PLACEMENT_3D('',#21,#22,#23); +#25=CIRCLE('',#24,1.00000000000000000E3); +#26=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#27=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#28=VECTOR('',#27,1.00000000000000000E0); +#29=LINE('',#26,#28); +#30=DEFINITIONAL_REPRESENTATION('',(#29),#1); +#31=PCURVE('',#6,#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#33=DIRECTION('',(1.00000000000000000E3,0.00000000000000000E0)); +#34=AXIS2_PLACEMENT_2D('',#32,#33); +#35=CIRCLE('',#34,1.00000000000000000E3); +#36=DEFINITIONAL_REPRESENTATION('',(#35),#1); +#37=PCURVE('',#11,#36); +#38=SURFACE_CURVE('',#25,(#31,#37),.CURVE_3D.); +#39=EDGE_CURVE('',#18,#18,#38,.T.); +#40=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#41=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#42=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#43=AXIS2_PLACEMENT_3D('',#40,#41,#42); +#44=CIRCLE('',#43,1.00000000000000000E3); +#45=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#46=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#47=VECTOR('',#46,1.00000000000000000E0); +#48=LINE('',#45,#47); +#49=DEFINITIONAL_REPRESENTATION('',(#48),#1); +#50=PCURVE('',#6,#49); +#51=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#52=DIRECTION('',(1.00000000000000000E3,0.00000000000000000E0)); +#53=AXIS2_PLACEMENT_2D('',#51,#52); +#54=CIRCLE('',#53,1.00000000000000000E3); +#55=DEFINITIONAL_REPRESENTATION('',(#54),#1); +#56=PCURVE('',#16,#55); +#57=SURFACE_CURVE('',#44,(#50,#56),.CURVE_3D.); +#58=EDGE_CURVE('',#20,#20,#57,.T.); +#59=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#60=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#61=VECTOR('',#60,1.00000000000000000E3); +#62=LINE('',#59,#61); +#63=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#64=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#65=VECTOR('',#64,1.00000000000000000E3); +#66=LINE('',#63,#65); +#67=DEFINITIONAL_REPRESENTATION('',(#66),#1); +#68=PCURVE('',#6,#67); +#69=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#70=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#71=VECTOR('',#70,1.00000000000000000E3); +#72=LINE('',#69,#71); +#73=DEFINITIONAL_REPRESENTATION('',(#72),#1); +#74=PCURVE('',#6,#73); +#75=SEAM_CURVE('',#62,(#68,#74),.CURVE_3D.); +#76=EDGE_CURVE('',#18,#20,#75,.T.); +#77=ORIENTED_EDGE('',*,*,#39,.T.); +#78=ORIENTED_EDGE('',*,*,#76,.T.); +#79=ORIENTED_EDGE('',*,*,#58,.F.); +#80=ORIENTED_EDGE('',*,*,#76,.F.); +#81=EDGE_LOOP('',(#77,#78,#79,#80)); +#82=FACE_OUTER_BOUND('',#81,.T.); +#83=ADVANCED_FACE('lateral',(#82),#6,.T.); +#84=ORIENTED_EDGE('',*,*,#39,.F.); +#85=EDGE_LOOP('',(#84)); +#86=FACE_OUTER_BOUND('',#85,.T.); +#87=ADVANCED_FACE('lower_cap',(#86),#11,.T.); +#88=ORIENTED_EDGE('',*,*,#58,.T.); +#89=EDGE_LOOP('',(#88)); +#90=FACE_OUTER_BOUND('',#89,.T.); +#91=ADVANCED_FACE('upper_cap',(#90),#16,.T.); +#92=CLOSED_SHELL('',(#83,#87,#91)); +#93=MANIFOLD_SOLID_BREP('cylinder-0',#92); +#94=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#95=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#96=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#97=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217094312E-5),#94,'distance_accuracy_value','exchange error bound'); +#98=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#97)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#94,#95,#96)) REPRESENTATION_CONTEXT('','')); +#99=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#93),#98); +#100=APPLICATION_CONTEXT('automotive design'); +#101=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#100); +#102=PRODUCT_CONTEXT('',#100,'mechanical'); +#103=PRODUCT('cylinder','cylinder','',(#102)); +#104=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#103,.NOT_KNOWN.); +#105=PRODUCT_DEFINITION_CONTEXT('part definition',#100,'design'); +#106=PRODUCT_DEFINITION('','',#104,#105); +#107=PRODUCT_DEFINITION_SHAPE('','',#106); +#108=SHAPE_DEFINITION_REPRESENTATION(#107,#99); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/cylinder.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/cylinder.step.mm.report.json new file mode 100644 index 0000000..5167910 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":3,"exchange_error_bound":0.000010000028421709431,"faces":3,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/cylinder.tess.json b/main/opengeometry/tests/fixtures/cases/cylinder.tess.json new file mode 100644 index 0000000..4e2b79a --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/cylinder.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[0,1,34,0,34,33,1,2,35,1,35,34,2,3,36,2,36,35,3,4,37,3,37,36,4,5,38,4,38,37,5,6,39,5,39,38,6,7,40,6,40,39,7,8,41,7,41,40,8,9,42,8,42,41,9,10,43,9,43,42,10,11,44,10,44,43,11,12,45,11,45,44,12,13,46,12,46,45,13,14,47,13,47,46,14,15,48,14,48,47,15,16,49,15,49,48,16,17,50,16,50,49,17,18,51,17,51,50,18,19,52,18,52,51,19,20,53,19,53,52,20,21,54,20,54,53,21,22,55,21,55,54,22,23,56,22,56,55,23,24,57,23,57,56,24,25,58,24,58,57,25,26,59,25,59,58,26,27,60,26,60,59,27,28,61,27,61,60,28,29,62,28,62,61,29,30,63,29,63,62,30,31,64,30,64,63,31,32,65,31,65,64,96,97,66,66,67,68,68,69,70,70,71,72,72,73,74,74,75,76,76,77,78,78,79,80,80,81,82,82,83,84,84,85,86,86,87,88,88,89,90,90,91,92,92,93,94,94,95,96,96,66,68,68,70,72,72,74,76,76,78,80,80,82,84,84,86,88,88,90,92,92,94,96,96,68,72,72,76,80,80,84,88,88,92,96,96,72,80,80,88,96,128,129,98,98,99,100,100,101,102,102,103,104,104,105,106,106,107,108,108,109,110,110,111,112,112,113,114,114,115,116,116,117,118,118,119,120,120,121,122,122,123,124,124,125,126,126,127,128,128,98,100,100,102,104,104,106,108,108,110,112,112,114,116,116,118,120,120,122,124,124,126,128,128,100,104,104,108,112,112,116,120,120,124,128,128,104,112,112,120,128],"normals":[1.0,0.0,0.0,0.9807852506637573,0.0,-0.19509032368659973,0.9238795042037964,0.0,-0.3826834261417389,0.8314695954322815,0.0,-0.5555702447891235,0.7071067690849304,0.0,-0.7071067690849304,0.5555702447891235,0.0,-0.8314695954322815,0.3826834261417389,0.0,-0.9238795042037964,0.19509032368659973,0.0,-0.9807852506637573,6.123234262925839e-17,0.0,-1.0,-0.19509032368659973,0.0,-0.9807852506637573,-0.3826834261417389,0.0,-0.9238795042037964,-0.5555702447891235,0.0,-0.8314695954322815,-0.7071067690849304,0.0,-0.7071067690849304,-0.8314695954322815,0.0,-0.5555702447891235,-0.9238795042037964,0.0,-0.3826834261417389,-0.9807852506637573,0.0,-0.19509032368659973,-1.0,0.0,-1.2246468525851679e-16,-0.9807852506637573,0.0,0.19509032368659973,-0.9238795042037964,0.0,0.3826834261417389,-0.8314695954322815,0.0,0.5555702447891235,-0.7071067690849304,0.0,0.7071067690849304,-0.5555702447891235,0.0,0.8314695954322815,-0.3826834261417389,0.0,0.9238795042037964,-0.19509032368659973,0.0,0.9807852506637573,-1.8369701465288538e-16,0.0,1.0,0.19509032368659973,0.0,0.9807852506637573,0.3826834261417389,0.0,0.9238795042037964,0.5555702447891235,0.0,0.8314695954322815,0.7071067690849304,0.0,0.7071067690849304,0.8314695954322815,0.0,0.5555702447891235,0.9238795042037964,0.0,0.3826834261417389,0.9807852506637573,0.0,0.19509032368659973,1.0,0.0,2.4492937051703357e-16,1.0,0.0,0.0,0.9807852506637573,0.0,-0.19509032368659973,0.9238795042037964,0.0,-0.3826834261417389,0.8314695954322815,0.0,-0.5555702447891235,0.7071067690849304,0.0,-0.7071067690849304,0.5555702447891235,0.0,-0.8314695954322815,0.3826834261417389,0.0,-0.9238795042037964,0.19509032368659973,0.0,-0.9807852506637573,6.123234262925839e-17,0.0,-1.0,-0.19509032368659973,0.0,-0.9807852506637573,-0.3826834261417389,0.0,-0.9238795042037964,-0.5555702447891235,0.0,-0.8314695954322815,-0.7071067690849304,0.0,-0.7071067690849304,-0.8314695954322815,0.0,-0.5555702447891235,-0.9238795042037964,0.0,-0.3826834261417389,-0.9807852506637573,0.0,-0.19509032368659973,-1.0,0.0,-1.2246468525851679e-16,-0.9807852506637573,0.0,0.19509032368659973,-0.9238795042037964,0.0,0.3826834261417389,-0.8314695954322815,0.0,0.5555702447891235,-0.7071067690849304,0.0,0.7071067690849304,-0.5555702447891235,0.0,0.8314695954322815,-0.3826834261417389,0.0,0.9238795042037964,-0.19509032368659973,0.0,0.9807852506637573,-1.8369701465288538e-16,0.0,1.0,0.19509032368659973,0.0,0.9807852506637573,0.3826834261417389,0.0,0.9238795042037964,0.5555702447891235,0.0,0.8314695954322815,0.7071067690849304,0.0,0.7071067690849304,0.8314695954322815,0.0,0.5555702447891235,0.9238795042037964,0.0,0.3826834261417389,0.9807852506637573,0.0,0.19509032368659973,1.0,0.0,2.4492937051703357e-16,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0],"outlineEdgeIds":[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,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],"outlinePositions":[1.0,0.0,0.0,0.9807852804032304,0.0,-0.19509032201612825,0.9807852804032304,0.0,-0.19509032201612825,0.9238795325112867,0.0,-0.3826834323650898,0.9238795325112867,0.0,-0.3826834323650898,0.8314696123025452,0.0,-0.5555702330196022,0.8314696123025452,0.0,-0.5555702330196022,0.7071067811865476,0.0,-0.7071067811865475,0.7071067811865476,0.0,-0.7071067811865475,0.5555702330196023,0.0,-0.8314696123025452,0.5555702330196023,0.0,-0.8314696123025452,0.38268343236508984,0.0,-0.9238795325112867,0.38268343236508984,0.0,-0.9238795325112867,0.19509032201612833,0.0,-0.9807852804032304,0.19509032201612833,0.0,-0.9807852804032304,6.123233995736766e-17,0.0,-1.0,6.123233995736766e-17,0.0,-1.0,-0.1950903220161282,0.0,-0.9807852804032304,-0.1950903220161282,0.0,-0.9807852804032304,-0.3826834323650897,0.0,-0.9238795325112867,-0.3826834323650897,0.0,-0.9238795325112867,-0.555570233019602,0.0,-0.8314696123025453,-0.555570233019602,0.0,-0.8314696123025453,-0.7071067811865475,0.0,-0.7071067811865476,-0.7071067811865475,0.0,-0.7071067811865476,-0.8314696123025453,0.0,-0.5555702330196022,-0.8314696123025453,0.0,-0.5555702330196022,-0.9238795325112867,0.0,-0.3826834323650899,-0.9238795325112867,0.0,-0.3826834323650899,-0.9807852804032304,0.0,-0.1950903220161286,-0.9807852804032304,0.0,-0.1950903220161286,-1.0,0.0,-1.2246467991473532e-16,-1.0,0.0,-1.2246467991473532e-16,-0.9807852804032304,0.0,0.19509032201612836,-0.9807852804032304,0.0,0.19509032201612836,-0.9238795325112868,0.0,0.38268343236508967,-0.9238795325112868,0.0,0.38268343236508967,-0.8314696123025455,0.0,0.555570233019602,-0.8314696123025455,0.0,0.555570233019602,-0.7071067811865477,0.0,0.7071067811865475,-0.7071067811865477,0.0,0.7071067811865475,-0.5555702330196022,0.0,0.8314696123025452,-0.5555702330196022,0.0,0.8314696123025452,-0.38268343236509034,0.0,0.9238795325112865,-0.38268343236509034,0.0,0.9238795325112865,-0.19509032201612866,0.0,0.9807852804032303,-0.19509032201612866,0.0,0.9807852804032303,-1.8369701987210297e-16,0.0,1.0,-1.8369701987210297e-16,0.0,1.0,0.1950903220161283,0.0,0.9807852804032304,0.1950903220161283,0.0,0.9807852804032304,0.38268343236509,0.0,0.9238795325112866,0.38268343236509,0.0,0.9238795325112866,0.5555702330196018,0.0,0.8314696123025455,0.5555702330196018,0.0,0.8314696123025455,0.7071067811865474,0.0,0.7071067811865477,0.7071067811865474,0.0,0.7071067811865477,0.8314696123025452,0.0,0.5555702330196022,0.8314696123025452,0.0,0.5555702330196022,0.9238795325112865,0.0,0.3826834323650904,0.9238795325112865,0.0,0.3826834323650904,0.9807852804032303,0.0,0.19509032201612872,0.9807852804032303,0.0,0.19509032201612872,1.0,0.0,0.0,1.0,2.0,0.0,0.9807852804032304,2.0,-0.19509032201612825,0.9807852804032304,2.0,-0.19509032201612825,0.9238795325112867,2.0,-0.3826834323650898,0.9238795325112867,2.0,-0.3826834323650898,0.8314696123025452,2.0,-0.5555702330196022,0.8314696123025452,2.0,-0.5555702330196022,0.7071067811865476,2.0,-0.7071067811865475,0.7071067811865476,2.0,-0.7071067811865475,0.5555702330196023,2.0,-0.8314696123025452,0.5555702330196023,2.0,-0.8314696123025452,0.38268343236508984,2.0,-0.9238795325112867,0.38268343236508984,2.0,-0.9238795325112867,0.19509032201612833,2.0,-0.9807852804032304,0.19509032201612833,2.0,-0.9807852804032304,6.123233995736766e-17,2.0,-1.0,6.123233995736766e-17,2.0,-1.0,-0.1950903220161282,2.0,-0.9807852804032304,-0.1950903220161282,2.0,-0.9807852804032304,-0.3826834323650897,2.0,-0.9238795325112867,-0.3826834323650897,2.0,-0.9238795325112867,-0.555570233019602,2.0,-0.8314696123025453,-0.555570233019602,2.0,-0.8314696123025453,-0.7071067811865475,2.0,-0.7071067811865476,-0.7071067811865475,2.0,-0.7071067811865476,-0.8314696123025453,2.0,-0.5555702330196022,-0.8314696123025453,2.0,-0.5555702330196022,-0.9238795325112867,2.0,-0.3826834323650899,-0.9238795325112867,2.0,-0.3826834323650899,-0.9807852804032304,2.0,-0.1950903220161286,-0.9807852804032304,2.0,-0.1950903220161286,-1.0,2.0,-1.2246467991473532e-16,-1.0,2.0,-1.2246467991473532e-16,-0.9807852804032304,2.0,0.19509032201612836,-0.9807852804032304,2.0,0.19509032201612836,-0.9238795325112868,2.0,0.38268343236508967,-0.9238795325112868,2.0,0.38268343236508967,-0.8314696123025455,2.0,0.555570233019602,-0.8314696123025455,2.0,0.555570233019602,-0.7071067811865477,2.0,0.7071067811865475,-0.7071067811865477,2.0,0.7071067811865475,-0.5555702330196022,2.0,0.8314696123025452,-0.5555702330196022,2.0,0.8314696123025452,-0.38268343236509034,2.0,0.9238795325112865,-0.38268343236509034,2.0,0.9238795325112865,-0.19509032201612866,2.0,0.9807852804032303,-0.19509032201612866,2.0,0.9807852804032303,-1.8369701987210297e-16,2.0,1.0,-1.8369701987210297e-16,2.0,1.0,0.1950903220161283,2.0,0.9807852804032304,0.1950903220161283,2.0,0.9807852804032304,0.38268343236509,2.0,0.9238795325112866,0.38268343236509,2.0,0.9238795325112866,0.5555702330196018,2.0,0.8314696123025455,0.5555702330196018,2.0,0.8314696123025455,0.7071067811865474,2.0,0.7071067811865477,0.7071067811865474,2.0,0.7071067811865477,0.8314696123025452,2.0,0.5555702330196022,0.8314696123025452,2.0,0.5555702330196022,0.9238795325112865,2.0,0.3826834323650904,0.9238795325112865,2.0,0.3826834323650904,0.9807852804032303,2.0,0.19509032201612872,0.9807852804032303,2.0,0.19509032201612872,1.0,2.0,0.0],"positions":[1.0,0.0,0.0,0.9807852804032304,0.0,-0.19509032201612825,0.9238795325112867,0.0,-0.3826834323650898,0.8314696123025452,0.0,-0.5555702330196022,0.7071067811865476,0.0,-0.7071067811865475,0.5555702330196023,0.0,-0.8314696123025452,0.38268343236508984,0.0,-0.9238795325112867,0.19509032201612833,0.0,-0.9807852804032304,6.123233995736766e-17,0.0,-1.0,-0.1950903220161282,0.0,-0.9807852804032304,-0.3826834323650897,0.0,-0.9238795325112867,-0.555570233019602,0.0,-0.8314696123025453,-0.7071067811865475,0.0,-0.7071067811865476,-0.8314696123025453,0.0,-0.5555702330196022,-0.9238795325112867,0.0,-0.3826834323650899,-0.9807852804032304,0.0,-0.1950903220161286,-1.0,0.0,-1.2246467991473532e-16,-0.9807852804032304,0.0,0.19509032201612836,-0.9238795325112868,0.0,0.38268343236508967,-0.8314696123025455,0.0,0.555570233019602,-0.7071067811865477,0.0,0.7071067811865475,-0.5555702330196022,0.0,0.8314696123025452,-0.38268343236509034,0.0,0.9238795325112865,-0.19509032201612866,0.0,0.9807852804032303,-1.8369701987210297e-16,0.0,1.0,0.1950903220161283,0.0,0.9807852804032304,0.38268343236509,0.0,0.9238795325112866,0.5555702330196018,0.0,0.8314696123025455,0.7071067811865474,0.0,0.7071067811865477,0.8314696123025452,0.0,0.5555702330196022,0.9238795325112865,0.0,0.3826834323650904,0.9807852804032303,0.0,0.19509032201612872,1.0,0.0,0.0,1.0,2.0,0.0,0.9807852804032304,2.0,-0.19509032201612825,0.9238795325112867,2.0,-0.3826834323650898,0.8314696123025452,2.0,-0.5555702330196022,0.7071067811865476,2.0,-0.7071067811865475,0.5555702330196023,2.0,-0.8314696123025452,0.38268343236508984,2.0,-0.9238795325112867,0.19509032201612833,2.0,-0.9807852804032304,6.123233995736766e-17,2.0,-1.0,-0.1950903220161282,2.0,-0.9807852804032304,-0.3826834323650897,2.0,-0.9238795325112867,-0.555570233019602,2.0,-0.8314696123025453,-0.7071067811865475,2.0,-0.7071067811865476,-0.8314696123025453,2.0,-0.5555702330196022,-0.9238795325112867,2.0,-0.3826834323650899,-0.9807852804032304,2.0,-0.1950903220161286,-1.0,2.0,-1.2246467991473532e-16,-0.9807852804032304,2.0,0.19509032201612836,-0.9238795325112868,2.0,0.38268343236508967,-0.8314696123025455,2.0,0.555570233019602,-0.7071067811865477,2.0,0.7071067811865475,-0.5555702330196022,2.0,0.8314696123025452,-0.38268343236509034,2.0,0.9238795325112865,-0.19509032201612866,2.0,0.9807852804032303,-1.8369701987210297e-16,2.0,1.0,0.1950903220161283,2.0,0.9807852804032304,0.38268343236509,2.0,0.9238795325112866,0.5555702330196018,2.0,0.8314696123025455,0.7071067811865474,2.0,0.7071067811865477,0.8314696123025452,2.0,0.5555702330196022,0.9238795325112865,2.0,0.3826834323650904,0.9807852804032303,2.0,0.19509032201612872,1.0,2.0,0.0,1.0,0.0,0.0,0.9807852804032303,0.0,0.19509032201612872,0.9238795325112865,0.0,0.3826834323650904,0.8314696123025452,0.0,0.5555702330196022,0.7071067811865474,0.0,0.7071067811865477,0.5555702330196018,0.0,0.8314696123025455,0.38268343236509,0.0,0.9238795325112866,0.1950903220161283,0.0,0.9807852804032304,-1.8369701987210297e-16,0.0,1.0,-0.19509032201612866,0.0,0.9807852804032303,-0.38268343236509034,0.0,0.9238795325112865,-0.5555702330196022,0.0,0.8314696123025452,-0.7071067811865477,0.0,0.7071067811865475,-0.8314696123025455,0.0,0.555570233019602,-0.9238795325112868,0.0,0.38268343236508967,-0.9807852804032304,0.0,0.19509032201612836,-1.0,0.0,-1.2246467991473532e-16,-0.9807852804032304,0.0,-0.1950903220161286,-0.9238795325112867,0.0,-0.3826834323650899,-0.8314696123025453,0.0,-0.5555702330196022,-0.7071067811865475,0.0,-0.7071067811865476,-0.555570233019602,0.0,-0.8314696123025453,-0.3826834323650897,0.0,-0.9238795325112867,-0.1950903220161282,0.0,-0.9807852804032304,6.123233995736766e-17,0.0,-1.0,0.19509032201612833,0.0,-0.9807852804032304,0.38268343236508984,0.0,-0.9238795325112867,0.5555702330196023,0.0,-0.8314696123025452,0.7071067811865476,0.0,-0.7071067811865475,0.8314696123025452,0.0,-0.5555702330196022,0.9238795325112867,0.0,-0.3826834323650898,0.9807852804032304,0.0,-0.19509032201612825,1.0,2.0,0.0,0.9807852804032304,2.0,-0.19509032201612825,0.9238795325112867,2.0,-0.3826834323650898,0.8314696123025452,2.0,-0.5555702330196022,0.7071067811865476,2.0,-0.7071067811865475,0.5555702330196023,2.0,-0.8314696123025452,0.38268343236508984,2.0,-0.9238795325112867,0.19509032201612833,2.0,-0.9807852804032304,6.123233995736766e-17,2.0,-1.0,-0.1950903220161282,2.0,-0.9807852804032304,-0.3826834323650897,2.0,-0.9238795325112867,-0.555570233019602,2.0,-0.8314696123025453,-0.7071067811865475,2.0,-0.7071067811865476,-0.8314696123025453,2.0,-0.5555702330196022,-0.9238795325112867,2.0,-0.3826834323650899,-0.9807852804032304,2.0,-0.1950903220161286,-1.0,2.0,-1.2246467991473532e-16,-0.9807852804032304,2.0,0.19509032201612836,-0.9238795325112868,2.0,0.38268343236508967,-0.8314696123025455,2.0,0.555570233019602,-0.7071067811865477,2.0,0.7071067811865475,-0.5555702330196022,2.0,0.8314696123025452,-0.38268343236509034,2.0,0.9238795325112865,-0.19509032201612866,2.0,0.9807852804032303,-1.8369701987210297e-16,2.0,1.0,0.1950903220161283,2.0,0.9807852804032304,0.38268343236509,2.0,0.9238795325112866,0.5555702330196018,2.0,0.8314696123025455,0.7071067811865474,2.0,0.7071067811865477,0.8314696123025452,2.0,0.5555702330196022,0.9238795325112865,2.0,0.3826834323650904,0.9807852804032303,2.0,0.19509032201612872],"revision":0,"triangleFaceIds":[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,0,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,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/frustum.brep.json b/main/opengeometry/tests/fixtures/cases/frustum.brep.json new file mode 100644 index 0000000..a887523 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/frustum.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"frustum","revision":0,"geometry":{"surfaces":[{"kind":"Cone","frame":{"origin":[0.0,3.3333333333333335,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"semi_angle":0.2914567944778671},{"kind":"Plane","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]}}],"curves":[{"kind":"Circle","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"radius":0.4},{"kind":"Circle","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"radius":1.0},{"kind":"Line","origin":[0.4,2.0,0.0],"direction":[0.2873478855663454,-0.9578262852211513,0.0]}],"pcurves":[{"kind":"Line2","origin":[0.0,1.3333333333333335],"direction":[1.0,0.0]},{"kind":"Line2","origin":[6.283185307179586,1.3333333333333335],"direction":[0.0,0.9578262852211513]},{"kind":"Line2","origin":[0.0,3.3333333333333335],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,1.3333333333333335],"direction":[0.0,0.9578262852211513]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4]},{"kind":"Conic2","origin":[0.0,0.0],"axis_a":[1.0,0.0],"axis_b":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[0.4,2.0,0.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[1.0,0.0,0.0],"outgoing_halfedge":2,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":0,"twin_halfedge":4,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":2,"twin_halfedge":5,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":2.08806130178211}},"halfedge":1,"twin_halfedge":3,"tolerance":1e-8,"chart_seam":true}],"halfedges":[{"id":0,"from":0,"to":0,"twin":4,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":0,"to":1,"twin":3,"next":2,"prev":0,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":1,"to":1,"twin":5,"next":3,"prev":1,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":1,"to":0,"twin":1,"next":0,"prev":2,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":0,"to":0,"twin":0,"next":4,"prev":4,"edge":0,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":1,"to":1,"twin":2,"next":5,"prev":5,"edge":1,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":5,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false},{"id":2,"start_halfedge":5,"face_ref":2,"is_hole":false}],"faces":[{"id":0,"key":"lateral","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.0,"hi":6.283185307179586},{"lo":1.3333333333333335,"hi":3.3333333333333335}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"frustum","body":"frustum","key":"lateral","face":0}],"role":"Authored","reversed":false}},{"id":1,"key":"lower_cap","surface":1,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-0.4,"hi":0.4},{"lo":-0.4,"hi":0.4}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"frustum","body":"frustum","key":"lower_cap","face":1}],"role":"Authored","reversed":false}},{"id":2,"key":"upper_cap","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.0,"hi":1.0},{"lo":-1.0,"hi":1.0}],"outer":2,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"frustum","body":"frustum","key":"upper_cap","face":2}],"role":"Authored","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,1,2],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/frustum.step.m b/main/opengeometry/tests/fixtures/cases/frustum.step.m new file mode 100644 index 0000000..a46367f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/frustum.step.m @@ -0,0 +1,117 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('frustum','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,3.33333333333333348E0,0.00000000000000000E0)); +#3=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=CONICAL_SURFACE('',#5,0.,2.91456794477867098E-1); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(4.00000000000000022E-1,2.00000000000000000E0,0.00000000000000000E0)); +#18=VERTEX_POINT('',#17); +#19=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#20=VERTEX_POINT('',#19); +#21=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#22=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#23=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#24=AXIS2_PLACEMENT_3D('',#21,#22,#23); +#25=CIRCLE('',#24,4.00000000000000022E-1); +#26=CARTESIAN_POINT('',(0.00000000000000000E0,1.39204086785474024E0)); +#27=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#28=VECTOR('',#27,1.00000000000000000E0); +#29=LINE('',#26,#28); +#30=DEFINITIONAL_REPRESENTATION('',(#29),#1); +#31=PCURVE('',#6,#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#33=DIRECTION('',(4.00000000000000022E-1,0.00000000000000000E0)); +#34=AXIS2_PLACEMENT_2D('',#32,#33); +#35=CIRCLE('',#34,4.00000000000000022E-1); +#36=DEFINITIONAL_REPRESENTATION('',(#35),#1); +#37=PCURVE('',#11,#36); +#38=SURFACE_CURVE('',#25,(#31,#37),.CURVE_3D.); +#39=EDGE_CURVE('',#18,#18,#38,.T.); +#40=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#41=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#42=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#43=AXIS2_PLACEMENT_3D('',#40,#41,#42); +#44=CIRCLE('',#43,1.00000000000000000E0); +#45=CARTESIAN_POINT('',(0.00000000000000000E0,3.48010216963685037E0)); +#46=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#47=VECTOR('',#46,1.00000000000000000E0); +#48=LINE('',#45,#47); +#49=DEFINITIONAL_REPRESENTATION('',(#48),#1); +#50=PCURVE('',#6,#49); +#51=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#52=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#53=AXIS2_PLACEMENT_2D('',#51,#52); +#54=CIRCLE('',#53,1.00000000000000000E0); +#55=DEFINITIONAL_REPRESENTATION('',(#54),#1); +#56=PCURVE('',#16,#55); +#57=SURFACE_CURVE('',#44,(#50,#56),.CURVE_3D.); +#58=EDGE_CURVE('',#20,#20,#57,.T.); +#59=CARTESIAN_POINT('',(4.00000000000000022E-1,2.00000000000000000E0,0.00000000000000000E0)); +#60=DIRECTION('',(2.87347885566345440E-1,-9.57826285221151430E-1,0.00000000000000000E0)); +#61=VECTOR('',#60,9.99999999999999889E-1); +#62=LINE('',#59,#61); +#63=CARTESIAN_POINT('',(6.28318530717958623E0,1.39204086785474024E0)); +#64=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#65=VECTOR('',#64,1.00000000000000000E0); +#66=LINE('',#63,#65); +#67=DEFINITIONAL_REPRESENTATION('',(#66),#1); +#68=PCURVE('',#6,#67); +#69=CARTESIAN_POINT('',(0.00000000000000000E0,1.39204086785474024E0)); +#70=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#71=VECTOR('',#70,1.00000000000000000E0); +#72=LINE('',#69,#71); +#73=DEFINITIONAL_REPRESENTATION('',(#72),#1); +#74=PCURVE('',#6,#73); +#75=SEAM_CURVE('',#62,(#68,#74),.CURVE_3D.); +#76=EDGE_CURVE('',#18,#20,#75,.T.); +#77=ORIENTED_EDGE('',*,*,#39,.T.); +#78=ORIENTED_EDGE('',*,*,#76,.T.); +#79=ORIENTED_EDGE('',*,*,#58,.F.); +#80=ORIENTED_EDGE('',*,*,#76,.F.); +#81=EDGE_LOOP('',(#77,#78,#79,#80)); +#82=FACE_OUTER_BOUND('',#81,.T.); +#83=ADVANCED_FACE('lateral',(#82),#6,.T.); +#84=ORIENTED_EDGE('',*,*,#39,.F.); +#85=EDGE_LOOP('',(#84)); +#86=FACE_OUTER_BOUND('',#85,.T.); +#87=ADVANCED_FACE('lower_cap',(#86),#11,.T.); +#88=ORIENTED_EDGE('',*,*,#58,.T.); +#89=EDGE_LOOP('',(#88)); +#90=FACE_OUTER_BOUND('',#89,.T.); +#91=ADVANCED_FACE('upper_cap',(#90),#16,.T.); +#92=CLOSED_SHELL('',(#83,#87,#91)); +#93=MANIFOLD_SOLID_BREP('frustum-0',#92); +#94=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#95=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#96=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#97=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000473695157181E-8),#94,'distance_accuracy_value','exchange error bound'); +#98=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#97)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#94,#95,#96)) REPRESENTATION_CONTEXT('','')); +#99=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#93),#98); +#100=APPLICATION_CONTEXT('automotive design'); +#101=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#100); +#102=PRODUCT_CONTEXT('',#100,'mechanical'); +#103=PRODUCT('frustum','frustum','',(#102)); +#104=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#103,.NOT_KNOWN.); +#105=PRODUCT_DEFINITION_CONTEXT('part definition',#100,'design'); +#106=PRODUCT_DEFINITION('','',#104,#105); +#107=PRODUCT_DEFINITION_SHAPE('','',#106); +#108=SHAPE_DEFINITION_REPRESENTATION(#107,#99); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/frustum.step.m.report.json b/main/opengeometry/tests/fixtures/cases/frustum.step.m.report.json new file mode 100644 index 0000000..ce7ff89 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/frustum.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":3,"exchange_error_bound":1.0000047369515718e-8,"faces":3,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/frustum.step.mm b/main/opengeometry/tests/fixtures/cases/frustum.step.mm new file mode 100644 index 0000000..e595338 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/frustum.step.mm @@ -0,0 +1,117 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('frustum','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,3.33333333333333348E3,0.00000000000000000E0)); +#3=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=CONICAL_SURFACE('',#5,0.,2.91456794477867098E-1); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#8=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#13=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(4.00000000000000000E2,2.00000000000000000E3,0.00000000000000000E0)); +#18=VERTEX_POINT('',#17); +#19=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#20=VERTEX_POINT('',#19); +#21=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#22=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#23=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#24=AXIS2_PLACEMENT_3D('',#21,#22,#23); +#25=CIRCLE('',#24,4.00000000000000000E2); +#26=CARTESIAN_POINT('',(0.00000000000000000E0,1.39204086785474033E3)); +#27=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#28=VECTOR('',#27,1.00000000000000000E0); +#29=LINE('',#26,#28); +#30=DEFINITIONAL_REPRESENTATION('',(#29),#1); +#31=PCURVE('',#6,#30); +#32=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#33=DIRECTION('',(4.00000000000000000E2,0.00000000000000000E0)); +#34=AXIS2_PLACEMENT_2D('',#32,#33); +#35=CIRCLE('',#34,4.00000000000000000E2); +#36=DEFINITIONAL_REPRESENTATION('',(#35),#1); +#37=PCURVE('',#11,#36); +#38=SURFACE_CURVE('',#25,(#31,#37),.CURVE_3D.); +#39=EDGE_CURVE('',#18,#18,#38,.T.); +#40=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#41=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#42=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#43=AXIS2_PLACEMENT_3D('',#40,#41,#42); +#44=CIRCLE('',#43,1.00000000000000000E3); +#45=CARTESIAN_POINT('',(0.00000000000000000E0,3.48010216963685025E3)); +#46=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#47=VECTOR('',#46,1.00000000000000000E0); +#48=LINE('',#45,#47); +#49=DEFINITIONAL_REPRESENTATION('',(#48),#1); +#50=PCURVE('',#6,#49); +#51=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#52=DIRECTION('',(1.00000000000000000E3,0.00000000000000000E0)); +#53=AXIS2_PLACEMENT_2D('',#51,#52); +#54=CIRCLE('',#53,1.00000000000000000E3); +#55=DEFINITIONAL_REPRESENTATION('',(#54),#1); +#56=PCURVE('',#16,#55); +#57=SURFACE_CURVE('',#44,(#50,#56),.CURVE_3D.); +#58=EDGE_CURVE('',#20,#20,#57,.T.); +#59=CARTESIAN_POINT('',(4.00000000000000000E2,2.00000000000000000E3,0.00000000000000000E0)); +#60=DIRECTION('',(2.87347885566345440E-1,-9.57826285221151430E-1,0.00000000000000000E0)); +#61=VECTOR('',#60,9.99999999999999886E2); +#62=LINE('',#59,#61); +#63=CARTESIAN_POINT('',(6.28318530717958623E0,1.39204086785474033E3)); +#64=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#65=VECTOR('',#64,1.00000000000000000E3); +#66=LINE('',#63,#65); +#67=DEFINITIONAL_REPRESENTATION('',(#66),#1); +#68=PCURVE('',#6,#67); +#69=CARTESIAN_POINT('',(0.00000000000000000E0,1.39204086785474033E3)); +#70=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#71=VECTOR('',#70,1.00000000000000000E3); +#72=LINE('',#69,#71); +#73=DEFINITIONAL_REPRESENTATION('',(#72),#1); +#74=PCURVE('',#6,#73); +#75=SEAM_CURVE('',#62,(#68,#74),.CURVE_3D.); +#76=EDGE_CURVE('',#18,#20,#75,.T.); +#77=ORIENTED_EDGE('',*,*,#39,.T.); +#78=ORIENTED_EDGE('',*,*,#76,.T.); +#79=ORIENTED_EDGE('',*,*,#58,.F.); +#80=ORIENTED_EDGE('',*,*,#76,.F.); +#81=EDGE_LOOP('',(#77,#78,#79,#80)); +#82=FACE_OUTER_BOUND('',#81,.T.); +#83=ADVANCED_FACE('lateral',(#82),#6,.T.); +#84=ORIENTED_EDGE('',*,*,#39,.F.); +#85=EDGE_LOOP('',(#84)); +#86=FACE_OUTER_BOUND('',#85,.T.); +#87=ADVANCED_FACE('lower_cap',(#86),#11,.T.); +#88=ORIENTED_EDGE('',*,*,#58,.T.); +#89=EDGE_LOOP('',(#88)); +#90=FACE_OUTER_BOUND('',#89,.T.); +#91=ADVANCED_FACE('upper_cap',(#90),#16,.T.); +#92=CLOSED_SHELL('',(#83,#87,#91)); +#93=MANIFOLD_SOLID_BREP('frustum-0',#92); +#94=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#95=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#96=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#97=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000473695157187E-5),#94,'distance_accuracy_value','exchange error bound'); +#98=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#97)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#94,#95,#96)) REPRESENTATION_CONTEXT('','')); +#99=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#93),#98); +#100=APPLICATION_CONTEXT('automotive design'); +#101=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#100); +#102=PRODUCT_CONTEXT('',#100,'mechanical'); +#103=PRODUCT('frustum','frustum','',(#102)); +#104=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#103,.NOT_KNOWN.); +#105=PRODUCT_DEFINITION_CONTEXT('part definition',#100,'design'); +#106=PRODUCT_DEFINITION('','',#104,#105); +#107=PRODUCT_DEFINITION_SHAPE('','',#106); +#108=SHAPE_DEFINITION_REPRESENTATION(#107,#99); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/frustum.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/frustum.step.mm.report.json new file mode 100644 index 0000000..c1093ae --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/frustum.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":3,"exchange_error_bound":0.000010000047369515719,"faces":3,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/frustum.tess.json b/main/opengeometry/tests/fixtures/cases/frustum.tess.json new file mode 100644 index 0000000..6ba07ed --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/frustum.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[0,1,34,0,34,33,1,2,35,1,35,34,2,3,36,2,36,35,3,4,37,3,37,36,4,5,38,4,38,37,5,6,39,5,39,38,6,7,40,6,40,39,7,8,41,7,41,40,8,9,42,8,42,41,9,10,43,9,43,42,10,11,44,10,44,43,11,12,45,11,45,44,12,13,46,12,46,45,13,14,47,13,47,46,14,15,48,14,48,47,15,16,49,15,49,48,16,17,50,16,50,49,17,18,51,17,51,50,18,19,52,18,52,51,19,20,53,19,53,52,20,21,54,20,54,53,21,22,55,21,55,54,22,23,56,22,56,55,23,24,57,23,57,56,24,25,58,24,58,57,25,26,59,25,59,58,26,27,60,26,60,59,27,28,61,27,61,60,28,29,62,28,62,61,29,30,63,29,63,62,30,31,64,30,64,63,31,32,65,31,65,64,96,97,66,66,67,68,68,69,70,70,71,72,72,73,74,74,75,76,76,77,78,78,79,80,80,81,82,82,83,84,84,85,86,86,87,88,88,89,90,90,91,92,92,93,94,94,95,96,96,66,68,68,70,72,72,74,76,76,78,80,80,82,84,84,86,88,88,90,92,92,94,96,96,68,72,72,76,80,80,84,88,88,92,96,96,72,80,80,88,96,128,129,98,98,99,100,100,101,102,102,103,104,104,105,106,106,107,108,108,109,110,110,111,112,112,113,114,114,115,116,116,117,118,118,119,120,120,121,122,122,123,124,124,125,126,126,127,128,128,98,100,100,102,104,104,106,108,108,110,112,112,114,116,116,118,120,120,122,124,124,126,128,128,100,104,104,108,112,112,116,120,120,124,128,128,104,112,112,120,128],"normals":[0.9578262567520142,0.2873478829860687,0.0,0.9394218921661377,0.2873478829860687,0.18686263263225555,0.8849161267280579,0.2873478829860687,0.366544246673584,0.7964034676551819,0.2873478829860687,0.532139778137207,0.6772854328155518,0.2873478829860687,0.6772854328155518,0.532139778137207,0.2873478829860687,0.7964034676551819,0.366544246673584,0.2873478829860687,0.8849161267280579,0.18686263263225555,0.2873478829860687,0.9394218921661377,5.864994431387194e-17,0.2873478829860687,0.9578262567520142,-0.18686263263225555,0.2873478829860687,0.9394218921661377,-0.366544246673584,0.2873478829860687,0.8849161267280579,-0.532139778137207,0.2873478829860687,0.7964034676551819,-0.6772854328155518,0.2873478829860687,0.6772854328155518,-0.7964034676551819,0.2873478829860687,0.532139778137207,-0.8849161267280579,0.2873478829860687,0.366544246673584,-0.9394218921661377,0.2873478829860687,0.18686263263225555,-0.9578262567520142,0.2873478829860687,1.172998886277439e-16,-0.9394218921661377,0.2873478829860687,-0.18686263263225555,-0.8849161267280579,0.2873478829860687,-0.366544246673584,-0.7964034676551819,0.2873478829860687,-0.532139778137207,-0.6772854328155518,0.2873478829860687,-0.6772854328155518,-0.532139778137207,0.2873478829860687,-0.7964034676551819,-0.366544246673584,0.2873478829860687,-0.8849161267280579,-0.18686263263225555,0.2873478829860687,-0.9394218921661377,-1.7594983955906073e-16,0.2873478829860687,-0.9578262567520142,0.18686263263225555,0.2873478829860687,-0.9394218921661377,0.366544246673584,0.2873478829860687,-0.8849161267280579,0.532139778137207,0.2873478829860687,-0.7964034676551819,0.6772854328155518,0.2873478829860687,-0.6772854328155518,0.7964034676551819,0.2873478829860687,-0.532139778137207,0.8849161267280579,0.2873478829860687,-0.366544246673584,0.9394218921661377,0.2873478829860687,-0.18686263263225555,0.9578262567520142,0.2873478829860687,-2.345997772554878e-16,0.9578262567520142,0.2873478829860687,0.0,0.9394218921661377,0.2873478829860687,0.18686263263225555,0.8849161267280579,0.2873478829860687,0.366544246673584,0.7964034676551819,0.2873478829860687,0.532139778137207,0.6772854328155518,0.2873478829860687,0.6772854328155518,0.532139778137207,0.2873478829860687,0.7964034676551819,0.366544246673584,0.2873478829860687,0.8849161267280579,0.18686263263225555,0.2873478829860687,0.9394218921661377,5.864994431387194e-17,0.2873478829860687,0.9578262567520142,-0.18686263263225555,0.2873478829860687,0.9394218921661377,-0.366544246673584,0.2873478829860687,0.8849161267280579,-0.532139778137207,0.2873478829860687,0.7964034676551819,-0.6772854328155518,0.2873478829860687,0.6772854328155518,-0.7964034676551819,0.2873478829860687,0.532139778137207,-0.8849161267280579,0.2873478829860687,0.366544246673584,-0.9394218921661377,0.2873478829860687,0.18686263263225555,-0.9578262567520142,0.2873478829860687,1.172998886277439e-16,-0.9394218921661377,0.2873478829860687,-0.18686263263225555,-0.8849161267280579,0.2873478829860687,-0.366544246673584,-0.7964034676551819,0.2873478829860687,-0.532139778137207,-0.6772854328155518,0.2873478829860687,-0.6772854328155518,-0.532139778137207,0.2873478829860687,-0.7964034676551819,-0.366544246673584,0.2873478829860687,-0.8849161267280579,-0.18686263263225555,0.2873478829860687,-0.9394218921661377,-1.7594983955906073e-16,0.2873478829860687,-0.9578262567520142,0.18686263263225555,0.2873478829860687,-0.9394218921661377,0.366544246673584,0.2873478829860687,-0.8849161267280579,0.532139778137207,0.2873478829860687,-0.7964034676551819,0.6772854328155518,0.2873478829860687,-0.6772854328155518,0.7964034676551819,0.2873478829860687,-0.532139778137207,0.8849161267280579,0.2873478829860687,-0.366544246673584,0.9394218921661377,0.2873478829860687,-0.18686263263225555,0.9578262567520142,0.2873478829860687,-2.345997772554878e-16,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0],"outlineEdgeIds":[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,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],"outlinePositions":[0.4,2.0,0.0,0.3923141121612922,2.0,0.0780361288064513,0.3923141121612922,2.0,0.0780361288064513,0.36955181300451473,2.0,0.15307337294603593,0.36955181300451473,2.0,0.15307337294603593,0.3325878449210181,2.0,0.22222809320784087,0.3325878449210181,2.0,0.22222809320784087,0.28284271247461906,2.0,0.282842712474619,0.28284271247461906,2.0,0.282842712474619,0.22222809320784093,2.0,0.3325878449210181,0.22222809320784093,2.0,0.3325878449210181,0.15307337294603596,2.0,0.36955181300451473,0.15307337294603596,2.0,0.36955181300451473,0.07803612880645133,2.0,0.3923141121612922,0.07803612880645133,2.0,0.3923141121612922,2.4492935982947065e-17,2.0,0.4,2.4492935982947065e-17,2.0,0.4,-0.07803612880645128,2.0,0.3923141121612922,-0.07803612880645128,2.0,0.3923141121612922,-0.1530733729460359,2.0,0.36955181300451473,-0.1530733729460359,2.0,0.36955181300451473,-0.2222280932078408,2.0,0.33258784492101817,-0.2222280932078408,2.0,0.33258784492101817,-0.282842712474619,2.0,0.28284271247461906,-0.282842712474619,2.0,0.28284271247461906,-0.33258784492101817,2.0,0.22222809320784087,-0.33258784492101817,2.0,0.22222809320784087,-0.36955181300451473,2.0,0.15307337294603596,-0.36955181300451473,2.0,0.15307337294603596,-0.3923141121612922,2.0,0.07803612880645144,-0.3923141121612922,2.0,0.07803612880645144,-0.4,2.0,4.898587196589413e-17,-0.4,2.0,4.898587196589413e-17,-0.3923141121612922,2.0,-0.07803612880645135,-0.3923141121612922,2.0,-0.07803612880645135,-0.3695518130045148,2.0,-0.15307337294603587,-0.3695518130045148,2.0,-0.15307337294603587,-0.3325878449210182,2.0,-0.2222280932078408,-0.3325878449210182,2.0,-0.2222280932078408,-0.28284271247461906,2.0,-0.282842712474619,-0.28284271247461906,2.0,-0.282842712474619,-0.22222809320784087,2.0,-0.3325878449210181,-0.22222809320784087,2.0,-0.3325878449210181,-0.15307337294603615,2.0,-0.3695518130045146,-0.15307337294603615,2.0,-0.3695518130045146,-0.07803612880645147,2.0,-0.39231411216129214,-0.07803612880645147,2.0,-0.39231411216129214,-7.347880794884119e-17,2.0,-0.4,-7.347880794884119e-17,2.0,-0.4,0.07803612880645133,2.0,-0.3923141121612922,0.07803612880645133,2.0,-0.3923141121612922,0.153073372946036,2.0,-0.3695518130045147,0.153073372946036,2.0,-0.3695518130045147,0.22222809320784076,2.0,-0.3325878449210182,0.22222809320784076,2.0,-0.3325878449210182,0.28284271247461895,2.0,-0.28284271247461906,0.28284271247461895,2.0,-0.28284271247461906,0.3325878449210181,2.0,-0.22222809320784087,0.3325878449210181,2.0,-0.22222809320784087,0.3695518130045146,2.0,-0.15307337294603618,0.3695518130045146,2.0,-0.15307337294603618,0.39231411216129214,2.0,-0.0780361288064515,0.39231411216129214,2.0,-0.0780361288064515,0.4,2.0,0.0,1.0,0.0,0.0,0.9807852804032304,0.0,0.19509032201612825,0.9807852804032304,0.0,0.19509032201612825,0.9238795325112867,0.0,0.3826834323650898,0.9238795325112867,0.0,0.3826834323650898,0.8314696123025452,0.0,0.5555702330196022,0.8314696123025452,0.0,0.5555702330196022,0.7071067811865476,0.0,0.7071067811865475,0.7071067811865476,0.0,0.7071067811865475,0.5555702330196023,0.0,0.8314696123025452,0.5555702330196023,0.0,0.8314696123025452,0.38268343236508984,0.0,0.9238795325112867,0.38268343236508984,0.0,0.9238795325112867,0.19509032201612833,0.0,0.9807852804032304,0.19509032201612833,0.0,0.9807852804032304,6.123233995736766e-17,0.0,1.0,6.123233995736766e-17,0.0,1.0,-0.1950903220161282,0.0,0.9807852804032304,-0.1950903220161282,0.0,0.9807852804032304,-0.3826834323650897,0.0,0.9238795325112867,-0.3826834323650897,0.0,0.9238795325112867,-0.555570233019602,0.0,0.8314696123025453,-0.555570233019602,0.0,0.8314696123025453,-0.7071067811865475,0.0,0.7071067811865476,-0.7071067811865475,0.0,0.7071067811865476,-0.8314696123025453,0.0,0.5555702330196022,-0.8314696123025453,0.0,0.5555702330196022,-0.9238795325112867,0.0,0.3826834323650899,-0.9238795325112867,0.0,0.3826834323650899,-0.9807852804032304,0.0,0.1950903220161286,-0.9807852804032304,0.0,0.1950903220161286,-1.0,0.0,1.2246467991473532e-16,-1.0,0.0,1.2246467991473532e-16,-0.9807852804032304,0.0,-0.19509032201612836,-0.9807852804032304,0.0,-0.19509032201612836,-0.9238795325112868,0.0,-0.38268343236508967,-0.9238795325112868,0.0,-0.38268343236508967,-0.8314696123025455,0.0,-0.555570233019602,-0.8314696123025455,0.0,-0.555570233019602,-0.7071067811865477,0.0,-0.7071067811865475,-0.7071067811865477,0.0,-0.7071067811865475,-0.5555702330196022,0.0,-0.8314696123025452,-0.5555702330196022,0.0,-0.8314696123025452,-0.38268343236509034,0.0,-0.9238795325112865,-0.38268343236509034,0.0,-0.9238795325112865,-0.19509032201612866,0.0,-0.9807852804032303,-0.19509032201612866,0.0,-0.9807852804032303,-1.8369701987210297e-16,0.0,-1.0,-1.8369701987210297e-16,0.0,-1.0,0.1950903220161283,0.0,-0.9807852804032304,0.1950903220161283,0.0,-0.9807852804032304,0.38268343236509,0.0,-0.9238795325112866,0.38268343236509,0.0,-0.9238795325112866,0.5555702330196018,0.0,-0.8314696123025455,0.5555702330196018,0.0,-0.8314696123025455,0.7071067811865474,0.0,-0.7071067811865477,0.7071067811865474,0.0,-0.7071067811865477,0.8314696123025452,0.0,-0.5555702330196022,0.8314696123025452,0.0,-0.5555702330196022,0.9238795325112865,0.0,-0.3826834323650904,0.9238795325112865,0.0,-0.3826834323650904,0.9807852804032303,0.0,-0.19509032201612872,0.9807852804032303,0.0,-0.19509032201612872,1.0,0.0,0.0],"positions":[0.4,2.0,0.0,0.3923141121612922,2.0,0.0780361288064513,0.36955181300451473,2.0,0.15307337294603593,0.3325878449210181,2.0,0.22222809320784087,0.28284271247461906,2.0,0.282842712474619,0.22222809320784093,2.0,0.3325878449210181,0.15307337294603596,2.0,0.36955181300451473,0.07803612880645133,2.0,0.3923141121612922,2.4492935982947065e-17,2.0,0.4,-0.07803612880645128,2.0,0.3923141121612922,-0.1530733729460359,2.0,0.36955181300451473,-0.2222280932078408,2.0,0.33258784492101817,-0.282842712474619,2.0,0.28284271247461906,-0.33258784492101817,2.0,0.22222809320784087,-0.36955181300451473,2.0,0.15307337294603596,-0.3923141121612922,2.0,0.07803612880645144,-0.4,2.0,4.898587196589413e-17,-0.3923141121612922,2.0,-0.07803612880645135,-0.3695518130045148,2.0,-0.15307337294603587,-0.3325878449210182,2.0,-0.2222280932078408,-0.28284271247461906,2.0,-0.282842712474619,-0.22222809320784087,2.0,-0.3325878449210181,-0.15307337294603615,2.0,-0.3695518130045146,-0.07803612880645147,2.0,-0.39231411216129214,-7.347880794884119e-17,2.0,-0.4,0.07803612880645133,2.0,-0.3923141121612922,0.153073372946036,2.0,-0.3695518130045147,0.22222809320784076,2.0,-0.3325878449210182,0.28284271247461895,2.0,-0.28284271247461906,0.3325878449210181,2.0,-0.22222809320784087,0.3695518130045146,2.0,-0.15307337294603618,0.39231411216129214,2.0,-0.0780361288064515,0.4,2.0,0.0,1.0,0.0,0.0,0.9807852804032304,0.0,0.19509032201612825,0.9238795325112867,0.0,0.3826834323650898,0.8314696123025452,0.0,0.5555702330196022,0.7071067811865476,0.0,0.7071067811865475,0.5555702330196023,0.0,0.8314696123025452,0.38268343236508984,0.0,0.9238795325112867,0.19509032201612833,0.0,0.9807852804032304,6.123233995736766e-17,0.0,1.0,-0.1950903220161282,0.0,0.9807852804032304,-0.3826834323650897,0.0,0.9238795325112867,-0.555570233019602,0.0,0.8314696123025453,-0.7071067811865475,0.0,0.7071067811865476,-0.8314696123025453,0.0,0.5555702330196022,-0.9238795325112867,0.0,0.3826834323650899,-0.9807852804032304,0.0,0.1950903220161286,-1.0,0.0,1.2246467991473532e-16,-0.9807852804032304,0.0,-0.19509032201612836,-0.9238795325112868,0.0,-0.38268343236508967,-0.8314696123025455,0.0,-0.555570233019602,-0.7071067811865477,0.0,-0.7071067811865475,-0.5555702330196022,0.0,-0.8314696123025452,-0.38268343236509034,0.0,-0.9238795325112865,-0.19509032201612866,0.0,-0.9807852804032303,-1.8369701987210297e-16,0.0,-1.0,0.1950903220161283,0.0,-0.9807852804032304,0.38268343236509,0.0,-0.9238795325112866,0.5555702330196018,0.0,-0.8314696123025455,0.7071067811865474,0.0,-0.7071067811865477,0.8314696123025452,0.0,-0.5555702330196022,0.9238795325112865,0.0,-0.3826834323650904,0.9807852804032303,0.0,-0.19509032201612872,1.0,0.0,0.0,0.4,2.0,0.0,0.39231411216129214,2.0,-0.0780361288064515,0.3695518130045146,2.0,-0.15307337294603618,0.3325878449210181,2.0,-0.22222809320784087,0.28284271247461895,2.0,-0.28284271247461906,0.22222809320784076,2.0,-0.3325878449210182,0.153073372946036,2.0,-0.3695518130045147,0.07803612880645133,2.0,-0.3923141121612922,-7.347880794884119e-17,2.0,-0.4,-0.07803612880645147,2.0,-0.39231411216129214,-0.15307337294603615,2.0,-0.3695518130045146,-0.22222809320784087,2.0,-0.3325878449210181,-0.28284271247461906,2.0,-0.282842712474619,-0.3325878449210182,2.0,-0.2222280932078408,-0.3695518130045148,2.0,-0.15307337294603587,-0.3923141121612922,2.0,-0.07803612880645135,-0.4,2.0,4.898587196589413e-17,-0.3923141121612922,2.0,0.07803612880645144,-0.36955181300451473,2.0,0.15307337294603596,-0.33258784492101817,2.0,0.22222809320784087,-0.282842712474619,2.0,0.28284271247461906,-0.2222280932078408,2.0,0.33258784492101817,-0.1530733729460359,2.0,0.36955181300451473,-0.07803612880645128,2.0,0.3923141121612922,2.4492935982947065e-17,2.0,0.4,0.07803612880645133,2.0,0.3923141121612922,0.15307337294603596,2.0,0.36955181300451473,0.22222809320784093,2.0,0.3325878449210181,0.28284271247461906,2.0,0.282842712474619,0.3325878449210181,2.0,0.22222809320784087,0.36955181300451473,2.0,0.15307337294603593,0.3923141121612922,2.0,0.0780361288064513,1.0,0.0,0.0,0.9807852804032304,0.0,0.19509032201612825,0.9238795325112867,0.0,0.3826834323650898,0.8314696123025452,0.0,0.5555702330196022,0.7071067811865476,0.0,0.7071067811865475,0.5555702330196023,0.0,0.8314696123025452,0.38268343236508984,0.0,0.9238795325112867,0.19509032201612833,0.0,0.9807852804032304,6.123233995736766e-17,0.0,1.0,-0.1950903220161282,0.0,0.9807852804032304,-0.3826834323650897,0.0,0.9238795325112867,-0.555570233019602,0.0,0.8314696123025453,-0.7071067811865475,0.0,0.7071067811865476,-0.8314696123025453,0.0,0.5555702330196022,-0.9238795325112867,0.0,0.3826834323650899,-0.9807852804032304,0.0,0.1950903220161286,-1.0,0.0,1.2246467991473532e-16,-0.9807852804032304,0.0,-0.19509032201612836,-0.9238795325112868,0.0,-0.38268343236508967,-0.8314696123025455,0.0,-0.555570233019602,-0.7071067811865477,0.0,-0.7071067811865475,-0.5555702330196022,0.0,-0.8314696123025452,-0.38268343236509034,0.0,-0.9238795325112865,-0.19509032201612866,0.0,-0.9807852804032303,-1.8369701987210297e-16,0.0,-1.0,0.1950903220161283,0.0,-0.9807852804032304,0.38268343236509,0.0,-0.9238795325112866,0.5555702330196018,0.0,-0.8314696123025455,0.7071067811865474,0.0,-0.7071067811865477,0.8314696123025452,0.0,-0.5555702330196022,0.9238795325112865,0.0,-0.3826834323650904,0.9807852804032303,0.0,-0.19509032201612872],"revision":0,"triangleFaceIds":[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,0,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,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/linear-extrusion.brep.json b/main/opengeometry/tests/fixtures/cases/linear-extrusion.brep.json new file mode 100644 index 0000000..72779b2 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/linear-extrusion.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"linear-extrusion","revision":0,"geometry":{"surfaces":[{"kind":"Plane","frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]}},{"kind":"Plane","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]}},{"kind":"Plane","frame":{"origin":[-1.0,0.0,0.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]}},{"kind":"Plane","frame":{"origin":[1.0,0.0,0.5],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]}},{"kind":"Plane","frame":{"origin":[1.0,0.0,-0.5],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]}},{"kind":"Plane","frame":{"origin":[-1.0,0.0,-0.5],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]}}],"curves":[{"kind":"Line","origin":[-1.0,0.0,0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[-1.0,2.0,0.5],"direction":[1.0,0.0,0.0]},{"kind":"Line","origin":[-1.0,0.0,0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,0.0,0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.0,2.0,0.5],"direction":[0.0,0.0,-1.0]},{"kind":"Line","origin":[1.0,0.0,0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[1.0,0.0,-0.5],"direction":[-1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,2.0,-0.5],"direction":[-1.0,0.0,0.0]},{"kind":"Line","origin":[1.0,0.0,-0.5],"direction":[0.0,1.0,0.0]},{"kind":"Line","origin":[-1.0,0.0,-0.5],"direction":[0.0,0.0,1.0]},{"kind":"Line","origin":[-1.0,2.0,-0.5],"direction":[0.0,0.0,1.0]},{"kind":"Line","origin":[-1.0,0.0,-0.5],"direction":[0.0,1.0,0.0]}],"pcurves":[{"kind":"Line2","origin":[-1.0,-0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,-0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,0.5],"direction":[-1.0,0.0]},{"kind":"Line2","origin":[-1.0,0.5],"direction":[0.0,-1.0]},{"kind":"Line2","origin":[-1.0,-0.5],"direction":[0.0,1.0]},{"kind":"Line2","origin":[1.0,-0.5],"direction":[-1.0,0.0]},{"kind":"Line2","origin":[1.0,0.5],"direction":[0.0,-1.0]},{"kind":"Line2","origin":[-1.0,0.5],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[2.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[1.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,2.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[-1.0,0.0,0.5],"outgoing_halfedge":4,"tolerance":1e-8},{"id":1,"position":[1.0,0.0,0.5],"outgoing_halfedge":7,"tolerance":1e-8},{"id":2,"position":[1.0,0.0,-0.5],"outgoing_halfedge":6,"tolerance":1e-8},{"id":3,"position":[-1.0,0.0,-0.5],"outgoing_halfedge":5,"tolerance":1e-8},{"id":4,"position":[-1.0,2.0,0.5],"outgoing_halfedge":0,"tolerance":1e-8},{"id":5,"position":[1.0,2.0,0.5],"outgoing_halfedge":1,"tolerance":1e-8},{"id":6,"position":[1.0,2.0,-0.5],"outgoing_halfedge":2,"tolerance":1e-8},{"id":7,"position":[-1.0,2.0,-0.5],"outgoing_halfedge":3,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":2.0}},"halfedge":7,"twin_halfedge":8,"tolerance":1e-8,"chart_seam":false},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":2.0}},"halfedge":0,"twin_halfedge":10,"tolerance":1e-8,"chart_seam":false},{"id":2,"geometry":{"kind":"Curve","curve":2,"range":{"lo":0.0,"hi":2.0}},"halfedge":11,"twin_halfedge":21,"tolerance":1e-8,"chart_seam":false},{"id":3,"geometry":{"kind":"Curve","curve":3,"range":{"lo":0.0,"hi":1.0}},"halfedge":6,"twin_halfedge":12,"tolerance":1e-8,"chart_seam":false},{"id":4,"geometry":{"kind":"Curve","curve":4,"range":{"lo":0.0,"hi":1.0}},"halfedge":1,"twin_halfedge":14,"tolerance":1e-8,"chart_seam":false},{"id":5,"geometry":{"kind":"Curve","curve":5,"range":{"lo":0.0,"hi":2.0}},"halfedge":9,"twin_halfedge":15,"tolerance":1e-8,"chart_seam":false},{"id":6,"geometry":{"kind":"Curve","curve":6,"range":{"lo":0.0,"hi":2.0}},"halfedge":5,"twin_halfedge":16,"tolerance":1e-8,"chart_seam":false},{"id":7,"geometry":{"kind":"Curve","curve":7,"range":{"lo":0.0,"hi":2.0}},"halfedge":2,"twin_halfedge":18,"tolerance":1e-8,"chart_seam":false},{"id":8,"geometry":{"kind":"Curve","curve":8,"range":{"lo":0.0,"hi":2.0}},"halfedge":13,"twin_halfedge":19,"tolerance":1e-8,"chart_seam":false},{"id":9,"geometry":{"kind":"Curve","curve":9,"range":{"lo":0.0,"hi":1.0}},"halfedge":4,"twin_halfedge":20,"tolerance":1e-8,"chart_seam":false},{"id":10,"geometry":{"kind":"Curve","curve":10,"range":{"lo":0.0,"hi":1.0}},"halfedge":3,"twin_halfedge":22,"tolerance":1e-8,"chart_seam":false},{"id":11,"geometry":{"kind":"Curve","curve":11,"range":{"lo":0.0,"hi":2.0}},"halfedge":17,"twin_halfedge":23,"tolerance":1e-8,"chart_seam":false}],"halfedges":[{"id":0,"from":4,"to":5,"twin":10,"next":1,"prev":3,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":5,"to":6,"twin":14,"next":2,"prev":0,"edge":4,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":6,"to":7,"twin":18,"next":3,"prev":1,"edge":7,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":7,"to":4,"twin":22,"next":0,"prev":2,"edge":10,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":0,"to":3,"twin":20,"next":5,"prev":7,"edge":9,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":3,"to":2,"twin":16,"next":6,"prev":4,"edge":6,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":5,"periodic_lift":[0,0]}},{"id":6,"from":2,"to":1,"twin":12,"next":7,"prev":5,"edge":3,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":6,"periodic_lift":[0,0]}},{"id":7,"from":1,"to":0,"twin":8,"next":4,"prev":6,"edge":0,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":7,"periodic_lift":[0,0]}},{"id":8,"from":0,"to":1,"twin":7,"next":9,"prev":11,"edge":0,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":8,"periodic_lift":[0,0]}},{"id":9,"from":1,"to":5,"twin":15,"next":10,"prev":8,"edge":5,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":9,"periodic_lift":[0,0]}},{"id":10,"from":5,"to":4,"twin":0,"next":11,"prev":9,"edge":1,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":10,"periodic_lift":[0,0]}},{"id":11,"from":4,"to":0,"twin":21,"next":8,"prev":10,"edge":2,"face":2,"loop_ref":2,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":11,"periodic_lift":[0,0]}},{"id":12,"from":1,"to":2,"twin":6,"next":13,"prev":15,"edge":3,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":12,"periodic_lift":[0,0]}},{"id":13,"from":2,"to":6,"twin":19,"next":14,"prev":12,"edge":8,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":13,"periodic_lift":[0,0]}},{"id":14,"from":6,"to":5,"twin":1,"next":15,"prev":13,"edge":4,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":14,"periodic_lift":[0,0]}},{"id":15,"from":5,"to":1,"twin":9,"next":12,"prev":14,"edge":5,"face":3,"loop_ref":3,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":15,"periodic_lift":[0,0]}},{"id":16,"from":2,"to":3,"twin":5,"next":17,"prev":19,"edge":6,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":16,"periodic_lift":[0,0]}},{"id":17,"from":3,"to":7,"twin":23,"next":18,"prev":16,"edge":11,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":17,"periodic_lift":[0,0]}},{"id":18,"from":7,"to":6,"twin":2,"next":19,"prev":17,"edge":7,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":18,"periodic_lift":[0,0]}},{"id":19,"from":6,"to":2,"twin":13,"next":16,"prev":18,"edge":8,"face":4,"loop_ref":4,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":19,"periodic_lift":[0,0]}},{"id":20,"from":3,"to":0,"twin":4,"next":21,"prev":23,"edge":9,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":20,"periodic_lift":[0,0]}},{"id":21,"from":0,"to":4,"twin":11,"next":22,"prev":20,"edge":2,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":21,"periodic_lift":[0,0]}},{"id":22,"from":4,"to":7,"twin":3,"next":23,"prev":21,"edge":10,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":22,"periodic_lift":[0,0]}},{"id":23,"from":7,"to":3,"twin":17,"next":20,"prev":22,"edge":11,"face":5,"loop_ref":5,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":23,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false},{"id":2,"start_halfedge":8,"face_ref":2,"is_hole":false},{"id":3,"start_halfedge":12,"face_ref":3,"is_hole":false},{"id":4,"start_halfedge":16,"face_ref":4,"is_hole":false},{"id":5,"start_halfedge":20,"face_ref":5,"is_hole":false}],"faces":[{"id":0,"key":"top","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.00000001,"hi":1.00000001},{"lo":-0.50000001,"hi":0.50000001}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"linear-extrusion","body":"linear-extrusion","key":"top","face":0}],"role":"Authored","reversed":false}},{"id":1,"key":"bottom","surface":1,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1.00000001,"hi":1.00000001},{"lo":-0.50000001,"hi":0.50000001}],"outer":1,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"linear-extrusion","body":"linear-extrusion","key":"bottom","face":1}],"role":"Authored","reversed":false}},{"id":2,"key":"side-0-0","surface":2,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":2,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"linear-extrusion","body":"linear-extrusion","key":"side-0-0","face":2}],"role":"Authored","reversed":false}},{"id":3,"key":"side-0-1","surface":3,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":3,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"linear-extrusion","body":"linear-extrusion","key":"side-0-1","face":3}],"role":"Authored","reversed":false}},{"id":4,"key":"side-0-2","surface":4,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":2.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":4,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"linear-extrusion","body":"linear-extrusion","key":"side-0-2","face":4}],"role":"Authored","reversed":false}},{"id":5,"key":"side-0-3","surface":5,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":-1e-8,"hi":1.00000001},{"lo":-1e-8,"hi":2.00000001}],"outer":5,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"linear-extrusion","body":"linear-extrusion","key":"side-0-3","face":5}],"role":"Authored","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0,1,2,3,4,5],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.m b/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.m new file mode 100644 index 0000000..d0b8036 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.m @@ -0,0 +1,331 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('linear-extrusion','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(-1.00000000000000000E0,0.00000000000000000E0,5.00000000000000000E-1)); +#13=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,5.00000000000000000E-1)); +#18=DIRECTION('',(1.00000000000000000E0,-0.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#23=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#24=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(-1.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#28=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(-1.00000000000000000E0,0.00000000000000000E0,5.00000000000000000E-1)); +#33=VERTEX_POINT('',#32); +#34=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,5.00000000000000000E-1)); +#35=VERTEX_POINT('',#34); +#36=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#37=VERTEX_POINT('',#36); +#38=CARTESIAN_POINT('',(-1.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#39=VERTEX_POINT('',#38); +#40=CARTESIAN_POINT('',(-1.00000000000000000E0,2.00000000000000000E0,5.00000000000000000E-1)); +#41=VERTEX_POINT('',#40); +#42=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,5.00000000000000000E-1)); +#43=VERTEX_POINT('',#42); +#44=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,-5.00000000000000000E-1)); +#45=VERTEX_POINT('',#44); +#46=CARTESIAN_POINT('',(-1.00000000000000000E0,2.00000000000000000E0,-5.00000000000000000E-1)); +#47=VERTEX_POINT('',#46); +#48=CARTESIAN_POINT('',(-1.00000000000000000E0,0.00000000000000000E0,5.00000000000000000E-1)); +#49=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#50=VECTOR('',#49,1.00000000000000000E0); +#51=LINE('',#48,#50); +#52=CARTESIAN_POINT('',(-1.00000000000000000E0,5.00000000000000000E-1)); +#53=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#54=VECTOR('',#53,1.00000000000000000E0); +#55=LINE('',#52,#54); +#56=DEFINITIONAL_REPRESENTATION('',(#55),#1); +#57=PCURVE('',#11,#56); +#58=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#59=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#60=VECTOR('',#59,1.00000000000000000E0); +#61=LINE('',#58,#60); +#62=DEFINITIONAL_REPRESENTATION('',(#61),#1); +#63=PCURVE('',#16,#62); +#64=SURFACE_CURVE('',#51,(#57,#63),.CURVE_3D.); +#65=EDGE_CURVE('',#33,#35,#64,.T.); +#66=CARTESIAN_POINT('',(-1.00000000000000000E0,2.00000000000000000E0,5.00000000000000000E-1)); +#67=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#68=VECTOR('',#67,1.00000000000000000E0); +#69=LINE('',#66,#68); +#70=CARTESIAN_POINT('',(-1.00000000000000000E0,-5.00000000000000000E-1)); +#71=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#72=VECTOR('',#71,1.00000000000000000E0); +#73=LINE('',#70,#72); +#74=DEFINITIONAL_REPRESENTATION('',(#73),#1); +#75=PCURVE('',#6,#74); +#76=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#77=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#78=VECTOR('',#77,1.00000000000000000E0); +#79=LINE('',#76,#78); +#80=DEFINITIONAL_REPRESENTATION('',(#79),#1); +#81=PCURVE('',#16,#80); +#82=SURFACE_CURVE('',#69,(#75,#81),.CURVE_3D.); +#83=EDGE_CURVE('',#41,#43,#82,.T.); +#84=CARTESIAN_POINT('',(-1.00000000000000000E0,0.00000000000000000E0,5.00000000000000000E-1)); +#85=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#86=VECTOR('',#85,1.00000000000000000E0); +#87=LINE('',#84,#86); +#88=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#90=VECTOR('',#89,1.00000000000000000E0); +#91=LINE('',#88,#90); +#92=DEFINITIONAL_REPRESENTATION('',(#91),#1); +#93=PCURVE('',#16,#92); +#94=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#95=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#96=VECTOR('',#95,1.00000000000000000E0); +#97=LINE('',#94,#96); +#98=DEFINITIONAL_REPRESENTATION('',(#97),#1); +#99=PCURVE('',#31,#98); +#100=SURFACE_CURVE('',#87,(#93,#99),.CURVE_3D.); +#101=EDGE_CURVE('',#33,#41,#100,.T.); +#102=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,5.00000000000000000E-1)); +#103=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#104=VECTOR('',#103,1.00000000000000000E0); +#105=LINE('',#102,#104); +#106=CARTESIAN_POINT('',(1.00000000000000000E0,5.00000000000000000E-1)); +#107=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E0); +#109=LINE('',#106,#108); +#110=DEFINITIONAL_REPRESENTATION('',(#109),#1); +#111=PCURVE('',#11,#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#113=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#114=VECTOR('',#113,1.00000000000000000E0); +#115=LINE('',#112,#114); +#116=DEFINITIONAL_REPRESENTATION('',(#115),#1); +#117=PCURVE('',#21,#116); +#118=SURFACE_CURVE('',#105,(#111,#117),.CURVE_3D.); +#119=EDGE_CURVE('',#35,#37,#118,.T.); +#120=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,5.00000000000000000E-1)); +#121=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#122=VECTOR('',#121,1.00000000000000000E0); +#123=LINE('',#120,#122); +#124=CARTESIAN_POINT('',(1.00000000000000000E0,-5.00000000000000000E-1)); +#125=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E0); +#127=LINE('',#124,#126); +#128=DEFINITIONAL_REPRESENTATION('',(#127),#1); +#129=PCURVE('',#6,#128); +#130=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#131=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#132=VECTOR('',#131,1.00000000000000000E0); +#133=LINE('',#130,#132); +#134=DEFINITIONAL_REPRESENTATION('',(#133),#1); +#135=PCURVE('',#21,#134); +#136=SURFACE_CURVE('',#123,(#129,#135),.CURVE_3D.); +#137=EDGE_CURVE('',#43,#45,#136,.T.); +#138=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,5.00000000000000000E-1)); +#139=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#140=VECTOR('',#139,1.00000000000000000E0); +#141=LINE('',#138,#140); +#142=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#143=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#144=VECTOR('',#143,1.00000000000000000E0); +#145=LINE('',#142,#144); +#146=DEFINITIONAL_REPRESENTATION('',(#145),#1); +#147=PCURVE('',#16,#146); +#148=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#149=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#150=VECTOR('',#149,1.00000000000000000E0); +#151=LINE('',#148,#150); +#152=DEFINITIONAL_REPRESENTATION('',(#151),#1); +#153=PCURVE('',#21,#152); +#154=SURFACE_CURVE('',#141,(#147,#153),.CURVE_3D.); +#155=EDGE_CURVE('',#35,#43,#154,.T.); +#156=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#157=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#158=VECTOR('',#157,1.00000000000000000E0); +#159=LINE('',#156,#158); +#160=CARTESIAN_POINT('',(1.00000000000000000E0,-5.00000000000000000E-1)); +#161=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#162=VECTOR('',#161,1.00000000000000000E0); +#163=LINE('',#160,#162); +#164=DEFINITIONAL_REPRESENTATION('',(#163),#1); +#165=PCURVE('',#11,#164); +#166=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#167=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#168=VECTOR('',#167,1.00000000000000000E0); +#169=LINE('',#166,#168); +#170=DEFINITIONAL_REPRESENTATION('',(#169),#1); +#171=PCURVE('',#26,#170); +#172=SURFACE_CURVE('',#159,(#165,#171),.CURVE_3D.); +#173=EDGE_CURVE('',#37,#39,#172,.T.); +#174=CARTESIAN_POINT('',(1.00000000000000000E0,2.00000000000000000E0,-5.00000000000000000E-1)); +#175=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#176=VECTOR('',#175,1.00000000000000000E0); +#177=LINE('',#174,#176); +#178=CARTESIAN_POINT('',(1.00000000000000000E0,5.00000000000000000E-1)); +#179=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#180=VECTOR('',#179,1.00000000000000000E0); +#181=LINE('',#178,#180); +#182=DEFINITIONAL_REPRESENTATION('',(#181),#1); +#183=PCURVE('',#6,#182); +#184=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#185=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#186=VECTOR('',#185,1.00000000000000000E0); +#187=LINE('',#184,#186); +#188=DEFINITIONAL_REPRESENTATION('',(#187),#1); +#189=PCURVE('',#26,#188); +#190=SURFACE_CURVE('',#177,(#183,#189),.CURVE_3D.); +#191=EDGE_CURVE('',#45,#47,#190,.T.); +#192=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#193=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#194=VECTOR('',#193,1.00000000000000000E0); +#195=LINE('',#192,#194); +#196=CARTESIAN_POINT('',(1.00000000000000000E0,0.00000000000000000E0)); +#197=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#198=VECTOR('',#197,1.00000000000000000E0); +#199=LINE('',#196,#198); +#200=DEFINITIONAL_REPRESENTATION('',(#199),#1); +#201=PCURVE('',#21,#200); +#202=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#203=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#204=VECTOR('',#203,1.00000000000000000E0); +#205=LINE('',#202,#204); +#206=DEFINITIONAL_REPRESENTATION('',(#205),#1); +#207=PCURVE('',#26,#206); +#208=SURFACE_CURVE('',#195,(#201,#207),.CURVE_3D.); +#209=EDGE_CURVE('',#37,#45,#208,.T.); +#210=CARTESIAN_POINT('',(-1.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#211=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#212=VECTOR('',#211,1.00000000000000000E0); +#213=LINE('',#210,#212); +#214=CARTESIAN_POINT('',(-1.00000000000000000E0,-5.00000000000000000E-1)); +#215=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#216=VECTOR('',#215,1.00000000000000000E0); +#217=LINE('',#214,#216); +#218=DEFINITIONAL_REPRESENTATION('',(#217),#1); +#219=PCURVE('',#11,#218); +#220=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#221=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#222=VECTOR('',#221,1.00000000000000000E0); +#223=LINE('',#220,#222); +#224=DEFINITIONAL_REPRESENTATION('',(#223),#1); +#225=PCURVE('',#31,#224); +#226=SURFACE_CURVE('',#213,(#219,#225),.CURVE_3D.); +#227=EDGE_CURVE('',#39,#33,#226,.T.); +#228=CARTESIAN_POINT('',(-1.00000000000000000E0,2.00000000000000000E0,-5.00000000000000000E-1)); +#229=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#230=VECTOR('',#229,1.00000000000000000E0); +#231=LINE('',#228,#230); +#232=CARTESIAN_POINT('',(-1.00000000000000000E0,5.00000000000000000E-1)); +#233=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#234=VECTOR('',#233,1.00000000000000000E0); +#235=LINE('',#232,#234); +#236=DEFINITIONAL_REPRESENTATION('',(#235),#1); +#237=PCURVE('',#6,#236); +#238=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0)); +#239=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#240=VECTOR('',#239,1.00000000000000000E0); +#241=LINE('',#238,#240); +#242=DEFINITIONAL_REPRESENTATION('',(#241),#1); +#243=PCURVE('',#31,#242); +#244=SURFACE_CURVE('',#231,(#237,#243),.CURVE_3D.); +#245=EDGE_CURVE('',#47,#41,#244,.T.); +#246=CARTESIAN_POINT('',(-1.00000000000000000E0,0.00000000000000000E0,-5.00000000000000000E-1)); +#247=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#248=VECTOR('',#247,1.00000000000000000E0); +#249=LINE('',#246,#248); +#250=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0)); +#251=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#252=VECTOR('',#251,1.00000000000000000E0); +#253=LINE('',#250,#252); +#254=DEFINITIONAL_REPRESENTATION('',(#253),#1); +#255=PCURVE('',#26,#254); +#256=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#257=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#258=VECTOR('',#257,1.00000000000000000E0); +#259=LINE('',#256,#258); +#260=DEFINITIONAL_REPRESENTATION('',(#259),#1); +#261=PCURVE('',#31,#260); +#262=SURFACE_CURVE('',#249,(#255,#261),.CURVE_3D.); +#263=EDGE_CURVE('',#39,#47,#262,.T.); +#264=ORIENTED_EDGE('',*,*,#83,.T.); +#265=ORIENTED_EDGE('',*,*,#137,.T.); +#266=ORIENTED_EDGE('',*,*,#191,.T.); +#267=ORIENTED_EDGE('',*,*,#245,.T.); +#268=EDGE_LOOP('',(#264,#265,#266,#267)); +#269=FACE_OUTER_BOUND('',#268,.T.); +#270=ADVANCED_FACE('top',(#269),#6,.T.); +#271=ORIENTED_EDGE('',*,*,#227,.F.); +#272=ORIENTED_EDGE('',*,*,#173,.F.); +#273=ORIENTED_EDGE('',*,*,#119,.F.); +#274=ORIENTED_EDGE('',*,*,#65,.F.); +#275=EDGE_LOOP('',(#271,#272,#273,#274)); +#276=FACE_OUTER_BOUND('',#275,.T.); +#277=ADVANCED_FACE('bottom',(#276),#11,.T.); +#278=ORIENTED_EDGE('',*,*,#65,.T.); +#279=ORIENTED_EDGE('',*,*,#155,.T.); +#280=ORIENTED_EDGE('',*,*,#83,.F.); +#281=ORIENTED_EDGE('',*,*,#101,.F.); +#282=EDGE_LOOP('',(#278,#279,#280,#281)); +#283=FACE_OUTER_BOUND('',#282,.T.); +#284=ADVANCED_FACE('side-0-0',(#283),#16,.T.); +#285=ORIENTED_EDGE('',*,*,#119,.T.); +#286=ORIENTED_EDGE('',*,*,#209,.T.); +#287=ORIENTED_EDGE('',*,*,#137,.F.); +#288=ORIENTED_EDGE('',*,*,#155,.F.); +#289=EDGE_LOOP('',(#285,#286,#287,#288)); +#290=FACE_OUTER_BOUND('',#289,.T.); +#291=ADVANCED_FACE('side-0-1',(#290),#21,.T.); +#292=ORIENTED_EDGE('',*,*,#173,.T.); +#293=ORIENTED_EDGE('',*,*,#263,.T.); +#294=ORIENTED_EDGE('',*,*,#191,.F.); +#295=ORIENTED_EDGE('',*,*,#209,.F.); +#296=EDGE_LOOP('',(#292,#293,#294,#295)); +#297=FACE_OUTER_BOUND('',#296,.T.); +#298=ADVANCED_FACE('side-0-2',(#297),#26,.T.); +#299=ORIENTED_EDGE('',*,*,#227,.T.); +#300=ORIENTED_EDGE('',*,*,#101,.T.); +#301=ORIENTED_EDGE('',*,*,#245,.F.); +#302=ORIENTED_EDGE('',*,*,#263,.F.); +#303=EDGE_LOOP('',(#299,#300,#301,#302)); +#304=FACE_OUTER_BOUND('',#303,.T.); +#305=ADVANCED_FACE('side-0-3',(#304),#31,.T.); +#306=CLOSED_SHELL('',(#270,#277,#284,#291,#298,#305)); +#307=MANIFOLD_SOLID_BREP('linear-extrusion-0',#306); +#308=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#309=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#310=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#311=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217095729E-8),#308,'distance_accuracy_value','exchange error bound'); +#312=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#311)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#308,#309,#310)) REPRESENTATION_CONTEXT('','')); +#313=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#307),#312); +#314=APPLICATION_CONTEXT('automotive design'); +#315=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#314); +#316=PRODUCT_CONTEXT('',#314,'mechanical'); +#317=PRODUCT('linear-extrusion','linear-extrusion','',(#316)); +#318=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#317,.NOT_KNOWN.); +#319=PRODUCT_DEFINITION_CONTEXT('part definition',#314,'design'); +#320=PRODUCT_DEFINITION('','',#318,#319); +#321=PRODUCT_DEFINITION_SHAPE('','',#320); +#322=SHAPE_DEFINITION_REPRESENTATION(#321,#313); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.m.report.json b/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.m.report.json new file mode 100644 index 0000000..566198c --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":12,"exchange_error_bound":1.0000028421709573e-8,"faces":6,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.mm b/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.mm new file mode 100644 index 0000000..c101444 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.mm @@ -0,0 +1,331 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('linear-extrusion','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=PLANE('',#5); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(-0.00000000000000000E0,-1.00000000000000000E0,-0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=PLANE('',#10); +#12=CARTESIAN_POINT('',(-1.00000000000000000E3,0.00000000000000000E0,5.00000000000000000E2)); +#13=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#14=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#15=AXIS2_PLACEMENT_3D('',#12,#13,#14); +#16=PLANE('',#15); +#17=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,5.00000000000000000E2)); +#18=DIRECTION('',(1.00000000000000000E0,-0.00000000000000000E0,0.00000000000000000E0)); +#19=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#20=AXIS2_PLACEMENT_3D('',#17,#18,#19); +#21=PLANE('',#20); +#22=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#23=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#24=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#25=AXIS2_PLACEMENT_3D('',#22,#23,#24); +#26=PLANE('',#25); +#27=CARTESIAN_POINT('',(-1.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#28=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#30=AXIS2_PLACEMENT_3D('',#27,#28,#29); +#31=PLANE('',#30); +#32=CARTESIAN_POINT('',(-1.00000000000000000E3,0.00000000000000000E0,5.00000000000000000E2)); +#33=VERTEX_POINT('',#32); +#34=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,5.00000000000000000E2)); +#35=VERTEX_POINT('',#34); +#36=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#37=VERTEX_POINT('',#36); +#38=CARTESIAN_POINT('',(-1.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#39=VERTEX_POINT('',#38); +#40=CARTESIAN_POINT('',(-1.00000000000000000E3,2.00000000000000000E3,5.00000000000000000E2)); +#41=VERTEX_POINT('',#40); +#42=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,5.00000000000000000E2)); +#43=VERTEX_POINT('',#42); +#44=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,-5.00000000000000000E2)); +#45=VERTEX_POINT('',#44); +#46=CARTESIAN_POINT('',(-1.00000000000000000E3,2.00000000000000000E3,-5.00000000000000000E2)); +#47=VERTEX_POINT('',#46); +#48=CARTESIAN_POINT('',(-1.00000000000000000E3,0.00000000000000000E0,5.00000000000000000E2)); +#49=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#50=VECTOR('',#49,1.00000000000000000E3); +#51=LINE('',#48,#50); +#52=CARTESIAN_POINT('',(-1.00000000000000000E3,5.00000000000000000E2)); +#53=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#54=VECTOR('',#53,1.00000000000000000E3); +#55=LINE('',#52,#54); +#56=DEFINITIONAL_REPRESENTATION('',(#55),#1); +#57=PCURVE('',#11,#56); +#58=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#59=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#60=VECTOR('',#59,1.00000000000000000E3); +#61=LINE('',#58,#60); +#62=DEFINITIONAL_REPRESENTATION('',(#61),#1); +#63=PCURVE('',#16,#62); +#64=SURFACE_CURVE('',#51,(#57,#63),.CURVE_3D.); +#65=EDGE_CURVE('',#33,#35,#64,.T.); +#66=CARTESIAN_POINT('',(-1.00000000000000000E3,2.00000000000000000E3,5.00000000000000000E2)); +#67=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#68=VECTOR('',#67,1.00000000000000000E3); +#69=LINE('',#66,#68); +#70=CARTESIAN_POINT('',(-1.00000000000000000E3,-5.00000000000000000E2)); +#71=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#72=VECTOR('',#71,1.00000000000000000E3); +#73=LINE('',#70,#72); +#74=DEFINITIONAL_REPRESENTATION('',(#73),#1); +#75=PCURVE('',#6,#74); +#76=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#77=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#78=VECTOR('',#77,1.00000000000000000E3); +#79=LINE('',#76,#78); +#80=DEFINITIONAL_REPRESENTATION('',(#79),#1); +#81=PCURVE('',#16,#80); +#82=SURFACE_CURVE('',#69,(#75,#81),.CURVE_3D.); +#83=EDGE_CURVE('',#41,#43,#82,.T.); +#84=CARTESIAN_POINT('',(-1.00000000000000000E3,0.00000000000000000E0,5.00000000000000000E2)); +#85=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#86=VECTOR('',#85,1.00000000000000000E3); +#87=LINE('',#84,#86); +#88=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#89=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#90=VECTOR('',#89,1.00000000000000000E3); +#91=LINE('',#88,#90); +#92=DEFINITIONAL_REPRESENTATION('',(#91),#1); +#93=PCURVE('',#16,#92); +#94=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#95=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#96=VECTOR('',#95,1.00000000000000000E3); +#97=LINE('',#94,#96); +#98=DEFINITIONAL_REPRESENTATION('',(#97),#1); +#99=PCURVE('',#31,#98); +#100=SURFACE_CURVE('',#87,(#93,#99),.CURVE_3D.); +#101=EDGE_CURVE('',#33,#41,#100,.T.); +#102=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,5.00000000000000000E2)); +#103=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#104=VECTOR('',#103,1.00000000000000000E3); +#105=LINE('',#102,#104); +#106=CARTESIAN_POINT('',(1.00000000000000000E3,5.00000000000000000E2)); +#107=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#108=VECTOR('',#107,1.00000000000000000E3); +#109=LINE('',#106,#108); +#110=DEFINITIONAL_REPRESENTATION('',(#109),#1); +#111=PCURVE('',#11,#110); +#112=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#113=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#114=VECTOR('',#113,1.00000000000000000E3); +#115=LINE('',#112,#114); +#116=DEFINITIONAL_REPRESENTATION('',(#115),#1); +#117=PCURVE('',#21,#116); +#118=SURFACE_CURVE('',#105,(#111,#117),.CURVE_3D.); +#119=EDGE_CURVE('',#35,#37,#118,.T.); +#120=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,5.00000000000000000E2)); +#121=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,-1.00000000000000000E0)); +#122=VECTOR('',#121,1.00000000000000000E3); +#123=LINE('',#120,#122); +#124=CARTESIAN_POINT('',(1.00000000000000000E3,-5.00000000000000000E2)); +#125=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#126=VECTOR('',#125,1.00000000000000000E3); +#127=LINE('',#124,#126); +#128=DEFINITIONAL_REPRESENTATION('',(#127),#1); +#129=PCURVE('',#6,#128); +#130=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#131=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#132=VECTOR('',#131,1.00000000000000000E3); +#133=LINE('',#130,#132); +#134=DEFINITIONAL_REPRESENTATION('',(#133),#1); +#135=PCURVE('',#21,#134); +#136=SURFACE_CURVE('',#123,(#129,#135),.CURVE_3D.); +#137=EDGE_CURVE('',#43,#45,#136,.T.); +#138=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,5.00000000000000000E2)); +#139=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#140=VECTOR('',#139,1.00000000000000000E3); +#141=LINE('',#138,#140); +#142=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#143=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#144=VECTOR('',#143,1.00000000000000000E3); +#145=LINE('',#142,#144); +#146=DEFINITIONAL_REPRESENTATION('',(#145),#1); +#147=PCURVE('',#16,#146); +#148=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#149=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#150=VECTOR('',#149,1.00000000000000000E3); +#151=LINE('',#148,#150); +#152=DEFINITIONAL_REPRESENTATION('',(#151),#1); +#153=PCURVE('',#21,#152); +#154=SURFACE_CURVE('',#141,(#147,#153),.CURVE_3D.); +#155=EDGE_CURVE('',#35,#43,#154,.T.); +#156=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#157=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#158=VECTOR('',#157,1.00000000000000000E3); +#159=LINE('',#156,#158); +#160=CARTESIAN_POINT('',(1.00000000000000000E3,-5.00000000000000000E2)); +#161=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#162=VECTOR('',#161,1.00000000000000000E3); +#163=LINE('',#160,#162); +#164=DEFINITIONAL_REPRESENTATION('',(#163),#1); +#165=PCURVE('',#11,#164); +#166=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#167=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#168=VECTOR('',#167,1.00000000000000000E3); +#169=LINE('',#166,#168); +#170=DEFINITIONAL_REPRESENTATION('',(#169),#1); +#171=PCURVE('',#26,#170); +#172=SURFACE_CURVE('',#159,(#165,#171),.CURVE_3D.); +#173=EDGE_CURVE('',#37,#39,#172,.T.); +#174=CARTESIAN_POINT('',(1.00000000000000000E3,2.00000000000000000E3,-5.00000000000000000E2)); +#175=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#176=VECTOR('',#175,1.00000000000000000E3); +#177=LINE('',#174,#176); +#178=CARTESIAN_POINT('',(1.00000000000000000E3,5.00000000000000000E2)); +#179=DIRECTION('',(-1.00000000000000000E0,0.00000000000000000E0)); +#180=VECTOR('',#179,1.00000000000000000E3); +#181=LINE('',#178,#180); +#182=DEFINITIONAL_REPRESENTATION('',(#181),#1); +#183=PCURVE('',#6,#182); +#184=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#185=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#186=VECTOR('',#185,1.00000000000000000E3); +#187=LINE('',#184,#186); +#188=DEFINITIONAL_REPRESENTATION('',(#187),#1); +#189=PCURVE('',#26,#188); +#190=SURFACE_CURVE('',#177,(#183,#189),.CURVE_3D.); +#191=EDGE_CURVE('',#45,#47,#190,.T.); +#192=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#193=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#194=VECTOR('',#193,1.00000000000000000E3); +#195=LINE('',#192,#194); +#196=CARTESIAN_POINT('',(1.00000000000000000E3,0.00000000000000000E0)); +#197=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#198=VECTOR('',#197,1.00000000000000000E3); +#199=LINE('',#196,#198); +#200=DEFINITIONAL_REPRESENTATION('',(#199),#1); +#201=PCURVE('',#21,#200); +#202=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#203=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#204=VECTOR('',#203,1.00000000000000000E3); +#205=LINE('',#202,#204); +#206=DEFINITIONAL_REPRESENTATION('',(#205),#1); +#207=PCURVE('',#26,#206); +#208=SURFACE_CURVE('',#195,(#201,#207),.CURVE_3D.); +#209=EDGE_CURVE('',#37,#45,#208,.T.); +#210=CARTESIAN_POINT('',(-1.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#211=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#212=VECTOR('',#211,1.00000000000000000E3); +#213=LINE('',#210,#212); +#214=CARTESIAN_POINT('',(-1.00000000000000000E3,-5.00000000000000000E2)); +#215=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#216=VECTOR('',#215,1.00000000000000000E3); +#217=LINE('',#214,#216); +#218=DEFINITIONAL_REPRESENTATION('',(#217),#1); +#219=PCURVE('',#11,#218); +#220=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#221=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#222=VECTOR('',#221,1.00000000000000000E3); +#223=LINE('',#220,#222); +#224=DEFINITIONAL_REPRESENTATION('',(#223),#1); +#225=PCURVE('',#31,#224); +#226=SURFACE_CURVE('',#213,(#219,#225),.CURVE_3D.); +#227=EDGE_CURVE('',#39,#33,#226,.T.); +#228=CARTESIAN_POINT('',(-1.00000000000000000E3,2.00000000000000000E3,-5.00000000000000000E2)); +#229=DIRECTION('',(0.00000000000000000E0,0.00000000000000000E0,1.00000000000000000E0)); +#230=VECTOR('',#229,1.00000000000000000E3); +#231=LINE('',#228,#230); +#232=CARTESIAN_POINT('',(-1.00000000000000000E3,5.00000000000000000E2)); +#233=DIRECTION('',(0.00000000000000000E0,-1.00000000000000000E0)); +#234=VECTOR('',#233,1.00000000000000000E3); +#235=LINE('',#232,#234); +#236=DEFINITIONAL_REPRESENTATION('',(#235),#1); +#237=PCURVE('',#6,#236); +#238=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3)); +#239=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#240=VECTOR('',#239,1.00000000000000000E3); +#241=LINE('',#238,#240); +#242=DEFINITIONAL_REPRESENTATION('',(#241),#1); +#243=PCURVE('',#31,#242); +#244=SURFACE_CURVE('',#231,(#237,#243),.CURVE_3D.); +#245=EDGE_CURVE('',#47,#41,#244,.T.); +#246=CARTESIAN_POINT('',(-1.00000000000000000E3,0.00000000000000000E0,-5.00000000000000000E2)); +#247=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#248=VECTOR('',#247,1.00000000000000000E3); +#249=LINE('',#246,#248); +#250=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0)); +#251=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#252=VECTOR('',#251,1.00000000000000000E3); +#253=LINE('',#250,#252); +#254=DEFINITIONAL_REPRESENTATION('',(#253),#1); +#255=PCURVE('',#26,#254); +#256=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#257=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#258=VECTOR('',#257,1.00000000000000000E3); +#259=LINE('',#256,#258); +#260=DEFINITIONAL_REPRESENTATION('',(#259),#1); +#261=PCURVE('',#31,#260); +#262=SURFACE_CURVE('',#249,(#255,#261),.CURVE_3D.); +#263=EDGE_CURVE('',#39,#47,#262,.T.); +#264=ORIENTED_EDGE('',*,*,#83,.T.); +#265=ORIENTED_EDGE('',*,*,#137,.T.); +#266=ORIENTED_EDGE('',*,*,#191,.T.); +#267=ORIENTED_EDGE('',*,*,#245,.T.); +#268=EDGE_LOOP('',(#264,#265,#266,#267)); +#269=FACE_OUTER_BOUND('',#268,.T.); +#270=ADVANCED_FACE('top',(#269),#6,.T.); +#271=ORIENTED_EDGE('',*,*,#227,.F.); +#272=ORIENTED_EDGE('',*,*,#173,.F.); +#273=ORIENTED_EDGE('',*,*,#119,.F.); +#274=ORIENTED_EDGE('',*,*,#65,.F.); +#275=EDGE_LOOP('',(#271,#272,#273,#274)); +#276=FACE_OUTER_BOUND('',#275,.T.); +#277=ADVANCED_FACE('bottom',(#276),#11,.T.); +#278=ORIENTED_EDGE('',*,*,#65,.T.); +#279=ORIENTED_EDGE('',*,*,#155,.T.); +#280=ORIENTED_EDGE('',*,*,#83,.F.); +#281=ORIENTED_EDGE('',*,*,#101,.F.); +#282=EDGE_LOOP('',(#278,#279,#280,#281)); +#283=FACE_OUTER_BOUND('',#282,.T.); +#284=ADVANCED_FACE('side-0-0',(#283),#16,.T.); +#285=ORIENTED_EDGE('',*,*,#119,.T.); +#286=ORIENTED_EDGE('',*,*,#209,.T.); +#287=ORIENTED_EDGE('',*,*,#137,.F.); +#288=ORIENTED_EDGE('',*,*,#155,.F.); +#289=EDGE_LOOP('',(#285,#286,#287,#288)); +#290=FACE_OUTER_BOUND('',#289,.T.); +#291=ADVANCED_FACE('side-0-1',(#290),#21,.T.); +#292=ORIENTED_EDGE('',*,*,#173,.T.); +#293=ORIENTED_EDGE('',*,*,#263,.T.); +#294=ORIENTED_EDGE('',*,*,#191,.F.); +#295=ORIENTED_EDGE('',*,*,#209,.F.); +#296=EDGE_LOOP('',(#292,#293,#294,#295)); +#297=FACE_OUTER_BOUND('',#296,.T.); +#298=ADVANCED_FACE('side-0-2',(#297),#26,.T.); +#299=ORIENTED_EDGE('',*,*,#227,.T.); +#300=ORIENTED_EDGE('',*,*,#101,.T.); +#301=ORIENTED_EDGE('',*,*,#245,.F.); +#302=ORIENTED_EDGE('',*,*,#263,.F.); +#303=EDGE_LOOP('',(#299,#300,#301,#302)); +#304=FACE_OUTER_BOUND('',#303,.T.); +#305=ADVANCED_FACE('side-0-3',(#304),#31,.T.); +#306=CLOSED_SHELL('',(#270,#277,#284,#291,#298,#305)); +#307=MANIFOLD_SOLID_BREP('linear-extrusion-0',#306); +#308=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#309=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#310=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#311=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217095735E-5),#308,'distance_accuracy_value','exchange error bound'); +#312=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#311)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#308,#309,#310)) REPRESENTATION_CONTEXT('','')); +#313=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#307),#312); +#314=APPLICATION_CONTEXT('automotive design'); +#315=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#314); +#316=PRODUCT_CONTEXT('',#314,'mechanical'); +#317=PRODUCT('linear-extrusion','linear-extrusion','',(#316)); +#318=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#317,.NOT_KNOWN.); +#319=PRODUCT_DEFINITION_CONTEXT('part definition',#314,'design'); +#320=PRODUCT_DEFINITION('','',#318,#319); +#321=PRODUCT_DEFINITION_SHAPE('','',#320); +#322=SHAPE_DEFINITION_REPRESENTATION(#321,#313); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.mm.report.json new file mode 100644 index 0000000..3229f87 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/linear-extrusion.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":12,"exchange_error_bound":0.000010000028421709574,"faces":6,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/linear-extrusion.tess.json b/main/opengeometry/tests/fixtures/cases/linear-extrusion.tess.json new file mode 100644 index 0000000..a8b86a1 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/linear-extrusion.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[2,3,0,0,1,2,6,7,4,4,5,6,10,11,8,8,9,10,14,15,12,12,13,14,18,19,16,16,17,18,22,23,20,20,21,22],"normals":[0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0],"outlineEdgeIds":[0,1,2,3,4,5,6,7,8,9,10,11],"outlinePositions":[-1.0,0.0,0.5,1.0,0.0,0.5,-1.0,2.0,0.5,1.0,2.0,0.5,-1.0,0.0,0.5,-1.0,2.0,0.5,1.0,0.0,0.5,1.0,0.0,-0.5,1.0,2.0,0.5,1.0,2.0,-0.5,1.0,0.0,0.5,1.0,2.0,0.5,1.0,0.0,-0.5,-1.0,0.0,-0.5,1.0,2.0,-0.5,-1.0,2.0,-0.5,1.0,0.0,-0.5,1.0,2.0,-0.5,-1.0,0.0,-0.5,-1.0,0.0,0.5,-1.0,2.0,-0.5,-1.0,2.0,0.5,-1.0,0.0,-0.5,-1.0,2.0,-0.5],"positions":[-1.0,2.0,0.5,1.0,2.0,0.5,1.0,2.0,-0.5,-1.0,2.0,-0.5,-1.0,0.0,0.5,-1.0,0.0,-0.5,1.0,0.0,-0.5,1.0,0.0,0.5,-1.0,0.0,0.5,1.0,0.0,0.5,1.0,2.0,0.5,-1.0,2.0,0.5,1.0,0.0,0.5,1.0,0.0,-0.5,1.0,2.0,-0.5,1.0,2.0,0.5,1.0,0.0,-0.5,-1.0,0.0,-0.5,-1.0,2.0,-0.5,1.0,2.0,-0.5,-1.0,0.0,-0.5,-1.0,0.0,0.5,-1.0,2.0,0.5,-1.0,2.0,-0.5],"revision":0,"triangleFaceIds":[0,0,1,1,2,2,3,3,4,4,5,5]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/sphere-cut.brep.json b/main/opengeometry/tests/fixtures/cases/sphere-cut.brep.json new file mode 100644 index 0000000..ac79efa --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere-cut.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"sphere-cut","revision":1,"geometry":{"surfaces":[{"kind":"Sphere","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":2.0},{"kind":"Sphere","frame":{"origin":[0.7,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0}],"curves":[{"kind":"Circle","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"radius":2.0},{"kind":"Circle","frame":{"origin":[0.7,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"radius":1.0}],"pcurves":[{"kind":"Line2","origin":[0.0,-1.5707963267948966],"direction":[6.283185307179586,0.0]},{"kind":"Line2","origin":[6.283185307179586,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[6.283185307179586,1.5707963267948966],"direction":[-6.283185307179586,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,-1.5707963267948966],"direction":[6.283185307179586,0.0]},{"kind":"Line2","origin":[6.283185307179586,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[6.283185307179586,1.5707963267948966],"direction":[-6.283185307179586,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[0.0,-2.0,0.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[0.0,2.0,0.0],"outgoing_halfedge":2,"tolerance":1e-8},{"id":2,"position":[0.7,-1.0,0.0],"outgoing_halfedge":4,"tolerance":1e-8},{"id":3,"position":[0.7,1.0,0.0],"outgoing_halfedge":5,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"twin_halfedge":null,"tolerance":1e-8,"chart_seam":true},{"id":1,"geometry":{"kind":"Collapsed","vertex":1},"halfedge":2,"twin_halfedge":null,"tolerance":1e-8,"chart_seam":true},{"id":2,"geometry":{"kind":"Curve","curve":0,"range":{"lo":-1.5707963267948966,"hi":1.5707963267948966}},"halfedge":1,"twin_halfedge":3,"tolerance":1e-8,"chart_seam":true},{"id":3,"geometry":{"kind":"Collapsed","vertex":2},"halfedge":4,"twin_halfedge":null,"tolerance":1e-8,"chart_seam":true},{"id":4,"geometry":{"kind":"Collapsed","vertex":3},"halfedge":6,"twin_halfedge":null,"tolerance":1e-8,"chart_seam":true},{"id":5,"geometry":{"kind":"Curve","curve":1,"range":{"lo":-1.5707963267948966,"hi":1.5707963267948966}},"halfedge":5,"twin_halfedge":7,"tolerance":1e-8,"chart_seam":true}],"halfedges":[{"id":0,"from":0,"to":0,"twin":null,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":0,"to":1,"twin":3,"next":2,"prev":0,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":1,"to":1,"twin":null,"next":3,"prev":1,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":1,"to":0,"twin":1,"next":0,"prev":2,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":3,"periodic_lift":[0,0]}},{"id":4,"from":2,"to":2,"twin":null,"next":7,"prev":5,"edge":3,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":4,"periodic_lift":[0,0]}},{"id":5,"from":3,"to":2,"twin":7,"next":4,"prev":6,"edge":5,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":5,"periodic_lift":[0,0]}},{"id":6,"from":3,"to":3,"twin":null,"next":5,"prev":7,"edge":4,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":6,"periodic_lift":[0,0]}},{"id":7,"from":2,"to":3,"twin":5,"next":6,"prev":4,"edge":5,"face":1,"loop_ref":1,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":7,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false},{"id":1,"start_halfedge":4,"face_ref":1,"is_hole":false}],"faces":[{"id":0,"key":"0:skin","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.0,"hi":6.283185307179586},{"lo":-1.5707963267948966,"hi":1.5707963267948966}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"sphere-host","body":"sphere-host","key":"skin","face":0}],"role":"Preserved","reversed":false}},{"id":1,"key":"1:skin","surface":1,"sense":"Reverse","trim":{"chart":0,"uv_bounds":[{"lo":0.0,"hi":6.283185307179586},{"lo":-1.5707963267948966,"hi":1.5707963267948966}],"outer":1,"holes":[]},"shell_ref":1,"provenance":{"sources":[{"entity":"sphere-cutter","body":"sphere-cutter","key":"skin","face":0}],"role":"Cut","reversed":true}}],"wires":[],"shells":[{"id":0,"faces":[0],"is_closed":true},{"id":1,"faces":[1],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[1]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/sphere-cut.handlers.json b/main/opengeometry/tests/fixtures/cases/sphere-cut.handlers.json new file mode 100644 index 0000000..b71e5a7 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere-cut.handlers.json @@ -0,0 +1 @@ +["boolean_spheres"] \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/sphere-cut.step.m b/main/opengeometry/tests/fixtures/cases/sphere-cut.step.m new file mode 100644 index 0000000..62f406c --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere-cut.step.m @@ -0,0 +1,95 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('sphere-cut','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=SPHERICAL_SURFACE('',#5,2.00000000000000000E0); +#7=CARTESIAN_POINT('',(6.99999999999999956E-1,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=SPHERICAL_SURFACE('',#10,1.00000000000000000E0); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,-2.00000000000000000E0,0.00000000000000000E0)); +#13=VERTEX_POINT('',#12); +#14=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E0,0.00000000000000000E0)); +#15=VERTEX_POINT('',#14); +#16=CARTESIAN_POINT('',(6.99999999999999956E-1,-1.00000000000000000E0,0.00000000000000000E0)); +#17=VERTEX_POINT('',#16); +#18=CARTESIAN_POINT('',(6.99999999999999956E-1,1.00000000000000000E0,0.00000000000000000E0)); +#19=VERTEX_POINT('',#18); +#20=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#21=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#22=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=AXIS2_PLACEMENT_3D('',#20,#21,#22); +#24=CIRCLE('',#23,2.00000000000000000E0); +#25=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#26=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#27=VECTOR('',#26,1.00000000000000000E0); +#28=LINE('',#25,#27); +#29=DEFINITIONAL_REPRESENTATION('',(#28),#1); +#30=PCURVE('',#6,#29); +#31=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#32=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#33=VECTOR('',#32,1.00000000000000000E0); +#34=LINE('',#31,#33); +#35=DEFINITIONAL_REPRESENTATION('',(#34),#1); +#36=PCURVE('',#6,#35); +#37=SEAM_CURVE('',#24,(#30,#36),.CURVE_3D.); +#38=EDGE_CURVE('',#13,#15,#37,.T.); +#39=CARTESIAN_POINT('',(6.99999999999999956E-1,0.00000000000000000E0,0.00000000000000000E0)); +#40=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#41=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#42=AXIS2_PLACEMENT_3D('',#39,#40,#41); +#43=CIRCLE('',#42,1.00000000000000000E0); +#44=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#45=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#46=VECTOR('',#45,1.00000000000000000E0); +#47=LINE('',#44,#46); +#48=DEFINITIONAL_REPRESENTATION('',(#47),#1); +#49=PCURVE('',#11,#48); +#50=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#51=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#52=VECTOR('',#51,1.00000000000000000E0); +#53=LINE('',#50,#52); +#54=DEFINITIONAL_REPRESENTATION('',(#53),#1); +#55=PCURVE('',#11,#54); +#56=SEAM_CURVE('',#43,(#49,#55),.CURVE_3D.); +#57=EDGE_CURVE('',#17,#19,#56,.T.); +#58=ORIENTED_EDGE('',*,*,#38,.T.); +#59=ORIENTED_EDGE('',*,*,#38,.F.); +#60=EDGE_LOOP('',(#58,#59)); +#61=FACE_OUTER_BOUND('',#60,.T.); +#62=ADVANCED_FACE('0:skin',(#61),#6,.T.); +#63=ORIENTED_EDGE('',*,*,#57,.T.); +#64=ORIENTED_EDGE('',*,*,#57,.F.); +#65=EDGE_LOOP('',(#63,#64)); +#66=FACE_OUTER_BOUND('',#65,.T.); +#67=ADVANCED_FACE('1:skin',(#66),#11,.F.); +#68=CLOSED_SHELL('',(#62)); +#69=CLOSED_SHELL('',(#67)); +#70=ORIENTED_CLOSED_SHELL('',*,#69,.T.); +#71=BREP_WITH_VOIDS('sphere-cut-0',#68,(#70)); +#72=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#73=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#74=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#75=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217094306E-8),#72,'distance_accuracy_value','exchange error bound'); +#76=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#75)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#72,#73,#74)) REPRESENTATION_CONTEXT('','')); +#77=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#71),#76); +#78=APPLICATION_CONTEXT('automotive design'); +#79=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#78); +#80=PRODUCT_CONTEXT('',#78,'mechanical'); +#81=PRODUCT('sphere-cut','sphere-cut','',(#80)); +#82=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#81,.NOT_KNOWN.); +#83=PRODUCT_DEFINITION_CONTEXT('part definition',#78,'design'); +#84=PRODUCT_DEFINITION('','',#82,#83); +#85=PRODUCT_DEFINITION_SHAPE('','',#84); +#86=SHAPE_DEFINITION_REPRESENTATION(#85,#77); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/sphere-cut.step.m.report.json b/main/opengeometry/tests/fixtures/cases/sphere-cut.step.m.report.json new file mode 100644 index 0000000..4f8e2b7 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere-cut.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":1,"collapsed_chart_uses":4,"edges":2,"exchange_error_bound":1.000002842170943e-8,"faces":2,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"1","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/sphere-cut.step.mm b/main/opengeometry/tests/fixtures/cases/sphere-cut.step.mm new file mode 100644 index 0000000..485fb30 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere-cut.step.mm @@ -0,0 +1,95 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('sphere-cut','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=SPHERICAL_SURFACE('',#5,2.00000000000000000E3); +#7=CARTESIAN_POINT('',(7.00000000000000000E2,0.00000000000000000E0,0.00000000000000000E0)); +#8=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#9=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=AXIS2_PLACEMENT_3D('',#7,#8,#9); +#11=SPHERICAL_SURFACE('',#10,1.00000000000000000E3); +#12=CARTESIAN_POINT('',(0.00000000000000000E0,-2.00000000000000000E3,0.00000000000000000E0)); +#13=VERTEX_POINT('',#12); +#14=CARTESIAN_POINT('',(0.00000000000000000E0,2.00000000000000000E3,0.00000000000000000E0)); +#15=VERTEX_POINT('',#14); +#16=CARTESIAN_POINT('',(7.00000000000000000E2,-1.00000000000000000E3,0.00000000000000000E0)); +#17=VERTEX_POINT('',#16); +#18=CARTESIAN_POINT('',(7.00000000000000000E2,1.00000000000000000E3,0.00000000000000000E0)); +#19=VERTEX_POINT('',#18); +#20=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#21=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#22=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#23=AXIS2_PLACEMENT_3D('',#20,#21,#22); +#24=CIRCLE('',#23,2.00000000000000000E3); +#25=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#26=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#27=VECTOR('',#26,1.00000000000000000E0); +#28=LINE('',#25,#27); +#29=DEFINITIONAL_REPRESENTATION('',(#28),#1); +#30=PCURVE('',#6,#29); +#31=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#32=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#33=VECTOR('',#32,1.00000000000000000E0); +#34=LINE('',#31,#33); +#35=DEFINITIONAL_REPRESENTATION('',(#34),#1); +#36=PCURVE('',#6,#35); +#37=SEAM_CURVE('',#24,(#30,#36),.CURVE_3D.); +#38=EDGE_CURVE('',#13,#15,#37,.T.); +#39=CARTESIAN_POINT('',(7.00000000000000000E2,0.00000000000000000E0,0.00000000000000000E0)); +#40=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#41=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#42=AXIS2_PLACEMENT_3D('',#39,#40,#41); +#43=CIRCLE('',#42,1.00000000000000000E3); +#44=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#45=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#46=VECTOR('',#45,1.00000000000000000E0); +#47=LINE('',#44,#46); +#48=DEFINITIONAL_REPRESENTATION('',(#47),#1); +#49=PCURVE('',#11,#48); +#50=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#51=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#52=VECTOR('',#51,1.00000000000000000E0); +#53=LINE('',#50,#52); +#54=DEFINITIONAL_REPRESENTATION('',(#53),#1); +#55=PCURVE('',#11,#54); +#56=SEAM_CURVE('',#43,(#49,#55),.CURVE_3D.); +#57=EDGE_CURVE('',#17,#19,#56,.T.); +#58=ORIENTED_EDGE('',*,*,#38,.T.); +#59=ORIENTED_EDGE('',*,*,#38,.F.); +#60=EDGE_LOOP('',(#58,#59)); +#61=FACE_OUTER_BOUND('',#60,.T.); +#62=ADVANCED_FACE('0:skin',(#61),#6,.T.); +#63=ORIENTED_EDGE('',*,*,#57,.T.); +#64=ORIENTED_EDGE('',*,*,#57,.F.); +#65=EDGE_LOOP('',(#63,#64)); +#66=FACE_OUTER_BOUND('',#65,.T.); +#67=ADVANCED_FACE('1:skin',(#66),#11,.F.); +#68=CLOSED_SHELL('',(#62)); +#69=CLOSED_SHELL('',(#67)); +#70=ORIENTED_CLOSED_SHELL('',*,#69,.T.); +#71=BREP_WITH_VOIDS('sphere-cut-0',#68,(#70)); +#72=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#73=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#74=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#75=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000284217094312E-5),#72,'distance_accuracy_value','exchange error bound'); +#76=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#75)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#72,#73,#74)) REPRESENTATION_CONTEXT('','')); +#77=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#71),#76); +#78=APPLICATION_CONTEXT('automotive design'); +#79=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#78); +#80=PRODUCT_CONTEXT('',#78,'mechanical'); +#81=PRODUCT('sphere-cut','sphere-cut','',(#80)); +#82=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#81,.NOT_KNOWN.); +#83=PRODUCT_DEFINITION_CONTEXT('part definition',#78,'design'); +#84=PRODUCT_DEFINITION('','',#82,#83); +#85=PRODUCT_DEFINITION_SHAPE('','',#84); +#86=SHAPE_DEFINITION_REPRESENTATION(#85,#77); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/sphere-cut.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/sphere-cut.step.mm.report.json new file mode 100644 index 0000000..dfb6eee --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere-cut.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":1,"collapsed_chart_uses":4,"edges":2,"exchange_error_bound":0.000010000028421709431,"faces":2,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"1","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/sphere.brep.json b/main/opengeometry/tests/fixtures/cases/sphere.brep.json new file mode 100644 index 0000000..07a6ad9 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"sphere","revision":0,"geometry":{"surfaces":[{"kind":"Sphere","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":1.0}],"curves":[{"kind":"Circle","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"radius":1.0}],"pcurves":[{"kind":"Line2","origin":[0.0,-1.5707963267948966],"direction":[6.283185307179586,0.0]},{"kind":"Line2","origin":[6.283185307179586,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[6.283185307179586,1.5707963267948966],"direction":[-6.283185307179586,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[0.0,-1.0,0.0],"outgoing_halfedge":0,"tolerance":1e-8},{"id":1,"position":[0.0,1.0,0.0],"outgoing_halfedge":2,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"twin_halfedge":null,"tolerance":1e-8,"chart_seam":true},{"id":1,"geometry":{"kind":"Collapsed","vertex":1},"halfedge":2,"twin_halfedge":null,"tolerance":1e-8,"chart_seam":true},{"id":2,"geometry":{"kind":"Curve","curve":0,"range":{"lo":-1.5707963267948966,"hi":1.5707963267948966}},"halfedge":1,"twin_halfedge":3,"tolerance":1e-8,"chart_seam":true}],"halfedges":[{"id":0,"from":0,"to":0,"twin":null,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":0,"to":1,"twin":3,"next":2,"prev":0,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":1,"to":1,"twin":null,"next":3,"prev":1,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":1,"to":0,"twin":1,"next":0,"prev":2,"edge":2,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":3,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false}],"faces":[{"id":0,"key":"skin","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.0,"hi":6.283185307179586},{"lo":-1.5707963267948966,"hi":1.5707963267948966}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"sphere","body":"sphere","key":"skin","face":0}],"role":"Authored","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/sphere.step.m b/main/opengeometry/tests/fixtures/cases/sphere.step.m new file mode 100644 index 0000000..2396e15 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere.step.m @@ -0,0 +1,60 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('sphere','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=SPHERICAL_SURFACE('',#5,1.00000000000000000E0); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,-1.00000000000000000E0,0.00000000000000000E0)); +#8=VERTEX_POINT('',#7); +#9=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#10=VERTEX_POINT('',#9); +#11=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#12=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#13=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#14=AXIS2_PLACEMENT_3D('',#11,#12,#13); +#15=CIRCLE('',#14,1.00000000000000000E0); +#16=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#17=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#18=VECTOR('',#17,1.00000000000000000E0); +#19=LINE('',#16,#18); +#20=DEFINITIONAL_REPRESENTATION('',(#19),#1); +#21=PCURVE('',#6,#20); +#22=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#24=VECTOR('',#23,1.00000000000000000E0); +#25=LINE('',#22,#24); +#26=DEFINITIONAL_REPRESENTATION('',(#25),#1); +#27=PCURVE('',#6,#26); +#28=SEAM_CURVE('',#15,(#21,#27),.CURVE_3D.); +#29=EDGE_CURVE('',#8,#10,#28,.T.); +#30=ORIENTED_EDGE('',*,*,#29,.T.); +#31=ORIENTED_EDGE('',*,*,#29,.F.); +#32=EDGE_LOOP('',(#30,#31)); +#33=FACE_OUTER_BOUND('',#32,.T.); +#34=ADVANCED_FACE('skin',(#33),#6,.T.); +#35=CLOSED_SHELL('',(#34)); +#36=MANIFOLD_SOLID_BREP('sphere-0',#35); +#37=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#38=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#39=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#40=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000142108547154E-8),#37,'distance_accuracy_value','exchange error bound'); +#41=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#40)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#37,#38,#39)) REPRESENTATION_CONTEXT('','')); +#42=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#36),#41); +#43=APPLICATION_CONTEXT('automotive design'); +#44=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#43); +#45=PRODUCT_CONTEXT('',#43,'mechanical'); +#46=PRODUCT('sphere','sphere','',(#45)); +#47=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#46,.NOT_KNOWN.); +#48=PRODUCT_DEFINITION_CONTEXT('part definition',#43,'design'); +#49=PRODUCT_DEFINITION('','',#47,#48); +#50=PRODUCT_DEFINITION_SHAPE('','',#49); +#51=SHAPE_DEFINITION_REPRESENTATION(#50,#42); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/sphere.step.m.report.json b/main/opengeometry/tests/fixtures/cases/sphere.step.m.report.json new file mode 100644 index 0000000..2d97840 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":2,"edges":1,"exchange_error_bound":1.0000014210854715e-8,"faces":1,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/sphere.step.mm b/main/opengeometry/tests/fixtures/cases/sphere.step.mm new file mode 100644 index 0000000..2e1ce9c --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere.step.mm @@ -0,0 +1,60 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('sphere','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=SPHERICAL_SURFACE('',#5,1.00000000000000000E3); +#7=CARTESIAN_POINT('',(0.00000000000000000E0,-1.00000000000000000E3,0.00000000000000000E0)); +#8=VERTEX_POINT('',#7); +#9=CARTESIAN_POINT('',(0.00000000000000000E0,1.00000000000000000E3,0.00000000000000000E0)); +#10=VERTEX_POINT('',#9); +#11=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#12=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#13=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#14=AXIS2_PLACEMENT_3D('',#11,#12,#13); +#15=CIRCLE('',#14,1.00000000000000000E3); +#16=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#17=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#18=VECTOR('',#17,1.00000000000000000E0); +#19=LINE('',#16,#18); +#20=DEFINITIONAL_REPRESENTATION('',(#19),#1); +#21=PCURVE('',#6,#20); +#22=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#23=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#24=VECTOR('',#23,1.00000000000000000E0); +#25=LINE('',#22,#24); +#26=DEFINITIONAL_REPRESENTATION('',(#25),#1); +#27=PCURVE('',#6,#26); +#28=SEAM_CURVE('',#15,(#21,#27),.CURVE_3D.); +#29=EDGE_CURVE('',#8,#10,#28,.T.); +#30=ORIENTED_EDGE('',*,*,#29,.T.); +#31=ORIENTED_EDGE('',*,*,#29,.F.); +#32=EDGE_LOOP('',(#30,#31)); +#33=FACE_OUTER_BOUND('',#32,.T.); +#34=ADVANCED_FACE('skin',(#33),#6,.T.); +#35=CLOSED_SHELL('',(#34)); +#36=MANIFOLD_SOLID_BREP('sphere-0',#35); +#37=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#38=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#39=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#40=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000142108547160E-5),#37,'distance_accuracy_value','exchange error bound'); +#41=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#40)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#37,#38,#39)) REPRESENTATION_CONTEXT('','')); +#42=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#36),#41); +#43=APPLICATION_CONTEXT('automotive design'); +#44=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#43); +#45=PRODUCT_CONTEXT('',#43,'mechanical'); +#46=PRODUCT('sphere','sphere','',(#45)); +#47=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#46,.NOT_KNOWN.); +#48=PRODUCT_DEFINITION_CONTEXT('part definition',#43,'design'); +#49=PRODUCT_DEFINITION('','',#47,#48); +#50=PRODUCT_DEFINITION_SHAPE('','',#49); +#51=SHAPE_DEFINITION_REPRESENTATION(#50,#42); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/sphere.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/sphere.step.mm.report.json new file mode 100644 index 0000000..7e26c30 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":2,"edges":1,"exchange_error_bound":0.000010000014210854716,"faces":1,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/sphere.tess.json b/main/opengeometry/tests/fixtures/cases/sphere.tess.json new file mode 100644 index 0000000..16c1823 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/sphere.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[0,128,127,1,129,128,2,130,129,3,131,130,4,132,131,5,133,132,6,134,133,7,135,134,8,136,135,9,137,136,10,138,137,11,139,138,12,140,139,13,141,140,14,142,141,15,143,142,16,144,143,17,145,144,18,146,145,19,147,146,20,148,147,21,149,148,22,150,149,23,151,150,24,152,151,25,153,152,26,154,153,27,155,154,28,156,155,29,157,156,30,158,157,31,159,158,32,160,159,33,161,160,34,162,161,35,163,162,36,164,163,37,165,164,38,166,165,39,167,166,40,168,167,41,169,168,42,170,169,43,171,170,44,172,171,45,173,172,46,174,173,47,175,174,48,176,175,49,177,176,50,178,177,51,179,178,52,180,179,53,181,180,54,182,181,55,183,182,56,184,183,57,185,184,58,186,185,59,187,186,60,188,187,61,189,188,62,190,189,63,191,190,64,192,191,65,193,192,66,194,193,67,195,194,68,196,195,69,197,196,70,198,197,71,199,198,72,200,199,73,201,200,74,202,201,75,203,202,76,204,203,77,205,204,78,206,205,79,207,206,80,208,207,81,209,208,82,210,209,83,211,210,84,212,211,85,213,212,86,214,213,87,215,214,88,216,215,89,217,216,90,218,217,91,219,218,92,220,219,93,221,220,94,222,221,95,223,222,96,224,223,97,225,224,98,226,225,99,227,226,100,228,227,101,229,228,102,230,229,103,231,230,104,232,231,105,233,232,106,234,233,107,235,234,108,236,235,109,237,236,110,238,237,111,239,238,112,240,239,113,241,240,114,242,241,115,243,242,116,244,243,117,245,244,118,246,245,119,247,246,120,248,247,121,249,248,122,250,249,123,251,250,124,252,251,125,253,252,127,128,255,127,255,254,128,129,256,128,256,255,129,130,257,129,257,256,130,131,258,130,258,257,131,132,259,131,259,258,132,133,260,132,260,259,133,134,261,133,261,260,134,135,262,134,262,261,135,136,263,135,263,262,136,137,264,136,264,263,137,138,265,137,265,264,138,139,266,138,266,265,139,140,267,139,267,266,140,141,268,140,268,267,141,142,269,141,269,268,142,143,270,142,270,269,143,144,271,143,271,270,144,145,272,144,272,271,145,146,273,145,273,272,146,147,274,146,274,273,147,148,275,147,275,274,148,149,276,148,276,275,149,150,277,149,277,276,150,151,278,150,278,277,151,152,279,151,279,278,152,153,280,152,280,279,153,154,281,153,281,280,154,155,282,154,282,281,155,156,283,155,283,282,156,157,284,156,284,283,157,158,285,157,285,284,158,159,286,158,286,285,159,160,287,159,287,286,160,161,288,160,288,287,161,162,289,161,289,288,162,163,290,162,290,289,163,164,291,163,291,290,164,165,292,164,292,291,165,166,293,165,293,292,166,167,294,166,294,293,167,168,295,167,295,294,168,169,296,168,296,295,169,170,297,169,297,296,170,171,298,170,298,297,171,172,299,171,299,298,172,173,300,172,300,299,173,174,301,173,301,300,174,175,302,174,302,301,175,176,303,175,303,302,176,177,304,176,304,303,177,178,305,177,305,304,178,179,306,178,306,305,179,180,307,179,307,306,180,181,308,180,308,307,181,182,309,181,309,308,182,183,310,182,310,309,183,184,311,183,311,310,184,185,312,184,312,311,185,186,313,185,313,312,186,187,314,186,314,313,187,188,315,187,315,314,188,189,316,188,316,315,189,190,317,189,317,316,190,191,318,190,318,317,191,192,319,191,319,318,192,193,320,192,320,319,193,194,321,193,321,320,194,195,322,194,322,321,195,196,323,195,323,322,196,197,324,196,324,323,197,198,325,197,325,324,198,199,326,198,326,325,199,200,327,199,327,326,200,201,328,200,328,327,201,202,329,201,329,328,202,203,330,202,330,329,203,204,331,203,331,330,204,205,332,204,332,331,205,206,333,205,333,332,206,207,334,206,334,333,207,208,335,207,335,334,208,209,336,208,336,335,209,210,337,209,337,336,210,211,338,210,338,337,211,212,339,211,339,338,212,213,340,212,340,339,213,214,341,213,341,340,214,215,342,214,342,341,215,216,343,215,343,342,216,217,344,216,344,343,217,218,345,217,345,344,218,219,346,218,346,345,219,220,347,219,347,346,220,221,348,220,348,347,221,222,349,221,349,348,222,223,350,222,350,349,223,224,351,223,351,350,224,225,352,224,352,351,225,226,353,225,353,352,226,227,354,226,354,353,227,228,355,227,355,354,228,229,356,228,356,355,229,230,357,229,357,356,230,231,358,230,358,357,231,232,359,231,359,358,232,233,360,232,360,359,233,234,361,233,361,360,234,235,362,234,362,361,235,236,363,235,363,362,236,237,364,236,364,363,237,238,365,237,365,364,238,239,366,238,366,365,239,240,367,239,367,366,240,241,368,240,368,367,241,242,369,241,369,368,242,243,370,242,370,369,243,244,371,243,371,370,244,245,372,244,372,371,245,246,373,245,373,372,246,247,374,246,374,373,247,248,375,247,375,374,248,249,376,248,376,375,249,250,377,249,377,376,250,251,378,250,378,377,251,252,379,251,379,378,252,253,380,252,380,379,254,255,382,254,382,381,255,256,383,255,383,382,256,257,384,256,384,383,257,258,385,257,385,384,258,259,386,258,386,385,259,260,387,259,387,386,260,261,388,260,388,387,261,262,389,261,389,388,262,263,390,262,390,389,263,264,391,263,391,390,264,265,392,264,392,391,265,266,393,265,393,392,266,267,394,266,394,393,267,268,395,267,395,394,268,269,396,268,396,395,269,270,397,269,397,396,270,271,398,270,398,397,271,272,399,271,399,398,272,273,400,272,400,399,273,274,401,273,401,400,274,275,402,274,402,401,275,276,403,275,403,402,276,277,404,276,404,403,277,278,405,277,405,404,278,279,406,278,406,405,279,280,407,279,407,406,280,281,408,280,408,407,281,282,409,281,409,408,282,283,410,282,410,409,283,284,411,283,411,410,284,285,412,284,412,411,285,286,413,285,413,412,286,287,414,286,414,413,287,288,415,287,415,414,288,289,416,288,416,415,289,290,417,289,417,416,290,291,418,290,418,417,291,292,419,291,419,418,292,293,420,292,420,419,293,294,421,293,421,420,294,295,422,294,422,421,295,296,423,295,423,422,296,297,424,296,424,423,297,298,425,297,425,424,298,299,426,298,426,425,299,300,427,299,427,426,300,301,428,300,428,427,301,302,429,301,429,428,302,303,430,302,430,429,303,304,431,303,431,430,304,305,432,304,432,431,305,306,433,305,433,432,306,307,434,306,434,433,307,308,435,307,435,434,308,309,436,308,436,435,309,310,437,309,437,436,310,311,438,310,438,437,311,312,439,311,439,438,312,313,440,312,440,439,313,314,441,313,441,440,314,315,442,314,442,441,315,316,443,315,443,442,316,317,444,316,444,443,317,318,445,317,445,444,318,319,446,318,446,445,319,320,447,319,447,446,320,321,448,320,448,447,321,322,449,321,449,448,322,323,450,322,450,449,323,324,451,323,451,450,324,325,452,324,452,451,325,326,453,325,453,452,326,327,454,326,454,453,327,328,455,327,455,454,328,329,456,328,456,455,329,330,457,329,457,456,330,331,458,330,458,457,331,332,459,331,459,458,332,333,460,332,460,459,333,334,461,333,461,460,334,335,462,334,462,461,335,336,463,335,463,462,336,337,464,336,464,463,337,338,465,337,465,464,338,339,466,338,466,465,339,340,467,339,467,466,340,341,468,340,468,467,341,342,469,341,469,468,342,343,470,342,470,469,343,344,471,343,471,470,344,345,472,344,472,471,345,346,473,345,473,472,346,347,474,346,474,473,347,348,475,347,475,474,348,349,476,348,476,475,349,350,477,349,477,476,350,351,478,350,478,477,351,352,479,351,479,478,352,353,480,352,480,479,353,354,481,353,481,480,354,355,482,354,482,481,355,356,483,355,483,482,356,357,484,356,484,483,357,358,485,357,485,484,358,359,486,358,486,485,359,360,487,359,487,486,360,361,488,360,488,487,361,362,489,361,489,488,362,363,490,362,490,489,363,364,491,363,491,490,364,365,492,364,492,491,365,366,493,365,493,492,366,367,494,366,494,493,367,368,495,367,495,494,368,369,496,368,496,495,369,370,497,369,497,496,370,371,498,370,498,497,371,372,499,371,499,498,372,373,500,372,500,499,373,374,501,373,501,500,374,375,502,374,502,501,375,376,503,375,503,502,376,377,504,376,504,503,377,378,505,377,505,504,378,379,506,378,506,505,379,380,507,379,507,506,381,382,509,381,509,508,382,383,510,382,510,509,383,384,511,383,511,510,384,385,512,384,512,511,385,386,513,385,513,512,386,387,514,386,514,513,387,388,515,387,515,514,388,389,516,388,516,515,389,390,517,389,517,516,390,391,518,390,518,517,391,392,519,391,519,518,392,393,520,392,520,519,393,394,521,393,521,520,394,395,522,394,522,521,395,396,523,395,523,522,396,397,524,396,524,523,397,398,525,397,525,524,398,399,526,398,526,525,399,400,527,399,527,526,400,401,528,400,528,527,401,402,529,401,529,528,402,403,530,402,530,529,403,404,531,403,531,530,404,405,532,404,532,531,405,406,533,405,533,532,406,407,534,406,534,533,407,408,535,407,535,534,408,409,536,408,536,535,409,410,537,409,537,536,410,411,538,410,538,537,411,412,539,411,539,538,412,413,540,412,540,539,413,414,541,413,541,540,414,415,542,414,542,541,415,416,543,415,543,542,416,417,544,416,544,543,417,418,545,417,545,544,418,419,546,418,546,545,419,420,547,419,547,546,420,421,548,420,548,547,421,422,549,421,549,548,422,423,550,422,550,549,423,424,551,423,551,550,424,425,552,424,552,551,425,426,553,425,553,552,426,427,554,426,554,553,427,428,555,427,555,554,428,429,556,428,556,555,429,430,557,429,557,556,430,431,558,430,558,557,431,432,559,431,559,558,432,433,560,432,560,559,433,434,561,433,561,560,434,435,562,434,562,561,435,436,563,435,563,562,436,437,564,436,564,563,437,438,565,437,565,564,438,439,566,438,566,565,439,440,567,439,567,566,440,441,568,440,568,567,441,442,569,441,569,568,442,443,570,442,570,569,443,444,571,443,571,570,444,445,572,444,572,571,445,446,573,445,573,572,446,447,574,446,574,573,447,448,575,447,575,574,448,449,576,448,576,575,449,450,577,449,577,576,450,451,578,450,578,577,451,452,579,451,579,578,452,453,580,452,580,579,453,454,581,453,581,580,454,455,582,454,582,581,455,456,583,455,583,582,456,457,584,456,584,583,457,458,585,457,585,584,458,459,586,458,586,585,459,460,587,459,587,586,460,461,588,460,588,587,461,462,589,461,589,588,462,463,590,462,590,589,463,464,591,463,591,590,464,465,592,464,592,591,465,466,593,465,593,592,466,467,594,466,594,593,467,468,595,467,595,594,468,469,596,468,596,595,469,470,597,469,597,596,470,471,598,470,598,597,471,472,599,471,599,598,472,473,600,472,600,599,473,474,601,473,601,600,474,475,602,474,602,601,475,476,603,475,603,602,476,477,604,476,604,603,477,478,605,477,605,604,478,479,606,478,606,605,479,480,607,479,607,606,480,481,608,480,608,607,481,482,609,481,609,608,482,483,610,482,610,609,483,484,611,483,611,610,484,485,612,484,612,611,485,486,613,485,613,612,486,487,614,486,614,613,487,488,615,487,615,614,488,489,616,488,616,615,489,490,617,489,617,616,490,491,618,490,618,617,491,492,619,491,619,618,492,493,620,492,620,619,493,494,621,493,621,620,494,495,622,494,622,621,495,496,623,495,623,622,496,497,624,496,624,623,497,498,625,497,625,624,498,499,626,498,626,625,499,500,627,499,627,626,500,501,628,500,628,627,501,502,629,501,629,628,502,503,630,502,630,629,503,504,631,503,631,630,504,505,632,504,632,631,505,506,633,505,633,632,506,507,634,506,634,633,508,509,636,508,636,635,509,510,637,509,637,636,510,511,638,510,638,637,511,512,639,511,639,638,512,513,640,512,640,639,513,514,641,513,641,640,514,515,642,514,642,641,515,516,643,515,643,642,516,517,644,516,644,643,517,518,645,517,645,644,518,519,646,518,646,645,519,520,647,519,647,646,520,521,648,520,648,647,521,522,649,521,649,648,522,523,650,522,650,649,523,524,651,523,651,650,524,525,652,524,652,651,525,526,653,525,653,652,526,527,654,526,654,653,527,528,655,527,655,654,528,529,656,528,656,655,529,530,657,529,657,656,530,531,658,530,658,657,531,532,659,531,659,658,532,533,660,532,660,659,533,534,661,533,661,660,534,535,662,534,662,661,535,536,663,535,663,662,536,537,664,536,664,663,537,538,665,537,665,664,538,539,666,538,666,665,539,540,667,539,667,666,540,541,668,540,668,667,541,542,669,541,669,668,542,543,670,542,670,669,543,544,671,543,671,670,544,545,672,544,672,671,545,546,673,545,673,672,546,547,674,546,674,673,547,548,675,547,675,674,548,549,676,548,676,675,549,550,677,549,677,676,550,551,678,550,678,677,551,552,679,551,679,678,552,553,680,552,680,679,553,554,681,553,681,680,554,555,682,554,682,681,555,556,683,555,683,682,556,557,684,556,684,683,557,558,685,557,685,684,558,559,686,558,686,685,559,560,687,559,687,686,560,561,688,560,688,687,561,562,689,561,689,688,562,563,690,562,690,689,563,564,691,563,691,690,564,565,692,564,692,691,565,566,693,565,693,692,566,567,694,566,694,693,567,568,695,567,695,694,568,569,696,568,696,695,569,570,697,569,697,696,570,571,698,570,698,697,571,572,699,571,699,698,572,573,700,572,700,699,573,574,701,573,701,700,574,575,702,574,702,701,575,576,703,575,703,702,576,577,704,576,704,703,577,578,705,577,705,704,578,579,706,578,706,705,579,580,707,579,707,706,580,581,708,580,708,707,581,582,709,581,709,708,582,583,710,582,710,709,583,584,711,583,711,710,584,585,712,584,712,711,585,586,713,585,713,712,586,587,714,586,714,713,587,588,715,587,715,714,588,589,716,588,716,715,589,590,717,589,717,716,590,591,718,590,718,717,591,592,719,591,719,718,592,593,720,592,720,719,593,594,721,593,721,720,594,595,722,594,722,721,595,596,723,595,723,722,596,597,724,596,724,723,597,598,725,597,725,724,598,599,726,598,726,725,599,600,727,599,727,726,600,601,728,600,728,727,601,602,729,601,729,728,602,603,730,602,730,729,603,604,731,603,731,730,604,605,732,604,732,731,605,606,733,605,733,732,606,607,734,606,734,733,607,608,735,607,735,734,608,609,736,608,736,735,609,610,737,609,737,736,610,611,738,610,738,737,611,612,739,611,739,738,612,613,740,612,740,739,613,614,741,613,741,740,614,615,742,614,742,741,615,616,743,615,743,742,616,617,744,616,744,743,617,618,745,617,745,744,618,619,746,618,746,745,619,620,747,619,747,746,620,621,748,620,748,747,621,622,749,621,749,748,622,623,750,622,750,749,623,624,751,623,751,750,624,625,752,624,752,751,625,626,753,625,753,752,626,627,754,626,754,753,627,628,755,627,755,754,628,629,756,628,756,755,629,630,757,629,757,756,630,631,758,630,758,757,631,632,759,631,759,758,632,633,760,632,760,759,633,634,761,633,761,760,635,636,763,635,763,762,636,637,764,636,764,763,637,638,765,637,765,764,638,639,766,638,766,765,639,640,767,639,767,766,640,641,768,640,768,767,641,642,769,641,769,768,642,643,770,642,770,769,643,644,771,643,771,770,644,645,772,644,772,771,645,646,773,645,773,772,646,647,774,646,774,773,647,648,775,647,775,774,648,649,776,648,776,775,649,650,777,649,777,776,650,651,778,650,778,777,651,652,779,651,779,778,652,653,780,652,780,779,653,654,781,653,781,780,654,655,782,654,782,781,655,656,783,655,783,782,656,657,784,656,784,783,657,658,785,657,785,784,658,659,786,658,786,785,659,660,787,659,787,786,660,661,788,660,788,787,661,662,789,661,789,788,662,663,790,662,790,789,663,664,791,663,791,790,664,665,792,664,792,791,665,666,793,665,793,792,666,667,794,666,794,793,667,668,795,667,795,794,668,669,796,668,796,795,669,670,797,669,797,796,670,671,798,670,798,797,671,672,799,671,799,798,672,673,800,672,800,799,673,674,801,673,801,800,674,675,802,674,802,801,675,676,803,675,803,802,676,677,804,676,804,803,677,678,805,677,805,804,678,679,806,678,806,805,679,680,807,679,807,806,680,681,808,680,808,807,681,682,809,681,809,808,682,683,810,682,810,809,683,684,811,683,811,810,684,685,812,684,812,811,685,686,813,685,813,812,686,687,814,686,814,813,687,688,815,687,815,814,688,689,816,688,816,815,689,690,817,689,817,816,690,691,818,690,818,817,691,692,819,691,819,818,692,693,820,692,820,819,693,694,821,693,821,820,694,695,822,694,822,821,695,696,823,695,823,822,696,697,824,696,824,823,697,698,825,697,825,824,698,699,826,698,826,825,699,700,827,699,827,826,700,701,828,700,828,827,701,702,829,701,829,828,702,703,830,702,830,829,703,704,831,703,831,830,704,705,832,704,832,831,705,706,833,705,833,832,706,707,834,706,834,833,707,708,835,707,835,834,708,709,836,708,836,835,709,710,837,709,837,836,710,711,838,710,838,837,711,712,839,711,839,838,712,713,840,712,840,839,713,714,841,713,841,840,714,715,842,714,842,841,715,716,843,715,843,842,716,717,844,716,844,843,717,718,845,717,845,844,718,719,846,718,846,845,719,720,847,719,847,846,720,721,848,720,848,847,721,722,849,721,849,848,722,723,850,722,850,849,723,724,851,723,851,850,724,725,852,724,852,851,725,726,853,725,853,852,726,727,854,726,854,853,727,728,855,727,855,854,728,729,856,728,856,855,729,730,857,729,857,856,730,731,858,730,858,857,731,732,859,731,859,858,732,733,860,732,860,859,733,734,861,733,861,860,734,735,862,734,862,861,735,736,863,735,863,862,736,737,864,736,864,863,737,738,865,737,865,864,738,739,866,738,866,865,739,740,867,739,867,866,740,741,868,740,868,867,741,742,869,741,869,868,742,743,870,742,870,869,743,744,871,743,871,870,744,745,872,744,872,871,745,746,873,745,873,872,746,747,874,746,874,873,747,748,875,747,875,874,748,749,876,748,876,875,749,750,877,749,877,876,750,751,878,750,878,877,751,752,879,751,879,878,752,753,880,752,880,879,753,754,881,753,881,880,754,755,882,754,882,881,755,756,883,755,883,882,756,757,884,756,884,883,757,758,885,757,885,884,758,759,886,758,886,885,759,760,887,759,887,886,760,761,888,760,888,887,762,763,890,762,890,889,763,764,891,763,891,890,764,765,892,764,892,891,765,766,893,765,893,892,766,767,894,766,894,893,767,768,895,767,895,894,768,769,896,768,896,895,769,770,897,769,897,896,770,771,898,770,898,897,771,772,899,771,899,898,772,773,900,772,900,899,773,774,901,773,901,900,774,775,902,774,902,901,775,776,903,775,903,902,776,777,904,776,904,903,777,778,905,777,905,904,778,779,906,778,906,905,779,780,907,779,907,906,780,781,908,780,908,907,781,782,909,781,909,908,782,783,910,782,910,909,783,784,911,783,911,910,784,785,912,784,912,911,785,786,913,785,913,912,786,787,914,786,914,913,787,788,915,787,915,914,788,789,916,788,916,915,789,790,917,789,917,916,790,791,918,790,918,917,791,792,919,791,919,918,792,793,920,792,920,919,793,794,921,793,921,920,794,795,922,794,922,921,795,796,923,795,923,922,796,797,924,796,924,923,797,798,925,797,925,924,798,799,926,798,926,925,799,800,927,799,927,926,800,801,928,800,928,927,801,802,929,801,929,928,802,803,930,802,930,929,803,804,931,803,931,930,804,805,932,804,932,931,805,806,933,805,933,932,806,807,934,806,934,933,807,808,935,807,935,934,808,809,936,808,936,935,809,810,937,809,937,936,810,811,938,810,938,937,811,812,939,811,939,938,812,813,940,812,940,939,813,814,941,813,941,940,814,815,942,814,942,941,815,816,943,815,943,942,816,817,944,816,944,943,817,818,945,817,945,944,818,819,946,818,946,945,819,820,947,819,947,946,820,821,948,820,948,947,821,822,949,821,949,948,822,823,950,822,950,949,823,824,951,823,951,950,824,825,952,824,952,951,825,826,953,825,953,952,826,827,954,826,954,953,827,828,955,827,955,954,828,829,956,828,956,955,829,830,957,829,957,956,830,831,958,830,958,957,831,832,959,831,959,958,832,833,960,832,960,959,833,834,961,833,961,960,834,835,962,834,962,961,835,836,963,835,963,962,836,837,964,836,964,963,837,838,965,837,965,964,838,839,966,838,966,965,839,840,967,839,967,966,840,841,968,840,968,967,841,842,969,841,969,968,842,843,970,842,970,969,843,844,971,843,971,970,844,845,972,844,972,971,845,846,973,845,973,972,846,847,974,846,974,973,847,848,975,847,975,974,848,849,976,848,976,975,849,850,977,849,977,976,850,851,978,850,978,977,851,852,979,851,979,978,852,853,980,852,980,979,853,854,981,853,981,980,854,855,982,854,982,981,855,856,983,855,983,982,856,857,984,856,984,983,857,858,985,857,985,984,858,859,986,858,986,985,859,860,987,859,987,986,860,861,988,860,988,987,861,862,989,861,989,988,862,863,990,862,990,989,863,864,991,863,991,990,864,865,992,864,992,991,865,866,993,865,993,992,866,867,994,866,994,993,867,868,995,867,995,994,868,869,996,868,996,995,869,870,997,869,997,996,870,871,998,870,998,997,871,872,999,871,999,998,872,873,1000,872,1000,999,873,874,1001,873,1001,1000,874,875,1002,874,1002,1001,875,876,1003,875,1003,1002,876,877,1004,876,1004,1003,877,878,1005,877,1005,1004,878,879,1006,878,1006,1005,879,880,1007,879,1007,1006,880,881,1008,880,1008,1007,881,882,1009,881,1009,1008,882,883,1010,882,1010,1009,883,884,1011,883,1011,1010,884,885,1012,884,1012,1011,885,886,1013,885,1013,1012,886,887,1014,886,1014,1013,887,888,1015,887,1015,1014,889,890,1017,889,1017,1016,890,891,1018,890,1018,1017,891,892,1019,891,1019,1018,892,893,1020,892,1020,1019,893,894,1021,893,1021,1020,894,895,1022,894,1022,1021,895,896,1023,895,1023,1022,896,897,1024,896,1024,1023,897,898,1025,897,1025,1024,898,899,1026,898,1026,1025,899,900,1027,899,1027,1026,900,901,1028,900,1028,1027,901,902,1029,901,1029,1028,902,903,1030,902,1030,1029,903,904,1031,903,1031,1030,904,905,1032,904,1032,1031,905,906,1033,905,1033,1032,906,907,1034,906,1034,1033,907,908,1035,907,1035,1034,908,909,1036,908,1036,1035,909,910,1037,909,1037,1036,910,911,1038,910,1038,1037,911,912,1039,911,1039,1038,912,913,1040,912,1040,1039,913,914,1041,913,1041,1040,914,915,1042,914,1042,1041,915,916,1043,915,1043,1042,916,917,1044,916,1044,1043,917,918,1045,917,1045,1044,918,919,1046,918,1046,1045,919,920,1047,919,1047,1046,920,921,1048,920,1048,1047,921,922,1049,921,1049,1048,922,923,1050,922,1050,1049,923,924,1051,923,1051,1050,924,925,1052,924,1052,1051,925,926,1053,925,1053,1052,926,927,1054,926,1054,1053,927,928,1055,927,1055,1054,928,929,1056,928,1056,1055,929,930,1057,929,1057,1056,930,931,1058,930,1058,1057,931,932,1059,931,1059,1058,932,933,1060,932,1060,1059,933,934,1061,933,1061,1060,934,935,1062,934,1062,1061,935,936,1063,935,1063,1062,936,937,1064,936,1064,1063,937,938,1065,937,1065,1064,938,939,1066,938,1066,1065,939,940,1067,939,1067,1066,940,941,1068,940,1068,1067,941,942,1069,941,1069,1068,942,943,1070,942,1070,1069,943,944,1071,943,1071,1070,944,945,1072,944,1072,1071,945,946,1073,945,1073,1072,946,947,1074,946,1074,1073,947,948,1075,947,1075,1074,948,949,1076,948,1076,1075,949,950,1077,949,1077,1076,950,951,1078,950,1078,1077,951,952,1079,951,1079,1078,952,953,1080,952,1080,1079,953,954,1081,953,1081,1080,954,955,1082,954,1082,1081,955,956,1083,955,1083,1082,956,957,1084,956,1084,1083,957,958,1085,957,1085,1084,958,959,1086,958,1086,1085,959,960,1087,959,1087,1086,960,961,1088,960,1088,1087,961,962,1089,961,1089,1088,962,963,1090,962,1090,1089,963,964,1091,963,1091,1090,964,965,1092,964,1092,1091,965,966,1093,965,1093,1092,966,967,1094,966,1094,1093,967,968,1095,967,1095,1094,968,969,1096,968,1096,1095,969,970,1097,969,1097,1096,970,971,1098,970,1098,1097,971,972,1099,971,1099,1098,972,973,1100,972,1100,1099,973,974,1101,973,1101,1100,974,975,1102,974,1102,1101,975,976,1103,975,1103,1102,976,977,1104,976,1104,1103,977,978,1105,977,1105,1104,978,979,1106,978,1106,1105,979,980,1107,979,1107,1106,980,981,1108,980,1108,1107,981,982,1109,981,1109,1108,982,983,1110,982,1110,1109,983,984,1111,983,1111,1110,984,985,1112,984,1112,1111,985,986,1113,985,1113,1112,986,987,1114,986,1114,1113,987,988,1115,987,1115,1114,988,989,1116,988,1116,1115,989,990,1117,989,1117,1116,990,991,1118,990,1118,1117,991,992,1119,991,1119,1118,992,993,1120,992,1120,1119,993,994,1121,993,1121,1120,994,995,1122,994,1122,1121,995,996,1123,995,1123,1122,996,997,1124,996,1124,1123,997,998,1125,997,1125,1124,998,999,1126,998,1126,1125,999,1000,1127,999,1127,1126,1000,1001,1128,1000,1128,1127,1001,1002,1129,1001,1129,1128,1002,1003,1130,1002,1130,1129,1003,1004,1131,1003,1131,1130,1004,1005,1132,1004,1132,1131,1005,1006,1133,1005,1133,1132,1006,1007,1134,1006,1134,1133,1007,1008,1135,1007,1135,1134,1008,1009,1136,1008,1136,1135,1009,1010,1137,1009,1137,1136,1010,1011,1138,1010,1138,1137,1011,1012,1139,1011,1139,1138,1012,1013,1140,1012,1140,1139,1013,1014,1141,1013,1141,1140,1014,1015,1142,1014,1142,1141,1016,1017,1144,1016,1144,1143,1017,1018,1145,1017,1145,1144,1018,1019,1146,1018,1146,1145,1019,1020,1147,1019,1147,1146,1020,1021,1148,1020,1148,1147,1021,1022,1149,1021,1149,1148,1022,1023,1150,1022,1150,1149,1023,1024,1151,1023,1151,1150,1024,1025,1152,1024,1152,1151,1025,1026,1153,1025,1153,1152,1026,1027,1154,1026,1154,1153,1027,1028,1155,1027,1155,1154,1028,1029,1156,1028,1156,1155,1029,1030,1157,1029,1157,1156,1030,1031,1158,1030,1158,1157,1031,1032,1159,1031,1159,1158,1032,1033,1160,1032,1160,1159,1033,1034,1161,1033,1161,1160,1034,1035,1162,1034,1162,1161,1035,1036,1163,1035,1163,1162,1036,1037,1164,1036,1164,1163,1037,1038,1165,1037,1165,1164,1038,1039,1166,1038,1166,1165,1039,1040,1167,1039,1167,1166,1040,1041,1168,1040,1168,1167,1041,1042,1169,1041,1169,1168,1042,1043,1170,1042,1170,1169,1043,1044,1171,1043,1171,1170,1044,1045,1172,1044,1172,1171,1045,1046,1173,1045,1173,1172,1046,1047,1174,1046,1174,1173,1047,1048,1175,1047,1175,1174,1048,1049,1176,1048,1176,1175,1049,1050,1177,1049,1177,1176,1050,1051,1178,1050,1178,1177,1051,1052,1179,1051,1179,1178,1052,1053,1180,1052,1180,1179,1053,1054,1181,1053,1181,1180,1054,1055,1182,1054,1182,1181,1055,1056,1183,1055,1183,1182,1056,1057,1184,1056,1184,1183,1057,1058,1185,1057,1185,1184,1058,1059,1186,1058,1186,1185,1059,1060,1187,1059,1187,1186,1060,1061,1188,1060,1188,1187,1061,1062,1189,1061,1189,1188,1062,1063,1190,1062,1190,1189,1063,1064,1191,1063,1191,1190,1064,1065,1192,1064,1192,1191,1065,1066,1193,1065,1193,1192,1066,1067,1194,1066,1194,1193,1067,1068,1195,1067,1195,1194,1068,1069,1196,1068,1196,1195,1069,1070,1197,1069,1197,1196,1070,1071,1198,1070,1198,1197,1071,1072,1199,1071,1199,1198,1072,1073,1200,1072,1200,1199,1073,1074,1201,1073,1201,1200,1074,1075,1202,1074,1202,1201,1075,1076,1203,1075,1203,1202,1076,1077,1204,1076,1204,1203,1077,1078,1205,1077,1205,1204,1078,1079,1206,1078,1206,1205,1079,1080,1207,1079,1207,1206,1080,1081,1208,1080,1208,1207,1081,1082,1209,1081,1209,1208,1082,1083,1210,1082,1210,1209,1083,1084,1211,1083,1211,1210,1084,1085,1212,1084,1212,1211,1085,1086,1213,1085,1213,1212,1086,1087,1214,1086,1214,1213,1087,1088,1215,1087,1215,1214,1088,1089,1216,1088,1216,1215,1089,1090,1217,1089,1217,1216,1090,1091,1218,1090,1218,1217,1091,1092,1219,1091,1219,1218,1092,1093,1220,1092,1220,1219,1093,1094,1221,1093,1221,1220,1094,1095,1222,1094,1222,1221,1095,1096,1223,1095,1223,1222,1096,1097,1224,1096,1224,1223,1097,1098,1225,1097,1225,1224,1098,1099,1226,1098,1226,1225,1099,1100,1227,1099,1227,1226,1100,1101,1228,1100,1228,1227,1101,1102,1229,1101,1229,1228,1102,1103,1230,1102,1230,1229,1103,1104,1231,1103,1231,1230,1104,1105,1232,1104,1232,1231,1105,1106,1233,1105,1233,1232,1106,1107,1234,1106,1234,1233,1107,1108,1235,1107,1235,1234,1108,1109,1236,1108,1236,1235,1109,1110,1237,1109,1237,1236,1110,1111,1238,1110,1238,1237,1111,1112,1239,1111,1239,1238,1112,1113,1240,1112,1240,1239,1113,1114,1241,1113,1241,1240,1114,1115,1242,1114,1242,1241,1115,1116,1243,1115,1243,1242,1116,1117,1244,1116,1244,1243,1117,1118,1245,1117,1245,1244,1118,1119,1246,1118,1246,1245,1119,1120,1247,1119,1247,1246,1120,1121,1248,1120,1248,1247,1121,1122,1249,1121,1249,1248,1122,1123,1250,1122,1250,1249,1123,1124,1251,1123,1251,1250,1124,1125,1252,1124,1252,1251,1125,1126,1253,1125,1253,1252,1126,1127,1254,1126,1254,1253,1127,1128,1255,1127,1255,1254,1128,1129,1256,1128,1256,1255,1129,1130,1257,1129,1257,1256,1130,1131,1258,1130,1258,1257,1131,1132,1259,1131,1259,1258,1132,1133,1260,1132,1260,1259,1133,1134,1261,1133,1261,1260,1134,1135,1262,1134,1262,1261,1135,1136,1263,1135,1263,1262,1136,1137,1264,1136,1264,1263,1137,1138,1265,1137,1265,1264,1138,1139,1266,1138,1266,1265,1139,1140,1267,1139,1267,1266,1140,1141,1268,1140,1268,1267,1141,1142,1269,1141,1269,1268,1143,1144,1271,1143,1271,1270,1144,1145,1272,1144,1272,1271,1145,1146,1273,1145,1273,1272,1146,1147,1274,1146,1274,1273,1147,1148,1275,1147,1275,1274,1148,1149,1276,1148,1276,1275,1149,1150,1277,1149,1277,1276,1150,1151,1278,1150,1278,1277,1151,1152,1279,1151,1279,1278,1152,1153,1280,1152,1280,1279,1153,1154,1281,1153,1281,1280,1154,1155,1282,1154,1282,1281,1155,1156,1283,1155,1283,1282,1156,1157,1284,1156,1284,1283,1157,1158,1285,1157,1285,1284,1158,1159,1286,1158,1286,1285,1159,1160,1287,1159,1287,1286,1160,1161,1288,1160,1288,1287,1161,1162,1289,1161,1289,1288,1162,1163,1290,1162,1290,1289,1163,1164,1291,1163,1291,1290,1164,1165,1292,1164,1292,1291,1165,1166,1293,1165,1293,1292,1166,1167,1294,1166,1294,1293,1167,1168,1295,1167,1295,1294,1168,1169,1296,1168,1296,1295,1169,1170,1297,1169,1297,1296,1170,1171,1298,1170,1298,1297,1171,1172,1299,1171,1299,1298,1172,1173,1300,1172,1300,1299,1173,1174,1301,1173,1301,1300,1174,1175,1302,1174,1302,1301,1175,1176,1303,1175,1303,1302,1176,1177,1304,1176,1304,1303,1177,1178,1305,1177,1305,1304,1178,1179,1306,1178,1306,1305,1179,1180,1307,1179,1307,1306,1180,1181,1308,1180,1308,1307,1181,1182,1309,1181,1309,1308,1182,1183,1310,1182,1310,1309,1183,1184,1311,1183,1311,1310,1184,1185,1312,1184,1312,1311,1185,1186,1313,1185,1313,1312,1186,1187,1314,1186,1314,1313,1187,1188,1315,1187,1315,1314,1188,1189,1316,1188,1316,1315,1189,1190,1317,1189,1317,1316,1190,1191,1318,1190,1318,1317,1191,1192,1319,1191,1319,1318,1192,1193,1320,1192,1320,1319,1193,1194,1321,1193,1321,1320,1194,1195,1322,1194,1322,1321,1195,1196,1323,1195,1323,1322,1196,1197,1324,1196,1324,1323,1197,1198,1325,1197,1325,1324,1198,1199,1326,1198,1326,1325,1199,1200,1327,1199,1327,1326,1200,1201,1328,1200,1328,1327,1201,1202,1329,1201,1329,1328,1202,1203,1330,1202,1330,1329,1203,1204,1331,1203,1331,1330,1204,1205,1332,1204,1332,1331,1205,1206,1333,1205,1333,1332,1206,1207,1334,1206,1334,1333,1207,1208,1335,1207,1335,1334,1208,1209,1336,1208,1336,1335,1209,1210,1337,1209,1337,1336,1210,1211,1338,1210,1338,1337,1211,1212,1339,1211,1339,1338,1212,1213,1340,1212,1340,1339,1213,1214,1341,1213,1341,1340,1214,1215,1342,1214,1342,1341,1215,1216,1343,1215,1343,1342,1216,1217,1344,1216,1344,1343,1217,1218,1345,1217,1345,1344,1218,1219,1346,1218,1346,1345,1219,1220,1347,1219,1347,1346,1220,1221,1348,1220,1348,1347,1221,1222,1349,1221,1349,1348,1222,1223,1350,1222,1350,1349,1223,1224,1351,1223,1351,1350,1224,1225,1352,1224,1352,1351,1225,1226,1353,1225,1353,1352,1226,1227,1354,1226,1354,1353,1227,1228,1355,1227,1355,1354,1228,1229,1356,1228,1356,1355,1229,1230,1357,1229,1357,1356,1230,1231,1358,1230,1358,1357,1231,1232,1359,1231,1359,1358,1232,1233,1360,1232,1360,1359,1233,1234,1361,1233,1361,1360,1234,1235,1362,1234,1362,1361,1235,1236,1363,1235,1363,1362,1236,1237,1364,1236,1364,1363,1237,1238,1365,1237,1365,1364,1238,1239,1366,1238,1366,1365,1239,1240,1367,1239,1367,1366,1240,1241,1368,1240,1368,1367,1241,1242,1369,1241,1369,1368,1242,1243,1370,1242,1370,1369,1243,1244,1371,1243,1371,1370,1244,1245,1372,1244,1372,1371,1245,1246,1373,1245,1373,1372,1246,1247,1374,1246,1374,1373,1247,1248,1375,1247,1375,1374,1248,1249,1376,1248,1376,1375,1249,1250,1377,1249,1377,1376,1250,1251,1378,1250,1378,1377,1251,1252,1379,1251,1379,1378,1252,1253,1380,1252,1380,1379,1253,1254,1381,1253,1381,1380,1254,1255,1382,1254,1382,1381,1255,1256,1383,1255,1383,1382,1256,1257,1384,1256,1384,1383,1257,1258,1385,1257,1385,1384,1258,1259,1386,1258,1386,1385,1259,1260,1387,1259,1387,1386,1260,1261,1388,1260,1388,1387,1261,1262,1389,1261,1389,1388,1262,1263,1390,1262,1390,1389,1263,1264,1391,1263,1391,1390,1264,1265,1392,1264,1392,1391,1265,1266,1393,1265,1393,1392,1266,1267,1394,1266,1394,1393,1267,1268,1395,1267,1395,1394,1268,1269,1396,1268,1396,1395,1270,1271,1398,1270,1398,1397,1271,1272,1399,1271,1399,1398,1272,1273,1400,1272,1400,1399,1273,1274,1401,1273,1401,1400,1274,1275,1402,1274,1402,1401,1275,1276,1403,1275,1403,1402,1276,1277,1404,1276,1404,1403,1277,1278,1405,1277,1405,1404,1278,1279,1406,1278,1406,1405,1279,1280,1407,1279,1407,1406,1280,1281,1408,1280,1408,1407,1281,1282,1409,1281,1409,1408,1282,1283,1410,1282,1410,1409,1283,1284,1411,1283,1411,1410,1284,1285,1412,1284,1412,1411,1285,1286,1413,1285,1413,1412,1286,1287,1414,1286,1414,1413,1287,1288,1415,1287,1415,1414,1288,1289,1416,1288,1416,1415,1289,1290,1417,1289,1417,1416,1290,1291,1418,1290,1418,1417,1291,1292,1419,1291,1419,1418,1292,1293,1420,1292,1420,1419,1293,1294,1421,1293,1421,1420,1294,1295,1422,1294,1422,1421,1295,1296,1423,1295,1423,1422,1296,1297,1424,1296,1424,1423,1297,1298,1425,1297,1425,1424,1298,1299,1426,1298,1426,1425,1299,1300,1427,1299,1427,1426,1300,1301,1428,1300,1428,1427,1301,1302,1429,1301,1429,1428,1302,1303,1430,1302,1430,1429,1303,1304,1431,1303,1431,1430,1304,1305,1432,1304,1432,1431,1305,1306,1433,1305,1433,1432,1306,1307,1434,1306,1434,1433,1307,1308,1435,1307,1435,1434,1308,1309,1436,1308,1436,1435,1309,1310,1437,1309,1437,1436,1310,1311,1438,1310,1438,1437,1311,1312,1439,1311,1439,1438,1312,1313,1440,1312,1440,1439,1313,1314,1441,1313,1441,1440,1314,1315,1442,1314,1442,1441,1315,1316,1443,1315,1443,1442,1316,1317,1444,1316,1444,1443,1317,1318,1445,1317,1445,1444,1318,1319,1446,1318,1446,1445,1319,1320,1447,1319,1447,1446,1320,1321,1448,1320,1448,1447,1321,1322,1449,1321,1449,1448,1322,1323,1450,1322,1450,1449,1323,1324,1451,1323,1451,1450,1324,1325,1452,1324,1452,1451,1325,1326,1453,1325,1453,1452,1326,1327,1454,1326,1454,1453,1327,1328,1455,1327,1455,1454,1328,1329,1456,1328,1456,1455,1329,1330,1457,1329,1457,1456,1330,1331,1458,1330,1458,1457,1331,1332,1459,1331,1459,1458,1332,1333,1460,1332,1460,1459,1333,1334,1461,1333,1461,1460,1334,1335,1462,1334,1462,1461,1335,1336,1463,1335,1463,1462,1336,1337,1464,1336,1464,1463,1337,1338,1465,1337,1465,1464,1338,1339,1466,1338,1466,1465,1339,1340,1467,1339,1467,1466,1340,1341,1468,1340,1468,1467,1341,1342,1469,1341,1469,1468,1342,1343,1470,1342,1470,1469,1343,1344,1471,1343,1471,1470,1344,1345,1472,1344,1472,1471,1345,1346,1473,1345,1473,1472,1346,1347,1474,1346,1474,1473,1347,1348,1475,1347,1475,1474,1348,1349,1476,1348,1476,1475,1349,1350,1477,1349,1477,1476,1350,1351,1478,1350,1478,1477,1351,1352,1479,1351,1479,1478,1352,1353,1480,1352,1480,1479,1353,1354,1481,1353,1481,1480,1354,1355,1482,1354,1482,1481,1355,1356,1483,1355,1483,1482,1356,1357,1484,1356,1484,1483,1357,1358,1485,1357,1485,1484,1358,1359,1486,1358,1486,1485,1359,1360,1487,1359,1487,1486,1360,1361,1488,1360,1488,1487,1361,1362,1489,1361,1489,1488,1362,1363,1490,1362,1490,1489,1363,1364,1491,1363,1491,1490,1364,1365,1492,1364,1492,1491,1365,1366,1493,1365,1493,1492,1366,1367,1494,1366,1494,1493,1367,1368,1495,1367,1495,1494,1368,1369,1496,1368,1496,1495,1369,1370,1497,1369,1497,1496,1370,1371,1498,1370,1498,1497,1371,1372,1499,1371,1499,1498,1372,1373,1500,1372,1500,1499,1373,1374,1501,1373,1501,1500,1374,1375,1502,1374,1502,1501,1375,1376,1503,1375,1503,1502,1376,1377,1504,1376,1504,1503,1377,1378,1505,1377,1505,1504,1378,1379,1506,1378,1506,1505,1379,1380,1507,1379,1507,1506,1380,1381,1508,1380,1508,1507,1381,1382,1509,1381,1509,1508,1382,1383,1510,1382,1510,1509,1383,1384,1511,1383,1511,1510,1384,1385,1512,1384,1512,1511,1385,1386,1513,1385,1513,1512,1386,1387,1514,1386,1514,1513,1387,1388,1515,1387,1515,1514,1388,1389,1516,1388,1516,1515,1389,1390,1517,1389,1517,1516,1390,1391,1518,1390,1518,1517,1391,1392,1519,1391,1519,1518,1392,1393,1520,1392,1520,1519,1393,1394,1521,1393,1521,1520,1394,1395,1522,1394,1522,1521,1395,1396,1523,1395,1523,1522,1397,1398,1525,1397,1525,1524,1398,1399,1526,1398,1526,1525,1399,1400,1527,1399,1527,1526,1400,1401,1528,1400,1528,1527,1401,1402,1529,1401,1529,1528,1402,1403,1530,1402,1530,1529,1403,1404,1531,1403,1531,1530,1404,1405,1532,1404,1532,1531,1405,1406,1533,1405,1533,1532,1406,1407,1534,1406,1534,1533,1407,1408,1535,1407,1535,1534,1408,1409,1536,1408,1536,1535,1409,1410,1537,1409,1537,1536,1410,1411,1538,1410,1538,1537,1411,1412,1539,1411,1539,1538,1412,1413,1540,1412,1540,1539,1413,1414,1541,1413,1541,1540,1414,1415,1542,1414,1542,1541,1415,1416,1543,1415,1543,1542,1416,1417,1544,1416,1544,1543,1417,1418,1545,1417,1545,1544,1418,1419,1546,1418,1546,1545,1419,1420,1547,1419,1547,1546,1420,1421,1548,1420,1548,1547,1421,1422,1549,1421,1549,1548,1422,1423,1550,1422,1550,1549,1423,1424,1551,1423,1551,1550,1424,1425,1552,1424,1552,1551,1425,1426,1553,1425,1553,1552,1426,1427,1554,1426,1554,1553,1427,1428,1555,1427,1555,1554,1428,1429,1556,1428,1556,1555,1429,1430,1557,1429,1557,1556,1430,1431,1558,1430,1558,1557,1431,1432,1559,1431,1559,1558,1432,1433,1560,1432,1560,1559,1433,1434,1561,1433,1561,1560,1434,1435,1562,1434,1562,1561,1435,1436,1563,1435,1563,1562,1436,1437,1564,1436,1564,1563,1437,1438,1565,1437,1565,1564,1438,1439,1566,1438,1566,1565,1439,1440,1567,1439,1567,1566,1440,1441,1568,1440,1568,1567,1441,1442,1569,1441,1569,1568,1442,1443,1570,1442,1570,1569,1443,1444,1571,1443,1571,1570,1444,1445,1572,1444,1572,1571,1445,1446,1573,1445,1573,1572,1446,1447,1574,1446,1574,1573,1447,1448,1575,1447,1575,1574,1448,1449,1576,1448,1576,1575,1449,1450,1577,1449,1577,1576,1450,1451,1578,1450,1578,1577,1451,1452,1579,1451,1579,1578,1452,1453,1580,1452,1580,1579,1453,1454,1581,1453,1581,1580,1454,1455,1582,1454,1582,1581,1455,1456,1583,1455,1583,1582,1456,1457,1584,1456,1584,1583,1457,1458,1585,1457,1585,1584,1458,1459,1586,1458,1586,1585,1459,1460,1587,1459,1587,1586,1460,1461,1588,1460,1588,1587,1461,1462,1589,1461,1589,1588,1462,1463,1590,1462,1590,1589,1463,1464,1591,1463,1591,1590,1464,1465,1592,1464,1592,1591,1465,1466,1593,1465,1593,1592,1466,1467,1594,1466,1594,1593,1467,1468,1595,1467,1595,1594,1468,1469,1596,1468,1596,1595,1469,1470,1597,1469,1597,1596,1470,1471,1598,1470,1598,1597,1471,1472,1599,1471,1599,1598,1472,1473,1600,1472,1600,1599,1473,1474,1601,1473,1601,1600,1474,1475,1602,1474,1602,1601,1475,1476,1603,1475,1603,1602,1476,1477,1604,1476,1604,1603,1477,1478,1605,1477,1605,1604,1478,1479,1606,1478,1606,1605,1479,1480,1607,1479,1607,1606,1480,1481,1608,1480,1608,1607,1481,1482,1609,1481,1609,1608,1482,1483,1610,1482,1610,1609,1483,1484,1611,1483,1611,1610,1484,1485,1612,1484,1612,1611,1485,1486,1613,1485,1613,1612,1486,1487,1614,1486,1614,1613,1487,1488,1615,1487,1615,1614,1488,1489,1616,1488,1616,1615,1489,1490,1617,1489,1617,1616,1490,1491,1618,1490,1618,1617,1491,1492,1619,1491,1619,1618,1492,1493,1620,1492,1620,1619,1493,1494,1621,1493,1621,1620,1494,1495,1622,1494,1622,1621,1495,1496,1623,1495,1623,1622,1496,1497,1624,1496,1624,1623,1497,1498,1625,1497,1625,1624,1498,1499,1626,1498,1626,1625,1499,1500,1627,1499,1627,1626,1500,1501,1628,1500,1628,1627,1501,1502,1629,1501,1629,1628,1502,1503,1630,1502,1630,1629,1503,1504,1631,1503,1631,1630,1504,1505,1632,1504,1632,1631,1505,1506,1633,1505,1633,1632,1506,1507,1634,1506,1634,1633,1507,1508,1635,1507,1635,1634,1508,1509,1636,1508,1636,1635,1509,1510,1637,1509,1637,1636,1510,1511,1638,1510,1638,1637,1511,1512,1639,1511,1639,1638,1512,1513,1640,1512,1640,1639,1513,1514,1641,1513,1641,1640,1514,1515,1642,1514,1642,1641,1515,1516,1643,1515,1643,1642,1516,1517,1644,1516,1644,1643,1517,1518,1645,1517,1645,1644,1518,1519,1646,1518,1646,1645,1519,1520,1647,1519,1647,1646,1520,1521,1648,1520,1648,1647,1521,1522,1649,1521,1649,1648,1522,1523,1650,1522,1650,1649,1524,1525,1652,1524,1652,1651,1525,1526,1653,1525,1653,1652,1526,1527,1654,1526,1654,1653,1527,1528,1655,1527,1655,1654,1528,1529,1656,1528,1656,1655,1529,1530,1657,1529,1657,1656,1530,1531,1658,1530,1658,1657,1531,1532,1659,1531,1659,1658,1532,1533,1660,1532,1660,1659,1533,1534,1661,1533,1661,1660,1534,1535,1662,1534,1662,1661,1535,1536,1663,1535,1663,1662,1536,1537,1664,1536,1664,1663,1537,1538,1665,1537,1665,1664,1538,1539,1666,1538,1666,1665,1539,1540,1667,1539,1667,1666,1540,1541,1668,1540,1668,1667,1541,1542,1669,1541,1669,1668,1542,1543,1670,1542,1670,1669,1543,1544,1671,1543,1671,1670,1544,1545,1672,1544,1672,1671,1545,1546,1673,1545,1673,1672,1546,1547,1674,1546,1674,1673,1547,1548,1675,1547,1675,1674,1548,1549,1676,1548,1676,1675,1549,1550,1677,1549,1677,1676,1550,1551,1678,1550,1678,1677,1551,1552,1679,1551,1679,1678,1552,1553,1680,1552,1680,1679,1553,1554,1681,1553,1681,1680,1554,1555,1682,1554,1682,1681,1555,1556,1683,1555,1683,1682,1556,1557,1684,1556,1684,1683,1557,1558,1685,1557,1685,1684,1558,1559,1686,1558,1686,1685,1559,1560,1687,1559,1687,1686,1560,1561,1688,1560,1688,1687,1561,1562,1689,1561,1689,1688,1562,1563,1690,1562,1690,1689,1563,1564,1691,1563,1691,1690,1564,1565,1692,1564,1692,1691,1565,1566,1693,1565,1693,1692,1566,1567,1694,1566,1694,1693,1567,1568,1695,1567,1695,1694,1568,1569,1696,1568,1696,1695,1569,1570,1697,1569,1697,1696,1570,1571,1698,1570,1698,1697,1571,1572,1699,1571,1699,1698,1572,1573,1700,1572,1700,1699,1573,1574,1701,1573,1701,1700,1574,1575,1702,1574,1702,1701,1575,1576,1703,1575,1703,1702,1576,1577,1704,1576,1704,1703,1577,1578,1705,1577,1705,1704,1578,1579,1706,1578,1706,1705,1579,1580,1707,1579,1707,1706,1580,1581,1708,1580,1708,1707,1581,1582,1709,1581,1709,1708,1582,1583,1710,1582,1710,1709,1583,1584,1711,1583,1711,1710,1584,1585,1712,1584,1712,1711,1585,1586,1713,1585,1713,1712,1586,1587,1714,1586,1714,1713,1587,1588,1715,1587,1715,1714,1588,1589,1716,1588,1716,1715,1589,1590,1717,1589,1717,1716,1590,1591,1718,1590,1718,1717,1591,1592,1719,1591,1719,1718,1592,1593,1720,1592,1720,1719,1593,1594,1721,1593,1721,1720,1594,1595,1722,1594,1722,1721,1595,1596,1723,1595,1723,1722,1596,1597,1724,1596,1724,1723,1597,1598,1725,1597,1725,1724,1598,1599,1726,1598,1726,1725,1599,1600,1727,1599,1727,1726,1600,1601,1728,1600,1728,1727,1601,1602,1729,1601,1729,1728,1602,1603,1730,1602,1730,1729,1603,1604,1731,1603,1731,1730,1604,1605,1732,1604,1732,1731,1605,1606,1733,1605,1733,1732,1606,1607,1734,1606,1734,1733,1607,1608,1735,1607,1735,1734,1608,1609,1736,1608,1736,1735,1609,1610,1737,1609,1737,1736,1610,1611,1738,1610,1738,1737,1611,1612,1739,1611,1739,1738,1612,1613,1740,1612,1740,1739,1613,1614,1741,1613,1741,1740,1614,1615,1742,1614,1742,1741,1615,1616,1743,1615,1743,1742,1616,1617,1744,1616,1744,1743,1617,1618,1745,1617,1745,1744,1618,1619,1746,1618,1746,1745,1619,1620,1747,1619,1747,1746,1620,1621,1748,1620,1748,1747,1621,1622,1749,1621,1749,1748,1622,1623,1750,1622,1750,1749,1623,1624,1751,1623,1751,1750,1624,1625,1752,1624,1752,1751,1625,1626,1753,1625,1753,1752,1626,1627,1754,1626,1754,1753,1627,1628,1755,1627,1755,1754,1628,1629,1756,1628,1756,1755,1629,1630,1757,1629,1757,1756,1630,1631,1758,1630,1758,1757,1631,1632,1759,1631,1759,1758,1632,1633,1760,1632,1760,1759,1633,1634,1761,1633,1761,1760,1634,1635,1762,1634,1762,1761,1635,1636,1763,1635,1763,1762,1636,1637,1764,1636,1764,1763,1637,1638,1765,1637,1765,1764,1638,1639,1766,1638,1766,1765,1639,1640,1767,1639,1767,1766,1640,1641,1768,1640,1768,1767,1641,1642,1769,1641,1769,1768,1642,1643,1770,1642,1770,1769,1643,1644,1771,1643,1771,1770,1644,1645,1772,1644,1772,1771,1645,1646,1773,1645,1773,1772,1646,1647,1774,1646,1774,1773,1647,1648,1775,1647,1775,1774,1648,1649,1776,1648,1776,1775,1649,1650,1777,1649,1777,1776,1651,1652,1779,1651,1779,1778,1652,1653,1780,1652,1780,1779,1653,1654,1781,1653,1781,1780,1654,1655,1782,1654,1782,1781,1655,1656,1783,1655,1783,1782,1656,1657,1784,1656,1784,1783,1657,1658,1785,1657,1785,1784,1658,1659,1786,1658,1786,1785,1659,1660,1787,1659,1787,1786,1660,1661,1788,1660,1788,1787,1661,1662,1789,1661,1789,1788,1662,1663,1790,1662,1790,1789,1663,1664,1791,1663,1791,1790,1664,1665,1792,1664,1792,1791,1665,1666,1793,1665,1793,1792,1666,1667,1794,1666,1794,1793,1667,1668,1795,1667,1795,1794,1668,1669,1796,1668,1796,1795,1669,1670,1797,1669,1797,1796,1670,1671,1798,1670,1798,1797,1671,1672,1799,1671,1799,1798,1672,1673,1800,1672,1800,1799,1673,1674,1801,1673,1801,1800,1674,1675,1802,1674,1802,1801,1675,1676,1803,1675,1803,1802,1676,1677,1804,1676,1804,1803,1677,1678,1805,1677,1805,1804,1678,1679,1806,1678,1806,1805,1679,1680,1807,1679,1807,1806,1680,1681,1808,1680,1808,1807,1681,1682,1809,1681,1809,1808,1682,1683,1810,1682,1810,1809,1683,1684,1811,1683,1811,1810,1684,1685,1812,1684,1812,1811,1685,1686,1813,1685,1813,1812,1686,1687,1814,1686,1814,1813,1687,1688,1815,1687,1815,1814,1688,1689,1816,1688,1816,1815,1689,1690,1817,1689,1817,1816,1690,1691,1818,1690,1818,1817,1691,1692,1819,1691,1819,1818,1692,1693,1820,1692,1820,1819,1693,1694,1821,1693,1821,1820,1694,1695,1822,1694,1822,1821,1695,1696,1823,1695,1823,1822,1696,1697,1824,1696,1824,1823,1697,1698,1825,1697,1825,1824,1698,1699,1826,1698,1826,1825,1699,1700,1827,1699,1827,1826,1700,1701,1828,1700,1828,1827,1701,1702,1829,1701,1829,1828,1702,1703,1830,1702,1830,1829,1703,1704,1831,1703,1831,1830,1704,1705,1832,1704,1832,1831,1705,1706,1833,1705,1833,1832,1706,1707,1834,1706,1834,1833,1707,1708,1835,1707,1835,1834,1708,1709,1836,1708,1836,1835,1709,1710,1837,1709,1837,1836,1710,1711,1838,1710,1838,1837,1711,1712,1839,1711,1839,1838,1712,1713,1840,1712,1840,1839,1713,1714,1841,1713,1841,1840,1714,1715,1842,1714,1842,1841,1715,1716,1843,1715,1843,1842,1716,1717,1844,1716,1844,1843,1717,1718,1845,1717,1845,1844,1718,1719,1846,1718,1846,1845,1719,1720,1847,1719,1847,1846,1720,1721,1848,1720,1848,1847,1721,1722,1849,1721,1849,1848,1722,1723,1850,1722,1850,1849,1723,1724,1851,1723,1851,1850,1724,1725,1852,1724,1852,1851,1725,1726,1853,1725,1853,1852,1726,1727,1854,1726,1854,1853,1727,1728,1855,1727,1855,1854,1728,1729,1856,1728,1856,1855,1729,1730,1857,1729,1857,1856,1730,1731,1858,1730,1858,1857,1731,1732,1859,1731,1859,1858,1732,1733,1860,1732,1860,1859,1733,1734,1861,1733,1861,1860,1734,1735,1862,1734,1862,1861,1735,1736,1863,1735,1863,1862,1736,1737,1864,1736,1864,1863,1737,1738,1865,1737,1865,1864,1738,1739,1866,1738,1866,1865,1739,1740,1867,1739,1867,1866,1740,1741,1868,1740,1868,1867,1741,1742,1869,1741,1869,1868,1742,1743,1870,1742,1870,1869,1743,1744,1871,1743,1871,1870,1744,1745,1872,1744,1872,1871,1745,1746,1873,1745,1873,1872,1746,1747,1874,1746,1874,1873,1747,1748,1875,1747,1875,1874,1748,1749,1876,1748,1876,1875,1749,1750,1877,1749,1877,1876,1750,1751,1878,1750,1878,1877,1751,1752,1879,1751,1879,1878,1752,1753,1880,1752,1880,1879,1753,1754,1881,1753,1881,1880,1754,1755,1882,1754,1882,1881,1755,1756,1883,1755,1883,1882,1756,1757,1884,1756,1884,1883,1757,1758,1885,1757,1885,1884,1758,1759,1886,1758,1886,1885,1759,1760,1887,1759,1887,1886,1760,1761,1888,1760,1888,1887,1761,1762,1889,1761,1889,1888,1762,1763,1890,1762,1890,1889,1763,1764,1891,1763,1891,1890,1764,1765,1892,1764,1892,1891,1765,1766,1893,1765,1893,1892,1766,1767,1894,1766,1894,1893,1767,1768,1895,1767,1895,1894,1768,1769,1896,1768,1896,1895,1769,1770,1897,1769,1897,1896,1770,1771,1898,1770,1898,1897,1771,1772,1899,1771,1899,1898,1772,1773,1900,1772,1900,1899,1773,1774,1901,1773,1901,1900,1774,1775,1902,1774,1902,1901,1775,1776,1903,1775,1903,1902,1776,1777,1904,1776,1904,1903,1778,1779,1906,1778,1906,1905,1779,1780,1907,1779,1907,1906,1780,1781,1908,1780,1908,1907,1781,1782,1909,1781,1909,1908,1782,1783,1910,1782,1910,1909,1783,1784,1911,1783,1911,1910,1784,1785,1912,1784,1912,1911,1785,1786,1913,1785,1913,1912,1786,1787,1914,1786,1914,1913,1787,1788,1915,1787,1915,1914,1788,1789,1916,1788,1916,1915,1789,1790,1917,1789,1917,1916,1790,1791,1918,1790,1918,1917,1791,1792,1919,1791,1919,1918,1792,1793,1920,1792,1920,1919,1793,1794,1921,1793,1921,1920,1794,1795,1922,1794,1922,1921,1795,1796,1923,1795,1923,1922,1796,1797,1924,1796,1924,1923,1797,1798,1925,1797,1925,1924,1798,1799,1926,1798,1926,1925,1799,1800,1927,1799,1927,1926,1800,1801,1928,1800,1928,1927,1801,1802,1929,1801,1929,1928,1802,1803,1930,1802,1930,1929,1803,1804,1931,1803,1931,1930,1804,1805,1932,1804,1932,1931,1805,1806,1933,1805,1933,1932,1806,1807,1934,1806,1934,1933,1807,1808,1935,1807,1935,1934,1808,1809,1936,1808,1936,1935,1809,1810,1937,1809,1937,1936,1810,1811,1938,1810,1938,1937,1811,1812,1939,1811,1939,1938,1812,1813,1940,1812,1940,1939,1813,1814,1941,1813,1941,1940,1814,1815,1942,1814,1942,1941,1815,1816,1943,1815,1943,1942,1816,1817,1944,1816,1944,1943,1817,1818,1945,1817,1945,1944,1818,1819,1946,1818,1946,1945,1819,1820,1947,1819,1947,1946,1820,1821,1948,1820,1948,1947,1821,1822,1949,1821,1949,1948,1822,1823,1950,1822,1950,1949,1823,1824,1951,1823,1951,1950,1824,1825,1952,1824,1952,1951,1825,1826,1953,1825,1953,1952,1826,1827,1954,1826,1954,1953,1827,1828,1955,1827,1955,1954,1828,1829,1956,1828,1956,1955,1829,1830,1957,1829,1957,1956,1830,1831,1958,1830,1958,1957,1831,1832,1959,1831,1959,1958,1832,1833,1960,1832,1960,1959,1833,1834,1961,1833,1961,1960,1834,1835,1962,1834,1962,1961,1835,1836,1963,1835,1963,1962,1836,1837,1964,1836,1964,1963,1837,1838,1965,1837,1965,1964,1838,1839,1966,1838,1966,1965,1839,1840,1967,1839,1967,1966,1840,1841,1968,1840,1968,1967,1841,1842,1969,1841,1969,1968,1842,1843,1970,1842,1970,1969,1843,1844,1971,1843,1971,1970,1844,1845,1972,1844,1972,1971,1845,1846,1973,1845,1973,1972,1846,1847,1974,1846,1974,1973,1847,1848,1975,1847,1975,1974,1848,1849,1976,1848,1976,1975,1849,1850,1977,1849,1977,1976,1850,1851,1978,1850,1978,1977,1851,1852,1979,1851,1979,1978,1852,1853,1980,1852,1980,1979,1853,1854,1981,1853,1981,1980,1854,1855,1982,1854,1982,1981,1855,1856,1983,1855,1983,1982,1856,1857,1984,1856,1984,1983,1857,1858,1985,1857,1985,1984,1858,1859,1986,1858,1986,1985,1859,1860,1987,1859,1987,1986,1860,1861,1988,1860,1988,1987,1861,1862,1989,1861,1989,1988,1862,1863,1990,1862,1990,1989,1863,1864,1991,1863,1991,1990,1864,1865,1992,1864,1992,1991,1865,1866,1993,1865,1993,1992,1866,1867,1994,1866,1994,1993,1867,1868,1995,1867,1995,1994,1868,1869,1996,1868,1996,1995,1869,1870,1997,1869,1997,1996,1870,1871,1998,1870,1998,1997,1871,1872,1999,1871,1999,1998,1872,1873,2000,1872,2000,1999,1873,1874,2001,1873,2001,2000,1874,1875,2002,1874,2002,2001,1875,1876,2003,1875,2003,2002,1876,1877,2004,1876,2004,2003,1877,1878,2005,1877,2005,2004,1878,1879,2006,1878,2006,2005,1879,1880,2007,1879,2007,2006,1880,1881,2008,1880,2008,2007,1881,1882,2009,1881,2009,2008,1882,1883,2010,1882,2010,2009,1883,1884,2011,1883,2011,2010,1884,1885,2012,1884,2012,2011,1885,1886,2013,1885,2013,2012,1886,1887,2014,1886,2014,2013,1887,1888,2015,1887,2015,2014,1888,1889,2016,1888,2016,2015,1889,1890,2017,1889,2017,2016,1890,1891,2018,1890,2018,2017,1891,1892,2019,1891,2019,2018,1892,1893,2020,1892,2020,2019,1893,1894,2021,1893,2021,2020,1894,1895,2022,1894,2022,2021,1895,1896,2023,1895,2023,2022,1896,1897,2024,1896,2024,2023,1897,1898,2025,1897,2025,2024,1898,1899,2026,1898,2026,2025,1899,1900,2027,1899,2027,2026,1900,1901,2028,1900,2028,2027,1901,1902,2029,1901,2029,2028,1902,1903,2030,1902,2030,2029,1903,1904,2031,1903,2031,2030,1905,1906,2033,1905,2033,2032,1906,1907,2034,1906,2034,2033,1907,1908,2035,1907,2035,2034,1908,1909,2036,1908,2036,2035,1909,1910,2037,1909,2037,2036,1910,1911,2038,1910,2038,2037,1911,1912,2039,1911,2039,2038,1912,1913,2040,1912,2040,2039,1913,1914,2041,1913,2041,2040,1914,1915,2042,1914,2042,2041,1915,1916,2043,1915,2043,2042,1916,1917,2044,1916,2044,2043,1917,1918,2045,1917,2045,2044,1918,1919,2046,1918,2046,2045,1919,1920,2047,1919,2047,2046,1920,1921,2048,1920,2048,2047,1921,1922,2049,1921,2049,2048,1922,1923,2050,1922,2050,2049,1923,1924,2051,1923,2051,2050,1924,1925,2052,1924,2052,2051,1925,1926,2053,1925,2053,2052,1926,1927,2054,1926,2054,2053,1927,1928,2055,1927,2055,2054,1928,1929,2056,1928,2056,2055,1929,1930,2057,1929,2057,2056,1930,1931,2058,1930,2058,2057,1931,1932,2059,1931,2059,2058,1932,1933,2060,1932,2060,2059,1933,1934,2061,1933,2061,2060,1934,1935,2062,1934,2062,2061,1935,1936,2063,1935,2063,2062,1936,1937,2064,1936,2064,2063,1937,1938,2065,1937,2065,2064,1938,1939,2066,1938,2066,2065,1939,1940,2067,1939,2067,2066,1940,1941,2068,1940,2068,2067,1941,1942,2069,1941,2069,2068,1942,1943,2070,1942,2070,2069,1943,1944,2071,1943,2071,2070,1944,1945,2072,1944,2072,2071,1945,1946,2073,1945,2073,2072,1946,1947,2074,1946,2074,2073,1947,1948,2075,1947,2075,2074,1948,1949,2076,1948,2076,2075,1949,1950,2077,1949,2077,2076,1950,1951,2078,1950,2078,2077,1951,1952,2079,1951,2079,2078,1952,1953,2080,1952,2080,2079,1953,1954,2081,1953,2081,2080,1954,1955,2082,1954,2082,2081,1955,1956,2083,1955,2083,2082,1956,1957,2084,1956,2084,2083,1957,1958,2085,1957,2085,2084,1958,1959,2086,1958,2086,2085,1959,1960,2087,1959,2087,2086,1960,1961,2088,1960,2088,2087,1961,1962,2089,1961,2089,2088,1962,1963,2090,1962,2090,2089,1963,1964,2091,1963,2091,2090,1964,1965,2092,1964,2092,2091,1965,1966,2093,1965,2093,2092,1966,1967,2094,1966,2094,2093,1967,1968,2095,1967,2095,2094,1968,1969,2096,1968,2096,2095,1969,1970,2097,1969,2097,2096,1970,1971,2098,1970,2098,2097,1971,1972,2099,1971,2099,2098,1972,1973,2100,1972,2100,2099,1973,1974,2101,1973,2101,2100,1974,1975,2102,1974,2102,2101,1975,1976,2103,1975,2103,2102,1976,1977,2104,1976,2104,2103,1977,1978,2105,1977,2105,2104,1978,1979,2106,1978,2106,2105,1979,1980,2107,1979,2107,2106,1980,1981,2108,1980,2108,2107,1981,1982,2109,1981,2109,2108,1982,1983,2110,1982,2110,2109,1983,1984,2111,1983,2111,2110,1984,1985,2112,1984,2112,2111,1985,1986,2113,1985,2113,2112,1986,1987,2114,1986,2114,2113,1987,1988,2115,1987,2115,2114,1988,1989,2116,1988,2116,2115,1989,1990,2117,1989,2117,2116,1990,1991,2118,1990,2118,2117,1991,1992,2119,1991,2119,2118,1992,1993,2120,1992,2120,2119,1993,1994,2121,1993,2121,2120,1994,1995,2122,1994,2122,2121,1995,1996,2123,1995,2123,2122,1996,1997,2124,1996,2124,2123,1997,1998,2125,1997,2125,2124,1998,1999,2126,1998,2126,2125,1999,2000,2127,1999,2127,2126,2000,2001,2128,2000,2128,2127,2001,2002,2129,2001,2129,2128,2002,2003,2130,2002,2130,2129,2003,2004,2131,2003,2131,2130,2004,2005,2132,2004,2132,2131,2005,2006,2133,2005,2133,2132,2006,2007,2134,2006,2134,2133,2007,2008,2135,2007,2135,2134,2008,2009,2136,2008,2136,2135,2009,2010,2137,2009,2137,2136,2010,2011,2138,2010,2138,2137,2011,2012,2139,2011,2139,2138,2012,2013,2140,2012,2140,2139,2013,2014,2141,2013,2141,2140,2014,2015,2142,2014,2142,2141,2015,2016,2143,2015,2143,2142,2016,2017,2144,2016,2144,2143,2017,2018,2145,2017,2145,2144,2018,2019,2146,2018,2146,2145,2019,2020,2147,2019,2147,2146,2020,2021,2148,2020,2148,2147,2021,2022,2149,2021,2149,2148,2022,2023,2150,2022,2150,2149,2023,2024,2151,2023,2151,2150,2024,2025,2152,2024,2152,2151,2025,2026,2153,2025,2153,2152,2026,2027,2154,2026,2154,2153,2027,2028,2155,2027,2155,2154,2028,2029,2156,2028,2156,2155,2029,2030,2157,2029,2157,2156,2030,2031,2158,2030,2158,2157,2032,2033,2160,2032,2160,2159,2033,2034,2161,2033,2161,2160,2034,2035,2162,2034,2162,2161,2035,2036,2163,2035,2163,2162,2036,2037,2164,2036,2164,2163,2037,2038,2165,2037,2165,2164,2038,2039,2166,2038,2166,2165,2039,2040,2167,2039,2167,2166,2040,2041,2168,2040,2168,2167,2041,2042,2169,2041,2169,2168,2042,2043,2170,2042,2170,2169,2043,2044,2171,2043,2171,2170,2044,2045,2172,2044,2172,2171,2045,2046,2173,2045,2173,2172,2046,2047,2174,2046,2174,2173,2047,2048,2175,2047,2175,2174,2048,2049,2176,2048,2176,2175,2049,2050,2177,2049,2177,2176,2050,2051,2178,2050,2178,2177,2051,2052,2179,2051,2179,2178,2052,2053,2180,2052,2180,2179,2053,2054,2181,2053,2181,2180,2054,2055,2182,2054,2182,2181,2055,2056,2183,2055,2183,2182,2056,2057,2184,2056,2184,2183,2057,2058,2185,2057,2185,2184,2058,2059,2186,2058,2186,2185,2059,2060,2187,2059,2187,2186,2060,2061,2188,2060,2188,2187,2061,2062,2189,2061,2189,2188,2062,2063,2190,2062,2190,2189,2063,2064,2191,2063,2191,2190,2064,2065,2192,2064,2192,2191,2065,2066,2193,2065,2193,2192,2066,2067,2194,2066,2194,2193,2067,2068,2195,2067,2195,2194,2068,2069,2196,2068,2196,2195,2069,2070,2197,2069,2197,2196,2070,2071,2198,2070,2198,2197,2071,2072,2199,2071,2199,2198,2072,2073,2200,2072,2200,2199,2073,2074,2201,2073,2201,2200,2074,2075,2202,2074,2202,2201,2075,2076,2203,2075,2203,2202,2076,2077,2204,2076,2204,2203,2077,2078,2205,2077,2205,2204,2078,2079,2206,2078,2206,2205,2079,2080,2207,2079,2207,2206,2080,2081,2208,2080,2208,2207,2081,2082,2209,2081,2209,2208,2082,2083,2210,2082,2210,2209,2083,2084,2211,2083,2211,2210,2084,2085,2212,2084,2212,2211,2085,2086,2213,2085,2213,2212,2086,2087,2214,2086,2214,2213,2087,2088,2215,2087,2215,2214,2088,2089,2216,2088,2216,2215,2089,2090,2217,2089,2217,2216,2090,2091,2218,2090,2218,2217,2091,2092,2219,2091,2219,2218,2092,2093,2220,2092,2220,2219,2093,2094,2221,2093,2221,2220,2094,2095,2222,2094,2222,2221,2095,2096,2223,2095,2223,2222,2096,2097,2224,2096,2224,2223,2097,2098,2225,2097,2225,2224,2098,2099,2226,2098,2226,2225,2099,2100,2227,2099,2227,2226,2100,2101,2228,2100,2228,2227,2101,2102,2229,2101,2229,2228,2102,2103,2230,2102,2230,2229,2103,2104,2231,2103,2231,2230,2104,2105,2232,2104,2232,2231,2105,2106,2233,2105,2233,2232,2106,2107,2234,2106,2234,2233,2107,2108,2235,2107,2235,2234,2108,2109,2236,2108,2236,2235,2109,2110,2237,2109,2237,2236,2110,2111,2238,2110,2238,2237,2111,2112,2239,2111,2239,2238,2112,2113,2240,2112,2240,2239,2113,2114,2241,2113,2241,2240,2114,2115,2242,2114,2242,2241,2115,2116,2243,2115,2243,2242,2116,2117,2244,2116,2244,2243,2117,2118,2245,2117,2245,2244,2118,2119,2246,2118,2246,2245,2119,2120,2247,2119,2247,2246,2120,2121,2248,2120,2248,2247,2121,2122,2249,2121,2249,2248,2122,2123,2250,2122,2250,2249,2123,2124,2251,2123,2251,2250,2124,2125,2252,2124,2252,2251,2125,2126,2253,2125,2253,2252,2126,2127,2254,2126,2254,2253,2127,2128,2255,2127,2255,2254,2128,2129,2256,2128,2256,2255,2129,2130,2257,2129,2257,2256,2130,2131,2258,2130,2258,2257,2131,2132,2259,2131,2259,2258,2132,2133,2260,2132,2260,2259,2133,2134,2261,2133,2261,2260,2134,2135,2262,2134,2262,2261,2135,2136,2263,2135,2263,2262,2136,2137,2264,2136,2264,2263,2137,2138,2265,2137,2265,2264,2138,2139,2266,2138,2266,2265,2139,2140,2267,2139,2267,2266,2140,2141,2268,2140,2268,2267,2141,2142,2269,2141,2269,2268,2142,2143,2270,2142,2270,2269,2143,2144,2271,2143,2271,2270,2144,2145,2272,2144,2272,2271,2145,2146,2273,2145,2273,2272,2146,2147,2274,2146,2274,2273,2147,2148,2275,2147,2275,2274,2148,2149,2276,2148,2276,2275,2149,2150,2277,2149,2277,2276,2150,2151,2278,2150,2278,2277,2151,2152,2279,2151,2279,2278,2152,2153,2280,2152,2280,2279,2153,2154,2281,2153,2281,2280,2154,2155,2282,2154,2282,2281,2155,2156,2283,2155,2283,2282,2156,2157,2284,2156,2284,2283,2157,2158,2285,2157,2285,2284,2159,2160,2287,2159,2287,2286,2160,2161,2288,2160,2288,2287,2161,2162,2289,2161,2289,2288,2162,2163,2290,2162,2290,2289,2163,2164,2291,2163,2291,2290,2164,2165,2292,2164,2292,2291,2165,2166,2293,2165,2293,2292,2166,2167,2294,2166,2294,2293,2167,2168,2295,2167,2295,2294,2168,2169,2296,2168,2296,2295,2169,2170,2297,2169,2297,2296,2170,2171,2298,2170,2298,2297,2171,2172,2299,2171,2299,2298,2172,2173,2300,2172,2300,2299,2173,2174,2301,2173,2301,2300,2174,2175,2302,2174,2302,2301,2175,2176,2303,2175,2303,2302,2176,2177,2304,2176,2304,2303,2177,2178,2305,2177,2305,2304,2178,2179,2306,2178,2306,2305,2179,2180,2307,2179,2307,2306,2180,2181,2308,2180,2308,2307,2181,2182,2309,2181,2309,2308,2182,2183,2310,2182,2310,2309,2183,2184,2311,2183,2311,2310,2184,2185,2312,2184,2312,2311,2185,2186,2313,2185,2313,2312,2186,2187,2314,2186,2314,2313,2187,2188,2315,2187,2315,2314,2188,2189,2316,2188,2316,2315,2189,2190,2317,2189,2317,2316,2190,2191,2318,2190,2318,2317,2191,2192,2319,2191,2319,2318,2192,2193,2320,2192,2320,2319,2193,2194,2321,2193,2321,2320,2194,2195,2322,2194,2322,2321,2195,2196,2323,2195,2323,2322,2196,2197,2324,2196,2324,2323,2197,2198,2325,2197,2325,2324,2198,2199,2326,2198,2326,2325,2199,2200,2327,2199,2327,2326,2200,2201,2328,2200,2328,2327,2201,2202,2329,2201,2329,2328,2202,2203,2330,2202,2330,2329,2203,2204,2331,2203,2331,2330,2204,2205,2332,2204,2332,2331,2205,2206,2333,2205,2333,2332,2206,2207,2334,2206,2334,2333,2207,2208,2335,2207,2335,2334,2208,2209,2336,2208,2336,2335,2209,2210,2337,2209,2337,2336,2210,2211,2338,2210,2338,2337,2211,2212,2339,2211,2339,2338,2212,2213,2340,2212,2340,2339,2213,2214,2341,2213,2341,2340,2214,2215,2342,2214,2342,2341,2215,2216,2343,2215,2343,2342,2216,2217,2344,2216,2344,2343,2217,2218,2345,2217,2345,2344,2218,2219,2346,2218,2346,2345,2219,2220,2347,2219,2347,2346,2220,2221,2348,2220,2348,2347,2221,2222,2349,2221,2349,2348,2222,2223,2350,2222,2350,2349,2223,2224,2351,2223,2351,2350,2224,2225,2352,2224,2352,2351,2225,2226,2353,2225,2353,2352,2226,2227,2354,2226,2354,2353,2227,2228,2355,2227,2355,2354,2228,2229,2356,2228,2356,2355,2229,2230,2357,2229,2357,2356,2230,2231,2358,2230,2358,2357,2231,2232,2359,2231,2359,2358,2232,2233,2360,2232,2360,2359,2233,2234,2361,2233,2361,2360,2234,2235,2362,2234,2362,2361,2235,2236,2363,2235,2363,2362,2236,2237,2364,2236,2364,2363,2237,2238,2365,2237,2365,2364,2238,2239,2366,2238,2366,2365,2239,2240,2367,2239,2367,2366,2240,2241,2368,2240,2368,2367,2241,2242,2369,2241,2369,2368,2242,2243,2370,2242,2370,2369,2243,2244,2371,2243,2371,2370,2244,2245,2372,2244,2372,2371,2245,2246,2373,2245,2373,2372,2246,2247,2374,2246,2374,2373,2247,2248,2375,2247,2375,2374,2248,2249,2376,2248,2376,2375,2249,2250,2377,2249,2377,2376,2250,2251,2378,2250,2378,2377,2251,2252,2379,2251,2379,2378,2252,2253,2380,2252,2380,2379,2253,2254,2381,2253,2381,2380,2254,2255,2382,2254,2382,2381,2255,2256,2383,2255,2383,2382,2256,2257,2384,2256,2384,2383,2257,2258,2385,2257,2385,2384,2258,2259,2386,2258,2386,2385,2259,2260,2387,2259,2387,2386,2260,2261,2388,2260,2388,2387,2261,2262,2389,2261,2389,2388,2262,2263,2390,2262,2390,2389,2263,2264,2391,2263,2391,2390,2264,2265,2392,2264,2392,2391,2265,2266,2393,2265,2393,2392,2266,2267,2394,2266,2394,2393,2267,2268,2395,2267,2395,2394,2268,2269,2396,2268,2396,2395,2269,2270,2397,2269,2397,2396,2270,2271,2398,2270,2398,2397,2271,2272,2399,2271,2399,2398,2272,2273,2400,2272,2400,2399,2273,2274,2401,2273,2401,2400,2274,2275,2402,2274,2402,2401,2275,2276,2403,2275,2403,2402,2276,2277,2404,2276,2404,2403,2277,2278,2405,2277,2405,2404,2278,2279,2406,2278,2406,2405,2279,2280,2407,2279,2407,2406,2280,2281,2408,2280,2408,2407,2281,2282,2409,2281,2409,2408,2282,2283,2410,2282,2410,2409,2283,2284,2411,2283,2411,2410,2284,2285,2412,2284,2412,2411,2286,2287,2414,2286,2414,2413,2287,2288,2415,2287,2415,2414,2288,2289,2416,2288,2416,2415,2289,2290,2417,2289,2417,2416,2290,2291,2418,2290,2418,2417,2291,2292,2419,2291,2419,2418,2292,2293,2420,2292,2420,2419,2293,2294,2421,2293,2421,2420,2294,2295,2422,2294,2422,2421,2295,2296,2423,2295,2423,2422,2296,2297,2424,2296,2424,2423,2297,2298,2425,2297,2425,2424,2298,2299,2426,2298,2426,2425,2299,2300,2427,2299,2427,2426,2300,2301,2428,2300,2428,2427,2301,2302,2429,2301,2429,2428,2302,2303,2430,2302,2430,2429,2303,2304,2431,2303,2431,2430,2304,2305,2432,2304,2432,2431,2305,2306,2433,2305,2433,2432,2306,2307,2434,2306,2434,2433,2307,2308,2435,2307,2435,2434,2308,2309,2436,2308,2436,2435,2309,2310,2437,2309,2437,2436,2310,2311,2438,2310,2438,2437,2311,2312,2439,2311,2439,2438,2312,2313,2440,2312,2440,2439,2313,2314,2441,2313,2441,2440,2314,2315,2442,2314,2442,2441,2315,2316,2443,2315,2443,2442,2316,2317,2444,2316,2444,2443,2317,2318,2445,2317,2445,2444,2318,2319,2446,2318,2446,2445,2319,2320,2447,2319,2447,2446,2320,2321,2448,2320,2448,2447,2321,2322,2449,2321,2449,2448,2322,2323,2450,2322,2450,2449,2323,2324,2451,2323,2451,2450,2324,2325,2452,2324,2452,2451,2325,2326,2453,2325,2453,2452,2326,2327,2454,2326,2454,2453,2327,2328,2455,2327,2455,2454,2328,2329,2456,2328,2456,2455,2329,2330,2457,2329,2457,2456,2330,2331,2458,2330,2458,2457,2331,2332,2459,2331,2459,2458,2332,2333,2460,2332,2460,2459,2333,2334,2461,2333,2461,2460,2334,2335,2462,2334,2462,2461,2335,2336,2463,2335,2463,2462,2336,2337,2464,2336,2464,2463,2337,2338,2465,2337,2465,2464,2338,2339,2466,2338,2466,2465,2339,2340,2467,2339,2467,2466,2340,2341,2468,2340,2468,2467,2341,2342,2469,2341,2469,2468,2342,2343,2470,2342,2470,2469,2343,2344,2471,2343,2471,2470,2344,2345,2472,2344,2472,2471,2345,2346,2473,2345,2473,2472,2346,2347,2474,2346,2474,2473,2347,2348,2475,2347,2475,2474,2348,2349,2476,2348,2476,2475,2349,2350,2477,2349,2477,2476,2350,2351,2478,2350,2478,2477,2351,2352,2479,2351,2479,2478,2352,2353,2480,2352,2480,2479,2353,2354,2481,2353,2481,2480,2354,2355,2482,2354,2482,2481,2355,2356,2483,2355,2483,2482,2356,2357,2484,2356,2484,2483,2357,2358,2485,2357,2485,2484,2358,2359,2486,2358,2486,2485,2359,2360,2487,2359,2487,2486,2360,2361,2488,2360,2488,2487,2361,2362,2489,2361,2489,2488,2362,2363,2490,2362,2490,2489,2363,2364,2491,2363,2491,2490,2364,2365,2492,2364,2492,2491,2365,2366,2493,2365,2493,2492,2366,2367,2494,2366,2494,2493,2367,2368,2495,2367,2495,2494,2368,2369,2496,2368,2496,2495,2369,2370,2497,2369,2497,2496,2370,2371,2498,2370,2498,2497,2371,2372,2499,2371,2499,2498,2372,2373,2500,2372,2500,2499,2373,2374,2501,2373,2501,2500,2374,2375,2502,2374,2502,2501,2375,2376,2503,2375,2503,2502,2376,2377,2504,2376,2504,2503,2377,2378,2505,2377,2505,2504,2378,2379,2506,2378,2506,2505,2379,2380,2507,2379,2507,2506,2380,2381,2508,2380,2508,2507,2381,2382,2509,2381,2509,2508,2382,2383,2510,2382,2510,2509,2383,2384,2511,2383,2511,2510,2384,2385,2512,2384,2512,2511,2385,2386,2513,2385,2513,2512,2386,2387,2514,2386,2514,2513,2387,2388,2515,2387,2515,2514,2388,2389,2516,2388,2516,2515,2389,2390,2517,2389,2517,2516,2390,2391,2518,2390,2518,2517,2391,2392,2519,2391,2519,2518,2392,2393,2520,2392,2520,2519,2393,2394,2521,2393,2521,2520,2394,2395,2522,2394,2522,2521,2395,2396,2523,2395,2523,2522,2396,2397,2524,2396,2524,2523,2397,2398,2525,2397,2525,2524,2398,2399,2526,2398,2526,2525,2399,2400,2527,2399,2527,2526,2400,2401,2528,2400,2528,2527,2401,2402,2529,2401,2529,2528,2402,2403,2530,2402,2530,2529,2403,2404,2531,2403,2531,2530,2404,2405,2532,2404,2532,2531,2405,2406,2533,2405,2533,2532,2406,2407,2534,2406,2534,2533,2407,2408,2535,2407,2535,2534,2408,2409,2536,2408,2536,2535,2409,2410,2537,2409,2537,2536,2410,2411,2538,2410,2538,2537,2411,2412,2539,2411,2539,2538,2413,2414,2541,2413,2541,2540,2414,2415,2542,2414,2542,2541,2415,2416,2543,2415,2543,2542,2416,2417,2544,2416,2544,2543,2417,2418,2545,2417,2545,2544,2418,2419,2546,2418,2546,2545,2419,2420,2547,2419,2547,2546,2420,2421,2548,2420,2548,2547,2421,2422,2549,2421,2549,2548,2422,2423,2550,2422,2550,2549,2423,2424,2551,2423,2551,2550,2424,2425,2552,2424,2552,2551,2425,2426,2553,2425,2553,2552,2426,2427,2554,2426,2554,2553,2427,2428,2555,2427,2555,2554,2428,2429,2556,2428,2556,2555,2429,2430,2557,2429,2557,2556,2430,2431,2558,2430,2558,2557,2431,2432,2559,2431,2559,2558,2432,2433,2560,2432,2560,2559,2433,2434,2561,2433,2561,2560,2434,2435,2562,2434,2562,2561,2435,2436,2563,2435,2563,2562,2436,2437,2564,2436,2564,2563,2437,2438,2565,2437,2565,2564,2438,2439,2566,2438,2566,2565,2439,2440,2567,2439,2567,2566,2440,2441,2568,2440,2568,2567,2441,2442,2569,2441,2569,2568,2442,2443,2570,2442,2570,2569,2443,2444,2571,2443,2571,2570,2444,2445,2572,2444,2572,2571,2445,2446,2573,2445,2573,2572,2446,2447,2574,2446,2574,2573,2447,2448,2575,2447,2575,2574,2448,2449,2576,2448,2576,2575,2449,2450,2577,2449,2577,2576,2450,2451,2578,2450,2578,2577,2451,2452,2579,2451,2579,2578,2452,2453,2580,2452,2580,2579,2453,2454,2581,2453,2581,2580,2454,2455,2582,2454,2582,2581,2455,2456,2583,2455,2583,2582,2456,2457,2584,2456,2584,2583,2457,2458,2585,2457,2585,2584,2458,2459,2586,2458,2586,2585,2459,2460,2587,2459,2587,2586,2460,2461,2588,2460,2588,2587,2461,2462,2589,2461,2589,2588,2462,2463,2590,2462,2590,2589,2463,2464,2591,2463,2591,2590,2464,2465,2592,2464,2592,2591,2465,2466,2593,2465,2593,2592,2466,2467,2594,2466,2594,2593,2467,2468,2595,2467,2595,2594,2468,2469,2596,2468,2596,2595,2469,2470,2597,2469,2597,2596,2470,2471,2598,2470,2598,2597,2471,2472,2599,2471,2599,2598,2472,2473,2600,2472,2600,2599,2473,2474,2601,2473,2601,2600,2474,2475,2602,2474,2602,2601,2475,2476,2603,2475,2603,2602,2476,2477,2604,2476,2604,2603,2477,2478,2605,2477,2605,2604,2478,2479,2606,2478,2606,2605,2479,2480,2607,2479,2607,2606,2480,2481,2608,2480,2608,2607,2481,2482,2609,2481,2609,2608,2482,2483,2610,2482,2610,2609,2483,2484,2611,2483,2611,2610,2484,2485,2612,2484,2612,2611,2485,2486,2613,2485,2613,2612,2486,2487,2614,2486,2614,2613,2487,2488,2615,2487,2615,2614,2488,2489,2616,2488,2616,2615,2489,2490,2617,2489,2617,2616,2490,2491,2618,2490,2618,2617,2491,2492,2619,2491,2619,2618,2492,2493,2620,2492,2620,2619,2493,2494,2621,2493,2621,2620,2494,2495,2622,2494,2622,2621,2495,2496,2623,2495,2623,2622,2496,2497,2624,2496,2624,2623,2497,2498,2625,2497,2625,2624,2498,2499,2626,2498,2626,2625,2499,2500,2627,2499,2627,2626,2500,2501,2628,2500,2628,2627,2501,2502,2629,2501,2629,2628,2502,2503,2630,2502,2630,2629,2503,2504,2631,2503,2631,2630,2504,2505,2632,2504,2632,2631,2505,2506,2633,2505,2633,2632,2506,2507,2634,2506,2634,2633,2507,2508,2635,2507,2635,2634,2508,2509,2636,2508,2636,2635,2509,2510,2637,2509,2637,2636,2510,2511,2638,2510,2638,2637,2511,2512,2639,2511,2639,2638,2512,2513,2640,2512,2640,2639,2513,2514,2641,2513,2641,2640,2514,2515,2642,2514,2642,2641,2515,2516,2643,2515,2643,2642,2516,2517,2644,2516,2644,2643,2517,2518,2645,2517,2645,2644,2518,2519,2646,2518,2646,2645,2519,2520,2647,2519,2647,2646,2520,2521,2648,2520,2648,2647,2521,2522,2649,2521,2649,2648,2522,2523,2650,2522,2650,2649,2523,2524,2651,2523,2651,2650,2524,2525,2652,2524,2652,2651,2525,2526,2653,2525,2653,2652,2526,2527,2654,2526,2654,2653,2527,2528,2655,2527,2655,2654,2528,2529,2656,2528,2656,2655,2529,2530,2657,2529,2657,2656,2530,2531,2658,2530,2658,2657,2531,2532,2659,2531,2659,2658,2532,2533,2660,2532,2660,2659,2533,2534,2661,2533,2661,2660,2534,2535,2662,2534,2662,2661,2535,2536,2663,2535,2663,2662,2536,2537,2664,2536,2664,2663,2537,2538,2665,2537,2665,2664,2538,2539,2666,2538,2666,2665,2540,2541,2668,2540,2668,2667,2541,2542,2669,2541,2669,2668,2542,2543,2670,2542,2670,2669,2543,2544,2671,2543,2671,2670,2544,2545,2672,2544,2672,2671,2545,2546,2673,2545,2673,2672,2546,2547,2674,2546,2674,2673,2547,2548,2675,2547,2675,2674,2548,2549,2676,2548,2676,2675,2549,2550,2677,2549,2677,2676,2550,2551,2678,2550,2678,2677,2551,2552,2679,2551,2679,2678,2552,2553,2680,2552,2680,2679,2553,2554,2681,2553,2681,2680,2554,2555,2682,2554,2682,2681,2555,2556,2683,2555,2683,2682,2556,2557,2684,2556,2684,2683,2557,2558,2685,2557,2685,2684,2558,2559,2686,2558,2686,2685,2559,2560,2687,2559,2687,2686,2560,2561,2688,2560,2688,2687,2561,2562,2689,2561,2689,2688,2562,2563,2690,2562,2690,2689,2563,2564,2691,2563,2691,2690,2564,2565,2692,2564,2692,2691,2565,2566,2693,2565,2693,2692,2566,2567,2694,2566,2694,2693,2567,2568,2695,2567,2695,2694,2568,2569,2696,2568,2696,2695,2569,2570,2697,2569,2697,2696,2570,2571,2698,2570,2698,2697,2571,2572,2699,2571,2699,2698,2572,2573,2700,2572,2700,2699,2573,2574,2701,2573,2701,2700,2574,2575,2702,2574,2702,2701,2575,2576,2703,2575,2703,2702,2576,2577,2704,2576,2704,2703,2577,2578,2705,2577,2705,2704,2578,2579,2706,2578,2706,2705,2579,2580,2707,2579,2707,2706,2580,2581,2708,2580,2708,2707,2581,2582,2709,2581,2709,2708,2582,2583,2710,2582,2710,2709,2583,2584,2711,2583,2711,2710,2584,2585,2712,2584,2712,2711,2585,2586,2713,2585,2713,2712,2586,2587,2714,2586,2714,2713,2587,2588,2715,2587,2715,2714,2588,2589,2716,2588,2716,2715,2589,2590,2717,2589,2717,2716,2590,2591,2718,2590,2718,2717,2591,2592,2719,2591,2719,2718,2592,2593,2720,2592,2720,2719,2593,2594,2721,2593,2721,2720,2594,2595,2722,2594,2722,2721,2595,2596,2723,2595,2723,2722,2596,2597,2724,2596,2724,2723,2597,2598,2725,2597,2725,2724,2598,2599,2726,2598,2726,2725,2599,2600,2727,2599,2727,2726,2600,2601,2728,2600,2728,2727,2601,2602,2729,2601,2729,2728,2602,2603,2730,2602,2730,2729,2603,2604,2731,2603,2731,2730,2604,2605,2732,2604,2732,2731,2605,2606,2733,2605,2733,2732,2606,2607,2734,2606,2734,2733,2607,2608,2735,2607,2735,2734,2608,2609,2736,2608,2736,2735,2609,2610,2737,2609,2737,2736,2610,2611,2738,2610,2738,2737,2611,2612,2739,2611,2739,2738,2612,2613,2740,2612,2740,2739,2613,2614,2741,2613,2741,2740,2614,2615,2742,2614,2742,2741,2615,2616,2743,2615,2743,2742,2616,2617,2744,2616,2744,2743,2617,2618,2745,2617,2745,2744,2618,2619,2746,2618,2746,2745,2619,2620,2747,2619,2747,2746,2620,2621,2748,2620,2748,2747,2621,2622,2749,2621,2749,2748,2622,2623,2750,2622,2750,2749,2623,2624,2751,2623,2751,2750,2624,2625,2752,2624,2752,2751,2625,2626,2753,2625,2753,2752,2626,2627,2754,2626,2754,2753,2627,2628,2755,2627,2755,2754,2628,2629,2756,2628,2756,2755,2629,2630,2757,2629,2757,2756,2630,2631,2758,2630,2758,2757,2631,2632,2759,2631,2759,2758,2632,2633,2760,2632,2760,2759,2633,2634,2761,2633,2761,2760,2634,2635,2762,2634,2762,2761,2635,2636,2763,2635,2763,2762,2636,2637,2764,2636,2764,2763,2637,2638,2765,2637,2765,2764,2638,2639,2766,2638,2766,2765,2639,2640,2767,2639,2767,2766,2640,2641,2768,2640,2768,2767,2641,2642,2769,2641,2769,2768,2642,2643,2770,2642,2770,2769,2643,2644,2771,2643,2771,2770,2644,2645,2772,2644,2772,2771,2645,2646,2773,2645,2773,2772,2646,2647,2774,2646,2774,2773,2647,2648,2775,2647,2775,2774,2648,2649,2776,2648,2776,2775,2649,2650,2777,2649,2777,2776,2650,2651,2778,2650,2778,2777,2651,2652,2779,2651,2779,2778,2652,2653,2780,2652,2780,2779,2653,2654,2781,2653,2781,2780,2654,2655,2782,2654,2782,2781,2655,2656,2783,2655,2783,2782,2656,2657,2784,2656,2784,2783,2657,2658,2785,2657,2785,2784,2658,2659,2786,2658,2786,2785,2659,2660,2787,2659,2787,2786,2660,2661,2788,2660,2788,2787,2661,2662,2789,2661,2789,2788,2662,2663,2790,2662,2790,2789,2663,2664,2791,2663,2791,2790,2664,2665,2792,2664,2792,2791,2665,2666,2793,2665,2793,2792,2667,2668,2795,2667,2795,2794,2668,2669,2796,2668,2796,2795,2669,2670,2797,2669,2797,2796,2670,2671,2798,2670,2798,2797,2671,2672,2799,2671,2799,2798,2672,2673,2800,2672,2800,2799,2673,2674,2801,2673,2801,2800,2674,2675,2802,2674,2802,2801,2675,2676,2803,2675,2803,2802,2676,2677,2804,2676,2804,2803,2677,2678,2805,2677,2805,2804,2678,2679,2806,2678,2806,2805,2679,2680,2807,2679,2807,2806,2680,2681,2808,2680,2808,2807,2681,2682,2809,2681,2809,2808,2682,2683,2810,2682,2810,2809,2683,2684,2811,2683,2811,2810,2684,2685,2812,2684,2812,2811,2685,2686,2813,2685,2813,2812,2686,2687,2814,2686,2814,2813,2687,2688,2815,2687,2815,2814,2688,2689,2816,2688,2816,2815,2689,2690,2817,2689,2817,2816,2690,2691,2818,2690,2818,2817,2691,2692,2819,2691,2819,2818,2692,2693,2820,2692,2820,2819,2693,2694,2821,2693,2821,2820,2694,2695,2822,2694,2822,2821,2695,2696,2823,2695,2823,2822,2696,2697,2824,2696,2824,2823,2697,2698,2825,2697,2825,2824,2698,2699,2826,2698,2826,2825,2699,2700,2827,2699,2827,2826,2700,2701,2828,2700,2828,2827,2701,2702,2829,2701,2829,2828,2702,2703,2830,2702,2830,2829,2703,2704,2831,2703,2831,2830,2704,2705,2832,2704,2832,2831,2705,2706,2833,2705,2833,2832,2706,2707,2834,2706,2834,2833,2707,2708,2835,2707,2835,2834,2708,2709,2836,2708,2836,2835,2709,2710,2837,2709,2837,2836,2710,2711,2838,2710,2838,2837,2711,2712,2839,2711,2839,2838,2712,2713,2840,2712,2840,2839,2713,2714,2841,2713,2841,2840,2714,2715,2842,2714,2842,2841,2715,2716,2843,2715,2843,2842,2716,2717,2844,2716,2844,2843,2717,2718,2845,2717,2845,2844,2718,2719,2846,2718,2846,2845,2719,2720,2847,2719,2847,2846,2720,2721,2848,2720,2848,2847,2721,2722,2849,2721,2849,2848,2722,2723,2850,2722,2850,2849,2723,2724,2851,2723,2851,2850,2724,2725,2852,2724,2852,2851,2725,2726,2853,2725,2853,2852,2726,2727,2854,2726,2854,2853,2727,2728,2855,2727,2855,2854,2728,2729,2856,2728,2856,2855,2729,2730,2857,2729,2857,2856,2730,2731,2858,2730,2858,2857,2731,2732,2859,2731,2859,2858,2732,2733,2860,2732,2860,2859,2733,2734,2861,2733,2861,2860,2734,2735,2862,2734,2862,2861,2735,2736,2863,2735,2863,2862,2736,2737,2864,2736,2864,2863,2737,2738,2865,2737,2865,2864,2738,2739,2866,2738,2866,2865,2739,2740,2867,2739,2867,2866,2740,2741,2868,2740,2868,2867,2741,2742,2869,2741,2869,2868,2742,2743,2870,2742,2870,2869,2743,2744,2871,2743,2871,2870,2744,2745,2872,2744,2872,2871,2745,2746,2873,2745,2873,2872,2746,2747,2874,2746,2874,2873,2747,2748,2875,2747,2875,2874,2748,2749,2876,2748,2876,2875,2749,2750,2877,2749,2877,2876,2750,2751,2878,2750,2878,2877,2751,2752,2879,2751,2879,2878,2752,2753,2880,2752,2880,2879,2753,2754,2881,2753,2881,2880,2754,2755,2882,2754,2882,2881,2755,2756,2883,2755,2883,2882,2756,2757,2884,2756,2884,2883,2757,2758,2885,2757,2885,2884,2758,2759,2886,2758,2886,2885,2759,2760,2887,2759,2887,2886,2760,2761,2888,2760,2888,2887,2761,2762,2889,2761,2889,2888,2762,2763,2890,2762,2890,2889,2763,2764,2891,2763,2891,2890,2764,2765,2892,2764,2892,2891,2765,2766,2893,2765,2893,2892,2766,2767,2894,2766,2894,2893,2767,2768,2895,2767,2895,2894,2768,2769,2896,2768,2896,2895,2769,2770,2897,2769,2897,2896,2770,2771,2898,2770,2898,2897,2771,2772,2899,2771,2899,2898,2772,2773,2900,2772,2900,2899,2773,2774,2901,2773,2901,2900,2774,2775,2902,2774,2902,2901,2775,2776,2903,2775,2903,2902,2776,2777,2904,2776,2904,2903,2777,2778,2905,2777,2905,2904,2778,2779,2906,2778,2906,2905,2779,2780,2907,2779,2907,2906,2780,2781,2908,2780,2908,2907,2781,2782,2909,2781,2909,2908,2782,2783,2910,2782,2910,2909,2783,2784,2911,2783,2911,2910,2784,2785,2912,2784,2912,2911,2785,2786,2913,2785,2913,2912,2786,2787,2914,2786,2914,2913,2787,2788,2915,2787,2915,2914,2788,2789,2916,2788,2916,2915,2789,2790,2917,2789,2917,2916,2790,2791,2918,2790,2918,2917,2791,2792,2919,2791,2919,2918,2792,2793,2920,2792,2920,2919,2794,2795,2922,2794,2922,2921,2795,2796,2923,2795,2923,2922,2796,2797,2924,2796,2924,2923,2797,2798,2925,2797,2925,2924,2798,2799,2926,2798,2926,2925,2799,2800,2927,2799,2927,2926,2800,2801,2928,2800,2928,2927,2801,2802,2929,2801,2929,2928,2802,2803,2930,2802,2930,2929,2803,2804,2931,2803,2931,2930,2804,2805,2932,2804,2932,2931,2805,2806,2933,2805,2933,2932,2806,2807,2934,2806,2934,2933,2807,2808,2935,2807,2935,2934,2808,2809,2936,2808,2936,2935,2809,2810,2937,2809,2937,2936,2810,2811,2938,2810,2938,2937,2811,2812,2939,2811,2939,2938,2812,2813,2940,2812,2940,2939,2813,2814,2941,2813,2941,2940,2814,2815,2942,2814,2942,2941,2815,2816,2943,2815,2943,2942,2816,2817,2944,2816,2944,2943,2817,2818,2945,2817,2945,2944,2818,2819,2946,2818,2946,2945,2819,2820,2947,2819,2947,2946,2820,2821,2948,2820,2948,2947,2821,2822,2949,2821,2949,2948,2822,2823,2950,2822,2950,2949,2823,2824,2951,2823,2951,2950,2824,2825,2952,2824,2952,2951,2825,2826,2953,2825,2953,2952,2826,2827,2954,2826,2954,2953,2827,2828,2955,2827,2955,2954,2828,2829,2956,2828,2956,2955,2829,2830,2957,2829,2957,2956,2830,2831,2958,2830,2958,2957,2831,2832,2959,2831,2959,2958,2832,2833,2960,2832,2960,2959,2833,2834,2961,2833,2961,2960,2834,2835,2962,2834,2962,2961,2835,2836,2963,2835,2963,2962,2836,2837,2964,2836,2964,2963,2837,2838,2965,2837,2965,2964,2838,2839,2966,2838,2966,2965,2839,2840,2967,2839,2967,2966,2840,2841,2968,2840,2968,2967,2841,2842,2969,2841,2969,2968,2842,2843,2970,2842,2970,2969,2843,2844,2971,2843,2971,2970,2844,2845,2972,2844,2972,2971,2845,2846,2973,2845,2973,2972,2846,2847,2974,2846,2974,2973,2847,2848,2975,2847,2975,2974,2848,2849,2976,2848,2976,2975,2849,2850,2977,2849,2977,2976,2850,2851,2978,2850,2978,2977,2851,2852,2979,2851,2979,2978,2852,2853,2980,2852,2980,2979,2853,2854,2981,2853,2981,2980,2854,2855,2982,2854,2982,2981,2855,2856,2983,2855,2983,2982,2856,2857,2984,2856,2984,2983,2857,2858,2985,2857,2985,2984,2858,2859,2986,2858,2986,2985,2859,2860,2987,2859,2987,2986,2860,2861,2988,2860,2988,2987,2861,2862,2989,2861,2989,2988,2862,2863,2990,2862,2990,2989,2863,2864,2991,2863,2991,2990,2864,2865,2992,2864,2992,2991,2865,2866,2993,2865,2993,2992,2866,2867,2994,2866,2994,2993,2867,2868,2995,2867,2995,2994,2868,2869,2996,2868,2996,2995,2869,2870,2997,2869,2997,2996,2870,2871,2998,2870,2998,2997,2871,2872,2999,2871,2999,2998,2872,2873,3000,2872,3000,2999,2873,2874,3001,2873,3001,3000,2874,2875,3002,2874,3002,3001,2875,2876,3003,2875,3003,3002,2876,2877,3004,2876,3004,3003,2877,2878,3005,2877,3005,3004,2878,2879,3006,2878,3006,3005,2879,2880,3007,2879,3007,3006,2880,2881,3008,2880,3008,3007,2881,2882,3009,2881,3009,3008,2882,2883,3010,2882,3010,3009,2883,2884,3011,2883,3011,3010,2884,2885,3012,2884,3012,3011,2885,2886,3013,2885,3013,3012,2886,2887,3014,2886,3014,3013,2887,2888,3015,2887,3015,3014,2888,2889,3016,2888,3016,3015,2889,2890,3017,2889,3017,3016,2890,2891,3018,2890,3018,3017,2891,2892,3019,2891,3019,3018,2892,2893,3020,2892,3020,3019,2893,2894,3021,2893,3021,3020,2894,2895,3022,2894,3022,3021,2895,2896,3023,2895,3023,3022,2896,2897,3024,2896,3024,3023,2897,2898,3025,2897,3025,3024,2898,2899,3026,2898,3026,3025,2899,2900,3027,2899,3027,3026,2900,2901,3028,2900,3028,3027,2901,2902,3029,2901,3029,3028,2902,2903,3030,2902,3030,3029,2903,2904,3031,2903,3031,3030,2904,2905,3032,2904,3032,3031,2905,2906,3033,2905,3033,3032,2906,2907,3034,2906,3034,3033,2907,2908,3035,2907,3035,3034,2908,2909,3036,2908,3036,3035,2909,2910,3037,2909,3037,3036,2910,2911,3038,2910,3038,3037,2911,2912,3039,2911,3039,3038,2912,2913,3040,2912,3040,3039,2913,2914,3041,2913,3041,3040,2914,2915,3042,2914,3042,3041,2915,2916,3043,2915,3043,3042,2916,2917,3044,2916,3044,3043,2917,2918,3045,2917,3045,3044,2918,2919,3046,2918,3046,3045,2919,2920,3047,2919,3047,3046,2921,2922,3049,2921,3049,3048,2922,2923,3050,2922,3050,3049,2923,2924,3051,2923,3051,3050,2924,2925,3052,2924,3052,3051,2925,2926,3053,2925,3053,3052,2926,2927,3054,2926,3054,3053,2927,2928,3055,2927,3055,3054,2928,2929,3056,2928,3056,3055,2929,2930,3057,2929,3057,3056,2930,2931,3058,2930,3058,3057,2931,2932,3059,2931,3059,3058,2932,2933,3060,2932,3060,3059,2933,2934,3061,2933,3061,3060,2934,2935,3062,2934,3062,3061,2935,2936,3063,2935,3063,3062,2936,2937,3064,2936,3064,3063,2937,2938,3065,2937,3065,3064,2938,2939,3066,2938,3066,3065,2939,2940,3067,2939,3067,3066,2940,2941,3068,2940,3068,3067,2941,2942,3069,2941,3069,3068,2942,2943,3070,2942,3070,3069,2943,2944,3071,2943,3071,3070,2944,2945,3072,2944,3072,3071,2945,2946,3073,2945,3073,3072,2946,2947,3074,2946,3074,3073,2947,2948,3075,2947,3075,3074,2948,2949,3076,2948,3076,3075,2949,2950,3077,2949,3077,3076,2950,2951,3078,2950,3078,3077,2951,2952,3079,2951,3079,3078,2952,2953,3080,2952,3080,3079,2953,2954,3081,2953,3081,3080,2954,2955,3082,2954,3082,3081,2955,2956,3083,2955,3083,3082,2956,2957,3084,2956,3084,3083,2957,2958,3085,2957,3085,3084,2958,2959,3086,2958,3086,3085,2959,2960,3087,2959,3087,3086,2960,2961,3088,2960,3088,3087,2961,2962,3089,2961,3089,3088,2962,2963,3090,2962,3090,3089,2963,2964,3091,2963,3091,3090,2964,2965,3092,2964,3092,3091,2965,2966,3093,2965,3093,3092,2966,2967,3094,2966,3094,3093,2967,2968,3095,2967,3095,3094,2968,2969,3096,2968,3096,3095,2969,2970,3097,2969,3097,3096,2970,2971,3098,2970,3098,3097,2971,2972,3099,2971,3099,3098,2972,2973,3100,2972,3100,3099,2973,2974,3101,2973,3101,3100,2974,2975,3102,2974,3102,3101,2975,2976,3103,2975,3103,3102,2976,2977,3104,2976,3104,3103,2977,2978,3105,2977,3105,3104,2978,2979,3106,2978,3106,3105,2979,2980,3107,2979,3107,3106,2980,2981,3108,2980,3108,3107,2981,2982,3109,2981,3109,3108,2982,2983,3110,2982,3110,3109,2983,2984,3111,2983,3111,3110,2984,2985,3112,2984,3112,3111,2985,2986,3113,2985,3113,3112,2986,2987,3114,2986,3114,3113,2987,2988,3115,2987,3115,3114,2988,2989,3116,2988,3116,3115,2989,2990,3117,2989,3117,3116,2990,2991,3118,2990,3118,3117,2991,2992,3119,2991,3119,3118,2992,2993,3120,2992,3120,3119,2993,2994,3121,2993,3121,3120,2994,2995,3122,2994,3122,3121,2995,2996,3123,2995,3123,3122,2996,2997,3124,2996,3124,3123,2997,2998,3125,2997,3125,3124,2998,2999,3126,2998,3126,3125,2999,3000,3127,2999,3127,3126,3000,3001,3128,3000,3128,3127,3001,3002,3129,3001,3129,3128,3002,3003,3130,3002,3130,3129,3003,3004,3131,3003,3131,3130,3004,3005,3132,3004,3132,3131,3005,3006,3133,3005,3133,3132,3006,3007,3134,3006,3134,3133,3007,3008,3135,3007,3135,3134,3008,3009,3136,3008,3136,3135,3009,3010,3137,3009,3137,3136,3010,3011,3138,3010,3138,3137,3011,3012,3139,3011,3139,3138,3012,3013,3140,3012,3140,3139,3013,3014,3141,3013,3141,3140,3014,3015,3142,3014,3142,3141,3015,3016,3143,3015,3143,3142,3016,3017,3144,3016,3144,3143,3017,3018,3145,3017,3145,3144,3018,3019,3146,3018,3146,3145,3019,3020,3147,3019,3147,3146,3020,3021,3148,3020,3148,3147,3021,3022,3149,3021,3149,3148,3022,3023,3150,3022,3150,3149,3023,3024,3151,3023,3151,3150,3024,3025,3152,3024,3152,3151,3025,3026,3153,3025,3153,3152,3026,3027,3154,3026,3154,3153,3027,3028,3155,3027,3155,3154,3028,3029,3156,3028,3156,3155,3029,3030,3157,3029,3157,3156,3030,3031,3158,3030,3158,3157,3031,3032,3159,3031,3159,3158,3032,3033,3160,3032,3160,3159,3033,3034,3161,3033,3161,3160,3034,3035,3162,3034,3162,3161,3035,3036,3163,3035,3163,3162,3036,3037,3164,3036,3164,3163,3037,3038,3165,3037,3165,3164,3038,3039,3166,3038,3166,3165,3039,3040,3167,3039,3167,3166,3040,3041,3168,3040,3168,3167,3041,3042,3169,3041,3169,3168,3042,3043,3170,3042,3170,3169,3043,3044,3171,3043,3171,3170,3044,3045,3172,3044,3172,3171,3045,3046,3173,3045,3173,3172,3046,3047,3174,3046,3174,3173,3048,3049,3176,3048,3176,3175,3049,3050,3177,3049,3177,3176,3050,3051,3178,3050,3178,3177,3051,3052,3179,3051,3179,3178,3052,3053,3180,3052,3180,3179,3053,3054,3181,3053,3181,3180,3054,3055,3182,3054,3182,3181,3055,3056,3183,3055,3183,3182,3056,3057,3184,3056,3184,3183,3057,3058,3185,3057,3185,3184,3058,3059,3186,3058,3186,3185,3059,3060,3187,3059,3187,3186,3060,3061,3188,3060,3188,3187,3061,3062,3189,3061,3189,3188,3062,3063,3190,3062,3190,3189,3063,3064,3191,3063,3191,3190,3064,3065,3192,3064,3192,3191,3065,3066,3193,3065,3193,3192,3066,3067,3194,3066,3194,3193,3067,3068,3195,3067,3195,3194,3068,3069,3196,3068,3196,3195,3069,3070,3197,3069,3197,3196,3070,3071,3198,3070,3198,3197,3071,3072,3199,3071,3199,3198,3072,3073,3200,3072,3200,3199,3073,3074,3201,3073,3201,3200,3074,3075,3202,3074,3202,3201,3075,3076,3203,3075,3203,3202,3076,3077,3204,3076,3204,3203,3077,3078,3205,3077,3205,3204,3078,3079,3206,3078,3206,3205,3079,3080,3207,3079,3207,3206,3080,3081,3208,3080,3208,3207,3081,3082,3209,3081,3209,3208,3082,3083,3210,3082,3210,3209,3083,3084,3211,3083,3211,3210,3084,3085,3212,3084,3212,3211,3085,3086,3213,3085,3213,3212,3086,3087,3214,3086,3214,3213,3087,3088,3215,3087,3215,3214,3088,3089,3216,3088,3216,3215,3089,3090,3217,3089,3217,3216,3090,3091,3218,3090,3218,3217,3091,3092,3219,3091,3219,3218,3092,3093,3220,3092,3220,3219,3093,3094,3221,3093,3221,3220,3094,3095,3222,3094,3222,3221,3095,3096,3223,3095,3223,3222,3096,3097,3224,3096,3224,3223,3097,3098,3225,3097,3225,3224,3098,3099,3226,3098,3226,3225,3099,3100,3227,3099,3227,3226,3100,3101,3228,3100,3228,3227,3101,3102,3229,3101,3229,3228,3102,3103,3230,3102,3230,3229,3103,3104,3231,3103,3231,3230,3104,3105,3232,3104,3232,3231,3105,3106,3233,3105,3233,3232,3106,3107,3234,3106,3234,3233,3107,3108,3235,3107,3235,3234,3108,3109,3236,3108,3236,3235,3109,3110,3237,3109,3237,3236,3110,3111,3238,3110,3238,3237,3111,3112,3239,3111,3239,3238,3112,3113,3240,3112,3240,3239,3113,3114,3241,3113,3241,3240,3114,3115,3242,3114,3242,3241,3115,3116,3243,3115,3243,3242,3116,3117,3244,3116,3244,3243,3117,3118,3245,3117,3245,3244,3118,3119,3246,3118,3246,3245,3119,3120,3247,3119,3247,3246,3120,3121,3248,3120,3248,3247,3121,3122,3249,3121,3249,3248,3122,3123,3250,3122,3250,3249,3123,3124,3251,3123,3251,3250,3124,3125,3252,3124,3252,3251,3125,3126,3253,3125,3253,3252,3126,3127,3254,3126,3254,3253,3127,3128,3255,3127,3255,3254,3128,3129,3256,3128,3256,3255,3129,3130,3257,3129,3257,3256,3130,3131,3258,3130,3258,3257,3131,3132,3259,3131,3259,3258,3132,3133,3260,3132,3260,3259,3133,3134,3261,3133,3261,3260,3134,3135,3262,3134,3262,3261,3135,3136,3263,3135,3263,3262,3136,3137,3264,3136,3264,3263,3137,3138,3265,3137,3265,3264,3138,3139,3266,3138,3266,3265,3139,3140,3267,3139,3267,3266,3140,3141,3268,3140,3268,3267,3141,3142,3269,3141,3269,3268,3142,3143,3270,3142,3270,3269,3143,3144,3271,3143,3271,3270,3144,3145,3272,3144,3272,3271,3145,3146,3273,3145,3273,3272,3146,3147,3274,3146,3274,3273,3147,3148,3275,3147,3275,3274,3148,3149,3276,3148,3276,3275,3149,3150,3277,3149,3277,3276,3150,3151,3278,3150,3278,3277,3151,3152,3279,3151,3279,3278,3152,3153,3280,3152,3280,3279,3153,3154,3281,3153,3281,3280,3154,3155,3282,3154,3282,3281,3155,3156,3283,3155,3283,3282,3156,3157,3284,3156,3284,3283,3157,3158,3285,3157,3285,3284,3158,3159,3286,3158,3286,3285,3159,3160,3287,3159,3287,3286,3160,3161,3288,3160,3288,3287,3161,3162,3289,3161,3289,3288,3162,3163,3290,3162,3290,3289,3163,3164,3291,3163,3291,3290,3164,3165,3292,3164,3292,3291,3165,3166,3293,3165,3293,3292,3166,3167,3294,3166,3294,3293,3167,3168,3295,3167,3295,3294,3168,3169,3296,3168,3296,3295,3169,3170,3297,3169,3297,3296,3170,3171,3298,3170,3298,3297,3171,3172,3299,3171,3299,3298,3172,3173,3300,3172,3300,3299,3173,3174,3301,3173,3301,3300,3175,3176,3303,3175,3303,3302,3176,3177,3304,3176,3304,3303,3177,3178,3305,3177,3305,3304,3178,3179,3306,3178,3306,3305,3179,3180,3307,3179,3307,3306,3180,3181,3308,3180,3308,3307,3181,3182,3309,3181,3309,3308,3182,3183,3310,3182,3310,3309,3183,3184,3311,3183,3311,3310,3184,3185,3312,3184,3312,3311,3185,3186,3313,3185,3313,3312,3186,3187,3314,3186,3314,3313,3187,3188,3315,3187,3315,3314,3188,3189,3316,3188,3316,3315,3189,3190,3317,3189,3317,3316,3190,3191,3318,3190,3318,3317,3191,3192,3319,3191,3319,3318,3192,3193,3320,3192,3320,3319,3193,3194,3321,3193,3321,3320,3194,3195,3322,3194,3322,3321,3195,3196,3323,3195,3323,3322,3196,3197,3324,3196,3324,3323,3197,3198,3325,3197,3325,3324,3198,3199,3326,3198,3326,3325,3199,3200,3327,3199,3327,3326,3200,3201,3328,3200,3328,3327,3201,3202,3329,3201,3329,3328,3202,3203,3330,3202,3330,3329,3203,3204,3331,3203,3331,3330,3204,3205,3332,3204,3332,3331,3205,3206,3333,3205,3333,3332,3206,3207,3334,3206,3334,3333,3207,3208,3335,3207,3335,3334,3208,3209,3336,3208,3336,3335,3209,3210,3337,3209,3337,3336,3210,3211,3338,3210,3338,3337,3211,3212,3339,3211,3339,3338,3212,3213,3340,3212,3340,3339,3213,3214,3341,3213,3341,3340,3214,3215,3342,3214,3342,3341,3215,3216,3343,3215,3343,3342,3216,3217,3344,3216,3344,3343,3217,3218,3345,3217,3345,3344,3218,3219,3346,3218,3346,3345,3219,3220,3347,3219,3347,3346,3220,3221,3348,3220,3348,3347,3221,3222,3349,3221,3349,3348,3222,3223,3350,3222,3350,3349,3223,3224,3351,3223,3351,3350,3224,3225,3352,3224,3352,3351,3225,3226,3353,3225,3353,3352,3226,3227,3354,3226,3354,3353,3227,3228,3355,3227,3355,3354,3228,3229,3356,3228,3356,3355,3229,3230,3357,3229,3357,3356,3230,3231,3358,3230,3358,3357,3231,3232,3359,3231,3359,3358,3232,3233,3360,3232,3360,3359,3233,3234,3361,3233,3361,3360,3234,3235,3362,3234,3362,3361,3235,3236,3363,3235,3363,3362,3236,3237,3364,3236,3364,3363,3237,3238,3365,3237,3365,3364,3238,3239,3366,3238,3366,3365,3239,3240,3367,3239,3367,3366,3240,3241,3368,3240,3368,3367,3241,3242,3369,3241,3369,3368,3242,3243,3370,3242,3370,3369,3243,3244,3371,3243,3371,3370,3244,3245,3372,3244,3372,3371,3245,3246,3373,3245,3373,3372,3246,3247,3374,3246,3374,3373,3247,3248,3375,3247,3375,3374,3248,3249,3376,3248,3376,3375,3249,3250,3377,3249,3377,3376,3250,3251,3378,3250,3378,3377,3251,3252,3379,3251,3379,3378,3252,3253,3380,3252,3380,3379,3253,3254,3381,3253,3381,3380,3254,3255,3382,3254,3382,3381,3255,3256,3383,3255,3383,3382,3256,3257,3384,3256,3384,3383,3257,3258,3385,3257,3385,3384,3258,3259,3386,3258,3386,3385,3259,3260,3387,3259,3387,3386,3260,3261,3388,3260,3388,3387,3261,3262,3389,3261,3389,3388,3262,3263,3390,3262,3390,3389,3263,3264,3391,3263,3391,3390,3264,3265,3392,3264,3392,3391,3265,3266,3393,3265,3393,3392,3266,3267,3394,3266,3394,3393,3267,3268,3395,3267,3395,3394,3268,3269,3396,3268,3396,3395,3269,3270,3397,3269,3397,3396,3270,3271,3398,3270,3398,3397,3271,3272,3399,3271,3399,3398,3272,3273,3400,3272,3400,3399,3273,3274,3401,3273,3401,3400,3274,3275,3402,3274,3402,3401,3275,3276,3403,3275,3403,3402,3276,3277,3404,3276,3404,3403,3277,3278,3405,3277,3405,3404,3278,3279,3406,3278,3406,3405,3279,3280,3407,3279,3407,3406,3280,3281,3408,3280,3408,3407,3281,3282,3409,3281,3409,3408,3282,3283,3410,3282,3410,3409,3283,3284,3411,3283,3411,3410,3284,3285,3412,3284,3412,3411,3285,3286,3413,3285,3413,3412,3286,3287,3414,3286,3414,3413,3287,3288,3415,3287,3415,3414,3288,3289,3416,3288,3416,3415,3289,3290,3417,3289,3417,3416,3290,3291,3418,3290,3418,3417,3291,3292,3419,3291,3419,3418,3292,3293,3420,3292,3420,3419,3293,3294,3421,3293,3421,3420,3294,3295,3422,3294,3422,3421,3295,3296,3423,3295,3423,3422,3296,3297,3424,3296,3424,3423,3297,3298,3425,3297,3425,3424,3298,3299,3426,3298,3426,3425,3299,3300,3427,3299,3427,3426,3300,3301,3428,3300,3428,3427,3302,3303,3430,3302,3430,3429,3303,3304,3431,3303,3431,3430,3304,3305,3432,3304,3432,3431,3305,3306,3433,3305,3433,3432,3306,3307,3434,3306,3434,3433,3307,3308,3435,3307,3435,3434,3308,3309,3436,3308,3436,3435,3309,3310,3437,3309,3437,3436,3310,3311,3438,3310,3438,3437,3311,3312,3439,3311,3439,3438,3312,3313,3440,3312,3440,3439,3313,3314,3441,3313,3441,3440,3314,3315,3442,3314,3442,3441,3315,3316,3443,3315,3443,3442,3316,3317,3444,3316,3444,3443,3317,3318,3445,3317,3445,3444,3318,3319,3446,3318,3446,3445,3319,3320,3447,3319,3447,3446,3320,3321,3448,3320,3448,3447,3321,3322,3449,3321,3449,3448,3322,3323,3450,3322,3450,3449,3323,3324,3451,3323,3451,3450,3324,3325,3452,3324,3452,3451,3325,3326,3453,3325,3453,3452,3326,3327,3454,3326,3454,3453,3327,3328,3455,3327,3455,3454,3328,3329,3456,3328,3456,3455,3329,3330,3457,3329,3457,3456,3330,3331,3458,3330,3458,3457,3331,3332,3459,3331,3459,3458,3332,3333,3460,3332,3460,3459,3333,3334,3461,3333,3461,3460,3334,3335,3462,3334,3462,3461,3335,3336,3463,3335,3463,3462,3336,3337,3464,3336,3464,3463,3337,3338,3465,3337,3465,3464,3338,3339,3466,3338,3466,3465,3339,3340,3467,3339,3467,3466,3340,3341,3468,3340,3468,3467,3341,3342,3469,3341,3469,3468,3342,3343,3470,3342,3470,3469,3343,3344,3471,3343,3471,3470,3344,3345,3472,3344,3472,3471,3345,3346,3473,3345,3473,3472,3346,3347,3474,3346,3474,3473,3347,3348,3475,3347,3475,3474,3348,3349,3476,3348,3476,3475,3349,3350,3477,3349,3477,3476,3350,3351,3478,3350,3478,3477,3351,3352,3479,3351,3479,3478,3352,3353,3480,3352,3480,3479,3353,3354,3481,3353,3481,3480,3354,3355,3482,3354,3482,3481,3355,3356,3483,3355,3483,3482,3356,3357,3484,3356,3484,3483,3357,3358,3485,3357,3485,3484,3358,3359,3486,3358,3486,3485,3359,3360,3487,3359,3487,3486,3360,3361,3488,3360,3488,3487,3361,3362,3489,3361,3489,3488,3362,3363,3490,3362,3490,3489,3363,3364,3491,3363,3491,3490,3364,3365,3492,3364,3492,3491,3365,3366,3493,3365,3493,3492,3366,3367,3494,3366,3494,3493,3367,3368,3495,3367,3495,3494,3368,3369,3496,3368,3496,3495,3369,3370,3497,3369,3497,3496,3370,3371,3498,3370,3498,3497,3371,3372,3499,3371,3499,3498,3372,3373,3500,3372,3500,3499,3373,3374,3501,3373,3501,3500,3374,3375,3502,3374,3502,3501,3375,3376,3503,3375,3503,3502,3376,3377,3504,3376,3504,3503,3377,3378,3505,3377,3505,3504,3378,3379,3506,3378,3506,3505,3379,3380,3507,3379,3507,3506,3380,3381,3508,3380,3508,3507,3381,3382,3509,3381,3509,3508,3382,3383,3510,3382,3510,3509,3383,3384,3511,3383,3511,3510,3384,3385,3512,3384,3512,3511,3385,3386,3513,3385,3513,3512,3386,3387,3514,3386,3514,3513,3387,3388,3515,3387,3515,3514,3388,3389,3516,3388,3516,3515,3389,3390,3517,3389,3517,3516,3390,3391,3518,3390,3518,3517,3391,3392,3519,3391,3519,3518,3392,3393,3520,3392,3520,3519,3393,3394,3521,3393,3521,3520,3394,3395,3522,3394,3522,3521,3395,3396,3523,3395,3523,3522,3396,3397,3524,3396,3524,3523,3397,3398,3525,3397,3525,3524,3398,3399,3526,3398,3526,3525,3399,3400,3527,3399,3527,3526,3400,3401,3528,3400,3528,3527,3401,3402,3529,3401,3529,3528,3402,3403,3530,3402,3530,3529,3403,3404,3531,3403,3531,3530,3404,3405,3532,3404,3532,3531,3405,3406,3533,3405,3533,3532,3406,3407,3534,3406,3534,3533,3407,3408,3535,3407,3535,3534,3408,3409,3536,3408,3536,3535,3409,3410,3537,3409,3537,3536,3410,3411,3538,3410,3538,3537,3411,3412,3539,3411,3539,3538,3412,3413,3540,3412,3540,3539,3413,3414,3541,3413,3541,3540,3414,3415,3542,3414,3542,3541,3415,3416,3543,3415,3543,3542,3416,3417,3544,3416,3544,3543,3417,3418,3545,3417,3545,3544,3418,3419,3546,3418,3546,3545,3419,3420,3547,3419,3547,3546,3420,3421,3548,3420,3548,3547,3421,3422,3549,3421,3549,3548,3422,3423,3550,3422,3550,3549,3423,3424,3551,3423,3551,3550,3424,3425,3552,3424,3552,3551,3425,3426,3553,3425,3553,3552,3426,3427,3554,3426,3554,3553,3427,3428,3555,3427,3555,3554,3429,3430,3557,3429,3557,3556,3430,3431,3558,3430,3558,3557,3431,3432,3559,3431,3559,3558,3432,3433,3560,3432,3560,3559,3433,3434,3561,3433,3561,3560,3434,3435,3562,3434,3562,3561,3435,3436,3563,3435,3563,3562,3436,3437,3564,3436,3564,3563,3437,3438,3565,3437,3565,3564,3438,3439,3566,3438,3566,3565,3439,3440,3567,3439,3567,3566,3440,3441,3568,3440,3568,3567,3441,3442,3569,3441,3569,3568,3442,3443,3570,3442,3570,3569,3443,3444,3571,3443,3571,3570,3444,3445,3572,3444,3572,3571,3445,3446,3573,3445,3573,3572,3446,3447,3574,3446,3574,3573,3447,3448,3575,3447,3575,3574,3448,3449,3576,3448,3576,3575,3449,3450,3577,3449,3577,3576,3450,3451,3578,3450,3578,3577,3451,3452,3579,3451,3579,3578,3452,3453,3580,3452,3580,3579,3453,3454,3581,3453,3581,3580,3454,3455,3582,3454,3582,3581,3455,3456,3583,3455,3583,3582,3456,3457,3584,3456,3584,3583,3457,3458,3585,3457,3585,3584,3458,3459,3586,3458,3586,3585,3459,3460,3587,3459,3587,3586,3460,3461,3588,3460,3588,3587,3461,3462,3589,3461,3589,3588,3462,3463,3590,3462,3590,3589,3463,3464,3591,3463,3591,3590,3464,3465,3592,3464,3592,3591,3465,3466,3593,3465,3593,3592,3466,3467,3594,3466,3594,3593,3467,3468,3595,3467,3595,3594,3468,3469,3596,3468,3596,3595,3469,3470,3597,3469,3597,3596,3470,3471,3598,3470,3598,3597,3471,3472,3599,3471,3599,3598,3472,3473,3600,3472,3600,3599,3473,3474,3601,3473,3601,3600,3474,3475,3602,3474,3602,3601,3475,3476,3603,3475,3603,3602,3476,3477,3604,3476,3604,3603,3477,3478,3605,3477,3605,3604,3478,3479,3606,3478,3606,3605,3479,3480,3607,3479,3607,3606,3480,3481,3608,3480,3608,3607,3481,3482,3609,3481,3609,3608,3482,3483,3610,3482,3610,3609,3483,3484,3611,3483,3611,3610,3484,3485,3612,3484,3612,3611,3485,3486,3613,3485,3613,3612,3486,3487,3614,3486,3614,3613,3487,3488,3615,3487,3615,3614,3488,3489,3616,3488,3616,3615,3489,3490,3617,3489,3617,3616,3490,3491,3618,3490,3618,3617,3491,3492,3619,3491,3619,3618,3492,3493,3620,3492,3620,3619,3493,3494,3621,3493,3621,3620,3494,3495,3622,3494,3622,3621,3495,3496,3623,3495,3623,3622,3496,3497,3624,3496,3624,3623,3497,3498,3625,3497,3625,3624,3498,3499,3626,3498,3626,3625,3499,3500,3627,3499,3627,3626,3500,3501,3628,3500,3628,3627,3501,3502,3629,3501,3629,3628,3502,3503,3630,3502,3630,3629,3503,3504,3631,3503,3631,3630,3504,3505,3632,3504,3632,3631,3505,3506,3633,3505,3633,3632,3506,3507,3634,3506,3634,3633,3507,3508,3635,3507,3635,3634,3508,3509,3636,3508,3636,3635,3509,3510,3637,3509,3637,3636,3510,3511,3638,3510,3638,3637,3511,3512,3639,3511,3639,3638,3512,3513,3640,3512,3640,3639,3513,3514,3641,3513,3641,3640,3514,3515,3642,3514,3642,3641,3515,3516,3643,3515,3643,3642,3516,3517,3644,3516,3644,3643,3517,3518,3645,3517,3645,3644,3518,3519,3646,3518,3646,3645,3519,3520,3647,3519,3647,3646,3520,3521,3648,3520,3648,3647,3521,3522,3649,3521,3649,3648,3522,3523,3650,3522,3650,3649,3523,3524,3651,3523,3651,3650,3524,3525,3652,3524,3652,3651,3525,3526,3653,3525,3653,3652,3526,3527,3654,3526,3654,3653,3527,3528,3655,3527,3655,3654,3528,3529,3656,3528,3656,3655,3529,3530,3657,3529,3657,3656,3530,3531,3658,3530,3658,3657,3531,3532,3659,3531,3659,3658,3532,3533,3660,3532,3660,3659,3533,3534,3661,3533,3661,3660,3534,3535,3662,3534,3662,3661,3535,3536,3663,3535,3663,3662,3536,3537,3664,3536,3664,3663,3537,3538,3665,3537,3665,3664,3538,3539,3666,3538,3666,3665,3539,3540,3667,3539,3667,3666,3540,3541,3668,3540,3668,3667,3541,3542,3669,3541,3669,3668,3542,3543,3670,3542,3670,3669,3543,3544,3671,3543,3671,3670,3544,3545,3672,3544,3672,3671,3545,3546,3673,3545,3673,3672,3546,3547,3674,3546,3674,3673,3547,3548,3675,3547,3675,3674,3548,3549,3676,3548,3676,3675,3549,3550,3677,3549,3677,3676,3550,3551,3678,3550,3678,3677,3551,3552,3679,3551,3679,3678,3552,3553,3680,3552,3680,3679,3553,3554,3681,3553,3681,3680,3554,3555,3682,3554,3682,3681,3556,3557,3684,3556,3684,3683,3557,3558,3685,3557,3685,3684,3558,3559,3686,3558,3686,3685,3559,3560,3687,3559,3687,3686,3560,3561,3688,3560,3688,3687,3561,3562,3689,3561,3689,3688,3562,3563,3690,3562,3690,3689,3563,3564,3691,3563,3691,3690,3564,3565,3692,3564,3692,3691,3565,3566,3693,3565,3693,3692,3566,3567,3694,3566,3694,3693,3567,3568,3695,3567,3695,3694,3568,3569,3696,3568,3696,3695,3569,3570,3697,3569,3697,3696,3570,3571,3698,3570,3698,3697,3571,3572,3699,3571,3699,3698,3572,3573,3700,3572,3700,3699,3573,3574,3701,3573,3701,3700,3574,3575,3702,3574,3702,3701,3575,3576,3703,3575,3703,3702,3576,3577,3704,3576,3704,3703,3577,3578,3705,3577,3705,3704,3578,3579,3706,3578,3706,3705,3579,3580,3707,3579,3707,3706,3580,3581,3708,3580,3708,3707,3581,3582,3709,3581,3709,3708,3582,3583,3710,3582,3710,3709,3583,3584,3711,3583,3711,3710,3584,3585,3712,3584,3712,3711,3585,3586,3713,3585,3713,3712,3586,3587,3714,3586,3714,3713,3587,3588,3715,3587,3715,3714,3588,3589,3716,3588,3716,3715,3589,3590,3717,3589,3717,3716,3590,3591,3718,3590,3718,3717,3591,3592,3719,3591,3719,3718,3592,3593,3720,3592,3720,3719,3593,3594,3721,3593,3721,3720,3594,3595,3722,3594,3722,3721,3595,3596,3723,3595,3723,3722,3596,3597,3724,3596,3724,3723,3597,3598,3725,3597,3725,3724,3598,3599,3726,3598,3726,3725,3599,3600,3727,3599,3727,3726,3600,3601,3728,3600,3728,3727,3601,3602,3729,3601,3729,3728,3602,3603,3730,3602,3730,3729,3603,3604,3731,3603,3731,3730,3604,3605,3732,3604,3732,3731,3605,3606,3733,3605,3733,3732,3606,3607,3734,3606,3734,3733,3607,3608,3735,3607,3735,3734,3608,3609,3736,3608,3736,3735,3609,3610,3737,3609,3737,3736,3610,3611,3738,3610,3738,3737,3611,3612,3739,3611,3739,3738,3612,3613,3740,3612,3740,3739,3613,3614,3741,3613,3741,3740,3614,3615,3742,3614,3742,3741,3615,3616,3743,3615,3743,3742,3616,3617,3744,3616,3744,3743,3617,3618,3745,3617,3745,3744,3618,3619,3746,3618,3746,3745,3619,3620,3747,3619,3747,3746,3620,3621,3748,3620,3748,3747,3621,3622,3749,3621,3749,3748,3622,3623,3750,3622,3750,3749,3623,3624,3751,3623,3751,3750,3624,3625,3752,3624,3752,3751,3625,3626,3753,3625,3753,3752,3626,3627,3754,3626,3754,3753,3627,3628,3755,3627,3755,3754,3628,3629,3756,3628,3756,3755,3629,3630,3757,3629,3757,3756,3630,3631,3758,3630,3758,3757,3631,3632,3759,3631,3759,3758,3632,3633,3760,3632,3760,3759,3633,3634,3761,3633,3761,3760,3634,3635,3762,3634,3762,3761,3635,3636,3763,3635,3763,3762,3636,3637,3764,3636,3764,3763,3637,3638,3765,3637,3765,3764,3638,3639,3766,3638,3766,3765,3639,3640,3767,3639,3767,3766,3640,3641,3768,3640,3768,3767,3641,3642,3769,3641,3769,3768,3642,3643,3770,3642,3770,3769,3643,3644,3771,3643,3771,3770,3644,3645,3772,3644,3772,3771,3645,3646,3773,3645,3773,3772,3646,3647,3774,3646,3774,3773,3647,3648,3775,3647,3775,3774,3648,3649,3776,3648,3776,3775,3649,3650,3777,3649,3777,3776,3650,3651,3778,3650,3778,3777,3651,3652,3779,3651,3779,3778,3652,3653,3780,3652,3780,3779,3653,3654,3781,3653,3781,3780,3654,3655,3782,3654,3782,3781,3655,3656,3783,3655,3783,3782,3656,3657,3784,3656,3784,3783,3657,3658,3785,3657,3785,3784,3658,3659,3786,3658,3786,3785,3659,3660,3787,3659,3787,3786,3660,3661,3788,3660,3788,3787,3661,3662,3789,3661,3789,3788,3662,3663,3790,3662,3790,3789,3663,3664,3791,3663,3791,3790,3664,3665,3792,3664,3792,3791,3665,3666,3793,3665,3793,3792,3666,3667,3794,3666,3794,3793,3667,3668,3795,3667,3795,3794,3668,3669,3796,3668,3796,3795,3669,3670,3797,3669,3797,3796,3670,3671,3798,3670,3798,3797,3671,3672,3799,3671,3799,3798,3672,3673,3800,3672,3800,3799,3673,3674,3801,3673,3801,3800,3674,3675,3802,3674,3802,3801,3675,3676,3803,3675,3803,3802,3676,3677,3804,3676,3804,3803,3677,3678,3805,3677,3805,3804,3678,3679,3806,3678,3806,3805,3679,3680,3807,3679,3807,3806,3680,3681,3808,3680,3808,3807,3681,3682,3809,3681,3809,3808,3683,3684,3811,3683,3811,3810,3684,3685,3812,3684,3812,3811,3685,3686,3813,3685,3813,3812,3686,3687,3814,3686,3814,3813,3687,3688,3815,3687,3815,3814,3688,3689,3816,3688,3816,3815,3689,3690,3817,3689,3817,3816,3690,3691,3818,3690,3818,3817,3691,3692,3819,3691,3819,3818,3692,3693,3820,3692,3820,3819,3693,3694,3821,3693,3821,3820,3694,3695,3822,3694,3822,3821,3695,3696,3823,3695,3823,3822,3696,3697,3824,3696,3824,3823,3697,3698,3825,3697,3825,3824,3698,3699,3826,3698,3826,3825,3699,3700,3827,3699,3827,3826,3700,3701,3828,3700,3828,3827,3701,3702,3829,3701,3829,3828,3702,3703,3830,3702,3830,3829,3703,3704,3831,3703,3831,3830,3704,3705,3832,3704,3832,3831,3705,3706,3833,3705,3833,3832,3706,3707,3834,3706,3834,3833,3707,3708,3835,3707,3835,3834,3708,3709,3836,3708,3836,3835,3709,3710,3837,3709,3837,3836,3710,3711,3838,3710,3838,3837,3711,3712,3839,3711,3839,3838,3712,3713,3840,3712,3840,3839,3713,3714,3841,3713,3841,3840,3714,3715,3842,3714,3842,3841,3715,3716,3843,3715,3843,3842,3716,3717,3844,3716,3844,3843,3717,3718,3845,3717,3845,3844,3718,3719,3846,3718,3846,3845,3719,3720,3847,3719,3847,3846,3720,3721,3848,3720,3848,3847,3721,3722,3849,3721,3849,3848,3722,3723,3850,3722,3850,3849,3723,3724,3851,3723,3851,3850,3724,3725,3852,3724,3852,3851,3725,3726,3853,3725,3853,3852,3726,3727,3854,3726,3854,3853,3727,3728,3855,3727,3855,3854,3728,3729,3856,3728,3856,3855,3729,3730,3857,3729,3857,3856,3730,3731,3858,3730,3858,3857,3731,3732,3859,3731,3859,3858,3732,3733,3860,3732,3860,3859,3733,3734,3861,3733,3861,3860,3734,3735,3862,3734,3862,3861,3735,3736,3863,3735,3863,3862,3736,3737,3864,3736,3864,3863,3737,3738,3865,3737,3865,3864,3738,3739,3866,3738,3866,3865,3739,3740,3867,3739,3867,3866,3740,3741,3868,3740,3868,3867,3741,3742,3869,3741,3869,3868,3742,3743,3870,3742,3870,3869,3743,3744,3871,3743,3871,3870,3744,3745,3872,3744,3872,3871,3745,3746,3873,3745,3873,3872,3746,3747,3874,3746,3874,3873,3747,3748,3875,3747,3875,3874,3748,3749,3876,3748,3876,3875,3749,3750,3877,3749,3877,3876,3750,3751,3878,3750,3878,3877,3751,3752,3879,3751,3879,3878,3752,3753,3880,3752,3880,3879,3753,3754,3881,3753,3881,3880,3754,3755,3882,3754,3882,3881,3755,3756,3883,3755,3883,3882,3756,3757,3884,3756,3884,3883,3757,3758,3885,3757,3885,3884,3758,3759,3886,3758,3886,3885,3759,3760,3887,3759,3887,3886,3760,3761,3888,3760,3888,3887,3761,3762,3889,3761,3889,3888,3762,3763,3890,3762,3890,3889,3763,3764,3891,3763,3891,3890,3764,3765,3892,3764,3892,3891,3765,3766,3893,3765,3893,3892,3766,3767,3894,3766,3894,3893,3767,3768,3895,3767,3895,3894,3768,3769,3896,3768,3896,3895,3769,3770,3897,3769,3897,3896,3770,3771,3898,3770,3898,3897,3771,3772,3899,3771,3899,3898,3772,3773,3900,3772,3900,3899,3773,3774,3901,3773,3901,3900,3774,3775,3902,3774,3902,3901,3775,3776,3903,3775,3903,3902,3776,3777,3904,3776,3904,3903,3777,3778,3905,3777,3905,3904,3778,3779,3906,3778,3906,3905,3779,3780,3907,3779,3907,3906,3780,3781,3908,3780,3908,3907,3781,3782,3909,3781,3909,3908,3782,3783,3910,3782,3910,3909,3783,3784,3911,3783,3911,3910,3784,3785,3912,3784,3912,3911,3785,3786,3913,3785,3913,3912,3786,3787,3914,3786,3914,3913,3787,3788,3915,3787,3915,3914,3788,3789,3916,3788,3916,3915,3789,3790,3917,3789,3917,3916,3790,3791,3918,3790,3918,3917,3791,3792,3919,3791,3919,3918,3792,3793,3920,3792,3920,3919,3793,3794,3921,3793,3921,3920,3794,3795,3922,3794,3922,3921,3795,3796,3923,3795,3923,3922,3796,3797,3924,3796,3924,3923,3797,3798,3925,3797,3925,3924,3798,3799,3926,3798,3926,3925,3799,3800,3927,3799,3927,3926,3800,3801,3928,3800,3928,3927,3801,3802,3929,3801,3929,3928,3802,3803,3930,3802,3930,3929,3803,3804,3931,3803,3931,3930,3804,3805,3932,3804,3932,3931,3805,3806,3933,3805,3933,3932,3806,3807,3934,3806,3934,3933,3807,3808,3935,3807,3935,3934,3808,3809,3936,3808,3936,3935,3810,3811,3938,3810,3938,3937,3811,3812,3939,3811,3939,3938,3812,3813,3940,3812,3940,3939,3813,3814,3941,3813,3941,3940,3814,3815,3942,3814,3942,3941,3815,3816,3943,3815,3943,3942,3816,3817,3944,3816,3944,3943,3817,3818,3945,3817,3945,3944,3818,3819,3946,3818,3946,3945,3819,3820,3947,3819,3947,3946,3820,3821,3948,3820,3948,3947,3821,3822,3949,3821,3949,3948,3822,3823,3950,3822,3950,3949,3823,3824,3951,3823,3951,3950,3824,3825,3952,3824,3952,3951,3825,3826,3953,3825,3953,3952,3826,3827,3954,3826,3954,3953,3827,3828,3955,3827,3955,3954,3828,3829,3956,3828,3956,3955,3829,3830,3957,3829,3957,3956,3830,3831,3958,3830,3958,3957,3831,3832,3959,3831,3959,3958,3832,3833,3960,3832,3960,3959,3833,3834,3961,3833,3961,3960,3834,3835,3962,3834,3962,3961,3835,3836,3963,3835,3963,3962,3836,3837,3964,3836,3964,3963,3837,3838,3965,3837,3965,3964,3838,3839,3966,3838,3966,3965,3839,3840,3967,3839,3967,3966,3840,3841,3968,3840,3968,3967,3841,3842,3969,3841,3969,3968,3842,3843,3970,3842,3970,3969,3843,3844,3971,3843,3971,3970,3844,3845,3972,3844,3972,3971,3845,3846,3973,3845,3973,3972,3846,3847,3974,3846,3974,3973,3847,3848,3975,3847,3975,3974,3848,3849,3976,3848,3976,3975,3849,3850,3977,3849,3977,3976,3850,3851,3978,3850,3978,3977,3851,3852,3979,3851,3979,3978,3852,3853,3980,3852,3980,3979,3853,3854,3981,3853,3981,3980,3854,3855,3982,3854,3982,3981,3855,3856,3983,3855,3983,3982,3856,3857,3984,3856,3984,3983,3857,3858,3985,3857,3985,3984,3858,3859,3986,3858,3986,3985,3859,3860,3987,3859,3987,3986,3860,3861,3988,3860,3988,3987,3861,3862,3989,3861,3989,3988,3862,3863,3990,3862,3990,3989,3863,3864,3991,3863,3991,3990,3864,3865,3992,3864,3992,3991,3865,3866,3993,3865,3993,3992,3866,3867,3994,3866,3994,3993,3867,3868,3995,3867,3995,3994,3868,3869,3996,3868,3996,3995,3869,3870,3997,3869,3997,3996,3870,3871,3998,3870,3998,3997,3871,3872,3999,3871,3999,3998,3872,3873,4000,3872,4000,3999,3873,3874,4001,3873,4001,4000,3874,3875,4002,3874,4002,4001,3875,3876,4003,3875,4003,4002,3876,3877,4004,3876,4004,4003,3877,3878,4005,3877,4005,4004,3878,3879,4006,3878,4006,4005,3879,3880,4007,3879,4007,4006,3880,3881,4008,3880,4008,4007,3881,3882,4009,3881,4009,4008,3882,3883,4010,3882,4010,4009,3883,3884,4011,3883,4011,4010,3884,3885,4012,3884,4012,4011,3885,3886,4013,3885,4013,4012,3886,3887,4014,3886,4014,4013,3887,3888,4015,3887,4015,4014,3888,3889,4016,3888,4016,4015,3889,3890,4017,3889,4017,4016,3890,3891,4018,3890,4018,4017,3891,3892,4019,3891,4019,4018,3892,3893,4020,3892,4020,4019,3893,3894,4021,3893,4021,4020,3894,3895,4022,3894,4022,4021,3895,3896,4023,3895,4023,4022,3896,3897,4024,3896,4024,4023,3897,3898,4025,3897,4025,4024,3898,3899,4026,3898,4026,4025,3899,3900,4027,3899,4027,4026,3900,3901,4028,3900,4028,4027,3901,3902,4029,3901,4029,4028,3902,3903,4030,3902,4030,4029,3903,3904,4031,3903,4031,4030,3904,3905,4032,3904,4032,4031,3905,3906,4033,3905,4033,4032,3906,3907,4034,3906,4034,4033,3907,3908,4035,3907,4035,4034,3908,3909,4036,3908,4036,4035,3909,3910,4037,3909,4037,4036,3910,3911,4038,3910,4038,4037,3911,3912,4039,3911,4039,4038,3912,3913,4040,3912,4040,4039,3913,3914,4041,3913,4041,4040,3914,3915,4042,3914,4042,4041,3915,3916,4043,3915,4043,4042,3916,3917,4044,3916,4044,4043,3917,3918,4045,3917,4045,4044,3918,3919,4046,3918,4046,4045,3919,3920,4047,3919,4047,4046,3920,3921,4048,3920,4048,4047,3921,3922,4049,3921,4049,4048,3922,3923,4050,3922,4050,4049,3923,3924,4051,3923,4051,4050,3924,3925,4052,3924,4052,4051,3925,3926,4053,3925,4053,4052,3926,3927,4054,3926,4054,4053,3927,3928,4055,3927,4055,4054,3928,3929,4056,3928,4056,4055,3929,3930,4057,3929,4057,4056,3930,3931,4058,3930,4058,4057,3931,3932,4059,3931,4059,4058,3932,3933,4060,3932,4060,4059,3933,3934,4061,3933,4061,4060,3934,3935,4062,3934,4062,4061,3935,3936,4063,3935,4063,4062,3937,3938,4065,3937,4065,4064,3938,3939,4066,3938,4066,4065,3939,3940,4067,3939,4067,4066,3940,3941,4068,3940,4068,4067,3941,3942,4069,3941,4069,4068,3942,3943,4070,3942,4070,4069,3943,3944,4071,3943,4071,4070,3944,3945,4072,3944,4072,4071,3945,3946,4073,3945,4073,4072,3946,3947,4074,3946,4074,4073,3947,3948,4075,3947,4075,4074,3948,3949,4076,3948,4076,4075,3949,3950,4077,3949,4077,4076,3950,3951,4078,3950,4078,4077,3951,3952,4079,3951,4079,4078,3952,3953,4080,3952,4080,4079,3953,3954,4081,3953,4081,4080,3954,3955,4082,3954,4082,4081,3955,3956,4083,3955,4083,4082,3956,3957,4084,3956,4084,4083,3957,3958,4085,3957,4085,4084,3958,3959,4086,3958,4086,4085,3959,3960,4087,3959,4087,4086,3960,3961,4088,3960,4088,4087,3961,3962,4089,3961,4089,4088,3962,3963,4090,3962,4090,4089,3963,3964,4091,3963,4091,4090,3964,3965,4092,3964,4092,4091,3965,3966,4093,3965,4093,4092,3966,3967,4094,3966,4094,4093,3967,3968,4095,3967,4095,4094,3968,3969,4096,3968,4096,4095,3969,3970,4097,3969,4097,4096,3970,3971,4098,3970,4098,4097,3971,3972,4099,3971,4099,4098,3972,3973,4100,3972,4100,4099,3973,3974,4101,3973,4101,4100,3974,3975,4102,3974,4102,4101,3975,3976,4103,3975,4103,4102,3976,3977,4104,3976,4104,4103,3977,3978,4105,3977,4105,4104,3978,3979,4106,3978,4106,4105,3979,3980,4107,3979,4107,4106,3980,3981,4108,3980,4108,4107,3981,3982,4109,3981,4109,4108,3982,3983,4110,3982,4110,4109,3983,3984,4111,3983,4111,4110,3984,3985,4112,3984,4112,4111,3985,3986,4113,3985,4113,4112,3986,3987,4114,3986,4114,4113,3987,3988,4115,3987,4115,4114,3988,3989,4116,3988,4116,4115,3989,3990,4117,3989,4117,4116,3990,3991,4118,3990,4118,4117,3991,3992,4119,3991,4119,4118,3992,3993,4120,3992,4120,4119,3993,3994,4121,3993,4121,4120,3994,3995,4122,3994,4122,4121,3995,3996,4123,3995,4123,4122,3996,3997,4124,3996,4124,4123,3997,3998,4125,3997,4125,4124,3998,3999,4126,3998,4126,4125,3999,4000,4127,3999,4127,4126,4000,4001,4128,4000,4128,4127,4001,4002,4129,4001,4129,4128,4002,4003,4130,4002,4130,4129,4003,4004,4131,4003,4131,4130,4004,4005,4132,4004,4132,4131,4005,4006,4133,4005,4133,4132,4006,4007,4134,4006,4134,4133,4007,4008,4135,4007,4135,4134,4008,4009,4136,4008,4136,4135,4009,4010,4137,4009,4137,4136,4010,4011,4138,4010,4138,4137,4011,4012,4139,4011,4139,4138,4012,4013,4140,4012,4140,4139,4013,4014,4141,4013,4141,4140,4014,4015,4142,4014,4142,4141,4015,4016,4143,4015,4143,4142,4016,4017,4144,4016,4144,4143,4017,4018,4145,4017,4145,4144,4018,4019,4146,4018,4146,4145,4019,4020,4147,4019,4147,4146,4020,4021,4148,4020,4148,4147,4021,4022,4149,4021,4149,4148,4022,4023,4150,4022,4150,4149,4023,4024,4151,4023,4151,4150,4024,4025,4152,4024,4152,4151,4025,4026,4153,4025,4153,4152,4026,4027,4154,4026,4154,4153,4027,4028,4155,4027,4155,4154,4028,4029,4156,4028,4156,4155,4029,4030,4157,4029,4157,4156,4030,4031,4158,4030,4158,4157,4031,4032,4159,4031,4159,4158,4032,4033,4160,4032,4160,4159,4033,4034,4161,4033,4161,4160,4034,4035,4162,4034,4162,4161,4035,4036,4163,4035,4163,4162,4036,4037,4164,4036,4164,4163,4037,4038,4165,4037,4165,4164,4038,4039,4166,4038,4166,4165,4039,4040,4167,4039,4167,4166,4040,4041,4168,4040,4168,4167,4041,4042,4169,4041,4169,4168,4042,4043,4170,4042,4170,4169,4043,4044,4171,4043,4171,4170,4044,4045,4172,4044,4172,4171,4045,4046,4173,4045,4173,4172,4046,4047,4174,4046,4174,4173,4047,4048,4175,4047,4175,4174,4048,4049,4176,4048,4176,4175,4049,4050,4177,4049,4177,4176,4050,4051,4178,4050,4178,4177,4051,4052,4179,4051,4179,4178,4052,4053,4180,4052,4180,4179,4053,4054,4181,4053,4181,4180,4054,4055,4182,4054,4182,4181,4055,4056,4183,4055,4183,4182,4056,4057,4184,4056,4184,4183,4057,4058,4185,4057,4185,4184,4058,4059,4186,4058,4186,4185,4059,4060,4187,4059,4187,4186,4060,4061,4188,4060,4188,4187,4061,4062,4189,4061,4189,4188,4062,4063,4190,4062,4190,4189,4064,4065,4192,4064,4192,4191,4065,4066,4193,4065,4193,4192,4066,4067,4194,4066,4194,4193,4067,4068,4195,4067,4195,4194,4068,4069,4196,4068,4196,4195,4069,4070,4197,4069,4197,4196,4070,4071,4198,4070,4198,4197,4071,4072,4199,4071,4199,4198,4072,4073,4200,4072,4200,4199,4073,4074,4201,4073,4201,4200,4074,4075,4202,4074,4202,4201,4075,4076,4203,4075,4203,4202,4076,4077,4204,4076,4204,4203,4077,4078,4205,4077,4205,4204,4078,4079,4206,4078,4206,4205,4079,4080,4207,4079,4207,4206,4080,4081,4208,4080,4208,4207,4081,4082,4209,4081,4209,4208,4082,4083,4210,4082,4210,4209,4083,4084,4211,4083,4211,4210,4084,4085,4212,4084,4212,4211,4085,4086,4213,4085,4213,4212,4086,4087,4214,4086,4214,4213,4087,4088,4215,4087,4215,4214,4088,4089,4216,4088,4216,4215,4089,4090,4217,4089,4217,4216,4090,4091,4218,4090,4218,4217,4091,4092,4219,4091,4219,4218,4092,4093,4220,4092,4220,4219,4093,4094,4221,4093,4221,4220,4094,4095,4222,4094,4222,4221,4095,4096,4223,4095,4223,4222,4096,4097,4224,4096,4224,4223,4097,4098,4225,4097,4225,4224,4098,4099,4226,4098,4226,4225,4099,4100,4227,4099,4227,4226,4100,4101,4228,4100,4228,4227,4101,4102,4229,4101,4229,4228,4102,4103,4230,4102,4230,4229,4103,4104,4231,4103,4231,4230,4104,4105,4232,4104,4232,4231,4105,4106,4233,4105,4233,4232,4106,4107,4234,4106,4234,4233,4107,4108,4235,4107,4235,4234,4108,4109,4236,4108,4236,4235,4109,4110,4237,4109,4237,4236,4110,4111,4238,4110,4238,4237,4111,4112,4239,4111,4239,4238,4112,4113,4240,4112,4240,4239,4113,4114,4241,4113,4241,4240,4114,4115,4242,4114,4242,4241,4115,4116,4243,4115,4243,4242,4116,4117,4244,4116,4244,4243,4117,4118,4245,4117,4245,4244,4118,4119,4246,4118,4246,4245,4119,4120,4247,4119,4247,4246,4120,4121,4248,4120,4248,4247,4121,4122,4249,4121,4249,4248,4122,4123,4250,4122,4250,4249,4123,4124,4251,4123,4251,4250,4124,4125,4252,4124,4252,4251,4125,4126,4253,4125,4253,4252,4126,4127,4254,4126,4254,4253,4127,4128,4255,4127,4255,4254,4128,4129,4256,4128,4256,4255,4129,4130,4257,4129,4257,4256,4130,4131,4258,4130,4258,4257,4131,4132,4259,4131,4259,4258,4132,4133,4260,4132,4260,4259,4133,4134,4261,4133,4261,4260,4134,4135,4262,4134,4262,4261,4135,4136,4263,4135,4263,4262,4136,4137,4264,4136,4264,4263,4137,4138,4265,4137,4265,4264,4138,4139,4266,4138,4266,4265,4139,4140,4267,4139,4267,4266,4140,4141,4268,4140,4268,4267,4141,4142,4269,4141,4269,4268,4142,4143,4270,4142,4270,4269,4143,4144,4271,4143,4271,4270,4144,4145,4272,4144,4272,4271,4145,4146,4273,4145,4273,4272,4146,4147,4274,4146,4274,4273,4147,4148,4275,4147,4275,4274,4148,4149,4276,4148,4276,4275,4149,4150,4277,4149,4277,4276,4150,4151,4278,4150,4278,4277,4151,4152,4279,4151,4279,4278,4152,4153,4280,4152,4280,4279,4153,4154,4281,4153,4281,4280,4154,4155,4282,4154,4282,4281,4155,4156,4283,4155,4283,4282,4156,4157,4284,4156,4284,4283,4157,4158,4285,4157,4285,4284,4158,4159,4286,4158,4286,4285,4159,4160,4287,4159,4287,4286,4160,4161,4288,4160,4288,4287,4161,4162,4289,4161,4289,4288,4162,4163,4290,4162,4290,4289,4163,4164,4291,4163,4291,4290,4164,4165,4292,4164,4292,4291,4165,4166,4293,4165,4293,4292,4166,4167,4294,4166,4294,4293,4167,4168,4295,4167,4295,4294,4168,4169,4296,4168,4296,4295,4169,4170,4297,4169,4297,4296,4170,4171,4298,4170,4298,4297,4171,4172,4299,4171,4299,4298,4172,4173,4300,4172,4300,4299,4173,4174,4301,4173,4301,4300,4174,4175,4302,4174,4302,4301,4175,4176,4303,4175,4303,4302,4176,4177,4304,4176,4304,4303,4177,4178,4305,4177,4305,4304,4178,4179,4306,4178,4306,4305,4179,4180,4307,4179,4307,4306,4180,4181,4308,4180,4308,4307,4181,4182,4309,4181,4309,4308,4182,4183,4310,4182,4310,4309,4183,4184,4311,4183,4311,4310,4184,4185,4312,4184,4312,4311,4185,4186,4313,4185,4313,4312,4186,4187,4314,4186,4314,4313,4187,4188,4315,4187,4315,4314,4188,4189,4316,4188,4316,4315,4189,4190,4317,4189,4317,4316,4191,4192,4319,4191,4319,4318,4192,4193,4320,4192,4320,4319,4193,4194,4321,4193,4321,4320,4194,4195,4322,4194,4322,4321,4195,4196,4323,4195,4323,4322,4196,4197,4324,4196,4324,4323,4197,4198,4325,4197,4325,4324,4198,4199,4326,4198,4326,4325,4199,4200,4327,4199,4327,4326,4200,4201,4328,4200,4328,4327,4201,4202,4329,4201,4329,4328,4202,4203,4330,4202,4330,4329,4203,4204,4331,4203,4331,4330,4204,4205,4332,4204,4332,4331,4205,4206,4333,4205,4333,4332,4206,4207,4334,4206,4334,4333,4207,4208,4335,4207,4335,4334,4208,4209,4336,4208,4336,4335,4209,4210,4337,4209,4337,4336,4210,4211,4338,4210,4338,4337,4211,4212,4339,4211,4339,4338,4212,4213,4340,4212,4340,4339,4213,4214,4341,4213,4341,4340,4214,4215,4342,4214,4342,4341,4215,4216,4343,4215,4343,4342,4216,4217,4344,4216,4344,4343,4217,4218,4345,4217,4345,4344,4218,4219,4346,4218,4346,4345,4219,4220,4347,4219,4347,4346,4220,4221,4348,4220,4348,4347,4221,4222,4349,4221,4349,4348,4222,4223,4350,4222,4350,4349,4223,4224,4351,4223,4351,4350,4224,4225,4352,4224,4352,4351,4225,4226,4353,4225,4353,4352,4226,4227,4354,4226,4354,4353,4227,4228,4355,4227,4355,4354,4228,4229,4356,4228,4356,4355,4229,4230,4357,4229,4357,4356,4230,4231,4358,4230,4358,4357,4231,4232,4359,4231,4359,4358,4232,4233,4360,4232,4360,4359,4233,4234,4361,4233,4361,4360,4234,4235,4362,4234,4362,4361,4235,4236,4363,4235,4363,4362,4236,4237,4364,4236,4364,4363,4237,4238,4365,4237,4365,4364,4238,4239,4366,4238,4366,4365,4239,4240,4367,4239,4367,4366,4240,4241,4368,4240,4368,4367,4241,4242,4369,4241,4369,4368,4242,4243,4370,4242,4370,4369,4243,4244,4371,4243,4371,4370,4244,4245,4372,4244,4372,4371,4245,4246,4373,4245,4373,4372,4246,4247,4374,4246,4374,4373,4247,4248,4375,4247,4375,4374,4248,4249,4376,4248,4376,4375,4249,4250,4377,4249,4377,4376,4250,4251,4378,4250,4378,4377,4251,4252,4379,4251,4379,4378,4252,4253,4380,4252,4380,4379,4253,4254,4381,4253,4381,4380,4254,4255,4382,4254,4382,4381,4255,4256,4383,4255,4383,4382,4256,4257,4384,4256,4384,4383,4257,4258,4385,4257,4385,4384,4258,4259,4386,4258,4386,4385,4259,4260,4387,4259,4387,4386,4260,4261,4388,4260,4388,4387,4261,4262,4389,4261,4389,4388,4262,4263,4390,4262,4390,4389,4263,4264,4391,4263,4391,4390,4264,4265,4392,4264,4392,4391,4265,4266,4393,4265,4393,4392,4266,4267,4394,4266,4394,4393,4267,4268,4395,4267,4395,4394,4268,4269,4396,4268,4396,4395,4269,4270,4397,4269,4397,4396,4270,4271,4398,4270,4398,4397,4271,4272,4399,4271,4399,4398,4272,4273,4400,4272,4400,4399,4273,4274,4401,4273,4401,4400,4274,4275,4402,4274,4402,4401,4275,4276,4403,4275,4403,4402,4276,4277,4404,4276,4404,4403,4277,4278,4405,4277,4405,4404,4278,4279,4406,4278,4406,4405,4279,4280,4407,4279,4407,4406,4280,4281,4408,4280,4408,4407,4281,4282,4409,4281,4409,4408,4282,4283,4410,4282,4410,4409,4283,4284,4411,4283,4411,4410,4284,4285,4412,4284,4412,4411,4285,4286,4413,4285,4413,4412,4286,4287,4414,4286,4414,4413,4287,4288,4415,4287,4415,4414,4288,4289,4416,4288,4416,4415,4289,4290,4417,4289,4417,4416,4290,4291,4418,4290,4418,4417,4291,4292,4419,4291,4419,4418,4292,4293,4420,4292,4420,4419,4293,4294,4421,4293,4421,4420,4294,4295,4422,4294,4422,4421,4295,4296,4423,4295,4423,4422,4296,4297,4424,4296,4424,4423,4297,4298,4425,4297,4425,4424,4298,4299,4426,4298,4426,4425,4299,4300,4427,4299,4427,4426,4300,4301,4428,4300,4428,4427,4301,4302,4429,4301,4429,4428,4302,4303,4430,4302,4430,4429,4303,4304,4431,4303,4431,4430,4304,4305,4432,4304,4432,4431,4305,4306,4433,4305,4433,4432,4306,4307,4434,4306,4434,4433,4307,4308,4435,4307,4435,4434,4308,4309,4436,4308,4436,4435,4309,4310,4437,4309,4437,4436,4310,4311,4438,4310,4438,4437,4311,4312,4439,4311,4439,4438,4312,4313,4440,4312,4440,4439,4313,4314,4441,4313,4441,4440,4314,4315,4442,4314,4442,4441,4315,4316,4443,4315,4443,4442,4316,4317,4444,4316,4444,4443,4318,4319,4446,4318,4446,4445,4319,4320,4447,4319,4447,4446,4320,4321,4448,4320,4448,4447,4321,4322,4449,4321,4449,4448,4322,4323,4450,4322,4450,4449,4323,4324,4451,4323,4451,4450,4324,4325,4452,4324,4452,4451,4325,4326,4453,4325,4453,4452,4326,4327,4454,4326,4454,4453,4327,4328,4455,4327,4455,4454,4328,4329,4456,4328,4456,4455,4329,4330,4457,4329,4457,4456,4330,4331,4458,4330,4458,4457,4331,4332,4459,4331,4459,4458,4332,4333,4460,4332,4460,4459,4333,4334,4461,4333,4461,4460,4334,4335,4462,4334,4462,4461,4335,4336,4463,4335,4463,4462,4336,4337,4464,4336,4464,4463,4337,4338,4465,4337,4465,4464,4338,4339,4466,4338,4466,4465,4339,4340,4467,4339,4467,4466,4340,4341,4468,4340,4468,4467,4341,4342,4469,4341,4469,4468,4342,4343,4470,4342,4470,4469,4343,4344,4471,4343,4471,4470,4344,4345,4472,4344,4472,4471,4345,4346,4473,4345,4473,4472,4346,4347,4474,4346,4474,4473,4347,4348,4475,4347,4475,4474,4348,4349,4476,4348,4476,4475,4349,4350,4477,4349,4477,4476,4350,4351,4478,4350,4478,4477,4351,4352,4479,4351,4479,4478,4352,4353,4480,4352,4480,4479,4353,4354,4481,4353,4481,4480,4354,4355,4482,4354,4482,4481,4355,4356,4483,4355,4483,4482,4356,4357,4484,4356,4484,4483,4357,4358,4485,4357,4485,4484,4358,4359,4486,4358,4486,4485,4359,4360,4487,4359,4487,4486,4360,4361,4488,4360,4488,4487,4361,4362,4489,4361,4489,4488,4362,4363,4490,4362,4490,4489,4363,4364,4491,4363,4491,4490,4364,4365,4492,4364,4492,4491,4365,4366,4493,4365,4493,4492,4366,4367,4494,4366,4494,4493,4367,4368,4495,4367,4495,4494,4368,4369,4496,4368,4496,4495,4369,4370,4497,4369,4497,4496,4370,4371,4498,4370,4498,4497,4371,4372,4499,4371,4499,4498,4372,4373,4500,4372,4500,4499,4373,4374,4501,4373,4501,4500,4374,4375,4502,4374,4502,4501,4375,4376,4503,4375,4503,4502,4376,4377,4504,4376,4504,4503,4377,4378,4505,4377,4505,4504,4378,4379,4506,4378,4506,4505,4379,4380,4507,4379,4507,4506,4380,4381,4508,4380,4508,4507,4381,4382,4509,4381,4509,4508,4382,4383,4510,4382,4510,4509,4383,4384,4511,4383,4511,4510,4384,4385,4512,4384,4512,4511,4385,4386,4513,4385,4513,4512,4386,4387,4514,4386,4514,4513,4387,4388,4515,4387,4515,4514,4388,4389,4516,4388,4516,4515,4389,4390,4517,4389,4517,4516,4390,4391,4518,4390,4518,4517,4391,4392,4519,4391,4519,4518,4392,4393,4520,4392,4520,4519,4393,4394,4521,4393,4521,4520,4394,4395,4522,4394,4522,4521,4395,4396,4523,4395,4523,4522,4396,4397,4524,4396,4524,4523,4397,4398,4525,4397,4525,4524,4398,4399,4526,4398,4526,4525,4399,4400,4527,4399,4527,4526,4400,4401,4528,4400,4528,4527,4401,4402,4529,4401,4529,4528,4402,4403,4530,4402,4530,4529,4403,4404,4531,4403,4531,4530,4404,4405,4532,4404,4532,4531,4405,4406,4533,4405,4533,4532,4406,4407,4534,4406,4534,4533,4407,4408,4535,4407,4535,4534,4408,4409,4536,4408,4536,4535,4409,4410,4537,4409,4537,4536,4410,4411,4538,4410,4538,4537,4411,4412,4539,4411,4539,4538,4412,4413,4540,4412,4540,4539,4413,4414,4541,4413,4541,4540,4414,4415,4542,4414,4542,4541,4415,4416,4543,4415,4543,4542,4416,4417,4544,4416,4544,4543,4417,4418,4545,4417,4545,4544,4418,4419,4546,4418,4546,4545,4419,4420,4547,4419,4547,4546,4420,4421,4548,4420,4548,4547,4421,4422,4549,4421,4549,4548,4422,4423,4550,4422,4550,4549,4423,4424,4551,4423,4551,4550,4424,4425,4552,4424,4552,4551,4425,4426,4553,4425,4553,4552,4426,4427,4554,4426,4554,4553,4427,4428,4555,4427,4555,4554,4428,4429,4556,4428,4556,4555,4429,4430,4557,4429,4557,4556,4430,4431,4558,4430,4558,4557,4431,4432,4559,4431,4559,4558,4432,4433,4560,4432,4560,4559,4433,4434,4561,4433,4561,4560,4434,4435,4562,4434,4562,4561,4435,4436,4563,4435,4563,4562,4436,4437,4564,4436,4564,4563,4437,4438,4565,4437,4565,4564,4438,4439,4566,4438,4566,4565,4439,4440,4567,4439,4567,4566,4440,4441,4568,4440,4568,4567,4441,4442,4569,4441,4569,4568,4442,4443,4570,4442,4570,4569,4443,4444,4571,4443,4571,4570,4445,4446,4573,4445,4573,4572,4446,4447,4574,4446,4574,4573,4447,4448,4575,4447,4575,4574,4448,4449,4576,4448,4576,4575,4449,4450,4577,4449,4577,4576,4450,4451,4578,4450,4578,4577,4451,4452,4579,4451,4579,4578,4452,4453,4580,4452,4580,4579,4453,4454,4581,4453,4581,4580,4454,4455,4582,4454,4582,4581,4455,4456,4583,4455,4583,4582,4456,4457,4584,4456,4584,4583,4457,4458,4585,4457,4585,4584,4458,4459,4586,4458,4586,4585,4459,4460,4587,4459,4587,4586,4460,4461,4588,4460,4588,4587,4461,4462,4589,4461,4589,4588,4462,4463,4590,4462,4590,4589,4463,4464,4591,4463,4591,4590,4464,4465,4592,4464,4592,4591,4465,4466,4593,4465,4593,4592,4466,4467,4594,4466,4594,4593,4467,4468,4595,4467,4595,4594,4468,4469,4596,4468,4596,4595,4469,4470,4597,4469,4597,4596,4470,4471,4598,4470,4598,4597,4471,4472,4599,4471,4599,4598,4472,4473,4600,4472,4600,4599,4473,4474,4601,4473,4601,4600,4474,4475,4602,4474,4602,4601,4475,4476,4603,4475,4603,4602,4476,4477,4604,4476,4604,4603,4477,4478,4605,4477,4605,4604,4478,4479,4606,4478,4606,4605,4479,4480,4607,4479,4607,4606,4480,4481,4608,4480,4608,4607,4481,4482,4609,4481,4609,4608,4482,4483,4610,4482,4610,4609,4483,4484,4611,4483,4611,4610,4484,4485,4612,4484,4612,4611,4485,4486,4613,4485,4613,4612,4486,4487,4614,4486,4614,4613,4487,4488,4615,4487,4615,4614,4488,4489,4616,4488,4616,4615,4489,4490,4617,4489,4617,4616,4490,4491,4618,4490,4618,4617,4491,4492,4619,4491,4619,4618,4492,4493,4620,4492,4620,4619,4493,4494,4621,4493,4621,4620,4494,4495,4622,4494,4622,4621,4495,4496,4623,4495,4623,4622,4496,4497,4624,4496,4624,4623,4497,4498,4625,4497,4625,4624,4498,4499,4626,4498,4626,4625,4499,4500,4627,4499,4627,4626,4500,4501,4628,4500,4628,4627,4501,4502,4629,4501,4629,4628,4502,4503,4630,4502,4630,4629,4503,4504,4631,4503,4631,4630,4504,4505,4632,4504,4632,4631,4505,4506,4633,4505,4633,4632,4506,4507,4634,4506,4634,4633,4507,4508,4635,4507,4635,4634,4508,4509,4636,4508,4636,4635,4509,4510,4637,4509,4637,4636,4510,4511,4638,4510,4638,4637,4511,4512,4639,4511,4639,4638,4512,4513,4640,4512,4640,4639,4513,4514,4641,4513,4641,4640,4514,4515,4642,4514,4642,4641,4515,4516,4643,4515,4643,4642,4516,4517,4644,4516,4644,4643,4517,4518,4645,4517,4645,4644,4518,4519,4646,4518,4646,4645,4519,4520,4647,4519,4647,4646,4520,4521,4648,4520,4648,4647,4521,4522,4649,4521,4649,4648,4522,4523,4650,4522,4650,4649,4523,4524,4651,4523,4651,4650,4524,4525,4652,4524,4652,4651,4525,4526,4653,4525,4653,4652,4526,4527,4654,4526,4654,4653,4527,4528,4655,4527,4655,4654,4528,4529,4656,4528,4656,4655,4529,4530,4657,4529,4657,4656,4530,4531,4658,4530,4658,4657,4531,4532,4659,4531,4659,4658,4532,4533,4660,4532,4660,4659,4533,4534,4661,4533,4661,4660,4534,4535,4662,4534,4662,4661,4535,4536,4663,4535,4663,4662,4536,4537,4664,4536,4664,4663,4537,4538,4665,4537,4665,4664,4538,4539,4666,4538,4666,4665,4539,4540,4667,4539,4667,4666,4540,4541,4668,4540,4668,4667,4541,4542,4669,4541,4669,4668,4542,4543,4670,4542,4670,4669,4543,4544,4671,4543,4671,4670,4544,4545,4672,4544,4672,4671,4545,4546,4673,4545,4673,4672,4546,4547,4674,4546,4674,4673,4547,4548,4675,4547,4675,4674,4548,4549,4676,4548,4676,4675,4549,4550,4677,4549,4677,4676,4550,4551,4678,4550,4678,4677,4551,4552,4679,4551,4679,4678,4552,4553,4680,4552,4680,4679,4553,4554,4681,4553,4681,4680,4554,4555,4682,4554,4682,4681,4555,4556,4683,4555,4683,4682,4556,4557,4684,4556,4684,4683,4557,4558,4685,4557,4685,4684,4558,4559,4686,4558,4686,4685,4559,4560,4687,4559,4687,4686,4560,4561,4688,4560,4688,4687,4561,4562,4689,4561,4689,4688,4562,4563,4690,4562,4690,4689,4563,4564,4691,4563,4691,4690,4564,4565,4692,4564,4692,4691,4565,4566,4693,4565,4693,4692,4566,4567,4694,4566,4694,4693,4567,4568,4695,4567,4695,4694,4568,4569,4696,4568,4696,4695,4569,4570,4697,4569,4697,4696,4570,4571,4698,4570,4698,4697,4572,4573,4700,4572,4700,4699,4573,4574,4701,4573,4701,4700,4574,4575,4702,4574,4702,4701,4575,4576,4703,4575,4703,4702,4576,4577,4704,4576,4704,4703,4577,4578,4705,4577,4705,4704,4578,4579,4706,4578,4706,4705,4579,4580,4707,4579,4707,4706,4580,4581,4708,4580,4708,4707,4581,4582,4709,4581,4709,4708,4582,4583,4710,4582,4710,4709,4583,4584,4711,4583,4711,4710,4584,4585,4712,4584,4712,4711,4585,4586,4713,4585,4713,4712,4586,4587,4714,4586,4714,4713,4587,4588,4715,4587,4715,4714,4588,4589,4716,4588,4716,4715,4589,4590,4717,4589,4717,4716,4590,4591,4718,4590,4718,4717,4591,4592,4719,4591,4719,4718,4592,4593,4720,4592,4720,4719,4593,4594,4721,4593,4721,4720,4594,4595,4722,4594,4722,4721,4595,4596,4723,4595,4723,4722,4596,4597,4724,4596,4724,4723,4597,4598,4725,4597,4725,4724,4598,4599,4726,4598,4726,4725,4599,4600,4727,4599,4727,4726,4600,4601,4728,4600,4728,4727,4601,4602,4729,4601,4729,4728,4602,4603,4730,4602,4730,4729,4603,4604,4731,4603,4731,4730,4604,4605,4732,4604,4732,4731,4605,4606,4733,4605,4733,4732,4606,4607,4734,4606,4734,4733,4607,4608,4735,4607,4735,4734,4608,4609,4736,4608,4736,4735,4609,4610,4737,4609,4737,4736,4610,4611,4738,4610,4738,4737,4611,4612,4739,4611,4739,4738,4612,4613,4740,4612,4740,4739,4613,4614,4741,4613,4741,4740,4614,4615,4742,4614,4742,4741,4615,4616,4743,4615,4743,4742,4616,4617,4744,4616,4744,4743,4617,4618,4745,4617,4745,4744,4618,4619,4746,4618,4746,4745,4619,4620,4747,4619,4747,4746,4620,4621,4748,4620,4748,4747,4621,4622,4749,4621,4749,4748,4622,4623,4750,4622,4750,4749,4623,4624,4751,4623,4751,4750,4624,4625,4752,4624,4752,4751,4625,4626,4753,4625,4753,4752,4626,4627,4754,4626,4754,4753,4627,4628,4755,4627,4755,4754,4628,4629,4756,4628,4756,4755,4629,4630,4757,4629,4757,4756,4630,4631,4758,4630,4758,4757,4631,4632,4759,4631,4759,4758,4632,4633,4760,4632,4760,4759,4633,4634,4761,4633,4761,4760,4634,4635,4762,4634,4762,4761,4635,4636,4763,4635,4763,4762,4636,4637,4764,4636,4764,4763,4637,4638,4765,4637,4765,4764,4638,4639,4766,4638,4766,4765,4639,4640,4767,4639,4767,4766,4640,4641,4768,4640,4768,4767,4641,4642,4769,4641,4769,4768,4642,4643,4770,4642,4770,4769,4643,4644,4771,4643,4771,4770,4644,4645,4772,4644,4772,4771,4645,4646,4773,4645,4773,4772,4646,4647,4774,4646,4774,4773,4647,4648,4775,4647,4775,4774,4648,4649,4776,4648,4776,4775,4649,4650,4777,4649,4777,4776,4650,4651,4778,4650,4778,4777,4651,4652,4779,4651,4779,4778,4652,4653,4780,4652,4780,4779,4653,4654,4781,4653,4781,4780,4654,4655,4782,4654,4782,4781,4655,4656,4783,4655,4783,4782,4656,4657,4784,4656,4784,4783,4657,4658,4785,4657,4785,4784,4658,4659,4786,4658,4786,4785,4659,4660,4787,4659,4787,4786,4660,4661,4788,4660,4788,4787,4661,4662,4789,4661,4789,4788,4662,4663,4790,4662,4790,4789,4663,4664,4791,4663,4791,4790,4664,4665,4792,4664,4792,4791,4665,4666,4793,4665,4793,4792,4666,4667,4794,4666,4794,4793,4667,4668,4795,4667,4795,4794,4668,4669,4796,4668,4796,4795,4669,4670,4797,4669,4797,4796,4670,4671,4798,4670,4798,4797,4671,4672,4799,4671,4799,4798,4672,4673,4800,4672,4800,4799,4673,4674,4801,4673,4801,4800,4674,4675,4802,4674,4802,4801,4675,4676,4803,4675,4803,4802,4676,4677,4804,4676,4804,4803,4677,4678,4805,4677,4805,4804,4678,4679,4806,4678,4806,4805,4679,4680,4807,4679,4807,4806,4680,4681,4808,4680,4808,4807,4681,4682,4809,4681,4809,4808,4682,4683,4810,4682,4810,4809,4683,4684,4811,4683,4811,4810,4684,4685,4812,4684,4812,4811,4685,4686,4813,4685,4813,4812,4686,4687,4814,4686,4814,4813,4687,4688,4815,4687,4815,4814,4688,4689,4816,4688,4816,4815,4689,4690,4817,4689,4817,4816,4690,4691,4818,4690,4818,4817,4691,4692,4819,4691,4819,4818,4692,4693,4820,4692,4820,4819,4693,4694,4821,4693,4821,4820,4694,4695,4822,4694,4822,4821,4695,4696,4823,4695,4823,4822,4696,4697,4824,4696,4824,4823,4697,4698,4825,4697,4825,4824,4699,4700,4827,4699,4827,4826,4700,4701,4828,4700,4828,4827,4701,4702,4829,4701,4829,4828,4702,4703,4830,4702,4830,4829,4703,4704,4831,4703,4831,4830,4704,4705,4832,4704,4832,4831,4705,4706,4833,4705,4833,4832,4706,4707,4834,4706,4834,4833,4707,4708,4835,4707,4835,4834,4708,4709,4836,4708,4836,4835,4709,4710,4837,4709,4837,4836,4710,4711,4838,4710,4838,4837,4711,4712,4839,4711,4839,4838,4712,4713,4840,4712,4840,4839,4713,4714,4841,4713,4841,4840,4714,4715,4842,4714,4842,4841,4715,4716,4843,4715,4843,4842,4716,4717,4844,4716,4844,4843,4717,4718,4845,4717,4845,4844,4718,4719,4846,4718,4846,4845,4719,4720,4847,4719,4847,4846,4720,4721,4848,4720,4848,4847,4721,4722,4849,4721,4849,4848,4722,4723,4850,4722,4850,4849,4723,4724,4851,4723,4851,4850,4724,4725,4852,4724,4852,4851,4725,4726,4853,4725,4853,4852,4726,4727,4854,4726,4854,4853,4727,4728,4855,4727,4855,4854,4728,4729,4856,4728,4856,4855,4729,4730,4857,4729,4857,4856,4730,4731,4858,4730,4858,4857,4731,4732,4859,4731,4859,4858,4732,4733,4860,4732,4860,4859,4733,4734,4861,4733,4861,4860,4734,4735,4862,4734,4862,4861,4735,4736,4863,4735,4863,4862,4736,4737,4864,4736,4864,4863,4737,4738,4865,4737,4865,4864,4738,4739,4866,4738,4866,4865,4739,4740,4867,4739,4867,4866,4740,4741,4868,4740,4868,4867,4741,4742,4869,4741,4869,4868,4742,4743,4870,4742,4870,4869,4743,4744,4871,4743,4871,4870,4744,4745,4872,4744,4872,4871,4745,4746,4873,4745,4873,4872,4746,4747,4874,4746,4874,4873,4747,4748,4875,4747,4875,4874,4748,4749,4876,4748,4876,4875,4749,4750,4877,4749,4877,4876,4750,4751,4878,4750,4878,4877,4751,4752,4879,4751,4879,4878,4752,4753,4880,4752,4880,4879,4753,4754,4881,4753,4881,4880,4754,4755,4882,4754,4882,4881,4755,4756,4883,4755,4883,4882,4756,4757,4884,4756,4884,4883,4757,4758,4885,4757,4885,4884,4758,4759,4886,4758,4886,4885,4759,4760,4887,4759,4887,4886,4760,4761,4888,4760,4888,4887,4761,4762,4889,4761,4889,4888,4762,4763,4890,4762,4890,4889,4763,4764,4891,4763,4891,4890,4764,4765,4892,4764,4892,4891,4765,4766,4893,4765,4893,4892,4766,4767,4894,4766,4894,4893,4767,4768,4895,4767,4895,4894,4768,4769,4896,4768,4896,4895,4769,4770,4897,4769,4897,4896,4770,4771,4898,4770,4898,4897,4771,4772,4899,4771,4899,4898,4772,4773,4900,4772,4900,4899,4773,4774,4901,4773,4901,4900,4774,4775,4902,4774,4902,4901,4775,4776,4903,4775,4903,4902,4776,4777,4904,4776,4904,4903,4777,4778,4905,4777,4905,4904,4778,4779,4906,4778,4906,4905,4779,4780,4907,4779,4907,4906,4780,4781,4908,4780,4908,4907,4781,4782,4909,4781,4909,4908,4782,4783,4910,4782,4910,4909,4783,4784,4911,4783,4911,4910,4784,4785,4912,4784,4912,4911,4785,4786,4913,4785,4913,4912,4786,4787,4914,4786,4914,4913,4787,4788,4915,4787,4915,4914,4788,4789,4916,4788,4916,4915,4789,4790,4917,4789,4917,4916,4790,4791,4918,4790,4918,4917,4791,4792,4919,4791,4919,4918,4792,4793,4920,4792,4920,4919,4793,4794,4921,4793,4921,4920,4794,4795,4922,4794,4922,4921,4795,4796,4923,4795,4923,4922,4796,4797,4924,4796,4924,4923,4797,4798,4925,4797,4925,4924,4798,4799,4926,4798,4926,4925,4799,4800,4927,4799,4927,4926,4800,4801,4928,4800,4928,4927,4801,4802,4929,4801,4929,4928,4802,4803,4930,4802,4930,4929,4803,4804,4931,4803,4931,4930,4804,4805,4932,4804,4932,4931,4805,4806,4933,4805,4933,4932,4806,4807,4934,4806,4934,4933,4807,4808,4935,4807,4935,4934,4808,4809,4936,4808,4936,4935,4809,4810,4937,4809,4937,4936,4810,4811,4938,4810,4938,4937,4811,4812,4939,4811,4939,4938,4812,4813,4940,4812,4940,4939,4813,4814,4941,4813,4941,4940,4814,4815,4942,4814,4942,4941,4815,4816,4943,4815,4943,4942,4816,4817,4944,4816,4944,4943,4817,4818,4945,4817,4945,4944,4818,4819,4946,4818,4946,4945,4819,4820,4947,4819,4947,4946,4820,4821,4948,4820,4948,4947,4821,4822,4949,4821,4949,4948,4822,4823,4950,4822,4950,4949,4823,4824,4951,4823,4951,4950,4824,4825,4952,4824,4952,4951,4826,4827,4954,4826,4954,4953,4827,4828,4955,4827,4955,4954,4828,4829,4956,4828,4956,4955,4829,4830,4957,4829,4957,4956,4830,4831,4958,4830,4958,4957,4831,4832,4959,4831,4959,4958,4832,4833,4960,4832,4960,4959,4833,4834,4961,4833,4961,4960,4834,4835,4962,4834,4962,4961,4835,4836,4963,4835,4963,4962,4836,4837,4964,4836,4964,4963,4837,4838,4965,4837,4965,4964,4838,4839,4966,4838,4966,4965,4839,4840,4967,4839,4967,4966,4840,4841,4968,4840,4968,4967,4841,4842,4969,4841,4969,4968,4842,4843,4970,4842,4970,4969,4843,4844,4971,4843,4971,4970,4844,4845,4972,4844,4972,4971,4845,4846,4973,4845,4973,4972,4846,4847,4974,4846,4974,4973,4847,4848,4975,4847,4975,4974,4848,4849,4976,4848,4976,4975,4849,4850,4977,4849,4977,4976,4850,4851,4978,4850,4978,4977,4851,4852,4979,4851,4979,4978,4852,4853,4980,4852,4980,4979,4853,4854,4981,4853,4981,4980,4854,4855,4982,4854,4982,4981,4855,4856,4983,4855,4983,4982,4856,4857,4984,4856,4984,4983,4857,4858,4985,4857,4985,4984,4858,4859,4986,4858,4986,4985,4859,4860,4987,4859,4987,4986,4860,4861,4988,4860,4988,4987,4861,4862,4989,4861,4989,4988,4862,4863,4990,4862,4990,4989,4863,4864,4991,4863,4991,4990,4864,4865,4992,4864,4992,4991,4865,4866,4993,4865,4993,4992,4866,4867,4994,4866,4994,4993,4867,4868,4995,4867,4995,4994,4868,4869,4996,4868,4996,4995,4869,4870,4997,4869,4997,4996,4870,4871,4998,4870,4998,4997,4871,4872,4999,4871,4999,4998,4872,4873,5000,4872,5000,4999,4873,4874,5001,4873,5001,5000,4874,4875,5002,4874,5002,5001,4875,4876,5003,4875,5003,5002,4876,4877,5004,4876,5004,5003,4877,4878,5005,4877,5005,5004,4878,4879,5006,4878,5006,5005,4879,4880,5007,4879,5007,5006,4880,4881,5008,4880,5008,5007,4881,4882,5009,4881,5009,5008,4882,4883,5010,4882,5010,5009,4883,4884,5011,4883,5011,5010,4884,4885,5012,4884,5012,5011,4885,4886,5013,4885,5013,5012,4886,4887,5014,4886,5014,5013,4887,4888,5015,4887,5015,5014,4888,4889,5016,4888,5016,5015,4889,4890,5017,4889,5017,5016,4890,4891,5018,4890,5018,5017,4891,4892,5019,4891,5019,5018,4892,4893,5020,4892,5020,5019,4893,4894,5021,4893,5021,5020,4894,4895,5022,4894,5022,5021,4895,4896,5023,4895,5023,5022,4896,4897,5024,4896,5024,5023,4897,4898,5025,4897,5025,5024,4898,4899,5026,4898,5026,5025,4899,4900,5027,4899,5027,5026,4900,4901,5028,4900,5028,5027,4901,4902,5029,4901,5029,5028,4902,4903,5030,4902,5030,5029,4903,4904,5031,4903,5031,5030,4904,4905,5032,4904,5032,5031,4905,4906,5033,4905,5033,5032,4906,4907,5034,4906,5034,5033,4907,4908,5035,4907,5035,5034,4908,4909,5036,4908,5036,5035,4909,4910,5037,4909,5037,5036,4910,4911,5038,4910,5038,5037,4911,4912,5039,4911,5039,5038,4912,4913,5040,4912,5040,5039,4913,4914,5041,4913,5041,5040,4914,4915,5042,4914,5042,5041,4915,4916,5043,4915,5043,5042,4916,4917,5044,4916,5044,5043,4917,4918,5045,4917,5045,5044,4918,4919,5046,4918,5046,5045,4919,4920,5047,4919,5047,5046,4920,4921,5048,4920,5048,5047,4921,4922,5049,4921,5049,5048,4922,4923,5050,4922,5050,5049,4923,4924,5051,4923,5051,5050,4924,4925,5052,4924,5052,5051,4925,4926,5053,4925,5053,5052,4926,4927,5054,4926,5054,5053,4927,4928,5055,4927,5055,5054,4928,4929,5056,4928,5056,5055,4929,4930,5057,4929,5057,5056,4930,4931,5058,4930,5058,5057,4931,4932,5059,4931,5059,5058,4932,4933,5060,4932,5060,5059,4933,4934,5061,4933,5061,5060,4934,4935,5062,4934,5062,5061,4935,4936,5063,4935,5063,5062,4936,4937,5064,4936,5064,5063,4937,4938,5065,4937,5065,5064,4938,4939,5066,4938,5066,5065,4939,4940,5067,4939,5067,5066,4940,4941,5068,4940,5068,5067,4941,4942,5069,4941,5069,5068,4942,4943,5070,4942,5070,5069,4943,4944,5071,4943,5071,5070,4944,4945,5072,4944,5072,5071,4945,4946,5073,4945,5073,5072,4946,4947,5074,4946,5074,5073,4947,4948,5075,4947,5075,5074,4948,4949,5076,4948,5076,5075,4949,4950,5077,4949,5077,5076,4950,4951,5078,4950,5078,5077,4951,4952,5079,4951,5079,5078,4953,4954,5081,4953,5081,5080,4954,4955,5082,4954,5082,5081,4955,4956,5083,4955,5083,5082,4956,4957,5084,4956,5084,5083,4957,4958,5085,4957,5085,5084,4958,4959,5086,4958,5086,5085,4959,4960,5087,4959,5087,5086,4960,4961,5088,4960,5088,5087,4961,4962,5089,4961,5089,5088,4962,4963,5090,4962,5090,5089,4963,4964,5091,4963,5091,5090,4964,4965,5092,4964,5092,5091,4965,4966,5093,4965,5093,5092,4966,4967,5094,4966,5094,5093,4967,4968,5095,4967,5095,5094,4968,4969,5096,4968,5096,5095,4969,4970,5097,4969,5097,5096,4970,4971,5098,4970,5098,5097,4971,4972,5099,4971,5099,5098,4972,4973,5100,4972,5100,5099,4973,4974,5101,4973,5101,5100,4974,4975,5102,4974,5102,5101,4975,4976,5103,4975,5103,5102,4976,4977,5104,4976,5104,5103,4977,4978,5105,4977,5105,5104,4978,4979,5106,4978,5106,5105,4979,4980,5107,4979,5107,5106,4980,4981,5108,4980,5108,5107,4981,4982,5109,4981,5109,5108,4982,4983,5110,4982,5110,5109,4983,4984,5111,4983,5111,5110,4984,4985,5112,4984,5112,5111,4985,4986,5113,4985,5113,5112,4986,4987,5114,4986,5114,5113,4987,4988,5115,4987,5115,5114,4988,4989,5116,4988,5116,5115,4989,4990,5117,4989,5117,5116,4990,4991,5118,4990,5118,5117,4991,4992,5119,4991,5119,5118,4992,4993,5120,4992,5120,5119,4993,4994,5121,4993,5121,5120,4994,4995,5122,4994,5122,5121,4995,4996,5123,4995,5123,5122,4996,4997,5124,4996,5124,5123,4997,4998,5125,4997,5125,5124,4998,4999,5126,4998,5126,5125,4999,5000,5127,4999,5127,5126,5000,5001,5128,5000,5128,5127,5001,5002,5129,5001,5129,5128,5002,5003,5130,5002,5130,5129,5003,5004,5131,5003,5131,5130,5004,5005,5132,5004,5132,5131,5005,5006,5133,5005,5133,5132,5006,5007,5134,5006,5134,5133,5007,5008,5135,5007,5135,5134,5008,5009,5136,5008,5136,5135,5009,5010,5137,5009,5137,5136,5010,5011,5138,5010,5138,5137,5011,5012,5139,5011,5139,5138,5012,5013,5140,5012,5140,5139,5013,5014,5141,5013,5141,5140,5014,5015,5142,5014,5142,5141,5015,5016,5143,5015,5143,5142,5016,5017,5144,5016,5144,5143,5017,5018,5145,5017,5145,5144,5018,5019,5146,5018,5146,5145,5019,5020,5147,5019,5147,5146,5020,5021,5148,5020,5148,5147,5021,5022,5149,5021,5149,5148,5022,5023,5150,5022,5150,5149,5023,5024,5151,5023,5151,5150,5024,5025,5152,5024,5152,5151,5025,5026,5153,5025,5153,5152,5026,5027,5154,5026,5154,5153,5027,5028,5155,5027,5155,5154,5028,5029,5156,5028,5156,5155,5029,5030,5157,5029,5157,5156,5030,5031,5158,5030,5158,5157,5031,5032,5159,5031,5159,5158,5032,5033,5160,5032,5160,5159,5033,5034,5161,5033,5161,5160,5034,5035,5162,5034,5162,5161,5035,5036,5163,5035,5163,5162,5036,5037,5164,5036,5164,5163,5037,5038,5165,5037,5165,5164,5038,5039,5166,5038,5166,5165,5039,5040,5167,5039,5167,5166,5040,5041,5168,5040,5168,5167,5041,5042,5169,5041,5169,5168,5042,5043,5170,5042,5170,5169,5043,5044,5171,5043,5171,5170,5044,5045,5172,5044,5172,5171,5045,5046,5173,5045,5173,5172,5046,5047,5174,5046,5174,5173,5047,5048,5175,5047,5175,5174,5048,5049,5176,5048,5176,5175,5049,5050,5177,5049,5177,5176,5050,5051,5178,5050,5178,5177,5051,5052,5179,5051,5179,5178,5052,5053,5180,5052,5180,5179,5053,5054,5181,5053,5181,5180,5054,5055,5182,5054,5182,5181,5055,5056,5183,5055,5183,5182,5056,5057,5184,5056,5184,5183,5057,5058,5185,5057,5185,5184,5058,5059,5186,5058,5186,5185,5059,5060,5187,5059,5187,5186,5060,5061,5188,5060,5188,5187,5061,5062,5189,5061,5189,5188,5062,5063,5190,5062,5190,5189,5063,5064,5191,5063,5191,5190,5064,5065,5192,5064,5192,5191,5065,5066,5193,5065,5193,5192,5066,5067,5194,5066,5194,5193,5067,5068,5195,5067,5195,5194,5068,5069,5196,5068,5196,5195,5069,5070,5197,5069,5197,5196,5070,5071,5198,5070,5198,5197,5071,5072,5199,5071,5199,5198,5072,5073,5200,5072,5200,5199,5073,5074,5201,5073,5201,5200,5074,5075,5202,5074,5202,5201,5075,5076,5203,5075,5203,5202,5076,5077,5204,5076,5204,5203,5077,5078,5205,5077,5205,5204,5078,5079,5206,5078,5206,5205,5080,5081,5208,5080,5208,5207,5081,5082,5209,5081,5209,5208,5082,5083,5210,5082,5210,5209,5083,5084,5211,5083,5211,5210,5084,5085,5212,5084,5212,5211,5085,5086,5213,5085,5213,5212,5086,5087,5214,5086,5214,5213,5087,5088,5215,5087,5215,5214,5088,5089,5216,5088,5216,5215,5089,5090,5217,5089,5217,5216,5090,5091,5218,5090,5218,5217,5091,5092,5219,5091,5219,5218,5092,5093,5220,5092,5220,5219,5093,5094,5221,5093,5221,5220,5094,5095,5222,5094,5222,5221,5095,5096,5223,5095,5223,5222,5096,5097,5224,5096,5224,5223,5097,5098,5225,5097,5225,5224,5098,5099,5226,5098,5226,5225,5099,5100,5227,5099,5227,5226,5100,5101,5228,5100,5228,5227,5101,5102,5229,5101,5229,5228,5102,5103,5230,5102,5230,5229,5103,5104,5231,5103,5231,5230,5104,5105,5232,5104,5232,5231,5105,5106,5233,5105,5233,5232,5106,5107,5234,5106,5234,5233,5107,5108,5235,5107,5235,5234,5108,5109,5236,5108,5236,5235,5109,5110,5237,5109,5237,5236,5110,5111,5238,5110,5238,5237,5111,5112,5239,5111,5239,5238,5112,5113,5240,5112,5240,5239,5113,5114,5241,5113,5241,5240,5114,5115,5242,5114,5242,5241,5115,5116,5243,5115,5243,5242,5116,5117,5244,5116,5244,5243,5117,5118,5245,5117,5245,5244,5118,5119,5246,5118,5246,5245,5119,5120,5247,5119,5247,5246,5120,5121,5248,5120,5248,5247,5121,5122,5249,5121,5249,5248,5122,5123,5250,5122,5250,5249,5123,5124,5251,5123,5251,5250,5124,5125,5252,5124,5252,5251,5125,5126,5253,5125,5253,5252,5126,5127,5254,5126,5254,5253,5127,5128,5255,5127,5255,5254,5128,5129,5256,5128,5256,5255,5129,5130,5257,5129,5257,5256,5130,5131,5258,5130,5258,5257,5131,5132,5259,5131,5259,5258,5132,5133,5260,5132,5260,5259,5133,5134,5261,5133,5261,5260,5134,5135,5262,5134,5262,5261,5135,5136,5263,5135,5263,5262,5136,5137,5264,5136,5264,5263,5137,5138,5265,5137,5265,5264,5138,5139,5266,5138,5266,5265,5139,5140,5267,5139,5267,5266,5140,5141,5268,5140,5268,5267,5141,5142,5269,5141,5269,5268,5142,5143,5270,5142,5270,5269,5143,5144,5271,5143,5271,5270,5144,5145,5272,5144,5272,5271,5145,5146,5273,5145,5273,5272,5146,5147,5274,5146,5274,5273,5147,5148,5275,5147,5275,5274,5148,5149,5276,5148,5276,5275,5149,5150,5277,5149,5277,5276,5150,5151,5278,5150,5278,5277,5151,5152,5279,5151,5279,5278,5152,5153,5280,5152,5280,5279,5153,5154,5281,5153,5281,5280,5154,5155,5282,5154,5282,5281,5155,5156,5283,5155,5283,5282,5156,5157,5284,5156,5284,5283,5157,5158,5285,5157,5285,5284,5158,5159,5286,5158,5286,5285,5159,5160,5287,5159,5287,5286,5160,5161,5288,5160,5288,5287,5161,5162,5289,5161,5289,5288,5162,5163,5290,5162,5290,5289,5163,5164,5291,5163,5291,5290,5164,5165,5292,5164,5292,5291,5165,5166,5293,5165,5293,5292,5166,5167,5294,5166,5294,5293,5167,5168,5295,5167,5295,5294,5168,5169,5296,5168,5296,5295,5169,5170,5297,5169,5297,5296,5170,5171,5298,5170,5298,5297,5171,5172,5299,5171,5299,5298,5172,5173,5300,5172,5300,5299,5173,5174,5301,5173,5301,5300,5174,5175,5302,5174,5302,5301,5175,5176,5303,5175,5303,5302,5176,5177,5304,5176,5304,5303,5177,5178,5305,5177,5305,5304,5178,5179,5306,5178,5306,5305,5179,5180,5307,5179,5307,5306,5180,5181,5308,5180,5308,5307,5181,5182,5309,5181,5309,5308,5182,5183,5310,5182,5310,5309,5183,5184,5311,5183,5311,5310,5184,5185,5312,5184,5312,5311,5185,5186,5313,5185,5313,5312,5186,5187,5314,5186,5314,5313,5187,5188,5315,5187,5315,5314,5188,5189,5316,5188,5316,5315,5189,5190,5317,5189,5317,5316,5190,5191,5318,5190,5318,5317,5191,5192,5319,5191,5319,5318,5192,5193,5320,5192,5320,5319,5193,5194,5321,5193,5321,5320,5194,5195,5322,5194,5322,5321,5195,5196,5323,5195,5323,5322,5196,5197,5324,5196,5324,5323,5197,5198,5325,5197,5325,5324,5198,5199,5326,5198,5326,5325,5199,5200,5327,5199,5327,5326,5200,5201,5328,5200,5328,5327,5201,5202,5329,5201,5329,5328,5202,5203,5330,5202,5330,5329,5203,5204,5331,5203,5331,5330,5204,5205,5332,5204,5332,5331,5205,5206,5333,5205,5333,5332,5207,5208,5335,5207,5335,5334,5208,5209,5336,5208,5336,5335,5209,5210,5337,5209,5337,5336,5210,5211,5338,5210,5338,5337,5211,5212,5339,5211,5339,5338,5212,5213,5340,5212,5340,5339,5213,5214,5341,5213,5341,5340,5214,5215,5342,5214,5342,5341,5215,5216,5343,5215,5343,5342,5216,5217,5344,5216,5344,5343,5217,5218,5345,5217,5345,5344,5218,5219,5346,5218,5346,5345,5219,5220,5347,5219,5347,5346,5220,5221,5348,5220,5348,5347,5221,5222,5349,5221,5349,5348,5222,5223,5350,5222,5350,5349,5223,5224,5351,5223,5351,5350,5224,5225,5352,5224,5352,5351,5225,5226,5353,5225,5353,5352,5226,5227,5354,5226,5354,5353,5227,5228,5355,5227,5355,5354,5228,5229,5356,5228,5356,5355,5229,5230,5357,5229,5357,5356,5230,5231,5358,5230,5358,5357,5231,5232,5359,5231,5359,5358,5232,5233,5360,5232,5360,5359,5233,5234,5361,5233,5361,5360,5234,5235,5362,5234,5362,5361,5235,5236,5363,5235,5363,5362,5236,5237,5364,5236,5364,5363,5237,5238,5365,5237,5365,5364,5238,5239,5366,5238,5366,5365,5239,5240,5367,5239,5367,5366,5240,5241,5368,5240,5368,5367,5241,5242,5369,5241,5369,5368,5242,5243,5370,5242,5370,5369,5243,5244,5371,5243,5371,5370,5244,5245,5372,5244,5372,5371,5245,5246,5373,5245,5373,5372,5246,5247,5374,5246,5374,5373,5247,5248,5375,5247,5375,5374,5248,5249,5376,5248,5376,5375,5249,5250,5377,5249,5377,5376,5250,5251,5378,5250,5378,5377,5251,5252,5379,5251,5379,5378,5252,5253,5380,5252,5380,5379,5253,5254,5381,5253,5381,5380,5254,5255,5382,5254,5382,5381,5255,5256,5383,5255,5383,5382,5256,5257,5384,5256,5384,5383,5257,5258,5385,5257,5385,5384,5258,5259,5386,5258,5386,5385,5259,5260,5387,5259,5387,5386,5260,5261,5388,5260,5388,5387,5261,5262,5389,5261,5389,5388,5262,5263,5390,5262,5390,5389,5263,5264,5391,5263,5391,5390,5264,5265,5392,5264,5392,5391,5265,5266,5393,5265,5393,5392,5266,5267,5394,5266,5394,5393,5267,5268,5395,5267,5395,5394,5268,5269,5396,5268,5396,5395,5269,5270,5397,5269,5397,5396,5270,5271,5398,5270,5398,5397,5271,5272,5399,5271,5399,5398,5272,5273,5400,5272,5400,5399,5273,5274,5401,5273,5401,5400,5274,5275,5402,5274,5402,5401,5275,5276,5403,5275,5403,5402,5276,5277,5404,5276,5404,5403,5277,5278,5405,5277,5405,5404,5278,5279,5406,5278,5406,5405,5279,5280,5407,5279,5407,5406,5280,5281,5408,5280,5408,5407,5281,5282,5409,5281,5409,5408,5282,5283,5410,5282,5410,5409,5283,5284,5411,5283,5411,5410,5284,5285,5412,5284,5412,5411,5285,5286,5413,5285,5413,5412,5286,5287,5414,5286,5414,5413,5287,5288,5415,5287,5415,5414,5288,5289,5416,5288,5416,5415,5289,5290,5417,5289,5417,5416,5290,5291,5418,5290,5418,5417,5291,5292,5419,5291,5419,5418,5292,5293,5420,5292,5420,5419,5293,5294,5421,5293,5421,5420,5294,5295,5422,5294,5422,5421,5295,5296,5423,5295,5423,5422,5296,5297,5424,5296,5424,5423,5297,5298,5425,5297,5425,5424,5298,5299,5426,5298,5426,5425,5299,5300,5427,5299,5427,5426,5300,5301,5428,5300,5428,5427,5301,5302,5429,5301,5429,5428,5302,5303,5430,5302,5430,5429,5303,5304,5431,5303,5431,5430,5304,5305,5432,5304,5432,5431,5305,5306,5433,5305,5433,5432,5306,5307,5434,5306,5434,5433,5307,5308,5435,5307,5435,5434,5308,5309,5436,5308,5436,5435,5309,5310,5437,5309,5437,5436,5310,5311,5438,5310,5438,5437,5311,5312,5439,5311,5439,5438,5312,5313,5440,5312,5440,5439,5313,5314,5441,5313,5441,5440,5314,5315,5442,5314,5442,5441,5315,5316,5443,5315,5443,5442,5316,5317,5444,5316,5444,5443,5317,5318,5445,5317,5445,5444,5318,5319,5446,5318,5446,5445,5319,5320,5447,5319,5447,5446,5320,5321,5448,5320,5448,5447,5321,5322,5449,5321,5449,5448,5322,5323,5450,5322,5450,5449,5323,5324,5451,5323,5451,5450,5324,5325,5452,5324,5452,5451,5325,5326,5453,5325,5453,5452,5326,5327,5454,5326,5454,5453,5327,5328,5455,5327,5455,5454,5328,5329,5456,5328,5456,5455,5329,5330,5457,5329,5457,5456,5330,5331,5458,5330,5458,5457,5331,5332,5459,5331,5459,5458,5332,5333,5460,5332,5460,5459,5334,5335,5462,5334,5462,5461,5335,5336,5463,5335,5463,5462,5336,5337,5464,5336,5464,5463,5337,5338,5465,5337,5465,5464,5338,5339,5466,5338,5466,5465,5339,5340,5467,5339,5467,5466,5340,5341,5468,5340,5468,5467,5341,5342,5469,5341,5469,5468,5342,5343,5470,5342,5470,5469,5343,5344,5471,5343,5471,5470,5344,5345,5472,5344,5472,5471,5345,5346,5473,5345,5473,5472,5346,5347,5474,5346,5474,5473,5347,5348,5475,5347,5475,5474,5348,5349,5476,5348,5476,5475,5349,5350,5477,5349,5477,5476,5350,5351,5478,5350,5478,5477,5351,5352,5479,5351,5479,5478,5352,5353,5480,5352,5480,5479,5353,5354,5481,5353,5481,5480,5354,5355,5482,5354,5482,5481,5355,5356,5483,5355,5483,5482,5356,5357,5484,5356,5484,5483,5357,5358,5485,5357,5485,5484,5358,5359,5486,5358,5486,5485,5359,5360,5487,5359,5487,5486,5360,5361,5488,5360,5488,5487,5361,5362,5489,5361,5489,5488,5362,5363,5490,5362,5490,5489,5363,5364,5491,5363,5491,5490,5364,5365,5492,5364,5492,5491,5365,5366,5493,5365,5493,5492,5366,5367,5494,5366,5494,5493,5367,5368,5495,5367,5495,5494,5368,5369,5496,5368,5496,5495,5369,5370,5497,5369,5497,5496,5370,5371,5498,5370,5498,5497,5371,5372,5499,5371,5499,5498,5372,5373,5500,5372,5500,5499,5373,5374,5501,5373,5501,5500,5374,5375,5502,5374,5502,5501,5375,5376,5503,5375,5503,5502,5376,5377,5504,5376,5504,5503,5377,5378,5505,5377,5505,5504,5378,5379,5506,5378,5506,5505,5379,5380,5507,5379,5507,5506,5380,5381,5508,5380,5508,5507,5381,5382,5509,5381,5509,5508,5382,5383,5510,5382,5510,5509,5383,5384,5511,5383,5511,5510,5384,5385,5512,5384,5512,5511,5385,5386,5513,5385,5513,5512,5386,5387,5514,5386,5514,5513,5387,5388,5515,5387,5515,5514,5388,5389,5516,5388,5516,5515,5389,5390,5517,5389,5517,5516,5390,5391,5518,5390,5518,5517,5391,5392,5519,5391,5519,5518,5392,5393,5520,5392,5520,5519,5393,5394,5521,5393,5521,5520,5394,5395,5522,5394,5522,5521,5395,5396,5523,5395,5523,5522,5396,5397,5524,5396,5524,5523,5397,5398,5525,5397,5525,5524,5398,5399,5526,5398,5526,5525,5399,5400,5527,5399,5527,5526,5400,5401,5528,5400,5528,5527,5401,5402,5529,5401,5529,5528,5402,5403,5530,5402,5530,5529,5403,5404,5531,5403,5531,5530,5404,5405,5532,5404,5532,5531,5405,5406,5533,5405,5533,5532,5406,5407,5534,5406,5534,5533,5407,5408,5535,5407,5535,5534,5408,5409,5536,5408,5536,5535,5409,5410,5537,5409,5537,5536,5410,5411,5538,5410,5538,5537,5411,5412,5539,5411,5539,5538,5412,5413,5540,5412,5540,5539,5413,5414,5541,5413,5541,5540,5414,5415,5542,5414,5542,5541,5415,5416,5543,5415,5543,5542,5416,5417,5544,5416,5544,5543,5417,5418,5545,5417,5545,5544,5418,5419,5546,5418,5546,5545,5419,5420,5547,5419,5547,5546,5420,5421,5548,5420,5548,5547,5421,5422,5549,5421,5549,5548,5422,5423,5550,5422,5550,5549,5423,5424,5551,5423,5551,5550,5424,5425,5552,5424,5552,5551,5425,5426,5553,5425,5553,5552,5426,5427,5554,5426,5554,5553,5427,5428,5555,5427,5555,5554,5428,5429,5556,5428,5556,5555,5429,5430,5557,5429,5557,5556,5430,5431,5558,5430,5558,5557,5431,5432,5559,5431,5559,5558,5432,5433,5560,5432,5560,5559,5433,5434,5561,5433,5561,5560,5434,5435,5562,5434,5562,5561,5435,5436,5563,5435,5563,5562,5436,5437,5564,5436,5564,5563,5437,5438,5565,5437,5565,5564,5438,5439,5566,5438,5566,5565,5439,5440,5567,5439,5567,5566,5440,5441,5568,5440,5568,5567,5441,5442,5569,5441,5569,5568,5442,5443,5570,5442,5570,5569,5443,5444,5571,5443,5571,5570,5444,5445,5572,5444,5572,5571,5445,5446,5573,5445,5573,5572,5446,5447,5574,5446,5574,5573,5447,5448,5575,5447,5575,5574,5448,5449,5576,5448,5576,5575,5449,5450,5577,5449,5577,5576,5450,5451,5578,5450,5578,5577,5451,5452,5579,5451,5579,5578,5452,5453,5580,5452,5580,5579,5453,5454,5581,5453,5581,5580,5454,5455,5582,5454,5582,5581,5455,5456,5583,5455,5583,5582,5456,5457,5584,5456,5584,5583,5457,5458,5585,5457,5585,5584,5458,5459,5586,5458,5586,5585,5459,5460,5587,5459,5587,5586,5461,5462,5589,5461,5589,5588,5462,5463,5590,5462,5590,5589,5463,5464,5591,5463,5591,5590,5464,5465,5592,5464,5592,5591,5465,5466,5593,5465,5593,5592,5466,5467,5594,5466,5594,5593,5467,5468,5595,5467,5595,5594,5468,5469,5596,5468,5596,5595,5469,5470,5597,5469,5597,5596,5470,5471,5598,5470,5598,5597,5471,5472,5599,5471,5599,5598,5472,5473,5600,5472,5600,5599,5473,5474,5601,5473,5601,5600,5474,5475,5602,5474,5602,5601,5475,5476,5603,5475,5603,5602,5476,5477,5604,5476,5604,5603,5477,5478,5605,5477,5605,5604,5478,5479,5606,5478,5606,5605,5479,5480,5607,5479,5607,5606,5480,5481,5608,5480,5608,5607,5481,5482,5609,5481,5609,5608,5482,5483,5610,5482,5610,5609,5483,5484,5611,5483,5611,5610,5484,5485,5612,5484,5612,5611,5485,5486,5613,5485,5613,5612,5486,5487,5614,5486,5614,5613,5487,5488,5615,5487,5615,5614,5488,5489,5616,5488,5616,5615,5489,5490,5617,5489,5617,5616,5490,5491,5618,5490,5618,5617,5491,5492,5619,5491,5619,5618,5492,5493,5620,5492,5620,5619,5493,5494,5621,5493,5621,5620,5494,5495,5622,5494,5622,5621,5495,5496,5623,5495,5623,5622,5496,5497,5624,5496,5624,5623,5497,5498,5625,5497,5625,5624,5498,5499,5626,5498,5626,5625,5499,5500,5627,5499,5627,5626,5500,5501,5628,5500,5628,5627,5501,5502,5629,5501,5629,5628,5502,5503,5630,5502,5630,5629,5503,5504,5631,5503,5631,5630,5504,5505,5632,5504,5632,5631,5505,5506,5633,5505,5633,5632,5506,5507,5634,5506,5634,5633,5507,5508,5635,5507,5635,5634,5508,5509,5636,5508,5636,5635,5509,5510,5637,5509,5637,5636,5510,5511,5638,5510,5638,5637,5511,5512,5639,5511,5639,5638,5512,5513,5640,5512,5640,5639,5513,5514,5641,5513,5641,5640,5514,5515,5642,5514,5642,5641,5515,5516,5643,5515,5643,5642,5516,5517,5644,5516,5644,5643,5517,5518,5645,5517,5645,5644,5518,5519,5646,5518,5646,5645,5519,5520,5647,5519,5647,5646,5520,5521,5648,5520,5648,5647,5521,5522,5649,5521,5649,5648,5522,5523,5650,5522,5650,5649,5523,5524,5651,5523,5651,5650,5524,5525,5652,5524,5652,5651,5525,5526,5653,5525,5653,5652,5526,5527,5654,5526,5654,5653,5527,5528,5655,5527,5655,5654,5528,5529,5656,5528,5656,5655,5529,5530,5657,5529,5657,5656,5530,5531,5658,5530,5658,5657,5531,5532,5659,5531,5659,5658,5532,5533,5660,5532,5660,5659,5533,5534,5661,5533,5661,5660,5534,5535,5662,5534,5662,5661,5535,5536,5663,5535,5663,5662,5536,5537,5664,5536,5664,5663,5537,5538,5665,5537,5665,5664,5538,5539,5666,5538,5666,5665,5539,5540,5667,5539,5667,5666,5540,5541,5668,5540,5668,5667,5541,5542,5669,5541,5669,5668,5542,5543,5670,5542,5670,5669,5543,5544,5671,5543,5671,5670,5544,5545,5672,5544,5672,5671,5545,5546,5673,5545,5673,5672,5546,5547,5674,5546,5674,5673,5547,5548,5675,5547,5675,5674,5548,5549,5676,5548,5676,5675,5549,5550,5677,5549,5677,5676,5550,5551,5678,5550,5678,5677,5551,5552,5679,5551,5679,5678,5552,5553,5680,5552,5680,5679,5553,5554,5681,5553,5681,5680,5554,5555,5682,5554,5682,5681,5555,5556,5683,5555,5683,5682,5556,5557,5684,5556,5684,5683,5557,5558,5685,5557,5685,5684,5558,5559,5686,5558,5686,5685,5559,5560,5687,5559,5687,5686,5560,5561,5688,5560,5688,5687,5561,5562,5689,5561,5689,5688,5562,5563,5690,5562,5690,5689,5563,5564,5691,5563,5691,5690,5564,5565,5692,5564,5692,5691,5565,5566,5693,5565,5693,5692,5566,5567,5694,5566,5694,5693,5567,5568,5695,5567,5695,5694,5568,5569,5696,5568,5696,5695,5569,5570,5697,5569,5697,5696,5570,5571,5698,5570,5698,5697,5571,5572,5699,5571,5699,5698,5572,5573,5700,5572,5700,5699,5573,5574,5701,5573,5701,5700,5574,5575,5702,5574,5702,5701,5575,5576,5703,5575,5703,5702,5576,5577,5704,5576,5704,5703,5577,5578,5705,5577,5705,5704,5578,5579,5706,5578,5706,5705,5579,5580,5707,5579,5707,5706,5580,5581,5708,5580,5708,5707,5581,5582,5709,5581,5709,5708,5582,5583,5710,5582,5710,5709,5583,5584,5711,5583,5711,5710,5584,5585,5712,5584,5712,5711,5585,5586,5713,5585,5713,5712,5586,5587,5714,5586,5714,5713,5588,5589,5716,5588,5716,5715,5589,5590,5717,5589,5717,5716,5590,5591,5718,5590,5718,5717,5591,5592,5719,5591,5719,5718,5592,5593,5720,5592,5720,5719,5593,5594,5721,5593,5721,5720,5594,5595,5722,5594,5722,5721,5595,5596,5723,5595,5723,5722,5596,5597,5724,5596,5724,5723,5597,5598,5725,5597,5725,5724,5598,5599,5726,5598,5726,5725,5599,5600,5727,5599,5727,5726,5600,5601,5728,5600,5728,5727,5601,5602,5729,5601,5729,5728,5602,5603,5730,5602,5730,5729,5603,5604,5731,5603,5731,5730,5604,5605,5732,5604,5732,5731,5605,5606,5733,5605,5733,5732,5606,5607,5734,5606,5734,5733,5607,5608,5735,5607,5735,5734,5608,5609,5736,5608,5736,5735,5609,5610,5737,5609,5737,5736,5610,5611,5738,5610,5738,5737,5611,5612,5739,5611,5739,5738,5612,5613,5740,5612,5740,5739,5613,5614,5741,5613,5741,5740,5614,5615,5742,5614,5742,5741,5615,5616,5743,5615,5743,5742,5616,5617,5744,5616,5744,5743,5617,5618,5745,5617,5745,5744,5618,5619,5746,5618,5746,5745,5619,5620,5747,5619,5747,5746,5620,5621,5748,5620,5748,5747,5621,5622,5749,5621,5749,5748,5622,5623,5750,5622,5750,5749,5623,5624,5751,5623,5751,5750,5624,5625,5752,5624,5752,5751,5625,5626,5753,5625,5753,5752,5626,5627,5754,5626,5754,5753,5627,5628,5755,5627,5755,5754,5628,5629,5756,5628,5756,5755,5629,5630,5757,5629,5757,5756,5630,5631,5758,5630,5758,5757,5631,5632,5759,5631,5759,5758,5632,5633,5760,5632,5760,5759,5633,5634,5761,5633,5761,5760,5634,5635,5762,5634,5762,5761,5635,5636,5763,5635,5763,5762,5636,5637,5764,5636,5764,5763,5637,5638,5765,5637,5765,5764,5638,5639,5766,5638,5766,5765,5639,5640,5767,5639,5767,5766,5640,5641,5768,5640,5768,5767,5641,5642,5769,5641,5769,5768,5642,5643,5770,5642,5770,5769,5643,5644,5771,5643,5771,5770,5644,5645,5772,5644,5772,5771,5645,5646,5773,5645,5773,5772,5646,5647,5774,5646,5774,5773,5647,5648,5775,5647,5775,5774,5648,5649,5776,5648,5776,5775,5649,5650,5777,5649,5777,5776,5650,5651,5778,5650,5778,5777,5651,5652,5779,5651,5779,5778,5652,5653,5780,5652,5780,5779,5653,5654,5781,5653,5781,5780,5654,5655,5782,5654,5782,5781,5655,5656,5783,5655,5783,5782,5656,5657,5784,5656,5784,5783,5657,5658,5785,5657,5785,5784,5658,5659,5786,5658,5786,5785,5659,5660,5787,5659,5787,5786,5660,5661,5788,5660,5788,5787,5661,5662,5789,5661,5789,5788,5662,5663,5790,5662,5790,5789,5663,5664,5791,5663,5791,5790,5664,5665,5792,5664,5792,5791,5665,5666,5793,5665,5793,5792,5666,5667,5794,5666,5794,5793,5667,5668,5795,5667,5795,5794,5668,5669,5796,5668,5796,5795,5669,5670,5797,5669,5797,5796,5670,5671,5798,5670,5798,5797,5671,5672,5799,5671,5799,5798,5672,5673,5800,5672,5800,5799,5673,5674,5801,5673,5801,5800,5674,5675,5802,5674,5802,5801,5675,5676,5803,5675,5803,5802,5676,5677,5804,5676,5804,5803,5677,5678,5805,5677,5805,5804,5678,5679,5806,5678,5806,5805,5679,5680,5807,5679,5807,5806,5680,5681,5808,5680,5808,5807,5681,5682,5809,5681,5809,5808,5682,5683,5810,5682,5810,5809,5683,5684,5811,5683,5811,5810,5684,5685,5812,5684,5812,5811,5685,5686,5813,5685,5813,5812,5686,5687,5814,5686,5814,5813,5687,5688,5815,5687,5815,5814,5688,5689,5816,5688,5816,5815,5689,5690,5817,5689,5817,5816,5690,5691,5818,5690,5818,5817,5691,5692,5819,5691,5819,5818,5692,5693,5820,5692,5820,5819,5693,5694,5821,5693,5821,5820,5694,5695,5822,5694,5822,5821,5695,5696,5823,5695,5823,5822,5696,5697,5824,5696,5824,5823,5697,5698,5825,5697,5825,5824,5698,5699,5826,5698,5826,5825,5699,5700,5827,5699,5827,5826,5700,5701,5828,5700,5828,5827,5701,5702,5829,5701,5829,5828,5702,5703,5830,5702,5830,5829,5703,5704,5831,5703,5831,5830,5704,5705,5832,5704,5832,5831,5705,5706,5833,5705,5833,5832,5706,5707,5834,5706,5834,5833,5707,5708,5835,5707,5835,5834,5708,5709,5836,5708,5836,5835,5709,5710,5837,5709,5837,5836,5710,5711,5838,5710,5838,5837,5711,5712,5839,5711,5839,5838,5712,5713,5840,5712,5840,5839,5713,5714,5841,5713,5841,5840,5715,5716,5843,5715,5843,5842,5716,5717,5844,5716,5844,5843,5717,5718,5845,5717,5845,5844,5718,5719,5846,5718,5846,5845,5719,5720,5847,5719,5847,5846,5720,5721,5848,5720,5848,5847,5721,5722,5849,5721,5849,5848,5722,5723,5850,5722,5850,5849,5723,5724,5851,5723,5851,5850,5724,5725,5852,5724,5852,5851,5725,5726,5853,5725,5853,5852,5726,5727,5854,5726,5854,5853,5727,5728,5855,5727,5855,5854,5728,5729,5856,5728,5856,5855,5729,5730,5857,5729,5857,5856,5730,5731,5858,5730,5858,5857,5731,5732,5859,5731,5859,5858,5732,5733,5860,5732,5860,5859,5733,5734,5861,5733,5861,5860,5734,5735,5862,5734,5862,5861,5735,5736,5863,5735,5863,5862,5736,5737,5864,5736,5864,5863,5737,5738,5865,5737,5865,5864,5738,5739,5866,5738,5866,5865,5739,5740,5867,5739,5867,5866,5740,5741,5868,5740,5868,5867,5741,5742,5869,5741,5869,5868,5742,5743,5870,5742,5870,5869,5743,5744,5871,5743,5871,5870,5744,5745,5872,5744,5872,5871,5745,5746,5873,5745,5873,5872,5746,5747,5874,5746,5874,5873,5747,5748,5875,5747,5875,5874,5748,5749,5876,5748,5876,5875,5749,5750,5877,5749,5877,5876,5750,5751,5878,5750,5878,5877,5751,5752,5879,5751,5879,5878,5752,5753,5880,5752,5880,5879,5753,5754,5881,5753,5881,5880,5754,5755,5882,5754,5882,5881,5755,5756,5883,5755,5883,5882,5756,5757,5884,5756,5884,5883,5757,5758,5885,5757,5885,5884,5758,5759,5886,5758,5886,5885,5759,5760,5887,5759,5887,5886,5760,5761,5888,5760,5888,5887,5761,5762,5889,5761,5889,5888,5762,5763,5890,5762,5890,5889,5763,5764,5891,5763,5891,5890,5764,5765,5892,5764,5892,5891,5765,5766,5893,5765,5893,5892,5766,5767,5894,5766,5894,5893,5767,5768,5895,5767,5895,5894,5768,5769,5896,5768,5896,5895,5769,5770,5897,5769,5897,5896,5770,5771,5898,5770,5898,5897,5771,5772,5899,5771,5899,5898,5772,5773,5900,5772,5900,5899,5773,5774,5901,5773,5901,5900,5774,5775,5902,5774,5902,5901,5775,5776,5903,5775,5903,5902,5776,5777,5904,5776,5904,5903,5777,5778,5905,5777,5905,5904,5778,5779,5906,5778,5906,5905,5779,5780,5907,5779,5907,5906,5780,5781,5908,5780,5908,5907,5781,5782,5909,5781,5909,5908,5782,5783,5910,5782,5910,5909,5783,5784,5911,5783,5911,5910,5784,5785,5912,5784,5912,5911,5785,5786,5913,5785,5913,5912,5786,5787,5914,5786,5914,5913,5787,5788,5915,5787,5915,5914,5788,5789,5916,5788,5916,5915,5789,5790,5917,5789,5917,5916,5790,5791,5918,5790,5918,5917,5791,5792,5919,5791,5919,5918,5792,5793,5920,5792,5920,5919,5793,5794,5921,5793,5921,5920,5794,5795,5922,5794,5922,5921,5795,5796,5923,5795,5923,5922,5796,5797,5924,5796,5924,5923,5797,5798,5925,5797,5925,5924,5798,5799,5926,5798,5926,5925,5799,5800,5927,5799,5927,5926,5800,5801,5928,5800,5928,5927,5801,5802,5929,5801,5929,5928,5802,5803,5930,5802,5930,5929,5803,5804,5931,5803,5931,5930,5804,5805,5932,5804,5932,5931,5805,5806,5933,5805,5933,5932,5806,5807,5934,5806,5934,5933,5807,5808,5935,5807,5935,5934,5808,5809,5936,5808,5936,5935,5809,5810,5937,5809,5937,5936,5810,5811,5938,5810,5938,5937,5811,5812,5939,5811,5939,5938,5812,5813,5940,5812,5940,5939,5813,5814,5941,5813,5941,5940,5814,5815,5942,5814,5942,5941,5815,5816,5943,5815,5943,5942,5816,5817,5944,5816,5944,5943,5817,5818,5945,5817,5945,5944,5818,5819,5946,5818,5946,5945,5819,5820,5947,5819,5947,5946,5820,5821,5948,5820,5948,5947,5821,5822,5949,5821,5949,5948,5822,5823,5950,5822,5950,5949,5823,5824,5951,5823,5951,5950,5824,5825,5952,5824,5952,5951,5825,5826,5953,5825,5953,5952,5826,5827,5954,5826,5954,5953,5827,5828,5955,5827,5955,5954,5828,5829,5956,5828,5956,5955,5829,5830,5957,5829,5957,5956,5830,5831,5958,5830,5958,5957,5831,5832,5959,5831,5959,5958,5832,5833,5960,5832,5960,5959,5833,5834,5961,5833,5961,5960,5834,5835,5962,5834,5962,5961,5835,5836,5963,5835,5963,5962,5836,5837,5964,5836,5964,5963,5837,5838,5965,5837,5965,5964,5838,5839,5966,5838,5966,5965,5839,5840,5967,5839,5967,5966,5840,5841,5968,5840,5968,5967,5842,5843,5970,5842,5970,5969,5843,5844,5971,5843,5971,5970,5844,5845,5972,5844,5972,5971,5845,5846,5973,5845,5973,5972,5846,5847,5974,5846,5974,5973,5847,5848,5975,5847,5975,5974,5848,5849,5976,5848,5976,5975,5849,5850,5977,5849,5977,5976,5850,5851,5978,5850,5978,5977,5851,5852,5979,5851,5979,5978,5852,5853,5980,5852,5980,5979,5853,5854,5981,5853,5981,5980,5854,5855,5982,5854,5982,5981,5855,5856,5983,5855,5983,5982,5856,5857,5984,5856,5984,5983,5857,5858,5985,5857,5985,5984,5858,5859,5986,5858,5986,5985,5859,5860,5987,5859,5987,5986,5860,5861,5988,5860,5988,5987,5861,5862,5989,5861,5989,5988,5862,5863,5990,5862,5990,5989,5863,5864,5991,5863,5991,5990,5864,5865,5992,5864,5992,5991,5865,5866,5993,5865,5993,5992,5866,5867,5994,5866,5994,5993,5867,5868,5995,5867,5995,5994,5868,5869,5996,5868,5996,5995,5869,5870,5997,5869,5997,5996,5870,5871,5998,5870,5998,5997,5871,5872,5999,5871,5999,5998,5872,5873,6000,5872,6000,5999,5873,5874,6001,5873,6001,6000,5874,5875,6002,5874,6002,6001,5875,5876,6003,5875,6003,6002,5876,5877,6004,5876,6004,6003,5877,5878,6005,5877,6005,6004,5878,5879,6006,5878,6006,6005,5879,5880,6007,5879,6007,6006,5880,5881,6008,5880,6008,6007,5881,5882,6009,5881,6009,6008,5882,5883,6010,5882,6010,6009,5883,5884,6011,5883,6011,6010,5884,5885,6012,5884,6012,6011,5885,5886,6013,5885,6013,6012,5886,5887,6014,5886,6014,6013,5887,5888,6015,5887,6015,6014,5888,5889,6016,5888,6016,6015,5889,5890,6017,5889,6017,6016,5890,5891,6018,5890,6018,6017,5891,5892,6019,5891,6019,6018,5892,5893,6020,5892,6020,6019,5893,5894,6021,5893,6021,6020,5894,5895,6022,5894,6022,6021,5895,5896,6023,5895,6023,6022,5896,5897,6024,5896,6024,6023,5897,5898,6025,5897,6025,6024,5898,5899,6026,5898,6026,6025,5899,5900,6027,5899,6027,6026,5900,5901,6028,5900,6028,6027,5901,5902,6029,5901,6029,6028,5902,5903,6030,5902,6030,6029,5903,5904,6031,5903,6031,6030,5904,5905,6032,5904,6032,6031,5905,5906,6033,5905,6033,6032,5906,5907,6034,5906,6034,6033,5907,5908,6035,5907,6035,6034,5908,5909,6036,5908,6036,6035,5909,5910,6037,5909,6037,6036,5910,5911,6038,5910,6038,6037,5911,5912,6039,5911,6039,6038,5912,5913,6040,5912,6040,6039,5913,5914,6041,5913,6041,6040,5914,5915,6042,5914,6042,6041,5915,5916,6043,5915,6043,6042,5916,5917,6044,5916,6044,6043,5917,5918,6045,5917,6045,6044,5918,5919,6046,5918,6046,6045,5919,5920,6047,5919,6047,6046,5920,5921,6048,5920,6048,6047,5921,5922,6049,5921,6049,6048,5922,5923,6050,5922,6050,6049,5923,5924,6051,5923,6051,6050,5924,5925,6052,5924,6052,6051,5925,5926,6053,5925,6053,6052,5926,5927,6054,5926,6054,6053,5927,5928,6055,5927,6055,6054,5928,5929,6056,5928,6056,6055,5929,5930,6057,5929,6057,6056,5930,5931,6058,5930,6058,6057,5931,5932,6059,5931,6059,6058,5932,5933,6060,5932,6060,6059,5933,5934,6061,5933,6061,6060,5934,5935,6062,5934,6062,6061,5935,5936,6063,5935,6063,6062,5936,5937,6064,5936,6064,6063,5937,5938,6065,5937,6065,6064,5938,5939,6066,5938,6066,6065,5939,5940,6067,5939,6067,6066,5940,5941,6068,5940,6068,6067,5941,5942,6069,5941,6069,6068,5942,5943,6070,5942,6070,6069,5943,5944,6071,5943,6071,6070,5944,5945,6072,5944,6072,6071,5945,5946,6073,5945,6073,6072,5946,5947,6074,5946,6074,6073,5947,5948,6075,5947,6075,6074,5948,5949,6076,5948,6076,6075,5949,5950,6077,5949,6077,6076,5950,5951,6078,5950,6078,6077,5951,5952,6079,5951,6079,6078,5952,5953,6080,5952,6080,6079,5953,5954,6081,5953,6081,6080,5954,5955,6082,5954,6082,6081,5955,5956,6083,5955,6083,6082,5956,5957,6084,5956,6084,6083,5957,5958,6085,5957,6085,6084,5958,5959,6086,5958,6086,6085,5959,5960,6087,5959,6087,6086,5960,5961,6088,5960,6088,6087,5961,5962,6089,5961,6089,6088,5962,5963,6090,5962,6090,6089,5963,5964,6091,5963,6091,6090,5964,5965,6092,5964,6092,6091,5965,5966,6093,5965,6093,6092,5966,5967,6094,5966,6094,6093,5967,5968,6095,5967,6095,6094,5969,5970,6097,5969,6097,6096,5970,5971,6098,5970,6098,6097,5971,5972,6099,5971,6099,6098,5972,5973,6100,5972,6100,6099,5973,5974,6101,5973,6101,6100,5974,5975,6102,5974,6102,6101,5975,5976,6103,5975,6103,6102,5976,5977,6104,5976,6104,6103,5977,5978,6105,5977,6105,6104,5978,5979,6106,5978,6106,6105,5979,5980,6107,5979,6107,6106,5980,5981,6108,5980,6108,6107,5981,5982,6109,5981,6109,6108,5982,5983,6110,5982,6110,6109,5983,5984,6111,5983,6111,6110,5984,5985,6112,5984,6112,6111,5985,5986,6113,5985,6113,6112,5986,5987,6114,5986,6114,6113,5987,5988,6115,5987,6115,6114,5988,5989,6116,5988,6116,6115,5989,5990,6117,5989,6117,6116,5990,5991,6118,5990,6118,6117,5991,5992,6119,5991,6119,6118,5992,5993,6120,5992,6120,6119,5993,5994,6121,5993,6121,6120,5994,5995,6122,5994,6122,6121,5995,5996,6123,5995,6123,6122,5996,5997,6124,5996,6124,6123,5997,5998,6125,5997,6125,6124,5998,5999,6126,5998,6126,6125,5999,6000,6127,5999,6127,6126,6000,6001,6128,6000,6128,6127,6001,6002,6129,6001,6129,6128,6002,6003,6130,6002,6130,6129,6003,6004,6131,6003,6131,6130,6004,6005,6132,6004,6132,6131,6005,6006,6133,6005,6133,6132,6006,6007,6134,6006,6134,6133,6007,6008,6135,6007,6135,6134,6008,6009,6136,6008,6136,6135,6009,6010,6137,6009,6137,6136,6010,6011,6138,6010,6138,6137,6011,6012,6139,6011,6139,6138,6012,6013,6140,6012,6140,6139,6013,6014,6141,6013,6141,6140,6014,6015,6142,6014,6142,6141,6015,6016,6143,6015,6143,6142,6016,6017,6144,6016,6144,6143,6017,6018,6145,6017,6145,6144,6018,6019,6146,6018,6146,6145,6019,6020,6147,6019,6147,6146,6020,6021,6148,6020,6148,6147,6021,6022,6149,6021,6149,6148,6022,6023,6150,6022,6150,6149,6023,6024,6151,6023,6151,6150,6024,6025,6152,6024,6152,6151,6025,6026,6153,6025,6153,6152,6026,6027,6154,6026,6154,6153,6027,6028,6155,6027,6155,6154,6028,6029,6156,6028,6156,6155,6029,6030,6157,6029,6157,6156,6030,6031,6158,6030,6158,6157,6031,6032,6159,6031,6159,6158,6032,6033,6160,6032,6160,6159,6033,6034,6161,6033,6161,6160,6034,6035,6162,6034,6162,6161,6035,6036,6163,6035,6163,6162,6036,6037,6164,6036,6164,6163,6037,6038,6165,6037,6165,6164,6038,6039,6166,6038,6166,6165,6039,6040,6167,6039,6167,6166,6040,6041,6168,6040,6168,6167,6041,6042,6169,6041,6169,6168,6042,6043,6170,6042,6170,6169,6043,6044,6171,6043,6171,6170,6044,6045,6172,6044,6172,6171,6045,6046,6173,6045,6173,6172,6046,6047,6174,6046,6174,6173,6047,6048,6175,6047,6175,6174,6048,6049,6176,6048,6176,6175,6049,6050,6177,6049,6177,6176,6050,6051,6178,6050,6178,6177,6051,6052,6179,6051,6179,6178,6052,6053,6180,6052,6180,6179,6053,6054,6181,6053,6181,6180,6054,6055,6182,6054,6182,6181,6055,6056,6183,6055,6183,6182,6056,6057,6184,6056,6184,6183,6057,6058,6185,6057,6185,6184,6058,6059,6186,6058,6186,6185,6059,6060,6187,6059,6187,6186,6060,6061,6188,6060,6188,6187,6061,6062,6189,6061,6189,6188,6062,6063,6190,6062,6190,6189,6063,6064,6191,6063,6191,6190,6064,6065,6192,6064,6192,6191,6065,6066,6193,6065,6193,6192,6066,6067,6194,6066,6194,6193,6067,6068,6195,6067,6195,6194,6068,6069,6196,6068,6196,6195,6069,6070,6197,6069,6197,6196,6070,6071,6198,6070,6198,6197,6071,6072,6199,6071,6199,6198,6072,6073,6200,6072,6200,6199,6073,6074,6201,6073,6201,6200,6074,6075,6202,6074,6202,6201,6075,6076,6203,6075,6203,6202,6076,6077,6204,6076,6204,6203,6077,6078,6205,6077,6205,6204,6078,6079,6206,6078,6206,6205,6079,6080,6207,6079,6207,6206,6080,6081,6208,6080,6208,6207,6081,6082,6209,6081,6209,6208,6082,6083,6210,6082,6210,6209,6083,6084,6211,6083,6211,6210,6084,6085,6212,6084,6212,6211,6085,6086,6213,6085,6213,6212,6086,6087,6214,6086,6214,6213,6087,6088,6215,6087,6215,6214,6088,6089,6216,6088,6216,6215,6089,6090,6217,6089,6217,6216,6090,6091,6218,6090,6218,6217,6091,6092,6219,6091,6219,6218,6092,6093,6220,6092,6220,6219,6093,6094,6221,6093,6221,6220,6094,6095,6222,6094,6222,6221,6096,6097,6224,6096,6224,6223,6097,6098,6225,6097,6225,6224,6098,6099,6226,6098,6226,6225,6099,6100,6227,6099,6227,6226,6100,6101,6228,6100,6228,6227,6101,6102,6229,6101,6229,6228,6102,6103,6230,6102,6230,6229,6103,6104,6231,6103,6231,6230,6104,6105,6232,6104,6232,6231,6105,6106,6233,6105,6233,6232,6106,6107,6234,6106,6234,6233,6107,6108,6235,6107,6235,6234,6108,6109,6236,6108,6236,6235,6109,6110,6237,6109,6237,6236,6110,6111,6238,6110,6238,6237,6111,6112,6239,6111,6239,6238,6112,6113,6240,6112,6240,6239,6113,6114,6241,6113,6241,6240,6114,6115,6242,6114,6242,6241,6115,6116,6243,6115,6243,6242,6116,6117,6244,6116,6244,6243,6117,6118,6245,6117,6245,6244,6118,6119,6246,6118,6246,6245,6119,6120,6247,6119,6247,6246,6120,6121,6248,6120,6248,6247,6121,6122,6249,6121,6249,6248,6122,6123,6250,6122,6250,6249,6123,6124,6251,6123,6251,6250,6124,6125,6252,6124,6252,6251,6125,6126,6253,6125,6253,6252,6126,6127,6254,6126,6254,6253,6127,6128,6255,6127,6255,6254,6128,6129,6256,6128,6256,6255,6129,6130,6257,6129,6257,6256,6130,6131,6258,6130,6258,6257,6131,6132,6259,6131,6259,6258,6132,6133,6260,6132,6260,6259,6133,6134,6261,6133,6261,6260,6134,6135,6262,6134,6262,6261,6135,6136,6263,6135,6263,6262,6136,6137,6264,6136,6264,6263,6137,6138,6265,6137,6265,6264,6138,6139,6266,6138,6266,6265,6139,6140,6267,6139,6267,6266,6140,6141,6268,6140,6268,6267,6141,6142,6269,6141,6269,6268,6142,6143,6270,6142,6270,6269,6143,6144,6271,6143,6271,6270,6144,6145,6272,6144,6272,6271,6145,6146,6273,6145,6273,6272,6146,6147,6274,6146,6274,6273,6147,6148,6275,6147,6275,6274,6148,6149,6276,6148,6276,6275,6149,6150,6277,6149,6277,6276,6150,6151,6278,6150,6278,6277,6151,6152,6279,6151,6279,6278,6152,6153,6280,6152,6280,6279,6153,6154,6281,6153,6281,6280,6154,6155,6282,6154,6282,6281,6155,6156,6283,6155,6283,6282,6156,6157,6284,6156,6284,6283,6157,6158,6285,6157,6285,6284,6158,6159,6286,6158,6286,6285,6159,6160,6287,6159,6287,6286,6160,6161,6288,6160,6288,6287,6161,6162,6289,6161,6289,6288,6162,6163,6290,6162,6290,6289,6163,6164,6291,6163,6291,6290,6164,6165,6292,6164,6292,6291,6165,6166,6293,6165,6293,6292,6166,6167,6294,6166,6294,6293,6167,6168,6295,6167,6295,6294,6168,6169,6296,6168,6296,6295,6169,6170,6297,6169,6297,6296,6170,6171,6298,6170,6298,6297,6171,6172,6299,6171,6299,6298,6172,6173,6300,6172,6300,6299,6173,6174,6301,6173,6301,6300,6174,6175,6302,6174,6302,6301,6175,6176,6303,6175,6303,6302,6176,6177,6304,6176,6304,6303,6177,6178,6305,6177,6305,6304,6178,6179,6306,6178,6306,6305,6179,6180,6307,6179,6307,6306,6180,6181,6308,6180,6308,6307,6181,6182,6309,6181,6309,6308,6182,6183,6310,6182,6310,6309,6183,6184,6311,6183,6311,6310,6184,6185,6312,6184,6312,6311,6185,6186,6313,6185,6313,6312,6186,6187,6314,6186,6314,6313,6187,6188,6315,6187,6315,6314,6188,6189,6316,6188,6316,6315,6189,6190,6317,6189,6317,6316,6190,6191,6318,6190,6318,6317,6191,6192,6319,6191,6319,6318,6192,6193,6320,6192,6320,6319,6193,6194,6321,6193,6321,6320,6194,6195,6322,6194,6322,6321,6195,6196,6323,6195,6323,6322,6196,6197,6324,6196,6324,6323,6197,6198,6325,6197,6325,6324,6198,6199,6326,6198,6326,6325,6199,6200,6327,6199,6327,6326,6200,6201,6328,6200,6328,6327,6201,6202,6329,6201,6329,6328,6202,6203,6330,6202,6330,6329,6203,6204,6331,6203,6331,6330,6204,6205,6332,6204,6332,6331,6205,6206,6333,6205,6333,6332,6206,6207,6334,6206,6334,6333,6207,6208,6335,6207,6335,6334,6208,6209,6336,6208,6336,6335,6209,6210,6337,6209,6337,6336,6210,6211,6338,6210,6338,6337,6211,6212,6339,6211,6339,6338,6212,6213,6340,6212,6340,6339,6213,6214,6341,6213,6341,6340,6214,6215,6342,6214,6342,6341,6215,6216,6343,6215,6343,6342,6216,6217,6344,6216,6344,6343,6217,6218,6345,6217,6345,6344,6218,6219,6346,6218,6346,6345,6219,6220,6347,6219,6347,6346,6220,6221,6348,6220,6348,6347,6221,6222,6349,6221,6349,6348,6223,6224,6351,6223,6351,6350,6224,6225,6352,6224,6352,6351,6225,6226,6353,6225,6353,6352,6226,6227,6354,6226,6354,6353,6227,6228,6355,6227,6355,6354,6228,6229,6356,6228,6356,6355,6229,6230,6357,6229,6357,6356,6230,6231,6358,6230,6358,6357,6231,6232,6359,6231,6359,6358,6232,6233,6360,6232,6360,6359,6233,6234,6361,6233,6361,6360,6234,6235,6362,6234,6362,6361,6235,6236,6363,6235,6363,6362,6236,6237,6364,6236,6364,6363,6237,6238,6365,6237,6365,6364,6238,6239,6366,6238,6366,6365,6239,6240,6367,6239,6367,6366,6240,6241,6368,6240,6368,6367,6241,6242,6369,6241,6369,6368,6242,6243,6370,6242,6370,6369,6243,6244,6371,6243,6371,6370,6244,6245,6372,6244,6372,6371,6245,6246,6373,6245,6373,6372,6246,6247,6374,6246,6374,6373,6247,6248,6375,6247,6375,6374,6248,6249,6376,6248,6376,6375,6249,6250,6377,6249,6377,6376,6250,6251,6378,6250,6378,6377,6251,6252,6379,6251,6379,6378,6252,6253,6380,6252,6380,6379,6253,6254,6381,6253,6381,6380,6254,6255,6382,6254,6382,6381,6255,6256,6383,6255,6383,6382,6256,6257,6384,6256,6384,6383,6257,6258,6385,6257,6385,6384,6258,6259,6386,6258,6386,6385,6259,6260,6387,6259,6387,6386,6260,6261,6388,6260,6388,6387,6261,6262,6389,6261,6389,6388,6262,6263,6390,6262,6390,6389,6263,6264,6391,6263,6391,6390,6264,6265,6392,6264,6392,6391,6265,6266,6393,6265,6393,6392,6266,6267,6394,6266,6394,6393,6267,6268,6395,6267,6395,6394,6268,6269,6396,6268,6396,6395,6269,6270,6397,6269,6397,6396,6270,6271,6398,6270,6398,6397,6271,6272,6399,6271,6399,6398,6272,6273,6400,6272,6400,6399,6273,6274,6401,6273,6401,6400,6274,6275,6402,6274,6402,6401,6275,6276,6403,6275,6403,6402,6276,6277,6404,6276,6404,6403,6277,6278,6405,6277,6405,6404,6278,6279,6406,6278,6406,6405,6279,6280,6407,6279,6407,6406,6280,6281,6408,6280,6408,6407,6281,6282,6409,6281,6409,6408,6282,6283,6410,6282,6410,6409,6283,6284,6411,6283,6411,6410,6284,6285,6412,6284,6412,6411,6285,6286,6413,6285,6413,6412,6286,6287,6414,6286,6414,6413,6287,6288,6415,6287,6415,6414,6288,6289,6416,6288,6416,6415,6289,6290,6417,6289,6417,6416,6290,6291,6418,6290,6418,6417,6291,6292,6419,6291,6419,6418,6292,6293,6420,6292,6420,6419,6293,6294,6421,6293,6421,6420,6294,6295,6422,6294,6422,6421,6295,6296,6423,6295,6423,6422,6296,6297,6424,6296,6424,6423,6297,6298,6425,6297,6425,6424,6298,6299,6426,6298,6426,6425,6299,6300,6427,6299,6427,6426,6300,6301,6428,6300,6428,6427,6301,6302,6429,6301,6429,6428,6302,6303,6430,6302,6430,6429,6303,6304,6431,6303,6431,6430,6304,6305,6432,6304,6432,6431,6305,6306,6433,6305,6433,6432,6306,6307,6434,6306,6434,6433,6307,6308,6435,6307,6435,6434,6308,6309,6436,6308,6436,6435,6309,6310,6437,6309,6437,6436,6310,6311,6438,6310,6438,6437,6311,6312,6439,6311,6439,6438,6312,6313,6440,6312,6440,6439,6313,6314,6441,6313,6441,6440,6314,6315,6442,6314,6442,6441,6315,6316,6443,6315,6443,6442,6316,6317,6444,6316,6444,6443,6317,6318,6445,6317,6445,6444,6318,6319,6446,6318,6446,6445,6319,6320,6447,6319,6447,6446,6320,6321,6448,6320,6448,6447,6321,6322,6449,6321,6449,6448,6322,6323,6450,6322,6450,6449,6323,6324,6451,6323,6451,6450,6324,6325,6452,6324,6452,6451,6325,6326,6453,6325,6453,6452,6326,6327,6454,6326,6454,6453,6327,6328,6455,6327,6455,6454,6328,6329,6456,6328,6456,6455,6329,6330,6457,6329,6457,6456,6330,6331,6458,6330,6458,6457,6331,6332,6459,6331,6459,6458,6332,6333,6460,6332,6460,6459,6333,6334,6461,6333,6461,6460,6334,6335,6462,6334,6462,6461,6335,6336,6463,6335,6463,6462,6336,6337,6464,6336,6464,6463,6337,6338,6465,6337,6465,6464,6338,6339,6466,6338,6466,6465,6339,6340,6467,6339,6467,6466,6340,6341,6468,6340,6468,6467,6341,6342,6469,6341,6469,6468,6342,6343,6470,6342,6470,6469,6343,6344,6471,6343,6471,6470,6344,6345,6472,6344,6472,6471,6345,6346,6473,6345,6473,6472,6346,6347,6474,6346,6474,6473,6347,6348,6475,6347,6475,6474,6348,6349,6476,6348,6476,6475,6350,6351,6478,6350,6478,6477,6351,6352,6479,6351,6479,6478,6352,6353,6480,6352,6480,6479,6353,6354,6481,6353,6481,6480,6354,6355,6482,6354,6482,6481,6355,6356,6483,6355,6483,6482,6356,6357,6484,6356,6484,6483,6357,6358,6485,6357,6485,6484,6358,6359,6486,6358,6486,6485,6359,6360,6487,6359,6487,6486,6360,6361,6488,6360,6488,6487,6361,6362,6489,6361,6489,6488,6362,6363,6490,6362,6490,6489,6363,6364,6491,6363,6491,6490,6364,6365,6492,6364,6492,6491,6365,6366,6493,6365,6493,6492,6366,6367,6494,6366,6494,6493,6367,6368,6495,6367,6495,6494,6368,6369,6496,6368,6496,6495,6369,6370,6497,6369,6497,6496,6370,6371,6498,6370,6498,6497,6371,6372,6499,6371,6499,6498,6372,6373,6500,6372,6500,6499,6373,6374,6501,6373,6501,6500,6374,6375,6502,6374,6502,6501,6375,6376,6503,6375,6503,6502,6376,6377,6504,6376,6504,6503,6377,6378,6505,6377,6505,6504,6378,6379,6506,6378,6506,6505,6379,6380,6507,6379,6507,6506,6380,6381,6508,6380,6508,6507,6381,6382,6509,6381,6509,6508,6382,6383,6510,6382,6510,6509,6383,6384,6511,6383,6511,6510,6384,6385,6512,6384,6512,6511,6385,6386,6513,6385,6513,6512,6386,6387,6514,6386,6514,6513,6387,6388,6515,6387,6515,6514,6388,6389,6516,6388,6516,6515,6389,6390,6517,6389,6517,6516,6390,6391,6518,6390,6518,6517,6391,6392,6519,6391,6519,6518,6392,6393,6520,6392,6520,6519,6393,6394,6521,6393,6521,6520,6394,6395,6522,6394,6522,6521,6395,6396,6523,6395,6523,6522,6396,6397,6524,6396,6524,6523,6397,6398,6525,6397,6525,6524,6398,6399,6526,6398,6526,6525,6399,6400,6527,6399,6527,6526,6400,6401,6528,6400,6528,6527,6401,6402,6529,6401,6529,6528,6402,6403,6530,6402,6530,6529,6403,6404,6531,6403,6531,6530,6404,6405,6532,6404,6532,6531,6405,6406,6533,6405,6533,6532,6406,6407,6534,6406,6534,6533,6407,6408,6535,6407,6535,6534,6408,6409,6536,6408,6536,6535,6409,6410,6537,6409,6537,6536,6410,6411,6538,6410,6538,6537,6411,6412,6539,6411,6539,6538,6412,6413,6540,6412,6540,6539,6413,6414,6541,6413,6541,6540,6414,6415,6542,6414,6542,6541,6415,6416,6543,6415,6543,6542,6416,6417,6544,6416,6544,6543,6417,6418,6545,6417,6545,6544,6418,6419,6546,6418,6546,6545,6419,6420,6547,6419,6547,6546,6420,6421,6548,6420,6548,6547,6421,6422,6549,6421,6549,6548,6422,6423,6550,6422,6550,6549,6423,6424,6551,6423,6551,6550,6424,6425,6552,6424,6552,6551,6425,6426,6553,6425,6553,6552,6426,6427,6554,6426,6554,6553,6427,6428,6555,6427,6555,6554,6428,6429,6556,6428,6556,6555,6429,6430,6557,6429,6557,6556,6430,6431,6558,6430,6558,6557,6431,6432,6559,6431,6559,6558,6432,6433,6560,6432,6560,6559,6433,6434,6561,6433,6561,6560,6434,6435,6562,6434,6562,6561,6435,6436,6563,6435,6563,6562,6436,6437,6564,6436,6564,6563,6437,6438,6565,6437,6565,6564,6438,6439,6566,6438,6566,6565,6439,6440,6567,6439,6567,6566,6440,6441,6568,6440,6568,6567,6441,6442,6569,6441,6569,6568,6442,6443,6570,6442,6570,6569,6443,6444,6571,6443,6571,6570,6444,6445,6572,6444,6572,6571,6445,6446,6573,6445,6573,6572,6446,6447,6574,6446,6574,6573,6447,6448,6575,6447,6575,6574,6448,6449,6576,6448,6576,6575,6449,6450,6577,6449,6577,6576,6450,6451,6578,6450,6578,6577,6451,6452,6579,6451,6579,6578,6452,6453,6580,6452,6580,6579,6453,6454,6581,6453,6581,6580,6454,6455,6582,6454,6582,6581,6455,6456,6583,6455,6583,6582,6456,6457,6584,6456,6584,6583,6457,6458,6585,6457,6585,6584,6458,6459,6586,6458,6586,6585,6459,6460,6587,6459,6587,6586,6460,6461,6588,6460,6588,6587,6461,6462,6589,6461,6589,6588,6462,6463,6590,6462,6590,6589,6463,6464,6591,6463,6591,6590,6464,6465,6592,6464,6592,6591,6465,6466,6593,6465,6593,6592,6466,6467,6594,6466,6594,6593,6467,6468,6595,6467,6595,6594,6468,6469,6596,6468,6596,6595,6469,6470,6597,6469,6597,6596,6470,6471,6598,6470,6598,6597,6471,6472,6599,6471,6599,6598,6472,6473,6600,6472,6600,6599,6473,6474,6601,6473,6601,6600,6474,6475,6602,6474,6602,6601,6475,6476,6603,6475,6603,6602,6477,6478,6605,6477,6605,6604,6478,6479,6606,6478,6606,6605,6479,6480,6607,6479,6607,6606,6480,6481,6608,6480,6608,6607,6481,6482,6609,6481,6609,6608,6482,6483,6610,6482,6610,6609,6483,6484,6611,6483,6611,6610,6484,6485,6612,6484,6612,6611,6485,6486,6613,6485,6613,6612,6486,6487,6614,6486,6614,6613,6487,6488,6615,6487,6615,6614,6488,6489,6616,6488,6616,6615,6489,6490,6617,6489,6617,6616,6490,6491,6618,6490,6618,6617,6491,6492,6619,6491,6619,6618,6492,6493,6620,6492,6620,6619,6493,6494,6621,6493,6621,6620,6494,6495,6622,6494,6622,6621,6495,6496,6623,6495,6623,6622,6496,6497,6624,6496,6624,6623,6497,6498,6625,6497,6625,6624,6498,6499,6626,6498,6626,6625,6499,6500,6627,6499,6627,6626,6500,6501,6628,6500,6628,6627,6501,6502,6629,6501,6629,6628,6502,6503,6630,6502,6630,6629,6503,6504,6631,6503,6631,6630,6504,6505,6632,6504,6632,6631,6505,6506,6633,6505,6633,6632,6506,6507,6634,6506,6634,6633,6507,6508,6635,6507,6635,6634,6508,6509,6636,6508,6636,6635,6509,6510,6637,6509,6637,6636,6510,6511,6638,6510,6638,6637,6511,6512,6639,6511,6639,6638,6512,6513,6640,6512,6640,6639,6513,6514,6641,6513,6641,6640,6514,6515,6642,6514,6642,6641,6515,6516,6643,6515,6643,6642,6516,6517,6644,6516,6644,6643,6517,6518,6645,6517,6645,6644,6518,6519,6646,6518,6646,6645,6519,6520,6647,6519,6647,6646,6520,6521,6648,6520,6648,6647,6521,6522,6649,6521,6649,6648,6522,6523,6650,6522,6650,6649,6523,6524,6651,6523,6651,6650,6524,6525,6652,6524,6652,6651,6525,6526,6653,6525,6653,6652,6526,6527,6654,6526,6654,6653,6527,6528,6655,6527,6655,6654,6528,6529,6656,6528,6656,6655,6529,6530,6657,6529,6657,6656,6530,6531,6658,6530,6658,6657,6531,6532,6659,6531,6659,6658,6532,6533,6660,6532,6660,6659,6533,6534,6661,6533,6661,6660,6534,6535,6662,6534,6662,6661,6535,6536,6663,6535,6663,6662,6536,6537,6664,6536,6664,6663,6537,6538,6665,6537,6665,6664,6538,6539,6666,6538,6666,6665,6539,6540,6667,6539,6667,6666,6540,6541,6668,6540,6668,6667,6541,6542,6669,6541,6669,6668,6542,6543,6670,6542,6670,6669,6543,6544,6671,6543,6671,6670,6544,6545,6672,6544,6672,6671,6545,6546,6673,6545,6673,6672,6546,6547,6674,6546,6674,6673,6547,6548,6675,6547,6675,6674,6548,6549,6676,6548,6676,6675,6549,6550,6677,6549,6677,6676,6550,6551,6678,6550,6678,6677,6551,6552,6679,6551,6679,6678,6552,6553,6680,6552,6680,6679,6553,6554,6681,6553,6681,6680,6554,6555,6682,6554,6682,6681,6555,6556,6683,6555,6683,6682,6556,6557,6684,6556,6684,6683,6557,6558,6685,6557,6685,6684,6558,6559,6686,6558,6686,6685,6559,6560,6687,6559,6687,6686,6560,6561,6688,6560,6688,6687,6561,6562,6689,6561,6689,6688,6562,6563,6690,6562,6690,6689,6563,6564,6691,6563,6691,6690,6564,6565,6692,6564,6692,6691,6565,6566,6693,6565,6693,6692,6566,6567,6694,6566,6694,6693,6567,6568,6695,6567,6695,6694,6568,6569,6696,6568,6696,6695,6569,6570,6697,6569,6697,6696,6570,6571,6698,6570,6698,6697,6571,6572,6699,6571,6699,6698,6572,6573,6700,6572,6700,6699,6573,6574,6701,6573,6701,6700,6574,6575,6702,6574,6702,6701,6575,6576,6703,6575,6703,6702,6576,6577,6704,6576,6704,6703,6577,6578,6705,6577,6705,6704,6578,6579,6706,6578,6706,6705,6579,6580,6707,6579,6707,6706,6580,6581,6708,6580,6708,6707,6581,6582,6709,6581,6709,6708,6582,6583,6710,6582,6710,6709,6583,6584,6711,6583,6711,6710,6584,6585,6712,6584,6712,6711,6585,6586,6713,6585,6713,6712,6586,6587,6714,6586,6714,6713,6587,6588,6715,6587,6715,6714,6588,6589,6716,6588,6716,6715,6589,6590,6717,6589,6717,6716,6590,6591,6718,6590,6718,6717,6591,6592,6719,6591,6719,6718,6592,6593,6720,6592,6720,6719,6593,6594,6721,6593,6721,6720,6594,6595,6722,6594,6722,6721,6595,6596,6723,6595,6723,6722,6596,6597,6724,6596,6724,6723,6597,6598,6725,6597,6725,6724,6598,6599,6726,6598,6726,6725,6599,6600,6727,6599,6727,6726,6600,6601,6728,6600,6728,6727,6601,6602,6729,6601,6729,6728,6602,6603,6730,6602,6730,6729,6604,6605,6732,6604,6732,6731,6605,6606,6733,6605,6733,6732,6606,6607,6734,6606,6734,6733,6607,6608,6735,6607,6735,6734,6608,6609,6736,6608,6736,6735,6609,6610,6737,6609,6737,6736,6610,6611,6738,6610,6738,6737,6611,6612,6739,6611,6739,6738,6612,6613,6740,6612,6740,6739,6613,6614,6741,6613,6741,6740,6614,6615,6742,6614,6742,6741,6615,6616,6743,6615,6743,6742,6616,6617,6744,6616,6744,6743,6617,6618,6745,6617,6745,6744,6618,6619,6746,6618,6746,6745,6619,6620,6747,6619,6747,6746,6620,6621,6748,6620,6748,6747,6621,6622,6749,6621,6749,6748,6622,6623,6750,6622,6750,6749,6623,6624,6751,6623,6751,6750,6624,6625,6752,6624,6752,6751,6625,6626,6753,6625,6753,6752,6626,6627,6754,6626,6754,6753,6627,6628,6755,6627,6755,6754,6628,6629,6756,6628,6756,6755,6629,6630,6757,6629,6757,6756,6630,6631,6758,6630,6758,6757,6631,6632,6759,6631,6759,6758,6632,6633,6760,6632,6760,6759,6633,6634,6761,6633,6761,6760,6634,6635,6762,6634,6762,6761,6635,6636,6763,6635,6763,6762,6636,6637,6764,6636,6764,6763,6637,6638,6765,6637,6765,6764,6638,6639,6766,6638,6766,6765,6639,6640,6767,6639,6767,6766,6640,6641,6768,6640,6768,6767,6641,6642,6769,6641,6769,6768,6642,6643,6770,6642,6770,6769,6643,6644,6771,6643,6771,6770,6644,6645,6772,6644,6772,6771,6645,6646,6773,6645,6773,6772,6646,6647,6774,6646,6774,6773,6647,6648,6775,6647,6775,6774,6648,6649,6776,6648,6776,6775,6649,6650,6777,6649,6777,6776,6650,6651,6778,6650,6778,6777,6651,6652,6779,6651,6779,6778,6652,6653,6780,6652,6780,6779,6653,6654,6781,6653,6781,6780,6654,6655,6782,6654,6782,6781,6655,6656,6783,6655,6783,6782,6656,6657,6784,6656,6784,6783,6657,6658,6785,6657,6785,6784,6658,6659,6786,6658,6786,6785,6659,6660,6787,6659,6787,6786,6660,6661,6788,6660,6788,6787,6661,6662,6789,6661,6789,6788,6662,6663,6790,6662,6790,6789,6663,6664,6791,6663,6791,6790,6664,6665,6792,6664,6792,6791,6665,6666,6793,6665,6793,6792,6666,6667,6794,6666,6794,6793,6667,6668,6795,6667,6795,6794,6668,6669,6796,6668,6796,6795,6669,6670,6797,6669,6797,6796,6670,6671,6798,6670,6798,6797,6671,6672,6799,6671,6799,6798,6672,6673,6800,6672,6800,6799,6673,6674,6801,6673,6801,6800,6674,6675,6802,6674,6802,6801,6675,6676,6803,6675,6803,6802,6676,6677,6804,6676,6804,6803,6677,6678,6805,6677,6805,6804,6678,6679,6806,6678,6806,6805,6679,6680,6807,6679,6807,6806,6680,6681,6808,6680,6808,6807,6681,6682,6809,6681,6809,6808,6682,6683,6810,6682,6810,6809,6683,6684,6811,6683,6811,6810,6684,6685,6812,6684,6812,6811,6685,6686,6813,6685,6813,6812,6686,6687,6814,6686,6814,6813,6687,6688,6815,6687,6815,6814,6688,6689,6816,6688,6816,6815,6689,6690,6817,6689,6817,6816,6690,6691,6818,6690,6818,6817,6691,6692,6819,6691,6819,6818,6692,6693,6820,6692,6820,6819,6693,6694,6821,6693,6821,6820,6694,6695,6822,6694,6822,6821,6695,6696,6823,6695,6823,6822,6696,6697,6824,6696,6824,6823,6697,6698,6825,6697,6825,6824,6698,6699,6826,6698,6826,6825,6699,6700,6827,6699,6827,6826,6700,6701,6828,6700,6828,6827,6701,6702,6829,6701,6829,6828,6702,6703,6830,6702,6830,6829,6703,6704,6831,6703,6831,6830,6704,6705,6832,6704,6832,6831,6705,6706,6833,6705,6833,6832,6706,6707,6834,6706,6834,6833,6707,6708,6835,6707,6835,6834,6708,6709,6836,6708,6836,6835,6709,6710,6837,6709,6837,6836,6710,6711,6838,6710,6838,6837,6711,6712,6839,6711,6839,6838,6712,6713,6840,6712,6840,6839,6713,6714,6841,6713,6841,6840,6714,6715,6842,6714,6842,6841,6715,6716,6843,6715,6843,6842,6716,6717,6844,6716,6844,6843,6717,6718,6845,6717,6845,6844,6718,6719,6846,6718,6846,6845,6719,6720,6847,6719,6847,6846,6720,6721,6848,6720,6848,6847,6721,6722,6849,6721,6849,6848,6722,6723,6850,6722,6850,6849,6723,6724,6851,6723,6851,6850,6724,6725,6852,6724,6852,6851,6725,6726,6853,6725,6853,6852,6726,6727,6854,6726,6854,6853,6727,6728,6855,6727,6855,6854,6728,6729,6856,6728,6856,6855,6729,6730,6857,6729,6857,6856,6731,6732,6859,6731,6859,6858,6732,6733,6860,6732,6860,6859,6733,6734,6861,6733,6861,6860,6734,6735,6862,6734,6862,6861,6735,6736,6863,6735,6863,6862,6736,6737,6864,6736,6864,6863,6737,6738,6865,6737,6865,6864,6738,6739,6866,6738,6866,6865,6739,6740,6867,6739,6867,6866,6740,6741,6868,6740,6868,6867,6741,6742,6869,6741,6869,6868,6742,6743,6870,6742,6870,6869,6743,6744,6871,6743,6871,6870,6744,6745,6872,6744,6872,6871,6745,6746,6873,6745,6873,6872,6746,6747,6874,6746,6874,6873,6747,6748,6875,6747,6875,6874,6748,6749,6876,6748,6876,6875,6749,6750,6877,6749,6877,6876,6750,6751,6878,6750,6878,6877,6751,6752,6879,6751,6879,6878,6752,6753,6880,6752,6880,6879,6753,6754,6881,6753,6881,6880,6754,6755,6882,6754,6882,6881,6755,6756,6883,6755,6883,6882,6756,6757,6884,6756,6884,6883,6757,6758,6885,6757,6885,6884,6758,6759,6886,6758,6886,6885,6759,6760,6887,6759,6887,6886,6760,6761,6888,6760,6888,6887,6761,6762,6889,6761,6889,6888,6762,6763,6890,6762,6890,6889,6763,6764,6891,6763,6891,6890,6764,6765,6892,6764,6892,6891,6765,6766,6893,6765,6893,6892,6766,6767,6894,6766,6894,6893,6767,6768,6895,6767,6895,6894,6768,6769,6896,6768,6896,6895,6769,6770,6897,6769,6897,6896,6770,6771,6898,6770,6898,6897,6771,6772,6899,6771,6899,6898,6772,6773,6900,6772,6900,6899,6773,6774,6901,6773,6901,6900,6774,6775,6902,6774,6902,6901,6775,6776,6903,6775,6903,6902,6776,6777,6904,6776,6904,6903,6777,6778,6905,6777,6905,6904,6778,6779,6906,6778,6906,6905,6779,6780,6907,6779,6907,6906,6780,6781,6908,6780,6908,6907,6781,6782,6909,6781,6909,6908,6782,6783,6910,6782,6910,6909,6783,6784,6911,6783,6911,6910,6784,6785,6912,6784,6912,6911,6785,6786,6913,6785,6913,6912,6786,6787,6914,6786,6914,6913,6787,6788,6915,6787,6915,6914,6788,6789,6916,6788,6916,6915,6789,6790,6917,6789,6917,6916,6790,6791,6918,6790,6918,6917,6791,6792,6919,6791,6919,6918,6792,6793,6920,6792,6920,6919,6793,6794,6921,6793,6921,6920,6794,6795,6922,6794,6922,6921,6795,6796,6923,6795,6923,6922,6796,6797,6924,6796,6924,6923,6797,6798,6925,6797,6925,6924,6798,6799,6926,6798,6926,6925,6799,6800,6927,6799,6927,6926,6800,6801,6928,6800,6928,6927,6801,6802,6929,6801,6929,6928,6802,6803,6930,6802,6930,6929,6803,6804,6931,6803,6931,6930,6804,6805,6932,6804,6932,6931,6805,6806,6933,6805,6933,6932,6806,6807,6934,6806,6934,6933,6807,6808,6935,6807,6935,6934,6808,6809,6936,6808,6936,6935,6809,6810,6937,6809,6937,6936,6810,6811,6938,6810,6938,6937,6811,6812,6939,6811,6939,6938,6812,6813,6940,6812,6940,6939,6813,6814,6941,6813,6941,6940,6814,6815,6942,6814,6942,6941,6815,6816,6943,6815,6943,6942,6816,6817,6944,6816,6944,6943,6817,6818,6945,6817,6945,6944,6818,6819,6946,6818,6946,6945,6819,6820,6947,6819,6947,6946,6820,6821,6948,6820,6948,6947,6821,6822,6949,6821,6949,6948,6822,6823,6950,6822,6950,6949,6823,6824,6951,6823,6951,6950,6824,6825,6952,6824,6952,6951,6825,6826,6953,6825,6953,6952,6826,6827,6954,6826,6954,6953,6827,6828,6955,6827,6955,6954,6828,6829,6956,6828,6956,6955,6829,6830,6957,6829,6957,6956,6830,6831,6958,6830,6958,6957,6831,6832,6959,6831,6959,6958,6832,6833,6960,6832,6960,6959,6833,6834,6961,6833,6961,6960,6834,6835,6962,6834,6962,6961,6835,6836,6963,6835,6963,6962,6836,6837,6964,6836,6964,6963,6837,6838,6965,6837,6965,6964,6838,6839,6966,6838,6966,6965,6839,6840,6967,6839,6967,6966,6840,6841,6968,6840,6968,6967,6841,6842,6969,6841,6969,6968,6842,6843,6970,6842,6970,6969,6843,6844,6971,6843,6971,6970,6844,6845,6972,6844,6972,6971,6845,6846,6973,6845,6973,6972,6846,6847,6974,6846,6974,6973,6847,6848,6975,6847,6975,6974,6848,6849,6976,6848,6976,6975,6849,6850,6977,6849,6977,6976,6850,6851,6978,6850,6978,6977,6851,6852,6979,6851,6979,6978,6852,6853,6980,6852,6980,6979,6853,6854,6981,6853,6981,6980,6854,6855,6982,6854,6982,6981,6855,6856,6983,6855,6983,6982,6856,6857,6984,6856,6984,6983,6858,6859,6986,6858,6986,6985,6859,6860,6987,6859,6987,6986,6860,6861,6988,6860,6988,6987,6861,6862,6989,6861,6989,6988,6862,6863,6990,6862,6990,6989,6863,6864,6991,6863,6991,6990,6864,6865,6992,6864,6992,6991,6865,6866,6993,6865,6993,6992,6866,6867,6994,6866,6994,6993,6867,6868,6995,6867,6995,6994,6868,6869,6996,6868,6996,6995,6869,6870,6997,6869,6997,6996,6870,6871,6998,6870,6998,6997,6871,6872,6999,6871,6999,6998,6872,6873,7000,6872,7000,6999,6873,6874,7001,6873,7001,7000,6874,6875,7002,6874,7002,7001,6875,6876,7003,6875,7003,7002,6876,6877,7004,6876,7004,7003,6877,6878,7005,6877,7005,7004,6878,6879,7006,6878,7006,7005,6879,6880,7007,6879,7007,7006,6880,6881,7008,6880,7008,7007,6881,6882,7009,6881,7009,7008,6882,6883,7010,6882,7010,7009,6883,6884,7011,6883,7011,7010,6884,6885,7012,6884,7012,7011,6885,6886,7013,6885,7013,7012,6886,6887,7014,6886,7014,7013,6887,6888,7015,6887,7015,7014,6888,6889,7016,6888,7016,7015,6889,6890,7017,6889,7017,7016,6890,6891,7018,6890,7018,7017,6891,6892,7019,6891,7019,7018,6892,6893,7020,6892,7020,7019,6893,6894,7021,6893,7021,7020,6894,6895,7022,6894,7022,7021,6895,6896,7023,6895,7023,7022,6896,6897,7024,6896,7024,7023,6897,6898,7025,6897,7025,7024,6898,6899,7026,6898,7026,7025,6899,6900,7027,6899,7027,7026,6900,6901,7028,6900,7028,7027,6901,6902,7029,6901,7029,7028,6902,6903,7030,6902,7030,7029,6903,6904,7031,6903,7031,7030,6904,6905,7032,6904,7032,7031,6905,6906,7033,6905,7033,7032,6906,6907,7034,6906,7034,7033,6907,6908,7035,6907,7035,7034,6908,6909,7036,6908,7036,7035,6909,6910,7037,6909,7037,7036,6910,6911,7038,6910,7038,7037,6911,6912,7039,6911,7039,7038,6912,6913,7040,6912,7040,7039,6913,6914,7041,6913,7041,7040,6914,6915,7042,6914,7042,7041,6915,6916,7043,6915,7043,7042,6916,6917,7044,6916,7044,7043,6917,6918,7045,6917,7045,7044,6918,6919,7046,6918,7046,7045,6919,6920,7047,6919,7047,7046,6920,6921,7048,6920,7048,7047,6921,6922,7049,6921,7049,7048,6922,6923,7050,6922,7050,7049,6923,6924,7051,6923,7051,7050,6924,6925,7052,6924,7052,7051,6925,6926,7053,6925,7053,7052,6926,6927,7054,6926,7054,7053,6927,6928,7055,6927,7055,7054,6928,6929,7056,6928,7056,7055,6929,6930,7057,6929,7057,7056,6930,6931,7058,6930,7058,7057,6931,6932,7059,6931,7059,7058,6932,6933,7060,6932,7060,7059,6933,6934,7061,6933,7061,7060,6934,6935,7062,6934,7062,7061,6935,6936,7063,6935,7063,7062,6936,6937,7064,6936,7064,7063,6937,6938,7065,6937,7065,7064,6938,6939,7066,6938,7066,7065,6939,6940,7067,6939,7067,7066,6940,6941,7068,6940,7068,7067,6941,6942,7069,6941,7069,7068,6942,6943,7070,6942,7070,7069,6943,6944,7071,6943,7071,7070,6944,6945,7072,6944,7072,7071,6945,6946,7073,6945,7073,7072,6946,6947,7074,6946,7074,7073,6947,6948,7075,6947,7075,7074,6948,6949,7076,6948,7076,7075,6949,6950,7077,6949,7077,7076,6950,6951,7078,6950,7078,7077,6951,6952,7079,6951,7079,7078,6952,6953,7080,6952,7080,7079,6953,6954,7081,6953,7081,7080,6954,6955,7082,6954,7082,7081,6955,6956,7083,6955,7083,7082,6956,6957,7084,6956,7084,7083,6957,6958,7085,6957,7085,7084,6958,6959,7086,6958,7086,7085,6959,6960,7087,6959,7087,7086,6960,6961,7088,6960,7088,7087,6961,6962,7089,6961,7089,7088,6962,6963,7090,6962,7090,7089,6963,6964,7091,6963,7091,7090,6964,6965,7092,6964,7092,7091,6965,6966,7093,6965,7093,7092,6966,6967,7094,6966,7094,7093,6967,6968,7095,6967,7095,7094,6968,6969,7096,6968,7096,7095,6969,6970,7097,6969,7097,7096,6970,6971,7098,6970,7098,7097,6971,6972,7099,6971,7099,7098,6972,6973,7100,6972,7100,7099,6973,6974,7101,6973,7101,7100,6974,6975,7102,6974,7102,7101,6975,6976,7103,6975,7103,7102,6976,6977,7104,6976,7104,7103,6977,6978,7105,6977,7105,7104,6978,6979,7106,6978,7106,7105,6979,6980,7107,6979,7107,7106,6980,6981,7108,6980,7108,7107,6981,6982,7109,6981,7109,7108,6982,6983,7110,6982,7110,7109,6983,6984,7111,6983,7111,7110,6985,6986,7113,6985,7113,7112,6986,6987,7114,6986,7114,7113,6987,6988,7115,6987,7115,7114,6988,6989,7116,6988,7116,7115,6989,6990,7117,6989,7117,7116,6990,6991,7118,6990,7118,7117,6991,6992,7119,6991,7119,7118,6992,6993,7120,6992,7120,7119,6993,6994,7121,6993,7121,7120,6994,6995,7122,6994,7122,7121,6995,6996,7123,6995,7123,7122,6996,6997,7124,6996,7124,7123,6997,6998,7125,6997,7125,7124,6998,6999,7126,6998,7126,7125,6999,7000,7127,6999,7127,7126,7000,7001,7128,7000,7128,7127,7001,7002,7129,7001,7129,7128,7002,7003,7130,7002,7130,7129,7003,7004,7131,7003,7131,7130,7004,7005,7132,7004,7132,7131,7005,7006,7133,7005,7133,7132,7006,7007,7134,7006,7134,7133,7007,7008,7135,7007,7135,7134,7008,7009,7136,7008,7136,7135,7009,7010,7137,7009,7137,7136,7010,7011,7138,7010,7138,7137,7011,7012,7139,7011,7139,7138,7012,7013,7140,7012,7140,7139,7013,7014,7141,7013,7141,7140,7014,7015,7142,7014,7142,7141,7015,7016,7143,7015,7143,7142,7016,7017,7144,7016,7144,7143,7017,7018,7145,7017,7145,7144,7018,7019,7146,7018,7146,7145,7019,7020,7147,7019,7147,7146,7020,7021,7148,7020,7148,7147,7021,7022,7149,7021,7149,7148,7022,7023,7150,7022,7150,7149,7023,7024,7151,7023,7151,7150,7024,7025,7152,7024,7152,7151,7025,7026,7153,7025,7153,7152,7026,7027,7154,7026,7154,7153,7027,7028,7155,7027,7155,7154,7028,7029,7156,7028,7156,7155,7029,7030,7157,7029,7157,7156,7030,7031,7158,7030,7158,7157,7031,7032,7159,7031,7159,7158,7032,7033,7160,7032,7160,7159,7033,7034,7161,7033,7161,7160,7034,7035,7162,7034,7162,7161,7035,7036,7163,7035,7163,7162,7036,7037,7164,7036,7164,7163,7037,7038,7165,7037,7165,7164,7038,7039,7166,7038,7166,7165,7039,7040,7167,7039,7167,7166,7040,7041,7168,7040,7168,7167,7041,7042,7169,7041,7169,7168,7042,7043,7170,7042,7170,7169,7043,7044,7171,7043,7171,7170,7044,7045,7172,7044,7172,7171,7045,7046,7173,7045,7173,7172,7046,7047,7174,7046,7174,7173,7047,7048,7175,7047,7175,7174,7048,7049,7176,7048,7176,7175,7049,7050,7177,7049,7177,7176,7050,7051,7178,7050,7178,7177,7051,7052,7179,7051,7179,7178,7052,7053,7180,7052,7180,7179,7053,7054,7181,7053,7181,7180,7054,7055,7182,7054,7182,7181,7055,7056,7183,7055,7183,7182,7056,7057,7184,7056,7184,7183,7057,7058,7185,7057,7185,7184,7058,7059,7186,7058,7186,7185,7059,7060,7187,7059,7187,7186,7060,7061,7188,7060,7188,7187,7061,7062,7189,7061,7189,7188,7062,7063,7190,7062,7190,7189,7063,7064,7191,7063,7191,7190,7064,7065,7192,7064,7192,7191,7065,7066,7193,7065,7193,7192,7066,7067,7194,7066,7194,7193,7067,7068,7195,7067,7195,7194,7068,7069,7196,7068,7196,7195,7069,7070,7197,7069,7197,7196,7070,7071,7198,7070,7198,7197,7071,7072,7199,7071,7199,7198,7072,7073,7200,7072,7200,7199,7073,7074,7201,7073,7201,7200,7074,7075,7202,7074,7202,7201,7075,7076,7203,7075,7203,7202,7076,7077,7204,7076,7204,7203,7077,7078,7205,7077,7205,7204,7078,7079,7206,7078,7206,7205,7079,7080,7207,7079,7207,7206,7080,7081,7208,7080,7208,7207,7081,7082,7209,7081,7209,7208,7082,7083,7210,7082,7210,7209,7083,7084,7211,7083,7211,7210,7084,7085,7212,7084,7212,7211,7085,7086,7213,7085,7213,7212,7086,7087,7214,7086,7214,7213,7087,7088,7215,7087,7215,7214,7088,7089,7216,7088,7216,7215,7089,7090,7217,7089,7217,7216,7090,7091,7218,7090,7218,7217,7091,7092,7219,7091,7219,7218,7092,7093,7220,7092,7220,7219,7093,7094,7221,7093,7221,7220,7094,7095,7222,7094,7222,7221,7095,7096,7223,7095,7223,7222,7096,7097,7224,7096,7224,7223,7097,7098,7225,7097,7225,7224,7098,7099,7226,7098,7226,7225,7099,7100,7227,7099,7227,7226,7100,7101,7228,7100,7228,7227,7101,7102,7229,7101,7229,7228,7102,7103,7230,7102,7230,7229,7103,7104,7231,7103,7231,7230,7104,7105,7232,7104,7232,7231,7105,7106,7233,7105,7233,7232,7106,7107,7234,7106,7234,7233,7107,7108,7235,7107,7235,7234,7108,7109,7236,7108,7236,7235,7109,7110,7237,7109,7237,7236,7110,7111,7238,7110,7238,7237,7112,7113,7240,7112,7240,7239,7113,7114,7241,7113,7241,7240,7114,7115,7242,7114,7242,7241,7115,7116,7243,7115,7243,7242,7116,7117,7244,7116,7244,7243,7117,7118,7245,7117,7245,7244,7118,7119,7246,7118,7246,7245,7119,7120,7247,7119,7247,7246,7120,7121,7248,7120,7248,7247,7121,7122,7249,7121,7249,7248,7122,7123,7250,7122,7250,7249,7123,7124,7251,7123,7251,7250,7124,7125,7252,7124,7252,7251,7125,7126,7253,7125,7253,7252,7126,7127,7254,7126,7254,7253,7127,7128,7255,7127,7255,7254,7128,7129,7256,7128,7256,7255,7129,7130,7257,7129,7257,7256,7130,7131,7258,7130,7258,7257,7131,7132,7259,7131,7259,7258,7132,7133,7260,7132,7260,7259,7133,7134,7261,7133,7261,7260,7134,7135,7262,7134,7262,7261,7135,7136,7263,7135,7263,7262,7136,7137,7264,7136,7264,7263,7137,7138,7265,7137,7265,7264,7138,7139,7266,7138,7266,7265,7139,7140,7267,7139,7267,7266,7140,7141,7268,7140,7268,7267,7141,7142,7269,7141,7269,7268,7142,7143,7270,7142,7270,7269,7143,7144,7271,7143,7271,7270,7144,7145,7272,7144,7272,7271,7145,7146,7273,7145,7273,7272,7146,7147,7274,7146,7274,7273,7147,7148,7275,7147,7275,7274,7148,7149,7276,7148,7276,7275,7149,7150,7277,7149,7277,7276,7150,7151,7278,7150,7278,7277,7151,7152,7279,7151,7279,7278,7152,7153,7280,7152,7280,7279,7153,7154,7281,7153,7281,7280,7154,7155,7282,7154,7282,7281,7155,7156,7283,7155,7283,7282,7156,7157,7284,7156,7284,7283,7157,7158,7285,7157,7285,7284,7158,7159,7286,7158,7286,7285,7159,7160,7287,7159,7287,7286,7160,7161,7288,7160,7288,7287,7161,7162,7289,7161,7289,7288,7162,7163,7290,7162,7290,7289,7163,7164,7291,7163,7291,7290,7164,7165,7292,7164,7292,7291,7165,7166,7293,7165,7293,7292,7166,7167,7294,7166,7294,7293,7167,7168,7295,7167,7295,7294,7168,7169,7296,7168,7296,7295,7169,7170,7297,7169,7297,7296,7170,7171,7298,7170,7298,7297,7171,7172,7299,7171,7299,7298,7172,7173,7300,7172,7300,7299,7173,7174,7301,7173,7301,7300,7174,7175,7302,7174,7302,7301,7175,7176,7303,7175,7303,7302,7176,7177,7304,7176,7304,7303,7177,7178,7305,7177,7305,7304,7178,7179,7306,7178,7306,7305,7179,7180,7307,7179,7307,7306,7180,7181,7308,7180,7308,7307,7181,7182,7309,7181,7309,7308,7182,7183,7310,7182,7310,7309,7183,7184,7311,7183,7311,7310,7184,7185,7312,7184,7312,7311,7185,7186,7313,7185,7313,7312,7186,7187,7314,7186,7314,7313,7187,7188,7315,7187,7315,7314,7188,7189,7316,7188,7316,7315,7189,7190,7317,7189,7317,7316,7190,7191,7318,7190,7318,7317,7191,7192,7319,7191,7319,7318,7192,7193,7320,7192,7320,7319,7193,7194,7321,7193,7321,7320,7194,7195,7322,7194,7322,7321,7195,7196,7323,7195,7323,7322,7196,7197,7324,7196,7324,7323,7197,7198,7325,7197,7325,7324,7198,7199,7326,7198,7326,7325,7199,7200,7327,7199,7327,7326,7200,7201,7328,7200,7328,7327,7201,7202,7329,7201,7329,7328,7202,7203,7330,7202,7330,7329,7203,7204,7331,7203,7331,7330,7204,7205,7332,7204,7332,7331,7205,7206,7333,7205,7333,7332,7206,7207,7334,7206,7334,7333,7207,7208,7335,7207,7335,7334,7208,7209,7336,7208,7336,7335,7209,7210,7337,7209,7337,7336,7210,7211,7338,7210,7338,7337,7211,7212,7339,7211,7339,7338,7212,7213,7340,7212,7340,7339,7213,7214,7341,7213,7341,7340,7214,7215,7342,7214,7342,7341,7215,7216,7343,7215,7343,7342,7216,7217,7344,7216,7344,7343,7217,7218,7345,7217,7345,7344,7218,7219,7346,7218,7346,7345,7219,7220,7347,7219,7347,7346,7220,7221,7348,7220,7348,7347,7221,7222,7349,7221,7349,7348,7222,7223,7350,7222,7350,7349,7223,7224,7351,7223,7351,7350,7224,7225,7352,7224,7352,7351,7225,7226,7353,7225,7353,7352,7226,7227,7354,7226,7354,7353,7227,7228,7355,7227,7355,7354,7228,7229,7356,7228,7356,7355,7229,7230,7357,7229,7357,7356,7230,7231,7358,7230,7358,7357,7231,7232,7359,7231,7359,7358,7232,7233,7360,7232,7360,7359,7233,7234,7361,7233,7361,7360,7234,7235,7362,7234,7362,7361,7235,7236,7363,7235,7363,7362,7236,7237,7364,7236,7364,7363,7237,7238,7365,7237,7365,7364,7239,7240,7367,7239,7367,7366,7240,7241,7368,7240,7368,7367,7241,7242,7369,7241,7369,7368,7242,7243,7370,7242,7370,7369,7243,7244,7371,7243,7371,7370,7244,7245,7372,7244,7372,7371,7245,7246,7373,7245,7373,7372,7246,7247,7374,7246,7374,7373,7247,7248,7375,7247,7375,7374,7248,7249,7376,7248,7376,7375,7249,7250,7377,7249,7377,7376,7250,7251,7378,7250,7378,7377,7251,7252,7379,7251,7379,7378,7252,7253,7380,7252,7380,7379,7253,7254,7381,7253,7381,7380,7254,7255,7382,7254,7382,7381,7255,7256,7383,7255,7383,7382,7256,7257,7384,7256,7384,7383,7257,7258,7385,7257,7385,7384,7258,7259,7386,7258,7386,7385,7259,7260,7387,7259,7387,7386,7260,7261,7388,7260,7388,7387,7261,7262,7389,7261,7389,7388,7262,7263,7390,7262,7390,7389,7263,7264,7391,7263,7391,7390,7264,7265,7392,7264,7392,7391,7265,7266,7393,7265,7393,7392,7266,7267,7394,7266,7394,7393,7267,7268,7395,7267,7395,7394,7268,7269,7396,7268,7396,7395,7269,7270,7397,7269,7397,7396,7270,7271,7398,7270,7398,7397,7271,7272,7399,7271,7399,7398,7272,7273,7400,7272,7400,7399,7273,7274,7401,7273,7401,7400,7274,7275,7402,7274,7402,7401,7275,7276,7403,7275,7403,7402,7276,7277,7404,7276,7404,7403,7277,7278,7405,7277,7405,7404,7278,7279,7406,7278,7406,7405,7279,7280,7407,7279,7407,7406,7280,7281,7408,7280,7408,7407,7281,7282,7409,7281,7409,7408,7282,7283,7410,7282,7410,7409,7283,7284,7411,7283,7411,7410,7284,7285,7412,7284,7412,7411,7285,7286,7413,7285,7413,7412,7286,7287,7414,7286,7414,7413,7287,7288,7415,7287,7415,7414,7288,7289,7416,7288,7416,7415,7289,7290,7417,7289,7417,7416,7290,7291,7418,7290,7418,7417,7291,7292,7419,7291,7419,7418,7292,7293,7420,7292,7420,7419,7293,7294,7421,7293,7421,7420,7294,7295,7422,7294,7422,7421,7295,7296,7423,7295,7423,7422,7296,7297,7424,7296,7424,7423,7297,7298,7425,7297,7425,7424,7298,7299,7426,7298,7426,7425,7299,7300,7427,7299,7427,7426,7300,7301,7428,7300,7428,7427,7301,7302,7429,7301,7429,7428,7302,7303,7430,7302,7430,7429,7303,7304,7431,7303,7431,7430,7304,7305,7432,7304,7432,7431,7305,7306,7433,7305,7433,7432,7306,7307,7434,7306,7434,7433,7307,7308,7435,7307,7435,7434,7308,7309,7436,7308,7436,7435,7309,7310,7437,7309,7437,7436,7310,7311,7438,7310,7438,7437,7311,7312,7439,7311,7439,7438,7312,7313,7440,7312,7440,7439,7313,7314,7441,7313,7441,7440,7314,7315,7442,7314,7442,7441,7315,7316,7443,7315,7443,7442,7316,7317,7444,7316,7444,7443,7317,7318,7445,7317,7445,7444,7318,7319,7446,7318,7446,7445,7319,7320,7447,7319,7447,7446,7320,7321,7448,7320,7448,7447,7321,7322,7449,7321,7449,7448,7322,7323,7450,7322,7450,7449,7323,7324,7451,7323,7451,7450,7324,7325,7452,7324,7452,7451,7325,7326,7453,7325,7453,7452,7326,7327,7454,7326,7454,7453,7327,7328,7455,7327,7455,7454,7328,7329,7456,7328,7456,7455,7329,7330,7457,7329,7457,7456,7330,7331,7458,7330,7458,7457,7331,7332,7459,7331,7459,7458,7332,7333,7460,7332,7460,7459,7333,7334,7461,7333,7461,7460,7334,7335,7462,7334,7462,7461,7335,7336,7463,7335,7463,7462,7336,7337,7464,7336,7464,7463,7337,7338,7465,7337,7465,7464,7338,7339,7466,7338,7466,7465,7339,7340,7467,7339,7467,7466,7340,7341,7468,7340,7468,7467,7341,7342,7469,7341,7469,7468,7342,7343,7470,7342,7470,7469,7343,7344,7471,7343,7471,7470,7344,7345,7472,7344,7472,7471,7345,7346,7473,7345,7473,7472,7346,7347,7474,7346,7474,7473,7347,7348,7475,7347,7475,7474,7348,7349,7476,7348,7476,7475,7349,7350,7477,7349,7477,7476,7350,7351,7478,7350,7478,7477,7351,7352,7479,7351,7479,7478,7352,7353,7480,7352,7480,7479,7353,7354,7481,7353,7481,7480,7354,7355,7482,7354,7482,7481,7355,7356,7483,7355,7483,7482,7356,7357,7484,7356,7484,7483,7357,7358,7485,7357,7485,7484,7358,7359,7486,7358,7486,7485,7359,7360,7487,7359,7487,7486,7360,7361,7488,7360,7488,7487,7361,7362,7489,7361,7489,7488,7362,7363,7490,7362,7490,7489,7363,7364,7491,7363,7491,7490,7364,7365,7492,7364,7492,7491,7366,7367,7494,7366,7494,7493,7367,7368,7495,7367,7495,7494,7368,7369,7496,7368,7496,7495,7369,7370,7497,7369,7497,7496,7370,7371,7498,7370,7498,7497,7371,7372,7499,7371,7499,7498,7372,7373,7500,7372,7500,7499,7373,7374,7501,7373,7501,7500,7374,7375,7502,7374,7502,7501,7375,7376,7503,7375,7503,7502,7376,7377,7504,7376,7504,7503,7377,7378,7505,7377,7505,7504,7378,7379,7506,7378,7506,7505,7379,7380,7507,7379,7507,7506,7380,7381,7508,7380,7508,7507,7381,7382,7509,7381,7509,7508,7382,7383,7510,7382,7510,7509,7383,7384,7511,7383,7511,7510,7384,7385,7512,7384,7512,7511,7385,7386,7513,7385,7513,7512,7386,7387,7514,7386,7514,7513,7387,7388,7515,7387,7515,7514,7388,7389,7516,7388,7516,7515,7389,7390,7517,7389,7517,7516,7390,7391,7518,7390,7518,7517,7391,7392,7519,7391,7519,7518,7392,7393,7520,7392,7520,7519,7393,7394,7521,7393,7521,7520,7394,7395,7522,7394,7522,7521,7395,7396,7523,7395,7523,7522,7396,7397,7524,7396,7524,7523,7397,7398,7525,7397,7525,7524,7398,7399,7526,7398,7526,7525,7399,7400,7527,7399,7527,7526,7400,7401,7528,7400,7528,7527,7401,7402,7529,7401,7529,7528,7402,7403,7530,7402,7530,7529,7403,7404,7531,7403,7531,7530,7404,7405,7532,7404,7532,7531,7405,7406,7533,7405,7533,7532,7406,7407,7534,7406,7534,7533,7407,7408,7535,7407,7535,7534,7408,7409,7536,7408,7536,7535,7409,7410,7537,7409,7537,7536,7410,7411,7538,7410,7538,7537,7411,7412,7539,7411,7539,7538,7412,7413,7540,7412,7540,7539,7413,7414,7541,7413,7541,7540,7414,7415,7542,7414,7542,7541,7415,7416,7543,7415,7543,7542,7416,7417,7544,7416,7544,7543,7417,7418,7545,7417,7545,7544,7418,7419,7546,7418,7546,7545,7419,7420,7547,7419,7547,7546,7420,7421,7548,7420,7548,7547,7421,7422,7549,7421,7549,7548,7422,7423,7550,7422,7550,7549,7423,7424,7551,7423,7551,7550,7424,7425,7552,7424,7552,7551,7425,7426,7553,7425,7553,7552,7426,7427,7554,7426,7554,7553,7427,7428,7555,7427,7555,7554,7428,7429,7556,7428,7556,7555,7429,7430,7557,7429,7557,7556,7430,7431,7558,7430,7558,7557,7431,7432,7559,7431,7559,7558,7432,7433,7560,7432,7560,7559,7433,7434,7561,7433,7561,7560,7434,7435,7562,7434,7562,7561,7435,7436,7563,7435,7563,7562,7436,7437,7564,7436,7564,7563,7437,7438,7565,7437,7565,7564,7438,7439,7566,7438,7566,7565,7439,7440,7567,7439,7567,7566,7440,7441,7568,7440,7568,7567,7441,7442,7569,7441,7569,7568,7442,7443,7570,7442,7570,7569,7443,7444,7571,7443,7571,7570,7444,7445,7572,7444,7572,7571,7445,7446,7573,7445,7573,7572,7446,7447,7574,7446,7574,7573,7447,7448,7575,7447,7575,7574,7448,7449,7576,7448,7576,7575,7449,7450,7577,7449,7577,7576,7450,7451,7578,7450,7578,7577,7451,7452,7579,7451,7579,7578,7452,7453,7580,7452,7580,7579,7453,7454,7581,7453,7581,7580,7454,7455,7582,7454,7582,7581,7455,7456,7583,7455,7583,7582,7456,7457,7584,7456,7584,7583,7457,7458,7585,7457,7585,7584,7458,7459,7586,7458,7586,7585,7459,7460,7587,7459,7587,7586,7460,7461,7588,7460,7588,7587,7461,7462,7589,7461,7589,7588,7462,7463,7590,7462,7590,7589,7463,7464,7591,7463,7591,7590,7464,7465,7592,7464,7592,7591,7465,7466,7593,7465,7593,7592,7466,7467,7594,7466,7594,7593,7467,7468,7595,7467,7595,7594,7468,7469,7596,7468,7596,7595,7469,7470,7597,7469,7597,7596,7470,7471,7598,7470,7598,7597,7471,7472,7599,7471,7599,7598,7472,7473,7600,7472,7600,7599,7473,7474,7601,7473,7601,7600,7474,7475,7602,7474,7602,7601,7475,7476,7603,7475,7603,7602,7476,7477,7604,7476,7604,7603,7477,7478,7605,7477,7605,7604,7478,7479,7606,7478,7606,7605,7479,7480,7607,7479,7607,7606,7480,7481,7608,7480,7608,7607,7481,7482,7609,7481,7609,7608,7482,7483,7610,7482,7610,7609,7483,7484,7611,7483,7611,7610,7484,7485,7612,7484,7612,7611,7485,7486,7613,7485,7613,7612,7486,7487,7614,7486,7614,7613,7487,7488,7615,7487,7615,7614,7488,7489,7616,7488,7616,7615,7489,7490,7617,7489,7617,7616,7490,7491,7618,7490,7618,7617,7491,7492,7619,7491,7619,7618,7493,7494,7621,7493,7621,7620,7494,7495,7622,7494,7622,7621,7495,7496,7623,7495,7623,7622,7496,7497,7624,7496,7624,7623,7497,7498,7625,7497,7625,7624,7498,7499,7626,7498,7626,7625,7499,7500,7627,7499,7627,7626,7500,7501,7628,7500,7628,7627,7501,7502,7629,7501,7629,7628,7502,7503,7630,7502,7630,7629,7503,7504,7631,7503,7631,7630,7504,7505,7632,7504,7632,7631,7505,7506,7633,7505,7633,7632,7506,7507,7634,7506,7634,7633,7507,7508,7635,7507,7635,7634,7508,7509,7636,7508,7636,7635,7509,7510,7637,7509,7637,7636,7510,7511,7638,7510,7638,7637,7511,7512,7639,7511,7639,7638,7512,7513,7640,7512,7640,7639,7513,7514,7641,7513,7641,7640,7514,7515,7642,7514,7642,7641,7515,7516,7643,7515,7643,7642,7516,7517,7644,7516,7644,7643,7517,7518,7645,7517,7645,7644,7518,7519,7646,7518,7646,7645,7519,7520,7647,7519,7647,7646,7520,7521,7648,7520,7648,7647,7521,7522,7649,7521,7649,7648,7522,7523,7650,7522,7650,7649,7523,7524,7651,7523,7651,7650,7524,7525,7652,7524,7652,7651,7525,7526,7653,7525,7653,7652,7526,7527,7654,7526,7654,7653,7527,7528,7655,7527,7655,7654,7528,7529,7656,7528,7656,7655,7529,7530,7657,7529,7657,7656,7530,7531,7658,7530,7658,7657,7531,7532,7659,7531,7659,7658,7532,7533,7660,7532,7660,7659,7533,7534,7661,7533,7661,7660,7534,7535,7662,7534,7662,7661,7535,7536,7663,7535,7663,7662,7536,7537,7664,7536,7664,7663,7537,7538,7665,7537,7665,7664,7538,7539,7666,7538,7666,7665,7539,7540,7667,7539,7667,7666,7540,7541,7668,7540,7668,7667,7541,7542,7669,7541,7669,7668,7542,7543,7670,7542,7670,7669,7543,7544,7671,7543,7671,7670,7544,7545,7672,7544,7672,7671,7545,7546,7673,7545,7673,7672,7546,7547,7674,7546,7674,7673,7547,7548,7675,7547,7675,7674,7548,7549,7676,7548,7676,7675,7549,7550,7677,7549,7677,7676,7550,7551,7678,7550,7678,7677,7551,7552,7679,7551,7679,7678,7552,7553,7680,7552,7680,7679,7553,7554,7681,7553,7681,7680,7554,7555,7682,7554,7682,7681,7555,7556,7683,7555,7683,7682,7556,7557,7684,7556,7684,7683,7557,7558,7685,7557,7685,7684,7558,7559,7686,7558,7686,7685,7559,7560,7687,7559,7687,7686,7560,7561,7688,7560,7688,7687,7561,7562,7689,7561,7689,7688,7562,7563,7690,7562,7690,7689,7563,7564,7691,7563,7691,7690,7564,7565,7692,7564,7692,7691,7565,7566,7693,7565,7693,7692,7566,7567,7694,7566,7694,7693,7567,7568,7695,7567,7695,7694,7568,7569,7696,7568,7696,7695,7569,7570,7697,7569,7697,7696,7570,7571,7698,7570,7698,7697,7571,7572,7699,7571,7699,7698,7572,7573,7700,7572,7700,7699,7573,7574,7701,7573,7701,7700,7574,7575,7702,7574,7702,7701,7575,7576,7703,7575,7703,7702,7576,7577,7704,7576,7704,7703,7577,7578,7705,7577,7705,7704,7578,7579,7706,7578,7706,7705,7579,7580,7707,7579,7707,7706,7580,7581,7708,7580,7708,7707,7581,7582,7709,7581,7709,7708,7582,7583,7710,7582,7710,7709,7583,7584,7711,7583,7711,7710,7584,7585,7712,7584,7712,7711,7585,7586,7713,7585,7713,7712,7586,7587,7714,7586,7714,7713,7587,7588,7715,7587,7715,7714,7588,7589,7716,7588,7716,7715,7589,7590,7717,7589,7717,7716,7590,7591,7718,7590,7718,7717,7591,7592,7719,7591,7719,7718,7592,7593,7720,7592,7720,7719,7593,7594,7721,7593,7721,7720,7594,7595,7722,7594,7722,7721,7595,7596,7723,7595,7723,7722,7596,7597,7724,7596,7724,7723,7597,7598,7725,7597,7725,7724,7598,7599,7726,7598,7726,7725,7599,7600,7727,7599,7727,7726,7600,7601,7728,7600,7728,7727,7601,7602,7729,7601,7729,7728,7602,7603,7730,7602,7730,7729,7603,7604,7731,7603,7731,7730,7604,7605,7732,7604,7732,7731,7605,7606,7733,7605,7733,7732,7606,7607,7734,7606,7734,7733,7607,7608,7735,7607,7735,7734,7608,7609,7736,7608,7736,7735,7609,7610,7737,7609,7737,7736,7610,7611,7738,7610,7738,7737,7611,7612,7739,7611,7739,7738,7612,7613,7740,7612,7740,7739,7613,7614,7741,7613,7741,7740,7614,7615,7742,7614,7742,7741,7615,7616,7743,7615,7743,7742,7616,7617,7744,7616,7744,7743,7617,7618,7745,7617,7745,7744,7618,7619,7746,7618,7746,7745,7620,7621,7748,7620,7748,7747,7621,7622,7749,7621,7749,7748,7622,7623,7750,7622,7750,7749,7623,7624,7751,7623,7751,7750,7624,7625,7752,7624,7752,7751,7625,7626,7753,7625,7753,7752,7626,7627,7754,7626,7754,7753,7627,7628,7755,7627,7755,7754,7628,7629,7756,7628,7756,7755,7629,7630,7757,7629,7757,7756,7630,7631,7758,7630,7758,7757,7631,7632,7759,7631,7759,7758,7632,7633,7760,7632,7760,7759,7633,7634,7761,7633,7761,7760,7634,7635,7762,7634,7762,7761,7635,7636,7763,7635,7763,7762,7636,7637,7764,7636,7764,7763,7637,7638,7765,7637,7765,7764,7638,7639,7766,7638,7766,7765,7639,7640,7767,7639,7767,7766,7640,7641,7768,7640,7768,7767,7641,7642,7769,7641,7769,7768,7642,7643,7770,7642,7770,7769,7643,7644,7771,7643,7771,7770,7644,7645,7772,7644,7772,7771,7645,7646,7773,7645,7773,7772,7646,7647,7774,7646,7774,7773,7647,7648,7775,7647,7775,7774,7648,7649,7776,7648,7776,7775,7649,7650,7777,7649,7777,7776,7650,7651,7778,7650,7778,7777,7651,7652,7779,7651,7779,7778,7652,7653,7780,7652,7780,7779,7653,7654,7781,7653,7781,7780,7654,7655,7782,7654,7782,7781,7655,7656,7783,7655,7783,7782,7656,7657,7784,7656,7784,7783,7657,7658,7785,7657,7785,7784,7658,7659,7786,7658,7786,7785,7659,7660,7787,7659,7787,7786,7660,7661,7788,7660,7788,7787,7661,7662,7789,7661,7789,7788,7662,7663,7790,7662,7790,7789,7663,7664,7791,7663,7791,7790,7664,7665,7792,7664,7792,7791,7665,7666,7793,7665,7793,7792,7666,7667,7794,7666,7794,7793,7667,7668,7795,7667,7795,7794,7668,7669,7796,7668,7796,7795,7669,7670,7797,7669,7797,7796,7670,7671,7798,7670,7798,7797,7671,7672,7799,7671,7799,7798,7672,7673,7800,7672,7800,7799,7673,7674,7801,7673,7801,7800,7674,7675,7802,7674,7802,7801,7675,7676,7803,7675,7803,7802,7676,7677,7804,7676,7804,7803,7677,7678,7805,7677,7805,7804,7678,7679,7806,7678,7806,7805,7679,7680,7807,7679,7807,7806,7680,7681,7808,7680,7808,7807,7681,7682,7809,7681,7809,7808,7682,7683,7810,7682,7810,7809,7683,7684,7811,7683,7811,7810,7684,7685,7812,7684,7812,7811,7685,7686,7813,7685,7813,7812,7686,7687,7814,7686,7814,7813,7687,7688,7815,7687,7815,7814,7688,7689,7816,7688,7816,7815,7689,7690,7817,7689,7817,7816,7690,7691,7818,7690,7818,7817,7691,7692,7819,7691,7819,7818,7692,7693,7820,7692,7820,7819,7693,7694,7821,7693,7821,7820,7694,7695,7822,7694,7822,7821,7695,7696,7823,7695,7823,7822,7696,7697,7824,7696,7824,7823,7697,7698,7825,7697,7825,7824,7698,7699,7826,7698,7826,7825,7699,7700,7827,7699,7827,7826,7700,7701,7828,7700,7828,7827,7701,7702,7829,7701,7829,7828,7702,7703,7830,7702,7830,7829,7703,7704,7831,7703,7831,7830,7704,7705,7832,7704,7832,7831,7705,7706,7833,7705,7833,7832,7706,7707,7834,7706,7834,7833,7707,7708,7835,7707,7835,7834,7708,7709,7836,7708,7836,7835,7709,7710,7837,7709,7837,7836,7710,7711,7838,7710,7838,7837,7711,7712,7839,7711,7839,7838,7712,7713,7840,7712,7840,7839,7713,7714,7841,7713,7841,7840,7714,7715,7842,7714,7842,7841,7715,7716,7843,7715,7843,7842,7716,7717,7844,7716,7844,7843,7717,7718,7845,7717,7845,7844,7718,7719,7846,7718,7846,7845,7719,7720,7847,7719,7847,7846,7720,7721,7848,7720,7848,7847,7721,7722,7849,7721,7849,7848,7722,7723,7850,7722,7850,7849,7723,7724,7851,7723,7851,7850,7724,7725,7852,7724,7852,7851,7725,7726,7853,7725,7853,7852,7726,7727,7854,7726,7854,7853,7727,7728,7855,7727,7855,7854,7728,7729,7856,7728,7856,7855,7729,7730,7857,7729,7857,7856,7730,7731,7858,7730,7858,7857,7731,7732,7859,7731,7859,7858,7732,7733,7860,7732,7860,7859,7733,7734,7861,7733,7861,7860,7734,7735,7862,7734,7862,7861,7735,7736,7863,7735,7863,7862,7736,7737,7864,7736,7864,7863,7737,7738,7865,7737,7865,7864,7738,7739,7866,7738,7866,7865,7739,7740,7867,7739,7867,7866,7740,7741,7868,7740,7868,7867,7741,7742,7869,7741,7869,7868,7742,7743,7870,7742,7870,7869,7743,7744,7871,7743,7871,7870,7744,7745,7872,7744,7872,7871,7745,7746,7873,7745,7873,7872,7747,7748,7875,7747,7875,7874,7748,7749,7876,7748,7876,7875,7749,7750,7877,7749,7877,7876,7750,7751,7878,7750,7878,7877,7751,7752,7879,7751,7879,7878,7752,7753,7880,7752,7880,7879,7753,7754,7881,7753,7881,7880,7754,7755,7882,7754,7882,7881,7755,7756,7883,7755,7883,7882,7756,7757,7884,7756,7884,7883,7757,7758,7885,7757,7885,7884,7758,7759,7886,7758,7886,7885,7759,7760,7887,7759,7887,7886,7760,7761,7888,7760,7888,7887,7761,7762,7889,7761,7889,7888,7762,7763,7890,7762,7890,7889,7763,7764,7891,7763,7891,7890,7764,7765,7892,7764,7892,7891,7765,7766,7893,7765,7893,7892,7766,7767,7894,7766,7894,7893,7767,7768,7895,7767,7895,7894,7768,7769,7896,7768,7896,7895,7769,7770,7897,7769,7897,7896,7770,7771,7898,7770,7898,7897,7771,7772,7899,7771,7899,7898,7772,7773,7900,7772,7900,7899,7773,7774,7901,7773,7901,7900,7774,7775,7902,7774,7902,7901,7775,7776,7903,7775,7903,7902,7776,7777,7904,7776,7904,7903,7777,7778,7905,7777,7905,7904,7778,7779,7906,7778,7906,7905,7779,7780,7907,7779,7907,7906,7780,7781,7908,7780,7908,7907,7781,7782,7909,7781,7909,7908,7782,7783,7910,7782,7910,7909,7783,7784,7911,7783,7911,7910,7784,7785,7912,7784,7912,7911,7785,7786,7913,7785,7913,7912,7786,7787,7914,7786,7914,7913,7787,7788,7915,7787,7915,7914,7788,7789,7916,7788,7916,7915,7789,7790,7917,7789,7917,7916,7790,7791,7918,7790,7918,7917,7791,7792,7919,7791,7919,7918,7792,7793,7920,7792,7920,7919,7793,7794,7921,7793,7921,7920,7794,7795,7922,7794,7922,7921,7795,7796,7923,7795,7923,7922,7796,7797,7924,7796,7924,7923,7797,7798,7925,7797,7925,7924,7798,7799,7926,7798,7926,7925,7799,7800,7927,7799,7927,7926,7800,7801,7928,7800,7928,7927,7801,7802,7929,7801,7929,7928,7802,7803,7930,7802,7930,7929,7803,7804,7931,7803,7931,7930,7804,7805,7932,7804,7932,7931,7805,7806,7933,7805,7933,7932,7806,7807,7934,7806,7934,7933,7807,7808,7935,7807,7935,7934,7808,7809,7936,7808,7936,7935,7809,7810,7937,7809,7937,7936,7810,7811,7938,7810,7938,7937,7811,7812,7939,7811,7939,7938,7812,7813,7940,7812,7940,7939,7813,7814,7941,7813,7941,7940,7814,7815,7942,7814,7942,7941,7815,7816,7943,7815,7943,7942,7816,7817,7944,7816,7944,7943,7817,7818,7945,7817,7945,7944,7818,7819,7946,7818,7946,7945,7819,7820,7947,7819,7947,7946,7820,7821,7948,7820,7948,7947,7821,7822,7949,7821,7949,7948,7822,7823,7950,7822,7950,7949,7823,7824,7951,7823,7951,7950,7824,7825,7952,7824,7952,7951,7825,7826,7953,7825,7953,7952,7826,7827,7954,7826,7954,7953,7827,7828,7955,7827,7955,7954,7828,7829,7956,7828,7956,7955,7829,7830,7957,7829,7957,7956,7830,7831,7958,7830,7958,7957,7831,7832,7959,7831,7959,7958,7832,7833,7960,7832,7960,7959,7833,7834,7961,7833,7961,7960,7834,7835,7962,7834,7962,7961,7835,7836,7963,7835,7963,7962,7836,7837,7964,7836,7964,7963,7837,7838,7965,7837,7965,7964,7838,7839,7966,7838,7966,7965,7839,7840,7967,7839,7967,7966,7840,7841,7968,7840,7968,7967,7841,7842,7969,7841,7969,7968,7842,7843,7970,7842,7970,7969,7843,7844,7971,7843,7971,7970,7844,7845,7972,7844,7972,7971,7845,7846,7973,7845,7973,7972,7846,7847,7974,7846,7974,7973,7847,7848,7975,7847,7975,7974,7848,7849,7976,7848,7976,7975,7849,7850,7977,7849,7977,7976,7850,7851,7978,7850,7978,7977,7851,7852,7979,7851,7979,7978,7852,7853,7980,7852,7980,7979,7853,7854,7981,7853,7981,7980,7854,7855,7982,7854,7982,7981,7855,7856,7983,7855,7983,7982,7856,7857,7984,7856,7984,7983,7857,7858,7985,7857,7985,7984,7858,7859,7986,7858,7986,7985,7859,7860,7987,7859,7987,7986,7860,7861,7988,7860,7988,7987,7861,7862,7989,7861,7989,7988,7862,7863,7990,7862,7990,7989,7863,7864,7991,7863,7991,7990,7864,7865,7992,7864,7992,7991,7865,7866,7993,7865,7993,7992,7866,7867,7994,7866,7994,7993,7867,7868,7995,7867,7995,7994,7868,7869,7996,7868,7996,7995,7869,7870,7997,7869,7997,7996,7870,7871,7998,7870,7998,7997,7871,7872,7999,7871,7999,7998,7872,7873,8000,7872,8000,7999,7874,7875,8002,7875,7876,8003,7876,7877,8004,7877,7878,8005,7878,7879,8006,7879,7880,8007,7880,7881,8008,7881,7882,8009,7882,7883,8010,7883,7884,8011,7884,7885,8012,7885,7886,8013,7886,7887,8014,7887,7888,8015,7888,7889,8016,7889,7890,8017,7890,7891,8018,7891,7892,8019,7892,7893,8020,7893,7894,8021,7894,7895,8022,7895,7896,8023,7896,7897,8024,7897,7898,8025,7898,7899,8026,7899,7900,8027,7900,7901,8028,7901,7902,8029,7902,7903,8030,7903,7904,8031,7904,7905,8032,7905,7906,8033,7906,7907,8034,7907,7908,8035,7908,7909,8036,7909,7910,8037,7910,7911,8038,7911,7912,8039,7912,7913,8040,7913,7914,8041,7914,7915,8042,7915,7916,8043,7916,7917,8044,7917,7918,8045,7918,7919,8046,7919,7920,8047,7920,7921,8048,7921,7922,8049,7922,7923,8050,7923,7924,8051,7924,7925,8052,7925,7926,8053,7926,7927,8054,7927,7928,8055,7928,7929,8056,7929,7930,8057,7930,7931,8058,7931,7932,8059,7932,7933,8060,7933,7934,8061,7934,7935,8062,7935,7936,8063,7936,7937,8064,7937,7938,8065,7938,7939,8066,7939,7940,8067,7940,7941,8068,7941,7942,8069,7942,7943,8070,7943,7944,8071,7944,7945,8072,7945,7946,8073,7946,7947,8074,7947,7948,8075,7948,7949,8076,7949,7950,8077,7950,7951,8078,7951,7952,8079,7952,7953,8080,7953,7954,8081,7954,7955,8082,7955,7956,8083,7956,7957,8084,7957,7958,8085,7958,7959,8086,7959,7960,8087,7960,7961,8088,7961,7962,8089,7962,7963,8090,7963,7964,8091,7964,7965,8092,7965,7966,8093,7966,7967,8094,7967,7968,8095,7968,7969,8096,7969,7970,8097,7970,7971,8098,7971,7972,8099,7972,7973,8100,7973,7974,8101,7974,7975,8102,7975,7976,8103,7976,7977,8104,7977,7978,8105,7978,7979,8106,7979,7980,8107,7980,7981,8108,7981,7982,8109,7982,7983,8110,7983,7984,8111,7984,7985,8112,7985,7986,8113,7986,7987,8114,7987,7988,8115,7988,7989,8116,7989,7990,8117,7990,7991,8118,7991,7992,8119,7992,7993,8120,7993,7994,8121,7994,7995,8122,7995,7996,8123,7996,7997,8124,7997,7998,8125,7998,7999,8126,7999,8000,8127],"normals":[6.123234262925839e-17,-1.0,0.0,6.115622216056881e-17,-1.0,-3.0521802414446084e-18,6.092806589529199e-17,-1.0,-6.096772341539962e-18,6.054842308135465e-17,-1.0,-9.126206770500587e-18,6.001825324826736e-17,-1.0,-1.2132952048849189e-17,5.933886665012041e-17,-1.0,-1.510953253179982e-17,5.85119573528083e-17,-1.0,-1.804854826159258e-17,5.753957676425016e-17,-1.0,-2.0942694406238013e-17,5.642414686927956e-17,-1.0,-2.378477118568446e-17,5.5168430451142406e-17,-1.0,-2.656771613186194e-17,5.3775560869999064e-17,-1.0,-2.9284609051765835e-17,5.224899574081e-17,-1.0,-3.192869849723649e-17,5.059253347694806e-17,-1.0,-3.449340507368165e-17,4.881029012914131e-17,-1.0,-3.697235618166222e-17,4.6906692768028125e-17,-1.0,-3.9359390979975894e-17,4.4886482792879666e-17,-1.0,-4.1648567003102095e-17,4.2754672918208004e-17,-1.0,-4.383420317459381e-17,4.0516573643545727e-17,-1.0,-4.5910856646020435e-17,3.817774031388674e-17,-1.0,-4.7873372427804546e-17,3.574398966329852e-17,-1.0,-4.9716863536887175e-17,3.322137665386496e-17,-1.0,-5.143675401011968e-17,3.0616171314629196e-17,-1.0,-5.302876236065149e-17,2.7934845506703856e-17,-1.0,-5.4488934665154596e-17,2.5184071268909788e-17,-1.0,-5.581363463765622e-17,2.2370686076190357e-17,-1.0,-5.699958002548575e-17,1.9501682913444085e-17,-1.0,-5.804380621332779e-17,1.658419538627363e-17,-1.0,-5.894373239767118e-17,1.3625477868651082e-17,-1.0,-5.969711526469467e-17,1.0632883996222037e-17,-1.0,-6.030208207749146e-17,7.613855912957635e-18,-1.0,-6.075713067606913e-17,4.57589862855557e-18,-1.0,-6.106112285990482e-17,1.52656461220118e-18,-1.0,-6.121331085772477e-17,-1.52656461220118e-18,-1.0,-6.121331085772477e-17,-4.57589862855557e-18,-1.0,-6.106112285990482e-17,-7.613855912957635e-18,-1.0,-6.075713067606913e-17,-1.0632883996222037e-17,-1.0,-6.030208207749146e-17,-1.3625477868651082e-17,-1.0,-5.969711526469467e-17,-1.658419538627363e-17,-1.0,-5.894373239767118e-17,-1.9501682913444085e-17,-1.0,-5.804380621332779e-17,-2.2370686076190357e-17,-1.0,-5.699958002548575e-17,-2.5184071268909788e-17,-1.0,-5.581363463765622e-17,-2.7934845506703856e-17,-1.0,-5.4488934665154596e-17,-3.0616171314629196e-17,-1.0,-5.302876236065149e-17,-3.322137665386496e-17,-1.0,-5.143675401011968e-17,-3.574398966329852e-17,-1.0,-4.9716863536887175e-17,-3.817774031388674e-17,-1.0,-4.7873372427804546e-17,-4.0516573643545727e-17,-1.0,-4.5910856646020435e-17,-4.2754672918208004e-17,-1.0,-4.383420317459381e-17,-4.4886482792879666e-17,-1.0,-4.1648567003102095e-17,-4.6906692768028125e-17,-1.0,-3.9359390979975894e-17,-4.881029012914131e-17,-1.0,-3.697235618166222e-17,-5.059253347694806e-17,-1.0,-3.449340507368165e-17,-5.224899574081e-17,-1.0,-3.192869849723649e-17,-5.3775560869999064e-17,-1.0,-2.9284609051765835e-17,-5.5168430451142406e-17,-1.0,-2.656771613186194e-17,-5.642414686927956e-17,-1.0,-2.378477118568446e-17,-5.753957676425016e-17,-1.0,-2.0942694406238013e-17,-5.85119573528083e-17,-1.0,-1.804854826159258e-17,-5.933886665012041e-17,-1.0,-1.510953253179982e-17,-6.001825324826736e-17,-1.0,-1.2132952048849189e-17,-6.054842308135465e-17,-1.0,-9.126206770500587e-18,-6.092806589529199e-17,-1.0,-6.096772341539962e-18,-6.115622216056881e-17,-1.0,-3.0521802414446084e-18,-6.123234262925839e-17,-1.0,-7.498798786105971e-33,-6.115622216056881e-17,-1.0,3.0521802414446084e-18,-6.092806589529199e-17,-1.0,6.096772341539962e-18,-6.054842308135465e-17,-1.0,9.126206770500587e-18,-6.001825324826736e-17,-1.0,1.2132952048849189e-17,-5.933886665012041e-17,-1.0,1.510953253179982e-17,-5.85119573528083e-17,-1.0,1.804854826159258e-17,-5.753957676425016e-17,-1.0,2.0942694406238013e-17,-5.642414686927956e-17,-1.0,2.378477118568446e-17,-5.5168430451142406e-17,-1.0,2.656771613186194e-17,-5.3775560869999064e-17,-1.0,2.9284609051765835e-17,-5.224899574081e-17,-1.0,3.192869849723649e-17,-5.059253347694806e-17,-1.0,3.449340507368165e-17,-4.881029012914131e-17,-1.0,3.697235618166222e-17,-4.6906692768028125e-17,-1.0,3.9359390979975894e-17,-4.4886482792879666e-17,-1.0,4.1648567003102095e-17,-4.2754672918208004e-17,-1.0,4.383420317459381e-17,-4.0516573643545727e-17,-1.0,4.5910856646020435e-17,-3.817774031388674e-17,-1.0,4.7873372427804546e-17,-3.574398966329852e-17,-1.0,4.9716863536887175e-17,-3.322137665386496e-17,-1.0,5.143675401011968e-17,-3.0616171314629196e-17,-1.0,5.302876236065149e-17,-2.7934845506703856e-17,-1.0,5.4488934665154596e-17,-2.5184071268909788e-17,-1.0,5.581363463765622e-17,-2.2370686076190357e-17,-1.0,5.699958002548575e-17,-1.9501682913444085e-17,-1.0,5.804380621332779e-17,-1.658419538627363e-17,-1.0,5.894373239767118e-17,-1.3625477868651082e-17,-1.0,5.969711526469467e-17,-1.0632883996222037e-17,-1.0,6.030208207749146e-17,-7.613855912957635e-18,-1.0,6.075713067606913e-17,-4.57589862855557e-18,-1.0,6.106112285990482e-17,-1.52656461220118e-18,-1.0,6.121331085772477e-17,1.52656461220118e-18,-1.0,6.121331085772477e-17,4.57589862855557e-18,-1.0,6.106112285990482e-17,7.613855912957635e-18,-1.0,6.075713067606913e-17,1.0632883996222037e-17,-1.0,6.030208207749146e-17,1.3625477868651082e-17,-1.0,5.969711526469467e-17,1.658419538627363e-17,-1.0,5.894373239767118e-17,1.9501682913444085e-17,-1.0,5.804380621332779e-17,2.2370686076190357e-17,-1.0,5.699958002548575e-17,2.5184071268909788e-17,-1.0,5.581363463765622e-17,2.7934845506703856e-17,-1.0,5.4488934665154596e-17,3.0616171314629196e-17,-1.0,5.302876236065149e-17,3.322137665386496e-17,-1.0,5.143675401011968e-17,3.574398966329852e-17,-1.0,4.9716863536887175e-17,3.817774031388674e-17,-1.0,4.7873372427804546e-17,4.0516573643545727e-17,-1.0,4.5910856646020435e-17,4.2754672918208004e-17,-1.0,4.383420317459381e-17,4.4886482792879666e-17,-1.0,4.1648567003102095e-17,4.6906692768028125e-17,-1.0,3.9359390979975894e-17,4.881029012914131e-17,-1.0,3.697235618166222e-17,5.059253347694806e-17,-1.0,3.449340507368165e-17,5.224899574081e-17,-1.0,3.192869849723649e-17,5.3775560869999064e-17,-1.0,2.9284609051765835e-17,5.5168430451142406e-17,-1.0,2.656771613186194e-17,5.642414686927956e-17,-1.0,2.378477118568446e-17,5.753957676425016e-17,-1.0,2.0942694406238013e-17,5.85119573528083e-17,-1.0,1.804854826159258e-17,5.933886665012041e-17,-1.0,1.510953253179982e-17,6.001825324826736e-17,-1.0,1.2132952048849189e-17,6.054842308135465e-17,-1.0,9.126206770500587e-18,6.092806589529199e-17,-1.0,6.096772341539962e-18,6.115622216056881e-17,-1.0,3.0521802414446084e-18,6.123234262925839e-17,-1.0,1.4997597572211942e-32,0.04984588548541069,-0.9987569451332092,0.0,0.04978392273187637,-0.9987569451332092,-0.002484612399712205,0.04959819093346596,-0.9987569451332092,-0.004963047336786985,0.049289148300886154,-0.9987569451332092,-0.007429143879562616,0.048857565969228745,-0.9987569451332092,-0.009876769967377186,0.048304516822099686,-0.9987569451332092,-0.012299841269850731,0.04763137176632881,-0.9987569451332092,-0.014692332595586777,0.04683981090784073,-0.9987569451332092,-0.017048297449946404,0.045931797474622726,-0.9987569451332092,-0.01936187595129013,0.04490959271788597,-0.9987569451332092,-0.02162731997668743,0.043775733560323715,-0.9987569451332092,-0.023838993161916733,0.042533040046691895,-0.9987569451332092,-0.025991398841142654,0.04118460416793823,-0.9987569451332092,-0.028079187497496605,0.039733774960041046,-0.9987569451332092,-0.03009716607630253,0.038184162229299545,-0.9987569451332092,-0.0320403166115284,0.03653961792588234,-0.9987569451332092,-0.0339038111269474,0.03480423241853714,-0.9987569451332092,-0.03568301722407341,0.03298231586813927,-0.9987569451332092,-0.037373509258031845,0.03107840195298195,-0.9987569451332092,-0.038971081376075745,0.029097219929099083,-0.9987569451332092,-0.040471769869327545,0.027043698355555534,-0.9987569451332092,-0.04187183454632759,0.024922942742705345,-0.9987569451332092,-0.043167803436517715,0.02274022437632084,-0.9987569451332092,-0.04435644671320915,0.020500969141721725,-0.9987569451332092,-0.04543481767177582,0.018210746347904205,-0.9987569451332092,-0.04640022665262222,0.01587524823844433,-0.9987569451332092,-0.047250278294086456,0.013500283472239971,-0.9987569451332092,-0.04798285663127899,0.011091752909123898,-0.9987569451332092,-0.04859614372253418,0.008655646815896034,-0.9987569451332092,-0.04908861592411995,0.006198022048920393,-0.9987569451332092,-0.04945904389023781,0.003724987618625164,-0.9987569451332092,-0.049706507474184036,0.0012426924658939242,-0.9987569451332092,-0.04983039200305939,-0.0012426924658939242,-0.9987569451332092,-0.04983039200305939,-0.003724987618625164,-0.9987569451332092,-0.049706507474184036,-0.006198022048920393,-0.9987569451332092,-0.04945904389023781,-0.008655646815896034,-0.9987569451332092,-0.04908861592411995,-0.011091752909123898,-0.9987569451332092,-0.04859614372253418,-0.013500283472239971,-0.9987569451332092,-0.04798285663127899,-0.01587524823844433,-0.9987569451332092,-0.047250278294086456,-0.018210746347904205,-0.9987569451332092,-0.04640022665262222,-0.020500969141721725,-0.9987569451332092,-0.04543481767177582,-0.02274022437632084,-0.9987569451332092,-0.04435644671320915,-0.024922942742705345,-0.9987569451332092,-0.043167803436517715,-0.027043698355555534,-0.9987569451332092,-0.04187183454632759,-0.029097219929099083,-0.9987569451332092,-0.040471769869327545,-0.03107840195298195,-0.9987569451332092,-0.038971081376075745,-0.03298231586813927,-0.9987569451332092,-0.037373509258031845,-0.03480423241853714,-0.9987569451332092,-0.03568301722407341,-0.03653961792588234,-0.9987569451332092,-0.0339038111269474,-0.038184162229299545,-0.9987569451332092,-0.0320403166115284,-0.039733774960041046,-0.9987569451332092,-0.03009716607630253,-0.04118460416793823,-0.9987569451332092,-0.028079187497496605,-0.042533040046691895,-0.9987569451332092,-0.025991398841142654,-0.043775733560323715,-0.9987569451332092,-0.023838993161916733,-0.04490959271788597,-0.9987569451332092,-0.02162731997668743,-0.045931797474622726,-0.9987569451332092,-0.01936187595129013,-0.04683981090784073,-0.9987569451332092,-0.017048297449946404,-0.04763137176632881,-0.9987569451332092,-0.014692332595586777,-0.048304516822099686,-0.9987569451332092,-0.012299841269850731,-0.048857565969228745,-0.9987569451332092,-0.009876769967377186,-0.049289148300886154,-0.9987569451332092,-0.007429143879562616,-0.04959819093346596,-0.9987569451332092,-0.004963047336786985,-0.04978392273187637,-0.9987569451332092,-0.002484612399712205,-0.04984588548541069,-0.9987569451332092,-6.104360482889217e-18,-0.04978392273187637,-0.9987569451332092,0.002484612399712205,-0.04959819093346596,-0.9987569451332092,0.004963047336786985,-0.049289148300886154,-0.9987569451332092,0.007429143879562616,-0.048857565969228745,-0.9987569451332092,0.009876769967377186,-0.048304516822099686,-0.9987569451332092,0.012299841269850731,-0.04763137176632881,-0.9987569451332092,0.014692332595586777,-0.04683981090784073,-0.9987569451332092,0.017048297449946404,-0.045931797474622726,-0.9987569451332092,0.01936187595129013,-0.04490959271788597,-0.9987569451332092,0.02162731997668743,-0.043775733560323715,-0.9987569451332092,0.023838993161916733,-0.042533040046691895,-0.9987569451332092,0.025991398841142654,-0.04118460416793823,-0.9987569451332092,0.028079187497496605,-0.039733774960041046,-0.9987569451332092,0.03009716607630253,-0.038184162229299545,-0.9987569451332092,0.0320403166115284,-0.03653961792588234,-0.9987569451332092,0.0339038111269474,-0.03480423241853714,-0.9987569451332092,0.03568301722407341,-0.03298231586813927,-0.9987569451332092,0.037373509258031845,-0.03107840195298195,-0.9987569451332092,0.038971081376075745,-0.029097219929099083,-0.9987569451332092,0.040471769869327545,-0.027043698355555534,-0.9987569451332092,0.04187183454632759,-0.024922942742705345,-0.9987569451332092,0.043167803436517715,-0.02274022437632084,-0.9987569451332092,0.04435644671320915,-0.020500969141721725,-0.9987569451332092,0.04543481767177582,-0.018210746347904205,-0.9987569451332092,0.04640022665262222,-0.01587524823844433,-0.9987569451332092,0.047250278294086456,-0.013500283472239971,-0.9987569451332092,0.04798285663127899,-0.011091752909123898,-0.9987569451332092,0.04859614372253418,-0.008655646815896034,-0.9987569451332092,0.04908861592411995,-0.006198022048920393,-0.9987569451332092,0.04945904389023781,-0.003724987618625164,-0.9987569451332092,0.049706507474184036,-0.0012426924658939242,-0.9987569451332092,0.04983039200305939,0.0012426924658939242,-0.9987569451332092,0.04983039200305939,0.003724987618625164,-0.9987569451332092,0.049706507474184036,0.006198022048920393,-0.9987569451332092,0.04945904389023781,0.008655646815896034,-0.9987569451332092,0.04908861592411995,0.011091752909123898,-0.9987569451332092,0.04859614372253418,0.013500283472239971,-0.9987569451332092,0.04798285663127899,0.01587524823844433,-0.9987569451332092,0.047250278294086456,0.018210746347904205,-0.9987569451332092,0.04640022665262222,0.020500969141721725,-0.9987569451332092,0.04543481767177582,0.02274022437632084,-0.9987569451332092,0.04435644671320915,0.024922942742705345,-0.9987569451332092,0.043167803436517715,0.027043698355555534,-0.9987569451332092,0.04187183454632759,0.029097219929099083,-0.9987569451332092,0.040471769869327545,0.03107840195298195,-0.9987569451332092,0.038971081376075745,0.03298231586813927,-0.9987569451332092,0.037373509258031845,0.03480423241853714,-0.9987569451332092,0.03568301722407341,0.03653961792588234,-0.9987569451332092,0.0339038111269474,0.038184162229299545,-0.9987569451332092,0.0320403166115284,0.039733774960041046,-0.9987569451332092,0.03009716607630253,0.04118460416793823,-0.9987569451332092,0.028079187497496605,0.042533040046691895,-0.9987569451332092,0.025991398841142654,0.043775733560323715,-0.9987569451332092,0.023838993161916733,0.04490959271788597,-0.9987569451332092,0.02162731997668743,0.045931797474622726,-0.9987569451332092,0.01936187595129013,0.04683981090784073,-0.9987569451332092,0.017048297449946404,0.04763137176632881,-0.9987569451332092,0.014692332595586777,0.048304516822099686,-0.9987569451332092,0.012299841269850731,0.048857565969228745,-0.9987569451332092,0.009876769967377186,0.049289148300886154,-0.9987569451332092,0.007429143879562616,0.04959819093346596,-0.9987569451332092,0.004963047336786985,0.04978392273187637,-0.9987569451332092,0.002484612399712205,0.04984588548541069,-0.9987569451332092,1.2208720965778434e-17,0.09956784546375275,-0.9950307607650757,0.0,0.09944407641887665,-0.9950307607650757,-0.004963047336786985,0.09907307475805283,-0.9950307607650757,-0.009913756512105465,0.0984557569026947,-0.9950307607650757,-0.014839817769825459,0.09759366512298584,-0.9950307607650757,-0.01972898468375206,0.09648893773555756,-0.9950307607650757,-0.024569103494286537,0.09514432400465012,-0.9950307607650757,-0.029348138719797134,0.09356316924095154,-0.9950307607650757,-0.03405420854687691,0.0917493999004364,-0.9950307607650757,-0.038675617426633835,0.08970753103494644,-0.9950307607650757,-0.04320086911320686,0.08744262903928757,-0.9950307607650757,-0.047618716955184937,0.08496033400297165,-0.9950307607650757,-0.05191817879676819,0.08226681500673294,-0.9950307607650757,-0.05608856678009033,0.07936877012252808,-0.9950307607650757,-0.06011950597167015,0.07627339661121368,-0.9950307607650757,-0.06400097906589508,0.07298839837312698,-0.9950307607650757,-0.0677233338356018,0.0695219337940216,-0.9950307607650757,-0.07127732038497925,0.06588263064622879,-0.9950307607650757,-0.07465410232543945,0.0620795376598835,-0.9950307607650757,-0.07784527540206909,0.05812210217118263,-0.9950307607650757,-0.08084291964769363,0.05402016267180443,-0.9950307607650757,-0.08363956958055496,0.04978392273187637,-0.9950307607650757,-0.08622828125953674,0.04542391374707222,-0.9950307607650757,-0.08860261738300323,0.040950972586870193,-0.9950307607650757,-0.09075667709112167,0.0363762192428112,-0.9950307607650757,-0.09268509596586227,0.03171103075146675,-0.9950307607650757,-0.09438308328390121,0.0269670020788908,-0.9950307607650757,-0.09584642201662064,0.02215592935681343,-0.9950307607650757,-0.09707147628068924,0.017289774492383003,-0.9950307607650757,-0.09805518388748169,0.012380635365843773,-0.9950307607650757,-0.09879512339830399,0.007440714631229639,-0.9950307607650757,-0.0992894321680069,0.0024822952691465616,-0.9950307607650757,-0.09953689575195312,-0.0024822952691465616,-0.9950307607650757,-0.09953689575195312,-0.007440714631229639,-0.9950307607650757,-0.0992894321680069,-0.012380635365843773,-0.9950307607650757,-0.09879512339830399,-0.017289774492383003,-0.9950307607650757,-0.09805518388748169,-0.02215592935681343,-0.9950307607650757,-0.09707147628068924,-0.0269670020788908,-0.9950307607650757,-0.09584642201662064,-0.03171103075146675,-0.9950307607650757,-0.09438308328390121,-0.0363762192428112,-0.9950307607650757,-0.09268509596586227,-0.040950972586870193,-0.9950307607650757,-0.09075667709112167,-0.04542391374707222,-0.9950307607650757,-0.08860261738300323,-0.04978392273187637,-0.9950307607650757,-0.08622828125953674,-0.05402016267180443,-0.9950307607650757,-0.08363956958055496,-0.05812210217118263,-0.9950307607650757,-0.08084291964769363,-0.0620795376598835,-0.9950307607650757,-0.07784527540206909,-0.06588263064622879,-0.9950307607650757,-0.07465410232543945,-0.0695219337940216,-0.9950307607650757,-0.07127732038497925,-0.07298839837312698,-0.9950307607650757,-0.0677233338356018,-0.07627339661121368,-0.9950307607650757,-0.06400097906589508,-0.07936877012252808,-0.9950307607650757,-0.06011950597167015,-0.08226681500673294,-0.9950307607650757,-0.05608856678009033,-0.08496033400297165,-0.9950307607650757,-0.05191817879676819,-0.08744262903928757,-0.9950307607650757,-0.047618716955184937,-0.08970753103494644,-0.9950307607650757,-0.04320086911320686,-0.0917493999004364,-0.9950307607650757,-0.038675617426633835,-0.09356316924095154,-0.9950307607650757,-0.03405420854687691,-0.09514432400465012,-0.9950307607650757,-0.029348138719797134,-0.09648893773555756,-0.9950307607650757,-0.024569103494286537,-0.09759366512298584,-0.9950307607650757,-0.01972898468375206,-0.0984557569026947,-0.9950307607650757,-0.014839817769825459,-0.09907307475805283,-0.9950307607650757,-0.009913756512105465,-0.09944407641887665,-0.9950307607650757,-0.004963047336786985,-0.09956784546375275,-0.9950307607650757,-1.2193544683079923e-17,-0.09944407641887665,-0.9950307607650757,0.004963047336786985,-0.09907307475805283,-0.9950307607650757,0.009913756512105465,-0.0984557569026947,-0.9950307607650757,0.014839817769825459,-0.09759366512298584,-0.9950307607650757,0.01972898468375206,-0.09648893773555756,-0.9950307607650757,0.024569103494286537,-0.09514432400465012,-0.9950307607650757,0.029348138719797134,-0.09356316924095154,-0.9950307607650757,0.03405420854687691,-0.0917493999004364,-0.9950307607650757,0.038675617426633835,-0.08970753103494644,-0.9950307607650757,0.04320086911320686,-0.08744262903928757,-0.9950307607650757,0.047618716955184937,-0.08496033400297165,-0.9950307607650757,0.05191817879676819,-0.08226681500673294,-0.9950307607650757,0.05608856678009033,-0.07936877012252808,-0.9950307607650757,0.06011950597167015,-0.07627339661121368,-0.9950307607650757,0.06400097906589508,-0.07298839837312698,-0.9950307607650757,0.0677233338356018,-0.0695219337940216,-0.9950307607650757,0.07127732038497925,-0.06588263064622879,-0.9950307607650757,0.07465410232543945,-0.0620795376598835,-0.9950307607650757,0.07784527540206909,-0.05812210217118263,-0.9950307607650757,0.08084291964769363,-0.05402016267180443,-0.9950307607650757,0.08363956958055496,-0.04978392273187637,-0.9950307607650757,0.08622828125953674,-0.04542391374707222,-0.9950307607650757,0.08860261738300323,-0.040950972586870193,-0.9950307607650757,0.09075667709112167,-0.0363762192428112,-0.9950307607650757,0.09268509596586227,-0.03171103075146675,-0.9950307607650757,0.09438308328390121,-0.0269670020788908,-0.9950307607650757,0.09584642201662064,-0.02215592935681343,-0.9950307607650757,0.09707147628068924,-0.017289774492383003,-0.9950307607650757,0.09805518388748169,-0.012380635365843773,-0.9950307607650757,0.09879512339830399,-0.007440714631229639,-0.9950307607650757,0.0992894321680069,-0.0024822952691465616,-0.9950307607650757,0.09953689575195312,0.0024822952691465616,-0.9950307607650757,0.09953689575195312,0.007440714631229639,-0.9950307607650757,0.0992894321680069,0.012380635365843773,-0.9950307607650757,0.09879512339830399,0.017289774492383003,-0.9950307607650757,0.09805518388748169,0.02215592935681343,-0.9950307607650757,0.09707147628068924,0.0269670020788908,-0.9950307607650757,0.09584642201662064,0.03171103075146675,-0.9950307607650757,0.09438308328390121,0.0363762192428112,-0.9950307607650757,0.09268509596586227,0.040950972586870193,-0.9950307607650757,0.09075667709112167,0.04542391374707222,-0.9950307607650757,0.08860261738300323,0.04978392273187637,-0.9950307607650757,0.08622828125953674,0.05402016267180443,-0.9950307607650757,0.08363956958055496,0.05812210217118263,-0.9950307607650757,0.08084291964769363,0.0620795376598835,-0.9950307607650757,0.07784527540206909,0.06588263064622879,-0.9950307607650757,0.07465410232543945,0.0695219337940216,-0.9950307607650757,0.07127732038497925,0.07298839837312698,-0.9950307607650757,0.0677233338356018,0.07627339661121368,-0.9950307607650757,0.06400097906589508,0.07936877012252808,-0.9950307607650757,0.06011950597167015,0.08226681500673294,-0.9950307607650757,0.05608856678009033,0.08496033400297165,-0.9950307607650757,0.05191817879676819,0.08744262903928757,-0.9950307607650757,0.047618716955184937,0.08970753103494644,-0.9950307607650757,0.04320086911320686,0.0917493999004364,-0.9950307607650757,0.038675617426633835,0.09356316924095154,-0.9950307607650757,0.03405420854687691,0.09514432400465012,-0.9950307607650757,0.029348138719797134,0.09648893773555756,-0.9950307607650757,0.024569103494286537,0.09759366512298584,-0.9950307607650757,0.01972898468375206,0.0984557569026947,-0.9950307607650757,0.014839817769825459,0.09907307475805283,-0.9950307607650757,0.009913756512105465,0.09944407641887665,-0.9950307607650757,0.004963047336786985,0.09956784546375275,-0.9950307607650757,2.4387089366159847e-17,0.1490422636270523,-0.9888308048248291,0.0,0.1488569974899292,-0.9888308048248291,-0.007429143879562616,0.1483016461133957,-0.9888308048248291,-0.014839817769825459,0.14737758040428162,-0.9888308048248291,-0.02221359685063362,0.14608712494373322,-0.9888308048248291,-0.02953214943408966,0.14443346858024597,-0.9888308048248291,-0.036777280271053314,0.14242073893547058,-0.9888308048248291,-0.04393097758293152,0.1400539129972458,-0.9888308048248291,-0.050975456833839417,0.13733890652656555,-0.9888308048248291,-0.05789320170879364,0.1342824399471283,-0.9888308048248291,-0.06466701626777649,0.13089212775230408,-0.9888308048248291,-0.07128005474805832,0.1271764039993286,-0.9888308048248291,-0.07771588116884232,0.12314449995756149,-0.9888308048248291,-0.08395849913358688,0.11880643665790558,-0.9888308048248291,-0.08999237418174744,0.11417300254106522,-0.9888308048248291,-0.09580252319574356,0.10925570875406265,-0.9888308048248291,-0.10137448459863663,0.10406679660081863,-0.9888308048248291,-0.10669441521167755,0.09861915558576584,-0.9888308048248291,-0.1117490902543068,0.09292633086442947,-0.9888308048248291,-0.11652593314647675,0.0870024785399437,-0.9888308048248291,-0.1210130825638771,0.08086232095956802,-0.9888308048248291,-0.1251993626356125,0.07452113181352615,-0.9888308048248291,-0.12907439470291138,0.06799466907978058,-0.9888308048248291,-0.13262851536273956,0.0612991601228714,-0.9888308048248291,-0.1358529031276703,0.05445125326514244,-0.9888308048248291,-0.13873952627182007,0.047467973083257675,-0.9888308048248291,-0.14128124713897705,0.040366675704717636,-0.9888308048248291,-0.14347170293331146,0.03316502273082733,-0.9888308048248291,-0.14530546963214874,0.02588091790676117,-0.9888308048248291,-0.14677797257900238,0.018532468006014824,-0.9888308048248291,-0.14788557589054108,0.011137942783534527,-0.9888308048248291,-0.14862550795078278,0.0037157267797738314,-0.9888308048248291,-0.14899593591690063,-0.0037157267797738314,-0.9888308048248291,-0.14899593591690063,-0.011137942783534527,-0.9888308048248291,-0.14862550795078278,-0.018532468006014824,-0.9888308048248291,-0.14788557589054108,-0.02588091790676117,-0.9888308048248291,-0.14677797257900238,-0.03316502273082733,-0.9888308048248291,-0.14530546963214874,-0.040366675704717636,-0.9888308048248291,-0.14347170293331146,-0.047467973083257675,-0.9888308048248291,-0.14128124713897705,-0.05445125326514244,-0.9888308048248291,-0.13873952627182007,-0.0612991601228714,-0.9888308048248291,-0.1358529031276703,-0.06799466907978058,-0.9888308048248291,-0.13262851536273956,-0.07452113181352615,-0.9888308048248291,-0.12907439470291138,-0.08086232095956802,-0.9888308048248291,-0.1251993626356125,-0.0870024785399437,-0.9888308048248291,-0.1210130825638771,-0.09292633086442947,-0.9888308048248291,-0.11652593314647675,-0.09861915558576584,-0.9888308048248291,-0.1117490902543068,-0.10406679660081863,-0.9888308048248291,-0.10669441521167755,-0.10925570875406265,-0.9888308048248291,-0.10137448459863663,-0.11417300254106522,-0.9888308048248291,-0.09580252319574356,-0.11880643665790558,-0.9888308048248291,-0.08999237418174744,-0.12314449995756149,-0.9888308048248291,-0.08395849913358688,-0.1271764039993286,-0.9888308048248291,-0.07771588116884232,-0.13089212775230408,-0.9888308048248291,-0.07128005474805832,-0.1342824399471283,-0.9888308048248291,-0.06466701626777649,-0.13733890652656555,-0.9888308048248291,-0.05789320170879364,-0.1400539129972458,-0.9888308048248291,-0.050975456833839417,-0.14242073893547058,-0.9888308048248291,-0.04393097758293152,-0.14443346858024597,-0.9888308048248291,-0.036777280271053314,-0.14608712494373322,-0.9888308048248291,-0.02953214943408966,-0.14737758040428162,-0.9888308048248291,-0.02221359685063362,-0.1483016461133957,-0.9888308048248291,-0.014839817769825459,-0.1488569974899292,-0.9888308048248291,-0.007429143879562616,-0.1490422636270523,-0.9888308048248291,-1.8252413541001174e-17,-0.1488569974899292,-0.9888308048248291,0.007429143879562616,-0.1483016461133957,-0.9888308048248291,0.014839817769825459,-0.14737758040428162,-0.9888308048248291,0.02221359685063362,-0.14608712494373322,-0.9888308048248291,0.02953214943408966,-0.14443346858024597,-0.9888308048248291,0.036777280271053314,-0.14242073893547058,-0.9888308048248291,0.04393097758293152,-0.1400539129972458,-0.9888308048248291,0.050975456833839417,-0.13733890652656555,-0.9888308048248291,0.05789320170879364,-0.1342824399471283,-0.9888308048248291,0.06466701626777649,-0.13089212775230408,-0.9888308048248291,0.07128005474805832,-0.1271764039993286,-0.9888308048248291,0.07771588116884232,-0.12314449995756149,-0.9888308048248291,0.08395849913358688,-0.11880643665790558,-0.9888308048248291,0.08999237418174744,-0.11417300254106522,-0.9888308048248291,0.09580252319574356,-0.10925570875406265,-0.9888308048248291,0.10137448459863663,-0.10406679660081863,-0.9888308048248291,0.10669441521167755,-0.09861915558576584,-0.9888308048248291,0.1117490902543068,-0.09292633086442947,-0.9888308048248291,0.11652593314647675,-0.0870024785399437,-0.9888308048248291,0.1210130825638771,-0.08086232095956802,-0.9888308048248291,0.1251993626356125,-0.07452113181352615,-0.9888308048248291,0.12907439470291138,-0.06799466907978058,-0.9888308048248291,0.13262851536273956,-0.0612991601228714,-0.9888308048248291,0.1358529031276703,-0.05445125326514244,-0.9888308048248291,0.13873952627182007,-0.047467973083257675,-0.9888308048248291,0.14128124713897705,-0.040366675704717636,-0.9888308048248291,0.14347170293331146,-0.03316502273082733,-0.9888308048248291,0.14530546963214874,-0.02588091790676117,-0.9888308048248291,0.14677797257900238,-0.018532468006014824,-0.9888308048248291,0.14788557589054108,-0.011137942783534527,-0.9888308048248291,0.14862550795078278,-0.0037157267797738314,-0.9888308048248291,0.14899593591690063,0.0037157267797738314,-0.9888308048248291,0.14899593591690063,0.011137942783534527,-0.9888308048248291,0.14862550795078278,0.018532468006014824,-0.9888308048248291,0.14788557589054108,0.02588091790676117,-0.9888308048248291,0.14677797257900238,0.03316502273082733,-0.9888308048248291,0.14530546963214874,0.040366675704717636,-0.9888308048248291,0.14347170293331146,0.047467973083257675,-0.9888308048248291,0.14128124713897705,0.05445125326514244,-0.9888308048248291,0.13873952627182007,0.0612991601228714,-0.9888308048248291,0.1358529031276703,0.06799466907978058,-0.9888308048248291,0.13262851536273956,0.07452113181352615,-0.9888308048248291,0.12907439470291138,0.08086232095956802,-0.9888308048248291,0.1251993626356125,0.0870024785399437,-0.9888308048248291,0.1210130825638771,0.09292633086442947,-0.9888308048248291,0.11652593314647675,0.09861915558576584,-0.9888308048248291,0.1117490902543068,0.10406679660081863,-0.9888308048248291,0.10669441521167755,0.10925570875406265,-0.9888308048248291,0.10137448459863663,0.11417300254106522,-0.9888308048248291,0.09580252319574356,0.11880643665790558,-0.9888308048248291,0.08999237418174744,0.12314449995756149,-0.9888308048248291,0.08395849913358688,0.1271764039993286,-0.9888308048248291,0.07771588116884232,0.13089212775230408,-0.9888308048248291,0.07128005474805832,0.1342824399471283,-0.9888308048248291,0.06466701626777649,0.13733890652656555,-0.9888308048248291,0.05789320170879364,0.1400539129972458,-0.9888308048248291,0.050975456833839417,0.14242073893547058,-0.9888308048248291,0.04393097758293152,0.14443346858024597,-0.9888308048248291,0.036777280271053314,0.14608712494373322,-0.9888308048248291,0.02953214943408966,0.14737758040428162,-0.9888308048248291,0.02221359685063362,0.1483016461133957,-0.9888308048248291,0.014839817769825459,0.1488569974899292,-0.9888308048248291,0.007429143879562616,0.1490422636270523,-0.9888308048248291,3.650482708200235e-17,0.19814614951610565,-0.9801725149154663,0.0,0.19789983332157135,-0.9801725149154663,-0.009876769967377186,0.1971615105867386,-0.9801725149154663,-0.01972898468375206,0.1959330141544342,-0.9801725149154663,-0.02953214943408966,0.19421739876270294,-0.9801725149154663,-0.039261892437934875,0.19201892614364624,-0.9801725149154663,-0.04889402538537979,0.18934306502342224,-0.9801725149154663,-0.058404602110385895,0.1861964613199234,-0.9801725149154663,-0.06776997447013855,0.18258695304393768,-0.9801725149154663,-0.07696685940027237,0.17852351069450378,-0.9801725149154663,-0.0859723910689354,0.17401622235774994,-0.9801725149154663,-0.09476418048143387,0.16907629370689392,-0.9801725149154663,-0.10332037508487701,0.16371603310108185,-0.9801725149154663,-0.11161969602108002,0.15794873237609863,-0.9801725149154663,-0.11964151263237,0.15178875625133514,-0.9801725149154663,-0.12736588716506958,0.14525140821933746,-0.9801725149154663,-0.1347736120223999,0.13835293054580688,-0.9801725149154663,-0.14184625446796417,0.13111048936843872,-0.9801725149154663,-0.14856626093387604,0.12354210019111633,-0.9801725149154663,-0.1549168974161148,0.11566656082868576,-0.9801725149154663,-0.1608823835849762,0.10750345140695572,-0.9801725149154663,-0.16644789278507233,0.09907307475805283,-0.9801725149154663,-0.1715995967388153,0.09039638191461563,-0.9801725149154663,-0.17632466554641724,0.08149494975805283,-0.9801725149154663,-0.18061137199401855,0.07239091396331787,-0.9801725149154663,-0.18444904685020447,0.06310690194368362,-0.9801725149154663,-0.1878281533718109,0.053665995597839355,-0.9801725149154663,-0.19074028730392456,0.044091664254665375,-0.9801725149154663,-0.1931782066822052,0.03440771624445915,-0.9801725149154663,-0.19513586163520813,0.024638226255774498,-0.9801725149154663,-0.19660836458206177,0.014807479456067085,-0.9801725149154663,-0.19759207963943481,0.0049399202689528465,-0.9801725149154663,-0.19808456301689148,-0.0049399202689528465,-0.9801725149154663,-0.19808456301689148,-0.014807479456067085,-0.9801725149154663,-0.19759207963943481,-0.024638226255774498,-0.9801725149154663,-0.19660836458206177,-0.03440771624445915,-0.9801725149154663,-0.19513586163520813,-0.044091664254665375,-0.9801725149154663,-0.1931782066822052,-0.053665995597839355,-0.9801725149154663,-0.19074028730392456,-0.06310690194368362,-0.9801725149154663,-0.1878281533718109,-0.07239091396331787,-0.9801725149154663,-0.18444904685020447,-0.08149494975805283,-0.9801725149154663,-0.18061137199401855,-0.09039638191461563,-0.9801725149154663,-0.17632466554641724,-0.09907307475805283,-0.9801725149154663,-0.1715995967388153,-0.10750345140695572,-0.9801725149154663,-0.16644789278507233,-0.11566656082868576,-0.9801725149154663,-0.1608823835849762,-0.12354210019111633,-0.9801725149154663,-0.1549168974161148,-0.13111048936843872,-0.9801725149154663,-0.14856626093387604,-0.13835293054580688,-0.9801725149154663,-0.14184625446796417,-0.14525140821933746,-0.9801725149154663,-0.1347736120223999,-0.15178875625133514,-0.9801725149154663,-0.12736588716506958,-0.15794873237609863,-0.9801725149154663,-0.11964151263237,-0.16371603310108185,-0.9801725149154663,-0.11161969602108002,-0.16907629370689392,-0.9801725149154663,-0.10332037508487701,-0.17401622235774994,-0.9801725149154663,-0.09476418048143387,-0.17852351069450378,-0.9801725149154663,-0.0859723910689354,-0.18258695304393768,-0.9801725149154663,-0.07696685940027237,-0.1861964613199234,-0.9801725149154663,-0.06776997447013855,-0.18934306502342224,-0.9801725149154663,-0.058404602110385895,-0.19201892614364624,-0.9801725149154663,-0.04889402538537979,-0.19421739876270294,-0.9801725149154663,-0.039261892437934875,-0.1959330141544342,-0.9801725149154663,-0.02953214943408966,-0.1971615105867386,-0.9801725149154663,-0.01972898468375206,-0.19789983332157135,-0.9801725149154663,-0.009876769967377186,-0.19814614951610565,-0.9801725149154663,-2.4265904097698378e-17,-0.19789983332157135,-0.9801725149154663,0.009876769967377186,-0.1971615105867386,-0.9801725149154663,0.01972898468375206,-0.1959330141544342,-0.9801725149154663,0.02953214943408966,-0.19421739876270294,-0.9801725149154663,0.039261892437934875,-0.19201892614364624,-0.9801725149154663,0.04889402538537979,-0.18934306502342224,-0.9801725149154663,0.058404602110385895,-0.1861964613199234,-0.9801725149154663,0.06776997447013855,-0.18258695304393768,-0.9801725149154663,0.07696685940027237,-0.17852351069450378,-0.9801725149154663,0.0859723910689354,-0.17401622235774994,-0.9801725149154663,0.09476418048143387,-0.16907629370689392,-0.9801725149154663,0.10332037508487701,-0.16371603310108185,-0.9801725149154663,0.11161969602108002,-0.15794873237609863,-0.9801725149154663,0.11964151263237,-0.15178875625133514,-0.9801725149154663,0.12736588716506958,-0.14525140821933746,-0.9801725149154663,0.1347736120223999,-0.13835293054580688,-0.9801725149154663,0.14184625446796417,-0.13111048936843872,-0.9801725149154663,0.14856626093387604,-0.12354210019111633,-0.9801725149154663,0.1549168974161148,-0.11566656082868576,-0.9801725149154663,0.1608823835849762,-0.10750345140695572,-0.9801725149154663,0.16644789278507233,-0.09907307475805283,-0.9801725149154663,0.1715995967388153,-0.09039638191461563,-0.9801725149154663,0.17632466554641724,-0.08149494975805283,-0.9801725149154663,0.18061137199401855,-0.07239091396331787,-0.9801725149154663,0.18444904685020447,-0.06310690194368362,-0.9801725149154663,0.1878281533718109,-0.053665995597839355,-0.9801725149154663,0.19074028730392456,-0.044091664254665375,-0.9801725149154663,0.1931782066822052,-0.03440771624445915,-0.9801725149154663,0.19513586163520813,-0.024638226255774498,-0.9801725149154663,0.19660836458206177,-0.014807479456067085,-0.9801725149154663,0.19759207963943481,-0.0049399202689528465,-0.9801725149154663,0.19808456301689148,0.0049399202689528465,-0.9801725149154663,0.19808456301689148,0.014807479456067085,-0.9801725149154663,0.19759207963943481,0.024638226255774498,-0.9801725149154663,0.19660836458206177,0.03440771624445915,-0.9801725149154663,0.19513586163520813,0.044091664254665375,-0.9801725149154663,0.1931782066822052,0.053665995597839355,-0.9801725149154663,0.19074028730392456,0.06310690194368362,-0.9801725149154663,0.1878281533718109,0.07239091396331787,-0.9801725149154663,0.18444904685020447,0.08149494975805283,-0.9801725149154663,0.18061137199401855,0.09039638191461563,-0.9801725149154663,0.17632466554641724,0.09907307475805283,-0.9801725149154663,0.1715995967388153,0.10750345140695572,-0.9801725149154663,0.16644789278507233,0.11566656082868576,-0.9801725149154663,0.1608823835849762,0.12354210019111633,-0.9801725149154663,0.1549168974161148,0.13111048936843872,-0.9801725149154663,0.14856626093387604,0.13835293054580688,-0.9801725149154663,0.14184625446796417,0.14525140821933746,-0.9801725149154663,0.1347736120223999,0.15178875625133514,-0.9801725149154663,0.12736588716506958,0.15794873237609863,-0.9801725149154663,0.11964151263237,0.16371603310108185,-0.9801725149154663,0.11161969602108002,0.16907629370689392,-0.9801725149154663,0.10332037508487701,0.17401622235774994,-0.9801725149154663,0.09476418048143387,0.17852351069450378,-0.9801725149154663,0.0859723910689354,0.18258695304393768,-0.9801725149154663,0.07696685940027237,0.1861964613199234,-0.9801725149154663,0.06776997447013855,0.18934306502342224,-0.9801725149154663,0.058404602110385895,0.19201892614364624,-0.9801725149154663,0.04889402538537979,0.19421739876270294,-0.9801725149154663,0.039261892437934875,0.1959330141544342,-0.9801725149154663,0.02953214943408966,0.1971615105867386,-0.9801725149154663,0.01972898468375206,0.19789983332157135,-0.9801725149154663,0.009876769967377186,0.19814614951610565,-0.9801725149154663,4.8531808195396756e-17,0.2467574030160904,-0.9690772891044617,0.0,0.24645066261291504,-0.9690772891044617,-0.012299841269850731,0.24553120136260986,-0.9690772891044617,-0.024569103494286537,0.24400131404399872,-0.9690772891044617,-0.036777280271053314,0.24186481535434723,-0.9690772891044617,-0.04889402538537979,0.2391269952058792,-0.9690772891044617,-0.060889214277267456,0.23579466342926025,-0.9690772891044617,-0.07273302227258682,0.23187610507011414,-0.9690772891044617,-0.08439599722623825,0.22738106548786163,-0.9690772891044617,-0.09584915637969971,0.22232073545455933,-0.9690772891044617,-0.10706402361392975,0.21670767664909363,-0.9690772891044617,-0.11801270395517349,0.21055583655834198,-0.9690772891044617,-0.12866799533367157,0.20388053357601166,-0.9690772891044617,-0.13900339603424072,0.19669833779335022,-0.9690772891044617,-0.14899320900440216,0.18902713060379028,-0.9690772891044617,-0.15861259400844574,0.18088597059249878,-0.9690772891044617,-0.16783764958381653,0.17229509353637695,-0.9690772891044617,-0.1766454428434372,0.16327588260173798,-0.9690772891044617,-0.1850140541791916,0.153850719332695,-0.9690772891044617,-0.1929226964712143,0.14404307305812836,-0.9690772891044617,-0.20035171508789062,0.13387730717658997,-0.9690772891044617,-0.20728261768817902,0.1233787015080452,-0.9690772891044617,-0.21369817852973938,0.11257335543632507,-0.9690772891044617,-0.2195824533700943,0.1014881357550621,-0.9690772891044617,-0.22492080926895142,0.09015060216188431,-0.9690772891044617,-0.2296999841928482,0.07858894020318985,-0.9690772891044617,-0.23390808701515198,0.06683188676834106,-0.9690772891044617,-0.23753465712070465,0.05490868538618088,-0.9690772891044617,-0.24057067930698395,0.042848970741033554,-0.9690772891044617,-0.2430085986852646,0.030682729557156563,-0.9690772891044617,-0.24484236538410187,0.018440203741192818,-0.9690772891044617,-0.24606741964817047,0.006151832640171051,-0.9690772891044617,-0.24668070673942566,-0.006151832640171051,-0.9690772891044617,-0.24668070673942566,-0.018440203741192818,-0.9690772891044617,-0.24606741964817047,-0.030682729557156563,-0.9690772891044617,-0.24484236538410187,-0.042848970741033554,-0.9690772891044617,-0.2430085986852646,-0.05490868538618088,-0.9690772891044617,-0.24057067930698395,-0.06683188676834106,-0.9690772891044617,-0.23753465712070465,-0.07858894020318985,-0.9690772891044617,-0.23390808701515198,-0.09015060216188431,-0.9690772891044617,-0.2296999841928482,-0.1014881357550621,-0.9690772891044617,-0.22492080926895142,-0.11257335543632507,-0.9690772891044617,-0.2195824533700943,-0.1233787015080452,-0.9690772891044617,-0.21369817852973938,-0.13387730717658997,-0.9690772891044617,-0.20728261768817902,-0.14404307305812836,-0.9690772891044617,-0.20035171508789062,-0.153850719332695,-0.9690772891044617,-0.1929226964712143,-0.16327588260173798,-0.9690772891044617,-0.1850140541791916,-0.17229509353637695,-0.9690772891044617,-0.1766454428434372,-0.18088597059249878,-0.9690772891044617,-0.16783764958381653,-0.18902713060379028,-0.9690772891044617,-0.15861259400844574,-0.19669833779335022,-0.9690772891044617,-0.14899320900440216,-0.20388053357601166,-0.9690772891044617,-0.13900339603424072,-0.21055583655834198,-0.9690772891044617,-0.12866799533367157,-0.21670767664909363,-0.9690772891044617,-0.11801270395517349,-0.22232073545455933,-0.9690772891044617,-0.10706402361392975,-0.22738106548786163,-0.9690772891044617,-0.09584915637969971,-0.23187610507011414,-0.9690772891044617,-0.08439599722623825,-0.23579466342926025,-0.9690772891044617,-0.07273302227258682,-0.2391269952058792,-0.9690772891044617,-0.060889214277267456,-0.24186481535434723,-0.9690772891044617,-0.04889402538537979,-0.24400131404399872,-0.9690772891044617,-0.036777280271053314,-0.24553120136260986,-0.9690772891044617,-0.024569103494286537,-0.24645066261291504,-0.9690772891044617,-0.012299841269850731,-0.2467574030160904,-0.9690772891044617,-3.021906506359964e-17,-0.24645066261291504,-0.9690772891044617,0.012299841269850731,-0.24553120136260986,-0.9690772891044617,0.024569103494286537,-0.24400131404399872,-0.9690772891044617,0.036777280271053314,-0.24186481535434723,-0.9690772891044617,0.04889402538537979,-0.2391269952058792,-0.9690772891044617,0.060889214277267456,-0.23579466342926025,-0.9690772891044617,0.07273302227258682,-0.23187610507011414,-0.9690772891044617,0.08439599722623825,-0.22738106548786163,-0.9690772891044617,0.09584915637969971,-0.22232073545455933,-0.9690772891044617,0.10706402361392975,-0.21670767664909363,-0.9690772891044617,0.11801270395517349,-0.21055583655834198,-0.9690772891044617,0.12866799533367157,-0.20388053357601166,-0.9690772891044617,0.13900339603424072,-0.19669833779335022,-0.9690772891044617,0.14899320900440216,-0.18902713060379028,-0.9690772891044617,0.15861259400844574,-0.18088597059249878,-0.9690772891044617,0.16783764958381653,-0.17229509353637695,-0.9690772891044617,0.1766454428434372,-0.16327588260173798,-0.9690772891044617,0.1850140541791916,-0.153850719332695,-0.9690772891044617,0.1929226964712143,-0.14404307305812836,-0.9690772891044617,0.20035171508789062,-0.13387730717658997,-0.9690772891044617,0.20728261768817902,-0.1233787015080452,-0.9690772891044617,0.21369817852973938,-0.11257335543632507,-0.9690772891044617,0.2195824533700943,-0.1014881357550621,-0.9690772891044617,0.22492080926895142,-0.09015060216188431,-0.9690772891044617,0.2296999841928482,-0.07858894020318985,-0.9690772891044617,0.23390808701515198,-0.06683188676834106,-0.9690772891044617,0.23753465712070465,-0.05490868538618088,-0.9690772891044617,0.24057067930698395,-0.042848970741033554,-0.9690772891044617,0.2430085986852646,-0.030682729557156563,-0.9690772891044617,0.24484236538410187,-0.018440203741192818,-0.9690772891044617,0.24606741964817047,-0.006151832640171051,-0.9690772891044617,0.24668070673942566,0.006151832640171051,-0.9690772891044617,0.24668070673942566,0.018440203741192818,-0.9690772891044617,0.24606741964817047,0.030682729557156563,-0.9690772891044617,0.24484236538410187,0.042848970741033554,-0.9690772891044617,0.2430085986852646,0.05490868538618088,-0.9690772891044617,0.24057067930698395,0.06683188676834106,-0.9690772891044617,0.23753465712070465,0.07858894020318985,-0.9690772891044617,0.23390808701515198,0.09015060216188431,-0.9690772891044617,0.2296999841928482,0.1014881357550621,-0.9690772891044617,0.22492080926895142,0.11257335543632507,-0.9690772891044617,0.2195824533700943,0.1233787015080452,-0.9690772891044617,0.21369817852973938,0.13387730717658997,-0.9690772891044617,0.20728261768817902,0.14404307305812836,-0.9690772891044617,0.20035171508789062,0.153850719332695,-0.9690772891044617,0.1929226964712143,0.16327588260173798,-0.9690772891044617,0.1850140541791916,0.17229509353637695,-0.9690772891044617,0.1766454428434372,0.18088597059249878,-0.9690772891044617,0.16783764958381653,0.18902713060379028,-0.9690772891044617,0.15861259400844574,0.19669833779335022,-0.9690772891044617,0.14899320900440216,0.20388053357601166,-0.9690772891044617,0.13900339603424072,0.21055583655834198,-0.9690772891044617,0.12866799533367157,0.21670767664909363,-0.9690772891044617,0.11801270395517349,0.22232073545455933,-0.9690772891044617,0.10706402361392975,0.22738106548786163,-0.9690772891044617,0.09584915637969971,0.23187610507011414,-0.9690772891044617,0.08439599722623825,0.23579466342926025,-0.9690772891044617,0.07273302227258682,0.2391269952058792,-0.9690772891044617,0.060889214277267456,0.24186481535434723,-0.9690772891044617,0.04889402538537979,0.24400131404399872,-0.9690772891044617,0.036777280271053314,0.24553120136260986,-0.9690772891044617,0.024569103494286537,0.24645066261291504,-0.9690772891044617,0.012299841269850731,0.2467574030160904,-0.9690772891044617,6.043813012719928e-17,0.29475516080856323,-0.955572783946991,0.0,0.2943887710571289,-0.955572783946991,-0.014692332595586777,0.29329046607017517,-0.955572783946991,-0.029348138719797134,0.2914630174636841,-0.955572783946991,-0.04393097758293152,0.2889109253883362,-0.955572783946991,-0.058404602110385895,0.28564053773880005,-0.955572783946991,-0.07273302227258682,0.2816600203514099,-0.955572783946991,-0.08688060939311981,0.2769792675971985,-0.955572783946991,-0.10081220418214798,0.2716098725795746,-0.955572783946991,-0.11449316889047623,0.26556524634361267,-0.955572783946991,-0.127889484167099,0.25886034965515137,-0.955572783946991,-0.14096783101558685,0.25151190161705017,-0.955572783946991,-0.1536957174539566,0.24353815615177155,-0.955572783946991,-0.1660415083169937,0.2349589318037033,-0.955572783946991,-0.17797447741031647,0.22579556703567505,-0.955572783946991,-0.1894649714231491,0.21607083082199097,-0.955572783946991,-0.20048443973064423,0.20580890774726868,-0.955572783946991,-0.21100546419620514,0.19503532350063324,-0.955572783946991,-0.22100189328193665,0.1837768405675888,-0.955572783946991,-0.2304488718509674,0.1720614731311798,-0.955572783946991,-0.23932293057441711,0.15991832315921783,-0.955572783946991,-0.24760198593139648,0.14737758040428162,-0.955572783946991,-0.255265474319458,0.13447044789791107,-0.955572783946991,-0.26229432225227356,0.12122900038957596,-0.955572783946991,-0.26867106556892395,0.10768615454435349,-0.955572783946991,-0.2743798494338989,0.09387558698654175,-0.955572783946991,-0.27940648794174194,0.07983162999153137,-0.955572783946991,-0.28373846411705017,0.06558919697999954,-0.955572783946991,-0.28736504912376404,0.05118370056152344,-0.955572783946991,-0.2902771830558777,0.03665095195174217,-0.955572783946991,-0.2924676537513733,0.02202708087861538,-0.955572783946991,-0.2939309775829315,0.007348450366407633,-0.955572783946991,-0.29466354846954346,-0.007348450366407633,-0.955572783946991,-0.29466354846954346,-0.02202708087861538,-0.955572783946991,-0.2939309775829315,-0.03665095195174217,-0.955572783946991,-0.2924676537513733,-0.05118370056152344,-0.955572783946991,-0.2902771830558777,-0.06558919697999954,-0.955572783946991,-0.28736504912376404,-0.07983162999153137,-0.955572783946991,-0.28373846411705017,-0.09387558698654175,-0.955572783946991,-0.27940648794174194,-0.10768615454435349,-0.955572783946991,-0.2743798494338989,-0.12122900038957596,-0.955572783946991,-0.26867106556892395,-0.13447044789791107,-0.955572783946991,-0.26229432225227356,-0.14737758040428162,-0.955572783946991,-0.255265474319458,-0.15991832315921783,-0.955572783946991,-0.24760198593139648,-0.1720614731311798,-0.955572783946991,-0.23932293057441711,-0.1837768405675888,-0.955572783946991,-0.2304488718509674,-0.19503532350063324,-0.955572783946991,-0.22100189328193665,-0.20580890774726868,-0.955572783946991,-0.21100546419620514,-0.21607083082199097,-0.955572783946991,-0.20048443973064423,-0.22579556703567505,-0.955572783946991,-0.1894649714231491,-0.2349589318037033,-0.955572783946991,-0.17797447741031647,-0.24353815615177155,-0.955572783946991,-0.1660415083169937,-0.25151190161705017,-0.955572783946991,-0.1536957174539566,-0.25886034965515137,-0.955572783946991,-0.14096783101558685,-0.26556524634361267,-0.955572783946991,-0.127889484167099,-0.2716098725795746,-0.955572783946991,-0.11449316889047623,-0.2769792675971985,-0.955572783946991,-0.10081220418214798,-0.2816600203514099,-0.955572783946991,-0.08688060939311981,-0.28564053773880005,-0.955572783946991,-0.07273302227258682,-0.2889109253883362,-0.955572783946991,-0.058404602110385895,-0.2914630174636841,-0.955572783946991,-0.04393097758293152,-0.29329046607017517,-0.955572783946991,-0.029348138719797134,-0.2943887710571289,-0.955572783946991,-0.014692332595586777,-0.29475516080856323,-0.955572783946991,-3.609709652318516e-17,-0.2943887710571289,-0.955572783946991,0.014692332595586777,-0.29329046607017517,-0.955572783946991,0.029348138719797134,-0.2914630174636841,-0.955572783946991,0.04393097758293152,-0.2889109253883362,-0.955572783946991,0.058404602110385895,-0.28564053773880005,-0.955572783946991,0.07273302227258682,-0.2816600203514099,-0.955572783946991,0.08688060939311981,-0.2769792675971985,-0.955572783946991,0.10081220418214798,-0.2716098725795746,-0.955572783946991,0.11449316889047623,-0.26556524634361267,-0.955572783946991,0.127889484167099,-0.25886034965515137,-0.955572783946991,0.14096783101558685,-0.25151190161705017,-0.955572783946991,0.1536957174539566,-0.24353815615177155,-0.955572783946991,0.1660415083169937,-0.2349589318037033,-0.955572783946991,0.17797447741031647,-0.22579556703567505,-0.955572783946991,0.1894649714231491,-0.21607083082199097,-0.955572783946991,0.20048443973064423,-0.20580890774726868,-0.955572783946991,0.21100546419620514,-0.19503532350063324,-0.955572783946991,0.22100189328193665,-0.1837768405675888,-0.955572783946991,0.2304488718509674,-0.1720614731311798,-0.955572783946991,0.23932293057441711,-0.15991832315921783,-0.955572783946991,0.24760198593139648,-0.14737758040428162,-0.955572783946991,0.255265474319458,-0.13447044789791107,-0.955572783946991,0.26229432225227356,-0.12122900038957596,-0.955572783946991,0.26867106556892395,-0.10768615454435349,-0.955572783946991,0.2743798494338989,-0.09387558698654175,-0.955572783946991,0.27940648794174194,-0.07983162999153137,-0.955572783946991,0.28373846411705017,-0.06558919697999954,-0.955572783946991,0.28736504912376404,-0.05118370056152344,-0.955572783946991,0.2902771830558777,-0.03665095195174217,-0.955572783946991,0.2924676537513733,-0.02202708087861538,-0.955572783946991,0.2939309775829315,-0.007348450366407633,-0.955572783946991,0.29466354846954346,0.007348450366407633,-0.955572783946991,0.29466354846954346,0.02202708087861538,-0.955572783946991,0.2939309775829315,0.03665095195174217,-0.955572783946991,0.2924676537513733,0.05118370056152344,-0.955572783946991,0.2902771830558777,0.06558919697999954,-0.955572783946991,0.28736504912376404,0.07983162999153137,-0.955572783946991,0.28373846411705017,0.09387558698654175,-0.955572783946991,0.27940648794174194,0.10768615454435349,-0.955572783946991,0.2743798494338989,0.12122900038957596,-0.955572783946991,0.26867106556892395,0.13447044789791107,-0.955572783946991,0.26229432225227356,0.14737758040428162,-0.955572783946991,0.255265474319458,0.15991832315921783,-0.955572783946991,0.24760198593139648,0.1720614731311798,-0.955572783946991,0.23932293057441711,0.1837768405675888,-0.955572783946991,0.2304488718509674,0.19503532350063324,-0.955572783946991,0.22100189328193665,0.20580890774726868,-0.955572783946991,0.21100546419620514,0.21607083082199097,-0.955572783946991,0.20048443973064423,0.22579556703567505,-0.955572783946991,0.1894649714231491,0.2349589318037033,-0.955572783946991,0.17797447741031647,0.24353815615177155,-0.955572783946991,0.1660415083169937,0.25151190161705017,-0.955572783946991,0.1536957174539566,0.25886034965515137,-0.955572783946991,0.14096783101558685,0.26556524634361267,-0.955572783946991,0.127889484167099,0.2716098725795746,-0.955572783946991,0.11449316889047623,0.2769792675971985,-0.955572783946991,0.10081220418214798,0.2816600203514099,-0.955572783946991,0.08688060939311981,0.28564053773880005,-0.955572783946991,0.07273302227258682,0.2889109253883362,-0.955572783946991,0.058404602110385895,0.2914630174636841,-0.955572783946991,0.04393097758293152,0.29329046607017517,-0.955572783946991,0.029348138719797134,0.2943887710571289,-0.955572783946991,0.014692332595586777,0.29475516080856323,-0.955572783946991,7.219419304637032e-17,0.3420201539993286,-0.9396926164627075,0.0,0.34159499406814575,-0.9396926164627075,-0.017048297449946404,0.34032055735588074,-0.9396926164627075,-0.03405420854687691,0.33820006251335144,-0.9396926164627075,-0.050975456833839417,0.3352387249469757,-0.9396926164627075,-0.06776997447013855,0.3314439654350281,-0.9396926164627075,-0.08439599722623825,0.3268251419067383,-0.9396926164627075,-0.10081220418214798,0.32139381766319275,-0.9396926164627075,-0.11697778105735779,0.31516343355178833,-0.9396926164627075,-0.13285252451896667,0.3081494867801666,-0.9396926164627075,-0.14839698374271393,0.3003694713115692,-0.9396926164627075,-0.1635724902153015,0.29184266924858093,-0.9396926164627075,-0.178341343998909,0.2825902998447418,-0.9396926164627075,-0.19266681373119354,0.2726353704929352,-0.9396926164627075,-0.20651327073574066,0.2620026171207428,-0.9396926164627075,-0.21984630823135376,0.25071850419044495,-0.9396926164627075,-0.23263277113437653,0.23881106078624725,-0.9396926164627075,-0.24484087526798248,0.2263098806142807,-0.9396926164627075,-0.25644028186798096,0.21324607729911804,-0.9396926164627075,-0.26740211248397827,0.19965209066867828,-0.9396926164627075,-0.27769914269447327,0.18556174635887146,-0.9396926164627075,-0.2873057723045349,0.1710100769996643,-0.9396926164627075,-0.29619812965393066,0.1560332328081131,-0.9396926164627075,-0.30435407161712646,0.14066846668720245,-0.9396926164627075,-0.31175336241722107,0.124953992664814,-0.9396926164627075,-0.3183775842189789,0.1089288517832756,-0.9396926164627075,-0.3242102563381195,0.09263289719820023,-0.9396926164627075,-0.3292368948459625,0.07610664516687393,-0.9396926164627075,-0.3334449827671051,0.05939117446541786,-0.9396926164627075,-0.33682408928871155,0.042528051882982254,-0.9396926164627075,-0.33936581015586853,0.0255591981112957,-0.9396926164627075,-0.34106379747390747,0.008526798337697983,-0.9396926164627075,-0.3419138491153717,-0.008526798337697983,-0.9396926164627075,-0.3419138491153717,-0.0255591981112957,-0.9396926164627075,-0.34106379747390747,-0.042528051882982254,-0.9396926164627075,-0.33936581015586853,-0.05939117446541786,-0.9396926164627075,-0.33682408928871155,-0.07610664516687393,-0.9396926164627075,-0.3334449827671051,-0.09263289719820023,-0.9396926164627075,-0.3292368948459625,-0.1089288517832756,-0.9396926164627075,-0.3242102563381195,-0.124953992664814,-0.9396926164627075,-0.3183775842189789,-0.14066846668720245,-0.9396926164627075,-0.31175336241722107,-0.1560332328081131,-0.9396926164627075,-0.30435407161712646,-0.1710100769996643,-0.9396926164627075,-0.29619812965393066,-0.18556174635887146,-0.9396926164627075,-0.2873057723045349,-0.19965209066867828,-0.9396926164627075,-0.27769914269447327,-0.21324607729911804,-0.9396926164627075,-0.26740211248397827,-0.2263098806142807,-0.9396926164627075,-0.25644028186798096,-0.23881106078624725,-0.9396926164627075,-0.24484087526798248,-0.25071850419044495,-0.9396926164627075,-0.23263277113437653,-0.2620026171207428,-0.9396926164627075,-0.21984630823135376,-0.2726353704929352,-0.9396926164627075,-0.20651327073574066,-0.2825902998447418,-0.9396926164627075,-0.19266681373119354,-0.29184266924858093,-0.9396926164627075,-0.178341343998909,-0.3003694713115692,-0.9396926164627075,-0.1635724902153015,-0.3081494867801666,-0.9396926164627075,-0.14839698374271393,-0.31516343355178833,-0.9396926164627075,-0.13285252451896667,-0.32139381766319275,-0.9396926164627075,-0.11697778105735779,-0.3268251419067383,-0.9396926164627075,-0.10081220418214798,-0.3314439654350281,-0.9396926164627075,-0.08439599722623825,-0.3352387249469757,-0.9396926164627075,-0.06776997447013855,-0.33820006251335144,-0.9396926164627075,-0.050975456833839417,-0.34032055735588074,-0.9396926164627075,-0.03405420854687691,-0.34159499406814575,-0.9396926164627075,-0.017048297449946404,-0.3420201539993286,-0.9396926164627075,-4.1885388812476027e-17,-0.34159499406814575,-0.9396926164627075,0.017048297449946404,-0.34032055735588074,-0.9396926164627075,0.03405420854687691,-0.33820006251335144,-0.9396926164627075,0.050975456833839417,-0.3352387249469757,-0.9396926164627075,0.06776997447013855,-0.3314439654350281,-0.9396926164627075,0.08439599722623825,-0.3268251419067383,-0.9396926164627075,0.10081220418214798,-0.32139381766319275,-0.9396926164627075,0.11697778105735779,-0.31516343355178833,-0.9396926164627075,0.13285252451896667,-0.3081494867801666,-0.9396926164627075,0.14839698374271393,-0.3003694713115692,-0.9396926164627075,0.1635724902153015,-0.29184266924858093,-0.9396926164627075,0.178341343998909,-0.2825902998447418,-0.9396926164627075,0.19266681373119354,-0.2726353704929352,-0.9396926164627075,0.20651327073574066,-0.2620026171207428,-0.9396926164627075,0.21984630823135376,-0.25071850419044495,-0.9396926164627075,0.23263277113437653,-0.23881106078624725,-0.9396926164627075,0.24484087526798248,-0.2263098806142807,-0.9396926164627075,0.25644028186798096,-0.21324607729911804,-0.9396926164627075,0.26740211248397827,-0.19965209066867828,-0.9396926164627075,0.27769914269447327,-0.18556174635887146,-0.9396926164627075,0.2873057723045349,-0.1710100769996643,-0.9396926164627075,0.29619812965393066,-0.1560332328081131,-0.9396926164627075,0.30435407161712646,-0.14066846668720245,-0.9396926164627075,0.31175336241722107,-0.124953992664814,-0.9396926164627075,0.3183775842189789,-0.1089288517832756,-0.9396926164627075,0.3242102563381195,-0.09263289719820023,-0.9396926164627075,0.3292368948459625,-0.07610664516687393,-0.9396926164627075,0.3334449827671051,-0.05939117446541786,-0.9396926164627075,0.33682408928871155,-0.042528051882982254,-0.9396926164627075,0.33936581015586853,-0.0255591981112957,-0.9396926164627075,0.34106379747390747,-0.008526798337697983,-0.9396926164627075,0.3419138491153717,0.008526798337697983,-0.9396926164627075,0.3419138491153717,0.0255591981112957,-0.9396926164627075,0.34106379747390747,0.042528051882982254,-0.9396926164627075,0.33936581015586853,0.05939117446541786,-0.9396926164627075,0.33682408928871155,0.07610664516687393,-0.9396926164627075,0.3334449827671051,0.09263289719820023,-0.9396926164627075,0.3292368948459625,0.1089288517832756,-0.9396926164627075,0.3242102563381195,0.124953992664814,-0.9396926164627075,0.3183775842189789,0.14066846668720245,-0.9396926164627075,0.31175336241722107,0.1560332328081131,-0.9396926164627075,0.30435407161712646,0.1710100769996643,-0.9396926164627075,0.29619812965393066,0.18556174635887146,-0.9396926164627075,0.2873057723045349,0.19965209066867828,-0.9396926164627075,0.27769914269447327,0.21324607729911804,-0.9396926164627075,0.26740211248397827,0.2263098806142807,-0.9396926164627075,0.25644028186798096,0.23881106078624725,-0.9396926164627075,0.24484087526798248,0.25071850419044495,-0.9396926164627075,0.23263277113437653,0.2620026171207428,-0.9396926164627075,0.21984630823135376,0.2726353704929352,-0.9396926164627075,0.20651327073574066,0.2825902998447418,-0.9396926164627075,0.19266681373119354,0.29184266924858093,-0.9396926164627075,0.178341343998909,0.3003694713115692,-0.9396926164627075,0.1635724902153015,0.3081494867801666,-0.9396926164627075,0.14839698374271393,0.31516343355178833,-0.9396926164627075,0.13285252451896667,0.32139381766319275,-0.9396926164627075,0.11697778105735779,0.3268251419067383,-0.9396926164627075,0.10081220418214798,0.3314439654350281,-0.9396926164627075,0.08439599722623825,0.3352387249469757,-0.9396926164627075,0.06776997447013855,0.33820006251335144,-0.9396926164627075,0.050975456833839417,0.34032055735588074,-0.9396926164627075,0.03405420854687691,0.34159499406814575,-0.9396926164627075,0.017048297449946404,0.3420201539993286,-0.9396926164627075,8.377077762495205e-17,0.3884347975254059,-0.9214761853218079,0.0,0.38795194029808044,-0.9214761853218079,-0.01936187595129013,0.386504590511322,-0.9214761853218079,-0.038675617426633835,0.38409629464149475,-0.9214761853218079,-0.05789320170879364,0.38073310256004333,-0.9214761853218079,-0.07696685940027237,0.37642332911491394,-0.9214761853218079,-0.09584915637969971,0.3711777329444885,-0.9214761853218079,-0.11449316889047623,0.3650093078613281,-0.9214761853218079,-0.13285252451896667,0.3579334318637848,-0.9214761853218079,-0.1508815884590149,0.3499676585197449,-0.9214761853218079,-0.16853554546833038,0.3411318063735962,-0.9214761853218079,-0.18577048182487488,0.33144786953926086,-0.9214761853218079,-0.20254357159137726,0.32093989849090576,-0.9214761853218079,-0.21881310641765594,0.3096340000629425,-0.9214761853218079,-0.23453864455223083,0.2975583076477051,-0.9214761853218079,-0.24968107044696808,0.2847428619861603,-0.9214761853218079,-0.2642027735710144,0.2712194621562958,-0.9214761853218079,-0.27806758880615234,0.25702181458473206,-0.9214761853218079,-0.2912411093711853,0.24218513071537018,-0.9214761853218079,-0.3036905527114868,0.2267463505268097,-0.9214761853218079,-0.31538498401641846,0.21074384450912476,-0.9214761853218079,-0.32629528641700745,0.19421739876270294,-0.9214761853218079,-0.33639439940452576,0.1772080957889557,-0.9214761853218079,-0.3456571698188782,0.15975822508335114,-0.9214761853218079,-0.3540605902671814,0.14191116392612457,-0.9214761853218079,-0.36158376932144165,0.12371129542589188,-0.9214761853218079,-0.3682079613208771,0.10520385950803757,-0.9214761853218079,-0.37391674518585205,0.08643487095832825,-0.9214761853218079,-0.37869593501091003,0.06745099276304245,-0.9214761853218079,-0.38253360986709595,0.04829942062497139,-0.9214761853218079,-0.3854202330112457,0.029027769342064857,-0.9214761853218079,-0.38734865188598633,0.009683948010206223,-0.9214761853218079,-0.38831406831741333,-0.009683948010206223,-0.9214761853218079,-0.38831406831741333,-0.029027769342064857,-0.9214761853218079,-0.38734865188598633,-0.04829942062497139,-0.9214761853218079,-0.3854202330112457,-0.06745099276304245,-0.9214761853218079,-0.38253360986709595,-0.08643487095832825,-0.9214761853218079,-0.37869593501091003,-0.10520385950803757,-0.9214761853218079,-0.37391674518585205,-0.12371129542589188,-0.9214761853218079,-0.3682079613208771,-0.14191116392612457,-0.9214761853218079,-0.36158376932144165,-0.15975822508335114,-0.9214761853218079,-0.3540605902671814,-0.1772080957889557,-0.9214761853218079,-0.3456571698188782,-0.19421739876270294,-0.9214761853218079,-0.33639439940452576,-0.21074384450912476,-0.9214761853218079,-0.32629528641700745,-0.2267463505268097,-0.9214761853218079,-0.31538498401641846,-0.24218513071537018,-0.9214761853218079,-0.3036905527114868,-0.25702181458473206,-0.9214761853218079,-0.2912411093711853,-0.2712194621562958,-0.9214761853218079,-0.27806758880615234,-0.2847428619861603,-0.9214761853218079,-0.2642027735710144,-0.2975583076477051,-0.9214761853218079,-0.24968107044696808,-0.3096340000629425,-0.9214761853218079,-0.23453864455223083,-0.32093989849090576,-0.9214761853218079,-0.21881310641765594,-0.33144786953926086,-0.9214761853218079,-0.20254357159137726,-0.3411318063735962,-0.9214761853218079,-0.18577048182487488,-0.3499676585197449,-0.9214761853218079,-0.16853554546833038,-0.3579334318637848,-0.9214761853218079,-0.1508815884590149,-0.3650093078613281,-0.9214761853218079,-0.13285252451896667,-0.3711777329444885,-0.9214761853218079,-0.11449316889047623,-0.37642332911491394,-0.9214761853218079,-0.09584915637969971,-0.38073310256004333,-0.9214761853218079,-0.07696685940027237,-0.38409629464149475,-0.9214761853218079,-0.05789320170879364,-0.386504590511322,-0.9214761853218079,-0.038675617426633835,-0.38795194029808044,-0.9214761853218079,-0.01936187595129013,-0.3884347975254059,-0.9214761853218079,-4.756954237136892e-17,-0.38795194029808044,-0.9214761853218079,0.01936187595129013,-0.386504590511322,-0.9214761853218079,0.038675617426633835,-0.38409629464149475,-0.9214761853218079,0.05789320170879364,-0.38073310256004333,-0.9214761853218079,0.07696685940027237,-0.37642332911491394,-0.9214761853218079,0.09584915637969971,-0.3711777329444885,-0.9214761853218079,0.11449316889047623,-0.3650093078613281,-0.9214761853218079,0.13285252451896667,-0.3579334318637848,-0.9214761853218079,0.1508815884590149,-0.3499676585197449,-0.9214761853218079,0.16853554546833038,-0.3411318063735962,-0.9214761853218079,0.18577048182487488,-0.33144786953926086,-0.9214761853218079,0.20254357159137726,-0.32093989849090576,-0.9214761853218079,0.21881310641765594,-0.3096340000629425,-0.9214761853218079,0.23453864455223083,-0.2975583076477051,-0.9214761853218079,0.24968107044696808,-0.2847428619861603,-0.9214761853218079,0.2642027735710144,-0.2712194621562958,-0.9214761853218079,0.27806758880615234,-0.25702181458473206,-0.9214761853218079,0.2912411093711853,-0.24218513071537018,-0.9214761853218079,0.3036905527114868,-0.2267463505268097,-0.9214761853218079,0.31538498401641846,-0.21074384450912476,-0.9214761853218079,0.32629528641700745,-0.19421739876270294,-0.9214761853218079,0.33639439940452576,-0.1772080957889557,-0.9214761853218079,0.3456571698188782,-0.15975822508335114,-0.9214761853218079,0.3540605902671814,-0.14191116392612457,-0.9214761853218079,0.36158376932144165,-0.12371129542589188,-0.9214761853218079,0.3682079613208771,-0.10520385950803757,-0.9214761853218079,0.37391674518585205,-0.08643487095832825,-0.9214761853218079,0.37869593501091003,-0.06745099276304245,-0.9214761853218079,0.38253360986709595,-0.04829942062497139,-0.9214761853218079,0.3854202330112457,-0.029027769342064857,-0.9214761853218079,0.38734865188598633,-0.009683948010206223,-0.9214761853218079,0.38831406831741333,0.009683948010206223,-0.9214761853218079,0.38831406831741333,0.029027769342064857,-0.9214761853218079,0.38734865188598633,0.04829942062497139,-0.9214761853218079,0.3854202330112457,0.06745099276304245,-0.9214761853218079,0.38253360986709595,0.08643487095832825,-0.9214761853218079,0.37869593501091003,0.10520385950803757,-0.9214761853218079,0.37391674518585205,0.12371129542589188,-0.9214761853218079,0.3682079613208771,0.14191116392612457,-0.9214761853218079,0.36158376932144165,0.15975822508335114,-0.9214761853218079,0.3540605902671814,0.1772080957889557,-0.9214761853218079,0.3456571698188782,0.19421739876270294,-0.9214761853218079,0.33639439940452576,0.21074384450912476,-0.9214761853218079,0.32629528641700745,0.2267463505268097,-0.9214761853218079,0.31538498401641846,0.24218513071537018,-0.9214761853218079,0.3036905527114868,0.25702181458473206,-0.9214761853218079,0.2912411093711853,0.2712194621562958,-0.9214761853218079,0.27806758880615234,0.2847428619861603,-0.9214761853218079,0.2642027735710144,0.2975583076477051,-0.9214761853218079,0.24968107044696808,0.3096340000629425,-0.9214761853218079,0.23453864455223083,0.32093989849090576,-0.9214761853218079,0.21881310641765594,0.33144786953926086,-0.9214761853218079,0.20254357159137726,0.3411318063735962,-0.9214761853218079,0.18577048182487488,0.3499676585197449,-0.9214761853218079,0.16853554546833038,0.3579334318637848,-0.9214761853218079,0.1508815884590149,0.3650093078613281,-0.9214761853218079,0.13285252451896667,0.3711777329444885,-0.9214761853218079,0.11449316889047623,0.37642332911491394,-0.9214761853218079,0.09584915637969971,0.38073310256004333,-0.9214761853218079,0.07696685940027237,0.38409629464149475,-0.9214761853218079,0.05789320170879364,0.386504590511322,-0.9214761853218079,0.038675617426633835,0.38795194029808044,-0.9214761853218079,0.01936187595129013,0.3884347975254059,-0.9214761853218079,9.513908474273784e-17,0.4338837265968323,-0.9009688496589661,0.0,0.43334439396858215,-0.9009688496589661,-0.02162731997668743,0.43172767758369446,-0.9009688496589661,-0.04320086911320686,0.4290376305580139,-0.9009688496589661,-0.06466701626777649,0.425280898809433,-0.9009688496589661,-0.0859723910689354,0.4204668700695038,-0.9009688496589661,-0.10706402361392975,0.4146074950695038,-0.9009688496589661,-0.127889484167099,0.40771734714508057,-0.9009688496589661,-0.14839698374271393,0.39981353282928467,-0.9009688496589661,-0.16853554546833038,0.39091575145721436,-0.9009688496589661,-0.18825510144233704,0.3810460567474365,-0.9009688496589661,-0.20750662684440613,0.370229035615921,-0.9009688496589661,-0.22624225914478302,0.35849156975746155,-0.9009688496589661,-0.24441541731357574,0.34586283564567566,-0.9009688496589661,-0.26198092103004456,0.3323742151260376,-0.9009688496589661,-0.2788950800895691,0.3180592954158783,-0.9009688496589661,-0.2951158881187439,0.3029536008834839,-0.9009688496589661,-0.31060299277305603,0.28709471225738525,-0.9009688496589661,-0.3253178596496582,0.2705220878124237,-0.9009688496589661,-0.3392239809036255,0.25327688455581665,-0.9009688496589661,-0.352286696434021,0.23540200293064117,-0.9009688496589661,-0.3644735813140869,0.21694186329841614,-0.9009688496589661,-0.37575432658195496,0.19794239103794098,-0.9009688496589661,-0.3861009180545807,0.1784507930278778,-0.9009688496589661,-0.39548757672309875,0.15851552784442902,-0.9009688496589661,-0.403890997171402,0.13818617165088654,-0.9009688496589661,-0.4112902581691742,0.1175132766366005,-0.9009688496589661,-0.4176670014858246,0.09654821455478668,-0.9009688496589661,-0.4230053722858429,0.07534311711788177,-0.9009688496589661,-0.4272920787334442,0.0539507120847702,-0.9009688496589661,-0.43051645159721375,0.03242417424917221,-0.9009688496589661,-0.4326705038547516,0.010817022062838078,-0.9009688496589661,-0.43374887108802795,-0.010817022062838078,-0.9009688496589661,-0.43374887108802795,-0.03242417424917221,-0.9009688496589661,-0.4326705038547516,-0.0539507120847702,-0.9009688496589661,-0.43051645159721375,-0.07534311711788177,-0.9009688496589661,-0.4272920787334442,-0.09654821455478668,-0.9009688496589661,-0.4230053722858429,-0.1175132766366005,-0.9009688496589661,-0.4176670014858246,-0.13818617165088654,-0.9009688496589661,-0.4112902581691742,-0.15851552784442902,-0.9009688496589661,-0.403890997171402,-0.1784507930278778,-0.9009688496589661,-0.39548757672309875,-0.19794239103794098,-0.9009688496589661,-0.3861009180545807,-0.21694186329841614,-0.9009688496589661,-0.37575432658195496,-0.23540200293064117,-0.9009688496589661,-0.3644735813140869,-0.25327688455581665,-0.9009688496589661,-0.352286696434021,-0.2705220878124237,-0.9009688496589661,-0.3392239809036255,-0.28709471225738525,-0.9009688496589661,-0.3253178596496582,-0.3029536008834839,-0.9009688496589661,-0.31060299277305603,-0.3180592954158783,-0.9009688496589661,-0.2951158881187439,-0.3323742151260376,-0.9009688496589661,-0.2788950800895691,-0.34586283564567566,-0.9009688496589661,-0.26198092103004456,-0.35849156975746155,-0.9009688496589661,-0.24441541731357574,-0.370229035615921,-0.9009688496589661,-0.22624225914478302,-0.3810460567474365,-0.9009688496589661,-0.20750662684440613,-0.39091575145721436,-0.9009688496589661,-0.18825510144233704,-0.39981353282928467,-0.9009688496589661,-0.16853554546833038,-0.40771734714508057,-0.9009688496589661,-0.14839698374271393,-0.4146074950695038,-0.9009688496589661,-0.127889484167099,-0.4204668700695038,-0.9009688496589661,-0.10706402361392975,-0.425280898809433,-0.9009688496589661,-0.0859723910689354,-0.4290376305580139,-0.9009688496589661,-0.06466701626777649,-0.43172767758369446,-0.9009688496589661,-0.04320086911320686,-0.43334439396858215,-0.9009688496589661,-0.02162731997668743,-0.4338837265968323,-0.9009688496589661,-5.313543226372388e-17,-0.43334439396858215,-0.9009688496589661,0.02162731997668743,-0.43172767758369446,-0.9009688496589661,0.04320086911320686,-0.4290376305580139,-0.9009688496589661,0.06466701626777649,-0.425280898809433,-0.9009688496589661,0.0859723910689354,-0.4204668700695038,-0.9009688496589661,0.10706402361392975,-0.4146074950695038,-0.9009688496589661,0.127889484167099,-0.40771734714508057,-0.9009688496589661,0.14839698374271393,-0.39981353282928467,-0.9009688496589661,0.16853554546833038,-0.39091575145721436,-0.9009688496589661,0.18825510144233704,-0.3810460567474365,-0.9009688496589661,0.20750662684440613,-0.370229035615921,-0.9009688496589661,0.22624225914478302,-0.35849156975746155,-0.9009688496589661,0.24441541731357574,-0.34586283564567566,-0.9009688496589661,0.26198092103004456,-0.3323742151260376,-0.9009688496589661,0.2788950800895691,-0.3180592954158783,-0.9009688496589661,0.2951158881187439,-0.3029536008834839,-0.9009688496589661,0.31060299277305603,-0.28709471225738525,-0.9009688496589661,0.3253178596496582,-0.2705220878124237,-0.9009688496589661,0.3392239809036255,-0.25327688455581665,-0.9009688496589661,0.352286696434021,-0.23540200293064117,-0.9009688496589661,0.3644735813140869,-0.21694186329841614,-0.9009688496589661,0.37575432658195496,-0.19794239103794098,-0.9009688496589661,0.3861009180545807,-0.1784507930278778,-0.9009688496589661,0.39548757672309875,-0.15851552784442902,-0.9009688496589661,0.403890997171402,-0.13818617165088654,-0.9009688496589661,0.4112902581691742,-0.1175132766366005,-0.9009688496589661,0.4176670014858246,-0.09654821455478668,-0.9009688496589661,0.4230053722858429,-0.07534311711788177,-0.9009688496589661,0.4272920787334442,-0.0539507120847702,-0.9009688496589661,0.43051645159721375,-0.03242417424917221,-0.9009688496589661,0.4326705038547516,-0.010817022062838078,-0.9009688496589661,0.43374887108802795,0.010817022062838078,-0.9009688496589661,0.43374887108802795,0.03242417424917221,-0.9009688496589661,0.4326705038547516,0.0539507120847702,-0.9009688496589661,0.43051645159721375,0.07534311711788177,-0.9009688496589661,0.4272920787334442,0.09654821455478668,-0.9009688496589661,0.4230053722858429,0.1175132766366005,-0.9009688496589661,0.4176670014858246,0.13818617165088654,-0.9009688496589661,0.4112902581691742,0.15851552784442902,-0.9009688496589661,0.403890997171402,0.1784507930278778,-0.9009688496589661,0.39548757672309875,0.19794239103794098,-0.9009688496589661,0.3861009180545807,0.21694186329841614,-0.9009688496589661,0.37575432658195496,0.23540200293064117,-0.9009688496589661,0.3644735813140869,0.25327688455581665,-0.9009688496589661,0.352286696434021,0.2705220878124237,-0.9009688496589661,0.3392239809036255,0.28709471225738525,-0.9009688496589661,0.3253178596496582,0.3029536008834839,-0.9009688496589661,0.31060299277305603,0.3180592954158783,-0.9009688496589661,0.2951158881187439,0.3323742151260376,-0.9009688496589661,0.2788950800895691,0.34586283564567566,-0.9009688496589661,0.26198092103004456,0.35849156975746155,-0.9009688496589661,0.24441541731357574,0.370229035615921,-0.9009688496589661,0.22624225914478302,0.3810460567474365,-0.9009688496589661,0.20750662684440613,0.39091575145721436,-0.9009688496589661,0.18825510144233704,0.39981353282928467,-0.9009688496589661,0.16853554546833038,0.40771734714508057,-0.9009688496589661,0.14839698374271393,0.4146074950695038,-0.9009688496589661,0.127889484167099,0.4204668700695038,-0.9009688496589661,0.10706402361392975,0.425280898809433,-0.9009688496589661,0.0859723910689354,0.4290376305580139,-0.9009688496589661,0.06466701626777649,0.43172767758369446,-0.9009688496589661,0.04320086911320686,0.43334439396858215,-0.9009688496589661,0.02162731997668743,0.4338837265968323,-0.9009688496589661,1.0627086452744776e-16,0.4782539904117584,-0.8782215714454651,0.0,0.4776594638824463,-0.8782215714454651,-0.023838993161916733,0.47587743401527405,-0.8782215714454651,-0.047618716955184937,0.4729122817516327,-0.8782215714454651,-0.07128005474805832,0.46877139806747437,-0.8782215714454651,-0.09476418048143387,0.4634650647640228,-0.8782215714454651,-0.11801270395517349,0.4570064842700958,-0.8782215714454651,-0.14096783101558685,0.4494117200374603,-0.8782215714454651,-0.1635724902153015,0.44069966673851013,-0.8782215714454651,-0.18577048182487488,0.4308919310569763,-0.8782215714454651,-0.20750662684440613,0.4200129508972168,-0.8782215714454651,-0.22872686386108398,0.4080897569656372,-0.8782215714454651,-0.24937845766544342,0.3951519727706909,-0.8782215714454651,-0.26941007375717163,0.38123178482055664,-0.8782215714454651,-0.2887718677520752,0.3663637936115265,-0.8782215714454651,-0.3074157238006592,0.3505849838256836,-0.8782215714454651,-0.3252953290939331,0.3339345455169678,-0.8782215714454651,-0.34236615896224976,0.31645387411117554,-0.8782215714454651,-0.35858583450317383,0.2981864809989929,-0.8782215714454651,-0.3739140033721924,0.279177725315094,-0.8782215714454651,-0.38831257820129395,0.25947490334510803,-0.8782215714454651,-0.4017457365989685,0.2391269952058792,-0.8782215714454651,-0.41418009996414185,0.21818456053733826,-0.8782215714454651,-0.42558473348617554,0.19669969379901886,-0.8782215714454651,-0.4359312951564789,0.17472580075263977,-0.8782215714454651,-0.4451940655708313,0.15231750905513763,-0.8782215714454651,-0.4533500373363495,0.12953053414821625,-0.8782215714454651,-0.46037888526916504,0.10642152279615402,-0.8782215714454651,-0.46626314520835876,0.08304793387651443,-0.8782215714454651,-0.4709882140159607,0.059467870742082596,-0.8782215714454651,-0.4745423495769501,0.035739965736866,-0.8782215714454651,-0.47691667079925537,0.011923202313482761,-0.8782215714454651,-0.4781053364276886,-0.011923202313482761,-0.8782215714454651,-0.4781053364276886,-0.035739965736866,-0.8782215714454651,-0.47691667079925537,-0.059467870742082596,-0.8782215714454651,-0.4745423495769501,-0.08304793387651443,-0.8782215714454651,-0.4709882140159607,-0.10642152279615402,-0.8782215714454651,-0.46626314520835876,-0.12953053414821625,-0.8782215714454651,-0.46037888526916504,-0.15231750905513763,-0.8782215714454651,-0.4533500373363495,-0.17472580075263977,-0.8782215714454651,-0.4451940655708313,-0.19669969379901886,-0.8782215714454651,-0.4359312951564789,-0.21818456053733826,-0.8782215714454651,-0.42558473348617554,-0.2391269952058792,-0.8782215714454651,-0.41418009996414185,-0.25947490334510803,-0.8782215714454651,-0.4017457365989685,-0.279177725315094,-0.8782215714454651,-0.38831257820129395,-0.2981864809989929,-0.8782215714454651,-0.3739140033721924,-0.31645387411117554,-0.8782215714454651,-0.35858583450317383,-0.3339345455169678,-0.8782215714454651,-0.34236615896224976,-0.3505849838256836,-0.8782215714454651,-0.3252953290939331,-0.3663637936115265,-0.8782215714454651,-0.3074157238006592,-0.38123178482055664,-0.8782215714454651,-0.2887718677520752,-0.3951519727706909,-0.8782215714454651,-0.26941007375717163,-0.4080897569656372,-0.8782215714454651,-0.24937845766544342,-0.4200129508972168,-0.8782215714454651,-0.22872686386108398,-0.4308919310569763,-0.8782215714454651,-0.20750662684440613,-0.44069966673851013,-0.8782215714454651,-0.18577048182487488,-0.4494117200374603,-0.8782215714454651,-0.1635724902153015,-0.4570064842700958,-0.8782215714454651,-0.14096783101558685,-0.4634650647640228,-0.8782215714454651,-0.11801270395517349,-0.46877139806747437,-0.8782215714454651,-0.09476418048143387,-0.4729122817516327,-0.8782215714454651,-0.07128005474805832,-0.47587743401527405,-0.8782215714454651,-0.047618716955184937,-0.4776594638824463,-0.8782215714454651,-0.023838993161916733,-0.4782539904117584,-0.8782215714454651,-5.856921810353167e-17,-0.4776594638824463,-0.8782215714454651,0.023838993161916733,-0.47587743401527405,-0.8782215714454651,0.047618716955184937,-0.4729122817516327,-0.8782215714454651,0.07128005474805832,-0.46877139806747437,-0.8782215714454651,0.09476418048143387,-0.4634650647640228,-0.8782215714454651,0.11801270395517349,-0.4570064842700958,-0.8782215714454651,0.14096783101558685,-0.4494117200374603,-0.8782215714454651,0.1635724902153015,-0.44069966673851013,-0.8782215714454651,0.18577048182487488,-0.4308919310569763,-0.8782215714454651,0.20750662684440613,-0.4200129508972168,-0.8782215714454651,0.22872686386108398,-0.4080897569656372,-0.8782215714454651,0.24937845766544342,-0.3951519727706909,-0.8782215714454651,0.26941007375717163,-0.38123178482055664,-0.8782215714454651,0.2887718677520752,-0.3663637936115265,-0.8782215714454651,0.3074157238006592,-0.3505849838256836,-0.8782215714454651,0.3252953290939331,-0.3339345455169678,-0.8782215714454651,0.34236615896224976,-0.31645387411117554,-0.8782215714454651,0.35858583450317383,-0.2981864809989929,-0.8782215714454651,0.3739140033721924,-0.279177725315094,-0.8782215714454651,0.38831257820129395,-0.25947490334510803,-0.8782215714454651,0.4017457365989685,-0.2391269952058792,-0.8782215714454651,0.41418009996414185,-0.21818456053733826,-0.8782215714454651,0.42558473348617554,-0.19669969379901886,-0.8782215714454651,0.4359312951564789,-0.17472580075263977,-0.8782215714454651,0.4451940655708313,-0.15231750905513763,-0.8782215714454651,0.4533500373363495,-0.12953053414821625,-0.8782215714454651,0.46037888526916504,-0.10642152279615402,-0.8782215714454651,0.46626314520835876,-0.08304793387651443,-0.8782215714454651,0.4709882140159607,-0.059467870742082596,-0.8782215714454651,0.4745423495769501,-0.035739965736866,-0.8782215714454651,0.47691667079925537,-0.011923202313482761,-0.8782215714454651,0.4781053364276886,0.011923202313482761,-0.8782215714454651,0.4781053364276886,0.035739965736866,-0.8782215714454651,0.47691667079925537,0.059467870742082596,-0.8782215714454651,0.4745423495769501,0.08304793387651443,-0.8782215714454651,0.4709882140159607,0.10642152279615402,-0.8782215714454651,0.46626314520835876,0.12953053414821625,-0.8782215714454651,0.46037888526916504,0.15231750905513763,-0.8782215714454651,0.4533500373363495,0.17472580075263977,-0.8782215714454651,0.4451940655708313,0.19669969379901886,-0.8782215714454651,0.4359312951564789,0.21818456053733826,-0.8782215714454651,0.42558473348617554,0.2391269952058792,-0.8782215714454651,0.41418009996414185,0.25947490334510803,-0.8782215714454651,0.4017457365989685,0.279177725315094,-0.8782215714454651,0.38831257820129395,0.2981864809989929,-0.8782215714454651,0.3739140033721924,0.31645387411117554,-0.8782215714454651,0.35858583450317383,0.3339345455169678,-0.8782215714454651,0.34236615896224976,0.3505849838256836,-0.8782215714454651,0.3252953290939331,0.3663637936115265,-0.8782215714454651,0.3074157238006592,0.38123178482055664,-0.8782215714454651,0.2887718677520752,0.3951519727706909,-0.8782215714454651,0.26941007375717163,0.4080897569656372,-0.8782215714454651,0.24937845766544342,0.4200129508972168,-0.8782215714454651,0.22872686386108398,0.4308919310569763,-0.8782215714454651,0.20750662684440613,0.44069966673851013,-0.8782215714454651,0.18577048182487488,0.4494117200374603,-0.8782215714454651,0.1635724902153015,0.4570064842700958,-0.8782215714454651,0.14096783101558685,0.4634650647640228,-0.8782215714454651,0.11801270395517349,0.46877139806747437,-0.8782215714454651,0.09476418048143387,0.4729122817516327,-0.8782215714454651,0.07128005474805832,0.47587743401527405,-0.8782215714454651,0.047618716955184937,0.4776594638824463,-0.8782215714454651,0.023838993161916733,0.4782539904117584,-0.8782215714454651,1.1713843620706334e-16,0.5214352011680603,-0.853290855884552,0.0,0.5207870006561279,-0.853290855884552,-0.025991398841142654,0.5188440680503845,-0.853290855884552,-0.05191817879676819,0.5156112313270569,-0.853290855884552,-0.07771588116884232,0.5110964179039001,-0.853290855884552,-0.10332037508487701,0.5053110122680664,-0.853290855884552,-0.12866799533367157,0.49826928973197937,-0.853290855884552,-0.1536957174539566,0.4899888038635254,-0.853290855884552,-0.178341343998909,0.48049014806747437,-0.853290855884552,-0.20254357159137726,0.46979689598083496,-0.853290855884552,-0.22624225914478302,0.457935631275177,-0.853290855884552,-0.24937845766544342,0.4449359178543091,-0.853290855884552,-0.2718946635723114,0.4308299720287323,-0.853290855884552,-0.29373490810394287,0.41565296053886414,-0.853290855884552,-0.31484487652778625,0.39944255352020264,-0.853290855884552,-0.3351720869541168,0.38223904371261597,-0.853290855884552,-0.3546660244464874,0.36408525705337524,-0.853290855884552,-0.3732781708240509,0.34502628445625305,-0.853290855884552,-0.39096230268478394,0.3251095414161682,-0.853290855884552,-0.4076744616031647,0.3043844997882843,-0.853290855884552,-0.4233730733394623,0.28290271759033203,-0.853290855884552,-0.4380190968513489,0.26071760058403015,-0.853290855884552,-0.4515761435031891,0.2378842979669571,-0.853290855884552,-0.46401047706604004,0.21445956826210022,-0.853290855884552,-0.47529125213623047,0.19050167500972748,-0.853290855884552,-0.4853903353214264,0.1660701483488083,-0.853290855884552,-0.49428269267082214,0.14122575521469116,-0.853290855884552,-0.5019461512565613,0.1160302460193634,-0.853290855884552,-0.5083617568016052,0.09054627269506454,-0.853290855884552,-0.513513445854187,0.06483718752861023,-0.853290855884552,-0.5173884630203247,0.03896690160036087,-0.853290855884552,-0.5199771523475647,0.012999740429222584,-0.853290855884552,-0.521273136138916,-0.012999740429222584,-0.853290855884552,-0.521273136138916,-0.03896690160036087,-0.853290855884552,-0.5199771523475647,-0.06483718752861023,-0.853290855884552,-0.5173884630203247,-0.09054627269506454,-0.853290855884552,-0.513513445854187,-0.1160302460193634,-0.853290855884552,-0.5083617568016052,-0.14122575521469116,-0.853290855884552,-0.5019461512565613,-0.1660701483488083,-0.853290855884552,-0.49428269267082214,-0.19050167500972748,-0.853290855884552,-0.4853903353214264,-0.21445956826210022,-0.853290855884552,-0.47529125213623047,-0.2378842979669571,-0.853290855884552,-0.46401047706604004,-0.26071760058403015,-0.853290855884552,-0.4515761435031891,-0.28290271759033203,-0.853290855884552,-0.4380190968513489,-0.3043844997882843,-0.853290855884552,-0.4233730733394623,-0.3251095414161682,-0.853290855884552,-0.4076744616031647,-0.34502628445625305,-0.853290855884552,-0.39096230268478394,-0.36408525705337524,-0.853290855884552,-0.3732781708240509,-0.38223904371261597,-0.853290855884552,-0.3546660244464874,-0.39944255352020264,-0.853290855884552,-0.3351720869541168,-0.41565296053886414,-0.853290855884552,-0.31484487652778625,-0.4308299720287323,-0.853290855884552,-0.29373490810394287,-0.4449359178543091,-0.853290855884552,-0.2718946635723114,-0.457935631275177,-0.853290855884552,-0.24937845766544342,-0.46979689598083496,-0.853290855884552,-0.22624225914478302,-0.48049014806747437,-0.853290855884552,-0.20254357159137726,-0.4899888038635254,-0.853290855884552,-0.178341343998909,-0.49826928973197937,-0.853290855884552,-0.1536957174539566,-0.5053110122680664,-0.853290855884552,-0.12866799533367157,-0.5110964179039001,-0.853290855884552,-0.10332037508487701,-0.5156112313270569,-0.853290855884552,-0.07771588116884232,-0.5188440680503845,-0.853290855884552,-0.05191817879676819,-0.5207870006561279,-0.853290855884552,-0.025991398841142654,-0.5214352011680603,-0.853290855884552,-6.385739699447298e-17,-0.5207870006561279,-0.853290855884552,0.025991398841142654,-0.5188440680503845,-0.853290855884552,0.05191817879676819,-0.5156112313270569,-0.853290855884552,0.07771588116884232,-0.5110964179039001,-0.853290855884552,0.10332037508487701,-0.5053110122680664,-0.853290855884552,0.12866799533367157,-0.49826928973197937,-0.853290855884552,0.1536957174539566,-0.4899888038635254,-0.853290855884552,0.178341343998909,-0.48049014806747437,-0.853290855884552,0.20254357159137726,-0.46979689598083496,-0.853290855884552,0.22624225914478302,-0.457935631275177,-0.853290855884552,0.24937845766544342,-0.4449359178543091,-0.853290855884552,0.2718946635723114,-0.4308299720287323,-0.853290855884552,0.29373490810394287,-0.41565296053886414,-0.853290855884552,0.31484487652778625,-0.39944255352020264,-0.853290855884552,0.3351720869541168,-0.38223904371261597,-0.853290855884552,0.3546660244464874,-0.36408525705337524,-0.853290855884552,0.3732781708240509,-0.34502628445625305,-0.853290855884552,0.39096230268478394,-0.3251095414161682,-0.853290855884552,0.4076744616031647,-0.3043844997882843,-0.853290855884552,0.4233730733394623,-0.28290271759033203,-0.853290855884552,0.4380190968513489,-0.26071760058403015,-0.853290855884552,0.4515761435031891,-0.2378842979669571,-0.853290855884552,0.46401047706604004,-0.21445956826210022,-0.853290855884552,0.47529125213623047,-0.19050167500972748,-0.853290855884552,0.4853903353214264,-0.1660701483488083,-0.853290855884552,0.49428269267082214,-0.14122575521469116,-0.853290855884552,0.5019461512565613,-0.1160302460193634,-0.853290855884552,0.5083617568016052,-0.09054627269506454,-0.853290855884552,0.513513445854187,-0.06483718752861023,-0.853290855884552,0.5173884630203247,-0.03896690160036087,-0.853290855884552,0.5199771523475647,-0.012999740429222584,-0.853290855884552,0.521273136138916,0.012999740429222584,-0.853290855884552,0.521273136138916,0.03896690160036087,-0.853290855884552,0.5199771523475647,0.06483718752861023,-0.853290855884552,0.5173884630203247,0.09054627269506454,-0.853290855884552,0.513513445854187,0.1160302460193634,-0.853290855884552,0.5083617568016052,0.14122575521469116,-0.853290855884552,0.5019461512565613,0.1660701483488083,-0.853290855884552,0.49428269267082214,0.19050167500972748,-0.853290855884552,0.4853903353214264,0.21445956826210022,-0.853290855884552,0.47529125213623047,0.2378842979669571,-0.853290855884552,0.46401047706604004,0.26071760058403015,-0.853290855884552,0.4515761435031891,0.28290271759033203,-0.853290855884552,0.4380190968513489,0.3043844997882843,-0.853290855884552,0.4233730733394623,0.3251095414161682,-0.853290855884552,0.4076744616031647,0.34502628445625305,-0.853290855884552,0.39096230268478394,0.36408525705337524,-0.853290855884552,0.3732781708240509,0.38223904371261597,-0.853290855884552,0.3546660244464874,0.39944255352020264,-0.853290855884552,0.3351720869541168,0.41565296053886414,-0.853290855884552,0.31484487652778625,0.4308299720287323,-0.853290855884552,0.29373490810394287,0.4449359178543091,-0.853290855884552,0.2718946635723114,0.457935631275177,-0.853290855884552,0.24937845766544342,0.46979689598083496,-0.853290855884552,0.22624225914478302,0.48049014806747437,-0.853290855884552,0.20254357159137726,0.4899888038635254,-0.853290855884552,0.178341343998909,0.49826928973197937,-0.853290855884552,0.1536957174539566,0.5053110122680664,-0.853290855884552,0.12866799533367157,0.5110964179039001,-0.853290855884552,0.10332037508487701,0.5156112313270569,-0.853290855884552,0.07771588116884232,0.5188440680503845,-0.853290855884552,0.05191817879676819,0.5207870006561279,-0.853290855884552,0.025991398841142654,0.5214352011680603,-0.853290855884552,1.2771479398894595e-16,0.5633200407028198,-0.826238751411438,0.0,0.5626198053359985,-0.826238751411438,-0.028079187497496605,0.5605207681655884,-0.826238751411438,-0.05608856678009033,0.5570282340049744,-0.826238751411438,-0.08395849913358688,0.5521508455276489,-0.826238751411438,-0.11161969602108002,0.5459007024765015,-0.826238751411438,-0.13900339603424072,0.5382933020591736,-0.826238751411438,-0.1660415083169937,0.5293477177619934,-0.826238751411438,-0.19266681373119354,0.5190860629081726,-0.826238751411438,-0.21881310641765594,0.507533848285675,-0.826238751411438,-0.24441541731357574,0.49471983313560486,-0.826238751411438,-0.26941007375717163,0.4806758761405945,-0.826238751411438,-0.29373490810394287,0.4654368758201599,-0.826238751411438,-0.3173294961452484,0.4490407407283783,-0.826238751411438,-0.3401351273059845,0.4315282106399536,-0.826238751411438,-0.3620951473712921,0.4129428267478943,-0.826238751411438,-0.383154958486557,0.39333081245422363,-0.826238751411438,-0.4032621681690216,0.37274089455604553,-0.826238751411438,-0.42236679792404175,0.35122430324554443,-0.826238751411438,-0.44042134284973145,0.32883453369140625,-0.826238751411438,-0.4573809504508972,0.3056271970272064,-0.826238751411438,-0.4732034504413605,0.2816600203514099,-0.826238751411438,-0.48784947395324707,0.2569926083087921,-0.826238751411438,-0.5012826323509216,0.2316862791776657,-0.826238751411438,-0.5134695172309875,0.20580393075942993,-0.826238751411438,-0.5243798494338989,0.179409921169281,-0.826238751411438,-0.533986508846283,0.15256987512111664,-0.826238751411438,-0.5422655344009399,0.12535050511360168,-0.826238751411438,-0.5491964221000671,0.0978194996714592,-0.826238751411438,-0.5547619462013245,0.07004529982805252,-0.826238751411438,-0.5589482188224792,0.04209696128964424,-0.826238751411438,-0.5617449283599854,0.014043958857655525,-0.826238751411438,-0.5631449818611145,-0.014043958857655525,-0.826238751411438,-0.5631449818611145,-0.04209696128964424,-0.826238751411438,-0.5617449283599854,-0.07004529982805252,-0.826238751411438,-0.5589482188224792,-0.0978194996714592,-0.826238751411438,-0.5547619462013245,-0.12535050511360168,-0.826238751411438,-0.5491964221000671,-0.15256987512111664,-0.826238751411438,-0.5422655344009399,-0.179409921169281,-0.826238751411438,-0.533986508846283,-0.20580393075942993,-0.826238751411438,-0.5243798494338989,-0.2316862791776657,-0.826238751411438,-0.5134695172309875,-0.2569926083087921,-0.826238751411438,-0.5012826323509216,-0.2816600203514099,-0.826238751411438,-0.48784947395324707,-0.3056271970272064,-0.826238751411438,-0.4732034504413605,-0.32883453369140625,-0.826238751411438,-0.4573809504508972,-0.35122430324554443,-0.826238751411438,-0.44042134284973145,-0.37274089455604553,-0.826238751411438,-0.42236679792404175,-0.39333081245422363,-0.826238751411438,-0.4032621681690216,-0.4129428267478943,-0.826238751411438,-0.383154958486557,-0.4315282106399536,-0.826238751411438,-0.3620951473712921,-0.4490407407283783,-0.826238751411438,-0.3401351273059845,-0.4654368758201599,-0.826238751411438,-0.3173294961452484,-0.4806758761405945,-0.826238751411438,-0.29373490810394287,-0.49471983313560486,-0.826238751411438,-0.26941007375717163,-0.507533848285675,-0.826238751411438,-0.24441541731357574,-0.5190860629081726,-0.826238751411438,-0.21881310641765594,-0.5293477177619934,-0.826238751411438,-0.19266681373119354,-0.5382933020591736,-0.826238751411438,-0.1660415083169937,-0.5459007024765015,-0.826238751411438,-0.13900339603424072,-0.5521508455276489,-0.826238751411438,-0.11161969602108002,-0.5570282340049744,-0.826238751411438,-0.08395849913358688,-0.5605207681655884,-0.826238751411438,-0.05608856678009033,-0.5626198053359985,-0.826238751411438,-0.028079187497496605,-0.5633200407028198,-0.826238751411438,-6.89868101473633e-17,-0.5626198053359985,-0.826238751411438,0.028079187497496605,-0.5605207681655884,-0.826238751411438,0.05608856678009033,-0.5570282340049744,-0.826238751411438,0.08395849913358688,-0.5521508455276489,-0.826238751411438,0.11161969602108002,-0.5459007024765015,-0.826238751411438,0.13900339603424072,-0.5382933020591736,-0.826238751411438,0.1660415083169937,-0.5293477177619934,-0.826238751411438,0.19266681373119354,-0.5190860629081726,-0.826238751411438,0.21881310641765594,-0.507533848285675,-0.826238751411438,0.24441541731357574,-0.49471983313560486,-0.826238751411438,0.26941007375717163,-0.4806758761405945,-0.826238751411438,0.29373490810394287,-0.4654368758201599,-0.826238751411438,0.3173294961452484,-0.4490407407283783,-0.826238751411438,0.3401351273059845,-0.4315282106399536,-0.826238751411438,0.3620951473712921,-0.4129428267478943,-0.826238751411438,0.383154958486557,-0.39333081245422363,-0.826238751411438,0.4032621681690216,-0.37274089455604553,-0.826238751411438,0.42236679792404175,-0.35122430324554443,-0.826238751411438,0.44042134284973145,-0.32883453369140625,-0.826238751411438,0.4573809504508972,-0.3056271970272064,-0.826238751411438,0.4732034504413605,-0.2816600203514099,-0.826238751411438,0.48784947395324707,-0.2569926083087921,-0.826238751411438,0.5012826323509216,-0.2316862791776657,-0.826238751411438,0.5134695172309875,-0.20580393075942993,-0.826238751411438,0.5243798494338989,-0.179409921169281,-0.826238751411438,0.533986508846283,-0.15256987512111664,-0.826238751411438,0.5422655344009399,-0.12535050511360168,-0.826238751411438,0.5491964221000671,-0.0978194996714592,-0.826238751411438,0.5547619462013245,-0.07004529982805252,-0.826238751411438,0.5589482188224792,-0.04209696128964424,-0.826238751411438,0.5617449283599854,-0.014043958857655525,-0.826238751411438,0.5631449818611145,0.014043958857655525,-0.826238751411438,0.5631449818611145,0.04209696128964424,-0.826238751411438,0.5617449283599854,0.07004529982805252,-0.826238751411438,0.5589482188224792,0.0978194996714592,-0.826238751411438,0.5547619462013245,0.12535050511360168,-0.826238751411438,0.5491964221000671,0.15256987512111664,-0.826238751411438,0.5422655344009399,0.179409921169281,-0.826238751411438,0.533986508846283,0.20580393075942993,-0.826238751411438,0.5243798494338989,0.2316862791776657,-0.826238751411438,0.5134695172309875,0.2569926083087921,-0.826238751411438,0.5012826323509216,0.2816600203514099,-0.826238751411438,0.48784947395324707,0.3056271970272064,-0.826238751411438,0.4732034504413605,0.32883453369140625,-0.826238751411438,0.4573809504508972,0.35122430324554443,-0.826238751411438,0.44042134284973145,0.37274089455604553,-0.826238751411438,0.42236679792404175,0.39333081245422363,-0.826238751411438,0.4032621681690216,0.4129428267478943,-0.826238751411438,0.383154958486557,0.4315282106399536,-0.826238751411438,0.3620951473712921,0.4490407407283783,-0.826238751411438,0.3401351273059845,0.4654368758201599,-0.826238751411438,0.3173294961452484,0.4806758761405945,-0.826238751411438,0.29373490810394287,0.49471983313560486,-0.826238751411438,0.26941007375717163,0.507533848285675,-0.826238751411438,0.24441541731357574,0.5190860629081726,-0.826238751411438,0.21881310641765594,0.5293477177619934,-0.826238751411438,0.19266681373119354,0.5382933020591736,-0.826238751411438,0.1660415083169937,0.5459007024765015,-0.826238751411438,0.13900339603424072,0.5521508455276489,-0.826238751411438,0.11161969602108002,0.5570282340049744,-0.826238751411438,0.08395849913358688,0.5605207681655884,-0.826238751411438,0.05608856678009033,0.5626198053359985,-0.826238751411438,0.028079187497496605,0.5633200407028198,-0.826238751411438,1.379736202947266e-16,0.6038044095039368,-0.7971324920654297,0.0,0.6030538082122803,-0.7971324920654297,-0.03009716607630253,0.6008039712905884,-0.7971324920654297,-0.06011950597167015,0.5970604419708252,-0.7971324920654297,-0.08999237418174744,0.5918324589729309,-0.7971324920654297,-0.11964151263237,0.5851331353187561,-0.7971324920654297,-0.14899320900440216,0.5769791007041931,-0.7971324920654297,-0.17797447741031647,0.5673905611038208,-0.7971324920654297,-0.20651327073574066,0.5563914179801941,-0.7971324920654297,-0.23453864455223083,0.5440089702606201,-0.7971324920654297,-0.26198092103004456,0.5302740335464478,-0.7971324920654297,-0.2887718677520752,0.5152208209037781,-0.7971324920654297,-0.31484487652778625,0.4988866150379181,-0.7971324920654297,-0.3401351273059845,0.4813121259212494,-0.7971324920654297,-0.3645797669887543,0.46254101395606995,-0.7971324920654297,-0.3881179988384247,0.4426199495792389,-0.7971324920654297,-0.4106912910938263,0.4215984642505646,-0.7971324920654297,-0.43224355578422546,0.399528831243515,-0.7971324920654297,-0.4527212083339691,0.37646588683128357,-0.7971324920654297,-0.4720732867717743,0.35246700048446655,-0.7971324920654297,-0.490251749753952,0.32759183645248413,-0.7971324920654297,-0.5072113275527954,0.3019022047519684,-0.7971324920654297,-0.522909939289093,0.27546200156211853,-0.7971324920654297,-0.5373085141181946,0.24833697080612183,-0.7971324920654297,-0.5503712296485901,0.22059452533721924,-0.7971324920654297,-0.5620656609535217,0.1923036426305771,-0.7971324920654297,-0.5723627209663391,0.16353467106819153,-0.7971324920654297,-0.5812367796897888,0.13435912132263184,-0.7971324920654297,-0.588665783405304,0.10484953224658966,-0.7971324920654297,-0.5946312546730042,0.07507927715778351,-0.7971324920654297,-0.5991184115409851,0.045122358947992325,-0.7971324920654297,-0.602116048336029,0.015053262002766132,-0.7971324920654297,-0.6036167144775391,-0.015053262002766132,-0.7971324920654297,-0.6036167144775391,-0.045122358947992325,-0.7971324920654297,-0.602116048336029,-0.07507927715778351,-0.7971324920654297,-0.5991184115409851,-0.10484953224658966,-0.7971324920654297,-0.5946312546730042,-0.13435912132263184,-0.7971324920654297,-0.588665783405304,-0.16353467106819153,-0.7971324920654297,-0.5812367796897888,-0.1923036426305771,-0.7971324920654297,-0.5723627209663391,-0.22059452533721924,-0.7971324920654297,-0.5620656609535217,-0.24833697080612183,-0.7971324920654297,-0.5503712296485901,-0.27546200156211853,-0.7971324920654297,-0.5373085141181946,-0.3019022047519684,-0.7971324920654297,-0.522909939289093,-0.32759183645248413,-0.7971324920654297,-0.5072113275527954,-0.35246700048446655,-0.7971324920654297,-0.490251749753952,-0.37646588683128357,-0.7971324920654297,-0.4720732867717743,-0.399528831243515,-0.7971324920654297,-0.4527212083339691,-0.4215984642505646,-0.7971324920654297,-0.43224355578422546,-0.4426199495792389,-0.7971324920654297,-0.4106912910938263,-0.46254101395606995,-0.7971324920654297,-0.3881179988384247,-0.4813121259212494,-0.7971324920654297,-0.3645797669887543,-0.4988866150379181,-0.7971324920654297,-0.3401351273059845,-0.5152208209037781,-0.7971324920654297,-0.31484487652778625,-0.5302740335464478,-0.7971324920654297,-0.2887718677520752,-0.5440089702606201,-0.7971324920654297,-0.26198092103004456,-0.5563914179801941,-0.7971324920654297,-0.23453864455223083,-0.5673905611038208,-0.7971324920654297,-0.20651327073574066,-0.5769791007041931,-0.7971324920654297,-0.17797447741031647,-0.5851331353187561,-0.7971324920654297,-0.14899320900440216,-0.5918324589729309,-0.7971324920654297,-0.11964151263237,-0.5970604419708252,-0.7971324920654297,-0.08999237418174744,-0.6008039712905884,-0.7971324920654297,-0.06011950597167015,-0.6030538082122803,-0.7971324920654297,-0.03009716607630253,-0.6038044095039368,-0.7971324920654297,-7.394471236332444e-17,-0.6030538082122803,-0.7971324920654297,0.03009716607630253,-0.6008039712905884,-0.7971324920654297,0.06011950597167015,-0.5970604419708252,-0.7971324920654297,0.08999237418174744,-0.5918324589729309,-0.7971324920654297,0.11964151263237,-0.5851331353187561,-0.7971324920654297,0.14899320900440216,-0.5769791007041931,-0.7971324920654297,0.17797447741031647,-0.5673905611038208,-0.7971324920654297,0.20651327073574066,-0.5563914179801941,-0.7971324920654297,0.23453864455223083,-0.5440089702606201,-0.7971324920654297,0.26198092103004456,-0.5302740335464478,-0.7971324920654297,0.2887718677520752,-0.5152208209037781,-0.7971324920654297,0.31484487652778625,-0.4988866150379181,-0.7971324920654297,0.3401351273059845,-0.4813121259212494,-0.7971324920654297,0.3645797669887543,-0.46254101395606995,-0.7971324920654297,0.3881179988384247,-0.4426199495792389,-0.7971324920654297,0.4106912910938263,-0.4215984642505646,-0.7971324920654297,0.43224355578422546,-0.399528831243515,-0.7971324920654297,0.4527212083339691,-0.37646588683128357,-0.7971324920654297,0.4720732867717743,-0.35246700048446655,-0.7971324920654297,0.490251749753952,-0.32759183645248413,-0.7971324920654297,0.5072113275527954,-0.3019022047519684,-0.7971324920654297,0.522909939289093,-0.27546200156211853,-0.7971324920654297,0.5373085141181946,-0.24833697080612183,-0.7971324920654297,0.5503712296485901,-0.22059452533721924,-0.7971324920654297,0.5620656609535217,-0.1923036426305771,-0.7971324920654297,0.5723627209663391,-0.16353467106819153,-0.7971324920654297,0.5812367796897888,-0.13435912132263184,-0.7971324920654297,0.588665783405304,-0.10484953224658966,-0.7971324920654297,0.5946312546730042,-0.07507927715778351,-0.7971324920654297,0.5991184115409851,-0.045122358947992325,-0.7971324920654297,0.602116048336029,-0.015053262002766132,-0.7971324920654297,0.6036167144775391,0.015053262002766132,-0.7971324920654297,0.6036167144775391,0.045122358947992325,-0.7971324920654297,0.602116048336029,0.07507927715778351,-0.7971324920654297,0.5991184115409851,0.10484953224658966,-0.7971324920654297,0.5946312546730042,0.13435912132263184,-0.7971324920654297,0.588665783405304,0.16353467106819153,-0.7971324920654297,0.5812367796897888,0.1923036426305771,-0.7971324920654297,0.5723627209663391,0.22059452533721924,-0.7971324920654297,0.5620656609535217,0.24833697080612183,-0.7971324920654297,0.5503712296485901,0.27546200156211853,-0.7971324920654297,0.5373085141181946,0.3019022047519684,-0.7971324920654297,0.522909939289093,0.32759183645248413,-0.7971324920654297,0.5072113275527954,0.35246700048446655,-0.7971324920654297,0.490251749753952,0.37646588683128357,-0.7971324920654297,0.4720732867717743,0.399528831243515,-0.7971324920654297,0.4527212083339691,0.4215984642505646,-0.7971324920654297,0.43224355578422546,0.4426199495792389,-0.7971324920654297,0.4106912910938263,0.46254101395606995,-0.7971324920654297,0.3881179988384247,0.4813121259212494,-0.7971324920654297,0.3645797669887543,0.4988866150379181,-0.7971324920654297,0.3401351273059845,0.5152208209037781,-0.7971324920654297,0.31484487652778625,0.5302740335464478,-0.7971324920654297,0.2887718677520752,0.5440089702606201,-0.7971324920654297,0.26198092103004456,0.5563914179801941,-0.7971324920654297,0.23453864455223083,0.5673905611038208,-0.7971324920654297,0.20651327073574066,0.5769791007041931,-0.7971324920654297,0.17797447741031647,0.5851331353187561,-0.7971324920654297,0.14899320900440216,0.5918324589729309,-0.7971324920654297,0.11964151263237,0.5970604419708252,-0.7971324920654297,0.08999237418174744,0.6008039712905884,-0.7971324920654297,0.06011950597167015,0.6030538082122803,-0.7971324920654297,0.03009716607630253,0.6038044095039368,-0.7971324920654297,1.4788942472664888e-16,0.6427876353263855,-0.7660444378852844,0.0,0.6419885754585266,-0.7660444378852844,-0.0320403166115284,0.6395934820175171,-0.7660444378852844,-0.06400097906589508,0.6356081962585449,-0.7660444378852844,-0.09580252319574356,0.6300427317619324,-0.7660444378852844,-0.12736588716506958,0.6229108572006226,-0.7660444378852844,-0.15861259400844574,0.6142303347587585,-0.7660444378852844,-0.1894649714231491,0.6040228009223938,-0.7660444378852844,-0.21984630823135376,0.5923134684562683,-0.7660444378852844,-0.24968107044696808,0.5791316032409668,-0.7660444378852844,-0.2788950800895691,0.564509928226471,-0.7660444378852844,-0.3074157238006592,0.5484848022460938,-0.7660444378852844,-0.3351720869541168,0.5310960412025452,-0.7660444378852844,-0.3620951473712921,0.5123869180679321,-0.7660444378852844,-0.3881179988384247,0.49240386486053467,-0.7660444378852844,-0.41317591071128845,0.47119665145874023,-0.7660444378852844,-0.43720659613609314,0.4488179683685303,-0.7660444378852844,-0.4601503312587738,0.4253234565258026,-0.7660444378852844,-0.4819500744342804,0.40077152848243713,-0.7660444378852844,-0.5025516152381897,0.37522318959236145,-0.7660444378852844,-0.5219036936759949,0.3487420082092285,-0.7660444378852844,-0.5399582386016846,0.32139381766319275,-0.7660444378852844,-0.5566704273223877,0.2932465672492981,-0.7660444378852844,-0.5719985961914062,0.26437026262283325,-0.7660444378852844,-0.5859046578407288,0.23483668267726898,-0.7660444378852844,-0.5983541011810303,0.20471927523612976,-0.7660444378852844,-0.6093159317970276,0.17409290373325348,-0.7660444378852844,-0.6187629103660583,0.1430336982011795,-0.7660444378852844,-0.626671552658081,0.11161889880895615,-0.7660444378852844,-0.6330222487449646,0.07992659509181976,-0.7660444378852844,-0.637799084186554,0.048035576939582825,-0.7660444378852844,-0.6409902572631836,0.016025139018893242,-0.7660444378852844,-0.6425878405570984,-0.016025139018893242,-0.7660444378852844,-0.6425878405570984,-0.048035576939582825,-0.7660444378852844,-0.6409902572631836,-0.07992659509181976,-0.7660444378852844,-0.637799084186554,-0.11161889880895615,-0.7660444378852844,-0.6330222487449646,-0.1430336982011795,-0.7660444378852844,-0.626671552658081,-0.17409290373325348,-0.7660444378852844,-0.6187629103660583,-0.20471927523612976,-0.7660444378852844,-0.6093159317970276,-0.23483668267726898,-0.7660444378852844,-0.5983541011810303,-0.26437026262283325,-0.7660444378852844,-0.5859046578407288,-0.2932465672492981,-0.7660444378852844,-0.5719985961914062,-0.32139381766319275,-0.7660444378852844,-0.5566704273223877,-0.3487420082092285,-0.7660444378852844,-0.5399582386016846,-0.37522318959236145,-0.7660444378852844,-0.5219036936759949,-0.40077152848243713,-0.7660444378852844,-0.5025516152381897,-0.4253234565258026,-0.7660444378852844,-0.4819500744342804,-0.4488179683685303,-0.7660444378852844,-0.4601503312587738,-0.47119665145874023,-0.7660444378852844,-0.43720659613609314,-0.49240386486053467,-0.7660444378852844,-0.41317591071128845,-0.5123869180679321,-0.7660444378852844,-0.3881179988384247,-0.5310960412025452,-0.7660444378852844,-0.3620951473712921,-0.5484848022460938,-0.7660444378852844,-0.3351720869541168,-0.564509928226471,-0.7660444378852844,-0.3074157238006592,-0.5791316032409668,-0.7660444378852844,-0.2788950800895691,-0.5923134684562683,-0.7660444378852844,-0.24968107044696808,-0.6040228009223938,-0.7660444378852844,-0.21984630823135376,-0.6142303347587585,-0.7660444378852844,-0.1894649714231491,-0.6229108572006226,-0.7660444378852844,-0.15861259400844574,-0.6300427317619324,-0.7660444378852844,-0.12736588716506958,-0.6356081962585449,-0.7660444378852844,-0.09580252319574356,-0.6395934820175171,-0.7660444378852844,-0.06400097906589508,-0.6419885754585266,-0.7660444378852844,-0.0320403166115284,-0.6427876353263855,-0.7660444378852844,-7.871878195995179e-17,-0.6419885754585266,-0.7660444378852844,0.0320403166115284,-0.6395934820175171,-0.7660444378852844,0.06400097906589508,-0.6356081962585449,-0.7660444378852844,0.09580252319574356,-0.6300427317619324,-0.7660444378852844,0.12736588716506958,-0.6229108572006226,-0.7660444378852844,0.15861259400844574,-0.6142303347587585,-0.7660444378852844,0.1894649714231491,-0.6040228009223938,-0.7660444378852844,0.21984630823135376,-0.5923134684562683,-0.7660444378852844,0.24968107044696808,-0.5791316032409668,-0.7660444378852844,0.2788950800895691,-0.564509928226471,-0.7660444378852844,0.3074157238006592,-0.5484848022460938,-0.7660444378852844,0.3351720869541168,-0.5310960412025452,-0.7660444378852844,0.3620951473712921,-0.5123869180679321,-0.7660444378852844,0.3881179988384247,-0.49240386486053467,-0.7660444378852844,0.41317591071128845,-0.47119665145874023,-0.7660444378852844,0.43720659613609314,-0.4488179683685303,-0.7660444378852844,0.4601503312587738,-0.4253234565258026,-0.7660444378852844,0.4819500744342804,-0.40077152848243713,-0.7660444378852844,0.5025516152381897,-0.37522318959236145,-0.7660444378852844,0.5219036936759949,-0.3487420082092285,-0.7660444378852844,0.5399582386016846,-0.32139381766319275,-0.7660444378852844,0.5566704273223877,-0.2932465672492981,-0.7660444378852844,0.5719985961914062,-0.26437026262283325,-0.7660444378852844,0.5859046578407288,-0.23483668267726898,-0.7660444378852844,0.5983541011810303,-0.20471927523612976,-0.7660444378852844,0.6093159317970276,-0.17409290373325348,-0.7660444378852844,0.6187629103660583,-0.1430336982011795,-0.7660444378852844,0.626671552658081,-0.11161889880895615,-0.7660444378852844,0.6330222487449646,-0.07992659509181976,-0.7660444378852844,0.637799084186554,-0.048035576939582825,-0.7660444378852844,0.6409902572631836,-0.016025139018893242,-0.7660444378852844,0.6425878405570984,0.016025139018893242,-0.7660444378852844,0.6425878405570984,0.048035576939582825,-0.7660444378852844,0.6409902572631836,0.07992659509181976,-0.7660444378852844,0.637799084186554,0.11161889880895615,-0.7660444378852844,0.6330222487449646,0.1430336982011795,-0.7660444378852844,0.626671552658081,0.17409290373325348,-0.7660444378852844,0.6187629103660583,0.20471927523612976,-0.7660444378852844,0.6093159317970276,0.23483668267726898,-0.7660444378852844,0.5983541011810303,0.26437026262283325,-0.7660444378852844,0.5859046578407288,0.2932465672492981,-0.7660444378852844,0.5719985961914062,0.32139381766319275,-0.7660444378852844,0.5566704273223877,0.3487420082092285,-0.7660444378852844,0.5399582386016846,0.37522318959236145,-0.7660444378852844,0.5219036936759949,0.40077152848243713,-0.7660444378852844,0.5025516152381897,0.4253234565258026,-0.7660444378852844,0.4819500744342804,0.4488179683685303,-0.7660444378852844,0.4601503312587738,0.47119665145874023,-0.7660444378852844,0.43720659613609314,0.49240386486053467,-0.7660444378852844,0.41317591071128845,0.5123869180679321,-0.7660444378852844,0.3881179988384247,0.5310960412025452,-0.7660444378852844,0.3620951473712921,0.5484848022460938,-0.7660444378852844,0.3351720869541168,0.564509928226471,-0.7660444378852844,0.3074157238006592,0.5791316032409668,-0.7660444378852844,0.2788950800895691,0.5923134684562683,-0.7660444378852844,0.24968107044696808,0.6040228009223938,-0.7660444378852844,0.21984630823135376,0.6142303347587585,-0.7660444378852844,0.1894649714231491,0.6229108572006226,-0.7660444378852844,0.15861259400844574,0.6300427317619324,-0.7660444378852844,0.12736588716506958,0.6356081962585449,-0.7660444378852844,0.09580252319574356,0.6395934820175171,-0.7660444378852844,0.06400097906589508,0.6419885754585266,-0.7660444378852844,0.0320403166115284,0.6427876353263855,-0.7660444378852844,1.5743756391990358e-16,0.6801727414131165,-0.7330518960952759,0.0,0.6793272495269775,-0.7330518960952759,-0.0339038111269474,0.6767928004264832,-0.7330518960952759,-0.0677233338356018,0.6725757718086243,-0.7330518960952759,-0.10137448459863663,0.6666865944862366,-0.7330518960952759,-0.1347736120223999,0.6591399312019348,-0.7330518960952759,-0.16783764958381653,0.6499545574188232,-0.7330518960952759,-0.20048443973064423,0.6391533017158508,-0.7330518960952759,-0.23263277113437653,0.6267629861831665,-0.7330518960952759,-0.2642027735710144,0.6128144860267639,-0.7330518960952759,-0.2951158881187439,0.5973423719406128,-0.7330518960952759,-0.3252953290939331,0.5803852081298828,-0.7330518960952759,-0.3546660244464874,0.5619850754737854,-0.7330518960952759,-0.383154958486557,0.5421878099441528,-0.7330518960952759,-0.4106912910938263,0.52104252576828,-0.7330518960952759,-0.43720659613609314,0.49860191345214844,-0.7330518960952759,-0.46263495087623596,0.47492164373397827,-0.7330518960952759,-0.48691311478614807,0.4500606656074524,-0.7330518960952759,-0.5099807381629944,0.4240807592868805,-0.7330518960952759,-0.531780481338501,0.3970465362071991,-0.7330518960952759,-0.5522580742835999,0.36902517080307007,-0.7330518960952759,-0.5713627338409424,0.3400863707065582,-0.7330518960952759,-0.5890468955039978,0.3103020489215851,-0.7330518960952759,-0.6052665710449219,0.27974626421928406,-0.7330518960952759,-0.6199814081192017,0.24849499762058258,-0.7330518960952759,-0.6331549286842346,0.21662594377994537,-0.7330518960952759,-0.6447543501853943,0.18421830236911774,-0.7330518960952759,-0.6547507643699646,0.1513526737689972,-0.7330518960952759,-0.663119375705719,0.11811075359582901,-0.7330518960952759,-0.6698393821716309,0.08457519859075546,-0.7330518960952759,-0.6748940348625183,0.05082937330007553,-0.7330518960952759,-0.6782708168029785,0.016957176849246025,-0.7330518960952759,-0.6799613237380981,-0.016957176849246025,-0.7330518960952759,-0.6799613237380981,-0.05082937330007553,-0.7330518960952759,-0.6782708168029785,-0.08457519859075546,-0.7330518960952759,-0.6748940348625183,-0.11811075359582901,-0.7330518960952759,-0.6698393821716309,-0.1513526737689972,-0.7330518960952759,-0.663119375705719,-0.18421830236911774,-0.7330518960952759,-0.6547507643699646,-0.21662594377994537,-0.7330518960952759,-0.6447543501853943,-0.24849499762058258,-0.7330518960952759,-0.6331549286842346,-0.27974626421928406,-0.7330518960952759,-0.6199814081192017,-0.3103020489215851,-0.7330518960952759,-0.6052665710449219,-0.3400863707065582,-0.7330518960952759,-0.5890468955039978,-0.36902517080307007,-0.7330518960952759,-0.5713627338409424,-0.3970465362071991,-0.7330518960952759,-0.5522580742835999,-0.4240807592868805,-0.7330518960952759,-0.531780481338501,-0.4500606656074524,-0.7330518960952759,-0.5099807381629944,-0.47492164373397827,-0.7330518960952759,-0.48691311478614807,-0.49860191345214844,-0.7330518960952759,-0.46263495087623596,-0.52104252576828,-0.7330518960952759,-0.43720659613609314,-0.5421878099441528,-0.7330518960952759,-0.4106912910938263,-0.5619850754737854,-0.7330518960952759,-0.383154958486557,-0.5803852081298828,-0.7330518960952759,-0.3546660244464874,-0.5973423719406128,-0.7330518960952759,-0.3252953290939331,-0.6128144860267639,-0.7330518960952759,-0.2951158881187439,-0.6267629861831665,-0.7330518960952759,-0.2642027735710144,-0.6391533017158508,-0.7330518960952759,-0.23263277113437653,-0.6499545574188232,-0.7330518960952759,-0.20048443973064423,-0.6591399312019348,-0.7330518960952759,-0.16783764958381653,-0.6666865944862366,-0.7330518960952759,-0.1347736120223999,-0.6725757718086243,-0.7330518960952759,-0.10137448459863663,-0.6767928004264832,-0.7330518960952759,-0.0677233338356018,-0.6793272495269775,-0.7330518960952759,-0.0339038111269474,-0.6801727414131165,-0.7330518960952759,-8.329713400620419e-17,-0.6793272495269775,-0.7330518960952759,0.0339038111269474,-0.6767928004264832,-0.7330518960952759,0.0677233338356018,-0.6725757718086243,-0.7330518960952759,0.10137448459863663,-0.6666865944862366,-0.7330518960952759,0.1347736120223999,-0.6591399312019348,-0.7330518960952759,0.16783764958381653,-0.6499545574188232,-0.7330518960952759,0.20048443973064423,-0.6391533017158508,-0.7330518960952759,0.23263277113437653,-0.6267629861831665,-0.7330518960952759,0.2642027735710144,-0.6128144860267639,-0.7330518960952759,0.2951158881187439,-0.5973423719406128,-0.7330518960952759,0.3252953290939331,-0.5803852081298828,-0.7330518960952759,0.3546660244464874,-0.5619850754737854,-0.7330518960952759,0.383154958486557,-0.5421878099441528,-0.7330518960952759,0.4106912910938263,-0.52104252576828,-0.7330518960952759,0.43720659613609314,-0.49860191345214844,-0.7330518960952759,0.46263495087623596,-0.47492164373397827,-0.7330518960952759,0.48691311478614807,-0.4500606656074524,-0.7330518960952759,0.5099807381629944,-0.4240807592868805,-0.7330518960952759,0.531780481338501,-0.3970465362071991,-0.7330518960952759,0.5522580742835999,-0.36902517080307007,-0.7330518960952759,0.5713627338409424,-0.3400863707065582,-0.7330518960952759,0.5890468955039978,-0.3103020489215851,-0.7330518960952759,0.6052665710449219,-0.27974626421928406,-0.7330518960952759,0.6199814081192017,-0.24849499762058258,-0.7330518960952759,0.6331549286842346,-0.21662594377994537,-0.7330518960952759,0.6447543501853943,-0.18421830236911774,-0.7330518960952759,0.6547507643699646,-0.1513526737689972,-0.7330518960952759,0.663119375705719,-0.11811075359582901,-0.7330518960952759,0.6698393821716309,-0.08457519859075546,-0.7330518960952759,0.6748940348625183,-0.05082937330007553,-0.7330518960952759,0.6782708168029785,-0.016957176849246025,-0.7330518960952759,0.6799613237380981,0.016957176849246025,-0.7330518960952759,0.6799613237380981,0.05082937330007553,-0.7330518960952759,0.6782708168029785,0.08457519859075546,-0.7330518960952759,0.6748940348625183,0.11811075359582901,-0.7330518960952759,0.6698393821716309,0.1513526737689972,-0.7330518960952759,0.663119375705719,0.18421830236911774,-0.7330518960952759,0.6547507643699646,0.21662594377994537,-0.7330518960952759,0.6447543501853943,0.24849499762058258,-0.7330518960952759,0.6331549286842346,0.27974626421928406,-0.7330518960952759,0.6199814081192017,0.3103020489215851,-0.7330518960952759,0.6052665710449219,0.3400863707065582,-0.7330518960952759,0.5890468955039978,0.36902517080307007,-0.7330518960952759,0.5713627338409424,0.3970465362071991,-0.7330518960952759,0.5522580742835999,0.4240807592868805,-0.7330518960952759,0.531780481338501,0.4500606656074524,-0.7330518960952759,0.5099807381629944,0.47492164373397827,-0.7330518960952759,0.48691311478614807,0.49860191345214844,-0.7330518960952759,0.46263495087623596,0.52104252576828,-0.7330518960952759,0.43720659613609314,0.5421878099441528,-0.7330518960952759,0.4106912910938263,0.5619850754737854,-0.7330518960952759,0.383154958486557,0.5803852081298828,-0.7330518960952759,0.3546660244464874,0.5973423719406128,-0.7330518960952759,0.3252953290939331,0.6128144860267639,-0.7330518960952759,0.2951158881187439,0.6267629861831665,-0.7330518960952759,0.2642027735710144,0.6391533017158508,-0.7330518960952759,0.23263277113437653,0.6499545574188232,-0.7330518960952759,0.20048443973064423,0.6591399312019348,-0.7330518960952759,0.16783764958381653,0.6666865944862366,-0.7330518960952759,0.1347736120223999,0.6725757718086243,-0.7330518960952759,0.10137448459863663,0.6767928004264832,-0.7330518960952759,0.0677233338356018,0.6793272495269775,-0.7330518960952759,0.0339038111269474,0.6801727414131165,-0.7330518960952759,1.6659426801240838e-16,0.7158668637275696,-0.6982368230819702,0.0,0.714976966381073,-0.6982368230819702,-0.03568301722407341,0.7123095393180847,-0.6982368230819702,-0.07127732038497925,0.7078711986541748,-0.6982368230819702,-0.10669441521167755,0.7016729712486267,-0.6982368230819702,-0.14184625446796417,0.6937302947044373,-0.6982368230819702,-0.1766454428434372,0.6840628981590271,-0.6982368230819702,-0.21100546419620514,0.6726948022842407,-0.6982368230819702,-0.24484087526798248,0.6596542596817017,-0.6982368230819702,-0.27806758880615234,0.6449737548828125,-0.6982368230819702,-0.31060299277305603,0.628689706325531,-0.6982368230819702,-0.34236615896224976,0.6108426451683044,-0.6982368230819702,-0.3732781708240509,0.5914769768714905,-0.6982368230819702,-0.4032621681690216,0.5706407427787781,-0.6982368230819702,-0.43224355578422546,0.5483857989311218,-0.6982368230819702,-0.4601503312587738,0.5247675180435181,-0.6982368230819702,-0.48691311478614807,0.49984458088874817,-0.6982368230819702,-0.5124653577804565,0.47367894649505615,-0.6982368230819702,-0.5367435216903687,0.44633567333221436,-0.6982368230819702,-0.5596872568130493,0.4178827404975891,-0.6982368230819702,-0.5812395215034485,0.3883908987045288,-0.6982368230819702,-0.6013467311859131,0.3579334318637848,-0.6982368230819702,-0.6199588775634766,0.3265860974788666,-0.6982368230819702,-0.6370297074317932,0.2944267988204956,-0.6982368230819702,-0.6525168418884277,0.26153552532196045,-0.6982368230819702,-0.6663816571235657,0.22799403965473175,-0.6982368230819702,-0.6785897612571716,0.1938857138156891,-0.6982368230819702,-0.6891108155250549,0.15929536521434784,-0.6982368230819702,-0.6979185938835144,0.12430897355079651,-0.6982368230819702,-0.7049912214279175,0.08901353925466537,-0.6982368230819702,-0.7103111743927002,0.053496796637773514,-0.6982368230819702,-0.7138651609420776,0.017847055569291115,-0.6982368230819702,-0.715644359588623,-0.017847055569291115,-0.6982368230819702,-0.715644359588623,-0.053496796637773514,-0.6982368230819702,-0.7138651609420776,-0.08901353925466537,-0.6982368230819702,-0.7103111743927002,-0.12430897355079651,-0.6982368230819702,-0.7049912214279175,-0.15929536521434784,-0.6982368230819702,-0.6979185938835144,-0.1938857138156891,-0.6982368230819702,-0.6891108155250549,-0.22799403965473175,-0.6982368230819702,-0.6785897612571716,-0.26153552532196045,-0.6982368230819702,-0.6663816571235657,-0.2944267988204956,-0.6982368230819702,-0.6525168418884277,-0.3265860974788666,-0.6982368230819702,-0.6370297074317932,-0.3579334318637848,-0.6982368230819702,-0.6199588775634766,-0.3883908987045288,-0.6982368230819702,-0.6013467311859131,-0.4178827404975891,-0.6982368230819702,-0.5812395215034485,-0.44633567333221436,-0.6982368230819702,-0.5596872568130493,-0.47367894649505615,-0.6982368230819702,-0.5367435216903687,-0.49984458088874817,-0.6982368230819702,-0.5124653577804565,-0.5247675180435181,-0.6982368230819702,-0.48691311478614807,-0.5483857989311218,-0.6982368230819702,-0.4601503312587738,-0.5706407427787781,-0.6982368230819702,-0.43224355578422546,-0.5914769768714905,-0.6982368230819702,-0.4032621681690216,-0.6108426451683044,-0.6982368230819702,-0.3732781708240509,-0.628689706325531,-0.6982368230819702,-0.34236615896224976,-0.6449737548828125,-0.6982368230819702,-0.31060299277305603,-0.6596542596817017,-0.6982368230819702,-0.27806758880615234,-0.6726948022842407,-0.6982368230819702,-0.24484087526798248,-0.6840628981590271,-0.6982368230819702,-0.21100546419620514,-0.6937302947044373,-0.6982368230819702,-0.1766454428434372,-0.7016729712486267,-0.6982368230819702,-0.14184625446796417,-0.7078711986541748,-0.6982368230819702,-0.10669441521167755,-0.7123095393180847,-0.6982368230819702,-0.07127732038497925,-0.714976966381073,-0.6982368230819702,-0.03568301722407341,-0.7158668637275696,-0.6982368230819702,-8.766840634918762e-17,-0.714976966381073,-0.6982368230819702,0.03568301722407341,-0.7123095393180847,-0.6982368230819702,0.07127732038497925,-0.7078711986541748,-0.6982368230819702,0.10669441521167755,-0.7016729712486267,-0.6982368230819702,0.14184625446796417,-0.6937302947044373,-0.6982368230819702,0.1766454428434372,-0.6840628981590271,-0.6982368230819702,0.21100546419620514,-0.6726948022842407,-0.6982368230819702,0.24484087526798248,-0.6596542596817017,-0.6982368230819702,0.27806758880615234,-0.6449737548828125,-0.6982368230819702,0.31060299277305603,-0.628689706325531,-0.6982368230819702,0.34236615896224976,-0.6108426451683044,-0.6982368230819702,0.3732781708240509,-0.5914769768714905,-0.6982368230819702,0.4032621681690216,-0.5706407427787781,-0.6982368230819702,0.43224355578422546,-0.5483857989311218,-0.6982368230819702,0.4601503312587738,-0.5247675180435181,-0.6982368230819702,0.48691311478614807,-0.49984458088874817,-0.6982368230819702,0.5124653577804565,-0.47367894649505615,-0.6982368230819702,0.5367435216903687,-0.44633567333221436,-0.6982368230819702,0.5596872568130493,-0.4178827404975891,-0.6982368230819702,0.5812395215034485,-0.3883908987045288,-0.6982368230819702,0.6013467311859131,-0.3579334318637848,-0.6982368230819702,0.6199588775634766,-0.3265860974788666,-0.6982368230819702,0.6370297074317932,-0.2944267988204956,-0.6982368230819702,0.6525168418884277,-0.26153552532196045,-0.6982368230819702,0.6663816571235657,-0.22799403965473175,-0.6982368230819702,0.6785897612571716,-0.1938857138156891,-0.6982368230819702,0.6891108155250549,-0.15929536521434784,-0.6982368230819702,0.6979185938835144,-0.12430897355079651,-0.6982368230819702,0.7049912214279175,-0.08901353925466537,-0.6982368230819702,0.7103111743927002,-0.053496796637773514,-0.6982368230819702,0.7138651609420776,-0.017847055569291115,-0.6982368230819702,0.715644359588623,0.017847055569291115,-0.6982368230819702,0.715644359588623,0.053496796637773514,-0.6982368230819702,0.7138651609420776,0.08901353925466537,-0.6982368230819702,0.7103111743927002,0.12430897355079651,-0.6982368230819702,0.7049912214279175,0.15929536521434784,-0.6982368230819702,0.6979185938835144,0.1938857138156891,-0.6982368230819702,0.6891108155250549,0.22799403965473175,-0.6982368230819702,0.6785897612571716,0.26153552532196045,-0.6982368230819702,0.6663816571235657,0.2944267988204956,-0.6982368230819702,0.6525168418884277,0.3265860974788666,-0.6982368230819702,0.6370297074317932,0.3579334318637848,-0.6982368230819702,0.6199588775634766,0.3883908987045288,-0.6982368230819702,0.6013467311859131,0.4178827404975891,-0.6982368230819702,0.5812395215034485,0.44633567333221436,-0.6982368230819702,0.5596872568130493,0.47367894649505615,-0.6982368230819702,0.5367435216903687,0.49984458088874817,-0.6982368230819702,0.5124653577804565,0.5247675180435181,-0.6982368230819702,0.48691311478614807,0.5483857989311218,-0.6982368230819702,0.4601503312587738,0.5706407427787781,-0.6982368230819702,0.43224355578422546,0.5914769768714905,-0.6982368230819702,0.4032621681690216,0.6108426451683044,-0.6982368230819702,0.3732781708240509,0.628689706325531,-0.6982368230819702,0.34236615896224976,0.6449737548828125,-0.6982368230819702,0.31060299277305603,0.6596542596817017,-0.6982368230819702,0.27806758880615234,0.6726948022842407,-0.6982368230819702,0.24484087526798248,0.6840628981590271,-0.6982368230819702,0.21100546419620514,0.6937302947044373,-0.6982368230819702,0.1766454428434372,0.7016729712486267,-0.6982368230819702,0.14184625446796417,0.7078711986541748,-0.6982368230819702,0.10669441521167755,0.7123095393180847,-0.6982368230819702,0.07127732038497925,0.714976966381073,-0.6982368230819702,0.03568301722407341,0.7158668637275696,-0.6982368230819702,1.7533681269837523e-16,0.7497811913490295,-0.6616858243942261,0.0,0.7488491535186768,-0.6616858243942261,-0.037373509258031845,0.7460553646087646,-0.6616858243942261,-0.07465410232543945,0.7414067387580872,-0.6616858243942261,-0.1117490902543068,0.7349148988723755,-0.6616858243942261,-0.14856626093387604,0.726595938205719,-0.6616858243942261,-0.1850140541791916,0.7164705395698547,-0.6616858243942261,-0.22100189328193665,0.7045638561248779,-0.6616858243942261,-0.25644028186798096,0.6909055709838867,-0.6616858243942261,-0.2912411093711853,0.6755295395851135,-0.6616858243942261,-0.3253178596496582,0.6584740281105042,-0.6616858243942261,-0.35858583450317383,0.6397814750671387,-0.6616858243942261,-0.39096230268478394,0.6194983124732971,-0.6616858243942261,-0.42236679792404175,0.5976749658584595,-0.6616858243942261,-0.4527212083339691,0.5743657350540161,-0.6616858243942261,-0.4819500744342804,0.549628496170044,-0.6616858243942261,-0.5099807381629944,0.523524820804596,-0.6616858243942261,-0.5367435216903687,0.49611958861351013,-0.6616858243942261,-0.5621718764305115,0.46748092770576477,-0.6616858243942261,-0.5862025618553162,0.43768003582954407,-0.6616858243942261,-0.6087758541107178,0.40679100155830383,-0.6616858243942261,-0.6298356652259827,0.37489059567451477,-0.6616858243942261,-0.6493295431137085,0.3420581817626953,-0.6616858243942261,-0.6672091484069824,0.3083753287792206,-0.6616858243942261,-0.6834299564361572,0.2739258408546448,-0.6616858243942261,-0.69795161485672,0.23879531025886536,-0.6616858243942261,-0.7107381224632263,0.20307108759880066,-0.6616858243942261,-0.7217575907707214,0.1668420135974884,-0.6616858243942261,-0.7309826016426086,0.130198135972023,-0.6616858243942261,-0.738390326499939,0.09323057532310486,-0.6616858243942261,-0.743962287902832,0.05603121966123581,-0.6616858243942261,-0.7476846575737,0.018692564219236374,-0.6616858243942261,-0.7495481371879578,-0.018692564219236374,-0.6616858243942261,-0.7495481371879578,-0.05603121966123581,-0.6616858243942261,-0.7476846575737,-0.09323057532310486,-0.6616858243942261,-0.743962287902832,-0.130198135972023,-0.6616858243942261,-0.738390326499939,-0.1668420135974884,-0.6616858243942261,-0.7309826016426086,-0.20307108759880066,-0.6616858243942261,-0.7217575907707214,-0.23879531025886536,-0.6616858243942261,-0.7107381224632263,-0.2739258408546448,-0.6616858243942261,-0.69795161485672,-0.3083753287792206,-0.6616858243942261,-0.6834299564361572,-0.3420581817626953,-0.6616858243942261,-0.6672091484069824,-0.37489059567451477,-0.6616858243942261,-0.6493295431137085,-0.40679100155830383,-0.6616858243942261,-0.6298356652259827,-0.43768003582954407,-0.6616858243942261,-0.6087758541107178,-0.46748092770576477,-0.6616858243942261,-0.5862025618553162,-0.49611958861351013,-0.6616858243942261,-0.5621718764305115,-0.523524820804596,-0.6616858243942261,-0.5367435216903687,-0.549628496170044,-0.6616858243942261,-0.5099807381629944,-0.5743657350540161,-0.6616858243942261,-0.4819500744342804,-0.5976749658584595,-0.6616858243942261,-0.4527212083339691,-0.6194983124732971,-0.6616858243942261,-0.42236679792404175,-0.6397814750671387,-0.6616858243942261,-0.39096230268478394,-0.6584740281105042,-0.6616858243942261,-0.35858583450317383,-0.6755295395851135,-0.6616858243942261,-0.3253178596496582,-0.6909055709838867,-0.6616858243942261,-0.2912411093711853,-0.7045638561248779,-0.6616858243942261,-0.25644028186798096,-0.7164705395698547,-0.6616858243942261,-0.22100189328193665,-0.726595938205719,-0.6616858243942261,-0.1850140541791916,-0.7349148988723755,-0.6616858243942261,-0.14856626093387604,-0.7414067387580872,-0.6616858243942261,-0.1117490902543068,-0.7460553646087646,-0.6616858243942261,-0.07465410232543945,-0.7488491535186768,-0.6616858243942261,-0.037373509258031845,-0.7497811913490295,-0.6616858243942261,-9.182171329204087e-17,-0.7488491535186768,-0.6616858243942261,0.037373509258031845,-0.7460553646087646,-0.6616858243942261,0.07465410232543945,-0.7414067387580872,-0.6616858243942261,0.1117490902543068,-0.7349148988723755,-0.6616858243942261,0.14856626093387604,-0.726595938205719,-0.6616858243942261,0.1850140541791916,-0.7164705395698547,-0.6616858243942261,0.22100189328193665,-0.7045638561248779,-0.6616858243942261,0.25644028186798096,-0.6909055709838867,-0.6616858243942261,0.2912411093711853,-0.6755295395851135,-0.6616858243942261,0.3253178596496582,-0.6584740281105042,-0.6616858243942261,0.35858583450317383,-0.6397814750671387,-0.6616858243942261,0.39096230268478394,-0.6194983124732971,-0.6616858243942261,0.42236679792404175,-0.5976749658584595,-0.6616858243942261,0.4527212083339691,-0.5743657350540161,-0.6616858243942261,0.4819500744342804,-0.549628496170044,-0.6616858243942261,0.5099807381629944,-0.523524820804596,-0.6616858243942261,0.5367435216903687,-0.49611958861351013,-0.6616858243942261,0.5621718764305115,-0.46748092770576477,-0.6616858243942261,0.5862025618553162,-0.43768003582954407,-0.6616858243942261,0.6087758541107178,-0.40679100155830383,-0.6616858243942261,0.6298356652259827,-0.37489059567451477,-0.6616858243942261,0.6493295431137085,-0.3420581817626953,-0.6616858243942261,0.6672091484069824,-0.3083753287792206,-0.6616858243942261,0.6834299564361572,-0.2739258408546448,-0.6616858243942261,0.69795161485672,-0.23879531025886536,-0.6616858243942261,0.7107381224632263,-0.20307108759880066,-0.6616858243942261,0.7217575907707214,-0.1668420135974884,-0.6616858243942261,0.7309826016426086,-0.130198135972023,-0.6616858243942261,0.738390326499939,-0.09323057532310486,-0.6616858243942261,0.743962287902832,-0.05603121966123581,-0.6616858243942261,0.7476846575737,-0.018692564219236374,-0.6616858243942261,0.7495481371879578,0.018692564219236374,-0.6616858243942261,0.7495481371879578,0.05603121966123581,-0.6616858243942261,0.7476846575737,0.09323057532310486,-0.6616858243942261,0.743962287902832,0.130198135972023,-0.6616858243942261,0.738390326499939,0.1668420135974884,-0.6616858243942261,0.7309826016426086,0.20307108759880066,-0.6616858243942261,0.7217575907707214,0.23879531025886536,-0.6616858243942261,0.7107381224632263,0.2739258408546448,-0.6616858243942261,0.69795161485672,0.3083753287792206,-0.6616858243942261,0.6834299564361572,0.3420581817626953,-0.6616858243942261,0.6672091484069824,0.37489059567451477,-0.6616858243942261,0.6493295431137085,0.40679100155830383,-0.6616858243942261,0.6298356652259827,0.43768003582954407,-0.6616858243942261,0.6087758541107178,0.46748092770576477,-0.6616858243942261,0.5862025618553162,0.49611958861351013,-0.6616858243942261,0.5621718764305115,0.523524820804596,-0.6616858243942261,0.5367435216903687,0.549628496170044,-0.6616858243942261,0.5099807381629944,0.5743657350540161,-0.6616858243942261,0.4819500744342804,0.5976749658584595,-0.6616858243942261,0.4527212083339691,0.6194983124732971,-0.6616858243942261,0.42236679792404175,0.6397814750671387,-0.6616858243942261,0.39096230268478394,0.6584740281105042,-0.6616858243942261,0.35858583450317383,0.6755295395851135,-0.6616858243942261,0.3253178596496582,0.6909055709838867,-0.6616858243942261,0.2912411093711853,0.7045638561248779,-0.6616858243942261,0.25644028186798096,0.7164705395698547,-0.6616858243942261,0.22100189328193665,0.726595938205719,-0.6616858243942261,0.1850140541791916,0.7349148988723755,-0.6616858243942261,0.14856626093387604,0.7414067387580872,-0.6616858243942261,0.1117490902543068,0.7460553646087646,-0.6616858243942261,0.07465410232543945,0.7488491535186768,-0.6616858243942261,0.037373509258031845,0.7497811913490295,-0.6616858243942261,1.8364342658408174e-16,0.7818315029144287,-0.6234897971153259,0.0,0.7808595895767212,-0.6234897971153259,-0.038971081376075745,0.777946412563324,-0.6234897971153259,-0.07784527540206909,0.7730990648269653,-0.6234897971153259,-0.11652593314647675,0.7663297057151794,-0.6234897971153259,-0.1549168974161148,0.757655143737793,-0.6234897971153259,-0.1929226964712143,0.7470968961715698,-0.6234897971153259,-0.2304488718509674,0.734681248664856,-0.6234897971153259,-0.26740211248397827,0.7204391360282898,-0.6234897971153259,-0.3036905527114868,0.7044058442115784,-0.6234897971153259,-0.3392239809036255,0.6866212487220764,-0.6234897971153259,-0.3739140033721924,0.6671296954154968,-0.6234897971153259,-0.4076744616031647,0.6459794640541077,-0.6234897971153259,-0.44042134284973145,0.6232233047485352,-0.6234897971153259,-0.4720732867717743,0.5989176630973816,-0.6234897971153259,-0.5025516152381897,0.573123037815094,-0.6234897971153259,-0.531780481338501,0.5459035038948059,-0.6234897971153259,-0.5596872568130493,0.517326831817627,-0.6234897971153259,-0.5862025618553162,0.48746395111083984,-0.6234897971153259,-0.6112604737281799,0.4563891887664795,-0.6234897971153259,-0.6347987055778503,0.4241797626018524,-0.6234897971153259,-0.656758725643158,0.39091575145721436,-0.6234897971153259,-0.6770859360694885,0.35667985677719116,-0.6234897971153259,-0.6957297921180725,0.3215571939945221,-0.6234897971153259,-0.7126439809799194,0.2856351137161255,-0.6234897971153259,-0.7277864217758179,0.24900288879871368,-0.6234897971153259,-0.7411194443702698,0.21175161004066467,-0.6234897971153259,-0.7526099681854248,0.17397387325763702,-0.6234897971153259,-0.762229323387146,0.13576361536979675,-0.6234897971153259,-0.769953727722168,0.09721582382917404,-0.6234897971153259,-0.7757638692855835,0.05842633917927742,-0.6234897971153259,-0.7796453237533569,0.01949159987270832,-0.6234897971153259,-0.7815884947776794,-0.01949159987270832,-0.6234897971153259,-0.7815884947776794,-0.05842633917927742,-0.6234897971153259,-0.7796453237533569,-0.09721582382917404,-0.6234897971153259,-0.7757638692855835,-0.13576361536979675,-0.6234897971153259,-0.769953727722168,-0.17397387325763702,-0.6234897971153259,-0.762229323387146,-0.21175161004066467,-0.6234897971153259,-0.7526099681854248,-0.24900288879871368,-0.6234897971153259,-0.7411194443702698,-0.2856351137161255,-0.6234897971153259,-0.7277864217758179,-0.3215571939945221,-0.6234897971153259,-0.7126439809799194,-0.35667985677719116,-0.6234897971153259,-0.6957297921180725,-0.39091575145721436,-0.6234897971153259,-0.6770859360694885,-0.4241797626018524,-0.6234897971153259,-0.656758725643158,-0.4563891887664795,-0.6234897971153259,-0.6347987055778503,-0.48746395111083984,-0.6234897971153259,-0.6112604737281799,-0.517326831817627,-0.6234897971153259,-0.5862025618553162,-0.5459035038948059,-0.6234897971153259,-0.5596872568130493,-0.573123037815094,-0.6234897971153259,-0.531780481338501,-0.5989176630973816,-0.6234897971153259,-0.5025516152381897,-0.6232233047485352,-0.6234897971153259,-0.4720732867717743,-0.6459794640541077,-0.6234897971153259,-0.44042134284973145,-0.6671296954154968,-0.6234897971153259,-0.4076744616031647,-0.6866212487220764,-0.6234897971153259,-0.3739140033721924,-0.7044058442115784,-0.6234897971153259,-0.3392239809036255,-0.7204391360282898,-0.6234897971153259,-0.3036905527114868,-0.734681248664856,-0.6234897971153259,-0.26740211248397827,-0.7470968961715698,-0.6234897971153259,-0.2304488718509674,-0.757655143737793,-0.6234897971153259,-0.1929226964712143,-0.7663297057151794,-0.6234897971153259,-0.1549168974161148,-0.7730990648269653,-0.6234897971153259,-0.11652593314647675,-0.777946412563324,-0.6234897971153259,-0.07784527540206909,-0.7808595895767212,-0.6234897971153259,-0.038971081376075745,-0.7818315029144287,-0.6234897971153259,-9.574674485560909e-17,-0.7808595895767212,-0.6234897971153259,0.038971081376075745,-0.777946412563324,-0.6234897971153259,0.07784527540206909,-0.7730990648269653,-0.6234897971153259,0.11652593314647675,-0.7663297057151794,-0.6234897971153259,0.1549168974161148,-0.757655143737793,-0.6234897971153259,0.1929226964712143,-0.7470968961715698,-0.6234897971153259,0.2304488718509674,-0.734681248664856,-0.6234897971153259,0.26740211248397827,-0.7204391360282898,-0.6234897971153259,0.3036905527114868,-0.7044058442115784,-0.6234897971153259,0.3392239809036255,-0.6866212487220764,-0.6234897971153259,0.3739140033721924,-0.6671296954154968,-0.6234897971153259,0.4076744616031647,-0.6459794640541077,-0.6234897971153259,0.44042134284973145,-0.6232233047485352,-0.6234897971153259,0.4720732867717743,-0.5989176630973816,-0.6234897971153259,0.5025516152381897,-0.573123037815094,-0.6234897971153259,0.531780481338501,-0.5459035038948059,-0.6234897971153259,0.5596872568130493,-0.517326831817627,-0.6234897971153259,0.5862025618553162,-0.48746395111083984,-0.6234897971153259,0.6112604737281799,-0.4563891887664795,-0.6234897971153259,0.6347987055778503,-0.4241797626018524,-0.6234897971153259,0.656758725643158,-0.39091575145721436,-0.6234897971153259,0.6770859360694885,-0.35667985677719116,-0.6234897971153259,0.6957297921180725,-0.3215571939945221,-0.6234897971153259,0.7126439809799194,-0.2856351137161255,-0.6234897971153259,0.7277864217758179,-0.24900288879871368,-0.6234897971153259,0.7411194443702698,-0.21175161004066467,-0.6234897971153259,0.7526099681854248,-0.17397387325763702,-0.6234897971153259,0.762229323387146,-0.13576361536979675,-0.6234897971153259,0.769953727722168,-0.09721582382917404,-0.6234897971153259,0.7757638692855835,-0.05842633917927742,-0.6234897971153259,0.7796453237533569,-0.01949159987270832,-0.6234897971153259,0.7815884947776794,0.01949159987270832,-0.6234897971153259,0.7815884947776794,0.05842633917927742,-0.6234897971153259,0.7796453237533569,0.09721582382917404,-0.6234897971153259,0.7757638692855835,0.13576361536979675,-0.6234897971153259,0.769953727722168,0.17397387325763702,-0.6234897971153259,0.762229323387146,0.21175161004066467,-0.6234897971153259,0.7526099681854248,0.24900288879871368,-0.6234897971153259,0.7411194443702698,0.2856351137161255,-0.6234897971153259,0.7277864217758179,0.3215571939945221,-0.6234897971153259,0.7126439809799194,0.35667985677719116,-0.6234897971153259,0.6957297921180725,0.39091575145721436,-0.6234897971153259,0.6770859360694885,0.4241797626018524,-0.6234897971153259,0.656758725643158,0.4563891887664795,-0.6234897971153259,0.6347987055778503,0.48746395111083984,-0.6234897971153259,0.6112604737281799,0.517326831817627,-0.6234897971153259,0.5862025618553162,0.5459035038948059,-0.6234897971153259,0.5596872568130493,0.573123037815094,-0.6234897971153259,0.531780481338501,0.5989176630973816,-0.6234897971153259,0.5025516152381897,0.6232233047485352,-0.6234897971153259,0.4720732867717743,0.6459794640541077,-0.6234897971153259,0.44042134284973145,0.6671296954154968,-0.6234897971153259,0.4076744616031647,0.6866212487220764,-0.6234897971153259,0.3739140033721924,0.7044058442115784,-0.6234897971153259,0.3392239809036255,0.7204391360282898,-0.6234897971153259,0.3036905527114868,0.734681248664856,-0.6234897971153259,0.26740211248397827,0.7470968961715698,-0.6234897971153259,0.2304488718509674,0.757655143737793,-0.6234897971153259,0.1929226964712143,0.7663297057151794,-0.6234897971153259,0.1549168974161148,0.7730990648269653,-0.6234897971153259,0.11652593314647675,0.777946412563324,-0.6234897971153259,0.07784527540206909,0.7808595895767212,-0.6234897971153259,0.038971081376075745,0.7818315029144287,-0.6234897971153259,1.9149348971121818e-16,0.8119379878044128,-0.583743691444397,0.0,0.8109287023544312,-0.583743691444397,-0.040471769869327545,0.8079032897949219,-0.583743691444397,-0.08084291964769363,0.8028693199157715,-0.583743691444397,-0.1210130825638771,0.7958393096923828,-0.583743691444397,-0.1608823835849762,0.7868306636810303,-0.583743691444397,-0.20035171508789062,0.7758658528327942,-0.583743691444397,-0.23932293057441711,0.7629721760749817,-0.583743691444397,-0.27769914269447327,0.7481815814971924,-0.583743691444397,-0.31538498401641846,0.7315308451652527,-0.583743691444397,-0.352286696434021,0.7130614519119263,-0.583743691444397,-0.38831257820129395,0.6928192973136902,-0.583743691444397,-0.4233730733394623,0.6708546876907349,-0.583743691444397,-0.4573809504508972,0.6472221612930298,-0.583743691444397,-0.490251749753952,0.621980607509613,-0.583743691444397,-0.5219036936759949,0.5951926708221436,-0.583743691444397,-0.5522580742835999,0.5669249892234802,-0.583743691444397,-0.5812395215034485,0.537247896194458,-0.583743691444397,-0.6087758541107178,0.5062350630760193,-0.583743691444397,-0.6347987055778503,0.4739636778831482,-0.583743691444397,-0.6592433452606201,0.44051393866539,-0.583743691444397,-0.6820489764213562,0.4059689939022064,-0.583743691444397,-0.7031589150428772,0.37041476368904114,-0.583743691444397,-0.7225207686424255,0.33393964171409607,-0.583743691444397,-0.7400862574577332,0.2966342568397522,-0.583743691444397,-0.7558117508888245,0.2585914134979248,-0.583743691444397,-0.7696582674980164,0.21990567445755005,-0.583743691444397,-0.7815912365913391,0.18067319691181183,-0.583743691444397,-0.7915810346603394,0.14099155366420746,-0.583743691444397,-0.7996028661727905,0.10095938295125961,-0.583743691444397,-0.8056367039680481,0.060676202178001404,-0.583743691444397,-0.8096676468849182,0.020242176949977875,-0.583743691444397,-0.8116856217384338,-0.020242176949977875,-0.583743691444397,-0.8116856217384338,-0.060676202178001404,-0.583743691444397,-0.8096676468849182,-0.10095938295125961,-0.583743691444397,-0.8056367039680481,-0.14099155366420746,-0.583743691444397,-0.7996028661727905,-0.18067319691181183,-0.583743691444397,-0.7915810346603394,-0.21990567445755005,-0.583743691444397,-0.7815912365913391,-0.2585914134979248,-0.583743691444397,-0.7696582674980164,-0.2966342568397522,-0.583743691444397,-0.7558117508888245,-0.33393964171409607,-0.583743691444397,-0.7400862574577332,-0.37041476368904114,-0.583743691444397,-0.7225207686424255,-0.4059689939022064,-0.583743691444397,-0.7031589150428772,-0.44051393866539,-0.583743691444397,-0.6820489764213562,-0.4739636778831482,-0.583743691444397,-0.6592433452606201,-0.5062350630760193,-0.583743691444397,-0.6347987055778503,-0.537247896194458,-0.583743691444397,-0.6087758541107178,-0.5669249892234802,-0.583743691444397,-0.5812395215034485,-0.5951926708221436,-0.583743691444397,-0.5522580742835999,-0.621980607509613,-0.583743691444397,-0.5219036936759949,-0.6472221612930298,-0.583743691444397,-0.490251749753952,-0.6708546876907349,-0.583743691444397,-0.4573809504508972,-0.6928192973136902,-0.583743691444397,-0.4233730733394623,-0.7130614519119263,-0.583743691444397,-0.38831257820129395,-0.7315308451652527,-0.583743691444397,-0.352286696434021,-0.7481815814971924,-0.583743691444397,-0.31538498401641846,-0.7629721760749817,-0.583743691444397,-0.27769914269447327,-0.7758658528327942,-0.583743691444397,-0.23932293057441711,-0.7868306636810303,-0.583743691444397,-0.20035171508789062,-0.7958393096923828,-0.583743691444397,-0.1608823835849762,-0.8028693199157715,-0.583743691444397,-0.1210130825638771,-0.8079032897949219,-0.583743691444397,-0.08084291964769363,-0.8109287023544312,-0.583743691444397,-0.040471769869327545,-0.8119379878044128,-0.583743691444397,-9.943372707377435e-17,-0.8109287023544312,-0.583743691444397,0.040471769869327545,-0.8079032897949219,-0.583743691444397,0.08084291964769363,-0.8028693199157715,-0.583743691444397,0.1210130825638771,-0.7958393096923828,-0.583743691444397,0.1608823835849762,-0.7868306636810303,-0.583743691444397,0.20035171508789062,-0.7758658528327942,-0.583743691444397,0.23932293057441711,-0.7629721760749817,-0.583743691444397,0.27769914269447327,-0.7481815814971924,-0.583743691444397,0.31538498401641846,-0.7315308451652527,-0.583743691444397,0.352286696434021,-0.7130614519119263,-0.583743691444397,0.38831257820129395,-0.6928192973136902,-0.583743691444397,0.4233730733394623,-0.6708546876907349,-0.583743691444397,0.4573809504508972,-0.6472221612930298,-0.583743691444397,0.490251749753952,-0.621980607509613,-0.583743691444397,0.5219036936759949,-0.5951926708221436,-0.583743691444397,0.5522580742835999,-0.5669249892234802,-0.583743691444397,0.5812395215034485,-0.537247896194458,-0.583743691444397,0.6087758541107178,-0.5062350630760193,-0.583743691444397,0.6347987055778503,-0.4739636778831482,-0.583743691444397,0.6592433452606201,-0.44051393866539,-0.583743691444397,0.6820489764213562,-0.4059689939022064,-0.583743691444397,0.7031589150428772,-0.37041476368904114,-0.583743691444397,0.7225207686424255,-0.33393964171409607,-0.583743691444397,0.7400862574577332,-0.2966342568397522,-0.583743691444397,0.7558117508888245,-0.2585914134979248,-0.583743691444397,0.7696582674980164,-0.21990567445755005,-0.583743691444397,0.7815912365913391,-0.18067319691181183,-0.583743691444397,0.7915810346603394,-0.14099155366420746,-0.583743691444397,0.7996028661727905,-0.10095938295125961,-0.583743691444397,0.8056367039680481,-0.060676202178001404,-0.583743691444397,0.8096676468849182,-0.020242176949977875,-0.583743691444397,0.8116856217384338,0.020242176949977875,-0.583743691444397,0.8116856217384338,0.060676202178001404,-0.583743691444397,0.8096676468849182,0.10095938295125961,-0.583743691444397,0.8056367039680481,0.14099155366420746,-0.583743691444397,0.7996028661727905,0.18067319691181183,-0.583743691444397,0.7915810346603394,0.21990567445755005,-0.583743691444397,0.7815912365913391,0.2585914134979248,-0.583743691444397,0.7696582674980164,0.2966342568397522,-0.583743691444397,0.7558117508888245,0.33393964171409607,-0.583743691444397,0.7400862574577332,0.37041476368904114,-0.583743691444397,0.7225207686424255,0.4059689939022064,-0.583743691444397,0.7031589150428772,0.44051393866539,-0.583743691444397,0.6820489764213562,0.4739636778831482,-0.583743691444397,0.6592433452606201,0.5062350630760193,-0.583743691444397,0.6347987055778503,0.537247896194458,-0.583743691444397,0.6087758541107178,0.5669249892234802,-0.583743691444397,0.5812395215034485,0.5951926708221436,-0.583743691444397,0.5522580742835999,0.621980607509613,-0.583743691444397,0.5219036936759949,0.6472221612930298,-0.583743691444397,0.490251749753952,0.6708546876907349,-0.583743691444397,0.4573809504508972,0.6928192973136902,-0.583743691444397,0.4233730733394623,0.7130614519119263,-0.583743691444397,0.38831257820129395,0.7315308451652527,-0.583743691444397,0.352286696434021,0.7481815814971924,-0.583743691444397,0.31538498401641846,0.7629721760749817,-0.583743691444397,0.27769914269447327,0.7758658528327942,-0.583743691444397,0.23932293057441711,0.7868306636810303,-0.583743691444397,0.20035171508789062,0.7958393096923828,-0.583743691444397,0.1608823835849762,0.8028693199157715,-0.583743691444397,0.1210130825638771,0.8079032897949219,-0.583743691444397,0.08084291964769363,0.8109287023544312,-0.583743691444397,0.040471769869327545,0.8119379878044128,-0.583743691444397,1.988674541475487e-16,0.8400259017944336,-0.542546272277832,0.0,0.8389816880226135,-0.542546272277832,-0.04187183454632759,0.8358516693115234,-0.542546272277832,-0.08363956958055496,0.8306435346603394,-0.542546272277832,-0.1251993626356125,0.8233702778816223,-0.542546272277832,-0.16644789278507233,0.814050018787384,-0.542546272277832,-0.20728261768817902,0.8027059435844421,-0.542546272277832,-0.24760198593139648,0.7893661856651306,-0.542546272277832,-0.2873057723045349,0.7740638852119446,-0.542546272277832,-0.32629528641700745,0.7568371891975403,-0.542546272277832,-0.3644735813140869,0.7377288937568665,-0.542546272277832,-0.4017457365989685,0.7167864441871643,-0.542546272277832,-0.4380190968513489,0.6940619945526123,-0.542546272277832,-0.4732034504413605,0.6696119904518127,-0.542546272277832,-0.5072113275527954,0.6434971690177917,-0.542546272277832,-0.5399582386016846,0.6157825589179993,-0.542546272277832,-0.5713627338409424,0.5865370035171509,-0.542546272277832,-0.6013467311859131,0.5558332800865173,-0.542546272277832,-0.6298356652259827,0.5237476229667664,-0.542546272277832,-0.656758725643158,0.4903598129749298,-0.542546272277832,-0.6820489764213562,0.4557529389858246,-0.542546272277832,-0.7056435346603394,0.4200129508972168,-0.542546272277832,-0.7274838089942932,0.38322877883911133,-0.542546272277832,-0.7475153803825378,0.34549182653427124,-0.542546272277832,-0.7656885385513306,0.3068959414958954,-0.542546272277832,-0.7819581031799316,0.26753702759742737,-0.542546272277832,-0.7962835431098938,0.22751301527023315,-0.542546272277832,-0.8086293339729309,0.18692335486412048,-0.542546272277832,-0.8189647197723389,0.14586897194385529,-0.542546272277832,-0.8272640705108643,0.10445193201303482,-0.542546272277832,-0.8335066437721252,0.06277521699666977,-0.542546272277832,-0.8376770615577698,0.020942427217960358,-0.542546272277832,-0.8397648334503174,-0.020942427217960358,-0.542546272277832,-0.8397648334503174,-0.06277521699666977,-0.542546272277832,-0.8376770615577698,-0.10445193201303482,-0.542546272277832,-0.8335066437721252,-0.14586897194385529,-0.542546272277832,-0.8272640705108643,-0.18692335486412048,-0.542546272277832,-0.8189647197723389,-0.22751301527023315,-0.542546272277832,-0.8086293339729309,-0.26753702759742737,-0.542546272277832,-0.7962835431098938,-0.3068959414958954,-0.542546272277832,-0.7819581031799316,-0.34549182653427124,-0.542546272277832,-0.7656885385513306,-0.38322877883911133,-0.542546272277832,-0.7475153803825378,-0.4200129508972168,-0.542546272277832,-0.7274838089942932,-0.4557529389858246,-0.542546272277832,-0.7056435346603394,-0.4903598129749298,-0.542546272277832,-0.6820489764213562,-0.5237476229667664,-0.542546272277832,-0.656758725643158,-0.5558332800865173,-0.542546272277832,-0.6298356652259827,-0.5865370035171509,-0.542546272277832,-0.6013467311859131,-0.6157825589179993,-0.542546272277832,-0.5713627338409424,-0.6434971690177917,-0.542546272277832,-0.5399582386016846,-0.6696119904518127,-0.542546272277832,-0.5072113275527954,-0.6940619945526123,-0.542546272277832,-0.4732034504413605,-0.7167864441871643,-0.542546272277832,-0.4380190968513489,-0.7377288937568665,-0.542546272277832,-0.4017457365989685,-0.7568371891975403,-0.542546272277832,-0.3644735813140869,-0.7740638852119446,-0.542546272277832,-0.32629528641700745,-0.7893661856651306,-0.542546272277832,-0.2873057723045349,-0.8027059435844421,-0.542546272277832,-0.24760198593139648,-0.814050018787384,-0.542546272277832,-0.20728261768817902,-0.8233702778816223,-0.542546272277832,-0.16644789278507233,-0.8306435346603394,-0.542546272277832,-0.1251993626356125,-0.8358516693115234,-0.542546272277832,-0.08363956958055496,-0.8389816880226135,-0.542546272277832,-0.04187183454632759,-0.8400259017944336,-0.542546272277832,-1.0287350802023936e-16,-0.8389816880226135,-0.542546272277832,0.04187183454632759,-0.8358516693115234,-0.542546272277832,0.08363956958055496,-0.8306435346603394,-0.542546272277832,0.1251993626356125,-0.8233702778816223,-0.542546272277832,0.16644789278507233,-0.814050018787384,-0.542546272277832,0.20728261768817902,-0.8027059435844421,-0.542546272277832,0.24760198593139648,-0.7893661856651306,-0.542546272277832,0.2873057723045349,-0.7740638852119446,-0.542546272277832,0.32629528641700745,-0.7568371891975403,-0.542546272277832,0.3644735813140869,-0.7377288937568665,-0.542546272277832,0.4017457365989685,-0.7167864441871643,-0.542546272277832,0.4380190968513489,-0.6940619945526123,-0.542546272277832,0.4732034504413605,-0.6696119904518127,-0.542546272277832,0.5072113275527954,-0.6434971690177917,-0.542546272277832,0.5399582386016846,-0.6157825589179993,-0.542546272277832,0.5713627338409424,-0.5865370035171509,-0.542546272277832,0.6013467311859131,-0.5558332800865173,-0.542546272277832,0.6298356652259827,-0.5237476229667664,-0.542546272277832,0.656758725643158,-0.4903598129749298,-0.542546272277832,0.6820489764213562,-0.4557529389858246,-0.542546272277832,0.7056435346603394,-0.4200129508972168,-0.542546272277832,0.7274838089942932,-0.38322877883911133,-0.542546272277832,0.7475153803825378,-0.34549182653427124,-0.542546272277832,0.7656885385513306,-0.3068959414958954,-0.542546272277832,0.7819581031799316,-0.26753702759742737,-0.542546272277832,0.7962835431098938,-0.22751301527023315,-0.542546272277832,0.8086293339729309,-0.18692335486412048,-0.542546272277832,0.8189647197723389,-0.14586897194385529,-0.542546272277832,0.8272640705108643,-0.10445193201303482,-0.542546272277832,0.8335066437721252,-0.06277521699666977,-0.542546272277832,0.8376770615577698,-0.020942427217960358,-0.542546272277832,0.8397648334503174,0.020942427217960358,-0.542546272277832,0.8397648334503174,0.06277521699666977,-0.542546272277832,0.8376770615577698,0.10445193201303482,-0.542546272277832,0.8335066437721252,0.14586897194385529,-0.542546272277832,0.8272640705108643,0.18692335486412048,-0.542546272277832,0.8189647197723389,0.22751301527023315,-0.542546272277832,0.8086293339729309,0.26753702759742737,-0.542546272277832,0.7962835431098938,0.3068959414958954,-0.542546272277832,0.7819581031799316,0.34549182653427124,-0.542546272277832,0.7656885385513306,0.38322877883911133,-0.542546272277832,0.7475153803825378,0.4200129508972168,-0.542546272277832,0.7274838089942932,0.4557529389858246,-0.542546272277832,0.7056435346603394,0.4903598129749298,-0.542546272277832,0.6820489764213562,0.5237476229667664,-0.542546272277832,0.656758725643158,0.5558332800865173,-0.542546272277832,0.6298356652259827,0.5865370035171509,-0.542546272277832,0.6013467311859131,0.6157825589179993,-0.542546272277832,0.5713627338409424,0.6434971690177917,-0.542546272277832,0.5399582386016846,0.6696119904518127,-0.542546272277832,0.5072113275527954,0.6940619945526123,-0.542546272277832,0.4732034504413605,0.7167864441871643,-0.542546272277832,0.4380190968513489,0.7377288937568665,-0.542546272277832,0.4017457365989685,0.7568371891975403,-0.542546272277832,0.3644735813140869,0.7740638852119446,-0.542546272277832,0.32629528641700745,0.7893661856651306,-0.542546272277832,0.2873057723045349,0.8027059435844421,-0.542546272277832,0.24760198593139648,0.814050018787384,-0.542546272277832,0.20728261768817902,0.8233702778816223,-0.542546272277832,0.16644789278507233,0.8306435346603394,-0.542546272277832,0.1251993626356125,0.8358516693115234,-0.542546272277832,0.08363956958055496,0.8389816880226135,-0.542546272277832,0.04187183454632759,0.8400259017944336,-0.542546272277832,2.0574701604047873e-16,0.8660253882408142,-0.5,0.0,0.8649488687515259,-0.5,-0.043167803436517715,0.861721932888031,-0.5,-0.08622828125953674,0.8563526272773743,-0.5,-0.12907439470291138,0.8488543033599854,-0.5,-0.1715995967388153,0.8392455577850342,-0.5,-0.21369817852973938,0.8275503516197205,-0.5,-0.255265474319458,0.813797652721405,-0.5,-0.29619812965393066,0.7980217933654785,-0.5,-0.33639439940452576,0.7802619338035583,-0.5,-0.37575432658195496,0.7605621814727783,-0.5,-0.41418009996414185,0.7389715909957886,-0.5,-0.4515761435031891,0.7155437469482422,-0.5,-0.48784947395324707,0.6903370022773743,-0.5,-0.522909939289093,0.663413941860199,-0.5,-0.5566704273223877,0.6348415613174438,-0.5,-0.5890468955039978,0.6046908497810364,-0.5,-0.6199588775634766,0.5730367302894592,-0.5,-0.6493295431137085,0.5399580001831055,-0.5,-0.6770859360694885,0.5055368542671204,-0.5,-0.7031589150428772,0.469858855009079,-0.5,-0.7274838089942932,0.4330126941204071,-0.5,-0.75,0.3950900137424469,-0.5,-0.7706515789031982,0.35618507862091064,-0.5,-0.7893872261047363,0.3163945972919464,-0.5,-0.8061603307723999,0.27581754326820374,-0.5,-0.8209291696548462,0.234554722905159,-0.5,-0.8336570262908936,0.19270877540111542,-0.5,-0.844312310218811,0.15038372576236725,-0.5,-0.8528685569763184,0.10768480598926544,-0.5,-0.8593043684959412,0.06471815705299377,-0.5,-0.8636038303375244,0.021590612828731537,-0.5,-0.8657562136650085,-0.021590612828731537,-0.5,-0.8657562136650085,-0.06471815705299377,-0.5,-0.8636038303375244,-0.10768480598926544,-0.5,-0.8593043684959412,-0.15038372576236725,-0.5,-0.8528685569763184,-0.19270877540111542,-0.5,-0.844312310218811,-0.234554722905159,-0.5,-0.8336570262908936,-0.27581754326820374,-0.5,-0.8209291696548462,-0.3163945972919464,-0.5,-0.8061603307723999,-0.35618507862091064,-0.5,-0.7893872261047363,-0.3950900137424469,-0.5,-0.7706515789031982,-0.4330126941204071,-0.5,-0.75,-0.469858855009079,-0.5,-0.7274838089942932,-0.5055368542671204,-0.5,-0.7031589150428772,-0.5399580001831055,-0.5,-0.6770859360694885,-0.5730367302894592,-0.5,-0.6493295431137085,-0.6046908497810364,-0.5,-0.6199588775634766,-0.6348415613174438,-0.5,-0.5890468955039978,-0.663413941860199,-0.5,-0.5566704273223877,-0.6903370022773743,-0.5,-0.522909939289093,-0.7155437469482422,-0.5,-0.48784947395324707,-0.7389715909957886,-0.5,-0.4515761435031891,-0.7605621814727783,-0.5,-0.41418009996414185,-0.7802619338035583,-0.5,-0.37575432658195496,-0.7980217933654785,-0.5,-0.33639439940452576,-0.813797652721405,-0.5,-0.29619812965393066,-0.8275503516197205,-0.5,-0.255265474319458,-0.8392455577850342,-0.5,-0.21369817852973938,-0.8488543033599854,-0.5,-0.1715995967388153,-0.8563526272773743,-0.5,-0.12907439470291138,-0.861721932888031,-0.5,-0.08622828125953674,-0.8649488687515259,-0.5,-0.043167803436517715,-0.8660253882408142,-0.5,-1.0605752472130298e-16,-0.8649488687515259,-0.5,0.043167803436517715,-0.861721932888031,-0.5,0.08622828125953674,-0.8563526272773743,-0.5,0.12907439470291138,-0.8488543033599854,-0.5,0.1715995967388153,-0.8392455577850342,-0.5,0.21369817852973938,-0.8275503516197205,-0.5,0.255265474319458,-0.813797652721405,-0.5,0.29619812965393066,-0.7980217933654785,-0.5,0.33639439940452576,-0.7802619338035583,-0.5,0.37575432658195496,-0.7605621814727783,-0.5,0.41418009996414185,-0.7389715909957886,-0.5,0.4515761435031891,-0.7155437469482422,-0.5,0.48784947395324707,-0.6903370022773743,-0.5,0.522909939289093,-0.663413941860199,-0.5,0.5566704273223877,-0.6348415613174438,-0.5,0.5890468955039978,-0.6046908497810364,-0.5,0.6199588775634766,-0.5730367302894592,-0.5,0.6493295431137085,-0.5399580001831055,-0.5,0.6770859360694885,-0.5055368542671204,-0.5,0.7031589150428772,-0.469858855009079,-0.5,0.7274838089942932,-0.4330126941204071,-0.5,0.75,-0.3950900137424469,-0.5,0.7706515789031982,-0.35618507862091064,-0.5,0.7893872261047363,-0.3163945972919464,-0.5,0.8061603307723999,-0.27581754326820374,-0.5,0.8209291696548462,-0.234554722905159,-0.5,0.8336570262908936,-0.19270877540111542,-0.5,0.844312310218811,-0.15038372576236725,-0.5,0.8528685569763184,-0.10768480598926544,-0.5,0.8593043684959412,-0.06471815705299377,-0.5,0.8636038303375244,-0.021590612828731537,-0.5,0.8657562136650085,0.021590612828731537,-0.5,0.8657562136650085,0.06471815705299377,-0.5,0.8636038303375244,0.10768480598926544,-0.5,0.8593043684959412,0.15038372576236725,-0.5,0.8528685569763184,0.19270877540111542,-0.5,0.844312310218811,0.234554722905159,-0.5,0.8336570262908936,0.27581754326820374,-0.5,0.8209291696548462,0.3163945972919464,-0.5,0.8061603307723999,0.35618507862091064,-0.5,0.7893872261047363,0.3950900137424469,-0.5,0.7706515789031982,0.4330126941204071,-0.5,0.75,0.469858855009079,-0.5,0.7274838089942932,0.5055368542671204,-0.5,0.7031589150428772,0.5399580001831055,-0.5,0.6770859360694885,0.5730367302894592,-0.5,0.6493295431137085,0.6046908497810364,-0.5,0.6199588775634766,0.6348415613174438,-0.5,0.5890468955039978,0.663413941860199,-0.5,0.5566704273223877,0.6903370022773743,-0.5,0.522909939289093,0.7155437469482422,-0.5,0.48784947395324707,0.7389715909957886,-0.5,0.4515761435031891,0.7605621814727783,-0.5,0.41418009996414185,0.7802619338035583,-0.5,0.37575432658195496,0.7980217933654785,-0.5,0.33639439940452576,0.813797652721405,-0.5,0.29619812965393066,0.8275503516197205,-0.5,0.255265474319458,0.8392455577850342,-0.5,0.21369817852973938,0.8488543033599854,-0.5,0.1715995967388153,0.8563526272773743,-0.5,0.12907439470291138,0.861721932888031,-0.5,0.08622828125953674,0.8649488687515259,-0.5,0.043167803436517715,0.8660253882408142,-0.5,2.1211504944260596e-16,0.8898718357086182,-0.4562106430530548,0.0,0.8887656331062317,-0.4562106430530548,-0.04435644671320915,0.8854498267173767,-0.4562106430530548,-0.08860261738300323,0.879932701587677,-0.4562106430530548,-0.13262851536273956,0.8722278475761414,-0.4562106430530548,-0.17632466554641724,0.862354576587677,-0.4562106430530548,-0.2195824533700943,0.8503373265266418,-0.4562106430530548,-0.26229432225227356,0.8362059593200684,-0.4562106430530548,-0.30435407161712646,0.8199957013130188,-0.4562106430530548,-0.3456571698188782,0.8017467856407166,-0.4562106430530548,-0.3861009180545807,0.7815046310424805,-0.4562106430530548,-0.42558473348617554,0.7593194842338562,-0.4562106430530548,-0.46401047706604004,0.7352465987205505,-0.4562106430530548,-0.5012826323509216,0.7093457579612732,-0.4562106430530548,-0.5373085141181946,0.6816813349723816,-0.4562106430530548,-0.5719985961914062,0.6523221731185913,-0.4562106430530548,-0.6052665710449219,0.6213412880897522,-0.4562106430530548,-0.6370297074317932,0.5888155698776245,-0.4562106430530548,-0.6672091484069824,0.554826021194458,-0.4562106430530548,-0.6957297921180725,0.5194570422172546,-0.4562106430530548,-0.7225207686424255,0.48279663920402527,-0.4562106430530548,-0.7475153803825378,0.4449359178543091,-0.4562106430530548,-0.7706515789031982,0.4059689939022064,-0.4562106430530548,-0.7918718457221985,0.36599278450012207,-0.4562106430530548,-0.8111233711242676,0.325106680393219,-0.4562106430530548,-0.8283582925796509,0.28341227769851685,-0.4562106430530548,-0.8435338139533997,0.241013303399086,-0.4562106430530548,-0.8566122055053711,0.19801510870456696,-0.4562106430530548,-0.867560863494873,0.15452462434768677,-0.4562106430530548,-0.8763526678085327,0.1106499582529068,-0.4562106430530548,-0.8829656839370728,0.06650020182132721,-0.4562106430530548,-0.8873835206031799,0.02218511886894703,-0.4562106430530548,-0.8895952105522156,-0.02218511886894703,-0.4562106430530548,-0.8895952105522156,-0.06650020182132721,-0.4562106430530548,-0.8873835206031799,-0.1106499582529068,-0.4562106430530548,-0.8829656839370728,-0.15452462434768677,-0.4562106430530548,-0.8763526678085327,-0.19801510870456696,-0.4562106430530548,-0.867560863494873,-0.241013303399086,-0.4562106430530548,-0.8566122055053711,-0.28341227769851685,-0.4562106430530548,-0.8435338139533997,-0.325106680393219,-0.4562106430530548,-0.8283582925796509,-0.36599278450012207,-0.4562106430530548,-0.8111233711242676,-0.4059689939022064,-0.4562106430530548,-0.7918718457221985,-0.4449359178543091,-0.4562106430530548,-0.7706515789031982,-0.48279663920402527,-0.4562106430530548,-0.7475153803825378,-0.5194570422172546,-0.4562106430530548,-0.7225207686424255,-0.554826021194458,-0.4562106430530548,-0.6957297921180725,-0.5888155698776245,-0.4562106430530548,-0.6672091484069824,-0.6213412880897522,-0.4562106430530548,-0.6370297074317932,-0.6523221731185913,-0.4562106430530548,-0.6052665710449219,-0.6816813349723816,-0.4562106430530548,-0.5719985961914062,-0.7093457579612732,-0.4562106430530548,-0.5373085141181946,-0.7352465987205505,-0.4562106430530548,-0.5012826323509216,-0.7593194842338562,-0.4562106430530548,-0.46401047706604004,-0.7815046310424805,-0.4562106430530548,-0.42558473348617554,-0.8017467856407166,-0.4562106430530548,-0.3861009180545807,-0.8199957013130188,-0.4562106430530548,-0.3456571698188782,-0.8362059593200684,-0.4562106430530548,-0.30435407161712646,-0.8503373265266418,-0.4562106430530548,-0.26229432225227356,-0.862354576587677,-0.4562106430530548,-0.2195824533700943,-0.8722278475761414,-0.4562106430530548,-0.17632466554641724,-0.879932701587677,-0.4562106430530548,-0.13262851536273956,-0.8854498267173767,-0.4562106430530548,-0.08860261738300323,-0.8887656331062317,-0.4562106430530548,-0.04435644671320915,-0.8898718357086182,-0.4562106430530548,-1.0897786933030919e-16,-0.8887656331062317,-0.4562106430530548,0.04435644671320915,-0.8854498267173767,-0.4562106430530548,0.08860261738300323,-0.879932701587677,-0.4562106430530548,0.13262851536273956,-0.8722278475761414,-0.4562106430530548,0.17632466554641724,-0.862354576587677,-0.4562106430530548,0.2195824533700943,-0.8503373265266418,-0.4562106430530548,0.26229432225227356,-0.8362059593200684,-0.4562106430530548,0.30435407161712646,-0.8199957013130188,-0.4562106430530548,0.3456571698188782,-0.8017467856407166,-0.4562106430530548,0.3861009180545807,-0.7815046310424805,-0.4562106430530548,0.42558473348617554,-0.7593194842338562,-0.4562106430530548,0.46401047706604004,-0.7352465987205505,-0.4562106430530548,0.5012826323509216,-0.7093457579612732,-0.4562106430530548,0.5373085141181946,-0.6816813349723816,-0.4562106430530548,0.5719985961914062,-0.6523221731185913,-0.4562106430530548,0.6052665710449219,-0.6213412880897522,-0.4562106430530548,0.6370297074317932,-0.5888155698776245,-0.4562106430530548,0.6672091484069824,-0.554826021194458,-0.4562106430530548,0.6957297921180725,-0.5194570422172546,-0.4562106430530548,0.7225207686424255,-0.48279663920402527,-0.4562106430530548,0.7475153803825378,-0.4449359178543091,-0.4562106430530548,0.7706515789031982,-0.4059689939022064,-0.4562106430530548,0.7918718457221985,-0.36599278450012207,-0.4562106430530548,0.8111233711242676,-0.325106680393219,-0.4562106430530548,0.8283582925796509,-0.28341227769851685,-0.4562106430530548,0.8435338139533997,-0.241013303399086,-0.4562106430530548,0.8566122055053711,-0.19801510870456696,-0.4562106430530548,0.867560863494873,-0.15452462434768677,-0.4562106430530548,0.8763526678085327,-0.1106499582529068,-0.4562106430530548,0.8829656839370728,-0.06650020182132721,-0.4562106430530548,0.8873835206031799,-0.02218511886894703,-0.4562106430530548,0.8895952105522156,0.02218511886894703,-0.4562106430530548,0.8895952105522156,0.06650020182132721,-0.4562106430530548,0.8873835206031799,0.1106499582529068,-0.4562106430530548,0.8829656839370728,0.15452462434768677,-0.4562106430530548,0.8763526678085327,0.19801510870456696,-0.4562106430530548,0.867560863494873,0.241013303399086,-0.4562106430530548,0.8566122055053711,0.28341227769851685,-0.4562106430530548,0.8435338139533997,0.325106680393219,-0.4562106430530548,0.8283582925796509,0.36599278450012207,-0.4562106430530548,0.8111233711242676,0.4059689939022064,-0.4562106430530548,0.7918718457221985,0.4449359178543091,-0.4562106430530548,0.7706515789031982,0.48279663920402527,-0.4562106430530548,0.7475153803825378,0.5194570422172546,-0.4562106430530548,0.7225207686424255,0.554826021194458,-0.4562106430530548,0.6957297921180725,0.5888155698776245,-0.4562106430530548,0.6672091484069824,0.6213412880897522,-0.4562106430530548,0.6370297074317932,0.6523221731185913,-0.4562106430530548,0.6052665710449219,0.6816813349723816,-0.4562106430530548,0.5719985961914062,0.7093457579612732,-0.4562106430530548,0.5373085141181946,0.7352465987205505,-0.4562106430530548,0.5012826323509216,0.7593194842338562,-0.4562106430530548,0.46401047706604004,0.7815046310424805,-0.4562106430530548,0.42558473348617554,0.8017467856407166,-0.4562106430530548,0.3861009180545807,0.8199957013130188,-0.4562106430530548,0.3456571698188782,0.8362059593200684,-0.4562106430530548,0.30435407161712646,0.8503373265266418,-0.4562106430530548,0.26229432225227356,0.862354576587677,-0.4562106430530548,0.2195824533700943,0.8722278475761414,-0.4562106430530548,0.17632466554641724,0.879932701587677,-0.4562106430530548,0.13262851536273956,0.8854498267173767,-0.4562106430530548,0.08860261738300323,0.8887656331062317,-0.4562106430530548,0.04435644671320915,0.8898718357086182,-0.4562106430530548,2.1795573866061839e-16,0.9115058779716492,-0.4112870991230011,0.0,0.910372793674469,-0.4112870991230011,-0.04543481767177582,0.9069764018058777,-0.4112870991230011,-0.09075667709112167,0.9013251066207886,-0.4112870991230011,-0.1358529031276703,0.8934329748153687,-0.4112870991230011,-0.18061137199401855,0.883319616317749,-0.4112870991230011,-0.22492080926895142,0.8710101842880249,-0.4112870991230011,-0.26867106556892395,0.8565353155136108,-0.4112870991230011,-0.31175336241722107,0.8399309515953064,-0.4112870991230011,-0.3540605902671814,0.8212383985519409,-0.4112870991230011,-0.39548757672309875,0.8005040884017944,-0.4112870991230011,-0.4359312951564789,0.7777796387672424,-0.4112870991230011,-0.47529125213623047,0.7531214952468872,-0.4112870991230011,-0.5134695172309875,0.7265909314155579,-0.4112870991230011,-0.5503712296485901,0.6982539892196655,-0.4112870991230011,-0.5859046578407288,0.6681810617446899,-0.4112870991230011,-0.6199814081192017,0.6364469528198242,-0.4112870991230011,-0.6525168418884277,0.6031305193901062,-0.4112870991230011,-0.6834299564361572,0.5683146119117737,-0.4112870991230011,-0.7126439809799194,0.5320857763290405,-0.4112870991230011,-0.7400862574577332,0.49453410506248474,-0.4112870991230011,-0.7656885385513306,0.4557529389858246,-0.4112870991230011,-0.7893872261047363,0.41583868861198425,-0.4112870991230011,-0.8111233711242676,0.37489059567451477,-0.4112870991230011,-0.830842912197113,0.3330104947090149,-0.4112870991230011,-0.8484968543052673,0.29030245542526245,-0.4112870991230011,-0.8640413284301758,0.246872678399086,-0.4112870991230011,-0.8774376511573792,0.20282913744449615,-0.4112870991230011,-0.888652503490448,0.1582813262939453,-0.4112870991230011,-0.8976580500602722,0.11334001272916794,-0.4112870991230011,-0.9044318199157715,0.0681169182062149,-0.4112870991230011,-0.9089571237564087,0.022724471986293793,-0.4112870991230011,-0.911222517490387,-0.022724471986293793,-0.4112870991230011,-0.911222517490387,-0.0681169182062149,-0.4112870991230011,-0.9089571237564087,-0.11334001272916794,-0.4112870991230011,-0.9044318199157715,-0.1582813262939453,-0.4112870991230011,-0.8976580500602722,-0.20282913744449615,-0.4112870991230011,-0.888652503490448,-0.246872678399086,-0.4112870991230011,-0.8774376511573792,-0.29030245542526245,-0.4112870991230011,-0.8640413284301758,-0.3330104947090149,-0.4112870991230011,-0.8484968543052673,-0.37489059567451477,-0.4112870991230011,-0.830842912197113,-0.41583868861198425,-0.4112870991230011,-0.8111233711242676,-0.4557529389858246,-0.4112870991230011,-0.7893872261047363,-0.49453410506248474,-0.4112870991230011,-0.7656885385513306,-0.5320857763290405,-0.4112870991230011,-0.7400862574577332,-0.5683146119117737,-0.4112870991230011,-0.7126439809799194,-0.6031305193901062,-0.4112870991230011,-0.6834299564361572,-0.6364469528198242,-0.4112870991230011,-0.6525168418884277,-0.6681810617446899,-0.4112870991230011,-0.6199814081192017,-0.6982539892196655,-0.4112870991230011,-0.5859046578407288,-0.7265909314155579,-0.4112870991230011,-0.5503712296485901,-0.7531214952468872,-0.4112870991230011,-0.5134695172309875,-0.7777796387672424,-0.4112870991230011,-0.47529125213623047,-0.8005040884017944,-0.4112870991230011,-0.4359312951564789,-0.8212383985519409,-0.4112870991230011,-0.39548757672309875,-0.8399309515953064,-0.4112870991230011,-0.3540605902671814,-0.8565353155136108,-0.4112870991230011,-0.31175336241722107,-0.8710101842880249,-0.4112870991230011,-0.26867106556892395,-0.883319616317749,-0.4112870991230011,-0.22492080926895142,-0.8934329748153687,-0.4112870991230011,-0.18061137199401855,-0.9013251066207886,-0.4112870991230011,-0.1358529031276703,-0.9069764018058777,-0.4112870991230011,-0.09075667709112167,-0.910372793674469,-0.4112870991230011,-0.04543481767177582,-0.9115058779716492,-0.4112870991230011,-1.1162726927531244e-16,-0.910372793674469,-0.4112870991230011,0.04543481767177582,-0.9069764018058777,-0.4112870991230011,0.09075667709112167,-0.9013251066207886,-0.4112870991230011,0.1358529031276703,-0.8934329748153687,-0.4112870991230011,0.18061137199401855,-0.883319616317749,-0.4112870991230011,0.22492080926895142,-0.8710101842880249,-0.4112870991230011,0.26867106556892395,-0.8565353155136108,-0.4112870991230011,0.31175336241722107,-0.8399309515953064,-0.4112870991230011,0.3540605902671814,-0.8212383985519409,-0.4112870991230011,0.39548757672309875,-0.8005040884017944,-0.4112870991230011,0.4359312951564789,-0.7777796387672424,-0.4112870991230011,0.47529125213623047,-0.7531214952468872,-0.4112870991230011,0.5134695172309875,-0.7265909314155579,-0.4112870991230011,0.5503712296485901,-0.6982539892196655,-0.4112870991230011,0.5859046578407288,-0.6681810617446899,-0.4112870991230011,0.6199814081192017,-0.6364469528198242,-0.4112870991230011,0.6525168418884277,-0.6031305193901062,-0.4112870991230011,0.6834299564361572,-0.5683146119117737,-0.4112870991230011,0.7126439809799194,-0.5320857763290405,-0.4112870991230011,0.7400862574577332,-0.49453410506248474,-0.4112870991230011,0.7656885385513306,-0.4557529389858246,-0.4112870991230011,0.7893872261047363,-0.41583868861198425,-0.4112870991230011,0.8111233711242676,-0.37489059567451477,-0.4112870991230011,0.830842912197113,-0.3330104947090149,-0.4112870991230011,0.8484968543052673,-0.29030245542526245,-0.4112870991230011,0.8640413284301758,-0.246872678399086,-0.4112870991230011,0.8774376511573792,-0.20282913744449615,-0.4112870991230011,0.888652503490448,-0.1582813262939453,-0.4112870991230011,0.8976580500602722,-0.11334001272916794,-0.4112870991230011,0.9044318199157715,-0.0681169182062149,-0.4112870991230011,0.9089571237564087,-0.022724471986293793,-0.4112870991230011,0.911222517490387,0.022724471986293793,-0.4112870991230011,0.911222517490387,0.0681169182062149,-0.4112870991230011,0.9089571237564087,0.11334001272916794,-0.4112870991230011,0.9044318199157715,0.1582813262939453,-0.4112870991230011,0.8976580500602722,0.20282913744449615,-0.4112870991230011,0.888652503490448,0.246872678399086,-0.4112870991230011,0.8774376511573792,0.29030245542526245,-0.4112870991230011,0.8640413284301758,0.3330104947090149,-0.4112870991230011,0.8484968543052673,0.37489059567451477,-0.4112870991230011,0.830842912197113,0.41583868861198425,-0.4112870991230011,0.8111233711242676,0.4557529389858246,-0.4112870991230011,0.7893872261047363,0.49453410506248474,-0.4112870991230011,0.7656885385513306,0.5320857763290405,-0.4112870991230011,0.7400862574577332,0.5683146119117737,-0.4112870991230011,0.7126439809799194,0.6031305193901062,-0.4112870991230011,0.6834299564361572,0.6364469528198242,-0.4112870991230011,0.6525168418884277,0.6681810617446899,-0.4112870991230011,0.6199814081192017,0.6982539892196655,-0.4112870991230011,0.5859046578407288,0.7265909314155579,-0.4112870991230011,0.5503712296485901,0.7531214952468872,-0.4112870991230011,0.5134695172309875,0.7777796387672424,-0.4112870991230011,0.47529125213623047,0.8005040884017944,-0.4112870991230011,0.4359312951564789,0.8212383985519409,-0.4112870991230011,0.39548757672309875,0.8399309515953064,-0.4112870991230011,0.3540605902671814,0.8565353155136108,-0.4112870991230011,0.31175336241722107,0.8710101842880249,-0.4112870991230011,0.26867106556892395,0.883319616317749,-0.4112870991230011,0.22492080926895142,0.8934329748153687,-0.4112870991230011,0.18061137199401855,0.9013251066207886,-0.4112870991230011,0.1358529031276703,0.9069764018058777,-0.4112870991230011,0.09075667709112167,0.910372793674469,-0.4112870991230011,0.04543481767177582,0.9115058779716492,-0.4112870991230011,2.2325453855062487e-16,0.9308737516403198,-0.36534103751182556,0.0,0.9297165870666504,-0.36534103751182556,-0.04640022665262222,0.9262480139732361,-0.36534103751182556,-0.09268509596586227,0.9204766750335693,-0.36534103751182556,-0.13873952627182007,0.9124168157577515,-0.36534103751182556,-0.18444904685020447,0.9020885825157166,-0.36534103751182556,-0.2296999841928482,0.8895176649093628,-0.36534103751182556,-0.2743798494338989,0.8747351765632629,-0.36534103751182556,-0.3183775842189789,0.857778012752533,-0.36534103751182556,-0.36158376932144165,0.8386882543563843,-0.36534103751182556,-0.403890997171402,0.8175134062767029,-0.36534103751182556,-0.4451940655708313,0.7943060994148254,-0.36534103751182556,-0.4853903353214264,0.7691239714622498,-0.36534103751182556,-0.5243798494338989,0.7420297265052795,-0.36534103751182556,-0.5620656609535217,0.7130906581878662,-0.36534103751182556,-0.5983541011810303,0.6823787689208984,-0.36534103751182556,-0.6331549286842346,0.6499703526496887,-0.36534103751182556,-0.6663816571235657,0.6159459948539734,-0.36534103751182556,-0.69795161485672,0.5803902745246887,-0.36534103751182556,-0.7277864217758179,0.5433916449546814,-0.36534103751182556,-0.7558117508888245,0.5050420761108398,-0.36534103751182556,-0.7819581031799316,0.4654368758201599,-0.36534103751182556,-0.8061603307723999,0.42467451095581055,-0.36534103751182556,-0.8283582925796509,0.3828563690185547,-0.36534103751182556,-0.8484968543052673,0.3400863707065582,-0.36534103751182556,-0.8665259480476379,0.29647085070610046,-0.36534103751182556,-0.8824006915092468,0.2521182894706726,-0.36534103751182556,-0.8960816264152527,0.20713889598846436,-0.36534103751182556,-0.9075347781181335,0.16164453327655792,-0.36534103751182556,-0.9167316555976868,0.11574829369783401,-0.36534103751182556,-0.9236494302749634,0.06956428289413452,-0.36534103751182556,-0.9282708168029785,0.023207327350974083,-0.36534103751182556,-0.9305844306945801,-0.023207327350974083,-0.36534103751182556,-0.9305844306945801,-0.06956428289413452,-0.36534103751182556,-0.9282708168029785,-0.11574829369783401,-0.36534103751182556,-0.9236494302749634,-0.16164453327655792,-0.36534103751182556,-0.9167316555976868,-0.20713889598846436,-0.36534103751182556,-0.9075347781181335,-0.2521182894706726,-0.36534103751182556,-0.8960816264152527,-0.29647085070610046,-0.36534103751182556,-0.8824006915092468,-0.3400863707065582,-0.36534103751182556,-0.8665259480476379,-0.3828563690185547,-0.36534103751182556,-0.8484968543052673,-0.42467451095581055,-0.36534103751182556,-0.8283582925796509,-0.4654368758201599,-0.36534103751182556,-0.8061603307723999,-0.5050420761108398,-0.36534103751182556,-0.7819581031799316,-0.5433916449546814,-0.36534103751182556,-0.7558117508888245,-0.5803902745246887,-0.36534103751182556,-0.7277864217758179,-0.6159459948539734,-0.36534103751182556,-0.69795161485672,-0.6499703526496887,-0.36534103751182556,-0.6663816571235657,-0.6823787689208984,-0.36534103751182556,-0.6331549286842346,-0.7130906581878662,-0.36534103751182556,-0.5983541011810303,-0.7420297265052795,-0.36534103751182556,-0.5620656609535217,-0.7691239714622498,-0.36534103751182556,-0.5243798494338989,-0.7943060994148254,-0.36534103751182556,-0.4853903353214264,-0.8175134062767029,-0.36534103751182556,-0.4451940655708313,-0.8386882543563843,-0.36534103751182556,-0.403890997171402,-0.857778012752533,-0.36534103751182556,-0.36158376932144165,-0.8747351765632629,-0.36534103751182556,-0.3183775842189789,-0.8895176649093628,-0.36534103751182556,-0.2743798494338989,-0.9020885825157166,-0.36534103751182556,-0.2296999841928482,-0.9124168157577515,-0.36534103751182556,-0.18444904685020447,-0.9204766750335693,-0.36534103751182556,-0.13873952627182007,-0.9262480139732361,-0.36534103751182556,-0.09268509596586227,-0.9297165870666504,-0.36534103751182556,-0.04640022665262222,-0.9308737516403198,-0.36534103751182556,-1.139991600509715e-16,-0.9297165870666504,-0.36534103751182556,0.04640022665262222,-0.9262480139732361,-0.36534103751182556,0.09268509596586227,-0.9204766750335693,-0.36534103751182556,0.13873952627182007,-0.9124168157577515,-0.36534103751182556,0.18444904685020447,-0.9020885825157166,-0.36534103751182556,0.2296999841928482,-0.8895176649093628,-0.36534103751182556,0.2743798494338989,-0.8747351765632629,-0.36534103751182556,0.3183775842189789,-0.857778012752533,-0.36534103751182556,0.36158376932144165,-0.8386882543563843,-0.36534103751182556,0.403890997171402,-0.8175134062767029,-0.36534103751182556,0.4451940655708313,-0.7943060994148254,-0.36534103751182556,0.4853903353214264,-0.7691239714622498,-0.36534103751182556,0.5243798494338989,-0.7420297265052795,-0.36534103751182556,0.5620656609535217,-0.7130906581878662,-0.36534103751182556,0.5983541011810303,-0.6823787689208984,-0.36534103751182556,0.6331549286842346,-0.6499703526496887,-0.36534103751182556,0.6663816571235657,-0.6159459948539734,-0.36534103751182556,0.69795161485672,-0.5803902745246887,-0.36534103751182556,0.7277864217758179,-0.5433916449546814,-0.36534103751182556,0.7558117508888245,-0.5050420761108398,-0.36534103751182556,0.7819581031799316,-0.4654368758201599,-0.36534103751182556,0.8061603307723999,-0.42467451095581055,-0.36534103751182556,0.8283582925796509,-0.3828563690185547,-0.36534103751182556,0.8484968543052673,-0.3400863707065582,-0.36534103751182556,0.8665259480476379,-0.29647085070610046,-0.36534103751182556,0.8824006915092468,-0.2521182894706726,-0.36534103751182556,0.8960816264152527,-0.20713889598846436,-0.36534103751182556,0.9075347781181335,-0.16164453327655792,-0.36534103751182556,0.9167316555976868,-0.11574829369783401,-0.36534103751182556,0.9236494302749634,-0.06956428289413452,-0.36534103751182556,0.9282708168029785,-0.023207327350974083,-0.36534103751182556,0.9305844306945801,0.023207327350974083,-0.36534103751182556,0.9305844306945801,0.06956428289413452,-0.36534103751182556,0.9282708168029785,0.11574829369783401,-0.36534103751182556,0.9236494302749634,0.16164453327655792,-0.36534103751182556,0.9167316555976868,0.20713889598846436,-0.36534103751182556,0.9075347781181335,0.2521182894706726,-0.36534103751182556,0.8960816264152527,0.29647085070610046,-0.36534103751182556,0.8824006915092468,0.3400863707065582,-0.36534103751182556,0.8665259480476379,0.3828563690185547,-0.36534103751182556,0.8484968543052673,0.42467451095581055,-0.36534103751182556,0.8283582925796509,0.4654368758201599,-0.36534103751182556,0.8061603307723999,0.5050420761108398,-0.36534103751182556,0.7819581031799316,0.5433916449546814,-0.36534103751182556,0.7558117508888245,0.5803902745246887,-0.36534103751182556,0.7277864217758179,0.6159459948539734,-0.36534103751182556,0.69795161485672,0.6499703526496887,-0.36534103751182556,0.6663816571235657,0.6823787689208984,-0.36534103751182556,0.6331549286842346,0.7130906581878662,-0.36534103751182556,0.5983541011810303,0.7420297265052795,-0.36534103751182556,0.5620656609535217,0.7691239714622498,-0.36534103751182556,0.5243798494338989,0.7943060994148254,-0.36534103751182556,0.4853903353214264,0.8175134062767029,-0.36534103751182556,0.4451940655708313,0.8386882543563843,-0.36534103751182556,0.403890997171402,0.857778012752533,-0.36534103751182556,0.36158376932144165,0.8747351765632629,-0.36534103751182556,0.3183775842189789,0.8895176649093628,-0.36534103751182556,0.2743798494338989,0.9020885825157166,-0.36534103751182556,0.2296999841928482,0.9124168157577515,-0.36534103751182556,0.18444904685020447,0.9204766750335693,-0.36534103751182556,0.13873952627182007,0.9262480139732361,-0.36534103751182556,0.09268509596586227,0.9297165870666504,-0.36534103751182556,0.04640022665262222,0.9308737516403198,-0.36534103751182556,2.27998320101943e-16,0.9479273557662964,-0.31848666071891785,0.0,0.9467489719390869,-0.31848666071891785,-0.047250278294086456,0.943216860294342,-0.31848666071891785,-0.09438308328390121,0.9373397827148438,-0.31848666071891785,-0.14128124713897705,0.9291322827339172,-0.31848666071891785,-0.1878281533718109,0.9186148643493652,-0.31848666071891785,-0.23390808701515198,0.9058135747909546,-0.31848666071891785,-0.27940648794174194,0.8907603025436401,-0.31848666071891785,-0.3242102563381195,0.8734924793243408,-0.31848666071891785,-0.3682079613208771,0.8540530204772949,-0.31848666071891785,-0.4112902581691742,0.8324902653694153,-0.31848666071891785,-0.4533500373363495,0.8088577389717102,-0.31848666071891785,-0.49428269267082214,0.7832143306732178,-0.31848666071891785,-0.533986508846283,0.7556236982345581,-0.31848666071891785,-0.5723627209663391,0.7261544466018677,-0.31848666071891785,-0.6093159317970276,0.6948798894882202,-0.31848666071891785,-0.6447543501853943,0.6618777513504028,-0.31848666071891785,-0.6785897612571716,0.6272301077842712,-0.31848666071891785,-0.7107381224632263,0.5910230278968811,-0.31848666071891785,-0.7411194443702698,0.553346574306488,-0.31848666071891785,-0.7696582674980164,0.514294445514679,-0.31848666071891785,-0.7962835431098938,0.4739636778831482,-0.31848666071891785,-0.8209291696548462,0.43245455622673035,-0.31848666071891785,-0.8435338139533997,0.389870285987854,-0.31848666071891785,-0.8640413284301758,0.34631675481796265,-0.31848666071891785,-0.8824006915092468,0.3019022047519684,-0.31848666071891785,-0.8985662460327148,0.25673708319664,-0.31848666071891785,-0.9124978184700012,0.21093368530273438,-0.31848666071891785,-0.9241608381271362,0.16460585594177246,-0.31848666071891785,-0.9335262179374695,0.11786879599094391,-0.31848666071891785,-0.9405706524848938,0.07083869725465775,-0.31848666071891785,-0.9452767372131348,0.023632483556866646,-0.31848666071891785,-0.9476327300071716,-0.023632483556866646,-0.31848666071891785,-0.9476327300071716,-0.07083869725465775,-0.31848666071891785,-0.9452767372131348,-0.11786879599094391,-0.31848666071891785,-0.9405706524848938,-0.16460585594177246,-0.31848666071891785,-0.9335262179374695,-0.21093368530273438,-0.31848666071891785,-0.9241608381271362,-0.25673708319664,-0.31848666071891785,-0.9124978184700012,-0.3019022047519684,-0.31848666071891785,-0.8985662460327148,-0.34631675481796265,-0.31848666071891785,-0.8824006915092468,-0.389870285987854,-0.31848666071891785,-0.8640413284301758,-0.43245455622673035,-0.31848666071891785,-0.8435338139533997,-0.4739636778831482,-0.31848666071891785,-0.8209291696548462,-0.514294445514679,-0.31848666071891785,-0.7962835431098938,-0.553346574306488,-0.31848666071891785,-0.7696582674980164,-0.5910230278968811,-0.31848666071891785,-0.7411194443702698,-0.6272301077842712,-0.31848666071891785,-0.7107381224632263,-0.6618777513504028,-0.31848666071891785,-0.6785897612571716,-0.6948798894882202,-0.31848666071891785,-0.6447543501853943,-0.7261544466018677,-0.31848666071891785,-0.6093159317970276,-0.7556236982345581,-0.31848666071891785,-0.5723627209663391,-0.7832143306732178,-0.31848666071891785,-0.533986508846283,-0.8088577389717102,-0.31848666071891785,-0.49428269267082214,-0.8324902653694153,-0.31848666071891785,-0.4533500373363495,-0.8540530204772949,-0.31848666071891785,-0.4112902581691742,-0.8734924793243408,-0.31848666071891785,-0.3682079613208771,-0.8907603025436401,-0.31848666071891785,-0.3242102563381195,-0.9058135747909546,-0.31848666071891785,-0.27940648794174194,-0.9186148643493652,-0.31848666071891785,-0.23390808701515198,-0.9291322827339172,-0.31848666071891785,-0.1878281533718109,-0.9373397827148438,-0.31848666071891785,-0.14128124713897705,-0.943216860294342,-0.31848666071891785,-0.09438308328390121,-0.9467489719390869,-0.31848666071891785,-0.047250278294086456,-0.9479273557662964,-0.31848666071891785,-1.1608761242665557e-16,-0.9467489719390869,-0.31848666071891785,0.047250278294086456,-0.943216860294342,-0.31848666071891785,0.09438308328390121,-0.9373397827148438,-0.31848666071891785,0.14128124713897705,-0.9291322827339172,-0.31848666071891785,0.1878281533718109,-0.9186148643493652,-0.31848666071891785,0.23390808701515198,-0.9058135747909546,-0.31848666071891785,0.27940648794174194,-0.8907603025436401,-0.31848666071891785,0.3242102563381195,-0.8734924793243408,-0.31848666071891785,0.3682079613208771,-0.8540530204772949,-0.31848666071891785,0.4112902581691742,-0.8324902653694153,-0.31848666071891785,0.4533500373363495,-0.8088577389717102,-0.31848666071891785,0.49428269267082214,-0.7832143306732178,-0.31848666071891785,0.533986508846283,-0.7556236982345581,-0.31848666071891785,0.5723627209663391,-0.7261544466018677,-0.31848666071891785,0.6093159317970276,-0.6948798894882202,-0.31848666071891785,0.6447543501853943,-0.6618777513504028,-0.31848666071891785,0.6785897612571716,-0.6272301077842712,-0.31848666071891785,0.7107381224632263,-0.5910230278968811,-0.31848666071891785,0.7411194443702698,-0.553346574306488,-0.31848666071891785,0.7696582674980164,-0.514294445514679,-0.31848666071891785,0.7962835431098938,-0.4739636778831482,-0.31848666071891785,0.8209291696548462,-0.43245455622673035,-0.31848666071891785,0.8435338139533997,-0.389870285987854,-0.31848666071891785,0.8640413284301758,-0.34631675481796265,-0.31848666071891785,0.8824006915092468,-0.3019022047519684,-0.31848666071891785,0.8985662460327148,-0.25673708319664,-0.31848666071891785,0.9124978184700012,-0.21093368530273438,-0.31848666071891785,0.9241608381271362,-0.16460585594177246,-0.31848666071891785,0.9335262179374695,-0.11786879599094391,-0.31848666071891785,0.9405706524848938,-0.07083869725465775,-0.31848666071891785,0.9452767372131348,-0.023632483556866646,-0.31848666071891785,0.9476327300071716,0.023632483556866646,-0.31848666071891785,0.9476327300071716,0.07083869725465775,-0.31848666071891785,0.9452767372131348,0.11786879599094391,-0.31848666071891785,0.9405706524848938,0.16460585594177246,-0.31848666071891785,0.9335262179374695,0.21093368530273438,-0.31848666071891785,0.9241608381271362,0.25673708319664,-0.31848666071891785,0.9124978184700012,0.3019022047519684,-0.31848666071891785,0.8985662460327148,0.34631675481796265,-0.31848666071891785,0.8824006915092468,0.389870285987854,-0.31848666071891785,0.8640413284301758,0.43245455622673035,-0.31848666071891785,0.8435338139533997,0.4739636778831482,-0.31848666071891785,0.8209291696548462,0.514294445514679,-0.31848666071891785,0.7962835431098938,0.553346574306488,-0.31848666071891785,0.7696582674980164,0.5910230278968811,-0.31848666071891785,0.7411194443702698,0.6272301077842712,-0.31848666071891785,0.7107381224632263,0.6618777513504028,-0.31848666071891785,0.6785897612571716,0.6948798894882202,-0.31848666071891785,0.6447543501853943,0.7261544466018677,-0.31848666071891785,0.6093159317970276,0.7556236982345581,-0.31848666071891785,0.5723627209663391,0.7832143306732178,-0.31848666071891785,0.533986508846283,0.8088577389717102,-0.31848666071891785,0.49428269267082214,0.8324902653694153,-0.31848666071891785,0.4533500373363495,0.8540530204772949,-0.31848666071891785,0.4112902581691742,0.8734924793243408,-0.31848666071891785,0.3682079613208771,0.8907603025436401,-0.31848666071891785,0.3242102563381195,0.9058135747909546,-0.31848666071891785,0.27940648794174194,0.9186148643493652,-0.31848666071891785,0.23390808701515198,0.9291322827339172,-0.31848666071891785,0.1878281533718109,0.9373397827148438,-0.31848666071891785,0.14128124713897705,0.943216860294342,-0.31848666071891785,0.09438308328390121,0.9467489719390869,-0.31848666071891785,0.047250278294086456,0.9479273557662964,-0.31848666071891785,2.3217522485331115e-16,0.9626242518424988,-0.27084046602249146,0.0,0.961427628993988,-0.27084046602249146,-0.04798285663127899,0.9578407406806946,-0.27084046602249146,-0.09584642201662064,0.9518725275993347,-0.27084046602249146,-0.14347170293331146,0.9435378313064575,-0.27084046602249146,-0.19074028730392456,0.9328572750091553,-0.27084046602249146,-0.23753465712070465,0.9198575615882874,-0.27084046602249146,-0.28373846411705017,0.9045708775520325,-0.27084046602249146,-0.3292368948459625,0.8870353698730469,-0.27084046602249146,-0.37391674518585205,0.8672944903373718,-0.27084046602249146,-0.4176670014858246,0.8453973531723022,-0.27084046602249146,-0.46037888526916504,0.8213984966278076,-0.27084046602249146,-0.5019461512565613,0.7953574657440186,-0.27084046602249146,-0.5422655344009399,0.7673390507698059,-0.27084046602249146,-0.5812367796897888,0.7374129295349121,-0.27084046602249146,-0.6187629103660583,0.7056534886360168,-0.27084046602249146,-0.6547507643699646,0.6721397042274475,-0.27084046602249146,-0.6891108155250549,0.6369548439979553,-0.27084046602249146,-0.7217575907707214,0.6001864075660706,-0.27084046602249146,-0.7526099681854248,0.5619258284568787,-0.27084046602249146,-0.7815912365913391,0.5222681760787964,-0.27084046602249146,-0.8086293339729309,0.4813121259212494,-0.27084046602249146,-0.8336570262908936,0.43915945291519165,-0.27084046602249146,-0.8566122055053711,0.3959149420261383,-0.27084046602249146,-0.8774376511573792,0.35168612003326416,-0.27084046602249146,-0.8960816264152527,0.3065829575061798,-0.27084046602249146,-0.9124978184700012,0.26071760058403015,-0.27084046602249146,-0.9266454577445984,0.21420404314994812,-0.27084046602249146,-0.9384892582893372,0.16715794801712036,-0.27084046602249146,-0.9479998350143433,0.11969626694917679,-0.27084046602249146,-0.9551535248756409,0.07193700224161148,-0.27084046602249146,-0.9599325656890869,0.023998888209462166,-0.27084046602249146,-0.9623250365257263,-0.023998888209462166,-0.27084046602249146,-0.9623250365257263,-0.07193700224161148,-0.27084046602249146,-0.9599325656890869,-0.11969626694917679,-0.27084046602249146,-0.9551535248756409,-0.16715794801712036,-0.27084046602249146,-0.9479998350143433,-0.21420404314994812,-0.27084046602249146,-0.9384892582893372,-0.26071760058403015,-0.27084046602249146,-0.9266454577445984,-0.3065829575061798,-0.27084046602249146,-0.9124978184700012,-0.35168612003326416,-0.27084046602249146,-0.8960816264152527,-0.3959149420261383,-0.27084046602249146,-0.8774376511573792,-0.43915945291519165,-0.27084046602249146,-0.8566122055053711,-0.4813121259212494,-0.27084046602249146,-0.8336570262908936,-0.5222681760787964,-0.27084046602249146,-0.8086293339729309,-0.5619258284568787,-0.27084046602249146,-0.7815912365913391,-0.6001864075660706,-0.27084046602249146,-0.7526099681854248,-0.6369548439979553,-0.27084046602249146,-0.7217575907707214,-0.6721397042274475,-0.27084046602249146,-0.6891108155250549,-0.7056534886360168,-0.27084046602249146,-0.6547507643699646,-0.7374129295349121,-0.27084046602249146,-0.6187629103660583,-0.7673390507698059,-0.27084046602249146,-0.5812367796897888,-0.7953574657440186,-0.27084046602249146,-0.5422655344009399,-0.8213984966278076,-0.27084046602249146,-0.5019461512565613,-0.8453973531723022,-0.27084046602249146,-0.46037888526916504,-0.8672944903373718,-0.27084046602249146,-0.4176670014858246,-0.8870353698730469,-0.27084046602249146,-0.37391674518585205,-0.9045708775520325,-0.27084046602249146,-0.3292368948459625,-0.9198575615882874,-0.27084046602249146,-0.28373846411705017,-0.9328572750091553,-0.27084046602249146,-0.23753465712070465,-0.9435378313064575,-0.27084046602249146,-0.19074028730392456,-0.9518725275993347,-0.27084046602249146,-0.14347170293331146,-0.9578407406806946,-0.27084046602249146,-0.09584642201662064,-0.961427628993988,-0.27084046602249146,-0.04798285663127899,-0.9626242518424988,-0.27084046602249146,-1.1788746479534236e-16,-0.961427628993988,-0.27084046602249146,0.04798285663127899,-0.9578407406806946,-0.27084046602249146,0.09584642201662064,-0.9518725275993347,-0.27084046602249146,0.14347170293331146,-0.9435378313064575,-0.27084046602249146,0.19074028730392456,-0.9328572750091553,-0.27084046602249146,0.23753465712070465,-0.9198575615882874,-0.27084046602249146,0.28373846411705017,-0.9045708775520325,-0.27084046602249146,0.3292368948459625,-0.8870353698730469,-0.27084046602249146,0.37391674518585205,-0.8672944903373718,-0.27084046602249146,0.4176670014858246,-0.8453973531723022,-0.27084046602249146,0.46037888526916504,-0.8213984966278076,-0.27084046602249146,0.5019461512565613,-0.7953574657440186,-0.27084046602249146,0.5422655344009399,-0.7673390507698059,-0.27084046602249146,0.5812367796897888,-0.7374129295349121,-0.27084046602249146,0.6187629103660583,-0.7056534886360168,-0.27084046602249146,0.6547507643699646,-0.6721397042274475,-0.27084046602249146,0.6891108155250549,-0.6369548439979553,-0.27084046602249146,0.7217575907707214,-0.6001864075660706,-0.27084046602249146,0.7526099681854248,-0.5619258284568787,-0.27084046602249146,0.7815912365913391,-0.5222681760787964,-0.27084046602249146,0.8086293339729309,-0.4813121259212494,-0.27084046602249146,0.8336570262908936,-0.43915945291519165,-0.27084046602249146,0.8566122055053711,-0.3959149420261383,-0.27084046602249146,0.8774376511573792,-0.35168612003326416,-0.27084046602249146,0.8960816264152527,-0.3065829575061798,-0.27084046602249146,0.9124978184700012,-0.26071760058403015,-0.27084046602249146,0.9266454577445984,-0.21420404314994812,-0.27084046602249146,0.9384892582893372,-0.16715794801712036,-0.27084046602249146,0.9479998350143433,-0.11969626694917679,-0.27084046602249146,0.9551535248756409,-0.07193700224161148,-0.27084046602249146,0.9599325656890869,-0.023998888209462166,-0.27084046602249146,0.9623250365257263,0.023998888209462166,-0.27084046602249146,0.9623250365257263,0.07193700224161148,-0.27084046602249146,0.9599325656890869,0.11969626694917679,-0.27084046602249146,0.9551535248756409,0.16715794801712036,-0.27084046602249146,0.9479998350143433,0.21420404314994812,-0.27084046602249146,0.9384892582893372,0.26071760058403015,-0.27084046602249146,0.9266454577445984,0.3065829575061798,-0.27084046602249146,0.9124978184700012,0.35168612003326416,-0.27084046602249146,0.8960816264152527,0.3959149420261383,-0.27084046602249146,0.8774376511573792,0.43915945291519165,-0.27084046602249146,0.8566122055053711,0.4813121259212494,-0.27084046602249146,0.8336570262908936,0.5222681760787964,-0.27084046602249146,0.8086293339729309,0.5619258284568787,-0.27084046602249146,0.7815912365913391,0.6001864075660706,-0.27084046602249146,0.7526099681854248,0.6369548439979553,-0.27084046602249146,0.7217575907707214,0.6721397042274475,-0.27084046602249146,0.6891108155250549,0.7056534886360168,-0.27084046602249146,0.6547507643699646,0.7374129295349121,-0.27084046602249146,0.6187629103660583,0.7673390507698059,-0.27084046602249146,0.5812367796897888,0.7953574657440186,-0.27084046602249146,0.5422655344009399,0.8213984966278076,-0.27084046602249146,0.5019461512565613,0.8453973531723022,-0.27084046602249146,0.46037888526916504,0.8672944903373718,-0.27084046602249146,0.4176670014858246,0.8870353698730469,-0.27084046602249146,0.37391674518585205,0.9045708775520325,-0.27084046602249146,0.3292368948459625,0.9198575615882874,-0.27084046602249146,0.28373846411705017,0.9328572750091553,-0.27084046602249146,0.23753465712070465,0.9435378313064575,-0.27084046602249146,0.19074028730392456,0.9518725275993347,-0.27084046602249146,0.14347170293331146,0.9578407406806946,-0.27084046602249146,0.09584642201662064,0.961427628993988,-0.27084046602249146,0.04798285663127899,0.9626242518424988,-0.27084046602249146,2.357749295906847e-16,0.9749279022216797,-0.22252093255519867,0.0,0.9737160205841064,-0.22252093255519867,-0.04859614372253418,0.9700832962989807,-0.22252093255519867,-0.09707147628068924,0.9640387892723083,-0.22252093255519867,-0.14530546963214874,0.9555975198745728,-0.22252093255519867,-0.1931782066822052,0.9447804689407349,-0.22252093255519867,-0.24057067930698395,0.9316145777702332,-0.22252093255519867,-0.28736504912376404,0.9161325693130493,-0.22252093255519867,-0.3334449827671051,0.8983728885650635,-0.22252093255519867,-0.37869593501091003,0.8783797025680542,-0.22252093255519867,-0.4230053722858429,0.8562027215957642,-0.22252093255519867,-0.46626314520835876,0.8318970799446106,-0.22252093255519867,-0.5083617568016052,0.8055232167243958,-0.22252093255519867,-0.5491964221000671,0.7771467566490173,-0.22252093255519867,-0.588665783405304,0.7468380928039551,-0.22252093255519867,-0.626671552658081,0.7146727442741394,-0.22252093255519867,-0.663119375705719,0.6807305812835693,-0.22252093255519867,-0.6979185938835144,0.6450960040092468,-0.22252093255519867,-0.7309826016426086,0.6078575849533081,-0.22252093255519867,-0.762229323387146,0.5691080093383789,-0.22252093255519867,-0.7915810346603394,0.5289434790611267,-0.22252093255519867,-0.8189647197723389,0.48746395111083984,-0.22252093255519867,-0.844312310218811,0.44477251172065735,-0.22252093255519867,-0.867560863494873,0.4009752869606018,-0.22252093255519867,-0.888652503490448,0.35618117451667786,-0.22252093255519867,-0.9075347781181335,0.3105015158653259,-0.22252093255519867,-0.9241608381271362,0.2640499174594879,-0.22252093255519867,-0.9384892582893372,0.21694186329841614,-0.22252093255519867,-0.9504844546318054,0.16929446160793304,-0.22252093255519867,-0.9601165652275085,0.12122614681720734,-0.22252093255519867,-0.9673616886138916,0.07285645604133606,-0.22252093255519867,-0.9722018241882324,0.02430562674999237,-0.22252093255519867,-0.9746248722076416,-0.02430562674999237,-0.22252093255519867,-0.9746248722076416,-0.07285645604133606,-0.22252093255519867,-0.9722018241882324,-0.12122614681720734,-0.22252093255519867,-0.9673616886138916,-0.16929446160793304,-0.22252093255519867,-0.9601165652275085,-0.21694186329841614,-0.22252093255519867,-0.9504844546318054,-0.2640499174594879,-0.22252093255519867,-0.9384892582893372,-0.3105015158653259,-0.22252093255519867,-0.9241608381271362,-0.35618117451667786,-0.22252093255519867,-0.9075347781181335,-0.4009752869606018,-0.22252093255519867,-0.888652503490448,-0.44477251172065735,-0.22252093255519867,-0.867560863494873,-0.48746395111083984,-0.22252093255519867,-0.844312310218811,-0.5289434790611267,-0.22252093255519867,-0.8189647197723389,-0.5691080093383789,-0.22252093255519867,-0.7915810346603394,-0.6078575849533081,-0.22252093255519867,-0.762229323387146,-0.6450960040092468,-0.22252093255519867,-0.7309826016426086,-0.6807305812835693,-0.22252093255519867,-0.6979185938835144,-0.7146727442741394,-0.22252093255519867,-0.663119375705719,-0.7468380928039551,-0.22252093255519867,-0.626671552658081,-0.7771467566490173,-0.22252093255519867,-0.588665783405304,-0.8055232167243958,-0.22252093255519867,-0.5491964221000671,-0.8318970799446106,-0.22252093255519867,-0.5083617568016052,-0.8562027215957642,-0.22252093255519867,-0.46626314520835876,-0.8783797025680542,-0.22252093255519867,-0.4230053722858429,-0.8983728885650635,-0.22252093255519867,-0.37869593501091003,-0.9161325693130493,-0.22252093255519867,-0.3334449827671051,-0.9316145777702332,-0.22252093255519867,-0.28736504912376404,-0.9447804689407349,-0.22252093255519867,-0.24057067930698395,-0.9555975198745728,-0.22252093255519867,-0.1931782066822052,-0.9640387892723083,-0.22252093255519867,-0.14530546963214874,-0.9700832962989807,-0.22252093255519867,-0.09707147628068924,-0.9737160205841064,-0.22252093255519867,-0.04859614372253418,-0.9749279022216797,-0.22252093255519867,-1.1939423052938935e-16,-0.9737160205841064,-0.22252093255519867,0.04859614372253418,-0.9700832962989807,-0.22252093255519867,0.09707147628068924,-0.9640387892723083,-0.22252093255519867,0.14530546963214874,-0.9555975198745728,-0.22252093255519867,0.1931782066822052,-0.9447804689407349,-0.22252093255519867,0.24057067930698395,-0.9316145777702332,-0.22252093255519867,0.28736504912376404,-0.9161325693130493,-0.22252093255519867,0.3334449827671051,-0.8983728885650635,-0.22252093255519867,0.37869593501091003,-0.8783797025680542,-0.22252093255519867,0.4230053722858429,-0.8562027215957642,-0.22252093255519867,0.46626314520835876,-0.8318970799446106,-0.22252093255519867,0.5083617568016052,-0.8055232167243958,-0.22252093255519867,0.5491964221000671,-0.7771467566490173,-0.22252093255519867,0.588665783405304,-0.7468380928039551,-0.22252093255519867,0.626671552658081,-0.7146727442741394,-0.22252093255519867,0.663119375705719,-0.6807305812835693,-0.22252093255519867,0.6979185938835144,-0.6450960040092468,-0.22252093255519867,0.7309826016426086,-0.6078575849533081,-0.22252093255519867,0.762229323387146,-0.5691080093383789,-0.22252093255519867,0.7915810346603394,-0.5289434790611267,-0.22252093255519867,0.8189647197723389,-0.48746395111083984,-0.22252093255519867,0.844312310218811,-0.44477251172065735,-0.22252093255519867,0.867560863494873,-0.4009752869606018,-0.22252093255519867,0.888652503490448,-0.35618117451667786,-0.22252093255519867,0.9075347781181335,-0.3105015158653259,-0.22252093255519867,0.9241608381271362,-0.2640499174594879,-0.22252093255519867,0.9384892582893372,-0.21694186329841614,-0.22252093255519867,0.9504844546318054,-0.16929446160793304,-0.22252093255519867,0.9601165652275085,-0.12122614681720734,-0.22252093255519867,0.9673616886138916,-0.07285645604133606,-0.22252093255519867,0.9722018241882324,-0.02430562674999237,-0.22252093255519867,0.9746248722076416,0.02430562674999237,-0.22252093255519867,0.9746248722076416,0.07285645604133606,-0.22252093255519867,0.9722018241882324,0.12122614681720734,-0.22252093255519867,0.9673616886138916,0.16929446160793304,-0.22252093255519867,0.9601165652275085,0.21694186329841614,-0.22252093255519867,0.9504844546318054,0.2640499174594879,-0.22252093255519867,0.9384892582893372,0.3105015158653259,-0.22252093255519867,0.9241608381271362,0.35618117451667786,-0.22252093255519867,0.9075347781181335,0.4009752869606018,-0.22252093255519867,0.888652503490448,0.44477251172065735,-0.22252093255519867,0.867560863494873,0.48746395111083984,-0.22252093255519867,0.844312310218811,0.5289434790611267,-0.22252093255519867,0.8189647197723389,0.5691080093383789,-0.22252093255519867,0.7915810346603394,0.6078575849533081,-0.22252093255519867,0.762229323387146,0.6450960040092468,-0.22252093255519867,0.7309826016426086,0.6807305812835693,-0.22252093255519867,0.6979185938835144,0.7146727442741394,-0.22252093255519867,0.663119375705719,0.7468380928039551,-0.22252093255519867,0.626671552658081,0.7771467566490173,-0.22252093255519867,0.588665783405304,0.8055232167243958,-0.22252093255519867,0.5491964221000671,0.8318970799446106,-0.22252093255519867,0.5083617568016052,0.8562027215957642,-0.22252093255519867,0.46626314520835876,0.8783797025680542,-0.22252093255519867,0.4230053722858429,0.8983728885650635,-0.22252093255519867,0.37869593501091003,0.9161325693130493,-0.22252093255519867,0.3334449827671051,0.9316145777702332,-0.22252093255519867,0.28736504912376404,0.9447804689407349,-0.22252093255519867,0.24057067930698395,0.9555975198745728,-0.22252093255519867,0.1931782066822052,0.9640387892723083,-0.22252093255519867,0.14530546963214874,0.9700832962989807,-0.22252093255519867,0.09707147628068924,0.9737160205841064,-0.22252093255519867,0.04859614372253418,0.9749279022216797,-0.22252093255519867,2.387884610587787e-16,0.9848077297210693,-0.1736481785774231,0.0,0.9835835695266724,-0.1736481785774231,-0.04908861592411995,0.9799140095710754,-0.1736481785774231,-0.09805518388748169,0.9738082885742188,-0.1736481785774231,-0.14677797257900238,0.9652814865112305,-0.1736481785774231,-0.19513586163520813,0.9543548226356506,-0.1736481785774231,-0.2430085986852646,0.9410555362701416,-0.1736481785774231,-0.2902771830558777,0.9254165887832642,-0.1736481785774231,-0.33682408928871155,0.9074769020080566,-0.1736481785774231,-0.38253360986709595,0.8872811198234558,-0.1736481785774231,-0.4272920787334442,0.8648794293403625,-0.1736481785774231,-0.4709882140159607,0.8403275012969971,-0.1736481785774231,-0.513513445854187,0.8136863708496094,-0.1736481785774231,-0.5547619462013245,0.7850222587585449,-0.1736481785774231,-0.5946312546730042,0.7544065117835999,-0.1736481785774231,-0.6330222487449646,0.7219151854515076,-0.1736481785774231,-0.6698393821716309,0.6876290440559387,-0.1736481785774231,-0.7049912214279175,0.6516333222389221,-0.1736481785774231,-0.738390326499939,0.6140176057815552,-0.1736481785774231,-0.769953727722168,0.5748752951622009,-0.1736481785774231,-0.7996028661727905,0.5343037843704224,-0.1736481785774231,-0.8272640705108643,0.49240386486053467,-0.1736481785774231,-0.8528685569763184,0.44927978515625,-0.1736481785774231,-0.8763526678085327,0.4050387144088745,-0.1736481785774231,-0.8976580500602722,0.3597906827926636,-0.1736481785774231,-0.9167316555976868,0.31364813446998596,-0.1736481785774231,-0.9335262179374695,0.2667257785797119,-0.1736481785774231,-0.9479998350143433,0.21914033591747284,-0.1736481785774231,-0.9601165652275085,0.1710100769996643,-0.1736481785774231,-0.9698463082313538,0.12245464324951172,-0.1736481785774231,-0.9771648645401001,0.07359477877616882,-0.1736481785774231,-0.9820540547370911,0.024551939219236374,-0.1736481785774231,-0.9845016598701477,-0.024551939219236374,-0.1736481785774231,-0.9845016598701477,-0.07359477877616882,-0.1736481785774231,-0.9820540547370911,-0.12245464324951172,-0.1736481785774231,-0.9771648645401001,-0.1710100769996643,-0.1736481785774231,-0.9698463082313538,-0.21914033591747284,-0.1736481785774231,-0.9601165652275085,-0.2667257785797119,-0.1736481785774231,-0.9479998350143433,-0.31364813446998596,-0.1736481785774231,-0.9335262179374695,-0.3597906827926636,-0.1736481785774231,-0.9167316555976868,-0.4050387144088745,-0.1736481785774231,-0.8976580500602722,-0.44927978515625,-0.1736481785774231,-0.8763526678085327,-0.49240386486053467,-0.1736481785774231,-0.8528685569763184,-0.5343037843704224,-0.1736481785774231,-0.8272640705108643,-0.5748752951622009,-0.1736481785774231,-0.7996028661727905,-0.6140176057815552,-0.1736481785774231,-0.769953727722168,-0.6516333222389221,-0.1736481785774231,-0.738390326499939,-0.6876290440559387,-0.1736481785774231,-0.7049912214279175,-0.7219151854515076,-0.1736481785774231,-0.6698393821716309,-0.7544065117835999,-0.1736481785774231,-0.6330222487449646,-0.7850222587585449,-0.1736481785774231,-0.5946312546730042,-0.8136863708496094,-0.1736481785774231,-0.5547619462013245,-0.8403275012969971,-0.1736481785774231,-0.513513445854187,-0.8648794293403625,-0.1736481785774231,-0.4709882140159607,-0.8872811198234558,-0.1736481785774231,-0.4272920787334442,-0.9074769020080566,-0.1736481785774231,-0.38253360986709595,-0.9254165887832642,-0.1736481785774231,-0.33682408928871155,-0.9410555362701416,-0.1736481785774231,-0.2902771830558777,-0.9543548226356506,-0.1736481785774231,-0.2430085986852646,-0.9652814865112305,-0.1736481785774231,-0.19513586163520813,-0.9738082885742188,-0.1736481785774231,-0.14677797257900238,-0.9799140095710754,-0.1736481785774231,-0.09805518388748169,-0.9835835695266724,-0.1736481785774231,-0.04908861592411995,-0.9848077297210693,-0.1736481785774231,-1.2060416415498291e-16,-0.9835835695266724,-0.1736481785774231,0.04908861592411995,-0.9799140095710754,-0.1736481785774231,0.09805518388748169,-0.9738082885742188,-0.1736481785774231,0.14677797257900238,-0.9652814865112305,-0.1736481785774231,0.19513586163520813,-0.9543548226356506,-0.1736481785774231,0.2430085986852646,-0.9410555362701416,-0.1736481785774231,0.2902771830558777,-0.9254165887832642,-0.1736481785774231,0.33682408928871155,-0.9074769020080566,-0.1736481785774231,0.38253360986709595,-0.8872811198234558,-0.1736481785774231,0.4272920787334442,-0.8648794293403625,-0.1736481785774231,0.4709882140159607,-0.8403275012969971,-0.1736481785774231,0.513513445854187,-0.8136863708496094,-0.1736481785774231,0.5547619462013245,-0.7850222587585449,-0.1736481785774231,0.5946312546730042,-0.7544065117835999,-0.1736481785774231,0.6330222487449646,-0.7219151854515076,-0.1736481785774231,0.6698393821716309,-0.6876290440559387,-0.1736481785774231,0.7049912214279175,-0.6516333222389221,-0.1736481785774231,0.738390326499939,-0.6140176057815552,-0.1736481785774231,0.769953727722168,-0.5748752951622009,-0.1736481785774231,0.7996028661727905,-0.5343037843704224,-0.1736481785774231,0.8272640705108643,-0.49240386486053467,-0.1736481785774231,0.8528685569763184,-0.44927978515625,-0.1736481785774231,0.8763526678085327,-0.4050387144088745,-0.1736481785774231,0.8976580500602722,-0.3597906827926636,-0.1736481785774231,0.9167316555976868,-0.31364813446998596,-0.1736481785774231,0.9335262179374695,-0.2667257785797119,-0.1736481785774231,0.9479998350143433,-0.21914033591747284,-0.1736481785774231,0.9601165652275085,-0.1710100769996643,-0.1736481785774231,0.9698463082313538,-0.12245464324951172,-0.1736481785774231,0.9771648645401001,-0.07359477877616882,-0.1736481785774231,0.9820540547370911,-0.024551939219236374,-0.1736481785774231,0.9845016598701477,0.024551939219236374,-0.1736481785774231,0.9845016598701477,0.07359477877616882,-0.1736481785774231,0.9820540547370911,0.12245464324951172,-0.1736481785774231,0.9771648645401001,0.1710100769996643,-0.1736481785774231,0.9698463082313538,0.21914033591747284,-0.1736481785774231,0.9601165652275085,0.2667257785797119,-0.1736481785774231,0.9479998350143433,0.31364813446998596,-0.1736481785774231,0.9335262179374695,0.3597906827926636,-0.1736481785774231,0.9167316555976868,0.4050387144088745,-0.1736481785774231,0.8976580500602722,0.44927978515625,-0.1736481785774231,0.8763526678085327,0.49240386486053467,-0.1736481785774231,0.8528685569763184,0.5343037843704224,-0.1736481785774231,0.8272640705108643,0.5748752951622009,-0.1736481785774231,0.7996028661727905,0.6140176057815552,-0.1736481785774231,0.769953727722168,0.6516333222389221,-0.1736481785774231,0.738390326499939,0.6876290440559387,-0.1736481785774231,0.7049912214279175,0.7219151854515076,-0.1736481785774231,0.6698393821716309,0.7544065117835999,-0.1736481785774231,0.6330222487449646,0.7850222587585449,-0.1736481785774231,0.5946312546730042,0.8136863708496094,-0.1736481785774231,0.5547619462013245,0.8403275012969971,-0.1736481785774231,0.513513445854187,0.8648794293403625,-0.1736481785774231,0.4709882140159607,0.8872811198234558,-0.1736481785774231,0.4272920787334442,0.9074769020080566,-0.1736481785774231,0.38253360986709595,0.9254165887832642,-0.1736481785774231,0.33682408928871155,0.9410555362701416,-0.1736481785774231,0.2902771830558777,0.9543548226356506,-0.1736481785774231,0.2430085986852646,0.9652814865112305,-0.1736481785774231,0.19513586163520813,0.9738082885742188,-0.1736481785774231,0.14677797257900238,0.9799140095710754,-0.1736481785774231,0.09805518388748169,0.9835835695266724,-0.1736481785774231,0.04908861592411995,0.9848077297210693,-0.1736481785774231,2.4120832830996583e-16,0.9922391772270203,-0.12434370815753937,0.0,0.9910057783126831,-0.12434370815753937,-0.04945904389023781,0.9873085618019104,-0.12434370815753937,-0.09879512339830399,0.9811567068099976,-0.12434370815753937,-0.14788557589054108,0.9725655913352966,-0.12434370815753937,-0.19660836458206177,0.9615564942359924,-0.12434370815753937,-0.24484236538410187,0.9481567740440369,-0.12434370815753937,-0.2924676537513733,0.9323998689651489,-0.12434370815753937,-0.33936581015586853,0.9143248200416565,-0.12434370815753937,-0.3854202330112457,0.893976628780365,-0.12434370815753937,-0.43051645159721375,0.8714058995246887,-0.12434370815753937,-0.4745423495769501,0.8466686606407166,-0.12434370815753937,-0.5173884630203247,0.8198264837265015,-0.12434370815753937,-0.5589482188224792,0.7909461259841919,-0.12434370815753937,-0.5991184115409851,0.7600993514060974,-0.12434370815753937,-0.637799084186554,0.7273628115653992,-0.12434370815753937,-0.6748940348625183,0.6928179264068604,-0.12434370815753937,-0.7103111743927002,0.6565506458282471,-0.12434370815753937,-0.743962287902832,0.6186510324478149,-0.12434370815753937,-0.7757638692855835,0.5792133808135986,-0.12434370815753937,-0.8056367039680481,0.5383356809616089,-0.12434370815753937,-0.8335066437721252,0.49611958861351013,-0.12434370815753937,-0.8593043684959412,0.4526700973510742,-0.12434370815753937,-0.8829656839370728,0.40809518098831177,-0.12434370815753937,-0.9044318199157715,0.3625056743621826,-0.12434370815753937,-0.9236494302749634,0.31601494550704956,-0.12434370815753937,-0.9405706524848938,0.2687385380268097,-0.12434370815753937,-0.9551535248756409,0.22079399228096008,-0.12434370815753937,-0.9673616886138916,0.1723005324602127,-0.12434370815753937,-0.9771648645401001,0.1233787015080452,-0.12434370815753937,-0.9845386147499084,0.07415013015270233,-0.12434370815753937,-0.9894647002220154,0.02473720908164978,-0.12434370815753937,-0.9919307827949524,-0.02473720908164978,-0.12434370815753937,-0.9919307827949524,-0.07415013015270233,-0.12434370815753937,-0.9894647002220154,-0.1233787015080452,-0.12434370815753937,-0.9845386147499084,-0.1723005324602127,-0.12434370815753937,-0.9771648645401001,-0.22079399228096008,-0.12434370815753937,-0.9673616886138916,-0.2687385380268097,-0.12434370815753937,-0.9551535248756409,-0.31601494550704956,-0.12434370815753937,-0.9405706524848938,-0.3625056743621826,-0.12434370815753937,-0.9236494302749634,-0.40809518098831177,-0.12434370815753937,-0.9044318199157715,-0.4526700973510742,-0.12434370815753937,-0.8829656839370728,-0.49611958861351013,-0.12434370815753937,-0.8593043684959412,-0.5383356809616089,-0.12434370815753937,-0.8335066437721252,-0.5792133808135986,-0.12434370815753937,-0.8056367039680481,-0.6186510324478149,-0.12434370815753937,-0.7757638692855835,-0.6565506458282471,-0.12434370815753937,-0.743962287902832,-0.6928179264068604,-0.12434370815753937,-0.7103111743927002,-0.7273628115653992,-0.12434370815753937,-0.6748940348625183,-0.7600993514060974,-0.12434370815753937,-0.637799084186554,-0.7909461259841919,-0.12434370815753937,-0.5991184115409851,-0.8198264837265015,-0.12434370815753937,-0.5589482188224792,-0.8466686606407166,-0.12434370815753937,-0.5173884630203247,-0.8714058995246887,-0.12434370815753937,-0.4745423495769501,-0.893976628780365,-0.12434370815753937,-0.43051645159721375,-0.9143248200416565,-0.12434370815753937,-0.3854202330112457,-0.9323998689651489,-0.12434370815753937,-0.33936581015586853,-0.9481567740440369,-0.12434370815753937,-0.2924676537513733,-0.9615564942359924,-0.12434370815753937,-0.24484236538410187,-0.9725655913352966,-0.12434370815753937,-0.19660836458206177,-0.9811567068099976,-0.12434370815753937,-0.14788557589054108,-0.9873085618019104,-0.12434370815753937,-0.09879512339830399,-0.9910057783126831,-0.12434370815753937,-0.04945904389023781,-0.9922391772270203,-0.12434370815753937,-1.2151426135213826e-16,-0.9910057783126831,-0.12434370815753937,0.04945904389023781,-0.9873085618019104,-0.12434370815753937,0.09879512339830399,-0.9811567068099976,-0.12434370815753937,0.14788557589054108,-0.9725655913352966,-0.12434370815753937,0.19660836458206177,-0.9615564942359924,-0.12434370815753937,0.24484236538410187,-0.9481567740440369,-0.12434370815753937,0.2924676537513733,-0.9323998689651489,-0.12434370815753937,0.33936581015586853,-0.9143248200416565,-0.12434370815753937,0.3854202330112457,-0.893976628780365,-0.12434370815753937,0.43051645159721375,-0.8714058995246887,-0.12434370815753937,0.4745423495769501,-0.8466686606407166,-0.12434370815753937,0.5173884630203247,-0.8198264837265015,-0.12434370815753937,0.5589482188224792,-0.7909461259841919,-0.12434370815753937,0.5991184115409851,-0.7600993514060974,-0.12434370815753937,0.637799084186554,-0.7273628115653992,-0.12434370815753937,0.6748940348625183,-0.6928179264068604,-0.12434370815753937,0.7103111743927002,-0.6565506458282471,-0.12434370815753937,0.743962287902832,-0.6186510324478149,-0.12434370815753937,0.7757638692855835,-0.5792133808135986,-0.12434370815753937,0.8056367039680481,-0.5383356809616089,-0.12434370815753937,0.8335066437721252,-0.49611958861351013,-0.12434370815753937,0.8593043684959412,-0.4526700973510742,-0.12434370815753937,0.8829656839370728,-0.40809518098831177,-0.12434370815753937,0.9044318199157715,-0.3625056743621826,-0.12434370815753937,0.9236494302749634,-0.31601494550704956,-0.12434370815753937,0.9405706524848938,-0.2687385380268097,-0.12434370815753937,0.9551535248756409,-0.22079399228096008,-0.12434370815753937,0.9673616886138916,-0.1723005324602127,-0.12434370815753937,0.9771648645401001,-0.1233787015080452,-0.12434370815753937,0.9845386147499084,-0.07415013015270233,-0.12434370815753937,0.9894647002220154,-0.02473720908164978,-0.12434370815753937,0.9919307827949524,0.02473720908164978,-0.12434370815753937,0.9919307827949524,0.07415013015270233,-0.12434370815753937,0.9894647002220154,0.1233787015080452,-0.12434370815753937,0.9845386147499084,0.1723005324602127,-0.12434370815753937,0.9771648645401001,0.22079399228096008,-0.12434370815753937,0.9673616886138916,0.2687385380268097,-0.12434370815753937,0.9551535248756409,0.31601494550704956,-0.12434370815753937,0.9405706524848938,0.3625056743621826,-0.12434370815753937,0.9236494302749634,0.40809518098831177,-0.12434370815753937,0.9044318199157715,0.4526700973510742,-0.12434370815753937,0.8829656839370728,0.49611958861351013,-0.12434370815753937,0.8593043684959412,0.5383356809616089,-0.12434370815753937,0.8335066437721252,0.5792133808135986,-0.12434370815753937,0.8056367039680481,0.6186510324478149,-0.12434370815753937,0.7757638692855835,0.6565506458282471,-0.12434370815753937,0.743962287902832,0.6928179264068604,-0.12434370815753937,0.7103111743927002,0.7273628115653992,-0.12434370815753937,0.6748940348625183,0.7600993514060974,-0.12434370815753937,0.637799084186554,0.7909461259841919,-0.12434370815753937,0.5991184115409851,0.8198264837265015,-0.12434370815753937,0.5589482188224792,0.8466686606407166,-0.12434370815753937,0.5173884630203247,0.8714058995246887,-0.12434370815753937,0.4745423495769501,0.893976628780365,-0.12434370815753937,0.43051645159721375,0.9143248200416565,-0.12434370815753937,0.3854202330112457,0.9323998689651489,-0.12434370815753937,0.33936581015586853,0.9481567740440369,-0.12434370815753937,0.2924676537513733,0.9615564942359924,-0.12434370815753937,0.24484236538410187,0.9725655913352966,-0.12434370815753937,0.19660836458206177,0.9811567068099976,-0.12434370815753937,0.14788557589054108,0.9873085618019104,-0.12434370815753937,0.09879512339830399,0.9910057783126831,-0.12434370815753937,0.04945904389023781,0.9922391772270203,-0.12434370815753937,2.430285227042765e-16,0.9972038269042969,-0.07473009079694748,0.0,0.9959641695022583,-0.07473009079694748,-0.049706507474184036,0.9922484755516052,-0.07473009079694748,-0.0992894321680069,0.9860658645629883,-0.07473009079694748,-0.14862550795078278,0.9774317145347595,-0.07473009079694748,-0.19759207963943481,0.9663675427436829,-0.07473009079694748,-0.24606741964817047,0.9529008269309998,-0.07473009079694748,-0.2939309775829315,0.937065064907074,-0.07473009079694748,-0.34106379747390747,0.9188995957374573,-0.07473009079694748,-0.38734865188598633,0.8984495997428894,-0.07473009079694748,-0.4326705038547516,0.875765860080719,-0.07473009079694748,-0.47691667079925537,0.8509048819541931,-0.07473009079694748,-0.5199771523475647,0.8239284157752991,-0.07473009079694748,-0.5617449283599854,0.794903576374054,-0.07473009079694748,-0.602116048336029,0.7639024257659912,-0.07473009079694748,-0.6409902572631836,0.7310020923614502,-0.07473009079694748,-0.6782708168029785,0.6962844133377075,-0.07473009079694748,-0.7138651609420776,0.6598356366157532,-0.07473009079694748,-0.7476846575737,0.6217464208602905,-0.07473009079694748,-0.7796453237533569,0.5821114182472229,-0.07473009079694748,-0.8096676468849182,0.5410292148590088,-0.07473009079694748,-0.8376770615577698,0.49860191345214844,-0.07473009079694748,-0.8636038303375244,0.4549350142478943,-0.07473009079694748,-0.8873835206031799,0.4101370573043823,-0.07473009079694748,-0.9089571237564087,0.36431944370269775,-0.07473009079694748,-0.9282708168029785,0.31759610772132874,-0.07473009079694748,-0.9452767372131348,0.27008312940597534,-0.07473009079694748,-0.9599325656890869,0.22189871966838837,-0.07473009079694748,-0.9722018241882324,0.17316262423992157,-0.07473009079694748,-0.9820540547370911,0.12399601191282272,-0.07473009079694748,-0.9894647002220154,0.07452113181352615,-0.07473009079694748,-0.9944154024124146,0.024860979989171028,-0.07473009079694748,-0.9968938231468201,-0.024860979989171028,-0.07473009079694748,-0.9968938231468201,-0.07452113181352615,-0.07473009079694748,-0.9944154024124146,-0.12399601191282272,-0.07473009079694748,-0.9894647002220154,-0.17316262423992157,-0.07473009079694748,-0.9820540547370911,-0.22189871966838837,-0.07473009079694748,-0.9722018241882324,-0.27008312940597534,-0.07473009079694748,-0.9599325656890869,-0.31759610772132874,-0.07473009079694748,-0.9452767372131348,-0.36431944370269775,-0.07473009079694748,-0.9282708168029785,-0.4101370573043823,-0.07473009079694748,-0.9089571237564087,-0.4549350142478943,-0.07473009079694748,-0.8873835206031799,-0.49860191345214844,-0.07473009079694748,-0.8636038303375244,-0.5410292148590088,-0.07473009079694748,-0.8376770615577698,-0.5821114182472229,-0.07473009079694748,-0.8096676468849182,-0.6217464208602905,-0.07473009079694748,-0.7796453237533569,-0.6598356366157532,-0.07473009079694748,-0.7476846575737,-0.6962844133377075,-0.07473009079694748,-0.7138651609420776,-0.7310020923614502,-0.07473009079694748,-0.6782708168029785,-0.7639024257659912,-0.07473009079694748,-0.6409902572631836,-0.794903576374054,-0.07473009079694748,-0.602116048336029,-0.8239284157752991,-0.07473009079694748,-0.5617449283599854,-0.8509048819541931,-0.07473009079694748,-0.5199771523475647,-0.875765860080719,-0.07473009079694748,-0.47691667079925537,-0.8984495997428894,-0.07473009079694748,-0.4326705038547516,-0.9188995957374573,-0.07473009079694748,-0.38734865188598633,-0.937065064907074,-0.07473009079694748,-0.34106379747390747,-0.9529008269309998,-0.07473009079694748,-0.2939309775829315,-0.9663675427436829,-0.07473009079694748,-0.24606741964817047,-0.9774317145347595,-0.07473009079694748,-0.19759207963943481,-0.9860658645629883,-0.07473009079694748,-0.14862550795078278,-0.9922484755516052,-0.07473009079694748,-0.0992894321680069,-0.9959641695022583,-0.07473009079694748,-0.049706507474184036,-0.9972038269042969,-0.07473009079694748,-1.2212224571980963e-16,-0.9959641695022583,-0.07473009079694748,0.049706507474184036,-0.9922484755516052,-0.07473009079694748,0.0992894321680069,-0.9860658645629883,-0.07473009079694748,0.14862550795078278,-0.9774317145347595,-0.07473009079694748,0.19759207963943481,-0.9663675427436829,-0.07473009079694748,0.24606741964817047,-0.9529008269309998,-0.07473009079694748,0.2939309775829315,-0.937065064907074,-0.07473009079694748,0.34106379747390747,-0.9188995957374573,-0.07473009079694748,0.38734865188598633,-0.8984495997428894,-0.07473009079694748,0.4326705038547516,-0.875765860080719,-0.07473009079694748,0.47691667079925537,-0.8509048819541931,-0.07473009079694748,0.5199771523475647,-0.8239284157752991,-0.07473009079694748,0.5617449283599854,-0.794903576374054,-0.07473009079694748,0.602116048336029,-0.7639024257659912,-0.07473009079694748,0.6409902572631836,-0.7310020923614502,-0.07473009079694748,0.6782708168029785,-0.6962844133377075,-0.07473009079694748,0.7138651609420776,-0.6598356366157532,-0.07473009079694748,0.7476846575737,-0.6217464208602905,-0.07473009079694748,0.7796453237533569,-0.5821114182472229,-0.07473009079694748,0.8096676468849182,-0.5410292148590088,-0.07473009079694748,0.8376770615577698,-0.49860191345214844,-0.07473009079694748,0.8636038303375244,-0.4549350142478943,-0.07473009079694748,0.8873835206031799,-0.4101370573043823,-0.07473009079694748,0.9089571237564087,-0.36431944370269775,-0.07473009079694748,0.9282708168029785,-0.31759610772132874,-0.07473009079694748,0.9452767372131348,-0.27008312940597534,-0.07473009079694748,0.9599325656890869,-0.22189871966838837,-0.07473009079694748,0.9722018241882324,-0.17316262423992157,-0.07473009079694748,0.9820540547370911,-0.12399601191282272,-0.07473009079694748,0.9894647002220154,-0.07452113181352615,-0.07473009079694748,0.9944154024124146,-0.024860979989171028,-0.07473009079694748,0.9968938231468201,0.024860979989171028,-0.07473009079694748,0.9968938231468201,0.07452113181352615,-0.07473009079694748,0.9944154024124146,0.12399601191282272,-0.07473009079694748,0.9894647002220154,0.17316262423992157,-0.07473009079694748,0.9820540547370911,0.22189871966838837,-0.07473009079694748,0.9722018241882324,0.27008312940597534,-0.07473009079694748,0.9599325656890869,0.31759610772132874,-0.07473009079694748,0.9452767372131348,0.36431944370269775,-0.07473009079694748,0.9282708168029785,0.4101370573043823,-0.07473009079694748,0.9089571237564087,0.4549350142478943,-0.07473009079694748,0.8873835206031799,0.49860191345214844,-0.07473009079694748,0.8636038303375244,0.5410292148590088,-0.07473009079694748,0.8376770615577698,0.5821114182472229,-0.07473009079694748,0.8096676468849182,0.6217464208602905,-0.07473009079694748,0.7796453237533569,0.6598356366157532,-0.07473009079694748,0.7476846575737,0.6962844133377075,-0.07473009079694748,0.7138651609420776,0.7310020923614502,-0.07473009079694748,0.6782708168029785,0.7639024257659912,-0.07473009079694748,0.6409902572631836,0.794903576374054,-0.07473009079694748,0.602116048336029,0.8239284157752991,-0.07473009079694748,0.5617449283599854,0.8509048819541931,-0.07473009079694748,0.5199771523475647,0.875765860080719,-0.07473009079694748,0.47691667079925537,0.8984495997428894,-0.07473009079694748,0.4326705038547516,0.9188995957374573,-0.07473009079694748,0.38734865188598633,0.937065064907074,-0.07473009079694748,0.34106379747390747,0.9529008269309998,-0.07473009079694748,0.2939309775829315,0.9663675427436829,-0.07473009079694748,0.24606741964817047,0.9774317145347595,-0.07473009079694748,0.19759207963943481,0.9860658645629883,-0.07473009079694748,0.14862550795078278,0.9922484755516052,-0.07473009079694748,0.0992894321680069,0.9959641695022583,-0.07473009079694748,0.049706507474184036,0.9972038269042969,-0.07473009079694748,2.4424449143961927e-16,0.9996891617774963,-0.024930691346526146,0.0,0.9984464645385742,-0.024930691346526146,-0.04983039200305939,0.9947214722633362,-0.024930691346526146,-0.09953689575195312,0.9885234832763672,-0.024930691346526146,-0.14899593591690063,0.9798678159713745,-0.024930691346526146,-0.19808456301689148,0.9687761068344116,-0.024930691346526146,-0.24668070673942566,0.9552757740020752,-0.024930691346526146,-0.29466354846954346,0.9394005537033081,-0.024930691346526146,-0.3419138491153717,0.9211897850036621,-0.024930691346526146,-0.38831406831741333,0.9006888270378113,-0.024930691346526146,-0.43374887108802795,0.8779485821723938,-0.024930691346526146,-0.4781053364276886,0.8530256748199463,-0.024930691346526146,-0.521273136138916,0.8259819746017456,-0.024930691346526146,-0.5631449818611145,0.7968847155570984,-0.024930691346526146,-0.6036167144775391,0.7658063173294067,-0.024930691346526146,-0.6425878405570984,0.7328240275382996,-0.024930691346526146,-0.6799613237380981,0.698019802570343,-0.024930691346526146,-0.715644359588623,0.661480188369751,-0.024930691346526146,-0.7495481371879578,0.6232960224151611,-0.024930691346526146,-0.7815884947776794,0.5835622549057007,-0.024930691346526146,-0.8116856217384338,0.5423776507377625,-0.024930691346526146,-0.8397648334503174,0.49984458088874817,-0.024930691346526146,-0.8657562136650085,0.45606887340545654,-0.024930691346526146,-0.8895952105522156,0.4111592769622803,-0.024930691346526146,-0.911222517490387,0.36522746086120605,-0.024930691346526146,-0.9305844306945801,0.3183876574039459,-0.024930691346526146,-0.9476327300071716,0.2707562744617462,-0.024930691346526146,-0.9623250365257263,0.22245177626609802,-0.024930691346526146,-0.9746248722076416,0.17359420657157898,-0.024930691346526146,-0.9845016598701477,0.12430505454540253,-0.024930691346526146,-0.9919307827949524,0.07470686733722687,-0.024930691346526146,-0.9968938231468201,0.024922942742705345,-0.024930691346526146,-0.9993784427642822,-0.024922942742705345,-0.024930691346526146,-0.9993784427642822,-0.07470686733722687,-0.024930691346526146,-0.9968938231468201,-0.12430505454540253,-0.024930691346526146,-0.9919307827949524,-0.17359420657157898,-0.024930691346526146,-0.9845016598701477,-0.22245177626609802,-0.024930691346526146,-0.9746248722076416,-0.2707562744617462,-0.024930691346526146,-0.9623250365257263,-0.3183876574039459,-0.024930691346526146,-0.9476327300071716,-0.36522746086120605,-0.024930691346526146,-0.9305844306945801,-0.4111592769622803,-0.024930691346526146,-0.911222517490387,-0.45606887340545654,-0.024930691346526146,-0.8895952105522156,-0.49984458088874817,-0.024930691346526146,-0.8657562136650085,-0.5423776507377625,-0.024930691346526146,-0.8397648334503174,-0.5835622549057007,-0.024930691346526146,-0.8116856217384338,-0.6232960224151611,-0.024930691346526146,-0.7815884947776794,-0.661480188369751,-0.024930691346526146,-0.7495481371879578,-0.698019802570343,-0.024930691346526146,-0.715644359588623,-0.7328240275382996,-0.024930691346526146,-0.6799613237380981,-0.7658063173294067,-0.024930691346526146,-0.6425878405570984,-0.7968847155570984,-0.024930691346526146,-0.6036167144775391,-0.8259819746017456,-0.024930691346526146,-0.5631449818611145,-0.8530256748199463,-0.024930691346526146,-0.521273136138916,-0.8779485821723938,-0.024930691346526146,-0.4781053364276886,-0.9006888270378113,-0.024930691346526146,-0.43374887108802795,-0.9211897850036621,-0.024930691346526146,-0.38831406831741333,-0.9394005537033081,-0.024930691346526146,-0.3419138491153717,-0.9552757740020752,-0.024930691346526146,-0.29466354846954346,-0.9687761068344116,-0.024930691346526146,-0.24668070673942566,-0.9798678159713745,-0.024930691346526146,-0.19808456301689148,-0.9885234832763672,-0.024930691346526146,-0.14899593591690063,-0.9947214722633362,-0.024930691346526146,-0.09953689575195312,-0.9984464645385742,-0.024930691346526146,-0.04983039200305939,-0.9996891617774963,-0.024930691346526146,-1.2242662171544955e-16,-0.9984464645385742,-0.024930691346526146,0.04983039200305939,-0.9947214722633362,-0.024930691346526146,0.09953689575195312,-0.9885234832763672,-0.024930691346526146,0.14899593591690063,-0.9798678159713745,-0.024930691346526146,0.19808456301689148,-0.9687761068344116,-0.024930691346526146,0.24668070673942566,-0.9552757740020752,-0.024930691346526146,0.29466354846954346,-0.9394005537033081,-0.024930691346526146,0.3419138491153717,-0.9211897850036621,-0.024930691346526146,0.38831406831741333,-0.9006888270378113,-0.024930691346526146,0.43374887108802795,-0.8779485821723938,-0.024930691346526146,0.4781053364276886,-0.8530256748199463,-0.024930691346526146,0.521273136138916,-0.8259819746017456,-0.024930691346526146,0.5631449818611145,-0.7968847155570984,-0.024930691346526146,0.6036167144775391,-0.7658063173294067,-0.024930691346526146,0.6425878405570984,-0.7328240275382996,-0.024930691346526146,0.6799613237380981,-0.698019802570343,-0.024930691346526146,0.715644359588623,-0.661480188369751,-0.024930691346526146,0.7495481371879578,-0.6232960224151611,-0.024930691346526146,0.7815884947776794,-0.5835622549057007,-0.024930691346526146,0.8116856217384338,-0.5423776507377625,-0.024930691346526146,0.8397648334503174,-0.49984458088874817,-0.024930691346526146,0.8657562136650085,-0.45606887340545654,-0.024930691346526146,0.8895952105522156,-0.4111592769622803,-0.024930691346526146,0.911222517490387,-0.36522746086120605,-0.024930691346526146,0.9305844306945801,-0.3183876574039459,-0.024930691346526146,0.9476327300071716,-0.2707562744617462,-0.024930691346526146,0.9623250365257263,-0.22245177626609802,-0.024930691346526146,0.9746248722076416,-0.17359420657157898,-0.024930691346526146,0.9845016598701477,-0.12430505454540253,-0.024930691346526146,0.9919307827949524,-0.07470686733722687,-0.024930691346526146,0.9968938231468201,-0.024922942742705345,-0.024930691346526146,0.9993784427642822,0.024922942742705345,-0.024930691346526146,0.9993784427642822,0.07470686733722687,-0.024930691346526146,0.9968938231468201,0.12430505454540253,-0.024930691346526146,0.9919307827949524,0.17359420657157898,-0.024930691346526146,0.9845016598701477,0.22245177626609802,-0.024930691346526146,0.9746248722076416,0.2707562744617462,-0.024930691346526146,0.9623250365257263,0.3183876574039459,-0.024930691346526146,0.9476327300071716,0.36522746086120605,-0.024930691346526146,0.9305844306945801,0.4111592769622803,-0.024930691346526146,0.911222517490387,0.45606887340545654,-0.024930691346526146,0.8895952105522156,0.49984458088874817,-0.024930691346526146,0.8657562136650085,0.5423776507377625,-0.024930691346526146,0.8397648334503174,0.5835622549057007,-0.024930691346526146,0.8116856217384338,0.6232960224151611,-0.024930691346526146,0.7815884947776794,0.661480188369751,-0.024930691346526146,0.7495481371879578,0.698019802570343,-0.024930691346526146,0.715644359588623,0.7328240275382996,-0.024930691346526146,0.6799613237380981,0.7658063173294067,-0.024930691346526146,0.6425878405570984,0.7968847155570984,-0.024930691346526146,0.6036167144775391,0.8259819746017456,-0.024930691346526146,0.5631449818611145,0.8530256748199463,-0.024930691346526146,0.521273136138916,0.8779485821723938,-0.024930691346526146,0.4781053364276886,0.9006888270378113,-0.024930691346526146,0.43374887108802795,0.9211897850036621,-0.024930691346526146,0.38831406831741333,0.9394005537033081,-0.024930691346526146,0.3419138491153717,0.9552757740020752,-0.024930691346526146,0.29466354846954346,0.9687761068344116,-0.024930691346526146,0.24668070673942566,0.9798678159713745,-0.024930691346526146,0.19808456301689148,0.9885234832763672,-0.024930691346526146,0.14899593591690063,0.9947214722633362,-0.024930691346526146,0.09953689575195312,0.9984464645385742,-0.024930691346526146,0.04983039200305939,0.9996891617774963,-0.024930691346526146,2.448532434308991e-16,0.9996891617774963,0.024930691346526146,0.0,0.9984464645385742,0.024930691346526146,-0.04983039200305939,0.9947214722633362,0.024930691346526146,-0.09953689575195312,0.9885234832763672,0.024930691346526146,-0.14899593591690063,0.9798678159713745,0.024930691346526146,-0.19808456301689148,0.9687761068344116,0.024930691346526146,-0.24668070673942566,0.9552757740020752,0.024930691346526146,-0.29466354846954346,0.9394005537033081,0.024930691346526146,-0.3419138491153717,0.9211897850036621,0.024930691346526146,-0.38831406831741333,0.9006888270378113,0.024930691346526146,-0.43374887108802795,0.8779485821723938,0.024930691346526146,-0.4781053364276886,0.8530256748199463,0.024930691346526146,-0.521273136138916,0.8259819746017456,0.024930691346526146,-0.5631449818611145,0.7968847155570984,0.024930691346526146,-0.6036167144775391,0.7658063173294067,0.024930691346526146,-0.6425878405570984,0.7328240275382996,0.024930691346526146,-0.6799613237380981,0.698019802570343,0.024930691346526146,-0.715644359588623,0.661480188369751,0.024930691346526146,-0.7495481371879578,0.6232960224151611,0.024930691346526146,-0.7815884947776794,0.5835622549057007,0.024930691346526146,-0.8116856217384338,0.5423776507377625,0.024930691346526146,-0.8397648334503174,0.49984458088874817,0.024930691346526146,-0.8657562136650085,0.45606887340545654,0.024930691346526146,-0.8895952105522156,0.4111592769622803,0.024930691346526146,-0.911222517490387,0.36522746086120605,0.024930691346526146,-0.9305844306945801,0.3183876574039459,0.024930691346526146,-0.9476327300071716,0.2707562744617462,0.024930691346526146,-0.9623250365257263,0.22245177626609802,0.024930691346526146,-0.9746248722076416,0.17359420657157898,0.024930691346526146,-0.9845016598701477,0.12430505454540253,0.024930691346526146,-0.9919307827949524,0.07470686733722687,0.024930691346526146,-0.9968938231468201,0.024922942742705345,0.024930691346526146,-0.9993784427642822,-0.024922942742705345,0.024930691346526146,-0.9993784427642822,-0.07470686733722687,0.024930691346526146,-0.9968938231468201,-0.12430505454540253,0.024930691346526146,-0.9919307827949524,-0.17359420657157898,0.024930691346526146,-0.9845016598701477,-0.22245177626609802,0.024930691346526146,-0.9746248722076416,-0.2707562744617462,0.024930691346526146,-0.9623250365257263,-0.3183876574039459,0.024930691346526146,-0.9476327300071716,-0.36522746086120605,0.024930691346526146,-0.9305844306945801,-0.4111592769622803,0.024930691346526146,-0.911222517490387,-0.45606887340545654,0.024930691346526146,-0.8895952105522156,-0.49984458088874817,0.024930691346526146,-0.8657562136650085,-0.5423776507377625,0.024930691346526146,-0.8397648334503174,-0.5835622549057007,0.024930691346526146,-0.8116856217384338,-0.6232960224151611,0.024930691346526146,-0.7815884947776794,-0.661480188369751,0.024930691346526146,-0.7495481371879578,-0.698019802570343,0.024930691346526146,-0.715644359588623,-0.7328240275382996,0.024930691346526146,-0.6799613237380981,-0.7658063173294067,0.024930691346526146,-0.6425878405570984,-0.7968847155570984,0.024930691346526146,-0.6036167144775391,-0.8259819746017456,0.024930691346526146,-0.5631449818611145,-0.8530256748199463,0.024930691346526146,-0.521273136138916,-0.8779485821723938,0.024930691346526146,-0.4781053364276886,-0.9006888270378113,0.024930691346526146,-0.43374887108802795,-0.9211897850036621,0.024930691346526146,-0.38831406831741333,-0.9394005537033081,0.024930691346526146,-0.3419138491153717,-0.9552757740020752,0.024930691346526146,-0.29466354846954346,-0.9687761068344116,0.024930691346526146,-0.24668070673942566,-0.9798678159713745,0.024930691346526146,-0.19808456301689148,-0.9885234832763672,0.024930691346526146,-0.14899593591690063,-0.9947214722633362,0.024930691346526146,-0.09953689575195312,-0.9984464645385742,0.024930691346526146,-0.04983039200305939,-0.9996891617774963,0.024930691346526146,-1.2242662171544955e-16,-0.9984464645385742,0.024930691346526146,0.04983039200305939,-0.9947214722633362,0.024930691346526146,0.09953689575195312,-0.9885234832763672,0.024930691346526146,0.14899593591690063,-0.9798678159713745,0.024930691346526146,0.19808456301689148,-0.9687761068344116,0.024930691346526146,0.24668070673942566,-0.9552757740020752,0.024930691346526146,0.29466354846954346,-0.9394005537033081,0.024930691346526146,0.3419138491153717,-0.9211897850036621,0.024930691346526146,0.38831406831741333,-0.9006888270378113,0.024930691346526146,0.43374887108802795,-0.8779485821723938,0.024930691346526146,0.4781053364276886,-0.8530256748199463,0.024930691346526146,0.521273136138916,-0.8259819746017456,0.024930691346526146,0.5631449818611145,-0.7968847155570984,0.024930691346526146,0.6036167144775391,-0.7658063173294067,0.024930691346526146,0.6425878405570984,-0.7328240275382996,0.024930691346526146,0.6799613237380981,-0.698019802570343,0.024930691346526146,0.715644359588623,-0.661480188369751,0.024930691346526146,0.7495481371879578,-0.6232960224151611,0.024930691346526146,0.7815884947776794,-0.5835622549057007,0.024930691346526146,0.8116856217384338,-0.5423776507377625,0.024930691346526146,0.8397648334503174,-0.49984458088874817,0.024930691346526146,0.8657562136650085,-0.45606887340545654,0.024930691346526146,0.8895952105522156,-0.4111592769622803,0.024930691346526146,0.911222517490387,-0.36522746086120605,0.024930691346526146,0.9305844306945801,-0.3183876574039459,0.024930691346526146,0.9476327300071716,-0.2707562744617462,0.024930691346526146,0.9623250365257263,-0.22245177626609802,0.024930691346526146,0.9746248722076416,-0.17359420657157898,0.024930691346526146,0.9845016598701477,-0.12430505454540253,0.024930691346526146,0.9919307827949524,-0.07470686733722687,0.024930691346526146,0.9968938231468201,-0.024922942742705345,0.024930691346526146,0.9993784427642822,0.024922942742705345,0.024930691346526146,0.9993784427642822,0.07470686733722687,0.024930691346526146,0.9968938231468201,0.12430505454540253,0.024930691346526146,0.9919307827949524,0.17359420657157898,0.024930691346526146,0.9845016598701477,0.22245177626609802,0.024930691346526146,0.9746248722076416,0.2707562744617462,0.024930691346526146,0.9623250365257263,0.3183876574039459,0.024930691346526146,0.9476327300071716,0.36522746086120605,0.024930691346526146,0.9305844306945801,0.4111592769622803,0.024930691346526146,0.911222517490387,0.45606887340545654,0.024930691346526146,0.8895952105522156,0.49984458088874817,0.024930691346526146,0.8657562136650085,0.5423776507377625,0.024930691346526146,0.8397648334503174,0.5835622549057007,0.024930691346526146,0.8116856217384338,0.6232960224151611,0.024930691346526146,0.7815884947776794,0.661480188369751,0.024930691346526146,0.7495481371879578,0.698019802570343,0.024930691346526146,0.715644359588623,0.7328240275382996,0.024930691346526146,0.6799613237380981,0.7658063173294067,0.024930691346526146,0.6425878405570984,0.7968847155570984,0.024930691346526146,0.6036167144775391,0.8259819746017456,0.024930691346526146,0.5631449818611145,0.8530256748199463,0.024930691346526146,0.521273136138916,0.8779485821723938,0.024930691346526146,0.4781053364276886,0.9006888270378113,0.024930691346526146,0.43374887108802795,0.9211897850036621,0.024930691346526146,0.38831406831741333,0.9394005537033081,0.024930691346526146,0.3419138491153717,0.9552757740020752,0.024930691346526146,0.29466354846954346,0.9687761068344116,0.024930691346526146,0.24668070673942566,0.9798678159713745,0.024930691346526146,0.19808456301689148,0.9885234832763672,0.024930691346526146,0.14899593591690063,0.9947214722633362,0.024930691346526146,0.09953689575195312,0.9984464645385742,0.024930691346526146,0.04983039200305939,0.9996891617774963,0.024930691346526146,2.448532434308991e-16,0.9972038269042969,0.07473009079694748,0.0,0.9959641695022583,0.07473009079694748,-0.049706507474184036,0.9922484755516052,0.07473009079694748,-0.0992894321680069,0.9860658645629883,0.07473009079694748,-0.14862550795078278,0.9774317145347595,0.07473009079694748,-0.19759207963943481,0.9663675427436829,0.07473009079694748,-0.24606741964817047,0.9529008269309998,0.07473009079694748,-0.2939309775829315,0.937065064907074,0.07473009079694748,-0.34106379747390747,0.9188995957374573,0.07473009079694748,-0.38734865188598633,0.8984495997428894,0.07473009079694748,-0.4326705038547516,0.875765860080719,0.07473009079694748,-0.47691667079925537,0.8509048819541931,0.07473009079694748,-0.5199771523475647,0.8239284157752991,0.07473009079694748,-0.5617449283599854,0.794903576374054,0.07473009079694748,-0.602116048336029,0.7639024257659912,0.07473009079694748,-0.6409902572631836,0.7310020923614502,0.07473009079694748,-0.6782708168029785,0.6962844133377075,0.07473009079694748,-0.7138651609420776,0.6598356366157532,0.07473009079694748,-0.7476846575737,0.6217464208602905,0.07473009079694748,-0.7796453237533569,0.5821114182472229,0.07473009079694748,-0.8096676468849182,0.5410292148590088,0.07473009079694748,-0.8376770615577698,0.49860191345214844,0.07473009079694748,-0.8636038303375244,0.4549350142478943,0.07473009079694748,-0.8873835206031799,0.4101370573043823,0.07473009079694748,-0.9089571237564087,0.36431944370269775,0.07473009079694748,-0.9282708168029785,0.31759610772132874,0.07473009079694748,-0.9452767372131348,0.27008312940597534,0.07473009079694748,-0.9599325656890869,0.22189871966838837,0.07473009079694748,-0.9722018241882324,0.17316262423992157,0.07473009079694748,-0.9820540547370911,0.12399601191282272,0.07473009079694748,-0.9894647002220154,0.07452113181352615,0.07473009079694748,-0.9944154024124146,0.024860979989171028,0.07473009079694748,-0.9968938231468201,-0.024860979989171028,0.07473009079694748,-0.9968938231468201,-0.07452113181352615,0.07473009079694748,-0.9944154024124146,-0.12399601191282272,0.07473009079694748,-0.9894647002220154,-0.17316262423992157,0.07473009079694748,-0.9820540547370911,-0.22189871966838837,0.07473009079694748,-0.9722018241882324,-0.27008312940597534,0.07473009079694748,-0.9599325656890869,-0.31759610772132874,0.07473009079694748,-0.9452767372131348,-0.36431944370269775,0.07473009079694748,-0.9282708168029785,-0.4101370573043823,0.07473009079694748,-0.9089571237564087,-0.4549350142478943,0.07473009079694748,-0.8873835206031799,-0.49860191345214844,0.07473009079694748,-0.8636038303375244,-0.5410292148590088,0.07473009079694748,-0.8376770615577698,-0.5821114182472229,0.07473009079694748,-0.8096676468849182,-0.6217464208602905,0.07473009079694748,-0.7796453237533569,-0.6598356366157532,0.07473009079694748,-0.7476846575737,-0.6962844133377075,0.07473009079694748,-0.7138651609420776,-0.7310020923614502,0.07473009079694748,-0.6782708168029785,-0.7639024257659912,0.07473009079694748,-0.6409902572631836,-0.794903576374054,0.07473009079694748,-0.602116048336029,-0.8239284157752991,0.07473009079694748,-0.5617449283599854,-0.8509048819541931,0.07473009079694748,-0.5199771523475647,-0.875765860080719,0.07473009079694748,-0.47691667079925537,-0.8984495997428894,0.07473009079694748,-0.4326705038547516,-0.9188995957374573,0.07473009079694748,-0.38734865188598633,-0.937065064907074,0.07473009079694748,-0.34106379747390747,-0.9529008269309998,0.07473009079694748,-0.2939309775829315,-0.9663675427436829,0.07473009079694748,-0.24606741964817047,-0.9774317145347595,0.07473009079694748,-0.19759207963943481,-0.9860658645629883,0.07473009079694748,-0.14862550795078278,-0.9922484755516052,0.07473009079694748,-0.0992894321680069,-0.9959641695022583,0.07473009079694748,-0.049706507474184036,-0.9972038269042969,0.07473009079694748,-1.2212224571980963e-16,-0.9959641695022583,0.07473009079694748,0.049706507474184036,-0.9922484755516052,0.07473009079694748,0.0992894321680069,-0.9860658645629883,0.07473009079694748,0.14862550795078278,-0.9774317145347595,0.07473009079694748,0.19759207963943481,-0.9663675427436829,0.07473009079694748,0.24606741964817047,-0.9529008269309998,0.07473009079694748,0.2939309775829315,-0.937065064907074,0.07473009079694748,0.34106379747390747,-0.9188995957374573,0.07473009079694748,0.38734865188598633,-0.8984495997428894,0.07473009079694748,0.4326705038547516,-0.875765860080719,0.07473009079694748,0.47691667079925537,-0.8509048819541931,0.07473009079694748,0.5199771523475647,-0.8239284157752991,0.07473009079694748,0.5617449283599854,-0.794903576374054,0.07473009079694748,0.602116048336029,-0.7639024257659912,0.07473009079694748,0.6409902572631836,-0.7310020923614502,0.07473009079694748,0.6782708168029785,-0.6962844133377075,0.07473009079694748,0.7138651609420776,-0.6598356366157532,0.07473009079694748,0.7476846575737,-0.6217464208602905,0.07473009079694748,0.7796453237533569,-0.5821114182472229,0.07473009079694748,0.8096676468849182,-0.5410292148590088,0.07473009079694748,0.8376770615577698,-0.49860191345214844,0.07473009079694748,0.8636038303375244,-0.4549350142478943,0.07473009079694748,0.8873835206031799,-0.4101370573043823,0.07473009079694748,0.9089571237564087,-0.36431944370269775,0.07473009079694748,0.9282708168029785,-0.31759610772132874,0.07473009079694748,0.9452767372131348,-0.27008312940597534,0.07473009079694748,0.9599325656890869,-0.22189871966838837,0.07473009079694748,0.9722018241882324,-0.17316262423992157,0.07473009079694748,0.9820540547370911,-0.12399601191282272,0.07473009079694748,0.9894647002220154,-0.07452113181352615,0.07473009079694748,0.9944154024124146,-0.024860979989171028,0.07473009079694748,0.9968938231468201,0.024860979989171028,0.07473009079694748,0.9968938231468201,0.07452113181352615,0.07473009079694748,0.9944154024124146,0.12399601191282272,0.07473009079694748,0.9894647002220154,0.17316262423992157,0.07473009079694748,0.9820540547370911,0.22189871966838837,0.07473009079694748,0.9722018241882324,0.27008312940597534,0.07473009079694748,0.9599325656890869,0.31759610772132874,0.07473009079694748,0.9452767372131348,0.36431944370269775,0.07473009079694748,0.9282708168029785,0.4101370573043823,0.07473009079694748,0.9089571237564087,0.4549350142478943,0.07473009079694748,0.8873835206031799,0.49860191345214844,0.07473009079694748,0.8636038303375244,0.5410292148590088,0.07473009079694748,0.8376770615577698,0.5821114182472229,0.07473009079694748,0.8096676468849182,0.6217464208602905,0.07473009079694748,0.7796453237533569,0.6598356366157532,0.07473009079694748,0.7476846575737,0.6962844133377075,0.07473009079694748,0.7138651609420776,0.7310020923614502,0.07473009079694748,0.6782708168029785,0.7639024257659912,0.07473009079694748,0.6409902572631836,0.794903576374054,0.07473009079694748,0.602116048336029,0.8239284157752991,0.07473009079694748,0.5617449283599854,0.8509048819541931,0.07473009079694748,0.5199771523475647,0.875765860080719,0.07473009079694748,0.47691667079925537,0.8984495997428894,0.07473009079694748,0.4326705038547516,0.9188995957374573,0.07473009079694748,0.38734865188598633,0.937065064907074,0.07473009079694748,0.34106379747390747,0.9529008269309998,0.07473009079694748,0.2939309775829315,0.9663675427436829,0.07473009079694748,0.24606741964817047,0.9774317145347595,0.07473009079694748,0.19759207963943481,0.9860658645629883,0.07473009079694748,0.14862550795078278,0.9922484755516052,0.07473009079694748,0.0992894321680069,0.9959641695022583,0.07473009079694748,0.049706507474184036,0.9972038269042969,0.07473009079694748,2.4424449143961927e-16,0.9922391772270203,0.12434370815753937,0.0,0.9910057783126831,0.12434370815753937,-0.04945904389023781,0.9873085618019104,0.12434370815753937,-0.09879512339830399,0.9811567068099976,0.12434370815753937,-0.14788557589054108,0.9725655913352966,0.12434370815753937,-0.19660836458206177,0.9615564942359924,0.12434370815753937,-0.24484236538410187,0.9481567740440369,0.12434370815753937,-0.2924676537513733,0.9323998689651489,0.12434370815753937,-0.33936581015586853,0.9143248200416565,0.12434370815753937,-0.3854202330112457,0.893976628780365,0.12434370815753937,-0.43051645159721375,0.8714058995246887,0.12434370815753937,-0.4745423495769501,0.8466686606407166,0.12434370815753937,-0.5173884630203247,0.8198264837265015,0.12434370815753937,-0.5589482188224792,0.7909461259841919,0.12434370815753937,-0.5991184115409851,0.7600993514060974,0.12434370815753937,-0.637799084186554,0.7273628115653992,0.12434370815753937,-0.6748940348625183,0.6928179264068604,0.12434370815753937,-0.7103111743927002,0.6565506458282471,0.12434370815753937,-0.743962287902832,0.6186510324478149,0.12434370815753937,-0.7757638692855835,0.5792133808135986,0.12434370815753937,-0.8056367039680481,0.5383356809616089,0.12434370815753937,-0.8335066437721252,0.49611958861351013,0.12434370815753937,-0.8593043684959412,0.4526700973510742,0.12434370815753937,-0.8829656839370728,0.40809518098831177,0.12434370815753937,-0.9044318199157715,0.3625056743621826,0.12434370815753937,-0.9236494302749634,0.31601494550704956,0.12434370815753937,-0.9405706524848938,0.2687385380268097,0.12434370815753937,-0.9551535248756409,0.22079399228096008,0.12434370815753937,-0.9673616886138916,0.1723005324602127,0.12434370815753937,-0.9771648645401001,0.1233787015080452,0.12434370815753937,-0.9845386147499084,0.07415013015270233,0.12434370815753937,-0.9894647002220154,0.02473720908164978,0.12434370815753937,-0.9919307827949524,-0.02473720908164978,0.12434370815753937,-0.9919307827949524,-0.07415013015270233,0.12434370815753937,-0.9894647002220154,-0.1233787015080452,0.12434370815753937,-0.9845386147499084,-0.1723005324602127,0.12434370815753937,-0.9771648645401001,-0.22079399228096008,0.12434370815753937,-0.9673616886138916,-0.2687385380268097,0.12434370815753937,-0.9551535248756409,-0.31601494550704956,0.12434370815753937,-0.9405706524848938,-0.3625056743621826,0.12434370815753937,-0.9236494302749634,-0.40809518098831177,0.12434370815753937,-0.9044318199157715,-0.4526700973510742,0.12434370815753937,-0.8829656839370728,-0.49611958861351013,0.12434370815753937,-0.8593043684959412,-0.5383356809616089,0.12434370815753937,-0.8335066437721252,-0.5792133808135986,0.12434370815753937,-0.8056367039680481,-0.6186510324478149,0.12434370815753937,-0.7757638692855835,-0.6565506458282471,0.12434370815753937,-0.743962287902832,-0.6928179264068604,0.12434370815753937,-0.7103111743927002,-0.7273628115653992,0.12434370815753937,-0.6748940348625183,-0.7600993514060974,0.12434370815753937,-0.637799084186554,-0.7909461259841919,0.12434370815753937,-0.5991184115409851,-0.8198264837265015,0.12434370815753937,-0.5589482188224792,-0.8466686606407166,0.12434370815753937,-0.5173884630203247,-0.8714058995246887,0.12434370815753937,-0.4745423495769501,-0.893976628780365,0.12434370815753937,-0.43051645159721375,-0.9143248200416565,0.12434370815753937,-0.3854202330112457,-0.9323998689651489,0.12434370815753937,-0.33936581015586853,-0.9481567740440369,0.12434370815753937,-0.2924676537513733,-0.9615564942359924,0.12434370815753937,-0.24484236538410187,-0.9725655913352966,0.12434370815753937,-0.19660836458206177,-0.9811567068099976,0.12434370815753937,-0.14788557589054108,-0.9873085618019104,0.12434370815753937,-0.09879512339830399,-0.9910057783126831,0.12434370815753937,-0.04945904389023781,-0.9922391772270203,0.12434370815753937,-1.2151426135213826e-16,-0.9910057783126831,0.12434370815753937,0.04945904389023781,-0.9873085618019104,0.12434370815753937,0.09879512339830399,-0.9811567068099976,0.12434370815753937,0.14788557589054108,-0.9725655913352966,0.12434370815753937,0.19660836458206177,-0.9615564942359924,0.12434370815753937,0.24484236538410187,-0.9481567740440369,0.12434370815753937,0.2924676537513733,-0.9323998689651489,0.12434370815753937,0.33936581015586853,-0.9143248200416565,0.12434370815753937,0.3854202330112457,-0.893976628780365,0.12434370815753937,0.43051645159721375,-0.8714058995246887,0.12434370815753937,0.4745423495769501,-0.8466686606407166,0.12434370815753937,0.5173884630203247,-0.8198264837265015,0.12434370815753937,0.5589482188224792,-0.7909461259841919,0.12434370815753937,0.5991184115409851,-0.7600993514060974,0.12434370815753937,0.637799084186554,-0.7273628115653992,0.12434370815753937,0.6748940348625183,-0.6928179264068604,0.12434370815753937,0.7103111743927002,-0.6565506458282471,0.12434370815753937,0.743962287902832,-0.6186510324478149,0.12434370815753937,0.7757638692855835,-0.5792133808135986,0.12434370815753937,0.8056367039680481,-0.5383356809616089,0.12434370815753937,0.8335066437721252,-0.49611958861351013,0.12434370815753937,0.8593043684959412,-0.4526700973510742,0.12434370815753937,0.8829656839370728,-0.40809518098831177,0.12434370815753937,0.9044318199157715,-0.3625056743621826,0.12434370815753937,0.9236494302749634,-0.31601494550704956,0.12434370815753937,0.9405706524848938,-0.2687385380268097,0.12434370815753937,0.9551535248756409,-0.22079399228096008,0.12434370815753937,0.9673616886138916,-0.1723005324602127,0.12434370815753937,0.9771648645401001,-0.1233787015080452,0.12434370815753937,0.9845386147499084,-0.07415013015270233,0.12434370815753937,0.9894647002220154,-0.02473720908164978,0.12434370815753937,0.9919307827949524,0.02473720908164978,0.12434370815753937,0.9919307827949524,0.07415013015270233,0.12434370815753937,0.9894647002220154,0.1233787015080452,0.12434370815753937,0.9845386147499084,0.1723005324602127,0.12434370815753937,0.9771648645401001,0.22079399228096008,0.12434370815753937,0.9673616886138916,0.2687385380268097,0.12434370815753937,0.9551535248756409,0.31601494550704956,0.12434370815753937,0.9405706524848938,0.3625056743621826,0.12434370815753937,0.9236494302749634,0.40809518098831177,0.12434370815753937,0.9044318199157715,0.4526700973510742,0.12434370815753937,0.8829656839370728,0.49611958861351013,0.12434370815753937,0.8593043684959412,0.5383356809616089,0.12434370815753937,0.8335066437721252,0.5792133808135986,0.12434370815753937,0.8056367039680481,0.6186510324478149,0.12434370815753937,0.7757638692855835,0.6565506458282471,0.12434370815753937,0.743962287902832,0.6928179264068604,0.12434370815753937,0.7103111743927002,0.7273628115653992,0.12434370815753937,0.6748940348625183,0.7600993514060974,0.12434370815753937,0.637799084186554,0.7909461259841919,0.12434370815753937,0.5991184115409851,0.8198264837265015,0.12434370815753937,0.5589482188224792,0.8466686606407166,0.12434370815753937,0.5173884630203247,0.8714058995246887,0.12434370815753937,0.4745423495769501,0.893976628780365,0.12434370815753937,0.43051645159721375,0.9143248200416565,0.12434370815753937,0.3854202330112457,0.9323998689651489,0.12434370815753937,0.33936581015586853,0.9481567740440369,0.12434370815753937,0.2924676537513733,0.9615564942359924,0.12434370815753937,0.24484236538410187,0.9725655913352966,0.12434370815753937,0.19660836458206177,0.9811567068099976,0.12434370815753937,0.14788557589054108,0.9873085618019104,0.12434370815753937,0.09879512339830399,0.9910057783126831,0.12434370815753937,0.04945904389023781,0.9922391772270203,0.12434370815753937,2.430285227042765e-16,0.9848077297210693,0.1736481785774231,0.0,0.9835835695266724,0.1736481785774231,-0.04908861592411995,0.9799140095710754,0.1736481785774231,-0.09805518388748169,0.9738082885742188,0.1736481785774231,-0.14677797257900238,0.9652814865112305,0.1736481785774231,-0.19513586163520813,0.9543548226356506,0.1736481785774231,-0.2430085986852646,0.9410555362701416,0.1736481785774231,-0.2902771830558777,0.9254165887832642,0.1736481785774231,-0.33682408928871155,0.9074769020080566,0.1736481785774231,-0.38253360986709595,0.8872811198234558,0.1736481785774231,-0.4272920787334442,0.8648794293403625,0.1736481785774231,-0.4709882140159607,0.8403275012969971,0.1736481785774231,-0.513513445854187,0.8136863708496094,0.1736481785774231,-0.5547619462013245,0.7850222587585449,0.1736481785774231,-0.5946312546730042,0.7544065117835999,0.1736481785774231,-0.6330222487449646,0.7219151854515076,0.1736481785774231,-0.6698393821716309,0.6876290440559387,0.1736481785774231,-0.7049912214279175,0.6516333222389221,0.1736481785774231,-0.738390326499939,0.6140176057815552,0.1736481785774231,-0.769953727722168,0.5748752951622009,0.1736481785774231,-0.7996028661727905,0.5343037843704224,0.1736481785774231,-0.8272640705108643,0.49240386486053467,0.1736481785774231,-0.8528685569763184,0.44927978515625,0.1736481785774231,-0.8763526678085327,0.4050387144088745,0.1736481785774231,-0.8976580500602722,0.3597906827926636,0.1736481785774231,-0.9167316555976868,0.31364813446998596,0.1736481785774231,-0.9335262179374695,0.2667257785797119,0.1736481785774231,-0.9479998350143433,0.21914033591747284,0.1736481785774231,-0.9601165652275085,0.1710100769996643,0.1736481785774231,-0.9698463082313538,0.12245464324951172,0.1736481785774231,-0.9771648645401001,0.07359477877616882,0.1736481785774231,-0.9820540547370911,0.024551939219236374,0.1736481785774231,-0.9845016598701477,-0.024551939219236374,0.1736481785774231,-0.9845016598701477,-0.07359477877616882,0.1736481785774231,-0.9820540547370911,-0.12245464324951172,0.1736481785774231,-0.9771648645401001,-0.1710100769996643,0.1736481785774231,-0.9698463082313538,-0.21914033591747284,0.1736481785774231,-0.9601165652275085,-0.2667257785797119,0.1736481785774231,-0.9479998350143433,-0.31364813446998596,0.1736481785774231,-0.9335262179374695,-0.3597906827926636,0.1736481785774231,-0.9167316555976868,-0.4050387144088745,0.1736481785774231,-0.8976580500602722,-0.44927978515625,0.1736481785774231,-0.8763526678085327,-0.49240386486053467,0.1736481785774231,-0.8528685569763184,-0.5343037843704224,0.1736481785774231,-0.8272640705108643,-0.5748752951622009,0.1736481785774231,-0.7996028661727905,-0.6140176057815552,0.1736481785774231,-0.769953727722168,-0.6516333222389221,0.1736481785774231,-0.738390326499939,-0.6876290440559387,0.1736481785774231,-0.7049912214279175,-0.7219151854515076,0.1736481785774231,-0.6698393821716309,-0.7544065117835999,0.1736481785774231,-0.6330222487449646,-0.7850222587585449,0.1736481785774231,-0.5946312546730042,-0.8136863708496094,0.1736481785774231,-0.5547619462013245,-0.8403275012969971,0.1736481785774231,-0.513513445854187,-0.8648794293403625,0.1736481785774231,-0.4709882140159607,-0.8872811198234558,0.1736481785774231,-0.4272920787334442,-0.9074769020080566,0.1736481785774231,-0.38253360986709595,-0.9254165887832642,0.1736481785774231,-0.33682408928871155,-0.9410555362701416,0.1736481785774231,-0.2902771830558777,-0.9543548226356506,0.1736481785774231,-0.2430085986852646,-0.9652814865112305,0.1736481785774231,-0.19513586163520813,-0.9738082885742188,0.1736481785774231,-0.14677797257900238,-0.9799140095710754,0.1736481785774231,-0.09805518388748169,-0.9835835695266724,0.1736481785774231,-0.04908861592411995,-0.9848077297210693,0.1736481785774231,-1.2060416415498291e-16,-0.9835835695266724,0.1736481785774231,0.04908861592411995,-0.9799140095710754,0.1736481785774231,0.09805518388748169,-0.9738082885742188,0.1736481785774231,0.14677797257900238,-0.9652814865112305,0.1736481785774231,0.19513586163520813,-0.9543548226356506,0.1736481785774231,0.2430085986852646,-0.9410555362701416,0.1736481785774231,0.2902771830558777,-0.9254165887832642,0.1736481785774231,0.33682408928871155,-0.9074769020080566,0.1736481785774231,0.38253360986709595,-0.8872811198234558,0.1736481785774231,0.4272920787334442,-0.8648794293403625,0.1736481785774231,0.4709882140159607,-0.8403275012969971,0.1736481785774231,0.513513445854187,-0.8136863708496094,0.1736481785774231,0.5547619462013245,-0.7850222587585449,0.1736481785774231,0.5946312546730042,-0.7544065117835999,0.1736481785774231,0.6330222487449646,-0.7219151854515076,0.1736481785774231,0.6698393821716309,-0.6876290440559387,0.1736481785774231,0.7049912214279175,-0.6516333222389221,0.1736481785774231,0.738390326499939,-0.6140176057815552,0.1736481785774231,0.769953727722168,-0.5748752951622009,0.1736481785774231,0.7996028661727905,-0.5343037843704224,0.1736481785774231,0.8272640705108643,-0.49240386486053467,0.1736481785774231,0.8528685569763184,-0.44927978515625,0.1736481785774231,0.8763526678085327,-0.4050387144088745,0.1736481785774231,0.8976580500602722,-0.3597906827926636,0.1736481785774231,0.9167316555976868,-0.31364813446998596,0.1736481785774231,0.9335262179374695,-0.2667257785797119,0.1736481785774231,0.9479998350143433,-0.21914033591747284,0.1736481785774231,0.9601165652275085,-0.1710100769996643,0.1736481785774231,0.9698463082313538,-0.12245464324951172,0.1736481785774231,0.9771648645401001,-0.07359477877616882,0.1736481785774231,0.9820540547370911,-0.024551939219236374,0.1736481785774231,0.9845016598701477,0.024551939219236374,0.1736481785774231,0.9845016598701477,0.07359477877616882,0.1736481785774231,0.9820540547370911,0.12245464324951172,0.1736481785774231,0.9771648645401001,0.1710100769996643,0.1736481785774231,0.9698463082313538,0.21914033591747284,0.1736481785774231,0.9601165652275085,0.2667257785797119,0.1736481785774231,0.9479998350143433,0.31364813446998596,0.1736481785774231,0.9335262179374695,0.3597906827926636,0.1736481785774231,0.9167316555976868,0.4050387144088745,0.1736481785774231,0.8976580500602722,0.44927978515625,0.1736481785774231,0.8763526678085327,0.49240386486053467,0.1736481785774231,0.8528685569763184,0.5343037843704224,0.1736481785774231,0.8272640705108643,0.5748752951622009,0.1736481785774231,0.7996028661727905,0.6140176057815552,0.1736481785774231,0.769953727722168,0.6516333222389221,0.1736481785774231,0.738390326499939,0.6876290440559387,0.1736481785774231,0.7049912214279175,0.7219151854515076,0.1736481785774231,0.6698393821716309,0.7544065117835999,0.1736481785774231,0.6330222487449646,0.7850222587585449,0.1736481785774231,0.5946312546730042,0.8136863708496094,0.1736481785774231,0.5547619462013245,0.8403275012969971,0.1736481785774231,0.513513445854187,0.8648794293403625,0.1736481785774231,0.4709882140159607,0.8872811198234558,0.1736481785774231,0.4272920787334442,0.9074769020080566,0.1736481785774231,0.38253360986709595,0.9254165887832642,0.1736481785774231,0.33682408928871155,0.9410555362701416,0.1736481785774231,0.2902771830558777,0.9543548226356506,0.1736481785774231,0.2430085986852646,0.9652814865112305,0.1736481785774231,0.19513586163520813,0.9738082885742188,0.1736481785774231,0.14677797257900238,0.9799140095710754,0.1736481785774231,0.09805518388748169,0.9835835695266724,0.1736481785774231,0.04908861592411995,0.9848077297210693,0.1736481785774231,2.4120832830996583e-16,0.9749279022216797,0.22252093255519867,0.0,0.9737160205841064,0.22252093255519867,-0.04859614372253418,0.9700832962989807,0.22252093255519867,-0.09707147628068924,0.9640387892723083,0.22252093255519867,-0.14530546963214874,0.9555975198745728,0.22252093255519867,-0.1931782066822052,0.9447804689407349,0.22252093255519867,-0.24057067930698395,0.9316145777702332,0.22252093255519867,-0.28736504912376404,0.9161325693130493,0.22252093255519867,-0.3334449827671051,0.8983728885650635,0.22252093255519867,-0.37869593501091003,0.8783797025680542,0.22252093255519867,-0.4230053722858429,0.8562027215957642,0.22252093255519867,-0.46626314520835876,0.8318970799446106,0.22252093255519867,-0.5083617568016052,0.8055232167243958,0.22252093255519867,-0.5491964221000671,0.7771467566490173,0.22252093255519867,-0.588665783405304,0.7468380928039551,0.22252093255519867,-0.626671552658081,0.7146727442741394,0.22252093255519867,-0.663119375705719,0.6807305812835693,0.22252093255519867,-0.6979185938835144,0.6450960040092468,0.22252093255519867,-0.7309826016426086,0.6078575849533081,0.22252093255519867,-0.762229323387146,0.5691080093383789,0.22252093255519867,-0.7915810346603394,0.5289434790611267,0.22252093255519867,-0.8189647197723389,0.48746395111083984,0.22252093255519867,-0.844312310218811,0.44477251172065735,0.22252093255519867,-0.867560863494873,0.4009752869606018,0.22252093255519867,-0.888652503490448,0.35618117451667786,0.22252093255519867,-0.9075347781181335,0.3105015158653259,0.22252093255519867,-0.9241608381271362,0.2640499174594879,0.22252093255519867,-0.9384892582893372,0.21694186329841614,0.22252093255519867,-0.9504844546318054,0.16929446160793304,0.22252093255519867,-0.9601165652275085,0.12122614681720734,0.22252093255519867,-0.9673616886138916,0.07285645604133606,0.22252093255519867,-0.9722018241882324,0.02430562674999237,0.22252093255519867,-0.9746248722076416,-0.02430562674999237,0.22252093255519867,-0.9746248722076416,-0.07285645604133606,0.22252093255519867,-0.9722018241882324,-0.12122614681720734,0.22252093255519867,-0.9673616886138916,-0.16929446160793304,0.22252093255519867,-0.9601165652275085,-0.21694186329841614,0.22252093255519867,-0.9504844546318054,-0.2640499174594879,0.22252093255519867,-0.9384892582893372,-0.3105015158653259,0.22252093255519867,-0.9241608381271362,-0.35618117451667786,0.22252093255519867,-0.9075347781181335,-0.4009752869606018,0.22252093255519867,-0.888652503490448,-0.44477251172065735,0.22252093255519867,-0.867560863494873,-0.48746395111083984,0.22252093255519867,-0.844312310218811,-0.5289434790611267,0.22252093255519867,-0.8189647197723389,-0.5691080093383789,0.22252093255519867,-0.7915810346603394,-0.6078575849533081,0.22252093255519867,-0.762229323387146,-0.6450960040092468,0.22252093255519867,-0.7309826016426086,-0.6807305812835693,0.22252093255519867,-0.6979185938835144,-0.7146727442741394,0.22252093255519867,-0.663119375705719,-0.7468380928039551,0.22252093255519867,-0.626671552658081,-0.7771467566490173,0.22252093255519867,-0.588665783405304,-0.8055232167243958,0.22252093255519867,-0.5491964221000671,-0.8318970799446106,0.22252093255519867,-0.5083617568016052,-0.8562027215957642,0.22252093255519867,-0.46626314520835876,-0.8783797025680542,0.22252093255519867,-0.4230053722858429,-0.8983728885650635,0.22252093255519867,-0.37869593501091003,-0.9161325693130493,0.22252093255519867,-0.3334449827671051,-0.9316145777702332,0.22252093255519867,-0.28736504912376404,-0.9447804689407349,0.22252093255519867,-0.24057067930698395,-0.9555975198745728,0.22252093255519867,-0.1931782066822052,-0.9640387892723083,0.22252093255519867,-0.14530546963214874,-0.9700832962989807,0.22252093255519867,-0.09707147628068924,-0.9737160205841064,0.22252093255519867,-0.04859614372253418,-0.9749279022216797,0.22252093255519867,-1.1939423052938935e-16,-0.9737160205841064,0.22252093255519867,0.04859614372253418,-0.9700832962989807,0.22252093255519867,0.09707147628068924,-0.9640387892723083,0.22252093255519867,0.14530546963214874,-0.9555975198745728,0.22252093255519867,0.1931782066822052,-0.9447804689407349,0.22252093255519867,0.24057067930698395,-0.9316145777702332,0.22252093255519867,0.28736504912376404,-0.9161325693130493,0.22252093255519867,0.3334449827671051,-0.8983728885650635,0.22252093255519867,0.37869593501091003,-0.8783797025680542,0.22252093255519867,0.4230053722858429,-0.8562027215957642,0.22252093255519867,0.46626314520835876,-0.8318970799446106,0.22252093255519867,0.5083617568016052,-0.8055232167243958,0.22252093255519867,0.5491964221000671,-0.7771467566490173,0.22252093255519867,0.588665783405304,-0.7468380928039551,0.22252093255519867,0.626671552658081,-0.7146727442741394,0.22252093255519867,0.663119375705719,-0.6807305812835693,0.22252093255519867,0.6979185938835144,-0.6450960040092468,0.22252093255519867,0.7309826016426086,-0.6078575849533081,0.22252093255519867,0.762229323387146,-0.5691080093383789,0.22252093255519867,0.7915810346603394,-0.5289434790611267,0.22252093255519867,0.8189647197723389,-0.48746395111083984,0.22252093255519867,0.844312310218811,-0.44477251172065735,0.22252093255519867,0.867560863494873,-0.4009752869606018,0.22252093255519867,0.888652503490448,-0.35618117451667786,0.22252093255519867,0.9075347781181335,-0.3105015158653259,0.22252093255519867,0.9241608381271362,-0.2640499174594879,0.22252093255519867,0.9384892582893372,-0.21694186329841614,0.22252093255519867,0.9504844546318054,-0.16929446160793304,0.22252093255519867,0.9601165652275085,-0.12122614681720734,0.22252093255519867,0.9673616886138916,-0.07285645604133606,0.22252093255519867,0.9722018241882324,-0.02430562674999237,0.22252093255519867,0.9746248722076416,0.02430562674999237,0.22252093255519867,0.9746248722076416,0.07285645604133606,0.22252093255519867,0.9722018241882324,0.12122614681720734,0.22252093255519867,0.9673616886138916,0.16929446160793304,0.22252093255519867,0.9601165652275085,0.21694186329841614,0.22252093255519867,0.9504844546318054,0.2640499174594879,0.22252093255519867,0.9384892582893372,0.3105015158653259,0.22252093255519867,0.9241608381271362,0.35618117451667786,0.22252093255519867,0.9075347781181335,0.4009752869606018,0.22252093255519867,0.888652503490448,0.44477251172065735,0.22252093255519867,0.867560863494873,0.48746395111083984,0.22252093255519867,0.844312310218811,0.5289434790611267,0.22252093255519867,0.8189647197723389,0.5691080093383789,0.22252093255519867,0.7915810346603394,0.6078575849533081,0.22252093255519867,0.762229323387146,0.6450960040092468,0.22252093255519867,0.7309826016426086,0.6807305812835693,0.22252093255519867,0.6979185938835144,0.7146727442741394,0.22252093255519867,0.663119375705719,0.7468380928039551,0.22252093255519867,0.626671552658081,0.7771467566490173,0.22252093255519867,0.588665783405304,0.8055232167243958,0.22252093255519867,0.5491964221000671,0.8318970799446106,0.22252093255519867,0.5083617568016052,0.8562027215957642,0.22252093255519867,0.46626314520835876,0.8783797025680542,0.22252093255519867,0.4230053722858429,0.8983728885650635,0.22252093255519867,0.37869593501091003,0.9161325693130493,0.22252093255519867,0.3334449827671051,0.9316145777702332,0.22252093255519867,0.28736504912376404,0.9447804689407349,0.22252093255519867,0.24057067930698395,0.9555975198745728,0.22252093255519867,0.1931782066822052,0.9640387892723083,0.22252093255519867,0.14530546963214874,0.9700832962989807,0.22252093255519867,0.09707147628068924,0.9737160205841064,0.22252093255519867,0.04859614372253418,0.9749279022216797,0.22252093255519867,2.387884610587787e-16,0.9626242518424988,0.27084046602249146,0.0,0.961427628993988,0.27084046602249146,-0.04798285663127899,0.9578407406806946,0.27084046602249146,-0.09584642201662064,0.9518725275993347,0.27084046602249146,-0.14347170293331146,0.9435378313064575,0.27084046602249146,-0.19074028730392456,0.9328572750091553,0.27084046602249146,-0.23753465712070465,0.9198575615882874,0.27084046602249146,-0.28373846411705017,0.9045708775520325,0.27084046602249146,-0.3292368948459625,0.8870353698730469,0.27084046602249146,-0.37391674518585205,0.8672944903373718,0.27084046602249146,-0.4176670014858246,0.8453973531723022,0.27084046602249146,-0.46037888526916504,0.8213984966278076,0.27084046602249146,-0.5019461512565613,0.7953574657440186,0.27084046602249146,-0.5422655344009399,0.7673390507698059,0.27084046602249146,-0.5812367796897888,0.7374129295349121,0.27084046602249146,-0.6187629103660583,0.7056534886360168,0.27084046602249146,-0.6547507643699646,0.6721397042274475,0.27084046602249146,-0.6891108155250549,0.6369548439979553,0.27084046602249146,-0.7217575907707214,0.6001864075660706,0.27084046602249146,-0.7526099681854248,0.5619258284568787,0.27084046602249146,-0.7815912365913391,0.5222681760787964,0.27084046602249146,-0.8086293339729309,0.4813121259212494,0.27084046602249146,-0.8336570262908936,0.43915945291519165,0.27084046602249146,-0.8566122055053711,0.3959149420261383,0.27084046602249146,-0.8774376511573792,0.35168612003326416,0.27084046602249146,-0.8960816264152527,0.3065829575061798,0.27084046602249146,-0.9124978184700012,0.26071760058403015,0.27084046602249146,-0.9266454577445984,0.21420404314994812,0.27084046602249146,-0.9384892582893372,0.16715794801712036,0.27084046602249146,-0.9479998350143433,0.11969626694917679,0.27084046602249146,-0.9551535248756409,0.07193700224161148,0.27084046602249146,-0.9599325656890869,0.023998888209462166,0.27084046602249146,-0.9623250365257263,-0.023998888209462166,0.27084046602249146,-0.9623250365257263,-0.07193700224161148,0.27084046602249146,-0.9599325656890869,-0.11969626694917679,0.27084046602249146,-0.9551535248756409,-0.16715794801712036,0.27084046602249146,-0.9479998350143433,-0.21420404314994812,0.27084046602249146,-0.9384892582893372,-0.26071760058403015,0.27084046602249146,-0.9266454577445984,-0.3065829575061798,0.27084046602249146,-0.9124978184700012,-0.35168612003326416,0.27084046602249146,-0.8960816264152527,-0.3959149420261383,0.27084046602249146,-0.8774376511573792,-0.43915945291519165,0.27084046602249146,-0.8566122055053711,-0.4813121259212494,0.27084046602249146,-0.8336570262908936,-0.5222681760787964,0.27084046602249146,-0.8086293339729309,-0.5619258284568787,0.27084046602249146,-0.7815912365913391,-0.6001864075660706,0.27084046602249146,-0.7526099681854248,-0.6369548439979553,0.27084046602249146,-0.7217575907707214,-0.6721397042274475,0.27084046602249146,-0.6891108155250549,-0.7056534886360168,0.27084046602249146,-0.6547507643699646,-0.7374129295349121,0.27084046602249146,-0.6187629103660583,-0.7673390507698059,0.27084046602249146,-0.5812367796897888,-0.7953574657440186,0.27084046602249146,-0.5422655344009399,-0.8213984966278076,0.27084046602249146,-0.5019461512565613,-0.8453973531723022,0.27084046602249146,-0.46037888526916504,-0.8672944903373718,0.27084046602249146,-0.4176670014858246,-0.8870353698730469,0.27084046602249146,-0.37391674518585205,-0.9045708775520325,0.27084046602249146,-0.3292368948459625,-0.9198575615882874,0.27084046602249146,-0.28373846411705017,-0.9328572750091553,0.27084046602249146,-0.23753465712070465,-0.9435378313064575,0.27084046602249146,-0.19074028730392456,-0.9518725275993347,0.27084046602249146,-0.14347170293331146,-0.9578407406806946,0.27084046602249146,-0.09584642201662064,-0.961427628993988,0.27084046602249146,-0.04798285663127899,-0.9626242518424988,0.27084046602249146,-1.1788746479534236e-16,-0.961427628993988,0.27084046602249146,0.04798285663127899,-0.9578407406806946,0.27084046602249146,0.09584642201662064,-0.9518725275993347,0.27084046602249146,0.14347170293331146,-0.9435378313064575,0.27084046602249146,0.19074028730392456,-0.9328572750091553,0.27084046602249146,0.23753465712070465,-0.9198575615882874,0.27084046602249146,0.28373846411705017,-0.9045708775520325,0.27084046602249146,0.3292368948459625,-0.8870353698730469,0.27084046602249146,0.37391674518585205,-0.8672944903373718,0.27084046602249146,0.4176670014858246,-0.8453973531723022,0.27084046602249146,0.46037888526916504,-0.8213984966278076,0.27084046602249146,0.5019461512565613,-0.7953574657440186,0.27084046602249146,0.5422655344009399,-0.7673390507698059,0.27084046602249146,0.5812367796897888,-0.7374129295349121,0.27084046602249146,0.6187629103660583,-0.7056534886360168,0.27084046602249146,0.6547507643699646,-0.6721397042274475,0.27084046602249146,0.6891108155250549,-0.6369548439979553,0.27084046602249146,0.7217575907707214,-0.6001864075660706,0.27084046602249146,0.7526099681854248,-0.5619258284568787,0.27084046602249146,0.7815912365913391,-0.5222681760787964,0.27084046602249146,0.8086293339729309,-0.4813121259212494,0.27084046602249146,0.8336570262908936,-0.43915945291519165,0.27084046602249146,0.8566122055053711,-0.3959149420261383,0.27084046602249146,0.8774376511573792,-0.35168612003326416,0.27084046602249146,0.8960816264152527,-0.3065829575061798,0.27084046602249146,0.9124978184700012,-0.26071760058403015,0.27084046602249146,0.9266454577445984,-0.21420404314994812,0.27084046602249146,0.9384892582893372,-0.16715794801712036,0.27084046602249146,0.9479998350143433,-0.11969626694917679,0.27084046602249146,0.9551535248756409,-0.07193700224161148,0.27084046602249146,0.9599325656890869,-0.023998888209462166,0.27084046602249146,0.9623250365257263,0.023998888209462166,0.27084046602249146,0.9623250365257263,0.07193700224161148,0.27084046602249146,0.9599325656890869,0.11969626694917679,0.27084046602249146,0.9551535248756409,0.16715794801712036,0.27084046602249146,0.9479998350143433,0.21420404314994812,0.27084046602249146,0.9384892582893372,0.26071760058403015,0.27084046602249146,0.9266454577445984,0.3065829575061798,0.27084046602249146,0.9124978184700012,0.35168612003326416,0.27084046602249146,0.8960816264152527,0.3959149420261383,0.27084046602249146,0.8774376511573792,0.43915945291519165,0.27084046602249146,0.8566122055053711,0.4813121259212494,0.27084046602249146,0.8336570262908936,0.5222681760787964,0.27084046602249146,0.8086293339729309,0.5619258284568787,0.27084046602249146,0.7815912365913391,0.6001864075660706,0.27084046602249146,0.7526099681854248,0.6369548439979553,0.27084046602249146,0.7217575907707214,0.6721397042274475,0.27084046602249146,0.6891108155250549,0.7056534886360168,0.27084046602249146,0.6547507643699646,0.7374129295349121,0.27084046602249146,0.6187629103660583,0.7673390507698059,0.27084046602249146,0.5812367796897888,0.7953574657440186,0.27084046602249146,0.5422655344009399,0.8213984966278076,0.27084046602249146,0.5019461512565613,0.8453973531723022,0.27084046602249146,0.46037888526916504,0.8672944903373718,0.27084046602249146,0.4176670014858246,0.8870353698730469,0.27084046602249146,0.37391674518585205,0.9045708775520325,0.27084046602249146,0.3292368948459625,0.9198575615882874,0.27084046602249146,0.28373846411705017,0.9328572750091553,0.27084046602249146,0.23753465712070465,0.9435378313064575,0.27084046602249146,0.19074028730392456,0.9518725275993347,0.27084046602249146,0.14347170293331146,0.9578407406806946,0.27084046602249146,0.09584642201662064,0.961427628993988,0.27084046602249146,0.04798285663127899,0.9626242518424988,0.27084046602249146,2.357749295906847e-16,0.9479273557662964,0.31848666071891785,0.0,0.9467489719390869,0.31848666071891785,-0.047250278294086456,0.943216860294342,0.31848666071891785,-0.09438308328390121,0.9373397827148438,0.31848666071891785,-0.14128124713897705,0.9291322827339172,0.31848666071891785,-0.1878281533718109,0.9186148643493652,0.31848666071891785,-0.23390808701515198,0.9058135747909546,0.31848666071891785,-0.27940648794174194,0.8907603025436401,0.31848666071891785,-0.3242102563381195,0.8734924793243408,0.31848666071891785,-0.3682079613208771,0.8540530204772949,0.31848666071891785,-0.4112902581691742,0.8324902653694153,0.31848666071891785,-0.4533500373363495,0.8088577389717102,0.31848666071891785,-0.49428269267082214,0.7832143306732178,0.31848666071891785,-0.533986508846283,0.7556236982345581,0.31848666071891785,-0.5723627209663391,0.7261544466018677,0.31848666071891785,-0.6093159317970276,0.6948798894882202,0.31848666071891785,-0.6447543501853943,0.6618777513504028,0.31848666071891785,-0.6785897612571716,0.6272301077842712,0.31848666071891785,-0.7107381224632263,0.5910230278968811,0.31848666071891785,-0.7411194443702698,0.553346574306488,0.31848666071891785,-0.7696582674980164,0.514294445514679,0.31848666071891785,-0.7962835431098938,0.4739636778831482,0.31848666071891785,-0.8209291696548462,0.43245455622673035,0.31848666071891785,-0.8435338139533997,0.389870285987854,0.31848666071891785,-0.8640413284301758,0.34631675481796265,0.31848666071891785,-0.8824006915092468,0.3019022047519684,0.31848666071891785,-0.8985662460327148,0.25673708319664,0.31848666071891785,-0.9124978184700012,0.21093368530273438,0.31848666071891785,-0.9241608381271362,0.16460585594177246,0.31848666071891785,-0.9335262179374695,0.11786879599094391,0.31848666071891785,-0.9405706524848938,0.07083869725465775,0.31848666071891785,-0.9452767372131348,0.023632483556866646,0.31848666071891785,-0.9476327300071716,-0.023632483556866646,0.31848666071891785,-0.9476327300071716,-0.07083869725465775,0.31848666071891785,-0.9452767372131348,-0.11786879599094391,0.31848666071891785,-0.9405706524848938,-0.16460585594177246,0.31848666071891785,-0.9335262179374695,-0.21093368530273438,0.31848666071891785,-0.9241608381271362,-0.25673708319664,0.31848666071891785,-0.9124978184700012,-0.3019022047519684,0.31848666071891785,-0.8985662460327148,-0.34631675481796265,0.31848666071891785,-0.8824006915092468,-0.389870285987854,0.31848666071891785,-0.8640413284301758,-0.43245455622673035,0.31848666071891785,-0.8435338139533997,-0.4739636778831482,0.31848666071891785,-0.8209291696548462,-0.514294445514679,0.31848666071891785,-0.7962835431098938,-0.553346574306488,0.31848666071891785,-0.7696582674980164,-0.5910230278968811,0.31848666071891785,-0.7411194443702698,-0.6272301077842712,0.31848666071891785,-0.7107381224632263,-0.6618777513504028,0.31848666071891785,-0.6785897612571716,-0.6948798894882202,0.31848666071891785,-0.6447543501853943,-0.7261544466018677,0.31848666071891785,-0.6093159317970276,-0.7556236982345581,0.31848666071891785,-0.5723627209663391,-0.7832143306732178,0.31848666071891785,-0.533986508846283,-0.8088577389717102,0.31848666071891785,-0.49428269267082214,-0.8324902653694153,0.31848666071891785,-0.4533500373363495,-0.8540530204772949,0.31848666071891785,-0.4112902581691742,-0.8734924793243408,0.31848666071891785,-0.3682079613208771,-0.8907603025436401,0.31848666071891785,-0.3242102563381195,-0.9058135747909546,0.31848666071891785,-0.27940648794174194,-0.9186148643493652,0.31848666071891785,-0.23390808701515198,-0.9291322827339172,0.31848666071891785,-0.1878281533718109,-0.9373397827148438,0.31848666071891785,-0.14128124713897705,-0.943216860294342,0.31848666071891785,-0.09438308328390121,-0.9467489719390869,0.31848666071891785,-0.047250278294086456,-0.9479273557662964,0.31848666071891785,-1.1608761242665557e-16,-0.9467489719390869,0.31848666071891785,0.047250278294086456,-0.943216860294342,0.31848666071891785,0.09438308328390121,-0.9373397827148438,0.31848666071891785,0.14128124713897705,-0.9291322827339172,0.31848666071891785,0.1878281533718109,-0.9186148643493652,0.31848666071891785,0.23390808701515198,-0.9058135747909546,0.31848666071891785,0.27940648794174194,-0.8907603025436401,0.31848666071891785,0.3242102563381195,-0.8734924793243408,0.31848666071891785,0.3682079613208771,-0.8540530204772949,0.31848666071891785,0.4112902581691742,-0.8324902653694153,0.31848666071891785,0.4533500373363495,-0.8088577389717102,0.31848666071891785,0.49428269267082214,-0.7832143306732178,0.31848666071891785,0.533986508846283,-0.7556236982345581,0.31848666071891785,0.5723627209663391,-0.7261544466018677,0.31848666071891785,0.6093159317970276,-0.6948798894882202,0.31848666071891785,0.6447543501853943,-0.6618777513504028,0.31848666071891785,0.6785897612571716,-0.6272301077842712,0.31848666071891785,0.7107381224632263,-0.5910230278968811,0.31848666071891785,0.7411194443702698,-0.553346574306488,0.31848666071891785,0.7696582674980164,-0.514294445514679,0.31848666071891785,0.7962835431098938,-0.4739636778831482,0.31848666071891785,0.8209291696548462,-0.43245455622673035,0.31848666071891785,0.8435338139533997,-0.389870285987854,0.31848666071891785,0.8640413284301758,-0.34631675481796265,0.31848666071891785,0.8824006915092468,-0.3019022047519684,0.31848666071891785,0.8985662460327148,-0.25673708319664,0.31848666071891785,0.9124978184700012,-0.21093368530273438,0.31848666071891785,0.9241608381271362,-0.16460585594177246,0.31848666071891785,0.9335262179374695,-0.11786879599094391,0.31848666071891785,0.9405706524848938,-0.07083869725465775,0.31848666071891785,0.9452767372131348,-0.023632483556866646,0.31848666071891785,0.9476327300071716,0.023632483556866646,0.31848666071891785,0.9476327300071716,0.07083869725465775,0.31848666071891785,0.9452767372131348,0.11786879599094391,0.31848666071891785,0.9405706524848938,0.16460585594177246,0.31848666071891785,0.9335262179374695,0.21093368530273438,0.31848666071891785,0.9241608381271362,0.25673708319664,0.31848666071891785,0.9124978184700012,0.3019022047519684,0.31848666071891785,0.8985662460327148,0.34631675481796265,0.31848666071891785,0.8824006915092468,0.389870285987854,0.31848666071891785,0.8640413284301758,0.43245455622673035,0.31848666071891785,0.8435338139533997,0.4739636778831482,0.31848666071891785,0.8209291696548462,0.514294445514679,0.31848666071891785,0.7962835431098938,0.553346574306488,0.31848666071891785,0.7696582674980164,0.5910230278968811,0.31848666071891785,0.7411194443702698,0.6272301077842712,0.31848666071891785,0.7107381224632263,0.6618777513504028,0.31848666071891785,0.6785897612571716,0.6948798894882202,0.31848666071891785,0.6447543501853943,0.7261544466018677,0.31848666071891785,0.6093159317970276,0.7556236982345581,0.31848666071891785,0.5723627209663391,0.7832143306732178,0.31848666071891785,0.533986508846283,0.8088577389717102,0.31848666071891785,0.49428269267082214,0.8324902653694153,0.31848666071891785,0.4533500373363495,0.8540530204772949,0.31848666071891785,0.4112902581691742,0.8734924793243408,0.31848666071891785,0.3682079613208771,0.8907603025436401,0.31848666071891785,0.3242102563381195,0.9058135747909546,0.31848666071891785,0.27940648794174194,0.9186148643493652,0.31848666071891785,0.23390808701515198,0.9291322827339172,0.31848666071891785,0.1878281533718109,0.9373397827148438,0.31848666071891785,0.14128124713897705,0.943216860294342,0.31848666071891785,0.09438308328390121,0.9467489719390869,0.31848666071891785,0.047250278294086456,0.9479273557662964,0.31848666071891785,2.3217522485331115e-16,0.9308737516403198,0.36534103751182556,0.0,0.9297165870666504,0.36534103751182556,-0.04640022665262222,0.9262480139732361,0.36534103751182556,-0.09268509596586227,0.9204766750335693,0.36534103751182556,-0.13873952627182007,0.9124168157577515,0.36534103751182556,-0.18444904685020447,0.9020885825157166,0.36534103751182556,-0.2296999841928482,0.8895176649093628,0.36534103751182556,-0.2743798494338989,0.8747351765632629,0.36534103751182556,-0.3183775842189789,0.857778012752533,0.36534103751182556,-0.36158376932144165,0.8386882543563843,0.36534103751182556,-0.403890997171402,0.8175134062767029,0.36534103751182556,-0.4451940655708313,0.7943060994148254,0.36534103751182556,-0.4853903353214264,0.7691239714622498,0.36534103751182556,-0.5243798494338989,0.7420297265052795,0.36534103751182556,-0.5620656609535217,0.7130906581878662,0.36534103751182556,-0.5983541011810303,0.6823787689208984,0.36534103751182556,-0.6331549286842346,0.6499703526496887,0.36534103751182556,-0.6663816571235657,0.6159459948539734,0.36534103751182556,-0.69795161485672,0.5803902745246887,0.36534103751182556,-0.7277864217758179,0.5433916449546814,0.36534103751182556,-0.7558117508888245,0.5050420761108398,0.36534103751182556,-0.7819581031799316,0.4654368758201599,0.36534103751182556,-0.8061603307723999,0.42467451095581055,0.36534103751182556,-0.8283582925796509,0.3828563690185547,0.36534103751182556,-0.8484968543052673,0.3400863707065582,0.36534103751182556,-0.8665259480476379,0.29647085070610046,0.36534103751182556,-0.8824006915092468,0.2521182894706726,0.36534103751182556,-0.8960816264152527,0.20713889598846436,0.36534103751182556,-0.9075347781181335,0.16164453327655792,0.36534103751182556,-0.9167316555976868,0.11574829369783401,0.36534103751182556,-0.9236494302749634,0.06956428289413452,0.36534103751182556,-0.9282708168029785,0.023207327350974083,0.36534103751182556,-0.9305844306945801,-0.023207327350974083,0.36534103751182556,-0.9305844306945801,-0.06956428289413452,0.36534103751182556,-0.9282708168029785,-0.11574829369783401,0.36534103751182556,-0.9236494302749634,-0.16164453327655792,0.36534103751182556,-0.9167316555976868,-0.20713889598846436,0.36534103751182556,-0.9075347781181335,-0.2521182894706726,0.36534103751182556,-0.8960816264152527,-0.29647085070610046,0.36534103751182556,-0.8824006915092468,-0.3400863707065582,0.36534103751182556,-0.8665259480476379,-0.3828563690185547,0.36534103751182556,-0.8484968543052673,-0.42467451095581055,0.36534103751182556,-0.8283582925796509,-0.4654368758201599,0.36534103751182556,-0.8061603307723999,-0.5050420761108398,0.36534103751182556,-0.7819581031799316,-0.5433916449546814,0.36534103751182556,-0.7558117508888245,-0.5803902745246887,0.36534103751182556,-0.7277864217758179,-0.6159459948539734,0.36534103751182556,-0.69795161485672,-0.6499703526496887,0.36534103751182556,-0.6663816571235657,-0.6823787689208984,0.36534103751182556,-0.6331549286842346,-0.7130906581878662,0.36534103751182556,-0.5983541011810303,-0.7420297265052795,0.36534103751182556,-0.5620656609535217,-0.7691239714622498,0.36534103751182556,-0.5243798494338989,-0.7943060994148254,0.36534103751182556,-0.4853903353214264,-0.8175134062767029,0.36534103751182556,-0.4451940655708313,-0.8386882543563843,0.36534103751182556,-0.403890997171402,-0.857778012752533,0.36534103751182556,-0.36158376932144165,-0.8747351765632629,0.36534103751182556,-0.3183775842189789,-0.8895176649093628,0.36534103751182556,-0.2743798494338989,-0.9020885825157166,0.36534103751182556,-0.2296999841928482,-0.9124168157577515,0.36534103751182556,-0.18444904685020447,-0.9204766750335693,0.36534103751182556,-0.13873952627182007,-0.9262480139732361,0.36534103751182556,-0.09268509596586227,-0.9297165870666504,0.36534103751182556,-0.04640022665262222,-0.9308737516403198,0.36534103751182556,-1.139991600509715e-16,-0.9297165870666504,0.36534103751182556,0.04640022665262222,-0.9262480139732361,0.36534103751182556,0.09268509596586227,-0.9204766750335693,0.36534103751182556,0.13873952627182007,-0.9124168157577515,0.36534103751182556,0.18444904685020447,-0.9020885825157166,0.36534103751182556,0.2296999841928482,-0.8895176649093628,0.36534103751182556,0.2743798494338989,-0.8747351765632629,0.36534103751182556,0.3183775842189789,-0.857778012752533,0.36534103751182556,0.36158376932144165,-0.8386882543563843,0.36534103751182556,0.403890997171402,-0.8175134062767029,0.36534103751182556,0.4451940655708313,-0.7943060994148254,0.36534103751182556,0.4853903353214264,-0.7691239714622498,0.36534103751182556,0.5243798494338989,-0.7420297265052795,0.36534103751182556,0.5620656609535217,-0.7130906581878662,0.36534103751182556,0.5983541011810303,-0.6823787689208984,0.36534103751182556,0.6331549286842346,-0.6499703526496887,0.36534103751182556,0.6663816571235657,-0.6159459948539734,0.36534103751182556,0.69795161485672,-0.5803902745246887,0.36534103751182556,0.7277864217758179,-0.5433916449546814,0.36534103751182556,0.7558117508888245,-0.5050420761108398,0.36534103751182556,0.7819581031799316,-0.4654368758201599,0.36534103751182556,0.8061603307723999,-0.42467451095581055,0.36534103751182556,0.8283582925796509,-0.3828563690185547,0.36534103751182556,0.8484968543052673,-0.3400863707065582,0.36534103751182556,0.8665259480476379,-0.29647085070610046,0.36534103751182556,0.8824006915092468,-0.2521182894706726,0.36534103751182556,0.8960816264152527,-0.20713889598846436,0.36534103751182556,0.9075347781181335,-0.16164453327655792,0.36534103751182556,0.9167316555976868,-0.11574829369783401,0.36534103751182556,0.9236494302749634,-0.06956428289413452,0.36534103751182556,0.9282708168029785,-0.023207327350974083,0.36534103751182556,0.9305844306945801,0.023207327350974083,0.36534103751182556,0.9305844306945801,0.06956428289413452,0.36534103751182556,0.9282708168029785,0.11574829369783401,0.36534103751182556,0.9236494302749634,0.16164453327655792,0.36534103751182556,0.9167316555976868,0.20713889598846436,0.36534103751182556,0.9075347781181335,0.2521182894706726,0.36534103751182556,0.8960816264152527,0.29647085070610046,0.36534103751182556,0.8824006915092468,0.3400863707065582,0.36534103751182556,0.8665259480476379,0.3828563690185547,0.36534103751182556,0.8484968543052673,0.42467451095581055,0.36534103751182556,0.8283582925796509,0.4654368758201599,0.36534103751182556,0.8061603307723999,0.5050420761108398,0.36534103751182556,0.7819581031799316,0.5433916449546814,0.36534103751182556,0.7558117508888245,0.5803902745246887,0.36534103751182556,0.7277864217758179,0.6159459948539734,0.36534103751182556,0.69795161485672,0.6499703526496887,0.36534103751182556,0.6663816571235657,0.6823787689208984,0.36534103751182556,0.6331549286842346,0.7130906581878662,0.36534103751182556,0.5983541011810303,0.7420297265052795,0.36534103751182556,0.5620656609535217,0.7691239714622498,0.36534103751182556,0.5243798494338989,0.7943060994148254,0.36534103751182556,0.4853903353214264,0.8175134062767029,0.36534103751182556,0.4451940655708313,0.8386882543563843,0.36534103751182556,0.403890997171402,0.857778012752533,0.36534103751182556,0.36158376932144165,0.8747351765632629,0.36534103751182556,0.3183775842189789,0.8895176649093628,0.36534103751182556,0.2743798494338989,0.9020885825157166,0.36534103751182556,0.2296999841928482,0.9124168157577515,0.36534103751182556,0.18444904685020447,0.9204766750335693,0.36534103751182556,0.13873952627182007,0.9262480139732361,0.36534103751182556,0.09268509596586227,0.9297165870666504,0.36534103751182556,0.04640022665262222,0.9308737516403198,0.36534103751182556,2.27998320101943e-16,0.9115058779716492,0.4112870991230011,0.0,0.910372793674469,0.4112870991230011,-0.04543481767177582,0.9069764018058777,0.4112870991230011,-0.09075667709112167,0.9013251066207886,0.4112870991230011,-0.1358529031276703,0.8934329748153687,0.4112870991230011,-0.18061137199401855,0.883319616317749,0.4112870991230011,-0.22492080926895142,0.8710101842880249,0.4112870991230011,-0.26867106556892395,0.8565353155136108,0.4112870991230011,-0.31175336241722107,0.8399309515953064,0.4112870991230011,-0.3540605902671814,0.8212383985519409,0.4112870991230011,-0.39548757672309875,0.8005040884017944,0.4112870991230011,-0.4359312951564789,0.7777796387672424,0.4112870991230011,-0.47529125213623047,0.7531214952468872,0.4112870991230011,-0.5134695172309875,0.7265909314155579,0.4112870991230011,-0.5503712296485901,0.6982539892196655,0.4112870991230011,-0.5859046578407288,0.6681810617446899,0.4112870991230011,-0.6199814081192017,0.6364469528198242,0.4112870991230011,-0.6525168418884277,0.6031305193901062,0.4112870991230011,-0.6834299564361572,0.5683146119117737,0.4112870991230011,-0.7126439809799194,0.5320857763290405,0.4112870991230011,-0.7400862574577332,0.49453410506248474,0.4112870991230011,-0.7656885385513306,0.4557529389858246,0.4112870991230011,-0.7893872261047363,0.41583868861198425,0.4112870991230011,-0.8111233711242676,0.37489059567451477,0.4112870991230011,-0.830842912197113,0.3330104947090149,0.4112870991230011,-0.8484968543052673,0.29030245542526245,0.4112870991230011,-0.8640413284301758,0.246872678399086,0.4112870991230011,-0.8774376511573792,0.20282913744449615,0.4112870991230011,-0.888652503490448,0.1582813262939453,0.4112870991230011,-0.8976580500602722,0.11334001272916794,0.4112870991230011,-0.9044318199157715,0.0681169182062149,0.4112870991230011,-0.9089571237564087,0.022724471986293793,0.4112870991230011,-0.911222517490387,-0.022724471986293793,0.4112870991230011,-0.911222517490387,-0.0681169182062149,0.4112870991230011,-0.9089571237564087,-0.11334001272916794,0.4112870991230011,-0.9044318199157715,-0.1582813262939453,0.4112870991230011,-0.8976580500602722,-0.20282913744449615,0.4112870991230011,-0.888652503490448,-0.246872678399086,0.4112870991230011,-0.8774376511573792,-0.29030245542526245,0.4112870991230011,-0.8640413284301758,-0.3330104947090149,0.4112870991230011,-0.8484968543052673,-0.37489059567451477,0.4112870991230011,-0.830842912197113,-0.41583868861198425,0.4112870991230011,-0.8111233711242676,-0.4557529389858246,0.4112870991230011,-0.7893872261047363,-0.49453410506248474,0.4112870991230011,-0.7656885385513306,-0.5320857763290405,0.4112870991230011,-0.7400862574577332,-0.5683146119117737,0.4112870991230011,-0.7126439809799194,-0.6031305193901062,0.4112870991230011,-0.6834299564361572,-0.6364469528198242,0.4112870991230011,-0.6525168418884277,-0.6681810617446899,0.4112870991230011,-0.6199814081192017,-0.6982539892196655,0.4112870991230011,-0.5859046578407288,-0.7265909314155579,0.4112870991230011,-0.5503712296485901,-0.7531214952468872,0.4112870991230011,-0.5134695172309875,-0.7777796387672424,0.4112870991230011,-0.47529125213623047,-0.8005040884017944,0.4112870991230011,-0.4359312951564789,-0.8212383985519409,0.4112870991230011,-0.39548757672309875,-0.8399309515953064,0.4112870991230011,-0.3540605902671814,-0.8565353155136108,0.4112870991230011,-0.31175336241722107,-0.8710101842880249,0.4112870991230011,-0.26867106556892395,-0.883319616317749,0.4112870991230011,-0.22492080926895142,-0.8934329748153687,0.4112870991230011,-0.18061137199401855,-0.9013251066207886,0.4112870991230011,-0.1358529031276703,-0.9069764018058777,0.4112870991230011,-0.09075667709112167,-0.910372793674469,0.4112870991230011,-0.04543481767177582,-0.9115058779716492,0.4112870991230011,-1.1162726927531244e-16,-0.910372793674469,0.4112870991230011,0.04543481767177582,-0.9069764018058777,0.4112870991230011,0.09075667709112167,-0.9013251066207886,0.4112870991230011,0.1358529031276703,-0.8934329748153687,0.4112870991230011,0.18061137199401855,-0.883319616317749,0.4112870991230011,0.22492080926895142,-0.8710101842880249,0.4112870991230011,0.26867106556892395,-0.8565353155136108,0.4112870991230011,0.31175336241722107,-0.8399309515953064,0.4112870991230011,0.3540605902671814,-0.8212383985519409,0.4112870991230011,0.39548757672309875,-0.8005040884017944,0.4112870991230011,0.4359312951564789,-0.7777796387672424,0.4112870991230011,0.47529125213623047,-0.7531214952468872,0.4112870991230011,0.5134695172309875,-0.7265909314155579,0.4112870991230011,0.5503712296485901,-0.6982539892196655,0.4112870991230011,0.5859046578407288,-0.6681810617446899,0.4112870991230011,0.6199814081192017,-0.6364469528198242,0.4112870991230011,0.6525168418884277,-0.6031305193901062,0.4112870991230011,0.6834299564361572,-0.5683146119117737,0.4112870991230011,0.7126439809799194,-0.5320857763290405,0.4112870991230011,0.7400862574577332,-0.49453410506248474,0.4112870991230011,0.7656885385513306,-0.4557529389858246,0.4112870991230011,0.7893872261047363,-0.41583868861198425,0.4112870991230011,0.8111233711242676,-0.37489059567451477,0.4112870991230011,0.830842912197113,-0.3330104947090149,0.4112870991230011,0.8484968543052673,-0.29030245542526245,0.4112870991230011,0.8640413284301758,-0.246872678399086,0.4112870991230011,0.8774376511573792,-0.20282913744449615,0.4112870991230011,0.888652503490448,-0.1582813262939453,0.4112870991230011,0.8976580500602722,-0.11334001272916794,0.4112870991230011,0.9044318199157715,-0.0681169182062149,0.4112870991230011,0.9089571237564087,-0.022724471986293793,0.4112870991230011,0.911222517490387,0.022724471986293793,0.4112870991230011,0.911222517490387,0.0681169182062149,0.4112870991230011,0.9089571237564087,0.11334001272916794,0.4112870991230011,0.9044318199157715,0.1582813262939453,0.4112870991230011,0.8976580500602722,0.20282913744449615,0.4112870991230011,0.888652503490448,0.246872678399086,0.4112870991230011,0.8774376511573792,0.29030245542526245,0.4112870991230011,0.8640413284301758,0.3330104947090149,0.4112870991230011,0.8484968543052673,0.37489059567451477,0.4112870991230011,0.830842912197113,0.41583868861198425,0.4112870991230011,0.8111233711242676,0.4557529389858246,0.4112870991230011,0.7893872261047363,0.49453410506248474,0.4112870991230011,0.7656885385513306,0.5320857763290405,0.4112870991230011,0.7400862574577332,0.5683146119117737,0.4112870991230011,0.7126439809799194,0.6031305193901062,0.4112870991230011,0.6834299564361572,0.6364469528198242,0.4112870991230011,0.6525168418884277,0.6681810617446899,0.4112870991230011,0.6199814081192017,0.6982539892196655,0.4112870991230011,0.5859046578407288,0.7265909314155579,0.4112870991230011,0.5503712296485901,0.7531214952468872,0.4112870991230011,0.5134695172309875,0.7777796387672424,0.4112870991230011,0.47529125213623047,0.8005040884017944,0.4112870991230011,0.4359312951564789,0.8212383985519409,0.4112870991230011,0.39548757672309875,0.8399309515953064,0.4112870991230011,0.3540605902671814,0.8565353155136108,0.4112870991230011,0.31175336241722107,0.8710101842880249,0.4112870991230011,0.26867106556892395,0.883319616317749,0.4112870991230011,0.22492080926895142,0.8934329748153687,0.4112870991230011,0.18061137199401855,0.9013251066207886,0.4112870991230011,0.1358529031276703,0.9069764018058777,0.4112870991230011,0.09075667709112167,0.910372793674469,0.4112870991230011,0.04543481767177582,0.9115058779716492,0.4112870991230011,2.2325453855062487e-16,0.8898718357086182,0.4562106430530548,0.0,0.8887656331062317,0.4562106430530548,-0.04435644671320915,0.8854498267173767,0.4562106430530548,-0.08860261738300323,0.879932701587677,0.4562106430530548,-0.13262851536273956,0.8722278475761414,0.4562106430530548,-0.17632466554641724,0.862354576587677,0.4562106430530548,-0.2195824533700943,0.8503373265266418,0.4562106430530548,-0.26229432225227356,0.8362059593200684,0.4562106430530548,-0.30435407161712646,0.8199957013130188,0.4562106430530548,-0.3456571698188782,0.8017467856407166,0.4562106430530548,-0.3861009180545807,0.7815046310424805,0.4562106430530548,-0.42558473348617554,0.7593194842338562,0.4562106430530548,-0.46401047706604004,0.7352465987205505,0.4562106430530548,-0.5012826323509216,0.7093457579612732,0.4562106430530548,-0.5373085141181946,0.6816813349723816,0.4562106430530548,-0.5719985961914062,0.6523221731185913,0.4562106430530548,-0.6052665710449219,0.6213412880897522,0.4562106430530548,-0.6370297074317932,0.5888155698776245,0.4562106430530548,-0.6672091484069824,0.554826021194458,0.4562106430530548,-0.6957297921180725,0.5194570422172546,0.4562106430530548,-0.7225207686424255,0.48279663920402527,0.4562106430530548,-0.7475153803825378,0.4449359178543091,0.4562106430530548,-0.7706515789031982,0.4059689939022064,0.4562106430530548,-0.7918718457221985,0.36599278450012207,0.4562106430530548,-0.8111233711242676,0.325106680393219,0.4562106430530548,-0.8283582925796509,0.28341227769851685,0.4562106430530548,-0.8435338139533997,0.241013303399086,0.4562106430530548,-0.8566122055053711,0.19801510870456696,0.4562106430530548,-0.867560863494873,0.15452462434768677,0.4562106430530548,-0.8763526678085327,0.1106499582529068,0.4562106430530548,-0.8829656839370728,0.06650020182132721,0.4562106430530548,-0.8873835206031799,0.02218511886894703,0.4562106430530548,-0.8895952105522156,-0.02218511886894703,0.4562106430530548,-0.8895952105522156,-0.06650020182132721,0.4562106430530548,-0.8873835206031799,-0.1106499582529068,0.4562106430530548,-0.8829656839370728,-0.15452462434768677,0.4562106430530548,-0.8763526678085327,-0.19801510870456696,0.4562106430530548,-0.867560863494873,-0.241013303399086,0.4562106430530548,-0.8566122055053711,-0.28341227769851685,0.4562106430530548,-0.8435338139533997,-0.325106680393219,0.4562106430530548,-0.8283582925796509,-0.36599278450012207,0.4562106430530548,-0.8111233711242676,-0.4059689939022064,0.4562106430530548,-0.7918718457221985,-0.4449359178543091,0.4562106430530548,-0.7706515789031982,-0.48279663920402527,0.4562106430530548,-0.7475153803825378,-0.5194570422172546,0.4562106430530548,-0.7225207686424255,-0.554826021194458,0.4562106430530548,-0.6957297921180725,-0.5888155698776245,0.4562106430530548,-0.6672091484069824,-0.6213412880897522,0.4562106430530548,-0.6370297074317932,-0.6523221731185913,0.4562106430530548,-0.6052665710449219,-0.6816813349723816,0.4562106430530548,-0.5719985961914062,-0.7093457579612732,0.4562106430530548,-0.5373085141181946,-0.7352465987205505,0.4562106430530548,-0.5012826323509216,-0.7593194842338562,0.4562106430530548,-0.46401047706604004,-0.7815046310424805,0.4562106430530548,-0.42558473348617554,-0.8017467856407166,0.4562106430530548,-0.3861009180545807,-0.8199957013130188,0.4562106430530548,-0.3456571698188782,-0.8362059593200684,0.4562106430530548,-0.30435407161712646,-0.8503373265266418,0.4562106430530548,-0.26229432225227356,-0.862354576587677,0.4562106430530548,-0.2195824533700943,-0.8722278475761414,0.4562106430530548,-0.17632466554641724,-0.879932701587677,0.4562106430530548,-0.13262851536273956,-0.8854498267173767,0.4562106430530548,-0.08860261738300323,-0.8887656331062317,0.4562106430530548,-0.04435644671320915,-0.8898718357086182,0.4562106430530548,-1.0897786933030919e-16,-0.8887656331062317,0.4562106430530548,0.04435644671320915,-0.8854498267173767,0.4562106430530548,0.08860261738300323,-0.879932701587677,0.4562106430530548,0.13262851536273956,-0.8722278475761414,0.4562106430530548,0.17632466554641724,-0.862354576587677,0.4562106430530548,0.2195824533700943,-0.8503373265266418,0.4562106430530548,0.26229432225227356,-0.8362059593200684,0.4562106430530548,0.30435407161712646,-0.8199957013130188,0.4562106430530548,0.3456571698188782,-0.8017467856407166,0.4562106430530548,0.3861009180545807,-0.7815046310424805,0.4562106430530548,0.42558473348617554,-0.7593194842338562,0.4562106430530548,0.46401047706604004,-0.7352465987205505,0.4562106430530548,0.5012826323509216,-0.7093457579612732,0.4562106430530548,0.5373085141181946,-0.6816813349723816,0.4562106430530548,0.5719985961914062,-0.6523221731185913,0.4562106430530548,0.6052665710449219,-0.6213412880897522,0.4562106430530548,0.6370297074317932,-0.5888155698776245,0.4562106430530548,0.6672091484069824,-0.554826021194458,0.4562106430530548,0.6957297921180725,-0.5194570422172546,0.4562106430530548,0.7225207686424255,-0.48279663920402527,0.4562106430530548,0.7475153803825378,-0.4449359178543091,0.4562106430530548,0.7706515789031982,-0.4059689939022064,0.4562106430530548,0.7918718457221985,-0.36599278450012207,0.4562106430530548,0.8111233711242676,-0.325106680393219,0.4562106430530548,0.8283582925796509,-0.28341227769851685,0.4562106430530548,0.8435338139533997,-0.241013303399086,0.4562106430530548,0.8566122055053711,-0.19801510870456696,0.4562106430530548,0.867560863494873,-0.15452462434768677,0.4562106430530548,0.8763526678085327,-0.1106499582529068,0.4562106430530548,0.8829656839370728,-0.06650020182132721,0.4562106430530548,0.8873835206031799,-0.02218511886894703,0.4562106430530548,0.8895952105522156,0.02218511886894703,0.4562106430530548,0.8895952105522156,0.06650020182132721,0.4562106430530548,0.8873835206031799,0.1106499582529068,0.4562106430530548,0.8829656839370728,0.15452462434768677,0.4562106430530548,0.8763526678085327,0.19801510870456696,0.4562106430530548,0.867560863494873,0.241013303399086,0.4562106430530548,0.8566122055053711,0.28341227769851685,0.4562106430530548,0.8435338139533997,0.325106680393219,0.4562106430530548,0.8283582925796509,0.36599278450012207,0.4562106430530548,0.8111233711242676,0.4059689939022064,0.4562106430530548,0.7918718457221985,0.4449359178543091,0.4562106430530548,0.7706515789031982,0.48279663920402527,0.4562106430530548,0.7475153803825378,0.5194570422172546,0.4562106430530548,0.7225207686424255,0.554826021194458,0.4562106430530548,0.6957297921180725,0.5888155698776245,0.4562106430530548,0.6672091484069824,0.6213412880897522,0.4562106430530548,0.6370297074317932,0.6523221731185913,0.4562106430530548,0.6052665710449219,0.6816813349723816,0.4562106430530548,0.5719985961914062,0.7093457579612732,0.4562106430530548,0.5373085141181946,0.7352465987205505,0.4562106430530548,0.5012826323509216,0.7593194842338562,0.4562106430530548,0.46401047706604004,0.7815046310424805,0.4562106430530548,0.42558473348617554,0.8017467856407166,0.4562106430530548,0.3861009180545807,0.8199957013130188,0.4562106430530548,0.3456571698188782,0.8362059593200684,0.4562106430530548,0.30435407161712646,0.8503373265266418,0.4562106430530548,0.26229432225227356,0.862354576587677,0.4562106430530548,0.2195824533700943,0.8722278475761414,0.4562106430530548,0.17632466554641724,0.879932701587677,0.4562106430530548,0.13262851536273956,0.8854498267173767,0.4562106430530548,0.08860261738300323,0.8887656331062317,0.4562106430530548,0.04435644671320915,0.8898718357086182,0.4562106430530548,2.1795573866061839e-16,0.8660253882408142,0.5,0.0,0.8649488687515259,0.5,-0.043167803436517715,0.861721932888031,0.5,-0.08622828125953674,0.8563526272773743,0.5,-0.12907439470291138,0.8488543033599854,0.5,-0.1715995967388153,0.8392455577850342,0.5,-0.21369817852973938,0.8275503516197205,0.5,-0.255265474319458,0.813797652721405,0.5,-0.29619812965393066,0.7980217933654785,0.5,-0.33639439940452576,0.7802619338035583,0.5,-0.37575432658195496,0.7605621814727783,0.5,-0.41418009996414185,0.7389715909957886,0.5,-0.4515761435031891,0.7155437469482422,0.5,-0.48784947395324707,0.6903370022773743,0.5,-0.522909939289093,0.663413941860199,0.5,-0.5566704273223877,0.6348415613174438,0.5,-0.5890468955039978,0.6046908497810364,0.5,-0.6199588775634766,0.5730367302894592,0.5,-0.6493295431137085,0.5399580001831055,0.5,-0.6770859360694885,0.5055368542671204,0.5,-0.7031589150428772,0.469858855009079,0.5,-0.7274838089942932,0.4330126941204071,0.5,-0.75,0.3950900137424469,0.5,-0.7706515789031982,0.35618507862091064,0.5,-0.7893872261047363,0.3163945972919464,0.5,-0.8061603307723999,0.27581754326820374,0.5,-0.8209291696548462,0.234554722905159,0.5,-0.8336570262908936,0.19270877540111542,0.5,-0.844312310218811,0.15038372576236725,0.5,-0.8528685569763184,0.10768480598926544,0.5,-0.8593043684959412,0.06471815705299377,0.5,-0.8636038303375244,0.021590612828731537,0.5,-0.8657562136650085,-0.021590612828731537,0.5,-0.8657562136650085,-0.06471815705299377,0.5,-0.8636038303375244,-0.10768480598926544,0.5,-0.8593043684959412,-0.15038372576236725,0.5,-0.8528685569763184,-0.19270877540111542,0.5,-0.844312310218811,-0.234554722905159,0.5,-0.8336570262908936,-0.27581754326820374,0.5,-0.8209291696548462,-0.3163945972919464,0.5,-0.8061603307723999,-0.35618507862091064,0.5,-0.7893872261047363,-0.3950900137424469,0.5,-0.7706515789031982,-0.4330126941204071,0.5,-0.75,-0.469858855009079,0.5,-0.7274838089942932,-0.5055368542671204,0.5,-0.7031589150428772,-0.5399580001831055,0.5,-0.6770859360694885,-0.5730367302894592,0.5,-0.6493295431137085,-0.6046908497810364,0.5,-0.6199588775634766,-0.6348415613174438,0.5,-0.5890468955039978,-0.663413941860199,0.5,-0.5566704273223877,-0.6903370022773743,0.5,-0.522909939289093,-0.7155437469482422,0.5,-0.48784947395324707,-0.7389715909957886,0.5,-0.4515761435031891,-0.7605621814727783,0.5,-0.41418009996414185,-0.7802619338035583,0.5,-0.37575432658195496,-0.7980217933654785,0.5,-0.33639439940452576,-0.813797652721405,0.5,-0.29619812965393066,-0.8275503516197205,0.5,-0.255265474319458,-0.8392455577850342,0.5,-0.21369817852973938,-0.8488543033599854,0.5,-0.1715995967388153,-0.8563526272773743,0.5,-0.12907439470291138,-0.861721932888031,0.5,-0.08622828125953674,-0.8649488687515259,0.5,-0.043167803436517715,-0.8660253882408142,0.5,-1.0605752472130298e-16,-0.8649488687515259,0.5,0.043167803436517715,-0.861721932888031,0.5,0.08622828125953674,-0.8563526272773743,0.5,0.12907439470291138,-0.8488543033599854,0.5,0.1715995967388153,-0.8392455577850342,0.5,0.21369817852973938,-0.8275503516197205,0.5,0.255265474319458,-0.813797652721405,0.5,0.29619812965393066,-0.7980217933654785,0.5,0.33639439940452576,-0.7802619338035583,0.5,0.37575432658195496,-0.7605621814727783,0.5,0.41418009996414185,-0.7389715909957886,0.5,0.4515761435031891,-0.7155437469482422,0.5,0.48784947395324707,-0.6903370022773743,0.5,0.522909939289093,-0.663413941860199,0.5,0.5566704273223877,-0.6348415613174438,0.5,0.5890468955039978,-0.6046908497810364,0.5,0.6199588775634766,-0.5730367302894592,0.5,0.6493295431137085,-0.5399580001831055,0.5,0.6770859360694885,-0.5055368542671204,0.5,0.7031589150428772,-0.469858855009079,0.5,0.7274838089942932,-0.4330126941204071,0.5,0.75,-0.3950900137424469,0.5,0.7706515789031982,-0.35618507862091064,0.5,0.7893872261047363,-0.3163945972919464,0.5,0.8061603307723999,-0.27581754326820374,0.5,0.8209291696548462,-0.234554722905159,0.5,0.8336570262908936,-0.19270877540111542,0.5,0.844312310218811,-0.15038372576236725,0.5,0.8528685569763184,-0.10768480598926544,0.5,0.8593043684959412,-0.06471815705299377,0.5,0.8636038303375244,-0.021590612828731537,0.5,0.8657562136650085,0.021590612828731537,0.5,0.8657562136650085,0.06471815705299377,0.5,0.8636038303375244,0.10768480598926544,0.5,0.8593043684959412,0.15038372576236725,0.5,0.8528685569763184,0.19270877540111542,0.5,0.844312310218811,0.234554722905159,0.5,0.8336570262908936,0.27581754326820374,0.5,0.8209291696548462,0.3163945972919464,0.5,0.8061603307723999,0.35618507862091064,0.5,0.7893872261047363,0.3950900137424469,0.5,0.7706515789031982,0.4330126941204071,0.5,0.75,0.469858855009079,0.5,0.7274838089942932,0.5055368542671204,0.5,0.7031589150428772,0.5399580001831055,0.5,0.6770859360694885,0.5730367302894592,0.5,0.6493295431137085,0.6046908497810364,0.5,0.6199588775634766,0.6348415613174438,0.5,0.5890468955039978,0.663413941860199,0.5,0.5566704273223877,0.6903370022773743,0.5,0.522909939289093,0.7155437469482422,0.5,0.48784947395324707,0.7389715909957886,0.5,0.4515761435031891,0.7605621814727783,0.5,0.41418009996414185,0.7802619338035583,0.5,0.37575432658195496,0.7980217933654785,0.5,0.33639439940452576,0.813797652721405,0.5,0.29619812965393066,0.8275503516197205,0.5,0.255265474319458,0.8392455577850342,0.5,0.21369817852973938,0.8488543033599854,0.5,0.1715995967388153,0.8563526272773743,0.5,0.12907439470291138,0.861721932888031,0.5,0.08622828125953674,0.8649488687515259,0.5,0.043167803436517715,0.8660253882408142,0.5,2.1211504944260596e-16,0.8400259017944336,0.542546272277832,0.0,0.8389816880226135,0.542546272277832,-0.04187183454632759,0.8358516693115234,0.542546272277832,-0.08363956958055496,0.8306435346603394,0.542546272277832,-0.1251993626356125,0.8233702778816223,0.542546272277832,-0.16644789278507233,0.814050018787384,0.542546272277832,-0.20728261768817902,0.8027059435844421,0.542546272277832,-0.24760198593139648,0.7893661856651306,0.542546272277832,-0.2873057723045349,0.7740638852119446,0.542546272277832,-0.32629528641700745,0.7568371891975403,0.542546272277832,-0.3644735813140869,0.7377288937568665,0.542546272277832,-0.4017457365989685,0.7167864441871643,0.542546272277832,-0.4380190968513489,0.6940619945526123,0.542546272277832,-0.4732034504413605,0.6696119904518127,0.542546272277832,-0.5072113275527954,0.6434971690177917,0.542546272277832,-0.5399582386016846,0.6157825589179993,0.542546272277832,-0.5713627338409424,0.5865370035171509,0.542546272277832,-0.6013467311859131,0.5558332800865173,0.542546272277832,-0.6298356652259827,0.5237476229667664,0.542546272277832,-0.656758725643158,0.4903598129749298,0.542546272277832,-0.6820489764213562,0.4557529389858246,0.542546272277832,-0.7056435346603394,0.4200129508972168,0.542546272277832,-0.7274838089942932,0.38322877883911133,0.542546272277832,-0.7475153803825378,0.34549182653427124,0.542546272277832,-0.7656885385513306,0.3068959414958954,0.542546272277832,-0.7819581031799316,0.26753702759742737,0.542546272277832,-0.7962835431098938,0.22751301527023315,0.542546272277832,-0.8086293339729309,0.18692335486412048,0.542546272277832,-0.8189647197723389,0.14586897194385529,0.542546272277832,-0.8272640705108643,0.10445193201303482,0.542546272277832,-0.8335066437721252,0.06277521699666977,0.542546272277832,-0.8376770615577698,0.020942427217960358,0.542546272277832,-0.8397648334503174,-0.020942427217960358,0.542546272277832,-0.8397648334503174,-0.06277521699666977,0.542546272277832,-0.8376770615577698,-0.10445193201303482,0.542546272277832,-0.8335066437721252,-0.14586897194385529,0.542546272277832,-0.8272640705108643,-0.18692335486412048,0.542546272277832,-0.8189647197723389,-0.22751301527023315,0.542546272277832,-0.8086293339729309,-0.26753702759742737,0.542546272277832,-0.7962835431098938,-0.3068959414958954,0.542546272277832,-0.7819581031799316,-0.34549182653427124,0.542546272277832,-0.7656885385513306,-0.38322877883911133,0.542546272277832,-0.7475153803825378,-0.4200129508972168,0.542546272277832,-0.7274838089942932,-0.4557529389858246,0.542546272277832,-0.7056435346603394,-0.4903598129749298,0.542546272277832,-0.6820489764213562,-0.5237476229667664,0.542546272277832,-0.656758725643158,-0.5558332800865173,0.542546272277832,-0.6298356652259827,-0.5865370035171509,0.542546272277832,-0.6013467311859131,-0.6157825589179993,0.542546272277832,-0.5713627338409424,-0.6434971690177917,0.542546272277832,-0.5399582386016846,-0.6696119904518127,0.542546272277832,-0.5072113275527954,-0.6940619945526123,0.542546272277832,-0.4732034504413605,-0.7167864441871643,0.542546272277832,-0.4380190968513489,-0.7377288937568665,0.542546272277832,-0.4017457365989685,-0.7568371891975403,0.542546272277832,-0.3644735813140869,-0.7740638852119446,0.542546272277832,-0.32629528641700745,-0.7893661856651306,0.542546272277832,-0.2873057723045349,-0.8027059435844421,0.542546272277832,-0.24760198593139648,-0.814050018787384,0.542546272277832,-0.20728261768817902,-0.8233702778816223,0.542546272277832,-0.16644789278507233,-0.8306435346603394,0.542546272277832,-0.1251993626356125,-0.8358516693115234,0.542546272277832,-0.08363956958055496,-0.8389816880226135,0.542546272277832,-0.04187183454632759,-0.8400259017944336,0.542546272277832,-1.0287350802023936e-16,-0.8389816880226135,0.542546272277832,0.04187183454632759,-0.8358516693115234,0.542546272277832,0.08363956958055496,-0.8306435346603394,0.542546272277832,0.1251993626356125,-0.8233702778816223,0.542546272277832,0.16644789278507233,-0.814050018787384,0.542546272277832,0.20728261768817902,-0.8027059435844421,0.542546272277832,0.24760198593139648,-0.7893661856651306,0.542546272277832,0.2873057723045349,-0.7740638852119446,0.542546272277832,0.32629528641700745,-0.7568371891975403,0.542546272277832,0.3644735813140869,-0.7377288937568665,0.542546272277832,0.4017457365989685,-0.7167864441871643,0.542546272277832,0.4380190968513489,-0.6940619945526123,0.542546272277832,0.4732034504413605,-0.6696119904518127,0.542546272277832,0.5072113275527954,-0.6434971690177917,0.542546272277832,0.5399582386016846,-0.6157825589179993,0.542546272277832,0.5713627338409424,-0.5865370035171509,0.542546272277832,0.6013467311859131,-0.5558332800865173,0.542546272277832,0.6298356652259827,-0.5237476229667664,0.542546272277832,0.656758725643158,-0.4903598129749298,0.542546272277832,0.6820489764213562,-0.4557529389858246,0.542546272277832,0.7056435346603394,-0.4200129508972168,0.542546272277832,0.7274838089942932,-0.38322877883911133,0.542546272277832,0.7475153803825378,-0.34549182653427124,0.542546272277832,0.7656885385513306,-0.3068959414958954,0.542546272277832,0.7819581031799316,-0.26753702759742737,0.542546272277832,0.7962835431098938,-0.22751301527023315,0.542546272277832,0.8086293339729309,-0.18692335486412048,0.542546272277832,0.8189647197723389,-0.14586897194385529,0.542546272277832,0.8272640705108643,-0.10445193201303482,0.542546272277832,0.8335066437721252,-0.06277521699666977,0.542546272277832,0.8376770615577698,-0.020942427217960358,0.542546272277832,0.8397648334503174,0.020942427217960358,0.542546272277832,0.8397648334503174,0.06277521699666977,0.542546272277832,0.8376770615577698,0.10445193201303482,0.542546272277832,0.8335066437721252,0.14586897194385529,0.542546272277832,0.8272640705108643,0.18692335486412048,0.542546272277832,0.8189647197723389,0.22751301527023315,0.542546272277832,0.8086293339729309,0.26753702759742737,0.542546272277832,0.7962835431098938,0.3068959414958954,0.542546272277832,0.7819581031799316,0.34549182653427124,0.542546272277832,0.7656885385513306,0.38322877883911133,0.542546272277832,0.7475153803825378,0.4200129508972168,0.542546272277832,0.7274838089942932,0.4557529389858246,0.542546272277832,0.7056435346603394,0.4903598129749298,0.542546272277832,0.6820489764213562,0.5237476229667664,0.542546272277832,0.656758725643158,0.5558332800865173,0.542546272277832,0.6298356652259827,0.5865370035171509,0.542546272277832,0.6013467311859131,0.6157825589179993,0.542546272277832,0.5713627338409424,0.6434971690177917,0.542546272277832,0.5399582386016846,0.6696119904518127,0.542546272277832,0.5072113275527954,0.6940619945526123,0.542546272277832,0.4732034504413605,0.7167864441871643,0.542546272277832,0.4380190968513489,0.7377288937568665,0.542546272277832,0.4017457365989685,0.7568371891975403,0.542546272277832,0.3644735813140869,0.7740638852119446,0.542546272277832,0.32629528641700745,0.7893661856651306,0.542546272277832,0.2873057723045349,0.8027059435844421,0.542546272277832,0.24760198593139648,0.814050018787384,0.542546272277832,0.20728261768817902,0.8233702778816223,0.542546272277832,0.16644789278507233,0.8306435346603394,0.542546272277832,0.1251993626356125,0.8358516693115234,0.542546272277832,0.08363956958055496,0.8389816880226135,0.542546272277832,0.04187183454632759,0.8400259017944336,0.542546272277832,2.0574701604047873e-16,0.8119379878044128,0.583743691444397,0.0,0.8109287023544312,0.583743691444397,-0.040471769869327545,0.8079032897949219,0.583743691444397,-0.08084291964769363,0.8028693199157715,0.583743691444397,-0.1210130825638771,0.7958393096923828,0.583743691444397,-0.1608823835849762,0.7868306636810303,0.583743691444397,-0.20035171508789062,0.7758658528327942,0.583743691444397,-0.23932293057441711,0.7629721760749817,0.583743691444397,-0.27769914269447327,0.7481815814971924,0.583743691444397,-0.31538498401641846,0.7315308451652527,0.583743691444397,-0.352286696434021,0.7130614519119263,0.583743691444397,-0.38831257820129395,0.6928192973136902,0.583743691444397,-0.4233730733394623,0.6708546876907349,0.583743691444397,-0.4573809504508972,0.6472221612930298,0.583743691444397,-0.490251749753952,0.621980607509613,0.583743691444397,-0.5219036936759949,0.5951926708221436,0.583743691444397,-0.5522580742835999,0.5669249892234802,0.583743691444397,-0.5812395215034485,0.537247896194458,0.583743691444397,-0.6087758541107178,0.5062350630760193,0.583743691444397,-0.6347987055778503,0.4739636778831482,0.583743691444397,-0.6592433452606201,0.44051393866539,0.583743691444397,-0.6820489764213562,0.4059689939022064,0.583743691444397,-0.7031589150428772,0.37041476368904114,0.583743691444397,-0.7225207686424255,0.33393964171409607,0.583743691444397,-0.7400862574577332,0.2966342568397522,0.583743691444397,-0.7558117508888245,0.2585914134979248,0.583743691444397,-0.7696582674980164,0.21990567445755005,0.583743691444397,-0.7815912365913391,0.18067319691181183,0.583743691444397,-0.7915810346603394,0.14099155366420746,0.583743691444397,-0.7996028661727905,0.10095938295125961,0.583743691444397,-0.8056367039680481,0.060676202178001404,0.583743691444397,-0.8096676468849182,0.020242176949977875,0.583743691444397,-0.8116856217384338,-0.020242176949977875,0.583743691444397,-0.8116856217384338,-0.060676202178001404,0.583743691444397,-0.8096676468849182,-0.10095938295125961,0.583743691444397,-0.8056367039680481,-0.14099155366420746,0.583743691444397,-0.7996028661727905,-0.18067319691181183,0.583743691444397,-0.7915810346603394,-0.21990567445755005,0.583743691444397,-0.7815912365913391,-0.2585914134979248,0.583743691444397,-0.7696582674980164,-0.2966342568397522,0.583743691444397,-0.7558117508888245,-0.33393964171409607,0.583743691444397,-0.7400862574577332,-0.37041476368904114,0.583743691444397,-0.7225207686424255,-0.4059689939022064,0.583743691444397,-0.7031589150428772,-0.44051393866539,0.583743691444397,-0.6820489764213562,-0.4739636778831482,0.583743691444397,-0.6592433452606201,-0.5062350630760193,0.583743691444397,-0.6347987055778503,-0.537247896194458,0.583743691444397,-0.6087758541107178,-0.5669249892234802,0.583743691444397,-0.5812395215034485,-0.5951926708221436,0.583743691444397,-0.5522580742835999,-0.621980607509613,0.583743691444397,-0.5219036936759949,-0.6472221612930298,0.583743691444397,-0.490251749753952,-0.6708546876907349,0.583743691444397,-0.4573809504508972,-0.6928192973136902,0.583743691444397,-0.4233730733394623,-0.7130614519119263,0.583743691444397,-0.38831257820129395,-0.7315308451652527,0.583743691444397,-0.352286696434021,-0.7481815814971924,0.583743691444397,-0.31538498401641846,-0.7629721760749817,0.583743691444397,-0.27769914269447327,-0.7758658528327942,0.583743691444397,-0.23932293057441711,-0.7868306636810303,0.583743691444397,-0.20035171508789062,-0.7958393096923828,0.583743691444397,-0.1608823835849762,-0.8028693199157715,0.583743691444397,-0.1210130825638771,-0.8079032897949219,0.583743691444397,-0.08084291964769363,-0.8109287023544312,0.583743691444397,-0.040471769869327545,-0.8119379878044128,0.583743691444397,-9.943372707377435e-17,-0.8109287023544312,0.583743691444397,0.040471769869327545,-0.8079032897949219,0.583743691444397,0.08084291964769363,-0.8028693199157715,0.583743691444397,0.1210130825638771,-0.7958393096923828,0.583743691444397,0.1608823835849762,-0.7868306636810303,0.583743691444397,0.20035171508789062,-0.7758658528327942,0.583743691444397,0.23932293057441711,-0.7629721760749817,0.583743691444397,0.27769914269447327,-0.7481815814971924,0.583743691444397,0.31538498401641846,-0.7315308451652527,0.583743691444397,0.352286696434021,-0.7130614519119263,0.583743691444397,0.38831257820129395,-0.6928192973136902,0.583743691444397,0.4233730733394623,-0.6708546876907349,0.583743691444397,0.4573809504508972,-0.6472221612930298,0.583743691444397,0.490251749753952,-0.621980607509613,0.583743691444397,0.5219036936759949,-0.5951926708221436,0.583743691444397,0.5522580742835999,-0.5669249892234802,0.583743691444397,0.5812395215034485,-0.537247896194458,0.583743691444397,0.6087758541107178,-0.5062350630760193,0.583743691444397,0.6347987055778503,-0.4739636778831482,0.583743691444397,0.6592433452606201,-0.44051393866539,0.583743691444397,0.6820489764213562,-0.4059689939022064,0.583743691444397,0.7031589150428772,-0.37041476368904114,0.583743691444397,0.7225207686424255,-0.33393964171409607,0.583743691444397,0.7400862574577332,-0.2966342568397522,0.583743691444397,0.7558117508888245,-0.2585914134979248,0.583743691444397,0.7696582674980164,-0.21990567445755005,0.583743691444397,0.7815912365913391,-0.18067319691181183,0.583743691444397,0.7915810346603394,-0.14099155366420746,0.583743691444397,0.7996028661727905,-0.10095938295125961,0.583743691444397,0.8056367039680481,-0.060676202178001404,0.583743691444397,0.8096676468849182,-0.020242176949977875,0.583743691444397,0.8116856217384338,0.020242176949977875,0.583743691444397,0.8116856217384338,0.060676202178001404,0.583743691444397,0.8096676468849182,0.10095938295125961,0.583743691444397,0.8056367039680481,0.14099155366420746,0.583743691444397,0.7996028661727905,0.18067319691181183,0.583743691444397,0.7915810346603394,0.21990567445755005,0.583743691444397,0.7815912365913391,0.2585914134979248,0.583743691444397,0.7696582674980164,0.2966342568397522,0.583743691444397,0.7558117508888245,0.33393964171409607,0.583743691444397,0.7400862574577332,0.37041476368904114,0.583743691444397,0.7225207686424255,0.4059689939022064,0.583743691444397,0.7031589150428772,0.44051393866539,0.583743691444397,0.6820489764213562,0.4739636778831482,0.583743691444397,0.6592433452606201,0.5062350630760193,0.583743691444397,0.6347987055778503,0.537247896194458,0.583743691444397,0.6087758541107178,0.5669249892234802,0.583743691444397,0.5812395215034485,0.5951926708221436,0.583743691444397,0.5522580742835999,0.621980607509613,0.583743691444397,0.5219036936759949,0.6472221612930298,0.583743691444397,0.490251749753952,0.6708546876907349,0.583743691444397,0.4573809504508972,0.6928192973136902,0.583743691444397,0.4233730733394623,0.7130614519119263,0.583743691444397,0.38831257820129395,0.7315308451652527,0.583743691444397,0.352286696434021,0.7481815814971924,0.583743691444397,0.31538498401641846,0.7629721760749817,0.583743691444397,0.27769914269447327,0.7758658528327942,0.583743691444397,0.23932293057441711,0.7868306636810303,0.583743691444397,0.20035171508789062,0.7958393096923828,0.583743691444397,0.1608823835849762,0.8028693199157715,0.583743691444397,0.1210130825638771,0.8079032897949219,0.583743691444397,0.08084291964769363,0.8109287023544312,0.583743691444397,0.040471769869327545,0.8119379878044128,0.583743691444397,1.988674541475487e-16,0.7818315029144287,0.6234897971153259,0.0,0.7808595895767212,0.6234897971153259,-0.038971081376075745,0.777946412563324,0.6234897971153259,-0.07784527540206909,0.7730990648269653,0.6234897971153259,-0.11652593314647675,0.7663297057151794,0.6234897971153259,-0.1549168974161148,0.757655143737793,0.6234897971153259,-0.1929226964712143,0.7470968961715698,0.6234897971153259,-0.2304488718509674,0.734681248664856,0.6234897971153259,-0.26740211248397827,0.7204391360282898,0.6234897971153259,-0.3036905527114868,0.7044058442115784,0.6234897971153259,-0.3392239809036255,0.6866212487220764,0.6234897971153259,-0.3739140033721924,0.6671296954154968,0.6234897971153259,-0.4076744616031647,0.6459794640541077,0.6234897971153259,-0.44042134284973145,0.6232233047485352,0.6234897971153259,-0.4720732867717743,0.5989176630973816,0.6234897971153259,-0.5025516152381897,0.573123037815094,0.6234897971153259,-0.531780481338501,0.5459035038948059,0.6234897971153259,-0.5596872568130493,0.517326831817627,0.6234897971153259,-0.5862025618553162,0.48746395111083984,0.6234897971153259,-0.6112604737281799,0.4563891887664795,0.6234897971153259,-0.6347987055778503,0.4241797626018524,0.6234897971153259,-0.656758725643158,0.39091575145721436,0.6234897971153259,-0.6770859360694885,0.35667985677719116,0.6234897971153259,-0.6957297921180725,0.3215571939945221,0.6234897971153259,-0.7126439809799194,0.2856351137161255,0.6234897971153259,-0.7277864217758179,0.24900288879871368,0.6234897971153259,-0.7411194443702698,0.21175161004066467,0.6234897971153259,-0.7526099681854248,0.17397387325763702,0.6234897971153259,-0.762229323387146,0.13576361536979675,0.6234897971153259,-0.769953727722168,0.09721582382917404,0.6234897971153259,-0.7757638692855835,0.05842633917927742,0.6234897971153259,-0.7796453237533569,0.01949159987270832,0.6234897971153259,-0.7815884947776794,-0.01949159987270832,0.6234897971153259,-0.7815884947776794,-0.05842633917927742,0.6234897971153259,-0.7796453237533569,-0.09721582382917404,0.6234897971153259,-0.7757638692855835,-0.13576361536979675,0.6234897971153259,-0.769953727722168,-0.17397387325763702,0.6234897971153259,-0.762229323387146,-0.21175161004066467,0.6234897971153259,-0.7526099681854248,-0.24900288879871368,0.6234897971153259,-0.7411194443702698,-0.2856351137161255,0.6234897971153259,-0.7277864217758179,-0.3215571939945221,0.6234897971153259,-0.7126439809799194,-0.35667985677719116,0.6234897971153259,-0.6957297921180725,-0.39091575145721436,0.6234897971153259,-0.6770859360694885,-0.4241797626018524,0.6234897971153259,-0.656758725643158,-0.4563891887664795,0.6234897971153259,-0.6347987055778503,-0.48746395111083984,0.6234897971153259,-0.6112604737281799,-0.517326831817627,0.6234897971153259,-0.5862025618553162,-0.5459035038948059,0.6234897971153259,-0.5596872568130493,-0.573123037815094,0.6234897971153259,-0.531780481338501,-0.5989176630973816,0.6234897971153259,-0.5025516152381897,-0.6232233047485352,0.6234897971153259,-0.4720732867717743,-0.6459794640541077,0.6234897971153259,-0.44042134284973145,-0.6671296954154968,0.6234897971153259,-0.4076744616031647,-0.6866212487220764,0.6234897971153259,-0.3739140033721924,-0.7044058442115784,0.6234897971153259,-0.3392239809036255,-0.7204391360282898,0.6234897971153259,-0.3036905527114868,-0.734681248664856,0.6234897971153259,-0.26740211248397827,-0.7470968961715698,0.6234897971153259,-0.2304488718509674,-0.757655143737793,0.6234897971153259,-0.1929226964712143,-0.7663297057151794,0.6234897971153259,-0.1549168974161148,-0.7730990648269653,0.6234897971153259,-0.11652593314647675,-0.777946412563324,0.6234897971153259,-0.07784527540206909,-0.7808595895767212,0.6234897971153259,-0.038971081376075745,-0.7818315029144287,0.6234897971153259,-9.574674485560909e-17,-0.7808595895767212,0.6234897971153259,0.038971081376075745,-0.777946412563324,0.6234897971153259,0.07784527540206909,-0.7730990648269653,0.6234897971153259,0.11652593314647675,-0.7663297057151794,0.6234897971153259,0.1549168974161148,-0.757655143737793,0.6234897971153259,0.1929226964712143,-0.7470968961715698,0.6234897971153259,0.2304488718509674,-0.734681248664856,0.6234897971153259,0.26740211248397827,-0.7204391360282898,0.6234897971153259,0.3036905527114868,-0.7044058442115784,0.6234897971153259,0.3392239809036255,-0.6866212487220764,0.6234897971153259,0.3739140033721924,-0.6671296954154968,0.6234897971153259,0.4076744616031647,-0.6459794640541077,0.6234897971153259,0.44042134284973145,-0.6232233047485352,0.6234897971153259,0.4720732867717743,-0.5989176630973816,0.6234897971153259,0.5025516152381897,-0.573123037815094,0.6234897971153259,0.531780481338501,-0.5459035038948059,0.6234897971153259,0.5596872568130493,-0.517326831817627,0.6234897971153259,0.5862025618553162,-0.48746395111083984,0.6234897971153259,0.6112604737281799,-0.4563891887664795,0.6234897971153259,0.6347987055778503,-0.4241797626018524,0.6234897971153259,0.656758725643158,-0.39091575145721436,0.6234897971153259,0.6770859360694885,-0.35667985677719116,0.6234897971153259,0.6957297921180725,-0.3215571939945221,0.6234897971153259,0.7126439809799194,-0.2856351137161255,0.6234897971153259,0.7277864217758179,-0.24900288879871368,0.6234897971153259,0.7411194443702698,-0.21175161004066467,0.6234897971153259,0.7526099681854248,-0.17397387325763702,0.6234897971153259,0.762229323387146,-0.13576361536979675,0.6234897971153259,0.769953727722168,-0.09721582382917404,0.6234897971153259,0.7757638692855835,-0.05842633917927742,0.6234897971153259,0.7796453237533569,-0.01949159987270832,0.6234897971153259,0.7815884947776794,0.01949159987270832,0.6234897971153259,0.7815884947776794,0.05842633917927742,0.6234897971153259,0.7796453237533569,0.09721582382917404,0.6234897971153259,0.7757638692855835,0.13576361536979675,0.6234897971153259,0.769953727722168,0.17397387325763702,0.6234897971153259,0.762229323387146,0.21175161004066467,0.6234897971153259,0.7526099681854248,0.24900288879871368,0.6234897971153259,0.7411194443702698,0.2856351137161255,0.6234897971153259,0.7277864217758179,0.3215571939945221,0.6234897971153259,0.7126439809799194,0.35667985677719116,0.6234897971153259,0.6957297921180725,0.39091575145721436,0.6234897971153259,0.6770859360694885,0.4241797626018524,0.6234897971153259,0.656758725643158,0.4563891887664795,0.6234897971153259,0.6347987055778503,0.48746395111083984,0.6234897971153259,0.6112604737281799,0.517326831817627,0.6234897971153259,0.5862025618553162,0.5459035038948059,0.6234897971153259,0.5596872568130493,0.573123037815094,0.6234897971153259,0.531780481338501,0.5989176630973816,0.6234897971153259,0.5025516152381897,0.6232233047485352,0.6234897971153259,0.4720732867717743,0.6459794640541077,0.6234897971153259,0.44042134284973145,0.6671296954154968,0.6234897971153259,0.4076744616031647,0.6866212487220764,0.6234897971153259,0.3739140033721924,0.7044058442115784,0.6234897971153259,0.3392239809036255,0.7204391360282898,0.6234897971153259,0.3036905527114868,0.734681248664856,0.6234897971153259,0.26740211248397827,0.7470968961715698,0.6234897971153259,0.2304488718509674,0.757655143737793,0.6234897971153259,0.1929226964712143,0.7663297057151794,0.6234897971153259,0.1549168974161148,0.7730990648269653,0.6234897971153259,0.11652593314647675,0.777946412563324,0.6234897971153259,0.07784527540206909,0.7808595895767212,0.6234897971153259,0.038971081376075745,0.7818315029144287,0.6234897971153259,1.9149348971121818e-16,0.7497811913490295,0.6616858243942261,0.0,0.7488491535186768,0.6616858243942261,-0.037373509258031845,0.7460553646087646,0.6616858243942261,-0.07465410232543945,0.7414067387580872,0.6616858243942261,-0.1117490902543068,0.7349148988723755,0.6616858243942261,-0.14856626093387604,0.726595938205719,0.6616858243942261,-0.1850140541791916,0.7164705395698547,0.6616858243942261,-0.22100189328193665,0.7045638561248779,0.6616858243942261,-0.25644028186798096,0.6909055709838867,0.6616858243942261,-0.2912411093711853,0.6755295395851135,0.6616858243942261,-0.3253178596496582,0.6584740281105042,0.6616858243942261,-0.35858583450317383,0.6397814750671387,0.6616858243942261,-0.39096230268478394,0.6194983124732971,0.6616858243942261,-0.42236679792404175,0.5976749658584595,0.6616858243942261,-0.4527212083339691,0.5743657350540161,0.6616858243942261,-0.4819500744342804,0.549628496170044,0.6616858243942261,-0.5099807381629944,0.523524820804596,0.6616858243942261,-0.5367435216903687,0.49611958861351013,0.6616858243942261,-0.5621718764305115,0.46748092770576477,0.6616858243942261,-0.5862025618553162,0.43768003582954407,0.6616858243942261,-0.6087758541107178,0.40679100155830383,0.6616858243942261,-0.6298356652259827,0.37489059567451477,0.6616858243942261,-0.6493295431137085,0.3420581817626953,0.6616858243942261,-0.6672091484069824,0.3083753287792206,0.6616858243942261,-0.6834299564361572,0.2739258408546448,0.6616858243942261,-0.69795161485672,0.23879531025886536,0.6616858243942261,-0.7107381224632263,0.20307108759880066,0.6616858243942261,-0.7217575907707214,0.1668420135974884,0.6616858243942261,-0.7309826016426086,0.130198135972023,0.6616858243942261,-0.738390326499939,0.09323057532310486,0.6616858243942261,-0.743962287902832,0.05603121966123581,0.6616858243942261,-0.7476846575737,0.018692564219236374,0.6616858243942261,-0.7495481371879578,-0.018692564219236374,0.6616858243942261,-0.7495481371879578,-0.05603121966123581,0.6616858243942261,-0.7476846575737,-0.09323057532310486,0.6616858243942261,-0.743962287902832,-0.130198135972023,0.6616858243942261,-0.738390326499939,-0.1668420135974884,0.6616858243942261,-0.7309826016426086,-0.20307108759880066,0.6616858243942261,-0.7217575907707214,-0.23879531025886536,0.6616858243942261,-0.7107381224632263,-0.2739258408546448,0.6616858243942261,-0.69795161485672,-0.3083753287792206,0.6616858243942261,-0.6834299564361572,-0.3420581817626953,0.6616858243942261,-0.6672091484069824,-0.37489059567451477,0.6616858243942261,-0.6493295431137085,-0.40679100155830383,0.6616858243942261,-0.6298356652259827,-0.43768003582954407,0.6616858243942261,-0.6087758541107178,-0.46748092770576477,0.6616858243942261,-0.5862025618553162,-0.49611958861351013,0.6616858243942261,-0.5621718764305115,-0.523524820804596,0.6616858243942261,-0.5367435216903687,-0.549628496170044,0.6616858243942261,-0.5099807381629944,-0.5743657350540161,0.6616858243942261,-0.4819500744342804,-0.5976749658584595,0.6616858243942261,-0.4527212083339691,-0.6194983124732971,0.6616858243942261,-0.42236679792404175,-0.6397814750671387,0.6616858243942261,-0.39096230268478394,-0.6584740281105042,0.6616858243942261,-0.35858583450317383,-0.6755295395851135,0.6616858243942261,-0.3253178596496582,-0.6909055709838867,0.6616858243942261,-0.2912411093711853,-0.7045638561248779,0.6616858243942261,-0.25644028186798096,-0.7164705395698547,0.6616858243942261,-0.22100189328193665,-0.726595938205719,0.6616858243942261,-0.1850140541791916,-0.7349148988723755,0.6616858243942261,-0.14856626093387604,-0.7414067387580872,0.6616858243942261,-0.1117490902543068,-0.7460553646087646,0.6616858243942261,-0.07465410232543945,-0.7488491535186768,0.6616858243942261,-0.037373509258031845,-0.7497811913490295,0.6616858243942261,-9.182171329204087e-17,-0.7488491535186768,0.6616858243942261,0.037373509258031845,-0.7460553646087646,0.6616858243942261,0.07465410232543945,-0.7414067387580872,0.6616858243942261,0.1117490902543068,-0.7349148988723755,0.6616858243942261,0.14856626093387604,-0.726595938205719,0.6616858243942261,0.1850140541791916,-0.7164705395698547,0.6616858243942261,0.22100189328193665,-0.7045638561248779,0.6616858243942261,0.25644028186798096,-0.6909055709838867,0.6616858243942261,0.2912411093711853,-0.6755295395851135,0.6616858243942261,0.3253178596496582,-0.6584740281105042,0.6616858243942261,0.35858583450317383,-0.6397814750671387,0.6616858243942261,0.39096230268478394,-0.6194983124732971,0.6616858243942261,0.42236679792404175,-0.5976749658584595,0.6616858243942261,0.4527212083339691,-0.5743657350540161,0.6616858243942261,0.4819500744342804,-0.549628496170044,0.6616858243942261,0.5099807381629944,-0.523524820804596,0.6616858243942261,0.5367435216903687,-0.49611958861351013,0.6616858243942261,0.5621718764305115,-0.46748092770576477,0.6616858243942261,0.5862025618553162,-0.43768003582954407,0.6616858243942261,0.6087758541107178,-0.40679100155830383,0.6616858243942261,0.6298356652259827,-0.37489059567451477,0.6616858243942261,0.6493295431137085,-0.3420581817626953,0.6616858243942261,0.6672091484069824,-0.3083753287792206,0.6616858243942261,0.6834299564361572,-0.2739258408546448,0.6616858243942261,0.69795161485672,-0.23879531025886536,0.6616858243942261,0.7107381224632263,-0.20307108759880066,0.6616858243942261,0.7217575907707214,-0.1668420135974884,0.6616858243942261,0.7309826016426086,-0.130198135972023,0.6616858243942261,0.738390326499939,-0.09323057532310486,0.6616858243942261,0.743962287902832,-0.05603121966123581,0.6616858243942261,0.7476846575737,-0.018692564219236374,0.6616858243942261,0.7495481371879578,0.018692564219236374,0.6616858243942261,0.7495481371879578,0.05603121966123581,0.6616858243942261,0.7476846575737,0.09323057532310486,0.6616858243942261,0.743962287902832,0.130198135972023,0.6616858243942261,0.738390326499939,0.1668420135974884,0.6616858243942261,0.7309826016426086,0.20307108759880066,0.6616858243942261,0.7217575907707214,0.23879531025886536,0.6616858243942261,0.7107381224632263,0.2739258408546448,0.6616858243942261,0.69795161485672,0.3083753287792206,0.6616858243942261,0.6834299564361572,0.3420581817626953,0.6616858243942261,0.6672091484069824,0.37489059567451477,0.6616858243942261,0.6493295431137085,0.40679100155830383,0.6616858243942261,0.6298356652259827,0.43768003582954407,0.6616858243942261,0.6087758541107178,0.46748092770576477,0.6616858243942261,0.5862025618553162,0.49611958861351013,0.6616858243942261,0.5621718764305115,0.523524820804596,0.6616858243942261,0.5367435216903687,0.549628496170044,0.6616858243942261,0.5099807381629944,0.5743657350540161,0.6616858243942261,0.4819500744342804,0.5976749658584595,0.6616858243942261,0.4527212083339691,0.6194983124732971,0.6616858243942261,0.42236679792404175,0.6397814750671387,0.6616858243942261,0.39096230268478394,0.6584740281105042,0.6616858243942261,0.35858583450317383,0.6755295395851135,0.6616858243942261,0.3253178596496582,0.6909055709838867,0.6616858243942261,0.2912411093711853,0.7045638561248779,0.6616858243942261,0.25644028186798096,0.7164705395698547,0.6616858243942261,0.22100189328193665,0.726595938205719,0.6616858243942261,0.1850140541791916,0.7349148988723755,0.6616858243942261,0.14856626093387604,0.7414067387580872,0.6616858243942261,0.1117490902543068,0.7460553646087646,0.6616858243942261,0.07465410232543945,0.7488491535186768,0.6616858243942261,0.037373509258031845,0.7497811913490295,0.6616858243942261,1.8364342658408174e-16,0.7158668637275696,0.6982368230819702,0.0,0.714976966381073,0.6982368230819702,-0.03568301722407341,0.7123095393180847,0.6982368230819702,-0.07127732038497925,0.7078711986541748,0.6982368230819702,-0.10669441521167755,0.7016729712486267,0.6982368230819702,-0.14184625446796417,0.6937302947044373,0.6982368230819702,-0.1766454428434372,0.6840628981590271,0.6982368230819702,-0.21100546419620514,0.6726948022842407,0.6982368230819702,-0.24484087526798248,0.6596542596817017,0.6982368230819702,-0.27806758880615234,0.6449737548828125,0.6982368230819702,-0.31060299277305603,0.628689706325531,0.6982368230819702,-0.34236615896224976,0.6108426451683044,0.6982368230819702,-0.3732781708240509,0.5914769768714905,0.6982368230819702,-0.4032621681690216,0.5706407427787781,0.6982368230819702,-0.43224355578422546,0.5483857989311218,0.6982368230819702,-0.4601503312587738,0.5247675180435181,0.6982368230819702,-0.48691311478614807,0.49984458088874817,0.6982368230819702,-0.5124653577804565,0.47367894649505615,0.6982368230819702,-0.5367435216903687,0.44633567333221436,0.6982368230819702,-0.5596872568130493,0.4178827404975891,0.6982368230819702,-0.5812395215034485,0.3883908987045288,0.6982368230819702,-0.6013467311859131,0.3579334318637848,0.6982368230819702,-0.6199588775634766,0.3265860974788666,0.6982368230819702,-0.6370297074317932,0.2944267988204956,0.6982368230819702,-0.6525168418884277,0.26153552532196045,0.6982368230819702,-0.6663816571235657,0.22799403965473175,0.6982368230819702,-0.6785897612571716,0.1938857138156891,0.6982368230819702,-0.6891108155250549,0.15929536521434784,0.6982368230819702,-0.6979185938835144,0.12430897355079651,0.6982368230819702,-0.7049912214279175,0.08901353925466537,0.6982368230819702,-0.7103111743927002,0.053496796637773514,0.6982368230819702,-0.7138651609420776,0.017847055569291115,0.6982368230819702,-0.715644359588623,-0.017847055569291115,0.6982368230819702,-0.715644359588623,-0.053496796637773514,0.6982368230819702,-0.7138651609420776,-0.08901353925466537,0.6982368230819702,-0.7103111743927002,-0.12430897355079651,0.6982368230819702,-0.7049912214279175,-0.15929536521434784,0.6982368230819702,-0.6979185938835144,-0.1938857138156891,0.6982368230819702,-0.6891108155250549,-0.22799403965473175,0.6982368230819702,-0.6785897612571716,-0.26153552532196045,0.6982368230819702,-0.6663816571235657,-0.2944267988204956,0.6982368230819702,-0.6525168418884277,-0.3265860974788666,0.6982368230819702,-0.6370297074317932,-0.3579334318637848,0.6982368230819702,-0.6199588775634766,-0.3883908987045288,0.6982368230819702,-0.6013467311859131,-0.4178827404975891,0.6982368230819702,-0.5812395215034485,-0.44633567333221436,0.6982368230819702,-0.5596872568130493,-0.47367894649505615,0.6982368230819702,-0.5367435216903687,-0.49984458088874817,0.6982368230819702,-0.5124653577804565,-0.5247675180435181,0.6982368230819702,-0.48691311478614807,-0.5483857989311218,0.6982368230819702,-0.4601503312587738,-0.5706407427787781,0.6982368230819702,-0.43224355578422546,-0.5914769768714905,0.6982368230819702,-0.4032621681690216,-0.6108426451683044,0.6982368230819702,-0.3732781708240509,-0.628689706325531,0.6982368230819702,-0.34236615896224976,-0.6449737548828125,0.6982368230819702,-0.31060299277305603,-0.6596542596817017,0.6982368230819702,-0.27806758880615234,-0.6726948022842407,0.6982368230819702,-0.24484087526798248,-0.6840628981590271,0.6982368230819702,-0.21100546419620514,-0.6937302947044373,0.6982368230819702,-0.1766454428434372,-0.7016729712486267,0.6982368230819702,-0.14184625446796417,-0.7078711986541748,0.6982368230819702,-0.10669441521167755,-0.7123095393180847,0.6982368230819702,-0.07127732038497925,-0.714976966381073,0.6982368230819702,-0.03568301722407341,-0.7158668637275696,0.6982368230819702,-8.766840634918762e-17,-0.714976966381073,0.6982368230819702,0.03568301722407341,-0.7123095393180847,0.6982368230819702,0.07127732038497925,-0.7078711986541748,0.6982368230819702,0.10669441521167755,-0.7016729712486267,0.6982368230819702,0.14184625446796417,-0.6937302947044373,0.6982368230819702,0.1766454428434372,-0.6840628981590271,0.6982368230819702,0.21100546419620514,-0.6726948022842407,0.6982368230819702,0.24484087526798248,-0.6596542596817017,0.6982368230819702,0.27806758880615234,-0.6449737548828125,0.6982368230819702,0.31060299277305603,-0.628689706325531,0.6982368230819702,0.34236615896224976,-0.6108426451683044,0.6982368230819702,0.3732781708240509,-0.5914769768714905,0.6982368230819702,0.4032621681690216,-0.5706407427787781,0.6982368230819702,0.43224355578422546,-0.5483857989311218,0.6982368230819702,0.4601503312587738,-0.5247675180435181,0.6982368230819702,0.48691311478614807,-0.49984458088874817,0.6982368230819702,0.5124653577804565,-0.47367894649505615,0.6982368230819702,0.5367435216903687,-0.44633567333221436,0.6982368230819702,0.5596872568130493,-0.4178827404975891,0.6982368230819702,0.5812395215034485,-0.3883908987045288,0.6982368230819702,0.6013467311859131,-0.3579334318637848,0.6982368230819702,0.6199588775634766,-0.3265860974788666,0.6982368230819702,0.6370297074317932,-0.2944267988204956,0.6982368230819702,0.6525168418884277,-0.26153552532196045,0.6982368230819702,0.6663816571235657,-0.22799403965473175,0.6982368230819702,0.6785897612571716,-0.1938857138156891,0.6982368230819702,0.6891108155250549,-0.15929536521434784,0.6982368230819702,0.6979185938835144,-0.12430897355079651,0.6982368230819702,0.7049912214279175,-0.08901353925466537,0.6982368230819702,0.7103111743927002,-0.053496796637773514,0.6982368230819702,0.7138651609420776,-0.017847055569291115,0.6982368230819702,0.715644359588623,0.017847055569291115,0.6982368230819702,0.715644359588623,0.053496796637773514,0.6982368230819702,0.7138651609420776,0.08901353925466537,0.6982368230819702,0.7103111743927002,0.12430897355079651,0.6982368230819702,0.7049912214279175,0.15929536521434784,0.6982368230819702,0.6979185938835144,0.1938857138156891,0.6982368230819702,0.6891108155250549,0.22799403965473175,0.6982368230819702,0.6785897612571716,0.26153552532196045,0.6982368230819702,0.6663816571235657,0.2944267988204956,0.6982368230819702,0.6525168418884277,0.3265860974788666,0.6982368230819702,0.6370297074317932,0.3579334318637848,0.6982368230819702,0.6199588775634766,0.3883908987045288,0.6982368230819702,0.6013467311859131,0.4178827404975891,0.6982368230819702,0.5812395215034485,0.44633567333221436,0.6982368230819702,0.5596872568130493,0.47367894649505615,0.6982368230819702,0.5367435216903687,0.49984458088874817,0.6982368230819702,0.5124653577804565,0.5247675180435181,0.6982368230819702,0.48691311478614807,0.5483857989311218,0.6982368230819702,0.4601503312587738,0.5706407427787781,0.6982368230819702,0.43224355578422546,0.5914769768714905,0.6982368230819702,0.4032621681690216,0.6108426451683044,0.6982368230819702,0.3732781708240509,0.628689706325531,0.6982368230819702,0.34236615896224976,0.6449737548828125,0.6982368230819702,0.31060299277305603,0.6596542596817017,0.6982368230819702,0.27806758880615234,0.6726948022842407,0.6982368230819702,0.24484087526798248,0.6840628981590271,0.6982368230819702,0.21100546419620514,0.6937302947044373,0.6982368230819702,0.1766454428434372,0.7016729712486267,0.6982368230819702,0.14184625446796417,0.7078711986541748,0.6982368230819702,0.10669441521167755,0.7123095393180847,0.6982368230819702,0.07127732038497925,0.714976966381073,0.6982368230819702,0.03568301722407341,0.7158668637275696,0.6982368230819702,1.7533681269837523e-16,0.6801727414131165,0.7330518960952759,0.0,0.6793272495269775,0.7330518960952759,-0.0339038111269474,0.6767928004264832,0.7330518960952759,-0.0677233338356018,0.6725757718086243,0.7330518960952759,-0.10137448459863663,0.6666865944862366,0.7330518960952759,-0.1347736120223999,0.6591399312019348,0.7330518960952759,-0.16783764958381653,0.6499545574188232,0.7330518960952759,-0.20048443973064423,0.6391533017158508,0.7330518960952759,-0.23263277113437653,0.6267629861831665,0.7330518960952759,-0.2642027735710144,0.6128144860267639,0.7330518960952759,-0.2951158881187439,0.5973423719406128,0.7330518960952759,-0.3252953290939331,0.5803852081298828,0.7330518960952759,-0.3546660244464874,0.5619850754737854,0.7330518960952759,-0.383154958486557,0.5421878099441528,0.7330518960952759,-0.4106912910938263,0.52104252576828,0.7330518960952759,-0.43720659613609314,0.49860191345214844,0.7330518960952759,-0.46263495087623596,0.47492164373397827,0.7330518960952759,-0.48691311478614807,0.4500606656074524,0.7330518960952759,-0.5099807381629944,0.4240807592868805,0.7330518960952759,-0.531780481338501,0.3970465362071991,0.7330518960952759,-0.5522580742835999,0.36902517080307007,0.7330518960952759,-0.5713627338409424,0.3400863707065582,0.7330518960952759,-0.5890468955039978,0.3103020489215851,0.7330518960952759,-0.6052665710449219,0.27974626421928406,0.7330518960952759,-0.6199814081192017,0.24849499762058258,0.7330518960952759,-0.6331549286842346,0.21662594377994537,0.7330518960952759,-0.6447543501853943,0.18421830236911774,0.7330518960952759,-0.6547507643699646,0.1513526737689972,0.7330518960952759,-0.663119375705719,0.11811075359582901,0.7330518960952759,-0.6698393821716309,0.08457519859075546,0.7330518960952759,-0.6748940348625183,0.05082937330007553,0.7330518960952759,-0.6782708168029785,0.016957176849246025,0.7330518960952759,-0.6799613237380981,-0.016957176849246025,0.7330518960952759,-0.6799613237380981,-0.05082937330007553,0.7330518960952759,-0.6782708168029785,-0.08457519859075546,0.7330518960952759,-0.6748940348625183,-0.11811075359582901,0.7330518960952759,-0.6698393821716309,-0.1513526737689972,0.7330518960952759,-0.663119375705719,-0.18421830236911774,0.7330518960952759,-0.6547507643699646,-0.21662594377994537,0.7330518960952759,-0.6447543501853943,-0.24849499762058258,0.7330518960952759,-0.6331549286842346,-0.27974626421928406,0.7330518960952759,-0.6199814081192017,-0.3103020489215851,0.7330518960952759,-0.6052665710449219,-0.3400863707065582,0.7330518960952759,-0.5890468955039978,-0.36902517080307007,0.7330518960952759,-0.5713627338409424,-0.3970465362071991,0.7330518960952759,-0.5522580742835999,-0.4240807592868805,0.7330518960952759,-0.531780481338501,-0.4500606656074524,0.7330518960952759,-0.5099807381629944,-0.47492164373397827,0.7330518960952759,-0.48691311478614807,-0.49860191345214844,0.7330518960952759,-0.46263495087623596,-0.52104252576828,0.7330518960952759,-0.43720659613609314,-0.5421878099441528,0.7330518960952759,-0.4106912910938263,-0.5619850754737854,0.7330518960952759,-0.383154958486557,-0.5803852081298828,0.7330518960952759,-0.3546660244464874,-0.5973423719406128,0.7330518960952759,-0.3252953290939331,-0.6128144860267639,0.7330518960952759,-0.2951158881187439,-0.6267629861831665,0.7330518960952759,-0.2642027735710144,-0.6391533017158508,0.7330518960952759,-0.23263277113437653,-0.6499545574188232,0.7330518960952759,-0.20048443973064423,-0.6591399312019348,0.7330518960952759,-0.16783764958381653,-0.6666865944862366,0.7330518960952759,-0.1347736120223999,-0.6725757718086243,0.7330518960952759,-0.10137448459863663,-0.6767928004264832,0.7330518960952759,-0.0677233338356018,-0.6793272495269775,0.7330518960952759,-0.0339038111269474,-0.6801727414131165,0.7330518960952759,-8.329713400620419e-17,-0.6793272495269775,0.7330518960952759,0.0339038111269474,-0.6767928004264832,0.7330518960952759,0.0677233338356018,-0.6725757718086243,0.7330518960952759,0.10137448459863663,-0.6666865944862366,0.7330518960952759,0.1347736120223999,-0.6591399312019348,0.7330518960952759,0.16783764958381653,-0.6499545574188232,0.7330518960952759,0.20048443973064423,-0.6391533017158508,0.7330518960952759,0.23263277113437653,-0.6267629861831665,0.7330518960952759,0.2642027735710144,-0.6128144860267639,0.7330518960952759,0.2951158881187439,-0.5973423719406128,0.7330518960952759,0.3252953290939331,-0.5803852081298828,0.7330518960952759,0.3546660244464874,-0.5619850754737854,0.7330518960952759,0.383154958486557,-0.5421878099441528,0.7330518960952759,0.4106912910938263,-0.52104252576828,0.7330518960952759,0.43720659613609314,-0.49860191345214844,0.7330518960952759,0.46263495087623596,-0.47492164373397827,0.7330518960952759,0.48691311478614807,-0.4500606656074524,0.7330518960952759,0.5099807381629944,-0.4240807592868805,0.7330518960952759,0.531780481338501,-0.3970465362071991,0.7330518960952759,0.5522580742835999,-0.36902517080307007,0.7330518960952759,0.5713627338409424,-0.3400863707065582,0.7330518960952759,0.5890468955039978,-0.3103020489215851,0.7330518960952759,0.6052665710449219,-0.27974626421928406,0.7330518960952759,0.6199814081192017,-0.24849499762058258,0.7330518960952759,0.6331549286842346,-0.21662594377994537,0.7330518960952759,0.6447543501853943,-0.18421830236911774,0.7330518960952759,0.6547507643699646,-0.1513526737689972,0.7330518960952759,0.663119375705719,-0.11811075359582901,0.7330518960952759,0.6698393821716309,-0.08457519859075546,0.7330518960952759,0.6748940348625183,-0.05082937330007553,0.7330518960952759,0.6782708168029785,-0.016957176849246025,0.7330518960952759,0.6799613237380981,0.016957176849246025,0.7330518960952759,0.6799613237380981,0.05082937330007553,0.7330518960952759,0.6782708168029785,0.08457519859075546,0.7330518960952759,0.6748940348625183,0.11811075359582901,0.7330518960952759,0.6698393821716309,0.1513526737689972,0.7330518960952759,0.663119375705719,0.18421830236911774,0.7330518960952759,0.6547507643699646,0.21662594377994537,0.7330518960952759,0.6447543501853943,0.24849499762058258,0.7330518960952759,0.6331549286842346,0.27974626421928406,0.7330518960952759,0.6199814081192017,0.3103020489215851,0.7330518960952759,0.6052665710449219,0.3400863707065582,0.7330518960952759,0.5890468955039978,0.36902517080307007,0.7330518960952759,0.5713627338409424,0.3970465362071991,0.7330518960952759,0.5522580742835999,0.4240807592868805,0.7330518960952759,0.531780481338501,0.4500606656074524,0.7330518960952759,0.5099807381629944,0.47492164373397827,0.7330518960952759,0.48691311478614807,0.49860191345214844,0.7330518960952759,0.46263495087623596,0.52104252576828,0.7330518960952759,0.43720659613609314,0.5421878099441528,0.7330518960952759,0.4106912910938263,0.5619850754737854,0.7330518960952759,0.383154958486557,0.5803852081298828,0.7330518960952759,0.3546660244464874,0.5973423719406128,0.7330518960952759,0.3252953290939331,0.6128144860267639,0.7330518960952759,0.2951158881187439,0.6267629861831665,0.7330518960952759,0.2642027735710144,0.6391533017158508,0.7330518960952759,0.23263277113437653,0.6499545574188232,0.7330518960952759,0.20048443973064423,0.6591399312019348,0.7330518960952759,0.16783764958381653,0.6666865944862366,0.7330518960952759,0.1347736120223999,0.6725757718086243,0.7330518960952759,0.10137448459863663,0.6767928004264832,0.7330518960952759,0.0677233338356018,0.6793272495269775,0.7330518960952759,0.0339038111269474,0.6801727414131165,0.7330518960952759,1.6659426801240838e-16,0.6427876353263855,0.7660444378852844,0.0,0.6419885754585266,0.7660444378852844,-0.0320403166115284,0.6395934820175171,0.7660444378852844,-0.06400097906589508,0.6356081962585449,0.7660444378852844,-0.09580252319574356,0.6300427317619324,0.7660444378852844,-0.12736588716506958,0.6229108572006226,0.7660444378852844,-0.15861259400844574,0.6142303347587585,0.7660444378852844,-0.1894649714231491,0.6040228009223938,0.7660444378852844,-0.21984630823135376,0.5923134684562683,0.7660444378852844,-0.24968107044696808,0.5791316032409668,0.7660444378852844,-0.2788950800895691,0.564509928226471,0.7660444378852844,-0.3074157238006592,0.5484848022460938,0.7660444378852844,-0.3351720869541168,0.5310960412025452,0.7660444378852844,-0.3620951473712921,0.5123869180679321,0.7660444378852844,-0.3881179988384247,0.49240386486053467,0.7660444378852844,-0.41317591071128845,0.47119665145874023,0.7660444378852844,-0.43720659613609314,0.4488179683685303,0.7660444378852844,-0.4601503312587738,0.4253234565258026,0.7660444378852844,-0.4819500744342804,0.40077152848243713,0.7660444378852844,-0.5025516152381897,0.37522318959236145,0.7660444378852844,-0.5219036936759949,0.3487420082092285,0.7660444378852844,-0.5399582386016846,0.32139381766319275,0.7660444378852844,-0.5566704273223877,0.2932465672492981,0.7660444378852844,-0.5719985961914062,0.26437026262283325,0.7660444378852844,-0.5859046578407288,0.23483668267726898,0.7660444378852844,-0.5983541011810303,0.20471927523612976,0.7660444378852844,-0.6093159317970276,0.17409290373325348,0.7660444378852844,-0.6187629103660583,0.1430336982011795,0.7660444378852844,-0.626671552658081,0.11161889880895615,0.7660444378852844,-0.6330222487449646,0.07992659509181976,0.7660444378852844,-0.637799084186554,0.048035576939582825,0.7660444378852844,-0.6409902572631836,0.016025139018893242,0.7660444378852844,-0.6425878405570984,-0.016025139018893242,0.7660444378852844,-0.6425878405570984,-0.048035576939582825,0.7660444378852844,-0.6409902572631836,-0.07992659509181976,0.7660444378852844,-0.637799084186554,-0.11161889880895615,0.7660444378852844,-0.6330222487449646,-0.1430336982011795,0.7660444378852844,-0.626671552658081,-0.17409290373325348,0.7660444378852844,-0.6187629103660583,-0.20471927523612976,0.7660444378852844,-0.6093159317970276,-0.23483668267726898,0.7660444378852844,-0.5983541011810303,-0.26437026262283325,0.7660444378852844,-0.5859046578407288,-0.2932465672492981,0.7660444378852844,-0.5719985961914062,-0.32139381766319275,0.7660444378852844,-0.5566704273223877,-0.3487420082092285,0.7660444378852844,-0.5399582386016846,-0.37522318959236145,0.7660444378852844,-0.5219036936759949,-0.40077152848243713,0.7660444378852844,-0.5025516152381897,-0.4253234565258026,0.7660444378852844,-0.4819500744342804,-0.4488179683685303,0.7660444378852844,-0.4601503312587738,-0.47119665145874023,0.7660444378852844,-0.43720659613609314,-0.49240386486053467,0.7660444378852844,-0.41317591071128845,-0.5123869180679321,0.7660444378852844,-0.3881179988384247,-0.5310960412025452,0.7660444378852844,-0.3620951473712921,-0.5484848022460938,0.7660444378852844,-0.3351720869541168,-0.564509928226471,0.7660444378852844,-0.3074157238006592,-0.5791316032409668,0.7660444378852844,-0.2788950800895691,-0.5923134684562683,0.7660444378852844,-0.24968107044696808,-0.6040228009223938,0.7660444378852844,-0.21984630823135376,-0.6142303347587585,0.7660444378852844,-0.1894649714231491,-0.6229108572006226,0.7660444378852844,-0.15861259400844574,-0.6300427317619324,0.7660444378852844,-0.12736588716506958,-0.6356081962585449,0.7660444378852844,-0.09580252319574356,-0.6395934820175171,0.7660444378852844,-0.06400097906589508,-0.6419885754585266,0.7660444378852844,-0.0320403166115284,-0.6427876353263855,0.7660444378852844,-7.871878195995179e-17,-0.6419885754585266,0.7660444378852844,0.0320403166115284,-0.6395934820175171,0.7660444378852844,0.06400097906589508,-0.6356081962585449,0.7660444378852844,0.09580252319574356,-0.6300427317619324,0.7660444378852844,0.12736588716506958,-0.6229108572006226,0.7660444378852844,0.15861259400844574,-0.6142303347587585,0.7660444378852844,0.1894649714231491,-0.6040228009223938,0.7660444378852844,0.21984630823135376,-0.5923134684562683,0.7660444378852844,0.24968107044696808,-0.5791316032409668,0.7660444378852844,0.2788950800895691,-0.564509928226471,0.7660444378852844,0.3074157238006592,-0.5484848022460938,0.7660444378852844,0.3351720869541168,-0.5310960412025452,0.7660444378852844,0.3620951473712921,-0.5123869180679321,0.7660444378852844,0.3881179988384247,-0.49240386486053467,0.7660444378852844,0.41317591071128845,-0.47119665145874023,0.7660444378852844,0.43720659613609314,-0.4488179683685303,0.7660444378852844,0.4601503312587738,-0.4253234565258026,0.7660444378852844,0.4819500744342804,-0.40077152848243713,0.7660444378852844,0.5025516152381897,-0.37522318959236145,0.7660444378852844,0.5219036936759949,-0.3487420082092285,0.7660444378852844,0.5399582386016846,-0.32139381766319275,0.7660444378852844,0.5566704273223877,-0.2932465672492981,0.7660444378852844,0.5719985961914062,-0.26437026262283325,0.7660444378852844,0.5859046578407288,-0.23483668267726898,0.7660444378852844,0.5983541011810303,-0.20471927523612976,0.7660444378852844,0.6093159317970276,-0.17409290373325348,0.7660444378852844,0.6187629103660583,-0.1430336982011795,0.7660444378852844,0.626671552658081,-0.11161889880895615,0.7660444378852844,0.6330222487449646,-0.07992659509181976,0.7660444378852844,0.637799084186554,-0.048035576939582825,0.7660444378852844,0.6409902572631836,-0.016025139018893242,0.7660444378852844,0.6425878405570984,0.016025139018893242,0.7660444378852844,0.6425878405570984,0.048035576939582825,0.7660444378852844,0.6409902572631836,0.07992659509181976,0.7660444378852844,0.637799084186554,0.11161889880895615,0.7660444378852844,0.6330222487449646,0.1430336982011795,0.7660444378852844,0.626671552658081,0.17409290373325348,0.7660444378852844,0.6187629103660583,0.20471927523612976,0.7660444378852844,0.6093159317970276,0.23483668267726898,0.7660444378852844,0.5983541011810303,0.26437026262283325,0.7660444378852844,0.5859046578407288,0.2932465672492981,0.7660444378852844,0.5719985961914062,0.32139381766319275,0.7660444378852844,0.5566704273223877,0.3487420082092285,0.7660444378852844,0.5399582386016846,0.37522318959236145,0.7660444378852844,0.5219036936759949,0.40077152848243713,0.7660444378852844,0.5025516152381897,0.4253234565258026,0.7660444378852844,0.4819500744342804,0.4488179683685303,0.7660444378852844,0.4601503312587738,0.47119665145874023,0.7660444378852844,0.43720659613609314,0.49240386486053467,0.7660444378852844,0.41317591071128845,0.5123869180679321,0.7660444378852844,0.3881179988384247,0.5310960412025452,0.7660444378852844,0.3620951473712921,0.5484848022460938,0.7660444378852844,0.3351720869541168,0.564509928226471,0.7660444378852844,0.3074157238006592,0.5791316032409668,0.7660444378852844,0.2788950800895691,0.5923134684562683,0.7660444378852844,0.24968107044696808,0.6040228009223938,0.7660444378852844,0.21984630823135376,0.6142303347587585,0.7660444378852844,0.1894649714231491,0.6229108572006226,0.7660444378852844,0.15861259400844574,0.6300427317619324,0.7660444378852844,0.12736588716506958,0.6356081962585449,0.7660444378852844,0.09580252319574356,0.6395934820175171,0.7660444378852844,0.06400097906589508,0.6419885754585266,0.7660444378852844,0.0320403166115284,0.6427876353263855,0.7660444378852844,1.5743756391990358e-16,0.6038044095039368,0.7971324920654297,0.0,0.6030538082122803,0.7971324920654297,-0.03009716607630253,0.6008039712905884,0.7971324920654297,-0.06011950597167015,0.5970604419708252,0.7971324920654297,-0.08999237418174744,0.5918324589729309,0.7971324920654297,-0.11964151263237,0.5851331353187561,0.7971324920654297,-0.14899320900440216,0.5769791007041931,0.7971324920654297,-0.17797447741031647,0.5673905611038208,0.7971324920654297,-0.20651327073574066,0.5563914179801941,0.7971324920654297,-0.23453864455223083,0.5440089702606201,0.7971324920654297,-0.26198092103004456,0.5302740335464478,0.7971324920654297,-0.2887718677520752,0.5152208209037781,0.7971324920654297,-0.31484487652778625,0.4988866150379181,0.7971324920654297,-0.3401351273059845,0.4813121259212494,0.7971324920654297,-0.3645797669887543,0.46254101395606995,0.7971324920654297,-0.3881179988384247,0.4426199495792389,0.7971324920654297,-0.4106912910938263,0.4215984642505646,0.7971324920654297,-0.43224355578422546,0.399528831243515,0.7971324920654297,-0.4527212083339691,0.37646588683128357,0.7971324920654297,-0.4720732867717743,0.35246700048446655,0.7971324920654297,-0.490251749753952,0.32759183645248413,0.7971324920654297,-0.5072113275527954,0.3019022047519684,0.7971324920654297,-0.522909939289093,0.27546200156211853,0.7971324920654297,-0.5373085141181946,0.24833697080612183,0.7971324920654297,-0.5503712296485901,0.22059452533721924,0.7971324920654297,-0.5620656609535217,0.1923036426305771,0.7971324920654297,-0.5723627209663391,0.16353467106819153,0.7971324920654297,-0.5812367796897888,0.13435912132263184,0.7971324920654297,-0.588665783405304,0.10484953224658966,0.7971324920654297,-0.5946312546730042,0.07507927715778351,0.7971324920654297,-0.5991184115409851,0.045122358947992325,0.7971324920654297,-0.602116048336029,0.015053262002766132,0.7971324920654297,-0.6036167144775391,-0.015053262002766132,0.7971324920654297,-0.6036167144775391,-0.045122358947992325,0.7971324920654297,-0.602116048336029,-0.07507927715778351,0.7971324920654297,-0.5991184115409851,-0.10484953224658966,0.7971324920654297,-0.5946312546730042,-0.13435912132263184,0.7971324920654297,-0.588665783405304,-0.16353467106819153,0.7971324920654297,-0.5812367796897888,-0.1923036426305771,0.7971324920654297,-0.5723627209663391,-0.22059452533721924,0.7971324920654297,-0.5620656609535217,-0.24833697080612183,0.7971324920654297,-0.5503712296485901,-0.27546200156211853,0.7971324920654297,-0.5373085141181946,-0.3019022047519684,0.7971324920654297,-0.522909939289093,-0.32759183645248413,0.7971324920654297,-0.5072113275527954,-0.35246700048446655,0.7971324920654297,-0.490251749753952,-0.37646588683128357,0.7971324920654297,-0.4720732867717743,-0.399528831243515,0.7971324920654297,-0.4527212083339691,-0.4215984642505646,0.7971324920654297,-0.43224355578422546,-0.4426199495792389,0.7971324920654297,-0.4106912910938263,-0.46254101395606995,0.7971324920654297,-0.3881179988384247,-0.4813121259212494,0.7971324920654297,-0.3645797669887543,-0.4988866150379181,0.7971324920654297,-0.3401351273059845,-0.5152208209037781,0.7971324920654297,-0.31484487652778625,-0.5302740335464478,0.7971324920654297,-0.2887718677520752,-0.5440089702606201,0.7971324920654297,-0.26198092103004456,-0.5563914179801941,0.7971324920654297,-0.23453864455223083,-0.5673905611038208,0.7971324920654297,-0.20651327073574066,-0.5769791007041931,0.7971324920654297,-0.17797447741031647,-0.5851331353187561,0.7971324920654297,-0.14899320900440216,-0.5918324589729309,0.7971324920654297,-0.11964151263237,-0.5970604419708252,0.7971324920654297,-0.08999237418174744,-0.6008039712905884,0.7971324920654297,-0.06011950597167015,-0.6030538082122803,0.7971324920654297,-0.03009716607630253,-0.6038044095039368,0.7971324920654297,-7.394471236332444e-17,-0.6030538082122803,0.7971324920654297,0.03009716607630253,-0.6008039712905884,0.7971324920654297,0.06011950597167015,-0.5970604419708252,0.7971324920654297,0.08999237418174744,-0.5918324589729309,0.7971324920654297,0.11964151263237,-0.5851331353187561,0.7971324920654297,0.14899320900440216,-0.5769791007041931,0.7971324920654297,0.17797447741031647,-0.5673905611038208,0.7971324920654297,0.20651327073574066,-0.5563914179801941,0.7971324920654297,0.23453864455223083,-0.5440089702606201,0.7971324920654297,0.26198092103004456,-0.5302740335464478,0.7971324920654297,0.2887718677520752,-0.5152208209037781,0.7971324920654297,0.31484487652778625,-0.4988866150379181,0.7971324920654297,0.3401351273059845,-0.4813121259212494,0.7971324920654297,0.3645797669887543,-0.46254101395606995,0.7971324920654297,0.3881179988384247,-0.4426199495792389,0.7971324920654297,0.4106912910938263,-0.4215984642505646,0.7971324920654297,0.43224355578422546,-0.399528831243515,0.7971324920654297,0.4527212083339691,-0.37646588683128357,0.7971324920654297,0.4720732867717743,-0.35246700048446655,0.7971324920654297,0.490251749753952,-0.32759183645248413,0.7971324920654297,0.5072113275527954,-0.3019022047519684,0.7971324920654297,0.522909939289093,-0.27546200156211853,0.7971324920654297,0.5373085141181946,-0.24833697080612183,0.7971324920654297,0.5503712296485901,-0.22059452533721924,0.7971324920654297,0.5620656609535217,-0.1923036426305771,0.7971324920654297,0.5723627209663391,-0.16353467106819153,0.7971324920654297,0.5812367796897888,-0.13435912132263184,0.7971324920654297,0.588665783405304,-0.10484953224658966,0.7971324920654297,0.5946312546730042,-0.07507927715778351,0.7971324920654297,0.5991184115409851,-0.045122358947992325,0.7971324920654297,0.602116048336029,-0.015053262002766132,0.7971324920654297,0.6036167144775391,0.015053262002766132,0.7971324920654297,0.6036167144775391,0.045122358947992325,0.7971324920654297,0.602116048336029,0.07507927715778351,0.7971324920654297,0.5991184115409851,0.10484953224658966,0.7971324920654297,0.5946312546730042,0.13435912132263184,0.7971324920654297,0.588665783405304,0.16353467106819153,0.7971324920654297,0.5812367796897888,0.1923036426305771,0.7971324920654297,0.5723627209663391,0.22059452533721924,0.7971324920654297,0.5620656609535217,0.24833697080612183,0.7971324920654297,0.5503712296485901,0.27546200156211853,0.7971324920654297,0.5373085141181946,0.3019022047519684,0.7971324920654297,0.522909939289093,0.32759183645248413,0.7971324920654297,0.5072113275527954,0.35246700048446655,0.7971324920654297,0.490251749753952,0.37646588683128357,0.7971324920654297,0.4720732867717743,0.399528831243515,0.7971324920654297,0.4527212083339691,0.4215984642505646,0.7971324920654297,0.43224355578422546,0.4426199495792389,0.7971324920654297,0.4106912910938263,0.46254101395606995,0.7971324920654297,0.3881179988384247,0.4813121259212494,0.7971324920654297,0.3645797669887543,0.4988866150379181,0.7971324920654297,0.3401351273059845,0.5152208209037781,0.7971324920654297,0.31484487652778625,0.5302740335464478,0.7971324920654297,0.2887718677520752,0.5440089702606201,0.7971324920654297,0.26198092103004456,0.5563914179801941,0.7971324920654297,0.23453864455223083,0.5673905611038208,0.7971324920654297,0.20651327073574066,0.5769791007041931,0.7971324920654297,0.17797447741031647,0.5851331353187561,0.7971324920654297,0.14899320900440216,0.5918324589729309,0.7971324920654297,0.11964151263237,0.5970604419708252,0.7971324920654297,0.08999237418174744,0.6008039712905884,0.7971324920654297,0.06011950597167015,0.6030538082122803,0.7971324920654297,0.03009716607630253,0.6038044095039368,0.7971324920654297,1.4788942472664888e-16,0.5633200407028198,0.826238751411438,0.0,0.5626198053359985,0.826238751411438,-0.028079187497496605,0.5605207681655884,0.826238751411438,-0.05608856678009033,0.5570282340049744,0.826238751411438,-0.08395849913358688,0.5521508455276489,0.826238751411438,-0.11161969602108002,0.5459007024765015,0.826238751411438,-0.13900339603424072,0.5382933020591736,0.826238751411438,-0.1660415083169937,0.5293477177619934,0.826238751411438,-0.19266681373119354,0.5190860629081726,0.826238751411438,-0.21881310641765594,0.507533848285675,0.826238751411438,-0.24441541731357574,0.49471983313560486,0.826238751411438,-0.26941007375717163,0.4806758761405945,0.826238751411438,-0.29373490810394287,0.4654368758201599,0.826238751411438,-0.3173294961452484,0.4490407407283783,0.826238751411438,-0.3401351273059845,0.4315282106399536,0.826238751411438,-0.3620951473712921,0.4129428267478943,0.826238751411438,-0.383154958486557,0.39333081245422363,0.826238751411438,-0.4032621681690216,0.37274089455604553,0.826238751411438,-0.42236679792404175,0.35122430324554443,0.826238751411438,-0.44042134284973145,0.32883453369140625,0.826238751411438,-0.4573809504508972,0.3056271970272064,0.826238751411438,-0.4732034504413605,0.2816600203514099,0.826238751411438,-0.48784947395324707,0.2569926083087921,0.826238751411438,-0.5012826323509216,0.2316862791776657,0.826238751411438,-0.5134695172309875,0.20580393075942993,0.826238751411438,-0.5243798494338989,0.179409921169281,0.826238751411438,-0.533986508846283,0.15256987512111664,0.826238751411438,-0.5422655344009399,0.12535050511360168,0.826238751411438,-0.5491964221000671,0.0978194996714592,0.826238751411438,-0.5547619462013245,0.07004529982805252,0.826238751411438,-0.5589482188224792,0.04209696128964424,0.826238751411438,-0.5617449283599854,0.014043958857655525,0.826238751411438,-0.5631449818611145,-0.014043958857655525,0.826238751411438,-0.5631449818611145,-0.04209696128964424,0.826238751411438,-0.5617449283599854,-0.07004529982805252,0.826238751411438,-0.5589482188224792,-0.0978194996714592,0.826238751411438,-0.5547619462013245,-0.12535050511360168,0.826238751411438,-0.5491964221000671,-0.15256987512111664,0.826238751411438,-0.5422655344009399,-0.179409921169281,0.826238751411438,-0.533986508846283,-0.20580393075942993,0.826238751411438,-0.5243798494338989,-0.2316862791776657,0.826238751411438,-0.5134695172309875,-0.2569926083087921,0.826238751411438,-0.5012826323509216,-0.2816600203514099,0.826238751411438,-0.48784947395324707,-0.3056271970272064,0.826238751411438,-0.4732034504413605,-0.32883453369140625,0.826238751411438,-0.4573809504508972,-0.35122430324554443,0.826238751411438,-0.44042134284973145,-0.37274089455604553,0.826238751411438,-0.42236679792404175,-0.39333081245422363,0.826238751411438,-0.4032621681690216,-0.4129428267478943,0.826238751411438,-0.383154958486557,-0.4315282106399536,0.826238751411438,-0.3620951473712921,-0.4490407407283783,0.826238751411438,-0.3401351273059845,-0.4654368758201599,0.826238751411438,-0.3173294961452484,-0.4806758761405945,0.826238751411438,-0.29373490810394287,-0.49471983313560486,0.826238751411438,-0.26941007375717163,-0.507533848285675,0.826238751411438,-0.24441541731357574,-0.5190860629081726,0.826238751411438,-0.21881310641765594,-0.5293477177619934,0.826238751411438,-0.19266681373119354,-0.5382933020591736,0.826238751411438,-0.1660415083169937,-0.5459007024765015,0.826238751411438,-0.13900339603424072,-0.5521508455276489,0.826238751411438,-0.11161969602108002,-0.5570282340049744,0.826238751411438,-0.08395849913358688,-0.5605207681655884,0.826238751411438,-0.05608856678009033,-0.5626198053359985,0.826238751411438,-0.028079187497496605,-0.5633200407028198,0.826238751411438,-6.89868101473633e-17,-0.5626198053359985,0.826238751411438,0.028079187497496605,-0.5605207681655884,0.826238751411438,0.05608856678009033,-0.5570282340049744,0.826238751411438,0.08395849913358688,-0.5521508455276489,0.826238751411438,0.11161969602108002,-0.5459007024765015,0.826238751411438,0.13900339603424072,-0.5382933020591736,0.826238751411438,0.1660415083169937,-0.5293477177619934,0.826238751411438,0.19266681373119354,-0.5190860629081726,0.826238751411438,0.21881310641765594,-0.507533848285675,0.826238751411438,0.24441541731357574,-0.49471983313560486,0.826238751411438,0.26941007375717163,-0.4806758761405945,0.826238751411438,0.29373490810394287,-0.4654368758201599,0.826238751411438,0.3173294961452484,-0.4490407407283783,0.826238751411438,0.3401351273059845,-0.4315282106399536,0.826238751411438,0.3620951473712921,-0.4129428267478943,0.826238751411438,0.383154958486557,-0.39333081245422363,0.826238751411438,0.4032621681690216,-0.37274089455604553,0.826238751411438,0.42236679792404175,-0.35122430324554443,0.826238751411438,0.44042134284973145,-0.32883453369140625,0.826238751411438,0.4573809504508972,-0.3056271970272064,0.826238751411438,0.4732034504413605,-0.2816600203514099,0.826238751411438,0.48784947395324707,-0.2569926083087921,0.826238751411438,0.5012826323509216,-0.2316862791776657,0.826238751411438,0.5134695172309875,-0.20580393075942993,0.826238751411438,0.5243798494338989,-0.179409921169281,0.826238751411438,0.533986508846283,-0.15256987512111664,0.826238751411438,0.5422655344009399,-0.12535050511360168,0.826238751411438,0.5491964221000671,-0.0978194996714592,0.826238751411438,0.5547619462013245,-0.07004529982805252,0.826238751411438,0.5589482188224792,-0.04209696128964424,0.826238751411438,0.5617449283599854,-0.014043958857655525,0.826238751411438,0.5631449818611145,0.014043958857655525,0.826238751411438,0.5631449818611145,0.04209696128964424,0.826238751411438,0.5617449283599854,0.07004529982805252,0.826238751411438,0.5589482188224792,0.0978194996714592,0.826238751411438,0.5547619462013245,0.12535050511360168,0.826238751411438,0.5491964221000671,0.15256987512111664,0.826238751411438,0.5422655344009399,0.179409921169281,0.826238751411438,0.533986508846283,0.20580393075942993,0.826238751411438,0.5243798494338989,0.2316862791776657,0.826238751411438,0.5134695172309875,0.2569926083087921,0.826238751411438,0.5012826323509216,0.2816600203514099,0.826238751411438,0.48784947395324707,0.3056271970272064,0.826238751411438,0.4732034504413605,0.32883453369140625,0.826238751411438,0.4573809504508972,0.35122430324554443,0.826238751411438,0.44042134284973145,0.37274089455604553,0.826238751411438,0.42236679792404175,0.39333081245422363,0.826238751411438,0.4032621681690216,0.4129428267478943,0.826238751411438,0.383154958486557,0.4315282106399536,0.826238751411438,0.3620951473712921,0.4490407407283783,0.826238751411438,0.3401351273059845,0.4654368758201599,0.826238751411438,0.3173294961452484,0.4806758761405945,0.826238751411438,0.29373490810394287,0.49471983313560486,0.826238751411438,0.26941007375717163,0.507533848285675,0.826238751411438,0.24441541731357574,0.5190860629081726,0.826238751411438,0.21881310641765594,0.5293477177619934,0.826238751411438,0.19266681373119354,0.5382933020591736,0.826238751411438,0.1660415083169937,0.5459007024765015,0.826238751411438,0.13900339603424072,0.5521508455276489,0.826238751411438,0.11161969602108002,0.5570282340049744,0.826238751411438,0.08395849913358688,0.5605207681655884,0.826238751411438,0.05608856678009033,0.5626198053359985,0.826238751411438,0.028079187497496605,0.5633200407028198,0.826238751411438,1.379736202947266e-16,0.5214352011680603,0.853290855884552,0.0,0.5207870006561279,0.853290855884552,-0.025991398841142654,0.5188440680503845,0.853290855884552,-0.05191817879676819,0.5156112313270569,0.853290855884552,-0.07771588116884232,0.5110964179039001,0.853290855884552,-0.10332037508487701,0.5053110122680664,0.853290855884552,-0.12866799533367157,0.49826928973197937,0.853290855884552,-0.1536957174539566,0.4899888038635254,0.853290855884552,-0.178341343998909,0.48049014806747437,0.853290855884552,-0.20254357159137726,0.46979689598083496,0.853290855884552,-0.22624225914478302,0.457935631275177,0.853290855884552,-0.24937845766544342,0.4449359178543091,0.853290855884552,-0.2718946635723114,0.4308299720287323,0.853290855884552,-0.29373490810394287,0.41565296053886414,0.853290855884552,-0.31484487652778625,0.39944255352020264,0.853290855884552,-0.3351720869541168,0.38223904371261597,0.853290855884552,-0.3546660244464874,0.36408525705337524,0.853290855884552,-0.3732781708240509,0.34502628445625305,0.853290855884552,-0.39096230268478394,0.3251095414161682,0.853290855884552,-0.4076744616031647,0.3043844997882843,0.853290855884552,-0.4233730733394623,0.28290271759033203,0.853290855884552,-0.4380190968513489,0.26071760058403015,0.853290855884552,-0.4515761435031891,0.2378842979669571,0.853290855884552,-0.46401047706604004,0.21445956826210022,0.853290855884552,-0.47529125213623047,0.19050167500972748,0.853290855884552,-0.4853903353214264,0.1660701483488083,0.853290855884552,-0.49428269267082214,0.14122575521469116,0.853290855884552,-0.5019461512565613,0.1160302460193634,0.853290855884552,-0.5083617568016052,0.09054627269506454,0.853290855884552,-0.513513445854187,0.06483718752861023,0.853290855884552,-0.5173884630203247,0.03896690160036087,0.853290855884552,-0.5199771523475647,0.012999740429222584,0.853290855884552,-0.521273136138916,-0.012999740429222584,0.853290855884552,-0.521273136138916,-0.03896690160036087,0.853290855884552,-0.5199771523475647,-0.06483718752861023,0.853290855884552,-0.5173884630203247,-0.09054627269506454,0.853290855884552,-0.513513445854187,-0.1160302460193634,0.853290855884552,-0.5083617568016052,-0.14122575521469116,0.853290855884552,-0.5019461512565613,-0.1660701483488083,0.853290855884552,-0.49428269267082214,-0.19050167500972748,0.853290855884552,-0.4853903353214264,-0.21445956826210022,0.853290855884552,-0.47529125213623047,-0.2378842979669571,0.853290855884552,-0.46401047706604004,-0.26071760058403015,0.853290855884552,-0.4515761435031891,-0.28290271759033203,0.853290855884552,-0.4380190968513489,-0.3043844997882843,0.853290855884552,-0.4233730733394623,-0.3251095414161682,0.853290855884552,-0.4076744616031647,-0.34502628445625305,0.853290855884552,-0.39096230268478394,-0.36408525705337524,0.853290855884552,-0.3732781708240509,-0.38223904371261597,0.853290855884552,-0.3546660244464874,-0.39944255352020264,0.853290855884552,-0.3351720869541168,-0.41565296053886414,0.853290855884552,-0.31484487652778625,-0.4308299720287323,0.853290855884552,-0.29373490810394287,-0.4449359178543091,0.853290855884552,-0.2718946635723114,-0.457935631275177,0.853290855884552,-0.24937845766544342,-0.46979689598083496,0.853290855884552,-0.22624225914478302,-0.48049014806747437,0.853290855884552,-0.20254357159137726,-0.4899888038635254,0.853290855884552,-0.178341343998909,-0.49826928973197937,0.853290855884552,-0.1536957174539566,-0.5053110122680664,0.853290855884552,-0.12866799533367157,-0.5110964179039001,0.853290855884552,-0.10332037508487701,-0.5156112313270569,0.853290855884552,-0.07771588116884232,-0.5188440680503845,0.853290855884552,-0.05191817879676819,-0.5207870006561279,0.853290855884552,-0.025991398841142654,-0.5214352011680603,0.853290855884552,-6.385739699447298e-17,-0.5207870006561279,0.853290855884552,0.025991398841142654,-0.5188440680503845,0.853290855884552,0.05191817879676819,-0.5156112313270569,0.853290855884552,0.07771588116884232,-0.5110964179039001,0.853290855884552,0.10332037508487701,-0.5053110122680664,0.853290855884552,0.12866799533367157,-0.49826928973197937,0.853290855884552,0.1536957174539566,-0.4899888038635254,0.853290855884552,0.178341343998909,-0.48049014806747437,0.853290855884552,0.20254357159137726,-0.46979689598083496,0.853290855884552,0.22624225914478302,-0.457935631275177,0.853290855884552,0.24937845766544342,-0.4449359178543091,0.853290855884552,0.2718946635723114,-0.4308299720287323,0.853290855884552,0.29373490810394287,-0.41565296053886414,0.853290855884552,0.31484487652778625,-0.39944255352020264,0.853290855884552,0.3351720869541168,-0.38223904371261597,0.853290855884552,0.3546660244464874,-0.36408525705337524,0.853290855884552,0.3732781708240509,-0.34502628445625305,0.853290855884552,0.39096230268478394,-0.3251095414161682,0.853290855884552,0.4076744616031647,-0.3043844997882843,0.853290855884552,0.4233730733394623,-0.28290271759033203,0.853290855884552,0.4380190968513489,-0.26071760058403015,0.853290855884552,0.4515761435031891,-0.2378842979669571,0.853290855884552,0.46401047706604004,-0.21445956826210022,0.853290855884552,0.47529125213623047,-0.19050167500972748,0.853290855884552,0.4853903353214264,-0.1660701483488083,0.853290855884552,0.49428269267082214,-0.14122575521469116,0.853290855884552,0.5019461512565613,-0.1160302460193634,0.853290855884552,0.5083617568016052,-0.09054627269506454,0.853290855884552,0.513513445854187,-0.06483718752861023,0.853290855884552,0.5173884630203247,-0.03896690160036087,0.853290855884552,0.5199771523475647,-0.012999740429222584,0.853290855884552,0.521273136138916,0.012999740429222584,0.853290855884552,0.521273136138916,0.03896690160036087,0.853290855884552,0.5199771523475647,0.06483718752861023,0.853290855884552,0.5173884630203247,0.09054627269506454,0.853290855884552,0.513513445854187,0.1160302460193634,0.853290855884552,0.5083617568016052,0.14122575521469116,0.853290855884552,0.5019461512565613,0.1660701483488083,0.853290855884552,0.49428269267082214,0.19050167500972748,0.853290855884552,0.4853903353214264,0.21445956826210022,0.853290855884552,0.47529125213623047,0.2378842979669571,0.853290855884552,0.46401047706604004,0.26071760058403015,0.853290855884552,0.4515761435031891,0.28290271759033203,0.853290855884552,0.4380190968513489,0.3043844997882843,0.853290855884552,0.4233730733394623,0.3251095414161682,0.853290855884552,0.4076744616031647,0.34502628445625305,0.853290855884552,0.39096230268478394,0.36408525705337524,0.853290855884552,0.3732781708240509,0.38223904371261597,0.853290855884552,0.3546660244464874,0.39944255352020264,0.853290855884552,0.3351720869541168,0.41565296053886414,0.853290855884552,0.31484487652778625,0.4308299720287323,0.853290855884552,0.29373490810394287,0.4449359178543091,0.853290855884552,0.2718946635723114,0.457935631275177,0.853290855884552,0.24937845766544342,0.46979689598083496,0.853290855884552,0.22624225914478302,0.48049014806747437,0.853290855884552,0.20254357159137726,0.4899888038635254,0.853290855884552,0.178341343998909,0.49826928973197937,0.853290855884552,0.1536957174539566,0.5053110122680664,0.853290855884552,0.12866799533367157,0.5110964179039001,0.853290855884552,0.10332037508487701,0.5156112313270569,0.853290855884552,0.07771588116884232,0.5188440680503845,0.853290855884552,0.05191817879676819,0.5207870006561279,0.853290855884552,0.025991398841142654,0.5214352011680603,0.853290855884552,1.2771479398894595e-16,0.4782539904117584,0.8782215714454651,0.0,0.4776594638824463,0.8782215714454651,-0.023838993161916733,0.47587743401527405,0.8782215714454651,-0.047618716955184937,0.4729122817516327,0.8782215714454651,-0.07128005474805832,0.46877139806747437,0.8782215714454651,-0.09476418048143387,0.4634650647640228,0.8782215714454651,-0.11801270395517349,0.4570064842700958,0.8782215714454651,-0.14096783101558685,0.4494117200374603,0.8782215714454651,-0.1635724902153015,0.44069966673851013,0.8782215714454651,-0.18577048182487488,0.4308919310569763,0.8782215714454651,-0.20750662684440613,0.4200129508972168,0.8782215714454651,-0.22872686386108398,0.4080897569656372,0.8782215714454651,-0.24937845766544342,0.3951519727706909,0.8782215714454651,-0.26941007375717163,0.38123178482055664,0.8782215714454651,-0.2887718677520752,0.3663637936115265,0.8782215714454651,-0.3074157238006592,0.3505849838256836,0.8782215714454651,-0.3252953290939331,0.3339345455169678,0.8782215714454651,-0.34236615896224976,0.31645387411117554,0.8782215714454651,-0.35858583450317383,0.2981864809989929,0.8782215714454651,-0.3739140033721924,0.279177725315094,0.8782215714454651,-0.38831257820129395,0.25947490334510803,0.8782215714454651,-0.4017457365989685,0.2391269952058792,0.8782215714454651,-0.41418009996414185,0.21818456053733826,0.8782215714454651,-0.42558473348617554,0.19669969379901886,0.8782215714454651,-0.4359312951564789,0.17472580075263977,0.8782215714454651,-0.4451940655708313,0.15231750905513763,0.8782215714454651,-0.4533500373363495,0.12953053414821625,0.8782215714454651,-0.46037888526916504,0.10642152279615402,0.8782215714454651,-0.46626314520835876,0.08304793387651443,0.8782215714454651,-0.4709882140159607,0.059467870742082596,0.8782215714454651,-0.4745423495769501,0.035739965736866,0.8782215714454651,-0.47691667079925537,0.011923202313482761,0.8782215714454651,-0.4781053364276886,-0.011923202313482761,0.8782215714454651,-0.4781053364276886,-0.035739965736866,0.8782215714454651,-0.47691667079925537,-0.059467870742082596,0.8782215714454651,-0.4745423495769501,-0.08304793387651443,0.8782215714454651,-0.4709882140159607,-0.10642152279615402,0.8782215714454651,-0.46626314520835876,-0.12953053414821625,0.8782215714454651,-0.46037888526916504,-0.15231750905513763,0.8782215714454651,-0.4533500373363495,-0.17472580075263977,0.8782215714454651,-0.4451940655708313,-0.19669969379901886,0.8782215714454651,-0.4359312951564789,-0.21818456053733826,0.8782215714454651,-0.42558473348617554,-0.2391269952058792,0.8782215714454651,-0.41418009996414185,-0.25947490334510803,0.8782215714454651,-0.4017457365989685,-0.279177725315094,0.8782215714454651,-0.38831257820129395,-0.2981864809989929,0.8782215714454651,-0.3739140033721924,-0.31645387411117554,0.8782215714454651,-0.35858583450317383,-0.3339345455169678,0.8782215714454651,-0.34236615896224976,-0.3505849838256836,0.8782215714454651,-0.3252953290939331,-0.3663637936115265,0.8782215714454651,-0.3074157238006592,-0.38123178482055664,0.8782215714454651,-0.2887718677520752,-0.3951519727706909,0.8782215714454651,-0.26941007375717163,-0.4080897569656372,0.8782215714454651,-0.24937845766544342,-0.4200129508972168,0.8782215714454651,-0.22872686386108398,-0.4308919310569763,0.8782215714454651,-0.20750662684440613,-0.44069966673851013,0.8782215714454651,-0.18577048182487488,-0.4494117200374603,0.8782215714454651,-0.1635724902153015,-0.4570064842700958,0.8782215714454651,-0.14096783101558685,-0.4634650647640228,0.8782215714454651,-0.11801270395517349,-0.46877139806747437,0.8782215714454651,-0.09476418048143387,-0.4729122817516327,0.8782215714454651,-0.07128005474805832,-0.47587743401527405,0.8782215714454651,-0.047618716955184937,-0.4776594638824463,0.8782215714454651,-0.023838993161916733,-0.4782539904117584,0.8782215714454651,-5.856921810353167e-17,-0.4776594638824463,0.8782215714454651,0.023838993161916733,-0.47587743401527405,0.8782215714454651,0.047618716955184937,-0.4729122817516327,0.8782215714454651,0.07128005474805832,-0.46877139806747437,0.8782215714454651,0.09476418048143387,-0.4634650647640228,0.8782215714454651,0.11801270395517349,-0.4570064842700958,0.8782215714454651,0.14096783101558685,-0.4494117200374603,0.8782215714454651,0.1635724902153015,-0.44069966673851013,0.8782215714454651,0.18577048182487488,-0.4308919310569763,0.8782215714454651,0.20750662684440613,-0.4200129508972168,0.8782215714454651,0.22872686386108398,-0.4080897569656372,0.8782215714454651,0.24937845766544342,-0.3951519727706909,0.8782215714454651,0.26941007375717163,-0.38123178482055664,0.8782215714454651,0.2887718677520752,-0.3663637936115265,0.8782215714454651,0.3074157238006592,-0.3505849838256836,0.8782215714454651,0.3252953290939331,-0.3339345455169678,0.8782215714454651,0.34236615896224976,-0.31645387411117554,0.8782215714454651,0.35858583450317383,-0.2981864809989929,0.8782215714454651,0.3739140033721924,-0.279177725315094,0.8782215714454651,0.38831257820129395,-0.25947490334510803,0.8782215714454651,0.4017457365989685,-0.2391269952058792,0.8782215714454651,0.41418009996414185,-0.21818456053733826,0.8782215714454651,0.42558473348617554,-0.19669969379901886,0.8782215714454651,0.4359312951564789,-0.17472580075263977,0.8782215714454651,0.4451940655708313,-0.15231750905513763,0.8782215714454651,0.4533500373363495,-0.12953053414821625,0.8782215714454651,0.46037888526916504,-0.10642152279615402,0.8782215714454651,0.46626314520835876,-0.08304793387651443,0.8782215714454651,0.4709882140159607,-0.059467870742082596,0.8782215714454651,0.4745423495769501,-0.035739965736866,0.8782215714454651,0.47691667079925537,-0.011923202313482761,0.8782215714454651,0.4781053364276886,0.011923202313482761,0.8782215714454651,0.4781053364276886,0.035739965736866,0.8782215714454651,0.47691667079925537,0.059467870742082596,0.8782215714454651,0.4745423495769501,0.08304793387651443,0.8782215714454651,0.4709882140159607,0.10642152279615402,0.8782215714454651,0.46626314520835876,0.12953053414821625,0.8782215714454651,0.46037888526916504,0.15231750905513763,0.8782215714454651,0.4533500373363495,0.17472580075263977,0.8782215714454651,0.4451940655708313,0.19669969379901886,0.8782215714454651,0.4359312951564789,0.21818456053733826,0.8782215714454651,0.42558473348617554,0.2391269952058792,0.8782215714454651,0.41418009996414185,0.25947490334510803,0.8782215714454651,0.4017457365989685,0.279177725315094,0.8782215714454651,0.38831257820129395,0.2981864809989929,0.8782215714454651,0.3739140033721924,0.31645387411117554,0.8782215714454651,0.35858583450317383,0.3339345455169678,0.8782215714454651,0.34236615896224976,0.3505849838256836,0.8782215714454651,0.3252953290939331,0.3663637936115265,0.8782215714454651,0.3074157238006592,0.38123178482055664,0.8782215714454651,0.2887718677520752,0.3951519727706909,0.8782215714454651,0.26941007375717163,0.4080897569656372,0.8782215714454651,0.24937845766544342,0.4200129508972168,0.8782215714454651,0.22872686386108398,0.4308919310569763,0.8782215714454651,0.20750662684440613,0.44069966673851013,0.8782215714454651,0.18577048182487488,0.4494117200374603,0.8782215714454651,0.1635724902153015,0.4570064842700958,0.8782215714454651,0.14096783101558685,0.4634650647640228,0.8782215714454651,0.11801270395517349,0.46877139806747437,0.8782215714454651,0.09476418048143387,0.4729122817516327,0.8782215714454651,0.07128005474805832,0.47587743401527405,0.8782215714454651,0.047618716955184937,0.4776594638824463,0.8782215714454651,0.023838993161916733,0.4782539904117584,0.8782215714454651,1.1713843620706334e-16,0.4338837265968323,0.9009688496589661,0.0,0.43334439396858215,0.9009688496589661,-0.02162731997668743,0.43172767758369446,0.9009688496589661,-0.04320086911320686,0.4290376305580139,0.9009688496589661,-0.06466701626777649,0.425280898809433,0.9009688496589661,-0.0859723910689354,0.4204668700695038,0.9009688496589661,-0.10706402361392975,0.4146074950695038,0.9009688496589661,-0.127889484167099,0.40771734714508057,0.9009688496589661,-0.14839698374271393,0.39981353282928467,0.9009688496589661,-0.16853554546833038,0.39091575145721436,0.9009688496589661,-0.18825510144233704,0.3810460567474365,0.9009688496589661,-0.20750662684440613,0.370229035615921,0.9009688496589661,-0.22624225914478302,0.35849156975746155,0.9009688496589661,-0.24441541731357574,0.34586283564567566,0.9009688496589661,-0.26198092103004456,0.3323742151260376,0.9009688496589661,-0.2788950800895691,0.3180592954158783,0.9009688496589661,-0.2951158881187439,0.3029536008834839,0.9009688496589661,-0.31060299277305603,0.28709471225738525,0.9009688496589661,-0.3253178596496582,0.2705220878124237,0.9009688496589661,-0.3392239809036255,0.25327688455581665,0.9009688496589661,-0.352286696434021,0.23540200293064117,0.9009688496589661,-0.3644735813140869,0.21694186329841614,0.9009688496589661,-0.37575432658195496,0.19794239103794098,0.9009688496589661,-0.3861009180545807,0.1784507930278778,0.9009688496589661,-0.39548757672309875,0.15851552784442902,0.9009688496589661,-0.403890997171402,0.13818617165088654,0.9009688496589661,-0.4112902581691742,0.1175132766366005,0.9009688496589661,-0.4176670014858246,0.09654821455478668,0.9009688496589661,-0.4230053722858429,0.07534311711788177,0.9009688496589661,-0.4272920787334442,0.0539507120847702,0.9009688496589661,-0.43051645159721375,0.03242417424917221,0.9009688496589661,-0.4326705038547516,0.010817022062838078,0.9009688496589661,-0.43374887108802795,-0.010817022062838078,0.9009688496589661,-0.43374887108802795,-0.03242417424917221,0.9009688496589661,-0.4326705038547516,-0.0539507120847702,0.9009688496589661,-0.43051645159721375,-0.07534311711788177,0.9009688496589661,-0.4272920787334442,-0.09654821455478668,0.9009688496589661,-0.4230053722858429,-0.1175132766366005,0.9009688496589661,-0.4176670014858246,-0.13818617165088654,0.9009688496589661,-0.4112902581691742,-0.15851552784442902,0.9009688496589661,-0.403890997171402,-0.1784507930278778,0.9009688496589661,-0.39548757672309875,-0.19794239103794098,0.9009688496589661,-0.3861009180545807,-0.21694186329841614,0.9009688496589661,-0.37575432658195496,-0.23540200293064117,0.9009688496589661,-0.3644735813140869,-0.25327688455581665,0.9009688496589661,-0.352286696434021,-0.2705220878124237,0.9009688496589661,-0.3392239809036255,-0.28709471225738525,0.9009688496589661,-0.3253178596496582,-0.3029536008834839,0.9009688496589661,-0.31060299277305603,-0.3180592954158783,0.9009688496589661,-0.2951158881187439,-0.3323742151260376,0.9009688496589661,-0.2788950800895691,-0.34586283564567566,0.9009688496589661,-0.26198092103004456,-0.35849156975746155,0.9009688496589661,-0.24441541731357574,-0.370229035615921,0.9009688496589661,-0.22624225914478302,-0.3810460567474365,0.9009688496589661,-0.20750662684440613,-0.39091575145721436,0.9009688496589661,-0.18825510144233704,-0.39981353282928467,0.9009688496589661,-0.16853554546833038,-0.40771734714508057,0.9009688496589661,-0.14839698374271393,-0.4146074950695038,0.9009688496589661,-0.127889484167099,-0.4204668700695038,0.9009688496589661,-0.10706402361392975,-0.425280898809433,0.9009688496589661,-0.0859723910689354,-0.4290376305580139,0.9009688496589661,-0.06466701626777649,-0.43172767758369446,0.9009688496589661,-0.04320086911320686,-0.43334439396858215,0.9009688496589661,-0.02162731997668743,-0.4338837265968323,0.9009688496589661,-5.313543226372388e-17,-0.43334439396858215,0.9009688496589661,0.02162731997668743,-0.43172767758369446,0.9009688496589661,0.04320086911320686,-0.4290376305580139,0.9009688496589661,0.06466701626777649,-0.425280898809433,0.9009688496589661,0.0859723910689354,-0.4204668700695038,0.9009688496589661,0.10706402361392975,-0.4146074950695038,0.9009688496589661,0.127889484167099,-0.40771734714508057,0.9009688496589661,0.14839698374271393,-0.39981353282928467,0.9009688496589661,0.16853554546833038,-0.39091575145721436,0.9009688496589661,0.18825510144233704,-0.3810460567474365,0.9009688496589661,0.20750662684440613,-0.370229035615921,0.9009688496589661,0.22624225914478302,-0.35849156975746155,0.9009688496589661,0.24441541731357574,-0.34586283564567566,0.9009688496589661,0.26198092103004456,-0.3323742151260376,0.9009688496589661,0.2788950800895691,-0.3180592954158783,0.9009688496589661,0.2951158881187439,-0.3029536008834839,0.9009688496589661,0.31060299277305603,-0.28709471225738525,0.9009688496589661,0.3253178596496582,-0.2705220878124237,0.9009688496589661,0.3392239809036255,-0.25327688455581665,0.9009688496589661,0.352286696434021,-0.23540200293064117,0.9009688496589661,0.3644735813140869,-0.21694186329841614,0.9009688496589661,0.37575432658195496,-0.19794239103794098,0.9009688496589661,0.3861009180545807,-0.1784507930278778,0.9009688496589661,0.39548757672309875,-0.15851552784442902,0.9009688496589661,0.403890997171402,-0.13818617165088654,0.9009688496589661,0.4112902581691742,-0.1175132766366005,0.9009688496589661,0.4176670014858246,-0.09654821455478668,0.9009688496589661,0.4230053722858429,-0.07534311711788177,0.9009688496589661,0.4272920787334442,-0.0539507120847702,0.9009688496589661,0.43051645159721375,-0.03242417424917221,0.9009688496589661,0.4326705038547516,-0.010817022062838078,0.9009688496589661,0.43374887108802795,0.010817022062838078,0.9009688496589661,0.43374887108802795,0.03242417424917221,0.9009688496589661,0.4326705038547516,0.0539507120847702,0.9009688496589661,0.43051645159721375,0.07534311711788177,0.9009688496589661,0.4272920787334442,0.09654821455478668,0.9009688496589661,0.4230053722858429,0.1175132766366005,0.9009688496589661,0.4176670014858246,0.13818617165088654,0.9009688496589661,0.4112902581691742,0.15851552784442902,0.9009688496589661,0.403890997171402,0.1784507930278778,0.9009688496589661,0.39548757672309875,0.19794239103794098,0.9009688496589661,0.3861009180545807,0.21694186329841614,0.9009688496589661,0.37575432658195496,0.23540200293064117,0.9009688496589661,0.3644735813140869,0.25327688455581665,0.9009688496589661,0.352286696434021,0.2705220878124237,0.9009688496589661,0.3392239809036255,0.28709471225738525,0.9009688496589661,0.3253178596496582,0.3029536008834839,0.9009688496589661,0.31060299277305603,0.3180592954158783,0.9009688496589661,0.2951158881187439,0.3323742151260376,0.9009688496589661,0.2788950800895691,0.34586283564567566,0.9009688496589661,0.26198092103004456,0.35849156975746155,0.9009688496589661,0.24441541731357574,0.370229035615921,0.9009688496589661,0.22624225914478302,0.3810460567474365,0.9009688496589661,0.20750662684440613,0.39091575145721436,0.9009688496589661,0.18825510144233704,0.39981353282928467,0.9009688496589661,0.16853554546833038,0.40771734714508057,0.9009688496589661,0.14839698374271393,0.4146074950695038,0.9009688496589661,0.127889484167099,0.4204668700695038,0.9009688496589661,0.10706402361392975,0.425280898809433,0.9009688496589661,0.0859723910689354,0.4290376305580139,0.9009688496589661,0.06466701626777649,0.43172767758369446,0.9009688496589661,0.04320086911320686,0.43334439396858215,0.9009688496589661,0.02162731997668743,0.4338837265968323,0.9009688496589661,1.0627086452744776e-16,0.3884347975254059,0.9214761853218079,0.0,0.38795194029808044,0.9214761853218079,-0.01936187595129013,0.386504590511322,0.9214761853218079,-0.038675617426633835,0.38409629464149475,0.9214761853218079,-0.05789320170879364,0.38073310256004333,0.9214761853218079,-0.07696685940027237,0.37642332911491394,0.9214761853218079,-0.09584915637969971,0.3711777329444885,0.9214761853218079,-0.11449316889047623,0.3650093078613281,0.9214761853218079,-0.13285252451896667,0.3579334318637848,0.9214761853218079,-0.1508815884590149,0.3499676585197449,0.9214761853218079,-0.16853554546833038,0.3411318063735962,0.9214761853218079,-0.18577048182487488,0.33144786953926086,0.9214761853218079,-0.20254357159137726,0.32093989849090576,0.9214761853218079,-0.21881310641765594,0.3096340000629425,0.9214761853218079,-0.23453864455223083,0.2975583076477051,0.9214761853218079,-0.24968107044696808,0.2847428619861603,0.9214761853218079,-0.2642027735710144,0.2712194621562958,0.9214761853218079,-0.27806758880615234,0.25702181458473206,0.9214761853218079,-0.2912411093711853,0.24218513071537018,0.9214761853218079,-0.3036905527114868,0.2267463505268097,0.9214761853218079,-0.31538498401641846,0.21074384450912476,0.9214761853218079,-0.32629528641700745,0.19421739876270294,0.9214761853218079,-0.33639439940452576,0.1772080957889557,0.9214761853218079,-0.3456571698188782,0.15975822508335114,0.9214761853218079,-0.3540605902671814,0.14191116392612457,0.9214761853218079,-0.36158376932144165,0.12371129542589188,0.9214761853218079,-0.3682079613208771,0.10520385950803757,0.9214761853218079,-0.37391674518585205,0.08643487095832825,0.9214761853218079,-0.37869593501091003,0.06745099276304245,0.9214761853218079,-0.38253360986709595,0.04829942062497139,0.9214761853218079,-0.3854202330112457,0.029027769342064857,0.9214761853218079,-0.38734865188598633,0.009683948010206223,0.9214761853218079,-0.38831406831741333,-0.009683948010206223,0.9214761853218079,-0.38831406831741333,-0.029027769342064857,0.9214761853218079,-0.38734865188598633,-0.04829942062497139,0.9214761853218079,-0.3854202330112457,-0.06745099276304245,0.9214761853218079,-0.38253360986709595,-0.08643487095832825,0.9214761853218079,-0.37869593501091003,-0.10520385950803757,0.9214761853218079,-0.37391674518585205,-0.12371129542589188,0.9214761853218079,-0.3682079613208771,-0.14191116392612457,0.9214761853218079,-0.36158376932144165,-0.15975822508335114,0.9214761853218079,-0.3540605902671814,-0.1772080957889557,0.9214761853218079,-0.3456571698188782,-0.19421739876270294,0.9214761853218079,-0.33639439940452576,-0.21074384450912476,0.9214761853218079,-0.32629528641700745,-0.2267463505268097,0.9214761853218079,-0.31538498401641846,-0.24218513071537018,0.9214761853218079,-0.3036905527114868,-0.25702181458473206,0.9214761853218079,-0.2912411093711853,-0.2712194621562958,0.9214761853218079,-0.27806758880615234,-0.2847428619861603,0.9214761853218079,-0.2642027735710144,-0.2975583076477051,0.9214761853218079,-0.24968107044696808,-0.3096340000629425,0.9214761853218079,-0.23453864455223083,-0.32093989849090576,0.9214761853218079,-0.21881310641765594,-0.33144786953926086,0.9214761853218079,-0.20254357159137726,-0.3411318063735962,0.9214761853218079,-0.18577048182487488,-0.3499676585197449,0.9214761853218079,-0.16853554546833038,-0.3579334318637848,0.9214761853218079,-0.1508815884590149,-0.3650093078613281,0.9214761853218079,-0.13285252451896667,-0.3711777329444885,0.9214761853218079,-0.11449316889047623,-0.37642332911491394,0.9214761853218079,-0.09584915637969971,-0.38073310256004333,0.9214761853218079,-0.07696685940027237,-0.38409629464149475,0.9214761853218079,-0.05789320170879364,-0.386504590511322,0.9214761853218079,-0.038675617426633835,-0.38795194029808044,0.9214761853218079,-0.01936187595129013,-0.3884347975254059,0.9214761853218079,-4.756954237136892e-17,-0.38795194029808044,0.9214761853218079,0.01936187595129013,-0.386504590511322,0.9214761853218079,0.038675617426633835,-0.38409629464149475,0.9214761853218079,0.05789320170879364,-0.38073310256004333,0.9214761853218079,0.07696685940027237,-0.37642332911491394,0.9214761853218079,0.09584915637969971,-0.3711777329444885,0.9214761853218079,0.11449316889047623,-0.3650093078613281,0.9214761853218079,0.13285252451896667,-0.3579334318637848,0.9214761853218079,0.1508815884590149,-0.3499676585197449,0.9214761853218079,0.16853554546833038,-0.3411318063735962,0.9214761853218079,0.18577048182487488,-0.33144786953926086,0.9214761853218079,0.20254357159137726,-0.32093989849090576,0.9214761853218079,0.21881310641765594,-0.3096340000629425,0.9214761853218079,0.23453864455223083,-0.2975583076477051,0.9214761853218079,0.24968107044696808,-0.2847428619861603,0.9214761853218079,0.2642027735710144,-0.2712194621562958,0.9214761853218079,0.27806758880615234,-0.25702181458473206,0.9214761853218079,0.2912411093711853,-0.24218513071537018,0.9214761853218079,0.3036905527114868,-0.2267463505268097,0.9214761853218079,0.31538498401641846,-0.21074384450912476,0.9214761853218079,0.32629528641700745,-0.19421739876270294,0.9214761853218079,0.33639439940452576,-0.1772080957889557,0.9214761853218079,0.3456571698188782,-0.15975822508335114,0.9214761853218079,0.3540605902671814,-0.14191116392612457,0.9214761853218079,0.36158376932144165,-0.12371129542589188,0.9214761853218079,0.3682079613208771,-0.10520385950803757,0.9214761853218079,0.37391674518585205,-0.08643487095832825,0.9214761853218079,0.37869593501091003,-0.06745099276304245,0.9214761853218079,0.38253360986709595,-0.04829942062497139,0.9214761853218079,0.3854202330112457,-0.029027769342064857,0.9214761853218079,0.38734865188598633,-0.009683948010206223,0.9214761853218079,0.38831406831741333,0.009683948010206223,0.9214761853218079,0.38831406831741333,0.029027769342064857,0.9214761853218079,0.38734865188598633,0.04829942062497139,0.9214761853218079,0.3854202330112457,0.06745099276304245,0.9214761853218079,0.38253360986709595,0.08643487095832825,0.9214761853218079,0.37869593501091003,0.10520385950803757,0.9214761853218079,0.37391674518585205,0.12371129542589188,0.9214761853218079,0.3682079613208771,0.14191116392612457,0.9214761853218079,0.36158376932144165,0.15975822508335114,0.9214761853218079,0.3540605902671814,0.1772080957889557,0.9214761853218079,0.3456571698188782,0.19421739876270294,0.9214761853218079,0.33639439940452576,0.21074384450912476,0.9214761853218079,0.32629528641700745,0.2267463505268097,0.9214761853218079,0.31538498401641846,0.24218513071537018,0.9214761853218079,0.3036905527114868,0.25702181458473206,0.9214761853218079,0.2912411093711853,0.2712194621562958,0.9214761853218079,0.27806758880615234,0.2847428619861603,0.9214761853218079,0.2642027735710144,0.2975583076477051,0.9214761853218079,0.24968107044696808,0.3096340000629425,0.9214761853218079,0.23453864455223083,0.32093989849090576,0.9214761853218079,0.21881310641765594,0.33144786953926086,0.9214761853218079,0.20254357159137726,0.3411318063735962,0.9214761853218079,0.18577048182487488,0.3499676585197449,0.9214761853218079,0.16853554546833038,0.3579334318637848,0.9214761853218079,0.1508815884590149,0.3650093078613281,0.9214761853218079,0.13285252451896667,0.3711777329444885,0.9214761853218079,0.11449316889047623,0.37642332911491394,0.9214761853218079,0.09584915637969971,0.38073310256004333,0.9214761853218079,0.07696685940027237,0.38409629464149475,0.9214761853218079,0.05789320170879364,0.386504590511322,0.9214761853218079,0.038675617426633835,0.38795194029808044,0.9214761853218079,0.01936187595129013,0.3884347975254059,0.9214761853218079,9.513908474273784e-17,0.3420201539993286,0.9396926164627075,0.0,0.34159499406814575,0.9396926164627075,-0.017048297449946404,0.34032055735588074,0.9396926164627075,-0.03405420854687691,0.33820006251335144,0.9396926164627075,-0.050975456833839417,0.3352387249469757,0.9396926164627075,-0.06776997447013855,0.3314439654350281,0.9396926164627075,-0.08439599722623825,0.3268251419067383,0.9396926164627075,-0.10081220418214798,0.32139381766319275,0.9396926164627075,-0.11697778105735779,0.31516343355178833,0.9396926164627075,-0.13285252451896667,0.3081494867801666,0.9396926164627075,-0.14839698374271393,0.3003694713115692,0.9396926164627075,-0.1635724902153015,0.29184266924858093,0.9396926164627075,-0.178341343998909,0.2825902998447418,0.9396926164627075,-0.19266681373119354,0.2726353704929352,0.9396926164627075,-0.20651327073574066,0.2620026171207428,0.9396926164627075,-0.21984630823135376,0.25071850419044495,0.9396926164627075,-0.23263277113437653,0.23881106078624725,0.9396926164627075,-0.24484087526798248,0.2263098806142807,0.9396926164627075,-0.25644028186798096,0.21324607729911804,0.9396926164627075,-0.26740211248397827,0.19965209066867828,0.9396926164627075,-0.27769914269447327,0.18556174635887146,0.9396926164627075,-0.2873057723045349,0.1710100769996643,0.9396926164627075,-0.29619812965393066,0.1560332328081131,0.9396926164627075,-0.30435407161712646,0.14066846668720245,0.9396926164627075,-0.31175336241722107,0.124953992664814,0.9396926164627075,-0.3183775842189789,0.1089288517832756,0.9396926164627075,-0.3242102563381195,0.09263289719820023,0.9396926164627075,-0.3292368948459625,0.07610664516687393,0.9396926164627075,-0.3334449827671051,0.05939117446541786,0.9396926164627075,-0.33682408928871155,0.042528051882982254,0.9396926164627075,-0.33936581015586853,0.0255591981112957,0.9396926164627075,-0.34106379747390747,0.008526798337697983,0.9396926164627075,-0.3419138491153717,-0.008526798337697983,0.9396926164627075,-0.3419138491153717,-0.0255591981112957,0.9396926164627075,-0.34106379747390747,-0.042528051882982254,0.9396926164627075,-0.33936581015586853,-0.05939117446541786,0.9396926164627075,-0.33682408928871155,-0.07610664516687393,0.9396926164627075,-0.3334449827671051,-0.09263289719820023,0.9396926164627075,-0.3292368948459625,-0.1089288517832756,0.9396926164627075,-0.3242102563381195,-0.124953992664814,0.9396926164627075,-0.3183775842189789,-0.14066846668720245,0.9396926164627075,-0.31175336241722107,-0.1560332328081131,0.9396926164627075,-0.30435407161712646,-0.1710100769996643,0.9396926164627075,-0.29619812965393066,-0.18556174635887146,0.9396926164627075,-0.2873057723045349,-0.19965209066867828,0.9396926164627075,-0.27769914269447327,-0.21324607729911804,0.9396926164627075,-0.26740211248397827,-0.2263098806142807,0.9396926164627075,-0.25644028186798096,-0.23881106078624725,0.9396926164627075,-0.24484087526798248,-0.25071850419044495,0.9396926164627075,-0.23263277113437653,-0.2620026171207428,0.9396926164627075,-0.21984630823135376,-0.2726353704929352,0.9396926164627075,-0.20651327073574066,-0.2825902998447418,0.9396926164627075,-0.19266681373119354,-0.29184266924858093,0.9396926164627075,-0.178341343998909,-0.3003694713115692,0.9396926164627075,-0.1635724902153015,-0.3081494867801666,0.9396926164627075,-0.14839698374271393,-0.31516343355178833,0.9396926164627075,-0.13285252451896667,-0.32139381766319275,0.9396926164627075,-0.11697778105735779,-0.3268251419067383,0.9396926164627075,-0.10081220418214798,-0.3314439654350281,0.9396926164627075,-0.08439599722623825,-0.3352387249469757,0.9396926164627075,-0.06776997447013855,-0.33820006251335144,0.9396926164627075,-0.050975456833839417,-0.34032055735588074,0.9396926164627075,-0.03405420854687691,-0.34159499406814575,0.9396926164627075,-0.017048297449946404,-0.3420201539993286,0.9396926164627075,-4.1885388812476027e-17,-0.34159499406814575,0.9396926164627075,0.017048297449946404,-0.34032055735588074,0.9396926164627075,0.03405420854687691,-0.33820006251335144,0.9396926164627075,0.050975456833839417,-0.3352387249469757,0.9396926164627075,0.06776997447013855,-0.3314439654350281,0.9396926164627075,0.08439599722623825,-0.3268251419067383,0.9396926164627075,0.10081220418214798,-0.32139381766319275,0.9396926164627075,0.11697778105735779,-0.31516343355178833,0.9396926164627075,0.13285252451896667,-0.3081494867801666,0.9396926164627075,0.14839698374271393,-0.3003694713115692,0.9396926164627075,0.1635724902153015,-0.29184266924858093,0.9396926164627075,0.178341343998909,-0.2825902998447418,0.9396926164627075,0.19266681373119354,-0.2726353704929352,0.9396926164627075,0.20651327073574066,-0.2620026171207428,0.9396926164627075,0.21984630823135376,-0.25071850419044495,0.9396926164627075,0.23263277113437653,-0.23881106078624725,0.9396926164627075,0.24484087526798248,-0.2263098806142807,0.9396926164627075,0.25644028186798096,-0.21324607729911804,0.9396926164627075,0.26740211248397827,-0.19965209066867828,0.9396926164627075,0.27769914269447327,-0.18556174635887146,0.9396926164627075,0.2873057723045349,-0.1710100769996643,0.9396926164627075,0.29619812965393066,-0.1560332328081131,0.9396926164627075,0.30435407161712646,-0.14066846668720245,0.9396926164627075,0.31175336241722107,-0.124953992664814,0.9396926164627075,0.3183775842189789,-0.1089288517832756,0.9396926164627075,0.3242102563381195,-0.09263289719820023,0.9396926164627075,0.3292368948459625,-0.07610664516687393,0.9396926164627075,0.3334449827671051,-0.05939117446541786,0.9396926164627075,0.33682408928871155,-0.042528051882982254,0.9396926164627075,0.33936581015586853,-0.0255591981112957,0.9396926164627075,0.34106379747390747,-0.008526798337697983,0.9396926164627075,0.3419138491153717,0.008526798337697983,0.9396926164627075,0.3419138491153717,0.0255591981112957,0.9396926164627075,0.34106379747390747,0.042528051882982254,0.9396926164627075,0.33936581015586853,0.05939117446541786,0.9396926164627075,0.33682408928871155,0.07610664516687393,0.9396926164627075,0.3334449827671051,0.09263289719820023,0.9396926164627075,0.3292368948459625,0.1089288517832756,0.9396926164627075,0.3242102563381195,0.124953992664814,0.9396926164627075,0.3183775842189789,0.14066846668720245,0.9396926164627075,0.31175336241722107,0.1560332328081131,0.9396926164627075,0.30435407161712646,0.1710100769996643,0.9396926164627075,0.29619812965393066,0.18556174635887146,0.9396926164627075,0.2873057723045349,0.19965209066867828,0.9396926164627075,0.27769914269447327,0.21324607729911804,0.9396926164627075,0.26740211248397827,0.2263098806142807,0.9396926164627075,0.25644028186798096,0.23881106078624725,0.9396926164627075,0.24484087526798248,0.25071850419044495,0.9396926164627075,0.23263277113437653,0.2620026171207428,0.9396926164627075,0.21984630823135376,0.2726353704929352,0.9396926164627075,0.20651327073574066,0.2825902998447418,0.9396926164627075,0.19266681373119354,0.29184266924858093,0.9396926164627075,0.178341343998909,0.3003694713115692,0.9396926164627075,0.1635724902153015,0.3081494867801666,0.9396926164627075,0.14839698374271393,0.31516343355178833,0.9396926164627075,0.13285252451896667,0.32139381766319275,0.9396926164627075,0.11697778105735779,0.3268251419067383,0.9396926164627075,0.10081220418214798,0.3314439654350281,0.9396926164627075,0.08439599722623825,0.3352387249469757,0.9396926164627075,0.06776997447013855,0.33820006251335144,0.9396926164627075,0.050975456833839417,0.34032055735588074,0.9396926164627075,0.03405420854687691,0.34159499406814575,0.9396926164627075,0.017048297449946404,0.3420201539993286,0.9396926164627075,8.377077762495205e-17,0.29475516080856323,0.955572783946991,0.0,0.2943887710571289,0.955572783946991,-0.014692332595586777,0.29329046607017517,0.955572783946991,-0.029348138719797134,0.2914630174636841,0.955572783946991,-0.04393097758293152,0.2889109253883362,0.955572783946991,-0.058404602110385895,0.28564053773880005,0.955572783946991,-0.07273302227258682,0.2816600203514099,0.955572783946991,-0.08688060939311981,0.2769792675971985,0.955572783946991,-0.10081220418214798,0.2716098725795746,0.955572783946991,-0.11449316889047623,0.26556524634361267,0.955572783946991,-0.127889484167099,0.25886034965515137,0.955572783946991,-0.14096783101558685,0.25151190161705017,0.955572783946991,-0.1536957174539566,0.24353815615177155,0.955572783946991,-0.1660415083169937,0.2349589318037033,0.955572783946991,-0.17797447741031647,0.22579556703567505,0.955572783946991,-0.1894649714231491,0.21607083082199097,0.955572783946991,-0.20048443973064423,0.20580890774726868,0.955572783946991,-0.21100546419620514,0.19503532350063324,0.955572783946991,-0.22100189328193665,0.1837768405675888,0.955572783946991,-0.2304488718509674,0.1720614731311798,0.955572783946991,-0.23932293057441711,0.15991832315921783,0.955572783946991,-0.24760198593139648,0.14737758040428162,0.955572783946991,-0.255265474319458,0.13447044789791107,0.955572783946991,-0.26229432225227356,0.12122900038957596,0.955572783946991,-0.26867106556892395,0.10768615454435349,0.955572783946991,-0.2743798494338989,0.09387558698654175,0.955572783946991,-0.27940648794174194,0.07983162999153137,0.955572783946991,-0.28373846411705017,0.06558919697999954,0.955572783946991,-0.28736504912376404,0.05118370056152344,0.955572783946991,-0.2902771830558777,0.03665095195174217,0.955572783946991,-0.2924676537513733,0.02202708087861538,0.955572783946991,-0.2939309775829315,0.007348450366407633,0.955572783946991,-0.29466354846954346,-0.007348450366407633,0.955572783946991,-0.29466354846954346,-0.02202708087861538,0.955572783946991,-0.2939309775829315,-0.03665095195174217,0.955572783946991,-0.2924676537513733,-0.05118370056152344,0.955572783946991,-0.2902771830558777,-0.06558919697999954,0.955572783946991,-0.28736504912376404,-0.07983162999153137,0.955572783946991,-0.28373846411705017,-0.09387558698654175,0.955572783946991,-0.27940648794174194,-0.10768615454435349,0.955572783946991,-0.2743798494338989,-0.12122900038957596,0.955572783946991,-0.26867106556892395,-0.13447044789791107,0.955572783946991,-0.26229432225227356,-0.14737758040428162,0.955572783946991,-0.255265474319458,-0.15991832315921783,0.955572783946991,-0.24760198593139648,-0.1720614731311798,0.955572783946991,-0.23932293057441711,-0.1837768405675888,0.955572783946991,-0.2304488718509674,-0.19503532350063324,0.955572783946991,-0.22100189328193665,-0.20580890774726868,0.955572783946991,-0.21100546419620514,-0.21607083082199097,0.955572783946991,-0.20048443973064423,-0.22579556703567505,0.955572783946991,-0.1894649714231491,-0.2349589318037033,0.955572783946991,-0.17797447741031647,-0.24353815615177155,0.955572783946991,-0.1660415083169937,-0.25151190161705017,0.955572783946991,-0.1536957174539566,-0.25886034965515137,0.955572783946991,-0.14096783101558685,-0.26556524634361267,0.955572783946991,-0.127889484167099,-0.2716098725795746,0.955572783946991,-0.11449316889047623,-0.2769792675971985,0.955572783946991,-0.10081220418214798,-0.2816600203514099,0.955572783946991,-0.08688060939311981,-0.28564053773880005,0.955572783946991,-0.07273302227258682,-0.2889109253883362,0.955572783946991,-0.058404602110385895,-0.2914630174636841,0.955572783946991,-0.04393097758293152,-0.29329046607017517,0.955572783946991,-0.029348138719797134,-0.2943887710571289,0.955572783946991,-0.014692332595586777,-0.29475516080856323,0.955572783946991,-3.609709652318516e-17,-0.2943887710571289,0.955572783946991,0.014692332595586777,-0.29329046607017517,0.955572783946991,0.029348138719797134,-0.2914630174636841,0.955572783946991,0.04393097758293152,-0.2889109253883362,0.955572783946991,0.058404602110385895,-0.28564053773880005,0.955572783946991,0.07273302227258682,-0.2816600203514099,0.955572783946991,0.08688060939311981,-0.2769792675971985,0.955572783946991,0.10081220418214798,-0.2716098725795746,0.955572783946991,0.11449316889047623,-0.26556524634361267,0.955572783946991,0.127889484167099,-0.25886034965515137,0.955572783946991,0.14096783101558685,-0.25151190161705017,0.955572783946991,0.1536957174539566,-0.24353815615177155,0.955572783946991,0.1660415083169937,-0.2349589318037033,0.955572783946991,0.17797447741031647,-0.22579556703567505,0.955572783946991,0.1894649714231491,-0.21607083082199097,0.955572783946991,0.20048443973064423,-0.20580890774726868,0.955572783946991,0.21100546419620514,-0.19503532350063324,0.955572783946991,0.22100189328193665,-0.1837768405675888,0.955572783946991,0.2304488718509674,-0.1720614731311798,0.955572783946991,0.23932293057441711,-0.15991832315921783,0.955572783946991,0.24760198593139648,-0.14737758040428162,0.955572783946991,0.255265474319458,-0.13447044789791107,0.955572783946991,0.26229432225227356,-0.12122900038957596,0.955572783946991,0.26867106556892395,-0.10768615454435349,0.955572783946991,0.2743798494338989,-0.09387558698654175,0.955572783946991,0.27940648794174194,-0.07983162999153137,0.955572783946991,0.28373846411705017,-0.06558919697999954,0.955572783946991,0.28736504912376404,-0.05118370056152344,0.955572783946991,0.2902771830558777,-0.03665095195174217,0.955572783946991,0.2924676537513733,-0.02202708087861538,0.955572783946991,0.2939309775829315,-0.007348450366407633,0.955572783946991,0.29466354846954346,0.007348450366407633,0.955572783946991,0.29466354846954346,0.02202708087861538,0.955572783946991,0.2939309775829315,0.03665095195174217,0.955572783946991,0.2924676537513733,0.05118370056152344,0.955572783946991,0.2902771830558777,0.06558919697999954,0.955572783946991,0.28736504912376404,0.07983162999153137,0.955572783946991,0.28373846411705017,0.09387558698654175,0.955572783946991,0.27940648794174194,0.10768615454435349,0.955572783946991,0.2743798494338989,0.12122900038957596,0.955572783946991,0.26867106556892395,0.13447044789791107,0.955572783946991,0.26229432225227356,0.14737758040428162,0.955572783946991,0.255265474319458,0.15991832315921783,0.955572783946991,0.24760198593139648,0.1720614731311798,0.955572783946991,0.23932293057441711,0.1837768405675888,0.955572783946991,0.2304488718509674,0.19503532350063324,0.955572783946991,0.22100189328193665,0.20580890774726868,0.955572783946991,0.21100546419620514,0.21607083082199097,0.955572783946991,0.20048443973064423,0.22579556703567505,0.955572783946991,0.1894649714231491,0.2349589318037033,0.955572783946991,0.17797447741031647,0.24353815615177155,0.955572783946991,0.1660415083169937,0.25151190161705017,0.955572783946991,0.1536957174539566,0.25886034965515137,0.955572783946991,0.14096783101558685,0.26556524634361267,0.955572783946991,0.127889484167099,0.2716098725795746,0.955572783946991,0.11449316889047623,0.2769792675971985,0.955572783946991,0.10081220418214798,0.2816600203514099,0.955572783946991,0.08688060939311981,0.28564053773880005,0.955572783946991,0.07273302227258682,0.2889109253883362,0.955572783946991,0.058404602110385895,0.2914630174636841,0.955572783946991,0.04393097758293152,0.29329046607017517,0.955572783946991,0.029348138719797134,0.2943887710571289,0.955572783946991,0.014692332595586777,0.29475516080856323,0.955572783946991,7.219419304637032e-17,0.2467574030160904,0.9690772891044617,0.0,0.24645066261291504,0.9690772891044617,-0.012299841269850731,0.24553120136260986,0.9690772891044617,-0.024569103494286537,0.24400131404399872,0.9690772891044617,-0.036777280271053314,0.24186481535434723,0.9690772891044617,-0.04889402538537979,0.2391269952058792,0.9690772891044617,-0.060889214277267456,0.23579466342926025,0.9690772891044617,-0.07273302227258682,0.23187610507011414,0.9690772891044617,-0.08439599722623825,0.22738106548786163,0.9690772891044617,-0.09584915637969971,0.22232073545455933,0.9690772891044617,-0.10706402361392975,0.21670767664909363,0.9690772891044617,-0.11801270395517349,0.21055583655834198,0.9690772891044617,-0.12866799533367157,0.20388053357601166,0.9690772891044617,-0.13900339603424072,0.19669833779335022,0.9690772891044617,-0.14899320900440216,0.18902713060379028,0.9690772891044617,-0.15861259400844574,0.18088597059249878,0.9690772891044617,-0.16783764958381653,0.17229509353637695,0.9690772891044617,-0.1766454428434372,0.16327588260173798,0.9690772891044617,-0.1850140541791916,0.153850719332695,0.9690772891044617,-0.1929226964712143,0.14404307305812836,0.9690772891044617,-0.20035171508789062,0.13387730717658997,0.9690772891044617,-0.20728261768817902,0.1233787015080452,0.9690772891044617,-0.21369817852973938,0.11257335543632507,0.9690772891044617,-0.2195824533700943,0.1014881357550621,0.9690772891044617,-0.22492080926895142,0.09015060216188431,0.9690772891044617,-0.2296999841928482,0.07858894020318985,0.9690772891044617,-0.23390808701515198,0.06683188676834106,0.9690772891044617,-0.23753465712070465,0.05490868538618088,0.9690772891044617,-0.24057067930698395,0.042848970741033554,0.9690772891044617,-0.2430085986852646,0.030682729557156563,0.9690772891044617,-0.24484236538410187,0.018440203741192818,0.9690772891044617,-0.24606741964817047,0.006151832640171051,0.9690772891044617,-0.24668070673942566,-0.006151832640171051,0.9690772891044617,-0.24668070673942566,-0.018440203741192818,0.9690772891044617,-0.24606741964817047,-0.030682729557156563,0.9690772891044617,-0.24484236538410187,-0.042848970741033554,0.9690772891044617,-0.2430085986852646,-0.05490868538618088,0.9690772891044617,-0.24057067930698395,-0.06683188676834106,0.9690772891044617,-0.23753465712070465,-0.07858894020318985,0.9690772891044617,-0.23390808701515198,-0.09015060216188431,0.9690772891044617,-0.2296999841928482,-0.1014881357550621,0.9690772891044617,-0.22492080926895142,-0.11257335543632507,0.9690772891044617,-0.2195824533700943,-0.1233787015080452,0.9690772891044617,-0.21369817852973938,-0.13387730717658997,0.9690772891044617,-0.20728261768817902,-0.14404307305812836,0.9690772891044617,-0.20035171508789062,-0.153850719332695,0.9690772891044617,-0.1929226964712143,-0.16327588260173798,0.9690772891044617,-0.1850140541791916,-0.17229509353637695,0.9690772891044617,-0.1766454428434372,-0.18088597059249878,0.9690772891044617,-0.16783764958381653,-0.18902713060379028,0.9690772891044617,-0.15861259400844574,-0.19669833779335022,0.9690772891044617,-0.14899320900440216,-0.20388053357601166,0.9690772891044617,-0.13900339603424072,-0.21055583655834198,0.9690772891044617,-0.12866799533367157,-0.21670767664909363,0.9690772891044617,-0.11801270395517349,-0.22232073545455933,0.9690772891044617,-0.10706402361392975,-0.22738106548786163,0.9690772891044617,-0.09584915637969971,-0.23187610507011414,0.9690772891044617,-0.08439599722623825,-0.23579466342926025,0.9690772891044617,-0.07273302227258682,-0.2391269952058792,0.9690772891044617,-0.060889214277267456,-0.24186481535434723,0.9690772891044617,-0.04889402538537979,-0.24400131404399872,0.9690772891044617,-0.036777280271053314,-0.24553120136260986,0.9690772891044617,-0.024569103494286537,-0.24645066261291504,0.9690772891044617,-0.012299841269850731,-0.2467574030160904,0.9690772891044617,-3.021906506359964e-17,-0.24645066261291504,0.9690772891044617,0.012299841269850731,-0.24553120136260986,0.9690772891044617,0.024569103494286537,-0.24400131404399872,0.9690772891044617,0.036777280271053314,-0.24186481535434723,0.9690772891044617,0.04889402538537979,-0.2391269952058792,0.9690772891044617,0.060889214277267456,-0.23579466342926025,0.9690772891044617,0.07273302227258682,-0.23187610507011414,0.9690772891044617,0.08439599722623825,-0.22738106548786163,0.9690772891044617,0.09584915637969971,-0.22232073545455933,0.9690772891044617,0.10706402361392975,-0.21670767664909363,0.9690772891044617,0.11801270395517349,-0.21055583655834198,0.9690772891044617,0.12866799533367157,-0.20388053357601166,0.9690772891044617,0.13900339603424072,-0.19669833779335022,0.9690772891044617,0.14899320900440216,-0.18902713060379028,0.9690772891044617,0.15861259400844574,-0.18088597059249878,0.9690772891044617,0.16783764958381653,-0.17229509353637695,0.9690772891044617,0.1766454428434372,-0.16327588260173798,0.9690772891044617,0.1850140541791916,-0.153850719332695,0.9690772891044617,0.1929226964712143,-0.14404307305812836,0.9690772891044617,0.20035171508789062,-0.13387730717658997,0.9690772891044617,0.20728261768817902,-0.1233787015080452,0.9690772891044617,0.21369817852973938,-0.11257335543632507,0.9690772891044617,0.2195824533700943,-0.1014881357550621,0.9690772891044617,0.22492080926895142,-0.09015060216188431,0.9690772891044617,0.2296999841928482,-0.07858894020318985,0.9690772891044617,0.23390808701515198,-0.06683188676834106,0.9690772891044617,0.23753465712070465,-0.05490868538618088,0.9690772891044617,0.24057067930698395,-0.042848970741033554,0.9690772891044617,0.2430085986852646,-0.030682729557156563,0.9690772891044617,0.24484236538410187,-0.018440203741192818,0.9690772891044617,0.24606741964817047,-0.006151832640171051,0.9690772891044617,0.24668070673942566,0.006151832640171051,0.9690772891044617,0.24668070673942566,0.018440203741192818,0.9690772891044617,0.24606741964817047,0.030682729557156563,0.9690772891044617,0.24484236538410187,0.042848970741033554,0.9690772891044617,0.2430085986852646,0.05490868538618088,0.9690772891044617,0.24057067930698395,0.06683188676834106,0.9690772891044617,0.23753465712070465,0.07858894020318985,0.9690772891044617,0.23390808701515198,0.09015060216188431,0.9690772891044617,0.2296999841928482,0.1014881357550621,0.9690772891044617,0.22492080926895142,0.11257335543632507,0.9690772891044617,0.2195824533700943,0.1233787015080452,0.9690772891044617,0.21369817852973938,0.13387730717658997,0.9690772891044617,0.20728261768817902,0.14404307305812836,0.9690772891044617,0.20035171508789062,0.153850719332695,0.9690772891044617,0.1929226964712143,0.16327588260173798,0.9690772891044617,0.1850140541791916,0.17229509353637695,0.9690772891044617,0.1766454428434372,0.18088597059249878,0.9690772891044617,0.16783764958381653,0.18902713060379028,0.9690772891044617,0.15861259400844574,0.19669833779335022,0.9690772891044617,0.14899320900440216,0.20388053357601166,0.9690772891044617,0.13900339603424072,0.21055583655834198,0.9690772891044617,0.12866799533367157,0.21670767664909363,0.9690772891044617,0.11801270395517349,0.22232073545455933,0.9690772891044617,0.10706402361392975,0.22738106548786163,0.9690772891044617,0.09584915637969971,0.23187610507011414,0.9690772891044617,0.08439599722623825,0.23579466342926025,0.9690772891044617,0.07273302227258682,0.2391269952058792,0.9690772891044617,0.060889214277267456,0.24186481535434723,0.9690772891044617,0.04889402538537979,0.24400131404399872,0.9690772891044617,0.036777280271053314,0.24553120136260986,0.9690772891044617,0.024569103494286537,0.24645066261291504,0.9690772891044617,0.012299841269850731,0.2467574030160904,0.9690772891044617,6.043813012719928e-17,0.19814614951610565,0.9801725149154663,0.0,0.19789983332157135,0.9801725149154663,-0.009876769967377186,0.1971615105867386,0.9801725149154663,-0.01972898468375206,0.1959330141544342,0.9801725149154663,-0.02953214943408966,0.19421739876270294,0.9801725149154663,-0.039261892437934875,0.19201892614364624,0.9801725149154663,-0.04889402538537979,0.18934306502342224,0.9801725149154663,-0.058404602110385895,0.1861964613199234,0.9801725149154663,-0.06776997447013855,0.18258695304393768,0.9801725149154663,-0.07696685940027237,0.17852351069450378,0.9801725149154663,-0.0859723910689354,0.17401622235774994,0.9801725149154663,-0.09476418048143387,0.16907629370689392,0.9801725149154663,-0.10332037508487701,0.16371603310108185,0.9801725149154663,-0.11161969602108002,0.15794873237609863,0.9801725149154663,-0.11964151263237,0.15178875625133514,0.9801725149154663,-0.12736588716506958,0.14525140821933746,0.9801725149154663,-0.1347736120223999,0.13835293054580688,0.9801725149154663,-0.14184625446796417,0.13111048936843872,0.9801725149154663,-0.14856626093387604,0.12354210019111633,0.9801725149154663,-0.1549168974161148,0.11566656082868576,0.9801725149154663,-0.1608823835849762,0.10750345140695572,0.9801725149154663,-0.16644789278507233,0.09907307475805283,0.9801725149154663,-0.1715995967388153,0.09039638191461563,0.9801725149154663,-0.17632466554641724,0.08149494975805283,0.9801725149154663,-0.18061137199401855,0.07239091396331787,0.9801725149154663,-0.18444904685020447,0.06310690194368362,0.9801725149154663,-0.1878281533718109,0.053665995597839355,0.9801725149154663,-0.19074028730392456,0.044091664254665375,0.9801725149154663,-0.1931782066822052,0.03440771624445915,0.9801725149154663,-0.19513586163520813,0.024638226255774498,0.9801725149154663,-0.19660836458206177,0.014807479456067085,0.9801725149154663,-0.19759207963943481,0.0049399202689528465,0.9801725149154663,-0.19808456301689148,-0.0049399202689528465,0.9801725149154663,-0.19808456301689148,-0.014807479456067085,0.9801725149154663,-0.19759207963943481,-0.024638226255774498,0.9801725149154663,-0.19660836458206177,-0.03440771624445915,0.9801725149154663,-0.19513586163520813,-0.044091664254665375,0.9801725149154663,-0.1931782066822052,-0.053665995597839355,0.9801725149154663,-0.19074028730392456,-0.06310690194368362,0.9801725149154663,-0.1878281533718109,-0.07239091396331787,0.9801725149154663,-0.18444904685020447,-0.08149494975805283,0.9801725149154663,-0.18061137199401855,-0.09039638191461563,0.9801725149154663,-0.17632466554641724,-0.09907307475805283,0.9801725149154663,-0.1715995967388153,-0.10750345140695572,0.9801725149154663,-0.16644789278507233,-0.11566656082868576,0.9801725149154663,-0.1608823835849762,-0.12354210019111633,0.9801725149154663,-0.1549168974161148,-0.13111048936843872,0.9801725149154663,-0.14856626093387604,-0.13835293054580688,0.9801725149154663,-0.14184625446796417,-0.14525140821933746,0.9801725149154663,-0.1347736120223999,-0.15178875625133514,0.9801725149154663,-0.12736588716506958,-0.15794873237609863,0.9801725149154663,-0.11964151263237,-0.16371603310108185,0.9801725149154663,-0.11161969602108002,-0.16907629370689392,0.9801725149154663,-0.10332037508487701,-0.17401622235774994,0.9801725149154663,-0.09476418048143387,-0.17852351069450378,0.9801725149154663,-0.0859723910689354,-0.18258695304393768,0.9801725149154663,-0.07696685940027237,-0.1861964613199234,0.9801725149154663,-0.06776997447013855,-0.18934306502342224,0.9801725149154663,-0.058404602110385895,-0.19201892614364624,0.9801725149154663,-0.04889402538537979,-0.19421739876270294,0.9801725149154663,-0.039261892437934875,-0.1959330141544342,0.9801725149154663,-0.02953214943408966,-0.1971615105867386,0.9801725149154663,-0.01972898468375206,-0.19789983332157135,0.9801725149154663,-0.009876769967377186,-0.19814614951610565,0.9801725149154663,-2.4265904097698378e-17,-0.19789983332157135,0.9801725149154663,0.009876769967377186,-0.1971615105867386,0.9801725149154663,0.01972898468375206,-0.1959330141544342,0.9801725149154663,0.02953214943408966,-0.19421739876270294,0.9801725149154663,0.039261892437934875,-0.19201892614364624,0.9801725149154663,0.04889402538537979,-0.18934306502342224,0.9801725149154663,0.058404602110385895,-0.1861964613199234,0.9801725149154663,0.06776997447013855,-0.18258695304393768,0.9801725149154663,0.07696685940027237,-0.17852351069450378,0.9801725149154663,0.0859723910689354,-0.17401622235774994,0.9801725149154663,0.09476418048143387,-0.16907629370689392,0.9801725149154663,0.10332037508487701,-0.16371603310108185,0.9801725149154663,0.11161969602108002,-0.15794873237609863,0.9801725149154663,0.11964151263237,-0.15178875625133514,0.9801725149154663,0.12736588716506958,-0.14525140821933746,0.9801725149154663,0.1347736120223999,-0.13835293054580688,0.9801725149154663,0.14184625446796417,-0.13111048936843872,0.9801725149154663,0.14856626093387604,-0.12354210019111633,0.9801725149154663,0.1549168974161148,-0.11566656082868576,0.9801725149154663,0.1608823835849762,-0.10750345140695572,0.9801725149154663,0.16644789278507233,-0.09907307475805283,0.9801725149154663,0.1715995967388153,-0.09039638191461563,0.9801725149154663,0.17632466554641724,-0.08149494975805283,0.9801725149154663,0.18061137199401855,-0.07239091396331787,0.9801725149154663,0.18444904685020447,-0.06310690194368362,0.9801725149154663,0.1878281533718109,-0.053665995597839355,0.9801725149154663,0.19074028730392456,-0.044091664254665375,0.9801725149154663,0.1931782066822052,-0.03440771624445915,0.9801725149154663,0.19513586163520813,-0.024638226255774498,0.9801725149154663,0.19660836458206177,-0.014807479456067085,0.9801725149154663,0.19759207963943481,-0.0049399202689528465,0.9801725149154663,0.19808456301689148,0.0049399202689528465,0.9801725149154663,0.19808456301689148,0.014807479456067085,0.9801725149154663,0.19759207963943481,0.024638226255774498,0.9801725149154663,0.19660836458206177,0.03440771624445915,0.9801725149154663,0.19513586163520813,0.044091664254665375,0.9801725149154663,0.1931782066822052,0.053665995597839355,0.9801725149154663,0.19074028730392456,0.06310690194368362,0.9801725149154663,0.1878281533718109,0.07239091396331787,0.9801725149154663,0.18444904685020447,0.08149494975805283,0.9801725149154663,0.18061137199401855,0.09039638191461563,0.9801725149154663,0.17632466554641724,0.09907307475805283,0.9801725149154663,0.1715995967388153,0.10750345140695572,0.9801725149154663,0.16644789278507233,0.11566656082868576,0.9801725149154663,0.1608823835849762,0.12354210019111633,0.9801725149154663,0.1549168974161148,0.13111048936843872,0.9801725149154663,0.14856626093387604,0.13835293054580688,0.9801725149154663,0.14184625446796417,0.14525140821933746,0.9801725149154663,0.1347736120223999,0.15178875625133514,0.9801725149154663,0.12736588716506958,0.15794873237609863,0.9801725149154663,0.11964151263237,0.16371603310108185,0.9801725149154663,0.11161969602108002,0.16907629370689392,0.9801725149154663,0.10332037508487701,0.17401622235774994,0.9801725149154663,0.09476418048143387,0.17852351069450378,0.9801725149154663,0.0859723910689354,0.18258695304393768,0.9801725149154663,0.07696685940027237,0.1861964613199234,0.9801725149154663,0.06776997447013855,0.18934306502342224,0.9801725149154663,0.058404602110385895,0.19201892614364624,0.9801725149154663,0.04889402538537979,0.19421739876270294,0.9801725149154663,0.039261892437934875,0.1959330141544342,0.9801725149154663,0.02953214943408966,0.1971615105867386,0.9801725149154663,0.01972898468375206,0.19789983332157135,0.9801725149154663,0.009876769967377186,0.19814614951610565,0.9801725149154663,4.8531808195396756e-17,0.1490422636270523,0.9888308048248291,0.0,0.1488569974899292,0.9888308048248291,-0.007429143879562616,0.1483016461133957,0.9888308048248291,-0.014839817769825459,0.14737758040428162,0.9888308048248291,-0.02221359685063362,0.14608712494373322,0.9888308048248291,-0.02953214943408966,0.14443346858024597,0.9888308048248291,-0.036777280271053314,0.14242073893547058,0.9888308048248291,-0.04393097758293152,0.1400539129972458,0.9888308048248291,-0.050975456833839417,0.13733890652656555,0.9888308048248291,-0.05789320170879364,0.1342824399471283,0.9888308048248291,-0.06466701626777649,0.13089212775230408,0.9888308048248291,-0.07128005474805832,0.1271764039993286,0.9888308048248291,-0.07771588116884232,0.12314449995756149,0.9888308048248291,-0.08395849913358688,0.11880643665790558,0.9888308048248291,-0.08999237418174744,0.11417300254106522,0.9888308048248291,-0.09580252319574356,0.10925570875406265,0.9888308048248291,-0.10137448459863663,0.10406679660081863,0.9888308048248291,-0.10669441521167755,0.09861915558576584,0.9888308048248291,-0.1117490902543068,0.09292633086442947,0.9888308048248291,-0.11652593314647675,0.0870024785399437,0.9888308048248291,-0.1210130825638771,0.08086232095956802,0.9888308048248291,-0.1251993626356125,0.07452113181352615,0.9888308048248291,-0.12907439470291138,0.06799466907978058,0.9888308048248291,-0.13262851536273956,0.0612991601228714,0.9888308048248291,-0.1358529031276703,0.05445125326514244,0.9888308048248291,-0.13873952627182007,0.047467973083257675,0.9888308048248291,-0.14128124713897705,0.040366675704717636,0.9888308048248291,-0.14347170293331146,0.03316502273082733,0.9888308048248291,-0.14530546963214874,0.02588091790676117,0.9888308048248291,-0.14677797257900238,0.018532468006014824,0.9888308048248291,-0.14788557589054108,0.011137942783534527,0.9888308048248291,-0.14862550795078278,0.0037157267797738314,0.9888308048248291,-0.14899593591690063,-0.0037157267797738314,0.9888308048248291,-0.14899593591690063,-0.011137942783534527,0.9888308048248291,-0.14862550795078278,-0.018532468006014824,0.9888308048248291,-0.14788557589054108,-0.02588091790676117,0.9888308048248291,-0.14677797257900238,-0.03316502273082733,0.9888308048248291,-0.14530546963214874,-0.040366675704717636,0.9888308048248291,-0.14347170293331146,-0.047467973083257675,0.9888308048248291,-0.14128124713897705,-0.05445125326514244,0.9888308048248291,-0.13873952627182007,-0.0612991601228714,0.9888308048248291,-0.1358529031276703,-0.06799466907978058,0.9888308048248291,-0.13262851536273956,-0.07452113181352615,0.9888308048248291,-0.12907439470291138,-0.08086232095956802,0.9888308048248291,-0.1251993626356125,-0.0870024785399437,0.9888308048248291,-0.1210130825638771,-0.09292633086442947,0.9888308048248291,-0.11652593314647675,-0.09861915558576584,0.9888308048248291,-0.1117490902543068,-0.10406679660081863,0.9888308048248291,-0.10669441521167755,-0.10925570875406265,0.9888308048248291,-0.10137448459863663,-0.11417300254106522,0.9888308048248291,-0.09580252319574356,-0.11880643665790558,0.9888308048248291,-0.08999237418174744,-0.12314449995756149,0.9888308048248291,-0.08395849913358688,-0.1271764039993286,0.9888308048248291,-0.07771588116884232,-0.13089212775230408,0.9888308048248291,-0.07128005474805832,-0.1342824399471283,0.9888308048248291,-0.06466701626777649,-0.13733890652656555,0.9888308048248291,-0.05789320170879364,-0.1400539129972458,0.9888308048248291,-0.050975456833839417,-0.14242073893547058,0.9888308048248291,-0.04393097758293152,-0.14443346858024597,0.9888308048248291,-0.036777280271053314,-0.14608712494373322,0.9888308048248291,-0.02953214943408966,-0.14737758040428162,0.9888308048248291,-0.02221359685063362,-0.1483016461133957,0.9888308048248291,-0.014839817769825459,-0.1488569974899292,0.9888308048248291,-0.007429143879562616,-0.1490422636270523,0.9888308048248291,-1.8252413541001174e-17,-0.1488569974899292,0.9888308048248291,0.007429143879562616,-0.1483016461133957,0.9888308048248291,0.014839817769825459,-0.14737758040428162,0.9888308048248291,0.02221359685063362,-0.14608712494373322,0.9888308048248291,0.02953214943408966,-0.14443346858024597,0.9888308048248291,0.036777280271053314,-0.14242073893547058,0.9888308048248291,0.04393097758293152,-0.1400539129972458,0.9888308048248291,0.050975456833839417,-0.13733890652656555,0.9888308048248291,0.05789320170879364,-0.1342824399471283,0.9888308048248291,0.06466701626777649,-0.13089212775230408,0.9888308048248291,0.07128005474805832,-0.1271764039993286,0.9888308048248291,0.07771588116884232,-0.12314449995756149,0.9888308048248291,0.08395849913358688,-0.11880643665790558,0.9888308048248291,0.08999237418174744,-0.11417300254106522,0.9888308048248291,0.09580252319574356,-0.10925570875406265,0.9888308048248291,0.10137448459863663,-0.10406679660081863,0.9888308048248291,0.10669441521167755,-0.09861915558576584,0.9888308048248291,0.1117490902543068,-0.09292633086442947,0.9888308048248291,0.11652593314647675,-0.0870024785399437,0.9888308048248291,0.1210130825638771,-0.08086232095956802,0.9888308048248291,0.1251993626356125,-0.07452113181352615,0.9888308048248291,0.12907439470291138,-0.06799466907978058,0.9888308048248291,0.13262851536273956,-0.0612991601228714,0.9888308048248291,0.1358529031276703,-0.05445125326514244,0.9888308048248291,0.13873952627182007,-0.047467973083257675,0.9888308048248291,0.14128124713897705,-0.040366675704717636,0.9888308048248291,0.14347170293331146,-0.03316502273082733,0.9888308048248291,0.14530546963214874,-0.02588091790676117,0.9888308048248291,0.14677797257900238,-0.018532468006014824,0.9888308048248291,0.14788557589054108,-0.011137942783534527,0.9888308048248291,0.14862550795078278,-0.0037157267797738314,0.9888308048248291,0.14899593591690063,0.0037157267797738314,0.9888308048248291,0.14899593591690063,0.011137942783534527,0.9888308048248291,0.14862550795078278,0.018532468006014824,0.9888308048248291,0.14788557589054108,0.02588091790676117,0.9888308048248291,0.14677797257900238,0.03316502273082733,0.9888308048248291,0.14530546963214874,0.040366675704717636,0.9888308048248291,0.14347170293331146,0.047467973083257675,0.9888308048248291,0.14128124713897705,0.05445125326514244,0.9888308048248291,0.13873952627182007,0.0612991601228714,0.9888308048248291,0.1358529031276703,0.06799466907978058,0.9888308048248291,0.13262851536273956,0.07452113181352615,0.9888308048248291,0.12907439470291138,0.08086232095956802,0.9888308048248291,0.1251993626356125,0.0870024785399437,0.9888308048248291,0.1210130825638771,0.09292633086442947,0.9888308048248291,0.11652593314647675,0.09861915558576584,0.9888308048248291,0.1117490902543068,0.10406679660081863,0.9888308048248291,0.10669441521167755,0.10925570875406265,0.9888308048248291,0.10137448459863663,0.11417300254106522,0.9888308048248291,0.09580252319574356,0.11880643665790558,0.9888308048248291,0.08999237418174744,0.12314449995756149,0.9888308048248291,0.08395849913358688,0.1271764039993286,0.9888308048248291,0.07771588116884232,0.13089212775230408,0.9888308048248291,0.07128005474805832,0.1342824399471283,0.9888308048248291,0.06466701626777649,0.13733890652656555,0.9888308048248291,0.05789320170879364,0.1400539129972458,0.9888308048248291,0.050975456833839417,0.14242073893547058,0.9888308048248291,0.04393097758293152,0.14443346858024597,0.9888308048248291,0.036777280271053314,0.14608712494373322,0.9888308048248291,0.02953214943408966,0.14737758040428162,0.9888308048248291,0.02221359685063362,0.1483016461133957,0.9888308048248291,0.014839817769825459,0.1488569974899292,0.9888308048248291,0.007429143879562616,0.1490422636270523,0.9888308048248291,3.650482708200235e-17,0.09956784546375275,0.9950307607650757,0.0,0.09944407641887665,0.9950307607650757,-0.004963047336786985,0.09907307475805283,0.9950307607650757,-0.009913756512105465,0.0984557569026947,0.9950307607650757,-0.014839817769825459,0.09759366512298584,0.9950307607650757,-0.01972898468375206,0.09648893773555756,0.9950307607650757,-0.024569103494286537,0.09514432400465012,0.9950307607650757,-0.029348138719797134,0.09356316924095154,0.9950307607650757,-0.03405420854687691,0.0917493999004364,0.9950307607650757,-0.038675617426633835,0.08970753103494644,0.9950307607650757,-0.04320086911320686,0.08744262903928757,0.9950307607650757,-0.047618716955184937,0.08496033400297165,0.9950307607650757,-0.05191817879676819,0.08226681500673294,0.9950307607650757,-0.05608856678009033,0.07936877012252808,0.9950307607650757,-0.06011950597167015,0.07627339661121368,0.9950307607650757,-0.06400097906589508,0.07298839837312698,0.9950307607650757,-0.0677233338356018,0.0695219337940216,0.9950307607650757,-0.07127732038497925,0.06588263064622879,0.9950307607650757,-0.07465410232543945,0.0620795376598835,0.9950307607650757,-0.07784527540206909,0.05812210217118263,0.9950307607650757,-0.08084291964769363,0.05402016267180443,0.9950307607650757,-0.08363956958055496,0.04978392273187637,0.9950307607650757,-0.08622828125953674,0.04542391374707222,0.9950307607650757,-0.08860261738300323,0.040950972586870193,0.9950307607650757,-0.09075667709112167,0.0363762192428112,0.9950307607650757,-0.09268509596586227,0.03171103075146675,0.9950307607650757,-0.09438308328390121,0.0269670020788908,0.9950307607650757,-0.09584642201662064,0.02215592935681343,0.9950307607650757,-0.09707147628068924,0.017289774492383003,0.9950307607650757,-0.09805518388748169,0.012380635365843773,0.9950307607650757,-0.09879512339830399,0.007440714631229639,0.9950307607650757,-0.0992894321680069,0.0024822952691465616,0.9950307607650757,-0.09953689575195312,-0.0024822952691465616,0.9950307607650757,-0.09953689575195312,-0.007440714631229639,0.9950307607650757,-0.0992894321680069,-0.012380635365843773,0.9950307607650757,-0.09879512339830399,-0.017289774492383003,0.9950307607650757,-0.09805518388748169,-0.02215592935681343,0.9950307607650757,-0.09707147628068924,-0.0269670020788908,0.9950307607650757,-0.09584642201662064,-0.03171103075146675,0.9950307607650757,-0.09438308328390121,-0.0363762192428112,0.9950307607650757,-0.09268509596586227,-0.040950972586870193,0.9950307607650757,-0.09075667709112167,-0.04542391374707222,0.9950307607650757,-0.08860261738300323,-0.04978392273187637,0.9950307607650757,-0.08622828125953674,-0.05402016267180443,0.9950307607650757,-0.08363956958055496,-0.05812210217118263,0.9950307607650757,-0.08084291964769363,-0.0620795376598835,0.9950307607650757,-0.07784527540206909,-0.06588263064622879,0.9950307607650757,-0.07465410232543945,-0.0695219337940216,0.9950307607650757,-0.07127732038497925,-0.07298839837312698,0.9950307607650757,-0.0677233338356018,-0.07627339661121368,0.9950307607650757,-0.06400097906589508,-0.07936877012252808,0.9950307607650757,-0.06011950597167015,-0.08226681500673294,0.9950307607650757,-0.05608856678009033,-0.08496033400297165,0.9950307607650757,-0.05191817879676819,-0.08744262903928757,0.9950307607650757,-0.047618716955184937,-0.08970753103494644,0.9950307607650757,-0.04320086911320686,-0.0917493999004364,0.9950307607650757,-0.038675617426633835,-0.09356316924095154,0.9950307607650757,-0.03405420854687691,-0.09514432400465012,0.9950307607650757,-0.029348138719797134,-0.09648893773555756,0.9950307607650757,-0.024569103494286537,-0.09759366512298584,0.9950307607650757,-0.01972898468375206,-0.0984557569026947,0.9950307607650757,-0.014839817769825459,-0.09907307475805283,0.9950307607650757,-0.009913756512105465,-0.09944407641887665,0.9950307607650757,-0.004963047336786985,-0.09956784546375275,0.9950307607650757,-1.2193544683079923e-17,-0.09944407641887665,0.9950307607650757,0.004963047336786985,-0.09907307475805283,0.9950307607650757,0.009913756512105465,-0.0984557569026947,0.9950307607650757,0.014839817769825459,-0.09759366512298584,0.9950307607650757,0.01972898468375206,-0.09648893773555756,0.9950307607650757,0.024569103494286537,-0.09514432400465012,0.9950307607650757,0.029348138719797134,-0.09356316924095154,0.9950307607650757,0.03405420854687691,-0.0917493999004364,0.9950307607650757,0.038675617426633835,-0.08970753103494644,0.9950307607650757,0.04320086911320686,-0.08744262903928757,0.9950307607650757,0.047618716955184937,-0.08496033400297165,0.9950307607650757,0.05191817879676819,-0.08226681500673294,0.9950307607650757,0.05608856678009033,-0.07936877012252808,0.9950307607650757,0.06011950597167015,-0.07627339661121368,0.9950307607650757,0.06400097906589508,-0.07298839837312698,0.9950307607650757,0.0677233338356018,-0.0695219337940216,0.9950307607650757,0.07127732038497925,-0.06588263064622879,0.9950307607650757,0.07465410232543945,-0.0620795376598835,0.9950307607650757,0.07784527540206909,-0.05812210217118263,0.9950307607650757,0.08084291964769363,-0.05402016267180443,0.9950307607650757,0.08363956958055496,-0.04978392273187637,0.9950307607650757,0.08622828125953674,-0.04542391374707222,0.9950307607650757,0.08860261738300323,-0.040950972586870193,0.9950307607650757,0.09075667709112167,-0.0363762192428112,0.9950307607650757,0.09268509596586227,-0.03171103075146675,0.9950307607650757,0.09438308328390121,-0.0269670020788908,0.9950307607650757,0.09584642201662064,-0.02215592935681343,0.9950307607650757,0.09707147628068924,-0.017289774492383003,0.9950307607650757,0.09805518388748169,-0.012380635365843773,0.9950307607650757,0.09879512339830399,-0.007440714631229639,0.9950307607650757,0.0992894321680069,-0.0024822952691465616,0.9950307607650757,0.09953689575195312,0.0024822952691465616,0.9950307607650757,0.09953689575195312,0.007440714631229639,0.9950307607650757,0.0992894321680069,0.012380635365843773,0.9950307607650757,0.09879512339830399,0.017289774492383003,0.9950307607650757,0.09805518388748169,0.02215592935681343,0.9950307607650757,0.09707147628068924,0.0269670020788908,0.9950307607650757,0.09584642201662064,0.03171103075146675,0.9950307607650757,0.09438308328390121,0.0363762192428112,0.9950307607650757,0.09268509596586227,0.040950972586870193,0.9950307607650757,0.09075667709112167,0.04542391374707222,0.9950307607650757,0.08860261738300323,0.04978392273187637,0.9950307607650757,0.08622828125953674,0.05402016267180443,0.9950307607650757,0.08363956958055496,0.05812210217118263,0.9950307607650757,0.08084291964769363,0.0620795376598835,0.9950307607650757,0.07784527540206909,0.06588263064622879,0.9950307607650757,0.07465410232543945,0.0695219337940216,0.9950307607650757,0.07127732038497925,0.07298839837312698,0.9950307607650757,0.0677233338356018,0.07627339661121368,0.9950307607650757,0.06400097906589508,0.07936877012252808,0.9950307607650757,0.06011950597167015,0.08226681500673294,0.9950307607650757,0.05608856678009033,0.08496033400297165,0.9950307607650757,0.05191817879676819,0.08744262903928757,0.9950307607650757,0.047618716955184937,0.08970753103494644,0.9950307607650757,0.04320086911320686,0.0917493999004364,0.9950307607650757,0.038675617426633835,0.09356316924095154,0.9950307607650757,0.03405420854687691,0.09514432400465012,0.9950307607650757,0.029348138719797134,0.09648893773555756,0.9950307607650757,0.024569103494286537,0.09759366512298584,0.9950307607650757,0.01972898468375206,0.0984557569026947,0.9950307607650757,0.014839817769825459,0.09907307475805283,0.9950307607650757,0.009913756512105465,0.09944407641887665,0.9950307607650757,0.004963047336786985,0.09956784546375275,0.9950307607650757,2.4387089366159847e-17,0.04984588548541069,0.9987569451332092,0.0,0.04978392273187637,0.9987569451332092,-0.002484612399712205,0.04959819093346596,0.9987569451332092,-0.004963047336786985,0.049289148300886154,0.9987569451332092,-0.007429143879562616,0.048857565969228745,0.9987569451332092,-0.009876769967377186,0.048304516822099686,0.9987569451332092,-0.012299841269850731,0.04763137176632881,0.9987569451332092,-0.014692332595586777,0.04683981090784073,0.9987569451332092,-0.017048297449946404,0.045931797474622726,0.9987569451332092,-0.01936187595129013,0.04490959271788597,0.9987569451332092,-0.02162731997668743,0.043775733560323715,0.9987569451332092,-0.023838993161916733,0.042533040046691895,0.9987569451332092,-0.025991398841142654,0.04118460416793823,0.9987569451332092,-0.028079187497496605,0.039733774960041046,0.9987569451332092,-0.03009716607630253,0.038184162229299545,0.9987569451332092,-0.0320403166115284,0.03653961792588234,0.9987569451332092,-0.0339038111269474,0.03480423241853714,0.9987569451332092,-0.03568301722407341,0.03298231586813927,0.9987569451332092,-0.037373509258031845,0.03107840195298195,0.9987569451332092,-0.038971081376075745,0.029097219929099083,0.9987569451332092,-0.040471769869327545,0.027043698355555534,0.9987569451332092,-0.04187183454632759,0.024922942742705345,0.9987569451332092,-0.043167803436517715,0.02274022437632084,0.9987569451332092,-0.04435644671320915,0.020500969141721725,0.9987569451332092,-0.04543481767177582,0.018210746347904205,0.9987569451332092,-0.04640022665262222,0.01587524823844433,0.9987569451332092,-0.047250278294086456,0.013500283472239971,0.9987569451332092,-0.04798285663127899,0.011091752909123898,0.9987569451332092,-0.04859614372253418,0.008655646815896034,0.9987569451332092,-0.04908861592411995,0.006198022048920393,0.9987569451332092,-0.04945904389023781,0.003724987618625164,0.9987569451332092,-0.049706507474184036,0.0012426924658939242,0.9987569451332092,-0.04983039200305939,-0.0012426924658939242,0.9987569451332092,-0.04983039200305939,-0.003724987618625164,0.9987569451332092,-0.049706507474184036,-0.006198022048920393,0.9987569451332092,-0.04945904389023781,-0.008655646815896034,0.9987569451332092,-0.04908861592411995,-0.011091752909123898,0.9987569451332092,-0.04859614372253418,-0.013500283472239971,0.9987569451332092,-0.04798285663127899,-0.01587524823844433,0.9987569451332092,-0.047250278294086456,-0.018210746347904205,0.9987569451332092,-0.04640022665262222,-0.020500969141721725,0.9987569451332092,-0.04543481767177582,-0.02274022437632084,0.9987569451332092,-0.04435644671320915,-0.024922942742705345,0.9987569451332092,-0.043167803436517715,-0.027043698355555534,0.9987569451332092,-0.04187183454632759,-0.029097219929099083,0.9987569451332092,-0.040471769869327545,-0.03107840195298195,0.9987569451332092,-0.038971081376075745,-0.03298231586813927,0.9987569451332092,-0.037373509258031845,-0.03480423241853714,0.9987569451332092,-0.03568301722407341,-0.03653961792588234,0.9987569451332092,-0.0339038111269474,-0.038184162229299545,0.9987569451332092,-0.0320403166115284,-0.039733774960041046,0.9987569451332092,-0.03009716607630253,-0.04118460416793823,0.9987569451332092,-0.028079187497496605,-0.042533040046691895,0.9987569451332092,-0.025991398841142654,-0.043775733560323715,0.9987569451332092,-0.023838993161916733,-0.04490959271788597,0.9987569451332092,-0.02162731997668743,-0.045931797474622726,0.9987569451332092,-0.01936187595129013,-0.04683981090784073,0.9987569451332092,-0.017048297449946404,-0.04763137176632881,0.9987569451332092,-0.014692332595586777,-0.048304516822099686,0.9987569451332092,-0.012299841269850731,-0.048857565969228745,0.9987569451332092,-0.009876769967377186,-0.049289148300886154,0.9987569451332092,-0.007429143879562616,-0.04959819093346596,0.9987569451332092,-0.004963047336786985,-0.04978392273187637,0.9987569451332092,-0.002484612399712205,-0.04984588548541069,0.9987569451332092,-6.104360482889217e-18,-0.04978392273187637,0.9987569451332092,0.002484612399712205,-0.04959819093346596,0.9987569451332092,0.004963047336786985,-0.049289148300886154,0.9987569451332092,0.007429143879562616,-0.048857565969228745,0.9987569451332092,0.009876769967377186,-0.048304516822099686,0.9987569451332092,0.012299841269850731,-0.04763137176632881,0.9987569451332092,0.014692332595586777,-0.04683981090784073,0.9987569451332092,0.017048297449946404,-0.045931797474622726,0.9987569451332092,0.01936187595129013,-0.04490959271788597,0.9987569451332092,0.02162731997668743,-0.043775733560323715,0.9987569451332092,0.023838993161916733,-0.042533040046691895,0.9987569451332092,0.025991398841142654,-0.04118460416793823,0.9987569451332092,0.028079187497496605,-0.039733774960041046,0.9987569451332092,0.03009716607630253,-0.038184162229299545,0.9987569451332092,0.0320403166115284,-0.03653961792588234,0.9987569451332092,0.0339038111269474,-0.03480423241853714,0.9987569451332092,0.03568301722407341,-0.03298231586813927,0.9987569451332092,0.037373509258031845,-0.03107840195298195,0.9987569451332092,0.038971081376075745,-0.029097219929099083,0.9987569451332092,0.040471769869327545,-0.027043698355555534,0.9987569451332092,0.04187183454632759,-0.024922942742705345,0.9987569451332092,0.043167803436517715,-0.02274022437632084,0.9987569451332092,0.04435644671320915,-0.020500969141721725,0.9987569451332092,0.04543481767177582,-0.018210746347904205,0.9987569451332092,0.04640022665262222,-0.01587524823844433,0.9987569451332092,0.047250278294086456,-0.013500283472239971,0.9987569451332092,0.04798285663127899,-0.011091752909123898,0.9987569451332092,0.04859614372253418,-0.008655646815896034,0.9987569451332092,0.04908861592411995,-0.006198022048920393,0.9987569451332092,0.04945904389023781,-0.003724987618625164,0.9987569451332092,0.049706507474184036,-0.0012426924658939242,0.9987569451332092,0.04983039200305939,0.0012426924658939242,0.9987569451332092,0.04983039200305939,0.003724987618625164,0.9987569451332092,0.049706507474184036,0.006198022048920393,0.9987569451332092,0.04945904389023781,0.008655646815896034,0.9987569451332092,0.04908861592411995,0.011091752909123898,0.9987569451332092,0.04859614372253418,0.013500283472239971,0.9987569451332092,0.04798285663127899,0.01587524823844433,0.9987569451332092,0.047250278294086456,0.018210746347904205,0.9987569451332092,0.04640022665262222,0.020500969141721725,0.9987569451332092,0.04543481767177582,0.02274022437632084,0.9987569451332092,0.04435644671320915,0.024922942742705345,0.9987569451332092,0.043167803436517715,0.027043698355555534,0.9987569451332092,0.04187183454632759,0.029097219929099083,0.9987569451332092,0.040471769869327545,0.03107840195298195,0.9987569451332092,0.038971081376075745,0.03298231586813927,0.9987569451332092,0.037373509258031845,0.03480423241853714,0.9987569451332092,0.03568301722407341,0.03653961792588234,0.9987569451332092,0.0339038111269474,0.038184162229299545,0.9987569451332092,0.0320403166115284,0.039733774960041046,0.9987569451332092,0.03009716607630253,0.04118460416793823,0.9987569451332092,0.028079187497496605,0.042533040046691895,0.9987569451332092,0.025991398841142654,0.043775733560323715,0.9987569451332092,0.023838993161916733,0.04490959271788597,0.9987569451332092,0.02162731997668743,0.045931797474622726,0.9987569451332092,0.01936187595129013,0.04683981090784073,0.9987569451332092,0.017048297449946404,0.04763137176632881,0.9987569451332092,0.014692332595586777,0.048304516822099686,0.9987569451332092,0.012299841269850731,0.048857565969228745,0.9987569451332092,0.009876769967377186,0.049289148300886154,0.9987569451332092,0.007429143879562616,0.04959819093346596,0.9987569451332092,0.004963047336786985,0.04978392273187637,0.9987569451332092,0.002484612399712205,0.04984588548541069,0.9987569451332092,1.2208720965778434e-17,6.123234262925839e-17,1.0,0.0,6.115622216056881e-17,1.0,-3.0521802414446084e-18,6.092806589529199e-17,1.0,-6.096772341539962e-18,6.054842308135465e-17,1.0,-9.126206770500587e-18,6.001825324826736e-17,1.0,-1.2132952048849189e-17,5.933886665012041e-17,1.0,-1.510953253179982e-17,5.85119573528083e-17,1.0,-1.804854826159258e-17,5.753957676425016e-17,1.0,-2.0942694406238013e-17,5.642414686927956e-17,1.0,-2.378477118568446e-17,5.5168430451142406e-17,1.0,-2.656771613186194e-17,5.3775560869999064e-17,1.0,-2.9284609051765835e-17,5.224899574081e-17,1.0,-3.192869849723649e-17,5.059253347694806e-17,1.0,-3.449340507368165e-17,4.881029012914131e-17,1.0,-3.697235618166222e-17,4.6906692768028125e-17,1.0,-3.9359390979975894e-17,4.4886482792879666e-17,1.0,-4.1648567003102095e-17,4.2754672918208004e-17,1.0,-4.383420317459381e-17,4.0516573643545727e-17,1.0,-4.5910856646020435e-17,3.817774031388674e-17,1.0,-4.7873372427804546e-17,3.574398966329852e-17,1.0,-4.9716863536887175e-17,3.322137665386496e-17,1.0,-5.143675401011968e-17,3.0616171314629196e-17,1.0,-5.302876236065149e-17,2.7934845506703856e-17,1.0,-5.4488934665154596e-17,2.5184071268909788e-17,1.0,-5.581363463765622e-17,2.2370686076190357e-17,1.0,-5.699958002548575e-17,1.9501682913444085e-17,1.0,-5.804380621332779e-17,1.658419538627363e-17,1.0,-5.894373239767118e-17,1.3625477868651082e-17,1.0,-5.969711526469467e-17,1.0632883996222037e-17,1.0,-6.030208207749146e-17,7.613855912957635e-18,1.0,-6.075713067606913e-17,4.57589862855557e-18,1.0,-6.106112285990482e-17,1.52656461220118e-18,1.0,-6.121331085772477e-17,-1.52656461220118e-18,1.0,-6.121331085772477e-17,-4.57589862855557e-18,1.0,-6.106112285990482e-17,-7.613855912957635e-18,1.0,-6.075713067606913e-17,-1.0632883996222037e-17,1.0,-6.030208207749146e-17,-1.3625477868651082e-17,1.0,-5.969711526469467e-17,-1.658419538627363e-17,1.0,-5.894373239767118e-17,-1.9501682913444085e-17,1.0,-5.804380621332779e-17,-2.2370686076190357e-17,1.0,-5.699958002548575e-17,-2.5184071268909788e-17,1.0,-5.581363463765622e-17,-2.7934845506703856e-17,1.0,-5.4488934665154596e-17,-3.0616171314629196e-17,1.0,-5.302876236065149e-17,-3.322137665386496e-17,1.0,-5.143675401011968e-17,-3.574398966329852e-17,1.0,-4.9716863536887175e-17,-3.817774031388674e-17,1.0,-4.7873372427804546e-17,-4.0516573643545727e-17,1.0,-4.5910856646020435e-17,-4.2754672918208004e-17,1.0,-4.383420317459381e-17,-4.4886482792879666e-17,1.0,-4.1648567003102095e-17,-4.6906692768028125e-17,1.0,-3.9359390979975894e-17,-4.881029012914131e-17,1.0,-3.697235618166222e-17,-5.059253347694806e-17,1.0,-3.449340507368165e-17,-5.224899574081e-17,1.0,-3.192869849723649e-17,-5.3775560869999064e-17,1.0,-2.9284609051765835e-17,-5.5168430451142406e-17,1.0,-2.656771613186194e-17,-5.642414686927956e-17,1.0,-2.378477118568446e-17,-5.753957676425016e-17,1.0,-2.0942694406238013e-17,-5.85119573528083e-17,1.0,-1.804854826159258e-17,-5.933886665012041e-17,1.0,-1.510953253179982e-17,-6.001825324826736e-17,1.0,-1.2132952048849189e-17,-6.054842308135465e-17,1.0,-9.126206770500587e-18,-6.092806589529199e-17,1.0,-6.096772341539962e-18,-6.115622216056881e-17,1.0,-3.0521802414446084e-18,-6.123234262925839e-17,1.0,-7.498798786105971e-33,-6.115622216056881e-17,1.0,3.0521802414446084e-18,-6.092806589529199e-17,1.0,6.096772341539962e-18,-6.054842308135465e-17,1.0,9.126206770500587e-18,-6.001825324826736e-17,1.0,1.2132952048849189e-17,-5.933886665012041e-17,1.0,1.510953253179982e-17,-5.85119573528083e-17,1.0,1.804854826159258e-17,-5.753957676425016e-17,1.0,2.0942694406238013e-17,-5.642414686927956e-17,1.0,2.378477118568446e-17,-5.5168430451142406e-17,1.0,2.656771613186194e-17,-5.3775560869999064e-17,1.0,2.9284609051765835e-17,-5.224899574081e-17,1.0,3.192869849723649e-17,-5.059253347694806e-17,1.0,3.449340507368165e-17,-4.881029012914131e-17,1.0,3.697235618166222e-17,-4.6906692768028125e-17,1.0,3.9359390979975894e-17,-4.4886482792879666e-17,1.0,4.1648567003102095e-17,-4.2754672918208004e-17,1.0,4.383420317459381e-17,-4.0516573643545727e-17,1.0,4.5910856646020435e-17,-3.817774031388674e-17,1.0,4.7873372427804546e-17,-3.574398966329852e-17,1.0,4.9716863536887175e-17,-3.322137665386496e-17,1.0,5.143675401011968e-17,-3.0616171314629196e-17,1.0,5.302876236065149e-17,-2.7934845506703856e-17,1.0,5.4488934665154596e-17,-2.5184071268909788e-17,1.0,5.581363463765622e-17,-2.2370686076190357e-17,1.0,5.699958002548575e-17,-1.9501682913444085e-17,1.0,5.804380621332779e-17,-1.658419538627363e-17,1.0,5.894373239767118e-17,-1.3625477868651082e-17,1.0,5.969711526469467e-17,-1.0632883996222037e-17,1.0,6.030208207749146e-17,-7.613855912957635e-18,1.0,6.075713067606913e-17,-4.57589862855557e-18,1.0,6.106112285990482e-17,-1.52656461220118e-18,1.0,6.121331085772477e-17,1.52656461220118e-18,1.0,6.121331085772477e-17,4.57589862855557e-18,1.0,6.106112285990482e-17,7.613855912957635e-18,1.0,6.075713067606913e-17,1.0632883996222037e-17,1.0,6.030208207749146e-17,1.3625477868651082e-17,1.0,5.969711526469467e-17,1.658419538627363e-17,1.0,5.894373239767118e-17,1.9501682913444085e-17,1.0,5.804380621332779e-17,2.2370686076190357e-17,1.0,5.699958002548575e-17,2.5184071268909788e-17,1.0,5.581363463765622e-17,2.7934845506703856e-17,1.0,5.4488934665154596e-17,3.0616171314629196e-17,1.0,5.302876236065149e-17,3.322137665386496e-17,1.0,5.143675401011968e-17,3.574398966329852e-17,1.0,4.9716863536887175e-17,3.817774031388674e-17,1.0,4.7873372427804546e-17,4.0516573643545727e-17,1.0,4.5910856646020435e-17,4.2754672918208004e-17,1.0,4.383420317459381e-17,4.4886482792879666e-17,1.0,4.1648567003102095e-17,4.6906692768028125e-17,1.0,3.9359390979975894e-17,4.881029012914131e-17,1.0,3.697235618166222e-17,5.059253347694806e-17,1.0,3.449340507368165e-17,5.224899574081e-17,1.0,3.192869849723649e-17,5.3775560869999064e-17,1.0,2.9284609051765835e-17,5.5168430451142406e-17,1.0,2.656771613186194e-17,5.642414686927956e-17,1.0,2.378477118568446e-17,5.753957676425016e-17,1.0,2.0942694406238013e-17,5.85119573528083e-17,1.0,1.804854826159258e-17,5.933886665012041e-17,1.0,1.510953253179982e-17,6.001825324826736e-17,1.0,1.2132952048849189e-17,6.054842308135465e-17,1.0,9.126206770500587e-18,6.092806589529199e-17,1.0,6.096772341539962e-18,6.115622216056881e-17,1.0,3.0521802414446084e-18,6.123234262925839e-17,1.0,1.4997597572211942e-32],"outlineEdgeIds":[],"outlinePositions":[],"positions":[0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0,-1.0,0.0,0.0498458856606972,-0.9987569212189223,0.0,0.049783923297908365,-0.9987569212189223,-0.002484612317299297,0.04959819025773868,-0.9987569212189223,-0.004963047496896916,0.04928914830179049,-0.9987569212189223,-0.0074291437584287886,0.04885756575705963,-0.9987569212189223,-0.009876769998025305,0.04830451560575335,-0.9987569212189223,-0.012299841031201563,0.047631372817687576,-0.9987569212189223,-0.01469233272158479,0.046839810931895286,-0.9987569212189223,-0.01704829695786655,0.045931797895944725,-0.9987569212189223,-0.019361876441744644,0.04490959117331178,-0.9987569212189223,-0.021627319250089573,0.04377573213097001,-0.9987569212189223,-0.02383899313513175,0.04253303972115194,-0.9987569212189223,-0.025991399527116845,0.041184603472989675,-0.9987569212189223,-0.02807918720461661,0.03973377581145868,-0.9987569212189223,-0.03009716559850844,0.038184163722721036,-0.9987569212189223,-0.032040317696548096,0.03653961978658958,-0.9987569212189223,-0.03390381251645262,0.03480423259840742,-0.9987569212189223,-0.03568301711648348,0.03298231660415571,-0.9987569212189223,-0.03737350811366968,0.031078401374061187,-0.9987569212189223,-0.0389710826810348,0.02909722034137084,-0.9987569212189223,-0.04047176899648709,0.027043699034291095,-0.9987569212189223,-0.041871836117394964,0.024922942830348592,-0.9987569212189223,-0.04316780325629825,0.022740224263617274,-0.9987569212189223,-0.04435644843469426,0.020500969916367847,-0.9987569212189223,-0.045434816493384,0.018210746927729313,-0.9987569212189223,-0.046400226439463586,0.01587524915290392,-0.9987569212189223,-0.047250278111694986,0.013500283007345938,-0.9987569212189223,-0.04798285814768504,0.011091753031098002,-0.9987569212189223,-0.048596145238037416,0.008655647209174246,-0.9987569212189223,-0.04908861465441465,0.006198022084486054,-0.9987569212189223,-0.04945904204025308,0.0037249877003221114,-0.9987569212189223,-0.04970650645470618,0.0012426924098180767,-0.9987569212189223,-0.0498303926622486,-0.0012426924098180706,-0.9987569212189223,-0.0498303926622486,-0.003724987700322105,-0.9987569212189223,-0.04970650645470618,-0.0061980220844860484,-0.9987569212189223,-0.04945904204025308,-0.008655647209174241,-0.9987569212189223,-0.04908861465441465,-0.011091753031097997,-0.9987569212189223,-0.048596145238037416,-0.01350028300734593,-0.9987569212189223,-0.04798285814768505,-0.015875249152903916,-0.9987569212189223,-0.047250278111694986,-0.018210746927729306,-0.9987569212189223,-0.04640022643946359,-0.020500969916367833,-0.9987569212189223,-0.04543481649338401,-0.02274022426361725,-0.9987569212189223,-0.044356448434694266,-0.02492294283034861,-0.9987569212189223,-0.04316780325629825,-0.027043699034291095,-0.9987569212189223,-0.04187183611739497,-0.029097220341370824,-0.9987569212189223,-0.0404717689964871,-0.03107840137406118,-0.9987569212189223,-0.0389710826810348,-0.032982316604155695,-0.9987569212189223,-0.03737350811366969,-0.03480423259840741,-0.9987569212189223,-0.03568301711648349,-0.03653961978658958,-0.9987569212189223,-0.03390381251645262,-0.03818416372272103,-0.9987569212189223,-0.0320403176965481,-0.03973377581145868,-0.9987569212189223,-0.03009716559850844,-0.041184603472989675,-0.9987569212189223,-0.028079187204616624,-0.04253303972115194,-0.9987569212189223,-0.025991399527116852,-0.04377573213097001,-0.9987569212189223,-0.023838993135131742,-0.04490959117331177,-0.9987569212189223,-0.02162731925008958,-0.04593179789594473,-0.9987569212189223,-0.019361876441744637,-0.04683981093189528,-0.9987569212189223,-0.017048296957866558,-0.047631372817687576,-0.9987569212189223,-0.014692332721584812,-0.04830451560575334,-0.9987569212189223,-0.012299841031201573,-0.04885756575705963,-0.9987569212189223,-0.009876769998025307,-0.04928914830179049,-0.9987569212189223,-0.007429143758428802,-0.04959819025773868,-0.9987569212189223,-0.004963047496896919,-0.049783923297908365,-0.9987569212189223,-0.002484612317299313,-0.0498458856606972,-0.9987569212189223,-6.104360432503777e-18,-0.049783923297908365,-0.9987569212189223,0.0024846123172993003,-0.04959819025773868,-0.9987569212189223,0.004963047496896907,-0.04928914830179049,-0.9987569212189223,0.00742914375842879,-0.048857565757059636,-0.9987569212189223,0.009876769998025295,-0.04830451560575335,-0.9987569212189223,0.012299841031201563,-0.04763137281768758,-0.9987569212189223,0.014692332721584779,-0.046839810931895286,-0.9987569212189223,0.017048296957866547,-0.045931797895944725,-0.9987569212189223,0.019361876441744648,-0.04490959117331178,-0.9987569212189223,0.02162731925008957,-0.04377573213097001,-0.9987569212189223,0.02383899313513173,-0.04253303972115194,-0.9987569212189223,0.02599139952711684,-0.041184603472989675,-0.9987569212189223,0.02807918720461661,-0.039733775811458684,-0.9987569212189223,0.03009716559850843,-0.038184163722721036,-0.9987569212189223,0.032040317696548096,-0.036539619786589594,-0.9987569212189223,0.033903812516452615,-0.034804232598407425,-0.9987569212189223,0.03568301711648347,-0.03298231660415572,-0.9987569212189223,0.037373508113669664,-0.031078401374061187,-0.9987569212189223,0.038971082681034795,-0.02909722034137087,-0.9987569212189223,0.04047176899648707,-0.027043699034291085,-0.9987569212189223,0.04187183611739497,-0.024922942830348578,-0.9987569212189223,0.04316780325629826,-0.02274022426361728,-0.9987569212189223,0.04435644843469425,-0.020500969916367844,-0.9987569212189223,0.045434816493384,-0.018210746927729348,-0.9987569212189223,0.04640022643946358,-0.015875249152903947,-0.9987569212189223,0.04725027811169497,-0.01350028300734591,-0.9987569212189223,0.047982858147685055,-0.011091753031098009,-0.9987569212189223,0.048596145238037416,-0.008655647209174243,-0.9987569212189223,0.04908861465441465,-0.006198022084486082,-0.9987569212189223,0.04945904204025307,-0.003724987700322128,-0.9987569212189223,0.04970650645470618,-0.0012426924098180828,-0.9987569212189223,0.0498303926622486,0.0012426924098180646,-0.9987569212189223,0.0498303926622486,0.0037249877003221097,-0.9987569212189223,0.04970650645470618,0.00619802208448602,-0.9987569212189223,0.04945904204025309,0.008655647209174224,-0.9987569212189223,0.04908861465441465,0.011091753031097992,-0.9987569212189223,0.048596145238037416,0.013500283007345935,-0.9987569212189223,0.04798285814768504,0.01587524915290393,-0.9987569212189223,0.04725027811169498,0.018210746927729293,-0.9987569212189223,0.0464002264394636,0.020500969916367826,-0.9987569212189223,0.04543481649338401,0.022740224263617263,-0.9987569212189223,0.044356448434694266,0.024922942830348606,-0.9987569212189223,0.04316780325629825,0.02704369903429111,-0.9987569212189223,0.04187183611739496,0.02909722034137082,-0.9987569212189223,0.0404717689964871,0.031078401374061176,-0.9987569212189223,0.0389710826810348,0.032982316604155675,-0.9987569212189223,0.037373508113669705,0.034804232598407425,-0.9987569212189223,0.03568301711648347,0.036539619786589594,-0.9987569212189223,0.033903812516452615,0.03818416372272102,-0.9987569212189223,0.03204031769654811,0.03973377581145867,-0.9987569212189223,0.030097165598508446,0.041184603472989655,-0.9987569212189223,0.028079187204616645,0.042533039721151945,-0.9987569212189223,0.02599139952711684,0.043775732130969996,-0.9987569212189223,0.023838993135131766,0.04490959117331177,-0.9987569212189223,0.021627319250089587,0.045931797895944725,-0.9987569212189223,0.019361876441744644,0.046839810931895265,-0.9987569212189223,0.017048296957866586,0.047631372817687576,-0.9987569212189223,0.014692332721584817,0.04830451560575334,-0.9987569212189223,0.012299841031201578,0.04885756575705963,-0.9987569212189223,0.009876769998025312,0.04928914830179049,-0.9987569212189223,0.007429143758428786,0.04959819025773867,-0.9987569212189223,0.004963047496896947,0.049783923297908365,-0.9987569212189223,0.0024846123172993185,0.0498458856606972,-0.9987569212189223,0.0,0.09956784659581666,-0.9950307753654014,0.0,0.0994440759184358,-0.9950307753654014,-0.0049630474968969124,0.09907307159969879,-0.9950307753654014,-0.00991375607572808,0.09845575601479824,-0.9950307753654014,-0.01483981749492221,0.09759366390754379,-0.9950307753654014,-0.01972898478963034,0.09648893857474715,-0.9950307753654014,-0.02456910271961008,0.09514432653764857,-0.9950307753654014,-0.029348137989068095,0.09356317071363225,-0.9950307753654014,-0.03405420916332941,0.091749402105207,-0.9950307753654014,-0.0386756162079561,0.08970753002691467,-0.9950307753654014,-0.04320086957687636,0.08744263089446366,-0.9950307753654014,-0.047618718777206394,0.08496033560395963,-0.9950307753654014,-0.05191818033974832,0.08226681553261056,-0.9950307753654014,-0.05608856512562525,0.07936876719571066,-0.9950307753654014,-0.06011950490116467,0.0762733955980482,-0.9950307753654014,-0.06400097811496101,0.07298839632112841,-0.9950307753654014,-0.06772333481303153,0.06952193639074525,-0.9950307753654014,-0.07127732063012225,0.06588263397246855,-0.9950307753654014,-0.07465409979751823,0.062079536945526516,-0.9950307753654014,-0.07784527711015672,0.05812210040835225,-0.9950307753654014,-0.08084291879842971,0.054020163171719406,-0.9950307753654014,-0.08363957225278529,0.04978392329790832,-0.9950307753654014,-0.08622828455208917,0.04542391274671642,-0.9950307753654014,-0.0886026197496822,0.04095097119134657,-0.9950307753654014,-0.09075667487415778,0.036376219069271735,-0.9950307753654014,-0.09268509460507893,0.03171102993507523,-0.9950307753654014,-0.09438308458714857,0.026967002184001908,-0.9950307753654014,-0.09584642334973233,0.022155930216520167,-0.9950307753654014,-0.09707147280209963,0.017289775115584043,-0.9950307753654014,-0.09805518727829043,0.01238063490949635,-0.9950307753654014,-0.09879512110912077,0.0074407144943041265,-0.9950307753654014,-0.09928943470250161,0.00248229529050405,-0.9950307753654014,-0.09953689911695471,-0.0024822952905040377,-0.9950307753654014,-0.09953689911695471,-0.007440714494304114,-0.9950307753654014,-0.09928943470250161,-0.012380634909496337,-0.9950307753654014,-0.09879512110912077,-0.017289775115584032,-0.9950307753654014,-0.09805518727829043,-0.022155930216520157,-0.9950307753654014,-0.09707147280209963,-0.02696700218400189,-0.9950307753654014,-0.09584642334973234,-0.03171102993507522,-0.9950307753654014,-0.09438308458714857,-0.03637621906927172,-0.9950307753654014,-0.09268509460507894,-0.040950971191346545,-0.9950307753654014,-0.0907566748741578,-0.045423912746716366,-0.9950307753654014,-0.08860261974968221,-0.04978392329790835,-0.9950307753654014,-0.08622828455208917,-0.054020163171719406,-0.9950307753654014,-0.08363957225278529,-0.058122100408352216,-0.9950307753654014,-0.08084291879842972,-0.0620795369455265,-0.9950307753654014,-0.07784527711015672,-0.06588263397246853,-0.9950307753654014,-0.07465409979751826,-0.06952193639074523,-0.9950307753654014,-0.07127732063012227,-0.07298839632112841,-0.9950307753654014,-0.06772333481303153,-0.07627339559804819,-0.9950307753654014,-0.06400097811496104,-0.07936876719571066,-0.9950307753654014,-0.06011950490116467,-0.08226681553261056,-0.9950307753654014,-0.05608856512562528,-0.08496033560395962,-0.9950307753654014,-0.05191818033974833,-0.08744263089446368,-0.9950307753654014,-0.04761871877720638,-0.08970753002691466,-0.9950307753654014,-0.043200869576876376,-0.091749402105207,-0.9950307753654014,-0.03867561620795609,-0.09356317071363224,-0.9950307753654014,-0.03405420916332943,-0.09514432653764855,-0.9950307753654014,-0.029348137989068133,-0.09648893857474714,-0.9950307753654014,-0.024569102719610103,-0.09759366390754379,-0.9950307753654014,-0.019728984789630342,-0.09845575601479824,-0.9950307753654014,-0.014839817494922239,-0.09907307159969879,-0.9950307753654014,-0.009913756075728086,-0.0994440759184358,-0.9950307753654014,-0.0049630474968969445,-0.09956784659581666,-0.9950307753654014,-1.2193544463156156e-17,-0.0994440759184358,-0.9950307753654014,0.004963047496896919,-0.09907307159969879,-0.9950307753654014,0.009913756075728062,-0.09845575601479824,-0.9950307753654014,0.014839817494922214,-0.09759366390754379,-0.9950307753654014,0.019728984789630317,-0.09648893857474715,-0.9950307753654014,0.02456910271961008,-0.09514432653764858,-0.9950307753654014,0.029348137989068067,-0.09356317071363225,-0.9950307753654014,0.03405420916332941,-0.091749402105207,-0.9950307753654014,0.03867561620795611,-0.08970753002691467,-0.9950307753654014,0.043200869576876355,-0.08744263089446368,-0.9950307753654014,0.04761871877720636,-0.08496033560395963,-0.9950307753654014,0.05191818033974831,-0.08226681553261056,-0.9950307753654014,0.05608856512562525,-0.07936876719571068,-0.9950307753654014,0.06011950490116465,-0.0762733955980482,-0.9950307753654014,0.06400097811496101,-0.07298839632112843,-0.9950307753654014,0.06772333481303151,-0.06952193639074526,-0.9950307753654014,0.07127732063012225,-0.06588263397246859,-0.9950307753654014,0.07465409979751822,-0.062079536945526516,-0.9950307753654014,0.07784527711015671,-0.058122100408352306,-0.9950307753654014,0.08084291879842967,-0.054020163171719385,-0.9950307753654014,0.08363957225278529,-0.04978392329790829,-0.9950307753654014,0.08622828455208918,-0.04542391274671643,-0.9950307753654014,0.08860261974968218,-0.040950971191346566,-0.9950307753654014,0.09075667487415778,-0.036376219069271805,-0.9950307753654014,0.0926850946050789,-0.03171102993507528,-0.9950307753654014,0.09438308458714856,-0.026967002184001852,-0.9950307753654014,0.09584642334973235,-0.02215593021652018,-0.9950307753654014,0.09707147280209963,-0.017289775115584036,-0.9950307753654014,0.09805518727829043,-0.012380634909496405,-0.9950307753654014,0.09879512110912075,-0.007440714494304159,-0.9950307753654014,0.09928943470250161,-0.002482295290504062,-0.9950307753654014,0.09953689911695471,0.002482295290504025,-0.9950307753654014,0.09953689911695471,0.007440714494304124,-0.9950307753654014,0.09928943470250161,0.012380634909496282,-0.9950307753654014,0.09879512110912078,0.017289775115584,-0.9950307753654014,0.09805518727829045,0.022155930216520146,-0.9950307753654014,0.09707147280209963,0.0269670021840019,-0.9950307753654014,0.09584642334973233,0.03171102993507525,-0.9950307753654014,0.09438308458714857,0.03637621906927169,-0.9950307753654014,0.09268509460507895,0.04095097119134653,-0.9950307753654014,0.0907566748741578,0.045423912746716394,-0.9950307753654014,0.0886026197496822,0.049783923297908345,-0.9950307753654014,0.08622828455208917,0.054020163171719426,-0.9950307753654014,0.08363957225278527,0.0581221004083522,-0.9950307753654014,0.08084291879842974,0.06207953694552649,-0.9950307753654014,0.07784527711015672,0.06588263397246849,-0.9950307753654014,0.07465409979751829,0.06952193639074526,-0.9950307753654014,0.07127732063012225,0.07298839632112843,-0.9950307753654014,0.06772333481303151,0.07627339559804819,-0.9950307753654014,0.06400097811496105,0.07936876719571065,-0.9950307753654014,0.06011950490116468,0.08226681553261052,-0.9950307753654014,0.05608856512562532,0.08496033560395964,-0.9950307753654014,0.05191818033974831,0.08744263089446364,-0.9950307753654014,0.04761871877720643,0.08970753002691466,-0.9950307753654014,0.04320086957687638,0.091749402105207,-0.9950307753654014,0.0386756162079561,0.09356317071363221,-0.9950307753654014,0.034054209163329484,0.09514432653764855,-0.9950307753654014,0.029348137989068143,0.09648893857474714,-0.9950307753654014,0.024569102719610113,0.09759366390754379,-0.9950307753654014,0.019728984789630352,0.09845575601479824,-0.9950307753654014,0.014839817494922206,0.09907307159969879,-0.9950307753654014,0.009913756075728142,0.0994440759184358,-0.9950307753654014,0.004963047496896956,0.09956784659581666,-0.9950307753654014,0.0,0.14904226617617444,-0.9888308262251285,0.0,0.14885699489760712,-0.9888308262251285,-0.007429143758428783,0.1483016416754954,-0.9888308262251285,-0.01483981749492221,0.1473775872054521,-0.9888308262251285,-0.022213597106929633,0.14608712883248579,-0.9888308262251285,-0.029532150216506993,0.14443347483943902,-0.9888308262251285,-0.03677728174749687,0.1424207364706918,-0.9888308262251285,-0.04393097916135471,0.14005391771096032,-0.9888308262251285,-0.05097545723915765,0.1373389028446022,-0.9888308262251285,-0.05789320229846115,0.1342824418263589,-0.9888308262251285,-0.06466701573507293,0.1308921334999043,-0.9888308262251285,-0.07128005678149295,0.1271764067059223,-0.9888308262251285,-0.07771588437571479,0.12314449932668063,-0.9888308262251285,-0.0839584980362964,0.11880643531920011,-0.9888308262251285,-0.08999237764207785,0.11417299979411805,-0.9888308262251285,-0.0958025220176481,0.10925571220220388,-0.9888308262251285,-0.10137448622863066,0.10406679769518956,-0.9888308262251285,-0.1066944174940663,0.09861915673211605,-0.9888308262251285,-0.11174908962660932,0.09292633300675963,-0.9888308262251285,-0.11652593591491316,0.0870024797758751,-0.9888308262251285,-0.12101308036645493,0.0808623246719695,-0.9888308262251285,-0.12519936723312391,0.07452113308808721,-0.9888308262251285,-0.12907438874616925,0.06799467022563764,-0.9888308262251285,-0.1326285109915527,0.061299161899620334,-0.9888308262251285,-0.13585289786137714,0.05445125419869248,-0.9888308262251285,-0.1387395330218428,0.047467972100369724,-0.9888308262251285,-0.1412812398431163,0.04036667714424947,-0.9888308262251285,-0.14347169924156317,0.033165024268487955,-0.9888308262251285,-0.14530546538998537,0.025880917916842272,-0.9888308262251285,-0.14677797925680577,0.018532467525402118,-0.9888308262251285,-0.147885579940539,0.011137942499678291,-0.9888308262251285,-0.14862551377136932,0.003715726793982034,-0.9888308262251285,-0.14899594115720774,-0.0037157267939820163,-0.9888308262251285,-0.14899594115720774,-0.011137942499678272,-0.9888308262251285,-0.14862551377136932,-0.018532467525402104,-0.9888308262251285,-0.147885579940539,-0.025880917916842258,-0.9888308262251285,-0.14677797925680577,-0.033165024268487935,-0.9888308262251285,-0.14530546538998537,-0.04036667714424944,-0.9888308262251285,-0.1434716992415632,-0.04746797210036971,-0.9888308262251285,-0.1412812398431163,-0.054451254198692456,-0.9888308262251285,-0.1387395330218428,-0.06129916189962029,-0.9888308262251285,-0.13585289786137716,-0.06799467022563757,-0.9888308262251285,-0.13262851099155273,-0.07452113308808725,-0.9888308262251285,-0.12907438874616925,-0.0808623246719695,-0.9888308262251285,-0.12519936723312394,-0.08700247977587505,-0.9888308262251285,-0.12101308036645494,-0.09292633300675962,-0.9888308262251285,-0.11652593591491317,-0.09861915673211602,-0.9888308262251285,-0.11174908962660934,-0.10406679769518955,-0.9888308262251285,-0.10669441749406632,-0.10925571220220388,-0.9888308262251285,-0.10137448622863066,-0.11417299979411803,-0.9888308262251285,-0.09580252201764815,-0.11880643531920011,-0.9888308262251285,-0.08999237764207785,-0.12314449932668062,-0.9888308262251285,-0.08395849803629643,-0.12717640670592228,-0.9888308262251285,-0.07771588437571482,-0.13089213349990433,-0.9888308262251285,-0.07128005678149293,-0.1342824418263589,-0.9888308262251285,-0.06466701573507294,-0.13733890284460223,-0.9888308262251285,-0.05789320229846113,-0.1400539177109603,-0.9888308262251285,-0.050975457239157675,-0.1424207364706918,-0.9888308262251285,-0.043930979161354765,-0.144433474839439,-0.9888308262251285,-0.03677728174749691,-0.14608712883248579,-0.9888308262251285,-0.029532150216506996,-0.1473775872054521,-0.9888308262251285,-0.022213597106929675,-0.1483016416754954,-0.9888308262251285,-0.014839817494922221,-0.14885699489760712,-0.9888308262251285,-0.007429143758428831,-0.14904226617617444,-0.9888308262251285,-1.8252413421031987e-17,-0.14885699489760712,-0.9888308262251285,0.007429143758428794,-0.1483016416754954,-0.9888308262251285,0.014839817494922185,-0.1473775872054521,-0.9888308262251285,0.022213597106929637,-0.14608712883248579,-0.9888308262251285,0.029532150216506958,-0.14443347483943902,-0.9888308262251285,0.03677728174749687,-0.14242073647069184,-0.9888308262251285,0.04393097916135467,-0.14005391771096032,-0.9888308262251285,0.05097545723915764,-0.1373389028446022,-0.9888308262251285,0.057893202298461156,-0.1342824418263589,-0.9888308262251285,0.06466701573507291,-0.13089213349990433,-0.9888308262251285,0.07128005678149289,-0.1271764067059223,-0.9888308262251285,0.07771588437571478,-0.12314449932668063,-0.9888308262251285,0.0839584980362964,-0.11880643531920013,-0.9888308262251285,0.08999237764207782,-0.11417299979411805,-0.9888308262251285,0.0958025220176481,-0.10925571220220393,-0.9888308262251285,0.10137448622863063,-0.10406679769518958,-0.9888308262251285,0.10669441749406629,-0.0986191567321161,-0.9888308262251285,0.11174908962660929,-0.09292633300675963,-0.9888308262251285,0.11652593591491314,-0.08700247977587518,-0.9888308262251285,0.12101308036645486,-0.08086232467196947,-0.9888308262251285,0.12519936723312394,-0.07452113308808717,-0.9888308262251285,0.12907438874616928,-0.06799467022563767,-0.9888308262251285,0.1326285109915527,-0.06129916189962033,-0.9888308262251285,0.13585289786137714,-0.05445125419869258,-0.9888308262251285,0.13873953302184275,-0.04746797210036981,-0.9888308262251285,0.14128123984311627,-0.04036667714424939,-0.9888308262251285,0.1434716992415632,-0.03316502426848797,-0.9888308262251285,0.14530546538998537,-0.02588091791684226,-0.9888308262251285,0.14677797925680577,-0.018532467525402205,-0.9888308262251285,0.14788557994053897,-0.01113794249967834,-0.9888308262251285,0.14862551377136932,-0.0037157267939820523,-0.9888308262251285,0.14899594115720774,0.0037157267939819977,-0.9888308262251285,0.14899594115720774,0.011137942499678286,-0.9888308262251285,0.14862551377136932,0.018532467525402017,-0.9888308262251285,0.147885579940539,0.025880917916842206,-0.9888308262251285,0.14677797925680577,0.03316502426848792,-0.9888308262251285,0.14530546538998537,0.04036667714424946,-0.9888308262251285,0.14347169924156317,0.04746797210036975,-0.9888308262251285,0.1412812398431163,0.05445125419869241,-0.9888308262251285,0.13873953302184283,0.06129916189962028,-0.9888308262251285,0.13585289786137716,0.06799467022563761,-0.9888308262251285,0.13262851099155273,0.07452113308808724,-0.9888308262251285,0.12907438874616925,0.08086232467196952,-0.9888308262251285,0.12519936723312391,0.08700247977587502,-0.9888308262251285,0.12101308036645496,0.0929263330067596,-0.9888308262251285,0.11652593591491317,0.09861915673211595,-0.9888308262251285,0.1117490896266094,0.10406679769518958,-0.9888308262251285,0.10669441749406629,0.10925571220220393,-0.9888308262251285,0.10137448622863063,0.114172999794118,-0.9888308262251285,0.09580252201764816,0.1188064353192001,-0.9888308262251285,0.08999237764207786,0.12314449932668058,-0.9888308262251285,0.0839584980362965,0.12717640670592234,-0.9888308262251285,0.07771588437571478,0.13089213349990428,-0.9888308262251285,0.071280056781493,0.1342824418263589,-0.9888308262251285,0.06466701573507296,0.1373389028446022,-0.9888308262251285,0.05789320229846115,0.14005391771096026,-0.9888308262251285,0.05097545723915776,0.1424207364706918,-0.9888308262251285,0.04393097916135478,0.144433474839439,-0.9888308262251285,0.03677728174749692,0.14608712883248579,-0.9888308262251285,0.02953215021650701,0.1473775872054521,-0.9888308262251285,0.022213597106929623,0.1483016416754954,-0.9888308262251285,0.014839817494922304,0.14885699489760712,-0.9888308262251285,0.007429143758428848,0.14904226617617444,-0.9888308262251285,0.0,0.19814614319939755,-0.9801724878485438,0.0,0.19789983193323402,-0.9801724878485438,-0.009876769998025297,0.1971615105033604,-0.9801724878485438,-0.019728984789630335,0.1959330144931829,-0.9801724878485438,-0.029532150216506986,0.1942173981373473,-0.9801724878485438,-0.03926189406479617,0.19201892672843046,-0.9801724878485438,-0.048894026658251606,0.18934306601275072,-0.9801724878485438,-0.058404600997586424,0.18619646860166178,-0.9801724878485438,-0.06776997229648643,0.18258695743211217,-0.9801724878485438,-0.07696685676627447,0.17852350631759184,-0.9801724878485438,-0.08597238950307771,0.17401621763781752,-0.9801724878485438,-0.09476418133358133,0.16907629722262532,-0.9801724878485438,-0.103320374478041,0.16371602649251185,-0.9801724878485438,-0.1116196968921674,0.15794873192508802,-0.9801724878485438,-0.11964151515277983,0.15178875192335578,-0.9801724878485438,-0.12736588575574748,0.1452514011681792,-0.9801724878485438,-0.13477360469868285,0.1383529325435747,-0.9801724878485438,-0.1418462552251177,0.1311104967294806,-0.9801724878485438,-0.14856625361146122,0.12354209956246463,-0.9801724878485438,-0.1549168928829075,0.11566655727037688,-0.9801724878485438,-0.16088238434960736,0.1075034496922429,-0.9801724878485438,-0.16644789685983888,0.09907307159969875,-0.9801724878485438,-0.17159959367258748,0.09039638224099114,-0.9801724878485438,-0.17632466685786416,0.08149495323298357,-0.9801724878485438,-0.1806113691392377,0.0723909149307183,-0.9801724878485438,-0.18444904309941448,0.0631069014078667,-0.9801724878485438,-0.18782814767625738,0.05366599418485577,-0.9801724878485438,-0.1907402818833693,0.04409166484457157,-0.9801724878485438,-0.1931782056962693,0.03440771667830602,-0.9801724878485438,-0.1951358580522339,0.02463822550702421,-0.9801724878485438,-0.1966083719190543,0.014807479825080029,-0.9801724878485438,-0.19759208639524511,0.004939920415192257,-0.9801724878485438,-0.19808455581162235,-0.004939920415192233,-0.9801724878485438,-0.19808455581162235,-0.014807479825080003,-0.9801724878485438,-0.19759208639524511,-0.02463822550702419,-0.9801724878485438,-0.1966083719190543,-0.034407716678306,-0.9801724878485438,-0.1951358580522339,-0.04409166484457155,-0.9801724878485438,-0.1931782056962693,-0.053665994184855736,-0.9801724878485438,-0.19074028188336933,-0.06310690140786669,-0.9801724878485438,-0.18782814767625738,-0.07239091493071827,-0.9801724878485438,-0.1844490430994145,-0.08149495323298352,-0.9801724878485438,-0.18061136913923773,-0.09039638224099104,-0.9801724878485438,-0.17632466685786422,-0.09907307159969882,-0.9801724878485438,-0.17159959367258745,-0.1075034496922429,-0.9801724878485438,-0.1664478968598389,-0.11566655727037681,-0.9801724878485438,-0.1608823843496074,-0.1235420995624646,-0.9801724878485438,-0.15491689288290753,-0.13111049672948058,-0.9801724878485438,-0.14856625361146128,-0.13835293254357467,-0.9801724878485438,-0.14184625522511773,-0.1452514011681792,-0.9801724878485438,-0.13477360469868285,-0.15178875192335575,-0.9801724878485438,-0.1273658857557475,-0.15794873192508802,-0.9801724878485438,-0.11964151515277983,-0.16371602649251182,-0.9801724878485438,-0.11161969689216744,-0.1690762972226253,-0.9801724878485438,-0.10332037447804102,-0.17401621763781755,-0.9801724878485438,-0.0947641813335813,-0.1785235063175918,-0.9801724878485438,-0.08597238950307774,-0.1825869574321122,-0.9801724878485438,-0.07696685676627446,-0.18619646860166178,-0.9801724878485438,-0.06776997229648646,-0.18934306601275072,-0.9801724878485438,-0.0584046009975865,-0.19201892672843043,-0.9801724878485438,-0.048894026658251655,-0.1942173981373473,-0.9801724878485438,-0.039261894064796174,-0.1959330144931829,-0.9801724878485438,-0.02953215021650704,-0.1971615105033604,-0.9801724878485438,-0.01972898478963035,-0.19789983193323402,-0.9801724878485438,-0.00987676999802536,-0.19814614319939755,-0.9801724878485438,-2.426590400325353e-17,-0.19789983193323402,-0.9801724878485438,0.00987676999802531,-0.1971615105033604,-0.9801724878485438,0.0197289847896303,-0.1959330144931829,-0.9801724878485438,0.029532150216506993,-0.19421739813734734,-0.9801724878485438,0.03926189406479612,-0.19201892672843046,-0.9801724878485438,0.048894026658251606,-0.18934306601275075,-0.9801724878485438,0.05840460099758637,-0.18619646860166178,-0.9801724878485438,0.06776997229648642,-0.18258695743211217,-0.9801724878485438,0.07696685676627449,-0.17852350631759184,-0.9801724878485438,0.0859723895030777,-0.17401621763781755,-0.9801724878485438,0.09476418133358126,-0.16907629722262532,-0.9801724878485438,0.10332037447804097,-0.16371602649251185,-0.9801724878485438,0.1116196968921674,-0.15794873192508802,-0.9801724878485438,0.11964151515277979,-0.15178875192335578,-0.9801724878485438,0.12736588575574748,-0.14525140116817922,-0.9801724878485438,0.13477360469868283,-0.13835293254357472,-0.9801724878485438,0.14184625522511768,-0.1311104967294807,-0.9801724878485438,0.14856625361146117,-0.12354209956246463,-0.9801724878485438,0.1549168928829075,-0.11566655727037699,-0.9801724878485438,0.16088238434960728,-0.10750344969224286,-0.9801724878485438,0.1664478968598389,-0.09907307159969869,-0.9801724878485438,0.1715995936725875,-0.09039638224099115,-0.9801724878485438,0.17632466685786413,-0.08149495323298356,-0.9801724878485438,0.1806113691392377,-0.07239091493071843,-0.9801724878485438,0.18444904309941446,-0.06310690140786682,-0.9801724878485438,0.18782814767625736,-0.05366599418485566,-0.9801724878485438,0.19074028188336936,-0.044091664844571596,-0.9801724878485438,0.1931782056962693,-0.034407716678306006,-0.9801724878485438,0.1951358580522339,-0.024638225507024325,-0.9801724878485438,0.19660837191905428,-0.014807479825080095,-0.9801724878485438,0.19759208639524511,-0.004939920415192281,-0.9801724878485438,0.19808455581162235,0.004939920415192208,-0.9801724878485438,0.19808455581162235,0.014807479825080024,-0.9801724878485438,0.19759208639524511,0.024638225507024075,-0.9801724878485438,0.19660837191905434,0.03440771667830593,-0.9801724878485438,0.19513585805223393,0.044091664844571526,-0.9801724878485438,0.1931782056962693,0.05366599418485576,-0.9801724878485438,0.1907402818833693,0.06310690140786675,-0.9801724878485438,0.18782814767625738,0.0723909149307182,-0.9801724878485438,0.18444904309941454,0.08149495323298349,-0.9801724878485438,0.18061136913923773,0.0903963822409911,-0.9801724878485438,0.1763246668578642,0.0990730715996988,-0.9801724878485438,0.17159959367258745,0.10750344969224294,-0.9801724878485438,0.16644789685983885,0.1156665572703768,-0.9801724878485438,0.16088238434960742,0.12354209956246459,-0.9801724878485438,0.15491689288290753,0.13111049672948047,-0.9801724878485438,0.14856625361146134,0.13835293254357472,-0.9801724878485438,0.14184625522511768,0.14525140116817922,-0.9801724878485438,0.13477360469868283,0.15178875192335572,-0.9801724878485438,0.12736588575574753,0.157948731925088,-0.9801724878485438,0.11964151515277986,0.16371602649251174,-0.9801724878485438,0.11161969689216754,0.16907629722262532,-0.9801724878485438,0.10332037447804097,0.1740162176378175,-0.9801724878485438,0.0947641813335814,0.1785235063175918,-0.9801724878485438,0.08597238950307777,0.18258695743211217,-0.9801724878485438,0.07696685676627447,0.18619646860166172,-0.9801724878485438,0.06776997229648657,0.18934306601275072,-0.9801724878485438,0.05840460099758653,0.19201892672843043,-0.9801724878485438,0.048894026658251676,0.1942173981373473,-0.9801724878485438,0.039261894064796195,0.1959330144931829,-0.9801724878485438,0.029532150216506976,0.1971615105033604,-0.9801724878485438,0.01972898478963046,0.19789983193323402,-0.9801724878485438,0.009876769998025383,0.19814614319939755,-0.9801724878485438,0.0,0.24675739769029378,-0.969077286229078,0.0,0.24645065880515105,-0.969077286229078,-0.012299841031201563,0.24553120475092174,-0.969077286229078,-0.024569102719610096,0.24400132143525582,-0.969077286229078,-0.0367772817474969,0.24186481238912777,-0.969077286229078,-0.04889402665825165,0.23912698931065926,-0.969077286229078,-0.06088921331488576,0.23579465885940057,-0.969077286229078,-0.0727330197933834,0.23187610573390283,-0.969077286229078,-0.08439600052470331,0.22738107207465158,-0.969077286229078,-0.09584915950110309,0.22232073324357102,-0.969077286229078,-0.10706402236478296,0.216707670040313,-0.969077286229078,-0.11801270719962588,0.21055583742440726,-0.969077286229078,-0.128667993850034,0.20388052982103286,-0.969077286229078,-0.13900339159452457,0.19669834309666767,-0.969077286229078,-0.14899320500583715,0.1890271332991493,-0.969077286229078,-0.1586125978338147,0.18088597226472672,-0.969077286229078,-0.16783765475223467,0.17229510020247038,-0.969077286229078,-0.17664544081607794,0.16327587537392338,-0.969077286229078,-0.18501405848141608,0.153850720993098,-0.969077286229078,-0.19292270204615558,0.14404306947883258,-0.969077286229078,-0.20035170937629163,0.13387730419810626,-0.969077286229078,-0.20728261078907106,0.12337869884514686,-0.969077286229078,-0.213698174971534,0.1125733546070448,-0.969077286229078,-0.2195824518202726,0.10148813527208922,-0.969077286229078,-0.22492081209590173,0.09015060044215784,-0.969077286229078,-0.22969998379365247,0.0785889370152044,-0.969077286229078,-0.23390808513966774,0.06683188910818887,-0.969077286229078,-0.2375346541309637,0.054908686594673885,-0.969077286229078,-0.24057067454561806,0.04284897243475354,-0.969077286229078,-0.24300859835851804,0.030682728977983954,-0.969077286229078,-0.24484236450694025,0.018440203422538198,-0.969077286229078,-0.24606741395930753,0.006151832615905809,-0.969077286229078,-0.2466807010496599,-0.00615183261590578,-0.969077286229078,-0.2466807010496599,-0.018440203422538166,-0.969077286229078,-0.24606741395930753,-0.030682728977983926,-0.969077286229078,-0.24484236450694025,-0.04284897243475351,-0.969077286229078,-0.24300859835851804,-0.05490868659467386,-0.969077286229078,-0.24057067454561806,-0.06683188910818882,-0.969077286229078,-0.23753465413096372,-0.07858893701520438,-0.969077286229078,-0.23390808513966774,-0.09015060044215781,-0.969077286229078,-0.2296999837936525,-0.10148813527208915,-0.969077286229078,-0.22492081209590176,-0.11257335460704469,-0.969077286229078,-0.21958245182027267,-0.12337869884514695,-0.969077286229078,-0.21369817497153398,-0.13387730419810626,-0.969077286229078,-0.2072826107890711,-0.1440430694788325,-0.969077286229078,-0.20035170937629165,-0.15385072099309796,-0.969077286229078,-0.1929227020461556,-0.16327587537392332,-0.969077286229078,-0.18501405848141614,-0.17229510020247038,-0.969077286229078,-0.17664544081607797,-0.18088597226472672,-0.969077286229078,-0.16783765475223467,-0.18902713329914927,-0.969077286229078,-0.15861259783381476,-0.19669834309666767,-0.969077286229078,-0.14899320500583715,-0.20388052982103283,-0.969077286229078,-0.13900339159452463,-0.21055583742440723,-0.969077286229078,-0.12866799385003402,-0.21670767004031302,-0.969077286229078,-0.11801270719962585,-0.222320733243571,-0.969077286229078,-0.10706402236478299,-0.2273810720746516,-0.969077286229078,-0.09584915950110307,-0.2318761057339028,-0.969077286229078,-0.08439600052470335,-0.23579465885940054,-0.969077286229078,-0.0727330197933835,-0.23912698931065923,-0.969077286229078,-0.060889213314885816,-0.24186481238912777,-0.969077286229078,-0.048894026658251655,-0.24400132143525582,-0.969077286229078,-0.03677728174749697,-0.24553120475092174,-0.969077286229078,-0.024569102719610113,-0.24645065880515105,-0.969077286229078,-0.01229984103120164,-0.24675739769029378,-0.969077286229078,-3.021906572473488e-17,-0.24645065880515105,-0.969077286229078,0.01229984103120158,-0.24553120475092174,-0.969077286229078,0.02456910271961005,-0.24400132143525582,-0.969077286229078,0.03677728174749691,-0.2418648123891278,-0.969077286229078,0.04889402665825159,-0.23912698931065926,-0.969077286229078,0.06088921331488576,-0.2357946588594006,-0.969077286229078,0.07273301979338333,-0.23187610573390283,-0.969077286229078,0.0843960005247033,-0.22738107207465158,-0.969077286229078,0.0958491595011031,-0.22232073324357102,-0.969077286229078,0.10706402236478293,-0.21670767004031302,-0.969077286229078,0.1180127071996258,-0.21055583742440726,-0.969077286229078,0.128667993850034,-0.20388052982103286,-0.969077286229078,0.13900339159452457,-0.1966983430966677,-0.969077286229078,0.1489932050058371,-0.1890271332991493,-0.969077286229078,0.1586125978338147,-0.18088597226472677,-0.969077286229078,0.1678376547522346,-0.1722951002024704,-0.969077286229078,0.17664544081607791,-0.16327587537392346,-0.969077286229078,0.18501405848141603,-0.153850720993098,-0.969077286229078,0.19292270204615555,-0.14404306947883272,-0.969077286229078,0.20035170937629151,-0.13387730419810623,-0.969077286229078,0.2072826107890711,-0.1233786988451468,-0.969077286229078,0.21369817497153404,-0.11257335460704483,-0.969077286229078,0.21958245182027258,-0.1014881352720892,-0.969077286229078,0.22492081209590173,-0.09015060044215802,-0.969077286229078,0.22969998379365242,-0.07858893701520454,-0.969077286229078,0.23390808513966768,-0.06683188910818873,-0.969077286229078,0.23753465413096375,-0.05490868659467392,-0.969077286229078,0.24057067454561806,-0.04284897243475352,-0.969077286229078,0.24300859835851804,-0.030682728977984096,-0.969077286229078,0.24484236450694022,-0.018440203422538277,-0.969077286229078,0.24606741395930753,-0.00615183261590584,-0.969077286229078,0.2466807010496599,0.006151832615905749,-0.969077286229078,0.2466807010496599,0.01844020342253819,-0.969077286229078,0.24606741395930753,0.030682728977983787,-0.969077286229078,0.24484236450694027,0.04284897243475343,-0.969077286229078,0.24300859835851807,0.05490868659467383,-0.969077286229078,0.24057067454561806,0.06683188910818885,-0.969077286229078,0.2375346541309637,0.07858893701520445,-0.969077286229078,0.2339080851396677,0.09015060044215772,-0.969077286229078,0.22969998379365253,0.10148813527208912,-0.969077286229078,0.22492081209590176,0.11257335460704476,-0.969077286229078,0.21958245182027264,0.12337869884514692,-0.969077286229078,0.21369817497153398,0.1338773041981063,-0.969077286229078,0.20728261078907104,0.14404306947883247,-0.969077286229078,0.20035170937629168,0.15385072099309793,-0.969077286229078,0.1929227020461556,0.1632758753739232,-0.969077286229078,0.18501405848141622,0.1722951002024704,-0.969077286229078,0.17664544081607791,0.18088597226472677,-0.969077286229078,0.1678376547522346,0.18902713329914925,-0.969077286229078,0.1586125978338148,0.19669834309666764,-0.969077286229078,0.14899320500583718,0.20388052982103275,-0.969077286229078,0.13900339159452474,0.21055583742440728,-0.969077286229078,0.128667993850034,0.21670767004031294,-0.969077286229078,0.11801270719962596,0.222320733243571,-0.969077286229078,0.10706402236478302,0.22738107207465158,-0.969077286229078,0.09584915950110309,0.23187610573390274,-0.969077286229078,0.08439600052470349,0.23579465885940054,-0.969077286229078,0.07273301979338352,0.23912698931065923,-0.969077286229078,0.060889213314885844,0.24186481238912777,-0.969077286229078,0.04889402665825168,0.24400132143525582,-0.969077286229078,0.036777281747496886,0.24553120475092172,-0.969077286229078,0.02456910271961025,0.24645065880515105,-0.969077286229078,0.01229984103120167,0.24675739769029378,-0.969077286229078,0.0,0.2947551744109043,-0.9555728057861407,0.0,0.2943887705079813,-0.9555728057861407,-0.014692332721584787,0.29329046973704626,-0.9555728057861407,-0.029348137989068105,0.29146300264686636,-0.9555728057861407,-0.04393097916135472,0.28891091260856755,-0.9555728057861407,-0.05840460099758646,0.28564054452009774,-0.9555728057861407,-0.07273301979338337,0.2816600290318111,-0.9555728057861407,-0.08688061284200259,0.27697926233239023,-0.9555728057861407,-0.10081220699799998,0.2716098815453614,-0.9555728057861407,-0.11449316612321174,0.26556523579737257,-0.9555728057861407,-0.12788947719765115,0.25886035303016053,-0.9555728057861407,-0.14096783488123554,0.25151190263872036,-0.9555728057861407,-0.1536957243161093,0.24353815402856288,-0.9555728057861407,-0.16604150196370368,0.23495893119509398,-0.9555728057861407,-0.17797447427555932,0.22579556343803844,-0.9555728057861407,-0.18946497400232418,0.21607083233344035,-0.9555728057861407,-0.20048443395120963,0.20580891509507526,-0.9555728057861407,-0.21100545800853285,0.1950353244660876,-0.9555728057861407,-0.22100188925077216,0.1837768452902912,-0.9555728057861407,-0.23044887497480002,0.17206146792082727,-0.9555728057861407,-0.23932292848561912,0.15991831863173644,-0.9555728057861407,-0.24760198748798654,0.14737758720545213,-0.9555728057861407,-0.25526546893675606,0.1344704518762449,-0.9555728057861407,-0.26229432020957133,0.12122900181621904,-0.9555728057861407,-0.2686710664746872,0.1076861573565752,-0.9555728057861407,-0.27437985413615473,0.09387558814247993,-0.9555728057861407,-0.2794064902483586,0.07983162942502246,-0.9555728057861407,-0.28373847780191624,0.06558919669837078,-0.9555728057861407,-0.28736504679321223,0.05118369889435177,-0.9555728057861407,-0.2902771810003242,0.0366509503502675,-0.9555728057861407,-0.29246764039877104,0.022027081768809725,-0.9555728057861407,-0.2939309791613548,0.007348450391440209,-0.9555728057861407,-0.2946635591973449,-0.007348450391440173,-0.9555728057861407,-0.2946635591973449,-0.022027081768809686,-0.9555728057861407,-0.2939309791613548,-0.03665095035026746,-0.9555728057861407,-0.29246764039877104,-0.051183698894351745,-0.9555728057861407,-0.2902771810003242,-0.06558919669837075,-0.9555728057861407,-0.28736504679321223,-0.07983162942502241,-0.9555728057861407,-0.28373847780191624,-0.0938755881424799,-0.9555728057861407,-0.2794064902483586,-0.10768615735657516,-0.9555728057861407,-0.2743798541361548,-0.12122900181621896,-0.9555728057861407,-0.26867106647468725,-0.13447045187624473,-0.9555728057861407,-0.2622943202095714,-0.14737758720545222,-0.9555728057861407,-0.25526546893675606,-0.15991831863173644,-0.9555728057861407,-0.24760198748798656,-0.1720614679208272,-0.9555728057861407,-0.23932292848561915,-0.18377684529029117,-0.9555728057861407,-0.23044887497480004,-0.19503532446608754,-0.9555728057861407,-0.22100188925077222,-0.20580891509507523,-0.9555728057861407,-0.2110054580085329,-0.21607083233344035,-0.9555728057861407,-0.20048443395120963,-0.22579556343803842,-0.9555728057861407,-0.18946497400232423,-0.23495893119509398,-0.9555728057861407,-0.17797447427555932,-0.24353815402856285,-0.9555728057861407,-0.16604150196370374,-0.25151190263872036,-0.9555728057861407,-0.15369572431610934,-0.25886035303016053,-0.9555728057861407,-0.14096783488123552,-0.2655652357973725,-0.9555728057861407,-0.12788947719765117,-0.2716098815453615,-0.9555728057861407,-0.1144931661232117,-0.2769792623323902,-0.9555728057861407,-0.10081220699800002,-0.2816600290318111,-0.9555728057861407,-0.0868806128420027,-0.2856405445200977,-0.9555728057861407,-0.07273301979338344,-0.28891091260856755,-0.9555728057861407,-0.058404600997586466,-0.29146300264686636,-0.9555728057861407,-0.043930979161354806,-0.29329046973704626,-0.9555728057861407,-0.029348137989068126,-0.2943887705079813,-0.9555728057861407,-0.014692332721584881,-0.2947551744109043,-0.9555728057861407,-3.609709808744338e-17,-0.2943887705079813,-0.9555728057861407,0.014692332721584808,-0.29329046973704626,-0.9555728057861407,0.029348137989068053,-0.29146300264686636,-0.9555728057861407,0.04393097916135473,-0.28891091260856755,-0.9555728057861407,0.05840460099758639,-0.28564054452009774,-0.9555728057861407,0.07273301979338337,-0.28166002903181114,-0.9555728057861407,0.0868806128420025,-0.27697926233239023,-0.9555728057861407,0.10081220699799996,-0.2716098815453614,-0.9555728057861407,0.11449316612321175,-0.26556523579737257,-0.9555728057861407,0.12788947719765112,-0.25886035303016053,-0.9555728057861407,0.14096783488123546,-0.25151190263872036,-0.9555728057861407,0.15369572431610926,-0.24353815402856288,-0.9555728057861407,0.16604150196370368,-0.234958931195094,-0.9555728057861407,0.17797447427555926,-0.22579556343803844,-0.9555728057861407,0.18946497400232418,-0.2160708323334404,-0.9555728057861407,0.20048443395120955,-0.20580891509507532,-0.9555728057861407,0.21100545800853282,-0.1950353244660877,-0.9555728057861407,0.22100188925077208,-0.1837768452902912,-0.9555728057861407,0.23044887497479996,-0.17206146792082744,-0.9555728057861407,0.23932292848561898,-0.15991831863173636,-0.9555728057861407,0.24760198748798656,-0.14737758720545205,-0.9555728057861407,0.2552654689367561,-0.13447045187624493,-0.9555728057861407,0.2622943202095713,-0.12122900181621903,-0.9555728057861407,0.2686710664746872,-0.10768615735657541,-0.9555728057861407,0.2743798541361547,-0.0938755881424801,-0.9555728057861407,0.27940649024835856,-0.0798316294250223,-0.9555728057861407,0.2837384778019163,-0.06558919669837082,-0.9555728057861407,0.28736504679321223,-0.05118369889435175,-0.9555728057861407,0.2902771810003242,-0.036650950350267664,-0.9555728057861407,0.292467640398771,-0.022027081768809822,-0.9555728057861407,0.2939309791613548,-0.007348450391440244,-0.9555728057861407,0.2946635591973449,0.007348450391440136,-0.9555728057861407,0.2946635591973449,0.022027081768809714,-0.9555728057861407,0.2939309791613548,0.036650950350267296,-0.9555728057861407,0.2924676403987711,0.05118369889435164,-0.9555728057861407,0.2902771810003242,0.06558919669837072,-0.9555728057861407,0.28736504679321223,0.07983162942502245,-0.9555728057861407,0.28373847780191624,0.09387558814247998,-0.9555728057861407,0.27940649024835856,0.10768615735657507,-0.9555728057861407,0.2743798541361548,0.12122900181621891,-0.9555728057861407,0.26867106647468725,0.13447045187624482,-0.9555728057861407,0.26229432020957133,0.1473775872054522,-0.9555728057861407,0.25526546893675606,0.1599183186317365,-0.9555728057861407,0.2476019874879865,0.17206146792082716,-0.9555728057861407,0.23932292848561917,0.18377684529029112,-0.9555728057861407,0.23044887497480004,0.1950353244660874,-0.9555728057861407,0.2210018892507723,0.20580891509507532,-0.9555728057861407,0.21100545800853282,0.2160708323334404,-0.9555728057861407,0.20048443395120955,0.22579556343803836,-0.9555728057861407,0.18946497400232426,0.23495893119509395,-0.9555728057861407,0.17797447427555935,0.24353815402856277,-0.9555728057861407,0.16604150196370387,0.2515119026387204,-0.9555728057861407,0.15369572431610926,0.2588603530301605,-0.9555728057861407,0.14096783488123565,0.2655652357973725,-0.9555728057861407,0.12788947719765123,0.2716098815453614,-0.9555728057861407,0.11449316612321174,0.2769792623323901,-0.9555728057861407,0.10081220699800018,0.2816600290318111,-0.9555728057861407,0.08688061284200274,0.2856405445200977,-0.9555728057861407,0.07273301979338347,0.28891091260856755,-0.9555728057861407,0.058404600997586494,0.29146300264686636,-0.9555728057861407,0.04393097916135471,0.2932904697370462,-0.9555728057861407,0.02934813798906829,0.2943887705079813,-0.9555728057861407,0.014692332721584916,0.2947551744109043,-0.9555728057861407,0.0,0.3420201433256688,-0.9396926207859083,0.0,0.34159498534279953,-0.9396926207859083,-0.017048296957866544,0.340320568403926,-0.9396926207859083,-0.03405420916332942,0.338200060910358,-0.9396926207859083,-0.05097545723915767,0.33523873477783633,-0.9396926207859083,-0.06776997229648646,0.33144395232971946,-0.9396926207859083,-0.08439600052470328,0.3268251479930873,-0.9396926207859083,-0.10081220699799998,0.32139380484326974,-0.9396926207859083,-0.11697777844051101,0.31516342605511116,-0.9396926207859083,-0.13285252469454806,0.30814950133195096,-0.9396926207859083,-0.14839697863966433,0.30036946839577994,-0.9396926207859083,-0.1635724943141346,0.2918426696343162,-0.9396926207859083,-0.17834134299490517,0.2825903040127815,-0.9396926207859083,-0.1926668069971441,0.2726353743699337,-0.9396926207859083,-0.20651327096019073,0.262002630229385,-0.9396926207859083,-0.21984631039295421,0.25071850626838704,-0.9396926207859083,-0.2326327772586224,0.2388110565970576,-0.9396926207859083,-0.24484088238590385,0.22630988501144308,-0.9396926207859083,-0.25644027450191687,0.21324607139381693,-0.9396926207859083,-0.2674021156902357,0.1996520944431951,-0.9396926207859083,-0.27769915308649495,0.18556175092817317,-0.9396926207859083,-0.2873057866333041,0.17101007166283438,-0.9396926207859083,-0.29619813272602397,0.15603323441462644,-0.9396926207859083,-0.3043540835911706,0.14066847396073093,-0.9396926207859083,-0.31175336224982436,0.12495398951654108,-0.9396926207859083,-0.31837757292939334,0.10892884976639326,-0.9396926207859083,-0.3242102467984033,0.09263289573266187,-0.9396926207859083,-0.32923688291060715,0.07610664172470036,-0.9396926207859083,-0.3334449842566224,0.05939117461388475,-0.9396926207859083,-0.33682408883346526,0.042528051685177544,-0.9396926207859083,-0.3393657956547388,0.025559197319169516,-0.9396926207859083,-0.3410637856368084,0.008526798761463807,-0.9396926207859083,-0.3419138373090398,-0.008526798761463766,-0.9396926207859083,-0.3419138373090398,-0.02555919731916947,-0.9396926207859083,-0.3410637856368084,-0.04252805168517751,-0.9396926207859083,-0.3393657956547388,-0.059391174613884705,-0.9396926207859083,-0.33682408883346526,-0.07610664172470032,-0.9396926207859083,-0.3334449842566224,-0.09263289573266181,-0.9396926207859083,-0.32923688291060715,-0.10892884976639323,-0.9396926207859083,-0.3242102467984033,-0.12495398951654105,-0.9396926207859083,-0.3183775729293934,-0.14066847396073084,-0.9396926207859083,-0.3117533622498244,-0.15603323441462624,-0.9396926207859083,-0.3043540835911707,-0.1710100716628345,-0.9396926207859083,-0.2961981327260239,-0.18556175092817317,-0.9396926207859083,-0.28730578663330414,-0.19965209444319498,-0.9396926207859083,-0.277699153086495,-0.21324607139381688,-0.9396926207859083,-0.2674021156902357,-0.226309885011443,-0.9396926207859083,-0.2564402745019169,-0.23881105659705754,-0.9396926207859083,-0.2448408823859039,-0.25071850626838704,-0.9396926207859083,-0.2326327772586224,-0.26200263022938497,-0.9396926207859083,-0.2198463103929543,-0.2726353743699337,-0.9396926207859083,-0.20651327096019073,-0.28259030401278146,-0.9396926207859083,-0.19266680699714417,-0.29184266963431615,-0.9396926207859083,-0.17834134299490523,-0.30036946839578,-0.9396926207859083,-0.16357249431413456,-0.30814950133195096,-0.9396926207859083,-0.14839697863966436,-0.3151634260551112,-0.9396926207859083,-0.13285252469454803,-0.32139380484326974,-0.9396926207859083,-0.11697777844051106,-0.3268251479930873,-0.9396926207859083,-0.1008122069980001,-0.3314439523297194,-0.9396926207859083,-0.08439600052470336,-0.33523873477783633,-0.9396926207859083,-0.06776997229648647,-0.338200060910358,-0.9396926207859083,-0.05097545723915776,-0.340320568403926,-0.9396926207859083,-0.03405420916332944,-0.34159498534279953,-0.9396926207859083,-0.017048296957866655,-0.3420201433256688,-0.9396926207859083,-4.188538737676993e-17,-0.34159498534279953,-0.9396926207859083,0.01704829695786657,-0.340320568403926,-0.9396926207859083,0.03405420916332936,-0.338200060910358,-0.9396926207859083,0.050975457239157675,-0.3352387347778364,-0.9396926207859083,0.06776997229648639,-0.33144395232971946,-0.9396926207859083,0.08439600052470328,-0.3268251479930874,-0.9396926207859083,0.10081220699799988,-0.32139380484326974,-0.9396926207859083,0.116977778440511,-0.31516342605511116,-0.9396926207859083,0.13285252469454809,-0.30814950133195096,-0.9396926207859083,0.14839697863966428,-0.30036946839578,-0.9396926207859083,0.16357249431413448,-0.2918426696343162,-0.9396926207859083,0.17834134299490514,-0.2825903040127815,-0.9396926207859083,0.1926668069971441,-0.27263537436993374,-0.9396926207859083,0.20651327096019065,-0.262002630229385,-0.9396926207859083,0.21984631039295421,-0.2507185062683871,-0.9396926207859083,0.23263277725862233,-0.23881105659705762,-0.9396926207859083,0.24484088238590382,-0.2263098850114432,-0.9396926207859083,0.25644027450191675,-0.21324607139381693,-0.9396926207859083,0.26740211569023564,-0.1996520944431953,-0.9396926207859083,0.2776991530864948,-0.1855617509281731,-0.9396926207859083,0.28730578663330414,-0.17101007166283427,-0.9396926207859083,0.29619813272602397,-0.15603323441462646,-0.9396926207859083,0.3043540835911706,-0.14066847396073093,-0.9396926207859083,0.31175336224982436,-0.12495398951654133,-0.9396926207859083,0.3183775729293933,-0.10892884976639346,-0.9396926207859083,0.3242102467984032,-0.09263289573266167,-0.9396926207859083,0.3292368829106072,-0.0761066417247004,-0.9396926207859083,0.3334449842566224,-0.05939117461388472,-0.9396926207859083,0.33682408883346526,-0.04252805168517774,-0.9396926207859083,0.33936579565473873,-0.02555919731916963,-0.9396926207859083,0.3410637856368084,-0.008526798761463849,-0.9396926207859083,0.3419138373090398,0.008526798761463722,-0.9396926207859083,0.3419138373090398,0.025559197319169506,-0.9396926207859083,0.3410637856368084,0.042528051685177315,-0.9396926207859083,0.3393657956547388,0.059391174613884594,-0.9396926207859083,0.33682408883346526,0.07610664172470027,-0.9396926207859083,0.3334449842566224,0.09263289573266185,-0.9396926207859083,0.32923688291060715,0.10892884976639333,-0.9396926207859083,0.32421024679840327,0.12495398951654092,-0.9396926207859083,0.31837757292939345,0.14066847396073082,-0.9396926207859083,0.3117533622498244,0.15603323441462635,-0.9396926207859083,0.30435408359117067,0.17101007166283444,-0.9396926207859083,0.2961981327260239,0.18556175092817326,-0.9396926207859083,0.2873057866333041,0.19965209444319496,-0.9396926207859083,0.277699153086495,0.21324607139381685,-0.9396926207859083,0.2674021156902357,0.22630988501144284,-0.9396926207859083,0.25644027450191703,0.23881105659705762,-0.9396926207859083,0.24484088238590382,0.2507185062683871,-0.9396926207859083,0.23263277725862233,0.26200263022938497,-0.9396926207859083,0.21984631039295435,0.2726353743699337,-0.9396926207859083,0.20651327096019076,0.28259030401278135,-0.9396926207859083,0.19266680699714434,0.29184266963431627,-0.9396926207859083,0.17834134299490514,0.3003694683957799,-0.9396926207859083,0.16357249431413473,0.30814950133195096,-0.9396926207859083,0.14839697863966442,0.31516342605511116,-0.9396926207859083,0.13285252469454806,0.3213938048432696,-0.9396926207859083,0.11697777844051126,0.3268251479930873,-0.9396926207859083,0.10081220699800014,0.3314439523297194,-0.9396926207859083,0.0843960005247034,0.33523873477783633,-0.9396926207859083,0.0677699722964865,0.338200060910358,-0.9396926207859083,0.05097545723915765,0.34032056840392594,-0.9396926207859083,0.034054209163329636,0.34159498534279953,-0.9396926207859083,0.017048296957866693,0.3420201433256688,-0.9396926207859083,0.0,0.3884347962746948,-0.9214762118704076,0.0,0.38795194122161347,-0.9214762118704076,-0.019361876441744634,0.3865045765161113,-0.9214762118704076,-0.03867561620795611,0.3840963005348959,-0.9214762118704076,-0.057893202298461156,0.3807331006315099,-0.9214762118704076,-0.0769668567662745,0.37642333825082597,-0.9214762118704076,-0.09584915950110305,0.37117772814117805,-0.9214762118704076,-0.11449316612321171,0.36500931171580836,-0.9214762118704076,-0.13285252469454803,0.3579334246298593,-0.9214762118704076,-0.15088159095696363,0.3499676586535186,-0.9214762118704076,-0.1685355418110315,0.34113181793610664,-0.9214762118704076,-0.18577048675333396,0.33144786976984114,-0.9214762118704076,-0.20254357699516937,0.320939889975687,-0.9214762118704076,-0.21881311199139228,0.30963400304707256,-0.9214762118704076,-0.23453864311453904,0.29755831720028225,-0.9214762118704076,-0.2496810742164889,0.2847428544930023,-0.9214762118704076,-0.2642027588276485,0.27121947618475484,-0.9214762118704076,-0.2780675937520064,0.2570218035247869,-0.9214762118704076,-0.2912411088253674,0.242185134164347,-0.9214762118704076,-0.3036905526136118,0.22674635440116284,-0.9214762118704076,-0.3153849738379207,0.21074384747429306,-0.9214762118704076,-0.3262952983245323,0.19421739813734734,-0.9214762118704076,-0.33639440128771875,0.1772080937473205,-0.9214762118704076,-0.34565717476627694,0.15975822211494853,-0.9214762118704076,-0.3540605900458768,0.14191116637054893,-0.9214762118704076,-0.36158375491207295,0.12371129710672302,-0.9214762118704076,-0.36820796559164193,0.10520386206607117,-0.9214762118704076,-0.3739167532531094,0.08643487364817581,-0.9214762118704076,-0.3786959249508602,0.06745099451552612,-0.9214762118704076,-0.38253359891103694,0.048299421582786765,-0.9214762118704076,-0.3854202340715026,0.029027768677831634,-0.9214762118704076,-0.3873486538024238,0.009683948166265615,-0.9214762118704076,-0.38831406374850336,-0.009683948166265569,-0.9214762118704076,-0.38831406374850336,-0.029027768677831585,-0.9214762118704076,-0.3873486538024238,-0.04829942158278672,-0.9214762118704076,-0.3854202340715026,-0.06745099451552608,-0.9214762118704076,-0.38253359891103694,-0.08643487364817577,-0.9214762118704076,-0.3786959249508602,-0.10520386206607112,-0.9214762118704076,-0.37391675325310947,-0.12371129710672298,-0.9214762118704076,-0.36820796559164193,-0.1419111663705489,-0.9214762118704076,-0.36158375491207295,-0.15975822211494842,-0.9214762118704076,-0.3540605900458768,-0.1772080937473203,-0.9214762118704076,-0.34565717476627705,-0.19421739813734748,-0.9214762118704076,-0.3363944012877187,-0.21074384747429306,-0.9214762118704076,-0.32629529832453236,-0.22674635440116273,-0.9214762118704076,-0.31538497383792075,-0.24218513416434698,-0.9214762118704076,-0.30369055261361183,-0.25702180352478676,-0.9214762118704076,-0.2912411088253675,-0.2712194761847548,-0.9214762118704076,-0.2780675937520064,-0.2847428544930023,-0.9214762118704076,-0.2642027588276485,-0.2975583172002822,-0.9214762118704076,-0.249681074216489,-0.30963400304707256,-0.9214762118704076,-0.23453864311453904,-0.32093988997568695,-0.9214762118704076,-0.21881311199139236,-0.3314478697698411,-0.9214762118704076,-0.2025435769951694,-0.3411318179361067,-0.9214762118704076,-0.18577048675333394,-0.34996765865351853,-0.9214762118704076,-0.16853554181103156,-0.35793342462985933,-0.9214762118704076,-0.15088159095696357,-0.3650093117158083,-0.9214762118704076,-0.1328525246945481,-0.371177728141178,-0.9214762118704076,-0.11449316612321186,-0.37642333825082597,-0.9214762118704076,-0.09584915950110313,-0.3807331006315099,-0.9214762118704076,-0.07696685676627452,-0.3840963005348959,-0.9214762118704076,-0.05789320229846126,-0.3865045765161113,-0.9214762118704076,-0.038675616207956134,-0.38795194122161347,-0.9214762118704076,-0.019361876441744755,-0.3884347962746948,-0.9214762118704076,-4.756954299352592e-17,-0.38795194122161347,-0.9214762118704076,0.01936187644174466,-0.3865045765161113,-0.9214762118704076,0.03867561620795604,-0.3840963005348959,-0.9214762118704076,0.05789320229846116,-0.38073310063150995,-0.9214762118704076,0.07696685676627442,-0.37642333825082597,-0.9214762118704076,0.09584915950110305,-0.3711777281411781,-0.9214762118704076,0.1144931661232116,-0.36500931171580836,-0.9214762118704076,0.13285252469454803,-0.3579334246298593,-0.9214762118704076,0.15088159095696366,-0.3499676586535186,-0.9214762118704076,0.16853554181103148,-0.3411318179361067,-0.9214762118704076,0.18577048675333385,-0.33144786976984114,-0.9214762118704076,0.2025435769951693,-0.320939889975687,-0.9214762118704076,0.21881311199139228,-0.3096340030470726,-0.9214762118704076,0.23453864311453895,-0.29755831720028225,-0.9214762118704076,0.2496810742164889,-0.28474285449300235,-0.9214762118704076,0.2642027588276484,-0.2712194761847549,-0.9214762118704076,0.27806759375200635,-0.257021803524787,-0.9214762118704076,0.29124110882536736,-0.242185134164347,-0.9214762118704076,0.3036905526136117,-0.22674635440116306,-0.9214762118704076,0.3153849738379205,-0.21074384747429298,-0.9214762118704076,0.32629529832453236,-0.19421739813734726,-0.9214762118704076,0.3363944012877188,-0.17720809374732052,-0.9214762118704076,0.3456571747662769,-0.1597582221149485,-0.9214762118704076,0.3540605900458768,-0.14191116637054924,-0.9214762118704076,0.36158375491207284,-0.12371129710672323,-0.9214762118704076,0.3682079655916419,-0.10520386206607096,-0.9214762118704076,0.3739167532531095,-0.08643487364817587,-0.9214762118704076,0.3786959249508602,-0.0674509945155261,-0.9214762118704076,0.38253359891103694,-0.04829942158278698,-0.9214762118704076,0.3854202340715026,-0.029027768677831765,-0.9214762118704076,0.3873486538024238,-0.009683948166265662,-0.9214762118704076,0.38831406374850336,0.00968394816626552,-0.9214762118704076,0.38831406374850336,0.029027768677831623,-0.9214762118704076,0.3873486538024238,0.048299421582786495,-0.9214762118704076,0.38542023407150267,0.06745099451552596,-0.9214762118704076,0.382533598911037,0.08643487364817573,-0.9214762118704076,0.3786959249508602,0.10520386206607116,-0.9214762118704076,0.3739167532531094,0.12371129710672309,-0.9214762118704076,0.3682079655916419,0.14191116637054876,-0.9214762118704076,0.361583754912073,0.1597582221149484,-0.9214762118704076,0.3540605900458768,0.1772080937473204,-0.9214762118704076,0.345657174766277,0.19421739813734745,-0.9214762118704076,0.3363944012877187,0.21074384747429317,-0.9214762118704076,0.32629529832453225,0.22674635440116267,-0.9214762118704076,0.3153849738379208,0.24218513416434692,-0.9214762118704076,0.30369055261361183,0.2570218035247866,-0.9214762118704076,0.29124110882536763,0.2712194761847549,-0.9214762118704076,0.27806759375200635,0.28474285449300235,-0.9214762118704076,0.2642027588276484,0.29755831720028214,-0.9214762118704076,0.24968107421648902,0.30963400304707256,-0.9214762118704076,0.23453864311453906,0.32093988997568684,-0.9214762118704076,0.21881311199139253,0.33144786976984114,-0.9214762118704076,0.2025435769951693,0.3411318179361066,-0.9214762118704076,0.18577048675333413,0.34996765865351853,-0.9214762118704076,0.1685355418110316,0.3579334246298593,-0.9214762118704076,0.15088159095696363,0.36500931171580825,-0.9214762118704076,0.1328525246945483,0.371177728141178,-0.9214762118704076,0.1144931661232119,0.37642333825082597,-0.9214762118704076,0.09584915950110318,0.3807331006315099,-0.9214762118704076,0.07696685676627456,0.3840963005348959,-0.9214762118704076,0.057893202298461136,0.38650457651611125,-0.9214762118704076,0.03867561620795635,0.38795194122161347,-0.9214762118704076,0.019361876441744804,0.3884347962746948,-0.9214762118704076,0.0,0.4338837391175582,-0.9009688679024191,0.0,0.4333443874480065,-0.9009688679024191,-0.021627319250089563,0.43172767335258344,-0.9009688679024191,-0.04320086957687637,0.42903761623726316,-0.9009688679024191,-0.06466701573507294,0.42528090400788554,-0.9009688679024191,-0.08597238950307774,0.42046687644296854,-0.9009688679024191,-0.10706402236478291,0.41460750197354695,-0.9009688679024191,-0.12788947719765112,0.4077173479277676,-0.9009688679024191,-0.1483969786396643,0.39981354431421573,-0.9009688679024191,-0.1685355418110315,0.39091574123401496,-0.9009688679024191,-0.18825509907063326,0.38104606002757974,-0.9009688679024191,-0.20750662449206628,0.37022903827747744,-0.9009688679024191,-0.22624225574982101,0.3584915688041322,-0.9009688679024191,-0.24441541311256432,0.3458628328060356,-0.9009688679024191,-0.26198091524769046,0.3323742273106898,-0.9009688679024191,-0.2788950915492332,0.31805928711665005,-0.9009688679024191,-0.2951158907098729,0.3029536014207316,-0.9009688679024191,-0.310602985267112,0.28709472533765873,-0.9009688679024191,-0.3253178718637013,0.27052208653213283,-0.9009688679024191,-0.3392239669730524,0.253276887195445,-0.9009688679024191,-0.3522866978516491,0.23540200161033706,-0.9009688679024191,-0.3644735884923353,0.21694186955877903,-0.9009688679024191,-0.3757543403647854,0.19794238583766954,-0.9009688679024191,-0.38610090774242484,0.17845078615713855,-0.9009688679024191,-0.3954875674285255,0.1585155297051304,-0.9009688679024191,-0.40389098270812535,0.13818617867022684,-0.9009688679024191,-0.4112902613667791,0.11751327502223698,-0.9009688679024191,-0.4176670076318949,0.09654821485689694,-0.9009688679024191,-0.423005367907524,0.07534312061707783,-0.9009688679024191,-0.42729207018889753,0.053950711508180205,-0.9009688679024191,-0.430516457058722,0.032424172429882867,-0.9009688679024191,-0.4326705121831976,0.010817021750102344,-0.9009688679024191,-0.4337488802418873,-0.01081702175010229,-0.9009688679024191,-0.4337488802418873,-0.03242417242988281,-0.9009688679024191,-0.4326705121831976,-0.053950711508180156,-0.9009688679024191,-0.430516457058722,-0.07534312061707778,-0.9009688679024191,-0.42729207018889753,-0.09654821485689688,-0.9009688679024191,-0.423005367907524,-0.11751327502223691,-0.9009688679024191,-0.41766700763189496,-0.13818617867022678,-0.9009688679024191,-0.4112902613667791,-0.15851552970513033,-0.9009688679024191,-0.4038909827081254,-0.17845078615713844,-0.9009688679024191,-0.39548756742852553,-0.19794238583766932,-0.9009688679024191,-0.38610090774242495,-0.21694186955877917,-0.9009688679024191,-0.37575434036478533,-0.23540200161033706,-0.9009688679024191,-0.3644735884923353,-0.25327688719544483,-0.9009688679024191,-0.3522866978516492,-0.2705220865321328,-0.9009688679024191,-0.33922396697305246,-0.2870947253376586,-0.9009688679024191,-0.3253178718637014,-0.3029536014207315,-0.9009688679024191,-0.31060298526711205,-0.31805928711665005,-0.9009688679024191,-0.2951158907098729,-0.3323742273106897,-0.9009688679024191,-0.2788950915492333,-0.3458628328060356,-0.9009688679024191,-0.26198091524769046,-0.35849156880413213,-0.9009688679024191,-0.24441541311256443,-0.37022903827747744,-0.9009688679024191,-0.22624225574982104,-0.3810460600275798,-0.9009688679024191,-0.20750662449206622,-0.3909157412340149,-0.9009688679024191,-0.1882550990706333,-0.39981354431421573,-0.9009688679024191,-0.16853554181103148,-0.4077173479277676,-0.9009688679024191,-0.14839697863966436,-0.4146075019735469,-0.9009688679024191,-0.12788947719765129,-0.4204668764429685,-0.9009688679024191,-0.107064022364783,-0.42528090400788554,-0.9009688679024191,-0.08597238950307776,-0.42903761623726316,-0.9009688679024191,-0.06466701573507305,-0.43172767335258344,-0.9009688679024191,-0.0432008695768764,-0.4333443874480065,-0.9009688679024191,-0.0216273192500897,-0.4338837391175582,-0.9009688679024191,-5.313543323124029e-17,-0.4333443874480065,-0.9009688679024191,0.02162731925008959,-0.43172767335258344,-0.9009688679024191,0.04320086957687629,-0.42903761623726316,-0.9009688679024191,0.06466701573507296,-0.4252809040078856,-0.9009688679024191,0.08597238950307765,-0.42046687644296854,-0.9009688679024191,0.10706402236478291,-0.414607501973547,-0.9009688679024191,0.127889477197651,-0.4077173479277676,-0.9009688679024191,0.14839697863966428,-0.39981354431421573,-0.9009688679024191,0.16853554181103153,-0.39091574123401496,-0.9009688679024191,0.1882550990706332,-0.3810460600275798,-0.9009688679024191,0.2075066244920661,-0.37022903827747744,-0.9009688679024191,0.22624225574982096,-0.3584915688041322,-0.9009688679024191,0.24441541311256432,-0.34586283280603564,-0.9009688679024191,0.2619809152476904,-0.3323742273106898,-0.9009688679024191,0.2788950915492332,-0.31805928711665016,-0.9009688679024191,0.29511589070987276,-0.30295360142073163,-0.9009688679024191,0.310602985267112,-0.28709472533765884,-0.9009688679024191,0.3253178718637012,-0.27052208653213283,-0.9009688679024191,0.33922396697305235,-0.2532768871954452,-0.9009688679024191,0.35228669785164896,-0.23540200161033695,-0.9009688679024191,0.3644735884923353,-0.21694186955877892,-0.9009688679024191,0.37575434036478544,-0.19794238583766957,-0.9009688679024191,0.3861009077424248,-0.17845078615713852,-0.9009688679024191,0.3954875674285255,-0.1585155297051307,-0.9009688679024191,0.40389098270812523,-0.1381861786702271,-0.9009688679024191,0.411290261366779,-0.11751327502223674,-0.9009688679024191,0.417667007631895,-0.09654821485689699,-0.9009688679024191,0.423005367907524,-0.07534312061707779,-0.9009688679024191,0.42729207018889753,-0.053950711508180454,-0.9009688679024191,0.43051645705872194,-0.03242417242988301,-0.9009688679024191,0.4326705121831976,-0.010817021750102396,-0.9009688679024191,0.4337488802418873,0.010817021750102237,-0.9009688679024191,0.4337488802418873,0.03242417242988286,-0.9009688679024191,0.4326705121831976,0.05395071150817991,-0.9009688679024191,0.430516457058722,0.07534312061707764,-0.9009688679024191,0.4272920701888976,0.09654821485689684,-0.9009688679024191,0.423005367907524,0.11751327502223695,-0.9009688679024191,0.4176670076318949,0.13818617867022692,-0.9009688679024191,0.41129026136677904,0.1585155297051302,-0.9009688679024191,0.40389098270812546,0.17845078615713839,-0.9009688679024191,0.39548756742852553,0.19794238583766943,-0.9009688679024191,0.3861009077424249,0.21694186955877914,-0.9009688679024191,0.37575434036478533,0.23540200161033714,-0.9009688679024191,0.36447358849233524,0.2532768871954448,-0.9009688679024191,0.35228669785164923,0.2705220865321327,-0.9009688679024191,0.33922396697305246,0.2870947253376584,-0.9009688679024191,0.3253178718637016,0.30295360142073163,-0.9009688679024191,0.310602985267112,0.31805928711665016,-0.9009688679024191,0.29511589070987276,0.3323742273106897,-0.9009688679024191,0.27889509154923336,0.3458628328060355,-0.9009688679024191,0.2619809152476905,0.35849156880413197,-0.9009688679024191,0.24441541311256462,0.3702290382774775,-0.9009688679024191,0.22624225574982096,0.3810460600275796,-0.9009688679024191,0.20750662449206644,0.3909157412340149,-0.9009688679024191,0.18825509907063334,0.39981354431421573,-0.9009688679024191,0.1685355418110315,0.4077173479277675,-0.9009688679024191,0.1483969786396646,0.4146075019735469,-0.9009688679024191,0.12788947719765134,0.4204668764429685,-0.9009688679024191,0.10706402236478306,0.42528090400788554,-0.9009688679024191,0.0859723895030778,0.42903761623726316,-0.9009688679024191,0.06466701573507291,0.4317276733525834,-0.9009688679024191,0.04320086957687664,0.4333443874480065,-0.9009688679024191,0.02162731925008975,0.4338837391175582,-0.9009688679024191,0.0,0.47825397862131824,-0.8782215733702285,0.0,0.4776594712485281,-0.8782215733702285,-0.023838993135131735,0.4758774271691584,-0.8782215733702285,-0.047618718777206394,0.4729122768255731,-0.8782215733702285,-0.07128005678149295,0.4687713920487218,-0.8782215733702285,-0.09476418133358135,0.46346506773060653,-0.8782215733702285,-0.11801270719962581,0.457006496229558,-0.8782215733702285,-0.14096783488123552,0.4494117345719544,-0.8782215733702285,-0.16357249431413456,0.4406996645319233,-0.8782215733702285,-0.18577048675333396,0.4308919456882769,-0.8782215733702285,-0.20750662449206625,0.42001296157538576,-0.8782215733702285,-0.2287268680671203,0.4080897590618708,-0.8782215733702285,-0.24937846060946117,0.395151981107826,-0.8782215733702285,-0.26941005900611925,0.3812317930677494,-0.8782215733702285,-0.28877186154725853,0.3663638027224034,-0.8782215733702285,-0.3074157317410744,0.3505849742384191,-0.8782215733702285,-0.32529531798869676,0.33393453626955394,-0.8782215733702285,-0.34236616882156784,0.3164538844280774,-0.8782215733702285,-0.35858584341479705,0.2981864783687567,-0.8782215733702285,-0.37391401710173866,0.27917773374130705,-0.8782215733702285,-0.388312581627467,0.259474909279931,-0.8782215733702285,-0.40174573989190215,0.23912698931065907,-0.8782215733702285,-0.41418009494704144,0.2181845619685972,-0.8782215733702285,-0.42558473302703387,0.19669969342785146,-0.8782215733702285,-0.43593130040467337,0.17472579845681638,-0.8782215733702285,-0.4451940738832316,0.15231750762064436,-0.8782215733702285,-0.4533500247483784,0.12953053146105262,-0.8782215733702285,-0.46037887602119365,0.10642152199113898,-0.8782215733702285,-0.4662631528699323,0.083047931849551,-0.8782215733702285,-0.470988226055209,0.059467871464173934,-0.8782215733702285,-0.47454234830059244,0.03573996458045092,-0.8782215733702285,-0.4769166834981855,0.011923202513515056,-0.8782215733702285,-0.4781053286765815,-0.011923202513514999,-0.8782215733702285,-0.4781053286765815,-0.03573996458045086,-0.8782215733702285,-0.4769166834981855,-0.05946787146417388,-0.8782215733702285,-0.47454234830059244,-0.08304793184955096,-0.8782215733702285,-0.470988226055209,-0.10642152199113893,-0.8782215733702285,-0.4662631528699323,-0.12953053146105253,-0.8782215733702285,-0.4603788760211937,-0.1523175076206443,-0.8782215733702285,-0.4533500247483784,-0.17472579845681632,-0.8782215733702285,-0.4451940738832317,-0.19669969342785135,-0.8782215733702285,-0.4359313004046734,-0.21818456196859695,-0.8782215733702285,-0.425584733027034,-0.23912698931065923,-0.8782215733702285,-0.4141800949470414,-0.259474909279931,-0.8782215733702285,-0.4017457398919022,-0.2791777337413069,-0.8782215733702285,-0.38831258162746707,-0.29818647836875667,-0.8782215733702285,-0.3739140171017387,-0.3164538844280773,-0.8782215733702285,-0.35858584341479716,-0.3339345362695539,-0.8782215733702285,-0.3423661688215679,-0.3505849742384191,-0.8782215733702285,-0.32529531798869676,-0.3663638027224033,-0.8782215733702285,-0.3074157317410745,-0.3812317930677494,-0.8782215733702285,-0.28877186154725853,-0.39515198110782596,-0.8782215733702285,-0.26941005900611936,-0.4080897590618707,-0.8782215733702285,-0.24937846060946123,-0.4200129615753858,-0.8782215733702285,-0.22872686806712025,-0.4308919456882768,-0.8782215733702285,-0.2075066244920663,-0.4406996645319233,-0.8782215733702285,-0.1857704867533339,-0.4494117345719543,-0.8782215733702285,-0.16357249431413465,-0.45700649622955797,-0.8782215733702285,-0.1409678348812357,-0.4634650677306065,-0.8782215733702285,-0.11801270719962591,-0.4687713920487218,-0.8782215733702285,-0.09476418133358136,-0.4729122768255731,-0.8782215733702285,-0.07128005678149309,-0.4758774271691584,-0.8782215733702285,-0.04761871877720643,-0.4776594712485281,-0.8782215733702285,-0.023838993135131888,-0.47825397862131824,-0.8782215733702285,-5.856922040980841e-17,-0.4776594712485281,-0.8782215733702285,0.023838993135131766,-0.4758774271691584,-0.8782215733702285,0.04761871877720631,-0.4729122768255731,-0.8782215733702285,0.07128005678149296,-0.46877139204872187,-0.8782215733702285,0.09476418133358125,-0.46346506773060653,-0.8782215733702285,0.11801270719962581,-0.4570064962295581,-0.8782215733702285,0.14096783488123538,-0.4494117345719544,-0.8782215733702285,0.16357249431413454,-0.4406996645319233,-0.8782215733702285,0.185770486753334,-0.4308919456882769,-0.8782215733702285,0.20750662449206622,-0.4200129615753858,-0.8782215733702285,0.22872686806712014,-0.4080897590618708,-0.8782215733702285,0.24937846060946112,-0.395151981107826,-0.8782215733702285,0.26941005900611925,-0.38123179306774946,-0.8782215733702285,0.2887718615472584,-0.3663638027224034,-0.8782215733702285,0.3074157317410744,-0.35058497423841917,-0.8782215733702285,0.32529531798869665,-0.333934536269554,-0.8782215733702285,0.3423661688215678,-0.3164538844280776,-0.8782215733702285,0.35858584341479693,-0.2981864783687567,-0.8782215733702285,0.3739140171017386,-0.27917773374130733,-0.8782215733702285,0.3883125816274668,-0.2594749092799309,-0.8782215733702285,0.4017457398919022,-0.23912698931065893,-0.8782215733702285,0.4141800949470415,-0.21818456196859726,-0.8782215733702285,0.4255847330270338,-0.19669969342785143,-0.8782215733702285,0.43593130040467337,-0.1747257984568167,-0.8782215733702285,0.4451940738832315,-0.1523175076206446,-0.8782215733702285,0.45335002474837827,-0.12953053146105234,-0.8782215733702285,0.4603788760211937,-0.10642152199113905,-0.8782215733702285,0.4662631528699323,-0.08304793184955098,-0.8782215733702285,0.470988226055209,-0.059467871464174205,-0.8782215733702285,0.4745423483005924,-0.03573996458045108,-0.8782215733702285,0.4769166834981855,-0.011923202513515115,-0.8782215733702285,0.4781053286765815,0.011923202513514938,-0.8782215733702285,0.4781053286765815,0.03573996458045091,-0.8782215733702285,0.4769166834981855,0.05946787146417361,-0.8782215733702285,0.4745423483005925,0.0830479318495508,-0.8782215733702285,0.47098822605520907,0.10642152199113887,-0.8782215733702285,0.4662631528699323,0.1295305314610526,-0.8782215733702285,0.46037887602119365,0.15231750762064442,-0.8782215733702285,0.4533500247483783,0.17472579845681616,-0.8782215733702285,0.44519407388323173,0.1966996934278513,-0.8782215733702285,0.4359313004046734,0.2181845619685971,-0.8782215733702285,0.4255847330270339,0.23912698931065918,-0.8782215733702285,0.4141800949470414,0.2594749092799311,-0.8782215733702285,0.4017457398919021,0.27917773374130683,-0.8782215733702285,0.3883125816274671,0.2981864783687566,-0.8782215733702285,0.3739140171017387,0.31645388442807715,-0.8782215733702285,0.35858584341479727,0.333934536269554,-0.8782215733702285,0.3423661688215678,0.35058497423841917,-0.8782215733702285,0.32529531798869665,0.36636380272240326,-0.8782215733702285,0.30741573174107456,0.38123179306774935,-0.8782215733702285,0.2887718615472586,0.3951519811078258,-0.8782215733702285,0.2694100590061196,0.40808975906187084,-0.8782215733702285,0.24937846060946112,0.42001296157538565,-0.8782215733702285,0.22872686806712048,0.4308919456882768,-0.8782215733702285,0.20750662449206636,0.4406996645319233,-0.8782215733702285,0.18577048675333396,0.4494117345719542,-0.8782215733702285,0.1635724943141349,0.45700649622955797,-0.8782215733702285,0.14096783488123574,0.4634650677306065,-0.8782215733702285,0.11801270719962596,0.4687713920487218,-0.8782215733702285,0.09476418133358142,0.4729122768255731,-0.8782215733702285,0.07128005678149293,0.4758774271691584,-0.8782215733702285,0.04761871877720669,0.4776594712485281,-0.8782215733702285,0.023838993135131943,0.47825397862131824,-0.8782215733702285,0.0,0.521435203379498,-0.8532908816321557,0.0,0.52078701834247,-0.8532908816321557,-0.025991399527116828,0.5188440747215177,-0.8532908816321557,-0.05191818033974832,0.5156112029806169,-0.8532908816321557,-0.07771588437571479,0.511096440548294,-0.8532908816321557,-0.10332037447804102,0.5053110118353112,-0.8532908816321557,-0.1286679938500339,0.4982693003290138,-0.8532908816321557,-0.15369572431610923,0.48998881283371365,-0.8532908816321557,-0.17834134299490512,0.4804901359460154,-0.8532908816321557,-0.2025435769951693,0.469796884873294,-0.8532908816321557,-0.22624225574982096,0.45793564472256787,-0.8532908816321557,-0.24937846060946114,0.4449359044057343,-0.8532908816321557,-0.27189467132341844,0.43082998332548794,-0.8532908816321557,-0.2937349090441554,0.41565295102419375,-0.8532908816321557,-0.3148448754995032,0.3994425399954786,-0.8532908816321557,-0.335172087986722,0.3822390518753072,-0.8532908816321557,-0.3546660098527693,0.36408525724576496,-0.8532908816321557,-0.3732781761363817,0.3450262893006519,-0.8532908816321557,-0.39096231405960513,0.32510953163725165,-0.8532908816321557,-0.40767445806921154,0.3043845004532427,-0.8532908816321557,-0.42337305914199164,0.28290272144162903,-0.8532908816321557,-0.43801908808217305,0.26071760168974895,-0.8532908816321557,-0.4515761325541507,0.23788429690084106,-0.8532908816321557,-0.46401048760928987,0.21445957426827505,-0.8532908816321557,-0.47529123948173996,0.1905016713433653,-0.8532908816321557,-0.48539034244492635,0.16607015124764213,-0.8532908816321557,-0.4942826885376462,0.14122575458954634,-0.8532908816321557,-0.5019461699864156,0.11603024845370667,-0.8532908816321557,-0.5083617341688785,0.09054627283823506,-0.8532908816321557,-0.5135134309816272,0.06483718492182171,-0.8532908816321557,-0.5173884524946725,0.038966901547806125,-0.8532908816321557,-0.5199771647939764,0.012999740316833684,-0.8532908816321557,-0.5212731319328796,-0.01299974031683362,-0.8532908816321557,-0.5212731319328796,-0.038966901547806056,-0.8532908816321557,-0.5199771647939764,-0.06483718492182165,-0.8532908816321557,-0.5173884524946725,-0.090546272838235,-0.8532908816321557,-0.5135134309816272,-0.11603024845370662,-0.8532908816321557,-0.5083617341688785,-0.14122575458954625,-0.8532908816321557,-0.5019461699864157,-0.16607015124764207,-0.8532908816321557,-0.4942826885376462,-0.19050167134336524,-0.8532908816321557,-0.4853903424449264,-0.21445957426827492,-0.8532908816321557,-0.47529123948174,-0.2378842969008408,-0.8532908816321557,-0.46401048760929,-0.2607176016897491,-0.8532908816321557,-0.45157613255415063,-0.28290272144162903,-0.8532908816321557,-0.4380190880821731,-0.30438450045324256,-0.8532908816321557,-0.4233730591419917,-0.3251095316372516,-0.8532908816321557,-0.4076744580692116,-0.34502628930065177,-0.8532908816321557,-0.3909623140596053,-0.3640852572457649,-0.8532908816321557,-0.37327817613638176,-0.3822390518753072,-0.8532908816321557,-0.3546660098527693,-0.3994425399954785,-0.8532908816321557,-0.3351720879867221,-0.41565295102419375,-0.8532908816321557,-0.3148448754995032,-0.4308299833254879,-0.8532908816321557,-0.2937349090441555,-0.4449359044057342,-0.8532908816321557,-0.2718946713234185,-0.4579356447225679,-0.8532908816321557,-0.2493784606094611,-0.46979688487329396,-0.8532908816321557,-0.22624225574982104,-0.4804901359460155,-0.8532908816321557,-0.20254357699516926,-0.4899888128337136,-0.8532908816321557,-0.1783413429949052,-0.49826930032901373,-0.8532908816321557,-0.15369572431610945,-0.5053110118353112,-0.8532908816321557,-0.12866799385003405,-0.511096440548294,-0.8532908816321557,-0.10332037447804103,-0.5156112029806169,-0.8532908816321557,-0.07771588437571494,-0.5188440747215177,-0.8532908816321557,-0.051918180339748356,-0.52078701834247,-0.8532908816321557,-0.025991399527116994,-0.521435203379498,-0.8532908816321557,-6.385739527814514e-17,-0.52078701834247,-0.8532908816321557,0.025991399527116866,-0.5188440747215177,-0.8532908816321557,0.051918180339748224,-0.5156112029806169,-0.8532908816321557,0.0777158843757148,-0.5110964405482941,-0.8532908816321557,0.10332037447804089,-0.5053110118353112,-0.8532908816321557,0.1286679938500339,-0.49826930032901384,-0.8532908816321557,0.1536957243161091,-0.48998881283371365,-0.8532908816321557,0.1783413429949051,-0.4804901359460154,-0.8532908816321557,0.20254357699516937,-0.469796884873294,-0.8532908816321557,0.2262422557498209,-0.4579356447225679,-0.8532908816321557,0.24937846060946098,-0.4449359044057343,-0.8532908816321557,0.2718946713234184,-0.43082998332548794,-0.8532908816321557,0.2937349090441554,-0.4156529510241938,-0.8532908816321557,0.314844875499503,-0.3994425399954786,-0.8532908816321557,0.335172087986722,-0.3822390518753073,-0.8532908816321557,0.3546660098527692,-0.364085257245765,-0.8532908816321557,0.37327817613638165,-0.34502628930065204,-0.8532908816321557,0.390962314059605,-0.32510953163725165,-0.8532908816321557,0.4076744580692115,-0.304384500453243,-0.8532908816321557,0.4233730591419914,-0.2829027214416289,-0.8532908816321557,0.4380190880821731,-0.2607176016897488,-0.8532908816321557,0.45157613255415074,-0.23788429690084112,-0.8532908816321557,0.4640104876092898,-0.21445957426827503,-0.8532908816321557,0.47529123948173996,-0.19050167134336568,-0.8532908816321557,0.48539034244492624,-0.1660701512476424,-0.8532908816321557,0.49428268853764606,-0.14122575458954606,-0.8532908816321557,0.5019461699864157,-0.11603024845370674,-0.8532908816321557,0.5083617341688785,-0.09054627283823502,-0.8532908816321557,0.5135134309816272,-0.06483718492182201,-0.8532908816321557,0.5173884524946724,-0.0389669015478063,-0.8532908816321557,0.5199771647939764,-0.012999740316833746,-0.8532908816321557,0.5212731319328796,0.012999740316833555,-0.8532908816321557,0.5212731319328796,0.03896690154780611,-0.8532908816321557,0.5199771647939764,0.06483718492182136,-0.8532908816321557,0.5173884524946725,0.09054627283823483,-0.8532908816321557,0.5135134309816272,0.11603024845370655,-0.8532908816321557,0.5083617341688785,0.1412257545895463,-0.8532908816321557,0.5019461699864156,0.1660701512476422,-0.8532908816321557,0.4942826885376461,0.19050167134336507,-0.8532908816321557,0.48539034244492646,0.21445957426827486,-0.8532908816321557,0.47529123948174,0.23788429690084095,-0.8532908816321557,0.4640104876092899,0.26071760168974906,-0.8532908816321557,0.45157613255415063,0.2829027214416292,-0.8532908816321557,0.438019088082173,0.3043845004532425,-0.8532908816321557,0.42337305914199175,0.32510953163725154,-0.8532908816321557,0.4076744580692116,0.3450262893006515,-0.8532908816321557,0.39096231405960546,0.364085257245765,-0.8532908816321557,0.37327817613638165,0.3822390518753073,-0.8532908816321557,0.3546660098527692,0.39944253999547846,-0.8532908816321557,0.33517208798672216,0.4156529510241937,-0.8532908816321557,0.31484487549950324,0.4308299833254877,-0.8532908816321557,0.2937349090441557,0.44493590440573433,-0.8532908816321557,0.2718946713234184,0.45793564472256776,-0.8532908816321557,0.24937846060946134,0.46979688487329396,-0.8532908816321557,0.2262422557498211,0.4804901359460154,-0.8532908816321557,0.2025435769951693,0.4899888128337135,-0.8532908816321557,0.1783413429949055,0.49826930032901373,-0.8532908816321557,0.1536957243161095,0.5053110118353112,-0.8532908816321557,0.1286679938500341,0.511096440548294,-0.8532908816321557,0.10332037447804109,0.5156112029806169,-0.8532908816321557,0.07771588437571476,0.5188440747215176,-0.8532908816321557,0.05191818033974865,0.52078701834247,-0.8532908816321557,0.025991399527117057,0.521435203379498,-0.8532908816321557,0.0,0.5633200580636221,-0.8262387743159948,0.0,0.5626198068524877,-0.8262387743159948,-0.028079187204616593,0.5605207941539289,-0.8262387743159948,-0.05608856512562525,0.5570282384442388,-0.8262387743159948,-0.0839584980362964,0.5521508227672066,-0.8262387743159948,-0.11161969689216741,0.5459006731467015,-0.8262387743159948,-0.1390033915945245,0.538293328439467,-0.8262387743159948,-0.1660415019637036,0.5293477017030751,-0.8262387743159948,-0.19266680699714403,0.5190860331750845,-0.8262387743159948,-0.21881311199139225,0.5075338349803066,-0.8262387743159948,-0.2444154131125643,0.49471982770364265,-0.8262387743159948,-0.26941005900611925,0.4806758689861852,-0.8262387743159948,-0.2937349090441554,0.4654368743221022,-0.8262387743159948,-0.31732948781680254,0.44904073025321734,-0.8262387743159948,-0.340135135483619,0.4315282001770977,-0.8262387743159948,-0.36209515361119815,0.4129428230028246,-0.8262387743159948,-0.3831549461344071,0.39333080490640526,-0.8262387743159948,-0.40326215509080676,0.37274090445493924,-0.8262387743159948,-0.4223667907907964,0.35122431138513804,-0.8262387743159948,-0.4404213560998583,0.32883451933757385,-0.8262387743159948,-0.4573809645239178,0.30562719286306084,-0.8262387743159948,-0.4732034518042403,0.281660029031811,-0.8262387743159948,-0.48784948074442175,0.256992613989427,-0.8262387743159948,-0.5012826390088568,0.2316862748163551,-0.8262387743159948,-0.5134695296495431,0.20580392705910078,-0.8262387743159948,-0.5243798541361546,0.17940991831226738,-0.8262387743159948,-0.5339864876829639,0.15256986824029625,-0.8262387743159948,-0.5422655466853312,0.12535050543664247,-0.8262387743159948,-0.5491964480981107,0.0978195015259774,-0.8262387743159948,-0.5547619606083422,0.07004530292186728,-0.8262387743159948,-0.5589482474750113,0.0420969606582045,-0.8262387743159948,-0.5617449009293668,0.014043958717457564,-0.8262387743159948,-0.5631449680502747,-0.014043958717457497,-0.8262387743159948,-0.5631449680502747,-0.04209696065820443,-0.8262387743159948,-0.5617449009293668,-0.07004530292186721,-0.8262387743159948,-0.5589482474750113,-0.09781950152597733,-0.8262387743159948,-0.5547619606083422,-0.1253505054366424,-0.8262387743159948,-0.5491964480981107,-0.15256986824029617,-0.8262387743159948,-0.5422655466853313,-0.1794099183122673,-0.8262387743159948,-0.5339864876829639,-0.20580392705910072,-0.8262387743159948,-0.5243798541361547,-0.23168627481635493,-0.8262387743159948,-0.5134695296495431,-0.25699261398942674,-0.8262387743159948,-0.501282639008857,-0.28166002903181114,-0.8262387743159948,-0.4878494807444217,-0.30562719286306084,-0.8262387743159948,-0.47320345180424034,-0.3288345193375737,-0.8262387743159948,-0.45738096452391785,-0.351224311385138,-0.8262387743159948,-0.44042135609985833,-0.3727409044549391,-0.8262387743159948,-0.42236679079079653,-0.3933308049064052,-0.8262387743159948,-0.4032621550908068,-0.4129428230028246,-0.8262387743159948,-0.3831549461344071,-0.43152820017709764,-0.8262387743159948,-0.36209515361119826,-0.44904073025321734,-0.8262387743159948,-0.340135135483619,-0.4654368743221021,-0.8262387743159948,-0.31732948781680265,-0.48067586898618514,-0.8262387743159948,-0.29373490904415545,-0.49471982770364276,-0.8262387743159948,-0.26941005900611914,-0.5075338349803066,-0.8262387743159948,-0.24441541311256434,-0.5190860331750846,-0.8262387743159948,-0.2188131119913922,-0.5293477017030751,-0.8262387743159948,-0.19266680699714414,-0.538293328439467,-0.8262387743159948,-0.16604150196370382,-0.5459006731467014,-0.8262387743159948,-0.1390033915945246,-0.5521508227672066,-0.8262387743159948,-0.11161969689216743,-0.5570282384442388,-0.8262387743159948,-0.08395849803629656,-0.5605207941539289,-0.8262387743159948,-0.056088565125625295,-0.5626198068524877,-0.8262387743159948,-0.028079187204616773,-0.5633200580636221,-0.8262387743159948,-6.89868106003116e-17,-0.5626198068524877,-0.8262387743159948,0.02807918720461663,-0.5605207941539289,-0.8262387743159948,0.056088565125625156,-0.5570282384442388,-0.8262387743159948,0.08395849803629642,-0.5521508227672066,-0.8262387743159948,0.11161969689216729,-0.5459006731467015,-0.8262387743159948,0.1390033915945245,-0.5382933284394671,-0.8262387743159948,0.16604150196370346,-0.5293477017030751,-0.8262387743159948,0.192666806997144,-0.5190860331750845,-0.8262387743159948,0.21881311199139228,-0.5075338349803066,-0.8262387743159948,0.24441541311256423,-0.49471982770364276,-0.8262387743159948,0.26941005900611903,-0.4806758689861852,-0.8262387743159948,0.29373490904415533,-0.4654368743221022,-0.8262387743159948,0.31732948781680254,-0.4490407302532174,-0.8262387743159948,0.3401351354836189,-0.4315282001770977,-0.8262387743159948,0.36209515361119815,-0.41294282300282475,-0.8262387743159948,0.3831549461344069,-0.3933308049064053,-0.8262387743159948,0.4032621550908067,-0.3727409044549394,-0.8262387743159948,0.4223667907907963,-0.35122431138513804,-0.8262387743159948,0.4404213560998582,-0.3288345193375742,-0.8262387743159948,0.4573809645239175,-0.3056271928630607,-0.8262387743159948,0.47320345180424034,-0.2816600290318108,-0.8262387743159948,0.4878494807444218,-0.2569926139894271,-0.8262387743159948,0.5012826390088568,-0.23168627481635506,-0.8262387743159948,0.5134695296495431,-0.2058039270591012,-0.8262387743159948,0.5243798541361545,-0.17940991831226769,-0.8262387743159948,0.5339864876829638,-0.15256986824029595,-0.8262387743159948,0.5422655466853313,-0.12535050543664256,-0.8262387743159948,0.5491964480981107,-0.09781950152597735,-0.8262387743159948,0.5547619606083422,-0.0700453029218676,-0.8262387743159948,0.5589482474750112,-0.04209696065820469,-0.8262387743159948,0.5617449009293668,-0.014043958717457634,-0.8262387743159948,0.5631449680502747,0.014043958717457426,-0.8262387743159948,0.5631449680502747,0.04209696065820449,-0.8262387743159948,0.5617449009293668,0.07004530292186689,-0.8262387743159948,0.5589482474750113,0.09781950152597715,-0.8262387743159948,0.5547619606083423,0.12535050543664233,-0.8262387743159948,0.5491964480981107,0.15256986824029622,-0.8262387743159948,0.5422655466853312,0.17940991831226746,-0.8262387743159948,0.5339864876829638,0.20580392705910053,-0.8262387743159948,0.5243798541361548,0.23168627481635487,-0.8262387743159948,0.5134695296495431,0.2569926139894269,-0.8262387743159948,0.501282639008857,0.2816600290318111,-0.8262387743159948,0.4878494807444217,0.30562719286306095,-0.8262387743159948,0.4732034518042402,0.32883451933757357,-0.8262387743159948,0.4573809645239179,0.3512243113851379,-0.8262387743159948,0.44042135609985833,0.37274090445493885,-0.8262387743159948,0.42236679079079675,0.3933308049064053,-0.8262387743159948,0.4032621550908067,0.41294282300282475,-0.8262387743159948,0.3831549461344069,0.4315282001770976,-0.8262387743159948,0.3620951536111983,0.4490407302532173,-0.8262387743159948,0.34013513548361907,0.4654368743221019,-0.8262387743159948,0.3173294878168029,0.4806758689861853,-0.8262387743159948,0.29373490904415533,0.49471982770364253,-0.8262387743159948,0.2694100590061195,0.5075338349803066,-0.8262387743159948,0.2444154131125644,0.5190860331750845,-0.8262387743159948,0.21881311199139225,0.529347701703075,-0.8262387743159948,0.19266680699714445,0.538293328439467,-0.8262387743159948,0.16604150196370387,0.5459006731467014,-0.8262387743159948,0.13900339159452466,0.5521508227672066,-0.8262387743159948,0.1116196968921675,0.5570282384442388,-0.8262387743159948,0.08395849803629638,0.5605207941539287,-0.8262387743159948,0.05608856512562561,0.5626198068524877,-0.8262387743159948,0.02807918720461684,0.5633200580636221,-0.8262387743159948,0.0,0.6038044103254774,-0.7971325072229225,0.0,0.6030538338750807,-0.7971325072229225,-0.03009716559850842,0.6008039705752087,-0.7971325072229225,-0.06011950490116467,0.5970604139405183,-0.7971325072229225,-0.08999237764207785,0.5918324710426461,-0.7971325072229225,-0.11964151515277985,0.5851331393713622,-0.7971325072229225,-0.14899320500583704,0.5769790745207626,-0.7971325072229225,-0.17797447427555926,0.5673905487808378,-0.7971325072229225,-0.20651327096019065,0.5563914007373661,-0.7971325072229225,-0.23453864311453898,0.5440089760054331,-0.7971325072229225,-0.26198091524769046,0.5302740592439238,-0.7971325072229225,-0.2887718615472585,0.5152207976200105,-0.7971325072229225,-0.3148448754995032,0.4988866159139145,-0.7971325072229225,-0.340135135483619,0.48131212347500607,-0.7971325072229225,-0.36457976592849745,0.46254101326056324,-0.7971325072229225,-0.38811799363130395,0.4426199532081957,-0.7971325072229225,-0.4106912988492355,0.42159847021199887,-0.7971325072229225,-0.4322435607888217,0.39952882699089126,-0.7971325072229225,-0.45272119713105985,0.3764658921552614,-0.7971325072229225,-0.47207329724550257,0.3524670037949561,-0.7971325072229225,-0.4902517487621068,0.32759182692775574,-0.7971325072229225,-0.5072113571861663,0.30190220516273863,-0.7971325072229225,-0.5229099582589465,0.27546200694732503,-0.7971325072229225,-0.5373085227846747,0.24833696678025277,-0.7971325072229225,-0.5503712536632714,0.220594521785257,-0.7971325072229225,-0.5620656748875804,0.19230364405175487,-0.7971325072229225,-0.5723627122838397,0.1635346691593635,-0.7971325072229225,-0.5812367657946588,0.13435912131256694,-0.7971325072229225,-0.5886657731247948,0.10484953552027465,-0.7971325072229225,-0.5946312645914946,0.07507927726236016,-0.7971325072229225,-0.5991184090430364,0.045122360091518716,-0.7971325072229225,-0.6021160507313094,0.01505326162391344,-0.7971325072229225,-0.6036167370467617,-0.015053261623913365,-0.7971325072229225,-0.6036167370467617,-0.04512236009151865,-0.7971325072229225,-0.6021160507313094,-0.0750792772623601,-0.7971325072229225,-0.5991184090430364,-0.10484953552027458,-0.7971325072229225,-0.5946312645914946,-0.1343591213125669,-0.7971325072229225,-0.5886657731247948,-0.16353466915936338,-0.7971325072229225,-0.5812367657946588,-0.1923036440517548,-0.7971325072229225,-0.5723627122838397,-0.22059452178525693,-0.7971325072229225,-0.5620656748875804,-0.2483369667802526,-0.7971325072229225,-0.5503712536632716,-0.27546200694732476,-0.7971325072229225,-0.5373085227846749,-0.3019022051627388,-0.7971325072229225,-0.5229099582589464,-0.32759182692775574,-0.7971325072229225,-0.5072113571861664,-0.3524670037949559,-0.7971325072229225,-0.49025174876210686,-0.37646589215526133,-0.7971325072229225,-0.4720732972455026,-0.39952882699089115,-0.7971325072229225,-0.45272119713105996,-0.42159847021199875,-0.7971325072229225,-0.43224356078882176,-0.4426199532081957,-0.7971325072229225,-0.4106912988492355,-0.4625410132605631,-0.7971325072229225,-0.38811799363130406,-0.48131212347500607,-0.7971325072229225,-0.36457976592849745,-0.4988866159139144,-0.7971325072229225,-0.3401351354836191,-0.5152207976200104,-0.7971325072229225,-0.31484487549950324,-0.5302740592439239,-0.7971325072229225,-0.2887718615472584,-0.5440089760054331,-0.7971325072229225,-0.2619809152476905,-0.5563914007373661,-0.7971325072229225,-0.23453864311453892,-0.5673905487808378,-0.7971325072229225,-0.20651327096019076,-0.5769790745207626,-0.7971325072229225,-0.17797447427555949,-0.5851331393713622,-0.7971325072229225,-0.14899320500583718,-0.5918324710426461,-0.7971325072229225,-0.11964151515277988,-0.5970604139405183,-0.7971325072229225,-0.08999237764207801,-0.6008039705752087,-0.7971325072229225,-0.06011950490116471,-0.6030538338750807,-0.7971325072229225,-0.030097165598508613,-0.6038044103254774,-0.7971325072229225,-7.394471384161509e-17,-0.6030538338750807,-0.7971325072229225,0.03009716559850846,-0.6008039705752087,-0.7971325072229225,0.060119504901164564,-0.5970604139405183,-0.7971325072229225,0.08999237764207788,-0.5918324710426462,-0.7971325072229225,0.11964151515277972,-0.5851331393713622,-0.7971325072229225,0.14899320500583704,-0.5769790745207627,-0.7971325072229225,0.1779744742755591,-0.5673905487808378,-0.7971325072229225,0.20651327096019062,-0.5563914007373661,-0.7971325072229225,0.23453864311453904,-0.5440089760054331,-0.7971325072229225,0.2619809152476904,-0.5302740592439239,-0.7971325072229225,0.2887718615472583,-0.5152207976200105,-0.7971325072229225,0.3148448754995031,-0.4988866159139145,-0.7971325072229225,0.340135135483619,-0.4813121234750061,-0.7971325072229225,0.36457976592849733,-0.46254101326056324,-0.7971325072229225,0.38811799363130395,-0.44261995320819586,-0.7971325072229225,0.4106912988492354,-0.4215984702119989,-0.7971325072229225,0.43224356078882165,-0.3995288269908915,-0.7971325072229225,0.4527211971310597,-0.3764658921552614,-0.7971325072229225,0.47207329724550245,-0.35246700379495643,-0.7971325072229225,0.49025174876210653,-0.3275918269277556,-0.7971325072229225,0.5072113571861664,-0.30190220516273847,-0.7971325072229225,0.5229099582589465,-0.2754620069473251,-0.7971325072229225,0.5373085227846747,-0.24833696678025274,-0.7971325072229225,0.5503712536632714,-0.22059452178525743,-0.7971325072229225,0.5620656748875803,-0.1923036440517552,-0.7971325072229225,0.5723627122838396,-0.16353466915936313,-0.7971325072229225,0.5812367657946589,-0.13435912131256703,-0.7971325072229225,0.5886657731247948,-0.1048495355202746,-0.7971325072229225,0.5946312645914946,-0.07507927726236051,-0.7971325072229225,0.5991184090430364,-0.04512236009151892,-0.7971325072229225,0.6021160507313094,-0.015053261623913513,-0.7971325072229225,0.6036167370467617,0.015053261623913291,-0.7971325072229225,0.6036167370467617,0.0451223600915187,-0.7971325072229225,0.6021160507313094,0.07507927726235976,-0.7971325072229225,0.5991184090430365,0.10484953552027439,-0.7971325072229225,0.5946312645914947,0.1343591213125668,-0.7971325072229225,0.5886657731247948,0.16353466915936343,-0.7971325072229225,0.5812367657946588,0.19230364405175498,-0.7971325072229225,0.5723627122838396,0.2205945217852567,-0.7971325072229225,0.5620656748875805,0.24833696678025255,-0.7971325072229225,0.5503712536632716,0.2754620069473249,-0.7971325072229225,0.5373085227846748,0.30190220516273875,-0.7971325072229225,0.5229099582589464,0.32759182692775585,-0.7971325072229225,0.5072113571861663,0.3524670037949558,-0.7971325072229225,0.490251748762107,0.3764658921552612,-0.7971325072229225,0.4720732972455026,0.3995288269908909,-0.7971325072229225,0.4527211971310602,0.4215984702119989,-0.7971325072229225,0.43224356078882165,0.44261995320819586,-0.7971325072229225,0.4106912988492354,0.46254101326056307,-0.7971325072229225,0.38811799363130417,0.481312123475006,-0.7971325072229225,0.3645797659284975,0.4988866159139142,-0.7971325072229225,0.3401351354836194,0.5152207976200106,-0.7971325072229225,0.3148448754995031,0.5302740592439237,-0.7971325072229225,0.2887718615472587,0.5440089760054331,-0.7971325072229225,0.26198091524769057,0.5563914007373661,-0.7971325072229225,0.23453864311453898,0.5673905487808376,-0.7971325072229225,0.2065132709601911,0.5769790745207626,-0.7971325072229225,0.17797447427555957,0.5851331393713622,-0.7971325072229225,0.14899320500583726,0.5918324710426461,-0.7971325072229225,0.11964151515277995,0.5970604139405183,-0.7971325072229225,0.08999237764207782,0.6008039705752086,-0.7971325072229225,0.06011950490116505,0.6030538338750807,-0.7971325072229225,0.030097165598508682,0.6038044103254774,-0.7971325072229225,0.0,0.6427876096865394,-0.766044443118978,0.0,0.6419885740481984,-0.766044443118978,-0.032040317696548075,0.6395934536616703,-0.766044443118978,-0.06400097811496103,0.6356082031736161,-0.766044443118978,-0.09580252201764812,0.6300427305446741,-0.766044443118978,-0.1273658857557475,0.6229108724167074,-0.766044443118978,-0.15861259783381462,0.6142303597127331,-0.766044443118978,-0.18946497400232412,0.6040227735550537,-0.766044443118978,-0.2198463103929542,0.5923134916111864,-0.766044443118978,-0.2496810742164889,0.5791316250009835,-0.766044443118978,-0.2788950915492332,0.5645099459218009,-0.766044443118978,-0.3074157317410744,0.5484848061716532,-0.766044443118978,-0.33517208798672204,0.5310960467729144,-0.766044443118978,-0.3620951536111982,0.5123868989212604,-0.766044443118978,-0.388117993631304,0.49240387650610407,-0.766044443118978,-0.4131759111665348,0.4711966604697375,-0.766044443118978,-0.4372066082857186,0.4488179752926818,-0.766044443118978,-0.4601503408894886,0.42532345791232096,-0.766044443118978,-0.4819500672435279,0.40077151940070943,-0.766044443118978,-0.5025515897933084,0.3752231997454433,-0.766044443118978,-0.5219036899077512,0.34874201609463396,-0.766044443118978,-0.539958255216813,0.3213938048432696,-0.766044443118978,-0.5566703992264195,0.2932465579535645,-0.766044443118978,-0.571998572913361,0.2643702539162271,-0.766044443118978,-0.5859046680227121,0.23483668377290676,-0.766044443118978,-0.5983541118109565,0.2047192726323531,-0.766044443118978,-0.6093159529992754,0.1740928971240256,-0.766044443118978,-0.6187629387233032,0.14303369924299567,-0.766044443118978,-0.6266715822880428,0.11161889704894971,-0.766044443118978,-0.6330222215594891,0.0799265926899261,-0.766044443118978,-0.6377990678477929,0.04803557822806912,-0.766044443118978,-0.6409902451604313,0.01602513975014792,-0.766044443118978,-0.6425878197277964,-0.016025139750147844,-0.766044443118978,-0.6425878197277964,-0.04803557822806904,-0.766044443118978,-0.6409902451604313,-0.07992659268992602,-0.766044443118978,-0.6377990678477929,-0.11161889704894963,-0.766044443118978,-0.6330222215594891,-0.14303369924299558,-0.766044443118978,-0.6266715822880428,-0.1740928971240255,-0.766044443118978,-0.6187629387233032,-0.20471927263235304,-0.766044443118978,-0.6093159529992754,-0.2348366837729067,-0.766044443118978,-0.5983541118109565,-0.2643702539162269,-0.766044443118978,-0.5859046680227122,-0.2932465579535642,-0.766044443118978,-0.5719985729133612,-0.32139380484326985,-0.766044443118978,-0.5566703992264194,-0.34874201609463396,-0.766044443118978,-0.5399582552168131,-0.3752231997454431,-0.766044443118978,-0.5219036899077512,-0.4007715194007093,-0.766044443118978,-0.5025515897933085,-0.4253234579123208,-0.766044443118978,-0.481950067243528,-0.4488179752926817,-0.766044443118978,-0.4601503408894887,-0.4711966604697375,-0.766044443118978,-0.4372066082857186,-0.49240387650610395,-0.766044443118978,-0.41317591116653496,-0.5123868989212604,-0.766044443118978,-0.388117993631304,-0.5310960467729143,-0.766044443118978,-0.3620951536111983,-0.5484848061716531,-0.766044443118978,-0.3351720879867221,-0.564509945921801,-0.766044443118978,-0.30741573174107434,-0.5791316250009834,-0.766044443118978,-0.2788950915492333,-0.5923134916111865,-0.766044443118978,-0.24968107421648886,-0.6040227735550536,-0.766044443118978,-0.2198463103929543,-0.614230359712733,-0.766044443118978,-0.18946497400232437,-0.6229108724167073,-0.766044443118978,-0.15861259783381476,-0.6300427305446741,-0.766044443118978,-0.12736588575574753,-0.6356082031736161,-0.766044443118978,-0.0958025220176483,-0.6395934536616703,-0.766044443118978,-0.06400097811496107,-0.6419885740481984,-0.766044443118978,-0.03204031769654828,-0.6427876096865394,-0.766044443118978,-7.871877887341986e-17,-0.6419885740481984,-0.766044443118978,0.03204031769654812,-0.6395934536616703,-0.766044443118978,0.06400097811496092,-0.6356082031736161,-0.766044443118978,0.09580252201764815,-0.6300427305446741,-0.766044443118978,0.12736588575574737,-0.6229108724167074,-0.766044443118978,0.15861259783381462,-0.6142303597127331,-0.766044443118978,0.18946497400232395,-0.6040227735550537,-0.766044443118978,0.21984631039295416,-0.5923134916111864,-0.766044443118978,0.24968107421648894,-0.5791316250009835,-0.766044443118978,0.27889509154923314,-0.564509945921801,-0.766044443118978,0.3074157317410742,-0.5484848061716532,-0.766044443118978,0.33517208798672193,-0.5310960467729144,-0.766044443118978,0.3620951536111982,-0.5123868989212605,-0.766044443118978,0.3881179936313039,-0.49240387650610407,-0.766044443118978,0.4131759111665348,-0.4711966604697376,-0.766044443118978,0.43720660828571845,-0.44881797529268186,-0.766044443118978,0.46015034088948853,-0.4253234579123211,-0.766044443118978,0.48195006724352774,-0.40077151940070943,-0.766044443118978,0.5025515897933083,-0.3752231997454437,-0.766044443118978,0.5219036899077508,-0.34874201609463384,-0.766044443118978,0.5399582552168131,-0.32139380484326946,-0.766044443118978,0.5566703992264195,-0.2932465579535646,-0.766044443118978,0.571998572913361,-0.26437025391622704,-0.766044443118978,0.5859046680227121,-0.23483668377290723,-0.766044443118978,0.5983541118109563,-0.20471927263235346,-0.766044443118978,0.6093159529992752,-0.17409289712402526,-0.766044443118978,0.6187629387233033,-0.14303369924299575,-0.766044443118978,0.6266715822880428,-0.11161889704894966,-0.766044443118978,0.6330222215594891,-0.07992659268992645,-0.766044443118978,0.6377990678477928,-0.04803557822806934,-0.766044443118978,0.6409902451604313,-0.016025139750148,-0.766044443118978,0.6425878197277964,0.016025139750147764,-0.766044443118978,0.6425878197277964,0.0480355782280691,-0.766044443118978,0.6409902451604313,0.07992659268992566,-0.766044443118978,0.6377990678477929,0.11161889704894942,-0.766044443118978,0.6330222215594891,0.1430336992429955,-0.766044443118978,0.6266715822880428,0.17409289712402556,-0.766044443118978,0.6187629387233032,0.2047192726323532,-0.766044443118978,0.6093159529992753,0.23483668377290648,-0.766044443118978,0.5983541118109567,0.2643702539162268,-0.766044443118978,0.5859046680227122,0.2932465579535644,-0.766044443118978,0.5719985729133611,0.32139380484326974,-0.766044443118978,0.5566703992264194,0.3487420160946341,-0.766044443118978,0.5399582552168128,0.37522319974544305,-0.766044443118978,0.5219036899077513,0.40077151940070926,-0.766044443118978,0.5025515897933085,0.4253234579123205,-0.766044443118978,0.48195006724352824,0.44881797529268186,-0.766044443118978,0.46015034088948853,0.4711966604697376,-0.766044443118978,0.43720660828571845,0.4924038765061039,-0.766044443118978,0.413175911166535,0.5123868989212603,-0.766044443118978,0.38811799363130406,0.5310960467729141,-0.766044443118978,0.3620951536111986,0.5484848061716533,-0.766044443118978,0.33517208798672193,0.5645099459218008,-0.766044443118978,0.30741573174107467,0.5791316250009834,-0.766044443118978,0.27889509154923336,0.5923134916111864,-0.766044443118978,0.2496810742164889,0.6040227735550535,-0.766044443118978,0.21984631039295466,0.614230359712733,-0.766044443118978,0.18946497400232445,0.6229108724167073,-0.766044443118978,0.15861259783381484,0.6300427305446741,-0.766044443118978,0.1273658857557476,0.6356082031736161,-0.766044443118978,0.09580252201764809,0.6395934536616702,-0.766044443118978,0.06400097811496143,0.6419885740481984,-0.766044443118978,0.03204031769654836,0.6427876096865394,-0.766044443118978,0.0,0.6801727377709194,-0.7330518718298263,0.0,0.6793272294731288,-0.7330518718298263,-0.0339038125164526,0.6767928066466057,-0.7330518718298263,-0.06772333481303153,0.6725757702658258,-0.7330518718298263,-0.10137448622863066,0.6666866045476773,-0.7330518718298263,-0.13477360469868288,0.6591399508860448,-0.7330518718298263,-0.16783765475223456,0.6499545714509983,-0.7330518718298263,-0.20048443395120955,0.6391533025430817,-0.7330518718298263,-0.23263277725862233,0.6267629978186708,-0.7330518718298263,-0.26420275882764843,0.6128144615275543,-0.7330518718298263,-0.2951158907098728,0.5973423719287126,-0.7330518718298263,-0.3252953179886967,0.5803851950747049,-0.7330518718298263,-0.3546660098527693,0.561985089178999,-0.7330518718298263,-0.3831549461344071,0.5421877998040123,-0.7330518718298263,-0.4106912988492355,0.5210425461304345,-0.7330518718298263,-0.4372066082857185,0.49860189859059006,-0.7330518718298263,-0.4626349532067878,0.47492164817005955,-0.7330518718298263,-0.4869131147404247,0.45006066770249975,-0.7330518718298263,-0.5099807335517371,0.4240807655025029,-0.7330518718298263,-0.5317804599057764,0.3970465317003873,-0.7330518718298263,-0.5522580962480145,0.3690251776609572,-0.7330518718298263,-0.5713627319480041,0.3400863688854596,-0.7330518718298263,-0.5890468698712276,0.31030205181217174,-0.7330518718298263,-0.6052665444644567,0.2797462749462186,-0.7330518718298263,-0.619981431061046,0.24849500476332304,-0.7330518718298263,-0.633154946134407,0.21662593684517747,-0.7330518718298263,-0.6447543382504201,0.1842183027159853,-0.7330518718298263,-0.6547507694926592,0.15135267286040832,-0.7330518718298263,-0.6631193871579975,0.11811075641264708,-0.7330518718298263,-0.6698393855443409,0.08457519801465865,-0.7330518718298263,-0.6748940576768839,0.0508293723485553,-0.7330518718298263,-0.6782708368442799,0.016957176854007976,-0.7330518718298263,-0.679961327841466,-0.016957176854007893,-0.7330518718298263,-0.679961327841466,-0.05082937234855522,-0.7330518718298263,-0.6782708368442799,-0.08457519801465857,-0.7330518718298263,-0.6748940576768839,-0.118110756412647,-0.7330518718298263,-0.6698393855443409,-0.15135267286040827,-0.7330518718298263,-0.6631193871579975,-0.1842183027159852,-0.7330518718298263,-0.6547507694926593,-0.2166259368451774,-0.7330518718298263,-0.6447543382504201,-0.24849500476332295,-0.7330518718298263,-0.6331549461344071,-0.2797462749462184,-0.7330518718298263,-0.6199814310610461,-0.3103020518121714,-0.7330518718298263,-0.6052665444644568,-0.34008636888545984,-0.7330518718298263,-0.5890468698712275,-0.3690251776609572,-0.7330518718298263,-0.5713627319480041,-0.39704653170038706,-0.7330518718298263,-0.5522580962480146,-0.4240807655025028,-0.7330518718298263,-0.5317804599057765,-0.45006066770249964,-0.7330518718298263,-0.5099807335517373,-0.4749216481700595,-0.7330518718298263,-0.4869131147404248,-0.49860189859059006,-0.7330518718298263,-0.4626349532067878,-0.5210425461304345,-0.7330518718298263,-0.43720660828571867,-0.5421877998040123,-0.7330518718298263,-0.4106912988492355,-0.5619850891789989,-0.7330518718298263,-0.3831549461344072,-0.5803851950747048,-0.7330518718298263,-0.35466600985276936,-0.5973423719287128,-0.7330518718298263,-0.32529531798869665,-0.6128144615275541,-0.7330518718298263,-0.29511589070987293,-0.626762997818671,-0.7330518718298263,-0.2642027588276484,-0.6391533025430816,-0.7330518718298263,-0.23263277725862244,-0.6499545714509982,-0.7330518718298263,-0.20048443395120982,-0.6591399508860447,-0.7330518718298263,-0.1678376547522347,-0.6666866045476773,-0.7330518718298263,-0.1347736046986829,-0.6725757702658258,-0.7330518718298263,-0.10137448622863085,-0.6767928066466057,-0.7330518718298263,-0.06772333481303158,-0.6793272294731288,-0.7330518718298263,-0.033903812516452816,-0.6801727377709194,-0.7330518718298263,-8.329713661784484e-17,-0.6793272294731288,-0.7330518718298263,0.03390381251645265,-0.6767928066466057,-0.7330518718298263,0.0677233348130314,-0.6725757702658258,-0.7330518718298263,0.10137448622863068,-0.6666866045476774,-0.7330518718298263,0.13477360469868274,-0.6591399508860448,-0.7330518718298263,0.16783765475223456,-0.6499545714509984,-0.7330518718298263,0.20048443395120935,-0.6391533025430817,-0.7330518718298263,0.23263277725862228,-0.6267629978186708,-0.7330518718298263,0.2642027588276485,-0.6128144615275543,-0.7330518718298263,0.29511589070987276,-0.5973423719287128,-0.7330518718298263,0.3252953179886965,-0.5803851950747049,-0.7330518718298263,0.35466600985276925,-0.561985089178999,-0.7330518718298263,0.3831549461344071,-0.5421877998040124,-0.7330518718298263,0.4106912988492354,-0.5210425461304345,-0.7330518718298263,0.4372066082857185,-0.49860189859059023,-0.7330518718298263,0.46263495320678766,-0.4749216481700596,-0.7330518718298263,0.48691311474042465,-0.45006066770250003,-0.7330518718298263,0.509980733551737,-0.4240807655025029,-0.7330518718298263,0.5317804599057763,-0.3970465317003877,-0.7330518718298263,0.5522580962480141,-0.36902517766095705,-0.7330518718298263,0.5713627319480041,-0.3400863688854594,-0.7330518718298263,0.5890468698712277,-0.3103020518121718,-0.7330518718298263,0.6052665444644566,-0.27974627494621856,-0.7330518718298263,0.619981431061046,-0.24849500476332353,-0.7330518718298263,0.6331549461344069,-0.21662593684517784,-0.7330518718298263,0.64475433825042,-0.1842183027159849,-0.7330518718298263,0.6547507694926594,-0.15135267286040843,-0.7330518718298263,0.6631193871579975,-0.11811075641264702,-0.7330518718298263,0.6698393855443409,-0.08457519801465903,-0.7330518718298263,0.6748940576768838,-0.05082937234855553,-0.7330518718298263,0.6782708368442799,-0.01695717685400806,-0.7330518718298263,0.679961327841466,0.01695717685400781,-0.7330518718298263,0.679961327841466,0.05082937234855528,-0.7330518718298263,0.6782708368442799,0.08457519801465818,-0.7330518718298263,0.6748940576768839,0.11811075641264678,-0.7330518718298263,0.669839385544341,0.15135267286040818,-0.7330518718298263,0.6631193871579975,0.18421830271598524,-0.7330518718298263,0.6547507694926592,0.21662593684517759,-0.7330518718298263,0.64475433825042,0.24849500476332273,-0.7330518718298263,0.6331549461344071,0.27974627494621834,-0.7330518718298263,0.6199814310610461,0.3103020518121716,-0.7330518718298263,0.6052665444644567,0.34008636888545973,-0.7330518718298263,0.5890468698712275,0.3690251776609574,-0.7330518718298263,0.571362731948004,0.397046531700387,-0.7330518718298263,0.5522580962480146,0.42408076550250273,-0.7330518718298263,0.5317804599057765,0.4500606677024993,-0.7330518718298263,0.5099807335517376,0.4749216481700596,-0.7330518718298263,0.48691311474042465,0.49860189859059023,-0.7330518718298263,0.46263495320678766,0.5210425461304344,-0.7330518718298263,0.4372066082857188,0.5421877998040123,-0.7330518718298263,0.4106912988492356,0.5619850891789987,-0.7330518718298263,0.38315494613440754,0.5803851950747049,-0.7330518718298263,0.35466600985276925,0.5973423719287125,-0.7330518718298263,0.325295317988697,0.6128144615275541,-0.7330518718298263,0.295115890709873,0.6267629978186708,-0.7330518718298263,0.26420275882764843,0.6391533025430814,-0.7330518718298263,0.2326327772586228,0.6499545714509982,-0.7330518718298263,0.20048443395120988,0.6591399508860447,-0.7330518718298263,0.16783765475223478,0.6666866045476773,-0.7330518718298263,0.134773604698683,0.6725757702658258,-0.7330518718298263,0.10137448622863063,0.6767928066466057,-0.7330518718298263,0.06772333481303196,0.6793272294731288,-0.7330518718298263,0.0339038125164529,0.6801727377709194,-0.7330518718298263,0.0,0.7158668492597184,-0.6982368180860727,0.0,0.7149769703693267,-0.6982368180860727,-0.03568301711648346,0.7123095460772846,-0.6982368180860727,-0.07127732063012225,0.7078712080206669,-0.6982368180860727,-0.1066944174940663,0.7016729906071967,-0.6982368180860727,-0.14184625522511773,0.6937303035819684,-0.6982368180860727,-0.1766454408160778,0.6840628937163934,-0.6982368180860727,-0.2110054580085328,0.6726947957146157,-0.6982368180860727,-0.2448408823859038,0.6596542724594495,-0.6982368180860727,-0.27806759375200635,0.6449737447464002,-0.6982368180860727,-0.310602985267112,0.6286897106804582,-0.6982368180860727,-0.3423661688215678,0.6108426549360587,-0.6982368180860727,-0.3732781761363817,0.5914769481058029,-0.6982368180860727,-0.40326215509080676,0.5706407363881733,-0.6982368180860727,-0.4322435607888217,0.5483858218884984,-0.6982368180860727,-0.46015034088948853,0.5247675338307567,-0.6982368180860727,-0.4869131147404247,0.4998445910004082,-0.6982368180860727,-0.5124653458690365,0.4736789557602415,-0.6982368180860727,-0.5367435074026733,0.44633568000217777,-0.6982368180860727,-0.5596872400064433,0.41788274341801684,-0.6982368180860727,-0.5812395019460295,0.38839088449121306,-0.6982368180860727,-0.6013467109024291,0.35793342462985916,-0.6982368180860727,-0.6199588771860416,0.32658608587811383,-0.6982368180860727,-0.6370297280189126,0.29442680265926785,-0.6982368180860727,-0.6525168225761517,0.2615355280184892,-0.6982368180860727,-0.6663816575005096,0.22799403484695524,-0.6982368180860727,-0.6785897626277911,0.19388571258156026,-0.6982368180860727,-0.6891107866851143,0.15929535988563673,-0.6982368180860727,-0.6979185727489575,0.12430897382611727,-0.6982368180860727,-0.7049912232753923,0.0890135360712763,-0.6982368180860727,-0.710311154540828,0.053496796640597526,-0.6982368180860727,-0.7138651403579187,0.017847055744399636,-0.6982368180860727,-0.7156443449579496,-0.01784705574439955,-0.6982368180860727,-0.7156443449579496,-0.053496796640597435,-0.6982368180860727,-0.7138651403579187,-0.08901353607127621,-0.6982368180860727,-0.710311154540828,-0.1243089738261172,-0.6982368180860727,-0.7049912232753923,-0.15929535988563664,-0.6982368180860727,-0.6979185727489575,-0.19388571258156012,-0.6982368180860727,-0.6891107866851143,-0.22799403484695516,-0.6982368180860727,-0.6785897626277911,-0.2615355280184891,-0.6982368180860727,-0.6663816575005097,-0.2944268026592677,-0.6982368180860727,-0.6525168225761517,-0.3265860858781135,-0.6982368180860727,-0.6370297280189127,-0.3579334246298594,-0.6982368180860727,-0.6199588771860415,-0.38839088449121306,-0.6982368180860727,-0.6013467109024292,-0.4178827434180166,-0.6982368180860727,-0.5812395019460296,-0.44633568000217766,-0.6982368180860727,-0.5596872400064433,-0.4736789557602413,-0.6982368180860727,-0.5367435074026735,-0.49984459100040807,-0.6982368180860727,-0.5124653458690365,-0.5247675338307567,-0.6982368180860727,-0.4869131147404247,-0.5483858218884983,-0.6982368180860727,-0.4601503408894887,-0.5706407363881733,-0.6982368180860727,-0.4322435607888217,-0.5914769481058028,-0.6982368180860727,-0.40326215509080693,-0.6108426549360586,-0.6982368180860727,-0.3732781761363818,-0.6286897106804582,-0.6982368180860727,-0.34236616882156773,-0.6449737447464002,-0.6982368180860727,-0.31060298526711205,-0.6596542724594495,-0.6982368180860727,-0.27806759375200624,-0.6726947957146155,-0.6982368180860727,-0.2448408823859039,-0.6840628937163933,-0.6982368180860727,-0.21100545800853307,-0.6937303035819683,-0.6982368180860727,-0.17664544081607797,-0.7016729906071967,-0.6982368180860727,-0.14184625522511776,-0.7078712080206669,-0.6982368180860727,-0.1066944174940665,-0.7123095460772846,-0.6982368180860727,-0.0712773206301223,-0.7149769703693267,-0.6982368180860727,-0.03568301711648369,-0.7158668492597184,-0.6982368180860727,-8.76684045561615e-17,-0.7149769703693267,-0.6982368180860727,0.03568301711648351,-0.7123095460772846,-0.6982368180860727,0.07127732063012213,-0.7078712080206669,-0.6982368180860727,0.10669441749406632,-0.7016729906071968,-0.6982368180860727,0.14184625522511757,-0.6937303035819684,-0.6982368180860727,0.1766454408160778,-0.6840628937163935,-0.6982368180860727,0.21100545800853257,-0.6726947957146157,-0.6982368180860727,0.24484088238590374,-0.6596542724594495,-0.6982368180860727,0.2780675937520064,-0.6449737447464002,-0.6982368180860727,0.3106029852671119,-0.6286897106804582,-0.6982368180860727,0.34236616882156756,-0.6108426549360587,-0.6982368180860727,0.37327817613638165,-0.5914769481058029,-0.6982368180860727,0.40326215509080676,-0.5706407363881734,-0.6982368180860727,0.43224356078882153,-0.5483858218884984,-0.6982368180860727,0.46015034088948853,-0.5247675338307569,-0.6982368180860727,0.4869131147404246,-0.49984459100040823,-0.6982368180860727,0.5124653458690364,-0.4736789557602417,-0.6982368180860727,0.5367435074026732,-0.44633568000217777,-0.6982368180860727,0.5596872400064432,-0.4178827434180173,-0.6982368180860727,0.5812395019460291,-0.3883908844912129,-0.6982368180860727,0.6013467109024292,-0.35793342462985894,-0.6982368180860727,0.6199588771860417,-0.32658608587811394,-0.6982368180860727,0.6370297280189124,-0.2944268026592678,-0.6982368180860727,0.6525168225761517,-0.2615355280184897,-0.6982368180860727,0.6663816575005095,-0.22799403484695566,-0.6982368180860727,0.6785897626277909,-0.19388571258155984,-0.6982368180860727,0.6891107866851144,-0.1592953598856368,-0.6982368180860727,0.6979185727489575,-0.12430897382611722,-0.6982368180860727,0.7049912232753923,-0.0890135360712767,-0.6982368180860727,0.7103111545408279,-0.05349679664059776,-0.6982368180860727,0.7138651403579187,-0.017847055744399723,-0.6982368180860727,0.7156443449579496,0.01784705574439946,-0.6982368180860727,0.7156443449579496,0.053496796640597505,-0.6982368180860727,0.7138651403579187,0.08901353607127581,-0.6982368180860727,0.710311154540828,0.12430897382611697,-0.6982368180860727,0.7049912232753924,0.15929535988563656,-0.6982368180860727,0.6979185727489575,0.1938857125815602,-0.6982368180860727,0.6891107866851143,0.22799403484695538,-0.6982368180860727,0.678589762627791,0.2615355280184889,-0.6982368180860727,0.6663816575005097,0.2944268026592676,-0.6982368180860727,0.6525168225761517,0.3265860858781137,-0.6982368180860727,0.6370297280189127,0.3579334246298593,-0.6982368180860727,0.6199588771860415,0.3883908844912132,-0.6982368180860727,0.601346710902429,0.41788274341801657,-0.6982368180860727,0.5812395019460296,0.4463356800021776,-0.6982368180860727,0.5596872400064433,0.473678955760241,-0.6982368180860727,0.5367435074026737,0.49984459100040823,-0.6982368180860727,0.5124653458690364,0.5247675338307569,-0.6982368180860727,0.4869131147404246,0.5483858218884983,-0.6982368180860727,0.46015034088948875,0.5706407363881733,-0.6982368180860727,0.43224356078882176,0.5914769481058025,-0.6982368180860727,0.40326215509080726,0.6108426549360587,-0.6982368180860727,0.37327817613638165,0.628689710680458,-0.6982368180860727,0.34236616882156806,0.6449737447464002,-0.6982368180860727,0.31060298526711216,0.6596542724594495,-0.6982368180860727,0.27806759375200635,0.6726947957146154,-0.6982368180860727,0.2448408823859043,0.6840628937163933,-0.6982368180860727,0.21100545800853313,0.6937303035819683,-0.6982368180860727,0.17664544081607805,0.7016729906071967,-0.6982368180860727,0.14184625522511782,0.7078712080206669,-0.6982368180860727,0.10669441749406626,0.7123095460772845,-0.6982368180860727,0.07127732063012271,0.7149769703693267,-0.6982368180860727,0.03568301711648377,0.7158668492597184,-0.6982368180860727,0.0,0.7497812029677342,-0.6616858375968594,0.0,0.7488491658638742,-0.6616858375968594,-0.03737350811366965,0.746055371743388,-0.6616858375968594,-0.07465409979751823,0.7414067664186554,-0.6616858375968594,-0.11174908962660932,0.734914907054958,-0.6616858375968594,-0.14856625361146125,0.7265959334375454,-0.6616858375968594,-0.18501405848141594,0.7164705278455856,-0.6616858375968594,-0.22100188925077208,0.7045638636327612,-0.6616858375968594,-0.25644027450191675,0.6909055426423449,-0.6616858375968594,-0.29124110882536736,0.6755295216123535,-0.6616858375968594,-0.32531787186370126,0.6584740277537462,-0.6616858375968594,-0.358585843414797,0.6397814637115562,-0.6616858375968594,-0.39096231405960513,0.6194983021452329,-0.6616858375968594,-0.4223667907907964,0.5976749701902889,-0.6616858375968594,-0.45272119713105985,0.5743657240884954,-0.6616858375968594,-0.4819500672435278,0.5496285142983165,-0.6616858375968594,-0.5099807335517371,0.5235248414209387,-0.6616858375968594,-0.5367435074026733,0.49611960330008603,-0.6616858375968594,-0.5621718523237427,0.4674809336757555,-0.6616858375968594,-0.5862025494429264,0.43768003279300355,-0.6616858375968594,-0.6087758546608579,0.40679099038691885,-0.6616858375968594,-0.6298356471840668,0.37489060148386705,-0.6616858375968594,-0.6493295690501142,0.3420581754769554,-0.6616858375968594,-0.6672091552977365,0.3083753389503846,-0.6616858375968594,-0.6834299544583762,0.27392583274289994,-0.6616858375968594,-0.6979516390695357,0.23879530375487198,-0.6616858375968594,-0.7107381059352039,0.2030710920166067,-0.6616858375968594,-0.7217575658840892,0.16684201354726919,-0.6616858375968594,-0.7309826228025093,0.13019813954426593,-0.6616858375968594,-0.7383903417454446,0.09323057245205615,-0.6616858375968594,-0.7439623059564272,0.05603121946712064,-0.6616858375968594,-0.7476846626544976,0.018692564042190152,-0.6616858375968594,-0.7495481574744022,-0.018692564042190062,-0.6616858375968594,-0.7495481574744022,-0.056031219467120544,-0.6616858375968594,-0.7476846626544976,-0.09323057245205607,-0.6616858375968594,-0.7439623059564272,-0.13019813954426585,-0.6616858375968594,-0.7383903417454446,-0.1668420135472691,-0.6616858375968594,-0.7309826228025093,-0.20307109201660659,-0.6616858375968594,-0.7217575658840893,-0.2387953037548719,-0.6616858375968594,-0.7107381059352039,-0.27392583274289983,-0.6616858375968594,-0.6979516390695358,-0.3083753389503844,-0.6616858375968594,-0.6834299544583763,-0.34205817547695505,-0.6616858375968594,-0.6672091552977366,-0.37489060148386727,-0.6616858375968594,-0.6493295690501141,-0.40679099038691885,-0.6616858375968594,-0.6298356471840669,-0.4376800327930033,-0.6616858375968594,-0.608775854660858,-0.46748093367575544,-0.6616858375968594,-0.5862025494429265,-0.49611960330008587,-0.6616858375968594,-0.5621718523237428,-0.5235248414209386,-0.6616858375968594,-0.5367435074026734,-0.5496285142983165,-0.6616858375968594,-0.5099807335517371,-0.5743657240884953,-0.6616858375968594,-0.48195006724352796,-0.5976749701902889,-0.6616858375968594,-0.45272119713105985,-0.6194983021452328,-0.6616858375968594,-0.4223667907907966,-0.6397814637115561,-0.6616858375968594,-0.39096231405960524,-0.6584740277537463,-0.6616858375968594,-0.3585858434147969,-0.6755295216123534,-0.6616858375968594,-0.32531787186370137,-0.690905542642345,-0.6616858375968594,-0.2912411088253673,-0.7045638636327612,-0.6616858375968594,-0.25644027450191687,-0.7164705278455855,-0.6616858375968594,-0.22100188925077235,-0.7265959334375452,-0.6616858375968594,-0.1850140584814161,-0.734914907054958,-0.6616858375968594,-0.14856625361146125,-0.7414067664186554,-0.6616858375968594,-0.11174908962660952,-0.746055371743388,-0.6616858375968594,-0.07465409979751829,-0.7488491658638742,-0.6616858375968594,-0.03737350811366989,-0.7497812029677342,-0.6616858375968594,-9.182171502752876e-17,-0.7488491658638742,-0.6616858375968594,0.037373508113669705,-0.746055371743388,-0.6616858375968594,0.0746540997975181,-0.7414067664186554,-0.6616858375968594,0.11174908962660934,-0.7349149070549581,-0.6616858375968594,0.14856625361146109,-0.7265959334375454,-0.6616858375968594,0.18501405848141594,-0.7164705278455857,-0.6616858375968594,0.22100188925077185,-0.7045638636327612,-0.6616858375968594,0.2564402745019167,-0.6909055426423449,-0.6616858375968594,0.2912411088253674,-0.6755295216123535,-0.6616858375968594,0.3253178718637012,-0.6584740277537463,-0.6616858375968594,0.3585858434147967,-0.6397814637115562,-0.6616858375968594,0.3909623140596051,-0.6194983021452329,-0.6616858375968594,0.4223667907907964,-0.597674970190289,-0.6616858375968594,0.4527211971310597,-0.5743657240884954,-0.6616858375968594,0.4819500672435278,-0.5496285142983167,-0.6616858375968594,0.509980733551737,-0.5235248414209388,-0.6616858375968594,0.5367435074026733,-0.4961196033000863,-0.6616858375968594,0.5621718523237424,-0.4674809336757555,-0.6616858375968594,0.5862025494429263,-0.437680032793004,-0.6616858375968594,0.6087758546608576,-0.4067909903869187,-0.6616858375968594,0.6298356471840669,-0.3748906014838668,-0.6616858375968594,0.6493295690501143,-0.3420581754769555,-0.6616858375968594,0.6672091552977364,-0.30837533895038455,-0.6616858375968594,0.6834299544583762,-0.27392583274290044,-0.6616858375968594,0.6979516390695355,-0.2387953037548724,-0.6616858375968594,0.7107381059352037,-0.20307109201660628,-0.6616858375968594,0.7217575658840895,-0.1668420135472693,-0.6616858375968594,0.7309826228025093,-0.13019813954426587,-0.6616858375968594,0.7383903417454446,-0.09323057245205657,-0.6616858375968594,0.7439623059564271,-0.05603121946712089,-0.6616858375968594,0.7476846626544976,-0.018692564042190243,-0.6616858375968594,0.7495481574744022,0.01869256404218997,-0.6616858375968594,0.7495481574744022,0.05603121946712062,-0.6616858375968594,0.7476846626544976,0.09323057245205564,-0.6616858375968594,0.7439623059564272,0.1301981395442656,-0.6616858375968594,0.7383903417454447,0.16684201354726902,-0.6616858375968594,0.7309826228025093,0.20307109201660667,-0.6616858375968594,0.7217575658840892,0.2387953037548721,-0.6616858375968594,0.7107381059352038,0.2739258327428996,-0.6616858375968594,0.6979516390695358,0.30837533895038427,-0.6616858375968594,0.6834299544583763,0.3420581754769552,-0.6616858375968594,0.6672091552977365,0.37489060148386716,-0.6616858375968594,0.6493295690501141,0.406790990386919,-0.6616858375968594,0.6298356471840667,0.4376800327930032,-0.6616858375968594,0.6087758546608582,0.46748093367575533,-0.6616858375968594,0.5862025494429265,0.49611960330008553,-0.6616858375968594,0.562171852323743,0.5235248414209388,-0.6616858375968594,0.5367435074026733,0.5496285142983167,-0.6616858375968594,0.509980733551737,0.5743657240884952,-0.6616858375968594,0.48195006724352807,0.5976749701902888,-0.6616858375968594,0.4527211971310599,0.6194983021452326,-0.6616858375968594,0.4223667907907969,0.6397814637115563,-0.6616858375968594,0.3909623140596051,0.6584740277537461,-0.6616858375968594,0.35858584341479727,0.6755295216123534,-0.6616858375968594,0.3253178718637014,0.6909055426423449,-0.6616858375968594,0.29124110882536736,0.704563863632761,-0.6616858375968594,0.2564402745019173,0.7164705278455855,-0.6616858375968594,0.22100188925077244,0.7265959334375452,-0.6616858375968594,0.1850140584814162,0.734914907054958,-0.6616858375968594,0.14856625361146134,0.7414067664186554,-0.6616858375968594,0.11174908962660927,0.7460553717433879,-0.6616858375968594,0.0746540997975187,0.7488491658638742,-0.6616858375968594,0.03737350811366998,0.7497812029677342,-0.6616858375968594,0.0,0.7818314824680298,-0.6234898018587335,0.0,0.7808596043417954,-0.6234898018587335,-0.038971082681034774,0.7779463862052449,-0.6234898018587335,-0.07784527711015672,0.773099070777679,-0.6234898018587335,-0.11652593591491316,0.7663297092490039,-0.6234898018587335,-0.15491689288290753,0.7576551313185753,-0.6234898018587335,-0.19292270204615544,0.7470969033539131,-0.6234898018587335,-0.23044887497479993,0.7346812747733149,-0.6234898018587335,-0.2674021156902356,0.7204391127856651,-0.6234898018587335,-0.3036905526136117,0.7044058256496909,-0.6234898018587335,-0.33922396697305235,0.6866212746434514,-0.6234898018587335,-0.3739140171017386,0.6671296749629204,-0.6234898018587335,-0.40767445806921154,0.6459794857960423,-0.6234898018587335,-0.4404213560998583,0.623223289845555,-0.6234898018587335,-0.47207329724550257,0.5989176626001069,-0.6234898018587335,-0.5025515897933083,0.5731230316786773,-0.6234898018587335,-0.5317804599057764,0.5459035265979943,-0.6234898018587335,-0.5596872400064433,0.5173268193364526,-0.6234898018587335,-0.5862025494429264,0.48746395609091187,-0.6234898018587335,-0.6112604669781572,0.4563891806446575,-0.6234898018587335,-0.6347986946809637,0.4241797497856576,-0.6234898018587335,-0.6567587128085429,0.3909157412340148,-0.6234898018587335,-0.6770859252957618,0.3566798545561379,-0.6234898018587335,-0.6957297954895776,0.3215572055605876,-0.6234898018587335,-0.7126439717911205,0.28563511468676717,-0.6234898018587335,-0.7277864028930704,0.24900288991255135,-0.6234898018587335,-0.7411194423258339,0.21175160472058094,-0.6234898018587335,-0.7526099420525987,0.17397387167523587,-0.6234898018587335,-0.7622293348805763,0.13576361217320804,-0.6234898018587335,-0.7699537054835439,0.09721582294011026,-0.6234898018587335,-0.7757638498591142,0.05842633985364879,-0.6234898018587335,-0.7796453230729106,0.019491599680531102,-0.6234898018587335,-0.7815884751709502,-0.01949159968053101,-0.6234898018587335,-0.7815884751709502,-0.058426339853648694,-0.6234898018587335,-0.7796453230729106,-0.09721582294011018,-0.6234898018587335,-0.7757638498591142,-0.13576361217320795,-0.6234898018587335,-0.7699537054835439,-0.17397387167523579,-0.6234898018587335,-0.7622293348805763,-0.2117516047205808,-0.6234898018587335,-0.7526099420525988,-0.24900288991255126,-0.6234898018587335,-0.7411194423258339,-0.2856351146867671,-0.6234898018587335,-0.7277864028930704,-0.32155720556058737,-0.6234898018587335,-0.7126439717911205,-0.3566798545561375,-0.6234898018587335,-0.6957297954895778,-0.39091574123401507,-0.6234898018587335,-0.6770859252957617,-0.4241797497856576,-0.6234898018587335,-0.6567587128085429,-0.4563891806446573,-0.6234898018587335,-0.6347986946809638,-0.48746395609091175,-0.6234898018587335,-0.6112604669781573,-0.5173268193364524,-0.6234898018587335,-0.5862025494429266,-0.5459035265979943,-0.6234898018587335,-0.5596872400064434,-0.5731230316786773,-0.6234898018587335,-0.5317804599057764,-0.5989176626001068,-0.6234898018587335,-0.5025515897933085,-0.623223289845555,-0.6234898018587335,-0.47207329724550257,-0.6459794857960421,-0.6234898018587335,-0.44042135609985844,-0.6671296749629204,-0.6234898018587335,-0.40767445806921165,-0.6866212746434515,-0.6234898018587335,-0.37391401710173855,-0.7044058256496908,-0.6234898018587335,-0.33922396697305246,-0.7204391127856652,-0.6234898018587335,-0.30369055261361166,-0.7346812747733149,-0.6234898018587335,-0.26740211569023575,-0.747096903353913,-0.6234898018587335,-0.2304488749748002,-0.7576551313185752,-0.6234898018587335,-0.19292270204615564,-0.7663297092490039,-0.6234898018587335,-0.15491689288290755,-0.773099070777679,-0.6234898018587335,-0.11652593591491338,-0.7779463862052449,-0.6234898018587335,-0.07784527711015676,-0.7808596043417954,-0.6234898018587335,-0.038971082681035024,-0.7818314824680298,-0.6234898018587335,-9.574674224771027e-17,-0.7808596043417954,-0.6234898018587335,0.03897108268103483,-0.7779463862052449,-0.6234898018587335,0.07784527711015657,-0.773099070777679,-0.6234898018587335,0.11652593591491318,-0.766329709249004,-0.6234898018587335,0.15491689288290736,-0.7576551313185753,-0.6234898018587335,0.19292270204615544,-0.7470969033539132,-0.6234898018587335,0.2304488749747997,-0.7346812747733149,-0.6234898018587335,0.26740211569023553,-0.7204391127856651,-0.6234898018587335,0.3036905526136118,-0.7044058256496909,-0.6234898018587335,0.3392239669730523,-0.6866212746434515,-0.6234898018587335,0.3739140171017384,-0.6671296749629204,-0.6234898018587335,0.4076744580692115,-0.6459794857960423,-0.6234898018587335,0.4404213560998583,-0.6232232898455551,-0.6234898018587335,0.47207329724550234,-0.5989176626001069,-0.6234898018587335,0.5025515897933083,-0.5731230316786775,-0.6234898018587335,0.5317804599057762,-0.5459035265979945,-0.6234898018587335,0.5596872400064432,-0.5173268193364529,-0.6234898018587335,0.5862025494429262,-0.48746395609091187,-0.6234898018587335,0.6112604669781571,-0.45638918064465794,-0.6234898018587335,0.6347986946809634,-0.4241797497856574,-0.6234898018587335,0.6567587128085429,-0.3909157412340146,-0.6234898018587335,0.677085925295762,-0.35667985455613793,-0.6234898018587335,0.6957297954895776,-0.32155720556058753,-0.6234898018587335,0.7126439717911205,-0.2856351146867678,-0.6234898018587335,0.7277864028930702,-0.2490028899125518,-0.6234898018587335,0.7411194423258337,-0.2117516047205805,-0.6234898018587335,0.7526099420525989,-0.17397387167523598,-0.6234898018587335,0.7622293348805763,-0.13576361217320795,-0.6234898018587335,0.7699537054835439,-0.0972158229401107,-0.6234898018587335,0.7757638498591142,-0.05842633985364905,-0.6234898018587335,0.7796453230729106,-0.0194915996805312,-0.6234898018587335,0.7815884751709502,0.01949159968053091,-0.6234898018587335,0.7815884751709502,0.05842633985364877,-0.6234898018587335,0.7796453230729106,0.09721582294010973,-0.6234898018587335,0.7757638498591143,0.13576361217320768,-0.6234898018587335,0.769953705483544,0.1739738716752357,-0.6234898018587335,0.7622293348805763,0.21175160472058088,-0.6234898018587335,0.7526099420525987,0.24900288991255148,-0.6234898018587335,0.7411194423258338,0.28563511468676683,-0.6234898018587335,0.7277864028930705,0.3215572055605873,-0.6234898018587335,0.7126439717911205,0.3566798545561377,-0.6234898018587335,0.6957297954895777,0.390915741234015,-0.6234898018587335,0.6770859252957617,0.42417974978565776,-0.6234898018587335,0.6567587128085428,0.45638918064465717,-0.6234898018587335,0.634798694680964,0.4874639560909117,-0.6234898018587335,0.6112604669781573,0.5173268193364521,-0.6234898018587335,0.5862025494429268,0.5459035265979945,-0.6234898018587335,0.5596872400064432,0.5731230316786775,-0.6234898018587335,0.5317804599057762,0.5989176626001067,-0.6234898018587335,0.5025515897933086,0.6232232898455549,-0.6234898018587335,0.4720732972455026,0.6459794857960418,-0.6234898018587335,0.4404213560998588,0.6671296749629205,-0.6234898018587335,0.4076744580692115,0.6866212746434512,-0.6234898018587335,0.37391401710173894,0.7044058256496908,-0.6234898018587335,0.3392239669730525,0.7204391127856651,-0.6234898018587335,0.3036905526136117,0.7346812747733147,-0.6234898018587335,0.26740211569023614,0.747096903353913,-0.6234898018587335,0.23044887497480032,0.7576551313185752,-0.6234898018587335,0.19292270204615572,0.7663297092490039,-0.6234898018587335,0.15491689288290764,0.773099070777679,-0.6234898018587335,0.11652593591491311,0.7779463862052448,-0.6234898018587335,0.07784527711015721,0.7808596043417954,-0.6234898018587335,0.038971082681035114,0.7818314824680298,-0.6234898018587335,0.0,0.8119380057158565,-0.5837436722347898,0.0,0.8109287028094007,-0.5837436722347898,-0.040471768996487065,0.8079033033760864,-0.5837436722347898,-0.08084291879842971,0.8028693290355935,-0.5837436722347898,-0.12101308036645493,0.7958392950412962,-0.5837436722347898,-0.1608823843496074,0.7868306791653719,-0.5837436722347898,-0.2003517093762915,0.7758658782463045,-0.5837436722347898,-0.23932292848561904,0.762972152506817,-0.5837436722347898,-0.27769915308649484,0.7481815577806609,-0.5837436722347898,-0.31538497383792063,0.7315308658167632,-0.5837436722347898,-0.3522866978516491,0.7130614728588651,-0.5837436722347898,-0.38831258162746696,0.6928192967279375,-0.5837436722347898,-0.42337305914199164,0.6708546626632426,-0.5837436722347898,-0.4573809645239178,0.6472221782058603,-0.5837436722347898,-0.4902517487621068,0.6219805974357369,-0.5837436722347898,-0.521903689907751,0.5951926748997849,-0.5837436722347898,-0.5522580962480145,0.5669250095941912,-0.5837436722347898,-0.5812395019460295,0.5372478793888201,-0.5837436722347898,-0.6087758546608579,0.5062350663053546,-0.5837436722347898,-0.6347986946809637,0.47396367308356596,-0.5837436722347898,-0.6592433251258423,0.44051393149175355,-0.5837436722347898,-0.6820489727926586,0.40596900285792814,-0.5837436722347898,-0.7031589392480065,0.37041477131764716,-0.5837436722347898,-0.7225207417891457,0.33393963029252066,-0.5837436722347898,-0.7400862439242719,0.2966342627302389,-0.5837436722347898,-0.7558117750474187,0.2585914156524761,-0.5837436722347898,-0.7696582390104654,0.21990566957118057,-0.5837436722347898,-0.7815912113223209,0.18067320334651976,-0.5837436722347898,-0.7915810247336336,0.14099155507108022,-0.5837436722347898,-0.799602842994246,0.10095937957480067,-0.5837436722347898,-0.8056367226000274,0.06067620315352074,-0.5837436722347898,-0.8096676623755669,0.0202421761309278,-0.5837436722347898,-0.8116856407694587,-0.0202421761309277,-0.5837436722347898,-0.8116856407694587,-0.06067620315352064,-0.5837436722347898,-0.8096676623755669,-0.10095937957480058,-0.5837436722347898,-0.8056367226000274,-0.14099155507108013,-0.5837436722347898,-0.799602842994246,-0.18067320334651968,-0.5837436722347898,-0.7915810247336336,-0.21990566957118043,-0.5837436722347898,-0.781591211322321,-0.25859141565247606,-0.5837436722347898,-0.7696582390104654,-0.2966342627302388,-0.5837436722347898,-0.7558117750474188,-0.33393963029252044,-0.5837436722347898,-0.740086243924272,-0.3704147713176468,-0.5837436722347898,-0.7225207417891459,-0.4059690028579284,-0.5837436722347898,-0.7031589392480064,-0.44051393149175355,-0.5837436722347898,-0.6820489727926587,-0.4739636730835657,-0.5837436722347898,-0.6592433251258423,-0.5062350663053545,-0.5837436722347898,-0.6347986946809638,-0.5372478793888199,-0.5837436722347898,-0.6087758546608582,-0.5669250095941912,-0.5837436722347898,-0.5812395019460296,-0.5951926748997849,-0.5837436722347898,-0.5522580962480145,-0.6219805974357367,-0.5837436722347898,-0.5219036899077513,-0.6472221782058603,-0.5837436722347898,-0.4902517487621068,-0.6708546626632425,-0.5837436722347898,-0.45738096452391797,-0.6928192967279374,-0.5837436722347898,-0.42337305914199175,-0.7130614728588652,-0.5837436722347898,-0.38831258162746685,-0.7315308658167631,-0.5837436722347898,-0.3522866978516492,-0.7481815577806609,-0.5837436722347898,-0.3153849738379206,-0.762972152506817,-0.5837436722347898,-0.277699153086495,-0.7758658782463045,-0.5837436722347898,-0.23932292848561934,-0.7868306791653717,-0.5837436722347898,-0.20035170937629168,-0.7958392950412962,-0.5837436722347898,-0.16088238434960742,-0.8028693290355935,-0.5837436722347898,-0.12101308036645515,-0.8079033033760864,-0.5837436722347898,-0.08084291879842977,-0.8109287028094007,-0.5837436722347898,-0.04047176899648732,-0.8119380057158565,-0.5837436722347898,-9.94337279806009e-17,-0.8109287028094007,-0.5837436722347898,0.04047176899648712,-0.8079033033760864,-0.5837436722347898,0.08084291879842957,-0.8028693290355935,-0.5837436722347898,0.12101308036645496,-0.7958392950412964,-0.5837436722347898,0.16088238434960722,-0.7868306791653719,-0.5837436722347898,0.2003517093762915,-0.7758658782463046,-0.5837436722347898,0.23932292848561879,-0.762972152506817,-0.5837436722347898,0.27769915308649484,-0.7481815577806609,-0.5837436722347898,0.3153849738379207,-0.7315308658167632,-0.5837436722347898,0.352286697851649,-0.7130614728588652,-0.5837436722347898,0.3883125816274667,-0.6928192967279375,-0.5837436722347898,0.4233730591419916,-0.6708546626632426,-0.5837436722347898,0.4573809645239178,-0.6472221782058604,-0.5837436722347898,0.49025174876210664,-0.6219805974357369,-0.5837436722347898,0.521903689907751,-0.595192674899785,-0.5837436722347898,0.5522580962480143,-0.5669250095941913,-0.5837436722347898,0.5812395019460294,-0.5372478793888203,-0.5837436722347898,0.6087758546608577,-0.5062350663053546,-0.5837436722347898,0.6347986946809636,-0.4739636730835664,-0.5837436722347898,0.6592433251258418,-0.4405139314917534,-0.5837436722347898,0.6820489727926587,-0.4059690028579279,-0.5837436722347898,0.7031589392480067,-0.3704147713176473,-0.5837436722347898,0.7225207417891456,-0.3339396302925206,-0.5837436722347898,0.7400862439242719,-0.29663426273023946,-0.5837436722347898,0.7558117750474185,-0.2585914156524766,-0.5837436722347898,0.7696582390104652,-0.21990566957118013,-0.5837436722347898,0.7815912113223211,-0.18067320334651987,-0.5837436722347898,0.7915810247336336,-0.14099155507108013,-0.5837436722347898,0.799602842994246,-0.10095937957480114,-0.5837436722347898,0.8056367226000274,-0.06067620315352101,-0.5837436722347898,0.8096676623755669,-0.020242176130927898,-0.5837436722347898,0.8116856407694587,0.0202421761309276,-0.5837436722347898,0.8116856407694587,0.060676203153520715,-0.5837436722347898,0.8096676623755669,0.10095937957480011,-0.5837436722347898,0.8056367226000275,0.14099155507107985,-0.5837436722347898,0.799602842994246,0.1806732033465196,-0.5837436722347898,0.7915810247336336,0.21990566957118055,-0.5837436722347898,0.7815912113223209,0.2585914156524763,-0.5837436722347898,0.7696582390104653,0.2966342627302385,-0.5837436722347898,0.7558117750474188,0.33393963029252033,-0.5837436722347898,0.740086243924272,0.370414771317647,-0.5837436722347898,0.7225207417891458,0.40596900285792836,-0.5837436722347898,0.7031589392480064,0.4405139314917538,-0.5837436722347898,0.6820489727926585,0.4739636730835656,-0.5837436722347898,0.6592433251258424,0.5062350663053545,-0.5837436722347898,0.6347986946809638,0.5372478793888196,-0.5837436722347898,0.6087758546608584,0.5669250095941913,-0.5837436722347898,0.5812395019460294,0.595192674899785,-0.5837436722347898,0.5522580962480143,0.6219805974357367,-0.5837436722347898,0.5219036899077513,0.6472221782058601,-0.5837436722347898,0.4902517487621069,0.6708546626632422,-0.5837436722347898,0.45738096452391835,0.6928192967279376,-0.5837436722347898,0.4233730591419916,0.7130614728588649,-0.5837436722347898,0.3883125816274673,0.7315308658167631,-0.5837436722347898,0.3522866978516493,0.7481815577806609,-0.5837436722347898,0.31538497383792063,0.7629721525068168,-0.5837436722347898,0.27769915308649545,0.7758658782463045,-0.5837436722347898,0.23932292848561942,0.7868306791653717,-0.5837436722347898,0.20035170937629176,0.7958392950412962,-0.5837436722347898,0.1608823843496075,0.8028693290355935,-0.5837436722347898,0.12101308036645489,0.8079033033760863,-0.5837436722347898,0.08084291879843022,0.8109287028094007,-0.5837436722347898,0.04047176899648742,0.8119380057158565,-0.5837436722347898,0.0,0.8400259231507714,-0.5425462638657594,0.0,0.8389817047501475,-0.5425462638657594,-0.04187183611739494,0.8358516456397491,-0.5425462638657594,-0.08363957225278529,0.8306435276397036,-0.5425462638657594,-0.12519936723312391,0.8233702989519613,-0.5425462638657594,-0.1664478968598389,0.8140500419690255,-0.5425462638657594,-0.20728261078907093,0.8027059283182756,-0.5425462638657594,-0.24760198748798645,0.7893661612536504,-0.5425462638657594,-0.28730578663330403,0.7740639055379149,-0.5425462638657594,-0.32629529832453225,0.7568372049898351,-0.5425462638657594,-0.36447358849233524,0.7377288879012491,-0.5425462638657594,-0.4017457398919021,0.7167864605591873,-0.5425462638657594,-0.43801908808217305,0.6940619891377555,-0.5425462638657594,-0.4732034518042403,0.6696119702534244,-0.5425462638657594,-0.5072113571861663,0.6434971905055381,-0.5425462638657594,-0.5399582552168128,0.6157825753512508,-0.5425462638657594,-0.5713627319480041,0.5865370276906106,-0.5425462638657594,-0.6013467109024291,0.5558332565630932,-0.5425462638657594,-0.6298356471840668,0.5237475963814743,-0.5425462638657594,-0.6567587128085429,0.49035981715245075,-0.5425462638657594,-0.6820489727926586,0.45575292615583657,-0.5425462638657594,-0.7056435515653057,0.4200129615753856,-0.5425462638657594,-0.7274837892860427,0.3832287785943111,-0.5425462638657594,-0.7475153876827006,0.3454918284872986,-0.5425462638657594,-0.7656885450454439,0.3068959312582294,-0.5425462638657594,-0.7819580800416668,0.26753704238886805,-0.5425462638657594,-0.7962835440439058,0.227513014278415,-0.5425462638657594,-0.8086293216915,0.1869233529670249,-0.5425462638657594,-0.8189647194359906,0.1458689707481124,-0.5425462638657594,-0.8272640418501169,0.10445193528449068,-0.5425462638657594,-0.8335066555106986,0.0627752158520797,-0.5425462638657594,-0.8376770402965754,0.02094242734206211,-0.5425462638657594,-0.8397648279740751,-0.020942427342062007,-0.5425462638657594,-0.8397648279740751,-0.06277521585207958,-0.5425462638657594,-0.8376770402965754,-0.1044519352844906,-0.5425462638657594,-0.8335066555106986,-0.14586897074811228,-0.5425462638657594,-0.8272640418501169,-0.18692335296702478,-0.5425462638657594,-0.8189647194359906,-0.22751301427841486,-0.5425462638657594,-0.8086293216915001,-0.267537042388868,-0.5425462638657594,-0.7962835440439058,-0.30689593125822934,-0.5425462638657594,-0.7819580800416669,-0.34549182848729837,-0.5425462638657594,-0.765688545045444,-0.38322877859431065,-0.5425462638657594,-0.7475153876827008,-0.42001296157538587,-0.5425462638657594,-0.7274837892860426,-0.45575292615583657,-0.5425462638657594,-0.7056435515653058,-0.4903598171524505,-0.5425462638657594,-0.6820489727926587,-0.5237475963814742,-0.5425462638657594,-0.6567587128085429,-0.555833256563093,-0.5425462638657594,-0.629835647184067,-0.5865370276906104,-0.5425462638657594,-0.6013467109024292,-0.6157825753512508,-0.5425462638657594,-0.5713627319480041,-0.643497190505538,-0.5425462638657594,-0.5399582552168131,-0.6696119702534244,-0.5425462638657594,-0.5072113571861663,-0.6940619891377554,-0.5425462638657594,-0.47320345180424045,-0.7167864605591872,-0.5425462638657594,-0.43801908808217316,-0.7377288879012492,-0.5425462638657594,-0.401745739891902,-0.756837204989835,-0.5425462638657594,-0.3644735884923353,-0.774063905537915,-0.5425462638657594,-0.3262952983245322,-0.7893661612536504,-0.5425462638657594,-0.28730578663330414,-0.8027059283182755,-0.5425462638657594,-0.24760198748798676,-0.8140500419690254,-0.5425462638657594,-0.20728261078907112,-0.8233702989519613,-0.5425462638657594,-0.1664478968598389,-0.8306435276397036,-0.5425462638657594,-0.12519936723312416,-0.8358516456397491,-0.5425462638657594,-0.08363957225278534,-0.8389817047501475,-0.5425462638657594,-0.0418718361173952,-0.8400259231507714,-0.5425462638657594,-1.0287350579873927e-16,-0.8389817047501475,-0.5425462638657594,0.04187183611739499,-0.8358516456397491,-0.5425462638657594,0.08363957225278514,-0.8306435276397036,-0.5425462638657594,0.12519936723312394,-0.8233702989519613,-0.5425462638657594,0.1664478968598387,-0.8140500419690255,-0.5425462638657594,0.20728261078907093,-0.8027059283182757,-0.5425462638657594,0.2476019874879862,-0.7893661612536504,-0.5425462638657594,0.287305786633304,-0.7740639055379149,-0.5425462638657594,0.3262952983245323,-0.7568372049898351,-0.5425462638657594,0.36447358849233513,-0.7377288879012492,-0.5425462638657594,0.4017457398919018,-0.7167864605591873,-0.5425462638657594,0.43801908808217294,-0.6940619891377555,-0.5425462638657594,0.4732034518042403,-0.6696119702534246,-0.5425462638657594,0.5072113571861662,-0.6434971905055381,-0.5425462638657594,0.5399582552168128,-0.615782575351251,-0.5425462638657594,0.5713627319480039,-0.5865370276906107,-0.5425462638657594,0.601346710902429,-0.5558332565630935,-0.5425462638657594,0.6298356471840666,-0.5237475963814743,-0.5425462638657594,0.6567587128085428,-0.49035981715245125,-0.5425462638657594,0.6820489727926583,-0.4557529261558364,-0.5425462638657594,0.7056435515653058,-0.42001296157538537,-0.5425462638657594,0.7274837892860427,-0.38322877859431115,-0.5425462638657594,0.7475153876827005,-0.34549182848729854,-0.5425462638657594,0.7656885450454439,-0.30689593125823,-0.5425462638657594,0.7819580800416667,-0.26753704238886855,-0.5425462638657594,0.7962835440439056,-0.22751301427841453,-0.5425462638657594,0.8086293216915001,-0.186923352967025,-0.5425462638657594,0.8189647194359906,-0.1458689707481123,-0.5425462638657594,0.8272640418501169,-0.10445193528449116,-0.5425462638657594,0.8335066555106985,-0.06277521585207997,-0.5425462638657594,0.8376770402965754,-0.02094242734206221,-0.5425462638657594,0.8397648279740751,0.020942427342061903,-0.5425462638657594,0.8397648279740751,0.06277521585207967,-0.5425462638657594,0.8376770402965754,0.10445193528449011,-0.5425462638657594,0.8335066555106986,0.145868970748112,-0.5425462638657594,0.827264041850117,0.1869233529670247,-0.5425462638657594,0.8189647194359906,0.22751301427841497,-0.5425462638657594,0.8086293216915,0.2675370423888682,-0.5425462638657594,0.7962835440439057,0.30689593125822906,-0.5425462638657594,0.781958080041667,0.34549182848729826,-0.5425462638657594,0.765688545045444,0.38322877859431087,-0.5425462638657594,0.7475153876827008,0.4200129615753858,-0.5425462638657594,0.7274837892860426,0.4557529261558368,-0.5425462638657594,0.7056435515653056,0.49035981715245036,-0.5425462638657594,0.6820489727926589,0.5237475963814741,-0.5425462638657594,0.6567587128085429,0.5558332565630927,-0.5425462638657594,0.6298356471840673,0.5865370276906107,-0.5425462638657594,0.601346710902429,0.615782575351251,-0.5425462638657594,0.5713627319480039,0.6434971905055379,-0.5425462638657594,0.5399582552168132,0.6696119702534243,-0.5425462638657594,0.5072113571861664,0.6940619891377551,-0.5425462638657594,0.47320345180424084,0.7167864605591873,-0.5425462638657594,0.43801908808217294,0.737728887901249,-0.5425462638657594,0.40174573989190243,0.756837204989835,-0.5425462638657594,0.3644735884923354,0.7740639055379149,-0.5425462638657594,0.32629529832453225,0.7893661612536502,-0.5425462638657594,0.28730578663330464,0.8027059283182755,-0.5425462638657594,0.24760198748798687,0.8140500419690254,-0.5425462638657594,0.2072826107890712,0.8233702989519613,-0.5425462638657594,0.16644789685983902,0.8306435276397036,-0.5425462638657594,0.1251993672331239,0.8358516456397491,-0.5425462638657594,0.0836395722527858,0.8389817047501475,-0.5425462638657594,0.0418718361173953,0.8400259231507714,-0.5425462638657594,0.0,0.8660254037844387,-0.49999999999999983,0.0,0.8649488659811201,-0.49999999999999983,-0.04316780325629822,0.8617219290137649,-0.49999999999999983,-0.08622828455208917,0.8563526155561171,-0.49999999999999983,-0.12907438874616925,0.848854274567433,-0.49999999999999983,-0.1715995936725875,0.8392455481048653,-0.49999999999999983,-0.21369817497153384,0.8275503249763715,-0.49999999999999983,-0.25526546893675595,0.8137976813493738,-0.49999999999999983,-0.29619813272602386,0.7980218084628248,-0.49999999999999983,-0.3363944012877187,0.7802619276224012,-0.49999999999999983,-0.37575434036478533,0.7605621926901572,-0.49999999999999983,-0.4141800949470414,0.7389715803110674,-0.49999999999999983,-0.4515761325541507,0.7155437681493693,-0.49999999999999983,-0.48784948074442175,0.6903370014374335,-0.49999999999999983,-0.5229099582589465,0.6634139481689384,-0.49999999999999983,-0.5566703992264194,0.634841543296364,-0.49999999999999983,-0.5890468698712276,0.6046908223201529,-0.49999999999999983,-0.6199588771860416,0.5730367446832647,-0.49999999999999983,-0.6493295690501142,0.5399580074101894,-0.49999999999999983,-0.6770859252957618,0.505536849453745,-0.49999999999999983,-0.7031589392480065,0.46985884723608295,-0.49999999999999983,-0.7274837892860427,0.43301270189221924,-0.49999999999999983,-0.7500000000000001,0.39509001874503724,-0.49999999999999983,-0.7706515925423408,0.35618507956002005,-0.49999999999999983,-0.7893872238000956,0.3163946081459277,-0.49999999999999983,-0.8061603140419311,0.2758175298841683,-0.49999999999999983,-0.8209291627227018,0.23455472578471245,-0.49999999999999983,-0.8336570521575755,0.19270878168000763,-0.49999999999999983,-0.8443123388079836,0.15038373318043535,-0.49999999999999983,-0.8528685319524433,0.10768480702539142,-0.49999999999999983,-0.8593043595466651,0.06471815947303208,-0.49999999999999983,-0.8636038211092071,0.02159061237909007,-0.49999999999999983,-0.8657562275011921,-0.021590612379089966,-0.49999999999999983,-0.8657562275011921,-0.06471815947303197,-0.49999999999999983,-0.8636038211092071,-0.10768480702539132,-0.49999999999999983,-0.8593043595466651,-0.15038373318043527,-0.49999999999999983,-0.8528685319524433,-0.19270878168000755,-0.49999999999999983,-0.8443123388079836,-0.23455472578471231,-0.49999999999999983,-0.8336570521575755,-0.27581752988416824,-0.49999999999999983,-0.8209291627227018,-0.31639460814592757,-0.49999999999999983,-0.8061603140419312,-0.3561850795600198,-0.49999999999999983,-0.7893872238000957,-0.39509001874503685,-0.49999999999999983,-0.7706515925423411,-0.4330127018922195,-0.49999999999999983,-0.75,-0.46985884723608295,-0.49999999999999983,-0.7274837892860427,-0.5055368494537448,-0.49999999999999983,-0.7031589392480067,-0.5399580074101894,-0.49999999999999983,-0.677085925295762,-0.5730367446832645,-0.49999999999999983,-0.6493295690501144,-0.6046908223201528,-0.49999999999999983,-0.6199588771860417,-0.634841543296364,-0.49999999999999983,-0.5890468698712276,-0.6634139481689383,-0.49999999999999983,-0.5566703992264196,-0.6903370014374335,-0.49999999999999983,-0.5229099582589465,-0.7155437681493692,-0.49999999999999983,-0.48784948074442197,-0.7389715803110672,-0.49999999999999983,-0.4515761325541508,-0.7605621926901573,-0.49999999999999983,-0.4141800949470413,-0.7802619276224011,-0.49999999999999983,-0.37575434036478544,-0.7980218084628249,-0.49999999999999983,-0.3363944012877186,-0.8137976813493737,-0.49999999999999983,-0.29619813272602397,-0.8275503249763714,-0.49999999999999983,-0.25526546893675633,-0.8392455481048652,-0.49999999999999983,-0.21369817497153404,-0.848854274567433,-0.49999999999999983,-0.17159959367258754,-0.8563526155561171,-0.49999999999999983,-0.1290743887461695,-0.8617219290137649,-0.49999999999999983,-0.08622828455208924,-0.8649488659811201,-0.49999999999999983,-0.0431678032562985,-0.8660254037844387,-0.49999999999999983,-1.0605752387249069e-16,-0.8649488659811201,-0.49999999999999983,0.043167803256298286,-0.8617219290137649,-0.49999999999999983,0.08622828455208902,-0.8563526155561171,-0.49999999999999983,0.12907438874616928,-0.8488542745674331,-0.49999999999999983,0.17159959367258731,-0.8392455481048653,-0.49999999999999983,0.21369817497153384,-0.8275503249763716,-0.49999999999999983,0.2552654689367557,-0.8137976813493738,-0.49999999999999983,0.2961981327260238,-0.7980218084628248,-0.49999999999999983,0.33639440128771875,-0.7802619276224012,-0.49999999999999983,0.3757543403647852,-0.7605621926901573,-0.49999999999999983,0.4141800949470411,-0.7389715803110674,-0.49999999999999983,0.4515761325541506,-0.7155437681493693,-0.49999999999999983,0.48784948074442175,-0.6903370014374336,-0.49999999999999983,0.5229099582589463,-0.6634139481689384,-0.49999999999999983,0.5566703992264194,-0.6348415432963641,-0.49999999999999983,0.5890468698712273,-0.604690822320153,-0.49999999999999983,0.6199588771860415,-0.5730367446832649,-0.49999999999999983,0.649329569050114,-0.5399580074101894,-0.49999999999999983,0.6770859252957617,-0.5055368494537454,-0.49999999999999983,0.7031589392480061,-0.46985884723608273,-0.49999999999999983,0.7274837892860427,-0.433012701892219,-0.49999999999999983,0.7500000000000002,-0.39509001874503735,-0.49999999999999983,0.7706515925423408,-0.35618507956002,-0.49999999999999983,0.7893872238000956,-0.3163946081459283,-0.49999999999999983,0.8061603140419309,-0.2758175298841688,-0.49999999999999983,0.8209291627227016,-0.23455472578471198,-0.49999999999999983,0.8336570521575756,-0.19270878168000777,-0.49999999999999983,0.8443123388079836,-0.1503837331804353,-0.49999999999999983,0.8528685319524433,-0.10768480702539192,-0.49999999999999983,0.8593043595466651,-0.06471815947303236,-0.49999999999999983,0.8636038211092071,-0.021590612379090175,-0.49999999999999983,0.8657562275011921,0.021590612379089855,-0.49999999999999983,0.8657562275011921,0.06471815947303206,-0.49999999999999983,0.8636038211092071,0.10768480702539084,-0.49999999999999983,0.8593043595466652,0.150383733180435,-0.49999999999999983,0.8528685319524434,0.19270878168000743,-0.49999999999999983,0.8443123388079836,0.2345547257847124,-0.49999999999999983,0.8336570521575755,0.27581752988416847,-0.49999999999999983,0.8209291627227017,0.3163946081459273,-0.49999999999999983,0.8061603140419312,0.3561850795600197,-0.49999999999999983,0.7893872238000957,0.3950900187450371,-0.49999999999999983,0.770651592542341,0.43301270189221946,-0.49999999999999983,0.75,0.4698588472360831,-0.49999999999999983,0.7274837892860426,0.5055368494537447,-0.49999999999999983,0.7031589392480068,0.5399580074101893,-0.49999999999999983,0.677085925295762,0.5730367446832642,-0.49999999999999983,0.6493295690501147,0.604690822320153,-0.49999999999999983,0.6199588771860415,0.6348415432963641,-0.49999999999999983,0.5890468698712273,0.6634139481689383,-0.49999999999999983,0.5566703992264196,0.6903370014374334,-0.49999999999999983,0.5229099582589466,0.7155437681493688,-0.49999999999999983,0.48784948074442236,0.7389715803110675,-0.49999999999999983,0.4515761325541506,0.760562192690157,-0.49999999999999983,0.4141800949470417,0.7802619276224011,-0.49999999999999983,0.37575434036478556,0.7980218084628248,-0.49999999999999983,0.3363944012877187,0.8137976813493735,-0.49999999999999983,0.29619813272602447,0.8275503249763714,-0.49999999999999983,0.2552654689367564,0.8392455481048652,-0.49999999999999983,0.21369817497153415,0.848854274567433,-0.49999999999999983,0.17159959367258762,0.8563526155561171,-0.49999999999999983,0.12907438874616922,0.8617219290137648,-0.49999999999999983,0.08622828455208971,0.8649488659811201,-0.49999999999999983,0.043167803256298605,0.8660254037844387,-0.49999999999999983,0.0,0.8898718088114685,-0.456210657353163,0.0,0.8887656280480558,-0.456210657353163,-0.044356448434694225,0.8854498358974878,-0.456210657353163,-0.0886026197496822,0.879932675941494,-0.456210657353163,-0.1326285109915527,0.8722278647090209,-0.456210657353163,-0.1763246668578642,0.8623545575747789,-0.456210657353163,-0.21958245182027247,0.8503373011359632,-0.456210657353163,-0.2622943202095712,0.8362059721855457,-0.456210657353163,-0.30435408359117055,0.8199957034338596,-0.456210657353163,-0.3456571747662769,0.8017467961631468,-0.456210657353163,-0.3861009077424248,0.7815046200322191,-0.456210657353163,-0.4255847330270338,0.7593195002803391,-0.456210657353163,-0.46401048760928987,0.7352465926107451,-0.456210657353163,-0.5012826390088568,0.7093457460648831,-0.456210657353163,-0.5373085227846747,0.681681354228259,-0.456210657353163,-0.5719985729133609,0.6523221951378404,-0.456210657353163,-0.6052665444644567,0.6213412602890179,-0.456210657353163,-0.6370297280189126,0.5888155731672489,-0.456210657353163,-0.6672091552977365,0.5548259977555354,-0.456210657353163,-0.6957297954895776,0.5194570374938215,-0.456210657353163,-0.7225207417891457,0.48279662519012767,-0.456210657353163,-0.7475153876827006,0.44493590440573416,-0.456210657353163,-0.7706515925423408,0.4059690028579283,-0.456210657353163,-0.7918718361173949,0.36599279840366644,-0.456210657353163,-0.8111233615388279,0.32510667818595873,-0.456210657353163,-0.8283583064811304,0.283412291541772,-0.456210657353163,-0.8435338221556007,0.24101329728576093,-0.456210657353163,-0.856612179839185,0.19801510599812286,-0.456210657353163,-0.8675608646740279,0.15452461795728661,-0.456210657353163,-0.8763526565045315,0.11064995736897673,-0.456210657353163,-0.8829656975509516,0.0665002035524018,-0.456210657353163,-0.8873835467512816,0.022185119751880186,-0.456210657353163,-0.8895952206363238,-0.02218511975188008,-0.456210657353163,-0.8895952206363238,-0.06650020355240169,-0.456210657353163,-0.8873835467512816,-0.11064995736897663,-0.456210657353163,-0.8829656975509516,-0.15452461795728653,-0.456210657353163,-0.8763526565045315,-0.19801510599812278,-0.456210657353163,-0.8675608646740279,-0.2410132972857608,-0.456210657353163,-0.8566121798391851,-0.2834122915417719,-0.456210657353163,-0.8435338221556007,-0.3251066781859586,-0.456210657353163,-0.8283583064811304,-0.36599279840366616,-0.456210657353163,-0.811123361538828,-0.40596900285792786,-0.456210657353163,-0.791871836117395,-0.4449359044057345,-0.456210657353163,-0.7706515925423408,-0.48279662519012767,-0.456210657353163,-0.7475153876827008,-0.5194570374938212,-0.456210657353163,-0.7225207417891458,-0.5548259977555353,-0.456210657353163,-0.6957297954895777,-0.5888155731672486,-0.456210657353163,-0.6672091552977366,-0.6213412602890178,-0.456210657353163,-0.6370297280189127,-0.6523221951378404,-0.456210657353163,-0.6052665444644567,-0.681681354228259,-0.456210657353163,-0.5719985729133611,-0.7093457460648831,-0.456210657353163,-0.5373085227846747,-0.735246592610745,-0.456210657353163,-0.5012826390088571,-0.759319500280339,-0.456210657353163,-0.46401048760929,-0.7815046200322192,-0.456210657353163,-0.4255847330270337,-0.8017467961631467,-0.456210657353163,-0.3861009077424249,-0.8199957034338597,-0.456210657353163,-0.3456571747662768,-0.8362059721855456,-0.456210657353163,-0.30435408359117067,-0.8503373011359631,-0.456210657353163,-0.26229432020957155,-0.8623545575747787,-0.456210657353163,-0.21958245182027267,-0.8722278647090209,-0.456210657353163,-0.17632466685786422,-0.879932675941494,-0.456210657353163,-0.13262851099155296,-0.8854498358974878,-0.456210657353163,-0.08860261974968225,-0.8887656280480558,-0.456210657353163,-0.04435644843469451,-0.8898718088114685,-0.456210657353163,-1.0897786623124304e-16,-0.8887656280480558,-0.456210657353163,0.04435644843469429,-0.8854498358974878,-0.456210657353163,0.08860261974968203,-0.879932675941494,-0.456210657353163,0.13262851099155273,-0.8722278647090209,-0.456210657353163,0.176324666857864,-0.8623545575747789,-0.456210657353163,0.21958245182027247,-0.8503373011359633,-0.456210657353163,0.26229432020957094,-0.8362059721855457,-0.456210657353163,0.3043540835911705,-0.8199957034338596,-0.456210657353163,0.34565717476627694,-0.8017467961631468,-0.456210657353163,0.38610090774242467,-0.7815046200322192,-0.456210657353163,0.42558473302703353,-0.7593195002803391,-0.456210657353163,0.46401048760928976,-0.7352465926107451,-0.456210657353163,0.5012826390088568,-0.7093457460648832,-0.456210657353163,0.5373085227846746,-0.681681354228259,-0.456210657353163,0.5719985729133609,-0.6523221951378406,-0.456210657353163,0.6052665444644565,-0.621341260289018,-0.456210657353163,0.6370297280189124,-0.5888155731672492,-0.456210657353163,0.6672091552977363,-0.5548259977555354,-0.456210657353163,0.6957297954895776,-0.5194570374938221,-0.456210657353163,0.7225207417891453,-0.48279662519012745,-0.456210657353163,0.7475153876827008,-0.44493590440573394,-0.456210657353163,0.770651592542341,-0.4059690028579284,-0.456210657353163,0.7918718361173948,-0.3659927984036664,-0.456210657353163,0.8111233615388279,-0.32510667818595934,-0.456210657353163,0.8283583064811302,-0.2834122915417725,-0.456210657353163,0.8435338221556005,-0.24101329728576043,-0.456210657353163,0.8566121798391851,-0.198015105998123,-0.456210657353163,0.8675608646740279,-0.15452461795728656,-0.456210657353163,0.8763526565045315,-0.11064995736897723,-0.456210657353163,0.8829656975509514,-0.06650020355240209,-0.456210657353163,0.8873835467512816,-0.022185119751880294,-0.456210657353163,0.8895952206363238,0.022185119751879968,-0.456210657353163,0.8895952206363238,0.06650020355240177,-0.456210657353163,0.8873835467512816,0.11064995736897612,-0.456210657353163,0.8829656975509517,0.15452461795728623,-0.456210657353163,0.8763526565045316,0.19801510599812266,-0.456210657353163,0.8675608646740279,0.24101329728576087,-0.456210657353163,0.856612179839185,0.2834122915417721,-0.456210657353163,0.8435338221556006,0.3251066781859583,-0.456210657353163,0.8283583064811305,0.36599279840366605,-0.456210657353163,0.811123361538828,0.40596900285792814,-0.456210657353163,0.791871836117395,0.4449359044057344,-0.456210657353163,0.7706515925423408,0.48279662519012784,-0.456210657353163,0.7475153876827005,0.5194570374938212,-0.456210657353163,0.7225207417891459,0.5548259977555352,-0.456210657353163,0.6957297954895777,0.5888155731672483,-0.456210657353163,0.6672091552977369,0.621341260289018,-0.456210657353163,0.6370297280189124,0.6523221951378406,-0.456210657353163,0.6052665444644565,0.6816813542282589,-0.456210657353163,0.5719985729133612,0.709345746064883,-0.456210657353163,0.5373085227846748,0.7352465926107448,-0.456210657353163,0.5012826390088575,0.7593195002803392,-0.456210657353163,0.46401048760928976,0.7815046200322189,-0.456210657353163,0.42558473302703415,0.8017467961631467,-0.456210657353163,0.38610090774242495,0.8199957034338596,-0.456210657353163,0.3456571747662769,0.8362059721855454,-0.456210657353163,0.30435408359117117,0.8503373011359631,-0.456210657353163,0.26229432020957166,0.8623545575747787,-0.456210657353163,0.21958245182027278,0.8722278647090209,-0.456210657353163,0.17632466685786433,0.879932675941494,-0.456210657353163,0.13262851099155265,0.8854498358974877,-0.456210657353163,0.08860261974968275,0.8887656280480558,-0.456210657353163,0.04435644843469461,0.8898718088114685,-0.456210657353163,0.0,0.9115058523116731,-0.4112871031306116,0.0,0.9103727787278364,-0.4112871031306116,-0.045434816493383975,0.9069763749757852,-0.4112871031306116,-0.09075667487415778,0.9013250850503918,-0.4112871031306116,-0.13585289786137714,0.89343295894884,-0.4112871031306116,-0.18061136913923773,0.8833196177401189,-0.4112871031306116,-0.22492081209590156,0.8710102047839531,-0.4112871031306116,-0.2686710664746871,0.8565353232204493,-0.4112871031306116,-0.31175336224982425,0.8399309598858677,-0.4112871031306116,-0.35406059004587676,0.8212383958436779,-0.4112871031306116,-0.3954875674285254,0.8005041037533288,-0.4112871031306116,-0.4359313004046733,0.7777796323318971,-0.4112871031306116,-0.47529123948173996,0.7531214781958531,-0.4112871031306116,-0.5134695296495431,0.7265909454015709,-0.4112871031306116,-0.5503712536632714,0.6982539930337851,-0.4112871031306116,-0.585904668022712,0.6681810712209132,-0.4112871031306116,-0.619981431061046,0.6364469459849365,-0.4112871031306116,-0.6525168225761517,0.6031305133612886,-0.4112871031306116,-0.6834299544583762,0.5683146032508811,-0.4112871031306116,-0.7126439717911205,0.5320857734919182,-0.4112871031306116,-0.7400862439242719,0.494534094663473,-0.4112871031306116,-0.7656885450454439,0.45575292615583646,-0.4112871031306116,-0.7893872238000956,0.4158386840643636,-0.4112871031306116,-0.8111233615388279,0.3748906014838672,-0.4112871031306116,-0.8308429187984296,0.3330104817995106,-0.4112871031306116,-0.8484968696524976,0.29030244558755136,-0.4112871031306116,-0.8640413235976139,0.2468726717551825,-0.4112871031306116,-0.8774376346720533,0.20282913356303994,-0.4112871031306116,-0.8886524975357332,0.15828133018666427,-0.4112871031306116,-0.8976580302725363,0.11334001448429702,-0.4112871031306116,-0.9044318437091482,0.06811691764782486,-0.4112871031306116,-0.9089570970780684,0.02272447142143185,-0.4112871031306116,-0.9112225398864133,-0.02272447142143174,-0.4112871031306116,-0.9112225398864133,-0.06811691764782475,-0.4112871031306116,-0.9089570970780684,-0.11334001448429691,-0.4112871031306116,-0.9044318437091482,-0.15828133018666415,-0.4112871031306116,-0.8976580302725363,-0.20282913356303983,-0.4112871031306116,-0.8886524975357332,-0.24687267175518232,-0.4112871031306116,-0.8774376346720534,-0.29030244558755125,-0.4112871031306116,-0.8640413235976139,-0.3330104817995105,-0.4112871031306116,-0.8484968696524977,-0.37489060148386694,-0.4112871031306116,-0.8308429187984298,-0.41583868406436314,-0.4112871031306116,-0.8111233615388281,-0.4557529261558368,-0.4112871031306116,-0.7893872238000956,-0.494534094663473,-0.4112871031306116,-0.765688545045444,-0.5320857734919179,-0.4112871031306116,-0.740086243924272,-0.5683146032508811,-0.4112871031306116,-0.7126439717911205,-0.6031305133612884,-0.4112871031306116,-0.6834299544583764,-0.6364469459849365,-0.4112871031306116,-0.6525168225761518,-0.6681810712209132,-0.4112871031306116,-0.619981431061046,-0.698253993033785,-0.4112871031306116,-0.5859046680227122,-0.7265909454015709,-0.4112871031306116,-0.5503712536632714,-0.753121478195853,-0.4112871031306116,-0.5134695296495432,-0.777779632331897,-0.4112871031306116,-0.47529123948174007,-0.8005041037533288,-0.4112871031306116,-0.43593130040467326,-0.8212383958436777,-0.4112871031306116,-0.39548756742852553,-0.8399309598858679,-0.4112871031306116,-0.35406059004587664,-0.8565353232204492,-0.4112871031306116,-0.3117533622498244,-0.871010204783953,-0.4112871031306116,-0.26867106647468747,-0.8833196177401188,-0.4112871031306116,-0.22492081209590176,-0.89343295894884,-0.4112871031306116,-0.18061136913923775,-0.9013250850503918,-0.4112871031306116,-0.1358528978613774,-0.9069763749757852,-0.4112871031306116,-0.09075667487415785,-0.9103727787278364,-0.4112871031306116,-0.04543481649338427,-0.9115058523116731,-0.4112871031306116,-1.1162727244375705e-16,-0.9103727787278364,-0.4112871031306116,0.04543481649338404,-0.9069763749757852,-0.4112871031306116,0.09075667487415763,-0.9013250850503918,-0.4112871031306116,0.13585289786137716,-0.8934329589488401,-0.4112871031306116,0.18061136913923753,-0.8833196177401189,-0.4112871031306116,0.22492081209590156,-0.8710102047839532,-0.4112871031306116,0.26867106647468686,-0.8565353232204493,-0.4112871031306116,0.3117533622498242,-0.8399309598858677,-0.4112871031306116,0.3540605900458768,-0.8212383958436779,-0.4112871031306116,0.39548756742852537,-0.8005041037533288,-0.4112871031306116,0.43593130040467304,-0.7777796323318971,-0.4112871031306116,0.47529123948173985,-0.7531214781958531,-0.4112871031306116,0.5134695296495431,-0.726590945401571,-0.4112871031306116,0.5503712536632712,-0.6982539930337851,-0.4112871031306116,0.585904668022712,-0.6681810712209134,-0.4112871031306116,0.6199814310610458,-0.6364469459849366,-0.4112871031306116,0.6525168225761516,-0.603130513361289,-0.4112871031306116,0.683429954458376,-0.5683146032508811,-0.4112871031306116,0.7126439717911204,-0.5320857734919187,-0.4112871031306116,0.7400862439242715,-0.49453409466347276,-0.4112871031306116,0.765688545045444,-0.45575292615583624,-0.4112871031306116,0.7893872238000957,-0.4158386840643637,-0.4112871031306116,0.8111233615388278,-0.37489060148386716,-0.4112871031306116,0.8308429187984296,-0.33301048179951126,-0.4112871031306116,0.8484968696524974,-0.29030244558755186,-0.4112871031306116,0.8640413235976137,-0.24687267175518196,-0.4112871031306116,0.8774376346720535,-0.20282913356304005,-0.4112871031306116,0.8886524975357332,-0.15828133018666418,-0.4112871031306116,0.8976580302725363,-0.11334001448429754,-0.4112871031306116,0.9044318437091481,-0.06811691764782517,-0.4112871031306116,0.9089570970780684,-0.02272447142143196,-0.4112871031306116,0.9112225398864133,0.022724471421431625,-0.4112871031306116,0.9112225398864133,0.06811691764782483,-0.4112871031306116,0.9089570970780684,0.1133400144842964,-0.4112871031306116,0.9044318437091483,0.15828133018666385,-0.4112871031306116,0.8976580302725364,0.20282913356303972,-0.4112871031306116,0.8886524975357332,0.24687267175518243,-0.4112871031306116,0.8774376346720533,0.2903024455875515,-0.4112871031306116,0.8640413235976138,0.3330104817995102,-0.4112871031306116,0.8484968696524978,0.3748906014838668,-0.4112871031306116,0.8308429187984298,0.41583868406436336,-0.4112871031306116,0.811123361538828,0.4557529261558367,-0.4112871031306116,0.7893872238000956,0.49453409466347314,-0.4112871031306116,0.7656885450454438,0.5320857734919178,-0.4112871031306116,0.7400862439242721,0.568314603250881,-0.4112871031306116,0.7126439717911205,0.6031305133612881,-0.4112871031306116,0.6834299544583766,0.6364469459849366,-0.4112871031306116,0.6525168225761516,0.6681810712209134,-0.4112871031306116,0.6199814310610458,0.6982539930337849,-0.4112871031306116,0.5859046680227123,0.7265909454015708,-0.4112871031306116,0.5503712536632716,0.7531214781958527,-0.4112871031306116,0.5134695296495436,0.7777796323318972,-0.4112871031306116,0.47529123948173985,0.8005041037533286,-0.4112871031306116,0.4359313004046737,0.8212383958436777,-0.4112871031306116,0.39548756742852564,0.8399309598858677,-0.4112871031306116,0.35406059004587676,0.856535323220449,-0.4112871031306116,0.3117533622498249,0.871010204783953,-0.4112871031306116,0.2686710664746876,0.8833196177401188,-0.4112871031306116,0.22492081209590187,0.89343295894884,-0.4112871031306116,0.18061136913923787,0.9013250850503918,-0.4112871031306116,0.13585289786137708,0.9069763749757851,-0.4112871031306116,0.09075667487415835,0.9103727787278364,-0.4112871031306116,0.04543481649338437,0.9115058523116731,-0.4112871031306116,0.0,0.9308737486442042,-0.36534102436639493,0.0,0.9297165992394024,-0.36534102436639493,-0.04640022643946356,0.9262480278807403,-0.36534102436639493,-0.09268509460507893,0.920476657983131,-0.36534102436639493,-0.1387395330218428,0.9124168380814898,-0.36534102436639493,-0.1844490430994145,0.9020886061580143,-0.36534102436639493,-0.22969998379365233,0.889517639824605,-0.36534102436639493,-0.2743798541361546,0.8747351924842752,-0.36534102436639493,-0.3183775729293932,0.8577780156302673,-0.36534102436639493,-0.3615837549120729,0.8386882674760497,-0.36534102436639493,-0.4038909827081253,0.8175134081433557,-0.36534102436639493,-0.44519407388323157,0.7943060816688428,-0.36534102436639493,-0.48539034244492635,0.7691239851227228,-0.36534102436639493,-0.5243798541361546,0.7420297251647551,-0.36534102436639493,-0.5620656748875804,0.7130906623942249,-0.36534102436639493,-0.5983541118109564,0.6823787438808812,-0.36534102436639493,-0.633154946134407,0.6499703242931839,-0.36534102436639493,-0.6663816575005096,0.6159459760685686,-0.36534102436639493,-0.6979516390695357,0.5803902890976714,-0.36534102436639493,-0.7277864028930704,0.5433916604205327,-0.36534102436639493,-0.7558117750474187,0.505042074457627,-0.36534102436639493,-0.7819580800416668,0.465436874322102,-0.36534102436639493,-0.8061603140419311,0.42467452478177553,-0.36534102436639493,-0.8283583064811304,0.3828563674602079,-0.36534102436639493,-0.8484968696524976,0.3400863688854597,-0.36534102436639493,-0.8665259359149131,0.2964708620129211,-0.36534102436639493,-0.8824006821689503,0.2521182818648304,-0.36534102436639493,-0.8960816412941619,0.20713889594372384,-0.36534102436639493,-0.9075348002705617,0.1616445300900503,-0.36534102436639493,-0.9167316847403497,0.11574829046551238,-0.36534102436639493,-0.9236494297996533,0.06956428235332708,-0.36534102436639493,-0.9282708368442799,0.023207326474513137,-0.36534102436639493,-0.930584416328158,-0.023207326474513023,-0.36534102436639493,-0.930584416328158,-0.06956428235332697,-0.36534102436639493,-0.9282708368442799,-0.11574829046551227,-0.36534102436639493,-0.9236494297996533,-0.1616445300900502,-0.36534102436639493,-0.9167316847403497,-0.20713889594372373,-0.36534102436639493,-0.9075348002705617,-0.25211828186483026,-0.36534102436639493,-0.896081641294162,-0.296470862012921,-0.36534102436639493,-0.8824006821689503,-0.34008636888545957,-0.36534102436639493,-0.8665259359149132,-0.3828563674602077,-0.36534102436639493,-0.8484968696524977,-0.42467452478177503,-0.36534102436639493,-0.8283583064811305,-0.46543687432210235,-0.36534102436639493,-0.806160314041931,-0.505042074457627,-0.36534102436639493,-0.7819580800416669,-0.5433916604205323,-0.36534102436639493,-0.7558117750474188,-0.5803902890976714,-0.36534102436639493,-0.7277864028930705,-0.6159459760685685,-0.36534102436639493,-0.697951639069536,-0.6499703242931838,-0.36534102436639493,-0.6663816575005097,-0.6823787438808812,-0.36534102436639493,-0.633154946134407,-0.7130906623942248,-0.36534102436639493,-0.5983541118109567,-0.7420297251647551,-0.36534102436639493,-0.5620656748875804,-0.7691239851227227,-0.36534102436639493,-0.5243798541361548,-0.7943060816688426,-0.36534102436639493,-0.48539034244492646,-0.8175134081433558,-0.36534102436639493,-0.44519407388323146,-0.8386882674760496,-0.36534102436639493,-0.4038909827081254,-0.8577780156302675,-0.36534102436639493,-0.3615837549120728,-0.8747351924842751,-0.36534102436639493,-0.3183775729293934,-0.8895176398246049,-0.36534102436639493,-0.274379854136155,-0.9020886061580142,-0.36534102436639493,-0.22969998379365253,-0.9124168380814898,-0.36534102436639493,-0.18444904309941454,-0.920476657983131,-0.36534102436639493,-0.13873953302184305,-0.9262480278807403,-0.36534102436639493,-0.092685094605079,-0.9297165992394024,-0.36534102436639493,-0.04640022643946385,-0.9308737486442042,-0.36534102436639493,-1.1399915566874225e-16,-0.9297165992394024,-0.36534102436639493,0.04640022643946362,-0.9262480278807403,-0.36534102436639493,0.09268509460507876,-0.920476657983131,-0.36534102436639493,0.13873953302184283,-0.9124168380814899,-0.36534102436639493,0.18444904309941432,-0.9020886061580143,-0.36534102436639493,0.22969998379365233,-0.889517639824605,-0.36534102436639493,0.27437985413615434,-0.8747351924842752,-0.36534102436639493,0.31837757292939317,-0.8577780156302673,-0.36534102436639493,0.36158375491207295,-0.8386882674760497,-0.36534102436639493,0.4038909827081252,-0.8175134081433558,-0.36534102436639493,0.4451940738832313,-0.7943060816688428,-0.36534102436639493,0.48539034244492624,-0.7691239851227228,-0.36534102436639493,0.5243798541361546,-0.7420297251647552,-0.36534102436639493,0.5620656748875802,-0.7130906623942249,-0.36534102436639493,0.5983541118109564,-0.6823787438808814,-0.36534102436639493,0.6331549461344068,-0.649970324293184,-0.36534102436639493,0.6663816575005095,-0.6159459760685689,-0.36534102436639493,0.6979516390695355,-0.5803902890976714,-0.36534102436639493,0.7277864028930703,-0.5433916604205331,-0.36534102436639493,0.7558117750474183,-0.5050420744576268,-0.36534102436639493,0.7819580800416669,-0.46543687432210173,-0.36534102436639493,0.8061603140419312,-0.42467452478177564,-0.36534102436639493,0.8283583064811303,-0.38285636746020785,-0.36534102436639493,0.8484968696524976,-0.34008636888546034,-0.36534102436639493,0.8665259359149129,-0.2964708620129216,-0.36534102436639493,0.8824006821689501,-0.2521182818648299,-0.36534102436639493,0.8960816412941621,-0.20713889594372395,-0.36534102436639493,0.9075348002705617,-0.16164453009005023,-0.36534102436639493,0.9167316847403497,-0.11574829046551291,-0.36534102436639493,0.9236494297996531,-0.06956428235332739,-0.36534102436639493,0.9282708368442799,-0.023207326474513252,-0.36534102436639493,0.930584416328158,0.02320732647451291,-0.36534102436639493,0.930584416328158,0.06956428235332705,-0.36534102436639493,0.9282708368442799,0.11574829046551174,-0.36534102436639493,0.9236494297996534,0.1616445300900499,-0.36534102436639493,0.9167316847403498,0.20713889594372362,-0.36534102436639493,0.9075348002705617,0.2521182818648303,-0.36534102436639493,0.8960816412941619,0.2964708620129212,-0.36534102436639493,0.8824006821689502,0.34008636888545923,-0.36534102436639493,0.8665259359149133,0.38285636746020757,-0.36534102436639493,0.8484968696524977,0.4246745247817753,-0.36534102436639493,0.8283583064811304,0.46543687432210223,-0.36534102436639493,0.806160314041931,0.5050420744576273,-0.36534102436639493,0.7819580800416667,0.5433916604205322,-0.36534102436639493,0.7558117750474189,0.5803902890976713,-0.36534102436639493,0.7277864028930705,0.615945976068568,-0.36534102436639493,0.6979516390695362,0.649970324293184,-0.36534102436639493,0.6663816575005095,0.6823787438808814,-0.36534102436639493,0.6331549461344068,0.7130906623942247,-0.36534102436639493,0.5983541118109568,0.742029725164755,-0.36534102436639493,0.5620656748875805,0.7691239851227224,-0.36534102436639493,0.5243798541361553,0.7943060816688429,-0.36534102436639493,0.48539034244492624,0.8175134081433555,-0.36534102436639493,0.44519407388323196,0.8386882674760496,-0.36534102436639493,0.4038909827081255,0.8577780156302673,-0.36534102436639493,0.3615837549120729,0.874735192484275,-0.36534102436639493,0.3183775729293939,0.8895176398246049,-0.36534102436639493,0.27437985413615507,0.9020886061580142,-0.36534102436639493,0.22969998379365264,0.9124168380814898,-0.36534102436639493,0.18444904309941465,0.920476657983131,-0.36534102436639493,0.13873953302184275,0.9262480278807402,-0.36534102436639493,0.09268509460507951,0.9297165992394024,-0.36534102436639493,0.04640022643946396,0.9308737486442042,-0.36534102436639493,0.0,0.9479273461671318,-0.3184866502516844,0.0,0.9467489977971082,-0.3184866502516844,-0.04725027811169495,0.9432168822467485,-0.3184866502516844,-0.09438308458714857,0.9373397809118383,-0.3184866502516844,-0.1412812398431163,0.9291323051923054,-0.3184866502516844,-0.1878281476762574,0.9186148601659758,-0.3184866502516844,-0.23390808513966757,0.9058135938583364,-0.3184866502516844,-0.2794064902483585,0.8907603322344232,-0.3184866502516844,-0.3242102467984032,0.8734925000744572,-0.3184866502516844,-0.3682079655916419,0.8540530279299453,-0.3184866502516844,-0.41129026136677904,0.8324902453915638,-0.3184866502516844,-0.4533500247483783,0.8088577609341816,-0.3184866502516844,-0.4942826885376462,0.7832143286377448,-0.3184866502516844,-0.5339864876829639,0.755623702115377,-0.3184866502516844,-0.5723627122838397,0.7261544760118512,-0.3184866502516844,-0.6093159529992753,0.6948799154664957,-0.3184866502516844,-0.6447543382504201,0.6618777739645134,-0.3184866502516844,-0.6785897626277911,0.6272301000295667,-0.3184866502516844,-0.7107381059352039,0.5910230332382201,-0.3184866502516844,-0.7411194423258339,0.5533465900633805,-0.3184866502516844,-0.7696582390104654,0.5142944400791618,-0.3184866502516844,-0.7962835440439058,0.4739636730835658,-0.3184866502516844,-0.8209291627227018,0.43245455771794655,-0.3184866502516844,-0.8435338221556007,0.38987029218336816,-0.3184866502516844,-0.8640413235976139,0.34631674767361825,-0.3184866502516844,-0.8824006821689503,0.3019022051627387,-0.3184866502516844,-0.8985662536114614,0.2567370862014625,-0.3184866502516844,-0.9124978477674587,0.21093367839184077,-0.3184866502516844,-0.9241608284987786,0.16460585622257196,-0.3184866502516844,-0.9335261997976786,0.11786879795908037,-0.3184866502516844,-0.9405706778754815,0.07083869929220067,-0.3184866502516844,-0.9452767490497428,0.023632484457382412,-0.3184866502516844,-0.9476327132860246,-0.023632484457382294,-0.3184866502516844,-0.9476327132860246,-0.07083869929220056,-0.3184866502516844,-0.9452767490497428,-0.11786879795908026,-0.3184866502516844,-0.9405706778754815,-0.16460585622257184,-0.3184866502516844,-0.9335261997976786,-0.21093367839184066,-0.3184866502516844,-0.9241608284987786,-0.25673708620146235,-0.3184866502516844,-0.9124978477674588,-0.3019022051627386,-0.3184866502516844,-0.8985662536114614,-0.34631674767361814,-0.3184866502516844,-0.8824006821689504,-0.38987029218336794,-0.3184866502516844,-0.864041323597614,-0.4324545577179461,-0.3184866502516844,-0.8435338221556009,-0.47396367308356613,-0.3184866502516844,-0.8209291627227017,-0.5142944400791618,-0.3184866502516844,-0.7962835440439059,-0.5533465900633802,-0.3184866502516844,-0.7696582390104655,-0.59102303323822,-0.3184866502516844,-0.741119442325834,-0.6272301000295665,-0.3184866502516844,-0.7107381059352041,-0.6618777739645133,-0.3184866502516844,-0.6785897626277912,-0.6948799154664957,-0.3184866502516844,-0.6447543382504201,-0.7261544760118511,-0.3184866502516844,-0.6093159529992755,-0.755623702115377,-0.3184866502516844,-0.5723627122838397,-0.7832143286377447,-0.3184866502516844,-0.5339864876829641,-0.8088577609341815,-0.3184866502516844,-0.4942826885376463,-0.8324902453915639,-0.3184866502516844,-0.4533500247483782,-0.8540530279299452,-0.3184866502516844,-0.41129026136677915,-0.8734925000744573,-0.3184866502516844,-0.36820796559164176,-0.8907603322344231,-0.3184866502516844,-0.3242102467984034,-0.9058135938583363,-0.3184866502516844,-0.2794064902483589,-0.9186148601659757,-0.3184866502516844,-0.2339080851396678,-0.9291323051923054,-0.3184866502516844,-0.18782814767625744,-0.9373397809118383,-0.3184866502516844,-0.14128123984311658,-0.9432168822467485,-0.3184866502516844,-0.09438308458714864,-0.9467489977971082,-0.3184866502516844,-0.04725027811169526,-0.9479273461671318,-0.3184866502516844,-1.160876190307823e-16,-0.9467489977971082,-0.3184866502516844,0.047250278111695014,-0.9432168822467485,-0.3184866502516844,0.0943830845871484,-0.9373397809118383,-0.3184866502516844,0.14128123984311633,-0.9291323051923055,-0.3184866502516844,0.18782814767625722,-0.9186148601659758,-0.3184866502516844,0.23390808513966757,-0.9058135938583365,-0.3184866502516844,0.27940649024835823,-0.8907603322344232,-0.3184866502516844,0.32421024679840316,-0.8734925000744572,-0.3184866502516844,0.36820796559164193,-0.8540530279299453,-0.3184866502516844,0.41129026136677893,-0.8324902453915639,-0.3184866502516844,0.45335002474837804,-0.8088577609341816,-0.3184866502516844,0.49428268853764606,-0.7832143286377448,-0.3184866502516844,0.5339864876829639,-0.7556237021153771,-0.3184866502516844,0.5723627122838395,-0.7261544760118512,-0.3184866502516844,0.6093159529992753,-0.694879915466496,-0.3184866502516844,0.6447543382504198,-0.6618777739645135,-0.3184866502516844,0.678589762627791,-0.627230100029567,-0.3184866502516844,0.7107381059352037,-0.5910230332382201,-0.3184866502516844,0.7411194423258338,-0.553346590063381,-0.3184866502516844,0.7696582390104649,-0.5142944400791616,-0.3184866502516844,0.7962835440439059,-0.4739636730835655,-0.3184866502516844,0.8209291627227019,-0.43245455771794666,-0.3184866502516844,0.8435338221556006,-0.3898702921833681,-0.3184866502516844,0.8640413235976139,-0.3463167476736189,-0.3184866502516844,0.8824006821689501,-0.30190220516273925,-0.3184866502516844,0.8985662536114611,-0.25673708620146196,-0.3184866502516844,0.9124978477674588,-0.2109336783918409,-0.3184866502516844,0.9241608284987786,-0.16460585622257187,-0.3184866502516844,0.9335261997976786,-0.11786879795908091,-0.3184866502516844,0.9405706778754814,-0.07083869929220099,-0.3184866502516844,0.9452767490497428,-0.023632484457382526,-0.3184866502516844,0.9476327132860246,0.023632484457382176,-0.3184866502516844,0.9476327132860246,0.07083869929220064,-0.3184866502516844,0.9452767490497428,0.11786879795907973,-0.3184866502516844,0.9405706778754817,0.16460585622257154,-0.3184866502516844,0.9335261997976787,0.21093367839184055,-0.3184866502516844,0.9241608284987786,0.25673708620146246,-0.3184866502516844,0.9124978477674587,0.30190220516273886,-0.3184866502516844,0.8985662536114613,0.3463167476736178,-0.3184866502516844,0.8824006821689505,0.38987029218336783,-0.3184866502516844,0.864041323597614,0.4324545577179464,-0.3184866502516844,0.8435338221556008,0.473963673083566,-0.3184866502516844,0.8209291627227017,0.514294440079162,-0.3184866502516844,0.7962835440439057,0.55334659006338,-0.3184866502516844,0.7696582390104656,0.5910230332382199,-0.3184866502516844,0.741119442325834,0.627230100029566,-0.3184866502516844,0.7107381059352045,0.6618777739645135,-0.3184866502516844,0.678589762627791,0.694879915466496,-0.3184866502516844,0.6447543382504198,0.726154476011851,-0.3184866502516844,0.6093159529992757,0.7556237021153769,-0.3184866502516844,0.5723627122838398,0.7832143286377444,-0.3184866502516844,0.5339864876829645,0.8088577609341817,-0.3184866502516844,0.49428268853764606,0.8324902453915636,-0.3184866502516844,0.4533500247483787,0.8540530279299452,-0.3184866502516844,0.41129026136677926,0.8734925000744572,-0.3184866502516844,0.3682079655916419,0.8907603322344229,-0.3184866502516844,0.3242102467984039,0.9058135938583363,-0.3184866502516844,0.279406490248359,0.9186148601659757,-0.3184866502516844,0.23390808513966788,0.9291323051923054,-0.3184866502516844,0.18782814767625755,0.9373397809118383,-0.3184866502516844,0.14128123984311625,0.9432168822467484,-0.3184866502516844,0.09438308458714917,0.9467489977971082,-0.3184866502516844,0.04725027811169537,0.9479273461671318,-0.3184866502516844,0.0,0.962624246950012,-0.2708404681430051,0.0,0.9614276291744777,-0.2708404681430051,-0.047982858147685006,0.9578407508282061,-0.2708404681430051,-0.09584642334973233,0.9518725294559226,-0.2708404681430051,-0.14347169924156317,0.9435378029963243,-0.2708404681430051,-0.19074028188336933,0.9328572928926274,-0.2708404681430051,-0.23753465413096353,0.9198575525757937,-0.2708404681430051,-0.28373847780191613,0.9045709014485184,-0.2708404681430051,-0.32923688291060704,0.8870353445341008,-0.2708404681430051,-0.37391675325310936,0.8672944779899711,-0.2708404681430051,-0.41766700763189485,0.845397380720771,-0.2708404681430051,-0.4603788760211936,0.8213984923604658,-0.2708404681430051,-0.5019461699864156,0.7953574779268355,-0.2708404681430051,-0.5422655466853312,0.7673390794848408,-0.2708404681430051,-0.5812367657946588,0.7374129551876475,-0.2708404681430051,-0.6187629387233031,0.7056535060954833,-0.2708404681430051,-0.6547507694926592,0.6721396912028784,-0.2708404681430051,-0.6891107866851143,0.6369548311341647,-0.2708404681430051,-0.7217575658840892,0.6001864009952756,-0.2708404681430051,-0.7526099420525987,0.5619258128968493,-0.2708404681430051,-0.7815912113223209,0.5222681886893192,-0.2708404681430051,-0.8086293216915,0.4813121234750059,-0.2708404681430051,-0.8336570521575755,0.4391594404851586,-0.2708404681430051,-0.856612179839185,0.395914937931357,-0.2708404681430051,-0.8774376346720533,0.35168612846064695,-0.2708404681430051,-0.8960816412941619,0.3065829718621596,-0.2708404681430051,-0.9124978477674587,0.26071760168974906,-0.2708404681430051,-0.9266454408160777,0.21420404648031055,-0.2708404681430051,-0.9384892472945754,0.1671579462608708,-0.2708404681430051,-0.9479998216339102,0.11969626504926022,-0.2708404681430051,-0.955153519047768,0.0719370000631357,-0.2708404681430051,-0.959932554317226,0.02399888836030544,-0.2708404681430051,-0.9623250460076093,-0.023998888360305323,-0.2708404681430051,-0.9623250460076093,-0.07193700006313558,-0.2708404681430051,-0.959932554317226,-0.11969626504926012,-0.2708404681430051,-0.955153519047768,-0.1671579462608707,-0.2708404681430051,-0.9479998216339102,-0.21420404648031044,-0.2708404681430051,-0.9384892472945754,-0.2607176016897489,-0.2708404681430051,-0.9266454408160778,-0.3065829718621595,-0.2708404681430051,-0.9124978477674587,-0.35168612846064684,-0.2708404681430051,-0.896081641294162,-0.39591493793135674,-0.2708404681430051,-0.8774376346720534,-0.4391594404851581,-0.2708404681430051,-0.8566121798391853,-0.48131212347500624,-0.2708404681430051,-0.8336570521575754,-0.5222681886893192,-0.2708404681430051,-0.8086293216915001,-0.561925812896849,-0.2708404681430051,-0.781591211322321,-0.6001864009952755,-0.2708404681430051,-0.7526099420525988,-0.6369548311341645,-0.2708404681430051,-0.7217575658840895,-0.6721396912028783,-0.2708404681430051,-0.6891107866851144,-0.7056535060954833,-0.2708404681430051,-0.6547507694926592,-0.7374129551876474,-0.2708404681430051,-0.6187629387233033,-0.7673390794848408,-0.2708404681430051,-0.5812367657946588,-0.7953574779268354,-0.2708404681430051,-0.5422655466853314,-0.8213984923604657,-0.2708404681430051,-0.5019461699864157,-0.8453973807207711,-0.2708404681430051,-0.4603788760211935,-0.867294477989971,-0.2708404681430051,-0.41766700763189496,-0.8870353445341009,-0.2708404681430051,-0.37391675325310925,-0.9045709014485183,-0.2708404681430051,-0.3292368829106072,-0.9198575525757937,-0.2708404681430051,-0.28373847780191647,-0.9328572928926273,-0.2708404681430051,-0.23753465413096375,-0.9435378029963243,-0.2708404681430051,-0.19074028188336936,-0.9518725294559226,-0.2708404681430051,-0.14347169924156344,-0.9578407508282061,-0.2708404681430051,-0.0958464233497324,-0.9614276291744777,-0.2708404681430051,-0.04798285814768532,-0.962624246950012,-0.2708404681430051,-1.1788747028089635e-16,-0.9614276291744777,-0.2708404681430051,0.047982858147685076,-0.9578407508282061,-0.2708404681430051,0.09584642334973216,-0.9518725294559226,-0.2708404681430051,0.1434716992415632,-0.9435378029963244,-0.2708404681430051,0.19074028188336914,-0.9328572928926274,-0.2708404681430051,0.23753465413096353,-0.9198575525757938,-0.2708404681430051,0.28373847780191586,-0.9045709014485184,-0.2708404681430051,0.329236882910607,-0.8870353445341008,-0.2708404681430051,0.3739167532531094,-0.8672944779899711,-0.2708404681430051,0.41766700763189474,-0.8453973807207711,-0.2708404681430051,0.46037887602119326,-0.8213984923604658,-0.2708404681430051,0.5019461699864155,-0.7953574779268355,-0.2708404681430051,0.5422655466853312,-0.7673390794848409,-0.2708404681430051,0.5812367657946585,-0.7374129551876475,-0.2708404681430051,0.6187629387233031,-0.7056535060954835,-0.2708404681430051,0.6547507694926591,-0.6721396912028785,-0.2708404681430051,0.6891107866851142,-0.6369548311341651,-0.2708404681430051,0.721757565884089,-0.6001864009952756,-0.2708404681430051,0.7526099420525986,-0.5619258128968498,-0.2708404681430051,0.7815912113223205,-0.522268188689319,-0.2708404681430051,0.8086293216915001,-0.4813121234750056,-0.2708404681430051,0.8336570521575755,-0.4391594404851587,-0.2708404681430051,0.8566121798391849,-0.39591493793135696,-0.2708404681430051,0.8774376346720533,-0.3516861284606477,-0.2708404681430051,0.8960816412941617,-0.3065829718621601,-0.2708404681430051,0.9124978477674585,-0.26071760168974856,-0.2708404681430051,0.9266454408160779,-0.2142040464803107,-0.2708404681430051,0.9384892472945754,-0.16715794626087072,-0.2708404681430051,0.9479998216339102,-0.11969626504926077,-0.2708404681430051,0.9551535190477679,-0.07193700006313603,-0.2708404681430051,0.959932554317226,-0.02399888836030556,-0.2708404681430051,0.9623250460076093,0.023998888360305205,-0.2708404681430051,0.9623250460076093,0.07193700006313568,-0.2708404681430051,0.959932554317226,0.11969626504925956,-0.2708404681430051,0.9551535190477681,0.16715794626087035,-0.2708404681430051,0.9479998216339103,0.21420404648031036,-0.2708404681430051,0.9384892472945754,0.260717601689749,-0.2708404681430051,0.9266454408160777,0.30658297186215977,-0.2708404681430051,0.9124978477674586,0.35168612846064656,-0.2708404681430051,0.8960816412941621,0.3959149379313566,-0.2708404681430051,0.8774376346720534,0.43915944048515837,-0.2708404681430051,0.8566121798391851,0.4813121234750061,-0.2708404681430051,0.8336570521575754,0.5222681886893195,-0.2708404681430051,0.8086293216914999,0.561925812896849,-0.2708404681430051,0.7815912113223211,0.6001864009952754,-0.2708404681430051,0.7526099420525988,0.6369548311341641,-0.2708404681430051,0.7217575658840898,0.6721396912028785,-0.2708404681430051,0.6891107866851142,0.7056535060954835,-0.2708404681430051,0.6547507694926591,0.7374129551876473,-0.2708404681430051,0.6187629387233035,0.7673390794848407,-0.2708404681430051,0.5812367657946589,0.7953574779268351,-0.2708404681430051,0.5422655466853319,0.8213984923604658,-0.2708404681430051,0.5019461699864155,0.8453973807207708,-0.2708404681430051,0.46037887602119393,0.867294477989971,-0.2708404681430051,0.41766700763189507,0.8870353445341008,-0.2708404681430051,0.37391675325310936,0.904570901448518,-0.2708404681430051,0.3292368829106077,0.9198575525757937,-0.2708404681430051,0.2837384778019166,0.9328572928926273,-0.2708404681430051,0.23753465413096386,0.9435378029963243,-0.2708404681430051,0.19074028188336947,0.9518725294559226,-0.2708404681430051,0.1434716992415631,0.957840750828206,-0.2708404681430051,0.09584642334973294,0.9614276291744777,-0.2708404681430051,0.04798285814768543,0.962624246950012,-0.2708404681430051,0.0,0.9749279121818236,-0.2225209339563144,0.0,0.97371599998111,-0.2225209339563144,-0.04859614523803738,0.9700832763836519,-0.2225209339563144,-0.09707147280209963,0.9640387729126922,-0.2225209339563144,-0.14530546538998537,0.9555975171562447,-0.2225209339563144,-0.19317820569626934,0.9447804954061425,-0.2225209339563144,-0.2405706745456179,0.9316146004828093,-0.2225209339563144,-0.2873650467932121,0.9161325648754718,-0.2225209339563144,-0.3334449842566223,0.8983728793640322,-0.2225209339563144,-0.37869592495086013,0.8783796973249267,-0.2225209339563144,-0.42300536790752397,0.8562027249588732,-0.2225209339563144,-0.46626315286993225,0.8318970977134251,-0.2225209339563144,-0.5083617341688785,0.8055232432075619,-0.2225209339563144,-0.5491964480981107,0.7771467309991062,-0.2225209339563144,-0.5886657731247948,0.746838109568473,-0.2225209339563144,-0.6266715822880427,0.7146727309240304,-0.2225209339563144,-0.6631193871579975,0.6807305632651348,-0.2225209339563144,-0.6979185727489575,0.6450959921685874,-0.2225209339563144,-0.7309826228025093,0.607857610792794,-0.2225209339563144,-0.7622293348805763,0.5691079996212145,-0.2225209339563144,-0.7915810247336336,0.5289434962926937,-0.2225209339563144,-0.8189647194359906,0.4874639560909117,-0.2225209339563144,-0.8443123388079836,0.4447725036884166,-0.2225209339563144,-0.8675608646740279,0.4009752767624376,-0.2225209339563144,-0.8886524975357332,0.3561811621198982,-0.2225209339563144,-0.9075348002705617,0.3105015249876574,-0.2225209339563144,-0.9241608284987786,0.26404993214100775,-0.2225209339563144,-0.9384892472945754,0.21694186955877912,-0.2225209339563144,-0.9504844339512096,0.16929445530699885,-0.2225209339563144,-0.960116566544665,0.12122614836492614,-0.2225209339563144,-0.9673616980756549,0.07285645411736502,-0.2225209339563144,-0.9722018160056346,0.024305627245448182,-0.2225209339563144,-0.9746248870388109,-0.024305627245448064,-0.2225209339563144,-0.9746248870388109,-0.0728564541173649,-0.2225209339563144,-0.9722018160056346,-0.12122614836492603,-0.2225209339563144,-0.9673616980756549,-0.16929445530699874,-0.2225209339563144,-0.960116566544665,-0.216941869558779,-0.2225209339563144,-0.9504844339512096,-0.2640499321410076,-0.2225209339563144,-0.9384892472945755,-0.31050152498765726,-0.2225209339563144,-0.9241608284987786,-0.3561811621198981,-0.2225209339563144,-0.9075348002705618,-0.4009752767624373,-0.2225209339563144,-0.8886524975357333,-0.4447725036884161,-0.2225209339563144,-0.8675608646740282,-0.48746395609091203,-0.2225209339563144,-0.8443123388079835,-0.5289434962926937,-0.2225209339563144,-0.8189647194359907,-0.5691079996212142,-0.2225209339563144,-0.7915810247336337,-0.6078576107927939,-0.2225209339563144,-0.7622293348805764,-0.6450959921685872,-0.2225209339563144,-0.7309826228025095,-0.6807305632651347,-0.2225209339563144,-0.6979185727489576,-0.7146727309240304,-0.2225209339563144,-0.6631193871579975,-0.7468381095684729,-0.2225209339563144,-0.6266715822880429,-0.7771467309991062,-0.2225209339563144,-0.5886657731247948,-0.8055232432075617,-0.2225209339563144,-0.549196448098111,-0.831897097713425,-0.2225209339563144,-0.5083617341688786,-0.8562027249588733,-0.2225209339563144,-0.46626315286993214,-0.8783796973249266,-0.2225209339563144,-0.4230053679075241,-0.8983728793640323,-0.2225209339563144,-0.37869592495086,-0.9161325648754717,-0.2225209339563144,-0.33344498425662245,-0.9316146004828092,-0.2225209339563144,-0.2873650467932125,-0.9447804954061424,-0.2225209339563144,-0.24057067454561812,-0.9555975171562447,-0.2225209339563144,-0.19317820569626937,-0.9640387729126922,-0.2225209339563144,-0.14530546538998565,-0.9700832763836519,-0.2225209339563144,-0.0970714728020997,-0.97371599998111,-0.2225209339563144,-0.048596145238037694,-0.9749279121818236,-0.2225209339563144,-1.1939423470528823e-16,-0.97371599998111,-0.2225209339563144,0.04859614523803745,-0.9700832763836519,-0.2225209339563144,0.09707147280209946,-0.9640387729126922,-0.2225209339563144,0.1453054653899854,-0.9555975171562449,-0.2225209339563144,0.19317820569626912,-0.9447804954061425,-0.2225209339563144,0.2405706745456179,-0.9316146004828094,-0.2225209339563144,0.28736504679321184,-0.9161325648754718,-0.2225209339563144,0.3334449842566222,-0.8983728793640322,-0.2225209339563144,0.3786959249508602,-0.8783796973249267,-0.2225209339563144,0.42300536790752385,-0.8562027249588733,-0.2225209339563144,0.4662631528699319,-0.8318970977134251,-0.2225209339563144,0.5083617341688784,-0.8055232432075619,-0.2225209339563144,0.5491964480981107,-0.7771467309991064,-0.2225209339563144,0.5886657731247946,-0.746838109568473,-0.2225209339563144,0.6266715822880427,-0.7146727309240306,-0.2225209339563144,0.6631193871579972,-0.6807305632651349,-0.2225209339563144,0.6979185727489574,-0.6450959921685877,-0.2225209339563144,0.730982622802509,-0.607857610792794,-0.2225209339563144,0.7622293348805762,-0.569107999621215,-0.2225209339563144,0.7915810247336331,-0.5289434962926934,-0.2225209339563144,0.8189647194359907,-0.4874639560909114,-0.2225209339563144,0.8443123388079837,-0.4447725036884167,-0.2225209339563144,0.8675608646740278,-0.40097527676243755,-0.2225209339563144,0.8886524975357332,-0.3561811621198989,-0.2225209339563144,0.9075348002705615,-0.31050152498765793,-0.2225209339563144,0.9241608284987783,-0.2640499321410072,-0.2225209339563144,0.9384892472945756,-0.21694186955877925,-0.2225209339563144,0.9504844339512096,-0.16929445530699877,-0.2225209339563144,0.960116566544665,-0.12122614836492669,-0.2225209339563144,0.9673616980756548,-0.07285645411736534,-0.2225209339563144,0.9722018160056346,-0.0243056272454483,-0.2225209339563144,0.9746248870388109,0.024305627245447943,-0.2225209339563144,0.9746248870388109,0.072856454117365,-0.2225209339563144,0.9722018160056346,0.12122614836492547,-0.2225209339563144,0.967361698075655,0.1692944553069984,-0.2225209339563144,0.960116566544665,0.2169418695587789,-0.2225209339563144,0.9504844339512096,0.2640499321410077,-0.2225209339563144,0.9384892472945754,0.31050152498765754,-0.2225209339563144,0.9241608284987785,0.35618116211989775,-0.2225209339563144,0.9075348002705619,0.4009752767624372,-0.2225209339563144,0.8886524975357333,0.44477250368841637,-0.2225209339563144,0.867560864674028,0.4874639560909119,-0.2225209339563144,0.8443123388079835,0.5289434962926939,-0.2225209339563144,0.8189647194359905,0.5691079996212141,-0.2225209339563144,0.7915810247336338,0.6078576107927938,-0.2225209339563144,0.7622293348805764,0.6450959921685867,-0.2225209339563144,0.7309826228025098,0.6807305632651349,-0.2225209339563144,0.6979185727489574,0.7146727309240306,-0.2225209339563144,0.6631193871579972,0.7468381095684727,-0.2225209339563144,0.626671582288043,0.7771467309991061,-0.2225209339563144,0.588665773124795,0.8055232432075614,-0.2225209339563144,0.5491964480981114,0.8318970977134252,-0.2225209339563144,0.5083617341688784,0.856202724958873,-0.2225209339563144,0.46626315286993264,0.8783796973249266,-0.2225209339563144,0.4230053679075242,0.8983728793640322,-0.2225209339563144,0.37869592495086013,0.9161325648754715,-0.2225209339563144,0.333444984256623,0.9316146004828092,-0.2225209339563144,0.2873650467932126,0.9447804954061424,-0.2225209339563144,0.24057067454561823,0.9555975171562447,-0.2225209339563144,0.19317820569626948,0.9640387729126922,-0.2225209339563144,0.14530546538998532,0.9700832763836518,-0.2225209339563144,0.09707147280210024,0.97371599998111,-0.2225209339563144,0.04859614523803781,0.9749279121818236,-0.2225209339563144,0.0,0.984807753012208,-0.17364817766693036,0.0,0.9835835593909977,-0.17364817766693036,-0.049088614654414615,0.9799140220655961,-0.17364817766693036,-0.09805518727829043,0.9738082640839739,-0.17364817766693036,-0.14677797925680577,0.9652814653225102,-0.17364817766693036,-0.19513585805223393,0.9543548247464266,-0.17364817766693036,-0.24300859835851787,0.9410555077058202,-0.17364817766693036,-0.29027718100032407,0.9254165783983234,-0.17364817766693036,-0.33682408883346515,0.9074769176662975,-0.17364817766693036,-0.38253359891103694,0.8872811263329343,-0.17364817766693036,-0.4272920701888975,0.8648794143175808,-0.17364817766693036,-0.47098822605520896,0.8403274758059692,-0.17364817766693036,-0.5135134309816272,0.8136863507856958,-0.17364817766693036,-0.5547619606083422,0.785022273291194,-0.17364817766693036,-0.5946312645914946,0.7544065067354889,-0.17364817766693036,-0.633022221559489,0.7219151667381244,-0.17364817766693036,-0.6698393855443409,0.6876290318897392,-0.17364817766693036,-0.7049912232753923,0.6516333429237638,-0.17364817766693036,-0.7383903417454446,0.6140175907945262,-0.17364817766693036,-0.7699537054835439,0.5748752941886384,-0.17364817766693036,-0.799602842994246,0.534303767022807,-0.17364817766693036,-0.8272640418501169,0.4924038765061039,-0.17364817766693036,-0.8528685319524433,0.4492797923681909,-0.17364817766693036,-0.8763526565045315,0.40503872787695794,-0.17364817766693036,-0.8976580302725363,0.3597906732894478,-0.17364817766693036,-0.9167316847403497,0.31364812239874634,-0.17364817766693036,-0.9335261997976786,0.2667257928566874,-0.17364817766693036,-0.9479998216339102,0.21914034096769597,-0.17364817766693036,-0.960116566544665,0.1710100716628344,-0.17364817766693036,-0.9698463103929541,0.12245464437510362,-0.17364817766693036,-0.9771648635025315,0.07359477554723863,-0.17364817766693036,-0.9820540307972396,0.024551938511611724,-0.17364817766693036,-0.9845016570368361,-0.024551938511611606,-0.17364817766693036,-0.9845016570368361,-0.0735947755472385,-0.17364817766693036,-0.9820540307972396,-0.1224546443751035,-0.17364817766693036,-0.9771648635025315,-0.17101007166283433,-0.17364817766693036,-0.9698463103929541,-0.21914034096769586,-0.17364817766693036,-0.960116566544665,-0.26672579285668724,-0.17364817766693036,-0.9479998216339103,-0.31364812239874623,-0.17364817766693036,-0.9335261997976786,-0.3597906732894477,-0.17364817766693036,-0.9167316847403498,-0.40503872787695766,-0.17364817766693036,-0.8976580302725364,-0.4492797923681904,-0.17364817766693036,-0.8763526565045318,-0.49240387650610423,-0.17364817766693036,-0.8528685319524432,-0.534303767022807,-0.17364817766693036,-0.827264041850117,-0.574875294188638,-0.17364817766693036,-0.7996028429942461,-0.6140175907945261,-0.17364817766693036,-0.769953705483544,-0.6516333429237636,-0.17364817766693036,-0.7383903417454448,-0.6876290318897391,-0.17364817766693036,-0.7049912232753924,-0.7219151667381244,-0.17364817766693036,-0.6698393855443409,-0.7544065067354888,-0.17364817766693036,-0.6330222215594892,-0.785022273291194,-0.17364817766693036,-0.5946312645914946,-0.8136863507856957,-0.17364817766693036,-0.5547619606083424,-0.8403274758059691,-0.17364817766693036,-0.5135134309816273,-0.8648794143175809,-0.17364817766693036,-0.47098822605520885,-0.8872811263329342,-0.17364817766693036,-0.4272920701888976,-0.9074769176662976,-0.17364817766693036,-0.38253359891103683,-0.9254165783983233,-0.17364817766693036,-0.3368240888334653,-0.9410555077058201,-0.17364817766693036,-0.29027718100032446,-0.9543548247464265,-0.17364817766693036,-0.2430085983585181,-0.9652814653225102,-0.17364817766693036,-0.19513585805223396,-0.9738082640839739,-0.17364817766693036,-0.14677797925680605,-0.9799140220655961,-0.17364817766693036,-0.0980551872782905,-0.9835835593909977,-0.17364817766693036,-0.04908861465441493,-0.984807753012208,-0.17364817766693036,-1.2060416625018976e-16,-0.9835835593909977,-0.17364817766693036,0.049088614654414685,-0.9799140220655961,-0.17364817766693036,0.09805518727829025,-0.9738082640839739,-0.17364817766693036,0.1467779792568058,-0.9652814653225104,-0.17364817766693036,0.1951358580522337,-0.9543548247464266,-0.17364817766693036,0.24300859835851787,-0.9410555077058204,-0.17364817766693036,0.2902771810003238,-0.9254165783983234,-0.17364817766693036,0.3368240888334651,-0.9074769176662975,-0.17364817766693036,0.38253359891103694,-0.8872811263329343,-0.17364817766693036,0.42729207018889737,-0.8648794143175809,-0.17364817766693036,0.4709882260552086,-0.8403274758059692,-0.17364817766693036,0.513513430981627,-0.8136863507856958,-0.17364817766693036,0.5547619606083422,-0.7850222732911941,-0.17364817766693036,0.5946312645914944,-0.7544065067354889,-0.17364817766693036,0.633022221559489,-0.7219151667381246,-0.17364817766693036,0.6698393855443406,-0.6876290318897393,-0.17364817766693036,0.7049912232753922,-0.6516333429237642,-0.17364817766693036,0.7383903417454444,-0.6140175907945262,-0.17364817766693036,0.7699537054835438,-0.5748752941886389,-0.17364817766693036,0.7996028429942456,-0.5343037670228068,-0.17364817766693036,0.827264041850117,-0.4924038765061036,-0.17364817766693036,0.8528685319524434,-0.449279792368191,-0.17364817766693036,0.8763526565045314,-0.4050387278769579,-0.17364817766693036,0.8976580302725363,-0.35979067328944847,-0.17364817766693036,0.9167316847403495,-0.31364812239874684,-0.17364817766693036,0.9335261997976784,-0.2667257928566869,-0.17364817766693036,0.9479998216339104,-0.2191403409676961,-0.17364817766693036,0.960116566544665,-0.17101007166283433,-0.17364817766693036,0.9698463103929541,-0.12245464437510419,-0.17364817766693036,0.9771648635025314,-0.07359477554723895,-0.17364817766693036,0.9820540307972396,-0.024551938511611842,-0.17364817766693036,0.9845016570368361,0.02455193851161148,-0.17364817766693036,0.9845016570368361,0.0735947755472386,-0.17364817766693036,0.9820540307972396,0.12245464437510295,-0.17364817766693036,0.9771648635025316,0.171010071662834,-0.17364817766693036,0.9698463103929542,0.21914034096769575,-0.17364817766693036,0.960116566544665,0.26672579285668735,-0.17364817766693036,0.9479998216339102,0.3136481223987465,-0.17364817766693036,0.9335261997976785,0.35979067328944736,-0.17364817766693036,0.9167316847403499,0.40503872787695755,-0.17364817766693036,0.8976580302725364,0.44927979236819066,-0.17364817766693036,0.8763526565045316,0.4924038765061041,-0.17364817766693036,0.8528685319524432,0.5343037670228072,-0.17364817766693036,0.8272640418501168,0.5748752941886379,-0.17364817766693036,0.7996028429942462,0.614017590794526,-0.17364817766693036,0.769953705483544,0.6516333429237633,-0.17364817766693036,0.7383903417454452,0.6876290318897393,-0.17364817766693036,0.7049912232753922,0.7219151667381246,-0.17364817766693036,0.6698393855443406,0.7544065067354887,-0.17364817766693036,0.6330222215594893,0.7850222732911939,-0.17364817766693036,0.5946312645914947,0.8136863507856954,-0.17364817766693036,0.5547619606083428,0.8403274758059693,-0.17364817766693036,0.513513430981627,0.8648794143175805,-0.17364817766693036,0.47098822605520935,0.8872811263329342,-0.17364817766693036,0.4272920701888977,0.9074769176662975,-0.17364817766693036,0.38253359891103694,0.925416578398323,-0.17364817766693036,0.3368240888334659,0.9410555077058201,-0.17364817766693036,0.2902771810003245,0.9543548247464265,-0.17364817766693036,0.2430085983585182,0.9652814653225102,-0.17364817766693036,0.19513585805223407,0.9738082640839739,-0.17364817766693036,0.14677797925680572,0.979914022065596,-0.17364817766693036,0.09805518727829104,0.9835835593909977,-0.17364817766693036,0.049088614654415046,0.984807753012208,-0.17364817766693036,0.0,0.9922392066001721,-0.12434370464748522,0.0,0.9910057750966941,-0.12434370464748522,-0.049459042040253046,0.9873085470913199,-0.12434370464748522,-0.09879512110912077,0.9811567144754142,-0.12434370464748522,-0.147885579940539,0.9725655716741559,-0.12434370464748522,-0.19660837191905434,0.9615564776221882,-0.12434370464748522,-0.24484236450694008,0.9481568026619406,-0.12434370464748522,-0.29246764039877093,0.9323998604966461,-0.12434370464748522,-0.3393657956547387,0.9143248253672254,-0.12434370464748522,-0.38542023407150255,0.8939766346589516,-0.12434370464748522,-0.43051645705872194,0.8714058771800304,-0.12434370464748522,-0.4745423483005924,0.8466686673898515,-0.12434370464748522,-0.5173884524946725,0.8198265058896014,-0.12434370464748522,-0.5589482474750113,0.7909461265220785,-0.12434370464748522,-0.5991184090430364,0.7600993304608454,-0.12434370464748522,-0.6377990678477928,0.7273628077011979,-0.12434370464748522,-0.6748940576768839,0.6928179463967536,-0.12434370464748522,-0.710311154540828,0.6565506305156781,-0.12434370464748522,-0.7439623059564272,0.6186510263196083,-0.12434370464748522,-0.7757638498591142,0.5792133581961189,-0.12434370464748522,-0.8056367226000274,0.5383356744020488,-0.12434370464748522,-0.8335066555106986,0.4961196033000859,-0.12434370464748522,-0.8593043595466651,0.45267010069464547,-0.12434370464748522,-0.8829656975509516,0.4080951888952013,-0.12434370464748522,-0.9044318437091482,0.3625056881558059,-0.12434370464748522,-0.9236494297996533,0.31601494115847784,-0.12434370464748522,-0.9405706778754815,0.2687385312254346,-0.12434370464748522,-0.955153519047768,0.22079399496074273,-0.12434370464748522,-0.9673616980756549,0.17230053003580076,-0.12434370464748522,-0.9771648635025315,0.12337869884514692,-0.12434370464748522,-0.984538643114539,0.07415012876935034,-0.12434370464748522,-0.989464704533733,0.024737209790179054,-0.12434370464748522,-0.991930800795265,-0.024737209790178932,-0.12434370464748522,-0.991930800795265,-0.07415012876935022,-0.12434370464748522,-0.989464704533733,-0.12337869884514681,-0.12434370464748522,-0.984538643114539,-0.17230053003580065,-0.12434370464748522,-0.9771648635025315,-0.22079399496074262,-0.12434370464748522,-0.9673616980756549,-0.26873853122543445,-0.12434370464748522,-0.9551535190477681,-0.3160149411584777,-0.12434370464748522,-0.9405706778754815,-0.3625056881558058,-0.12434370464748522,-0.9236494297996534,-0.408095188895201,-0.12434370464748522,-0.9044318437091483,-0.452670100694645,-0.12434370464748522,-0.8829656975509518,-0.49611960330008625,-0.12434370464748522,-0.859304359546665,-0.5383356744020488,-0.12434370464748522,-0.8335066555106987,-0.5792133581961185,-0.12434370464748522,-0.8056367226000275,-0.6186510263196082,-0.12434370464748522,-0.7757638498591143,-0.6565506305156779,-0.12434370464748522,-0.7439623059564274,-0.6928179463967535,-0.12434370464748522,-0.7103111545408282,-0.7273628077011979,-0.12434370464748522,-0.6748940576768839,-0.7600993304608453,-0.12434370464748522,-0.637799067847793,-0.7909461265220785,-0.12434370464748522,-0.5991184090430364,-0.8198265058896013,-0.12434370464748522,-0.5589482474750115,-0.8466686673898514,-0.12434370464748522,-0.5173884524946726,-0.8714058771800305,-0.12434370464748522,-0.4745423483005923,-0.8939766346589515,-0.12434370464748522,-0.43051645705872205,-0.9143248253672255,-0.12434370464748522,-0.38542023407150244,-0.932399860496646,-0.12434370464748522,-0.33936579565473884,-0.9481568026619405,-0.12434370464748522,-0.2924676403987713,-0.9615564776221881,-0.12434370464748522,-0.2448423645069403,-0.9725655716741559,-0.12434370464748522,-0.19660837191905436,-0.9811567144754142,-0.12434370464748522,-0.14788557994053927,-0.9873085470913199,-0.12434370464748522,-0.09879512110912084,-0.9910057750966941,-0.12434370464748522,-0.049459042040253365,-0.9922392066001721,-0.12434370464748522,-1.21514256835141e-16,-0.9910057750966941,-0.12434370464748522,0.049459042040253115,-0.9873085470913199,-0.12434370464748522,0.09879512110912059,-0.9811567144754142,-0.12434370464748522,0.14788557994053902,-0.972565571674156,-0.12434370464748522,0.19660837191905411,-0.9615564776221882,-0.12434370464748522,0.24484236450694008,-0.9481568026619407,-0.12434370464748522,0.29246764039877066,-0.9323998604966461,-0.12434370464748522,0.3393657956547386,-0.9143248253672254,-0.12434370464748522,0.3854202340715026,-0.8939766346589516,-0.12434370464748522,0.4305164570587218,-0.8714058771800305,-0.12434370464748522,0.47454234830059205,-0.8466686673898515,-0.12434370464748522,0.5173884524946724,-0.8198265058896014,-0.12434370464748522,0.5589482474750113,-0.7909461265220786,-0.12434370464748522,0.5991184090430361,-0.7600993304608454,-0.12434370464748522,0.6377990678477928,-0.7273628077011981,-0.12434370464748522,0.6748940576768837,-0.6928179463967538,-0.12434370464748522,0.7103111545408279,-0.6565506305156784,-0.12434370464748522,0.743962305956427,-0.6186510263196083,-0.12434370464748522,0.7757638498591141,-0.5792133581961194,-0.12434370464748522,0.8056367226000269,-0.5383356744020485,-0.12434370464748522,0.8335066555106987,-0.49611960330008564,-0.12434370464748522,0.8593043595466652,-0.4526701006946456,-0.12434370464748522,0.8829656975509514,-0.40809518889520124,-0.12434370464748522,0.9044318437091482,-0.3625056881558066,-0.12434370464748522,0.923649429799653,-0.3160149411584784,-0.12434370464748522,0.9405706778754813,-0.26873853122543406,-0.12434370464748522,0.9551535190477682,-0.22079399496074287,-0.12434370464748522,0.9673616980756549,-0.17230053003580068,-0.12434370464748522,0.9771648635025315,-0.12337869884514747,-0.12434370464748522,0.9845386431145389,-0.07415012876935068,-0.12434370464748522,0.989464704533733,-0.02473720979017917,-0.12434370464748522,0.991930800795265,0.024737209790178807,-0.12434370464748522,0.991930800795265,0.07415012876935032,-0.12434370464748522,0.989464704533733,0.12337869884514624,-0.12434370464748522,0.9845386431145391,0.17230053003580031,-0.12434370464748522,0.9771648635025316,0.2207939949607425,-0.12434370464748522,0.9673616980756549,0.26873853122543456,-0.12434370464748522,0.955153519047768,0.316014941158478,-0.12434370464748522,0.9405706778754814,0.36250568815580547,-0.12434370464748522,0.9236494297996535,0.4080951888952009,-0.12434370464748522,0.9044318437091483,0.45267010069464525,-0.12434370464748522,0.8829656975509517,0.49611960330008614,-0.12434370464748522,0.859304359546665,0.538335674402049,-0.12434370464748522,0.8335066555106985,0.5792133581961184,-0.12434370464748522,0.8056367226000276,0.6186510263196081,-0.12434370464748522,0.7757638498591143,0.6565506305156774,-0.12434370464748522,0.7439623059564278,0.6928179463967538,-0.12434370464748522,0.7103111545408279,0.7273628077011981,-0.12434370464748522,0.6748940576768837,0.7600993304608452,-0.12434370464748522,0.6377990678477932,0.7909461265220784,-0.12434370464748522,0.5991184090430365,0.8198265058896009,-0.12434370464748522,0.5589482474750119,0.8466686673898516,-0.12434370464748522,0.5173884524946724,0.8714058771800302,-0.12434370464748522,0.4745423483005928,0.8939766346589515,-0.12434370464748522,0.4305164570587221,0.9143248253672254,-0.12434370464748522,0.38542023407150255,0.9323998604966458,-0.12434370464748522,0.3393657956547394,0.9481568026619405,-0.12434370464748522,0.29246764039877143,0.9615564776221881,-0.12434370464748522,0.2448423645069404,0.9725655716741559,-0.12434370464748522,0.19660837191905448,0.9811567144754142,-0.12434370464748522,0.14788557994053894,0.9873085470913198,-0.12434370464748522,0.09879512110912139,0.9910057750966941,-0.12434370464748522,0.04945904204025348,0.9922392066001721,-0.12434370464748522,0.0,0.9972037971811801,-0.07473009358642432,0.0,0.9959641943004941,-0.07473009358642432,-0.04970650645470615,0.9922484675065122,-0.07473009358642432,-0.09928943470250161,0.9860658546815019,-0.07473009358642432,-0.14862551377136932,0.977431726775092,-0.07473009358642432,-0.19759208639524514,0.9663675495896699,-0.07473009358642432,-0.24606741395930737,0.9529008304130139,-0.07473009358642432,-0.2939309791613547,0.9370650496308426,-0.07473009358642432,-0.3410637856368083,0.9188995774893002,-0.07473009358642432,-0.3873486538024237,0.8984495762143214,-0.07473009358642432,-0.4326705121831975,0.8757658877312223,-0.07473009358642432,-0.47691668349818545,0.8509049072636626,-0.07473009358642432,-0.5199771647939764,0.8239284431262343,-0.07473009358642432,-0.5617449009293668,0.7949035630592528,-0.07473009358642432,-0.6021160507313094,0.7639024274877875,-0.07473009358642432,-0.6409902451604312,0.7310021101194746,-0.07473009358642432,-0.6782708368442799,0.6962844063271367,-0.07473009358642432,-0.7138651403579187,0.6598356297925979,-0.07473009358642432,-0.7476846626544976,0.6217463979172707,-0.07473009358642432,-0.7796453230729106,0.5821114065330187,-0.07473009358642432,-0.8096676623755669,0.5410291944733978,-0.07473009358642432,-0.8376770402965754,0.49860189859058995,-0.07473009358642432,-0.8636038211092071,0.4549349998270965,-0.07473009358642432,-0.8873835467512816,0.4101370609734936,-0.07473009358642432,-0.9089570970780684,0.3643194567642311,-0.07473009358642432,-0.9282708368442799,0.3175960969824942,-0.07473009358642432,-0.9452767490497428,0.2700831432625332,-0.07473009358642432,-0.959932554317226,0.22189872029353938,-0.07473009358642432,-0.9722018160056346,0.17316262214305522,-0.07473009358642432,-0.9820540307972396,0.12399601443004747,-0.07473009358642432,-0.989464704533733,0.07452113308808736,-0.07473009358642432,-0.9944154131125642,0.024860980467559905,-0.07473009358642432,-0.9968938482921619,-0.024860980467559784,-0.07473009358642432,-0.9968938482921619,-0.07452113308808724,-0.07473009358642432,-0.9944154131125642,-0.12399601443004736,-0.07473009358642432,-0.989464704533733,-0.1731626221430551,-0.07473009358642432,-0.9820540307972396,-0.22189872029353927,-0.07473009358642432,-0.9722018160056346,-0.270083143262533,-0.07473009358642432,-0.9599325543172261,-0.3175960969824941,-0.07473009358642432,-0.9452767490497428,-0.364319456764231,-0.07473009358642432,-0.92827083684428,-0.41013706097349334,-0.07473009358642432,-0.9089570970780685,-0.454934999827096,-0.07473009358642432,-0.8873835467512818,-0.4986018985905903,-0.07473009358642432,-0.863603821109207,-0.5410291944733978,-0.07473009358642432,-0.8376770402965755,-0.5821114065330184,-0.07473009358642432,-0.809667662375567,-0.6217463979172706,-0.07473009358642432,-0.7796453230729107,-0.6598356297925977,-0.07473009358642432,-0.7476846626544978,-0.6962844063271366,-0.07473009358642432,-0.7138651403579188,-0.7310021101194746,-0.07473009358642432,-0.6782708368442799,-0.7639024274877874,-0.07473009358642432,-0.6409902451604315,-0.7949035630592528,-0.07473009358642432,-0.6021160507313094,-0.8239284431262341,-0.07473009358642432,-0.561744900929367,-0.8509049072636625,-0.07473009358642432,-0.5199771647939765,-0.8757658877312224,-0.07473009358642432,-0.47691668349818533,-0.8984495762143213,-0.07473009358642432,-0.43267051218319763,-0.9188995774893003,-0.07473009358642432,-0.3873486538024236,-0.9370650496308426,-0.07473009358642432,-0.34106378563680845,-0.9529008304130138,-0.07473009358642432,-0.2939309791613551,-0.9663675495896697,-0.07473009358642432,-0.2460674139593076,-0.977431726775092,-0.07473009358642432,-0.19759208639524517,-0.9860658546815019,-0.07473009358642432,-0.1486255137713696,-0.9922484675065122,-0.07473009358642432,-0.09928943470250168,-0.9959641943004941,-0.07473009358642432,-0.049706506454706464,-0.9972037971811801,-0.07473009358642432,-1.2212224383155186e-16,-0.9959641943004941,-0.07473009358642432,0.049706506454706215,-0.9922484675065122,-0.07473009358642432,0.09928943470250143,-0.9860658546815019,-0.07473009358642432,0.14862551377136934,-0.9774317267750922,-0.07473009358642432,0.19759208639524492,-0.9663675495896699,-0.07473009358642432,0.24606741395930737,-0.952900830413014,-0.07473009358642432,0.2939309791613544,-0.9370650496308426,-0.07473009358642432,0.3410637856368082,-0.9188995774893002,-0.07473009358642432,0.3873486538024238,-0.8984495762143214,-0.07473009358642432,0.4326705121831974,-0.8757658877312224,-0.07473009358642432,0.4769166834981851,-0.8509049072636626,-0.07473009358642432,0.5199771647939763,-0.8239284431262343,-0.07473009358642432,0.5617449009293668,-0.7949035630592529,-0.07473009358642432,0.6021160507313091,-0.7639024274877875,-0.07473009358642432,0.6409902451604312,-0.7310021101194748,-0.07473009358642432,0.6782708368442797,-0.6962844063271368,-0.07473009358642432,0.7138651403579186,-0.6598356297925982,-0.07473009358642432,0.7476846626544974,-0.6217463979172707,-0.07473009358642432,0.7796453230729105,-0.5821114065330193,-0.07473009358642432,0.8096676623755664,-0.5410291944733976,-0.07473009358642432,0.8376770402965755,-0.4986018985905897,-0.07473009358642432,0.8636038211092072,-0.4549349998270966,-0.07473009358642432,0.8873835467512815,-0.41013706097349356,-0.07473009358642432,0.9089570970780684,-0.36431945676423183,-0.07473009358642432,0.9282708368442797,-0.31759609698249475,-0.07473009358642432,0.9452767490497426,-0.27008314326253263,-0.07473009358642432,0.9599325543172262,-0.22189872029353952,-0.07473009358642432,0.9722018160056346,-0.17316262214305514,-0.07473009358642432,0.9820540307972396,-0.12399601443004804,-0.07473009358642432,0.9894647045337329,-0.0745211330880877,-0.07473009358642432,0.9944154131125642,-0.024860980467560027,-0.07473009358642432,0.9968938482921619,0.02486098046755966,-0.07473009358642432,0.9968938482921619,0.07452113308808733,-0.07473009358642432,0.9944154131125642,0.12399601443004679,-0.07473009358642432,0.9894647045337331,0.17316262214305478,-0.07473009358642432,0.9820540307972397,0.22189872029353916,-0.07473009358642432,0.9722018160056346,0.27008314326253313,-0.07473009358642432,0.959932554317226,0.31759609698249436,-0.07473009358642432,0.9452767490497427,0.36431945676423066,-0.07473009358642432,0.9282708368442801,0.41013706097349323,-0.07473009358642432,0.9089570970780685,0.4549349998270963,-0.07473009358642432,0.8873835467512817,0.4986018985905902,-0.07473009358642432,0.863603821109207,0.541029194473398,-0.07473009358642432,0.8376770402965753,0.5821114065330183,-0.07473009358642432,0.8096676623755671,0.6217463979172705,-0.07473009358642432,0.7796453230729107,0.6598356297925972,-0.07473009358642432,0.7476846626544982,0.6962844063271368,-0.07473009358642432,0.7138651403579186,0.7310021101194748,-0.07473009358642432,0.6782708368442797,0.7639024274877873,-0.07473009358642432,0.6409902451604316,0.7949035630592527,-0.07473009358642432,0.6021160507313095,0.8239284431262338,-0.07473009358642432,0.5617449009293675,0.8509049072636627,-0.07473009358642432,0.5199771647939763,0.8757658877312221,-0.07473009358642432,0.47691668349818583,0.8984495762143213,-0.07473009358642432,0.43267051218319774,0.9188995774893002,-0.07473009358642432,0.3873486538024237,0.9370650496308424,-0.07473009358642432,0.341063785636809,0.9529008304130138,-0.07473009358642432,0.2939309791613552,0.9663675495896697,-0.07473009358642432,0.2460674139593077,0.977431726775092,-0.07473009358642432,0.19759208639524528,0.9860658546815019,-0.07473009358642432,0.14862551377136926,0.9922484675065121,-0.07473009358642432,0.09928943470250223,0.9959641943004941,-0.07473009358642432,0.04970650645470658,0.9972037971811801,-0.07473009358642432,0.0,0.9996891820008162,-0.024930691738072972,0.0,0.9984464895909981,-0.024930691738072972,-0.04983039266224856,0.9947215018906761,-0.024930691738072972,-0.09953689911695471,0.98852347980619,-0.024930691738072972,-0.14899594115720774,0.9798678325970158,-0.024930691738072972,-0.19808455581162238,0.9687760795659178,-0.024930691738072972,-0.24668070104965972,0.9552757965585719,-0.024930691738072972,-0.29466355919734477,0.939400547405668,-0.024930691738072972,-0.3419138373090397,0.9211898004779386,-0.024930691738072972,-0.3883140637485033,0.9006888305615709,-0.024930691738072972,-0.43374888024188724,0.8779486062979536,-0.024930691738072972,-0.47810532867658145,0.8530256634676051,-0.024930691738072972,-0.5212731319328796,0.825981964433314,-0.024930691738072972,-0.5631449680502747,0.7968847440919431,-0.024930691738072972,-0.6036167370467617,0.765806342717882,-0.024930691738072972,-0.6425878197277963,0.7328240261137263,-0.024930691738072972,-0.679961327841466,0.698019793515319,-0.024930691738072972,-0.7156443449579496,0.6614801737287294,-0.024930691738072972,-0.7495481574744022,0.6232960100060084,-0.024930691738072972,-0.7815884751709502,0.5835622341945498,-0.024930691738072972,-0.8116856407694587,0.5423776307215601,-0.024930691738072972,-0.8397648279740751,0.499844591000408,-0.024930691738072972,-0.8657562275011921,0.45606885886943827,-0.024930691738072972,-0.8895952206363238,0.41115926769612654,-0.024930691738072972,-0.9112225398864133,0.36522746980018167,-0.024930691738072972,-0.930584416328158,0.31838765886828646,-0.024930691738072972,-0.9476327132860246,0.270756286050599,-0.024930691738072972,-0.9623250460076093,0.22245177044484565,-0.024930691738072972,-0.9746248870388109,0.17359420468778608,-0.024930691738072972,-0.9845016570368361,0.12430505638599565,-0.024930691738072972,-0.991930800795265,0.07470686612825704,-0.024930691738072972,-0.9968938482921619,0.02492294283034874,-0.024930691738072972,-0.9993784606094611,-0.02492294283034862,-0.024930691738072972,-0.9993784606094611,-0.07470686612825692,-0.024930691738072972,-0.9968938482921619,-0.12430505638599554,-0.024930691738072972,-0.991930800795265,-0.17359420468778597,-0.024930691738072972,-0.9845016570368361,-0.22245177044484554,-0.024930691738072972,-0.9746248870388109,-0.2707562860505988,-0.024930691738072972,-0.9623250460076094,-0.31838765886828635,-0.024930691738072972,-0.9476327132860246,-0.36522746980018156,-0.024930691738072972,-0.9305844163281581,-0.41115926769612626,-0.024930691738072972,-0.9112225398864134,-0.45606885886943777,-0.024930691738072972,-0.889595220636324,-0.49984459100040834,-0.024930691738072972,-0.865756227501192,-0.5423776307215601,-0.024930691738072972,-0.8397648279740753,-0.5835622341945494,-0.024930691738072972,-0.8116856407694588,-0.6232960100060083,-0.024930691738072972,-0.7815884751709503,-0.6614801737287291,-0.024930691738072972,-0.7495481574744024,-0.6980197935153188,-0.024930691738072972,-0.7156443449579497,-0.7328240261137263,-0.024930691738072972,-0.679961327841466,-0.7658063427178818,-0.024930691738072972,-0.6425878197277965,-0.7968847440919431,-0.024930691738072972,-0.6036167370467617,-0.8259819644333138,-0.024930691738072972,-0.5631449680502749,-0.853025663467605,-0.024930691738072972,-0.5212731319328797,-0.8779486062979537,-0.024930691738072972,-0.47810532867658134,-0.9006888305615708,-0.024930691738072972,-0.43374888024188735,-0.9211898004779387,-0.024930691738072972,-0.3883140637485032,-0.9394005474056679,-0.024930691738072972,-0.34191383730903985,-0.9552757965585718,-0.024930691738072972,-0.29466355919734516,-0.9687760795659177,-0.024930691738072972,-0.24668070104965995,-0.9798678325970158,-0.024930691738072972,-0.1980845558116224,-0.98852347980619,-0.024930691738072972,-0.14899594115720802,-0.9947215018906761,-0.024930691738072972,-0.09953689911695478,-0.9984464895909981,-0.024930691738072972,-0.04983039266224888,-0.9996891820008162,-0.024930691738072972,-1.2242661568795353e-16,-0.9984464895909981,-0.024930691738072972,0.04983039266224863,-0.9947215018906761,-0.024930691738072972,0.09953689911695453,-0.98852347980619,-0.024930691738072972,0.14899594115720777,-0.9798678325970159,-0.024930691738072972,0.19808455581162215,-0.9687760795659178,-0.024930691738072972,0.24668070104965972,-0.955275796558572,-0.024930691738072972,0.2946635591973445,-0.939400547405668,-0.024930691738072972,0.3419138373090396,-0.9211898004779386,-0.024930691738072972,0.38831406374850336,-0.9006888305615709,-0.024930691738072972,0.4337488802418871,-0.8779486062979537,-0.024930691738072972,0.4781053286765811,-0.8530256634676051,-0.024930691738072972,0.5212731319328795,-0.825981964433314,-0.024930691738072972,0.5631449680502747,-0.7968847440919432,-0.024930691738072972,0.6036167370467614,-0.765806342717882,-0.024930691738072972,0.6425878197277963,-0.7328240261137265,-0.024930691738072972,0.6799613278414658,-0.6980197935153191,-0.024930691738072972,0.7156443449579495,-0.6614801737287297,-0.024930691738072972,0.749548157474402,-0.6232960100060084,-0.024930691738072972,0.7815884751709501,-0.5835622341945503,-0.024930691738072972,0.8116856407694583,-0.5423776307215599,-0.024930691738072972,0.8397648279740753,-0.49984459100040773,-0.024930691738072972,0.8657562275011922,-0.4560688588694384,-0.024930691738072972,0.8895952206363237,-0.4111592676961265,-0.024930691738072972,0.9112225398864133,-0.3652274698001824,-0.024930691738072972,0.9305844163281578,-0.318387658868287,-0.024930691738072972,0.9476327132860244,-0.2707562860505984,-0.024930691738072972,0.9623250460076095,-0.2224517704448458,-0.024930691738072972,0.9746248870388109,-0.173594204687786,-0.024930691738072972,0.9845016570368361,-0.12430505638599622,-0.024930691738072972,0.9919308007952649,-0.07470686612825737,-0.024930691738072972,0.9968938482921619,-0.024922942830348863,-0.024930691738072972,0.9993784606094611,0.024922942830348495,-0.024930691738072972,0.9993784606094611,0.07470686612825701,-0.024930691738072972,0.9968938482921619,0.12430505638599497,-0.024930691738072972,0.9919308007952651,0.17359420468778564,-0.024930691738072972,0.9845016570368362,0.22245177044484543,-0.024930691738072972,0.9746248870388109,0.2707562860505989,-0.024930691738072972,0.9623250460076093,0.3183876588682866,-0.024930691738072972,0.9476327132860245,0.3652274698001812,-0.024930691738072972,0.9305844163281582,0.41115926769612615,-0.024930691738072972,0.9112225398864134,0.45606885886943804,-0.024930691738072972,0.8895952206363239,0.49984459100040823,-0.024930691738072972,0.865756227501192,0.5423776307215603,-0.024930691738072972,0.839764827974075,0.5835622341945493,-0.024930691738072972,0.8116856407694589,0.6232960100060082,-0.024930691738072972,0.7815884751709503,0.6614801737287287,-0.024930691738072972,0.7495481574744027,0.6980197935153191,-0.024930691738072972,0.7156443449579495,0.7328240261137265,-0.024930691738072972,0.6799613278414658,0.7658063427178817,-0.024930691738072972,0.6425878197277967,0.796884744091943,-0.024930691738072972,0.6036167370467618,0.8259819644333135,-0.024930691738072972,0.5631449680502754,0.8530256634676052,-0.024930691738072972,0.5212731319328795,0.8779486062979533,-0.024930691738072972,0.47810532867658184,0.9006888305615708,-0.024930691738072972,0.43374888024188746,0.9211898004779386,-0.024930691738072972,0.3883140637485033,0.9394005474056677,-0.024930691738072972,0.3419138373090404,0.9552757965585718,-0.024930691738072972,0.29466355919734527,0.9687760795659177,-0.024930691738072972,0.24668070104966006,0.9798678325970158,-0.024930691738072972,0.19808455581162251,0.98852347980619,-0.024930691738072972,0.14899594115720768,0.994721501890676,-0.024930691738072972,0.09953689911695533,0.9984464895909981,-0.024930691738072972,0.049830392662249,0.9996891820008162,-0.024930691738072972,0.0,0.9996891820008162,0.024930691738072972,0.0,0.9984464895909981,0.024930691738072972,-0.04983039266224856,0.9947215018906761,0.024930691738072972,-0.09953689911695471,0.98852347980619,0.024930691738072972,-0.14899594115720774,0.9798678325970158,0.024930691738072972,-0.19808455581162238,0.9687760795659178,0.024930691738072972,-0.24668070104965972,0.9552757965585719,0.024930691738072972,-0.29466355919734477,0.939400547405668,0.024930691738072972,-0.3419138373090397,0.9211898004779386,0.024930691738072972,-0.3883140637485033,0.9006888305615709,0.024930691738072972,-0.43374888024188724,0.8779486062979536,0.024930691738072972,-0.47810532867658145,0.8530256634676051,0.024930691738072972,-0.5212731319328796,0.825981964433314,0.024930691738072972,-0.5631449680502747,0.7968847440919431,0.024930691738072972,-0.6036167370467617,0.765806342717882,0.024930691738072972,-0.6425878197277963,0.7328240261137263,0.024930691738072972,-0.679961327841466,0.698019793515319,0.024930691738072972,-0.7156443449579496,0.6614801737287294,0.024930691738072972,-0.7495481574744022,0.6232960100060084,0.024930691738072972,-0.7815884751709502,0.5835622341945498,0.024930691738072972,-0.8116856407694587,0.5423776307215601,0.024930691738072972,-0.8397648279740751,0.499844591000408,0.024930691738072972,-0.8657562275011921,0.45606885886943827,0.024930691738072972,-0.8895952206363238,0.41115926769612654,0.024930691738072972,-0.9112225398864133,0.36522746980018167,0.024930691738072972,-0.930584416328158,0.31838765886828646,0.024930691738072972,-0.9476327132860246,0.270756286050599,0.024930691738072972,-0.9623250460076093,0.22245177044484565,0.024930691738072972,-0.9746248870388109,0.17359420468778608,0.024930691738072972,-0.9845016570368361,0.12430505638599565,0.024930691738072972,-0.991930800795265,0.07470686612825704,0.024930691738072972,-0.9968938482921619,0.02492294283034874,0.024930691738072972,-0.9993784606094611,-0.02492294283034862,0.024930691738072972,-0.9993784606094611,-0.07470686612825692,0.024930691738072972,-0.9968938482921619,-0.12430505638599554,0.024930691738072972,-0.991930800795265,-0.17359420468778597,0.024930691738072972,-0.9845016570368361,-0.22245177044484554,0.024930691738072972,-0.9746248870388109,-0.2707562860505988,0.024930691738072972,-0.9623250460076094,-0.31838765886828635,0.024930691738072972,-0.9476327132860246,-0.36522746980018156,0.024930691738072972,-0.9305844163281581,-0.41115926769612626,0.024930691738072972,-0.9112225398864134,-0.45606885886943777,0.024930691738072972,-0.889595220636324,-0.49984459100040834,0.024930691738072972,-0.865756227501192,-0.5423776307215601,0.024930691738072972,-0.8397648279740753,-0.5835622341945494,0.024930691738072972,-0.8116856407694588,-0.6232960100060083,0.024930691738072972,-0.7815884751709503,-0.6614801737287291,0.024930691738072972,-0.7495481574744024,-0.6980197935153188,0.024930691738072972,-0.7156443449579497,-0.7328240261137263,0.024930691738072972,-0.679961327841466,-0.7658063427178818,0.024930691738072972,-0.6425878197277965,-0.7968847440919431,0.024930691738072972,-0.6036167370467617,-0.8259819644333138,0.024930691738072972,-0.5631449680502749,-0.853025663467605,0.024930691738072972,-0.5212731319328797,-0.8779486062979537,0.024930691738072972,-0.47810532867658134,-0.9006888305615708,0.024930691738072972,-0.43374888024188735,-0.9211898004779387,0.024930691738072972,-0.3883140637485032,-0.9394005474056679,0.024930691738072972,-0.34191383730903985,-0.9552757965585718,0.024930691738072972,-0.29466355919734516,-0.9687760795659177,0.024930691738072972,-0.24668070104965995,-0.9798678325970158,0.024930691738072972,-0.1980845558116224,-0.98852347980619,0.024930691738072972,-0.14899594115720802,-0.9947215018906761,0.024930691738072972,-0.09953689911695478,-0.9984464895909981,0.024930691738072972,-0.04983039266224888,-0.9996891820008162,0.024930691738072972,-1.2242661568795353e-16,-0.9984464895909981,0.024930691738072972,0.04983039266224863,-0.9947215018906761,0.024930691738072972,0.09953689911695453,-0.98852347980619,0.024930691738072972,0.14899594115720777,-0.9798678325970159,0.024930691738072972,0.19808455581162215,-0.9687760795659178,0.024930691738072972,0.24668070104965972,-0.955275796558572,0.024930691738072972,0.2946635591973445,-0.939400547405668,0.024930691738072972,0.3419138373090396,-0.9211898004779386,0.024930691738072972,0.38831406374850336,-0.9006888305615709,0.024930691738072972,0.4337488802418871,-0.8779486062979537,0.024930691738072972,0.4781053286765811,-0.8530256634676051,0.024930691738072972,0.5212731319328795,-0.825981964433314,0.024930691738072972,0.5631449680502747,-0.7968847440919432,0.024930691738072972,0.6036167370467614,-0.765806342717882,0.024930691738072972,0.6425878197277963,-0.7328240261137265,0.024930691738072972,0.6799613278414658,-0.6980197935153191,0.024930691738072972,0.7156443449579495,-0.6614801737287297,0.024930691738072972,0.749548157474402,-0.6232960100060084,0.024930691738072972,0.7815884751709501,-0.5835622341945503,0.024930691738072972,0.8116856407694583,-0.5423776307215599,0.024930691738072972,0.8397648279740753,-0.49984459100040773,0.024930691738072972,0.8657562275011922,-0.4560688588694384,0.024930691738072972,0.8895952206363237,-0.4111592676961265,0.024930691738072972,0.9112225398864133,-0.3652274698001824,0.024930691738072972,0.9305844163281578,-0.318387658868287,0.024930691738072972,0.9476327132860244,-0.2707562860505984,0.024930691738072972,0.9623250460076095,-0.2224517704448458,0.024930691738072972,0.9746248870388109,-0.173594204687786,0.024930691738072972,0.9845016570368361,-0.12430505638599622,0.024930691738072972,0.9919308007952649,-0.07470686612825737,0.024930691738072972,0.9968938482921619,-0.024922942830348863,0.024930691738072972,0.9993784606094611,0.024922942830348495,0.024930691738072972,0.9993784606094611,0.07470686612825701,0.024930691738072972,0.9968938482921619,0.12430505638599497,0.024930691738072972,0.9919308007952651,0.17359420468778564,0.024930691738072972,0.9845016570368362,0.22245177044484543,0.024930691738072972,0.9746248870388109,0.2707562860505989,0.024930691738072972,0.9623250460076093,0.3183876588682866,0.024930691738072972,0.9476327132860245,0.3652274698001812,0.024930691738072972,0.9305844163281582,0.41115926769612615,0.024930691738072972,0.9112225398864134,0.45606885886943804,0.024930691738072972,0.8895952206363239,0.49984459100040823,0.024930691738072972,0.865756227501192,0.5423776307215603,0.024930691738072972,0.839764827974075,0.5835622341945493,0.024930691738072972,0.8116856407694589,0.6232960100060082,0.024930691738072972,0.7815884751709503,0.6614801737287287,0.024930691738072972,0.7495481574744027,0.6980197935153191,0.024930691738072972,0.7156443449579495,0.7328240261137265,0.024930691738072972,0.6799613278414658,0.7658063427178817,0.024930691738072972,0.6425878197277967,0.796884744091943,0.024930691738072972,0.6036167370467618,0.8259819644333135,0.024930691738072972,0.5631449680502754,0.8530256634676052,0.024930691738072972,0.5212731319328795,0.8779486062979533,0.024930691738072972,0.47810532867658184,0.9006888305615708,0.024930691738072972,0.43374888024188746,0.9211898004779386,0.024930691738072972,0.3883140637485033,0.9394005474056677,0.024930691738072972,0.3419138373090404,0.9552757965585718,0.024930691738072972,0.29466355919734527,0.9687760795659177,0.024930691738072972,0.24668070104966006,0.9798678325970158,0.024930691738072972,0.19808455581162251,0.98852347980619,0.024930691738072972,0.14899594115720768,0.994721501890676,0.024930691738072972,0.09953689911695533,0.9984464895909981,0.024930691738072972,0.049830392662249,0.9996891820008162,0.024930691738072972,0.0,0.9972037971811801,0.07473009358642432,0.0,0.9959641943004941,0.07473009358642432,-0.04970650645470615,0.9922484675065122,0.07473009358642432,-0.09928943470250161,0.9860658546815019,0.07473009358642432,-0.14862551377136932,0.977431726775092,0.07473009358642432,-0.19759208639524514,0.9663675495896699,0.07473009358642432,-0.24606741395930737,0.9529008304130139,0.07473009358642432,-0.2939309791613547,0.9370650496308426,0.07473009358642432,-0.3410637856368083,0.9188995774893002,0.07473009358642432,-0.3873486538024237,0.8984495762143214,0.07473009358642432,-0.4326705121831975,0.8757658877312223,0.07473009358642432,-0.47691668349818545,0.8509049072636626,0.07473009358642432,-0.5199771647939764,0.8239284431262343,0.07473009358642432,-0.5617449009293668,0.7949035630592528,0.07473009358642432,-0.6021160507313094,0.7639024274877875,0.07473009358642432,-0.6409902451604312,0.7310021101194746,0.07473009358642432,-0.6782708368442799,0.6962844063271367,0.07473009358642432,-0.7138651403579187,0.6598356297925979,0.07473009358642432,-0.7476846626544976,0.6217463979172707,0.07473009358642432,-0.7796453230729106,0.5821114065330187,0.07473009358642432,-0.8096676623755669,0.5410291944733978,0.07473009358642432,-0.8376770402965754,0.49860189859058995,0.07473009358642432,-0.8636038211092071,0.4549349998270965,0.07473009358642432,-0.8873835467512816,0.4101370609734936,0.07473009358642432,-0.9089570970780684,0.3643194567642311,0.07473009358642432,-0.9282708368442799,0.3175960969824942,0.07473009358642432,-0.9452767490497428,0.2700831432625332,0.07473009358642432,-0.959932554317226,0.22189872029353938,0.07473009358642432,-0.9722018160056346,0.17316262214305522,0.07473009358642432,-0.9820540307972396,0.12399601443004747,0.07473009358642432,-0.989464704533733,0.07452113308808736,0.07473009358642432,-0.9944154131125642,0.024860980467559905,0.07473009358642432,-0.9968938482921619,-0.024860980467559784,0.07473009358642432,-0.9968938482921619,-0.07452113308808724,0.07473009358642432,-0.9944154131125642,-0.12399601443004736,0.07473009358642432,-0.989464704533733,-0.1731626221430551,0.07473009358642432,-0.9820540307972396,-0.22189872029353927,0.07473009358642432,-0.9722018160056346,-0.270083143262533,0.07473009358642432,-0.9599325543172261,-0.3175960969824941,0.07473009358642432,-0.9452767490497428,-0.364319456764231,0.07473009358642432,-0.92827083684428,-0.41013706097349334,0.07473009358642432,-0.9089570970780685,-0.454934999827096,0.07473009358642432,-0.8873835467512818,-0.4986018985905903,0.07473009358642432,-0.863603821109207,-0.5410291944733978,0.07473009358642432,-0.8376770402965755,-0.5821114065330184,0.07473009358642432,-0.809667662375567,-0.6217463979172706,0.07473009358642432,-0.7796453230729107,-0.6598356297925977,0.07473009358642432,-0.7476846626544978,-0.6962844063271366,0.07473009358642432,-0.7138651403579188,-0.7310021101194746,0.07473009358642432,-0.6782708368442799,-0.7639024274877874,0.07473009358642432,-0.6409902451604315,-0.7949035630592528,0.07473009358642432,-0.6021160507313094,-0.8239284431262341,0.07473009358642432,-0.561744900929367,-0.8509049072636625,0.07473009358642432,-0.5199771647939765,-0.8757658877312224,0.07473009358642432,-0.47691668349818533,-0.8984495762143213,0.07473009358642432,-0.43267051218319763,-0.9188995774893003,0.07473009358642432,-0.3873486538024236,-0.9370650496308426,0.07473009358642432,-0.34106378563680845,-0.9529008304130138,0.07473009358642432,-0.2939309791613551,-0.9663675495896697,0.07473009358642432,-0.2460674139593076,-0.977431726775092,0.07473009358642432,-0.19759208639524517,-0.9860658546815019,0.07473009358642432,-0.1486255137713696,-0.9922484675065122,0.07473009358642432,-0.09928943470250168,-0.9959641943004941,0.07473009358642432,-0.049706506454706464,-0.9972037971811801,0.07473009358642432,-1.2212224383155186e-16,-0.9959641943004941,0.07473009358642432,0.049706506454706215,-0.9922484675065122,0.07473009358642432,0.09928943470250143,-0.9860658546815019,0.07473009358642432,0.14862551377136934,-0.9774317267750922,0.07473009358642432,0.19759208639524492,-0.9663675495896699,0.07473009358642432,0.24606741395930737,-0.952900830413014,0.07473009358642432,0.2939309791613544,-0.9370650496308426,0.07473009358642432,0.3410637856368082,-0.9188995774893002,0.07473009358642432,0.3873486538024238,-0.8984495762143214,0.07473009358642432,0.4326705121831974,-0.8757658877312224,0.07473009358642432,0.4769166834981851,-0.8509049072636626,0.07473009358642432,0.5199771647939763,-0.8239284431262343,0.07473009358642432,0.5617449009293668,-0.7949035630592529,0.07473009358642432,0.6021160507313091,-0.7639024274877875,0.07473009358642432,0.6409902451604312,-0.7310021101194748,0.07473009358642432,0.6782708368442797,-0.6962844063271368,0.07473009358642432,0.7138651403579186,-0.6598356297925982,0.07473009358642432,0.7476846626544974,-0.6217463979172707,0.07473009358642432,0.7796453230729105,-0.5821114065330193,0.07473009358642432,0.8096676623755664,-0.5410291944733976,0.07473009358642432,0.8376770402965755,-0.4986018985905897,0.07473009358642432,0.8636038211092072,-0.4549349998270966,0.07473009358642432,0.8873835467512815,-0.41013706097349356,0.07473009358642432,0.9089570970780684,-0.36431945676423183,0.07473009358642432,0.9282708368442797,-0.31759609698249475,0.07473009358642432,0.9452767490497426,-0.27008314326253263,0.07473009358642432,0.9599325543172262,-0.22189872029353952,0.07473009358642432,0.9722018160056346,-0.17316262214305514,0.07473009358642432,0.9820540307972396,-0.12399601443004804,0.07473009358642432,0.9894647045337329,-0.0745211330880877,0.07473009358642432,0.9944154131125642,-0.024860980467560027,0.07473009358642432,0.9968938482921619,0.02486098046755966,0.07473009358642432,0.9968938482921619,0.07452113308808733,0.07473009358642432,0.9944154131125642,0.12399601443004679,0.07473009358642432,0.9894647045337331,0.17316262214305478,0.07473009358642432,0.9820540307972397,0.22189872029353916,0.07473009358642432,0.9722018160056346,0.27008314326253313,0.07473009358642432,0.959932554317226,0.31759609698249436,0.07473009358642432,0.9452767490497427,0.36431945676423066,0.07473009358642432,0.9282708368442801,0.41013706097349323,0.07473009358642432,0.9089570970780685,0.4549349998270963,0.07473009358642432,0.8873835467512817,0.4986018985905902,0.07473009358642432,0.863603821109207,0.541029194473398,0.07473009358642432,0.8376770402965753,0.5821114065330183,0.07473009358642432,0.8096676623755671,0.6217463979172705,0.07473009358642432,0.7796453230729107,0.6598356297925972,0.07473009358642432,0.7476846626544982,0.6962844063271368,0.07473009358642432,0.7138651403579186,0.7310021101194748,0.07473009358642432,0.6782708368442797,0.7639024274877873,0.07473009358642432,0.6409902451604316,0.7949035630592527,0.07473009358642432,0.6021160507313095,0.8239284431262338,0.07473009358642432,0.5617449009293675,0.8509049072636627,0.07473009358642432,0.5199771647939763,0.8757658877312221,0.07473009358642432,0.47691668349818583,0.8984495762143213,0.07473009358642432,0.43267051218319774,0.9188995774893002,0.07473009358642432,0.3873486538024237,0.9370650496308424,0.07473009358642432,0.341063785636809,0.9529008304130138,0.07473009358642432,0.2939309791613552,0.9663675495896697,0.07473009358642432,0.2460674139593077,0.977431726775092,0.07473009358642432,0.19759208639524528,0.9860658546815019,0.07473009358642432,0.14862551377136926,0.9922484675065121,0.07473009358642432,0.09928943470250223,0.9959641943004941,0.07473009358642432,0.04970650645470658,0.9972037971811801,0.07473009358642432,0.0,0.9922392066001721,0.12434370464748522,0.0,0.9910057750966941,0.12434370464748522,-0.049459042040253046,0.9873085470913199,0.12434370464748522,-0.09879512110912077,0.9811567144754142,0.12434370464748522,-0.147885579940539,0.9725655716741559,0.12434370464748522,-0.19660837191905434,0.9615564776221882,0.12434370464748522,-0.24484236450694008,0.9481568026619406,0.12434370464748522,-0.29246764039877093,0.9323998604966461,0.12434370464748522,-0.3393657956547387,0.9143248253672254,0.12434370464748522,-0.38542023407150255,0.8939766346589516,0.12434370464748522,-0.43051645705872194,0.8714058771800304,0.12434370464748522,-0.4745423483005924,0.8466686673898515,0.12434370464748522,-0.5173884524946725,0.8198265058896014,0.12434370464748522,-0.5589482474750113,0.7909461265220785,0.12434370464748522,-0.5991184090430364,0.7600993304608454,0.12434370464748522,-0.6377990678477928,0.7273628077011979,0.12434370464748522,-0.6748940576768839,0.6928179463967536,0.12434370464748522,-0.710311154540828,0.6565506305156781,0.12434370464748522,-0.7439623059564272,0.6186510263196083,0.12434370464748522,-0.7757638498591142,0.5792133581961189,0.12434370464748522,-0.8056367226000274,0.5383356744020488,0.12434370464748522,-0.8335066555106986,0.4961196033000859,0.12434370464748522,-0.8593043595466651,0.45267010069464547,0.12434370464748522,-0.8829656975509516,0.4080951888952013,0.12434370464748522,-0.9044318437091482,0.3625056881558059,0.12434370464748522,-0.9236494297996533,0.31601494115847784,0.12434370464748522,-0.9405706778754815,0.2687385312254346,0.12434370464748522,-0.955153519047768,0.22079399496074273,0.12434370464748522,-0.9673616980756549,0.17230053003580076,0.12434370464748522,-0.9771648635025315,0.12337869884514692,0.12434370464748522,-0.984538643114539,0.07415012876935034,0.12434370464748522,-0.989464704533733,0.024737209790179054,0.12434370464748522,-0.991930800795265,-0.024737209790178932,0.12434370464748522,-0.991930800795265,-0.07415012876935022,0.12434370464748522,-0.989464704533733,-0.12337869884514681,0.12434370464748522,-0.984538643114539,-0.17230053003580065,0.12434370464748522,-0.9771648635025315,-0.22079399496074262,0.12434370464748522,-0.9673616980756549,-0.26873853122543445,0.12434370464748522,-0.9551535190477681,-0.3160149411584777,0.12434370464748522,-0.9405706778754815,-0.3625056881558058,0.12434370464748522,-0.9236494297996534,-0.408095188895201,0.12434370464748522,-0.9044318437091483,-0.452670100694645,0.12434370464748522,-0.8829656975509518,-0.49611960330008625,0.12434370464748522,-0.859304359546665,-0.5383356744020488,0.12434370464748522,-0.8335066555106987,-0.5792133581961185,0.12434370464748522,-0.8056367226000275,-0.6186510263196082,0.12434370464748522,-0.7757638498591143,-0.6565506305156779,0.12434370464748522,-0.7439623059564274,-0.6928179463967535,0.12434370464748522,-0.7103111545408282,-0.7273628077011979,0.12434370464748522,-0.6748940576768839,-0.7600993304608453,0.12434370464748522,-0.637799067847793,-0.7909461265220785,0.12434370464748522,-0.5991184090430364,-0.8198265058896013,0.12434370464748522,-0.5589482474750115,-0.8466686673898514,0.12434370464748522,-0.5173884524946726,-0.8714058771800305,0.12434370464748522,-0.4745423483005923,-0.8939766346589515,0.12434370464748522,-0.43051645705872205,-0.9143248253672255,0.12434370464748522,-0.38542023407150244,-0.932399860496646,0.12434370464748522,-0.33936579565473884,-0.9481568026619405,0.12434370464748522,-0.2924676403987713,-0.9615564776221881,0.12434370464748522,-0.2448423645069403,-0.9725655716741559,0.12434370464748522,-0.19660837191905436,-0.9811567144754142,0.12434370464748522,-0.14788557994053927,-0.9873085470913199,0.12434370464748522,-0.09879512110912084,-0.9910057750966941,0.12434370464748522,-0.049459042040253365,-0.9922392066001721,0.12434370464748522,-1.21514256835141e-16,-0.9910057750966941,0.12434370464748522,0.049459042040253115,-0.9873085470913199,0.12434370464748522,0.09879512110912059,-0.9811567144754142,0.12434370464748522,0.14788557994053902,-0.972565571674156,0.12434370464748522,0.19660837191905411,-0.9615564776221882,0.12434370464748522,0.24484236450694008,-0.9481568026619407,0.12434370464748522,0.29246764039877066,-0.9323998604966461,0.12434370464748522,0.3393657956547386,-0.9143248253672254,0.12434370464748522,0.3854202340715026,-0.8939766346589516,0.12434370464748522,0.4305164570587218,-0.8714058771800305,0.12434370464748522,0.47454234830059205,-0.8466686673898515,0.12434370464748522,0.5173884524946724,-0.8198265058896014,0.12434370464748522,0.5589482474750113,-0.7909461265220786,0.12434370464748522,0.5991184090430361,-0.7600993304608454,0.12434370464748522,0.6377990678477928,-0.7273628077011981,0.12434370464748522,0.6748940576768837,-0.6928179463967538,0.12434370464748522,0.7103111545408279,-0.6565506305156784,0.12434370464748522,0.743962305956427,-0.6186510263196083,0.12434370464748522,0.7757638498591141,-0.5792133581961194,0.12434370464748522,0.8056367226000269,-0.5383356744020485,0.12434370464748522,0.8335066555106987,-0.49611960330008564,0.12434370464748522,0.8593043595466652,-0.4526701006946456,0.12434370464748522,0.8829656975509514,-0.40809518889520124,0.12434370464748522,0.9044318437091482,-0.3625056881558066,0.12434370464748522,0.923649429799653,-0.3160149411584784,0.12434370464748522,0.9405706778754813,-0.26873853122543406,0.12434370464748522,0.9551535190477682,-0.22079399496074287,0.12434370464748522,0.9673616980756549,-0.17230053003580068,0.12434370464748522,0.9771648635025315,-0.12337869884514747,0.12434370464748522,0.9845386431145389,-0.07415012876935068,0.12434370464748522,0.989464704533733,-0.02473720979017917,0.12434370464748522,0.991930800795265,0.024737209790178807,0.12434370464748522,0.991930800795265,0.07415012876935032,0.12434370464748522,0.989464704533733,0.12337869884514624,0.12434370464748522,0.9845386431145391,0.17230053003580031,0.12434370464748522,0.9771648635025316,0.2207939949607425,0.12434370464748522,0.9673616980756549,0.26873853122543456,0.12434370464748522,0.955153519047768,0.316014941158478,0.12434370464748522,0.9405706778754814,0.36250568815580547,0.12434370464748522,0.9236494297996535,0.4080951888952009,0.12434370464748522,0.9044318437091483,0.45267010069464525,0.12434370464748522,0.8829656975509517,0.49611960330008614,0.12434370464748522,0.859304359546665,0.538335674402049,0.12434370464748522,0.8335066555106985,0.5792133581961184,0.12434370464748522,0.8056367226000276,0.6186510263196081,0.12434370464748522,0.7757638498591143,0.6565506305156774,0.12434370464748522,0.7439623059564278,0.6928179463967538,0.12434370464748522,0.7103111545408279,0.7273628077011981,0.12434370464748522,0.6748940576768837,0.7600993304608452,0.12434370464748522,0.6377990678477932,0.7909461265220784,0.12434370464748522,0.5991184090430365,0.8198265058896009,0.12434370464748522,0.5589482474750119,0.8466686673898516,0.12434370464748522,0.5173884524946724,0.8714058771800302,0.12434370464748522,0.4745423483005928,0.8939766346589515,0.12434370464748522,0.4305164570587221,0.9143248253672254,0.12434370464748522,0.38542023407150255,0.9323998604966458,0.12434370464748522,0.3393657956547394,0.9481568026619405,0.12434370464748522,0.29246764039877143,0.9615564776221881,0.12434370464748522,0.2448423645069404,0.9725655716741559,0.12434370464748522,0.19660837191905448,0.9811567144754142,0.12434370464748522,0.14788557994053894,0.9873085470913198,0.12434370464748522,0.09879512110912139,0.9910057750966941,0.12434370464748522,0.04945904204025348,0.9922392066001721,0.12434370464748522,0.0,0.984807753012208,0.17364817766693036,0.0,0.9835835593909977,0.17364817766693036,-0.049088614654414615,0.9799140220655961,0.17364817766693036,-0.09805518727829043,0.9738082640839739,0.17364817766693036,-0.14677797925680577,0.9652814653225102,0.17364817766693036,-0.19513585805223393,0.9543548247464266,0.17364817766693036,-0.24300859835851787,0.9410555077058202,0.17364817766693036,-0.29027718100032407,0.9254165783983234,0.17364817766693036,-0.33682408883346515,0.9074769176662975,0.17364817766693036,-0.38253359891103694,0.8872811263329343,0.17364817766693036,-0.4272920701888975,0.8648794143175808,0.17364817766693036,-0.47098822605520896,0.8403274758059692,0.17364817766693036,-0.5135134309816272,0.8136863507856958,0.17364817766693036,-0.5547619606083422,0.785022273291194,0.17364817766693036,-0.5946312645914946,0.7544065067354889,0.17364817766693036,-0.633022221559489,0.7219151667381244,0.17364817766693036,-0.6698393855443409,0.6876290318897392,0.17364817766693036,-0.7049912232753923,0.6516333429237638,0.17364817766693036,-0.7383903417454446,0.6140175907945262,0.17364817766693036,-0.7699537054835439,0.5748752941886384,0.17364817766693036,-0.799602842994246,0.534303767022807,0.17364817766693036,-0.8272640418501169,0.4924038765061039,0.17364817766693036,-0.8528685319524433,0.4492797923681909,0.17364817766693036,-0.8763526565045315,0.40503872787695794,0.17364817766693036,-0.8976580302725363,0.3597906732894478,0.17364817766693036,-0.9167316847403497,0.31364812239874634,0.17364817766693036,-0.9335261997976786,0.2667257928566874,0.17364817766693036,-0.9479998216339102,0.21914034096769597,0.17364817766693036,-0.960116566544665,0.1710100716628344,0.17364817766693036,-0.9698463103929541,0.12245464437510362,0.17364817766693036,-0.9771648635025315,0.07359477554723863,0.17364817766693036,-0.9820540307972396,0.024551938511611724,0.17364817766693036,-0.9845016570368361,-0.024551938511611606,0.17364817766693036,-0.9845016570368361,-0.0735947755472385,0.17364817766693036,-0.9820540307972396,-0.1224546443751035,0.17364817766693036,-0.9771648635025315,-0.17101007166283433,0.17364817766693036,-0.9698463103929541,-0.21914034096769586,0.17364817766693036,-0.960116566544665,-0.26672579285668724,0.17364817766693036,-0.9479998216339103,-0.31364812239874623,0.17364817766693036,-0.9335261997976786,-0.3597906732894477,0.17364817766693036,-0.9167316847403498,-0.40503872787695766,0.17364817766693036,-0.8976580302725364,-0.4492797923681904,0.17364817766693036,-0.8763526565045318,-0.49240387650610423,0.17364817766693036,-0.8528685319524432,-0.534303767022807,0.17364817766693036,-0.827264041850117,-0.574875294188638,0.17364817766693036,-0.7996028429942461,-0.6140175907945261,0.17364817766693036,-0.769953705483544,-0.6516333429237636,0.17364817766693036,-0.7383903417454448,-0.6876290318897391,0.17364817766693036,-0.7049912232753924,-0.7219151667381244,0.17364817766693036,-0.6698393855443409,-0.7544065067354888,0.17364817766693036,-0.6330222215594892,-0.785022273291194,0.17364817766693036,-0.5946312645914946,-0.8136863507856957,0.17364817766693036,-0.5547619606083424,-0.8403274758059691,0.17364817766693036,-0.5135134309816273,-0.8648794143175809,0.17364817766693036,-0.47098822605520885,-0.8872811263329342,0.17364817766693036,-0.4272920701888976,-0.9074769176662976,0.17364817766693036,-0.38253359891103683,-0.9254165783983233,0.17364817766693036,-0.3368240888334653,-0.9410555077058201,0.17364817766693036,-0.29027718100032446,-0.9543548247464265,0.17364817766693036,-0.2430085983585181,-0.9652814653225102,0.17364817766693036,-0.19513585805223396,-0.9738082640839739,0.17364817766693036,-0.14677797925680605,-0.9799140220655961,0.17364817766693036,-0.0980551872782905,-0.9835835593909977,0.17364817766693036,-0.04908861465441493,-0.984807753012208,0.17364817766693036,-1.2060416625018976e-16,-0.9835835593909977,0.17364817766693036,0.049088614654414685,-0.9799140220655961,0.17364817766693036,0.09805518727829025,-0.9738082640839739,0.17364817766693036,0.1467779792568058,-0.9652814653225104,0.17364817766693036,0.1951358580522337,-0.9543548247464266,0.17364817766693036,0.24300859835851787,-0.9410555077058204,0.17364817766693036,0.2902771810003238,-0.9254165783983234,0.17364817766693036,0.3368240888334651,-0.9074769176662975,0.17364817766693036,0.38253359891103694,-0.8872811263329343,0.17364817766693036,0.42729207018889737,-0.8648794143175809,0.17364817766693036,0.4709882260552086,-0.8403274758059692,0.17364817766693036,0.513513430981627,-0.8136863507856958,0.17364817766693036,0.5547619606083422,-0.7850222732911941,0.17364817766693036,0.5946312645914944,-0.7544065067354889,0.17364817766693036,0.633022221559489,-0.7219151667381246,0.17364817766693036,0.6698393855443406,-0.6876290318897393,0.17364817766693036,0.7049912232753922,-0.6516333429237642,0.17364817766693036,0.7383903417454444,-0.6140175907945262,0.17364817766693036,0.7699537054835438,-0.5748752941886389,0.17364817766693036,0.7996028429942456,-0.5343037670228068,0.17364817766693036,0.827264041850117,-0.4924038765061036,0.17364817766693036,0.8528685319524434,-0.449279792368191,0.17364817766693036,0.8763526565045314,-0.4050387278769579,0.17364817766693036,0.8976580302725363,-0.35979067328944847,0.17364817766693036,0.9167316847403495,-0.31364812239874684,0.17364817766693036,0.9335261997976784,-0.2667257928566869,0.17364817766693036,0.9479998216339104,-0.2191403409676961,0.17364817766693036,0.960116566544665,-0.17101007166283433,0.17364817766693036,0.9698463103929541,-0.12245464437510419,0.17364817766693036,0.9771648635025314,-0.07359477554723895,0.17364817766693036,0.9820540307972396,-0.024551938511611842,0.17364817766693036,0.9845016570368361,0.02455193851161148,0.17364817766693036,0.9845016570368361,0.0735947755472386,0.17364817766693036,0.9820540307972396,0.12245464437510295,0.17364817766693036,0.9771648635025316,0.171010071662834,0.17364817766693036,0.9698463103929542,0.21914034096769575,0.17364817766693036,0.960116566544665,0.26672579285668735,0.17364817766693036,0.9479998216339102,0.3136481223987465,0.17364817766693036,0.9335261997976785,0.35979067328944736,0.17364817766693036,0.9167316847403499,0.40503872787695755,0.17364817766693036,0.8976580302725364,0.44927979236819066,0.17364817766693036,0.8763526565045316,0.4924038765061041,0.17364817766693036,0.8528685319524432,0.5343037670228072,0.17364817766693036,0.8272640418501168,0.5748752941886379,0.17364817766693036,0.7996028429942462,0.614017590794526,0.17364817766693036,0.769953705483544,0.6516333429237633,0.17364817766693036,0.7383903417454452,0.6876290318897393,0.17364817766693036,0.7049912232753922,0.7219151667381246,0.17364817766693036,0.6698393855443406,0.7544065067354887,0.17364817766693036,0.6330222215594893,0.7850222732911939,0.17364817766693036,0.5946312645914947,0.8136863507856954,0.17364817766693036,0.5547619606083428,0.8403274758059693,0.17364817766693036,0.513513430981627,0.8648794143175805,0.17364817766693036,0.47098822605520935,0.8872811263329342,0.17364817766693036,0.4272920701888977,0.9074769176662975,0.17364817766693036,0.38253359891103694,0.925416578398323,0.17364817766693036,0.3368240888334659,0.9410555077058201,0.17364817766693036,0.2902771810003245,0.9543548247464265,0.17364817766693036,0.2430085983585182,0.9652814653225102,0.17364817766693036,0.19513585805223407,0.9738082640839739,0.17364817766693036,0.14677797925680572,0.979914022065596,0.17364817766693036,0.09805518727829104,0.9835835593909977,0.17364817766693036,0.049088614654415046,0.984807753012208,0.17364817766693036,0.0,0.9749279121818236,0.2225209339563144,0.0,0.97371599998111,0.2225209339563144,-0.04859614523803738,0.9700832763836519,0.2225209339563144,-0.09707147280209963,0.9640387729126922,0.2225209339563144,-0.14530546538998537,0.9555975171562447,0.2225209339563144,-0.19317820569626934,0.9447804954061425,0.2225209339563144,-0.2405706745456179,0.9316146004828093,0.2225209339563144,-0.2873650467932121,0.9161325648754718,0.2225209339563144,-0.3334449842566223,0.8983728793640322,0.2225209339563144,-0.37869592495086013,0.8783796973249267,0.2225209339563144,-0.42300536790752397,0.8562027249588732,0.2225209339563144,-0.46626315286993225,0.8318970977134251,0.2225209339563144,-0.5083617341688785,0.8055232432075619,0.2225209339563144,-0.5491964480981107,0.7771467309991062,0.2225209339563144,-0.5886657731247948,0.746838109568473,0.2225209339563144,-0.6266715822880427,0.7146727309240304,0.2225209339563144,-0.6631193871579975,0.6807305632651348,0.2225209339563144,-0.6979185727489575,0.6450959921685874,0.2225209339563144,-0.7309826228025093,0.607857610792794,0.2225209339563144,-0.7622293348805763,0.5691079996212145,0.2225209339563144,-0.7915810247336336,0.5289434962926937,0.2225209339563144,-0.8189647194359906,0.4874639560909117,0.2225209339563144,-0.8443123388079836,0.4447725036884166,0.2225209339563144,-0.8675608646740279,0.4009752767624376,0.2225209339563144,-0.8886524975357332,0.3561811621198982,0.2225209339563144,-0.9075348002705617,0.3105015249876574,0.2225209339563144,-0.9241608284987786,0.26404993214100775,0.2225209339563144,-0.9384892472945754,0.21694186955877912,0.2225209339563144,-0.9504844339512096,0.16929445530699885,0.2225209339563144,-0.960116566544665,0.12122614836492614,0.2225209339563144,-0.9673616980756549,0.07285645411736502,0.2225209339563144,-0.9722018160056346,0.024305627245448182,0.2225209339563144,-0.9746248870388109,-0.024305627245448064,0.2225209339563144,-0.9746248870388109,-0.0728564541173649,0.2225209339563144,-0.9722018160056346,-0.12122614836492603,0.2225209339563144,-0.9673616980756549,-0.16929445530699874,0.2225209339563144,-0.960116566544665,-0.216941869558779,0.2225209339563144,-0.9504844339512096,-0.2640499321410076,0.2225209339563144,-0.9384892472945755,-0.31050152498765726,0.2225209339563144,-0.9241608284987786,-0.3561811621198981,0.2225209339563144,-0.9075348002705618,-0.4009752767624373,0.2225209339563144,-0.8886524975357333,-0.4447725036884161,0.2225209339563144,-0.8675608646740282,-0.48746395609091203,0.2225209339563144,-0.8443123388079835,-0.5289434962926937,0.2225209339563144,-0.8189647194359907,-0.5691079996212142,0.2225209339563144,-0.7915810247336337,-0.6078576107927939,0.2225209339563144,-0.7622293348805764,-0.6450959921685872,0.2225209339563144,-0.7309826228025095,-0.6807305632651347,0.2225209339563144,-0.6979185727489576,-0.7146727309240304,0.2225209339563144,-0.6631193871579975,-0.7468381095684729,0.2225209339563144,-0.6266715822880429,-0.7771467309991062,0.2225209339563144,-0.5886657731247948,-0.8055232432075617,0.2225209339563144,-0.549196448098111,-0.831897097713425,0.2225209339563144,-0.5083617341688786,-0.8562027249588733,0.2225209339563144,-0.46626315286993214,-0.8783796973249266,0.2225209339563144,-0.4230053679075241,-0.8983728793640323,0.2225209339563144,-0.37869592495086,-0.9161325648754717,0.2225209339563144,-0.33344498425662245,-0.9316146004828092,0.2225209339563144,-0.2873650467932125,-0.9447804954061424,0.2225209339563144,-0.24057067454561812,-0.9555975171562447,0.2225209339563144,-0.19317820569626937,-0.9640387729126922,0.2225209339563144,-0.14530546538998565,-0.9700832763836519,0.2225209339563144,-0.0970714728020997,-0.97371599998111,0.2225209339563144,-0.048596145238037694,-0.9749279121818236,0.2225209339563144,-1.1939423470528823e-16,-0.97371599998111,0.2225209339563144,0.04859614523803745,-0.9700832763836519,0.2225209339563144,0.09707147280209946,-0.9640387729126922,0.2225209339563144,0.1453054653899854,-0.9555975171562449,0.2225209339563144,0.19317820569626912,-0.9447804954061425,0.2225209339563144,0.2405706745456179,-0.9316146004828094,0.2225209339563144,0.28736504679321184,-0.9161325648754718,0.2225209339563144,0.3334449842566222,-0.8983728793640322,0.2225209339563144,0.3786959249508602,-0.8783796973249267,0.2225209339563144,0.42300536790752385,-0.8562027249588733,0.2225209339563144,0.4662631528699319,-0.8318970977134251,0.2225209339563144,0.5083617341688784,-0.8055232432075619,0.2225209339563144,0.5491964480981107,-0.7771467309991064,0.2225209339563144,0.5886657731247946,-0.746838109568473,0.2225209339563144,0.6266715822880427,-0.7146727309240306,0.2225209339563144,0.6631193871579972,-0.6807305632651349,0.2225209339563144,0.6979185727489574,-0.6450959921685877,0.2225209339563144,0.730982622802509,-0.607857610792794,0.2225209339563144,0.7622293348805762,-0.569107999621215,0.2225209339563144,0.7915810247336331,-0.5289434962926934,0.2225209339563144,0.8189647194359907,-0.4874639560909114,0.2225209339563144,0.8443123388079837,-0.4447725036884167,0.2225209339563144,0.8675608646740278,-0.40097527676243755,0.2225209339563144,0.8886524975357332,-0.3561811621198989,0.2225209339563144,0.9075348002705615,-0.31050152498765793,0.2225209339563144,0.9241608284987783,-0.2640499321410072,0.2225209339563144,0.9384892472945756,-0.21694186955877925,0.2225209339563144,0.9504844339512096,-0.16929445530699877,0.2225209339563144,0.960116566544665,-0.12122614836492669,0.2225209339563144,0.9673616980756548,-0.07285645411736534,0.2225209339563144,0.9722018160056346,-0.0243056272454483,0.2225209339563144,0.9746248870388109,0.024305627245447943,0.2225209339563144,0.9746248870388109,0.072856454117365,0.2225209339563144,0.9722018160056346,0.12122614836492547,0.2225209339563144,0.967361698075655,0.1692944553069984,0.2225209339563144,0.960116566544665,0.2169418695587789,0.2225209339563144,0.9504844339512096,0.2640499321410077,0.2225209339563144,0.9384892472945754,0.31050152498765754,0.2225209339563144,0.9241608284987785,0.35618116211989775,0.2225209339563144,0.9075348002705619,0.4009752767624372,0.2225209339563144,0.8886524975357333,0.44477250368841637,0.2225209339563144,0.867560864674028,0.4874639560909119,0.2225209339563144,0.8443123388079835,0.5289434962926939,0.2225209339563144,0.8189647194359905,0.5691079996212141,0.2225209339563144,0.7915810247336338,0.6078576107927938,0.2225209339563144,0.7622293348805764,0.6450959921685867,0.2225209339563144,0.7309826228025098,0.6807305632651349,0.2225209339563144,0.6979185727489574,0.7146727309240306,0.2225209339563144,0.6631193871579972,0.7468381095684727,0.2225209339563144,0.626671582288043,0.7771467309991061,0.2225209339563144,0.588665773124795,0.8055232432075614,0.2225209339563144,0.5491964480981114,0.8318970977134252,0.2225209339563144,0.5083617341688784,0.856202724958873,0.2225209339563144,0.46626315286993264,0.8783796973249266,0.2225209339563144,0.4230053679075242,0.8983728793640322,0.2225209339563144,0.37869592495086013,0.9161325648754715,0.2225209339563144,0.333444984256623,0.9316146004828092,0.2225209339563144,0.2873650467932126,0.9447804954061424,0.2225209339563144,0.24057067454561823,0.9555975171562447,0.2225209339563144,0.19317820569626948,0.9640387729126922,0.2225209339563144,0.14530546538998532,0.9700832763836518,0.2225209339563144,0.09707147280210024,0.97371599998111,0.2225209339563144,0.04859614523803781,0.9749279121818236,0.2225209339563144,0.0,0.962624246950012,0.2708404681430051,0.0,0.9614276291744777,0.2708404681430051,-0.047982858147685006,0.9578407508282061,0.2708404681430051,-0.09584642334973233,0.9518725294559226,0.2708404681430051,-0.14347169924156317,0.9435378029963243,0.2708404681430051,-0.19074028188336933,0.9328572928926274,0.2708404681430051,-0.23753465413096353,0.9198575525757937,0.2708404681430051,-0.28373847780191613,0.9045709014485184,0.2708404681430051,-0.32923688291060704,0.8870353445341008,0.2708404681430051,-0.37391675325310936,0.8672944779899711,0.2708404681430051,-0.41766700763189485,0.845397380720771,0.2708404681430051,-0.4603788760211936,0.8213984923604658,0.2708404681430051,-0.5019461699864156,0.7953574779268355,0.2708404681430051,-0.5422655466853312,0.7673390794848408,0.2708404681430051,-0.5812367657946588,0.7374129551876475,0.2708404681430051,-0.6187629387233031,0.7056535060954833,0.2708404681430051,-0.6547507694926592,0.6721396912028784,0.2708404681430051,-0.6891107866851143,0.6369548311341647,0.2708404681430051,-0.7217575658840892,0.6001864009952756,0.2708404681430051,-0.7526099420525987,0.5619258128968493,0.2708404681430051,-0.7815912113223209,0.5222681886893192,0.2708404681430051,-0.8086293216915,0.4813121234750059,0.2708404681430051,-0.8336570521575755,0.4391594404851586,0.2708404681430051,-0.856612179839185,0.395914937931357,0.2708404681430051,-0.8774376346720533,0.35168612846064695,0.2708404681430051,-0.8960816412941619,0.3065829718621596,0.2708404681430051,-0.9124978477674587,0.26071760168974906,0.2708404681430051,-0.9266454408160777,0.21420404648031055,0.2708404681430051,-0.9384892472945754,0.1671579462608708,0.2708404681430051,-0.9479998216339102,0.11969626504926022,0.2708404681430051,-0.955153519047768,0.0719370000631357,0.2708404681430051,-0.959932554317226,0.02399888836030544,0.2708404681430051,-0.9623250460076093,-0.023998888360305323,0.2708404681430051,-0.9623250460076093,-0.07193700006313558,0.2708404681430051,-0.959932554317226,-0.11969626504926012,0.2708404681430051,-0.955153519047768,-0.1671579462608707,0.2708404681430051,-0.9479998216339102,-0.21420404648031044,0.2708404681430051,-0.9384892472945754,-0.2607176016897489,0.2708404681430051,-0.9266454408160778,-0.3065829718621595,0.2708404681430051,-0.9124978477674587,-0.35168612846064684,0.2708404681430051,-0.896081641294162,-0.39591493793135674,0.2708404681430051,-0.8774376346720534,-0.4391594404851581,0.2708404681430051,-0.8566121798391853,-0.48131212347500624,0.2708404681430051,-0.8336570521575754,-0.5222681886893192,0.2708404681430051,-0.8086293216915001,-0.561925812896849,0.2708404681430051,-0.781591211322321,-0.6001864009952755,0.2708404681430051,-0.7526099420525988,-0.6369548311341645,0.2708404681430051,-0.7217575658840895,-0.6721396912028783,0.2708404681430051,-0.6891107866851144,-0.7056535060954833,0.2708404681430051,-0.6547507694926592,-0.7374129551876474,0.2708404681430051,-0.6187629387233033,-0.7673390794848408,0.2708404681430051,-0.5812367657946588,-0.7953574779268354,0.2708404681430051,-0.5422655466853314,-0.8213984923604657,0.2708404681430051,-0.5019461699864157,-0.8453973807207711,0.2708404681430051,-0.4603788760211935,-0.867294477989971,0.2708404681430051,-0.41766700763189496,-0.8870353445341009,0.2708404681430051,-0.37391675325310925,-0.9045709014485183,0.2708404681430051,-0.3292368829106072,-0.9198575525757937,0.2708404681430051,-0.28373847780191647,-0.9328572928926273,0.2708404681430051,-0.23753465413096375,-0.9435378029963243,0.2708404681430051,-0.19074028188336936,-0.9518725294559226,0.2708404681430051,-0.14347169924156344,-0.9578407508282061,0.2708404681430051,-0.0958464233497324,-0.9614276291744777,0.2708404681430051,-0.04798285814768532,-0.962624246950012,0.2708404681430051,-1.1788747028089635e-16,-0.9614276291744777,0.2708404681430051,0.047982858147685076,-0.9578407508282061,0.2708404681430051,0.09584642334973216,-0.9518725294559226,0.2708404681430051,0.1434716992415632,-0.9435378029963244,0.2708404681430051,0.19074028188336914,-0.9328572928926274,0.2708404681430051,0.23753465413096353,-0.9198575525757938,0.2708404681430051,0.28373847780191586,-0.9045709014485184,0.2708404681430051,0.329236882910607,-0.8870353445341008,0.2708404681430051,0.3739167532531094,-0.8672944779899711,0.2708404681430051,0.41766700763189474,-0.8453973807207711,0.2708404681430051,0.46037887602119326,-0.8213984923604658,0.2708404681430051,0.5019461699864155,-0.7953574779268355,0.2708404681430051,0.5422655466853312,-0.7673390794848409,0.2708404681430051,0.5812367657946585,-0.7374129551876475,0.2708404681430051,0.6187629387233031,-0.7056535060954835,0.2708404681430051,0.6547507694926591,-0.6721396912028785,0.2708404681430051,0.6891107866851142,-0.6369548311341651,0.2708404681430051,0.721757565884089,-0.6001864009952756,0.2708404681430051,0.7526099420525986,-0.5619258128968498,0.2708404681430051,0.7815912113223205,-0.522268188689319,0.2708404681430051,0.8086293216915001,-0.4813121234750056,0.2708404681430051,0.8336570521575755,-0.4391594404851587,0.2708404681430051,0.8566121798391849,-0.39591493793135696,0.2708404681430051,0.8774376346720533,-0.3516861284606477,0.2708404681430051,0.8960816412941617,-0.3065829718621601,0.2708404681430051,0.9124978477674585,-0.26071760168974856,0.2708404681430051,0.9266454408160779,-0.2142040464803107,0.2708404681430051,0.9384892472945754,-0.16715794626087072,0.2708404681430051,0.9479998216339102,-0.11969626504926077,0.2708404681430051,0.9551535190477679,-0.07193700006313603,0.2708404681430051,0.959932554317226,-0.02399888836030556,0.2708404681430051,0.9623250460076093,0.023998888360305205,0.2708404681430051,0.9623250460076093,0.07193700006313568,0.2708404681430051,0.959932554317226,0.11969626504925956,0.2708404681430051,0.9551535190477681,0.16715794626087035,0.2708404681430051,0.9479998216339103,0.21420404648031036,0.2708404681430051,0.9384892472945754,0.260717601689749,0.2708404681430051,0.9266454408160777,0.30658297186215977,0.2708404681430051,0.9124978477674586,0.35168612846064656,0.2708404681430051,0.8960816412941621,0.3959149379313566,0.2708404681430051,0.8774376346720534,0.43915944048515837,0.2708404681430051,0.8566121798391851,0.4813121234750061,0.2708404681430051,0.8336570521575754,0.5222681886893195,0.2708404681430051,0.8086293216914999,0.561925812896849,0.2708404681430051,0.7815912113223211,0.6001864009952754,0.2708404681430051,0.7526099420525988,0.6369548311341641,0.2708404681430051,0.7217575658840898,0.6721396912028785,0.2708404681430051,0.6891107866851142,0.7056535060954835,0.2708404681430051,0.6547507694926591,0.7374129551876473,0.2708404681430051,0.6187629387233035,0.7673390794848407,0.2708404681430051,0.5812367657946589,0.7953574779268351,0.2708404681430051,0.5422655466853319,0.8213984923604658,0.2708404681430051,0.5019461699864155,0.8453973807207708,0.2708404681430051,0.46037887602119393,0.867294477989971,0.2708404681430051,0.41766700763189507,0.8870353445341008,0.2708404681430051,0.37391675325310936,0.904570901448518,0.2708404681430051,0.3292368829106077,0.9198575525757937,0.2708404681430051,0.2837384778019166,0.9328572928926273,0.2708404681430051,0.23753465413096386,0.9435378029963243,0.2708404681430051,0.19074028188336947,0.9518725294559226,0.2708404681430051,0.1434716992415631,0.957840750828206,0.2708404681430051,0.09584642334973294,0.9614276291744777,0.2708404681430051,0.04798285814768543,0.962624246950012,0.2708404681430051,0.0,0.9479273461671318,0.3184866502516844,0.0,0.9467489977971082,0.3184866502516844,-0.04725027811169495,0.9432168822467485,0.3184866502516844,-0.09438308458714857,0.9373397809118383,0.3184866502516844,-0.1412812398431163,0.9291323051923054,0.3184866502516844,-0.1878281476762574,0.9186148601659758,0.3184866502516844,-0.23390808513966757,0.9058135938583364,0.3184866502516844,-0.2794064902483585,0.8907603322344232,0.3184866502516844,-0.3242102467984032,0.8734925000744572,0.3184866502516844,-0.3682079655916419,0.8540530279299453,0.3184866502516844,-0.41129026136677904,0.8324902453915638,0.3184866502516844,-0.4533500247483783,0.8088577609341816,0.3184866502516844,-0.4942826885376462,0.7832143286377448,0.3184866502516844,-0.5339864876829639,0.755623702115377,0.3184866502516844,-0.5723627122838397,0.7261544760118512,0.3184866502516844,-0.6093159529992753,0.6948799154664957,0.3184866502516844,-0.6447543382504201,0.6618777739645134,0.3184866502516844,-0.6785897626277911,0.6272301000295667,0.3184866502516844,-0.7107381059352039,0.5910230332382201,0.3184866502516844,-0.7411194423258339,0.5533465900633805,0.3184866502516844,-0.7696582390104654,0.5142944400791618,0.3184866502516844,-0.7962835440439058,0.4739636730835658,0.3184866502516844,-0.8209291627227018,0.43245455771794655,0.3184866502516844,-0.8435338221556007,0.38987029218336816,0.3184866502516844,-0.8640413235976139,0.34631674767361825,0.3184866502516844,-0.8824006821689503,0.3019022051627387,0.3184866502516844,-0.8985662536114614,0.2567370862014625,0.3184866502516844,-0.9124978477674587,0.21093367839184077,0.3184866502516844,-0.9241608284987786,0.16460585622257196,0.3184866502516844,-0.9335261997976786,0.11786879795908037,0.3184866502516844,-0.9405706778754815,0.07083869929220067,0.3184866502516844,-0.9452767490497428,0.023632484457382412,0.3184866502516844,-0.9476327132860246,-0.023632484457382294,0.3184866502516844,-0.9476327132860246,-0.07083869929220056,0.3184866502516844,-0.9452767490497428,-0.11786879795908026,0.3184866502516844,-0.9405706778754815,-0.16460585622257184,0.3184866502516844,-0.9335261997976786,-0.21093367839184066,0.3184866502516844,-0.9241608284987786,-0.25673708620146235,0.3184866502516844,-0.9124978477674588,-0.3019022051627386,0.3184866502516844,-0.8985662536114614,-0.34631674767361814,0.3184866502516844,-0.8824006821689504,-0.38987029218336794,0.3184866502516844,-0.864041323597614,-0.4324545577179461,0.3184866502516844,-0.8435338221556009,-0.47396367308356613,0.3184866502516844,-0.8209291627227017,-0.5142944400791618,0.3184866502516844,-0.7962835440439059,-0.5533465900633802,0.3184866502516844,-0.7696582390104655,-0.59102303323822,0.3184866502516844,-0.741119442325834,-0.6272301000295665,0.3184866502516844,-0.7107381059352041,-0.6618777739645133,0.3184866502516844,-0.6785897626277912,-0.6948799154664957,0.3184866502516844,-0.6447543382504201,-0.7261544760118511,0.3184866502516844,-0.6093159529992755,-0.755623702115377,0.3184866502516844,-0.5723627122838397,-0.7832143286377447,0.3184866502516844,-0.5339864876829641,-0.8088577609341815,0.3184866502516844,-0.4942826885376463,-0.8324902453915639,0.3184866502516844,-0.4533500247483782,-0.8540530279299452,0.3184866502516844,-0.41129026136677915,-0.8734925000744573,0.3184866502516844,-0.36820796559164176,-0.8907603322344231,0.3184866502516844,-0.3242102467984034,-0.9058135938583363,0.3184866502516844,-0.2794064902483589,-0.9186148601659757,0.3184866502516844,-0.2339080851396678,-0.9291323051923054,0.3184866502516844,-0.18782814767625744,-0.9373397809118383,0.3184866502516844,-0.14128123984311658,-0.9432168822467485,0.3184866502516844,-0.09438308458714864,-0.9467489977971082,0.3184866502516844,-0.04725027811169526,-0.9479273461671318,0.3184866502516844,-1.160876190307823e-16,-0.9467489977971082,0.3184866502516844,0.047250278111695014,-0.9432168822467485,0.3184866502516844,0.0943830845871484,-0.9373397809118383,0.3184866502516844,0.14128123984311633,-0.9291323051923055,0.3184866502516844,0.18782814767625722,-0.9186148601659758,0.3184866502516844,0.23390808513966757,-0.9058135938583365,0.3184866502516844,0.27940649024835823,-0.8907603322344232,0.3184866502516844,0.32421024679840316,-0.8734925000744572,0.3184866502516844,0.36820796559164193,-0.8540530279299453,0.3184866502516844,0.41129026136677893,-0.8324902453915639,0.3184866502516844,0.45335002474837804,-0.8088577609341816,0.3184866502516844,0.49428268853764606,-0.7832143286377448,0.3184866502516844,0.5339864876829639,-0.7556237021153771,0.3184866502516844,0.5723627122838395,-0.7261544760118512,0.3184866502516844,0.6093159529992753,-0.694879915466496,0.3184866502516844,0.6447543382504198,-0.6618777739645135,0.3184866502516844,0.678589762627791,-0.627230100029567,0.3184866502516844,0.7107381059352037,-0.5910230332382201,0.3184866502516844,0.7411194423258338,-0.553346590063381,0.3184866502516844,0.7696582390104649,-0.5142944400791616,0.3184866502516844,0.7962835440439059,-0.4739636730835655,0.3184866502516844,0.8209291627227019,-0.43245455771794666,0.3184866502516844,0.8435338221556006,-0.3898702921833681,0.3184866502516844,0.8640413235976139,-0.3463167476736189,0.3184866502516844,0.8824006821689501,-0.30190220516273925,0.3184866502516844,0.8985662536114611,-0.25673708620146196,0.3184866502516844,0.9124978477674588,-0.2109336783918409,0.3184866502516844,0.9241608284987786,-0.16460585622257187,0.3184866502516844,0.9335261997976786,-0.11786879795908091,0.3184866502516844,0.9405706778754814,-0.07083869929220099,0.3184866502516844,0.9452767490497428,-0.023632484457382526,0.3184866502516844,0.9476327132860246,0.023632484457382176,0.3184866502516844,0.9476327132860246,0.07083869929220064,0.3184866502516844,0.9452767490497428,0.11786879795907973,0.3184866502516844,0.9405706778754817,0.16460585622257154,0.3184866502516844,0.9335261997976787,0.21093367839184055,0.3184866502516844,0.9241608284987786,0.25673708620146246,0.3184866502516844,0.9124978477674587,0.30190220516273886,0.3184866502516844,0.8985662536114613,0.3463167476736178,0.3184866502516844,0.8824006821689505,0.38987029218336783,0.3184866502516844,0.864041323597614,0.4324545577179464,0.3184866502516844,0.8435338221556008,0.473963673083566,0.3184866502516844,0.8209291627227017,0.514294440079162,0.3184866502516844,0.7962835440439057,0.55334659006338,0.3184866502516844,0.7696582390104656,0.5910230332382199,0.3184866502516844,0.741119442325834,0.627230100029566,0.3184866502516844,0.7107381059352045,0.6618777739645135,0.3184866502516844,0.678589762627791,0.694879915466496,0.3184866502516844,0.6447543382504198,0.726154476011851,0.3184866502516844,0.6093159529992757,0.7556237021153769,0.3184866502516844,0.5723627122838398,0.7832143286377444,0.3184866502516844,0.5339864876829645,0.8088577609341817,0.3184866502516844,0.49428268853764606,0.8324902453915636,0.3184866502516844,0.4533500247483787,0.8540530279299452,0.3184866502516844,0.41129026136677926,0.8734925000744572,0.3184866502516844,0.3682079655916419,0.8907603322344229,0.3184866502516844,0.3242102467984039,0.9058135938583363,0.3184866502516844,0.279406490248359,0.9186148601659757,0.3184866502516844,0.23390808513966788,0.9291323051923054,0.3184866502516844,0.18782814767625755,0.9373397809118383,0.3184866502516844,0.14128123984311625,0.9432168822467484,0.3184866502516844,0.09438308458714917,0.9467489977971082,0.3184866502516844,0.04725027811169537,0.9479273461671318,0.3184866502516844,0.0,0.9308737486442042,0.36534102436639493,0.0,0.9297165992394024,0.36534102436639493,-0.04640022643946356,0.9262480278807403,0.36534102436639493,-0.09268509460507893,0.920476657983131,0.36534102436639493,-0.1387395330218428,0.9124168380814898,0.36534102436639493,-0.1844490430994145,0.9020886061580143,0.36534102436639493,-0.22969998379365233,0.889517639824605,0.36534102436639493,-0.2743798541361546,0.8747351924842752,0.36534102436639493,-0.3183775729293932,0.8577780156302673,0.36534102436639493,-0.3615837549120729,0.8386882674760497,0.36534102436639493,-0.4038909827081253,0.8175134081433557,0.36534102436639493,-0.44519407388323157,0.7943060816688428,0.36534102436639493,-0.48539034244492635,0.7691239851227228,0.36534102436639493,-0.5243798541361546,0.7420297251647551,0.36534102436639493,-0.5620656748875804,0.7130906623942249,0.36534102436639493,-0.5983541118109564,0.6823787438808812,0.36534102436639493,-0.633154946134407,0.6499703242931839,0.36534102436639493,-0.6663816575005096,0.6159459760685686,0.36534102436639493,-0.6979516390695357,0.5803902890976714,0.36534102436639493,-0.7277864028930704,0.5433916604205327,0.36534102436639493,-0.7558117750474187,0.505042074457627,0.36534102436639493,-0.7819580800416668,0.465436874322102,0.36534102436639493,-0.8061603140419311,0.42467452478177553,0.36534102436639493,-0.8283583064811304,0.3828563674602079,0.36534102436639493,-0.8484968696524976,0.3400863688854597,0.36534102436639493,-0.8665259359149131,0.2964708620129211,0.36534102436639493,-0.8824006821689503,0.2521182818648304,0.36534102436639493,-0.8960816412941619,0.20713889594372384,0.36534102436639493,-0.9075348002705617,0.1616445300900503,0.36534102436639493,-0.9167316847403497,0.11574829046551238,0.36534102436639493,-0.9236494297996533,0.06956428235332708,0.36534102436639493,-0.9282708368442799,0.023207326474513137,0.36534102436639493,-0.930584416328158,-0.023207326474513023,0.36534102436639493,-0.930584416328158,-0.06956428235332697,0.36534102436639493,-0.9282708368442799,-0.11574829046551227,0.36534102436639493,-0.9236494297996533,-0.1616445300900502,0.36534102436639493,-0.9167316847403497,-0.20713889594372373,0.36534102436639493,-0.9075348002705617,-0.25211828186483026,0.36534102436639493,-0.896081641294162,-0.296470862012921,0.36534102436639493,-0.8824006821689503,-0.34008636888545957,0.36534102436639493,-0.8665259359149132,-0.3828563674602077,0.36534102436639493,-0.8484968696524977,-0.42467452478177503,0.36534102436639493,-0.8283583064811305,-0.46543687432210235,0.36534102436639493,-0.806160314041931,-0.505042074457627,0.36534102436639493,-0.7819580800416669,-0.5433916604205323,0.36534102436639493,-0.7558117750474188,-0.5803902890976714,0.36534102436639493,-0.7277864028930705,-0.6159459760685685,0.36534102436639493,-0.697951639069536,-0.6499703242931838,0.36534102436639493,-0.6663816575005097,-0.6823787438808812,0.36534102436639493,-0.633154946134407,-0.7130906623942248,0.36534102436639493,-0.5983541118109567,-0.7420297251647551,0.36534102436639493,-0.5620656748875804,-0.7691239851227227,0.36534102436639493,-0.5243798541361548,-0.7943060816688426,0.36534102436639493,-0.48539034244492646,-0.8175134081433558,0.36534102436639493,-0.44519407388323146,-0.8386882674760496,0.36534102436639493,-0.4038909827081254,-0.8577780156302675,0.36534102436639493,-0.3615837549120728,-0.8747351924842751,0.36534102436639493,-0.3183775729293934,-0.8895176398246049,0.36534102436639493,-0.274379854136155,-0.9020886061580142,0.36534102436639493,-0.22969998379365253,-0.9124168380814898,0.36534102436639493,-0.18444904309941454,-0.920476657983131,0.36534102436639493,-0.13873953302184305,-0.9262480278807403,0.36534102436639493,-0.092685094605079,-0.9297165992394024,0.36534102436639493,-0.04640022643946385,-0.9308737486442042,0.36534102436639493,-1.1399915566874225e-16,-0.9297165992394024,0.36534102436639493,0.04640022643946362,-0.9262480278807403,0.36534102436639493,0.09268509460507876,-0.920476657983131,0.36534102436639493,0.13873953302184283,-0.9124168380814899,0.36534102436639493,0.18444904309941432,-0.9020886061580143,0.36534102436639493,0.22969998379365233,-0.889517639824605,0.36534102436639493,0.27437985413615434,-0.8747351924842752,0.36534102436639493,0.31837757292939317,-0.8577780156302673,0.36534102436639493,0.36158375491207295,-0.8386882674760497,0.36534102436639493,0.4038909827081252,-0.8175134081433558,0.36534102436639493,0.4451940738832313,-0.7943060816688428,0.36534102436639493,0.48539034244492624,-0.7691239851227228,0.36534102436639493,0.5243798541361546,-0.7420297251647552,0.36534102436639493,0.5620656748875802,-0.7130906623942249,0.36534102436639493,0.5983541118109564,-0.6823787438808814,0.36534102436639493,0.6331549461344068,-0.649970324293184,0.36534102436639493,0.6663816575005095,-0.6159459760685689,0.36534102436639493,0.6979516390695355,-0.5803902890976714,0.36534102436639493,0.7277864028930703,-0.5433916604205331,0.36534102436639493,0.7558117750474183,-0.5050420744576268,0.36534102436639493,0.7819580800416669,-0.46543687432210173,0.36534102436639493,0.8061603140419312,-0.42467452478177564,0.36534102436639493,0.8283583064811303,-0.38285636746020785,0.36534102436639493,0.8484968696524976,-0.34008636888546034,0.36534102436639493,0.8665259359149129,-0.2964708620129216,0.36534102436639493,0.8824006821689501,-0.2521182818648299,0.36534102436639493,0.8960816412941621,-0.20713889594372395,0.36534102436639493,0.9075348002705617,-0.16164453009005023,0.36534102436639493,0.9167316847403497,-0.11574829046551291,0.36534102436639493,0.9236494297996531,-0.06956428235332739,0.36534102436639493,0.9282708368442799,-0.023207326474513252,0.36534102436639493,0.930584416328158,0.02320732647451291,0.36534102436639493,0.930584416328158,0.06956428235332705,0.36534102436639493,0.9282708368442799,0.11574829046551174,0.36534102436639493,0.9236494297996534,0.1616445300900499,0.36534102436639493,0.9167316847403498,0.20713889594372362,0.36534102436639493,0.9075348002705617,0.2521182818648303,0.36534102436639493,0.8960816412941619,0.2964708620129212,0.36534102436639493,0.8824006821689502,0.34008636888545923,0.36534102436639493,0.8665259359149133,0.38285636746020757,0.36534102436639493,0.8484968696524977,0.4246745247817753,0.36534102436639493,0.8283583064811304,0.46543687432210223,0.36534102436639493,0.806160314041931,0.5050420744576273,0.36534102436639493,0.7819580800416667,0.5433916604205322,0.36534102436639493,0.7558117750474189,0.5803902890976713,0.36534102436639493,0.7277864028930705,0.615945976068568,0.36534102436639493,0.6979516390695362,0.649970324293184,0.36534102436639493,0.6663816575005095,0.6823787438808814,0.36534102436639493,0.6331549461344068,0.7130906623942247,0.36534102436639493,0.5983541118109568,0.742029725164755,0.36534102436639493,0.5620656748875805,0.7691239851227224,0.36534102436639493,0.5243798541361553,0.7943060816688429,0.36534102436639493,0.48539034244492624,0.8175134081433555,0.36534102436639493,0.44519407388323196,0.8386882674760496,0.36534102436639493,0.4038909827081255,0.8577780156302673,0.36534102436639493,0.3615837549120729,0.874735192484275,0.36534102436639493,0.3183775729293939,0.8895176398246049,0.36534102436639493,0.27437985413615507,0.9020886061580142,0.36534102436639493,0.22969998379365264,0.9124168380814898,0.36534102436639493,0.18444904309941465,0.920476657983131,0.36534102436639493,0.13873953302184275,0.9262480278807402,0.36534102436639493,0.09268509460507951,0.9297165992394024,0.36534102436639493,0.04640022643946396,0.9308737486442042,0.36534102436639493,0.0,0.9115058523116732,0.41128710313061145,0.0,0.9103727787278365,0.41128710313061145,-0.04543481649338398,0.9069763749757853,0.41128710313061145,-0.0907566748741578,0.9013250850503919,0.41128710313061145,-0.13585289786137716,0.8934329589488401,0.41128710313061145,-0.18061136913923775,0.883319617740119,0.41128710313061145,-0.2249208120959016,0.8710102047839532,0.41128710313061145,-0.26867106647468714,0.8565353232204495,0.41128710313061145,-0.3117533622498243,0.8399309598858679,0.41128710313061145,-0.3540605900458768,0.8212383958436779,0.41128710313061145,-0.3954875674285255,0.8005041037533288,0.41128710313061145,-0.43593130040467337,0.7777796323318972,0.41128710313061145,-0.47529123948174,0.7531214781958532,0.41128710313061145,-0.5134695296495431,0.726590945401571,0.41128710313061145,-0.5503712536632716,0.6982539930337851,0.41128710313061145,-0.5859046680227121,0.6681810712209133,0.41128710313061145,-0.6199814310610461,0.6364469459849366,0.41128710313061145,-0.6525168225761517,0.6031305133612888,0.41128710313061145,-0.6834299544583763,0.5683146032508812,0.41128710313061145,-0.7126439717911205,0.5320857734919182,0.41128710313061145,-0.740086243924272,0.49453409466347303,0.41128710313061145,-0.765688545045444,0.4557529261558365,0.41128710313061145,-0.7893872238000957,0.41583868406436364,0.41128710313061145,-0.811123361538828,0.37489060148386727,0.41128710313061145,-0.8308429187984298,0.33301048179951065,0.41128710313061145,-0.8484968696524977,0.2903024455875514,0.41128710313061145,-0.864041323597614,0.24687267175518252,0.41128710313061145,-0.8774376346720534,0.20282913356303994,0.41128710313061145,-0.8886524975357333,0.15828133018666427,0.41128710313061145,-0.8976580302725364,0.11334001448429704,0.41128710313061145,-0.9044318437091483,0.06811691764782488,0.41128710313061145,-0.9089570970780685,0.02272447142143185,0.41128710313061145,-0.9112225398864134,-0.02272447142143174,0.41128710313061145,-0.9112225398864134,-0.06811691764782477,0.41128710313061145,-0.9089570970780685,-0.11334001448429692,0.41128710313061145,-0.9044318437091483,-0.15828133018666418,0.41128710313061145,-0.8976580302725364,-0.20282913356303986,0.41128710313061145,-0.8886524975357333,-0.24687267175518235,0.41128710313061145,-0.8774376346720535,-0.2903024455875513,0.41128710313061145,-0.864041323597614,-0.33301048179951054,0.41128710313061145,-0.8484968696524978,-0.374890601483867,0.41128710313061145,-0.8308429187984299,-0.4158386840643632,0.41128710313061145,-0.8111233615388281,-0.45575292615583685,0.41128710313061145,-0.7893872238000956,-0.49453409466347303,0.41128710313061145,-0.7656885450454441,-0.532085773491918,0.41128710313061145,-0.7400862439242721,-0.5683146032508811,0.41128710313061145,-0.7126439717911206,-0.6031305133612885,0.41128710313061145,-0.6834299544583764,-0.6364469459849365,0.41128710313061145,-0.6525168225761518,-0.6681810712209133,0.41128710313061145,-0.6199814310610461,-0.6982539930337851,0.41128710313061145,-0.5859046680227123,-0.726590945401571,0.41128710313061145,-0.5503712536632716,-0.7531214781958531,0.41128710313061145,-0.5134695296495433,-0.7777796323318971,0.41128710313061145,-0.4752912394817401,-0.8005041037533289,0.41128710313061145,-0.4359313004046733,-0.8212383958436779,0.41128710313061145,-0.3954875674285256,-0.839930959885868,0.41128710313061145,-0.3540605900458767,-0.8565353232204493,0.41128710313061145,-0.3117533622498245,-0.8710102047839531,0.41128710313061145,-0.26867106647468747,-0.8833196177401189,0.41128710313061145,-0.22492081209590178,-0.8934329589488401,0.41128710313061145,-0.18061136913923778,-0.9013250850503919,0.41128710313061145,-0.13585289786137741,-0.9069763749757853,0.41128710313061145,-0.09075667487415787,-0.9103727787278365,0.41128710313061145,-0.045434816493384274,-0.9115058523116732,0.41128710313061145,-1.1162727244375708e-16,-0.9103727787278365,0.41128710313061145,0.045434816493384045,-0.9069763749757853,0.41128710313061145,0.09075667487415763,-0.9013250850503919,0.41128710313061145,0.1358528978613772,-0.8934329589488402,0.41128710313061145,0.18061136913923753,-0.883319617740119,0.41128710313061145,0.2249208120959016,-0.8710102047839533,0.41128710313061145,0.2686710664746869,-0.8565353232204495,0.41128710313061145,0.31175336224982425,-0.8399309598858679,0.41128710313061145,0.3540605900458768,-0.8212383958436779,0.41128710313061145,0.3954875674285254,-0.8005041037533289,0.41128710313061145,0.4359313004046731,-0.7777796323318972,0.41128710313061145,0.4752912394817399,-0.7531214781958532,0.41128710313061145,0.5134695296495431,-0.7265909454015711,0.41128710313061145,0.5503712536632713,-0.6982539930337851,0.41128710313061145,0.5859046680227121,-0.6681810712209135,0.41128710313061145,0.6199814310610459,-0.6364469459849367,0.41128710313061145,0.6525168225761516,-0.603130513361289,0.41128710313061145,0.6834299544583761,-0.5683146032508812,0.41128710313061145,0.7126439717911204,-0.5320857734919188,0.41128710313061145,0.7400862439242716,-0.4945340946634728,0.41128710313061145,0.7656885450454441,-0.4557529261558363,0.41128710313061145,0.7893872238000958,-0.41583868406436375,0.41128710313061145,0.8111233615388279,-0.3748906014838672,0.41128710313061145,0.8308429187984298,-0.3330104817995113,0.41128710313061145,0.8484968696524975,-0.2903024455875519,0.41128710313061145,0.8640413235976138,-0.24687267175518202,0.41128710313061145,0.8774376346720535,-0.20282913356304008,0.41128710313061145,0.8886524975357333,-0.15828133018666418,0.41128710313061145,0.8976580302725364,-0.11334001448429755,0.41128710313061145,0.9044318437091482,-0.06811691764782518,0.41128710313061145,0.9089570970780685,-0.022724471421431962,0.41128710313061145,0.9112225398864134,0.02272447142143163,0.41128710313061145,0.9112225398864134,0.06811691764782485,0.41128710313061145,0.9089570970780685,0.11334001448429641,0.41128710313061145,0.9044318437091484,0.15828133018666388,0.41128710313061145,0.8976580302725365,0.20282913356303975,0.41128710313061145,0.8886524975357333,0.24687267175518246,0.41128710313061145,0.8774376346720534,0.2903024455875515,0.41128710313061145,0.8640413235976139,0.33301048179951026,0.41128710313061145,0.8484968696524979,0.3748906014838669,0.41128710313061145,0.8308429187984299,0.4158386840643634,0.41128710313061145,0.8111233615388281,0.45575292615583674,0.41128710313061145,0.7893872238000956,0.4945340946634732,0.41128710313061145,0.7656885450454439,0.5320857734919179,0.41128710313061145,0.7400862439242722,0.568314603250881,0.41128710313061145,0.7126439717911206,0.6031305133612881,0.41128710313061145,0.6834299544583767,0.6364469459849367,0.41128710313061145,0.6525168225761516,0.6681810712209135,0.41128710313061145,0.6199814310610459,0.698253993033785,0.41128710313061145,0.5859046680227125,0.7265909454015709,0.41128710313061145,0.5503712536632717,0.7531214781958527,0.41128710313061145,0.5134695296495437,0.7777796323318973,0.41128710313061145,0.4752912394817399,0.8005041037533287,0.41128710313061145,0.43593130040467376,0.8212383958436779,0.41128710313061145,0.3954875674285257,0.8399309598858679,0.41128710313061145,0.3540605900458768,0.8565353232204491,0.41128710313061145,0.311753362249825,0.8710102047839531,0.41128710313061145,0.2686710664746876,0.8833196177401189,0.41128710313061145,0.2249208120959019,0.8934329589488401,0.41128710313061145,0.18061136913923787,0.9013250850503919,0.41128710313061145,0.1358528978613771,0.9069763749757852,0.41128710313061145,0.09075667487415837,0.9103727787278365,0.41128710313061145,0.04543481649338438,0.9115058523116732,0.41128710313061145,0.0,0.8898718088114688,0.45621065735316263,0.0,0.888765628048056,0.45621065735316263,-0.04435644843469424,0.885449835897488,0.45621065735316263,-0.08860261974968221,0.8799326759414943,0.45621065735316263,-0.13262851099155273,0.8722278647090211,0.45621065735316263,-0.17632466685786424,0.8623545575747791,0.45621065735316263,-0.21958245182027253,0.8503373011359634,0.45621065735316263,-0.2622943202095713,0.8362059721855459,0.45621065735316263,-0.3043540835911706,0.8199957034338599,0.45621065735316263,-0.345657174766277,0.801746796163147,0.45621065735316263,-0.3861009077424249,0.7815046200322193,0.45621065735316263,-0.4255847330270339,0.7593195002803393,0.45621065735316263,-0.46401048760929,0.7352465926107453,0.45621065735316263,-0.501282639008857,0.7093457460648832,0.45621065735316263,-0.5373085227846749,0.6816813542282593,0.45621065735316263,-0.5719985729133611,0.6523221951378405,0.45621065735316263,-0.6052665444644568,0.6213412602890181,0.45621065735316263,-0.6370297280189127,0.5888155731672491,0.45621065735316263,-0.6672091552977366,0.5548259977555355,0.45621065735316263,-0.6957297954895778,0.5194570374938217,0.45621065735316263,-0.7225207417891459,0.4827966251901278,0.45621065735316263,-0.7475153876827008,0.4449359044057343,0.45621065735316263,-0.7706515925423411,0.4059690028579284,0.45621065735316263,-0.791871836117395,0.3659927984036665,0.45621065735316263,-0.8111233615388281,0.3251066781859588,0.45621065735316263,-0.8283583064811305,0.28341229154177205,0.45621065735316263,-0.8435338221556009,0.24101329728576099,0.45621065735316263,-0.8566121798391853,0.19801510599812291,0.45621065735316263,-0.8675608646740282,0.15452461795728667,0.45621065735316263,-0.8763526565045318,0.11064995736897676,0.45621065735316263,-0.8829656975509518,0.06650020355240181,0.45621065735316263,-0.8873835467512818,0.02218511975188019,0.45621065735316263,-0.889595220636324,-0.022185119751880082,0.45621065735316263,-0.889595220636324,-0.0665002035524017,0.45621065735316263,-0.8873835467512818,-0.11064995736897666,0.45621065735316263,-0.8829656975509518,-0.15452461795728656,0.45621065735316263,-0.8763526565045318,-0.19801510599812283,0.45621065735316263,-0.8675608646740282,-0.24101329728576085,0.45621065735316263,-0.8566121798391853,-0.28341229154177194,0.45621065735316263,-0.8435338221556009,-0.3251066781859587,0.45621065735316263,-0.8283583064811306,-0.3659927984036663,0.45621065735316263,-0.8111233615388281,-0.405969002857928,0.45621065735316263,-0.7918718361173952,-0.4449359044057346,0.45621065735316263,-0.770651592542341,-0.4827966251901278,0.45621065735316263,-0.7475153876827009,-0.5194570374938214,0.45621065735316263,-0.7225207417891459,-0.5548259977555354,0.45621065735316263,-0.6957297954895779,-0.5888155731672489,0.45621065735316263,-0.6672091552977368,-0.621341260289018,0.45621065735316263,-0.6370297280189128,-0.6523221951378405,0.45621065735316263,-0.6052665444644568,-0.6816813542282592,0.45621065735316263,-0.5719985729133613,-0.7093457460648832,0.45621065735316263,-0.5373085227846749,-0.7352465926107452,0.45621065735316263,-0.5012826390088572,-0.7593195002803392,0.45621065735316263,-0.4640104876092901,-0.7815046200322194,0.45621065735316263,-0.4255847330270338,-0.8017467961631469,0.45621065735316263,-0.38610090774242495,-0.81999570343386,0.45621065735316263,-0.3456571747662769,-0.8362059721855458,0.45621065735316263,-0.3043540835911708,-0.8503373011359633,0.45621065735316263,-0.2622943202095716,-0.862354557574779,0.45621065735316263,-0.21958245182027272,-0.8722278647090211,0.45621065735316263,-0.17632466685786427,-0.8799326759414943,0.45621065735316263,-0.13262851099155298,-0.885449835897488,0.45621065735316263,-0.08860261974968228,-0.888765628048056,0.45621065735316263,-0.04435644843469452,-0.8898718088114688,0.45621065735316263,-1.0897786623124307e-16,-0.888765628048056,0.45621065735316263,0.0443564484346943,-0.885449835897488,0.45621065735316263,0.08860261974968206,-0.8799326759414943,0.45621065735316263,0.13262851099155276,-0.8722278647090211,0.45621065735316263,0.17632466685786405,-0.8623545575747791,0.45621065735316263,0.21958245182027253,-0.8503373011359634,0.45621065735316263,0.26229432020957105,-0.8362059721855459,0.45621065735316263,0.30435408359117055,-0.8199957034338599,0.45621065735316263,0.34565717476627705,-0.801746796163147,0.45621065735316263,0.3861009077424248,-0.7815046200322194,0.45621065735316263,0.42558473302703365,-0.7593195002803393,0.45621065735316263,0.46401048760928987,-0.7352465926107453,0.45621065735316263,0.501282639008857,-0.7093457460648833,0.45621065735316263,0.5373085227846747,-0.6816813542282593,0.45621065735316263,0.5719985729133611,-0.6523221951378407,0.45621065735316263,0.6052665444644566,-0.6213412602890183,0.45621065735316263,0.6370297280189126,-0.5888155731672493,0.45621065735316263,0.6672091552977364,-0.5548259977555355,0.45621065735316263,0.6957297954895777,-0.5194570374938222,0.45621065735316263,0.7225207417891455,-0.48279662519012756,0.45621065735316263,0.7475153876827009,-0.44493590440573405,0.45621065735316263,0.7706515925423412,-0.40596900285792853,0.45621065735316263,0.791871836117395,-0.36599279840366644,0.45621065735316263,0.8111233615388281,-0.32510667818595945,0.45621065735316263,0.8283583064811304,-0.28341229154177255,0.45621065735316263,0.8435338221556007,-0.24101329728576049,0.45621065735316263,0.8566121798391854,-0.19801510599812305,0.45621065735316263,0.8675608646740282,-0.1545246179572866,0.45621065735316263,0.8763526565045318,-0.11064995736897726,0.45621065735316263,0.8829656975509517,-0.06650020355240212,0.45621065735316263,0.8873835467512818,-0.0221851197518803,0.45621065735316263,0.889595220636324,0.02218511975187997,0.45621065735316263,0.889595220636324,0.06650020355240178,0.45621065735316263,0.8873835467512818,0.11064995736897615,0.45621065735316263,0.8829656975509519,0.15452461795728628,0.45621065735316263,0.8763526565045319,0.19801510599812272,0.45621065735316263,0.8675608646740282,0.24101329728576093,0.45621065735316263,0.8566121798391853,0.2834122915417722,0.45621065735316263,0.8435338221556008,0.3251066781859584,0.45621065735316263,0.8283583064811307,0.36599279840366616,0.45621065735316263,0.8111233615388281,0.4059690028579282,0.45621065735316263,0.7918718361173951,0.4449359044057345,0.45621065735316263,0.770651592542341,0.48279662519012795,0.45621065735316263,0.7475153876827008,0.5194570374938213,0.45621065735316263,0.722520741789146,0.5548259977555353,0.45621065735316263,0.6957297954895779,0.5888155731672484,0.45621065735316263,0.6672091552977372,0.6213412602890183,0.45621065735316263,0.6370297280189126,0.6523221951378407,0.45621065735316263,0.6052665444644566,0.681681354228259,0.45621065735316263,0.5719985729133613,0.7093457460648831,0.45621065735316263,0.5373085227846749,0.7352465926107449,0.45621065735316263,0.5012826390088576,0.7593195002803393,0.45621065735316263,0.46401048760928987,0.7815046200322191,0.45621065735316263,0.42558473302703426,0.8017467961631469,0.45621065735316263,0.38610090774242506,0.8199957034338599,0.45621065735316263,0.345657174766277,0.8362059721855456,0.45621065735316263,0.3043540835911713,0.8503373011359633,0.45621065735316263,0.2622943202095717,0.862354557574779,0.45621065735316263,0.21958245182027283,0.8722278647090211,0.45621065735316263,0.17632466685786435,0.8799326759414943,0.45621065735316263,0.1326285109915527,0.8854498358974879,0.45621065735316263,0.08860261974968277,0.888765628048056,0.45621065735316263,0.04435644843469463,0.8898718088114688,0.45621065735316263,0.0,0.8660254037844385,0.5000000000000002,0.0,0.8649488659811199,0.5000000000000002,-0.043167803256298216,0.8617219290137647,0.5000000000000002,-0.08622828455208915,0.8563526155561169,0.5000000000000002,-0.12907438874616922,0.8488542745674328,0.5000000000000002,-0.17159959367258745,0.8392455481048651,0.5000000000000002,-0.2136981749715338,0.8275503249763713,0.5000000000000002,-0.2552654689367559,0.8137976813493736,0.5000000000000002,-0.29619813272602374,0.7980218084628246,0.5000000000000002,-0.33639440128771864,0.780261927622401,0.5000000000000002,-0.3757543403647852,0.760562192690157,0.5000000000000002,-0.4141800949470413,0.7389715803110671,0.5000000000000002,-0.4515761325541506,0.715543768149369,0.5000000000000002,-0.48784948074442164,0.6903370014374333,0.5000000000000002,-0.5229099582589464,0.6634139481689383,0.5000000000000002,-0.5566703992264191,0.6348415432963638,0.5000000000000002,-0.5890468698712275,0.6046908223201528,0.5000000000000002,-0.6199588771860414,0.5730367446832646,0.5000000000000002,-0.6493295690501141,0.5399580074101893,0.5000000000000002,-0.6770859252957616,0.5055368494537449,0.5000000000000002,-0.7031589392480063,0.46985884723608284,0.5000000000000002,-0.7274837892860425,0.43301270189221913,0.5000000000000002,-0.7499999999999999,0.3950900187450372,0.5000000000000002,-0.7706515925423407,0.35618507956002,0.5000000000000002,-0.7893872238000955,0.31639460814592757,0.5000000000000002,-0.8061603140419309,0.27581752988416824,0.5000000000000002,-0.8209291627227016,0.2345547257847124,0.5000000000000002,-0.8336570521575752,0.1927087816800076,0.5000000000000002,-0.8443123388079834,0.15038373318043533,0.5000000000000002,-0.852868531952443,0.10768480702539139,0.5000000000000002,-0.8593043595466648,0.06471815947303206,0.5000000000000002,-0.8636038211092069,0.021590612379090064,0.5000000000000002,-0.8657562275011919,-0.02159061237908996,0.5000000000000002,-0.8657562275011919,-0.06471815947303196,0.5000000000000002,-0.8636038211092069,-0.1076848070253913,0.5000000000000002,-0.8593043595466648,-0.15038373318043524,0.5000000000000002,-0.852868531952443,-0.1927087816800075,0.5000000000000002,-0.8443123388079834,-0.23455472578471226,0.5000000000000002,-0.8336570521575752,-0.27581752988416813,0.5000000000000002,-0.8209291627227016,-0.3163946081459275,0.5000000000000002,-0.806160314041931,-0.3561850795600197,0.5000000000000002,-0.7893872238000956,-0.39509001874503674,0.5000000000000002,-0.7706515925423408,-0.4330127018922194,0.5000000000000002,-0.7499999999999998,-0.46985884723608284,0.5000000000000002,-0.7274837892860426,-0.5055368494537446,0.5000000000000002,-0.7031589392480064,-0.5399580074101892,0.5000000000000002,-0.6770859252957617,-0.5730367446832644,0.5000000000000002,-0.6493295690501142,-0.6046908223201527,0.5000000000000002,-0.6199588771860415,-0.6348415432963638,0.5000000000000002,-0.5890468698712275,-0.6634139481689382,0.5000000000000002,-0.5566703992264194,-0.6903370014374333,0.5000000000000002,-0.5229099582589464,-0.7155437681493689,0.5000000000000002,-0.4878494807444218,-0.738971580311067,0.5000000000000002,-0.45157613255415063,-0.7605621926901571,0.5000000000000002,-0.4141800949470412,-0.7802619276224009,0.5000000000000002,-0.37575434036478533,-0.7980218084628247,0.5000000000000002,-0.3363944012877185,-0.8137976813493735,0.5000000000000002,-0.2961981327260239,-0.8275503249763712,0.5000000000000002,-0.2552654689367562,-0.8392455481048651,0.5000000000000002,-0.21369817497153398,-0.8488542745674328,0.5000000000000002,-0.17159959367258748,-0.8563526155561169,0.5000000000000002,-0.12907438874616947,-0.8617219290137647,0.5000000000000002,-0.08622828455208921,-0.8649488659811199,0.5000000000000002,-0.04316780325629849,-0.8660254037844385,0.5000000000000002,-1.0605752387249067e-16,-0.8649488659811199,0.5000000000000002,0.04316780325629827,-0.8617219290137647,0.5000000000000002,0.086228284552089,-0.8563526155561169,0.5000000000000002,0.12907438874616925,-0.8488542745674329,0.5000000000000002,0.17159959367258726,-0.8392455481048651,0.5000000000000002,0.2136981749715338,-0.8275503249763714,0.5000000000000002,0.25526546893675567,-0.8137976813493736,0.5000000000000002,0.29619813272602374,-0.7980218084628246,0.5000000000000002,0.33639440128771864,-0.780261927622401,0.5000000000000002,0.37575434036478517,-0.7605621926901571,0.5000000000000002,0.414180094947041,-0.7389715803110671,0.5000000000000002,0.45157613255415047,-0.715543768149369,0.5000000000000002,0.48784948074442164,-0.6903370014374334,0.5000000000000002,0.5229099582589462,-0.6634139481689383,0.5000000000000002,0.5566703992264191,-0.634841543296364,0.5000000000000002,0.5890468698712272,-0.6046908223201528,0.5000000000000002,0.6199588771860414,-0.5730367446832648,0.5000000000000002,0.6493295690501139,-0.5399580074101893,0.5000000000000002,0.6770859252957615,-0.5055368494537453,0.5000000000000002,0.703158939248006,-0.4698588472360826,0.5000000000000002,0.7274837892860426,-0.4330127018922189,0.5000000000000002,0.75,-0.39509001874503724,0.5000000000000002,0.7706515925423406,-0.35618507956001993,0.5000000000000002,0.7893872238000955,-0.31639460814592824,0.5000000000000002,0.8061603140419307,-0.27581752988416874,0.5000000000000002,0.8209291627227013,-0.23455472578471193,0.5000000000000002,0.8336570521575754,-0.1927087816800077,0.5000000000000002,0.8443123388079834,-0.15038373318043524,0.5000000000000002,0.852868531952443,-0.10768480702539189,0.5000000000000002,0.8593043595466648,-0.06471815947303235,0.5000000000000002,0.8636038211092069,-0.02159061237909017,0.5000000000000002,0.8657562275011919,0.021590612379089852,0.5000000000000002,0.8657562275011919,0.06471815947303204,0.5000000000000002,0.8636038211092069,0.10768480702539081,0.5000000000000002,0.859304359546665,0.15038373318043494,0.5000000000000002,0.8528685319524432,0.1927087816800074,0.5000000000000002,0.8443123388079834,0.23455472578471234,0.5000000000000002,0.8336570521575752,0.2758175298841684,0.5000000000000002,0.8209291627227014,0.31639460814592724,0.5000000000000002,0.8061603140419311,0.35618507956001966,0.5000000000000002,0.7893872238000956,0.39509001874503696,0.5000000000000002,0.7706515925423408,0.43301270189221935,0.5000000000000002,0.7499999999999998,0.469858847236083,0.5000000000000002,0.7274837892860424,0.5055368494537446,0.5000000000000002,0.7031589392480065,0.5399580074101891,0.5000000000000002,0.6770859252957617,0.5730367446832639,0.5000000000000002,0.6493295690501145,0.6046908223201528,0.5000000000000002,0.6199588771860414,0.634841543296364,0.5000000000000002,0.5890468698712272,0.6634139481689381,0.5000000000000002,0.5566703992264195,0.6903370014374332,0.5000000000000002,0.5229099582589464,0.7155437681493686,0.5000000000000002,0.4878494807444222,0.7389715803110672,0.5000000000000002,0.45157613255415047,0.7605621926901569,0.5000000000000002,0.4141800949470416,0.7802619276224009,0.5000000000000002,0.37575434036478544,0.7980218084628246,0.5000000000000002,0.33639440128771864,0.8137976813493732,0.5000000000000002,0.2961981327260244,0.8275503249763712,0.5000000000000002,0.25526546893675633,0.8392455481048651,0.5000000000000002,0.2136981749715341,0.8488542745674328,0.5000000000000002,0.1715995936725876,0.8563526155561169,0.5000000000000002,0.1290743887461692,0.8617219290137645,0.5000000000000002,0.0862282845520897,0.8649488659811199,0.5000000000000002,0.04316780325629859,0.8660254037844385,0.5000000000000002,0.0,0.8400259231507714,0.5425462638657594,0.0,0.8389817047501475,0.5425462638657594,-0.04187183611739494,0.8358516456397491,0.5425462638657594,-0.08363957225278529,0.8306435276397036,0.5425462638657594,-0.12519936723312391,0.8233702989519613,0.5425462638657594,-0.1664478968598389,0.8140500419690255,0.5425462638657594,-0.20728261078907093,0.8027059283182756,0.5425462638657594,-0.24760198748798645,0.7893661612536504,0.5425462638657594,-0.28730578663330403,0.7740639055379149,0.5425462638657594,-0.32629529832453225,0.7568372049898351,0.5425462638657594,-0.36447358849233524,0.7377288879012491,0.5425462638657594,-0.4017457398919021,0.7167864605591873,0.5425462638657594,-0.43801908808217305,0.6940619891377555,0.5425462638657594,-0.4732034518042403,0.6696119702534244,0.5425462638657594,-0.5072113571861663,0.6434971905055381,0.5425462638657594,-0.5399582552168128,0.6157825753512508,0.5425462638657594,-0.5713627319480041,0.5865370276906106,0.5425462638657594,-0.6013467109024291,0.5558332565630932,0.5425462638657594,-0.6298356471840668,0.5237475963814743,0.5425462638657594,-0.6567587128085429,0.49035981715245075,0.5425462638657594,-0.6820489727926586,0.45575292615583657,0.5425462638657594,-0.7056435515653057,0.4200129615753856,0.5425462638657594,-0.7274837892860427,0.3832287785943111,0.5425462638657594,-0.7475153876827006,0.3454918284872986,0.5425462638657594,-0.7656885450454439,0.3068959312582294,0.5425462638657594,-0.7819580800416668,0.26753704238886805,0.5425462638657594,-0.7962835440439058,0.227513014278415,0.5425462638657594,-0.8086293216915,0.1869233529670249,0.5425462638657594,-0.8189647194359906,0.1458689707481124,0.5425462638657594,-0.8272640418501169,0.10445193528449068,0.5425462638657594,-0.8335066555106986,0.0627752158520797,0.5425462638657594,-0.8376770402965754,0.02094242734206211,0.5425462638657594,-0.8397648279740751,-0.020942427342062007,0.5425462638657594,-0.8397648279740751,-0.06277521585207958,0.5425462638657594,-0.8376770402965754,-0.1044519352844906,0.5425462638657594,-0.8335066555106986,-0.14586897074811228,0.5425462638657594,-0.8272640418501169,-0.18692335296702478,0.5425462638657594,-0.8189647194359906,-0.22751301427841486,0.5425462638657594,-0.8086293216915001,-0.267537042388868,0.5425462638657594,-0.7962835440439058,-0.30689593125822934,0.5425462638657594,-0.7819580800416669,-0.34549182848729837,0.5425462638657594,-0.765688545045444,-0.38322877859431065,0.5425462638657594,-0.7475153876827008,-0.42001296157538587,0.5425462638657594,-0.7274837892860426,-0.45575292615583657,0.5425462638657594,-0.7056435515653058,-0.4903598171524505,0.5425462638657594,-0.6820489727926587,-0.5237475963814742,0.5425462638657594,-0.6567587128085429,-0.555833256563093,0.5425462638657594,-0.629835647184067,-0.5865370276906104,0.5425462638657594,-0.6013467109024292,-0.6157825753512508,0.5425462638657594,-0.5713627319480041,-0.643497190505538,0.5425462638657594,-0.5399582552168131,-0.6696119702534244,0.5425462638657594,-0.5072113571861663,-0.6940619891377554,0.5425462638657594,-0.47320345180424045,-0.7167864605591872,0.5425462638657594,-0.43801908808217316,-0.7377288879012492,0.5425462638657594,-0.401745739891902,-0.756837204989835,0.5425462638657594,-0.3644735884923353,-0.774063905537915,0.5425462638657594,-0.3262952983245322,-0.7893661612536504,0.5425462638657594,-0.28730578663330414,-0.8027059283182755,0.5425462638657594,-0.24760198748798676,-0.8140500419690254,0.5425462638657594,-0.20728261078907112,-0.8233702989519613,0.5425462638657594,-0.1664478968598389,-0.8306435276397036,0.5425462638657594,-0.12519936723312416,-0.8358516456397491,0.5425462638657594,-0.08363957225278534,-0.8389817047501475,0.5425462638657594,-0.0418718361173952,-0.8400259231507714,0.5425462638657594,-1.0287350579873927e-16,-0.8389817047501475,0.5425462638657594,0.04187183611739499,-0.8358516456397491,0.5425462638657594,0.08363957225278514,-0.8306435276397036,0.5425462638657594,0.12519936723312394,-0.8233702989519613,0.5425462638657594,0.1664478968598387,-0.8140500419690255,0.5425462638657594,0.20728261078907093,-0.8027059283182757,0.5425462638657594,0.2476019874879862,-0.7893661612536504,0.5425462638657594,0.287305786633304,-0.7740639055379149,0.5425462638657594,0.3262952983245323,-0.7568372049898351,0.5425462638657594,0.36447358849233513,-0.7377288879012492,0.5425462638657594,0.4017457398919018,-0.7167864605591873,0.5425462638657594,0.43801908808217294,-0.6940619891377555,0.5425462638657594,0.4732034518042403,-0.6696119702534246,0.5425462638657594,0.5072113571861662,-0.6434971905055381,0.5425462638657594,0.5399582552168128,-0.615782575351251,0.5425462638657594,0.5713627319480039,-0.5865370276906107,0.5425462638657594,0.601346710902429,-0.5558332565630935,0.5425462638657594,0.6298356471840666,-0.5237475963814743,0.5425462638657594,0.6567587128085428,-0.49035981715245125,0.5425462638657594,0.6820489727926583,-0.4557529261558364,0.5425462638657594,0.7056435515653058,-0.42001296157538537,0.5425462638657594,0.7274837892860427,-0.38322877859431115,0.5425462638657594,0.7475153876827005,-0.34549182848729854,0.5425462638657594,0.7656885450454439,-0.30689593125823,0.5425462638657594,0.7819580800416667,-0.26753704238886855,0.5425462638657594,0.7962835440439056,-0.22751301427841453,0.5425462638657594,0.8086293216915001,-0.186923352967025,0.5425462638657594,0.8189647194359906,-0.1458689707481123,0.5425462638657594,0.8272640418501169,-0.10445193528449116,0.5425462638657594,0.8335066555106985,-0.06277521585207997,0.5425462638657594,0.8376770402965754,-0.02094242734206221,0.5425462638657594,0.8397648279740751,0.020942427342061903,0.5425462638657594,0.8397648279740751,0.06277521585207967,0.5425462638657594,0.8376770402965754,0.10445193528449011,0.5425462638657594,0.8335066555106986,0.145868970748112,0.5425462638657594,0.827264041850117,0.1869233529670247,0.5425462638657594,0.8189647194359906,0.22751301427841497,0.5425462638657594,0.8086293216915,0.2675370423888682,0.5425462638657594,0.7962835440439057,0.30689593125822906,0.5425462638657594,0.781958080041667,0.34549182848729826,0.5425462638657594,0.765688545045444,0.38322877859431087,0.5425462638657594,0.7475153876827008,0.4200129615753858,0.5425462638657594,0.7274837892860426,0.4557529261558368,0.5425462638657594,0.7056435515653056,0.49035981715245036,0.5425462638657594,0.6820489727926589,0.5237475963814741,0.5425462638657594,0.6567587128085429,0.5558332565630927,0.5425462638657594,0.6298356471840673,0.5865370276906107,0.5425462638657594,0.601346710902429,0.615782575351251,0.5425462638657594,0.5713627319480039,0.6434971905055379,0.5425462638657594,0.5399582552168132,0.6696119702534243,0.5425462638657594,0.5072113571861664,0.6940619891377551,0.5425462638657594,0.47320345180424084,0.7167864605591873,0.5425462638657594,0.43801908808217294,0.737728887901249,0.5425462638657594,0.40174573989190243,0.756837204989835,0.5425462638657594,0.3644735884923354,0.7740639055379149,0.5425462638657594,0.32629529832453225,0.7893661612536502,0.5425462638657594,0.28730578663330464,0.8027059283182755,0.5425462638657594,0.24760198748798687,0.8140500419690254,0.5425462638657594,0.2072826107890712,0.8233702989519613,0.5425462638657594,0.16644789685983902,0.8306435276397036,0.5425462638657594,0.1251993672331239,0.8358516456397491,0.5425462638657594,0.0836395722527858,0.8389817047501475,0.5425462638657594,0.0418718361173953,0.8400259231507714,0.5425462638657594,0.0,0.8119380057158566,0.5837436722347897,0.0,0.8109287028094008,0.5837436722347897,-0.04047176899648707,0.8079033033760865,0.5837436722347897,-0.08084291879842972,0.8028693290355936,0.5837436722347897,-0.12101308036645494,0.7958392950412964,0.5837436722347897,-0.16088238434960742,0.786830679165372,0.5837436722347897,-0.20035170937629151,0.7758658782463046,0.5837436722347897,-0.23932292848561906,0.7629721525068172,0.5837436722347897,-0.2776991530864949,0.748181557780661,0.5837436722347897,-0.3153849738379207,0.7315308658167633,0.5837436722347897,-0.3522866978516491,0.7130614728588652,0.5837436722347897,-0.388312581627467,0.6928192967279376,0.5837436722347897,-0.4233730591419917,0.6708546626632427,0.5837436722347897,-0.45738096452391785,0.6472221782058604,0.5837436722347897,-0.49025174876210686,0.621980597435737,0.5837436722347897,-0.5219036899077512,0.5951926748997849,0.5837436722347897,-0.5522580962480146,0.5669250095941913,0.5837436722347897,-0.5812395019460296,0.5372478793888202,0.5837436722347897,-0.608775854660858,0.5062350663053548,0.5837436722347897,-0.6347986946809638,0.473963673083566,0.5837436722347897,-0.6592433251258423,0.44051393149175366,0.5837436722347897,-0.6820489727926587,0.4059690028579282,0.5837436722347897,-0.7031589392480067,0.3704147713176472,0.5837436722347897,-0.7225207417891458,0.3339396302925207,0.5837436722347897,-0.740086243924272,0.2966342627302389,0.5837436722347897,-0.7558117750474188,0.2585914156524762,0.5837436722347897,-0.7696582390104655,0.2199056695711806,0.5837436722347897,-0.781591211322321,0.1806732033465198,0.5837436722347897,-0.7915810247336337,0.14099155507108024,0.5837436722347897,-0.7996028429942461,0.10095937957480068,0.5837436722347897,-0.8056367226000275,0.06067620315352075,0.5837436722347897,-0.809667662375567,0.020242176130927804,0.5837436722347897,-0.8116856407694588,-0.020242176130927703,0.5837436722347897,-0.8116856407694588,-0.060676203153520646,0.5837436722347897,-0.809667662375567,-0.1009593795748006,0.5837436722347897,-0.8056367226000275,-0.14099155507108013,0.5837436722347897,-0.7996028429942461,-0.1806732033465197,0.5837436722347897,-0.7915810247336337,-0.21990566957118046,0.5837436722347897,-0.7815912113223211,-0.25859141565247606,0.5837436722347897,-0.7696582390104655,-0.29663426273023885,0.5837436722347897,-0.7558117750474189,-0.33393963029252044,0.5837436722347897,-0.7400862439242721,-0.37041477131764683,0.5837436722347897,-0.7225207417891459,-0.4059690028579285,0.5837436722347897,-0.7031589392480065,-0.44051393149175366,0.5837436722347897,-0.6820489727926589,-0.47396367308356574,0.5837436722347897,-0.6592433251258424,-0.5062350663053546,0.5837436722347897,-0.634798694680964,-0.53724787938882,0.5837436722347897,-0.6087758546608582,-0.5669250095941912,0.5837436722347897,-0.5812395019460296,-0.5951926748997849,0.5837436722347897,-0.5522580962480146,-0.6219805974357369,0.5837436722347897,-0.5219036899077513,-0.6472221782058604,0.5837436722347897,-0.49025174876210686,-0.6708546626632426,0.5837436722347897,-0.457380964523918,-0.6928192967279375,0.5837436722347897,-0.4233730591419918,-0.7130614728588653,0.5837436722347897,-0.3883125816274669,-0.7315308658167632,0.5837436722347897,-0.35228669785164923,-0.748181557780661,0.5837436722347897,-0.3153849738379206,-0.7629721525068172,0.5837436722347897,-0.277699153086495,-0.7758658782463046,0.5837436722347897,-0.23932292848561937,-0.7868306791653719,0.5837436722347897,-0.2003517093762917,-0.7958392950412964,0.5837436722347897,-0.16088238434960744,-0.8028693290355936,0.5837436722347897,-0.12101308036645517,-0.8079033033760865,0.5837436722347897,-0.08084291879842978,-0.8109287028094008,0.5837436722347897,-0.04047176899648733,-0.8119380057158566,0.5837436722347897,-9.943372798060091e-17,-0.8109287028094008,0.5837436722347897,0.04047176899648713,-0.8079033033760865,0.5837436722347897,0.08084291879842957,-0.8028693290355936,0.5837436722347897,0.12101308036645497,-0.7958392950412965,0.5837436722347897,0.16088238434960722,-0.786830679165372,0.5837436722347897,0.20035170937629151,-0.7758658782463047,0.5837436722347897,0.23932292848561884,-0.7629721525068172,0.5837436722347897,0.27769915308649484,-0.748181557780661,0.5837436722347897,0.31538497383792075,-0.7315308658167633,0.5837436722347897,0.35228669785164907,-0.7130614728588653,0.5837436722347897,0.38831258162746674,-0.6928192967279376,0.5837436722347897,0.42337305914199164,-0.6708546626632427,0.5837436722347897,0.45738096452391785,-0.6472221782058605,0.5837436722347897,0.4902517487621067,-0.621980597435737,0.5837436722347897,0.5219036899077512,-0.5951926748997851,0.5837436722347897,0.5522580962480144,-0.5669250095941915,0.5837436722347897,0.5812395019460295,-0.5372478793888205,0.5837436722347897,0.6087758546608578,-0.5062350663053548,0.5837436722347897,0.6347986946809637,-0.47396367308356646,0.5837436722347897,0.6592433251258419,-0.44051393149175344,0.5837436722347897,0.6820489727926589,-0.405969002857928,0.5837436722347897,0.7031589392480068,-0.37041477131764733,0.5837436722347897,0.7225207417891457,-0.33393963029252066,0.5837436722347897,0.740086243924272,-0.2966342627302395,0.5837436722347897,0.7558117750474186,-0.2585914156524766,0.5837436722347897,0.7696582390104653,-0.21990566957118016,0.5837436722347897,0.7815912113223212,-0.1806732033465199,0.5837436722347897,0.7915810247336337,-0.14099155507108016,0.5837436722347897,0.7996028429942461,-0.10095937957480115,0.5837436722347897,0.8056367226000275,-0.06067620315352102,0.5837436722347897,0.809667662375567,-0.0202421761309279,0.5837436722347897,0.8116856407694588,0.020242176130927603,0.5837436722347897,0.8116856407694588,0.06067620315352073,0.5837436722347897,0.809667662375567,0.10095937957480013,0.5837436722347897,0.8056367226000276,0.14099155507107988,0.5837436722347897,0.7996028429942461,0.18067320334651962,0.5837436722347897,0.7915810247336337,0.21990566957118057,0.5837436722347897,0.781591211322321,0.25859141565247634,0.5837436722347897,0.7696582390104654,0.2966342627302386,0.5837436722347897,0.7558117750474189,0.3339396302925204,0.5837436722347897,0.7400862439242721,0.37041477131764705,0.5837436722347897,0.7225207417891459,0.4059690028579284,0.5837436722347897,0.7031589392480065,0.44051393149175383,0.5837436722347897,0.6820489727926586,0.4739636730835657,0.5837436722347897,0.6592433251258425,0.5062350663053545,0.5837436722347897,0.634798694680964,0.5372478793888197,0.5837436722347897,0.6087758546608585,0.5669250095941915,0.5837436722347897,0.5812395019460295,0.5951926748997851,0.5837436722347897,0.5522580962480144,0.6219805974357367,0.5837436722347897,0.5219036899077514,0.6472221782058603,0.5837436722347897,0.490251748762107,0.6708546626632422,0.5837436722347897,0.4573809645239184,0.6928192967279376,0.5837436722347897,0.42337305914199164,0.713061472858865,0.5837436722347897,0.38831258162746735,0.7315308658167632,0.5837436722347897,0.35228669785164934,0.748181557780661,0.5837436722347897,0.3153849738379207,0.7629721525068169,0.5837436722347897,0.2776991530864955,0.7758658782463046,0.5837436722347897,0.23932292848561945,0.7868306791653719,0.5837436722347897,0.2003517093762918,0.7958392950412964,0.5837436722347897,0.16088238434960753,0.8028693290355936,0.5837436722347897,0.1210130803664549,0.8079033033760864,0.5837436722347897,0.08084291879843022,0.8109287028094008,0.5837436722347897,0.040471768996487426,0.8119380057158566,0.5837436722347897,0.0,0.7818314824680298,0.6234898018587335,0.0,0.7808596043417954,0.6234898018587335,-0.038971082681034774,0.7779463862052449,0.6234898018587335,-0.07784527711015672,0.773099070777679,0.6234898018587335,-0.11652593591491316,0.7663297092490039,0.6234898018587335,-0.15491689288290753,0.7576551313185753,0.6234898018587335,-0.19292270204615544,0.7470969033539131,0.6234898018587335,-0.23044887497479993,0.7346812747733149,0.6234898018587335,-0.2674021156902356,0.7204391127856651,0.6234898018587335,-0.3036905526136117,0.7044058256496909,0.6234898018587335,-0.33922396697305235,0.6866212746434514,0.6234898018587335,-0.3739140171017386,0.6671296749629204,0.6234898018587335,-0.40767445806921154,0.6459794857960423,0.6234898018587335,-0.4404213560998583,0.623223289845555,0.6234898018587335,-0.47207329724550257,0.5989176626001069,0.6234898018587335,-0.5025515897933083,0.5731230316786773,0.6234898018587335,-0.5317804599057764,0.5459035265979943,0.6234898018587335,-0.5596872400064433,0.5173268193364526,0.6234898018587335,-0.5862025494429264,0.48746395609091187,0.6234898018587335,-0.6112604669781572,0.4563891806446575,0.6234898018587335,-0.6347986946809637,0.4241797497856576,0.6234898018587335,-0.6567587128085429,0.3909157412340148,0.6234898018587335,-0.6770859252957618,0.3566798545561379,0.6234898018587335,-0.6957297954895776,0.3215572055605876,0.6234898018587335,-0.7126439717911205,0.28563511468676717,0.6234898018587335,-0.7277864028930704,0.24900288991255135,0.6234898018587335,-0.7411194423258339,0.21175160472058094,0.6234898018587335,-0.7526099420525987,0.17397387167523587,0.6234898018587335,-0.7622293348805763,0.13576361217320804,0.6234898018587335,-0.7699537054835439,0.09721582294011026,0.6234898018587335,-0.7757638498591142,0.05842633985364879,0.6234898018587335,-0.7796453230729106,0.019491599680531102,0.6234898018587335,-0.7815884751709502,-0.01949159968053101,0.6234898018587335,-0.7815884751709502,-0.058426339853648694,0.6234898018587335,-0.7796453230729106,-0.09721582294011018,0.6234898018587335,-0.7757638498591142,-0.13576361217320795,0.6234898018587335,-0.7699537054835439,-0.17397387167523579,0.6234898018587335,-0.7622293348805763,-0.2117516047205808,0.6234898018587335,-0.7526099420525988,-0.24900288991255126,0.6234898018587335,-0.7411194423258339,-0.2856351146867671,0.6234898018587335,-0.7277864028930704,-0.32155720556058737,0.6234898018587335,-0.7126439717911205,-0.3566798545561375,0.6234898018587335,-0.6957297954895778,-0.39091574123401507,0.6234898018587335,-0.6770859252957617,-0.4241797497856576,0.6234898018587335,-0.6567587128085429,-0.4563891806446573,0.6234898018587335,-0.6347986946809638,-0.48746395609091175,0.6234898018587335,-0.6112604669781573,-0.5173268193364524,0.6234898018587335,-0.5862025494429266,-0.5459035265979943,0.6234898018587335,-0.5596872400064434,-0.5731230316786773,0.6234898018587335,-0.5317804599057764,-0.5989176626001068,0.6234898018587335,-0.5025515897933085,-0.623223289845555,0.6234898018587335,-0.47207329724550257,-0.6459794857960421,0.6234898018587335,-0.44042135609985844,-0.6671296749629204,0.6234898018587335,-0.40767445806921165,-0.6866212746434515,0.6234898018587335,-0.37391401710173855,-0.7044058256496908,0.6234898018587335,-0.33922396697305246,-0.7204391127856652,0.6234898018587335,-0.30369055261361166,-0.7346812747733149,0.6234898018587335,-0.26740211569023575,-0.747096903353913,0.6234898018587335,-0.2304488749748002,-0.7576551313185752,0.6234898018587335,-0.19292270204615564,-0.7663297092490039,0.6234898018587335,-0.15491689288290755,-0.773099070777679,0.6234898018587335,-0.11652593591491338,-0.7779463862052449,0.6234898018587335,-0.07784527711015676,-0.7808596043417954,0.6234898018587335,-0.038971082681035024,-0.7818314824680298,0.6234898018587335,-9.574674224771027e-17,-0.7808596043417954,0.6234898018587335,0.03897108268103483,-0.7779463862052449,0.6234898018587335,0.07784527711015657,-0.773099070777679,0.6234898018587335,0.11652593591491318,-0.766329709249004,0.6234898018587335,0.15491689288290736,-0.7576551313185753,0.6234898018587335,0.19292270204615544,-0.7470969033539132,0.6234898018587335,0.2304488749747997,-0.7346812747733149,0.6234898018587335,0.26740211569023553,-0.7204391127856651,0.6234898018587335,0.3036905526136118,-0.7044058256496909,0.6234898018587335,0.3392239669730523,-0.6866212746434515,0.6234898018587335,0.3739140171017384,-0.6671296749629204,0.6234898018587335,0.4076744580692115,-0.6459794857960423,0.6234898018587335,0.4404213560998583,-0.6232232898455551,0.6234898018587335,0.47207329724550234,-0.5989176626001069,0.6234898018587335,0.5025515897933083,-0.5731230316786775,0.6234898018587335,0.5317804599057762,-0.5459035265979945,0.6234898018587335,0.5596872400064432,-0.5173268193364529,0.6234898018587335,0.5862025494429262,-0.48746395609091187,0.6234898018587335,0.6112604669781571,-0.45638918064465794,0.6234898018587335,0.6347986946809634,-0.4241797497856574,0.6234898018587335,0.6567587128085429,-0.3909157412340146,0.6234898018587335,0.677085925295762,-0.35667985455613793,0.6234898018587335,0.6957297954895776,-0.32155720556058753,0.6234898018587335,0.7126439717911205,-0.2856351146867678,0.6234898018587335,0.7277864028930702,-0.2490028899125518,0.6234898018587335,0.7411194423258337,-0.2117516047205805,0.6234898018587335,0.7526099420525989,-0.17397387167523598,0.6234898018587335,0.7622293348805763,-0.13576361217320795,0.6234898018587335,0.7699537054835439,-0.0972158229401107,0.6234898018587335,0.7757638498591142,-0.05842633985364905,0.6234898018587335,0.7796453230729106,-0.0194915996805312,0.6234898018587335,0.7815884751709502,0.01949159968053091,0.6234898018587335,0.7815884751709502,0.05842633985364877,0.6234898018587335,0.7796453230729106,0.09721582294010973,0.6234898018587335,0.7757638498591143,0.13576361217320768,0.6234898018587335,0.769953705483544,0.1739738716752357,0.6234898018587335,0.7622293348805763,0.21175160472058088,0.6234898018587335,0.7526099420525987,0.24900288991255148,0.6234898018587335,0.7411194423258338,0.28563511468676683,0.6234898018587335,0.7277864028930705,0.3215572055605873,0.6234898018587335,0.7126439717911205,0.3566798545561377,0.6234898018587335,0.6957297954895777,0.390915741234015,0.6234898018587335,0.6770859252957617,0.42417974978565776,0.6234898018587335,0.6567587128085428,0.45638918064465717,0.6234898018587335,0.634798694680964,0.4874639560909117,0.6234898018587335,0.6112604669781573,0.5173268193364521,0.6234898018587335,0.5862025494429268,0.5459035265979945,0.6234898018587335,0.5596872400064432,0.5731230316786775,0.6234898018587335,0.5317804599057762,0.5989176626001067,0.6234898018587335,0.5025515897933086,0.6232232898455549,0.6234898018587335,0.4720732972455026,0.6459794857960418,0.6234898018587335,0.4404213560998588,0.6671296749629205,0.6234898018587335,0.4076744580692115,0.6866212746434512,0.6234898018587335,0.37391401710173894,0.7044058256496908,0.6234898018587335,0.3392239669730525,0.7204391127856651,0.6234898018587335,0.3036905526136117,0.7346812747733147,0.6234898018587335,0.26740211569023614,0.747096903353913,0.6234898018587335,0.23044887497480032,0.7576551313185752,0.6234898018587335,0.19292270204615572,0.7663297092490039,0.6234898018587335,0.15491689288290764,0.773099070777679,0.6234898018587335,0.11652593591491311,0.7779463862052448,0.6234898018587335,0.07784527711015721,0.7808596043417954,0.6234898018587335,0.038971082681035114,0.7818314824680298,0.6234898018587335,0.0,0.7497812029677343,0.6616858375968592,0.0,0.7488491658638743,0.6616858375968592,-0.03737350811366966,0.7460553717433881,0.6616858375968592,-0.07465409979751825,0.7414067664186555,0.6616858375968592,-0.11174908962660933,0.7349149070549581,0.6616858375968592,-0.14856625361146125,0.7265959334375455,0.6616858375968592,-0.18501405848141597,0.7164705278455857,0.6616858375968592,-0.2210018892507721,0.7045638636327614,0.6616858375968592,-0.2564402745019168,0.690905542642345,0.6616858375968592,-0.2912411088253674,0.6755295216123536,0.6616858375968592,-0.3253178718637013,0.6584740277537463,0.6616858375968592,-0.35858584341479705,0.6397814637115563,0.6616858375968592,-0.3909623140596052,0.619498302145233,0.6616858375968592,-0.4223667907907965,0.597674970190289,0.6616858375968592,-0.4527211971310599,0.5743657240884955,0.6616858375968592,-0.48195006724352785,0.5496285142983166,0.6616858375968592,-0.5099807335517372,0.5235248414209388,0.6616858375968592,-0.5367435074026734,0.49611960330008614,0.6616858375968592,-0.5621718523237427,0.4674809336757556,0.6616858375968592,-0.5862025494429265,0.4376800327930036,0.6616858375968592,-0.608775854660858,0.4067909903869189,0.6616858375968592,-0.6298356471840669,0.3748906014838671,0.6616858375968592,-0.6493295690501143,0.34205817547695544,0.6616858375968592,-0.6672091552977366,0.3083753389503846,0.6616858375968592,-0.6834299544583763,0.27392583274289994,0.6616858375968592,-0.6979516390695358,0.238795303754872,0.6616858375968592,-0.710738105935204,0.20307109201660672,0.6616858375968592,-0.7217575658840893,0.1668420135472692,0.6616858375968592,-0.7309826228025094,0.13019813954426596,0.6616858375968592,-0.7383903417454447,0.09323057245205617,0.6616858375968592,-0.7439623059564273,0.05603121946712065,0.6616858375968592,-0.7476846626544977,0.018692564042190156,0.6616858375968592,-0.7495481574744023,-0.018692564042190062,0.6616858375968592,-0.7495481574744023,-0.05603121946712056,0.6616858375968592,-0.7476846626544977,-0.09323057245205608,0.6616858375968592,-0.7439623059564273,-0.13019813954426587,0.6616858375968592,-0.7383903417454447,-0.16684201354726913,0.6616858375968592,-0.7309826228025094,-0.2030710920166066,0.6616858375968592,-0.7217575658840895,-0.23879530375487193,0.6616858375968592,-0.710738105935204,-0.2739258327428999,0.6616858375968592,-0.6979516390695358,-0.30837533895038444,0.6616858375968592,-0.6834299544583763,-0.3420581754769551,0.6616858375968592,-0.6672091552977367,-0.3748906014838673,0.6616858375968592,-0.6493295690501142,-0.4067909903869189,0.6616858375968592,-0.629835647184067,-0.4376800327930034,0.6616858375968592,-0.6087758546608582,-0.4674809336757555,0.6616858375968592,-0.5862025494429266,-0.496119603300086,0.6616858375968592,-0.5621718523237429,-0.5235248414209387,0.6616858375968592,-0.5367435074026735,-0.5496285142983166,0.6616858375968592,-0.5099807335517372,-0.5743657240884954,0.6616858375968592,-0.481950067243528,-0.597674970190289,0.6616858375968592,-0.4527211971310599,-0.6194983021452329,0.6616858375968592,-0.42236679079079664,-0.6397814637115562,0.6616858375968592,-0.3909623140596053,-0.6584740277537464,0.6616858375968592,-0.35858584341479693,-0.6755295216123535,0.6616858375968592,-0.3253178718637014,-0.6909055426423452,0.6616858375968592,-0.29124110882536736,-0.7045638636327614,0.6616858375968592,-0.2564402745019169,-0.7164705278455856,0.6616858375968592,-0.22100188925077238,-0.7265959334375454,0.6616858375968592,-0.18501405848141614,-0.7349149070549581,0.6616858375968592,-0.14856625361146128,-0.7414067664186555,0.6616858375968592,-0.11174908962660954,-0.7460553717433881,0.6616858375968592,-0.0746540997975183,-0.7488491658638743,0.6616858375968592,-0.0373735081136699,-0.7497812029677343,0.6616858375968592,-9.182171502752879e-17,-0.7488491658638743,0.6616858375968592,0.03737350811366971,-0.7460553717433881,0.6616858375968592,0.07465409979751811,-0.7414067664186555,0.6616858375968592,0.11174908962660936,-0.7349149070549582,0.6616858375968592,0.14856625361146109,-0.7265959334375455,0.6616858375968592,0.18501405848141597,-0.7164705278455858,0.6616858375968592,0.22100188925077188,-0.7045638636327614,0.6616858375968592,0.25644027450191675,-0.690905542642345,0.6616858375968592,0.29124110882536747,-0.6755295216123536,0.6616858375968592,0.32531787186370126,-0.6584740277537464,0.6616858375968592,0.35858584341479677,-0.6397814637115563,0.6616858375968592,0.39096231405960513,-0.619498302145233,0.6616858375968592,0.4223667907907965,-0.597674970190289,0.6616858375968592,0.45272119713105974,-0.5743657240884955,0.6616858375968592,0.48195006724352785,-0.5496285142983167,0.6616858375968592,0.509980733551737,-0.5235248414209388,0.6616858375968592,0.5367435074026733,-0.49611960330008636,0.6616858375968592,0.5621718523237426,-0.4674809336757556,0.6616858375968592,0.5862025494429264,-0.43768003279300405,0.6616858375968592,0.6087758546608577,-0.40679099038691874,0.6616858375968592,0.629835647184067,-0.3748906014838669,0.6616858375968592,0.6493295690501144,-0.34205817547695555,0.6616858375968592,0.6672091552977365,-0.3083753389503846,0.6616858375968592,0.6834299544583763,-0.2739258327429005,0.6616858375968592,0.6979516390695356,-0.23879530375487243,0.6616858375968592,0.7107381059352038,-0.2030710920166063,0.6616858375968592,0.7217575658840896,-0.16684201354726932,0.6616858375968592,0.7309826228025094,-0.13019813954426587,0.6616858375968592,0.7383903417454447,-0.09323057245205658,0.6616858375968592,0.7439623059564272,-0.0560312194671209,0.6616858375968592,0.7476846626544977,-0.018692564042190246,0.6616858375968592,0.7495481574744023,0.01869256404218997,0.6616858375968592,0.7495481574744023,0.05603121946712063,0.6616858375968592,0.7476846626544977,0.09323057245205565,0.6616858375968592,0.7439623059564273,0.13019813954426562,0.6616858375968592,0.7383903417454448,0.16684201354726905,0.6616858375968592,0.7309826228025094,0.2030710920166067,0.6616858375968592,0.7217575658840893,0.23879530375487215,0.6616858375968592,0.7107381059352039,0.2739258327428996,0.6616858375968592,0.697951639069536,0.3083753389503843,0.6616858375968592,0.6834299544583763,0.3420581754769553,0.6616858375968592,0.6672091552977366,0.3748906014838672,0.6616858375968592,0.6493295690501142,0.40679099038691907,0.6616858375968592,0.6298356471840668,0.43768003279300327,0.6616858375968592,0.6087758546608582,0.46748093367575544,0.6616858375968592,0.5862025494429266,0.49611960330008564,0.6616858375968592,0.5621718523237431,0.5235248414209388,0.6616858375968592,0.5367435074026733,0.5496285142983167,0.6616858375968592,0.509980733551737,0.5743657240884953,0.6616858375968592,0.4819500672435281,0.5976749701902889,0.6616858375968592,0.45272119713105996,0.6194983021452327,0.6616858375968592,0.422366790790797,0.6397814637115564,0.6616858375968592,0.39096231405960513,0.6584740277537462,0.6616858375968592,0.3585858434147973,0.6755295216123535,0.6616858375968592,0.3253178718637015,0.690905542642345,0.6616858375968592,0.2912411088253674,0.7045638636327611,0.6616858375968592,0.2564402745019173,0.7164705278455856,0.6616858375968592,0.22100188925077247,0.7265959334375454,0.6616858375968592,0.18501405848141622,0.7349149070549581,0.6616858375968592,0.14856625361146136,0.7414067664186555,0.6616858375968592,0.11174908962660929,0.746055371743388,0.6616858375968592,0.07465409979751872,0.7488491658638743,0.6616858375968592,0.03737350811366998,0.7497812029677343,0.6616858375968592,0.0,0.7158668492597184,0.6982368180860727,0.0,0.7149769703693267,0.6982368180860727,-0.03568301711648346,0.7123095460772846,0.6982368180860727,-0.07127732063012225,0.7078712080206669,0.6982368180860727,-0.1066944174940663,0.7016729906071967,0.6982368180860727,-0.14184625522511773,0.6937303035819684,0.6982368180860727,-0.1766454408160778,0.6840628937163934,0.6982368180860727,-0.2110054580085328,0.6726947957146157,0.6982368180860727,-0.2448408823859038,0.6596542724594495,0.6982368180860727,-0.27806759375200635,0.6449737447464002,0.6982368180860727,-0.310602985267112,0.6286897106804582,0.6982368180860727,-0.3423661688215678,0.6108426549360587,0.6982368180860727,-0.3732781761363817,0.5914769481058029,0.6982368180860727,-0.40326215509080676,0.5706407363881733,0.6982368180860727,-0.4322435607888217,0.5483858218884984,0.6982368180860727,-0.46015034088948853,0.5247675338307567,0.6982368180860727,-0.4869131147404247,0.4998445910004082,0.6982368180860727,-0.5124653458690365,0.4736789557602415,0.6982368180860727,-0.5367435074026733,0.44633568000217777,0.6982368180860727,-0.5596872400064433,0.41788274341801684,0.6982368180860727,-0.5812395019460295,0.38839088449121306,0.6982368180860727,-0.6013467109024291,0.35793342462985916,0.6982368180860727,-0.6199588771860416,0.32658608587811383,0.6982368180860727,-0.6370297280189126,0.29442680265926785,0.6982368180860727,-0.6525168225761517,0.2615355280184892,0.6982368180860727,-0.6663816575005096,0.22799403484695524,0.6982368180860727,-0.6785897626277911,0.19388571258156026,0.6982368180860727,-0.6891107866851143,0.15929535988563673,0.6982368180860727,-0.6979185727489575,0.12430897382611727,0.6982368180860727,-0.7049912232753923,0.0890135360712763,0.6982368180860727,-0.710311154540828,0.053496796640597526,0.6982368180860727,-0.7138651403579187,0.017847055744399636,0.6982368180860727,-0.7156443449579496,-0.01784705574439955,0.6982368180860727,-0.7156443449579496,-0.053496796640597435,0.6982368180860727,-0.7138651403579187,-0.08901353607127621,0.6982368180860727,-0.710311154540828,-0.1243089738261172,0.6982368180860727,-0.7049912232753923,-0.15929535988563664,0.6982368180860727,-0.6979185727489575,-0.19388571258156012,0.6982368180860727,-0.6891107866851143,-0.22799403484695516,0.6982368180860727,-0.6785897626277911,-0.2615355280184891,0.6982368180860727,-0.6663816575005097,-0.2944268026592677,0.6982368180860727,-0.6525168225761517,-0.3265860858781135,0.6982368180860727,-0.6370297280189127,-0.3579334246298594,0.6982368180860727,-0.6199588771860415,-0.38839088449121306,0.6982368180860727,-0.6013467109024292,-0.4178827434180166,0.6982368180860727,-0.5812395019460296,-0.44633568000217766,0.6982368180860727,-0.5596872400064433,-0.4736789557602413,0.6982368180860727,-0.5367435074026735,-0.49984459100040807,0.6982368180860727,-0.5124653458690365,-0.5247675338307567,0.6982368180860727,-0.4869131147404247,-0.5483858218884983,0.6982368180860727,-0.4601503408894887,-0.5706407363881733,0.6982368180860727,-0.4322435607888217,-0.5914769481058028,0.6982368180860727,-0.40326215509080693,-0.6108426549360586,0.6982368180860727,-0.3732781761363818,-0.6286897106804582,0.6982368180860727,-0.34236616882156773,-0.6449737447464002,0.6982368180860727,-0.31060298526711205,-0.6596542724594495,0.6982368180860727,-0.27806759375200624,-0.6726947957146155,0.6982368180860727,-0.2448408823859039,-0.6840628937163933,0.6982368180860727,-0.21100545800853307,-0.6937303035819683,0.6982368180860727,-0.17664544081607797,-0.7016729906071967,0.6982368180860727,-0.14184625522511776,-0.7078712080206669,0.6982368180860727,-0.1066944174940665,-0.7123095460772846,0.6982368180860727,-0.0712773206301223,-0.7149769703693267,0.6982368180860727,-0.03568301711648369,-0.7158668492597184,0.6982368180860727,-8.76684045561615e-17,-0.7149769703693267,0.6982368180860727,0.03568301711648351,-0.7123095460772846,0.6982368180860727,0.07127732063012213,-0.7078712080206669,0.6982368180860727,0.10669441749406632,-0.7016729906071968,0.6982368180860727,0.14184625522511757,-0.6937303035819684,0.6982368180860727,0.1766454408160778,-0.6840628937163935,0.6982368180860727,0.21100545800853257,-0.6726947957146157,0.6982368180860727,0.24484088238590374,-0.6596542724594495,0.6982368180860727,0.2780675937520064,-0.6449737447464002,0.6982368180860727,0.3106029852671119,-0.6286897106804582,0.6982368180860727,0.34236616882156756,-0.6108426549360587,0.6982368180860727,0.37327817613638165,-0.5914769481058029,0.6982368180860727,0.40326215509080676,-0.5706407363881734,0.6982368180860727,0.43224356078882153,-0.5483858218884984,0.6982368180860727,0.46015034088948853,-0.5247675338307569,0.6982368180860727,0.4869131147404246,-0.49984459100040823,0.6982368180860727,0.5124653458690364,-0.4736789557602417,0.6982368180860727,0.5367435074026732,-0.44633568000217777,0.6982368180860727,0.5596872400064432,-0.4178827434180173,0.6982368180860727,0.5812395019460291,-0.3883908844912129,0.6982368180860727,0.6013467109024292,-0.35793342462985894,0.6982368180860727,0.6199588771860417,-0.32658608587811394,0.6982368180860727,0.6370297280189124,-0.2944268026592678,0.6982368180860727,0.6525168225761517,-0.2615355280184897,0.6982368180860727,0.6663816575005095,-0.22799403484695566,0.6982368180860727,0.6785897626277909,-0.19388571258155984,0.6982368180860727,0.6891107866851144,-0.1592953598856368,0.6982368180860727,0.6979185727489575,-0.12430897382611722,0.6982368180860727,0.7049912232753923,-0.0890135360712767,0.6982368180860727,0.7103111545408279,-0.05349679664059776,0.6982368180860727,0.7138651403579187,-0.017847055744399723,0.6982368180860727,0.7156443449579496,0.01784705574439946,0.6982368180860727,0.7156443449579496,0.053496796640597505,0.6982368180860727,0.7138651403579187,0.08901353607127581,0.6982368180860727,0.710311154540828,0.12430897382611697,0.6982368180860727,0.7049912232753924,0.15929535988563656,0.6982368180860727,0.6979185727489575,0.1938857125815602,0.6982368180860727,0.6891107866851143,0.22799403484695538,0.6982368180860727,0.678589762627791,0.2615355280184889,0.6982368180860727,0.6663816575005097,0.2944268026592676,0.6982368180860727,0.6525168225761517,0.3265860858781137,0.6982368180860727,0.6370297280189127,0.3579334246298593,0.6982368180860727,0.6199588771860415,0.3883908844912132,0.6982368180860727,0.601346710902429,0.41788274341801657,0.6982368180860727,0.5812395019460296,0.4463356800021776,0.6982368180860727,0.5596872400064433,0.473678955760241,0.6982368180860727,0.5367435074026737,0.49984459100040823,0.6982368180860727,0.5124653458690364,0.5247675338307569,0.6982368180860727,0.4869131147404246,0.5483858218884983,0.6982368180860727,0.46015034088948875,0.5706407363881733,0.6982368180860727,0.43224356078882176,0.5914769481058025,0.6982368180860727,0.40326215509080726,0.6108426549360587,0.6982368180860727,0.37327817613638165,0.628689710680458,0.6982368180860727,0.34236616882156806,0.6449737447464002,0.6982368180860727,0.31060298526711216,0.6596542724594495,0.6982368180860727,0.27806759375200635,0.6726947957146154,0.6982368180860727,0.2448408823859043,0.6840628937163933,0.6982368180860727,0.21100545800853313,0.6937303035819683,0.6982368180860727,0.17664544081607805,0.7016729906071967,0.6982368180860727,0.14184625522511782,0.7078712080206669,0.6982368180860727,0.10669441749406626,0.7123095460772845,0.6982368180860727,0.07127732063012271,0.7149769703693267,0.6982368180860727,0.03568301711648377,0.7158668492597184,0.6982368180860727,0.0,0.6801727377709192,0.7330518718298265,0.0,0.6793272294731287,0.7330518718298265,-0.033903812516452594,0.6767928066466056,0.7330518718298265,-0.06772333481303153,0.6725757702658257,0.7330518718298265,-0.10137448622863064,0.6666866045476771,0.7330518718298265,-0.13477360469868285,0.6591399508860447,0.7330518718298265,-0.16783765475223453,0.6499545714509982,0.7330518718298265,-0.20048443395120952,0.6391533025430816,0.7330518718298265,-0.23263277725862228,0.6267629978186708,0.7330518718298265,-0.2642027588276484,0.6128144615275541,0.7330518718298265,-0.2951158907098728,0.5973423719287125,0.7330518718298265,-0.32529531798869665,0.5803851950747048,0.7330518718298265,-0.35466600985276925,0.5619850891789989,0.7330518718298265,-0.38315494613440704,0.5421877998040122,0.7330518718298265,-0.41069129884923544,0.5210425461304345,0.7330518718298265,-0.43720660828571845,0.49860189859058995,0.7330518718298265,-0.46263495320678777,0.4749216481700595,0.7330518718298265,-0.48691311474042465,0.4500606677024997,0.7330518718298265,-0.509980733551737,0.4240807655025028,0.7330518718298265,-0.5317804599057763,0.39704653170038723,0.7330518718298265,-0.5522580962480144,0.36902517766095716,0.7330518718298265,-0.571362731948004,0.34008636888545957,0.7330518718298265,-0.5890468698712275,0.3103020518121717,0.7330518718298265,-0.6052665444644566,0.27974627494621856,0.7330518718298265,-0.6199814310610459,0.248495004763323,0.7330518718298265,-0.6331549461344069,0.21662593684517742,0.7330518718298265,-0.64475433825042,0.18421830271598527,0.7330518718298265,-0.6547507694926591,0.1513526728604083,0.7330518718298265,-0.6631193871579973,0.11811075641264705,0.7330518718298265,-0.6698393855443407,0.08457519801465864,0.7330518718298265,-0.6748940576768838,0.05082937234855529,0.7330518718298265,-0.6782708368442798,0.016957176854007973,0.7330518718298265,-0.6799613278414659,-0.01695717685400789,0.7330518718298265,-0.6799613278414659,-0.05082937234855521,0.7330518718298265,-0.6782708368442798,-0.08457519801465856,0.7330518718298265,-0.6748940576768838,-0.11811075641264698,0.7330518718298265,-0.6698393855443407,-0.15135267286040824,0.7330518718298265,-0.6631193871579973,-0.18421830271598513,0.7330518718298265,-0.6547507694926592,-0.21662593684517736,0.7330518718298265,-0.64475433825042,-0.24849500476332292,0.7330518718298265,-0.633154946134407,-0.27974627494621834,0.7330518718298265,-0.619981431061046,-0.31030205181217135,0.7330518718298265,-0.6052665444644567,-0.3400863688854598,0.7330518718298265,-0.5890468698712275,-0.36902517766095716,0.7330518718298265,-0.571362731948004,-0.397046531700387,0.7330518718298265,-0.5522580962480145,-0.42408076550250273,0.7330518718298265,-0.5317804599057764,-0.45006066770249953,0.7330518718298265,-0.5099807335517372,-0.4749216481700594,0.7330518718298265,-0.4869131147404247,-0.49860189859058995,0.7330518718298265,-0.46263495320678777,-0.5210425461304344,0.7330518718298265,-0.4372066082857186,-0.5421877998040122,0.7330518718298265,-0.41069129884923544,-0.5619850891789988,0.7330518718298265,-0.38315494613440715,-0.5803851950747047,0.7330518718298265,-0.3546660098527693,-0.5973423719287126,0.7330518718298265,-0.3252953179886966,-0.612814461527554,0.7330518718298265,-0.2951158907098729,-0.6267629978186708,0.7330518718298265,-0.2642027588276483,-0.6391533025430814,0.7330518718298265,-0.2326327772586224,-0.6499545714509981,0.7330518718298265,-0.20048443395120977,-0.6591399508860446,0.7330518718298265,-0.16783765475223467,-0.6666866045476771,0.7330518718298265,-0.13477360469868288,-0.6725757702658257,0.7330518718298265,-0.10137448622863084,-0.6767928066466056,0.7330518718298265,-0.06772333481303157,-0.6793272294731287,0.7330518718298265,-0.03390381251645281,-0.6801727377709192,0.7330518718298265,-8.329713661784483e-17,-0.6793272294731287,0.7330518718298265,0.03390381251645264,-0.6767928066466056,0.7330518718298265,0.0677233348130314,-0.6725757702658257,0.7330518718298265,0.10137448622863067,-0.6666866045476773,0.7330518718298265,0.13477360469868271,-0.6591399508860447,0.7330518718298265,0.16783765475223453,-0.6499545714509983,0.7330518718298265,0.20048443395120932,-0.6391533025430816,0.7330518718298265,0.23263277725862225,-0.6267629978186708,0.7330518718298265,0.26420275882764843,-0.6128144615275541,0.7330518718298265,0.2951158907098727,-0.5973423719287126,0.7330518718298265,0.32529531798869643,-0.5803851950747048,0.7330518718298265,0.35466600985276914,-0.5619850891789989,0.7330518718298265,0.38315494613440704,-0.5421877998040123,0.7330518718298265,0.4106912988492353,-0.5210425461304345,0.7330518718298265,0.43720660828571845,-0.4986018985905901,0.7330518718298265,0.4626349532067876,-0.47492164817005955,0.7330518718298265,0.4869131147404246,-0.4500606677024999,0.7330518718298265,0.5099807335517369,-0.4240807655025028,0.7330518718298265,0.5317804599057762,-0.3970465317003876,0.7330518718298265,0.5522580962480141,-0.369025177660957,0.7330518718298265,0.571362731948004,-0.34008636888545934,0.7330518718298265,0.5890468698712276,-0.31030205181217174,0.7330518718298265,0.6052665444644565,-0.2797462749462185,0.7330518718298265,0.6199814310610459,-0.24849500476332348,0.7330518718298265,0.6331549461344068,-0.2166259368451778,0.7330518718298265,0.6447543382504198,-0.18421830271598488,0.7330518718298265,0.6547507694926593,-0.1513526728604084,0.7330518718298265,0.6631193871579973,-0.118110756412647,0.7330518718298265,0.6698393855443407,-0.08457519801465901,0.7330518718298265,0.6748940576768837,-0.05082937234855552,0.7330518718298265,0.6782708368442798,-0.016957176854008056,0.7330518718298265,0.6799613278414659,0.016957176854007806,0.7330518718298265,0.6799613278414659,0.05082937234855527,0.7330518718298265,0.6782708368442798,0.08457519801465817,0.7330518718298265,0.6748940576768838,0.11811075641264675,0.7330518718298265,0.6698393855443409,0.15135267286040816,0.7330518718298265,0.6631193871579973,0.18421830271598522,0.7330518718298265,0.6547507694926591,0.21662593684517753,0.7330518718298265,0.6447543382504198,0.2484950047633227,0.7330518718298265,0.633154946134407,0.2797462749462183,0.7330518718298265,0.619981431061046,0.3103020518121715,0.7330518718298265,0.6052665444644566,0.3400863688854597,0.7330518718298265,0.5890468698712275,0.3690251776609573,0.7330518718298265,0.5713627319480039,0.3970465317003869,0.7330518718298265,0.5522580962480146,0.4240807655025027,0.7330518718298265,0.5317804599057764,0.45006066770249925,0.7330518718298265,0.5099807335517375,0.47492164817005955,0.7330518718298265,0.4869131147404246,0.4986018985905901,0.7330518718298265,0.4626349532067876,0.5210425461304343,0.7330518718298265,0.43720660828571867,0.5421877998040122,0.7330518718298265,0.41069129884923555,0.5619850891789986,0.7330518718298265,0.3831549461344075,0.5803851950747049,0.7330518718298265,0.35466600985276914,0.5973423719287124,0.7330518718298265,0.32529531798869693,0.612814461527554,0.7330518718298265,0.29511589070987293,0.6267629978186708,0.7330518718298265,0.2642027588276484,0.6391533025430813,0.7330518718298265,0.23263277725862277,0.6499545714509981,0.7330518718298265,0.20048443395120985,0.6591399508860446,0.7330518718298265,0.16783765475223475,0.6666866045476771,0.7330518718298265,0.13477360469868296,0.6725757702658257,0.7330518718298265,0.10137448622863061,0.6767928066466056,0.7330518718298265,0.06772333481303194,0.6793272294731287,0.7330518718298265,0.03390381251645289,0.6801727377709192,0.7330518718298265,0.0,0.6427876096865395,0.766044443118978,0.0,0.6419885740481985,0.766044443118978,-0.03204031769654808,0.6395934536616704,0.766044443118978,-0.06400097811496104,0.6356082031736162,0.766044443118978,-0.09580252201764815,0.6300427305446742,0.766044443118978,-0.12736588575574753,0.6229108724167075,0.766044443118978,-0.15861259783381465,0.6142303597127332,0.766044443118978,-0.18946497400232415,0.6040227735550538,0.766044443118978,-0.21984631039295424,0.5923134916111865,0.766044443118978,-0.24968107421648897,0.5791316250009836,0.766044443118978,-0.27889509154923325,0.564509945921801,0.766044443118978,-0.30741573174107445,0.5484848061716533,0.766044443118978,-0.3351720879867221,0.5310960467729146,0.766044443118978,-0.36209515361119826,0.5123868989212605,0.766044443118978,-0.38811799363130406,0.4924038765061041,0.766044443118978,-0.41317591116653485,0.47119666046973757,0.766044443118978,-0.43720660828571867,0.44881797529268186,0.766044443118978,-0.4601503408894887,0.425323457912321,0.766044443118978,-0.48195006724352796,0.4007715194007095,0.766044443118978,-0.5025515897933085,0.3752231997454434,0.766044443118978,-0.5219036899077513,0.348742016094634,0.766044443118978,-0.5399582552168131,0.3213938048432697,0.766044443118978,-0.5566703992264196,0.2932465579535646,0.766044443118978,-0.5719985729133611,0.26437025391622715,0.766044443118978,-0.5859046680227122,0.2348366837729068,0.766044443118978,-0.5983541118109567,0.20471927263235315,0.766044443118978,-0.6093159529992755,0.17409289712402562,0.766044443118978,-0.6187629387233033,0.1430336992429957,0.766044443118978,-0.6266715822880429,0.11161889704894973,0.766044443118978,-0.6330222215594892,0.07992659268992611,0.766044443118978,-0.637799067847793,0.04803557822806913,0.766044443118978,-0.6409902451604315,0.016025139750147924,0.766044443118978,-0.6425878197277965,-0.016025139750147847,0.766044443118978,-0.6425878197277965,-0.04803557822806905,0.766044443118978,-0.6409902451604315,-0.07992659268992604,0.766044443118978,-0.637799067847793,-0.11161889704894966,0.766044443118978,-0.6330222215594892,-0.1430336992429956,0.766044443118978,-0.6266715822880429,-0.17409289712402554,0.766044443118978,-0.6187629387233033,-0.20471927263235307,0.766044443118978,-0.6093159529992755,-0.23483668377290673,0.766044443118978,-0.5983541118109567,-0.2643702539162269,0.766044443118978,-0.5859046680227123,-0.29324655795356425,0.766044443118978,-0.5719985729133613,-0.3213938048432699,0.766044443118978,-0.5566703992264195,-0.348742016094634,0.766044443118978,-0.5399582552168132,-0.37522319974544316,0.766044443118978,-0.5219036899077513,-0.40077151940070943,0.766044443118978,-0.5025515897933085,-0.42532345791232085,0.766044443118978,-0.4819500672435281,-0.4488179752926818,0.766044443118978,-0.46015034088948875,-0.47119666046973757,0.766044443118978,-0.43720660828571867,-0.49240387650610407,0.766044443118978,-0.413175911166535,-0.5123868989212605,0.766044443118978,-0.38811799363130406,-0.5310960467729144,0.766044443118978,-0.3620951536111984,-0.5484848061716532,0.766044443118978,-0.33517208798672216,-0.5645099459218011,0.766044443118978,-0.3074157317410744,-0.5791316250009835,0.766044443118978,-0.27889509154923336,-0.5923134916111866,0.766044443118978,-0.24968107421648889,-0.6040227735550537,0.766044443118978,-0.21984631039295432,-0.6142303597127331,0.766044443118978,-0.1894649740023244,-0.6229108724167074,0.766044443118978,-0.1586125978338148,-0.6300427305446742,0.766044443118978,-0.12736588575574756,-0.6356082031736162,0.766044443118978,-0.09580252201764831,-0.6395934536616704,0.766044443118978,-0.06400097811496108,-0.6419885740481985,0.766044443118978,-0.03204031769654829,-0.6427876096865395,0.766044443118978,-7.871877887341988e-17,-0.6419885740481985,0.766044443118978,0.03204031769654813,-0.6395934536616704,0.766044443118978,0.06400097811496092,-0.6356082031736162,0.766044443118978,0.09580252201764816,-0.6300427305446742,0.766044443118978,0.1273658857557474,-0.6229108724167075,0.766044443118978,0.15861259783381465,-0.6142303597127332,0.766044443118978,0.18946497400232398,-0.6040227735550538,0.766044443118978,0.2198463103929542,-0.5923134916111865,0.766044443118978,0.249681074216489,-0.5791316250009836,0.766044443118978,0.2788950915492332,-0.5645099459218011,0.766044443118978,0.3074157317410742,-0.5484848061716533,0.766044443118978,0.33517208798672204,-0.5310960467729146,0.766044443118978,0.36209515361119826,-0.5123868989212605,0.766044443118978,0.38811799363130395,-0.4924038765061041,0.766044443118978,0.41317591116653485,-0.47119666046973774,0.766044443118978,0.43720660828571856,-0.4488179752926819,0.766044443118978,0.46015034088948864,-0.42532345791232123,0.766044443118978,0.48195006724352785,-0.4007715194007095,0.766044443118978,0.5025515897933084,-0.37522319974544377,0.766044443118978,0.5219036899077509,-0.3487420160946339,0.766044443118978,0.5399582552168132,-0.3213938048432695,0.766044443118978,0.5566703992264196,-0.29324655795356463,0.766044443118978,0.5719985729133611,-0.2643702539162271,0.766044443118978,0.5859046680227122,-0.23483668377290726,0.766044443118978,0.5983541118109564,-0.2047192726323535,0.766044443118978,0.6093159529992753,-0.1740928971240253,0.766044443118978,0.6187629387233035,-0.14303369924299578,0.766044443118978,0.6266715822880429,-0.11161889704894967,0.766044443118978,0.6330222215594892,-0.07992659268992647,0.766044443118978,0.6377990678477929,-0.048035578228069344,0.766044443118978,0.6409902451604315,-0.016025139750148003,0.766044443118978,0.6425878197277965,0.016025139750147764,0.766044443118978,0.6425878197277965,0.048035578228069115,0.766044443118978,0.6409902451604315,0.07992659268992568,0.766044443118978,0.637799067847793,0.11161889704894944,0.766044443118978,0.6330222215594892,0.14303369924299553,0.766044443118978,0.6266715822880429,0.1740928971240256,0.766044443118978,0.6187629387233033,0.20471927263235326,0.766044443118978,0.6093159529992754,0.23483668377290653,0.766044443118978,0.5983541118109568,0.26437025391622687,0.766044443118978,0.5859046680227123,0.29324655795356447,0.766044443118978,0.5719985729133612,0.3213938048432698,0.766044443118978,0.5566703992264195,0.3487420160946342,0.766044443118978,0.539958255216813,0.3752231997454431,0.766044443118978,0.5219036899077514,0.4007715194007093,0.766044443118978,0.5025515897933085,0.42532345791232057,0.766044443118978,0.48195006724352835,0.4488179752926819,0.766044443118978,0.46015034088948864,0.47119666046973774,0.766044443118978,0.43720660828571856,0.492403876506104,0.766044443118978,0.41317591116653507,0.5123868989212604,0.766044443118978,0.38811799363130417,0.5310960467729142,0.766044443118978,0.3620951536111987,0.5484848061716533,0.766044443118978,0.33517208798672204,0.5645099459218009,0.766044443118978,0.3074157317410747,0.5791316250009835,0.766044443118978,0.2788950915492334,0.5923134916111865,0.766044443118978,0.24968107421648897,0.6040227735550536,0.766044443118978,0.21984631039295469,0.6142303597127331,0.766044443118978,0.18946497400232448,0.6229108724167074,0.766044443118978,0.15861259783381487,0.6300427305446742,0.766044443118978,0.12736588575574762,0.6356082031736162,0.766044443118978,0.0958025220176481,0.6395934536616703,0.766044443118978,0.06400097811496144,0.6419885740481985,0.766044443118978,0.032040317696548366,0.6427876096865395,0.766044443118978,0.0,0.6038044103254773,0.7971325072229225,0.0,0.6030538338750806,0.7971325072229225,-0.030097165598508415,0.6008039705752086,0.7971325072229225,-0.06011950490116466,0.5970604139405182,0.7971325072229225,-0.08999237764207783,0.591832471042646,0.7971325072229225,-0.11964151515277983,0.5851331393713621,0.7971325072229225,-0.14899320500583701,0.5769790745207625,0.7971325072229225,-0.17797447427555924,0.5673905487808377,0.7971325072229225,-0.20651327096019062,0.556391400737366,0.7971325072229225,-0.23453864311453895,0.544008976005433,0.7971325072229225,-0.2619809152476904,0.5302740592439237,0.7971325072229225,-0.2887718615472584,0.5152207976200104,0.7971325072229225,-0.31484487549950313,0.4988866159139144,0.7971325072229225,-0.34013513548361896,0.48131212347500596,0.7971325072229225,-0.3645797659284974,0.4625410132605631,0.7971325072229225,-0.3881179936313039,0.44261995320819564,0.7971325072229225,-0.41069129884923544,0.42159847021199875,0.7971325072229225,-0.4322435607888216,0.3995288269908912,0.7971325072229225,-0.45272119713105974,0.3764658921552613,0.7971325072229225,-0.47207329724550245,0.35246700379495605,0.7971325072229225,-0.49025174876210675,0.3275918269277557,0.7971325072229225,-0.5072113571861662,0.3019022051627386,0.7971325072229225,-0.5229099582589464,0.275462006947325,0.7971325072229225,-0.5373085227846746,0.24833696678025274,0.7971325072229225,-0.5503712536632713,0.22059452178525696,0.7971325072229225,-0.5620656748875803,0.19230364405175485,0.7971325072229225,-0.5723627122838396,0.16353466915936343,0.7971325072229225,-0.5812367657946587,0.13435912131256691,0.7971325072229225,-0.5886657731247947,0.10484953552027464,0.7971325072229225,-0.5946312645914945,0.07507927726236015,0.7971325072229225,-0.5991184090430363,0.04512236009151871,0.7971325072229225,-0.6021160507313092,0.015053261623913437,0.7971325072229225,-0.6036167370467616,-0.015053261623913364,0.7971325072229225,-0.6036167370467616,-0.04512236009151863,0.7971325072229225,-0.6021160507313092,-0.07507927726236009,0.7971325072229225,-0.5991184090430363,-0.10484953552027457,0.7971325072229225,-0.5946312645914945,-0.13435912131256686,0.7971325072229225,-0.5886657731247947,-0.16353466915936335,0.7971325072229225,-0.5812367657946587,-0.19230364405175476,0.7971325072229225,-0.5723627122838396,-0.22059452178525688,0.7971325072229225,-0.5620656748875804,-0.24833696678025258,0.7971325072229225,-0.5503712536632714,-0.2754620069473247,0.7971325072229225,-0.5373085227846748,-0.30190220516273875,0.7971325072229225,-0.5229099582589463,-0.3275918269277557,0.7971325072229225,-0.5072113571861663,-0.3524670037949558,0.7971325072229225,-0.4902517487621068,-0.3764658921552612,0.7971325072229225,-0.4720732972455025,-0.3995288269908911,0.7971325072229225,-0.45272119713105985,-0.4215984702119987,0.7971325072229225,-0.4322435607888217,-0.44261995320819564,0.7971325072229225,-0.41069129884923544,-0.46254101326056307,0.7971325072229225,-0.388117993631304,-0.48131212347500596,0.7971325072229225,-0.3645797659284974,-0.4988866159139143,0.7971325072229225,-0.34013513548361907,-0.5152207976200104,0.7971325072229225,-0.3148448754995032,-0.5302740592439238,0.7971325072229225,-0.28877186154725837,-0.544008976005433,0.7971325072229225,-0.26198091524769046,-0.5563914007373661,0.7971325072229225,-0.2345386431145389,-0.5673905487808377,0.7971325072229225,-0.20651327096019073,-0.5769790745207625,0.7971325072229225,-0.17797447427555946,-0.5851331393713621,0.7971325072229225,-0.14899320500583715,-0.591832471042646,0.7971325072229225,-0.11964151515277985,-0.5970604139405182,0.7971325072229225,-0.089992377642078,-0.6008039705752086,0.7971325072229225,-0.0601195049011647,-0.6030538338750806,0.7971325072229225,-0.030097165598508606,-0.6038044103254773,0.7971325072229225,-7.394471384161508e-17,-0.6030538338750806,0.7971325072229225,0.030097165598508457,-0.6008039705752086,0.7971325072229225,0.06011950490116455,-0.5970604139405182,0.7971325072229225,0.08999237764207785,-0.5918324710426461,0.7971325072229225,0.1196415151527797,-0.5851331393713621,0.7971325072229225,0.14899320500583701,-0.5769790745207626,0.7971325072229225,0.17797447427555904,-0.5673905487808377,0.7971325072229225,0.2065132709601906,-0.556391400737366,0.7971325072229225,0.23453864311453898,-0.544008976005433,0.7971325072229225,0.26198091524769035,-0.5302740592439238,0.7971325072229225,0.28877186154725826,-0.5152207976200104,0.7971325072229225,0.314844875499503,-0.4988866159139144,0.7971325072229225,0.34013513548361896,-0.481312123475006,0.7971325072229225,0.3645797659284973,-0.4625410132605631,0.7971325072229225,0.3881179936313039,-0.44261995320819575,0.7971325072229225,0.41069129884923533,-0.4215984702119988,0.7971325072229225,0.43224356078882153,-0.39952882699089143,0.7971325072229225,0.4527211971310596,-0.3764658921552613,0.7971325072229225,0.4720732972455024,-0.3524670037949564,0.7971325072229225,0.4902517487621065,-0.3275918269277555,0.7971325072229225,0.5072113571861663,-0.3019022051627384,0.7971325072229225,0.5229099582589465,-0.27546200694732503,0.7971325072229225,0.5373085227846746,-0.24833696678025272,0.7971325072229225,0.5503712536632713,-0.22059452178525737,0.7971325072229225,0.5620656748875802,-0.19230364405175518,0.7971325072229225,0.5723627122838395,-0.1635346691593631,0.7971325072229225,0.5812367657946588,-0.134359121312567,0.7971325072229225,0.5886657731247947,-0.10484953552027458,0.7971325072229225,0.5946312645914945,-0.0750792772623605,0.7971325072229225,0.5991184090430363,-0.04512236009151891,0.7971325072229225,0.6021160507313092,-0.01505326162391351,0.7971325072229225,0.6036167370467616,0.015053261623913287,0.7971325072229225,0.6036167370467616,0.045122360091518696,0.7971325072229225,0.6021160507313092,0.07507927726235974,0.7971325072229225,0.5991184090430364,0.10484953552027436,0.7971325072229225,0.5946312645914946,0.13435912131256678,0.7971325072229225,0.5886657731247947,0.1635346691593634,0.7971325072229225,0.5812367657946587,0.19230364405175493,0.7971325072229225,0.5723627122838395,0.22059452178525668,0.7971325072229225,0.5620656748875804,0.2483369667802525,0.7971325072229225,0.5503712536632714,0.27546200694732487,0.7971325072229225,0.5373085227846747,0.3019022051627387,0.7971325072229225,0.5229099582589463,0.3275918269277558,0.7971325072229225,0.5072113571861662,0.35246700379495577,0.7971325072229225,0.49025174876210686,0.37646589215526116,0.7971325072229225,0.4720732972455025,0.3995288269908908,0.7971325072229225,0.45272119713106007,0.4215984702119988,0.7971325072229225,0.43224356078882153,0.44261995320819575,0.7971325072229225,0.41069129884923533,0.462541013260563,0.7971325072229225,0.38811799363130406,0.4813121234750059,0.7971325072229225,0.36457976592849745,0.49888661591391414,0.7971325072229225,0.34013513548361934,0.5152207976200105,0.7971325072229225,0.314844875499503,0.5302740592439236,0.7971325072229225,0.2887718615472587,0.544008976005433,0.7971325072229225,0.2619809152476905,0.556391400737366,0.7971325072229225,0.23453864311453895,0.5673905487808376,0.7971325072229225,0.20651327096019106,0.5769790745207625,0.7971325072229225,0.1779744742755595,0.5851331393713621,0.7971325072229225,0.14899320500583724,0.591832471042646,0.7971325072229225,0.11964151515277992,0.5970604139405182,0.7971325072229225,0.0899923776420778,0.6008039705752085,0.7971325072229225,0.060119504901165036,0.6030538338750806,0.7971325072229225,0.03009716559850868,0.6038044103254773,0.7971325072229225,0.0,0.5633200580636222,0.8262387743159948,0.0,0.5626198068524878,0.8262387743159948,-0.028079187204616596,0.560520794153929,0.8262387743159948,-0.05608856512562527,0.5570282384442389,0.8262387743159948,-0.08395849803629642,0.5521508227672067,0.8262387743159948,-0.11161969689216744,0.5459006731467017,0.8262387743159948,-0.13900339159452452,0.5382933284394671,0.8262387743159948,-0.16604150196370365,0.5293477017030752,0.8262387743159948,-0.19266680699714409,0.5190860331750846,0.8262387743159948,-0.21881311199139228,0.5075338349803067,0.8262387743159948,-0.24441541311256434,0.49471982770364276,0.8262387743159948,-0.2694100590061193,0.4806758689861853,0.8262387743159948,-0.29373490904415545,0.4654368743221023,0.8262387743159948,-0.3173294878168026,0.44904073025321745,0.8262387743159948,-0.34013513548361907,0.4315282001770978,0.8262387743159948,-0.3620951536111982,0.4129428230028247,0.8262387743159948,-0.38315494613440715,0.3933308049064054,0.8262387743159948,-0.4032621550908069,0.3727409044549393,0.8262387743159948,-0.42236679079079653,0.3512243113851381,0.8262387743159948,-0.44042135609985833,0.3288345193375739,0.8262387743159948,-0.45738096452391785,0.3056271928630609,0.8262387743159948,-0.4732034518042404,0.28166002903181103,0.8262387743159948,-0.48784948074442186,0.2569926139894271,0.8262387743159948,-0.501282639008857,0.23168627481635512,0.8262387743159948,-0.5134695296495432,0.20580392705910083,0.8262387743159948,-0.5243798541361547,0.1794099183122674,0.8262387743159948,-0.533986487682964,0.15256986824029628,0.8262387743159948,-0.5422655466853313,0.1253505054366425,0.8262387743159948,-0.5491964480981109,0.09781950152597742,0.8262387743159948,-0.5547619606083423,0.0700453029218673,0.8262387743159948,-0.5589482474750114,0.04209696065820451,0.8262387743159948,-0.5617449009293669,0.014043958717457568,0.8262387743159948,-0.5631449680502748,-0.014043958717457498,0.8262387743159948,-0.5631449680502748,-0.04209696065820444,0.8262387743159948,-0.5617449009293669,-0.07004530292186723,0.8262387743159948,-0.5589482474750114,-0.09781950152597736,0.8262387743159948,-0.5547619606083423,-0.12535050543664242,0.8262387743159948,-0.5491964480981109,-0.1525698682402962,0.8262387743159948,-0.5422655466853314,-0.17940991831226735,0.8262387743159948,-0.533986487682964,-0.20580392705910078,0.8262387743159948,-0.5243798541361548,-0.23168627481635498,0.8262387743159948,-0.5134695296495432,-0.2569926139894268,0.8262387743159948,-0.5012826390088571,-0.2816600290318112,0.8262387743159948,-0.4878494807444218,-0.3056271928630609,0.8262387743159948,-0.47320345180424045,-0.32883451933757374,0.8262387743159948,-0.45738096452391797,-0.35122431138513804,0.8262387743159948,-0.44042135609985844,-0.3727409044549392,0.8262387743159948,-0.42236679079079664,-0.3933308049064053,0.8262387743159948,-0.40326215509080693,-0.4129428230028247,0.8262387743159948,-0.38315494613440715,-0.43152820017709775,0.8262387743159948,-0.3620951536111983,-0.44904073025321745,0.8262387743159948,-0.34013513548361907,-0.46543687432210223,0.8262387743159948,-0.3173294878168027,-0.48067586898618525,0.8262387743159948,-0.2937349090441555,-0.4947198277036428,0.8262387743159948,-0.2694100590061192,-0.5075338349803066,0.8262387743159948,-0.2444154131125644,-0.5190860331750847,0.8262387743159948,-0.21881311199139222,-0.5293477017030752,0.8262387743159948,-0.19266680699714417,-0.5382933284394671,0.8262387743159948,-0.16604150196370385,-0.5459006731467015,0.8262387743159948,-0.13900339159452463,-0.5521508227672067,0.8262387743159948,-0.11161969689216746,-0.5570282384442389,0.8262387743159948,-0.08395849803629657,-0.560520794153929,0.8262387743159948,-0.05608856512562531,-0.5626198068524878,0.8262387743159948,-0.028079187204616777,-0.5633200580636222,0.8262387743159948,-6.898681060031161e-17,-0.5626198068524878,0.8262387743159948,0.028079187204616638,-0.560520794153929,0.8262387743159948,0.05608856512562516,-0.5570282384442389,0.8262387743159948,0.08395849803629643,-0.5521508227672067,0.8262387743159948,0.11161969689216732,-0.5459006731467017,0.8262387743159948,0.13900339159452452,-0.5382933284394672,0.8262387743159948,0.1660415019637035,-0.5293477017030752,0.8262387743159948,0.19266680699714406,-0.5190860331750846,0.8262387743159948,0.21881311199139233,-0.5075338349803067,0.8262387743159948,0.24441541311256426,-0.4947198277036428,0.8262387743159948,0.2694100590061191,-0.4806758689861853,0.8262387743159948,0.2937349090441554,-0.4654368743221023,0.8262387743159948,0.3173294878168026,-0.4490407302532175,0.8262387743159948,0.34013513548361896,-0.4315282001770978,0.8262387743159948,0.3620951536111982,-0.4129428230028248,0.8262387743159948,0.38315494613440704,-0.39333080490640543,0.8262387743159948,0.4032621550908068,-0.3727409044549395,0.8262387743159948,0.42236679079079636,-0.3512243113851381,0.8262387743159948,0.4404213560998583,-0.32883451933757424,0.8262387743159948,0.45738096452391763,-0.3056271928630608,0.8262387743159948,0.47320345180424045,-0.28166002903181087,0.8262387743159948,0.4878494807444219,-0.25699261398942713,0.8262387743159948,0.501282639008857,-0.2316862748163551,0.8262387743159948,0.5134695296495432,-0.20580392705910122,0.8262387743159948,0.5243798541361546,-0.1794099183122677,0.8262387743159948,0.5339864876829639,-0.15256986824029597,0.8262387743159948,0.5422655466853314,-0.12535050543664256,0.8262387743159948,0.5491964480981109,-0.09781950152597738,0.8262387743159948,0.5547619606083423,-0.07004530292186761,0.8262387743159948,0.5589482474750113,-0.042096960658204695,0.8262387743159948,0.5617449009293669,-0.014043958717457635,0.8262387743159948,0.5631449680502748,0.014043958717457429,0.8262387743159948,0.5631449680502748,0.042096960658204494,0.8262387743159948,0.5617449009293669,0.0700453029218669,0.8262387743159948,0.5589482474750114,0.09781950152597717,0.8262387743159948,0.5547619606083424,0.12535050543664236,0.8262387743159948,0.5491964480981109,0.15256986824029625,0.8262387743159948,0.5422655466853313,0.1794099183122675,0.8262387743159948,0.5339864876829639,0.20580392705910058,0.8262387743159948,0.5243798541361548,0.23168627481635493,0.8262387743159948,0.5134695296495432,0.25699261398942697,0.8262387743159948,0.5012826390088571,0.28166002903181114,0.8262387743159948,0.4878494807444218,0.305627192863061,0.8262387743159948,0.4732034518042403,0.3288345193375737,0.8262387743159948,0.457380964523918,0.351224311385138,0.8262387743159948,0.44042135609985844,0.37274090445493896,0.8262387743159948,0.4223667907907968,0.39333080490640543,0.8262387743159948,0.4032621550908068,0.4129428230028248,0.8262387743159948,0.38315494613440704,0.4315282001770977,0.8262387743159948,0.3620951536111984,0.4490407302532174,0.8262387743159948,0.3401351354836191,0.465436874322102,0.8262387743159948,0.317329487816803,0.48067586898618536,0.8262387743159948,0.2937349090441554,0.49471982770364265,0.8262387743159948,0.2694100590061195,0.5075338349803066,0.8262387743159948,0.24441541311256446,0.5190860331750846,0.8262387743159948,0.21881311199139228,0.529347701703075,0.8262387743159948,0.19266680699714447,0.5382933284394671,0.8262387743159948,0.16604150196370393,0.5459006731467015,0.8262387743159948,0.13900339159452468,0.5521508227672067,0.8262387743159948,0.11161969689216751,0.5570282384442389,0.8262387743159948,0.08395849803629639,0.5605207941539289,0.8262387743159948,0.05608856512562562,0.5626198068524878,0.8262387743159948,0.028079187204616843,0.5633200580636222,0.8262387743159948,0.0,0.521435203379498,0.8532908816321557,0.0,0.52078701834247,0.8532908816321557,-0.025991399527116828,0.5188440747215177,0.8532908816321557,-0.05191818033974832,0.5156112029806169,0.8532908816321557,-0.07771588437571479,0.511096440548294,0.8532908816321557,-0.10332037447804102,0.5053110118353112,0.8532908816321557,-0.1286679938500339,0.4982693003290138,0.8532908816321557,-0.15369572431610923,0.48998881283371365,0.8532908816321557,-0.17834134299490512,0.4804901359460154,0.8532908816321557,-0.2025435769951693,0.469796884873294,0.8532908816321557,-0.22624225574982096,0.45793564472256787,0.8532908816321557,-0.24937846060946114,0.4449359044057343,0.8532908816321557,-0.27189467132341844,0.43082998332548794,0.8532908816321557,-0.2937349090441554,0.41565295102419375,0.8532908816321557,-0.3148448754995032,0.3994425399954786,0.8532908816321557,-0.335172087986722,0.3822390518753072,0.8532908816321557,-0.3546660098527693,0.36408525724576496,0.8532908816321557,-0.3732781761363817,0.3450262893006519,0.8532908816321557,-0.39096231405960513,0.32510953163725165,0.8532908816321557,-0.40767445806921154,0.3043845004532427,0.8532908816321557,-0.42337305914199164,0.28290272144162903,0.8532908816321557,-0.43801908808217305,0.26071760168974895,0.8532908816321557,-0.4515761325541507,0.23788429690084106,0.8532908816321557,-0.46401048760928987,0.21445957426827505,0.8532908816321557,-0.47529123948173996,0.1905016713433653,0.8532908816321557,-0.48539034244492635,0.16607015124764213,0.8532908816321557,-0.4942826885376462,0.14122575458954634,0.8532908816321557,-0.5019461699864156,0.11603024845370667,0.8532908816321557,-0.5083617341688785,0.09054627283823506,0.8532908816321557,-0.5135134309816272,0.06483718492182171,0.8532908816321557,-0.5173884524946725,0.038966901547806125,0.8532908816321557,-0.5199771647939764,0.012999740316833684,0.8532908816321557,-0.5212731319328796,-0.01299974031683362,0.8532908816321557,-0.5212731319328796,-0.038966901547806056,0.8532908816321557,-0.5199771647939764,-0.06483718492182165,0.8532908816321557,-0.5173884524946725,-0.090546272838235,0.8532908816321557,-0.5135134309816272,-0.11603024845370662,0.8532908816321557,-0.5083617341688785,-0.14122575458954625,0.8532908816321557,-0.5019461699864157,-0.16607015124764207,0.8532908816321557,-0.4942826885376462,-0.19050167134336524,0.8532908816321557,-0.4853903424449264,-0.21445957426827492,0.8532908816321557,-0.47529123948174,-0.2378842969008408,0.8532908816321557,-0.46401048760929,-0.2607176016897491,0.8532908816321557,-0.45157613255415063,-0.28290272144162903,0.8532908816321557,-0.4380190880821731,-0.30438450045324256,0.8532908816321557,-0.4233730591419917,-0.3251095316372516,0.8532908816321557,-0.4076744580692116,-0.34502628930065177,0.8532908816321557,-0.3909623140596053,-0.3640852572457649,0.8532908816321557,-0.37327817613638176,-0.3822390518753072,0.8532908816321557,-0.3546660098527693,-0.3994425399954785,0.8532908816321557,-0.3351720879867221,-0.41565295102419375,0.8532908816321557,-0.3148448754995032,-0.4308299833254879,0.8532908816321557,-0.2937349090441555,-0.4449359044057342,0.8532908816321557,-0.2718946713234185,-0.4579356447225679,0.8532908816321557,-0.2493784606094611,-0.46979688487329396,0.8532908816321557,-0.22624225574982104,-0.4804901359460155,0.8532908816321557,-0.20254357699516926,-0.4899888128337136,0.8532908816321557,-0.1783413429949052,-0.49826930032901373,0.8532908816321557,-0.15369572431610945,-0.5053110118353112,0.8532908816321557,-0.12866799385003405,-0.511096440548294,0.8532908816321557,-0.10332037447804103,-0.5156112029806169,0.8532908816321557,-0.07771588437571494,-0.5188440747215177,0.8532908816321557,-0.051918180339748356,-0.52078701834247,0.8532908816321557,-0.025991399527116994,-0.521435203379498,0.8532908816321557,-6.385739527814514e-17,-0.52078701834247,0.8532908816321557,0.025991399527116866,-0.5188440747215177,0.8532908816321557,0.051918180339748224,-0.5156112029806169,0.8532908816321557,0.0777158843757148,-0.5110964405482941,0.8532908816321557,0.10332037447804089,-0.5053110118353112,0.8532908816321557,0.1286679938500339,-0.49826930032901384,0.8532908816321557,0.1536957243161091,-0.48998881283371365,0.8532908816321557,0.1783413429949051,-0.4804901359460154,0.8532908816321557,0.20254357699516937,-0.469796884873294,0.8532908816321557,0.2262422557498209,-0.4579356447225679,0.8532908816321557,0.24937846060946098,-0.4449359044057343,0.8532908816321557,0.2718946713234184,-0.43082998332548794,0.8532908816321557,0.2937349090441554,-0.4156529510241938,0.8532908816321557,0.314844875499503,-0.3994425399954786,0.8532908816321557,0.335172087986722,-0.3822390518753073,0.8532908816321557,0.3546660098527692,-0.364085257245765,0.8532908816321557,0.37327817613638165,-0.34502628930065204,0.8532908816321557,0.390962314059605,-0.32510953163725165,0.8532908816321557,0.4076744580692115,-0.304384500453243,0.8532908816321557,0.4233730591419914,-0.2829027214416289,0.8532908816321557,0.4380190880821731,-0.2607176016897488,0.8532908816321557,0.45157613255415074,-0.23788429690084112,0.8532908816321557,0.4640104876092898,-0.21445957426827503,0.8532908816321557,0.47529123948173996,-0.19050167134336568,0.8532908816321557,0.48539034244492624,-0.1660701512476424,0.8532908816321557,0.49428268853764606,-0.14122575458954606,0.8532908816321557,0.5019461699864157,-0.11603024845370674,0.8532908816321557,0.5083617341688785,-0.09054627283823502,0.8532908816321557,0.5135134309816272,-0.06483718492182201,0.8532908816321557,0.5173884524946724,-0.0389669015478063,0.8532908816321557,0.5199771647939764,-0.012999740316833746,0.8532908816321557,0.5212731319328796,0.012999740316833555,0.8532908816321557,0.5212731319328796,0.03896690154780611,0.8532908816321557,0.5199771647939764,0.06483718492182136,0.8532908816321557,0.5173884524946725,0.09054627283823483,0.8532908816321557,0.5135134309816272,0.11603024845370655,0.8532908816321557,0.5083617341688785,0.1412257545895463,0.8532908816321557,0.5019461699864156,0.1660701512476422,0.8532908816321557,0.4942826885376461,0.19050167134336507,0.8532908816321557,0.48539034244492646,0.21445957426827486,0.8532908816321557,0.47529123948174,0.23788429690084095,0.8532908816321557,0.4640104876092899,0.26071760168974906,0.8532908816321557,0.45157613255415063,0.2829027214416292,0.8532908816321557,0.438019088082173,0.3043845004532425,0.8532908816321557,0.42337305914199175,0.32510953163725154,0.8532908816321557,0.4076744580692116,0.3450262893006515,0.8532908816321557,0.39096231405960546,0.364085257245765,0.8532908816321557,0.37327817613638165,0.3822390518753073,0.8532908816321557,0.3546660098527692,0.39944253999547846,0.8532908816321557,0.33517208798672216,0.4156529510241937,0.8532908816321557,0.31484487549950324,0.4308299833254877,0.8532908816321557,0.2937349090441557,0.44493590440573433,0.8532908816321557,0.2718946713234184,0.45793564472256776,0.8532908816321557,0.24937846060946134,0.46979688487329396,0.8532908816321557,0.2262422557498211,0.4804901359460154,0.8532908816321557,0.2025435769951693,0.4899888128337135,0.8532908816321557,0.1783413429949055,0.49826930032901373,0.8532908816321557,0.1536957243161095,0.5053110118353112,0.8532908816321557,0.1286679938500341,0.511096440548294,0.8532908816321557,0.10332037447804109,0.5156112029806169,0.8532908816321557,0.07771588437571476,0.5188440747215176,0.8532908816321557,0.05191818033974865,0.52078701834247,0.8532908816321557,0.025991399527117057,0.521435203379498,0.8532908816321557,0.0,0.478253978621318,0.8782215733702287,0.0,0.47765947124852787,0.8782215733702287,-0.023838993135131725,0.4758774271691582,0.8782215733702287,-0.04761871877720637,0.47291227682557285,0.8782215733702287,-0.07128005678149292,0.4687713920487216,0.8782215733702287,-0.0947641813335813,0.46346506773060636,0.8782215733702287,-0.11801270719962575,0.4570064962295578,0.8782215733702287,-0.14096783488123543,0.44941173457195416,0.8782215733702287,-0.16357249431413448,0.44069966453192305,0.8782215733702287,-0.18577048675333388,0.43089194568827666,0.8782215733702287,-0.20750662449206617,0.42001296157538553,0.8782215733702287,-0.2287268680671202,0.4080897590618706,0.8782215733702287,-0.24937846060946106,0.39515198110782584,0.8782215733702287,-0.26941005900611914,0.38123179306774924,0.8782215733702287,-0.28877186154725837,0.36636380272240315,0.8782215733702287,-0.3074157317410743,0.35058497423841894,0.8782215733702287,-0.3252953179886966,0.3339345362695538,0.8782215733702287,-0.3423661688215677,0.3164538844280773,0.8782215733702287,-0.3585858434147969,0.29818647836875656,0.8782215733702287,-0.3739140171017385,0.2791777337413069,0.8782215733702287,-0.38831258162746685,0.2594749092799309,0.8782215733702287,-0.401745739891902,0.23912698931065896,0.8782215733702287,-0.4141800949470413,0.2181845619685971,0.8782215733702287,-0.4255847330270337,0.19669969342785137,0.8782215733702287,-0.4359313004046732,0.1747257984568163,0.8782215733702287,-0.4451940738832314,0.15231750762064428,0.8782215733702287,-0.4533500247483782,0.12953053146105256,0.8782215733702287,-0.46037887602119343,0.10642152199113893,0.8782215733702287,-0.4662631528699321,0.08304793184955098,0.8782215733702287,-0.4709882260552088,0.059467871464173906,0.8782215733702287,-0.4745423483005922,0.03573996458045091,0.8782215733702287,-0.4769166834981853,0.01192320251351505,0.8782215733702287,-0.4781053286765813,-0.011923202513514993,0.8782215733702287,-0.4781053286765813,-0.035739964580450846,0.8782215733702287,-0.4769166834981853,-0.05946787146417385,0.8782215733702287,-0.4745423483005922,-0.08304793184955092,0.8782215733702287,-0.4709882260552088,-0.10642152199113887,0.8782215733702287,-0.4662631528699321,-0.12953053146105248,0.8782215733702287,-0.4603788760211935,-0.15231750762064422,0.8782215733702287,-0.4533500247483782,-0.17472579845681624,0.8782215733702287,-0.44519407388323146,-0.19669969342785124,0.8782215733702287,-0.43593130040467326,-0.21818456196859687,0.8782215733702287,-0.42558473302703376,-0.23912698931065912,0.8782215733702287,-0.4141800949470412,-0.2594749092799309,0.8782215733702287,-0.401745739891902,-0.2791777337413068,0.8782215733702287,-0.3883125816274669,-0.29818647836875656,0.8782215733702287,-0.37391401710173855,-0.3164538844280772,0.8782215733702287,-0.358585843414797,-0.33393453626955377,0.8782215733702287,-0.34236616882156773,-0.35058497423841894,0.8782215733702287,-0.3252953179886966,-0.36636380272240315,0.8782215733702287,-0.30741573174107434,-0.38123179306774924,0.8782215733702287,-0.28877186154725837,-0.3951519811078258,0.8782215733702287,-0.26941005900611925,-0.40808975906187056,0.8782215733702287,-0.24937846060946112,-0.4200129615753856,0.8782215733702287,-0.22872686806712014,-0.4308919456882766,0.8782215733702287,-0.20750662449206622,-0.4406996645319231,0.8782215733702287,-0.18577048675333382,-0.4494117345719541,0.8782215733702287,-0.16357249431413456,-0.45700649622955775,0.8782215733702287,-0.14096783488123563,-0.4634650677306063,0.8782215733702287,-0.11801270719962585,-0.4687713920487216,0.8782215733702287,-0.09476418133358132,-0.47291227682557285,0.8782215733702287,-0.07128005678149306,-0.4758774271691582,0.8782215733702287,-0.04761871877720641,-0.47765947124852787,0.8782215733702287,-0.023838993135131877,-0.478253978621318,0.8782215733702287,-5.856922040980838e-17,-0.47765947124852787,0.8782215733702287,0.023838993135131756,-0.4758774271691582,0.8782215733702287,0.04761871877720629,-0.47291227682557285,0.8782215733702287,0.07128005678149293,-0.46877139204872165,0.8782215733702287,0.09476418133358121,-0.46346506773060636,0.8782215733702287,0.11801270719962575,-0.45700649622955786,0.8782215733702287,0.14096783488123532,-0.44941173457195416,0.8782215733702287,0.16357249431413445,-0.44069966453192305,0.8782215733702287,0.1857704867533339,-0.43089194568827666,0.8782215733702287,0.2075066244920661,-0.4200129615753856,0.8782215733702287,0.22872686806712003,-0.4080897590618706,0.8782215733702287,0.249378460609461,-0.39515198110782584,0.8782215733702287,0.26941005900611914,-0.3812317930677493,0.8782215733702287,0.2887718615472583,-0.36636380272240315,0.8782215733702287,0.3074157317410743,-0.35058497423841906,0.8782215733702287,0.3252953179886965,-0.3339345362695539,0.8782215733702287,0.3423661688215676,-0.3164538844280775,0.8782215733702287,0.35858584341479677,-0.29818647836875656,0.8782215733702287,0.37391401710173844,-0.27917773374130717,0.8782215733702287,0.3883125816274666,-0.2594749092799308,0.8782215733702287,0.401745739891902,-0.23912698931065882,0.8782215733702287,0.41418009494704133,-0.21818456196859715,0.8782215733702287,0.42558473302703365,-0.19669969342785135,0.8782215733702287,0.4359313004046732,-0.17472579845681663,0.8782215733702287,0.4451940738832313,-0.15231750762064455,0.8782215733702287,0.4533500247483781,-0.12953053146105228,0.8782215733702287,0.46037887602119354,-0.106421521991139,0.8782215733702287,0.4662631528699321,-0.08304793184955093,0.8782215733702287,0.4709882260552088,-0.05946787146417418,0.8782215733702287,0.47454234830059216,-0.03573996458045107,0.8782215733702287,0.4769166834981853,-0.01192320251351511,0.8782215733702287,0.4781053286765813,0.011923202513514933,0.8782215733702287,0.4781053286765813,0.035739964580450895,0.8782215733702287,0.4769166834981853,0.05946787146417358,0.8782215733702287,0.4745423483005923,0.08304793184955075,0.8782215733702287,0.47098822605520885,0.10642152199113883,0.8782215733702287,0.4662631528699321,0.12953053146105253,0.8782215733702287,0.46037887602119343,0.15231750762064436,0.8782215733702287,0.45335002474837816,0.17472579845681607,0.8782215733702287,0.4451940738832315,0.19669969342785118,0.8782215733702287,0.43593130040467326,0.21818456196859698,0.8782215733702287,0.4255847330270337,0.23912698931065907,0.8782215733702287,0.4141800949470412,0.259474909279931,0.8782215733702287,0.40174573989190193,0.2791777337413067,0.8782215733702287,0.38831258162746696,0.2981864783687565,0.8782215733702287,0.37391401710173855,0.316453884428077,0.8782215733702287,0.3585858434147971,0.3339345362695539,0.8782215733702287,0.3423661688215676,0.35058497423841906,0.8782215733702287,0.3252953179886965,0.3663638027224031,0.8782215733702287,0.3074157317410744,0.3812317930677492,0.8782215733702287,0.2887718615472584,0.3951519811078256,0.8782215733702287,0.2694100590061195,0.40808975906187067,0.8782215733702287,0.249378460609461,0.4200129615753855,0.8782215733702287,0.2287268680671204,0.4308919456882766,0.8782215733702287,0.20750662449206628,0.44069966453192305,0.8782215733702287,0.18577048675333388,0.449411734571954,0.8782215733702287,0.16357249431413484,0.45700649622955775,0.8782215733702287,0.14096783488123568,0.4634650677306063,0.8782215733702287,0.11801270719962591,0.4687713920487216,0.8782215733702287,0.09476418133358137,0.47291227682557285,0.8782215733702287,0.07128005678149289,0.47587742716915815,0.8782215733702287,0.04761871877720667,0.47765947124852787,0.8782215733702287,0.023838993135131933,0.478253978621318,0.8782215733702287,0.0,0.4338837391175582,0.9009688679024191,0.0,0.4333443874480065,0.9009688679024191,-0.021627319250089563,0.43172767335258344,0.9009688679024191,-0.04320086957687637,0.42903761623726316,0.9009688679024191,-0.06466701573507294,0.42528090400788554,0.9009688679024191,-0.08597238950307774,0.42046687644296854,0.9009688679024191,-0.10706402236478291,0.41460750197354695,0.9009688679024191,-0.12788947719765112,0.4077173479277676,0.9009688679024191,-0.1483969786396643,0.39981354431421573,0.9009688679024191,-0.1685355418110315,0.39091574123401496,0.9009688679024191,-0.18825509907063326,0.38104606002757974,0.9009688679024191,-0.20750662449206628,0.37022903827747744,0.9009688679024191,-0.22624225574982101,0.3584915688041322,0.9009688679024191,-0.24441541311256432,0.3458628328060356,0.9009688679024191,-0.26198091524769046,0.3323742273106898,0.9009688679024191,-0.2788950915492332,0.31805928711665005,0.9009688679024191,-0.2951158907098729,0.3029536014207316,0.9009688679024191,-0.310602985267112,0.28709472533765873,0.9009688679024191,-0.3253178718637013,0.27052208653213283,0.9009688679024191,-0.3392239669730524,0.253276887195445,0.9009688679024191,-0.3522866978516491,0.23540200161033706,0.9009688679024191,-0.3644735884923353,0.21694186955877903,0.9009688679024191,-0.3757543403647854,0.19794238583766954,0.9009688679024191,-0.38610090774242484,0.17845078615713855,0.9009688679024191,-0.3954875674285255,0.1585155297051304,0.9009688679024191,-0.40389098270812535,0.13818617867022684,0.9009688679024191,-0.4112902613667791,0.11751327502223698,0.9009688679024191,-0.4176670076318949,0.09654821485689694,0.9009688679024191,-0.423005367907524,0.07534312061707783,0.9009688679024191,-0.42729207018889753,0.053950711508180205,0.9009688679024191,-0.430516457058722,0.032424172429882867,0.9009688679024191,-0.4326705121831976,0.010817021750102344,0.9009688679024191,-0.4337488802418873,-0.01081702175010229,0.9009688679024191,-0.4337488802418873,-0.03242417242988281,0.9009688679024191,-0.4326705121831976,-0.053950711508180156,0.9009688679024191,-0.430516457058722,-0.07534312061707778,0.9009688679024191,-0.42729207018889753,-0.09654821485689688,0.9009688679024191,-0.423005367907524,-0.11751327502223691,0.9009688679024191,-0.41766700763189496,-0.13818617867022678,0.9009688679024191,-0.4112902613667791,-0.15851552970513033,0.9009688679024191,-0.4038909827081254,-0.17845078615713844,0.9009688679024191,-0.39548756742852553,-0.19794238583766932,0.9009688679024191,-0.38610090774242495,-0.21694186955877917,0.9009688679024191,-0.37575434036478533,-0.23540200161033706,0.9009688679024191,-0.3644735884923353,-0.25327688719544483,0.9009688679024191,-0.3522866978516492,-0.2705220865321328,0.9009688679024191,-0.33922396697305246,-0.2870947253376586,0.9009688679024191,-0.3253178718637014,-0.3029536014207315,0.9009688679024191,-0.31060298526711205,-0.31805928711665005,0.9009688679024191,-0.2951158907098729,-0.3323742273106897,0.9009688679024191,-0.2788950915492333,-0.3458628328060356,0.9009688679024191,-0.26198091524769046,-0.35849156880413213,0.9009688679024191,-0.24441541311256443,-0.37022903827747744,0.9009688679024191,-0.22624225574982104,-0.3810460600275798,0.9009688679024191,-0.20750662449206622,-0.3909157412340149,0.9009688679024191,-0.1882550990706333,-0.39981354431421573,0.9009688679024191,-0.16853554181103148,-0.4077173479277676,0.9009688679024191,-0.14839697863966436,-0.4146075019735469,0.9009688679024191,-0.12788947719765129,-0.4204668764429685,0.9009688679024191,-0.107064022364783,-0.42528090400788554,0.9009688679024191,-0.08597238950307776,-0.42903761623726316,0.9009688679024191,-0.06466701573507305,-0.43172767335258344,0.9009688679024191,-0.0432008695768764,-0.4333443874480065,0.9009688679024191,-0.0216273192500897,-0.4338837391175582,0.9009688679024191,-5.313543323124029e-17,-0.4333443874480065,0.9009688679024191,0.02162731925008959,-0.43172767335258344,0.9009688679024191,0.04320086957687629,-0.42903761623726316,0.9009688679024191,0.06466701573507296,-0.4252809040078856,0.9009688679024191,0.08597238950307765,-0.42046687644296854,0.9009688679024191,0.10706402236478291,-0.414607501973547,0.9009688679024191,0.127889477197651,-0.4077173479277676,0.9009688679024191,0.14839697863966428,-0.39981354431421573,0.9009688679024191,0.16853554181103153,-0.39091574123401496,0.9009688679024191,0.1882550990706332,-0.3810460600275798,0.9009688679024191,0.2075066244920661,-0.37022903827747744,0.9009688679024191,0.22624225574982096,-0.3584915688041322,0.9009688679024191,0.24441541311256432,-0.34586283280603564,0.9009688679024191,0.2619809152476904,-0.3323742273106898,0.9009688679024191,0.2788950915492332,-0.31805928711665016,0.9009688679024191,0.29511589070987276,-0.30295360142073163,0.9009688679024191,0.310602985267112,-0.28709472533765884,0.9009688679024191,0.3253178718637012,-0.27052208653213283,0.9009688679024191,0.33922396697305235,-0.2532768871954452,0.9009688679024191,0.35228669785164896,-0.23540200161033695,0.9009688679024191,0.3644735884923353,-0.21694186955877892,0.9009688679024191,0.37575434036478544,-0.19794238583766957,0.9009688679024191,0.3861009077424248,-0.17845078615713852,0.9009688679024191,0.3954875674285255,-0.1585155297051307,0.9009688679024191,0.40389098270812523,-0.1381861786702271,0.9009688679024191,0.411290261366779,-0.11751327502223674,0.9009688679024191,0.417667007631895,-0.09654821485689699,0.9009688679024191,0.423005367907524,-0.07534312061707779,0.9009688679024191,0.42729207018889753,-0.053950711508180454,0.9009688679024191,0.43051645705872194,-0.03242417242988301,0.9009688679024191,0.4326705121831976,-0.010817021750102396,0.9009688679024191,0.4337488802418873,0.010817021750102237,0.9009688679024191,0.4337488802418873,0.03242417242988286,0.9009688679024191,0.4326705121831976,0.05395071150817991,0.9009688679024191,0.430516457058722,0.07534312061707764,0.9009688679024191,0.4272920701888976,0.09654821485689684,0.9009688679024191,0.423005367907524,0.11751327502223695,0.9009688679024191,0.4176670076318949,0.13818617867022692,0.9009688679024191,0.41129026136677904,0.1585155297051302,0.9009688679024191,0.40389098270812546,0.17845078615713839,0.9009688679024191,0.39548756742852553,0.19794238583766943,0.9009688679024191,0.3861009077424249,0.21694186955877914,0.9009688679024191,0.37575434036478533,0.23540200161033714,0.9009688679024191,0.36447358849233524,0.2532768871954448,0.9009688679024191,0.35228669785164923,0.2705220865321327,0.9009688679024191,0.33922396697305246,0.2870947253376584,0.9009688679024191,0.3253178718637016,0.30295360142073163,0.9009688679024191,0.310602985267112,0.31805928711665016,0.9009688679024191,0.29511589070987276,0.3323742273106897,0.9009688679024191,0.27889509154923336,0.3458628328060355,0.9009688679024191,0.2619809152476905,0.35849156880413197,0.9009688679024191,0.24441541311256462,0.3702290382774775,0.9009688679024191,0.22624225574982096,0.3810460600275796,0.9009688679024191,0.20750662449206644,0.3909157412340149,0.9009688679024191,0.18825509907063334,0.39981354431421573,0.9009688679024191,0.1685355418110315,0.4077173479277675,0.9009688679024191,0.1483969786396646,0.4146075019735469,0.9009688679024191,0.12788947719765134,0.4204668764429685,0.9009688679024191,0.10706402236478306,0.42528090400788554,0.9009688679024191,0.0859723895030778,0.42903761623726316,0.9009688679024191,0.06466701573507291,0.4317276733525834,0.9009688679024191,0.04320086957687664,0.4333443874480065,0.9009688679024191,0.02162731925008975,0.4338837391175582,0.9009688679024191,0.0,0.38843479627469457,0.9214762118704077,0.0,0.38795194122161325,0.9214762118704077,-0.019361876441744624,0.3865045765161111,0.9214762118704077,-0.038675616207956086,0.3840963005348957,0.9214762118704077,-0.05789320229846112,0.38073310063150967,0.9214762118704077,-0.07696685676627446,0.3764233382508258,0.9214762118704077,-0.095849159501103,0.37117772814117783,0.9214762118704077,-0.11449316612321164,0.36500931171580814,0.9214762118704077,-0.13285252469454797,0.35793342462985905,0.9214762118704077,-0.15088159095696355,0.34996765865351837,0.9214762118704077,-0.16853554181103142,0.3411318179361065,0.9214762118704077,-0.18577048675333385,0.3314478697698409,0.9214762118704077,-0.20254357699516923,0.32093988997568684,0.9214762118704077,-0.21881311199139217,0.3096340030470724,0.9214762118704077,-0.2345386431145389,0.2975583172002821,0.9214762118704077,-0.24968107421648877,0.28474285449300213,0.9214762118704077,-0.2642027588276483,0.27121947618475467,0.9214762118704077,-0.27806759375200624,0.2570218035247867,0.9214762118704077,-0.29124110882536725,0.24218513416434687,0.9214762118704077,-0.3036905526136116,0.22674635440116273,0.9214762118704077,-0.3153849738379205,0.21074384747429295,0.9214762118704077,-0.32629529832453213,0.19421739813734723,0.9214762118704077,-0.3363944012877186,0.17720809374732038,0.9214762118704077,-0.3456571747662767,0.15975822211494844,0.9214762118704077,-0.3540605900458766,0.14191116637054887,0.9214762118704077,-0.36158375491207273,0.12371129710672295,0.9214762118704077,-0.3682079655916417,0.10520386206607112,0.9214762118704077,-0.3739167532531092,0.08643487364817577,0.9214762118704077,-0.37869592495085996,0.06745099451552608,0.9214762118704077,-0.3825335989110368,0.04829942158278674,0.9214762118704077,-0.3854202340715024,0.02902776867783162,0.9214762118704077,-0.38734865380242356,0.00968394816626561,0.9214762118704077,-0.38831406374850314,-0.009683948166265563,0.9214762118704077,-0.38831406374850314,-0.02902776867783157,0.9214762118704077,-0.38734865380242356,-0.04829942158278669,0.9214762118704077,-0.3854202340715024,-0.06745099451552604,0.9214762118704077,-0.3825335989110368,-0.08643487364817573,0.9214762118704077,-0.37869592495085996,-0.10520386206607105,0.9214762118704077,-0.37391675325310925,-0.1237112971067229,0.9214762118704077,-0.3682079655916417,-0.14191116637054882,0.9214762118704077,-0.3615837549120728,-0.15975822211494833,0.9214762118704077,-0.35406059004587664,-0.1772080937473202,0.9214762118704077,-0.34565717476627683,-0.19421739813734737,0.9214762118704077,-0.3363944012877185,-0.21074384747429295,0.9214762118704077,-0.3262952983245322,-0.2267463544011626,0.9214762118704077,-0.3153849738379206,-0.24218513416434684,0.9214762118704077,-0.30369055261361166,-0.25702180352478665,0.9214762118704077,-0.29124110882536736,-0.2712194761847546,0.9214762118704077,-0.27806759375200624,-0.28474285449300213,0.9214762118704077,-0.2642027588276483,-0.29755831720028203,0.9214762118704077,-0.24968107421648886,-0.3096340030470724,0.9214762118704077,-0.2345386431145389,-0.3209398899756868,0.9214762118704077,-0.21881311199139225,-0.33144786976984086,0.9214762118704077,-0.20254357699516928,-0.3411318179361065,0.9214762118704077,-0.18577048675333382,-0.34996765865351837,0.9214762118704077,-0.16853554181103145,-0.3579334246298591,0.9214762118704077,-0.1508815909569635,-0.36500931171580814,0.9214762118704077,-0.13285252469454803,-0.3711777281411778,0.9214762118704077,-0.1144931661232118,-0.37642333825082575,0.9214762118704077,-0.09584915950110308,-0.38073310063150967,0.9214762118704077,-0.07696685676627446,-0.3840963005348957,0.9214762118704077,-0.057893202298461226,-0.3865045765161111,0.9214762118704077,-0.038675616207956114,-0.38795194122161325,0.9214762118704077,-0.019361876441744745,-0.38843479627469457,0.9214762118704077,-4.7569542993525894e-17,-0.38795194122161325,0.9214762118704077,0.019361876441744648,-0.3865045765161111,0.9214762118704077,0.038675616207956016,-0.3840963005348957,0.9214762118704077,0.05789320229846113,-0.3807331006315097,0.9214762118704077,0.07696685676627436,-0.3764233382508258,0.9214762118704077,0.095849159501103,-0.3711777281411779,0.9214762118704077,0.11449316612321153,-0.36500931171580814,0.9214762118704077,0.13285252469454795,-0.35793342462985905,0.9214762118704077,0.15088159095696357,-0.34996765865351837,0.9214762118704077,0.16853554181103136,-0.3411318179361065,0.9214762118704077,0.18577048675333374,-0.3314478697698409,0.9214762118704077,0.2025435769951692,-0.32093988997568684,0.9214762118704077,0.21881311199139217,-0.30963400304707245,0.9214762118704077,0.23453864311453881,-0.2975583172002821,0.9214762118704077,0.24968107421648877,-0.2847428544930022,0.9214762118704077,0.2642027588276482,-0.2712194761847547,0.9214762118704077,0.2780675937520062,-0.2570218035247869,0.9214762118704077,0.2912411088253672,-0.24218513416434687,0.9214762118704077,0.30369055261361155,-0.22674635440116295,0.9214762118704077,0.31538497383792036,-0.21074384747429287,0.9214762118704077,0.3262952983245322,-0.19421739813734715,0.9214762118704077,0.3363944012877186,-0.17720809374732044,0.9214762118704077,0.3456571747662767,-0.15975822211494842,0.9214762118704077,0.3540605900458766,-0.14191116637054915,0.9214762118704077,0.3615837549120726,-0.12371129710672316,0.9214762118704077,0.36820796559164165,-0.1052038620660709,0.9214762118704077,0.3739167532531093,-0.08643487364817583,0.9214762118704077,0.37869592495085996,-0.06745099451552605,0.9214762118704077,0.3825335989110368,-0.04829942158278695,0.9214762118704077,0.3854202340715024,-0.029027768677831748,0.9214762118704077,0.38734865380242356,-0.009683948166265657,0.9214762118704077,0.38831406374850314,0.009683948166265515,0.9214762118704077,0.38831406374850314,0.02902776867783161,0.9214762118704077,0.38734865380242356,0.04829942158278647,0.9214762118704077,0.38542023407150244,0.06745099451552591,0.9214762118704077,0.3825335989110368,0.08643487364817568,0.9214762118704077,0.37869592495085996,0.10520386206607109,0.9214762118704077,0.3739167532531092,0.123711297106723,0.9214762118704077,0.3682079655916417,0.14191116637054868,0.9214762118704077,0.3615837549120728,0.15975822211494828,0.9214762118704077,0.35406059004587664,0.1772080937473203,0.9214762118704077,0.3456571747662768,0.19421739813734734,0.9214762118704077,0.3363944012877185,0.21074384747429303,0.9214762118704077,0.3262952983245321,0.22674635440116256,0.9214762118704077,0.3153849738379206,0.24218513416434678,0.9214762118704077,0.30369055261361166,0.2570218035247865,0.9214762118704077,0.29124110882536747,0.2712194761847547,0.9214762118704077,0.2780675937520062,0.2847428544930022,0.9214762118704077,0.2642027588276482,0.297558317200282,0.9214762118704077,0.24968107421648889,0.30963400304707234,0.9214762118704077,0.23453864311453892,0.3209398899756867,0.9214762118704077,0.21881311199139242,0.331447869769841,0.9214762118704077,0.2025435769951692,0.34113181793610636,0.9214762118704077,0.18577048675333402,0.34996765865351837,0.9214762118704077,0.1685355418110315,0.35793342462985905,0.9214762118704077,0.15088159095696355,0.365009311715808,0.9214762118704077,0.13285252469454825,0.3711777281411778,0.9214762118704077,0.11449316612321184,0.37642333825082575,0.9214762118704077,0.09584915950110312,0.38073310063150967,0.9214762118704077,0.07696685676627452,0.3840963005348957,0.9214762118704077,0.0578932022984611,0.38650457651611103,0.9214762118704077,0.03867561620795633,0.38795194122161325,0.9214762118704077,0.01936187644174479,0.38843479627469457,0.9214762118704077,0.0,0.3420201433256688,0.9396926207859083,0.0,0.34159498534279953,0.9396926207859083,-0.017048296957866544,0.340320568403926,0.9396926207859083,-0.03405420916332942,0.338200060910358,0.9396926207859083,-0.05097545723915767,0.33523873477783633,0.9396926207859083,-0.06776997229648646,0.33144395232971946,0.9396926207859083,-0.08439600052470328,0.3268251479930873,0.9396926207859083,-0.10081220699799998,0.32139380484326974,0.9396926207859083,-0.11697777844051101,0.31516342605511116,0.9396926207859083,-0.13285252469454806,0.30814950133195096,0.9396926207859083,-0.14839697863966433,0.30036946839577994,0.9396926207859083,-0.1635724943141346,0.2918426696343162,0.9396926207859083,-0.17834134299490517,0.2825903040127815,0.9396926207859083,-0.1926668069971441,0.2726353743699337,0.9396926207859083,-0.20651327096019073,0.262002630229385,0.9396926207859083,-0.21984631039295421,0.25071850626838704,0.9396926207859083,-0.2326327772586224,0.2388110565970576,0.9396926207859083,-0.24484088238590385,0.22630988501144308,0.9396926207859083,-0.25644027450191687,0.21324607139381693,0.9396926207859083,-0.2674021156902357,0.1996520944431951,0.9396926207859083,-0.27769915308649495,0.18556175092817317,0.9396926207859083,-0.2873057866333041,0.17101007166283438,0.9396926207859083,-0.29619813272602397,0.15603323441462644,0.9396926207859083,-0.3043540835911706,0.14066847396073093,0.9396926207859083,-0.31175336224982436,0.12495398951654108,0.9396926207859083,-0.31837757292939334,0.10892884976639326,0.9396926207859083,-0.3242102467984033,0.09263289573266187,0.9396926207859083,-0.32923688291060715,0.07610664172470036,0.9396926207859083,-0.3334449842566224,0.05939117461388475,0.9396926207859083,-0.33682408883346526,0.042528051685177544,0.9396926207859083,-0.3393657956547388,0.025559197319169516,0.9396926207859083,-0.3410637856368084,0.008526798761463807,0.9396926207859083,-0.3419138373090398,-0.008526798761463766,0.9396926207859083,-0.3419138373090398,-0.02555919731916947,0.9396926207859083,-0.3410637856368084,-0.04252805168517751,0.9396926207859083,-0.3393657956547388,-0.059391174613884705,0.9396926207859083,-0.33682408883346526,-0.07610664172470032,0.9396926207859083,-0.3334449842566224,-0.09263289573266181,0.9396926207859083,-0.32923688291060715,-0.10892884976639323,0.9396926207859083,-0.3242102467984033,-0.12495398951654105,0.9396926207859083,-0.3183775729293934,-0.14066847396073084,0.9396926207859083,-0.3117533622498244,-0.15603323441462624,0.9396926207859083,-0.3043540835911707,-0.1710100716628345,0.9396926207859083,-0.2961981327260239,-0.18556175092817317,0.9396926207859083,-0.28730578663330414,-0.19965209444319498,0.9396926207859083,-0.277699153086495,-0.21324607139381688,0.9396926207859083,-0.2674021156902357,-0.226309885011443,0.9396926207859083,-0.2564402745019169,-0.23881105659705754,0.9396926207859083,-0.2448408823859039,-0.25071850626838704,0.9396926207859083,-0.2326327772586224,-0.26200263022938497,0.9396926207859083,-0.2198463103929543,-0.2726353743699337,0.9396926207859083,-0.20651327096019073,-0.28259030401278146,0.9396926207859083,-0.19266680699714417,-0.29184266963431615,0.9396926207859083,-0.17834134299490523,-0.30036946839578,0.9396926207859083,-0.16357249431413456,-0.30814950133195096,0.9396926207859083,-0.14839697863966436,-0.3151634260551112,0.9396926207859083,-0.13285252469454803,-0.32139380484326974,0.9396926207859083,-0.11697777844051106,-0.3268251479930873,0.9396926207859083,-0.1008122069980001,-0.3314439523297194,0.9396926207859083,-0.08439600052470336,-0.33523873477783633,0.9396926207859083,-0.06776997229648647,-0.338200060910358,0.9396926207859083,-0.05097545723915776,-0.340320568403926,0.9396926207859083,-0.03405420916332944,-0.34159498534279953,0.9396926207859083,-0.017048296957866655,-0.3420201433256688,0.9396926207859083,-4.188538737676993e-17,-0.34159498534279953,0.9396926207859083,0.01704829695786657,-0.340320568403926,0.9396926207859083,0.03405420916332936,-0.338200060910358,0.9396926207859083,0.050975457239157675,-0.3352387347778364,0.9396926207859083,0.06776997229648639,-0.33144395232971946,0.9396926207859083,0.08439600052470328,-0.3268251479930874,0.9396926207859083,0.10081220699799988,-0.32139380484326974,0.9396926207859083,0.116977778440511,-0.31516342605511116,0.9396926207859083,0.13285252469454809,-0.30814950133195096,0.9396926207859083,0.14839697863966428,-0.30036946839578,0.9396926207859083,0.16357249431413448,-0.2918426696343162,0.9396926207859083,0.17834134299490514,-0.2825903040127815,0.9396926207859083,0.1926668069971441,-0.27263537436993374,0.9396926207859083,0.20651327096019065,-0.262002630229385,0.9396926207859083,0.21984631039295421,-0.2507185062683871,0.9396926207859083,0.23263277725862233,-0.23881105659705762,0.9396926207859083,0.24484088238590382,-0.2263098850114432,0.9396926207859083,0.25644027450191675,-0.21324607139381693,0.9396926207859083,0.26740211569023564,-0.1996520944431953,0.9396926207859083,0.2776991530864948,-0.1855617509281731,0.9396926207859083,0.28730578663330414,-0.17101007166283427,0.9396926207859083,0.29619813272602397,-0.15603323441462646,0.9396926207859083,0.3043540835911706,-0.14066847396073093,0.9396926207859083,0.31175336224982436,-0.12495398951654133,0.9396926207859083,0.3183775729293933,-0.10892884976639346,0.9396926207859083,0.3242102467984032,-0.09263289573266167,0.9396926207859083,0.3292368829106072,-0.0761066417247004,0.9396926207859083,0.3334449842566224,-0.05939117461388472,0.9396926207859083,0.33682408883346526,-0.04252805168517774,0.9396926207859083,0.33936579565473873,-0.02555919731916963,0.9396926207859083,0.3410637856368084,-0.008526798761463849,0.9396926207859083,0.3419138373090398,0.008526798761463722,0.9396926207859083,0.3419138373090398,0.025559197319169506,0.9396926207859083,0.3410637856368084,0.042528051685177315,0.9396926207859083,0.3393657956547388,0.059391174613884594,0.9396926207859083,0.33682408883346526,0.07610664172470027,0.9396926207859083,0.3334449842566224,0.09263289573266185,0.9396926207859083,0.32923688291060715,0.10892884976639333,0.9396926207859083,0.32421024679840327,0.12495398951654092,0.9396926207859083,0.31837757292939345,0.14066847396073082,0.9396926207859083,0.3117533622498244,0.15603323441462635,0.9396926207859083,0.30435408359117067,0.17101007166283444,0.9396926207859083,0.2961981327260239,0.18556175092817326,0.9396926207859083,0.2873057866333041,0.19965209444319496,0.9396926207859083,0.277699153086495,0.21324607139381685,0.9396926207859083,0.2674021156902357,0.22630988501144284,0.9396926207859083,0.25644027450191703,0.23881105659705762,0.9396926207859083,0.24484088238590382,0.2507185062683871,0.9396926207859083,0.23263277725862233,0.26200263022938497,0.9396926207859083,0.21984631039295435,0.2726353743699337,0.9396926207859083,0.20651327096019076,0.28259030401278135,0.9396926207859083,0.19266680699714434,0.29184266963431627,0.9396926207859083,0.17834134299490514,0.3003694683957799,0.9396926207859083,0.16357249431413473,0.30814950133195096,0.9396926207859083,0.14839697863966442,0.31516342605511116,0.9396926207859083,0.13285252469454806,0.3213938048432696,0.9396926207859083,0.11697777844051126,0.3268251479930873,0.9396926207859083,0.10081220699800014,0.3314439523297194,0.9396926207859083,0.0843960005247034,0.33523873477783633,0.9396926207859083,0.0677699722964865,0.338200060910358,0.9396926207859083,0.05097545723915765,0.34032056840392594,0.9396926207859083,0.034054209163329636,0.34159498534279953,0.9396926207859083,0.017048296957866693,0.3420201433256688,0.9396926207859083,0.0,0.29475517441090454,0.9555728057861407,0.0,0.2943887705079815,0.9555728057861407,-0.014692332721584798,0.2932904697370465,0.9555728057861407,-0.02934813798906813,0.2914630026468666,0.9555728057861407,-0.04393097916135476,0.28891091260856777,0.9555728057861407,-0.0584046009975865,0.28564054452009796,0.9555728057861407,-0.07273301979338342,0.2816600290318113,0.9555728057861407,-0.08688061284200266,0.2769792623323904,0.9555728057861407,-0.10081220699800005,0.27160988154536164,0.9555728057861407,-0.11449316612321182,0.2655652357973728,0.9555728057861407,-0.12788947719765126,0.2588603530301607,0.9555728057861407,-0.14096783488123565,0.2515119026387206,0.9555728057861407,-0.15369572431610942,0.24353815402856308,0.9555728057861407,-0.1660415019637038,0.23495893119509415,0.9555728057861407,-0.17797447427555946,0.2257955634380386,0.9555728057861407,-0.18946497400232432,0.21607083233344052,0.9555728057861407,-0.20048443395120977,0.20580891509507543,0.9555728057861407,-0.21100545800853301,0.19503532446608776,0.9555728057861407,-0.22100188925077233,0.18377684529029134,0.9555728057861407,-0.23044887497480018,0.1720614679208274,0.9555728057861407,-0.23932292848561929,0.15991831863173656,0.9555728057861407,-0.24760198748798673,0.14737758720545224,0.9555728057861407,-0.2552654689367563,0.13447045187624498,0.9555728057861407,-0.2622943202095715,0.12122900181621912,0.9555728057861407,-0.2686710664746874,0.10768615735657527,0.9555728057861407,-0.27437985413615495,0.09387558814248,0.9555728057861407,-0.27940649024835884,0.07983162942502252,0.9555728057861407,-0.28373847780191647,0.06558919669837084,0.9555728057861407,-0.28736504679321245,0.051183698894351815,0.9555728057861407,-0.2902771810003244,0.036650950350267525,0.9555728057861407,-0.29246764039877127,0.022027081768809742,0.9555728057861407,-0.293930979161355,0.007348450391440214,0.9555728057861407,-0.2946635591973451,-0.007348450391440178,0.9555728057861407,-0.2946635591973451,-0.022027081768809704,0.9555728057861407,-0.293930979161355,-0.03665095035026749,0.9555728057861407,-0.29246764039877127,-0.05118369889435178,0.9555728057861407,-0.2902771810003244,-0.06558919669837081,0.9555728057861407,-0.28736504679321245,-0.07983162942502248,0.9555728057861407,-0.28373847780191647,-0.09387558814247997,0.9555728057861407,-0.27940649024835884,-0.10768615735657525,0.9555728057861407,-0.27437985413615495,-0.12122900181621904,0.9555728057861407,-0.26867106647468747,-0.13447045187624485,0.9555728057861407,-0.26229432020957155,-0.14737758720545233,0.9555728057861407,-0.2552654689367562,-0.15991831863173656,0.9555728057861407,-0.24760198748798676,-0.1720614679208273,0.9555728057861407,-0.2393229284856193,-0.1837768452902913,0.9555728057861407,-0.2304488749748002,-0.19503532446608768,0.9555728057861407,-0.22100188925077238,-0.2058089150950754,0.9555728057861407,-0.21100545800853304,-0.21607083233344052,0.9555728057861407,-0.20048443395120977,-0.22579556343803858,0.9555728057861407,-0.18946497400232437,-0.23495893119509415,0.9555728057861407,-0.17797447427555946,-0.24353815402856305,0.9555728057861407,-0.16604150196370385,-0.25151190263872053,0.9555728057861407,-0.15369572431610945,-0.25886035303016075,0.9555728057861407,-0.14096783488123563,-0.26556523579737273,0.9555728057861407,-0.12788947719765129,-0.27160988154536164,0.9555728057861407,-0.11449316612321178,-0.2769792623323904,0.9555728057861407,-0.1008122069980001,-0.28166002903181125,0.9555728057861407,-0.08688061284200277,-0.2856405445200979,0.9555728057861407,-0.0727330197933835,-0.28891091260856777,0.9555728057861407,-0.05840460099758651,-0.2914630026468666,0.9555728057861407,-0.04393097916135484,-0.2932904697370465,0.9555728057861407,-0.02934813798906815,-0.2943887705079815,0.9555728057861407,-0.014692332721584891,-0.29475517441090454,0.9555728057861407,-3.6097098087443407e-17,-0.2943887705079815,0.9555728057861407,0.014692332721584819,-0.2932904697370465,0.9555728057861407,0.029348137989068074,-0.2914630026468666,0.9555728057861407,0.043930979161354765,-0.28891091260856777,0.9555728057861407,0.05840460099758644,-0.28564054452009796,0.9555728057861407,0.07273301979338342,-0.28166002903181137,0.9555728057861407,0.08688061284200257,-0.2769792623323904,0.9555728057861407,0.10081220699800003,-0.27160988154536164,0.9555728057861407,0.11449316612321184,-0.2655652357973728,0.9555728057861407,0.12788947719765123,-0.25886035303016075,0.9555728057861407,0.14096783488123554,-0.2515119026387206,0.9555728057861407,0.1536957243161094,-0.24353815402856308,0.9555728057861407,0.1660415019637038,-0.23495893119509417,0.9555728057861407,0.1779744742755594,-0.2257955634380386,0.9555728057861407,0.18946497400232432,-0.21607083233344057,0.9555728057861407,0.2004844339512097,-0.20580891509507546,0.9555728057861407,0.211005458008533,-0.19503532446608784,0.9555728057861407,0.22100188925077224,-0.18377684529029134,0.9555728057861407,0.23044887497480016,-0.17206146792082758,0.9555728057861407,0.23932292848561917,-0.1599183186317365,0.9555728057861407,0.24760198748798676,-0.14737758720545216,0.9555728057861407,0.2552654689367563,-0.134470451876245,0.9555728057861407,0.2622943202095715,-0.12122900181621911,0.9555728057861407,0.2686710664746874,-0.1076861573565755,0.9555728057861407,0.2743798541361549,-0.09387558814248016,0.9555728057861407,0.27940649024835873,-0.07983162942502235,0.9555728057861407,0.2837384778019165,-0.06558919669837088,0.9555728057861407,0.28736504679321245,-0.05118369889435179,0.9555728057861407,0.2902771810003244,-0.03665095035026769,0.9555728057861407,0.2924676403987712,-0.02202708176880984,0.9555728057861407,0.293930979161355,-0.00734845039144025,0.9555728057861407,0.2946635591973451,0.007348450391440142,0.9555728057861407,0.2946635591973451,0.02202708176880973,0.9555728057861407,0.293930979161355,0.036650950350267324,0.9555728057861407,0.29246764039877127,0.05118369889435168,0.9555728057861407,0.2902771810003244,0.06558919669837077,0.9555728057861407,0.28736504679321245,0.0798316294250225,0.9555728057861407,0.28373847780191647,0.09387558814248005,0.9555728057861407,0.2794064902483588,0.10768615735657515,0.9555728057861407,0.274379854136155,0.12122900181621901,0.9555728057861407,0.26867106647468747,0.13447045187624493,0.9555728057861407,0.26229432020957155,0.1473775872054523,0.9555728057861407,0.2552654689367562,0.1599183186317366,0.9555728057861407,0.24760198748798667,0.17206146792082727,0.9555728057861407,0.23932292848561937,0.18377684529029126,0.9555728057861407,0.2304488749748002,0.19503532446608754,0.9555728057861407,0.22100188925077247,0.20580891509507546,0.9555728057861407,0.211005458008533,0.21607083233344057,0.9555728057861407,0.2004844339512097,0.22579556343803855,0.9555728057861407,0.1894649740023244,0.23495893119509412,0.9555728057861407,0.17797447427555949,0.24353815402856294,0.9555728057861407,0.16604150196370399,0.2515119026387206,0.9555728057861407,0.1536957243161094,0.25886035303016064,0.9555728057861407,0.14096783488123577,0.26556523579737273,0.9555728057861407,0.1278894771976513,0.27160988154536164,0.9555728057861407,0.11449316612321182,0.27697926233239034,0.9555728057861407,0.10081220699800027,0.28166002903181125,0.9555728057861407,0.08688061284200281,0.2856405445200979,0.9555728057861407,0.07273301979338352,0.28891091260856777,0.9555728057861407,0.05840460099758654,0.2914630026468666,0.9555728057861407,0.04393097916135474,0.2932904697370464,0.9555728057861407,0.029348137989068313,0.2943887705079815,0.9555728057861407,0.014692332721584926,0.29475517441090454,0.9555728057861407,0.0,0.24675739769029378,0.969077286229078,0.0,0.24645065880515105,0.969077286229078,-0.012299841031201563,0.24553120475092174,0.969077286229078,-0.024569102719610096,0.24400132143525582,0.969077286229078,-0.0367772817474969,0.24186481238912777,0.969077286229078,-0.04889402665825165,0.23912698931065926,0.969077286229078,-0.06088921331488576,0.23579465885940057,0.969077286229078,-0.0727330197933834,0.23187610573390283,0.969077286229078,-0.08439600052470331,0.22738107207465158,0.969077286229078,-0.09584915950110309,0.22232073324357102,0.969077286229078,-0.10706402236478296,0.216707670040313,0.969077286229078,-0.11801270719962588,0.21055583742440726,0.969077286229078,-0.128667993850034,0.20388052982103286,0.969077286229078,-0.13900339159452457,0.19669834309666767,0.969077286229078,-0.14899320500583715,0.1890271332991493,0.969077286229078,-0.1586125978338147,0.18088597226472672,0.969077286229078,-0.16783765475223467,0.17229510020247038,0.969077286229078,-0.17664544081607794,0.16327587537392338,0.969077286229078,-0.18501405848141608,0.153850720993098,0.969077286229078,-0.19292270204615558,0.14404306947883258,0.969077286229078,-0.20035170937629163,0.13387730419810626,0.969077286229078,-0.20728261078907106,0.12337869884514686,0.969077286229078,-0.213698174971534,0.1125733546070448,0.969077286229078,-0.2195824518202726,0.10148813527208922,0.969077286229078,-0.22492081209590173,0.09015060044215784,0.969077286229078,-0.22969998379365247,0.0785889370152044,0.969077286229078,-0.23390808513966774,0.06683188910818887,0.969077286229078,-0.2375346541309637,0.054908686594673885,0.969077286229078,-0.24057067454561806,0.04284897243475354,0.969077286229078,-0.24300859835851804,0.030682728977983954,0.969077286229078,-0.24484236450694025,0.018440203422538198,0.969077286229078,-0.24606741395930753,0.006151832615905809,0.969077286229078,-0.2466807010496599,-0.00615183261590578,0.969077286229078,-0.2466807010496599,-0.018440203422538166,0.969077286229078,-0.24606741395930753,-0.030682728977983926,0.969077286229078,-0.24484236450694025,-0.04284897243475351,0.969077286229078,-0.24300859835851804,-0.05490868659467386,0.969077286229078,-0.24057067454561806,-0.06683188910818882,0.969077286229078,-0.23753465413096372,-0.07858893701520438,0.969077286229078,-0.23390808513966774,-0.09015060044215781,0.969077286229078,-0.2296999837936525,-0.10148813527208915,0.969077286229078,-0.22492081209590176,-0.11257335460704469,0.969077286229078,-0.21958245182027267,-0.12337869884514695,0.969077286229078,-0.21369817497153398,-0.13387730419810626,0.969077286229078,-0.2072826107890711,-0.1440430694788325,0.969077286229078,-0.20035170937629165,-0.15385072099309796,0.969077286229078,-0.1929227020461556,-0.16327587537392332,0.969077286229078,-0.18501405848141614,-0.17229510020247038,0.969077286229078,-0.17664544081607797,-0.18088597226472672,0.969077286229078,-0.16783765475223467,-0.18902713329914927,0.969077286229078,-0.15861259783381476,-0.19669834309666767,0.969077286229078,-0.14899320500583715,-0.20388052982103283,0.969077286229078,-0.13900339159452463,-0.21055583742440723,0.969077286229078,-0.12866799385003402,-0.21670767004031302,0.969077286229078,-0.11801270719962585,-0.222320733243571,0.969077286229078,-0.10706402236478299,-0.2273810720746516,0.969077286229078,-0.09584915950110307,-0.2318761057339028,0.969077286229078,-0.08439600052470335,-0.23579465885940054,0.969077286229078,-0.0727330197933835,-0.23912698931065923,0.969077286229078,-0.060889213314885816,-0.24186481238912777,0.969077286229078,-0.048894026658251655,-0.24400132143525582,0.969077286229078,-0.03677728174749697,-0.24553120475092174,0.969077286229078,-0.024569102719610113,-0.24645065880515105,0.969077286229078,-0.01229984103120164,-0.24675739769029378,0.969077286229078,-3.021906572473488e-17,-0.24645065880515105,0.969077286229078,0.01229984103120158,-0.24553120475092174,0.969077286229078,0.02456910271961005,-0.24400132143525582,0.969077286229078,0.03677728174749691,-0.2418648123891278,0.969077286229078,0.04889402665825159,-0.23912698931065926,0.969077286229078,0.06088921331488576,-0.2357946588594006,0.969077286229078,0.07273301979338333,-0.23187610573390283,0.969077286229078,0.0843960005247033,-0.22738107207465158,0.969077286229078,0.0958491595011031,-0.22232073324357102,0.969077286229078,0.10706402236478293,-0.21670767004031302,0.969077286229078,0.1180127071996258,-0.21055583742440726,0.969077286229078,0.128667993850034,-0.20388052982103286,0.969077286229078,0.13900339159452457,-0.1966983430966677,0.969077286229078,0.1489932050058371,-0.1890271332991493,0.969077286229078,0.1586125978338147,-0.18088597226472677,0.969077286229078,0.1678376547522346,-0.1722951002024704,0.969077286229078,0.17664544081607791,-0.16327587537392346,0.969077286229078,0.18501405848141603,-0.153850720993098,0.969077286229078,0.19292270204615555,-0.14404306947883272,0.969077286229078,0.20035170937629151,-0.13387730419810623,0.969077286229078,0.2072826107890711,-0.1233786988451468,0.969077286229078,0.21369817497153404,-0.11257335460704483,0.969077286229078,0.21958245182027258,-0.1014881352720892,0.969077286229078,0.22492081209590173,-0.09015060044215802,0.969077286229078,0.22969998379365242,-0.07858893701520454,0.969077286229078,0.23390808513966768,-0.06683188910818873,0.969077286229078,0.23753465413096375,-0.05490868659467392,0.969077286229078,0.24057067454561806,-0.04284897243475352,0.969077286229078,0.24300859835851804,-0.030682728977984096,0.969077286229078,0.24484236450694022,-0.018440203422538277,0.969077286229078,0.24606741395930753,-0.00615183261590584,0.969077286229078,0.2466807010496599,0.006151832615905749,0.969077286229078,0.2466807010496599,0.01844020342253819,0.969077286229078,0.24606741395930753,0.030682728977983787,0.969077286229078,0.24484236450694027,0.04284897243475343,0.969077286229078,0.24300859835851807,0.05490868659467383,0.969077286229078,0.24057067454561806,0.06683188910818885,0.969077286229078,0.2375346541309637,0.07858893701520445,0.969077286229078,0.2339080851396677,0.09015060044215772,0.969077286229078,0.22969998379365253,0.10148813527208912,0.969077286229078,0.22492081209590176,0.11257335460704476,0.969077286229078,0.21958245182027264,0.12337869884514692,0.969077286229078,0.21369817497153398,0.1338773041981063,0.969077286229078,0.20728261078907104,0.14404306947883247,0.969077286229078,0.20035170937629168,0.15385072099309793,0.969077286229078,0.1929227020461556,0.1632758753739232,0.969077286229078,0.18501405848141622,0.1722951002024704,0.969077286229078,0.17664544081607791,0.18088597226472677,0.969077286229078,0.1678376547522346,0.18902713329914925,0.969077286229078,0.1586125978338148,0.19669834309666764,0.969077286229078,0.14899320500583718,0.20388052982103275,0.969077286229078,0.13900339159452474,0.21055583742440728,0.969077286229078,0.128667993850034,0.21670767004031294,0.969077286229078,0.11801270719962596,0.222320733243571,0.969077286229078,0.10706402236478302,0.22738107207465158,0.969077286229078,0.09584915950110309,0.23187610573390274,0.969077286229078,0.08439600052470349,0.23579465885940054,0.969077286229078,0.07273301979338352,0.23912698931065923,0.969077286229078,0.060889213314885844,0.24186481238912777,0.969077286229078,0.04889402665825168,0.24400132143525582,0.969077286229078,0.036777281747496886,0.24553120475092172,0.969077286229078,0.02456910271961025,0.24645065880515105,0.969077286229078,0.01229984103120167,0.24675739769029378,0.969077286229078,0.0,0.19814614319939755,0.9801724878485438,0.0,0.19789983193323402,0.9801724878485438,-0.009876769998025297,0.1971615105033604,0.9801724878485438,-0.019728984789630335,0.1959330144931829,0.9801724878485438,-0.029532150216506986,0.1942173981373473,0.9801724878485438,-0.03926189406479617,0.19201892672843046,0.9801724878485438,-0.048894026658251606,0.18934306601275072,0.9801724878485438,-0.058404600997586424,0.18619646860166178,0.9801724878485438,-0.06776997229648643,0.18258695743211217,0.9801724878485438,-0.07696685676627447,0.17852350631759184,0.9801724878485438,-0.08597238950307771,0.17401621763781752,0.9801724878485438,-0.09476418133358133,0.16907629722262532,0.9801724878485438,-0.103320374478041,0.16371602649251185,0.9801724878485438,-0.1116196968921674,0.15794873192508802,0.9801724878485438,-0.11964151515277983,0.15178875192335578,0.9801724878485438,-0.12736588575574748,0.1452514011681792,0.9801724878485438,-0.13477360469868285,0.1383529325435747,0.9801724878485438,-0.1418462552251177,0.1311104967294806,0.9801724878485438,-0.14856625361146122,0.12354209956246463,0.9801724878485438,-0.1549168928829075,0.11566655727037688,0.9801724878485438,-0.16088238434960736,0.1075034496922429,0.9801724878485438,-0.16644789685983888,0.09907307159969875,0.9801724878485438,-0.17159959367258748,0.09039638224099114,0.9801724878485438,-0.17632466685786416,0.08149495323298357,0.9801724878485438,-0.1806113691392377,0.0723909149307183,0.9801724878485438,-0.18444904309941448,0.0631069014078667,0.9801724878485438,-0.18782814767625738,0.05366599418485577,0.9801724878485438,-0.1907402818833693,0.04409166484457157,0.9801724878485438,-0.1931782056962693,0.03440771667830602,0.9801724878485438,-0.1951358580522339,0.02463822550702421,0.9801724878485438,-0.1966083719190543,0.014807479825080029,0.9801724878485438,-0.19759208639524511,0.004939920415192257,0.9801724878485438,-0.19808455581162235,-0.004939920415192233,0.9801724878485438,-0.19808455581162235,-0.014807479825080003,0.9801724878485438,-0.19759208639524511,-0.02463822550702419,0.9801724878485438,-0.1966083719190543,-0.034407716678306,0.9801724878485438,-0.1951358580522339,-0.04409166484457155,0.9801724878485438,-0.1931782056962693,-0.053665994184855736,0.9801724878485438,-0.19074028188336933,-0.06310690140786669,0.9801724878485438,-0.18782814767625738,-0.07239091493071827,0.9801724878485438,-0.1844490430994145,-0.08149495323298352,0.9801724878485438,-0.18061136913923773,-0.09039638224099104,0.9801724878485438,-0.17632466685786422,-0.09907307159969882,0.9801724878485438,-0.17159959367258745,-0.1075034496922429,0.9801724878485438,-0.1664478968598389,-0.11566655727037681,0.9801724878485438,-0.1608823843496074,-0.1235420995624646,0.9801724878485438,-0.15491689288290753,-0.13111049672948058,0.9801724878485438,-0.14856625361146128,-0.13835293254357467,0.9801724878485438,-0.14184625522511773,-0.1452514011681792,0.9801724878485438,-0.13477360469868285,-0.15178875192335575,0.9801724878485438,-0.1273658857557475,-0.15794873192508802,0.9801724878485438,-0.11964151515277983,-0.16371602649251182,0.9801724878485438,-0.11161969689216744,-0.1690762972226253,0.9801724878485438,-0.10332037447804102,-0.17401621763781755,0.9801724878485438,-0.0947641813335813,-0.1785235063175918,0.9801724878485438,-0.08597238950307774,-0.1825869574321122,0.9801724878485438,-0.07696685676627446,-0.18619646860166178,0.9801724878485438,-0.06776997229648646,-0.18934306601275072,0.9801724878485438,-0.0584046009975865,-0.19201892672843043,0.9801724878485438,-0.048894026658251655,-0.1942173981373473,0.9801724878485438,-0.039261894064796174,-0.1959330144931829,0.9801724878485438,-0.02953215021650704,-0.1971615105033604,0.9801724878485438,-0.01972898478963035,-0.19789983193323402,0.9801724878485438,-0.00987676999802536,-0.19814614319939755,0.9801724878485438,-2.426590400325353e-17,-0.19789983193323402,0.9801724878485438,0.00987676999802531,-0.1971615105033604,0.9801724878485438,0.0197289847896303,-0.1959330144931829,0.9801724878485438,0.029532150216506993,-0.19421739813734734,0.9801724878485438,0.03926189406479612,-0.19201892672843046,0.9801724878485438,0.048894026658251606,-0.18934306601275075,0.9801724878485438,0.05840460099758637,-0.18619646860166178,0.9801724878485438,0.06776997229648642,-0.18258695743211217,0.9801724878485438,0.07696685676627449,-0.17852350631759184,0.9801724878485438,0.0859723895030777,-0.17401621763781755,0.9801724878485438,0.09476418133358126,-0.16907629722262532,0.9801724878485438,0.10332037447804097,-0.16371602649251185,0.9801724878485438,0.1116196968921674,-0.15794873192508802,0.9801724878485438,0.11964151515277979,-0.15178875192335578,0.9801724878485438,0.12736588575574748,-0.14525140116817922,0.9801724878485438,0.13477360469868283,-0.13835293254357472,0.9801724878485438,0.14184625522511768,-0.1311104967294807,0.9801724878485438,0.14856625361146117,-0.12354209956246463,0.9801724878485438,0.1549168928829075,-0.11566655727037699,0.9801724878485438,0.16088238434960728,-0.10750344969224286,0.9801724878485438,0.1664478968598389,-0.09907307159969869,0.9801724878485438,0.1715995936725875,-0.09039638224099115,0.9801724878485438,0.17632466685786413,-0.08149495323298356,0.9801724878485438,0.1806113691392377,-0.07239091493071843,0.9801724878485438,0.18444904309941446,-0.06310690140786682,0.9801724878485438,0.18782814767625736,-0.05366599418485566,0.9801724878485438,0.19074028188336936,-0.044091664844571596,0.9801724878485438,0.1931782056962693,-0.034407716678306006,0.9801724878485438,0.1951358580522339,-0.024638225507024325,0.9801724878485438,0.19660837191905428,-0.014807479825080095,0.9801724878485438,0.19759208639524511,-0.004939920415192281,0.9801724878485438,0.19808455581162235,0.004939920415192208,0.9801724878485438,0.19808455581162235,0.014807479825080024,0.9801724878485438,0.19759208639524511,0.024638225507024075,0.9801724878485438,0.19660837191905434,0.03440771667830593,0.9801724878485438,0.19513585805223393,0.044091664844571526,0.9801724878485438,0.1931782056962693,0.05366599418485576,0.9801724878485438,0.1907402818833693,0.06310690140786675,0.9801724878485438,0.18782814767625738,0.0723909149307182,0.9801724878485438,0.18444904309941454,0.08149495323298349,0.9801724878485438,0.18061136913923773,0.0903963822409911,0.9801724878485438,0.1763246668578642,0.0990730715996988,0.9801724878485438,0.17159959367258745,0.10750344969224294,0.9801724878485438,0.16644789685983885,0.1156665572703768,0.9801724878485438,0.16088238434960742,0.12354209956246459,0.9801724878485438,0.15491689288290753,0.13111049672948047,0.9801724878485438,0.14856625361146134,0.13835293254357472,0.9801724878485438,0.14184625522511768,0.14525140116817922,0.9801724878485438,0.13477360469868283,0.15178875192335572,0.9801724878485438,0.12736588575574753,0.157948731925088,0.9801724878485438,0.11964151515277986,0.16371602649251174,0.9801724878485438,0.11161969689216754,0.16907629722262532,0.9801724878485438,0.10332037447804097,0.1740162176378175,0.9801724878485438,0.0947641813335814,0.1785235063175918,0.9801724878485438,0.08597238950307777,0.18258695743211217,0.9801724878485438,0.07696685676627447,0.18619646860166172,0.9801724878485438,0.06776997229648657,0.18934306601275072,0.9801724878485438,0.05840460099758653,0.19201892672843043,0.9801724878485438,0.048894026658251676,0.1942173981373473,0.9801724878485438,0.039261894064796195,0.1959330144931829,0.9801724878485438,0.029532150216506976,0.1971615105033604,0.9801724878485438,0.01972898478963046,0.19789983193323402,0.9801724878485438,0.009876769998025383,0.19814614319939755,0.9801724878485438,0.0,0.14904226617617464,0.9888308262251285,0.0,0.14885699489760731,0.9888308262251285,-0.007429143758428793,0.1483016416754956,0.9888308262251285,-0.01483981749492223,0.1473775872054523,0.9888308262251285,-0.02221359710692966,0.14608712883248598,0.9888308262251285,-0.02953215021650703,0.14443347483943922,0.9888308262251285,-0.03677728174749692,0.142420736470692,0.9888308262251285,-0.043930979161354765,0.1400539177109605,0.9888308262251285,-0.050975457239157716,0.1373389028446024,0.9888308262251285,-0.05789320229846122,0.13428244182635907,0.9888308262251285,-0.06466701573507301,0.13089213349990447,0.9888308262251285,-0.07128005678149305,0.12717640670592248,0.9888308262251285,-0.0777158843757149,0.1231444993266808,0.9888308262251285,-0.08395849803629651,0.11880643531920027,0.9888308262251285,-0.08999237764207797,0.1141729997941182,0.9888308262251285,-0.09580252201764823,0.10925571220220404,0.9888308262251285,-0.1013744862286308,0.1040667976951897,0.9888308262251285,-0.10669441749406644,0.09861915673211619,0.9888308262251285,-0.11174908962660947,0.09292633300675976,0.9888308262251285,-0.11652593591491331,0.0870024797758752,0.9888308262251285,-0.12101308036645508,0.0808623246719696,0.9888308262251285,-0.12519936723312408,0.0745211330880873,0.9888308262251285,-0.12907438874616944,0.06799467022563774,0.9888308262251285,-0.13262851099155287,0.06129916189962041,0.9888308262251285,-0.1358528978613773,0.054451254198692546,0.9888308262251285,-0.13873953302184297,0.047467972100369786,0.9888308262251285,-0.1412812398431165,0.040366677144249526,0.9888308262251285,-0.14347169924156336,0.033165024268488,0.9888308262251285,-0.14530546538998557,0.025880917916842307,0.9888308262251285,-0.14677797925680597,0.018532467525402142,0.9888308262251285,-0.1478855799405392,0.011137942499678305,0.9888308262251285,-0.1486255137713695,0.0037157267939820393,0.9888308262251285,-0.14899594115720793,-0.003715726793982021,0.9888308262251285,-0.14899594115720793,-0.011137942499678286,0.9888308262251285,-0.1486255137713695,-0.01853246752540213,0.9888308262251285,-0.1478855799405392,-0.02588091791684229,0.9888308262251285,-0.14677797925680597,-0.033165024268487976,0.9888308262251285,-0.14530546538998557,-0.0403666771442495,0.9888308262251285,-0.1434716992415634,-0.04746797210036977,0.9888308262251285,-0.1412812398431165,-0.054451254198692525,0.9888308262251285,-0.138739533021843,-0.061299161899620376,0.9888308262251285,-0.13585289786137733,-0.06799467022563765,0.9888308262251285,-0.13262851099155293,-0.07452113308808735,0.9888308262251285,-0.12907438874616942,-0.0808623246719696,0.9888308262251285,-0.1251993672331241,-0.08700247977587516,0.9888308262251285,-0.12101308036645511,-0.09292633300675975,0.9888308262251285,-0.11652593591491332,-0.09861915673211614,0.9888308262251285,-0.1117490896266095,-0.10406679769518969,0.9888308262251285,-0.10669441749406645,-0.10925571220220404,0.9888308262251285,-0.1013744862286308,-0.11417299979411817,0.9888308262251285,-0.09580252201764827,-0.11880643531920027,0.9888308262251285,-0.08999237764207797,-0.12314449932668078,0.9888308262251285,-0.08395849803629654,-0.12717640670592245,0.9888308262251285,-0.07771588437571492,-0.1308921334999045,0.9888308262251285,-0.07128005678149302,-0.13428244182635907,0.9888308262251285,-0.06466701573507302,-0.1373389028446024,0.9888308262251285,-0.057893202298461205,-0.14005391771096048,0.9888308262251285,-0.05097545723915774,-0.142420736470692,0.9888308262251285,-0.04393097916135482,-0.1444334748394392,0.9888308262251285,-0.036777281747496955,-0.14608712883248598,0.9888308262251285,-0.029532150216507035,-0.1473775872054523,0.9888308262251285,-0.022213597106929703,-0.1483016416754956,0.9888308262251285,-0.01483981749492224,-0.14885699489760731,0.9888308262251285,-0.007429143758428841,-0.14904226617617464,0.9888308262251285,-1.825241342103201e-17,-0.14885699489760731,0.9888308262251285,0.007429143758428803,-0.1483016416754956,0.9888308262251285,0.014839817494922204,-0.1473775872054523,0.9888308262251285,0.022213597106929665,-0.14608712883248598,0.9888308262251285,0.029532150216506996,-0.14443347483943922,0.9888308262251285,0.03677728174749692,-0.14242073647069203,0.9888308262251285,0.04393097916135472,-0.1400539177109605,0.9888308262251285,0.05097545723915771,-0.1373389028446024,0.9888308262251285,0.05789320229846123,-0.13428244182635907,0.9888308262251285,0.064667015735073,-0.1308921334999045,0.9888308262251285,0.07128005678149299,-0.12717640670592248,0.9888308262251285,0.07771588437571487,-0.1231444993266808,0.9888308262251285,0.08395849803629651,-0.11880643531920028,0.9888308262251285,0.08999237764207793,-0.1141729997941182,0.9888308262251285,0.09580252201764823,-0.10925571220220406,0.9888308262251285,0.10137448622863077,-0.10406679769518971,0.9888308262251285,0.10669441749406643,-0.09861915673211623,0.9888308262251285,0.11174908962660943,-0.09292633300675976,0.9888308262251285,0.1165259359149133,-0.08700247977587529,0.9888308262251285,0.12101308036645503,-0.08086232467196956,0.9888308262251285,0.1251993672331241,-0.07452113308808726,0.9888308262251285,0.12907438874616944,-0.06799467022563775,0.9888308262251285,0.13262851099155287,-0.061299161899620404,0.9888308262251285,0.1358528978613773,-0.05445125419869265,0.9888308262251285,0.13873953302184294,-0.04746797210036987,0.9888308262251285,0.14128123984311647,-0.04036667714424944,0.9888308262251285,0.1434716992415634,-0.03316502426848802,0.9888308262251285,0.14530546538998557,-0.025880917916842293,0.9888308262251285,0.14677797925680597,-0.01853246752540223,0.9888308262251285,0.14788557994053916,-0.011137942499678356,0.9888308262251285,0.1486255137713695,-0.003715726793982057,0.9888308262251285,0.14899594115720793,0.0037157267939820025,0.9888308262251285,0.14899594115720793,0.011137942499678302,0.9888308262251285,0.1486255137713695,0.01853246752540204,0.9888308262251285,0.1478855799405392,0.02588091791684224,0.9888308262251285,0.14677797925680597,0.03316502426848796,0.9888308262251285,0.14530546538998557,0.04036667714424951,0.9888308262251285,0.14347169924156336,0.047467972100369814,0.9888308262251285,0.14128123984311647,0.05445125419869248,0.9888308262251285,0.138739533021843,0.061299161899620355,0.9888308262251285,0.13585289786137733,0.0679946702256377,0.9888308262251285,0.1326285109915529,0.07452113308808733,0.9888308262251285,0.12907438874616942,0.08086232467196963,0.9888308262251285,0.12519936723312408,0.08700247977587514,0.9888308262251285,0.12101308036645513,0.09292633300675972,0.9888308262251285,0.11652593591491332,0.09861915673211608,0.9888308262251285,0.11174908962660955,0.10406679769518971,0.9888308262251285,0.10669441749406643,0.10925571220220406,0.9888308262251285,0.10137448622863077,0.11417299979411816,0.9888308262251285,0.09580252201764829,0.11880643531920025,0.9888308262251285,0.08999237764207799,0.12314449932668073,0.9888308262251285,0.08395849803629661,0.1271764067059225,0.9888308262251285,0.07771588437571487,0.13089213349990445,0.9888308262251285,0.0712800567814931,0.13428244182635907,0.9888308262251285,0.06466701573507305,0.1373389028446024,0.9888308262251285,0.05789320229846122,0.14005391771096046,0.9888308262251285,0.05097545723915782,0.142420736470692,0.9888308262251285,0.04393097916135484,0.1444334748394392,0.9888308262251285,0.03677728174749697,0.14608712883248598,0.9888308262251285,0.029532150216507052,0.1473775872054523,0.9888308262251285,0.022213597106929654,0.14830164167549556,0.9888308262251285,0.014839817494922324,0.14885699489760731,0.9888308262251285,0.007429143758428858,0.14904226617617464,0.9888308262251285,0.0,0.09956784659581666,0.9950307753654014,0.0,0.0994440759184358,0.9950307753654014,-0.0049630474968969124,0.09907307159969879,0.9950307753654014,-0.00991375607572808,0.09845575601479824,0.9950307753654014,-0.01483981749492221,0.09759366390754379,0.9950307753654014,-0.01972898478963034,0.09648893857474715,0.9950307753654014,-0.02456910271961008,0.09514432653764857,0.9950307753654014,-0.029348137989068095,0.09356317071363225,0.9950307753654014,-0.03405420916332941,0.091749402105207,0.9950307753654014,-0.0386756162079561,0.08970753002691467,0.9950307753654014,-0.04320086957687636,0.08744263089446366,0.9950307753654014,-0.047618718777206394,0.08496033560395963,0.9950307753654014,-0.05191818033974832,0.08226681553261056,0.9950307753654014,-0.05608856512562525,0.07936876719571066,0.9950307753654014,-0.06011950490116467,0.0762733955980482,0.9950307753654014,-0.06400097811496101,0.07298839632112841,0.9950307753654014,-0.06772333481303153,0.06952193639074525,0.9950307753654014,-0.07127732063012225,0.06588263397246855,0.9950307753654014,-0.07465409979751823,0.062079536945526516,0.9950307753654014,-0.07784527711015672,0.05812210040835225,0.9950307753654014,-0.08084291879842971,0.054020163171719406,0.9950307753654014,-0.08363957225278529,0.04978392329790832,0.9950307753654014,-0.08622828455208917,0.04542391274671642,0.9950307753654014,-0.0886026197496822,0.04095097119134657,0.9950307753654014,-0.09075667487415778,0.036376219069271735,0.9950307753654014,-0.09268509460507893,0.03171102993507523,0.9950307753654014,-0.09438308458714857,0.026967002184001908,0.9950307753654014,-0.09584642334973233,0.022155930216520167,0.9950307753654014,-0.09707147280209963,0.017289775115584043,0.9950307753654014,-0.09805518727829043,0.01238063490949635,0.9950307753654014,-0.09879512110912077,0.0074407144943041265,0.9950307753654014,-0.09928943470250161,0.00248229529050405,0.9950307753654014,-0.09953689911695471,-0.0024822952905040377,0.9950307753654014,-0.09953689911695471,-0.007440714494304114,0.9950307753654014,-0.09928943470250161,-0.012380634909496337,0.9950307753654014,-0.09879512110912077,-0.017289775115584032,0.9950307753654014,-0.09805518727829043,-0.022155930216520157,0.9950307753654014,-0.09707147280209963,-0.02696700218400189,0.9950307753654014,-0.09584642334973234,-0.03171102993507522,0.9950307753654014,-0.09438308458714857,-0.03637621906927172,0.9950307753654014,-0.09268509460507894,-0.040950971191346545,0.9950307753654014,-0.0907566748741578,-0.045423912746716366,0.9950307753654014,-0.08860261974968221,-0.04978392329790835,0.9950307753654014,-0.08622828455208917,-0.054020163171719406,0.9950307753654014,-0.08363957225278529,-0.058122100408352216,0.9950307753654014,-0.08084291879842972,-0.0620795369455265,0.9950307753654014,-0.07784527711015672,-0.06588263397246853,0.9950307753654014,-0.07465409979751826,-0.06952193639074523,0.9950307753654014,-0.07127732063012227,-0.07298839632112841,0.9950307753654014,-0.06772333481303153,-0.07627339559804819,0.9950307753654014,-0.06400097811496104,-0.07936876719571066,0.9950307753654014,-0.06011950490116467,-0.08226681553261056,0.9950307753654014,-0.05608856512562528,-0.08496033560395962,0.9950307753654014,-0.05191818033974833,-0.08744263089446368,0.9950307753654014,-0.04761871877720638,-0.08970753002691466,0.9950307753654014,-0.043200869576876376,-0.091749402105207,0.9950307753654014,-0.03867561620795609,-0.09356317071363224,0.9950307753654014,-0.03405420916332943,-0.09514432653764855,0.9950307753654014,-0.029348137989068133,-0.09648893857474714,0.9950307753654014,-0.024569102719610103,-0.09759366390754379,0.9950307753654014,-0.019728984789630342,-0.09845575601479824,0.9950307753654014,-0.014839817494922239,-0.09907307159969879,0.9950307753654014,-0.009913756075728086,-0.0994440759184358,0.9950307753654014,-0.0049630474968969445,-0.09956784659581666,0.9950307753654014,-1.2193544463156156e-17,-0.0994440759184358,0.9950307753654014,0.004963047496896919,-0.09907307159969879,0.9950307753654014,0.009913756075728062,-0.09845575601479824,0.9950307753654014,0.014839817494922214,-0.09759366390754379,0.9950307753654014,0.019728984789630317,-0.09648893857474715,0.9950307753654014,0.02456910271961008,-0.09514432653764858,0.9950307753654014,0.029348137989068067,-0.09356317071363225,0.9950307753654014,0.03405420916332941,-0.091749402105207,0.9950307753654014,0.03867561620795611,-0.08970753002691467,0.9950307753654014,0.043200869576876355,-0.08744263089446368,0.9950307753654014,0.04761871877720636,-0.08496033560395963,0.9950307753654014,0.05191818033974831,-0.08226681553261056,0.9950307753654014,0.05608856512562525,-0.07936876719571068,0.9950307753654014,0.06011950490116465,-0.0762733955980482,0.9950307753654014,0.06400097811496101,-0.07298839632112843,0.9950307753654014,0.06772333481303151,-0.06952193639074526,0.9950307753654014,0.07127732063012225,-0.06588263397246859,0.9950307753654014,0.07465409979751822,-0.062079536945526516,0.9950307753654014,0.07784527711015671,-0.058122100408352306,0.9950307753654014,0.08084291879842967,-0.054020163171719385,0.9950307753654014,0.08363957225278529,-0.04978392329790829,0.9950307753654014,0.08622828455208918,-0.04542391274671643,0.9950307753654014,0.08860261974968218,-0.040950971191346566,0.9950307753654014,0.09075667487415778,-0.036376219069271805,0.9950307753654014,0.0926850946050789,-0.03171102993507528,0.9950307753654014,0.09438308458714856,-0.026967002184001852,0.9950307753654014,0.09584642334973235,-0.02215593021652018,0.9950307753654014,0.09707147280209963,-0.017289775115584036,0.9950307753654014,0.09805518727829043,-0.012380634909496405,0.9950307753654014,0.09879512110912075,-0.007440714494304159,0.9950307753654014,0.09928943470250161,-0.002482295290504062,0.9950307753654014,0.09953689911695471,0.002482295290504025,0.9950307753654014,0.09953689911695471,0.007440714494304124,0.9950307753654014,0.09928943470250161,0.012380634909496282,0.9950307753654014,0.09879512110912078,0.017289775115584,0.9950307753654014,0.09805518727829045,0.022155930216520146,0.9950307753654014,0.09707147280209963,0.0269670021840019,0.9950307753654014,0.09584642334973233,0.03171102993507525,0.9950307753654014,0.09438308458714857,0.03637621906927169,0.9950307753654014,0.09268509460507895,0.04095097119134653,0.9950307753654014,0.0907566748741578,0.045423912746716394,0.9950307753654014,0.0886026197496822,0.049783923297908345,0.9950307753654014,0.08622828455208917,0.054020163171719426,0.9950307753654014,0.08363957225278527,0.0581221004083522,0.9950307753654014,0.08084291879842974,0.06207953694552649,0.9950307753654014,0.07784527711015672,0.06588263397246849,0.9950307753654014,0.07465409979751829,0.06952193639074526,0.9950307753654014,0.07127732063012225,0.07298839632112843,0.9950307753654014,0.06772333481303151,0.07627339559804819,0.9950307753654014,0.06400097811496105,0.07936876719571065,0.9950307753654014,0.06011950490116468,0.08226681553261052,0.9950307753654014,0.05608856512562532,0.08496033560395964,0.9950307753654014,0.05191818033974831,0.08744263089446364,0.9950307753654014,0.04761871877720643,0.08970753002691466,0.9950307753654014,0.04320086957687638,0.091749402105207,0.9950307753654014,0.0386756162079561,0.09356317071363221,0.9950307753654014,0.034054209163329484,0.09514432653764855,0.9950307753654014,0.029348137989068143,0.09648893857474714,0.9950307753654014,0.024569102719610113,0.09759366390754379,0.9950307753654014,0.019728984789630352,0.09845575601479824,0.9950307753654014,0.014839817494922206,0.09907307159969879,0.9950307753654014,0.009913756075728142,0.0994440759184358,0.9950307753654014,0.004963047496896956,0.09956784659581666,0.9950307753654014,0.0,0.04984588566069742,0.9987569212189223,0.0,0.04978392329790858,0.9987569212189223,-0.0024846123172993077,0.0495981902577389,0.9987569212189223,-0.0049630474968969385,0.049289148301790715,0.9987569212189223,-0.0074291437584288215,0.048857565757059844,0.9987569212189223,-0.009876769998025349,0.048304515605753565,0.9987569212189223,-0.012299841031201617,0.04763137281768779,0.9987569212189223,-0.014692332721584857,0.046839810931895494,0.9987569212189223,-0.017048296957866627,0.045931797895944926,0.9987569212189223,-0.01936187644174473,0.04490959117331198,0.9987569212189223,-0.02162731925008967,0.043775732130970205,0.9987569212189223,-0.023838993135131853,0.04253303972115213,0.9987569212189223,-0.025991399527116963,0.04118460347298986,0.9987569212189223,-0.02807918720461674,0.03973377581145886,0.9987569212189223,-0.030097165598508575,0.03818416372272121,0.9987569212189223,-0.032040317696548234,0.03653961978658975,0.9987569212189223,-0.033903812516452775,0.03480423259840757,0.9987569212189223,-0.03568301711648364,0.032982316604155855,0.9987569212189223,-0.037373508113669844,0.031078401374061326,0.9987569212189223,-0.038971082681034976,0.02909722034137097,0.9987569212189223,-0.04047176899648727,0.027043699034291217,0.9987569212189223,-0.04187183611739515,0.024922942830348703,0.9987569212189223,-0.043167803256298445,0.022740224263617374,0.9987569212189223,-0.044356448434694454,0.020500969916367937,0.9987569212189223,-0.04543481649338421,0.018210746927729393,0.9987569212189223,-0.046400226439463794,0.015875249152903993,0.9987569212189223,-0.047250278111695194,0.013500283007345997,0.9987569212189223,-0.047982858147685256,0.011091753031098052,0.9987569212189223,-0.04859614523803764,0.008655647209174284,0.9987569212189223,-0.049088614654414865,0.006198022084486081,0.9987569212189223,-0.0494590420402533,0.003724987700322128,0.9987569212189223,-0.0497065064547064,0.0012426924098180823,0.9987569212189223,-0.04983039266224882,-0.0012426924098180763,0.9987569212189223,-0.04983039266224882,-0.003724987700322122,0.9987569212189223,-0.0497065064547064,-0.006198022084486075,0.9987569212189223,-0.0494590420402533,-0.00865564720917428,0.9987569212189223,-0.049088614654414865,-0.011091753031098047,0.9987569212189223,-0.04859614523803764,-0.013500283007345988,0.9987569212189223,-0.04798285814768526,-0.015875249152903986,0.9987569212189223,-0.047250278111695194,-0.01821074692772939,0.9987569212189223,-0.0464002264394638,-0.020500969916367923,0.9987569212189223,-0.04543481649338421,-0.02274022426361735,0.9987569212189223,-0.04435644843469447,-0.02492294283034872,0.9987569212189223,-0.04316780325629844,-0.027043699034291217,0.9987569212189223,-0.04187183611739515,-0.029097220341370956,0.9987569212189223,-0.04047176899648728,-0.03107840137406132,0.9987569212189223,-0.038971082681034976,-0.03298231660415584,0.9987569212189223,-0.03737350811366986,-0.034804232598407564,0.9987569212189223,-0.03568301711648365,-0.03653961978658975,0.9987569212189223,-0.033903812516452775,-0.0381841637227212,0.9987569212189223,-0.03204031769654825,-0.03973377581145886,0.9987569212189223,-0.030097165598508575,-0.041184603472989856,0.9987569212189223,-0.02807918720461675,-0.042533039721152126,0.9987569212189223,-0.025991399527116966,-0.043775732130970205,0.9987569212189223,-0.02383899313513185,-0.044909591173311975,0.9987569212189223,-0.021627319250089677,-0.04593179789594493,0.9987569212189223,-0.019361876441744724,-0.04683981093189549,0.9987569212189223,-0.017048296957866634,-0.047631372817687784,0.9987569212189223,-0.014692332721584876,-0.04830451560575356,0.9987569212189223,-0.012299841031201629,-0.048857565757059844,0.9987569212189223,-0.00987676999802535,-0.049289148301790715,0.9987569212189223,-0.007429143758428835,-0.0495981902577389,0.9987569212189223,-0.004963047496896942,-0.04978392329790858,0.9987569212189223,-0.0024846123172993237,-0.04984588566069742,0.9987569212189223,-6.104360432503805e-18,-0.04978392329790858,0.9987569212189223,0.002484612317299311,-0.0495981902577389,0.9987569212189223,0.004963047496896929,-0.049289148301790715,0.9987569212189223,0.007429143758428823,-0.04885756575705985,0.9987569212189223,0.009876769998025338,-0.048304515605753565,0.9987569212189223,0.012299841031201617,-0.0476313728176878,0.9987569212189223,0.014692332721584843,-0.046839810931895494,0.9987569212189223,0.017048296957866624,-0.045931797895944926,0.9987569212189223,0.01936187644174473,-0.04490959117331198,0.9987569212189223,0.021627319250089663,-0.043775732130970205,0.9987569212189223,0.02383899313513184,-0.04253303972115213,0.9987569212189223,0.025991399527116956,-0.04118460347298986,0.9987569212189223,0.02807918720461674,-0.03973377581145886,0.9987569212189223,0.030097165598508564,-0.03818416372272121,0.9987569212189223,0.032040317696548234,-0.036539619786589754,0.9987569212189223,0.03390381251645276,-0.03480423259840758,0.9987569212189223,0.03568301711648363,-0.03298231660415587,0.9987569212189223,0.03737350811366983,-0.031078401374061326,0.9987569212189223,0.03897108268103497,-0.029097220341370997,0.9987569212189223,0.04047176899648725,-0.027043699034291206,0.9987569212189223,0.04187183611739515,-0.02492294283034869,0.9987569212189223,0.04316780325629845,-0.02274022426361738,0.9987569212189223,0.044356448434694454,-0.020500969916367934,0.9987569212189223,0.04543481649338421,-0.01821074692772943,0.9987569212189223,0.04640022643946378,-0.01587524915290402,0.9987569212189223,0.04725027811169518,-0.01350028300734597,0.9987569212189223,0.04798285814768527,-0.01109175303109806,0.9987569212189223,0.04859614523803764,-0.008655647209174281,0.9987569212189223,0.049088614654414865,-0.00619802208448611,0.9987569212189223,0.049459042040253295,-0.0037249877003221444,0.9987569212189223,0.0497065064547064,-0.0012426924098180884,0.9987569212189223,0.04983039266224882,0.00124269240981807,0.9987569212189223,0.04983039266224882,0.0037249877003221266,0.9987569212189223,0.0497065064547064,0.006198022084486048,0.9987569212189223,0.04945904204025331,0.008655647209174262,0.9987569212189223,0.04908861465441487,0.01109175303109804,0.9987569212189223,0.04859614523803764,0.013500283007345995,0.9987569212189223,0.047982858147685256,0.015875249152904,0.9987569212189223,0.04725027811169519,0.018210746927729372,0.9987569212189223,0.04640022643946381,0.020500969916367916,0.9987569212189223,0.04543481649338421,0.022740224263617364,0.9987569212189223,0.04435644843469446,0.024922942830348717,0.9987569212189223,0.04316780325629844,0.027043699034291227,0.9987569212189223,0.041871836117395145,0.02909722034137095,0.9987569212189223,0.04047176899648728,0.03107840137406131,0.9987569212189223,0.038971082681034976,0.03298231660415582,0.9987569212189223,0.03737350811366987,0.03480423259840758,0.9987569212189223,0.03568301711648363,0.036539619786589754,0.9987569212189223,0.03390381251645276,0.038184163722721196,0.9987569212189223,0.032040317696548255,0.03973377581145885,0.9987569212189223,0.030097165598508578,0.04118460347298984,0.9987569212189223,0.02807918720461677,0.04253303972115213,0.9987569212189223,0.025991399527116956,0.04377573213097019,0.9987569212189223,0.023838993135131874,0.044909591173311975,0.9987569212189223,0.02162731925008968,0.045931797895944926,0.9987569212189223,0.01936187644174473,0.04683981093189547,0.9987569212189223,0.017048296957866662,0.047631372817687784,0.9987569212189223,0.014692332721584883,0.04830451560575356,0.9987569212189223,0.012299841031201634,0.048857565757059844,0.9987569212189223,0.009876769998025356,0.049289148301790715,0.9987569212189223,0.007429143758428819,0.04959819025773889,0.9987569212189223,0.00496304749689697,0.04978392329790858,0.9987569212189223,0.00248461231729933,0.04984588566069742,0.9987569212189223,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0,0.0,1.0,0.0],"revision":0,"triangleFaceIds":[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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/torus.brep.json b/main/opengeometry/tests/fixtures/cases/torus.brep.json new file mode 100644 index 0000000..d7612c4 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/torus.brep.json @@ -0,0 +1 @@ +{"schema_version":2,"id":"torus","revision":0,"geometry":{"surfaces":[{"kind":"Torus","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"major_radius":2.0,"minor_radius":0.5}],"curves":[{"kind":"Circle","frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"radius":2.5},{"kind":"Circle","frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"radius":0.5}],"pcurves":[{"kind":"Line2","origin":[0.0,0.0],"direction":[1.0,0.0]},{"kind":"Line2","origin":[6.283185307179586,0.0],"direction":[0.0,1.0]},{"kind":"Line2","origin":[0.0,6.283185307179586],"direction":[1.0,0.0]},{"kind":"Line2","origin":[0.0,0.0],"direction":[0.0,1.0]}],"intersections":[]},"topology":{"vertices":[{"id":0,"position":[2.5,0.0,0.0],"outgoing_halfedge":0,"tolerance":1e-8}],"edges":[{"id":0,"geometry":{"kind":"Curve","curve":0,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":0,"twin_halfedge":2,"tolerance":1e-8,"chart_seam":true},{"id":1,"geometry":{"kind":"Curve","curve":1,"range":{"lo":0.0,"hi":6.283185307179586}},"halfedge":1,"twin_halfedge":3,"tolerance":1e-8,"chart_seam":true}],"halfedges":[{"id":0,"from":0,"to":0,"twin":2,"next":1,"prev":3,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":0,"periodic_lift":[0,0]}},{"id":1,"from":0,"to":0,"twin":3,"next":2,"prev":0,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Forward","pcurve":1,"periodic_lift":[0,0]}},{"id":2,"from":0,"to":0,"twin":0,"next":3,"prev":1,"edge":0,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":2,"periodic_lift":[0,0]}},{"id":3,"from":0,"to":0,"twin":1,"next":0,"prev":2,"edge":1,"face":0,"loop_ref":0,"wire_ref":null,"geometry_use":{"sense":"Reverse","pcurve":3,"periodic_lift":[0,0]}}],"loops":[{"id":0,"start_halfedge":0,"face_ref":0,"is_hole":false}],"faces":[{"id":0,"key":"skin","surface":0,"sense":"Forward","trim":{"chart":0,"uv_bounds":[{"lo":0.0,"hi":6.283185307179586},{"lo":0.0,"hi":6.283185307179586}],"outer":0,"holes":[]},"shell_ref":0,"provenance":{"sources":[{"entity":"torus","body":"torus","key":"skin","face":0}],"role":"Authored","reversed":false}}],"wires":[],"shells":[{"id":0,"faces":[0],"is_closed":true}]},"solids":[{"outer_shell":0,"cavity_shells":[]}],"accuracy":{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6},"quality":{"kind":"Analytic"}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/torus.step.m b/main/opengeometry/tests/fixtures/cases/torus.step.m new file mode 100644 index 0000000..33bf923 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/torus.step.m @@ -0,0 +1,79 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('torus','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=TOROIDAL_SURFACE('',#5,2.00000000000000000E0,5.00000000000000000E-1); +#7=CARTESIAN_POINT('',(2.50000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#8=VERTEX_POINT('',#7); +#9=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#11=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#12=AXIS2_PLACEMENT_3D('',#9,#10,#11); +#13=CIRCLE('',#12,2.50000000000000000E0); +#14=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#15=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#16=VECTOR('',#15,1.00000000000000000E0); +#17=LINE('',#14,#16); +#18=DEFINITIONAL_REPRESENTATION('',(#17),#1); +#19=PCURVE('',#6,#18); +#20=CARTESIAN_POINT('',(0.00000000000000000E0,6.28318530717958623E0)); +#21=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#22=VECTOR('',#21,1.00000000000000000E0); +#23=LINE('',#20,#22); +#24=DEFINITIONAL_REPRESENTATION('',(#23),#1); +#25=PCURVE('',#6,#24); +#26=SEAM_CURVE('',#13,(#19,#25),.CURVE_3D.); +#27=EDGE_CURVE('',#8,#8,#26,.T.); +#28=CARTESIAN_POINT('',(2.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#30=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#31=AXIS2_PLACEMENT_3D('',#28,#29,#30); +#32=CIRCLE('',#31,5.00000000000000000E-1); +#33=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#34=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#35=VECTOR('',#34,1.00000000000000000E0); +#36=LINE('',#33,#35); +#37=DEFINITIONAL_REPRESENTATION('',(#36),#1); +#38=PCURVE('',#6,#37); +#39=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#40=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#41=VECTOR('',#40,1.00000000000000000E0); +#42=LINE('',#39,#41); +#43=DEFINITIONAL_REPRESENTATION('',(#42),#1); +#44=PCURVE('',#6,#43); +#45=SEAM_CURVE('',#32,(#38,#44),.CURVE_3D.); +#46=EDGE_CURVE('',#8,#8,#45,.T.); +#47=ORIENTED_EDGE('',*,*,#27,.T.); +#48=ORIENTED_EDGE('',*,*,#46,.T.); +#49=ORIENTED_EDGE('',*,*,#27,.F.); +#50=ORIENTED_EDGE('',*,*,#46,.F.); +#51=EDGE_LOOP('',(#47,#48,#49,#50)); +#52=FACE_OUTER_BOUND('',#51,.T.); +#53=ADVANCED_FACE('skin',(#52),#6,.T.); +#54=CLOSED_SHELL('',(#53)); +#55=MANIFOLD_SOLID_BREP('torus-0',#54); +#56=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT($,.METRE.)); +#57=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#58=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#59=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000355271367882E-8),#56,'distance_accuracy_value','exchange error bound'); +#60=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#59)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#56,#57,#58)) REPRESENTATION_CONTEXT('','')); +#61=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#55),#60); +#62=APPLICATION_CONTEXT('automotive design'); +#63=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#62); +#64=PRODUCT_CONTEXT('',#62,'mechanical'); +#65=PRODUCT('torus','torus','',(#64)); +#66=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#65,.NOT_KNOWN.); +#67=PRODUCT_DEFINITION_CONTEXT('part definition',#62,'design'); +#68=PRODUCT_DEFINITION('','',#66,#67); +#69=PRODUCT_DEFINITION_SHAPE('','',#68); +#70=SHAPE_DEFINITION_REPRESENTATION(#69,#61); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/torus.step.m.report.json b/main/opengeometry/tests/fixtures/cases/torus.step.m.report.json new file mode 100644 index 0000000..5863cba --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/torus.step.m.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":2,"exchange_error_bound":1.0000035527136788e-8,"faces":1,"geometric_tolerance":1e-8,"length_unit":"metre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/torus.step.mm b/main/opengeometry/tests/fixtures/cases/torus.step.mm new file mode 100644 index 0000000..cce96de --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/torus.step.mm @@ -0,0 +1,79 @@ +ISO-10303-21; +HEADER; +FILE_DESCRIPTION(('OpenGeometry authoritative analytic BRep v2'),'2;1'); +FILE_NAME('torus','1970-01-01T00:00:00',('OpenGeometry'),('OpenGeometry'),'OpenGeometry','OpenGeometry',''); +FILE_SCHEMA(('AUTOMOTIVE_DESIGN')); +ENDSEC; +DATA; +#1=(GEOMETRIC_REPRESENTATION_CONTEXT(2) REPRESENTATION_CONTEXT('','')); +#2=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#3=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#4=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#5=AXIS2_PLACEMENT_3D('',#2,#3,#4); +#6=TOROIDAL_SURFACE('',#5,2.00000000000000000E3,5.00000000000000000E2); +#7=CARTESIAN_POINT('',(2.50000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#8=VERTEX_POINT('',#7); +#9=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#10=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0,0.00000000000000000E0)); +#11=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#12=AXIS2_PLACEMENT_3D('',#9,#10,#11); +#13=CIRCLE('',#12,2.50000000000000000E3); +#14=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#15=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#16=VECTOR('',#15,1.00000000000000000E0); +#17=LINE('',#14,#16); +#18=DEFINITIONAL_REPRESENTATION('',(#17),#1); +#19=PCURVE('',#6,#18); +#20=CARTESIAN_POINT('',(0.00000000000000000E0,6.28318530717958623E0)); +#21=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0)); +#22=VECTOR('',#21,1.00000000000000000E0); +#23=LINE('',#20,#22); +#24=DEFINITIONAL_REPRESENTATION('',(#23),#1); +#25=PCURVE('',#6,#24); +#26=SEAM_CURVE('',#13,(#19,#25),.CURVE_3D.); +#27=EDGE_CURVE('',#8,#8,#26,.T.); +#28=CARTESIAN_POINT('',(2.00000000000000000E3,0.00000000000000000E0,0.00000000000000000E0)); +#29=DIRECTION('',(-0.00000000000000000E0,-0.00000000000000000E0,1.00000000000000000E0)); +#30=DIRECTION('',(1.00000000000000000E0,0.00000000000000000E0,0.00000000000000000E0)); +#31=AXIS2_PLACEMENT_3D('',#28,#29,#30); +#32=CIRCLE('',#31,5.00000000000000000E2); +#33=CARTESIAN_POINT('',(6.28318530717958623E0,0.00000000000000000E0)); +#34=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#35=VECTOR('',#34,1.00000000000000000E0); +#36=LINE('',#33,#35); +#37=DEFINITIONAL_REPRESENTATION('',(#36),#1); +#38=PCURVE('',#6,#37); +#39=CARTESIAN_POINT('',(0.00000000000000000E0,0.00000000000000000E0)); +#40=DIRECTION('',(0.00000000000000000E0,1.00000000000000000E0)); +#41=VECTOR('',#40,1.00000000000000000E0); +#42=LINE('',#39,#41); +#43=DEFINITIONAL_REPRESENTATION('',(#42),#1); +#44=PCURVE('',#6,#43); +#45=SEAM_CURVE('',#32,(#38,#44),.CURVE_3D.); +#46=EDGE_CURVE('',#8,#8,#45,.T.); +#47=ORIENTED_EDGE('',*,*,#27,.T.); +#48=ORIENTED_EDGE('',*,*,#46,.T.); +#49=ORIENTED_EDGE('',*,*,#27,.F.); +#50=ORIENTED_EDGE('',*,*,#46,.F.); +#51=EDGE_LOOP('',(#47,#48,#49,#50)); +#52=FACE_OUTER_BOUND('',#51,.T.); +#53=ADVANCED_FACE('skin',(#52),#6,.T.); +#54=CLOSED_SHELL('',(#53)); +#55=MANIFOLD_SOLID_BREP('torus-0',#54); +#56=(LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.)); +#57=(NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.)); +#58=(NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT()); +#59=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.00000355271367888E-5),#56,'distance_accuracy_value','exchange error bound'); +#60=(GEOMETRIC_REPRESENTATION_CONTEXT(3) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#59)) GLOBAL_UNIT_ASSIGNED_CONTEXT((#56,#57,#58)) REPRESENTATION_CONTEXT('','')); +#61=ADVANCED_BREP_SHAPE_REPRESENTATION('',(#55),#60); +#62=APPLICATION_CONTEXT('automotive design'); +#63=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#62); +#64=PRODUCT_CONTEXT('',#62,'mechanical'); +#65=PRODUCT('torus','torus','',(#64)); +#66=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('1','',#65,.NOT_KNOWN.); +#67=PRODUCT_DEFINITION_CONTEXT('part definition',#62,'design'); +#68=PRODUCT_DEFINITION('','',#66,#67); +#69=PRODUCT_DEFINITION_SHAPE('','',#68); +#70=SHAPE_DEFINITION_REPRESENTATION(#69,#61); +ENDSEC; +END-ISO-10303-21; diff --git a/main/opengeometry/tests/fixtures/cases/torus.step.mm.report.json b/main/opengeometry/tests/fixtures/cases/torus.step.mm.report.json new file mode 100644 index 0000000..bad0075 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/torus.step.mm.report.json @@ -0,0 +1 @@ +{"cavity_shells":0,"collapsed_chart_uses":0,"edges":2,"exchange_error_bound":0.000010000035527136789,"faces":1,"geometric_tolerance":0.00001,"length_unit":"millimetre","quality":{"kind":"Analytic"},"revision":"0","schema_version":2,"solids":1,"validation_level":"v2 structure, sampled residuals and Part-21 references; external schema/import validation pending"} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/torus.tess.json b/main/opengeometry/tests/fixtures/cases/torus.tess.json new file mode 100644 index 0000000..5045e01 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/torus.tess.json @@ -0,0 +1 @@ +{"achievedDeflection":0.01,"indices":[0,1,156,0,156,155,1,2,157,1,157,156,2,3,158,2,158,157,3,4,159,3,159,158,4,5,160,4,160,159,5,6,161,5,161,160,6,7,162,6,162,161,7,8,163,7,163,162,8,9,164,8,164,163,9,10,165,9,165,164,10,11,166,10,166,165,11,12,167,11,167,166,12,13,168,12,168,167,13,14,169,13,169,168,14,15,170,14,170,169,15,16,171,15,171,170,16,17,172,16,172,171,17,18,173,17,173,172,18,19,174,18,174,173,19,20,175,19,175,174,20,21,176,20,176,175,21,22,177,21,177,176,22,23,178,22,178,177,23,24,179,23,179,178,24,25,180,24,180,179,25,26,181,25,181,180,26,27,182,26,182,181,27,28,183,27,183,182,28,29,184,28,184,183,29,30,185,29,185,184,30,31,186,30,186,185,31,32,187,31,187,186,32,33,188,32,188,187,33,34,189,33,189,188,34,35,190,34,190,189,35,36,191,35,191,190,36,37,192,36,192,191,37,38,193,37,193,192,38,39,194,38,194,193,39,40,195,39,195,194,40,41,196,40,196,195,41,42,197,41,197,196,42,43,198,42,198,197,43,44,199,43,199,198,44,45,200,44,200,199,45,46,201,45,201,200,46,47,202,46,202,201,47,48,203,47,203,202,48,49,204,48,204,203,49,50,205,49,205,204,50,51,206,50,206,205,51,52,207,51,207,206,52,53,208,52,208,207,53,54,209,53,209,208,54,55,210,54,210,209,55,56,211,55,211,210,56,57,212,56,212,211,57,58,213,57,213,212,58,59,214,58,214,213,59,60,215,59,215,214,60,61,216,60,216,215,61,62,217,61,217,216,62,63,218,62,218,217,63,64,219,63,219,218,64,65,220,64,220,219,65,66,221,65,221,220,66,67,222,66,222,221,67,68,223,67,223,222,68,69,224,68,224,223,69,70,225,69,225,224,70,71,226,70,226,225,71,72,227,71,227,226,72,73,228,72,228,227,73,74,229,73,229,228,74,75,230,74,230,229,75,76,231,75,231,230,76,77,232,76,232,231,77,78,233,77,233,232,78,79,234,78,234,233,79,80,235,79,235,234,80,81,236,80,236,235,81,82,237,81,237,236,82,83,238,82,238,237,83,84,239,83,239,238,84,85,240,84,240,239,85,86,241,85,241,240,86,87,242,86,242,241,87,88,243,87,243,242,88,89,244,88,244,243,89,90,245,89,245,244,90,91,246,90,246,245,91,92,247,91,247,246,92,93,248,92,248,247,93,94,249,93,249,248,94,95,250,94,250,249,95,96,251,95,251,250,96,97,252,96,252,251,97,98,253,97,253,252,98,99,254,98,254,253,99,100,255,99,255,254,100,101,256,100,256,255,101,102,257,101,257,256,102,103,258,102,258,257,103,104,259,103,259,258,104,105,260,104,260,259,105,106,261,105,261,260,106,107,262,106,262,261,107,108,263,107,263,262,108,109,264,108,264,263,109,110,265,109,265,264,110,111,266,110,266,265,111,112,267,111,267,266,112,113,268,112,268,267,113,114,269,113,269,268,114,115,270,114,270,269,115,116,271,115,271,270,116,117,272,116,272,271,117,118,273,117,273,272,118,119,274,118,274,273,119,120,275,119,275,274,120,121,276,120,276,275,121,122,277,121,277,276,122,123,278,122,278,277,123,124,279,123,279,278,124,125,280,124,280,279,125,126,281,125,281,280,126,127,282,126,282,281,127,128,283,127,283,282,128,129,284,128,284,283,129,130,285,129,285,284,130,131,286,130,286,285,131,132,287,131,287,286,132,133,288,132,288,287,133,134,289,133,289,288,134,135,290,134,290,289,135,136,291,135,291,290,136,137,292,136,292,291,137,138,293,137,293,292,138,139,294,138,294,293,139,140,295,139,295,294,140,141,296,140,296,295,141,142,297,141,297,296,142,143,298,142,298,297,143,144,299,143,299,298,144,145,300,144,300,299,145,146,301,145,301,300,146,147,302,146,302,301,147,148,303,147,303,302,148,149,304,148,304,303,149,150,305,149,305,304,150,151,306,150,306,305,151,152,307,151,307,306,152,153,308,152,308,307,153,154,309,153,309,308,155,156,311,155,311,310,156,157,312,156,312,311,157,158,313,157,313,312,158,159,314,158,314,313,159,160,315,159,315,314,160,161,316,160,316,315,161,162,317,161,317,316,162,163,318,162,318,317,163,164,319,163,319,318,164,165,320,164,320,319,165,166,321,165,321,320,166,167,322,166,322,321,167,168,323,167,323,322,168,169,324,168,324,323,169,170,325,169,325,324,170,171,326,170,326,325,171,172,327,171,327,326,172,173,328,172,328,327,173,174,329,173,329,328,174,175,330,174,330,329,175,176,331,175,331,330,176,177,332,176,332,331,177,178,333,177,333,332,178,179,334,178,334,333,179,180,335,179,335,334,180,181,336,180,336,335,181,182,337,181,337,336,182,183,338,182,338,337,183,184,339,183,339,338,184,185,340,184,340,339,185,186,341,185,341,340,186,187,342,186,342,341,187,188,343,187,343,342,188,189,344,188,344,343,189,190,345,189,345,344,190,191,346,190,346,345,191,192,347,191,347,346,192,193,348,192,348,347,193,194,349,193,349,348,194,195,350,194,350,349,195,196,351,195,351,350,196,197,352,196,352,351,197,198,353,197,353,352,198,199,354,198,354,353,199,200,355,199,355,354,200,201,356,200,356,355,201,202,357,201,357,356,202,203,358,202,358,357,203,204,359,203,359,358,204,205,360,204,360,359,205,206,361,205,361,360,206,207,362,206,362,361,207,208,363,207,363,362,208,209,364,208,364,363,209,210,365,209,365,364,210,211,366,210,366,365,211,212,367,211,367,366,212,213,368,212,368,367,213,214,369,213,369,368,214,215,370,214,370,369,215,216,371,215,371,370,216,217,372,216,372,371,217,218,373,217,373,372,218,219,374,218,374,373,219,220,375,219,375,374,220,221,376,220,376,375,221,222,377,221,377,376,222,223,378,222,378,377,223,224,379,223,379,378,224,225,380,224,380,379,225,226,381,225,381,380,226,227,382,226,382,381,227,228,383,227,383,382,228,229,384,228,384,383,229,230,385,229,385,384,230,231,386,230,386,385,231,232,387,231,387,386,232,233,388,232,388,387,233,234,389,233,389,388,234,235,390,234,390,389,235,236,391,235,391,390,236,237,392,236,392,391,237,238,393,237,393,392,238,239,394,238,394,393,239,240,395,239,395,394,240,241,396,240,396,395,241,242,397,241,397,396,242,243,398,242,398,397,243,244,399,243,399,398,244,245,400,244,400,399,245,246,401,245,401,400,246,247,402,246,402,401,247,248,403,247,403,402,248,249,404,248,404,403,249,250,405,249,405,404,250,251,406,250,406,405,251,252,407,251,407,406,252,253,408,252,408,407,253,254,409,253,409,408,254,255,410,254,410,409,255,256,411,255,411,410,256,257,412,256,412,411,257,258,413,257,413,412,258,259,414,258,414,413,259,260,415,259,415,414,260,261,416,260,416,415,261,262,417,261,417,416,262,263,418,262,418,417,263,264,419,263,419,418,264,265,420,264,420,419,265,266,421,265,421,420,266,267,422,266,422,421,267,268,423,267,423,422,268,269,424,268,424,423,269,270,425,269,425,424,270,271,426,270,426,425,271,272,427,271,427,426,272,273,428,272,428,427,273,274,429,273,429,428,274,275,430,274,430,429,275,276,431,275,431,430,276,277,432,276,432,431,277,278,433,277,433,432,278,279,434,278,434,433,279,280,435,279,435,434,280,281,436,280,436,435,281,282,437,281,437,436,282,283,438,282,438,437,283,284,439,283,439,438,284,285,440,284,440,439,285,286,441,285,441,440,286,287,442,286,442,441,287,288,443,287,443,442,288,289,444,288,444,443,289,290,445,289,445,444,290,291,446,290,446,445,291,292,447,291,447,446,292,293,448,292,448,447,293,294,449,293,449,448,294,295,450,294,450,449,295,296,451,295,451,450,296,297,452,296,452,451,297,298,453,297,453,452,298,299,454,298,454,453,299,300,455,299,455,454,300,301,456,300,456,455,301,302,457,301,457,456,302,303,458,302,458,457,303,304,459,303,459,458,304,305,460,304,460,459,305,306,461,305,461,460,306,307,462,306,462,461,307,308,463,307,463,462,308,309,464,308,464,463,310,311,466,310,466,465,311,312,467,311,467,466,312,313,468,312,468,467,313,314,469,313,469,468,314,315,470,314,470,469,315,316,471,315,471,470,316,317,472,316,472,471,317,318,473,317,473,472,318,319,474,318,474,473,319,320,475,319,475,474,320,321,476,320,476,475,321,322,477,321,477,476,322,323,478,322,478,477,323,324,479,323,479,478,324,325,480,324,480,479,325,326,481,325,481,480,326,327,482,326,482,481,327,328,483,327,483,482,328,329,484,328,484,483,329,330,485,329,485,484,330,331,486,330,486,485,331,332,487,331,487,486,332,333,488,332,488,487,333,334,489,333,489,488,334,335,490,334,490,489,335,336,491,335,491,490,336,337,492,336,492,491,337,338,493,337,493,492,338,339,494,338,494,493,339,340,495,339,495,494,340,341,496,340,496,495,341,342,497,341,497,496,342,343,498,342,498,497,343,344,499,343,499,498,344,345,500,344,500,499,345,346,501,345,501,500,346,347,502,346,502,501,347,348,503,347,503,502,348,349,504,348,504,503,349,350,505,349,505,504,350,351,506,350,506,505,351,352,507,351,507,506,352,353,508,352,508,507,353,354,509,353,509,508,354,355,510,354,510,509,355,356,511,355,511,510,356,357,512,356,512,511,357,358,513,357,513,512,358,359,514,358,514,513,359,360,515,359,515,514,360,361,516,360,516,515,361,362,517,361,517,516,362,363,518,362,518,517,363,364,519,363,519,518,364,365,520,364,520,519,365,366,521,365,521,520,366,367,522,366,522,521,367,368,523,367,523,522,368,369,524,368,524,523,369,370,525,369,525,524,370,371,526,370,526,525,371,372,527,371,527,526,372,373,528,372,528,527,373,374,529,373,529,528,374,375,530,374,530,529,375,376,531,375,531,530,376,377,532,376,532,531,377,378,533,377,533,532,378,379,534,378,534,533,379,380,535,379,535,534,380,381,536,380,536,535,381,382,537,381,537,536,382,383,538,382,538,537,383,384,539,383,539,538,384,385,540,384,540,539,385,386,541,385,541,540,386,387,542,386,542,541,387,388,543,387,543,542,388,389,544,388,544,543,389,390,545,389,545,544,390,391,546,390,546,545,391,392,547,391,547,546,392,393,548,392,548,547,393,394,549,393,549,548,394,395,550,394,550,549,395,396,551,395,551,550,396,397,552,396,552,551,397,398,553,397,553,552,398,399,554,398,554,553,399,400,555,399,555,554,400,401,556,400,556,555,401,402,557,401,557,556,402,403,558,402,558,557,403,404,559,403,559,558,404,405,560,404,560,559,405,406,561,405,561,560,406,407,562,406,562,561,407,408,563,407,563,562,408,409,564,408,564,563,409,410,565,409,565,564,410,411,566,410,566,565,411,412,567,411,567,566,412,413,568,412,568,567,413,414,569,413,569,568,414,415,570,414,570,569,415,416,571,415,571,570,416,417,572,416,572,571,417,418,573,417,573,572,418,419,574,418,574,573,419,420,575,419,575,574,420,421,576,420,576,575,421,422,577,421,577,576,422,423,578,422,578,577,423,424,579,423,579,578,424,425,580,424,580,579,425,426,581,425,581,580,426,427,582,426,582,581,427,428,583,427,583,582,428,429,584,428,584,583,429,430,585,429,585,584,430,431,586,430,586,585,431,432,587,431,587,586,432,433,588,432,588,587,433,434,589,433,589,588,434,435,590,434,590,589,435,436,591,435,591,590,436,437,592,436,592,591,437,438,593,437,593,592,438,439,594,438,594,593,439,440,595,439,595,594,440,441,596,440,596,595,441,442,597,441,597,596,442,443,598,442,598,597,443,444,599,443,599,598,444,445,600,444,600,599,445,446,601,445,601,600,446,447,602,446,602,601,447,448,603,447,603,602,448,449,604,448,604,603,449,450,605,449,605,604,450,451,606,450,606,605,451,452,607,451,607,606,452,453,608,452,608,607,453,454,609,453,609,608,454,455,610,454,610,609,455,456,611,455,611,610,456,457,612,456,612,611,457,458,613,457,613,612,458,459,614,458,614,613,459,460,615,459,615,614,460,461,616,460,616,615,461,462,617,461,617,616,462,463,618,462,618,617,463,464,619,463,619,618,465,466,621,465,621,620,466,467,622,466,622,621,467,468,623,467,623,622,468,469,624,468,624,623,469,470,625,469,625,624,470,471,626,470,626,625,471,472,627,471,627,626,472,473,628,472,628,627,473,474,629,473,629,628,474,475,630,474,630,629,475,476,631,475,631,630,476,477,632,476,632,631,477,478,633,477,633,632,478,479,634,478,634,633,479,480,635,479,635,634,480,481,636,480,636,635,481,482,637,481,637,636,482,483,638,482,638,637,483,484,639,483,639,638,484,485,640,484,640,639,485,486,641,485,641,640,486,487,642,486,642,641,487,488,643,487,643,642,488,489,644,488,644,643,489,490,645,489,645,644,490,491,646,490,646,645,491,492,647,491,647,646,492,493,648,492,648,647,493,494,649,493,649,648,494,495,650,494,650,649,495,496,651,495,651,650,496,497,652,496,652,651,497,498,653,497,653,652,498,499,654,498,654,653,499,500,655,499,655,654,500,501,656,500,656,655,501,502,657,501,657,656,502,503,658,502,658,657,503,504,659,503,659,658,504,505,660,504,660,659,505,506,661,505,661,660,506,507,662,506,662,661,507,508,663,507,663,662,508,509,664,508,664,663,509,510,665,509,665,664,510,511,666,510,666,665,511,512,667,511,667,666,512,513,668,512,668,667,513,514,669,513,669,668,514,515,670,514,670,669,515,516,671,515,671,670,516,517,672,516,672,671,517,518,673,517,673,672,518,519,674,518,674,673,519,520,675,519,675,674,520,521,676,520,676,675,521,522,677,521,677,676,522,523,678,522,678,677,523,524,679,523,679,678,524,525,680,524,680,679,525,526,681,525,681,680,526,527,682,526,682,681,527,528,683,527,683,682,528,529,684,528,684,683,529,530,685,529,685,684,530,531,686,530,686,685,531,532,687,531,687,686,532,533,688,532,688,687,533,534,689,533,689,688,534,535,690,534,690,689,535,536,691,535,691,690,536,537,692,536,692,691,537,538,693,537,693,692,538,539,694,538,694,693,539,540,695,539,695,694,540,541,696,540,696,695,541,542,697,541,697,696,542,543,698,542,698,697,543,544,699,543,699,698,544,545,700,544,700,699,545,546,701,545,701,700,546,547,702,546,702,701,547,548,703,547,703,702,548,549,704,548,704,703,549,550,705,549,705,704,550,551,706,550,706,705,551,552,707,551,707,706,552,553,708,552,708,707,553,554,709,553,709,708,554,555,710,554,710,709,555,556,711,555,711,710,556,557,712,556,712,711,557,558,713,557,713,712,558,559,714,558,714,713,559,560,715,559,715,714,560,561,716,560,716,715,561,562,717,561,717,716,562,563,718,562,718,717,563,564,719,563,719,718,564,565,720,564,720,719,565,566,721,565,721,720,566,567,722,566,722,721,567,568,723,567,723,722,568,569,724,568,724,723,569,570,725,569,725,724,570,571,726,570,726,725,571,572,727,571,727,726,572,573,728,572,728,727,573,574,729,573,729,728,574,575,730,574,730,729,575,576,731,575,731,730,576,577,732,576,732,731,577,578,733,577,733,732,578,579,734,578,734,733,579,580,735,579,735,734,580,581,736,580,736,735,581,582,737,581,737,736,582,583,738,582,738,737,583,584,739,583,739,738,584,585,740,584,740,739,585,586,741,585,741,740,586,587,742,586,742,741,587,588,743,587,743,742,588,589,744,588,744,743,589,590,745,589,745,744,590,591,746,590,746,745,591,592,747,591,747,746,592,593,748,592,748,747,593,594,749,593,749,748,594,595,750,594,750,749,595,596,751,595,751,750,596,597,752,596,752,751,597,598,753,597,753,752,598,599,754,598,754,753,599,600,755,599,755,754,600,601,756,600,756,755,601,602,757,601,757,756,602,603,758,602,758,757,603,604,759,603,759,758,604,605,760,604,760,759,605,606,761,605,761,760,606,607,762,606,762,761,607,608,763,607,763,762,608,609,764,608,764,763,609,610,765,609,765,764,610,611,766,610,766,765,611,612,767,611,767,766,612,613,768,612,768,767,613,614,769,613,769,768,614,615,770,614,770,769,615,616,771,615,771,770,616,617,772,616,772,771,617,618,773,617,773,772,618,619,774,618,774,773,620,621,776,620,776,775,621,622,777,621,777,776,622,623,778,622,778,777,623,624,779,623,779,778,624,625,780,624,780,779,625,626,781,625,781,780,626,627,782,626,782,781,627,628,783,627,783,782,628,629,784,628,784,783,629,630,785,629,785,784,630,631,786,630,786,785,631,632,787,631,787,786,632,633,788,632,788,787,633,634,789,633,789,788,634,635,790,634,790,789,635,636,791,635,791,790,636,637,792,636,792,791,637,638,793,637,793,792,638,639,794,638,794,793,639,640,795,639,795,794,640,641,796,640,796,795,641,642,797,641,797,796,642,643,798,642,798,797,643,644,799,643,799,798,644,645,800,644,800,799,645,646,801,645,801,800,646,647,802,646,802,801,647,648,803,647,803,802,648,649,804,648,804,803,649,650,805,649,805,804,650,651,806,650,806,805,651,652,807,651,807,806,652,653,808,652,808,807,653,654,809,653,809,808,654,655,810,654,810,809,655,656,811,655,811,810,656,657,812,656,812,811,657,658,813,657,813,812,658,659,814,658,814,813,659,660,815,659,815,814,660,661,816,660,816,815,661,662,817,661,817,816,662,663,818,662,818,817,663,664,819,663,819,818,664,665,820,664,820,819,665,666,821,665,821,820,666,667,822,666,822,821,667,668,823,667,823,822,668,669,824,668,824,823,669,670,825,669,825,824,670,671,826,670,826,825,671,672,827,671,827,826,672,673,828,672,828,827,673,674,829,673,829,828,674,675,830,674,830,829,675,676,831,675,831,830,676,677,832,676,832,831,677,678,833,677,833,832,678,679,834,678,834,833,679,680,835,679,835,834,680,681,836,680,836,835,681,682,837,681,837,836,682,683,838,682,838,837,683,684,839,683,839,838,684,685,840,684,840,839,685,686,841,685,841,840,686,687,842,686,842,841,687,688,843,687,843,842,688,689,844,688,844,843,689,690,845,689,845,844,690,691,846,690,846,845,691,692,847,691,847,846,692,693,848,692,848,847,693,694,849,693,849,848,694,695,850,694,850,849,695,696,851,695,851,850,696,697,852,696,852,851,697,698,853,697,853,852,698,699,854,698,854,853,699,700,855,699,855,854,700,701,856,700,856,855,701,702,857,701,857,856,702,703,858,702,858,857,703,704,859,703,859,858,704,705,860,704,860,859,705,706,861,705,861,860,706,707,862,706,862,861,707,708,863,707,863,862,708,709,864,708,864,863,709,710,865,709,865,864,710,711,866,710,866,865,711,712,867,711,867,866,712,713,868,712,868,867,713,714,869,713,869,868,714,715,870,714,870,869,715,716,871,715,871,870,716,717,872,716,872,871,717,718,873,717,873,872,718,719,874,718,874,873,719,720,875,719,875,874,720,721,876,720,876,875,721,722,877,721,877,876,722,723,878,722,878,877,723,724,879,723,879,878,724,725,880,724,880,879,725,726,881,725,881,880,726,727,882,726,882,881,727,728,883,727,883,882,728,729,884,728,884,883,729,730,885,729,885,884,730,731,886,730,886,885,731,732,887,731,887,886,732,733,888,732,888,887,733,734,889,733,889,888,734,735,890,734,890,889,735,736,891,735,891,890,736,737,892,736,892,891,737,738,893,737,893,892,738,739,894,738,894,893,739,740,895,739,895,894,740,741,896,740,896,895,741,742,897,741,897,896,742,743,898,742,898,897,743,744,899,743,899,898,744,745,900,744,900,899,745,746,901,745,901,900,746,747,902,746,902,901,747,748,903,747,903,902,748,749,904,748,904,903,749,750,905,749,905,904,750,751,906,750,906,905,751,752,907,751,907,906,752,753,908,752,908,907,753,754,909,753,909,908,754,755,910,754,910,909,755,756,911,755,911,910,756,757,912,756,912,911,757,758,913,757,913,912,758,759,914,758,914,913,759,760,915,759,915,914,760,761,916,760,916,915,761,762,917,761,917,916,762,763,918,762,918,917,763,764,919,763,919,918,764,765,920,764,920,919,765,766,921,765,921,920,766,767,922,766,922,921,767,768,923,767,923,922,768,769,924,768,924,923,769,770,925,769,925,924,770,771,926,770,926,925,771,772,927,771,927,926,772,773,928,772,928,927,773,774,929,773,929,928,775,776,931,775,931,930,776,777,932,776,932,931,777,778,933,777,933,932,778,779,934,778,934,933,779,780,935,779,935,934,780,781,936,780,936,935,781,782,937,781,937,936,782,783,938,782,938,937,783,784,939,783,939,938,784,785,940,784,940,939,785,786,941,785,941,940,786,787,942,786,942,941,787,788,943,787,943,942,788,789,944,788,944,943,789,790,945,789,945,944,790,791,946,790,946,945,791,792,947,791,947,946,792,793,948,792,948,947,793,794,949,793,949,948,794,795,950,794,950,949,795,796,951,795,951,950,796,797,952,796,952,951,797,798,953,797,953,952,798,799,954,798,954,953,799,800,955,799,955,954,800,801,956,800,956,955,801,802,957,801,957,956,802,803,958,802,958,957,803,804,959,803,959,958,804,805,960,804,960,959,805,806,961,805,961,960,806,807,962,806,962,961,807,808,963,807,963,962,808,809,964,808,964,963,809,810,965,809,965,964,810,811,966,810,966,965,811,812,967,811,967,966,812,813,968,812,968,967,813,814,969,813,969,968,814,815,970,814,970,969,815,816,971,815,971,970,816,817,972,816,972,971,817,818,973,817,973,972,818,819,974,818,974,973,819,820,975,819,975,974,820,821,976,820,976,975,821,822,977,821,977,976,822,823,978,822,978,977,823,824,979,823,979,978,824,825,980,824,980,979,825,826,981,825,981,980,826,827,982,826,982,981,827,828,983,827,983,982,828,829,984,828,984,983,829,830,985,829,985,984,830,831,986,830,986,985,831,832,987,831,987,986,832,833,988,832,988,987,833,834,989,833,989,988,834,835,990,834,990,989,835,836,991,835,991,990,836,837,992,836,992,991,837,838,993,837,993,992,838,839,994,838,994,993,839,840,995,839,995,994,840,841,996,840,996,995,841,842,997,841,997,996,842,843,998,842,998,997,843,844,999,843,999,998,844,845,1000,844,1000,999,845,846,1001,845,1001,1000,846,847,1002,846,1002,1001,847,848,1003,847,1003,1002,848,849,1004,848,1004,1003,849,850,1005,849,1005,1004,850,851,1006,850,1006,1005,851,852,1007,851,1007,1006,852,853,1008,852,1008,1007,853,854,1009,853,1009,1008,854,855,1010,854,1010,1009,855,856,1011,855,1011,1010,856,857,1012,856,1012,1011,857,858,1013,857,1013,1012,858,859,1014,858,1014,1013,859,860,1015,859,1015,1014,860,861,1016,860,1016,1015,861,862,1017,861,1017,1016,862,863,1018,862,1018,1017,863,864,1019,863,1019,1018,864,865,1020,864,1020,1019,865,866,1021,865,1021,1020,866,867,1022,866,1022,1021,867,868,1023,867,1023,1022,868,869,1024,868,1024,1023,869,870,1025,869,1025,1024,870,871,1026,870,1026,1025,871,872,1027,871,1027,1026,872,873,1028,872,1028,1027,873,874,1029,873,1029,1028,874,875,1030,874,1030,1029,875,876,1031,875,1031,1030,876,877,1032,876,1032,1031,877,878,1033,877,1033,1032,878,879,1034,878,1034,1033,879,880,1035,879,1035,1034,880,881,1036,880,1036,1035,881,882,1037,881,1037,1036,882,883,1038,882,1038,1037,883,884,1039,883,1039,1038,884,885,1040,884,1040,1039,885,886,1041,885,1041,1040,886,887,1042,886,1042,1041,887,888,1043,887,1043,1042,888,889,1044,888,1044,1043,889,890,1045,889,1045,1044,890,891,1046,890,1046,1045,891,892,1047,891,1047,1046,892,893,1048,892,1048,1047,893,894,1049,893,1049,1048,894,895,1050,894,1050,1049,895,896,1051,895,1051,1050,896,897,1052,896,1052,1051,897,898,1053,897,1053,1052,898,899,1054,898,1054,1053,899,900,1055,899,1055,1054,900,901,1056,900,1056,1055,901,902,1057,901,1057,1056,902,903,1058,902,1058,1057,903,904,1059,903,1059,1058,904,905,1060,904,1060,1059,905,906,1061,905,1061,1060,906,907,1062,906,1062,1061,907,908,1063,907,1063,1062,908,909,1064,908,1064,1063,909,910,1065,909,1065,1064,910,911,1066,910,1066,1065,911,912,1067,911,1067,1066,912,913,1068,912,1068,1067,913,914,1069,913,1069,1068,914,915,1070,914,1070,1069,915,916,1071,915,1071,1070,916,917,1072,916,1072,1071,917,918,1073,917,1073,1072,918,919,1074,918,1074,1073,919,920,1075,919,1075,1074,920,921,1076,920,1076,1075,921,922,1077,921,1077,1076,922,923,1078,922,1078,1077,923,924,1079,923,1079,1078,924,925,1080,924,1080,1079,925,926,1081,925,1081,1080,926,927,1082,926,1082,1081,927,928,1083,927,1083,1082,928,929,1084,928,1084,1083,930,931,1086,930,1086,1085,931,932,1087,931,1087,1086,932,933,1088,932,1088,1087,933,934,1089,933,1089,1088,934,935,1090,934,1090,1089,935,936,1091,935,1091,1090,936,937,1092,936,1092,1091,937,938,1093,937,1093,1092,938,939,1094,938,1094,1093,939,940,1095,939,1095,1094,940,941,1096,940,1096,1095,941,942,1097,941,1097,1096,942,943,1098,942,1098,1097,943,944,1099,943,1099,1098,944,945,1100,944,1100,1099,945,946,1101,945,1101,1100,946,947,1102,946,1102,1101,947,948,1103,947,1103,1102,948,949,1104,948,1104,1103,949,950,1105,949,1105,1104,950,951,1106,950,1106,1105,951,952,1107,951,1107,1106,952,953,1108,952,1108,1107,953,954,1109,953,1109,1108,954,955,1110,954,1110,1109,955,956,1111,955,1111,1110,956,957,1112,956,1112,1111,957,958,1113,957,1113,1112,958,959,1114,958,1114,1113,959,960,1115,959,1115,1114,960,961,1116,960,1116,1115,961,962,1117,961,1117,1116,962,963,1118,962,1118,1117,963,964,1119,963,1119,1118,964,965,1120,964,1120,1119,965,966,1121,965,1121,1120,966,967,1122,966,1122,1121,967,968,1123,967,1123,1122,968,969,1124,968,1124,1123,969,970,1125,969,1125,1124,970,971,1126,970,1126,1125,971,972,1127,971,1127,1126,972,973,1128,972,1128,1127,973,974,1129,973,1129,1128,974,975,1130,974,1130,1129,975,976,1131,975,1131,1130,976,977,1132,976,1132,1131,977,978,1133,977,1133,1132,978,979,1134,978,1134,1133,979,980,1135,979,1135,1134,980,981,1136,980,1136,1135,981,982,1137,981,1137,1136,982,983,1138,982,1138,1137,983,984,1139,983,1139,1138,984,985,1140,984,1140,1139,985,986,1141,985,1141,1140,986,987,1142,986,1142,1141,987,988,1143,987,1143,1142,988,989,1144,988,1144,1143,989,990,1145,989,1145,1144,990,991,1146,990,1146,1145,991,992,1147,991,1147,1146,992,993,1148,992,1148,1147,993,994,1149,993,1149,1148,994,995,1150,994,1150,1149,995,996,1151,995,1151,1150,996,997,1152,996,1152,1151,997,998,1153,997,1153,1152,998,999,1154,998,1154,1153,999,1000,1155,999,1155,1154,1000,1001,1156,1000,1156,1155,1001,1002,1157,1001,1157,1156,1002,1003,1158,1002,1158,1157,1003,1004,1159,1003,1159,1158,1004,1005,1160,1004,1160,1159,1005,1006,1161,1005,1161,1160,1006,1007,1162,1006,1162,1161,1007,1008,1163,1007,1163,1162,1008,1009,1164,1008,1164,1163,1009,1010,1165,1009,1165,1164,1010,1011,1166,1010,1166,1165,1011,1012,1167,1011,1167,1166,1012,1013,1168,1012,1168,1167,1013,1014,1169,1013,1169,1168,1014,1015,1170,1014,1170,1169,1015,1016,1171,1015,1171,1170,1016,1017,1172,1016,1172,1171,1017,1018,1173,1017,1173,1172,1018,1019,1174,1018,1174,1173,1019,1020,1175,1019,1175,1174,1020,1021,1176,1020,1176,1175,1021,1022,1177,1021,1177,1176,1022,1023,1178,1022,1178,1177,1023,1024,1179,1023,1179,1178,1024,1025,1180,1024,1180,1179,1025,1026,1181,1025,1181,1180,1026,1027,1182,1026,1182,1181,1027,1028,1183,1027,1183,1182,1028,1029,1184,1028,1184,1183,1029,1030,1185,1029,1185,1184,1030,1031,1186,1030,1186,1185,1031,1032,1187,1031,1187,1186,1032,1033,1188,1032,1188,1187,1033,1034,1189,1033,1189,1188,1034,1035,1190,1034,1190,1189,1035,1036,1191,1035,1191,1190,1036,1037,1192,1036,1192,1191,1037,1038,1193,1037,1193,1192,1038,1039,1194,1038,1194,1193,1039,1040,1195,1039,1195,1194,1040,1041,1196,1040,1196,1195,1041,1042,1197,1041,1197,1196,1042,1043,1198,1042,1198,1197,1043,1044,1199,1043,1199,1198,1044,1045,1200,1044,1200,1199,1045,1046,1201,1045,1201,1200,1046,1047,1202,1046,1202,1201,1047,1048,1203,1047,1203,1202,1048,1049,1204,1048,1204,1203,1049,1050,1205,1049,1205,1204,1050,1051,1206,1050,1206,1205,1051,1052,1207,1051,1207,1206,1052,1053,1208,1052,1208,1207,1053,1054,1209,1053,1209,1208,1054,1055,1210,1054,1210,1209,1055,1056,1211,1055,1211,1210,1056,1057,1212,1056,1212,1211,1057,1058,1213,1057,1213,1212,1058,1059,1214,1058,1214,1213,1059,1060,1215,1059,1215,1214,1060,1061,1216,1060,1216,1215,1061,1062,1217,1061,1217,1216,1062,1063,1218,1062,1218,1217,1063,1064,1219,1063,1219,1218,1064,1065,1220,1064,1220,1219,1065,1066,1221,1065,1221,1220,1066,1067,1222,1066,1222,1221,1067,1068,1223,1067,1223,1222,1068,1069,1224,1068,1224,1223,1069,1070,1225,1069,1225,1224,1070,1071,1226,1070,1226,1225,1071,1072,1227,1071,1227,1226,1072,1073,1228,1072,1228,1227,1073,1074,1229,1073,1229,1228,1074,1075,1230,1074,1230,1229,1075,1076,1231,1075,1231,1230,1076,1077,1232,1076,1232,1231,1077,1078,1233,1077,1233,1232,1078,1079,1234,1078,1234,1233,1079,1080,1235,1079,1235,1234,1080,1081,1236,1080,1236,1235,1081,1082,1237,1081,1237,1236,1082,1083,1238,1082,1238,1237,1083,1084,1239,1083,1239,1238,1085,1086,1241,1085,1241,1240,1086,1087,1242,1086,1242,1241,1087,1088,1243,1087,1243,1242,1088,1089,1244,1088,1244,1243,1089,1090,1245,1089,1245,1244,1090,1091,1246,1090,1246,1245,1091,1092,1247,1091,1247,1246,1092,1093,1248,1092,1248,1247,1093,1094,1249,1093,1249,1248,1094,1095,1250,1094,1250,1249,1095,1096,1251,1095,1251,1250,1096,1097,1252,1096,1252,1251,1097,1098,1253,1097,1253,1252,1098,1099,1254,1098,1254,1253,1099,1100,1255,1099,1255,1254,1100,1101,1256,1100,1256,1255,1101,1102,1257,1101,1257,1256,1102,1103,1258,1102,1258,1257,1103,1104,1259,1103,1259,1258,1104,1105,1260,1104,1260,1259,1105,1106,1261,1105,1261,1260,1106,1107,1262,1106,1262,1261,1107,1108,1263,1107,1263,1262,1108,1109,1264,1108,1264,1263,1109,1110,1265,1109,1265,1264,1110,1111,1266,1110,1266,1265,1111,1112,1267,1111,1267,1266,1112,1113,1268,1112,1268,1267,1113,1114,1269,1113,1269,1268,1114,1115,1270,1114,1270,1269,1115,1116,1271,1115,1271,1270,1116,1117,1272,1116,1272,1271,1117,1118,1273,1117,1273,1272,1118,1119,1274,1118,1274,1273,1119,1120,1275,1119,1275,1274,1120,1121,1276,1120,1276,1275,1121,1122,1277,1121,1277,1276,1122,1123,1278,1122,1278,1277,1123,1124,1279,1123,1279,1278,1124,1125,1280,1124,1280,1279,1125,1126,1281,1125,1281,1280,1126,1127,1282,1126,1282,1281,1127,1128,1283,1127,1283,1282,1128,1129,1284,1128,1284,1283,1129,1130,1285,1129,1285,1284,1130,1131,1286,1130,1286,1285,1131,1132,1287,1131,1287,1286,1132,1133,1288,1132,1288,1287,1133,1134,1289,1133,1289,1288,1134,1135,1290,1134,1290,1289,1135,1136,1291,1135,1291,1290,1136,1137,1292,1136,1292,1291,1137,1138,1293,1137,1293,1292,1138,1139,1294,1138,1294,1293,1139,1140,1295,1139,1295,1294,1140,1141,1296,1140,1296,1295,1141,1142,1297,1141,1297,1296,1142,1143,1298,1142,1298,1297,1143,1144,1299,1143,1299,1298,1144,1145,1300,1144,1300,1299,1145,1146,1301,1145,1301,1300,1146,1147,1302,1146,1302,1301,1147,1148,1303,1147,1303,1302,1148,1149,1304,1148,1304,1303,1149,1150,1305,1149,1305,1304,1150,1151,1306,1150,1306,1305,1151,1152,1307,1151,1307,1306,1152,1153,1308,1152,1308,1307,1153,1154,1309,1153,1309,1308,1154,1155,1310,1154,1310,1309,1155,1156,1311,1155,1311,1310,1156,1157,1312,1156,1312,1311,1157,1158,1313,1157,1313,1312,1158,1159,1314,1158,1314,1313,1159,1160,1315,1159,1315,1314,1160,1161,1316,1160,1316,1315,1161,1162,1317,1161,1317,1316,1162,1163,1318,1162,1318,1317,1163,1164,1319,1163,1319,1318,1164,1165,1320,1164,1320,1319,1165,1166,1321,1165,1321,1320,1166,1167,1322,1166,1322,1321,1167,1168,1323,1167,1323,1322,1168,1169,1324,1168,1324,1323,1169,1170,1325,1169,1325,1324,1170,1171,1326,1170,1326,1325,1171,1172,1327,1171,1327,1326,1172,1173,1328,1172,1328,1327,1173,1174,1329,1173,1329,1328,1174,1175,1330,1174,1330,1329,1175,1176,1331,1175,1331,1330,1176,1177,1332,1176,1332,1331,1177,1178,1333,1177,1333,1332,1178,1179,1334,1178,1334,1333,1179,1180,1335,1179,1335,1334,1180,1181,1336,1180,1336,1335,1181,1182,1337,1181,1337,1336,1182,1183,1338,1182,1338,1337,1183,1184,1339,1183,1339,1338,1184,1185,1340,1184,1340,1339,1185,1186,1341,1185,1341,1340,1186,1187,1342,1186,1342,1341,1187,1188,1343,1187,1343,1342,1188,1189,1344,1188,1344,1343,1189,1190,1345,1189,1345,1344,1190,1191,1346,1190,1346,1345,1191,1192,1347,1191,1347,1346,1192,1193,1348,1192,1348,1347,1193,1194,1349,1193,1349,1348,1194,1195,1350,1194,1350,1349,1195,1196,1351,1195,1351,1350,1196,1197,1352,1196,1352,1351,1197,1198,1353,1197,1353,1352,1198,1199,1354,1198,1354,1353,1199,1200,1355,1199,1355,1354,1200,1201,1356,1200,1356,1355,1201,1202,1357,1201,1357,1356,1202,1203,1358,1202,1358,1357,1203,1204,1359,1203,1359,1358,1204,1205,1360,1204,1360,1359,1205,1206,1361,1205,1361,1360,1206,1207,1362,1206,1362,1361,1207,1208,1363,1207,1363,1362,1208,1209,1364,1208,1364,1363,1209,1210,1365,1209,1365,1364,1210,1211,1366,1210,1366,1365,1211,1212,1367,1211,1367,1366,1212,1213,1368,1212,1368,1367,1213,1214,1369,1213,1369,1368,1214,1215,1370,1214,1370,1369,1215,1216,1371,1215,1371,1370,1216,1217,1372,1216,1372,1371,1217,1218,1373,1217,1373,1372,1218,1219,1374,1218,1374,1373,1219,1220,1375,1219,1375,1374,1220,1221,1376,1220,1376,1375,1221,1222,1377,1221,1377,1376,1222,1223,1378,1222,1378,1377,1223,1224,1379,1223,1379,1378,1224,1225,1380,1224,1380,1379,1225,1226,1381,1225,1381,1380,1226,1227,1382,1226,1382,1381,1227,1228,1383,1227,1383,1382,1228,1229,1384,1228,1384,1383,1229,1230,1385,1229,1385,1384,1230,1231,1386,1230,1386,1385,1231,1232,1387,1231,1387,1386,1232,1233,1388,1232,1388,1387,1233,1234,1389,1233,1389,1388,1234,1235,1390,1234,1390,1389,1235,1236,1391,1235,1391,1390,1236,1237,1392,1236,1392,1391,1237,1238,1393,1237,1393,1392,1238,1239,1394,1238,1394,1393,1240,1241,1396,1240,1396,1395,1241,1242,1397,1241,1397,1396,1242,1243,1398,1242,1398,1397,1243,1244,1399,1243,1399,1398,1244,1245,1400,1244,1400,1399,1245,1246,1401,1245,1401,1400,1246,1247,1402,1246,1402,1401,1247,1248,1403,1247,1403,1402,1248,1249,1404,1248,1404,1403,1249,1250,1405,1249,1405,1404,1250,1251,1406,1250,1406,1405,1251,1252,1407,1251,1407,1406,1252,1253,1408,1252,1408,1407,1253,1254,1409,1253,1409,1408,1254,1255,1410,1254,1410,1409,1255,1256,1411,1255,1411,1410,1256,1257,1412,1256,1412,1411,1257,1258,1413,1257,1413,1412,1258,1259,1414,1258,1414,1413,1259,1260,1415,1259,1415,1414,1260,1261,1416,1260,1416,1415,1261,1262,1417,1261,1417,1416,1262,1263,1418,1262,1418,1417,1263,1264,1419,1263,1419,1418,1264,1265,1420,1264,1420,1419,1265,1266,1421,1265,1421,1420,1266,1267,1422,1266,1422,1421,1267,1268,1423,1267,1423,1422,1268,1269,1424,1268,1424,1423,1269,1270,1425,1269,1425,1424,1270,1271,1426,1270,1426,1425,1271,1272,1427,1271,1427,1426,1272,1273,1428,1272,1428,1427,1273,1274,1429,1273,1429,1428,1274,1275,1430,1274,1430,1429,1275,1276,1431,1275,1431,1430,1276,1277,1432,1276,1432,1431,1277,1278,1433,1277,1433,1432,1278,1279,1434,1278,1434,1433,1279,1280,1435,1279,1435,1434,1280,1281,1436,1280,1436,1435,1281,1282,1437,1281,1437,1436,1282,1283,1438,1282,1438,1437,1283,1284,1439,1283,1439,1438,1284,1285,1440,1284,1440,1439,1285,1286,1441,1285,1441,1440,1286,1287,1442,1286,1442,1441,1287,1288,1443,1287,1443,1442,1288,1289,1444,1288,1444,1443,1289,1290,1445,1289,1445,1444,1290,1291,1446,1290,1446,1445,1291,1292,1447,1291,1447,1446,1292,1293,1448,1292,1448,1447,1293,1294,1449,1293,1449,1448,1294,1295,1450,1294,1450,1449,1295,1296,1451,1295,1451,1450,1296,1297,1452,1296,1452,1451,1297,1298,1453,1297,1453,1452,1298,1299,1454,1298,1454,1453,1299,1300,1455,1299,1455,1454,1300,1301,1456,1300,1456,1455,1301,1302,1457,1301,1457,1456,1302,1303,1458,1302,1458,1457,1303,1304,1459,1303,1459,1458,1304,1305,1460,1304,1460,1459,1305,1306,1461,1305,1461,1460,1306,1307,1462,1306,1462,1461,1307,1308,1463,1307,1463,1462,1308,1309,1464,1308,1464,1463,1309,1310,1465,1309,1465,1464,1310,1311,1466,1310,1466,1465,1311,1312,1467,1311,1467,1466,1312,1313,1468,1312,1468,1467,1313,1314,1469,1313,1469,1468,1314,1315,1470,1314,1470,1469,1315,1316,1471,1315,1471,1470,1316,1317,1472,1316,1472,1471,1317,1318,1473,1317,1473,1472,1318,1319,1474,1318,1474,1473,1319,1320,1475,1319,1475,1474,1320,1321,1476,1320,1476,1475,1321,1322,1477,1321,1477,1476,1322,1323,1478,1322,1478,1477,1323,1324,1479,1323,1479,1478,1324,1325,1480,1324,1480,1479,1325,1326,1481,1325,1481,1480,1326,1327,1482,1326,1482,1481,1327,1328,1483,1327,1483,1482,1328,1329,1484,1328,1484,1483,1329,1330,1485,1329,1485,1484,1330,1331,1486,1330,1486,1485,1331,1332,1487,1331,1487,1486,1332,1333,1488,1332,1488,1487,1333,1334,1489,1333,1489,1488,1334,1335,1490,1334,1490,1489,1335,1336,1491,1335,1491,1490,1336,1337,1492,1336,1492,1491,1337,1338,1493,1337,1493,1492,1338,1339,1494,1338,1494,1493,1339,1340,1495,1339,1495,1494,1340,1341,1496,1340,1496,1495,1341,1342,1497,1341,1497,1496,1342,1343,1498,1342,1498,1497,1343,1344,1499,1343,1499,1498,1344,1345,1500,1344,1500,1499,1345,1346,1501,1345,1501,1500,1346,1347,1502,1346,1502,1501,1347,1348,1503,1347,1503,1502,1348,1349,1504,1348,1504,1503,1349,1350,1505,1349,1505,1504,1350,1351,1506,1350,1506,1505,1351,1352,1507,1351,1507,1506,1352,1353,1508,1352,1508,1507,1353,1354,1509,1353,1509,1508,1354,1355,1510,1354,1510,1509,1355,1356,1511,1355,1511,1510,1356,1357,1512,1356,1512,1511,1357,1358,1513,1357,1513,1512,1358,1359,1514,1358,1514,1513,1359,1360,1515,1359,1515,1514,1360,1361,1516,1360,1516,1515,1361,1362,1517,1361,1517,1516,1362,1363,1518,1362,1518,1517,1363,1364,1519,1363,1519,1518,1364,1365,1520,1364,1520,1519,1365,1366,1521,1365,1521,1520,1366,1367,1522,1366,1522,1521,1367,1368,1523,1367,1523,1522,1368,1369,1524,1368,1524,1523,1369,1370,1525,1369,1525,1524,1370,1371,1526,1370,1526,1525,1371,1372,1527,1371,1527,1526,1372,1373,1528,1372,1528,1527,1373,1374,1529,1373,1529,1528,1374,1375,1530,1374,1530,1529,1375,1376,1531,1375,1531,1530,1376,1377,1532,1376,1532,1531,1377,1378,1533,1377,1533,1532,1378,1379,1534,1378,1534,1533,1379,1380,1535,1379,1535,1534,1380,1381,1536,1380,1536,1535,1381,1382,1537,1381,1537,1536,1382,1383,1538,1382,1538,1537,1383,1384,1539,1383,1539,1538,1384,1385,1540,1384,1540,1539,1385,1386,1541,1385,1541,1540,1386,1387,1542,1386,1542,1541,1387,1388,1543,1387,1543,1542,1388,1389,1544,1388,1544,1543,1389,1390,1545,1389,1545,1544,1390,1391,1546,1390,1546,1545,1391,1392,1547,1391,1547,1546,1392,1393,1548,1392,1548,1547,1393,1394,1549,1393,1549,1548,1395,1396,1551,1395,1551,1550,1396,1397,1552,1396,1552,1551,1397,1398,1553,1397,1553,1552,1398,1399,1554,1398,1554,1553,1399,1400,1555,1399,1555,1554,1400,1401,1556,1400,1556,1555,1401,1402,1557,1401,1557,1556,1402,1403,1558,1402,1558,1557,1403,1404,1559,1403,1559,1558,1404,1405,1560,1404,1560,1559,1405,1406,1561,1405,1561,1560,1406,1407,1562,1406,1562,1561,1407,1408,1563,1407,1563,1562,1408,1409,1564,1408,1564,1563,1409,1410,1565,1409,1565,1564,1410,1411,1566,1410,1566,1565,1411,1412,1567,1411,1567,1566,1412,1413,1568,1412,1568,1567,1413,1414,1569,1413,1569,1568,1414,1415,1570,1414,1570,1569,1415,1416,1571,1415,1571,1570,1416,1417,1572,1416,1572,1571,1417,1418,1573,1417,1573,1572,1418,1419,1574,1418,1574,1573,1419,1420,1575,1419,1575,1574,1420,1421,1576,1420,1576,1575,1421,1422,1577,1421,1577,1576,1422,1423,1578,1422,1578,1577,1423,1424,1579,1423,1579,1578,1424,1425,1580,1424,1580,1579,1425,1426,1581,1425,1581,1580,1426,1427,1582,1426,1582,1581,1427,1428,1583,1427,1583,1582,1428,1429,1584,1428,1584,1583,1429,1430,1585,1429,1585,1584,1430,1431,1586,1430,1586,1585,1431,1432,1587,1431,1587,1586,1432,1433,1588,1432,1588,1587,1433,1434,1589,1433,1589,1588,1434,1435,1590,1434,1590,1589,1435,1436,1591,1435,1591,1590,1436,1437,1592,1436,1592,1591,1437,1438,1593,1437,1593,1592,1438,1439,1594,1438,1594,1593,1439,1440,1595,1439,1595,1594,1440,1441,1596,1440,1596,1595,1441,1442,1597,1441,1597,1596,1442,1443,1598,1442,1598,1597,1443,1444,1599,1443,1599,1598,1444,1445,1600,1444,1600,1599,1445,1446,1601,1445,1601,1600,1446,1447,1602,1446,1602,1601,1447,1448,1603,1447,1603,1602,1448,1449,1604,1448,1604,1603,1449,1450,1605,1449,1605,1604,1450,1451,1606,1450,1606,1605,1451,1452,1607,1451,1607,1606,1452,1453,1608,1452,1608,1607,1453,1454,1609,1453,1609,1608,1454,1455,1610,1454,1610,1609,1455,1456,1611,1455,1611,1610,1456,1457,1612,1456,1612,1611,1457,1458,1613,1457,1613,1612,1458,1459,1614,1458,1614,1613,1459,1460,1615,1459,1615,1614,1460,1461,1616,1460,1616,1615,1461,1462,1617,1461,1617,1616,1462,1463,1618,1462,1618,1617,1463,1464,1619,1463,1619,1618,1464,1465,1620,1464,1620,1619,1465,1466,1621,1465,1621,1620,1466,1467,1622,1466,1622,1621,1467,1468,1623,1467,1623,1622,1468,1469,1624,1468,1624,1623,1469,1470,1625,1469,1625,1624,1470,1471,1626,1470,1626,1625,1471,1472,1627,1471,1627,1626,1472,1473,1628,1472,1628,1627,1473,1474,1629,1473,1629,1628,1474,1475,1630,1474,1630,1629,1475,1476,1631,1475,1631,1630,1476,1477,1632,1476,1632,1631,1477,1478,1633,1477,1633,1632,1478,1479,1634,1478,1634,1633,1479,1480,1635,1479,1635,1634,1480,1481,1636,1480,1636,1635,1481,1482,1637,1481,1637,1636,1482,1483,1638,1482,1638,1637,1483,1484,1639,1483,1639,1638,1484,1485,1640,1484,1640,1639,1485,1486,1641,1485,1641,1640,1486,1487,1642,1486,1642,1641,1487,1488,1643,1487,1643,1642,1488,1489,1644,1488,1644,1643,1489,1490,1645,1489,1645,1644,1490,1491,1646,1490,1646,1645,1491,1492,1647,1491,1647,1646,1492,1493,1648,1492,1648,1647,1493,1494,1649,1493,1649,1648,1494,1495,1650,1494,1650,1649,1495,1496,1651,1495,1651,1650,1496,1497,1652,1496,1652,1651,1497,1498,1653,1497,1653,1652,1498,1499,1654,1498,1654,1653,1499,1500,1655,1499,1655,1654,1500,1501,1656,1500,1656,1655,1501,1502,1657,1501,1657,1656,1502,1503,1658,1502,1658,1657,1503,1504,1659,1503,1659,1658,1504,1505,1660,1504,1660,1659,1505,1506,1661,1505,1661,1660,1506,1507,1662,1506,1662,1661,1507,1508,1663,1507,1663,1662,1508,1509,1664,1508,1664,1663,1509,1510,1665,1509,1665,1664,1510,1511,1666,1510,1666,1665,1511,1512,1667,1511,1667,1666,1512,1513,1668,1512,1668,1667,1513,1514,1669,1513,1669,1668,1514,1515,1670,1514,1670,1669,1515,1516,1671,1515,1671,1670,1516,1517,1672,1516,1672,1671,1517,1518,1673,1517,1673,1672,1518,1519,1674,1518,1674,1673,1519,1520,1675,1519,1675,1674,1520,1521,1676,1520,1676,1675,1521,1522,1677,1521,1677,1676,1522,1523,1678,1522,1678,1677,1523,1524,1679,1523,1679,1678,1524,1525,1680,1524,1680,1679,1525,1526,1681,1525,1681,1680,1526,1527,1682,1526,1682,1681,1527,1528,1683,1527,1683,1682,1528,1529,1684,1528,1684,1683,1529,1530,1685,1529,1685,1684,1530,1531,1686,1530,1686,1685,1531,1532,1687,1531,1687,1686,1532,1533,1688,1532,1688,1687,1533,1534,1689,1533,1689,1688,1534,1535,1690,1534,1690,1689,1535,1536,1691,1535,1691,1690,1536,1537,1692,1536,1692,1691,1537,1538,1693,1537,1693,1692,1538,1539,1694,1538,1694,1693,1539,1540,1695,1539,1695,1694,1540,1541,1696,1540,1696,1695,1541,1542,1697,1541,1697,1696,1542,1543,1698,1542,1698,1697,1543,1544,1699,1543,1699,1698,1544,1545,1700,1544,1700,1699,1545,1546,1701,1545,1701,1700,1546,1547,1702,1546,1702,1701,1547,1548,1703,1547,1703,1702,1548,1549,1704,1548,1704,1703,1550,1551,1706,1550,1706,1705,1551,1552,1707,1551,1707,1706,1552,1553,1708,1552,1708,1707,1553,1554,1709,1553,1709,1708,1554,1555,1710,1554,1710,1709,1555,1556,1711,1555,1711,1710,1556,1557,1712,1556,1712,1711,1557,1558,1713,1557,1713,1712,1558,1559,1714,1558,1714,1713,1559,1560,1715,1559,1715,1714,1560,1561,1716,1560,1716,1715,1561,1562,1717,1561,1717,1716,1562,1563,1718,1562,1718,1717,1563,1564,1719,1563,1719,1718,1564,1565,1720,1564,1720,1719,1565,1566,1721,1565,1721,1720,1566,1567,1722,1566,1722,1721,1567,1568,1723,1567,1723,1722,1568,1569,1724,1568,1724,1723,1569,1570,1725,1569,1725,1724,1570,1571,1726,1570,1726,1725,1571,1572,1727,1571,1727,1726,1572,1573,1728,1572,1728,1727,1573,1574,1729,1573,1729,1728,1574,1575,1730,1574,1730,1729,1575,1576,1731,1575,1731,1730,1576,1577,1732,1576,1732,1731,1577,1578,1733,1577,1733,1732,1578,1579,1734,1578,1734,1733,1579,1580,1735,1579,1735,1734,1580,1581,1736,1580,1736,1735,1581,1582,1737,1581,1737,1736,1582,1583,1738,1582,1738,1737,1583,1584,1739,1583,1739,1738,1584,1585,1740,1584,1740,1739,1585,1586,1741,1585,1741,1740,1586,1587,1742,1586,1742,1741,1587,1588,1743,1587,1743,1742,1588,1589,1744,1588,1744,1743,1589,1590,1745,1589,1745,1744,1590,1591,1746,1590,1746,1745,1591,1592,1747,1591,1747,1746,1592,1593,1748,1592,1748,1747,1593,1594,1749,1593,1749,1748,1594,1595,1750,1594,1750,1749,1595,1596,1751,1595,1751,1750,1596,1597,1752,1596,1752,1751,1597,1598,1753,1597,1753,1752,1598,1599,1754,1598,1754,1753,1599,1600,1755,1599,1755,1754,1600,1601,1756,1600,1756,1755,1601,1602,1757,1601,1757,1756,1602,1603,1758,1602,1758,1757,1603,1604,1759,1603,1759,1758,1604,1605,1760,1604,1760,1759,1605,1606,1761,1605,1761,1760,1606,1607,1762,1606,1762,1761,1607,1608,1763,1607,1763,1762,1608,1609,1764,1608,1764,1763,1609,1610,1765,1609,1765,1764,1610,1611,1766,1610,1766,1765,1611,1612,1767,1611,1767,1766,1612,1613,1768,1612,1768,1767,1613,1614,1769,1613,1769,1768,1614,1615,1770,1614,1770,1769,1615,1616,1771,1615,1771,1770,1616,1617,1772,1616,1772,1771,1617,1618,1773,1617,1773,1772,1618,1619,1774,1618,1774,1773,1619,1620,1775,1619,1775,1774,1620,1621,1776,1620,1776,1775,1621,1622,1777,1621,1777,1776,1622,1623,1778,1622,1778,1777,1623,1624,1779,1623,1779,1778,1624,1625,1780,1624,1780,1779,1625,1626,1781,1625,1781,1780,1626,1627,1782,1626,1782,1781,1627,1628,1783,1627,1783,1782,1628,1629,1784,1628,1784,1783,1629,1630,1785,1629,1785,1784,1630,1631,1786,1630,1786,1785,1631,1632,1787,1631,1787,1786,1632,1633,1788,1632,1788,1787,1633,1634,1789,1633,1789,1788,1634,1635,1790,1634,1790,1789,1635,1636,1791,1635,1791,1790,1636,1637,1792,1636,1792,1791,1637,1638,1793,1637,1793,1792,1638,1639,1794,1638,1794,1793,1639,1640,1795,1639,1795,1794,1640,1641,1796,1640,1796,1795,1641,1642,1797,1641,1797,1796,1642,1643,1798,1642,1798,1797,1643,1644,1799,1643,1799,1798,1644,1645,1800,1644,1800,1799,1645,1646,1801,1645,1801,1800,1646,1647,1802,1646,1802,1801,1647,1648,1803,1647,1803,1802,1648,1649,1804,1648,1804,1803,1649,1650,1805,1649,1805,1804,1650,1651,1806,1650,1806,1805,1651,1652,1807,1651,1807,1806,1652,1653,1808,1652,1808,1807,1653,1654,1809,1653,1809,1808,1654,1655,1810,1654,1810,1809,1655,1656,1811,1655,1811,1810,1656,1657,1812,1656,1812,1811,1657,1658,1813,1657,1813,1812,1658,1659,1814,1658,1814,1813,1659,1660,1815,1659,1815,1814,1660,1661,1816,1660,1816,1815,1661,1662,1817,1661,1817,1816,1662,1663,1818,1662,1818,1817,1663,1664,1819,1663,1819,1818,1664,1665,1820,1664,1820,1819,1665,1666,1821,1665,1821,1820,1666,1667,1822,1666,1822,1821,1667,1668,1823,1667,1823,1822,1668,1669,1824,1668,1824,1823,1669,1670,1825,1669,1825,1824,1670,1671,1826,1670,1826,1825,1671,1672,1827,1671,1827,1826,1672,1673,1828,1672,1828,1827,1673,1674,1829,1673,1829,1828,1674,1675,1830,1674,1830,1829,1675,1676,1831,1675,1831,1830,1676,1677,1832,1676,1832,1831,1677,1678,1833,1677,1833,1832,1678,1679,1834,1678,1834,1833,1679,1680,1835,1679,1835,1834,1680,1681,1836,1680,1836,1835,1681,1682,1837,1681,1837,1836,1682,1683,1838,1682,1838,1837,1683,1684,1839,1683,1839,1838,1684,1685,1840,1684,1840,1839,1685,1686,1841,1685,1841,1840,1686,1687,1842,1686,1842,1841,1687,1688,1843,1687,1843,1842,1688,1689,1844,1688,1844,1843,1689,1690,1845,1689,1845,1844,1690,1691,1846,1690,1846,1845,1691,1692,1847,1691,1847,1846,1692,1693,1848,1692,1848,1847,1693,1694,1849,1693,1849,1848,1694,1695,1850,1694,1850,1849,1695,1696,1851,1695,1851,1850,1696,1697,1852,1696,1852,1851,1697,1698,1853,1697,1853,1852,1698,1699,1854,1698,1854,1853,1699,1700,1855,1699,1855,1854,1700,1701,1856,1700,1856,1855,1701,1702,1857,1701,1857,1856,1702,1703,1858,1702,1858,1857,1703,1704,1859,1703,1859,1858,1705,1706,1861,1705,1861,1860,1706,1707,1862,1706,1862,1861,1707,1708,1863,1707,1863,1862,1708,1709,1864,1708,1864,1863,1709,1710,1865,1709,1865,1864,1710,1711,1866,1710,1866,1865,1711,1712,1867,1711,1867,1866,1712,1713,1868,1712,1868,1867,1713,1714,1869,1713,1869,1868,1714,1715,1870,1714,1870,1869,1715,1716,1871,1715,1871,1870,1716,1717,1872,1716,1872,1871,1717,1718,1873,1717,1873,1872,1718,1719,1874,1718,1874,1873,1719,1720,1875,1719,1875,1874,1720,1721,1876,1720,1876,1875,1721,1722,1877,1721,1877,1876,1722,1723,1878,1722,1878,1877,1723,1724,1879,1723,1879,1878,1724,1725,1880,1724,1880,1879,1725,1726,1881,1725,1881,1880,1726,1727,1882,1726,1882,1881,1727,1728,1883,1727,1883,1882,1728,1729,1884,1728,1884,1883,1729,1730,1885,1729,1885,1884,1730,1731,1886,1730,1886,1885,1731,1732,1887,1731,1887,1886,1732,1733,1888,1732,1888,1887,1733,1734,1889,1733,1889,1888,1734,1735,1890,1734,1890,1889,1735,1736,1891,1735,1891,1890,1736,1737,1892,1736,1892,1891,1737,1738,1893,1737,1893,1892,1738,1739,1894,1738,1894,1893,1739,1740,1895,1739,1895,1894,1740,1741,1896,1740,1896,1895,1741,1742,1897,1741,1897,1896,1742,1743,1898,1742,1898,1897,1743,1744,1899,1743,1899,1898,1744,1745,1900,1744,1900,1899,1745,1746,1901,1745,1901,1900,1746,1747,1902,1746,1902,1901,1747,1748,1903,1747,1903,1902,1748,1749,1904,1748,1904,1903,1749,1750,1905,1749,1905,1904,1750,1751,1906,1750,1906,1905,1751,1752,1907,1751,1907,1906,1752,1753,1908,1752,1908,1907,1753,1754,1909,1753,1909,1908,1754,1755,1910,1754,1910,1909,1755,1756,1911,1755,1911,1910,1756,1757,1912,1756,1912,1911,1757,1758,1913,1757,1913,1912,1758,1759,1914,1758,1914,1913,1759,1760,1915,1759,1915,1914,1760,1761,1916,1760,1916,1915,1761,1762,1917,1761,1917,1916,1762,1763,1918,1762,1918,1917,1763,1764,1919,1763,1919,1918,1764,1765,1920,1764,1920,1919,1765,1766,1921,1765,1921,1920,1766,1767,1922,1766,1922,1921,1767,1768,1923,1767,1923,1922,1768,1769,1924,1768,1924,1923,1769,1770,1925,1769,1925,1924,1770,1771,1926,1770,1926,1925,1771,1772,1927,1771,1927,1926,1772,1773,1928,1772,1928,1927,1773,1774,1929,1773,1929,1928,1774,1775,1930,1774,1930,1929,1775,1776,1931,1775,1931,1930,1776,1777,1932,1776,1932,1931,1777,1778,1933,1777,1933,1932,1778,1779,1934,1778,1934,1933,1779,1780,1935,1779,1935,1934,1780,1781,1936,1780,1936,1935,1781,1782,1937,1781,1937,1936,1782,1783,1938,1782,1938,1937,1783,1784,1939,1783,1939,1938,1784,1785,1940,1784,1940,1939,1785,1786,1941,1785,1941,1940,1786,1787,1942,1786,1942,1941,1787,1788,1943,1787,1943,1942,1788,1789,1944,1788,1944,1943,1789,1790,1945,1789,1945,1944,1790,1791,1946,1790,1946,1945,1791,1792,1947,1791,1947,1946,1792,1793,1948,1792,1948,1947,1793,1794,1949,1793,1949,1948,1794,1795,1950,1794,1950,1949,1795,1796,1951,1795,1951,1950,1796,1797,1952,1796,1952,1951,1797,1798,1953,1797,1953,1952,1798,1799,1954,1798,1954,1953,1799,1800,1955,1799,1955,1954,1800,1801,1956,1800,1956,1955,1801,1802,1957,1801,1957,1956,1802,1803,1958,1802,1958,1957,1803,1804,1959,1803,1959,1958,1804,1805,1960,1804,1960,1959,1805,1806,1961,1805,1961,1960,1806,1807,1962,1806,1962,1961,1807,1808,1963,1807,1963,1962,1808,1809,1964,1808,1964,1963,1809,1810,1965,1809,1965,1964,1810,1811,1966,1810,1966,1965,1811,1812,1967,1811,1967,1966,1812,1813,1968,1812,1968,1967,1813,1814,1969,1813,1969,1968,1814,1815,1970,1814,1970,1969,1815,1816,1971,1815,1971,1970,1816,1817,1972,1816,1972,1971,1817,1818,1973,1817,1973,1972,1818,1819,1974,1818,1974,1973,1819,1820,1975,1819,1975,1974,1820,1821,1976,1820,1976,1975,1821,1822,1977,1821,1977,1976,1822,1823,1978,1822,1978,1977,1823,1824,1979,1823,1979,1978,1824,1825,1980,1824,1980,1979,1825,1826,1981,1825,1981,1980,1826,1827,1982,1826,1982,1981,1827,1828,1983,1827,1983,1982,1828,1829,1984,1828,1984,1983,1829,1830,1985,1829,1985,1984,1830,1831,1986,1830,1986,1985,1831,1832,1987,1831,1987,1986,1832,1833,1988,1832,1988,1987,1833,1834,1989,1833,1989,1988,1834,1835,1990,1834,1990,1989,1835,1836,1991,1835,1991,1990,1836,1837,1992,1836,1992,1991,1837,1838,1993,1837,1993,1992,1838,1839,1994,1838,1994,1993,1839,1840,1995,1839,1995,1994,1840,1841,1996,1840,1996,1995,1841,1842,1997,1841,1997,1996,1842,1843,1998,1842,1998,1997,1843,1844,1999,1843,1999,1998,1844,1845,2000,1844,2000,1999,1845,1846,2001,1845,2001,2000,1846,1847,2002,1846,2002,2001,1847,1848,2003,1847,2003,2002,1848,1849,2004,1848,2004,2003,1849,1850,2005,1849,2005,2004,1850,1851,2006,1850,2006,2005,1851,1852,2007,1851,2007,2006,1852,1853,2008,1852,2008,2007,1853,1854,2009,1853,2009,2008,1854,1855,2010,1854,2010,2009,1855,1856,2011,1855,2011,2010,1856,1857,2012,1856,2012,2011,1857,1858,2013,1857,2013,2012,1858,1859,2014,1858,2014,2013,1860,1861,2016,1860,2016,2015,1861,1862,2017,1861,2017,2016,1862,1863,2018,1862,2018,2017,1863,1864,2019,1863,2019,2018,1864,1865,2020,1864,2020,2019,1865,1866,2021,1865,2021,2020,1866,1867,2022,1866,2022,2021,1867,1868,2023,1867,2023,2022,1868,1869,2024,1868,2024,2023,1869,1870,2025,1869,2025,2024,1870,1871,2026,1870,2026,2025,1871,1872,2027,1871,2027,2026,1872,1873,2028,1872,2028,2027,1873,1874,2029,1873,2029,2028,1874,1875,2030,1874,2030,2029,1875,1876,2031,1875,2031,2030,1876,1877,2032,1876,2032,2031,1877,1878,2033,1877,2033,2032,1878,1879,2034,1878,2034,2033,1879,1880,2035,1879,2035,2034,1880,1881,2036,1880,2036,2035,1881,1882,2037,1881,2037,2036,1882,1883,2038,1882,2038,2037,1883,1884,2039,1883,2039,2038,1884,1885,2040,1884,2040,2039,1885,1886,2041,1885,2041,2040,1886,1887,2042,1886,2042,2041,1887,1888,2043,1887,2043,2042,1888,1889,2044,1888,2044,2043,1889,1890,2045,1889,2045,2044,1890,1891,2046,1890,2046,2045,1891,1892,2047,1891,2047,2046,1892,1893,2048,1892,2048,2047,1893,1894,2049,1893,2049,2048,1894,1895,2050,1894,2050,2049,1895,1896,2051,1895,2051,2050,1896,1897,2052,1896,2052,2051,1897,1898,2053,1897,2053,2052,1898,1899,2054,1898,2054,2053,1899,1900,2055,1899,2055,2054,1900,1901,2056,1900,2056,2055,1901,1902,2057,1901,2057,2056,1902,1903,2058,1902,2058,2057,1903,1904,2059,1903,2059,2058,1904,1905,2060,1904,2060,2059,1905,1906,2061,1905,2061,2060,1906,1907,2062,1906,2062,2061,1907,1908,2063,1907,2063,2062,1908,1909,2064,1908,2064,2063,1909,1910,2065,1909,2065,2064,1910,1911,2066,1910,2066,2065,1911,1912,2067,1911,2067,2066,1912,1913,2068,1912,2068,2067,1913,1914,2069,1913,2069,2068,1914,1915,2070,1914,2070,2069,1915,1916,2071,1915,2071,2070,1916,1917,2072,1916,2072,2071,1917,1918,2073,1917,2073,2072,1918,1919,2074,1918,2074,2073,1919,1920,2075,1919,2075,2074,1920,1921,2076,1920,2076,2075,1921,1922,2077,1921,2077,2076,1922,1923,2078,1922,2078,2077,1923,1924,2079,1923,2079,2078,1924,1925,2080,1924,2080,2079,1925,1926,2081,1925,2081,2080,1926,1927,2082,1926,2082,2081,1927,1928,2083,1927,2083,2082,1928,1929,2084,1928,2084,2083,1929,1930,2085,1929,2085,2084,1930,1931,2086,1930,2086,2085,1931,1932,2087,1931,2087,2086,1932,1933,2088,1932,2088,2087,1933,1934,2089,1933,2089,2088,1934,1935,2090,1934,2090,2089,1935,1936,2091,1935,2091,2090,1936,1937,2092,1936,2092,2091,1937,1938,2093,1937,2093,2092,1938,1939,2094,1938,2094,2093,1939,1940,2095,1939,2095,2094,1940,1941,2096,1940,2096,2095,1941,1942,2097,1941,2097,2096,1942,1943,2098,1942,2098,2097,1943,1944,2099,1943,2099,2098,1944,1945,2100,1944,2100,2099,1945,1946,2101,1945,2101,2100,1946,1947,2102,1946,2102,2101,1947,1948,2103,1947,2103,2102,1948,1949,2104,1948,2104,2103,1949,1950,2105,1949,2105,2104,1950,1951,2106,1950,2106,2105,1951,1952,2107,1951,2107,2106,1952,1953,2108,1952,2108,2107,1953,1954,2109,1953,2109,2108,1954,1955,2110,1954,2110,2109,1955,1956,2111,1955,2111,2110,1956,1957,2112,1956,2112,2111,1957,1958,2113,1957,2113,2112,1958,1959,2114,1958,2114,2113,1959,1960,2115,1959,2115,2114,1960,1961,2116,1960,2116,2115,1961,1962,2117,1961,2117,2116,1962,1963,2118,1962,2118,2117,1963,1964,2119,1963,2119,2118,1964,1965,2120,1964,2120,2119,1965,1966,2121,1965,2121,2120,1966,1967,2122,1966,2122,2121,1967,1968,2123,1967,2123,2122,1968,1969,2124,1968,2124,2123,1969,1970,2125,1969,2125,2124,1970,1971,2126,1970,2126,2125,1971,1972,2127,1971,2127,2126,1972,1973,2128,1972,2128,2127,1973,1974,2129,1973,2129,2128,1974,1975,2130,1974,2130,2129,1975,1976,2131,1975,2131,2130,1976,1977,2132,1976,2132,2131,1977,1978,2133,1977,2133,2132,1978,1979,2134,1978,2134,2133,1979,1980,2135,1979,2135,2134,1980,1981,2136,1980,2136,2135,1981,1982,2137,1981,2137,2136,1982,1983,2138,1982,2138,2137,1983,1984,2139,1983,2139,2138,1984,1985,2140,1984,2140,2139,1985,1986,2141,1985,2141,2140,1986,1987,2142,1986,2142,2141,1987,1988,2143,1987,2143,2142,1988,1989,2144,1988,2144,2143,1989,1990,2145,1989,2145,2144,1990,1991,2146,1990,2146,2145,1991,1992,2147,1991,2147,2146,1992,1993,2148,1992,2148,2147,1993,1994,2149,1993,2149,2148,1994,1995,2150,1994,2150,2149,1995,1996,2151,1995,2151,2150,1996,1997,2152,1996,2152,2151,1997,1998,2153,1997,2153,2152,1998,1999,2154,1998,2154,2153,1999,2000,2155,1999,2155,2154,2000,2001,2156,2000,2156,2155,2001,2002,2157,2001,2157,2156,2002,2003,2158,2002,2158,2157,2003,2004,2159,2003,2159,2158,2004,2005,2160,2004,2160,2159,2005,2006,2161,2005,2161,2160,2006,2007,2162,2006,2162,2161,2007,2008,2163,2007,2163,2162,2008,2009,2164,2008,2164,2163,2009,2010,2165,2009,2165,2164,2010,2011,2166,2010,2166,2165,2011,2012,2167,2011,2167,2166,2012,2013,2168,2012,2168,2167,2013,2014,2169,2013,2169,2168,2015,2016,2171,2015,2171,2170,2016,2017,2172,2016,2172,2171,2017,2018,2173,2017,2173,2172,2018,2019,2174,2018,2174,2173,2019,2020,2175,2019,2175,2174,2020,2021,2176,2020,2176,2175,2021,2022,2177,2021,2177,2176,2022,2023,2178,2022,2178,2177,2023,2024,2179,2023,2179,2178,2024,2025,2180,2024,2180,2179,2025,2026,2181,2025,2181,2180,2026,2027,2182,2026,2182,2181,2027,2028,2183,2027,2183,2182,2028,2029,2184,2028,2184,2183,2029,2030,2185,2029,2185,2184,2030,2031,2186,2030,2186,2185,2031,2032,2187,2031,2187,2186,2032,2033,2188,2032,2188,2187,2033,2034,2189,2033,2189,2188,2034,2035,2190,2034,2190,2189,2035,2036,2191,2035,2191,2190,2036,2037,2192,2036,2192,2191,2037,2038,2193,2037,2193,2192,2038,2039,2194,2038,2194,2193,2039,2040,2195,2039,2195,2194,2040,2041,2196,2040,2196,2195,2041,2042,2197,2041,2197,2196,2042,2043,2198,2042,2198,2197,2043,2044,2199,2043,2199,2198,2044,2045,2200,2044,2200,2199,2045,2046,2201,2045,2201,2200,2046,2047,2202,2046,2202,2201,2047,2048,2203,2047,2203,2202,2048,2049,2204,2048,2204,2203,2049,2050,2205,2049,2205,2204,2050,2051,2206,2050,2206,2205,2051,2052,2207,2051,2207,2206,2052,2053,2208,2052,2208,2207,2053,2054,2209,2053,2209,2208,2054,2055,2210,2054,2210,2209,2055,2056,2211,2055,2211,2210,2056,2057,2212,2056,2212,2211,2057,2058,2213,2057,2213,2212,2058,2059,2214,2058,2214,2213,2059,2060,2215,2059,2215,2214,2060,2061,2216,2060,2216,2215,2061,2062,2217,2061,2217,2216,2062,2063,2218,2062,2218,2217,2063,2064,2219,2063,2219,2218,2064,2065,2220,2064,2220,2219,2065,2066,2221,2065,2221,2220,2066,2067,2222,2066,2222,2221,2067,2068,2223,2067,2223,2222,2068,2069,2224,2068,2224,2223,2069,2070,2225,2069,2225,2224,2070,2071,2226,2070,2226,2225,2071,2072,2227,2071,2227,2226,2072,2073,2228,2072,2228,2227,2073,2074,2229,2073,2229,2228,2074,2075,2230,2074,2230,2229,2075,2076,2231,2075,2231,2230,2076,2077,2232,2076,2232,2231,2077,2078,2233,2077,2233,2232,2078,2079,2234,2078,2234,2233,2079,2080,2235,2079,2235,2234,2080,2081,2236,2080,2236,2235,2081,2082,2237,2081,2237,2236,2082,2083,2238,2082,2238,2237,2083,2084,2239,2083,2239,2238,2084,2085,2240,2084,2240,2239,2085,2086,2241,2085,2241,2240,2086,2087,2242,2086,2242,2241,2087,2088,2243,2087,2243,2242,2088,2089,2244,2088,2244,2243,2089,2090,2245,2089,2245,2244,2090,2091,2246,2090,2246,2245,2091,2092,2247,2091,2247,2246,2092,2093,2248,2092,2248,2247,2093,2094,2249,2093,2249,2248,2094,2095,2250,2094,2250,2249,2095,2096,2251,2095,2251,2250,2096,2097,2252,2096,2252,2251,2097,2098,2253,2097,2253,2252,2098,2099,2254,2098,2254,2253,2099,2100,2255,2099,2255,2254,2100,2101,2256,2100,2256,2255,2101,2102,2257,2101,2257,2256,2102,2103,2258,2102,2258,2257,2103,2104,2259,2103,2259,2258,2104,2105,2260,2104,2260,2259,2105,2106,2261,2105,2261,2260,2106,2107,2262,2106,2262,2261,2107,2108,2263,2107,2263,2262,2108,2109,2264,2108,2264,2263,2109,2110,2265,2109,2265,2264,2110,2111,2266,2110,2266,2265,2111,2112,2267,2111,2267,2266,2112,2113,2268,2112,2268,2267,2113,2114,2269,2113,2269,2268,2114,2115,2270,2114,2270,2269,2115,2116,2271,2115,2271,2270,2116,2117,2272,2116,2272,2271,2117,2118,2273,2117,2273,2272,2118,2119,2274,2118,2274,2273,2119,2120,2275,2119,2275,2274,2120,2121,2276,2120,2276,2275,2121,2122,2277,2121,2277,2276,2122,2123,2278,2122,2278,2277,2123,2124,2279,2123,2279,2278,2124,2125,2280,2124,2280,2279,2125,2126,2281,2125,2281,2280,2126,2127,2282,2126,2282,2281,2127,2128,2283,2127,2283,2282,2128,2129,2284,2128,2284,2283,2129,2130,2285,2129,2285,2284,2130,2131,2286,2130,2286,2285,2131,2132,2287,2131,2287,2286,2132,2133,2288,2132,2288,2287,2133,2134,2289,2133,2289,2288,2134,2135,2290,2134,2290,2289,2135,2136,2291,2135,2291,2290,2136,2137,2292,2136,2292,2291,2137,2138,2293,2137,2293,2292,2138,2139,2294,2138,2294,2293,2139,2140,2295,2139,2295,2294,2140,2141,2296,2140,2296,2295,2141,2142,2297,2141,2297,2296,2142,2143,2298,2142,2298,2297,2143,2144,2299,2143,2299,2298,2144,2145,2300,2144,2300,2299,2145,2146,2301,2145,2301,2300,2146,2147,2302,2146,2302,2301,2147,2148,2303,2147,2303,2302,2148,2149,2304,2148,2304,2303,2149,2150,2305,2149,2305,2304,2150,2151,2306,2150,2306,2305,2151,2152,2307,2151,2307,2306,2152,2153,2308,2152,2308,2307,2153,2154,2309,2153,2309,2308,2154,2155,2310,2154,2310,2309,2155,2156,2311,2155,2311,2310,2156,2157,2312,2156,2312,2311,2157,2158,2313,2157,2313,2312,2158,2159,2314,2158,2314,2313,2159,2160,2315,2159,2315,2314,2160,2161,2316,2160,2316,2315,2161,2162,2317,2161,2317,2316,2162,2163,2318,2162,2318,2317,2163,2164,2319,2163,2319,2318,2164,2165,2320,2164,2320,2319,2165,2166,2321,2165,2321,2320,2166,2167,2322,2166,2322,2321,2167,2168,2323,2167,2323,2322,2168,2169,2324,2168,2324,2323,2170,2171,2326,2170,2326,2325,2171,2172,2327,2171,2327,2326,2172,2173,2328,2172,2328,2327,2173,2174,2329,2173,2329,2328,2174,2175,2330,2174,2330,2329,2175,2176,2331,2175,2331,2330,2176,2177,2332,2176,2332,2331,2177,2178,2333,2177,2333,2332,2178,2179,2334,2178,2334,2333,2179,2180,2335,2179,2335,2334,2180,2181,2336,2180,2336,2335,2181,2182,2337,2181,2337,2336,2182,2183,2338,2182,2338,2337,2183,2184,2339,2183,2339,2338,2184,2185,2340,2184,2340,2339,2185,2186,2341,2185,2341,2340,2186,2187,2342,2186,2342,2341,2187,2188,2343,2187,2343,2342,2188,2189,2344,2188,2344,2343,2189,2190,2345,2189,2345,2344,2190,2191,2346,2190,2346,2345,2191,2192,2347,2191,2347,2346,2192,2193,2348,2192,2348,2347,2193,2194,2349,2193,2349,2348,2194,2195,2350,2194,2350,2349,2195,2196,2351,2195,2351,2350,2196,2197,2352,2196,2352,2351,2197,2198,2353,2197,2353,2352,2198,2199,2354,2198,2354,2353,2199,2200,2355,2199,2355,2354,2200,2201,2356,2200,2356,2355,2201,2202,2357,2201,2357,2356,2202,2203,2358,2202,2358,2357,2203,2204,2359,2203,2359,2358,2204,2205,2360,2204,2360,2359,2205,2206,2361,2205,2361,2360,2206,2207,2362,2206,2362,2361,2207,2208,2363,2207,2363,2362,2208,2209,2364,2208,2364,2363,2209,2210,2365,2209,2365,2364,2210,2211,2366,2210,2366,2365,2211,2212,2367,2211,2367,2366,2212,2213,2368,2212,2368,2367,2213,2214,2369,2213,2369,2368,2214,2215,2370,2214,2370,2369,2215,2216,2371,2215,2371,2370,2216,2217,2372,2216,2372,2371,2217,2218,2373,2217,2373,2372,2218,2219,2374,2218,2374,2373,2219,2220,2375,2219,2375,2374,2220,2221,2376,2220,2376,2375,2221,2222,2377,2221,2377,2376,2222,2223,2378,2222,2378,2377,2223,2224,2379,2223,2379,2378,2224,2225,2380,2224,2380,2379,2225,2226,2381,2225,2381,2380,2226,2227,2382,2226,2382,2381,2227,2228,2383,2227,2383,2382,2228,2229,2384,2228,2384,2383,2229,2230,2385,2229,2385,2384,2230,2231,2386,2230,2386,2385,2231,2232,2387,2231,2387,2386,2232,2233,2388,2232,2388,2387,2233,2234,2389,2233,2389,2388,2234,2235,2390,2234,2390,2389,2235,2236,2391,2235,2391,2390,2236,2237,2392,2236,2392,2391,2237,2238,2393,2237,2393,2392,2238,2239,2394,2238,2394,2393,2239,2240,2395,2239,2395,2394,2240,2241,2396,2240,2396,2395,2241,2242,2397,2241,2397,2396,2242,2243,2398,2242,2398,2397,2243,2244,2399,2243,2399,2398,2244,2245,2400,2244,2400,2399,2245,2246,2401,2245,2401,2400,2246,2247,2402,2246,2402,2401,2247,2248,2403,2247,2403,2402,2248,2249,2404,2248,2404,2403,2249,2250,2405,2249,2405,2404,2250,2251,2406,2250,2406,2405,2251,2252,2407,2251,2407,2406,2252,2253,2408,2252,2408,2407,2253,2254,2409,2253,2409,2408,2254,2255,2410,2254,2410,2409,2255,2256,2411,2255,2411,2410,2256,2257,2412,2256,2412,2411,2257,2258,2413,2257,2413,2412,2258,2259,2414,2258,2414,2413,2259,2260,2415,2259,2415,2414,2260,2261,2416,2260,2416,2415,2261,2262,2417,2261,2417,2416,2262,2263,2418,2262,2418,2417,2263,2264,2419,2263,2419,2418,2264,2265,2420,2264,2420,2419,2265,2266,2421,2265,2421,2420,2266,2267,2422,2266,2422,2421,2267,2268,2423,2267,2423,2422,2268,2269,2424,2268,2424,2423,2269,2270,2425,2269,2425,2424,2270,2271,2426,2270,2426,2425,2271,2272,2427,2271,2427,2426,2272,2273,2428,2272,2428,2427,2273,2274,2429,2273,2429,2428,2274,2275,2430,2274,2430,2429,2275,2276,2431,2275,2431,2430,2276,2277,2432,2276,2432,2431,2277,2278,2433,2277,2433,2432,2278,2279,2434,2278,2434,2433,2279,2280,2435,2279,2435,2434,2280,2281,2436,2280,2436,2435,2281,2282,2437,2281,2437,2436,2282,2283,2438,2282,2438,2437,2283,2284,2439,2283,2439,2438,2284,2285,2440,2284,2440,2439,2285,2286,2441,2285,2441,2440,2286,2287,2442,2286,2442,2441,2287,2288,2443,2287,2443,2442,2288,2289,2444,2288,2444,2443,2289,2290,2445,2289,2445,2444,2290,2291,2446,2290,2446,2445,2291,2292,2447,2291,2447,2446,2292,2293,2448,2292,2448,2447,2293,2294,2449,2293,2449,2448,2294,2295,2450,2294,2450,2449,2295,2296,2451,2295,2451,2450,2296,2297,2452,2296,2452,2451,2297,2298,2453,2297,2453,2452,2298,2299,2454,2298,2454,2453,2299,2300,2455,2299,2455,2454,2300,2301,2456,2300,2456,2455,2301,2302,2457,2301,2457,2456,2302,2303,2458,2302,2458,2457,2303,2304,2459,2303,2459,2458,2304,2305,2460,2304,2460,2459,2305,2306,2461,2305,2461,2460,2306,2307,2462,2306,2462,2461,2307,2308,2463,2307,2463,2462,2308,2309,2464,2308,2464,2463,2309,2310,2465,2309,2465,2464,2310,2311,2466,2310,2466,2465,2311,2312,2467,2311,2467,2466,2312,2313,2468,2312,2468,2467,2313,2314,2469,2313,2469,2468,2314,2315,2470,2314,2470,2469,2315,2316,2471,2315,2471,2470,2316,2317,2472,2316,2472,2471,2317,2318,2473,2317,2473,2472,2318,2319,2474,2318,2474,2473,2319,2320,2475,2319,2475,2474,2320,2321,2476,2320,2476,2475,2321,2322,2477,2321,2477,2476,2322,2323,2478,2322,2478,2477,2323,2324,2479,2323,2479,2478,2325,2326,2481,2325,2481,2480,2326,2327,2482,2326,2482,2481,2327,2328,2483,2327,2483,2482,2328,2329,2484,2328,2484,2483,2329,2330,2485,2329,2485,2484,2330,2331,2486,2330,2486,2485,2331,2332,2487,2331,2487,2486,2332,2333,2488,2332,2488,2487,2333,2334,2489,2333,2489,2488,2334,2335,2490,2334,2490,2489,2335,2336,2491,2335,2491,2490,2336,2337,2492,2336,2492,2491,2337,2338,2493,2337,2493,2492,2338,2339,2494,2338,2494,2493,2339,2340,2495,2339,2495,2494,2340,2341,2496,2340,2496,2495,2341,2342,2497,2341,2497,2496,2342,2343,2498,2342,2498,2497,2343,2344,2499,2343,2499,2498,2344,2345,2500,2344,2500,2499,2345,2346,2501,2345,2501,2500,2346,2347,2502,2346,2502,2501,2347,2348,2503,2347,2503,2502,2348,2349,2504,2348,2504,2503,2349,2350,2505,2349,2505,2504,2350,2351,2506,2350,2506,2505,2351,2352,2507,2351,2507,2506,2352,2353,2508,2352,2508,2507,2353,2354,2509,2353,2509,2508,2354,2355,2510,2354,2510,2509,2355,2356,2511,2355,2511,2510,2356,2357,2512,2356,2512,2511,2357,2358,2513,2357,2513,2512,2358,2359,2514,2358,2514,2513,2359,2360,2515,2359,2515,2514,2360,2361,2516,2360,2516,2515,2361,2362,2517,2361,2517,2516,2362,2363,2518,2362,2518,2517,2363,2364,2519,2363,2519,2518,2364,2365,2520,2364,2520,2519,2365,2366,2521,2365,2521,2520,2366,2367,2522,2366,2522,2521,2367,2368,2523,2367,2523,2522,2368,2369,2524,2368,2524,2523,2369,2370,2525,2369,2525,2524,2370,2371,2526,2370,2526,2525,2371,2372,2527,2371,2527,2526,2372,2373,2528,2372,2528,2527,2373,2374,2529,2373,2529,2528,2374,2375,2530,2374,2530,2529,2375,2376,2531,2375,2531,2530,2376,2377,2532,2376,2532,2531,2377,2378,2533,2377,2533,2532,2378,2379,2534,2378,2534,2533,2379,2380,2535,2379,2535,2534,2380,2381,2536,2380,2536,2535,2381,2382,2537,2381,2537,2536,2382,2383,2538,2382,2538,2537,2383,2384,2539,2383,2539,2538,2384,2385,2540,2384,2540,2539,2385,2386,2541,2385,2541,2540,2386,2387,2542,2386,2542,2541,2387,2388,2543,2387,2543,2542,2388,2389,2544,2388,2544,2543,2389,2390,2545,2389,2545,2544,2390,2391,2546,2390,2546,2545,2391,2392,2547,2391,2547,2546,2392,2393,2548,2392,2548,2547,2393,2394,2549,2393,2549,2548,2394,2395,2550,2394,2550,2549,2395,2396,2551,2395,2551,2550,2396,2397,2552,2396,2552,2551,2397,2398,2553,2397,2553,2552,2398,2399,2554,2398,2554,2553,2399,2400,2555,2399,2555,2554,2400,2401,2556,2400,2556,2555,2401,2402,2557,2401,2557,2556,2402,2403,2558,2402,2558,2557,2403,2404,2559,2403,2559,2558,2404,2405,2560,2404,2560,2559,2405,2406,2561,2405,2561,2560,2406,2407,2562,2406,2562,2561,2407,2408,2563,2407,2563,2562,2408,2409,2564,2408,2564,2563,2409,2410,2565,2409,2565,2564,2410,2411,2566,2410,2566,2565,2411,2412,2567,2411,2567,2566,2412,2413,2568,2412,2568,2567,2413,2414,2569,2413,2569,2568,2414,2415,2570,2414,2570,2569,2415,2416,2571,2415,2571,2570,2416,2417,2572,2416,2572,2571,2417,2418,2573,2417,2573,2572,2418,2419,2574,2418,2574,2573,2419,2420,2575,2419,2575,2574,2420,2421,2576,2420,2576,2575,2421,2422,2577,2421,2577,2576,2422,2423,2578,2422,2578,2577,2423,2424,2579,2423,2579,2578,2424,2425,2580,2424,2580,2579,2425,2426,2581,2425,2581,2580,2426,2427,2582,2426,2582,2581,2427,2428,2583,2427,2583,2582,2428,2429,2584,2428,2584,2583,2429,2430,2585,2429,2585,2584,2430,2431,2586,2430,2586,2585,2431,2432,2587,2431,2587,2586,2432,2433,2588,2432,2588,2587,2433,2434,2589,2433,2589,2588,2434,2435,2590,2434,2590,2589,2435,2436,2591,2435,2591,2590,2436,2437,2592,2436,2592,2591,2437,2438,2593,2437,2593,2592,2438,2439,2594,2438,2594,2593,2439,2440,2595,2439,2595,2594,2440,2441,2596,2440,2596,2595,2441,2442,2597,2441,2597,2596,2442,2443,2598,2442,2598,2597,2443,2444,2599,2443,2599,2598,2444,2445,2600,2444,2600,2599,2445,2446,2601,2445,2601,2600,2446,2447,2602,2446,2602,2601,2447,2448,2603,2447,2603,2602,2448,2449,2604,2448,2604,2603,2449,2450,2605,2449,2605,2604,2450,2451,2606,2450,2606,2605,2451,2452,2607,2451,2607,2606,2452,2453,2608,2452,2608,2607,2453,2454,2609,2453,2609,2608,2454,2455,2610,2454,2610,2609,2455,2456,2611,2455,2611,2610,2456,2457,2612,2456,2612,2611,2457,2458,2613,2457,2613,2612,2458,2459,2614,2458,2614,2613,2459,2460,2615,2459,2615,2614,2460,2461,2616,2460,2616,2615,2461,2462,2617,2461,2617,2616,2462,2463,2618,2462,2618,2617,2463,2464,2619,2463,2619,2618,2464,2465,2620,2464,2620,2619,2465,2466,2621,2465,2621,2620,2466,2467,2622,2466,2622,2621,2467,2468,2623,2467,2623,2622,2468,2469,2624,2468,2624,2623,2469,2470,2625,2469,2625,2624,2470,2471,2626,2470,2626,2625,2471,2472,2627,2471,2627,2626,2472,2473,2628,2472,2628,2627,2473,2474,2629,2473,2629,2628,2474,2475,2630,2474,2630,2629,2475,2476,2631,2475,2631,2630,2476,2477,2632,2476,2632,2631,2477,2478,2633,2477,2633,2632,2478,2479,2634,2478,2634,2633,2480,2481,2636,2480,2636,2635,2481,2482,2637,2481,2637,2636,2482,2483,2638,2482,2638,2637,2483,2484,2639,2483,2639,2638,2484,2485,2640,2484,2640,2639,2485,2486,2641,2485,2641,2640,2486,2487,2642,2486,2642,2641,2487,2488,2643,2487,2643,2642,2488,2489,2644,2488,2644,2643,2489,2490,2645,2489,2645,2644,2490,2491,2646,2490,2646,2645,2491,2492,2647,2491,2647,2646,2492,2493,2648,2492,2648,2647,2493,2494,2649,2493,2649,2648,2494,2495,2650,2494,2650,2649,2495,2496,2651,2495,2651,2650,2496,2497,2652,2496,2652,2651,2497,2498,2653,2497,2653,2652,2498,2499,2654,2498,2654,2653,2499,2500,2655,2499,2655,2654,2500,2501,2656,2500,2656,2655,2501,2502,2657,2501,2657,2656,2502,2503,2658,2502,2658,2657,2503,2504,2659,2503,2659,2658,2504,2505,2660,2504,2660,2659,2505,2506,2661,2505,2661,2660,2506,2507,2662,2506,2662,2661,2507,2508,2663,2507,2663,2662,2508,2509,2664,2508,2664,2663,2509,2510,2665,2509,2665,2664,2510,2511,2666,2510,2666,2665,2511,2512,2667,2511,2667,2666,2512,2513,2668,2512,2668,2667,2513,2514,2669,2513,2669,2668,2514,2515,2670,2514,2670,2669,2515,2516,2671,2515,2671,2670,2516,2517,2672,2516,2672,2671,2517,2518,2673,2517,2673,2672,2518,2519,2674,2518,2674,2673,2519,2520,2675,2519,2675,2674,2520,2521,2676,2520,2676,2675,2521,2522,2677,2521,2677,2676,2522,2523,2678,2522,2678,2677,2523,2524,2679,2523,2679,2678,2524,2525,2680,2524,2680,2679,2525,2526,2681,2525,2681,2680,2526,2527,2682,2526,2682,2681,2527,2528,2683,2527,2683,2682,2528,2529,2684,2528,2684,2683,2529,2530,2685,2529,2685,2684,2530,2531,2686,2530,2686,2685,2531,2532,2687,2531,2687,2686,2532,2533,2688,2532,2688,2687,2533,2534,2689,2533,2689,2688,2534,2535,2690,2534,2690,2689,2535,2536,2691,2535,2691,2690,2536,2537,2692,2536,2692,2691,2537,2538,2693,2537,2693,2692,2538,2539,2694,2538,2694,2693,2539,2540,2695,2539,2695,2694,2540,2541,2696,2540,2696,2695,2541,2542,2697,2541,2697,2696,2542,2543,2698,2542,2698,2697,2543,2544,2699,2543,2699,2698,2544,2545,2700,2544,2700,2699,2545,2546,2701,2545,2701,2700,2546,2547,2702,2546,2702,2701,2547,2548,2703,2547,2703,2702,2548,2549,2704,2548,2704,2703,2549,2550,2705,2549,2705,2704,2550,2551,2706,2550,2706,2705,2551,2552,2707,2551,2707,2706,2552,2553,2708,2552,2708,2707,2553,2554,2709,2553,2709,2708,2554,2555,2710,2554,2710,2709,2555,2556,2711,2555,2711,2710,2556,2557,2712,2556,2712,2711,2557,2558,2713,2557,2713,2712,2558,2559,2714,2558,2714,2713,2559,2560,2715,2559,2715,2714,2560,2561,2716,2560,2716,2715,2561,2562,2717,2561,2717,2716,2562,2563,2718,2562,2718,2717,2563,2564,2719,2563,2719,2718,2564,2565,2720,2564,2720,2719,2565,2566,2721,2565,2721,2720,2566,2567,2722,2566,2722,2721,2567,2568,2723,2567,2723,2722,2568,2569,2724,2568,2724,2723,2569,2570,2725,2569,2725,2724,2570,2571,2726,2570,2726,2725,2571,2572,2727,2571,2727,2726,2572,2573,2728,2572,2728,2727,2573,2574,2729,2573,2729,2728,2574,2575,2730,2574,2730,2729,2575,2576,2731,2575,2731,2730,2576,2577,2732,2576,2732,2731,2577,2578,2733,2577,2733,2732,2578,2579,2734,2578,2734,2733,2579,2580,2735,2579,2735,2734,2580,2581,2736,2580,2736,2735,2581,2582,2737,2581,2737,2736,2582,2583,2738,2582,2738,2737,2583,2584,2739,2583,2739,2738,2584,2585,2740,2584,2740,2739,2585,2586,2741,2585,2741,2740,2586,2587,2742,2586,2742,2741,2587,2588,2743,2587,2743,2742,2588,2589,2744,2588,2744,2743,2589,2590,2745,2589,2745,2744,2590,2591,2746,2590,2746,2745,2591,2592,2747,2591,2747,2746,2592,2593,2748,2592,2748,2747,2593,2594,2749,2593,2749,2748,2594,2595,2750,2594,2750,2749,2595,2596,2751,2595,2751,2750,2596,2597,2752,2596,2752,2751,2597,2598,2753,2597,2753,2752,2598,2599,2754,2598,2754,2753,2599,2600,2755,2599,2755,2754,2600,2601,2756,2600,2756,2755,2601,2602,2757,2601,2757,2756,2602,2603,2758,2602,2758,2757,2603,2604,2759,2603,2759,2758,2604,2605,2760,2604,2760,2759,2605,2606,2761,2605,2761,2760,2606,2607,2762,2606,2762,2761,2607,2608,2763,2607,2763,2762,2608,2609,2764,2608,2764,2763,2609,2610,2765,2609,2765,2764,2610,2611,2766,2610,2766,2765,2611,2612,2767,2611,2767,2766,2612,2613,2768,2612,2768,2767,2613,2614,2769,2613,2769,2768,2614,2615,2770,2614,2770,2769,2615,2616,2771,2615,2771,2770,2616,2617,2772,2616,2772,2771,2617,2618,2773,2617,2773,2772,2618,2619,2774,2618,2774,2773,2619,2620,2775,2619,2775,2774,2620,2621,2776,2620,2776,2775,2621,2622,2777,2621,2777,2776,2622,2623,2778,2622,2778,2777,2623,2624,2779,2623,2779,2778,2624,2625,2780,2624,2780,2779,2625,2626,2781,2625,2781,2780,2626,2627,2782,2626,2782,2781,2627,2628,2783,2627,2783,2782,2628,2629,2784,2628,2784,2783,2629,2630,2785,2629,2785,2784,2630,2631,2786,2630,2786,2785,2631,2632,2787,2631,2787,2786,2632,2633,2788,2632,2788,2787,2633,2634,2789,2633,2789,2788,2635,2636,2791,2635,2791,2790,2636,2637,2792,2636,2792,2791,2637,2638,2793,2637,2793,2792,2638,2639,2794,2638,2794,2793,2639,2640,2795,2639,2795,2794,2640,2641,2796,2640,2796,2795,2641,2642,2797,2641,2797,2796,2642,2643,2798,2642,2798,2797,2643,2644,2799,2643,2799,2798,2644,2645,2800,2644,2800,2799,2645,2646,2801,2645,2801,2800,2646,2647,2802,2646,2802,2801,2647,2648,2803,2647,2803,2802,2648,2649,2804,2648,2804,2803,2649,2650,2805,2649,2805,2804,2650,2651,2806,2650,2806,2805,2651,2652,2807,2651,2807,2806,2652,2653,2808,2652,2808,2807,2653,2654,2809,2653,2809,2808,2654,2655,2810,2654,2810,2809,2655,2656,2811,2655,2811,2810,2656,2657,2812,2656,2812,2811,2657,2658,2813,2657,2813,2812,2658,2659,2814,2658,2814,2813,2659,2660,2815,2659,2815,2814,2660,2661,2816,2660,2816,2815,2661,2662,2817,2661,2817,2816,2662,2663,2818,2662,2818,2817,2663,2664,2819,2663,2819,2818,2664,2665,2820,2664,2820,2819,2665,2666,2821,2665,2821,2820,2666,2667,2822,2666,2822,2821,2667,2668,2823,2667,2823,2822,2668,2669,2824,2668,2824,2823,2669,2670,2825,2669,2825,2824,2670,2671,2826,2670,2826,2825,2671,2672,2827,2671,2827,2826,2672,2673,2828,2672,2828,2827,2673,2674,2829,2673,2829,2828,2674,2675,2830,2674,2830,2829,2675,2676,2831,2675,2831,2830,2676,2677,2832,2676,2832,2831,2677,2678,2833,2677,2833,2832,2678,2679,2834,2678,2834,2833,2679,2680,2835,2679,2835,2834,2680,2681,2836,2680,2836,2835,2681,2682,2837,2681,2837,2836,2682,2683,2838,2682,2838,2837,2683,2684,2839,2683,2839,2838,2684,2685,2840,2684,2840,2839,2685,2686,2841,2685,2841,2840,2686,2687,2842,2686,2842,2841,2687,2688,2843,2687,2843,2842,2688,2689,2844,2688,2844,2843,2689,2690,2845,2689,2845,2844,2690,2691,2846,2690,2846,2845,2691,2692,2847,2691,2847,2846,2692,2693,2848,2692,2848,2847,2693,2694,2849,2693,2849,2848,2694,2695,2850,2694,2850,2849,2695,2696,2851,2695,2851,2850,2696,2697,2852,2696,2852,2851,2697,2698,2853,2697,2853,2852,2698,2699,2854,2698,2854,2853,2699,2700,2855,2699,2855,2854,2700,2701,2856,2700,2856,2855,2701,2702,2857,2701,2857,2856,2702,2703,2858,2702,2858,2857,2703,2704,2859,2703,2859,2858,2704,2705,2860,2704,2860,2859,2705,2706,2861,2705,2861,2860,2706,2707,2862,2706,2862,2861,2707,2708,2863,2707,2863,2862,2708,2709,2864,2708,2864,2863,2709,2710,2865,2709,2865,2864,2710,2711,2866,2710,2866,2865,2711,2712,2867,2711,2867,2866,2712,2713,2868,2712,2868,2867,2713,2714,2869,2713,2869,2868,2714,2715,2870,2714,2870,2869,2715,2716,2871,2715,2871,2870,2716,2717,2872,2716,2872,2871,2717,2718,2873,2717,2873,2872,2718,2719,2874,2718,2874,2873,2719,2720,2875,2719,2875,2874,2720,2721,2876,2720,2876,2875,2721,2722,2877,2721,2877,2876,2722,2723,2878,2722,2878,2877,2723,2724,2879,2723,2879,2878,2724,2725,2880,2724,2880,2879,2725,2726,2881,2725,2881,2880,2726,2727,2882,2726,2882,2881,2727,2728,2883,2727,2883,2882,2728,2729,2884,2728,2884,2883,2729,2730,2885,2729,2885,2884,2730,2731,2886,2730,2886,2885,2731,2732,2887,2731,2887,2886,2732,2733,2888,2732,2888,2887,2733,2734,2889,2733,2889,2888,2734,2735,2890,2734,2890,2889,2735,2736,2891,2735,2891,2890,2736,2737,2892,2736,2892,2891,2737,2738,2893,2737,2893,2892,2738,2739,2894,2738,2894,2893,2739,2740,2895,2739,2895,2894,2740,2741,2896,2740,2896,2895,2741,2742,2897,2741,2897,2896,2742,2743,2898,2742,2898,2897,2743,2744,2899,2743,2899,2898,2744,2745,2900,2744,2900,2899,2745,2746,2901,2745,2901,2900,2746,2747,2902,2746,2902,2901,2747,2748,2903,2747,2903,2902,2748,2749,2904,2748,2904,2903,2749,2750,2905,2749,2905,2904,2750,2751,2906,2750,2906,2905,2751,2752,2907,2751,2907,2906,2752,2753,2908,2752,2908,2907,2753,2754,2909,2753,2909,2908,2754,2755,2910,2754,2910,2909,2755,2756,2911,2755,2911,2910,2756,2757,2912,2756,2912,2911,2757,2758,2913,2757,2913,2912,2758,2759,2914,2758,2914,2913,2759,2760,2915,2759,2915,2914,2760,2761,2916,2760,2916,2915,2761,2762,2917,2761,2917,2916,2762,2763,2918,2762,2918,2917,2763,2764,2919,2763,2919,2918,2764,2765,2920,2764,2920,2919,2765,2766,2921,2765,2921,2920,2766,2767,2922,2766,2922,2921,2767,2768,2923,2767,2923,2922,2768,2769,2924,2768,2924,2923,2769,2770,2925,2769,2925,2924,2770,2771,2926,2770,2926,2925,2771,2772,2927,2771,2927,2926,2772,2773,2928,2772,2928,2927,2773,2774,2929,2773,2929,2928,2774,2775,2930,2774,2930,2929,2775,2776,2931,2775,2931,2930,2776,2777,2932,2776,2932,2931,2777,2778,2933,2777,2933,2932,2778,2779,2934,2778,2934,2933,2779,2780,2935,2779,2935,2934,2780,2781,2936,2780,2936,2935,2781,2782,2937,2781,2937,2936,2782,2783,2938,2782,2938,2937,2783,2784,2939,2783,2939,2938,2784,2785,2940,2784,2940,2939,2785,2786,2941,2785,2941,2940,2786,2787,2942,2786,2942,2941,2787,2788,2943,2787,2943,2942,2788,2789,2944,2788,2944,2943,2790,2791,2946,2790,2946,2945,2791,2792,2947,2791,2947,2946,2792,2793,2948,2792,2948,2947,2793,2794,2949,2793,2949,2948,2794,2795,2950,2794,2950,2949,2795,2796,2951,2795,2951,2950,2796,2797,2952,2796,2952,2951,2797,2798,2953,2797,2953,2952,2798,2799,2954,2798,2954,2953,2799,2800,2955,2799,2955,2954,2800,2801,2956,2800,2956,2955,2801,2802,2957,2801,2957,2956,2802,2803,2958,2802,2958,2957,2803,2804,2959,2803,2959,2958,2804,2805,2960,2804,2960,2959,2805,2806,2961,2805,2961,2960,2806,2807,2962,2806,2962,2961,2807,2808,2963,2807,2963,2962,2808,2809,2964,2808,2964,2963,2809,2810,2965,2809,2965,2964,2810,2811,2966,2810,2966,2965,2811,2812,2967,2811,2967,2966,2812,2813,2968,2812,2968,2967,2813,2814,2969,2813,2969,2968,2814,2815,2970,2814,2970,2969,2815,2816,2971,2815,2971,2970,2816,2817,2972,2816,2972,2971,2817,2818,2973,2817,2973,2972,2818,2819,2974,2818,2974,2973,2819,2820,2975,2819,2975,2974,2820,2821,2976,2820,2976,2975,2821,2822,2977,2821,2977,2976,2822,2823,2978,2822,2978,2977,2823,2824,2979,2823,2979,2978,2824,2825,2980,2824,2980,2979,2825,2826,2981,2825,2981,2980,2826,2827,2982,2826,2982,2981,2827,2828,2983,2827,2983,2982,2828,2829,2984,2828,2984,2983,2829,2830,2985,2829,2985,2984,2830,2831,2986,2830,2986,2985,2831,2832,2987,2831,2987,2986,2832,2833,2988,2832,2988,2987,2833,2834,2989,2833,2989,2988,2834,2835,2990,2834,2990,2989,2835,2836,2991,2835,2991,2990,2836,2837,2992,2836,2992,2991,2837,2838,2993,2837,2993,2992,2838,2839,2994,2838,2994,2993,2839,2840,2995,2839,2995,2994,2840,2841,2996,2840,2996,2995,2841,2842,2997,2841,2997,2996,2842,2843,2998,2842,2998,2997,2843,2844,2999,2843,2999,2998,2844,2845,3000,2844,3000,2999,2845,2846,3001,2845,3001,3000,2846,2847,3002,2846,3002,3001,2847,2848,3003,2847,3003,3002,2848,2849,3004,2848,3004,3003,2849,2850,3005,2849,3005,3004,2850,2851,3006,2850,3006,3005,2851,2852,3007,2851,3007,3006,2852,2853,3008,2852,3008,3007,2853,2854,3009,2853,3009,3008,2854,2855,3010,2854,3010,3009,2855,2856,3011,2855,3011,3010,2856,2857,3012,2856,3012,3011,2857,2858,3013,2857,3013,3012,2858,2859,3014,2858,3014,3013,2859,2860,3015,2859,3015,3014,2860,2861,3016,2860,3016,3015,2861,2862,3017,2861,3017,3016,2862,2863,3018,2862,3018,3017,2863,2864,3019,2863,3019,3018,2864,2865,3020,2864,3020,3019,2865,2866,3021,2865,3021,3020,2866,2867,3022,2866,3022,3021,2867,2868,3023,2867,3023,3022,2868,2869,3024,2868,3024,3023,2869,2870,3025,2869,3025,3024,2870,2871,3026,2870,3026,3025,2871,2872,3027,2871,3027,3026,2872,2873,3028,2872,3028,3027,2873,2874,3029,2873,3029,3028,2874,2875,3030,2874,3030,3029,2875,2876,3031,2875,3031,3030,2876,2877,3032,2876,3032,3031,2877,2878,3033,2877,3033,3032,2878,2879,3034,2878,3034,3033,2879,2880,3035,2879,3035,3034,2880,2881,3036,2880,3036,3035,2881,2882,3037,2881,3037,3036,2882,2883,3038,2882,3038,3037,2883,2884,3039,2883,3039,3038,2884,2885,3040,2884,3040,3039,2885,2886,3041,2885,3041,3040,2886,2887,3042,2886,3042,3041,2887,2888,3043,2887,3043,3042,2888,2889,3044,2888,3044,3043,2889,2890,3045,2889,3045,3044,2890,2891,3046,2890,3046,3045,2891,2892,3047,2891,3047,3046,2892,2893,3048,2892,3048,3047,2893,2894,3049,2893,3049,3048,2894,2895,3050,2894,3050,3049,2895,2896,3051,2895,3051,3050,2896,2897,3052,2896,3052,3051,2897,2898,3053,2897,3053,3052,2898,2899,3054,2898,3054,3053,2899,2900,3055,2899,3055,3054,2900,2901,3056,2900,3056,3055,2901,2902,3057,2901,3057,3056,2902,2903,3058,2902,3058,3057,2903,2904,3059,2903,3059,3058,2904,2905,3060,2904,3060,3059,2905,2906,3061,2905,3061,3060,2906,2907,3062,2906,3062,3061,2907,2908,3063,2907,3063,3062,2908,2909,3064,2908,3064,3063,2909,2910,3065,2909,3065,3064,2910,2911,3066,2910,3066,3065,2911,2912,3067,2911,3067,3066,2912,2913,3068,2912,3068,3067,2913,2914,3069,2913,3069,3068,2914,2915,3070,2914,3070,3069,2915,2916,3071,2915,3071,3070,2916,2917,3072,2916,3072,3071,2917,2918,3073,2917,3073,3072,2918,2919,3074,2918,3074,3073,2919,2920,3075,2919,3075,3074,2920,2921,3076,2920,3076,3075,2921,2922,3077,2921,3077,3076,2922,2923,3078,2922,3078,3077,2923,2924,3079,2923,3079,3078,2924,2925,3080,2924,3080,3079,2925,2926,3081,2925,3081,3080,2926,2927,3082,2926,3082,3081,2927,2928,3083,2927,3083,3082,2928,2929,3084,2928,3084,3083,2929,2930,3085,2929,3085,3084,2930,2931,3086,2930,3086,3085,2931,2932,3087,2931,3087,3086,2932,2933,3088,2932,3088,3087,2933,2934,3089,2933,3089,3088,2934,2935,3090,2934,3090,3089,2935,2936,3091,2935,3091,3090,2936,2937,3092,2936,3092,3091,2937,2938,3093,2937,3093,3092,2938,2939,3094,2938,3094,3093,2939,2940,3095,2939,3095,3094,2940,2941,3096,2940,3096,3095,2941,2942,3097,2941,3097,3096,2942,2943,3098,2942,3098,3097,2943,2944,3099,2943,3099,3098,2945,2946,3101,2945,3101,3100,2946,2947,3102,2946,3102,3101,2947,2948,3103,2947,3103,3102,2948,2949,3104,2948,3104,3103,2949,2950,3105,2949,3105,3104,2950,2951,3106,2950,3106,3105,2951,2952,3107,2951,3107,3106,2952,2953,3108,2952,3108,3107,2953,2954,3109,2953,3109,3108,2954,2955,3110,2954,3110,3109,2955,2956,3111,2955,3111,3110,2956,2957,3112,2956,3112,3111,2957,2958,3113,2957,3113,3112,2958,2959,3114,2958,3114,3113,2959,2960,3115,2959,3115,3114,2960,2961,3116,2960,3116,3115,2961,2962,3117,2961,3117,3116,2962,2963,3118,2962,3118,3117,2963,2964,3119,2963,3119,3118,2964,2965,3120,2964,3120,3119,2965,2966,3121,2965,3121,3120,2966,2967,3122,2966,3122,3121,2967,2968,3123,2967,3123,3122,2968,2969,3124,2968,3124,3123,2969,2970,3125,2969,3125,3124,2970,2971,3126,2970,3126,3125,2971,2972,3127,2971,3127,3126,2972,2973,3128,2972,3128,3127,2973,2974,3129,2973,3129,3128,2974,2975,3130,2974,3130,3129,2975,2976,3131,2975,3131,3130,2976,2977,3132,2976,3132,3131,2977,2978,3133,2977,3133,3132,2978,2979,3134,2978,3134,3133,2979,2980,3135,2979,3135,3134,2980,2981,3136,2980,3136,3135,2981,2982,3137,2981,3137,3136,2982,2983,3138,2982,3138,3137,2983,2984,3139,2983,3139,3138,2984,2985,3140,2984,3140,3139,2985,2986,3141,2985,3141,3140,2986,2987,3142,2986,3142,3141,2987,2988,3143,2987,3143,3142,2988,2989,3144,2988,3144,3143,2989,2990,3145,2989,3145,3144,2990,2991,3146,2990,3146,3145,2991,2992,3147,2991,3147,3146,2992,2993,3148,2992,3148,3147,2993,2994,3149,2993,3149,3148,2994,2995,3150,2994,3150,3149,2995,2996,3151,2995,3151,3150,2996,2997,3152,2996,3152,3151,2997,2998,3153,2997,3153,3152,2998,2999,3154,2998,3154,3153,2999,3000,3155,2999,3155,3154,3000,3001,3156,3000,3156,3155,3001,3002,3157,3001,3157,3156,3002,3003,3158,3002,3158,3157,3003,3004,3159,3003,3159,3158,3004,3005,3160,3004,3160,3159,3005,3006,3161,3005,3161,3160,3006,3007,3162,3006,3162,3161,3007,3008,3163,3007,3163,3162,3008,3009,3164,3008,3164,3163,3009,3010,3165,3009,3165,3164,3010,3011,3166,3010,3166,3165,3011,3012,3167,3011,3167,3166,3012,3013,3168,3012,3168,3167,3013,3014,3169,3013,3169,3168,3014,3015,3170,3014,3170,3169,3015,3016,3171,3015,3171,3170,3016,3017,3172,3016,3172,3171,3017,3018,3173,3017,3173,3172,3018,3019,3174,3018,3174,3173,3019,3020,3175,3019,3175,3174,3020,3021,3176,3020,3176,3175,3021,3022,3177,3021,3177,3176,3022,3023,3178,3022,3178,3177,3023,3024,3179,3023,3179,3178,3024,3025,3180,3024,3180,3179,3025,3026,3181,3025,3181,3180,3026,3027,3182,3026,3182,3181,3027,3028,3183,3027,3183,3182,3028,3029,3184,3028,3184,3183,3029,3030,3185,3029,3185,3184,3030,3031,3186,3030,3186,3185,3031,3032,3187,3031,3187,3186,3032,3033,3188,3032,3188,3187,3033,3034,3189,3033,3189,3188,3034,3035,3190,3034,3190,3189,3035,3036,3191,3035,3191,3190,3036,3037,3192,3036,3192,3191,3037,3038,3193,3037,3193,3192,3038,3039,3194,3038,3194,3193,3039,3040,3195,3039,3195,3194,3040,3041,3196,3040,3196,3195,3041,3042,3197,3041,3197,3196,3042,3043,3198,3042,3198,3197,3043,3044,3199,3043,3199,3198,3044,3045,3200,3044,3200,3199,3045,3046,3201,3045,3201,3200,3046,3047,3202,3046,3202,3201,3047,3048,3203,3047,3203,3202,3048,3049,3204,3048,3204,3203,3049,3050,3205,3049,3205,3204,3050,3051,3206,3050,3206,3205,3051,3052,3207,3051,3207,3206,3052,3053,3208,3052,3208,3207,3053,3054,3209,3053,3209,3208,3054,3055,3210,3054,3210,3209,3055,3056,3211,3055,3211,3210,3056,3057,3212,3056,3212,3211,3057,3058,3213,3057,3213,3212,3058,3059,3214,3058,3214,3213,3059,3060,3215,3059,3215,3214,3060,3061,3216,3060,3216,3215,3061,3062,3217,3061,3217,3216,3062,3063,3218,3062,3218,3217,3063,3064,3219,3063,3219,3218,3064,3065,3220,3064,3220,3219,3065,3066,3221,3065,3221,3220,3066,3067,3222,3066,3222,3221,3067,3068,3223,3067,3223,3222,3068,3069,3224,3068,3224,3223,3069,3070,3225,3069,3225,3224,3070,3071,3226,3070,3226,3225,3071,3072,3227,3071,3227,3226,3072,3073,3228,3072,3228,3227,3073,3074,3229,3073,3229,3228,3074,3075,3230,3074,3230,3229,3075,3076,3231,3075,3231,3230,3076,3077,3232,3076,3232,3231,3077,3078,3233,3077,3233,3232,3078,3079,3234,3078,3234,3233,3079,3080,3235,3079,3235,3234,3080,3081,3236,3080,3236,3235,3081,3082,3237,3081,3237,3236,3082,3083,3238,3082,3238,3237,3083,3084,3239,3083,3239,3238,3084,3085,3240,3084,3240,3239,3085,3086,3241,3085,3241,3240,3086,3087,3242,3086,3242,3241,3087,3088,3243,3087,3243,3242,3088,3089,3244,3088,3244,3243,3089,3090,3245,3089,3245,3244,3090,3091,3246,3090,3246,3245,3091,3092,3247,3091,3247,3246,3092,3093,3248,3092,3248,3247,3093,3094,3249,3093,3249,3248,3094,3095,3250,3094,3250,3249,3095,3096,3251,3095,3251,3250,3096,3097,3252,3096,3252,3251,3097,3098,3253,3097,3253,3252,3098,3099,3254,3098,3254,3253,3100,3101,3256,3100,3256,3255,3101,3102,3257,3101,3257,3256,3102,3103,3258,3102,3258,3257,3103,3104,3259,3103,3259,3258,3104,3105,3260,3104,3260,3259,3105,3106,3261,3105,3261,3260,3106,3107,3262,3106,3262,3261,3107,3108,3263,3107,3263,3262,3108,3109,3264,3108,3264,3263,3109,3110,3265,3109,3265,3264,3110,3111,3266,3110,3266,3265,3111,3112,3267,3111,3267,3266,3112,3113,3268,3112,3268,3267,3113,3114,3269,3113,3269,3268,3114,3115,3270,3114,3270,3269,3115,3116,3271,3115,3271,3270,3116,3117,3272,3116,3272,3271,3117,3118,3273,3117,3273,3272,3118,3119,3274,3118,3274,3273,3119,3120,3275,3119,3275,3274,3120,3121,3276,3120,3276,3275,3121,3122,3277,3121,3277,3276,3122,3123,3278,3122,3278,3277,3123,3124,3279,3123,3279,3278,3124,3125,3280,3124,3280,3279,3125,3126,3281,3125,3281,3280,3126,3127,3282,3126,3282,3281,3127,3128,3283,3127,3283,3282,3128,3129,3284,3128,3284,3283,3129,3130,3285,3129,3285,3284,3130,3131,3286,3130,3286,3285,3131,3132,3287,3131,3287,3286,3132,3133,3288,3132,3288,3287,3133,3134,3289,3133,3289,3288,3134,3135,3290,3134,3290,3289,3135,3136,3291,3135,3291,3290,3136,3137,3292,3136,3292,3291,3137,3138,3293,3137,3293,3292,3138,3139,3294,3138,3294,3293,3139,3140,3295,3139,3295,3294,3140,3141,3296,3140,3296,3295,3141,3142,3297,3141,3297,3296,3142,3143,3298,3142,3298,3297,3143,3144,3299,3143,3299,3298,3144,3145,3300,3144,3300,3299,3145,3146,3301,3145,3301,3300,3146,3147,3302,3146,3302,3301,3147,3148,3303,3147,3303,3302,3148,3149,3304,3148,3304,3303,3149,3150,3305,3149,3305,3304,3150,3151,3306,3150,3306,3305,3151,3152,3307,3151,3307,3306,3152,3153,3308,3152,3308,3307,3153,3154,3309,3153,3309,3308,3154,3155,3310,3154,3310,3309,3155,3156,3311,3155,3311,3310,3156,3157,3312,3156,3312,3311,3157,3158,3313,3157,3313,3312,3158,3159,3314,3158,3314,3313,3159,3160,3315,3159,3315,3314,3160,3161,3316,3160,3316,3315,3161,3162,3317,3161,3317,3316,3162,3163,3318,3162,3318,3317,3163,3164,3319,3163,3319,3318,3164,3165,3320,3164,3320,3319,3165,3166,3321,3165,3321,3320,3166,3167,3322,3166,3322,3321,3167,3168,3323,3167,3323,3322,3168,3169,3324,3168,3324,3323,3169,3170,3325,3169,3325,3324,3170,3171,3326,3170,3326,3325,3171,3172,3327,3171,3327,3326,3172,3173,3328,3172,3328,3327,3173,3174,3329,3173,3329,3328,3174,3175,3330,3174,3330,3329,3175,3176,3331,3175,3331,3330,3176,3177,3332,3176,3332,3331,3177,3178,3333,3177,3333,3332,3178,3179,3334,3178,3334,3333,3179,3180,3335,3179,3335,3334,3180,3181,3336,3180,3336,3335,3181,3182,3337,3181,3337,3336,3182,3183,3338,3182,3338,3337,3183,3184,3339,3183,3339,3338,3184,3185,3340,3184,3340,3339,3185,3186,3341,3185,3341,3340,3186,3187,3342,3186,3342,3341,3187,3188,3343,3187,3343,3342,3188,3189,3344,3188,3344,3343,3189,3190,3345,3189,3345,3344,3190,3191,3346,3190,3346,3345,3191,3192,3347,3191,3347,3346,3192,3193,3348,3192,3348,3347,3193,3194,3349,3193,3349,3348,3194,3195,3350,3194,3350,3349,3195,3196,3351,3195,3351,3350,3196,3197,3352,3196,3352,3351,3197,3198,3353,3197,3353,3352,3198,3199,3354,3198,3354,3353,3199,3200,3355,3199,3355,3354,3200,3201,3356,3200,3356,3355,3201,3202,3357,3201,3357,3356,3202,3203,3358,3202,3358,3357,3203,3204,3359,3203,3359,3358,3204,3205,3360,3204,3360,3359,3205,3206,3361,3205,3361,3360,3206,3207,3362,3206,3362,3361,3207,3208,3363,3207,3363,3362,3208,3209,3364,3208,3364,3363,3209,3210,3365,3209,3365,3364,3210,3211,3366,3210,3366,3365,3211,3212,3367,3211,3367,3366,3212,3213,3368,3212,3368,3367,3213,3214,3369,3213,3369,3368,3214,3215,3370,3214,3370,3369,3215,3216,3371,3215,3371,3370,3216,3217,3372,3216,3372,3371,3217,3218,3373,3217,3373,3372,3218,3219,3374,3218,3374,3373,3219,3220,3375,3219,3375,3374,3220,3221,3376,3220,3376,3375,3221,3222,3377,3221,3377,3376,3222,3223,3378,3222,3378,3377,3223,3224,3379,3223,3379,3378,3224,3225,3380,3224,3380,3379,3225,3226,3381,3225,3381,3380,3226,3227,3382,3226,3382,3381,3227,3228,3383,3227,3383,3382,3228,3229,3384,3228,3384,3383,3229,3230,3385,3229,3385,3384,3230,3231,3386,3230,3386,3385,3231,3232,3387,3231,3387,3386,3232,3233,3388,3232,3388,3387,3233,3234,3389,3233,3389,3388,3234,3235,3390,3234,3390,3389,3235,3236,3391,3235,3391,3390,3236,3237,3392,3236,3392,3391,3237,3238,3393,3237,3393,3392,3238,3239,3394,3238,3394,3393,3239,3240,3395,3239,3395,3394,3240,3241,3396,3240,3396,3395,3241,3242,3397,3241,3397,3396,3242,3243,3398,3242,3398,3397,3243,3244,3399,3243,3399,3398,3244,3245,3400,3244,3400,3399,3245,3246,3401,3245,3401,3400,3246,3247,3402,3246,3402,3401,3247,3248,3403,3247,3403,3402,3248,3249,3404,3248,3404,3403,3249,3250,3405,3249,3405,3404,3250,3251,3406,3250,3406,3405,3251,3252,3407,3251,3407,3406,3252,3253,3408,3252,3408,3407,3253,3254,3409,3253,3409,3408,3255,3256,3411,3255,3411,3410,3256,3257,3412,3256,3412,3411,3257,3258,3413,3257,3413,3412,3258,3259,3414,3258,3414,3413,3259,3260,3415,3259,3415,3414,3260,3261,3416,3260,3416,3415,3261,3262,3417,3261,3417,3416,3262,3263,3418,3262,3418,3417,3263,3264,3419,3263,3419,3418,3264,3265,3420,3264,3420,3419,3265,3266,3421,3265,3421,3420,3266,3267,3422,3266,3422,3421,3267,3268,3423,3267,3423,3422,3268,3269,3424,3268,3424,3423,3269,3270,3425,3269,3425,3424,3270,3271,3426,3270,3426,3425,3271,3272,3427,3271,3427,3426,3272,3273,3428,3272,3428,3427,3273,3274,3429,3273,3429,3428,3274,3275,3430,3274,3430,3429,3275,3276,3431,3275,3431,3430,3276,3277,3432,3276,3432,3431,3277,3278,3433,3277,3433,3432,3278,3279,3434,3278,3434,3433,3279,3280,3435,3279,3435,3434,3280,3281,3436,3280,3436,3435,3281,3282,3437,3281,3437,3436,3282,3283,3438,3282,3438,3437,3283,3284,3439,3283,3439,3438,3284,3285,3440,3284,3440,3439,3285,3286,3441,3285,3441,3440,3286,3287,3442,3286,3442,3441,3287,3288,3443,3287,3443,3442,3288,3289,3444,3288,3444,3443,3289,3290,3445,3289,3445,3444,3290,3291,3446,3290,3446,3445,3291,3292,3447,3291,3447,3446,3292,3293,3448,3292,3448,3447,3293,3294,3449,3293,3449,3448,3294,3295,3450,3294,3450,3449,3295,3296,3451,3295,3451,3450,3296,3297,3452,3296,3452,3451,3297,3298,3453,3297,3453,3452,3298,3299,3454,3298,3454,3453,3299,3300,3455,3299,3455,3454,3300,3301,3456,3300,3456,3455,3301,3302,3457,3301,3457,3456,3302,3303,3458,3302,3458,3457,3303,3304,3459,3303,3459,3458,3304,3305,3460,3304,3460,3459,3305,3306,3461,3305,3461,3460,3306,3307,3462,3306,3462,3461,3307,3308,3463,3307,3463,3462,3308,3309,3464,3308,3464,3463,3309,3310,3465,3309,3465,3464,3310,3311,3466,3310,3466,3465,3311,3312,3467,3311,3467,3466,3312,3313,3468,3312,3468,3467,3313,3314,3469,3313,3469,3468,3314,3315,3470,3314,3470,3469,3315,3316,3471,3315,3471,3470,3316,3317,3472,3316,3472,3471,3317,3318,3473,3317,3473,3472,3318,3319,3474,3318,3474,3473,3319,3320,3475,3319,3475,3474,3320,3321,3476,3320,3476,3475,3321,3322,3477,3321,3477,3476,3322,3323,3478,3322,3478,3477,3323,3324,3479,3323,3479,3478,3324,3325,3480,3324,3480,3479,3325,3326,3481,3325,3481,3480,3326,3327,3482,3326,3482,3481,3327,3328,3483,3327,3483,3482,3328,3329,3484,3328,3484,3483,3329,3330,3485,3329,3485,3484,3330,3331,3486,3330,3486,3485,3331,3332,3487,3331,3487,3486,3332,3333,3488,3332,3488,3487,3333,3334,3489,3333,3489,3488,3334,3335,3490,3334,3490,3489,3335,3336,3491,3335,3491,3490,3336,3337,3492,3336,3492,3491,3337,3338,3493,3337,3493,3492,3338,3339,3494,3338,3494,3493,3339,3340,3495,3339,3495,3494,3340,3341,3496,3340,3496,3495,3341,3342,3497,3341,3497,3496,3342,3343,3498,3342,3498,3497,3343,3344,3499,3343,3499,3498,3344,3345,3500,3344,3500,3499,3345,3346,3501,3345,3501,3500,3346,3347,3502,3346,3502,3501,3347,3348,3503,3347,3503,3502,3348,3349,3504,3348,3504,3503,3349,3350,3505,3349,3505,3504,3350,3351,3506,3350,3506,3505,3351,3352,3507,3351,3507,3506,3352,3353,3508,3352,3508,3507,3353,3354,3509,3353,3509,3508,3354,3355,3510,3354,3510,3509,3355,3356,3511,3355,3511,3510,3356,3357,3512,3356,3512,3511,3357,3358,3513,3357,3513,3512,3358,3359,3514,3358,3514,3513,3359,3360,3515,3359,3515,3514,3360,3361,3516,3360,3516,3515,3361,3362,3517,3361,3517,3516,3362,3363,3518,3362,3518,3517,3363,3364,3519,3363,3519,3518,3364,3365,3520,3364,3520,3519,3365,3366,3521,3365,3521,3520,3366,3367,3522,3366,3522,3521,3367,3368,3523,3367,3523,3522,3368,3369,3524,3368,3524,3523,3369,3370,3525,3369,3525,3524,3370,3371,3526,3370,3526,3525,3371,3372,3527,3371,3527,3526,3372,3373,3528,3372,3528,3527,3373,3374,3529,3373,3529,3528,3374,3375,3530,3374,3530,3529,3375,3376,3531,3375,3531,3530,3376,3377,3532,3376,3532,3531,3377,3378,3533,3377,3533,3532,3378,3379,3534,3378,3534,3533,3379,3380,3535,3379,3535,3534,3380,3381,3536,3380,3536,3535,3381,3382,3537,3381,3537,3536,3382,3383,3538,3382,3538,3537,3383,3384,3539,3383,3539,3538,3384,3385,3540,3384,3540,3539,3385,3386,3541,3385,3541,3540,3386,3387,3542,3386,3542,3541,3387,3388,3543,3387,3543,3542,3388,3389,3544,3388,3544,3543,3389,3390,3545,3389,3545,3544,3390,3391,3546,3390,3546,3545,3391,3392,3547,3391,3547,3546,3392,3393,3548,3392,3548,3547,3393,3394,3549,3393,3549,3548,3394,3395,3550,3394,3550,3549,3395,3396,3551,3395,3551,3550,3396,3397,3552,3396,3552,3551,3397,3398,3553,3397,3553,3552,3398,3399,3554,3398,3554,3553,3399,3400,3555,3399,3555,3554,3400,3401,3556,3400,3556,3555,3401,3402,3557,3401,3557,3556,3402,3403,3558,3402,3558,3557,3403,3404,3559,3403,3559,3558,3404,3405,3560,3404,3560,3559,3405,3406,3561,3405,3561,3560,3406,3407,3562,3406,3562,3561,3407,3408,3563,3407,3563,3562,3408,3409,3564,3408,3564,3563,3410,3411,3566,3410,3566,3565,3411,3412,3567,3411,3567,3566,3412,3413,3568,3412,3568,3567,3413,3414,3569,3413,3569,3568,3414,3415,3570,3414,3570,3569,3415,3416,3571,3415,3571,3570,3416,3417,3572,3416,3572,3571,3417,3418,3573,3417,3573,3572,3418,3419,3574,3418,3574,3573,3419,3420,3575,3419,3575,3574,3420,3421,3576,3420,3576,3575,3421,3422,3577,3421,3577,3576,3422,3423,3578,3422,3578,3577,3423,3424,3579,3423,3579,3578,3424,3425,3580,3424,3580,3579,3425,3426,3581,3425,3581,3580,3426,3427,3582,3426,3582,3581,3427,3428,3583,3427,3583,3582,3428,3429,3584,3428,3584,3583,3429,3430,3585,3429,3585,3584,3430,3431,3586,3430,3586,3585,3431,3432,3587,3431,3587,3586,3432,3433,3588,3432,3588,3587,3433,3434,3589,3433,3589,3588,3434,3435,3590,3434,3590,3589,3435,3436,3591,3435,3591,3590,3436,3437,3592,3436,3592,3591,3437,3438,3593,3437,3593,3592,3438,3439,3594,3438,3594,3593,3439,3440,3595,3439,3595,3594,3440,3441,3596,3440,3596,3595,3441,3442,3597,3441,3597,3596,3442,3443,3598,3442,3598,3597,3443,3444,3599,3443,3599,3598,3444,3445,3600,3444,3600,3599,3445,3446,3601,3445,3601,3600,3446,3447,3602,3446,3602,3601,3447,3448,3603,3447,3603,3602,3448,3449,3604,3448,3604,3603,3449,3450,3605,3449,3605,3604,3450,3451,3606,3450,3606,3605,3451,3452,3607,3451,3607,3606,3452,3453,3608,3452,3608,3607,3453,3454,3609,3453,3609,3608,3454,3455,3610,3454,3610,3609,3455,3456,3611,3455,3611,3610,3456,3457,3612,3456,3612,3611,3457,3458,3613,3457,3613,3612,3458,3459,3614,3458,3614,3613,3459,3460,3615,3459,3615,3614,3460,3461,3616,3460,3616,3615,3461,3462,3617,3461,3617,3616,3462,3463,3618,3462,3618,3617,3463,3464,3619,3463,3619,3618,3464,3465,3620,3464,3620,3619,3465,3466,3621,3465,3621,3620,3466,3467,3622,3466,3622,3621,3467,3468,3623,3467,3623,3622,3468,3469,3624,3468,3624,3623,3469,3470,3625,3469,3625,3624,3470,3471,3626,3470,3626,3625,3471,3472,3627,3471,3627,3626,3472,3473,3628,3472,3628,3627,3473,3474,3629,3473,3629,3628,3474,3475,3630,3474,3630,3629,3475,3476,3631,3475,3631,3630,3476,3477,3632,3476,3632,3631,3477,3478,3633,3477,3633,3632,3478,3479,3634,3478,3634,3633,3479,3480,3635,3479,3635,3634,3480,3481,3636,3480,3636,3635,3481,3482,3637,3481,3637,3636,3482,3483,3638,3482,3638,3637,3483,3484,3639,3483,3639,3638,3484,3485,3640,3484,3640,3639,3485,3486,3641,3485,3641,3640,3486,3487,3642,3486,3642,3641,3487,3488,3643,3487,3643,3642,3488,3489,3644,3488,3644,3643,3489,3490,3645,3489,3645,3644,3490,3491,3646,3490,3646,3645,3491,3492,3647,3491,3647,3646,3492,3493,3648,3492,3648,3647,3493,3494,3649,3493,3649,3648,3494,3495,3650,3494,3650,3649,3495,3496,3651,3495,3651,3650,3496,3497,3652,3496,3652,3651,3497,3498,3653,3497,3653,3652,3498,3499,3654,3498,3654,3653,3499,3500,3655,3499,3655,3654,3500,3501,3656,3500,3656,3655,3501,3502,3657,3501,3657,3656,3502,3503,3658,3502,3658,3657,3503,3504,3659,3503,3659,3658,3504,3505,3660,3504,3660,3659,3505,3506,3661,3505,3661,3660,3506,3507,3662,3506,3662,3661,3507,3508,3663,3507,3663,3662,3508,3509,3664,3508,3664,3663,3509,3510,3665,3509,3665,3664,3510,3511,3666,3510,3666,3665,3511,3512,3667,3511,3667,3666,3512,3513,3668,3512,3668,3667,3513,3514,3669,3513,3669,3668,3514,3515,3670,3514,3670,3669,3515,3516,3671,3515,3671,3670,3516,3517,3672,3516,3672,3671,3517,3518,3673,3517,3673,3672,3518,3519,3674,3518,3674,3673,3519,3520,3675,3519,3675,3674,3520,3521,3676,3520,3676,3675,3521,3522,3677,3521,3677,3676,3522,3523,3678,3522,3678,3677,3523,3524,3679,3523,3679,3678,3524,3525,3680,3524,3680,3679,3525,3526,3681,3525,3681,3680,3526,3527,3682,3526,3682,3681,3527,3528,3683,3527,3683,3682,3528,3529,3684,3528,3684,3683,3529,3530,3685,3529,3685,3684,3530,3531,3686,3530,3686,3685,3531,3532,3687,3531,3687,3686,3532,3533,3688,3532,3688,3687,3533,3534,3689,3533,3689,3688,3534,3535,3690,3534,3690,3689,3535,3536,3691,3535,3691,3690,3536,3537,3692,3536,3692,3691,3537,3538,3693,3537,3693,3692,3538,3539,3694,3538,3694,3693,3539,3540,3695,3539,3695,3694,3540,3541,3696,3540,3696,3695,3541,3542,3697,3541,3697,3696,3542,3543,3698,3542,3698,3697,3543,3544,3699,3543,3699,3698,3544,3545,3700,3544,3700,3699,3545,3546,3701,3545,3701,3700,3546,3547,3702,3546,3702,3701,3547,3548,3703,3547,3703,3702,3548,3549,3704,3548,3704,3703,3549,3550,3705,3549,3705,3704,3550,3551,3706,3550,3706,3705,3551,3552,3707,3551,3707,3706,3552,3553,3708,3552,3708,3707,3553,3554,3709,3553,3709,3708,3554,3555,3710,3554,3710,3709,3555,3556,3711,3555,3711,3710,3556,3557,3712,3556,3712,3711,3557,3558,3713,3557,3713,3712,3558,3559,3714,3558,3714,3713,3559,3560,3715,3559,3715,3714,3560,3561,3716,3560,3716,3715,3561,3562,3717,3561,3717,3716,3562,3563,3718,3562,3718,3717,3563,3564,3719,3563,3719,3718,3565,3566,3721,3565,3721,3720,3566,3567,3722,3566,3722,3721,3567,3568,3723,3567,3723,3722,3568,3569,3724,3568,3724,3723,3569,3570,3725,3569,3725,3724,3570,3571,3726,3570,3726,3725,3571,3572,3727,3571,3727,3726,3572,3573,3728,3572,3728,3727,3573,3574,3729,3573,3729,3728,3574,3575,3730,3574,3730,3729,3575,3576,3731,3575,3731,3730,3576,3577,3732,3576,3732,3731,3577,3578,3733,3577,3733,3732,3578,3579,3734,3578,3734,3733,3579,3580,3735,3579,3735,3734,3580,3581,3736,3580,3736,3735,3581,3582,3737,3581,3737,3736,3582,3583,3738,3582,3738,3737,3583,3584,3739,3583,3739,3738,3584,3585,3740,3584,3740,3739,3585,3586,3741,3585,3741,3740,3586,3587,3742,3586,3742,3741,3587,3588,3743,3587,3743,3742,3588,3589,3744,3588,3744,3743,3589,3590,3745,3589,3745,3744,3590,3591,3746,3590,3746,3745,3591,3592,3747,3591,3747,3746,3592,3593,3748,3592,3748,3747,3593,3594,3749,3593,3749,3748,3594,3595,3750,3594,3750,3749,3595,3596,3751,3595,3751,3750,3596,3597,3752,3596,3752,3751,3597,3598,3753,3597,3753,3752,3598,3599,3754,3598,3754,3753,3599,3600,3755,3599,3755,3754,3600,3601,3756,3600,3756,3755,3601,3602,3757,3601,3757,3756,3602,3603,3758,3602,3758,3757,3603,3604,3759,3603,3759,3758,3604,3605,3760,3604,3760,3759,3605,3606,3761,3605,3761,3760,3606,3607,3762,3606,3762,3761,3607,3608,3763,3607,3763,3762,3608,3609,3764,3608,3764,3763,3609,3610,3765,3609,3765,3764,3610,3611,3766,3610,3766,3765,3611,3612,3767,3611,3767,3766,3612,3613,3768,3612,3768,3767,3613,3614,3769,3613,3769,3768,3614,3615,3770,3614,3770,3769,3615,3616,3771,3615,3771,3770,3616,3617,3772,3616,3772,3771,3617,3618,3773,3617,3773,3772,3618,3619,3774,3618,3774,3773,3619,3620,3775,3619,3775,3774,3620,3621,3776,3620,3776,3775,3621,3622,3777,3621,3777,3776,3622,3623,3778,3622,3778,3777,3623,3624,3779,3623,3779,3778,3624,3625,3780,3624,3780,3779,3625,3626,3781,3625,3781,3780,3626,3627,3782,3626,3782,3781,3627,3628,3783,3627,3783,3782,3628,3629,3784,3628,3784,3783,3629,3630,3785,3629,3785,3784,3630,3631,3786,3630,3786,3785,3631,3632,3787,3631,3787,3786,3632,3633,3788,3632,3788,3787,3633,3634,3789,3633,3789,3788,3634,3635,3790,3634,3790,3789,3635,3636,3791,3635,3791,3790,3636,3637,3792,3636,3792,3791,3637,3638,3793,3637,3793,3792,3638,3639,3794,3638,3794,3793,3639,3640,3795,3639,3795,3794,3640,3641,3796,3640,3796,3795,3641,3642,3797,3641,3797,3796,3642,3643,3798,3642,3798,3797,3643,3644,3799,3643,3799,3798,3644,3645,3800,3644,3800,3799,3645,3646,3801,3645,3801,3800,3646,3647,3802,3646,3802,3801,3647,3648,3803,3647,3803,3802,3648,3649,3804,3648,3804,3803,3649,3650,3805,3649,3805,3804,3650,3651,3806,3650,3806,3805,3651,3652,3807,3651,3807,3806,3652,3653,3808,3652,3808,3807,3653,3654,3809,3653,3809,3808,3654,3655,3810,3654,3810,3809,3655,3656,3811,3655,3811,3810,3656,3657,3812,3656,3812,3811,3657,3658,3813,3657,3813,3812,3658,3659,3814,3658,3814,3813,3659,3660,3815,3659,3815,3814,3660,3661,3816,3660,3816,3815,3661,3662,3817,3661,3817,3816,3662,3663,3818,3662,3818,3817,3663,3664,3819,3663,3819,3818,3664,3665,3820,3664,3820,3819,3665,3666,3821,3665,3821,3820,3666,3667,3822,3666,3822,3821,3667,3668,3823,3667,3823,3822,3668,3669,3824,3668,3824,3823,3669,3670,3825,3669,3825,3824,3670,3671,3826,3670,3826,3825,3671,3672,3827,3671,3827,3826,3672,3673,3828,3672,3828,3827,3673,3674,3829,3673,3829,3828,3674,3675,3830,3674,3830,3829,3675,3676,3831,3675,3831,3830,3676,3677,3832,3676,3832,3831,3677,3678,3833,3677,3833,3832,3678,3679,3834,3678,3834,3833,3679,3680,3835,3679,3835,3834,3680,3681,3836,3680,3836,3835,3681,3682,3837,3681,3837,3836,3682,3683,3838,3682,3838,3837,3683,3684,3839,3683,3839,3838,3684,3685,3840,3684,3840,3839,3685,3686,3841,3685,3841,3840,3686,3687,3842,3686,3842,3841,3687,3688,3843,3687,3843,3842,3688,3689,3844,3688,3844,3843,3689,3690,3845,3689,3845,3844,3690,3691,3846,3690,3846,3845,3691,3692,3847,3691,3847,3846,3692,3693,3848,3692,3848,3847,3693,3694,3849,3693,3849,3848,3694,3695,3850,3694,3850,3849,3695,3696,3851,3695,3851,3850,3696,3697,3852,3696,3852,3851,3697,3698,3853,3697,3853,3852,3698,3699,3854,3698,3854,3853,3699,3700,3855,3699,3855,3854,3700,3701,3856,3700,3856,3855,3701,3702,3857,3701,3857,3856,3702,3703,3858,3702,3858,3857,3703,3704,3859,3703,3859,3858,3704,3705,3860,3704,3860,3859,3705,3706,3861,3705,3861,3860,3706,3707,3862,3706,3862,3861,3707,3708,3863,3707,3863,3862,3708,3709,3864,3708,3864,3863,3709,3710,3865,3709,3865,3864,3710,3711,3866,3710,3866,3865,3711,3712,3867,3711,3867,3866,3712,3713,3868,3712,3868,3867,3713,3714,3869,3713,3869,3868,3714,3715,3870,3714,3870,3869,3715,3716,3871,3715,3871,3870,3716,3717,3872,3716,3872,3871,3717,3718,3873,3717,3873,3872,3718,3719,3874,3718,3874,3873,3720,3721,3876,3720,3876,3875,3721,3722,3877,3721,3877,3876,3722,3723,3878,3722,3878,3877,3723,3724,3879,3723,3879,3878,3724,3725,3880,3724,3880,3879,3725,3726,3881,3725,3881,3880,3726,3727,3882,3726,3882,3881,3727,3728,3883,3727,3883,3882,3728,3729,3884,3728,3884,3883,3729,3730,3885,3729,3885,3884,3730,3731,3886,3730,3886,3885,3731,3732,3887,3731,3887,3886,3732,3733,3888,3732,3888,3887,3733,3734,3889,3733,3889,3888,3734,3735,3890,3734,3890,3889,3735,3736,3891,3735,3891,3890,3736,3737,3892,3736,3892,3891,3737,3738,3893,3737,3893,3892,3738,3739,3894,3738,3894,3893,3739,3740,3895,3739,3895,3894,3740,3741,3896,3740,3896,3895,3741,3742,3897,3741,3897,3896,3742,3743,3898,3742,3898,3897,3743,3744,3899,3743,3899,3898,3744,3745,3900,3744,3900,3899,3745,3746,3901,3745,3901,3900,3746,3747,3902,3746,3902,3901,3747,3748,3903,3747,3903,3902,3748,3749,3904,3748,3904,3903,3749,3750,3905,3749,3905,3904,3750,3751,3906,3750,3906,3905,3751,3752,3907,3751,3907,3906,3752,3753,3908,3752,3908,3907,3753,3754,3909,3753,3909,3908,3754,3755,3910,3754,3910,3909,3755,3756,3911,3755,3911,3910,3756,3757,3912,3756,3912,3911,3757,3758,3913,3757,3913,3912,3758,3759,3914,3758,3914,3913,3759,3760,3915,3759,3915,3914,3760,3761,3916,3760,3916,3915,3761,3762,3917,3761,3917,3916,3762,3763,3918,3762,3918,3917,3763,3764,3919,3763,3919,3918,3764,3765,3920,3764,3920,3919,3765,3766,3921,3765,3921,3920,3766,3767,3922,3766,3922,3921,3767,3768,3923,3767,3923,3922,3768,3769,3924,3768,3924,3923,3769,3770,3925,3769,3925,3924,3770,3771,3926,3770,3926,3925,3771,3772,3927,3771,3927,3926,3772,3773,3928,3772,3928,3927,3773,3774,3929,3773,3929,3928,3774,3775,3930,3774,3930,3929,3775,3776,3931,3775,3931,3930,3776,3777,3932,3776,3932,3931,3777,3778,3933,3777,3933,3932,3778,3779,3934,3778,3934,3933,3779,3780,3935,3779,3935,3934,3780,3781,3936,3780,3936,3935,3781,3782,3937,3781,3937,3936,3782,3783,3938,3782,3938,3937,3783,3784,3939,3783,3939,3938,3784,3785,3940,3784,3940,3939,3785,3786,3941,3785,3941,3940,3786,3787,3942,3786,3942,3941,3787,3788,3943,3787,3943,3942,3788,3789,3944,3788,3944,3943,3789,3790,3945,3789,3945,3944,3790,3791,3946,3790,3946,3945,3791,3792,3947,3791,3947,3946,3792,3793,3948,3792,3948,3947,3793,3794,3949,3793,3949,3948,3794,3795,3950,3794,3950,3949,3795,3796,3951,3795,3951,3950,3796,3797,3952,3796,3952,3951,3797,3798,3953,3797,3953,3952,3798,3799,3954,3798,3954,3953,3799,3800,3955,3799,3955,3954,3800,3801,3956,3800,3956,3955,3801,3802,3957,3801,3957,3956,3802,3803,3958,3802,3958,3957,3803,3804,3959,3803,3959,3958,3804,3805,3960,3804,3960,3959,3805,3806,3961,3805,3961,3960,3806,3807,3962,3806,3962,3961,3807,3808,3963,3807,3963,3962,3808,3809,3964,3808,3964,3963,3809,3810,3965,3809,3965,3964,3810,3811,3966,3810,3966,3965,3811,3812,3967,3811,3967,3966,3812,3813,3968,3812,3968,3967,3813,3814,3969,3813,3969,3968,3814,3815,3970,3814,3970,3969,3815,3816,3971,3815,3971,3970,3816,3817,3972,3816,3972,3971,3817,3818,3973,3817,3973,3972,3818,3819,3974,3818,3974,3973,3819,3820,3975,3819,3975,3974,3820,3821,3976,3820,3976,3975,3821,3822,3977,3821,3977,3976,3822,3823,3978,3822,3978,3977,3823,3824,3979,3823,3979,3978,3824,3825,3980,3824,3980,3979,3825,3826,3981,3825,3981,3980,3826,3827,3982,3826,3982,3981,3827,3828,3983,3827,3983,3982,3828,3829,3984,3828,3984,3983,3829,3830,3985,3829,3985,3984,3830,3831,3986,3830,3986,3985,3831,3832,3987,3831,3987,3986,3832,3833,3988,3832,3988,3987,3833,3834,3989,3833,3989,3988,3834,3835,3990,3834,3990,3989,3835,3836,3991,3835,3991,3990,3836,3837,3992,3836,3992,3991,3837,3838,3993,3837,3993,3992,3838,3839,3994,3838,3994,3993,3839,3840,3995,3839,3995,3994,3840,3841,3996,3840,3996,3995,3841,3842,3997,3841,3997,3996,3842,3843,3998,3842,3998,3997,3843,3844,3999,3843,3999,3998,3844,3845,4000,3844,4000,3999,3845,3846,4001,3845,4001,4000,3846,3847,4002,3846,4002,4001,3847,3848,4003,3847,4003,4002,3848,3849,4004,3848,4004,4003,3849,3850,4005,3849,4005,4004,3850,3851,4006,3850,4006,4005,3851,3852,4007,3851,4007,4006,3852,3853,4008,3852,4008,4007,3853,3854,4009,3853,4009,4008,3854,3855,4010,3854,4010,4009,3855,3856,4011,3855,4011,4010,3856,3857,4012,3856,4012,4011,3857,3858,4013,3857,4013,4012,3858,3859,4014,3858,4014,4013,3859,3860,4015,3859,4015,4014,3860,3861,4016,3860,4016,4015,3861,3862,4017,3861,4017,4016,3862,3863,4018,3862,4018,4017,3863,3864,4019,3863,4019,4018,3864,3865,4020,3864,4020,4019,3865,3866,4021,3865,4021,4020,3866,3867,4022,3866,4022,4021,3867,3868,4023,3867,4023,4022,3868,3869,4024,3868,4024,4023,3869,3870,4025,3869,4025,4024,3870,3871,4026,3870,4026,4025,3871,3872,4027,3871,4027,4026,3872,3873,4028,3872,4028,4027,3873,3874,4029,3873,4029,4028,3875,3876,4031,3875,4031,4030,3876,3877,4032,3876,4032,4031,3877,3878,4033,3877,4033,4032,3878,3879,4034,3878,4034,4033,3879,3880,4035,3879,4035,4034,3880,3881,4036,3880,4036,4035,3881,3882,4037,3881,4037,4036,3882,3883,4038,3882,4038,4037,3883,3884,4039,3883,4039,4038,3884,3885,4040,3884,4040,4039,3885,3886,4041,3885,4041,4040,3886,3887,4042,3886,4042,4041,3887,3888,4043,3887,4043,4042,3888,3889,4044,3888,4044,4043,3889,3890,4045,3889,4045,4044,3890,3891,4046,3890,4046,4045,3891,3892,4047,3891,4047,4046,3892,3893,4048,3892,4048,4047,3893,3894,4049,3893,4049,4048,3894,3895,4050,3894,4050,4049,3895,3896,4051,3895,4051,4050,3896,3897,4052,3896,4052,4051,3897,3898,4053,3897,4053,4052,3898,3899,4054,3898,4054,4053,3899,3900,4055,3899,4055,4054,3900,3901,4056,3900,4056,4055,3901,3902,4057,3901,4057,4056,3902,3903,4058,3902,4058,4057,3903,3904,4059,3903,4059,4058,3904,3905,4060,3904,4060,4059,3905,3906,4061,3905,4061,4060,3906,3907,4062,3906,4062,4061,3907,3908,4063,3907,4063,4062,3908,3909,4064,3908,4064,4063,3909,3910,4065,3909,4065,4064,3910,3911,4066,3910,4066,4065,3911,3912,4067,3911,4067,4066,3912,3913,4068,3912,4068,4067,3913,3914,4069,3913,4069,4068,3914,3915,4070,3914,4070,4069,3915,3916,4071,3915,4071,4070,3916,3917,4072,3916,4072,4071,3917,3918,4073,3917,4073,4072,3918,3919,4074,3918,4074,4073,3919,3920,4075,3919,4075,4074,3920,3921,4076,3920,4076,4075,3921,3922,4077,3921,4077,4076,3922,3923,4078,3922,4078,4077,3923,3924,4079,3923,4079,4078,3924,3925,4080,3924,4080,4079,3925,3926,4081,3925,4081,4080,3926,3927,4082,3926,4082,4081,3927,3928,4083,3927,4083,4082,3928,3929,4084,3928,4084,4083,3929,3930,4085,3929,4085,4084,3930,3931,4086,3930,4086,4085,3931,3932,4087,3931,4087,4086,3932,3933,4088,3932,4088,4087,3933,3934,4089,3933,4089,4088,3934,3935,4090,3934,4090,4089,3935,3936,4091,3935,4091,4090,3936,3937,4092,3936,4092,4091,3937,3938,4093,3937,4093,4092,3938,3939,4094,3938,4094,4093,3939,3940,4095,3939,4095,4094,3940,3941,4096,3940,4096,4095,3941,3942,4097,3941,4097,4096,3942,3943,4098,3942,4098,4097,3943,3944,4099,3943,4099,4098,3944,3945,4100,3944,4100,4099,3945,3946,4101,3945,4101,4100,3946,3947,4102,3946,4102,4101,3947,3948,4103,3947,4103,4102,3948,3949,4104,3948,4104,4103,3949,3950,4105,3949,4105,4104,3950,3951,4106,3950,4106,4105,3951,3952,4107,3951,4107,4106,3952,3953,4108,3952,4108,4107,3953,3954,4109,3953,4109,4108,3954,3955,4110,3954,4110,4109,3955,3956,4111,3955,4111,4110,3956,3957,4112,3956,4112,4111,3957,3958,4113,3957,4113,4112,3958,3959,4114,3958,4114,4113,3959,3960,4115,3959,4115,4114,3960,3961,4116,3960,4116,4115,3961,3962,4117,3961,4117,4116,3962,3963,4118,3962,4118,4117,3963,3964,4119,3963,4119,4118,3964,3965,4120,3964,4120,4119,3965,3966,4121,3965,4121,4120,3966,3967,4122,3966,4122,4121,3967,3968,4123,3967,4123,4122,3968,3969,4124,3968,4124,4123,3969,3970,4125,3969,4125,4124,3970,3971,4126,3970,4126,4125,3971,3972,4127,3971,4127,4126,3972,3973,4128,3972,4128,4127,3973,3974,4129,3973,4129,4128,3974,3975,4130,3974,4130,4129,3975,3976,4131,3975,4131,4130,3976,3977,4132,3976,4132,4131,3977,3978,4133,3977,4133,4132,3978,3979,4134,3978,4134,4133,3979,3980,4135,3979,4135,4134,3980,3981,4136,3980,4136,4135,3981,3982,4137,3981,4137,4136,3982,3983,4138,3982,4138,4137,3983,3984,4139,3983,4139,4138,3984,3985,4140,3984,4140,4139,3985,3986,4141,3985,4141,4140,3986,3987,4142,3986,4142,4141,3987,3988,4143,3987,4143,4142,3988,3989,4144,3988,4144,4143,3989,3990,4145,3989,4145,4144,3990,3991,4146,3990,4146,4145,3991,3992,4147,3991,4147,4146,3992,3993,4148,3992,4148,4147,3993,3994,4149,3993,4149,4148,3994,3995,4150,3994,4150,4149,3995,3996,4151,3995,4151,4150,3996,3997,4152,3996,4152,4151,3997,3998,4153,3997,4153,4152,3998,3999,4154,3998,4154,4153,3999,4000,4155,3999,4155,4154,4000,4001,4156,4000,4156,4155,4001,4002,4157,4001,4157,4156,4002,4003,4158,4002,4158,4157,4003,4004,4159,4003,4159,4158,4004,4005,4160,4004,4160,4159,4005,4006,4161,4005,4161,4160,4006,4007,4162,4006,4162,4161,4007,4008,4163,4007,4163,4162,4008,4009,4164,4008,4164,4163,4009,4010,4165,4009,4165,4164,4010,4011,4166,4010,4166,4165,4011,4012,4167,4011,4167,4166,4012,4013,4168,4012,4168,4167,4013,4014,4169,4013,4169,4168,4014,4015,4170,4014,4170,4169,4015,4016,4171,4015,4171,4170,4016,4017,4172,4016,4172,4171,4017,4018,4173,4017,4173,4172,4018,4019,4174,4018,4174,4173,4019,4020,4175,4019,4175,4174,4020,4021,4176,4020,4176,4175,4021,4022,4177,4021,4177,4176,4022,4023,4178,4022,4178,4177,4023,4024,4179,4023,4179,4178,4024,4025,4180,4024,4180,4179,4025,4026,4181,4025,4181,4180,4026,4027,4182,4026,4182,4181,4027,4028,4183,4027,4183,4182,4028,4029,4184,4028,4184,4183,4030,4031,4186,4030,4186,4185,4031,4032,4187,4031,4187,4186,4032,4033,4188,4032,4188,4187,4033,4034,4189,4033,4189,4188,4034,4035,4190,4034,4190,4189,4035,4036,4191,4035,4191,4190,4036,4037,4192,4036,4192,4191,4037,4038,4193,4037,4193,4192,4038,4039,4194,4038,4194,4193,4039,4040,4195,4039,4195,4194,4040,4041,4196,4040,4196,4195,4041,4042,4197,4041,4197,4196,4042,4043,4198,4042,4198,4197,4043,4044,4199,4043,4199,4198,4044,4045,4200,4044,4200,4199,4045,4046,4201,4045,4201,4200,4046,4047,4202,4046,4202,4201,4047,4048,4203,4047,4203,4202,4048,4049,4204,4048,4204,4203,4049,4050,4205,4049,4205,4204,4050,4051,4206,4050,4206,4205,4051,4052,4207,4051,4207,4206,4052,4053,4208,4052,4208,4207,4053,4054,4209,4053,4209,4208,4054,4055,4210,4054,4210,4209,4055,4056,4211,4055,4211,4210,4056,4057,4212,4056,4212,4211,4057,4058,4213,4057,4213,4212,4058,4059,4214,4058,4214,4213,4059,4060,4215,4059,4215,4214,4060,4061,4216,4060,4216,4215,4061,4062,4217,4061,4217,4216,4062,4063,4218,4062,4218,4217,4063,4064,4219,4063,4219,4218,4064,4065,4220,4064,4220,4219,4065,4066,4221,4065,4221,4220,4066,4067,4222,4066,4222,4221,4067,4068,4223,4067,4223,4222,4068,4069,4224,4068,4224,4223,4069,4070,4225,4069,4225,4224,4070,4071,4226,4070,4226,4225,4071,4072,4227,4071,4227,4226,4072,4073,4228,4072,4228,4227,4073,4074,4229,4073,4229,4228,4074,4075,4230,4074,4230,4229,4075,4076,4231,4075,4231,4230,4076,4077,4232,4076,4232,4231,4077,4078,4233,4077,4233,4232,4078,4079,4234,4078,4234,4233,4079,4080,4235,4079,4235,4234,4080,4081,4236,4080,4236,4235,4081,4082,4237,4081,4237,4236,4082,4083,4238,4082,4238,4237,4083,4084,4239,4083,4239,4238,4084,4085,4240,4084,4240,4239,4085,4086,4241,4085,4241,4240,4086,4087,4242,4086,4242,4241,4087,4088,4243,4087,4243,4242,4088,4089,4244,4088,4244,4243,4089,4090,4245,4089,4245,4244,4090,4091,4246,4090,4246,4245,4091,4092,4247,4091,4247,4246,4092,4093,4248,4092,4248,4247,4093,4094,4249,4093,4249,4248,4094,4095,4250,4094,4250,4249,4095,4096,4251,4095,4251,4250,4096,4097,4252,4096,4252,4251,4097,4098,4253,4097,4253,4252,4098,4099,4254,4098,4254,4253,4099,4100,4255,4099,4255,4254,4100,4101,4256,4100,4256,4255,4101,4102,4257,4101,4257,4256,4102,4103,4258,4102,4258,4257,4103,4104,4259,4103,4259,4258,4104,4105,4260,4104,4260,4259,4105,4106,4261,4105,4261,4260,4106,4107,4262,4106,4262,4261,4107,4108,4263,4107,4263,4262,4108,4109,4264,4108,4264,4263,4109,4110,4265,4109,4265,4264,4110,4111,4266,4110,4266,4265,4111,4112,4267,4111,4267,4266,4112,4113,4268,4112,4268,4267,4113,4114,4269,4113,4269,4268,4114,4115,4270,4114,4270,4269,4115,4116,4271,4115,4271,4270,4116,4117,4272,4116,4272,4271,4117,4118,4273,4117,4273,4272,4118,4119,4274,4118,4274,4273,4119,4120,4275,4119,4275,4274,4120,4121,4276,4120,4276,4275,4121,4122,4277,4121,4277,4276,4122,4123,4278,4122,4278,4277,4123,4124,4279,4123,4279,4278,4124,4125,4280,4124,4280,4279,4125,4126,4281,4125,4281,4280,4126,4127,4282,4126,4282,4281,4127,4128,4283,4127,4283,4282,4128,4129,4284,4128,4284,4283,4129,4130,4285,4129,4285,4284,4130,4131,4286,4130,4286,4285,4131,4132,4287,4131,4287,4286,4132,4133,4288,4132,4288,4287,4133,4134,4289,4133,4289,4288,4134,4135,4290,4134,4290,4289,4135,4136,4291,4135,4291,4290,4136,4137,4292,4136,4292,4291,4137,4138,4293,4137,4293,4292,4138,4139,4294,4138,4294,4293,4139,4140,4295,4139,4295,4294,4140,4141,4296,4140,4296,4295,4141,4142,4297,4141,4297,4296,4142,4143,4298,4142,4298,4297,4143,4144,4299,4143,4299,4298,4144,4145,4300,4144,4300,4299,4145,4146,4301,4145,4301,4300,4146,4147,4302,4146,4302,4301,4147,4148,4303,4147,4303,4302,4148,4149,4304,4148,4304,4303,4149,4150,4305,4149,4305,4304,4150,4151,4306,4150,4306,4305,4151,4152,4307,4151,4307,4306,4152,4153,4308,4152,4308,4307,4153,4154,4309,4153,4309,4308,4154,4155,4310,4154,4310,4309,4155,4156,4311,4155,4311,4310,4156,4157,4312,4156,4312,4311,4157,4158,4313,4157,4313,4312,4158,4159,4314,4158,4314,4313,4159,4160,4315,4159,4315,4314,4160,4161,4316,4160,4316,4315,4161,4162,4317,4161,4317,4316,4162,4163,4318,4162,4318,4317,4163,4164,4319,4163,4319,4318,4164,4165,4320,4164,4320,4319,4165,4166,4321,4165,4321,4320,4166,4167,4322,4166,4322,4321,4167,4168,4323,4167,4323,4322,4168,4169,4324,4168,4324,4323,4169,4170,4325,4169,4325,4324,4170,4171,4326,4170,4326,4325,4171,4172,4327,4171,4327,4326,4172,4173,4328,4172,4328,4327,4173,4174,4329,4173,4329,4328,4174,4175,4330,4174,4330,4329,4175,4176,4331,4175,4331,4330,4176,4177,4332,4176,4332,4331,4177,4178,4333,4177,4333,4332,4178,4179,4334,4178,4334,4333,4179,4180,4335,4179,4335,4334,4180,4181,4336,4180,4336,4335,4181,4182,4337,4181,4337,4336,4182,4183,4338,4182,4338,4337,4183,4184,4339,4183,4339,4338,4185,4186,4341,4185,4341,4340,4186,4187,4342,4186,4342,4341,4187,4188,4343,4187,4343,4342,4188,4189,4344,4188,4344,4343,4189,4190,4345,4189,4345,4344,4190,4191,4346,4190,4346,4345,4191,4192,4347,4191,4347,4346,4192,4193,4348,4192,4348,4347,4193,4194,4349,4193,4349,4348,4194,4195,4350,4194,4350,4349,4195,4196,4351,4195,4351,4350,4196,4197,4352,4196,4352,4351,4197,4198,4353,4197,4353,4352,4198,4199,4354,4198,4354,4353,4199,4200,4355,4199,4355,4354,4200,4201,4356,4200,4356,4355,4201,4202,4357,4201,4357,4356,4202,4203,4358,4202,4358,4357,4203,4204,4359,4203,4359,4358,4204,4205,4360,4204,4360,4359,4205,4206,4361,4205,4361,4360,4206,4207,4362,4206,4362,4361,4207,4208,4363,4207,4363,4362,4208,4209,4364,4208,4364,4363,4209,4210,4365,4209,4365,4364,4210,4211,4366,4210,4366,4365,4211,4212,4367,4211,4367,4366,4212,4213,4368,4212,4368,4367,4213,4214,4369,4213,4369,4368,4214,4215,4370,4214,4370,4369,4215,4216,4371,4215,4371,4370,4216,4217,4372,4216,4372,4371,4217,4218,4373,4217,4373,4372,4218,4219,4374,4218,4374,4373,4219,4220,4375,4219,4375,4374,4220,4221,4376,4220,4376,4375,4221,4222,4377,4221,4377,4376,4222,4223,4378,4222,4378,4377,4223,4224,4379,4223,4379,4378,4224,4225,4380,4224,4380,4379,4225,4226,4381,4225,4381,4380,4226,4227,4382,4226,4382,4381,4227,4228,4383,4227,4383,4382,4228,4229,4384,4228,4384,4383,4229,4230,4385,4229,4385,4384,4230,4231,4386,4230,4386,4385,4231,4232,4387,4231,4387,4386,4232,4233,4388,4232,4388,4387,4233,4234,4389,4233,4389,4388,4234,4235,4390,4234,4390,4389,4235,4236,4391,4235,4391,4390,4236,4237,4392,4236,4392,4391,4237,4238,4393,4237,4393,4392,4238,4239,4394,4238,4394,4393,4239,4240,4395,4239,4395,4394,4240,4241,4396,4240,4396,4395,4241,4242,4397,4241,4397,4396,4242,4243,4398,4242,4398,4397,4243,4244,4399,4243,4399,4398,4244,4245,4400,4244,4400,4399,4245,4246,4401,4245,4401,4400,4246,4247,4402,4246,4402,4401,4247,4248,4403,4247,4403,4402,4248,4249,4404,4248,4404,4403,4249,4250,4405,4249,4405,4404,4250,4251,4406,4250,4406,4405,4251,4252,4407,4251,4407,4406,4252,4253,4408,4252,4408,4407,4253,4254,4409,4253,4409,4408,4254,4255,4410,4254,4410,4409,4255,4256,4411,4255,4411,4410,4256,4257,4412,4256,4412,4411,4257,4258,4413,4257,4413,4412,4258,4259,4414,4258,4414,4413,4259,4260,4415,4259,4415,4414,4260,4261,4416,4260,4416,4415,4261,4262,4417,4261,4417,4416,4262,4263,4418,4262,4418,4417,4263,4264,4419,4263,4419,4418,4264,4265,4420,4264,4420,4419,4265,4266,4421,4265,4421,4420,4266,4267,4422,4266,4422,4421,4267,4268,4423,4267,4423,4422,4268,4269,4424,4268,4424,4423,4269,4270,4425,4269,4425,4424,4270,4271,4426,4270,4426,4425,4271,4272,4427,4271,4427,4426,4272,4273,4428,4272,4428,4427,4273,4274,4429,4273,4429,4428,4274,4275,4430,4274,4430,4429,4275,4276,4431,4275,4431,4430,4276,4277,4432,4276,4432,4431,4277,4278,4433,4277,4433,4432,4278,4279,4434,4278,4434,4433,4279,4280,4435,4279,4435,4434,4280,4281,4436,4280,4436,4435,4281,4282,4437,4281,4437,4436,4282,4283,4438,4282,4438,4437,4283,4284,4439,4283,4439,4438,4284,4285,4440,4284,4440,4439,4285,4286,4441,4285,4441,4440,4286,4287,4442,4286,4442,4441,4287,4288,4443,4287,4443,4442,4288,4289,4444,4288,4444,4443,4289,4290,4445,4289,4445,4444,4290,4291,4446,4290,4446,4445,4291,4292,4447,4291,4447,4446,4292,4293,4448,4292,4448,4447,4293,4294,4449,4293,4449,4448,4294,4295,4450,4294,4450,4449,4295,4296,4451,4295,4451,4450,4296,4297,4452,4296,4452,4451,4297,4298,4453,4297,4453,4452,4298,4299,4454,4298,4454,4453,4299,4300,4455,4299,4455,4454,4300,4301,4456,4300,4456,4455,4301,4302,4457,4301,4457,4456,4302,4303,4458,4302,4458,4457,4303,4304,4459,4303,4459,4458,4304,4305,4460,4304,4460,4459,4305,4306,4461,4305,4461,4460,4306,4307,4462,4306,4462,4461,4307,4308,4463,4307,4463,4462,4308,4309,4464,4308,4464,4463,4309,4310,4465,4309,4465,4464,4310,4311,4466,4310,4466,4465,4311,4312,4467,4311,4467,4466,4312,4313,4468,4312,4468,4467,4313,4314,4469,4313,4469,4468,4314,4315,4470,4314,4470,4469,4315,4316,4471,4315,4471,4470,4316,4317,4472,4316,4472,4471,4317,4318,4473,4317,4473,4472,4318,4319,4474,4318,4474,4473,4319,4320,4475,4319,4475,4474,4320,4321,4476,4320,4476,4475,4321,4322,4477,4321,4477,4476,4322,4323,4478,4322,4478,4477,4323,4324,4479,4323,4479,4478,4324,4325,4480,4324,4480,4479,4325,4326,4481,4325,4481,4480,4326,4327,4482,4326,4482,4481,4327,4328,4483,4327,4483,4482,4328,4329,4484,4328,4484,4483,4329,4330,4485,4329,4485,4484,4330,4331,4486,4330,4486,4485,4331,4332,4487,4331,4487,4486,4332,4333,4488,4332,4488,4487,4333,4334,4489,4333,4489,4488,4334,4335,4490,4334,4490,4489,4335,4336,4491,4335,4491,4490,4336,4337,4492,4336,4492,4491,4337,4338,4493,4337,4493,4492,4338,4339,4494,4338,4494,4493,4340,4341,4496,4340,4496,4495,4341,4342,4497,4341,4497,4496,4342,4343,4498,4342,4498,4497,4343,4344,4499,4343,4499,4498,4344,4345,4500,4344,4500,4499,4345,4346,4501,4345,4501,4500,4346,4347,4502,4346,4502,4501,4347,4348,4503,4347,4503,4502,4348,4349,4504,4348,4504,4503,4349,4350,4505,4349,4505,4504,4350,4351,4506,4350,4506,4505,4351,4352,4507,4351,4507,4506,4352,4353,4508,4352,4508,4507,4353,4354,4509,4353,4509,4508,4354,4355,4510,4354,4510,4509,4355,4356,4511,4355,4511,4510,4356,4357,4512,4356,4512,4511,4357,4358,4513,4357,4513,4512,4358,4359,4514,4358,4514,4513,4359,4360,4515,4359,4515,4514,4360,4361,4516,4360,4516,4515,4361,4362,4517,4361,4517,4516,4362,4363,4518,4362,4518,4517,4363,4364,4519,4363,4519,4518,4364,4365,4520,4364,4520,4519,4365,4366,4521,4365,4521,4520,4366,4367,4522,4366,4522,4521,4367,4368,4523,4367,4523,4522,4368,4369,4524,4368,4524,4523,4369,4370,4525,4369,4525,4524,4370,4371,4526,4370,4526,4525,4371,4372,4527,4371,4527,4526,4372,4373,4528,4372,4528,4527,4373,4374,4529,4373,4529,4528,4374,4375,4530,4374,4530,4529,4375,4376,4531,4375,4531,4530,4376,4377,4532,4376,4532,4531,4377,4378,4533,4377,4533,4532,4378,4379,4534,4378,4534,4533,4379,4380,4535,4379,4535,4534,4380,4381,4536,4380,4536,4535,4381,4382,4537,4381,4537,4536,4382,4383,4538,4382,4538,4537,4383,4384,4539,4383,4539,4538,4384,4385,4540,4384,4540,4539,4385,4386,4541,4385,4541,4540,4386,4387,4542,4386,4542,4541,4387,4388,4543,4387,4543,4542,4388,4389,4544,4388,4544,4543,4389,4390,4545,4389,4545,4544,4390,4391,4546,4390,4546,4545,4391,4392,4547,4391,4547,4546,4392,4393,4548,4392,4548,4547,4393,4394,4549,4393,4549,4548,4394,4395,4550,4394,4550,4549,4395,4396,4551,4395,4551,4550,4396,4397,4552,4396,4552,4551,4397,4398,4553,4397,4553,4552,4398,4399,4554,4398,4554,4553,4399,4400,4555,4399,4555,4554,4400,4401,4556,4400,4556,4555,4401,4402,4557,4401,4557,4556,4402,4403,4558,4402,4558,4557,4403,4404,4559,4403,4559,4558,4404,4405,4560,4404,4560,4559,4405,4406,4561,4405,4561,4560,4406,4407,4562,4406,4562,4561,4407,4408,4563,4407,4563,4562,4408,4409,4564,4408,4564,4563,4409,4410,4565,4409,4565,4564,4410,4411,4566,4410,4566,4565,4411,4412,4567,4411,4567,4566,4412,4413,4568,4412,4568,4567,4413,4414,4569,4413,4569,4568,4414,4415,4570,4414,4570,4569,4415,4416,4571,4415,4571,4570,4416,4417,4572,4416,4572,4571,4417,4418,4573,4417,4573,4572,4418,4419,4574,4418,4574,4573,4419,4420,4575,4419,4575,4574,4420,4421,4576,4420,4576,4575,4421,4422,4577,4421,4577,4576,4422,4423,4578,4422,4578,4577,4423,4424,4579,4423,4579,4578,4424,4425,4580,4424,4580,4579,4425,4426,4581,4425,4581,4580,4426,4427,4582,4426,4582,4581,4427,4428,4583,4427,4583,4582,4428,4429,4584,4428,4584,4583,4429,4430,4585,4429,4585,4584,4430,4431,4586,4430,4586,4585,4431,4432,4587,4431,4587,4586,4432,4433,4588,4432,4588,4587,4433,4434,4589,4433,4589,4588,4434,4435,4590,4434,4590,4589,4435,4436,4591,4435,4591,4590,4436,4437,4592,4436,4592,4591,4437,4438,4593,4437,4593,4592,4438,4439,4594,4438,4594,4593,4439,4440,4595,4439,4595,4594,4440,4441,4596,4440,4596,4595,4441,4442,4597,4441,4597,4596,4442,4443,4598,4442,4598,4597,4443,4444,4599,4443,4599,4598,4444,4445,4600,4444,4600,4599,4445,4446,4601,4445,4601,4600,4446,4447,4602,4446,4602,4601,4447,4448,4603,4447,4603,4602,4448,4449,4604,4448,4604,4603,4449,4450,4605,4449,4605,4604,4450,4451,4606,4450,4606,4605,4451,4452,4607,4451,4607,4606,4452,4453,4608,4452,4608,4607,4453,4454,4609,4453,4609,4608,4454,4455,4610,4454,4610,4609,4455,4456,4611,4455,4611,4610,4456,4457,4612,4456,4612,4611,4457,4458,4613,4457,4613,4612,4458,4459,4614,4458,4614,4613,4459,4460,4615,4459,4615,4614,4460,4461,4616,4460,4616,4615,4461,4462,4617,4461,4617,4616,4462,4463,4618,4462,4618,4617,4463,4464,4619,4463,4619,4618,4464,4465,4620,4464,4620,4619,4465,4466,4621,4465,4621,4620,4466,4467,4622,4466,4622,4621,4467,4468,4623,4467,4623,4622,4468,4469,4624,4468,4624,4623,4469,4470,4625,4469,4625,4624,4470,4471,4626,4470,4626,4625,4471,4472,4627,4471,4627,4626,4472,4473,4628,4472,4628,4627,4473,4474,4629,4473,4629,4628,4474,4475,4630,4474,4630,4629,4475,4476,4631,4475,4631,4630,4476,4477,4632,4476,4632,4631,4477,4478,4633,4477,4633,4632,4478,4479,4634,4478,4634,4633,4479,4480,4635,4479,4635,4634,4480,4481,4636,4480,4636,4635,4481,4482,4637,4481,4637,4636,4482,4483,4638,4482,4638,4637,4483,4484,4639,4483,4639,4638,4484,4485,4640,4484,4640,4639,4485,4486,4641,4485,4641,4640,4486,4487,4642,4486,4642,4641,4487,4488,4643,4487,4643,4642,4488,4489,4644,4488,4644,4643,4489,4490,4645,4489,4645,4644,4490,4491,4646,4490,4646,4645,4491,4492,4647,4491,4647,4646,4492,4493,4648,4492,4648,4647,4493,4494,4649,4493,4649,4648,4495,4496,4651,4495,4651,4650,4496,4497,4652,4496,4652,4651,4497,4498,4653,4497,4653,4652,4498,4499,4654,4498,4654,4653,4499,4500,4655,4499,4655,4654,4500,4501,4656,4500,4656,4655,4501,4502,4657,4501,4657,4656,4502,4503,4658,4502,4658,4657,4503,4504,4659,4503,4659,4658,4504,4505,4660,4504,4660,4659,4505,4506,4661,4505,4661,4660,4506,4507,4662,4506,4662,4661,4507,4508,4663,4507,4663,4662,4508,4509,4664,4508,4664,4663,4509,4510,4665,4509,4665,4664,4510,4511,4666,4510,4666,4665,4511,4512,4667,4511,4667,4666,4512,4513,4668,4512,4668,4667,4513,4514,4669,4513,4669,4668,4514,4515,4670,4514,4670,4669,4515,4516,4671,4515,4671,4670,4516,4517,4672,4516,4672,4671,4517,4518,4673,4517,4673,4672,4518,4519,4674,4518,4674,4673,4519,4520,4675,4519,4675,4674,4520,4521,4676,4520,4676,4675,4521,4522,4677,4521,4677,4676,4522,4523,4678,4522,4678,4677,4523,4524,4679,4523,4679,4678,4524,4525,4680,4524,4680,4679,4525,4526,4681,4525,4681,4680,4526,4527,4682,4526,4682,4681,4527,4528,4683,4527,4683,4682,4528,4529,4684,4528,4684,4683,4529,4530,4685,4529,4685,4684,4530,4531,4686,4530,4686,4685,4531,4532,4687,4531,4687,4686,4532,4533,4688,4532,4688,4687,4533,4534,4689,4533,4689,4688,4534,4535,4690,4534,4690,4689,4535,4536,4691,4535,4691,4690,4536,4537,4692,4536,4692,4691,4537,4538,4693,4537,4693,4692,4538,4539,4694,4538,4694,4693,4539,4540,4695,4539,4695,4694,4540,4541,4696,4540,4696,4695,4541,4542,4697,4541,4697,4696,4542,4543,4698,4542,4698,4697,4543,4544,4699,4543,4699,4698,4544,4545,4700,4544,4700,4699,4545,4546,4701,4545,4701,4700,4546,4547,4702,4546,4702,4701,4547,4548,4703,4547,4703,4702,4548,4549,4704,4548,4704,4703,4549,4550,4705,4549,4705,4704,4550,4551,4706,4550,4706,4705,4551,4552,4707,4551,4707,4706,4552,4553,4708,4552,4708,4707,4553,4554,4709,4553,4709,4708,4554,4555,4710,4554,4710,4709,4555,4556,4711,4555,4711,4710,4556,4557,4712,4556,4712,4711,4557,4558,4713,4557,4713,4712,4558,4559,4714,4558,4714,4713,4559,4560,4715,4559,4715,4714,4560,4561,4716,4560,4716,4715,4561,4562,4717,4561,4717,4716,4562,4563,4718,4562,4718,4717,4563,4564,4719,4563,4719,4718,4564,4565,4720,4564,4720,4719,4565,4566,4721,4565,4721,4720,4566,4567,4722,4566,4722,4721,4567,4568,4723,4567,4723,4722,4568,4569,4724,4568,4724,4723,4569,4570,4725,4569,4725,4724,4570,4571,4726,4570,4726,4725,4571,4572,4727,4571,4727,4726,4572,4573,4728,4572,4728,4727,4573,4574,4729,4573,4729,4728,4574,4575,4730,4574,4730,4729,4575,4576,4731,4575,4731,4730,4576,4577,4732,4576,4732,4731,4577,4578,4733,4577,4733,4732,4578,4579,4734,4578,4734,4733,4579,4580,4735,4579,4735,4734,4580,4581,4736,4580,4736,4735,4581,4582,4737,4581,4737,4736,4582,4583,4738,4582,4738,4737,4583,4584,4739,4583,4739,4738,4584,4585,4740,4584,4740,4739,4585,4586,4741,4585,4741,4740,4586,4587,4742,4586,4742,4741,4587,4588,4743,4587,4743,4742,4588,4589,4744,4588,4744,4743,4589,4590,4745,4589,4745,4744,4590,4591,4746,4590,4746,4745,4591,4592,4747,4591,4747,4746,4592,4593,4748,4592,4748,4747,4593,4594,4749,4593,4749,4748,4594,4595,4750,4594,4750,4749,4595,4596,4751,4595,4751,4750,4596,4597,4752,4596,4752,4751,4597,4598,4753,4597,4753,4752,4598,4599,4754,4598,4754,4753,4599,4600,4755,4599,4755,4754,4600,4601,4756,4600,4756,4755,4601,4602,4757,4601,4757,4756,4602,4603,4758,4602,4758,4757,4603,4604,4759,4603,4759,4758,4604,4605,4760,4604,4760,4759,4605,4606,4761,4605,4761,4760,4606,4607,4762,4606,4762,4761,4607,4608,4763,4607,4763,4762,4608,4609,4764,4608,4764,4763,4609,4610,4765,4609,4765,4764,4610,4611,4766,4610,4766,4765,4611,4612,4767,4611,4767,4766,4612,4613,4768,4612,4768,4767,4613,4614,4769,4613,4769,4768,4614,4615,4770,4614,4770,4769,4615,4616,4771,4615,4771,4770,4616,4617,4772,4616,4772,4771,4617,4618,4773,4617,4773,4772,4618,4619,4774,4618,4774,4773,4619,4620,4775,4619,4775,4774,4620,4621,4776,4620,4776,4775,4621,4622,4777,4621,4777,4776,4622,4623,4778,4622,4778,4777,4623,4624,4779,4623,4779,4778,4624,4625,4780,4624,4780,4779,4625,4626,4781,4625,4781,4780,4626,4627,4782,4626,4782,4781,4627,4628,4783,4627,4783,4782,4628,4629,4784,4628,4784,4783,4629,4630,4785,4629,4785,4784,4630,4631,4786,4630,4786,4785,4631,4632,4787,4631,4787,4786,4632,4633,4788,4632,4788,4787,4633,4634,4789,4633,4789,4788,4634,4635,4790,4634,4790,4789,4635,4636,4791,4635,4791,4790,4636,4637,4792,4636,4792,4791,4637,4638,4793,4637,4793,4792,4638,4639,4794,4638,4794,4793,4639,4640,4795,4639,4795,4794,4640,4641,4796,4640,4796,4795,4641,4642,4797,4641,4797,4796,4642,4643,4798,4642,4798,4797,4643,4644,4799,4643,4799,4798,4644,4645,4800,4644,4800,4799,4645,4646,4801,4645,4801,4800,4646,4647,4802,4646,4802,4801,4647,4648,4803,4647,4803,4802,4648,4649,4804,4648,4804,4803,4650,4651,4806,4650,4806,4805,4651,4652,4807,4651,4807,4806,4652,4653,4808,4652,4808,4807,4653,4654,4809,4653,4809,4808,4654,4655,4810,4654,4810,4809,4655,4656,4811,4655,4811,4810,4656,4657,4812,4656,4812,4811,4657,4658,4813,4657,4813,4812,4658,4659,4814,4658,4814,4813,4659,4660,4815,4659,4815,4814,4660,4661,4816,4660,4816,4815,4661,4662,4817,4661,4817,4816,4662,4663,4818,4662,4818,4817,4663,4664,4819,4663,4819,4818,4664,4665,4820,4664,4820,4819,4665,4666,4821,4665,4821,4820,4666,4667,4822,4666,4822,4821,4667,4668,4823,4667,4823,4822,4668,4669,4824,4668,4824,4823,4669,4670,4825,4669,4825,4824,4670,4671,4826,4670,4826,4825,4671,4672,4827,4671,4827,4826,4672,4673,4828,4672,4828,4827,4673,4674,4829,4673,4829,4828,4674,4675,4830,4674,4830,4829,4675,4676,4831,4675,4831,4830,4676,4677,4832,4676,4832,4831,4677,4678,4833,4677,4833,4832,4678,4679,4834,4678,4834,4833,4679,4680,4835,4679,4835,4834,4680,4681,4836,4680,4836,4835,4681,4682,4837,4681,4837,4836,4682,4683,4838,4682,4838,4837,4683,4684,4839,4683,4839,4838,4684,4685,4840,4684,4840,4839,4685,4686,4841,4685,4841,4840,4686,4687,4842,4686,4842,4841,4687,4688,4843,4687,4843,4842,4688,4689,4844,4688,4844,4843,4689,4690,4845,4689,4845,4844,4690,4691,4846,4690,4846,4845,4691,4692,4847,4691,4847,4846,4692,4693,4848,4692,4848,4847,4693,4694,4849,4693,4849,4848,4694,4695,4850,4694,4850,4849,4695,4696,4851,4695,4851,4850,4696,4697,4852,4696,4852,4851,4697,4698,4853,4697,4853,4852,4698,4699,4854,4698,4854,4853,4699,4700,4855,4699,4855,4854,4700,4701,4856,4700,4856,4855,4701,4702,4857,4701,4857,4856,4702,4703,4858,4702,4858,4857,4703,4704,4859,4703,4859,4858,4704,4705,4860,4704,4860,4859,4705,4706,4861,4705,4861,4860,4706,4707,4862,4706,4862,4861,4707,4708,4863,4707,4863,4862,4708,4709,4864,4708,4864,4863,4709,4710,4865,4709,4865,4864,4710,4711,4866,4710,4866,4865,4711,4712,4867,4711,4867,4866,4712,4713,4868,4712,4868,4867,4713,4714,4869,4713,4869,4868,4714,4715,4870,4714,4870,4869,4715,4716,4871,4715,4871,4870,4716,4717,4872,4716,4872,4871,4717,4718,4873,4717,4873,4872,4718,4719,4874,4718,4874,4873,4719,4720,4875,4719,4875,4874,4720,4721,4876,4720,4876,4875,4721,4722,4877,4721,4877,4876,4722,4723,4878,4722,4878,4877,4723,4724,4879,4723,4879,4878,4724,4725,4880,4724,4880,4879,4725,4726,4881,4725,4881,4880,4726,4727,4882,4726,4882,4881,4727,4728,4883,4727,4883,4882,4728,4729,4884,4728,4884,4883,4729,4730,4885,4729,4885,4884,4730,4731,4886,4730,4886,4885,4731,4732,4887,4731,4887,4886,4732,4733,4888,4732,4888,4887,4733,4734,4889,4733,4889,4888,4734,4735,4890,4734,4890,4889,4735,4736,4891,4735,4891,4890,4736,4737,4892,4736,4892,4891,4737,4738,4893,4737,4893,4892,4738,4739,4894,4738,4894,4893,4739,4740,4895,4739,4895,4894,4740,4741,4896,4740,4896,4895,4741,4742,4897,4741,4897,4896,4742,4743,4898,4742,4898,4897,4743,4744,4899,4743,4899,4898,4744,4745,4900,4744,4900,4899,4745,4746,4901,4745,4901,4900,4746,4747,4902,4746,4902,4901,4747,4748,4903,4747,4903,4902,4748,4749,4904,4748,4904,4903,4749,4750,4905,4749,4905,4904,4750,4751,4906,4750,4906,4905,4751,4752,4907,4751,4907,4906,4752,4753,4908,4752,4908,4907,4753,4754,4909,4753,4909,4908,4754,4755,4910,4754,4910,4909,4755,4756,4911,4755,4911,4910,4756,4757,4912,4756,4912,4911,4757,4758,4913,4757,4913,4912,4758,4759,4914,4758,4914,4913,4759,4760,4915,4759,4915,4914,4760,4761,4916,4760,4916,4915,4761,4762,4917,4761,4917,4916,4762,4763,4918,4762,4918,4917,4763,4764,4919,4763,4919,4918,4764,4765,4920,4764,4920,4919,4765,4766,4921,4765,4921,4920,4766,4767,4922,4766,4922,4921,4767,4768,4923,4767,4923,4922,4768,4769,4924,4768,4924,4923,4769,4770,4925,4769,4925,4924,4770,4771,4926,4770,4926,4925,4771,4772,4927,4771,4927,4926,4772,4773,4928,4772,4928,4927,4773,4774,4929,4773,4929,4928,4774,4775,4930,4774,4930,4929,4775,4776,4931,4775,4931,4930,4776,4777,4932,4776,4932,4931,4777,4778,4933,4777,4933,4932,4778,4779,4934,4778,4934,4933,4779,4780,4935,4779,4935,4934,4780,4781,4936,4780,4936,4935,4781,4782,4937,4781,4937,4936,4782,4783,4938,4782,4938,4937,4783,4784,4939,4783,4939,4938,4784,4785,4940,4784,4940,4939,4785,4786,4941,4785,4941,4940,4786,4787,4942,4786,4942,4941,4787,4788,4943,4787,4943,4942,4788,4789,4944,4788,4944,4943,4789,4790,4945,4789,4945,4944,4790,4791,4946,4790,4946,4945,4791,4792,4947,4791,4947,4946,4792,4793,4948,4792,4948,4947,4793,4794,4949,4793,4949,4948,4794,4795,4950,4794,4950,4949,4795,4796,4951,4795,4951,4950,4796,4797,4952,4796,4952,4951,4797,4798,4953,4797,4953,4952,4798,4799,4954,4798,4954,4953,4799,4800,4955,4799,4955,4954,4800,4801,4956,4800,4956,4955,4801,4802,4957,4801,4957,4956,4802,4803,4958,4802,4958,4957,4803,4804,4959,4803,4959,4958,4805,4806,4961,4805,4961,4960,4806,4807,4962,4806,4962,4961,4807,4808,4963,4807,4963,4962,4808,4809,4964,4808,4964,4963,4809,4810,4965,4809,4965,4964,4810,4811,4966,4810,4966,4965,4811,4812,4967,4811,4967,4966,4812,4813,4968,4812,4968,4967,4813,4814,4969,4813,4969,4968,4814,4815,4970,4814,4970,4969,4815,4816,4971,4815,4971,4970,4816,4817,4972,4816,4972,4971,4817,4818,4973,4817,4973,4972,4818,4819,4974,4818,4974,4973,4819,4820,4975,4819,4975,4974,4820,4821,4976,4820,4976,4975,4821,4822,4977,4821,4977,4976,4822,4823,4978,4822,4978,4977,4823,4824,4979,4823,4979,4978,4824,4825,4980,4824,4980,4979,4825,4826,4981,4825,4981,4980,4826,4827,4982,4826,4982,4981,4827,4828,4983,4827,4983,4982,4828,4829,4984,4828,4984,4983,4829,4830,4985,4829,4985,4984,4830,4831,4986,4830,4986,4985,4831,4832,4987,4831,4987,4986,4832,4833,4988,4832,4988,4987,4833,4834,4989,4833,4989,4988,4834,4835,4990,4834,4990,4989,4835,4836,4991,4835,4991,4990,4836,4837,4992,4836,4992,4991,4837,4838,4993,4837,4993,4992,4838,4839,4994,4838,4994,4993,4839,4840,4995,4839,4995,4994,4840,4841,4996,4840,4996,4995,4841,4842,4997,4841,4997,4996,4842,4843,4998,4842,4998,4997,4843,4844,4999,4843,4999,4998,4844,4845,5000,4844,5000,4999,4845,4846,5001,4845,5001,5000,4846,4847,5002,4846,5002,5001,4847,4848,5003,4847,5003,5002,4848,4849,5004,4848,5004,5003,4849,4850,5005,4849,5005,5004,4850,4851,5006,4850,5006,5005,4851,4852,5007,4851,5007,5006,4852,4853,5008,4852,5008,5007,4853,4854,5009,4853,5009,5008,4854,4855,5010,4854,5010,5009,4855,4856,5011,4855,5011,5010,4856,4857,5012,4856,5012,5011,4857,4858,5013,4857,5013,5012,4858,4859,5014,4858,5014,5013,4859,4860,5015,4859,5015,5014,4860,4861,5016,4860,5016,5015,4861,4862,5017,4861,5017,5016,4862,4863,5018,4862,5018,5017,4863,4864,5019,4863,5019,5018,4864,4865,5020,4864,5020,5019,4865,4866,5021,4865,5021,5020,4866,4867,5022,4866,5022,5021,4867,4868,5023,4867,5023,5022,4868,4869,5024,4868,5024,5023,4869,4870,5025,4869,5025,5024,4870,4871,5026,4870,5026,5025,4871,4872,5027,4871,5027,5026,4872,4873,5028,4872,5028,5027,4873,4874,5029,4873,5029,5028,4874,4875,5030,4874,5030,5029,4875,4876,5031,4875,5031,5030,4876,4877,5032,4876,5032,5031,4877,4878,5033,4877,5033,5032,4878,4879,5034,4878,5034,5033,4879,4880,5035,4879,5035,5034,4880,4881,5036,4880,5036,5035,4881,4882,5037,4881,5037,5036,4882,4883,5038,4882,5038,5037,4883,4884,5039,4883,5039,5038,4884,4885,5040,4884,5040,5039,4885,4886,5041,4885,5041,5040,4886,4887,5042,4886,5042,5041,4887,4888,5043,4887,5043,5042,4888,4889,5044,4888,5044,5043,4889,4890,5045,4889,5045,5044,4890,4891,5046,4890,5046,5045,4891,4892,5047,4891,5047,5046,4892,4893,5048,4892,5048,5047,4893,4894,5049,4893,5049,5048,4894,4895,5050,4894,5050,5049,4895,4896,5051,4895,5051,5050,4896,4897,5052,4896,5052,5051,4897,4898,5053,4897,5053,5052,4898,4899,5054,4898,5054,5053,4899,4900,5055,4899,5055,5054,4900,4901,5056,4900,5056,5055,4901,4902,5057,4901,5057,5056,4902,4903,5058,4902,5058,5057,4903,4904,5059,4903,5059,5058,4904,4905,5060,4904,5060,5059,4905,4906,5061,4905,5061,5060,4906,4907,5062,4906,5062,5061,4907,4908,5063,4907,5063,5062,4908,4909,5064,4908,5064,5063,4909,4910,5065,4909,5065,5064,4910,4911,5066,4910,5066,5065,4911,4912,5067,4911,5067,5066,4912,4913,5068,4912,5068,5067,4913,4914,5069,4913,5069,5068,4914,4915,5070,4914,5070,5069,4915,4916,5071,4915,5071,5070,4916,4917,5072,4916,5072,5071,4917,4918,5073,4917,5073,5072,4918,4919,5074,4918,5074,5073,4919,4920,5075,4919,5075,5074,4920,4921,5076,4920,5076,5075,4921,4922,5077,4921,5077,5076,4922,4923,5078,4922,5078,5077,4923,4924,5079,4923,5079,5078,4924,4925,5080,4924,5080,5079,4925,4926,5081,4925,5081,5080,4926,4927,5082,4926,5082,5081,4927,4928,5083,4927,5083,5082,4928,4929,5084,4928,5084,5083,4929,4930,5085,4929,5085,5084,4930,4931,5086,4930,5086,5085,4931,4932,5087,4931,5087,5086,4932,4933,5088,4932,5088,5087,4933,4934,5089,4933,5089,5088,4934,4935,5090,4934,5090,5089,4935,4936,5091,4935,5091,5090,4936,4937,5092,4936,5092,5091,4937,4938,5093,4937,5093,5092,4938,4939,5094,4938,5094,5093,4939,4940,5095,4939,5095,5094,4940,4941,5096,4940,5096,5095,4941,4942,5097,4941,5097,5096,4942,4943,5098,4942,5098,5097,4943,4944,5099,4943,5099,5098,4944,4945,5100,4944,5100,5099,4945,4946,5101,4945,5101,5100,4946,4947,5102,4946,5102,5101,4947,4948,5103,4947,5103,5102,4948,4949,5104,4948,5104,5103,4949,4950,5105,4949,5105,5104,4950,4951,5106,4950,5106,5105,4951,4952,5107,4951,5107,5106,4952,4953,5108,4952,5108,5107,4953,4954,5109,4953,5109,5108,4954,4955,5110,4954,5110,5109,4955,4956,5111,4955,5111,5110,4956,4957,5112,4956,5112,5111,4957,4958,5113,4957,5113,5112,4958,4959,5114,4958,5114,5113,4960,4961,5116,4960,5116,5115,4961,4962,5117,4961,5117,5116,4962,4963,5118,4962,5118,5117,4963,4964,5119,4963,5119,5118,4964,4965,5120,4964,5120,5119,4965,4966,5121,4965,5121,5120,4966,4967,5122,4966,5122,5121,4967,4968,5123,4967,5123,5122,4968,4969,5124,4968,5124,5123,4969,4970,5125,4969,5125,5124,4970,4971,5126,4970,5126,5125,4971,4972,5127,4971,5127,5126,4972,4973,5128,4972,5128,5127,4973,4974,5129,4973,5129,5128,4974,4975,5130,4974,5130,5129,4975,4976,5131,4975,5131,5130,4976,4977,5132,4976,5132,5131,4977,4978,5133,4977,5133,5132,4978,4979,5134,4978,5134,5133,4979,4980,5135,4979,5135,5134,4980,4981,5136,4980,5136,5135,4981,4982,5137,4981,5137,5136,4982,4983,5138,4982,5138,5137,4983,4984,5139,4983,5139,5138,4984,4985,5140,4984,5140,5139,4985,4986,5141,4985,5141,5140,4986,4987,5142,4986,5142,5141,4987,4988,5143,4987,5143,5142,4988,4989,5144,4988,5144,5143,4989,4990,5145,4989,5145,5144,4990,4991,5146,4990,5146,5145,4991,4992,5147,4991,5147,5146,4992,4993,5148,4992,5148,5147,4993,4994,5149,4993,5149,5148,4994,4995,5150,4994,5150,5149,4995,4996,5151,4995,5151,5150,4996,4997,5152,4996,5152,5151,4997,4998,5153,4997,5153,5152,4998,4999,5154,4998,5154,5153,4999,5000,5155,4999,5155,5154,5000,5001,5156,5000,5156,5155,5001,5002,5157,5001,5157,5156,5002,5003,5158,5002,5158,5157,5003,5004,5159,5003,5159,5158,5004,5005,5160,5004,5160,5159,5005,5006,5161,5005,5161,5160,5006,5007,5162,5006,5162,5161,5007,5008,5163,5007,5163,5162,5008,5009,5164,5008,5164,5163,5009,5010,5165,5009,5165,5164,5010,5011,5166,5010,5166,5165,5011,5012,5167,5011,5167,5166,5012,5013,5168,5012,5168,5167,5013,5014,5169,5013,5169,5168,5014,5015,5170,5014,5170,5169,5015,5016,5171,5015,5171,5170,5016,5017,5172,5016,5172,5171,5017,5018,5173,5017,5173,5172,5018,5019,5174,5018,5174,5173,5019,5020,5175,5019,5175,5174,5020,5021,5176,5020,5176,5175,5021,5022,5177,5021,5177,5176,5022,5023,5178,5022,5178,5177,5023,5024,5179,5023,5179,5178,5024,5025,5180,5024,5180,5179,5025,5026,5181,5025,5181,5180,5026,5027,5182,5026,5182,5181,5027,5028,5183,5027,5183,5182,5028,5029,5184,5028,5184,5183,5029,5030,5185,5029,5185,5184,5030,5031,5186,5030,5186,5185,5031,5032,5187,5031,5187,5186,5032,5033,5188,5032,5188,5187,5033,5034,5189,5033,5189,5188,5034,5035,5190,5034,5190,5189,5035,5036,5191,5035,5191,5190,5036,5037,5192,5036,5192,5191,5037,5038,5193,5037,5193,5192,5038,5039,5194,5038,5194,5193,5039,5040,5195,5039,5195,5194,5040,5041,5196,5040,5196,5195,5041,5042,5197,5041,5197,5196,5042,5043,5198,5042,5198,5197,5043,5044,5199,5043,5199,5198,5044,5045,5200,5044,5200,5199,5045,5046,5201,5045,5201,5200,5046,5047,5202,5046,5202,5201,5047,5048,5203,5047,5203,5202,5048,5049,5204,5048,5204,5203,5049,5050,5205,5049,5205,5204,5050,5051,5206,5050,5206,5205,5051,5052,5207,5051,5207,5206,5052,5053,5208,5052,5208,5207,5053,5054,5209,5053,5209,5208,5054,5055,5210,5054,5210,5209,5055,5056,5211,5055,5211,5210,5056,5057,5212,5056,5212,5211,5057,5058,5213,5057,5213,5212,5058,5059,5214,5058,5214,5213,5059,5060,5215,5059,5215,5214,5060,5061,5216,5060,5216,5215,5061,5062,5217,5061,5217,5216,5062,5063,5218,5062,5218,5217,5063,5064,5219,5063,5219,5218,5064,5065,5220,5064,5220,5219,5065,5066,5221,5065,5221,5220,5066,5067,5222,5066,5222,5221,5067,5068,5223,5067,5223,5222,5068,5069,5224,5068,5224,5223,5069,5070,5225,5069,5225,5224,5070,5071,5226,5070,5226,5225,5071,5072,5227,5071,5227,5226,5072,5073,5228,5072,5228,5227,5073,5074,5229,5073,5229,5228,5074,5075,5230,5074,5230,5229,5075,5076,5231,5075,5231,5230,5076,5077,5232,5076,5232,5231,5077,5078,5233,5077,5233,5232,5078,5079,5234,5078,5234,5233,5079,5080,5235,5079,5235,5234,5080,5081,5236,5080,5236,5235,5081,5082,5237,5081,5237,5236,5082,5083,5238,5082,5238,5237,5083,5084,5239,5083,5239,5238,5084,5085,5240,5084,5240,5239,5085,5086,5241,5085,5241,5240,5086,5087,5242,5086,5242,5241,5087,5088,5243,5087,5243,5242,5088,5089,5244,5088,5244,5243,5089,5090,5245,5089,5245,5244,5090,5091,5246,5090,5246,5245,5091,5092,5247,5091,5247,5246,5092,5093,5248,5092,5248,5247,5093,5094,5249,5093,5249,5248,5094,5095,5250,5094,5250,5249,5095,5096,5251,5095,5251,5250,5096,5097,5252,5096,5252,5251,5097,5098,5253,5097,5253,5252,5098,5099,5254,5098,5254,5253,5099,5100,5255,5099,5255,5254,5100,5101,5256,5100,5256,5255,5101,5102,5257,5101,5257,5256,5102,5103,5258,5102,5258,5257,5103,5104,5259,5103,5259,5258,5104,5105,5260,5104,5260,5259,5105,5106,5261,5105,5261,5260,5106,5107,5262,5106,5262,5261,5107,5108,5263,5107,5263,5262,5108,5109,5264,5108,5264,5263,5109,5110,5265,5109,5265,5264,5110,5111,5266,5110,5266,5265,5111,5112,5267,5111,5267,5266,5112,5113,5268,5112,5268,5267,5113,5114,5269,5113,5269,5268,5115,5116,5271,5115,5271,5270,5116,5117,5272,5116,5272,5271,5117,5118,5273,5117,5273,5272,5118,5119,5274,5118,5274,5273,5119,5120,5275,5119,5275,5274,5120,5121,5276,5120,5276,5275,5121,5122,5277,5121,5277,5276,5122,5123,5278,5122,5278,5277,5123,5124,5279,5123,5279,5278,5124,5125,5280,5124,5280,5279,5125,5126,5281,5125,5281,5280,5126,5127,5282,5126,5282,5281,5127,5128,5283,5127,5283,5282,5128,5129,5284,5128,5284,5283,5129,5130,5285,5129,5285,5284,5130,5131,5286,5130,5286,5285,5131,5132,5287,5131,5287,5286,5132,5133,5288,5132,5288,5287,5133,5134,5289,5133,5289,5288,5134,5135,5290,5134,5290,5289,5135,5136,5291,5135,5291,5290,5136,5137,5292,5136,5292,5291,5137,5138,5293,5137,5293,5292,5138,5139,5294,5138,5294,5293,5139,5140,5295,5139,5295,5294,5140,5141,5296,5140,5296,5295,5141,5142,5297,5141,5297,5296,5142,5143,5298,5142,5298,5297,5143,5144,5299,5143,5299,5298,5144,5145,5300,5144,5300,5299,5145,5146,5301,5145,5301,5300,5146,5147,5302,5146,5302,5301,5147,5148,5303,5147,5303,5302,5148,5149,5304,5148,5304,5303,5149,5150,5305,5149,5305,5304,5150,5151,5306,5150,5306,5305,5151,5152,5307,5151,5307,5306,5152,5153,5308,5152,5308,5307,5153,5154,5309,5153,5309,5308,5154,5155,5310,5154,5310,5309,5155,5156,5311,5155,5311,5310,5156,5157,5312,5156,5312,5311,5157,5158,5313,5157,5313,5312,5158,5159,5314,5158,5314,5313,5159,5160,5315,5159,5315,5314,5160,5161,5316,5160,5316,5315,5161,5162,5317,5161,5317,5316,5162,5163,5318,5162,5318,5317,5163,5164,5319,5163,5319,5318,5164,5165,5320,5164,5320,5319,5165,5166,5321,5165,5321,5320,5166,5167,5322,5166,5322,5321,5167,5168,5323,5167,5323,5322,5168,5169,5324,5168,5324,5323,5169,5170,5325,5169,5325,5324,5170,5171,5326,5170,5326,5325,5171,5172,5327,5171,5327,5326,5172,5173,5328,5172,5328,5327,5173,5174,5329,5173,5329,5328,5174,5175,5330,5174,5330,5329,5175,5176,5331,5175,5331,5330,5176,5177,5332,5176,5332,5331,5177,5178,5333,5177,5333,5332,5178,5179,5334,5178,5334,5333,5179,5180,5335,5179,5335,5334,5180,5181,5336,5180,5336,5335,5181,5182,5337,5181,5337,5336,5182,5183,5338,5182,5338,5337,5183,5184,5339,5183,5339,5338,5184,5185,5340,5184,5340,5339,5185,5186,5341,5185,5341,5340,5186,5187,5342,5186,5342,5341,5187,5188,5343,5187,5343,5342,5188,5189,5344,5188,5344,5343,5189,5190,5345,5189,5345,5344,5190,5191,5346,5190,5346,5345,5191,5192,5347,5191,5347,5346,5192,5193,5348,5192,5348,5347,5193,5194,5349,5193,5349,5348,5194,5195,5350,5194,5350,5349,5195,5196,5351,5195,5351,5350,5196,5197,5352,5196,5352,5351,5197,5198,5353,5197,5353,5352,5198,5199,5354,5198,5354,5353,5199,5200,5355,5199,5355,5354,5200,5201,5356,5200,5356,5355,5201,5202,5357,5201,5357,5356,5202,5203,5358,5202,5358,5357,5203,5204,5359,5203,5359,5358,5204,5205,5360,5204,5360,5359,5205,5206,5361,5205,5361,5360,5206,5207,5362,5206,5362,5361,5207,5208,5363,5207,5363,5362,5208,5209,5364,5208,5364,5363,5209,5210,5365,5209,5365,5364,5210,5211,5366,5210,5366,5365,5211,5212,5367,5211,5367,5366,5212,5213,5368,5212,5368,5367,5213,5214,5369,5213,5369,5368,5214,5215,5370,5214,5370,5369,5215,5216,5371,5215,5371,5370,5216,5217,5372,5216,5372,5371,5217,5218,5373,5217,5373,5372,5218,5219,5374,5218,5374,5373,5219,5220,5375,5219,5375,5374,5220,5221,5376,5220,5376,5375,5221,5222,5377,5221,5377,5376,5222,5223,5378,5222,5378,5377,5223,5224,5379,5223,5379,5378,5224,5225,5380,5224,5380,5379,5225,5226,5381,5225,5381,5380,5226,5227,5382,5226,5382,5381,5227,5228,5383,5227,5383,5382,5228,5229,5384,5228,5384,5383,5229,5230,5385,5229,5385,5384,5230,5231,5386,5230,5386,5385,5231,5232,5387,5231,5387,5386,5232,5233,5388,5232,5388,5387,5233,5234,5389,5233,5389,5388,5234,5235,5390,5234,5390,5389,5235,5236,5391,5235,5391,5390,5236,5237,5392,5236,5392,5391,5237,5238,5393,5237,5393,5392,5238,5239,5394,5238,5394,5393,5239,5240,5395,5239,5395,5394,5240,5241,5396,5240,5396,5395,5241,5242,5397,5241,5397,5396,5242,5243,5398,5242,5398,5397,5243,5244,5399,5243,5399,5398,5244,5245,5400,5244,5400,5399,5245,5246,5401,5245,5401,5400,5246,5247,5402,5246,5402,5401,5247,5248,5403,5247,5403,5402,5248,5249,5404,5248,5404,5403,5249,5250,5405,5249,5405,5404,5250,5251,5406,5250,5406,5405,5251,5252,5407,5251,5407,5406,5252,5253,5408,5252,5408,5407,5253,5254,5409,5253,5409,5408,5254,5255,5410,5254,5410,5409,5255,5256,5411,5255,5411,5410,5256,5257,5412,5256,5412,5411,5257,5258,5413,5257,5413,5412,5258,5259,5414,5258,5414,5413,5259,5260,5415,5259,5415,5414,5260,5261,5416,5260,5416,5415,5261,5262,5417,5261,5417,5416,5262,5263,5418,5262,5418,5417,5263,5264,5419,5263,5419,5418,5264,5265,5420,5264,5420,5419,5265,5266,5421,5265,5421,5420,5266,5267,5422,5266,5422,5421,5267,5268,5423,5267,5423,5422,5268,5269,5424,5268,5424,5423,5270,5271,5426,5270,5426,5425,5271,5272,5427,5271,5427,5426,5272,5273,5428,5272,5428,5427,5273,5274,5429,5273,5429,5428,5274,5275,5430,5274,5430,5429,5275,5276,5431,5275,5431,5430,5276,5277,5432,5276,5432,5431,5277,5278,5433,5277,5433,5432,5278,5279,5434,5278,5434,5433,5279,5280,5435,5279,5435,5434,5280,5281,5436,5280,5436,5435,5281,5282,5437,5281,5437,5436,5282,5283,5438,5282,5438,5437,5283,5284,5439,5283,5439,5438,5284,5285,5440,5284,5440,5439,5285,5286,5441,5285,5441,5440,5286,5287,5442,5286,5442,5441,5287,5288,5443,5287,5443,5442,5288,5289,5444,5288,5444,5443,5289,5290,5445,5289,5445,5444,5290,5291,5446,5290,5446,5445,5291,5292,5447,5291,5447,5446,5292,5293,5448,5292,5448,5447,5293,5294,5449,5293,5449,5448,5294,5295,5450,5294,5450,5449,5295,5296,5451,5295,5451,5450,5296,5297,5452,5296,5452,5451,5297,5298,5453,5297,5453,5452,5298,5299,5454,5298,5454,5453,5299,5300,5455,5299,5455,5454,5300,5301,5456,5300,5456,5455,5301,5302,5457,5301,5457,5456,5302,5303,5458,5302,5458,5457,5303,5304,5459,5303,5459,5458,5304,5305,5460,5304,5460,5459,5305,5306,5461,5305,5461,5460,5306,5307,5462,5306,5462,5461,5307,5308,5463,5307,5463,5462,5308,5309,5464,5308,5464,5463,5309,5310,5465,5309,5465,5464,5310,5311,5466,5310,5466,5465,5311,5312,5467,5311,5467,5466,5312,5313,5468,5312,5468,5467,5313,5314,5469,5313,5469,5468,5314,5315,5470,5314,5470,5469,5315,5316,5471,5315,5471,5470,5316,5317,5472,5316,5472,5471,5317,5318,5473,5317,5473,5472,5318,5319,5474,5318,5474,5473,5319,5320,5475,5319,5475,5474,5320,5321,5476,5320,5476,5475,5321,5322,5477,5321,5477,5476,5322,5323,5478,5322,5478,5477,5323,5324,5479,5323,5479,5478,5324,5325,5480,5324,5480,5479,5325,5326,5481,5325,5481,5480,5326,5327,5482,5326,5482,5481,5327,5328,5483,5327,5483,5482,5328,5329,5484,5328,5484,5483,5329,5330,5485,5329,5485,5484,5330,5331,5486,5330,5486,5485,5331,5332,5487,5331,5487,5486,5332,5333,5488,5332,5488,5487,5333,5334,5489,5333,5489,5488,5334,5335,5490,5334,5490,5489,5335,5336,5491,5335,5491,5490,5336,5337,5492,5336,5492,5491,5337,5338,5493,5337,5493,5492,5338,5339,5494,5338,5494,5493,5339,5340,5495,5339,5495,5494,5340,5341,5496,5340,5496,5495,5341,5342,5497,5341,5497,5496,5342,5343,5498,5342,5498,5497,5343,5344,5499,5343,5499,5498,5344,5345,5500,5344,5500,5499,5345,5346,5501,5345,5501,5500,5346,5347,5502,5346,5502,5501,5347,5348,5503,5347,5503,5502,5348,5349,5504,5348,5504,5503,5349,5350,5505,5349,5505,5504,5350,5351,5506,5350,5506,5505,5351,5352,5507,5351,5507,5506,5352,5353,5508,5352,5508,5507,5353,5354,5509,5353,5509,5508,5354,5355,5510,5354,5510,5509,5355,5356,5511,5355,5511,5510,5356,5357,5512,5356,5512,5511,5357,5358,5513,5357,5513,5512,5358,5359,5514,5358,5514,5513,5359,5360,5515,5359,5515,5514,5360,5361,5516,5360,5516,5515,5361,5362,5517,5361,5517,5516,5362,5363,5518,5362,5518,5517,5363,5364,5519,5363,5519,5518,5364,5365,5520,5364,5520,5519,5365,5366,5521,5365,5521,5520,5366,5367,5522,5366,5522,5521,5367,5368,5523,5367,5523,5522,5368,5369,5524,5368,5524,5523,5369,5370,5525,5369,5525,5524,5370,5371,5526,5370,5526,5525,5371,5372,5527,5371,5527,5526,5372,5373,5528,5372,5528,5527,5373,5374,5529,5373,5529,5528,5374,5375,5530,5374,5530,5529,5375,5376,5531,5375,5531,5530,5376,5377,5532,5376,5532,5531,5377,5378,5533,5377,5533,5532,5378,5379,5534,5378,5534,5533,5379,5380,5535,5379,5535,5534,5380,5381,5536,5380,5536,5535,5381,5382,5537,5381,5537,5536,5382,5383,5538,5382,5538,5537,5383,5384,5539,5383,5539,5538,5384,5385,5540,5384,5540,5539,5385,5386,5541,5385,5541,5540,5386,5387,5542,5386,5542,5541,5387,5388,5543,5387,5543,5542,5388,5389,5544,5388,5544,5543,5389,5390,5545,5389,5545,5544,5390,5391,5546,5390,5546,5545,5391,5392,5547,5391,5547,5546,5392,5393,5548,5392,5548,5547,5393,5394,5549,5393,5549,5548,5394,5395,5550,5394,5550,5549,5395,5396,5551,5395,5551,5550,5396,5397,5552,5396,5552,5551,5397,5398,5553,5397,5553,5552,5398,5399,5554,5398,5554,5553,5399,5400,5555,5399,5555,5554,5400,5401,5556,5400,5556,5555,5401,5402,5557,5401,5557,5556,5402,5403,5558,5402,5558,5557,5403,5404,5559,5403,5559,5558,5404,5405,5560,5404,5560,5559,5405,5406,5561,5405,5561,5560,5406,5407,5562,5406,5562,5561,5407,5408,5563,5407,5563,5562,5408,5409,5564,5408,5564,5563,5409,5410,5565,5409,5565,5564,5410,5411,5566,5410,5566,5565,5411,5412,5567,5411,5567,5566,5412,5413,5568,5412,5568,5567,5413,5414,5569,5413,5569,5568,5414,5415,5570,5414,5570,5569,5415,5416,5571,5415,5571,5570,5416,5417,5572,5416,5572,5571,5417,5418,5573,5417,5573,5572,5418,5419,5574,5418,5574,5573,5419,5420,5575,5419,5575,5574,5420,5421,5576,5420,5576,5575,5421,5422,5577,5421,5577,5576,5422,5423,5578,5422,5578,5577,5423,5424,5579,5423,5579,5578,5425,5426,5581,5425,5581,5580,5426,5427,5582,5426,5582,5581,5427,5428,5583,5427,5583,5582,5428,5429,5584,5428,5584,5583,5429,5430,5585,5429,5585,5584,5430,5431,5586,5430,5586,5585,5431,5432,5587,5431,5587,5586,5432,5433,5588,5432,5588,5587,5433,5434,5589,5433,5589,5588,5434,5435,5590,5434,5590,5589,5435,5436,5591,5435,5591,5590,5436,5437,5592,5436,5592,5591,5437,5438,5593,5437,5593,5592,5438,5439,5594,5438,5594,5593,5439,5440,5595,5439,5595,5594,5440,5441,5596,5440,5596,5595,5441,5442,5597,5441,5597,5596,5442,5443,5598,5442,5598,5597,5443,5444,5599,5443,5599,5598,5444,5445,5600,5444,5600,5599,5445,5446,5601,5445,5601,5600,5446,5447,5602,5446,5602,5601,5447,5448,5603,5447,5603,5602,5448,5449,5604,5448,5604,5603,5449,5450,5605,5449,5605,5604,5450,5451,5606,5450,5606,5605,5451,5452,5607,5451,5607,5606,5452,5453,5608,5452,5608,5607,5453,5454,5609,5453,5609,5608,5454,5455,5610,5454,5610,5609,5455,5456,5611,5455,5611,5610,5456,5457,5612,5456,5612,5611,5457,5458,5613,5457,5613,5612,5458,5459,5614,5458,5614,5613,5459,5460,5615,5459,5615,5614,5460,5461,5616,5460,5616,5615,5461,5462,5617,5461,5617,5616,5462,5463,5618,5462,5618,5617,5463,5464,5619,5463,5619,5618,5464,5465,5620,5464,5620,5619,5465,5466,5621,5465,5621,5620,5466,5467,5622,5466,5622,5621,5467,5468,5623,5467,5623,5622,5468,5469,5624,5468,5624,5623,5469,5470,5625,5469,5625,5624,5470,5471,5626,5470,5626,5625,5471,5472,5627,5471,5627,5626,5472,5473,5628,5472,5628,5627,5473,5474,5629,5473,5629,5628,5474,5475,5630,5474,5630,5629,5475,5476,5631,5475,5631,5630,5476,5477,5632,5476,5632,5631,5477,5478,5633,5477,5633,5632,5478,5479,5634,5478,5634,5633,5479,5480,5635,5479,5635,5634,5480,5481,5636,5480,5636,5635,5481,5482,5637,5481,5637,5636,5482,5483,5638,5482,5638,5637,5483,5484,5639,5483,5639,5638,5484,5485,5640,5484,5640,5639,5485,5486,5641,5485,5641,5640,5486,5487,5642,5486,5642,5641,5487,5488,5643,5487,5643,5642,5488,5489,5644,5488,5644,5643,5489,5490,5645,5489,5645,5644,5490,5491,5646,5490,5646,5645,5491,5492,5647,5491,5647,5646,5492,5493,5648,5492,5648,5647,5493,5494,5649,5493,5649,5648,5494,5495,5650,5494,5650,5649,5495,5496,5651,5495,5651,5650,5496,5497,5652,5496,5652,5651,5497,5498,5653,5497,5653,5652,5498,5499,5654,5498,5654,5653,5499,5500,5655,5499,5655,5654,5500,5501,5656,5500,5656,5655,5501,5502,5657,5501,5657,5656,5502,5503,5658,5502,5658,5657,5503,5504,5659,5503,5659,5658,5504,5505,5660,5504,5660,5659,5505,5506,5661,5505,5661,5660,5506,5507,5662,5506,5662,5661,5507,5508,5663,5507,5663,5662,5508,5509,5664,5508,5664,5663,5509,5510,5665,5509,5665,5664,5510,5511,5666,5510,5666,5665,5511,5512,5667,5511,5667,5666,5512,5513,5668,5512,5668,5667,5513,5514,5669,5513,5669,5668,5514,5515,5670,5514,5670,5669,5515,5516,5671,5515,5671,5670,5516,5517,5672,5516,5672,5671,5517,5518,5673,5517,5673,5672,5518,5519,5674,5518,5674,5673,5519,5520,5675,5519,5675,5674,5520,5521,5676,5520,5676,5675,5521,5522,5677,5521,5677,5676,5522,5523,5678,5522,5678,5677,5523,5524,5679,5523,5679,5678,5524,5525,5680,5524,5680,5679,5525,5526,5681,5525,5681,5680,5526,5527,5682,5526,5682,5681,5527,5528,5683,5527,5683,5682,5528,5529,5684,5528,5684,5683,5529,5530,5685,5529,5685,5684,5530,5531,5686,5530,5686,5685,5531,5532,5687,5531,5687,5686,5532,5533,5688,5532,5688,5687,5533,5534,5689,5533,5689,5688,5534,5535,5690,5534,5690,5689,5535,5536,5691,5535,5691,5690,5536,5537,5692,5536,5692,5691,5537,5538,5693,5537,5693,5692,5538,5539,5694,5538,5694,5693,5539,5540,5695,5539,5695,5694,5540,5541,5696,5540,5696,5695,5541,5542,5697,5541,5697,5696,5542,5543,5698,5542,5698,5697,5543,5544,5699,5543,5699,5698,5544,5545,5700,5544,5700,5699,5545,5546,5701,5545,5701,5700,5546,5547,5702,5546,5702,5701,5547,5548,5703,5547,5703,5702,5548,5549,5704,5548,5704,5703,5549,5550,5705,5549,5705,5704,5550,5551,5706,5550,5706,5705,5551,5552,5707,5551,5707,5706,5552,5553,5708,5552,5708,5707,5553,5554,5709,5553,5709,5708,5554,5555,5710,5554,5710,5709,5555,5556,5711,5555,5711,5710,5556,5557,5712,5556,5712,5711,5557,5558,5713,5557,5713,5712,5558,5559,5714,5558,5714,5713,5559,5560,5715,5559,5715,5714,5560,5561,5716,5560,5716,5715,5561,5562,5717,5561,5717,5716,5562,5563,5718,5562,5718,5717,5563,5564,5719,5563,5719,5718,5564,5565,5720,5564,5720,5719,5565,5566,5721,5565,5721,5720,5566,5567,5722,5566,5722,5721,5567,5568,5723,5567,5723,5722,5568,5569,5724,5568,5724,5723,5569,5570,5725,5569,5725,5724,5570,5571,5726,5570,5726,5725,5571,5572,5727,5571,5727,5726,5572,5573,5728,5572,5728,5727,5573,5574,5729,5573,5729,5728,5574,5575,5730,5574,5730,5729,5575,5576,5731,5575,5731,5730,5576,5577,5732,5576,5732,5731,5577,5578,5733,5577,5733,5732,5578,5579,5734,5578,5734,5733,5580,5581,5736,5580,5736,5735,5581,5582,5737,5581,5737,5736,5582,5583,5738,5582,5738,5737,5583,5584,5739,5583,5739,5738,5584,5585,5740,5584,5740,5739,5585,5586,5741,5585,5741,5740,5586,5587,5742,5586,5742,5741,5587,5588,5743,5587,5743,5742,5588,5589,5744,5588,5744,5743,5589,5590,5745,5589,5745,5744,5590,5591,5746,5590,5746,5745,5591,5592,5747,5591,5747,5746,5592,5593,5748,5592,5748,5747,5593,5594,5749,5593,5749,5748,5594,5595,5750,5594,5750,5749,5595,5596,5751,5595,5751,5750,5596,5597,5752,5596,5752,5751,5597,5598,5753,5597,5753,5752,5598,5599,5754,5598,5754,5753,5599,5600,5755,5599,5755,5754,5600,5601,5756,5600,5756,5755,5601,5602,5757,5601,5757,5756,5602,5603,5758,5602,5758,5757,5603,5604,5759,5603,5759,5758,5604,5605,5760,5604,5760,5759,5605,5606,5761,5605,5761,5760,5606,5607,5762,5606,5762,5761,5607,5608,5763,5607,5763,5762,5608,5609,5764,5608,5764,5763,5609,5610,5765,5609,5765,5764,5610,5611,5766,5610,5766,5765,5611,5612,5767,5611,5767,5766,5612,5613,5768,5612,5768,5767,5613,5614,5769,5613,5769,5768,5614,5615,5770,5614,5770,5769,5615,5616,5771,5615,5771,5770,5616,5617,5772,5616,5772,5771,5617,5618,5773,5617,5773,5772,5618,5619,5774,5618,5774,5773,5619,5620,5775,5619,5775,5774,5620,5621,5776,5620,5776,5775,5621,5622,5777,5621,5777,5776,5622,5623,5778,5622,5778,5777,5623,5624,5779,5623,5779,5778,5624,5625,5780,5624,5780,5779,5625,5626,5781,5625,5781,5780,5626,5627,5782,5626,5782,5781,5627,5628,5783,5627,5783,5782,5628,5629,5784,5628,5784,5783,5629,5630,5785,5629,5785,5784,5630,5631,5786,5630,5786,5785,5631,5632,5787,5631,5787,5786,5632,5633,5788,5632,5788,5787,5633,5634,5789,5633,5789,5788,5634,5635,5790,5634,5790,5789,5635,5636,5791,5635,5791,5790,5636,5637,5792,5636,5792,5791,5637,5638,5793,5637,5793,5792,5638,5639,5794,5638,5794,5793,5639,5640,5795,5639,5795,5794,5640,5641,5796,5640,5796,5795,5641,5642,5797,5641,5797,5796,5642,5643,5798,5642,5798,5797,5643,5644,5799,5643,5799,5798,5644,5645,5800,5644,5800,5799,5645,5646,5801,5645,5801,5800,5646,5647,5802,5646,5802,5801,5647,5648,5803,5647,5803,5802,5648,5649,5804,5648,5804,5803,5649,5650,5805,5649,5805,5804,5650,5651,5806,5650,5806,5805,5651,5652,5807,5651,5807,5806,5652,5653,5808,5652,5808,5807,5653,5654,5809,5653,5809,5808,5654,5655,5810,5654,5810,5809,5655,5656,5811,5655,5811,5810,5656,5657,5812,5656,5812,5811,5657,5658,5813,5657,5813,5812,5658,5659,5814,5658,5814,5813,5659,5660,5815,5659,5815,5814,5660,5661,5816,5660,5816,5815,5661,5662,5817,5661,5817,5816,5662,5663,5818,5662,5818,5817,5663,5664,5819,5663,5819,5818,5664,5665,5820,5664,5820,5819,5665,5666,5821,5665,5821,5820,5666,5667,5822,5666,5822,5821,5667,5668,5823,5667,5823,5822,5668,5669,5824,5668,5824,5823,5669,5670,5825,5669,5825,5824,5670,5671,5826,5670,5826,5825,5671,5672,5827,5671,5827,5826,5672,5673,5828,5672,5828,5827,5673,5674,5829,5673,5829,5828,5674,5675,5830,5674,5830,5829,5675,5676,5831,5675,5831,5830,5676,5677,5832,5676,5832,5831,5677,5678,5833,5677,5833,5832,5678,5679,5834,5678,5834,5833,5679,5680,5835,5679,5835,5834,5680,5681,5836,5680,5836,5835,5681,5682,5837,5681,5837,5836,5682,5683,5838,5682,5838,5837,5683,5684,5839,5683,5839,5838,5684,5685,5840,5684,5840,5839,5685,5686,5841,5685,5841,5840,5686,5687,5842,5686,5842,5841,5687,5688,5843,5687,5843,5842,5688,5689,5844,5688,5844,5843,5689,5690,5845,5689,5845,5844,5690,5691,5846,5690,5846,5845,5691,5692,5847,5691,5847,5846,5692,5693,5848,5692,5848,5847,5693,5694,5849,5693,5849,5848,5694,5695,5850,5694,5850,5849,5695,5696,5851,5695,5851,5850,5696,5697,5852,5696,5852,5851,5697,5698,5853,5697,5853,5852,5698,5699,5854,5698,5854,5853,5699,5700,5855,5699,5855,5854,5700,5701,5856,5700,5856,5855,5701,5702,5857,5701,5857,5856,5702,5703,5858,5702,5858,5857,5703,5704,5859,5703,5859,5858,5704,5705,5860,5704,5860,5859,5705,5706,5861,5705,5861,5860,5706,5707,5862,5706,5862,5861,5707,5708,5863,5707,5863,5862,5708,5709,5864,5708,5864,5863,5709,5710,5865,5709,5865,5864,5710,5711,5866,5710,5866,5865,5711,5712,5867,5711,5867,5866,5712,5713,5868,5712,5868,5867,5713,5714,5869,5713,5869,5868,5714,5715,5870,5714,5870,5869,5715,5716,5871,5715,5871,5870,5716,5717,5872,5716,5872,5871,5717,5718,5873,5717,5873,5872,5718,5719,5874,5718,5874,5873,5719,5720,5875,5719,5875,5874,5720,5721,5876,5720,5876,5875,5721,5722,5877,5721,5877,5876,5722,5723,5878,5722,5878,5877,5723,5724,5879,5723,5879,5878,5724,5725,5880,5724,5880,5879,5725,5726,5881,5725,5881,5880,5726,5727,5882,5726,5882,5881,5727,5728,5883,5727,5883,5882,5728,5729,5884,5728,5884,5883,5729,5730,5885,5729,5885,5884,5730,5731,5886,5730,5886,5885,5731,5732,5887,5731,5887,5886,5732,5733,5888,5732,5888,5887,5733,5734,5889,5733,5889,5888,5735,5736,5891,5735,5891,5890,5736,5737,5892,5736,5892,5891,5737,5738,5893,5737,5893,5892,5738,5739,5894,5738,5894,5893,5739,5740,5895,5739,5895,5894,5740,5741,5896,5740,5896,5895,5741,5742,5897,5741,5897,5896,5742,5743,5898,5742,5898,5897,5743,5744,5899,5743,5899,5898,5744,5745,5900,5744,5900,5899,5745,5746,5901,5745,5901,5900,5746,5747,5902,5746,5902,5901,5747,5748,5903,5747,5903,5902,5748,5749,5904,5748,5904,5903,5749,5750,5905,5749,5905,5904,5750,5751,5906,5750,5906,5905,5751,5752,5907,5751,5907,5906,5752,5753,5908,5752,5908,5907,5753,5754,5909,5753,5909,5908,5754,5755,5910,5754,5910,5909,5755,5756,5911,5755,5911,5910,5756,5757,5912,5756,5912,5911,5757,5758,5913,5757,5913,5912,5758,5759,5914,5758,5914,5913,5759,5760,5915,5759,5915,5914,5760,5761,5916,5760,5916,5915,5761,5762,5917,5761,5917,5916,5762,5763,5918,5762,5918,5917,5763,5764,5919,5763,5919,5918,5764,5765,5920,5764,5920,5919,5765,5766,5921,5765,5921,5920,5766,5767,5922,5766,5922,5921,5767,5768,5923,5767,5923,5922,5768,5769,5924,5768,5924,5923,5769,5770,5925,5769,5925,5924,5770,5771,5926,5770,5926,5925,5771,5772,5927,5771,5927,5926,5772,5773,5928,5772,5928,5927,5773,5774,5929,5773,5929,5928,5774,5775,5930,5774,5930,5929,5775,5776,5931,5775,5931,5930,5776,5777,5932,5776,5932,5931,5777,5778,5933,5777,5933,5932,5778,5779,5934,5778,5934,5933,5779,5780,5935,5779,5935,5934,5780,5781,5936,5780,5936,5935,5781,5782,5937,5781,5937,5936,5782,5783,5938,5782,5938,5937,5783,5784,5939,5783,5939,5938,5784,5785,5940,5784,5940,5939,5785,5786,5941,5785,5941,5940,5786,5787,5942,5786,5942,5941,5787,5788,5943,5787,5943,5942,5788,5789,5944,5788,5944,5943,5789,5790,5945,5789,5945,5944,5790,5791,5946,5790,5946,5945,5791,5792,5947,5791,5947,5946,5792,5793,5948,5792,5948,5947,5793,5794,5949,5793,5949,5948,5794,5795,5950,5794,5950,5949,5795,5796,5951,5795,5951,5950,5796,5797,5952,5796,5952,5951,5797,5798,5953,5797,5953,5952,5798,5799,5954,5798,5954,5953,5799,5800,5955,5799,5955,5954,5800,5801,5956,5800,5956,5955,5801,5802,5957,5801,5957,5956,5802,5803,5958,5802,5958,5957,5803,5804,5959,5803,5959,5958,5804,5805,5960,5804,5960,5959,5805,5806,5961,5805,5961,5960,5806,5807,5962,5806,5962,5961,5807,5808,5963,5807,5963,5962,5808,5809,5964,5808,5964,5963,5809,5810,5965,5809,5965,5964,5810,5811,5966,5810,5966,5965,5811,5812,5967,5811,5967,5966,5812,5813,5968,5812,5968,5967,5813,5814,5969,5813,5969,5968,5814,5815,5970,5814,5970,5969,5815,5816,5971,5815,5971,5970,5816,5817,5972,5816,5972,5971,5817,5818,5973,5817,5973,5972,5818,5819,5974,5818,5974,5973,5819,5820,5975,5819,5975,5974,5820,5821,5976,5820,5976,5975,5821,5822,5977,5821,5977,5976,5822,5823,5978,5822,5978,5977,5823,5824,5979,5823,5979,5978,5824,5825,5980,5824,5980,5979,5825,5826,5981,5825,5981,5980,5826,5827,5982,5826,5982,5981,5827,5828,5983,5827,5983,5982,5828,5829,5984,5828,5984,5983,5829,5830,5985,5829,5985,5984,5830,5831,5986,5830,5986,5985,5831,5832,5987,5831,5987,5986,5832,5833,5988,5832,5988,5987,5833,5834,5989,5833,5989,5988,5834,5835,5990,5834,5990,5989,5835,5836,5991,5835,5991,5990,5836,5837,5992,5836,5992,5991,5837,5838,5993,5837,5993,5992,5838,5839,5994,5838,5994,5993,5839,5840,5995,5839,5995,5994,5840,5841,5996,5840,5996,5995,5841,5842,5997,5841,5997,5996,5842,5843,5998,5842,5998,5997,5843,5844,5999,5843,5999,5998,5844,5845,6000,5844,6000,5999,5845,5846,6001,5845,6001,6000,5846,5847,6002,5846,6002,6001,5847,5848,6003,5847,6003,6002,5848,5849,6004,5848,6004,6003,5849,5850,6005,5849,6005,6004,5850,5851,6006,5850,6006,6005,5851,5852,6007,5851,6007,6006,5852,5853,6008,5852,6008,6007,5853,5854,6009,5853,6009,6008,5854,5855,6010,5854,6010,6009,5855,5856,6011,5855,6011,6010,5856,5857,6012,5856,6012,6011,5857,5858,6013,5857,6013,6012,5858,5859,6014,5858,6014,6013,5859,5860,6015,5859,6015,6014,5860,5861,6016,5860,6016,6015,5861,5862,6017,5861,6017,6016,5862,5863,6018,5862,6018,6017,5863,5864,6019,5863,6019,6018,5864,5865,6020,5864,6020,6019,5865,5866,6021,5865,6021,6020,5866,5867,6022,5866,6022,6021,5867,5868,6023,5867,6023,6022,5868,5869,6024,5868,6024,6023,5869,5870,6025,5869,6025,6024,5870,5871,6026,5870,6026,6025,5871,5872,6027,5871,6027,6026,5872,5873,6028,5872,6028,6027,5873,5874,6029,5873,6029,6028,5874,5875,6030,5874,6030,6029,5875,5876,6031,5875,6031,6030,5876,5877,6032,5876,6032,6031,5877,5878,6033,5877,6033,6032,5878,5879,6034,5878,6034,6033,5879,5880,6035,5879,6035,6034,5880,5881,6036,5880,6036,6035,5881,5882,6037,5881,6037,6036,5882,5883,6038,5882,6038,6037,5883,5884,6039,5883,6039,6038,5884,5885,6040,5884,6040,6039,5885,5886,6041,5885,6041,6040,5886,5887,6042,5886,6042,6041,5887,5888,6043,5887,6043,6042,5888,5889,6044,5888,6044,6043,5890,5891,6046,5890,6046,6045,5891,5892,6047,5891,6047,6046,5892,5893,6048,5892,6048,6047,5893,5894,6049,5893,6049,6048,5894,5895,6050,5894,6050,6049,5895,5896,6051,5895,6051,6050,5896,5897,6052,5896,6052,6051,5897,5898,6053,5897,6053,6052,5898,5899,6054,5898,6054,6053,5899,5900,6055,5899,6055,6054,5900,5901,6056,5900,6056,6055,5901,5902,6057,5901,6057,6056,5902,5903,6058,5902,6058,6057,5903,5904,6059,5903,6059,6058,5904,5905,6060,5904,6060,6059,5905,5906,6061,5905,6061,6060,5906,5907,6062,5906,6062,6061,5907,5908,6063,5907,6063,6062,5908,5909,6064,5908,6064,6063,5909,5910,6065,5909,6065,6064,5910,5911,6066,5910,6066,6065,5911,5912,6067,5911,6067,6066,5912,5913,6068,5912,6068,6067,5913,5914,6069,5913,6069,6068,5914,5915,6070,5914,6070,6069,5915,5916,6071,5915,6071,6070,5916,5917,6072,5916,6072,6071,5917,5918,6073,5917,6073,6072,5918,5919,6074,5918,6074,6073,5919,5920,6075,5919,6075,6074,5920,5921,6076,5920,6076,6075,5921,5922,6077,5921,6077,6076,5922,5923,6078,5922,6078,6077,5923,5924,6079,5923,6079,6078,5924,5925,6080,5924,6080,6079,5925,5926,6081,5925,6081,6080,5926,5927,6082,5926,6082,6081,5927,5928,6083,5927,6083,6082,5928,5929,6084,5928,6084,6083,5929,5930,6085,5929,6085,6084,5930,5931,6086,5930,6086,6085,5931,5932,6087,5931,6087,6086,5932,5933,6088,5932,6088,6087,5933,5934,6089,5933,6089,6088,5934,5935,6090,5934,6090,6089,5935,5936,6091,5935,6091,6090,5936,5937,6092,5936,6092,6091,5937,5938,6093,5937,6093,6092,5938,5939,6094,5938,6094,6093,5939,5940,6095,5939,6095,6094,5940,5941,6096,5940,6096,6095,5941,5942,6097,5941,6097,6096,5942,5943,6098,5942,6098,6097,5943,5944,6099,5943,6099,6098,5944,5945,6100,5944,6100,6099,5945,5946,6101,5945,6101,6100,5946,5947,6102,5946,6102,6101,5947,5948,6103,5947,6103,6102,5948,5949,6104,5948,6104,6103,5949,5950,6105,5949,6105,6104,5950,5951,6106,5950,6106,6105,5951,5952,6107,5951,6107,6106,5952,5953,6108,5952,6108,6107,5953,5954,6109,5953,6109,6108,5954,5955,6110,5954,6110,6109,5955,5956,6111,5955,6111,6110,5956,5957,6112,5956,6112,6111,5957,5958,6113,5957,6113,6112,5958,5959,6114,5958,6114,6113,5959,5960,6115,5959,6115,6114,5960,5961,6116,5960,6116,6115,5961,5962,6117,5961,6117,6116,5962,5963,6118,5962,6118,6117,5963,5964,6119,5963,6119,6118,5964,5965,6120,5964,6120,6119,5965,5966,6121,5965,6121,6120,5966,5967,6122,5966,6122,6121,5967,5968,6123,5967,6123,6122,5968,5969,6124,5968,6124,6123,5969,5970,6125,5969,6125,6124,5970,5971,6126,5970,6126,6125,5971,5972,6127,5971,6127,6126,5972,5973,6128,5972,6128,6127,5973,5974,6129,5973,6129,6128,5974,5975,6130,5974,6130,6129,5975,5976,6131,5975,6131,6130,5976,5977,6132,5976,6132,6131,5977,5978,6133,5977,6133,6132,5978,5979,6134,5978,6134,6133,5979,5980,6135,5979,6135,6134,5980,5981,6136,5980,6136,6135,5981,5982,6137,5981,6137,6136,5982,5983,6138,5982,6138,6137,5983,5984,6139,5983,6139,6138,5984,5985,6140,5984,6140,6139,5985,5986,6141,5985,6141,6140,5986,5987,6142,5986,6142,6141,5987,5988,6143,5987,6143,6142,5988,5989,6144,5988,6144,6143,5989,5990,6145,5989,6145,6144,5990,5991,6146,5990,6146,6145,5991,5992,6147,5991,6147,6146,5992,5993,6148,5992,6148,6147,5993,5994,6149,5993,6149,6148,5994,5995,6150,5994,6150,6149,5995,5996,6151,5995,6151,6150,5996,5997,6152,5996,6152,6151,5997,5998,6153,5997,6153,6152,5998,5999,6154,5998,6154,6153,5999,6000,6155,5999,6155,6154,6000,6001,6156,6000,6156,6155,6001,6002,6157,6001,6157,6156,6002,6003,6158,6002,6158,6157,6003,6004,6159,6003,6159,6158,6004,6005,6160,6004,6160,6159,6005,6006,6161,6005,6161,6160,6006,6007,6162,6006,6162,6161,6007,6008,6163,6007,6163,6162,6008,6009,6164,6008,6164,6163,6009,6010,6165,6009,6165,6164,6010,6011,6166,6010,6166,6165,6011,6012,6167,6011,6167,6166,6012,6013,6168,6012,6168,6167,6013,6014,6169,6013,6169,6168,6014,6015,6170,6014,6170,6169,6015,6016,6171,6015,6171,6170,6016,6017,6172,6016,6172,6171,6017,6018,6173,6017,6173,6172,6018,6019,6174,6018,6174,6173,6019,6020,6175,6019,6175,6174,6020,6021,6176,6020,6176,6175,6021,6022,6177,6021,6177,6176,6022,6023,6178,6022,6178,6177,6023,6024,6179,6023,6179,6178,6024,6025,6180,6024,6180,6179,6025,6026,6181,6025,6181,6180,6026,6027,6182,6026,6182,6181,6027,6028,6183,6027,6183,6182,6028,6029,6184,6028,6184,6183,6029,6030,6185,6029,6185,6184,6030,6031,6186,6030,6186,6185,6031,6032,6187,6031,6187,6186,6032,6033,6188,6032,6188,6187,6033,6034,6189,6033,6189,6188,6034,6035,6190,6034,6190,6189,6035,6036,6191,6035,6191,6190,6036,6037,6192,6036,6192,6191,6037,6038,6193,6037,6193,6192,6038,6039,6194,6038,6194,6193,6039,6040,6195,6039,6195,6194,6040,6041,6196,6040,6196,6195,6041,6042,6197,6041,6197,6196,6042,6043,6198,6042,6198,6197,6043,6044,6199,6043,6199,6198,6045,6046,6201,6045,6201,6200,6046,6047,6202,6046,6202,6201,6047,6048,6203,6047,6203,6202,6048,6049,6204,6048,6204,6203,6049,6050,6205,6049,6205,6204,6050,6051,6206,6050,6206,6205,6051,6052,6207,6051,6207,6206,6052,6053,6208,6052,6208,6207,6053,6054,6209,6053,6209,6208,6054,6055,6210,6054,6210,6209,6055,6056,6211,6055,6211,6210,6056,6057,6212,6056,6212,6211,6057,6058,6213,6057,6213,6212,6058,6059,6214,6058,6214,6213,6059,6060,6215,6059,6215,6214,6060,6061,6216,6060,6216,6215,6061,6062,6217,6061,6217,6216,6062,6063,6218,6062,6218,6217,6063,6064,6219,6063,6219,6218,6064,6065,6220,6064,6220,6219,6065,6066,6221,6065,6221,6220,6066,6067,6222,6066,6222,6221,6067,6068,6223,6067,6223,6222,6068,6069,6224,6068,6224,6223,6069,6070,6225,6069,6225,6224,6070,6071,6226,6070,6226,6225,6071,6072,6227,6071,6227,6226,6072,6073,6228,6072,6228,6227,6073,6074,6229,6073,6229,6228,6074,6075,6230,6074,6230,6229,6075,6076,6231,6075,6231,6230,6076,6077,6232,6076,6232,6231,6077,6078,6233,6077,6233,6232,6078,6079,6234,6078,6234,6233,6079,6080,6235,6079,6235,6234,6080,6081,6236,6080,6236,6235,6081,6082,6237,6081,6237,6236,6082,6083,6238,6082,6238,6237,6083,6084,6239,6083,6239,6238,6084,6085,6240,6084,6240,6239,6085,6086,6241,6085,6241,6240,6086,6087,6242,6086,6242,6241,6087,6088,6243,6087,6243,6242,6088,6089,6244,6088,6244,6243,6089,6090,6245,6089,6245,6244,6090,6091,6246,6090,6246,6245,6091,6092,6247,6091,6247,6246,6092,6093,6248,6092,6248,6247,6093,6094,6249,6093,6249,6248,6094,6095,6250,6094,6250,6249,6095,6096,6251,6095,6251,6250,6096,6097,6252,6096,6252,6251,6097,6098,6253,6097,6253,6252,6098,6099,6254,6098,6254,6253,6099,6100,6255,6099,6255,6254,6100,6101,6256,6100,6256,6255,6101,6102,6257,6101,6257,6256,6102,6103,6258,6102,6258,6257,6103,6104,6259,6103,6259,6258,6104,6105,6260,6104,6260,6259,6105,6106,6261,6105,6261,6260,6106,6107,6262,6106,6262,6261,6107,6108,6263,6107,6263,6262,6108,6109,6264,6108,6264,6263,6109,6110,6265,6109,6265,6264,6110,6111,6266,6110,6266,6265,6111,6112,6267,6111,6267,6266,6112,6113,6268,6112,6268,6267,6113,6114,6269,6113,6269,6268,6114,6115,6270,6114,6270,6269,6115,6116,6271,6115,6271,6270,6116,6117,6272,6116,6272,6271,6117,6118,6273,6117,6273,6272,6118,6119,6274,6118,6274,6273,6119,6120,6275,6119,6275,6274,6120,6121,6276,6120,6276,6275,6121,6122,6277,6121,6277,6276,6122,6123,6278,6122,6278,6277,6123,6124,6279,6123,6279,6278,6124,6125,6280,6124,6280,6279,6125,6126,6281,6125,6281,6280,6126,6127,6282,6126,6282,6281,6127,6128,6283,6127,6283,6282,6128,6129,6284,6128,6284,6283,6129,6130,6285,6129,6285,6284,6130,6131,6286,6130,6286,6285,6131,6132,6287,6131,6287,6286,6132,6133,6288,6132,6288,6287,6133,6134,6289,6133,6289,6288,6134,6135,6290,6134,6290,6289,6135,6136,6291,6135,6291,6290,6136,6137,6292,6136,6292,6291,6137,6138,6293,6137,6293,6292,6138,6139,6294,6138,6294,6293,6139,6140,6295,6139,6295,6294,6140,6141,6296,6140,6296,6295,6141,6142,6297,6141,6297,6296,6142,6143,6298,6142,6298,6297,6143,6144,6299,6143,6299,6298,6144,6145,6300,6144,6300,6299,6145,6146,6301,6145,6301,6300,6146,6147,6302,6146,6302,6301,6147,6148,6303,6147,6303,6302,6148,6149,6304,6148,6304,6303,6149,6150,6305,6149,6305,6304,6150,6151,6306,6150,6306,6305,6151,6152,6307,6151,6307,6306,6152,6153,6308,6152,6308,6307,6153,6154,6309,6153,6309,6308,6154,6155,6310,6154,6310,6309,6155,6156,6311,6155,6311,6310,6156,6157,6312,6156,6312,6311,6157,6158,6313,6157,6313,6312,6158,6159,6314,6158,6314,6313,6159,6160,6315,6159,6315,6314,6160,6161,6316,6160,6316,6315,6161,6162,6317,6161,6317,6316,6162,6163,6318,6162,6318,6317,6163,6164,6319,6163,6319,6318,6164,6165,6320,6164,6320,6319,6165,6166,6321,6165,6321,6320,6166,6167,6322,6166,6322,6321,6167,6168,6323,6167,6323,6322,6168,6169,6324,6168,6324,6323,6169,6170,6325,6169,6325,6324,6170,6171,6326,6170,6326,6325,6171,6172,6327,6171,6327,6326,6172,6173,6328,6172,6328,6327,6173,6174,6329,6173,6329,6328,6174,6175,6330,6174,6330,6329,6175,6176,6331,6175,6331,6330,6176,6177,6332,6176,6332,6331,6177,6178,6333,6177,6333,6332,6178,6179,6334,6178,6334,6333,6179,6180,6335,6179,6335,6334,6180,6181,6336,6180,6336,6335,6181,6182,6337,6181,6337,6336,6182,6183,6338,6182,6338,6337,6183,6184,6339,6183,6339,6338,6184,6185,6340,6184,6340,6339,6185,6186,6341,6185,6341,6340,6186,6187,6342,6186,6342,6341,6187,6188,6343,6187,6343,6342,6188,6189,6344,6188,6344,6343,6189,6190,6345,6189,6345,6344,6190,6191,6346,6190,6346,6345,6191,6192,6347,6191,6347,6346,6192,6193,6348,6192,6348,6347,6193,6194,6349,6193,6349,6348,6194,6195,6350,6194,6350,6349,6195,6196,6351,6195,6351,6350,6196,6197,6352,6196,6352,6351,6197,6198,6353,6197,6353,6352,6198,6199,6354,6198,6354,6353,6200,6201,6356,6200,6356,6355,6201,6202,6357,6201,6357,6356,6202,6203,6358,6202,6358,6357,6203,6204,6359,6203,6359,6358,6204,6205,6360,6204,6360,6359,6205,6206,6361,6205,6361,6360,6206,6207,6362,6206,6362,6361,6207,6208,6363,6207,6363,6362,6208,6209,6364,6208,6364,6363,6209,6210,6365,6209,6365,6364,6210,6211,6366,6210,6366,6365,6211,6212,6367,6211,6367,6366,6212,6213,6368,6212,6368,6367,6213,6214,6369,6213,6369,6368,6214,6215,6370,6214,6370,6369,6215,6216,6371,6215,6371,6370,6216,6217,6372,6216,6372,6371,6217,6218,6373,6217,6373,6372,6218,6219,6374,6218,6374,6373,6219,6220,6375,6219,6375,6374,6220,6221,6376,6220,6376,6375,6221,6222,6377,6221,6377,6376,6222,6223,6378,6222,6378,6377,6223,6224,6379,6223,6379,6378,6224,6225,6380,6224,6380,6379,6225,6226,6381,6225,6381,6380,6226,6227,6382,6226,6382,6381,6227,6228,6383,6227,6383,6382,6228,6229,6384,6228,6384,6383,6229,6230,6385,6229,6385,6384,6230,6231,6386,6230,6386,6385,6231,6232,6387,6231,6387,6386,6232,6233,6388,6232,6388,6387,6233,6234,6389,6233,6389,6388,6234,6235,6390,6234,6390,6389,6235,6236,6391,6235,6391,6390,6236,6237,6392,6236,6392,6391,6237,6238,6393,6237,6393,6392,6238,6239,6394,6238,6394,6393,6239,6240,6395,6239,6395,6394,6240,6241,6396,6240,6396,6395,6241,6242,6397,6241,6397,6396,6242,6243,6398,6242,6398,6397,6243,6244,6399,6243,6399,6398,6244,6245,6400,6244,6400,6399,6245,6246,6401,6245,6401,6400,6246,6247,6402,6246,6402,6401,6247,6248,6403,6247,6403,6402,6248,6249,6404,6248,6404,6403,6249,6250,6405,6249,6405,6404,6250,6251,6406,6250,6406,6405,6251,6252,6407,6251,6407,6406,6252,6253,6408,6252,6408,6407,6253,6254,6409,6253,6409,6408,6254,6255,6410,6254,6410,6409,6255,6256,6411,6255,6411,6410,6256,6257,6412,6256,6412,6411,6257,6258,6413,6257,6413,6412,6258,6259,6414,6258,6414,6413,6259,6260,6415,6259,6415,6414,6260,6261,6416,6260,6416,6415,6261,6262,6417,6261,6417,6416,6262,6263,6418,6262,6418,6417,6263,6264,6419,6263,6419,6418,6264,6265,6420,6264,6420,6419,6265,6266,6421,6265,6421,6420,6266,6267,6422,6266,6422,6421,6267,6268,6423,6267,6423,6422,6268,6269,6424,6268,6424,6423,6269,6270,6425,6269,6425,6424,6270,6271,6426,6270,6426,6425,6271,6272,6427,6271,6427,6426,6272,6273,6428,6272,6428,6427,6273,6274,6429,6273,6429,6428,6274,6275,6430,6274,6430,6429,6275,6276,6431,6275,6431,6430,6276,6277,6432,6276,6432,6431,6277,6278,6433,6277,6433,6432,6278,6279,6434,6278,6434,6433,6279,6280,6435,6279,6435,6434,6280,6281,6436,6280,6436,6435,6281,6282,6437,6281,6437,6436,6282,6283,6438,6282,6438,6437,6283,6284,6439,6283,6439,6438,6284,6285,6440,6284,6440,6439,6285,6286,6441,6285,6441,6440,6286,6287,6442,6286,6442,6441,6287,6288,6443,6287,6443,6442,6288,6289,6444,6288,6444,6443,6289,6290,6445,6289,6445,6444,6290,6291,6446,6290,6446,6445,6291,6292,6447,6291,6447,6446,6292,6293,6448,6292,6448,6447,6293,6294,6449,6293,6449,6448,6294,6295,6450,6294,6450,6449,6295,6296,6451,6295,6451,6450,6296,6297,6452,6296,6452,6451,6297,6298,6453,6297,6453,6452,6298,6299,6454,6298,6454,6453,6299,6300,6455,6299,6455,6454,6300,6301,6456,6300,6456,6455,6301,6302,6457,6301,6457,6456,6302,6303,6458,6302,6458,6457,6303,6304,6459,6303,6459,6458,6304,6305,6460,6304,6460,6459,6305,6306,6461,6305,6461,6460,6306,6307,6462,6306,6462,6461,6307,6308,6463,6307,6463,6462,6308,6309,6464,6308,6464,6463,6309,6310,6465,6309,6465,6464,6310,6311,6466,6310,6466,6465,6311,6312,6467,6311,6467,6466,6312,6313,6468,6312,6468,6467,6313,6314,6469,6313,6469,6468,6314,6315,6470,6314,6470,6469,6315,6316,6471,6315,6471,6470,6316,6317,6472,6316,6472,6471,6317,6318,6473,6317,6473,6472,6318,6319,6474,6318,6474,6473,6319,6320,6475,6319,6475,6474,6320,6321,6476,6320,6476,6475,6321,6322,6477,6321,6477,6476,6322,6323,6478,6322,6478,6477,6323,6324,6479,6323,6479,6478,6324,6325,6480,6324,6480,6479,6325,6326,6481,6325,6481,6480,6326,6327,6482,6326,6482,6481,6327,6328,6483,6327,6483,6482,6328,6329,6484,6328,6484,6483,6329,6330,6485,6329,6485,6484,6330,6331,6486,6330,6486,6485,6331,6332,6487,6331,6487,6486,6332,6333,6488,6332,6488,6487,6333,6334,6489,6333,6489,6488,6334,6335,6490,6334,6490,6489,6335,6336,6491,6335,6491,6490,6336,6337,6492,6336,6492,6491,6337,6338,6493,6337,6493,6492,6338,6339,6494,6338,6494,6493,6339,6340,6495,6339,6495,6494,6340,6341,6496,6340,6496,6495,6341,6342,6497,6341,6497,6496,6342,6343,6498,6342,6498,6497,6343,6344,6499,6343,6499,6498,6344,6345,6500,6344,6500,6499,6345,6346,6501,6345,6501,6500,6346,6347,6502,6346,6502,6501,6347,6348,6503,6347,6503,6502,6348,6349,6504,6348,6504,6503,6349,6350,6505,6349,6505,6504,6350,6351,6506,6350,6506,6505,6351,6352,6507,6351,6507,6506,6352,6353,6508,6352,6508,6507,6353,6354,6509,6353,6509,6508,6355,6356,6511,6355,6511,6510,6356,6357,6512,6356,6512,6511,6357,6358,6513,6357,6513,6512,6358,6359,6514,6358,6514,6513,6359,6360,6515,6359,6515,6514,6360,6361,6516,6360,6516,6515,6361,6362,6517,6361,6517,6516,6362,6363,6518,6362,6518,6517,6363,6364,6519,6363,6519,6518,6364,6365,6520,6364,6520,6519,6365,6366,6521,6365,6521,6520,6366,6367,6522,6366,6522,6521,6367,6368,6523,6367,6523,6522,6368,6369,6524,6368,6524,6523,6369,6370,6525,6369,6525,6524,6370,6371,6526,6370,6526,6525,6371,6372,6527,6371,6527,6526,6372,6373,6528,6372,6528,6527,6373,6374,6529,6373,6529,6528,6374,6375,6530,6374,6530,6529,6375,6376,6531,6375,6531,6530,6376,6377,6532,6376,6532,6531,6377,6378,6533,6377,6533,6532,6378,6379,6534,6378,6534,6533,6379,6380,6535,6379,6535,6534,6380,6381,6536,6380,6536,6535,6381,6382,6537,6381,6537,6536,6382,6383,6538,6382,6538,6537,6383,6384,6539,6383,6539,6538,6384,6385,6540,6384,6540,6539,6385,6386,6541,6385,6541,6540,6386,6387,6542,6386,6542,6541,6387,6388,6543,6387,6543,6542,6388,6389,6544,6388,6544,6543,6389,6390,6545,6389,6545,6544,6390,6391,6546,6390,6546,6545,6391,6392,6547,6391,6547,6546,6392,6393,6548,6392,6548,6547,6393,6394,6549,6393,6549,6548,6394,6395,6550,6394,6550,6549,6395,6396,6551,6395,6551,6550,6396,6397,6552,6396,6552,6551,6397,6398,6553,6397,6553,6552,6398,6399,6554,6398,6554,6553,6399,6400,6555,6399,6555,6554,6400,6401,6556,6400,6556,6555,6401,6402,6557,6401,6557,6556,6402,6403,6558,6402,6558,6557,6403,6404,6559,6403,6559,6558,6404,6405,6560,6404,6560,6559,6405,6406,6561,6405,6561,6560,6406,6407,6562,6406,6562,6561,6407,6408,6563,6407,6563,6562,6408,6409,6564,6408,6564,6563,6409,6410,6565,6409,6565,6564,6410,6411,6566,6410,6566,6565,6411,6412,6567,6411,6567,6566,6412,6413,6568,6412,6568,6567,6413,6414,6569,6413,6569,6568,6414,6415,6570,6414,6570,6569,6415,6416,6571,6415,6571,6570,6416,6417,6572,6416,6572,6571,6417,6418,6573,6417,6573,6572,6418,6419,6574,6418,6574,6573,6419,6420,6575,6419,6575,6574,6420,6421,6576,6420,6576,6575,6421,6422,6577,6421,6577,6576,6422,6423,6578,6422,6578,6577,6423,6424,6579,6423,6579,6578,6424,6425,6580,6424,6580,6579,6425,6426,6581,6425,6581,6580,6426,6427,6582,6426,6582,6581,6427,6428,6583,6427,6583,6582,6428,6429,6584,6428,6584,6583,6429,6430,6585,6429,6585,6584,6430,6431,6586,6430,6586,6585,6431,6432,6587,6431,6587,6586,6432,6433,6588,6432,6588,6587,6433,6434,6589,6433,6589,6588,6434,6435,6590,6434,6590,6589,6435,6436,6591,6435,6591,6590,6436,6437,6592,6436,6592,6591,6437,6438,6593,6437,6593,6592,6438,6439,6594,6438,6594,6593,6439,6440,6595,6439,6595,6594,6440,6441,6596,6440,6596,6595,6441,6442,6597,6441,6597,6596,6442,6443,6598,6442,6598,6597,6443,6444,6599,6443,6599,6598,6444,6445,6600,6444,6600,6599,6445,6446,6601,6445,6601,6600,6446,6447,6602,6446,6602,6601,6447,6448,6603,6447,6603,6602,6448,6449,6604,6448,6604,6603,6449,6450,6605,6449,6605,6604,6450,6451,6606,6450,6606,6605,6451,6452,6607,6451,6607,6606,6452,6453,6608,6452,6608,6607,6453,6454,6609,6453,6609,6608,6454,6455,6610,6454,6610,6609,6455,6456,6611,6455,6611,6610,6456,6457,6612,6456,6612,6611,6457,6458,6613,6457,6613,6612,6458,6459,6614,6458,6614,6613,6459,6460,6615,6459,6615,6614,6460,6461,6616,6460,6616,6615,6461,6462,6617,6461,6617,6616,6462,6463,6618,6462,6618,6617,6463,6464,6619,6463,6619,6618,6464,6465,6620,6464,6620,6619,6465,6466,6621,6465,6621,6620,6466,6467,6622,6466,6622,6621,6467,6468,6623,6467,6623,6622,6468,6469,6624,6468,6624,6623,6469,6470,6625,6469,6625,6624,6470,6471,6626,6470,6626,6625,6471,6472,6627,6471,6627,6626,6472,6473,6628,6472,6628,6627,6473,6474,6629,6473,6629,6628,6474,6475,6630,6474,6630,6629,6475,6476,6631,6475,6631,6630,6476,6477,6632,6476,6632,6631,6477,6478,6633,6477,6633,6632,6478,6479,6634,6478,6634,6633,6479,6480,6635,6479,6635,6634,6480,6481,6636,6480,6636,6635,6481,6482,6637,6481,6637,6636,6482,6483,6638,6482,6638,6637,6483,6484,6639,6483,6639,6638,6484,6485,6640,6484,6640,6639,6485,6486,6641,6485,6641,6640,6486,6487,6642,6486,6642,6641,6487,6488,6643,6487,6643,6642,6488,6489,6644,6488,6644,6643,6489,6490,6645,6489,6645,6644,6490,6491,6646,6490,6646,6645,6491,6492,6647,6491,6647,6646,6492,6493,6648,6492,6648,6647,6493,6494,6649,6493,6649,6648,6494,6495,6650,6494,6650,6649,6495,6496,6651,6495,6651,6650,6496,6497,6652,6496,6652,6651,6497,6498,6653,6497,6653,6652,6498,6499,6654,6498,6654,6653,6499,6500,6655,6499,6655,6654,6500,6501,6656,6500,6656,6655,6501,6502,6657,6501,6657,6656,6502,6503,6658,6502,6658,6657,6503,6504,6659,6503,6659,6658,6504,6505,6660,6504,6660,6659,6505,6506,6661,6505,6661,6660,6506,6507,6662,6506,6662,6661,6507,6508,6663,6507,6663,6662,6508,6509,6664,6508,6664,6663,6510,6511,6666,6510,6666,6665,6511,6512,6667,6511,6667,6666,6512,6513,6668,6512,6668,6667,6513,6514,6669,6513,6669,6668,6514,6515,6670,6514,6670,6669,6515,6516,6671,6515,6671,6670,6516,6517,6672,6516,6672,6671,6517,6518,6673,6517,6673,6672,6518,6519,6674,6518,6674,6673,6519,6520,6675,6519,6675,6674,6520,6521,6676,6520,6676,6675,6521,6522,6677,6521,6677,6676,6522,6523,6678,6522,6678,6677,6523,6524,6679,6523,6679,6678,6524,6525,6680,6524,6680,6679,6525,6526,6681,6525,6681,6680,6526,6527,6682,6526,6682,6681,6527,6528,6683,6527,6683,6682,6528,6529,6684,6528,6684,6683,6529,6530,6685,6529,6685,6684,6530,6531,6686,6530,6686,6685,6531,6532,6687,6531,6687,6686,6532,6533,6688,6532,6688,6687,6533,6534,6689,6533,6689,6688,6534,6535,6690,6534,6690,6689,6535,6536,6691,6535,6691,6690,6536,6537,6692,6536,6692,6691,6537,6538,6693,6537,6693,6692,6538,6539,6694,6538,6694,6693,6539,6540,6695,6539,6695,6694,6540,6541,6696,6540,6696,6695,6541,6542,6697,6541,6697,6696,6542,6543,6698,6542,6698,6697,6543,6544,6699,6543,6699,6698,6544,6545,6700,6544,6700,6699,6545,6546,6701,6545,6701,6700,6546,6547,6702,6546,6702,6701,6547,6548,6703,6547,6703,6702,6548,6549,6704,6548,6704,6703,6549,6550,6705,6549,6705,6704,6550,6551,6706,6550,6706,6705,6551,6552,6707,6551,6707,6706,6552,6553,6708,6552,6708,6707,6553,6554,6709,6553,6709,6708,6554,6555,6710,6554,6710,6709,6555,6556,6711,6555,6711,6710,6556,6557,6712,6556,6712,6711,6557,6558,6713,6557,6713,6712,6558,6559,6714,6558,6714,6713,6559,6560,6715,6559,6715,6714,6560,6561,6716,6560,6716,6715,6561,6562,6717,6561,6717,6716,6562,6563,6718,6562,6718,6717,6563,6564,6719,6563,6719,6718,6564,6565,6720,6564,6720,6719,6565,6566,6721,6565,6721,6720,6566,6567,6722,6566,6722,6721,6567,6568,6723,6567,6723,6722,6568,6569,6724,6568,6724,6723,6569,6570,6725,6569,6725,6724,6570,6571,6726,6570,6726,6725,6571,6572,6727,6571,6727,6726,6572,6573,6728,6572,6728,6727,6573,6574,6729,6573,6729,6728,6574,6575,6730,6574,6730,6729,6575,6576,6731,6575,6731,6730,6576,6577,6732,6576,6732,6731,6577,6578,6733,6577,6733,6732,6578,6579,6734,6578,6734,6733,6579,6580,6735,6579,6735,6734,6580,6581,6736,6580,6736,6735,6581,6582,6737,6581,6737,6736,6582,6583,6738,6582,6738,6737,6583,6584,6739,6583,6739,6738,6584,6585,6740,6584,6740,6739,6585,6586,6741,6585,6741,6740,6586,6587,6742,6586,6742,6741,6587,6588,6743,6587,6743,6742,6588,6589,6744,6588,6744,6743,6589,6590,6745,6589,6745,6744,6590,6591,6746,6590,6746,6745,6591,6592,6747,6591,6747,6746,6592,6593,6748,6592,6748,6747,6593,6594,6749,6593,6749,6748,6594,6595,6750,6594,6750,6749,6595,6596,6751,6595,6751,6750,6596,6597,6752,6596,6752,6751,6597,6598,6753,6597,6753,6752,6598,6599,6754,6598,6754,6753,6599,6600,6755,6599,6755,6754,6600,6601,6756,6600,6756,6755,6601,6602,6757,6601,6757,6756,6602,6603,6758,6602,6758,6757,6603,6604,6759,6603,6759,6758,6604,6605,6760,6604,6760,6759,6605,6606,6761,6605,6761,6760,6606,6607,6762,6606,6762,6761,6607,6608,6763,6607,6763,6762,6608,6609,6764,6608,6764,6763,6609,6610,6765,6609,6765,6764,6610,6611,6766,6610,6766,6765,6611,6612,6767,6611,6767,6766,6612,6613,6768,6612,6768,6767,6613,6614,6769,6613,6769,6768,6614,6615,6770,6614,6770,6769,6615,6616,6771,6615,6771,6770,6616,6617,6772,6616,6772,6771,6617,6618,6773,6617,6773,6772,6618,6619,6774,6618,6774,6773,6619,6620,6775,6619,6775,6774,6620,6621,6776,6620,6776,6775,6621,6622,6777,6621,6777,6776,6622,6623,6778,6622,6778,6777,6623,6624,6779,6623,6779,6778,6624,6625,6780,6624,6780,6779,6625,6626,6781,6625,6781,6780,6626,6627,6782,6626,6782,6781,6627,6628,6783,6627,6783,6782,6628,6629,6784,6628,6784,6783,6629,6630,6785,6629,6785,6784,6630,6631,6786,6630,6786,6785,6631,6632,6787,6631,6787,6786,6632,6633,6788,6632,6788,6787,6633,6634,6789,6633,6789,6788,6634,6635,6790,6634,6790,6789,6635,6636,6791,6635,6791,6790,6636,6637,6792,6636,6792,6791,6637,6638,6793,6637,6793,6792,6638,6639,6794,6638,6794,6793,6639,6640,6795,6639,6795,6794,6640,6641,6796,6640,6796,6795,6641,6642,6797,6641,6797,6796,6642,6643,6798,6642,6798,6797,6643,6644,6799,6643,6799,6798,6644,6645,6800,6644,6800,6799,6645,6646,6801,6645,6801,6800,6646,6647,6802,6646,6802,6801,6647,6648,6803,6647,6803,6802,6648,6649,6804,6648,6804,6803,6649,6650,6805,6649,6805,6804,6650,6651,6806,6650,6806,6805,6651,6652,6807,6651,6807,6806,6652,6653,6808,6652,6808,6807,6653,6654,6809,6653,6809,6808,6654,6655,6810,6654,6810,6809,6655,6656,6811,6655,6811,6810,6656,6657,6812,6656,6812,6811,6657,6658,6813,6657,6813,6812,6658,6659,6814,6658,6814,6813,6659,6660,6815,6659,6815,6814,6660,6661,6816,6660,6816,6815,6661,6662,6817,6661,6817,6816,6662,6663,6818,6662,6818,6817,6663,6664,6819,6663,6819,6818,6665,6666,6821,6665,6821,6820,6666,6667,6822,6666,6822,6821,6667,6668,6823,6667,6823,6822,6668,6669,6824,6668,6824,6823,6669,6670,6825,6669,6825,6824,6670,6671,6826,6670,6826,6825,6671,6672,6827,6671,6827,6826,6672,6673,6828,6672,6828,6827,6673,6674,6829,6673,6829,6828,6674,6675,6830,6674,6830,6829,6675,6676,6831,6675,6831,6830,6676,6677,6832,6676,6832,6831,6677,6678,6833,6677,6833,6832,6678,6679,6834,6678,6834,6833,6679,6680,6835,6679,6835,6834,6680,6681,6836,6680,6836,6835,6681,6682,6837,6681,6837,6836,6682,6683,6838,6682,6838,6837,6683,6684,6839,6683,6839,6838,6684,6685,6840,6684,6840,6839,6685,6686,6841,6685,6841,6840,6686,6687,6842,6686,6842,6841,6687,6688,6843,6687,6843,6842,6688,6689,6844,6688,6844,6843,6689,6690,6845,6689,6845,6844,6690,6691,6846,6690,6846,6845,6691,6692,6847,6691,6847,6846,6692,6693,6848,6692,6848,6847,6693,6694,6849,6693,6849,6848,6694,6695,6850,6694,6850,6849,6695,6696,6851,6695,6851,6850,6696,6697,6852,6696,6852,6851,6697,6698,6853,6697,6853,6852,6698,6699,6854,6698,6854,6853,6699,6700,6855,6699,6855,6854,6700,6701,6856,6700,6856,6855,6701,6702,6857,6701,6857,6856,6702,6703,6858,6702,6858,6857,6703,6704,6859,6703,6859,6858,6704,6705,6860,6704,6860,6859,6705,6706,6861,6705,6861,6860,6706,6707,6862,6706,6862,6861,6707,6708,6863,6707,6863,6862,6708,6709,6864,6708,6864,6863,6709,6710,6865,6709,6865,6864,6710,6711,6866,6710,6866,6865,6711,6712,6867,6711,6867,6866,6712,6713,6868,6712,6868,6867,6713,6714,6869,6713,6869,6868,6714,6715,6870,6714,6870,6869,6715,6716,6871,6715,6871,6870,6716,6717,6872,6716,6872,6871,6717,6718,6873,6717,6873,6872,6718,6719,6874,6718,6874,6873,6719,6720,6875,6719,6875,6874,6720,6721,6876,6720,6876,6875,6721,6722,6877,6721,6877,6876,6722,6723,6878,6722,6878,6877,6723,6724,6879,6723,6879,6878,6724,6725,6880,6724,6880,6879,6725,6726,6881,6725,6881,6880,6726,6727,6882,6726,6882,6881,6727,6728,6883,6727,6883,6882,6728,6729,6884,6728,6884,6883,6729,6730,6885,6729,6885,6884,6730,6731,6886,6730,6886,6885,6731,6732,6887,6731,6887,6886,6732,6733,6888,6732,6888,6887,6733,6734,6889,6733,6889,6888,6734,6735,6890,6734,6890,6889,6735,6736,6891,6735,6891,6890,6736,6737,6892,6736,6892,6891,6737,6738,6893,6737,6893,6892,6738,6739,6894,6738,6894,6893,6739,6740,6895,6739,6895,6894,6740,6741,6896,6740,6896,6895,6741,6742,6897,6741,6897,6896,6742,6743,6898,6742,6898,6897,6743,6744,6899,6743,6899,6898,6744,6745,6900,6744,6900,6899,6745,6746,6901,6745,6901,6900,6746,6747,6902,6746,6902,6901,6747,6748,6903,6747,6903,6902,6748,6749,6904,6748,6904,6903,6749,6750,6905,6749,6905,6904,6750,6751,6906,6750,6906,6905,6751,6752,6907,6751,6907,6906,6752,6753,6908,6752,6908,6907,6753,6754,6909,6753,6909,6908,6754,6755,6910,6754,6910,6909,6755,6756,6911,6755,6911,6910,6756,6757,6912,6756,6912,6911,6757,6758,6913,6757,6913,6912,6758,6759,6914,6758,6914,6913,6759,6760,6915,6759,6915,6914,6760,6761,6916,6760,6916,6915,6761,6762,6917,6761,6917,6916,6762,6763,6918,6762,6918,6917,6763,6764,6919,6763,6919,6918,6764,6765,6920,6764,6920,6919,6765,6766,6921,6765,6921,6920,6766,6767,6922,6766,6922,6921,6767,6768,6923,6767,6923,6922,6768,6769,6924,6768,6924,6923,6769,6770,6925,6769,6925,6924,6770,6771,6926,6770,6926,6925,6771,6772,6927,6771,6927,6926,6772,6773,6928,6772,6928,6927,6773,6774,6929,6773,6929,6928,6774,6775,6930,6774,6930,6929,6775,6776,6931,6775,6931,6930,6776,6777,6932,6776,6932,6931,6777,6778,6933,6777,6933,6932,6778,6779,6934,6778,6934,6933,6779,6780,6935,6779,6935,6934,6780,6781,6936,6780,6936,6935,6781,6782,6937,6781,6937,6936,6782,6783,6938,6782,6938,6937,6783,6784,6939,6783,6939,6938,6784,6785,6940,6784,6940,6939,6785,6786,6941,6785,6941,6940,6786,6787,6942,6786,6942,6941,6787,6788,6943,6787,6943,6942,6788,6789,6944,6788,6944,6943,6789,6790,6945,6789,6945,6944,6790,6791,6946,6790,6946,6945,6791,6792,6947,6791,6947,6946,6792,6793,6948,6792,6948,6947,6793,6794,6949,6793,6949,6948,6794,6795,6950,6794,6950,6949,6795,6796,6951,6795,6951,6950,6796,6797,6952,6796,6952,6951,6797,6798,6953,6797,6953,6952,6798,6799,6954,6798,6954,6953,6799,6800,6955,6799,6955,6954,6800,6801,6956,6800,6956,6955,6801,6802,6957,6801,6957,6956,6802,6803,6958,6802,6958,6957,6803,6804,6959,6803,6959,6958,6804,6805,6960,6804,6960,6959,6805,6806,6961,6805,6961,6960,6806,6807,6962,6806,6962,6961,6807,6808,6963,6807,6963,6962,6808,6809,6964,6808,6964,6963,6809,6810,6965,6809,6965,6964,6810,6811,6966,6810,6966,6965,6811,6812,6967,6811,6967,6966,6812,6813,6968,6812,6968,6967,6813,6814,6969,6813,6969,6968,6814,6815,6970,6814,6970,6969,6815,6816,6971,6815,6971,6970,6816,6817,6972,6816,6972,6971,6817,6818,6973,6817,6973,6972,6818,6819,6974,6818,6974,6973,6820,6821,6976,6820,6976,6975,6821,6822,6977,6821,6977,6976,6822,6823,6978,6822,6978,6977,6823,6824,6979,6823,6979,6978,6824,6825,6980,6824,6980,6979,6825,6826,6981,6825,6981,6980,6826,6827,6982,6826,6982,6981,6827,6828,6983,6827,6983,6982,6828,6829,6984,6828,6984,6983,6829,6830,6985,6829,6985,6984,6830,6831,6986,6830,6986,6985,6831,6832,6987,6831,6987,6986,6832,6833,6988,6832,6988,6987,6833,6834,6989,6833,6989,6988,6834,6835,6990,6834,6990,6989,6835,6836,6991,6835,6991,6990,6836,6837,6992,6836,6992,6991,6837,6838,6993,6837,6993,6992,6838,6839,6994,6838,6994,6993,6839,6840,6995,6839,6995,6994,6840,6841,6996,6840,6996,6995,6841,6842,6997,6841,6997,6996,6842,6843,6998,6842,6998,6997,6843,6844,6999,6843,6999,6998,6844,6845,7000,6844,7000,6999,6845,6846,7001,6845,7001,7000,6846,6847,7002,6846,7002,7001,6847,6848,7003,6847,7003,7002,6848,6849,7004,6848,7004,7003,6849,6850,7005,6849,7005,7004,6850,6851,7006,6850,7006,7005,6851,6852,7007,6851,7007,7006,6852,6853,7008,6852,7008,7007,6853,6854,7009,6853,7009,7008,6854,6855,7010,6854,7010,7009,6855,6856,7011,6855,7011,7010,6856,6857,7012,6856,7012,7011,6857,6858,7013,6857,7013,7012,6858,6859,7014,6858,7014,7013,6859,6860,7015,6859,7015,7014,6860,6861,7016,6860,7016,7015,6861,6862,7017,6861,7017,7016,6862,6863,7018,6862,7018,7017,6863,6864,7019,6863,7019,7018,6864,6865,7020,6864,7020,7019,6865,6866,7021,6865,7021,7020,6866,6867,7022,6866,7022,7021,6867,6868,7023,6867,7023,7022,6868,6869,7024,6868,7024,7023,6869,6870,7025,6869,7025,7024,6870,6871,7026,6870,7026,7025,6871,6872,7027,6871,7027,7026,6872,6873,7028,6872,7028,7027,6873,6874,7029,6873,7029,7028,6874,6875,7030,6874,7030,7029,6875,6876,7031,6875,7031,7030,6876,6877,7032,6876,7032,7031,6877,6878,7033,6877,7033,7032,6878,6879,7034,6878,7034,7033,6879,6880,7035,6879,7035,7034,6880,6881,7036,6880,7036,7035,6881,6882,7037,6881,7037,7036,6882,6883,7038,6882,7038,7037,6883,6884,7039,6883,7039,7038,6884,6885,7040,6884,7040,7039,6885,6886,7041,6885,7041,7040,6886,6887,7042,6886,7042,7041,6887,6888,7043,6887,7043,7042,6888,6889,7044,6888,7044,7043,6889,6890,7045,6889,7045,7044,6890,6891,7046,6890,7046,7045,6891,6892,7047,6891,7047,7046,6892,6893,7048,6892,7048,7047,6893,6894,7049,6893,7049,7048,6894,6895,7050,6894,7050,7049,6895,6896,7051,6895,7051,7050,6896,6897,7052,6896,7052,7051,6897,6898,7053,6897,7053,7052,6898,6899,7054,6898,7054,7053,6899,6900,7055,6899,7055,7054,6900,6901,7056,6900,7056,7055,6901,6902,7057,6901,7057,7056,6902,6903,7058,6902,7058,7057,6903,6904,7059,6903,7059,7058,6904,6905,7060,6904,7060,7059,6905,6906,7061,6905,7061,7060,6906,6907,7062,6906,7062,7061,6907,6908,7063,6907,7063,7062,6908,6909,7064,6908,7064,7063,6909,6910,7065,6909,7065,7064,6910,6911,7066,6910,7066,7065,6911,6912,7067,6911,7067,7066,6912,6913,7068,6912,7068,7067,6913,6914,7069,6913,7069,7068,6914,6915,7070,6914,7070,7069,6915,6916,7071,6915,7071,7070,6916,6917,7072,6916,7072,7071,6917,6918,7073,6917,7073,7072,6918,6919,7074,6918,7074,7073,6919,6920,7075,6919,7075,7074,6920,6921,7076,6920,7076,7075,6921,6922,7077,6921,7077,7076,6922,6923,7078,6922,7078,7077,6923,6924,7079,6923,7079,7078,6924,6925,7080,6924,7080,7079,6925,6926,7081,6925,7081,7080,6926,6927,7082,6926,7082,7081,6927,6928,7083,6927,7083,7082,6928,6929,7084,6928,7084,7083,6929,6930,7085,6929,7085,7084,6930,6931,7086,6930,7086,7085,6931,6932,7087,6931,7087,7086,6932,6933,7088,6932,7088,7087,6933,6934,7089,6933,7089,7088,6934,6935,7090,6934,7090,7089,6935,6936,7091,6935,7091,7090,6936,6937,7092,6936,7092,7091,6937,6938,7093,6937,7093,7092,6938,6939,7094,6938,7094,7093,6939,6940,7095,6939,7095,7094,6940,6941,7096,6940,7096,7095,6941,6942,7097,6941,7097,7096,6942,6943,7098,6942,7098,7097,6943,6944,7099,6943,7099,7098,6944,6945,7100,6944,7100,7099,6945,6946,7101,6945,7101,7100,6946,6947,7102,6946,7102,7101,6947,6948,7103,6947,7103,7102,6948,6949,7104,6948,7104,7103,6949,6950,7105,6949,7105,7104,6950,6951,7106,6950,7106,7105,6951,6952,7107,6951,7107,7106,6952,6953,7108,6952,7108,7107,6953,6954,7109,6953,7109,7108,6954,6955,7110,6954,7110,7109,6955,6956,7111,6955,7111,7110,6956,6957,7112,6956,7112,7111,6957,6958,7113,6957,7113,7112,6958,6959,7114,6958,7114,7113,6959,6960,7115,6959,7115,7114,6960,6961,7116,6960,7116,7115,6961,6962,7117,6961,7117,7116,6962,6963,7118,6962,7118,7117,6963,6964,7119,6963,7119,7118,6964,6965,7120,6964,7120,7119,6965,6966,7121,6965,7121,7120,6966,6967,7122,6966,7122,7121,6967,6968,7123,6967,7123,7122,6968,6969,7124,6968,7124,7123,6969,6970,7125,6969,7125,7124,6970,6971,7126,6970,7126,7125,6971,6972,7127,6971,7127,7126,6972,6973,7128,6972,7128,7127,6973,6974,7129,6973,7129,7128,6975,6976,7131,6975,7131,7130,6976,6977,7132,6976,7132,7131,6977,6978,7133,6977,7133,7132,6978,6979,7134,6978,7134,7133,6979,6980,7135,6979,7135,7134,6980,6981,7136,6980,7136,7135,6981,6982,7137,6981,7137,7136,6982,6983,7138,6982,7138,7137,6983,6984,7139,6983,7139,7138,6984,6985,7140,6984,7140,7139,6985,6986,7141,6985,7141,7140,6986,6987,7142,6986,7142,7141,6987,6988,7143,6987,7143,7142,6988,6989,7144,6988,7144,7143,6989,6990,7145,6989,7145,7144,6990,6991,7146,6990,7146,7145,6991,6992,7147,6991,7147,7146,6992,6993,7148,6992,7148,7147,6993,6994,7149,6993,7149,7148,6994,6995,7150,6994,7150,7149,6995,6996,7151,6995,7151,7150,6996,6997,7152,6996,7152,7151,6997,6998,7153,6997,7153,7152,6998,6999,7154,6998,7154,7153,6999,7000,7155,6999,7155,7154,7000,7001,7156,7000,7156,7155,7001,7002,7157,7001,7157,7156,7002,7003,7158,7002,7158,7157,7003,7004,7159,7003,7159,7158,7004,7005,7160,7004,7160,7159,7005,7006,7161,7005,7161,7160,7006,7007,7162,7006,7162,7161,7007,7008,7163,7007,7163,7162,7008,7009,7164,7008,7164,7163,7009,7010,7165,7009,7165,7164,7010,7011,7166,7010,7166,7165,7011,7012,7167,7011,7167,7166,7012,7013,7168,7012,7168,7167,7013,7014,7169,7013,7169,7168,7014,7015,7170,7014,7170,7169,7015,7016,7171,7015,7171,7170,7016,7017,7172,7016,7172,7171,7017,7018,7173,7017,7173,7172,7018,7019,7174,7018,7174,7173,7019,7020,7175,7019,7175,7174,7020,7021,7176,7020,7176,7175,7021,7022,7177,7021,7177,7176,7022,7023,7178,7022,7178,7177,7023,7024,7179,7023,7179,7178,7024,7025,7180,7024,7180,7179,7025,7026,7181,7025,7181,7180,7026,7027,7182,7026,7182,7181,7027,7028,7183,7027,7183,7182,7028,7029,7184,7028,7184,7183,7029,7030,7185,7029,7185,7184,7030,7031,7186,7030,7186,7185,7031,7032,7187,7031,7187,7186,7032,7033,7188,7032,7188,7187,7033,7034,7189,7033,7189,7188,7034,7035,7190,7034,7190,7189,7035,7036,7191,7035,7191,7190,7036,7037,7192,7036,7192,7191,7037,7038,7193,7037,7193,7192,7038,7039,7194,7038,7194,7193,7039,7040,7195,7039,7195,7194,7040,7041,7196,7040,7196,7195,7041,7042,7197,7041,7197,7196,7042,7043,7198,7042,7198,7197,7043,7044,7199,7043,7199,7198,7044,7045,7200,7044,7200,7199,7045,7046,7201,7045,7201,7200,7046,7047,7202,7046,7202,7201,7047,7048,7203,7047,7203,7202,7048,7049,7204,7048,7204,7203,7049,7050,7205,7049,7205,7204,7050,7051,7206,7050,7206,7205,7051,7052,7207,7051,7207,7206,7052,7053,7208,7052,7208,7207,7053,7054,7209,7053,7209,7208,7054,7055,7210,7054,7210,7209,7055,7056,7211,7055,7211,7210,7056,7057,7212,7056,7212,7211,7057,7058,7213,7057,7213,7212,7058,7059,7214,7058,7214,7213,7059,7060,7215,7059,7215,7214,7060,7061,7216,7060,7216,7215,7061,7062,7217,7061,7217,7216,7062,7063,7218,7062,7218,7217,7063,7064,7219,7063,7219,7218,7064,7065,7220,7064,7220,7219,7065,7066,7221,7065,7221,7220,7066,7067,7222,7066,7222,7221,7067,7068,7223,7067,7223,7222,7068,7069,7224,7068,7224,7223,7069,7070,7225,7069,7225,7224,7070,7071,7226,7070,7226,7225,7071,7072,7227,7071,7227,7226,7072,7073,7228,7072,7228,7227,7073,7074,7229,7073,7229,7228,7074,7075,7230,7074,7230,7229,7075,7076,7231,7075,7231,7230,7076,7077,7232,7076,7232,7231,7077,7078,7233,7077,7233,7232,7078,7079,7234,7078,7234,7233,7079,7080,7235,7079,7235,7234,7080,7081,7236,7080,7236,7235,7081,7082,7237,7081,7237,7236,7082,7083,7238,7082,7238,7237,7083,7084,7239,7083,7239,7238,7084,7085,7240,7084,7240,7239,7085,7086,7241,7085,7241,7240,7086,7087,7242,7086,7242,7241,7087,7088,7243,7087,7243,7242,7088,7089,7244,7088,7244,7243,7089,7090,7245,7089,7245,7244,7090,7091,7246,7090,7246,7245,7091,7092,7247,7091,7247,7246,7092,7093,7248,7092,7248,7247,7093,7094,7249,7093,7249,7248,7094,7095,7250,7094,7250,7249,7095,7096,7251,7095,7251,7250,7096,7097,7252,7096,7252,7251,7097,7098,7253,7097,7253,7252,7098,7099,7254,7098,7254,7253,7099,7100,7255,7099,7255,7254,7100,7101,7256,7100,7256,7255,7101,7102,7257,7101,7257,7256,7102,7103,7258,7102,7258,7257,7103,7104,7259,7103,7259,7258,7104,7105,7260,7104,7260,7259,7105,7106,7261,7105,7261,7260,7106,7107,7262,7106,7262,7261,7107,7108,7263,7107,7263,7262,7108,7109,7264,7108,7264,7263,7109,7110,7265,7109,7265,7264,7110,7111,7266,7110,7266,7265,7111,7112,7267,7111,7267,7266,7112,7113,7268,7112,7268,7267,7113,7114,7269,7113,7269,7268,7114,7115,7270,7114,7270,7269,7115,7116,7271,7115,7271,7270,7116,7117,7272,7116,7272,7271,7117,7118,7273,7117,7273,7272,7118,7119,7274,7118,7274,7273,7119,7120,7275,7119,7275,7274,7120,7121,7276,7120,7276,7275,7121,7122,7277,7121,7277,7276,7122,7123,7278,7122,7278,7277,7123,7124,7279,7123,7279,7278,7124,7125,7280,7124,7280,7279,7125,7126,7281,7125,7281,7280,7126,7127,7282,7126,7282,7281,7127,7128,7283,7127,7283,7282,7128,7129,7284,7128,7284,7283,7130,7131,7286,7130,7286,7285,7131,7132,7287,7131,7287,7286,7132,7133,7288,7132,7288,7287,7133,7134,7289,7133,7289,7288,7134,7135,7290,7134,7290,7289,7135,7136,7291,7135,7291,7290,7136,7137,7292,7136,7292,7291,7137,7138,7293,7137,7293,7292,7138,7139,7294,7138,7294,7293,7139,7140,7295,7139,7295,7294,7140,7141,7296,7140,7296,7295,7141,7142,7297,7141,7297,7296,7142,7143,7298,7142,7298,7297,7143,7144,7299,7143,7299,7298,7144,7145,7300,7144,7300,7299,7145,7146,7301,7145,7301,7300,7146,7147,7302,7146,7302,7301,7147,7148,7303,7147,7303,7302,7148,7149,7304,7148,7304,7303,7149,7150,7305,7149,7305,7304,7150,7151,7306,7150,7306,7305,7151,7152,7307,7151,7307,7306,7152,7153,7308,7152,7308,7307,7153,7154,7309,7153,7309,7308,7154,7155,7310,7154,7310,7309,7155,7156,7311,7155,7311,7310,7156,7157,7312,7156,7312,7311,7157,7158,7313,7157,7313,7312,7158,7159,7314,7158,7314,7313,7159,7160,7315,7159,7315,7314,7160,7161,7316,7160,7316,7315,7161,7162,7317,7161,7317,7316,7162,7163,7318,7162,7318,7317,7163,7164,7319,7163,7319,7318,7164,7165,7320,7164,7320,7319,7165,7166,7321,7165,7321,7320,7166,7167,7322,7166,7322,7321,7167,7168,7323,7167,7323,7322,7168,7169,7324,7168,7324,7323,7169,7170,7325,7169,7325,7324,7170,7171,7326,7170,7326,7325,7171,7172,7327,7171,7327,7326,7172,7173,7328,7172,7328,7327,7173,7174,7329,7173,7329,7328,7174,7175,7330,7174,7330,7329,7175,7176,7331,7175,7331,7330,7176,7177,7332,7176,7332,7331,7177,7178,7333,7177,7333,7332,7178,7179,7334,7178,7334,7333,7179,7180,7335,7179,7335,7334,7180,7181,7336,7180,7336,7335,7181,7182,7337,7181,7337,7336,7182,7183,7338,7182,7338,7337,7183,7184,7339,7183,7339,7338,7184,7185,7340,7184,7340,7339,7185,7186,7341,7185,7341,7340,7186,7187,7342,7186,7342,7341,7187,7188,7343,7187,7343,7342,7188,7189,7344,7188,7344,7343,7189,7190,7345,7189,7345,7344,7190,7191,7346,7190,7346,7345,7191,7192,7347,7191,7347,7346,7192,7193,7348,7192,7348,7347,7193,7194,7349,7193,7349,7348,7194,7195,7350,7194,7350,7349,7195,7196,7351,7195,7351,7350,7196,7197,7352,7196,7352,7351,7197,7198,7353,7197,7353,7352,7198,7199,7354,7198,7354,7353,7199,7200,7355,7199,7355,7354,7200,7201,7356,7200,7356,7355,7201,7202,7357,7201,7357,7356,7202,7203,7358,7202,7358,7357,7203,7204,7359,7203,7359,7358,7204,7205,7360,7204,7360,7359,7205,7206,7361,7205,7361,7360,7206,7207,7362,7206,7362,7361,7207,7208,7363,7207,7363,7362,7208,7209,7364,7208,7364,7363,7209,7210,7365,7209,7365,7364,7210,7211,7366,7210,7366,7365,7211,7212,7367,7211,7367,7366,7212,7213,7368,7212,7368,7367,7213,7214,7369,7213,7369,7368,7214,7215,7370,7214,7370,7369,7215,7216,7371,7215,7371,7370,7216,7217,7372,7216,7372,7371,7217,7218,7373,7217,7373,7372,7218,7219,7374,7218,7374,7373,7219,7220,7375,7219,7375,7374,7220,7221,7376,7220,7376,7375,7221,7222,7377,7221,7377,7376,7222,7223,7378,7222,7378,7377,7223,7224,7379,7223,7379,7378,7224,7225,7380,7224,7380,7379,7225,7226,7381,7225,7381,7380,7226,7227,7382,7226,7382,7381,7227,7228,7383,7227,7383,7382,7228,7229,7384,7228,7384,7383,7229,7230,7385,7229,7385,7384,7230,7231,7386,7230,7386,7385,7231,7232,7387,7231,7387,7386,7232,7233,7388,7232,7388,7387,7233,7234,7389,7233,7389,7388,7234,7235,7390,7234,7390,7389,7235,7236,7391,7235,7391,7390,7236,7237,7392,7236,7392,7391,7237,7238,7393,7237,7393,7392,7238,7239,7394,7238,7394,7393,7239,7240,7395,7239,7395,7394,7240,7241,7396,7240,7396,7395,7241,7242,7397,7241,7397,7396,7242,7243,7398,7242,7398,7397,7243,7244,7399,7243,7399,7398,7244,7245,7400,7244,7400,7399,7245,7246,7401,7245,7401,7400,7246,7247,7402,7246,7402,7401,7247,7248,7403,7247,7403,7402,7248,7249,7404,7248,7404,7403,7249,7250,7405,7249,7405,7404,7250,7251,7406,7250,7406,7405,7251,7252,7407,7251,7407,7406,7252,7253,7408,7252,7408,7407,7253,7254,7409,7253,7409,7408,7254,7255,7410,7254,7410,7409,7255,7256,7411,7255,7411,7410,7256,7257,7412,7256,7412,7411,7257,7258,7413,7257,7413,7412,7258,7259,7414,7258,7414,7413,7259,7260,7415,7259,7415,7414,7260,7261,7416,7260,7416,7415,7261,7262,7417,7261,7417,7416,7262,7263,7418,7262,7418,7417,7263,7264,7419,7263,7419,7418,7264,7265,7420,7264,7420,7419,7265,7266,7421,7265,7421,7420,7266,7267,7422,7266,7422,7421,7267,7268,7423,7267,7423,7422,7268,7269,7424,7268,7424,7423,7269,7270,7425,7269,7425,7424,7270,7271,7426,7270,7426,7425,7271,7272,7427,7271,7427,7426,7272,7273,7428,7272,7428,7427,7273,7274,7429,7273,7429,7428,7274,7275,7430,7274,7430,7429,7275,7276,7431,7275,7431,7430,7276,7277,7432,7276,7432,7431,7277,7278,7433,7277,7433,7432,7278,7279,7434,7278,7434,7433,7279,7280,7435,7279,7435,7434,7280,7281,7436,7280,7436,7435,7281,7282,7437,7281,7437,7436,7282,7283,7438,7282,7438,7437,7283,7284,7439,7283,7439,7438,7285,7286,7441,7285,7441,7440,7286,7287,7442,7286,7442,7441,7287,7288,7443,7287,7443,7442,7288,7289,7444,7288,7444,7443,7289,7290,7445,7289,7445,7444,7290,7291,7446,7290,7446,7445,7291,7292,7447,7291,7447,7446,7292,7293,7448,7292,7448,7447,7293,7294,7449,7293,7449,7448,7294,7295,7450,7294,7450,7449,7295,7296,7451,7295,7451,7450,7296,7297,7452,7296,7452,7451,7297,7298,7453,7297,7453,7452,7298,7299,7454,7298,7454,7453,7299,7300,7455,7299,7455,7454,7300,7301,7456,7300,7456,7455,7301,7302,7457,7301,7457,7456,7302,7303,7458,7302,7458,7457,7303,7304,7459,7303,7459,7458,7304,7305,7460,7304,7460,7459,7305,7306,7461,7305,7461,7460,7306,7307,7462,7306,7462,7461,7307,7308,7463,7307,7463,7462,7308,7309,7464,7308,7464,7463,7309,7310,7465,7309,7465,7464,7310,7311,7466,7310,7466,7465,7311,7312,7467,7311,7467,7466,7312,7313,7468,7312,7468,7467,7313,7314,7469,7313,7469,7468,7314,7315,7470,7314,7470,7469,7315,7316,7471,7315,7471,7470,7316,7317,7472,7316,7472,7471,7317,7318,7473,7317,7473,7472,7318,7319,7474,7318,7474,7473,7319,7320,7475,7319,7475,7474,7320,7321,7476,7320,7476,7475,7321,7322,7477,7321,7477,7476,7322,7323,7478,7322,7478,7477,7323,7324,7479,7323,7479,7478,7324,7325,7480,7324,7480,7479,7325,7326,7481,7325,7481,7480,7326,7327,7482,7326,7482,7481,7327,7328,7483,7327,7483,7482,7328,7329,7484,7328,7484,7483,7329,7330,7485,7329,7485,7484,7330,7331,7486,7330,7486,7485,7331,7332,7487,7331,7487,7486,7332,7333,7488,7332,7488,7487,7333,7334,7489,7333,7489,7488,7334,7335,7490,7334,7490,7489,7335,7336,7491,7335,7491,7490,7336,7337,7492,7336,7492,7491,7337,7338,7493,7337,7493,7492,7338,7339,7494,7338,7494,7493,7339,7340,7495,7339,7495,7494,7340,7341,7496,7340,7496,7495,7341,7342,7497,7341,7497,7496,7342,7343,7498,7342,7498,7497,7343,7344,7499,7343,7499,7498,7344,7345,7500,7344,7500,7499,7345,7346,7501,7345,7501,7500,7346,7347,7502,7346,7502,7501,7347,7348,7503,7347,7503,7502,7348,7349,7504,7348,7504,7503,7349,7350,7505,7349,7505,7504,7350,7351,7506,7350,7506,7505,7351,7352,7507,7351,7507,7506,7352,7353,7508,7352,7508,7507,7353,7354,7509,7353,7509,7508,7354,7355,7510,7354,7510,7509,7355,7356,7511,7355,7511,7510,7356,7357,7512,7356,7512,7511,7357,7358,7513,7357,7513,7512,7358,7359,7514,7358,7514,7513,7359,7360,7515,7359,7515,7514,7360,7361,7516,7360,7516,7515,7361,7362,7517,7361,7517,7516,7362,7363,7518,7362,7518,7517,7363,7364,7519,7363,7519,7518,7364,7365,7520,7364,7520,7519,7365,7366,7521,7365,7521,7520,7366,7367,7522,7366,7522,7521,7367,7368,7523,7367,7523,7522,7368,7369,7524,7368,7524,7523,7369,7370,7525,7369,7525,7524,7370,7371,7526,7370,7526,7525,7371,7372,7527,7371,7527,7526,7372,7373,7528,7372,7528,7527,7373,7374,7529,7373,7529,7528,7374,7375,7530,7374,7530,7529,7375,7376,7531,7375,7531,7530,7376,7377,7532,7376,7532,7531,7377,7378,7533,7377,7533,7532,7378,7379,7534,7378,7534,7533,7379,7380,7535,7379,7535,7534,7380,7381,7536,7380,7536,7535,7381,7382,7537,7381,7537,7536,7382,7383,7538,7382,7538,7537,7383,7384,7539,7383,7539,7538,7384,7385,7540,7384,7540,7539,7385,7386,7541,7385,7541,7540,7386,7387,7542,7386,7542,7541,7387,7388,7543,7387,7543,7542,7388,7389,7544,7388,7544,7543,7389,7390,7545,7389,7545,7544,7390,7391,7546,7390,7546,7545,7391,7392,7547,7391,7547,7546,7392,7393,7548,7392,7548,7547,7393,7394,7549,7393,7549,7548,7394,7395,7550,7394,7550,7549,7395,7396,7551,7395,7551,7550,7396,7397,7552,7396,7552,7551,7397,7398,7553,7397,7553,7552,7398,7399,7554,7398,7554,7553,7399,7400,7555,7399,7555,7554,7400,7401,7556,7400,7556,7555,7401,7402,7557,7401,7557,7556,7402,7403,7558,7402,7558,7557,7403,7404,7559,7403,7559,7558,7404,7405,7560,7404,7560,7559,7405,7406,7561,7405,7561,7560,7406,7407,7562,7406,7562,7561,7407,7408,7563,7407,7563,7562,7408,7409,7564,7408,7564,7563,7409,7410,7565,7409,7565,7564,7410,7411,7566,7410,7566,7565,7411,7412,7567,7411,7567,7566,7412,7413,7568,7412,7568,7567,7413,7414,7569,7413,7569,7568,7414,7415,7570,7414,7570,7569,7415,7416,7571,7415,7571,7570,7416,7417,7572,7416,7572,7571,7417,7418,7573,7417,7573,7572,7418,7419,7574,7418,7574,7573,7419,7420,7575,7419,7575,7574,7420,7421,7576,7420,7576,7575,7421,7422,7577,7421,7577,7576,7422,7423,7578,7422,7578,7577,7423,7424,7579,7423,7579,7578,7424,7425,7580,7424,7580,7579,7425,7426,7581,7425,7581,7580,7426,7427,7582,7426,7582,7581,7427,7428,7583,7427,7583,7582,7428,7429,7584,7428,7584,7583,7429,7430,7585,7429,7585,7584,7430,7431,7586,7430,7586,7585,7431,7432,7587,7431,7587,7586,7432,7433,7588,7432,7588,7587,7433,7434,7589,7433,7589,7588,7434,7435,7590,7434,7590,7589,7435,7436,7591,7435,7591,7590,7436,7437,7592,7436,7592,7591,7437,7438,7593,7437,7593,7592,7438,7439,7594,7438,7594,7593,7440,7441,7596,7440,7596,7595,7441,7442,7597,7441,7597,7596,7442,7443,7598,7442,7598,7597,7443,7444,7599,7443,7599,7598,7444,7445,7600,7444,7600,7599,7445,7446,7601,7445,7601,7600,7446,7447,7602,7446,7602,7601,7447,7448,7603,7447,7603,7602,7448,7449,7604,7448,7604,7603,7449,7450,7605,7449,7605,7604,7450,7451,7606,7450,7606,7605,7451,7452,7607,7451,7607,7606,7452,7453,7608,7452,7608,7607,7453,7454,7609,7453,7609,7608,7454,7455,7610,7454,7610,7609,7455,7456,7611,7455,7611,7610,7456,7457,7612,7456,7612,7611,7457,7458,7613,7457,7613,7612,7458,7459,7614,7458,7614,7613,7459,7460,7615,7459,7615,7614,7460,7461,7616,7460,7616,7615,7461,7462,7617,7461,7617,7616,7462,7463,7618,7462,7618,7617,7463,7464,7619,7463,7619,7618,7464,7465,7620,7464,7620,7619,7465,7466,7621,7465,7621,7620,7466,7467,7622,7466,7622,7621,7467,7468,7623,7467,7623,7622,7468,7469,7624,7468,7624,7623,7469,7470,7625,7469,7625,7624,7470,7471,7626,7470,7626,7625,7471,7472,7627,7471,7627,7626,7472,7473,7628,7472,7628,7627,7473,7474,7629,7473,7629,7628,7474,7475,7630,7474,7630,7629,7475,7476,7631,7475,7631,7630,7476,7477,7632,7476,7632,7631,7477,7478,7633,7477,7633,7632,7478,7479,7634,7478,7634,7633,7479,7480,7635,7479,7635,7634,7480,7481,7636,7480,7636,7635,7481,7482,7637,7481,7637,7636,7482,7483,7638,7482,7638,7637,7483,7484,7639,7483,7639,7638,7484,7485,7640,7484,7640,7639,7485,7486,7641,7485,7641,7640,7486,7487,7642,7486,7642,7641,7487,7488,7643,7487,7643,7642,7488,7489,7644,7488,7644,7643,7489,7490,7645,7489,7645,7644,7490,7491,7646,7490,7646,7645,7491,7492,7647,7491,7647,7646,7492,7493,7648,7492,7648,7647,7493,7494,7649,7493,7649,7648,7494,7495,7650,7494,7650,7649,7495,7496,7651,7495,7651,7650,7496,7497,7652,7496,7652,7651,7497,7498,7653,7497,7653,7652,7498,7499,7654,7498,7654,7653,7499,7500,7655,7499,7655,7654,7500,7501,7656,7500,7656,7655,7501,7502,7657,7501,7657,7656,7502,7503,7658,7502,7658,7657,7503,7504,7659,7503,7659,7658,7504,7505,7660,7504,7660,7659,7505,7506,7661,7505,7661,7660,7506,7507,7662,7506,7662,7661,7507,7508,7663,7507,7663,7662,7508,7509,7664,7508,7664,7663,7509,7510,7665,7509,7665,7664,7510,7511,7666,7510,7666,7665,7511,7512,7667,7511,7667,7666,7512,7513,7668,7512,7668,7667,7513,7514,7669,7513,7669,7668,7514,7515,7670,7514,7670,7669,7515,7516,7671,7515,7671,7670,7516,7517,7672,7516,7672,7671,7517,7518,7673,7517,7673,7672,7518,7519,7674,7518,7674,7673,7519,7520,7675,7519,7675,7674,7520,7521,7676,7520,7676,7675,7521,7522,7677,7521,7677,7676,7522,7523,7678,7522,7678,7677,7523,7524,7679,7523,7679,7678,7524,7525,7680,7524,7680,7679,7525,7526,7681,7525,7681,7680,7526,7527,7682,7526,7682,7681,7527,7528,7683,7527,7683,7682,7528,7529,7684,7528,7684,7683,7529,7530,7685,7529,7685,7684,7530,7531,7686,7530,7686,7685,7531,7532,7687,7531,7687,7686,7532,7533,7688,7532,7688,7687,7533,7534,7689,7533,7689,7688,7534,7535,7690,7534,7690,7689,7535,7536,7691,7535,7691,7690,7536,7537,7692,7536,7692,7691,7537,7538,7693,7537,7693,7692,7538,7539,7694,7538,7694,7693,7539,7540,7695,7539,7695,7694,7540,7541,7696,7540,7696,7695,7541,7542,7697,7541,7697,7696,7542,7543,7698,7542,7698,7697,7543,7544,7699,7543,7699,7698,7544,7545,7700,7544,7700,7699,7545,7546,7701,7545,7701,7700,7546,7547,7702,7546,7702,7701,7547,7548,7703,7547,7703,7702,7548,7549,7704,7548,7704,7703,7549,7550,7705,7549,7705,7704,7550,7551,7706,7550,7706,7705,7551,7552,7707,7551,7707,7706,7552,7553,7708,7552,7708,7707,7553,7554,7709,7553,7709,7708,7554,7555,7710,7554,7710,7709,7555,7556,7711,7555,7711,7710,7556,7557,7712,7556,7712,7711,7557,7558,7713,7557,7713,7712,7558,7559,7714,7558,7714,7713,7559,7560,7715,7559,7715,7714,7560,7561,7716,7560,7716,7715,7561,7562,7717,7561,7717,7716,7562,7563,7718,7562,7718,7717,7563,7564,7719,7563,7719,7718,7564,7565,7720,7564,7720,7719,7565,7566,7721,7565,7721,7720,7566,7567,7722,7566,7722,7721,7567,7568,7723,7567,7723,7722,7568,7569,7724,7568,7724,7723,7569,7570,7725,7569,7725,7724,7570,7571,7726,7570,7726,7725,7571,7572,7727,7571,7727,7726,7572,7573,7728,7572,7728,7727,7573,7574,7729,7573,7729,7728,7574,7575,7730,7574,7730,7729,7575,7576,7731,7575,7731,7730,7576,7577,7732,7576,7732,7731,7577,7578,7733,7577,7733,7732,7578,7579,7734,7578,7734,7733,7579,7580,7735,7579,7735,7734,7580,7581,7736,7580,7736,7735,7581,7582,7737,7581,7737,7736,7582,7583,7738,7582,7738,7737,7583,7584,7739,7583,7739,7738,7584,7585,7740,7584,7740,7739,7585,7586,7741,7585,7741,7740,7586,7587,7742,7586,7742,7741,7587,7588,7743,7587,7743,7742,7588,7589,7744,7588,7744,7743,7589,7590,7745,7589,7745,7744,7590,7591,7746,7590,7746,7745,7591,7592,7747,7591,7747,7746,7592,7593,7748,7592,7748,7747,7593,7594,7749,7593,7749,7748,7595,7596,7751,7595,7751,7750,7596,7597,7752,7596,7752,7751,7597,7598,7753,7597,7753,7752,7598,7599,7754,7598,7754,7753,7599,7600,7755,7599,7755,7754,7600,7601,7756,7600,7756,7755,7601,7602,7757,7601,7757,7756,7602,7603,7758,7602,7758,7757,7603,7604,7759,7603,7759,7758,7604,7605,7760,7604,7760,7759,7605,7606,7761,7605,7761,7760,7606,7607,7762,7606,7762,7761,7607,7608,7763,7607,7763,7762,7608,7609,7764,7608,7764,7763,7609,7610,7765,7609,7765,7764,7610,7611,7766,7610,7766,7765,7611,7612,7767,7611,7767,7766,7612,7613,7768,7612,7768,7767,7613,7614,7769,7613,7769,7768,7614,7615,7770,7614,7770,7769,7615,7616,7771,7615,7771,7770,7616,7617,7772,7616,7772,7771,7617,7618,7773,7617,7773,7772,7618,7619,7774,7618,7774,7773,7619,7620,7775,7619,7775,7774,7620,7621,7776,7620,7776,7775,7621,7622,7777,7621,7777,7776,7622,7623,7778,7622,7778,7777,7623,7624,7779,7623,7779,7778,7624,7625,7780,7624,7780,7779,7625,7626,7781,7625,7781,7780,7626,7627,7782,7626,7782,7781,7627,7628,7783,7627,7783,7782,7628,7629,7784,7628,7784,7783,7629,7630,7785,7629,7785,7784,7630,7631,7786,7630,7786,7785,7631,7632,7787,7631,7787,7786,7632,7633,7788,7632,7788,7787,7633,7634,7789,7633,7789,7788,7634,7635,7790,7634,7790,7789,7635,7636,7791,7635,7791,7790,7636,7637,7792,7636,7792,7791,7637,7638,7793,7637,7793,7792,7638,7639,7794,7638,7794,7793,7639,7640,7795,7639,7795,7794,7640,7641,7796,7640,7796,7795,7641,7642,7797,7641,7797,7796,7642,7643,7798,7642,7798,7797,7643,7644,7799,7643,7799,7798,7644,7645,7800,7644,7800,7799,7645,7646,7801,7645,7801,7800,7646,7647,7802,7646,7802,7801,7647,7648,7803,7647,7803,7802,7648,7649,7804,7648,7804,7803,7649,7650,7805,7649,7805,7804,7650,7651,7806,7650,7806,7805,7651,7652,7807,7651,7807,7806,7652,7653,7808,7652,7808,7807,7653,7654,7809,7653,7809,7808,7654,7655,7810,7654,7810,7809,7655,7656,7811,7655,7811,7810,7656,7657,7812,7656,7812,7811,7657,7658,7813,7657,7813,7812,7658,7659,7814,7658,7814,7813,7659,7660,7815,7659,7815,7814,7660,7661,7816,7660,7816,7815,7661,7662,7817,7661,7817,7816,7662,7663,7818,7662,7818,7817,7663,7664,7819,7663,7819,7818,7664,7665,7820,7664,7820,7819,7665,7666,7821,7665,7821,7820,7666,7667,7822,7666,7822,7821,7667,7668,7823,7667,7823,7822,7668,7669,7824,7668,7824,7823,7669,7670,7825,7669,7825,7824,7670,7671,7826,7670,7826,7825,7671,7672,7827,7671,7827,7826,7672,7673,7828,7672,7828,7827,7673,7674,7829,7673,7829,7828,7674,7675,7830,7674,7830,7829,7675,7676,7831,7675,7831,7830,7676,7677,7832,7676,7832,7831,7677,7678,7833,7677,7833,7832,7678,7679,7834,7678,7834,7833,7679,7680,7835,7679,7835,7834,7680,7681,7836,7680,7836,7835,7681,7682,7837,7681,7837,7836,7682,7683,7838,7682,7838,7837,7683,7684,7839,7683,7839,7838,7684,7685,7840,7684,7840,7839,7685,7686,7841,7685,7841,7840,7686,7687,7842,7686,7842,7841,7687,7688,7843,7687,7843,7842,7688,7689,7844,7688,7844,7843,7689,7690,7845,7689,7845,7844,7690,7691,7846,7690,7846,7845,7691,7692,7847,7691,7847,7846,7692,7693,7848,7692,7848,7847,7693,7694,7849,7693,7849,7848,7694,7695,7850,7694,7850,7849,7695,7696,7851,7695,7851,7850,7696,7697,7852,7696,7852,7851,7697,7698,7853,7697,7853,7852,7698,7699,7854,7698,7854,7853,7699,7700,7855,7699,7855,7854,7700,7701,7856,7700,7856,7855,7701,7702,7857,7701,7857,7856,7702,7703,7858,7702,7858,7857,7703,7704,7859,7703,7859,7858,7704,7705,7860,7704,7860,7859,7705,7706,7861,7705,7861,7860,7706,7707,7862,7706,7862,7861,7707,7708,7863,7707,7863,7862,7708,7709,7864,7708,7864,7863,7709,7710,7865,7709,7865,7864,7710,7711,7866,7710,7866,7865,7711,7712,7867,7711,7867,7866,7712,7713,7868,7712,7868,7867,7713,7714,7869,7713,7869,7868,7714,7715,7870,7714,7870,7869,7715,7716,7871,7715,7871,7870,7716,7717,7872,7716,7872,7871,7717,7718,7873,7717,7873,7872,7718,7719,7874,7718,7874,7873,7719,7720,7875,7719,7875,7874,7720,7721,7876,7720,7876,7875,7721,7722,7877,7721,7877,7876,7722,7723,7878,7722,7878,7877,7723,7724,7879,7723,7879,7878,7724,7725,7880,7724,7880,7879,7725,7726,7881,7725,7881,7880,7726,7727,7882,7726,7882,7881,7727,7728,7883,7727,7883,7882,7728,7729,7884,7728,7884,7883,7729,7730,7885,7729,7885,7884,7730,7731,7886,7730,7886,7885,7731,7732,7887,7731,7887,7886,7732,7733,7888,7732,7888,7887,7733,7734,7889,7733,7889,7888,7734,7735,7890,7734,7890,7889,7735,7736,7891,7735,7891,7890,7736,7737,7892,7736,7892,7891,7737,7738,7893,7737,7893,7892,7738,7739,7894,7738,7894,7893,7739,7740,7895,7739,7895,7894,7740,7741,7896,7740,7896,7895,7741,7742,7897,7741,7897,7896,7742,7743,7898,7742,7898,7897,7743,7744,7899,7743,7899,7898,7744,7745,7900,7744,7900,7899,7745,7746,7901,7745,7901,7900,7746,7747,7902,7746,7902,7901,7747,7748,7903,7747,7903,7902,7748,7749,7904,7748,7904,7903,7750,7751,7906,7750,7906,7905,7751,7752,7907,7751,7907,7906,7752,7753,7908,7752,7908,7907,7753,7754,7909,7753,7909,7908,7754,7755,7910,7754,7910,7909,7755,7756,7911,7755,7911,7910,7756,7757,7912,7756,7912,7911,7757,7758,7913,7757,7913,7912,7758,7759,7914,7758,7914,7913,7759,7760,7915,7759,7915,7914,7760,7761,7916,7760,7916,7915,7761,7762,7917,7761,7917,7916,7762,7763,7918,7762,7918,7917,7763,7764,7919,7763,7919,7918,7764,7765,7920,7764,7920,7919,7765,7766,7921,7765,7921,7920,7766,7767,7922,7766,7922,7921,7767,7768,7923,7767,7923,7922,7768,7769,7924,7768,7924,7923,7769,7770,7925,7769,7925,7924,7770,7771,7926,7770,7926,7925,7771,7772,7927,7771,7927,7926,7772,7773,7928,7772,7928,7927,7773,7774,7929,7773,7929,7928,7774,7775,7930,7774,7930,7929,7775,7776,7931,7775,7931,7930,7776,7777,7932,7776,7932,7931,7777,7778,7933,7777,7933,7932,7778,7779,7934,7778,7934,7933,7779,7780,7935,7779,7935,7934,7780,7781,7936,7780,7936,7935,7781,7782,7937,7781,7937,7936,7782,7783,7938,7782,7938,7937,7783,7784,7939,7783,7939,7938,7784,7785,7940,7784,7940,7939,7785,7786,7941,7785,7941,7940,7786,7787,7942,7786,7942,7941,7787,7788,7943,7787,7943,7942,7788,7789,7944,7788,7944,7943,7789,7790,7945,7789,7945,7944,7790,7791,7946,7790,7946,7945,7791,7792,7947,7791,7947,7946,7792,7793,7948,7792,7948,7947,7793,7794,7949,7793,7949,7948,7794,7795,7950,7794,7950,7949,7795,7796,7951,7795,7951,7950,7796,7797,7952,7796,7952,7951,7797,7798,7953,7797,7953,7952,7798,7799,7954,7798,7954,7953,7799,7800,7955,7799,7955,7954,7800,7801,7956,7800,7956,7955,7801,7802,7957,7801,7957,7956,7802,7803,7958,7802,7958,7957,7803,7804,7959,7803,7959,7958,7804,7805,7960,7804,7960,7959,7805,7806,7961,7805,7961,7960,7806,7807,7962,7806,7962,7961,7807,7808,7963,7807,7963,7962,7808,7809,7964,7808,7964,7963,7809,7810,7965,7809,7965,7964,7810,7811,7966,7810,7966,7965,7811,7812,7967,7811,7967,7966,7812,7813,7968,7812,7968,7967,7813,7814,7969,7813,7969,7968,7814,7815,7970,7814,7970,7969,7815,7816,7971,7815,7971,7970,7816,7817,7972,7816,7972,7971,7817,7818,7973,7817,7973,7972,7818,7819,7974,7818,7974,7973,7819,7820,7975,7819,7975,7974,7820,7821,7976,7820,7976,7975,7821,7822,7977,7821,7977,7976,7822,7823,7978,7822,7978,7977,7823,7824,7979,7823,7979,7978,7824,7825,7980,7824,7980,7979,7825,7826,7981,7825,7981,7980,7826,7827,7982,7826,7982,7981,7827,7828,7983,7827,7983,7982,7828,7829,7984,7828,7984,7983,7829,7830,7985,7829,7985,7984,7830,7831,7986,7830,7986,7985,7831,7832,7987,7831,7987,7986,7832,7833,7988,7832,7988,7987,7833,7834,7989,7833,7989,7988,7834,7835,7990,7834,7990,7989,7835,7836,7991,7835,7991,7990,7836,7837,7992,7836,7992,7991,7837,7838,7993,7837,7993,7992,7838,7839,7994,7838,7994,7993,7839,7840,7995,7839,7995,7994,7840,7841,7996,7840,7996,7995,7841,7842,7997,7841,7997,7996,7842,7843,7998,7842,7998,7997,7843,7844,7999,7843,7999,7998,7844,7845,8000,7844,8000,7999,7845,7846,8001,7845,8001,8000,7846,7847,8002,7846,8002,8001,7847,7848,8003,7847,8003,8002,7848,7849,8004,7848,8004,8003,7849,7850,8005,7849,8005,8004,7850,7851,8006,7850,8006,8005,7851,7852,8007,7851,8007,8006,7852,7853,8008,7852,8008,8007,7853,7854,8009,7853,8009,8008,7854,7855,8010,7854,8010,8009,7855,7856,8011,7855,8011,8010,7856,7857,8012,7856,8012,8011,7857,7858,8013,7857,8013,8012,7858,7859,8014,7858,8014,8013,7859,7860,8015,7859,8015,8014,7860,7861,8016,7860,8016,8015,7861,7862,8017,7861,8017,8016,7862,7863,8018,7862,8018,8017,7863,7864,8019,7863,8019,8018,7864,7865,8020,7864,8020,8019,7865,7866,8021,7865,8021,8020,7866,7867,8022,7866,8022,8021,7867,7868,8023,7867,8023,8022,7868,7869,8024,7868,8024,8023,7869,7870,8025,7869,8025,8024,7870,7871,8026,7870,8026,8025,7871,7872,8027,7871,8027,8026,7872,7873,8028,7872,8028,8027,7873,7874,8029,7873,8029,8028,7874,7875,8030,7874,8030,8029,7875,7876,8031,7875,8031,8030,7876,7877,8032,7876,8032,8031,7877,7878,8033,7877,8033,8032,7878,7879,8034,7878,8034,8033,7879,7880,8035,7879,8035,8034,7880,7881,8036,7880,8036,8035,7881,7882,8037,7881,8037,8036,7882,7883,8038,7882,8038,8037,7883,7884,8039,7883,8039,8038,7884,7885,8040,7884,8040,8039,7885,7886,8041,7885,8041,8040,7886,7887,8042,7886,8042,8041,7887,7888,8043,7887,8043,8042,7888,7889,8044,7888,8044,8043,7889,7890,8045,7889,8045,8044,7890,7891,8046,7890,8046,8045,7891,7892,8047,7891,8047,8046,7892,7893,8048,7892,8048,8047,7893,7894,8049,7893,8049,8048,7894,7895,8050,7894,8050,8049,7895,7896,8051,7895,8051,8050,7896,7897,8052,7896,8052,8051,7897,7898,8053,7897,8053,8052,7898,7899,8054,7898,8054,8053,7899,7900,8055,7899,8055,8054,7900,7901,8056,7900,8056,8055,7901,7902,8057,7901,8057,8056,7902,7903,8058,7902,8058,8057,7903,7904,8059,7903,8059,8058,7905,7906,8061,7905,8061,8060,7906,7907,8062,7906,8062,8061,7907,7908,8063,7907,8063,8062,7908,7909,8064,7908,8064,8063,7909,7910,8065,7909,8065,8064,7910,7911,8066,7910,8066,8065,7911,7912,8067,7911,8067,8066,7912,7913,8068,7912,8068,8067,7913,7914,8069,7913,8069,8068,7914,7915,8070,7914,8070,8069,7915,7916,8071,7915,8071,8070,7916,7917,8072,7916,8072,8071,7917,7918,8073,7917,8073,8072,7918,7919,8074,7918,8074,8073,7919,7920,8075,7919,8075,8074,7920,7921,8076,7920,8076,8075,7921,7922,8077,7921,8077,8076,7922,7923,8078,7922,8078,8077,7923,7924,8079,7923,8079,8078,7924,7925,8080,7924,8080,8079,7925,7926,8081,7925,8081,8080,7926,7927,8082,7926,8082,8081,7927,7928,8083,7927,8083,8082,7928,7929,8084,7928,8084,8083,7929,7930,8085,7929,8085,8084,7930,7931,8086,7930,8086,8085,7931,7932,8087,7931,8087,8086,7932,7933,8088,7932,8088,8087,7933,7934,8089,7933,8089,8088,7934,7935,8090,7934,8090,8089,7935,7936,8091,7935,8091,8090,7936,7937,8092,7936,8092,8091,7937,7938,8093,7937,8093,8092,7938,7939,8094,7938,8094,8093,7939,7940,8095,7939,8095,8094,7940,7941,8096,7940,8096,8095,7941,7942,8097,7941,8097,8096,7942,7943,8098,7942,8098,8097,7943,7944,8099,7943,8099,8098,7944,7945,8100,7944,8100,8099,7945,7946,8101,7945,8101,8100,7946,7947,8102,7946,8102,8101,7947,7948,8103,7947,8103,8102,7948,7949,8104,7948,8104,8103,7949,7950,8105,7949,8105,8104,7950,7951,8106,7950,8106,8105,7951,7952,8107,7951,8107,8106,7952,7953,8108,7952,8108,8107,7953,7954,8109,7953,8109,8108,7954,7955,8110,7954,8110,8109,7955,7956,8111,7955,8111,8110,7956,7957,8112,7956,8112,8111,7957,7958,8113,7957,8113,8112,7958,7959,8114,7958,8114,8113,7959,7960,8115,7959,8115,8114,7960,7961,8116,7960,8116,8115,7961,7962,8117,7961,8117,8116,7962,7963,8118,7962,8118,8117,7963,7964,8119,7963,8119,8118,7964,7965,8120,7964,8120,8119,7965,7966,8121,7965,8121,8120,7966,7967,8122,7966,8122,8121,7967,7968,8123,7967,8123,8122,7968,7969,8124,7968,8124,8123,7969,7970,8125,7969,8125,8124,7970,7971,8126,7970,8126,8125,7971,7972,8127,7971,8127,8126,7972,7973,8128,7972,8128,8127,7973,7974,8129,7973,8129,8128,7974,7975,8130,7974,8130,8129,7975,7976,8131,7975,8131,8130,7976,7977,8132,7976,8132,8131,7977,7978,8133,7977,8133,8132,7978,7979,8134,7978,8134,8133,7979,7980,8135,7979,8135,8134,7980,7981,8136,7980,8136,8135,7981,7982,8137,7981,8137,8136,7982,7983,8138,7982,8138,8137,7983,7984,8139,7983,8139,8138,7984,7985,8140,7984,8140,8139,7985,7986,8141,7985,8141,8140,7986,7987,8142,7986,8142,8141,7987,7988,8143,7987,8143,8142,7988,7989,8144,7988,8144,8143,7989,7990,8145,7989,8145,8144,7990,7991,8146,7990,8146,8145,7991,7992,8147,7991,8147,8146,7992,7993,8148,7992,8148,8147,7993,7994,8149,7993,8149,8148,7994,7995,8150,7994,8150,8149,7995,7996,8151,7995,8151,8150,7996,7997,8152,7996,8152,8151,7997,7998,8153,7997,8153,8152,7998,7999,8154,7998,8154,8153,7999,8000,8155,7999,8155,8154,8000,8001,8156,8000,8156,8155,8001,8002,8157,8001,8157,8156,8002,8003,8158,8002,8158,8157,8003,8004,8159,8003,8159,8158,8004,8005,8160,8004,8160,8159,8005,8006,8161,8005,8161,8160,8006,8007,8162,8006,8162,8161,8007,8008,8163,8007,8163,8162,8008,8009,8164,8008,8164,8163,8009,8010,8165,8009,8165,8164,8010,8011,8166,8010,8166,8165,8011,8012,8167,8011,8167,8166,8012,8013,8168,8012,8168,8167,8013,8014,8169,8013,8169,8168,8014,8015,8170,8014,8170,8169,8015,8016,8171,8015,8171,8170,8016,8017,8172,8016,8172,8171,8017,8018,8173,8017,8173,8172,8018,8019,8174,8018,8174,8173,8019,8020,8175,8019,8175,8174,8020,8021,8176,8020,8176,8175,8021,8022,8177,8021,8177,8176,8022,8023,8178,8022,8178,8177,8023,8024,8179,8023,8179,8178,8024,8025,8180,8024,8180,8179,8025,8026,8181,8025,8181,8180,8026,8027,8182,8026,8182,8181,8027,8028,8183,8027,8183,8182,8028,8029,8184,8028,8184,8183,8029,8030,8185,8029,8185,8184,8030,8031,8186,8030,8186,8185,8031,8032,8187,8031,8187,8186,8032,8033,8188,8032,8188,8187,8033,8034,8189,8033,8189,8188,8034,8035,8190,8034,8190,8189,8035,8036,8191,8035,8191,8190,8036,8037,8192,8036,8192,8191,8037,8038,8193,8037,8193,8192,8038,8039,8194,8038,8194,8193,8039,8040,8195,8039,8195,8194,8040,8041,8196,8040,8196,8195,8041,8042,8197,8041,8197,8196,8042,8043,8198,8042,8198,8197,8043,8044,8199,8043,8199,8198,8044,8045,8200,8044,8200,8199,8045,8046,8201,8045,8201,8200,8046,8047,8202,8046,8202,8201,8047,8048,8203,8047,8203,8202,8048,8049,8204,8048,8204,8203,8049,8050,8205,8049,8205,8204,8050,8051,8206,8050,8206,8205,8051,8052,8207,8051,8207,8206,8052,8053,8208,8052,8208,8207,8053,8054,8209,8053,8209,8208,8054,8055,8210,8054,8210,8209,8055,8056,8211,8055,8211,8210,8056,8057,8212,8056,8212,8211,8057,8058,8213,8057,8213,8212,8058,8059,8214,8058,8214,8213,8060,8061,8216,8060,8216,8215,8061,8062,8217,8061,8217,8216,8062,8063,8218,8062,8218,8217,8063,8064,8219,8063,8219,8218,8064,8065,8220,8064,8220,8219,8065,8066,8221,8065,8221,8220,8066,8067,8222,8066,8222,8221,8067,8068,8223,8067,8223,8222,8068,8069,8224,8068,8224,8223,8069,8070,8225,8069,8225,8224,8070,8071,8226,8070,8226,8225,8071,8072,8227,8071,8227,8226,8072,8073,8228,8072,8228,8227,8073,8074,8229,8073,8229,8228,8074,8075,8230,8074,8230,8229,8075,8076,8231,8075,8231,8230,8076,8077,8232,8076,8232,8231,8077,8078,8233,8077,8233,8232,8078,8079,8234,8078,8234,8233,8079,8080,8235,8079,8235,8234,8080,8081,8236,8080,8236,8235,8081,8082,8237,8081,8237,8236,8082,8083,8238,8082,8238,8237,8083,8084,8239,8083,8239,8238,8084,8085,8240,8084,8240,8239,8085,8086,8241,8085,8241,8240,8086,8087,8242,8086,8242,8241,8087,8088,8243,8087,8243,8242,8088,8089,8244,8088,8244,8243,8089,8090,8245,8089,8245,8244,8090,8091,8246,8090,8246,8245,8091,8092,8247,8091,8247,8246,8092,8093,8248,8092,8248,8247,8093,8094,8249,8093,8249,8248,8094,8095,8250,8094,8250,8249,8095,8096,8251,8095,8251,8250,8096,8097,8252,8096,8252,8251,8097,8098,8253,8097,8253,8252,8098,8099,8254,8098,8254,8253,8099,8100,8255,8099,8255,8254,8100,8101,8256,8100,8256,8255,8101,8102,8257,8101,8257,8256,8102,8103,8258,8102,8258,8257,8103,8104,8259,8103,8259,8258,8104,8105,8260,8104,8260,8259,8105,8106,8261,8105,8261,8260,8106,8107,8262,8106,8262,8261,8107,8108,8263,8107,8263,8262,8108,8109,8264,8108,8264,8263,8109,8110,8265,8109,8265,8264,8110,8111,8266,8110,8266,8265,8111,8112,8267,8111,8267,8266,8112,8113,8268,8112,8268,8267,8113,8114,8269,8113,8269,8268,8114,8115,8270,8114,8270,8269,8115,8116,8271,8115,8271,8270,8116,8117,8272,8116,8272,8271,8117,8118,8273,8117,8273,8272,8118,8119,8274,8118,8274,8273,8119,8120,8275,8119,8275,8274,8120,8121,8276,8120,8276,8275,8121,8122,8277,8121,8277,8276,8122,8123,8278,8122,8278,8277,8123,8124,8279,8123,8279,8278,8124,8125,8280,8124,8280,8279,8125,8126,8281,8125,8281,8280,8126,8127,8282,8126,8282,8281,8127,8128,8283,8127,8283,8282,8128,8129,8284,8128,8284,8283,8129,8130,8285,8129,8285,8284,8130,8131,8286,8130,8286,8285,8131,8132,8287,8131,8287,8286,8132,8133,8288,8132,8288,8287,8133,8134,8289,8133,8289,8288,8134,8135,8290,8134,8290,8289,8135,8136,8291,8135,8291,8290,8136,8137,8292,8136,8292,8291,8137,8138,8293,8137,8293,8292,8138,8139,8294,8138,8294,8293,8139,8140,8295,8139,8295,8294,8140,8141,8296,8140,8296,8295,8141,8142,8297,8141,8297,8296,8142,8143,8298,8142,8298,8297,8143,8144,8299,8143,8299,8298,8144,8145,8300,8144,8300,8299,8145,8146,8301,8145,8301,8300,8146,8147,8302,8146,8302,8301,8147,8148,8303,8147,8303,8302,8148,8149,8304,8148,8304,8303,8149,8150,8305,8149,8305,8304,8150,8151,8306,8150,8306,8305,8151,8152,8307,8151,8307,8306,8152,8153,8308,8152,8308,8307,8153,8154,8309,8153,8309,8308,8154,8155,8310,8154,8310,8309,8155,8156,8311,8155,8311,8310,8156,8157,8312,8156,8312,8311,8157,8158,8313,8157,8313,8312,8158,8159,8314,8158,8314,8313,8159,8160,8315,8159,8315,8314,8160,8161,8316,8160,8316,8315,8161,8162,8317,8161,8317,8316,8162,8163,8318,8162,8318,8317,8163,8164,8319,8163,8319,8318,8164,8165,8320,8164,8320,8319,8165,8166,8321,8165,8321,8320,8166,8167,8322,8166,8322,8321,8167,8168,8323,8167,8323,8322,8168,8169,8324,8168,8324,8323,8169,8170,8325,8169,8325,8324,8170,8171,8326,8170,8326,8325,8171,8172,8327,8171,8327,8326,8172,8173,8328,8172,8328,8327,8173,8174,8329,8173,8329,8328,8174,8175,8330,8174,8330,8329,8175,8176,8331,8175,8331,8330,8176,8177,8332,8176,8332,8331,8177,8178,8333,8177,8333,8332,8178,8179,8334,8178,8334,8333,8179,8180,8335,8179,8335,8334,8180,8181,8336,8180,8336,8335,8181,8182,8337,8181,8337,8336,8182,8183,8338,8182,8338,8337,8183,8184,8339,8183,8339,8338,8184,8185,8340,8184,8340,8339,8185,8186,8341,8185,8341,8340,8186,8187,8342,8186,8342,8341,8187,8188,8343,8187,8343,8342,8188,8189,8344,8188,8344,8343,8189,8190,8345,8189,8345,8344,8190,8191,8346,8190,8346,8345,8191,8192,8347,8191,8347,8346,8192,8193,8348,8192,8348,8347,8193,8194,8349,8193,8349,8348,8194,8195,8350,8194,8350,8349,8195,8196,8351,8195,8351,8350,8196,8197,8352,8196,8352,8351,8197,8198,8353,8197,8353,8352,8198,8199,8354,8198,8354,8353,8199,8200,8355,8199,8355,8354,8200,8201,8356,8200,8356,8355,8201,8202,8357,8201,8357,8356,8202,8203,8358,8202,8358,8357,8203,8204,8359,8203,8359,8358,8204,8205,8360,8204,8360,8359,8205,8206,8361,8205,8361,8360,8206,8207,8362,8206,8362,8361,8207,8208,8363,8207,8363,8362,8208,8209,8364,8208,8364,8363,8209,8210,8365,8209,8365,8364,8210,8211,8366,8210,8366,8365,8211,8212,8367,8211,8367,8366,8212,8213,8368,8212,8368,8367,8213,8214,8369,8213,8369,8368,8215,8216,8371,8215,8371,8370,8216,8217,8372,8216,8372,8371,8217,8218,8373,8217,8373,8372,8218,8219,8374,8218,8374,8373,8219,8220,8375,8219,8375,8374,8220,8221,8376,8220,8376,8375,8221,8222,8377,8221,8377,8376,8222,8223,8378,8222,8378,8377,8223,8224,8379,8223,8379,8378,8224,8225,8380,8224,8380,8379,8225,8226,8381,8225,8381,8380,8226,8227,8382,8226,8382,8381,8227,8228,8383,8227,8383,8382,8228,8229,8384,8228,8384,8383,8229,8230,8385,8229,8385,8384,8230,8231,8386,8230,8386,8385,8231,8232,8387,8231,8387,8386,8232,8233,8388,8232,8388,8387,8233,8234,8389,8233,8389,8388,8234,8235,8390,8234,8390,8389,8235,8236,8391,8235,8391,8390,8236,8237,8392,8236,8392,8391,8237,8238,8393,8237,8393,8392,8238,8239,8394,8238,8394,8393,8239,8240,8395,8239,8395,8394,8240,8241,8396,8240,8396,8395,8241,8242,8397,8241,8397,8396,8242,8243,8398,8242,8398,8397,8243,8244,8399,8243,8399,8398,8244,8245,8400,8244,8400,8399,8245,8246,8401,8245,8401,8400,8246,8247,8402,8246,8402,8401,8247,8248,8403,8247,8403,8402,8248,8249,8404,8248,8404,8403,8249,8250,8405,8249,8405,8404,8250,8251,8406,8250,8406,8405,8251,8252,8407,8251,8407,8406,8252,8253,8408,8252,8408,8407,8253,8254,8409,8253,8409,8408,8254,8255,8410,8254,8410,8409,8255,8256,8411,8255,8411,8410,8256,8257,8412,8256,8412,8411,8257,8258,8413,8257,8413,8412,8258,8259,8414,8258,8414,8413,8259,8260,8415,8259,8415,8414,8260,8261,8416,8260,8416,8415,8261,8262,8417,8261,8417,8416,8262,8263,8418,8262,8418,8417,8263,8264,8419,8263,8419,8418,8264,8265,8420,8264,8420,8419,8265,8266,8421,8265,8421,8420,8266,8267,8422,8266,8422,8421,8267,8268,8423,8267,8423,8422,8268,8269,8424,8268,8424,8423,8269,8270,8425,8269,8425,8424,8270,8271,8426,8270,8426,8425,8271,8272,8427,8271,8427,8426,8272,8273,8428,8272,8428,8427,8273,8274,8429,8273,8429,8428,8274,8275,8430,8274,8430,8429,8275,8276,8431,8275,8431,8430,8276,8277,8432,8276,8432,8431,8277,8278,8433,8277,8433,8432,8278,8279,8434,8278,8434,8433,8279,8280,8435,8279,8435,8434,8280,8281,8436,8280,8436,8435,8281,8282,8437,8281,8437,8436,8282,8283,8438,8282,8438,8437,8283,8284,8439,8283,8439,8438,8284,8285,8440,8284,8440,8439,8285,8286,8441,8285,8441,8440,8286,8287,8442,8286,8442,8441,8287,8288,8443,8287,8443,8442,8288,8289,8444,8288,8444,8443,8289,8290,8445,8289,8445,8444,8290,8291,8446,8290,8446,8445,8291,8292,8447,8291,8447,8446,8292,8293,8448,8292,8448,8447,8293,8294,8449,8293,8449,8448,8294,8295,8450,8294,8450,8449,8295,8296,8451,8295,8451,8450,8296,8297,8452,8296,8452,8451,8297,8298,8453,8297,8453,8452,8298,8299,8454,8298,8454,8453,8299,8300,8455,8299,8455,8454,8300,8301,8456,8300,8456,8455,8301,8302,8457,8301,8457,8456,8302,8303,8458,8302,8458,8457,8303,8304,8459,8303,8459,8458,8304,8305,8460,8304,8460,8459,8305,8306,8461,8305,8461,8460,8306,8307,8462,8306,8462,8461,8307,8308,8463,8307,8463,8462,8308,8309,8464,8308,8464,8463,8309,8310,8465,8309,8465,8464,8310,8311,8466,8310,8466,8465,8311,8312,8467,8311,8467,8466,8312,8313,8468,8312,8468,8467,8313,8314,8469,8313,8469,8468,8314,8315,8470,8314,8470,8469,8315,8316,8471,8315,8471,8470,8316,8317,8472,8316,8472,8471,8317,8318,8473,8317,8473,8472,8318,8319,8474,8318,8474,8473,8319,8320,8475,8319,8475,8474,8320,8321,8476,8320,8476,8475,8321,8322,8477,8321,8477,8476,8322,8323,8478,8322,8478,8477,8323,8324,8479,8323,8479,8478,8324,8325,8480,8324,8480,8479,8325,8326,8481,8325,8481,8480,8326,8327,8482,8326,8482,8481,8327,8328,8483,8327,8483,8482,8328,8329,8484,8328,8484,8483,8329,8330,8485,8329,8485,8484,8330,8331,8486,8330,8486,8485,8331,8332,8487,8331,8487,8486,8332,8333,8488,8332,8488,8487,8333,8334,8489,8333,8489,8488,8334,8335,8490,8334,8490,8489,8335,8336,8491,8335,8491,8490,8336,8337,8492,8336,8492,8491,8337,8338,8493,8337,8493,8492,8338,8339,8494,8338,8494,8493,8339,8340,8495,8339,8495,8494,8340,8341,8496,8340,8496,8495,8341,8342,8497,8341,8497,8496,8342,8343,8498,8342,8498,8497,8343,8344,8499,8343,8499,8498,8344,8345,8500,8344,8500,8499,8345,8346,8501,8345,8501,8500,8346,8347,8502,8346,8502,8501,8347,8348,8503,8347,8503,8502,8348,8349,8504,8348,8504,8503,8349,8350,8505,8349,8505,8504,8350,8351,8506,8350,8506,8505,8351,8352,8507,8351,8507,8506,8352,8353,8508,8352,8508,8507,8353,8354,8509,8353,8509,8508,8354,8355,8510,8354,8510,8509,8355,8356,8511,8355,8511,8510,8356,8357,8512,8356,8512,8511,8357,8358,8513,8357,8513,8512,8358,8359,8514,8358,8514,8513,8359,8360,8515,8359,8515,8514,8360,8361,8516,8360,8516,8515,8361,8362,8517,8361,8517,8516,8362,8363,8518,8362,8518,8517,8363,8364,8519,8363,8519,8518,8364,8365,8520,8364,8520,8519,8365,8366,8521,8365,8521,8520,8366,8367,8522,8366,8522,8521,8367,8368,8523,8367,8523,8522,8368,8369,8524,8368,8524,8523,8370,8371,8526,8370,8526,8525,8371,8372,8527,8371,8527,8526,8372,8373,8528,8372,8528,8527,8373,8374,8529,8373,8529,8528,8374,8375,8530,8374,8530,8529,8375,8376,8531,8375,8531,8530,8376,8377,8532,8376,8532,8531,8377,8378,8533,8377,8533,8532,8378,8379,8534,8378,8534,8533,8379,8380,8535,8379,8535,8534,8380,8381,8536,8380,8536,8535,8381,8382,8537,8381,8537,8536,8382,8383,8538,8382,8538,8537,8383,8384,8539,8383,8539,8538,8384,8385,8540,8384,8540,8539,8385,8386,8541,8385,8541,8540,8386,8387,8542,8386,8542,8541,8387,8388,8543,8387,8543,8542,8388,8389,8544,8388,8544,8543,8389,8390,8545,8389,8545,8544,8390,8391,8546,8390,8546,8545,8391,8392,8547,8391,8547,8546,8392,8393,8548,8392,8548,8547,8393,8394,8549,8393,8549,8548,8394,8395,8550,8394,8550,8549,8395,8396,8551,8395,8551,8550,8396,8397,8552,8396,8552,8551,8397,8398,8553,8397,8553,8552,8398,8399,8554,8398,8554,8553,8399,8400,8555,8399,8555,8554,8400,8401,8556,8400,8556,8555,8401,8402,8557,8401,8557,8556,8402,8403,8558,8402,8558,8557,8403,8404,8559,8403,8559,8558,8404,8405,8560,8404,8560,8559,8405,8406,8561,8405,8561,8560,8406,8407,8562,8406,8562,8561,8407,8408,8563,8407,8563,8562,8408,8409,8564,8408,8564,8563,8409,8410,8565,8409,8565,8564,8410,8411,8566,8410,8566,8565,8411,8412,8567,8411,8567,8566,8412,8413,8568,8412,8568,8567,8413,8414,8569,8413,8569,8568,8414,8415,8570,8414,8570,8569,8415,8416,8571,8415,8571,8570,8416,8417,8572,8416,8572,8571,8417,8418,8573,8417,8573,8572,8418,8419,8574,8418,8574,8573,8419,8420,8575,8419,8575,8574,8420,8421,8576,8420,8576,8575,8421,8422,8577,8421,8577,8576,8422,8423,8578,8422,8578,8577,8423,8424,8579,8423,8579,8578,8424,8425,8580,8424,8580,8579,8425,8426,8581,8425,8581,8580,8426,8427,8582,8426,8582,8581,8427,8428,8583,8427,8583,8582,8428,8429,8584,8428,8584,8583,8429,8430,8585,8429,8585,8584,8430,8431,8586,8430,8586,8585,8431,8432,8587,8431,8587,8586,8432,8433,8588,8432,8588,8587,8433,8434,8589,8433,8589,8588,8434,8435,8590,8434,8590,8589,8435,8436,8591,8435,8591,8590,8436,8437,8592,8436,8592,8591,8437,8438,8593,8437,8593,8592,8438,8439,8594,8438,8594,8593,8439,8440,8595,8439,8595,8594,8440,8441,8596,8440,8596,8595,8441,8442,8597,8441,8597,8596,8442,8443,8598,8442,8598,8597,8443,8444,8599,8443,8599,8598,8444,8445,8600,8444,8600,8599,8445,8446,8601,8445,8601,8600,8446,8447,8602,8446,8602,8601,8447,8448,8603,8447,8603,8602,8448,8449,8604,8448,8604,8603,8449,8450,8605,8449,8605,8604,8450,8451,8606,8450,8606,8605,8451,8452,8607,8451,8607,8606,8452,8453,8608,8452,8608,8607,8453,8454,8609,8453,8609,8608,8454,8455,8610,8454,8610,8609,8455,8456,8611,8455,8611,8610,8456,8457,8612,8456,8612,8611,8457,8458,8613,8457,8613,8612,8458,8459,8614,8458,8614,8613,8459,8460,8615,8459,8615,8614,8460,8461,8616,8460,8616,8615,8461,8462,8617,8461,8617,8616,8462,8463,8618,8462,8618,8617,8463,8464,8619,8463,8619,8618,8464,8465,8620,8464,8620,8619,8465,8466,8621,8465,8621,8620,8466,8467,8622,8466,8622,8621,8467,8468,8623,8467,8623,8622,8468,8469,8624,8468,8624,8623,8469,8470,8625,8469,8625,8624,8470,8471,8626,8470,8626,8625,8471,8472,8627,8471,8627,8626,8472,8473,8628,8472,8628,8627,8473,8474,8629,8473,8629,8628,8474,8475,8630,8474,8630,8629,8475,8476,8631,8475,8631,8630,8476,8477,8632,8476,8632,8631,8477,8478,8633,8477,8633,8632,8478,8479,8634,8478,8634,8633,8479,8480,8635,8479,8635,8634,8480,8481,8636,8480,8636,8635,8481,8482,8637,8481,8637,8636,8482,8483,8638,8482,8638,8637,8483,8484,8639,8483,8639,8638,8484,8485,8640,8484,8640,8639,8485,8486,8641,8485,8641,8640,8486,8487,8642,8486,8642,8641,8487,8488,8643,8487,8643,8642,8488,8489,8644,8488,8644,8643,8489,8490,8645,8489,8645,8644,8490,8491,8646,8490,8646,8645,8491,8492,8647,8491,8647,8646,8492,8493,8648,8492,8648,8647,8493,8494,8649,8493,8649,8648,8494,8495,8650,8494,8650,8649,8495,8496,8651,8495,8651,8650,8496,8497,8652,8496,8652,8651,8497,8498,8653,8497,8653,8652,8498,8499,8654,8498,8654,8653,8499,8500,8655,8499,8655,8654,8500,8501,8656,8500,8656,8655,8501,8502,8657,8501,8657,8656,8502,8503,8658,8502,8658,8657,8503,8504,8659,8503,8659,8658,8504,8505,8660,8504,8660,8659,8505,8506,8661,8505,8661,8660,8506,8507,8662,8506,8662,8661,8507,8508,8663,8507,8663,8662,8508,8509,8664,8508,8664,8663,8509,8510,8665,8509,8665,8664,8510,8511,8666,8510,8666,8665,8511,8512,8667,8511,8667,8666,8512,8513,8668,8512,8668,8667,8513,8514,8669,8513,8669,8668,8514,8515,8670,8514,8670,8669,8515,8516,8671,8515,8671,8670,8516,8517,8672,8516,8672,8671,8517,8518,8673,8517,8673,8672,8518,8519,8674,8518,8674,8673,8519,8520,8675,8519,8675,8674,8520,8521,8676,8520,8676,8675,8521,8522,8677,8521,8677,8676,8522,8523,8678,8522,8678,8677,8523,8524,8679,8523,8679,8678,8525,8526,8681,8525,8681,8680,8526,8527,8682,8526,8682,8681,8527,8528,8683,8527,8683,8682,8528,8529,8684,8528,8684,8683,8529,8530,8685,8529,8685,8684,8530,8531,8686,8530,8686,8685,8531,8532,8687,8531,8687,8686,8532,8533,8688,8532,8688,8687,8533,8534,8689,8533,8689,8688,8534,8535,8690,8534,8690,8689,8535,8536,8691,8535,8691,8690,8536,8537,8692,8536,8692,8691,8537,8538,8693,8537,8693,8692,8538,8539,8694,8538,8694,8693,8539,8540,8695,8539,8695,8694,8540,8541,8696,8540,8696,8695,8541,8542,8697,8541,8697,8696,8542,8543,8698,8542,8698,8697,8543,8544,8699,8543,8699,8698,8544,8545,8700,8544,8700,8699,8545,8546,8701,8545,8701,8700,8546,8547,8702,8546,8702,8701,8547,8548,8703,8547,8703,8702,8548,8549,8704,8548,8704,8703,8549,8550,8705,8549,8705,8704,8550,8551,8706,8550,8706,8705,8551,8552,8707,8551,8707,8706,8552,8553,8708,8552,8708,8707,8553,8554,8709,8553,8709,8708,8554,8555,8710,8554,8710,8709,8555,8556,8711,8555,8711,8710,8556,8557,8712,8556,8712,8711,8557,8558,8713,8557,8713,8712,8558,8559,8714,8558,8714,8713,8559,8560,8715,8559,8715,8714,8560,8561,8716,8560,8716,8715,8561,8562,8717,8561,8717,8716,8562,8563,8718,8562,8718,8717,8563,8564,8719,8563,8719,8718,8564,8565,8720,8564,8720,8719,8565,8566,8721,8565,8721,8720,8566,8567,8722,8566,8722,8721,8567,8568,8723,8567,8723,8722,8568,8569,8724,8568,8724,8723,8569,8570,8725,8569,8725,8724,8570,8571,8726,8570,8726,8725,8571,8572,8727,8571,8727,8726,8572,8573,8728,8572,8728,8727,8573,8574,8729,8573,8729,8728,8574,8575,8730,8574,8730,8729,8575,8576,8731,8575,8731,8730,8576,8577,8732,8576,8732,8731,8577,8578,8733,8577,8733,8732,8578,8579,8734,8578,8734,8733,8579,8580,8735,8579,8735,8734,8580,8581,8736,8580,8736,8735,8581,8582,8737,8581,8737,8736,8582,8583,8738,8582,8738,8737,8583,8584,8739,8583,8739,8738,8584,8585,8740,8584,8740,8739,8585,8586,8741,8585,8741,8740,8586,8587,8742,8586,8742,8741,8587,8588,8743,8587,8743,8742,8588,8589,8744,8588,8744,8743,8589,8590,8745,8589,8745,8744,8590,8591,8746,8590,8746,8745,8591,8592,8747,8591,8747,8746,8592,8593,8748,8592,8748,8747,8593,8594,8749,8593,8749,8748,8594,8595,8750,8594,8750,8749,8595,8596,8751,8595,8751,8750,8596,8597,8752,8596,8752,8751,8597,8598,8753,8597,8753,8752,8598,8599,8754,8598,8754,8753,8599,8600,8755,8599,8755,8754,8600,8601,8756,8600,8756,8755,8601,8602,8757,8601,8757,8756,8602,8603,8758,8602,8758,8757,8603,8604,8759,8603,8759,8758,8604,8605,8760,8604,8760,8759,8605,8606,8761,8605,8761,8760,8606,8607,8762,8606,8762,8761,8607,8608,8763,8607,8763,8762,8608,8609,8764,8608,8764,8763,8609,8610,8765,8609,8765,8764,8610,8611,8766,8610,8766,8765,8611,8612,8767,8611,8767,8766,8612,8613,8768,8612,8768,8767,8613,8614,8769,8613,8769,8768,8614,8615,8770,8614,8770,8769,8615,8616,8771,8615,8771,8770,8616,8617,8772,8616,8772,8771,8617,8618,8773,8617,8773,8772,8618,8619,8774,8618,8774,8773,8619,8620,8775,8619,8775,8774,8620,8621,8776,8620,8776,8775,8621,8622,8777,8621,8777,8776,8622,8623,8778,8622,8778,8777,8623,8624,8779,8623,8779,8778,8624,8625,8780,8624,8780,8779,8625,8626,8781,8625,8781,8780,8626,8627,8782,8626,8782,8781,8627,8628,8783,8627,8783,8782,8628,8629,8784,8628,8784,8783,8629,8630,8785,8629,8785,8784,8630,8631,8786,8630,8786,8785,8631,8632,8787,8631,8787,8786,8632,8633,8788,8632,8788,8787,8633,8634,8789,8633,8789,8788,8634,8635,8790,8634,8790,8789,8635,8636,8791,8635,8791,8790,8636,8637,8792,8636,8792,8791,8637,8638,8793,8637,8793,8792,8638,8639,8794,8638,8794,8793,8639,8640,8795,8639,8795,8794,8640,8641,8796,8640,8796,8795,8641,8642,8797,8641,8797,8796,8642,8643,8798,8642,8798,8797,8643,8644,8799,8643,8799,8798,8644,8645,8800,8644,8800,8799,8645,8646,8801,8645,8801,8800,8646,8647,8802,8646,8802,8801,8647,8648,8803,8647,8803,8802,8648,8649,8804,8648,8804,8803,8649,8650,8805,8649,8805,8804,8650,8651,8806,8650,8806,8805,8651,8652,8807,8651,8807,8806,8652,8653,8808,8652,8808,8807,8653,8654,8809,8653,8809,8808,8654,8655,8810,8654,8810,8809,8655,8656,8811,8655,8811,8810,8656,8657,8812,8656,8812,8811,8657,8658,8813,8657,8813,8812,8658,8659,8814,8658,8814,8813,8659,8660,8815,8659,8815,8814,8660,8661,8816,8660,8816,8815,8661,8662,8817,8661,8817,8816,8662,8663,8818,8662,8818,8817,8663,8664,8819,8663,8819,8818,8664,8665,8820,8664,8820,8819,8665,8666,8821,8665,8821,8820,8666,8667,8822,8666,8822,8821,8667,8668,8823,8667,8823,8822,8668,8669,8824,8668,8824,8823,8669,8670,8825,8669,8825,8824,8670,8671,8826,8670,8826,8825,8671,8672,8827,8671,8827,8826,8672,8673,8828,8672,8828,8827,8673,8674,8829,8673,8829,8828,8674,8675,8830,8674,8830,8829,8675,8676,8831,8675,8831,8830,8676,8677,8832,8676,8832,8831,8677,8678,8833,8677,8833,8832,8678,8679,8834,8678,8834,8833,8680,8681,8836,8680,8836,8835,8681,8682,8837,8681,8837,8836,8682,8683,8838,8682,8838,8837,8683,8684,8839,8683,8839,8838,8684,8685,8840,8684,8840,8839,8685,8686,8841,8685,8841,8840,8686,8687,8842,8686,8842,8841,8687,8688,8843,8687,8843,8842,8688,8689,8844,8688,8844,8843,8689,8690,8845,8689,8845,8844,8690,8691,8846,8690,8846,8845,8691,8692,8847,8691,8847,8846,8692,8693,8848,8692,8848,8847,8693,8694,8849,8693,8849,8848,8694,8695,8850,8694,8850,8849,8695,8696,8851,8695,8851,8850,8696,8697,8852,8696,8852,8851,8697,8698,8853,8697,8853,8852,8698,8699,8854,8698,8854,8853,8699,8700,8855,8699,8855,8854,8700,8701,8856,8700,8856,8855,8701,8702,8857,8701,8857,8856,8702,8703,8858,8702,8858,8857,8703,8704,8859,8703,8859,8858,8704,8705,8860,8704,8860,8859,8705,8706,8861,8705,8861,8860,8706,8707,8862,8706,8862,8861,8707,8708,8863,8707,8863,8862,8708,8709,8864,8708,8864,8863,8709,8710,8865,8709,8865,8864,8710,8711,8866,8710,8866,8865,8711,8712,8867,8711,8867,8866,8712,8713,8868,8712,8868,8867,8713,8714,8869,8713,8869,8868,8714,8715,8870,8714,8870,8869,8715,8716,8871,8715,8871,8870,8716,8717,8872,8716,8872,8871,8717,8718,8873,8717,8873,8872,8718,8719,8874,8718,8874,8873,8719,8720,8875,8719,8875,8874,8720,8721,8876,8720,8876,8875,8721,8722,8877,8721,8877,8876,8722,8723,8878,8722,8878,8877,8723,8724,8879,8723,8879,8878,8724,8725,8880,8724,8880,8879,8725,8726,8881,8725,8881,8880,8726,8727,8882,8726,8882,8881,8727,8728,8883,8727,8883,8882,8728,8729,8884,8728,8884,8883,8729,8730,8885,8729,8885,8884,8730,8731,8886,8730,8886,8885,8731,8732,8887,8731,8887,8886,8732,8733,8888,8732,8888,8887,8733,8734,8889,8733,8889,8888,8734,8735,8890,8734,8890,8889,8735,8736,8891,8735,8891,8890,8736,8737,8892,8736,8892,8891,8737,8738,8893,8737,8893,8892,8738,8739,8894,8738,8894,8893,8739,8740,8895,8739,8895,8894,8740,8741,8896,8740,8896,8895,8741,8742,8897,8741,8897,8896,8742,8743,8898,8742,8898,8897,8743,8744,8899,8743,8899,8898,8744,8745,8900,8744,8900,8899,8745,8746,8901,8745,8901,8900,8746,8747,8902,8746,8902,8901,8747,8748,8903,8747,8903,8902,8748,8749,8904,8748,8904,8903,8749,8750,8905,8749,8905,8904,8750,8751,8906,8750,8906,8905,8751,8752,8907,8751,8907,8906,8752,8753,8908,8752,8908,8907,8753,8754,8909,8753,8909,8908,8754,8755,8910,8754,8910,8909,8755,8756,8911,8755,8911,8910,8756,8757,8912,8756,8912,8911,8757,8758,8913,8757,8913,8912,8758,8759,8914,8758,8914,8913,8759,8760,8915,8759,8915,8914,8760,8761,8916,8760,8916,8915,8761,8762,8917,8761,8917,8916,8762,8763,8918,8762,8918,8917,8763,8764,8919,8763,8919,8918,8764,8765,8920,8764,8920,8919,8765,8766,8921,8765,8921,8920,8766,8767,8922,8766,8922,8921,8767,8768,8923,8767,8923,8922,8768,8769,8924,8768,8924,8923,8769,8770,8925,8769,8925,8924,8770,8771,8926,8770,8926,8925,8771,8772,8927,8771,8927,8926,8772,8773,8928,8772,8928,8927,8773,8774,8929,8773,8929,8928,8774,8775,8930,8774,8930,8929,8775,8776,8931,8775,8931,8930,8776,8777,8932,8776,8932,8931,8777,8778,8933,8777,8933,8932,8778,8779,8934,8778,8934,8933,8779,8780,8935,8779,8935,8934,8780,8781,8936,8780,8936,8935,8781,8782,8937,8781,8937,8936,8782,8783,8938,8782,8938,8937,8783,8784,8939,8783,8939,8938,8784,8785,8940,8784,8940,8939,8785,8786,8941,8785,8941,8940,8786,8787,8942,8786,8942,8941,8787,8788,8943,8787,8943,8942,8788,8789,8944,8788,8944,8943,8789,8790,8945,8789,8945,8944,8790,8791,8946,8790,8946,8945,8791,8792,8947,8791,8947,8946,8792,8793,8948,8792,8948,8947,8793,8794,8949,8793,8949,8948,8794,8795,8950,8794,8950,8949,8795,8796,8951,8795,8951,8950,8796,8797,8952,8796,8952,8951,8797,8798,8953,8797,8953,8952,8798,8799,8954,8798,8954,8953,8799,8800,8955,8799,8955,8954,8800,8801,8956,8800,8956,8955,8801,8802,8957,8801,8957,8956,8802,8803,8958,8802,8958,8957,8803,8804,8959,8803,8959,8958,8804,8805,8960,8804,8960,8959,8805,8806,8961,8805,8961,8960,8806,8807,8962,8806,8962,8961,8807,8808,8963,8807,8963,8962,8808,8809,8964,8808,8964,8963,8809,8810,8965,8809,8965,8964,8810,8811,8966,8810,8966,8965,8811,8812,8967,8811,8967,8966,8812,8813,8968,8812,8968,8967,8813,8814,8969,8813,8969,8968,8814,8815,8970,8814,8970,8969,8815,8816,8971,8815,8971,8970,8816,8817,8972,8816,8972,8971,8817,8818,8973,8817,8973,8972,8818,8819,8974,8818,8974,8973,8819,8820,8975,8819,8975,8974,8820,8821,8976,8820,8976,8975,8821,8822,8977,8821,8977,8976,8822,8823,8978,8822,8978,8977,8823,8824,8979,8823,8979,8978,8824,8825,8980,8824,8980,8979,8825,8826,8981,8825,8981,8980,8826,8827,8982,8826,8982,8981,8827,8828,8983,8827,8983,8982,8828,8829,8984,8828,8984,8983,8829,8830,8985,8829,8985,8984,8830,8831,8986,8830,8986,8985,8831,8832,8987,8831,8987,8986,8832,8833,8988,8832,8988,8987,8833,8834,8989,8833,8989,8988,8835,8836,8991,8835,8991,8990,8836,8837,8992,8836,8992,8991,8837,8838,8993,8837,8993,8992,8838,8839,8994,8838,8994,8993,8839,8840,8995,8839,8995,8994,8840,8841,8996,8840,8996,8995,8841,8842,8997,8841,8997,8996,8842,8843,8998,8842,8998,8997,8843,8844,8999,8843,8999,8998,8844,8845,9000,8844,9000,8999,8845,8846,9001,8845,9001,9000,8846,8847,9002,8846,9002,9001,8847,8848,9003,8847,9003,9002,8848,8849,9004,8848,9004,9003,8849,8850,9005,8849,9005,9004,8850,8851,9006,8850,9006,9005,8851,8852,9007,8851,9007,9006,8852,8853,9008,8852,9008,9007,8853,8854,9009,8853,9009,9008,8854,8855,9010,8854,9010,9009,8855,8856,9011,8855,9011,9010,8856,8857,9012,8856,9012,9011,8857,8858,9013,8857,9013,9012,8858,8859,9014,8858,9014,9013,8859,8860,9015,8859,9015,9014,8860,8861,9016,8860,9016,9015,8861,8862,9017,8861,9017,9016,8862,8863,9018,8862,9018,9017,8863,8864,9019,8863,9019,9018,8864,8865,9020,8864,9020,9019,8865,8866,9021,8865,9021,9020,8866,8867,9022,8866,9022,9021,8867,8868,9023,8867,9023,9022,8868,8869,9024,8868,9024,9023,8869,8870,9025,8869,9025,9024,8870,8871,9026,8870,9026,9025,8871,8872,9027,8871,9027,9026,8872,8873,9028,8872,9028,9027,8873,8874,9029,8873,9029,9028,8874,8875,9030,8874,9030,9029,8875,8876,9031,8875,9031,9030,8876,8877,9032,8876,9032,9031,8877,8878,9033,8877,9033,9032,8878,8879,9034,8878,9034,9033,8879,8880,9035,8879,9035,9034,8880,8881,9036,8880,9036,9035,8881,8882,9037,8881,9037,9036,8882,8883,9038,8882,9038,9037,8883,8884,9039,8883,9039,9038,8884,8885,9040,8884,9040,9039,8885,8886,9041,8885,9041,9040,8886,8887,9042,8886,9042,9041,8887,8888,9043,8887,9043,9042,8888,8889,9044,8888,9044,9043,8889,8890,9045,8889,9045,9044,8890,8891,9046,8890,9046,9045,8891,8892,9047,8891,9047,9046,8892,8893,9048,8892,9048,9047,8893,8894,9049,8893,9049,9048,8894,8895,9050,8894,9050,9049,8895,8896,9051,8895,9051,9050,8896,8897,9052,8896,9052,9051,8897,8898,9053,8897,9053,9052,8898,8899,9054,8898,9054,9053,8899,8900,9055,8899,9055,9054,8900,8901,9056,8900,9056,9055,8901,8902,9057,8901,9057,9056,8902,8903,9058,8902,9058,9057,8903,8904,9059,8903,9059,9058,8904,8905,9060,8904,9060,9059,8905,8906,9061,8905,9061,9060,8906,8907,9062,8906,9062,9061,8907,8908,9063,8907,9063,9062,8908,8909,9064,8908,9064,9063,8909,8910,9065,8909,9065,9064,8910,8911,9066,8910,9066,9065,8911,8912,9067,8911,9067,9066,8912,8913,9068,8912,9068,9067,8913,8914,9069,8913,9069,9068,8914,8915,9070,8914,9070,9069,8915,8916,9071,8915,9071,9070,8916,8917,9072,8916,9072,9071,8917,8918,9073,8917,9073,9072,8918,8919,9074,8918,9074,9073,8919,8920,9075,8919,9075,9074,8920,8921,9076,8920,9076,9075,8921,8922,9077,8921,9077,9076,8922,8923,9078,8922,9078,9077,8923,8924,9079,8923,9079,9078,8924,8925,9080,8924,9080,9079,8925,8926,9081,8925,9081,9080,8926,8927,9082,8926,9082,9081,8927,8928,9083,8927,9083,9082,8928,8929,9084,8928,9084,9083,8929,8930,9085,8929,9085,9084,8930,8931,9086,8930,9086,9085,8931,8932,9087,8931,9087,9086,8932,8933,9088,8932,9088,9087,8933,8934,9089,8933,9089,9088,8934,8935,9090,8934,9090,9089,8935,8936,9091,8935,9091,9090,8936,8937,9092,8936,9092,9091,8937,8938,9093,8937,9093,9092,8938,8939,9094,8938,9094,9093,8939,8940,9095,8939,9095,9094,8940,8941,9096,8940,9096,9095,8941,8942,9097,8941,9097,9096,8942,8943,9098,8942,9098,9097,8943,8944,9099,8943,9099,9098,8944,8945,9100,8944,9100,9099,8945,8946,9101,8945,9101,9100,8946,8947,9102,8946,9102,9101,8947,8948,9103,8947,9103,9102,8948,8949,9104,8948,9104,9103,8949,8950,9105,8949,9105,9104,8950,8951,9106,8950,9106,9105,8951,8952,9107,8951,9107,9106,8952,8953,9108,8952,9108,9107,8953,8954,9109,8953,9109,9108,8954,8955,9110,8954,9110,9109,8955,8956,9111,8955,9111,9110,8956,8957,9112,8956,9112,9111,8957,8958,9113,8957,9113,9112,8958,8959,9114,8958,9114,9113,8959,8960,9115,8959,9115,9114,8960,8961,9116,8960,9116,9115,8961,8962,9117,8961,9117,9116,8962,8963,9118,8962,9118,9117,8963,8964,9119,8963,9119,9118,8964,8965,9120,8964,9120,9119,8965,8966,9121,8965,9121,9120,8966,8967,9122,8966,9122,9121,8967,8968,9123,8967,9123,9122,8968,8969,9124,8968,9124,9123,8969,8970,9125,8969,9125,9124,8970,8971,9126,8970,9126,9125,8971,8972,9127,8971,9127,9126,8972,8973,9128,8972,9128,9127,8973,8974,9129,8973,9129,9128,8974,8975,9130,8974,9130,9129,8975,8976,9131,8975,9131,9130,8976,8977,9132,8976,9132,9131,8977,8978,9133,8977,9133,9132,8978,8979,9134,8978,9134,9133,8979,8980,9135,8979,9135,9134,8980,8981,9136,8980,9136,9135,8981,8982,9137,8981,9137,9136,8982,8983,9138,8982,9138,9137,8983,8984,9139,8983,9139,9138,8984,8985,9140,8984,9140,9139,8985,8986,9141,8985,9141,9140,8986,8987,9142,8986,9142,9141,8987,8988,9143,8987,9143,9142,8988,8989,9144,8988,9144,9143,8990,8991,9146,8990,9146,9145,8991,8992,9147,8991,9147,9146,8992,8993,9148,8992,9148,9147,8993,8994,9149,8993,9149,9148,8994,8995,9150,8994,9150,9149,8995,8996,9151,8995,9151,9150,8996,8997,9152,8996,9152,9151,8997,8998,9153,8997,9153,9152,8998,8999,9154,8998,9154,9153,8999,9000,9155,8999,9155,9154,9000,9001,9156,9000,9156,9155,9001,9002,9157,9001,9157,9156,9002,9003,9158,9002,9158,9157,9003,9004,9159,9003,9159,9158,9004,9005,9160,9004,9160,9159,9005,9006,9161,9005,9161,9160,9006,9007,9162,9006,9162,9161,9007,9008,9163,9007,9163,9162,9008,9009,9164,9008,9164,9163,9009,9010,9165,9009,9165,9164,9010,9011,9166,9010,9166,9165,9011,9012,9167,9011,9167,9166,9012,9013,9168,9012,9168,9167,9013,9014,9169,9013,9169,9168,9014,9015,9170,9014,9170,9169,9015,9016,9171,9015,9171,9170,9016,9017,9172,9016,9172,9171,9017,9018,9173,9017,9173,9172,9018,9019,9174,9018,9174,9173,9019,9020,9175,9019,9175,9174,9020,9021,9176,9020,9176,9175,9021,9022,9177,9021,9177,9176,9022,9023,9178,9022,9178,9177,9023,9024,9179,9023,9179,9178,9024,9025,9180,9024,9180,9179,9025,9026,9181,9025,9181,9180,9026,9027,9182,9026,9182,9181,9027,9028,9183,9027,9183,9182,9028,9029,9184,9028,9184,9183,9029,9030,9185,9029,9185,9184,9030,9031,9186,9030,9186,9185,9031,9032,9187,9031,9187,9186,9032,9033,9188,9032,9188,9187,9033,9034,9189,9033,9189,9188,9034,9035,9190,9034,9190,9189,9035,9036,9191,9035,9191,9190,9036,9037,9192,9036,9192,9191,9037,9038,9193,9037,9193,9192,9038,9039,9194,9038,9194,9193,9039,9040,9195,9039,9195,9194,9040,9041,9196,9040,9196,9195,9041,9042,9197,9041,9197,9196,9042,9043,9198,9042,9198,9197,9043,9044,9199,9043,9199,9198,9044,9045,9200,9044,9200,9199,9045,9046,9201,9045,9201,9200,9046,9047,9202,9046,9202,9201,9047,9048,9203,9047,9203,9202,9048,9049,9204,9048,9204,9203,9049,9050,9205,9049,9205,9204,9050,9051,9206,9050,9206,9205,9051,9052,9207,9051,9207,9206,9052,9053,9208,9052,9208,9207,9053,9054,9209,9053,9209,9208,9054,9055,9210,9054,9210,9209,9055,9056,9211,9055,9211,9210,9056,9057,9212,9056,9212,9211,9057,9058,9213,9057,9213,9212,9058,9059,9214,9058,9214,9213,9059,9060,9215,9059,9215,9214,9060,9061,9216,9060,9216,9215,9061,9062,9217,9061,9217,9216,9062,9063,9218,9062,9218,9217,9063,9064,9219,9063,9219,9218,9064,9065,9220,9064,9220,9219,9065,9066,9221,9065,9221,9220,9066,9067,9222,9066,9222,9221,9067,9068,9223,9067,9223,9222,9068,9069,9224,9068,9224,9223,9069,9070,9225,9069,9225,9224,9070,9071,9226,9070,9226,9225,9071,9072,9227,9071,9227,9226,9072,9073,9228,9072,9228,9227,9073,9074,9229,9073,9229,9228,9074,9075,9230,9074,9230,9229,9075,9076,9231,9075,9231,9230,9076,9077,9232,9076,9232,9231,9077,9078,9233,9077,9233,9232,9078,9079,9234,9078,9234,9233,9079,9080,9235,9079,9235,9234,9080,9081,9236,9080,9236,9235,9081,9082,9237,9081,9237,9236,9082,9083,9238,9082,9238,9237,9083,9084,9239,9083,9239,9238,9084,9085,9240,9084,9240,9239,9085,9086,9241,9085,9241,9240,9086,9087,9242,9086,9242,9241,9087,9088,9243,9087,9243,9242,9088,9089,9244,9088,9244,9243,9089,9090,9245,9089,9245,9244,9090,9091,9246,9090,9246,9245,9091,9092,9247,9091,9247,9246,9092,9093,9248,9092,9248,9247,9093,9094,9249,9093,9249,9248,9094,9095,9250,9094,9250,9249,9095,9096,9251,9095,9251,9250,9096,9097,9252,9096,9252,9251,9097,9098,9253,9097,9253,9252,9098,9099,9254,9098,9254,9253,9099,9100,9255,9099,9255,9254,9100,9101,9256,9100,9256,9255,9101,9102,9257,9101,9257,9256,9102,9103,9258,9102,9258,9257,9103,9104,9259,9103,9259,9258,9104,9105,9260,9104,9260,9259,9105,9106,9261,9105,9261,9260,9106,9107,9262,9106,9262,9261,9107,9108,9263,9107,9263,9262,9108,9109,9264,9108,9264,9263,9109,9110,9265,9109,9265,9264,9110,9111,9266,9110,9266,9265,9111,9112,9267,9111,9267,9266,9112,9113,9268,9112,9268,9267,9113,9114,9269,9113,9269,9268,9114,9115,9270,9114,9270,9269,9115,9116,9271,9115,9271,9270,9116,9117,9272,9116,9272,9271,9117,9118,9273,9117,9273,9272,9118,9119,9274,9118,9274,9273,9119,9120,9275,9119,9275,9274,9120,9121,9276,9120,9276,9275,9121,9122,9277,9121,9277,9276,9122,9123,9278,9122,9278,9277,9123,9124,9279,9123,9279,9278,9124,9125,9280,9124,9280,9279,9125,9126,9281,9125,9281,9280,9126,9127,9282,9126,9282,9281,9127,9128,9283,9127,9283,9282,9128,9129,9284,9128,9284,9283,9129,9130,9285,9129,9285,9284,9130,9131,9286,9130,9286,9285,9131,9132,9287,9131,9287,9286,9132,9133,9288,9132,9288,9287,9133,9134,9289,9133,9289,9288,9134,9135,9290,9134,9290,9289,9135,9136,9291,9135,9291,9290,9136,9137,9292,9136,9292,9291,9137,9138,9293,9137,9293,9292,9138,9139,9294,9138,9294,9293,9139,9140,9295,9139,9295,9294,9140,9141,9296,9140,9296,9295,9141,9142,9297,9141,9297,9296,9142,9143,9298,9142,9298,9297,9143,9144,9299,9143,9299,9298,9145,9146,9301,9145,9301,9300,9146,9147,9302,9146,9302,9301,9147,9148,9303,9147,9303,9302,9148,9149,9304,9148,9304,9303,9149,9150,9305,9149,9305,9304,9150,9151,9306,9150,9306,9305,9151,9152,9307,9151,9307,9306,9152,9153,9308,9152,9308,9307,9153,9154,9309,9153,9309,9308,9154,9155,9310,9154,9310,9309,9155,9156,9311,9155,9311,9310,9156,9157,9312,9156,9312,9311,9157,9158,9313,9157,9313,9312,9158,9159,9314,9158,9314,9313,9159,9160,9315,9159,9315,9314,9160,9161,9316,9160,9316,9315,9161,9162,9317,9161,9317,9316,9162,9163,9318,9162,9318,9317,9163,9164,9319,9163,9319,9318,9164,9165,9320,9164,9320,9319,9165,9166,9321,9165,9321,9320,9166,9167,9322,9166,9322,9321,9167,9168,9323,9167,9323,9322,9168,9169,9324,9168,9324,9323,9169,9170,9325,9169,9325,9324,9170,9171,9326,9170,9326,9325,9171,9172,9327,9171,9327,9326,9172,9173,9328,9172,9328,9327,9173,9174,9329,9173,9329,9328,9174,9175,9330,9174,9330,9329,9175,9176,9331,9175,9331,9330,9176,9177,9332,9176,9332,9331,9177,9178,9333,9177,9333,9332,9178,9179,9334,9178,9334,9333,9179,9180,9335,9179,9335,9334,9180,9181,9336,9180,9336,9335,9181,9182,9337,9181,9337,9336,9182,9183,9338,9182,9338,9337,9183,9184,9339,9183,9339,9338,9184,9185,9340,9184,9340,9339,9185,9186,9341,9185,9341,9340,9186,9187,9342,9186,9342,9341,9187,9188,9343,9187,9343,9342,9188,9189,9344,9188,9344,9343,9189,9190,9345,9189,9345,9344,9190,9191,9346,9190,9346,9345,9191,9192,9347,9191,9347,9346,9192,9193,9348,9192,9348,9347,9193,9194,9349,9193,9349,9348,9194,9195,9350,9194,9350,9349,9195,9196,9351,9195,9351,9350,9196,9197,9352,9196,9352,9351,9197,9198,9353,9197,9353,9352,9198,9199,9354,9198,9354,9353,9199,9200,9355,9199,9355,9354,9200,9201,9356,9200,9356,9355,9201,9202,9357,9201,9357,9356,9202,9203,9358,9202,9358,9357,9203,9204,9359,9203,9359,9358,9204,9205,9360,9204,9360,9359,9205,9206,9361,9205,9361,9360,9206,9207,9362,9206,9362,9361,9207,9208,9363,9207,9363,9362,9208,9209,9364,9208,9364,9363,9209,9210,9365,9209,9365,9364,9210,9211,9366,9210,9366,9365,9211,9212,9367,9211,9367,9366,9212,9213,9368,9212,9368,9367,9213,9214,9369,9213,9369,9368,9214,9215,9370,9214,9370,9369,9215,9216,9371,9215,9371,9370,9216,9217,9372,9216,9372,9371,9217,9218,9373,9217,9373,9372,9218,9219,9374,9218,9374,9373,9219,9220,9375,9219,9375,9374,9220,9221,9376,9220,9376,9375,9221,9222,9377,9221,9377,9376,9222,9223,9378,9222,9378,9377,9223,9224,9379,9223,9379,9378,9224,9225,9380,9224,9380,9379,9225,9226,9381,9225,9381,9380,9226,9227,9382,9226,9382,9381,9227,9228,9383,9227,9383,9382,9228,9229,9384,9228,9384,9383,9229,9230,9385,9229,9385,9384,9230,9231,9386,9230,9386,9385,9231,9232,9387,9231,9387,9386,9232,9233,9388,9232,9388,9387,9233,9234,9389,9233,9389,9388,9234,9235,9390,9234,9390,9389,9235,9236,9391,9235,9391,9390,9236,9237,9392,9236,9392,9391,9237,9238,9393,9237,9393,9392,9238,9239,9394,9238,9394,9393,9239,9240,9395,9239,9395,9394,9240,9241,9396,9240,9396,9395,9241,9242,9397,9241,9397,9396,9242,9243,9398,9242,9398,9397,9243,9244,9399,9243,9399,9398,9244,9245,9400,9244,9400,9399,9245,9246,9401,9245,9401,9400,9246,9247,9402,9246,9402,9401,9247,9248,9403,9247,9403,9402,9248,9249,9404,9248,9404,9403,9249,9250,9405,9249,9405,9404,9250,9251,9406,9250,9406,9405,9251,9252,9407,9251,9407,9406,9252,9253,9408,9252,9408,9407,9253,9254,9409,9253,9409,9408,9254,9255,9410,9254,9410,9409,9255,9256,9411,9255,9411,9410,9256,9257,9412,9256,9412,9411,9257,9258,9413,9257,9413,9412,9258,9259,9414,9258,9414,9413,9259,9260,9415,9259,9415,9414,9260,9261,9416,9260,9416,9415,9261,9262,9417,9261,9417,9416,9262,9263,9418,9262,9418,9417,9263,9264,9419,9263,9419,9418,9264,9265,9420,9264,9420,9419,9265,9266,9421,9265,9421,9420,9266,9267,9422,9266,9422,9421,9267,9268,9423,9267,9423,9422,9268,9269,9424,9268,9424,9423,9269,9270,9425,9269,9425,9424,9270,9271,9426,9270,9426,9425,9271,9272,9427,9271,9427,9426,9272,9273,9428,9272,9428,9427,9273,9274,9429,9273,9429,9428,9274,9275,9430,9274,9430,9429,9275,9276,9431,9275,9431,9430,9276,9277,9432,9276,9432,9431,9277,9278,9433,9277,9433,9432,9278,9279,9434,9278,9434,9433,9279,9280,9435,9279,9435,9434,9280,9281,9436,9280,9436,9435,9281,9282,9437,9281,9437,9436,9282,9283,9438,9282,9438,9437,9283,9284,9439,9283,9439,9438,9284,9285,9440,9284,9440,9439,9285,9286,9441,9285,9441,9440,9286,9287,9442,9286,9442,9441,9287,9288,9443,9287,9443,9442,9288,9289,9444,9288,9444,9443,9289,9290,9445,9289,9445,9444,9290,9291,9446,9290,9446,9445,9291,9292,9447,9291,9447,9446,9292,9293,9448,9292,9448,9447,9293,9294,9449,9293,9449,9448,9294,9295,9450,9294,9450,9449,9295,9296,9451,9295,9451,9450,9296,9297,9452,9296,9452,9451,9297,9298,9453,9297,9453,9452,9298,9299,9454,9298,9454,9453,9300,9301,9456,9300,9456,9455,9301,9302,9457,9301,9457,9456,9302,9303,9458,9302,9458,9457,9303,9304,9459,9303,9459,9458,9304,9305,9460,9304,9460,9459,9305,9306,9461,9305,9461,9460,9306,9307,9462,9306,9462,9461,9307,9308,9463,9307,9463,9462,9308,9309,9464,9308,9464,9463,9309,9310,9465,9309,9465,9464,9310,9311,9466,9310,9466,9465,9311,9312,9467,9311,9467,9466,9312,9313,9468,9312,9468,9467,9313,9314,9469,9313,9469,9468,9314,9315,9470,9314,9470,9469,9315,9316,9471,9315,9471,9470,9316,9317,9472,9316,9472,9471,9317,9318,9473,9317,9473,9472,9318,9319,9474,9318,9474,9473,9319,9320,9475,9319,9475,9474,9320,9321,9476,9320,9476,9475,9321,9322,9477,9321,9477,9476,9322,9323,9478,9322,9478,9477,9323,9324,9479,9323,9479,9478,9324,9325,9480,9324,9480,9479,9325,9326,9481,9325,9481,9480,9326,9327,9482,9326,9482,9481,9327,9328,9483,9327,9483,9482,9328,9329,9484,9328,9484,9483,9329,9330,9485,9329,9485,9484,9330,9331,9486,9330,9486,9485,9331,9332,9487,9331,9487,9486,9332,9333,9488,9332,9488,9487,9333,9334,9489,9333,9489,9488,9334,9335,9490,9334,9490,9489,9335,9336,9491,9335,9491,9490,9336,9337,9492,9336,9492,9491,9337,9338,9493,9337,9493,9492,9338,9339,9494,9338,9494,9493,9339,9340,9495,9339,9495,9494,9340,9341,9496,9340,9496,9495,9341,9342,9497,9341,9497,9496,9342,9343,9498,9342,9498,9497,9343,9344,9499,9343,9499,9498,9344,9345,9500,9344,9500,9499,9345,9346,9501,9345,9501,9500,9346,9347,9502,9346,9502,9501,9347,9348,9503,9347,9503,9502,9348,9349,9504,9348,9504,9503,9349,9350,9505,9349,9505,9504,9350,9351,9506,9350,9506,9505,9351,9352,9507,9351,9507,9506,9352,9353,9508,9352,9508,9507,9353,9354,9509,9353,9509,9508,9354,9355,9510,9354,9510,9509,9355,9356,9511,9355,9511,9510,9356,9357,9512,9356,9512,9511,9357,9358,9513,9357,9513,9512,9358,9359,9514,9358,9514,9513,9359,9360,9515,9359,9515,9514,9360,9361,9516,9360,9516,9515,9361,9362,9517,9361,9517,9516,9362,9363,9518,9362,9518,9517,9363,9364,9519,9363,9519,9518,9364,9365,9520,9364,9520,9519,9365,9366,9521,9365,9521,9520,9366,9367,9522,9366,9522,9521,9367,9368,9523,9367,9523,9522,9368,9369,9524,9368,9524,9523,9369,9370,9525,9369,9525,9524,9370,9371,9526,9370,9526,9525,9371,9372,9527,9371,9527,9526,9372,9373,9528,9372,9528,9527,9373,9374,9529,9373,9529,9528,9374,9375,9530,9374,9530,9529,9375,9376,9531,9375,9531,9530,9376,9377,9532,9376,9532,9531,9377,9378,9533,9377,9533,9532,9378,9379,9534,9378,9534,9533,9379,9380,9535,9379,9535,9534,9380,9381,9536,9380,9536,9535,9381,9382,9537,9381,9537,9536,9382,9383,9538,9382,9538,9537,9383,9384,9539,9383,9539,9538,9384,9385,9540,9384,9540,9539,9385,9386,9541,9385,9541,9540,9386,9387,9542,9386,9542,9541,9387,9388,9543,9387,9543,9542,9388,9389,9544,9388,9544,9543,9389,9390,9545,9389,9545,9544,9390,9391,9546,9390,9546,9545,9391,9392,9547,9391,9547,9546,9392,9393,9548,9392,9548,9547,9393,9394,9549,9393,9549,9548,9394,9395,9550,9394,9550,9549,9395,9396,9551,9395,9551,9550,9396,9397,9552,9396,9552,9551,9397,9398,9553,9397,9553,9552,9398,9399,9554,9398,9554,9553,9399,9400,9555,9399,9555,9554,9400,9401,9556,9400,9556,9555,9401,9402,9557,9401,9557,9556,9402,9403,9558,9402,9558,9557,9403,9404,9559,9403,9559,9558,9404,9405,9560,9404,9560,9559,9405,9406,9561,9405,9561,9560,9406,9407,9562,9406,9562,9561,9407,9408,9563,9407,9563,9562,9408,9409,9564,9408,9564,9563,9409,9410,9565,9409,9565,9564,9410,9411,9566,9410,9566,9565,9411,9412,9567,9411,9567,9566,9412,9413,9568,9412,9568,9567,9413,9414,9569,9413,9569,9568,9414,9415,9570,9414,9570,9569,9415,9416,9571,9415,9571,9570,9416,9417,9572,9416,9572,9571,9417,9418,9573,9417,9573,9572,9418,9419,9574,9418,9574,9573,9419,9420,9575,9419,9575,9574,9420,9421,9576,9420,9576,9575,9421,9422,9577,9421,9577,9576,9422,9423,9578,9422,9578,9577,9423,9424,9579,9423,9579,9578,9424,9425,9580,9424,9580,9579,9425,9426,9581,9425,9581,9580,9426,9427,9582,9426,9582,9581,9427,9428,9583,9427,9583,9582,9428,9429,9584,9428,9584,9583,9429,9430,9585,9429,9585,9584,9430,9431,9586,9430,9586,9585,9431,9432,9587,9431,9587,9586,9432,9433,9588,9432,9588,9587,9433,9434,9589,9433,9589,9588,9434,9435,9590,9434,9590,9589,9435,9436,9591,9435,9591,9590,9436,9437,9592,9436,9592,9591,9437,9438,9593,9437,9593,9592,9438,9439,9594,9438,9594,9593,9439,9440,9595,9439,9595,9594,9440,9441,9596,9440,9596,9595,9441,9442,9597,9441,9597,9596,9442,9443,9598,9442,9598,9597,9443,9444,9599,9443,9599,9598,9444,9445,9600,9444,9600,9599,9445,9446,9601,9445,9601,9600,9446,9447,9602,9446,9602,9601,9447,9448,9603,9447,9603,9602,9448,9449,9604,9448,9604,9603,9449,9450,9605,9449,9605,9604,9450,9451,9606,9450,9606,9605,9451,9452,9607,9451,9607,9606,9452,9453,9608,9452,9608,9607,9453,9454,9609,9453,9609,9608,9455,9456,9611,9455,9611,9610,9456,9457,9612,9456,9612,9611,9457,9458,9613,9457,9613,9612,9458,9459,9614,9458,9614,9613,9459,9460,9615,9459,9615,9614,9460,9461,9616,9460,9616,9615,9461,9462,9617,9461,9617,9616,9462,9463,9618,9462,9618,9617,9463,9464,9619,9463,9619,9618,9464,9465,9620,9464,9620,9619,9465,9466,9621,9465,9621,9620,9466,9467,9622,9466,9622,9621,9467,9468,9623,9467,9623,9622,9468,9469,9624,9468,9624,9623,9469,9470,9625,9469,9625,9624,9470,9471,9626,9470,9626,9625,9471,9472,9627,9471,9627,9626,9472,9473,9628,9472,9628,9627,9473,9474,9629,9473,9629,9628,9474,9475,9630,9474,9630,9629,9475,9476,9631,9475,9631,9630,9476,9477,9632,9476,9632,9631,9477,9478,9633,9477,9633,9632,9478,9479,9634,9478,9634,9633,9479,9480,9635,9479,9635,9634,9480,9481,9636,9480,9636,9635,9481,9482,9637,9481,9637,9636,9482,9483,9638,9482,9638,9637,9483,9484,9639,9483,9639,9638,9484,9485,9640,9484,9640,9639,9485,9486,9641,9485,9641,9640,9486,9487,9642,9486,9642,9641,9487,9488,9643,9487,9643,9642,9488,9489,9644,9488,9644,9643,9489,9490,9645,9489,9645,9644,9490,9491,9646,9490,9646,9645,9491,9492,9647,9491,9647,9646,9492,9493,9648,9492,9648,9647,9493,9494,9649,9493,9649,9648,9494,9495,9650,9494,9650,9649,9495,9496,9651,9495,9651,9650,9496,9497,9652,9496,9652,9651,9497,9498,9653,9497,9653,9652,9498,9499,9654,9498,9654,9653,9499,9500,9655,9499,9655,9654,9500,9501,9656,9500,9656,9655,9501,9502,9657,9501,9657,9656,9502,9503,9658,9502,9658,9657,9503,9504,9659,9503,9659,9658,9504,9505,9660,9504,9660,9659,9505,9506,9661,9505,9661,9660,9506,9507,9662,9506,9662,9661,9507,9508,9663,9507,9663,9662,9508,9509,9664,9508,9664,9663,9509,9510,9665,9509,9665,9664,9510,9511,9666,9510,9666,9665,9511,9512,9667,9511,9667,9666,9512,9513,9668,9512,9668,9667,9513,9514,9669,9513,9669,9668,9514,9515,9670,9514,9670,9669,9515,9516,9671,9515,9671,9670,9516,9517,9672,9516,9672,9671,9517,9518,9673,9517,9673,9672,9518,9519,9674,9518,9674,9673,9519,9520,9675,9519,9675,9674,9520,9521,9676,9520,9676,9675,9521,9522,9677,9521,9677,9676,9522,9523,9678,9522,9678,9677,9523,9524,9679,9523,9679,9678,9524,9525,9680,9524,9680,9679,9525,9526,9681,9525,9681,9680,9526,9527,9682,9526,9682,9681,9527,9528,9683,9527,9683,9682,9528,9529,9684,9528,9684,9683,9529,9530,9685,9529,9685,9684,9530,9531,9686,9530,9686,9685,9531,9532,9687,9531,9687,9686,9532,9533,9688,9532,9688,9687,9533,9534,9689,9533,9689,9688,9534,9535,9690,9534,9690,9689,9535,9536,9691,9535,9691,9690,9536,9537,9692,9536,9692,9691,9537,9538,9693,9537,9693,9692,9538,9539,9694,9538,9694,9693,9539,9540,9695,9539,9695,9694,9540,9541,9696,9540,9696,9695,9541,9542,9697,9541,9697,9696,9542,9543,9698,9542,9698,9697,9543,9544,9699,9543,9699,9698,9544,9545,9700,9544,9700,9699,9545,9546,9701,9545,9701,9700,9546,9547,9702,9546,9702,9701,9547,9548,9703,9547,9703,9702,9548,9549,9704,9548,9704,9703,9549,9550,9705,9549,9705,9704,9550,9551,9706,9550,9706,9705,9551,9552,9707,9551,9707,9706,9552,9553,9708,9552,9708,9707,9553,9554,9709,9553,9709,9708,9554,9555,9710,9554,9710,9709,9555,9556,9711,9555,9711,9710,9556,9557,9712,9556,9712,9711,9557,9558,9713,9557,9713,9712,9558,9559,9714,9558,9714,9713,9559,9560,9715,9559,9715,9714,9560,9561,9716,9560,9716,9715,9561,9562,9717,9561,9717,9716,9562,9563,9718,9562,9718,9717,9563,9564,9719,9563,9719,9718,9564,9565,9720,9564,9720,9719,9565,9566,9721,9565,9721,9720,9566,9567,9722,9566,9722,9721,9567,9568,9723,9567,9723,9722,9568,9569,9724,9568,9724,9723,9569,9570,9725,9569,9725,9724,9570,9571,9726,9570,9726,9725,9571,9572,9727,9571,9727,9726,9572,9573,9728,9572,9728,9727,9573,9574,9729,9573,9729,9728,9574,9575,9730,9574,9730,9729,9575,9576,9731,9575,9731,9730,9576,9577,9732,9576,9732,9731,9577,9578,9733,9577,9733,9732,9578,9579,9734,9578,9734,9733,9579,9580,9735,9579,9735,9734,9580,9581,9736,9580,9736,9735,9581,9582,9737,9581,9737,9736,9582,9583,9738,9582,9738,9737,9583,9584,9739,9583,9739,9738,9584,9585,9740,9584,9740,9739,9585,9586,9741,9585,9741,9740,9586,9587,9742,9586,9742,9741,9587,9588,9743,9587,9743,9742,9588,9589,9744,9588,9744,9743,9589,9590,9745,9589,9745,9744,9590,9591,9746,9590,9746,9745,9591,9592,9747,9591,9747,9746,9592,9593,9748,9592,9748,9747,9593,9594,9749,9593,9749,9748,9594,9595,9750,9594,9750,9749,9595,9596,9751,9595,9751,9750,9596,9597,9752,9596,9752,9751,9597,9598,9753,9597,9753,9752,9598,9599,9754,9598,9754,9753,9599,9600,9755,9599,9755,9754,9600,9601,9756,9600,9756,9755,9601,9602,9757,9601,9757,9756,9602,9603,9758,9602,9758,9757,9603,9604,9759,9603,9759,9758,9604,9605,9760,9604,9760,9759,9605,9606,9761,9605,9761,9760,9606,9607,9762,9606,9762,9761,9607,9608,9763,9607,9763,9762,9608,9609,9764,9608,9764,9763,9610,9611,9766,9610,9766,9765,9611,9612,9767,9611,9767,9766,9612,9613,9768,9612,9768,9767,9613,9614,9769,9613,9769,9768,9614,9615,9770,9614,9770,9769,9615,9616,9771,9615,9771,9770,9616,9617,9772,9616,9772,9771,9617,9618,9773,9617,9773,9772,9618,9619,9774,9618,9774,9773,9619,9620,9775,9619,9775,9774,9620,9621,9776,9620,9776,9775,9621,9622,9777,9621,9777,9776,9622,9623,9778,9622,9778,9777,9623,9624,9779,9623,9779,9778,9624,9625,9780,9624,9780,9779,9625,9626,9781,9625,9781,9780,9626,9627,9782,9626,9782,9781,9627,9628,9783,9627,9783,9782,9628,9629,9784,9628,9784,9783,9629,9630,9785,9629,9785,9784,9630,9631,9786,9630,9786,9785,9631,9632,9787,9631,9787,9786,9632,9633,9788,9632,9788,9787,9633,9634,9789,9633,9789,9788,9634,9635,9790,9634,9790,9789,9635,9636,9791,9635,9791,9790,9636,9637,9792,9636,9792,9791,9637,9638,9793,9637,9793,9792,9638,9639,9794,9638,9794,9793,9639,9640,9795,9639,9795,9794,9640,9641,9796,9640,9796,9795,9641,9642,9797,9641,9797,9796,9642,9643,9798,9642,9798,9797,9643,9644,9799,9643,9799,9798,9644,9645,9800,9644,9800,9799,9645,9646,9801,9645,9801,9800,9646,9647,9802,9646,9802,9801,9647,9648,9803,9647,9803,9802,9648,9649,9804,9648,9804,9803,9649,9650,9805,9649,9805,9804,9650,9651,9806,9650,9806,9805,9651,9652,9807,9651,9807,9806,9652,9653,9808,9652,9808,9807,9653,9654,9809,9653,9809,9808,9654,9655,9810,9654,9810,9809,9655,9656,9811,9655,9811,9810,9656,9657,9812,9656,9812,9811,9657,9658,9813,9657,9813,9812,9658,9659,9814,9658,9814,9813,9659,9660,9815,9659,9815,9814,9660,9661,9816,9660,9816,9815,9661,9662,9817,9661,9817,9816,9662,9663,9818,9662,9818,9817,9663,9664,9819,9663,9819,9818,9664,9665,9820,9664,9820,9819,9665,9666,9821,9665,9821,9820,9666,9667,9822,9666,9822,9821,9667,9668,9823,9667,9823,9822,9668,9669,9824,9668,9824,9823,9669,9670,9825,9669,9825,9824,9670,9671,9826,9670,9826,9825,9671,9672,9827,9671,9827,9826,9672,9673,9828,9672,9828,9827,9673,9674,9829,9673,9829,9828,9674,9675,9830,9674,9830,9829,9675,9676,9831,9675,9831,9830,9676,9677,9832,9676,9832,9831,9677,9678,9833,9677,9833,9832,9678,9679,9834,9678,9834,9833,9679,9680,9835,9679,9835,9834,9680,9681,9836,9680,9836,9835,9681,9682,9837,9681,9837,9836,9682,9683,9838,9682,9838,9837,9683,9684,9839,9683,9839,9838,9684,9685,9840,9684,9840,9839,9685,9686,9841,9685,9841,9840,9686,9687,9842,9686,9842,9841,9687,9688,9843,9687,9843,9842,9688,9689,9844,9688,9844,9843,9689,9690,9845,9689,9845,9844,9690,9691,9846,9690,9846,9845,9691,9692,9847,9691,9847,9846,9692,9693,9848,9692,9848,9847,9693,9694,9849,9693,9849,9848,9694,9695,9850,9694,9850,9849,9695,9696,9851,9695,9851,9850,9696,9697,9852,9696,9852,9851,9697,9698,9853,9697,9853,9852,9698,9699,9854,9698,9854,9853,9699,9700,9855,9699,9855,9854,9700,9701,9856,9700,9856,9855,9701,9702,9857,9701,9857,9856,9702,9703,9858,9702,9858,9857,9703,9704,9859,9703,9859,9858,9704,9705,9860,9704,9860,9859,9705,9706,9861,9705,9861,9860,9706,9707,9862,9706,9862,9861,9707,9708,9863,9707,9863,9862,9708,9709,9864,9708,9864,9863,9709,9710,9865,9709,9865,9864,9710,9711,9866,9710,9866,9865,9711,9712,9867,9711,9867,9866,9712,9713,9868,9712,9868,9867,9713,9714,9869,9713,9869,9868,9714,9715,9870,9714,9870,9869,9715,9716,9871,9715,9871,9870,9716,9717,9872,9716,9872,9871,9717,9718,9873,9717,9873,9872,9718,9719,9874,9718,9874,9873,9719,9720,9875,9719,9875,9874,9720,9721,9876,9720,9876,9875,9721,9722,9877,9721,9877,9876,9722,9723,9878,9722,9878,9877,9723,9724,9879,9723,9879,9878,9724,9725,9880,9724,9880,9879,9725,9726,9881,9725,9881,9880,9726,9727,9882,9726,9882,9881,9727,9728,9883,9727,9883,9882,9728,9729,9884,9728,9884,9883,9729,9730,9885,9729,9885,9884,9730,9731,9886,9730,9886,9885,9731,9732,9887,9731,9887,9886,9732,9733,9888,9732,9888,9887,9733,9734,9889,9733,9889,9888,9734,9735,9890,9734,9890,9889,9735,9736,9891,9735,9891,9890,9736,9737,9892,9736,9892,9891,9737,9738,9893,9737,9893,9892,9738,9739,9894,9738,9894,9893,9739,9740,9895,9739,9895,9894,9740,9741,9896,9740,9896,9895,9741,9742,9897,9741,9897,9896,9742,9743,9898,9742,9898,9897,9743,9744,9899,9743,9899,9898,9744,9745,9900,9744,9900,9899,9745,9746,9901,9745,9901,9900,9746,9747,9902,9746,9902,9901,9747,9748,9903,9747,9903,9902,9748,9749,9904,9748,9904,9903,9749,9750,9905,9749,9905,9904,9750,9751,9906,9750,9906,9905,9751,9752,9907,9751,9907,9906,9752,9753,9908,9752,9908,9907,9753,9754,9909,9753,9909,9908,9754,9755,9910,9754,9910,9909,9755,9756,9911,9755,9911,9910,9756,9757,9912,9756,9912,9911,9757,9758,9913,9757,9913,9912,9758,9759,9914,9758,9914,9913,9759,9760,9915,9759,9915,9914,9760,9761,9916,9760,9916,9915,9761,9762,9917,9761,9917,9916,9762,9763,9918,9762,9918,9917,9763,9764,9919,9763,9919,9918,9765,9766,9921,9765,9921,9920,9766,9767,9922,9766,9922,9921,9767,9768,9923,9767,9923,9922,9768,9769,9924,9768,9924,9923,9769,9770,9925,9769,9925,9924,9770,9771,9926,9770,9926,9925,9771,9772,9927,9771,9927,9926,9772,9773,9928,9772,9928,9927,9773,9774,9929,9773,9929,9928,9774,9775,9930,9774,9930,9929,9775,9776,9931,9775,9931,9930,9776,9777,9932,9776,9932,9931,9777,9778,9933,9777,9933,9932,9778,9779,9934,9778,9934,9933,9779,9780,9935,9779,9935,9934,9780,9781,9936,9780,9936,9935,9781,9782,9937,9781,9937,9936,9782,9783,9938,9782,9938,9937,9783,9784,9939,9783,9939,9938,9784,9785,9940,9784,9940,9939,9785,9786,9941,9785,9941,9940,9786,9787,9942,9786,9942,9941,9787,9788,9943,9787,9943,9942,9788,9789,9944,9788,9944,9943,9789,9790,9945,9789,9945,9944,9790,9791,9946,9790,9946,9945,9791,9792,9947,9791,9947,9946,9792,9793,9948,9792,9948,9947,9793,9794,9949,9793,9949,9948,9794,9795,9950,9794,9950,9949,9795,9796,9951,9795,9951,9950,9796,9797,9952,9796,9952,9951,9797,9798,9953,9797,9953,9952,9798,9799,9954,9798,9954,9953,9799,9800,9955,9799,9955,9954,9800,9801,9956,9800,9956,9955,9801,9802,9957,9801,9957,9956,9802,9803,9958,9802,9958,9957,9803,9804,9959,9803,9959,9958,9804,9805,9960,9804,9960,9959,9805,9806,9961,9805,9961,9960,9806,9807,9962,9806,9962,9961,9807,9808,9963,9807,9963,9962,9808,9809,9964,9808,9964,9963,9809,9810,9965,9809,9965,9964,9810,9811,9966,9810,9966,9965,9811,9812,9967,9811,9967,9966,9812,9813,9968,9812,9968,9967,9813,9814,9969,9813,9969,9968,9814,9815,9970,9814,9970,9969,9815,9816,9971,9815,9971,9970,9816,9817,9972,9816,9972,9971,9817,9818,9973,9817,9973,9972,9818,9819,9974,9818,9974,9973,9819,9820,9975,9819,9975,9974,9820,9821,9976,9820,9976,9975,9821,9822,9977,9821,9977,9976,9822,9823,9978,9822,9978,9977,9823,9824,9979,9823,9979,9978,9824,9825,9980,9824,9980,9979,9825,9826,9981,9825,9981,9980,9826,9827,9982,9826,9982,9981,9827,9828,9983,9827,9983,9982,9828,9829,9984,9828,9984,9983,9829,9830,9985,9829,9985,9984,9830,9831,9986,9830,9986,9985,9831,9832,9987,9831,9987,9986,9832,9833,9988,9832,9988,9987,9833,9834,9989,9833,9989,9988,9834,9835,9990,9834,9990,9989,9835,9836,9991,9835,9991,9990,9836,9837,9992,9836,9992,9991,9837,9838,9993,9837,9993,9992,9838,9839,9994,9838,9994,9993,9839,9840,9995,9839,9995,9994,9840,9841,9996,9840,9996,9995,9841,9842,9997,9841,9997,9996,9842,9843,9998,9842,9998,9997,9843,9844,9999,9843,9999,9998,9844,9845,10000,9844,10000,9999,9845,9846,10001,9845,10001,10000,9846,9847,10002,9846,10002,10001,9847,9848,10003,9847,10003,10002,9848,9849,10004,9848,10004,10003,9849,9850,10005,9849,10005,10004,9850,9851,10006,9850,10006,10005,9851,9852,10007,9851,10007,10006,9852,9853,10008,9852,10008,10007,9853,9854,10009,9853,10009,10008,9854,9855,10010,9854,10010,10009,9855,9856,10011,9855,10011,10010,9856,9857,10012,9856,10012,10011,9857,9858,10013,9857,10013,10012,9858,9859,10014,9858,10014,10013,9859,9860,10015,9859,10015,10014,9860,9861,10016,9860,10016,10015,9861,9862,10017,9861,10017,10016,9862,9863,10018,9862,10018,10017,9863,9864,10019,9863,10019,10018,9864,9865,10020,9864,10020,10019,9865,9866,10021,9865,10021,10020,9866,9867,10022,9866,10022,10021,9867,9868,10023,9867,10023,10022,9868,9869,10024,9868,10024,10023,9869,9870,10025,9869,10025,10024,9870,9871,10026,9870,10026,10025,9871,9872,10027,9871,10027,10026,9872,9873,10028,9872,10028,10027,9873,9874,10029,9873,10029,10028,9874,9875,10030,9874,10030,10029,9875,9876,10031,9875,10031,10030,9876,9877,10032,9876,10032,10031,9877,9878,10033,9877,10033,10032,9878,9879,10034,9878,10034,10033,9879,9880,10035,9879,10035,10034,9880,9881,10036,9880,10036,10035,9881,9882,10037,9881,10037,10036,9882,9883,10038,9882,10038,10037,9883,9884,10039,9883,10039,10038,9884,9885,10040,9884,10040,10039,9885,9886,10041,9885,10041,10040,9886,9887,10042,9886,10042,10041,9887,9888,10043,9887,10043,10042,9888,9889,10044,9888,10044,10043,9889,9890,10045,9889,10045,10044,9890,9891,10046,9890,10046,10045,9891,9892,10047,9891,10047,10046,9892,9893,10048,9892,10048,10047,9893,9894,10049,9893,10049,10048,9894,9895,10050,9894,10050,10049,9895,9896,10051,9895,10051,10050,9896,9897,10052,9896,10052,10051,9897,9898,10053,9897,10053,10052,9898,9899,10054,9898,10054,10053,9899,9900,10055,9899,10055,10054,9900,9901,10056,9900,10056,10055,9901,9902,10057,9901,10057,10056,9902,9903,10058,9902,10058,10057,9903,9904,10059,9903,10059,10058,9904,9905,10060,9904,10060,10059,9905,9906,10061,9905,10061,10060,9906,9907,10062,9906,10062,10061,9907,9908,10063,9907,10063,10062,9908,9909,10064,9908,10064,10063,9909,9910,10065,9909,10065,10064,9910,9911,10066,9910,10066,10065,9911,9912,10067,9911,10067,10066,9912,9913,10068,9912,10068,10067,9913,9914,10069,9913,10069,10068,9914,9915,10070,9914,10070,10069,9915,9916,10071,9915,10071,10070,9916,9917,10072,9916,10072,10071,9917,9918,10073,9917,10073,10072,9918,9919,10074,9918,10074,10073,9920,9921,10076,9920,10076,10075,9921,9922,10077,9921,10077,10076,9922,9923,10078,9922,10078,10077,9923,9924,10079,9923,10079,10078,9924,9925,10080,9924,10080,10079,9925,9926,10081,9925,10081,10080,9926,9927,10082,9926,10082,10081,9927,9928,10083,9927,10083,10082,9928,9929,10084,9928,10084,10083,9929,9930,10085,9929,10085,10084,9930,9931,10086,9930,10086,10085,9931,9932,10087,9931,10087,10086,9932,9933,10088,9932,10088,10087,9933,9934,10089,9933,10089,10088,9934,9935,10090,9934,10090,10089,9935,9936,10091,9935,10091,10090,9936,9937,10092,9936,10092,10091,9937,9938,10093,9937,10093,10092,9938,9939,10094,9938,10094,10093,9939,9940,10095,9939,10095,10094,9940,9941,10096,9940,10096,10095,9941,9942,10097,9941,10097,10096,9942,9943,10098,9942,10098,10097,9943,9944,10099,9943,10099,10098,9944,9945,10100,9944,10100,10099,9945,9946,10101,9945,10101,10100,9946,9947,10102,9946,10102,10101,9947,9948,10103,9947,10103,10102,9948,9949,10104,9948,10104,10103,9949,9950,10105,9949,10105,10104,9950,9951,10106,9950,10106,10105,9951,9952,10107,9951,10107,10106,9952,9953,10108,9952,10108,10107,9953,9954,10109,9953,10109,10108,9954,9955,10110,9954,10110,10109,9955,9956,10111,9955,10111,10110,9956,9957,10112,9956,10112,10111,9957,9958,10113,9957,10113,10112,9958,9959,10114,9958,10114,10113,9959,9960,10115,9959,10115,10114,9960,9961,10116,9960,10116,10115,9961,9962,10117,9961,10117,10116,9962,9963,10118,9962,10118,10117,9963,9964,10119,9963,10119,10118,9964,9965,10120,9964,10120,10119,9965,9966,10121,9965,10121,10120,9966,9967,10122,9966,10122,10121,9967,9968,10123,9967,10123,10122,9968,9969,10124,9968,10124,10123,9969,9970,10125,9969,10125,10124,9970,9971,10126,9970,10126,10125,9971,9972,10127,9971,10127,10126,9972,9973,10128,9972,10128,10127,9973,9974,10129,9973,10129,10128,9974,9975,10130,9974,10130,10129,9975,9976,10131,9975,10131,10130,9976,9977,10132,9976,10132,10131,9977,9978,10133,9977,10133,10132,9978,9979,10134,9978,10134,10133,9979,9980,10135,9979,10135,10134,9980,9981,10136,9980,10136,10135,9981,9982,10137,9981,10137,10136,9982,9983,10138,9982,10138,10137,9983,9984,10139,9983,10139,10138,9984,9985,10140,9984,10140,10139,9985,9986,10141,9985,10141,10140,9986,9987,10142,9986,10142,10141,9987,9988,10143,9987,10143,10142,9988,9989,10144,9988,10144,10143,9989,9990,10145,9989,10145,10144,9990,9991,10146,9990,10146,10145,9991,9992,10147,9991,10147,10146,9992,9993,10148,9992,10148,10147,9993,9994,10149,9993,10149,10148,9994,9995,10150,9994,10150,10149,9995,9996,10151,9995,10151,10150,9996,9997,10152,9996,10152,10151,9997,9998,10153,9997,10153,10152,9998,9999,10154,9998,10154,10153,9999,10000,10155,9999,10155,10154,10000,10001,10156,10000,10156,10155,10001,10002,10157,10001,10157,10156,10002,10003,10158,10002,10158,10157,10003,10004,10159,10003,10159,10158,10004,10005,10160,10004,10160,10159,10005,10006,10161,10005,10161,10160,10006,10007,10162,10006,10162,10161,10007,10008,10163,10007,10163,10162,10008,10009,10164,10008,10164,10163,10009,10010,10165,10009,10165,10164,10010,10011,10166,10010,10166,10165,10011,10012,10167,10011,10167,10166,10012,10013,10168,10012,10168,10167,10013,10014,10169,10013,10169,10168,10014,10015,10170,10014,10170,10169,10015,10016,10171,10015,10171,10170,10016,10017,10172,10016,10172,10171,10017,10018,10173,10017,10173,10172,10018,10019,10174,10018,10174,10173,10019,10020,10175,10019,10175,10174,10020,10021,10176,10020,10176,10175,10021,10022,10177,10021,10177,10176,10022,10023,10178,10022,10178,10177,10023,10024,10179,10023,10179,10178,10024,10025,10180,10024,10180,10179,10025,10026,10181,10025,10181,10180,10026,10027,10182,10026,10182,10181,10027,10028,10183,10027,10183,10182,10028,10029,10184,10028,10184,10183,10029,10030,10185,10029,10185,10184,10030,10031,10186,10030,10186,10185,10031,10032,10187,10031,10187,10186,10032,10033,10188,10032,10188,10187,10033,10034,10189,10033,10189,10188,10034,10035,10190,10034,10190,10189,10035,10036,10191,10035,10191,10190,10036,10037,10192,10036,10192,10191,10037,10038,10193,10037,10193,10192,10038,10039,10194,10038,10194,10193,10039,10040,10195,10039,10195,10194,10040,10041,10196,10040,10196,10195,10041,10042,10197,10041,10197,10196,10042,10043,10198,10042,10198,10197,10043,10044,10199,10043,10199,10198,10044,10045,10200,10044,10200,10199,10045,10046,10201,10045,10201,10200,10046,10047,10202,10046,10202,10201,10047,10048,10203,10047,10203,10202,10048,10049,10204,10048,10204,10203,10049,10050,10205,10049,10205,10204,10050,10051,10206,10050,10206,10205,10051,10052,10207,10051,10207,10206,10052,10053,10208,10052,10208,10207,10053,10054,10209,10053,10209,10208,10054,10055,10210,10054,10210,10209,10055,10056,10211,10055,10211,10210,10056,10057,10212,10056,10212,10211,10057,10058,10213,10057,10213,10212,10058,10059,10214,10058,10214,10213,10059,10060,10215,10059,10215,10214,10060,10061,10216,10060,10216,10215,10061,10062,10217,10061,10217,10216,10062,10063,10218,10062,10218,10217,10063,10064,10219,10063,10219,10218,10064,10065,10220,10064,10220,10219,10065,10066,10221,10065,10221,10220,10066,10067,10222,10066,10222,10221,10067,10068,10223,10067,10223,10222,10068,10069,10224,10068,10224,10223,10069,10070,10225,10069,10225,10224,10070,10071,10226,10070,10226,10225,10071,10072,10227,10071,10227,10226,10072,10073,10228,10072,10228,10227,10073,10074,10229,10073,10229,10228,10075,10076,10231,10075,10231,10230,10076,10077,10232,10076,10232,10231,10077,10078,10233,10077,10233,10232,10078,10079,10234,10078,10234,10233,10079,10080,10235,10079,10235,10234,10080,10081,10236,10080,10236,10235,10081,10082,10237,10081,10237,10236,10082,10083,10238,10082,10238,10237,10083,10084,10239,10083,10239,10238,10084,10085,10240,10084,10240,10239,10085,10086,10241,10085,10241,10240,10086,10087,10242,10086,10242,10241,10087,10088,10243,10087,10243,10242,10088,10089,10244,10088,10244,10243,10089,10090,10245,10089,10245,10244,10090,10091,10246,10090,10246,10245,10091,10092,10247,10091,10247,10246,10092,10093,10248,10092,10248,10247,10093,10094,10249,10093,10249,10248,10094,10095,10250,10094,10250,10249,10095,10096,10251,10095,10251,10250,10096,10097,10252,10096,10252,10251,10097,10098,10253,10097,10253,10252,10098,10099,10254,10098,10254,10253,10099,10100,10255,10099,10255,10254,10100,10101,10256,10100,10256,10255,10101,10102,10257,10101,10257,10256,10102,10103,10258,10102,10258,10257,10103,10104,10259,10103,10259,10258,10104,10105,10260,10104,10260,10259,10105,10106,10261,10105,10261,10260,10106,10107,10262,10106,10262,10261,10107,10108,10263,10107,10263,10262,10108,10109,10264,10108,10264,10263,10109,10110,10265,10109,10265,10264,10110,10111,10266,10110,10266,10265,10111,10112,10267,10111,10267,10266,10112,10113,10268,10112,10268,10267,10113,10114,10269,10113,10269,10268,10114,10115,10270,10114,10270,10269,10115,10116,10271,10115,10271,10270,10116,10117,10272,10116,10272,10271,10117,10118,10273,10117,10273,10272,10118,10119,10274,10118,10274,10273,10119,10120,10275,10119,10275,10274,10120,10121,10276,10120,10276,10275,10121,10122,10277,10121,10277,10276,10122,10123,10278,10122,10278,10277,10123,10124,10279,10123,10279,10278,10124,10125,10280,10124,10280,10279,10125,10126,10281,10125,10281,10280,10126,10127,10282,10126,10282,10281,10127,10128,10283,10127,10283,10282,10128,10129,10284,10128,10284,10283,10129,10130,10285,10129,10285,10284,10130,10131,10286,10130,10286,10285,10131,10132,10287,10131,10287,10286,10132,10133,10288,10132,10288,10287,10133,10134,10289,10133,10289,10288,10134,10135,10290,10134,10290,10289,10135,10136,10291,10135,10291,10290,10136,10137,10292,10136,10292,10291,10137,10138,10293,10137,10293,10292,10138,10139,10294,10138,10294,10293,10139,10140,10295,10139,10295,10294,10140,10141,10296,10140,10296,10295,10141,10142,10297,10141,10297,10296,10142,10143,10298,10142,10298,10297,10143,10144,10299,10143,10299,10298,10144,10145,10300,10144,10300,10299,10145,10146,10301,10145,10301,10300,10146,10147,10302,10146,10302,10301,10147,10148,10303,10147,10303,10302,10148,10149,10304,10148,10304,10303,10149,10150,10305,10149,10305,10304,10150,10151,10306,10150,10306,10305,10151,10152,10307,10151,10307,10306,10152,10153,10308,10152,10308,10307,10153,10154,10309,10153,10309,10308,10154,10155,10310,10154,10310,10309,10155,10156,10311,10155,10311,10310,10156,10157,10312,10156,10312,10311,10157,10158,10313,10157,10313,10312,10158,10159,10314,10158,10314,10313,10159,10160,10315,10159,10315,10314,10160,10161,10316,10160,10316,10315,10161,10162,10317,10161,10317,10316,10162,10163,10318,10162,10318,10317,10163,10164,10319,10163,10319,10318,10164,10165,10320,10164,10320,10319,10165,10166,10321,10165,10321,10320,10166,10167,10322,10166,10322,10321,10167,10168,10323,10167,10323,10322,10168,10169,10324,10168,10324,10323,10169,10170,10325,10169,10325,10324,10170,10171,10326,10170,10326,10325,10171,10172,10327,10171,10327,10326,10172,10173,10328,10172,10328,10327,10173,10174,10329,10173,10329,10328,10174,10175,10330,10174,10330,10329,10175,10176,10331,10175,10331,10330,10176,10177,10332,10176,10332,10331,10177,10178,10333,10177,10333,10332,10178,10179,10334,10178,10334,10333,10179,10180,10335,10179,10335,10334,10180,10181,10336,10180,10336,10335,10181,10182,10337,10181,10337,10336,10182,10183,10338,10182,10338,10337,10183,10184,10339,10183,10339,10338,10184,10185,10340,10184,10340,10339,10185,10186,10341,10185,10341,10340,10186,10187,10342,10186,10342,10341,10187,10188,10343,10187,10343,10342,10188,10189,10344,10188,10344,10343,10189,10190,10345,10189,10345,10344,10190,10191,10346,10190,10346,10345,10191,10192,10347,10191,10347,10346,10192,10193,10348,10192,10348,10347,10193,10194,10349,10193,10349,10348,10194,10195,10350,10194,10350,10349,10195,10196,10351,10195,10351,10350,10196,10197,10352,10196,10352,10351,10197,10198,10353,10197,10353,10352,10198,10199,10354,10198,10354,10353,10199,10200,10355,10199,10355,10354,10200,10201,10356,10200,10356,10355,10201,10202,10357,10201,10357,10356,10202,10203,10358,10202,10358,10357,10203,10204,10359,10203,10359,10358,10204,10205,10360,10204,10360,10359,10205,10206,10361,10205,10361,10360,10206,10207,10362,10206,10362,10361,10207,10208,10363,10207,10363,10362,10208,10209,10364,10208,10364,10363,10209,10210,10365,10209,10365,10364,10210,10211,10366,10210,10366,10365,10211,10212,10367,10211,10367,10366,10212,10213,10368,10212,10368,10367,10213,10214,10369,10213,10369,10368,10214,10215,10370,10214,10370,10369,10215,10216,10371,10215,10371,10370,10216,10217,10372,10216,10372,10371,10217,10218,10373,10217,10373,10372,10218,10219,10374,10218,10374,10373,10219,10220,10375,10219,10375,10374,10220,10221,10376,10220,10376,10375,10221,10222,10377,10221,10377,10376,10222,10223,10378,10222,10378,10377,10223,10224,10379,10223,10379,10378,10224,10225,10380,10224,10380,10379,10225,10226,10381,10225,10381,10380,10226,10227,10382,10226,10382,10381,10227,10228,10383,10227,10383,10382,10228,10229,10384,10228,10384,10383,10230,10231,10386,10230,10386,10385,10231,10232,10387,10231,10387,10386,10232,10233,10388,10232,10388,10387,10233,10234,10389,10233,10389,10388,10234,10235,10390,10234,10390,10389,10235,10236,10391,10235,10391,10390,10236,10237,10392,10236,10392,10391,10237,10238,10393,10237,10393,10392,10238,10239,10394,10238,10394,10393,10239,10240,10395,10239,10395,10394,10240,10241,10396,10240,10396,10395,10241,10242,10397,10241,10397,10396,10242,10243,10398,10242,10398,10397,10243,10244,10399,10243,10399,10398,10244,10245,10400,10244,10400,10399,10245,10246,10401,10245,10401,10400,10246,10247,10402,10246,10402,10401,10247,10248,10403,10247,10403,10402,10248,10249,10404,10248,10404,10403,10249,10250,10405,10249,10405,10404,10250,10251,10406,10250,10406,10405,10251,10252,10407,10251,10407,10406,10252,10253,10408,10252,10408,10407,10253,10254,10409,10253,10409,10408,10254,10255,10410,10254,10410,10409,10255,10256,10411,10255,10411,10410,10256,10257,10412,10256,10412,10411,10257,10258,10413,10257,10413,10412,10258,10259,10414,10258,10414,10413,10259,10260,10415,10259,10415,10414,10260,10261,10416,10260,10416,10415,10261,10262,10417,10261,10417,10416,10262,10263,10418,10262,10418,10417,10263,10264,10419,10263,10419,10418,10264,10265,10420,10264,10420,10419,10265,10266,10421,10265,10421,10420,10266,10267,10422,10266,10422,10421,10267,10268,10423,10267,10423,10422,10268,10269,10424,10268,10424,10423,10269,10270,10425,10269,10425,10424,10270,10271,10426,10270,10426,10425,10271,10272,10427,10271,10427,10426,10272,10273,10428,10272,10428,10427,10273,10274,10429,10273,10429,10428,10274,10275,10430,10274,10430,10429,10275,10276,10431,10275,10431,10430,10276,10277,10432,10276,10432,10431,10277,10278,10433,10277,10433,10432,10278,10279,10434,10278,10434,10433,10279,10280,10435,10279,10435,10434,10280,10281,10436,10280,10436,10435,10281,10282,10437,10281,10437,10436,10282,10283,10438,10282,10438,10437,10283,10284,10439,10283,10439,10438,10284,10285,10440,10284,10440,10439,10285,10286,10441,10285,10441,10440,10286,10287,10442,10286,10442,10441,10287,10288,10443,10287,10443,10442,10288,10289,10444,10288,10444,10443,10289,10290,10445,10289,10445,10444,10290,10291,10446,10290,10446,10445,10291,10292,10447,10291,10447,10446,10292,10293,10448,10292,10448,10447,10293,10294,10449,10293,10449,10448,10294,10295,10450,10294,10450,10449,10295,10296,10451,10295,10451,10450,10296,10297,10452,10296,10452,10451,10297,10298,10453,10297,10453,10452,10298,10299,10454,10298,10454,10453,10299,10300,10455,10299,10455,10454,10300,10301,10456,10300,10456,10455,10301,10302,10457,10301,10457,10456,10302,10303,10458,10302,10458,10457,10303,10304,10459,10303,10459,10458,10304,10305,10460,10304,10460,10459,10305,10306,10461,10305,10461,10460,10306,10307,10462,10306,10462,10461,10307,10308,10463,10307,10463,10462,10308,10309,10464,10308,10464,10463,10309,10310,10465,10309,10465,10464,10310,10311,10466,10310,10466,10465,10311,10312,10467,10311,10467,10466,10312,10313,10468,10312,10468,10467,10313,10314,10469,10313,10469,10468,10314,10315,10470,10314,10470,10469,10315,10316,10471,10315,10471,10470,10316,10317,10472,10316,10472,10471,10317,10318,10473,10317,10473,10472,10318,10319,10474,10318,10474,10473,10319,10320,10475,10319,10475,10474,10320,10321,10476,10320,10476,10475,10321,10322,10477,10321,10477,10476,10322,10323,10478,10322,10478,10477,10323,10324,10479,10323,10479,10478,10324,10325,10480,10324,10480,10479,10325,10326,10481,10325,10481,10480,10326,10327,10482,10326,10482,10481,10327,10328,10483,10327,10483,10482,10328,10329,10484,10328,10484,10483,10329,10330,10485,10329,10485,10484,10330,10331,10486,10330,10486,10485,10331,10332,10487,10331,10487,10486,10332,10333,10488,10332,10488,10487,10333,10334,10489,10333,10489,10488,10334,10335,10490,10334,10490,10489,10335,10336,10491,10335,10491,10490,10336,10337,10492,10336,10492,10491,10337,10338,10493,10337,10493,10492,10338,10339,10494,10338,10494,10493,10339,10340,10495,10339,10495,10494,10340,10341,10496,10340,10496,10495,10341,10342,10497,10341,10497,10496,10342,10343,10498,10342,10498,10497,10343,10344,10499,10343,10499,10498,10344,10345,10500,10344,10500,10499,10345,10346,10501,10345,10501,10500,10346,10347,10502,10346,10502,10501,10347,10348,10503,10347,10503,10502,10348,10349,10504,10348,10504,10503,10349,10350,10505,10349,10505,10504,10350,10351,10506,10350,10506,10505,10351,10352,10507,10351,10507,10506,10352,10353,10508,10352,10508,10507,10353,10354,10509,10353,10509,10508,10354,10355,10510,10354,10510,10509,10355,10356,10511,10355,10511,10510,10356,10357,10512,10356,10512,10511,10357,10358,10513,10357,10513,10512,10358,10359,10514,10358,10514,10513,10359,10360,10515,10359,10515,10514,10360,10361,10516,10360,10516,10515,10361,10362,10517,10361,10517,10516,10362,10363,10518,10362,10518,10517,10363,10364,10519,10363,10519,10518,10364,10365,10520,10364,10520,10519,10365,10366,10521,10365,10521,10520,10366,10367,10522,10366,10522,10521,10367,10368,10523,10367,10523,10522,10368,10369,10524,10368,10524,10523,10369,10370,10525,10369,10525,10524,10370,10371,10526,10370,10526,10525,10371,10372,10527,10371,10527,10526,10372,10373,10528,10372,10528,10527,10373,10374,10529,10373,10529,10528,10374,10375,10530,10374,10530,10529,10375,10376,10531,10375,10531,10530,10376,10377,10532,10376,10532,10531,10377,10378,10533,10377,10533,10532,10378,10379,10534,10378,10534,10533,10379,10380,10535,10379,10535,10534,10380,10381,10536,10380,10536,10535,10381,10382,10537,10381,10537,10536,10382,10383,10538,10382,10538,10537,10383,10384,10539,10383,10539,10538,10385,10386,10541,10385,10541,10540,10386,10387,10542,10386,10542,10541,10387,10388,10543,10387,10543,10542,10388,10389,10544,10388,10544,10543,10389,10390,10545,10389,10545,10544,10390,10391,10546,10390,10546,10545,10391,10392,10547,10391,10547,10546,10392,10393,10548,10392,10548,10547,10393,10394,10549,10393,10549,10548,10394,10395,10550,10394,10550,10549,10395,10396,10551,10395,10551,10550,10396,10397,10552,10396,10552,10551,10397,10398,10553,10397,10553,10552,10398,10399,10554,10398,10554,10553,10399,10400,10555,10399,10555,10554,10400,10401,10556,10400,10556,10555,10401,10402,10557,10401,10557,10556,10402,10403,10558,10402,10558,10557,10403,10404,10559,10403,10559,10558,10404,10405,10560,10404,10560,10559,10405,10406,10561,10405,10561,10560,10406,10407,10562,10406,10562,10561,10407,10408,10563,10407,10563,10562,10408,10409,10564,10408,10564,10563,10409,10410,10565,10409,10565,10564,10410,10411,10566,10410,10566,10565,10411,10412,10567,10411,10567,10566,10412,10413,10568,10412,10568,10567,10413,10414,10569,10413,10569,10568,10414,10415,10570,10414,10570,10569,10415,10416,10571,10415,10571,10570,10416,10417,10572,10416,10572,10571,10417,10418,10573,10417,10573,10572,10418,10419,10574,10418,10574,10573,10419,10420,10575,10419,10575,10574,10420,10421,10576,10420,10576,10575,10421,10422,10577,10421,10577,10576,10422,10423,10578,10422,10578,10577,10423,10424,10579,10423,10579,10578,10424,10425,10580,10424,10580,10579,10425,10426,10581,10425,10581,10580,10426,10427,10582,10426,10582,10581,10427,10428,10583,10427,10583,10582,10428,10429,10584,10428,10584,10583,10429,10430,10585,10429,10585,10584,10430,10431,10586,10430,10586,10585,10431,10432,10587,10431,10587,10586,10432,10433,10588,10432,10588,10587,10433,10434,10589,10433,10589,10588,10434,10435,10590,10434,10590,10589,10435,10436,10591,10435,10591,10590,10436,10437,10592,10436,10592,10591,10437,10438,10593,10437,10593,10592,10438,10439,10594,10438,10594,10593,10439,10440,10595,10439,10595,10594,10440,10441,10596,10440,10596,10595,10441,10442,10597,10441,10597,10596,10442,10443,10598,10442,10598,10597,10443,10444,10599,10443,10599,10598,10444,10445,10600,10444,10600,10599,10445,10446,10601,10445,10601,10600,10446,10447,10602,10446,10602,10601,10447,10448,10603,10447,10603,10602,10448,10449,10604,10448,10604,10603,10449,10450,10605,10449,10605,10604,10450,10451,10606,10450,10606,10605,10451,10452,10607,10451,10607,10606,10452,10453,10608,10452,10608,10607,10453,10454,10609,10453,10609,10608,10454,10455,10610,10454,10610,10609,10455,10456,10611,10455,10611,10610,10456,10457,10612,10456,10612,10611,10457,10458,10613,10457,10613,10612,10458,10459,10614,10458,10614,10613,10459,10460,10615,10459,10615,10614,10460,10461,10616,10460,10616,10615,10461,10462,10617,10461,10617,10616,10462,10463,10618,10462,10618,10617,10463,10464,10619,10463,10619,10618,10464,10465,10620,10464,10620,10619,10465,10466,10621,10465,10621,10620,10466,10467,10622,10466,10622,10621,10467,10468,10623,10467,10623,10622,10468,10469,10624,10468,10624,10623,10469,10470,10625,10469,10625,10624,10470,10471,10626,10470,10626,10625,10471,10472,10627,10471,10627,10626,10472,10473,10628,10472,10628,10627,10473,10474,10629,10473,10629,10628,10474,10475,10630,10474,10630,10629,10475,10476,10631,10475,10631,10630,10476,10477,10632,10476,10632,10631,10477,10478,10633,10477,10633,10632,10478,10479,10634,10478,10634,10633,10479,10480,10635,10479,10635,10634,10480,10481,10636,10480,10636,10635,10481,10482,10637,10481,10637,10636,10482,10483,10638,10482,10638,10637,10483,10484,10639,10483,10639,10638,10484,10485,10640,10484,10640,10639,10485,10486,10641,10485,10641,10640,10486,10487,10642,10486,10642,10641,10487,10488,10643,10487,10643,10642,10488,10489,10644,10488,10644,10643,10489,10490,10645,10489,10645,10644,10490,10491,10646,10490,10646,10645,10491,10492,10647,10491,10647,10646,10492,10493,10648,10492,10648,10647,10493,10494,10649,10493,10649,10648,10494,10495,10650,10494,10650,10649,10495,10496,10651,10495,10651,10650,10496,10497,10652,10496,10652,10651,10497,10498,10653,10497,10653,10652,10498,10499,10654,10498,10654,10653,10499,10500,10655,10499,10655,10654,10500,10501,10656,10500,10656,10655,10501,10502,10657,10501,10657,10656,10502,10503,10658,10502,10658,10657,10503,10504,10659,10503,10659,10658,10504,10505,10660,10504,10660,10659,10505,10506,10661,10505,10661,10660,10506,10507,10662,10506,10662,10661,10507,10508,10663,10507,10663,10662,10508,10509,10664,10508,10664,10663,10509,10510,10665,10509,10665,10664,10510,10511,10666,10510,10666,10665,10511,10512,10667,10511,10667,10666,10512,10513,10668,10512,10668,10667,10513,10514,10669,10513,10669,10668,10514,10515,10670,10514,10670,10669,10515,10516,10671,10515,10671,10670,10516,10517,10672,10516,10672,10671,10517,10518,10673,10517,10673,10672,10518,10519,10674,10518,10674,10673,10519,10520,10675,10519,10675,10674,10520,10521,10676,10520,10676,10675,10521,10522,10677,10521,10677,10676,10522,10523,10678,10522,10678,10677,10523,10524,10679,10523,10679,10678,10524,10525,10680,10524,10680,10679,10525,10526,10681,10525,10681,10680,10526,10527,10682,10526,10682,10681,10527,10528,10683,10527,10683,10682,10528,10529,10684,10528,10684,10683,10529,10530,10685,10529,10685,10684,10530,10531,10686,10530,10686,10685,10531,10532,10687,10531,10687,10686,10532,10533,10688,10532,10688,10687,10533,10534,10689,10533,10689,10688,10534,10535,10690,10534,10690,10689,10535,10536,10691,10535,10691,10690,10536,10537,10692,10536,10692,10691,10537,10538,10693,10537,10693,10692,10538,10539,10694,10538,10694,10693,10540,10541,10696,10540,10696,10695,10541,10542,10697,10541,10697,10696,10542,10543,10698,10542,10698,10697,10543,10544,10699,10543,10699,10698,10544,10545,10700,10544,10700,10699,10545,10546,10701,10545,10701,10700,10546,10547,10702,10546,10702,10701,10547,10548,10703,10547,10703,10702,10548,10549,10704,10548,10704,10703,10549,10550,10705,10549,10705,10704,10550,10551,10706,10550,10706,10705,10551,10552,10707,10551,10707,10706,10552,10553,10708,10552,10708,10707,10553,10554,10709,10553,10709,10708,10554,10555,10710,10554,10710,10709,10555,10556,10711,10555,10711,10710,10556,10557,10712,10556,10712,10711,10557,10558,10713,10557,10713,10712,10558,10559,10714,10558,10714,10713,10559,10560,10715,10559,10715,10714,10560,10561,10716,10560,10716,10715,10561,10562,10717,10561,10717,10716,10562,10563,10718,10562,10718,10717,10563,10564,10719,10563,10719,10718,10564,10565,10720,10564,10720,10719,10565,10566,10721,10565,10721,10720,10566,10567,10722,10566,10722,10721,10567,10568,10723,10567,10723,10722,10568,10569,10724,10568,10724,10723,10569,10570,10725,10569,10725,10724,10570,10571,10726,10570,10726,10725,10571,10572,10727,10571,10727,10726,10572,10573,10728,10572,10728,10727,10573,10574,10729,10573,10729,10728,10574,10575,10730,10574,10730,10729,10575,10576,10731,10575,10731,10730,10576,10577,10732,10576,10732,10731,10577,10578,10733,10577,10733,10732,10578,10579,10734,10578,10734,10733,10579,10580,10735,10579,10735,10734,10580,10581,10736,10580,10736,10735,10581,10582,10737,10581,10737,10736,10582,10583,10738,10582,10738,10737,10583,10584,10739,10583,10739,10738,10584,10585,10740,10584,10740,10739,10585,10586,10741,10585,10741,10740,10586,10587,10742,10586,10742,10741,10587,10588,10743,10587,10743,10742,10588,10589,10744,10588,10744,10743,10589,10590,10745,10589,10745,10744,10590,10591,10746,10590,10746,10745,10591,10592,10747,10591,10747,10746,10592,10593,10748,10592,10748,10747,10593,10594,10749,10593,10749,10748,10594,10595,10750,10594,10750,10749,10595,10596,10751,10595,10751,10750,10596,10597,10752,10596,10752,10751,10597,10598,10753,10597,10753,10752,10598,10599,10754,10598,10754,10753,10599,10600,10755,10599,10755,10754,10600,10601,10756,10600,10756,10755,10601,10602,10757,10601,10757,10756,10602,10603,10758,10602,10758,10757,10603,10604,10759,10603,10759,10758,10604,10605,10760,10604,10760,10759,10605,10606,10761,10605,10761,10760,10606,10607,10762,10606,10762,10761,10607,10608,10763,10607,10763,10762,10608,10609,10764,10608,10764,10763,10609,10610,10765,10609,10765,10764,10610,10611,10766,10610,10766,10765,10611,10612,10767,10611,10767,10766,10612,10613,10768,10612,10768,10767,10613,10614,10769,10613,10769,10768,10614,10615,10770,10614,10770,10769,10615,10616,10771,10615,10771,10770,10616,10617,10772,10616,10772,10771,10617,10618,10773,10617,10773,10772,10618,10619,10774,10618,10774,10773,10619,10620,10775,10619,10775,10774,10620,10621,10776,10620,10776,10775,10621,10622,10777,10621,10777,10776,10622,10623,10778,10622,10778,10777,10623,10624,10779,10623,10779,10778,10624,10625,10780,10624,10780,10779,10625,10626,10781,10625,10781,10780,10626,10627,10782,10626,10782,10781,10627,10628,10783,10627,10783,10782,10628,10629,10784,10628,10784,10783,10629,10630,10785,10629,10785,10784,10630,10631,10786,10630,10786,10785,10631,10632,10787,10631,10787,10786,10632,10633,10788,10632,10788,10787,10633,10634,10789,10633,10789,10788,10634,10635,10790,10634,10790,10789,10635,10636,10791,10635,10791,10790,10636,10637,10792,10636,10792,10791,10637,10638,10793,10637,10793,10792,10638,10639,10794,10638,10794,10793,10639,10640,10795,10639,10795,10794,10640,10641,10796,10640,10796,10795,10641,10642,10797,10641,10797,10796,10642,10643,10798,10642,10798,10797,10643,10644,10799,10643,10799,10798,10644,10645,10800,10644,10800,10799,10645,10646,10801,10645,10801,10800,10646,10647,10802,10646,10802,10801,10647,10648,10803,10647,10803,10802,10648,10649,10804,10648,10804,10803,10649,10650,10805,10649,10805,10804,10650,10651,10806,10650,10806,10805,10651,10652,10807,10651,10807,10806,10652,10653,10808,10652,10808,10807,10653,10654,10809,10653,10809,10808,10654,10655,10810,10654,10810,10809,10655,10656,10811,10655,10811,10810,10656,10657,10812,10656,10812,10811,10657,10658,10813,10657,10813,10812,10658,10659,10814,10658,10814,10813,10659,10660,10815,10659,10815,10814,10660,10661,10816,10660,10816,10815,10661,10662,10817,10661,10817,10816,10662,10663,10818,10662,10818,10817,10663,10664,10819,10663,10819,10818,10664,10665,10820,10664,10820,10819,10665,10666,10821,10665,10821,10820,10666,10667,10822,10666,10822,10821,10667,10668,10823,10667,10823,10822,10668,10669,10824,10668,10824,10823,10669,10670,10825,10669,10825,10824,10670,10671,10826,10670,10826,10825,10671,10672,10827,10671,10827,10826,10672,10673,10828,10672,10828,10827,10673,10674,10829,10673,10829,10828,10674,10675,10830,10674,10830,10829,10675,10676,10831,10675,10831,10830,10676,10677,10832,10676,10832,10831,10677,10678,10833,10677,10833,10832,10678,10679,10834,10678,10834,10833,10679,10680,10835,10679,10835,10834,10680,10681,10836,10680,10836,10835,10681,10682,10837,10681,10837,10836,10682,10683,10838,10682,10838,10837,10683,10684,10839,10683,10839,10838,10684,10685,10840,10684,10840,10839,10685,10686,10841,10685,10841,10840,10686,10687,10842,10686,10842,10841,10687,10688,10843,10687,10843,10842,10688,10689,10844,10688,10844,10843,10689,10690,10845,10689,10845,10844,10690,10691,10846,10690,10846,10845,10691,10692,10847,10691,10847,10846,10692,10693,10848,10692,10848,10847,10693,10694,10849,10693,10849,10848,10695,10696,10851,10695,10851,10850,10696,10697,10852,10696,10852,10851,10697,10698,10853,10697,10853,10852,10698,10699,10854,10698,10854,10853,10699,10700,10855,10699,10855,10854,10700,10701,10856,10700,10856,10855,10701,10702,10857,10701,10857,10856,10702,10703,10858,10702,10858,10857,10703,10704,10859,10703,10859,10858,10704,10705,10860,10704,10860,10859,10705,10706,10861,10705,10861,10860,10706,10707,10862,10706,10862,10861,10707,10708,10863,10707,10863,10862,10708,10709,10864,10708,10864,10863,10709,10710,10865,10709,10865,10864,10710,10711,10866,10710,10866,10865,10711,10712,10867,10711,10867,10866,10712,10713,10868,10712,10868,10867,10713,10714,10869,10713,10869,10868,10714,10715,10870,10714,10870,10869,10715,10716,10871,10715,10871,10870,10716,10717,10872,10716,10872,10871,10717,10718,10873,10717,10873,10872,10718,10719,10874,10718,10874,10873,10719,10720,10875,10719,10875,10874,10720,10721,10876,10720,10876,10875,10721,10722,10877,10721,10877,10876,10722,10723,10878,10722,10878,10877,10723,10724,10879,10723,10879,10878,10724,10725,10880,10724,10880,10879,10725,10726,10881,10725,10881,10880,10726,10727,10882,10726,10882,10881,10727,10728,10883,10727,10883,10882,10728,10729,10884,10728,10884,10883,10729,10730,10885,10729,10885,10884,10730,10731,10886,10730,10886,10885,10731,10732,10887,10731,10887,10886,10732,10733,10888,10732,10888,10887,10733,10734,10889,10733,10889,10888,10734,10735,10890,10734,10890,10889,10735,10736,10891,10735,10891,10890,10736,10737,10892,10736,10892,10891,10737,10738,10893,10737,10893,10892,10738,10739,10894,10738,10894,10893,10739,10740,10895,10739,10895,10894,10740,10741,10896,10740,10896,10895,10741,10742,10897,10741,10897,10896,10742,10743,10898,10742,10898,10897,10743,10744,10899,10743,10899,10898,10744,10745,10900,10744,10900,10899,10745,10746,10901,10745,10901,10900,10746,10747,10902,10746,10902,10901,10747,10748,10903,10747,10903,10902,10748,10749,10904,10748,10904,10903,10749,10750,10905,10749,10905,10904,10750,10751,10906,10750,10906,10905,10751,10752,10907,10751,10907,10906,10752,10753,10908,10752,10908,10907,10753,10754,10909,10753,10909,10908,10754,10755,10910,10754,10910,10909,10755,10756,10911,10755,10911,10910,10756,10757,10912,10756,10912,10911,10757,10758,10913,10757,10913,10912,10758,10759,10914,10758,10914,10913,10759,10760,10915,10759,10915,10914,10760,10761,10916,10760,10916,10915,10761,10762,10917,10761,10917,10916,10762,10763,10918,10762,10918,10917,10763,10764,10919,10763,10919,10918,10764,10765,10920,10764,10920,10919,10765,10766,10921,10765,10921,10920,10766,10767,10922,10766,10922,10921,10767,10768,10923,10767,10923,10922,10768,10769,10924,10768,10924,10923,10769,10770,10925,10769,10925,10924,10770,10771,10926,10770,10926,10925,10771,10772,10927,10771,10927,10926,10772,10773,10928,10772,10928,10927,10773,10774,10929,10773,10929,10928,10774,10775,10930,10774,10930,10929,10775,10776,10931,10775,10931,10930,10776,10777,10932,10776,10932,10931,10777,10778,10933,10777,10933,10932,10778,10779,10934,10778,10934,10933,10779,10780,10935,10779,10935,10934,10780,10781,10936,10780,10936,10935,10781,10782,10937,10781,10937,10936,10782,10783,10938,10782,10938,10937,10783,10784,10939,10783,10939,10938,10784,10785,10940,10784,10940,10939,10785,10786,10941,10785,10941,10940,10786,10787,10942,10786,10942,10941,10787,10788,10943,10787,10943,10942,10788,10789,10944,10788,10944,10943,10789,10790,10945,10789,10945,10944,10790,10791,10946,10790,10946,10945,10791,10792,10947,10791,10947,10946,10792,10793,10948,10792,10948,10947,10793,10794,10949,10793,10949,10948,10794,10795,10950,10794,10950,10949,10795,10796,10951,10795,10951,10950,10796,10797,10952,10796,10952,10951,10797,10798,10953,10797,10953,10952,10798,10799,10954,10798,10954,10953,10799,10800,10955,10799,10955,10954,10800,10801,10956,10800,10956,10955,10801,10802,10957,10801,10957,10956,10802,10803,10958,10802,10958,10957,10803,10804,10959,10803,10959,10958,10804,10805,10960,10804,10960,10959,10805,10806,10961,10805,10961,10960,10806,10807,10962,10806,10962,10961,10807,10808,10963,10807,10963,10962,10808,10809,10964,10808,10964,10963,10809,10810,10965,10809,10965,10964,10810,10811,10966,10810,10966,10965,10811,10812,10967,10811,10967,10966,10812,10813,10968,10812,10968,10967,10813,10814,10969,10813,10969,10968,10814,10815,10970,10814,10970,10969,10815,10816,10971,10815,10971,10970,10816,10817,10972,10816,10972,10971,10817,10818,10973,10817,10973,10972,10818,10819,10974,10818,10974,10973,10819,10820,10975,10819,10975,10974,10820,10821,10976,10820,10976,10975,10821,10822,10977,10821,10977,10976,10822,10823,10978,10822,10978,10977,10823,10824,10979,10823,10979,10978,10824,10825,10980,10824,10980,10979,10825,10826,10981,10825,10981,10980,10826,10827,10982,10826,10982,10981,10827,10828,10983,10827,10983,10982,10828,10829,10984,10828,10984,10983,10829,10830,10985,10829,10985,10984,10830,10831,10986,10830,10986,10985,10831,10832,10987,10831,10987,10986,10832,10833,10988,10832,10988,10987,10833,10834,10989,10833,10989,10988,10834,10835,10990,10834,10990,10989,10835,10836,10991,10835,10991,10990,10836,10837,10992,10836,10992,10991,10837,10838,10993,10837,10993,10992,10838,10839,10994,10838,10994,10993,10839,10840,10995,10839,10995,10994,10840,10841,10996,10840,10996,10995,10841,10842,10997,10841,10997,10996,10842,10843,10998,10842,10998,10997,10843,10844,10999,10843,10999,10998,10844,10845,11000,10844,11000,10999,10845,10846,11001,10845,11001,11000,10846,10847,11002,10846,11002,11001,10847,10848,11003,10847,11003,11002,10848,10849,11004,10848,11004,11003,10850,10851,11006,10850,11006,11005,10851,10852,11007,10851,11007,11006,10852,10853,11008,10852,11008,11007,10853,10854,11009,10853,11009,11008,10854,10855,11010,10854,11010,11009,10855,10856,11011,10855,11011,11010,10856,10857,11012,10856,11012,11011,10857,10858,11013,10857,11013,11012,10858,10859,11014,10858,11014,11013,10859,10860,11015,10859,11015,11014,10860,10861,11016,10860,11016,11015,10861,10862,11017,10861,11017,11016,10862,10863,11018,10862,11018,11017,10863,10864,11019,10863,11019,11018,10864,10865,11020,10864,11020,11019,10865,10866,11021,10865,11021,11020,10866,10867,11022,10866,11022,11021,10867,10868,11023,10867,11023,11022,10868,10869,11024,10868,11024,11023,10869,10870,11025,10869,11025,11024,10870,10871,11026,10870,11026,11025,10871,10872,11027,10871,11027,11026,10872,10873,11028,10872,11028,11027,10873,10874,11029,10873,11029,11028,10874,10875,11030,10874,11030,11029,10875,10876,11031,10875,11031,11030,10876,10877,11032,10876,11032,11031,10877,10878,11033,10877,11033,11032,10878,10879,11034,10878,11034,11033,10879,10880,11035,10879,11035,11034,10880,10881,11036,10880,11036,11035,10881,10882,11037,10881,11037,11036,10882,10883,11038,10882,11038,11037,10883,10884,11039,10883,11039,11038,10884,10885,11040,10884,11040,11039,10885,10886,11041,10885,11041,11040,10886,10887,11042,10886,11042,11041,10887,10888,11043,10887,11043,11042,10888,10889,11044,10888,11044,11043,10889,10890,11045,10889,11045,11044,10890,10891,11046,10890,11046,11045,10891,10892,11047,10891,11047,11046,10892,10893,11048,10892,11048,11047,10893,10894,11049,10893,11049,11048,10894,10895,11050,10894,11050,11049,10895,10896,11051,10895,11051,11050,10896,10897,11052,10896,11052,11051,10897,10898,11053,10897,11053,11052,10898,10899,11054,10898,11054,11053,10899,10900,11055,10899,11055,11054,10900,10901,11056,10900,11056,11055,10901,10902,11057,10901,11057,11056,10902,10903,11058,10902,11058,11057,10903,10904,11059,10903,11059,11058,10904,10905,11060,10904,11060,11059,10905,10906,11061,10905,11061,11060,10906,10907,11062,10906,11062,11061,10907,10908,11063,10907,11063,11062,10908,10909,11064,10908,11064,11063,10909,10910,11065,10909,11065,11064,10910,10911,11066,10910,11066,11065,10911,10912,11067,10911,11067,11066,10912,10913,11068,10912,11068,11067,10913,10914,11069,10913,11069,11068,10914,10915,11070,10914,11070,11069,10915,10916,11071,10915,11071,11070,10916,10917,11072,10916,11072,11071,10917,10918,11073,10917,11073,11072,10918,10919,11074,10918,11074,11073,10919,10920,11075,10919,11075,11074,10920,10921,11076,10920,11076,11075,10921,10922,11077,10921,11077,11076,10922,10923,11078,10922,11078,11077,10923,10924,11079,10923,11079,11078,10924,10925,11080,10924,11080,11079,10925,10926,11081,10925,11081,11080,10926,10927,11082,10926,11082,11081,10927,10928,11083,10927,11083,11082,10928,10929,11084,10928,11084,11083,10929,10930,11085,10929,11085,11084,10930,10931,11086,10930,11086,11085,10931,10932,11087,10931,11087,11086,10932,10933,11088,10932,11088,11087,10933,10934,11089,10933,11089,11088,10934,10935,11090,10934,11090,11089,10935,10936,11091,10935,11091,11090,10936,10937,11092,10936,11092,11091,10937,10938,11093,10937,11093,11092,10938,10939,11094,10938,11094,11093,10939,10940,11095,10939,11095,11094,10940,10941,11096,10940,11096,11095,10941,10942,11097,10941,11097,11096,10942,10943,11098,10942,11098,11097,10943,10944,11099,10943,11099,11098,10944,10945,11100,10944,11100,11099,10945,10946,11101,10945,11101,11100,10946,10947,11102,10946,11102,11101,10947,10948,11103,10947,11103,11102,10948,10949,11104,10948,11104,11103,10949,10950,11105,10949,11105,11104,10950,10951,11106,10950,11106,11105,10951,10952,11107,10951,11107,11106,10952,10953,11108,10952,11108,11107,10953,10954,11109,10953,11109,11108,10954,10955,11110,10954,11110,11109,10955,10956,11111,10955,11111,11110,10956,10957,11112,10956,11112,11111,10957,10958,11113,10957,11113,11112,10958,10959,11114,10958,11114,11113,10959,10960,11115,10959,11115,11114,10960,10961,11116,10960,11116,11115,10961,10962,11117,10961,11117,11116,10962,10963,11118,10962,11118,11117,10963,10964,11119,10963,11119,11118,10964,10965,11120,10964,11120,11119,10965,10966,11121,10965,11121,11120,10966,10967,11122,10966,11122,11121,10967,10968,11123,10967,11123,11122,10968,10969,11124,10968,11124,11123,10969,10970,11125,10969,11125,11124,10970,10971,11126,10970,11126,11125,10971,10972,11127,10971,11127,11126,10972,10973,11128,10972,11128,11127,10973,10974,11129,10973,11129,11128,10974,10975,11130,10974,11130,11129,10975,10976,11131,10975,11131,11130,10976,10977,11132,10976,11132,11131,10977,10978,11133,10977,11133,11132,10978,10979,11134,10978,11134,11133,10979,10980,11135,10979,11135,11134,10980,10981,11136,10980,11136,11135,10981,10982,11137,10981,11137,11136,10982,10983,11138,10982,11138,11137,10983,10984,11139,10983,11139,11138,10984,10985,11140,10984,11140,11139,10985,10986,11141,10985,11141,11140,10986,10987,11142,10986,11142,11141,10987,10988,11143,10987,11143,11142,10988,10989,11144,10988,11144,11143,10989,10990,11145,10989,11145,11144,10990,10991,11146,10990,11146,11145,10991,10992,11147,10991,11147,11146,10992,10993,11148,10992,11148,11147,10993,10994,11149,10993,11149,11148,10994,10995,11150,10994,11150,11149,10995,10996,11151,10995,11151,11150,10996,10997,11152,10996,11152,11151,10997,10998,11153,10997,11153,11152,10998,10999,11154,10998,11154,11153,10999,11000,11155,10999,11155,11154,11000,11001,11156,11000,11156,11155,11001,11002,11157,11001,11157,11156,11002,11003,11158,11002,11158,11157,11003,11004,11159,11003,11159,11158,11005,11006,11161,11005,11161,11160,11006,11007,11162,11006,11162,11161,11007,11008,11163,11007,11163,11162,11008,11009,11164,11008,11164,11163,11009,11010,11165,11009,11165,11164,11010,11011,11166,11010,11166,11165,11011,11012,11167,11011,11167,11166,11012,11013,11168,11012,11168,11167,11013,11014,11169,11013,11169,11168,11014,11015,11170,11014,11170,11169,11015,11016,11171,11015,11171,11170,11016,11017,11172,11016,11172,11171,11017,11018,11173,11017,11173,11172,11018,11019,11174,11018,11174,11173,11019,11020,11175,11019,11175,11174,11020,11021,11176,11020,11176,11175,11021,11022,11177,11021,11177,11176,11022,11023,11178,11022,11178,11177,11023,11024,11179,11023,11179,11178,11024,11025,11180,11024,11180,11179,11025,11026,11181,11025,11181,11180,11026,11027,11182,11026,11182,11181,11027,11028,11183,11027,11183,11182,11028,11029,11184,11028,11184,11183,11029,11030,11185,11029,11185,11184,11030,11031,11186,11030,11186,11185,11031,11032,11187,11031,11187,11186,11032,11033,11188,11032,11188,11187,11033,11034,11189,11033,11189,11188,11034,11035,11190,11034,11190,11189,11035,11036,11191,11035,11191,11190,11036,11037,11192,11036,11192,11191,11037,11038,11193,11037,11193,11192,11038,11039,11194,11038,11194,11193,11039,11040,11195,11039,11195,11194,11040,11041,11196,11040,11196,11195,11041,11042,11197,11041,11197,11196,11042,11043,11198,11042,11198,11197,11043,11044,11199,11043,11199,11198,11044,11045,11200,11044,11200,11199,11045,11046,11201,11045,11201,11200,11046,11047,11202,11046,11202,11201,11047,11048,11203,11047,11203,11202,11048,11049,11204,11048,11204,11203,11049,11050,11205,11049,11205,11204,11050,11051,11206,11050,11206,11205,11051,11052,11207,11051,11207,11206,11052,11053,11208,11052,11208,11207,11053,11054,11209,11053,11209,11208,11054,11055,11210,11054,11210,11209,11055,11056,11211,11055,11211,11210,11056,11057,11212,11056,11212,11211,11057,11058,11213,11057,11213,11212,11058,11059,11214,11058,11214,11213,11059,11060,11215,11059,11215,11214,11060,11061,11216,11060,11216,11215,11061,11062,11217,11061,11217,11216,11062,11063,11218,11062,11218,11217,11063,11064,11219,11063,11219,11218,11064,11065,11220,11064,11220,11219,11065,11066,11221,11065,11221,11220,11066,11067,11222,11066,11222,11221,11067,11068,11223,11067,11223,11222,11068,11069,11224,11068,11224,11223,11069,11070,11225,11069,11225,11224,11070,11071,11226,11070,11226,11225,11071,11072,11227,11071,11227,11226,11072,11073,11228,11072,11228,11227,11073,11074,11229,11073,11229,11228,11074,11075,11230,11074,11230,11229,11075,11076,11231,11075,11231,11230,11076,11077,11232,11076,11232,11231,11077,11078,11233,11077,11233,11232,11078,11079,11234,11078,11234,11233,11079,11080,11235,11079,11235,11234,11080,11081,11236,11080,11236,11235,11081,11082,11237,11081,11237,11236,11082,11083,11238,11082,11238,11237,11083,11084,11239,11083,11239,11238,11084,11085,11240,11084,11240,11239,11085,11086,11241,11085,11241,11240,11086,11087,11242,11086,11242,11241,11087,11088,11243,11087,11243,11242,11088,11089,11244,11088,11244,11243,11089,11090,11245,11089,11245,11244,11090,11091,11246,11090,11246,11245,11091,11092,11247,11091,11247,11246,11092,11093,11248,11092,11248,11247,11093,11094,11249,11093,11249,11248,11094,11095,11250,11094,11250,11249,11095,11096,11251,11095,11251,11250,11096,11097,11252,11096,11252,11251,11097,11098,11253,11097,11253,11252,11098,11099,11254,11098,11254,11253,11099,11100,11255,11099,11255,11254,11100,11101,11256,11100,11256,11255,11101,11102,11257,11101,11257,11256,11102,11103,11258,11102,11258,11257,11103,11104,11259,11103,11259,11258,11104,11105,11260,11104,11260,11259,11105,11106,11261,11105,11261,11260,11106,11107,11262,11106,11262,11261,11107,11108,11263,11107,11263,11262,11108,11109,11264,11108,11264,11263,11109,11110,11265,11109,11265,11264,11110,11111,11266,11110,11266,11265,11111,11112,11267,11111,11267,11266,11112,11113,11268,11112,11268,11267,11113,11114,11269,11113,11269,11268,11114,11115,11270,11114,11270,11269,11115,11116,11271,11115,11271,11270,11116,11117,11272,11116,11272,11271,11117,11118,11273,11117,11273,11272,11118,11119,11274,11118,11274,11273,11119,11120,11275,11119,11275,11274,11120,11121,11276,11120,11276,11275,11121,11122,11277,11121,11277,11276,11122,11123,11278,11122,11278,11277,11123,11124,11279,11123,11279,11278,11124,11125,11280,11124,11280,11279,11125,11126,11281,11125,11281,11280,11126,11127,11282,11126,11282,11281,11127,11128,11283,11127,11283,11282,11128,11129,11284,11128,11284,11283,11129,11130,11285,11129,11285,11284,11130,11131,11286,11130,11286,11285,11131,11132,11287,11131,11287,11286,11132,11133,11288,11132,11288,11287,11133,11134,11289,11133,11289,11288,11134,11135,11290,11134,11290,11289,11135,11136,11291,11135,11291,11290,11136,11137,11292,11136,11292,11291,11137,11138,11293,11137,11293,11292,11138,11139,11294,11138,11294,11293,11139,11140,11295,11139,11295,11294,11140,11141,11296,11140,11296,11295,11141,11142,11297,11141,11297,11296,11142,11143,11298,11142,11298,11297,11143,11144,11299,11143,11299,11298,11144,11145,11300,11144,11300,11299,11145,11146,11301,11145,11301,11300,11146,11147,11302,11146,11302,11301,11147,11148,11303,11147,11303,11302,11148,11149,11304,11148,11304,11303,11149,11150,11305,11149,11305,11304,11150,11151,11306,11150,11306,11305,11151,11152,11307,11151,11307,11306,11152,11153,11308,11152,11308,11307,11153,11154,11309,11153,11309,11308,11154,11155,11310,11154,11310,11309,11155,11156,11311,11155,11311,11310,11156,11157,11312,11156,11312,11311,11157,11158,11313,11157,11313,11312,11158,11159,11314,11158,11314,11313,11160,11161,11316,11160,11316,11315,11161,11162,11317,11161,11317,11316,11162,11163,11318,11162,11318,11317,11163,11164,11319,11163,11319,11318,11164,11165,11320,11164,11320,11319,11165,11166,11321,11165,11321,11320,11166,11167,11322,11166,11322,11321,11167,11168,11323,11167,11323,11322,11168,11169,11324,11168,11324,11323,11169,11170,11325,11169,11325,11324,11170,11171,11326,11170,11326,11325,11171,11172,11327,11171,11327,11326,11172,11173,11328,11172,11328,11327,11173,11174,11329,11173,11329,11328,11174,11175,11330,11174,11330,11329,11175,11176,11331,11175,11331,11330,11176,11177,11332,11176,11332,11331,11177,11178,11333,11177,11333,11332,11178,11179,11334,11178,11334,11333,11179,11180,11335,11179,11335,11334,11180,11181,11336,11180,11336,11335,11181,11182,11337,11181,11337,11336,11182,11183,11338,11182,11338,11337,11183,11184,11339,11183,11339,11338,11184,11185,11340,11184,11340,11339,11185,11186,11341,11185,11341,11340,11186,11187,11342,11186,11342,11341,11187,11188,11343,11187,11343,11342,11188,11189,11344,11188,11344,11343,11189,11190,11345,11189,11345,11344,11190,11191,11346,11190,11346,11345,11191,11192,11347,11191,11347,11346,11192,11193,11348,11192,11348,11347,11193,11194,11349,11193,11349,11348,11194,11195,11350,11194,11350,11349,11195,11196,11351,11195,11351,11350,11196,11197,11352,11196,11352,11351,11197,11198,11353,11197,11353,11352,11198,11199,11354,11198,11354,11353,11199,11200,11355,11199,11355,11354,11200,11201,11356,11200,11356,11355,11201,11202,11357,11201,11357,11356,11202,11203,11358,11202,11358,11357,11203,11204,11359,11203,11359,11358,11204,11205,11360,11204,11360,11359,11205,11206,11361,11205,11361,11360,11206,11207,11362,11206,11362,11361,11207,11208,11363,11207,11363,11362,11208,11209,11364,11208,11364,11363,11209,11210,11365,11209,11365,11364,11210,11211,11366,11210,11366,11365,11211,11212,11367,11211,11367,11366,11212,11213,11368,11212,11368,11367,11213,11214,11369,11213,11369,11368,11214,11215,11370,11214,11370,11369,11215,11216,11371,11215,11371,11370,11216,11217,11372,11216,11372,11371,11217,11218,11373,11217,11373,11372,11218,11219,11374,11218,11374,11373,11219,11220,11375,11219,11375,11374,11220,11221,11376,11220,11376,11375,11221,11222,11377,11221,11377,11376,11222,11223,11378,11222,11378,11377,11223,11224,11379,11223,11379,11378,11224,11225,11380,11224,11380,11379,11225,11226,11381,11225,11381,11380,11226,11227,11382,11226,11382,11381,11227,11228,11383,11227,11383,11382,11228,11229,11384,11228,11384,11383,11229,11230,11385,11229,11385,11384,11230,11231,11386,11230,11386,11385,11231,11232,11387,11231,11387,11386,11232,11233,11388,11232,11388,11387,11233,11234,11389,11233,11389,11388,11234,11235,11390,11234,11390,11389,11235,11236,11391,11235,11391,11390,11236,11237,11392,11236,11392,11391,11237,11238,11393,11237,11393,11392,11238,11239,11394,11238,11394,11393,11239,11240,11395,11239,11395,11394,11240,11241,11396,11240,11396,11395,11241,11242,11397,11241,11397,11396,11242,11243,11398,11242,11398,11397,11243,11244,11399,11243,11399,11398,11244,11245,11400,11244,11400,11399,11245,11246,11401,11245,11401,11400,11246,11247,11402,11246,11402,11401,11247,11248,11403,11247,11403,11402,11248,11249,11404,11248,11404,11403,11249,11250,11405,11249,11405,11404,11250,11251,11406,11250,11406,11405,11251,11252,11407,11251,11407,11406,11252,11253,11408,11252,11408,11407,11253,11254,11409,11253,11409,11408,11254,11255,11410,11254,11410,11409,11255,11256,11411,11255,11411,11410,11256,11257,11412,11256,11412,11411,11257,11258,11413,11257,11413,11412,11258,11259,11414,11258,11414,11413,11259,11260,11415,11259,11415,11414,11260,11261,11416,11260,11416,11415,11261,11262,11417,11261,11417,11416,11262,11263,11418,11262,11418,11417,11263,11264,11419,11263,11419,11418,11264,11265,11420,11264,11420,11419,11265,11266,11421,11265,11421,11420,11266,11267,11422,11266,11422,11421,11267,11268,11423,11267,11423,11422,11268,11269,11424,11268,11424,11423,11269,11270,11425,11269,11425,11424,11270,11271,11426,11270,11426,11425,11271,11272,11427,11271,11427,11426,11272,11273,11428,11272,11428,11427,11273,11274,11429,11273,11429,11428,11274,11275,11430,11274,11430,11429,11275,11276,11431,11275,11431,11430,11276,11277,11432,11276,11432,11431,11277,11278,11433,11277,11433,11432,11278,11279,11434,11278,11434,11433,11279,11280,11435,11279,11435,11434,11280,11281,11436,11280,11436,11435,11281,11282,11437,11281,11437,11436,11282,11283,11438,11282,11438,11437,11283,11284,11439,11283,11439,11438,11284,11285,11440,11284,11440,11439,11285,11286,11441,11285,11441,11440,11286,11287,11442,11286,11442,11441,11287,11288,11443,11287,11443,11442,11288,11289,11444,11288,11444,11443,11289,11290,11445,11289,11445,11444,11290,11291,11446,11290,11446,11445,11291,11292,11447,11291,11447,11446,11292,11293,11448,11292,11448,11447,11293,11294,11449,11293,11449,11448,11294,11295,11450,11294,11450,11449,11295,11296,11451,11295,11451,11450,11296,11297,11452,11296,11452,11451,11297,11298,11453,11297,11453,11452,11298,11299,11454,11298,11454,11453,11299,11300,11455,11299,11455,11454,11300,11301,11456,11300,11456,11455,11301,11302,11457,11301,11457,11456,11302,11303,11458,11302,11458,11457,11303,11304,11459,11303,11459,11458,11304,11305,11460,11304,11460,11459,11305,11306,11461,11305,11461,11460,11306,11307,11462,11306,11462,11461,11307,11308,11463,11307,11463,11462,11308,11309,11464,11308,11464,11463,11309,11310,11465,11309,11465,11464,11310,11311,11466,11310,11466,11465,11311,11312,11467,11311,11467,11466,11312,11313,11468,11312,11468,11467,11313,11314,11469,11313,11469,11468,11315,11316,11471,11315,11471,11470,11316,11317,11472,11316,11472,11471,11317,11318,11473,11317,11473,11472,11318,11319,11474,11318,11474,11473,11319,11320,11475,11319,11475,11474,11320,11321,11476,11320,11476,11475,11321,11322,11477,11321,11477,11476,11322,11323,11478,11322,11478,11477,11323,11324,11479,11323,11479,11478,11324,11325,11480,11324,11480,11479,11325,11326,11481,11325,11481,11480,11326,11327,11482,11326,11482,11481,11327,11328,11483,11327,11483,11482,11328,11329,11484,11328,11484,11483,11329,11330,11485,11329,11485,11484,11330,11331,11486,11330,11486,11485,11331,11332,11487,11331,11487,11486,11332,11333,11488,11332,11488,11487,11333,11334,11489,11333,11489,11488,11334,11335,11490,11334,11490,11489,11335,11336,11491,11335,11491,11490,11336,11337,11492,11336,11492,11491,11337,11338,11493,11337,11493,11492,11338,11339,11494,11338,11494,11493,11339,11340,11495,11339,11495,11494,11340,11341,11496,11340,11496,11495,11341,11342,11497,11341,11497,11496,11342,11343,11498,11342,11498,11497,11343,11344,11499,11343,11499,11498,11344,11345,11500,11344,11500,11499,11345,11346,11501,11345,11501,11500,11346,11347,11502,11346,11502,11501,11347,11348,11503,11347,11503,11502,11348,11349,11504,11348,11504,11503,11349,11350,11505,11349,11505,11504,11350,11351,11506,11350,11506,11505,11351,11352,11507,11351,11507,11506,11352,11353,11508,11352,11508,11507,11353,11354,11509,11353,11509,11508,11354,11355,11510,11354,11510,11509,11355,11356,11511,11355,11511,11510,11356,11357,11512,11356,11512,11511,11357,11358,11513,11357,11513,11512,11358,11359,11514,11358,11514,11513,11359,11360,11515,11359,11515,11514,11360,11361,11516,11360,11516,11515,11361,11362,11517,11361,11517,11516,11362,11363,11518,11362,11518,11517,11363,11364,11519,11363,11519,11518,11364,11365,11520,11364,11520,11519,11365,11366,11521,11365,11521,11520,11366,11367,11522,11366,11522,11521,11367,11368,11523,11367,11523,11522,11368,11369,11524,11368,11524,11523,11369,11370,11525,11369,11525,11524,11370,11371,11526,11370,11526,11525,11371,11372,11527,11371,11527,11526,11372,11373,11528,11372,11528,11527,11373,11374,11529,11373,11529,11528,11374,11375,11530,11374,11530,11529,11375,11376,11531,11375,11531,11530,11376,11377,11532,11376,11532,11531,11377,11378,11533,11377,11533,11532,11378,11379,11534,11378,11534,11533,11379,11380,11535,11379,11535,11534,11380,11381,11536,11380,11536,11535,11381,11382,11537,11381,11537,11536,11382,11383,11538,11382,11538,11537,11383,11384,11539,11383,11539,11538,11384,11385,11540,11384,11540,11539,11385,11386,11541,11385,11541,11540,11386,11387,11542,11386,11542,11541,11387,11388,11543,11387,11543,11542,11388,11389,11544,11388,11544,11543,11389,11390,11545,11389,11545,11544,11390,11391,11546,11390,11546,11545,11391,11392,11547,11391,11547,11546,11392,11393,11548,11392,11548,11547,11393,11394,11549,11393,11549,11548,11394,11395,11550,11394,11550,11549,11395,11396,11551,11395,11551,11550,11396,11397,11552,11396,11552,11551,11397,11398,11553,11397,11553,11552,11398,11399,11554,11398,11554,11553,11399,11400,11555,11399,11555,11554,11400,11401,11556,11400,11556,11555,11401,11402,11557,11401,11557,11556,11402,11403,11558,11402,11558,11557,11403,11404,11559,11403,11559,11558,11404,11405,11560,11404,11560,11559,11405,11406,11561,11405,11561,11560,11406,11407,11562,11406,11562,11561,11407,11408,11563,11407,11563,11562,11408,11409,11564,11408,11564,11563,11409,11410,11565,11409,11565,11564,11410,11411,11566,11410,11566,11565,11411,11412,11567,11411,11567,11566,11412,11413,11568,11412,11568,11567,11413,11414,11569,11413,11569,11568,11414,11415,11570,11414,11570,11569,11415,11416,11571,11415,11571,11570,11416,11417,11572,11416,11572,11571,11417,11418,11573,11417,11573,11572,11418,11419,11574,11418,11574,11573,11419,11420,11575,11419,11575,11574,11420,11421,11576,11420,11576,11575,11421,11422,11577,11421,11577,11576,11422,11423,11578,11422,11578,11577,11423,11424,11579,11423,11579,11578,11424,11425,11580,11424,11580,11579,11425,11426,11581,11425,11581,11580,11426,11427,11582,11426,11582,11581,11427,11428,11583,11427,11583,11582,11428,11429,11584,11428,11584,11583,11429,11430,11585,11429,11585,11584,11430,11431,11586,11430,11586,11585,11431,11432,11587,11431,11587,11586,11432,11433,11588,11432,11588,11587,11433,11434,11589,11433,11589,11588,11434,11435,11590,11434,11590,11589,11435,11436,11591,11435,11591,11590,11436,11437,11592,11436,11592,11591,11437,11438,11593,11437,11593,11592,11438,11439,11594,11438,11594,11593,11439,11440,11595,11439,11595,11594,11440,11441,11596,11440,11596,11595,11441,11442,11597,11441,11597,11596,11442,11443,11598,11442,11598,11597,11443,11444,11599,11443,11599,11598,11444,11445,11600,11444,11600,11599,11445,11446,11601,11445,11601,11600,11446,11447,11602,11446,11602,11601,11447,11448,11603,11447,11603,11602,11448,11449,11604,11448,11604,11603,11449,11450,11605,11449,11605,11604,11450,11451,11606,11450,11606,11605,11451,11452,11607,11451,11607,11606,11452,11453,11608,11452,11608,11607,11453,11454,11609,11453,11609,11608,11454,11455,11610,11454,11610,11609,11455,11456,11611,11455,11611,11610,11456,11457,11612,11456,11612,11611,11457,11458,11613,11457,11613,11612,11458,11459,11614,11458,11614,11613,11459,11460,11615,11459,11615,11614,11460,11461,11616,11460,11616,11615,11461,11462,11617,11461,11617,11616,11462,11463,11618,11462,11618,11617,11463,11464,11619,11463,11619,11618,11464,11465,11620,11464,11620,11619,11465,11466,11621,11465,11621,11620,11466,11467,11622,11466,11622,11621,11467,11468,11623,11467,11623,11622,11468,11469,11624,11468,11624,11623,11470,11471,11626,11470,11626,11625,11471,11472,11627,11471,11627,11626,11472,11473,11628,11472,11628,11627,11473,11474,11629,11473,11629,11628,11474,11475,11630,11474,11630,11629,11475,11476,11631,11475,11631,11630,11476,11477,11632,11476,11632,11631,11477,11478,11633,11477,11633,11632,11478,11479,11634,11478,11634,11633,11479,11480,11635,11479,11635,11634,11480,11481,11636,11480,11636,11635,11481,11482,11637,11481,11637,11636,11482,11483,11638,11482,11638,11637,11483,11484,11639,11483,11639,11638,11484,11485,11640,11484,11640,11639,11485,11486,11641,11485,11641,11640,11486,11487,11642,11486,11642,11641,11487,11488,11643,11487,11643,11642,11488,11489,11644,11488,11644,11643,11489,11490,11645,11489,11645,11644,11490,11491,11646,11490,11646,11645,11491,11492,11647,11491,11647,11646,11492,11493,11648,11492,11648,11647,11493,11494,11649,11493,11649,11648,11494,11495,11650,11494,11650,11649,11495,11496,11651,11495,11651,11650,11496,11497,11652,11496,11652,11651,11497,11498,11653,11497,11653,11652,11498,11499,11654,11498,11654,11653,11499,11500,11655,11499,11655,11654,11500,11501,11656,11500,11656,11655,11501,11502,11657,11501,11657,11656,11502,11503,11658,11502,11658,11657,11503,11504,11659,11503,11659,11658,11504,11505,11660,11504,11660,11659,11505,11506,11661,11505,11661,11660,11506,11507,11662,11506,11662,11661,11507,11508,11663,11507,11663,11662,11508,11509,11664,11508,11664,11663,11509,11510,11665,11509,11665,11664,11510,11511,11666,11510,11666,11665,11511,11512,11667,11511,11667,11666,11512,11513,11668,11512,11668,11667,11513,11514,11669,11513,11669,11668,11514,11515,11670,11514,11670,11669,11515,11516,11671,11515,11671,11670,11516,11517,11672,11516,11672,11671,11517,11518,11673,11517,11673,11672,11518,11519,11674,11518,11674,11673,11519,11520,11675,11519,11675,11674,11520,11521,11676,11520,11676,11675,11521,11522,11677,11521,11677,11676,11522,11523,11678,11522,11678,11677,11523,11524,11679,11523,11679,11678,11524,11525,11680,11524,11680,11679,11525,11526,11681,11525,11681,11680,11526,11527,11682,11526,11682,11681,11527,11528,11683,11527,11683,11682,11528,11529,11684,11528,11684,11683,11529,11530,11685,11529,11685,11684,11530,11531,11686,11530,11686,11685,11531,11532,11687,11531,11687,11686,11532,11533,11688,11532,11688,11687,11533,11534,11689,11533,11689,11688,11534,11535,11690,11534,11690,11689,11535,11536,11691,11535,11691,11690,11536,11537,11692,11536,11692,11691,11537,11538,11693,11537,11693,11692,11538,11539,11694,11538,11694,11693,11539,11540,11695,11539,11695,11694,11540,11541,11696,11540,11696,11695,11541,11542,11697,11541,11697,11696,11542,11543,11698,11542,11698,11697,11543,11544,11699,11543,11699,11698,11544,11545,11700,11544,11700,11699,11545,11546,11701,11545,11701,11700,11546,11547,11702,11546,11702,11701,11547,11548,11703,11547,11703,11702,11548,11549,11704,11548,11704,11703,11549,11550,11705,11549,11705,11704,11550,11551,11706,11550,11706,11705,11551,11552,11707,11551,11707,11706,11552,11553,11708,11552,11708,11707,11553,11554,11709,11553,11709,11708,11554,11555,11710,11554,11710,11709,11555,11556,11711,11555,11711,11710,11556,11557,11712,11556,11712,11711,11557,11558,11713,11557,11713,11712,11558,11559,11714,11558,11714,11713,11559,11560,11715,11559,11715,11714,11560,11561,11716,11560,11716,11715,11561,11562,11717,11561,11717,11716,11562,11563,11718,11562,11718,11717,11563,11564,11719,11563,11719,11718,11564,11565,11720,11564,11720,11719,11565,11566,11721,11565,11721,11720,11566,11567,11722,11566,11722,11721,11567,11568,11723,11567,11723,11722,11568,11569,11724,11568,11724,11723,11569,11570,11725,11569,11725,11724,11570,11571,11726,11570,11726,11725,11571,11572,11727,11571,11727,11726,11572,11573,11728,11572,11728,11727,11573,11574,11729,11573,11729,11728,11574,11575,11730,11574,11730,11729,11575,11576,11731,11575,11731,11730,11576,11577,11732,11576,11732,11731,11577,11578,11733,11577,11733,11732,11578,11579,11734,11578,11734,11733,11579,11580,11735,11579,11735,11734,11580,11581,11736,11580,11736,11735,11581,11582,11737,11581,11737,11736,11582,11583,11738,11582,11738,11737,11583,11584,11739,11583,11739,11738,11584,11585,11740,11584,11740,11739,11585,11586,11741,11585,11741,11740,11586,11587,11742,11586,11742,11741,11587,11588,11743,11587,11743,11742,11588,11589,11744,11588,11744,11743,11589,11590,11745,11589,11745,11744,11590,11591,11746,11590,11746,11745,11591,11592,11747,11591,11747,11746,11592,11593,11748,11592,11748,11747,11593,11594,11749,11593,11749,11748,11594,11595,11750,11594,11750,11749,11595,11596,11751,11595,11751,11750,11596,11597,11752,11596,11752,11751,11597,11598,11753,11597,11753,11752,11598,11599,11754,11598,11754,11753,11599,11600,11755,11599,11755,11754,11600,11601,11756,11600,11756,11755,11601,11602,11757,11601,11757,11756,11602,11603,11758,11602,11758,11757,11603,11604,11759,11603,11759,11758,11604,11605,11760,11604,11760,11759,11605,11606,11761,11605,11761,11760,11606,11607,11762,11606,11762,11761,11607,11608,11763,11607,11763,11762,11608,11609,11764,11608,11764,11763,11609,11610,11765,11609,11765,11764,11610,11611,11766,11610,11766,11765,11611,11612,11767,11611,11767,11766,11612,11613,11768,11612,11768,11767,11613,11614,11769,11613,11769,11768,11614,11615,11770,11614,11770,11769,11615,11616,11771,11615,11771,11770,11616,11617,11772,11616,11772,11771,11617,11618,11773,11617,11773,11772,11618,11619,11774,11618,11774,11773,11619,11620,11775,11619,11775,11774,11620,11621,11776,11620,11776,11775,11621,11622,11777,11621,11777,11776,11622,11623,11778,11622,11778,11777,11623,11624,11779,11623,11779,11778,11625,11626,11781,11625,11781,11780,11626,11627,11782,11626,11782,11781,11627,11628,11783,11627,11783,11782,11628,11629,11784,11628,11784,11783,11629,11630,11785,11629,11785,11784,11630,11631,11786,11630,11786,11785,11631,11632,11787,11631,11787,11786,11632,11633,11788,11632,11788,11787,11633,11634,11789,11633,11789,11788,11634,11635,11790,11634,11790,11789,11635,11636,11791,11635,11791,11790,11636,11637,11792,11636,11792,11791,11637,11638,11793,11637,11793,11792,11638,11639,11794,11638,11794,11793,11639,11640,11795,11639,11795,11794,11640,11641,11796,11640,11796,11795,11641,11642,11797,11641,11797,11796,11642,11643,11798,11642,11798,11797,11643,11644,11799,11643,11799,11798,11644,11645,11800,11644,11800,11799,11645,11646,11801,11645,11801,11800,11646,11647,11802,11646,11802,11801,11647,11648,11803,11647,11803,11802,11648,11649,11804,11648,11804,11803,11649,11650,11805,11649,11805,11804,11650,11651,11806,11650,11806,11805,11651,11652,11807,11651,11807,11806,11652,11653,11808,11652,11808,11807,11653,11654,11809,11653,11809,11808,11654,11655,11810,11654,11810,11809,11655,11656,11811,11655,11811,11810,11656,11657,11812,11656,11812,11811,11657,11658,11813,11657,11813,11812,11658,11659,11814,11658,11814,11813,11659,11660,11815,11659,11815,11814,11660,11661,11816,11660,11816,11815,11661,11662,11817,11661,11817,11816,11662,11663,11818,11662,11818,11817,11663,11664,11819,11663,11819,11818,11664,11665,11820,11664,11820,11819,11665,11666,11821,11665,11821,11820,11666,11667,11822,11666,11822,11821,11667,11668,11823,11667,11823,11822,11668,11669,11824,11668,11824,11823,11669,11670,11825,11669,11825,11824,11670,11671,11826,11670,11826,11825,11671,11672,11827,11671,11827,11826,11672,11673,11828,11672,11828,11827,11673,11674,11829,11673,11829,11828,11674,11675,11830,11674,11830,11829,11675,11676,11831,11675,11831,11830,11676,11677,11832,11676,11832,11831,11677,11678,11833,11677,11833,11832,11678,11679,11834,11678,11834,11833,11679,11680,11835,11679,11835,11834,11680,11681,11836,11680,11836,11835,11681,11682,11837,11681,11837,11836,11682,11683,11838,11682,11838,11837,11683,11684,11839,11683,11839,11838,11684,11685,11840,11684,11840,11839,11685,11686,11841,11685,11841,11840,11686,11687,11842,11686,11842,11841,11687,11688,11843,11687,11843,11842,11688,11689,11844,11688,11844,11843,11689,11690,11845,11689,11845,11844,11690,11691,11846,11690,11846,11845,11691,11692,11847,11691,11847,11846,11692,11693,11848,11692,11848,11847,11693,11694,11849,11693,11849,11848,11694,11695,11850,11694,11850,11849,11695,11696,11851,11695,11851,11850,11696,11697,11852,11696,11852,11851,11697,11698,11853,11697,11853,11852,11698,11699,11854,11698,11854,11853,11699,11700,11855,11699,11855,11854,11700,11701,11856,11700,11856,11855,11701,11702,11857,11701,11857,11856,11702,11703,11858,11702,11858,11857,11703,11704,11859,11703,11859,11858,11704,11705,11860,11704,11860,11859,11705,11706,11861,11705,11861,11860,11706,11707,11862,11706,11862,11861,11707,11708,11863,11707,11863,11862,11708,11709,11864,11708,11864,11863,11709,11710,11865,11709,11865,11864,11710,11711,11866,11710,11866,11865,11711,11712,11867,11711,11867,11866,11712,11713,11868,11712,11868,11867,11713,11714,11869,11713,11869,11868,11714,11715,11870,11714,11870,11869,11715,11716,11871,11715,11871,11870,11716,11717,11872,11716,11872,11871,11717,11718,11873,11717,11873,11872,11718,11719,11874,11718,11874,11873,11719,11720,11875,11719,11875,11874,11720,11721,11876,11720,11876,11875,11721,11722,11877,11721,11877,11876,11722,11723,11878,11722,11878,11877,11723,11724,11879,11723,11879,11878,11724,11725,11880,11724,11880,11879,11725,11726,11881,11725,11881,11880,11726,11727,11882,11726,11882,11881,11727,11728,11883,11727,11883,11882,11728,11729,11884,11728,11884,11883,11729,11730,11885,11729,11885,11884,11730,11731,11886,11730,11886,11885,11731,11732,11887,11731,11887,11886,11732,11733,11888,11732,11888,11887,11733,11734,11889,11733,11889,11888,11734,11735,11890,11734,11890,11889,11735,11736,11891,11735,11891,11890,11736,11737,11892,11736,11892,11891,11737,11738,11893,11737,11893,11892,11738,11739,11894,11738,11894,11893,11739,11740,11895,11739,11895,11894,11740,11741,11896,11740,11896,11895,11741,11742,11897,11741,11897,11896,11742,11743,11898,11742,11898,11897,11743,11744,11899,11743,11899,11898,11744,11745,11900,11744,11900,11899,11745,11746,11901,11745,11901,11900,11746,11747,11902,11746,11902,11901,11747,11748,11903,11747,11903,11902,11748,11749,11904,11748,11904,11903,11749,11750,11905,11749,11905,11904,11750,11751,11906,11750,11906,11905,11751,11752,11907,11751,11907,11906,11752,11753,11908,11752,11908,11907,11753,11754,11909,11753,11909,11908,11754,11755,11910,11754,11910,11909,11755,11756,11911,11755,11911,11910,11756,11757,11912,11756,11912,11911,11757,11758,11913,11757,11913,11912,11758,11759,11914,11758,11914,11913,11759,11760,11915,11759,11915,11914,11760,11761,11916,11760,11916,11915,11761,11762,11917,11761,11917,11916,11762,11763,11918,11762,11918,11917,11763,11764,11919,11763,11919,11918,11764,11765,11920,11764,11920,11919,11765,11766,11921,11765,11921,11920,11766,11767,11922,11766,11922,11921,11767,11768,11923,11767,11923,11922,11768,11769,11924,11768,11924,11923,11769,11770,11925,11769,11925,11924,11770,11771,11926,11770,11926,11925,11771,11772,11927,11771,11927,11926,11772,11773,11928,11772,11928,11927,11773,11774,11929,11773,11929,11928,11774,11775,11930,11774,11930,11929,11775,11776,11931,11775,11931,11930,11776,11777,11932,11776,11932,11931,11777,11778,11933,11777,11933,11932,11778,11779,11934,11778,11934,11933,11780,11781,11936,11780,11936,11935,11781,11782,11937,11781,11937,11936,11782,11783,11938,11782,11938,11937,11783,11784,11939,11783,11939,11938,11784,11785,11940,11784,11940,11939,11785,11786,11941,11785,11941,11940,11786,11787,11942,11786,11942,11941,11787,11788,11943,11787,11943,11942,11788,11789,11944,11788,11944,11943,11789,11790,11945,11789,11945,11944,11790,11791,11946,11790,11946,11945,11791,11792,11947,11791,11947,11946,11792,11793,11948,11792,11948,11947,11793,11794,11949,11793,11949,11948,11794,11795,11950,11794,11950,11949,11795,11796,11951,11795,11951,11950,11796,11797,11952,11796,11952,11951,11797,11798,11953,11797,11953,11952,11798,11799,11954,11798,11954,11953,11799,11800,11955,11799,11955,11954,11800,11801,11956,11800,11956,11955,11801,11802,11957,11801,11957,11956,11802,11803,11958,11802,11958,11957,11803,11804,11959,11803,11959,11958,11804,11805,11960,11804,11960,11959,11805,11806,11961,11805,11961,11960,11806,11807,11962,11806,11962,11961,11807,11808,11963,11807,11963,11962,11808,11809,11964,11808,11964,11963,11809,11810,11965,11809,11965,11964,11810,11811,11966,11810,11966,11965,11811,11812,11967,11811,11967,11966,11812,11813,11968,11812,11968,11967,11813,11814,11969,11813,11969,11968,11814,11815,11970,11814,11970,11969,11815,11816,11971,11815,11971,11970,11816,11817,11972,11816,11972,11971,11817,11818,11973,11817,11973,11972,11818,11819,11974,11818,11974,11973,11819,11820,11975,11819,11975,11974,11820,11821,11976,11820,11976,11975,11821,11822,11977,11821,11977,11976,11822,11823,11978,11822,11978,11977,11823,11824,11979,11823,11979,11978,11824,11825,11980,11824,11980,11979,11825,11826,11981,11825,11981,11980,11826,11827,11982,11826,11982,11981,11827,11828,11983,11827,11983,11982,11828,11829,11984,11828,11984,11983,11829,11830,11985,11829,11985,11984,11830,11831,11986,11830,11986,11985,11831,11832,11987,11831,11987,11986,11832,11833,11988,11832,11988,11987,11833,11834,11989,11833,11989,11988,11834,11835,11990,11834,11990,11989,11835,11836,11991,11835,11991,11990,11836,11837,11992,11836,11992,11991,11837,11838,11993,11837,11993,11992,11838,11839,11994,11838,11994,11993,11839,11840,11995,11839,11995,11994,11840,11841,11996,11840,11996,11995,11841,11842,11997,11841,11997,11996,11842,11843,11998,11842,11998,11997,11843,11844,11999,11843,11999,11998,11844,11845,12000,11844,12000,11999,11845,11846,12001,11845,12001,12000,11846,11847,12002,11846,12002,12001,11847,11848,12003,11847,12003,12002,11848,11849,12004,11848,12004,12003,11849,11850,12005,11849,12005,12004,11850,11851,12006,11850,12006,12005,11851,11852,12007,11851,12007,12006,11852,11853,12008,11852,12008,12007,11853,11854,12009,11853,12009,12008,11854,11855,12010,11854,12010,12009,11855,11856,12011,11855,12011,12010,11856,11857,12012,11856,12012,12011,11857,11858,12013,11857,12013,12012,11858,11859,12014,11858,12014,12013,11859,11860,12015,11859,12015,12014,11860,11861,12016,11860,12016,12015,11861,11862,12017,11861,12017,12016,11862,11863,12018,11862,12018,12017,11863,11864,12019,11863,12019,12018,11864,11865,12020,11864,12020,12019,11865,11866,12021,11865,12021,12020,11866,11867,12022,11866,12022,12021,11867,11868,12023,11867,12023,12022,11868,11869,12024,11868,12024,12023,11869,11870,12025,11869,12025,12024,11870,11871,12026,11870,12026,12025,11871,11872,12027,11871,12027,12026,11872,11873,12028,11872,12028,12027,11873,11874,12029,11873,12029,12028,11874,11875,12030,11874,12030,12029,11875,11876,12031,11875,12031,12030,11876,11877,12032,11876,12032,12031,11877,11878,12033,11877,12033,12032,11878,11879,12034,11878,12034,12033,11879,11880,12035,11879,12035,12034,11880,11881,12036,11880,12036,12035,11881,11882,12037,11881,12037,12036,11882,11883,12038,11882,12038,12037,11883,11884,12039,11883,12039,12038,11884,11885,12040,11884,12040,12039,11885,11886,12041,11885,12041,12040,11886,11887,12042,11886,12042,12041,11887,11888,12043,11887,12043,12042,11888,11889,12044,11888,12044,12043,11889,11890,12045,11889,12045,12044,11890,11891,12046,11890,12046,12045,11891,11892,12047,11891,12047,12046,11892,11893,12048,11892,12048,12047,11893,11894,12049,11893,12049,12048,11894,11895,12050,11894,12050,12049,11895,11896,12051,11895,12051,12050,11896,11897,12052,11896,12052,12051,11897,11898,12053,11897,12053,12052,11898,11899,12054,11898,12054,12053,11899,11900,12055,11899,12055,12054,11900,11901,12056,11900,12056,12055,11901,11902,12057,11901,12057,12056,11902,11903,12058,11902,12058,12057,11903,11904,12059,11903,12059,12058,11904,11905,12060,11904,12060,12059,11905,11906,12061,11905,12061,12060,11906,11907,12062,11906,12062,12061,11907,11908,12063,11907,12063,12062,11908,11909,12064,11908,12064,12063,11909,11910,12065,11909,12065,12064,11910,11911,12066,11910,12066,12065,11911,11912,12067,11911,12067,12066,11912,11913,12068,11912,12068,12067,11913,11914,12069,11913,12069,12068,11914,11915,12070,11914,12070,12069,11915,11916,12071,11915,12071,12070,11916,11917,12072,11916,12072,12071,11917,11918,12073,11917,12073,12072,11918,11919,12074,11918,12074,12073,11919,11920,12075,11919,12075,12074,11920,11921,12076,11920,12076,12075,11921,11922,12077,11921,12077,12076,11922,11923,12078,11922,12078,12077,11923,11924,12079,11923,12079,12078,11924,11925,12080,11924,12080,12079,11925,11926,12081,11925,12081,12080,11926,11927,12082,11926,12082,12081,11927,11928,12083,11927,12083,12082,11928,11929,12084,11928,12084,12083,11929,11930,12085,11929,12085,12084,11930,11931,12086,11930,12086,12085,11931,11932,12087,11931,12087,12086,11932,11933,12088,11932,12088,12087,11933,11934,12089,11933,12089,12088,11935,11936,12091,11935,12091,12090,11936,11937,12092,11936,12092,12091,11937,11938,12093,11937,12093,12092,11938,11939,12094,11938,12094,12093,11939,11940,12095,11939,12095,12094,11940,11941,12096,11940,12096,12095,11941,11942,12097,11941,12097,12096,11942,11943,12098,11942,12098,12097,11943,11944,12099,11943,12099,12098,11944,11945,12100,11944,12100,12099,11945,11946,12101,11945,12101,12100,11946,11947,12102,11946,12102,12101,11947,11948,12103,11947,12103,12102,11948,11949,12104,11948,12104,12103,11949,11950,12105,11949,12105,12104,11950,11951,12106,11950,12106,12105,11951,11952,12107,11951,12107,12106,11952,11953,12108,11952,12108,12107,11953,11954,12109,11953,12109,12108,11954,11955,12110,11954,12110,12109,11955,11956,12111,11955,12111,12110,11956,11957,12112,11956,12112,12111,11957,11958,12113,11957,12113,12112,11958,11959,12114,11958,12114,12113,11959,11960,12115,11959,12115,12114,11960,11961,12116,11960,12116,12115,11961,11962,12117,11961,12117,12116,11962,11963,12118,11962,12118,12117,11963,11964,12119,11963,12119,12118,11964,11965,12120,11964,12120,12119,11965,11966,12121,11965,12121,12120,11966,11967,12122,11966,12122,12121,11967,11968,12123,11967,12123,12122,11968,11969,12124,11968,12124,12123,11969,11970,12125,11969,12125,12124,11970,11971,12126,11970,12126,12125,11971,11972,12127,11971,12127,12126,11972,11973,12128,11972,12128,12127,11973,11974,12129,11973,12129,12128,11974,11975,12130,11974,12130,12129,11975,11976,12131,11975,12131,12130,11976,11977,12132,11976,12132,12131,11977,11978,12133,11977,12133,12132,11978,11979,12134,11978,12134,12133,11979,11980,12135,11979,12135,12134,11980,11981,12136,11980,12136,12135,11981,11982,12137,11981,12137,12136,11982,11983,12138,11982,12138,12137,11983,11984,12139,11983,12139,12138,11984,11985,12140,11984,12140,12139,11985,11986,12141,11985,12141,12140,11986,11987,12142,11986,12142,12141,11987,11988,12143,11987,12143,12142,11988,11989,12144,11988,12144,12143,11989,11990,12145,11989,12145,12144,11990,11991,12146,11990,12146,12145,11991,11992,12147,11991,12147,12146,11992,11993,12148,11992,12148,12147,11993,11994,12149,11993,12149,12148,11994,11995,12150,11994,12150,12149,11995,11996,12151,11995,12151,12150,11996,11997,12152,11996,12152,12151,11997,11998,12153,11997,12153,12152,11998,11999,12154,11998,12154,12153,11999,12000,12155,11999,12155,12154,12000,12001,12156,12000,12156,12155,12001,12002,12157,12001,12157,12156,12002,12003,12158,12002,12158,12157,12003,12004,12159,12003,12159,12158,12004,12005,12160,12004,12160,12159,12005,12006,12161,12005,12161,12160,12006,12007,12162,12006,12162,12161,12007,12008,12163,12007,12163,12162,12008,12009,12164,12008,12164,12163,12009,12010,12165,12009,12165,12164,12010,12011,12166,12010,12166,12165,12011,12012,12167,12011,12167,12166,12012,12013,12168,12012,12168,12167,12013,12014,12169,12013,12169,12168,12014,12015,12170,12014,12170,12169,12015,12016,12171,12015,12171,12170,12016,12017,12172,12016,12172,12171,12017,12018,12173,12017,12173,12172,12018,12019,12174,12018,12174,12173,12019,12020,12175,12019,12175,12174,12020,12021,12176,12020,12176,12175,12021,12022,12177,12021,12177,12176,12022,12023,12178,12022,12178,12177,12023,12024,12179,12023,12179,12178,12024,12025,12180,12024,12180,12179,12025,12026,12181,12025,12181,12180,12026,12027,12182,12026,12182,12181,12027,12028,12183,12027,12183,12182,12028,12029,12184,12028,12184,12183,12029,12030,12185,12029,12185,12184,12030,12031,12186,12030,12186,12185,12031,12032,12187,12031,12187,12186,12032,12033,12188,12032,12188,12187,12033,12034,12189,12033,12189,12188,12034,12035,12190,12034,12190,12189,12035,12036,12191,12035,12191,12190,12036,12037,12192,12036,12192,12191,12037,12038,12193,12037,12193,12192,12038,12039,12194,12038,12194,12193,12039,12040,12195,12039,12195,12194,12040,12041,12196,12040,12196,12195,12041,12042,12197,12041,12197,12196,12042,12043,12198,12042,12198,12197,12043,12044,12199,12043,12199,12198,12044,12045,12200,12044,12200,12199,12045,12046,12201,12045,12201,12200,12046,12047,12202,12046,12202,12201,12047,12048,12203,12047,12203,12202,12048,12049,12204,12048,12204,12203,12049,12050,12205,12049,12205,12204,12050,12051,12206,12050,12206,12205,12051,12052,12207,12051,12207,12206,12052,12053,12208,12052,12208,12207,12053,12054,12209,12053,12209,12208,12054,12055,12210,12054,12210,12209,12055,12056,12211,12055,12211,12210,12056,12057,12212,12056,12212,12211,12057,12058,12213,12057,12213,12212,12058,12059,12214,12058,12214,12213,12059,12060,12215,12059,12215,12214,12060,12061,12216,12060,12216,12215,12061,12062,12217,12061,12217,12216,12062,12063,12218,12062,12218,12217,12063,12064,12219,12063,12219,12218,12064,12065,12220,12064,12220,12219,12065,12066,12221,12065,12221,12220,12066,12067,12222,12066,12222,12221,12067,12068,12223,12067,12223,12222,12068,12069,12224,12068,12224,12223,12069,12070,12225,12069,12225,12224,12070,12071,12226,12070,12226,12225,12071,12072,12227,12071,12227,12226,12072,12073,12228,12072,12228,12227,12073,12074,12229,12073,12229,12228,12074,12075,12230,12074,12230,12229,12075,12076,12231,12075,12231,12230,12076,12077,12232,12076,12232,12231,12077,12078,12233,12077,12233,12232,12078,12079,12234,12078,12234,12233,12079,12080,12235,12079,12235,12234,12080,12081,12236,12080,12236,12235,12081,12082,12237,12081,12237,12236,12082,12083,12238,12082,12238,12237,12083,12084,12239,12083,12239,12238,12084,12085,12240,12084,12240,12239,12085,12086,12241,12085,12241,12240,12086,12087,12242,12086,12242,12241,12087,12088,12243,12087,12243,12242,12088,12089,12244,12088,12244,12243,12090,12091,12246,12090,12246,12245,12091,12092,12247,12091,12247,12246,12092,12093,12248,12092,12248,12247,12093,12094,12249,12093,12249,12248,12094,12095,12250,12094,12250,12249,12095,12096,12251,12095,12251,12250,12096,12097,12252,12096,12252,12251,12097,12098,12253,12097,12253,12252,12098,12099,12254,12098,12254,12253,12099,12100,12255,12099,12255,12254,12100,12101,12256,12100,12256,12255,12101,12102,12257,12101,12257,12256,12102,12103,12258,12102,12258,12257,12103,12104,12259,12103,12259,12258,12104,12105,12260,12104,12260,12259,12105,12106,12261,12105,12261,12260,12106,12107,12262,12106,12262,12261,12107,12108,12263,12107,12263,12262,12108,12109,12264,12108,12264,12263,12109,12110,12265,12109,12265,12264,12110,12111,12266,12110,12266,12265,12111,12112,12267,12111,12267,12266,12112,12113,12268,12112,12268,12267,12113,12114,12269,12113,12269,12268,12114,12115,12270,12114,12270,12269,12115,12116,12271,12115,12271,12270,12116,12117,12272,12116,12272,12271,12117,12118,12273,12117,12273,12272,12118,12119,12274,12118,12274,12273,12119,12120,12275,12119,12275,12274,12120,12121,12276,12120,12276,12275,12121,12122,12277,12121,12277,12276,12122,12123,12278,12122,12278,12277,12123,12124,12279,12123,12279,12278,12124,12125,12280,12124,12280,12279,12125,12126,12281,12125,12281,12280,12126,12127,12282,12126,12282,12281,12127,12128,12283,12127,12283,12282,12128,12129,12284,12128,12284,12283,12129,12130,12285,12129,12285,12284,12130,12131,12286,12130,12286,12285,12131,12132,12287,12131,12287,12286,12132,12133,12288,12132,12288,12287,12133,12134,12289,12133,12289,12288,12134,12135,12290,12134,12290,12289,12135,12136,12291,12135,12291,12290,12136,12137,12292,12136,12292,12291,12137,12138,12293,12137,12293,12292,12138,12139,12294,12138,12294,12293,12139,12140,12295,12139,12295,12294,12140,12141,12296,12140,12296,12295,12141,12142,12297,12141,12297,12296,12142,12143,12298,12142,12298,12297,12143,12144,12299,12143,12299,12298,12144,12145,12300,12144,12300,12299,12145,12146,12301,12145,12301,12300,12146,12147,12302,12146,12302,12301,12147,12148,12303,12147,12303,12302,12148,12149,12304,12148,12304,12303,12149,12150,12305,12149,12305,12304,12150,12151,12306,12150,12306,12305,12151,12152,12307,12151,12307,12306,12152,12153,12308,12152,12308,12307,12153,12154,12309,12153,12309,12308,12154,12155,12310,12154,12310,12309,12155,12156,12311,12155,12311,12310,12156,12157,12312,12156,12312,12311,12157,12158,12313,12157,12313,12312,12158,12159,12314,12158,12314,12313,12159,12160,12315,12159,12315,12314,12160,12161,12316,12160,12316,12315,12161,12162,12317,12161,12317,12316,12162,12163,12318,12162,12318,12317,12163,12164,12319,12163,12319,12318,12164,12165,12320,12164,12320,12319,12165,12166,12321,12165,12321,12320,12166,12167,12322,12166,12322,12321,12167,12168,12323,12167,12323,12322,12168,12169,12324,12168,12324,12323,12169,12170,12325,12169,12325,12324,12170,12171,12326,12170,12326,12325,12171,12172,12327,12171,12327,12326,12172,12173,12328,12172,12328,12327,12173,12174,12329,12173,12329,12328,12174,12175,12330,12174,12330,12329,12175,12176,12331,12175,12331,12330,12176,12177,12332,12176,12332,12331,12177,12178,12333,12177,12333,12332,12178,12179,12334,12178,12334,12333,12179,12180,12335,12179,12335,12334,12180,12181,12336,12180,12336,12335,12181,12182,12337,12181,12337,12336,12182,12183,12338,12182,12338,12337,12183,12184,12339,12183,12339,12338,12184,12185,12340,12184,12340,12339,12185,12186,12341,12185,12341,12340,12186,12187,12342,12186,12342,12341,12187,12188,12343,12187,12343,12342,12188,12189,12344,12188,12344,12343,12189,12190,12345,12189,12345,12344,12190,12191,12346,12190,12346,12345,12191,12192,12347,12191,12347,12346,12192,12193,12348,12192,12348,12347,12193,12194,12349,12193,12349,12348,12194,12195,12350,12194,12350,12349,12195,12196,12351,12195,12351,12350,12196,12197,12352,12196,12352,12351,12197,12198,12353,12197,12353,12352,12198,12199,12354,12198,12354,12353,12199,12200,12355,12199,12355,12354,12200,12201,12356,12200,12356,12355,12201,12202,12357,12201,12357,12356,12202,12203,12358,12202,12358,12357,12203,12204,12359,12203,12359,12358,12204,12205,12360,12204,12360,12359,12205,12206,12361,12205,12361,12360,12206,12207,12362,12206,12362,12361,12207,12208,12363,12207,12363,12362,12208,12209,12364,12208,12364,12363,12209,12210,12365,12209,12365,12364,12210,12211,12366,12210,12366,12365,12211,12212,12367,12211,12367,12366,12212,12213,12368,12212,12368,12367,12213,12214,12369,12213,12369,12368,12214,12215,12370,12214,12370,12369,12215,12216,12371,12215,12371,12370,12216,12217,12372,12216,12372,12371,12217,12218,12373,12217,12373,12372,12218,12219,12374,12218,12374,12373,12219,12220,12375,12219,12375,12374,12220,12221,12376,12220,12376,12375,12221,12222,12377,12221,12377,12376,12222,12223,12378,12222,12378,12377,12223,12224,12379,12223,12379,12378,12224,12225,12380,12224,12380,12379,12225,12226,12381,12225,12381,12380,12226,12227,12382,12226,12382,12381,12227,12228,12383,12227,12383,12382,12228,12229,12384,12228,12384,12383,12229,12230,12385,12229,12385,12384,12230,12231,12386,12230,12386,12385,12231,12232,12387,12231,12387,12386,12232,12233,12388,12232,12388,12387,12233,12234,12389,12233,12389,12388,12234,12235,12390,12234,12390,12389,12235,12236,12391,12235,12391,12390,12236,12237,12392,12236,12392,12391,12237,12238,12393,12237,12393,12392,12238,12239,12394,12238,12394,12393,12239,12240,12395,12239,12395,12394,12240,12241,12396,12240,12396,12395,12241,12242,12397,12241,12397,12396,12242,12243,12398,12242,12398,12397,12243,12244,12399,12243,12399,12398,12245,12246,12401,12245,12401,12400,12246,12247,12402,12246,12402,12401,12247,12248,12403,12247,12403,12402,12248,12249,12404,12248,12404,12403,12249,12250,12405,12249,12405,12404,12250,12251,12406,12250,12406,12405,12251,12252,12407,12251,12407,12406,12252,12253,12408,12252,12408,12407,12253,12254,12409,12253,12409,12408,12254,12255,12410,12254,12410,12409,12255,12256,12411,12255,12411,12410,12256,12257,12412,12256,12412,12411,12257,12258,12413,12257,12413,12412,12258,12259,12414,12258,12414,12413,12259,12260,12415,12259,12415,12414,12260,12261,12416,12260,12416,12415,12261,12262,12417,12261,12417,12416,12262,12263,12418,12262,12418,12417,12263,12264,12419,12263,12419,12418,12264,12265,12420,12264,12420,12419,12265,12266,12421,12265,12421,12420,12266,12267,12422,12266,12422,12421,12267,12268,12423,12267,12423,12422,12268,12269,12424,12268,12424,12423,12269,12270,12425,12269,12425,12424,12270,12271,12426,12270,12426,12425,12271,12272,12427,12271,12427,12426,12272,12273,12428,12272,12428,12427,12273,12274,12429,12273,12429,12428,12274,12275,12430,12274,12430,12429,12275,12276,12431,12275,12431,12430,12276,12277,12432,12276,12432,12431,12277,12278,12433,12277,12433,12432,12278,12279,12434,12278,12434,12433,12279,12280,12435,12279,12435,12434,12280,12281,12436,12280,12436,12435,12281,12282,12437,12281,12437,12436,12282,12283,12438,12282,12438,12437,12283,12284,12439,12283,12439,12438,12284,12285,12440,12284,12440,12439,12285,12286,12441,12285,12441,12440,12286,12287,12442,12286,12442,12441,12287,12288,12443,12287,12443,12442,12288,12289,12444,12288,12444,12443,12289,12290,12445,12289,12445,12444,12290,12291,12446,12290,12446,12445,12291,12292,12447,12291,12447,12446,12292,12293,12448,12292,12448,12447,12293,12294,12449,12293,12449,12448,12294,12295,12450,12294,12450,12449,12295,12296,12451,12295,12451,12450,12296,12297,12452,12296,12452,12451,12297,12298,12453,12297,12453,12452,12298,12299,12454,12298,12454,12453,12299,12300,12455,12299,12455,12454,12300,12301,12456,12300,12456,12455,12301,12302,12457,12301,12457,12456,12302,12303,12458,12302,12458,12457,12303,12304,12459,12303,12459,12458,12304,12305,12460,12304,12460,12459,12305,12306,12461,12305,12461,12460,12306,12307,12462,12306,12462,12461,12307,12308,12463,12307,12463,12462,12308,12309,12464,12308,12464,12463,12309,12310,12465,12309,12465,12464,12310,12311,12466,12310,12466,12465,12311,12312,12467,12311,12467,12466,12312,12313,12468,12312,12468,12467,12313,12314,12469,12313,12469,12468,12314,12315,12470,12314,12470,12469,12315,12316,12471,12315,12471,12470,12316,12317,12472,12316,12472,12471,12317,12318,12473,12317,12473,12472,12318,12319,12474,12318,12474,12473,12319,12320,12475,12319,12475,12474,12320,12321,12476,12320,12476,12475,12321,12322,12477,12321,12477,12476,12322,12323,12478,12322,12478,12477,12323,12324,12479,12323,12479,12478,12324,12325,12480,12324,12480,12479,12325,12326,12481,12325,12481,12480,12326,12327,12482,12326,12482,12481,12327,12328,12483,12327,12483,12482,12328,12329,12484,12328,12484,12483,12329,12330,12485,12329,12485,12484,12330,12331,12486,12330,12486,12485,12331,12332,12487,12331,12487,12486,12332,12333,12488,12332,12488,12487,12333,12334,12489,12333,12489,12488,12334,12335,12490,12334,12490,12489,12335,12336,12491,12335,12491,12490,12336,12337,12492,12336,12492,12491,12337,12338,12493,12337,12493,12492,12338,12339,12494,12338,12494,12493,12339,12340,12495,12339,12495,12494,12340,12341,12496,12340,12496,12495,12341,12342,12497,12341,12497,12496,12342,12343,12498,12342,12498,12497,12343,12344,12499,12343,12499,12498,12344,12345,12500,12344,12500,12499,12345,12346,12501,12345,12501,12500,12346,12347,12502,12346,12502,12501,12347,12348,12503,12347,12503,12502,12348,12349,12504,12348,12504,12503,12349,12350,12505,12349,12505,12504,12350,12351,12506,12350,12506,12505,12351,12352,12507,12351,12507,12506,12352,12353,12508,12352,12508,12507,12353,12354,12509,12353,12509,12508,12354,12355,12510,12354,12510,12509,12355,12356,12511,12355,12511,12510,12356,12357,12512,12356,12512,12511,12357,12358,12513,12357,12513,12512,12358,12359,12514,12358,12514,12513,12359,12360,12515,12359,12515,12514,12360,12361,12516,12360,12516,12515,12361,12362,12517,12361,12517,12516,12362,12363,12518,12362,12518,12517,12363,12364,12519,12363,12519,12518,12364,12365,12520,12364,12520,12519,12365,12366,12521,12365,12521,12520,12366,12367,12522,12366,12522,12521,12367,12368,12523,12367,12523,12522,12368,12369,12524,12368,12524,12523,12369,12370,12525,12369,12525,12524,12370,12371,12526,12370,12526,12525,12371,12372,12527,12371,12527,12526,12372,12373,12528,12372,12528,12527,12373,12374,12529,12373,12529,12528,12374,12375,12530,12374,12530,12529,12375,12376,12531,12375,12531,12530,12376,12377,12532,12376,12532,12531,12377,12378,12533,12377,12533,12532,12378,12379,12534,12378,12534,12533,12379,12380,12535,12379,12535,12534,12380,12381,12536,12380,12536,12535,12381,12382,12537,12381,12537,12536,12382,12383,12538,12382,12538,12537,12383,12384,12539,12383,12539,12538,12384,12385,12540,12384,12540,12539,12385,12386,12541,12385,12541,12540,12386,12387,12542,12386,12542,12541,12387,12388,12543,12387,12543,12542,12388,12389,12544,12388,12544,12543,12389,12390,12545,12389,12545,12544,12390,12391,12546,12390,12546,12545,12391,12392,12547,12391,12547,12546,12392,12393,12548,12392,12548,12547,12393,12394,12549,12393,12549,12548,12394,12395,12550,12394,12550,12549,12395,12396,12551,12395,12551,12550,12396,12397,12552,12396,12552,12551,12397,12398,12553,12397,12553,12552,12398,12399,12554,12398,12554,12553,12400,12401,12556,12400,12556,12555,12401,12402,12557,12401,12557,12556,12402,12403,12558,12402,12558,12557,12403,12404,12559,12403,12559,12558,12404,12405,12560,12404,12560,12559,12405,12406,12561,12405,12561,12560,12406,12407,12562,12406,12562,12561,12407,12408,12563,12407,12563,12562,12408,12409,12564,12408,12564,12563,12409,12410,12565,12409,12565,12564,12410,12411,12566,12410,12566,12565,12411,12412,12567,12411,12567,12566,12412,12413,12568,12412,12568,12567,12413,12414,12569,12413,12569,12568,12414,12415,12570,12414,12570,12569,12415,12416,12571,12415,12571,12570,12416,12417,12572,12416,12572,12571,12417,12418,12573,12417,12573,12572,12418,12419,12574,12418,12574,12573,12419,12420,12575,12419,12575,12574,12420,12421,12576,12420,12576,12575,12421,12422,12577,12421,12577,12576,12422,12423,12578,12422,12578,12577,12423,12424,12579,12423,12579,12578,12424,12425,12580,12424,12580,12579,12425,12426,12581,12425,12581,12580,12426,12427,12582,12426,12582,12581,12427,12428,12583,12427,12583,12582,12428,12429,12584,12428,12584,12583,12429,12430,12585,12429,12585,12584,12430,12431,12586,12430,12586,12585,12431,12432,12587,12431,12587,12586,12432,12433,12588,12432,12588,12587,12433,12434,12589,12433,12589,12588,12434,12435,12590,12434,12590,12589,12435,12436,12591,12435,12591,12590,12436,12437,12592,12436,12592,12591,12437,12438,12593,12437,12593,12592,12438,12439,12594,12438,12594,12593,12439,12440,12595,12439,12595,12594,12440,12441,12596,12440,12596,12595,12441,12442,12597,12441,12597,12596,12442,12443,12598,12442,12598,12597,12443,12444,12599,12443,12599,12598,12444,12445,12600,12444,12600,12599,12445,12446,12601,12445,12601,12600,12446,12447,12602,12446,12602,12601,12447,12448,12603,12447,12603,12602,12448,12449,12604,12448,12604,12603,12449,12450,12605,12449,12605,12604,12450,12451,12606,12450,12606,12605,12451,12452,12607,12451,12607,12606,12452,12453,12608,12452,12608,12607,12453,12454,12609,12453,12609,12608,12454,12455,12610,12454,12610,12609,12455,12456,12611,12455,12611,12610,12456,12457,12612,12456,12612,12611,12457,12458,12613,12457,12613,12612,12458,12459,12614,12458,12614,12613,12459,12460,12615,12459,12615,12614,12460,12461,12616,12460,12616,12615,12461,12462,12617,12461,12617,12616,12462,12463,12618,12462,12618,12617,12463,12464,12619,12463,12619,12618,12464,12465,12620,12464,12620,12619,12465,12466,12621,12465,12621,12620,12466,12467,12622,12466,12622,12621,12467,12468,12623,12467,12623,12622,12468,12469,12624,12468,12624,12623,12469,12470,12625,12469,12625,12624,12470,12471,12626,12470,12626,12625,12471,12472,12627,12471,12627,12626,12472,12473,12628,12472,12628,12627,12473,12474,12629,12473,12629,12628,12474,12475,12630,12474,12630,12629,12475,12476,12631,12475,12631,12630,12476,12477,12632,12476,12632,12631,12477,12478,12633,12477,12633,12632,12478,12479,12634,12478,12634,12633,12479,12480,12635,12479,12635,12634,12480,12481,12636,12480,12636,12635,12481,12482,12637,12481,12637,12636,12482,12483,12638,12482,12638,12637,12483,12484,12639,12483,12639,12638,12484,12485,12640,12484,12640,12639,12485,12486,12641,12485,12641,12640,12486,12487,12642,12486,12642,12641,12487,12488,12643,12487,12643,12642,12488,12489,12644,12488,12644,12643,12489,12490,12645,12489,12645,12644,12490,12491,12646,12490,12646,12645,12491,12492,12647,12491,12647,12646,12492,12493,12648,12492,12648,12647,12493,12494,12649,12493,12649,12648,12494,12495,12650,12494,12650,12649,12495,12496,12651,12495,12651,12650,12496,12497,12652,12496,12652,12651,12497,12498,12653,12497,12653,12652,12498,12499,12654,12498,12654,12653,12499,12500,12655,12499,12655,12654,12500,12501,12656,12500,12656,12655,12501,12502,12657,12501,12657,12656,12502,12503,12658,12502,12658,12657,12503,12504,12659,12503,12659,12658,12504,12505,12660,12504,12660,12659,12505,12506,12661,12505,12661,12660,12506,12507,12662,12506,12662,12661,12507,12508,12663,12507,12663,12662,12508,12509,12664,12508,12664,12663,12509,12510,12665,12509,12665,12664,12510,12511,12666,12510,12666,12665,12511,12512,12667,12511,12667,12666,12512,12513,12668,12512,12668,12667,12513,12514,12669,12513,12669,12668,12514,12515,12670,12514,12670,12669,12515,12516,12671,12515,12671,12670,12516,12517,12672,12516,12672,12671,12517,12518,12673,12517,12673,12672,12518,12519,12674,12518,12674,12673,12519,12520,12675,12519,12675,12674,12520,12521,12676,12520,12676,12675,12521,12522,12677,12521,12677,12676,12522,12523,12678,12522,12678,12677,12523,12524,12679,12523,12679,12678,12524,12525,12680,12524,12680,12679,12525,12526,12681,12525,12681,12680,12526,12527,12682,12526,12682,12681,12527,12528,12683,12527,12683,12682,12528,12529,12684,12528,12684,12683,12529,12530,12685,12529,12685,12684,12530,12531,12686,12530,12686,12685,12531,12532,12687,12531,12687,12686,12532,12533,12688,12532,12688,12687,12533,12534,12689,12533,12689,12688,12534,12535,12690,12534,12690,12689,12535,12536,12691,12535,12691,12690,12536,12537,12692,12536,12692,12691,12537,12538,12693,12537,12693,12692,12538,12539,12694,12538,12694,12693,12539,12540,12695,12539,12695,12694,12540,12541,12696,12540,12696,12695,12541,12542,12697,12541,12697,12696,12542,12543,12698,12542,12698,12697,12543,12544,12699,12543,12699,12698,12544,12545,12700,12544,12700,12699,12545,12546,12701,12545,12701,12700,12546,12547,12702,12546,12702,12701,12547,12548,12703,12547,12703,12702,12548,12549,12704,12548,12704,12703,12549,12550,12705,12549,12705,12704,12550,12551,12706,12550,12706,12705,12551,12552,12707,12551,12707,12706,12552,12553,12708,12552,12708,12707,12553,12554,12709,12553,12709,12708,12555,12556,12711,12555,12711,12710,12556,12557,12712,12556,12712,12711,12557,12558,12713,12557,12713,12712,12558,12559,12714,12558,12714,12713,12559,12560,12715,12559,12715,12714,12560,12561,12716,12560,12716,12715,12561,12562,12717,12561,12717,12716,12562,12563,12718,12562,12718,12717,12563,12564,12719,12563,12719,12718,12564,12565,12720,12564,12720,12719,12565,12566,12721,12565,12721,12720,12566,12567,12722,12566,12722,12721,12567,12568,12723,12567,12723,12722,12568,12569,12724,12568,12724,12723,12569,12570,12725,12569,12725,12724,12570,12571,12726,12570,12726,12725,12571,12572,12727,12571,12727,12726,12572,12573,12728,12572,12728,12727,12573,12574,12729,12573,12729,12728,12574,12575,12730,12574,12730,12729,12575,12576,12731,12575,12731,12730,12576,12577,12732,12576,12732,12731,12577,12578,12733,12577,12733,12732,12578,12579,12734,12578,12734,12733,12579,12580,12735,12579,12735,12734,12580,12581,12736,12580,12736,12735,12581,12582,12737,12581,12737,12736,12582,12583,12738,12582,12738,12737,12583,12584,12739,12583,12739,12738,12584,12585,12740,12584,12740,12739,12585,12586,12741,12585,12741,12740,12586,12587,12742,12586,12742,12741,12587,12588,12743,12587,12743,12742,12588,12589,12744,12588,12744,12743,12589,12590,12745,12589,12745,12744,12590,12591,12746,12590,12746,12745,12591,12592,12747,12591,12747,12746,12592,12593,12748,12592,12748,12747,12593,12594,12749,12593,12749,12748,12594,12595,12750,12594,12750,12749,12595,12596,12751,12595,12751,12750,12596,12597,12752,12596,12752,12751,12597,12598,12753,12597,12753,12752,12598,12599,12754,12598,12754,12753,12599,12600,12755,12599,12755,12754,12600,12601,12756,12600,12756,12755,12601,12602,12757,12601,12757,12756,12602,12603,12758,12602,12758,12757,12603,12604,12759,12603,12759,12758,12604,12605,12760,12604,12760,12759,12605,12606,12761,12605,12761,12760,12606,12607,12762,12606,12762,12761,12607,12608,12763,12607,12763,12762,12608,12609,12764,12608,12764,12763,12609,12610,12765,12609,12765,12764,12610,12611,12766,12610,12766,12765,12611,12612,12767,12611,12767,12766,12612,12613,12768,12612,12768,12767,12613,12614,12769,12613,12769,12768,12614,12615,12770,12614,12770,12769,12615,12616,12771,12615,12771,12770,12616,12617,12772,12616,12772,12771,12617,12618,12773,12617,12773,12772,12618,12619,12774,12618,12774,12773,12619,12620,12775,12619,12775,12774,12620,12621,12776,12620,12776,12775,12621,12622,12777,12621,12777,12776,12622,12623,12778,12622,12778,12777,12623,12624,12779,12623,12779,12778,12624,12625,12780,12624,12780,12779,12625,12626,12781,12625,12781,12780,12626,12627,12782,12626,12782,12781,12627,12628,12783,12627,12783,12782,12628,12629,12784,12628,12784,12783,12629,12630,12785,12629,12785,12784,12630,12631,12786,12630,12786,12785,12631,12632,12787,12631,12787,12786,12632,12633,12788,12632,12788,12787,12633,12634,12789,12633,12789,12788,12634,12635,12790,12634,12790,12789,12635,12636,12791,12635,12791,12790,12636,12637,12792,12636,12792,12791,12637,12638,12793,12637,12793,12792,12638,12639,12794,12638,12794,12793,12639,12640,12795,12639,12795,12794,12640,12641,12796,12640,12796,12795,12641,12642,12797,12641,12797,12796,12642,12643,12798,12642,12798,12797,12643,12644,12799,12643,12799,12798,12644,12645,12800,12644,12800,12799,12645,12646,12801,12645,12801,12800,12646,12647,12802,12646,12802,12801,12647,12648,12803,12647,12803,12802,12648,12649,12804,12648,12804,12803,12649,12650,12805,12649,12805,12804,12650,12651,12806,12650,12806,12805,12651,12652,12807,12651,12807,12806,12652,12653,12808,12652,12808,12807,12653,12654,12809,12653,12809,12808,12654,12655,12810,12654,12810,12809,12655,12656,12811,12655,12811,12810,12656,12657,12812,12656,12812,12811,12657,12658,12813,12657,12813,12812,12658,12659,12814,12658,12814,12813,12659,12660,12815,12659,12815,12814,12660,12661,12816,12660,12816,12815,12661,12662,12817,12661,12817,12816,12662,12663,12818,12662,12818,12817,12663,12664,12819,12663,12819,12818,12664,12665,12820,12664,12820,12819,12665,12666,12821,12665,12821,12820,12666,12667,12822,12666,12822,12821,12667,12668,12823,12667,12823,12822,12668,12669,12824,12668,12824,12823,12669,12670,12825,12669,12825,12824,12670,12671,12826,12670,12826,12825,12671,12672,12827,12671,12827,12826,12672,12673,12828,12672,12828,12827,12673,12674,12829,12673,12829,12828,12674,12675,12830,12674,12830,12829,12675,12676,12831,12675,12831,12830,12676,12677,12832,12676,12832,12831,12677,12678,12833,12677,12833,12832,12678,12679,12834,12678,12834,12833,12679,12680,12835,12679,12835,12834,12680,12681,12836,12680,12836,12835,12681,12682,12837,12681,12837,12836,12682,12683,12838,12682,12838,12837,12683,12684,12839,12683,12839,12838,12684,12685,12840,12684,12840,12839,12685,12686,12841,12685,12841,12840,12686,12687,12842,12686,12842,12841,12687,12688,12843,12687,12843,12842,12688,12689,12844,12688,12844,12843,12689,12690,12845,12689,12845,12844,12690,12691,12846,12690,12846,12845,12691,12692,12847,12691,12847,12846,12692,12693,12848,12692,12848,12847,12693,12694,12849,12693,12849,12848,12694,12695,12850,12694,12850,12849,12695,12696,12851,12695,12851,12850,12696,12697,12852,12696,12852,12851,12697,12698,12853,12697,12853,12852,12698,12699,12854,12698,12854,12853,12699,12700,12855,12699,12855,12854,12700,12701,12856,12700,12856,12855,12701,12702,12857,12701,12857,12856,12702,12703,12858,12702,12858,12857,12703,12704,12859,12703,12859,12858,12704,12705,12860,12704,12860,12859,12705,12706,12861,12705,12861,12860,12706,12707,12862,12706,12862,12861,12707,12708,12863,12707,12863,12862,12708,12709,12864,12708,12864,12863,12710,12711,12866,12710,12866,12865,12711,12712,12867,12711,12867,12866,12712,12713,12868,12712,12868,12867,12713,12714,12869,12713,12869,12868,12714,12715,12870,12714,12870,12869,12715,12716,12871,12715,12871,12870,12716,12717,12872,12716,12872,12871,12717,12718,12873,12717,12873,12872,12718,12719,12874,12718,12874,12873,12719,12720,12875,12719,12875,12874,12720,12721,12876,12720,12876,12875,12721,12722,12877,12721,12877,12876,12722,12723,12878,12722,12878,12877,12723,12724,12879,12723,12879,12878,12724,12725,12880,12724,12880,12879,12725,12726,12881,12725,12881,12880,12726,12727,12882,12726,12882,12881,12727,12728,12883,12727,12883,12882,12728,12729,12884,12728,12884,12883,12729,12730,12885,12729,12885,12884,12730,12731,12886,12730,12886,12885,12731,12732,12887,12731,12887,12886,12732,12733,12888,12732,12888,12887,12733,12734,12889,12733,12889,12888,12734,12735,12890,12734,12890,12889,12735,12736,12891,12735,12891,12890,12736,12737,12892,12736,12892,12891,12737,12738,12893,12737,12893,12892,12738,12739,12894,12738,12894,12893,12739,12740,12895,12739,12895,12894,12740,12741,12896,12740,12896,12895,12741,12742,12897,12741,12897,12896,12742,12743,12898,12742,12898,12897,12743,12744,12899,12743,12899,12898,12744,12745,12900,12744,12900,12899,12745,12746,12901,12745,12901,12900,12746,12747,12902,12746,12902,12901,12747,12748,12903,12747,12903,12902,12748,12749,12904,12748,12904,12903,12749,12750,12905,12749,12905,12904,12750,12751,12906,12750,12906,12905,12751,12752,12907,12751,12907,12906,12752,12753,12908,12752,12908,12907,12753,12754,12909,12753,12909,12908,12754,12755,12910,12754,12910,12909,12755,12756,12911,12755,12911,12910,12756,12757,12912,12756,12912,12911,12757,12758,12913,12757,12913,12912,12758,12759,12914,12758,12914,12913,12759,12760,12915,12759,12915,12914,12760,12761,12916,12760,12916,12915,12761,12762,12917,12761,12917,12916,12762,12763,12918,12762,12918,12917,12763,12764,12919,12763,12919,12918,12764,12765,12920,12764,12920,12919,12765,12766,12921,12765,12921,12920,12766,12767,12922,12766,12922,12921,12767,12768,12923,12767,12923,12922,12768,12769,12924,12768,12924,12923,12769,12770,12925,12769,12925,12924,12770,12771,12926,12770,12926,12925,12771,12772,12927,12771,12927,12926,12772,12773,12928,12772,12928,12927,12773,12774,12929,12773,12929,12928,12774,12775,12930,12774,12930,12929,12775,12776,12931,12775,12931,12930,12776,12777,12932,12776,12932,12931,12777,12778,12933,12777,12933,12932,12778,12779,12934,12778,12934,12933,12779,12780,12935,12779,12935,12934,12780,12781,12936,12780,12936,12935,12781,12782,12937,12781,12937,12936,12782,12783,12938,12782,12938,12937,12783,12784,12939,12783,12939,12938,12784,12785,12940,12784,12940,12939,12785,12786,12941,12785,12941,12940,12786,12787,12942,12786,12942,12941,12787,12788,12943,12787,12943,12942,12788,12789,12944,12788,12944,12943,12789,12790,12945,12789,12945,12944,12790,12791,12946,12790,12946,12945,12791,12792,12947,12791,12947,12946,12792,12793,12948,12792,12948,12947,12793,12794,12949,12793,12949,12948,12794,12795,12950,12794,12950,12949,12795,12796,12951,12795,12951,12950,12796,12797,12952,12796,12952,12951,12797,12798,12953,12797,12953,12952,12798,12799,12954,12798,12954,12953,12799,12800,12955,12799,12955,12954,12800,12801,12956,12800,12956,12955,12801,12802,12957,12801,12957,12956,12802,12803,12958,12802,12958,12957,12803,12804,12959,12803,12959,12958,12804,12805,12960,12804,12960,12959,12805,12806,12961,12805,12961,12960,12806,12807,12962,12806,12962,12961,12807,12808,12963,12807,12963,12962,12808,12809,12964,12808,12964,12963,12809,12810,12965,12809,12965,12964,12810,12811,12966,12810,12966,12965,12811,12812,12967,12811,12967,12966,12812,12813,12968,12812,12968,12967,12813,12814,12969,12813,12969,12968,12814,12815,12970,12814,12970,12969,12815,12816,12971,12815,12971,12970,12816,12817,12972,12816,12972,12971,12817,12818,12973,12817,12973,12972,12818,12819,12974,12818,12974,12973,12819,12820,12975,12819,12975,12974,12820,12821,12976,12820,12976,12975,12821,12822,12977,12821,12977,12976,12822,12823,12978,12822,12978,12977,12823,12824,12979,12823,12979,12978,12824,12825,12980,12824,12980,12979,12825,12826,12981,12825,12981,12980,12826,12827,12982,12826,12982,12981,12827,12828,12983,12827,12983,12982,12828,12829,12984,12828,12984,12983,12829,12830,12985,12829,12985,12984,12830,12831,12986,12830,12986,12985,12831,12832,12987,12831,12987,12986,12832,12833,12988,12832,12988,12987,12833,12834,12989,12833,12989,12988,12834,12835,12990,12834,12990,12989,12835,12836,12991,12835,12991,12990,12836,12837,12992,12836,12992,12991,12837,12838,12993,12837,12993,12992,12838,12839,12994,12838,12994,12993,12839,12840,12995,12839,12995,12994,12840,12841,12996,12840,12996,12995,12841,12842,12997,12841,12997,12996,12842,12843,12998,12842,12998,12997,12843,12844,12999,12843,12999,12998,12844,12845,13000,12844,13000,12999,12845,12846,13001,12845,13001,13000,12846,12847,13002,12846,13002,13001,12847,12848,13003,12847,13003,13002,12848,12849,13004,12848,13004,13003,12849,12850,13005,12849,13005,13004,12850,12851,13006,12850,13006,13005,12851,12852,13007,12851,13007,13006,12852,12853,13008,12852,13008,13007,12853,12854,13009,12853,13009,13008,12854,12855,13010,12854,13010,13009,12855,12856,13011,12855,13011,13010,12856,12857,13012,12856,13012,13011,12857,12858,13013,12857,13013,13012,12858,12859,13014,12858,13014,13013,12859,12860,13015,12859,13015,13014,12860,12861,13016,12860,13016,13015,12861,12862,13017,12861,13017,13016,12862,12863,13018,12862,13018,13017,12863,12864,13019,12863,13019,13018,12865,12866,13021,12865,13021,13020,12866,12867,13022,12866,13022,13021,12867,12868,13023,12867,13023,13022,12868,12869,13024,12868,13024,13023,12869,12870,13025,12869,13025,13024,12870,12871,13026,12870,13026,13025,12871,12872,13027,12871,13027,13026,12872,12873,13028,12872,13028,13027,12873,12874,13029,12873,13029,13028,12874,12875,13030,12874,13030,13029,12875,12876,13031,12875,13031,13030,12876,12877,13032,12876,13032,13031,12877,12878,13033,12877,13033,13032,12878,12879,13034,12878,13034,13033,12879,12880,13035,12879,13035,13034,12880,12881,13036,12880,13036,13035,12881,12882,13037,12881,13037,13036,12882,12883,13038,12882,13038,13037,12883,12884,13039,12883,13039,13038,12884,12885,13040,12884,13040,13039,12885,12886,13041,12885,13041,13040,12886,12887,13042,12886,13042,13041,12887,12888,13043,12887,13043,13042,12888,12889,13044,12888,13044,13043,12889,12890,13045,12889,13045,13044,12890,12891,13046,12890,13046,13045,12891,12892,13047,12891,13047,13046,12892,12893,13048,12892,13048,13047,12893,12894,13049,12893,13049,13048,12894,12895,13050,12894,13050,13049,12895,12896,13051,12895,13051,13050,12896,12897,13052,12896,13052,13051,12897,12898,13053,12897,13053,13052,12898,12899,13054,12898,13054,13053,12899,12900,13055,12899,13055,13054,12900,12901,13056,12900,13056,13055,12901,12902,13057,12901,13057,13056,12902,12903,13058,12902,13058,13057,12903,12904,13059,12903,13059,13058,12904,12905,13060,12904,13060,13059,12905,12906,13061,12905,13061,13060,12906,12907,13062,12906,13062,13061,12907,12908,13063,12907,13063,13062,12908,12909,13064,12908,13064,13063,12909,12910,13065,12909,13065,13064,12910,12911,13066,12910,13066,13065,12911,12912,13067,12911,13067,13066,12912,12913,13068,12912,13068,13067,12913,12914,13069,12913,13069,13068,12914,12915,13070,12914,13070,13069,12915,12916,13071,12915,13071,13070,12916,12917,13072,12916,13072,13071,12917,12918,13073,12917,13073,13072,12918,12919,13074,12918,13074,13073,12919,12920,13075,12919,13075,13074,12920,12921,13076,12920,13076,13075,12921,12922,13077,12921,13077,13076,12922,12923,13078,12922,13078,13077,12923,12924,13079,12923,13079,13078,12924,12925,13080,12924,13080,13079,12925,12926,13081,12925,13081,13080,12926,12927,13082,12926,13082,13081,12927,12928,13083,12927,13083,13082,12928,12929,13084,12928,13084,13083,12929,12930,13085,12929,13085,13084,12930,12931,13086,12930,13086,13085,12931,12932,13087,12931,13087,13086,12932,12933,13088,12932,13088,13087,12933,12934,13089,12933,13089,13088,12934,12935,13090,12934,13090,13089,12935,12936,13091,12935,13091,13090,12936,12937,13092,12936,13092,13091,12937,12938,13093,12937,13093,13092,12938,12939,13094,12938,13094,13093,12939,12940,13095,12939,13095,13094,12940,12941,13096,12940,13096,13095,12941,12942,13097,12941,13097,13096,12942,12943,13098,12942,13098,13097,12943,12944,13099,12943,13099,13098,12944,12945,13100,12944,13100,13099,12945,12946,13101,12945,13101,13100,12946,12947,13102,12946,13102,13101,12947,12948,13103,12947,13103,13102,12948,12949,13104,12948,13104,13103,12949,12950,13105,12949,13105,13104,12950,12951,13106,12950,13106,13105,12951,12952,13107,12951,13107,13106,12952,12953,13108,12952,13108,13107,12953,12954,13109,12953,13109,13108,12954,12955,13110,12954,13110,13109,12955,12956,13111,12955,13111,13110,12956,12957,13112,12956,13112,13111,12957,12958,13113,12957,13113,13112,12958,12959,13114,12958,13114,13113,12959,12960,13115,12959,13115,13114,12960,12961,13116,12960,13116,13115,12961,12962,13117,12961,13117,13116,12962,12963,13118,12962,13118,13117,12963,12964,13119,12963,13119,13118,12964,12965,13120,12964,13120,13119,12965,12966,13121,12965,13121,13120,12966,12967,13122,12966,13122,13121,12967,12968,13123,12967,13123,13122,12968,12969,13124,12968,13124,13123,12969,12970,13125,12969,13125,13124,12970,12971,13126,12970,13126,13125,12971,12972,13127,12971,13127,13126,12972,12973,13128,12972,13128,13127,12973,12974,13129,12973,13129,13128,12974,12975,13130,12974,13130,13129,12975,12976,13131,12975,13131,13130,12976,12977,13132,12976,13132,13131,12977,12978,13133,12977,13133,13132,12978,12979,13134,12978,13134,13133,12979,12980,13135,12979,13135,13134,12980,12981,13136,12980,13136,13135,12981,12982,13137,12981,13137,13136,12982,12983,13138,12982,13138,13137,12983,12984,13139,12983,13139,13138,12984,12985,13140,12984,13140,13139,12985,12986,13141,12985,13141,13140,12986,12987,13142,12986,13142,13141,12987,12988,13143,12987,13143,13142,12988,12989,13144,12988,13144,13143,12989,12990,13145,12989,13145,13144,12990,12991,13146,12990,13146,13145,12991,12992,13147,12991,13147,13146,12992,12993,13148,12992,13148,13147,12993,12994,13149,12993,13149,13148,12994,12995,13150,12994,13150,13149,12995,12996,13151,12995,13151,13150,12996,12997,13152,12996,13152,13151,12997,12998,13153,12997,13153,13152,12998,12999,13154,12998,13154,13153,12999,13000,13155,12999,13155,13154,13000,13001,13156,13000,13156,13155,13001,13002,13157,13001,13157,13156,13002,13003,13158,13002,13158,13157,13003,13004,13159,13003,13159,13158,13004,13005,13160,13004,13160,13159,13005,13006,13161,13005,13161,13160,13006,13007,13162,13006,13162,13161,13007,13008,13163,13007,13163,13162,13008,13009,13164,13008,13164,13163,13009,13010,13165,13009,13165,13164,13010,13011,13166,13010,13166,13165,13011,13012,13167,13011,13167,13166,13012,13013,13168,13012,13168,13167,13013,13014,13169,13013,13169,13168,13014,13015,13170,13014,13170,13169,13015,13016,13171,13015,13171,13170,13016,13017,13172,13016,13172,13171,13017,13018,13173,13017,13173,13172,13018,13019,13174,13018,13174,13173,13020,13021,13176,13020,13176,13175,13021,13022,13177,13021,13177,13176,13022,13023,13178,13022,13178,13177,13023,13024,13179,13023,13179,13178,13024,13025,13180,13024,13180,13179,13025,13026,13181,13025,13181,13180,13026,13027,13182,13026,13182,13181,13027,13028,13183,13027,13183,13182,13028,13029,13184,13028,13184,13183,13029,13030,13185,13029,13185,13184,13030,13031,13186,13030,13186,13185,13031,13032,13187,13031,13187,13186,13032,13033,13188,13032,13188,13187,13033,13034,13189,13033,13189,13188,13034,13035,13190,13034,13190,13189,13035,13036,13191,13035,13191,13190,13036,13037,13192,13036,13192,13191,13037,13038,13193,13037,13193,13192,13038,13039,13194,13038,13194,13193,13039,13040,13195,13039,13195,13194,13040,13041,13196,13040,13196,13195,13041,13042,13197,13041,13197,13196,13042,13043,13198,13042,13198,13197,13043,13044,13199,13043,13199,13198,13044,13045,13200,13044,13200,13199,13045,13046,13201,13045,13201,13200,13046,13047,13202,13046,13202,13201,13047,13048,13203,13047,13203,13202,13048,13049,13204,13048,13204,13203,13049,13050,13205,13049,13205,13204,13050,13051,13206,13050,13206,13205,13051,13052,13207,13051,13207,13206,13052,13053,13208,13052,13208,13207,13053,13054,13209,13053,13209,13208,13054,13055,13210,13054,13210,13209,13055,13056,13211,13055,13211,13210,13056,13057,13212,13056,13212,13211,13057,13058,13213,13057,13213,13212,13058,13059,13214,13058,13214,13213,13059,13060,13215,13059,13215,13214,13060,13061,13216,13060,13216,13215,13061,13062,13217,13061,13217,13216,13062,13063,13218,13062,13218,13217,13063,13064,13219,13063,13219,13218,13064,13065,13220,13064,13220,13219,13065,13066,13221,13065,13221,13220,13066,13067,13222,13066,13222,13221,13067,13068,13223,13067,13223,13222,13068,13069,13224,13068,13224,13223,13069,13070,13225,13069,13225,13224,13070,13071,13226,13070,13226,13225,13071,13072,13227,13071,13227,13226,13072,13073,13228,13072,13228,13227,13073,13074,13229,13073,13229,13228,13074,13075,13230,13074,13230,13229,13075,13076,13231,13075,13231,13230,13076,13077,13232,13076,13232,13231,13077,13078,13233,13077,13233,13232,13078,13079,13234,13078,13234,13233,13079,13080,13235,13079,13235,13234,13080,13081,13236,13080,13236,13235,13081,13082,13237,13081,13237,13236,13082,13083,13238,13082,13238,13237,13083,13084,13239,13083,13239,13238,13084,13085,13240,13084,13240,13239,13085,13086,13241,13085,13241,13240,13086,13087,13242,13086,13242,13241,13087,13088,13243,13087,13243,13242,13088,13089,13244,13088,13244,13243,13089,13090,13245,13089,13245,13244,13090,13091,13246,13090,13246,13245,13091,13092,13247,13091,13247,13246,13092,13093,13248,13092,13248,13247,13093,13094,13249,13093,13249,13248,13094,13095,13250,13094,13250,13249,13095,13096,13251,13095,13251,13250,13096,13097,13252,13096,13252,13251,13097,13098,13253,13097,13253,13252,13098,13099,13254,13098,13254,13253,13099,13100,13255,13099,13255,13254,13100,13101,13256,13100,13256,13255,13101,13102,13257,13101,13257,13256,13102,13103,13258,13102,13258,13257,13103,13104,13259,13103,13259,13258,13104,13105,13260,13104,13260,13259,13105,13106,13261,13105,13261,13260,13106,13107,13262,13106,13262,13261,13107,13108,13263,13107,13263,13262,13108,13109,13264,13108,13264,13263,13109,13110,13265,13109,13265,13264,13110,13111,13266,13110,13266,13265,13111,13112,13267,13111,13267,13266,13112,13113,13268,13112,13268,13267,13113,13114,13269,13113,13269,13268,13114,13115,13270,13114,13270,13269,13115,13116,13271,13115,13271,13270,13116,13117,13272,13116,13272,13271,13117,13118,13273,13117,13273,13272,13118,13119,13274,13118,13274,13273,13119,13120,13275,13119,13275,13274,13120,13121,13276,13120,13276,13275,13121,13122,13277,13121,13277,13276,13122,13123,13278,13122,13278,13277,13123,13124,13279,13123,13279,13278,13124,13125,13280,13124,13280,13279,13125,13126,13281,13125,13281,13280,13126,13127,13282,13126,13282,13281,13127,13128,13283,13127,13283,13282,13128,13129,13284,13128,13284,13283,13129,13130,13285,13129,13285,13284,13130,13131,13286,13130,13286,13285,13131,13132,13287,13131,13287,13286,13132,13133,13288,13132,13288,13287,13133,13134,13289,13133,13289,13288,13134,13135,13290,13134,13290,13289,13135,13136,13291,13135,13291,13290,13136,13137,13292,13136,13292,13291,13137,13138,13293,13137,13293,13292,13138,13139,13294,13138,13294,13293,13139,13140,13295,13139,13295,13294,13140,13141,13296,13140,13296,13295,13141,13142,13297,13141,13297,13296,13142,13143,13298,13142,13298,13297,13143,13144,13299,13143,13299,13298,13144,13145,13300,13144,13300,13299,13145,13146,13301,13145,13301,13300,13146,13147,13302,13146,13302,13301,13147,13148,13303,13147,13303,13302,13148,13149,13304,13148,13304,13303,13149,13150,13305,13149,13305,13304,13150,13151,13306,13150,13306,13305,13151,13152,13307,13151,13307,13306,13152,13153,13308,13152,13308,13307,13153,13154,13309,13153,13309,13308,13154,13155,13310,13154,13310,13309,13155,13156,13311,13155,13311,13310,13156,13157,13312,13156,13312,13311,13157,13158,13313,13157,13313,13312,13158,13159,13314,13158,13314,13313,13159,13160,13315,13159,13315,13314,13160,13161,13316,13160,13316,13315,13161,13162,13317,13161,13317,13316,13162,13163,13318,13162,13318,13317,13163,13164,13319,13163,13319,13318,13164,13165,13320,13164,13320,13319,13165,13166,13321,13165,13321,13320,13166,13167,13322,13166,13322,13321,13167,13168,13323,13167,13323,13322,13168,13169,13324,13168,13324,13323,13169,13170,13325,13169,13325,13324,13170,13171,13326,13170,13326,13325,13171,13172,13327,13171,13327,13326,13172,13173,13328,13172,13328,13327,13173,13174,13329,13173,13329,13328,13175,13176,13331,13175,13331,13330,13176,13177,13332,13176,13332,13331,13177,13178,13333,13177,13333,13332,13178,13179,13334,13178,13334,13333,13179,13180,13335,13179,13335,13334,13180,13181,13336,13180,13336,13335,13181,13182,13337,13181,13337,13336,13182,13183,13338,13182,13338,13337,13183,13184,13339,13183,13339,13338,13184,13185,13340,13184,13340,13339,13185,13186,13341,13185,13341,13340,13186,13187,13342,13186,13342,13341,13187,13188,13343,13187,13343,13342,13188,13189,13344,13188,13344,13343,13189,13190,13345,13189,13345,13344,13190,13191,13346,13190,13346,13345,13191,13192,13347,13191,13347,13346,13192,13193,13348,13192,13348,13347,13193,13194,13349,13193,13349,13348,13194,13195,13350,13194,13350,13349,13195,13196,13351,13195,13351,13350,13196,13197,13352,13196,13352,13351,13197,13198,13353,13197,13353,13352,13198,13199,13354,13198,13354,13353,13199,13200,13355,13199,13355,13354,13200,13201,13356,13200,13356,13355,13201,13202,13357,13201,13357,13356,13202,13203,13358,13202,13358,13357,13203,13204,13359,13203,13359,13358,13204,13205,13360,13204,13360,13359,13205,13206,13361,13205,13361,13360,13206,13207,13362,13206,13362,13361,13207,13208,13363,13207,13363,13362,13208,13209,13364,13208,13364,13363,13209,13210,13365,13209,13365,13364,13210,13211,13366,13210,13366,13365,13211,13212,13367,13211,13367,13366,13212,13213,13368,13212,13368,13367,13213,13214,13369,13213,13369,13368,13214,13215,13370,13214,13370,13369,13215,13216,13371,13215,13371,13370,13216,13217,13372,13216,13372,13371,13217,13218,13373,13217,13373,13372,13218,13219,13374,13218,13374,13373,13219,13220,13375,13219,13375,13374,13220,13221,13376,13220,13376,13375,13221,13222,13377,13221,13377,13376,13222,13223,13378,13222,13378,13377,13223,13224,13379,13223,13379,13378,13224,13225,13380,13224,13380,13379,13225,13226,13381,13225,13381,13380,13226,13227,13382,13226,13382,13381,13227,13228,13383,13227,13383,13382,13228,13229,13384,13228,13384,13383,13229,13230,13385,13229,13385,13384,13230,13231,13386,13230,13386,13385,13231,13232,13387,13231,13387,13386,13232,13233,13388,13232,13388,13387,13233,13234,13389,13233,13389,13388,13234,13235,13390,13234,13390,13389,13235,13236,13391,13235,13391,13390,13236,13237,13392,13236,13392,13391,13237,13238,13393,13237,13393,13392,13238,13239,13394,13238,13394,13393,13239,13240,13395,13239,13395,13394,13240,13241,13396,13240,13396,13395,13241,13242,13397,13241,13397,13396,13242,13243,13398,13242,13398,13397,13243,13244,13399,13243,13399,13398,13244,13245,13400,13244,13400,13399,13245,13246,13401,13245,13401,13400,13246,13247,13402,13246,13402,13401,13247,13248,13403,13247,13403,13402,13248,13249,13404,13248,13404,13403,13249,13250,13405,13249,13405,13404,13250,13251,13406,13250,13406,13405,13251,13252,13407,13251,13407,13406,13252,13253,13408,13252,13408,13407,13253,13254,13409,13253,13409,13408,13254,13255,13410,13254,13410,13409,13255,13256,13411,13255,13411,13410,13256,13257,13412,13256,13412,13411,13257,13258,13413,13257,13413,13412,13258,13259,13414,13258,13414,13413,13259,13260,13415,13259,13415,13414,13260,13261,13416,13260,13416,13415,13261,13262,13417,13261,13417,13416,13262,13263,13418,13262,13418,13417,13263,13264,13419,13263,13419,13418,13264,13265,13420,13264,13420,13419,13265,13266,13421,13265,13421,13420,13266,13267,13422,13266,13422,13421,13267,13268,13423,13267,13423,13422,13268,13269,13424,13268,13424,13423,13269,13270,13425,13269,13425,13424,13270,13271,13426,13270,13426,13425,13271,13272,13427,13271,13427,13426,13272,13273,13428,13272,13428,13427,13273,13274,13429,13273,13429,13428,13274,13275,13430,13274,13430,13429,13275,13276,13431,13275,13431,13430,13276,13277,13432,13276,13432,13431,13277,13278,13433,13277,13433,13432,13278,13279,13434,13278,13434,13433,13279,13280,13435,13279,13435,13434,13280,13281,13436,13280,13436,13435,13281,13282,13437,13281,13437,13436,13282,13283,13438,13282,13438,13437,13283,13284,13439,13283,13439,13438,13284,13285,13440,13284,13440,13439,13285,13286,13441,13285,13441,13440,13286,13287,13442,13286,13442,13441,13287,13288,13443,13287,13443,13442,13288,13289,13444,13288,13444,13443,13289,13290,13445,13289,13445,13444,13290,13291,13446,13290,13446,13445,13291,13292,13447,13291,13447,13446,13292,13293,13448,13292,13448,13447,13293,13294,13449,13293,13449,13448,13294,13295,13450,13294,13450,13449,13295,13296,13451,13295,13451,13450,13296,13297,13452,13296,13452,13451,13297,13298,13453,13297,13453,13452,13298,13299,13454,13298,13454,13453,13299,13300,13455,13299,13455,13454,13300,13301,13456,13300,13456,13455,13301,13302,13457,13301,13457,13456,13302,13303,13458,13302,13458,13457,13303,13304,13459,13303,13459,13458,13304,13305,13460,13304,13460,13459,13305,13306,13461,13305,13461,13460,13306,13307,13462,13306,13462,13461,13307,13308,13463,13307,13463,13462,13308,13309,13464,13308,13464,13463,13309,13310,13465,13309,13465,13464,13310,13311,13466,13310,13466,13465,13311,13312,13467,13311,13467,13466,13312,13313,13468,13312,13468,13467,13313,13314,13469,13313,13469,13468,13314,13315,13470,13314,13470,13469,13315,13316,13471,13315,13471,13470,13316,13317,13472,13316,13472,13471,13317,13318,13473,13317,13473,13472,13318,13319,13474,13318,13474,13473,13319,13320,13475,13319,13475,13474,13320,13321,13476,13320,13476,13475,13321,13322,13477,13321,13477,13476,13322,13323,13478,13322,13478,13477,13323,13324,13479,13323,13479,13478,13324,13325,13480,13324,13480,13479,13325,13326,13481,13325,13481,13480,13326,13327,13482,13326,13482,13481,13327,13328,13483,13327,13483,13482,13328,13329,13484,13328,13484,13483,13330,13331,13486,13330,13486,13485,13331,13332,13487,13331,13487,13486,13332,13333,13488,13332,13488,13487,13333,13334,13489,13333,13489,13488,13334,13335,13490,13334,13490,13489,13335,13336,13491,13335,13491,13490,13336,13337,13492,13336,13492,13491,13337,13338,13493,13337,13493,13492,13338,13339,13494,13338,13494,13493,13339,13340,13495,13339,13495,13494,13340,13341,13496,13340,13496,13495,13341,13342,13497,13341,13497,13496,13342,13343,13498,13342,13498,13497,13343,13344,13499,13343,13499,13498,13344,13345,13500,13344,13500,13499,13345,13346,13501,13345,13501,13500,13346,13347,13502,13346,13502,13501,13347,13348,13503,13347,13503,13502,13348,13349,13504,13348,13504,13503,13349,13350,13505,13349,13505,13504,13350,13351,13506,13350,13506,13505,13351,13352,13507,13351,13507,13506,13352,13353,13508,13352,13508,13507,13353,13354,13509,13353,13509,13508,13354,13355,13510,13354,13510,13509,13355,13356,13511,13355,13511,13510,13356,13357,13512,13356,13512,13511,13357,13358,13513,13357,13513,13512,13358,13359,13514,13358,13514,13513,13359,13360,13515,13359,13515,13514,13360,13361,13516,13360,13516,13515,13361,13362,13517,13361,13517,13516,13362,13363,13518,13362,13518,13517,13363,13364,13519,13363,13519,13518,13364,13365,13520,13364,13520,13519,13365,13366,13521,13365,13521,13520,13366,13367,13522,13366,13522,13521,13367,13368,13523,13367,13523,13522,13368,13369,13524,13368,13524,13523,13369,13370,13525,13369,13525,13524,13370,13371,13526,13370,13526,13525,13371,13372,13527,13371,13527,13526,13372,13373,13528,13372,13528,13527,13373,13374,13529,13373,13529,13528,13374,13375,13530,13374,13530,13529,13375,13376,13531,13375,13531,13530,13376,13377,13532,13376,13532,13531,13377,13378,13533,13377,13533,13532,13378,13379,13534,13378,13534,13533,13379,13380,13535,13379,13535,13534,13380,13381,13536,13380,13536,13535,13381,13382,13537,13381,13537,13536,13382,13383,13538,13382,13538,13537,13383,13384,13539,13383,13539,13538,13384,13385,13540,13384,13540,13539,13385,13386,13541,13385,13541,13540,13386,13387,13542,13386,13542,13541,13387,13388,13543,13387,13543,13542,13388,13389,13544,13388,13544,13543,13389,13390,13545,13389,13545,13544,13390,13391,13546,13390,13546,13545,13391,13392,13547,13391,13547,13546,13392,13393,13548,13392,13548,13547,13393,13394,13549,13393,13549,13548,13394,13395,13550,13394,13550,13549,13395,13396,13551,13395,13551,13550,13396,13397,13552,13396,13552,13551,13397,13398,13553,13397,13553,13552,13398,13399,13554,13398,13554,13553,13399,13400,13555,13399,13555,13554,13400,13401,13556,13400,13556,13555,13401,13402,13557,13401,13557,13556,13402,13403,13558,13402,13558,13557,13403,13404,13559,13403,13559,13558,13404,13405,13560,13404,13560,13559,13405,13406,13561,13405,13561,13560,13406,13407,13562,13406,13562,13561,13407,13408,13563,13407,13563,13562,13408,13409,13564,13408,13564,13563,13409,13410,13565,13409,13565,13564,13410,13411,13566,13410,13566,13565,13411,13412,13567,13411,13567,13566,13412,13413,13568,13412,13568,13567,13413,13414,13569,13413,13569,13568,13414,13415,13570,13414,13570,13569,13415,13416,13571,13415,13571,13570,13416,13417,13572,13416,13572,13571,13417,13418,13573,13417,13573,13572,13418,13419,13574,13418,13574,13573,13419,13420,13575,13419,13575,13574,13420,13421,13576,13420,13576,13575,13421,13422,13577,13421,13577,13576,13422,13423,13578,13422,13578,13577,13423,13424,13579,13423,13579,13578,13424,13425,13580,13424,13580,13579,13425,13426,13581,13425,13581,13580,13426,13427,13582,13426,13582,13581,13427,13428,13583,13427,13583,13582,13428,13429,13584,13428,13584,13583,13429,13430,13585,13429,13585,13584,13430,13431,13586,13430,13586,13585,13431,13432,13587,13431,13587,13586,13432,13433,13588,13432,13588,13587,13433,13434,13589,13433,13589,13588,13434,13435,13590,13434,13590,13589,13435,13436,13591,13435,13591,13590,13436,13437,13592,13436,13592,13591,13437,13438,13593,13437,13593,13592,13438,13439,13594,13438,13594,13593,13439,13440,13595,13439,13595,13594,13440,13441,13596,13440,13596,13595,13441,13442,13597,13441,13597,13596,13442,13443,13598,13442,13598,13597,13443,13444,13599,13443,13599,13598,13444,13445,13600,13444,13600,13599,13445,13446,13601,13445,13601,13600,13446,13447,13602,13446,13602,13601,13447,13448,13603,13447,13603,13602,13448,13449,13604,13448,13604,13603,13449,13450,13605,13449,13605,13604,13450,13451,13606,13450,13606,13605,13451,13452,13607,13451,13607,13606,13452,13453,13608,13452,13608,13607,13453,13454,13609,13453,13609,13608,13454,13455,13610,13454,13610,13609,13455,13456,13611,13455,13611,13610,13456,13457,13612,13456,13612,13611,13457,13458,13613,13457,13613,13612,13458,13459,13614,13458,13614,13613,13459,13460,13615,13459,13615,13614,13460,13461,13616,13460,13616,13615,13461,13462,13617,13461,13617,13616,13462,13463,13618,13462,13618,13617,13463,13464,13619,13463,13619,13618,13464,13465,13620,13464,13620,13619,13465,13466,13621,13465,13621,13620,13466,13467,13622,13466,13622,13621,13467,13468,13623,13467,13623,13622,13468,13469,13624,13468,13624,13623,13469,13470,13625,13469,13625,13624,13470,13471,13626,13470,13626,13625,13471,13472,13627,13471,13627,13626,13472,13473,13628,13472,13628,13627,13473,13474,13629,13473,13629,13628,13474,13475,13630,13474,13630,13629,13475,13476,13631,13475,13631,13630,13476,13477,13632,13476,13632,13631,13477,13478,13633,13477,13633,13632,13478,13479,13634,13478,13634,13633,13479,13480,13635,13479,13635,13634,13480,13481,13636,13480,13636,13635,13481,13482,13637,13481,13637,13636,13482,13483,13638,13482,13638,13637,13483,13484,13639,13483,13639,13638,13485,13486,13641,13485,13641,13640,13486,13487,13642,13486,13642,13641,13487,13488,13643,13487,13643,13642,13488,13489,13644,13488,13644,13643,13489,13490,13645,13489,13645,13644,13490,13491,13646,13490,13646,13645,13491,13492,13647,13491,13647,13646,13492,13493,13648,13492,13648,13647,13493,13494,13649,13493,13649,13648,13494,13495,13650,13494,13650,13649,13495,13496,13651,13495,13651,13650,13496,13497,13652,13496,13652,13651,13497,13498,13653,13497,13653,13652,13498,13499,13654,13498,13654,13653,13499,13500,13655,13499,13655,13654,13500,13501,13656,13500,13656,13655,13501,13502,13657,13501,13657,13656,13502,13503,13658,13502,13658,13657,13503,13504,13659,13503,13659,13658,13504,13505,13660,13504,13660,13659,13505,13506,13661,13505,13661,13660,13506,13507,13662,13506,13662,13661,13507,13508,13663,13507,13663,13662,13508,13509,13664,13508,13664,13663,13509,13510,13665,13509,13665,13664,13510,13511,13666,13510,13666,13665,13511,13512,13667,13511,13667,13666,13512,13513,13668,13512,13668,13667,13513,13514,13669,13513,13669,13668,13514,13515,13670,13514,13670,13669,13515,13516,13671,13515,13671,13670,13516,13517,13672,13516,13672,13671,13517,13518,13673,13517,13673,13672,13518,13519,13674,13518,13674,13673,13519,13520,13675,13519,13675,13674,13520,13521,13676,13520,13676,13675,13521,13522,13677,13521,13677,13676,13522,13523,13678,13522,13678,13677,13523,13524,13679,13523,13679,13678,13524,13525,13680,13524,13680,13679,13525,13526,13681,13525,13681,13680,13526,13527,13682,13526,13682,13681,13527,13528,13683,13527,13683,13682,13528,13529,13684,13528,13684,13683,13529,13530,13685,13529,13685,13684,13530,13531,13686,13530,13686,13685,13531,13532,13687,13531,13687,13686,13532,13533,13688,13532,13688,13687,13533,13534,13689,13533,13689,13688,13534,13535,13690,13534,13690,13689,13535,13536,13691,13535,13691,13690,13536,13537,13692,13536,13692,13691,13537,13538,13693,13537,13693,13692,13538,13539,13694,13538,13694,13693,13539,13540,13695,13539,13695,13694,13540,13541,13696,13540,13696,13695,13541,13542,13697,13541,13697,13696,13542,13543,13698,13542,13698,13697,13543,13544,13699,13543,13699,13698,13544,13545,13700,13544,13700,13699,13545,13546,13701,13545,13701,13700,13546,13547,13702,13546,13702,13701,13547,13548,13703,13547,13703,13702,13548,13549,13704,13548,13704,13703,13549,13550,13705,13549,13705,13704,13550,13551,13706,13550,13706,13705,13551,13552,13707,13551,13707,13706,13552,13553,13708,13552,13708,13707,13553,13554,13709,13553,13709,13708,13554,13555,13710,13554,13710,13709,13555,13556,13711,13555,13711,13710,13556,13557,13712,13556,13712,13711,13557,13558,13713,13557,13713,13712,13558,13559,13714,13558,13714,13713,13559,13560,13715,13559,13715,13714,13560,13561,13716,13560,13716,13715,13561,13562,13717,13561,13717,13716,13562,13563,13718,13562,13718,13717,13563,13564,13719,13563,13719,13718,13564,13565,13720,13564,13720,13719,13565,13566,13721,13565,13721,13720,13566,13567,13722,13566,13722,13721,13567,13568,13723,13567,13723,13722,13568,13569,13724,13568,13724,13723,13569,13570,13725,13569,13725,13724,13570,13571,13726,13570,13726,13725,13571,13572,13727,13571,13727,13726,13572,13573,13728,13572,13728,13727,13573,13574,13729,13573,13729,13728,13574,13575,13730,13574,13730,13729,13575,13576,13731,13575,13731,13730,13576,13577,13732,13576,13732,13731,13577,13578,13733,13577,13733,13732,13578,13579,13734,13578,13734,13733,13579,13580,13735,13579,13735,13734,13580,13581,13736,13580,13736,13735,13581,13582,13737,13581,13737,13736,13582,13583,13738,13582,13738,13737,13583,13584,13739,13583,13739,13738,13584,13585,13740,13584,13740,13739,13585,13586,13741,13585,13741,13740,13586,13587,13742,13586,13742,13741,13587,13588,13743,13587,13743,13742,13588,13589,13744,13588,13744,13743,13589,13590,13745,13589,13745,13744,13590,13591,13746,13590,13746,13745,13591,13592,13747,13591,13747,13746,13592,13593,13748,13592,13748,13747,13593,13594,13749,13593,13749,13748,13594,13595,13750,13594,13750,13749,13595,13596,13751,13595,13751,13750,13596,13597,13752,13596,13752,13751,13597,13598,13753,13597,13753,13752,13598,13599,13754,13598,13754,13753,13599,13600,13755,13599,13755,13754,13600,13601,13756,13600,13756,13755,13601,13602,13757,13601,13757,13756,13602,13603,13758,13602,13758,13757,13603,13604,13759,13603,13759,13758,13604,13605,13760,13604,13760,13759,13605,13606,13761,13605,13761,13760,13606,13607,13762,13606,13762,13761,13607,13608,13763,13607,13763,13762,13608,13609,13764,13608,13764,13763,13609,13610,13765,13609,13765,13764,13610,13611,13766,13610,13766,13765,13611,13612,13767,13611,13767,13766,13612,13613,13768,13612,13768,13767,13613,13614,13769,13613,13769,13768,13614,13615,13770,13614,13770,13769,13615,13616,13771,13615,13771,13770,13616,13617,13772,13616,13772,13771,13617,13618,13773,13617,13773,13772,13618,13619,13774,13618,13774,13773,13619,13620,13775,13619,13775,13774,13620,13621,13776,13620,13776,13775,13621,13622,13777,13621,13777,13776,13622,13623,13778,13622,13778,13777,13623,13624,13779,13623,13779,13778,13624,13625,13780,13624,13780,13779,13625,13626,13781,13625,13781,13780,13626,13627,13782,13626,13782,13781,13627,13628,13783,13627,13783,13782,13628,13629,13784,13628,13784,13783,13629,13630,13785,13629,13785,13784,13630,13631,13786,13630,13786,13785,13631,13632,13787,13631,13787,13786,13632,13633,13788,13632,13788,13787,13633,13634,13789,13633,13789,13788,13634,13635,13790,13634,13790,13789,13635,13636,13791,13635,13791,13790,13636,13637,13792,13636,13792,13791,13637,13638,13793,13637,13793,13792,13638,13639,13794,13638,13794,13793,13640,13641,13796,13640,13796,13795,13641,13642,13797,13641,13797,13796,13642,13643,13798,13642,13798,13797,13643,13644,13799,13643,13799,13798,13644,13645,13800,13644,13800,13799,13645,13646,13801,13645,13801,13800,13646,13647,13802,13646,13802,13801,13647,13648,13803,13647,13803,13802,13648,13649,13804,13648,13804,13803,13649,13650,13805,13649,13805,13804,13650,13651,13806,13650,13806,13805,13651,13652,13807,13651,13807,13806,13652,13653,13808,13652,13808,13807,13653,13654,13809,13653,13809,13808,13654,13655,13810,13654,13810,13809,13655,13656,13811,13655,13811,13810,13656,13657,13812,13656,13812,13811,13657,13658,13813,13657,13813,13812,13658,13659,13814,13658,13814,13813,13659,13660,13815,13659,13815,13814,13660,13661,13816,13660,13816,13815,13661,13662,13817,13661,13817,13816,13662,13663,13818,13662,13818,13817,13663,13664,13819,13663,13819,13818,13664,13665,13820,13664,13820,13819,13665,13666,13821,13665,13821,13820,13666,13667,13822,13666,13822,13821,13667,13668,13823,13667,13823,13822,13668,13669,13824,13668,13824,13823,13669,13670,13825,13669,13825,13824,13670,13671,13826,13670,13826,13825,13671,13672,13827,13671,13827,13826,13672,13673,13828,13672,13828,13827,13673,13674,13829,13673,13829,13828,13674,13675,13830,13674,13830,13829,13675,13676,13831,13675,13831,13830,13676,13677,13832,13676,13832,13831,13677,13678,13833,13677,13833,13832,13678,13679,13834,13678,13834,13833,13679,13680,13835,13679,13835,13834,13680,13681,13836,13680,13836,13835,13681,13682,13837,13681,13837,13836,13682,13683,13838,13682,13838,13837,13683,13684,13839,13683,13839,13838,13684,13685,13840,13684,13840,13839,13685,13686,13841,13685,13841,13840,13686,13687,13842,13686,13842,13841,13687,13688,13843,13687,13843,13842,13688,13689,13844,13688,13844,13843,13689,13690,13845,13689,13845,13844,13690,13691,13846,13690,13846,13845,13691,13692,13847,13691,13847,13846,13692,13693,13848,13692,13848,13847,13693,13694,13849,13693,13849,13848,13694,13695,13850,13694,13850,13849,13695,13696,13851,13695,13851,13850,13696,13697,13852,13696,13852,13851,13697,13698,13853,13697,13853,13852,13698,13699,13854,13698,13854,13853,13699,13700,13855,13699,13855,13854,13700,13701,13856,13700,13856,13855,13701,13702,13857,13701,13857,13856,13702,13703,13858,13702,13858,13857,13703,13704,13859,13703,13859,13858,13704,13705,13860,13704,13860,13859,13705,13706,13861,13705,13861,13860,13706,13707,13862,13706,13862,13861,13707,13708,13863,13707,13863,13862,13708,13709,13864,13708,13864,13863,13709,13710,13865,13709,13865,13864,13710,13711,13866,13710,13866,13865,13711,13712,13867,13711,13867,13866,13712,13713,13868,13712,13868,13867,13713,13714,13869,13713,13869,13868,13714,13715,13870,13714,13870,13869,13715,13716,13871,13715,13871,13870,13716,13717,13872,13716,13872,13871,13717,13718,13873,13717,13873,13872,13718,13719,13874,13718,13874,13873,13719,13720,13875,13719,13875,13874,13720,13721,13876,13720,13876,13875,13721,13722,13877,13721,13877,13876,13722,13723,13878,13722,13878,13877,13723,13724,13879,13723,13879,13878,13724,13725,13880,13724,13880,13879,13725,13726,13881,13725,13881,13880,13726,13727,13882,13726,13882,13881,13727,13728,13883,13727,13883,13882,13728,13729,13884,13728,13884,13883,13729,13730,13885,13729,13885,13884,13730,13731,13886,13730,13886,13885,13731,13732,13887,13731,13887,13886,13732,13733,13888,13732,13888,13887,13733,13734,13889,13733,13889,13888,13734,13735,13890,13734,13890,13889,13735,13736,13891,13735,13891,13890,13736,13737,13892,13736,13892,13891,13737,13738,13893,13737,13893,13892,13738,13739,13894,13738,13894,13893,13739,13740,13895,13739,13895,13894,13740,13741,13896,13740,13896,13895,13741,13742,13897,13741,13897,13896,13742,13743,13898,13742,13898,13897,13743,13744,13899,13743,13899,13898,13744,13745,13900,13744,13900,13899,13745,13746,13901,13745,13901,13900,13746,13747,13902,13746,13902,13901,13747,13748,13903,13747,13903,13902,13748,13749,13904,13748,13904,13903,13749,13750,13905,13749,13905,13904,13750,13751,13906,13750,13906,13905,13751,13752,13907,13751,13907,13906,13752,13753,13908,13752,13908,13907,13753,13754,13909,13753,13909,13908,13754,13755,13910,13754,13910,13909,13755,13756,13911,13755,13911,13910,13756,13757,13912,13756,13912,13911,13757,13758,13913,13757,13913,13912,13758,13759,13914,13758,13914,13913,13759,13760,13915,13759,13915,13914,13760,13761,13916,13760,13916,13915,13761,13762,13917,13761,13917,13916,13762,13763,13918,13762,13918,13917,13763,13764,13919,13763,13919,13918,13764,13765,13920,13764,13920,13919,13765,13766,13921,13765,13921,13920,13766,13767,13922,13766,13922,13921,13767,13768,13923,13767,13923,13922,13768,13769,13924,13768,13924,13923,13769,13770,13925,13769,13925,13924,13770,13771,13926,13770,13926,13925,13771,13772,13927,13771,13927,13926,13772,13773,13928,13772,13928,13927,13773,13774,13929,13773,13929,13928,13774,13775,13930,13774,13930,13929,13775,13776,13931,13775,13931,13930,13776,13777,13932,13776,13932,13931,13777,13778,13933,13777,13933,13932,13778,13779,13934,13778,13934,13933,13779,13780,13935,13779,13935,13934,13780,13781,13936,13780,13936,13935,13781,13782,13937,13781,13937,13936,13782,13783,13938,13782,13938,13937,13783,13784,13939,13783,13939,13938,13784,13785,13940,13784,13940,13939,13785,13786,13941,13785,13941,13940,13786,13787,13942,13786,13942,13941,13787,13788,13943,13787,13943,13942,13788,13789,13944,13788,13944,13943,13789,13790,13945,13789,13945,13944,13790,13791,13946,13790,13946,13945,13791,13792,13947,13791,13947,13946,13792,13793,13948,13792,13948,13947,13793,13794,13949,13793,13949,13948],"normals":[1.0,0.0,0.0,0.999167799949646,0.0,-0.04078858718276024,0.9966725707054138,0.0,-0.08150928467512131,0.9925184845924377,0.0,-0.12209431827068329,0.9867124557495117,0.0,-0.16247613728046417,0.9792641401290894,0.0,-0.2025875300168991,0.9701859354972839,0.0,-0.24236173927783966,0.9594929814338684,0.0,-0.28173255920410156,0.9472029805183411,0.0,-0.3206344544887543,0.9333364963531494,0.0,-0.3590027093887329,0.9179165959358215,0.0,-0.39677342772483826,0.9009688496589661,0.0,-0.4338837265968323,0.8825215697288513,0.0,-0.47027191519737244,0.8626053929328918,0.0,-0.5058773756027222,0.8412535190582275,0.0,-0.5406408309936523,0.8185014724731445,0.0,-0.5745044350624084,0.7943871021270752,0.0,-0.6074118614196777,0.7689505219459534,0.0,-0.6393082737922668,0.7422341108322144,0.0,-0.6701406836509705,0.7142823338508606,0.0,-0.6998576521873474,0.6851417422294617,0.0,-0.7284097671508789,0.6548607349395752,0.0,-0.7557495832443237,0.6234897971153259,0.0,-0.7818315029144287,0.5910811424255371,0.0,-0.8066121339797974,0.5576886534690857,0.0,-0.8300502300262451,0.5233680009841919,0.0,-0.8521067500114441,0.4881761968135834,0.0,-0.8727450966835022,0.4521719217300415,0.0,-0.8919308185577393,0.4154150187969208,0.0,-0.9096319675445557,0.3779667019844055,0.0,-0.9258192181587219,0.3398892879486084,0.0,-0.9404654502868652,0.30124619603157043,0.0,-0.953546404838562,0.2621016800403595,0.0,-0.9650402665138245,0.22252093255519867,0.0,-0.9749279022216797,0.18256981670856476,0.0,-0.9831928610801697,0.1423148363828659,0.0,-0.9898214340209961,0.10182298719882965,0.0,-0.9948025345802307,0.06116165965795517,0.0,-0.9981278777122498,0.020398536697030067,0.0,-0.9997919201850891,-0.020398536697030067,0.0,-0.9997919201850891,-0.06116165965795517,0.0,-0.9981278777122498,-0.10182298719882965,0.0,-0.9948025345802307,-0.1423148363828659,0.0,-0.9898214340209961,-0.18256981670856476,0.0,-0.9831928610801697,-0.22252093255519867,0.0,-0.9749279022216797,-0.2621016800403595,0.0,-0.9650402665138245,-0.30124619603157043,0.0,-0.953546404838562,-0.3398892879486084,0.0,-0.9404654502868652,-0.3779667019844055,0.0,-0.9258192181587219,-0.4154150187969208,0.0,-0.9096319675445557,-0.4521719217300415,0.0,-0.8919308185577393,-0.4881761968135834,0.0,-0.8727450966835022,-0.5233680009841919,0.0,-0.8521067500114441,-0.5576886534690857,0.0,-0.8300502300262451,-0.5910811424255371,0.0,-0.8066121339797974,-0.6234897971153259,0.0,-0.7818315029144287,-0.6548607349395752,0.0,-0.7557495832443237,-0.6851417422294617,0.0,-0.7284097671508789,-0.7142823338508606,0.0,-0.6998576521873474,-0.7422341108322144,0.0,-0.6701406836509705,-0.7689505219459534,0.0,-0.6393082737922668,-0.7943871021270752,0.0,-0.6074118614196777,-0.8185014724731445,0.0,-0.5745044350624084,-0.8412535190582275,0.0,-0.5406408309936523,-0.8626053929328918,0.0,-0.5058773756027222,-0.8825215697288513,0.0,-0.47027191519737244,-0.9009688496589661,0.0,-0.4338837265968323,-0.9179165959358215,0.0,-0.39677342772483826,-0.9333364963531494,0.0,-0.3590027093887329,-0.9472029805183411,0.0,-0.3206344544887543,-0.9594929814338684,0.0,-0.28173255920410156,-0.9701859354972839,0.0,-0.24236173927783966,-0.9792641401290894,0.0,-0.2025875300168991,-0.9867124557495117,0.0,-0.16247613728046417,-0.9925184845924377,0.0,-0.12209431827068329,-0.9966725707054138,0.0,-0.08150928467512131,-0.999167799949646,0.0,-0.04078858718276024,-1.0,0.0,-1.2246468525851679e-16,-0.999167799949646,0.0,0.04078858718276024,-0.9966725707054138,0.0,0.08150928467512131,-0.9925184845924377,0.0,0.12209431827068329,-0.9867124557495117,0.0,0.16247613728046417,-0.9792641401290894,0.0,0.2025875300168991,-0.9701859354972839,0.0,0.24236173927783966,-0.9594929814338684,0.0,0.28173255920410156,-0.9472029805183411,0.0,0.3206344544887543,-0.9333364963531494,0.0,0.3590027093887329,-0.9179165959358215,0.0,0.39677342772483826,-0.9009688496589661,0.0,0.4338837265968323,-0.8825215697288513,0.0,0.47027191519737244,-0.8626053929328918,0.0,0.5058773756027222,-0.8412535190582275,0.0,0.5406408309936523,-0.8185014724731445,0.0,0.5745044350624084,-0.7943871021270752,0.0,0.6074118614196777,-0.7689505219459534,0.0,0.6393082737922668,-0.7422341108322144,0.0,0.6701406836509705,-0.7142823338508606,0.0,0.6998576521873474,-0.6851417422294617,0.0,0.7284097671508789,-0.6548607349395752,0.0,0.7557495832443237,-0.6234897971153259,0.0,0.7818315029144287,-0.5910811424255371,0.0,0.8066121339797974,-0.5576886534690857,0.0,0.8300502300262451,-0.5233680009841919,0.0,0.8521067500114441,-0.4881761968135834,0.0,0.8727450966835022,-0.4521719217300415,0.0,0.8919308185577393,-0.4154150187969208,0.0,0.9096319675445557,-0.3779667019844055,0.0,0.9258192181587219,-0.3398892879486084,0.0,0.9404654502868652,-0.30124619603157043,0.0,0.953546404838562,-0.2621016800403595,0.0,0.9650402665138245,-0.22252093255519867,0.0,0.9749279022216797,-0.18256981670856476,0.0,0.9831928610801697,-0.1423148363828659,0.0,0.9898214340209961,-0.10182298719882965,0.0,0.9948025345802307,-0.06116165965795517,0.0,0.9981278777122498,-0.020398536697030067,0.0,0.9997919201850891,0.020398536697030067,0.0,0.9997919201850891,0.06116165965795517,0.0,0.9981278777122498,0.10182298719882965,0.0,0.9948025345802307,0.1423148363828659,0.0,0.9898214340209961,0.18256981670856476,0.0,0.9831928610801697,0.22252093255519867,0.0,0.9749279022216797,0.2621016800403595,0.0,0.9650402665138245,0.30124619603157043,0.0,0.953546404838562,0.3398892879486084,0.0,0.9404654502868652,0.3779667019844055,0.0,0.9258192181587219,0.4154150187969208,0.0,0.9096319675445557,0.4521719217300415,0.0,0.8919308185577393,0.4881761968135834,0.0,0.8727450966835022,0.5233680009841919,0.0,0.8521067500114441,0.5576886534690857,0.0,0.8300502300262451,0.5910811424255371,0.0,0.8066121339797974,0.6234897971153259,0.0,0.7818315029144287,0.6548607349395752,0.0,0.7557495832443237,0.6851417422294617,0.0,0.7284097671508789,0.7142823338508606,0.0,0.6998576521873474,0.7422341108322144,0.0,0.6701406836509705,0.7689505219459534,0.0,0.6393082737922668,0.7943871021270752,0.0,0.6074118614196777,0.8185014724731445,0.0,0.5745044350624084,0.8412535190582275,0.0,0.5406408309936523,0.8626053929328918,0.0,0.5058773756027222,0.8825215697288513,0.0,0.47027191519737244,0.9009688496589661,0.0,0.4338837265968323,0.9179165959358215,0.0,0.39677342772483826,0.9333364963531494,0.0,0.3590027093887329,0.9472029805183411,0.0,0.3206344544887543,0.9594929814338684,0.0,0.28173255920410156,0.9701859354972839,0.0,0.24236173927783966,0.9792641401290894,0.0,0.2025875300168991,0.9867124557495117,0.0,0.16247613728046417,0.9925184845924377,0.0,0.12209431827068329,0.9966725707054138,0.0,0.08150928467512131,0.999167799949646,0.0,0.04078858718276024,1.0,0.0,2.4492937051703357e-16,0.9975090026855469,0.0705389603972435,0.0,0.99667888879776,0.0705389603972435,-0.04068698361515999,0.9941899180412292,0.0705389603972435,-0.08130624890327454,0.9900461435317993,0.0705389603972435,-0.12179018557071686,0.9842545986175537,0.0705389603972435,-0.16207140684127808,0.9768248200416565,0.0705389603972435,-0.20208288729190826,0.9677692651748657,0.0705389603972435,-0.24175801873207092,0.95710289478302,0.0705389603972435,-0.2810307741165161,0.9448435306549072,0.0705389603972435,-0.3198357820510864,0.9310116171836853,0.0705389603972435,-0.35810843110084534,0.9156301021575928,0.0705389603972435,-0.39578506350517273,0.8987245559692383,0.0705389603972435,-0.4328029453754425,0.8803232312202454,0.0705389603972435,-0.4691004753112793,0.8604567050933838,0.0705389603972435,-0.5046172142028809,0.8391579985618591,0.0705389603972435,-0.5392941236495972,0.8164625763893127,0.0705389603972435,-0.5730733871459961,0.792408287525177,0.0705389603972435,-0.6058987975120544,0.7670350670814514,0.0705389603972435,-0.6377158164978027,0.7403852343559265,0.0705389603972435,-0.6684713363647461,0.7125030755996704,0.0705389603972435,-0.6981143355369568,0.6834350228309631,0.0705389603972435,-0.7265953421592712,0.6532294750213623,0.0705389603972435,-0.7538670301437378,0.6219366788864136,0.0705389603972435,-0.7798839807510376,0.5896087884902954,0.0705389603972435,-0.804602861404419,0.5562995076179504,0.0705389603972435,-0.8279826045036316,0.5220642685890198,0.0705389603972435,-0.8499841690063477,0.48696017265319824,0.0705389603972435,-0.8705710768699646,0.45104557275772095,0.0705389603972435,-0.8897090554237366,0.4143802225589752,0.0705389603972435,-0.9073660969734192,0.37702518701553345,0.0705389603972435,-0.9235129952430725,0.33904263377189636,0.0705389603972435,-0.9381227493286133,0.30049580335617065,0.0705389603972435,-0.9511711597442627,0.26144880056381226,0.0705389603972435,-0.9626363515853882,0.22196663916110992,0.0705389603972435,-0.9724993705749512,0.18211504817008972,0.0705389603972435,-0.9807437658309937,0.14196033775806427,0.0705389603972435,-0.9873558282852173,0.10156934708356857,0.0705389603972435,-0.9923245310783386,0.06100931018590927,0.0705389603972435,-0.9956415891647339,0.020347725600004196,0.0705389603972435,-0.997301459312439,-0.020347725600004196,0.0705389603972435,-0.997301459312439,-0.06100931018590927,0.0705389603972435,-0.9956415891647339,-0.10156934708356857,0.0705389603972435,-0.9923245310783386,-0.14196033775806427,0.0705389603972435,-0.9873558282852173,-0.18211504817008972,0.0705389603972435,-0.9807437658309937,-0.22196663916110992,0.0705389603972435,-0.9724993705749512,-0.26144880056381226,0.0705389603972435,-0.9626363515853882,-0.30049580335617065,0.0705389603972435,-0.9511711597442627,-0.33904263377189636,0.0705389603972435,-0.9381227493286133,-0.37702518701553345,0.0705389603972435,-0.9235129952430725,-0.4143802225589752,0.0705389603972435,-0.9073660969734192,-0.45104557275772095,0.0705389603972435,-0.8897090554237366,-0.48696017265319824,0.0705389603972435,-0.8705710768699646,-0.5220642685890198,0.0705389603972435,-0.8499841690063477,-0.5562995076179504,0.0705389603972435,-0.8279826045036316,-0.5896087884902954,0.0705389603972435,-0.804602861404419,-0.6219366788864136,0.0705389603972435,-0.7798839807510376,-0.6532294750213623,0.0705389603972435,-0.7538670301437378,-0.6834350228309631,0.0705389603972435,-0.7265953421592712,-0.7125030755996704,0.0705389603972435,-0.6981143355369568,-0.7403852343559265,0.0705389603972435,-0.6684713363647461,-0.7670350670814514,0.0705389603972435,-0.6377158164978027,-0.792408287525177,0.0705389603972435,-0.6058987975120544,-0.8164625763893127,0.0705389603972435,-0.5730733871459961,-0.8391579985618591,0.0705389603972435,-0.5392941236495972,-0.8604567050933838,0.0705389603972435,-0.5046172142028809,-0.8803232312202454,0.0705389603972435,-0.4691004753112793,-0.8987245559692383,0.0705389603972435,-0.4328029453754425,-0.9156301021575928,0.0705389603972435,-0.39578506350517273,-0.9310116171836853,0.0705389603972435,-0.35810843110084534,-0.9448435306549072,0.0705389603972435,-0.3198357820510864,-0.95710289478302,0.0705389603972435,-0.2810307741165161,-0.9677692651748657,0.0705389603972435,-0.24175801873207092,-0.9768248200416565,0.0705389603972435,-0.20208288729190826,-0.9842545986175537,0.0705389603972435,-0.16207140684127808,-0.9900461435317993,0.0705389603972435,-0.12179018557071686,-0.9941899180412292,0.0705389603972435,-0.08130624890327454,-0.99667888879776,0.0705389603972435,-0.04068698361515999,-0.9975090026855469,0.0705389603972435,-1.2215962104860723e-16,-0.99667888879776,0.0705389603972435,0.04068698361515999,-0.9941899180412292,0.0705389603972435,0.08130624890327454,-0.9900461435317993,0.0705389603972435,0.12179018557071686,-0.9842545986175537,0.0705389603972435,0.16207140684127808,-0.9768248200416565,0.0705389603972435,0.20208288729190826,-0.9677692651748657,0.0705389603972435,0.24175801873207092,-0.95710289478302,0.0705389603972435,0.2810307741165161,-0.9448435306549072,0.0705389603972435,0.3198357820510864,-0.9310116171836853,0.0705389603972435,0.35810843110084534,-0.9156301021575928,0.0705389603972435,0.39578506350517273,-0.8987245559692383,0.0705389603972435,0.4328029453754425,-0.8803232312202454,0.0705389603972435,0.4691004753112793,-0.8604567050933838,0.0705389603972435,0.5046172142028809,-0.8391579985618591,0.0705389603972435,0.5392941236495972,-0.8164625763893127,0.0705389603972435,0.5730733871459961,-0.792408287525177,0.0705389603972435,0.6058987975120544,-0.7670350670814514,0.0705389603972435,0.6377158164978027,-0.7403852343559265,0.0705389603972435,0.6684713363647461,-0.7125030755996704,0.0705389603972435,0.6981143355369568,-0.6834350228309631,0.0705389603972435,0.7265953421592712,-0.6532294750213623,0.0705389603972435,0.7538670301437378,-0.6219366788864136,0.0705389603972435,0.7798839807510376,-0.5896087884902954,0.0705389603972435,0.804602861404419,-0.5562995076179504,0.0705389603972435,0.8279826045036316,-0.5220642685890198,0.0705389603972435,0.8499841690063477,-0.48696017265319824,0.0705389603972435,0.8705710768699646,-0.45104557275772095,0.0705389603972435,0.8897090554237366,-0.4143802225589752,0.0705389603972435,0.9073660969734192,-0.37702518701553345,0.0705389603972435,0.9235129952430725,-0.33904263377189636,0.0705389603972435,0.9381227493286133,-0.30049580335617065,0.0705389603972435,0.9511711597442627,-0.26144880056381226,0.0705389603972435,0.9626363515853882,-0.22196663916110992,0.0705389603972435,0.9724993705749512,-0.18211504817008972,0.0705389603972435,0.9807437658309937,-0.14196033775806427,0.0705389603972435,0.9873558282852173,-0.10156934708356857,0.0705389603972435,0.9923245310783386,-0.06100931018590927,0.0705389603972435,0.9956415891647339,-0.020347725600004196,0.0705389603972435,0.997301459312439,0.020347725600004196,0.0705389603972435,0.997301459312439,0.06100931018590927,0.0705389603972435,0.9956415891647339,0.10156934708356857,0.0705389603972435,0.9923245310783386,0.14196033775806427,0.0705389603972435,0.9873558282852173,0.18211504817008972,0.0705389603972435,0.9807437658309937,0.22196663916110992,0.0705389603972435,0.9724993705749512,0.26144880056381226,0.0705389603972435,0.9626363515853882,0.30049580335617065,0.0705389603972435,0.9511711597442627,0.33904263377189636,0.0705389603972435,0.9381227493286133,0.37702518701553345,0.0705389603972435,0.9235129952430725,0.4143802225589752,0.0705389603972435,0.9073660969734192,0.45104557275772095,0.0705389603972435,0.8897090554237366,0.48696017265319824,0.0705389603972435,0.8705710768699646,0.5220642685890198,0.0705389603972435,0.8499841690063477,0.5562995076179504,0.0705389603972435,0.8279826045036316,0.5896087884902954,0.0705389603972435,0.804602861404419,0.6219366788864136,0.0705389603972435,0.7798839807510376,0.6532294750213623,0.0705389603972435,0.7538670301437378,0.6834350228309631,0.0705389603972435,0.7265953421592712,0.7125030755996704,0.0705389603972435,0.6981143355369568,0.7403852343559265,0.0705389603972435,0.6684713363647461,0.7670350670814514,0.0705389603972435,0.6377158164978027,0.792408287525177,0.0705389603972435,0.6058987975120544,0.8164625763893127,0.0705389603972435,0.5730733871459961,0.8391579985618591,0.0705389603972435,0.5392941236495972,0.8604567050933838,0.0705389603972435,0.5046172142028809,0.8803232312202454,0.0705389603972435,0.4691004753112793,0.8987245559692383,0.0705389603972435,0.4328029453754425,0.9156301021575928,0.0705389603972435,0.39578506350517273,0.9310116171836853,0.0705389603972435,0.35810843110084534,0.9448435306549072,0.0705389603972435,0.3198357820510864,0.95710289478302,0.0705389603972435,0.2810307741165161,0.9677692651748657,0.0705389603972435,0.24175801873207092,0.9768248200416565,0.0705389603972435,0.20208288729190826,0.9842545986175537,0.0705389603972435,0.16207140684127808,0.9900461435317993,0.0705389603972435,0.12179018557071686,0.9941899180412292,0.0705389603972435,0.08130624890327454,0.99667888879776,0.0705389603972435,0.04068698361515999,0.9975090026855469,0.0705389603972435,2.4431924209721446e-16,0.9900485277175903,0.1407264918088913,0.0,0.9892246127128601,0.1407264918088913,-0.04038267955183983,0.9867541790008545,0.1407264918088913,-0.08069814741611481,0.9826414585113525,0.1407264918088913,-0.12087929993867874,0.9768931865692139,0.1407264918088913,-0.16085925698280334,0.9695190191268921,0.1407264918088913,-0.2005714774131775,0.9605311751365662,0.1407264918088913,-0.23994988203048706,0.9499446153640747,0.1407264918088913,-0.2789289057254791,0.9377769231796265,0.1407264918088913,-0.3174436688423157,0.9240484237670898,0.1407264918088913,-0.35543009638786316,0.9087819457054138,0.1407264918088913,-0.3928249180316925,0.8920028805732727,0.1407264918088913,-0.4295659363269806,0.8737391829490662,0.1407264918088913,-0.46559199690818787,0.8540211915969849,0.1407264918088913,-0.5008431077003479,0.8328818082809448,0.1407264918088913,-0.5352606177330017,0.8103561401367188,0.1407264918088913,-0.5687872767448425,0.7864817380905151,0.1407264918088913,-0.601367175579071,0.7612982988357544,0.1407264918088913,-0.6329461932182312,0.7348477840423584,0.1407264918088913,-0.6634717583656311,0.7071741819381714,0.1407264918088913,-0.6928930282592773,0.67832350730896,0.1407264918088913,-0.7211610078811646,0.6483439207077026,0.1407264918088913,-0.7482287287712097,0.6172851324081421,0.1407264918088913,-0.7740510702133179,0.5851989984512329,0.1407264918088913,-0.7985851168632507,0.5521388053894043,0.1407264918088913,-0.8217899799346924,0.5181596875190735,0.1407264918088913,-0.8436270356178284,0.48331812024116516,0.1407264918088913,-0.8640599846839905,0.44767212867736816,0.1407264918088913,-0.883054792881012,0.411281019449234,0.1407264918088913,-0.9005798101425171,0.3742053806781769,0.1407264918088913,-0.9166058897972107,0.3365069031715393,0.1407264918088913,-0.9311063885688782,0.2982483506202698,0.1407264918088913,-0.9440571665763855,0.2594933807849884,0.1407264918088913,-0.955436646938324,0.22030651569366455,0.1407264918088913,-0.9652259349822998,0.1807529777288437,0.1407264918088913,-0.9734086394309998,0.14089860022068024,0.1407264918088913,-0.9799712300300598,0.10080969333648682,0.1407264918088913,-0.9849027395248413,0.06055301055312157,0.1407264918088913,-0.98819500207901,0.02019554190337658,0.1407264918088913,-0.9898425340652466,-0.02019554190337658,0.1407264918088913,-0.9898425340652466,-0.06055301055312157,0.1407264918088913,-0.98819500207901,-0.10080969333648682,0.1407264918088913,-0.9849027395248413,-0.14089860022068024,0.1407264918088913,-0.9799712300300598,-0.1807529777288437,0.1407264918088913,-0.9734086394309998,-0.22030651569366455,0.1407264918088913,-0.9652259349822998,-0.2594933807849884,0.1407264918088913,-0.955436646938324,-0.2982483506202698,0.1407264918088913,-0.9440571665763855,-0.3365069031715393,0.1407264918088913,-0.9311063885688782,-0.3742053806781769,0.1407264918088913,-0.9166058897972107,-0.411281019449234,0.1407264918088913,-0.9005798101425171,-0.44767212867736816,0.1407264918088913,-0.883054792881012,-0.48331812024116516,0.1407264918088913,-0.8640599846839905,-0.5181596875190735,0.1407264918088913,-0.8436270356178284,-0.5521388053894043,0.1407264918088913,-0.8217899799346924,-0.5851989984512329,0.1407264918088913,-0.7985851168632507,-0.6172851324081421,0.1407264918088913,-0.7740510702133179,-0.6483439207077026,0.1407264918088913,-0.7482287287712097,-0.67832350730896,0.1407264918088913,-0.7211610078811646,-0.7071741819381714,0.1407264918088913,-0.6928930282592773,-0.7348477840423584,0.1407264918088913,-0.6634717583656311,-0.7612982988357544,0.1407264918088913,-0.6329461932182312,-0.7864817380905151,0.1407264918088913,-0.601367175579071,-0.8103561401367188,0.1407264918088913,-0.5687872767448425,-0.8328818082809448,0.1407264918088913,-0.5352606177330017,-0.8540211915969849,0.1407264918088913,-0.5008431077003479,-0.8737391829490662,0.1407264918088913,-0.46559199690818787,-0.8920028805732727,0.1407264918088913,-0.4295659363269806,-0.9087819457054138,0.1407264918088913,-0.3928249180316925,-0.9240484237670898,0.1407264918088913,-0.35543009638786316,-0.9377769231796265,0.1407264918088913,-0.3174436688423157,-0.9499446153640747,0.1407264918088913,-0.2789289057254791,-0.9605311751365662,0.1407264918088913,-0.23994988203048706,-0.9695190191268921,0.1407264918088913,-0.2005714774131775,-0.9768931865692139,0.1407264918088913,-0.16085925698280334,-0.9826414585113525,0.1407264918088913,-0.12087929993867874,-0.9867541790008545,0.1407264918088913,-0.08069814741611481,-0.9892246127128601,0.1407264918088913,-0.04038267955183983,-0.9900485277175903,0.1407264918088913,-1.2124597690098526e-16,-0.9892246127128601,0.1407264918088913,0.04038267955183983,-0.9867541790008545,0.1407264918088913,0.08069814741611481,-0.9826414585113525,0.1407264918088913,0.12087929993867874,-0.9768931865692139,0.1407264918088913,0.16085925698280334,-0.9695190191268921,0.1407264918088913,0.2005714774131775,-0.9605311751365662,0.1407264918088913,0.23994988203048706,-0.9499446153640747,0.1407264918088913,0.2789289057254791,-0.9377769231796265,0.1407264918088913,0.3174436688423157,-0.9240484237670898,0.1407264918088913,0.35543009638786316,-0.9087819457054138,0.1407264918088913,0.3928249180316925,-0.8920028805732727,0.1407264918088913,0.4295659363269806,-0.8737391829490662,0.1407264918088913,0.46559199690818787,-0.8540211915969849,0.1407264918088913,0.5008431077003479,-0.8328818082809448,0.1407264918088913,0.5352606177330017,-0.8103561401367188,0.1407264918088913,0.5687872767448425,-0.7864817380905151,0.1407264918088913,0.601367175579071,-0.7612982988357544,0.1407264918088913,0.6329461932182312,-0.7348477840423584,0.1407264918088913,0.6634717583656311,-0.7071741819381714,0.1407264918088913,0.6928930282592773,-0.67832350730896,0.1407264918088913,0.7211610078811646,-0.6483439207077026,0.1407264918088913,0.7482287287712097,-0.6172851324081421,0.1407264918088913,0.7740510702133179,-0.5851989984512329,0.1407264918088913,0.7985851168632507,-0.5521388053894043,0.1407264918088913,0.8217899799346924,-0.5181596875190735,0.1407264918088913,0.8436270356178284,-0.48331812024116516,0.1407264918088913,0.8640599846839905,-0.44767212867736816,0.1407264918088913,0.883054792881012,-0.411281019449234,0.1407264918088913,0.9005798101425171,-0.3742053806781769,0.1407264918088913,0.9166058897972107,-0.3365069031715393,0.1407264918088913,0.9311063885688782,-0.2982483506202698,0.1407264918088913,0.9440571665763855,-0.2594933807849884,0.1407264918088913,0.955436646938324,-0.22030651569366455,0.1407264918088913,0.9652259349822998,-0.1807529777288437,0.1407264918088913,0.9734086394309998,-0.14089860022068024,0.1407264918088913,0.9799712300300598,-0.10080969333648682,0.1407264918088913,0.9849027395248413,-0.06055301055312157,0.1407264918088913,0.98819500207901,-0.02019554190337658,0.1407264918088913,0.9898425340652466,0.02019554190337658,0.1407264918088913,0.9898425340652466,0.06055301055312157,0.1407264918088913,0.98819500207901,0.10080969333648682,0.1407264918088913,0.9849027395248413,0.14089860022068024,0.1407264918088913,0.9799712300300598,0.1807529777288437,0.1407264918088913,0.9734086394309998,0.22030651569366455,0.1407264918088913,0.9652259349822998,0.2594933807849884,0.1407264918088913,0.955436646938324,0.2982483506202698,0.1407264918088913,0.9440571665763855,0.3365069031715393,0.1407264918088913,0.9311063885688782,0.3742053806781769,0.1407264918088913,0.9166058897972107,0.411281019449234,0.1407264918088913,0.9005798101425171,0.44767212867736816,0.1407264918088913,0.883054792881012,0.48331812024116516,0.1407264918088913,0.8640599846839905,0.5181596875190735,0.1407264918088913,0.8436270356178284,0.5521388053894043,0.1407264918088913,0.8217899799346924,0.5851989984512329,0.1407264918088913,0.7985851168632507,0.6172851324081421,0.1407264918088913,0.7740510702133179,0.6483439207077026,0.1407264918088913,0.7482287287712097,0.67832350730896,0.1407264918088913,0.7211610078811646,0.7071741819381714,0.1407264918088913,0.6928930282592773,0.7348477840423584,0.1407264918088913,0.6634717583656311,0.7612982988357544,0.1407264918088913,0.6329461932182312,0.7864817380905151,0.1407264918088913,0.601367175579071,0.8103561401367188,0.1407264918088913,0.5687872767448425,0.8328818082809448,0.1407264918088913,0.5352606177330017,0.8540211915969849,0.1407264918088913,0.5008431077003479,0.8737391829490662,0.1407264918088913,0.46559199690818787,0.8920028805732727,0.1407264918088913,0.4295659363269806,0.9087819457054138,0.1407264918088913,0.3928249180316925,0.9240484237670898,0.1407264918088913,0.35543009638786316,0.9377769231796265,0.1407264918088913,0.3174436688423157,0.9499446153640747,0.1407264918088913,0.2789289057254791,0.9605311751365662,0.1407264918088913,0.23994988203048706,0.9695190191268921,0.1407264918088913,0.2005714774131775,0.9768931865692139,0.1407264918088913,0.16085925698280334,0.9826414585113525,0.1407264918088913,0.12087929993867874,0.9867541790008545,0.1407264918088913,0.08069814741611481,0.9892246127128601,0.1407264918088913,0.04038267955183983,0.9900485277175903,0.1407264918088913,2.424919538019705e-16,0.9776556491851807,0.21021294593811035,0.0,0.9768420457839966,0.21021294593811035,-0.03987719118595123,0.9744025468826294,0.21021294593811035,-0.07968801259994507,0.9703413248062134,0.21021294593811035,-0.11936619877815247,0.9646649956703186,0.21021294593811035,-0.1588457077741623,0.9573830962181091,0.21021294593811035,-0.19806084036827087,0.9485077261924744,0.21021294593811035,-0.23694631457328796,0.9380537271499634,0.21021294593811035,-0.2754374146461487,0.9260383248329163,0.21021294593811035,-0.31347009539604187,0.9124817252159119,0.21021294593811035,-0.3509809970855713,0.8974063396453857,0.21021294593811035,-0.3879077732563019,0.8808372616767883,0.21021294593811035,-0.42418888211250305,0.8628022074699402,0.21021294593811035,-0.4597639739513397,0.8433310389518738,0.21021294593811035,-0.49457383155822754,0.8224562406539917,0.21021294593811035,-0.5285605192184448,0.800212562084198,0.21021294593811035,-0.5616675019264221,0.7766370177268982,0.21021294593811035,-0.5938395857810974,0.7517688274383545,0.21021294593811035,-0.625023365020752,0.725649356842041,0.21021294593811035,-0.6551668047904968,0.6983221769332886,0.21021294593811035,-0.6842197775840759,0.6698326468467712,0.21021294593811035,-0.7121339440345764,0.640228271484375,0.21021294593811035,-0.7388628125190735,0.6095582842826843,0.21021294593811035,-0.7643619179725647,0.5778737664222717,0.21021294593811035,-0.7885888814926147,0.5452274680137634,0.21021294593811035,-0.8115032315254211,0.511673629283905,0.21021294593811035,-0.833066999912262,0.4772682189941406,0.21021294593811035,-0.8532441258430481,0.4420683979988098,0.21021294593811035,-0.8720011711120605,0.4061328172683716,0.21021294593811035,-0.8893068432807922,0.36952126026153564,0.21021294593811035,-0.9051323533058167,0.33229467272758484,0.21021294593811035,-0.9194513559341431,0.29451504349708557,0.21021294593811035,-0.9322400093078613,0.2562451958656311,0.21021294593811035,-0.9434770345687866,0.21754884719848633,0.21021294593811035,-0.9531437754631042,0.17849041521549225,0.21021294593811035,-0.9612240791320801,0.13913489878177643,0.21021294593811035,-0.9677044749259949,0.09954781830310822,0.21021294593811035,-0.9725742936134338,0.059795040637254715,0.21021294593811035,-0.975825309753418,0.019942745566368103,0.21021294593811035,-0.9774522185325623,-0.019942745566368103,0.21021294593811035,-0.9774522185325623,-0.059795040637254715,0.21021294593811035,-0.975825309753418,-0.09954781830310822,0.21021294593811035,-0.9725742936134338,-0.13913489878177643,0.21021294593811035,-0.9677044749259949,-0.17849041521549225,0.21021294593811035,-0.9612240791320801,-0.21754884719848633,0.21021294593811035,-0.9531437754631042,-0.2562451958656311,0.21021294593811035,-0.9434770345687866,-0.29451504349708557,0.21021294593811035,-0.9322400093078613,-0.33229467272758484,0.21021294593811035,-0.9194513559341431,-0.36952126026153564,0.21021294593811035,-0.9051323533058167,-0.4061328172683716,0.21021294593811035,-0.8893068432807922,-0.4420683979988098,0.21021294593811035,-0.8720011711120605,-0.4772682189941406,0.21021294593811035,-0.8532441258430481,-0.511673629283905,0.21021294593811035,-0.833066999912262,-0.5452274680137634,0.21021294593811035,-0.8115032315254211,-0.5778737664222717,0.21021294593811035,-0.7885888814926147,-0.6095582842826843,0.21021294593811035,-0.7643619179725647,-0.640228271484375,0.21021294593811035,-0.7388628125190735,-0.6698326468467712,0.21021294593811035,-0.7121339440345764,-0.6983221769332886,0.21021294593811035,-0.6842197775840759,-0.725649356842041,0.21021294593811035,-0.6551668047904968,-0.7517688274383545,0.21021294593811035,-0.625023365020752,-0.7766370177268982,0.21021294593811035,-0.5938395857810974,-0.800212562084198,0.21021294593811035,-0.5616675019264221,-0.8224562406539917,0.21021294593811035,-0.5285605192184448,-0.8433310389518738,0.21021294593811035,-0.49457383155822754,-0.8628022074699402,0.21021294593811035,-0.4597639739513397,-0.8808372616767883,0.21021294593811035,-0.42418888211250305,-0.8974063396453857,0.21021294593811035,-0.3879077732563019,-0.9124817252159119,0.21021294593811035,-0.3509809970855713,-0.9260383248329163,0.21021294593811035,-0.31347009539604187,-0.9380537271499634,0.21021294593811035,-0.2754374146461487,-0.9485077261924744,0.21021294593811035,-0.23694631457328796,-0.9573830962181091,0.21021294593811035,-0.19806084036827087,-0.9646649956703186,0.21021294593811035,-0.1588457077741623,-0.9703413248062134,0.21021294593811035,-0.11936619877815247,-0.9744025468826294,0.21021294593811035,-0.07968801259994507,-0.9768420457839966,0.21021294593811035,-0.03987719118595123,-0.9776556491851807,0.21021294593811035,-1.1972827914796276e-16,-0.9768420457839966,0.21021294593811035,0.03987719118595123,-0.9744025468826294,0.21021294593811035,0.07968801259994507,-0.9703413248062134,0.21021294593811035,0.11936619877815247,-0.9646649956703186,0.21021294593811035,0.1588457077741623,-0.9573830962181091,0.21021294593811035,0.19806084036827087,-0.9485077261924744,0.21021294593811035,0.23694631457328796,-0.9380537271499634,0.21021294593811035,0.2754374146461487,-0.9260383248329163,0.21021294593811035,0.31347009539604187,-0.9124817252159119,0.21021294593811035,0.3509809970855713,-0.8974063396453857,0.21021294593811035,0.3879077732563019,-0.8808372616767883,0.21021294593811035,0.42418888211250305,-0.8628022074699402,0.21021294593811035,0.4597639739513397,-0.8433310389518738,0.21021294593811035,0.49457383155822754,-0.8224562406539917,0.21021294593811035,0.5285605192184448,-0.800212562084198,0.21021294593811035,0.5616675019264221,-0.7766370177268982,0.21021294593811035,0.5938395857810974,-0.7517688274383545,0.21021294593811035,0.625023365020752,-0.725649356842041,0.21021294593811035,0.6551668047904968,-0.6983221769332886,0.21021294593811035,0.6842197775840759,-0.6698326468467712,0.21021294593811035,0.7121339440345764,-0.640228271484375,0.21021294593811035,0.7388628125190735,-0.6095582842826843,0.21021294593811035,0.7643619179725647,-0.5778737664222717,0.21021294593811035,0.7885888814926147,-0.5452274680137634,0.21021294593811035,0.8115032315254211,-0.511673629283905,0.21021294593811035,0.833066999912262,-0.4772682189941406,0.21021294593811035,0.8532441258430481,-0.4420683979988098,0.21021294593811035,0.8720011711120605,-0.4061328172683716,0.21021294593811035,0.8893068432807922,-0.36952126026153564,0.21021294593811035,0.9051323533058167,-0.33229467272758484,0.21021294593811035,0.9194513559341431,-0.29451504349708557,0.21021294593811035,0.9322400093078613,-0.2562451958656311,0.21021294593811035,0.9434770345687866,-0.21754884719848633,0.21021294593811035,0.9531437754631042,-0.17849041521549225,0.21021294593811035,0.9612240791320801,-0.13913489878177643,0.21021294593811035,0.9677044749259949,-0.09954781830310822,0.21021294593811035,0.9725742936134338,-0.059795040637254715,0.21021294593811035,0.975825309753418,-0.019942745566368103,0.21021294593811035,0.9774522185325623,0.019942745566368103,0.21021294593811035,0.9774522185325623,0.059795040637254715,0.21021294593811035,0.975825309753418,0.09954781830310822,0.21021294593811035,0.9725742936134338,0.13913489878177643,0.21021294593811035,0.9677044749259949,0.17849041521549225,0.21021294593811035,0.9612240791320801,0.21754884719848633,0.21021294593811035,0.9531437754631042,0.2562451958656311,0.21021294593811035,0.9434770345687866,0.29451504349708557,0.21021294593811035,0.9322400093078613,0.33229467272758484,0.21021294593811035,0.9194513559341431,0.36952126026153564,0.21021294593811035,0.9051323533058167,0.4061328172683716,0.21021294593811035,0.8893068432807922,0.4420683979988098,0.21021294593811035,0.8720011711120605,0.4772682189941406,0.21021294593811035,0.8532441258430481,0.511673629283905,0.21021294593811035,0.833066999912262,0.5452274680137634,0.21021294593811035,0.8115032315254211,0.5778737664222717,0.21021294593811035,0.7885888814926147,0.6095582842826843,0.21021294593811035,0.7643619179725647,0.640228271484375,0.21021294593811035,0.7388628125190735,0.6698326468467712,0.21021294593811035,0.7121339440345764,0.6983221769332886,0.21021294593811035,0.6842197775840759,0.725649356842041,0.21021294593811035,0.6551668047904968,0.7517688274383545,0.21021294593811035,0.625023365020752,0.7766370177268982,0.21021294593811035,0.5938395857810974,0.800212562084198,0.21021294593811035,0.5616675019264221,0.8224562406539917,0.21021294593811035,0.5285605192184448,0.8433310389518738,0.21021294593811035,0.49457383155822754,0.8628022074699402,0.21021294593811035,0.4597639739513397,0.8808372616767883,0.21021294593811035,0.42418888211250305,0.8974063396453857,0.21021294593811035,0.3879077732563019,0.9124817252159119,0.21021294593811035,0.3509809970855713,0.9260383248329163,0.21021294593811035,0.31347009539604187,0.9380537271499634,0.21021294593811035,0.2754374146461487,0.9485077261924744,0.21021294593811035,0.23694631457328796,0.9573830962181091,0.21021294593811035,0.19806084036827087,0.9646649956703186,0.21021294593811035,0.1588457077741623,0.9703413248062134,0.21021294593811035,0.11936619877815247,0.9744025468826294,0.21021294593811035,0.07968801259994507,0.9768420457839966,0.21021294593811035,0.03987719118595123,0.9776556491851807,0.21021294593811035,2.3945655829592553e-16,0.9603921175003052,0.2786521017551422,0.0,0.959592878818512,0.2786521017551422,-0.03917303681373596,0.9571964740753174,0.2786521017551422,-0.07828087359666824,0.9532069563865662,0.2786521017551422,-0.11725841462612152,0.9476308822631836,0.2786521017551422,-0.15604080259799957,0.9404775500297546,0.2786521017551422,-0.19456346333026886,0.9317589402198792,0.2786521017551422,-0.23276229202747345,0.9214894771575928,0.2786521017551422,-0.2705737352371216,0.909686267375946,0.2786521017551422,-0.30793479084968567,0.8963690400123596,0.2786521017551422,-0.34478336572647095,0.8815598487854004,0.2786521017551422,-0.38105806708335876,0.8652833700180054,0.2786521017551422,-0.41669851541519165,0.8475667834281921,0.2786521017551422,-0.4516454339027405,0.8284394145011902,0.2786521017551422,-0.4858406186103821,0.8079332709312439,0.2786521017551422,-0.519227147102356,0.7860823273658752,0.2786521017551422,-0.5517495274543762,0.7629230618476868,0.2786521017551422,-0.5833535194396973,0.7384940385818481,0.2786521017551422,-0.613986611366272,0.7128357887268066,0.2786521017551422,-0.6435977816581726,0.685991108417511,0.2786521017551422,-0.6721377372741699,0.6580047011375427,0.2786521017551422,-0.6995590329170227,0.6289230585098267,0.2786521017551422,-0.7258159518241882,0.59879469871521,0.2786521017551422,-0.7508648037910461,0.5676696300506592,0.2786521017551422,-0.7746639251708984,0.5355997681617737,0.2786521017551422,-0.7971736788749695,0.5026384592056274,0.2786521017551422,-0.8183566331863403,0.4688405692577362,0.2786521017551422,-0.838177502155304,0.43426233530044556,0.2786521017551422,-0.8566033244132996,0.39896130561828613,0.2786521017551422,-0.8736034035682678,0.36299625039100647,0.2786521017551422,-0.8891494274139404,0.32642701268196106,0.2786521017551422,-0.9032155871391296,0.2893144488334656,0.2786521017551422,-0.9157784581184387,0.2517203986644745,0.2786521017551422,-0.9268170595169067,0.2137073427438736,0.2786521017551422,-0.9363130927085876,0.17533861100673676,0.2786521017551422,-0.9442507028579712,0.1366780400276184,0.2786521017551422,-0.950616717338562,0.09778999537229538,0.2786521017551422,-0.9554005265235901,0.058739177882671356,0.2786521017551422,-0.9585941433906555,0.0195905938744545,0.2786521017551422,-0.9601922631263733,-0.0195905938744545,0.2786521017551422,-0.9601922631263733,-0.058739177882671356,0.2786521017551422,-0.9585941433906555,-0.09778999537229538,0.2786521017551422,-0.9554005265235901,-0.1366780400276184,0.2786521017551422,-0.950616717338562,-0.17533861100673676,0.2786521017551422,-0.9442507028579712,-0.2137073427438736,0.2786521017551422,-0.9363130927085876,-0.2517203986644745,0.2786521017551422,-0.9268170595169067,-0.2893144488334656,0.2786521017551422,-0.9157784581184387,-0.32642701268196106,0.2786521017551422,-0.9032155871391296,-0.36299625039100647,0.2786521017551422,-0.8891494274139404,-0.39896130561828613,0.2786521017551422,-0.8736034035682678,-0.43426233530044556,0.2786521017551422,-0.8566033244132996,-0.4688405692577362,0.2786521017551422,-0.838177502155304,-0.5026384592056274,0.2786521017551422,-0.8183566331863403,-0.5355997681617737,0.2786521017551422,-0.7971736788749695,-0.5676696300506592,0.2786521017551422,-0.7746639251708984,-0.59879469871521,0.2786521017551422,-0.7508648037910461,-0.6289230585098267,0.2786521017551422,-0.7258159518241882,-0.6580047011375427,0.2786521017551422,-0.6995590329170227,-0.685991108417511,0.2786521017551422,-0.6721377372741699,-0.7128357887268066,0.2786521017551422,-0.6435977816581726,-0.7384940385818481,0.2786521017551422,-0.613986611366272,-0.7629230618476868,0.2786521017551422,-0.5833535194396973,-0.7860823273658752,0.2786521017551422,-0.5517495274543762,-0.8079332709312439,0.2786521017551422,-0.519227147102356,-0.8284394145011902,0.2786521017551422,-0.4858406186103821,-0.8475667834281921,0.2786521017551422,-0.4516454339027405,-0.8652833700180054,0.2786521017551422,-0.41669851541519165,-0.8815598487854004,0.2786521017551422,-0.38105806708335876,-0.8963690400123596,0.2786521017551422,-0.34478336572647095,-0.909686267375946,0.2786521017551422,-0.30793479084968567,-0.9214894771575928,0.2786521017551422,-0.2705737352371216,-0.9317589402198792,0.2786521017551422,-0.23276229202747345,-0.9404775500297546,0.2786521017551422,-0.19456346333026886,-0.9476308822631836,0.2786521017551422,-0.15604080259799957,-0.9532069563865662,0.2786521017551422,-0.11725841462612152,-0.9571964740753174,0.2786521017551422,-0.07828087359666824,-0.959592878818512,0.2786521017551422,-0.03917303681373596,-0.9603921175003052,0.2786521017551422,-1.1761411138139563e-16,-0.959592878818512,0.2786521017551422,0.03917303681373596,-0.9571964740753174,0.2786521017551422,0.07828087359666824,-0.9532069563865662,0.2786521017551422,0.11725841462612152,-0.9476308822631836,0.2786521017551422,0.15604080259799957,-0.9404775500297546,0.2786521017551422,0.19456346333026886,-0.9317589402198792,0.2786521017551422,0.23276229202747345,-0.9214894771575928,0.2786521017551422,0.2705737352371216,-0.909686267375946,0.2786521017551422,0.30793479084968567,-0.8963690400123596,0.2786521017551422,0.34478336572647095,-0.8815598487854004,0.2786521017551422,0.38105806708335876,-0.8652833700180054,0.2786521017551422,0.41669851541519165,-0.8475667834281921,0.2786521017551422,0.4516454339027405,-0.8284394145011902,0.2786521017551422,0.4858406186103821,-0.8079332709312439,0.2786521017551422,0.519227147102356,-0.7860823273658752,0.2786521017551422,0.5517495274543762,-0.7629230618476868,0.2786521017551422,0.5833535194396973,-0.7384940385818481,0.2786521017551422,0.613986611366272,-0.7128357887268066,0.2786521017551422,0.6435977816581726,-0.685991108417511,0.2786521017551422,0.6721377372741699,-0.6580047011375427,0.2786521017551422,0.6995590329170227,-0.6289230585098267,0.2786521017551422,0.7258159518241882,-0.59879469871521,0.2786521017551422,0.7508648037910461,-0.5676696300506592,0.2786521017551422,0.7746639251708984,-0.5355997681617737,0.2786521017551422,0.7971736788749695,-0.5026384592056274,0.2786521017551422,0.8183566331863403,-0.4688405692577362,0.2786521017551422,0.838177502155304,-0.43426233530044556,0.2786521017551422,0.8566033244132996,-0.39896130561828613,0.2786521017551422,0.8736034035682678,-0.36299625039100647,0.2786521017551422,0.8891494274139404,-0.32642701268196106,0.2786521017551422,0.9032155871391296,-0.2893144488334656,0.2786521017551422,0.9157784581184387,-0.2517203986644745,0.2786521017551422,0.9268170595169067,-0.2137073427438736,0.2786521017551422,0.9363130927085876,-0.17533861100673676,0.2786521017551422,0.9442507028579712,-0.1366780400276184,0.2786521017551422,0.950616717338562,-0.09778999537229538,0.2786521017551422,0.9554005265235901,-0.058739177882671356,0.2786521017551422,0.9585941433906555,-0.0195905938744545,0.2786521017551422,0.9601922631263733,0.0195905938744545,0.2786521017551422,0.9601922631263733,0.058739177882671356,0.2786521017551422,0.9585941433906555,0.09778999537229538,0.2786521017551422,0.9554005265235901,0.1366780400276184,0.2786521017551422,0.950616717338562,0.17533861100673676,0.2786521017551422,0.9442507028579712,0.2137073427438736,0.2786521017551422,0.9363130927085876,0.2517203986644745,0.2786521017551422,0.9268170595169067,0.2893144488334656,0.2786521017551422,0.9157784581184387,0.32642701268196106,0.2786521017551422,0.9032155871391296,0.36299625039100647,0.2786521017551422,0.8891494274139404,0.39896130561828613,0.2786521017551422,0.8736034035682678,0.43426233530044556,0.2786521017551422,0.8566033244132996,0.4688405692577362,0.2786521017551422,0.838177502155304,0.5026384592056274,0.2786521017551422,0.8183566331863403,0.5355997681617737,0.2786521017551422,0.7971736788749695,0.5676696300506592,0.2786521017551422,0.7746639251708984,0.59879469871521,0.2786521017551422,0.7508648037910461,0.6289230585098267,0.2786521017551422,0.7258159518241882,0.6580047011375427,0.2786521017551422,0.6995590329170227,0.685991108417511,0.2786521017551422,0.6721377372741699,0.7128357887268066,0.2786521017551422,0.6435977816581726,0.7384940385818481,0.2786521017551422,0.613986611366272,0.7629230618476868,0.2786521017551422,0.5833535194396973,0.7860823273658752,0.2786521017551422,0.5517495274543762,0.8079332709312439,0.2786521017551422,0.519227147102356,0.8284394145011902,0.2786521017551422,0.4858406186103821,0.8475667834281921,0.2786521017551422,0.4516454339027405,0.8652833700180054,0.2786521017551422,0.41669851541519165,0.8815598487854004,0.2786521017551422,0.38105806708335876,0.8963690400123596,0.2786521017551422,0.34478336572647095,0.909686267375946,0.2786521017551422,0.30793479084968567,0.9214894771575928,0.2786521017551422,0.2705737352371216,0.9317589402198792,0.2786521017551422,0.23276229202747345,0.9404775500297546,0.2786521017551422,0.19456346333026886,0.9476308822631836,0.2786521017551422,0.15604080259799957,0.9532069563865662,0.2786521017551422,0.11725841462612152,0.9571964740753174,0.2786521017551422,0.07828087359666824,0.959592878818512,0.2786521017551422,0.03917303681373596,0.9603921175003052,0.2786521017551422,2.3522822276279126e-16,0.9383439421653748,0.3457030653953552,0.0,0.9375630617141724,0.3457030653953552,-0.03827372193336487,0.9352216720581055,0.3457030653953552,-0.07648374140262604,0.931323766708374,0.3457030653953552,-0.11456646770238876,0.9258756637573242,0.3457030653953552,-0.15245850384235382,0.9188866019248962,0.3457030653953552,-0.19009678065776825,0.9103681445121765,0.3457030653953552,-0.2274186760187149,0.9003344178199768,0.3457030653953552,-0.26436203718185425,0.8888022303581238,0.3457030653953552,-0.30086541175842285,0.8757907152175903,0.3457030653953552,-0.336868017911911,0.8613215088844299,0.3457030653953552,-0.37230995297431946,0.8454186916351318,0.3457030653953552,-0.40713217854499817,0.8281087875366211,0.3457030653953552,-0.44127681851387024,0.8094205856323242,0.3457030653953552,-0.47468695044517517,0.789385199546814,0.3457030653953552,-0.5073070526123047,0.768035888671875,0.3457030653953552,-0.5390827655792236,0.745408296585083,0.3457030653953552,-0.5699612498283386,0.721540093421936,0.3457030653953552,-0.5998910665512085,0.6964709162712097,0.3457030653953552,-0.6288224458694458,0.6702425479888916,0.3457030653953552,-0.656707227230072,0.6428986191749573,0.3457030653953552,-0.6834989190101624,0.6144846081733704,0.3457030653953552,-0.7091530561447144,0.5850479006767273,0.3457030653953552,-0.7336268424987793,0.5546374320983887,0.3457030653953552,-0.7568796277046204,0.5233038067817688,0.3457030653953552,-0.7788726091384888,0.49109917879104614,0.3457030653953552,-0.7995692491531372,0.45807719230651855,0.3457030653953552,-0.8189350962638855,0.42429277300834656,0.3457030653953552,-0.8369379043579102,0.38980215787887573,0.3457030653953552,-0.8535476922988892,0.35466277599334717,0.3457030653953552,-0.8687368631362915,0.31893306970596313,0.3457030653953552,-0.8824800848960876,0.28267255425453186,0.3457030653953552,-0.8947545289993286,0.24594153463840485,0.3457030653953552,-0.9055396914482117,0.20880118012428284,0.3457030653953552,-0.914817750453949,0.17131328582763672,0.3457030653953552,-0.9225730895996094,0.13354027271270752,0.3457030653953552,-0.9287929534912109,0.09554498642683029,0.3457030653953552,-0.9334669709205627,0.05739067494869232,0.3457030653953552,-0.9365872740745544,0.01914084516465664,0.3457030653953552,-0.9381487369537354,-0.01914084516465664,0.3457030653953552,-0.9381487369537354,-0.05739067494869232,0.3457030653953552,-0.9365872740745544,-0.09554498642683029,0.3457030653953552,-0.9334669709205627,-0.13354027271270752,0.3457030653953552,-0.9287929534912109,-0.17131328582763672,0.3457030653953552,-0.9225730895996094,-0.20880118012428284,0.3457030653953552,-0.914817750453949,-0.24594153463840485,0.3457030653953552,-0.9055396914482117,-0.28267255425453186,0.3457030653953552,-0.8947545289993286,-0.31893306970596313,0.3457030653953552,-0.8824800848960876,-0.35466277599334717,0.3457030653953552,-0.8687368631362915,-0.38980215787887573,0.3457030653953552,-0.8535476922988892,-0.42429277300834656,0.3457030653953552,-0.8369379043579102,-0.45807719230651855,0.3457030653953552,-0.8189350962638855,-0.49109917879104614,0.3457030653953552,-0.7995692491531372,-0.5233038067817688,0.3457030653953552,-0.7788726091384888,-0.5546374320983887,0.3457030653953552,-0.7568796277046204,-0.5850479006767273,0.3457030653953552,-0.7336268424987793,-0.6144846081733704,0.3457030653953552,-0.7091530561447144,-0.6428986191749573,0.3457030653953552,-0.6834989190101624,-0.6702425479888916,0.3457030653953552,-0.656707227230072,-0.6964709162712097,0.3457030653953552,-0.6288224458694458,-0.721540093421936,0.3457030653953552,-0.5998910665512085,-0.745408296585083,0.3457030653953552,-0.5699612498283386,-0.768035888671875,0.3457030653953552,-0.5390827655792236,-0.789385199546814,0.3457030653953552,-0.5073070526123047,-0.8094205856323242,0.3457030653953552,-0.47468695044517517,-0.8281087875366211,0.3457030653953552,-0.44127681851387024,-0.8454186916351318,0.3457030653953552,-0.40713217854499817,-0.8613215088844299,0.3457030653953552,-0.37230995297431946,-0.8757907152175903,0.3457030653953552,-0.336868017911911,-0.8888022303581238,0.3457030653953552,-0.30086541175842285,-0.9003344178199768,0.3457030653953552,-0.26436203718185425,-0.9103681445121765,0.3457030653953552,-0.2274186760187149,-0.9188866019248962,0.3457030653953552,-0.19009678065776825,-0.9258756637573242,0.3457030653953552,-0.15245850384235382,-0.931323766708374,0.3457030653953552,-0.11456646770238876,-0.9352216720581055,0.3457030653953552,-0.07648374140262604,-0.9375630617141724,0.3457030653953552,-0.03827372193336487,-0.9383439421653748,0.3457030653953552,-1.1491399533867554e-16,-0.9375630617141724,0.3457030653953552,0.03827372193336487,-0.9352216720581055,0.3457030653953552,0.07648374140262604,-0.931323766708374,0.3457030653953552,0.11456646770238876,-0.9258756637573242,0.3457030653953552,0.15245850384235382,-0.9188866019248962,0.3457030653953552,0.19009678065776825,-0.9103681445121765,0.3457030653953552,0.2274186760187149,-0.9003344178199768,0.3457030653953552,0.26436203718185425,-0.8888022303581238,0.3457030653953552,0.30086541175842285,-0.8757907152175903,0.3457030653953552,0.336868017911911,-0.8613215088844299,0.3457030653953552,0.37230995297431946,-0.8454186916351318,0.3457030653953552,0.40713217854499817,-0.8281087875366211,0.3457030653953552,0.44127681851387024,-0.8094205856323242,0.3457030653953552,0.47468695044517517,-0.789385199546814,0.3457030653953552,0.5073070526123047,-0.768035888671875,0.3457030653953552,0.5390827655792236,-0.745408296585083,0.3457030653953552,0.5699612498283386,-0.721540093421936,0.3457030653953552,0.5998910665512085,-0.6964709162712097,0.3457030653953552,0.6288224458694458,-0.6702425479888916,0.3457030653953552,0.656707227230072,-0.6428986191749573,0.3457030653953552,0.6834989190101624,-0.6144846081733704,0.3457030653953552,0.7091530561447144,-0.5850479006767273,0.3457030653953552,0.7336268424987793,-0.5546374320983887,0.3457030653953552,0.7568796277046204,-0.5233038067817688,0.3457030653953552,0.7788726091384888,-0.49109917879104614,0.3457030653953552,0.7995692491531372,-0.45807719230651855,0.3457030653953552,0.8189350962638855,-0.42429277300834656,0.3457030653953552,0.8369379043579102,-0.38980215787887573,0.3457030653953552,0.8535476922988892,-0.35466277599334717,0.3457030653953552,0.8687368631362915,-0.31893306970596313,0.3457030653953552,0.8824800848960876,-0.28267255425453186,0.3457030653953552,0.8947545289993286,-0.24594153463840485,0.3457030653953552,0.9055396914482117,-0.20880118012428284,0.3457030653953552,0.914817750453949,-0.17131328582763672,0.3457030653953552,0.9225730895996094,-0.13354027271270752,0.3457030653953552,0.9287929534912109,-0.09554498642683029,0.3457030653953552,0.9334669709205627,-0.05739067494869232,0.3457030653953552,0.9365872740745544,-0.01914084516465664,0.3457030653953552,0.9381487369537354,0.01914084516465664,0.3457030653953552,0.9381487369537354,0.05739067494869232,0.3457030653953552,0.9365872740745544,0.09554498642683029,0.3457030653953552,0.9334669709205627,0.13354027271270752,0.3457030653953552,0.9287929534912109,0.17131328582763672,0.3457030653953552,0.9225730895996094,0.20880118012428284,0.3457030653953552,0.914817750453949,0.24594153463840485,0.3457030653953552,0.9055396914482117,0.28267255425453186,0.3457030653953552,0.8947545289993286,0.31893306970596313,0.3457030653953552,0.8824800848960876,0.35466277599334717,0.3457030653953552,0.8687368631362915,0.38980215787887573,0.3457030653953552,0.8535476922988892,0.42429277300834656,0.3457030653953552,0.8369379043579102,0.45807719230651855,0.3457030653953552,0.8189350962638855,0.49109917879104614,0.3457030653953552,0.7995692491531372,0.5233038067817688,0.3457030653953552,0.7788726091384888,0.5546374320983887,0.3457030653953552,0.7568796277046204,0.5850479006767273,0.3457030653953552,0.7336268424987793,0.6144846081733704,0.3457030653953552,0.7091530561447144,0.6428986191749573,0.3457030653953552,0.6834989190101624,0.6702425479888916,0.3457030653953552,0.656707227230072,0.6964709162712097,0.3457030653953552,0.6288224458694458,0.721540093421936,0.3457030653953552,0.5998910665512085,0.745408296585083,0.3457030653953552,0.5699612498283386,0.768035888671875,0.3457030653953552,0.5390827655792236,0.789385199546814,0.3457030653953552,0.5073070526123047,0.8094205856323242,0.3457030653953552,0.47468695044517517,0.8281087875366211,0.3457030653953552,0.44127681851387024,0.8454186916351318,0.3457030653953552,0.40713217854499817,0.8613215088844299,0.3457030653953552,0.37230995297431946,0.8757907152175903,0.3457030653953552,0.336868017911911,0.8888022303581238,0.3457030653953552,0.30086541175842285,0.9003344178199768,0.3457030653953552,0.26436203718185425,0.9103681445121765,0.3457030653953552,0.2274186760187149,0.9188866019248962,0.3457030653953552,0.19009678065776825,0.9258756637573242,0.3457030653953552,0.15245850384235382,0.931323766708374,0.3457030653953552,0.11456646770238876,0.9352216720581055,0.3457030653953552,0.07648374140262604,0.9375630617141724,0.3457030653953552,0.03827372193336487,0.9383439421653748,0.3457030653953552,2.2982799067735107e-16,0.9116210341453552,0.41103172302246094,0.0,0.910862386226654,0.41103172302246094,-0.0371837317943573,0.9085876941680908,0.41103172302246094,-0.07430557906627655,0.9048007726669312,0.41103172302246094,-0.11130374670028687,0.8995078206062317,0.41103172302246094,-0.14811666309833527,0.8927178382873535,0.41103172302246094,-0.18468305468559265,0.8844419121742249,0.41103172302246094,-0.22094206511974335,0.8746939897537231,0.41103172302246094,-0.2568333148956299,0.8634901642799377,0.41103172302246094,-0.2922971248626709,0.8508492112159729,0.41103172302246094,-0.3272744119167328,0.8367920517921448,0.41103172302246094,-0.36170700192451477,0.8213421702384949,0.41103172302246094,-0.3955375552177429,0.8045252561569214,0.41103172302246094,-0.4287097752094269,0.786369264125824,0.41103172302246094,-0.46116843819618225,0.7669044137001038,0.41103172302246094,-0.49285954236984253,0.7461631298065186,0.41103172302246094,-0.5237303376197815,0.7241799831390381,0.41103172302246094,-0.5537294149398804,0.7009914517402649,0.41103172302246094,-0.5828068852424622,0.6766362190246582,0.41103172302246094,-0.6109143495559692,0.6511548161506653,0.41103172302246094,-0.6380049586296082,0.6245896220207214,0.41103172302246094,-0.6640337109565735,0.5969848036766052,0.41103172302246094,-0.6889572143554688,0.568386435508728,0.41103172302246094,-0.712734043598175,0.5388420224189758,0.41103172302246094,-0.7353245615959167,0.5084007382392883,0.41103172302246094,-0.7566912174224854,0.477113276720047,0.41103172302246094,-0.7767984867095947,0.44503170251846313,0.41103172302246094,-0.7956128120422363,0.4122094213962555,0.41103172302246094,-0.8131029009819031,0.3787010610103607,0.41103172302246094,-0.8292396664619446,0.3445624113082886,0.41103172302246094,-0.8439962267875671,0.3098502457141876,0.41103172302246094,-0.8573480844497681,0.2746223509311676,0.41103172302246094,-0.8692729473114014,0.2389374077320099,0.41103172302246094,-0.8797510266304016,0.20285476744174957,0.41103172302246094,-0.8887647986412048,0.16643449664115906,0.41103172302246094,-0.8962993025779724,0.12973719835281372,0.41103172302246094,-0.9023420214653015,0.09282397478818893,0.41103172302246094,-0.9068829417228699,0.055756255984306335,0.41103172302246094,-0.9099143743515015,0.018595736473798752,0.41103172302246094,-0.9114313721656799,-0.018595736473798752,0.41103172302246094,-0.9114313721656799,-0.055756255984306335,0.41103172302246094,-0.9099143743515015,-0.09282397478818893,0.41103172302246094,-0.9068829417228699,-0.12973719835281372,0.41103172302246094,-0.9023420214653015,-0.16643449664115906,0.41103172302246094,-0.8962993025779724,-0.20285476744174957,0.41103172302246094,-0.8887647986412048,-0.2389374077320099,0.41103172302246094,-0.8797510266304016,-0.2746223509311676,0.41103172302246094,-0.8692729473114014,-0.3098502457141876,0.41103172302246094,-0.8573480844497681,-0.3445624113082886,0.41103172302246094,-0.8439962267875671,-0.3787010610103607,0.41103172302246094,-0.8292396664619446,-0.4122094213962555,0.41103172302246094,-0.8131029009819031,-0.44503170251846313,0.41103172302246094,-0.7956128120422363,-0.477113276720047,0.41103172302246094,-0.7767984867095947,-0.5084007382392883,0.41103172302246094,-0.7566912174224854,-0.5388420224189758,0.41103172302246094,-0.7353245615959167,-0.568386435508728,0.41103172302246094,-0.712734043598175,-0.5969848036766052,0.41103172302246094,-0.6889572143554688,-0.6245896220207214,0.41103172302246094,-0.6640337109565735,-0.6511548161506653,0.41103172302246094,-0.6380049586296082,-0.6766362190246582,0.41103172302246094,-0.6109143495559692,-0.7009914517402649,0.41103172302246094,-0.5828068852424622,-0.7241799831390381,0.41103172302246094,-0.5537294149398804,-0.7461631298065186,0.41103172302246094,-0.5237303376197815,-0.7669044137001038,0.41103172302246094,-0.49285954236984253,-0.786369264125824,0.41103172302246094,-0.46116843819618225,-0.8045252561569214,0.41103172302246094,-0.4287097752094269,-0.8213421702384949,0.41103172302246094,-0.3955375552177429,-0.8367920517921448,0.41103172302246094,-0.36170700192451477,-0.8508492112159729,0.41103172302246094,-0.3272744119167328,-0.8634901642799377,0.41103172302246094,-0.2922971248626709,-0.8746939897537231,0.41103172302246094,-0.2568333148956299,-0.8844419121742249,0.41103172302246094,-0.22094206511974335,-0.8927178382873535,0.41103172302246094,-0.18468305468559265,-0.8995078206062317,0.41103172302246094,-0.14811666309833527,-0.9048007726669312,0.41103172302246094,-0.11130374670028687,-0.9085876941680908,0.41103172302246094,-0.07430557906627655,-0.910862386226654,0.41103172302246094,-0.0371837317943573,-0.9116210341453552,0.41103172302246094,-1.1164137766784014e-16,-0.910862386226654,0.41103172302246094,0.0371837317943573,-0.9085876941680908,0.41103172302246094,0.07430557906627655,-0.9048007726669312,0.41103172302246094,0.11130374670028687,-0.8995078206062317,0.41103172302246094,0.14811666309833527,-0.8927178382873535,0.41103172302246094,0.18468305468559265,-0.8844419121742249,0.41103172302246094,0.22094206511974335,-0.8746939897537231,0.41103172302246094,0.2568333148956299,-0.8634901642799377,0.41103172302246094,0.2922971248626709,-0.8508492112159729,0.41103172302246094,0.3272744119167328,-0.8367920517921448,0.41103172302246094,0.36170700192451477,-0.8213421702384949,0.41103172302246094,0.3955375552177429,-0.8045252561569214,0.41103172302246094,0.4287097752094269,-0.786369264125824,0.41103172302246094,0.46116843819618225,-0.7669044137001038,0.41103172302246094,0.49285954236984253,-0.7461631298065186,0.41103172302246094,0.5237303376197815,-0.7241799831390381,0.41103172302246094,0.5537294149398804,-0.7009914517402649,0.41103172302246094,0.5828068852424622,-0.6766362190246582,0.41103172302246094,0.6109143495559692,-0.6511548161506653,0.41103172302246094,0.6380049586296082,-0.6245896220207214,0.41103172302246094,0.6640337109565735,-0.5969848036766052,0.41103172302246094,0.6889572143554688,-0.568386435508728,0.41103172302246094,0.712734043598175,-0.5388420224189758,0.41103172302246094,0.7353245615959167,-0.5084007382392883,0.41103172302246094,0.7566912174224854,-0.477113276720047,0.41103172302246094,0.7767984867095947,-0.44503170251846313,0.41103172302246094,0.7956128120422363,-0.4122094213962555,0.41103172302246094,0.8131029009819031,-0.3787010610103607,0.41103172302246094,0.8292396664619446,-0.3445624113082886,0.41103172302246094,0.8439962267875671,-0.3098502457141876,0.41103172302246094,0.8573480844497681,-0.2746223509311676,0.41103172302246094,0.8692729473114014,-0.2389374077320099,0.41103172302246094,0.8797510266304016,-0.20285476744174957,0.41103172302246094,0.8887647986412048,-0.16643449664115906,0.41103172302246094,0.8962993025779724,-0.12973719835281372,0.41103172302246094,0.9023420214653015,-0.09282397478818893,0.41103172302246094,0.9068829417228699,-0.055756255984306335,0.41103172302246094,0.9099143743515015,-0.018595736473798752,0.41103172302246094,0.9114313721656799,0.018595736473798752,0.41103172302246094,0.9114313721656799,0.055756255984306335,0.41103172302246094,0.9099143743515015,0.09282397478818893,0.41103172302246094,0.9068829417228699,0.12973719835281372,0.41103172302246094,0.9023420214653015,0.16643449664115906,0.41103172302246094,0.8962993025779724,0.20285476744174957,0.41103172302246094,0.8887647986412048,0.2389374077320099,0.41103172302246094,0.8797510266304016,0.2746223509311676,0.41103172302246094,0.8692729473114014,0.3098502457141876,0.41103172302246094,0.8573480844497681,0.3445624113082886,0.41103172302246094,0.8439962267875671,0.3787010610103607,0.41103172302246094,0.8292396664619446,0.4122094213962555,0.41103172302246094,0.8131029009819031,0.44503170251846313,0.41103172302246094,0.7956128120422363,0.477113276720047,0.41103172302246094,0.7767984867095947,0.5084007382392883,0.41103172302246094,0.7566912174224854,0.5388420224189758,0.41103172302246094,0.7353245615959167,0.568386435508728,0.41103172302246094,0.712734043598175,0.5969848036766052,0.41103172302246094,0.6889572143554688,0.6245896220207214,0.41103172302246094,0.6640337109565735,0.6511548161506653,0.41103172302246094,0.6380049586296082,0.6766362190246582,0.41103172302246094,0.6109143495559692,0.7009914517402649,0.41103172302246094,0.5828068852424622,0.7241799831390381,0.41103172302246094,0.5537294149398804,0.7461631298065186,0.41103172302246094,0.5237303376197815,0.7669044137001038,0.41103172302246094,0.49285954236984253,0.786369264125824,0.41103172302246094,0.46116843819618225,0.8045252561569214,0.41103172302246094,0.4287097752094269,0.8213421702384949,0.41103172302246094,0.3955375552177429,0.8367920517921448,0.41103172302246094,0.36170700192451477,0.8508492112159729,0.41103172302246094,0.3272744119167328,0.8634901642799377,0.41103172302246094,0.2922971248626709,0.8746939897537231,0.41103172302246094,0.2568333148956299,0.8844419121742249,0.41103172302246094,0.22094206511974335,0.8927178382873535,0.41103172302246094,0.18468305468559265,0.8995078206062317,0.41103172302246094,0.14811666309833527,0.9048007726669312,0.41103172302246094,0.11130374670028687,0.9085876941680908,0.41103172302246094,0.07430557906627655,0.910862386226654,0.41103172302246094,0.0371837317943573,0.9116210341453552,0.41103172302246094,2.232827553356803e-16,0.88035649061203,0.4743126630783081,0.0,0.8796238303184509,0.4743126630783081,-0.035908494144678116,0.8774271607398987,0.4743126630783081,-0.0717572271823883,0.8737700581550598,0.4743126630783081,-0.10748652368783951,0.868658721446991,0.4743126630783081,-0.14303691685199738,0.8621015548706055,0.4743126630783081,-0.17834924161434174,0.8541094660758972,0.4743126630783081,-0.21336472034454346,0.8446958661079407,0.4743126630783081,-0.2480250746011734,0.8338763117790222,0.4743126630783081,-0.282272607088089,0.8216688632965088,0.4743126630783081,-0.31605035066604614,0.808093786239624,0.4743126630783081,-0.3493020534515381,0.7931737899780273,0.4743126630783081,-0.3819723427295685,0.7769335508346558,0.4743126630783081,-0.41400691866874695,0.7594002485275269,0.4743126630783081,-0.4453524053096771,0.740602970123291,0.4743126630783081,-0.47595664858818054,0.7205730676651001,0.4743126630783081,-0.5057687163352966,0.6993438005447388,0.4743126630783081,-0.5347389578819275,0.6769505739212036,0.4743126630783081,-0.5628191828727722,0.6534305810928345,0.4743126630783081,-0.5899626612663269,0.6288231015205383,0.4743126630783081,-0.6161242127418518,0.6031689643859863,0.4743126630783081,-0.6412602663040161,0.5765109062194824,0.4743126630783081,-0.6653290390968323,0.5488932728767395,0.4743126630783081,-0.6882904171943665,0.5203620791435242,0.4743126630783081,-0.7101061940193176,0.4909648299217224,0.4743126630783081,-0.7307400703430176,0.46075040102005005,0.4743126630783081,-0.7501577138900757,0.4297690689563751,0.4743126630783081,-0.7683267593383789,0.3980724513530731,0.4743126630783081,-0.7852170467376709,0.36571329832077026,0.4743126630783081,-0.8008003830909729,0.33274543285369873,0.4743126630783081,-0.8150508999824524,0.29922375082969666,0.4743126630783081,-0.827944815158844,0.2652040421962738,0.4743126630783081,-0.8394607305526733,0.2307429164648056,0.4743126630783081,-0.8495794534683228,0.19589774310588837,0.4743126630783081,-0.8582841157913208,0.16072651743888855,0.4743126630783081,-0.8655602335929871,0.12528778612613678,0.4743126630783081,-0.8713957071304321,0.0896405279636383,0.4743126630783081,-0.8757808208465576,0.053844064474105835,0.4743126630783081,-0.8787083029747009,0.017957983538508415,0.4743126630783081,-0.8801732659339905,-0.017957983538508415,0.4743126630783081,-0.8801732659339905,-0.053844064474105835,0.4743126630783081,-0.8787083029747009,-0.0896405279636383,0.4743126630783081,-0.8757808208465576,-0.12528778612613678,0.4743126630783081,-0.8713957071304321,-0.16072651743888855,0.4743126630783081,-0.8655602335929871,-0.19589774310588837,0.4743126630783081,-0.8582841157913208,-0.2307429164648056,0.4743126630783081,-0.8495794534683228,-0.2652040421962738,0.4743126630783081,-0.8394607305526733,-0.29922375082969666,0.4743126630783081,-0.827944815158844,-0.33274543285369873,0.4743126630783081,-0.8150508999824524,-0.36571329832077026,0.4743126630783081,-0.8008003830909729,-0.3980724513530731,0.4743126630783081,-0.7852170467376709,-0.4297690689563751,0.4743126630783081,-0.7683267593383789,-0.46075040102005005,0.4743126630783081,-0.7501577138900757,-0.4909648299217224,0.4743126630783081,-0.7307400703430176,-0.5203620791435242,0.4743126630783081,-0.7101061940193176,-0.5488932728767395,0.4743126630783081,-0.6882904171943665,-0.5765109062194824,0.4743126630783081,-0.6653290390968323,-0.6031689643859863,0.4743126630783081,-0.6412602663040161,-0.6288231015205383,0.4743126630783081,-0.6161242127418518,-0.6534305810928345,0.4743126630783081,-0.5899626612663269,-0.6769505739212036,0.4743126630783081,-0.5628191828727722,-0.6993438005447388,0.4743126630783081,-0.5347389578819275,-0.7205730676651001,0.4743126630783081,-0.5057687163352966,-0.740602970123291,0.4743126630783081,-0.47595664858818054,-0.7594002485275269,0.4743126630783081,-0.4453524053096771,-0.7769335508346558,0.4743126630783081,-0.41400691866874695,-0.7931737899780273,0.4743126630783081,-0.3819723427295685,-0.808093786239624,0.4743126630783081,-0.3493020534515381,-0.8216688632965088,0.4743126630783081,-0.31605035066604614,-0.8338763117790222,0.4743126630783081,-0.282272607088089,-0.8446958661079407,0.4743126630783081,-0.2480250746011734,-0.8541094660758972,0.4743126630783081,-0.21336472034454346,-0.8621015548706055,0.4743126630783081,-0.17834924161434174,-0.868658721446991,0.4743126630783081,-0.14303691685199738,-0.8737700581550598,0.4743126630783081,-0.10748652368783951,-0.8774271607398987,0.4743126630783081,-0.0717572271823883,-0.8796238303184509,0.4743126630783081,-0.035908494144678116,-0.88035649061203,0.4743126630783081,-1.0781257037056899e-16,-0.8796238303184509,0.4743126630783081,0.035908494144678116,-0.8774271607398987,0.4743126630783081,0.0717572271823883,-0.8737700581550598,0.4743126630783081,0.10748652368783951,-0.868658721446991,0.4743126630783081,0.14303691685199738,-0.8621015548706055,0.4743126630783081,0.17834924161434174,-0.8541094660758972,0.4743126630783081,0.21336472034454346,-0.8446958661079407,0.4743126630783081,0.2480250746011734,-0.8338763117790222,0.4743126630783081,0.282272607088089,-0.8216688632965088,0.4743126630783081,0.31605035066604614,-0.808093786239624,0.4743126630783081,0.3493020534515381,-0.7931737899780273,0.4743126630783081,0.3819723427295685,-0.7769335508346558,0.4743126630783081,0.41400691866874695,-0.7594002485275269,0.4743126630783081,0.4453524053096771,-0.740602970123291,0.4743126630783081,0.47595664858818054,-0.7205730676651001,0.4743126630783081,0.5057687163352966,-0.6993438005447388,0.4743126630783081,0.5347389578819275,-0.6769505739212036,0.4743126630783081,0.5628191828727722,-0.6534305810928345,0.4743126630783081,0.5899626612663269,-0.6288231015205383,0.4743126630783081,0.6161242127418518,-0.6031689643859863,0.4743126630783081,0.6412602663040161,-0.5765109062194824,0.4743126630783081,0.6653290390968323,-0.5488932728767395,0.4743126630783081,0.6882904171943665,-0.5203620791435242,0.4743126630783081,0.7101061940193176,-0.4909648299217224,0.4743126630783081,0.7307400703430176,-0.46075040102005005,0.4743126630783081,0.7501577138900757,-0.4297690689563751,0.4743126630783081,0.7683267593383789,-0.3980724513530731,0.4743126630783081,0.7852170467376709,-0.36571329832077026,0.4743126630783081,0.8008003830909729,-0.33274543285369873,0.4743126630783081,0.8150508999824524,-0.29922375082969666,0.4743126630783081,0.827944815158844,-0.2652040421962738,0.4743126630783081,0.8394607305526733,-0.2307429164648056,0.4743126630783081,0.8495794534683228,-0.19589774310588837,0.4743126630783081,0.8582841157913208,-0.16072651743888855,0.4743126630783081,0.8655602335929871,-0.12528778612613678,0.4743126630783081,0.8713957071304321,-0.0896405279636383,0.4743126630783081,0.8757808208465576,-0.053844064474105835,0.4743126630783081,0.8787083029747009,-0.017957983538508415,0.4743126630783081,0.8801732659339905,0.017957983538508415,0.4743126630783081,0.8801732659339905,0.053844064474105835,0.4743126630783081,0.8787083029747009,0.0896405279636383,0.4743126630783081,0.8757808208465576,0.12528778612613678,0.4743126630783081,0.8713957071304321,0.16072651743888855,0.4743126630783081,0.8655602335929871,0.19589774310588837,0.4743126630783081,0.8582841157913208,0.2307429164648056,0.4743126630783081,0.8495794534683228,0.2652040421962738,0.4743126630783081,0.8394607305526733,0.29922375082969666,0.4743126630783081,0.827944815158844,0.33274543285369873,0.4743126630783081,0.8150508999824524,0.36571329832077026,0.4743126630783081,0.8008003830909729,0.3980724513530731,0.4743126630783081,0.7852170467376709,0.4297690689563751,0.4743126630783081,0.7683267593383789,0.46075040102005005,0.4743126630783081,0.7501577138900757,0.4909648299217224,0.4743126630783081,0.7307400703430176,0.5203620791435242,0.4743126630783081,0.7101061940193176,0.5488932728767395,0.4743126630783081,0.6882904171943665,0.5765109062194824,0.4743126630783081,0.6653290390968323,0.6031689643859863,0.4743126630783081,0.6412602663040161,0.6288231015205383,0.4743126630783081,0.6161242127418518,0.6534305810928345,0.4743126630783081,0.5899626612663269,0.6769505739212036,0.4743126630783081,0.5628191828727722,0.6993438005447388,0.4743126630783081,0.5347389578819275,0.7205730676651001,0.4743126630783081,0.5057687163352966,0.740602970123291,0.4743126630783081,0.47595664858818054,0.7594002485275269,0.4743126630783081,0.4453524053096771,0.7769335508346558,0.4743126630783081,0.41400691866874695,0.7931737899780273,0.4743126630783081,0.3819723427295685,0.808093786239624,0.4743126630783081,0.3493020534515381,0.8216688632965088,0.4743126630783081,0.31605035066604614,0.8338763117790222,0.4743126630783081,0.282272607088089,0.8446958661079407,0.4743126630783081,0.2480250746011734,0.8541094660758972,0.4743126630783081,0.21336472034454346,0.8621015548706055,0.4743126630783081,0.17834924161434174,0.868658721446991,0.4743126630783081,0.14303691685199738,0.8737700581550598,0.4743126630783081,0.10748652368783951,0.8774271607398987,0.4743126630783081,0.0717572271823883,0.8796238303184509,0.4743126630783081,0.035908494144678116,0.88035649061203,0.4743126630783081,2.1562514074113798e-16,0.8447059988975525,0.5352305769920349,0.0,0.8440030217170715,0.5352305769920349,-0.03445436432957649,0.8418952822685242,0.5352305769920349,-0.06885138154029846,0.8383863568305969,0.5352305769920349,-0.10313380509614944,0.8334819674491882,0.5352305769920349,-0.1372445672750473,0.8271902799606323,0.5352305769920349,-0.17112690210342407,0.8195219039916992,0.5352305769920349,-0.20472441613674164,0.8104894757270813,0.5352305769920349,-0.2379811853170395,0.8001080751419067,0.5352305769920349,-0.2708418369293213,0.7883949279785156,0.5352305769920349,-0.3032517433166504,0.7753696441650391,0.5352305769920349,-0.3351568877696991,0.7610538005828857,0.5352305769920349,-0.3665041923522949,0.7454712390899658,0.5352305769920349,-0.3972415030002594,0.7286479473114014,0.5352305769920349,-0.42731761932373047,0.710611879825592,0.5352305769920349,-0.4566825330257416,0.6913930773735046,0.5352305769920349,-0.4852873384952545,0.6710235476493835,0.5352305769920349,-0.5130844116210938,0.6495371460914612,0.5352305769920349,-0.5400275588035583,0.6269696354866028,0.5352305769920349,-0.5660718083381653,0.6033585667610168,0.5352305769920349,-0.5911739468574524,0.5787433385848999,0.5352305769920349,-0.6152921319007874,0.5531647801399231,0.5352305769920349,-0.6383861899375916,0.5266655683517456,0.5352305769920349,-0.6604177355766296,0.49928978085517883,0.5352305769920349,-0.6813501119613647,0.4710829555988312,0.5352305769920349,-0.7011483907699585,0.44209206104278564,0.5352305769920349,-0.7197797298431396,0.4123653769493103,0.5352305769920349,-0.7372130155563354,0.38195231556892395,0.5352305769920349,-0.7534192800521851,0.35090354084968567,0.5352305769920349,-0.76837158203125,0.3192707300186157,0.5352305769920349,-0.7820450067520142,0.28710654377937317,0.5352305769920349,-0.7944167852401733,0.2544644773006439,0.5352305769920349,-0.80546635389328,0.22139886021614075,0.5352305769920349,-0.8151752948760986,0.1879647672176361,0.5352305769920349,-0.823527455329895,0.15421782433986664,0.5352305769920349,-0.8305089473724365,0.12021419405937195,0.5352305769920349,-0.8361080884933472,0.08601048588752747,0.5352305769920349,-0.8403156399726868,0.05166362226009369,0.5352305769920349,-0.8431246280670166,0.017230767756700516,0.5352305769920349,-0.8445302248001099,-0.017230767756700516,0.5352305769920349,-0.8445302248001099,-0.05166362226009369,0.5352305769920349,-0.8431246280670166,-0.08601048588752747,0.5352305769920349,-0.8403156399726868,-0.12021419405937195,0.5352305769920349,-0.8361080884933472,-0.15421782433986664,0.5352305769920349,-0.8305089473724365,-0.1879647672176361,0.5352305769920349,-0.823527455329895,-0.22139886021614075,0.5352305769920349,-0.8151752948760986,-0.2544644773006439,0.5352305769920349,-0.80546635389328,-0.28710654377937317,0.5352305769920349,-0.7944167852401733,-0.3192707300186157,0.5352305769920349,-0.7820450067520142,-0.35090354084968567,0.5352305769920349,-0.76837158203125,-0.38195231556892395,0.5352305769920349,-0.7534192800521851,-0.4123653769493103,0.5352305769920349,-0.7372130155563354,-0.44209206104278564,0.5352305769920349,-0.7197797298431396,-0.4710829555988312,0.5352305769920349,-0.7011483907699585,-0.49928978085517883,0.5352305769920349,-0.6813501119613647,-0.5266655683517456,0.5352305769920349,-0.6604177355766296,-0.5531647801399231,0.5352305769920349,-0.6383861899375916,-0.5787433385848999,0.5352305769920349,-0.6152921319007874,-0.6033585667610168,0.5352305769920349,-0.5911739468574524,-0.6269696354866028,0.5352305769920349,-0.5660718083381653,-0.6495371460914612,0.5352305769920349,-0.5400275588035583,-0.6710235476493835,0.5352305769920349,-0.5130844116210938,-0.6913930773735046,0.5352305769920349,-0.4852873384952545,-0.710611879825592,0.5352305769920349,-0.4566825330257416,-0.7286479473114014,0.5352305769920349,-0.42731761932373047,-0.7454712390899658,0.5352305769920349,-0.3972415030002594,-0.7610538005828857,0.5352305769920349,-0.3665041923522949,-0.7753696441650391,0.5352305769920349,-0.3351568877696991,-0.7883949279785156,0.5352305769920349,-0.3032517433166504,-0.8001080751419067,0.5352305769920349,-0.2708418369293213,-0.8104894757270813,0.5352305769920349,-0.2379811853170395,-0.8195219039916992,0.5352305769920349,-0.20472441613674164,-0.8271902799606323,0.5352305769920349,-0.17112690210342407,-0.8334819674491882,0.5352305769920349,-0.1372445672750473,-0.8383863568305969,0.5352305769920349,-0.10313380509614944,-0.8418952822685242,0.5352305769920349,-0.06885138154029846,-0.8440030217170715,0.5352305769920349,-0.03445436432957649,-0.8447059988975525,0.5352305769920349,-1.0344665154051e-16,-0.8440030217170715,0.5352305769920349,0.03445436432957649,-0.8418952822685242,0.5352305769920349,0.06885138154029846,-0.8383863568305969,0.5352305769920349,0.10313380509614944,-0.8334819674491882,0.5352305769920349,0.1372445672750473,-0.8271902799606323,0.5352305769920349,0.17112690210342407,-0.8195219039916992,0.5352305769920349,0.20472441613674164,-0.8104894757270813,0.5352305769920349,0.2379811853170395,-0.8001080751419067,0.5352305769920349,0.2708418369293213,-0.7883949279785156,0.5352305769920349,0.3032517433166504,-0.7753696441650391,0.5352305769920349,0.3351568877696991,-0.7610538005828857,0.5352305769920349,0.3665041923522949,-0.7454712390899658,0.5352305769920349,0.3972415030002594,-0.7286479473114014,0.5352305769920349,0.42731761932373047,-0.710611879825592,0.5352305769920349,0.4566825330257416,-0.6913930773735046,0.5352305769920349,0.4852873384952545,-0.6710235476493835,0.5352305769920349,0.5130844116210938,-0.6495371460914612,0.5352305769920349,0.5400275588035583,-0.6269696354866028,0.5352305769920349,0.5660718083381653,-0.6033585667610168,0.5352305769920349,0.5911739468574524,-0.5787433385848999,0.5352305769920349,0.6152921319007874,-0.5531647801399231,0.5352305769920349,0.6383861899375916,-0.5266655683517456,0.5352305769920349,0.6604177355766296,-0.49928978085517883,0.5352305769920349,0.6813501119613647,-0.4710829555988312,0.5352305769920349,0.7011483907699585,-0.44209206104278564,0.5352305769920349,0.7197797298431396,-0.4123653769493103,0.5352305769920349,0.7372130155563354,-0.38195231556892395,0.5352305769920349,0.7534192800521851,-0.35090354084968567,0.5352305769920349,0.76837158203125,-0.3192707300186157,0.5352305769920349,0.7820450067520142,-0.28710654377937317,0.5352305769920349,0.7944167852401733,-0.2544644773006439,0.5352305769920349,0.80546635389328,-0.22139886021614075,0.5352305769920349,0.8151752948760986,-0.1879647672176361,0.5352305769920349,0.823527455329895,-0.15421782433986664,0.5352305769920349,0.8305089473724365,-0.12021419405937195,0.5352305769920349,0.8361080884933472,-0.08601048588752747,0.5352305769920349,0.8403156399726868,-0.05166362226009369,0.5352305769920349,0.8431246280670166,-0.017230767756700516,0.5352305769920349,0.8445302248001099,0.017230767756700516,0.5352305769920349,0.8445302248001099,0.05166362226009369,0.5352305769920349,0.8431246280670166,0.08601048588752747,0.5352305769920349,0.8403156399726868,0.12021419405937195,0.5352305769920349,0.8361080884933472,0.15421782433986664,0.5352305769920349,0.8305089473724365,0.1879647672176361,0.5352305769920349,0.823527455329895,0.22139886021614075,0.5352305769920349,0.8151752948760986,0.2544644773006439,0.5352305769920349,0.80546635389328,0.28710654377937317,0.5352305769920349,0.7944167852401733,0.3192707300186157,0.5352305769920349,0.7820450067520142,0.35090354084968567,0.5352305769920349,0.76837158203125,0.38195231556892395,0.5352305769920349,0.7534192800521851,0.4123653769493103,0.5352305769920349,0.7372130155563354,0.44209206104278564,0.5352305769920349,0.7197797298431396,0.4710829555988312,0.5352305769920349,0.7011483907699585,0.49928978085517883,0.5352305769920349,0.6813501119613647,0.5266655683517456,0.5352305769920349,0.6604177355766296,0.5531647801399231,0.5352305769920349,0.6383861899375916,0.5787433385848999,0.5352305769920349,0.6152921319007874,0.6033585667610168,0.5352305769920349,0.5911739468574524,0.6269696354866028,0.5352305769920349,0.5660718083381653,0.6495371460914612,0.5352305769920349,0.5400275588035583,0.6710235476493835,0.5352305769920349,0.5130844116210938,0.6913930773735046,0.5352305769920349,0.4852873384952545,0.710611879825592,0.5352305769920349,0.4566825330257416,0.7286479473114014,0.5352305769920349,0.42731761932373047,0.7454712390899658,0.5352305769920349,0.3972415030002594,0.7610538005828857,0.5352305769920349,0.3665041923522949,0.7753696441650391,0.5352305769920349,0.3351568877696991,0.7883949279785156,0.5352305769920349,0.3032517433166504,0.8001080751419067,0.5352305769920349,0.2708418369293213,0.8104894757270813,0.5352305769920349,0.2379811853170395,0.8195219039916992,0.5352305769920349,0.20472441613674164,0.8271902799606323,0.5352305769920349,0.17112690210342407,0.8334819674491882,0.5352305769920349,0.1372445672750473,0.8383863568305969,0.5352305769920349,0.10313380509614944,0.8418952822685242,0.5352305769920349,0.06885138154029846,0.8440030217170715,0.5352305769920349,0.03445436432957649,0.8447059988975525,0.5352305769920349,2.0689330308102e-16,0.804847240447998,0.5934820175170898,0.0,0.804177463054657,0.5934820175170898,-0.03282858058810234,0.8021692037582397,0.5934820175170898,-0.06560252606868744,0.7988258004188538,0.5934820175170898,-0.09826727211475372,0.7941527962684631,0.5934820175170898,-0.13076847791671753,0.7881580591201782,0.5934820175170898,-0.1630520224571228,0.7808514833450317,0.5934820175170898,-0.19506417214870453,0.7722452878952026,0.5934820175170898,-0.2267516702413559,0.7623537182807922,0.5934820175170898,-0.2580617666244507,0.7511933445930481,0.5934820175170898,-0.2889423370361328,0.7387826442718506,0.5934820175170898,-0.3193419873714447,0.7251423001289368,0.5934820175170898,-0.34921014308929443,0.7102950811386108,0.5934820175170898,-0.37849706411361694,0.694265604019165,0.5934820175170898,-0.40715399384498596,0.6770805716514587,0.5934820175170898,-0.43513327836990356,0.6587686538696289,0.5934820175170898,-0.4623883068561554,0.639360249042511,0.5934820175170898,-0.48887374997138977,0.618887722492218,0.5934820175170898,-0.5145455002784729,0.5973851084709167,0.5934820175170898,-0.5393608808517456,0.5748881697654724,0.5934820175170898,-0.5632784962654114,0.5514343976974487,0.5934820175170898,-0.586258590221405,0.5270628333091736,0.5934820175170898,-0.6082629561424255,0.501814067363739,0.5934820175170898,-0.629254937171936,0.4757300317287445,0.5934820175170898,-0.6491995453834534,0.4488541781902313,0.5934820175170898,-0.6680636405944824,0.4212312698364258,0.5934820175170898,-0.6858158111572266,0.3929072618484497,0.5934820175170898,-0.7024264931678772,0.36392930150032043,0.5934820175170898,-0.7178680300712585,0.33434563875198364,0.5934820175170898,-0.7321147918701172,0.30420544743537903,0.5934820175170898,-0.7451429963111877,0.27355897426605225,0.5934820175170898,-0.7569310069084167,0.24245716631412506,0.5934820175170898,-0.7674592137336731,0.2109518200159073,0.5934820175170898,-0.7767099738121033,0.17909535765647888,0.5934820175170898,-0.7846680283546448,0.14694081246852875,0.5934820175170898,-0.7913200855255127,0.11454170197248459,0.5934820175170898,-0.7966550588607788,0.08195195347070694,0.5934820175170898,-0.8006640672683716,0.04922579601407051,0.5934820175170898,-0.803340494632721,0.01641770638525486,0.5934820175170898,-0.8046797513961792,-0.01641770638525486,0.5934820175170898,-0.8046797513961792,-0.04922579601407051,0.5934820175170898,-0.803340494632721,-0.08195195347070694,0.5934820175170898,-0.8006640672683716,-0.11454170197248459,0.5934820175170898,-0.7966550588607788,-0.14694081246852875,0.5934820175170898,-0.7913200855255127,-0.17909535765647888,0.5934820175170898,-0.7846680283546448,-0.2109518200159073,0.5934820175170898,-0.7767099738121033,-0.24245716631412506,0.5934820175170898,-0.7674592137336731,-0.27355897426605225,0.5934820175170898,-0.7569310069084167,-0.30420544743537903,0.5934820175170898,-0.7451429963111877,-0.33434563875198364,0.5934820175170898,-0.7321147918701172,-0.36392930150032043,0.5934820175170898,-0.7178680300712585,-0.3929072618484497,0.5934820175170898,-0.7024264931678772,-0.4212312698364258,0.5934820175170898,-0.6858158111572266,-0.4488541781902313,0.5934820175170898,-0.6680636405944824,-0.4757300317287445,0.5934820175170898,-0.6491995453834534,-0.501814067363739,0.5934820175170898,-0.629254937171936,-0.5270628333091736,0.5934820175170898,-0.6082629561424255,-0.5514343976974487,0.5934820175170898,-0.586258590221405,-0.5748881697654724,0.5934820175170898,-0.5632784962654114,-0.5973851084709167,0.5934820175170898,-0.5393608808517456,-0.618887722492218,0.5934820175170898,-0.5145455002784729,-0.639360249042511,0.5934820175170898,-0.48887374997138977,-0.6587686538696289,0.5934820175170898,-0.4623883068561554,-0.6770805716514587,0.5934820175170898,-0.43513327836990356,-0.694265604019165,0.5934820175170898,-0.40715399384498596,-0.7102950811386108,0.5934820175170898,-0.37849706411361694,-0.7251423001289368,0.5934820175170898,-0.34921014308929443,-0.7387826442718506,0.5934820175170898,-0.3193419873714447,-0.7511933445930481,0.5934820175170898,-0.2889423370361328,-0.7623537182807922,0.5934820175170898,-0.2580617666244507,-0.7722452878952026,0.5934820175170898,-0.2267516702413559,-0.7808514833450317,0.5934820175170898,-0.19506417214870453,-0.7881580591201782,0.5934820175170898,-0.1630520224571228,-0.7941527962684631,0.5934820175170898,-0.13076847791671753,-0.7988258004188538,0.5934820175170898,-0.09826727211475372,-0.8021692037582397,0.5934820175170898,-0.06560252606868744,-0.804177463054657,0.5934820175170898,-0.03282858058810234,-0.804847240447998,0.5934820175170898,-9.856535948416108e-17,-0.804177463054657,0.5934820175170898,0.03282858058810234,-0.8021692037582397,0.5934820175170898,0.06560252606868744,-0.7988258004188538,0.5934820175170898,0.09826727211475372,-0.7941527962684631,0.5934820175170898,0.13076847791671753,-0.7881580591201782,0.5934820175170898,0.1630520224571228,-0.7808514833450317,0.5934820175170898,0.19506417214870453,-0.7722452878952026,0.5934820175170898,0.2267516702413559,-0.7623537182807922,0.5934820175170898,0.2580617666244507,-0.7511933445930481,0.5934820175170898,0.2889423370361328,-0.7387826442718506,0.5934820175170898,0.3193419873714447,-0.7251423001289368,0.5934820175170898,0.34921014308929443,-0.7102950811386108,0.5934820175170898,0.37849706411361694,-0.694265604019165,0.5934820175170898,0.40715399384498596,-0.6770805716514587,0.5934820175170898,0.43513327836990356,-0.6587686538696289,0.5934820175170898,0.4623883068561554,-0.639360249042511,0.5934820175170898,0.48887374997138977,-0.618887722492218,0.5934820175170898,0.5145455002784729,-0.5973851084709167,0.5934820175170898,0.5393608808517456,-0.5748881697654724,0.5934820175170898,0.5632784962654114,-0.5514343976974487,0.5934820175170898,0.586258590221405,-0.5270628333091736,0.5934820175170898,0.6082629561424255,-0.501814067363739,0.5934820175170898,0.629254937171936,-0.4757300317287445,0.5934820175170898,0.6491995453834534,-0.4488541781902313,0.5934820175170898,0.6680636405944824,-0.4212312698364258,0.5934820175170898,0.6858158111572266,-0.3929072618484497,0.5934820175170898,0.7024264931678772,-0.36392930150032043,0.5934820175170898,0.7178680300712585,-0.33434563875198364,0.5934820175170898,0.7321147918701172,-0.30420544743537903,0.5934820175170898,0.7451429963111877,-0.27355897426605225,0.5934820175170898,0.7569310069084167,-0.24245716631412506,0.5934820175170898,0.7674592137336731,-0.2109518200159073,0.5934820175170898,0.7767099738121033,-0.17909535765647888,0.5934820175170898,0.7846680283546448,-0.14694081246852875,0.5934820175170898,0.7913200855255127,-0.11454170197248459,0.5934820175170898,0.7966550588607788,-0.08195195347070694,0.5934820175170898,0.8006640672683716,-0.04922579601407051,0.5934820175170898,0.803340494632721,-0.01641770638525486,0.5934820175170898,0.8046797513961792,0.01641770638525486,0.5934820175170898,0.8046797513961792,0.04922579601407051,0.5934820175170898,0.803340494632721,0.08195195347070694,0.5934820175170898,0.8006640672683716,0.11454170197248459,0.5934820175170898,0.7966550588607788,0.14694081246852875,0.5934820175170898,0.7913200855255127,0.17909535765647888,0.5934820175170898,0.7846680283546448,0.2109518200159073,0.5934820175170898,0.7767099738121033,0.24245716631412506,0.5934820175170898,0.7674592137336731,0.27355897426605225,0.5934820175170898,0.7569310069084167,0.30420544743537903,0.5934820175170898,0.7451429963111877,0.33434563875198364,0.5934820175170898,0.7321147918701172,0.36392930150032043,0.5934820175170898,0.7178680300712585,0.3929072618484497,0.5934820175170898,0.7024264931678772,0.4212312698364258,0.5934820175170898,0.6858158111572266,0.4488541781902313,0.5934820175170898,0.6680636405944824,0.4757300317287445,0.5934820175170898,0.6491995453834534,0.501814067363739,0.5934820175170898,0.629254937171936,0.5270628333091736,0.5934820175170898,0.6082629561424255,0.5514343976974487,0.5934820175170898,0.586258590221405,0.5748881697654724,0.5934820175170898,0.5632784962654114,0.5973851084709167,0.5934820175170898,0.5393608808517456,0.618887722492218,0.5934820175170898,0.5145455002784729,0.639360249042511,0.5934820175170898,0.48887374997138977,0.6587686538696289,0.5934820175170898,0.4623883068561554,0.6770805716514587,0.5934820175170898,0.43513327836990356,0.694265604019165,0.5934820175170898,0.40715399384498596,0.7102950811386108,0.5934820175170898,0.37849706411361694,0.7251423001289368,0.5934820175170898,0.34921014308929443,0.7387826442718506,0.5934820175170898,0.3193419873714447,0.7511933445930481,0.5934820175170898,0.2889423370361328,0.7623537182807922,0.5934820175170898,0.2580617666244507,0.7722452878952026,0.5934820175170898,0.2267516702413559,0.7808514833450317,0.5934820175170898,0.19506417214870453,0.7881580591201782,0.5934820175170898,0.1630520224571228,0.7941527962684631,0.5934820175170898,0.13076847791671753,0.7988258004188538,0.5934820175170898,0.09826727211475372,0.8021692037582397,0.5934820175170898,0.06560252606868744,0.804177463054657,0.5934820175170898,0.03282858058810234,0.804847240447998,0.5934820175170898,1.9713071896832217e-16,0.7609787583351135,0.6487767696380615,0.0,0.7603455185890198,0.6487767696380615,-0.031039249151945114,0.7584466934204102,0.6487767696380615,-0.06202683597803116,0.7552855014801025,0.6487767696380615,-0.09291118383407593,0.7508672475814819,0.6487767696380615,-0.12364089488983154,0.7451992630958557,0.6487767696380615,-0.15416480600833893,0.7382909059524536,0.6487767696380615,-0.18443213403224945,0.7301537990570068,0.6487767696380615,-0.2143924981355667,0.7208014130592346,0.6487767696380615,-0.2439960241317749,0.7102493047714233,0.6487767696380615,-0.27319344878196716,0.6985150575637817,0.6487767696380615,-0.30193614959716797,0.6856182217597961,0.6487767696380615,-0.33017632365226746,0.6715801954269409,0.6487767696380615,-0.35786694288253784,0.6564244031906128,0.6487767696380615,-0.38496193289756775,0.6401761174201965,0.6487767696380615,-0.4114162027835846,0.6228622794151306,0.6487767696380615,-0.43718570470809937,0.6045117378234863,0.6487767696380615,-0.4622275233268738,0.5851550102233887,0.6487767696380615,-0.4865000545978546,0.5648244023323059,0.6487767696380615,-0.5099628567695618,0.5435537099838257,0.6487767696380615,-0.5325767993927002,0.5213783383369446,0.6487767696380615,-0.5543044209480286,0.4983351230621338,0.6487767696380615,-0.5751094222068787,0.47446250915527344,0.6487767696380615,-0.594957172870636,0.44980019330978394,0.6487767696380615,-0.6138147115707397,0.4243892431259155,0.6487767696380615,-0.6316506266593933,0.39827194809913635,0.6487767696380615,-0.6484351754188538,0.3714917302131653,0.6487767696380615,-0.664140522480011,0.3440932333469391,0.6487767696380615,-0.6787404417991638,0.31612202525138855,0.6487767696380615,-0.6922106742858887,0.28762465715408325,0.6487767696380615,-0.7045287489891052,0.258648544549942,0.6487767696380615,-0.7156742811203003,0.2292419672012329,0.6487767696380615,-0.7256285548210144,0.19945381581783295,0.6487767696380615,-0.7343751788139343,0.16933371126651764,0.6487767696380615,-0.7418994903564453,0.1389317661523819,0.6487767696380615,-0.7481889128684998,0.10829856991767883,0.6487767696380615,-0.7532331347465515,0.07748513668775558,0.6487767696380615,-0.7570236325263977,0.04654272645711899,0.6487767696380615,-0.7595541477203369,0.015522854402661324,0.6487767696380615,-0.7608204483985901,-0.015522854402661324,0.6487767696380615,-0.7608204483985901,-0.04654272645711899,0.6487767696380615,-0.7595541477203369,-0.07748513668775558,0.6487767696380615,-0.7570236325263977,-0.10829856991767883,0.6487767696380615,-0.7532331347465515,-0.1389317661523819,0.6487767696380615,-0.7481889128684998,-0.16933371126651764,0.6487767696380615,-0.7418994903564453,-0.19945381581783295,0.6487767696380615,-0.7343751788139343,-0.2292419672012329,0.6487767696380615,-0.7256285548210144,-0.258648544549942,0.6487767696380615,-0.7156742811203003,-0.28762465715408325,0.6487767696380615,-0.7045287489891052,-0.31612202525138855,0.6487767696380615,-0.6922106742858887,-0.3440932333469391,0.6487767696380615,-0.6787404417991638,-0.3714917302131653,0.6487767696380615,-0.664140522480011,-0.39827194809913635,0.6487767696380615,-0.6484351754188538,-0.4243892431259155,0.6487767696380615,-0.6316506266593933,-0.44980019330978394,0.6487767696380615,-0.6138147115707397,-0.47446250915527344,0.6487767696380615,-0.594957172870636,-0.4983351230621338,0.6487767696380615,-0.5751094222068787,-0.5213783383369446,0.6487767696380615,-0.5543044209480286,-0.5435537099838257,0.6487767696380615,-0.5325767993927002,-0.5648244023323059,0.6487767696380615,-0.5099628567695618,-0.5851550102233887,0.6487767696380615,-0.4865000545978546,-0.6045117378234863,0.6487767696380615,-0.4622275233268738,-0.6228622794151306,0.6487767696380615,-0.43718570470809937,-0.6401761174201965,0.6487767696380615,-0.4114162027835846,-0.6564244031906128,0.6487767696380615,-0.38496193289756775,-0.6715801954269409,0.6487767696380615,-0.35786694288253784,-0.6856182217597961,0.6487767696380615,-0.33017632365226746,-0.6985150575637817,0.6487767696380615,-0.30193614959716797,-0.7102493047714233,0.6487767696380615,-0.27319344878196716,-0.7208014130592346,0.6487767696380615,-0.2439960241317749,-0.7301537990570068,0.6487767696380615,-0.2143924981355667,-0.7382909059524536,0.6487767696380615,-0.18443213403224945,-0.7451992630958557,0.6487767696380615,-0.15416480600833893,-0.7508672475814819,0.6487767696380615,-0.12364089488983154,-0.7552855014801025,0.6487767696380615,-0.09291118383407593,-0.7584466934204102,0.6487767696380615,-0.06202683597803116,-0.7603455185890198,0.6487767696380615,-0.031039249151945114,-0.7609787583351135,0.6487767696380615,-9.319302654642108e-17,-0.7603455185890198,0.6487767696380615,0.031039249151945114,-0.7584466934204102,0.6487767696380615,0.06202683597803116,-0.7552855014801025,0.6487767696380615,0.09291118383407593,-0.7508672475814819,0.6487767696380615,0.12364089488983154,-0.7451992630958557,0.6487767696380615,0.15416480600833893,-0.7382909059524536,0.6487767696380615,0.18443213403224945,-0.7301537990570068,0.6487767696380615,0.2143924981355667,-0.7208014130592346,0.6487767696380615,0.2439960241317749,-0.7102493047714233,0.6487767696380615,0.27319344878196716,-0.6985150575637817,0.6487767696380615,0.30193614959716797,-0.6856182217597961,0.6487767696380615,0.33017632365226746,-0.6715801954269409,0.6487767696380615,0.35786694288253784,-0.6564244031906128,0.6487767696380615,0.38496193289756775,-0.6401761174201965,0.6487767696380615,0.4114162027835846,-0.6228622794151306,0.6487767696380615,0.43718570470809937,-0.6045117378234863,0.6487767696380615,0.4622275233268738,-0.5851550102233887,0.6487767696380615,0.4865000545978546,-0.5648244023323059,0.6487767696380615,0.5099628567695618,-0.5435537099838257,0.6487767696380615,0.5325767993927002,-0.5213783383369446,0.6487767696380615,0.5543044209480286,-0.4983351230621338,0.6487767696380615,0.5751094222068787,-0.47446250915527344,0.6487767696380615,0.594957172870636,-0.44980019330978394,0.6487767696380615,0.6138147115707397,-0.4243892431259155,0.6487767696380615,0.6316506266593933,-0.39827194809913635,0.6487767696380615,0.6484351754188538,-0.3714917302131653,0.6487767696380615,0.664140522480011,-0.3440932333469391,0.6487767696380615,0.6787404417991638,-0.31612202525138855,0.6487767696380615,0.6922106742858887,-0.28762465715408325,0.6487767696380615,0.7045287489891052,-0.258648544549942,0.6487767696380615,0.7156742811203003,-0.2292419672012329,0.6487767696380615,0.7256285548210144,-0.19945381581783295,0.6487767696380615,0.7343751788139343,-0.16933371126651764,0.6487767696380615,0.7418994903564453,-0.1389317661523819,0.6487767696380615,0.7481889128684998,-0.10829856991767883,0.6487767696380615,0.7532331347465515,-0.07748513668775558,0.6487767696380615,0.7570236325263977,-0.04654272645711899,0.6487767696380615,0.7595541477203369,-0.015522854402661324,0.6487767696380615,0.7608204483985901,0.015522854402661324,0.6487767696380615,0.7608204483985901,0.04654272645711899,0.6487767696380615,0.7595541477203369,0.07748513668775558,0.6487767696380615,0.7570236325263977,0.10829856991767883,0.6487767696380615,0.7532331347465515,0.1389317661523819,0.6487767696380615,0.7481889128684998,0.16933371126651764,0.6487767696380615,0.7418994903564453,0.19945381581783295,0.6487767696380615,0.7343751788139343,0.2292419672012329,0.6487767696380615,0.7256285548210144,0.258648544549942,0.6487767696380615,0.7156742811203003,0.28762465715408325,0.6487767696380615,0.7045287489891052,0.31612202525138855,0.6487767696380615,0.6922106742858887,0.3440932333469391,0.6487767696380615,0.6787404417991638,0.3714917302131653,0.6487767696380615,0.664140522480011,0.39827194809913635,0.6487767696380615,0.6484351754188538,0.4243892431259155,0.6487767696380615,0.6316506266593933,0.44980019330978394,0.6487767696380615,0.6138147115707397,0.47446250915527344,0.6487767696380615,0.594957172870636,0.4983351230621338,0.6487767696380615,0.5751094222068787,0.5213783383369446,0.6487767696380615,0.5543044209480286,0.5435537099838257,0.6487767696380615,0.5325767993927002,0.5648244023323059,0.6487767696380615,0.5099628567695618,0.5851550102233887,0.6487767696380615,0.4865000545978546,0.6045117378234863,0.6487767696380615,0.4622275233268738,0.6228622794151306,0.6487767696380615,0.43718570470809937,0.6401761174201965,0.6487767696380615,0.4114162027835846,0.6564244031906128,0.6487767696380615,0.38496193289756775,0.6715801954269409,0.6487767696380615,0.35786694288253784,0.6856182217597961,0.6487767696380615,0.33017632365226746,0.6985150575637817,0.6487767696380615,0.30193614959716797,0.7102493047714233,0.6487767696380615,0.27319344878196716,0.7208014130592346,0.6487767696380615,0.2439960241317749,0.7301537990570068,0.6487767696380615,0.2143924981355667,0.7382909059524536,0.6487767696380615,0.18443213403224945,0.7451992630958557,0.6487767696380615,0.15416480600833893,0.7508672475814819,0.6487767696380615,0.12364089488983154,0.7552855014801025,0.6487767696380615,0.09291118383407593,0.7584466934204102,0.6487767696380615,0.06202683597803116,0.7603455185890198,0.6487767696380615,0.031039249151945114,0.7609787583351135,0.6487767696380615,1.8638605309284216e-16,0.7133191823959351,0.7008393406867981,0.0,0.7127255201339722,0.7008393406867981,-0.029095280915498734,0.7109456658363342,0.7008393406867981,-0.05814213305711746,0.7079824805259705,0.7008393406867981,-0.08709221333265305,0.7038409113883972,0.7008393406867981,-0.11589734256267548,0.6985278725624084,0.7008393406867981,-0.14450956881046295,0.6920522451400757,0.7008393406867981,-0.17288127541542053,0.6844247579574585,0.7008393406867981,-0.20096524059772491,0.6756580471992493,0.7008393406867981,-0.2287147045135498,0.6657668352127075,0.7008393406867981,-0.25608351826667786,0.6547675132751465,0.7008393406867981,-0.2830260694026947,0.6426783800125122,0.7008393406867981,-0.309497594833374,0.6295195817947388,0.7008393406867981,-0.33545395731925964,0.6153129935264587,0.7008393406867981,-0.3608520030975342,0.600082278251648,0.7008393406867981,-0.3856494724750519,0.5838527679443359,0.7008393406867981,-0.409805029630661,0.5666515231132507,0.7008393406867981,-0.43327853083610535,0.5485071539878845,0.7008393406867981,-0.45603084564208984,0.5294498205184937,0.7008393406867981,-0.4780241847038269,0.5095112919807434,0.7008393406867981,-0.49922189116477966,0.4887247085571289,0.7008393406867981,-0.5195886492729187,0.46712473034858704,0.7008393406867981,-0.539090633392334,0.4447472393512726,0.7008393406867981,-0.5576953887939453,0.42162951827049255,0.7008393406867981,-0.5753718614578247,0.3978100121021271,0.7008393406867981,-0.5920907258987427,0.37332841753959656,0.7008393406867981,-0.6078240871429443,0.3482254445552826,0.7008393406867981,-0.6225457787513733,0.32254287600517273,0.7008393406867981,-0.6362313628196716,0.2963234782218933,0.7008393406867981,-0.6488579511642456,0.2696108818054199,0.7008393406867981,-0.6604045629501343,0.242449551820755,0.7008393406867981,-0.6708520650863647,0.2148846834897995,0.7008393406867981,-0.680182933807373,0.18696215748786926,0.7008393406867981,-0.6883817315101624,0.1587284505367279,0.7008393406867981,-0.6954347491264343,0.13023054599761963,0.7008393406867981,-0.7013303637504578,0.10151590406894684,0.7008393406867981,-0.7060586214065552,0.07263229042291641,0.7008393406867981,-0.709611713886261,0.0436277836561203,0.7008393406867981,-0.7119837403297424,0.014550668187439442,0.7008393406867981,-0.7131707668304443,-0.014550668187439442,0.7008393406867981,-0.7131707668304443,-0.0436277836561203,0.7008393406867981,-0.7119837403297424,-0.07263229042291641,0.7008393406867981,-0.709611713886261,-0.10151590406894684,0.7008393406867981,-0.7060586214065552,-0.13023054599761963,0.7008393406867981,-0.7013303637504578,-0.1587284505367279,0.7008393406867981,-0.6954347491264343,-0.18696215748786926,0.7008393406867981,-0.6883817315101624,-0.2148846834897995,0.7008393406867981,-0.680182933807373,-0.242449551820755,0.7008393406867981,-0.6708520650863647,-0.2696108818054199,0.7008393406867981,-0.6604045629501343,-0.2963234782218933,0.7008393406867981,-0.6488579511642456,-0.32254287600517273,0.7008393406867981,-0.6362313628196716,-0.3482254445552826,0.7008393406867981,-0.6225457787513733,-0.37332841753959656,0.7008393406867981,-0.6078240871429443,-0.3978100121021271,0.7008393406867981,-0.5920907258987427,-0.42162951827049255,0.7008393406867981,-0.5753718614578247,-0.4447472393512726,0.7008393406867981,-0.5576953887939453,-0.46712473034858704,0.7008393406867981,-0.539090633392334,-0.4887247085571289,0.7008393406867981,-0.5195886492729187,-0.5095112919807434,0.7008393406867981,-0.49922189116477966,-0.5294498205184937,0.7008393406867981,-0.4780241847038269,-0.5485071539878845,0.7008393406867981,-0.45603084564208984,-0.5666515231132507,0.7008393406867981,-0.43327853083610535,-0.5838527679443359,0.7008393406867981,-0.409805029630661,-0.600082278251648,0.7008393406867981,-0.3856494724750519,-0.6153129935264587,0.7008393406867981,-0.3608520030975342,-0.6295195817947388,0.7008393406867981,-0.33545395731925964,-0.6426783800125122,0.7008393406867981,-0.309497594833374,-0.6547675132751465,0.7008393406867981,-0.2830260694026947,-0.6657668352127075,0.7008393406867981,-0.25608351826667786,-0.6756580471992493,0.7008393406867981,-0.2287147045135498,-0.6844247579574585,0.7008393406867981,-0.20096524059772491,-0.6920522451400757,0.7008393406867981,-0.17288127541542053,-0.6985278725624084,0.7008393406867981,-0.14450956881046295,-0.7038409113883972,0.7008393406867981,-0.11589734256267548,-0.7079824805259705,0.7008393406867981,-0.08709221333265305,-0.7109456658363342,0.7008393406867981,-0.05814213305711746,-0.7127255201339722,0.7008393406867981,-0.029095280915498734,-0.7133191823959351,0.7008393406867981,-8.735640705702242e-17,-0.7127255201339722,0.7008393406867981,0.029095280915498734,-0.7109456658363342,0.7008393406867981,0.05814213305711746,-0.7079824805259705,0.7008393406867981,0.08709221333265305,-0.7038409113883972,0.7008393406867981,0.11589734256267548,-0.6985278725624084,0.7008393406867981,0.14450956881046295,-0.6920522451400757,0.7008393406867981,0.17288127541542053,-0.6844247579574585,0.7008393406867981,0.20096524059772491,-0.6756580471992493,0.7008393406867981,0.2287147045135498,-0.6657668352127075,0.7008393406867981,0.25608351826667786,-0.6547675132751465,0.7008393406867981,0.2830260694026947,-0.6426783800125122,0.7008393406867981,0.309497594833374,-0.6295195817947388,0.7008393406867981,0.33545395731925964,-0.6153129935264587,0.7008393406867981,0.3608520030975342,-0.600082278251648,0.7008393406867981,0.3856494724750519,-0.5838527679443359,0.7008393406867981,0.409805029630661,-0.5666515231132507,0.7008393406867981,0.43327853083610535,-0.5485071539878845,0.7008393406867981,0.45603084564208984,-0.5294498205184937,0.7008393406867981,0.4780241847038269,-0.5095112919807434,0.7008393406867981,0.49922189116477966,-0.4887247085571289,0.7008393406867981,0.5195886492729187,-0.46712473034858704,0.7008393406867981,0.539090633392334,-0.4447472393512726,0.7008393406867981,0.5576953887939453,-0.42162951827049255,0.7008393406867981,0.5753718614578247,-0.3978100121021271,0.7008393406867981,0.5920907258987427,-0.37332841753959656,0.7008393406867981,0.6078240871429443,-0.3482254445552826,0.7008393406867981,0.6225457787513733,-0.32254287600517273,0.7008393406867981,0.6362313628196716,-0.2963234782218933,0.7008393406867981,0.6488579511642456,-0.2696108818054199,0.7008393406867981,0.6604045629501343,-0.242449551820755,0.7008393406867981,0.6708520650863647,-0.2148846834897995,0.7008393406867981,0.680182933807373,-0.18696215748786926,0.7008393406867981,0.6883817315101624,-0.1587284505367279,0.7008393406867981,0.6954347491264343,-0.13023054599761963,0.7008393406867981,0.7013303637504578,-0.10151590406894684,0.7008393406867981,0.7060586214065552,-0.07263229042291641,0.7008393406867981,0.709611713886261,-0.0436277836561203,0.7008393406867981,0.7119837403297424,-0.014550668187439442,0.7008393406867981,0.7131707668304443,0.014550668187439442,0.7008393406867981,0.7131707668304443,0.0436277836561203,0.7008393406867981,0.7119837403297424,0.07263229042291641,0.7008393406867981,0.709611713886261,0.10151590406894684,0.7008393406867981,0.7060586214065552,0.13023054599761963,0.7008393406867981,0.7013303637504578,0.1587284505367279,0.7008393406867981,0.6954347491264343,0.18696215748786926,0.7008393406867981,0.6883817315101624,0.2148846834897995,0.7008393406867981,0.680182933807373,0.242449551820755,0.7008393406867981,0.6708520650863647,0.2696108818054199,0.7008393406867981,0.6604045629501343,0.2963234782218933,0.7008393406867981,0.6488579511642456,0.32254287600517273,0.7008393406867981,0.6362313628196716,0.3482254445552826,0.7008393406867981,0.6225457787513733,0.37332841753959656,0.7008393406867981,0.6078240871429443,0.3978100121021271,0.7008393406867981,0.5920907258987427,0.42162951827049255,0.7008393406867981,0.5753718614578247,0.4447472393512726,0.7008393406867981,0.5576953887939453,0.46712473034858704,0.7008393406867981,0.539090633392334,0.4887247085571289,0.7008393406867981,0.5195886492729187,0.5095112919807434,0.7008393406867981,0.49922189116477966,0.5294498205184937,0.7008393406867981,0.4780241847038269,0.5485071539878845,0.7008393406867981,0.45603084564208984,0.5666515231132507,0.7008393406867981,0.43327853083610535,0.5838527679443359,0.7008393406867981,0.409805029630661,0.600082278251648,0.7008393406867981,0.3856494724750519,0.6153129935264587,0.7008393406867981,0.3608520030975342,0.6295195817947388,0.7008393406867981,0.33545395731925964,0.6426783800125122,0.7008393406867981,0.309497594833374,0.6547675132751465,0.7008393406867981,0.2830260694026947,0.6657668352127075,0.7008393406867981,0.25608351826667786,0.6756580471992493,0.7008393406867981,0.2287147045135498,0.6844247579574585,0.7008393406867981,0.20096524059772491,0.6920522451400757,0.7008393406867981,0.17288127541542053,0.6985278725624084,0.7008393406867981,0.14450956881046295,0.7038409113883972,0.7008393406867981,0.11589734256267548,0.7079824805259705,0.7008393406867981,0.08709221333265305,0.7109456658363342,0.7008393406867981,0.05814213305711746,0.7127255201339722,0.7008393406867981,0.029095280915498734,0.7133191823959351,0.7008393406867981,1.7471281411404483e-16,0.6621058583259583,0.7494103312492371,0.0,0.6615548133850098,0.7494103312492371,-0.0270063616335392,0.6599027514457703,0.7494103312492371,-0.05396777391433716,0.6571522951126099,0.7494103312492371,-0.0808393582701683,0.653308093547821,0.7494103312492371,-0.1075764000415802,0.6483765244483948,0.7494103312492371,-0.13413438200950623,0.6423657536506653,0.7494103312492371,-0.16046911478042603,0.6352859139442444,0.7494103312492371,-0.1865367740392685,0.6271486282348633,0.7494103312492371,-0.21229395270347595,0.6179675459861755,0.7494103312492371,-0.2376977801322937,0.6077579259872437,0.7494103312492371,-0.2627059817314148,0.5965367555618286,0.7494103312492371,-0.287276953458786,0.5843226909637451,0.7494103312492371,-0.31136977672576904,0.5711360573768616,0.7494103312492371,-0.33494433760643005,0.5569988489151001,0.7494103312492371,-0.3579614460468292,0.541934609413147,0.7494103312492371,-0.3803827464580536,0.5259683132171631,0.7494103312492371,-0.40217092633247375,0.509126603603363,0.7494103312492371,-0.4232897460460663,0.49143755435943604,0.7494103312492371,-0.4437040388584137,0.47293052077293396,0.7494103312492371,-0.463379830121994,0.4536363184452057,0.7494103312492371,-0.4822843670845032,0.43358710408210754,0.7494103312492371,-0.5003861784934998,0.41281622648239136,0.7494103312492371,-0.5176551938056946,0.39135825634002686,0.7494103312492371,-0.5340625643730164,0.3692489266395569,0.7494103312492371,-0.5495811104774475,0.3465249836444855,0.7494103312492371,-0.564184844493866,0.3232243061065674,0.7494103312492371,-0.5778496265411377,0.299385666847229,0.7494103312492371,-0.590552568435669,0.27504870295524597,0.7494103312492371,-0.602272629737854,0.25025394558906555,0.7494103312492371,-0.6129902601242065,0.2250426858663559,0.7494103312492371,-0.6226876378059387,0.1994568556547165,0.7494103312492371,-0.6313486099243164,0.17353905737400055,0.7494103312492371,-0.6389588117599487,0.14733241498470306,0.7494103312492371,-0.6455054879188538,0.12088054418563843,0.7494103312492371,-0.6509777307510376,0.09422748535871506,0.7494103312492371,-0.6553665399551392,0.06741759181022644,0.7494103312492371,-0.6586645841598511,0.04049549251794815,0.7494103312492371,-0.6608662605285645,0.013505990616977215,0.7494103312492371,-0.6619680523872375,-0.013505990616977215,0.7494103312492371,-0.6619680523872375,-0.04049549251794815,0.7494103312492371,-0.6608662605285645,-0.06741759181022644,0.7494103312492371,-0.6586645841598511,-0.09422748535871506,0.7494103312492371,-0.6553665399551392,-0.12088054418563843,0.7494103312492371,-0.6509777307510376,-0.14733241498470306,0.7494103312492371,-0.6455054879188538,-0.17353905737400055,0.7494103312492371,-0.6389588117599487,-0.1994568556547165,0.7494103312492371,-0.6313486099243164,-0.2250426858663559,0.7494103312492371,-0.6226876378059387,-0.25025394558906555,0.7494103312492371,-0.6129902601242065,-0.27504870295524597,0.7494103312492371,-0.602272629737854,-0.299385666847229,0.7494103312492371,-0.590552568435669,-0.3232243061065674,0.7494103312492371,-0.5778496265411377,-0.3465249836444855,0.7494103312492371,-0.564184844493866,-0.3692489266395569,0.7494103312492371,-0.5495811104774475,-0.39135825634002686,0.7494103312492371,-0.5340625643730164,-0.41281622648239136,0.7494103312492371,-0.5176551938056946,-0.43358710408210754,0.7494103312492371,-0.5003861784934998,-0.4536363184452057,0.7494103312492371,-0.4822843670845032,-0.47293052077293396,0.7494103312492371,-0.463379830121994,-0.49143755435943604,0.7494103312492371,-0.4437040388584137,-0.509126603603363,0.7494103312492371,-0.4232897460460663,-0.5259683132171631,0.7494103312492371,-0.40217092633247375,-0.541934609413147,0.7494103312492371,-0.3803827464580536,-0.5569988489151001,0.7494103312492371,-0.3579614460468292,-0.5711360573768616,0.7494103312492371,-0.33494433760643005,-0.5843226909637451,0.7494103312492371,-0.31136977672576904,-0.5965367555618286,0.7494103312492371,-0.287276953458786,-0.6077579259872437,0.7494103312492371,-0.2627059817314148,-0.6179675459861755,0.7494103312492371,-0.2376977801322937,-0.6271486282348633,0.7494103312492371,-0.21229395270347595,-0.6352859139442444,0.7494103312492371,-0.1865367740392685,-0.6423657536506653,0.7494103312492371,-0.16046911478042603,-0.6483765244483948,0.7494103312492371,-0.13413438200950623,-0.653308093547821,0.7494103312492371,-0.1075764000415802,-0.6571522951126099,0.7494103312492371,-0.0808393582701683,-0.6599027514457703,0.7494103312492371,-0.05396777391433716,-0.6615548133850098,0.7494103312492371,-0.0270063616335392,-0.6621058583259583,0.7494103312492371,-8.108457806885755e-17,-0.6615548133850098,0.7494103312492371,0.0270063616335392,-0.6599027514457703,0.7494103312492371,0.05396777391433716,-0.6571522951126099,0.7494103312492371,0.0808393582701683,-0.653308093547821,0.7494103312492371,0.1075764000415802,-0.6483765244483948,0.7494103312492371,0.13413438200950623,-0.6423657536506653,0.7494103312492371,0.16046911478042603,-0.6352859139442444,0.7494103312492371,0.1865367740392685,-0.6271486282348633,0.7494103312492371,0.21229395270347595,-0.6179675459861755,0.7494103312492371,0.2376977801322937,-0.6077579259872437,0.7494103312492371,0.2627059817314148,-0.5965367555618286,0.7494103312492371,0.287276953458786,-0.5843226909637451,0.7494103312492371,0.31136977672576904,-0.5711360573768616,0.7494103312492371,0.33494433760643005,-0.5569988489151001,0.7494103312492371,0.3579614460468292,-0.541934609413147,0.7494103312492371,0.3803827464580536,-0.5259683132171631,0.7494103312492371,0.40217092633247375,-0.509126603603363,0.7494103312492371,0.4232897460460663,-0.49143755435943604,0.7494103312492371,0.4437040388584137,-0.47293052077293396,0.7494103312492371,0.463379830121994,-0.4536363184452057,0.7494103312492371,0.4822843670845032,-0.43358710408210754,0.7494103312492371,0.5003861784934998,-0.41281622648239136,0.7494103312492371,0.5176551938056946,-0.39135825634002686,0.7494103312492371,0.5340625643730164,-0.3692489266395569,0.7494103312492371,0.5495811104774475,-0.3465249836444855,0.7494103312492371,0.564184844493866,-0.3232243061065674,0.7494103312492371,0.5778496265411377,-0.299385666847229,0.7494103312492371,0.590552568435669,-0.27504870295524597,0.7494103312492371,0.602272629737854,-0.25025394558906555,0.7494103312492371,0.6129902601242065,-0.2250426858663559,0.7494103312492371,0.6226876378059387,-0.1994568556547165,0.7494103312492371,0.6313486099243164,-0.17353905737400055,0.7494103312492371,0.6389588117599487,-0.14733241498470306,0.7494103312492371,0.6455054879188538,-0.12088054418563843,0.7494103312492371,0.6509777307510376,-0.09422748535871506,0.7494103312492371,0.6553665399551392,-0.06741759181022644,0.7494103312492371,0.6586645841598511,-0.04049549251794815,0.7494103312492371,0.6608662605285645,-0.013505990616977215,0.7494103312492371,0.6619680523872375,0.013505990616977215,0.7494103312492371,0.6619680523872375,0.04049549251794815,0.7494103312492371,0.6608662605285645,0.06741759181022644,0.7494103312492371,0.6586645841598511,0.09422748535871506,0.7494103312492371,0.6553665399551392,0.12088054418563843,0.7494103312492371,0.6509777307510376,0.14733241498470306,0.7494103312492371,0.6455054879188538,0.17353905737400055,0.7494103312492371,0.6389588117599487,0.1994568556547165,0.7494103312492371,0.6313486099243164,0.2250426858663559,0.7494103312492371,0.6226876378059387,0.25025394558906555,0.7494103312492371,0.6129902601242065,0.27504870295524597,0.7494103312492371,0.602272629737854,0.299385666847229,0.7494103312492371,0.590552568435669,0.3232243061065674,0.7494103312492371,0.5778496265411377,0.3465249836444855,0.7494103312492371,0.564184844493866,0.3692489266395569,0.7494103312492371,0.5495811104774475,0.39135825634002686,0.7494103312492371,0.5340625643730164,0.41281622648239136,0.7494103312492371,0.5176551938056946,0.43358710408210754,0.7494103312492371,0.5003861784934998,0.4536363184452057,0.7494103312492371,0.4822843670845032,0.47293052077293396,0.7494103312492371,0.463379830121994,0.49143755435943604,0.7494103312492371,0.4437040388584137,0.509126603603363,0.7494103312492371,0.4232897460460663,0.5259683132171631,0.7494103312492371,0.40217092633247375,0.541934609413147,0.7494103312492371,0.3803827464580536,0.5569988489151001,0.7494103312492371,0.3579614460468292,0.5711360573768616,0.7494103312492371,0.33494433760643005,0.5843226909637451,0.7494103312492371,0.31136977672576904,0.5965367555618286,0.7494103312492371,0.287276953458786,0.6077579259872437,0.7494103312492371,0.2627059817314148,0.6179675459861755,0.7494103312492371,0.2376977801322937,0.6271486282348633,0.7494103312492371,0.21229395270347595,0.6352859139442444,0.7494103312492371,0.1865367740392685,0.6423657536506653,0.7494103312492371,0.16046911478042603,0.6483765244483948,0.7494103312492371,0.13413438200950623,0.653308093547821,0.7494103312492371,0.1075764000415802,0.6571522951126099,0.7494103312492371,0.0808393582701683,0.6599027514457703,0.7494103312492371,0.05396777391433716,0.6615548133850098,0.7494103312492371,0.0270063616335392,0.6621058583259583,0.7494103312492371,1.621691561377151e-16,0.6075938940048218,0.7942478656768799,0.0,0.6070882678031921,0.7942478656768799,-0.024782896041870117,0.6055722236633301,0.7942478656768799,-0.049524545669555664,0.6030482053756714,0.7942478656768799,-0.07418376207351685,0.5995205044746399,0.7942478656768799,-0.0987195149064064,0.5949949622154236,0.7942478656768799,-0.1230909526348114,0.5894790887832642,0.7942478656768799,-0.14725752174854279,0.5829821228981018,0.7942478656768799,-0.17117898166179657,0.5755147933959961,0.7942478656768799,-0.19481554627418518,0.5670896172523499,0.7942478656768799,-0.21812786161899567,0.5577205419540405,0.7942478656768799,-0.24107711017131805,0.5474231839179993,0.7942478656768799,-0.2636251151561737,0.5362147688865662,0.7942478656768799,-0.2857343554496765,0.5241138339042664,0.7942478656768799,-0.3073680102825165,0.5111405253410339,0.7942478656768799,-0.32849007844924927,0.49731650948524475,0.7942478656768799,-0.3490653932094574,0.4826647639274597,0.7942478656768799,-0.3690597414970398,0.46720966696739197,0.7942478656768799,-0.3884398341178894,0.4509769380092621,0.7942478656768799,-0.40717339515686035,0.4339936077594757,0.7942478656768799,-0.4252292513847351,0.41628792881965637,0.7942478656768799,-0.4425773620605469,0.3978894054889679,0.7942478656768799,-0.459188848733902,0.37882861495018005,0.7942478656768799,-0.47503605484962463,0.35913729667663574,0.7942478656768799,-0.49009260535240173,0.3388482332229614,0.7942478656768799,-0.5043334364891052,0.31799519062042236,0.7942478656768799,-0.5177348852157593,0.2966128885746002,0.7942478656768799,-0.5302746295928955,0.2747369110584259,0.7942478656768799,-0.5419317483901978,0.2524036467075348,0.7942478656768799,-0.552686870098114,0.22965027391910553,0.7942478656768799,-0.5625221133232117,0.20651467144489288,0.7942478656768799,-0.5714210867881775,0.18303535878658295,0.7942478656768799,-0.5793690085411072,0.1592513918876648,0.7942478656768799,-0.5863525867462158,0.13520236313343048,0.7942478656768799,-0.592360258102417,0.11092831194400787,0.7942478656768799,-0.5973820090293884,0.0864696279168129,0.7942478656768799,-0.6014094948768616,0.06186702847480774,0.7942478656768799,-0.6044359803199768,0.0371614545583725,0.7942478656768799,-0.6064563989639282,0.012394026853144169,0.7942478656768799,-0.6074674725532532,-0.012394026853144169,0.7942478656768799,-0.6074674725532532,-0.0371614545583725,0.7942478656768799,-0.6064563989639282,-0.06186702847480774,0.7942478656768799,-0.6044359803199768,-0.0864696279168129,0.7942478656768799,-0.6014094948768616,-0.11092831194400787,0.7942478656768799,-0.5973820090293884,-0.13520236313343048,0.7942478656768799,-0.592360258102417,-0.1592513918876648,0.7942478656768799,-0.5863525867462158,-0.18303535878658295,0.7942478656768799,-0.5793690085411072,-0.20651467144489288,0.7942478656768799,-0.5714210867881775,-0.22965027391910553,0.7942478656768799,-0.5625221133232117,-0.2524036467075348,0.7942478656768799,-0.552686870098114,-0.2747369110584259,0.7942478656768799,-0.5419317483901978,-0.2966128885746002,0.7942478656768799,-0.5302746295928955,-0.31799519062042236,0.7942478656768799,-0.5177348852157593,-0.3388482332229614,0.7942478656768799,-0.5043334364891052,-0.35913729667663574,0.7942478656768799,-0.49009260535240173,-0.37882861495018005,0.7942478656768799,-0.47503605484962463,-0.3978894054889679,0.7942478656768799,-0.459188848733902,-0.41628792881965637,0.7942478656768799,-0.4425773620605469,-0.4339936077594757,0.7942478656768799,-0.4252292513847351,-0.4509769380092621,0.7942478656768799,-0.40717339515686035,-0.46720966696739197,0.7942478656768799,-0.3884398341178894,-0.4826647639274597,0.7942478656768799,-0.3690597414970398,-0.49731650948524475,0.7942478656768799,-0.3490653932094574,-0.5111405253410339,0.7942478656768799,-0.32849007844924927,-0.5241138339042664,0.7942478656768799,-0.3073680102825165,-0.5362147688865662,0.7942478656768799,-0.2857343554496765,-0.5474231839179993,0.7942478656768799,-0.2636251151561737,-0.5577205419540405,0.7942478656768799,-0.24107711017131805,-0.5670896172523499,0.7942478656768799,-0.21812786161899567,-0.5755147933959961,0.7942478656768799,-0.19481554627418518,-0.5829821228981018,0.7942478656768799,-0.17117898166179657,-0.5894790887832642,0.7942478656768799,-0.14725752174854279,-0.5949949622154236,0.7942478656768799,-0.1230909526348114,-0.5995205044746399,0.7942478656768799,-0.0987195149064064,-0.6030482053756714,0.7942478656768799,-0.07418376207351685,-0.6055722236633301,0.7942478656768799,-0.049524545669555664,-0.6070882678031921,0.7942478656768799,-0.024782896041870117,-0.6075938940048218,0.7942478656768799,-7.440879377419119e-17,-0.6070882678031921,0.7942478656768799,0.024782896041870117,-0.6055722236633301,0.7942478656768799,0.049524545669555664,-0.6030482053756714,0.7942478656768799,0.07418376207351685,-0.5995205044746399,0.7942478656768799,0.0987195149064064,-0.5949949622154236,0.7942478656768799,0.1230909526348114,-0.5894790887832642,0.7942478656768799,0.14725752174854279,-0.5829821228981018,0.7942478656768799,0.17117898166179657,-0.5755147933959961,0.7942478656768799,0.19481554627418518,-0.5670896172523499,0.7942478656768799,0.21812786161899567,-0.5577205419540405,0.7942478656768799,0.24107711017131805,-0.5474231839179993,0.7942478656768799,0.2636251151561737,-0.5362147688865662,0.7942478656768799,0.2857343554496765,-0.5241138339042664,0.7942478656768799,0.3073680102825165,-0.5111405253410339,0.7942478656768799,0.32849007844924927,-0.49731650948524475,0.7942478656768799,0.3490653932094574,-0.4826647639274597,0.7942478656768799,0.3690597414970398,-0.46720966696739197,0.7942478656768799,0.3884398341178894,-0.4509769380092621,0.7942478656768799,0.40717339515686035,-0.4339936077594757,0.7942478656768799,0.4252292513847351,-0.41628792881965637,0.7942478656768799,0.4425773620605469,-0.3978894054889679,0.7942478656768799,0.459188848733902,-0.37882861495018005,0.7942478656768799,0.47503605484962463,-0.35913729667663574,0.7942478656768799,0.49009260535240173,-0.3388482332229614,0.7942478656768799,0.5043334364891052,-0.31799519062042236,0.7942478656768799,0.5177348852157593,-0.2966128885746002,0.7942478656768799,0.5302746295928955,-0.2747369110584259,0.7942478656768799,0.5419317483901978,-0.2524036467075348,0.7942478656768799,0.552686870098114,-0.22965027391910553,0.7942478656768799,0.5625221133232117,-0.20651467144489288,0.7942478656768799,0.5714210867881775,-0.18303535878658295,0.7942478656768799,0.5793690085411072,-0.1592513918876648,0.7942478656768799,0.5863525867462158,-0.13520236313343048,0.7942478656768799,0.592360258102417,-0.11092831194400787,0.7942478656768799,0.5973820090293884,-0.0864696279168129,0.7942478656768799,0.6014094948768616,-0.06186702847480774,0.7942478656768799,0.6044359803199768,-0.0371614545583725,0.7942478656768799,0.6064563989639282,-0.012394026853144169,0.7942478656768799,0.6074674725532532,0.012394026853144169,0.7942478656768799,0.6074674725532532,0.0371614545583725,0.7942478656768799,0.6064563989639282,0.06186702847480774,0.7942478656768799,0.6044359803199768,0.0864696279168129,0.7942478656768799,0.6014094948768616,0.11092831194400787,0.7942478656768799,0.5973820090293884,0.13520236313343048,0.7942478656768799,0.592360258102417,0.1592513918876648,0.7942478656768799,0.5863525867462158,0.18303535878658295,0.7942478656768799,0.5793690085411072,0.20651467144489288,0.7942478656768799,0.5714210867881775,0.22965027391910553,0.7942478656768799,0.5625221133232117,0.2524036467075348,0.7942478656768799,0.552686870098114,0.2747369110584259,0.7942478656768799,0.5419317483901978,0.2966128885746002,0.7942478656768799,0.5302746295928955,0.31799519062042236,0.7942478656768799,0.5177348852157593,0.3388482332229614,0.7942478656768799,0.5043334364891052,0.35913729667663574,0.7942478656768799,0.49009260535240173,0.37882861495018005,0.7942478656768799,0.47503605484962463,0.3978894054889679,0.7942478656768799,0.459188848733902,0.41628792881965637,0.7942478656768799,0.4425773620605469,0.4339936077594757,0.7942478656768799,0.4252292513847351,0.4509769380092621,0.7942478656768799,0.40717339515686035,0.46720966696739197,0.7942478656768799,0.3884398341178894,0.4826647639274597,0.7942478656768799,0.3690597414970398,0.49731650948524475,0.7942478656768799,0.3490653932094574,0.5111405253410339,0.7942478656768799,0.32849007844924927,0.5241138339042664,0.7942478656768799,0.3073680102825165,0.5362147688865662,0.7942478656768799,0.2857343554496765,0.5474231839179993,0.7942478656768799,0.2636251151561737,0.5577205419540405,0.7942478656768799,0.24107711017131805,0.5670896172523499,0.7942478656768799,0.21812786161899567,0.5755147933959961,0.7942478656768799,0.19481554627418518,0.5829821228981018,0.7942478656768799,0.17117898166179657,0.5894790887832642,0.7942478656768799,0.14725752174854279,0.5949949622154236,0.7942478656768799,0.1230909526348114,0.5995205044746399,0.7942478656768799,0.0987195149064064,0.6030482053756714,0.7942478656768799,0.07418376207351685,0.6055722236633301,0.7942478656768799,0.049524545669555664,0.6070882678031921,0.7942478656768799,0.024782896041870117,0.6075938940048218,0.7942478656768799,1.4881758754838238e-16,0.5500550270080566,0.8351284265518188,0.0,0.5495972633361816,0.8351284265518188,-0.02243596501648426,0.5482247471809387,0.8351284265518188,-0.04483458772301674,0.5459397435188293,0.8351284265518188,-0.06715858727693558,0.5427461266517639,0.8351284265518188,-0.08937080949544907,0.5386491417884827,0.8351284265518188,-0.11143428087234497,0.5336556434631348,0.8351284265518188,-0.13331228494644165,0.527773916721344,0.8351284265518188,-0.15496839582920074,0.5210137367248535,0.8351284265518188,-0.17636658251285553,0.5133864283561707,0.8351284265518188,-0.1974712312221527,0.5049046277999878,0.8351284265518188,-0.2182472050189972,0.4955824315547943,0.8351284265518188,-0.23865991830825806,0.4854353964328766,0.8351284265518188,-0.2586754262447357,0.47448042035102844,0.8351284265518188,-0.27826038002967834,0.46273571252822876,0.8351284265518188,-0.2973821759223938,0.45022082328796387,0.8351284265518188,-0.3160090446472168,0.43695658445358276,0.8351284265518188,-0.33410993218421936,0.42296507954597473,0.8351284265518188,-0.3516547381877899,0.40826958417892456,0.8351284265518188,-0.36861422657966614,0.39289456605911255,0.8351284265518188,-0.38496020436286926,0.3768656253814697,0.8351284265518188,-0.4006654620170593,0.3602094352245331,0.8351284265518188,-0.41570383310317993,0.3429536819458008,0.8351284265518188,-0.4300503134727478,0.32512712478637695,0.8351284265518188,-0.4436810314655304,0.30675944685935974,0.8351284265518188,-0.4565732777118683,0.28788116574287415,0.8351284265518188,-0.46870559453964233,0.2685237526893616,0.8351284265518188,-0.48005780577659607,0.24871942400932312,0.8351284265518188,-0.4906110167503357,0.2285011112689972,0.8351284265518188,-0.5003476142883301,0.2079024761915207,0.8351284265518188,-0.5092514753341675,0.18695780634880066,0.8351284265518188,-0.517307698726654,0.1657019704580307,0.8351284265518188,-0.5245029926300049,0.144170343875885,0.8351284265518188,-0.5308251976966858,0.1223987564444542,0.8351284265518188,-0.5362640023231506,0.10042344033718109,0.8351284265518188,-0.5408101677894592,0.0782809853553772,0.8351284265518188,-0.5444562435150146,0.056008242070674896,0.8351284265518188,-0.547196090221405,0.033642277121543884,0.8351284265518188,-0.5490252375602722,0.011220317333936691,0.8351284265518188,-0.5499405264854431,-0.011220317333936691,0.8351284265518188,-0.5499405264854431,-0.033642277121543884,0.8351284265518188,-0.5490252375602722,-0.056008242070674896,0.8351284265518188,-0.547196090221405,-0.0782809853553772,0.8351284265518188,-0.5444562435150146,-0.10042344033718109,0.8351284265518188,-0.5408101677894592,-0.1223987564444542,0.8351284265518188,-0.5362640023231506,-0.144170343875885,0.8351284265518188,-0.5308251976966858,-0.1657019704580307,0.8351284265518188,-0.5245029926300049,-0.18695780634880066,0.8351284265518188,-0.517307698726654,-0.2079024761915207,0.8351284265518188,-0.5092514753341675,-0.2285011112689972,0.8351284265518188,-0.5003476142883301,-0.24871942400932312,0.8351284265518188,-0.4906110167503357,-0.2685237526893616,0.8351284265518188,-0.48005780577659607,-0.28788116574287415,0.8351284265518188,-0.46870559453964233,-0.30675944685935974,0.8351284265518188,-0.4565732777118683,-0.32512712478637695,0.8351284265518188,-0.4436810314655304,-0.3429536819458008,0.8351284265518188,-0.4300503134727478,-0.3602094352245331,0.8351284265518188,-0.41570383310317993,-0.3768656253814697,0.8351284265518188,-0.4006654620170593,-0.39289456605911255,0.8351284265518188,-0.38496020436286926,-0.40826958417892456,0.8351284265518188,-0.36861422657966614,-0.42296507954597473,0.8351284265518188,-0.3516547381877899,-0.43695658445358276,0.8351284265518188,-0.33410993218421936,-0.45022082328796387,0.8351284265518188,-0.3160090446472168,-0.46273571252822876,0.8351284265518188,-0.2973821759223938,-0.47448042035102844,0.8351284265518188,-0.27826038002967834,-0.4854353964328766,0.8351284265518188,-0.2586754262447357,-0.4955824315547943,0.8351284265518188,-0.23865991830825806,-0.5049046277999878,0.8351284265518188,-0.2182472050189972,-0.5133864283561707,0.8351284265518188,-0.1974712312221527,-0.5210137367248535,0.8351284265518188,-0.17636658251285553,-0.527773916721344,0.8351284265518188,-0.15496839582920074,-0.5336556434631348,0.8351284265518188,-0.13331228494644165,-0.5386491417884827,0.8351284265518188,-0.11143428087234497,-0.5427461266517639,0.8351284265518188,-0.08937080949544907,-0.5459397435188293,0.8351284265518188,-0.06715858727693558,-0.5482247471809387,0.8351284265518188,-0.04483458772301674,-0.5495972633361816,0.8351284265518188,-0.02243596501648426,-0.5500550270080566,0.8351284265518188,-6.736230683364796e-17,-0.5495972633361816,0.8351284265518188,0.02243596501648426,-0.5482247471809387,0.8351284265518188,0.04483458772301674,-0.5459397435188293,0.8351284265518188,0.06715858727693558,-0.5427461266517639,0.8351284265518188,0.08937080949544907,-0.5386491417884827,0.8351284265518188,0.11143428087234497,-0.5336556434631348,0.8351284265518188,0.13331228494644165,-0.527773916721344,0.8351284265518188,0.15496839582920074,-0.5210137367248535,0.8351284265518188,0.17636658251285553,-0.5133864283561707,0.8351284265518188,0.1974712312221527,-0.5049046277999878,0.8351284265518188,0.2182472050189972,-0.4955824315547943,0.8351284265518188,0.23865991830825806,-0.4854353964328766,0.8351284265518188,0.2586754262447357,-0.47448042035102844,0.8351284265518188,0.27826038002967834,-0.46273571252822876,0.8351284265518188,0.2973821759223938,-0.45022082328796387,0.8351284265518188,0.3160090446472168,-0.43695658445358276,0.8351284265518188,0.33410993218421936,-0.42296507954597473,0.8351284265518188,0.3516547381877899,-0.40826958417892456,0.8351284265518188,0.36861422657966614,-0.39289456605911255,0.8351284265518188,0.38496020436286926,-0.3768656253814697,0.8351284265518188,0.4006654620170593,-0.3602094352245331,0.8351284265518188,0.41570383310317993,-0.3429536819458008,0.8351284265518188,0.4300503134727478,-0.32512712478637695,0.8351284265518188,0.4436810314655304,-0.30675944685935974,0.8351284265518188,0.4565732777118683,-0.28788116574287415,0.8351284265518188,0.46870559453964233,-0.2685237526893616,0.8351284265518188,0.48005780577659607,-0.24871942400932312,0.8351284265518188,0.4906110167503357,-0.2285011112689972,0.8351284265518188,0.5003476142883301,-0.2079024761915207,0.8351284265518188,0.5092514753341675,-0.18695780634880066,0.8351284265518188,0.517307698726654,-0.1657019704580307,0.8351284265518188,0.5245029926300049,-0.144170343875885,0.8351284265518188,0.5308251976966858,-0.1223987564444542,0.8351284265518188,0.5362640023231506,-0.10042344033718109,0.8351284265518188,0.5408101677894592,-0.0782809853553772,0.8351284265518188,0.5444562435150146,-0.056008242070674896,0.8351284265518188,0.547196090221405,-0.033642277121543884,0.8351284265518188,0.5490252375602722,-0.011220317333936691,0.8351284265518188,0.5499405264854431,0.011220317333936691,0.8351284265518188,0.5499405264854431,0.033642277121543884,0.8351284265518188,0.5490252375602722,0.056008242070674896,0.8351284265518188,0.547196090221405,0.0782809853553772,0.8351284265518188,0.5444562435150146,0.10042344033718109,0.8351284265518188,0.5408101677894592,0.1223987564444542,0.8351284265518188,0.5362640023231506,0.144170343875885,0.8351284265518188,0.5308251976966858,0.1657019704580307,0.8351284265518188,0.5245029926300049,0.18695780634880066,0.8351284265518188,0.517307698726654,0.2079024761915207,0.8351284265518188,0.5092514753341675,0.2285011112689972,0.8351284265518188,0.5003476142883301,0.24871942400932312,0.8351284265518188,0.4906110167503357,0.2685237526893616,0.8351284265518188,0.48005780577659607,0.28788116574287415,0.8351284265518188,0.46870559453964233,0.30675944685935974,0.8351284265518188,0.4565732777118683,0.32512712478637695,0.8351284265518188,0.4436810314655304,0.3429536819458008,0.8351284265518188,0.4300503134727478,0.3602094352245331,0.8351284265518188,0.41570383310317993,0.3768656253814697,0.8351284265518188,0.4006654620170593,0.39289456605911255,0.8351284265518188,0.38496020436286926,0.40826958417892456,0.8351284265518188,0.36861422657966614,0.42296507954597473,0.8351284265518188,0.3516547381877899,0.43695658445358276,0.8351284265518188,0.33410993218421936,0.45022082328796387,0.8351284265518188,0.3160090446472168,0.46273571252822876,0.8351284265518188,0.2973821759223938,0.47448042035102844,0.8351284265518188,0.27826038002967834,0.4854353964328766,0.8351284265518188,0.2586754262447357,0.4955824315547943,0.8351284265518188,0.23865991830825806,0.5049046277999878,0.8351284265518188,0.2182472050189972,0.5133864283561707,0.8351284265518188,0.1974712312221527,0.5210137367248535,0.8351284265518188,0.17636658251285553,0.527773916721344,0.8351284265518188,0.15496839582920074,0.5336556434631348,0.8351284265518188,0.13331228494644165,0.5386491417884827,0.8351284265518188,0.11143428087234497,0.5427461266517639,0.8351284265518188,0.08937080949544907,0.5459397435188293,0.8351284265518188,0.06715858727693558,0.5482247471809387,0.8351284265518188,0.04483458772301674,0.5495972633361816,0.8351284265518188,0.02243596501648426,0.5500550270080566,0.8351284265518188,1.3472461366729593e-16,0.48977574706077576,0.871848464012146,0.0,0.48936814069747925,0.871848464012146,-0.019977260380983353,0.4881460666656494,0.871848464012146,-0.039921268820762634,0.48611149191856384,0.871848464012146,-0.05979883298277855,0.4832678437232971,0.871848464012146,-0.07957687228918076,0.4796198308467865,0.871848464012146,-0.09922245889902115,0.4751735329627991,0.871848464012146,-0.11870289593935013,0.4699363708496094,0.871848464012146,-0.13798576593399048,0.4639170467853546,0.871848464012146,-0.15703898668289185,0.45712557435035706,0.871848464012146,-0.17583081126213074,0.449573278427124,0.871848464012146,-0.19432999193668365,0.44127270579338074,0.871848464012146,-0.21250572800636292,0.4322376549243927,0.871848464012146,-0.230327770113945,0.4224832057952881,0.871848464012146,-0.24776645004749298,0.4120255708694458,0.871848464012146,-0.2647927701473236,0.40088215470314026,0.871848464012146,-0.2813783288002014,0.389071524143219,0.871848464012146,-0.29749560356140137,0.3766133189201355,0.871848464012146,-0.3131176829338074,0.3635282814502716,0.871848464012146,-0.32821863889694214,0.34983816742897034,0.871848464012146,-0.34277328848838806,0.33556580543518066,0.871848464012146,-0.35675743222236633,0.3207348883152008,0.871848464012146,-0.37014779448509216,0.30537018179893494,0.871848464012146,-0.38292208313941956,0.2894971966743469,0.871848464012146,-0.3950590491294861,0.27314236760139465,0.871848464012146,-0.4065384566783905,0.2563329339027405,0.871848464012146,-0.4173412322998047,0.23909686505794525,0.871848464012146,-0.42744937539100647,0.22146283090114594,0.871848464012146,-0.4368460774421692,0.2034602016210556,0.871848464012146,-0.4455156922340393,0.1851189136505127,0.871848464012146,-0.4534437656402588,0.1664695292711258,0.871848464012146,-0.46061715483665466,0.1475430727005005,0.871848464012146,-0.4670238792896271,0.12837104499340057,0.871848464012146,-0.4726532995700836,0.10898535698652267,0.871848464012146,-0.47749602794647217,0.08941826969385147,0.871848464012146,-0.48154401779174805,0.06970235705375671,0.871848464012146,-0.48479053378105164,0.04987042769789696,0.871848464012146,-0.4872301518917084,0.02995549701154232,0.871848464012146,-0.48885881900787354,0.009990708902478218,0.871848464012146,-0.48967382311820984,-0.009990708902478218,0.871848464012146,-0.48967382311820984,-0.02995549701154232,0.871848464012146,-0.48885881900787354,-0.04987042769789696,0.871848464012146,-0.4872301518917084,-0.06970235705375671,0.871848464012146,-0.48479053378105164,-0.08941826969385147,0.871848464012146,-0.48154401779174805,-0.10898535698652267,0.871848464012146,-0.47749602794647217,-0.12837104499340057,0.871848464012146,-0.4726532995700836,-0.1475430727005005,0.871848464012146,-0.4670238792896271,-0.1664695292711258,0.871848464012146,-0.46061715483665466,-0.1851189136505127,0.871848464012146,-0.4534437656402588,-0.2034602016210556,0.871848464012146,-0.4455156922340393,-0.22146283090114594,0.871848464012146,-0.4368460774421692,-0.23909686505794525,0.871848464012146,-0.42744937539100647,-0.2563329339027405,0.871848464012146,-0.4173412322998047,-0.27314236760139465,0.871848464012146,-0.4065384566783905,-0.2894971966743469,0.871848464012146,-0.3950590491294861,-0.30537018179893494,0.871848464012146,-0.38292208313941956,-0.3207348883152008,0.871848464012146,-0.37014779448509216,-0.33556580543518066,0.871848464012146,-0.35675743222236633,-0.34983816742897034,0.871848464012146,-0.34277328848838806,-0.3635282814502716,0.871848464012146,-0.32821863889694214,-0.3766133189201355,0.871848464012146,-0.3131176829338074,-0.389071524143219,0.871848464012146,-0.29749560356140137,-0.40088215470314026,0.871848464012146,-0.2813783288002014,-0.4120255708694458,0.871848464012146,-0.2647927701473236,-0.4224832057952881,0.871848464012146,-0.24776645004749298,-0.4322376549243927,0.871848464012146,-0.230327770113945,-0.44127270579338074,0.871848464012146,-0.21250572800636292,-0.449573278427124,0.871848464012146,-0.19432999193668365,-0.45712557435035706,0.871848464012146,-0.17583081126213074,-0.4639170467853546,0.871848464012146,-0.15703898668289185,-0.4699363708496094,0.871848464012146,-0.13798576593399048,-0.4751735329627991,0.871848464012146,-0.11870289593935013,-0.4796198308467865,0.871848464012146,-0.09922245889902115,-0.4832678437232971,0.871848464012146,-0.07957687228918076,-0.48611149191856384,0.871848464012146,-0.05979883298277855,-0.4881460666656494,0.871848464012146,-0.039921268820762634,-0.48936814069747925,0.871848464012146,-0.019977260380983353,-0.48977574706077576,0.871848464012146,-5.998022940986952e-17,-0.48936814069747925,0.871848464012146,0.019977260380983353,-0.4881460666656494,0.871848464012146,0.039921268820762634,-0.48611149191856384,0.871848464012146,0.05979883298277855,-0.4832678437232971,0.871848464012146,0.07957687228918076,-0.4796198308467865,0.871848464012146,0.09922245889902115,-0.4751735329627991,0.871848464012146,0.11870289593935013,-0.4699363708496094,0.871848464012146,0.13798576593399048,-0.4639170467853546,0.871848464012146,0.15703898668289185,-0.45712557435035706,0.871848464012146,0.17583081126213074,-0.449573278427124,0.871848464012146,0.19432999193668365,-0.44127270579338074,0.871848464012146,0.21250572800636292,-0.4322376549243927,0.871848464012146,0.230327770113945,-0.4224832057952881,0.871848464012146,0.24776645004749298,-0.4120255708694458,0.871848464012146,0.2647927701473236,-0.40088215470314026,0.871848464012146,0.2813783288002014,-0.389071524143219,0.871848464012146,0.29749560356140137,-0.3766133189201355,0.871848464012146,0.3131176829338074,-0.3635282814502716,0.871848464012146,0.32821863889694214,-0.34983816742897034,0.871848464012146,0.34277328848838806,-0.33556580543518066,0.871848464012146,0.35675743222236633,-0.3207348883152008,0.871848464012146,0.37014779448509216,-0.30537018179893494,0.871848464012146,0.38292208313941956,-0.2894971966743469,0.871848464012146,0.3950590491294861,-0.27314236760139465,0.871848464012146,0.4065384566783905,-0.2563329339027405,0.871848464012146,0.4173412322998047,-0.23909686505794525,0.871848464012146,0.42744937539100647,-0.22146283090114594,0.871848464012146,0.4368460774421692,-0.2034602016210556,0.871848464012146,0.4455156922340393,-0.1851189136505127,0.871848464012146,0.4534437656402588,-0.1664695292711258,0.871848464012146,0.46061715483665466,-0.1475430727005005,0.871848464012146,0.4670238792896271,-0.12837104499340057,0.871848464012146,0.4726532995700836,-0.10898535698652267,0.871848464012146,0.47749602794647217,-0.08941826969385147,0.871848464012146,0.48154401779174805,-0.06970235705375671,0.871848464012146,0.48479053378105164,-0.04987042769789696,0.871848464012146,0.4872301518917084,-0.02995549701154232,0.871848464012146,0.48885881900787354,-0.009990708902478218,0.871848464012146,0.48967382311820984,0.009990708902478218,0.871848464012146,0.48967382311820984,0.02995549701154232,0.871848464012146,0.48885881900787354,0.04987042769789696,0.871848464012146,0.4872301518917084,0.06970235705375671,0.871848464012146,0.48479053378105164,0.08941826969385147,0.871848464012146,0.48154401779174805,0.10898535698652267,0.871848464012146,0.47749602794647217,0.12837104499340057,0.871848464012146,0.4726532995700836,0.1475430727005005,0.871848464012146,0.4670238792896271,0.1664695292711258,0.871848464012146,0.46061715483665466,0.1851189136505127,0.871848464012146,0.4534437656402588,0.2034602016210556,0.871848464012146,0.4455156922340393,0.22146283090114594,0.871848464012146,0.4368460774421692,0.23909686505794525,0.871848464012146,0.42744937539100647,0.2563329339027405,0.871848464012146,0.4173412322998047,0.27314236760139465,0.871848464012146,0.4065384566783905,0.2894971966743469,0.871848464012146,0.3950590491294861,0.30537018179893494,0.871848464012146,0.38292208313941956,0.3207348883152008,0.871848464012146,0.37014779448509216,0.33556580543518066,0.871848464012146,0.35675743222236633,0.34983816742897034,0.871848464012146,0.34277328848838806,0.3635282814502716,0.871848464012146,0.32821863889694214,0.3766133189201355,0.871848464012146,0.3131176829338074,0.389071524143219,0.871848464012146,0.29749560356140137,0.40088215470314026,0.871848464012146,0.2813783288002014,0.4120255708694458,0.871848464012146,0.2647927701473236,0.4224832057952881,0.871848464012146,0.24776645004749298,0.4322376549243927,0.871848464012146,0.230327770113945,0.44127270579338074,0.871848464012146,0.21250572800636292,0.449573278427124,0.871848464012146,0.19432999193668365,0.45712557435035706,0.871848464012146,0.17583081126213074,0.4639170467853546,0.871848464012146,0.15703898668289185,0.4699363708496094,0.871848464012146,0.13798576593399048,0.4751735329627991,0.871848464012146,0.11870289593935013,0.4796198308467865,0.871848464012146,0.09922245889902115,0.4832678437232971,0.871848464012146,0.07957687228918076,0.48611149191856384,0.871848464012146,0.05979883298277855,0.4881460666656494,0.871848464012146,0.039921268820762634,0.48936814069747925,0.871848464012146,0.019977260380983353,0.48977574706077576,0.871848464012146,1.1996045881973905e-16,0.4270564317703247,0.9042249917984009,0.0,0.42670103907585144,0.9042249917984009,-0.017419029027223587,0.4256354570388794,0.9042249917984009,-0.034809064120054245,0.42386141419410706,0.9042249917984009,-0.05214116349816322,0.4213819205760956,0.9042249917984009,-0.06938648223876953,0.4182010591030121,0.9042249917984009,-0.08651630580425262,0.41432416439056396,0.9042249917984009,-0.10350213944911957,0.4097576439380646,0.9042249917984009,-0.12031570076942444,0.40450915694236755,0.9042249917984009,-0.1369290053844452,0.3985873758792877,0.9042249917984009,-0.15331441164016724,0.3920021951198578,0.9042249917984009,-0.16944463551044464,0.38476455211639404,0.9042249917984009,-0.18529284000396729,0.3768865168094635,0.9042249917984009,-0.20083265006542206,0.36838120222091675,0.9042249917984009,-0.21603818237781525,0.35926273465156555,0.9042249917984009,-0.2308841347694397,0.34954631328582764,0.9042249917984009,-0.2453458160161972,0.3392481207847595,0.9042249917984009,-0.2593991458415985,0.32838526368141174,0.9042249917984009,-0.2730207145214081,0.316975861787796,0.9042249917984009,-0.28618788719177246,0.3050388693809509,0.9042249917984009,-0.2988787293434143,0.29259419441223145,0.9042249917984009,-0.31107208132743835,0.27966248989105225,0.9042249917984009,-0.32274770736694336,0.266265332698822,0.9042249917984009,-0.33388617634773254,0.25242501497268677,0.9042249917984009,-0.344468891620636,0.2381645292043686,0.9042249917984009,-0.35447829961776733,0.22350767254829407,0.9042249917984009,-0.3638976812362671,0.20847879350185394,0.9042249917984009,-0.3727114200592041,0.19310292601585388,0.9042249917984009,-0.38090479373931885,0.17740565538406372,0.9042249917984009,-0.38846421241760254,0.16141311824321747,0.9042249917984009,-0.3953770399093628,0.14515191316604614,0.9042249917984009,-0.40163183212280273,0.12864913046360016,0.9042249917984009,-0.4072181284427643,0.11193221062421799,0.9042249917984009,-0.41212666034698486,0.09502899646759033,0.9042249917984009,-0.41634923219680786,0.07796761393547058,0.9042249917984009,-0.41987884044647217,0.060776468366384506,0.9042249917984009,-0.4227096140384674,0.0434841625392437,0.9042249917984009,-0.42483681440353394,0.026119481772184372,0.9042249917984009,-0.42625692486763,0.008711326867341995,0.9042249917984009,-0.426967591047287,-0.008711326867341995,0.9042249917984009,-0.426967591047287,-0.026119481772184372,0.9042249917984009,-0.42625692486763,-0.0434841625392437,0.9042249917984009,-0.42483681440353394,-0.060776468366384506,0.9042249917984009,-0.4227096140384674,-0.07796761393547058,0.9042249917984009,-0.41987884044647217,-0.09502899646759033,0.9042249917984009,-0.41634923219680786,-0.11193221062421799,0.9042249917984009,-0.41212666034698486,-0.12864913046360016,0.9042249917984009,-0.4072181284427643,-0.14515191316604614,0.9042249917984009,-0.40163183212280273,-0.16141311824321747,0.9042249917984009,-0.3953770399093628,-0.17740565538406372,0.9042249917984009,-0.38846421241760254,-0.19310292601585388,0.9042249917984009,-0.38090479373931885,-0.20847879350185394,0.9042249917984009,-0.3727114200592041,-0.22350767254829407,0.9042249917984009,-0.3638976812362671,-0.2381645292043686,0.9042249917984009,-0.35447829961776733,-0.25242501497268677,0.9042249917984009,-0.344468891620636,-0.266265332698822,0.9042249917984009,-0.33388617634773254,-0.27966248989105225,0.9042249917984009,-0.32274770736694336,-0.29259419441223145,0.9042249917984009,-0.31107208132743835,-0.3050388693809509,0.9042249917984009,-0.2988787293434143,-0.316975861787796,0.9042249917984009,-0.28618788719177246,-0.32838526368141174,0.9042249917984009,-0.2730207145214081,-0.3392481207847595,0.9042249917984009,-0.2593991458415985,-0.34954631328582764,0.9042249917984009,-0.2453458160161972,-0.35926273465156555,0.9042249917984009,-0.2308841347694397,-0.36838120222091675,0.9042249917984009,-0.21603818237781525,-0.3768865168094635,0.9042249917984009,-0.20083265006542206,-0.38476455211639404,0.9042249917984009,-0.18529284000396729,-0.3920021951198578,0.9042249917984009,-0.16944463551044464,-0.3985873758792877,0.9042249917984009,-0.15331441164016724,-0.40450915694236755,0.9042249917984009,-0.1369290053844452,-0.4097576439380646,0.9042249917984009,-0.12031570076942444,-0.41432416439056396,0.9042249917984009,-0.10350213944911957,-0.4182010591030121,0.9042249917984009,-0.08651630580425262,-0.4213819205760956,0.9042249917984009,-0.06938648223876953,-0.42386141419410706,0.9042249917984009,-0.05214116349816322,-0.4256354570388794,0.9042249917984009,-0.034809064120054245,-0.42670103907585144,0.9042249917984009,-0.017419029027223587,-0.4270564317703247,0.9042249917984009,-5.229933133544508e-17,-0.42670103907585144,0.9042249917984009,0.017419029027223587,-0.4256354570388794,0.9042249917984009,0.034809064120054245,-0.42386141419410706,0.9042249917984009,0.05214116349816322,-0.4213819205760956,0.9042249917984009,0.06938648223876953,-0.4182010591030121,0.9042249917984009,0.08651630580425262,-0.41432416439056396,0.9042249917984009,0.10350213944911957,-0.4097576439380646,0.9042249917984009,0.12031570076942444,-0.40450915694236755,0.9042249917984009,0.1369290053844452,-0.3985873758792877,0.9042249917984009,0.15331441164016724,-0.3920021951198578,0.9042249917984009,0.16944463551044464,-0.38476455211639404,0.9042249917984009,0.18529284000396729,-0.3768865168094635,0.9042249917984009,0.20083265006542206,-0.36838120222091675,0.9042249917984009,0.21603818237781525,-0.35926273465156555,0.9042249917984009,0.2308841347694397,-0.34954631328582764,0.9042249917984009,0.2453458160161972,-0.3392481207847595,0.9042249917984009,0.2593991458415985,-0.32838526368141174,0.9042249917984009,0.2730207145214081,-0.316975861787796,0.9042249917984009,0.28618788719177246,-0.3050388693809509,0.9042249917984009,0.2988787293434143,-0.29259419441223145,0.9042249917984009,0.31107208132743835,-0.27966248989105225,0.9042249917984009,0.32274770736694336,-0.266265332698822,0.9042249917984009,0.33388617634773254,-0.25242501497268677,0.9042249917984009,0.344468891620636,-0.2381645292043686,0.9042249917984009,0.35447829961776733,-0.22350767254829407,0.9042249917984009,0.3638976812362671,-0.20847879350185394,0.9042249917984009,0.3727114200592041,-0.19310292601585388,0.9042249917984009,0.38090479373931885,-0.17740565538406372,0.9042249917984009,0.38846421241760254,-0.16141311824321747,0.9042249917984009,0.3953770399093628,-0.14515191316604614,0.9042249917984009,0.40163183212280273,-0.12864913046360016,0.9042249917984009,0.4072181284427643,-0.11193221062421799,0.9042249917984009,0.41212666034698486,-0.09502899646759033,0.9042249917984009,0.41634923219680786,-0.07796761393547058,0.9042249917984009,0.41987884044647217,-0.060776468366384506,0.9042249917984009,0.4227096140384674,-0.0434841625392437,0.9042249917984009,0.42483681440353394,-0.026119481772184372,0.9042249917984009,0.42625692486763,-0.008711326867341995,0.9042249917984009,0.426967591047287,0.008711326867341995,0.9042249917984009,0.426967591047287,0.026119481772184372,0.9042249917984009,0.42625692486763,0.0434841625392437,0.9042249917984009,0.42483681440353394,0.060776468366384506,0.9042249917984009,0.4227096140384674,0.07796761393547058,0.9042249917984009,0.41987884044647217,0.09502899646759033,0.9042249917984009,0.41634923219680786,0.11193221062421799,0.9042249917984009,0.41212666034698486,0.12864913046360016,0.9042249917984009,0.4072181284427643,0.14515191316604614,0.9042249917984009,0.40163183212280273,0.16141311824321747,0.9042249917984009,0.3953770399093628,0.17740565538406372,0.9042249917984009,0.38846421241760254,0.19310292601585388,0.9042249917984009,0.38090479373931885,0.20847879350185394,0.9042249917984009,0.3727114200592041,0.22350767254829407,0.9042249917984009,0.3638976812362671,0.2381645292043686,0.9042249917984009,0.35447829961776733,0.25242501497268677,0.9042249917984009,0.344468891620636,0.266265332698822,0.9042249917984009,0.33388617634773254,0.27966248989105225,0.9042249917984009,0.32274770736694336,0.29259419441223145,0.9042249917984009,0.31107208132743835,0.3050388693809509,0.9042249917984009,0.2988787293434143,0.316975861787796,0.9042249917984009,0.28618788719177246,0.32838526368141174,0.9042249917984009,0.2730207145214081,0.3392481207847595,0.9042249917984009,0.2593991458415985,0.34954631328582764,0.9042249917984009,0.2453458160161972,0.35926273465156555,0.9042249917984009,0.2308841347694397,0.36838120222091675,0.9042249917984009,0.21603818237781525,0.3768865168094635,0.9042249917984009,0.20083265006542206,0.38476455211639404,0.9042249917984009,0.18529284000396729,0.3920021951198578,0.9042249917984009,0.16944463551044464,0.3985873758792877,0.9042249917984009,0.15331441164016724,0.40450915694236755,0.9042249917984009,0.1369290053844452,0.4097576439380646,0.9042249917984009,0.12031570076942444,0.41432416439056396,0.9042249917984009,0.10350213944911957,0.4182010591030121,0.9042249917984009,0.08651630580425262,0.4213819205760956,0.9042249917984009,0.06938648223876953,0.42386141419410706,0.9042249917984009,0.05214116349816322,0.4256354570388794,0.9042249917984009,0.034809064120054245,0.42670103907585144,0.9042249917984009,0.017419029027223587,0.4270564317703247,0.9042249917984009,1.0459866267089016e-16,0.3622095584869385,0.9320966601371765,0.0,0.3619081377983093,0.9320966601371765,-0.014774016104638577,0.3610043525695801,0.9320966601371765,-0.029523441568017006,0.3594996929168701,0.9320966601371765,-0.04422372952103615,0.3573966920375824,0.9320966601371765,-0.05885041132569313,0.3546988368034363,0.9320966601371765,-0.07337914407253265,0.3514106273651123,0.9320966601371765,-0.0877857357263565,0.3475375175476074,0.9320966601371765,-0.10204622894525528,0.34308597445487976,0.9320966601371765,-0.11613687127828598,0.33806341886520386,0.9320966601371765,-0.1300342082977295,0.3324781656265259,0.9320966601371765,-0.14371512830257416,0.3263395428657532,0.9320966601371765,-0.157156839966774,0.31965774297714233,0.9320966601371765,-0.17033697664737701,0.31244394183158875,0.9320966601371765,-0.1832336187362671,0.3047100603580475,0.9320966601371765,-0.19582527875900269,0.2964690625667572,0.9320966601371765,-0.20809100568294525,0.28773459792137146,0.9320966601371765,-0.22001038491725922,0.27852123975753784,0.9320966601371765,-0.23156358301639557,0.2688443064689636,0.9320966601371765,-0.24273136258125305,0.25871989130973816,0.9320966601371765,-0.25349512696266174,0.24816487729549408,0.9320966601371765,-0.26383697986602783,0.23719681799411774,0.9320966601371765,-0.2737397253513336,0.2258339673280716,0.9320966601371765,-0.2831868529319763,0.21409523487091064,0.9320966601371765,-0.29216262698173523,0.20200017094612122,0.9320966601371765,-0.3006521165370941,0.189568892121315,0.9320966601371765,-0.3086412250995636,0.17682209610939026,0.9320966601371765,-0.3161166310310364,0.16378098726272583,0.9320966601371765,-0.3230658769607544,0.15046729147434235,0.9320966601371765,-0.3294773995876312,0.13690315186977386,0.9320966601371765,-0.33534055948257446,0.12311115860939026,0.9320966601371765,-0.34064558148384094,0.10911425203084946,0.9320966601371765,-0.3453836143016815,0.09493573755025864,0.9320966601371765,-0.3495468199253082,0.08059921115636826,0.9320966601371765,-0.35312822461128235,0.06612853705883026,0.9320966601371765,-0.35612186789512634,0.05154779553413391,0.9320966601371765,-0.35852280259132385,0.03688126057386398,0.9320966601371765,-0.36032700538635254,0.02215333841741085,0.9320966601371765,-0.36153146624565125,0.007388545200228691,0.9320966601371765,-0.36213418841362,-0.007388545200228691,0.9320966601371765,-0.36213418841362,-0.02215333841741085,0.9320966601371765,-0.36153146624565125,-0.03688126057386398,0.9320966601371765,-0.36032700538635254,-0.05154779553413391,0.9320966601371765,-0.35852280259132385,-0.06612853705883026,0.9320966601371765,-0.35612186789512634,-0.08059921115636826,0.9320966601371765,-0.35312822461128235,-0.09493573755025864,0.9320966601371765,-0.3495468199253082,-0.10911425203084946,0.9320966601371765,-0.3453836143016815,-0.12311115860939026,0.9320966601371765,-0.34064558148384094,-0.13690315186977386,0.9320966601371765,-0.33534055948257446,-0.15046729147434235,0.9320966601371765,-0.3294773995876312,-0.16378098726272583,0.9320966601371765,-0.3230658769607544,-0.17682209610939026,0.9320966601371765,-0.3161166310310364,-0.189568892121315,0.9320966601371765,-0.3086412250995636,-0.20200017094612122,0.9320966601371765,-0.3006521165370941,-0.21409523487091064,0.9320966601371765,-0.29216262698173523,-0.2258339673280716,0.9320966601371765,-0.2831868529319763,-0.23719681799411774,0.9320966601371765,-0.2737397253513336,-0.24816487729549408,0.9320966601371765,-0.26383697986602783,-0.25871989130973816,0.9320966601371765,-0.25349512696266174,-0.2688443064689636,0.9320966601371765,-0.24273136258125305,-0.27852123975753784,0.9320966601371765,-0.23156358301639557,-0.28773459792137146,0.9320966601371765,-0.22001038491725922,-0.2964690625667572,0.9320966601371765,-0.20809100568294525,-0.3047100603580475,0.9320966601371765,-0.19582527875900269,-0.31244394183158875,0.9320966601371765,-0.1832336187362671,-0.31965774297714233,0.9320966601371765,-0.17033697664737701,-0.3263395428657532,0.9320966601371765,-0.157156839966774,-0.3324781656265259,0.9320966601371765,-0.14371512830257416,-0.33806341886520386,0.9320966601371765,-0.1300342082977295,-0.34308597445487976,0.9320966601371765,-0.11613687127828598,-0.3475375175476074,0.9320966601371765,-0.10204622894525528,-0.3514106273651123,0.9320966601371765,-0.0877857357263565,-0.3546988368034363,0.9320966601371765,-0.07337914407253265,-0.3573966920375824,0.9320966601371765,-0.05885041132569313,-0.3594996929168701,0.9320966601371765,-0.04422372952103615,-0.3610043525695801,0.9320966601371765,-0.029523441568017006,-0.3619081377983093,0.9320966601371765,-0.014774016104638577,-0.3622095584869385,0.9320966601371765,-4.435787798551133e-17,-0.3619081377983093,0.9320966601371765,0.014774016104638577,-0.3610043525695801,0.9320966601371765,0.029523441568017006,-0.3594996929168701,0.9320966601371765,0.04422372952103615,-0.3573966920375824,0.9320966601371765,0.05885041132569313,-0.3546988368034363,0.9320966601371765,0.07337914407253265,-0.3514106273651123,0.9320966601371765,0.0877857357263565,-0.3475375175476074,0.9320966601371765,0.10204622894525528,-0.34308597445487976,0.9320966601371765,0.11613687127828598,-0.33806341886520386,0.9320966601371765,0.1300342082977295,-0.3324781656265259,0.9320966601371765,0.14371512830257416,-0.3263395428657532,0.9320966601371765,0.157156839966774,-0.31965774297714233,0.9320966601371765,0.17033697664737701,-0.31244394183158875,0.9320966601371765,0.1832336187362671,-0.3047100603580475,0.9320966601371765,0.19582527875900269,-0.2964690625667572,0.9320966601371765,0.20809100568294525,-0.28773459792137146,0.9320966601371765,0.22001038491725922,-0.27852123975753784,0.9320966601371765,0.23156358301639557,-0.2688443064689636,0.9320966601371765,0.24273136258125305,-0.25871989130973816,0.9320966601371765,0.25349512696266174,-0.24816487729549408,0.9320966601371765,0.26383697986602783,-0.23719681799411774,0.9320966601371765,0.2737397253513336,-0.2258339673280716,0.9320966601371765,0.2831868529319763,-0.21409523487091064,0.9320966601371765,0.29216262698173523,-0.20200017094612122,0.9320966601371765,0.3006521165370941,-0.189568892121315,0.9320966601371765,0.3086412250995636,-0.17682209610939026,0.9320966601371765,0.3161166310310364,-0.16378098726272583,0.9320966601371765,0.3230658769607544,-0.15046729147434235,0.9320966601371765,0.3294773995876312,-0.13690315186977386,0.9320966601371765,0.33534055948257446,-0.12311115860939026,0.9320966601371765,0.34064558148384094,-0.10911425203084946,0.9320966601371765,0.3453836143016815,-0.09493573755025864,0.9320966601371765,0.3495468199253082,-0.08059921115636826,0.9320966601371765,0.35312822461128235,-0.06612853705883026,0.9320966601371765,0.35612186789512634,-0.05154779553413391,0.9320966601371765,0.35852280259132385,-0.03688126057386398,0.9320966601371765,0.36032700538635254,-0.02215333841741085,0.9320966601371765,0.36153146624565125,-0.007388545200228691,0.9320966601371765,0.36213418841362,0.007388545200228691,0.9320966601371765,0.36213418841362,0.02215333841741085,0.9320966601371765,0.36153146624565125,0.03688126057386398,0.9320966601371765,0.36032700538635254,0.05154779553413391,0.9320966601371765,0.35852280259132385,0.06612853705883026,0.9320966601371765,0.35612186789512634,0.08059921115636826,0.9320966601371765,0.35312822461128235,0.09493573755025864,0.9320966601371765,0.3495468199253082,0.10911425203084946,0.9320966601371765,0.3453836143016815,0.12311115860939026,0.9320966601371765,0.34064558148384094,0.13690315186977386,0.9320966601371765,0.33534055948257446,0.15046729147434235,0.9320966601371765,0.3294773995876312,0.16378098726272583,0.9320966601371765,0.3230658769607544,0.17682209610939026,0.9320966601371765,0.3161166310310364,0.189568892121315,0.9320966601371765,0.3086412250995636,0.20200017094612122,0.9320966601371765,0.3006521165370941,0.21409523487091064,0.9320966601371765,0.29216262698173523,0.2258339673280716,0.9320966601371765,0.2831868529319763,0.23719681799411774,0.9320966601371765,0.2737397253513336,0.24816487729549408,0.9320966601371765,0.26383697986602783,0.25871989130973816,0.9320966601371765,0.25349512696266174,0.2688443064689636,0.9320966601371765,0.24273136258125305,0.27852123975753784,0.9320966601371765,0.23156358301639557,0.28773459792137146,0.9320966601371765,0.22001038491725922,0.2964690625667572,0.9320966601371765,0.20809100568294525,0.3047100603580475,0.9320966601371765,0.19582527875900269,0.31244394183158875,0.9320966601371765,0.1832336187362671,0.31965774297714233,0.9320966601371765,0.17033697664737701,0.3263395428657532,0.9320966601371765,0.157156839966774,0.3324781656265259,0.9320966601371765,0.14371512830257416,0.33806341886520386,0.9320966601371765,0.1300342082977295,0.34308597445487976,0.9320966601371765,0.11613687127828598,0.3475375175476074,0.9320966601371765,0.10204622894525528,0.3514106273651123,0.9320966601371765,0.0877857357263565,0.3546988368034363,0.9320966601371765,0.07337914407253265,0.3573966920375824,0.9320966601371765,0.05885041132569313,0.3594996929168701,0.9320966601371765,0.04422372952103615,0.3610043525695801,0.9320966601371765,0.029523441568017006,0.3619081377983093,0.9320966601371765,0.014774016104638577,0.3622095584869385,0.9320966601371765,8.871575597102266e-17,0.2955581843852997,0.9553247690200806,0.0,0.29531222581863403,0.9553247690200806,-0.01205539982765913,0.2945747375488281,0.9553247690200806,-0.024090735241770744,0.29334697127342224,0.9553247690200806,-0.03608597442507744,0.29163095355033875,0.9553247690200806,-0.04802115261554718,0.28942954540252686,0.9553247690200806,-0.05987640097737312,0.28674638271331787,0.9553247690200806,-0.07163199037313461,0.2835859954357147,0.9553247690200806,-0.08326835930347443,0.27995359897613525,0.9553247690200806,-0.09476613998413086,0.27585524320602417,0.9553247690200806,-0.10610618442296982,0.27129775285720825,0.9553247690200806,-0.11726962774991989,0.26628872752189636,0.9553247690200806,-0.12823788821697235,0.26083648204803467,0.9553247690200806,-0.13899271190166473,0.25495007634162903,0.9553247690200806,-0.14951619505882263,0.24863936007022858,0.9553247690200806,-0.15979081392288208,0.2419148087501526,0.9553247690200806,-0.16979949176311493,0.23478759825229645,0.9553247690200806,-0.17952553927898407,0.22726961970329285,0.9553247690200806,-0.18895278871059418,0.21937336027622223,0.9553247690200806,-0.19806554913520813,0.21111199259757996,0.9553247690200806,-0.2068486511707306,0.20249924063682556,0.9553247690200806,-0.2152874767780304,0.19354943931102753,0.9553247690200806,-0.22336797416210175,0.1842775046825409,0.9553247690200806,-0.2310766875743866,0.1746988594532013,0.9553247690200806,-0.23840080201625824,0.16482944786548615,0.9553247690200806,-0.2453281283378601,0.15468569099903107,0.9553247690200806,-0.25184711813926697,0.1442844718694687,0.9553247690200806,-0.2579469382762909,0.13364310562610626,0.9553247690200806,-0.26361745595932007,0.12277930229902267,0.9553247690200806,-0.2688491642475128,0.11171115189790726,0.9553247690200806,-0.2736334204673767,0.10045706480741501,0.9553247690200806,-0.27796226739883423,0.0890357717871666,0.9553247690200806,-0.2818284332752228,0.07746629416942596,0.9553247690200806,-0.2852255403995514,0.06576788425445557,0.9553247690200806,-0.2881479263305664,0.05396000295877457,0.9553247690200806,-0.29059070348739624,0.042062316089868546,0.9553247690200806,-0.29254981875419617,0.03009461611509323,0.9553247690200806,-0.2940220236778259,0.018076829612255096,0.9553247690200806,-0.29500484466552734,0.006028954405337572,0.9553247690200806,-0.2954966723918915,-0.006028954405337572,0.9553247690200806,-0.2954966723918915,-0.018076829612255096,0.9553247690200806,-0.29500484466552734,-0.03009461611509323,0.9553247690200806,-0.2940220236778259,-0.042062316089868546,0.9553247690200806,-0.29254981875419617,-0.05396000295877457,0.9553247690200806,-0.29059070348739624,-0.06576788425445557,0.9553247690200806,-0.2881479263305664,-0.07746629416942596,0.9553247690200806,-0.2852255403995514,-0.0890357717871666,0.9553247690200806,-0.2818284332752228,-0.10045706480741501,0.9553247690200806,-0.27796226739883423,-0.11171115189790726,0.9553247690200806,-0.2736334204673767,-0.12277930229902267,0.9553247690200806,-0.2688491642475128,-0.13364310562610626,0.9553247690200806,-0.26361745595932007,-0.1442844718694687,0.9553247690200806,-0.2579469382762909,-0.15468569099903107,0.9553247690200806,-0.25184711813926697,-0.16482944786548615,0.9553247690200806,-0.2453281283378601,-0.1746988594532013,0.9553247690200806,-0.23840080201625824,-0.1842775046825409,0.9553247690200806,-0.2310766875743866,-0.19354943931102753,0.9553247690200806,-0.22336797416210175,-0.20249924063682556,0.9553247690200806,-0.2152874767780304,-0.21111199259757996,0.9553247690200806,-0.2068486511707306,-0.21937336027622223,0.9553247690200806,-0.19806554913520813,-0.22726961970329285,0.9553247690200806,-0.18895278871059418,-0.23478759825229645,0.9553247690200806,-0.17952553927898407,-0.2419148087501526,0.9553247690200806,-0.16979949176311493,-0.24863936007022858,0.9553247690200806,-0.15979081392288208,-0.25495007634162903,0.9553247690200806,-0.14951619505882263,-0.26083648204803467,0.9553247690200806,-0.13899271190166473,-0.26628872752189636,0.9553247690200806,-0.12823788821697235,-0.27129775285720825,0.9553247690200806,-0.11726962774991989,-0.27585524320602417,0.9553247690200806,-0.10610618442296982,-0.27995359897613525,0.9553247690200806,-0.09476613998413086,-0.2835859954357147,0.9553247690200806,-0.08326835930347443,-0.28674638271331787,0.9553247690200806,-0.07163199037313461,-0.28942954540252686,0.9553247690200806,-0.05987640097737312,-0.29163095355033875,0.9553247690200806,-0.04802115261554718,-0.29334697127342224,0.9553247690200806,-0.03608597442507744,-0.2945747375488281,0.9553247690200806,-0.024090735241770744,-0.29531222581863403,0.9553247690200806,-0.01205539982765913,-0.2955581843852997,0.9553247690200806,-3.6195438371850364e-17,-0.29531222581863403,0.9553247690200806,0.01205539982765913,-0.2945747375488281,0.9553247690200806,0.024090735241770744,-0.29334697127342224,0.9553247690200806,0.03608597442507744,-0.29163095355033875,0.9553247690200806,0.04802115261554718,-0.28942954540252686,0.9553247690200806,0.05987640097737312,-0.28674638271331787,0.9553247690200806,0.07163199037313461,-0.2835859954357147,0.9553247690200806,0.08326835930347443,-0.27995359897613525,0.9553247690200806,0.09476613998413086,-0.27585524320602417,0.9553247690200806,0.10610618442296982,-0.27129775285720825,0.9553247690200806,0.11726962774991989,-0.26628872752189636,0.9553247690200806,0.12823788821697235,-0.26083648204803467,0.9553247690200806,0.13899271190166473,-0.25495007634162903,0.9553247690200806,0.14951619505882263,-0.24863936007022858,0.9553247690200806,0.15979081392288208,-0.2419148087501526,0.9553247690200806,0.16979949176311493,-0.23478759825229645,0.9553247690200806,0.17952553927898407,-0.22726961970329285,0.9553247690200806,0.18895278871059418,-0.21937336027622223,0.9553247690200806,0.19806554913520813,-0.21111199259757996,0.9553247690200806,0.2068486511707306,-0.20249924063682556,0.9553247690200806,0.2152874767780304,-0.19354943931102753,0.9553247690200806,0.22336797416210175,-0.1842775046825409,0.9553247690200806,0.2310766875743866,-0.1746988594532013,0.9553247690200806,0.23840080201625824,-0.16482944786548615,0.9553247690200806,0.2453281283378601,-0.15468569099903107,0.9553247690200806,0.25184711813926697,-0.1442844718694687,0.9553247690200806,0.2579469382762909,-0.13364310562610626,0.9553247690200806,0.26361745595932007,-0.12277930229902267,0.9553247690200806,0.2688491642475128,-0.11171115189790726,0.9553247690200806,0.2736334204673767,-0.10045706480741501,0.9553247690200806,0.27796226739883423,-0.0890357717871666,0.9553247690200806,0.2818284332752228,-0.07746629416942596,0.9553247690200806,0.2852255403995514,-0.06576788425445557,0.9553247690200806,0.2881479263305664,-0.05396000295877457,0.9553247690200806,0.29059070348739624,-0.042062316089868546,0.9553247690200806,0.29254981875419617,-0.03009461611509323,0.9553247690200806,0.2940220236778259,-0.018076829612255096,0.9553247690200806,0.29500484466552734,-0.006028954405337572,0.9553247690200806,0.2954966723918915,0.006028954405337572,0.9553247690200806,0.2954966723918915,0.018076829612255096,0.9553247690200806,0.29500484466552734,0.03009461611509323,0.9553247690200806,0.2940220236778259,0.042062316089868546,0.9553247690200806,0.29254981875419617,0.05396000295877457,0.9553247690200806,0.29059070348739624,0.06576788425445557,0.9553247690200806,0.2881479263305664,0.07746629416942596,0.9553247690200806,0.2852255403995514,0.0890357717871666,0.9553247690200806,0.2818284332752228,0.10045706480741501,0.9553247690200806,0.27796226739883423,0.11171115189790726,0.9553247690200806,0.2736334204673767,0.12277930229902267,0.9553247690200806,0.2688491642475128,0.13364310562610626,0.9553247690200806,0.26361745595932007,0.1442844718694687,0.9553247690200806,0.2579469382762909,0.15468569099903107,0.9553247690200806,0.25184711813926697,0.16482944786548615,0.9553247690200806,0.2453281283378601,0.1746988594532013,0.9553247690200806,0.23840080201625824,0.1842775046825409,0.9553247690200806,0.2310766875743866,0.19354943931102753,0.9553247690200806,0.22336797416210175,0.20249924063682556,0.9553247690200806,0.2152874767780304,0.21111199259757996,0.9553247690200806,0.2068486511707306,0.21937336027622223,0.9553247690200806,0.19806554913520813,0.22726961970329285,0.9553247690200806,0.18895278871059418,0.23478759825229645,0.9553247690200806,0.17952553927898407,0.2419148087501526,0.9553247690200806,0.16979949176311493,0.24863936007022858,0.9553247690200806,0.15979081392288208,0.25495007634162903,0.9553247690200806,0.14951619505882263,0.26083648204803467,0.9553247690200806,0.13899271190166473,0.26628872752189636,0.9553247690200806,0.12823788821697235,0.27129775285720825,0.9553247690200806,0.11726962774991989,0.27585524320602417,0.9553247690200806,0.10610618442296982,0.27995359897613525,0.9553247690200806,0.09476613998413086,0.2835859954357147,0.9553247690200806,0.08326835930347443,0.28674638271331787,0.9553247690200806,0.07163199037313461,0.28942954540252686,0.9553247690200806,0.05987640097737312,0.29163095355033875,0.9553247690200806,0.04802115261554718,0.29334697127342224,0.9553247690200806,0.03608597442507744,0.2945747375488281,0.9553247690200806,0.024090735241770744,0.29531222581863403,0.9553247690200806,0.01205539982765913,0.2955581843852997,0.9553247690200806,7.239087674370073e-17,0.22743433713912964,0.9737934470176697,0.0,0.22724506258964539,0.9737934470176697,-0.009276725351810455,0.2266775667667389,0.9737934470176697,-0.018538009375333786,0.22573278844356537,0.9737934470176697,-0.027768440544605255,0.22441229224205017,0.9737934470176697,-0.03695265203714371,0.22271829843521118,0.9737934470176697,-0.04607535898685455,0.22065359354019165,0.9737934470176697,-0.05512138083577156,0.21822164952754974,0.9737934470176697,-0.06407565623521805,0.2154264897108078,0.9737934470176697,-0.07292328774929047,0.2122727781534195,0.9737934470176697,-0.0816495418548584,0.20876574516296387,0.9737934470176697,-0.09023989737033844,0.2049112617969513,0.9737934470176697,-0.09868006408214569,0.20071570575237274,0.9737934470176697,-0.10695598274469376,0.1961860954761505,0.9737934470176697,-0.11505388468503952,0.1913299411535263,0.9737934470176697,-0.12296028435230255,0.18615533411502838,0.9737934470176697,-0.13066203892230988,0.18067090213298798,0.9737934470176697,-0.138146311044693,0.17488574981689453,0.9737934470176697,-0.14540065824985504,0.16880953311920166,0.9737934470176697,-0.1524129956960678,0.1624523252248764,0.9737934470176697,-0.15917165577411652,0.15582475066184998,0.9737934470176697,-0.165665403008461,0.14893782138824463,0.9737934470176697,-0.17188340425491333,0.14180299639701843,0.9737934470176697,-0.17781533300876617,0.13443215191364288,0.9737934470176697,-0.18345129489898682,0.12683755159378052,0.9737934470176697,-0.18878191709518433,0.11903185397386551,0.9737934470176697,-0.19379833340644836,0.1110280305147171,0.9737934470176697,-0.19849219918251038,0.10283941775560379,0.9737934470176697,-0.2028556913137436,0.09447963535785675,0.9737934470176697,-0.2068815529346466,0.08596260845661163,0.9737934470176697,-0.2105630785226822,0.07730250060558319,0.9737934470176697,-0.21389414370059967,0.06851372867822647,0.9737934470176697,-0.21686919033527374,0.05961092188954353,0.9737934470176697,-0.21948328614234924,0.05060890316963196,0.9737934470176697,-0.22173207998275757,0.04152264818549156,0.9737934470176697,-0.22361183166503906,0.0323672816157341,0.9737934470176697,-0.22511938214302063,0.02315804362297058,0.9737934470176697,-0.2262522578239441,0.013910261914134026,0.9737934470176697,-0.22700855135917664,0.004639327991753817,0.9737934470176697,-0.22738701105117798,-0.004639327991753817,0.9737934470176697,-0.22738701105117798,-0.013910261914134026,0.9737934470176697,-0.22700855135917664,-0.02315804362297058,0.9737934470176697,-0.2262522578239441,-0.0323672816157341,0.9737934470176697,-0.22511938214302063,-0.04152264818549156,0.9737934470176697,-0.22361183166503906,-0.05060890316963196,0.9737934470176697,-0.22173207998275757,-0.05961092188954353,0.9737934470176697,-0.21948328614234924,-0.06851372867822647,0.9737934470176697,-0.21686919033527374,-0.07730250060558319,0.9737934470176697,-0.21389414370059967,-0.08596260845661163,0.9737934470176697,-0.2105630785226822,-0.09447963535785675,0.9737934470176697,-0.2068815529346466,-0.10283941775560379,0.9737934470176697,-0.2028556913137436,-0.1110280305147171,0.9737934470176697,-0.19849219918251038,-0.11903185397386551,0.9737934470176697,-0.19379833340644836,-0.12683755159378052,0.9737934470176697,-0.18878191709518433,-0.13443215191364288,0.9737934470176697,-0.18345129489898682,-0.14180299639701843,0.9737934470176697,-0.17781533300876617,-0.14893782138824463,0.9737934470176697,-0.17188340425491333,-0.15582475066184998,0.9737934470176697,-0.165665403008461,-0.1624523252248764,0.9737934470176697,-0.15917165577411652,-0.16880953311920166,0.9737934470176697,-0.1524129956960678,-0.17488574981689453,0.9737934470176697,-0.14540065824985504,-0.18067090213298798,0.9737934470176697,-0.138146311044693,-0.18615533411502838,0.9737934470176697,-0.13066203892230988,-0.1913299411535263,0.9737934470176697,-0.12296028435230255,-0.1961860954761505,0.9737934470176697,-0.11505388468503952,-0.20071570575237274,0.9737934470176697,-0.10695598274469376,-0.2049112617969513,0.9737934470176697,-0.09868006408214569,-0.20876574516296387,0.9737934470176697,-0.09023989737033844,-0.2122727781534195,0.9737934470176697,-0.0816495418548584,-0.2154264897108078,0.9737934470176697,-0.07292328774929047,-0.21822164952754974,0.9737934470176697,-0.06407565623521805,-0.22065359354019165,0.9737934470176697,-0.05512138083577156,-0.22271829843521118,0.9737934470176697,-0.04607535898685455,-0.22441229224205017,0.9737934470176697,-0.03695265203714371,-0.22573278844356537,0.9737934470176697,-0.027768440544605255,-0.2266775667667389,0.9737934470176697,-0.018538009375333786,-0.22724506258964539,0.9737934470176697,-0.009276725351810455,-0.22743433713912964,0.9737934470176697,-2.785267338465284e-17,-0.22724506258964539,0.9737934470176697,0.009276725351810455,-0.2266775667667389,0.9737934470176697,0.018538009375333786,-0.22573278844356537,0.9737934470176697,0.027768440544605255,-0.22441229224205017,0.9737934470176697,0.03695265203714371,-0.22271829843521118,0.9737934470176697,0.04607535898685455,-0.22065359354019165,0.9737934470176697,0.05512138083577156,-0.21822164952754974,0.9737934470176697,0.06407565623521805,-0.2154264897108078,0.9737934470176697,0.07292328774929047,-0.2122727781534195,0.9737934470176697,0.0816495418548584,-0.20876574516296387,0.9737934470176697,0.09023989737033844,-0.2049112617969513,0.9737934470176697,0.09868006408214569,-0.20071570575237274,0.9737934470176697,0.10695598274469376,-0.1961860954761505,0.9737934470176697,0.11505388468503952,-0.1913299411535263,0.9737934470176697,0.12296028435230255,-0.18615533411502838,0.9737934470176697,0.13066203892230988,-0.18067090213298798,0.9737934470176697,0.138146311044693,-0.17488574981689453,0.9737934470176697,0.14540065824985504,-0.16880953311920166,0.9737934470176697,0.1524129956960678,-0.1624523252248764,0.9737934470176697,0.15917165577411652,-0.15582475066184998,0.9737934470176697,0.165665403008461,-0.14893782138824463,0.9737934470176697,0.17188340425491333,-0.14180299639701843,0.9737934470176697,0.17781533300876617,-0.13443215191364288,0.9737934470176697,0.18345129489898682,-0.12683755159378052,0.9737934470176697,0.18878191709518433,-0.11903185397386551,0.9737934470176697,0.19379833340644836,-0.1110280305147171,0.9737934470176697,0.19849219918251038,-0.10283941775560379,0.9737934470176697,0.2028556913137436,-0.09447963535785675,0.9737934470176697,0.2068815529346466,-0.08596260845661163,0.9737934470176697,0.2105630785226822,-0.07730250060558319,0.9737934470176697,0.21389414370059967,-0.06851372867822647,0.9737934470176697,0.21686919033527374,-0.05961092188954353,0.9737934470176697,0.21948328614234924,-0.05060890316963196,0.9737934470176697,0.22173207998275757,-0.04152264818549156,0.9737934470176697,0.22361183166503906,-0.0323672816157341,0.9737934470176697,0.22511938214302063,-0.02315804362297058,0.9737934470176697,0.2262522578239441,-0.013910261914134026,0.9737934470176697,0.22700855135917664,-0.004639327991753817,0.9737934470176697,0.22738701105117798,0.004639327991753817,0.9737934470176697,0.22738701105117798,0.013910261914134026,0.9737934470176697,0.22700855135917664,0.02315804362297058,0.9737934470176697,0.2262522578239441,0.0323672816157341,0.9737934470176697,0.22511938214302063,0.04152264818549156,0.9737934470176697,0.22361183166503906,0.05060890316963196,0.9737934470176697,0.22173207998275757,0.05961092188954353,0.9737934470176697,0.21948328614234924,0.06851372867822647,0.9737934470176697,0.21686919033527374,0.07730250060558319,0.9737934470176697,0.21389414370059967,0.08596260845661163,0.9737934470176697,0.2105630785226822,0.09447963535785675,0.9737934470176697,0.2068815529346466,0.10283941775560379,0.9737934470176697,0.2028556913137436,0.1110280305147171,0.9737934470176697,0.19849219918251038,0.11903185397386551,0.9737934470176697,0.19379833340644836,0.12683755159378052,0.9737934470176697,0.18878191709518433,0.13443215191364288,0.9737934470176697,0.18345129489898682,0.14180299639701843,0.9737934470176697,0.17781533300876617,0.14893782138824463,0.9737934470176697,0.17188340425491333,0.15582475066184998,0.9737934470176697,0.165665403008461,0.1624523252248764,0.9737934470176697,0.15917165577411652,0.16880953311920166,0.9737934470176697,0.1524129956960678,0.17488574981689453,0.9737934470176697,0.14540065824985504,0.18067090213298798,0.9737934470176697,0.138146311044693,0.18615533411502838,0.9737934470176697,0.13066203892230988,0.1913299411535263,0.9737934470176697,0.12296028435230255,0.1961860954761505,0.9737934470176697,0.11505388468503952,0.20071570575237274,0.9737934470176697,0.10695598274469376,0.2049112617969513,0.9737934470176697,0.09868006408214569,0.20876574516296387,0.9737934470176697,0.09023989737033844,0.2122727781534195,0.9737934470176697,0.0816495418548584,0.2154264897108078,0.9737934470176697,0.07292328774929047,0.21822164952754974,0.9737934470176697,0.06407565623521805,0.22065359354019165,0.9737934470176697,0.05512138083577156,0.22271829843521118,0.9737934470176697,0.04607535898685455,0.22441229224205017,0.9737934470176697,0.03695265203714371,0.22573278844356537,0.9737934470176697,0.027768440544605255,0.2266775667667389,0.9737934470176697,0.018538009375333786,0.22724506258964539,0.9737934470176697,0.009276725351810455,0.22743433713912964,0.9737934470176697,5.570534676930568e-17,0.1581774353981018,0.9874107241630554,0.0,0.15804579854011536,0.9874107241630554,-0.0064518335275352,0.1576511114835739,0.9874107241630554,-0.012892928905785084,0.15699402987957,0.9874107241630554,-0.01931256614625454,0.1560756415128708,0.9874107241630554,-0.025700056925415993,0.15489749610424042,0.9874107241630554,-0.032044775784015656,0.1534615159034729,0.9874107241630554,-0.03833615779876709,0.15177012979984283,0.9874107241630554,-0.04456373304128647,0.14982613921165466,0.9874107241630554,-0.05071713402867317,0.14763277769088745,0.9874107241630554,-0.05678612366318703,0.1451936811208725,0.9874107241630554,-0.0627605989575386,0.1425129473209381,0.9874107241630554,-0.06863061338663101,0.13959500193595886,0.9874107241630554,-0.07438640296459198,0.13644470274448395,0.9874107241630554,-0.08001837879419327,0.133067324757576,0.9874107241630554,-0.08551717549562454,0.12946845591068268,0.9874107241630554,-0.09087363630533218,0.12565410137176514,0.9874107241630554,-0.09607884287834167,0.12163061648607254,0.9874107241630554,-0.10112414509057999,0.11740468442440033,0.9874107241630554,-0.10600113123655319,0.1129833459854126,0.9874107241630554,-0.11070168763399124,0.10837395489215851,0.9874107241630554,-0.11521799117326736,0.1035841852426529,0.9874107241630554,-0.11954252421855927,0.09862201660871506,0.9874107241630554,-0.12366809695959091,0.0934956967830658,0.9874107241630554,-0.12758782505989075,0.08821376413106918,0.9874107241630554,-0.13129520416259766,0.08278500288724899,0.9874107241630554,-0.1347840577363968,0.07721845805644989,0.9874107241630554,-0.13804857432842255,0.07152339071035385,0.9874107241630554,-0.14108332991600037,0.06570927798748016,0.9874107241630554,-0.14388325810432434,0.05978580191731453,0.9874107241630554,-0.1464436948299408,0.05376281589269638,0.9874107241630554,-0.14876040816307068,0.04765034839510918,0.9874107241630554,-0.1508295238018036,0.04145856946706772,0.9874107241630554,-0.15264758467674255,0.03519779071211815,0.9874107241630554,-0.1542115956544876,0.02887842431664467,0.9874107241630554,-0.15551891922950745,0.022510996088385582,0.9874107241630554,-0.15656740963459015,0.016106098890304565,0.9874107241630554,-0.15735530853271484,0.009674394503235817,0.9874107241630554,-0.15788130462169647,0.003226588247343898,0.9874107241630554,-0.1581445187330246,-0.003226588247343898,0.9874107241630554,-0.1581445187330246,-0.009674394503235817,0.9874107241630554,-0.15788130462169647,-0.016106098890304565,0.9874107241630554,-0.15735530853271484,-0.022510996088385582,0.9874107241630554,-0.15656740963459015,-0.02887842431664467,0.9874107241630554,-0.15551891922950745,-0.03519779071211815,0.9874107241630554,-0.1542115956544876,-0.04145856946706772,0.9874107241630554,-0.15264758467674255,-0.04765034839510918,0.9874107241630554,-0.1508295238018036,-0.05376281589269638,0.9874107241630554,-0.14876040816307068,-0.05978580191731453,0.9874107241630554,-0.1464436948299408,-0.06570927798748016,0.9874107241630554,-0.14388325810432434,-0.07152339071035385,0.9874107241630554,-0.14108332991600037,-0.07721845805644989,0.9874107241630554,-0.13804857432842255,-0.08278500288724899,0.9874107241630554,-0.1347840577363968,-0.08821376413106918,0.9874107241630554,-0.13129520416259766,-0.0934956967830658,0.9874107241630554,-0.12758782505989075,-0.09862201660871506,0.9874107241630554,-0.12366809695959091,-0.1035841852426529,0.9874107241630554,-0.11954252421855927,-0.10837395489215851,0.9874107241630554,-0.11521799117326736,-0.1129833459854126,0.9874107241630554,-0.11070168763399124,-0.11740468442440033,0.9874107241630554,-0.10600113123655319,-0.12163061648607254,0.9874107241630554,-0.10112414509057999,-0.12565410137176514,0.9874107241630554,-0.09607884287834167,-0.12946845591068268,0.9874107241630554,-0.09087363630533218,-0.133067324757576,0.9874107241630554,-0.08551717549562454,-0.13644470274448395,0.9874107241630554,-0.08001837879419327,-0.13959500193595886,0.9874107241630554,-0.07438640296459198,-0.1425129473209381,0.9874107241630554,-0.06863061338663101,-0.1451936811208725,0.9874107241630554,-0.0627605989575386,-0.14763277769088745,0.9874107241630554,-0.05678612366318703,-0.14982613921165466,0.9874107241630554,-0.05071713402867317,-0.15177012979984283,0.9874107241630554,-0.04456373304128647,-0.1534615159034729,0.9874107241630554,-0.03833615779876709,-0.15489749610424042,0.9874107241630554,-0.032044775784015656,-0.1560756415128708,0.9874107241630554,-0.025700056925415993,-0.15699402987957,0.9874107241630554,-0.01931256614625454,-0.1576511114835739,0.9874107241630554,-0.012892928905785084,-0.15804579854011536,0.9874107241630554,-0.0064518335275352,-0.1581774353981018,0.9874107241630554,-1.9371148849699542e-17,-0.15804579854011536,0.9874107241630554,0.0064518335275352,-0.1576511114835739,0.9874107241630554,0.012892928905785084,-0.15699402987957,0.9874107241630554,0.01931256614625454,-0.1560756415128708,0.9874107241630554,0.025700056925415993,-0.15489749610424042,0.9874107241630554,0.032044775784015656,-0.1534615159034729,0.9874107241630554,0.03833615779876709,-0.15177012979984283,0.9874107241630554,0.04456373304128647,-0.14982613921165466,0.9874107241630554,0.05071713402867317,-0.14763277769088745,0.9874107241630554,0.05678612366318703,-0.1451936811208725,0.9874107241630554,0.0627605989575386,-0.1425129473209381,0.9874107241630554,0.06863061338663101,-0.13959500193595886,0.9874107241630554,0.07438640296459198,-0.13644470274448395,0.9874107241630554,0.08001837879419327,-0.133067324757576,0.9874107241630554,0.08551717549562454,-0.12946845591068268,0.9874107241630554,0.09087363630533218,-0.12565410137176514,0.9874107241630554,0.09607884287834167,-0.12163061648607254,0.9874107241630554,0.10112414509057999,-0.11740468442440033,0.9874107241630554,0.10600113123655319,-0.1129833459854126,0.9874107241630554,0.11070168763399124,-0.10837395489215851,0.9874107241630554,0.11521799117326736,-0.1035841852426529,0.9874107241630554,0.11954252421855927,-0.09862201660871506,0.9874107241630554,0.12366809695959091,-0.0934956967830658,0.9874107241630554,0.12758782505989075,-0.08821376413106918,0.9874107241630554,0.13129520416259766,-0.08278500288724899,0.9874107241630554,0.1347840577363968,-0.07721845805644989,0.9874107241630554,0.13804857432842255,-0.07152339071035385,0.9874107241630554,0.14108332991600037,-0.06570927798748016,0.9874107241630554,0.14388325810432434,-0.05978580191731453,0.9874107241630554,0.1464436948299408,-0.05376281589269638,0.9874107241630554,0.14876040816307068,-0.04765034839510918,0.9874107241630554,0.1508295238018036,-0.04145856946706772,0.9874107241630554,0.15264758467674255,-0.03519779071211815,0.9874107241630554,0.1542115956544876,-0.02887842431664467,0.9874107241630554,0.15551891922950745,-0.022510996088385582,0.9874107241630554,0.15656740963459015,-0.016106098890304565,0.9874107241630554,0.15735530853271484,-0.009674394503235817,0.9874107241630554,0.15788130462169647,-0.003226588247343898,0.9874107241630554,0.1581445187330246,0.003226588247343898,0.9874107241630554,0.1581445187330246,0.009674394503235817,0.9874107241630554,0.15788130462169647,0.016106098890304565,0.9874107241630554,0.15735530853271484,0.022510996088385582,0.9874107241630554,0.15656740963459015,0.02887842431664467,0.9874107241630554,0.15551891922950745,0.03519779071211815,0.9874107241630554,0.1542115956544876,0.04145856946706772,0.9874107241630554,0.15264758467674255,0.04765034839510918,0.9874107241630554,0.1508295238018036,0.05376281589269638,0.9874107241630554,0.14876040816307068,0.05978580191731453,0.9874107241630554,0.1464436948299408,0.06570927798748016,0.9874107241630554,0.14388325810432434,0.07152339071035385,0.9874107241630554,0.14108332991600037,0.07721845805644989,0.9874107241630554,0.13804857432842255,0.08278500288724899,0.9874107241630554,0.1347840577363968,0.08821376413106918,0.9874107241630554,0.13129520416259766,0.0934956967830658,0.9874107241630554,0.12758782505989075,0.09862201660871506,0.9874107241630554,0.12366809695959091,0.1035841852426529,0.9874107241630554,0.11954252421855927,0.10837395489215851,0.9874107241630554,0.11521799117326736,0.1129833459854126,0.9874107241630554,0.11070168763399124,0.11740468442440033,0.9874107241630554,0.10600113123655319,0.12163061648607254,0.9874107241630554,0.10112414509057999,0.12565410137176514,0.9874107241630554,0.09607884287834167,0.12946845591068268,0.9874107241630554,0.09087363630533218,0.133067324757576,0.9874107241630554,0.08551717549562454,0.13644470274448395,0.9874107241630554,0.08001837879419327,0.13959500193595886,0.9874107241630554,0.07438640296459198,0.1425129473209381,0.9874107241630554,0.06863061338663101,0.1451936811208725,0.9874107241630554,0.0627605989575386,0.14763277769088745,0.9874107241630554,0.05678612366318703,0.14982613921165466,0.9874107241630554,0.05071713402867317,0.15177012979984283,0.9874107241630554,0.04456373304128647,0.1534615159034729,0.9874107241630554,0.03833615779876709,0.15489749610424042,0.9874107241630554,0.032044775784015656,0.1560756415128708,0.9874107241630554,0.025700056925415993,0.15699402987957,0.9874107241630554,0.01931256614625454,0.1576511114835739,0.9874107241630554,0.012892928905785084,0.15804579854011536,0.9874107241630554,0.0064518335275352,0.1581774353981018,0.9874107241630554,3.8742297699399084e-17,0.08813249319791794,0.9961087703704834,0.0,0.08805914968252182,0.9961087703704834,-0.003594799665734172,0.08783923834562302,0.9961087703704834,-0.007183616049587727,0.08747313171625137,0.9961087703704834,-0.010760476812720299,0.08696142584085464,0.9961087703704834,-0.014319426380097866,0.08630499243736267,0.9961087703704834,-0.017854543402791023,0.08550490438938141,0.9961087703704834,-0.021359942853450775,0.0845625028014183,0.9961087703704834,-0.024829791858792305,0.08347935974597931,0.9961087703704834,-0.028258314356207848,0.08225727081298828,0.9961087703704834,-0.03163980320096016,0.08089827746152878,0.9961087703704834,-0.034968629479408264,0.07940462976694107,0.9961087703704834,-0.0382392555475235,0.07777882367372513,0.9961087703704834,-0.04144623503088951,0.07602356374263763,0.9961087703704834,-0.044584233313798904,0.07414177060127258,0.9961087703704834,-0.047648023813962936,0.07213657349348068,0.9961087703704834,-0.05063250660896301,0.07001131027936935,0.9961087703704834,-0.05353271961212158,0.06776952743530273,0.9961087703704834,-0.05634383112192154,0.06541494280099869,0.9961087703704834,-0.059061165899038315,0.0629514828324318,0.9961087703704834,-0.06168019771575928,0.06038324534893036,0.9961087703704834,-0.06419657170772552,0.05771450698375702,0.9961087703704834,-0.06660608947277069,0.05494970828294754,0.9961087703704834,-0.06890475749969482,0.05209345370531082,0.9961087703704834,-0.07108873128890991,0.0491504929959774,0.9961087703704834,-0.07315438985824585,0.04612572491168976,0.9961087703704834,-0.07509829103946686,0.04302418604493141,0.9961087703704834,-0.07691720128059387,0.03985103592276573,0.9961087703704834,-0.07860808074474335,0.036611560732126236,0.9961087703704834,-0.08016813546419144,0.0333111472427845,0.9961087703704834,-0.08159475028514862,0.029955290257930756,0.9961087703704834,-0.08288556337356567,0.026549577713012695,0.9961087703704834,-0.08403842151165009,0.02309967391192913,0.9961087703704834,-0.08505140244960785,0.01961132511496544,0.9961087703704834,-0.08592282235622406,0.016090333461761475,0.9961087703704834,-0.08665123581886292,0.012542561627924442,0.9961087703704834,-0.08723542839288712,0.00897391326725483,0.9961087703704834,-0.08767442405223846,0.005390329752117395,0.9961087703704834,-0.08796749264001846,0.0017977738752961159,0.9961087703704834,-0.08811415731906891,-0.0017977738752961159,0.9961087703704834,-0.08811415731906891,-0.005390329752117395,0.9961087703704834,-0.08796749264001846,-0.00897391326725483,0.9961087703704834,-0.08767442405223846,-0.012542561627924442,0.9961087703704834,-0.08723542839288712,-0.016090333461761475,0.9961087703704834,-0.08665123581886292,-0.01961132511496544,0.9961087703704834,-0.08592282235622406,-0.02309967391192913,0.9961087703704834,-0.08505140244960785,-0.026549577713012695,0.9961087703704834,-0.08403842151165009,-0.029955290257930756,0.9961087703704834,-0.08288556337356567,-0.0333111472427845,0.9961087703704834,-0.08159475028514862,-0.036611560732126236,0.9961087703704834,-0.08016813546419144,-0.03985103592276573,0.9961087703704834,-0.07860808074474335,-0.04302418604493141,0.9961087703704834,-0.07691720128059387,-0.04612572491168976,0.9961087703704834,-0.07509829103946686,-0.0491504929959774,0.9961087703704834,-0.07315438985824585,-0.05209345370531082,0.9961087703704834,-0.07108873128890991,-0.05494970828294754,0.9961087703704834,-0.06890475749969482,-0.05771450698375702,0.9961087703704834,-0.06660608947277069,-0.06038324534893036,0.9961087703704834,-0.06419657170772552,-0.0629514828324318,0.9961087703704834,-0.06168019771575928,-0.06541494280099869,0.9961087703704834,-0.059061165899038315,-0.06776952743530273,0.9961087703704834,-0.05634383112192154,-0.07001131027936935,0.9961087703704834,-0.05353271961212158,-0.07213657349348068,0.9961087703704834,-0.05063250660896301,-0.07414177060127258,0.9961087703704834,-0.047648023813962936,-0.07602356374263763,0.9961087703704834,-0.044584233313798904,-0.07777882367372513,0.9961087703704834,-0.04144623503088951,-0.07940462976694107,0.9961087703704834,-0.0382392555475235,-0.08089827746152878,0.9961087703704834,-0.034968629479408264,-0.08225727081298828,0.9961087703704834,-0.03163980320096016,-0.08347935974597931,0.9961087703704834,-0.028258314356207848,-0.0845625028014183,0.9961087703704834,-0.024829791858792305,-0.08550490438938141,0.9961087703704834,-0.021359942853450775,-0.08630499243736267,0.9961087703704834,-0.017854543402791023,-0.08696142584085464,0.9961087703704834,-0.014319426380097866,-0.08747313171625137,0.9961087703704834,-0.010760476812720299,-0.08783923834562302,0.9961087703704834,-0.007183616049587727,-0.08805914968252182,0.9961087703704834,-0.003594799665734172,-0.08813249319791794,0.9961087703704834,-1.0793117152679684e-17,-0.08805914968252182,0.9961087703704834,0.003594799665734172,-0.08783923834562302,0.9961087703704834,0.007183616049587727,-0.08747313171625137,0.9961087703704834,0.010760476812720299,-0.08696142584085464,0.9961087703704834,0.014319426380097866,-0.08630499243736267,0.9961087703704834,0.017854543402791023,-0.08550490438938141,0.9961087703704834,0.021359942853450775,-0.0845625028014183,0.9961087703704834,0.024829791858792305,-0.08347935974597931,0.9961087703704834,0.028258314356207848,-0.08225727081298828,0.9961087703704834,0.03163980320096016,-0.08089827746152878,0.9961087703704834,0.034968629479408264,-0.07940462976694107,0.9961087703704834,0.0382392555475235,-0.07777882367372513,0.9961087703704834,0.04144623503088951,-0.07602356374263763,0.9961087703704834,0.044584233313798904,-0.07414177060127258,0.9961087703704834,0.047648023813962936,-0.07213657349348068,0.9961087703704834,0.05063250660896301,-0.07001131027936935,0.9961087703704834,0.05353271961212158,-0.06776952743530273,0.9961087703704834,0.05634383112192154,-0.06541494280099869,0.9961087703704834,0.059061165899038315,-0.0629514828324318,0.9961087703704834,0.06168019771575928,-0.06038324534893036,0.9961087703704834,0.06419657170772552,-0.05771450698375702,0.9961087703704834,0.06660608947277069,-0.05494970828294754,0.9961087703704834,0.06890475749969482,-0.05209345370531082,0.9961087703704834,0.07108873128890991,-0.0491504929959774,0.9961087703704834,0.07315438985824585,-0.04612572491168976,0.9961087703704834,0.07509829103946686,-0.04302418604493141,0.9961087703704834,0.07691720128059387,-0.03985103592276573,0.9961087703704834,0.07860808074474335,-0.036611560732126236,0.9961087703704834,0.08016813546419144,-0.0333111472427845,0.9961087703704834,0.08159475028514862,-0.029955290257930756,0.9961087703704834,0.08288556337356567,-0.026549577713012695,0.9961087703704834,0.08403842151165009,-0.02309967391192913,0.9961087703704834,0.08505140244960785,-0.01961132511496544,0.9961087703704834,0.08592282235622406,-0.016090333461761475,0.9961087703704834,0.08665123581886292,-0.012542561627924442,0.9961087703704834,0.08723542839288712,-0.00897391326725483,0.9961087703704834,0.08767442405223846,-0.005390329752117395,0.9961087703704834,0.08796749264001846,-0.0017977738752961159,0.9961087703704834,0.08811415731906891,0.0017977738752961159,0.9961087703704834,0.08811415731906891,0.005390329752117395,0.9961087703704834,0.08796749264001846,0.00897391326725483,0.9961087703704834,0.08767442405223846,0.012542561627924442,0.9961087703704834,0.08723542839288712,0.016090333461761475,0.9961087703704834,0.08665123581886292,0.01961132511496544,0.9961087703704834,0.08592282235622406,0.02309967391192913,0.9961087703704834,0.08505140244960785,0.026549577713012695,0.9961087703704834,0.08403842151165009,0.029955290257930756,0.9961087703704834,0.08288556337356567,0.0333111472427845,0.9961087703704834,0.08159475028514862,0.036611560732126236,0.9961087703704834,0.08016813546419144,0.03985103592276573,0.9961087703704834,0.07860808074474335,0.04302418604493141,0.9961087703704834,0.07691720128059387,0.04612572491168976,0.9961087703704834,0.07509829103946686,0.0491504929959774,0.9961087703704834,0.07315438985824585,0.05209345370531082,0.9961087703704834,0.07108873128890991,0.05494970828294754,0.9961087703704834,0.06890475749969482,0.05771450698375702,0.9961087703704834,0.06660608947277069,0.06038324534893036,0.9961087703704834,0.06419657170772552,0.0629514828324318,0.9961087703704834,0.06168019771575928,0.06541494280099869,0.9961087703704834,0.059061165899038315,0.06776952743530273,0.9961087703704834,0.05634383112192154,0.07001131027936935,0.9961087703704834,0.05353271961212158,0.07213657349348068,0.9961087703704834,0.05063250660896301,0.07414177060127258,0.9961087703704834,0.047648023813962936,0.07602356374263763,0.9961087703704834,0.044584233313798904,0.07777882367372513,0.9961087703704834,0.04144623503088951,0.07940462976694107,0.9961087703704834,0.0382392555475235,0.08089827746152878,0.9961087703704834,0.034968629479408264,0.08225727081298828,0.9961087703704834,0.03163980320096016,0.08347935974597931,0.9961087703704834,0.028258314356207848,0.0845625028014183,0.9961087703704834,0.024829791858792305,0.08550490438938141,0.9961087703704834,0.021359942853450775,0.08630499243736267,0.9961087703704834,0.017854543402791023,0.08696142584085464,0.9961087703704834,0.014319426380097866,0.08747313171625137,0.9961087703704834,0.010760476812720299,0.08783923834562302,0.9961087703704834,0.007183616049587727,0.08805914968252182,0.9961087703704834,0.003594799665734172,0.08813249319791794,0.9961087703704834,2.158623430535937e-17,0.01764848083257675,0.9998442530632019,0.0,0.017633793875575066,0.9998442530632019,-0.0007198565872386098,0.017589757218956947,0.9998442530632019,-0.0014385150279849768,0.017516443505883217,0.9998442530632019,-0.002154779154807329,0.017413975670933723,0.9998442530632019,-0.002867456991225481,0.017282525077462196,0.9998442530632019,-0.003575362032279372,0.017122307792305946,0.9998442530632019,-0.004277316387742758,0.016933593899011612,0.9998442530632019,-0.004972151480615139,0.016716694459319115,0.9998442530632019,-0.005658710841089487,0.016471970826387405,0.9998442530632019,-0.006335852202028036,0.016199832782149315,0.9998442530632019,-0.007002447731792927,0.015900731086730957,0.9998442530632019,-0.007657388690859079,0.015575164929032326,0.9998442530632019,-0.008299584500491619,0.015223675407469273,0.9998442530632019,-0.008927966468036175,0.014846846461296082,0.9998442530632019,-0.00954148918390274,0.014445307664573193,0.9998442530632019,-0.010139130055904388,0.014019724912941456,0.9998442530632019,-0.010719896294176579,0.013570808805525303,0.9998442530632019,-0.011282820254564285,0.013099304400384426,0.9998442530632019,-0.011826964095234871,0.012605997733771801,0.9998442530632019,-0.012351424433290958,0.012091710232198238,0.9998442530632019,-0.01285532582551241,0.011557296849787235,0.9998442530632019,-0.013337831944227219,0.011003647930920124,0.9998442530632019,-0.013798138126730919,0.010431683622300625,0.9998442530632019,-0.014235477894544601,0.009842357598245144,0.9998442530632019,-0.014649124816060066,0.009236649610102177,0.9998442530632019,-0.015038389712572098,0.008615568280220032,0.9998442530632019,-0.015402624383568764,0.007980147376656532,0.9998442530632019,-0.015741223469376564,0.007331443950533867,0.9998442530632019,-0.01605362258851528,0.006670537870377302,0.9998442530632019,-0.01633930206298828,0.005998529959470034,0.9998442530632019,-0.01659778691828251,0.005316537339240313,0.9998442530632019,-0.016828645020723343,0.004625696688890457,0.9998442530632019,-0.01703149452805519,0.003927156329154968,0.9998442530632019,-0.017205996438860893,0.003222079947590828,0.9998442530632019,-0.01735186018049717,0.002511640777811408,0.9998442530632019,-0.01746884547173977,0.001797021017409861,0.9998442530632019,-0.017556753009557724,0.0010794104309752584,0.9998442530632019,-0.017615441232919693,0.00036000317777507007,0.9998442530632019,-0.017644807696342468,-0.00036000317777507007,0.9998442530632019,-0.017644807696342468,-0.0010794104309752584,0.9998442530632019,-0.017615441232919693,-0.001797021017409861,0.9998442530632019,-0.017556753009557724,-0.002511640777811408,0.9998442530632019,-0.01746884547173977,-0.003222079947590828,0.9998442530632019,-0.01735186018049717,-0.003927156329154968,0.9998442530632019,-0.017205996438860893,-0.004625696688890457,0.9998442530632019,-0.01703149452805519,-0.005316537339240313,0.9998442530632019,-0.016828645020723343,-0.005998529959470034,0.9998442530632019,-0.01659778691828251,-0.006670537870377302,0.9998442530632019,-0.01633930206298828,-0.007331443950533867,0.9998442530632019,-0.01605362258851528,-0.007980147376656532,0.9998442530632019,-0.015741223469376564,-0.008615568280220032,0.9998442530632019,-0.015402624383568764,-0.009236649610102177,0.9998442530632019,-0.015038389712572098,-0.009842357598245144,0.9998442530632019,-0.014649124816060066,-0.010431683622300625,0.9998442530632019,-0.014235477894544601,-0.011003647930920124,0.9998442530632019,-0.013798138126730919,-0.011557296849787235,0.9998442530632019,-0.013337831944227219,-0.012091710232198238,0.9998442530632019,-0.01285532582551241,-0.012605997733771801,0.9998442530632019,-0.012351424433290958,-0.013099304400384426,0.9998442530632019,-0.011826964095234871,-0.013570808805525303,0.9998442530632019,-0.011282820254564285,-0.014019724912941456,0.9998442530632019,-0.010719896294176579,-0.014445307664573193,0.9998442530632019,-0.010139130055904388,-0.014846846461296082,0.9998442530632019,-0.00954148918390274,-0.015223675407469273,0.9998442530632019,-0.008927966468036175,-0.015575164929032326,0.9998442530632019,-0.008299584500491619,-0.015900731086730957,0.9998442530632019,-0.007657388690859079,-0.016199832782149315,0.9998442530632019,-0.007002447731792927,-0.016471970826387405,0.9998442530632019,-0.006335852202028036,-0.016716694459319115,0.9998442530632019,-0.005658710841089487,-0.016933593899011612,0.9998442530632019,-0.004972151480615139,-0.017122307792305946,0.9998442530632019,-0.004277316387742758,-0.017282525077462196,0.9998442530632019,-0.003575362032279372,-0.017413975670933723,0.9998442530632019,-0.002867456991225481,-0.017516443505883217,0.9998442530632019,-0.002154779154807329,-0.017589757218956947,0.9998442530632019,-0.0014385150279849768,-0.017633793875575066,0.9998442530632019,-0.0007198565872386098,-0.01764848083257675,0.9998442530632019,-2.1613156139165826e-18,-0.017633793875575066,0.9998442530632019,0.0007198565872386098,-0.017589757218956947,0.9998442530632019,0.0014385150279849768,-0.017516443505883217,0.9998442530632019,0.002154779154807329,-0.017413975670933723,0.9998442530632019,0.002867456991225481,-0.017282525077462196,0.9998442530632019,0.003575362032279372,-0.017122307792305946,0.9998442530632019,0.004277316387742758,-0.016933593899011612,0.9998442530632019,0.004972151480615139,-0.016716694459319115,0.9998442530632019,0.005658710841089487,-0.016471970826387405,0.9998442530632019,0.006335852202028036,-0.016199832782149315,0.9998442530632019,0.007002447731792927,-0.015900731086730957,0.9998442530632019,0.007657388690859079,-0.015575164929032326,0.9998442530632019,0.008299584500491619,-0.015223675407469273,0.9998442530632019,0.008927966468036175,-0.014846846461296082,0.9998442530632019,0.00954148918390274,-0.014445307664573193,0.9998442530632019,0.010139130055904388,-0.014019724912941456,0.9998442530632019,0.010719896294176579,-0.013570808805525303,0.9998442530632019,0.011282820254564285,-0.013099304400384426,0.9998442530632019,0.011826964095234871,-0.012605997733771801,0.9998442530632019,0.012351424433290958,-0.012091710232198238,0.9998442530632019,0.01285532582551241,-0.011557296849787235,0.9998442530632019,0.013337831944227219,-0.011003647930920124,0.9998442530632019,0.013798138126730919,-0.010431683622300625,0.9998442530632019,0.014235477894544601,-0.009842357598245144,0.9998442530632019,0.014649124816060066,-0.009236649610102177,0.9998442530632019,0.015038389712572098,-0.008615568280220032,0.9998442530632019,0.015402624383568764,-0.007980147376656532,0.9998442530632019,0.015741223469376564,-0.007331443950533867,0.9998442530632019,0.01605362258851528,-0.006670537870377302,0.9998442530632019,0.01633930206298828,-0.005998529959470034,0.9998442530632019,0.01659778691828251,-0.005316537339240313,0.9998442530632019,0.016828645020723343,-0.004625696688890457,0.9998442530632019,0.01703149452805519,-0.003927156329154968,0.9998442530632019,0.017205996438860893,-0.003222079947590828,0.9998442530632019,0.01735186018049717,-0.002511640777811408,0.9998442530632019,0.01746884547173977,-0.001797021017409861,0.9998442530632019,0.017556753009557724,-0.0010794104309752584,0.9998442530632019,0.017615441232919693,-0.00036000317777507007,0.9998442530632019,0.017644807696342468,0.00036000317777507007,0.9998442530632019,0.017644807696342468,0.0010794104309752584,0.9998442530632019,0.017615441232919693,0.001797021017409861,0.9998442530632019,0.017556753009557724,0.002511640777811408,0.9998442530632019,0.01746884547173977,0.003222079947590828,0.9998442530632019,0.01735186018049717,0.003927156329154968,0.9998442530632019,0.017205996438860893,0.004625696688890457,0.9998442530632019,0.01703149452805519,0.005316537339240313,0.9998442530632019,0.016828645020723343,0.005998529959470034,0.9998442530632019,0.01659778691828251,0.006670537870377302,0.9998442530632019,0.01633930206298828,0.007331443950533867,0.9998442530632019,0.01605362258851528,0.007980147376656532,0.9998442530632019,0.015741223469376564,0.008615568280220032,0.9998442530632019,0.015402624383568764,0.009236649610102177,0.9998442530632019,0.015038389712572098,0.009842357598245144,0.9998442530632019,0.014649124816060066,0.010431683622300625,0.9998442530632019,0.014235477894544601,0.011003647930920124,0.9998442530632019,0.013798138126730919,0.011557296849787235,0.9998442530632019,0.013337831944227219,0.012091710232198238,0.9998442530632019,0.01285532582551241,0.012605997733771801,0.9998442530632019,0.012351424433290958,0.013099304400384426,0.9998442530632019,0.011826964095234871,0.013570808805525303,0.9998442530632019,0.011282820254564285,0.014019724912941456,0.9998442530632019,0.010719896294176579,0.014445307664573193,0.9998442530632019,0.010139130055904388,0.014846846461296082,0.9998442530632019,0.00954148918390274,0.015223675407469273,0.9998442530632019,0.008927966468036175,0.015575164929032326,0.9998442530632019,0.008299584500491619,0.015900731086730957,0.9998442530632019,0.007657388690859079,0.016199832782149315,0.9998442530632019,0.007002447731792927,0.016471970826387405,0.9998442530632019,0.006335852202028036,0.016716694459319115,0.9998442530632019,0.005658710841089487,0.016933593899011612,0.9998442530632019,0.004972151480615139,0.017122307792305946,0.9998442530632019,0.004277316387742758,0.017282525077462196,0.9998442530632019,0.003575362032279372,0.017413975670933723,0.9998442530632019,0.002867456991225481,0.017516443505883217,0.9998442530632019,0.002154779154807329,0.017589757218956947,0.9998442530632019,0.0014385150279849768,0.017633793875575066,0.9998442530632019,0.0007198565872386098,0.01764848083257675,0.9998442530632019,4.322631227833165e-18,-0.05292345583438873,0.998598575592041,0.0,-0.05287941172719002,0.998598575592041,0.002158672781661153,-0.05274735391139984,0.998598575592041,0.0043137529864907265,-0.05252750590443611,0.998598575592041,0.006461652927100658,-0.05222023278474808,0.998598575592041,0.00859879795461893,-0.05182604119181633,0.998598575592041,0.010721632279455662,-0.0513455905020237,0.998598575592041,0.012826620601117611,-0.05077968165278435,0.998598575592041,0.01491026021540165,-0.050129253417253494,0.998598575592041,0.01696908287703991,-0.04939539358019829,0.998598575592041,0.01899966225028038,-0.04857931658625603,0.998598575592041,0.020998619496822357,-0.04768238589167595,0.998598575592041,0.022962626069784164,-0.04670609161257744,0.998598575592041,0.0248884130269289,-0.045652057975530624,0.998598575592041,0.02677277661859989,-0.04452204331755638,0.998598575592041,0.02861258015036583,-0.043317925184965134,0.998598575592041,0.03040475957095623,-0.042041707783937454,0.998598575592041,0.032146334648132324,-0.040695518255233765,0.998598575592041,0.03383440151810646,-0.03928159177303314,0.998598575592041,0.03546615689992905,-0.03780229017138481,0.998598575592041,0.037038885056972504,-0.03626006469130516,0.998598575592041,0.038549963384866714,-0.034657493233680725,0.998598575592041,0.03999687731266022,-0.03299723565578461,0.998598575592041,0.041377224028110504,-0.031282056123018265,0.998598575592041,0.04268869757652283,-0.02951481007039547,0.998598575592041,0.043929122388362885,-0.027698442339897156,0.998598575592041,0.04509643465280533,-0.025835970416665077,0.998598575592041,0.04618868604302406,-0.023930499330163002,0.998598575592041,0.04720405861735344,-0.021985197439789772,0.998598575592041,0.04814086854457855,-0.0200033038854599,0.998598575592041,0.04899755120277405,-0.017988115549087524,0.998598575592041,0.049772679805755615,-0.01594298891723156,0.998598575592041,0.050464969128370285,-0.013871326111257076,0.998598575592041,0.051073264330625534,-0.01177657674998045,0.998598575592041,0.05159655213356018,-0.009662225842475891,0.998598575592041,0.05203396454453468,-0.0075317928567528725,0.998598575592041,0.05238477140665054,-0.0053888242691755295,0.998598575592041,0.0526483878493309,-0.0032368863467127085,0.998598575592041,0.05282437428832054,-0.0010795610724017024,0.998598575592041,0.05291244387626648,0.0010795610724017024,0.998598575592041,0.05291244387626648,0.0032368863467127085,0.998598575592041,0.05282437428832054,0.0053888242691755295,0.998598575592041,0.0526483878493309,0.0075317928567528725,0.998598575592041,0.05238477140665054,0.009662225842475891,0.998598575592041,0.05203396454453468,0.01177657674998045,0.998598575592041,0.05159655213356018,0.013871326111257076,0.998598575592041,0.051073264330625534,0.01594298891723156,0.998598575592041,0.050464969128370285,0.017988115549087524,0.998598575592041,0.049772679805755615,0.0200033038854599,0.998598575592041,0.04899755120277405,0.021985197439789772,0.998598575592041,0.04814086854457855,0.023930499330163002,0.998598575592041,0.04720405861735344,0.025835970416665077,0.998598575592041,0.04618868604302406,0.027698442339897156,0.998598575592041,0.04509643465280533,0.02951481007039547,0.998598575592041,0.043929122388362885,0.031282056123018265,0.998598575592041,0.04268869757652283,0.03299723565578461,0.998598575592041,0.041377224028110504,0.034657493233680725,0.998598575592041,0.03999687731266022,0.03626006469130516,0.998598575592041,0.038549963384866714,0.03780229017138481,0.998598575592041,0.037038885056972504,0.03928159177303314,0.998598575592041,0.03546615689992905,0.040695518255233765,0.998598575592041,0.03383440151810646,0.042041707783937454,0.998598575592041,0.032146334648132324,0.043317925184965134,0.998598575592041,0.03040475957095623,0.04452204331755638,0.998598575592041,0.02861258015036583,0.045652057975530624,0.998598575592041,0.02677277661859989,0.04670609161257744,0.998598575592041,0.0248884130269289,0.04768238589167595,0.998598575592041,0.022962626069784164,0.04857931658625603,0.998598575592041,0.020998619496822357,0.04939539358019829,0.998598575592041,0.01899966225028038,0.050129253417253494,0.998598575592041,0.01696908287703991,0.05077968165278435,0.998598575592041,0.01491026021540165,0.0513455905020237,0.998598575592041,0.012826620601117611,0.05182604119181633,0.998598575592041,0.010721632279455662,0.05222023278474808,0.998598575592041,0.00859879795461893,0.05252750590443611,0.998598575592041,0.006461652927100658,0.05274735391139984,0.998598575592041,0.0043137529864907265,0.05287941172719002,0.998598575592041,0.002158672781661153,0.05292345583438873,0.998598575592041,6.481253748470428e-18,0.05287941172719002,0.998598575592041,-0.002158672781661153,0.05274735391139984,0.998598575592041,-0.0043137529864907265,0.05252750590443611,0.998598575592041,-0.006461652927100658,0.05222023278474808,0.998598575592041,-0.00859879795461893,0.05182604119181633,0.998598575592041,-0.010721632279455662,0.0513455905020237,0.998598575592041,-0.012826620601117611,0.05077968165278435,0.998598575592041,-0.01491026021540165,0.050129253417253494,0.998598575592041,-0.01696908287703991,0.04939539358019829,0.998598575592041,-0.01899966225028038,0.04857931658625603,0.998598575592041,-0.020998619496822357,0.04768238589167595,0.998598575592041,-0.022962626069784164,0.04670609161257744,0.998598575592041,-0.0248884130269289,0.045652057975530624,0.998598575592041,-0.02677277661859989,0.04452204331755638,0.998598575592041,-0.02861258015036583,0.043317925184965134,0.998598575592041,-0.03040475957095623,0.042041707783937454,0.998598575592041,-0.032146334648132324,0.040695518255233765,0.998598575592041,-0.03383440151810646,0.03928159177303314,0.998598575592041,-0.03546615689992905,0.03780229017138481,0.998598575592041,-0.037038885056972504,0.03626006469130516,0.998598575592041,-0.038549963384866714,0.034657493233680725,0.998598575592041,-0.03999687731266022,0.03299723565578461,0.998598575592041,-0.041377224028110504,0.031282056123018265,0.998598575592041,-0.04268869757652283,0.02951481007039547,0.998598575592041,-0.043929122388362885,0.027698442339897156,0.998598575592041,-0.04509643465280533,0.025835970416665077,0.998598575592041,-0.04618868604302406,0.023930499330163002,0.998598575592041,-0.04720405861735344,0.021985197439789772,0.998598575592041,-0.04814086854457855,0.0200033038854599,0.998598575592041,-0.04899755120277405,0.017988115549087524,0.998598575592041,-0.049772679805755615,0.01594298891723156,0.998598575592041,-0.050464969128370285,0.013871326111257076,0.998598575592041,-0.051073264330625534,0.01177657674998045,0.998598575592041,-0.05159655213356018,0.009662225842475891,0.998598575592041,-0.05203396454453468,0.0075317928567528725,0.998598575592041,-0.05238477140665054,0.0053888242691755295,0.998598575592041,-0.0526483878493309,0.0032368863467127085,0.998598575592041,-0.05282437428832054,0.0010795610724017024,0.998598575592041,-0.05291244387626648,-0.0010795610724017024,0.998598575592041,-0.05291244387626648,-0.0032368863467127085,0.998598575592041,-0.05282437428832054,-0.0053888242691755295,0.998598575592041,-0.0526483878493309,-0.0075317928567528725,0.998598575592041,-0.05238477140665054,-0.009662225842475891,0.998598575592041,-0.05203396454453468,-0.01177657674998045,0.998598575592041,-0.05159655213356018,-0.013871326111257076,0.998598575592041,-0.051073264330625534,-0.01594298891723156,0.998598575592041,-0.050464969128370285,-0.017988115549087524,0.998598575592041,-0.049772679805755615,-0.0200033038854599,0.998598575592041,-0.04899755120277405,-0.021985197439789772,0.998598575592041,-0.04814086854457855,-0.023930499330163002,0.998598575592041,-0.04720405861735344,-0.025835970416665077,0.998598575592041,-0.04618868604302406,-0.027698442339897156,0.998598575592041,-0.04509643465280533,-0.02951481007039547,0.998598575592041,-0.043929122388362885,-0.031282056123018265,0.998598575592041,-0.04268869757652283,-0.03299723565578461,0.998598575592041,-0.041377224028110504,-0.034657493233680725,0.998598575592041,-0.03999687731266022,-0.03626006469130516,0.998598575592041,-0.038549963384866714,-0.03780229017138481,0.998598575592041,-0.037038885056972504,-0.03928159177303314,0.998598575592041,-0.03546615689992905,-0.040695518255233765,0.998598575592041,-0.03383440151810646,-0.042041707783937454,0.998598575592041,-0.032146334648132324,-0.043317925184965134,0.998598575592041,-0.03040475957095623,-0.04452204331755638,0.998598575592041,-0.02861258015036583,-0.045652057975530624,0.998598575592041,-0.02677277661859989,-0.04670609161257744,0.998598575592041,-0.0248884130269289,-0.04768238589167595,0.998598575592041,-0.022962626069784164,-0.04857931658625603,0.998598575592041,-0.020998619496822357,-0.04939539358019829,0.998598575592041,-0.01899966225028038,-0.050129253417253494,0.998598575592041,-0.01696908287703991,-0.05077968165278435,0.998598575592041,-0.01491026021540165,-0.0513455905020237,0.998598575592041,-0.012826620601117611,-0.05182604119181633,0.998598575592041,-0.010721632279455662,-0.05222023278474808,0.998598575592041,-0.00859879795461893,-0.05252750590443611,0.998598575592041,-0.006461652927100658,-0.05274735391139984,0.998598575592041,-0.0043137529864907265,-0.05287941172719002,0.998598575592041,-0.002158672781661153,-0.05292345583438873,0.998598575592041,-1.2962507496940856e-17,-0.12323172390460968,0.9923779368400574,0.0,-0.12312917411327362,0.9923779368400574,0.005026448052376509,-0.12282168120145798,0.9923779368400574,0.010044530034065247,-0.12230976670980453,0.9923779368400574,0.015045893378555775,-0.12159428000450134,0.9923779368400574,0.020022215321660042,-0.12067641317844391,0.9923779368400574,0.024965211749076843,-0.1195576936006546,0.9923779368400574,0.0298666562885046,-0.11823997646570206,0.9923779368400574,0.03471839055418968,-0.11672545969486237,0.9923779368400574,0.03951233997941017,-0.11501666903495789,0.9923779368400574,0.044240523129701614,-0.11311644315719604,0.9923779368400574,0.04889507219195366,-0.11102794855833054,0.9923779368400574,0.0534682422876358,-0.10875465720891953,0.9923779368400574,0.05795241892337799,-0.10630035400390625,0.9923779368400574,0.0623401403427124,-0.10366912931203842,0.9923779368400574,0.06662410497665405,-0.10086534917354584,0.9923779368400574,0.07079717516899109,-0.09789369255304337,0.9923779368400574,0.07485241442918777,-0.09475909918546677,0.9923779368400574,0.0787830650806427,-0.09146679192781448,0.9923779368400574,0.08258259296417236,-0.08802224695682526,0.9923779368400574,0.08624466508626938,-0.08443119376897812,0.9923779368400574,0.08976319432258606,-0.080699622631073,0.9923779368400574,0.09313232451677322,-0.07683372497558594,0.9923779368400574,0.09634644538164139,-0.07283994555473328,0.9923779368400574,0.09940019994974136,-0.06872493773698807,0.9923779368400574,0.10228852182626724,-0.06449554115533829,0.9923779368400574,0.10500659048557281,-0.06015879660844803,0.9923779368400574,0.10754988342523575,-0.05572192370891571,0.9923779368400574,0.10991417616605759,-0.05119230970740318,0.9923779368400574,0.11209551990032196,-0.046577490866184235,0.9923779368400574,0.11409030109643936,-0.041885145008563995,0.9923779368400574,0.11589518189430237,-0.03712308779358864,0.9923779368400574,0.11750716716051102,-0.03229924291372299,0.9923779368400574,0.11892357468605042,-0.02742163836956024,0.9923779368400574,0.12014205008745193,-0.022498393431305885,0.9923779368400574,0.12116055935621262,-0.01753770373761654,0.9923779368400574,0.12197740375995636,-0.012547822669148445,0.9923779368400574,0.12259123474359512,-0.0075370571576058865,0.9923779368400574,0.12300102412700653,-0.002513746963813901,0.9923779368400574,0.12320608645677567,0.002513746963813901,0.9923779368400574,0.12320608645677567,0.0075370571576058865,0.9923779368400574,0.12300102412700653,0.012547822669148445,0.9923779368400574,0.12259123474359512,0.01753770373761654,0.9923779368400574,0.12197740375995636,0.022498393431305885,0.9923779368400574,0.12116055935621262,0.02742163836956024,0.9923779368400574,0.12014205008745193,0.03229924291372299,0.9923779368400574,0.11892357468605042,0.03712308779358864,0.9923779368400574,0.11750716716051102,0.041885145008563995,0.9923779368400574,0.11589518189430237,0.046577490866184235,0.9923779368400574,0.11409030109643936,0.05119230970740318,0.9923779368400574,0.11209551990032196,0.05572192370891571,0.9923779368400574,0.10991417616605759,0.06015879660844803,0.9923779368400574,0.10754988342523575,0.06449554115533829,0.9923779368400574,0.10500659048557281,0.06872493773698807,0.9923779368400574,0.10228852182626724,0.07283994555473328,0.9923779368400574,0.09940019994974136,0.07683372497558594,0.9923779368400574,0.09634644538164139,0.080699622631073,0.9923779368400574,0.09313232451677322,0.08443119376897812,0.9923779368400574,0.08976319432258606,0.08802224695682526,0.9923779368400574,0.08624466508626938,0.09146679192781448,0.9923779368400574,0.08258259296417236,0.09475909918546677,0.9923779368400574,0.0787830650806427,0.09789369255304337,0.9923779368400574,0.07485241442918777,0.10086534917354584,0.9923779368400574,0.07079717516899109,0.10366912931203842,0.9923779368400574,0.06662410497665405,0.10630035400390625,0.9923779368400574,0.0623401403427124,0.10875465720891953,0.9923779368400574,0.05795241892337799,0.11102794855833054,0.9923779368400574,0.0534682422876358,0.11311644315719604,0.9923779368400574,0.04889507219195366,0.11501666903495789,0.9923779368400574,0.044240523129701614,0.11672545969486237,0.9923779368400574,0.03951233997941017,0.11823997646570206,0.9923779368400574,0.03471839055418968,0.1195576936006546,0.9923779368400574,0.0298666562885046,0.12067641317844391,0.9923779368400574,0.024965211749076843,0.12159428000450134,0.9923779368400574,0.020022215321660042,0.12230976670980453,0.9923779368400574,0.015045893378555775,0.12282168120145798,0.9923779368400574,0.010044530034065247,0.12312917411327362,0.9923779368400574,0.005026448052376509,0.12323172390460968,0.9923779368400574,1.509153473603189e-17,0.12312917411327362,0.9923779368400574,-0.005026448052376509,0.12282168120145798,0.9923779368400574,-0.010044530034065247,0.12230976670980453,0.9923779368400574,-0.015045893378555775,0.12159428000450134,0.9923779368400574,-0.020022215321660042,0.12067641317844391,0.9923779368400574,-0.024965211749076843,0.1195576936006546,0.9923779368400574,-0.0298666562885046,0.11823997646570206,0.9923779368400574,-0.03471839055418968,0.11672545969486237,0.9923779368400574,-0.03951233997941017,0.11501666903495789,0.9923779368400574,-0.044240523129701614,0.11311644315719604,0.9923779368400574,-0.04889507219195366,0.11102794855833054,0.9923779368400574,-0.0534682422876358,0.10875465720891953,0.9923779368400574,-0.05795241892337799,0.10630035400390625,0.9923779368400574,-0.0623401403427124,0.10366912931203842,0.9923779368400574,-0.06662410497665405,0.10086534917354584,0.9923779368400574,-0.07079717516899109,0.09789369255304337,0.9923779368400574,-0.07485241442918777,0.09475909918546677,0.9923779368400574,-0.0787830650806427,0.09146679192781448,0.9923779368400574,-0.08258259296417236,0.08802224695682526,0.9923779368400574,-0.08624466508626938,0.08443119376897812,0.9923779368400574,-0.08976319432258606,0.080699622631073,0.9923779368400574,-0.09313232451677322,0.07683372497558594,0.9923779368400574,-0.09634644538164139,0.07283994555473328,0.9923779368400574,-0.09940019994974136,0.06872493773698807,0.9923779368400574,-0.10228852182626724,0.06449554115533829,0.9923779368400574,-0.10500659048557281,0.06015879660844803,0.9923779368400574,-0.10754988342523575,0.05572192370891571,0.9923779368400574,-0.10991417616605759,0.05119230970740318,0.9923779368400574,-0.11209551990032196,0.046577490866184235,0.9923779368400574,-0.11409030109643936,0.041885145008563995,0.9923779368400574,-0.11589518189430237,0.03712308779358864,0.9923779368400574,-0.11750716716051102,0.03229924291372299,0.9923779368400574,-0.11892357468605042,0.02742163836956024,0.9923779368400574,-0.12014205008745193,0.022498393431305885,0.9923779368400574,-0.12116055935621262,0.01753770373761654,0.9923779368400574,-0.12197740375995636,0.012547822669148445,0.9923779368400574,-0.12259123474359512,0.0075370571576058865,0.9923779368400574,-0.12300102412700653,0.002513746963813901,0.9923779368400574,-0.12320608645677567,-0.002513746963813901,0.9923779368400574,-0.12320608645677567,-0.0075370571576058865,0.9923779368400574,-0.12300102412700653,-0.012547822669148445,0.9923779368400574,-0.12259123474359512,-0.01753770373761654,0.9923779368400574,-0.12197740375995636,-0.022498393431305885,0.9923779368400574,-0.12116055935621262,-0.02742163836956024,0.9923779368400574,-0.12014205008745193,-0.03229924291372299,0.9923779368400574,-0.11892357468605042,-0.03712308779358864,0.9923779368400574,-0.11750716716051102,-0.041885145008563995,0.9923779368400574,-0.11589518189430237,-0.046577490866184235,0.9923779368400574,-0.11409030109643936,-0.05119230970740318,0.9923779368400574,-0.11209551990032196,-0.05572192370891571,0.9923779368400574,-0.10991417616605759,-0.06015879660844803,0.9923779368400574,-0.10754988342523575,-0.06449554115533829,0.9923779368400574,-0.10500659048557281,-0.06872493773698807,0.9923779368400574,-0.10228852182626724,-0.07283994555473328,0.9923779368400574,-0.09940019994974136,-0.07683372497558594,0.9923779368400574,-0.09634644538164139,-0.080699622631073,0.9923779368400574,-0.09313232451677322,-0.08443119376897812,0.9923779368400574,-0.08976319432258606,-0.08802224695682526,0.9923779368400574,-0.08624466508626938,-0.09146679192781448,0.9923779368400574,-0.08258259296417236,-0.09475909918546677,0.9923779368400574,-0.0787830650806427,-0.09789369255304337,0.9923779368400574,-0.07485241442918777,-0.10086534917354584,0.9923779368400574,-0.07079717516899109,-0.10366912931203842,0.9923779368400574,-0.06662410497665405,-0.10630035400390625,0.9923779368400574,-0.0623401403427124,-0.10875465720891953,0.9923779368400574,-0.05795241892337799,-0.11102794855833054,0.9923779368400574,-0.0534682422876358,-0.11311644315719604,0.9923779368400574,-0.04889507219195366,-0.11501666903495789,0.9923779368400574,-0.044240523129701614,-0.11672545969486237,0.9923779368400574,-0.03951233997941017,-0.11823997646570206,0.9923779368400574,-0.03471839055418968,-0.1195576936006546,0.9923779368400574,-0.0298666562885046,-0.12067641317844391,0.9923779368400574,-0.024965211749076843,-0.12159428000450134,0.9923779368400574,-0.020022215321660042,-0.12230976670980453,0.9923779368400574,-0.015045893378555775,-0.12282168120145798,0.9923779368400574,-0.010044530034065247,-0.12312917411327362,0.9923779368400574,-0.005026448052376509,-0.12323172390460968,0.9923779368400574,-3.018306947206378e-17,-0.1929260641336441,0.9812132716178894,0.0,-0.1927655190229416,0.9812132716178894,0.007869181223213673,-0.19228412210941315,0.9812132716178894,0.015725266188383102,-0.19148269295692444,0.9812132716178894,0.023555176332592964,-0.19036255776882172,0.9812132716178894,0.03134588152170181,-0.1889255791902542,0.9812132716178894,0.03908441588282585,-0.18717415630817413,0.9812132716178894,0.04675789549946785,-0.18511120975017548,0.9812132716178894,0.05435355380177498,-0.18274015188217163,0.9812132716178894,0.06185874342918396,-0.18006494641304016,0.9812132716178894,0.06926097720861435,-0.17709003388881683,0.9812132716178894,0.07654793560504913,-0.1738203763961792,0.9812132716178894,0.08370748162269592,-0.170261412858963,0.9812132716178894,0.09072770923376083,-0.16641907393932343,0.9812132716178894,0.09759692847728729,-0.1622997373342514,0.9812132716178894,0.10430370271205902,-0.1579102724790573,0.9812132716178894,0.11083687841892242,-0.15325798094272614,0.9812132716178894,0.117185577750206,-0.14835059642791748,0.9812132716178894,0.12333923578262329,-0.14319631457328796,0.9812132716178894,0.12928760051727295,-0.13780368864536285,0.9812132716178894,0.13502077758312225,-0.13218168914318085,0.9812132716178894,0.14052923023700714,-0.12633970379829407,0.9812132716178894,0.1458037942647934,-0.12028743326663971,0.9812132716178894,0.15083567798137665,-0.11403495818376541,0.9812132716178894,0.15561650693416595,-0.10759267956018448,0.9812132716178894,0.1601383239030838,-0.10097132623195648,0.9812132716178894,0.1643936038017273,-0.09418191760778427,0.9812132716178894,0.16837528347969055,-0.08723574876785278,0.9812132716178894,0.17207670211791992,-0.08014438301324844,0.9812132716178894,0.17549172043800354,-0.07291962951421738,0.9812132716178894,0.17861464619636536,-0.06557350605726242,0.9812132716178894,0.1814402937889099,-0.058118242770433426,0.9812132716178894,0.18396395444869995,-0.05056624487042427,0.9812132716178894,0.1861814260482788,-0.04293008893728256,0.9812132716178894,0.1880890130996704,-0.035222478210926056,0.9812132716178894,0.18968354165554047,-0.027456242591142654,0.9812132716178894,0.19096235930919647,-0.01964430883526802,0.9812132716178894,0.1919233351945877,-0.011799678206443787,0.9812132716178894,0.19256488978862762,-0.003935409709811211,0.9812132716178894,0.19288592040538788,0.003935409709811211,0.9812132716178894,0.19288592040538788,0.011799678206443787,0.9812132716178894,0.19256488978862762,0.01964430883526802,0.9812132716178894,0.1919233351945877,0.027456242591142654,0.9812132716178894,0.19096235930919647,0.035222478210926056,0.9812132716178894,0.18968354165554047,0.04293008893728256,0.9812132716178894,0.1880890130996704,0.05056624487042427,0.9812132716178894,0.1861814260482788,0.058118242770433426,0.9812132716178894,0.18396395444869995,0.06557350605726242,0.9812132716178894,0.1814402937889099,0.07291962951421738,0.9812132716178894,0.17861464619636536,0.08014438301324844,0.9812132716178894,0.17549172043800354,0.08723574876785278,0.9812132716178894,0.17207670211791992,0.09418191760778427,0.9812132716178894,0.16837528347969055,0.10097132623195648,0.9812132716178894,0.1643936038017273,0.10759267956018448,0.9812132716178894,0.1601383239030838,0.11403495818376541,0.9812132716178894,0.15561650693416595,0.12028743326663971,0.9812132716178894,0.15083567798137665,0.12633970379829407,0.9812132716178894,0.1458037942647934,0.13218168914318085,0.9812132716178894,0.14052923023700714,0.13780368864536285,0.9812132716178894,0.13502077758312225,0.14319631457328796,0.9812132716178894,0.12928760051727295,0.14835059642791748,0.9812132716178894,0.12333923578262329,0.15325798094272614,0.9812132716178894,0.117185577750206,0.1579102724790573,0.9812132716178894,0.11083687841892242,0.1622997373342514,0.9812132716178894,0.10430370271205902,0.16641907393932343,0.9812132716178894,0.09759692847728729,0.170261412858963,0.9812132716178894,0.09072770923376083,0.1738203763961792,0.9812132716178894,0.08370748162269592,0.17709003388881683,0.9812132716178894,0.07654793560504913,0.18006494641304016,0.9812132716178894,0.06926097720861435,0.18274015188217163,0.9812132716178894,0.06185874342918396,0.18511120975017548,0.9812132716178894,0.05435355380177498,0.18717415630817413,0.9812132716178894,0.04675789549946785,0.1889255791902542,0.9812132716178894,0.03908441588282585,0.19036255776882172,0.9812132716178894,0.03134588152170181,0.19148269295692444,0.9812132716178894,0.023555176332592964,0.19228412210941315,0.9812132716178894,0.015725266188383102,0.1927655190229416,0.9812132716178894,0.007869181223213673,0.1929260641336441,0.9812132716178894,2.3626629141815346e-17,0.1927655190229416,0.9812132716178894,-0.007869181223213673,0.19228412210941315,0.9812132716178894,-0.015725266188383102,0.19148269295692444,0.9812132716178894,-0.023555176332592964,0.19036255776882172,0.9812132716178894,-0.03134588152170181,0.1889255791902542,0.9812132716178894,-0.03908441588282585,0.18717415630817413,0.9812132716178894,-0.04675789549946785,0.18511120975017548,0.9812132716178894,-0.05435355380177498,0.18274015188217163,0.9812132716178894,-0.06185874342918396,0.18006494641304016,0.9812132716178894,-0.06926097720861435,0.17709003388881683,0.9812132716178894,-0.07654793560504913,0.1738203763961792,0.9812132716178894,-0.08370748162269592,0.170261412858963,0.9812132716178894,-0.09072770923376083,0.16641907393932343,0.9812132716178894,-0.09759692847728729,0.1622997373342514,0.9812132716178894,-0.10430370271205902,0.1579102724790573,0.9812132716178894,-0.11083687841892242,0.15325798094272614,0.9812132716178894,-0.117185577750206,0.14835059642791748,0.9812132716178894,-0.12333923578262329,0.14319631457328796,0.9812132716178894,-0.12928760051727295,0.13780368864536285,0.9812132716178894,-0.13502077758312225,0.13218168914318085,0.9812132716178894,-0.14052923023700714,0.12633970379829407,0.9812132716178894,-0.1458037942647934,0.12028743326663971,0.9812132716178894,-0.15083567798137665,0.11403495818376541,0.9812132716178894,-0.15561650693416595,0.10759267956018448,0.9812132716178894,-0.1601383239030838,0.10097132623195648,0.9812132716178894,-0.1643936038017273,0.09418191760778427,0.9812132716178894,-0.16837528347969055,0.08723574876785278,0.9812132716178894,-0.17207670211791992,0.08014438301324844,0.9812132716178894,-0.17549172043800354,0.07291962951421738,0.9812132716178894,-0.17861464619636536,0.06557350605726242,0.9812132716178894,-0.1814402937889099,0.058118242770433426,0.9812132716178894,-0.18396395444869995,0.05056624487042427,0.9812132716178894,-0.1861814260482788,0.04293008893728256,0.9812132716178894,-0.1880890130996704,0.035222478210926056,0.9812132716178894,-0.18968354165554047,0.027456242591142654,0.9812132716178894,-0.19096235930919647,0.01964430883526802,0.9812132716178894,-0.1919233351945877,0.011799678206443787,0.9812132716178894,-0.19256488978862762,0.003935409709811211,0.9812132716178894,-0.19288592040538788,-0.003935409709811211,0.9812132716178894,-0.19288592040538788,-0.011799678206443787,0.9812132716178894,-0.19256488978862762,-0.01964430883526802,0.9812132716178894,-0.1919233351945877,-0.027456242591142654,0.9812132716178894,-0.19096235930919647,-0.035222478210926056,0.9812132716178894,-0.18968354165554047,-0.04293008893728256,0.9812132716178894,-0.1880890130996704,-0.05056624487042427,0.9812132716178894,-0.1861814260482788,-0.058118242770433426,0.9812132716178894,-0.18396395444869995,-0.06557350605726242,0.9812132716178894,-0.1814402937889099,-0.07291962951421738,0.9812132716178894,-0.17861464619636536,-0.08014438301324844,0.9812132716178894,-0.17549172043800354,-0.08723574876785278,0.9812132716178894,-0.17207670211791992,-0.09418191760778427,0.9812132716178894,-0.16837528347969055,-0.10097132623195648,0.9812132716178894,-0.1643936038017273,-0.10759267956018448,0.9812132716178894,-0.1601383239030838,-0.11403495818376541,0.9812132716178894,-0.15561650693416595,-0.12028743326663971,0.9812132716178894,-0.15083567798137665,-0.12633970379829407,0.9812132716178894,-0.1458037942647934,-0.13218168914318085,0.9812132716178894,-0.14052923023700714,-0.13780368864536285,0.9812132716178894,-0.13502077758312225,-0.14319631457328796,0.9812132716178894,-0.12928760051727295,-0.14835059642791748,0.9812132716178894,-0.12333923578262329,-0.15325798094272614,0.9812132716178894,-0.117185577750206,-0.1579102724790573,0.9812132716178894,-0.11083687841892242,-0.1622997373342514,0.9812132716178894,-0.10430370271205902,-0.16641907393932343,0.9812132716178894,-0.09759692847728729,-0.170261412858963,0.9812132716178894,-0.09072770923376083,-0.1738203763961792,0.9812132716178894,-0.08370748162269592,-0.17709003388881683,0.9812132716178894,-0.07654793560504913,-0.18006494641304016,0.9812132716178894,-0.06926097720861435,-0.18274015188217163,0.9812132716178894,-0.06185874342918396,-0.18511120975017548,0.9812132716178894,-0.05435355380177498,-0.18717415630817413,0.9812132716178894,-0.04675789549946785,-0.1889255791902542,0.9812132716178894,-0.03908441588282585,-0.19036255776882172,0.9812132716178894,-0.03134588152170181,-0.19148269295692444,0.9812132716178894,-0.023555176332592964,-0.19228412210941315,0.9812132716178894,-0.015725266188383102,-0.1927655190229416,0.9812132716178894,-0.007869181223213673,-0.1929260641336441,0.9812132716178894,-4.725325828363069e-17,-0.26165926456451416,0.9651603102684021,0.0,-0.26144149899482727,0.9651603102684021,0.010672710835933685,-0.26078861951828003,0.9651603102684021,0.021327657625079155,-0.2597016394138336,0.9651603102684021,0.03194710984826088,-0.25818246603012085,0.9651603102684021,0.04251338541507721,-0.2562335431575775,0.9651603102684021,0.053008902817964554,-0.25385814905166626,0.9651603102684021,0.06341619044542313,-0.25106021761894226,0.9651603102684021,0.07371792942285538,-0.24784442782402039,0.9651603102684021,0.08389697223901749,-0.24421614408493042,0.9651603102684021,0.09393637627363205,-0.24018137156963348,0.9651603102684021,0.10381943732500076,-0.2357468456029892,0.9651603102684021,0.11352969706058502,-0.2309199422597885,0.9651603102684021,0.1230509951710701,-0.22570869326591492,0.9651603102684021,0.13236749172210693,-0.22012177109718323,0.9651603102684021,0.14146366715431213,-0.2141684889793396,0.9651603102684021,0.15032440423965454,-0.20785872638225555,0.9651603102684021,0.15893493592739105,-0.20120301842689514,0.9651603102684021,0.16728092730045319,-0.1942124217748642,0.9651603102684021,0.17534850537776947,-0.1868985891342163,0.9651603102684021,0.18312422931194305,-0.17927366495132446,0.9651603102684021,0.19059516489505768,-0.1713503748178482,0.9651603102684021,0.19774886965751648,-0.1631418764591217,0.9651603102684021,0.20457343757152557,-0.1546618491411209,0.9651603102684021,0.21105752885341644,-0.1459244042634964,0.9651603102684021,0.21719032526016235,-0.13694407045841217,0.9651603102684021,0.22296161949634552,-0.12773582339286804,0.9651603102684021,0.2283618301153183,-0.1183149665594101,0.9651603102684021,0.2333819568157196,-0.10869718343019485,0.9651603102684021,0.2380136251449585,-0.09889848530292511,0.9651603102684021,0.24224916100502014,-0.08893518149852753,0.9651603102684021,0.24608148634433746,-0.07882385700941086,0.9651603102684021,0.2495042383670807,-0.06858132779598236,0.9651603102684021,0.2525117099285126,-0.05822466313838959,0.9651603102684021,0.2550989091396332,-0.04777108505368233,0.9651603102684021,0.2572615146636963,-0.037237994372844696,0.9651603102684021,0.2589959502220154,-0.026642926037311554,0.9651603102684021,0.26029929518699646,-0.01600351557135582,0.9651603102684021,0.2611694037914276,-0.005337466020137072,0.9651603102684021,0.26160481572151184,0.005337466020137072,0.9651603102684021,0.26160481572151184,0.01600351557135582,0.9651603102684021,0.2611694037914276,0.026642926037311554,0.9651603102684021,0.26029929518699646,0.037237994372844696,0.9651603102684021,0.2589959502220154,0.04777108505368233,0.9651603102684021,0.2572615146636963,0.05822466313838959,0.9651603102684021,0.2550989091396332,0.06858132779598236,0.9651603102684021,0.2525117099285126,0.07882385700941086,0.9651603102684021,0.2495042383670807,0.08893518149852753,0.9651603102684021,0.24608148634433746,0.09889848530292511,0.9651603102684021,0.24224916100502014,0.10869718343019485,0.9651603102684021,0.2380136251449585,0.1183149665594101,0.9651603102684021,0.2333819568157196,0.12773582339286804,0.9651603102684021,0.2283618301153183,0.13694407045841217,0.9651603102684021,0.22296161949634552,0.1459244042634964,0.9651603102684021,0.21719032526016235,0.1546618491411209,0.9651603102684021,0.21105752885341644,0.1631418764591217,0.9651603102684021,0.20457343757152557,0.1713503748178482,0.9651603102684021,0.19774886965751648,0.17927366495132446,0.9651603102684021,0.19059516489505768,0.1868985891342163,0.9651603102684021,0.18312422931194305,0.1942124217748642,0.9651603102684021,0.17534850537776947,0.20120301842689514,0.9651603102684021,0.16728092730045319,0.20785872638225555,0.9651603102684021,0.15893493592739105,0.2141684889793396,0.9651603102684021,0.15032440423965454,0.22012177109718323,0.9651603102684021,0.14146366715431213,0.22570869326591492,0.9651603102684021,0.13236749172210693,0.2309199422597885,0.9651603102684021,0.1230509951710701,0.2357468456029892,0.9651603102684021,0.11352969706058502,0.24018137156963348,0.9651603102684021,0.10381943732500076,0.24421614408493042,0.9651603102684021,0.09393637627363205,0.24784442782402039,0.9651603102684021,0.08389697223901749,0.25106021761894226,0.9651603102684021,0.07371792942285538,0.25385814905166626,0.9651603102684021,0.06341619044542313,0.2562335431575775,0.9651603102684021,0.053008902817964554,0.25818246603012085,0.9651603102684021,0.04251338541507721,0.2597016394138336,0.9651603102684021,0.03194710984826088,0.26078861951828003,0.9651603102684021,0.021327657625079155,0.26144149899482727,0.9651603102684021,0.010672710835933685,0.26165926456451416,0.9651603102684021,3.204401740079373e-17,0.26144149899482727,0.9651603102684021,-0.010672710835933685,0.26078861951828003,0.9651603102684021,-0.021327657625079155,0.2597016394138336,0.9651603102684021,-0.03194710984826088,0.25818246603012085,0.9651603102684021,-0.04251338541507721,0.2562335431575775,0.9651603102684021,-0.053008902817964554,0.25385814905166626,0.9651603102684021,-0.06341619044542313,0.25106021761894226,0.9651603102684021,-0.07371792942285538,0.24784442782402039,0.9651603102684021,-0.08389697223901749,0.24421614408493042,0.9651603102684021,-0.09393637627363205,0.24018137156963348,0.9651603102684021,-0.10381943732500076,0.2357468456029892,0.9651603102684021,-0.11352969706058502,0.2309199422597885,0.9651603102684021,-0.1230509951710701,0.22570869326591492,0.9651603102684021,-0.13236749172210693,0.22012177109718323,0.9651603102684021,-0.14146366715431213,0.2141684889793396,0.9651603102684021,-0.15032440423965454,0.20785872638225555,0.9651603102684021,-0.15893493592739105,0.20120301842689514,0.9651603102684021,-0.16728092730045319,0.1942124217748642,0.9651603102684021,-0.17534850537776947,0.1868985891342163,0.9651603102684021,-0.18312422931194305,0.17927366495132446,0.9651603102684021,-0.19059516489505768,0.1713503748178482,0.9651603102684021,-0.19774886965751648,0.1631418764591217,0.9651603102684021,-0.20457343757152557,0.1546618491411209,0.9651603102684021,-0.21105752885341644,0.1459244042634964,0.9651603102684021,-0.21719032526016235,0.13694407045841217,0.9651603102684021,-0.22296161949634552,0.12773582339286804,0.9651603102684021,-0.2283618301153183,0.1183149665594101,0.9651603102684021,-0.2333819568157196,0.10869718343019485,0.9651603102684021,-0.2380136251449585,0.09889848530292511,0.9651603102684021,-0.24224916100502014,0.08893518149852753,0.9651603102684021,-0.24608148634433746,0.07882385700941086,0.9651603102684021,-0.2495042383670807,0.06858132779598236,0.9651603102684021,-0.2525117099285126,0.05822466313838959,0.9651603102684021,-0.2550989091396332,0.04777108505368233,0.9651603102684021,-0.2572615146636963,0.037237994372844696,0.9651603102684021,-0.2589959502220154,0.026642926037311554,0.9651603102684021,-0.26029929518699646,0.01600351557135582,0.9651603102684021,-0.2611694037914276,0.005337466020137072,0.9651603102684021,-0.26160481572151184,-0.005337466020137072,0.9651603102684021,-0.26160481572151184,-0.01600351557135582,0.9651603102684021,-0.2611694037914276,-0.026642926037311554,0.9651603102684021,-0.26029929518699646,-0.037237994372844696,0.9651603102684021,-0.2589959502220154,-0.04777108505368233,0.9651603102684021,-0.2572615146636963,-0.05822466313838959,0.9651603102684021,-0.2550989091396332,-0.06858132779598236,0.9651603102684021,-0.2525117099285126,-0.07882385700941086,0.9651603102684021,-0.2495042383670807,-0.08893518149852753,0.9651603102684021,-0.24608148634433746,-0.09889848530292511,0.9651603102684021,-0.24224916100502014,-0.10869718343019485,0.9651603102684021,-0.2380136251449585,-0.1183149665594101,0.9651603102684021,-0.2333819568157196,-0.12773582339286804,0.9651603102684021,-0.2283618301153183,-0.13694407045841217,0.9651603102684021,-0.22296161949634552,-0.1459244042634964,0.9651603102684021,-0.21719032526016235,-0.1546618491411209,0.9651603102684021,-0.21105752885341644,-0.1631418764591217,0.9651603102684021,-0.20457343757152557,-0.1713503748178482,0.9651603102684021,-0.19774886965751648,-0.17927366495132446,0.9651603102684021,-0.19059516489505768,-0.1868985891342163,0.9651603102684021,-0.18312422931194305,-0.1942124217748642,0.9651603102684021,-0.17534850537776947,-0.20120301842689514,0.9651603102684021,-0.16728092730045319,-0.20785872638225555,0.9651603102684021,-0.15893493592739105,-0.2141684889793396,0.9651603102684021,-0.15032440423965454,-0.22012177109718323,0.9651603102684021,-0.14146366715431213,-0.22570869326591492,0.9651603102684021,-0.13236749172210693,-0.2309199422597885,0.9651603102684021,-0.1230509951710701,-0.2357468456029892,0.9651603102684021,-0.11352969706058502,-0.24018137156963348,0.9651603102684021,-0.10381943732500076,-0.24421614408493042,0.9651603102684021,-0.09393637627363205,-0.24784442782402039,0.9651603102684021,-0.08389697223901749,-0.25106021761894226,0.9651603102684021,-0.07371792942285538,-0.25385814905166626,0.9651603102684021,-0.06341619044542313,-0.2562335431575775,0.9651603102684021,-0.053008902817964554,-0.25818246603012085,0.9651603102684021,-0.04251338541507721,-0.2597016394138336,0.9651603102684021,-0.03194710984826088,-0.26078861951828003,0.9651603102684021,-0.021327657625079155,-0.26144149899482727,0.9651603102684021,-0.010672710835933685,-0.26165926456451416,0.9651603102684021,-6.408803480158746e-17,-0.32908886671066284,0.9442989826202393,0.0,-0.3288150131702423,0.9442989826202393,0.013423069380223751,-0.32799386978149414,0.9442989826202393,0.026823798194527626,-0.32662680745124817,0.9442989826202393,0.04017988219857216,-0.32471609115600586,0.9442989826202393,0.053469087928533554,-0.3222649395465851,0.9442989826202393,0.06666930019855499,-0.31927740573883057,0.9442989826202393,0.07975855469703674,-0.31575846672058105,0.9442989826202393,0.09271504729986191,-0.31171396374702454,0.9442989826202393,0.10551723092794418,-0.307150661945343,0.9442989826202393,0.11814379692077637,-0.302076131105423,0.9442989826202393,0.13057371973991394,-0.2964988350868225,0.9442989826202393,0.14278630912303925,-0.2904280424118042,0.9442989826202393,0.15476125478744507,-0.28387385606765747,0.9442989826202393,0.16647860407829285,-0.2768471837043762,0.9442989826202393,0.17791888117790222,-0.2693597376346588,0.9442989826202393,0.18906301259994507,-0.2614239454269409,0.9442989826202393,0.19989247620105743,-0.25305306911468506,0.9442989826202393,0.21038924157619476,-0.2442609965801239,0.9442989826202393,0.22053582966327667,-0.235062375664711,0.9442989826202393,0.23031537234783173,-0.22547250986099243,0.9442989826202393,0.23971155285835266,-0.2155073881149292,0.9442989826202393,0.24870876967906952,-0.20518355071544647,0.9442989826202393,0.2572920322418213,-0.19451822340488434,0.9442989826202393,0.26544708013534546,-0.18352913856506348,0.9442989826202393,0.27316027879714966,-0.17223457992076874,0.9442989826202393,0.28041887283325195,-0.16065335273742676,0.9442989826202393,0.28721070289611816,-0.14880473911762238,0.9442989826202393,0.29352450370788574,-0.13670845329761505,0.9442989826202393,0.29934975504875183,-0.12438463419675827,0.9442989826202393,0.3046768009662628,-0.11185378581285477,0.9442989826202393,0.3094967007637024,-0.09913676977157593,0.9442989826202393,0.31380149722099304,-0.08625474572181702,0.9442989826202393,0.31758400797843933,-0.07322916388511658,0.9442989826202393,0.32083791494369507,-0.06008169800043106,0.9442989826202393,0.32355785369873047,-0.04683423042297363,0.9442989826202393,0.3257392346858978,-0.03350881114602089,0.9442989826202393,0.32737845182418823,-0.02012762241065502,0.9442989826202393,0.328472763299942,-0.006712931673973799,0.9442989826202393,0.3290204107761383,0.006712931673973799,0.9442989826202393,0.3290204107761383,0.02012762241065502,0.9442989826202393,0.328472763299942,0.03350881114602089,0.9442989826202393,0.32737845182418823,0.04683423042297363,0.9442989826202393,0.3257392346858978,0.06008169800043106,0.9442989826202393,0.32355785369873047,0.07322916388511658,0.9442989826202393,0.32083791494369507,0.08625474572181702,0.9442989826202393,0.31758400797843933,0.09913676977157593,0.9442989826202393,0.31380149722099304,0.11185378581285477,0.9442989826202393,0.3094967007637024,0.12438463419675827,0.9442989826202393,0.3046768009662628,0.13670845329761505,0.9442989826202393,0.29934975504875183,0.14880473911762238,0.9442989826202393,0.29352450370788574,0.16065335273742676,0.9442989826202393,0.28721070289611816,0.17223457992076874,0.9442989826202393,0.28041887283325195,0.18352913856506348,0.9442989826202393,0.27316027879714966,0.19451822340488434,0.9442989826202393,0.26544708013534546,0.20518355071544647,0.9442989826202393,0.2572920322418213,0.2155073881149292,0.9442989826202393,0.24870876967906952,0.22547250986099243,0.9442989826202393,0.23971155285835266,0.235062375664711,0.9442989826202393,0.23031537234783173,0.2442609965801239,0.9442989826202393,0.22053582966327667,0.25305306911468506,0.9442989826202393,0.21038924157619476,0.2614239454269409,0.9442989826202393,0.19989247620105743,0.2693597376346588,0.9442989826202393,0.18906301259994507,0.2768471837043762,0.9442989826202393,0.17791888117790222,0.28387385606765747,0.9442989826202393,0.16647860407829285,0.2904280424118042,0.9442989826202393,0.15476125478744507,0.2964988350868225,0.9442989826202393,0.14278630912303925,0.302076131105423,0.9442989826202393,0.13057371973991394,0.307150661945343,0.9442989826202393,0.11814379692077637,0.31171396374702454,0.9442989826202393,0.10551723092794418,0.31575846672058105,0.9442989826202393,0.09271504729986191,0.31927740573883057,0.9442989826202393,0.07975855469703674,0.3222649395465851,0.9442989826202393,0.06666930019855499,0.32471609115600586,0.9442989826202393,0.053469087928533554,0.32662680745124817,0.9442989826202393,0.04017988219857216,0.32799386978149414,0.9442989826202393,0.026823798194527626,0.3288150131702423,0.9442989826202393,0.013423069380223751,0.32908886671066284,0.9442989826202393,4.0301764764633056e-17,0.3288150131702423,0.9442989826202393,-0.013423069380223751,0.32799386978149414,0.9442989826202393,-0.026823798194527626,0.32662680745124817,0.9442989826202393,-0.04017988219857216,0.32471609115600586,0.9442989826202393,-0.053469087928533554,0.3222649395465851,0.9442989826202393,-0.06666930019855499,0.31927740573883057,0.9442989826202393,-0.07975855469703674,0.31575846672058105,0.9442989826202393,-0.09271504729986191,0.31171396374702454,0.9442989826202393,-0.10551723092794418,0.307150661945343,0.9442989826202393,-0.11814379692077637,0.302076131105423,0.9442989826202393,-0.13057371973991394,0.2964988350868225,0.9442989826202393,-0.14278630912303925,0.2904280424118042,0.9442989826202393,-0.15476125478744507,0.28387385606765747,0.9442989826202393,-0.16647860407829285,0.2768471837043762,0.9442989826202393,-0.17791888117790222,0.2693597376346588,0.9442989826202393,-0.18906301259994507,0.2614239454269409,0.9442989826202393,-0.19989247620105743,0.25305306911468506,0.9442989826202393,-0.21038924157619476,0.2442609965801239,0.9442989826202393,-0.22053582966327667,0.235062375664711,0.9442989826202393,-0.23031537234783173,0.22547250986099243,0.9442989826202393,-0.23971155285835266,0.2155073881149292,0.9442989826202393,-0.24870876967906952,0.20518355071544647,0.9442989826202393,-0.2572920322418213,0.19451822340488434,0.9442989826202393,-0.26544708013534546,0.18352913856506348,0.9442989826202393,-0.27316027879714966,0.17223457992076874,0.9442989826202393,-0.28041887283325195,0.16065335273742676,0.9442989826202393,-0.28721070289611816,0.14880473911762238,0.9442989826202393,-0.29352450370788574,0.13670845329761505,0.9442989826202393,-0.29934975504875183,0.12438463419675827,0.9442989826202393,-0.3046768009662628,0.11185378581285477,0.9442989826202393,-0.3094967007637024,0.09913676977157593,0.9442989826202393,-0.31380149722099304,0.08625474572181702,0.9442989826202393,-0.31758400797843933,0.07322916388511658,0.9442989826202393,-0.32083791494369507,0.06008169800043106,0.9442989826202393,-0.32355785369873047,0.04683423042297363,0.9442989826202393,-0.3257392346858978,0.03350881114602089,0.9442989826202393,-0.32737845182418823,0.02012762241065502,0.9442989826202393,-0.328472763299942,0.006712931673973799,0.9442989826202393,-0.3290204107761383,-0.006712931673973799,0.9442989826202393,-0.3290204107761383,-0.02012762241065502,0.9442989826202393,-0.328472763299942,-0.03350881114602089,0.9442989826202393,-0.32737845182418823,-0.04683423042297363,0.9442989826202393,-0.3257392346858978,-0.06008169800043106,0.9442989826202393,-0.32355785369873047,-0.07322916388511658,0.9442989826202393,-0.32083791494369507,-0.08625474572181702,0.9442989826202393,-0.31758400797843933,-0.09913676977157593,0.9442989826202393,-0.31380149722099304,-0.11185378581285477,0.9442989826202393,-0.3094967007637024,-0.12438463419675827,0.9442989826202393,-0.3046768009662628,-0.13670845329761505,0.9442989826202393,-0.29934975504875183,-0.14880473911762238,0.9442989826202393,-0.29352450370788574,-0.16065335273742676,0.9442989826202393,-0.28721070289611816,-0.17223457992076874,0.9442989826202393,-0.28041887283325195,-0.18352913856506348,0.9442989826202393,-0.27316027879714966,-0.19451822340488434,0.9442989826202393,-0.26544708013534546,-0.20518355071544647,0.9442989826202393,-0.2572920322418213,-0.2155073881149292,0.9442989826202393,-0.24870876967906952,-0.22547250986099243,0.9442989826202393,-0.23971155285835266,-0.235062375664711,0.9442989826202393,-0.23031537234783173,-0.2442609965801239,0.9442989826202393,-0.22053582966327667,-0.25305306911468506,0.9442989826202393,-0.21038924157619476,-0.2614239454269409,0.9442989826202393,-0.19989247620105743,-0.2693597376346588,0.9442989826202393,-0.18906301259994507,-0.2768471837043762,0.9442989826202393,-0.17791888117790222,-0.28387385606765747,0.9442989826202393,-0.16647860407829285,-0.2904280424118042,0.9442989826202393,-0.15476125478744507,-0.2964988350868225,0.9442989826202393,-0.14278630912303925,-0.302076131105423,0.9442989826202393,-0.13057371973991394,-0.307150661945343,0.9442989826202393,-0.11814379692077637,-0.31171396374702454,0.9442989826202393,-0.10551723092794418,-0.31575846672058105,0.9442989826202393,-0.09271504729986191,-0.31927740573883057,0.9442989826202393,-0.07975855469703674,-0.3222649395465851,0.9442989826202393,-0.06666930019855499,-0.32471609115600586,0.9442989826202393,-0.053469087928533554,-0.32662680745124817,0.9442989826202393,-0.04017988219857216,-0.32799386978149414,0.9442989826202393,-0.026823798194527626,-0.3288150131702423,0.9442989826202393,-0.013423069380223751,-0.32908886671066284,0.9442989826202393,-8.060352952926611e-17,-0.39487898349761963,0.9187331199645996,0.0,-0.3945503532886505,0.9187331199645996,0.016106555238366127,-0.3935650587081909,0.9187331199645996,0.03218630328774452,-0.3919247090816498,0.9187331199645996,0.048212479799985886,-0.3896320164203644,0.9187331199645996,0.06415840983390808,-0.38669082522392273,0.9187331199645996,0.07999756187200546,-0.3831060528755188,0.9187331199645996,0.09570355713367462,-0.37888360023498535,0.9187331199645996,0.11125026643276215,-0.37403056025505066,0.9187331199645996,0.12661181390285492,-0.3685549795627594,0.9187331199645996,0.1417626291513443,-0.3624659776687622,0.9187331199645996,0.1566774845123291,-0.3557736873626709,0.9187331199645996,0.17133156955242157,-0.3484892249107361,0.9187331199645996,0.18570049107074738,-0.34062474966049194,0.9187331199645996,0.1997603327035904,-0.3321933448314667,0.9187331199645996,0.21348769962787628,-0.3232090175151825,0.9187331199645996,0.22685973346233368,-0.3136867582798004,0.9187331199645996,0.23985417187213898,-0.3036423921585083,0.9187331199645996,0.2524494230747223,-0.29309266805648804,0.9187331199645996,0.2646244764328003,-0.2820550799369812,0.9187331199645996,0.27635908126831055,-0.2705480754375458,0.9187331199645996,0.2876337170600891,-0.2585907280445099,0.9187331199645996,0.2984296381473541,-0.24620302021503448,0.9187331199645996,0.30872881412506104,-0.23340551555156708,0.9187331199645996,0.3185141682624817,-0.22021953761577606,0.9187331199645996,0.32776939868927,-0.20666702091693878,0.9187331199645996,0.3364790678024292,-0.19277052581310272,0.9187331199645996,0.3446286916732788,-0.17855317890644073,0.9187331199645996,0.35220474004745483,-0.16403865814208984,0.9187331199645996,0.3591945469379425,-0.14925110340118408,0.9187331199645996,0.3655865490436554,-0.13421514630317688,0.9187331199645996,0.3713700473308563,-0.11895579099655151,0.9187331199645996,0.37653544545173645,-0.1034984439611435,0.9187331199645996,0.38107413053512573,-0.0878688395023346,0.9187331199645996,0.3849785327911377,-0.07209298759698868,0.9187331199645996,0.3882422149181366,-0.056197140365839005,0.9187331199645996,0.3908596932888031,-0.04020775854587555,0.9187331199645996,0.3928266167640686,-0.024151453748345375,0.9187331199645996,0.39413973689079285,-0.008054953999817371,0.9187331199645996,0.39479681849479675,0.008054953999817371,0.9187331199645996,0.39479681849479675,0.024151453748345375,0.9187331199645996,0.39413973689079285,0.04020775854587555,0.9187331199645996,0.3928266167640686,0.056197140365839005,0.9187331199645996,0.3908596932888031,0.07209298759698868,0.9187331199645996,0.3882422149181366,0.0878688395023346,0.9187331199645996,0.3849785327911377,0.1034984439611435,0.9187331199645996,0.38107413053512573,0.11895579099655151,0.9187331199645996,0.37653544545173645,0.13421514630317688,0.9187331199645996,0.3713700473308563,0.14925110340118408,0.9187331199645996,0.3655865490436554,0.16403865814208984,0.9187331199645996,0.3591945469379425,0.17855317890644073,0.9187331199645996,0.35220474004745483,0.19277052581310272,0.9187331199645996,0.3446286916732788,0.20666702091693878,0.9187331199645996,0.3364790678024292,0.22021953761577606,0.9187331199645996,0.32776939868927,0.23340551555156708,0.9187331199645996,0.3185141682624817,0.24620302021503448,0.9187331199645996,0.30872881412506104,0.2585907280445099,0.9187331199645996,0.2984296381473541,0.2705480754375458,0.9187331199645996,0.2876337170600891,0.2820550799369812,0.9187331199645996,0.27635908126831055,0.29309266805648804,0.9187331199645996,0.2646244764328003,0.3036423921585083,0.9187331199645996,0.2524494230747223,0.3136867582798004,0.9187331199645996,0.23985417187213898,0.3232090175151825,0.9187331199645996,0.22685973346233368,0.3321933448314667,0.9187331199645996,0.21348769962787628,0.34062474966049194,0.9187331199645996,0.1997603327035904,0.3484892249107361,0.9187331199645996,0.18570049107074738,0.3557736873626709,0.9187331199645996,0.17133156955242157,0.3624659776687622,0.9187331199645996,0.1566774845123291,0.3685549795627594,0.9187331199645996,0.1417626291513443,0.37403056025505066,0.9187331199645996,0.12661181390285492,0.37888360023498535,0.9187331199645996,0.11125026643276215,0.3831060528755188,0.9187331199645996,0.09570355713367462,0.38669082522392273,0.9187331199645996,0.07999756187200546,0.3896320164203644,0.9187331199645996,0.06415840983390808,0.3919247090816498,0.9187331199645996,0.048212479799985886,0.3935650587081909,0.9187331199645996,0.03218630328774452,0.3945503532886505,0.9187331199645996,0.016106555238366127,0.39487898349761963,0.9187331199645996,4.835872892402616e-17,0.3945503532886505,0.9187331199645996,-0.016106555238366127,0.3935650587081909,0.9187331199645996,-0.03218630328774452,0.3919247090816498,0.9187331199645996,-0.048212479799985886,0.3896320164203644,0.9187331199645996,-0.06415840983390808,0.38669082522392273,0.9187331199645996,-0.07999756187200546,0.3831060528755188,0.9187331199645996,-0.09570355713367462,0.37888360023498535,0.9187331199645996,-0.11125026643276215,0.37403056025505066,0.9187331199645996,-0.12661181390285492,0.3685549795627594,0.9187331199645996,-0.1417626291513443,0.3624659776687622,0.9187331199645996,-0.1566774845123291,0.3557736873626709,0.9187331199645996,-0.17133156955242157,0.3484892249107361,0.9187331199645996,-0.18570049107074738,0.34062474966049194,0.9187331199645996,-0.1997603327035904,0.3321933448314667,0.9187331199645996,-0.21348769962787628,0.3232090175151825,0.9187331199645996,-0.22685973346233368,0.3136867582798004,0.9187331199645996,-0.23985417187213898,0.3036423921585083,0.9187331199645996,-0.2524494230747223,0.29309266805648804,0.9187331199645996,-0.2646244764328003,0.2820550799369812,0.9187331199645996,-0.27635908126831055,0.2705480754375458,0.9187331199645996,-0.2876337170600891,0.2585907280445099,0.9187331199645996,-0.2984296381473541,0.24620302021503448,0.9187331199645996,-0.30872881412506104,0.23340551555156708,0.9187331199645996,-0.3185141682624817,0.22021953761577606,0.9187331199645996,-0.32776939868927,0.20666702091693878,0.9187331199645996,-0.3364790678024292,0.19277052581310272,0.9187331199645996,-0.3446286916732788,0.17855317890644073,0.9187331199645996,-0.35220474004745483,0.16403865814208984,0.9187331199645996,-0.3591945469379425,0.14925110340118408,0.9187331199645996,-0.3655865490436554,0.13421514630317688,0.9187331199645996,-0.3713700473308563,0.11895579099655151,0.9187331199645996,-0.37653544545173645,0.1034984439611435,0.9187331199645996,-0.38107413053512573,0.0878688395023346,0.9187331199645996,-0.3849785327911377,0.07209298759698868,0.9187331199645996,-0.3882422149181366,0.056197140365839005,0.9187331199645996,-0.3908596932888031,0.04020775854587555,0.9187331199645996,-0.3928266167640686,0.024151453748345375,0.9187331199645996,-0.39413973689079285,0.008054953999817371,0.9187331199645996,-0.39479681849479675,-0.008054953999817371,0.9187331199645996,-0.39479681849479675,-0.024151453748345375,0.9187331199645996,-0.39413973689079285,-0.04020775854587555,0.9187331199645996,-0.3928266167640686,-0.056197140365839005,0.9187331199645996,-0.3908596932888031,-0.07209298759698868,0.9187331199645996,-0.3882422149181366,-0.0878688395023346,0.9187331199645996,-0.3849785327911377,-0.1034984439611435,0.9187331199645996,-0.38107413053512573,-0.11895579099655151,0.9187331199645996,-0.37653544545173645,-0.13421514630317688,0.9187331199645996,-0.3713700473308563,-0.14925110340118408,0.9187331199645996,-0.3655865490436554,-0.16403865814208984,0.9187331199645996,-0.3591945469379425,-0.17855317890644073,0.9187331199645996,-0.35220474004745483,-0.19277052581310272,0.9187331199645996,-0.3446286916732788,-0.20666702091693878,0.9187331199645996,-0.3364790678024292,-0.22021953761577606,0.9187331199645996,-0.32776939868927,-0.23340551555156708,0.9187331199645996,-0.3185141682624817,-0.24620302021503448,0.9187331199645996,-0.30872881412506104,-0.2585907280445099,0.9187331199645996,-0.2984296381473541,-0.2705480754375458,0.9187331199645996,-0.2876337170600891,-0.2820550799369812,0.9187331199645996,-0.27635908126831055,-0.29309266805648804,0.9187331199645996,-0.2646244764328003,-0.3036423921585083,0.9187331199645996,-0.2524494230747223,-0.3136867582798004,0.9187331199645996,-0.23985417187213898,-0.3232090175151825,0.9187331199645996,-0.22685973346233368,-0.3321933448314667,0.9187331199645996,-0.21348769962787628,-0.34062474966049194,0.9187331199645996,-0.1997603327035904,-0.3484892249107361,0.9187331199645996,-0.18570049107074738,-0.3557736873626709,0.9187331199645996,-0.17133156955242157,-0.3624659776687622,0.9187331199645996,-0.1566774845123291,-0.3685549795627594,0.9187331199645996,-0.1417626291513443,-0.37403056025505066,0.9187331199645996,-0.12661181390285492,-0.37888360023498535,0.9187331199645996,-0.11125026643276215,-0.3831060528755188,0.9187331199645996,-0.09570355713367462,-0.38669082522392273,0.9187331199645996,-0.07999756187200546,-0.3896320164203644,0.9187331199645996,-0.06415840983390808,-0.3919247090816498,0.9187331199645996,-0.048212479799985886,-0.3935650587081909,0.9187331199645996,-0.03218630328774452,-0.3945503532886505,0.9187331199645996,-0.016106555238366127,-0.39487898349761963,0.9187331199645996,-9.671745784805232e-17,-0.45870181918144226,0.8885902762413025,0.0,-0.45832011103630066,0.8885902762413025,0.018709799274802208,-0.4571755528450012,0.8885902762413025,0.03738845884799957,-0.45527005195617676,0.8885902762413025,0.05600488558411598,-0.45260682702064514,0.8885902762413025,0.07452809810638428,-0.44919025897979736,0.8885902762413025,0.09292726963758469,-0.44502606987953186,0.8885902762413025,0.11117177456617355,-0.4401211738586426,0.8885902762413025,0.12923124432563782,-0.43448376655578613,0.8885902762413025,0.1470756083726883,-0.4281231760978699,0.8885902762413025,0.16467519104480743,-0.4210500121116638,0.8885902762413025,0.18200069665908813,-0.4132760763168335,0.8885902762413025,0.1990232616662979,-0.4048142731189728,0.8885902762413025,0.21571458876132965,-0.39567869901657104,0.8885902762413025,0.23204687237739563,-0.38588452339172363,0.8885902762413025,0.24799293279647827,-0.3754481077194214,0.8885902762413025,0.26352623105049133,-0.36438679695129395,0.8885902762413025,0.2786209285259247,-0.3527190089225769,0.8885902762413025,0.2932518720626831,-0.34046414494514465,0.8885902762413025,0.30739474296569824,-0.32764261960983276,0.8885902762413025,0.3210259974002838,-0.3142757713794708,0.8885902762413025,0.334122896194458,-0.30038583278656006,0.8885902762413025,0.3466637134552002,-0.2859959006309509,0.8885902762413025,0.3586275279521942,-0.2711299955844879,0.8885902762413025,0.36999446153640747,-0.2558128237724304,0.8885902762413025,0.3807455599308014,-0.24006985127925873,0.8885902762413025,0.39086294174194336,-0.2239273190498352,0.8885902762413025,0.4003297686576843,-0.20741207897663116,0.8885902762413025,0.4091303050518036,-0.19055162370204926,0.8885902762413025,0.417249858379364,-0.1733740121126175,0.8885902762413025,0.42467495799064636,-0.15590783953666687,0.8885902762413025,0.43139323592185974,-0.13818217813968658,0.8885902762413025,0.4373934864997864,-0.12022652477025986,0.8885902762413025,0.4426657259464264,-0.10207076370716095,0.8885902762413025,0.4472012221813202,-0.08374511450529099,0.8885902762413025,0.4509923756122589,-0.06528007984161377,0.8885902762413025,0.45403289794921875,-0.046706389635801315,0.8885902762413025,0.4563177525997162,-0.028054965659976006,0.8885902762413025,0.4578430950641632,-0.00935684610158205,0.8885902762413025,0.45860639214515686,0.00935684610158205,0.8885902762413025,0.45860639214515686,0.028054965659976006,0.8885902762413025,0.4578430950641632,0.046706389635801315,0.8885902762413025,0.4563177525997162,0.06528007984161377,0.8885902762413025,0.45403289794921875,0.08374511450529099,0.8885902762413025,0.4509923756122589,0.10207076370716095,0.8885902762413025,0.4472012221813202,0.12022652477025986,0.8885902762413025,0.4426657259464264,0.13818217813968658,0.8885902762413025,0.4373934864997864,0.15590783953666687,0.8885902762413025,0.43139323592185974,0.1733740121126175,0.8885902762413025,0.42467495799064636,0.19055162370204926,0.8885902762413025,0.417249858379364,0.20741207897663116,0.8885902762413025,0.4091303050518036,0.2239273190498352,0.8885902762413025,0.4003297686576843,0.24006985127925873,0.8885902762413025,0.39086294174194336,0.2558128237724304,0.8885902762413025,0.3807455599308014,0.2711299955844879,0.8885902762413025,0.36999446153640747,0.2859959006309509,0.8885902762413025,0.3586275279521942,0.30038583278656006,0.8885902762413025,0.3466637134552002,0.3142757713794708,0.8885902762413025,0.334122896194458,0.32764261960983276,0.8885902762413025,0.3210259974002838,0.34046414494514465,0.8885902762413025,0.30739474296569824,0.3527190089225769,0.8885902762413025,0.2932518720626831,0.36438679695129395,0.8885902762413025,0.2786209285259247,0.3754481077194214,0.8885902762413025,0.26352623105049133,0.38588452339172363,0.8885902762413025,0.24799293279647827,0.39567869901657104,0.8885902762413025,0.23204687237739563,0.4048142731189728,0.8885902762413025,0.21571458876132965,0.4132760763168335,0.8885902762413025,0.1990232616662979,0.4210500121116638,0.8885902762413025,0.18200069665908813,0.4281231760978699,0.8885902762413025,0.16467519104480743,0.43448376655578613,0.8885902762413025,0.1470756083726883,0.4401211738586426,0.8885902762413025,0.12923124432563782,0.44502606987953186,0.8885902762413025,0.11117177456617355,0.44919025897979736,0.8885902762413025,0.09292726963758469,0.45260682702064514,0.8885902762413025,0.07452809810638428,0.45527005195617676,0.8885902762413025,0.05600488558411598,0.4571755528450012,0.8885902762413025,0.03738845884799957,0.45832011103630066,0.8885902762413025,0.018709799274802208,0.45870181918144226,0.8885902762413025,5.617477507565197e-17,0.45832011103630066,0.8885902762413025,-0.018709799274802208,0.4571755528450012,0.8885902762413025,-0.03738845884799957,0.45527005195617676,0.8885902762413025,-0.05600488558411598,0.45260682702064514,0.8885902762413025,-0.07452809810638428,0.44919025897979736,0.8885902762413025,-0.09292726963758469,0.44502606987953186,0.8885902762413025,-0.11117177456617355,0.4401211738586426,0.8885902762413025,-0.12923124432563782,0.43448376655578613,0.8885902762413025,-0.1470756083726883,0.4281231760978699,0.8885902762413025,-0.16467519104480743,0.4210500121116638,0.8885902762413025,-0.18200069665908813,0.4132760763168335,0.8885902762413025,-0.1990232616662979,0.4048142731189728,0.8885902762413025,-0.21571458876132965,0.39567869901657104,0.8885902762413025,-0.23204687237739563,0.38588452339172363,0.8885902762413025,-0.24799293279647827,0.3754481077194214,0.8885902762413025,-0.26352623105049133,0.36438679695129395,0.8885902762413025,-0.2786209285259247,0.3527190089225769,0.8885902762413025,-0.2932518720626831,0.34046414494514465,0.8885902762413025,-0.30739474296569824,0.32764261960983276,0.8885902762413025,-0.3210259974002838,0.3142757713794708,0.8885902762413025,-0.334122896194458,0.30038583278656006,0.8885902762413025,-0.3466637134552002,0.2859959006309509,0.8885902762413025,-0.3586275279521942,0.2711299955844879,0.8885902762413025,-0.36999446153640747,0.2558128237724304,0.8885902762413025,-0.3807455599308014,0.24006985127925873,0.8885902762413025,-0.39086294174194336,0.2239273190498352,0.8885902762413025,-0.4003297686576843,0.20741207897663116,0.8885902762413025,-0.4091303050518036,0.19055162370204926,0.8885902762413025,-0.417249858379364,0.1733740121126175,0.8885902762413025,-0.42467495799064636,0.15590783953666687,0.8885902762413025,-0.43139323592185974,0.13818217813968658,0.8885902762413025,-0.4373934864997864,0.12022652477025986,0.8885902762413025,-0.4426657259464264,0.10207076370716095,0.8885902762413025,-0.4472012221813202,0.08374511450529099,0.8885902762413025,-0.4509923756122589,0.06528007984161377,0.8885902762413025,-0.45403289794921875,0.046706389635801315,0.8885902762413025,-0.4563177525997162,0.028054965659976006,0.8885902762413025,-0.4578430950641632,0.00935684610158205,0.8885902762413025,-0.45860639214515686,-0.00935684610158205,0.8885902762413025,-0.45860639214515686,-0.028054965659976006,0.8885902762413025,-0.4578430950641632,-0.046706389635801315,0.8885902762413025,-0.4563177525997162,-0.06528007984161377,0.8885902762413025,-0.45403289794921875,-0.08374511450529099,0.8885902762413025,-0.4509923756122589,-0.10207076370716095,0.8885902762413025,-0.4472012221813202,-0.12022652477025986,0.8885902762413025,-0.4426657259464264,-0.13818217813968658,0.8885902762413025,-0.4373934864997864,-0.15590783953666687,0.8885902762413025,-0.43139323592185974,-0.1733740121126175,0.8885902762413025,-0.42467495799064636,-0.19055162370204926,0.8885902762413025,-0.417249858379364,-0.20741207897663116,0.8885902762413025,-0.4091303050518036,-0.2239273190498352,0.8885902762413025,-0.4003297686576843,-0.24006985127925873,0.8885902762413025,-0.39086294174194336,-0.2558128237724304,0.8885902762413025,-0.3807455599308014,-0.2711299955844879,0.8885902762413025,-0.36999446153640747,-0.2859959006309509,0.8885902762413025,-0.3586275279521942,-0.30038583278656006,0.8885902762413025,-0.3466637134552002,-0.3142757713794708,0.8885902762413025,-0.334122896194458,-0.32764261960983276,0.8885902762413025,-0.3210259974002838,-0.34046414494514465,0.8885902762413025,-0.30739474296569824,-0.3527190089225769,0.8885902762413025,-0.2932518720626831,-0.36438679695129395,0.8885902762413025,-0.2786209285259247,-0.3754481077194214,0.8885902762413025,-0.26352623105049133,-0.38588452339172363,0.8885902762413025,-0.24799293279647827,-0.39567869901657104,0.8885902762413025,-0.23204687237739563,-0.4048142731189728,0.8885902762413025,-0.21571458876132965,-0.4132760763168335,0.8885902762413025,-0.1990232616662979,-0.4210500121116638,0.8885902762413025,-0.18200069665908813,-0.4281231760978699,0.8885902762413025,-0.16467519104480743,-0.43448376655578613,0.8885902762413025,-0.1470756083726883,-0.4401211738586426,0.8885902762413025,-0.12923124432563782,-0.44502606987953186,0.8885902762413025,-0.11117177456617355,-0.44919025897979736,0.8885902762413025,-0.09292726963758469,-0.45260682702064514,0.8885902762413025,-0.07452809810638428,-0.45527005195617676,0.8885902762413025,-0.05600488558411598,-0.4571755528450012,0.8885902762413025,-0.03738845884799957,-0.45832011103630066,0.8885902762413025,-0.018709799274802208,-0.45870181918144226,0.8885902762413025,-1.1234955015130394e-16,-0.5202394723892212,0.8540204167366028,0.0,-0.5198065042495728,0.8540204167366028,0.021219830960035324,-0.5185083746910095,0.8540204167366028,0.04240434616804123,-0.5163472890853882,0.8540204167366028,0.06351827830076218,-0.5133267641067505,0.8540204167366028,0.08452649414539337,-0.5094518661499023,0.8540204167366028,0.10539402812719345,-0.5047290325164795,0.8540204167366028,0.12608613073825836,-0.4991661012172699,0.8540204167366028,0.1465683877468109,-0.4927723705768585,0.8540204167366028,0.16680669784545898,-0.48555848002433777,0.8540204167366028,0.1867673695087433,-0.4775364100933075,0.8540204167366028,0.20641718804836273,-0.4687195420265198,0.8540204167366028,0.22572343051433563,-0.45912253856658936,0.8540204167366028,0.24465399980545044,-0.44876137375831604,0.8540204167366028,0.26317736506462097,-0.4376532733440399,0.8540204167366028,0.28126266598701477,-0.42581674456596375,0.8540204167366028,0.29887986183166504,-0.4132714867591858,0.8540204167366028,0.3159995973110199,-0.40003839135169983,0.8540204167366028,0.3325933814048767,-0.38613948225975037,0.8540204167366028,0.34863361716270447,-0.37159785628318787,0.8540204167366028,0.3640935719013214,-0.3564377427101135,0.8540204167366028,0.37894749641418457,-0.3406843841075897,0.8540204167366028,0.3931707441806793,-0.32436397671699524,0.8540204167366028,0.40673959255218506,-0.30750373005867004,0.8540204167366028,0.4196314513683319,-0.29013165831565857,0.8540204167366028,0.43182486295700073,-0.2722766697406769,0.8540204167366028,0.4432995617389679,-0.2539685070514679,0.8540204167366028,0.4540364146232605,-0.23523765802383423,0.8540204167366028,0.4640175998210907,-0.21611528098583221,0.8540204167366028,0.4732264578342438,-0.19663318991661072,0.8540204167366028,0.48164767026901245,-0.17682382464408875,0.8540204167366028,0.4892672300338745,-0.1567201465368271,0.8540204167366028,0.4960724413394928,-0.13635563850402832,0.8540204167366028,0.5020520091056824,-0.11576417088508606,0.8540204167366028,0.5071959495544434,-0.09498002380132675,0.8540204167366028,0.5114957094192505,-0.07403779029846191,0.8540204167366028,0.5149441361427307,-0.05297233536839485,0.8540204167366028,0.5175355076789856,-0.03181871026754379,0.8540204167366028,0.5192654728889465,-0.010612123645842075,0.8540204167366028,0.5201312303543091,0.010612123645842075,0.8540204167366028,0.5201312303543091,0.03181871026754379,0.8540204167366028,0.5192654728889465,0.05297233536839485,0.8540204167366028,0.5175355076789856,0.07403779029846191,0.8540204167366028,0.5149441361427307,0.09498002380132675,0.8540204167366028,0.5114957094192505,0.11576417088508606,0.8540204167366028,0.5071959495544434,0.13635563850402832,0.8540204167366028,0.5020520091056824,0.1567201465368271,0.8540204167366028,0.4960724413394928,0.17682382464408875,0.8540204167366028,0.4892672300338745,0.19663318991661072,0.8540204167366028,0.48164767026901245,0.21611528098583221,0.8540204167366028,0.4732264578342438,0.23523765802383423,0.8540204167366028,0.4640175998210907,0.2539685070514679,0.8540204167366028,0.4540364146232605,0.2722766697406769,0.8540204167366028,0.4432995617389679,0.29013165831565857,0.8540204167366028,0.43182486295700073,0.30750373005867004,0.8540204167366028,0.4196314513683319,0.32436397671699524,0.8540204167366028,0.40673959255218506,0.3406843841075897,0.8540204167366028,0.3931707441806793,0.3564377427101135,0.8540204167366028,0.37894749641418457,0.37159785628318787,0.8540204167366028,0.3640935719013214,0.38613948225975037,0.8540204167366028,0.34863361716270447,0.40003839135169983,0.8540204167366028,0.3325933814048767,0.4132714867591858,0.8540204167366028,0.3159995973110199,0.42581674456596375,0.8540204167366028,0.29887986183166504,0.4376532733440399,0.8540204167366028,0.28126266598701477,0.44876137375831604,0.8540204167366028,0.26317736506462097,0.45912253856658936,0.8540204167366028,0.24465399980545044,0.4687195420265198,0.8540204167366028,0.22572343051433563,0.4775364100933075,0.8540204167366028,0.20641718804836273,0.48555848002433777,0.8540204167366028,0.1867673695087433,0.4927723705768585,0.8540204167366028,0.16680669784545898,0.4991661012172699,0.8540204167366028,0.1465683877468109,0.5047290325164795,0.8540204167366028,0.12608613073825836,0.5094518661499023,0.8540204167366028,0.10539402812719345,0.5133267641067505,0.8540204167366028,0.08452649414539337,0.5163472890853882,0.8540204167366028,0.06351827830076218,0.5185083746910095,0.8540204167366028,0.04240434616804123,0.5198065042495728,0.8540204167366028,0.021219830960035324,0.5202394723892212,0.8540204167366028,6.371095955627149e-17,0.5198065042495728,0.8540204167366028,-0.021219830960035324,0.5185083746910095,0.8540204167366028,-0.04240434616804123,0.5163472890853882,0.8540204167366028,-0.06351827830076218,0.5133267641067505,0.8540204167366028,-0.08452649414539337,0.5094518661499023,0.8540204167366028,-0.10539402812719345,0.5047290325164795,0.8540204167366028,-0.12608613073825836,0.4991661012172699,0.8540204167366028,-0.1465683877468109,0.4927723705768585,0.8540204167366028,-0.16680669784545898,0.48555848002433777,0.8540204167366028,-0.1867673695087433,0.4775364100933075,0.8540204167366028,-0.20641718804836273,0.4687195420265198,0.8540204167366028,-0.22572343051433563,0.45912253856658936,0.8540204167366028,-0.24465399980545044,0.44876137375831604,0.8540204167366028,-0.26317736506462097,0.4376532733440399,0.8540204167366028,-0.28126266598701477,0.42581674456596375,0.8540204167366028,-0.29887986183166504,0.4132714867591858,0.8540204167366028,-0.3159995973110199,0.40003839135169983,0.8540204167366028,-0.3325933814048767,0.38613948225975037,0.8540204167366028,-0.34863361716270447,0.37159785628318787,0.8540204167366028,-0.3640935719013214,0.3564377427101135,0.8540204167366028,-0.37894749641418457,0.3406843841075897,0.8540204167366028,-0.3931707441806793,0.32436397671699524,0.8540204167366028,-0.40673959255218506,0.30750373005867004,0.8540204167366028,-0.4196314513683319,0.29013165831565857,0.8540204167366028,-0.43182486295700073,0.2722766697406769,0.8540204167366028,-0.4432995617389679,0.2539685070514679,0.8540204167366028,-0.4540364146232605,0.23523765802383423,0.8540204167366028,-0.4640175998210907,0.21611528098583221,0.8540204167366028,-0.4732264578342438,0.19663318991661072,0.8540204167366028,-0.48164767026901245,0.17682382464408875,0.8540204167366028,-0.4892672300338745,0.1567201465368271,0.8540204167366028,-0.4960724413394928,0.13635563850402832,0.8540204167366028,-0.5020520091056824,0.11576417088508606,0.8540204167366028,-0.5071959495544434,0.09498002380132675,0.8540204167366028,-0.5114957094192505,0.07403779029846191,0.8540204167366028,-0.5149441361427307,0.05297233536839485,0.8540204167366028,-0.5175355076789856,0.03181871026754379,0.8540204167366028,-0.5192654728889465,0.010612123645842075,0.8540204167366028,-0.5201312303543091,-0.010612123645842075,0.8540204167366028,-0.5201312303543091,-0.03181871026754379,0.8540204167366028,-0.5192654728889465,-0.05297233536839485,0.8540204167366028,-0.5175355076789856,-0.07403779029846191,0.8540204167366028,-0.5149441361427307,-0.09498002380132675,0.8540204167366028,-0.5114957094192505,-0.11576417088508606,0.8540204167366028,-0.5071959495544434,-0.13635563850402832,0.8540204167366028,-0.5020520091056824,-0.1567201465368271,0.8540204167366028,-0.4960724413394928,-0.17682382464408875,0.8540204167366028,-0.4892672300338745,-0.19663318991661072,0.8540204167366028,-0.48164767026901245,-0.21611528098583221,0.8540204167366028,-0.4732264578342438,-0.23523765802383423,0.8540204167366028,-0.4640175998210907,-0.2539685070514679,0.8540204167366028,-0.4540364146232605,-0.2722766697406769,0.8540204167366028,-0.4432995617389679,-0.29013165831565857,0.8540204167366028,-0.43182486295700073,-0.30750373005867004,0.8540204167366028,-0.4196314513683319,-0.32436397671699524,0.8540204167366028,-0.40673959255218506,-0.3406843841075897,0.8540204167366028,-0.3931707441806793,-0.3564377427101135,0.8540204167366028,-0.37894749641418457,-0.37159785628318787,0.8540204167366028,-0.3640935719013214,-0.38613948225975037,0.8540204167366028,-0.34863361716270447,-0.40003839135169983,0.8540204167366028,-0.3325933814048767,-0.4132714867591858,0.8540204167366028,-0.3159995973110199,-0.42581674456596375,0.8540204167366028,-0.29887986183166504,-0.4376532733440399,0.8540204167366028,-0.28126266598701477,-0.44876137375831604,0.8540204167366028,-0.26317736506462097,-0.45912253856658936,0.8540204167366028,-0.24465399980545044,-0.4687195420265198,0.8540204167366028,-0.22572343051433563,-0.4775364100933075,0.8540204167366028,-0.20641718804836273,-0.48555848002433777,0.8540204167366028,-0.1867673695087433,-0.4927723705768585,0.8540204167366028,-0.16680669784545898,-0.4991661012172699,0.8540204167366028,-0.1465683877468109,-0.5047290325164795,0.8540204167366028,-0.12608613073825836,-0.5094518661499023,0.8540204167366028,-0.10539402812719345,-0.5133267641067505,0.8540204167366028,-0.08452649414539337,-0.5163472890853882,0.8540204167366028,-0.06351827830076218,-0.5185083746910095,0.8540204167366028,-0.04240434616804123,-0.5198065042495728,0.8540204167366028,-0.021219830960035324,-0.5202394723892212,0.8540204167366028,-1.2742191911254298e-16,-0.5791852474212646,0.8151959776878357,0.0,-0.5787032842636108,0.8151959776878357,0.023624148219823837,-0.5772580504417419,0.8151959776878357,0.04720897600054741,-0.5748521089553833,0.8151959776878357,0.07071522623300552,-0.5714893341064453,0.8151959776878357,0.09410378336906433,-0.5671753883361816,0.8151959776878357,0.1173357143998146,-0.5619174242019653,0.8151959776878357,0.1403723508119583,-0.5557242035865784,0.8151959776878357,0.16317534446716309,-0.5486060380935669,0.8151959776878357,0.1857067495584488,-0.5405747294425964,0.8151959776878357,0.2079290747642517,-0.5316437482833862,0.8151959776878357,0.22980530560016632,-0.5218278765678406,0.8151959776878357,0.25129905343055725,-0.5111435055732727,0.8151959776878357,0.27237457036972046,-0.4996083378791809,0.8151959776878357,0.29299670457839966,-0.48724165558815,0.8151959776878357,0.313131183385849,-0.4740639925003052,0.8151959776878357,0.3327445089817047,-0.4600972831249237,0.8151959776878357,0.3518039882183075,-0.4453648030757904,0.8151959776878357,0.37027794122695923,-0.42989104986190796,0.8151959776878357,0.3881355822086334,-0.4137018024921417,0.8151959776878357,0.40534722805023193,-0.3968239724636078,0.8151959776878357,0.4218842089176178,-0.37928569316864014,0.8151959776878357,0.4377190172672272,-0.36111611127853394,0.8151959776878357,0.45282527804374695,-0.3423454761505127,0.8151959776878357,0.4671778380870819,-0.3230050504207611,0.8151959776878357,0.4807528555393219,-0.3031270205974579,0.8151959776878357,0.49352768063545227,-0.28274446725845337,0.8151959776878357,0.5054810643196106,-0.26189130544662476,0.8151959776878357,0.5165931582450867,-0.2406022548675537,0.8151959776878357,0.5268454551696777,-0.218912735581398,0.8151959776878357,0.5362208485603333,-0.19685886800289154,0.8151959776878357,0.5447037220001221,-0.17447735369205475,0.8151959776878357,0.5522800087928772,-0.15180543065071106,0.8151959776878357,0.5589370727539062,-0.1288808435201645,0.8151959776878357,0.5646638870239258,-0.10574174672365189,0.8151959776878357,0.569450855255127,-0.08242665976285934,0.8151959776878357,0.5732899904251099,-0.05897437408566475,0.8151959776878357,0.5761749744415283,-0.03542393073439598,0.8151959776878357,0.5781009793281555,-0.011814531870186329,0.8151959776878357,0.5790647268295288,0.011814531870186329,0.8151959776878357,0.5790647268295288,0.03542393073439598,0.8151959776878357,0.5781009793281555,0.05897437408566475,0.8151959776878357,0.5761749744415283,0.08242665976285934,0.8151959776878357,0.5732899904251099,0.10574174672365189,0.8151959776878357,0.569450855255127,0.1288808435201645,0.8151959776878357,0.5646638870239258,0.15180543065071106,0.8151959776878357,0.5589370727539062,0.17447735369205475,0.8151959776878357,0.5522800087928772,0.19685886800289154,0.8151959776878357,0.5447037220001221,0.218912735581398,0.8151959776878357,0.5362208485603333,0.2406022548675537,0.8151959776878357,0.5268454551696777,0.26189130544662476,0.8151959776878357,0.5165931582450867,0.28274446725845337,0.8151959776878357,0.5054810643196106,0.3031270205974579,0.8151959776878357,0.49352768063545227,0.3230050504207611,0.8151959776878357,0.4807528555393219,0.3423454761505127,0.8151959776878357,0.4671778380870819,0.36111611127853394,0.8151959776878357,0.45282527804374695,0.37928569316864014,0.8151959776878357,0.4377190172672272,0.3968239724636078,0.8151959776878357,0.4218842089176178,0.4137018024921417,0.8151959776878357,0.40534722805023193,0.42989104986190796,0.8151959776878357,0.3881355822086334,0.4453648030757904,0.8151959776878357,0.37027794122695923,0.4600972831249237,0.8151959776878357,0.3518039882183075,0.4740639925003052,0.8151959776878357,0.3327445089817047,0.48724165558815,0.8151959776878357,0.313131183385849,0.4996083378791809,0.8151959776878357,0.29299670457839966,0.5111435055732727,0.8151959776878357,0.27237457036972046,0.5218278765678406,0.8151959776878357,0.25129905343055725,0.5316437482833862,0.8151959776878357,0.22980530560016632,0.5405747294425964,0.8151959776878357,0.2079290747642517,0.5486060380935669,0.8151959776878357,0.1857067495584488,0.5557242035865784,0.8151959776878357,0.16317534446716309,0.5619174242019653,0.8151959776878357,0.1403723508119583,0.5671753883361816,0.8151959776878357,0.1173357143998146,0.5714893341064453,0.8151959776878357,0.09410378336906433,0.5748521089553833,0.8151959776878357,0.07071522623300552,0.5772580504417419,0.8151959776878357,0.04720897600054741,0.5787032842636108,0.8151959776878357,0.023624148219823837,0.5791852474212646,0.8151959776878357,7.092973829224216e-17,0.5787032842636108,0.8151959776878357,-0.023624148219823837,0.5772580504417419,0.8151959776878357,-0.04720897600054741,0.5748521089553833,0.8151959776878357,-0.07071522623300552,0.5714893341064453,0.8151959776878357,-0.09410378336906433,0.5671753883361816,0.8151959776878357,-0.1173357143998146,0.5619174242019653,0.8151959776878357,-0.1403723508119583,0.5557242035865784,0.8151959776878357,-0.16317534446716309,0.5486060380935669,0.8151959776878357,-0.1857067495584488,0.5405747294425964,0.8151959776878357,-0.2079290747642517,0.5316437482833862,0.8151959776878357,-0.22980530560016632,0.5218278765678406,0.8151959776878357,-0.25129905343055725,0.5111435055732727,0.8151959776878357,-0.27237457036972046,0.4996083378791809,0.8151959776878357,-0.29299670457839966,0.48724165558815,0.8151959776878357,-0.313131183385849,0.4740639925003052,0.8151959776878357,-0.3327445089817047,0.4600972831249237,0.8151959776878357,-0.3518039882183075,0.4453648030757904,0.8151959776878357,-0.37027794122695923,0.42989104986190796,0.8151959776878357,-0.3881355822086334,0.4137018024921417,0.8151959776878357,-0.40534722805023193,0.3968239724636078,0.8151959776878357,-0.4218842089176178,0.37928569316864014,0.8151959776878357,-0.4377190172672272,0.36111611127853394,0.8151959776878357,-0.45282527804374695,0.3423454761505127,0.8151959776878357,-0.4671778380870819,0.3230050504207611,0.8151959776878357,-0.4807528555393219,0.3031270205974579,0.8151959776878357,-0.49352768063545227,0.28274446725845337,0.8151959776878357,-0.5054810643196106,0.26189130544662476,0.8151959776878357,-0.5165931582450867,0.2406022548675537,0.8151959776878357,-0.5268454551696777,0.218912735581398,0.8151959776878357,-0.5362208485603333,0.19685886800289154,0.8151959776878357,-0.5447037220001221,0.17447735369205475,0.8151959776878357,-0.5522800087928772,0.15180543065071106,0.8151959776878357,-0.5589370727539062,0.1288808435201645,0.8151959776878357,-0.5646638870239258,0.10574174672365189,0.8151959776878357,-0.569450855255127,0.08242665976285934,0.8151959776878357,-0.5732899904251099,0.05897437408566475,0.8151959776878357,-0.5761749744415283,0.03542393073439598,0.8151959776878357,-0.5781009793281555,0.011814531870186329,0.8151959776878357,-0.5790647268295288,-0.011814531870186329,0.8151959776878357,-0.5790647268295288,-0.03542393073439598,0.8151959776878357,-0.5781009793281555,-0.05897437408566475,0.8151959776878357,-0.5761749744415283,-0.08242665976285934,0.8151959776878357,-0.5732899904251099,-0.10574174672365189,0.8151959776878357,-0.569450855255127,-0.1288808435201645,0.8151959776878357,-0.5646638870239258,-0.15180543065071106,0.8151959776878357,-0.5589370727539062,-0.17447735369205475,0.8151959776878357,-0.5522800087928772,-0.19685886800289154,0.8151959776878357,-0.5447037220001221,-0.218912735581398,0.8151959776878357,-0.5362208485603333,-0.2406022548675537,0.8151959776878357,-0.5268454551696777,-0.26189130544662476,0.8151959776878357,-0.5165931582450867,-0.28274446725845337,0.8151959776878357,-0.5054810643196106,-0.3031270205974579,0.8151959776878357,-0.49352768063545227,-0.3230050504207611,0.8151959776878357,-0.4807528555393219,-0.3423454761505127,0.8151959776878357,-0.4671778380870819,-0.36111611127853394,0.8151959776878357,-0.45282527804374695,-0.37928569316864014,0.8151959776878357,-0.4377190172672272,-0.3968239724636078,0.8151959776878357,-0.4218842089176178,-0.4137018024921417,0.8151959776878357,-0.40534722805023193,-0.42989104986190796,0.8151959776878357,-0.3881355822086334,-0.4453648030757904,0.8151959776878357,-0.37027794122695923,-0.4600972831249237,0.8151959776878357,-0.3518039882183075,-0.4740639925003052,0.8151959776878357,-0.3327445089817047,-0.48724165558815,0.8151959776878357,-0.313131183385849,-0.4996083378791809,0.8151959776878357,-0.29299670457839966,-0.5111435055732727,0.8151959776878357,-0.27237457036972046,-0.5218278765678406,0.8151959776878357,-0.25129905343055725,-0.5316437482833862,0.8151959776878357,-0.22980530560016632,-0.5405747294425964,0.8151959776878357,-0.2079290747642517,-0.5486060380935669,0.8151959776878357,-0.1857067495584488,-0.5557242035865784,0.8151959776878357,-0.16317534446716309,-0.5619174242019653,0.8151959776878357,-0.1403723508119583,-0.5671753883361816,0.8151959776878357,-0.1173357143998146,-0.5714893341064453,0.8151959776878357,-0.09410378336906433,-0.5748521089553833,0.8151959776878357,-0.07071522623300552,-0.5772580504417419,0.8151959776878357,-0.04720897600054741,-0.5787032842636108,0.8151959776878357,-0.023624148219823837,-0.5791852474212646,0.8151959776878357,-1.4185947658448432e-16,-0.6352456212043762,0.772310197353363,0.0,-0.6347169280052185,0.772310197353363,0.025910770520567894,-0.6331318616867065,0.772310197353363,0.051778413355350494,-0.6304929852485657,0.772310197353363,0.07755987346172333,-0.626804769039154,0.772310197353363,0.10321225225925446,-0.622073233127594,0.772310197353363,0.1286928355693817,-0.6163063645362854,0.772310197353363,0.1539592295885086,-0.6095137000083923,0.772310197353363,0.1789693683385849,-0.6017065644264221,0.772310197353363,0.20368163287639618,-0.592897891998291,0.772310197353363,0.22805488109588623,-0.5831024646759033,0.772310197353363,0.2520485520362854,-0.5723364949226379,0.772310197353363,0.27562272548675537,-0.5606179237365723,0.772310197353363,0.29873815178871155,-0.5479663014411926,0.772310197353363,0.3213563561439514,-0.53440260887146,0.772310197353363,0.3434396982192993,-0.5199494361877441,0.772310197353363,0.36495140194892883,-0.5046309232711792,0.772310197353363,0.38585570454597473,-0.48847243189811707,0.772310197353363,0.4061177670955658,-0.471500962972641,0.772310197353363,0.42570391297340393,-0.45374470949172974,0.772310197353363,0.4445814788341522,-0.4352332651615143,0.772310197353363,0.4627191126346588,-0.41599738597869873,0.772310197353363,0.4800865948200226,-0.39606913924217224,0.772310197353363,0.49665501713752747,-0.3754816949367523,0.772310197353363,0.5123968124389648,-0.35426926612854004,0.772310197353363,0.5272857546806335,-0.3324671983718872,0.772310197353363,0.5412970781326294,-0.31011179089546204,0.772310197353363,0.5544074773788452,-0.287240207195282,0.772310197353363,0.5665951371192932,-0.2638905644416809,0.772310197353363,0.577839732170105,-0.2401016801595688,0.772310197353363,0.588122546672821,-0.21591317653656006,0.772310197353363,0.5974265336990356,-0.1913653165102005,0.772310197353363,0.6057361364364624,-0.16649894416332245,0.772310197353363,0.6130375862121582,-0.14135544002056122,0.772310197353363,0.6193186640739441,-0.11597667634487152,0.772310197353363,0.6245689392089844,-0.09040487557649612,0.772310197353363,0.6287797093391418,-0.06468260288238525,0.772310197353363,0.631943941116333,-0.03885267674922943,0.772310197353363,0.6340563297271729,-0.012958081439137459,0.772310197353363,0.6351134181022644,0.012958081439137459,0.772310197353363,0.6351134181022644,0.03885267674922943,0.772310197353363,0.6340563297271729,0.06468260288238525,0.772310197353363,0.631943941116333,0.09040487557649612,0.772310197353363,0.6287797093391418,0.11597667634487152,0.772310197353363,0.6245689392089844,0.14135544002056122,0.772310197353363,0.6193186640739441,0.16649894416332245,0.772310197353363,0.6130375862121582,0.1913653165102005,0.772310197353363,0.6057361364364624,0.21591317653656006,0.772310197353363,0.5974265336990356,0.2401016801595688,0.772310197353363,0.588122546672821,0.2638905644416809,0.772310197353363,0.577839732170105,0.287240207195282,0.772310197353363,0.5665951371192932,0.31011179089546204,0.772310197353363,0.5544074773788452,0.3324671983718872,0.772310197353363,0.5412970781326294,0.35426926612854004,0.772310197353363,0.5272857546806335,0.3754816949367523,0.772310197353363,0.5123968124389648,0.39606913924217224,0.772310197353363,0.49665501713752747,0.41599738597869873,0.772310197353363,0.4800865948200226,0.4352332651615143,0.772310197353363,0.4627191126346588,0.45374470949172974,0.772310197353363,0.4445814788341522,0.471500962972641,0.772310197353363,0.42570391297340393,0.48847243189811707,0.772310197353363,0.4061177670955658,0.5046309232711792,0.772310197353363,0.38585570454597473,0.5199494361877441,0.772310197353363,0.36495140194892883,0.53440260887146,0.772310197353363,0.3434396982192993,0.5479663014411926,0.772310197353363,0.3213563561439514,0.5606179237365723,0.772310197353363,0.29873815178871155,0.5723364949226379,0.772310197353363,0.27562272548675537,0.5831024646759033,0.772310197353363,0.2520485520362854,0.592897891998291,0.772310197353363,0.22805488109588623,0.6017065644264221,0.772310197353363,0.20368163287639618,0.6095137000083923,0.772310197353363,0.1789693683385849,0.6163063645362854,0.772310197353363,0.1539592295885086,0.622073233127594,0.772310197353363,0.1286928355693817,0.626804769039154,0.772310197353363,0.10321225225925446,0.6304929852485657,0.772310197353363,0.07755987346172333,0.6331318616867065,0.772310197353363,0.051778413355350494,0.6347169280052185,0.772310197353363,0.025910770520567894,0.6352456212043762,0.772310197353363,7.779514547053018e-17,0.6347169280052185,0.772310197353363,-0.025910770520567894,0.6331318616867065,0.772310197353363,-0.051778413355350494,0.6304929852485657,0.772310197353363,-0.07755987346172333,0.626804769039154,0.772310197353363,-0.10321225225925446,0.622073233127594,0.772310197353363,-0.1286928355693817,0.6163063645362854,0.772310197353363,-0.1539592295885086,0.6095137000083923,0.772310197353363,-0.1789693683385849,0.6017065644264221,0.772310197353363,-0.20368163287639618,0.592897891998291,0.772310197353363,-0.22805488109588623,0.5831024646759033,0.772310197353363,-0.2520485520362854,0.5723364949226379,0.772310197353363,-0.27562272548675537,0.5606179237365723,0.772310197353363,-0.29873815178871155,0.5479663014411926,0.772310197353363,-0.3213563561439514,0.53440260887146,0.772310197353363,-0.3434396982192993,0.5199494361877441,0.772310197353363,-0.36495140194892883,0.5046309232711792,0.772310197353363,-0.38585570454597473,0.48847243189811707,0.772310197353363,-0.4061177670955658,0.471500962972641,0.772310197353363,-0.42570391297340393,0.45374470949172974,0.772310197353363,-0.4445814788341522,0.4352332651615143,0.772310197353363,-0.4627191126346588,0.41599738597869873,0.772310197353363,-0.4800865948200226,0.39606913924217224,0.772310197353363,-0.49665501713752747,0.3754816949367523,0.772310197353363,-0.5123968124389648,0.35426926612854004,0.772310197353363,-0.5272857546806335,0.3324671983718872,0.772310197353363,-0.5412970781326294,0.31011179089546204,0.772310197353363,-0.5544074773788452,0.287240207195282,0.772310197353363,-0.5665951371192932,0.2638905644416809,0.772310197353363,-0.577839732170105,0.2401016801595688,0.772310197353363,-0.588122546672821,0.21591317653656006,0.772310197353363,-0.5974265336990356,0.1913653165102005,0.772310197353363,-0.6057361364364624,0.16649894416332245,0.772310197353363,-0.6130375862121582,0.14135544002056122,0.772310197353363,-0.6193186640739441,0.11597667634487152,0.772310197353363,-0.6245689392089844,0.09040487557649612,0.772310197353363,-0.6287797093391418,0.06468260288238525,0.772310197353363,-0.631943941116333,0.03885267674922943,0.772310197353363,-0.6340563297271729,0.012958081439137459,0.772310197353363,-0.6351134181022644,-0.012958081439137459,0.772310197353363,-0.6351134181022644,-0.03885267674922943,0.772310197353363,-0.6340563297271729,-0.06468260288238525,0.772310197353363,-0.631943941116333,-0.09040487557649612,0.772310197353363,-0.6287797093391418,-0.11597667634487152,0.772310197353363,-0.6245689392089844,-0.14135544002056122,0.772310197353363,-0.6193186640739441,-0.16649894416332245,0.772310197353363,-0.6130375862121582,-0.1913653165102005,0.772310197353363,-0.6057361364364624,-0.21591317653656006,0.772310197353363,-0.5974265336990356,-0.2401016801595688,0.772310197353363,-0.588122546672821,-0.2638905644416809,0.772310197353363,-0.577839732170105,-0.287240207195282,0.772310197353363,-0.5665951371192932,-0.31011179089546204,0.772310197353363,-0.5544074773788452,-0.3324671983718872,0.772310197353363,-0.5412970781326294,-0.35426926612854004,0.772310197353363,-0.5272857546806335,-0.3754816949367523,0.772310197353363,-0.5123968124389648,-0.39606913924217224,0.772310197353363,-0.49665501713752747,-0.41599738597869873,0.772310197353363,-0.4800865948200226,-0.4352332651615143,0.772310197353363,-0.4627191126346588,-0.45374470949172974,0.772310197353363,-0.4445814788341522,-0.471500962972641,0.772310197353363,-0.42570391297340393,-0.48847243189811707,0.772310197353363,-0.4061177670955658,-0.5046309232711792,0.772310197353363,-0.38585570454597473,-0.5199494361877441,0.772310197353363,-0.36495140194892883,-0.53440260887146,0.772310197353363,-0.3434396982192993,-0.5479663014411926,0.772310197353363,-0.3213563561439514,-0.5606179237365723,0.772310197353363,-0.29873815178871155,-0.5723364949226379,0.772310197353363,-0.27562272548675537,-0.5831024646759033,0.772310197353363,-0.2520485520362854,-0.592897891998291,0.772310197353363,-0.22805488109588623,-0.6017065644264221,0.772310197353363,-0.20368163287639618,-0.6095137000083923,0.772310197353363,-0.1789693683385849,-0.6163063645362854,0.772310197353363,-0.1539592295885086,-0.622073233127594,0.772310197353363,-0.1286928355693817,-0.626804769039154,0.772310197353363,-0.10321225225925446,-0.6304929852485657,0.772310197353363,-0.07755987346172333,-0.6331318616867065,0.772310197353363,-0.051778413355350494,-0.6347169280052185,0.772310197353363,-0.025910770520567894,-0.6352456212043762,0.772310197353363,-1.5559029094106035e-16,-0.6881411671638489,0.7255768179893494,0.0,-0.6875684857368469,0.7255768179893494,0.028068305924534798,-0.6858514547348022,0.7255768179893494,0.056089892983436584,-0.6829928755760193,0.7255768179893494,0.08401812613010406,-0.6789974570274353,0.7255768179893494,0.11180651932954788,-0.6738719940185547,0.7255768179893494,0.13940882682800293,-0.6676248908042908,0.7255768179893494,0.16677908599376678,-0.660266637802124,0.7255768179893494,0.19387176632881165,-0.6518093943595886,0.7255768179893494,0.22064177691936493,-0.6422672867774963,0.7255768179893494,0.24704453349113464,-0.6316561698913574,0.7255768179893494,0.2730361223220825,-0.6199937462806702,0.7255768179893494,0.29857325553894043,-0.6072994470596313,0.7255768179893494,0.3236134648323059,-0.5935943126678467,0.7255768179893494,0.3481150269508362,-0.5789011716842651,0.7255768179893494,0.37203720211982727,-0.5632445812225342,0.7255768179893494,0.3953401446342468,-0.5466504693031311,0.7255768179893494,0.41798511147499084,-0.5291464924812317,0.7255768179893494,0.4399343430995941,-0.5107618570327759,0.7255768179893494,0.4611513912677765,-0.49152708053588867,0.7255768179893494,0.4816008508205414,-0.47147423028945923,0.7255768179893494,0.5012487769126892,-0.450636625289917,0.7255768179893494,0.5200623869895935,-0.429049015045166,0.7255768179893494,0.5380104184150696,-0.4067472517490387,0.7255768179893494,0.555063009262085,-0.3837685286998749,0.7255768179893494,0.5711917281150818,-0.3601510524749756,0.7255768179893494,0.5863697528839111,-0.3359341323375702,0.7255768179893494,0.6005718111991882,-0.31115812063217163,0.7255768179893494,0.613774299621582,-0.2858641743659973,0.7255768179893494,0.6259552240371704,-0.26009446382522583,0.7255768179893494,0.6370943188667297,-0.2338918298482895,0.7255768179893494,0.6471729874610901,-0.20729990303516388,0.7255768179893494,0.6561745405197144,-0.18036295473575592,0.7255768179893494,0.6640839576721191,-0.1531258225440979,0.7255768179893494,0.6708880066871643,-0.12563380599021912,0.7255768179893494,0.6765754818916321,-0.09793269634246826,0.7255768179893494,0.6811369061470032,-0.0700685903429985,0.7255768179893494,0.6845645904541016,-0.0420878566801548,0.7255768179893494,0.6868528723716736,-0.014037073589861393,0.7255768179893494,0.6879979968070984,0.014037073589861393,0.7255768179893494,0.6879979968070984,0.0420878566801548,0.7255768179893494,0.6868528723716736,0.0700685903429985,0.7255768179893494,0.6845645904541016,0.09793269634246826,0.7255768179893494,0.6811369061470032,0.12563380599021912,0.7255768179893494,0.6765754818916321,0.1531258225440979,0.7255768179893494,0.6708880066871643,0.18036295473575592,0.7255768179893494,0.6640839576721191,0.20729990303516388,0.7255768179893494,0.6561745405197144,0.2338918298482895,0.7255768179893494,0.6471729874610901,0.26009446382522583,0.7255768179893494,0.6370943188667297,0.2858641743659973,0.7255768179893494,0.6259552240371704,0.31115812063217163,0.7255768179893494,0.613774299621582,0.3359341323375702,0.7255768179893494,0.6005718111991882,0.3601510524749756,0.7255768179893494,0.5863697528839111,0.3837685286998749,0.7255768179893494,0.5711917281150818,0.4067472517490387,0.7255768179893494,0.555063009262085,0.429049015045166,0.7255768179893494,0.5380104184150696,0.450636625289917,0.7255768179893494,0.5200623869895935,0.47147423028945923,0.7255768179893494,0.5012487769126892,0.49152708053588867,0.7255768179893494,0.4816008508205414,0.5107618570327759,0.7255768179893494,0.4611513912677765,0.5291464924812317,0.7255768179893494,0.4399343430995941,0.5466504693031311,0.7255768179893494,0.41798511147499084,0.5632445812225342,0.7255768179893494,0.3953401446342468,0.5789011716842651,0.7255768179893494,0.37203720211982727,0.5935943126678467,0.7255768179893494,0.3481150269508362,0.6072994470596313,0.7255768179893494,0.3236134648323059,0.6199937462806702,0.7255768179893494,0.29857325553894043,0.6316561698913574,0.7255768179893494,0.2730361223220825,0.6422672867774963,0.7255768179893494,0.24704453349113464,0.6518093943595886,0.7255768179893494,0.22064177691936493,0.660266637802124,0.7255768179893494,0.19387176632881165,0.6676248908042908,0.7255768179893494,0.16677908599376678,0.6738719940185547,0.7255768179893494,0.13940882682800293,0.6789974570274353,0.7255768179893494,0.11180651932954788,0.6829928755760193,0.7255768179893494,0.08401812613010406,0.6858514547348022,0.7255768179893494,0.056089892983436584,0.6875684857368469,0.7255768179893494,0.028068305924534798,0.6881411671638489,0.7255768179893494,8.427298875333505e-17,0.6875684857368469,0.7255768179893494,-0.028068305924534798,0.6858514547348022,0.7255768179893494,-0.056089892983436584,0.6829928755760193,0.7255768179893494,-0.08401812613010406,0.6789974570274353,0.7255768179893494,-0.11180651932954788,0.6738719940185547,0.7255768179893494,-0.13940882682800293,0.6676248908042908,0.7255768179893494,-0.16677908599376678,0.660266637802124,0.7255768179893494,-0.19387176632881165,0.6518093943595886,0.7255768179893494,-0.22064177691936493,0.6422672867774963,0.7255768179893494,-0.24704453349113464,0.6316561698913574,0.7255768179893494,-0.2730361223220825,0.6199937462806702,0.7255768179893494,-0.29857325553894043,0.6072994470596313,0.7255768179893494,-0.3236134648323059,0.5935943126678467,0.7255768179893494,-0.3481150269508362,0.5789011716842651,0.7255768179893494,-0.37203720211982727,0.5632445812225342,0.7255768179893494,-0.3953401446342468,0.5466504693031311,0.7255768179893494,-0.41798511147499084,0.5291464924812317,0.7255768179893494,-0.4399343430995941,0.5107618570327759,0.7255768179893494,-0.4611513912677765,0.49152708053588867,0.7255768179893494,-0.4816008508205414,0.47147423028945923,0.7255768179893494,-0.5012487769126892,0.450636625289917,0.7255768179893494,-0.5200623869895935,0.429049015045166,0.7255768179893494,-0.5380104184150696,0.4067472517490387,0.7255768179893494,-0.555063009262085,0.3837685286998749,0.7255768179893494,-0.5711917281150818,0.3601510524749756,0.7255768179893494,-0.5863697528839111,0.3359341323375702,0.7255768179893494,-0.6005718111991882,0.31115812063217163,0.7255768179893494,-0.613774299621582,0.2858641743659973,0.7255768179893494,-0.6259552240371704,0.26009446382522583,0.7255768179893494,-0.6370943188667297,0.2338918298482895,0.7255768179893494,-0.6471729874610901,0.20729990303516388,0.7255768179893494,-0.6561745405197144,0.18036295473575592,0.7255768179893494,-0.6640839576721191,0.1531258225440979,0.7255768179893494,-0.6708880066871643,0.12563380599021912,0.7255768179893494,-0.6765754818916321,0.09793269634246826,0.7255768179893494,-0.6811369061470032,0.0700685903429985,0.7255768179893494,-0.6845645904541016,0.0420878566801548,0.7255768179893494,-0.6868528723716736,0.014037073589861393,0.7255768179893494,-0.6879979968070984,-0.014037073589861393,0.7255768179893494,-0.6879979968070984,-0.0420878566801548,0.7255768179893494,-0.6868528723716736,-0.0700685903429985,0.7255768179893494,-0.6845645904541016,-0.09793269634246826,0.7255768179893494,-0.6811369061470032,-0.12563380599021912,0.7255768179893494,-0.6765754818916321,-0.1531258225440979,0.7255768179893494,-0.6708880066871643,-0.18036295473575592,0.7255768179893494,-0.6640839576721191,-0.20729990303516388,0.7255768179893494,-0.6561745405197144,-0.2338918298482895,0.7255768179893494,-0.6471729874610901,-0.26009446382522583,0.7255768179893494,-0.6370943188667297,-0.2858641743659973,0.7255768179893494,-0.6259552240371704,-0.31115812063217163,0.7255768179893494,-0.613774299621582,-0.3359341323375702,0.7255768179893494,-0.6005718111991882,-0.3601510524749756,0.7255768179893494,-0.5863697528839111,-0.3837685286998749,0.7255768179893494,-0.5711917281150818,-0.4067472517490387,0.7255768179893494,-0.555063009262085,-0.429049015045166,0.7255768179893494,-0.5380104184150696,-0.450636625289917,0.7255768179893494,-0.5200623869895935,-0.47147423028945923,0.7255768179893494,-0.5012487769126892,-0.49152708053588867,0.7255768179893494,-0.4816008508205414,-0.5107618570327759,0.7255768179893494,-0.4611513912677765,-0.5291464924812317,0.7255768179893494,-0.4399343430995941,-0.5466504693031311,0.7255768179893494,-0.41798511147499084,-0.5632445812225342,0.7255768179893494,-0.3953401446342468,-0.5789011716842651,0.7255768179893494,-0.37203720211982727,-0.5935943126678467,0.7255768179893494,-0.3481150269508362,-0.6072994470596313,0.7255768179893494,-0.3236134648323059,-0.6199937462806702,0.7255768179893494,-0.29857325553894043,-0.6316561698913574,0.7255768179893494,-0.2730361223220825,-0.6422672867774963,0.7255768179893494,-0.24704453349113464,-0.6518093943595886,0.7255768179893494,-0.22064177691936493,-0.660266637802124,0.7255768179893494,-0.19387176632881165,-0.6676248908042908,0.7255768179893494,-0.16677908599376678,-0.6738719940185547,0.7255768179893494,-0.13940882682800293,-0.6789974570274353,0.7255768179893494,-0.11180651932954788,-0.6829928755760193,0.7255768179893494,-0.08401812613010406,-0.6858514547348022,0.7255768179893494,-0.056089892983436584,-0.6875684857368469,0.7255768179893494,-0.028068305924534798,-0.6881411671638489,0.7255768179893494,-1.685459775066701e-16,-0.7376084923744202,0.6752286553382874,0.0,-0.7369946241378784,0.6752286553382874,0.030086006969213486,-0.7351541519165039,0.6752286553382874,0.060121938586235046,-0.7320900559425354,0.6752286553382874,0.09005780518054962,-0.7278074622154236,0.6752286553382874,0.11984377354383469,-0.7223135232925415,0.6752286553382874,0.1494302749633789,-0.7156173586845398,0.6752286553382874,0.1787680685520172,-0.7077301144599915,0.6752286553382874,0.20780831575393677,-0.6986649632453918,0.6752286553382874,0.23650269210338593,-0.6884369254112244,0.6752286553382874,0.2648034393787384,-0.6770630478858948,0.6752286553382874,0.29266342520713806,-0.6645622849464417,0.6752286553382874,0.3200363218784332,-0.6509553790092468,0.6752286553382874,0.3468765318393707,-0.6362650394439697,0.6752286553382874,0.3731394112110138,-0.6205157041549683,0.6752286553382874,0.3987812399864197,-0.6037335991859436,0.6752286553382874,0.4237593412399292,-0.5859466195106506,0.6752286553382874,0.44803211092948914,-0.5671843886375427,0.6752286553382874,0.47155919671058655,-0.547478199005127,0.6752286553382874,0.494301438331604,-0.5268607139587402,0.6752286553382874,0.5162209272384644,-0.505366325378418,0.6752286553382874,0.5372812151908875,-0.4830308258533478,0.6752286553382874,0.5574472546577454,-0.45989134907722473,0.6752286553382874,0.5766855478286743,-0.4359864592552185,0.6752286553382874,0.5949639081954956,-0.4113558828830719,0.6752286553382874,0.6122520565986633,-0.38604065775871277,0.6752286553382874,0.6285211443901062,-0.3600828945636749,0.6752286553382874,0.643744170665741,-0.3335258364677429,0.6752286553382874,0.6578956842422485,-0.3064136207103729,0.6752286553382874,0.6709522604942322,-0.2787914276123047,0.6752286553382874,0.6828920841217041,-0.25070521235466003,0.6752286553382874,0.6936952471733093,-0.22220173478126526,0.6752286553382874,0.7033438682556152,-0.19332842528820038,0.6752286553382874,0.7118218541145325,-0.16413332521915436,0.6752286553382874,0.7191150784492493,-0.13466504216194153,0.6752286553382874,0.7252113819122314,-0.10497263073921204,0.6752286553382874,0.730100691318512,-0.07510549575090408,0.6752286553382874,0.7337747812271118,-0.04511335864663124,0.6752286553382874,0.7362275719642639,-0.015046133659780025,0.6752286553382874,0.7374550104141235,0.015046133659780025,0.6752286553382874,0.7374550104141235,0.04511335864663124,0.6752286553382874,0.7362275719642639,0.07510549575090408,0.6752286553382874,0.7337747812271118,0.10497263073921204,0.6752286553382874,0.730100691318512,0.13466504216194153,0.6752286553382874,0.7252113819122314,0.16413332521915436,0.6752286553382874,0.7191150784492493,0.19332842528820038,0.6752286553382874,0.7118218541145325,0.22220173478126526,0.6752286553382874,0.7033438682556152,0.25070521235466003,0.6752286553382874,0.6936952471733093,0.2787914276123047,0.6752286553382874,0.6828920841217041,0.3064136207103729,0.6752286553382874,0.6709522604942322,0.3335258364677429,0.6752286553382874,0.6578956842422485,0.3600828945636749,0.6752286553382874,0.643744170665741,0.38604065775871277,0.6752286553382874,0.6285211443901062,0.4113558828830719,0.6752286553382874,0.6122520565986633,0.4359864592552185,0.6752286553382874,0.5949639081954956,0.45989134907722473,0.6752286553382874,0.5766855478286743,0.4830308258533478,0.6752286553382874,0.5574472546577454,0.505366325378418,0.6752286553382874,0.5372812151908875,0.5268607139587402,0.6752286553382874,0.5162209272384644,0.547478199005127,0.6752286553382874,0.494301438331604,0.5671843886375427,0.6752286553382874,0.47155919671058655,0.5859466195106506,0.6752286553382874,0.44803211092948914,0.6037335991859436,0.6752286553382874,0.4237593412399292,0.6205157041549683,0.6752286553382874,0.3987812399864197,0.6362650394439697,0.6752286553382874,0.3731394112110138,0.6509553790092468,0.6752286553382874,0.3468765318393707,0.6645622849464417,0.6752286553382874,0.3200363218784332,0.6770630478858948,0.6752286553382874,0.29266342520713806,0.6884369254112244,0.6752286553382874,0.2648034393787384,0.6986649632453918,0.6752286553382874,0.23650269210338593,0.7077301144599915,0.6752286553382874,0.20780831575393677,0.7156173586845398,0.6752286553382874,0.1787680685520172,0.7223135232925415,0.6752286553382874,0.1494302749633789,0.7278074622154236,0.6752286553382874,0.11984377354383469,0.7320900559425354,0.6752286553382874,0.09005780518054962,0.7351541519165039,0.6752286553382874,0.060121938586235046,0.7369946241378784,0.6752286553382874,0.030086006969213486,0.7376084923744202,0.6752286553382874,9.03309816269876e-17,0.7369946241378784,0.6752286553382874,-0.030086006969213486,0.7351541519165039,0.6752286553382874,-0.060121938586235046,0.7320900559425354,0.6752286553382874,-0.09005780518054962,0.7278074622154236,0.6752286553382874,-0.11984377354383469,0.7223135232925415,0.6752286553382874,-0.1494302749633789,0.7156173586845398,0.6752286553382874,-0.1787680685520172,0.7077301144599915,0.6752286553382874,-0.20780831575393677,0.6986649632453918,0.6752286553382874,-0.23650269210338593,0.6884369254112244,0.6752286553382874,-0.2648034393787384,0.6770630478858948,0.6752286553382874,-0.29266342520713806,0.6645622849464417,0.6752286553382874,-0.3200363218784332,0.6509553790092468,0.6752286553382874,-0.3468765318393707,0.6362650394439697,0.6752286553382874,-0.3731394112110138,0.6205157041549683,0.6752286553382874,-0.3987812399864197,0.6037335991859436,0.6752286553382874,-0.4237593412399292,0.5859466195106506,0.6752286553382874,-0.44803211092948914,0.5671843886375427,0.6752286553382874,-0.47155919671058655,0.547478199005127,0.6752286553382874,-0.494301438331604,0.5268607139587402,0.6752286553382874,-0.5162209272384644,0.505366325378418,0.6752286553382874,-0.5372812151908875,0.4830308258533478,0.6752286553382874,-0.5574472546577454,0.45989134907722473,0.6752286553382874,-0.5766855478286743,0.4359864592552185,0.6752286553382874,-0.5949639081954956,0.4113558828830719,0.6752286553382874,-0.6122520565986633,0.38604065775871277,0.6752286553382874,-0.6285211443901062,0.3600828945636749,0.6752286553382874,-0.643744170665741,0.3335258364677429,0.6752286553382874,-0.6578956842422485,0.3064136207103729,0.6752286553382874,-0.6709522604942322,0.2787914276123047,0.6752286553382874,-0.6828920841217041,0.25070521235466003,0.6752286553382874,-0.6936952471733093,0.22220173478126526,0.6752286553382874,-0.7033438682556152,0.19332842528820038,0.6752286553382874,-0.7118218541145325,0.16413332521915436,0.6752286553382874,-0.7191150784492493,0.13466504216194153,0.6752286553382874,-0.7252113819122314,0.10497263073921204,0.6752286553382874,-0.730100691318512,0.07510549575090408,0.6752286553382874,-0.7337747812271118,0.04511335864663124,0.6752286553382874,-0.7362275719642639,0.015046133659780025,0.6752286553382874,-0.7374550104141235,-0.015046133659780025,0.6752286553382874,-0.7374550104141235,-0.04511335864663124,0.6752286553382874,-0.7362275719642639,-0.07510549575090408,0.6752286553382874,-0.7337747812271118,-0.10497263073921204,0.6752286553382874,-0.730100691318512,-0.13466504216194153,0.6752286553382874,-0.7252113819122314,-0.16413332521915436,0.6752286553382874,-0.7191150784492493,-0.19332842528820038,0.6752286553382874,-0.7118218541145325,-0.22220173478126526,0.6752286553382874,-0.7033438682556152,-0.25070521235466003,0.6752286553382874,-0.6936952471733093,-0.2787914276123047,0.6752286553382874,-0.6828920841217041,-0.3064136207103729,0.6752286553382874,-0.6709522604942322,-0.3335258364677429,0.6752286553382874,-0.6578956842422485,-0.3600828945636749,0.6752286553382874,-0.643744170665741,-0.38604065775871277,0.6752286553382874,-0.6285211443901062,-0.4113558828830719,0.6752286553382874,-0.6122520565986633,-0.4359864592552185,0.6752286553382874,-0.5949639081954956,-0.45989134907722473,0.6752286553382874,-0.5766855478286743,-0.4830308258533478,0.6752286553382874,-0.5574472546577454,-0.505366325378418,0.6752286553382874,-0.5372812151908875,-0.5268607139587402,0.6752286553382874,-0.5162209272384644,-0.547478199005127,0.6752286553382874,-0.494301438331604,-0.5671843886375427,0.6752286553382874,-0.47155919671058655,-0.5859466195106506,0.6752286553382874,-0.44803211092948914,-0.6037335991859436,0.6752286553382874,-0.4237593412399292,-0.6205157041549683,0.6752286553382874,-0.3987812399864197,-0.6362650394439697,0.6752286553382874,-0.3731394112110138,-0.6509553790092468,0.6752286553382874,-0.3468765318393707,-0.6645622849464417,0.6752286553382874,-0.3200363218784332,-0.6770630478858948,0.6752286553382874,-0.29266342520713806,-0.6884369254112244,0.6752286553382874,-0.2648034393787384,-0.6986649632453918,0.6752286553382874,-0.23650269210338593,-0.7077301144599915,0.6752286553382874,-0.20780831575393677,-0.7156173586845398,0.6752286553382874,-0.1787680685520172,-0.7223135232925415,0.6752286553382874,-0.1494302749633789,-0.7278074622154236,0.6752286553382874,-0.11984377354383469,-0.7320900559425354,0.6752286553382874,-0.09005780518054962,-0.7351541519165039,0.6752286553382874,-0.060121938586235046,-0.7369946241378784,0.6752286553382874,-0.030086006969213486,-0.7376084923744202,0.6752286553382874,-1.806619632539752e-16,-0.7834010124206543,0.6215165853500366,0.0,-0.7827490568161011,0.6215165853500366,0.03195381909608841,-0.7807943224906921,0.6215165853500366,0.06385445594787598,-0.7775400280952454,0.6215165853500366,0.09564881026744843,-0.7729915380477905,0.6215165853500366,0.127283975481987,-0.7671565413475037,0.6215165853500366,0.15870727598667145,-0.7600446939468384,0.6215165853500366,0.18986643850803375,-0.7516677975654602,0.6215165853500366,0.22070957720279694,-0.7420397996902466,0.6215165853500366,0.2511853575706482,-0.7311767935752869,0.6215165853500366,0.28124308586120605,-0.7190967798233032,0.6215165853500366,0.3108327090740204,-0.7058199644088745,0.6215165853500366,0.3399049639701843,-0.6913682818412781,0.6215165853500366,0.36841148138046265,-0.6757659912109375,0.6215165853500366,0.3963048458099365,-0.6590389013290405,0.6215165853500366,0.42353856563568115,-0.641214907169342,0.6215165853500366,0.4500673711299896,-0.6223236322402954,0.6215165853500366,0.475847065448761,-0.6023966073989868,0.6215165853500366,0.5008347630500793,-0.5814669728279114,0.6215165853500366,0.5249888896942139,-0.5595695376396179,0.6215165853500366,0.5482692122459412,-0.5367407202720642,0.6215165853500366,0.5706369876861572,-0.5130185484886169,0.6215165853500366,0.5920549631118774,-0.4884425401687622,0.6215165853500366,0.6124876141548157,-0.46305355429649353,0.6215165853500366,0.6319007277488708,-0.43689388036727905,0.6215165853500366,0.650262176990509,-0.4100070297718048,0.6215165853500366,0.6675413250923157,-0.38243773579597473,0.6215165853500366,0.6837093830108643,-0.35423192381858826,0.6215165853500366,0.6987395286560059,-0.3254365622997284,0.6215165853500366,0.71260666847229,-0.2960995137691498,0.6215165853500366,0.725287675857544,-0.26626962423324585,0.6215165853500366,0.7367615699768066,-0.2359965741634369,0.6215165853500366,0.7470092177391052,-0.2053307294845581,0.6215165853500366,0.7560135126113892,-0.17432312667369843,0.6215165853500366,0.7637595534324646,-0.14302538335323334,0.6215165853500366,0.7702343463897705,-0.11148959398269653,0.6215165853500366,0.7754271626472473,-0.07976823300123215,0.6215165853500366,0.7793292999267578,-0.04791410639882088,0.6215165853500366,0.781934380531311,-0.015980234369635582,0.6215165853500366,0.7832379937171936,0.015980234369635582,0.6215165853500366,0.7832379937171936,0.04791410639882088,0.6215165853500366,0.781934380531311,0.07976823300123215,0.6215165853500366,0.7793292999267578,0.11148959398269653,0.6215165853500366,0.7754271626472473,0.14302538335323334,0.6215165853500366,0.7702343463897705,0.17432312667369843,0.6215165853500366,0.7637595534324646,0.2053307294845581,0.6215165853500366,0.7560135126113892,0.2359965741634369,0.6215165853500366,0.7470092177391052,0.26626962423324585,0.6215165853500366,0.7367615699768066,0.2960995137691498,0.6215165853500366,0.725287675857544,0.3254365622997284,0.6215165853500366,0.71260666847229,0.35423192381858826,0.6215165853500366,0.6987395286560059,0.38243773579597473,0.6215165853500366,0.6837093830108643,0.4100070297718048,0.6215165853500366,0.6675413250923157,0.43689388036727905,0.6215165853500366,0.650262176990509,0.46305355429649353,0.6215165853500366,0.6319007277488708,0.4884425401687622,0.6215165853500366,0.6124876141548157,0.5130185484886169,0.6215165853500366,0.5920549631118774,0.5367407202720642,0.6215165853500366,0.5706369876861572,0.5595695376396179,0.6215165853500366,0.5482692122459412,0.5814669728279114,0.6215165853500366,0.5249888896942139,0.6023966073989868,0.6215165853500366,0.5008347630500793,0.6223236322402954,0.6215165853500366,0.475847065448761,0.641214907169342,0.6215165853500366,0.4500673711299896,0.6590389013290405,0.6215165853500366,0.42353856563568115,0.6757659912109375,0.6215165853500366,0.3963048458099365,0.6913682818412781,0.6215165853500366,0.36841148138046265,0.7058199644088745,0.6215165853500366,0.3399049639701843,0.7190967798233032,0.6215165853500366,0.3108327090740204,0.7311767935752869,0.6215165853500366,0.28124308586120605,0.7420397996902466,0.6215165853500366,0.2511853575706482,0.7516677975654602,0.6215165853500366,0.22070957720279694,0.7600446939468384,0.6215165853500366,0.18986643850803375,0.7671565413475037,0.6215165853500366,0.15870727598667145,0.7729915380477905,0.6215165853500366,0.127283975481987,0.7775400280952454,0.6215165853500366,0.09564881026744843,0.7807943224906921,0.6215165853500366,0.06385445594787598,0.7827490568161011,0.6215165853500366,0.03195381909608841,0.7834010124206543,0.6215165853500366,9.593895516018681e-17,0.7827490568161011,0.6215165853500366,-0.03195381909608841,0.7807943224906921,0.6215165853500366,-0.06385445594787598,0.7775400280952454,0.6215165853500366,-0.09564881026744843,0.7729915380477905,0.6215165853500366,-0.127283975481987,0.7671565413475037,0.6215165853500366,-0.15870727598667145,0.7600446939468384,0.6215165853500366,-0.18986643850803375,0.7516677975654602,0.6215165853500366,-0.22070957720279694,0.7420397996902466,0.6215165853500366,-0.2511853575706482,0.7311767935752869,0.6215165853500366,-0.28124308586120605,0.7190967798233032,0.6215165853500366,-0.3108327090740204,0.7058199644088745,0.6215165853500366,-0.3399049639701843,0.6913682818412781,0.6215165853500366,-0.36841148138046265,0.6757659912109375,0.6215165853500366,-0.3963048458099365,0.6590389013290405,0.6215165853500366,-0.42353856563568115,0.641214907169342,0.6215165853500366,-0.4500673711299896,0.6223236322402954,0.6215165853500366,-0.475847065448761,0.6023966073989868,0.6215165853500366,-0.5008347630500793,0.5814669728279114,0.6215165853500366,-0.5249888896942139,0.5595695376396179,0.6215165853500366,-0.5482692122459412,0.5367407202720642,0.6215165853500366,-0.5706369876861572,0.5130185484886169,0.6215165853500366,-0.5920549631118774,0.4884425401687622,0.6215165853500366,-0.6124876141548157,0.46305355429649353,0.6215165853500366,-0.6319007277488708,0.43689388036727905,0.6215165853500366,-0.650262176990509,0.4100070297718048,0.6215165853500366,-0.6675413250923157,0.38243773579597473,0.6215165853500366,-0.6837093830108643,0.35423192381858826,0.6215165853500366,-0.6987395286560059,0.3254365622997284,0.6215165853500366,-0.71260666847229,0.2960995137691498,0.6215165853500366,-0.725287675857544,0.26626962423324585,0.6215165853500366,-0.7367615699768066,0.2359965741634369,0.6215165853500366,-0.7470092177391052,0.2053307294845581,0.6215165853500366,-0.7560135126113892,0.17432312667369843,0.6215165853500366,-0.7637595534324646,0.14302538335323334,0.6215165853500366,-0.7702343463897705,0.11148959398269653,0.6215165853500366,-0.7754271626472473,0.07976823300123215,0.6215165853500366,-0.7793292999267578,0.04791410639882088,0.6215165853500366,-0.781934380531311,0.015980234369635582,0.6215165853500366,-0.7832379937171936,-0.015980234369635582,0.6215165853500366,-0.7832379937171936,-0.04791410639882088,0.6215165853500366,-0.781934380531311,-0.07976823300123215,0.6215165853500366,-0.7793292999267578,-0.11148959398269653,0.6215165853500366,-0.7754271626472473,-0.14302538335323334,0.6215165853500366,-0.7702343463897705,-0.17432312667369843,0.6215165853500366,-0.7637595534324646,-0.2053307294845581,0.6215165853500366,-0.7560135126113892,-0.2359965741634369,0.6215165853500366,-0.7470092177391052,-0.26626962423324585,0.6215165853500366,-0.7367615699768066,-0.2960995137691498,0.6215165853500366,-0.725287675857544,-0.3254365622997284,0.6215165853500366,-0.71260666847229,-0.35423192381858826,0.6215165853500366,-0.6987395286560059,-0.38243773579597473,0.6215165853500366,-0.6837093830108643,-0.4100070297718048,0.6215165853500366,-0.6675413250923157,-0.43689388036727905,0.6215165853500366,-0.650262176990509,-0.46305355429649353,0.6215165853500366,-0.6319007277488708,-0.4884425401687622,0.6215165853500366,-0.6124876141548157,-0.5130185484886169,0.6215165853500366,-0.5920549631118774,-0.5367407202720642,0.6215165853500366,-0.5706369876861572,-0.5595695376396179,0.6215165853500366,-0.5482692122459412,-0.5814669728279114,0.6215165853500366,-0.5249888896942139,-0.6023966073989868,0.6215165853500366,-0.5008347630500793,-0.6223236322402954,0.6215165853500366,-0.475847065448761,-0.641214907169342,0.6215165853500366,-0.4500673711299896,-0.6590389013290405,0.6215165853500366,-0.42353856563568115,-0.6757659912109375,0.6215165853500366,-0.3963048458099365,-0.6913682818412781,0.6215165853500366,-0.36841148138046265,-0.7058199644088745,0.6215165853500366,-0.3399049639701843,-0.7190967798233032,0.6215165853500366,-0.3108327090740204,-0.7311767935752869,0.6215165853500366,-0.28124308586120605,-0.7420397996902466,0.6215165853500366,-0.2511853575706482,-0.7516677975654602,0.6215165853500366,-0.22070957720279694,-0.7600446939468384,0.6215165853500366,-0.18986643850803375,-0.7671565413475037,0.6215165853500366,-0.15870727598667145,-0.7729915380477905,0.6215165853500366,-0.127283975481987,-0.7775400280952454,0.6215165853500366,-0.09564881026744843,-0.7807943224906921,0.6215165853500366,-0.06385445594787598,-0.7827490568161011,0.6215165853500366,-0.03195381909608841,-0.7834010124206543,0.6215165853500366,-1.9187791032037363e-16,-0.8252907395362854,0.5647080540657043,0.0,-0.8246039152145386,0.5647080540657043,0.03366244211792946,-0.8225446343421936,0.5647080540657043,0.06726885586977005,-0.8191162943840027,0.5647080540657043,0.10076330602169037,-0.814324676990509,0.5647080540657043,0.13409005105495453,-0.808177649974823,0.5647080540657043,0.16719360649585724,-0.800685465335846,0.5647080540657043,0.20001889765262604,-0.7918606400489807,0.5647080540657043,0.2325112670660019,-0.781717836856842,0.5647080540657043,0.26461663842201233,-0.7702739834785461,0.5647080540657043,0.2962816059589386,-0.7575480341911316,0.5647080540657043,0.3274534046649933,-0.7435612678527832,0.5647080540657043,0.3580802381038666,-0.7283368706703186,0.5647080540657043,0.38811105489730835,-0.7119002342224121,0.5647080540657043,0.41749587655067444,-0.6942787170410156,0.5647080540657043,0.4461858570575714,-0.6755016446113586,0.5647080540657043,0.4741331934928894,-0.6556003093719482,0.5647080540657043,0.5012913942337036,-0.63460773229599,0.5647080540657043,0.5276151895523071,-0.6125589609146118,0.5647080540657043,0.5530608892440796,-0.5894905924797058,0.5647080540657043,0.5775860548019409,-0.5654411315917969,0.5647080540657043,0.60114985704422,-0.5404505133628845,0.5647080540657043,0.6237131357192993,-0.514560341835022,0.5647080540657043,0.6452382802963257,-0.4878137707710266,0.5647080540657043,0.6656894683837891,-0.46025529503822327,0.5647080540657043,0.6850327253341675,-0.4319307506084442,0.5647080540657043,0.7032358050346375,-0.4028872847557068,0.5647080540657043,0.7202684283256531,-0.3731732666492462,0.5647080540657043,0.7361022233963013,-0.3428381681442261,0.5647080540657043,0.7507108449935913,-0.31193241477012634,0.5647080540657043,0.7640699744224548,-0.28050747513771057,0.5647080540657043,0.7761574387550354,-0.24861568212509155,0.5647080540657043,0.7869529724121094,-0.21631008386611938,0.5647080540657043,0.7964387536048889,-0.18364445865154266,0.5647080540657043,0.8045989871025085,-0.15067318081855774,0.5647080540657043,0.8114199638366699,-0.11745111644268036,0.5647080540657043,0.8168904781341553,-0.08403356373310089,0.5647080540657043,0.8210012912750244,-0.05047615244984627,0.5647080540657043,0.8237456679344177,-0.016834722831845284,0.5647080540657043,0.8251190185546875,0.016834722831845284,0.5647080540657043,0.8251190185546875,0.05047615244984627,0.5647080540657043,0.8237456679344177,0.08403356373310089,0.5647080540657043,0.8210012912750244,0.11745111644268036,0.5647080540657043,0.8168904781341553,0.15067318081855774,0.5647080540657043,0.8114199638366699,0.18364445865154266,0.5647080540657043,0.8045989871025085,0.21631008386611938,0.5647080540657043,0.7964387536048889,0.24861568212509155,0.5647080540657043,0.7869529724121094,0.28050747513771057,0.5647080540657043,0.7761574387550354,0.31193241477012634,0.5647080540657043,0.7640699744224548,0.3428381681442261,0.5647080540657043,0.7507108449935913,0.3731732666492462,0.5647080540657043,0.7361022233963013,0.4028872847557068,0.5647080540657043,0.7202684283256531,0.4319307506084442,0.5647080540657043,0.7032358050346375,0.46025529503822327,0.5647080540657043,0.6850327253341675,0.4878137707710266,0.5647080540657043,0.6656894683837891,0.514560341835022,0.5647080540657043,0.6452382802963257,0.5404505133628845,0.5647080540657043,0.6237131357192993,0.5654411315917969,0.5647080540657043,0.60114985704422,0.5894905924797058,0.5647080540657043,0.5775860548019409,0.6125589609146118,0.5647080540657043,0.5530608892440796,0.63460773229599,0.5647080540657043,0.5276151895523071,0.6556003093719482,0.5647080540657043,0.5012913942337036,0.6755016446113586,0.5647080540657043,0.4741331934928894,0.6942787170410156,0.5647080540657043,0.4461858570575714,0.7119002342224121,0.5647080540657043,0.41749587655067444,0.7283368706703186,0.5647080540657043,0.38811105489730835,0.7435612678527832,0.5647080540657043,0.3580802381038666,0.7575480341911316,0.5647080540657043,0.3274534046649933,0.7702739834785461,0.5647080540657043,0.2962816059589386,0.781717836856842,0.5647080540657043,0.26461663842201233,0.7918606400489807,0.5647080540657043,0.2325112670660019,0.800685465335846,0.5647080540657043,0.20001889765262604,0.808177649974823,0.5647080540657043,0.16719360649585724,0.814324676990509,0.5647080540657043,0.13409005105495453,0.8191162943840027,0.5647080540657043,0.10076330602169037,0.8225446343421936,0.5647080540657043,0.06726885586977005,0.8246039152145386,0.5647080540657043,0.03366244211792946,0.8252907395362854,0.5647080540657043,1.0106896388311818e-16,0.8246039152145386,0.5647080540657043,-0.03366244211792946,0.8225446343421936,0.5647080540657043,-0.06726885586977005,0.8191162943840027,0.5647080540657043,-0.10076330602169037,0.814324676990509,0.5647080540657043,-0.13409005105495453,0.808177649974823,0.5647080540657043,-0.16719360649585724,0.800685465335846,0.5647080540657043,-0.20001889765262604,0.7918606400489807,0.5647080540657043,-0.2325112670660019,0.781717836856842,0.5647080540657043,-0.26461663842201233,0.7702739834785461,0.5647080540657043,-0.2962816059589386,0.7575480341911316,0.5647080540657043,-0.3274534046649933,0.7435612678527832,0.5647080540657043,-0.3580802381038666,0.7283368706703186,0.5647080540657043,-0.38811105489730835,0.7119002342224121,0.5647080540657043,-0.41749587655067444,0.6942787170410156,0.5647080540657043,-0.4461858570575714,0.6755016446113586,0.5647080540657043,-0.4741331934928894,0.6556003093719482,0.5647080540657043,-0.5012913942337036,0.63460773229599,0.5647080540657043,-0.5276151895523071,0.6125589609146118,0.5647080540657043,-0.5530608892440796,0.5894905924797058,0.5647080540657043,-0.5775860548019409,0.5654411315917969,0.5647080540657043,-0.60114985704422,0.5404505133628845,0.5647080540657043,-0.6237131357192993,0.514560341835022,0.5647080540657043,-0.6452382802963257,0.4878137707710266,0.5647080540657043,-0.6656894683837891,0.46025529503822327,0.5647080540657043,-0.6850327253341675,0.4319307506084442,0.5647080540657043,-0.7032358050346375,0.4028872847557068,0.5647080540657043,-0.7202684283256531,0.3731732666492462,0.5647080540657043,-0.7361022233963013,0.3428381681442261,0.5647080540657043,-0.7507108449935913,0.31193241477012634,0.5647080540657043,-0.7640699744224548,0.28050747513771057,0.5647080540657043,-0.7761574387550354,0.24861568212509155,0.5647080540657043,-0.7869529724121094,0.21631008386611938,0.5647080540657043,-0.7964387536048889,0.18364445865154266,0.5647080540657043,-0.8045989871025085,0.15067318081855774,0.5647080540657043,-0.8114199638366699,0.11745111644268036,0.5647080540657043,-0.8168904781341553,0.08403356373310089,0.5647080540657043,-0.8210012912750244,0.05047615244984627,0.5647080540657043,-0.8237456679344177,0.016834722831845284,0.5647080540657043,-0.8251190185546875,-0.016834722831845284,0.5647080540657043,-0.8251190185546875,-0.05047615244984627,0.5647080540657043,-0.8237456679344177,-0.08403356373310089,0.5647080540657043,-0.8210012912750244,-0.11745111644268036,0.5647080540657043,-0.8168904781341553,-0.15067318081855774,0.5647080540657043,-0.8114199638366699,-0.18364445865154266,0.5647080540657043,-0.8045989871025085,-0.21631008386611938,0.5647080540657043,-0.7964387536048889,-0.24861568212509155,0.5647080540657043,-0.7869529724121094,-0.28050747513771057,0.5647080540657043,-0.7761574387550354,-0.31193241477012634,0.5647080540657043,-0.7640699744224548,-0.3428381681442261,0.5647080540657043,-0.7507108449935913,-0.3731732666492462,0.5647080540657043,-0.7361022233963013,-0.4028872847557068,0.5647080540657043,-0.7202684283256531,-0.4319307506084442,0.5647080540657043,-0.7032358050346375,-0.46025529503822327,0.5647080540657043,-0.6850327253341675,-0.4878137707710266,0.5647080540657043,-0.6656894683837891,-0.514560341835022,0.5647080540657043,-0.6452382802963257,-0.5404505133628845,0.5647080540657043,-0.6237131357192993,-0.5654411315917969,0.5647080540657043,-0.60114985704422,-0.5894905924797058,0.5647080540657043,-0.5775860548019409,-0.6125589609146118,0.5647080540657043,-0.5530608892440796,-0.63460773229599,0.5647080540657043,-0.5276151895523071,-0.6556003093719482,0.5647080540657043,-0.5012913942337036,-0.6755016446113586,0.5647080540657043,-0.4741331934928894,-0.6942787170410156,0.5647080540657043,-0.4461858570575714,-0.7119002342224121,0.5647080540657043,-0.41749587655067444,-0.7283368706703186,0.5647080540657043,-0.38811105489730835,-0.7435612678527832,0.5647080540657043,-0.3580802381038666,-0.7575480341911316,0.5647080540657043,-0.3274534046649933,-0.7702739834785461,0.5647080540657043,-0.2962816059589386,-0.781717836856842,0.5647080540657043,-0.26461663842201233,-0.7918606400489807,0.5647080540657043,-0.2325112670660019,-0.800685465335846,0.5647080540657043,-0.20001889765262604,-0.808177649974823,0.5647080540657043,-0.16719360649585724,-0.814324676990509,0.5647080540657043,-0.13409005105495453,-0.8191162943840027,0.5647080540657043,-0.10076330602169037,-0.8225446343421936,0.5647080540657043,-0.06726885586977005,-0.8246039152145386,0.5647080540657043,-0.03366244211792946,-0.8252907395362854,0.5647080540657043,-2.0213792776623636e-16,-0.8630688786506653,0.5050862431526184,0.0,-0.8623506426811218,0.5050862431526184,0.03520336002111435,-0.8601970672607422,0.5050862431526184,0.07034812867641449,-0.8566118478775024,0.5050862431526184,0.10537580400705338,-0.8516008257865906,0.5050862431526184,0.14022809267044067,-0.8451724052429199,0.5050862431526184,0.17484699189662933,-0.83733731508255,0.5050862431526184,0.20917487144470215,-0.8281084895133972,0.5050862431526184,0.2431546002626419,-0.817501425743103,0.5050862431526184,0.27672961354255676,-0.8055337071418762,0.5050862431526184,0.309844046831131,-0.7922252416610718,0.5050862431526184,0.3424427807331085,-0.7775982022285461,0.5050862431526184,0.3744715452194214,-0.7616769075393677,0.5050862431526184,0.4058770537376404,-0.7444878816604614,0.5050862431526184,0.4366070032119751,-0.7260597348213196,0.5050862431526184,0.46661025285720825,-0.7064231634140015,0.5050862431526184,0.49583688378334045,-0.6856107711791992,0.5050862431526184,0.5242382884025574,-0.6636572480201721,0.5050862431526184,0.551767110824585,-0.6405991911888123,0.5050862431526184,0.5783775448799133,-0.6164748668670654,0.5050862431526184,0.6040253639221191,-0.591324508190155,0.5050862431526184,0.6286678314208984,-0.5651898980140686,0.5050862431526184,0.6522639393806458,-0.5381146669387817,0.5050862431526184,0.6747744083404541,-0.5101436972618103,0.5050862431526184,0.6961618065834045,-0.4813237190246582,0.5050862431526184,0.7163904905319214,-0.45170262455940247,0.5050862431526184,0.7354268431663513,-0.42132967710494995,0.5050862431526184,0.753239095211029,-0.39025551080703735,0.5050862431526184,0.769797682762146,-0.35853177309036255,0.5050862431526184,0.7850750684738159,-0.3262113034725189,0.5050862431526184,0.799045741558075,-0.29334786534309387,0.5050862431526184,0.8116864562034607,-0.2599962055683136,0.5050862431526184,0.8229762315750122,-0.2262118011713028,0.5050862431526184,0.8328962326049805,-0.19205088913440704,0.5050862431526184,0.8414299488067627,-0.15757033228874207,0.5050862431526184,0.8485631942749023,-0.12282750755548477,0.5050862431526184,0.8542840480804443,-0.08788024634122849,0.5050862431526184,0.8585830926895142,-0.052786726504564285,0.5050862431526184,0.861453115940094,-0.017605341970920563,0.5050862431526184,0.862889289855957,0.017605341970920563,0.5050862431526184,0.862889289855957,0.052786726504564285,0.5050862431526184,0.861453115940094,0.08788024634122849,0.5050862431526184,0.8585830926895142,0.12282750755548477,0.5050862431526184,0.8542840480804443,0.15757033228874207,0.5050862431526184,0.8485631942749023,0.19205088913440704,0.5050862431526184,0.8414299488067627,0.2262118011713028,0.5050862431526184,0.8328962326049805,0.2599962055683136,0.5050862431526184,0.8229762315750122,0.29334786534309387,0.5050862431526184,0.8116864562034607,0.3262113034725189,0.5050862431526184,0.799045741558075,0.35853177309036255,0.5050862431526184,0.7850750684738159,0.39025551080703735,0.5050862431526184,0.769797682762146,0.42132967710494995,0.5050862431526184,0.753239095211029,0.45170262455940247,0.5050862431526184,0.7354268431663513,0.4813237190246582,0.5050862431526184,0.7163904905319214,0.5101436972618103,0.5050862431526184,0.6961618065834045,0.5381146669387817,0.5050862431526184,0.6747744083404541,0.5651898980140686,0.5050862431526184,0.6522639393806458,0.591324508190155,0.5050862431526184,0.6286678314208984,0.6164748668670654,0.5050862431526184,0.6040253639221191,0.6405991911888123,0.5050862431526184,0.5783775448799133,0.6636572480201721,0.5050862431526184,0.551767110824585,0.6856107711791992,0.5050862431526184,0.5242382884025574,0.7064231634140015,0.5050862431526184,0.49583688378334045,0.7260597348213196,0.5050862431526184,0.46661025285720825,0.7444878816604614,0.5050862431526184,0.4366070032119751,0.7616769075393677,0.5050862431526184,0.4058770537376404,0.7775982022285461,0.5050862431526184,0.3744715452194214,0.7922252416610718,0.5050862431526184,0.3424427807331085,0.8055337071418762,0.5050862431526184,0.309844046831131,0.817501425743103,0.5050862431526184,0.27672961354255676,0.8281084895133972,0.5050862431526184,0.2431546002626419,0.83733731508255,0.5050862431526184,0.20917487144470215,0.8451724052429199,0.5050862431526184,0.17484699189662933,0.8516008257865906,0.5050862431526184,0.14022809267044067,0.8566118478775024,0.5050862431526184,0.10537580400705338,0.8601970672607422,0.5050862431526184,0.07034812867641449,0.8623506426811218,0.5050862431526184,0.03520336002111435,0.8630688786506653,0.5050862431526184,1.0569545122357627e-16,0.8623506426811218,0.5050862431526184,-0.03520336002111435,0.8601970672607422,0.5050862431526184,-0.07034812867641449,0.8566118478775024,0.5050862431526184,-0.10537580400705338,0.8516008257865906,0.5050862431526184,-0.14022809267044067,0.8451724052429199,0.5050862431526184,-0.17484699189662933,0.83733731508255,0.5050862431526184,-0.20917487144470215,0.8281084895133972,0.5050862431526184,-0.2431546002626419,0.817501425743103,0.5050862431526184,-0.27672961354255676,0.8055337071418762,0.5050862431526184,-0.309844046831131,0.7922252416610718,0.5050862431526184,-0.3424427807331085,0.7775982022285461,0.5050862431526184,-0.3744715452194214,0.7616769075393677,0.5050862431526184,-0.4058770537376404,0.7444878816604614,0.5050862431526184,-0.4366070032119751,0.7260597348213196,0.5050862431526184,-0.46661025285720825,0.7064231634140015,0.5050862431526184,-0.49583688378334045,0.6856107711791992,0.5050862431526184,-0.5242382884025574,0.6636572480201721,0.5050862431526184,-0.551767110824585,0.6405991911888123,0.5050862431526184,-0.5783775448799133,0.6164748668670654,0.5050862431526184,-0.6040253639221191,0.591324508190155,0.5050862431526184,-0.6286678314208984,0.5651898980140686,0.5050862431526184,-0.6522639393806458,0.5381146669387817,0.5050862431526184,-0.6747744083404541,0.5101436972618103,0.5050862431526184,-0.6961618065834045,0.4813237190246582,0.5050862431526184,-0.7163904905319214,0.45170262455940247,0.5050862431526184,-0.7354268431663513,0.42132967710494995,0.5050862431526184,-0.753239095211029,0.39025551080703735,0.5050862431526184,-0.769797682762146,0.35853177309036255,0.5050862431526184,-0.7850750684738159,0.3262113034725189,0.5050862431526184,-0.799045741558075,0.29334786534309387,0.5050862431526184,-0.8116864562034607,0.2599962055683136,0.5050862431526184,-0.8229762315750122,0.2262118011713028,0.5050862431526184,-0.8328962326049805,0.19205088913440704,0.5050862431526184,-0.8414299488067627,0.15757033228874207,0.5050862431526184,-0.8485631942749023,0.12282750755548477,0.5050862431526184,-0.8542840480804443,0.08788024634122849,0.5050862431526184,-0.8585830926895142,0.052786726504564285,0.5050862431526184,-0.861453115940094,0.017605341970920563,0.5050862431526184,-0.862889289855957,-0.017605341970920563,0.5050862431526184,-0.862889289855957,-0.052786726504564285,0.5050862431526184,-0.861453115940094,-0.08788024634122849,0.5050862431526184,-0.8585830926895142,-0.12282750755548477,0.5050862431526184,-0.8542840480804443,-0.15757033228874207,0.5050862431526184,-0.8485631942749023,-0.19205088913440704,0.5050862431526184,-0.8414299488067627,-0.2262118011713028,0.5050862431526184,-0.8328962326049805,-0.2599962055683136,0.5050862431526184,-0.8229762315750122,-0.29334786534309387,0.5050862431526184,-0.8116864562034607,-0.3262113034725189,0.5050862431526184,-0.799045741558075,-0.35853177309036255,0.5050862431526184,-0.7850750684738159,-0.39025551080703735,0.5050862431526184,-0.769797682762146,-0.42132967710494995,0.5050862431526184,-0.753239095211029,-0.45170262455940247,0.5050862431526184,-0.7354268431663513,-0.4813237190246582,0.5050862431526184,-0.7163904905319214,-0.5101436972618103,0.5050862431526184,-0.6961618065834045,-0.5381146669387817,0.5050862431526184,-0.6747744083404541,-0.5651898980140686,0.5050862431526184,-0.6522639393806458,-0.591324508190155,0.5050862431526184,-0.6286678314208984,-0.6164748668670654,0.5050862431526184,-0.6040253639221191,-0.6405991911888123,0.5050862431526184,-0.5783775448799133,-0.6636572480201721,0.5050862431526184,-0.551767110824585,-0.6856107711791992,0.5050862431526184,-0.5242382884025574,-0.7064231634140015,0.5050862431526184,-0.49583688378334045,-0.7260597348213196,0.5050862431526184,-0.46661025285720825,-0.7444878816604614,0.5050862431526184,-0.4366070032119751,-0.7616769075393677,0.5050862431526184,-0.4058770537376404,-0.7775982022285461,0.5050862431526184,-0.3744715452194214,-0.7922252416610718,0.5050862431526184,-0.3424427807331085,-0.8055337071418762,0.5050862431526184,-0.309844046831131,-0.817501425743103,0.5050862431526184,-0.27672961354255676,-0.8281084895133972,0.5050862431526184,-0.2431546002626419,-0.83733731508255,0.5050862431526184,-0.20917487144470215,-0.8451724052429199,0.5050862431526184,-0.17484699189662933,-0.8516008257865906,0.5050862431526184,-0.14022809267044067,-0.8566118478775024,0.5050862431526184,-0.10537580400705338,-0.8601970672607422,0.5050862431526184,-0.07034812867641449,-0.8623506426811218,0.5050862431526184,-0.03520336002111435,-0.8630688786506653,0.5050862431526184,-2.1139090244715254e-16,-0.896547257900238,0.4429481327533722,0.0,-0.8958011269569397,0.4429481327533722,0.03656889498233795,-0.8935640454292297,0.4429481327533722,0.07307692617177963,-0.8898397088050842,0.4429481327533722,0.10946332663297653,-0.8846343755722046,0.4429481327533722,0.14566753804683685,-0.8779565691947937,0.4429481327533722,0.18162928521633148,-0.8698175549507141,0.4429481327533722,0.21728874742984772,-0.8602308034896851,0.4429481327533722,0.2525865435600281,-0.8492122292518616,0.4429481327533722,0.2874639332294464,-0.836780309677124,0.4429481327533722,0.3218628764152527,-0.8229556083679199,0.4429481327533722,0.35572612285614014,-0.8077611327171326,0.4429481327533722,0.38899725675582886,-0.7912222743034363,0.4429481327533722,0.4216209948062897,-0.7733665108680725,0.4429481327533722,0.45354294776916504,-0.7542235255241394,0.4429481327533722,0.48471003770828247,-0.7338252067565918,0.4429481327533722,0.515070378780365,-0.7122055292129517,0.4429481327533722,0.5445734262466431,-0.6894004940986633,0.4429481327533722,0.5731700658798218,-0.6654479503631592,0.4429481327533722,0.6008127927780151,-0.6403878927230835,0.4429481327533722,0.627455472946167,-0.6142619252204895,0.4429481327533722,0.6530538201332092,-0.5871135592460632,0.4429481327533722,0.6775652170181274,-0.5589880347251892,0.4429481327533722,0.7009488344192505,-0.5299321413040161,0.4429481327533722,0.7231658697128296,-0.4999942481517792,0.4429481327533722,0.7441792488098145,-0.46922412514686584,0.4429481327533722,0.7639539837837219,-0.43767303228378296,0.4429481327533722,0.7824572324752808,-0.40539348125457764,0.4429481327533722,0.7996581196784973,-0.3724391758441925,0.4429481327533722,0.8155280351638794,-0.3388650119304657,0.4429481327533722,0.8300406336784363,-0.30472680926322937,0.4429481327533722,0.8431717157363892,-0.27008143067359924,0.4429481327533722,0.8548994064331055,-0.2349865436553955,0.4429481327533722,0.8652042150497437,-0.19950053095817566,0.4429481327533722,0.8740689158439636,-0.1636824756860733,0.4429481327533722,0.8814789056777954,-0.12759198248386383,0.4429481327533722,0.8874216675758362,-0.09128911793231964,0.4429481327533722,0.8918874859809875,-0.05483431741595268,0.4429481327533722,0.894868791103363,-0.018288252875208855,0.4429481327533722,0.8963606953620911,0.018288252875208855,0.4429481327533722,0.8963606953620911,0.05483431741595268,0.4429481327533722,0.894868791103363,0.09128911793231964,0.4429481327533722,0.8918874859809875,0.12759198248386383,0.4429481327533722,0.8874216675758362,0.1636824756860733,0.4429481327533722,0.8814789056777954,0.19950053095817566,0.4429481327533722,0.8740689158439636,0.2349865436553955,0.4429481327533722,0.8652042150497437,0.27008143067359924,0.4429481327533722,0.8548994064331055,0.30472680926322937,0.4429481327533722,0.8431717157363892,0.3388650119304657,0.4429481327533722,0.8300406336784363,0.3724391758441925,0.4429481327533722,0.8155280351638794,0.40539348125457764,0.4429481327533722,0.7996581196784973,0.43767303228378296,0.4429481327533722,0.7824572324752808,0.46922412514686584,0.4429481327533722,0.7639539837837219,0.4999942481517792,0.4429481327533722,0.7441792488098145,0.5299321413040161,0.4429481327533722,0.7231658697128296,0.5589880347251892,0.4429481327533722,0.7009488344192505,0.5871135592460632,0.4429481327533722,0.6775652170181274,0.6142619252204895,0.4429481327533722,0.6530538201332092,0.6403878927230835,0.4429481327533722,0.627455472946167,0.6654479503631592,0.4429481327533722,0.6008127927780151,0.6894004940986633,0.4429481327533722,0.5731700658798218,0.7122055292129517,0.4429481327533722,0.5445734262466431,0.7338252067565918,0.4429481327533722,0.515070378780365,0.7542235255241394,0.4429481327533722,0.48471003770828247,0.7733665108680725,0.4429481327533722,0.45354294776916504,0.7912222743034363,0.4429481327533722,0.4216209948062897,0.8077611327171326,0.4429481327533722,0.38899725675582886,0.8229556083679199,0.4429481327533722,0.35572612285614014,0.836780309677124,0.4429481327533722,0.3218628764152527,0.8492122292518616,0.4429481327533722,0.2874639332294464,0.8602308034896851,0.4429481327533722,0.2525865435600281,0.8698175549507141,0.4429481327533722,0.21728874742984772,0.8779565691947937,0.4429481327533722,0.18162928521633148,0.8846343755722046,0.4429481327533722,0.14566753804683685,0.8898397088050842,0.4429481327533722,0.10946332663297653,0.8935640454292297,0.4429481327533722,0.07307692617177963,0.8958011269569397,0.4429481327533722,0.03656889498233795,0.896547257900238,0.4429481327533722,1.097953686209729e-16,0.8958011269569397,0.4429481327533722,-0.03656889498233795,0.8935640454292297,0.4429481327533722,-0.07307692617177963,0.8898397088050842,0.4429481327533722,-0.10946332663297653,0.8846343755722046,0.4429481327533722,-0.14566753804683685,0.8779565691947937,0.4429481327533722,-0.18162928521633148,0.8698175549507141,0.4429481327533722,-0.21728874742984772,0.8602308034896851,0.4429481327533722,-0.2525865435600281,0.8492122292518616,0.4429481327533722,-0.2874639332294464,0.836780309677124,0.4429481327533722,-0.3218628764152527,0.8229556083679199,0.4429481327533722,-0.35572612285614014,0.8077611327171326,0.4429481327533722,-0.38899725675582886,0.7912222743034363,0.4429481327533722,-0.4216209948062897,0.7733665108680725,0.4429481327533722,-0.45354294776916504,0.7542235255241394,0.4429481327533722,-0.48471003770828247,0.7338252067565918,0.4429481327533722,-0.515070378780365,0.7122055292129517,0.4429481327533722,-0.5445734262466431,0.6894004940986633,0.4429481327533722,-0.5731700658798218,0.6654479503631592,0.4429481327533722,-0.6008127927780151,0.6403878927230835,0.4429481327533722,-0.627455472946167,0.6142619252204895,0.4429481327533722,-0.6530538201332092,0.5871135592460632,0.4429481327533722,-0.6775652170181274,0.5589880347251892,0.4429481327533722,-0.7009488344192505,0.5299321413040161,0.4429481327533722,-0.7231658697128296,0.4999942481517792,0.4429481327533722,-0.7441792488098145,0.46922412514686584,0.4429481327533722,-0.7639539837837219,0.43767303228378296,0.4429481327533722,-0.7824572324752808,0.40539348125457764,0.4429481327533722,-0.7996581196784973,0.3724391758441925,0.4429481327533722,-0.8155280351638794,0.3388650119304657,0.4429481327533722,-0.8300406336784363,0.30472680926322937,0.4429481327533722,-0.8431717157363892,0.27008143067359924,0.4429481327533722,-0.8548994064331055,0.2349865436553955,0.4429481327533722,-0.8652042150497437,0.19950053095817566,0.4429481327533722,-0.8740689158439636,0.1636824756860733,0.4429481327533722,-0.8814789056777954,0.12759198248386383,0.4429481327533722,-0.8874216675758362,0.09128911793231964,0.4429481327533722,-0.8918874859809875,0.05483431741595268,0.4429481327533722,-0.894868791103363,0.018288252875208855,0.4429481327533722,-0.8963606953620911,-0.018288252875208855,0.4429481327533722,-0.8963606953620911,-0.05483431741595268,0.4429481327533722,-0.894868791103363,-0.09128911793231964,0.4429481327533722,-0.8918874859809875,-0.12759198248386383,0.4429481327533722,-0.8874216675758362,-0.1636824756860733,0.4429481327533722,-0.8814789056777954,-0.19950053095817566,0.4429481327533722,-0.8740689158439636,-0.2349865436553955,0.4429481327533722,-0.8652042150497437,-0.27008143067359924,0.4429481327533722,-0.8548994064331055,-0.30472680926322937,0.4429481327533722,-0.8431717157363892,-0.3388650119304657,0.4429481327533722,-0.8300406336784363,-0.3724391758441925,0.4429481327533722,-0.8155280351638794,-0.40539348125457764,0.4429481327533722,-0.7996581196784973,-0.43767303228378296,0.4429481327533722,-0.7824572324752808,-0.46922412514686584,0.4429481327533722,-0.7639539837837219,-0.4999942481517792,0.4429481327533722,-0.7441792488098145,-0.5299321413040161,0.4429481327533722,-0.7231658697128296,-0.5589880347251892,0.4429481327533722,-0.7009488344192505,-0.5871135592460632,0.4429481327533722,-0.6775652170181274,-0.6142619252204895,0.4429481327533722,-0.6530538201332092,-0.6403878927230835,0.4429481327533722,-0.627455472946167,-0.6654479503631592,0.4429481327533722,-0.6008127927780151,-0.6894004940986633,0.4429481327533722,-0.5731700658798218,-0.7122055292129517,0.4429481327533722,-0.5445734262466431,-0.7338252067565918,0.4429481327533722,-0.515070378780365,-0.7542235255241394,0.4429481327533722,-0.48471003770828247,-0.7733665108680725,0.4429481327533722,-0.45354294776916504,-0.7912222743034363,0.4429481327533722,-0.4216209948062897,-0.8077611327171326,0.4429481327533722,-0.38899725675582886,-0.8229556083679199,0.4429481327533722,-0.35572612285614014,-0.836780309677124,0.4429481327533722,-0.3218628764152527,-0.8492122292518616,0.4429481327533722,-0.2874639332294464,-0.8602308034896851,0.4429481327533722,-0.2525865435600281,-0.8698175549507141,0.4429481327533722,-0.21728874742984772,-0.8779565691947937,0.4429481327533722,-0.18162928521633148,-0.8846343755722046,0.4429481327533722,-0.14566753804683685,-0.8898397088050842,0.4429481327533722,-0.10946332663297653,-0.8935640454292297,0.4429481327533722,-0.07307692617177963,-0.8958011269569397,0.4429481327533722,-0.03656889498233795,-0.896547257900238,0.4429481327533722,-2.195907372419458e-16,-0.9255590438842773,0.3786032497882843,0.0,-0.9247888326644897,0.3786032497882843,0.037752244621515274,-0.9224793314933777,0.3786032497882843,0.07544165849685669,-0.9186344742774963,0.3786032497882843,0.11300550401210785,-0.9132606983184814,0.3786032497882843,0.15038126707077026,-0.9063668251037598,0.3786032497882843,0.18750672042369843,-0.8979644179344177,0.3786032497882843,0.22432009875774384,-0.8880674242973328,0.3786032497882843,0.2607601284980774,-0.8766923546791077,0.3786032497882843,0.29676613211631775,-0.8638581037521362,0.3786032497882843,0.33227819204330444,-0.849586009979248,0.3786032497882843,0.36723724007606506,-0.8338999152183533,0.3786032497882843,0.40158504247665405,-0.8168258666992188,0.3786032497882843,0.4352644383907318,-0.7983922362327576,0.3786032497882843,0.4682193696498871,-0.7786298394203186,0.3786032497882843,0.5003949999809265,-0.7575714588165283,0.3786032497882843,0.5317378044128418,-0.7352521419525146,0.3786032497882843,0.5621955394744873,-0.7117091417312622,0.3786032497882843,0.5917176008224487,-0.6869814991950989,0.3786032497882843,0.6202547550201416,-0.661110520362854,0.3786032497882843,0.6477596163749695,-0.6341391205787659,0.3786032497882843,0.6741862893104553,-0.6061123013496399,0.3786032497882843,0.6994908452033997,-0.5770766139030457,0.3786032497882843,0.7236312031745911,-0.5470805168151855,0.3786032497882843,0.7465671300888062,-0.516173779964447,0.3786032497882843,0.7682604789733887,-0.4844079911708832,0.3786032497882843,0.7886751294136047,-0.45183590054512024,0.3786032497882843,0.8077771067619324,-0.4185118079185486,0.3786032497882843,0.8255346417427063,-0.3844911456108093,0.3786032497882843,0.8419181108474731,-0.3498305082321167,0.3786032497882843,0.8569003343582153,-0.31458762288093567,0.3786032497882843,0.870456337928772,-0.2788211405277252,0.3786032497882843,0.8825635313987732,-0.24259059131145477,0.3786032497882843,0.8932017683982849,-0.20595626533031464,0.3786032497882843,0.9023533463478088,-0.16897915303707123,0.3786032497882843,0.9100030660629272,-0.13172079622745514,0.3786032497882843,0.916138231754303,-0.09424319118261337,0.3786032497882843,0.9207485318183899,-0.05660872906446457,0.3786032497882843,0.923826277256012,-0.018880050629377365,0.3786032497882843,0.9253664612770081,0.018880050629377365,0.3786032497882843,0.9253664612770081,0.05660872906446457,0.3786032497882843,0.923826277256012,0.09424319118261337,0.3786032497882843,0.9207485318183899,0.13172079622745514,0.3786032497882843,0.916138231754303,0.16897915303707123,0.3786032497882843,0.9100030660629272,0.20595626533031464,0.3786032497882843,0.9023533463478088,0.24259059131145477,0.3786032497882843,0.8932017683982849,0.2788211405277252,0.3786032497882843,0.8825635313987732,0.31458762288093567,0.3786032497882843,0.870456337928772,0.3498305082321167,0.3786032497882843,0.8569003343582153,0.3844911456108093,0.3786032497882843,0.8419181108474731,0.4185118079185486,0.3786032497882843,0.8255346417427063,0.45183590054512024,0.3786032497882843,0.8077771067619324,0.4844079911708832,0.3786032497882843,0.7886751294136047,0.516173779964447,0.3786032497882843,0.7682604789733887,0.5470805168151855,0.3786032497882843,0.7465671300888062,0.5770766139030457,0.3786032497882843,0.7236312031745911,0.6061123013496399,0.3786032497882843,0.6994908452033997,0.6341391205787659,0.3786032497882843,0.6741862893104553,0.661110520362854,0.3786032497882843,0.6477596163749695,0.6869814991950989,0.3786032497882843,0.6202547550201416,0.7117091417312622,0.3786032497882843,0.5917176008224487,0.7352521419525146,0.3786032497882843,0.5621955394744873,0.7575714588165283,0.3786032497882843,0.5317378044128418,0.7786298394203186,0.3786032497882843,0.5003949999809265,0.7983922362327576,0.3786032497882843,0.4682193696498871,0.8168258666992188,0.3786032497882843,0.4352644383907318,0.8338999152183533,0.3786032497882843,0.40158504247665405,0.849586009979248,0.3786032497882843,0.36723724007606506,0.8638581037521362,0.3786032497882843,0.33227819204330444,0.8766923546791077,0.3786032497882843,0.29676613211631775,0.8880674242973328,0.3786032497882843,0.2607601284980774,0.8979644179344177,0.3786032497882843,0.22432009875774384,0.9063668251037598,0.3786032497882843,0.18750672042369843,0.9132606983184814,0.3786032497882843,0.15038126707077026,0.9186344742774963,0.3786032497882843,0.11300550401210785,0.9224793314933777,0.3786032497882843,0.07544165849685669,0.9247888326644897,0.3786032497882843,0.037752244621515274,0.9255590438842773,0.3786032497882843,1.1334829464034537e-16,0.9247888326644897,0.3786032497882843,-0.037752244621515274,0.9224793314933777,0.3786032497882843,-0.07544165849685669,0.9186344742774963,0.3786032497882843,-0.11300550401210785,0.9132606983184814,0.3786032497882843,-0.15038126707077026,0.9063668251037598,0.3786032497882843,-0.18750672042369843,0.8979644179344177,0.3786032497882843,-0.22432009875774384,0.8880674242973328,0.3786032497882843,-0.2607601284980774,0.8766923546791077,0.3786032497882843,-0.29676613211631775,0.8638581037521362,0.3786032497882843,-0.33227819204330444,0.849586009979248,0.3786032497882843,-0.36723724007606506,0.8338999152183533,0.3786032497882843,-0.40158504247665405,0.8168258666992188,0.3786032497882843,-0.4352644383907318,0.7983922362327576,0.3786032497882843,-0.4682193696498871,0.7786298394203186,0.3786032497882843,-0.5003949999809265,0.7575714588165283,0.3786032497882843,-0.5317378044128418,0.7352521419525146,0.3786032497882843,-0.5621955394744873,0.7117091417312622,0.3786032497882843,-0.5917176008224487,0.6869814991950989,0.3786032497882843,-0.6202547550201416,0.661110520362854,0.3786032497882843,-0.6477596163749695,0.6341391205787659,0.3786032497882843,-0.6741862893104553,0.6061123013496399,0.3786032497882843,-0.6994908452033997,0.5770766139030457,0.3786032497882843,-0.7236312031745911,0.5470805168151855,0.3786032497882843,-0.7465671300888062,0.516173779964447,0.3786032497882843,-0.7682604789733887,0.4844079911708832,0.3786032497882843,-0.7886751294136047,0.45183590054512024,0.3786032497882843,-0.8077771067619324,0.4185118079185486,0.3786032497882843,-0.8255346417427063,0.3844911456108093,0.3786032497882843,-0.8419181108474731,0.3498305082321167,0.3786032497882843,-0.8569003343582153,0.31458762288093567,0.3786032497882843,-0.870456337928772,0.2788211405277252,0.3786032497882843,-0.8825635313987732,0.24259059131145477,0.3786032497882843,-0.8932017683982849,0.20595626533031464,0.3786032497882843,-0.9023533463478088,0.16897915303707123,0.3786032497882843,-0.9100030660629272,0.13172079622745514,0.3786032497882843,-0.916138231754303,0.09424319118261337,0.3786032497882843,-0.9207485318183899,0.05660872906446457,0.3786032497882843,-0.923826277256012,0.018880050629377365,0.3786032497882843,-0.9253664612770081,-0.018880050629377365,0.3786032497882843,-0.9253664612770081,-0.05660872906446457,0.3786032497882843,-0.923826277256012,-0.09424319118261337,0.3786032497882843,-0.9207485318183899,-0.13172079622745514,0.3786032497882843,-0.916138231754303,-0.16897915303707123,0.3786032497882843,-0.9100030660629272,-0.20595626533031464,0.3786032497882843,-0.9023533463478088,-0.24259059131145477,0.3786032497882843,-0.8932017683982849,-0.2788211405277252,0.3786032497882843,-0.8825635313987732,-0.31458762288093567,0.3786032497882843,-0.870456337928772,-0.3498305082321167,0.3786032497882843,-0.8569003343582153,-0.3844911456108093,0.3786032497882843,-0.8419181108474731,-0.4185118079185486,0.3786032497882843,-0.8255346417427063,-0.45183590054512024,0.3786032497882843,-0.8077771067619324,-0.4844079911708832,0.3786032497882843,-0.7886751294136047,-0.516173779964447,0.3786032497882843,-0.7682604789733887,-0.5470805168151855,0.3786032497882843,-0.7465671300888062,-0.5770766139030457,0.3786032497882843,-0.7236312031745911,-0.6061123013496399,0.3786032497882843,-0.6994908452033997,-0.6341391205787659,0.3786032497882843,-0.6741862893104553,-0.661110520362854,0.3786032497882843,-0.6477596163749695,-0.6869814991950989,0.3786032497882843,-0.6202547550201416,-0.7117091417312622,0.3786032497882843,-0.5917176008224487,-0.7352521419525146,0.3786032497882843,-0.5621955394744873,-0.7575714588165283,0.3786032497882843,-0.5317378044128418,-0.7786298394203186,0.3786032497882843,-0.5003949999809265,-0.7983922362327576,0.3786032497882843,-0.4682193696498871,-0.8168258666992188,0.3786032497882843,-0.4352644383907318,-0.8338999152183533,0.3786032497882843,-0.40158504247665405,-0.849586009979248,0.3786032497882843,-0.36723724007606506,-0.8638581037521362,0.3786032497882843,-0.33227819204330444,-0.8766923546791077,0.3786032497882843,-0.29676613211631775,-0.8880674242973328,0.3786032497882843,-0.2607601284980774,-0.8979644179344177,0.3786032497882843,-0.22432009875774384,-0.9063668251037598,0.3786032497882843,-0.18750672042369843,-0.9132606983184814,0.3786032497882843,-0.15038126707077026,-0.9186344742774963,0.3786032497882843,-0.11300550401210785,-0.9224793314933777,0.3786032497882843,-0.07544165849685669,-0.9247888326644897,0.3786032497882843,-0.037752244621515274,-0.9255590438842773,0.3786032497882843,-2.2669658928069073e-16,-0.9499598145484924,0.3123721778392792,0.0,-0.9491692185401917,0.3123721778392792,0.03874751552939415,-0.9467988610267639,0.3123721778392792,0.07743054628372192,-0.9428526759147644,0.3123721778392792,0.11598469316959381,-0.9373371601104736,0.3123721778392792,0.1543457955121994,-0.9302616119384766,0.3123721778392792,0.19245001673698425,-0.9216376543045044,0.3123721778392792,0.23023390769958496,-0.9114797711372375,0.3123721778392792,0.2676346004009247,-0.8998047709465027,0.3123721778392792,0.3045898377895355,-0.8866322040557861,0.3123721778392792,0.34103813767433167,-0.8719838857650757,0.3123721778392792,0.3769187927246094,-0.8558841943740845,0.3123721778392792,0.4121721088886261,-0.8383600115776062,0.3123721778392792,0.44673940539360046,-0.819440484046936,0.3123721778392792,0.4805631637573242,-0.7991570234298706,0.3123721778392792,0.5135870575904846,-0.7775434851646423,0.3123721778392792,0.5457561016082764,-0.7546358108520508,0.3123721778392792,0.5770168304443359,-0.7304720878601074,0.3123721778392792,0.6073171496391296,-0.7050926089286804,0.3123721778392792,0.6366066932678223,-0.678539514541626,0.3123721778392792,0.6648366451263428,-0.6508570909500122,0.3123721778392792,0.6919600367546082,-0.6220913529396057,0.3123721778392792,0.7179316878318787,-0.5922902226448059,0.3123721778392792,0.7427085041999817,-0.5615032911300659,0.3123721778392792,0.7662490606307983,-0.5297818183898926,0.3123721778392792,0.7885143160820007,-0.4971785545349121,0.3123721778392792,0.8094671964645386,-0.4637477695941925,0.3123721778392792,0.8290727734565735,-0.429545134305954,0.3123721778392792,0.8472983837127686,-0.39462757110595703,0.3123721778392792,0.8641138076782227,-0.3590531647205353,0.3123721778392792,0.8794910311698914,-0.32288116216659546,0.3123721778392792,0.8934043645858765,-0.2861717641353607,0.3123721778392792,0.9058307409286499,-0.24898606538772583,0.3123721778392792,0.9167494773864746,-0.21138593554496765,0.3123721778392792,0.9261423349380493,-0.17343398928642273,0.3123721778392792,0.9339936971664429,-0.1351933777332306,0.3123721778392792,0.9402905702590942,-0.09672774374485016,0.3123721778392792,0.9450224041938782,-0.058101117610931396,0.3123721778392792,0.9481813311576843,-0.01937779039144516,0.3123721778392792,0.9497621655464172,0.01937779039144516,0.3123721778392792,0.9497621655464172,0.058101117610931396,0.3123721778392792,0.9481813311576843,0.09672774374485016,0.3123721778392792,0.9450224041938782,0.1351933777332306,0.3123721778392792,0.9402905702590942,0.17343398928642273,0.3123721778392792,0.9339936971664429,0.21138593554496765,0.3123721778392792,0.9261423349380493,0.24898606538772583,0.3123721778392792,0.9167494773864746,0.2861717641353607,0.3123721778392792,0.9058307409286499,0.32288116216659546,0.3123721778392792,0.8934043645858765,0.3590531647205353,0.3123721778392792,0.8794910311698914,0.39462757110595703,0.3123721778392792,0.8641138076782227,0.429545134305954,0.3123721778392792,0.8472983837127686,0.4637477695941925,0.3123721778392792,0.8290727734565735,0.4971785545349121,0.3123721778392792,0.8094671964645386,0.5297818183898926,0.3123721778392792,0.7885143160820007,0.5615032911300659,0.3123721778392792,0.7662490606307983,0.5922902226448059,0.3123721778392792,0.7427085041999817,0.6220913529396057,0.3123721778392792,0.7179316878318787,0.6508570909500122,0.3123721778392792,0.6919600367546082,0.678539514541626,0.3123721778392792,0.6648366451263428,0.7050926089286804,0.3123721778392792,0.6366066932678223,0.7304720878601074,0.3123721778392792,0.6073171496391296,0.7546358108520508,0.3123721778392792,0.5770168304443359,0.7775434851646423,0.3123721778392792,0.5457561016082764,0.7991570234298706,0.3123721778392792,0.5135870575904846,0.819440484046936,0.3123721778392792,0.4805631637573242,0.8383600115776062,0.3123721778392792,0.44673940539360046,0.8558841943740845,0.3123721778392792,0.4121721088886261,0.8719838857650757,0.3123721778392792,0.3769187927246094,0.8866322040557861,0.3123721778392792,0.34103813767433167,0.8998047709465027,0.3123721778392792,0.3045898377895355,0.9114797711372375,0.3123721778392792,0.2676346004009247,0.9216376543045044,0.3123721778392792,0.23023390769958496,0.9302616119384766,0.3123721778392792,0.19245001673698425,0.9373371601104736,0.3123721778392792,0.1543457955121994,0.9428526759147644,0.3123721778392792,0.11598469316959381,0.9467988610267639,0.3123721778392792,0.07743054628372192,0.9491692185401917,0.3123721778392792,0.03874751552939415,0.9499598145484924,0.3123721778392792,1.1633652099914013e-16,0.9491692185401917,0.3123721778392792,-0.03874751552939415,0.9467988610267639,0.3123721778392792,-0.07743054628372192,0.9428526759147644,0.3123721778392792,-0.11598469316959381,0.9373371601104736,0.3123721778392792,-0.1543457955121994,0.9302616119384766,0.3123721778392792,-0.19245001673698425,0.9216376543045044,0.3123721778392792,-0.23023390769958496,0.9114797711372375,0.3123721778392792,-0.2676346004009247,0.8998047709465027,0.3123721778392792,-0.3045898377895355,0.8866322040557861,0.3123721778392792,-0.34103813767433167,0.8719838857650757,0.3123721778392792,-0.3769187927246094,0.8558841943740845,0.3123721778392792,-0.4121721088886261,0.8383600115776062,0.3123721778392792,-0.44673940539360046,0.819440484046936,0.3123721778392792,-0.4805631637573242,0.7991570234298706,0.3123721778392792,-0.5135870575904846,0.7775434851646423,0.3123721778392792,-0.5457561016082764,0.7546358108520508,0.3123721778392792,-0.5770168304443359,0.7304720878601074,0.3123721778392792,-0.6073171496391296,0.7050926089286804,0.3123721778392792,-0.6366066932678223,0.678539514541626,0.3123721778392792,-0.6648366451263428,0.6508570909500122,0.3123721778392792,-0.6919600367546082,0.6220913529396057,0.3123721778392792,-0.7179316878318787,0.5922902226448059,0.3123721778392792,-0.7427085041999817,0.5615032911300659,0.3123721778392792,-0.7662490606307983,0.5297818183898926,0.3123721778392792,-0.7885143160820007,0.4971785545349121,0.3123721778392792,-0.8094671964645386,0.4637477695941925,0.3123721778392792,-0.8290727734565735,0.429545134305954,0.3123721778392792,-0.8472983837127686,0.39462757110595703,0.3123721778392792,-0.8641138076782227,0.3590531647205353,0.3123721778392792,-0.8794910311698914,0.32288116216659546,0.3123721778392792,-0.8934043645858765,0.2861717641353607,0.3123721778392792,-0.9058307409286499,0.24898606538772583,0.3123721778392792,-0.9167494773864746,0.21138593554496765,0.3123721778392792,-0.9261423349380493,0.17343398928642273,0.3123721778392792,-0.9339936971664429,0.1351933777332306,0.3123721778392792,-0.9402905702590942,0.09672774374485016,0.3123721778392792,-0.9450224041938782,0.058101117610931396,0.3123721778392792,-0.9481813311576843,0.01937779039144516,0.3123721778392792,-0.9497621655464172,-0.01937779039144516,0.3123721778392792,-0.9497621655464172,-0.058101117610931396,0.3123721778392792,-0.9481813311576843,-0.09672774374485016,0.3123721778392792,-0.9450224041938782,-0.1351933777332306,0.3123721778392792,-0.9402905702590942,-0.17343398928642273,0.3123721778392792,-0.9339936971664429,-0.21138593554496765,0.3123721778392792,-0.9261423349380493,-0.24898606538772583,0.3123721778392792,-0.9167494773864746,-0.2861717641353607,0.3123721778392792,-0.9058307409286499,-0.32288116216659546,0.3123721778392792,-0.8934043645858765,-0.3590531647205353,0.3123721778392792,-0.8794910311698914,-0.39462757110595703,0.3123721778392792,-0.8641138076782227,-0.429545134305954,0.3123721778392792,-0.8472983837127686,-0.4637477695941925,0.3123721778392792,-0.8290727734565735,-0.4971785545349121,0.3123721778392792,-0.8094671964645386,-0.5297818183898926,0.3123721778392792,-0.7885143160820007,-0.5615032911300659,0.3123721778392792,-0.7662490606307983,-0.5922902226448059,0.3123721778392792,-0.7427085041999817,-0.6220913529396057,0.3123721778392792,-0.7179316878318787,-0.6508570909500122,0.3123721778392792,-0.6919600367546082,-0.678539514541626,0.3123721778392792,-0.6648366451263428,-0.7050926089286804,0.3123721778392792,-0.6366066932678223,-0.7304720878601074,0.3123721778392792,-0.6073171496391296,-0.7546358108520508,0.3123721778392792,-0.5770168304443359,-0.7775434851646423,0.3123721778392792,-0.5457561016082764,-0.7991570234298706,0.3123721778392792,-0.5135870575904846,-0.819440484046936,0.3123721778392792,-0.4805631637573242,-0.8383600115776062,0.3123721778392792,-0.44673940539360046,-0.8558841943740845,0.3123721778392792,-0.4121721088886261,-0.8719838857650757,0.3123721778392792,-0.3769187927246094,-0.8866322040557861,0.3123721778392792,-0.34103813767433167,-0.8998047709465027,0.3123721778392792,-0.3045898377895355,-0.9114797711372375,0.3123721778392792,-0.2676346004009247,-0.9216376543045044,0.3123721778392792,-0.23023390769958496,-0.9302616119384766,0.3123721778392792,-0.19245001673698425,-0.9373371601104736,0.3123721778392792,-0.1543457955121994,-0.9428526759147644,0.3123721778392792,-0.11598469316959381,-0.9467988610267639,0.3123721778392792,-0.07743054628372192,-0.9491692185401917,0.3123721778392792,-0.03874751552939415,-0.9499598145484924,0.3123721778392792,-2.3267304199828026e-16,-0.969627857208252,0.24458488821983337,0.0,-0.9688209295272827,0.24458488821983337,0.039549749344587326,-0.9664015173912048,0.24458488821983337,0.07903367280960083,-0.9623736143112183,0.24458488821983337,0.11838605254888535,-0.9567439556121826,0.24458488821983337,0.1575413942337036,-0.9495218396186829,0.24458488821983337,0.19643451273441315,-0.9407193660736084,0.24458488821983337,0.23500069975852966,-0.9303511381149292,0.24458488821983337,0.27317574620246887,-0.9184344410896301,0.24458488821983337,0.310896098613739,-0.9049891233444214,0.24458488821983337,0.348099023103714,-0.8900375366210938,0.24458488821983337,0.3847225606441498,-0.8736045360565186,0.24458488821983337,0.42070576548576355,-0.8557175397872925,0.24458488821983337,0.4559887647628784,-0.8364062905311584,0.24458488821983337,0.49051278829574585,-0.8157028555870056,0.24458488821983337,0.5242204070091248,-0.7936418652534485,0.24458488821983337,0.5570555329322815,-0.7702598571777344,0.24458488821983337,0.5889634490013123,-0.7455958724021912,0.24458488821983337,0.6198911666870117,-0.7196909189224243,0.24458488821983337,0.6497870683670044,-0.6925880908966064,0.24458488821983337,0.6786015033721924,-0.6643325090408325,0.24458488821983337,0.7062864303588867,-0.6349712014198303,0.24458488821983337,0.7327958345413208,-0.6045531034469604,0.24458488821983337,0.7580856084823608,-0.5731287598609924,0.24458488821983337,0.7821136116981506,-0.5407505035400391,0.24458488821983337,0.8048398494720459,-0.5074722170829773,0.24458488821983337,0.8262264728546143,-0.47334927320480347,0.24458488821983337,0.8462379574775696,-0.4384384751319885,0.24458488821983337,0.8648409843444824,-0.40279796719551086,0.24458488821983337,0.8820045590400696,-0.366487056016922,0.24458488821983337,0.8977001309394836,-0.3295661509037018,0.24458488821983337,0.9119015336036682,-0.29209670424461365,0.24458488821983337,0.9245851635932922,-0.25414109230041504,0.24458488821983337,0.9357299208641052,-0.21576249599456787,0.24458488821983337,0.945317268371582,-0.17702478170394897,0.24458488821983337,0.9533312320709229,-0.13799244165420532,0.24458488821983337,0.959758460521698,-0.0987304076552391,0.24458488821983337,0.9645882844924927,-0.05930405110120773,0.24458488821983337,0.9678125977516174,-0.019778991118073463,0.24458488821983337,0.969426155090332,0.019778991118073463,0.24458488821983337,0.969426155090332,0.05930405110120773,0.24458488821983337,0.9678125977516174,0.0987304076552391,0.24458488821983337,0.9645882844924927,0.13799244165420532,0.24458488821983337,0.959758460521698,0.17702478170394897,0.24458488821983337,0.9533312320709229,0.21576249599456787,0.24458488821983337,0.945317268371582,0.25414109230041504,0.24458488821983337,0.9357299208641052,0.29209670424461365,0.24458488821983337,0.9245851635932922,0.3295661509037018,0.24458488821983337,0.9119015336036682,0.366487056016922,0.24458488821983337,0.8977001309394836,0.40279796719551086,0.24458488821983337,0.8820045590400696,0.4384384751319885,0.24458488821983337,0.8648409843444824,0.47334927320480347,0.24458488821983337,0.8462379574775696,0.5074722170829773,0.24458488821983337,0.8262264728546143,0.5407505035400391,0.24458488821983337,0.8048398494720459,0.5731287598609924,0.24458488821983337,0.7821136116981506,0.6045531034469604,0.24458488821983337,0.7580856084823608,0.6349712014198303,0.24458488821983337,0.7327958345413208,0.6643325090408325,0.24458488821983337,0.7062864303588867,0.6925880908966064,0.24458488821983337,0.6786015033721924,0.7196909189224243,0.24458488821983337,0.6497870683670044,0.7455958724021912,0.24458488821983337,0.6198911666870117,0.7702598571777344,0.24458488821983337,0.5889634490013123,0.7936418652534485,0.24458488821983337,0.5570555329322815,0.8157028555870056,0.24458488821983337,0.5242204070091248,0.8364062905311584,0.24458488821983337,0.49051278829574585,0.8557175397872925,0.24458488821983337,0.4559887647628784,0.8736045360565186,0.24458488821983337,0.42070576548576355,0.8900375366210938,0.24458488821983337,0.3847225606441498,0.9049891233444214,0.24458488821983337,0.348099023103714,0.9184344410896301,0.24458488821983337,0.310896098613739,0.9303511381149292,0.24458488821983337,0.27317574620246887,0.9407193660736084,0.24458488821983337,0.23500069975852966,0.9495218396186829,0.24458488821983337,0.19643451273441315,0.9567439556121826,0.24458488821983337,0.1575413942337036,0.9623736143112183,0.24458488821983337,0.11838605254888535,0.9664015173912048,0.24458488821983337,0.07903367280960083,0.9688209295272827,0.24458488821983337,0.039549749344587326,0.969627857208252,0.24458488821983337,1.1874516506377614e-16,0.9688209295272827,0.24458488821983337,-0.039549749344587326,0.9664015173912048,0.24458488821983337,-0.07903367280960083,0.9623736143112183,0.24458488821983337,-0.11838605254888535,0.9567439556121826,0.24458488821983337,-0.1575413942337036,0.9495218396186829,0.24458488821983337,-0.19643451273441315,0.9407193660736084,0.24458488821983337,-0.23500069975852966,0.9303511381149292,0.24458488821983337,-0.27317574620246887,0.9184344410896301,0.24458488821983337,-0.310896098613739,0.9049891233444214,0.24458488821983337,-0.348099023103714,0.8900375366210938,0.24458488821983337,-0.3847225606441498,0.8736045360565186,0.24458488821983337,-0.42070576548576355,0.8557175397872925,0.24458488821983337,-0.4559887647628784,0.8364062905311584,0.24458488821983337,-0.49051278829574585,0.8157028555870056,0.24458488821983337,-0.5242204070091248,0.7936418652534485,0.24458488821983337,-0.5570555329322815,0.7702598571777344,0.24458488821983337,-0.5889634490013123,0.7455958724021912,0.24458488821983337,-0.6198911666870117,0.7196909189224243,0.24458488821983337,-0.6497870683670044,0.6925880908966064,0.24458488821983337,-0.6786015033721924,0.6643325090408325,0.24458488821983337,-0.7062864303588867,0.6349712014198303,0.24458488821983337,-0.7327958345413208,0.6045531034469604,0.24458488821983337,-0.7580856084823608,0.5731287598609924,0.24458488821983337,-0.7821136116981506,0.5407505035400391,0.24458488821983337,-0.8048398494720459,0.5074722170829773,0.24458488821983337,-0.8262264728546143,0.47334927320480347,0.24458488821983337,-0.8462379574775696,0.4384384751319885,0.24458488821983337,-0.8648409843444824,0.40279796719551086,0.24458488821983337,-0.8820045590400696,0.366487056016922,0.24458488821983337,-0.8977001309394836,0.3295661509037018,0.24458488821983337,-0.9119015336036682,0.29209670424461365,0.24458488821983337,-0.9245851635932922,0.25414109230041504,0.24458488821983337,-0.9357299208641052,0.21576249599456787,0.24458488821983337,-0.945317268371582,0.17702478170394897,0.24458488821983337,-0.9533312320709229,0.13799244165420532,0.24458488821983337,-0.959758460521698,0.0987304076552391,0.24458488821983337,-0.9645882844924927,0.05930405110120773,0.24458488821983337,-0.9678125977516174,0.019778991118073463,0.24458488821983337,-0.969426155090332,-0.019778991118073463,0.24458488821983337,-0.969426155090332,-0.05930405110120773,0.24458488821983337,-0.9678125977516174,-0.0987304076552391,0.24458488821983337,-0.9645882844924927,-0.13799244165420532,0.24458488821983337,-0.959758460521698,-0.17702478170394897,0.24458488821983337,-0.9533312320709229,-0.21576249599456787,0.24458488821983337,-0.945317268371582,-0.25414109230041504,0.24458488821983337,-0.9357299208641052,-0.29209670424461365,0.24458488821983337,-0.9245851635932922,-0.3295661509037018,0.24458488821983337,-0.9119015336036682,-0.366487056016922,0.24458488821983337,-0.8977001309394836,-0.40279796719551086,0.24458488821983337,-0.8820045590400696,-0.4384384751319885,0.24458488821983337,-0.8648409843444824,-0.47334927320480347,0.24458488821983337,-0.8462379574775696,-0.5074722170829773,0.24458488821983337,-0.8262264728546143,-0.5407505035400391,0.24458488821983337,-0.8048398494720459,-0.5731287598609924,0.24458488821983337,-0.7821136116981506,-0.6045531034469604,0.24458488821983337,-0.7580856084823608,-0.6349712014198303,0.24458488821983337,-0.7327958345413208,-0.6643325090408325,0.24458488821983337,-0.7062864303588867,-0.6925880908966064,0.24458488821983337,-0.6786015033721924,-0.7196909189224243,0.24458488821983337,-0.6497870683670044,-0.7455958724021912,0.24458488821983337,-0.6198911666870117,-0.7702598571777344,0.24458488821983337,-0.5889634490013123,-0.7936418652534485,0.24458488821983337,-0.5570555329322815,-0.8157028555870056,0.24458488821983337,-0.5242204070091248,-0.8364062905311584,0.24458488821983337,-0.49051278829574585,-0.8557175397872925,0.24458488821983337,-0.4559887647628784,-0.8736045360565186,0.24458488821983337,-0.42070576548576355,-0.8900375366210938,0.24458488821983337,-0.3847225606441498,-0.9049891233444214,0.24458488821983337,-0.348099023103714,-0.9184344410896301,0.24458488821983337,-0.310896098613739,-0.9303511381149292,0.24458488821983337,-0.27317574620246887,-0.9407193660736084,0.24458488821983337,-0.23500069975852966,-0.9495218396186829,0.24458488821983337,-0.19643451273441315,-0.9567439556121826,0.24458488821983337,-0.1575413942337036,-0.9623736143112183,0.24458488821983337,-0.11838605254888535,-0.9664015173912048,0.24458488821983337,-0.07903367280960083,-0.9688209295272827,0.24458488821983337,-0.039549749344587326,-0.969627857208252,0.24458488821983337,-2.374903301275523e-16,-0.9844653010368347,0.17557910084724426,0.0,-0.983646035194397,0.17557910084724426,0.04015494883060455,-0.9811896085739136,0.17557910084724426,0.0802430659532547,-0.9771000742912292,0.17557910084724426,0.12019762396812439,-0.9713842272758484,0.17557910084724426,0.15995211899280548,-0.9640516042709351,0.17557910084724426,0.19944040477275848,-0.9551144242286682,0.17557910084724426,0.23859672248363495,-0.9445875883102417,0.17557910084724426,0.27735593914985657,-0.9324885010719299,0.17557910084724426,0.3156535029411316,-0.9188374280929565,0.17557910084724426,0.3534257113933563,-0.903657078742981,0.17557910084724426,0.3906096816062927,-0.8869726061820984,0.17557910084724426,0.42714348435401917,-0.8688119053840637,0.17557910084724426,0.4629663825035095,-0.8492051362991333,0.17557910084724426,0.49801871180534363,-0.828184962272644,0.17557910084724426,0.5322421193122864,-0.8057863116264343,0.17557910084724426,0.5655797123908997,-0.7820465564727783,0.17557910084724426,0.5979759097099304,-0.7570051550865173,0.17557910084724426,0.6293768286705017,-0.7307037711143494,0.17557910084724426,0.6597302556037903,-0.7031862139701843,0.17557910084724426,0.6889855861663818,-0.6744982600212097,0.17557910084724426,0.7170941829681396,-0.6446877121925354,0.17557910084724426,0.744009256362915,-0.6138041019439697,0.17557910084724426,0.7696859836578369,-0.5818988680839539,0.17557910084724426,0.7940816283226013,-0.5490251779556274,0.17557910084724426,0.817155659198761,-0.5152376294136047,0.17557910084724426,0.838869571685791,-0.4805925488471985,0.17557910084724426,0.8591873049736023,-0.4451475739479065,0.17557910084724426,0.8780749440193176,-0.408961683511734,0.17557910084724426,0.8955011367797852,-0.37209510803222656,0.17557910084724426,0.911436915397644,-0.3346092402935028,0.17557910084724426,0.9258556365966797,-0.2965664267539978,0.17557910084724426,0.9387333393096924,-0.2580300271511078,0.17557910084724426,0.9500486850738525,-0.2190641462802887,0.17557910084724426,0.9597827196121216,-0.17973366379737854,0.17557910084724426,0.9679192900657654,-0.1401040256023407,0.17557910084724426,0.9744448661804199,-0.10024119913578033,0.17557910084724426,0.9793485999107361,-0.060211535543203354,0.17557910084724426,0.9826222658157349,-0.020081652328372,0.17557910084724426,0.9842604994773865,0.020081652328372,0.17557910084724426,0.9842604994773865,0.060211535543203354,0.17557910084724426,0.9826222658157349,0.10024119913578033,0.17557910084724426,0.9793485999107361,0.1401040256023407,0.17557910084724426,0.9744448661804199,0.17973366379737854,0.17557910084724426,0.9679192900657654,0.2190641462802887,0.17557910084724426,0.9597827196121216,0.2580300271511078,0.17557910084724426,0.9500486850738525,0.2965664267539978,0.17557910084724426,0.9387333393096924,0.3346092402935028,0.17557910084724426,0.9258556365966797,0.37209510803222656,0.17557910084724426,0.911436915397644,0.408961683511734,0.17557910084724426,0.8955011367797852,0.4451475739479065,0.17557910084724426,0.8780749440193176,0.4805925488471985,0.17557910084724426,0.8591873049736023,0.5152376294136047,0.17557910084724426,0.838869571685791,0.5490251779556274,0.17557910084724426,0.817155659198761,0.5818988680839539,0.17557910084724426,0.7940816283226013,0.6138041019439697,0.17557910084724426,0.7696859836578369,0.6446877121925354,0.17557910084724426,0.744009256362915,0.6744982600212097,0.17557910084724426,0.7170941829681396,0.7031862139701843,0.17557910084724426,0.6889855861663818,0.7307037711143494,0.17557910084724426,0.6597302556037903,0.7570051550865173,0.17557910084724426,0.6293768286705017,0.7820465564727783,0.17557910084724426,0.5979759097099304,0.8057863116264343,0.17557910084724426,0.5655797123908997,0.828184962272644,0.17557910084724426,0.5322421193122864,0.8492051362991333,0.17557910084724426,0.49801871180534363,0.8688119053840637,0.17557910084724426,0.4629663825035095,0.8869726061820984,0.17557910084724426,0.42714348435401917,0.903657078742981,0.17557910084724426,0.3906096816062927,0.9188374280929565,0.17557910084724426,0.3534257113933563,0.9324885010719299,0.17557910084724426,0.3156535029411316,0.9445875883102417,0.17557910084724426,0.27735593914985657,0.9551144242286682,0.17557910084724426,0.23859672248363495,0.9640516042709351,0.17557910084724426,0.19944040477275848,0.9713842272758484,0.17557910084724426,0.15995211899280548,0.9771000742912292,0.17557910084724426,0.12019762396812439,0.9811896085739136,0.17557910084724426,0.0802430659532547,0.983646035194397,0.17557910084724426,0.04015494883060455,0.9844653010368347,0.17557910084724426,1.2056223602409383e-16,0.983646035194397,0.17557910084724426,-0.04015494883060455,0.9811896085739136,0.17557910084724426,-0.0802430659532547,0.9771000742912292,0.17557910084724426,-0.12019762396812439,0.9713842272758484,0.17557910084724426,-0.15995211899280548,0.9640516042709351,0.17557910084724426,-0.19944040477275848,0.9551144242286682,0.17557910084724426,-0.23859672248363495,0.9445875883102417,0.17557910084724426,-0.27735593914985657,0.9324885010719299,0.17557910084724426,-0.3156535029411316,0.9188374280929565,0.17557910084724426,-0.3534257113933563,0.903657078742981,0.17557910084724426,-0.3906096816062927,0.8869726061820984,0.17557910084724426,-0.42714348435401917,0.8688119053840637,0.17557910084724426,-0.4629663825035095,0.8492051362991333,0.17557910084724426,-0.49801871180534363,0.828184962272644,0.17557910084724426,-0.5322421193122864,0.8057863116264343,0.17557910084724426,-0.5655797123908997,0.7820465564727783,0.17557910084724426,-0.5979759097099304,0.7570051550865173,0.17557910084724426,-0.6293768286705017,0.7307037711143494,0.17557910084724426,-0.6597302556037903,0.7031862139701843,0.17557910084724426,-0.6889855861663818,0.6744982600212097,0.17557910084724426,-0.7170941829681396,0.6446877121925354,0.17557910084724426,-0.744009256362915,0.6138041019439697,0.17557910084724426,-0.7696859836578369,0.5818988680839539,0.17557910084724426,-0.7940816283226013,0.5490251779556274,0.17557910084724426,-0.817155659198761,0.5152376294136047,0.17557910084724426,-0.838869571685791,0.4805925488471985,0.17557910084724426,-0.8591873049736023,0.4451475739479065,0.17557910084724426,-0.8780749440193176,0.408961683511734,0.17557910084724426,-0.8955011367797852,0.37209510803222656,0.17557910084724426,-0.911436915397644,0.3346092402935028,0.17557910084724426,-0.9258556365966797,0.2965664267539978,0.17557910084724426,-0.9387333393096924,0.2580300271511078,0.17557910084724426,-0.9500486850738525,0.2190641462802887,0.17557910084724426,-0.9597827196121216,0.17973366379737854,0.17557910084724426,-0.9679192900657654,0.1401040256023407,0.17557910084724426,-0.9744448661804199,0.10024119913578033,0.17557910084724426,-0.9793485999107361,0.060211535543203354,0.17557910084724426,-0.9826222658157349,0.020081652328372,0.17557910084724426,-0.9842604994773865,-0.020081652328372,0.17557910084724426,-0.9842604994773865,-0.060211535543203354,0.17557910084724426,-0.9826222658157349,-0.10024119913578033,0.17557910084724426,-0.9793485999107361,-0.1401040256023407,0.17557910084724426,-0.9744448661804199,-0.17973366379737854,0.17557910084724426,-0.9679192900657654,-0.2190641462802887,0.17557910084724426,-0.9597827196121216,-0.2580300271511078,0.17557910084724426,-0.9500486850738525,-0.2965664267539978,0.17557910084724426,-0.9387333393096924,-0.3346092402935028,0.17557910084724426,-0.9258556365966797,-0.37209510803222656,0.17557910084724426,-0.911436915397644,-0.408961683511734,0.17557910084724426,-0.8955011367797852,-0.4451475739479065,0.17557910084724426,-0.8780749440193176,-0.4805925488471985,0.17557910084724426,-0.8591873049736023,-0.5152376294136047,0.17557910084724426,-0.838869571685791,-0.5490251779556274,0.17557910084724426,-0.817155659198761,-0.5818988680839539,0.17557910084724426,-0.7940816283226013,-0.6138041019439697,0.17557910084724426,-0.7696859836578369,-0.6446877121925354,0.17557910084724426,-0.744009256362915,-0.6744982600212097,0.17557910084724426,-0.7170941829681396,-0.7031862139701843,0.17557910084724426,-0.6889855861663818,-0.7307037711143494,0.17557910084724426,-0.6597302556037903,-0.7570051550865173,0.17557910084724426,-0.6293768286705017,-0.7820465564727783,0.17557910084724426,-0.5979759097099304,-0.8057863116264343,0.17557910084724426,-0.5655797123908997,-0.828184962272644,0.17557910084724426,-0.5322421193122864,-0.8492051362991333,0.17557910084724426,-0.49801871180534363,-0.8688119053840637,0.17557910084724426,-0.4629663825035095,-0.8869726061820984,0.17557910084724426,-0.42714348435401917,-0.903657078742981,0.17557910084724426,-0.3906096816062927,-0.9188374280929565,0.17557910084724426,-0.3534257113933563,-0.9324885010719299,0.17557910084724426,-0.3156535029411316,-0.9445875883102417,0.17557910084724426,-0.27735593914985657,-0.9551144242286682,0.17557910084724426,-0.23859672248363495,-0.9640516042709351,0.17557910084724426,-0.19944040477275848,-0.9713842272758484,0.17557910084724426,-0.15995211899280548,-0.9771000742912292,0.17557910084724426,-0.12019762396812439,-0.9811896085739136,0.17557910084724426,-0.0802430659532547,-0.983646035194397,0.17557910084724426,-0.04015494883060455,-0.9844653010368347,0.17557910084724426,-2.4112447204818765e-16,-0.9943982362747192,0.10569857060909271,0.0,-0.9935706853866577,0.10569857060909271,0.04056009650230408,-0.9910894632339478,0.10569857060909271,0.08105268329381943,-0.9869586229324341,0.10569857060909271,0.12141036987304688,-0.9811851382255554,0.10569857060909271,0.16156597435474396,-0.9737785458564758,0.10569857060909271,0.20145267248153687,-0.9647511839866638,0.10569857060909271,0.241004079580307,-0.9541180729866028,0.10569857060909271,0.28015434741973877,-0.9418969750404358,0.10569857060909271,0.31883832812309265,-0.9281081557273865,0.10569857060909271,0.35699164867401123,-0.9127746224403381,0.10569857060909271,0.39455077052116394,-0.8959218263626099,0.10569857060909271,0.43145322799682617,-0.8775779008865356,0.10569857060909271,0.4676375389099121,-0.8577733039855957,0.10569857060909271,0.503043532371521,-0.8365409970283508,0.10569857060909271,0.53761225938797,-0.8139163851737976,0.10569857060909271,0.5712862014770508,-0.7899370789527893,0.10569857060909271,0.6040092706680298,-0.7646430134773254,0.10569857060909271,0.635727047920227,-0.7380762696266174,0.10569857060909271,0.6663866639137268,-0.7102810740470886,0.10569857060909271,0.6959372162818909,-0.6813036799430847,0.10569857060909271,0.72432941198349,-0.6511923670768738,0.10569857060909271,0.7515160441398621,-0.6199971437454224,0.10569857060909271,0.7774518132209778,-0.5877700448036194,0.10569857060909271,0.8020936250686646,-0.5545645952224731,0.10569857060909271,0.8254004716873169,-0.52043616771698,0.10569857060909271,0.8473334312438965,-0.48544153571128845,0.10569857060909271,0.8678561449050903,-0.4496389329433441,0.10569857060909271,0.8869343996047974,-0.4130879342556,0.10569857060909271,0.9045364260673523,-0.3758494257926941,0.10569857060909271,0.9206329584121704,-0.3379853069782257,0.10569857060909271,0.9351971745491028,-0.2995586693286896,0.10569857060909271,0.9482048153877258,-0.2606334388256073,0.10569857060909271,0.9596343040466309,-0.22127442061901093,0.10569857060909271,0.9694665670394897,-0.1815471053123474,0.10569857060909271,0.977685272693634,-0.14151762425899506,0.10569857060909271,0.9842766523361206,-0.10125259310007095,0.10569857060909271,0.9892298579216003,-0.06081904470920563,0.10569857060909271,0.9925366044044495,-0.020284269005060196,0.10569857060909271,0.9941912889480591,0.020284269005060196,0.10569857060909271,0.9941912889480591,0.06081904470920563,0.10569857060909271,0.9925366044044495,0.10125259310007095,0.10569857060909271,0.9892298579216003,0.14151762425899506,0.10569857060909271,0.9842766523361206,0.1815471053123474,0.10569857060909271,0.977685272693634,0.22127442061901093,0.10569857060909271,0.9694665670394897,0.2606334388256073,0.10569857060909271,0.9596343040466309,0.2995586693286896,0.10569857060909271,0.9482048153877258,0.3379853069782257,0.10569857060909271,0.9351971745491028,0.3758494257926941,0.10569857060909271,0.9206329584121704,0.4130879342556,0.10569857060909271,0.9045364260673523,0.4496389329433441,0.10569857060909271,0.8869343996047974,0.48544153571128845,0.10569857060909271,0.8678561449050903,0.52043616771698,0.10569857060909271,0.8473334312438965,0.5545645952224731,0.10569857060909271,0.8254004716873169,0.5877700448036194,0.10569857060909271,0.8020936250686646,0.6199971437454224,0.10569857060909271,0.7774518132209778,0.6511923670768738,0.10569857060909271,0.7515160441398621,0.6813036799430847,0.10569857060909271,0.72432941198349,0.7102810740470886,0.10569857060909271,0.6959372162818909,0.7380762696266174,0.10569857060909271,0.6663866639137268,0.7646430134773254,0.10569857060909271,0.635727047920227,0.7899370789527893,0.10569857060909271,0.6040092706680298,0.8139163851737976,0.10569857060909271,0.5712862014770508,0.8365409970283508,0.10569857060909271,0.53761225938797,0.8577733039855957,0.10569857060909271,0.503043532371521,0.8775779008865356,0.10569857060909271,0.4676375389099121,0.8959218263626099,0.10569857060909271,0.43145322799682617,0.9127746224403381,0.10569857060909271,0.39455077052116394,0.9281081557273865,0.10569857060909271,0.35699164867401123,0.9418969750404358,0.10569857060909271,0.31883832812309265,0.9541180729866028,0.10569857060909271,0.28015434741973877,0.9647511839866638,0.10569857060909271,0.241004079580307,0.9737785458564758,0.10569857060909271,0.20145267248153687,0.9811851382255554,0.10569857060909271,0.16156597435474396,0.9869586229324341,0.10569857060909271,0.12141036987304688,0.9910894632339478,0.10569857060909271,0.08105268329381943,0.9935706853866577,0.10569857060909271,0.04056009650230408,0.9943982362747192,0.10569857060909271,1.217786547456898e-16,0.9935706853866577,0.10569857060909271,-0.04056009650230408,0.9910894632339478,0.10569857060909271,-0.08105268329381943,0.9869586229324341,0.10569857060909271,-0.12141036987304688,0.9811851382255554,0.10569857060909271,-0.16156597435474396,0.9737785458564758,0.10569857060909271,-0.20145267248153687,0.9647511839866638,0.10569857060909271,-0.241004079580307,0.9541180729866028,0.10569857060909271,-0.28015434741973877,0.9418969750404358,0.10569857060909271,-0.31883832812309265,0.9281081557273865,0.10569857060909271,-0.35699164867401123,0.9127746224403381,0.10569857060909271,-0.39455077052116394,0.8959218263626099,0.10569857060909271,-0.43145322799682617,0.8775779008865356,0.10569857060909271,-0.4676375389099121,0.8577733039855957,0.10569857060909271,-0.503043532371521,0.8365409970283508,0.10569857060909271,-0.53761225938797,0.8139163851737976,0.10569857060909271,-0.5712862014770508,0.7899370789527893,0.10569857060909271,-0.6040092706680298,0.7646430134773254,0.10569857060909271,-0.635727047920227,0.7380762696266174,0.10569857060909271,-0.6663866639137268,0.7102810740470886,0.10569857060909271,-0.6959372162818909,0.6813036799430847,0.10569857060909271,-0.72432941198349,0.6511923670768738,0.10569857060909271,-0.7515160441398621,0.6199971437454224,0.10569857060909271,-0.7774518132209778,0.5877700448036194,0.10569857060909271,-0.8020936250686646,0.5545645952224731,0.10569857060909271,-0.8254004716873169,0.52043616771698,0.10569857060909271,-0.8473334312438965,0.48544153571128845,0.10569857060909271,-0.8678561449050903,0.4496389329433441,0.10569857060909271,-0.8869343996047974,0.4130879342556,0.10569857060909271,-0.9045364260673523,0.3758494257926941,0.10569857060909271,-0.9206329584121704,0.3379853069782257,0.10569857060909271,-0.9351971745491028,0.2995586693286896,0.10569857060909271,-0.9482048153877258,0.2606334388256073,0.10569857060909271,-0.9596343040466309,0.22127442061901093,0.10569857060909271,-0.9694665670394897,0.1815471053123474,0.10569857060909271,-0.977685272693634,0.14151762425899506,0.10569857060909271,-0.9842766523361206,0.10125259310007095,0.10569857060909271,-0.9892298579216003,0.06081904470920563,0.10569857060909271,-0.9925366044044495,0.020284269005060196,0.10569857060909271,-0.9941912889480591,-0.020284269005060196,0.10569857060909271,-0.9941912889480591,-0.06081904470920563,0.10569857060909271,-0.9925366044044495,-0.10125259310007095,0.10569857060909271,-0.9892298579216003,-0.14151762425899506,0.10569857060909271,-0.9842766523361206,-0.1815471053123474,0.10569857060909271,-0.977685272693634,-0.22127442061901093,0.10569857060909271,-0.9694665670394897,-0.2606334388256073,0.10569857060909271,-0.9596343040466309,-0.2995586693286896,0.10569857060909271,-0.9482048153877258,-0.3379853069782257,0.10569857060909271,-0.9351971745491028,-0.3758494257926941,0.10569857060909271,-0.9206329584121704,-0.4130879342556,0.10569857060909271,-0.9045364260673523,-0.4496389329433441,0.10569857060909271,-0.8869343996047974,-0.48544153571128845,0.10569857060909271,-0.8678561449050903,-0.52043616771698,0.10569857060909271,-0.8473334312438965,-0.5545645952224731,0.10569857060909271,-0.8254004716873169,-0.5877700448036194,0.10569857060909271,-0.8020936250686646,-0.6199971437454224,0.10569857060909271,-0.7774518132209778,-0.6511923670768738,0.10569857060909271,-0.7515160441398621,-0.6813036799430847,0.10569857060909271,-0.72432941198349,-0.7102810740470886,0.10569857060909271,-0.6959372162818909,-0.7380762696266174,0.10569857060909271,-0.6663866639137268,-0.7646430134773254,0.10569857060909271,-0.635727047920227,-0.7899370789527893,0.10569857060909271,-0.6040092706680298,-0.8139163851737976,0.10569857060909271,-0.5712862014770508,-0.8365409970283508,0.10569857060909271,-0.53761225938797,-0.8577733039855957,0.10569857060909271,-0.503043532371521,-0.8775779008865356,0.10569857060909271,-0.4676375389099121,-0.8959218263626099,0.10569857060909271,-0.43145322799682617,-0.9127746224403381,0.10569857060909271,-0.39455077052116394,-0.9281081557273865,0.10569857060909271,-0.35699164867401123,-0.9418969750404358,0.10569857060909271,-0.31883832812309265,-0.9541180729866028,0.10569857060909271,-0.28015434741973877,-0.9647511839866638,0.10569857060909271,-0.241004079580307,-0.9737785458564758,0.10569857060909271,-0.20145267248153687,-0.9811851382255554,0.10569857060909271,-0.16156597435474396,-0.9869586229324341,0.10569857060909271,-0.12141036987304688,-0.9910894632339478,0.10569857060909271,-0.08105268329381943,-0.9935706853866577,0.10569857060909271,-0.04056009650230408,-0.9943982362747192,0.10569857060909271,-2.435573094913796e-16,-0.9993770718574524,0.035291463136672974,0.0,-0.9985454082489014,0.035291463136672974,0.04076317697763443,-0.9960517287254333,0.035291463136672974,0.08145850896835327,-0.9919002056121826,0.035291463136672974,0.12201826274394989,-0.9860978126525879,0.035291463136672974,0.16237492859363556,-0.978654146194458,0.035291463136672974,0.2024613320827484,-0.9695816040039062,0.035291463136672974,0.2422107607126236,-0.9588952660560608,0.035291463136672974,0.2815570533275604,-0.9466129541397095,0.035291463136672974,0.32043471932411194,-0.9327551126480103,0.035291463136672974,0.35877907276153564,-0.9173448085784912,0.035291463136672974,0.3965262472629547,-0.900407612323761,0.035291463136672974,0.43361344933509827,-0.8819718360900879,0.035291463136672974,0.4699789583683014,-0.8620680570602417,0.035291463136672974,0.5055622458457947,-0.8407294750213623,0.035291463136672974,0.5403040051460266,-0.8179916143417358,0.035291463136672974,0.574146568775177,-0.7938922047615051,0.035291463136672974,0.6070334911346436,-0.7684715390205383,0.035291463136672974,0.6389100551605225,-0.7417717576026917,0.035291463136672974,0.6697232127189636,-0.7138373851776123,0.035291463136672974,0.6994217038154602,-0.6847149133682251,0.035291463136672974,0.7279560565948486,-0.6544528007507324,0.035291463136672974,0.7552787661552429,-0.6231014132499695,0.035291463136672974,0.781344473361969,-0.5907129049301147,0.035291463136672974,0.8061096668243408,-0.5573412775993347,0.035291463136672974,0.8295331597328186,-0.5230419635772705,0.035291463136672974,0.8515759706497192,-0.48787209391593933,0.035291463136672974,0.8722014427185059,-0.451890230178833,0.035291463136672974,0.8913751840591431,-0.4151562452316284,0.035291463136672974,0.9090653657913208,-0.3777312636375427,0.035291463136672974,0.9252424836158752,-0.3396775722503662,0.035291463136672974,0.9398795962333679,-0.3010585308074951,0.035291463136672974,0.9529523849487305,-0.2619384229183197,0.035291463136672974,0.9644390940666199,-0.2223823219537735,0.035291463136672974,0.9743205904960632,-0.1824560910463333,0.035291463136672974,0.9825804233551025,-0.1422261893749237,0.035291463136672974,0.9892048239707947,-0.10175956040620804,0.035291463136672974,0.994182825088501,-0.0611235611140728,0.035291463136672974,0.9975060820579529,-0.020385829731822014,0.035291463136672974,0.999169111251831,0.020385829731822014,0.035291463136672974,0.999169111251831,0.0611235611140728,0.035291463136672974,0.9975060820579529,0.10175956040620804,0.035291463136672974,0.994182825088501,0.1422261893749237,0.035291463136672974,0.9892048239707947,0.1824560910463333,0.035291463136672974,0.9825804233551025,0.2223823219537735,0.035291463136672974,0.9743205904960632,0.2619384229183197,0.035291463136672974,0.9644390940666199,0.3010585308074951,0.035291463136672974,0.9529523849487305,0.3396775722503662,0.035291463136672974,0.9398795962333679,0.3777312636375427,0.035291463136672974,0.9252424836158752,0.4151562452316284,0.035291463136672974,0.9090653657913208,0.451890230178833,0.035291463136672974,0.8913751840591431,0.48787209391593933,0.035291463136672974,0.8722014427185059,0.5230419635772705,0.035291463136672974,0.8515759706497192,0.5573412775993347,0.035291463136672974,0.8295331597328186,0.5907129049301147,0.035291463136672974,0.8061096668243408,0.6231014132499695,0.035291463136672974,0.781344473361969,0.6544528007507324,0.035291463136672974,0.7552787661552429,0.6847149133682251,0.035291463136672974,0.7279560565948486,0.7138373851776123,0.035291463136672974,0.6994217038154602,0.7417717576026917,0.035291463136672974,0.6697232127189636,0.7684715390205383,0.035291463136672974,0.6389100551605225,0.7938922047615051,0.035291463136672974,0.6070334911346436,0.8179916143417358,0.035291463136672974,0.574146568775177,0.8407294750213623,0.035291463136672974,0.5403040051460266,0.8620680570602417,0.035291463136672974,0.5055622458457947,0.8819718360900879,0.035291463136672974,0.4699789583683014,0.900407612323761,0.035291463136672974,0.43361344933509827,0.9173448085784912,0.035291463136672974,0.3965262472629547,0.9327551126480103,0.035291463136672974,0.35877907276153564,0.9466129541397095,0.035291463136672974,0.32043471932411194,0.9588952660560608,0.035291463136672974,0.2815570533275604,0.9695816040039062,0.035291463136672974,0.2422107607126236,0.978654146194458,0.035291463136672974,0.2024613320827484,0.9860978126525879,0.035291463136672974,0.16237492859363556,0.9919002056121826,0.035291463136672974,0.12201826274394989,0.9960517287254333,0.035291463136672974,0.08145850896835327,0.9985454082489014,0.035291463136672974,0.04076317697763443,0.9993770718574524,0.035291463136672974,1.223883861188149e-16,0.9985454082489014,0.035291463136672974,-0.04076317697763443,0.9960517287254333,0.035291463136672974,-0.08145850896835327,0.9919002056121826,0.035291463136672974,-0.12201826274394989,0.9860978126525879,0.035291463136672974,-0.16237492859363556,0.978654146194458,0.035291463136672974,-0.2024613320827484,0.9695816040039062,0.035291463136672974,-0.2422107607126236,0.9588952660560608,0.035291463136672974,-0.2815570533275604,0.9466129541397095,0.035291463136672974,-0.32043471932411194,0.9327551126480103,0.035291463136672974,-0.35877907276153564,0.9173448085784912,0.035291463136672974,-0.3965262472629547,0.900407612323761,0.035291463136672974,-0.43361344933509827,0.8819718360900879,0.035291463136672974,-0.4699789583683014,0.8620680570602417,0.035291463136672974,-0.5055622458457947,0.8407294750213623,0.035291463136672974,-0.5403040051460266,0.8179916143417358,0.035291463136672974,-0.574146568775177,0.7938922047615051,0.035291463136672974,-0.6070334911346436,0.7684715390205383,0.035291463136672974,-0.6389100551605225,0.7417717576026917,0.035291463136672974,-0.6697232127189636,0.7138373851776123,0.035291463136672974,-0.6994217038154602,0.6847149133682251,0.035291463136672974,-0.7279560565948486,0.6544528007507324,0.035291463136672974,-0.7552787661552429,0.6231014132499695,0.035291463136672974,-0.781344473361969,0.5907129049301147,0.035291463136672974,-0.8061096668243408,0.5573412775993347,0.035291463136672974,-0.8295331597328186,0.5230419635772705,0.035291463136672974,-0.8515759706497192,0.48787209391593933,0.035291463136672974,-0.8722014427185059,0.451890230178833,0.035291463136672974,-0.8913751840591431,0.4151562452316284,0.035291463136672974,-0.9090653657913208,0.3777312636375427,0.035291463136672974,-0.9252424836158752,0.3396775722503662,0.035291463136672974,-0.9398795962333679,0.3010585308074951,0.035291463136672974,-0.9529523849487305,0.2619384229183197,0.035291463136672974,-0.9644390940666199,0.2223823219537735,0.035291463136672974,-0.9743205904960632,0.1824560910463333,0.035291463136672974,-0.9825804233551025,0.1422261893749237,0.035291463136672974,-0.9892048239707947,0.10175956040620804,0.035291463136672974,-0.994182825088501,0.0611235611140728,0.035291463136672974,-0.9975060820579529,0.020385829731822014,0.035291463136672974,-0.999169111251831,-0.020385829731822014,0.035291463136672974,-0.999169111251831,-0.0611235611140728,0.035291463136672974,-0.9975060820579529,-0.10175956040620804,0.035291463136672974,-0.994182825088501,-0.1422261893749237,0.035291463136672974,-0.9892048239707947,-0.1824560910463333,0.035291463136672974,-0.9825804233551025,-0.2223823219537735,0.035291463136672974,-0.9743205904960632,-0.2619384229183197,0.035291463136672974,-0.9644390940666199,-0.3010585308074951,0.035291463136672974,-0.9529523849487305,-0.3396775722503662,0.035291463136672974,-0.9398795962333679,-0.3777312636375427,0.035291463136672974,-0.9252424836158752,-0.4151562452316284,0.035291463136672974,-0.9090653657913208,-0.451890230178833,0.035291463136672974,-0.8913751840591431,-0.48787209391593933,0.035291463136672974,-0.8722014427185059,-0.5230419635772705,0.035291463136672974,-0.8515759706497192,-0.5573412775993347,0.035291463136672974,-0.8295331597328186,-0.5907129049301147,0.035291463136672974,-0.8061096668243408,-0.6231014132499695,0.035291463136672974,-0.781344473361969,-0.6544528007507324,0.035291463136672974,-0.7552787661552429,-0.6847149133682251,0.035291463136672974,-0.7279560565948486,-0.7138373851776123,0.035291463136672974,-0.6994217038154602,-0.7417717576026917,0.035291463136672974,-0.6697232127189636,-0.7684715390205383,0.035291463136672974,-0.6389100551605225,-0.7938922047615051,0.035291463136672974,-0.6070334911346436,-0.8179916143417358,0.035291463136672974,-0.574146568775177,-0.8407294750213623,0.035291463136672974,-0.5403040051460266,-0.8620680570602417,0.035291463136672974,-0.5055622458457947,-0.8819718360900879,0.035291463136672974,-0.4699789583683014,-0.900407612323761,0.035291463136672974,-0.43361344933509827,-0.9173448085784912,0.035291463136672974,-0.3965262472629547,-0.9327551126480103,0.035291463136672974,-0.35877907276153564,-0.9466129541397095,0.035291463136672974,-0.32043471932411194,-0.9588952660560608,0.035291463136672974,-0.2815570533275604,-0.9695816040039062,0.035291463136672974,-0.2422107607126236,-0.978654146194458,0.035291463136672974,-0.2024613320827484,-0.9860978126525879,0.035291463136672974,-0.16237492859363556,-0.9919002056121826,0.035291463136672974,-0.12201826274394989,-0.9960517287254333,0.035291463136672974,-0.08145850896835327,-0.9985454082489014,0.035291463136672974,-0.04076317697763443,-0.9993770718574524,0.035291463136672974,-2.447767722376298e-16,-0.9993770718574524,-0.035291463136672974,0.0,-0.9985454082489014,-0.035291463136672974,0.04076317697763443,-0.9960517287254333,-0.035291463136672974,0.08145850896835327,-0.9919002056121826,-0.035291463136672974,0.12201826274394989,-0.9860978126525879,-0.035291463136672974,0.16237492859363556,-0.978654146194458,-0.035291463136672974,0.2024613320827484,-0.9695816040039062,-0.035291463136672974,0.2422107607126236,-0.9588952660560608,-0.035291463136672974,0.2815570533275604,-0.9466129541397095,-0.035291463136672974,0.32043471932411194,-0.9327551126480103,-0.035291463136672974,0.35877907276153564,-0.9173448085784912,-0.035291463136672974,0.3965262472629547,-0.900407612323761,-0.035291463136672974,0.43361344933509827,-0.8819718360900879,-0.035291463136672974,0.4699789583683014,-0.8620680570602417,-0.035291463136672974,0.5055622458457947,-0.8407294750213623,-0.035291463136672974,0.5403040051460266,-0.8179916143417358,-0.035291463136672974,0.574146568775177,-0.7938922047615051,-0.035291463136672974,0.6070334911346436,-0.7684715390205383,-0.035291463136672974,0.6389100551605225,-0.7417717576026917,-0.035291463136672974,0.6697232127189636,-0.7138373851776123,-0.035291463136672974,0.6994217038154602,-0.6847149133682251,-0.035291463136672974,0.7279560565948486,-0.6544528007507324,-0.035291463136672974,0.7552787661552429,-0.6231014132499695,-0.035291463136672974,0.781344473361969,-0.5907129049301147,-0.035291463136672974,0.8061096668243408,-0.5573412775993347,-0.035291463136672974,0.8295331597328186,-0.5230419635772705,-0.035291463136672974,0.8515759706497192,-0.48787209391593933,-0.035291463136672974,0.8722014427185059,-0.451890230178833,-0.035291463136672974,0.8913751840591431,-0.4151562452316284,-0.035291463136672974,0.9090653657913208,-0.3777312636375427,-0.035291463136672974,0.9252424836158752,-0.3396775722503662,-0.035291463136672974,0.9398795962333679,-0.3010585308074951,-0.035291463136672974,0.9529523849487305,-0.2619384229183197,-0.035291463136672974,0.9644390940666199,-0.2223823219537735,-0.035291463136672974,0.9743205904960632,-0.1824560910463333,-0.035291463136672974,0.9825804233551025,-0.1422261893749237,-0.035291463136672974,0.9892048239707947,-0.10175956040620804,-0.035291463136672974,0.994182825088501,-0.0611235611140728,-0.035291463136672974,0.9975060820579529,-0.020385829731822014,-0.035291463136672974,0.999169111251831,0.020385829731822014,-0.035291463136672974,0.999169111251831,0.0611235611140728,-0.035291463136672974,0.9975060820579529,0.10175956040620804,-0.035291463136672974,0.994182825088501,0.1422261893749237,-0.035291463136672974,0.9892048239707947,0.1824560910463333,-0.035291463136672974,0.9825804233551025,0.2223823219537735,-0.035291463136672974,0.9743205904960632,0.2619384229183197,-0.035291463136672974,0.9644390940666199,0.3010585308074951,-0.035291463136672974,0.9529523849487305,0.3396775722503662,-0.035291463136672974,0.9398795962333679,0.3777312636375427,-0.035291463136672974,0.9252424836158752,0.4151562452316284,-0.035291463136672974,0.9090653657913208,0.451890230178833,-0.035291463136672974,0.8913751840591431,0.48787209391593933,-0.035291463136672974,0.8722014427185059,0.5230419635772705,-0.035291463136672974,0.8515759706497192,0.5573412775993347,-0.035291463136672974,0.8295331597328186,0.5907129049301147,-0.035291463136672974,0.8061096668243408,0.6231014132499695,-0.035291463136672974,0.781344473361969,0.6544528007507324,-0.035291463136672974,0.7552787661552429,0.6847149133682251,-0.035291463136672974,0.7279560565948486,0.7138373851776123,-0.035291463136672974,0.6994217038154602,0.7417717576026917,-0.035291463136672974,0.6697232127189636,0.7684715390205383,-0.035291463136672974,0.6389100551605225,0.7938922047615051,-0.035291463136672974,0.6070334911346436,0.8179916143417358,-0.035291463136672974,0.574146568775177,0.8407294750213623,-0.035291463136672974,0.5403040051460266,0.8620680570602417,-0.035291463136672974,0.5055622458457947,0.8819718360900879,-0.035291463136672974,0.4699789583683014,0.900407612323761,-0.035291463136672974,0.43361344933509827,0.9173448085784912,-0.035291463136672974,0.3965262472629547,0.9327551126480103,-0.035291463136672974,0.35877907276153564,0.9466129541397095,-0.035291463136672974,0.32043471932411194,0.9588952660560608,-0.035291463136672974,0.2815570533275604,0.9695816040039062,-0.035291463136672974,0.2422107607126236,0.978654146194458,-0.035291463136672974,0.2024613320827484,0.9860978126525879,-0.035291463136672974,0.16237492859363556,0.9919002056121826,-0.035291463136672974,0.12201826274394989,0.9960517287254333,-0.035291463136672974,0.08145850896835327,0.9985454082489014,-0.035291463136672974,0.04076317697763443,0.9993770718574524,-0.035291463136672974,1.223883861188149e-16,0.9985454082489014,-0.035291463136672974,-0.04076317697763443,0.9960517287254333,-0.035291463136672974,-0.08145850896835327,0.9919002056121826,-0.035291463136672974,-0.12201826274394989,0.9860978126525879,-0.035291463136672974,-0.16237492859363556,0.978654146194458,-0.035291463136672974,-0.2024613320827484,0.9695816040039062,-0.035291463136672974,-0.2422107607126236,0.9588952660560608,-0.035291463136672974,-0.2815570533275604,0.9466129541397095,-0.035291463136672974,-0.32043471932411194,0.9327551126480103,-0.035291463136672974,-0.35877907276153564,0.9173448085784912,-0.035291463136672974,-0.3965262472629547,0.900407612323761,-0.035291463136672974,-0.43361344933509827,0.8819718360900879,-0.035291463136672974,-0.4699789583683014,0.8620680570602417,-0.035291463136672974,-0.5055622458457947,0.8407294750213623,-0.035291463136672974,-0.5403040051460266,0.8179916143417358,-0.035291463136672974,-0.574146568775177,0.7938922047615051,-0.035291463136672974,-0.6070334911346436,0.7684715390205383,-0.035291463136672974,-0.6389100551605225,0.7417717576026917,-0.035291463136672974,-0.6697232127189636,0.7138373851776123,-0.035291463136672974,-0.6994217038154602,0.6847149133682251,-0.035291463136672974,-0.7279560565948486,0.6544528007507324,-0.035291463136672974,-0.7552787661552429,0.6231014132499695,-0.035291463136672974,-0.781344473361969,0.5907129049301147,-0.035291463136672974,-0.8061096668243408,0.5573412775993347,-0.035291463136672974,-0.8295331597328186,0.5230419635772705,-0.035291463136672974,-0.8515759706497192,0.48787209391593933,-0.035291463136672974,-0.8722014427185059,0.451890230178833,-0.035291463136672974,-0.8913751840591431,0.4151562452316284,-0.035291463136672974,-0.9090653657913208,0.3777312636375427,-0.035291463136672974,-0.9252424836158752,0.3396775722503662,-0.035291463136672974,-0.9398795962333679,0.3010585308074951,-0.035291463136672974,-0.9529523849487305,0.2619384229183197,-0.035291463136672974,-0.9644390940666199,0.2223823219537735,-0.035291463136672974,-0.9743205904960632,0.1824560910463333,-0.035291463136672974,-0.9825804233551025,0.1422261893749237,-0.035291463136672974,-0.9892048239707947,0.10175956040620804,-0.035291463136672974,-0.994182825088501,0.0611235611140728,-0.035291463136672974,-0.9975060820579529,0.020385829731822014,-0.035291463136672974,-0.999169111251831,-0.020385829731822014,-0.035291463136672974,-0.999169111251831,-0.0611235611140728,-0.035291463136672974,-0.9975060820579529,-0.10175956040620804,-0.035291463136672974,-0.994182825088501,-0.1422261893749237,-0.035291463136672974,-0.9892048239707947,-0.1824560910463333,-0.035291463136672974,-0.9825804233551025,-0.2223823219537735,-0.035291463136672974,-0.9743205904960632,-0.2619384229183197,-0.035291463136672974,-0.9644390940666199,-0.3010585308074951,-0.035291463136672974,-0.9529523849487305,-0.3396775722503662,-0.035291463136672974,-0.9398795962333679,-0.3777312636375427,-0.035291463136672974,-0.9252424836158752,-0.4151562452316284,-0.035291463136672974,-0.9090653657913208,-0.451890230178833,-0.035291463136672974,-0.8913751840591431,-0.48787209391593933,-0.035291463136672974,-0.8722014427185059,-0.5230419635772705,-0.035291463136672974,-0.8515759706497192,-0.5573412775993347,-0.035291463136672974,-0.8295331597328186,-0.5907129049301147,-0.035291463136672974,-0.8061096668243408,-0.6231014132499695,-0.035291463136672974,-0.781344473361969,-0.6544528007507324,-0.035291463136672974,-0.7552787661552429,-0.6847149133682251,-0.035291463136672974,-0.7279560565948486,-0.7138373851776123,-0.035291463136672974,-0.6994217038154602,-0.7417717576026917,-0.035291463136672974,-0.6697232127189636,-0.7684715390205383,-0.035291463136672974,-0.6389100551605225,-0.7938922047615051,-0.035291463136672974,-0.6070334911346436,-0.8179916143417358,-0.035291463136672974,-0.574146568775177,-0.8407294750213623,-0.035291463136672974,-0.5403040051460266,-0.8620680570602417,-0.035291463136672974,-0.5055622458457947,-0.8819718360900879,-0.035291463136672974,-0.4699789583683014,-0.900407612323761,-0.035291463136672974,-0.43361344933509827,-0.9173448085784912,-0.035291463136672974,-0.3965262472629547,-0.9327551126480103,-0.035291463136672974,-0.35877907276153564,-0.9466129541397095,-0.035291463136672974,-0.32043471932411194,-0.9588952660560608,-0.035291463136672974,-0.2815570533275604,-0.9695816040039062,-0.035291463136672974,-0.2422107607126236,-0.978654146194458,-0.035291463136672974,-0.2024613320827484,-0.9860978126525879,-0.035291463136672974,-0.16237492859363556,-0.9919002056121826,-0.035291463136672974,-0.12201826274394989,-0.9960517287254333,-0.035291463136672974,-0.08145850896835327,-0.9985454082489014,-0.035291463136672974,-0.04076317697763443,-0.9993770718574524,-0.035291463136672974,-2.447767722376298e-16,-0.9943982362747192,-0.10569857060909271,0.0,-0.9935706853866577,-0.10569857060909271,0.04056009650230408,-0.9910894632339478,-0.10569857060909271,0.08105268329381943,-0.9869586229324341,-0.10569857060909271,0.12141036987304688,-0.9811851382255554,-0.10569857060909271,0.16156597435474396,-0.9737785458564758,-0.10569857060909271,0.20145267248153687,-0.9647511839866638,-0.10569857060909271,0.241004079580307,-0.9541180729866028,-0.10569857060909271,0.28015434741973877,-0.9418969750404358,-0.10569857060909271,0.31883832812309265,-0.9281081557273865,-0.10569857060909271,0.35699164867401123,-0.9127746224403381,-0.10569857060909271,0.39455077052116394,-0.8959218263626099,-0.10569857060909271,0.43145322799682617,-0.8775779008865356,-0.10569857060909271,0.4676375389099121,-0.8577733039855957,-0.10569857060909271,0.503043532371521,-0.8365409970283508,-0.10569857060909271,0.53761225938797,-0.8139163851737976,-0.10569857060909271,0.5712862014770508,-0.7899370789527893,-0.10569857060909271,0.6040092706680298,-0.7646430134773254,-0.10569857060909271,0.635727047920227,-0.7380762696266174,-0.10569857060909271,0.6663866639137268,-0.7102810740470886,-0.10569857060909271,0.6959372162818909,-0.6813036799430847,-0.10569857060909271,0.72432941198349,-0.6511923670768738,-0.10569857060909271,0.7515160441398621,-0.6199971437454224,-0.10569857060909271,0.7774518132209778,-0.5877700448036194,-0.10569857060909271,0.8020936250686646,-0.5545645952224731,-0.10569857060909271,0.8254004716873169,-0.52043616771698,-0.10569857060909271,0.8473334312438965,-0.48544153571128845,-0.10569857060909271,0.8678561449050903,-0.4496389329433441,-0.10569857060909271,0.8869343996047974,-0.4130879342556,-0.10569857060909271,0.9045364260673523,-0.3758494257926941,-0.10569857060909271,0.9206329584121704,-0.3379853069782257,-0.10569857060909271,0.9351971745491028,-0.2995586693286896,-0.10569857060909271,0.9482048153877258,-0.2606334388256073,-0.10569857060909271,0.9596343040466309,-0.22127442061901093,-0.10569857060909271,0.9694665670394897,-0.1815471053123474,-0.10569857060909271,0.977685272693634,-0.14151762425899506,-0.10569857060909271,0.9842766523361206,-0.10125259310007095,-0.10569857060909271,0.9892298579216003,-0.06081904470920563,-0.10569857060909271,0.9925366044044495,-0.020284269005060196,-0.10569857060909271,0.9941912889480591,0.020284269005060196,-0.10569857060909271,0.9941912889480591,0.06081904470920563,-0.10569857060909271,0.9925366044044495,0.10125259310007095,-0.10569857060909271,0.9892298579216003,0.14151762425899506,-0.10569857060909271,0.9842766523361206,0.1815471053123474,-0.10569857060909271,0.977685272693634,0.22127442061901093,-0.10569857060909271,0.9694665670394897,0.2606334388256073,-0.10569857060909271,0.9596343040466309,0.2995586693286896,-0.10569857060909271,0.9482048153877258,0.3379853069782257,-0.10569857060909271,0.9351971745491028,0.3758494257926941,-0.10569857060909271,0.9206329584121704,0.4130879342556,-0.10569857060909271,0.9045364260673523,0.4496389329433441,-0.10569857060909271,0.8869343996047974,0.48544153571128845,-0.10569857060909271,0.8678561449050903,0.52043616771698,-0.10569857060909271,0.8473334312438965,0.5545645952224731,-0.10569857060909271,0.8254004716873169,0.5877700448036194,-0.10569857060909271,0.8020936250686646,0.6199971437454224,-0.10569857060909271,0.7774518132209778,0.6511923670768738,-0.10569857060909271,0.7515160441398621,0.6813036799430847,-0.10569857060909271,0.72432941198349,0.7102810740470886,-0.10569857060909271,0.6959372162818909,0.7380762696266174,-0.10569857060909271,0.6663866639137268,0.7646430134773254,-0.10569857060909271,0.635727047920227,0.7899370789527893,-0.10569857060909271,0.6040092706680298,0.8139163851737976,-0.10569857060909271,0.5712862014770508,0.8365409970283508,-0.10569857060909271,0.53761225938797,0.8577733039855957,-0.10569857060909271,0.503043532371521,0.8775779008865356,-0.10569857060909271,0.4676375389099121,0.8959218263626099,-0.10569857060909271,0.43145322799682617,0.9127746224403381,-0.10569857060909271,0.39455077052116394,0.9281081557273865,-0.10569857060909271,0.35699164867401123,0.9418969750404358,-0.10569857060909271,0.31883832812309265,0.9541180729866028,-0.10569857060909271,0.28015434741973877,0.9647511839866638,-0.10569857060909271,0.241004079580307,0.9737785458564758,-0.10569857060909271,0.20145267248153687,0.9811851382255554,-0.10569857060909271,0.16156597435474396,0.9869586229324341,-0.10569857060909271,0.12141036987304688,0.9910894632339478,-0.10569857060909271,0.08105268329381943,0.9935706853866577,-0.10569857060909271,0.04056009650230408,0.9943982362747192,-0.10569857060909271,1.217786547456898e-16,0.9935706853866577,-0.10569857060909271,-0.04056009650230408,0.9910894632339478,-0.10569857060909271,-0.08105268329381943,0.9869586229324341,-0.10569857060909271,-0.12141036987304688,0.9811851382255554,-0.10569857060909271,-0.16156597435474396,0.9737785458564758,-0.10569857060909271,-0.20145267248153687,0.9647511839866638,-0.10569857060909271,-0.241004079580307,0.9541180729866028,-0.10569857060909271,-0.28015434741973877,0.9418969750404358,-0.10569857060909271,-0.31883832812309265,0.9281081557273865,-0.10569857060909271,-0.35699164867401123,0.9127746224403381,-0.10569857060909271,-0.39455077052116394,0.8959218263626099,-0.10569857060909271,-0.43145322799682617,0.8775779008865356,-0.10569857060909271,-0.4676375389099121,0.8577733039855957,-0.10569857060909271,-0.503043532371521,0.8365409970283508,-0.10569857060909271,-0.53761225938797,0.8139163851737976,-0.10569857060909271,-0.5712862014770508,0.7899370789527893,-0.10569857060909271,-0.6040092706680298,0.7646430134773254,-0.10569857060909271,-0.635727047920227,0.7380762696266174,-0.10569857060909271,-0.6663866639137268,0.7102810740470886,-0.10569857060909271,-0.6959372162818909,0.6813036799430847,-0.10569857060909271,-0.72432941198349,0.6511923670768738,-0.10569857060909271,-0.7515160441398621,0.6199971437454224,-0.10569857060909271,-0.7774518132209778,0.5877700448036194,-0.10569857060909271,-0.8020936250686646,0.5545645952224731,-0.10569857060909271,-0.8254004716873169,0.52043616771698,-0.10569857060909271,-0.8473334312438965,0.48544153571128845,-0.10569857060909271,-0.8678561449050903,0.4496389329433441,-0.10569857060909271,-0.8869343996047974,0.4130879342556,-0.10569857060909271,-0.9045364260673523,0.3758494257926941,-0.10569857060909271,-0.9206329584121704,0.3379853069782257,-0.10569857060909271,-0.9351971745491028,0.2995586693286896,-0.10569857060909271,-0.9482048153877258,0.2606334388256073,-0.10569857060909271,-0.9596343040466309,0.22127442061901093,-0.10569857060909271,-0.9694665670394897,0.1815471053123474,-0.10569857060909271,-0.977685272693634,0.14151762425899506,-0.10569857060909271,-0.9842766523361206,0.10125259310007095,-0.10569857060909271,-0.9892298579216003,0.06081904470920563,-0.10569857060909271,-0.9925366044044495,0.020284269005060196,-0.10569857060909271,-0.9941912889480591,-0.020284269005060196,-0.10569857060909271,-0.9941912889480591,-0.06081904470920563,-0.10569857060909271,-0.9925366044044495,-0.10125259310007095,-0.10569857060909271,-0.9892298579216003,-0.14151762425899506,-0.10569857060909271,-0.9842766523361206,-0.1815471053123474,-0.10569857060909271,-0.977685272693634,-0.22127442061901093,-0.10569857060909271,-0.9694665670394897,-0.2606334388256073,-0.10569857060909271,-0.9596343040466309,-0.2995586693286896,-0.10569857060909271,-0.9482048153877258,-0.3379853069782257,-0.10569857060909271,-0.9351971745491028,-0.3758494257926941,-0.10569857060909271,-0.9206329584121704,-0.4130879342556,-0.10569857060909271,-0.9045364260673523,-0.4496389329433441,-0.10569857060909271,-0.8869343996047974,-0.48544153571128845,-0.10569857060909271,-0.8678561449050903,-0.52043616771698,-0.10569857060909271,-0.8473334312438965,-0.5545645952224731,-0.10569857060909271,-0.8254004716873169,-0.5877700448036194,-0.10569857060909271,-0.8020936250686646,-0.6199971437454224,-0.10569857060909271,-0.7774518132209778,-0.6511923670768738,-0.10569857060909271,-0.7515160441398621,-0.6813036799430847,-0.10569857060909271,-0.72432941198349,-0.7102810740470886,-0.10569857060909271,-0.6959372162818909,-0.7380762696266174,-0.10569857060909271,-0.6663866639137268,-0.7646430134773254,-0.10569857060909271,-0.635727047920227,-0.7899370789527893,-0.10569857060909271,-0.6040092706680298,-0.8139163851737976,-0.10569857060909271,-0.5712862014770508,-0.8365409970283508,-0.10569857060909271,-0.53761225938797,-0.8577733039855957,-0.10569857060909271,-0.503043532371521,-0.8775779008865356,-0.10569857060909271,-0.4676375389099121,-0.8959218263626099,-0.10569857060909271,-0.43145322799682617,-0.9127746224403381,-0.10569857060909271,-0.39455077052116394,-0.9281081557273865,-0.10569857060909271,-0.35699164867401123,-0.9418969750404358,-0.10569857060909271,-0.31883832812309265,-0.9541180729866028,-0.10569857060909271,-0.28015434741973877,-0.9647511839866638,-0.10569857060909271,-0.241004079580307,-0.9737785458564758,-0.10569857060909271,-0.20145267248153687,-0.9811851382255554,-0.10569857060909271,-0.16156597435474396,-0.9869586229324341,-0.10569857060909271,-0.12141036987304688,-0.9910894632339478,-0.10569857060909271,-0.08105268329381943,-0.9935706853866577,-0.10569857060909271,-0.04056009650230408,-0.9943982362747192,-0.10569857060909271,-2.435573094913796e-16,-0.9844653010368347,-0.17557910084724426,0.0,-0.983646035194397,-0.17557910084724426,0.04015494883060455,-0.9811896085739136,-0.17557910084724426,0.0802430659532547,-0.9771000742912292,-0.17557910084724426,0.12019762396812439,-0.9713842272758484,-0.17557910084724426,0.15995211899280548,-0.9640516042709351,-0.17557910084724426,0.19944040477275848,-0.9551144242286682,-0.17557910084724426,0.23859672248363495,-0.9445875883102417,-0.17557910084724426,0.27735593914985657,-0.9324885010719299,-0.17557910084724426,0.3156535029411316,-0.9188374280929565,-0.17557910084724426,0.3534257113933563,-0.903657078742981,-0.17557910084724426,0.3906096816062927,-0.8869726061820984,-0.17557910084724426,0.42714348435401917,-0.8688119053840637,-0.17557910084724426,0.4629663825035095,-0.8492051362991333,-0.17557910084724426,0.49801871180534363,-0.828184962272644,-0.17557910084724426,0.5322421193122864,-0.8057863116264343,-0.17557910084724426,0.5655797123908997,-0.7820465564727783,-0.17557910084724426,0.5979759097099304,-0.7570051550865173,-0.17557910084724426,0.6293768286705017,-0.7307037711143494,-0.17557910084724426,0.6597302556037903,-0.7031862139701843,-0.17557910084724426,0.6889855861663818,-0.6744982600212097,-0.17557910084724426,0.7170941829681396,-0.6446877121925354,-0.17557910084724426,0.744009256362915,-0.6138041019439697,-0.17557910084724426,0.7696859836578369,-0.5818988680839539,-0.17557910084724426,0.7940816283226013,-0.5490251779556274,-0.17557910084724426,0.817155659198761,-0.5152376294136047,-0.17557910084724426,0.838869571685791,-0.4805925488471985,-0.17557910084724426,0.8591873049736023,-0.4451475739479065,-0.17557910084724426,0.8780749440193176,-0.408961683511734,-0.17557910084724426,0.8955011367797852,-0.37209510803222656,-0.17557910084724426,0.911436915397644,-0.3346092402935028,-0.17557910084724426,0.9258556365966797,-0.2965664267539978,-0.17557910084724426,0.9387333393096924,-0.2580300271511078,-0.17557910084724426,0.9500486850738525,-0.2190641462802887,-0.17557910084724426,0.9597827196121216,-0.17973366379737854,-0.17557910084724426,0.9679192900657654,-0.1401040256023407,-0.17557910084724426,0.9744448661804199,-0.10024119913578033,-0.17557910084724426,0.9793485999107361,-0.060211535543203354,-0.17557910084724426,0.9826222658157349,-0.020081652328372,-0.17557910084724426,0.9842604994773865,0.020081652328372,-0.17557910084724426,0.9842604994773865,0.060211535543203354,-0.17557910084724426,0.9826222658157349,0.10024119913578033,-0.17557910084724426,0.9793485999107361,0.1401040256023407,-0.17557910084724426,0.9744448661804199,0.17973366379737854,-0.17557910084724426,0.9679192900657654,0.2190641462802887,-0.17557910084724426,0.9597827196121216,0.2580300271511078,-0.17557910084724426,0.9500486850738525,0.2965664267539978,-0.17557910084724426,0.9387333393096924,0.3346092402935028,-0.17557910084724426,0.9258556365966797,0.37209510803222656,-0.17557910084724426,0.911436915397644,0.408961683511734,-0.17557910084724426,0.8955011367797852,0.4451475739479065,-0.17557910084724426,0.8780749440193176,0.4805925488471985,-0.17557910084724426,0.8591873049736023,0.5152376294136047,-0.17557910084724426,0.838869571685791,0.5490251779556274,-0.17557910084724426,0.817155659198761,0.5818988680839539,-0.17557910084724426,0.7940816283226013,0.6138041019439697,-0.17557910084724426,0.7696859836578369,0.6446877121925354,-0.17557910084724426,0.744009256362915,0.6744982600212097,-0.17557910084724426,0.7170941829681396,0.7031862139701843,-0.17557910084724426,0.6889855861663818,0.7307037711143494,-0.17557910084724426,0.6597302556037903,0.7570051550865173,-0.17557910084724426,0.6293768286705017,0.7820465564727783,-0.17557910084724426,0.5979759097099304,0.8057863116264343,-0.17557910084724426,0.5655797123908997,0.828184962272644,-0.17557910084724426,0.5322421193122864,0.8492051362991333,-0.17557910084724426,0.49801871180534363,0.8688119053840637,-0.17557910084724426,0.4629663825035095,0.8869726061820984,-0.17557910084724426,0.42714348435401917,0.903657078742981,-0.17557910084724426,0.3906096816062927,0.9188374280929565,-0.17557910084724426,0.3534257113933563,0.9324885010719299,-0.17557910084724426,0.3156535029411316,0.9445875883102417,-0.17557910084724426,0.27735593914985657,0.9551144242286682,-0.17557910084724426,0.23859672248363495,0.9640516042709351,-0.17557910084724426,0.19944040477275848,0.9713842272758484,-0.17557910084724426,0.15995211899280548,0.9771000742912292,-0.17557910084724426,0.12019762396812439,0.9811896085739136,-0.17557910084724426,0.0802430659532547,0.983646035194397,-0.17557910084724426,0.04015494883060455,0.9844653010368347,-0.17557910084724426,1.2056223602409383e-16,0.983646035194397,-0.17557910084724426,-0.04015494883060455,0.9811896085739136,-0.17557910084724426,-0.0802430659532547,0.9771000742912292,-0.17557910084724426,-0.12019762396812439,0.9713842272758484,-0.17557910084724426,-0.15995211899280548,0.9640516042709351,-0.17557910084724426,-0.19944040477275848,0.9551144242286682,-0.17557910084724426,-0.23859672248363495,0.9445875883102417,-0.17557910084724426,-0.27735593914985657,0.9324885010719299,-0.17557910084724426,-0.3156535029411316,0.9188374280929565,-0.17557910084724426,-0.3534257113933563,0.903657078742981,-0.17557910084724426,-0.3906096816062927,0.8869726061820984,-0.17557910084724426,-0.42714348435401917,0.8688119053840637,-0.17557910084724426,-0.4629663825035095,0.8492051362991333,-0.17557910084724426,-0.49801871180534363,0.828184962272644,-0.17557910084724426,-0.5322421193122864,0.8057863116264343,-0.17557910084724426,-0.5655797123908997,0.7820465564727783,-0.17557910084724426,-0.5979759097099304,0.7570051550865173,-0.17557910084724426,-0.6293768286705017,0.7307037711143494,-0.17557910084724426,-0.6597302556037903,0.7031862139701843,-0.17557910084724426,-0.6889855861663818,0.6744982600212097,-0.17557910084724426,-0.7170941829681396,0.6446877121925354,-0.17557910084724426,-0.744009256362915,0.6138041019439697,-0.17557910084724426,-0.7696859836578369,0.5818988680839539,-0.17557910084724426,-0.7940816283226013,0.5490251779556274,-0.17557910084724426,-0.817155659198761,0.5152376294136047,-0.17557910084724426,-0.838869571685791,0.4805925488471985,-0.17557910084724426,-0.8591873049736023,0.4451475739479065,-0.17557910084724426,-0.8780749440193176,0.408961683511734,-0.17557910084724426,-0.8955011367797852,0.37209510803222656,-0.17557910084724426,-0.911436915397644,0.3346092402935028,-0.17557910084724426,-0.9258556365966797,0.2965664267539978,-0.17557910084724426,-0.9387333393096924,0.2580300271511078,-0.17557910084724426,-0.9500486850738525,0.2190641462802887,-0.17557910084724426,-0.9597827196121216,0.17973366379737854,-0.17557910084724426,-0.9679192900657654,0.1401040256023407,-0.17557910084724426,-0.9744448661804199,0.10024119913578033,-0.17557910084724426,-0.9793485999107361,0.060211535543203354,-0.17557910084724426,-0.9826222658157349,0.020081652328372,-0.17557910084724426,-0.9842604994773865,-0.020081652328372,-0.17557910084724426,-0.9842604994773865,-0.060211535543203354,-0.17557910084724426,-0.9826222658157349,-0.10024119913578033,-0.17557910084724426,-0.9793485999107361,-0.1401040256023407,-0.17557910084724426,-0.9744448661804199,-0.17973366379737854,-0.17557910084724426,-0.9679192900657654,-0.2190641462802887,-0.17557910084724426,-0.9597827196121216,-0.2580300271511078,-0.17557910084724426,-0.9500486850738525,-0.2965664267539978,-0.17557910084724426,-0.9387333393096924,-0.3346092402935028,-0.17557910084724426,-0.9258556365966797,-0.37209510803222656,-0.17557910084724426,-0.911436915397644,-0.408961683511734,-0.17557910084724426,-0.8955011367797852,-0.4451475739479065,-0.17557910084724426,-0.8780749440193176,-0.4805925488471985,-0.17557910084724426,-0.8591873049736023,-0.5152376294136047,-0.17557910084724426,-0.838869571685791,-0.5490251779556274,-0.17557910084724426,-0.817155659198761,-0.5818988680839539,-0.17557910084724426,-0.7940816283226013,-0.6138041019439697,-0.17557910084724426,-0.7696859836578369,-0.6446877121925354,-0.17557910084724426,-0.744009256362915,-0.6744982600212097,-0.17557910084724426,-0.7170941829681396,-0.7031862139701843,-0.17557910084724426,-0.6889855861663818,-0.7307037711143494,-0.17557910084724426,-0.6597302556037903,-0.7570051550865173,-0.17557910084724426,-0.6293768286705017,-0.7820465564727783,-0.17557910084724426,-0.5979759097099304,-0.8057863116264343,-0.17557910084724426,-0.5655797123908997,-0.828184962272644,-0.17557910084724426,-0.5322421193122864,-0.8492051362991333,-0.17557910084724426,-0.49801871180534363,-0.8688119053840637,-0.17557910084724426,-0.4629663825035095,-0.8869726061820984,-0.17557910084724426,-0.42714348435401917,-0.903657078742981,-0.17557910084724426,-0.3906096816062927,-0.9188374280929565,-0.17557910084724426,-0.3534257113933563,-0.9324885010719299,-0.17557910084724426,-0.3156535029411316,-0.9445875883102417,-0.17557910084724426,-0.27735593914985657,-0.9551144242286682,-0.17557910084724426,-0.23859672248363495,-0.9640516042709351,-0.17557910084724426,-0.19944040477275848,-0.9713842272758484,-0.17557910084724426,-0.15995211899280548,-0.9771000742912292,-0.17557910084724426,-0.12019762396812439,-0.9811896085739136,-0.17557910084724426,-0.0802430659532547,-0.983646035194397,-0.17557910084724426,-0.04015494883060455,-0.9844653010368347,-0.17557910084724426,-2.4112447204818765e-16,-0.969627857208252,-0.24458488821983337,0.0,-0.9688209295272827,-0.24458488821983337,0.039549749344587326,-0.9664015173912048,-0.24458488821983337,0.07903367280960083,-0.9623736143112183,-0.24458488821983337,0.11838605254888535,-0.9567439556121826,-0.24458488821983337,0.1575413942337036,-0.9495218396186829,-0.24458488821983337,0.19643451273441315,-0.9407193660736084,-0.24458488821983337,0.23500069975852966,-0.9303511381149292,-0.24458488821983337,0.27317574620246887,-0.9184344410896301,-0.24458488821983337,0.310896098613739,-0.9049891233444214,-0.24458488821983337,0.348099023103714,-0.8900375366210938,-0.24458488821983337,0.3847225606441498,-0.8736045360565186,-0.24458488821983337,0.42070576548576355,-0.8557175397872925,-0.24458488821983337,0.4559887647628784,-0.8364062905311584,-0.24458488821983337,0.49051278829574585,-0.8157028555870056,-0.24458488821983337,0.5242204070091248,-0.7936418652534485,-0.24458488821983337,0.5570555329322815,-0.7702598571777344,-0.24458488821983337,0.5889634490013123,-0.7455958724021912,-0.24458488821983337,0.6198911666870117,-0.7196909189224243,-0.24458488821983337,0.6497870683670044,-0.6925880908966064,-0.24458488821983337,0.6786015033721924,-0.6643325090408325,-0.24458488821983337,0.7062864303588867,-0.6349712014198303,-0.24458488821983337,0.7327958345413208,-0.6045531034469604,-0.24458488821983337,0.7580856084823608,-0.5731287598609924,-0.24458488821983337,0.7821136116981506,-0.5407505035400391,-0.24458488821983337,0.8048398494720459,-0.5074722170829773,-0.24458488821983337,0.8262264728546143,-0.47334927320480347,-0.24458488821983337,0.8462379574775696,-0.4384384751319885,-0.24458488821983337,0.8648409843444824,-0.40279796719551086,-0.24458488821983337,0.8820045590400696,-0.366487056016922,-0.24458488821983337,0.8977001309394836,-0.3295661509037018,-0.24458488821983337,0.9119015336036682,-0.29209670424461365,-0.24458488821983337,0.9245851635932922,-0.25414109230041504,-0.24458488821983337,0.9357299208641052,-0.21576249599456787,-0.24458488821983337,0.945317268371582,-0.17702478170394897,-0.24458488821983337,0.9533312320709229,-0.13799244165420532,-0.24458488821983337,0.959758460521698,-0.0987304076552391,-0.24458488821983337,0.9645882844924927,-0.05930405110120773,-0.24458488821983337,0.9678125977516174,-0.019778991118073463,-0.24458488821983337,0.969426155090332,0.019778991118073463,-0.24458488821983337,0.969426155090332,0.05930405110120773,-0.24458488821983337,0.9678125977516174,0.0987304076552391,-0.24458488821983337,0.9645882844924927,0.13799244165420532,-0.24458488821983337,0.959758460521698,0.17702478170394897,-0.24458488821983337,0.9533312320709229,0.21576249599456787,-0.24458488821983337,0.945317268371582,0.25414109230041504,-0.24458488821983337,0.9357299208641052,0.29209670424461365,-0.24458488821983337,0.9245851635932922,0.3295661509037018,-0.24458488821983337,0.9119015336036682,0.366487056016922,-0.24458488821983337,0.8977001309394836,0.40279796719551086,-0.24458488821983337,0.8820045590400696,0.4384384751319885,-0.24458488821983337,0.8648409843444824,0.47334927320480347,-0.24458488821983337,0.8462379574775696,0.5074722170829773,-0.24458488821983337,0.8262264728546143,0.5407505035400391,-0.24458488821983337,0.8048398494720459,0.5731287598609924,-0.24458488821983337,0.7821136116981506,0.6045531034469604,-0.24458488821983337,0.7580856084823608,0.6349712014198303,-0.24458488821983337,0.7327958345413208,0.6643325090408325,-0.24458488821983337,0.7062864303588867,0.6925880908966064,-0.24458488821983337,0.6786015033721924,0.7196909189224243,-0.24458488821983337,0.6497870683670044,0.7455958724021912,-0.24458488821983337,0.6198911666870117,0.7702598571777344,-0.24458488821983337,0.5889634490013123,0.7936418652534485,-0.24458488821983337,0.5570555329322815,0.8157028555870056,-0.24458488821983337,0.5242204070091248,0.8364062905311584,-0.24458488821983337,0.49051278829574585,0.8557175397872925,-0.24458488821983337,0.4559887647628784,0.8736045360565186,-0.24458488821983337,0.42070576548576355,0.8900375366210938,-0.24458488821983337,0.3847225606441498,0.9049891233444214,-0.24458488821983337,0.348099023103714,0.9184344410896301,-0.24458488821983337,0.310896098613739,0.9303511381149292,-0.24458488821983337,0.27317574620246887,0.9407193660736084,-0.24458488821983337,0.23500069975852966,0.9495218396186829,-0.24458488821983337,0.19643451273441315,0.9567439556121826,-0.24458488821983337,0.1575413942337036,0.9623736143112183,-0.24458488821983337,0.11838605254888535,0.9664015173912048,-0.24458488821983337,0.07903367280960083,0.9688209295272827,-0.24458488821983337,0.039549749344587326,0.969627857208252,-0.24458488821983337,1.1874516506377614e-16,0.9688209295272827,-0.24458488821983337,-0.039549749344587326,0.9664015173912048,-0.24458488821983337,-0.07903367280960083,0.9623736143112183,-0.24458488821983337,-0.11838605254888535,0.9567439556121826,-0.24458488821983337,-0.1575413942337036,0.9495218396186829,-0.24458488821983337,-0.19643451273441315,0.9407193660736084,-0.24458488821983337,-0.23500069975852966,0.9303511381149292,-0.24458488821983337,-0.27317574620246887,0.9184344410896301,-0.24458488821983337,-0.310896098613739,0.9049891233444214,-0.24458488821983337,-0.348099023103714,0.8900375366210938,-0.24458488821983337,-0.3847225606441498,0.8736045360565186,-0.24458488821983337,-0.42070576548576355,0.8557175397872925,-0.24458488821983337,-0.4559887647628784,0.8364062905311584,-0.24458488821983337,-0.49051278829574585,0.8157028555870056,-0.24458488821983337,-0.5242204070091248,0.7936418652534485,-0.24458488821983337,-0.5570555329322815,0.7702598571777344,-0.24458488821983337,-0.5889634490013123,0.7455958724021912,-0.24458488821983337,-0.6198911666870117,0.7196909189224243,-0.24458488821983337,-0.6497870683670044,0.6925880908966064,-0.24458488821983337,-0.6786015033721924,0.6643325090408325,-0.24458488821983337,-0.7062864303588867,0.6349712014198303,-0.24458488821983337,-0.7327958345413208,0.6045531034469604,-0.24458488821983337,-0.7580856084823608,0.5731287598609924,-0.24458488821983337,-0.7821136116981506,0.5407505035400391,-0.24458488821983337,-0.8048398494720459,0.5074722170829773,-0.24458488821983337,-0.8262264728546143,0.47334927320480347,-0.24458488821983337,-0.8462379574775696,0.4384384751319885,-0.24458488821983337,-0.8648409843444824,0.40279796719551086,-0.24458488821983337,-0.8820045590400696,0.366487056016922,-0.24458488821983337,-0.8977001309394836,0.3295661509037018,-0.24458488821983337,-0.9119015336036682,0.29209670424461365,-0.24458488821983337,-0.9245851635932922,0.25414109230041504,-0.24458488821983337,-0.9357299208641052,0.21576249599456787,-0.24458488821983337,-0.945317268371582,0.17702478170394897,-0.24458488821983337,-0.9533312320709229,0.13799244165420532,-0.24458488821983337,-0.959758460521698,0.0987304076552391,-0.24458488821983337,-0.9645882844924927,0.05930405110120773,-0.24458488821983337,-0.9678125977516174,0.019778991118073463,-0.24458488821983337,-0.969426155090332,-0.019778991118073463,-0.24458488821983337,-0.969426155090332,-0.05930405110120773,-0.24458488821983337,-0.9678125977516174,-0.0987304076552391,-0.24458488821983337,-0.9645882844924927,-0.13799244165420532,-0.24458488821983337,-0.959758460521698,-0.17702478170394897,-0.24458488821983337,-0.9533312320709229,-0.21576249599456787,-0.24458488821983337,-0.945317268371582,-0.25414109230041504,-0.24458488821983337,-0.9357299208641052,-0.29209670424461365,-0.24458488821983337,-0.9245851635932922,-0.3295661509037018,-0.24458488821983337,-0.9119015336036682,-0.366487056016922,-0.24458488821983337,-0.8977001309394836,-0.40279796719551086,-0.24458488821983337,-0.8820045590400696,-0.4384384751319885,-0.24458488821983337,-0.8648409843444824,-0.47334927320480347,-0.24458488821983337,-0.8462379574775696,-0.5074722170829773,-0.24458488821983337,-0.8262264728546143,-0.5407505035400391,-0.24458488821983337,-0.8048398494720459,-0.5731287598609924,-0.24458488821983337,-0.7821136116981506,-0.6045531034469604,-0.24458488821983337,-0.7580856084823608,-0.6349712014198303,-0.24458488821983337,-0.7327958345413208,-0.6643325090408325,-0.24458488821983337,-0.7062864303588867,-0.6925880908966064,-0.24458488821983337,-0.6786015033721924,-0.7196909189224243,-0.24458488821983337,-0.6497870683670044,-0.7455958724021912,-0.24458488821983337,-0.6198911666870117,-0.7702598571777344,-0.24458488821983337,-0.5889634490013123,-0.7936418652534485,-0.24458488821983337,-0.5570555329322815,-0.8157028555870056,-0.24458488821983337,-0.5242204070091248,-0.8364062905311584,-0.24458488821983337,-0.49051278829574585,-0.8557175397872925,-0.24458488821983337,-0.4559887647628784,-0.8736045360565186,-0.24458488821983337,-0.42070576548576355,-0.8900375366210938,-0.24458488821983337,-0.3847225606441498,-0.9049891233444214,-0.24458488821983337,-0.348099023103714,-0.9184344410896301,-0.24458488821983337,-0.310896098613739,-0.9303511381149292,-0.24458488821983337,-0.27317574620246887,-0.9407193660736084,-0.24458488821983337,-0.23500069975852966,-0.9495218396186829,-0.24458488821983337,-0.19643451273441315,-0.9567439556121826,-0.24458488821983337,-0.1575413942337036,-0.9623736143112183,-0.24458488821983337,-0.11838605254888535,-0.9664015173912048,-0.24458488821983337,-0.07903367280960083,-0.9688209295272827,-0.24458488821983337,-0.039549749344587326,-0.969627857208252,-0.24458488821983337,-2.374903301275523e-16,-0.9499598145484924,-0.3123721778392792,0.0,-0.9491692185401917,-0.3123721778392792,0.03874751552939415,-0.9467988610267639,-0.3123721778392792,0.07743054628372192,-0.9428526759147644,-0.3123721778392792,0.11598469316959381,-0.9373371601104736,-0.3123721778392792,0.1543457955121994,-0.9302616119384766,-0.3123721778392792,0.19245001673698425,-0.9216376543045044,-0.3123721778392792,0.23023390769958496,-0.9114797711372375,-0.3123721778392792,0.2676346004009247,-0.8998047709465027,-0.3123721778392792,0.3045898377895355,-0.8866322040557861,-0.3123721778392792,0.34103813767433167,-0.8719838857650757,-0.3123721778392792,0.3769187927246094,-0.8558841943740845,-0.3123721778392792,0.4121721088886261,-0.8383600115776062,-0.3123721778392792,0.44673940539360046,-0.819440484046936,-0.3123721778392792,0.4805631637573242,-0.7991570234298706,-0.3123721778392792,0.5135870575904846,-0.7775434851646423,-0.3123721778392792,0.5457561016082764,-0.7546358108520508,-0.3123721778392792,0.5770168304443359,-0.7304720878601074,-0.3123721778392792,0.6073171496391296,-0.7050926089286804,-0.3123721778392792,0.6366066932678223,-0.678539514541626,-0.3123721778392792,0.6648366451263428,-0.6508570909500122,-0.3123721778392792,0.6919600367546082,-0.6220913529396057,-0.3123721778392792,0.7179316878318787,-0.5922902226448059,-0.3123721778392792,0.7427085041999817,-0.5615032911300659,-0.3123721778392792,0.7662490606307983,-0.5297818183898926,-0.3123721778392792,0.7885143160820007,-0.4971785545349121,-0.3123721778392792,0.8094671964645386,-0.4637477695941925,-0.3123721778392792,0.8290727734565735,-0.429545134305954,-0.3123721778392792,0.8472983837127686,-0.39462757110595703,-0.3123721778392792,0.8641138076782227,-0.3590531647205353,-0.3123721778392792,0.8794910311698914,-0.32288116216659546,-0.3123721778392792,0.8934043645858765,-0.2861717641353607,-0.3123721778392792,0.9058307409286499,-0.24898606538772583,-0.3123721778392792,0.9167494773864746,-0.21138593554496765,-0.3123721778392792,0.9261423349380493,-0.17343398928642273,-0.3123721778392792,0.9339936971664429,-0.1351933777332306,-0.3123721778392792,0.9402905702590942,-0.09672774374485016,-0.3123721778392792,0.9450224041938782,-0.058101117610931396,-0.3123721778392792,0.9481813311576843,-0.01937779039144516,-0.3123721778392792,0.9497621655464172,0.01937779039144516,-0.3123721778392792,0.9497621655464172,0.058101117610931396,-0.3123721778392792,0.9481813311576843,0.09672774374485016,-0.3123721778392792,0.9450224041938782,0.1351933777332306,-0.3123721778392792,0.9402905702590942,0.17343398928642273,-0.3123721778392792,0.9339936971664429,0.21138593554496765,-0.3123721778392792,0.9261423349380493,0.24898606538772583,-0.3123721778392792,0.9167494773864746,0.2861717641353607,-0.3123721778392792,0.9058307409286499,0.32288116216659546,-0.3123721778392792,0.8934043645858765,0.3590531647205353,-0.3123721778392792,0.8794910311698914,0.39462757110595703,-0.3123721778392792,0.8641138076782227,0.429545134305954,-0.3123721778392792,0.8472983837127686,0.4637477695941925,-0.3123721778392792,0.8290727734565735,0.4971785545349121,-0.3123721778392792,0.8094671964645386,0.5297818183898926,-0.3123721778392792,0.7885143160820007,0.5615032911300659,-0.3123721778392792,0.7662490606307983,0.5922902226448059,-0.3123721778392792,0.7427085041999817,0.6220913529396057,-0.3123721778392792,0.7179316878318787,0.6508570909500122,-0.3123721778392792,0.6919600367546082,0.678539514541626,-0.3123721778392792,0.6648366451263428,0.7050926089286804,-0.3123721778392792,0.6366066932678223,0.7304720878601074,-0.3123721778392792,0.6073171496391296,0.7546358108520508,-0.3123721778392792,0.5770168304443359,0.7775434851646423,-0.3123721778392792,0.5457561016082764,0.7991570234298706,-0.3123721778392792,0.5135870575904846,0.819440484046936,-0.3123721778392792,0.4805631637573242,0.8383600115776062,-0.3123721778392792,0.44673940539360046,0.8558841943740845,-0.3123721778392792,0.4121721088886261,0.8719838857650757,-0.3123721778392792,0.3769187927246094,0.8866322040557861,-0.3123721778392792,0.34103813767433167,0.8998047709465027,-0.3123721778392792,0.3045898377895355,0.9114797711372375,-0.3123721778392792,0.2676346004009247,0.9216376543045044,-0.3123721778392792,0.23023390769958496,0.9302616119384766,-0.3123721778392792,0.19245001673698425,0.9373371601104736,-0.3123721778392792,0.1543457955121994,0.9428526759147644,-0.3123721778392792,0.11598469316959381,0.9467988610267639,-0.3123721778392792,0.07743054628372192,0.9491692185401917,-0.3123721778392792,0.03874751552939415,0.9499598145484924,-0.3123721778392792,1.1633652099914013e-16,0.9491692185401917,-0.3123721778392792,-0.03874751552939415,0.9467988610267639,-0.3123721778392792,-0.07743054628372192,0.9428526759147644,-0.3123721778392792,-0.11598469316959381,0.9373371601104736,-0.3123721778392792,-0.1543457955121994,0.9302616119384766,-0.3123721778392792,-0.19245001673698425,0.9216376543045044,-0.3123721778392792,-0.23023390769958496,0.9114797711372375,-0.3123721778392792,-0.2676346004009247,0.8998047709465027,-0.3123721778392792,-0.3045898377895355,0.8866322040557861,-0.3123721778392792,-0.34103813767433167,0.8719838857650757,-0.3123721778392792,-0.3769187927246094,0.8558841943740845,-0.3123721778392792,-0.4121721088886261,0.8383600115776062,-0.3123721778392792,-0.44673940539360046,0.819440484046936,-0.3123721778392792,-0.4805631637573242,0.7991570234298706,-0.3123721778392792,-0.5135870575904846,0.7775434851646423,-0.3123721778392792,-0.5457561016082764,0.7546358108520508,-0.3123721778392792,-0.5770168304443359,0.7304720878601074,-0.3123721778392792,-0.6073171496391296,0.7050926089286804,-0.3123721778392792,-0.6366066932678223,0.678539514541626,-0.3123721778392792,-0.6648366451263428,0.6508570909500122,-0.3123721778392792,-0.6919600367546082,0.6220913529396057,-0.3123721778392792,-0.7179316878318787,0.5922902226448059,-0.3123721778392792,-0.7427085041999817,0.5615032911300659,-0.3123721778392792,-0.7662490606307983,0.5297818183898926,-0.3123721778392792,-0.7885143160820007,0.4971785545349121,-0.3123721778392792,-0.8094671964645386,0.4637477695941925,-0.3123721778392792,-0.8290727734565735,0.429545134305954,-0.3123721778392792,-0.8472983837127686,0.39462757110595703,-0.3123721778392792,-0.8641138076782227,0.3590531647205353,-0.3123721778392792,-0.8794910311698914,0.32288116216659546,-0.3123721778392792,-0.8934043645858765,0.2861717641353607,-0.3123721778392792,-0.9058307409286499,0.24898606538772583,-0.3123721778392792,-0.9167494773864746,0.21138593554496765,-0.3123721778392792,-0.9261423349380493,0.17343398928642273,-0.3123721778392792,-0.9339936971664429,0.1351933777332306,-0.3123721778392792,-0.9402905702590942,0.09672774374485016,-0.3123721778392792,-0.9450224041938782,0.058101117610931396,-0.3123721778392792,-0.9481813311576843,0.01937779039144516,-0.3123721778392792,-0.9497621655464172,-0.01937779039144516,-0.3123721778392792,-0.9497621655464172,-0.058101117610931396,-0.3123721778392792,-0.9481813311576843,-0.09672774374485016,-0.3123721778392792,-0.9450224041938782,-0.1351933777332306,-0.3123721778392792,-0.9402905702590942,-0.17343398928642273,-0.3123721778392792,-0.9339936971664429,-0.21138593554496765,-0.3123721778392792,-0.9261423349380493,-0.24898606538772583,-0.3123721778392792,-0.9167494773864746,-0.2861717641353607,-0.3123721778392792,-0.9058307409286499,-0.32288116216659546,-0.3123721778392792,-0.8934043645858765,-0.3590531647205353,-0.3123721778392792,-0.8794910311698914,-0.39462757110595703,-0.3123721778392792,-0.8641138076782227,-0.429545134305954,-0.3123721778392792,-0.8472983837127686,-0.4637477695941925,-0.3123721778392792,-0.8290727734565735,-0.4971785545349121,-0.3123721778392792,-0.8094671964645386,-0.5297818183898926,-0.3123721778392792,-0.7885143160820007,-0.5615032911300659,-0.3123721778392792,-0.7662490606307983,-0.5922902226448059,-0.3123721778392792,-0.7427085041999817,-0.6220913529396057,-0.3123721778392792,-0.7179316878318787,-0.6508570909500122,-0.3123721778392792,-0.6919600367546082,-0.678539514541626,-0.3123721778392792,-0.6648366451263428,-0.7050926089286804,-0.3123721778392792,-0.6366066932678223,-0.7304720878601074,-0.3123721778392792,-0.6073171496391296,-0.7546358108520508,-0.3123721778392792,-0.5770168304443359,-0.7775434851646423,-0.3123721778392792,-0.5457561016082764,-0.7991570234298706,-0.3123721778392792,-0.5135870575904846,-0.819440484046936,-0.3123721778392792,-0.4805631637573242,-0.8383600115776062,-0.3123721778392792,-0.44673940539360046,-0.8558841943740845,-0.3123721778392792,-0.4121721088886261,-0.8719838857650757,-0.3123721778392792,-0.3769187927246094,-0.8866322040557861,-0.3123721778392792,-0.34103813767433167,-0.8998047709465027,-0.3123721778392792,-0.3045898377895355,-0.9114797711372375,-0.3123721778392792,-0.2676346004009247,-0.9216376543045044,-0.3123721778392792,-0.23023390769958496,-0.9302616119384766,-0.3123721778392792,-0.19245001673698425,-0.9373371601104736,-0.3123721778392792,-0.1543457955121994,-0.9428526759147644,-0.3123721778392792,-0.11598469316959381,-0.9467988610267639,-0.3123721778392792,-0.07743054628372192,-0.9491692185401917,-0.3123721778392792,-0.03874751552939415,-0.9499598145484924,-0.3123721778392792,-2.3267304199828026e-16,-0.9255590438842773,-0.3786032497882843,0.0,-0.9247888326644897,-0.3786032497882843,0.037752244621515274,-0.9224793314933777,-0.3786032497882843,0.07544165849685669,-0.9186344742774963,-0.3786032497882843,0.11300550401210785,-0.9132606983184814,-0.3786032497882843,0.15038126707077026,-0.9063668251037598,-0.3786032497882843,0.18750672042369843,-0.8979644179344177,-0.3786032497882843,0.22432009875774384,-0.8880674242973328,-0.3786032497882843,0.2607601284980774,-0.8766923546791077,-0.3786032497882843,0.29676613211631775,-0.8638581037521362,-0.3786032497882843,0.33227819204330444,-0.849586009979248,-0.3786032497882843,0.36723724007606506,-0.8338999152183533,-0.3786032497882843,0.40158504247665405,-0.8168258666992188,-0.3786032497882843,0.4352644383907318,-0.7983922362327576,-0.3786032497882843,0.4682193696498871,-0.7786298394203186,-0.3786032497882843,0.5003949999809265,-0.7575714588165283,-0.3786032497882843,0.5317378044128418,-0.7352521419525146,-0.3786032497882843,0.5621955394744873,-0.7117091417312622,-0.3786032497882843,0.5917176008224487,-0.6869814991950989,-0.3786032497882843,0.6202547550201416,-0.661110520362854,-0.3786032497882843,0.6477596163749695,-0.6341391205787659,-0.3786032497882843,0.6741862893104553,-0.6061123013496399,-0.3786032497882843,0.6994908452033997,-0.5770766139030457,-0.3786032497882843,0.7236312031745911,-0.5470805168151855,-0.3786032497882843,0.7465671300888062,-0.516173779964447,-0.3786032497882843,0.7682604789733887,-0.4844079911708832,-0.3786032497882843,0.7886751294136047,-0.45183590054512024,-0.3786032497882843,0.8077771067619324,-0.4185118079185486,-0.3786032497882843,0.8255346417427063,-0.3844911456108093,-0.3786032497882843,0.8419181108474731,-0.3498305082321167,-0.3786032497882843,0.8569003343582153,-0.31458762288093567,-0.3786032497882843,0.870456337928772,-0.2788211405277252,-0.3786032497882843,0.8825635313987732,-0.24259059131145477,-0.3786032497882843,0.8932017683982849,-0.20595626533031464,-0.3786032497882843,0.9023533463478088,-0.16897915303707123,-0.3786032497882843,0.9100030660629272,-0.13172079622745514,-0.3786032497882843,0.916138231754303,-0.09424319118261337,-0.3786032497882843,0.9207485318183899,-0.05660872906446457,-0.3786032497882843,0.923826277256012,-0.018880050629377365,-0.3786032497882843,0.9253664612770081,0.018880050629377365,-0.3786032497882843,0.9253664612770081,0.05660872906446457,-0.3786032497882843,0.923826277256012,0.09424319118261337,-0.3786032497882843,0.9207485318183899,0.13172079622745514,-0.3786032497882843,0.916138231754303,0.16897915303707123,-0.3786032497882843,0.9100030660629272,0.20595626533031464,-0.3786032497882843,0.9023533463478088,0.24259059131145477,-0.3786032497882843,0.8932017683982849,0.2788211405277252,-0.3786032497882843,0.8825635313987732,0.31458762288093567,-0.3786032497882843,0.870456337928772,0.3498305082321167,-0.3786032497882843,0.8569003343582153,0.3844911456108093,-0.3786032497882843,0.8419181108474731,0.4185118079185486,-0.3786032497882843,0.8255346417427063,0.45183590054512024,-0.3786032497882843,0.8077771067619324,0.4844079911708832,-0.3786032497882843,0.7886751294136047,0.516173779964447,-0.3786032497882843,0.7682604789733887,0.5470805168151855,-0.3786032497882843,0.7465671300888062,0.5770766139030457,-0.3786032497882843,0.7236312031745911,0.6061123013496399,-0.3786032497882843,0.6994908452033997,0.6341391205787659,-0.3786032497882843,0.6741862893104553,0.661110520362854,-0.3786032497882843,0.6477596163749695,0.6869814991950989,-0.3786032497882843,0.6202547550201416,0.7117091417312622,-0.3786032497882843,0.5917176008224487,0.7352521419525146,-0.3786032497882843,0.5621955394744873,0.7575714588165283,-0.3786032497882843,0.5317378044128418,0.7786298394203186,-0.3786032497882843,0.5003949999809265,0.7983922362327576,-0.3786032497882843,0.4682193696498871,0.8168258666992188,-0.3786032497882843,0.4352644383907318,0.8338999152183533,-0.3786032497882843,0.40158504247665405,0.849586009979248,-0.3786032497882843,0.36723724007606506,0.8638581037521362,-0.3786032497882843,0.33227819204330444,0.8766923546791077,-0.3786032497882843,0.29676613211631775,0.8880674242973328,-0.3786032497882843,0.2607601284980774,0.8979644179344177,-0.3786032497882843,0.22432009875774384,0.9063668251037598,-0.3786032497882843,0.18750672042369843,0.9132606983184814,-0.3786032497882843,0.15038126707077026,0.9186344742774963,-0.3786032497882843,0.11300550401210785,0.9224793314933777,-0.3786032497882843,0.07544165849685669,0.9247888326644897,-0.3786032497882843,0.037752244621515274,0.9255590438842773,-0.3786032497882843,1.1334829464034537e-16,0.9247888326644897,-0.3786032497882843,-0.037752244621515274,0.9224793314933777,-0.3786032497882843,-0.07544165849685669,0.9186344742774963,-0.3786032497882843,-0.11300550401210785,0.9132606983184814,-0.3786032497882843,-0.15038126707077026,0.9063668251037598,-0.3786032497882843,-0.18750672042369843,0.8979644179344177,-0.3786032497882843,-0.22432009875774384,0.8880674242973328,-0.3786032497882843,-0.2607601284980774,0.8766923546791077,-0.3786032497882843,-0.29676613211631775,0.8638581037521362,-0.3786032497882843,-0.33227819204330444,0.849586009979248,-0.3786032497882843,-0.36723724007606506,0.8338999152183533,-0.3786032497882843,-0.40158504247665405,0.8168258666992188,-0.3786032497882843,-0.4352644383907318,0.7983922362327576,-0.3786032497882843,-0.4682193696498871,0.7786298394203186,-0.3786032497882843,-0.5003949999809265,0.7575714588165283,-0.3786032497882843,-0.5317378044128418,0.7352521419525146,-0.3786032497882843,-0.5621955394744873,0.7117091417312622,-0.3786032497882843,-0.5917176008224487,0.6869814991950989,-0.3786032497882843,-0.6202547550201416,0.661110520362854,-0.3786032497882843,-0.6477596163749695,0.6341391205787659,-0.3786032497882843,-0.6741862893104553,0.6061123013496399,-0.3786032497882843,-0.6994908452033997,0.5770766139030457,-0.3786032497882843,-0.7236312031745911,0.5470805168151855,-0.3786032497882843,-0.7465671300888062,0.516173779964447,-0.3786032497882843,-0.7682604789733887,0.4844079911708832,-0.3786032497882843,-0.7886751294136047,0.45183590054512024,-0.3786032497882843,-0.8077771067619324,0.4185118079185486,-0.3786032497882843,-0.8255346417427063,0.3844911456108093,-0.3786032497882843,-0.8419181108474731,0.3498305082321167,-0.3786032497882843,-0.8569003343582153,0.31458762288093567,-0.3786032497882843,-0.870456337928772,0.2788211405277252,-0.3786032497882843,-0.8825635313987732,0.24259059131145477,-0.3786032497882843,-0.8932017683982849,0.20595626533031464,-0.3786032497882843,-0.9023533463478088,0.16897915303707123,-0.3786032497882843,-0.9100030660629272,0.13172079622745514,-0.3786032497882843,-0.916138231754303,0.09424319118261337,-0.3786032497882843,-0.9207485318183899,0.05660872906446457,-0.3786032497882843,-0.923826277256012,0.018880050629377365,-0.3786032497882843,-0.9253664612770081,-0.018880050629377365,-0.3786032497882843,-0.9253664612770081,-0.05660872906446457,-0.3786032497882843,-0.923826277256012,-0.09424319118261337,-0.3786032497882843,-0.9207485318183899,-0.13172079622745514,-0.3786032497882843,-0.916138231754303,-0.16897915303707123,-0.3786032497882843,-0.9100030660629272,-0.20595626533031464,-0.3786032497882843,-0.9023533463478088,-0.24259059131145477,-0.3786032497882843,-0.8932017683982849,-0.2788211405277252,-0.3786032497882843,-0.8825635313987732,-0.31458762288093567,-0.3786032497882843,-0.870456337928772,-0.3498305082321167,-0.3786032497882843,-0.8569003343582153,-0.3844911456108093,-0.3786032497882843,-0.8419181108474731,-0.4185118079185486,-0.3786032497882843,-0.8255346417427063,-0.45183590054512024,-0.3786032497882843,-0.8077771067619324,-0.4844079911708832,-0.3786032497882843,-0.7886751294136047,-0.516173779964447,-0.3786032497882843,-0.7682604789733887,-0.5470805168151855,-0.3786032497882843,-0.7465671300888062,-0.5770766139030457,-0.3786032497882843,-0.7236312031745911,-0.6061123013496399,-0.3786032497882843,-0.6994908452033997,-0.6341391205787659,-0.3786032497882843,-0.6741862893104553,-0.661110520362854,-0.3786032497882843,-0.6477596163749695,-0.6869814991950989,-0.3786032497882843,-0.6202547550201416,-0.7117091417312622,-0.3786032497882843,-0.5917176008224487,-0.7352521419525146,-0.3786032497882843,-0.5621955394744873,-0.7575714588165283,-0.3786032497882843,-0.5317378044128418,-0.7786298394203186,-0.3786032497882843,-0.5003949999809265,-0.7983922362327576,-0.3786032497882843,-0.4682193696498871,-0.8168258666992188,-0.3786032497882843,-0.4352644383907318,-0.8338999152183533,-0.3786032497882843,-0.40158504247665405,-0.849586009979248,-0.3786032497882843,-0.36723724007606506,-0.8638581037521362,-0.3786032497882843,-0.33227819204330444,-0.8766923546791077,-0.3786032497882843,-0.29676613211631775,-0.8880674242973328,-0.3786032497882843,-0.2607601284980774,-0.8979644179344177,-0.3786032497882843,-0.22432009875774384,-0.9063668251037598,-0.3786032497882843,-0.18750672042369843,-0.9132606983184814,-0.3786032497882843,-0.15038126707077026,-0.9186344742774963,-0.3786032497882843,-0.11300550401210785,-0.9224793314933777,-0.3786032497882843,-0.07544165849685669,-0.9247888326644897,-0.3786032497882843,-0.037752244621515274,-0.9255590438842773,-0.3786032497882843,-2.2669658928069073e-16,-0.896547257900238,-0.4429481327533722,0.0,-0.8958011269569397,-0.4429481327533722,0.03656889498233795,-0.8935640454292297,-0.4429481327533722,0.07307692617177963,-0.8898397088050842,-0.4429481327533722,0.10946332663297653,-0.8846343755722046,-0.4429481327533722,0.14566753804683685,-0.8779565691947937,-0.4429481327533722,0.18162928521633148,-0.8698175549507141,-0.4429481327533722,0.21728874742984772,-0.8602308034896851,-0.4429481327533722,0.2525865435600281,-0.8492122292518616,-0.4429481327533722,0.2874639332294464,-0.836780309677124,-0.4429481327533722,0.3218628764152527,-0.8229556083679199,-0.4429481327533722,0.35572612285614014,-0.8077611327171326,-0.4429481327533722,0.38899725675582886,-0.7912222743034363,-0.4429481327533722,0.4216209948062897,-0.7733665108680725,-0.4429481327533722,0.45354294776916504,-0.7542235255241394,-0.4429481327533722,0.48471003770828247,-0.7338252067565918,-0.4429481327533722,0.515070378780365,-0.7122055292129517,-0.4429481327533722,0.5445734262466431,-0.6894004940986633,-0.4429481327533722,0.5731700658798218,-0.6654479503631592,-0.4429481327533722,0.6008127927780151,-0.6403878927230835,-0.4429481327533722,0.627455472946167,-0.6142619252204895,-0.4429481327533722,0.6530538201332092,-0.5871135592460632,-0.4429481327533722,0.6775652170181274,-0.5589880347251892,-0.4429481327533722,0.7009488344192505,-0.5299321413040161,-0.4429481327533722,0.7231658697128296,-0.4999942481517792,-0.4429481327533722,0.7441792488098145,-0.46922412514686584,-0.4429481327533722,0.7639539837837219,-0.43767303228378296,-0.4429481327533722,0.7824572324752808,-0.40539348125457764,-0.4429481327533722,0.7996581196784973,-0.3724391758441925,-0.4429481327533722,0.8155280351638794,-0.3388650119304657,-0.4429481327533722,0.8300406336784363,-0.30472680926322937,-0.4429481327533722,0.8431717157363892,-0.27008143067359924,-0.4429481327533722,0.8548994064331055,-0.2349865436553955,-0.4429481327533722,0.8652042150497437,-0.19950053095817566,-0.4429481327533722,0.8740689158439636,-0.1636824756860733,-0.4429481327533722,0.8814789056777954,-0.12759198248386383,-0.4429481327533722,0.8874216675758362,-0.09128911793231964,-0.4429481327533722,0.8918874859809875,-0.05483431741595268,-0.4429481327533722,0.894868791103363,-0.018288252875208855,-0.4429481327533722,0.8963606953620911,0.018288252875208855,-0.4429481327533722,0.8963606953620911,0.05483431741595268,-0.4429481327533722,0.894868791103363,0.09128911793231964,-0.4429481327533722,0.8918874859809875,0.12759198248386383,-0.4429481327533722,0.8874216675758362,0.1636824756860733,-0.4429481327533722,0.8814789056777954,0.19950053095817566,-0.4429481327533722,0.8740689158439636,0.2349865436553955,-0.4429481327533722,0.8652042150497437,0.27008143067359924,-0.4429481327533722,0.8548994064331055,0.30472680926322937,-0.4429481327533722,0.8431717157363892,0.3388650119304657,-0.4429481327533722,0.8300406336784363,0.3724391758441925,-0.4429481327533722,0.8155280351638794,0.40539348125457764,-0.4429481327533722,0.7996581196784973,0.43767303228378296,-0.4429481327533722,0.7824572324752808,0.46922412514686584,-0.4429481327533722,0.7639539837837219,0.4999942481517792,-0.4429481327533722,0.7441792488098145,0.5299321413040161,-0.4429481327533722,0.7231658697128296,0.5589880347251892,-0.4429481327533722,0.7009488344192505,0.5871135592460632,-0.4429481327533722,0.6775652170181274,0.6142619252204895,-0.4429481327533722,0.6530538201332092,0.6403878927230835,-0.4429481327533722,0.627455472946167,0.6654479503631592,-0.4429481327533722,0.6008127927780151,0.6894004940986633,-0.4429481327533722,0.5731700658798218,0.7122055292129517,-0.4429481327533722,0.5445734262466431,0.7338252067565918,-0.4429481327533722,0.515070378780365,0.7542235255241394,-0.4429481327533722,0.48471003770828247,0.7733665108680725,-0.4429481327533722,0.45354294776916504,0.7912222743034363,-0.4429481327533722,0.4216209948062897,0.8077611327171326,-0.4429481327533722,0.38899725675582886,0.8229556083679199,-0.4429481327533722,0.35572612285614014,0.836780309677124,-0.4429481327533722,0.3218628764152527,0.8492122292518616,-0.4429481327533722,0.2874639332294464,0.8602308034896851,-0.4429481327533722,0.2525865435600281,0.8698175549507141,-0.4429481327533722,0.21728874742984772,0.8779565691947937,-0.4429481327533722,0.18162928521633148,0.8846343755722046,-0.4429481327533722,0.14566753804683685,0.8898397088050842,-0.4429481327533722,0.10946332663297653,0.8935640454292297,-0.4429481327533722,0.07307692617177963,0.8958011269569397,-0.4429481327533722,0.03656889498233795,0.896547257900238,-0.4429481327533722,1.097953686209729e-16,0.8958011269569397,-0.4429481327533722,-0.03656889498233795,0.8935640454292297,-0.4429481327533722,-0.07307692617177963,0.8898397088050842,-0.4429481327533722,-0.10946332663297653,0.8846343755722046,-0.4429481327533722,-0.14566753804683685,0.8779565691947937,-0.4429481327533722,-0.18162928521633148,0.8698175549507141,-0.4429481327533722,-0.21728874742984772,0.8602308034896851,-0.4429481327533722,-0.2525865435600281,0.8492122292518616,-0.4429481327533722,-0.2874639332294464,0.836780309677124,-0.4429481327533722,-0.3218628764152527,0.8229556083679199,-0.4429481327533722,-0.35572612285614014,0.8077611327171326,-0.4429481327533722,-0.38899725675582886,0.7912222743034363,-0.4429481327533722,-0.4216209948062897,0.7733665108680725,-0.4429481327533722,-0.45354294776916504,0.7542235255241394,-0.4429481327533722,-0.48471003770828247,0.7338252067565918,-0.4429481327533722,-0.515070378780365,0.7122055292129517,-0.4429481327533722,-0.5445734262466431,0.6894004940986633,-0.4429481327533722,-0.5731700658798218,0.6654479503631592,-0.4429481327533722,-0.6008127927780151,0.6403878927230835,-0.4429481327533722,-0.627455472946167,0.6142619252204895,-0.4429481327533722,-0.6530538201332092,0.5871135592460632,-0.4429481327533722,-0.6775652170181274,0.5589880347251892,-0.4429481327533722,-0.7009488344192505,0.5299321413040161,-0.4429481327533722,-0.7231658697128296,0.4999942481517792,-0.4429481327533722,-0.7441792488098145,0.46922412514686584,-0.4429481327533722,-0.7639539837837219,0.43767303228378296,-0.4429481327533722,-0.7824572324752808,0.40539348125457764,-0.4429481327533722,-0.7996581196784973,0.3724391758441925,-0.4429481327533722,-0.8155280351638794,0.3388650119304657,-0.4429481327533722,-0.8300406336784363,0.30472680926322937,-0.4429481327533722,-0.8431717157363892,0.27008143067359924,-0.4429481327533722,-0.8548994064331055,0.2349865436553955,-0.4429481327533722,-0.8652042150497437,0.19950053095817566,-0.4429481327533722,-0.8740689158439636,0.1636824756860733,-0.4429481327533722,-0.8814789056777954,0.12759198248386383,-0.4429481327533722,-0.8874216675758362,0.09128911793231964,-0.4429481327533722,-0.8918874859809875,0.05483431741595268,-0.4429481327533722,-0.894868791103363,0.018288252875208855,-0.4429481327533722,-0.8963606953620911,-0.018288252875208855,-0.4429481327533722,-0.8963606953620911,-0.05483431741595268,-0.4429481327533722,-0.894868791103363,-0.09128911793231964,-0.4429481327533722,-0.8918874859809875,-0.12759198248386383,-0.4429481327533722,-0.8874216675758362,-0.1636824756860733,-0.4429481327533722,-0.8814789056777954,-0.19950053095817566,-0.4429481327533722,-0.8740689158439636,-0.2349865436553955,-0.4429481327533722,-0.8652042150497437,-0.27008143067359924,-0.4429481327533722,-0.8548994064331055,-0.30472680926322937,-0.4429481327533722,-0.8431717157363892,-0.3388650119304657,-0.4429481327533722,-0.8300406336784363,-0.3724391758441925,-0.4429481327533722,-0.8155280351638794,-0.40539348125457764,-0.4429481327533722,-0.7996581196784973,-0.43767303228378296,-0.4429481327533722,-0.7824572324752808,-0.46922412514686584,-0.4429481327533722,-0.7639539837837219,-0.4999942481517792,-0.4429481327533722,-0.7441792488098145,-0.5299321413040161,-0.4429481327533722,-0.7231658697128296,-0.5589880347251892,-0.4429481327533722,-0.7009488344192505,-0.5871135592460632,-0.4429481327533722,-0.6775652170181274,-0.6142619252204895,-0.4429481327533722,-0.6530538201332092,-0.6403878927230835,-0.4429481327533722,-0.627455472946167,-0.6654479503631592,-0.4429481327533722,-0.6008127927780151,-0.6894004940986633,-0.4429481327533722,-0.5731700658798218,-0.7122055292129517,-0.4429481327533722,-0.5445734262466431,-0.7338252067565918,-0.4429481327533722,-0.515070378780365,-0.7542235255241394,-0.4429481327533722,-0.48471003770828247,-0.7733665108680725,-0.4429481327533722,-0.45354294776916504,-0.7912222743034363,-0.4429481327533722,-0.4216209948062897,-0.8077611327171326,-0.4429481327533722,-0.38899725675582886,-0.8229556083679199,-0.4429481327533722,-0.35572612285614014,-0.836780309677124,-0.4429481327533722,-0.3218628764152527,-0.8492122292518616,-0.4429481327533722,-0.2874639332294464,-0.8602308034896851,-0.4429481327533722,-0.2525865435600281,-0.8698175549507141,-0.4429481327533722,-0.21728874742984772,-0.8779565691947937,-0.4429481327533722,-0.18162928521633148,-0.8846343755722046,-0.4429481327533722,-0.14566753804683685,-0.8898397088050842,-0.4429481327533722,-0.10946332663297653,-0.8935640454292297,-0.4429481327533722,-0.07307692617177963,-0.8958011269569397,-0.4429481327533722,-0.03656889498233795,-0.896547257900238,-0.4429481327533722,-2.195907372419458e-16,-0.8630688786506653,-0.5050862431526184,0.0,-0.8623506426811218,-0.5050862431526184,0.03520336002111435,-0.8601970672607422,-0.5050862431526184,0.07034812867641449,-0.8566118478775024,-0.5050862431526184,0.10537580400705338,-0.8516008257865906,-0.5050862431526184,0.14022809267044067,-0.8451724052429199,-0.5050862431526184,0.17484699189662933,-0.83733731508255,-0.5050862431526184,0.20917487144470215,-0.8281084895133972,-0.5050862431526184,0.2431546002626419,-0.817501425743103,-0.5050862431526184,0.27672961354255676,-0.8055337071418762,-0.5050862431526184,0.309844046831131,-0.7922252416610718,-0.5050862431526184,0.3424427807331085,-0.7775982022285461,-0.5050862431526184,0.3744715452194214,-0.7616769075393677,-0.5050862431526184,0.4058770537376404,-0.7444878816604614,-0.5050862431526184,0.4366070032119751,-0.7260597348213196,-0.5050862431526184,0.46661025285720825,-0.7064231634140015,-0.5050862431526184,0.49583688378334045,-0.6856107711791992,-0.5050862431526184,0.5242382884025574,-0.6636572480201721,-0.5050862431526184,0.551767110824585,-0.6405991911888123,-0.5050862431526184,0.5783775448799133,-0.6164748668670654,-0.5050862431526184,0.6040253639221191,-0.591324508190155,-0.5050862431526184,0.6286678314208984,-0.5651898980140686,-0.5050862431526184,0.6522639393806458,-0.5381146669387817,-0.5050862431526184,0.6747744083404541,-0.5101436972618103,-0.5050862431526184,0.6961618065834045,-0.4813237190246582,-0.5050862431526184,0.7163904905319214,-0.45170262455940247,-0.5050862431526184,0.7354268431663513,-0.42132967710494995,-0.5050862431526184,0.753239095211029,-0.39025551080703735,-0.5050862431526184,0.769797682762146,-0.35853177309036255,-0.5050862431526184,0.7850750684738159,-0.3262113034725189,-0.5050862431526184,0.799045741558075,-0.29334786534309387,-0.5050862431526184,0.8116864562034607,-0.2599962055683136,-0.5050862431526184,0.8229762315750122,-0.2262118011713028,-0.5050862431526184,0.8328962326049805,-0.19205088913440704,-0.5050862431526184,0.8414299488067627,-0.15757033228874207,-0.5050862431526184,0.8485631942749023,-0.12282750755548477,-0.5050862431526184,0.8542840480804443,-0.08788024634122849,-0.5050862431526184,0.8585830926895142,-0.052786726504564285,-0.5050862431526184,0.861453115940094,-0.017605341970920563,-0.5050862431526184,0.862889289855957,0.017605341970920563,-0.5050862431526184,0.862889289855957,0.052786726504564285,-0.5050862431526184,0.861453115940094,0.08788024634122849,-0.5050862431526184,0.8585830926895142,0.12282750755548477,-0.5050862431526184,0.8542840480804443,0.15757033228874207,-0.5050862431526184,0.8485631942749023,0.19205088913440704,-0.5050862431526184,0.8414299488067627,0.2262118011713028,-0.5050862431526184,0.8328962326049805,0.2599962055683136,-0.5050862431526184,0.8229762315750122,0.29334786534309387,-0.5050862431526184,0.8116864562034607,0.3262113034725189,-0.5050862431526184,0.799045741558075,0.35853177309036255,-0.5050862431526184,0.7850750684738159,0.39025551080703735,-0.5050862431526184,0.769797682762146,0.42132967710494995,-0.5050862431526184,0.753239095211029,0.45170262455940247,-0.5050862431526184,0.7354268431663513,0.4813237190246582,-0.5050862431526184,0.7163904905319214,0.5101436972618103,-0.5050862431526184,0.6961618065834045,0.5381146669387817,-0.5050862431526184,0.6747744083404541,0.5651898980140686,-0.5050862431526184,0.6522639393806458,0.591324508190155,-0.5050862431526184,0.6286678314208984,0.6164748668670654,-0.5050862431526184,0.6040253639221191,0.6405991911888123,-0.5050862431526184,0.5783775448799133,0.6636572480201721,-0.5050862431526184,0.551767110824585,0.6856107711791992,-0.5050862431526184,0.5242382884025574,0.7064231634140015,-0.5050862431526184,0.49583688378334045,0.7260597348213196,-0.5050862431526184,0.46661025285720825,0.7444878816604614,-0.5050862431526184,0.4366070032119751,0.7616769075393677,-0.5050862431526184,0.4058770537376404,0.7775982022285461,-0.5050862431526184,0.3744715452194214,0.7922252416610718,-0.5050862431526184,0.3424427807331085,0.8055337071418762,-0.5050862431526184,0.309844046831131,0.817501425743103,-0.5050862431526184,0.27672961354255676,0.8281084895133972,-0.5050862431526184,0.2431546002626419,0.83733731508255,-0.5050862431526184,0.20917487144470215,0.8451724052429199,-0.5050862431526184,0.17484699189662933,0.8516008257865906,-0.5050862431526184,0.14022809267044067,0.8566118478775024,-0.5050862431526184,0.10537580400705338,0.8601970672607422,-0.5050862431526184,0.07034812867641449,0.8623506426811218,-0.5050862431526184,0.03520336002111435,0.8630688786506653,-0.5050862431526184,1.0569545122357627e-16,0.8623506426811218,-0.5050862431526184,-0.03520336002111435,0.8601970672607422,-0.5050862431526184,-0.07034812867641449,0.8566118478775024,-0.5050862431526184,-0.10537580400705338,0.8516008257865906,-0.5050862431526184,-0.14022809267044067,0.8451724052429199,-0.5050862431526184,-0.17484699189662933,0.83733731508255,-0.5050862431526184,-0.20917487144470215,0.8281084895133972,-0.5050862431526184,-0.2431546002626419,0.817501425743103,-0.5050862431526184,-0.27672961354255676,0.8055337071418762,-0.5050862431526184,-0.309844046831131,0.7922252416610718,-0.5050862431526184,-0.3424427807331085,0.7775982022285461,-0.5050862431526184,-0.3744715452194214,0.7616769075393677,-0.5050862431526184,-0.4058770537376404,0.7444878816604614,-0.5050862431526184,-0.4366070032119751,0.7260597348213196,-0.5050862431526184,-0.46661025285720825,0.7064231634140015,-0.5050862431526184,-0.49583688378334045,0.6856107711791992,-0.5050862431526184,-0.5242382884025574,0.6636572480201721,-0.5050862431526184,-0.551767110824585,0.6405991911888123,-0.5050862431526184,-0.5783775448799133,0.6164748668670654,-0.5050862431526184,-0.6040253639221191,0.591324508190155,-0.5050862431526184,-0.6286678314208984,0.5651898980140686,-0.5050862431526184,-0.6522639393806458,0.5381146669387817,-0.5050862431526184,-0.6747744083404541,0.5101436972618103,-0.5050862431526184,-0.6961618065834045,0.4813237190246582,-0.5050862431526184,-0.7163904905319214,0.45170262455940247,-0.5050862431526184,-0.7354268431663513,0.42132967710494995,-0.5050862431526184,-0.753239095211029,0.39025551080703735,-0.5050862431526184,-0.769797682762146,0.35853177309036255,-0.5050862431526184,-0.7850750684738159,0.3262113034725189,-0.5050862431526184,-0.799045741558075,0.29334786534309387,-0.5050862431526184,-0.8116864562034607,0.2599962055683136,-0.5050862431526184,-0.8229762315750122,0.2262118011713028,-0.5050862431526184,-0.8328962326049805,0.19205088913440704,-0.5050862431526184,-0.8414299488067627,0.15757033228874207,-0.5050862431526184,-0.8485631942749023,0.12282750755548477,-0.5050862431526184,-0.8542840480804443,0.08788024634122849,-0.5050862431526184,-0.8585830926895142,0.052786726504564285,-0.5050862431526184,-0.861453115940094,0.017605341970920563,-0.5050862431526184,-0.862889289855957,-0.017605341970920563,-0.5050862431526184,-0.862889289855957,-0.052786726504564285,-0.5050862431526184,-0.861453115940094,-0.08788024634122849,-0.5050862431526184,-0.8585830926895142,-0.12282750755548477,-0.5050862431526184,-0.8542840480804443,-0.15757033228874207,-0.5050862431526184,-0.8485631942749023,-0.19205088913440704,-0.5050862431526184,-0.8414299488067627,-0.2262118011713028,-0.5050862431526184,-0.8328962326049805,-0.2599962055683136,-0.5050862431526184,-0.8229762315750122,-0.29334786534309387,-0.5050862431526184,-0.8116864562034607,-0.3262113034725189,-0.5050862431526184,-0.799045741558075,-0.35853177309036255,-0.5050862431526184,-0.7850750684738159,-0.39025551080703735,-0.5050862431526184,-0.769797682762146,-0.42132967710494995,-0.5050862431526184,-0.753239095211029,-0.45170262455940247,-0.5050862431526184,-0.7354268431663513,-0.4813237190246582,-0.5050862431526184,-0.7163904905319214,-0.5101436972618103,-0.5050862431526184,-0.6961618065834045,-0.5381146669387817,-0.5050862431526184,-0.6747744083404541,-0.5651898980140686,-0.5050862431526184,-0.6522639393806458,-0.591324508190155,-0.5050862431526184,-0.6286678314208984,-0.6164748668670654,-0.5050862431526184,-0.6040253639221191,-0.6405991911888123,-0.5050862431526184,-0.5783775448799133,-0.6636572480201721,-0.5050862431526184,-0.551767110824585,-0.6856107711791992,-0.5050862431526184,-0.5242382884025574,-0.7064231634140015,-0.5050862431526184,-0.49583688378334045,-0.7260597348213196,-0.5050862431526184,-0.46661025285720825,-0.7444878816604614,-0.5050862431526184,-0.4366070032119751,-0.7616769075393677,-0.5050862431526184,-0.4058770537376404,-0.7775982022285461,-0.5050862431526184,-0.3744715452194214,-0.7922252416610718,-0.5050862431526184,-0.3424427807331085,-0.8055337071418762,-0.5050862431526184,-0.309844046831131,-0.817501425743103,-0.5050862431526184,-0.27672961354255676,-0.8281084895133972,-0.5050862431526184,-0.2431546002626419,-0.83733731508255,-0.5050862431526184,-0.20917487144470215,-0.8451724052429199,-0.5050862431526184,-0.17484699189662933,-0.8516008257865906,-0.5050862431526184,-0.14022809267044067,-0.8566118478775024,-0.5050862431526184,-0.10537580400705338,-0.8601970672607422,-0.5050862431526184,-0.07034812867641449,-0.8623506426811218,-0.5050862431526184,-0.03520336002111435,-0.8630688786506653,-0.5050862431526184,-2.1139090244715254e-16,-0.8252907395362854,-0.5647080540657043,0.0,-0.8246039152145386,-0.5647080540657043,0.03366244211792946,-0.8225446343421936,-0.5647080540657043,0.06726885586977005,-0.8191162943840027,-0.5647080540657043,0.10076330602169037,-0.814324676990509,-0.5647080540657043,0.13409005105495453,-0.808177649974823,-0.5647080540657043,0.16719360649585724,-0.800685465335846,-0.5647080540657043,0.20001889765262604,-0.7918606400489807,-0.5647080540657043,0.2325112670660019,-0.781717836856842,-0.5647080540657043,0.26461663842201233,-0.7702739834785461,-0.5647080540657043,0.2962816059589386,-0.7575480341911316,-0.5647080540657043,0.3274534046649933,-0.7435612678527832,-0.5647080540657043,0.3580802381038666,-0.7283368706703186,-0.5647080540657043,0.38811105489730835,-0.7119002342224121,-0.5647080540657043,0.41749587655067444,-0.6942787170410156,-0.5647080540657043,0.4461858570575714,-0.6755016446113586,-0.5647080540657043,0.4741331934928894,-0.6556003093719482,-0.5647080540657043,0.5012913942337036,-0.63460773229599,-0.5647080540657043,0.5276151895523071,-0.6125589609146118,-0.5647080540657043,0.5530608892440796,-0.5894905924797058,-0.5647080540657043,0.5775860548019409,-0.5654411315917969,-0.5647080540657043,0.60114985704422,-0.5404505133628845,-0.5647080540657043,0.6237131357192993,-0.514560341835022,-0.5647080540657043,0.6452382802963257,-0.4878137707710266,-0.5647080540657043,0.6656894683837891,-0.46025529503822327,-0.5647080540657043,0.6850327253341675,-0.4319307506084442,-0.5647080540657043,0.7032358050346375,-0.4028872847557068,-0.5647080540657043,0.7202684283256531,-0.3731732666492462,-0.5647080540657043,0.7361022233963013,-0.3428381681442261,-0.5647080540657043,0.7507108449935913,-0.31193241477012634,-0.5647080540657043,0.7640699744224548,-0.28050747513771057,-0.5647080540657043,0.7761574387550354,-0.24861568212509155,-0.5647080540657043,0.7869529724121094,-0.21631008386611938,-0.5647080540657043,0.7964387536048889,-0.18364445865154266,-0.5647080540657043,0.8045989871025085,-0.15067318081855774,-0.5647080540657043,0.8114199638366699,-0.11745111644268036,-0.5647080540657043,0.8168904781341553,-0.08403356373310089,-0.5647080540657043,0.8210012912750244,-0.05047615244984627,-0.5647080540657043,0.8237456679344177,-0.016834722831845284,-0.5647080540657043,0.8251190185546875,0.016834722831845284,-0.5647080540657043,0.8251190185546875,0.05047615244984627,-0.5647080540657043,0.8237456679344177,0.08403356373310089,-0.5647080540657043,0.8210012912750244,0.11745111644268036,-0.5647080540657043,0.8168904781341553,0.15067318081855774,-0.5647080540657043,0.8114199638366699,0.18364445865154266,-0.5647080540657043,0.8045989871025085,0.21631008386611938,-0.5647080540657043,0.7964387536048889,0.24861568212509155,-0.5647080540657043,0.7869529724121094,0.28050747513771057,-0.5647080540657043,0.7761574387550354,0.31193241477012634,-0.5647080540657043,0.7640699744224548,0.3428381681442261,-0.5647080540657043,0.7507108449935913,0.3731732666492462,-0.5647080540657043,0.7361022233963013,0.4028872847557068,-0.5647080540657043,0.7202684283256531,0.4319307506084442,-0.5647080540657043,0.7032358050346375,0.46025529503822327,-0.5647080540657043,0.6850327253341675,0.4878137707710266,-0.5647080540657043,0.6656894683837891,0.514560341835022,-0.5647080540657043,0.6452382802963257,0.5404505133628845,-0.5647080540657043,0.6237131357192993,0.5654411315917969,-0.5647080540657043,0.60114985704422,0.5894905924797058,-0.5647080540657043,0.5775860548019409,0.6125589609146118,-0.5647080540657043,0.5530608892440796,0.63460773229599,-0.5647080540657043,0.5276151895523071,0.6556003093719482,-0.5647080540657043,0.5012913942337036,0.6755016446113586,-0.5647080540657043,0.4741331934928894,0.6942787170410156,-0.5647080540657043,0.4461858570575714,0.7119002342224121,-0.5647080540657043,0.41749587655067444,0.7283368706703186,-0.5647080540657043,0.38811105489730835,0.7435612678527832,-0.5647080540657043,0.3580802381038666,0.7575480341911316,-0.5647080540657043,0.3274534046649933,0.7702739834785461,-0.5647080540657043,0.2962816059589386,0.781717836856842,-0.5647080540657043,0.26461663842201233,0.7918606400489807,-0.5647080540657043,0.2325112670660019,0.800685465335846,-0.5647080540657043,0.20001889765262604,0.808177649974823,-0.5647080540657043,0.16719360649585724,0.814324676990509,-0.5647080540657043,0.13409005105495453,0.8191162943840027,-0.5647080540657043,0.10076330602169037,0.8225446343421936,-0.5647080540657043,0.06726885586977005,0.8246039152145386,-0.5647080540657043,0.03366244211792946,0.8252907395362854,-0.5647080540657043,1.0106896388311818e-16,0.8246039152145386,-0.5647080540657043,-0.03366244211792946,0.8225446343421936,-0.5647080540657043,-0.06726885586977005,0.8191162943840027,-0.5647080540657043,-0.10076330602169037,0.814324676990509,-0.5647080540657043,-0.13409005105495453,0.808177649974823,-0.5647080540657043,-0.16719360649585724,0.800685465335846,-0.5647080540657043,-0.20001889765262604,0.7918606400489807,-0.5647080540657043,-0.2325112670660019,0.781717836856842,-0.5647080540657043,-0.26461663842201233,0.7702739834785461,-0.5647080540657043,-0.2962816059589386,0.7575480341911316,-0.5647080540657043,-0.3274534046649933,0.7435612678527832,-0.5647080540657043,-0.3580802381038666,0.7283368706703186,-0.5647080540657043,-0.38811105489730835,0.7119002342224121,-0.5647080540657043,-0.41749587655067444,0.6942787170410156,-0.5647080540657043,-0.4461858570575714,0.6755016446113586,-0.5647080540657043,-0.4741331934928894,0.6556003093719482,-0.5647080540657043,-0.5012913942337036,0.63460773229599,-0.5647080540657043,-0.5276151895523071,0.6125589609146118,-0.5647080540657043,-0.5530608892440796,0.5894905924797058,-0.5647080540657043,-0.5775860548019409,0.5654411315917969,-0.5647080540657043,-0.60114985704422,0.5404505133628845,-0.5647080540657043,-0.6237131357192993,0.514560341835022,-0.5647080540657043,-0.6452382802963257,0.4878137707710266,-0.5647080540657043,-0.6656894683837891,0.46025529503822327,-0.5647080540657043,-0.6850327253341675,0.4319307506084442,-0.5647080540657043,-0.7032358050346375,0.4028872847557068,-0.5647080540657043,-0.7202684283256531,0.3731732666492462,-0.5647080540657043,-0.7361022233963013,0.3428381681442261,-0.5647080540657043,-0.7507108449935913,0.31193241477012634,-0.5647080540657043,-0.7640699744224548,0.28050747513771057,-0.5647080540657043,-0.7761574387550354,0.24861568212509155,-0.5647080540657043,-0.7869529724121094,0.21631008386611938,-0.5647080540657043,-0.7964387536048889,0.18364445865154266,-0.5647080540657043,-0.8045989871025085,0.15067318081855774,-0.5647080540657043,-0.8114199638366699,0.11745111644268036,-0.5647080540657043,-0.8168904781341553,0.08403356373310089,-0.5647080540657043,-0.8210012912750244,0.05047615244984627,-0.5647080540657043,-0.8237456679344177,0.016834722831845284,-0.5647080540657043,-0.8251190185546875,-0.016834722831845284,-0.5647080540657043,-0.8251190185546875,-0.05047615244984627,-0.5647080540657043,-0.8237456679344177,-0.08403356373310089,-0.5647080540657043,-0.8210012912750244,-0.11745111644268036,-0.5647080540657043,-0.8168904781341553,-0.15067318081855774,-0.5647080540657043,-0.8114199638366699,-0.18364445865154266,-0.5647080540657043,-0.8045989871025085,-0.21631008386611938,-0.5647080540657043,-0.7964387536048889,-0.24861568212509155,-0.5647080540657043,-0.7869529724121094,-0.28050747513771057,-0.5647080540657043,-0.7761574387550354,-0.31193241477012634,-0.5647080540657043,-0.7640699744224548,-0.3428381681442261,-0.5647080540657043,-0.7507108449935913,-0.3731732666492462,-0.5647080540657043,-0.7361022233963013,-0.4028872847557068,-0.5647080540657043,-0.7202684283256531,-0.4319307506084442,-0.5647080540657043,-0.7032358050346375,-0.46025529503822327,-0.5647080540657043,-0.6850327253341675,-0.4878137707710266,-0.5647080540657043,-0.6656894683837891,-0.514560341835022,-0.5647080540657043,-0.6452382802963257,-0.5404505133628845,-0.5647080540657043,-0.6237131357192993,-0.5654411315917969,-0.5647080540657043,-0.60114985704422,-0.5894905924797058,-0.5647080540657043,-0.5775860548019409,-0.6125589609146118,-0.5647080540657043,-0.5530608892440796,-0.63460773229599,-0.5647080540657043,-0.5276151895523071,-0.6556003093719482,-0.5647080540657043,-0.5012913942337036,-0.6755016446113586,-0.5647080540657043,-0.4741331934928894,-0.6942787170410156,-0.5647080540657043,-0.4461858570575714,-0.7119002342224121,-0.5647080540657043,-0.41749587655067444,-0.7283368706703186,-0.5647080540657043,-0.38811105489730835,-0.7435612678527832,-0.5647080540657043,-0.3580802381038666,-0.7575480341911316,-0.5647080540657043,-0.3274534046649933,-0.7702739834785461,-0.5647080540657043,-0.2962816059589386,-0.781717836856842,-0.5647080540657043,-0.26461663842201233,-0.7918606400489807,-0.5647080540657043,-0.2325112670660019,-0.800685465335846,-0.5647080540657043,-0.20001889765262604,-0.808177649974823,-0.5647080540657043,-0.16719360649585724,-0.814324676990509,-0.5647080540657043,-0.13409005105495453,-0.8191162943840027,-0.5647080540657043,-0.10076330602169037,-0.8225446343421936,-0.5647080540657043,-0.06726885586977005,-0.8246039152145386,-0.5647080540657043,-0.03366244211792946,-0.8252907395362854,-0.5647080540657043,-2.0213792776623636e-16,-0.7834010124206543,-0.6215165853500366,0.0,-0.7827490568161011,-0.6215165853500366,0.03195381909608841,-0.7807943224906921,-0.6215165853500366,0.06385445594787598,-0.7775400280952454,-0.6215165853500366,0.09564881026744843,-0.7729915380477905,-0.6215165853500366,0.127283975481987,-0.7671565413475037,-0.6215165853500366,0.15870727598667145,-0.7600446939468384,-0.6215165853500366,0.18986643850803375,-0.7516677975654602,-0.6215165853500366,0.22070957720279694,-0.7420397996902466,-0.6215165853500366,0.2511853575706482,-0.7311767935752869,-0.6215165853500366,0.28124308586120605,-0.7190967798233032,-0.6215165853500366,0.3108327090740204,-0.7058199644088745,-0.6215165853500366,0.3399049639701843,-0.6913682818412781,-0.6215165853500366,0.36841148138046265,-0.6757659912109375,-0.6215165853500366,0.3963048458099365,-0.6590389013290405,-0.6215165853500366,0.42353856563568115,-0.641214907169342,-0.6215165853500366,0.4500673711299896,-0.6223236322402954,-0.6215165853500366,0.475847065448761,-0.6023966073989868,-0.6215165853500366,0.5008347630500793,-0.5814669728279114,-0.6215165853500366,0.5249888896942139,-0.5595695376396179,-0.6215165853500366,0.5482692122459412,-0.5367407202720642,-0.6215165853500366,0.5706369876861572,-0.5130185484886169,-0.6215165853500366,0.5920549631118774,-0.4884425401687622,-0.6215165853500366,0.6124876141548157,-0.46305355429649353,-0.6215165853500366,0.6319007277488708,-0.43689388036727905,-0.6215165853500366,0.650262176990509,-0.4100070297718048,-0.6215165853500366,0.6675413250923157,-0.38243773579597473,-0.6215165853500366,0.6837093830108643,-0.35423192381858826,-0.6215165853500366,0.6987395286560059,-0.3254365622997284,-0.6215165853500366,0.71260666847229,-0.2960995137691498,-0.6215165853500366,0.725287675857544,-0.26626962423324585,-0.6215165853500366,0.7367615699768066,-0.2359965741634369,-0.6215165853500366,0.7470092177391052,-0.2053307294845581,-0.6215165853500366,0.7560135126113892,-0.17432312667369843,-0.6215165853500366,0.7637595534324646,-0.14302538335323334,-0.6215165853500366,0.7702343463897705,-0.11148959398269653,-0.6215165853500366,0.7754271626472473,-0.07976823300123215,-0.6215165853500366,0.7793292999267578,-0.04791410639882088,-0.6215165853500366,0.781934380531311,-0.015980234369635582,-0.6215165853500366,0.7832379937171936,0.015980234369635582,-0.6215165853500366,0.7832379937171936,0.04791410639882088,-0.6215165853500366,0.781934380531311,0.07976823300123215,-0.6215165853500366,0.7793292999267578,0.11148959398269653,-0.6215165853500366,0.7754271626472473,0.14302538335323334,-0.6215165853500366,0.7702343463897705,0.17432312667369843,-0.6215165853500366,0.7637595534324646,0.2053307294845581,-0.6215165853500366,0.7560135126113892,0.2359965741634369,-0.6215165853500366,0.7470092177391052,0.26626962423324585,-0.6215165853500366,0.7367615699768066,0.2960995137691498,-0.6215165853500366,0.725287675857544,0.3254365622997284,-0.6215165853500366,0.71260666847229,0.35423192381858826,-0.6215165853500366,0.6987395286560059,0.38243773579597473,-0.6215165853500366,0.6837093830108643,0.4100070297718048,-0.6215165853500366,0.6675413250923157,0.43689388036727905,-0.6215165853500366,0.650262176990509,0.46305355429649353,-0.6215165853500366,0.6319007277488708,0.4884425401687622,-0.6215165853500366,0.6124876141548157,0.5130185484886169,-0.6215165853500366,0.5920549631118774,0.5367407202720642,-0.6215165853500366,0.5706369876861572,0.5595695376396179,-0.6215165853500366,0.5482692122459412,0.5814669728279114,-0.6215165853500366,0.5249888896942139,0.6023966073989868,-0.6215165853500366,0.5008347630500793,0.6223236322402954,-0.6215165853500366,0.475847065448761,0.641214907169342,-0.6215165853500366,0.4500673711299896,0.6590389013290405,-0.6215165853500366,0.42353856563568115,0.6757659912109375,-0.6215165853500366,0.3963048458099365,0.6913682818412781,-0.6215165853500366,0.36841148138046265,0.7058199644088745,-0.6215165853500366,0.3399049639701843,0.7190967798233032,-0.6215165853500366,0.3108327090740204,0.7311767935752869,-0.6215165853500366,0.28124308586120605,0.7420397996902466,-0.6215165853500366,0.2511853575706482,0.7516677975654602,-0.6215165853500366,0.22070957720279694,0.7600446939468384,-0.6215165853500366,0.18986643850803375,0.7671565413475037,-0.6215165853500366,0.15870727598667145,0.7729915380477905,-0.6215165853500366,0.127283975481987,0.7775400280952454,-0.6215165853500366,0.09564881026744843,0.7807943224906921,-0.6215165853500366,0.06385445594787598,0.7827490568161011,-0.6215165853500366,0.03195381909608841,0.7834010124206543,-0.6215165853500366,9.593895516018681e-17,0.7827490568161011,-0.6215165853500366,-0.03195381909608841,0.7807943224906921,-0.6215165853500366,-0.06385445594787598,0.7775400280952454,-0.6215165853500366,-0.09564881026744843,0.7729915380477905,-0.6215165853500366,-0.127283975481987,0.7671565413475037,-0.6215165853500366,-0.15870727598667145,0.7600446939468384,-0.6215165853500366,-0.18986643850803375,0.7516677975654602,-0.6215165853500366,-0.22070957720279694,0.7420397996902466,-0.6215165853500366,-0.2511853575706482,0.7311767935752869,-0.6215165853500366,-0.28124308586120605,0.7190967798233032,-0.6215165853500366,-0.3108327090740204,0.7058199644088745,-0.6215165853500366,-0.3399049639701843,0.6913682818412781,-0.6215165853500366,-0.36841148138046265,0.6757659912109375,-0.6215165853500366,-0.3963048458099365,0.6590389013290405,-0.6215165853500366,-0.42353856563568115,0.641214907169342,-0.6215165853500366,-0.4500673711299896,0.6223236322402954,-0.6215165853500366,-0.475847065448761,0.6023966073989868,-0.6215165853500366,-0.5008347630500793,0.5814669728279114,-0.6215165853500366,-0.5249888896942139,0.5595695376396179,-0.6215165853500366,-0.5482692122459412,0.5367407202720642,-0.6215165853500366,-0.5706369876861572,0.5130185484886169,-0.6215165853500366,-0.5920549631118774,0.4884425401687622,-0.6215165853500366,-0.6124876141548157,0.46305355429649353,-0.6215165853500366,-0.6319007277488708,0.43689388036727905,-0.6215165853500366,-0.650262176990509,0.4100070297718048,-0.6215165853500366,-0.6675413250923157,0.38243773579597473,-0.6215165853500366,-0.6837093830108643,0.35423192381858826,-0.6215165853500366,-0.6987395286560059,0.3254365622997284,-0.6215165853500366,-0.71260666847229,0.2960995137691498,-0.6215165853500366,-0.725287675857544,0.26626962423324585,-0.6215165853500366,-0.7367615699768066,0.2359965741634369,-0.6215165853500366,-0.7470092177391052,0.2053307294845581,-0.6215165853500366,-0.7560135126113892,0.17432312667369843,-0.6215165853500366,-0.7637595534324646,0.14302538335323334,-0.6215165853500366,-0.7702343463897705,0.11148959398269653,-0.6215165853500366,-0.7754271626472473,0.07976823300123215,-0.6215165853500366,-0.7793292999267578,0.04791410639882088,-0.6215165853500366,-0.781934380531311,0.015980234369635582,-0.6215165853500366,-0.7832379937171936,-0.015980234369635582,-0.6215165853500366,-0.7832379937171936,-0.04791410639882088,-0.6215165853500366,-0.781934380531311,-0.07976823300123215,-0.6215165853500366,-0.7793292999267578,-0.11148959398269653,-0.6215165853500366,-0.7754271626472473,-0.14302538335323334,-0.6215165853500366,-0.7702343463897705,-0.17432312667369843,-0.6215165853500366,-0.7637595534324646,-0.2053307294845581,-0.6215165853500366,-0.7560135126113892,-0.2359965741634369,-0.6215165853500366,-0.7470092177391052,-0.26626962423324585,-0.6215165853500366,-0.7367615699768066,-0.2960995137691498,-0.6215165853500366,-0.725287675857544,-0.3254365622997284,-0.6215165853500366,-0.71260666847229,-0.35423192381858826,-0.6215165853500366,-0.6987395286560059,-0.38243773579597473,-0.6215165853500366,-0.6837093830108643,-0.4100070297718048,-0.6215165853500366,-0.6675413250923157,-0.43689388036727905,-0.6215165853500366,-0.650262176990509,-0.46305355429649353,-0.6215165853500366,-0.6319007277488708,-0.4884425401687622,-0.6215165853500366,-0.6124876141548157,-0.5130185484886169,-0.6215165853500366,-0.5920549631118774,-0.5367407202720642,-0.6215165853500366,-0.5706369876861572,-0.5595695376396179,-0.6215165853500366,-0.5482692122459412,-0.5814669728279114,-0.6215165853500366,-0.5249888896942139,-0.6023966073989868,-0.6215165853500366,-0.5008347630500793,-0.6223236322402954,-0.6215165853500366,-0.475847065448761,-0.641214907169342,-0.6215165853500366,-0.4500673711299896,-0.6590389013290405,-0.6215165853500366,-0.42353856563568115,-0.6757659912109375,-0.6215165853500366,-0.3963048458099365,-0.6913682818412781,-0.6215165853500366,-0.36841148138046265,-0.7058199644088745,-0.6215165853500366,-0.3399049639701843,-0.7190967798233032,-0.6215165853500366,-0.3108327090740204,-0.7311767935752869,-0.6215165853500366,-0.28124308586120605,-0.7420397996902466,-0.6215165853500366,-0.2511853575706482,-0.7516677975654602,-0.6215165853500366,-0.22070957720279694,-0.7600446939468384,-0.6215165853500366,-0.18986643850803375,-0.7671565413475037,-0.6215165853500366,-0.15870727598667145,-0.7729915380477905,-0.6215165853500366,-0.127283975481987,-0.7775400280952454,-0.6215165853500366,-0.09564881026744843,-0.7807943224906921,-0.6215165853500366,-0.06385445594787598,-0.7827490568161011,-0.6215165853500366,-0.03195381909608841,-0.7834010124206543,-0.6215165853500366,-1.9187791032037363e-16,-0.7376084923744202,-0.6752286553382874,0.0,-0.7369946241378784,-0.6752286553382874,0.030086006969213486,-0.7351541519165039,-0.6752286553382874,0.060121938586235046,-0.7320900559425354,-0.6752286553382874,0.09005780518054962,-0.7278074622154236,-0.6752286553382874,0.11984377354383469,-0.7223135232925415,-0.6752286553382874,0.1494302749633789,-0.7156173586845398,-0.6752286553382874,0.1787680685520172,-0.7077301144599915,-0.6752286553382874,0.20780831575393677,-0.6986649632453918,-0.6752286553382874,0.23650269210338593,-0.6884369254112244,-0.6752286553382874,0.2648034393787384,-0.6770630478858948,-0.6752286553382874,0.29266342520713806,-0.6645622849464417,-0.6752286553382874,0.3200363218784332,-0.6509553790092468,-0.6752286553382874,0.3468765318393707,-0.6362650394439697,-0.6752286553382874,0.3731394112110138,-0.6205157041549683,-0.6752286553382874,0.3987812399864197,-0.6037335991859436,-0.6752286553382874,0.4237593412399292,-0.5859466195106506,-0.6752286553382874,0.44803211092948914,-0.5671843886375427,-0.6752286553382874,0.47155919671058655,-0.547478199005127,-0.6752286553382874,0.494301438331604,-0.5268607139587402,-0.6752286553382874,0.5162209272384644,-0.505366325378418,-0.6752286553382874,0.5372812151908875,-0.4830308258533478,-0.6752286553382874,0.5574472546577454,-0.45989134907722473,-0.6752286553382874,0.5766855478286743,-0.4359864592552185,-0.6752286553382874,0.5949639081954956,-0.4113558828830719,-0.6752286553382874,0.6122520565986633,-0.38604065775871277,-0.6752286553382874,0.6285211443901062,-0.3600828945636749,-0.6752286553382874,0.643744170665741,-0.3335258364677429,-0.6752286553382874,0.6578956842422485,-0.3064136207103729,-0.6752286553382874,0.6709522604942322,-0.2787914276123047,-0.6752286553382874,0.6828920841217041,-0.25070521235466003,-0.6752286553382874,0.6936952471733093,-0.22220173478126526,-0.6752286553382874,0.7033438682556152,-0.19332842528820038,-0.6752286553382874,0.7118218541145325,-0.16413332521915436,-0.6752286553382874,0.7191150784492493,-0.13466504216194153,-0.6752286553382874,0.7252113819122314,-0.10497263073921204,-0.6752286553382874,0.730100691318512,-0.07510549575090408,-0.6752286553382874,0.7337747812271118,-0.04511335864663124,-0.6752286553382874,0.7362275719642639,-0.015046133659780025,-0.6752286553382874,0.7374550104141235,0.015046133659780025,-0.6752286553382874,0.7374550104141235,0.04511335864663124,-0.6752286553382874,0.7362275719642639,0.07510549575090408,-0.6752286553382874,0.7337747812271118,0.10497263073921204,-0.6752286553382874,0.730100691318512,0.13466504216194153,-0.6752286553382874,0.7252113819122314,0.16413332521915436,-0.6752286553382874,0.7191150784492493,0.19332842528820038,-0.6752286553382874,0.7118218541145325,0.22220173478126526,-0.6752286553382874,0.7033438682556152,0.25070521235466003,-0.6752286553382874,0.6936952471733093,0.2787914276123047,-0.6752286553382874,0.6828920841217041,0.3064136207103729,-0.6752286553382874,0.6709522604942322,0.3335258364677429,-0.6752286553382874,0.6578956842422485,0.3600828945636749,-0.6752286553382874,0.643744170665741,0.38604065775871277,-0.6752286553382874,0.6285211443901062,0.4113558828830719,-0.6752286553382874,0.6122520565986633,0.4359864592552185,-0.6752286553382874,0.5949639081954956,0.45989134907722473,-0.6752286553382874,0.5766855478286743,0.4830308258533478,-0.6752286553382874,0.5574472546577454,0.505366325378418,-0.6752286553382874,0.5372812151908875,0.5268607139587402,-0.6752286553382874,0.5162209272384644,0.547478199005127,-0.6752286553382874,0.494301438331604,0.5671843886375427,-0.6752286553382874,0.47155919671058655,0.5859466195106506,-0.6752286553382874,0.44803211092948914,0.6037335991859436,-0.6752286553382874,0.4237593412399292,0.6205157041549683,-0.6752286553382874,0.3987812399864197,0.6362650394439697,-0.6752286553382874,0.3731394112110138,0.6509553790092468,-0.6752286553382874,0.3468765318393707,0.6645622849464417,-0.6752286553382874,0.3200363218784332,0.6770630478858948,-0.6752286553382874,0.29266342520713806,0.6884369254112244,-0.6752286553382874,0.2648034393787384,0.6986649632453918,-0.6752286553382874,0.23650269210338593,0.7077301144599915,-0.6752286553382874,0.20780831575393677,0.7156173586845398,-0.6752286553382874,0.1787680685520172,0.7223135232925415,-0.6752286553382874,0.1494302749633789,0.7278074622154236,-0.6752286553382874,0.11984377354383469,0.7320900559425354,-0.6752286553382874,0.09005780518054962,0.7351541519165039,-0.6752286553382874,0.060121938586235046,0.7369946241378784,-0.6752286553382874,0.030086006969213486,0.7376084923744202,-0.6752286553382874,9.03309816269876e-17,0.7369946241378784,-0.6752286553382874,-0.030086006969213486,0.7351541519165039,-0.6752286553382874,-0.060121938586235046,0.7320900559425354,-0.6752286553382874,-0.09005780518054962,0.7278074622154236,-0.6752286553382874,-0.11984377354383469,0.7223135232925415,-0.6752286553382874,-0.1494302749633789,0.7156173586845398,-0.6752286553382874,-0.1787680685520172,0.7077301144599915,-0.6752286553382874,-0.20780831575393677,0.6986649632453918,-0.6752286553382874,-0.23650269210338593,0.6884369254112244,-0.6752286553382874,-0.2648034393787384,0.6770630478858948,-0.6752286553382874,-0.29266342520713806,0.6645622849464417,-0.6752286553382874,-0.3200363218784332,0.6509553790092468,-0.6752286553382874,-0.3468765318393707,0.6362650394439697,-0.6752286553382874,-0.3731394112110138,0.6205157041549683,-0.6752286553382874,-0.3987812399864197,0.6037335991859436,-0.6752286553382874,-0.4237593412399292,0.5859466195106506,-0.6752286553382874,-0.44803211092948914,0.5671843886375427,-0.6752286553382874,-0.47155919671058655,0.547478199005127,-0.6752286553382874,-0.494301438331604,0.5268607139587402,-0.6752286553382874,-0.5162209272384644,0.505366325378418,-0.6752286553382874,-0.5372812151908875,0.4830308258533478,-0.6752286553382874,-0.5574472546577454,0.45989134907722473,-0.6752286553382874,-0.5766855478286743,0.4359864592552185,-0.6752286553382874,-0.5949639081954956,0.4113558828830719,-0.6752286553382874,-0.6122520565986633,0.38604065775871277,-0.6752286553382874,-0.6285211443901062,0.3600828945636749,-0.6752286553382874,-0.643744170665741,0.3335258364677429,-0.6752286553382874,-0.6578956842422485,0.3064136207103729,-0.6752286553382874,-0.6709522604942322,0.2787914276123047,-0.6752286553382874,-0.6828920841217041,0.25070521235466003,-0.6752286553382874,-0.6936952471733093,0.22220173478126526,-0.6752286553382874,-0.7033438682556152,0.19332842528820038,-0.6752286553382874,-0.7118218541145325,0.16413332521915436,-0.6752286553382874,-0.7191150784492493,0.13466504216194153,-0.6752286553382874,-0.7252113819122314,0.10497263073921204,-0.6752286553382874,-0.730100691318512,0.07510549575090408,-0.6752286553382874,-0.7337747812271118,0.04511335864663124,-0.6752286553382874,-0.7362275719642639,0.015046133659780025,-0.6752286553382874,-0.7374550104141235,-0.015046133659780025,-0.6752286553382874,-0.7374550104141235,-0.04511335864663124,-0.6752286553382874,-0.7362275719642639,-0.07510549575090408,-0.6752286553382874,-0.7337747812271118,-0.10497263073921204,-0.6752286553382874,-0.730100691318512,-0.13466504216194153,-0.6752286553382874,-0.7252113819122314,-0.16413332521915436,-0.6752286553382874,-0.7191150784492493,-0.19332842528820038,-0.6752286553382874,-0.7118218541145325,-0.22220173478126526,-0.6752286553382874,-0.7033438682556152,-0.25070521235466003,-0.6752286553382874,-0.6936952471733093,-0.2787914276123047,-0.6752286553382874,-0.6828920841217041,-0.3064136207103729,-0.6752286553382874,-0.6709522604942322,-0.3335258364677429,-0.6752286553382874,-0.6578956842422485,-0.3600828945636749,-0.6752286553382874,-0.643744170665741,-0.38604065775871277,-0.6752286553382874,-0.6285211443901062,-0.4113558828830719,-0.6752286553382874,-0.6122520565986633,-0.4359864592552185,-0.6752286553382874,-0.5949639081954956,-0.45989134907722473,-0.6752286553382874,-0.5766855478286743,-0.4830308258533478,-0.6752286553382874,-0.5574472546577454,-0.505366325378418,-0.6752286553382874,-0.5372812151908875,-0.5268607139587402,-0.6752286553382874,-0.5162209272384644,-0.547478199005127,-0.6752286553382874,-0.494301438331604,-0.5671843886375427,-0.6752286553382874,-0.47155919671058655,-0.5859466195106506,-0.6752286553382874,-0.44803211092948914,-0.6037335991859436,-0.6752286553382874,-0.4237593412399292,-0.6205157041549683,-0.6752286553382874,-0.3987812399864197,-0.6362650394439697,-0.6752286553382874,-0.3731394112110138,-0.6509553790092468,-0.6752286553382874,-0.3468765318393707,-0.6645622849464417,-0.6752286553382874,-0.3200363218784332,-0.6770630478858948,-0.6752286553382874,-0.29266342520713806,-0.6884369254112244,-0.6752286553382874,-0.2648034393787384,-0.6986649632453918,-0.6752286553382874,-0.23650269210338593,-0.7077301144599915,-0.6752286553382874,-0.20780831575393677,-0.7156173586845398,-0.6752286553382874,-0.1787680685520172,-0.7223135232925415,-0.6752286553382874,-0.1494302749633789,-0.7278074622154236,-0.6752286553382874,-0.11984377354383469,-0.7320900559425354,-0.6752286553382874,-0.09005780518054962,-0.7351541519165039,-0.6752286553382874,-0.060121938586235046,-0.7369946241378784,-0.6752286553382874,-0.030086006969213486,-0.7376084923744202,-0.6752286553382874,-1.806619632539752e-16,-0.6881411671638489,-0.7255768179893494,0.0,-0.6875684857368469,-0.7255768179893494,0.028068305924534798,-0.6858514547348022,-0.7255768179893494,0.056089892983436584,-0.6829928755760193,-0.7255768179893494,0.08401812613010406,-0.6789974570274353,-0.7255768179893494,0.11180651932954788,-0.6738719940185547,-0.7255768179893494,0.13940882682800293,-0.6676248908042908,-0.7255768179893494,0.16677908599376678,-0.660266637802124,-0.7255768179893494,0.19387176632881165,-0.6518093943595886,-0.7255768179893494,0.22064177691936493,-0.6422672867774963,-0.7255768179893494,0.24704453349113464,-0.6316561698913574,-0.7255768179893494,0.2730361223220825,-0.6199937462806702,-0.7255768179893494,0.29857325553894043,-0.6072994470596313,-0.7255768179893494,0.3236134648323059,-0.5935943126678467,-0.7255768179893494,0.3481150269508362,-0.5789011716842651,-0.7255768179893494,0.37203720211982727,-0.5632445812225342,-0.7255768179893494,0.3953401446342468,-0.5466504693031311,-0.7255768179893494,0.41798511147499084,-0.5291464924812317,-0.7255768179893494,0.4399343430995941,-0.5107618570327759,-0.7255768179893494,0.4611513912677765,-0.49152708053588867,-0.7255768179893494,0.4816008508205414,-0.47147423028945923,-0.7255768179893494,0.5012487769126892,-0.450636625289917,-0.7255768179893494,0.5200623869895935,-0.429049015045166,-0.7255768179893494,0.5380104184150696,-0.4067472517490387,-0.7255768179893494,0.555063009262085,-0.3837685286998749,-0.7255768179893494,0.5711917281150818,-0.3601510524749756,-0.7255768179893494,0.5863697528839111,-0.3359341323375702,-0.7255768179893494,0.6005718111991882,-0.31115812063217163,-0.7255768179893494,0.613774299621582,-0.2858641743659973,-0.7255768179893494,0.6259552240371704,-0.26009446382522583,-0.7255768179893494,0.6370943188667297,-0.2338918298482895,-0.7255768179893494,0.6471729874610901,-0.20729990303516388,-0.7255768179893494,0.6561745405197144,-0.18036295473575592,-0.7255768179893494,0.6640839576721191,-0.1531258225440979,-0.7255768179893494,0.6708880066871643,-0.12563380599021912,-0.7255768179893494,0.6765754818916321,-0.09793269634246826,-0.7255768179893494,0.6811369061470032,-0.0700685903429985,-0.7255768179893494,0.6845645904541016,-0.0420878566801548,-0.7255768179893494,0.6868528723716736,-0.014037073589861393,-0.7255768179893494,0.6879979968070984,0.014037073589861393,-0.7255768179893494,0.6879979968070984,0.0420878566801548,-0.7255768179893494,0.6868528723716736,0.0700685903429985,-0.7255768179893494,0.6845645904541016,0.09793269634246826,-0.7255768179893494,0.6811369061470032,0.12563380599021912,-0.7255768179893494,0.6765754818916321,0.1531258225440979,-0.7255768179893494,0.6708880066871643,0.18036295473575592,-0.7255768179893494,0.6640839576721191,0.20729990303516388,-0.7255768179893494,0.6561745405197144,0.2338918298482895,-0.7255768179893494,0.6471729874610901,0.26009446382522583,-0.7255768179893494,0.6370943188667297,0.2858641743659973,-0.7255768179893494,0.6259552240371704,0.31115812063217163,-0.7255768179893494,0.613774299621582,0.3359341323375702,-0.7255768179893494,0.6005718111991882,0.3601510524749756,-0.7255768179893494,0.5863697528839111,0.3837685286998749,-0.7255768179893494,0.5711917281150818,0.4067472517490387,-0.7255768179893494,0.555063009262085,0.429049015045166,-0.7255768179893494,0.5380104184150696,0.450636625289917,-0.7255768179893494,0.5200623869895935,0.47147423028945923,-0.7255768179893494,0.5012487769126892,0.49152708053588867,-0.7255768179893494,0.4816008508205414,0.5107618570327759,-0.7255768179893494,0.4611513912677765,0.5291464924812317,-0.7255768179893494,0.4399343430995941,0.5466504693031311,-0.7255768179893494,0.41798511147499084,0.5632445812225342,-0.7255768179893494,0.3953401446342468,0.5789011716842651,-0.7255768179893494,0.37203720211982727,0.5935943126678467,-0.7255768179893494,0.3481150269508362,0.6072994470596313,-0.7255768179893494,0.3236134648323059,0.6199937462806702,-0.7255768179893494,0.29857325553894043,0.6316561698913574,-0.7255768179893494,0.2730361223220825,0.6422672867774963,-0.7255768179893494,0.24704453349113464,0.6518093943595886,-0.7255768179893494,0.22064177691936493,0.660266637802124,-0.7255768179893494,0.19387176632881165,0.6676248908042908,-0.7255768179893494,0.16677908599376678,0.6738719940185547,-0.7255768179893494,0.13940882682800293,0.6789974570274353,-0.7255768179893494,0.11180651932954788,0.6829928755760193,-0.7255768179893494,0.08401812613010406,0.6858514547348022,-0.7255768179893494,0.056089892983436584,0.6875684857368469,-0.7255768179893494,0.028068305924534798,0.6881411671638489,-0.7255768179893494,8.427298875333505e-17,0.6875684857368469,-0.7255768179893494,-0.028068305924534798,0.6858514547348022,-0.7255768179893494,-0.056089892983436584,0.6829928755760193,-0.7255768179893494,-0.08401812613010406,0.6789974570274353,-0.7255768179893494,-0.11180651932954788,0.6738719940185547,-0.7255768179893494,-0.13940882682800293,0.6676248908042908,-0.7255768179893494,-0.16677908599376678,0.660266637802124,-0.7255768179893494,-0.19387176632881165,0.6518093943595886,-0.7255768179893494,-0.22064177691936493,0.6422672867774963,-0.7255768179893494,-0.24704453349113464,0.6316561698913574,-0.7255768179893494,-0.2730361223220825,0.6199937462806702,-0.7255768179893494,-0.29857325553894043,0.6072994470596313,-0.7255768179893494,-0.3236134648323059,0.5935943126678467,-0.7255768179893494,-0.3481150269508362,0.5789011716842651,-0.7255768179893494,-0.37203720211982727,0.5632445812225342,-0.7255768179893494,-0.3953401446342468,0.5466504693031311,-0.7255768179893494,-0.41798511147499084,0.5291464924812317,-0.7255768179893494,-0.4399343430995941,0.5107618570327759,-0.7255768179893494,-0.4611513912677765,0.49152708053588867,-0.7255768179893494,-0.4816008508205414,0.47147423028945923,-0.7255768179893494,-0.5012487769126892,0.450636625289917,-0.7255768179893494,-0.5200623869895935,0.429049015045166,-0.7255768179893494,-0.5380104184150696,0.4067472517490387,-0.7255768179893494,-0.555063009262085,0.3837685286998749,-0.7255768179893494,-0.5711917281150818,0.3601510524749756,-0.7255768179893494,-0.5863697528839111,0.3359341323375702,-0.7255768179893494,-0.6005718111991882,0.31115812063217163,-0.7255768179893494,-0.613774299621582,0.2858641743659973,-0.7255768179893494,-0.6259552240371704,0.26009446382522583,-0.7255768179893494,-0.6370943188667297,0.2338918298482895,-0.7255768179893494,-0.6471729874610901,0.20729990303516388,-0.7255768179893494,-0.6561745405197144,0.18036295473575592,-0.7255768179893494,-0.6640839576721191,0.1531258225440979,-0.7255768179893494,-0.6708880066871643,0.12563380599021912,-0.7255768179893494,-0.6765754818916321,0.09793269634246826,-0.7255768179893494,-0.6811369061470032,0.0700685903429985,-0.7255768179893494,-0.6845645904541016,0.0420878566801548,-0.7255768179893494,-0.6868528723716736,0.014037073589861393,-0.7255768179893494,-0.6879979968070984,-0.014037073589861393,-0.7255768179893494,-0.6879979968070984,-0.0420878566801548,-0.7255768179893494,-0.6868528723716736,-0.0700685903429985,-0.7255768179893494,-0.6845645904541016,-0.09793269634246826,-0.7255768179893494,-0.6811369061470032,-0.12563380599021912,-0.7255768179893494,-0.6765754818916321,-0.1531258225440979,-0.7255768179893494,-0.6708880066871643,-0.18036295473575592,-0.7255768179893494,-0.6640839576721191,-0.20729990303516388,-0.7255768179893494,-0.6561745405197144,-0.2338918298482895,-0.7255768179893494,-0.6471729874610901,-0.26009446382522583,-0.7255768179893494,-0.6370943188667297,-0.2858641743659973,-0.7255768179893494,-0.6259552240371704,-0.31115812063217163,-0.7255768179893494,-0.613774299621582,-0.3359341323375702,-0.7255768179893494,-0.6005718111991882,-0.3601510524749756,-0.7255768179893494,-0.5863697528839111,-0.3837685286998749,-0.7255768179893494,-0.5711917281150818,-0.4067472517490387,-0.7255768179893494,-0.555063009262085,-0.429049015045166,-0.7255768179893494,-0.5380104184150696,-0.450636625289917,-0.7255768179893494,-0.5200623869895935,-0.47147423028945923,-0.7255768179893494,-0.5012487769126892,-0.49152708053588867,-0.7255768179893494,-0.4816008508205414,-0.5107618570327759,-0.7255768179893494,-0.4611513912677765,-0.5291464924812317,-0.7255768179893494,-0.4399343430995941,-0.5466504693031311,-0.7255768179893494,-0.41798511147499084,-0.5632445812225342,-0.7255768179893494,-0.3953401446342468,-0.5789011716842651,-0.7255768179893494,-0.37203720211982727,-0.5935943126678467,-0.7255768179893494,-0.3481150269508362,-0.6072994470596313,-0.7255768179893494,-0.3236134648323059,-0.6199937462806702,-0.7255768179893494,-0.29857325553894043,-0.6316561698913574,-0.7255768179893494,-0.2730361223220825,-0.6422672867774963,-0.7255768179893494,-0.24704453349113464,-0.6518093943595886,-0.7255768179893494,-0.22064177691936493,-0.660266637802124,-0.7255768179893494,-0.19387176632881165,-0.6676248908042908,-0.7255768179893494,-0.16677908599376678,-0.6738719940185547,-0.7255768179893494,-0.13940882682800293,-0.6789974570274353,-0.7255768179893494,-0.11180651932954788,-0.6829928755760193,-0.7255768179893494,-0.08401812613010406,-0.6858514547348022,-0.7255768179893494,-0.056089892983436584,-0.6875684857368469,-0.7255768179893494,-0.028068305924534798,-0.6881411671638489,-0.7255768179893494,-1.685459775066701e-16,-0.6352456212043762,-0.772310197353363,0.0,-0.6347169280052185,-0.772310197353363,0.025910770520567894,-0.6331318616867065,-0.772310197353363,0.051778413355350494,-0.6304929852485657,-0.772310197353363,0.07755987346172333,-0.626804769039154,-0.772310197353363,0.10321225225925446,-0.622073233127594,-0.772310197353363,0.1286928355693817,-0.6163063645362854,-0.772310197353363,0.1539592295885086,-0.6095137000083923,-0.772310197353363,0.1789693683385849,-0.6017065644264221,-0.772310197353363,0.20368163287639618,-0.592897891998291,-0.772310197353363,0.22805488109588623,-0.5831024646759033,-0.772310197353363,0.2520485520362854,-0.5723364949226379,-0.772310197353363,0.27562272548675537,-0.5606179237365723,-0.772310197353363,0.29873815178871155,-0.5479663014411926,-0.772310197353363,0.3213563561439514,-0.53440260887146,-0.772310197353363,0.3434396982192993,-0.5199494361877441,-0.772310197353363,0.36495140194892883,-0.5046309232711792,-0.772310197353363,0.38585570454597473,-0.48847243189811707,-0.772310197353363,0.4061177670955658,-0.471500962972641,-0.772310197353363,0.42570391297340393,-0.45374470949172974,-0.772310197353363,0.4445814788341522,-0.4352332651615143,-0.772310197353363,0.4627191126346588,-0.41599738597869873,-0.772310197353363,0.4800865948200226,-0.39606913924217224,-0.772310197353363,0.49665501713752747,-0.3754816949367523,-0.772310197353363,0.5123968124389648,-0.35426926612854004,-0.772310197353363,0.5272857546806335,-0.3324671983718872,-0.772310197353363,0.5412970781326294,-0.31011179089546204,-0.772310197353363,0.5544074773788452,-0.287240207195282,-0.772310197353363,0.5665951371192932,-0.2638905644416809,-0.772310197353363,0.577839732170105,-0.2401016801595688,-0.772310197353363,0.588122546672821,-0.21591317653656006,-0.772310197353363,0.5974265336990356,-0.1913653165102005,-0.772310197353363,0.6057361364364624,-0.16649894416332245,-0.772310197353363,0.6130375862121582,-0.14135544002056122,-0.772310197353363,0.6193186640739441,-0.11597667634487152,-0.772310197353363,0.6245689392089844,-0.09040487557649612,-0.772310197353363,0.6287797093391418,-0.06468260288238525,-0.772310197353363,0.631943941116333,-0.03885267674922943,-0.772310197353363,0.6340563297271729,-0.012958081439137459,-0.772310197353363,0.6351134181022644,0.012958081439137459,-0.772310197353363,0.6351134181022644,0.03885267674922943,-0.772310197353363,0.6340563297271729,0.06468260288238525,-0.772310197353363,0.631943941116333,0.09040487557649612,-0.772310197353363,0.6287797093391418,0.11597667634487152,-0.772310197353363,0.6245689392089844,0.14135544002056122,-0.772310197353363,0.6193186640739441,0.16649894416332245,-0.772310197353363,0.6130375862121582,0.1913653165102005,-0.772310197353363,0.6057361364364624,0.21591317653656006,-0.772310197353363,0.5974265336990356,0.2401016801595688,-0.772310197353363,0.588122546672821,0.2638905644416809,-0.772310197353363,0.577839732170105,0.287240207195282,-0.772310197353363,0.5665951371192932,0.31011179089546204,-0.772310197353363,0.5544074773788452,0.3324671983718872,-0.772310197353363,0.5412970781326294,0.35426926612854004,-0.772310197353363,0.5272857546806335,0.3754816949367523,-0.772310197353363,0.5123968124389648,0.39606913924217224,-0.772310197353363,0.49665501713752747,0.41599738597869873,-0.772310197353363,0.4800865948200226,0.4352332651615143,-0.772310197353363,0.4627191126346588,0.45374470949172974,-0.772310197353363,0.4445814788341522,0.471500962972641,-0.772310197353363,0.42570391297340393,0.48847243189811707,-0.772310197353363,0.4061177670955658,0.5046309232711792,-0.772310197353363,0.38585570454597473,0.5199494361877441,-0.772310197353363,0.36495140194892883,0.53440260887146,-0.772310197353363,0.3434396982192993,0.5479663014411926,-0.772310197353363,0.3213563561439514,0.5606179237365723,-0.772310197353363,0.29873815178871155,0.5723364949226379,-0.772310197353363,0.27562272548675537,0.5831024646759033,-0.772310197353363,0.2520485520362854,0.592897891998291,-0.772310197353363,0.22805488109588623,0.6017065644264221,-0.772310197353363,0.20368163287639618,0.6095137000083923,-0.772310197353363,0.1789693683385849,0.6163063645362854,-0.772310197353363,0.1539592295885086,0.622073233127594,-0.772310197353363,0.1286928355693817,0.626804769039154,-0.772310197353363,0.10321225225925446,0.6304929852485657,-0.772310197353363,0.07755987346172333,0.6331318616867065,-0.772310197353363,0.051778413355350494,0.6347169280052185,-0.772310197353363,0.025910770520567894,0.6352456212043762,-0.772310197353363,7.779514547053018e-17,0.6347169280052185,-0.772310197353363,-0.025910770520567894,0.6331318616867065,-0.772310197353363,-0.051778413355350494,0.6304929852485657,-0.772310197353363,-0.07755987346172333,0.626804769039154,-0.772310197353363,-0.10321225225925446,0.622073233127594,-0.772310197353363,-0.1286928355693817,0.6163063645362854,-0.772310197353363,-0.1539592295885086,0.6095137000083923,-0.772310197353363,-0.1789693683385849,0.6017065644264221,-0.772310197353363,-0.20368163287639618,0.592897891998291,-0.772310197353363,-0.22805488109588623,0.5831024646759033,-0.772310197353363,-0.2520485520362854,0.5723364949226379,-0.772310197353363,-0.27562272548675537,0.5606179237365723,-0.772310197353363,-0.29873815178871155,0.5479663014411926,-0.772310197353363,-0.3213563561439514,0.53440260887146,-0.772310197353363,-0.3434396982192993,0.5199494361877441,-0.772310197353363,-0.36495140194892883,0.5046309232711792,-0.772310197353363,-0.38585570454597473,0.48847243189811707,-0.772310197353363,-0.4061177670955658,0.471500962972641,-0.772310197353363,-0.42570391297340393,0.45374470949172974,-0.772310197353363,-0.4445814788341522,0.4352332651615143,-0.772310197353363,-0.4627191126346588,0.41599738597869873,-0.772310197353363,-0.4800865948200226,0.39606913924217224,-0.772310197353363,-0.49665501713752747,0.3754816949367523,-0.772310197353363,-0.5123968124389648,0.35426926612854004,-0.772310197353363,-0.5272857546806335,0.3324671983718872,-0.772310197353363,-0.5412970781326294,0.31011179089546204,-0.772310197353363,-0.5544074773788452,0.287240207195282,-0.772310197353363,-0.5665951371192932,0.2638905644416809,-0.772310197353363,-0.577839732170105,0.2401016801595688,-0.772310197353363,-0.588122546672821,0.21591317653656006,-0.772310197353363,-0.5974265336990356,0.1913653165102005,-0.772310197353363,-0.6057361364364624,0.16649894416332245,-0.772310197353363,-0.6130375862121582,0.14135544002056122,-0.772310197353363,-0.6193186640739441,0.11597667634487152,-0.772310197353363,-0.6245689392089844,0.09040487557649612,-0.772310197353363,-0.6287797093391418,0.06468260288238525,-0.772310197353363,-0.631943941116333,0.03885267674922943,-0.772310197353363,-0.6340563297271729,0.012958081439137459,-0.772310197353363,-0.6351134181022644,-0.012958081439137459,-0.772310197353363,-0.6351134181022644,-0.03885267674922943,-0.772310197353363,-0.6340563297271729,-0.06468260288238525,-0.772310197353363,-0.631943941116333,-0.09040487557649612,-0.772310197353363,-0.6287797093391418,-0.11597667634487152,-0.772310197353363,-0.6245689392089844,-0.14135544002056122,-0.772310197353363,-0.6193186640739441,-0.16649894416332245,-0.772310197353363,-0.6130375862121582,-0.1913653165102005,-0.772310197353363,-0.6057361364364624,-0.21591317653656006,-0.772310197353363,-0.5974265336990356,-0.2401016801595688,-0.772310197353363,-0.588122546672821,-0.2638905644416809,-0.772310197353363,-0.577839732170105,-0.287240207195282,-0.772310197353363,-0.5665951371192932,-0.31011179089546204,-0.772310197353363,-0.5544074773788452,-0.3324671983718872,-0.772310197353363,-0.5412970781326294,-0.35426926612854004,-0.772310197353363,-0.5272857546806335,-0.3754816949367523,-0.772310197353363,-0.5123968124389648,-0.39606913924217224,-0.772310197353363,-0.49665501713752747,-0.41599738597869873,-0.772310197353363,-0.4800865948200226,-0.4352332651615143,-0.772310197353363,-0.4627191126346588,-0.45374470949172974,-0.772310197353363,-0.4445814788341522,-0.471500962972641,-0.772310197353363,-0.42570391297340393,-0.48847243189811707,-0.772310197353363,-0.4061177670955658,-0.5046309232711792,-0.772310197353363,-0.38585570454597473,-0.5199494361877441,-0.772310197353363,-0.36495140194892883,-0.53440260887146,-0.772310197353363,-0.3434396982192993,-0.5479663014411926,-0.772310197353363,-0.3213563561439514,-0.5606179237365723,-0.772310197353363,-0.29873815178871155,-0.5723364949226379,-0.772310197353363,-0.27562272548675537,-0.5831024646759033,-0.772310197353363,-0.2520485520362854,-0.592897891998291,-0.772310197353363,-0.22805488109588623,-0.6017065644264221,-0.772310197353363,-0.20368163287639618,-0.6095137000083923,-0.772310197353363,-0.1789693683385849,-0.6163063645362854,-0.772310197353363,-0.1539592295885086,-0.622073233127594,-0.772310197353363,-0.1286928355693817,-0.626804769039154,-0.772310197353363,-0.10321225225925446,-0.6304929852485657,-0.772310197353363,-0.07755987346172333,-0.6331318616867065,-0.772310197353363,-0.051778413355350494,-0.6347169280052185,-0.772310197353363,-0.025910770520567894,-0.6352456212043762,-0.772310197353363,-1.5559029094106035e-16,-0.5791852474212646,-0.8151959776878357,0.0,-0.5787032842636108,-0.8151959776878357,0.023624148219823837,-0.5772580504417419,-0.8151959776878357,0.04720897600054741,-0.5748521089553833,-0.8151959776878357,0.07071522623300552,-0.5714893341064453,-0.8151959776878357,0.09410378336906433,-0.5671753883361816,-0.8151959776878357,0.1173357143998146,-0.5619174242019653,-0.8151959776878357,0.1403723508119583,-0.5557242035865784,-0.8151959776878357,0.16317534446716309,-0.5486060380935669,-0.8151959776878357,0.1857067495584488,-0.5405747294425964,-0.8151959776878357,0.2079290747642517,-0.5316437482833862,-0.8151959776878357,0.22980530560016632,-0.5218278765678406,-0.8151959776878357,0.25129905343055725,-0.5111435055732727,-0.8151959776878357,0.27237457036972046,-0.4996083378791809,-0.8151959776878357,0.29299670457839966,-0.48724165558815,-0.8151959776878357,0.313131183385849,-0.4740639925003052,-0.8151959776878357,0.3327445089817047,-0.4600972831249237,-0.8151959776878357,0.3518039882183075,-0.4453648030757904,-0.8151959776878357,0.37027794122695923,-0.42989104986190796,-0.8151959776878357,0.3881355822086334,-0.4137018024921417,-0.8151959776878357,0.40534722805023193,-0.3968239724636078,-0.8151959776878357,0.4218842089176178,-0.37928569316864014,-0.8151959776878357,0.4377190172672272,-0.36111611127853394,-0.8151959776878357,0.45282527804374695,-0.3423454761505127,-0.8151959776878357,0.4671778380870819,-0.3230050504207611,-0.8151959776878357,0.4807528555393219,-0.3031270205974579,-0.8151959776878357,0.49352768063545227,-0.28274446725845337,-0.8151959776878357,0.5054810643196106,-0.26189130544662476,-0.8151959776878357,0.5165931582450867,-0.2406022548675537,-0.8151959776878357,0.5268454551696777,-0.218912735581398,-0.8151959776878357,0.5362208485603333,-0.19685886800289154,-0.8151959776878357,0.5447037220001221,-0.17447735369205475,-0.8151959776878357,0.5522800087928772,-0.15180543065071106,-0.8151959776878357,0.5589370727539062,-0.1288808435201645,-0.8151959776878357,0.5646638870239258,-0.10574174672365189,-0.8151959776878357,0.569450855255127,-0.08242665976285934,-0.8151959776878357,0.5732899904251099,-0.05897437408566475,-0.8151959776878357,0.5761749744415283,-0.03542393073439598,-0.8151959776878357,0.5781009793281555,-0.011814531870186329,-0.8151959776878357,0.5790647268295288,0.011814531870186329,-0.8151959776878357,0.5790647268295288,0.03542393073439598,-0.8151959776878357,0.5781009793281555,0.05897437408566475,-0.8151959776878357,0.5761749744415283,0.08242665976285934,-0.8151959776878357,0.5732899904251099,0.10574174672365189,-0.8151959776878357,0.569450855255127,0.1288808435201645,-0.8151959776878357,0.5646638870239258,0.15180543065071106,-0.8151959776878357,0.5589370727539062,0.17447735369205475,-0.8151959776878357,0.5522800087928772,0.19685886800289154,-0.8151959776878357,0.5447037220001221,0.218912735581398,-0.8151959776878357,0.5362208485603333,0.2406022548675537,-0.8151959776878357,0.5268454551696777,0.26189130544662476,-0.8151959776878357,0.5165931582450867,0.28274446725845337,-0.8151959776878357,0.5054810643196106,0.3031270205974579,-0.8151959776878357,0.49352768063545227,0.3230050504207611,-0.8151959776878357,0.4807528555393219,0.3423454761505127,-0.8151959776878357,0.4671778380870819,0.36111611127853394,-0.8151959776878357,0.45282527804374695,0.37928569316864014,-0.8151959776878357,0.4377190172672272,0.3968239724636078,-0.8151959776878357,0.4218842089176178,0.4137018024921417,-0.8151959776878357,0.40534722805023193,0.42989104986190796,-0.8151959776878357,0.3881355822086334,0.4453648030757904,-0.8151959776878357,0.37027794122695923,0.4600972831249237,-0.8151959776878357,0.3518039882183075,0.4740639925003052,-0.8151959776878357,0.3327445089817047,0.48724165558815,-0.8151959776878357,0.313131183385849,0.4996083378791809,-0.8151959776878357,0.29299670457839966,0.5111435055732727,-0.8151959776878357,0.27237457036972046,0.5218278765678406,-0.8151959776878357,0.25129905343055725,0.5316437482833862,-0.8151959776878357,0.22980530560016632,0.5405747294425964,-0.8151959776878357,0.2079290747642517,0.5486060380935669,-0.8151959776878357,0.1857067495584488,0.5557242035865784,-0.8151959776878357,0.16317534446716309,0.5619174242019653,-0.8151959776878357,0.1403723508119583,0.5671753883361816,-0.8151959776878357,0.1173357143998146,0.5714893341064453,-0.8151959776878357,0.09410378336906433,0.5748521089553833,-0.8151959776878357,0.07071522623300552,0.5772580504417419,-0.8151959776878357,0.04720897600054741,0.5787032842636108,-0.8151959776878357,0.023624148219823837,0.5791852474212646,-0.8151959776878357,7.092973829224216e-17,0.5787032842636108,-0.8151959776878357,-0.023624148219823837,0.5772580504417419,-0.8151959776878357,-0.04720897600054741,0.5748521089553833,-0.8151959776878357,-0.07071522623300552,0.5714893341064453,-0.8151959776878357,-0.09410378336906433,0.5671753883361816,-0.8151959776878357,-0.1173357143998146,0.5619174242019653,-0.8151959776878357,-0.1403723508119583,0.5557242035865784,-0.8151959776878357,-0.16317534446716309,0.5486060380935669,-0.8151959776878357,-0.1857067495584488,0.5405747294425964,-0.8151959776878357,-0.2079290747642517,0.5316437482833862,-0.8151959776878357,-0.22980530560016632,0.5218278765678406,-0.8151959776878357,-0.25129905343055725,0.5111435055732727,-0.8151959776878357,-0.27237457036972046,0.4996083378791809,-0.8151959776878357,-0.29299670457839966,0.48724165558815,-0.8151959776878357,-0.313131183385849,0.4740639925003052,-0.8151959776878357,-0.3327445089817047,0.4600972831249237,-0.8151959776878357,-0.3518039882183075,0.4453648030757904,-0.8151959776878357,-0.37027794122695923,0.42989104986190796,-0.8151959776878357,-0.3881355822086334,0.4137018024921417,-0.8151959776878357,-0.40534722805023193,0.3968239724636078,-0.8151959776878357,-0.4218842089176178,0.37928569316864014,-0.8151959776878357,-0.4377190172672272,0.36111611127853394,-0.8151959776878357,-0.45282527804374695,0.3423454761505127,-0.8151959776878357,-0.4671778380870819,0.3230050504207611,-0.8151959776878357,-0.4807528555393219,0.3031270205974579,-0.8151959776878357,-0.49352768063545227,0.28274446725845337,-0.8151959776878357,-0.5054810643196106,0.26189130544662476,-0.8151959776878357,-0.5165931582450867,0.2406022548675537,-0.8151959776878357,-0.5268454551696777,0.218912735581398,-0.8151959776878357,-0.5362208485603333,0.19685886800289154,-0.8151959776878357,-0.5447037220001221,0.17447735369205475,-0.8151959776878357,-0.5522800087928772,0.15180543065071106,-0.8151959776878357,-0.5589370727539062,0.1288808435201645,-0.8151959776878357,-0.5646638870239258,0.10574174672365189,-0.8151959776878357,-0.569450855255127,0.08242665976285934,-0.8151959776878357,-0.5732899904251099,0.05897437408566475,-0.8151959776878357,-0.5761749744415283,0.03542393073439598,-0.8151959776878357,-0.5781009793281555,0.011814531870186329,-0.8151959776878357,-0.5790647268295288,-0.011814531870186329,-0.8151959776878357,-0.5790647268295288,-0.03542393073439598,-0.8151959776878357,-0.5781009793281555,-0.05897437408566475,-0.8151959776878357,-0.5761749744415283,-0.08242665976285934,-0.8151959776878357,-0.5732899904251099,-0.10574174672365189,-0.8151959776878357,-0.569450855255127,-0.1288808435201645,-0.8151959776878357,-0.5646638870239258,-0.15180543065071106,-0.8151959776878357,-0.5589370727539062,-0.17447735369205475,-0.8151959776878357,-0.5522800087928772,-0.19685886800289154,-0.8151959776878357,-0.5447037220001221,-0.218912735581398,-0.8151959776878357,-0.5362208485603333,-0.2406022548675537,-0.8151959776878357,-0.5268454551696777,-0.26189130544662476,-0.8151959776878357,-0.5165931582450867,-0.28274446725845337,-0.8151959776878357,-0.5054810643196106,-0.3031270205974579,-0.8151959776878357,-0.49352768063545227,-0.3230050504207611,-0.8151959776878357,-0.4807528555393219,-0.3423454761505127,-0.8151959776878357,-0.4671778380870819,-0.36111611127853394,-0.8151959776878357,-0.45282527804374695,-0.37928569316864014,-0.8151959776878357,-0.4377190172672272,-0.3968239724636078,-0.8151959776878357,-0.4218842089176178,-0.4137018024921417,-0.8151959776878357,-0.40534722805023193,-0.42989104986190796,-0.8151959776878357,-0.3881355822086334,-0.4453648030757904,-0.8151959776878357,-0.37027794122695923,-0.4600972831249237,-0.8151959776878357,-0.3518039882183075,-0.4740639925003052,-0.8151959776878357,-0.3327445089817047,-0.48724165558815,-0.8151959776878357,-0.313131183385849,-0.4996083378791809,-0.8151959776878357,-0.29299670457839966,-0.5111435055732727,-0.8151959776878357,-0.27237457036972046,-0.5218278765678406,-0.8151959776878357,-0.25129905343055725,-0.5316437482833862,-0.8151959776878357,-0.22980530560016632,-0.5405747294425964,-0.8151959776878357,-0.2079290747642517,-0.5486060380935669,-0.8151959776878357,-0.1857067495584488,-0.5557242035865784,-0.8151959776878357,-0.16317534446716309,-0.5619174242019653,-0.8151959776878357,-0.1403723508119583,-0.5671753883361816,-0.8151959776878357,-0.1173357143998146,-0.5714893341064453,-0.8151959776878357,-0.09410378336906433,-0.5748521089553833,-0.8151959776878357,-0.07071522623300552,-0.5772580504417419,-0.8151959776878357,-0.04720897600054741,-0.5787032842636108,-0.8151959776878357,-0.023624148219823837,-0.5791852474212646,-0.8151959776878357,-1.4185947658448432e-16,-0.5202394723892212,-0.8540204167366028,0.0,-0.5198065042495728,-0.8540204167366028,0.021219830960035324,-0.5185083746910095,-0.8540204167366028,0.04240434616804123,-0.5163472890853882,-0.8540204167366028,0.06351827830076218,-0.5133267641067505,-0.8540204167366028,0.08452649414539337,-0.5094518661499023,-0.8540204167366028,0.10539402812719345,-0.5047290325164795,-0.8540204167366028,0.12608613073825836,-0.4991661012172699,-0.8540204167366028,0.1465683877468109,-0.4927723705768585,-0.8540204167366028,0.16680669784545898,-0.48555848002433777,-0.8540204167366028,0.1867673695087433,-0.4775364100933075,-0.8540204167366028,0.20641718804836273,-0.4687195420265198,-0.8540204167366028,0.22572343051433563,-0.45912253856658936,-0.8540204167366028,0.24465399980545044,-0.44876137375831604,-0.8540204167366028,0.26317736506462097,-0.4376532733440399,-0.8540204167366028,0.28126266598701477,-0.42581674456596375,-0.8540204167366028,0.29887986183166504,-0.4132714867591858,-0.8540204167366028,0.3159995973110199,-0.40003839135169983,-0.8540204167366028,0.3325933814048767,-0.38613948225975037,-0.8540204167366028,0.34863361716270447,-0.37159785628318787,-0.8540204167366028,0.3640935719013214,-0.3564377427101135,-0.8540204167366028,0.37894749641418457,-0.3406843841075897,-0.8540204167366028,0.3931707441806793,-0.32436397671699524,-0.8540204167366028,0.40673959255218506,-0.30750373005867004,-0.8540204167366028,0.4196314513683319,-0.29013165831565857,-0.8540204167366028,0.43182486295700073,-0.2722766697406769,-0.8540204167366028,0.4432995617389679,-0.2539685070514679,-0.8540204167366028,0.4540364146232605,-0.23523765802383423,-0.8540204167366028,0.4640175998210907,-0.21611528098583221,-0.8540204167366028,0.4732264578342438,-0.19663318991661072,-0.8540204167366028,0.48164767026901245,-0.17682382464408875,-0.8540204167366028,0.4892672300338745,-0.1567201465368271,-0.8540204167366028,0.4960724413394928,-0.13635563850402832,-0.8540204167366028,0.5020520091056824,-0.11576417088508606,-0.8540204167366028,0.5071959495544434,-0.09498002380132675,-0.8540204167366028,0.5114957094192505,-0.07403779029846191,-0.8540204167366028,0.5149441361427307,-0.05297233536839485,-0.8540204167366028,0.5175355076789856,-0.03181871026754379,-0.8540204167366028,0.5192654728889465,-0.010612123645842075,-0.8540204167366028,0.5201312303543091,0.010612123645842075,-0.8540204167366028,0.5201312303543091,0.03181871026754379,-0.8540204167366028,0.5192654728889465,0.05297233536839485,-0.8540204167366028,0.5175355076789856,0.07403779029846191,-0.8540204167366028,0.5149441361427307,0.09498002380132675,-0.8540204167366028,0.5114957094192505,0.11576417088508606,-0.8540204167366028,0.5071959495544434,0.13635563850402832,-0.8540204167366028,0.5020520091056824,0.1567201465368271,-0.8540204167366028,0.4960724413394928,0.17682382464408875,-0.8540204167366028,0.4892672300338745,0.19663318991661072,-0.8540204167366028,0.48164767026901245,0.21611528098583221,-0.8540204167366028,0.4732264578342438,0.23523765802383423,-0.8540204167366028,0.4640175998210907,0.2539685070514679,-0.8540204167366028,0.4540364146232605,0.2722766697406769,-0.8540204167366028,0.4432995617389679,0.29013165831565857,-0.8540204167366028,0.43182486295700073,0.30750373005867004,-0.8540204167366028,0.4196314513683319,0.32436397671699524,-0.8540204167366028,0.40673959255218506,0.3406843841075897,-0.8540204167366028,0.3931707441806793,0.3564377427101135,-0.8540204167366028,0.37894749641418457,0.37159785628318787,-0.8540204167366028,0.3640935719013214,0.38613948225975037,-0.8540204167366028,0.34863361716270447,0.40003839135169983,-0.8540204167366028,0.3325933814048767,0.4132714867591858,-0.8540204167366028,0.3159995973110199,0.42581674456596375,-0.8540204167366028,0.29887986183166504,0.4376532733440399,-0.8540204167366028,0.28126266598701477,0.44876137375831604,-0.8540204167366028,0.26317736506462097,0.45912253856658936,-0.8540204167366028,0.24465399980545044,0.4687195420265198,-0.8540204167366028,0.22572343051433563,0.4775364100933075,-0.8540204167366028,0.20641718804836273,0.48555848002433777,-0.8540204167366028,0.1867673695087433,0.4927723705768585,-0.8540204167366028,0.16680669784545898,0.4991661012172699,-0.8540204167366028,0.1465683877468109,0.5047290325164795,-0.8540204167366028,0.12608613073825836,0.5094518661499023,-0.8540204167366028,0.10539402812719345,0.5133267641067505,-0.8540204167366028,0.08452649414539337,0.5163472890853882,-0.8540204167366028,0.06351827830076218,0.5185083746910095,-0.8540204167366028,0.04240434616804123,0.5198065042495728,-0.8540204167366028,0.021219830960035324,0.5202394723892212,-0.8540204167366028,6.371095955627149e-17,0.5198065042495728,-0.8540204167366028,-0.021219830960035324,0.5185083746910095,-0.8540204167366028,-0.04240434616804123,0.5163472890853882,-0.8540204167366028,-0.06351827830076218,0.5133267641067505,-0.8540204167366028,-0.08452649414539337,0.5094518661499023,-0.8540204167366028,-0.10539402812719345,0.5047290325164795,-0.8540204167366028,-0.12608613073825836,0.4991661012172699,-0.8540204167366028,-0.1465683877468109,0.4927723705768585,-0.8540204167366028,-0.16680669784545898,0.48555848002433777,-0.8540204167366028,-0.1867673695087433,0.4775364100933075,-0.8540204167366028,-0.20641718804836273,0.4687195420265198,-0.8540204167366028,-0.22572343051433563,0.45912253856658936,-0.8540204167366028,-0.24465399980545044,0.44876137375831604,-0.8540204167366028,-0.26317736506462097,0.4376532733440399,-0.8540204167366028,-0.28126266598701477,0.42581674456596375,-0.8540204167366028,-0.29887986183166504,0.4132714867591858,-0.8540204167366028,-0.3159995973110199,0.40003839135169983,-0.8540204167366028,-0.3325933814048767,0.38613948225975037,-0.8540204167366028,-0.34863361716270447,0.37159785628318787,-0.8540204167366028,-0.3640935719013214,0.3564377427101135,-0.8540204167366028,-0.37894749641418457,0.3406843841075897,-0.8540204167366028,-0.3931707441806793,0.32436397671699524,-0.8540204167366028,-0.40673959255218506,0.30750373005867004,-0.8540204167366028,-0.4196314513683319,0.29013165831565857,-0.8540204167366028,-0.43182486295700073,0.2722766697406769,-0.8540204167366028,-0.4432995617389679,0.2539685070514679,-0.8540204167366028,-0.4540364146232605,0.23523765802383423,-0.8540204167366028,-0.4640175998210907,0.21611528098583221,-0.8540204167366028,-0.4732264578342438,0.19663318991661072,-0.8540204167366028,-0.48164767026901245,0.17682382464408875,-0.8540204167366028,-0.4892672300338745,0.1567201465368271,-0.8540204167366028,-0.4960724413394928,0.13635563850402832,-0.8540204167366028,-0.5020520091056824,0.11576417088508606,-0.8540204167366028,-0.5071959495544434,0.09498002380132675,-0.8540204167366028,-0.5114957094192505,0.07403779029846191,-0.8540204167366028,-0.5149441361427307,0.05297233536839485,-0.8540204167366028,-0.5175355076789856,0.03181871026754379,-0.8540204167366028,-0.5192654728889465,0.010612123645842075,-0.8540204167366028,-0.5201312303543091,-0.010612123645842075,-0.8540204167366028,-0.5201312303543091,-0.03181871026754379,-0.8540204167366028,-0.5192654728889465,-0.05297233536839485,-0.8540204167366028,-0.5175355076789856,-0.07403779029846191,-0.8540204167366028,-0.5149441361427307,-0.09498002380132675,-0.8540204167366028,-0.5114957094192505,-0.11576417088508606,-0.8540204167366028,-0.5071959495544434,-0.13635563850402832,-0.8540204167366028,-0.5020520091056824,-0.1567201465368271,-0.8540204167366028,-0.4960724413394928,-0.17682382464408875,-0.8540204167366028,-0.4892672300338745,-0.19663318991661072,-0.8540204167366028,-0.48164767026901245,-0.21611528098583221,-0.8540204167366028,-0.4732264578342438,-0.23523765802383423,-0.8540204167366028,-0.4640175998210907,-0.2539685070514679,-0.8540204167366028,-0.4540364146232605,-0.2722766697406769,-0.8540204167366028,-0.4432995617389679,-0.29013165831565857,-0.8540204167366028,-0.43182486295700073,-0.30750373005867004,-0.8540204167366028,-0.4196314513683319,-0.32436397671699524,-0.8540204167366028,-0.40673959255218506,-0.3406843841075897,-0.8540204167366028,-0.3931707441806793,-0.3564377427101135,-0.8540204167366028,-0.37894749641418457,-0.37159785628318787,-0.8540204167366028,-0.3640935719013214,-0.38613948225975037,-0.8540204167366028,-0.34863361716270447,-0.40003839135169983,-0.8540204167366028,-0.3325933814048767,-0.4132714867591858,-0.8540204167366028,-0.3159995973110199,-0.42581674456596375,-0.8540204167366028,-0.29887986183166504,-0.4376532733440399,-0.8540204167366028,-0.28126266598701477,-0.44876137375831604,-0.8540204167366028,-0.26317736506462097,-0.45912253856658936,-0.8540204167366028,-0.24465399980545044,-0.4687195420265198,-0.8540204167366028,-0.22572343051433563,-0.4775364100933075,-0.8540204167366028,-0.20641718804836273,-0.48555848002433777,-0.8540204167366028,-0.1867673695087433,-0.4927723705768585,-0.8540204167366028,-0.16680669784545898,-0.4991661012172699,-0.8540204167366028,-0.1465683877468109,-0.5047290325164795,-0.8540204167366028,-0.12608613073825836,-0.5094518661499023,-0.8540204167366028,-0.10539402812719345,-0.5133267641067505,-0.8540204167366028,-0.08452649414539337,-0.5163472890853882,-0.8540204167366028,-0.06351827830076218,-0.5185083746910095,-0.8540204167366028,-0.04240434616804123,-0.5198065042495728,-0.8540204167366028,-0.021219830960035324,-0.5202394723892212,-0.8540204167366028,-1.2742191911254298e-16,-0.45870181918144226,-0.8885902762413025,0.0,-0.45832011103630066,-0.8885902762413025,0.018709799274802208,-0.4571755528450012,-0.8885902762413025,0.03738845884799957,-0.45527005195617676,-0.8885902762413025,0.05600488558411598,-0.45260682702064514,-0.8885902762413025,0.07452809810638428,-0.44919025897979736,-0.8885902762413025,0.09292726963758469,-0.44502606987953186,-0.8885902762413025,0.11117177456617355,-0.4401211738586426,-0.8885902762413025,0.12923124432563782,-0.43448376655578613,-0.8885902762413025,0.1470756083726883,-0.4281231760978699,-0.8885902762413025,0.16467519104480743,-0.4210500121116638,-0.8885902762413025,0.18200069665908813,-0.4132760763168335,-0.8885902762413025,0.1990232616662979,-0.4048142731189728,-0.8885902762413025,0.21571458876132965,-0.39567869901657104,-0.8885902762413025,0.23204687237739563,-0.38588452339172363,-0.8885902762413025,0.24799293279647827,-0.3754481077194214,-0.8885902762413025,0.26352623105049133,-0.36438679695129395,-0.8885902762413025,0.2786209285259247,-0.3527190089225769,-0.8885902762413025,0.2932518720626831,-0.34046414494514465,-0.8885902762413025,0.30739474296569824,-0.32764261960983276,-0.8885902762413025,0.3210259974002838,-0.3142757713794708,-0.8885902762413025,0.334122896194458,-0.30038583278656006,-0.8885902762413025,0.3466637134552002,-0.2859959006309509,-0.8885902762413025,0.3586275279521942,-0.2711299955844879,-0.8885902762413025,0.36999446153640747,-0.2558128237724304,-0.8885902762413025,0.3807455599308014,-0.24006985127925873,-0.8885902762413025,0.39086294174194336,-0.2239273190498352,-0.8885902762413025,0.4003297686576843,-0.20741207897663116,-0.8885902762413025,0.4091303050518036,-0.19055162370204926,-0.8885902762413025,0.417249858379364,-0.1733740121126175,-0.8885902762413025,0.42467495799064636,-0.15590783953666687,-0.8885902762413025,0.43139323592185974,-0.13818217813968658,-0.8885902762413025,0.4373934864997864,-0.12022652477025986,-0.8885902762413025,0.4426657259464264,-0.10207076370716095,-0.8885902762413025,0.4472012221813202,-0.08374511450529099,-0.8885902762413025,0.4509923756122589,-0.06528007984161377,-0.8885902762413025,0.45403289794921875,-0.046706389635801315,-0.8885902762413025,0.4563177525997162,-0.028054965659976006,-0.8885902762413025,0.4578430950641632,-0.00935684610158205,-0.8885902762413025,0.45860639214515686,0.00935684610158205,-0.8885902762413025,0.45860639214515686,0.028054965659976006,-0.8885902762413025,0.4578430950641632,0.046706389635801315,-0.8885902762413025,0.4563177525997162,0.06528007984161377,-0.8885902762413025,0.45403289794921875,0.08374511450529099,-0.8885902762413025,0.4509923756122589,0.10207076370716095,-0.8885902762413025,0.4472012221813202,0.12022652477025986,-0.8885902762413025,0.4426657259464264,0.13818217813968658,-0.8885902762413025,0.4373934864997864,0.15590783953666687,-0.8885902762413025,0.43139323592185974,0.1733740121126175,-0.8885902762413025,0.42467495799064636,0.19055162370204926,-0.8885902762413025,0.417249858379364,0.20741207897663116,-0.8885902762413025,0.4091303050518036,0.2239273190498352,-0.8885902762413025,0.4003297686576843,0.24006985127925873,-0.8885902762413025,0.39086294174194336,0.2558128237724304,-0.8885902762413025,0.3807455599308014,0.2711299955844879,-0.8885902762413025,0.36999446153640747,0.2859959006309509,-0.8885902762413025,0.3586275279521942,0.30038583278656006,-0.8885902762413025,0.3466637134552002,0.3142757713794708,-0.8885902762413025,0.334122896194458,0.32764261960983276,-0.8885902762413025,0.3210259974002838,0.34046414494514465,-0.8885902762413025,0.30739474296569824,0.3527190089225769,-0.8885902762413025,0.2932518720626831,0.36438679695129395,-0.8885902762413025,0.2786209285259247,0.3754481077194214,-0.8885902762413025,0.26352623105049133,0.38588452339172363,-0.8885902762413025,0.24799293279647827,0.39567869901657104,-0.8885902762413025,0.23204687237739563,0.4048142731189728,-0.8885902762413025,0.21571458876132965,0.4132760763168335,-0.8885902762413025,0.1990232616662979,0.4210500121116638,-0.8885902762413025,0.18200069665908813,0.4281231760978699,-0.8885902762413025,0.16467519104480743,0.43448376655578613,-0.8885902762413025,0.1470756083726883,0.4401211738586426,-0.8885902762413025,0.12923124432563782,0.44502606987953186,-0.8885902762413025,0.11117177456617355,0.44919025897979736,-0.8885902762413025,0.09292726963758469,0.45260682702064514,-0.8885902762413025,0.07452809810638428,0.45527005195617676,-0.8885902762413025,0.05600488558411598,0.4571755528450012,-0.8885902762413025,0.03738845884799957,0.45832011103630066,-0.8885902762413025,0.018709799274802208,0.45870181918144226,-0.8885902762413025,5.617477507565197e-17,0.45832011103630066,-0.8885902762413025,-0.018709799274802208,0.4571755528450012,-0.8885902762413025,-0.03738845884799957,0.45527005195617676,-0.8885902762413025,-0.05600488558411598,0.45260682702064514,-0.8885902762413025,-0.07452809810638428,0.44919025897979736,-0.8885902762413025,-0.09292726963758469,0.44502606987953186,-0.8885902762413025,-0.11117177456617355,0.4401211738586426,-0.8885902762413025,-0.12923124432563782,0.43448376655578613,-0.8885902762413025,-0.1470756083726883,0.4281231760978699,-0.8885902762413025,-0.16467519104480743,0.4210500121116638,-0.8885902762413025,-0.18200069665908813,0.4132760763168335,-0.8885902762413025,-0.1990232616662979,0.4048142731189728,-0.8885902762413025,-0.21571458876132965,0.39567869901657104,-0.8885902762413025,-0.23204687237739563,0.38588452339172363,-0.8885902762413025,-0.24799293279647827,0.3754481077194214,-0.8885902762413025,-0.26352623105049133,0.36438679695129395,-0.8885902762413025,-0.2786209285259247,0.3527190089225769,-0.8885902762413025,-0.2932518720626831,0.34046414494514465,-0.8885902762413025,-0.30739474296569824,0.32764261960983276,-0.8885902762413025,-0.3210259974002838,0.3142757713794708,-0.8885902762413025,-0.334122896194458,0.30038583278656006,-0.8885902762413025,-0.3466637134552002,0.2859959006309509,-0.8885902762413025,-0.3586275279521942,0.2711299955844879,-0.8885902762413025,-0.36999446153640747,0.2558128237724304,-0.8885902762413025,-0.3807455599308014,0.24006985127925873,-0.8885902762413025,-0.39086294174194336,0.2239273190498352,-0.8885902762413025,-0.4003297686576843,0.20741207897663116,-0.8885902762413025,-0.4091303050518036,0.19055162370204926,-0.8885902762413025,-0.417249858379364,0.1733740121126175,-0.8885902762413025,-0.42467495799064636,0.15590783953666687,-0.8885902762413025,-0.43139323592185974,0.13818217813968658,-0.8885902762413025,-0.4373934864997864,0.12022652477025986,-0.8885902762413025,-0.4426657259464264,0.10207076370716095,-0.8885902762413025,-0.4472012221813202,0.08374511450529099,-0.8885902762413025,-0.4509923756122589,0.06528007984161377,-0.8885902762413025,-0.45403289794921875,0.046706389635801315,-0.8885902762413025,-0.4563177525997162,0.028054965659976006,-0.8885902762413025,-0.4578430950641632,0.00935684610158205,-0.8885902762413025,-0.45860639214515686,-0.00935684610158205,-0.8885902762413025,-0.45860639214515686,-0.028054965659976006,-0.8885902762413025,-0.4578430950641632,-0.046706389635801315,-0.8885902762413025,-0.4563177525997162,-0.06528007984161377,-0.8885902762413025,-0.45403289794921875,-0.08374511450529099,-0.8885902762413025,-0.4509923756122589,-0.10207076370716095,-0.8885902762413025,-0.4472012221813202,-0.12022652477025986,-0.8885902762413025,-0.4426657259464264,-0.13818217813968658,-0.8885902762413025,-0.4373934864997864,-0.15590783953666687,-0.8885902762413025,-0.43139323592185974,-0.1733740121126175,-0.8885902762413025,-0.42467495799064636,-0.19055162370204926,-0.8885902762413025,-0.417249858379364,-0.20741207897663116,-0.8885902762413025,-0.4091303050518036,-0.2239273190498352,-0.8885902762413025,-0.4003297686576843,-0.24006985127925873,-0.8885902762413025,-0.39086294174194336,-0.2558128237724304,-0.8885902762413025,-0.3807455599308014,-0.2711299955844879,-0.8885902762413025,-0.36999446153640747,-0.2859959006309509,-0.8885902762413025,-0.3586275279521942,-0.30038583278656006,-0.8885902762413025,-0.3466637134552002,-0.3142757713794708,-0.8885902762413025,-0.334122896194458,-0.32764261960983276,-0.8885902762413025,-0.3210259974002838,-0.34046414494514465,-0.8885902762413025,-0.30739474296569824,-0.3527190089225769,-0.8885902762413025,-0.2932518720626831,-0.36438679695129395,-0.8885902762413025,-0.2786209285259247,-0.3754481077194214,-0.8885902762413025,-0.26352623105049133,-0.38588452339172363,-0.8885902762413025,-0.24799293279647827,-0.39567869901657104,-0.8885902762413025,-0.23204687237739563,-0.4048142731189728,-0.8885902762413025,-0.21571458876132965,-0.4132760763168335,-0.8885902762413025,-0.1990232616662979,-0.4210500121116638,-0.8885902762413025,-0.18200069665908813,-0.4281231760978699,-0.8885902762413025,-0.16467519104480743,-0.43448376655578613,-0.8885902762413025,-0.1470756083726883,-0.4401211738586426,-0.8885902762413025,-0.12923124432563782,-0.44502606987953186,-0.8885902762413025,-0.11117177456617355,-0.44919025897979736,-0.8885902762413025,-0.09292726963758469,-0.45260682702064514,-0.8885902762413025,-0.07452809810638428,-0.45527005195617676,-0.8885902762413025,-0.05600488558411598,-0.4571755528450012,-0.8885902762413025,-0.03738845884799957,-0.45832011103630066,-0.8885902762413025,-0.018709799274802208,-0.45870181918144226,-0.8885902762413025,-1.1234955015130394e-16,-0.39487898349761963,-0.9187331199645996,0.0,-0.3945503532886505,-0.9187331199645996,0.016106555238366127,-0.3935650587081909,-0.9187331199645996,0.03218630328774452,-0.3919247090816498,-0.9187331199645996,0.048212479799985886,-0.3896320164203644,-0.9187331199645996,0.06415840983390808,-0.38669082522392273,-0.9187331199645996,0.07999756187200546,-0.3831060528755188,-0.9187331199645996,0.09570355713367462,-0.37888360023498535,-0.9187331199645996,0.11125026643276215,-0.37403056025505066,-0.9187331199645996,0.12661181390285492,-0.3685549795627594,-0.9187331199645996,0.1417626291513443,-0.3624659776687622,-0.9187331199645996,0.1566774845123291,-0.3557736873626709,-0.9187331199645996,0.17133156955242157,-0.3484892249107361,-0.9187331199645996,0.18570049107074738,-0.34062474966049194,-0.9187331199645996,0.1997603327035904,-0.3321933448314667,-0.9187331199645996,0.21348769962787628,-0.3232090175151825,-0.9187331199645996,0.22685973346233368,-0.3136867582798004,-0.9187331199645996,0.23985417187213898,-0.3036423921585083,-0.9187331199645996,0.2524494230747223,-0.29309266805648804,-0.9187331199645996,0.2646244764328003,-0.2820550799369812,-0.9187331199645996,0.27635908126831055,-0.2705480754375458,-0.9187331199645996,0.2876337170600891,-0.2585907280445099,-0.9187331199645996,0.2984296381473541,-0.24620302021503448,-0.9187331199645996,0.30872881412506104,-0.23340551555156708,-0.9187331199645996,0.3185141682624817,-0.22021953761577606,-0.9187331199645996,0.32776939868927,-0.20666702091693878,-0.9187331199645996,0.3364790678024292,-0.19277052581310272,-0.9187331199645996,0.3446286916732788,-0.17855317890644073,-0.9187331199645996,0.35220474004745483,-0.16403865814208984,-0.9187331199645996,0.3591945469379425,-0.14925110340118408,-0.9187331199645996,0.3655865490436554,-0.13421514630317688,-0.9187331199645996,0.3713700473308563,-0.11895579099655151,-0.9187331199645996,0.37653544545173645,-0.1034984439611435,-0.9187331199645996,0.38107413053512573,-0.0878688395023346,-0.9187331199645996,0.3849785327911377,-0.07209298759698868,-0.9187331199645996,0.3882422149181366,-0.056197140365839005,-0.9187331199645996,0.3908596932888031,-0.04020775854587555,-0.9187331199645996,0.3928266167640686,-0.024151453748345375,-0.9187331199645996,0.39413973689079285,-0.008054953999817371,-0.9187331199645996,0.39479681849479675,0.008054953999817371,-0.9187331199645996,0.39479681849479675,0.024151453748345375,-0.9187331199645996,0.39413973689079285,0.04020775854587555,-0.9187331199645996,0.3928266167640686,0.056197140365839005,-0.9187331199645996,0.3908596932888031,0.07209298759698868,-0.9187331199645996,0.3882422149181366,0.0878688395023346,-0.9187331199645996,0.3849785327911377,0.1034984439611435,-0.9187331199645996,0.38107413053512573,0.11895579099655151,-0.9187331199645996,0.37653544545173645,0.13421514630317688,-0.9187331199645996,0.3713700473308563,0.14925110340118408,-0.9187331199645996,0.3655865490436554,0.16403865814208984,-0.9187331199645996,0.3591945469379425,0.17855317890644073,-0.9187331199645996,0.35220474004745483,0.19277052581310272,-0.9187331199645996,0.3446286916732788,0.20666702091693878,-0.9187331199645996,0.3364790678024292,0.22021953761577606,-0.9187331199645996,0.32776939868927,0.23340551555156708,-0.9187331199645996,0.3185141682624817,0.24620302021503448,-0.9187331199645996,0.30872881412506104,0.2585907280445099,-0.9187331199645996,0.2984296381473541,0.2705480754375458,-0.9187331199645996,0.2876337170600891,0.2820550799369812,-0.9187331199645996,0.27635908126831055,0.29309266805648804,-0.9187331199645996,0.2646244764328003,0.3036423921585083,-0.9187331199645996,0.2524494230747223,0.3136867582798004,-0.9187331199645996,0.23985417187213898,0.3232090175151825,-0.9187331199645996,0.22685973346233368,0.3321933448314667,-0.9187331199645996,0.21348769962787628,0.34062474966049194,-0.9187331199645996,0.1997603327035904,0.3484892249107361,-0.9187331199645996,0.18570049107074738,0.3557736873626709,-0.9187331199645996,0.17133156955242157,0.3624659776687622,-0.9187331199645996,0.1566774845123291,0.3685549795627594,-0.9187331199645996,0.1417626291513443,0.37403056025505066,-0.9187331199645996,0.12661181390285492,0.37888360023498535,-0.9187331199645996,0.11125026643276215,0.3831060528755188,-0.9187331199645996,0.09570355713367462,0.38669082522392273,-0.9187331199645996,0.07999756187200546,0.3896320164203644,-0.9187331199645996,0.06415840983390808,0.3919247090816498,-0.9187331199645996,0.048212479799985886,0.3935650587081909,-0.9187331199645996,0.03218630328774452,0.3945503532886505,-0.9187331199645996,0.016106555238366127,0.39487898349761963,-0.9187331199645996,4.835872892402616e-17,0.3945503532886505,-0.9187331199645996,-0.016106555238366127,0.3935650587081909,-0.9187331199645996,-0.03218630328774452,0.3919247090816498,-0.9187331199645996,-0.048212479799985886,0.3896320164203644,-0.9187331199645996,-0.06415840983390808,0.38669082522392273,-0.9187331199645996,-0.07999756187200546,0.3831060528755188,-0.9187331199645996,-0.09570355713367462,0.37888360023498535,-0.9187331199645996,-0.11125026643276215,0.37403056025505066,-0.9187331199645996,-0.12661181390285492,0.3685549795627594,-0.9187331199645996,-0.1417626291513443,0.3624659776687622,-0.9187331199645996,-0.1566774845123291,0.3557736873626709,-0.9187331199645996,-0.17133156955242157,0.3484892249107361,-0.9187331199645996,-0.18570049107074738,0.34062474966049194,-0.9187331199645996,-0.1997603327035904,0.3321933448314667,-0.9187331199645996,-0.21348769962787628,0.3232090175151825,-0.9187331199645996,-0.22685973346233368,0.3136867582798004,-0.9187331199645996,-0.23985417187213898,0.3036423921585083,-0.9187331199645996,-0.2524494230747223,0.29309266805648804,-0.9187331199645996,-0.2646244764328003,0.2820550799369812,-0.9187331199645996,-0.27635908126831055,0.2705480754375458,-0.9187331199645996,-0.2876337170600891,0.2585907280445099,-0.9187331199645996,-0.2984296381473541,0.24620302021503448,-0.9187331199645996,-0.30872881412506104,0.23340551555156708,-0.9187331199645996,-0.3185141682624817,0.22021953761577606,-0.9187331199645996,-0.32776939868927,0.20666702091693878,-0.9187331199645996,-0.3364790678024292,0.19277052581310272,-0.9187331199645996,-0.3446286916732788,0.17855317890644073,-0.9187331199645996,-0.35220474004745483,0.16403865814208984,-0.9187331199645996,-0.3591945469379425,0.14925110340118408,-0.9187331199645996,-0.3655865490436554,0.13421514630317688,-0.9187331199645996,-0.3713700473308563,0.11895579099655151,-0.9187331199645996,-0.37653544545173645,0.1034984439611435,-0.9187331199645996,-0.38107413053512573,0.0878688395023346,-0.9187331199645996,-0.3849785327911377,0.07209298759698868,-0.9187331199645996,-0.3882422149181366,0.056197140365839005,-0.9187331199645996,-0.3908596932888031,0.04020775854587555,-0.9187331199645996,-0.3928266167640686,0.024151453748345375,-0.9187331199645996,-0.39413973689079285,0.008054953999817371,-0.9187331199645996,-0.39479681849479675,-0.008054953999817371,-0.9187331199645996,-0.39479681849479675,-0.024151453748345375,-0.9187331199645996,-0.39413973689079285,-0.04020775854587555,-0.9187331199645996,-0.3928266167640686,-0.056197140365839005,-0.9187331199645996,-0.3908596932888031,-0.07209298759698868,-0.9187331199645996,-0.3882422149181366,-0.0878688395023346,-0.9187331199645996,-0.3849785327911377,-0.1034984439611435,-0.9187331199645996,-0.38107413053512573,-0.11895579099655151,-0.9187331199645996,-0.37653544545173645,-0.13421514630317688,-0.9187331199645996,-0.3713700473308563,-0.14925110340118408,-0.9187331199645996,-0.3655865490436554,-0.16403865814208984,-0.9187331199645996,-0.3591945469379425,-0.17855317890644073,-0.9187331199645996,-0.35220474004745483,-0.19277052581310272,-0.9187331199645996,-0.3446286916732788,-0.20666702091693878,-0.9187331199645996,-0.3364790678024292,-0.22021953761577606,-0.9187331199645996,-0.32776939868927,-0.23340551555156708,-0.9187331199645996,-0.3185141682624817,-0.24620302021503448,-0.9187331199645996,-0.30872881412506104,-0.2585907280445099,-0.9187331199645996,-0.2984296381473541,-0.2705480754375458,-0.9187331199645996,-0.2876337170600891,-0.2820550799369812,-0.9187331199645996,-0.27635908126831055,-0.29309266805648804,-0.9187331199645996,-0.2646244764328003,-0.3036423921585083,-0.9187331199645996,-0.2524494230747223,-0.3136867582798004,-0.9187331199645996,-0.23985417187213898,-0.3232090175151825,-0.9187331199645996,-0.22685973346233368,-0.3321933448314667,-0.9187331199645996,-0.21348769962787628,-0.34062474966049194,-0.9187331199645996,-0.1997603327035904,-0.3484892249107361,-0.9187331199645996,-0.18570049107074738,-0.3557736873626709,-0.9187331199645996,-0.17133156955242157,-0.3624659776687622,-0.9187331199645996,-0.1566774845123291,-0.3685549795627594,-0.9187331199645996,-0.1417626291513443,-0.37403056025505066,-0.9187331199645996,-0.12661181390285492,-0.37888360023498535,-0.9187331199645996,-0.11125026643276215,-0.3831060528755188,-0.9187331199645996,-0.09570355713367462,-0.38669082522392273,-0.9187331199645996,-0.07999756187200546,-0.3896320164203644,-0.9187331199645996,-0.06415840983390808,-0.3919247090816498,-0.9187331199645996,-0.048212479799985886,-0.3935650587081909,-0.9187331199645996,-0.03218630328774452,-0.3945503532886505,-0.9187331199645996,-0.016106555238366127,-0.39487898349761963,-0.9187331199645996,-9.671745784805232e-17,-0.32908886671066284,-0.9442989826202393,0.0,-0.3288150131702423,-0.9442989826202393,0.013423069380223751,-0.32799386978149414,-0.9442989826202393,0.026823798194527626,-0.32662680745124817,-0.9442989826202393,0.04017988219857216,-0.32471609115600586,-0.9442989826202393,0.053469087928533554,-0.3222649395465851,-0.9442989826202393,0.06666930019855499,-0.31927740573883057,-0.9442989826202393,0.07975855469703674,-0.31575846672058105,-0.9442989826202393,0.09271504729986191,-0.31171396374702454,-0.9442989826202393,0.10551723092794418,-0.307150661945343,-0.9442989826202393,0.11814379692077637,-0.302076131105423,-0.9442989826202393,0.13057371973991394,-0.2964988350868225,-0.9442989826202393,0.14278630912303925,-0.2904280424118042,-0.9442989826202393,0.15476125478744507,-0.28387385606765747,-0.9442989826202393,0.16647860407829285,-0.2768471837043762,-0.9442989826202393,0.17791888117790222,-0.2693597376346588,-0.9442989826202393,0.18906301259994507,-0.2614239454269409,-0.9442989826202393,0.19989247620105743,-0.25305306911468506,-0.9442989826202393,0.21038924157619476,-0.2442609965801239,-0.9442989826202393,0.22053582966327667,-0.235062375664711,-0.9442989826202393,0.23031537234783173,-0.22547250986099243,-0.9442989826202393,0.23971155285835266,-0.2155073881149292,-0.9442989826202393,0.24870876967906952,-0.20518355071544647,-0.9442989826202393,0.2572920322418213,-0.19451822340488434,-0.9442989826202393,0.26544708013534546,-0.18352913856506348,-0.9442989826202393,0.27316027879714966,-0.17223457992076874,-0.9442989826202393,0.28041887283325195,-0.16065335273742676,-0.9442989826202393,0.28721070289611816,-0.14880473911762238,-0.9442989826202393,0.29352450370788574,-0.13670845329761505,-0.9442989826202393,0.29934975504875183,-0.12438463419675827,-0.9442989826202393,0.3046768009662628,-0.11185378581285477,-0.9442989826202393,0.3094967007637024,-0.09913676977157593,-0.9442989826202393,0.31380149722099304,-0.08625474572181702,-0.9442989826202393,0.31758400797843933,-0.07322916388511658,-0.9442989826202393,0.32083791494369507,-0.06008169800043106,-0.9442989826202393,0.32355785369873047,-0.04683423042297363,-0.9442989826202393,0.3257392346858978,-0.03350881114602089,-0.9442989826202393,0.32737845182418823,-0.02012762241065502,-0.9442989826202393,0.328472763299942,-0.006712931673973799,-0.9442989826202393,0.3290204107761383,0.006712931673973799,-0.9442989826202393,0.3290204107761383,0.02012762241065502,-0.9442989826202393,0.328472763299942,0.03350881114602089,-0.9442989826202393,0.32737845182418823,0.04683423042297363,-0.9442989826202393,0.3257392346858978,0.06008169800043106,-0.9442989826202393,0.32355785369873047,0.07322916388511658,-0.9442989826202393,0.32083791494369507,0.08625474572181702,-0.9442989826202393,0.31758400797843933,0.09913676977157593,-0.9442989826202393,0.31380149722099304,0.11185378581285477,-0.9442989826202393,0.3094967007637024,0.12438463419675827,-0.9442989826202393,0.3046768009662628,0.13670845329761505,-0.9442989826202393,0.29934975504875183,0.14880473911762238,-0.9442989826202393,0.29352450370788574,0.16065335273742676,-0.9442989826202393,0.28721070289611816,0.17223457992076874,-0.9442989826202393,0.28041887283325195,0.18352913856506348,-0.9442989826202393,0.27316027879714966,0.19451822340488434,-0.9442989826202393,0.26544708013534546,0.20518355071544647,-0.9442989826202393,0.2572920322418213,0.2155073881149292,-0.9442989826202393,0.24870876967906952,0.22547250986099243,-0.9442989826202393,0.23971155285835266,0.235062375664711,-0.9442989826202393,0.23031537234783173,0.2442609965801239,-0.9442989826202393,0.22053582966327667,0.25305306911468506,-0.9442989826202393,0.21038924157619476,0.2614239454269409,-0.9442989826202393,0.19989247620105743,0.2693597376346588,-0.9442989826202393,0.18906301259994507,0.2768471837043762,-0.9442989826202393,0.17791888117790222,0.28387385606765747,-0.9442989826202393,0.16647860407829285,0.2904280424118042,-0.9442989826202393,0.15476125478744507,0.2964988350868225,-0.9442989826202393,0.14278630912303925,0.302076131105423,-0.9442989826202393,0.13057371973991394,0.307150661945343,-0.9442989826202393,0.11814379692077637,0.31171396374702454,-0.9442989826202393,0.10551723092794418,0.31575846672058105,-0.9442989826202393,0.09271504729986191,0.31927740573883057,-0.9442989826202393,0.07975855469703674,0.3222649395465851,-0.9442989826202393,0.06666930019855499,0.32471609115600586,-0.9442989826202393,0.053469087928533554,0.32662680745124817,-0.9442989826202393,0.04017988219857216,0.32799386978149414,-0.9442989826202393,0.026823798194527626,0.3288150131702423,-0.9442989826202393,0.013423069380223751,0.32908886671066284,-0.9442989826202393,4.0301764764633056e-17,0.3288150131702423,-0.9442989826202393,-0.013423069380223751,0.32799386978149414,-0.9442989826202393,-0.026823798194527626,0.32662680745124817,-0.9442989826202393,-0.04017988219857216,0.32471609115600586,-0.9442989826202393,-0.053469087928533554,0.3222649395465851,-0.9442989826202393,-0.06666930019855499,0.31927740573883057,-0.9442989826202393,-0.07975855469703674,0.31575846672058105,-0.9442989826202393,-0.09271504729986191,0.31171396374702454,-0.9442989826202393,-0.10551723092794418,0.307150661945343,-0.9442989826202393,-0.11814379692077637,0.302076131105423,-0.9442989826202393,-0.13057371973991394,0.2964988350868225,-0.9442989826202393,-0.14278630912303925,0.2904280424118042,-0.9442989826202393,-0.15476125478744507,0.28387385606765747,-0.9442989826202393,-0.16647860407829285,0.2768471837043762,-0.9442989826202393,-0.17791888117790222,0.2693597376346588,-0.9442989826202393,-0.18906301259994507,0.2614239454269409,-0.9442989826202393,-0.19989247620105743,0.25305306911468506,-0.9442989826202393,-0.21038924157619476,0.2442609965801239,-0.9442989826202393,-0.22053582966327667,0.235062375664711,-0.9442989826202393,-0.23031537234783173,0.22547250986099243,-0.9442989826202393,-0.23971155285835266,0.2155073881149292,-0.9442989826202393,-0.24870876967906952,0.20518355071544647,-0.9442989826202393,-0.2572920322418213,0.19451822340488434,-0.9442989826202393,-0.26544708013534546,0.18352913856506348,-0.9442989826202393,-0.27316027879714966,0.17223457992076874,-0.9442989826202393,-0.28041887283325195,0.16065335273742676,-0.9442989826202393,-0.28721070289611816,0.14880473911762238,-0.9442989826202393,-0.29352450370788574,0.13670845329761505,-0.9442989826202393,-0.29934975504875183,0.12438463419675827,-0.9442989826202393,-0.3046768009662628,0.11185378581285477,-0.9442989826202393,-0.3094967007637024,0.09913676977157593,-0.9442989826202393,-0.31380149722099304,0.08625474572181702,-0.9442989826202393,-0.31758400797843933,0.07322916388511658,-0.9442989826202393,-0.32083791494369507,0.06008169800043106,-0.9442989826202393,-0.32355785369873047,0.04683423042297363,-0.9442989826202393,-0.3257392346858978,0.03350881114602089,-0.9442989826202393,-0.32737845182418823,0.02012762241065502,-0.9442989826202393,-0.328472763299942,0.006712931673973799,-0.9442989826202393,-0.3290204107761383,-0.006712931673973799,-0.9442989826202393,-0.3290204107761383,-0.02012762241065502,-0.9442989826202393,-0.328472763299942,-0.03350881114602089,-0.9442989826202393,-0.32737845182418823,-0.04683423042297363,-0.9442989826202393,-0.3257392346858978,-0.06008169800043106,-0.9442989826202393,-0.32355785369873047,-0.07322916388511658,-0.9442989826202393,-0.32083791494369507,-0.08625474572181702,-0.9442989826202393,-0.31758400797843933,-0.09913676977157593,-0.9442989826202393,-0.31380149722099304,-0.11185378581285477,-0.9442989826202393,-0.3094967007637024,-0.12438463419675827,-0.9442989826202393,-0.3046768009662628,-0.13670845329761505,-0.9442989826202393,-0.29934975504875183,-0.14880473911762238,-0.9442989826202393,-0.29352450370788574,-0.16065335273742676,-0.9442989826202393,-0.28721070289611816,-0.17223457992076874,-0.9442989826202393,-0.28041887283325195,-0.18352913856506348,-0.9442989826202393,-0.27316027879714966,-0.19451822340488434,-0.9442989826202393,-0.26544708013534546,-0.20518355071544647,-0.9442989826202393,-0.2572920322418213,-0.2155073881149292,-0.9442989826202393,-0.24870876967906952,-0.22547250986099243,-0.9442989826202393,-0.23971155285835266,-0.235062375664711,-0.9442989826202393,-0.23031537234783173,-0.2442609965801239,-0.9442989826202393,-0.22053582966327667,-0.25305306911468506,-0.9442989826202393,-0.21038924157619476,-0.2614239454269409,-0.9442989826202393,-0.19989247620105743,-0.2693597376346588,-0.9442989826202393,-0.18906301259994507,-0.2768471837043762,-0.9442989826202393,-0.17791888117790222,-0.28387385606765747,-0.9442989826202393,-0.16647860407829285,-0.2904280424118042,-0.9442989826202393,-0.15476125478744507,-0.2964988350868225,-0.9442989826202393,-0.14278630912303925,-0.302076131105423,-0.9442989826202393,-0.13057371973991394,-0.307150661945343,-0.9442989826202393,-0.11814379692077637,-0.31171396374702454,-0.9442989826202393,-0.10551723092794418,-0.31575846672058105,-0.9442989826202393,-0.09271504729986191,-0.31927740573883057,-0.9442989826202393,-0.07975855469703674,-0.3222649395465851,-0.9442989826202393,-0.06666930019855499,-0.32471609115600586,-0.9442989826202393,-0.053469087928533554,-0.32662680745124817,-0.9442989826202393,-0.04017988219857216,-0.32799386978149414,-0.9442989826202393,-0.026823798194527626,-0.3288150131702423,-0.9442989826202393,-0.013423069380223751,-0.32908886671066284,-0.9442989826202393,-8.060352952926611e-17,-0.26165926456451416,-0.9651603102684021,0.0,-0.26144149899482727,-0.9651603102684021,0.010672710835933685,-0.26078861951828003,-0.9651603102684021,0.021327657625079155,-0.2597016394138336,-0.9651603102684021,0.03194710984826088,-0.25818246603012085,-0.9651603102684021,0.04251338541507721,-0.2562335431575775,-0.9651603102684021,0.053008902817964554,-0.25385814905166626,-0.9651603102684021,0.06341619044542313,-0.25106021761894226,-0.9651603102684021,0.07371792942285538,-0.24784442782402039,-0.9651603102684021,0.08389697223901749,-0.24421614408493042,-0.9651603102684021,0.09393637627363205,-0.24018137156963348,-0.9651603102684021,0.10381943732500076,-0.2357468456029892,-0.9651603102684021,0.11352969706058502,-0.2309199422597885,-0.9651603102684021,0.1230509951710701,-0.22570869326591492,-0.9651603102684021,0.13236749172210693,-0.22012177109718323,-0.9651603102684021,0.14146366715431213,-0.2141684889793396,-0.9651603102684021,0.15032440423965454,-0.20785872638225555,-0.9651603102684021,0.15893493592739105,-0.20120301842689514,-0.9651603102684021,0.16728092730045319,-0.1942124217748642,-0.9651603102684021,0.17534850537776947,-0.1868985891342163,-0.9651603102684021,0.18312422931194305,-0.17927366495132446,-0.9651603102684021,0.19059516489505768,-0.1713503748178482,-0.9651603102684021,0.19774886965751648,-0.1631418764591217,-0.9651603102684021,0.20457343757152557,-0.1546618491411209,-0.9651603102684021,0.21105752885341644,-0.1459244042634964,-0.9651603102684021,0.21719032526016235,-0.13694407045841217,-0.9651603102684021,0.22296161949634552,-0.12773582339286804,-0.9651603102684021,0.2283618301153183,-0.1183149665594101,-0.9651603102684021,0.2333819568157196,-0.10869718343019485,-0.9651603102684021,0.2380136251449585,-0.09889848530292511,-0.9651603102684021,0.24224916100502014,-0.08893518149852753,-0.9651603102684021,0.24608148634433746,-0.07882385700941086,-0.9651603102684021,0.2495042383670807,-0.06858132779598236,-0.9651603102684021,0.2525117099285126,-0.05822466313838959,-0.9651603102684021,0.2550989091396332,-0.04777108505368233,-0.9651603102684021,0.2572615146636963,-0.037237994372844696,-0.9651603102684021,0.2589959502220154,-0.026642926037311554,-0.9651603102684021,0.26029929518699646,-0.01600351557135582,-0.9651603102684021,0.2611694037914276,-0.005337466020137072,-0.9651603102684021,0.26160481572151184,0.005337466020137072,-0.9651603102684021,0.26160481572151184,0.01600351557135582,-0.9651603102684021,0.2611694037914276,0.026642926037311554,-0.9651603102684021,0.26029929518699646,0.037237994372844696,-0.9651603102684021,0.2589959502220154,0.04777108505368233,-0.9651603102684021,0.2572615146636963,0.05822466313838959,-0.9651603102684021,0.2550989091396332,0.06858132779598236,-0.9651603102684021,0.2525117099285126,0.07882385700941086,-0.9651603102684021,0.2495042383670807,0.08893518149852753,-0.9651603102684021,0.24608148634433746,0.09889848530292511,-0.9651603102684021,0.24224916100502014,0.10869718343019485,-0.9651603102684021,0.2380136251449585,0.1183149665594101,-0.9651603102684021,0.2333819568157196,0.12773582339286804,-0.9651603102684021,0.2283618301153183,0.13694407045841217,-0.9651603102684021,0.22296161949634552,0.1459244042634964,-0.9651603102684021,0.21719032526016235,0.1546618491411209,-0.9651603102684021,0.21105752885341644,0.1631418764591217,-0.9651603102684021,0.20457343757152557,0.1713503748178482,-0.9651603102684021,0.19774886965751648,0.17927366495132446,-0.9651603102684021,0.19059516489505768,0.1868985891342163,-0.9651603102684021,0.18312422931194305,0.1942124217748642,-0.9651603102684021,0.17534850537776947,0.20120301842689514,-0.9651603102684021,0.16728092730045319,0.20785872638225555,-0.9651603102684021,0.15893493592739105,0.2141684889793396,-0.9651603102684021,0.15032440423965454,0.22012177109718323,-0.9651603102684021,0.14146366715431213,0.22570869326591492,-0.9651603102684021,0.13236749172210693,0.2309199422597885,-0.9651603102684021,0.1230509951710701,0.2357468456029892,-0.9651603102684021,0.11352969706058502,0.24018137156963348,-0.9651603102684021,0.10381943732500076,0.24421614408493042,-0.9651603102684021,0.09393637627363205,0.24784442782402039,-0.9651603102684021,0.08389697223901749,0.25106021761894226,-0.9651603102684021,0.07371792942285538,0.25385814905166626,-0.9651603102684021,0.06341619044542313,0.2562335431575775,-0.9651603102684021,0.053008902817964554,0.25818246603012085,-0.9651603102684021,0.04251338541507721,0.2597016394138336,-0.9651603102684021,0.03194710984826088,0.26078861951828003,-0.9651603102684021,0.021327657625079155,0.26144149899482727,-0.9651603102684021,0.010672710835933685,0.26165926456451416,-0.9651603102684021,3.204401740079373e-17,0.26144149899482727,-0.9651603102684021,-0.010672710835933685,0.26078861951828003,-0.9651603102684021,-0.021327657625079155,0.2597016394138336,-0.9651603102684021,-0.03194710984826088,0.25818246603012085,-0.9651603102684021,-0.04251338541507721,0.2562335431575775,-0.9651603102684021,-0.053008902817964554,0.25385814905166626,-0.9651603102684021,-0.06341619044542313,0.25106021761894226,-0.9651603102684021,-0.07371792942285538,0.24784442782402039,-0.9651603102684021,-0.08389697223901749,0.24421614408493042,-0.9651603102684021,-0.09393637627363205,0.24018137156963348,-0.9651603102684021,-0.10381943732500076,0.2357468456029892,-0.9651603102684021,-0.11352969706058502,0.2309199422597885,-0.9651603102684021,-0.1230509951710701,0.22570869326591492,-0.9651603102684021,-0.13236749172210693,0.22012177109718323,-0.9651603102684021,-0.14146366715431213,0.2141684889793396,-0.9651603102684021,-0.15032440423965454,0.20785872638225555,-0.9651603102684021,-0.15893493592739105,0.20120301842689514,-0.9651603102684021,-0.16728092730045319,0.1942124217748642,-0.9651603102684021,-0.17534850537776947,0.1868985891342163,-0.9651603102684021,-0.18312422931194305,0.17927366495132446,-0.9651603102684021,-0.19059516489505768,0.1713503748178482,-0.9651603102684021,-0.19774886965751648,0.1631418764591217,-0.9651603102684021,-0.20457343757152557,0.1546618491411209,-0.9651603102684021,-0.21105752885341644,0.1459244042634964,-0.9651603102684021,-0.21719032526016235,0.13694407045841217,-0.9651603102684021,-0.22296161949634552,0.12773582339286804,-0.9651603102684021,-0.2283618301153183,0.1183149665594101,-0.9651603102684021,-0.2333819568157196,0.10869718343019485,-0.9651603102684021,-0.2380136251449585,0.09889848530292511,-0.9651603102684021,-0.24224916100502014,0.08893518149852753,-0.9651603102684021,-0.24608148634433746,0.07882385700941086,-0.9651603102684021,-0.2495042383670807,0.06858132779598236,-0.9651603102684021,-0.2525117099285126,0.05822466313838959,-0.9651603102684021,-0.2550989091396332,0.04777108505368233,-0.9651603102684021,-0.2572615146636963,0.037237994372844696,-0.9651603102684021,-0.2589959502220154,0.026642926037311554,-0.9651603102684021,-0.26029929518699646,0.01600351557135582,-0.9651603102684021,-0.2611694037914276,0.005337466020137072,-0.9651603102684021,-0.26160481572151184,-0.005337466020137072,-0.9651603102684021,-0.26160481572151184,-0.01600351557135582,-0.9651603102684021,-0.2611694037914276,-0.026642926037311554,-0.9651603102684021,-0.26029929518699646,-0.037237994372844696,-0.9651603102684021,-0.2589959502220154,-0.04777108505368233,-0.9651603102684021,-0.2572615146636963,-0.05822466313838959,-0.9651603102684021,-0.2550989091396332,-0.06858132779598236,-0.9651603102684021,-0.2525117099285126,-0.07882385700941086,-0.9651603102684021,-0.2495042383670807,-0.08893518149852753,-0.9651603102684021,-0.24608148634433746,-0.09889848530292511,-0.9651603102684021,-0.24224916100502014,-0.10869718343019485,-0.9651603102684021,-0.2380136251449585,-0.1183149665594101,-0.9651603102684021,-0.2333819568157196,-0.12773582339286804,-0.9651603102684021,-0.2283618301153183,-0.13694407045841217,-0.9651603102684021,-0.22296161949634552,-0.1459244042634964,-0.9651603102684021,-0.21719032526016235,-0.1546618491411209,-0.9651603102684021,-0.21105752885341644,-0.1631418764591217,-0.9651603102684021,-0.20457343757152557,-0.1713503748178482,-0.9651603102684021,-0.19774886965751648,-0.17927366495132446,-0.9651603102684021,-0.19059516489505768,-0.1868985891342163,-0.9651603102684021,-0.18312422931194305,-0.1942124217748642,-0.9651603102684021,-0.17534850537776947,-0.20120301842689514,-0.9651603102684021,-0.16728092730045319,-0.20785872638225555,-0.9651603102684021,-0.15893493592739105,-0.2141684889793396,-0.9651603102684021,-0.15032440423965454,-0.22012177109718323,-0.9651603102684021,-0.14146366715431213,-0.22570869326591492,-0.9651603102684021,-0.13236749172210693,-0.2309199422597885,-0.9651603102684021,-0.1230509951710701,-0.2357468456029892,-0.9651603102684021,-0.11352969706058502,-0.24018137156963348,-0.9651603102684021,-0.10381943732500076,-0.24421614408493042,-0.9651603102684021,-0.09393637627363205,-0.24784442782402039,-0.9651603102684021,-0.08389697223901749,-0.25106021761894226,-0.9651603102684021,-0.07371792942285538,-0.25385814905166626,-0.9651603102684021,-0.06341619044542313,-0.2562335431575775,-0.9651603102684021,-0.053008902817964554,-0.25818246603012085,-0.9651603102684021,-0.04251338541507721,-0.2597016394138336,-0.9651603102684021,-0.03194710984826088,-0.26078861951828003,-0.9651603102684021,-0.021327657625079155,-0.26144149899482727,-0.9651603102684021,-0.010672710835933685,-0.26165926456451416,-0.9651603102684021,-6.408803480158746e-17,-0.1929260641336441,-0.9812132716178894,0.0,-0.1927655190229416,-0.9812132716178894,0.007869181223213673,-0.19228412210941315,-0.9812132716178894,0.015725266188383102,-0.19148269295692444,-0.9812132716178894,0.023555176332592964,-0.19036255776882172,-0.9812132716178894,0.03134588152170181,-0.1889255791902542,-0.9812132716178894,0.03908441588282585,-0.18717415630817413,-0.9812132716178894,0.04675789549946785,-0.18511120975017548,-0.9812132716178894,0.05435355380177498,-0.18274015188217163,-0.9812132716178894,0.06185874342918396,-0.18006494641304016,-0.9812132716178894,0.06926097720861435,-0.17709003388881683,-0.9812132716178894,0.07654793560504913,-0.1738203763961792,-0.9812132716178894,0.08370748162269592,-0.170261412858963,-0.9812132716178894,0.09072770923376083,-0.16641907393932343,-0.9812132716178894,0.09759692847728729,-0.1622997373342514,-0.9812132716178894,0.10430370271205902,-0.1579102724790573,-0.9812132716178894,0.11083687841892242,-0.15325798094272614,-0.9812132716178894,0.117185577750206,-0.14835059642791748,-0.9812132716178894,0.12333923578262329,-0.14319631457328796,-0.9812132716178894,0.12928760051727295,-0.13780368864536285,-0.9812132716178894,0.13502077758312225,-0.13218168914318085,-0.9812132716178894,0.14052923023700714,-0.12633970379829407,-0.9812132716178894,0.1458037942647934,-0.12028743326663971,-0.9812132716178894,0.15083567798137665,-0.11403495818376541,-0.9812132716178894,0.15561650693416595,-0.10759267956018448,-0.9812132716178894,0.1601383239030838,-0.10097132623195648,-0.9812132716178894,0.1643936038017273,-0.09418191760778427,-0.9812132716178894,0.16837528347969055,-0.08723574876785278,-0.9812132716178894,0.17207670211791992,-0.08014438301324844,-0.9812132716178894,0.17549172043800354,-0.07291962951421738,-0.9812132716178894,0.17861464619636536,-0.06557350605726242,-0.9812132716178894,0.1814402937889099,-0.058118242770433426,-0.9812132716178894,0.18396395444869995,-0.05056624487042427,-0.9812132716178894,0.1861814260482788,-0.04293008893728256,-0.9812132716178894,0.1880890130996704,-0.035222478210926056,-0.9812132716178894,0.18968354165554047,-0.027456242591142654,-0.9812132716178894,0.19096235930919647,-0.01964430883526802,-0.9812132716178894,0.1919233351945877,-0.011799678206443787,-0.9812132716178894,0.19256488978862762,-0.003935409709811211,-0.9812132716178894,0.19288592040538788,0.003935409709811211,-0.9812132716178894,0.19288592040538788,0.011799678206443787,-0.9812132716178894,0.19256488978862762,0.01964430883526802,-0.9812132716178894,0.1919233351945877,0.027456242591142654,-0.9812132716178894,0.19096235930919647,0.035222478210926056,-0.9812132716178894,0.18968354165554047,0.04293008893728256,-0.9812132716178894,0.1880890130996704,0.05056624487042427,-0.9812132716178894,0.1861814260482788,0.058118242770433426,-0.9812132716178894,0.18396395444869995,0.06557350605726242,-0.9812132716178894,0.1814402937889099,0.07291962951421738,-0.9812132716178894,0.17861464619636536,0.08014438301324844,-0.9812132716178894,0.17549172043800354,0.08723574876785278,-0.9812132716178894,0.17207670211791992,0.09418191760778427,-0.9812132716178894,0.16837528347969055,0.10097132623195648,-0.9812132716178894,0.1643936038017273,0.10759267956018448,-0.9812132716178894,0.1601383239030838,0.11403495818376541,-0.9812132716178894,0.15561650693416595,0.12028743326663971,-0.9812132716178894,0.15083567798137665,0.12633970379829407,-0.9812132716178894,0.1458037942647934,0.13218168914318085,-0.9812132716178894,0.14052923023700714,0.13780368864536285,-0.9812132716178894,0.13502077758312225,0.14319631457328796,-0.9812132716178894,0.12928760051727295,0.14835059642791748,-0.9812132716178894,0.12333923578262329,0.15325798094272614,-0.9812132716178894,0.117185577750206,0.1579102724790573,-0.9812132716178894,0.11083687841892242,0.1622997373342514,-0.9812132716178894,0.10430370271205902,0.16641907393932343,-0.9812132716178894,0.09759692847728729,0.170261412858963,-0.9812132716178894,0.09072770923376083,0.1738203763961792,-0.9812132716178894,0.08370748162269592,0.17709003388881683,-0.9812132716178894,0.07654793560504913,0.18006494641304016,-0.9812132716178894,0.06926097720861435,0.18274015188217163,-0.9812132716178894,0.06185874342918396,0.18511120975017548,-0.9812132716178894,0.05435355380177498,0.18717415630817413,-0.9812132716178894,0.04675789549946785,0.1889255791902542,-0.9812132716178894,0.03908441588282585,0.19036255776882172,-0.9812132716178894,0.03134588152170181,0.19148269295692444,-0.9812132716178894,0.023555176332592964,0.19228412210941315,-0.9812132716178894,0.015725266188383102,0.1927655190229416,-0.9812132716178894,0.007869181223213673,0.1929260641336441,-0.9812132716178894,2.3626629141815346e-17,0.1927655190229416,-0.9812132716178894,-0.007869181223213673,0.19228412210941315,-0.9812132716178894,-0.015725266188383102,0.19148269295692444,-0.9812132716178894,-0.023555176332592964,0.19036255776882172,-0.9812132716178894,-0.03134588152170181,0.1889255791902542,-0.9812132716178894,-0.03908441588282585,0.18717415630817413,-0.9812132716178894,-0.04675789549946785,0.18511120975017548,-0.9812132716178894,-0.05435355380177498,0.18274015188217163,-0.9812132716178894,-0.06185874342918396,0.18006494641304016,-0.9812132716178894,-0.06926097720861435,0.17709003388881683,-0.9812132716178894,-0.07654793560504913,0.1738203763961792,-0.9812132716178894,-0.08370748162269592,0.170261412858963,-0.9812132716178894,-0.09072770923376083,0.16641907393932343,-0.9812132716178894,-0.09759692847728729,0.1622997373342514,-0.9812132716178894,-0.10430370271205902,0.1579102724790573,-0.9812132716178894,-0.11083687841892242,0.15325798094272614,-0.9812132716178894,-0.117185577750206,0.14835059642791748,-0.9812132716178894,-0.12333923578262329,0.14319631457328796,-0.9812132716178894,-0.12928760051727295,0.13780368864536285,-0.9812132716178894,-0.13502077758312225,0.13218168914318085,-0.9812132716178894,-0.14052923023700714,0.12633970379829407,-0.9812132716178894,-0.1458037942647934,0.12028743326663971,-0.9812132716178894,-0.15083567798137665,0.11403495818376541,-0.9812132716178894,-0.15561650693416595,0.10759267956018448,-0.9812132716178894,-0.1601383239030838,0.10097132623195648,-0.9812132716178894,-0.1643936038017273,0.09418191760778427,-0.9812132716178894,-0.16837528347969055,0.08723574876785278,-0.9812132716178894,-0.17207670211791992,0.08014438301324844,-0.9812132716178894,-0.17549172043800354,0.07291962951421738,-0.9812132716178894,-0.17861464619636536,0.06557350605726242,-0.9812132716178894,-0.1814402937889099,0.058118242770433426,-0.9812132716178894,-0.18396395444869995,0.05056624487042427,-0.9812132716178894,-0.1861814260482788,0.04293008893728256,-0.9812132716178894,-0.1880890130996704,0.035222478210926056,-0.9812132716178894,-0.18968354165554047,0.027456242591142654,-0.9812132716178894,-0.19096235930919647,0.01964430883526802,-0.9812132716178894,-0.1919233351945877,0.011799678206443787,-0.9812132716178894,-0.19256488978862762,0.003935409709811211,-0.9812132716178894,-0.19288592040538788,-0.003935409709811211,-0.9812132716178894,-0.19288592040538788,-0.011799678206443787,-0.9812132716178894,-0.19256488978862762,-0.01964430883526802,-0.9812132716178894,-0.1919233351945877,-0.027456242591142654,-0.9812132716178894,-0.19096235930919647,-0.035222478210926056,-0.9812132716178894,-0.18968354165554047,-0.04293008893728256,-0.9812132716178894,-0.1880890130996704,-0.05056624487042427,-0.9812132716178894,-0.1861814260482788,-0.058118242770433426,-0.9812132716178894,-0.18396395444869995,-0.06557350605726242,-0.9812132716178894,-0.1814402937889099,-0.07291962951421738,-0.9812132716178894,-0.17861464619636536,-0.08014438301324844,-0.9812132716178894,-0.17549172043800354,-0.08723574876785278,-0.9812132716178894,-0.17207670211791992,-0.09418191760778427,-0.9812132716178894,-0.16837528347969055,-0.10097132623195648,-0.9812132716178894,-0.1643936038017273,-0.10759267956018448,-0.9812132716178894,-0.1601383239030838,-0.11403495818376541,-0.9812132716178894,-0.15561650693416595,-0.12028743326663971,-0.9812132716178894,-0.15083567798137665,-0.12633970379829407,-0.9812132716178894,-0.1458037942647934,-0.13218168914318085,-0.9812132716178894,-0.14052923023700714,-0.13780368864536285,-0.9812132716178894,-0.13502077758312225,-0.14319631457328796,-0.9812132716178894,-0.12928760051727295,-0.14835059642791748,-0.9812132716178894,-0.12333923578262329,-0.15325798094272614,-0.9812132716178894,-0.117185577750206,-0.1579102724790573,-0.9812132716178894,-0.11083687841892242,-0.1622997373342514,-0.9812132716178894,-0.10430370271205902,-0.16641907393932343,-0.9812132716178894,-0.09759692847728729,-0.170261412858963,-0.9812132716178894,-0.09072770923376083,-0.1738203763961792,-0.9812132716178894,-0.08370748162269592,-0.17709003388881683,-0.9812132716178894,-0.07654793560504913,-0.18006494641304016,-0.9812132716178894,-0.06926097720861435,-0.18274015188217163,-0.9812132716178894,-0.06185874342918396,-0.18511120975017548,-0.9812132716178894,-0.05435355380177498,-0.18717415630817413,-0.9812132716178894,-0.04675789549946785,-0.1889255791902542,-0.9812132716178894,-0.03908441588282585,-0.19036255776882172,-0.9812132716178894,-0.03134588152170181,-0.19148269295692444,-0.9812132716178894,-0.023555176332592964,-0.19228412210941315,-0.9812132716178894,-0.015725266188383102,-0.1927655190229416,-0.9812132716178894,-0.007869181223213673,-0.1929260641336441,-0.9812132716178894,-4.725325828363069e-17,-0.12323172390460968,-0.9923779368400574,0.0,-0.12312917411327362,-0.9923779368400574,0.005026448052376509,-0.12282168120145798,-0.9923779368400574,0.010044530034065247,-0.12230976670980453,-0.9923779368400574,0.015045893378555775,-0.12159428000450134,-0.9923779368400574,0.020022215321660042,-0.12067641317844391,-0.9923779368400574,0.024965211749076843,-0.1195576936006546,-0.9923779368400574,0.0298666562885046,-0.11823997646570206,-0.9923779368400574,0.03471839055418968,-0.11672545969486237,-0.9923779368400574,0.03951233997941017,-0.11501666903495789,-0.9923779368400574,0.044240523129701614,-0.11311644315719604,-0.9923779368400574,0.04889507219195366,-0.11102794855833054,-0.9923779368400574,0.0534682422876358,-0.10875465720891953,-0.9923779368400574,0.05795241892337799,-0.10630035400390625,-0.9923779368400574,0.0623401403427124,-0.10366912931203842,-0.9923779368400574,0.06662410497665405,-0.10086534917354584,-0.9923779368400574,0.07079717516899109,-0.09789369255304337,-0.9923779368400574,0.07485241442918777,-0.09475909918546677,-0.9923779368400574,0.0787830650806427,-0.09146679192781448,-0.9923779368400574,0.08258259296417236,-0.08802224695682526,-0.9923779368400574,0.08624466508626938,-0.08443119376897812,-0.9923779368400574,0.08976319432258606,-0.080699622631073,-0.9923779368400574,0.09313232451677322,-0.07683372497558594,-0.9923779368400574,0.09634644538164139,-0.07283994555473328,-0.9923779368400574,0.09940019994974136,-0.06872493773698807,-0.9923779368400574,0.10228852182626724,-0.06449554115533829,-0.9923779368400574,0.10500659048557281,-0.06015879660844803,-0.9923779368400574,0.10754988342523575,-0.05572192370891571,-0.9923779368400574,0.10991417616605759,-0.05119230970740318,-0.9923779368400574,0.11209551990032196,-0.046577490866184235,-0.9923779368400574,0.11409030109643936,-0.041885145008563995,-0.9923779368400574,0.11589518189430237,-0.03712308779358864,-0.9923779368400574,0.11750716716051102,-0.03229924291372299,-0.9923779368400574,0.11892357468605042,-0.02742163836956024,-0.9923779368400574,0.12014205008745193,-0.022498393431305885,-0.9923779368400574,0.12116055935621262,-0.01753770373761654,-0.9923779368400574,0.12197740375995636,-0.012547822669148445,-0.9923779368400574,0.12259123474359512,-0.0075370571576058865,-0.9923779368400574,0.12300102412700653,-0.002513746963813901,-0.9923779368400574,0.12320608645677567,0.002513746963813901,-0.9923779368400574,0.12320608645677567,0.0075370571576058865,-0.9923779368400574,0.12300102412700653,0.012547822669148445,-0.9923779368400574,0.12259123474359512,0.01753770373761654,-0.9923779368400574,0.12197740375995636,0.022498393431305885,-0.9923779368400574,0.12116055935621262,0.02742163836956024,-0.9923779368400574,0.12014205008745193,0.03229924291372299,-0.9923779368400574,0.11892357468605042,0.03712308779358864,-0.9923779368400574,0.11750716716051102,0.041885145008563995,-0.9923779368400574,0.11589518189430237,0.046577490866184235,-0.9923779368400574,0.11409030109643936,0.05119230970740318,-0.9923779368400574,0.11209551990032196,0.05572192370891571,-0.9923779368400574,0.10991417616605759,0.06015879660844803,-0.9923779368400574,0.10754988342523575,0.06449554115533829,-0.9923779368400574,0.10500659048557281,0.06872493773698807,-0.9923779368400574,0.10228852182626724,0.07283994555473328,-0.9923779368400574,0.09940019994974136,0.07683372497558594,-0.9923779368400574,0.09634644538164139,0.080699622631073,-0.9923779368400574,0.09313232451677322,0.08443119376897812,-0.9923779368400574,0.08976319432258606,0.08802224695682526,-0.9923779368400574,0.08624466508626938,0.09146679192781448,-0.9923779368400574,0.08258259296417236,0.09475909918546677,-0.9923779368400574,0.0787830650806427,0.09789369255304337,-0.9923779368400574,0.07485241442918777,0.10086534917354584,-0.9923779368400574,0.07079717516899109,0.10366912931203842,-0.9923779368400574,0.06662410497665405,0.10630035400390625,-0.9923779368400574,0.0623401403427124,0.10875465720891953,-0.9923779368400574,0.05795241892337799,0.11102794855833054,-0.9923779368400574,0.0534682422876358,0.11311644315719604,-0.9923779368400574,0.04889507219195366,0.11501666903495789,-0.9923779368400574,0.044240523129701614,0.11672545969486237,-0.9923779368400574,0.03951233997941017,0.11823997646570206,-0.9923779368400574,0.03471839055418968,0.1195576936006546,-0.9923779368400574,0.0298666562885046,0.12067641317844391,-0.9923779368400574,0.024965211749076843,0.12159428000450134,-0.9923779368400574,0.020022215321660042,0.12230976670980453,-0.9923779368400574,0.015045893378555775,0.12282168120145798,-0.9923779368400574,0.010044530034065247,0.12312917411327362,-0.9923779368400574,0.005026448052376509,0.12323172390460968,-0.9923779368400574,1.509153473603189e-17,0.12312917411327362,-0.9923779368400574,-0.005026448052376509,0.12282168120145798,-0.9923779368400574,-0.010044530034065247,0.12230976670980453,-0.9923779368400574,-0.015045893378555775,0.12159428000450134,-0.9923779368400574,-0.020022215321660042,0.12067641317844391,-0.9923779368400574,-0.024965211749076843,0.1195576936006546,-0.9923779368400574,-0.0298666562885046,0.11823997646570206,-0.9923779368400574,-0.03471839055418968,0.11672545969486237,-0.9923779368400574,-0.03951233997941017,0.11501666903495789,-0.9923779368400574,-0.044240523129701614,0.11311644315719604,-0.9923779368400574,-0.04889507219195366,0.11102794855833054,-0.9923779368400574,-0.0534682422876358,0.10875465720891953,-0.9923779368400574,-0.05795241892337799,0.10630035400390625,-0.9923779368400574,-0.0623401403427124,0.10366912931203842,-0.9923779368400574,-0.06662410497665405,0.10086534917354584,-0.9923779368400574,-0.07079717516899109,0.09789369255304337,-0.9923779368400574,-0.07485241442918777,0.09475909918546677,-0.9923779368400574,-0.0787830650806427,0.09146679192781448,-0.9923779368400574,-0.08258259296417236,0.08802224695682526,-0.9923779368400574,-0.08624466508626938,0.08443119376897812,-0.9923779368400574,-0.08976319432258606,0.080699622631073,-0.9923779368400574,-0.09313232451677322,0.07683372497558594,-0.9923779368400574,-0.09634644538164139,0.07283994555473328,-0.9923779368400574,-0.09940019994974136,0.06872493773698807,-0.9923779368400574,-0.10228852182626724,0.06449554115533829,-0.9923779368400574,-0.10500659048557281,0.06015879660844803,-0.9923779368400574,-0.10754988342523575,0.05572192370891571,-0.9923779368400574,-0.10991417616605759,0.05119230970740318,-0.9923779368400574,-0.11209551990032196,0.046577490866184235,-0.9923779368400574,-0.11409030109643936,0.041885145008563995,-0.9923779368400574,-0.11589518189430237,0.03712308779358864,-0.9923779368400574,-0.11750716716051102,0.03229924291372299,-0.9923779368400574,-0.11892357468605042,0.02742163836956024,-0.9923779368400574,-0.12014205008745193,0.022498393431305885,-0.9923779368400574,-0.12116055935621262,0.01753770373761654,-0.9923779368400574,-0.12197740375995636,0.012547822669148445,-0.9923779368400574,-0.12259123474359512,0.0075370571576058865,-0.9923779368400574,-0.12300102412700653,0.002513746963813901,-0.9923779368400574,-0.12320608645677567,-0.002513746963813901,-0.9923779368400574,-0.12320608645677567,-0.0075370571576058865,-0.9923779368400574,-0.12300102412700653,-0.012547822669148445,-0.9923779368400574,-0.12259123474359512,-0.01753770373761654,-0.9923779368400574,-0.12197740375995636,-0.022498393431305885,-0.9923779368400574,-0.12116055935621262,-0.02742163836956024,-0.9923779368400574,-0.12014205008745193,-0.03229924291372299,-0.9923779368400574,-0.11892357468605042,-0.03712308779358864,-0.9923779368400574,-0.11750716716051102,-0.041885145008563995,-0.9923779368400574,-0.11589518189430237,-0.046577490866184235,-0.9923779368400574,-0.11409030109643936,-0.05119230970740318,-0.9923779368400574,-0.11209551990032196,-0.05572192370891571,-0.9923779368400574,-0.10991417616605759,-0.06015879660844803,-0.9923779368400574,-0.10754988342523575,-0.06449554115533829,-0.9923779368400574,-0.10500659048557281,-0.06872493773698807,-0.9923779368400574,-0.10228852182626724,-0.07283994555473328,-0.9923779368400574,-0.09940019994974136,-0.07683372497558594,-0.9923779368400574,-0.09634644538164139,-0.080699622631073,-0.9923779368400574,-0.09313232451677322,-0.08443119376897812,-0.9923779368400574,-0.08976319432258606,-0.08802224695682526,-0.9923779368400574,-0.08624466508626938,-0.09146679192781448,-0.9923779368400574,-0.08258259296417236,-0.09475909918546677,-0.9923779368400574,-0.0787830650806427,-0.09789369255304337,-0.9923779368400574,-0.07485241442918777,-0.10086534917354584,-0.9923779368400574,-0.07079717516899109,-0.10366912931203842,-0.9923779368400574,-0.06662410497665405,-0.10630035400390625,-0.9923779368400574,-0.0623401403427124,-0.10875465720891953,-0.9923779368400574,-0.05795241892337799,-0.11102794855833054,-0.9923779368400574,-0.0534682422876358,-0.11311644315719604,-0.9923779368400574,-0.04889507219195366,-0.11501666903495789,-0.9923779368400574,-0.044240523129701614,-0.11672545969486237,-0.9923779368400574,-0.03951233997941017,-0.11823997646570206,-0.9923779368400574,-0.03471839055418968,-0.1195576936006546,-0.9923779368400574,-0.0298666562885046,-0.12067641317844391,-0.9923779368400574,-0.024965211749076843,-0.12159428000450134,-0.9923779368400574,-0.020022215321660042,-0.12230976670980453,-0.9923779368400574,-0.015045893378555775,-0.12282168120145798,-0.9923779368400574,-0.010044530034065247,-0.12312917411327362,-0.9923779368400574,-0.005026448052376509,-0.12323172390460968,-0.9923779368400574,-3.018306947206378e-17,-0.05292345583438873,-0.998598575592041,0.0,-0.05287941172719002,-0.998598575592041,0.002158672781661153,-0.05274735391139984,-0.998598575592041,0.0043137529864907265,-0.05252750590443611,-0.998598575592041,0.006461652927100658,-0.05222023278474808,-0.998598575592041,0.00859879795461893,-0.05182604119181633,-0.998598575592041,0.010721632279455662,-0.0513455905020237,-0.998598575592041,0.012826620601117611,-0.05077968165278435,-0.998598575592041,0.01491026021540165,-0.050129253417253494,-0.998598575592041,0.01696908287703991,-0.04939539358019829,-0.998598575592041,0.01899966225028038,-0.04857931658625603,-0.998598575592041,0.020998619496822357,-0.04768238589167595,-0.998598575592041,0.022962626069784164,-0.04670609161257744,-0.998598575592041,0.0248884130269289,-0.045652057975530624,-0.998598575592041,0.02677277661859989,-0.04452204331755638,-0.998598575592041,0.02861258015036583,-0.043317925184965134,-0.998598575592041,0.03040475957095623,-0.042041707783937454,-0.998598575592041,0.032146334648132324,-0.040695518255233765,-0.998598575592041,0.03383440151810646,-0.03928159177303314,-0.998598575592041,0.03546615689992905,-0.03780229017138481,-0.998598575592041,0.037038885056972504,-0.03626006469130516,-0.998598575592041,0.038549963384866714,-0.034657493233680725,-0.998598575592041,0.03999687731266022,-0.03299723565578461,-0.998598575592041,0.041377224028110504,-0.031282056123018265,-0.998598575592041,0.04268869757652283,-0.02951481007039547,-0.998598575592041,0.043929122388362885,-0.027698442339897156,-0.998598575592041,0.04509643465280533,-0.025835970416665077,-0.998598575592041,0.04618868604302406,-0.023930499330163002,-0.998598575592041,0.04720405861735344,-0.021985197439789772,-0.998598575592041,0.04814086854457855,-0.0200033038854599,-0.998598575592041,0.04899755120277405,-0.017988115549087524,-0.998598575592041,0.049772679805755615,-0.01594298891723156,-0.998598575592041,0.050464969128370285,-0.013871326111257076,-0.998598575592041,0.051073264330625534,-0.01177657674998045,-0.998598575592041,0.05159655213356018,-0.009662225842475891,-0.998598575592041,0.05203396454453468,-0.0075317928567528725,-0.998598575592041,0.05238477140665054,-0.0053888242691755295,-0.998598575592041,0.0526483878493309,-0.0032368863467127085,-0.998598575592041,0.05282437428832054,-0.0010795610724017024,-0.998598575592041,0.05291244387626648,0.0010795610724017024,-0.998598575592041,0.05291244387626648,0.0032368863467127085,-0.998598575592041,0.05282437428832054,0.0053888242691755295,-0.998598575592041,0.0526483878493309,0.0075317928567528725,-0.998598575592041,0.05238477140665054,0.009662225842475891,-0.998598575592041,0.05203396454453468,0.01177657674998045,-0.998598575592041,0.05159655213356018,0.013871326111257076,-0.998598575592041,0.051073264330625534,0.01594298891723156,-0.998598575592041,0.050464969128370285,0.017988115549087524,-0.998598575592041,0.049772679805755615,0.0200033038854599,-0.998598575592041,0.04899755120277405,0.021985197439789772,-0.998598575592041,0.04814086854457855,0.023930499330163002,-0.998598575592041,0.04720405861735344,0.025835970416665077,-0.998598575592041,0.04618868604302406,0.027698442339897156,-0.998598575592041,0.04509643465280533,0.02951481007039547,-0.998598575592041,0.043929122388362885,0.031282056123018265,-0.998598575592041,0.04268869757652283,0.03299723565578461,-0.998598575592041,0.041377224028110504,0.034657493233680725,-0.998598575592041,0.03999687731266022,0.03626006469130516,-0.998598575592041,0.038549963384866714,0.03780229017138481,-0.998598575592041,0.037038885056972504,0.03928159177303314,-0.998598575592041,0.03546615689992905,0.040695518255233765,-0.998598575592041,0.03383440151810646,0.042041707783937454,-0.998598575592041,0.032146334648132324,0.043317925184965134,-0.998598575592041,0.03040475957095623,0.04452204331755638,-0.998598575592041,0.02861258015036583,0.045652057975530624,-0.998598575592041,0.02677277661859989,0.04670609161257744,-0.998598575592041,0.0248884130269289,0.04768238589167595,-0.998598575592041,0.022962626069784164,0.04857931658625603,-0.998598575592041,0.020998619496822357,0.04939539358019829,-0.998598575592041,0.01899966225028038,0.050129253417253494,-0.998598575592041,0.01696908287703991,0.05077968165278435,-0.998598575592041,0.01491026021540165,0.0513455905020237,-0.998598575592041,0.012826620601117611,0.05182604119181633,-0.998598575592041,0.010721632279455662,0.05222023278474808,-0.998598575592041,0.00859879795461893,0.05252750590443611,-0.998598575592041,0.006461652927100658,0.05274735391139984,-0.998598575592041,0.0043137529864907265,0.05287941172719002,-0.998598575592041,0.002158672781661153,0.05292345583438873,-0.998598575592041,6.481253748470428e-18,0.05287941172719002,-0.998598575592041,-0.002158672781661153,0.05274735391139984,-0.998598575592041,-0.0043137529864907265,0.05252750590443611,-0.998598575592041,-0.006461652927100658,0.05222023278474808,-0.998598575592041,-0.00859879795461893,0.05182604119181633,-0.998598575592041,-0.010721632279455662,0.0513455905020237,-0.998598575592041,-0.012826620601117611,0.05077968165278435,-0.998598575592041,-0.01491026021540165,0.050129253417253494,-0.998598575592041,-0.01696908287703991,0.04939539358019829,-0.998598575592041,-0.01899966225028038,0.04857931658625603,-0.998598575592041,-0.020998619496822357,0.04768238589167595,-0.998598575592041,-0.022962626069784164,0.04670609161257744,-0.998598575592041,-0.0248884130269289,0.045652057975530624,-0.998598575592041,-0.02677277661859989,0.04452204331755638,-0.998598575592041,-0.02861258015036583,0.043317925184965134,-0.998598575592041,-0.03040475957095623,0.042041707783937454,-0.998598575592041,-0.032146334648132324,0.040695518255233765,-0.998598575592041,-0.03383440151810646,0.03928159177303314,-0.998598575592041,-0.03546615689992905,0.03780229017138481,-0.998598575592041,-0.037038885056972504,0.03626006469130516,-0.998598575592041,-0.038549963384866714,0.034657493233680725,-0.998598575592041,-0.03999687731266022,0.03299723565578461,-0.998598575592041,-0.041377224028110504,0.031282056123018265,-0.998598575592041,-0.04268869757652283,0.02951481007039547,-0.998598575592041,-0.043929122388362885,0.027698442339897156,-0.998598575592041,-0.04509643465280533,0.025835970416665077,-0.998598575592041,-0.04618868604302406,0.023930499330163002,-0.998598575592041,-0.04720405861735344,0.021985197439789772,-0.998598575592041,-0.04814086854457855,0.0200033038854599,-0.998598575592041,-0.04899755120277405,0.017988115549087524,-0.998598575592041,-0.049772679805755615,0.01594298891723156,-0.998598575592041,-0.050464969128370285,0.013871326111257076,-0.998598575592041,-0.051073264330625534,0.01177657674998045,-0.998598575592041,-0.05159655213356018,0.009662225842475891,-0.998598575592041,-0.05203396454453468,0.0075317928567528725,-0.998598575592041,-0.05238477140665054,0.0053888242691755295,-0.998598575592041,-0.0526483878493309,0.0032368863467127085,-0.998598575592041,-0.05282437428832054,0.0010795610724017024,-0.998598575592041,-0.05291244387626648,-0.0010795610724017024,-0.998598575592041,-0.05291244387626648,-0.0032368863467127085,-0.998598575592041,-0.05282437428832054,-0.0053888242691755295,-0.998598575592041,-0.0526483878493309,-0.0075317928567528725,-0.998598575592041,-0.05238477140665054,-0.009662225842475891,-0.998598575592041,-0.05203396454453468,-0.01177657674998045,-0.998598575592041,-0.05159655213356018,-0.013871326111257076,-0.998598575592041,-0.051073264330625534,-0.01594298891723156,-0.998598575592041,-0.050464969128370285,-0.017988115549087524,-0.998598575592041,-0.049772679805755615,-0.0200033038854599,-0.998598575592041,-0.04899755120277405,-0.021985197439789772,-0.998598575592041,-0.04814086854457855,-0.023930499330163002,-0.998598575592041,-0.04720405861735344,-0.025835970416665077,-0.998598575592041,-0.04618868604302406,-0.027698442339897156,-0.998598575592041,-0.04509643465280533,-0.02951481007039547,-0.998598575592041,-0.043929122388362885,-0.031282056123018265,-0.998598575592041,-0.04268869757652283,-0.03299723565578461,-0.998598575592041,-0.041377224028110504,-0.034657493233680725,-0.998598575592041,-0.03999687731266022,-0.03626006469130516,-0.998598575592041,-0.038549963384866714,-0.03780229017138481,-0.998598575592041,-0.037038885056972504,-0.03928159177303314,-0.998598575592041,-0.03546615689992905,-0.040695518255233765,-0.998598575592041,-0.03383440151810646,-0.042041707783937454,-0.998598575592041,-0.032146334648132324,-0.043317925184965134,-0.998598575592041,-0.03040475957095623,-0.04452204331755638,-0.998598575592041,-0.02861258015036583,-0.045652057975530624,-0.998598575592041,-0.02677277661859989,-0.04670609161257744,-0.998598575592041,-0.0248884130269289,-0.04768238589167595,-0.998598575592041,-0.022962626069784164,-0.04857931658625603,-0.998598575592041,-0.020998619496822357,-0.04939539358019829,-0.998598575592041,-0.01899966225028038,-0.050129253417253494,-0.998598575592041,-0.01696908287703991,-0.05077968165278435,-0.998598575592041,-0.01491026021540165,-0.0513455905020237,-0.998598575592041,-0.012826620601117611,-0.05182604119181633,-0.998598575592041,-0.010721632279455662,-0.05222023278474808,-0.998598575592041,-0.00859879795461893,-0.05252750590443611,-0.998598575592041,-0.006461652927100658,-0.05274735391139984,-0.998598575592041,-0.0043137529864907265,-0.05287941172719002,-0.998598575592041,-0.002158672781661153,-0.05292345583438873,-0.998598575592041,-1.2962507496940856e-17,0.01764848083257675,-0.9998442530632019,0.0,0.017633793875575066,-0.9998442530632019,-0.0007198565872386098,0.017589757218956947,-0.9998442530632019,-0.0014385150279849768,0.017516443505883217,-0.9998442530632019,-0.002154779154807329,0.017413975670933723,-0.9998442530632019,-0.002867456991225481,0.017282525077462196,-0.9998442530632019,-0.003575362032279372,0.017122307792305946,-0.9998442530632019,-0.004277316387742758,0.016933593899011612,-0.9998442530632019,-0.004972151480615139,0.016716694459319115,-0.9998442530632019,-0.005658710841089487,0.016471970826387405,-0.9998442530632019,-0.006335852202028036,0.016199832782149315,-0.9998442530632019,-0.007002447731792927,0.015900731086730957,-0.9998442530632019,-0.007657388690859079,0.015575164929032326,-0.9998442530632019,-0.008299584500491619,0.015223675407469273,-0.9998442530632019,-0.008927966468036175,0.014846846461296082,-0.9998442530632019,-0.00954148918390274,0.014445307664573193,-0.9998442530632019,-0.010139130055904388,0.014019724912941456,-0.9998442530632019,-0.010719896294176579,0.013570808805525303,-0.9998442530632019,-0.011282820254564285,0.013099304400384426,-0.9998442530632019,-0.011826964095234871,0.012605997733771801,-0.9998442530632019,-0.012351424433290958,0.012091710232198238,-0.9998442530632019,-0.01285532582551241,0.011557296849787235,-0.9998442530632019,-0.013337831944227219,0.011003647930920124,-0.9998442530632019,-0.013798138126730919,0.010431683622300625,-0.9998442530632019,-0.014235477894544601,0.009842357598245144,-0.9998442530632019,-0.014649124816060066,0.009236649610102177,-0.9998442530632019,-0.015038389712572098,0.008615568280220032,-0.9998442530632019,-0.015402624383568764,0.007980147376656532,-0.9998442530632019,-0.015741223469376564,0.007331443950533867,-0.9998442530632019,-0.01605362258851528,0.006670537870377302,-0.9998442530632019,-0.01633930206298828,0.005998529959470034,-0.9998442530632019,-0.01659778691828251,0.005316537339240313,-0.9998442530632019,-0.016828645020723343,0.004625696688890457,-0.9998442530632019,-0.01703149452805519,0.003927156329154968,-0.9998442530632019,-0.017205996438860893,0.003222079947590828,-0.9998442530632019,-0.01735186018049717,0.002511640777811408,-0.9998442530632019,-0.01746884547173977,0.001797021017409861,-0.9998442530632019,-0.017556753009557724,0.0010794104309752584,-0.9998442530632019,-0.017615441232919693,0.00036000317777507007,-0.9998442530632019,-0.017644807696342468,-0.00036000317777507007,-0.9998442530632019,-0.017644807696342468,-0.0010794104309752584,-0.9998442530632019,-0.017615441232919693,-0.001797021017409861,-0.9998442530632019,-0.017556753009557724,-0.002511640777811408,-0.9998442530632019,-0.01746884547173977,-0.003222079947590828,-0.9998442530632019,-0.01735186018049717,-0.003927156329154968,-0.9998442530632019,-0.017205996438860893,-0.004625696688890457,-0.9998442530632019,-0.01703149452805519,-0.005316537339240313,-0.9998442530632019,-0.016828645020723343,-0.005998529959470034,-0.9998442530632019,-0.01659778691828251,-0.006670537870377302,-0.9998442530632019,-0.01633930206298828,-0.007331443950533867,-0.9998442530632019,-0.01605362258851528,-0.007980147376656532,-0.9998442530632019,-0.015741223469376564,-0.008615568280220032,-0.9998442530632019,-0.015402624383568764,-0.009236649610102177,-0.9998442530632019,-0.015038389712572098,-0.009842357598245144,-0.9998442530632019,-0.014649124816060066,-0.010431683622300625,-0.9998442530632019,-0.014235477894544601,-0.011003647930920124,-0.9998442530632019,-0.013798138126730919,-0.011557296849787235,-0.9998442530632019,-0.013337831944227219,-0.012091710232198238,-0.9998442530632019,-0.01285532582551241,-0.012605997733771801,-0.9998442530632019,-0.012351424433290958,-0.013099304400384426,-0.9998442530632019,-0.011826964095234871,-0.013570808805525303,-0.9998442530632019,-0.011282820254564285,-0.014019724912941456,-0.9998442530632019,-0.010719896294176579,-0.014445307664573193,-0.9998442530632019,-0.010139130055904388,-0.014846846461296082,-0.9998442530632019,-0.00954148918390274,-0.015223675407469273,-0.9998442530632019,-0.008927966468036175,-0.015575164929032326,-0.9998442530632019,-0.008299584500491619,-0.015900731086730957,-0.9998442530632019,-0.007657388690859079,-0.016199832782149315,-0.9998442530632019,-0.007002447731792927,-0.016471970826387405,-0.9998442530632019,-0.006335852202028036,-0.016716694459319115,-0.9998442530632019,-0.005658710841089487,-0.016933593899011612,-0.9998442530632019,-0.004972151480615139,-0.017122307792305946,-0.9998442530632019,-0.004277316387742758,-0.017282525077462196,-0.9998442530632019,-0.003575362032279372,-0.017413975670933723,-0.9998442530632019,-0.002867456991225481,-0.017516443505883217,-0.9998442530632019,-0.002154779154807329,-0.017589757218956947,-0.9998442530632019,-0.0014385150279849768,-0.017633793875575066,-0.9998442530632019,-0.0007198565872386098,-0.01764848083257675,-0.9998442530632019,-2.1613156139165826e-18,-0.017633793875575066,-0.9998442530632019,0.0007198565872386098,-0.017589757218956947,-0.9998442530632019,0.0014385150279849768,-0.017516443505883217,-0.9998442530632019,0.002154779154807329,-0.017413975670933723,-0.9998442530632019,0.002867456991225481,-0.017282525077462196,-0.9998442530632019,0.003575362032279372,-0.017122307792305946,-0.9998442530632019,0.004277316387742758,-0.016933593899011612,-0.9998442530632019,0.004972151480615139,-0.016716694459319115,-0.9998442530632019,0.005658710841089487,-0.016471970826387405,-0.9998442530632019,0.006335852202028036,-0.016199832782149315,-0.9998442530632019,0.007002447731792927,-0.015900731086730957,-0.9998442530632019,0.007657388690859079,-0.015575164929032326,-0.9998442530632019,0.008299584500491619,-0.015223675407469273,-0.9998442530632019,0.008927966468036175,-0.014846846461296082,-0.9998442530632019,0.00954148918390274,-0.014445307664573193,-0.9998442530632019,0.010139130055904388,-0.014019724912941456,-0.9998442530632019,0.010719896294176579,-0.013570808805525303,-0.9998442530632019,0.011282820254564285,-0.013099304400384426,-0.9998442530632019,0.011826964095234871,-0.012605997733771801,-0.9998442530632019,0.012351424433290958,-0.012091710232198238,-0.9998442530632019,0.01285532582551241,-0.011557296849787235,-0.9998442530632019,0.013337831944227219,-0.011003647930920124,-0.9998442530632019,0.013798138126730919,-0.010431683622300625,-0.9998442530632019,0.014235477894544601,-0.009842357598245144,-0.9998442530632019,0.014649124816060066,-0.009236649610102177,-0.9998442530632019,0.015038389712572098,-0.008615568280220032,-0.9998442530632019,0.015402624383568764,-0.007980147376656532,-0.9998442530632019,0.015741223469376564,-0.007331443950533867,-0.9998442530632019,0.01605362258851528,-0.006670537870377302,-0.9998442530632019,0.01633930206298828,-0.005998529959470034,-0.9998442530632019,0.01659778691828251,-0.005316537339240313,-0.9998442530632019,0.016828645020723343,-0.004625696688890457,-0.9998442530632019,0.01703149452805519,-0.003927156329154968,-0.9998442530632019,0.017205996438860893,-0.003222079947590828,-0.9998442530632019,0.01735186018049717,-0.002511640777811408,-0.9998442530632019,0.01746884547173977,-0.001797021017409861,-0.9998442530632019,0.017556753009557724,-0.0010794104309752584,-0.9998442530632019,0.017615441232919693,-0.00036000317777507007,-0.9998442530632019,0.017644807696342468,0.00036000317777507007,-0.9998442530632019,0.017644807696342468,0.0010794104309752584,-0.9998442530632019,0.017615441232919693,0.001797021017409861,-0.9998442530632019,0.017556753009557724,0.002511640777811408,-0.9998442530632019,0.01746884547173977,0.003222079947590828,-0.9998442530632019,0.01735186018049717,0.003927156329154968,-0.9998442530632019,0.017205996438860893,0.004625696688890457,-0.9998442530632019,0.01703149452805519,0.005316537339240313,-0.9998442530632019,0.016828645020723343,0.005998529959470034,-0.9998442530632019,0.01659778691828251,0.006670537870377302,-0.9998442530632019,0.01633930206298828,0.007331443950533867,-0.9998442530632019,0.01605362258851528,0.007980147376656532,-0.9998442530632019,0.015741223469376564,0.008615568280220032,-0.9998442530632019,0.015402624383568764,0.009236649610102177,-0.9998442530632019,0.015038389712572098,0.009842357598245144,-0.9998442530632019,0.014649124816060066,0.010431683622300625,-0.9998442530632019,0.014235477894544601,0.011003647930920124,-0.9998442530632019,0.013798138126730919,0.011557296849787235,-0.9998442530632019,0.013337831944227219,0.012091710232198238,-0.9998442530632019,0.01285532582551241,0.012605997733771801,-0.9998442530632019,0.012351424433290958,0.013099304400384426,-0.9998442530632019,0.011826964095234871,0.013570808805525303,-0.9998442530632019,0.011282820254564285,0.014019724912941456,-0.9998442530632019,0.010719896294176579,0.014445307664573193,-0.9998442530632019,0.010139130055904388,0.014846846461296082,-0.9998442530632019,0.00954148918390274,0.015223675407469273,-0.9998442530632019,0.008927966468036175,0.015575164929032326,-0.9998442530632019,0.008299584500491619,0.015900731086730957,-0.9998442530632019,0.007657388690859079,0.016199832782149315,-0.9998442530632019,0.007002447731792927,0.016471970826387405,-0.9998442530632019,0.006335852202028036,0.016716694459319115,-0.9998442530632019,0.005658710841089487,0.016933593899011612,-0.9998442530632019,0.004972151480615139,0.017122307792305946,-0.9998442530632019,0.004277316387742758,0.017282525077462196,-0.9998442530632019,0.003575362032279372,0.017413975670933723,-0.9998442530632019,0.002867456991225481,0.017516443505883217,-0.9998442530632019,0.002154779154807329,0.017589757218956947,-0.9998442530632019,0.0014385150279849768,0.017633793875575066,-0.9998442530632019,0.0007198565872386098,0.01764848083257675,-0.9998442530632019,4.322631227833165e-18,0.08813249319791794,-0.9961087703704834,0.0,0.08805914968252182,-0.9961087703704834,-0.003594799665734172,0.08783923834562302,-0.9961087703704834,-0.007183616049587727,0.08747313171625137,-0.9961087703704834,-0.010760476812720299,0.08696142584085464,-0.9961087703704834,-0.014319426380097866,0.08630499243736267,-0.9961087703704834,-0.017854543402791023,0.08550490438938141,-0.9961087703704834,-0.021359942853450775,0.0845625028014183,-0.9961087703704834,-0.024829791858792305,0.08347935974597931,-0.9961087703704834,-0.028258314356207848,0.08225727081298828,-0.9961087703704834,-0.03163980320096016,0.08089827746152878,-0.9961087703704834,-0.034968629479408264,0.07940462976694107,-0.9961087703704834,-0.0382392555475235,0.07777882367372513,-0.9961087703704834,-0.04144623503088951,0.07602356374263763,-0.9961087703704834,-0.044584233313798904,0.07414177060127258,-0.9961087703704834,-0.047648023813962936,0.07213657349348068,-0.9961087703704834,-0.05063250660896301,0.07001131027936935,-0.9961087703704834,-0.05353271961212158,0.06776952743530273,-0.9961087703704834,-0.05634383112192154,0.06541494280099869,-0.9961087703704834,-0.059061165899038315,0.0629514828324318,-0.9961087703704834,-0.06168019771575928,0.06038324534893036,-0.9961087703704834,-0.06419657170772552,0.05771450698375702,-0.9961087703704834,-0.06660608947277069,0.05494970828294754,-0.9961087703704834,-0.06890475749969482,0.05209345370531082,-0.9961087703704834,-0.07108873128890991,0.0491504929959774,-0.9961087703704834,-0.07315438985824585,0.04612572491168976,-0.9961087703704834,-0.07509829103946686,0.04302418604493141,-0.9961087703704834,-0.07691720128059387,0.03985103592276573,-0.9961087703704834,-0.07860808074474335,0.036611560732126236,-0.9961087703704834,-0.08016813546419144,0.0333111472427845,-0.9961087703704834,-0.08159475028514862,0.029955290257930756,-0.9961087703704834,-0.08288556337356567,0.026549577713012695,-0.9961087703704834,-0.08403842151165009,0.02309967391192913,-0.9961087703704834,-0.08505140244960785,0.01961132511496544,-0.9961087703704834,-0.08592282235622406,0.016090333461761475,-0.9961087703704834,-0.08665123581886292,0.012542561627924442,-0.9961087703704834,-0.08723542839288712,0.00897391326725483,-0.9961087703704834,-0.08767442405223846,0.005390329752117395,-0.9961087703704834,-0.08796749264001846,0.0017977738752961159,-0.9961087703704834,-0.08811415731906891,-0.0017977738752961159,-0.9961087703704834,-0.08811415731906891,-0.005390329752117395,-0.9961087703704834,-0.08796749264001846,-0.00897391326725483,-0.9961087703704834,-0.08767442405223846,-0.012542561627924442,-0.9961087703704834,-0.08723542839288712,-0.016090333461761475,-0.9961087703704834,-0.08665123581886292,-0.01961132511496544,-0.9961087703704834,-0.08592282235622406,-0.02309967391192913,-0.9961087703704834,-0.08505140244960785,-0.026549577713012695,-0.9961087703704834,-0.08403842151165009,-0.029955290257930756,-0.9961087703704834,-0.08288556337356567,-0.0333111472427845,-0.9961087703704834,-0.08159475028514862,-0.036611560732126236,-0.9961087703704834,-0.08016813546419144,-0.03985103592276573,-0.9961087703704834,-0.07860808074474335,-0.04302418604493141,-0.9961087703704834,-0.07691720128059387,-0.04612572491168976,-0.9961087703704834,-0.07509829103946686,-0.0491504929959774,-0.9961087703704834,-0.07315438985824585,-0.05209345370531082,-0.9961087703704834,-0.07108873128890991,-0.05494970828294754,-0.9961087703704834,-0.06890475749969482,-0.05771450698375702,-0.9961087703704834,-0.06660608947277069,-0.06038324534893036,-0.9961087703704834,-0.06419657170772552,-0.0629514828324318,-0.9961087703704834,-0.06168019771575928,-0.06541494280099869,-0.9961087703704834,-0.059061165899038315,-0.06776952743530273,-0.9961087703704834,-0.05634383112192154,-0.07001131027936935,-0.9961087703704834,-0.05353271961212158,-0.07213657349348068,-0.9961087703704834,-0.05063250660896301,-0.07414177060127258,-0.9961087703704834,-0.047648023813962936,-0.07602356374263763,-0.9961087703704834,-0.044584233313798904,-0.07777882367372513,-0.9961087703704834,-0.04144623503088951,-0.07940462976694107,-0.9961087703704834,-0.0382392555475235,-0.08089827746152878,-0.9961087703704834,-0.034968629479408264,-0.08225727081298828,-0.9961087703704834,-0.03163980320096016,-0.08347935974597931,-0.9961087703704834,-0.028258314356207848,-0.0845625028014183,-0.9961087703704834,-0.024829791858792305,-0.08550490438938141,-0.9961087703704834,-0.021359942853450775,-0.08630499243736267,-0.9961087703704834,-0.017854543402791023,-0.08696142584085464,-0.9961087703704834,-0.014319426380097866,-0.08747313171625137,-0.9961087703704834,-0.010760476812720299,-0.08783923834562302,-0.9961087703704834,-0.007183616049587727,-0.08805914968252182,-0.9961087703704834,-0.003594799665734172,-0.08813249319791794,-0.9961087703704834,-1.0793117152679684e-17,-0.08805914968252182,-0.9961087703704834,0.003594799665734172,-0.08783923834562302,-0.9961087703704834,0.007183616049587727,-0.08747313171625137,-0.9961087703704834,0.010760476812720299,-0.08696142584085464,-0.9961087703704834,0.014319426380097866,-0.08630499243736267,-0.9961087703704834,0.017854543402791023,-0.08550490438938141,-0.9961087703704834,0.021359942853450775,-0.0845625028014183,-0.9961087703704834,0.024829791858792305,-0.08347935974597931,-0.9961087703704834,0.028258314356207848,-0.08225727081298828,-0.9961087703704834,0.03163980320096016,-0.08089827746152878,-0.9961087703704834,0.034968629479408264,-0.07940462976694107,-0.9961087703704834,0.0382392555475235,-0.07777882367372513,-0.9961087703704834,0.04144623503088951,-0.07602356374263763,-0.9961087703704834,0.044584233313798904,-0.07414177060127258,-0.9961087703704834,0.047648023813962936,-0.07213657349348068,-0.9961087703704834,0.05063250660896301,-0.07001131027936935,-0.9961087703704834,0.05353271961212158,-0.06776952743530273,-0.9961087703704834,0.05634383112192154,-0.06541494280099869,-0.9961087703704834,0.059061165899038315,-0.0629514828324318,-0.9961087703704834,0.06168019771575928,-0.06038324534893036,-0.9961087703704834,0.06419657170772552,-0.05771450698375702,-0.9961087703704834,0.06660608947277069,-0.05494970828294754,-0.9961087703704834,0.06890475749969482,-0.05209345370531082,-0.9961087703704834,0.07108873128890991,-0.0491504929959774,-0.9961087703704834,0.07315438985824585,-0.04612572491168976,-0.9961087703704834,0.07509829103946686,-0.04302418604493141,-0.9961087703704834,0.07691720128059387,-0.03985103592276573,-0.9961087703704834,0.07860808074474335,-0.036611560732126236,-0.9961087703704834,0.08016813546419144,-0.0333111472427845,-0.9961087703704834,0.08159475028514862,-0.029955290257930756,-0.9961087703704834,0.08288556337356567,-0.026549577713012695,-0.9961087703704834,0.08403842151165009,-0.02309967391192913,-0.9961087703704834,0.08505140244960785,-0.01961132511496544,-0.9961087703704834,0.08592282235622406,-0.016090333461761475,-0.9961087703704834,0.08665123581886292,-0.012542561627924442,-0.9961087703704834,0.08723542839288712,-0.00897391326725483,-0.9961087703704834,0.08767442405223846,-0.005390329752117395,-0.9961087703704834,0.08796749264001846,-0.0017977738752961159,-0.9961087703704834,0.08811415731906891,0.0017977738752961159,-0.9961087703704834,0.08811415731906891,0.005390329752117395,-0.9961087703704834,0.08796749264001846,0.00897391326725483,-0.9961087703704834,0.08767442405223846,0.012542561627924442,-0.9961087703704834,0.08723542839288712,0.016090333461761475,-0.9961087703704834,0.08665123581886292,0.01961132511496544,-0.9961087703704834,0.08592282235622406,0.02309967391192913,-0.9961087703704834,0.08505140244960785,0.026549577713012695,-0.9961087703704834,0.08403842151165009,0.029955290257930756,-0.9961087703704834,0.08288556337356567,0.0333111472427845,-0.9961087703704834,0.08159475028514862,0.036611560732126236,-0.9961087703704834,0.08016813546419144,0.03985103592276573,-0.9961087703704834,0.07860808074474335,0.04302418604493141,-0.9961087703704834,0.07691720128059387,0.04612572491168976,-0.9961087703704834,0.07509829103946686,0.0491504929959774,-0.9961087703704834,0.07315438985824585,0.05209345370531082,-0.9961087703704834,0.07108873128890991,0.05494970828294754,-0.9961087703704834,0.06890475749969482,0.05771450698375702,-0.9961087703704834,0.06660608947277069,0.06038324534893036,-0.9961087703704834,0.06419657170772552,0.0629514828324318,-0.9961087703704834,0.06168019771575928,0.06541494280099869,-0.9961087703704834,0.059061165899038315,0.06776952743530273,-0.9961087703704834,0.05634383112192154,0.07001131027936935,-0.9961087703704834,0.05353271961212158,0.07213657349348068,-0.9961087703704834,0.05063250660896301,0.07414177060127258,-0.9961087703704834,0.047648023813962936,0.07602356374263763,-0.9961087703704834,0.044584233313798904,0.07777882367372513,-0.9961087703704834,0.04144623503088951,0.07940462976694107,-0.9961087703704834,0.0382392555475235,0.08089827746152878,-0.9961087703704834,0.034968629479408264,0.08225727081298828,-0.9961087703704834,0.03163980320096016,0.08347935974597931,-0.9961087703704834,0.028258314356207848,0.0845625028014183,-0.9961087703704834,0.024829791858792305,0.08550490438938141,-0.9961087703704834,0.021359942853450775,0.08630499243736267,-0.9961087703704834,0.017854543402791023,0.08696142584085464,-0.9961087703704834,0.014319426380097866,0.08747313171625137,-0.9961087703704834,0.010760476812720299,0.08783923834562302,-0.9961087703704834,0.007183616049587727,0.08805914968252182,-0.9961087703704834,0.003594799665734172,0.08813249319791794,-0.9961087703704834,2.158623430535937e-17,0.1581774353981018,-0.9874107241630554,0.0,0.15804579854011536,-0.9874107241630554,-0.0064518335275352,0.1576511114835739,-0.9874107241630554,-0.012892928905785084,0.15699402987957,-0.9874107241630554,-0.01931256614625454,0.1560756415128708,-0.9874107241630554,-0.025700056925415993,0.15489749610424042,-0.9874107241630554,-0.032044775784015656,0.1534615159034729,-0.9874107241630554,-0.03833615779876709,0.15177012979984283,-0.9874107241630554,-0.04456373304128647,0.14982613921165466,-0.9874107241630554,-0.05071713402867317,0.14763277769088745,-0.9874107241630554,-0.05678612366318703,0.1451936811208725,-0.9874107241630554,-0.0627605989575386,0.1425129473209381,-0.9874107241630554,-0.06863061338663101,0.13959500193595886,-0.9874107241630554,-0.07438640296459198,0.13644470274448395,-0.9874107241630554,-0.08001837879419327,0.133067324757576,-0.9874107241630554,-0.08551717549562454,0.12946845591068268,-0.9874107241630554,-0.09087363630533218,0.12565410137176514,-0.9874107241630554,-0.09607884287834167,0.12163061648607254,-0.9874107241630554,-0.10112414509057999,0.11740468442440033,-0.9874107241630554,-0.10600113123655319,0.1129833459854126,-0.9874107241630554,-0.11070168763399124,0.10837395489215851,-0.9874107241630554,-0.11521799117326736,0.1035841852426529,-0.9874107241630554,-0.11954252421855927,0.09862201660871506,-0.9874107241630554,-0.12366809695959091,0.0934956967830658,-0.9874107241630554,-0.12758782505989075,0.08821376413106918,-0.9874107241630554,-0.13129520416259766,0.08278500288724899,-0.9874107241630554,-0.1347840577363968,0.07721845805644989,-0.9874107241630554,-0.13804857432842255,0.07152339071035385,-0.9874107241630554,-0.14108332991600037,0.06570927798748016,-0.9874107241630554,-0.14388325810432434,0.05978580191731453,-0.9874107241630554,-0.1464436948299408,0.05376281589269638,-0.9874107241630554,-0.14876040816307068,0.04765034839510918,-0.9874107241630554,-0.1508295238018036,0.04145856946706772,-0.9874107241630554,-0.15264758467674255,0.03519779071211815,-0.9874107241630554,-0.1542115956544876,0.02887842431664467,-0.9874107241630554,-0.15551891922950745,0.022510996088385582,-0.9874107241630554,-0.15656740963459015,0.016106098890304565,-0.9874107241630554,-0.15735530853271484,0.009674394503235817,-0.9874107241630554,-0.15788130462169647,0.003226588247343898,-0.9874107241630554,-0.1581445187330246,-0.003226588247343898,-0.9874107241630554,-0.1581445187330246,-0.009674394503235817,-0.9874107241630554,-0.15788130462169647,-0.016106098890304565,-0.9874107241630554,-0.15735530853271484,-0.022510996088385582,-0.9874107241630554,-0.15656740963459015,-0.02887842431664467,-0.9874107241630554,-0.15551891922950745,-0.03519779071211815,-0.9874107241630554,-0.1542115956544876,-0.04145856946706772,-0.9874107241630554,-0.15264758467674255,-0.04765034839510918,-0.9874107241630554,-0.1508295238018036,-0.05376281589269638,-0.9874107241630554,-0.14876040816307068,-0.05978580191731453,-0.9874107241630554,-0.1464436948299408,-0.06570927798748016,-0.9874107241630554,-0.14388325810432434,-0.07152339071035385,-0.9874107241630554,-0.14108332991600037,-0.07721845805644989,-0.9874107241630554,-0.13804857432842255,-0.08278500288724899,-0.9874107241630554,-0.1347840577363968,-0.08821376413106918,-0.9874107241630554,-0.13129520416259766,-0.0934956967830658,-0.9874107241630554,-0.12758782505989075,-0.09862201660871506,-0.9874107241630554,-0.12366809695959091,-0.1035841852426529,-0.9874107241630554,-0.11954252421855927,-0.10837395489215851,-0.9874107241630554,-0.11521799117326736,-0.1129833459854126,-0.9874107241630554,-0.11070168763399124,-0.11740468442440033,-0.9874107241630554,-0.10600113123655319,-0.12163061648607254,-0.9874107241630554,-0.10112414509057999,-0.12565410137176514,-0.9874107241630554,-0.09607884287834167,-0.12946845591068268,-0.9874107241630554,-0.09087363630533218,-0.133067324757576,-0.9874107241630554,-0.08551717549562454,-0.13644470274448395,-0.9874107241630554,-0.08001837879419327,-0.13959500193595886,-0.9874107241630554,-0.07438640296459198,-0.1425129473209381,-0.9874107241630554,-0.06863061338663101,-0.1451936811208725,-0.9874107241630554,-0.0627605989575386,-0.14763277769088745,-0.9874107241630554,-0.05678612366318703,-0.14982613921165466,-0.9874107241630554,-0.05071713402867317,-0.15177012979984283,-0.9874107241630554,-0.04456373304128647,-0.1534615159034729,-0.9874107241630554,-0.03833615779876709,-0.15489749610424042,-0.9874107241630554,-0.032044775784015656,-0.1560756415128708,-0.9874107241630554,-0.025700056925415993,-0.15699402987957,-0.9874107241630554,-0.01931256614625454,-0.1576511114835739,-0.9874107241630554,-0.012892928905785084,-0.15804579854011536,-0.9874107241630554,-0.0064518335275352,-0.1581774353981018,-0.9874107241630554,-1.9371148849699542e-17,-0.15804579854011536,-0.9874107241630554,0.0064518335275352,-0.1576511114835739,-0.9874107241630554,0.012892928905785084,-0.15699402987957,-0.9874107241630554,0.01931256614625454,-0.1560756415128708,-0.9874107241630554,0.025700056925415993,-0.15489749610424042,-0.9874107241630554,0.032044775784015656,-0.1534615159034729,-0.9874107241630554,0.03833615779876709,-0.15177012979984283,-0.9874107241630554,0.04456373304128647,-0.14982613921165466,-0.9874107241630554,0.05071713402867317,-0.14763277769088745,-0.9874107241630554,0.05678612366318703,-0.1451936811208725,-0.9874107241630554,0.0627605989575386,-0.1425129473209381,-0.9874107241630554,0.06863061338663101,-0.13959500193595886,-0.9874107241630554,0.07438640296459198,-0.13644470274448395,-0.9874107241630554,0.08001837879419327,-0.133067324757576,-0.9874107241630554,0.08551717549562454,-0.12946845591068268,-0.9874107241630554,0.09087363630533218,-0.12565410137176514,-0.9874107241630554,0.09607884287834167,-0.12163061648607254,-0.9874107241630554,0.10112414509057999,-0.11740468442440033,-0.9874107241630554,0.10600113123655319,-0.1129833459854126,-0.9874107241630554,0.11070168763399124,-0.10837395489215851,-0.9874107241630554,0.11521799117326736,-0.1035841852426529,-0.9874107241630554,0.11954252421855927,-0.09862201660871506,-0.9874107241630554,0.12366809695959091,-0.0934956967830658,-0.9874107241630554,0.12758782505989075,-0.08821376413106918,-0.9874107241630554,0.13129520416259766,-0.08278500288724899,-0.9874107241630554,0.1347840577363968,-0.07721845805644989,-0.9874107241630554,0.13804857432842255,-0.07152339071035385,-0.9874107241630554,0.14108332991600037,-0.06570927798748016,-0.9874107241630554,0.14388325810432434,-0.05978580191731453,-0.9874107241630554,0.1464436948299408,-0.05376281589269638,-0.9874107241630554,0.14876040816307068,-0.04765034839510918,-0.9874107241630554,0.1508295238018036,-0.04145856946706772,-0.9874107241630554,0.15264758467674255,-0.03519779071211815,-0.9874107241630554,0.1542115956544876,-0.02887842431664467,-0.9874107241630554,0.15551891922950745,-0.022510996088385582,-0.9874107241630554,0.15656740963459015,-0.016106098890304565,-0.9874107241630554,0.15735530853271484,-0.009674394503235817,-0.9874107241630554,0.15788130462169647,-0.003226588247343898,-0.9874107241630554,0.1581445187330246,0.003226588247343898,-0.9874107241630554,0.1581445187330246,0.009674394503235817,-0.9874107241630554,0.15788130462169647,0.016106098890304565,-0.9874107241630554,0.15735530853271484,0.022510996088385582,-0.9874107241630554,0.15656740963459015,0.02887842431664467,-0.9874107241630554,0.15551891922950745,0.03519779071211815,-0.9874107241630554,0.1542115956544876,0.04145856946706772,-0.9874107241630554,0.15264758467674255,0.04765034839510918,-0.9874107241630554,0.1508295238018036,0.05376281589269638,-0.9874107241630554,0.14876040816307068,0.05978580191731453,-0.9874107241630554,0.1464436948299408,0.06570927798748016,-0.9874107241630554,0.14388325810432434,0.07152339071035385,-0.9874107241630554,0.14108332991600037,0.07721845805644989,-0.9874107241630554,0.13804857432842255,0.08278500288724899,-0.9874107241630554,0.1347840577363968,0.08821376413106918,-0.9874107241630554,0.13129520416259766,0.0934956967830658,-0.9874107241630554,0.12758782505989075,0.09862201660871506,-0.9874107241630554,0.12366809695959091,0.1035841852426529,-0.9874107241630554,0.11954252421855927,0.10837395489215851,-0.9874107241630554,0.11521799117326736,0.1129833459854126,-0.9874107241630554,0.11070168763399124,0.11740468442440033,-0.9874107241630554,0.10600113123655319,0.12163061648607254,-0.9874107241630554,0.10112414509057999,0.12565410137176514,-0.9874107241630554,0.09607884287834167,0.12946845591068268,-0.9874107241630554,0.09087363630533218,0.133067324757576,-0.9874107241630554,0.08551717549562454,0.13644470274448395,-0.9874107241630554,0.08001837879419327,0.13959500193595886,-0.9874107241630554,0.07438640296459198,0.1425129473209381,-0.9874107241630554,0.06863061338663101,0.1451936811208725,-0.9874107241630554,0.0627605989575386,0.14763277769088745,-0.9874107241630554,0.05678612366318703,0.14982613921165466,-0.9874107241630554,0.05071713402867317,0.15177012979984283,-0.9874107241630554,0.04456373304128647,0.1534615159034729,-0.9874107241630554,0.03833615779876709,0.15489749610424042,-0.9874107241630554,0.032044775784015656,0.1560756415128708,-0.9874107241630554,0.025700056925415993,0.15699402987957,-0.9874107241630554,0.01931256614625454,0.1576511114835739,-0.9874107241630554,0.012892928905785084,0.15804579854011536,-0.9874107241630554,0.0064518335275352,0.1581774353981018,-0.9874107241630554,3.8742297699399084e-17,0.22743433713912964,-0.9737934470176697,0.0,0.22724506258964539,-0.9737934470176697,-0.009276725351810455,0.2266775667667389,-0.9737934470176697,-0.018538009375333786,0.22573278844356537,-0.9737934470176697,-0.027768440544605255,0.22441229224205017,-0.9737934470176697,-0.03695265203714371,0.22271829843521118,-0.9737934470176697,-0.04607535898685455,0.22065359354019165,-0.9737934470176697,-0.05512138083577156,0.21822164952754974,-0.9737934470176697,-0.06407565623521805,0.2154264897108078,-0.9737934470176697,-0.07292328774929047,0.2122727781534195,-0.9737934470176697,-0.0816495418548584,0.20876574516296387,-0.9737934470176697,-0.09023989737033844,0.2049112617969513,-0.9737934470176697,-0.09868006408214569,0.20071570575237274,-0.9737934470176697,-0.10695598274469376,0.1961860954761505,-0.9737934470176697,-0.11505388468503952,0.1913299411535263,-0.9737934470176697,-0.12296028435230255,0.18615533411502838,-0.9737934470176697,-0.13066203892230988,0.18067090213298798,-0.9737934470176697,-0.138146311044693,0.17488574981689453,-0.9737934470176697,-0.14540065824985504,0.16880953311920166,-0.9737934470176697,-0.1524129956960678,0.1624523252248764,-0.9737934470176697,-0.15917165577411652,0.15582475066184998,-0.9737934470176697,-0.165665403008461,0.14893782138824463,-0.9737934470176697,-0.17188340425491333,0.14180299639701843,-0.9737934470176697,-0.17781533300876617,0.13443215191364288,-0.9737934470176697,-0.18345129489898682,0.12683755159378052,-0.9737934470176697,-0.18878191709518433,0.11903185397386551,-0.9737934470176697,-0.19379833340644836,0.1110280305147171,-0.9737934470176697,-0.19849219918251038,0.10283941775560379,-0.9737934470176697,-0.2028556913137436,0.09447963535785675,-0.9737934470176697,-0.2068815529346466,0.08596260845661163,-0.9737934470176697,-0.2105630785226822,0.07730250060558319,-0.9737934470176697,-0.21389414370059967,0.06851372867822647,-0.9737934470176697,-0.21686919033527374,0.05961092188954353,-0.9737934470176697,-0.21948328614234924,0.05060890316963196,-0.9737934470176697,-0.22173207998275757,0.04152264818549156,-0.9737934470176697,-0.22361183166503906,0.0323672816157341,-0.9737934470176697,-0.22511938214302063,0.02315804362297058,-0.9737934470176697,-0.2262522578239441,0.013910261914134026,-0.9737934470176697,-0.22700855135917664,0.004639327991753817,-0.9737934470176697,-0.22738701105117798,-0.004639327991753817,-0.9737934470176697,-0.22738701105117798,-0.013910261914134026,-0.9737934470176697,-0.22700855135917664,-0.02315804362297058,-0.9737934470176697,-0.2262522578239441,-0.0323672816157341,-0.9737934470176697,-0.22511938214302063,-0.04152264818549156,-0.9737934470176697,-0.22361183166503906,-0.05060890316963196,-0.9737934470176697,-0.22173207998275757,-0.05961092188954353,-0.9737934470176697,-0.21948328614234924,-0.06851372867822647,-0.9737934470176697,-0.21686919033527374,-0.07730250060558319,-0.9737934470176697,-0.21389414370059967,-0.08596260845661163,-0.9737934470176697,-0.2105630785226822,-0.09447963535785675,-0.9737934470176697,-0.2068815529346466,-0.10283941775560379,-0.9737934470176697,-0.2028556913137436,-0.1110280305147171,-0.9737934470176697,-0.19849219918251038,-0.11903185397386551,-0.9737934470176697,-0.19379833340644836,-0.12683755159378052,-0.9737934470176697,-0.18878191709518433,-0.13443215191364288,-0.9737934470176697,-0.18345129489898682,-0.14180299639701843,-0.9737934470176697,-0.17781533300876617,-0.14893782138824463,-0.9737934470176697,-0.17188340425491333,-0.15582475066184998,-0.9737934470176697,-0.165665403008461,-0.1624523252248764,-0.9737934470176697,-0.15917165577411652,-0.16880953311920166,-0.9737934470176697,-0.1524129956960678,-0.17488574981689453,-0.9737934470176697,-0.14540065824985504,-0.18067090213298798,-0.9737934470176697,-0.138146311044693,-0.18615533411502838,-0.9737934470176697,-0.13066203892230988,-0.1913299411535263,-0.9737934470176697,-0.12296028435230255,-0.1961860954761505,-0.9737934470176697,-0.11505388468503952,-0.20071570575237274,-0.9737934470176697,-0.10695598274469376,-0.2049112617969513,-0.9737934470176697,-0.09868006408214569,-0.20876574516296387,-0.9737934470176697,-0.09023989737033844,-0.2122727781534195,-0.9737934470176697,-0.0816495418548584,-0.2154264897108078,-0.9737934470176697,-0.07292328774929047,-0.21822164952754974,-0.9737934470176697,-0.06407565623521805,-0.22065359354019165,-0.9737934470176697,-0.05512138083577156,-0.22271829843521118,-0.9737934470176697,-0.04607535898685455,-0.22441229224205017,-0.9737934470176697,-0.03695265203714371,-0.22573278844356537,-0.9737934470176697,-0.027768440544605255,-0.2266775667667389,-0.9737934470176697,-0.018538009375333786,-0.22724506258964539,-0.9737934470176697,-0.009276725351810455,-0.22743433713912964,-0.9737934470176697,-2.785267338465284e-17,-0.22724506258964539,-0.9737934470176697,0.009276725351810455,-0.2266775667667389,-0.9737934470176697,0.018538009375333786,-0.22573278844356537,-0.9737934470176697,0.027768440544605255,-0.22441229224205017,-0.9737934470176697,0.03695265203714371,-0.22271829843521118,-0.9737934470176697,0.04607535898685455,-0.22065359354019165,-0.9737934470176697,0.05512138083577156,-0.21822164952754974,-0.9737934470176697,0.06407565623521805,-0.2154264897108078,-0.9737934470176697,0.07292328774929047,-0.2122727781534195,-0.9737934470176697,0.0816495418548584,-0.20876574516296387,-0.9737934470176697,0.09023989737033844,-0.2049112617969513,-0.9737934470176697,0.09868006408214569,-0.20071570575237274,-0.9737934470176697,0.10695598274469376,-0.1961860954761505,-0.9737934470176697,0.11505388468503952,-0.1913299411535263,-0.9737934470176697,0.12296028435230255,-0.18615533411502838,-0.9737934470176697,0.13066203892230988,-0.18067090213298798,-0.9737934470176697,0.138146311044693,-0.17488574981689453,-0.9737934470176697,0.14540065824985504,-0.16880953311920166,-0.9737934470176697,0.1524129956960678,-0.1624523252248764,-0.9737934470176697,0.15917165577411652,-0.15582475066184998,-0.9737934470176697,0.165665403008461,-0.14893782138824463,-0.9737934470176697,0.17188340425491333,-0.14180299639701843,-0.9737934470176697,0.17781533300876617,-0.13443215191364288,-0.9737934470176697,0.18345129489898682,-0.12683755159378052,-0.9737934470176697,0.18878191709518433,-0.11903185397386551,-0.9737934470176697,0.19379833340644836,-0.1110280305147171,-0.9737934470176697,0.19849219918251038,-0.10283941775560379,-0.9737934470176697,0.2028556913137436,-0.09447963535785675,-0.9737934470176697,0.2068815529346466,-0.08596260845661163,-0.9737934470176697,0.2105630785226822,-0.07730250060558319,-0.9737934470176697,0.21389414370059967,-0.06851372867822647,-0.9737934470176697,0.21686919033527374,-0.05961092188954353,-0.9737934470176697,0.21948328614234924,-0.05060890316963196,-0.9737934470176697,0.22173207998275757,-0.04152264818549156,-0.9737934470176697,0.22361183166503906,-0.0323672816157341,-0.9737934470176697,0.22511938214302063,-0.02315804362297058,-0.9737934470176697,0.2262522578239441,-0.013910261914134026,-0.9737934470176697,0.22700855135917664,-0.004639327991753817,-0.9737934470176697,0.22738701105117798,0.004639327991753817,-0.9737934470176697,0.22738701105117798,0.013910261914134026,-0.9737934470176697,0.22700855135917664,0.02315804362297058,-0.9737934470176697,0.2262522578239441,0.0323672816157341,-0.9737934470176697,0.22511938214302063,0.04152264818549156,-0.9737934470176697,0.22361183166503906,0.05060890316963196,-0.9737934470176697,0.22173207998275757,0.05961092188954353,-0.9737934470176697,0.21948328614234924,0.06851372867822647,-0.9737934470176697,0.21686919033527374,0.07730250060558319,-0.9737934470176697,0.21389414370059967,0.08596260845661163,-0.9737934470176697,0.2105630785226822,0.09447963535785675,-0.9737934470176697,0.2068815529346466,0.10283941775560379,-0.9737934470176697,0.2028556913137436,0.1110280305147171,-0.9737934470176697,0.19849219918251038,0.11903185397386551,-0.9737934470176697,0.19379833340644836,0.12683755159378052,-0.9737934470176697,0.18878191709518433,0.13443215191364288,-0.9737934470176697,0.18345129489898682,0.14180299639701843,-0.9737934470176697,0.17781533300876617,0.14893782138824463,-0.9737934470176697,0.17188340425491333,0.15582475066184998,-0.9737934470176697,0.165665403008461,0.1624523252248764,-0.9737934470176697,0.15917165577411652,0.16880953311920166,-0.9737934470176697,0.1524129956960678,0.17488574981689453,-0.9737934470176697,0.14540065824985504,0.18067090213298798,-0.9737934470176697,0.138146311044693,0.18615533411502838,-0.9737934470176697,0.13066203892230988,0.1913299411535263,-0.9737934470176697,0.12296028435230255,0.1961860954761505,-0.9737934470176697,0.11505388468503952,0.20071570575237274,-0.9737934470176697,0.10695598274469376,0.2049112617969513,-0.9737934470176697,0.09868006408214569,0.20876574516296387,-0.9737934470176697,0.09023989737033844,0.2122727781534195,-0.9737934470176697,0.0816495418548584,0.2154264897108078,-0.9737934470176697,0.07292328774929047,0.21822164952754974,-0.9737934470176697,0.06407565623521805,0.22065359354019165,-0.9737934470176697,0.05512138083577156,0.22271829843521118,-0.9737934470176697,0.04607535898685455,0.22441229224205017,-0.9737934470176697,0.03695265203714371,0.22573278844356537,-0.9737934470176697,0.027768440544605255,0.2266775667667389,-0.9737934470176697,0.018538009375333786,0.22724506258964539,-0.9737934470176697,0.009276725351810455,0.22743433713912964,-0.9737934470176697,5.570534676930568e-17,0.2955581843852997,-0.9553247690200806,0.0,0.29531222581863403,-0.9553247690200806,-0.01205539982765913,0.2945747375488281,-0.9553247690200806,-0.024090735241770744,0.29334697127342224,-0.9553247690200806,-0.03608597442507744,0.29163095355033875,-0.9553247690200806,-0.04802115261554718,0.28942954540252686,-0.9553247690200806,-0.05987640097737312,0.28674638271331787,-0.9553247690200806,-0.07163199037313461,0.2835859954357147,-0.9553247690200806,-0.08326835930347443,0.27995359897613525,-0.9553247690200806,-0.09476613998413086,0.27585524320602417,-0.9553247690200806,-0.10610618442296982,0.27129775285720825,-0.9553247690200806,-0.11726962774991989,0.26628872752189636,-0.9553247690200806,-0.12823788821697235,0.26083648204803467,-0.9553247690200806,-0.13899271190166473,0.25495007634162903,-0.9553247690200806,-0.14951619505882263,0.24863936007022858,-0.9553247690200806,-0.15979081392288208,0.2419148087501526,-0.9553247690200806,-0.16979949176311493,0.23478759825229645,-0.9553247690200806,-0.17952553927898407,0.22726961970329285,-0.9553247690200806,-0.18895278871059418,0.21937336027622223,-0.9553247690200806,-0.19806554913520813,0.21111199259757996,-0.9553247690200806,-0.2068486511707306,0.20249924063682556,-0.9553247690200806,-0.2152874767780304,0.19354943931102753,-0.9553247690200806,-0.22336797416210175,0.1842775046825409,-0.9553247690200806,-0.2310766875743866,0.1746988594532013,-0.9553247690200806,-0.23840080201625824,0.16482944786548615,-0.9553247690200806,-0.2453281283378601,0.15468569099903107,-0.9553247690200806,-0.25184711813926697,0.1442844718694687,-0.9553247690200806,-0.2579469382762909,0.13364310562610626,-0.9553247690200806,-0.26361745595932007,0.12277930229902267,-0.9553247690200806,-0.2688491642475128,0.11171115189790726,-0.9553247690200806,-0.2736334204673767,0.10045706480741501,-0.9553247690200806,-0.27796226739883423,0.0890357717871666,-0.9553247690200806,-0.2818284332752228,0.07746629416942596,-0.9553247690200806,-0.2852255403995514,0.06576788425445557,-0.9553247690200806,-0.2881479263305664,0.05396000295877457,-0.9553247690200806,-0.29059070348739624,0.042062316089868546,-0.9553247690200806,-0.29254981875419617,0.03009461611509323,-0.9553247690200806,-0.2940220236778259,0.018076829612255096,-0.9553247690200806,-0.29500484466552734,0.006028954405337572,-0.9553247690200806,-0.2954966723918915,-0.006028954405337572,-0.9553247690200806,-0.2954966723918915,-0.018076829612255096,-0.9553247690200806,-0.29500484466552734,-0.03009461611509323,-0.9553247690200806,-0.2940220236778259,-0.042062316089868546,-0.9553247690200806,-0.29254981875419617,-0.05396000295877457,-0.9553247690200806,-0.29059070348739624,-0.06576788425445557,-0.9553247690200806,-0.2881479263305664,-0.07746629416942596,-0.9553247690200806,-0.2852255403995514,-0.0890357717871666,-0.9553247690200806,-0.2818284332752228,-0.10045706480741501,-0.9553247690200806,-0.27796226739883423,-0.11171115189790726,-0.9553247690200806,-0.2736334204673767,-0.12277930229902267,-0.9553247690200806,-0.2688491642475128,-0.13364310562610626,-0.9553247690200806,-0.26361745595932007,-0.1442844718694687,-0.9553247690200806,-0.2579469382762909,-0.15468569099903107,-0.9553247690200806,-0.25184711813926697,-0.16482944786548615,-0.9553247690200806,-0.2453281283378601,-0.1746988594532013,-0.9553247690200806,-0.23840080201625824,-0.1842775046825409,-0.9553247690200806,-0.2310766875743866,-0.19354943931102753,-0.9553247690200806,-0.22336797416210175,-0.20249924063682556,-0.9553247690200806,-0.2152874767780304,-0.21111199259757996,-0.9553247690200806,-0.2068486511707306,-0.21937336027622223,-0.9553247690200806,-0.19806554913520813,-0.22726961970329285,-0.9553247690200806,-0.18895278871059418,-0.23478759825229645,-0.9553247690200806,-0.17952553927898407,-0.2419148087501526,-0.9553247690200806,-0.16979949176311493,-0.24863936007022858,-0.9553247690200806,-0.15979081392288208,-0.25495007634162903,-0.9553247690200806,-0.14951619505882263,-0.26083648204803467,-0.9553247690200806,-0.13899271190166473,-0.26628872752189636,-0.9553247690200806,-0.12823788821697235,-0.27129775285720825,-0.9553247690200806,-0.11726962774991989,-0.27585524320602417,-0.9553247690200806,-0.10610618442296982,-0.27995359897613525,-0.9553247690200806,-0.09476613998413086,-0.2835859954357147,-0.9553247690200806,-0.08326835930347443,-0.28674638271331787,-0.9553247690200806,-0.07163199037313461,-0.28942954540252686,-0.9553247690200806,-0.05987640097737312,-0.29163095355033875,-0.9553247690200806,-0.04802115261554718,-0.29334697127342224,-0.9553247690200806,-0.03608597442507744,-0.2945747375488281,-0.9553247690200806,-0.024090735241770744,-0.29531222581863403,-0.9553247690200806,-0.01205539982765913,-0.2955581843852997,-0.9553247690200806,-3.6195438371850364e-17,-0.29531222581863403,-0.9553247690200806,0.01205539982765913,-0.2945747375488281,-0.9553247690200806,0.024090735241770744,-0.29334697127342224,-0.9553247690200806,0.03608597442507744,-0.29163095355033875,-0.9553247690200806,0.04802115261554718,-0.28942954540252686,-0.9553247690200806,0.05987640097737312,-0.28674638271331787,-0.9553247690200806,0.07163199037313461,-0.2835859954357147,-0.9553247690200806,0.08326835930347443,-0.27995359897613525,-0.9553247690200806,0.09476613998413086,-0.27585524320602417,-0.9553247690200806,0.10610618442296982,-0.27129775285720825,-0.9553247690200806,0.11726962774991989,-0.26628872752189636,-0.9553247690200806,0.12823788821697235,-0.26083648204803467,-0.9553247690200806,0.13899271190166473,-0.25495007634162903,-0.9553247690200806,0.14951619505882263,-0.24863936007022858,-0.9553247690200806,0.15979081392288208,-0.2419148087501526,-0.9553247690200806,0.16979949176311493,-0.23478759825229645,-0.9553247690200806,0.17952553927898407,-0.22726961970329285,-0.9553247690200806,0.18895278871059418,-0.21937336027622223,-0.9553247690200806,0.19806554913520813,-0.21111199259757996,-0.9553247690200806,0.2068486511707306,-0.20249924063682556,-0.9553247690200806,0.2152874767780304,-0.19354943931102753,-0.9553247690200806,0.22336797416210175,-0.1842775046825409,-0.9553247690200806,0.2310766875743866,-0.1746988594532013,-0.9553247690200806,0.23840080201625824,-0.16482944786548615,-0.9553247690200806,0.2453281283378601,-0.15468569099903107,-0.9553247690200806,0.25184711813926697,-0.1442844718694687,-0.9553247690200806,0.2579469382762909,-0.13364310562610626,-0.9553247690200806,0.26361745595932007,-0.12277930229902267,-0.9553247690200806,0.2688491642475128,-0.11171115189790726,-0.9553247690200806,0.2736334204673767,-0.10045706480741501,-0.9553247690200806,0.27796226739883423,-0.0890357717871666,-0.9553247690200806,0.2818284332752228,-0.07746629416942596,-0.9553247690200806,0.2852255403995514,-0.06576788425445557,-0.9553247690200806,0.2881479263305664,-0.05396000295877457,-0.9553247690200806,0.29059070348739624,-0.042062316089868546,-0.9553247690200806,0.29254981875419617,-0.03009461611509323,-0.9553247690200806,0.2940220236778259,-0.018076829612255096,-0.9553247690200806,0.29500484466552734,-0.006028954405337572,-0.9553247690200806,0.2954966723918915,0.006028954405337572,-0.9553247690200806,0.2954966723918915,0.018076829612255096,-0.9553247690200806,0.29500484466552734,0.03009461611509323,-0.9553247690200806,0.2940220236778259,0.042062316089868546,-0.9553247690200806,0.29254981875419617,0.05396000295877457,-0.9553247690200806,0.29059070348739624,0.06576788425445557,-0.9553247690200806,0.2881479263305664,0.07746629416942596,-0.9553247690200806,0.2852255403995514,0.0890357717871666,-0.9553247690200806,0.2818284332752228,0.10045706480741501,-0.9553247690200806,0.27796226739883423,0.11171115189790726,-0.9553247690200806,0.2736334204673767,0.12277930229902267,-0.9553247690200806,0.2688491642475128,0.13364310562610626,-0.9553247690200806,0.26361745595932007,0.1442844718694687,-0.9553247690200806,0.2579469382762909,0.15468569099903107,-0.9553247690200806,0.25184711813926697,0.16482944786548615,-0.9553247690200806,0.2453281283378601,0.1746988594532013,-0.9553247690200806,0.23840080201625824,0.1842775046825409,-0.9553247690200806,0.2310766875743866,0.19354943931102753,-0.9553247690200806,0.22336797416210175,0.20249924063682556,-0.9553247690200806,0.2152874767780304,0.21111199259757996,-0.9553247690200806,0.2068486511707306,0.21937336027622223,-0.9553247690200806,0.19806554913520813,0.22726961970329285,-0.9553247690200806,0.18895278871059418,0.23478759825229645,-0.9553247690200806,0.17952553927898407,0.2419148087501526,-0.9553247690200806,0.16979949176311493,0.24863936007022858,-0.9553247690200806,0.15979081392288208,0.25495007634162903,-0.9553247690200806,0.14951619505882263,0.26083648204803467,-0.9553247690200806,0.13899271190166473,0.26628872752189636,-0.9553247690200806,0.12823788821697235,0.27129775285720825,-0.9553247690200806,0.11726962774991989,0.27585524320602417,-0.9553247690200806,0.10610618442296982,0.27995359897613525,-0.9553247690200806,0.09476613998413086,0.2835859954357147,-0.9553247690200806,0.08326835930347443,0.28674638271331787,-0.9553247690200806,0.07163199037313461,0.28942954540252686,-0.9553247690200806,0.05987640097737312,0.29163095355033875,-0.9553247690200806,0.04802115261554718,0.29334697127342224,-0.9553247690200806,0.03608597442507744,0.2945747375488281,-0.9553247690200806,0.024090735241770744,0.29531222581863403,-0.9553247690200806,0.01205539982765913,0.2955581843852997,-0.9553247690200806,7.239087674370073e-17,0.3622095584869385,-0.9320966601371765,0.0,0.3619081377983093,-0.9320966601371765,-0.014774016104638577,0.3610043525695801,-0.9320966601371765,-0.029523441568017006,0.3594996929168701,-0.9320966601371765,-0.04422372952103615,0.3573966920375824,-0.9320966601371765,-0.05885041132569313,0.3546988368034363,-0.9320966601371765,-0.07337914407253265,0.3514106273651123,-0.9320966601371765,-0.0877857357263565,0.3475375175476074,-0.9320966601371765,-0.10204622894525528,0.34308597445487976,-0.9320966601371765,-0.11613687127828598,0.33806341886520386,-0.9320966601371765,-0.1300342082977295,0.3324781656265259,-0.9320966601371765,-0.14371512830257416,0.3263395428657532,-0.9320966601371765,-0.157156839966774,0.31965774297714233,-0.9320966601371765,-0.17033697664737701,0.31244394183158875,-0.9320966601371765,-0.1832336187362671,0.3047100603580475,-0.9320966601371765,-0.19582527875900269,0.2964690625667572,-0.9320966601371765,-0.20809100568294525,0.28773459792137146,-0.9320966601371765,-0.22001038491725922,0.27852123975753784,-0.9320966601371765,-0.23156358301639557,0.2688443064689636,-0.9320966601371765,-0.24273136258125305,0.25871989130973816,-0.9320966601371765,-0.25349512696266174,0.24816487729549408,-0.9320966601371765,-0.26383697986602783,0.23719681799411774,-0.9320966601371765,-0.2737397253513336,0.2258339673280716,-0.9320966601371765,-0.2831868529319763,0.21409523487091064,-0.9320966601371765,-0.29216262698173523,0.20200017094612122,-0.9320966601371765,-0.3006521165370941,0.189568892121315,-0.9320966601371765,-0.3086412250995636,0.17682209610939026,-0.9320966601371765,-0.3161166310310364,0.16378098726272583,-0.9320966601371765,-0.3230658769607544,0.15046729147434235,-0.9320966601371765,-0.3294773995876312,0.13690315186977386,-0.9320966601371765,-0.33534055948257446,0.12311115860939026,-0.9320966601371765,-0.34064558148384094,0.10911425203084946,-0.9320966601371765,-0.3453836143016815,0.09493573755025864,-0.9320966601371765,-0.3495468199253082,0.08059921115636826,-0.9320966601371765,-0.35312822461128235,0.06612853705883026,-0.9320966601371765,-0.35612186789512634,0.05154779553413391,-0.9320966601371765,-0.35852280259132385,0.03688126057386398,-0.9320966601371765,-0.36032700538635254,0.02215333841741085,-0.9320966601371765,-0.36153146624565125,0.007388545200228691,-0.9320966601371765,-0.36213418841362,-0.007388545200228691,-0.9320966601371765,-0.36213418841362,-0.02215333841741085,-0.9320966601371765,-0.36153146624565125,-0.03688126057386398,-0.9320966601371765,-0.36032700538635254,-0.05154779553413391,-0.9320966601371765,-0.35852280259132385,-0.06612853705883026,-0.9320966601371765,-0.35612186789512634,-0.08059921115636826,-0.9320966601371765,-0.35312822461128235,-0.09493573755025864,-0.9320966601371765,-0.3495468199253082,-0.10911425203084946,-0.9320966601371765,-0.3453836143016815,-0.12311115860939026,-0.9320966601371765,-0.34064558148384094,-0.13690315186977386,-0.9320966601371765,-0.33534055948257446,-0.15046729147434235,-0.9320966601371765,-0.3294773995876312,-0.16378098726272583,-0.9320966601371765,-0.3230658769607544,-0.17682209610939026,-0.9320966601371765,-0.3161166310310364,-0.189568892121315,-0.9320966601371765,-0.3086412250995636,-0.20200017094612122,-0.9320966601371765,-0.3006521165370941,-0.21409523487091064,-0.9320966601371765,-0.29216262698173523,-0.2258339673280716,-0.9320966601371765,-0.2831868529319763,-0.23719681799411774,-0.9320966601371765,-0.2737397253513336,-0.24816487729549408,-0.9320966601371765,-0.26383697986602783,-0.25871989130973816,-0.9320966601371765,-0.25349512696266174,-0.2688443064689636,-0.9320966601371765,-0.24273136258125305,-0.27852123975753784,-0.9320966601371765,-0.23156358301639557,-0.28773459792137146,-0.9320966601371765,-0.22001038491725922,-0.2964690625667572,-0.9320966601371765,-0.20809100568294525,-0.3047100603580475,-0.9320966601371765,-0.19582527875900269,-0.31244394183158875,-0.9320966601371765,-0.1832336187362671,-0.31965774297714233,-0.9320966601371765,-0.17033697664737701,-0.3263395428657532,-0.9320966601371765,-0.157156839966774,-0.3324781656265259,-0.9320966601371765,-0.14371512830257416,-0.33806341886520386,-0.9320966601371765,-0.1300342082977295,-0.34308597445487976,-0.9320966601371765,-0.11613687127828598,-0.3475375175476074,-0.9320966601371765,-0.10204622894525528,-0.3514106273651123,-0.9320966601371765,-0.0877857357263565,-0.3546988368034363,-0.9320966601371765,-0.07337914407253265,-0.3573966920375824,-0.9320966601371765,-0.05885041132569313,-0.3594996929168701,-0.9320966601371765,-0.04422372952103615,-0.3610043525695801,-0.9320966601371765,-0.029523441568017006,-0.3619081377983093,-0.9320966601371765,-0.014774016104638577,-0.3622095584869385,-0.9320966601371765,-4.435787798551133e-17,-0.3619081377983093,-0.9320966601371765,0.014774016104638577,-0.3610043525695801,-0.9320966601371765,0.029523441568017006,-0.3594996929168701,-0.9320966601371765,0.04422372952103615,-0.3573966920375824,-0.9320966601371765,0.05885041132569313,-0.3546988368034363,-0.9320966601371765,0.07337914407253265,-0.3514106273651123,-0.9320966601371765,0.0877857357263565,-0.3475375175476074,-0.9320966601371765,0.10204622894525528,-0.34308597445487976,-0.9320966601371765,0.11613687127828598,-0.33806341886520386,-0.9320966601371765,0.1300342082977295,-0.3324781656265259,-0.9320966601371765,0.14371512830257416,-0.3263395428657532,-0.9320966601371765,0.157156839966774,-0.31965774297714233,-0.9320966601371765,0.17033697664737701,-0.31244394183158875,-0.9320966601371765,0.1832336187362671,-0.3047100603580475,-0.9320966601371765,0.19582527875900269,-0.2964690625667572,-0.9320966601371765,0.20809100568294525,-0.28773459792137146,-0.9320966601371765,0.22001038491725922,-0.27852123975753784,-0.9320966601371765,0.23156358301639557,-0.2688443064689636,-0.9320966601371765,0.24273136258125305,-0.25871989130973816,-0.9320966601371765,0.25349512696266174,-0.24816487729549408,-0.9320966601371765,0.26383697986602783,-0.23719681799411774,-0.9320966601371765,0.2737397253513336,-0.2258339673280716,-0.9320966601371765,0.2831868529319763,-0.21409523487091064,-0.9320966601371765,0.29216262698173523,-0.20200017094612122,-0.9320966601371765,0.3006521165370941,-0.189568892121315,-0.9320966601371765,0.3086412250995636,-0.17682209610939026,-0.9320966601371765,0.3161166310310364,-0.16378098726272583,-0.9320966601371765,0.3230658769607544,-0.15046729147434235,-0.9320966601371765,0.3294773995876312,-0.13690315186977386,-0.9320966601371765,0.33534055948257446,-0.12311115860939026,-0.9320966601371765,0.34064558148384094,-0.10911425203084946,-0.9320966601371765,0.3453836143016815,-0.09493573755025864,-0.9320966601371765,0.3495468199253082,-0.08059921115636826,-0.9320966601371765,0.35312822461128235,-0.06612853705883026,-0.9320966601371765,0.35612186789512634,-0.05154779553413391,-0.9320966601371765,0.35852280259132385,-0.03688126057386398,-0.9320966601371765,0.36032700538635254,-0.02215333841741085,-0.9320966601371765,0.36153146624565125,-0.007388545200228691,-0.9320966601371765,0.36213418841362,0.007388545200228691,-0.9320966601371765,0.36213418841362,0.02215333841741085,-0.9320966601371765,0.36153146624565125,0.03688126057386398,-0.9320966601371765,0.36032700538635254,0.05154779553413391,-0.9320966601371765,0.35852280259132385,0.06612853705883026,-0.9320966601371765,0.35612186789512634,0.08059921115636826,-0.9320966601371765,0.35312822461128235,0.09493573755025864,-0.9320966601371765,0.3495468199253082,0.10911425203084946,-0.9320966601371765,0.3453836143016815,0.12311115860939026,-0.9320966601371765,0.34064558148384094,0.13690315186977386,-0.9320966601371765,0.33534055948257446,0.15046729147434235,-0.9320966601371765,0.3294773995876312,0.16378098726272583,-0.9320966601371765,0.3230658769607544,0.17682209610939026,-0.9320966601371765,0.3161166310310364,0.189568892121315,-0.9320966601371765,0.3086412250995636,0.20200017094612122,-0.9320966601371765,0.3006521165370941,0.21409523487091064,-0.9320966601371765,0.29216262698173523,0.2258339673280716,-0.9320966601371765,0.2831868529319763,0.23719681799411774,-0.9320966601371765,0.2737397253513336,0.24816487729549408,-0.9320966601371765,0.26383697986602783,0.25871989130973816,-0.9320966601371765,0.25349512696266174,0.2688443064689636,-0.9320966601371765,0.24273136258125305,0.27852123975753784,-0.9320966601371765,0.23156358301639557,0.28773459792137146,-0.9320966601371765,0.22001038491725922,0.2964690625667572,-0.9320966601371765,0.20809100568294525,0.3047100603580475,-0.9320966601371765,0.19582527875900269,0.31244394183158875,-0.9320966601371765,0.1832336187362671,0.31965774297714233,-0.9320966601371765,0.17033697664737701,0.3263395428657532,-0.9320966601371765,0.157156839966774,0.3324781656265259,-0.9320966601371765,0.14371512830257416,0.33806341886520386,-0.9320966601371765,0.1300342082977295,0.34308597445487976,-0.9320966601371765,0.11613687127828598,0.3475375175476074,-0.9320966601371765,0.10204622894525528,0.3514106273651123,-0.9320966601371765,0.0877857357263565,0.3546988368034363,-0.9320966601371765,0.07337914407253265,0.3573966920375824,-0.9320966601371765,0.05885041132569313,0.3594996929168701,-0.9320966601371765,0.04422372952103615,0.3610043525695801,-0.9320966601371765,0.029523441568017006,0.3619081377983093,-0.9320966601371765,0.014774016104638577,0.3622095584869385,-0.9320966601371765,8.871575597102266e-17,0.4270564317703247,-0.9042249917984009,0.0,0.42670103907585144,-0.9042249917984009,-0.017419029027223587,0.4256354570388794,-0.9042249917984009,-0.034809064120054245,0.42386141419410706,-0.9042249917984009,-0.05214116349816322,0.4213819205760956,-0.9042249917984009,-0.06938648223876953,0.4182010591030121,-0.9042249917984009,-0.08651630580425262,0.41432416439056396,-0.9042249917984009,-0.10350213944911957,0.4097576439380646,-0.9042249917984009,-0.12031570076942444,0.40450915694236755,-0.9042249917984009,-0.1369290053844452,0.3985873758792877,-0.9042249917984009,-0.15331441164016724,0.3920021951198578,-0.9042249917984009,-0.16944463551044464,0.38476455211639404,-0.9042249917984009,-0.18529284000396729,0.3768865168094635,-0.9042249917984009,-0.20083265006542206,0.36838120222091675,-0.9042249917984009,-0.21603818237781525,0.35926273465156555,-0.9042249917984009,-0.2308841347694397,0.34954631328582764,-0.9042249917984009,-0.2453458160161972,0.3392481207847595,-0.9042249917984009,-0.2593991458415985,0.32838526368141174,-0.9042249917984009,-0.2730207145214081,0.316975861787796,-0.9042249917984009,-0.28618788719177246,0.3050388693809509,-0.9042249917984009,-0.2988787293434143,0.29259419441223145,-0.9042249917984009,-0.31107208132743835,0.27966248989105225,-0.9042249917984009,-0.32274770736694336,0.266265332698822,-0.9042249917984009,-0.33388617634773254,0.25242501497268677,-0.9042249917984009,-0.344468891620636,0.2381645292043686,-0.9042249917984009,-0.35447829961776733,0.22350767254829407,-0.9042249917984009,-0.3638976812362671,0.20847879350185394,-0.9042249917984009,-0.3727114200592041,0.19310292601585388,-0.9042249917984009,-0.38090479373931885,0.17740565538406372,-0.9042249917984009,-0.38846421241760254,0.16141311824321747,-0.9042249917984009,-0.3953770399093628,0.14515191316604614,-0.9042249917984009,-0.40163183212280273,0.12864913046360016,-0.9042249917984009,-0.4072181284427643,0.11193221062421799,-0.9042249917984009,-0.41212666034698486,0.09502899646759033,-0.9042249917984009,-0.41634923219680786,0.07796761393547058,-0.9042249917984009,-0.41987884044647217,0.060776468366384506,-0.9042249917984009,-0.4227096140384674,0.0434841625392437,-0.9042249917984009,-0.42483681440353394,0.026119481772184372,-0.9042249917984009,-0.42625692486763,0.008711326867341995,-0.9042249917984009,-0.426967591047287,-0.008711326867341995,-0.9042249917984009,-0.426967591047287,-0.026119481772184372,-0.9042249917984009,-0.42625692486763,-0.0434841625392437,-0.9042249917984009,-0.42483681440353394,-0.060776468366384506,-0.9042249917984009,-0.4227096140384674,-0.07796761393547058,-0.9042249917984009,-0.41987884044647217,-0.09502899646759033,-0.9042249917984009,-0.41634923219680786,-0.11193221062421799,-0.9042249917984009,-0.41212666034698486,-0.12864913046360016,-0.9042249917984009,-0.4072181284427643,-0.14515191316604614,-0.9042249917984009,-0.40163183212280273,-0.16141311824321747,-0.9042249917984009,-0.3953770399093628,-0.17740565538406372,-0.9042249917984009,-0.38846421241760254,-0.19310292601585388,-0.9042249917984009,-0.38090479373931885,-0.20847879350185394,-0.9042249917984009,-0.3727114200592041,-0.22350767254829407,-0.9042249917984009,-0.3638976812362671,-0.2381645292043686,-0.9042249917984009,-0.35447829961776733,-0.25242501497268677,-0.9042249917984009,-0.344468891620636,-0.266265332698822,-0.9042249917984009,-0.33388617634773254,-0.27966248989105225,-0.9042249917984009,-0.32274770736694336,-0.29259419441223145,-0.9042249917984009,-0.31107208132743835,-0.3050388693809509,-0.9042249917984009,-0.2988787293434143,-0.316975861787796,-0.9042249917984009,-0.28618788719177246,-0.32838526368141174,-0.9042249917984009,-0.2730207145214081,-0.3392481207847595,-0.9042249917984009,-0.2593991458415985,-0.34954631328582764,-0.9042249917984009,-0.2453458160161972,-0.35926273465156555,-0.9042249917984009,-0.2308841347694397,-0.36838120222091675,-0.9042249917984009,-0.21603818237781525,-0.3768865168094635,-0.9042249917984009,-0.20083265006542206,-0.38476455211639404,-0.9042249917984009,-0.18529284000396729,-0.3920021951198578,-0.9042249917984009,-0.16944463551044464,-0.3985873758792877,-0.9042249917984009,-0.15331441164016724,-0.40450915694236755,-0.9042249917984009,-0.1369290053844452,-0.4097576439380646,-0.9042249917984009,-0.12031570076942444,-0.41432416439056396,-0.9042249917984009,-0.10350213944911957,-0.4182010591030121,-0.9042249917984009,-0.08651630580425262,-0.4213819205760956,-0.9042249917984009,-0.06938648223876953,-0.42386141419410706,-0.9042249917984009,-0.05214116349816322,-0.4256354570388794,-0.9042249917984009,-0.034809064120054245,-0.42670103907585144,-0.9042249917984009,-0.017419029027223587,-0.4270564317703247,-0.9042249917984009,-5.229933133544508e-17,-0.42670103907585144,-0.9042249917984009,0.017419029027223587,-0.4256354570388794,-0.9042249917984009,0.034809064120054245,-0.42386141419410706,-0.9042249917984009,0.05214116349816322,-0.4213819205760956,-0.9042249917984009,0.06938648223876953,-0.4182010591030121,-0.9042249917984009,0.08651630580425262,-0.41432416439056396,-0.9042249917984009,0.10350213944911957,-0.4097576439380646,-0.9042249917984009,0.12031570076942444,-0.40450915694236755,-0.9042249917984009,0.1369290053844452,-0.3985873758792877,-0.9042249917984009,0.15331441164016724,-0.3920021951198578,-0.9042249917984009,0.16944463551044464,-0.38476455211639404,-0.9042249917984009,0.18529284000396729,-0.3768865168094635,-0.9042249917984009,0.20083265006542206,-0.36838120222091675,-0.9042249917984009,0.21603818237781525,-0.35926273465156555,-0.9042249917984009,0.2308841347694397,-0.34954631328582764,-0.9042249917984009,0.2453458160161972,-0.3392481207847595,-0.9042249917984009,0.2593991458415985,-0.32838526368141174,-0.9042249917984009,0.2730207145214081,-0.316975861787796,-0.9042249917984009,0.28618788719177246,-0.3050388693809509,-0.9042249917984009,0.2988787293434143,-0.29259419441223145,-0.9042249917984009,0.31107208132743835,-0.27966248989105225,-0.9042249917984009,0.32274770736694336,-0.266265332698822,-0.9042249917984009,0.33388617634773254,-0.25242501497268677,-0.9042249917984009,0.344468891620636,-0.2381645292043686,-0.9042249917984009,0.35447829961776733,-0.22350767254829407,-0.9042249917984009,0.3638976812362671,-0.20847879350185394,-0.9042249917984009,0.3727114200592041,-0.19310292601585388,-0.9042249917984009,0.38090479373931885,-0.17740565538406372,-0.9042249917984009,0.38846421241760254,-0.16141311824321747,-0.9042249917984009,0.3953770399093628,-0.14515191316604614,-0.9042249917984009,0.40163183212280273,-0.12864913046360016,-0.9042249917984009,0.4072181284427643,-0.11193221062421799,-0.9042249917984009,0.41212666034698486,-0.09502899646759033,-0.9042249917984009,0.41634923219680786,-0.07796761393547058,-0.9042249917984009,0.41987884044647217,-0.060776468366384506,-0.9042249917984009,0.4227096140384674,-0.0434841625392437,-0.9042249917984009,0.42483681440353394,-0.026119481772184372,-0.9042249917984009,0.42625692486763,-0.008711326867341995,-0.9042249917984009,0.426967591047287,0.008711326867341995,-0.9042249917984009,0.426967591047287,0.026119481772184372,-0.9042249917984009,0.42625692486763,0.0434841625392437,-0.9042249917984009,0.42483681440353394,0.060776468366384506,-0.9042249917984009,0.4227096140384674,0.07796761393547058,-0.9042249917984009,0.41987884044647217,0.09502899646759033,-0.9042249917984009,0.41634923219680786,0.11193221062421799,-0.9042249917984009,0.41212666034698486,0.12864913046360016,-0.9042249917984009,0.4072181284427643,0.14515191316604614,-0.9042249917984009,0.40163183212280273,0.16141311824321747,-0.9042249917984009,0.3953770399093628,0.17740565538406372,-0.9042249917984009,0.38846421241760254,0.19310292601585388,-0.9042249917984009,0.38090479373931885,0.20847879350185394,-0.9042249917984009,0.3727114200592041,0.22350767254829407,-0.9042249917984009,0.3638976812362671,0.2381645292043686,-0.9042249917984009,0.35447829961776733,0.25242501497268677,-0.9042249917984009,0.344468891620636,0.266265332698822,-0.9042249917984009,0.33388617634773254,0.27966248989105225,-0.9042249917984009,0.32274770736694336,0.29259419441223145,-0.9042249917984009,0.31107208132743835,0.3050388693809509,-0.9042249917984009,0.2988787293434143,0.316975861787796,-0.9042249917984009,0.28618788719177246,0.32838526368141174,-0.9042249917984009,0.2730207145214081,0.3392481207847595,-0.9042249917984009,0.2593991458415985,0.34954631328582764,-0.9042249917984009,0.2453458160161972,0.35926273465156555,-0.9042249917984009,0.2308841347694397,0.36838120222091675,-0.9042249917984009,0.21603818237781525,0.3768865168094635,-0.9042249917984009,0.20083265006542206,0.38476455211639404,-0.9042249917984009,0.18529284000396729,0.3920021951198578,-0.9042249917984009,0.16944463551044464,0.3985873758792877,-0.9042249917984009,0.15331441164016724,0.40450915694236755,-0.9042249917984009,0.1369290053844452,0.4097576439380646,-0.9042249917984009,0.12031570076942444,0.41432416439056396,-0.9042249917984009,0.10350213944911957,0.4182010591030121,-0.9042249917984009,0.08651630580425262,0.4213819205760956,-0.9042249917984009,0.06938648223876953,0.42386141419410706,-0.9042249917984009,0.05214116349816322,0.4256354570388794,-0.9042249917984009,0.034809064120054245,0.42670103907585144,-0.9042249917984009,0.017419029027223587,0.4270564317703247,-0.9042249917984009,1.0459866267089016e-16,0.48977574706077576,-0.871848464012146,0.0,0.48936814069747925,-0.871848464012146,-0.019977260380983353,0.4881460666656494,-0.871848464012146,-0.039921268820762634,0.48611149191856384,-0.871848464012146,-0.05979883298277855,0.4832678437232971,-0.871848464012146,-0.07957687228918076,0.4796198308467865,-0.871848464012146,-0.09922245889902115,0.4751735329627991,-0.871848464012146,-0.11870289593935013,0.4699363708496094,-0.871848464012146,-0.13798576593399048,0.4639170467853546,-0.871848464012146,-0.15703898668289185,0.45712557435035706,-0.871848464012146,-0.17583081126213074,0.449573278427124,-0.871848464012146,-0.19432999193668365,0.44127270579338074,-0.871848464012146,-0.21250572800636292,0.4322376549243927,-0.871848464012146,-0.230327770113945,0.4224832057952881,-0.871848464012146,-0.24776645004749298,0.4120255708694458,-0.871848464012146,-0.2647927701473236,0.40088215470314026,-0.871848464012146,-0.2813783288002014,0.389071524143219,-0.871848464012146,-0.29749560356140137,0.3766133189201355,-0.871848464012146,-0.3131176829338074,0.3635282814502716,-0.871848464012146,-0.32821863889694214,0.34983816742897034,-0.871848464012146,-0.34277328848838806,0.33556580543518066,-0.871848464012146,-0.35675743222236633,0.3207348883152008,-0.871848464012146,-0.37014779448509216,0.30537018179893494,-0.871848464012146,-0.38292208313941956,0.2894971966743469,-0.871848464012146,-0.3950590491294861,0.27314236760139465,-0.871848464012146,-0.4065384566783905,0.2563329339027405,-0.871848464012146,-0.4173412322998047,0.23909686505794525,-0.871848464012146,-0.42744937539100647,0.22146283090114594,-0.871848464012146,-0.4368460774421692,0.2034602016210556,-0.871848464012146,-0.4455156922340393,0.1851189136505127,-0.871848464012146,-0.4534437656402588,0.1664695292711258,-0.871848464012146,-0.46061715483665466,0.1475430727005005,-0.871848464012146,-0.4670238792896271,0.12837104499340057,-0.871848464012146,-0.4726532995700836,0.10898535698652267,-0.871848464012146,-0.47749602794647217,0.08941826969385147,-0.871848464012146,-0.48154401779174805,0.06970235705375671,-0.871848464012146,-0.48479053378105164,0.04987042769789696,-0.871848464012146,-0.4872301518917084,0.02995549701154232,-0.871848464012146,-0.48885881900787354,0.009990708902478218,-0.871848464012146,-0.48967382311820984,-0.009990708902478218,-0.871848464012146,-0.48967382311820984,-0.02995549701154232,-0.871848464012146,-0.48885881900787354,-0.04987042769789696,-0.871848464012146,-0.4872301518917084,-0.06970235705375671,-0.871848464012146,-0.48479053378105164,-0.08941826969385147,-0.871848464012146,-0.48154401779174805,-0.10898535698652267,-0.871848464012146,-0.47749602794647217,-0.12837104499340057,-0.871848464012146,-0.4726532995700836,-0.1475430727005005,-0.871848464012146,-0.4670238792896271,-0.1664695292711258,-0.871848464012146,-0.46061715483665466,-0.1851189136505127,-0.871848464012146,-0.4534437656402588,-0.2034602016210556,-0.871848464012146,-0.4455156922340393,-0.22146283090114594,-0.871848464012146,-0.4368460774421692,-0.23909686505794525,-0.871848464012146,-0.42744937539100647,-0.2563329339027405,-0.871848464012146,-0.4173412322998047,-0.27314236760139465,-0.871848464012146,-0.4065384566783905,-0.2894971966743469,-0.871848464012146,-0.3950590491294861,-0.30537018179893494,-0.871848464012146,-0.38292208313941956,-0.3207348883152008,-0.871848464012146,-0.37014779448509216,-0.33556580543518066,-0.871848464012146,-0.35675743222236633,-0.34983816742897034,-0.871848464012146,-0.34277328848838806,-0.3635282814502716,-0.871848464012146,-0.32821863889694214,-0.3766133189201355,-0.871848464012146,-0.3131176829338074,-0.389071524143219,-0.871848464012146,-0.29749560356140137,-0.40088215470314026,-0.871848464012146,-0.2813783288002014,-0.4120255708694458,-0.871848464012146,-0.2647927701473236,-0.4224832057952881,-0.871848464012146,-0.24776645004749298,-0.4322376549243927,-0.871848464012146,-0.230327770113945,-0.44127270579338074,-0.871848464012146,-0.21250572800636292,-0.449573278427124,-0.871848464012146,-0.19432999193668365,-0.45712557435035706,-0.871848464012146,-0.17583081126213074,-0.4639170467853546,-0.871848464012146,-0.15703898668289185,-0.4699363708496094,-0.871848464012146,-0.13798576593399048,-0.4751735329627991,-0.871848464012146,-0.11870289593935013,-0.4796198308467865,-0.871848464012146,-0.09922245889902115,-0.4832678437232971,-0.871848464012146,-0.07957687228918076,-0.48611149191856384,-0.871848464012146,-0.05979883298277855,-0.4881460666656494,-0.871848464012146,-0.039921268820762634,-0.48936814069747925,-0.871848464012146,-0.019977260380983353,-0.48977574706077576,-0.871848464012146,-5.998022940986952e-17,-0.48936814069747925,-0.871848464012146,0.019977260380983353,-0.4881460666656494,-0.871848464012146,0.039921268820762634,-0.48611149191856384,-0.871848464012146,0.05979883298277855,-0.4832678437232971,-0.871848464012146,0.07957687228918076,-0.4796198308467865,-0.871848464012146,0.09922245889902115,-0.4751735329627991,-0.871848464012146,0.11870289593935013,-0.4699363708496094,-0.871848464012146,0.13798576593399048,-0.4639170467853546,-0.871848464012146,0.15703898668289185,-0.45712557435035706,-0.871848464012146,0.17583081126213074,-0.449573278427124,-0.871848464012146,0.19432999193668365,-0.44127270579338074,-0.871848464012146,0.21250572800636292,-0.4322376549243927,-0.871848464012146,0.230327770113945,-0.4224832057952881,-0.871848464012146,0.24776645004749298,-0.4120255708694458,-0.871848464012146,0.2647927701473236,-0.40088215470314026,-0.871848464012146,0.2813783288002014,-0.389071524143219,-0.871848464012146,0.29749560356140137,-0.3766133189201355,-0.871848464012146,0.3131176829338074,-0.3635282814502716,-0.871848464012146,0.32821863889694214,-0.34983816742897034,-0.871848464012146,0.34277328848838806,-0.33556580543518066,-0.871848464012146,0.35675743222236633,-0.3207348883152008,-0.871848464012146,0.37014779448509216,-0.30537018179893494,-0.871848464012146,0.38292208313941956,-0.2894971966743469,-0.871848464012146,0.3950590491294861,-0.27314236760139465,-0.871848464012146,0.4065384566783905,-0.2563329339027405,-0.871848464012146,0.4173412322998047,-0.23909686505794525,-0.871848464012146,0.42744937539100647,-0.22146283090114594,-0.871848464012146,0.4368460774421692,-0.2034602016210556,-0.871848464012146,0.4455156922340393,-0.1851189136505127,-0.871848464012146,0.4534437656402588,-0.1664695292711258,-0.871848464012146,0.46061715483665466,-0.1475430727005005,-0.871848464012146,0.4670238792896271,-0.12837104499340057,-0.871848464012146,0.4726532995700836,-0.10898535698652267,-0.871848464012146,0.47749602794647217,-0.08941826969385147,-0.871848464012146,0.48154401779174805,-0.06970235705375671,-0.871848464012146,0.48479053378105164,-0.04987042769789696,-0.871848464012146,0.4872301518917084,-0.02995549701154232,-0.871848464012146,0.48885881900787354,-0.009990708902478218,-0.871848464012146,0.48967382311820984,0.009990708902478218,-0.871848464012146,0.48967382311820984,0.02995549701154232,-0.871848464012146,0.48885881900787354,0.04987042769789696,-0.871848464012146,0.4872301518917084,0.06970235705375671,-0.871848464012146,0.48479053378105164,0.08941826969385147,-0.871848464012146,0.48154401779174805,0.10898535698652267,-0.871848464012146,0.47749602794647217,0.12837104499340057,-0.871848464012146,0.4726532995700836,0.1475430727005005,-0.871848464012146,0.4670238792896271,0.1664695292711258,-0.871848464012146,0.46061715483665466,0.1851189136505127,-0.871848464012146,0.4534437656402588,0.2034602016210556,-0.871848464012146,0.4455156922340393,0.22146283090114594,-0.871848464012146,0.4368460774421692,0.23909686505794525,-0.871848464012146,0.42744937539100647,0.2563329339027405,-0.871848464012146,0.4173412322998047,0.27314236760139465,-0.871848464012146,0.4065384566783905,0.2894971966743469,-0.871848464012146,0.3950590491294861,0.30537018179893494,-0.871848464012146,0.38292208313941956,0.3207348883152008,-0.871848464012146,0.37014779448509216,0.33556580543518066,-0.871848464012146,0.35675743222236633,0.34983816742897034,-0.871848464012146,0.34277328848838806,0.3635282814502716,-0.871848464012146,0.32821863889694214,0.3766133189201355,-0.871848464012146,0.3131176829338074,0.389071524143219,-0.871848464012146,0.29749560356140137,0.40088215470314026,-0.871848464012146,0.2813783288002014,0.4120255708694458,-0.871848464012146,0.2647927701473236,0.4224832057952881,-0.871848464012146,0.24776645004749298,0.4322376549243927,-0.871848464012146,0.230327770113945,0.44127270579338074,-0.871848464012146,0.21250572800636292,0.449573278427124,-0.871848464012146,0.19432999193668365,0.45712557435035706,-0.871848464012146,0.17583081126213074,0.4639170467853546,-0.871848464012146,0.15703898668289185,0.4699363708496094,-0.871848464012146,0.13798576593399048,0.4751735329627991,-0.871848464012146,0.11870289593935013,0.4796198308467865,-0.871848464012146,0.09922245889902115,0.4832678437232971,-0.871848464012146,0.07957687228918076,0.48611149191856384,-0.871848464012146,0.05979883298277855,0.4881460666656494,-0.871848464012146,0.039921268820762634,0.48936814069747925,-0.871848464012146,0.019977260380983353,0.48977574706077576,-0.871848464012146,1.1996045881973905e-16,0.5500550270080566,-0.8351284265518188,0.0,0.5495972633361816,-0.8351284265518188,-0.02243596501648426,0.5482247471809387,-0.8351284265518188,-0.04483458772301674,0.5459397435188293,-0.8351284265518188,-0.06715858727693558,0.5427461266517639,-0.8351284265518188,-0.08937080949544907,0.5386491417884827,-0.8351284265518188,-0.11143428087234497,0.5336556434631348,-0.8351284265518188,-0.13331228494644165,0.527773916721344,-0.8351284265518188,-0.15496839582920074,0.5210137367248535,-0.8351284265518188,-0.17636658251285553,0.5133864283561707,-0.8351284265518188,-0.1974712312221527,0.5049046277999878,-0.8351284265518188,-0.2182472050189972,0.4955824315547943,-0.8351284265518188,-0.23865991830825806,0.4854353964328766,-0.8351284265518188,-0.2586754262447357,0.47448042035102844,-0.8351284265518188,-0.27826038002967834,0.46273571252822876,-0.8351284265518188,-0.2973821759223938,0.45022082328796387,-0.8351284265518188,-0.3160090446472168,0.43695658445358276,-0.8351284265518188,-0.33410993218421936,0.42296507954597473,-0.8351284265518188,-0.3516547381877899,0.40826958417892456,-0.8351284265518188,-0.36861422657966614,0.39289456605911255,-0.8351284265518188,-0.38496020436286926,0.3768656253814697,-0.8351284265518188,-0.4006654620170593,0.3602094352245331,-0.8351284265518188,-0.41570383310317993,0.3429536819458008,-0.8351284265518188,-0.4300503134727478,0.32512712478637695,-0.8351284265518188,-0.4436810314655304,0.30675944685935974,-0.8351284265518188,-0.4565732777118683,0.28788116574287415,-0.8351284265518188,-0.46870559453964233,0.2685237526893616,-0.8351284265518188,-0.48005780577659607,0.24871942400932312,-0.8351284265518188,-0.4906110167503357,0.2285011112689972,-0.8351284265518188,-0.5003476142883301,0.2079024761915207,-0.8351284265518188,-0.5092514753341675,0.18695780634880066,-0.8351284265518188,-0.517307698726654,0.1657019704580307,-0.8351284265518188,-0.5245029926300049,0.144170343875885,-0.8351284265518188,-0.5308251976966858,0.1223987564444542,-0.8351284265518188,-0.5362640023231506,0.10042344033718109,-0.8351284265518188,-0.5408101677894592,0.0782809853553772,-0.8351284265518188,-0.5444562435150146,0.056008242070674896,-0.8351284265518188,-0.547196090221405,0.033642277121543884,-0.8351284265518188,-0.5490252375602722,0.011220317333936691,-0.8351284265518188,-0.5499405264854431,-0.011220317333936691,-0.8351284265518188,-0.5499405264854431,-0.033642277121543884,-0.8351284265518188,-0.5490252375602722,-0.056008242070674896,-0.8351284265518188,-0.547196090221405,-0.0782809853553772,-0.8351284265518188,-0.5444562435150146,-0.10042344033718109,-0.8351284265518188,-0.5408101677894592,-0.1223987564444542,-0.8351284265518188,-0.5362640023231506,-0.144170343875885,-0.8351284265518188,-0.5308251976966858,-0.1657019704580307,-0.8351284265518188,-0.5245029926300049,-0.18695780634880066,-0.8351284265518188,-0.517307698726654,-0.2079024761915207,-0.8351284265518188,-0.5092514753341675,-0.2285011112689972,-0.8351284265518188,-0.5003476142883301,-0.24871942400932312,-0.8351284265518188,-0.4906110167503357,-0.2685237526893616,-0.8351284265518188,-0.48005780577659607,-0.28788116574287415,-0.8351284265518188,-0.46870559453964233,-0.30675944685935974,-0.8351284265518188,-0.4565732777118683,-0.32512712478637695,-0.8351284265518188,-0.4436810314655304,-0.3429536819458008,-0.8351284265518188,-0.4300503134727478,-0.3602094352245331,-0.8351284265518188,-0.41570383310317993,-0.3768656253814697,-0.8351284265518188,-0.4006654620170593,-0.39289456605911255,-0.8351284265518188,-0.38496020436286926,-0.40826958417892456,-0.8351284265518188,-0.36861422657966614,-0.42296507954597473,-0.8351284265518188,-0.3516547381877899,-0.43695658445358276,-0.8351284265518188,-0.33410993218421936,-0.45022082328796387,-0.8351284265518188,-0.3160090446472168,-0.46273571252822876,-0.8351284265518188,-0.2973821759223938,-0.47448042035102844,-0.8351284265518188,-0.27826038002967834,-0.4854353964328766,-0.8351284265518188,-0.2586754262447357,-0.4955824315547943,-0.8351284265518188,-0.23865991830825806,-0.5049046277999878,-0.8351284265518188,-0.2182472050189972,-0.5133864283561707,-0.8351284265518188,-0.1974712312221527,-0.5210137367248535,-0.8351284265518188,-0.17636658251285553,-0.527773916721344,-0.8351284265518188,-0.15496839582920074,-0.5336556434631348,-0.8351284265518188,-0.13331228494644165,-0.5386491417884827,-0.8351284265518188,-0.11143428087234497,-0.5427461266517639,-0.8351284265518188,-0.08937080949544907,-0.5459397435188293,-0.8351284265518188,-0.06715858727693558,-0.5482247471809387,-0.8351284265518188,-0.04483458772301674,-0.5495972633361816,-0.8351284265518188,-0.02243596501648426,-0.5500550270080566,-0.8351284265518188,-6.736230683364796e-17,-0.5495972633361816,-0.8351284265518188,0.02243596501648426,-0.5482247471809387,-0.8351284265518188,0.04483458772301674,-0.5459397435188293,-0.8351284265518188,0.06715858727693558,-0.5427461266517639,-0.8351284265518188,0.08937080949544907,-0.5386491417884827,-0.8351284265518188,0.11143428087234497,-0.5336556434631348,-0.8351284265518188,0.13331228494644165,-0.527773916721344,-0.8351284265518188,0.15496839582920074,-0.5210137367248535,-0.8351284265518188,0.17636658251285553,-0.5133864283561707,-0.8351284265518188,0.1974712312221527,-0.5049046277999878,-0.8351284265518188,0.2182472050189972,-0.4955824315547943,-0.8351284265518188,0.23865991830825806,-0.4854353964328766,-0.8351284265518188,0.2586754262447357,-0.47448042035102844,-0.8351284265518188,0.27826038002967834,-0.46273571252822876,-0.8351284265518188,0.2973821759223938,-0.45022082328796387,-0.8351284265518188,0.3160090446472168,-0.43695658445358276,-0.8351284265518188,0.33410993218421936,-0.42296507954597473,-0.8351284265518188,0.3516547381877899,-0.40826958417892456,-0.8351284265518188,0.36861422657966614,-0.39289456605911255,-0.8351284265518188,0.38496020436286926,-0.3768656253814697,-0.8351284265518188,0.4006654620170593,-0.3602094352245331,-0.8351284265518188,0.41570383310317993,-0.3429536819458008,-0.8351284265518188,0.4300503134727478,-0.32512712478637695,-0.8351284265518188,0.4436810314655304,-0.30675944685935974,-0.8351284265518188,0.4565732777118683,-0.28788116574287415,-0.8351284265518188,0.46870559453964233,-0.2685237526893616,-0.8351284265518188,0.48005780577659607,-0.24871942400932312,-0.8351284265518188,0.4906110167503357,-0.2285011112689972,-0.8351284265518188,0.5003476142883301,-0.2079024761915207,-0.8351284265518188,0.5092514753341675,-0.18695780634880066,-0.8351284265518188,0.517307698726654,-0.1657019704580307,-0.8351284265518188,0.5245029926300049,-0.144170343875885,-0.8351284265518188,0.5308251976966858,-0.1223987564444542,-0.8351284265518188,0.5362640023231506,-0.10042344033718109,-0.8351284265518188,0.5408101677894592,-0.0782809853553772,-0.8351284265518188,0.5444562435150146,-0.056008242070674896,-0.8351284265518188,0.547196090221405,-0.033642277121543884,-0.8351284265518188,0.5490252375602722,-0.011220317333936691,-0.8351284265518188,0.5499405264854431,0.011220317333936691,-0.8351284265518188,0.5499405264854431,0.033642277121543884,-0.8351284265518188,0.5490252375602722,0.056008242070674896,-0.8351284265518188,0.547196090221405,0.0782809853553772,-0.8351284265518188,0.5444562435150146,0.10042344033718109,-0.8351284265518188,0.5408101677894592,0.1223987564444542,-0.8351284265518188,0.5362640023231506,0.144170343875885,-0.8351284265518188,0.5308251976966858,0.1657019704580307,-0.8351284265518188,0.5245029926300049,0.18695780634880066,-0.8351284265518188,0.517307698726654,0.2079024761915207,-0.8351284265518188,0.5092514753341675,0.2285011112689972,-0.8351284265518188,0.5003476142883301,0.24871942400932312,-0.8351284265518188,0.4906110167503357,0.2685237526893616,-0.8351284265518188,0.48005780577659607,0.28788116574287415,-0.8351284265518188,0.46870559453964233,0.30675944685935974,-0.8351284265518188,0.4565732777118683,0.32512712478637695,-0.8351284265518188,0.4436810314655304,0.3429536819458008,-0.8351284265518188,0.4300503134727478,0.3602094352245331,-0.8351284265518188,0.41570383310317993,0.3768656253814697,-0.8351284265518188,0.4006654620170593,0.39289456605911255,-0.8351284265518188,0.38496020436286926,0.40826958417892456,-0.8351284265518188,0.36861422657966614,0.42296507954597473,-0.8351284265518188,0.3516547381877899,0.43695658445358276,-0.8351284265518188,0.33410993218421936,0.45022082328796387,-0.8351284265518188,0.3160090446472168,0.46273571252822876,-0.8351284265518188,0.2973821759223938,0.47448042035102844,-0.8351284265518188,0.27826038002967834,0.4854353964328766,-0.8351284265518188,0.2586754262447357,0.4955824315547943,-0.8351284265518188,0.23865991830825806,0.5049046277999878,-0.8351284265518188,0.2182472050189972,0.5133864283561707,-0.8351284265518188,0.1974712312221527,0.5210137367248535,-0.8351284265518188,0.17636658251285553,0.527773916721344,-0.8351284265518188,0.15496839582920074,0.5336556434631348,-0.8351284265518188,0.13331228494644165,0.5386491417884827,-0.8351284265518188,0.11143428087234497,0.5427461266517639,-0.8351284265518188,0.08937080949544907,0.5459397435188293,-0.8351284265518188,0.06715858727693558,0.5482247471809387,-0.8351284265518188,0.04483458772301674,0.5495972633361816,-0.8351284265518188,0.02243596501648426,0.5500550270080566,-0.8351284265518188,1.3472461366729593e-16,0.6075938940048218,-0.7942478656768799,0.0,0.6070882678031921,-0.7942478656768799,-0.024782896041870117,0.6055722236633301,-0.7942478656768799,-0.049524545669555664,0.6030482053756714,-0.7942478656768799,-0.07418376207351685,0.5995205044746399,-0.7942478656768799,-0.0987195149064064,0.5949949622154236,-0.7942478656768799,-0.1230909526348114,0.5894790887832642,-0.7942478656768799,-0.14725752174854279,0.5829821228981018,-0.7942478656768799,-0.17117898166179657,0.5755147933959961,-0.7942478656768799,-0.19481554627418518,0.5670896172523499,-0.7942478656768799,-0.21812786161899567,0.5577205419540405,-0.7942478656768799,-0.24107711017131805,0.5474231839179993,-0.7942478656768799,-0.2636251151561737,0.5362147688865662,-0.7942478656768799,-0.2857343554496765,0.5241138339042664,-0.7942478656768799,-0.3073680102825165,0.5111405253410339,-0.7942478656768799,-0.32849007844924927,0.49731650948524475,-0.7942478656768799,-0.3490653932094574,0.4826647639274597,-0.7942478656768799,-0.3690597414970398,0.46720966696739197,-0.7942478656768799,-0.3884398341178894,0.4509769380092621,-0.7942478656768799,-0.40717339515686035,0.4339936077594757,-0.7942478656768799,-0.4252292513847351,0.41628792881965637,-0.7942478656768799,-0.4425773620605469,0.3978894054889679,-0.7942478656768799,-0.459188848733902,0.37882861495018005,-0.7942478656768799,-0.47503605484962463,0.35913729667663574,-0.7942478656768799,-0.49009260535240173,0.3388482332229614,-0.7942478656768799,-0.5043334364891052,0.31799519062042236,-0.7942478656768799,-0.5177348852157593,0.2966128885746002,-0.7942478656768799,-0.5302746295928955,0.2747369110584259,-0.7942478656768799,-0.5419317483901978,0.2524036467075348,-0.7942478656768799,-0.552686870098114,0.22965027391910553,-0.7942478656768799,-0.5625221133232117,0.20651467144489288,-0.7942478656768799,-0.5714210867881775,0.18303535878658295,-0.7942478656768799,-0.5793690085411072,0.1592513918876648,-0.7942478656768799,-0.5863525867462158,0.13520236313343048,-0.7942478656768799,-0.592360258102417,0.11092831194400787,-0.7942478656768799,-0.5973820090293884,0.0864696279168129,-0.7942478656768799,-0.6014094948768616,0.06186702847480774,-0.7942478656768799,-0.6044359803199768,0.0371614545583725,-0.7942478656768799,-0.6064563989639282,0.012394026853144169,-0.7942478656768799,-0.6074674725532532,-0.012394026853144169,-0.7942478656768799,-0.6074674725532532,-0.0371614545583725,-0.7942478656768799,-0.6064563989639282,-0.06186702847480774,-0.7942478656768799,-0.6044359803199768,-0.0864696279168129,-0.7942478656768799,-0.6014094948768616,-0.11092831194400787,-0.7942478656768799,-0.5973820090293884,-0.13520236313343048,-0.7942478656768799,-0.592360258102417,-0.1592513918876648,-0.7942478656768799,-0.5863525867462158,-0.18303535878658295,-0.7942478656768799,-0.5793690085411072,-0.20651467144489288,-0.7942478656768799,-0.5714210867881775,-0.22965027391910553,-0.7942478656768799,-0.5625221133232117,-0.2524036467075348,-0.7942478656768799,-0.552686870098114,-0.2747369110584259,-0.7942478656768799,-0.5419317483901978,-0.2966128885746002,-0.7942478656768799,-0.5302746295928955,-0.31799519062042236,-0.7942478656768799,-0.5177348852157593,-0.3388482332229614,-0.7942478656768799,-0.5043334364891052,-0.35913729667663574,-0.7942478656768799,-0.49009260535240173,-0.37882861495018005,-0.7942478656768799,-0.47503605484962463,-0.3978894054889679,-0.7942478656768799,-0.459188848733902,-0.41628792881965637,-0.7942478656768799,-0.4425773620605469,-0.4339936077594757,-0.7942478656768799,-0.4252292513847351,-0.4509769380092621,-0.7942478656768799,-0.40717339515686035,-0.46720966696739197,-0.7942478656768799,-0.3884398341178894,-0.4826647639274597,-0.7942478656768799,-0.3690597414970398,-0.49731650948524475,-0.7942478656768799,-0.3490653932094574,-0.5111405253410339,-0.7942478656768799,-0.32849007844924927,-0.5241138339042664,-0.7942478656768799,-0.3073680102825165,-0.5362147688865662,-0.7942478656768799,-0.2857343554496765,-0.5474231839179993,-0.7942478656768799,-0.2636251151561737,-0.5577205419540405,-0.7942478656768799,-0.24107711017131805,-0.5670896172523499,-0.7942478656768799,-0.21812786161899567,-0.5755147933959961,-0.7942478656768799,-0.19481554627418518,-0.5829821228981018,-0.7942478656768799,-0.17117898166179657,-0.5894790887832642,-0.7942478656768799,-0.14725752174854279,-0.5949949622154236,-0.7942478656768799,-0.1230909526348114,-0.5995205044746399,-0.7942478656768799,-0.0987195149064064,-0.6030482053756714,-0.7942478656768799,-0.07418376207351685,-0.6055722236633301,-0.7942478656768799,-0.049524545669555664,-0.6070882678031921,-0.7942478656768799,-0.024782896041870117,-0.6075938940048218,-0.7942478656768799,-7.440879377419119e-17,-0.6070882678031921,-0.7942478656768799,0.024782896041870117,-0.6055722236633301,-0.7942478656768799,0.049524545669555664,-0.6030482053756714,-0.7942478656768799,0.07418376207351685,-0.5995205044746399,-0.7942478656768799,0.0987195149064064,-0.5949949622154236,-0.7942478656768799,0.1230909526348114,-0.5894790887832642,-0.7942478656768799,0.14725752174854279,-0.5829821228981018,-0.7942478656768799,0.17117898166179657,-0.5755147933959961,-0.7942478656768799,0.19481554627418518,-0.5670896172523499,-0.7942478656768799,0.21812786161899567,-0.5577205419540405,-0.7942478656768799,0.24107711017131805,-0.5474231839179993,-0.7942478656768799,0.2636251151561737,-0.5362147688865662,-0.7942478656768799,0.2857343554496765,-0.5241138339042664,-0.7942478656768799,0.3073680102825165,-0.5111405253410339,-0.7942478656768799,0.32849007844924927,-0.49731650948524475,-0.7942478656768799,0.3490653932094574,-0.4826647639274597,-0.7942478656768799,0.3690597414970398,-0.46720966696739197,-0.7942478656768799,0.3884398341178894,-0.4509769380092621,-0.7942478656768799,0.40717339515686035,-0.4339936077594757,-0.7942478656768799,0.4252292513847351,-0.41628792881965637,-0.7942478656768799,0.4425773620605469,-0.3978894054889679,-0.7942478656768799,0.459188848733902,-0.37882861495018005,-0.7942478656768799,0.47503605484962463,-0.35913729667663574,-0.7942478656768799,0.49009260535240173,-0.3388482332229614,-0.7942478656768799,0.5043334364891052,-0.31799519062042236,-0.7942478656768799,0.5177348852157593,-0.2966128885746002,-0.7942478656768799,0.5302746295928955,-0.2747369110584259,-0.7942478656768799,0.5419317483901978,-0.2524036467075348,-0.7942478656768799,0.552686870098114,-0.22965027391910553,-0.7942478656768799,0.5625221133232117,-0.20651467144489288,-0.7942478656768799,0.5714210867881775,-0.18303535878658295,-0.7942478656768799,0.5793690085411072,-0.1592513918876648,-0.7942478656768799,0.5863525867462158,-0.13520236313343048,-0.7942478656768799,0.592360258102417,-0.11092831194400787,-0.7942478656768799,0.5973820090293884,-0.0864696279168129,-0.7942478656768799,0.6014094948768616,-0.06186702847480774,-0.7942478656768799,0.6044359803199768,-0.0371614545583725,-0.7942478656768799,0.6064563989639282,-0.012394026853144169,-0.7942478656768799,0.6074674725532532,0.012394026853144169,-0.7942478656768799,0.6074674725532532,0.0371614545583725,-0.7942478656768799,0.6064563989639282,0.06186702847480774,-0.7942478656768799,0.6044359803199768,0.0864696279168129,-0.7942478656768799,0.6014094948768616,0.11092831194400787,-0.7942478656768799,0.5973820090293884,0.13520236313343048,-0.7942478656768799,0.592360258102417,0.1592513918876648,-0.7942478656768799,0.5863525867462158,0.18303535878658295,-0.7942478656768799,0.5793690085411072,0.20651467144489288,-0.7942478656768799,0.5714210867881775,0.22965027391910553,-0.7942478656768799,0.5625221133232117,0.2524036467075348,-0.7942478656768799,0.552686870098114,0.2747369110584259,-0.7942478656768799,0.5419317483901978,0.2966128885746002,-0.7942478656768799,0.5302746295928955,0.31799519062042236,-0.7942478656768799,0.5177348852157593,0.3388482332229614,-0.7942478656768799,0.5043334364891052,0.35913729667663574,-0.7942478656768799,0.49009260535240173,0.37882861495018005,-0.7942478656768799,0.47503605484962463,0.3978894054889679,-0.7942478656768799,0.459188848733902,0.41628792881965637,-0.7942478656768799,0.4425773620605469,0.4339936077594757,-0.7942478656768799,0.4252292513847351,0.4509769380092621,-0.7942478656768799,0.40717339515686035,0.46720966696739197,-0.7942478656768799,0.3884398341178894,0.4826647639274597,-0.7942478656768799,0.3690597414970398,0.49731650948524475,-0.7942478656768799,0.3490653932094574,0.5111405253410339,-0.7942478656768799,0.32849007844924927,0.5241138339042664,-0.7942478656768799,0.3073680102825165,0.5362147688865662,-0.7942478656768799,0.2857343554496765,0.5474231839179993,-0.7942478656768799,0.2636251151561737,0.5577205419540405,-0.7942478656768799,0.24107711017131805,0.5670896172523499,-0.7942478656768799,0.21812786161899567,0.5755147933959961,-0.7942478656768799,0.19481554627418518,0.5829821228981018,-0.7942478656768799,0.17117898166179657,0.5894790887832642,-0.7942478656768799,0.14725752174854279,0.5949949622154236,-0.7942478656768799,0.1230909526348114,0.5995205044746399,-0.7942478656768799,0.0987195149064064,0.6030482053756714,-0.7942478656768799,0.07418376207351685,0.6055722236633301,-0.7942478656768799,0.049524545669555664,0.6070882678031921,-0.7942478656768799,0.024782896041870117,0.6075938940048218,-0.7942478656768799,1.4881758754838238e-16,0.6621058583259583,-0.7494103312492371,0.0,0.6615548133850098,-0.7494103312492371,-0.0270063616335392,0.6599027514457703,-0.7494103312492371,-0.05396777391433716,0.6571522951126099,-0.7494103312492371,-0.0808393582701683,0.653308093547821,-0.7494103312492371,-0.1075764000415802,0.6483765244483948,-0.7494103312492371,-0.13413438200950623,0.6423657536506653,-0.7494103312492371,-0.16046911478042603,0.6352859139442444,-0.7494103312492371,-0.1865367740392685,0.6271486282348633,-0.7494103312492371,-0.21229395270347595,0.6179675459861755,-0.7494103312492371,-0.2376977801322937,0.6077579259872437,-0.7494103312492371,-0.2627059817314148,0.5965367555618286,-0.7494103312492371,-0.287276953458786,0.5843226909637451,-0.7494103312492371,-0.31136977672576904,0.5711360573768616,-0.7494103312492371,-0.33494433760643005,0.5569988489151001,-0.7494103312492371,-0.3579614460468292,0.541934609413147,-0.7494103312492371,-0.3803827464580536,0.5259683132171631,-0.7494103312492371,-0.40217092633247375,0.509126603603363,-0.7494103312492371,-0.4232897460460663,0.49143755435943604,-0.7494103312492371,-0.4437040388584137,0.47293052077293396,-0.7494103312492371,-0.463379830121994,0.4536363184452057,-0.7494103312492371,-0.4822843670845032,0.43358710408210754,-0.7494103312492371,-0.5003861784934998,0.41281622648239136,-0.7494103312492371,-0.5176551938056946,0.39135825634002686,-0.7494103312492371,-0.5340625643730164,0.3692489266395569,-0.7494103312492371,-0.5495811104774475,0.3465249836444855,-0.7494103312492371,-0.564184844493866,0.3232243061065674,-0.7494103312492371,-0.5778496265411377,0.299385666847229,-0.7494103312492371,-0.590552568435669,0.27504870295524597,-0.7494103312492371,-0.602272629737854,0.25025394558906555,-0.7494103312492371,-0.6129902601242065,0.2250426858663559,-0.7494103312492371,-0.6226876378059387,0.1994568556547165,-0.7494103312492371,-0.6313486099243164,0.17353905737400055,-0.7494103312492371,-0.6389588117599487,0.14733241498470306,-0.7494103312492371,-0.6455054879188538,0.12088054418563843,-0.7494103312492371,-0.6509777307510376,0.09422748535871506,-0.7494103312492371,-0.6553665399551392,0.06741759181022644,-0.7494103312492371,-0.6586645841598511,0.04049549251794815,-0.7494103312492371,-0.6608662605285645,0.013505990616977215,-0.7494103312492371,-0.6619680523872375,-0.013505990616977215,-0.7494103312492371,-0.6619680523872375,-0.04049549251794815,-0.7494103312492371,-0.6608662605285645,-0.06741759181022644,-0.7494103312492371,-0.6586645841598511,-0.09422748535871506,-0.7494103312492371,-0.6553665399551392,-0.12088054418563843,-0.7494103312492371,-0.6509777307510376,-0.14733241498470306,-0.7494103312492371,-0.6455054879188538,-0.17353905737400055,-0.7494103312492371,-0.6389588117599487,-0.1994568556547165,-0.7494103312492371,-0.6313486099243164,-0.2250426858663559,-0.7494103312492371,-0.6226876378059387,-0.25025394558906555,-0.7494103312492371,-0.6129902601242065,-0.27504870295524597,-0.7494103312492371,-0.602272629737854,-0.299385666847229,-0.7494103312492371,-0.590552568435669,-0.3232243061065674,-0.7494103312492371,-0.5778496265411377,-0.3465249836444855,-0.7494103312492371,-0.564184844493866,-0.3692489266395569,-0.7494103312492371,-0.5495811104774475,-0.39135825634002686,-0.7494103312492371,-0.5340625643730164,-0.41281622648239136,-0.7494103312492371,-0.5176551938056946,-0.43358710408210754,-0.7494103312492371,-0.5003861784934998,-0.4536363184452057,-0.7494103312492371,-0.4822843670845032,-0.47293052077293396,-0.7494103312492371,-0.463379830121994,-0.49143755435943604,-0.7494103312492371,-0.4437040388584137,-0.509126603603363,-0.7494103312492371,-0.4232897460460663,-0.5259683132171631,-0.7494103312492371,-0.40217092633247375,-0.541934609413147,-0.7494103312492371,-0.3803827464580536,-0.5569988489151001,-0.7494103312492371,-0.3579614460468292,-0.5711360573768616,-0.7494103312492371,-0.33494433760643005,-0.5843226909637451,-0.7494103312492371,-0.31136977672576904,-0.5965367555618286,-0.7494103312492371,-0.287276953458786,-0.6077579259872437,-0.7494103312492371,-0.2627059817314148,-0.6179675459861755,-0.7494103312492371,-0.2376977801322937,-0.6271486282348633,-0.7494103312492371,-0.21229395270347595,-0.6352859139442444,-0.7494103312492371,-0.1865367740392685,-0.6423657536506653,-0.7494103312492371,-0.16046911478042603,-0.6483765244483948,-0.7494103312492371,-0.13413438200950623,-0.653308093547821,-0.7494103312492371,-0.1075764000415802,-0.6571522951126099,-0.7494103312492371,-0.0808393582701683,-0.6599027514457703,-0.7494103312492371,-0.05396777391433716,-0.6615548133850098,-0.7494103312492371,-0.0270063616335392,-0.6621058583259583,-0.7494103312492371,-8.108457806885755e-17,-0.6615548133850098,-0.7494103312492371,0.0270063616335392,-0.6599027514457703,-0.7494103312492371,0.05396777391433716,-0.6571522951126099,-0.7494103312492371,0.0808393582701683,-0.653308093547821,-0.7494103312492371,0.1075764000415802,-0.6483765244483948,-0.7494103312492371,0.13413438200950623,-0.6423657536506653,-0.7494103312492371,0.16046911478042603,-0.6352859139442444,-0.7494103312492371,0.1865367740392685,-0.6271486282348633,-0.7494103312492371,0.21229395270347595,-0.6179675459861755,-0.7494103312492371,0.2376977801322937,-0.6077579259872437,-0.7494103312492371,0.2627059817314148,-0.5965367555618286,-0.7494103312492371,0.287276953458786,-0.5843226909637451,-0.7494103312492371,0.31136977672576904,-0.5711360573768616,-0.7494103312492371,0.33494433760643005,-0.5569988489151001,-0.7494103312492371,0.3579614460468292,-0.541934609413147,-0.7494103312492371,0.3803827464580536,-0.5259683132171631,-0.7494103312492371,0.40217092633247375,-0.509126603603363,-0.7494103312492371,0.4232897460460663,-0.49143755435943604,-0.7494103312492371,0.4437040388584137,-0.47293052077293396,-0.7494103312492371,0.463379830121994,-0.4536363184452057,-0.7494103312492371,0.4822843670845032,-0.43358710408210754,-0.7494103312492371,0.5003861784934998,-0.41281622648239136,-0.7494103312492371,0.5176551938056946,-0.39135825634002686,-0.7494103312492371,0.5340625643730164,-0.3692489266395569,-0.7494103312492371,0.5495811104774475,-0.3465249836444855,-0.7494103312492371,0.564184844493866,-0.3232243061065674,-0.7494103312492371,0.5778496265411377,-0.299385666847229,-0.7494103312492371,0.590552568435669,-0.27504870295524597,-0.7494103312492371,0.602272629737854,-0.25025394558906555,-0.7494103312492371,0.6129902601242065,-0.2250426858663559,-0.7494103312492371,0.6226876378059387,-0.1994568556547165,-0.7494103312492371,0.6313486099243164,-0.17353905737400055,-0.7494103312492371,0.6389588117599487,-0.14733241498470306,-0.7494103312492371,0.6455054879188538,-0.12088054418563843,-0.7494103312492371,0.6509777307510376,-0.09422748535871506,-0.7494103312492371,0.6553665399551392,-0.06741759181022644,-0.7494103312492371,0.6586645841598511,-0.04049549251794815,-0.7494103312492371,0.6608662605285645,-0.013505990616977215,-0.7494103312492371,0.6619680523872375,0.013505990616977215,-0.7494103312492371,0.6619680523872375,0.04049549251794815,-0.7494103312492371,0.6608662605285645,0.06741759181022644,-0.7494103312492371,0.6586645841598511,0.09422748535871506,-0.7494103312492371,0.6553665399551392,0.12088054418563843,-0.7494103312492371,0.6509777307510376,0.14733241498470306,-0.7494103312492371,0.6455054879188538,0.17353905737400055,-0.7494103312492371,0.6389588117599487,0.1994568556547165,-0.7494103312492371,0.6313486099243164,0.2250426858663559,-0.7494103312492371,0.6226876378059387,0.25025394558906555,-0.7494103312492371,0.6129902601242065,0.27504870295524597,-0.7494103312492371,0.602272629737854,0.299385666847229,-0.7494103312492371,0.590552568435669,0.3232243061065674,-0.7494103312492371,0.5778496265411377,0.3465249836444855,-0.7494103312492371,0.564184844493866,0.3692489266395569,-0.7494103312492371,0.5495811104774475,0.39135825634002686,-0.7494103312492371,0.5340625643730164,0.41281622648239136,-0.7494103312492371,0.5176551938056946,0.43358710408210754,-0.7494103312492371,0.5003861784934998,0.4536363184452057,-0.7494103312492371,0.4822843670845032,0.47293052077293396,-0.7494103312492371,0.463379830121994,0.49143755435943604,-0.7494103312492371,0.4437040388584137,0.509126603603363,-0.7494103312492371,0.4232897460460663,0.5259683132171631,-0.7494103312492371,0.40217092633247375,0.541934609413147,-0.7494103312492371,0.3803827464580536,0.5569988489151001,-0.7494103312492371,0.3579614460468292,0.5711360573768616,-0.7494103312492371,0.33494433760643005,0.5843226909637451,-0.7494103312492371,0.31136977672576904,0.5965367555618286,-0.7494103312492371,0.287276953458786,0.6077579259872437,-0.7494103312492371,0.2627059817314148,0.6179675459861755,-0.7494103312492371,0.2376977801322937,0.6271486282348633,-0.7494103312492371,0.21229395270347595,0.6352859139442444,-0.7494103312492371,0.1865367740392685,0.6423657536506653,-0.7494103312492371,0.16046911478042603,0.6483765244483948,-0.7494103312492371,0.13413438200950623,0.653308093547821,-0.7494103312492371,0.1075764000415802,0.6571522951126099,-0.7494103312492371,0.0808393582701683,0.6599027514457703,-0.7494103312492371,0.05396777391433716,0.6615548133850098,-0.7494103312492371,0.0270063616335392,0.6621058583259583,-0.7494103312492371,1.621691561377151e-16,0.7133191823959351,-0.7008393406867981,0.0,0.7127255201339722,-0.7008393406867981,-0.029095280915498734,0.7109456658363342,-0.7008393406867981,-0.05814213305711746,0.7079824805259705,-0.7008393406867981,-0.08709221333265305,0.7038409113883972,-0.7008393406867981,-0.11589734256267548,0.6985278725624084,-0.7008393406867981,-0.14450956881046295,0.6920522451400757,-0.7008393406867981,-0.17288127541542053,0.6844247579574585,-0.7008393406867981,-0.20096524059772491,0.6756580471992493,-0.7008393406867981,-0.2287147045135498,0.6657668352127075,-0.7008393406867981,-0.25608351826667786,0.6547675132751465,-0.7008393406867981,-0.2830260694026947,0.6426783800125122,-0.7008393406867981,-0.309497594833374,0.6295195817947388,-0.7008393406867981,-0.33545395731925964,0.6153129935264587,-0.7008393406867981,-0.3608520030975342,0.600082278251648,-0.7008393406867981,-0.3856494724750519,0.5838527679443359,-0.7008393406867981,-0.409805029630661,0.5666515231132507,-0.7008393406867981,-0.43327853083610535,0.5485071539878845,-0.7008393406867981,-0.45603084564208984,0.5294498205184937,-0.7008393406867981,-0.4780241847038269,0.5095112919807434,-0.7008393406867981,-0.49922189116477966,0.4887247085571289,-0.7008393406867981,-0.5195886492729187,0.46712473034858704,-0.7008393406867981,-0.539090633392334,0.4447472393512726,-0.7008393406867981,-0.5576953887939453,0.42162951827049255,-0.7008393406867981,-0.5753718614578247,0.3978100121021271,-0.7008393406867981,-0.5920907258987427,0.37332841753959656,-0.7008393406867981,-0.6078240871429443,0.3482254445552826,-0.7008393406867981,-0.6225457787513733,0.32254287600517273,-0.7008393406867981,-0.6362313628196716,0.2963234782218933,-0.7008393406867981,-0.6488579511642456,0.2696108818054199,-0.7008393406867981,-0.6604045629501343,0.242449551820755,-0.7008393406867981,-0.6708520650863647,0.2148846834897995,-0.7008393406867981,-0.680182933807373,0.18696215748786926,-0.7008393406867981,-0.6883817315101624,0.1587284505367279,-0.7008393406867981,-0.6954347491264343,0.13023054599761963,-0.7008393406867981,-0.7013303637504578,0.10151590406894684,-0.7008393406867981,-0.7060586214065552,0.07263229042291641,-0.7008393406867981,-0.709611713886261,0.0436277836561203,-0.7008393406867981,-0.7119837403297424,0.014550668187439442,-0.7008393406867981,-0.7131707668304443,-0.014550668187439442,-0.7008393406867981,-0.7131707668304443,-0.0436277836561203,-0.7008393406867981,-0.7119837403297424,-0.07263229042291641,-0.7008393406867981,-0.709611713886261,-0.10151590406894684,-0.7008393406867981,-0.7060586214065552,-0.13023054599761963,-0.7008393406867981,-0.7013303637504578,-0.1587284505367279,-0.7008393406867981,-0.6954347491264343,-0.18696215748786926,-0.7008393406867981,-0.6883817315101624,-0.2148846834897995,-0.7008393406867981,-0.680182933807373,-0.242449551820755,-0.7008393406867981,-0.6708520650863647,-0.2696108818054199,-0.7008393406867981,-0.6604045629501343,-0.2963234782218933,-0.7008393406867981,-0.6488579511642456,-0.32254287600517273,-0.7008393406867981,-0.6362313628196716,-0.3482254445552826,-0.7008393406867981,-0.6225457787513733,-0.37332841753959656,-0.7008393406867981,-0.6078240871429443,-0.3978100121021271,-0.7008393406867981,-0.5920907258987427,-0.42162951827049255,-0.7008393406867981,-0.5753718614578247,-0.4447472393512726,-0.7008393406867981,-0.5576953887939453,-0.46712473034858704,-0.7008393406867981,-0.539090633392334,-0.4887247085571289,-0.7008393406867981,-0.5195886492729187,-0.5095112919807434,-0.7008393406867981,-0.49922189116477966,-0.5294498205184937,-0.7008393406867981,-0.4780241847038269,-0.5485071539878845,-0.7008393406867981,-0.45603084564208984,-0.5666515231132507,-0.7008393406867981,-0.43327853083610535,-0.5838527679443359,-0.7008393406867981,-0.409805029630661,-0.600082278251648,-0.7008393406867981,-0.3856494724750519,-0.6153129935264587,-0.7008393406867981,-0.3608520030975342,-0.6295195817947388,-0.7008393406867981,-0.33545395731925964,-0.6426783800125122,-0.7008393406867981,-0.309497594833374,-0.6547675132751465,-0.7008393406867981,-0.2830260694026947,-0.6657668352127075,-0.7008393406867981,-0.25608351826667786,-0.6756580471992493,-0.7008393406867981,-0.2287147045135498,-0.6844247579574585,-0.7008393406867981,-0.20096524059772491,-0.6920522451400757,-0.7008393406867981,-0.17288127541542053,-0.6985278725624084,-0.7008393406867981,-0.14450956881046295,-0.7038409113883972,-0.7008393406867981,-0.11589734256267548,-0.7079824805259705,-0.7008393406867981,-0.08709221333265305,-0.7109456658363342,-0.7008393406867981,-0.05814213305711746,-0.7127255201339722,-0.7008393406867981,-0.029095280915498734,-0.7133191823959351,-0.7008393406867981,-8.735640705702242e-17,-0.7127255201339722,-0.7008393406867981,0.029095280915498734,-0.7109456658363342,-0.7008393406867981,0.05814213305711746,-0.7079824805259705,-0.7008393406867981,0.08709221333265305,-0.7038409113883972,-0.7008393406867981,0.11589734256267548,-0.6985278725624084,-0.7008393406867981,0.14450956881046295,-0.6920522451400757,-0.7008393406867981,0.17288127541542053,-0.6844247579574585,-0.7008393406867981,0.20096524059772491,-0.6756580471992493,-0.7008393406867981,0.2287147045135498,-0.6657668352127075,-0.7008393406867981,0.25608351826667786,-0.6547675132751465,-0.7008393406867981,0.2830260694026947,-0.6426783800125122,-0.7008393406867981,0.309497594833374,-0.6295195817947388,-0.7008393406867981,0.33545395731925964,-0.6153129935264587,-0.7008393406867981,0.3608520030975342,-0.600082278251648,-0.7008393406867981,0.3856494724750519,-0.5838527679443359,-0.7008393406867981,0.409805029630661,-0.5666515231132507,-0.7008393406867981,0.43327853083610535,-0.5485071539878845,-0.7008393406867981,0.45603084564208984,-0.5294498205184937,-0.7008393406867981,0.4780241847038269,-0.5095112919807434,-0.7008393406867981,0.49922189116477966,-0.4887247085571289,-0.7008393406867981,0.5195886492729187,-0.46712473034858704,-0.7008393406867981,0.539090633392334,-0.4447472393512726,-0.7008393406867981,0.5576953887939453,-0.42162951827049255,-0.7008393406867981,0.5753718614578247,-0.3978100121021271,-0.7008393406867981,0.5920907258987427,-0.37332841753959656,-0.7008393406867981,0.6078240871429443,-0.3482254445552826,-0.7008393406867981,0.6225457787513733,-0.32254287600517273,-0.7008393406867981,0.6362313628196716,-0.2963234782218933,-0.7008393406867981,0.6488579511642456,-0.2696108818054199,-0.7008393406867981,0.6604045629501343,-0.242449551820755,-0.7008393406867981,0.6708520650863647,-0.2148846834897995,-0.7008393406867981,0.680182933807373,-0.18696215748786926,-0.7008393406867981,0.6883817315101624,-0.1587284505367279,-0.7008393406867981,0.6954347491264343,-0.13023054599761963,-0.7008393406867981,0.7013303637504578,-0.10151590406894684,-0.7008393406867981,0.7060586214065552,-0.07263229042291641,-0.7008393406867981,0.709611713886261,-0.0436277836561203,-0.7008393406867981,0.7119837403297424,-0.014550668187439442,-0.7008393406867981,0.7131707668304443,0.014550668187439442,-0.7008393406867981,0.7131707668304443,0.0436277836561203,-0.7008393406867981,0.7119837403297424,0.07263229042291641,-0.7008393406867981,0.709611713886261,0.10151590406894684,-0.7008393406867981,0.7060586214065552,0.13023054599761963,-0.7008393406867981,0.7013303637504578,0.1587284505367279,-0.7008393406867981,0.6954347491264343,0.18696215748786926,-0.7008393406867981,0.6883817315101624,0.2148846834897995,-0.7008393406867981,0.680182933807373,0.242449551820755,-0.7008393406867981,0.6708520650863647,0.2696108818054199,-0.7008393406867981,0.6604045629501343,0.2963234782218933,-0.7008393406867981,0.6488579511642456,0.32254287600517273,-0.7008393406867981,0.6362313628196716,0.3482254445552826,-0.7008393406867981,0.6225457787513733,0.37332841753959656,-0.7008393406867981,0.6078240871429443,0.3978100121021271,-0.7008393406867981,0.5920907258987427,0.42162951827049255,-0.7008393406867981,0.5753718614578247,0.4447472393512726,-0.7008393406867981,0.5576953887939453,0.46712473034858704,-0.7008393406867981,0.539090633392334,0.4887247085571289,-0.7008393406867981,0.5195886492729187,0.5095112919807434,-0.7008393406867981,0.49922189116477966,0.5294498205184937,-0.7008393406867981,0.4780241847038269,0.5485071539878845,-0.7008393406867981,0.45603084564208984,0.5666515231132507,-0.7008393406867981,0.43327853083610535,0.5838527679443359,-0.7008393406867981,0.409805029630661,0.600082278251648,-0.7008393406867981,0.3856494724750519,0.6153129935264587,-0.7008393406867981,0.3608520030975342,0.6295195817947388,-0.7008393406867981,0.33545395731925964,0.6426783800125122,-0.7008393406867981,0.309497594833374,0.6547675132751465,-0.7008393406867981,0.2830260694026947,0.6657668352127075,-0.7008393406867981,0.25608351826667786,0.6756580471992493,-0.7008393406867981,0.2287147045135498,0.6844247579574585,-0.7008393406867981,0.20096524059772491,0.6920522451400757,-0.7008393406867981,0.17288127541542053,0.6985278725624084,-0.7008393406867981,0.14450956881046295,0.7038409113883972,-0.7008393406867981,0.11589734256267548,0.7079824805259705,-0.7008393406867981,0.08709221333265305,0.7109456658363342,-0.7008393406867981,0.05814213305711746,0.7127255201339722,-0.7008393406867981,0.029095280915498734,0.7133191823959351,-0.7008393406867981,1.7471281411404483e-16,0.7609787583351135,-0.6487767696380615,0.0,0.7603455185890198,-0.6487767696380615,-0.031039249151945114,0.7584466934204102,-0.6487767696380615,-0.06202683597803116,0.7552855014801025,-0.6487767696380615,-0.09291118383407593,0.7508672475814819,-0.6487767696380615,-0.12364089488983154,0.7451992630958557,-0.6487767696380615,-0.15416480600833893,0.7382909059524536,-0.6487767696380615,-0.18443213403224945,0.7301537990570068,-0.6487767696380615,-0.2143924981355667,0.7208014130592346,-0.6487767696380615,-0.2439960241317749,0.7102493047714233,-0.6487767696380615,-0.27319344878196716,0.6985150575637817,-0.6487767696380615,-0.30193614959716797,0.6856182217597961,-0.6487767696380615,-0.33017632365226746,0.6715801954269409,-0.6487767696380615,-0.35786694288253784,0.6564244031906128,-0.6487767696380615,-0.38496193289756775,0.6401761174201965,-0.6487767696380615,-0.4114162027835846,0.6228622794151306,-0.6487767696380615,-0.43718570470809937,0.6045117378234863,-0.6487767696380615,-0.4622275233268738,0.5851550102233887,-0.6487767696380615,-0.4865000545978546,0.5648244023323059,-0.6487767696380615,-0.5099628567695618,0.5435537099838257,-0.6487767696380615,-0.5325767993927002,0.5213783383369446,-0.6487767696380615,-0.5543044209480286,0.4983351230621338,-0.6487767696380615,-0.5751094222068787,0.47446250915527344,-0.6487767696380615,-0.594957172870636,0.44980019330978394,-0.6487767696380615,-0.6138147115707397,0.4243892431259155,-0.6487767696380615,-0.6316506266593933,0.39827194809913635,-0.6487767696380615,-0.6484351754188538,0.3714917302131653,-0.6487767696380615,-0.664140522480011,0.3440932333469391,-0.6487767696380615,-0.6787404417991638,0.31612202525138855,-0.6487767696380615,-0.6922106742858887,0.28762465715408325,-0.6487767696380615,-0.7045287489891052,0.258648544549942,-0.6487767696380615,-0.7156742811203003,0.2292419672012329,-0.6487767696380615,-0.7256285548210144,0.19945381581783295,-0.6487767696380615,-0.7343751788139343,0.16933371126651764,-0.6487767696380615,-0.7418994903564453,0.1389317661523819,-0.6487767696380615,-0.7481889128684998,0.10829856991767883,-0.6487767696380615,-0.7532331347465515,0.07748513668775558,-0.6487767696380615,-0.7570236325263977,0.04654272645711899,-0.6487767696380615,-0.7595541477203369,0.015522854402661324,-0.6487767696380615,-0.7608204483985901,-0.015522854402661324,-0.6487767696380615,-0.7608204483985901,-0.04654272645711899,-0.6487767696380615,-0.7595541477203369,-0.07748513668775558,-0.6487767696380615,-0.7570236325263977,-0.10829856991767883,-0.6487767696380615,-0.7532331347465515,-0.1389317661523819,-0.6487767696380615,-0.7481889128684998,-0.16933371126651764,-0.6487767696380615,-0.7418994903564453,-0.19945381581783295,-0.6487767696380615,-0.7343751788139343,-0.2292419672012329,-0.6487767696380615,-0.7256285548210144,-0.258648544549942,-0.6487767696380615,-0.7156742811203003,-0.28762465715408325,-0.6487767696380615,-0.7045287489891052,-0.31612202525138855,-0.6487767696380615,-0.6922106742858887,-0.3440932333469391,-0.6487767696380615,-0.6787404417991638,-0.3714917302131653,-0.6487767696380615,-0.664140522480011,-0.39827194809913635,-0.6487767696380615,-0.6484351754188538,-0.4243892431259155,-0.6487767696380615,-0.6316506266593933,-0.44980019330978394,-0.6487767696380615,-0.6138147115707397,-0.47446250915527344,-0.6487767696380615,-0.594957172870636,-0.4983351230621338,-0.6487767696380615,-0.5751094222068787,-0.5213783383369446,-0.6487767696380615,-0.5543044209480286,-0.5435537099838257,-0.6487767696380615,-0.5325767993927002,-0.5648244023323059,-0.6487767696380615,-0.5099628567695618,-0.5851550102233887,-0.6487767696380615,-0.4865000545978546,-0.6045117378234863,-0.6487767696380615,-0.4622275233268738,-0.6228622794151306,-0.6487767696380615,-0.43718570470809937,-0.6401761174201965,-0.6487767696380615,-0.4114162027835846,-0.6564244031906128,-0.6487767696380615,-0.38496193289756775,-0.6715801954269409,-0.6487767696380615,-0.35786694288253784,-0.6856182217597961,-0.6487767696380615,-0.33017632365226746,-0.6985150575637817,-0.6487767696380615,-0.30193614959716797,-0.7102493047714233,-0.6487767696380615,-0.27319344878196716,-0.7208014130592346,-0.6487767696380615,-0.2439960241317749,-0.7301537990570068,-0.6487767696380615,-0.2143924981355667,-0.7382909059524536,-0.6487767696380615,-0.18443213403224945,-0.7451992630958557,-0.6487767696380615,-0.15416480600833893,-0.7508672475814819,-0.6487767696380615,-0.12364089488983154,-0.7552855014801025,-0.6487767696380615,-0.09291118383407593,-0.7584466934204102,-0.6487767696380615,-0.06202683597803116,-0.7603455185890198,-0.6487767696380615,-0.031039249151945114,-0.7609787583351135,-0.6487767696380615,-9.319302654642108e-17,-0.7603455185890198,-0.6487767696380615,0.031039249151945114,-0.7584466934204102,-0.6487767696380615,0.06202683597803116,-0.7552855014801025,-0.6487767696380615,0.09291118383407593,-0.7508672475814819,-0.6487767696380615,0.12364089488983154,-0.7451992630958557,-0.6487767696380615,0.15416480600833893,-0.7382909059524536,-0.6487767696380615,0.18443213403224945,-0.7301537990570068,-0.6487767696380615,0.2143924981355667,-0.7208014130592346,-0.6487767696380615,0.2439960241317749,-0.7102493047714233,-0.6487767696380615,0.27319344878196716,-0.6985150575637817,-0.6487767696380615,0.30193614959716797,-0.6856182217597961,-0.6487767696380615,0.33017632365226746,-0.6715801954269409,-0.6487767696380615,0.35786694288253784,-0.6564244031906128,-0.6487767696380615,0.38496193289756775,-0.6401761174201965,-0.6487767696380615,0.4114162027835846,-0.6228622794151306,-0.6487767696380615,0.43718570470809937,-0.6045117378234863,-0.6487767696380615,0.4622275233268738,-0.5851550102233887,-0.6487767696380615,0.4865000545978546,-0.5648244023323059,-0.6487767696380615,0.5099628567695618,-0.5435537099838257,-0.6487767696380615,0.5325767993927002,-0.5213783383369446,-0.6487767696380615,0.5543044209480286,-0.4983351230621338,-0.6487767696380615,0.5751094222068787,-0.47446250915527344,-0.6487767696380615,0.594957172870636,-0.44980019330978394,-0.6487767696380615,0.6138147115707397,-0.4243892431259155,-0.6487767696380615,0.6316506266593933,-0.39827194809913635,-0.6487767696380615,0.6484351754188538,-0.3714917302131653,-0.6487767696380615,0.664140522480011,-0.3440932333469391,-0.6487767696380615,0.6787404417991638,-0.31612202525138855,-0.6487767696380615,0.6922106742858887,-0.28762465715408325,-0.6487767696380615,0.7045287489891052,-0.258648544549942,-0.6487767696380615,0.7156742811203003,-0.2292419672012329,-0.6487767696380615,0.7256285548210144,-0.19945381581783295,-0.6487767696380615,0.7343751788139343,-0.16933371126651764,-0.6487767696380615,0.7418994903564453,-0.1389317661523819,-0.6487767696380615,0.7481889128684998,-0.10829856991767883,-0.6487767696380615,0.7532331347465515,-0.07748513668775558,-0.6487767696380615,0.7570236325263977,-0.04654272645711899,-0.6487767696380615,0.7595541477203369,-0.015522854402661324,-0.6487767696380615,0.7608204483985901,0.015522854402661324,-0.6487767696380615,0.7608204483985901,0.04654272645711899,-0.6487767696380615,0.7595541477203369,0.07748513668775558,-0.6487767696380615,0.7570236325263977,0.10829856991767883,-0.6487767696380615,0.7532331347465515,0.1389317661523819,-0.6487767696380615,0.7481889128684998,0.16933371126651764,-0.6487767696380615,0.7418994903564453,0.19945381581783295,-0.6487767696380615,0.7343751788139343,0.2292419672012329,-0.6487767696380615,0.7256285548210144,0.258648544549942,-0.6487767696380615,0.7156742811203003,0.28762465715408325,-0.6487767696380615,0.7045287489891052,0.31612202525138855,-0.6487767696380615,0.6922106742858887,0.3440932333469391,-0.6487767696380615,0.6787404417991638,0.3714917302131653,-0.6487767696380615,0.664140522480011,0.39827194809913635,-0.6487767696380615,0.6484351754188538,0.4243892431259155,-0.6487767696380615,0.6316506266593933,0.44980019330978394,-0.6487767696380615,0.6138147115707397,0.47446250915527344,-0.6487767696380615,0.594957172870636,0.4983351230621338,-0.6487767696380615,0.5751094222068787,0.5213783383369446,-0.6487767696380615,0.5543044209480286,0.5435537099838257,-0.6487767696380615,0.5325767993927002,0.5648244023323059,-0.6487767696380615,0.5099628567695618,0.5851550102233887,-0.6487767696380615,0.4865000545978546,0.6045117378234863,-0.6487767696380615,0.4622275233268738,0.6228622794151306,-0.6487767696380615,0.43718570470809937,0.6401761174201965,-0.6487767696380615,0.4114162027835846,0.6564244031906128,-0.6487767696380615,0.38496193289756775,0.6715801954269409,-0.6487767696380615,0.35786694288253784,0.6856182217597961,-0.6487767696380615,0.33017632365226746,0.6985150575637817,-0.6487767696380615,0.30193614959716797,0.7102493047714233,-0.6487767696380615,0.27319344878196716,0.7208014130592346,-0.6487767696380615,0.2439960241317749,0.7301537990570068,-0.6487767696380615,0.2143924981355667,0.7382909059524536,-0.6487767696380615,0.18443213403224945,0.7451992630958557,-0.6487767696380615,0.15416480600833893,0.7508672475814819,-0.6487767696380615,0.12364089488983154,0.7552855014801025,-0.6487767696380615,0.09291118383407593,0.7584466934204102,-0.6487767696380615,0.06202683597803116,0.7603455185890198,-0.6487767696380615,0.031039249151945114,0.7609787583351135,-0.6487767696380615,1.8638605309284216e-16,0.804847240447998,-0.5934820175170898,0.0,0.804177463054657,-0.5934820175170898,-0.03282858058810234,0.8021692037582397,-0.5934820175170898,-0.06560252606868744,0.7988258004188538,-0.5934820175170898,-0.09826727211475372,0.7941527962684631,-0.5934820175170898,-0.13076847791671753,0.7881580591201782,-0.5934820175170898,-0.1630520224571228,0.7808514833450317,-0.5934820175170898,-0.19506417214870453,0.7722452878952026,-0.5934820175170898,-0.2267516702413559,0.7623537182807922,-0.5934820175170898,-0.2580617666244507,0.7511933445930481,-0.5934820175170898,-0.2889423370361328,0.7387826442718506,-0.5934820175170898,-0.3193419873714447,0.7251423001289368,-0.5934820175170898,-0.34921014308929443,0.7102950811386108,-0.5934820175170898,-0.37849706411361694,0.694265604019165,-0.5934820175170898,-0.40715399384498596,0.6770805716514587,-0.5934820175170898,-0.43513327836990356,0.6587686538696289,-0.5934820175170898,-0.4623883068561554,0.639360249042511,-0.5934820175170898,-0.48887374997138977,0.618887722492218,-0.5934820175170898,-0.5145455002784729,0.5973851084709167,-0.5934820175170898,-0.5393608808517456,0.5748881697654724,-0.5934820175170898,-0.5632784962654114,0.5514343976974487,-0.5934820175170898,-0.586258590221405,0.5270628333091736,-0.5934820175170898,-0.6082629561424255,0.501814067363739,-0.5934820175170898,-0.629254937171936,0.4757300317287445,-0.5934820175170898,-0.6491995453834534,0.4488541781902313,-0.5934820175170898,-0.6680636405944824,0.4212312698364258,-0.5934820175170898,-0.6858158111572266,0.3929072618484497,-0.5934820175170898,-0.7024264931678772,0.36392930150032043,-0.5934820175170898,-0.7178680300712585,0.33434563875198364,-0.5934820175170898,-0.7321147918701172,0.30420544743537903,-0.5934820175170898,-0.7451429963111877,0.27355897426605225,-0.5934820175170898,-0.7569310069084167,0.24245716631412506,-0.5934820175170898,-0.7674592137336731,0.2109518200159073,-0.5934820175170898,-0.7767099738121033,0.17909535765647888,-0.5934820175170898,-0.7846680283546448,0.14694081246852875,-0.5934820175170898,-0.7913200855255127,0.11454170197248459,-0.5934820175170898,-0.7966550588607788,0.08195195347070694,-0.5934820175170898,-0.8006640672683716,0.04922579601407051,-0.5934820175170898,-0.803340494632721,0.01641770638525486,-0.5934820175170898,-0.8046797513961792,-0.01641770638525486,-0.5934820175170898,-0.8046797513961792,-0.04922579601407051,-0.5934820175170898,-0.803340494632721,-0.08195195347070694,-0.5934820175170898,-0.8006640672683716,-0.11454170197248459,-0.5934820175170898,-0.7966550588607788,-0.14694081246852875,-0.5934820175170898,-0.7913200855255127,-0.17909535765647888,-0.5934820175170898,-0.7846680283546448,-0.2109518200159073,-0.5934820175170898,-0.7767099738121033,-0.24245716631412506,-0.5934820175170898,-0.7674592137336731,-0.27355897426605225,-0.5934820175170898,-0.7569310069084167,-0.30420544743537903,-0.5934820175170898,-0.7451429963111877,-0.33434563875198364,-0.5934820175170898,-0.7321147918701172,-0.36392930150032043,-0.5934820175170898,-0.7178680300712585,-0.3929072618484497,-0.5934820175170898,-0.7024264931678772,-0.4212312698364258,-0.5934820175170898,-0.6858158111572266,-0.4488541781902313,-0.5934820175170898,-0.6680636405944824,-0.4757300317287445,-0.5934820175170898,-0.6491995453834534,-0.501814067363739,-0.5934820175170898,-0.629254937171936,-0.5270628333091736,-0.5934820175170898,-0.6082629561424255,-0.5514343976974487,-0.5934820175170898,-0.586258590221405,-0.5748881697654724,-0.5934820175170898,-0.5632784962654114,-0.5973851084709167,-0.5934820175170898,-0.5393608808517456,-0.618887722492218,-0.5934820175170898,-0.5145455002784729,-0.639360249042511,-0.5934820175170898,-0.48887374997138977,-0.6587686538696289,-0.5934820175170898,-0.4623883068561554,-0.6770805716514587,-0.5934820175170898,-0.43513327836990356,-0.694265604019165,-0.5934820175170898,-0.40715399384498596,-0.7102950811386108,-0.5934820175170898,-0.37849706411361694,-0.7251423001289368,-0.5934820175170898,-0.34921014308929443,-0.7387826442718506,-0.5934820175170898,-0.3193419873714447,-0.7511933445930481,-0.5934820175170898,-0.2889423370361328,-0.7623537182807922,-0.5934820175170898,-0.2580617666244507,-0.7722452878952026,-0.5934820175170898,-0.2267516702413559,-0.7808514833450317,-0.5934820175170898,-0.19506417214870453,-0.7881580591201782,-0.5934820175170898,-0.1630520224571228,-0.7941527962684631,-0.5934820175170898,-0.13076847791671753,-0.7988258004188538,-0.5934820175170898,-0.09826727211475372,-0.8021692037582397,-0.5934820175170898,-0.06560252606868744,-0.804177463054657,-0.5934820175170898,-0.03282858058810234,-0.804847240447998,-0.5934820175170898,-9.856535948416108e-17,-0.804177463054657,-0.5934820175170898,0.03282858058810234,-0.8021692037582397,-0.5934820175170898,0.06560252606868744,-0.7988258004188538,-0.5934820175170898,0.09826727211475372,-0.7941527962684631,-0.5934820175170898,0.13076847791671753,-0.7881580591201782,-0.5934820175170898,0.1630520224571228,-0.7808514833450317,-0.5934820175170898,0.19506417214870453,-0.7722452878952026,-0.5934820175170898,0.2267516702413559,-0.7623537182807922,-0.5934820175170898,0.2580617666244507,-0.7511933445930481,-0.5934820175170898,0.2889423370361328,-0.7387826442718506,-0.5934820175170898,0.3193419873714447,-0.7251423001289368,-0.5934820175170898,0.34921014308929443,-0.7102950811386108,-0.5934820175170898,0.37849706411361694,-0.694265604019165,-0.5934820175170898,0.40715399384498596,-0.6770805716514587,-0.5934820175170898,0.43513327836990356,-0.6587686538696289,-0.5934820175170898,0.4623883068561554,-0.639360249042511,-0.5934820175170898,0.48887374997138977,-0.618887722492218,-0.5934820175170898,0.5145455002784729,-0.5973851084709167,-0.5934820175170898,0.5393608808517456,-0.5748881697654724,-0.5934820175170898,0.5632784962654114,-0.5514343976974487,-0.5934820175170898,0.586258590221405,-0.5270628333091736,-0.5934820175170898,0.6082629561424255,-0.501814067363739,-0.5934820175170898,0.629254937171936,-0.4757300317287445,-0.5934820175170898,0.6491995453834534,-0.4488541781902313,-0.5934820175170898,0.6680636405944824,-0.4212312698364258,-0.5934820175170898,0.6858158111572266,-0.3929072618484497,-0.5934820175170898,0.7024264931678772,-0.36392930150032043,-0.5934820175170898,0.7178680300712585,-0.33434563875198364,-0.5934820175170898,0.7321147918701172,-0.30420544743537903,-0.5934820175170898,0.7451429963111877,-0.27355897426605225,-0.5934820175170898,0.7569310069084167,-0.24245716631412506,-0.5934820175170898,0.7674592137336731,-0.2109518200159073,-0.5934820175170898,0.7767099738121033,-0.17909535765647888,-0.5934820175170898,0.7846680283546448,-0.14694081246852875,-0.5934820175170898,0.7913200855255127,-0.11454170197248459,-0.5934820175170898,0.7966550588607788,-0.08195195347070694,-0.5934820175170898,0.8006640672683716,-0.04922579601407051,-0.5934820175170898,0.803340494632721,-0.01641770638525486,-0.5934820175170898,0.8046797513961792,0.01641770638525486,-0.5934820175170898,0.8046797513961792,0.04922579601407051,-0.5934820175170898,0.803340494632721,0.08195195347070694,-0.5934820175170898,0.8006640672683716,0.11454170197248459,-0.5934820175170898,0.7966550588607788,0.14694081246852875,-0.5934820175170898,0.7913200855255127,0.17909535765647888,-0.5934820175170898,0.7846680283546448,0.2109518200159073,-0.5934820175170898,0.7767099738121033,0.24245716631412506,-0.5934820175170898,0.7674592137336731,0.27355897426605225,-0.5934820175170898,0.7569310069084167,0.30420544743537903,-0.5934820175170898,0.7451429963111877,0.33434563875198364,-0.5934820175170898,0.7321147918701172,0.36392930150032043,-0.5934820175170898,0.7178680300712585,0.3929072618484497,-0.5934820175170898,0.7024264931678772,0.4212312698364258,-0.5934820175170898,0.6858158111572266,0.4488541781902313,-0.5934820175170898,0.6680636405944824,0.4757300317287445,-0.5934820175170898,0.6491995453834534,0.501814067363739,-0.5934820175170898,0.629254937171936,0.5270628333091736,-0.5934820175170898,0.6082629561424255,0.5514343976974487,-0.5934820175170898,0.586258590221405,0.5748881697654724,-0.5934820175170898,0.5632784962654114,0.5973851084709167,-0.5934820175170898,0.5393608808517456,0.618887722492218,-0.5934820175170898,0.5145455002784729,0.639360249042511,-0.5934820175170898,0.48887374997138977,0.6587686538696289,-0.5934820175170898,0.4623883068561554,0.6770805716514587,-0.5934820175170898,0.43513327836990356,0.694265604019165,-0.5934820175170898,0.40715399384498596,0.7102950811386108,-0.5934820175170898,0.37849706411361694,0.7251423001289368,-0.5934820175170898,0.34921014308929443,0.7387826442718506,-0.5934820175170898,0.3193419873714447,0.7511933445930481,-0.5934820175170898,0.2889423370361328,0.7623537182807922,-0.5934820175170898,0.2580617666244507,0.7722452878952026,-0.5934820175170898,0.2267516702413559,0.7808514833450317,-0.5934820175170898,0.19506417214870453,0.7881580591201782,-0.5934820175170898,0.1630520224571228,0.7941527962684631,-0.5934820175170898,0.13076847791671753,0.7988258004188538,-0.5934820175170898,0.09826727211475372,0.8021692037582397,-0.5934820175170898,0.06560252606868744,0.804177463054657,-0.5934820175170898,0.03282858058810234,0.804847240447998,-0.5934820175170898,1.9713071896832217e-16,0.8447059988975525,-0.5352305769920349,0.0,0.8440030217170715,-0.5352305769920349,-0.03445436432957649,0.8418952822685242,-0.5352305769920349,-0.06885138154029846,0.8383863568305969,-0.5352305769920349,-0.10313380509614944,0.8334819674491882,-0.5352305769920349,-0.1372445672750473,0.8271902799606323,-0.5352305769920349,-0.17112690210342407,0.8195219039916992,-0.5352305769920349,-0.20472441613674164,0.8104894757270813,-0.5352305769920349,-0.2379811853170395,0.8001080751419067,-0.5352305769920349,-0.2708418369293213,0.7883949279785156,-0.5352305769920349,-0.3032517433166504,0.7753696441650391,-0.5352305769920349,-0.3351568877696991,0.7610538005828857,-0.5352305769920349,-0.3665041923522949,0.7454712390899658,-0.5352305769920349,-0.3972415030002594,0.7286479473114014,-0.5352305769920349,-0.42731761932373047,0.710611879825592,-0.5352305769920349,-0.4566825330257416,0.6913930773735046,-0.5352305769920349,-0.4852873384952545,0.6710235476493835,-0.5352305769920349,-0.5130844116210938,0.6495371460914612,-0.5352305769920349,-0.5400275588035583,0.6269696354866028,-0.5352305769920349,-0.5660718083381653,0.6033585667610168,-0.5352305769920349,-0.5911739468574524,0.5787433385848999,-0.5352305769920349,-0.6152921319007874,0.5531647801399231,-0.5352305769920349,-0.6383861899375916,0.5266655683517456,-0.5352305769920349,-0.6604177355766296,0.49928978085517883,-0.5352305769920349,-0.6813501119613647,0.4710829555988312,-0.5352305769920349,-0.7011483907699585,0.44209206104278564,-0.5352305769920349,-0.7197797298431396,0.4123653769493103,-0.5352305769920349,-0.7372130155563354,0.38195231556892395,-0.5352305769920349,-0.7534192800521851,0.35090354084968567,-0.5352305769920349,-0.76837158203125,0.3192707300186157,-0.5352305769920349,-0.7820450067520142,0.28710654377937317,-0.5352305769920349,-0.7944167852401733,0.2544644773006439,-0.5352305769920349,-0.80546635389328,0.22139886021614075,-0.5352305769920349,-0.8151752948760986,0.1879647672176361,-0.5352305769920349,-0.823527455329895,0.15421782433986664,-0.5352305769920349,-0.8305089473724365,0.12021419405937195,-0.5352305769920349,-0.8361080884933472,0.08601048588752747,-0.5352305769920349,-0.8403156399726868,0.05166362226009369,-0.5352305769920349,-0.8431246280670166,0.017230767756700516,-0.5352305769920349,-0.8445302248001099,-0.017230767756700516,-0.5352305769920349,-0.8445302248001099,-0.05166362226009369,-0.5352305769920349,-0.8431246280670166,-0.08601048588752747,-0.5352305769920349,-0.8403156399726868,-0.12021419405937195,-0.5352305769920349,-0.8361080884933472,-0.15421782433986664,-0.5352305769920349,-0.8305089473724365,-0.1879647672176361,-0.5352305769920349,-0.823527455329895,-0.22139886021614075,-0.5352305769920349,-0.8151752948760986,-0.2544644773006439,-0.5352305769920349,-0.80546635389328,-0.28710654377937317,-0.5352305769920349,-0.7944167852401733,-0.3192707300186157,-0.5352305769920349,-0.7820450067520142,-0.35090354084968567,-0.5352305769920349,-0.76837158203125,-0.38195231556892395,-0.5352305769920349,-0.7534192800521851,-0.4123653769493103,-0.5352305769920349,-0.7372130155563354,-0.44209206104278564,-0.5352305769920349,-0.7197797298431396,-0.4710829555988312,-0.5352305769920349,-0.7011483907699585,-0.49928978085517883,-0.5352305769920349,-0.6813501119613647,-0.5266655683517456,-0.5352305769920349,-0.6604177355766296,-0.5531647801399231,-0.5352305769920349,-0.6383861899375916,-0.5787433385848999,-0.5352305769920349,-0.6152921319007874,-0.6033585667610168,-0.5352305769920349,-0.5911739468574524,-0.6269696354866028,-0.5352305769920349,-0.5660718083381653,-0.6495371460914612,-0.5352305769920349,-0.5400275588035583,-0.6710235476493835,-0.5352305769920349,-0.5130844116210938,-0.6913930773735046,-0.5352305769920349,-0.4852873384952545,-0.710611879825592,-0.5352305769920349,-0.4566825330257416,-0.7286479473114014,-0.5352305769920349,-0.42731761932373047,-0.7454712390899658,-0.5352305769920349,-0.3972415030002594,-0.7610538005828857,-0.5352305769920349,-0.3665041923522949,-0.7753696441650391,-0.5352305769920349,-0.3351568877696991,-0.7883949279785156,-0.5352305769920349,-0.3032517433166504,-0.8001080751419067,-0.5352305769920349,-0.2708418369293213,-0.8104894757270813,-0.5352305769920349,-0.2379811853170395,-0.8195219039916992,-0.5352305769920349,-0.20472441613674164,-0.8271902799606323,-0.5352305769920349,-0.17112690210342407,-0.8334819674491882,-0.5352305769920349,-0.1372445672750473,-0.8383863568305969,-0.5352305769920349,-0.10313380509614944,-0.8418952822685242,-0.5352305769920349,-0.06885138154029846,-0.8440030217170715,-0.5352305769920349,-0.03445436432957649,-0.8447059988975525,-0.5352305769920349,-1.0344665154051e-16,-0.8440030217170715,-0.5352305769920349,0.03445436432957649,-0.8418952822685242,-0.5352305769920349,0.06885138154029846,-0.8383863568305969,-0.5352305769920349,0.10313380509614944,-0.8334819674491882,-0.5352305769920349,0.1372445672750473,-0.8271902799606323,-0.5352305769920349,0.17112690210342407,-0.8195219039916992,-0.5352305769920349,0.20472441613674164,-0.8104894757270813,-0.5352305769920349,0.2379811853170395,-0.8001080751419067,-0.5352305769920349,0.2708418369293213,-0.7883949279785156,-0.5352305769920349,0.3032517433166504,-0.7753696441650391,-0.5352305769920349,0.3351568877696991,-0.7610538005828857,-0.5352305769920349,0.3665041923522949,-0.7454712390899658,-0.5352305769920349,0.3972415030002594,-0.7286479473114014,-0.5352305769920349,0.42731761932373047,-0.710611879825592,-0.5352305769920349,0.4566825330257416,-0.6913930773735046,-0.5352305769920349,0.4852873384952545,-0.6710235476493835,-0.5352305769920349,0.5130844116210938,-0.6495371460914612,-0.5352305769920349,0.5400275588035583,-0.6269696354866028,-0.5352305769920349,0.5660718083381653,-0.6033585667610168,-0.5352305769920349,0.5911739468574524,-0.5787433385848999,-0.5352305769920349,0.6152921319007874,-0.5531647801399231,-0.5352305769920349,0.6383861899375916,-0.5266655683517456,-0.5352305769920349,0.6604177355766296,-0.49928978085517883,-0.5352305769920349,0.6813501119613647,-0.4710829555988312,-0.5352305769920349,0.7011483907699585,-0.44209206104278564,-0.5352305769920349,0.7197797298431396,-0.4123653769493103,-0.5352305769920349,0.7372130155563354,-0.38195231556892395,-0.5352305769920349,0.7534192800521851,-0.35090354084968567,-0.5352305769920349,0.76837158203125,-0.3192707300186157,-0.5352305769920349,0.7820450067520142,-0.28710654377937317,-0.5352305769920349,0.7944167852401733,-0.2544644773006439,-0.5352305769920349,0.80546635389328,-0.22139886021614075,-0.5352305769920349,0.8151752948760986,-0.1879647672176361,-0.5352305769920349,0.823527455329895,-0.15421782433986664,-0.5352305769920349,0.8305089473724365,-0.12021419405937195,-0.5352305769920349,0.8361080884933472,-0.08601048588752747,-0.5352305769920349,0.8403156399726868,-0.05166362226009369,-0.5352305769920349,0.8431246280670166,-0.017230767756700516,-0.5352305769920349,0.8445302248001099,0.017230767756700516,-0.5352305769920349,0.8445302248001099,0.05166362226009369,-0.5352305769920349,0.8431246280670166,0.08601048588752747,-0.5352305769920349,0.8403156399726868,0.12021419405937195,-0.5352305769920349,0.8361080884933472,0.15421782433986664,-0.5352305769920349,0.8305089473724365,0.1879647672176361,-0.5352305769920349,0.823527455329895,0.22139886021614075,-0.5352305769920349,0.8151752948760986,0.2544644773006439,-0.5352305769920349,0.80546635389328,0.28710654377937317,-0.5352305769920349,0.7944167852401733,0.3192707300186157,-0.5352305769920349,0.7820450067520142,0.35090354084968567,-0.5352305769920349,0.76837158203125,0.38195231556892395,-0.5352305769920349,0.7534192800521851,0.4123653769493103,-0.5352305769920349,0.7372130155563354,0.44209206104278564,-0.5352305769920349,0.7197797298431396,0.4710829555988312,-0.5352305769920349,0.7011483907699585,0.49928978085517883,-0.5352305769920349,0.6813501119613647,0.5266655683517456,-0.5352305769920349,0.6604177355766296,0.5531647801399231,-0.5352305769920349,0.6383861899375916,0.5787433385848999,-0.5352305769920349,0.6152921319007874,0.6033585667610168,-0.5352305769920349,0.5911739468574524,0.6269696354866028,-0.5352305769920349,0.5660718083381653,0.6495371460914612,-0.5352305769920349,0.5400275588035583,0.6710235476493835,-0.5352305769920349,0.5130844116210938,0.6913930773735046,-0.5352305769920349,0.4852873384952545,0.710611879825592,-0.5352305769920349,0.4566825330257416,0.7286479473114014,-0.5352305769920349,0.42731761932373047,0.7454712390899658,-0.5352305769920349,0.3972415030002594,0.7610538005828857,-0.5352305769920349,0.3665041923522949,0.7753696441650391,-0.5352305769920349,0.3351568877696991,0.7883949279785156,-0.5352305769920349,0.3032517433166504,0.8001080751419067,-0.5352305769920349,0.2708418369293213,0.8104894757270813,-0.5352305769920349,0.2379811853170395,0.8195219039916992,-0.5352305769920349,0.20472441613674164,0.8271902799606323,-0.5352305769920349,0.17112690210342407,0.8334819674491882,-0.5352305769920349,0.1372445672750473,0.8383863568305969,-0.5352305769920349,0.10313380509614944,0.8418952822685242,-0.5352305769920349,0.06885138154029846,0.8440030217170715,-0.5352305769920349,0.03445436432957649,0.8447059988975525,-0.5352305769920349,2.0689330308102e-16,0.88035649061203,-0.4743126630783081,0.0,0.8796238303184509,-0.4743126630783081,-0.035908494144678116,0.8774271607398987,-0.4743126630783081,-0.0717572271823883,0.8737700581550598,-0.4743126630783081,-0.10748652368783951,0.868658721446991,-0.4743126630783081,-0.14303691685199738,0.8621015548706055,-0.4743126630783081,-0.17834924161434174,0.8541094660758972,-0.4743126630783081,-0.21336472034454346,0.8446958661079407,-0.4743126630783081,-0.2480250746011734,0.8338763117790222,-0.4743126630783081,-0.282272607088089,0.8216688632965088,-0.4743126630783081,-0.31605035066604614,0.808093786239624,-0.4743126630783081,-0.3493020534515381,0.7931737899780273,-0.4743126630783081,-0.3819723427295685,0.7769335508346558,-0.4743126630783081,-0.41400691866874695,0.7594002485275269,-0.4743126630783081,-0.4453524053096771,0.740602970123291,-0.4743126630783081,-0.47595664858818054,0.7205730676651001,-0.4743126630783081,-0.5057687163352966,0.6993438005447388,-0.4743126630783081,-0.5347389578819275,0.6769505739212036,-0.4743126630783081,-0.5628191828727722,0.6534305810928345,-0.4743126630783081,-0.5899626612663269,0.6288231015205383,-0.4743126630783081,-0.6161242127418518,0.6031689643859863,-0.4743126630783081,-0.6412602663040161,0.5765109062194824,-0.4743126630783081,-0.6653290390968323,0.5488932728767395,-0.4743126630783081,-0.6882904171943665,0.5203620791435242,-0.4743126630783081,-0.7101061940193176,0.4909648299217224,-0.4743126630783081,-0.7307400703430176,0.46075040102005005,-0.4743126630783081,-0.7501577138900757,0.4297690689563751,-0.4743126630783081,-0.7683267593383789,0.3980724513530731,-0.4743126630783081,-0.7852170467376709,0.36571329832077026,-0.4743126630783081,-0.8008003830909729,0.33274543285369873,-0.4743126630783081,-0.8150508999824524,0.29922375082969666,-0.4743126630783081,-0.827944815158844,0.2652040421962738,-0.4743126630783081,-0.8394607305526733,0.2307429164648056,-0.4743126630783081,-0.8495794534683228,0.19589774310588837,-0.4743126630783081,-0.8582841157913208,0.16072651743888855,-0.4743126630783081,-0.8655602335929871,0.12528778612613678,-0.4743126630783081,-0.8713957071304321,0.0896405279636383,-0.4743126630783081,-0.8757808208465576,0.053844064474105835,-0.4743126630783081,-0.8787083029747009,0.017957983538508415,-0.4743126630783081,-0.8801732659339905,-0.017957983538508415,-0.4743126630783081,-0.8801732659339905,-0.053844064474105835,-0.4743126630783081,-0.8787083029747009,-0.0896405279636383,-0.4743126630783081,-0.8757808208465576,-0.12528778612613678,-0.4743126630783081,-0.8713957071304321,-0.16072651743888855,-0.4743126630783081,-0.8655602335929871,-0.19589774310588837,-0.4743126630783081,-0.8582841157913208,-0.2307429164648056,-0.4743126630783081,-0.8495794534683228,-0.2652040421962738,-0.4743126630783081,-0.8394607305526733,-0.29922375082969666,-0.4743126630783081,-0.827944815158844,-0.33274543285369873,-0.4743126630783081,-0.8150508999824524,-0.36571329832077026,-0.4743126630783081,-0.8008003830909729,-0.3980724513530731,-0.4743126630783081,-0.7852170467376709,-0.4297690689563751,-0.4743126630783081,-0.7683267593383789,-0.46075040102005005,-0.4743126630783081,-0.7501577138900757,-0.4909648299217224,-0.4743126630783081,-0.7307400703430176,-0.5203620791435242,-0.4743126630783081,-0.7101061940193176,-0.5488932728767395,-0.4743126630783081,-0.6882904171943665,-0.5765109062194824,-0.4743126630783081,-0.6653290390968323,-0.6031689643859863,-0.4743126630783081,-0.6412602663040161,-0.6288231015205383,-0.4743126630783081,-0.6161242127418518,-0.6534305810928345,-0.4743126630783081,-0.5899626612663269,-0.6769505739212036,-0.4743126630783081,-0.5628191828727722,-0.6993438005447388,-0.4743126630783081,-0.5347389578819275,-0.7205730676651001,-0.4743126630783081,-0.5057687163352966,-0.740602970123291,-0.4743126630783081,-0.47595664858818054,-0.7594002485275269,-0.4743126630783081,-0.4453524053096771,-0.7769335508346558,-0.4743126630783081,-0.41400691866874695,-0.7931737899780273,-0.4743126630783081,-0.3819723427295685,-0.808093786239624,-0.4743126630783081,-0.3493020534515381,-0.8216688632965088,-0.4743126630783081,-0.31605035066604614,-0.8338763117790222,-0.4743126630783081,-0.282272607088089,-0.8446958661079407,-0.4743126630783081,-0.2480250746011734,-0.8541094660758972,-0.4743126630783081,-0.21336472034454346,-0.8621015548706055,-0.4743126630783081,-0.17834924161434174,-0.868658721446991,-0.4743126630783081,-0.14303691685199738,-0.8737700581550598,-0.4743126630783081,-0.10748652368783951,-0.8774271607398987,-0.4743126630783081,-0.0717572271823883,-0.8796238303184509,-0.4743126630783081,-0.035908494144678116,-0.88035649061203,-0.4743126630783081,-1.0781257037056899e-16,-0.8796238303184509,-0.4743126630783081,0.035908494144678116,-0.8774271607398987,-0.4743126630783081,0.0717572271823883,-0.8737700581550598,-0.4743126630783081,0.10748652368783951,-0.868658721446991,-0.4743126630783081,0.14303691685199738,-0.8621015548706055,-0.4743126630783081,0.17834924161434174,-0.8541094660758972,-0.4743126630783081,0.21336472034454346,-0.8446958661079407,-0.4743126630783081,0.2480250746011734,-0.8338763117790222,-0.4743126630783081,0.282272607088089,-0.8216688632965088,-0.4743126630783081,0.31605035066604614,-0.808093786239624,-0.4743126630783081,0.3493020534515381,-0.7931737899780273,-0.4743126630783081,0.3819723427295685,-0.7769335508346558,-0.4743126630783081,0.41400691866874695,-0.7594002485275269,-0.4743126630783081,0.4453524053096771,-0.740602970123291,-0.4743126630783081,0.47595664858818054,-0.7205730676651001,-0.4743126630783081,0.5057687163352966,-0.6993438005447388,-0.4743126630783081,0.5347389578819275,-0.6769505739212036,-0.4743126630783081,0.5628191828727722,-0.6534305810928345,-0.4743126630783081,0.5899626612663269,-0.6288231015205383,-0.4743126630783081,0.6161242127418518,-0.6031689643859863,-0.4743126630783081,0.6412602663040161,-0.5765109062194824,-0.4743126630783081,0.6653290390968323,-0.5488932728767395,-0.4743126630783081,0.6882904171943665,-0.5203620791435242,-0.4743126630783081,0.7101061940193176,-0.4909648299217224,-0.4743126630783081,0.7307400703430176,-0.46075040102005005,-0.4743126630783081,0.7501577138900757,-0.4297690689563751,-0.4743126630783081,0.7683267593383789,-0.3980724513530731,-0.4743126630783081,0.7852170467376709,-0.36571329832077026,-0.4743126630783081,0.8008003830909729,-0.33274543285369873,-0.4743126630783081,0.8150508999824524,-0.29922375082969666,-0.4743126630783081,0.827944815158844,-0.2652040421962738,-0.4743126630783081,0.8394607305526733,-0.2307429164648056,-0.4743126630783081,0.8495794534683228,-0.19589774310588837,-0.4743126630783081,0.8582841157913208,-0.16072651743888855,-0.4743126630783081,0.8655602335929871,-0.12528778612613678,-0.4743126630783081,0.8713957071304321,-0.0896405279636383,-0.4743126630783081,0.8757808208465576,-0.053844064474105835,-0.4743126630783081,0.8787083029747009,-0.017957983538508415,-0.4743126630783081,0.8801732659339905,0.017957983538508415,-0.4743126630783081,0.8801732659339905,0.053844064474105835,-0.4743126630783081,0.8787083029747009,0.0896405279636383,-0.4743126630783081,0.8757808208465576,0.12528778612613678,-0.4743126630783081,0.8713957071304321,0.16072651743888855,-0.4743126630783081,0.8655602335929871,0.19589774310588837,-0.4743126630783081,0.8582841157913208,0.2307429164648056,-0.4743126630783081,0.8495794534683228,0.2652040421962738,-0.4743126630783081,0.8394607305526733,0.29922375082969666,-0.4743126630783081,0.827944815158844,0.33274543285369873,-0.4743126630783081,0.8150508999824524,0.36571329832077026,-0.4743126630783081,0.8008003830909729,0.3980724513530731,-0.4743126630783081,0.7852170467376709,0.4297690689563751,-0.4743126630783081,0.7683267593383789,0.46075040102005005,-0.4743126630783081,0.7501577138900757,0.4909648299217224,-0.4743126630783081,0.7307400703430176,0.5203620791435242,-0.4743126630783081,0.7101061940193176,0.5488932728767395,-0.4743126630783081,0.6882904171943665,0.5765109062194824,-0.4743126630783081,0.6653290390968323,0.6031689643859863,-0.4743126630783081,0.6412602663040161,0.6288231015205383,-0.4743126630783081,0.6161242127418518,0.6534305810928345,-0.4743126630783081,0.5899626612663269,0.6769505739212036,-0.4743126630783081,0.5628191828727722,0.6993438005447388,-0.4743126630783081,0.5347389578819275,0.7205730676651001,-0.4743126630783081,0.5057687163352966,0.740602970123291,-0.4743126630783081,0.47595664858818054,0.7594002485275269,-0.4743126630783081,0.4453524053096771,0.7769335508346558,-0.4743126630783081,0.41400691866874695,0.7931737899780273,-0.4743126630783081,0.3819723427295685,0.808093786239624,-0.4743126630783081,0.3493020534515381,0.8216688632965088,-0.4743126630783081,0.31605035066604614,0.8338763117790222,-0.4743126630783081,0.282272607088089,0.8446958661079407,-0.4743126630783081,0.2480250746011734,0.8541094660758972,-0.4743126630783081,0.21336472034454346,0.8621015548706055,-0.4743126630783081,0.17834924161434174,0.868658721446991,-0.4743126630783081,0.14303691685199738,0.8737700581550598,-0.4743126630783081,0.10748652368783951,0.8774271607398987,-0.4743126630783081,0.0717572271823883,0.8796238303184509,-0.4743126630783081,0.035908494144678116,0.88035649061203,-0.4743126630783081,2.1562514074113798e-16,0.9116210341453552,-0.41103172302246094,0.0,0.910862386226654,-0.41103172302246094,-0.0371837317943573,0.9085876941680908,-0.41103172302246094,-0.07430557906627655,0.9048007726669312,-0.41103172302246094,-0.11130374670028687,0.8995078206062317,-0.41103172302246094,-0.14811666309833527,0.8927178382873535,-0.41103172302246094,-0.18468305468559265,0.8844419121742249,-0.41103172302246094,-0.22094206511974335,0.8746939897537231,-0.41103172302246094,-0.2568333148956299,0.8634901642799377,-0.41103172302246094,-0.2922971248626709,0.8508492112159729,-0.41103172302246094,-0.3272744119167328,0.8367920517921448,-0.41103172302246094,-0.36170700192451477,0.8213421702384949,-0.41103172302246094,-0.3955375552177429,0.8045252561569214,-0.41103172302246094,-0.4287097752094269,0.786369264125824,-0.41103172302246094,-0.46116843819618225,0.7669044137001038,-0.41103172302246094,-0.49285954236984253,0.7461631298065186,-0.41103172302246094,-0.5237303376197815,0.7241799831390381,-0.41103172302246094,-0.5537294149398804,0.7009914517402649,-0.41103172302246094,-0.5828068852424622,0.6766362190246582,-0.41103172302246094,-0.6109143495559692,0.6511548161506653,-0.41103172302246094,-0.6380049586296082,0.6245896220207214,-0.41103172302246094,-0.6640337109565735,0.5969848036766052,-0.41103172302246094,-0.6889572143554688,0.568386435508728,-0.41103172302246094,-0.712734043598175,0.5388420224189758,-0.41103172302246094,-0.7353245615959167,0.5084007382392883,-0.41103172302246094,-0.7566912174224854,0.477113276720047,-0.41103172302246094,-0.7767984867095947,0.44503170251846313,-0.41103172302246094,-0.7956128120422363,0.4122094213962555,-0.41103172302246094,-0.8131029009819031,0.3787010610103607,-0.41103172302246094,-0.8292396664619446,0.3445624113082886,-0.41103172302246094,-0.8439962267875671,0.3098502457141876,-0.41103172302246094,-0.8573480844497681,0.2746223509311676,-0.41103172302246094,-0.8692729473114014,0.2389374077320099,-0.41103172302246094,-0.8797510266304016,0.20285476744174957,-0.41103172302246094,-0.8887647986412048,0.16643449664115906,-0.41103172302246094,-0.8962993025779724,0.12973719835281372,-0.41103172302246094,-0.9023420214653015,0.09282397478818893,-0.41103172302246094,-0.9068829417228699,0.055756255984306335,-0.41103172302246094,-0.9099143743515015,0.018595736473798752,-0.41103172302246094,-0.9114313721656799,-0.018595736473798752,-0.41103172302246094,-0.9114313721656799,-0.055756255984306335,-0.41103172302246094,-0.9099143743515015,-0.09282397478818893,-0.41103172302246094,-0.9068829417228699,-0.12973719835281372,-0.41103172302246094,-0.9023420214653015,-0.16643449664115906,-0.41103172302246094,-0.8962993025779724,-0.20285476744174957,-0.41103172302246094,-0.8887647986412048,-0.2389374077320099,-0.41103172302246094,-0.8797510266304016,-0.2746223509311676,-0.41103172302246094,-0.8692729473114014,-0.3098502457141876,-0.41103172302246094,-0.8573480844497681,-0.3445624113082886,-0.41103172302246094,-0.8439962267875671,-0.3787010610103607,-0.41103172302246094,-0.8292396664619446,-0.4122094213962555,-0.41103172302246094,-0.8131029009819031,-0.44503170251846313,-0.41103172302246094,-0.7956128120422363,-0.477113276720047,-0.41103172302246094,-0.7767984867095947,-0.5084007382392883,-0.41103172302246094,-0.7566912174224854,-0.5388420224189758,-0.41103172302246094,-0.7353245615959167,-0.568386435508728,-0.41103172302246094,-0.712734043598175,-0.5969848036766052,-0.41103172302246094,-0.6889572143554688,-0.6245896220207214,-0.41103172302246094,-0.6640337109565735,-0.6511548161506653,-0.41103172302246094,-0.6380049586296082,-0.6766362190246582,-0.41103172302246094,-0.6109143495559692,-0.7009914517402649,-0.41103172302246094,-0.5828068852424622,-0.7241799831390381,-0.41103172302246094,-0.5537294149398804,-0.7461631298065186,-0.41103172302246094,-0.5237303376197815,-0.7669044137001038,-0.41103172302246094,-0.49285954236984253,-0.786369264125824,-0.41103172302246094,-0.46116843819618225,-0.8045252561569214,-0.41103172302246094,-0.4287097752094269,-0.8213421702384949,-0.41103172302246094,-0.3955375552177429,-0.8367920517921448,-0.41103172302246094,-0.36170700192451477,-0.8508492112159729,-0.41103172302246094,-0.3272744119167328,-0.8634901642799377,-0.41103172302246094,-0.2922971248626709,-0.8746939897537231,-0.41103172302246094,-0.2568333148956299,-0.8844419121742249,-0.41103172302246094,-0.22094206511974335,-0.8927178382873535,-0.41103172302246094,-0.18468305468559265,-0.8995078206062317,-0.41103172302246094,-0.14811666309833527,-0.9048007726669312,-0.41103172302246094,-0.11130374670028687,-0.9085876941680908,-0.41103172302246094,-0.07430557906627655,-0.910862386226654,-0.41103172302246094,-0.0371837317943573,-0.9116210341453552,-0.41103172302246094,-1.1164137766784014e-16,-0.910862386226654,-0.41103172302246094,0.0371837317943573,-0.9085876941680908,-0.41103172302246094,0.07430557906627655,-0.9048007726669312,-0.41103172302246094,0.11130374670028687,-0.8995078206062317,-0.41103172302246094,0.14811666309833527,-0.8927178382873535,-0.41103172302246094,0.18468305468559265,-0.8844419121742249,-0.41103172302246094,0.22094206511974335,-0.8746939897537231,-0.41103172302246094,0.2568333148956299,-0.8634901642799377,-0.41103172302246094,0.2922971248626709,-0.8508492112159729,-0.41103172302246094,0.3272744119167328,-0.8367920517921448,-0.41103172302246094,0.36170700192451477,-0.8213421702384949,-0.41103172302246094,0.3955375552177429,-0.8045252561569214,-0.41103172302246094,0.4287097752094269,-0.786369264125824,-0.41103172302246094,0.46116843819618225,-0.7669044137001038,-0.41103172302246094,0.49285954236984253,-0.7461631298065186,-0.41103172302246094,0.5237303376197815,-0.7241799831390381,-0.41103172302246094,0.5537294149398804,-0.7009914517402649,-0.41103172302246094,0.5828068852424622,-0.6766362190246582,-0.41103172302246094,0.6109143495559692,-0.6511548161506653,-0.41103172302246094,0.6380049586296082,-0.6245896220207214,-0.41103172302246094,0.6640337109565735,-0.5969848036766052,-0.41103172302246094,0.6889572143554688,-0.568386435508728,-0.41103172302246094,0.712734043598175,-0.5388420224189758,-0.41103172302246094,0.7353245615959167,-0.5084007382392883,-0.41103172302246094,0.7566912174224854,-0.477113276720047,-0.41103172302246094,0.7767984867095947,-0.44503170251846313,-0.41103172302246094,0.7956128120422363,-0.4122094213962555,-0.41103172302246094,0.8131029009819031,-0.3787010610103607,-0.41103172302246094,0.8292396664619446,-0.3445624113082886,-0.41103172302246094,0.8439962267875671,-0.3098502457141876,-0.41103172302246094,0.8573480844497681,-0.2746223509311676,-0.41103172302246094,0.8692729473114014,-0.2389374077320099,-0.41103172302246094,0.8797510266304016,-0.20285476744174957,-0.41103172302246094,0.8887647986412048,-0.16643449664115906,-0.41103172302246094,0.8962993025779724,-0.12973719835281372,-0.41103172302246094,0.9023420214653015,-0.09282397478818893,-0.41103172302246094,0.9068829417228699,-0.055756255984306335,-0.41103172302246094,0.9099143743515015,-0.018595736473798752,-0.41103172302246094,0.9114313721656799,0.018595736473798752,-0.41103172302246094,0.9114313721656799,0.055756255984306335,-0.41103172302246094,0.9099143743515015,0.09282397478818893,-0.41103172302246094,0.9068829417228699,0.12973719835281372,-0.41103172302246094,0.9023420214653015,0.16643449664115906,-0.41103172302246094,0.8962993025779724,0.20285476744174957,-0.41103172302246094,0.8887647986412048,0.2389374077320099,-0.41103172302246094,0.8797510266304016,0.2746223509311676,-0.41103172302246094,0.8692729473114014,0.3098502457141876,-0.41103172302246094,0.8573480844497681,0.3445624113082886,-0.41103172302246094,0.8439962267875671,0.3787010610103607,-0.41103172302246094,0.8292396664619446,0.4122094213962555,-0.41103172302246094,0.8131029009819031,0.44503170251846313,-0.41103172302246094,0.7956128120422363,0.477113276720047,-0.41103172302246094,0.7767984867095947,0.5084007382392883,-0.41103172302246094,0.7566912174224854,0.5388420224189758,-0.41103172302246094,0.7353245615959167,0.568386435508728,-0.41103172302246094,0.712734043598175,0.5969848036766052,-0.41103172302246094,0.6889572143554688,0.6245896220207214,-0.41103172302246094,0.6640337109565735,0.6511548161506653,-0.41103172302246094,0.6380049586296082,0.6766362190246582,-0.41103172302246094,0.6109143495559692,0.7009914517402649,-0.41103172302246094,0.5828068852424622,0.7241799831390381,-0.41103172302246094,0.5537294149398804,0.7461631298065186,-0.41103172302246094,0.5237303376197815,0.7669044137001038,-0.41103172302246094,0.49285954236984253,0.786369264125824,-0.41103172302246094,0.46116843819618225,0.8045252561569214,-0.41103172302246094,0.4287097752094269,0.8213421702384949,-0.41103172302246094,0.3955375552177429,0.8367920517921448,-0.41103172302246094,0.36170700192451477,0.8508492112159729,-0.41103172302246094,0.3272744119167328,0.8634901642799377,-0.41103172302246094,0.2922971248626709,0.8746939897537231,-0.41103172302246094,0.2568333148956299,0.8844419121742249,-0.41103172302246094,0.22094206511974335,0.8927178382873535,-0.41103172302246094,0.18468305468559265,0.8995078206062317,-0.41103172302246094,0.14811666309833527,0.9048007726669312,-0.41103172302246094,0.11130374670028687,0.9085876941680908,-0.41103172302246094,0.07430557906627655,0.910862386226654,-0.41103172302246094,0.0371837317943573,0.9116210341453552,-0.41103172302246094,2.232827553356803e-16,0.9383439421653748,-0.3457030653953552,0.0,0.9375630617141724,-0.3457030653953552,-0.03827372193336487,0.9352216720581055,-0.3457030653953552,-0.07648374140262604,0.931323766708374,-0.3457030653953552,-0.11456646770238876,0.9258756637573242,-0.3457030653953552,-0.15245850384235382,0.9188866019248962,-0.3457030653953552,-0.19009678065776825,0.9103681445121765,-0.3457030653953552,-0.2274186760187149,0.9003344178199768,-0.3457030653953552,-0.26436203718185425,0.8888022303581238,-0.3457030653953552,-0.30086541175842285,0.8757907152175903,-0.3457030653953552,-0.336868017911911,0.8613215088844299,-0.3457030653953552,-0.37230995297431946,0.8454186916351318,-0.3457030653953552,-0.40713217854499817,0.8281087875366211,-0.3457030653953552,-0.44127681851387024,0.8094205856323242,-0.3457030653953552,-0.47468695044517517,0.789385199546814,-0.3457030653953552,-0.5073070526123047,0.768035888671875,-0.3457030653953552,-0.5390827655792236,0.745408296585083,-0.3457030653953552,-0.5699612498283386,0.721540093421936,-0.3457030653953552,-0.5998910665512085,0.6964709162712097,-0.3457030653953552,-0.6288224458694458,0.6702425479888916,-0.3457030653953552,-0.656707227230072,0.6428986191749573,-0.3457030653953552,-0.6834989190101624,0.6144846081733704,-0.3457030653953552,-0.7091530561447144,0.5850479006767273,-0.3457030653953552,-0.7336268424987793,0.5546374320983887,-0.3457030653953552,-0.7568796277046204,0.5233038067817688,-0.3457030653953552,-0.7788726091384888,0.49109917879104614,-0.3457030653953552,-0.7995692491531372,0.45807719230651855,-0.3457030653953552,-0.8189350962638855,0.42429277300834656,-0.3457030653953552,-0.8369379043579102,0.38980215787887573,-0.3457030653953552,-0.8535476922988892,0.35466277599334717,-0.3457030653953552,-0.8687368631362915,0.31893306970596313,-0.3457030653953552,-0.8824800848960876,0.28267255425453186,-0.3457030653953552,-0.8947545289993286,0.24594153463840485,-0.3457030653953552,-0.9055396914482117,0.20880118012428284,-0.3457030653953552,-0.914817750453949,0.17131328582763672,-0.3457030653953552,-0.9225730895996094,0.13354027271270752,-0.3457030653953552,-0.9287929534912109,0.09554498642683029,-0.3457030653953552,-0.9334669709205627,0.05739067494869232,-0.3457030653953552,-0.9365872740745544,0.01914084516465664,-0.3457030653953552,-0.9381487369537354,-0.01914084516465664,-0.3457030653953552,-0.9381487369537354,-0.05739067494869232,-0.3457030653953552,-0.9365872740745544,-0.09554498642683029,-0.3457030653953552,-0.9334669709205627,-0.13354027271270752,-0.3457030653953552,-0.9287929534912109,-0.17131328582763672,-0.3457030653953552,-0.9225730895996094,-0.20880118012428284,-0.3457030653953552,-0.914817750453949,-0.24594153463840485,-0.3457030653953552,-0.9055396914482117,-0.28267255425453186,-0.3457030653953552,-0.8947545289993286,-0.31893306970596313,-0.3457030653953552,-0.8824800848960876,-0.35466277599334717,-0.3457030653953552,-0.8687368631362915,-0.38980215787887573,-0.3457030653953552,-0.8535476922988892,-0.42429277300834656,-0.3457030653953552,-0.8369379043579102,-0.45807719230651855,-0.3457030653953552,-0.8189350962638855,-0.49109917879104614,-0.3457030653953552,-0.7995692491531372,-0.5233038067817688,-0.3457030653953552,-0.7788726091384888,-0.5546374320983887,-0.3457030653953552,-0.7568796277046204,-0.5850479006767273,-0.3457030653953552,-0.7336268424987793,-0.6144846081733704,-0.3457030653953552,-0.7091530561447144,-0.6428986191749573,-0.3457030653953552,-0.6834989190101624,-0.6702425479888916,-0.3457030653953552,-0.656707227230072,-0.6964709162712097,-0.3457030653953552,-0.6288224458694458,-0.721540093421936,-0.3457030653953552,-0.5998910665512085,-0.745408296585083,-0.3457030653953552,-0.5699612498283386,-0.768035888671875,-0.3457030653953552,-0.5390827655792236,-0.789385199546814,-0.3457030653953552,-0.5073070526123047,-0.8094205856323242,-0.3457030653953552,-0.47468695044517517,-0.8281087875366211,-0.3457030653953552,-0.44127681851387024,-0.8454186916351318,-0.3457030653953552,-0.40713217854499817,-0.8613215088844299,-0.3457030653953552,-0.37230995297431946,-0.8757907152175903,-0.3457030653953552,-0.336868017911911,-0.8888022303581238,-0.3457030653953552,-0.30086541175842285,-0.9003344178199768,-0.3457030653953552,-0.26436203718185425,-0.9103681445121765,-0.3457030653953552,-0.2274186760187149,-0.9188866019248962,-0.3457030653953552,-0.19009678065776825,-0.9258756637573242,-0.3457030653953552,-0.15245850384235382,-0.931323766708374,-0.3457030653953552,-0.11456646770238876,-0.9352216720581055,-0.3457030653953552,-0.07648374140262604,-0.9375630617141724,-0.3457030653953552,-0.03827372193336487,-0.9383439421653748,-0.3457030653953552,-1.1491399533867554e-16,-0.9375630617141724,-0.3457030653953552,0.03827372193336487,-0.9352216720581055,-0.3457030653953552,0.07648374140262604,-0.931323766708374,-0.3457030653953552,0.11456646770238876,-0.9258756637573242,-0.3457030653953552,0.15245850384235382,-0.9188866019248962,-0.3457030653953552,0.19009678065776825,-0.9103681445121765,-0.3457030653953552,0.2274186760187149,-0.9003344178199768,-0.3457030653953552,0.26436203718185425,-0.8888022303581238,-0.3457030653953552,0.30086541175842285,-0.8757907152175903,-0.3457030653953552,0.336868017911911,-0.8613215088844299,-0.3457030653953552,0.37230995297431946,-0.8454186916351318,-0.3457030653953552,0.40713217854499817,-0.8281087875366211,-0.3457030653953552,0.44127681851387024,-0.8094205856323242,-0.3457030653953552,0.47468695044517517,-0.789385199546814,-0.3457030653953552,0.5073070526123047,-0.768035888671875,-0.3457030653953552,0.5390827655792236,-0.745408296585083,-0.3457030653953552,0.5699612498283386,-0.721540093421936,-0.3457030653953552,0.5998910665512085,-0.6964709162712097,-0.3457030653953552,0.6288224458694458,-0.6702425479888916,-0.3457030653953552,0.656707227230072,-0.6428986191749573,-0.3457030653953552,0.6834989190101624,-0.6144846081733704,-0.3457030653953552,0.7091530561447144,-0.5850479006767273,-0.3457030653953552,0.7336268424987793,-0.5546374320983887,-0.3457030653953552,0.7568796277046204,-0.5233038067817688,-0.3457030653953552,0.7788726091384888,-0.49109917879104614,-0.3457030653953552,0.7995692491531372,-0.45807719230651855,-0.3457030653953552,0.8189350962638855,-0.42429277300834656,-0.3457030653953552,0.8369379043579102,-0.38980215787887573,-0.3457030653953552,0.8535476922988892,-0.35466277599334717,-0.3457030653953552,0.8687368631362915,-0.31893306970596313,-0.3457030653953552,0.8824800848960876,-0.28267255425453186,-0.3457030653953552,0.8947545289993286,-0.24594153463840485,-0.3457030653953552,0.9055396914482117,-0.20880118012428284,-0.3457030653953552,0.914817750453949,-0.17131328582763672,-0.3457030653953552,0.9225730895996094,-0.13354027271270752,-0.3457030653953552,0.9287929534912109,-0.09554498642683029,-0.3457030653953552,0.9334669709205627,-0.05739067494869232,-0.3457030653953552,0.9365872740745544,-0.01914084516465664,-0.3457030653953552,0.9381487369537354,0.01914084516465664,-0.3457030653953552,0.9381487369537354,0.05739067494869232,-0.3457030653953552,0.9365872740745544,0.09554498642683029,-0.3457030653953552,0.9334669709205627,0.13354027271270752,-0.3457030653953552,0.9287929534912109,0.17131328582763672,-0.3457030653953552,0.9225730895996094,0.20880118012428284,-0.3457030653953552,0.914817750453949,0.24594153463840485,-0.3457030653953552,0.9055396914482117,0.28267255425453186,-0.3457030653953552,0.8947545289993286,0.31893306970596313,-0.3457030653953552,0.8824800848960876,0.35466277599334717,-0.3457030653953552,0.8687368631362915,0.38980215787887573,-0.3457030653953552,0.8535476922988892,0.42429277300834656,-0.3457030653953552,0.8369379043579102,0.45807719230651855,-0.3457030653953552,0.8189350962638855,0.49109917879104614,-0.3457030653953552,0.7995692491531372,0.5233038067817688,-0.3457030653953552,0.7788726091384888,0.5546374320983887,-0.3457030653953552,0.7568796277046204,0.5850479006767273,-0.3457030653953552,0.7336268424987793,0.6144846081733704,-0.3457030653953552,0.7091530561447144,0.6428986191749573,-0.3457030653953552,0.6834989190101624,0.6702425479888916,-0.3457030653953552,0.656707227230072,0.6964709162712097,-0.3457030653953552,0.6288224458694458,0.721540093421936,-0.3457030653953552,0.5998910665512085,0.745408296585083,-0.3457030653953552,0.5699612498283386,0.768035888671875,-0.3457030653953552,0.5390827655792236,0.789385199546814,-0.3457030653953552,0.5073070526123047,0.8094205856323242,-0.3457030653953552,0.47468695044517517,0.8281087875366211,-0.3457030653953552,0.44127681851387024,0.8454186916351318,-0.3457030653953552,0.40713217854499817,0.8613215088844299,-0.3457030653953552,0.37230995297431946,0.8757907152175903,-0.3457030653953552,0.336868017911911,0.8888022303581238,-0.3457030653953552,0.30086541175842285,0.9003344178199768,-0.3457030653953552,0.26436203718185425,0.9103681445121765,-0.3457030653953552,0.2274186760187149,0.9188866019248962,-0.3457030653953552,0.19009678065776825,0.9258756637573242,-0.3457030653953552,0.15245850384235382,0.931323766708374,-0.3457030653953552,0.11456646770238876,0.9352216720581055,-0.3457030653953552,0.07648374140262604,0.9375630617141724,-0.3457030653953552,0.03827372193336487,0.9383439421653748,-0.3457030653953552,2.2982799067735107e-16,0.9603921175003052,-0.2786521017551422,0.0,0.959592878818512,-0.2786521017551422,-0.03917303681373596,0.9571964740753174,-0.2786521017551422,-0.07828087359666824,0.9532069563865662,-0.2786521017551422,-0.11725841462612152,0.9476308822631836,-0.2786521017551422,-0.15604080259799957,0.9404775500297546,-0.2786521017551422,-0.19456346333026886,0.9317589402198792,-0.2786521017551422,-0.23276229202747345,0.9214894771575928,-0.2786521017551422,-0.2705737352371216,0.909686267375946,-0.2786521017551422,-0.30793479084968567,0.8963690400123596,-0.2786521017551422,-0.34478336572647095,0.8815598487854004,-0.2786521017551422,-0.38105806708335876,0.8652833700180054,-0.2786521017551422,-0.41669851541519165,0.8475667834281921,-0.2786521017551422,-0.4516454339027405,0.8284394145011902,-0.2786521017551422,-0.4858406186103821,0.8079332709312439,-0.2786521017551422,-0.519227147102356,0.7860823273658752,-0.2786521017551422,-0.5517495274543762,0.7629230618476868,-0.2786521017551422,-0.5833535194396973,0.7384940385818481,-0.2786521017551422,-0.613986611366272,0.7128357887268066,-0.2786521017551422,-0.6435977816581726,0.685991108417511,-0.2786521017551422,-0.6721377372741699,0.6580047011375427,-0.2786521017551422,-0.6995590329170227,0.6289230585098267,-0.2786521017551422,-0.7258159518241882,0.59879469871521,-0.2786521017551422,-0.7508648037910461,0.5676696300506592,-0.2786521017551422,-0.7746639251708984,0.5355997681617737,-0.2786521017551422,-0.7971736788749695,0.5026384592056274,-0.2786521017551422,-0.8183566331863403,0.4688405692577362,-0.2786521017551422,-0.838177502155304,0.43426233530044556,-0.2786521017551422,-0.8566033244132996,0.39896130561828613,-0.2786521017551422,-0.8736034035682678,0.36299625039100647,-0.2786521017551422,-0.8891494274139404,0.32642701268196106,-0.2786521017551422,-0.9032155871391296,0.2893144488334656,-0.2786521017551422,-0.9157784581184387,0.2517203986644745,-0.2786521017551422,-0.9268170595169067,0.2137073427438736,-0.2786521017551422,-0.9363130927085876,0.17533861100673676,-0.2786521017551422,-0.9442507028579712,0.1366780400276184,-0.2786521017551422,-0.950616717338562,0.09778999537229538,-0.2786521017551422,-0.9554005265235901,0.058739177882671356,-0.2786521017551422,-0.9585941433906555,0.0195905938744545,-0.2786521017551422,-0.9601922631263733,-0.0195905938744545,-0.2786521017551422,-0.9601922631263733,-0.058739177882671356,-0.2786521017551422,-0.9585941433906555,-0.09778999537229538,-0.2786521017551422,-0.9554005265235901,-0.1366780400276184,-0.2786521017551422,-0.950616717338562,-0.17533861100673676,-0.2786521017551422,-0.9442507028579712,-0.2137073427438736,-0.2786521017551422,-0.9363130927085876,-0.2517203986644745,-0.2786521017551422,-0.9268170595169067,-0.2893144488334656,-0.2786521017551422,-0.9157784581184387,-0.32642701268196106,-0.2786521017551422,-0.9032155871391296,-0.36299625039100647,-0.2786521017551422,-0.8891494274139404,-0.39896130561828613,-0.2786521017551422,-0.8736034035682678,-0.43426233530044556,-0.2786521017551422,-0.8566033244132996,-0.4688405692577362,-0.2786521017551422,-0.838177502155304,-0.5026384592056274,-0.2786521017551422,-0.8183566331863403,-0.5355997681617737,-0.2786521017551422,-0.7971736788749695,-0.5676696300506592,-0.2786521017551422,-0.7746639251708984,-0.59879469871521,-0.2786521017551422,-0.7508648037910461,-0.6289230585098267,-0.2786521017551422,-0.7258159518241882,-0.6580047011375427,-0.2786521017551422,-0.6995590329170227,-0.685991108417511,-0.2786521017551422,-0.6721377372741699,-0.7128357887268066,-0.2786521017551422,-0.6435977816581726,-0.7384940385818481,-0.2786521017551422,-0.613986611366272,-0.7629230618476868,-0.2786521017551422,-0.5833535194396973,-0.7860823273658752,-0.2786521017551422,-0.5517495274543762,-0.8079332709312439,-0.2786521017551422,-0.519227147102356,-0.8284394145011902,-0.2786521017551422,-0.4858406186103821,-0.8475667834281921,-0.2786521017551422,-0.4516454339027405,-0.8652833700180054,-0.2786521017551422,-0.41669851541519165,-0.8815598487854004,-0.2786521017551422,-0.38105806708335876,-0.8963690400123596,-0.2786521017551422,-0.34478336572647095,-0.909686267375946,-0.2786521017551422,-0.30793479084968567,-0.9214894771575928,-0.2786521017551422,-0.2705737352371216,-0.9317589402198792,-0.2786521017551422,-0.23276229202747345,-0.9404775500297546,-0.2786521017551422,-0.19456346333026886,-0.9476308822631836,-0.2786521017551422,-0.15604080259799957,-0.9532069563865662,-0.2786521017551422,-0.11725841462612152,-0.9571964740753174,-0.2786521017551422,-0.07828087359666824,-0.959592878818512,-0.2786521017551422,-0.03917303681373596,-0.9603921175003052,-0.2786521017551422,-1.1761411138139563e-16,-0.959592878818512,-0.2786521017551422,0.03917303681373596,-0.9571964740753174,-0.2786521017551422,0.07828087359666824,-0.9532069563865662,-0.2786521017551422,0.11725841462612152,-0.9476308822631836,-0.2786521017551422,0.15604080259799957,-0.9404775500297546,-0.2786521017551422,0.19456346333026886,-0.9317589402198792,-0.2786521017551422,0.23276229202747345,-0.9214894771575928,-0.2786521017551422,0.2705737352371216,-0.909686267375946,-0.2786521017551422,0.30793479084968567,-0.8963690400123596,-0.2786521017551422,0.34478336572647095,-0.8815598487854004,-0.2786521017551422,0.38105806708335876,-0.8652833700180054,-0.2786521017551422,0.41669851541519165,-0.8475667834281921,-0.2786521017551422,0.4516454339027405,-0.8284394145011902,-0.2786521017551422,0.4858406186103821,-0.8079332709312439,-0.2786521017551422,0.519227147102356,-0.7860823273658752,-0.2786521017551422,0.5517495274543762,-0.7629230618476868,-0.2786521017551422,0.5833535194396973,-0.7384940385818481,-0.2786521017551422,0.613986611366272,-0.7128357887268066,-0.2786521017551422,0.6435977816581726,-0.685991108417511,-0.2786521017551422,0.6721377372741699,-0.6580047011375427,-0.2786521017551422,0.6995590329170227,-0.6289230585098267,-0.2786521017551422,0.7258159518241882,-0.59879469871521,-0.2786521017551422,0.7508648037910461,-0.5676696300506592,-0.2786521017551422,0.7746639251708984,-0.5355997681617737,-0.2786521017551422,0.7971736788749695,-0.5026384592056274,-0.2786521017551422,0.8183566331863403,-0.4688405692577362,-0.2786521017551422,0.838177502155304,-0.43426233530044556,-0.2786521017551422,0.8566033244132996,-0.39896130561828613,-0.2786521017551422,0.8736034035682678,-0.36299625039100647,-0.2786521017551422,0.8891494274139404,-0.32642701268196106,-0.2786521017551422,0.9032155871391296,-0.2893144488334656,-0.2786521017551422,0.9157784581184387,-0.2517203986644745,-0.2786521017551422,0.9268170595169067,-0.2137073427438736,-0.2786521017551422,0.9363130927085876,-0.17533861100673676,-0.2786521017551422,0.9442507028579712,-0.1366780400276184,-0.2786521017551422,0.950616717338562,-0.09778999537229538,-0.2786521017551422,0.9554005265235901,-0.058739177882671356,-0.2786521017551422,0.9585941433906555,-0.0195905938744545,-0.2786521017551422,0.9601922631263733,0.0195905938744545,-0.2786521017551422,0.9601922631263733,0.058739177882671356,-0.2786521017551422,0.9585941433906555,0.09778999537229538,-0.2786521017551422,0.9554005265235901,0.1366780400276184,-0.2786521017551422,0.950616717338562,0.17533861100673676,-0.2786521017551422,0.9442507028579712,0.2137073427438736,-0.2786521017551422,0.9363130927085876,0.2517203986644745,-0.2786521017551422,0.9268170595169067,0.2893144488334656,-0.2786521017551422,0.9157784581184387,0.32642701268196106,-0.2786521017551422,0.9032155871391296,0.36299625039100647,-0.2786521017551422,0.8891494274139404,0.39896130561828613,-0.2786521017551422,0.8736034035682678,0.43426233530044556,-0.2786521017551422,0.8566033244132996,0.4688405692577362,-0.2786521017551422,0.838177502155304,0.5026384592056274,-0.2786521017551422,0.8183566331863403,0.5355997681617737,-0.2786521017551422,0.7971736788749695,0.5676696300506592,-0.2786521017551422,0.7746639251708984,0.59879469871521,-0.2786521017551422,0.7508648037910461,0.6289230585098267,-0.2786521017551422,0.7258159518241882,0.6580047011375427,-0.2786521017551422,0.6995590329170227,0.685991108417511,-0.2786521017551422,0.6721377372741699,0.7128357887268066,-0.2786521017551422,0.6435977816581726,0.7384940385818481,-0.2786521017551422,0.613986611366272,0.7629230618476868,-0.2786521017551422,0.5833535194396973,0.7860823273658752,-0.2786521017551422,0.5517495274543762,0.8079332709312439,-0.2786521017551422,0.519227147102356,0.8284394145011902,-0.2786521017551422,0.4858406186103821,0.8475667834281921,-0.2786521017551422,0.4516454339027405,0.8652833700180054,-0.2786521017551422,0.41669851541519165,0.8815598487854004,-0.2786521017551422,0.38105806708335876,0.8963690400123596,-0.2786521017551422,0.34478336572647095,0.909686267375946,-0.2786521017551422,0.30793479084968567,0.9214894771575928,-0.2786521017551422,0.2705737352371216,0.9317589402198792,-0.2786521017551422,0.23276229202747345,0.9404775500297546,-0.2786521017551422,0.19456346333026886,0.9476308822631836,-0.2786521017551422,0.15604080259799957,0.9532069563865662,-0.2786521017551422,0.11725841462612152,0.9571964740753174,-0.2786521017551422,0.07828087359666824,0.959592878818512,-0.2786521017551422,0.03917303681373596,0.9603921175003052,-0.2786521017551422,2.3522822276279126e-16,0.9776556491851807,-0.21021294593811035,0.0,0.9768420457839966,-0.21021294593811035,-0.03987719118595123,0.9744025468826294,-0.21021294593811035,-0.07968801259994507,0.9703413248062134,-0.21021294593811035,-0.11936619877815247,0.9646649956703186,-0.21021294593811035,-0.1588457077741623,0.9573830962181091,-0.21021294593811035,-0.19806084036827087,0.9485077261924744,-0.21021294593811035,-0.23694631457328796,0.9380537271499634,-0.21021294593811035,-0.2754374146461487,0.9260383248329163,-0.21021294593811035,-0.31347009539604187,0.9124817252159119,-0.21021294593811035,-0.3509809970855713,0.8974063396453857,-0.21021294593811035,-0.3879077732563019,0.8808372616767883,-0.21021294593811035,-0.42418888211250305,0.8628022074699402,-0.21021294593811035,-0.4597639739513397,0.8433310389518738,-0.21021294593811035,-0.49457383155822754,0.8224562406539917,-0.21021294593811035,-0.5285605192184448,0.800212562084198,-0.21021294593811035,-0.5616675019264221,0.7766370177268982,-0.21021294593811035,-0.5938395857810974,0.7517688274383545,-0.21021294593811035,-0.625023365020752,0.725649356842041,-0.21021294593811035,-0.6551668047904968,0.6983221769332886,-0.21021294593811035,-0.6842197775840759,0.6698326468467712,-0.21021294593811035,-0.7121339440345764,0.640228271484375,-0.21021294593811035,-0.7388628125190735,0.6095582842826843,-0.21021294593811035,-0.7643619179725647,0.5778737664222717,-0.21021294593811035,-0.7885888814926147,0.5452274680137634,-0.21021294593811035,-0.8115032315254211,0.511673629283905,-0.21021294593811035,-0.833066999912262,0.4772682189941406,-0.21021294593811035,-0.8532441258430481,0.4420683979988098,-0.21021294593811035,-0.8720011711120605,0.4061328172683716,-0.21021294593811035,-0.8893068432807922,0.36952126026153564,-0.21021294593811035,-0.9051323533058167,0.33229467272758484,-0.21021294593811035,-0.9194513559341431,0.29451504349708557,-0.21021294593811035,-0.9322400093078613,0.2562451958656311,-0.21021294593811035,-0.9434770345687866,0.21754884719848633,-0.21021294593811035,-0.9531437754631042,0.17849041521549225,-0.21021294593811035,-0.9612240791320801,0.13913489878177643,-0.21021294593811035,-0.9677044749259949,0.09954781830310822,-0.21021294593811035,-0.9725742936134338,0.059795040637254715,-0.21021294593811035,-0.975825309753418,0.019942745566368103,-0.21021294593811035,-0.9774522185325623,-0.019942745566368103,-0.21021294593811035,-0.9774522185325623,-0.059795040637254715,-0.21021294593811035,-0.975825309753418,-0.09954781830310822,-0.21021294593811035,-0.9725742936134338,-0.13913489878177643,-0.21021294593811035,-0.9677044749259949,-0.17849041521549225,-0.21021294593811035,-0.9612240791320801,-0.21754884719848633,-0.21021294593811035,-0.9531437754631042,-0.2562451958656311,-0.21021294593811035,-0.9434770345687866,-0.29451504349708557,-0.21021294593811035,-0.9322400093078613,-0.33229467272758484,-0.21021294593811035,-0.9194513559341431,-0.36952126026153564,-0.21021294593811035,-0.9051323533058167,-0.4061328172683716,-0.21021294593811035,-0.8893068432807922,-0.4420683979988098,-0.21021294593811035,-0.8720011711120605,-0.4772682189941406,-0.21021294593811035,-0.8532441258430481,-0.511673629283905,-0.21021294593811035,-0.833066999912262,-0.5452274680137634,-0.21021294593811035,-0.8115032315254211,-0.5778737664222717,-0.21021294593811035,-0.7885888814926147,-0.6095582842826843,-0.21021294593811035,-0.7643619179725647,-0.640228271484375,-0.21021294593811035,-0.7388628125190735,-0.6698326468467712,-0.21021294593811035,-0.7121339440345764,-0.6983221769332886,-0.21021294593811035,-0.6842197775840759,-0.725649356842041,-0.21021294593811035,-0.6551668047904968,-0.7517688274383545,-0.21021294593811035,-0.625023365020752,-0.7766370177268982,-0.21021294593811035,-0.5938395857810974,-0.800212562084198,-0.21021294593811035,-0.5616675019264221,-0.8224562406539917,-0.21021294593811035,-0.5285605192184448,-0.8433310389518738,-0.21021294593811035,-0.49457383155822754,-0.8628022074699402,-0.21021294593811035,-0.4597639739513397,-0.8808372616767883,-0.21021294593811035,-0.42418888211250305,-0.8974063396453857,-0.21021294593811035,-0.3879077732563019,-0.9124817252159119,-0.21021294593811035,-0.3509809970855713,-0.9260383248329163,-0.21021294593811035,-0.31347009539604187,-0.9380537271499634,-0.21021294593811035,-0.2754374146461487,-0.9485077261924744,-0.21021294593811035,-0.23694631457328796,-0.9573830962181091,-0.21021294593811035,-0.19806084036827087,-0.9646649956703186,-0.21021294593811035,-0.1588457077741623,-0.9703413248062134,-0.21021294593811035,-0.11936619877815247,-0.9744025468826294,-0.21021294593811035,-0.07968801259994507,-0.9768420457839966,-0.21021294593811035,-0.03987719118595123,-0.9776556491851807,-0.21021294593811035,-1.1972827914796276e-16,-0.9768420457839966,-0.21021294593811035,0.03987719118595123,-0.9744025468826294,-0.21021294593811035,0.07968801259994507,-0.9703413248062134,-0.21021294593811035,0.11936619877815247,-0.9646649956703186,-0.21021294593811035,0.1588457077741623,-0.9573830962181091,-0.21021294593811035,0.19806084036827087,-0.9485077261924744,-0.21021294593811035,0.23694631457328796,-0.9380537271499634,-0.21021294593811035,0.2754374146461487,-0.9260383248329163,-0.21021294593811035,0.31347009539604187,-0.9124817252159119,-0.21021294593811035,0.3509809970855713,-0.8974063396453857,-0.21021294593811035,0.3879077732563019,-0.8808372616767883,-0.21021294593811035,0.42418888211250305,-0.8628022074699402,-0.21021294593811035,0.4597639739513397,-0.8433310389518738,-0.21021294593811035,0.49457383155822754,-0.8224562406539917,-0.21021294593811035,0.5285605192184448,-0.800212562084198,-0.21021294593811035,0.5616675019264221,-0.7766370177268982,-0.21021294593811035,0.5938395857810974,-0.7517688274383545,-0.21021294593811035,0.625023365020752,-0.725649356842041,-0.21021294593811035,0.6551668047904968,-0.6983221769332886,-0.21021294593811035,0.6842197775840759,-0.6698326468467712,-0.21021294593811035,0.7121339440345764,-0.640228271484375,-0.21021294593811035,0.7388628125190735,-0.6095582842826843,-0.21021294593811035,0.7643619179725647,-0.5778737664222717,-0.21021294593811035,0.7885888814926147,-0.5452274680137634,-0.21021294593811035,0.8115032315254211,-0.511673629283905,-0.21021294593811035,0.833066999912262,-0.4772682189941406,-0.21021294593811035,0.8532441258430481,-0.4420683979988098,-0.21021294593811035,0.8720011711120605,-0.4061328172683716,-0.21021294593811035,0.8893068432807922,-0.36952126026153564,-0.21021294593811035,0.9051323533058167,-0.33229467272758484,-0.21021294593811035,0.9194513559341431,-0.29451504349708557,-0.21021294593811035,0.9322400093078613,-0.2562451958656311,-0.21021294593811035,0.9434770345687866,-0.21754884719848633,-0.21021294593811035,0.9531437754631042,-0.17849041521549225,-0.21021294593811035,0.9612240791320801,-0.13913489878177643,-0.21021294593811035,0.9677044749259949,-0.09954781830310822,-0.21021294593811035,0.9725742936134338,-0.059795040637254715,-0.21021294593811035,0.975825309753418,-0.019942745566368103,-0.21021294593811035,0.9774522185325623,0.019942745566368103,-0.21021294593811035,0.9774522185325623,0.059795040637254715,-0.21021294593811035,0.975825309753418,0.09954781830310822,-0.21021294593811035,0.9725742936134338,0.13913489878177643,-0.21021294593811035,0.9677044749259949,0.17849041521549225,-0.21021294593811035,0.9612240791320801,0.21754884719848633,-0.21021294593811035,0.9531437754631042,0.2562451958656311,-0.21021294593811035,0.9434770345687866,0.29451504349708557,-0.21021294593811035,0.9322400093078613,0.33229467272758484,-0.21021294593811035,0.9194513559341431,0.36952126026153564,-0.21021294593811035,0.9051323533058167,0.4061328172683716,-0.21021294593811035,0.8893068432807922,0.4420683979988098,-0.21021294593811035,0.8720011711120605,0.4772682189941406,-0.21021294593811035,0.8532441258430481,0.511673629283905,-0.21021294593811035,0.833066999912262,0.5452274680137634,-0.21021294593811035,0.8115032315254211,0.5778737664222717,-0.21021294593811035,0.7885888814926147,0.6095582842826843,-0.21021294593811035,0.7643619179725647,0.640228271484375,-0.21021294593811035,0.7388628125190735,0.6698326468467712,-0.21021294593811035,0.7121339440345764,0.6983221769332886,-0.21021294593811035,0.6842197775840759,0.725649356842041,-0.21021294593811035,0.6551668047904968,0.7517688274383545,-0.21021294593811035,0.625023365020752,0.7766370177268982,-0.21021294593811035,0.5938395857810974,0.800212562084198,-0.21021294593811035,0.5616675019264221,0.8224562406539917,-0.21021294593811035,0.5285605192184448,0.8433310389518738,-0.21021294593811035,0.49457383155822754,0.8628022074699402,-0.21021294593811035,0.4597639739513397,0.8808372616767883,-0.21021294593811035,0.42418888211250305,0.8974063396453857,-0.21021294593811035,0.3879077732563019,0.9124817252159119,-0.21021294593811035,0.3509809970855713,0.9260383248329163,-0.21021294593811035,0.31347009539604187,0.9380537271499634,-0.21021294593811035,0.2754374146461487,0.9485077261924744,-0.21021294593811035,0.23694631457328796,0.9573830962181091,-0.21021294593811035,0.19806084036827087,0.9646649956703186,-0.21021294593811035,0.1588457077741623,0.9703413248062134,-0.21021294593811035,0.11936619877815247,0.9744025468826294,-0.21021294593811035,0.07968801259994507,0.9768420457839966,-0.21021294593811035,0.03987719118595123,0.9776556491851807,-0.21021294593811035,2.3945655829592553e-16,0.9900485277175903,-0.1407264918088913,0.0,0.9892246127128601,-0.1407264918088913,-0.04038267955183983,0.9867541790008545,-0.1407264918088913,-0.08069814741611481,0.9826414585113525,-0.1407264918088913,-0.12087929993867874,0.9768931865692139,-0.1407264918088913,-0.16085925698280334,0.9695190191268921,-0.1407264918088913,-0.2005714774131775,0.9605311751365662,-0.1407264918088913,-0.23994988203048706,0.9499446153640747,-0.1407264918088913,-0.2789289057254791,0.9377769231796265,-0.1407264918088913,-0.3174436688423157,0.9240484237670898,-0.1407264918088913,-0.35543009638786316,0.9087819457054138,-0.1407264918088913,-0.3928249180316925,0.8920028805732727,-0.1407264918088913,-0.4295659363269806,0.8737391829490662,-0.1407264918088913,-0.46559199690818787,0.8540211915969849,-0.1407264918088913,-0.5008431077003479,0.8328818082809448,-0.1407264918088913,-0.5352606177330017,0.8103561401367188,-0.1407264918088913,-0.5687872767448425,0.7864817380905151,-0.1407264918088913,-0.601367175579071,0.7612982988357544,-0.1407264918088913,-0.6329461932182312,0.7348477840423584,-0.1407264918088913,-0.6634717583656311,0.7071741819381714,-0.1407264918088913,-0.6928930282592773,0.67832350730896,-0.1407264918088913,-0.7211610078811646,0.6483439207077026,-0.1407264918088913,-0.7482287287712097,0.6172851324081421,-0.1407264918088913,-0.7740510702133179,0.5851989984512329,-0.1407264918088913,-0.7985851168632507,0.5521388053894043,-0.1407264918088913,-0.8217899799346924,0.5181596875190735,-0.1407264918088913,-0.8436270356178284,0.48331812024116516,-0.1407264918088913,-0.8640599846839905,0.44767212867736816,-0.1407264918088913,-0.883054792881012,0.411281019449234,-0.1407264918088913,-0.9005798101425171,0.3742053806781769,-0.1407264918088913,-0.9166058897972107,0.3365069031715393,-0.1407264918088913,-0.9311063885688782,0.2982483506202698,-0.1407264918088913,-0.9440571665763855,0.2594933807849884,-0.1407264918088913,-0.955436646938324,0.22030651569366455,-0.1407264918088913,-0.9652259349822998,0.1807529777288437,-0.1407264918088913,-0.9734086394309998,0.14089860022068024,-0.1407264918088913,-0.9799712300300598,0.10080969333648682,-0.1407264918088913,-0.9849027395248413,0.06055301055312157,-0.1407264918088913,-0.98819500207901,0.02019554190337658,-0.1407264918088913,-0.9898425340652466,-0.02019554190337658,-0.1407264918088913,-0.9898425340652466,-0.06055301055312157,-0.1407264918088913,-0.98819500207901,-0.10080969333648682,-0.1407264918088913,-0.9849027395248413,-0.14089860022068024,-0.1407264918088913,-0.9799712300300598,-0.1807529777288437,-0.1407264918088913,-0.9734086394309998,-0.22030651569366455,-0.1407264918088913,-0.9652259349822998,-0.2594933807849884,-0.1407264918088913,-0.955436646938324,-0.2982483506202698,-0.1407264918088913,-0.9440571665763855,-0.3365069031715393,-0.1407264918088913,-0.9311063885688782,-0.3742053806781769,-0.1407264918088913,-0.9166058897972107,-0.411281019449234,-0.1407264918088913,-0.9005798101425171,-0.44767212867736816,-0.1407264918088913,-0.883054792881012,-0.48331812024116516,-0.1407264918088913,-0.8640599846839905,-0.5181596875190735,-0.1407264918088913,-0.8436270356178284,-0.5521388053894043,-0.1407264918088913,-0.8217899799346924,-0.5851989984512329,-0.1407264918088913,-0.7985851168632507,-0.6172851324081421,-0.1407264918088913,-0.7740510702133179,-0.6483439207077026,-0.1407264918088913,-0.7482287287712097,-0.67832350730896,-0.1407264918088913,-0.7211610078811646,-0.7071741819381714,-0.1407264918088913,-0.6928930282592773,-0.7348477840423584,-0.1407264918088913,-0.6634717583656311,-0.7612982988357544,-0.1407264918088913,-0.6329461932182312,-0.7864817380905151,-0.1407264918088913,-0.601367175579071,-0.8103561401367188,-0.1407264918088913,-0.5687872767448425,-0.8328818082809448,-0.1407264918088913,-0.5352606177330017,-0.8540211915969849,-0.1407264918088913,-0.5008431077003479,-0.8737391829490662,-0.1407264918088913,-0.46559199690818787,-0.8920028805732727,-0.1407264918088913,-0.4295659363269806,-0.9087819457054138,-0.1407264918088913,-0.3928249180316925,-0.9240484237670898,-0.1407264918088913,-0.35543009638786316,-0.9377769231796265,-0.1407264918088913,-0.3174436688423157,-0.9499446153640747,-0.1407264918088913,-0.2789289057254791,-0.9605311751365662,-0.1407264918088913,-0.23994988203048706,-0.9695190191268921,-0.1407264918088913,-0.2005714774131775,-0.9768931865692139,-0.1407264918088913,-0.16085925698280334,-0.9826414585113525,-0.1407264918088913,-0.12087929993867874,-0.9867541790008545,-0.1407264918088913,-0.08069814741611481,-0.9892246127128601,-0.1407264918088913,-0.04038267955183983,-0.9900485277175903,-0.1407264918088913,-1.2124597690098526e-16,-0.9892246127128601,-0.1407264918088913,0.04038267955183983,-0.9867541790008545,-0.1407264918088913,0.08069814741611481,-0.9826414585113525,-0.1407264918088913,0.12087929993867874,-0.9768931865692139,-0.1407264918088913,0.16085925698280334,-0.9695190191268921,-0.1407264918088913,0.2005714774131775,-0.9605311751365662,-0.1407264918088913,0.23994988203048706,-0.9499446153640747,-0.1407264918088913,0.2789289057254791,-0.9377769231796265,-0.1407264918088913,0.3174436688423157,-0.9240484237670898,-0.1407264918088913,0.35543009638786316,-0.9087819457054138,-0.1407264918088913,0.3928249180316925,-0.8920028805732727,-0.1407264918088913,0.4295659363269806,-0.8737391829490662,-0.1407264918088913,0.46559199690818787,-0.8540211915969849,-0.1407264918088913,0.5008431077003479,-0.8328818082809448,-0.1407264918088913,0.5352606177330017,-0.8103561401367188,-0.1407264918088913,0.5687872767448425,-0.7864817380905151,-0.1407264918088913,0.601367175579071,-0.7612982988357544,-0.1407264918088913,0.6329461932182312,-0.7348477840423584,-0.1407264918088913,0.6634717583656311,-0.7071741819381714,-0.1407264918088913,0.6928930282592773,-0.67832350730896,-0.1407264918088913,0.7211610078811646,-0.6483439207077026,-0.1407264918088913,0.7482287287712097,-0.6172851324081421,-0.1407264918088913,0.7740510702133179,-0.5851989984512329,-0.1407264918088913,0.7985851168632507,-0.5521388053894043,-0.1407264918088913,0.8217899799346924,-0.5181596875190735,-0.1407264918088913,0.8436270356178284,-0.48331812024116516,-0.1407264918088913,0.8640599846839905,-0.44767212867736816,-0.1407264918088913,0.883054792881012,-0.411281019449234,-0.1407264918088913,0.9005798101425171,-0.3742053806781769,-0.1407264918088913,0.9166058897972107,-0.3365069031715393,-0.1407264918088913,0.9311063885688782,-0.2982483506202698,-0.1407264918088913,0.9440571665763855,-0.2594933807849884,-0.1407264918088913,0.955436646938324,-0.22030651569366455,-0.1407264918088913,0.9652259349822998,-0.1807529777288437,-0.1407264918088913,0.9734086394309998,-0.14089860022068024,-0.1407264918088913,0.9799712300300598,-0.10080969333648682,-0.1407264918088913,0.9849027395248413,-0.06055301055312157,-0.1407264918088913,0.98819500207901,-0.02019554190337658,-0.1407264918088913,0.9898425340652466,0.02019554190337658,-0.1407264918088913,0.9898425340652466,0.06055301055312157,-0.1407264918088913,0.98819500207901,0.10080969333648682,-0.1407264918088913,0.9849027395248413,0.14089860022068024,-0.1407264918088913,0.9799712300300598,0.1807529777288437,-0.1407264918088913,0.9734086394309998,0.22030651569366455,-0.1407264918088913,0.9652259349822998,0.2594933807849884,-0.1407264918088913,0.955436646938324,0.2982483506202698,-0.1407264918088913,0.9440571665763855,0.3365069031715393,-0.1407264918088913,0.9311063885688782,0.3742053806781769,-0.1407264918088913,0.9166058897972107,0.411281019449234,-0.1407264918088913,0.9005798101425171,0.44767212867736816,-0.1407264918088913,0.883054792881012,0.48331812024116516,-0.1407264918088913,0.8640599846839905,0.5181596875190735,-0.1407264918088913,0.8436270356178284,0.5521388053894043,-0.1407264918088913,0.8217899799346924,0.5851989984512329,-0.1407264918088913,0.7985851168632507,0.6172851324081421,-0.1407264918088913,0.7740510702133179,0.6483439207077026,-0.1407264918088913,0.7482287287712097,0.67832350730896,-0.1407264918088913,0.7211610078811646,0.7071741819381714,-0.1407264918088913,0.6928930282592773,0.7348477840423584,-0.1407264918088913,0.6634717583656311,0.7612982988357544,-0.1407264918088913,0.6329461932182312,0.7864817380905151,-0.1407264918088913,0.601367175579071,0.8103561401367188,-0.1407264918088913,0.5687872767448425,0.8328818082809448,-0.1407264918088913,0.5352606177330017,0.8540211915969849,-0.1407264918088913,0.5008431077003479,0.8737391829490662,-0.1407264918088913,0.46559199690818787,0.8920028805732727,-0.1407264918088913,0.4295659363269806,0.9087819457054138,-0.1407264918088913,0.3928249180316925,0.9240484237670898,-0.1407264918088913,0.35543009638786316,0.9377769231796265,-0.1407264918088913,0.3174436688423157,0.9499446153640747,-0.1407264918088913,0.2789289057254791,0.9605311751365662,-0.1407264918088913,0.23994988203048706,0.9695190191268921,-0.1407264918088913,0.2005714774131775,0.9768931865692139,-0.1407264918088913,0.16085925698280334,0.9826414585113525,-0.1407264918088913,0.12087929993867874,0.9867541790008545,-0.1407264918088913,0.08069814741611481,0.9892246127128601,-0.1407264918088913,0.04038267955183983,0.9900485277175903,-0.1407264918088913,2.424919538019705e-16,0.9975090026855469,-0.0705389603972435,0.0,0.99667888879776,-0.0705389603972435,-0.04068698361515999,0.9941899180412292,-0.0705389603972435,-0.08130624890327454,0.9900461435317993,-0.0705389603972435,-0.12179018557071686,0.9842545986175537,-0.0705389603972435,-0.16207140684127808,0.9768248200416565,-0.0705389603972435,-0.20208288729190826,0.9677692651748657,-0.0705389603972435,-0.24175801873207092,0.95710289478302,-0.0705389603972435,-0.2810307741165161,0.9448435306549072,-0.0705389603972435,-0.3198357820510864,0.9310116171836853,-0.0705389603972435,-0.35810843110084534,0.9156301021575928,-0.0705389603972435,-0.39578506350517273,0.8987245559692383,-0.0705389603972435,-0.4328029453754425,0.8803232312202454,-0.0705389603972435,-0.4691004753112793,0.8604567050933838,-0.0705389603972435,-0.5046172142028809,0.8391579985618591,-0.0705389603972435,-0.5392941236495972,0.8164625763893127,-0.0705389603972435,-0.5730733871459961,0.792408287525177,-0.0705389603972435,-0.6058987975120544,0.7670350670814514,-0.0705389603972435,-0.6377158164978027,0.7403852343559265,-0.0705389603972435,-0.6684713363647461,0.7125030755996704,-0.0705389603972435,-0.6981143355369568,0.6834350228309631,-0.0705389603972435,-0.7265953421592712,0.6532294750213623,-0.0705389603972435,-0.7538670301437378,0.6219366788864136,-0.0705389603972435,-0.7798839807510376,0.5896087884902954,-0.0705389603972435,-0.804602861404419,0.5562995076179504,-0.0705389603972435,-0.8279826045036316,0.5220642685890198,-0.0705389603972435,-0.8499841690063477,0.48696017265319824,-0.0705389603972435,-0.8705710768699646,0.45104557275772095,-0.0705389603972435,-0.8897090554237366,0.4143802225589752,-0.0705389603972435,-0.9073660969734192,0.37702518701553345,-0.0705389603972435,-0.9235129952430725,0.33904263377189636,-0.0705389603972435,-0.9381227493286133,0.30049580335617065,-0.0705389603972435,-0.9511711597442627,0.26144880056381226,-0.0705389603972435,-0.9626363515853882,0.22196663916110992,-0.0705389603972435,-0.9724993705749512,0.18211504817008972,-0.0705389603972435,-0.9807437658309937,0.14196033775806427,-0.0705389603972435,-0.9873558282852173,0.10156934708356857,-0.0705389603972435,-0.9923245310783386,0.06100931018590927,-0.0705389603972435,-0.9956415891647339,0.020347725600004196,-0.0705389603972435,-0.997301459312439,-0.020347725600004196,-0.0705389603972435,-0.997301459312439,-0.06100931018590927,-0.0705389603972435,-0.9956415891647339,-0.10156934708356857,-0.0705389603972435,-0.9923245310783386,-0.14196033775806427,-0.0705389603972435,-0.9873558282852173,-0.18211504817008972,-0.0705389603972435,-0.9807437658309937,-0.22196663916110992,-0.0705389603972435,-0.9724993705749512,-0.26144880056381226,-0.0705389603972435,-0.9626363515853882,-0.30049580335617065,-0.0705389603972435,-0.9511711597442627,-0.33904263377189636,-0.0705389603972435,-0.9381227493286133,-0.37702518701553345,-0.0705389603972435,-0.9235129952430725,-0.4143802225589752,-0.0705389603972435,-0.9073660969734192,-0.45104557275772095,-0.0705389603972435,-0.8897090554237366,-0.48696017265319824,-0.0705389603972435,-0.8705710768699646,-0.5220642685890198,-0.0705389603972435,-0.8499841690063477,-0.5562995076179504,-0.0705389603972435,-0.8279826045036316,-0.5896087884902954,-0.0705389603972435,-0.804602861404419,-0.6219366788864136,-0.0705389603972435,-0.7798839807510376,-0.6532294750213623,-0.0705389603972435,-0.7538670301437378,-0.6834350228309631,-0.0705389603972435,-0.7265953421592712,-0.7125030755996704,-0.0705389603972435,-0.6981143355369568,-0.7403852343559265,-0.0705389603972435,-0.6684713363647461,-0.7670350670814514,-0.0705389603972435,-0.6377158164978027,-0.792408287525177,-0.0705389603972435,-0.6058987975120544,-0.8164625763893127,-0.0705389603972435,-0.5730733871459961,-0.8391579985618591,-0.0705389603972435,-0.5392941236495972,-0.8604567050933838,-0.0705389603972435,-0.5046172142028809,-0.8803232312202454,-0.0705389603972435,-0.4691004753112793,-0.8987245559692383,-0.0705389603972435,-0.4328029453754425,-0.9156301021575928,-0.0705389603972435,-0.39578506350517273,-0.9310116171836853,-0.0705389603972435,-0.35810843110084534,-0.9448435306549072,-0.0705389603972435,-0.3198357820510864,-0.95710289478302,-0.0705389603972435,-0.2810307741165161,-0.9677692651748657,-0.0705389603972435,-0.24175801873207092,-0.9768248200416565,-0.0705389603972435,-0.20208288729190826,-0.9842545986175537,-0.0705389603972435,-0.16207140684127808,-0.9900461435317993,-0.0705389603972435,-0.12179018557071686,-0.9941899180412292,-0.0705389603972435,-0.08130624890327454,-0.99667888879776,-0.0705389603972435,-0.04068698361515999,-0.9975090026855469,-0.0705389603972435,-1.2215962104860723e-16,-0.99667888879776,-0.0705389603972435,0.04068698361515999,-0.9941899180412292,-0.0705389603972435,0.08130624890327454,-0.9900461435317993,-0.0705389603972435,0.12179018557071686,-0.9842545986175537,-0.0705389603972435,0.16207140684127808,-0.9768248200416565,-0.0705389603972435,0.20208288729190826,-0.9677692651748657,-0.0705389603972435,0.24175801873207092,-0.95710289478302,-0.0705389603972435,0.2810307741165161,-0.9448435306549072,-0.0705389603972435,0.3198357820510864,-0.9310116171836853,-0.0705389603972435,0.35810843110084534,-0.9156301021575928,-0.0705389603972435,0.39578506350517273,-0.8987245559692383,-0.0705389603972435,0.4328029453754425,-0.8803232312202454,-0.0705389603972435,0.4691004753112793,-0.8604567050933838,-0.0705389603972435,0.5046172142028809,-0.8391579985618591,-0.0705389603972435,0.5392941236495972,-0.8164625763893127,-0.0705389603972435,0.5730733871459961,-0.792408287525177,-0.0705389603972435,0.6058987975120544,-0.7670350670814514,-0.0705389603972435,0.6377158164978027,-0.7403852343559265,-0.0705389603972435,0.6684713363647461,-0.7125030755996704,-0.0705389603972435,0.6981143355369568,-0.6834350228309631,-0.0705389603972435,0.7265953421592712,-0.6532294750213623,-0.0705389603972435,0.7538670301437378,-0.6219366788864136,-0.0705389603972435,0.7798839807510376,-0.5896087884902954,-0.0705389603972435,0.804602861404419,-0.5562995076179504,-0.0705389603972435,0.8279826045036316,-0.5220642685890198,-0.0705389603972435,0.8499841690063477,-0.48696017265319824,-0.0705389603972435,0.8705710768699646,-0.45104557275772095,-0.0705389603972435,0.8897090554237366,-0.4143802225589752,-0.0705389603972435,0.9073660969734192,-0.37702518701553345,-0.0705389603972435,0.9235129952430725,-0.33904263377189636,-0.0705389603972435,0.9381227493286133,-0.30049580335617065,-0.0705389603972435,0.9511711597442627,-0.26144880056381226,-0.0705389603972435,0.9626363515853882,-0.22196663916110992,-0.0705389603972435,0.9724993705749512,-0.18211504817008972,-0.0705389603972435,0.9807437658309937,-0.14196033775806427,-0.0705389603972435,0.9873558282852173,-0.10156934708356857,-0.0705389603972435,0.9923245310783386,-0.06100931018590927,-0.0705389603972435,0.9956415891647339,-0.020347725600004196,-0.0705389603972435,0.997301459312439,0.020347725600004196,-0.0705389603972435,0.997301459312439,0.06100931018590927,-0.0705389603972435,0.9956415891647339,0.10156934708356857,-0.0705389603972435,0.9923245310783386,0.14196033775806427,-0.0705389603972435,0.9873558282852173,0.18211504817008972,-0.0705389603972435,0.9807437658309937,0.22196663916110992,-0.0705389603972435,0.9724993705749512,0.26144880056381226,-0.0705389603972435,0.9626363515853882,0.30049580335617065,-0.0705389603972435,0.9511711597442627,0.33904263377189636,-0.0705389603972435,0.9381227493286133,0.37702518701553345,-0.0705389603972435,0.9235129952430725,0.4143802225589752,-0.0705389603972435,0.9073660969734192,0.45104557275772095,-0.0705389603972435,0.8897090554237366,0.48696017265319824,-0.0705389603972435,0.8705710768699646,0.5220642685890198,-0.0705389603972435,0.8499841690063477,0.5562995076179504,-0.0705389603972435,0.8279826045036316,0.5896087884902954,-0.0705389603972435,0.804602861404419,0.6219366788864136,-0.0705389603972435,0.7798839807510376,0.6532294750213623,-0.0705389603972435,0.7538670301437378,0.6834350228309631,-0.0705389603972435,0.7265953421592712,0.7125030755996704,-0.0705389603972435,0.6981143355369568,0.7403852343559265,-0.0705389603972435,0.6684713363647461,0.7670350670814514,-0.0705389603972435,0.6377158164978027,0.792408287525177,-0.0705389603972435,0.6058987975120544,0.8164625763893127,-0.0705389603972435,0.5730733871459961,0.8391579985618591,-0.0705389603972435,0.5392941236495972,0.8604567050933838,-0.0705389603972435,0.5046172142028809,0.8803232312202454,-0.0705389603972435,0.4691004753112793,0.8987245559692383,-0.0705389603972435,0.4328029453754425,0.9156301021575928,-0.0705389603972435,0.39578506350517273,0.9310116171836853,-0.0705389603972435,0.35810843110084534,0.9448435306549072,-0.0705389603972435,0.3198357820510864,0.95710289478302,-0.0705389603972435,0.2810307741165161,0.9677692651748657,-0.0705389603972435,0.24175801873207092,0.9768248200416565,-0.0705389603972435,0.20208288729190826,0.9842545986175537,-0.0705389603972435,0.16207140684127808,0.9900461435317993,-0.0705389603972435,0.12179018557071686,0.9941899180412292,-0.0705389603972435,0.08130624890327454,0.99667888879776,-0.0705389603972435,0.04068698361515999,0.9975090026855469,-0.0705389603972435,2.4431924209721446e-16,1.0,-2.4492937051703357e-16,0.0,0.999167799949646,-2.4492937051703357e-16,-0.04078858718276024,0.9966725707054138,-2.4492937051703357e-16,-0.08150928467512131,0.9925184845924377,-2.4492937051703357e-16,-0.12209431827068329,0.9867124557495117,-2.4492937051703357e-16,-0.16247613728046417,0.9792641401290894,-2.4492937051703357e-16,-0.2025875300168991,0.9701859354972839,-2.4492937051703357e-16,-0.24236173927783966,0.9594929814338684,-2.4492937051703357e-16,-0.28173255920410156,0.9472029805183411,-2.4492937051703357e-16,-0.3206344544887543,0.9333364963531494,-2.4492937051703357e-16,-0.3590027093887329,0.9179165959358215,-2.4492937051703357e-16,-0.39677342772483826,0.9009688496589661,-2.4492937051703357e-16,-0.4338837265968323,0.8825215697288513,-2.4492937051703357e-16,-0.47027191519737244,0.8626053929328918,-2.4492937051703357e-16,-0.5058773756027222,0.8412535190582275,-2.4492937051703357e-16,-0.5406408309936523,0.8185014724731445,-2.4492937051703357e-16,-0.5745044350624084,0.7943871021270752,-2.4492937051703357e-16,-0.6074118614196777,0.7689505219459534,-2.4492937051703357e-16,-0.6393082737922668,0.7422341108322144,-2.4492937051703357e-16,-0.6701406836509705,0.7142823338508606,-2.4492937051703357e-16,-0.6998576521873474,0.6851417422294617,-2.4492937051703357e-16,-0.7284097671508789,0.6548607349395752,-2.4492937051703357e-16,-0.7557495832443237,0.6234897971153259,-2.4492937051703357e-16,-0.7818315029144287,0.5910811424255371,-2.4492937051703357e-16,-0.8066121339797974,0.5576886534690857,-2.4492937051703357e-16,-0.8300502300262451,0.5233680009841919,-2.4492937051703357e-16,-0.8521067500114441,0.4881761968135834,-2.4492937051703357e-16,-0.8727450966835022,0.4521719217300415,-2.4492937051703357e-16,-0.8919308185577393,0.4154150187969208,-2.4492937051703357e-16,-0.9096319675445557,0.3779667019844055,-2.4492937051703357e-16,-0.9258192181587219,0.3398892879486084,-2.4492937051703357e-16,-0.9404654502868652,0.30124619603157043,-2.4492937051703357e-16,-0.953546404838562,0.2621016800403595,-2.4492937051703357e-16,-0.9650402665138245,0.22252093255519867,-2.4492937051703357e-16,-0.9749279022216797,0.18256981670856476,-2.4492937051703357e-16,-0.9831928610801697,0.1423148363828659,-2.4492937051703357e-16,-0.9898214340209961,0.10182298719882965,-2.4492937051703357e-16,-0.9948025345802307,0.06116165965795517,-2.4492937051703357e-16,-0.9981278777122498,0.020398536697030067,-2.4492937051703357e-16,-0.9997919201850891,-0.020398536697030067,-2.4492937051703357e-16,-0.9997919201850891,-0.06116165965795517,-2.4492937051703357e-16,-0.9981278777122498,-0.10182298719882965,-2.4492937051703357e-16,-0.9948025345802307,-0.1423148363828659,-2.4492937051703357e-16,-0.9898214340209961,-0.18256981670856476,-2.4492937051703357e-16,-0.9831928610801697,-0.22252093255519867,-2.4492937051703357e-16,-0.9749279022216797,-0.2621016800403595,-2.4492937051703357e-16,-0.9650402665138245,-0.30124619603157043,-2.4492937051703357e-16,-0.953546404838562,-0.3398892879486084,-2.4492937051703357e-16,-0.9404654502868652,-0.3779667019844055,-2.4492937051703357e-16,-0.9258192181587219,-0.4154150187969208,-2.4492937051703357e-16,-0.9096319675445557,-0.4521719217300415,-2.4492937051703357e-16,-0.8919308185577393,-0.4881761968135834,-2.4492937051703357e-16,-0.8727450966835022,-0.5233680009841919,-2.4492937051703357e-16,-0.8521067500114441,-0.5576886534690857,-2.4492937051703357e-16,-0.8300502300262451,-0.5910811424255371,-2.4492937051703357e-16,-0.8066121339797974,-0.6234897971153259,-2.4492937051703357e-16,-0.7818315029144287,-0.6548607349395752,-2.4492937051703357e-16,-0.7557495832443237,-0.6851417422294617,-2.4492937051703357e-16,-0.7284097671508789,-0.7142823338508606,-2.4492937051703357e-16,-0.6998576521873474,-0.7422341108322144,-2.4492937051703357e-16,-0.6701406836509705,-0.7689505219459534,-2.4492937051703357e-16,-0.6393082737922668,-0.7943871021270752,-2.4492937051703357e-16,-0.6074118614196777,-0.8185014724731445,-2.4492937051703357e-16,-0.5745044350624084,-0.8412535190582275,-2.4492937051703357e-16,-0.5406408309936523,-0.8626053929328918,-2.4492937051703357e-16,-0.5058773756027222,-0.8825215697288513,-2.4492937051703357e-16,-0.47027191519737244,-0.9009688496589661,-2.4492937051703357e-16,-0.4338837265968323,-0.9179165959358215,-2.4492937051703357e-16,-0.39677342772483826,-0.9333364963531494,-2.4492937051703357e-16,-0.3590027093887329,-0.9472029805183411,-2.4492937051703357e-16,-0.3206344544887543,-0.9594929814338684,-2.4492937051703357e-16,-0.28173255920410156,-0.9701859354972839,-2.4492937051703357e-16,-0.24236173927783966,-0.9792641401290894,-2.4492937051703357e-16,-0.2025875300168991,-0.9867124557495117,-2.4492937051703357e-16,-0.16247613728046417,-0.9925184845924377,-2.4492937051703357e-16,-0.12209431827068329,-0.9966725707054138,-2.4492937051703357e-16,-0.08150928467512131,-0.999167799949646,-2.4492937051703357e-16,-0.04078858718276024,-1.0,-2.4492937051703357e-16,-1.2246468525851679e-16,-0.999167799949646,-2.4492937051703357e-16,0.04078858718276024,-0.9966725707054138,-2.4492937051703357e-16,0.08150928467512131,-0.9925184845924377,-2.4492937051703357e-16,0.12209431827068329,-0.9867124557495117,-2.4492937051703357e-16,0.16247613728046417,-0.9792641401290894,-2.4492937051703357e-16,0.2025875300168991,-0.9701859354972839,-2.4492937051703357e-16,0.24236173927783966,-0.9594929814338684,-2.4492937051703357e-16,0.28173255920410156,-0.9472029805183411,-2.4492937051703357e-16,0.3206344544887543,-0.9333364963531494,-2.4492937051703357e-16,0.3590027093887329,-0.9179165959358215,-2.4492937051703357e-16,0.39677342772483826,-0.9009688496589661,-2.4492937051703357e-16,0.4338837265968323,-0.8825215697288513,-2.4492937051703357e-16,0.47027191519737244,-0.8626053929328918,-2.4492937051703357e-16,0.5058773756027222,-0.8412535190582275,-2.4492937051703357e-16,0.5406408309936523,-0.8185014724731445,-2.4492937051703357e-16,0.5745044350624084,-0.7943871021270752,-2.4492937051703357e-16,0.6074118614196777,-0.7689505219459534,-2.4492937051703357e-16,0.6393082737922668,-0.7422341108322144,-2.4492937051703357e-16,0.6701406836509705,-0.7142823338508606,-2.4492937051703357e-16,0.6998576521873474,-0.6851417422294617,-2.4492937051703357e-16,0.7284097671508789,-0.6548607349395752,-2.4492937051703357e-16,0.7557495832443237,-0.6234897971153259,-2.4492937051703357e-16,0.7818315029144287,-0.5910811424255371,-2.4492937051703357e-16,0.8066121339797974,-0.5576886534690857,-2.4492937051703357e-16,0.8300502300262451,-0.5233680009841919,-2.4492937051703357e-16,0.8521067500114441,-0.4881761968135834,-2.4492937051703357e-16,0.8727450966835022,-0.4521719217300415,-2.4492937051703357e-16,0.8919308185577393,-0.4154150187969208,-2.4492937051703357e-16,0.9096319675445557,-0.3779667019844055,-2.4492937051703357e-16,0.9258192181587219,-0.3398892879486084,-2.4492937051703357e-16,0.9404654502868652,-0.30124619603157043,-2.4492937051703357e-16,0.953546404838562,-0.2621016800403595,-2.4492937051703357e-16,0.9650402665138245,-0.22252093255519867,-2.4492937051703357e-16,0.9749279022216797,-0.18256981670856476,-2.4492937051703357e-16,0.9831928610801697,-0.1423148363828659,-2.4492937051703357e-16,0.9898214340209961,-0.10182298719882965,-2.4492937051703357e-16,0.9948025345802307,-0.06116165965795517,-2.4492937051703357e-16,0.9981278777122498,-0.020398536697030067,-2.4492937051703357e-16,0.9997919201850891,0.020398536697030067,-2.4492937051703357e-16,0.9997919201850891,0.06116165965795517,-2.4492937051703357e-16,0.9981278777122498,0.10182298719882965,-2.4492937051703357e-16,0.9948025345802307,0.1423148363828659,-2.4492937051703357e-16,0.9898214340209961,0.18256981670856476,-2.4492937051703357e-16,0.9831928610801697,0.22252093255519867,-2.4492937051703357e-16,0.9749279022216797,0.2621016800403595,-2.4492937051703357e-16,0.9650402665138245,0.30124619603157043,-2.4492937051703357e-16,0.953546404838562,0.3398892879486084,-2.4492937051703357e-16,0.9404654502868652,0.3779667019844055,-2.4492937051703357e-16,0.9258192181587219,0.4154150187969208,-2.4492937051703357e-16,0.9096319675445557,0.4521719217300415,-2.4492937051703357e-16,0.8919308185577393,0.4881761968135834,-2.4492937051703357e-16,0.8727450966835022,0.5233680009841919,-2.4492937051703357e-16,0.8521067500114441,0.5576886534690857,-2.4492937051703357e-16,0.8300502300262451,0.5910811424255371,-2.4492937051703357e-16,0.8066121339797974,0.6234897971153259,-2.4492937051703357e-16,0.7818315029144287,0.6548607349395752,-2.4492937051703357e-16,0.7557495832443237,0.6851417422294617,-2.4492937051703357e-16,0.7284097671508789,0.7142823338508606,-2.4492937051703357e-16,0.6998576521873474,0.7422341108322144,-2.4492937051703357e-16,0.6701406836509705,0.7689505219459534,-2.4492937051703357e-16,0.6393082737922668,0.7943871021270752,-2.4492937051703357e-16,0.6074118614196777,0.8185014724731445,-2.4492937051703357e-16,0.5745044350624084,0.8412535190582275,-2.4492937051703357e-16,0.5406408309936523,0.8626053929328918,-2.4492937051703357e-16,0.5058773756027222,0.8825215697288513,-2.4492937051703357e-16,0.47027191519737244,0.9009688496589661,-2.4492937051703357e-16,0.4338837265968323,0.9179165959358215,-2.4492937051703357e-16,0.39677342772483826,0.9333364963531494,-2.4492937051703357e-16,0.3590027093887329,0.9472029805183411,-2.4492937051703357e-16,0.3206344544887543,0.9594929814338684,-2.4492937051703357e-16,0.28173255920410156,0.9701859354972839,-2.4492937051703357e-16,0.24236173927783966,0.9792641401290894,-2.4492937051703357e-16,0.2025875300168991,0.9867124557495117,-2.4492937051703357e-16,0.16247613728046417,0.9925184845924377,-2.4492937051703357e-16,0.12209431827068329,0.9966725707054138,-2.4492937051703357e-16,0.08150928467512131,0.999167799949646,-2.4492937051703357e-16,0.04078858718276024,1.0,-2.4492937051703357e-16,2.4492937051703357e-16],"outlineEdgeIds":[],"outlinePositions":[],"positions":[2.5,0.0,0.0,2.4979194983614574,0.0,-0.10197146515396394,2.491681456235483,0.0,-0.2037732088676579,2.4812962562275693,0.0,-0.30523579218539965,2.466781183478222,0.0,-0.4061903406505145,2.448160396893553,0.0,-0.5064688253802024,2.4254648889351613,0.0,-0.6059043427330308,2.3987324340362433,0.0,-0.7043313921035742,2.3680075257297752,0.0,-0.8015861513818388,2.333341302593417,0.0,-0.8975067496189966,2.2947914631343984,0.0,-0.9919335364456062,2.252422169756048,0.0,-1.0847093477938952,2.2063039419658033,0.0,-1.175679767481842,2.156513539002452,0.0,-1.2646933842236707,2.103133832077953,0.0,-1.351602043638994,2.046253666446484,0.0,-1.436261094841158,1.9859677135302816,0.0,-1.5185296311943688,1.922376313348405,0.0,-1.5982707248388786,1.8555853075106747,0.0,-1.6753516545938987,1.7857058630547589,0.0,-1.7496441268589045,1.7128542874196104,0.0,-1.8210244891456728,1.6371518348632126,0.0,-1.8893739358856456,1.5587245046468339,0.0,-1.9545787061700741,1.4777028313216918,0.0,-2.0165302730938253,1.3942216674670738,0.0,-2.075125524387701,1.30841995924153,0.0,-2.1302669340386418,1.220440515120703,0.0,-2.1818627246121407,1.1304297682067146,0.0,-2.2298270200067343,1.0385375325047161,0.0,-2.274079988386296,0.9449167535722663,0.0,-2.3145479750522453,0.8497232539565422,0.0,-2.3511636250345287,0.7531154738430859,0.0,-2.383865995197319,0.6552542073477556,0.0,-2.4126006556728496,0.5563023348907861,0.0,-2.437319780454559,0.4564245520984042,0.0,-2.4579822269987575,0.3557870956832133,0.0,-2.4745536047023315,0.25455746675958885,0.0,-2.48700633214251,0.15290415205460708,0.0,-2.495319682983417,0.05099634347851358,0.0,-2.499479820473016,-0.050996343478513284,0.0,-2.499479820473016,-0.15290415205460675,0.0,-2.495319682983417,-0.254557466759588,0.0,-2.48700633214251,-0.3557870956832131,0.0,-2.4745536047023315,-0.45642455209840394,0.0,-2.4579822269987575,-0.5563023348907853,0.0,-2.437319780454559,-0.6552542073477553,0.0,-2.41260065567285,-0.7531154738430851,0.0,-2.3838659951973193,-0.8497232539565419,0.0,-2.3511636250345287,-0.944916753572266,0.0,-2.3145479750522453,-1.0385375325047155,0.0,-2.274079988386296,-1.1304297682067146,0.0,-2.2298270200067343,-1.2204405151207027,0.0,-2.1818627246121407,-1.3084199592415304,0.0,-2.130266934038641,-1.394221667467073,0.0,-2.0751255243877016,-1.4777028313216911,0.0,-2.0165302730938253,-1.5587245046468337,0.0,-1.9545787061700748,-1.6371518348632128,0.0,-1.8893739358856456,-1.7128542874196107,0.0,-1.8210244891456728,-1.7857058630547589,0.0,-1.7496441268589045,-1.8555853075106747,0.0,-1.6753516545938987,-1.9223763133484044,0.0,-1.5982707248388794,-1.9859677135302816,0.0,-1.5185296311943686,-2.0462536664464834,0.0,-1.436261094841159,-2.1031338320779533,0.0,-1.3516020436389935,-2.1565135390024515,0.0,-1.264693384223671,-2.206303941965803,0.0,-1.1756797674818422,-2.2524221697560476,0.0,-1.0847093477938956,-2.2947914631343984,0.0,-0.9919335364456063,-2.333341302593417,0.0,-0.8975067496189968,-2.3680075257297752,0.0,-0.8015861513818388,-2.3987324340362433,0.0,-0.7043313921035752,-2.4254648889351613,0.0,-0.6059043427330306,-2.4481603968935524,0.0,-0.5064688253802032,-2.466781183478222,0.0,-0.4061903406505152,-2.4812962562275693,0.0,-0.30523579218540026,-2.491681456235483,0.0,-0.20377320886765843,-2.4979194983614574,0.0,-0.10197146515396432,-2.5,0.0,-3.061616997868383e-16,-2.4979194983614574,0.0,0.10197146515396371,-2.491681456235483,0.0,0.2037732088676578,-2.4812962562275693,0.0,0.30523579218539965,-2.4667811834782225,0.0,0.4061903406505135,-2.4481603968935532,0.0,0.5064688253802015,-2.4254648889351613,0.0,0.6059043427330311,-2.3987324340362433,0.0,0.7043313921035745,-2.3680075257297752,0.0,0.8015861513818381,-2.3333413025934173,0.0,0.8975067496189961,-2.2947914631343984,0.0,0.9919335364456059,-2.2524221697560485,0.0,1.084709347793894,-2.2063039419658033,0.0,1.1756797674818418,-2.156513539002452,0.0,1.2646933842236705,-2.103133832077953,0.0,1.351602043638994,-2.0462536664464843,0.0,1.4362610948411576,-1.9859677135302822,0.0,1.518529631194368,-1.9223763133484049,0.0,1.598270724838879,-1.8555853075106745,0.0,1.6753516545938991,-1.7857058630547595,0.0,1.7496441268589042,-1.7128542874196109,0.0,1.8210244891456726,-1.637151834863214,0.0,1.8893739358856445,-1.5587245046468339,0.0,1.9545787061700741,-1.4777028313216904,0.0,2.0165302730938257,-1.3942216674670738,0.0,2.075125524387701,-1.308419959241531,0.0,2.130266934038641,-1.2204405151207032,0.0,2.1818627246121403,-1.1304297682067133,0.0,2.229827020006735,-1.038537532504716,0.0,2.274079988386296,-0.9449167535722671,0.0,2.314547975052245,-0.8497232539565415,0.0,2.3511636250345287,-0.7531154738430862,0.0,2.383865995197319,-0.655254207347757,0.0,2.412600655672849,-0.5563023348907865,0.0,2.437319780454559,-0.45642455209840344,0.0,2.4579822269987575,-0.3557870956832131,0.0,2.4745536047023315,-0.2545574667595897,0.0,2.48700633214251,-0.1529041520546068,0.0,2.495319682983417,-0.05099634347851223,0.0,2.499479820473016,0.050996343478513534,0.0,2.499479820473016,0.1529041520546059,0.0,2.495319682983417,0.2545574667595888,0.0,2.48700633214251,0.35578709568321215,0.0,2.474553604702332,0.45642455209840255,0.0,2.457982226998758,0.5563023348907856,0.0,2.437319780454559,0.6552542073477562,0.0,2.4126006556728496,0.7531154738430854,0.0,2.3838659951973193,0.8497232539565407,0.0,2.351163625034529,0.9449167535722642,0.0,2.314547975052246,1.038537532504717,0.0,2.2740799883862954,1.1304297682067144,0.0,2.2298270200067343,1.2204405151207025,0.0,2.1818627246121407,1.3084199592415287,0.0,2.130266934038642,1.3942216674670729,0.0,2.0751255243877016,1.4777028313216918,0.0,2.016530273093825,1.5587245046468334,0.0,1.9545787061700748,1.6371518348632133,0.0,1.8893739358856454,1.71285428741961,0.0,1.821024489145673,1.7857058630547582,0.0,1.7496441268589056,1.8555853075106745,0.0,1.6753516545938991,1.9223763133484058,0.0,1.5982707248388777,1.9859677135302813,0.0,1.5185296311943688,2.046253666446483,0.0,1.4362610948411592,2.103133832077952,0.0,1.3516020436389955,2.1565135390024515,0.0,1.2646933842236714,2.2063039419658033,0.0,1.1756797674818416,2.2524221697560476,0.0,1.0847093477938958,2.2947914631343975,0.0,0.9919335364456078,2.333341302593417,0.0,0.8975067496189971,2.368007525729775,0.0,0.8015861513818401,2.3987324340362433,0.0,0.7043313921035744,2.425464888935161,0.0,0.605904342733032,2.448160396893553,0.0,0.5064688253802024,2.466781183478222,0.0,0.40619034065051546,2.4812962562275693,0.0,0.30523579218540164,2.491681456235483,0.0,0.2037732088676587,2.4979194983614574,0.0,0.10197146515396353,2.5,0.0,0.0,2.498754512575382,0.03526947952115548,0.0,2.4966750474322903,0.03526947952115548,-0.10192066348295628,2.490440113067525,0.03526947952115548,-0.2036716900800104,2.480060086914016,0.03526947952115548,-0.30508372524911553,2.4655522455009,0.03526947952115548,-0.4059879786660019,2.4469407356984414,0.03526947952115548,-0.5062165051590136,2.4242565345279528,0.03526947952115548,-0.6056024842372727,2.3975373976035974,0.03526947952115548,-0.7039804977469227,2.3668277962918967,0.03526947952115548,-0.8011868051933211,2.3321788436935282,0.03526947952115548,-0.8970596162709326,2.2936482095706165,0.03526947952115548,-0.9914393601473264,2.251300024361103,0.03526947952115548,-1.084168951053078,2.205204772439962,0.03526947952115548,-1.1750940497355316,2.1554391748049135,0.03526947952115548,-1.2640633203412515,2.1020860613818964,0.03526947952115548,-1.3509286822996178,2.045234233162829,0.03526947952115548,-1.435545556788321,1.984978314405122,0.03526947952115548,-1.5177731073705438,1.9214185951389415,0.03526947952115548,-1.5974744744033098,1.8546608642443507,0.03526947952115548,-1.674517002826855,1.7848162333761586,0.03526947952115548,-1.7487724629558807,1.7120009520295367,0.03526947952115548,-1.820117263905212,1.6363362140542075,0.03526947952115548,-1.888432659294627,1.5579479559392413,0.03526947952115548,-1.95360494489049,1.4769666472041982,0.03526947952115548,-2.0155256478552253,1.3935270732454896,0.03526947952115548,-2.0740917072896496,1.3077681109993882,0.03526947952115548,-2.1292056457676716,1.219832497795072,0.03526947952115548,-2.1807757315778415,1.1298665937824284,0.03526947952115548,-2.228716131401738,1.0380201383300247,0.03526947952115548,-2.2729470531750517,0.9444460007987123,0.03526947952115548,-2.3133948788936043,0.8492999261056589,0.03526947952115548,-2.3499922871432486,0.7527402755023033,0.03526947952115548,-2.382678365149722,0.6549277629976837,0.03526947952115548,-2.4113987101619436,0.5560251878658293,0.03526947952115548,-2.4361055200000274,0.456197163682434,0.03526947952115548,-2.456757672617293,0.3556098443418074,0.03526947952115548,-2.473320794543852,0.2544306475101122,0.03526947952115548,-2.485767318097859,0.15282797597518472,0.03526947952115548,-2.4940765272691943,0.05097093735671199,0.03526947952115548,-2.498234592199222,-0.05097093735671169,0.03526947952115548,-2.498234592199222,-0.1528279759751844,0.03526947952115548,-2.4940765272691943,-0.25443064751011135,0.03526947952115548,-2.485767318097859,-0.35560984434180715,0.03526947952115548,-2.473320794543852,-0.45619716368243374,0.03526947952115548,-2.456757672617293,-0.5560251878658284,0.03526947952115548,-2.4361055200000274,-0.6549277629976835,0.03526947952115548,-2.4113987101619436,-0.7527402755023024,0.03526947952115548,-2.3826783651497223,-0.8492999261056586,0.03526947952115548,-2.349992287143249,-0.944446000798712,0.03526947952115548,-2.3133948788936043,-1.038020138330024,0.03526947952115548,-2.2729470531750517,-1.1298665937824286,0.03526947952115548,-2.228716131401738,-1.2198324977950719,0.03526947952115548,-2.1807757315778415,-1.3077681109993886,0.03526947952115548,-2.129205645767671,-1.3935270732454892,0.03526947952115548,-2.0740917072896505,-1.4769666472041978,0.03526947952115548,-2.0155256478552253,-1.557947955939241,0.03526947952115548,-1.9536049448904904,-1.636336214054208,0.03526947952115548,-1.888432659294627,-1.712000952029537,0.03526947952115548,-1.820117263905212,-1.7848162333761586,0.03526947952115548,-1.7487724629558807,-1.8546608642443507,0.03526947952115548,-1.674517002826855,-1.921418595138941,0.03526947952115548,-1.5974744744033107,-1.984978314405122,0.03526947952115548,-1.5177731073705436,-2.0452342331628284,0.03526947952115548,-1.4355455567883217,-2.102086061381897,0.03526947952115548,-1.3509286822996176,-2.155439174804913,0.03526947952115548,-1.2640633203412517,-2.2052047724399615,0.03526947952115548,-1.1750940497355318,-2.251300024361103,0.03526947952115548,-1.0841689510530785,-2.2936482095706165,0.03526947952115548,-0.9914393601473265,-2.3321788436935282,0.03526947952115548,-0.8970596162709327,-2.3668277962918967,0.03526947952115548,-0.8011868051933211,-2.397537397603597,0.03526947952115548,-0.7039804977469237,-2.4242565345279528,0.03526947952115548,-0.6056024842372725,-2.446940735698441,0.03526947952115548,-0.5062165051590144,-2.4655522455009,0.03526947952115548,-0.40598797866600256,-2.480060086914016,0.03526947952115548,-0.30508372524911614,-2.490440113067525,0.03526947952115548,-0.20367169008001093,-2.4966750474322903,0.03526947952115548,-0.10192066348295668,-2.498754512575382,0.03526947952115548,-3.0600917156804463e-16,-2.4966750474322903,0.03526947952115548,0.10192066348295607,-2.490440113067525,0.03526947952115548,0.20367169008001032,-2.480060086914016,0.03526947952115548,0.3050837252491156,-2.4655522455009,0.03526947952115548,0.4059879786660009,-2.446940735698442,0.03526947952115548,0.5062165051590127,-2.4242565345279528,0.03526947952115548,0.6056024842372729,-2.3975373976035974,0.03526947952115548,0.7039804977469232,-2.3668277962918967,0.03526947952115548,0.8011868051933205,-2.3321788436935282,0.03526947952115548,0.8970596162709321,-2.2936482095706165,0.03526947952115548,0.9914393601473259,-2.251300024361104,0.03526947952115548,1.084168951053077,-2.205204772439962,0.03526947952115548,1.175094049735531,-2.1554391748049135,0.03526947952115548,1.2640633203412512,-2.1020860613818964,0.03526947952115548,1.3509286822996178,-2.0452342331628293,0.03526947952115548,1.4355455567883204,-1.9849783144051225,0.03526947952115548,1.5177731073705432,-1.9214185951389413,0.03526947952115548,1.5974744744033103,-1.8546608642443505,0.03526947952115548,1.6745170028268552,-1.784816233376159,0.03526947952115548,1.7487724629558803,-1.7120009520295374,0.03526947952115548,1.8201172639052117,-1.636336214054209,0.03526947952115548,1.888432659294626,-1.5579479559392413,0.03526947952115548,1.95360494489049,-1.4769666472041971,0.03526947952115548,2.0155256478552257,-1.3935270732454896,0.03526947952115548,2.0740917072896496,-1.3077681109993893,0.03526947952115548,2.1292056457676707,-1.2198324977950723,0.03526947952115548,2.1807757315778415,-1.1298665937824273,0.03526947952115548,2.2287161314017387,-1.0380201383300247,0.03526947952115548,2.2729470531750517,-0.9444460007987131,0.03526947952115548,2.313394878893604,-0.8492999261056582,0.03526947952115548,2.349992287143249,-0.7527402755023035,0.03526947952115548,2.382678365149722,-0.6549277629976852,0.03526947952115548,2.411398710161943,-0.5560251878658297,0.03526947952115548,2.4361055200000274,-0.4561971636824333,0.03526947952115548,2.456757672617293,-0.35560984434180715,0.03526947952115548,2.473320794543852,-0.254430647510113,0.03526947952115548,2.4857673180978583,-0.15282797597518447,0.03526947952115548,2.4940765272691943,-0.050970937356710634,0.03526947952115548,2.498234592199222,0.05097093735671194,0.03526947952115548,2.498234592199222,0.15282797597518355,0.03526947952115548,2.4940765272691943,0.25443064751011213,0.03526947952115548,2.485767318097859,0.35560984434180626,0.03526947952115548,2.4733207945438522,0.45619716368243235,0.03526947952115548,2.456757672617293,0.5560251878658288,0.03526947952115548,2.4361055200000274,0.6549277629976843,0.03526947952115548,2.4113987101619436,0.7527402755023027,0.03526947952115548,2.3826783651497223,0.8492999261056573,0.03526947952115548,2.3499922871432495,0.9444460007987102,0.03526947952115548,2.313394878893605,1.0380201383300258,0.03526947952115548,2.2729470531750513,1.1298665937824284,0.03526947952115548,2.228716131401738,1.2198324977950716,0.03526947952115548,2.180775731577842,1.3077681109993868,0.03526947952115548,2.1292056457676725,1.393527073245489,0.03526947952115548,2.0740917072896505,1.4769666472041982,0.03526947952115548,2.015525647855225,1.5579479559392408,0.03526947952115548,1.9536049448904904,1.6363362140542084,0.03526947952115548,1.8884326592946268,1.7120009520295365,0.03526947952115548,1.8201172639052121,1.7848162333761577,0.03526947952115548,1.7487724629558818,1.8546608642443505,0.03526947952115548,1.6745170028268552,1.9214185951389422,0.03526947952115548,1.5974744744033091,1.9849783144051216,0.03526947952115548,1.5177731073705438,2.045234233162828,0.03526947952115548,1.4355455567883222,2.1020860613818955,0.03526947952115548,1.3509286822996194,2.155439174804913,0.03526947952115548,1.2640633203412521,2.205204772439962,0.03526947952115548,1.175094049735531,2.251300024361103,0.03526947952115548,1.0841689510530788,2.293648209570616,0.03526947952115548,0.9914393601473278,2.3321788436935282,0.03526947952115548,0.8970596162709331,2.3668277962918958,0.03526947952115548,0.8011868051933225,2.3975373976035974,0.03526947952115548,0.703980497746923,2.4242565345279528,0.03526947952115548,0.6056024842372738,2.4469407356984414,0.03526947952115548,0.5062165051590136,2.4655522455009,0.03526947952115548,0.40598797866600284,2.480060086914016,0.03526947952115548,0.3050837252491176,2.490440113067525,0.03526947952115548,0.2036716900800112,2.4966750474322903,0.03526947952115548,0.10192066348295588,2.498754512575382,0.03526947952115548,0.0,2.4950242552572273,0.07036324826944526,0.0,2.4929478944367207,0.07036324826944526,-0.10176851156130286,2.4867222678728718,0.07036324826944526,-0.20336763947856143,2.476357737506695,0.07036324826944526,-0.30462828203009057,2.4618715540761174,0.07036324826944526,-0.4053819008696917,2.4432878284038297,0.07036324826944526,-0.5054608015420969,2.4206374912672017,0.07036324826944526,-0.6046984125938399,2.3939582419170535,0.07036324826944526,-0.7029295628150025,2.363294485330977,0.07036324826944526,-0.7999907561503916,2.3286972583056276,0.07036324826944526,-0.8957204438225886,2.290224144511018,0.07036324826944526,-0.9899592932139466,2.2479391786481804,0.07036324826944526,-1.0825504530600063,2.201912739869725,0.07036324826944526,-1.1733398145129492,2.152221434640688,0.07036324826944526,-1.2621762676405626,2.0989479692346293,0.07036324826944526,-1.348911952933811,2.042181012077204,0.07036324826944526,-1.4334025074043961,1.9820150461663157,0.07036324826944526,-1.5155073048627048,1.9185502118144955,0.07036324826944526,-1.5950896899762208,1.8518921399752297,0.07036324826944526,-1.6720172057188423,1.7821517764306658,0.07036324826944526,-1.7461618138325279,1.709445197133305,0.07036324826944526,-1.817400107934342,1.6338934150090358,0.07036324826944526,-1.8856135189141998,1.555622178543063,0.07036324826944526,-1.9506885122814497,1.47476176248396,0.07036324826944526,-2.0125167771318297,1.3914467510142101,0.07036324826944526,-2.070995406420275,1.305815813748116,0.07036324826944526,-2.1260270682395435,1.2180114749299114,0.07036324826944526,-2.1775201678195644,1.1281798762162232,0.07036324826944526,-2.225388999977898,1.0364705334377031,0.07036324826944526,-2.269553891767553,0.9430360877446883,0.07036324826944526,-2.3099413350847406,0.8480320515510678,0.07036324826944526,-2.346484109015863,0.7516165496992157,0.07036324826944526,-2.379121391720088,0.6539500562767995,0.07036324826944526,-2.4077988616612997,0.5551951275234961,0.07036324826944526,-2.432468788020938,0.4555161312721738,0.07036324826944526,-2.4530901101412304,0.3550789733748565,0.07036324826944526,-2.4696285058666088,0.2540508215688038,0.07036324826944526,-2.48205644866955,0.15259982724231355,0.07036324826944526,-2.49035325346576,0.050894845563328045,0.07036324826944526,-2.494505111042462,-0.05089484556332774,0.07036324826944526,-2.494505111042462,-0.15259982724231322,0.07036324826944526,-2.4903532534657606,-0.254050821568803,0.07036324826944526,-2.48205644866955,-0.3550789733748562,0.07036324826944526,-2.4696285058666088,-0.4555161312721735,0.07036324826944526,-2.4530901101412304,-0.5551951275234953,0.07036324826944526,-2.432468788020938,-0.6539500562767993,0.07036324826944526,-2.4077988616613,-0.7516165496992149,0.07036324826944526,-2.379121391720088,-0.8480320515510675,0.07036324826944526,-2.3464841090158632,-0.9430360877446881,0.07036324826944526,-2.309941335084741,-1.0364705334377025,0.07036324826944526,-2.269553891767553,-1.1281798762162234,0.07036324826944526,-2.225388999977898,-1.2180114749299114,0.07036324826944526,-2.1775201678195644,-1.3058158137481164,0.07036324826944526,-2.126027068239543,-1.3914467510142097,0.07036324826944526,-2.0709954064202756,-1.4747617624839593,0.07036324826944526,-2.0125167771318297,-1.5556221785430626,0.07036324826944526,-1.9506885122814503,-1.6338934150090363,0.07036324826944526,-1.8856135189141998,-1.7094451971333051,0.07036324826944526,-1.817400107934342,-1.7821517764306658,0.07036324826944526,-1.7461618138325279,-1.8518921399752297,0.07036324826944526,-1.6720172057188423,-1.9185502118144948,0.07036324826944526,-1.5950896899762217,-1.9820150461663157,0.07036324826944526,-1.5155073048627046,-2.042181012077203,0.07036324826944526,-1.433402507404397,-2.0989479692346293,0.07036324826944526,-1.3489119529338105,-2.1522214346406874,0.07036324826944526,-1.2621762676405628,-2.2019127398697247,0.07036324826944526,-1.1733398145129494,-2.2479391786481804,0.07036324826944526,-1.0825504530600067,-2.290224144511018,0.07036324826944526,-0.9899592932139467,-2.3286972583056276,0.07036324826944526,-0.8957204438225889,-2.363294485330977,0.07036324826944526,-0.7999907561503916,-2.3939582419170535,0.07036324826944526,-0.7029295628150035,-2.4206374912672017,0.07036324826944526,-0.6046984125938398,-2.4432878284038297,0.07036324826944526,-0.5054608015420977,-2.4618715540761174,0.07036324826944526,-0.40538190086969245,-2.476357737506695,0.07036324826944526,-0.30462828203009124,-2.4867222678728718,0.07036324826944526,-0.20336763947856196,-2.4929478944367207,0.07036324826944526,-0.10176851156130326,-2.4950242552572273,0.07036324826944526,-3.0555234679957723e-16,-2.4929478944367207,0.07036324826944526,0.10176851156130265,-2.4867222678728718,0.07036324826944526,0.20336763947856132,-2.476357737506695,0.07036324826944526,0.3046282820300906,-2.4618715540761174,0.07036324826944526,0.4053819008696908,-2.44328782840383,0.07036324826944526,0.505460801542096,-2.4206374912672017,0.07036324826944526,0.6046984125938403,-2.3939582419170535,0.07036324826944526,0.702929562815003,-2.363294485330977,0.07036324826944526,0.7999907561503911,-2.3286972583056276,0.07036324826944526,0.8957204438225883,-2.290224144511018,0.07036324826944526,0.9899592932139462,-2.2479391786481813,0.07036324826944526,1.0825504530600052,-2.201912739869725,0.07036324826944526,1.173339814512949,-2.152221434640688,0.07036324826944526,1.2621762676405623,-2.0989479692346293,0.07036324826944526,1.348911952933811,-2.042181012077204,0.07036324826944526,1.4334025074043955,-1.9820150461663164,0.07036324826944526,1.515507304862704,-1.9185502118144953,0.07036324826944526,1.595089689976221,-1.8518921399752295,0.07036324826944526,1.6720172057188425,-1.7821517764306662,0.07036324826944526,1.7461618138325277,-1.7094451971333053,0.07036324826944526,1.8174001079343418,-1.6338934150090374,0.07036324826944526,1.8856135189141987,-1.555622178543063,0.07036324826944526,1.9506885122814497,-1.4747617624839588,0.07036324826944526,2.01251677713183,-1.3914467510142101,0.07036324826944526,2.070995406420275,-1.305815813748117,0.07036324826944526,2.1260270682395426,-1.2180114749299118,0.07036324826944526,2.177520167819564,-1.1281798762162218,0.07036324826944526,2.225388999977899,-1.036470533437703,0.07036324826944526,2.269553891767553,-0.9430360877446892,0.07036324826944526,2.30994133508474,-0.8480320515510671,0.07036324826944526,2.3464841090158632,-0.751616549699216,0.07036324826944526,2.379121391720088,-0.6539500562768009,0.07036324826944526,2.4077988616612997,-0.5551951275234964,0.07036324826944526,2.432468788020938,-0.45551613127217305,0.07036324826944526,2.4530901101412304,-0.3550789733748562,0.07036324826944526,2.4696285058666088,-0.25405082156880465,0.07036324826944526,2.4820564486695496,-0.15259982724231327,0.07036324826944526,2.4903532534657606,-0.05089484556332669,0.07036324826944526,2.494505111042462,0.050894845563327996,0.07036324826944526,2.494505111042462,0.15259982724231236,0.07036324826944526,2.4903532534657606,0.25405082156880376,0.07036324826944526,2.48205644866955,0.35507897337485533,0.07036324826944526,2.469628505866609,0.4555161312721721,0.07036324826944526,2.453090110141231,0.5551951275234955,0.07036324826944526,2.432468788020938,0.6539500562768,0.07036324826944526,2.4077988616612997,0.7516165496992151,0.07036324826944526,2.379121391720088,0.8480320515510663,0.07036324826944526,2.3464841090158637,0.9430360877446863,0.07036324826944526,2.3099413350847415,1.036470533437704,0.07036324826944526,2.2695538917675524,1.128179876216223,0.07036324826944526,2.225388999977898,1.218011474929911,0.07036324826944526,2.1775201678195644,1.3058158137481146,0.07036324826944526,2.1260270682395443,1.3914467510142092,0.07036324826944526,2.0709954064202756,1.47476176248396,0.07036324826944526,2.0125167771318293,1.5556221785430624,0.07036324826944526,1.9506885122814503,1.6338934150090367,0.07036324826944526,1.8856135189141994,1.7094451971333045,0.07036324826944526,1.8174001079343423,1.782151776430665,0.07036324826944526,1.746161813832529,1.8518921399752295,0.07036324826944526,1.6720172057188425,1.918550211814496,0.07036324826944526,1.59508968997622,1.9820150461663155,0.07036324826944526,1.5155073048627048,2.042181012077203,0.07036324826944526,1.4334025074043972,2.098947969234628,0.07036324826944526,1.3489119529338125,2.1522214346406874,0.07036324826944526,1.2621762676405632,2.2019127398697256,0.07036324826944526,1.1733398145129488,2.2479391786481804,0.07036324826944526,1.0825504530600072,2.290224144511017,0.07036324826944526,0.989959293213948,2.3286972583056276,0.07036324826944526,0.8957204438225892,2.363294485330976,0.07036324826944526,0.7999907561503931,2.3939582419170535,0.07036324826944526,0.7029295628150027,2.4206374912672013,0.07036324826944526,0.6046984125938412,2.4432878284038297,0.07036324826944526,0.5054608015420969,2.4618715540761174,0.07036324826944526,0.4053819008696927,2.476357737506695,0.07036324826944526,0.3046282820300926,2.4867222678728718,0.07036324826944526,0.20336763947856223,2.4929478944367207,0.07036324826944526,0.10176851156130247,2.4950242552572273,0.07036324826944526,0.0,2.4888278119998573,0.10510647085423555,0.0,2.486756607863491,0.10510647085423555,-0.1015157674022239,2.48054644276927,0.10510647085423555,-0.20286257182811318,2.4702076529241195,0.10510647085423555,-0.30387173152353253,2.4557574462234086,0.10510647085423555,-0.40437512671067866,2.4372198736101134,0.10510647085423555,-0.5042054794068588,2.41462578904439,0.10510647085423555,-0.6031966318421843,2.388012798150206,0.10510647085423555,-0.7011838230127808,2.357425195624493,0.10510647085423555,-0.7980039629092192,2.322913891512988,0.10510647085423555,-0.8934959039637405,2.2845363264754943,0.10510647085423555,-0.9875007092644795,2.2423563761815664,0.10510647085423555,-1.079861917090269,2.1964442449957637,0.10510647085423555,-1.1704258013257336,2.1468763491294163,0.10510647085423555,-1.2590416273232372,2.093735189453379,0.10510647085423555,-1.345561902785829,2.0371092141834755,0.10510647085423555,-1.4298426232536154,1.9770926716671722,0.10510647085423555,-1.5117435117849725,1.9137854535165053,0.10510647085423555,-1.591128252433669,1.8472929283483501,0.10510647085423555,-1.6678647171333094,1.777725766408757,0.10510647085423555,-1.741825185611459,1.7051997553732494,0.10510647085423555,-1.8128865579674331,1.6298356076296643,0.10510647085423555,-1.880930559559932,1.5517587593642965,0.10510647085423555,-1.945843937863511,1.471099161785744,0.10510647085423555,-2.0075186509662317,1.387991064833948,0.10510647085423555,-2.0658520473947597,1.3025727937344158,0.10510647085423555,-2.1207470369676145,1.214986518769535,0.10510647085423555,-2.1721122523921923,1.1253780186501692,0.10510647085423555,-2.219862201336609,1.0338964378813773,0.10510647085423555,-2.26391740872325,0.9406940385261088,0.10510647085423555,-2.3042045490071916,0.845925946780024,0.10510647085423555,-2.340656568219335,0.7497498947792492,0.10510647085423555,-2.3732127955711224,0.6523259580708062,0.10510647085423555,-2.4018190444350718,0.5538162891826588,0.10510647085423555,-2.426427702533077,0.45438484773683446,0.10510647085423555,-2.446997811182342,0.3541971275548143,0.10510647085423555,-2.4634951334670654,0.25341988120939757,0.10510647085423555,-2.4758922112224133,0.15222084248150447,0.10510647085423555,-2.484168410735918,0.05076844718384886,0.10510647085423555,-2.4883099570902614,-0.050768447183848556,0.10510647085423555,-2.4883099570902614,-0.15222084248150416,0.10510647085423555,-2.4841684107359185,-0.25341988120939674,0.10510647085423555,-2.4758922112224133,-0.35419712755481403,0.10510647085423555,-2.4634951334670654,-0.4543848477368342,0.10510647085423555,-2.446997811182342,-0.553816289182658,0.10510647085423555,-2.426427702533077,-0.6523259580708058,0.10510647085423555,-2.401819044435072,-0.7497498947792485,0.10510647085423555,-2.3732127955711224,-0.8459259467800238,0.10510647085423555,-2.3406565682193357,-0.9406940385261086,0.10510647085423555,-2.304204549007192,-1.0338964378813766,0.10510647085423555,-2.2639174087232505,-1.1253780186501694,0.10510647085423555,-2.219862201336609,-1.214986518769535,0.10510647085423555,-2.1721122523921923,-1.3025727937344163,0.10510647085423555,-2.120747036967614,-1.3879910648339473,0.10510647085423555,-2.06585204739476,-1.4710991617857434,0.10510647085423555,-2.007518650966232,-1.551758759364296,0.10510647085423555,-1.9458439378635117,-1.6298356076296645,0.10510647085423555,-1.880930559559932,-1.7051997553732496,0.10510647085423555,-1.8128865579674331,-1.777725766408757,0.10510647085423555,-1.741825185611459,-1.8472929283483501,0.10510647085423555,-1.6678647171333094,-1.9137854535165046,0.10510647085423555,-1.5911282524336696,-1.9770926716671722,0.10510647085423555,-1.5117435117849722,-2.0371092141834746,0.10510647085423555,-1.4298426232536163,-2.093735189453379,0.10510647085423555,-1.3455619027858288,-2.146876349129416,0.10510647085423555,-1.2590416273232377,-2.1964442449957637,0.10510647085423555,-1.1704258013257338,-2.242356376181566,0.10510647085423555,-1.0798619170902692,-2.2845363264754943,0.10510647085423555,-0.9875007092644796,-2.322913891512988,0.10510647085423555,-0.8934959039637406,-2.357425195624493,0.10510647085423555,-0.7980039629092192,-2.388012798150206,0.10510647085423555,-0.7011838230127818,-2.41462578904439,0.10510647085423555,-0.6031966318421841,-2.437219873610113,0.10510647085423555,-0.5042054794068596,-2.4557574462234086,0.10510647085423555,-0.4043751267106793,-2.4702076529241195,0.10510647085423555,-0.30387173152353314,-2.48054644276927,0.10510647085423555,-0.20286257182811368,-2.486756607863491,0.10510647085423555,-0.10151576740222429,-2.4888278119998573,0.10510647085423555,-3.047935013594536e-16,-2.486756607863491,0.10510647085423555,0.10151576740222369,-2.48054644276927,0.10510647085423555,0.20286257182811307,-2.4702076529241195,0.10510647085423555,0.30387173152353253,-2.455757446223409,0.10510647085423555,0.4043751267106777,-2.4372198736101134,0.10510647085423555,0.5042054794068579,-2.41462578904439,0.10510647085423555,0.6031966318421845,-2.388012798150206,0.10510647085423555,0.7011838230127813,-2.357425195624493,0.10510647085423555,0.7980039629092186,-2.3229138915129885,0.10510647085423555,0.89349590396374,-2.2845363264754943,0.10510647085423555,0.9875007092644791,-2.242356376181567,0.10510647085423555,1.079861917090268,-2.1964442449957637,0.10510647085423555,1.1704258013257332,-2.1468763491294163,0.10510647085423555,1.259041627323237,-2.093735189453379,0.10510647085423555,1.345561902785829,-2.0371092141834755,0.10510647085423555,1.429842623253615,-1.9770926716671726,0.10510647085423555,1.5117435117849716,-1.9137854535165049,0.10510647085423555,1.5911282524336692,-1.84729292834835,0.10510647085423555,1.6678647171333096,-1.7777257664087576,0.10510647085423555,1.7418251856114588,-1.7051997553732499,0.10510647085423555,1.812886557967433,-1.6298356076296656,0.10510647085423555,1.8809305595599308,-1.5517587593642965,0.10510647085423555,1.945843937863511,-1.471099161785743,0.10510647085423555,2.007518650966232,-1.387991064833948,0.10510647085423555,2.0658520473947597,-1.302572793734417,0.10510647085423555,2.1207470369676136,-1.2149865187695355,0.10510647085423555,2.172112252392192,-1.125378018650168,0.10510647085423555,2.21986220133661,-1.033896437881377,0.10510647085423555,2.2639174087232505,-0.9406940385261096,0.10510647085423555,2.3042045490071916,-0.8459259467800233,0.10510647085423555,2.3406565682193357,-0.7497498947792496,0.10510647085423555,2.3732127955711224,-0.6523259580708075,0.10510647085423555,2.4018190444350713,-0.5538162891826591,0.10510647085423555,2.426427702533077,-0.45438484773683374,0.10510647085423555,2.446997811182342,-0.35419712755481403,0.10510647085423555,2.4634951334670654,-0.2534198812093984,0.10510647085423555,2.4758922112224133,-0.15222084248150422,0.10510647085423555,2.4841684107359185,-0.050768447183847515,0.10510647085423555,2.4883099570902614,0.05076844718384881,0.10510647085423555,2.4883099570902614,0.1522208424815033,0.10510647085423555,2.4841684107359185,0.2534198812093975,0.10510647085423555,2.4758922112224133,0.35419712755481314,0.10510647085423555,2.463495133467066,0.45438484773683285,0.10510647085423555,2.446997811182342,0.5538162891826582,0.10510647085423555,2.426427702533077,0.6523259580708067,0.10510647085423555,2.4018190444350718,0.7497498947792487,0.10510647085423555,2.3732127955711224,0.8459259467800224,0.10510647085423555,2.340656568219336,0.9406940385261067,0.10510647085423555,2.3042045490071925,1.0338964378813782,0.10510647085423555,2.26391740872325,1.1253780186501692,0.10510647085423555,2.219862201336609,1.2149865187695346,0.10510647085423555,2.1721122523921927,1.3025727937344143,0.10510647085423555,2.1207470369676154,1.387991064833947,0.10510647085423555,2.06585204739476,1.471099161785744,0.10510647085423555,2.0075186509662313,1.5517587593642959,0.10510647085423555,1.9458439378635117,1.6298356076296652,0.10510647085423555,1.8809305595599317,1.7051997553732492,0.10510647085423555,1.8128865579674334,1.7777257664087562,0.10510647085423555,1.7418251856114602,1.84729292834835,0.10510647085423555,1.6678647171333096,1.9137854535165058,0.10510647085423555,1.591128252433668,1.9770926716671717,0.10510647085423555,1.5117435117849725,2.0371092141834746,0.10510647085423555,1.4298426232536166,2.0937351894533776,0.10510647085423555,1.3455619027858308,2.146876349129416,0.10510647085423555,1.2590416273232379,2.196444244995764,0.10510647085423555,1.1704258013257332,2.242356376181566,0.10510647085423555,1.0798619170902697,2.2845363264754934,0.10510647085423555,0.9875007092644811,2.322913891512988,0.10510647085423555,0.893495903963741,2.3574251956244923,0.10510647085423555,0.7980039629092206,2.388012798150206,0.10510647085423555,0.7011838230127811,2.4146257890443894,0.10510647085423555,0.6031966318421854,2.4372198736101134,0.10510647085423555,0.5042054794068588,2.4557574462234086,0.10510647085423555,0.4043751267106796,2.4702076529241195,0.10510647085423555,0.3038717315235345,2.48054644276927,0.10510647085423555,0.20286257182811396,2.486756607863491,0.10510647085423555,0.1015157674022235,2.4888278119998573,0.10510647085423555,0.0,2.4801960531718894,0.13932605828824607,0.0,2.4781320323908766,0.13932605828824607,-0.10116369016440648,2.471943405406732,0.13932605828824607,-0.20215900335029446,2.461640472578321,0.13932605828824607,-0.30281784282600926,2.4472403821205475,0.13932605828824607,-0.4029726718871805,2.4287671015628463,0.13932605828824607,-0.5024567927050324,2.406251377857593,0.13932605828824607,-0.6011046237784683,2.3797306862048364,0.13932605828824607,-0.6987519755281388,2.349249167678528,0.13932605828824607,-0.7952363235737924,2.314857555758059,0.13932605828824607,-0.8903970792400666,2.276613091887392,0.13932605828824607,-0.9840758568404908,2.2345794302023254,0.13932605828824607,-1.0761167372948293,2.1888265315844664,0.13932605828824607,-1.1663665276410036,2.1394305472182498,0.13932605828824607,-1.2546750160096591,2.086473691844804,0.13932605828824607,-1.340895221636997,2.030044106923631,0.13932605828824607,-1.4248836394997508,1.9702357139298425,0.13932605828824607,-1.5065004791651353,1.9071480580311366,0.13932605828824607,-1.5856098974582247,1.840886142404689,0.13932605828824607,-1.6620802245595128,1.7715602534697263,0.13932605828824607,-1.7357841831563325,1.6992857773266667,0.13932605828824607,-1.8065991002833817,1.6241830077083426,0.13932605828824607,-1.8744071114997667,1.5463769457629544,0.13932605828824607,-1.9390953570627345,1.4659970920019945,0.13932605828824607,-2.000556169771575,1.3831772307594268,0.13932605828824607,-2.0586872541690497,1.2980552075208667,0.13932605828824607,-2.113391856802088,1.210772699493374,0.13932605828824607,-2.164578927258358,1.1214749797977228,0.13932605828824607,-2.2121632697106954,1.030310675675628,0.13932605828824607,-2.256065684717147,0.9374315211143719,0.13932605828824607,-2.296213101040627,0.8429921043005564,0.13932605828824607,-2.33253869726878,0.7471496103233196,0.13932605828824607,-2.3649820130316277,0.6500635595552713,0.13932605828824607,-2.393489049631886,0.5518955421465738,0.13932605828824607,-2.4180123599204695,0.4528089490740838,0.13932605828824607,-2.4385111272675877,0.35296870019319804,0.13932605828824607,-2.454951233497998,0.25254096974502666,0.13932605828824607,-2.4673053156773403,0.15169290977577235,0.13932605828824607,-2.4755528116550405,0.050592371928642965,0.13932605828824607,-2.479679994287983,-0.05059237192864267,0.13932605828824607,-2.479679994287983,-0.15169290977577204,0.13932605828824607,-2.475552811655041,-0.2525409697450258,0.13932605828824607,-2.4673053156773403,-0.35296870019319776,0.13932605828824607,-2.454951233497998,-0.45280894907408353,0.13932605828824607,-2.4385111272675877,-0.5518955421465729,0.13932605828824607,-2.4180123599204695,-0.6500635595552711,0.13932605828824607,-2.393489049631886,-0.7471496103233187,0.13932605828824607,-2.3649820130316277,-0.8429921043005562,0.13932605828824607,-2.33253869726878,-0.9374315211143717,0.13932605828824607,-2.2962131010406273,-1.030310675675627,0.13932605828824607,-2.256065684717147,-1.121474979797723,0.13932605828824607,-2.2121632697106954,-1.2107726994933738,0.13932605828824607,-2.164578927258358,-1.2980552075208673,0.13932605828824607,-2.1133918568020875,-1.3831772307594261,0.13932605828824607,-2.05868725416905,-1.4659970920019938,0.13932605828824607,-2.000556169771575,-1.5463769457629541,0.13932605828824607,-1.939095357062735,-1.6241830077083428,0.13932605828824607,-1.8744071114997667,-1.699285777326667,0.13932605828824607,-1.8065991002833817,-1.7715602534697263,0.13932605828824607,-1.7357841831563325,-1.840886142404689,0.13932605828824607,-1.6620802245595128,-1.9071480580311362,0.13932605828824607,-1.5856098974582256,-1.9702357139298425,0.13932605828824607,-1.506500479165135,-2.0300441069236306,0.13932605828824607,-1.4248836394997515,-2.0864736918448044,0.13932605828824607,-1.3408952216369967,-2.1394305472182493,0.13932605828824607,-1.2546750160096596,-2.1888265315844664,0.13932605828824607,-1.166366527641004,-2.234579430202325,0.13932605828824607,-1.0761167372948297,-2.276613091887392,0.13932605828824607,-0.984075856840491,-2.314857555758059,0.13932605828824607,-0.8903970792400667,-2.349249167678528,0.13932605828824607,-0.7952363235737924,-2.379730686204836,0.13932605828824607,-0.6987519755281398,-2.406251377857593,0.13932605828824607,-0.6011046237784681,-2.4287671015628463,0.13932605828824607,-0.5024567927050332,-2.4472403821205475,0.13932605828824607,-0.4029726718871812,-2.461640472578321,0.13932605828824607,-0.3028178428260099,-2.471943405406732,0.13932605828824607,-0.202159003350295,-2.4781320323908766,0.13932605828824607,-0.10116369016440688,-2.4801960531718894,0.13932605828824607,-3.037364157774853e-16,-2.4781320323908766,0.13932605828824607,0.10116369016440627,-2.471943405406732,0.13932605828824607,0.20215900335029438,-2.461640472578321,0.13932605828824607,0.3028178428260093,-2.447240382120548,0.13932605828824607,0.40297267188717956,-2.4287671015628467,0.13932605828824607,0.5024567927050315,-2.406251377857593,0.13932605828824607,0.6011046237784686,-2.3797306862048364,0.13932605828824607,0.6987519755281393,-2.349249167678528,0.13932605828824607,0.7952363235737918,-2.3148575557580595,0.13932605828824607,0.8903970792400662,-2.276613091887392,0.13932605828824607,0.9840758568404905,-2.234579430202326,0.13932605828824607,1.0761167372948282,-2.1888265315844664,0.13932605828824607,1.1663665276410033,-2.1394305472182498,0.13932605828824607,1.254675016009659,-2.086473691844804,0.13932605828824607,1.340895221636997,-2.030044106923631,0.13932605828824607,1.4248836394997502,-1.970235713929843,0.13932605828824607,1.5065004791651346,-1.9071480580311364,0.13932605828824607,1.585609897458225,-1.8408861424046887,0.13932605828824607,1.6620802245595132,-1.7715602534697268,0.13932605828824607,1.7357841831563323,-1.6992857773266672,0.13932605828824607,1.8065991002833812,-1.624183007708344,0.13932605828824607,1.8744071114997656,-1.5463769457629544,0.13932605828824607,1.9390953570627345,-1.4659970920019934,0.13932605828824607,2.0005561697715755,-1.3831772307594268,0.13932605828824607,2.0586872541690497,-1.2980552075208678,0.13932605828824607,2.1133918568020875,-1.2107726994933743,0.13932605828824607,2.164578927258358,-1.1214749797977217,0.13932605828824607,2.2121632697106963,-1.0303106756756277,0.13932605828824607,2.256065684717147,-0.9374315211143728,0.13932605828824607,2.296213101040627,-0.8429921043005557,0.13932605828824607,2.33253869726878,-0.7471496103233198,0.13932605828824607,2.3649820130316277,-0.6500635595552727,0.13932605828824607,2.3934890496318855,-0.5518955421465741,0.13932605828824607,2.4180123599204695,-0.45280894907408303,0.13932605828824607,2.4385111272675877,-0.35296870019319776,0.13932605828824607,2.454951233497998,-0.25254096974502754,0.13932605828824607,2.46730531567734,-0.1516929097757721,0.13932605828824607,2.475552811655041,-0.050592371928641626,0.13932605828824607,2.479679994287983,0.05059237192864292,0.13932605828824607,2.479679994287983,0.15169290977577118,0.13932605828824607,2.475552811655041,0.25254096974502666,0.13932605828824607,2.4673053156773403,0.35296870019319687,0.13932605828824607,2.454951233497998,0.45280894907408215,0.13932605828824607,2.438511127267588,0.5518955421465732,0.13932605828824607,2.4180123599204695,0.6500635595552718,0.13932605828824607,2.393489049631886,0.7471496103233191,0.13932605828824607,2.3649820130316277,0.8429921043005549,0.13932605828824607,2.3325386972687805,0.9374315211143699,0.13932605828824607,2.2962131010406277,1.0303106756756288,0.13932605828824607,2.2560656847171465,1.1214749797977228,0.13932605828824607,2.2121632697106954,1.2107726994933736,0.13932605828824607,2.1645789272583587,1.2980552075208653,0.13932605828824607,2.113391856802089,1.383177230759426,0.13932605828824607,2.05868725416905,1.4659970920019945,0.13932605828824607,2.0005561697715746,1.546376945762954,0.13932605828824607,1.939095357062735,1.6241830077083435,0.13932605828824607,1.8744071114997665,1.6992857773266663,0.13932605828824607,1.8065991002833819,1.7715602534697255,0.13932605828824607,1.7357841831563336,1.8408861424046887,0.13932605828824607,1.6620802245595132,1.9071480580311373,0.13932605828824607,1.5856098974582238,1.970235713929842,0.13932605828824607,1.5065004791651353,2.03004410692363,0.13932605828824607,1.424883639499752,2.086473691844803,0.13932605828824607,1.3408952216369987,2.1394305472182493,0.13932605828824607,1.2546750160096598,2.188826531584467,0.13932605828824607,1.1663665276410033,2.234579430202325,0.13932605828824607,1.07611673729483,2.2766130918873912,0.13932605828824607,0.9840758568404924,2.314857555758059,0.13932605828824607,0.8903970792400672,2.3492491676785274,0.13932605828824607,0.7952363235737938,2.3797306862048364,0.13932605828824607,0.6987519755281392,2.4062513778575925,0.13932605828824607,0.6011046237784695,2.4287671015628463,0.13932605828824607,0.5024567927050324,2.4472403821205475,0.13932605828824607,0.4029726718871815,2.461640472578321,0.13932605828824607,0.3028178428260113,2.471943405406732,0.13932605828824607,0.20215900335029527,2.4781320323908766,0.13932605828824607,0.10116369016440609,2.4801960531718894,0.13932605828824607,0.0,2.469171981761615,0.17285153030817815,0.0,2.467117135220055,0.17285153030817815,-0.10071403387893943,2.4609560156846535,0.17285153030817815,-0.20126043918787134,2.450698877730841,0.17285153030817815,-0.30147186635799983,2.4363627933524734,0.17285153030817815,-0.40118152335858254,2.417971623547182,0.17285153030817815,-0.5002234533058046,2.395555978602099,0.17285153030817815,-0.5984328106816346,2.3691531671460533,0.17285153030817815,-0.6956461357029197,2.338807134053043,0.17285153030817815,-0.7917016263840642,2.304568387300326,0.17285153030817815,-0.8864394078404654,2.2664939139028792,0.17285153030817815,-0.9797017983844819,2.224647085064135,0.17285153030817815,-1.0713335719710404,2.179097550700866,0.17285153030817815,-1.1611822165560697,2.129921123517775,0.17285153030817815,-1.2490981879377459,2.0771996528247274,0.17285153030817815,-1.3349351586580573,2.021020888306654,0.17285153030817815,-1.4185502615504195,1.9614783339728594,0.17285153030817815,-1.4998043275279735,1.8986710925288275,0.17285153030817815,-1.5785621172167947,1.8327037004295474,0.17285153030817815,-1.654692546048487,1.7636859538889016,0.17285153030817815,-1.7280689024375084,1.6917327261347033,0.17285153030817815,-1.7985690586801013,1.616963776213545,0.17285153030817815,-1.8660756742238007,1.5395035496636857,0.17285153030817815,-1.9304763909692062,1.4594809713877324,0.17285153030817815,-1.9916640202789482,1.377029231069863,0.17285153030817815,-2.049536721382596,1.292285561494744,0.17285153030817815,-2.1039981708805726,1.2053910101371008,0.17285153030817815,-2.154957723064942,1.1164902044021185,0.17285153030817815,-2.2023305607902497,1.025731110907395,0.17285153030817815,-2.2460378366432883,0.9332647892071138,0.17285153030817815,-2.286006804176834,0.8392451403683213,0.17285153030817815,-2.322170938988932,0.743828650817788,0.17285153030817815,-2.354470049446195,0.6471741318857975,0.17285153030817815,-2.3828503768668408,0.5494424554803584,0.17285153030817815,-2.407264684996707,0.4507962863317897,0.17285153030817815,-2.42767233862934,0.3513998112533317,0.17285153030817815,-2.4440393712392816,0.25141846586839617,0.17285153030817815,-2.456338541516003,0.15101865925930139,0.17285153030817815,-2.4645493787043713,0.05036749699577496,0.17285153030817815,-2.468658216676209,-0.050367496995774655,0.17285153030817815,-2.468658216676209,-0.15101865925930108,0.17285153030817815,-2.4645493787043717,-0.25141846586839534,0.17285153030817815,-2.456338541516003,-0.3513998112533314,0.17285153030817815,-2.4440393712392816,-0.4507962863317894,0.17285153030817815,-2.42767233862934,-0.5494424554803575,0.17285153030817815,-2.407264684996707,-0.6471741318857973,0.17285153030817815,-2.3828503768668408,-0.7438286508177873,0.17285153030817815,-2.3544700494461956,-0.839245140368321,0.17285153030817815,-2.3221709389889322,-0.9332647892071135,0.17285153030817815,-2.2860068041768344,-1.0257311109073943,0.17285153030817815,-2.2460378366432883,-1.1164902044021185,0.17285153030817815,-2.2023305607902497,-1.2053910101371006,0.17285153030817815,-2.154957723064942,-1.2922855614947444,0.17285153030817815,-2.103998170880572,-1.3770292310698626,0.17285153030817815,-2.0495367213825966,-1.4594809713877317,0.17285153030817815,-1.9916640202789484,-1.5395035496636855,0.17285153030817815,-1.9304763909692066,-1.6169637762135451,0.17285153030817815,-1.8660756742238007,-1.6917327261347035,0.17285153030817815,-1.7985690586801013,-1.7636859538889016,0.17285153030817815,-1.7280689024375084,-1.8327037004295474,0.17285153030817815,-1.654692546048487,-1.8986710925288268,0.17285153030817815,-1.5785621172167954,-1.9614783339728594,0.17285153030817815,-1.4998043275279733,-2.0210208883066536,0.17285153030817815,-1.4185502615504204,-2.077199652824728,0.17285153030817815,-1.3349351586580571,-2.1299211235177746,0.17285153030817815,-1.249098187937746,-2.179097550700866,0.17285153030817815,-1.1611822165560701,-2.2246470850641344,0.17285153030817815,-1.0713335719710408,-2.2664939139028792,0.17285153030817815,-0.979701798384482,-2.304568387300326,0.17285153030817815,-0.8864394078404655,-2.338807134053043,0.17285153030817815,-0.7917016263840642,-2.3691531671460533,0.17285153030817815,-0.6956461357029207,-2.395555978602099,0.17285153030817815,-0.5984328106816343,-2.417971623547182,0.17285153030817815,-0.5002234533058054,-2.4363627933524734,0.17285153030817815,-0.4011815233585832,-2.450698877730841,0.17285153030817815,-0.3014718663580005,-2.4609560156846535,0.17285153030817815,-0.20126043918787184,-2.467117135220055,0.17285153030817815,-0.10071403387893982,-2.469171981761615,0.17285153030817815,-3.0238635640086884e-16,-2.467117135220055,0.17285153030817815,0.10071403387893922,-2.4609560156846535,0.17285153030817815,0.20126043918787123,-2.450698877730841,0.17285153030817815,0.3014718663579999,-2.436362793352474,0.17285153030817815,0.40118152335858154,-2.4179716235471824,0.17285153030817815,0.5002234533058038,-2.395555978602099,0.17285153030817815,0.5984328106816348,-2.3691531671460533,0.17285153030817815,0.6956461357029201,-2.338807134053043,0.17285153030817815,0.7917016263840636,-2.304568387300326,0.17285153030817815,0.8864394078404649,-2.2664939139028792,0.17285153030817815,0.9797017983844815,-2.2246470850641353,0.17285153030817815,1.0713335719710393,-2.179097550700866,0.17285153030817815,1.1611822165560695,-2.129921123517775,0.17285153030817815,1.2490981879377456,-2.0771996528247274,0.17285153030817815,1.3349351586580573,-2.0210208883066545,0.17285153030817815,1.418550261550419,-1.96147833397286,0.17285153030817815,1.4998043275279727,-1.898671092528827,0.17285153030817815,1.578562117216795,-1.8327037004295472,0.17285153030817815,1.6546925460484874,-1.7636859538889023,0.17285153030817815,1.7280689024375082,-1.6917327261347037,0.17285153030817815,1.798569058680101,-1.6169637762135463,0.17285153030817815,1.8660756742237998,-1.5395035496636857,0.17285153030817815,1.9304763909692062,-1.4594809713877313,0.17285153030817815,1.9916640202789488,-1.377029231069863,0.17285153030817815,2.049536721382596,-1.292285561494745,0.17285153030817815,2.1039981708805717,-1.205391010137101,0.17285153030817815,2.154957723064942,-1.1164902044021172,0.17285153030817815,2.2023305607902506,-1.0257311109073948,0.17285153030817815,2.2460378366432883,-0.9332647892071145,0.17285153030817815,2.2860068041768336,-0.8392451403683207,0.17285153030817815,2.3221709389889322,-0.7438286508177883,0.17285153030817815,2.354470049446195,-0.6471741318857989,0.17285153030817815,2.3828503768668403,-0.5494424554803587,0.17285153030817815,2.407264684996707,-0.4507962863317889,0.17285153030817815,2.42767233862934,-0.3513998112533314,0.17285153030817815,2.4440393712392816,-0.251418465868397,0.17285153030817815,2.4563385415160024,-0.15101865925930114,0.17285153030817815,2.4645493787043717,-0.05036749699577362,0.17285153030817815,2.468658216676209,0.050367496995774905,0.17285153030817815,2.468658216676209,0.15101865925930022,0.17285153030817815,2.4645493787043717,0.2514184658683961,0.17285153030817815,2.456338541516003,0.35139981125333053,0.17285153030817815,2.444039371239282,0.45079628633178803,0.17285153030817815,2.42767233862934,0.5494424554803579,0.17285153030817815,2.407264684996707,0.6471741318857981,0.17285153030817815,2.3828503768668408,0.7438286508177875,0.17285153030817815,2.3544700494461956,0.8392451403683198,0.17285153030817815,2.3221709389889322,0.9332647892071118,0.17285153030817815,2.286006804176835,1.0257311109073959,0.17285153030817815,2.246037836643288,1.1164902044021183,0.17285153030817815,2.2023305607902497,1.2053910101371004,0.17285153030817815,2.1549577230649426,1.2922855614947426,0.17285153030817815,2.1039981708805735,1.3770292310698622,0.17285153030817815,2.0495367213825966,1.4594809713877324,0.17285153030817815,1.991664020278948,1.5395035496636853,0.17285153030817815,1.9304763909692066,1.6169637762135458,0.17285153030817815,1.8660756742238005,1.691732726134703,0.17285153030817815,1.7985690586801015,1.763685953888901,0.17285153030817815,1.7280689024375095,1.8327037004295472,0.17285153030817815,1.6546925460484874,1.898671092528828,0.17285153030817815,1.5785621172167938,1.9614783339728592,0.17285153030817815,1.4998043275279735,2.021020888306653,0.17285153030817815,1.4185502615504206,2.0771996528247265,0.17285153030817815,1.334935158658059,2.1299211235177746,0.17285153030817815,1.2490981879377463,2.1790975507008663,0.17285153030817815,1.1611822165560695,2.2246470850641344,0.17285153030817815,1.071333571971041,2.266493913902879,0.17285153030817815,0.9797017983844833,2.304568387300326,0.17285153030817815,0.8864394078404659,2.3388071340530425,0.17285153030817815,0.7917016263840656,2.3691531671460533,0.17285153030817815,0.69564613570292,2.395555978602099,0.17285153030817815,0.5984328106816357,2.417971623547182,0.17285153030817815,0.5002234533058046,2.4363627933524734,0.17285153030817815,0.4011815233585835,2.450698877730841,0.17285153030817815,0.3014718663580019,2.4609560156846535,0.17285153030817815,0.20126043918787212,2.467117135220055,0.17285153030817815,0.10071403387893904,2.469171981761615,0.17285153030817815,0.0,2.455810519138271,0.20551586469881092,0.0,2.453766792014664,0.20551586469881092,-0.10016903871081852,2.447639012225946,0.20551586469881092,-0.20017135594230173,2.43743737885683,0.20551586469881092,-0.29984050770656306,2.423178871519269,0.20551586469881092,-0.39901060453675646,2.4048872220915647,0.20551586469881092,-0.49751658759372214,2.382592875219003,0.20551586469881092,-0.5951945033901349,2.356332937641742,0.20551586469881092,-0.6918817766749039,2.3261511164343087,0.20551586469881092,-0.7874174810236328,2.2920976462594833,0.20551586469881092,-0.8816426066847721,2.254229205757664,0.20551586469881092,-0.9744003252356582,2.2126088232108603,0.20551586469881092,-1.0655362506079444,2.167305771638341,0.20551586469881092,-1.1548986960479777,2.1183954534985285,0.20551586469881092,-1.2423389265844278,2.065959275189048,0.20551586469881092,-1.3277114065829703,2.010084511553812,0.20551586469881092,-1.4108740419759864,1.9508641606226584,0.20551586469881092,-1.491688416764116,1.888396788825305,0.20551586469881092,-1.5700200233960269,1.8227863669372555,0.20551586469881092,-1.6457384866429616,1.754142097030705,0.20551586469881092,-1.7187177805954372,1.6825782307184667,0.20551586469881092,-1.7888364384209359,1.6082138789934395,0.20551586469881092,-1.8559777545334584,1.5311728139801144,0.20551586469881092,-1.920029978838456,1.4515832629280867,0.20551586469881092,-1.9808865027298344,1.369577694790456,0.20551586469881092,-2.038446036529455,1.2852925997423268,0.20551586469881092,-2.092612778073812,1.1988682620063809,0.20551586469881092,-2.1432965721672734,1.1104485263636348,0.20551586469881092,-2.190413060636513,1.0201805587379944,0.20551586469881092,-2.233883822736361,0.9282146012531028,0.20551586469881092,-2.2736365056733954,0.8347037221691508,0.20551586469881092,-2.309604945030026,0.7398035611158615,0.20551586469881092,-2.34172927488864,0.6436720700456912,0.20551586469881092,-2.3699560274725098,0.5464692503383896,0.20551586469881092,-2.394238222137635,0.44835688649449396,0.20551586469881092,-2.414535443567385,0.34949827686099594,0.20551586469881092,-2.4308139090398053,0.2500579618373556,0.20551586469881092,-2.443046524655626,0.1502014500142487,0.20551586469881092,-2.4512129304333805,0.05009494270084881,0.20551586469881092,-2.4552995341965884,-0.050094942700848515,0.20551586469881092,-2.4552995341965884,-0.1502014500142484,0.20551586469881092,-2.451212930433381,-0.2500579618373548,0.20551586469881092,-2.443046524655626,-0.34949827686099566,0.20551586469881092,-2.4308139090398053,-0.4483568864944937,0.20551586469881092,-2.414535443567385,-0.5464692503383887,0.20551586469881092,-2.394238222137635,-0.6436720700456909,0.20551586469881092,-2.3699560274725098,-0.7398035611158608,0.20551586469881092,-2.34172927488864,-0.8347037221691505,0.20551586469881092,-2.3096049450300264,-0.9282146012531026,0.20551586469881092,-2.273636505673396,-1.0201805587379937,0.20551586469881092,-2.2338838227363613,-1.110448526363635,0.20551586469881092,-2.190413060636513,-1.1988682620063809,0.20551586469881092,-2.1432965721672734,-1.2852925997423275,0.20551586469881092,-2.092612778073811,-1.3695776947904557,0.20551586469881092,-2.0384460365294554,-1.451583262928086,0.20551586469881092,-1.9808865027298348,-1.531172813980114,0.20551586469881092,-1.9200299788384567,-1.60821387899344,0.20551586469881092,-1.8559777545334584,-1.682578230718467,0.20551586469881092,-1.7888364384209359,-1.754142097030705,0.20551586469881092,-1.7187177805954372,-1.8227863669372555,0.20551586469881092,-1.6457384866429616,-1.8883967888253044,0.20551586469881092,-1.5700200233960278,-1.9508641606226584,0.20551586469881092,-1.4916884167641158,-2.0100845115538117,0.20551586469881092,-1.410874041975987,-2.065959275189048,0.20551586469881092,-1.32771140658297,-2.118395453498528,0.20551586469881092,-1.2423389265844282,-2.167305771638341,0.20551586469881092,-1.154898696047978,-2.2126088232108603,0.20551586469881092,-1.0655362506079449,-2.254229205757664,0.20551586469881092,-0.9744003252356583,-2.2920976462594833,0.20551586469881092,-0.8816426066847722,-2.3261511164343087,0.20551586469881092,-0.7874174810236328,-2.356332937641742,0.20551586469881092,-0.6918817766749048,-2.382592875219003,0.20551586469881092,-0.5951945033901347,-2.4048872220915642,0.20551586469881092,-0.49751658759372286,-2.423178871519269,0.20551586469881092,-0.39901060453675713,-2.43743737885683,0.20551586469881092,-0.2998405077065637,-2.447639012225946,0.20551586469881092,-0.20017135594230223,-2.453766792014664,0.20551586469881092,-0.1001690387108189,-2.455810519138271,0.20551586469881092,-3.0075004915750833e-16,-2.453766792014664,0.20551586469881092,0.10016903871081831,-2.447639012225946,0.20551586469881092,0.20017135594230162,-2.43743737885683,0.20551586469881092,0.2998405077065631,-2.423178871519269,0.20551586469881092,0.3990106045367555,-2.404887222091565,0.20551586469881092,0.49751658759372125,-2.382592875219003,0.20551586469881092,0.5951945033901352,-2.356332937641742,0.20551586469881092,0.6918817766749042,-2.3261511164343087,0.20551586469881092,0.7874174810236323,-2.2920976462594838,0.20551586469881092,0.8816426066847717,-2.254229205757664,0.20551586469881092,0.9744003252356578,-2.2126088232108607,0.20551586469881092,1.0655362506079433,-2.167305771638341,0.20551586469881092,1.1548986960479775,-2.1183954534985285,0.20551586469881092,1.2423389265844276,-2.065959275189048,0.20551586469881092,1.3277114065829703,-2.010084511553812,0.20551586469881092,1.4108740419759858,-1.950864160622659,0.20551586469881092,1.4916884167641153,-1.8883967888253046,0.20551586469881092,1.570020023396027,-1.8227863669372553,0.20551586469881092,1.6457384866429619,-1.7541420970307053,0.20551586469881092,1.718717780595437,-1.6825782307184673,0.20551586469881092,1.7888364384209356,-1.6082138789934408,0.20551586469881092,1.8559777545334573,-1.5311728139801144,0.20551586469881092,1.920029978838456,-1.4515832629280856,0.20551586469881092,1.980886502729835,-1.369577694790456,0.20551586469881092,2.038446036529455,-1.285292599742328,0.20551586469881092,2.092612778073811,-1.1988682620063813,0.20551586469881092,2.143296572167273,-1.1104485263636337,0.20551586469881092,2.190413060636514,-1.0201805587379942,0.20551586469881092,2.2338838227363613,-0.9282146012531037,0.20551586469881092,2.273636505673395,-0.83470372216915,0.20551586469881092,2.3096049450300264,-0.7398035611158618,0.20551586469881092,2.34172927488864,-0.6436720700456926,0.20551586469881092,2.3699560274725093,-0.5464692503383899,0.20551586469881092,2.394238222137635,-0.44835688649449323,0.20551586469881092,2.414535443567385,-0.34949827686099566,0.20551586469881092,2.4308139090398053,-0.25005796183735646,0.20551586469881092,2.443046524655626,-0.15020145001424845,0.20551586469881092,2.451212930433381,-0.05009494270084748,0.20551586469881092,2.4552995341965884,0.050094942700848764,0.20551586469881092,2.4552995341965884,0.15020145001424753,0.20551586469881092,2.451212930433381,0.25005796183735557,0.20551586469881092,2.443046524655626,0.34949827686099477,0.20551586469881092,2.4308139090398058,0.44835688649449235,0.20551586469881092,2.4145354435673854,0.546469250338389,0.20551586469881092,2.394238222137635,0.6436720700456917,0.20551586469881092,2.3699560274725098,0.739803561115861,0.20551586469881092,2.34172927488864,0.8347037221691492,0.20551586469881092,2.3096049450300264,0.9282146012531008,0.20551586469881092,2.2736365056733963,1.0201805587379953,0.20551586469881092,2.233883822736361,1.1104485263636346,0.20551586469881092,2.190413060636513,1.1988682620063806,0.20551586469881092,2.143296572167274,1.2852925997423255,0.20551586469881092,2.0926127780738124,1.3695776947904554,0.20551586469881092,2.0384460365294554,1.4515832629280867,0.20551586469881092,1.9808865027298341,1.5311728139801137,0.20551586469881092,1.9200299788384567,1.6082138789934404,0.20551586469881092,1.8559777545334581,1.6825782307184665,0.20551586469881092,1.788836438420936,1.754142097030704,0.20551586469881092,1.7187177805954383,1.8227863669372553,0.20551586469881092,1.6457384866429619,1.8883967888253055,0.20551586469881092,1.5700200233960262,1.9508641606226582,0.20551586469881092,1.491688416764116,2.0100845115538113,0.20551586469881092,1.4108740419759875,2.0659592751890465,0.20551586469881092,1.3277114065829718,2.118395453498528,0.20551586469881092,1.2423389265844285,2.1673057716383415,0.20551586469881092,1.1548986960479772,2.2126088232108603,0.20551586469881092,1.065536250607945,2.254229205757663,0.20551586469881092,0.9744003252356597,2.2920976462594833,0.20551586469881092,0.8816426066847727,2.3261511164343083,0.20551586469881092,0.7874174810236342,2.356332937641742,0.20551586469881092,0.6918817766749041,2.382592875219003,0.20551586469881092,0.5951945033901361,2.4048872220915647,0.20551586469881092,0.49751658759372214,2.423178871519269,0.20551586469881092,0.3990106045367574,2.43743737885683,0.20551586469881092,0.29984050770656506,2.447639012225946,0.20551586469881092,0.2001713559423025,2.453766792014664,0.20551586469881092,0.10016903871081811,2.455810519138271,0.20551586469881092,0.0,2.440178231436546,0.2371563293892761,0.0,2.4381475135130097,0.2371563293892761,-0.09953141979855723,2.4320587396719753,0.2371563293892761,-0.19889717937153253,2.421922044076605,0.2371563293892761,-0.29793189421844063,2.407754298256335,0.2371563293892761,-0.3964707308300722,2.3895790830258807,0.2371563293892761,-0.49434968103760285,2.367426649237296,0.2371563293892761,-0.5914058349880039,2.3413338674304165,0.2371563293892761,-0.687477652291416,2.3113441664654855,0.2371563293892761,-0.782405230889185,2.2775074612401003,0.2371563293892761,-0.8760305731950584,2.239880069610792,0.2371563293892761,-0.9681978490665752,2.1985246186575123,0.2371563293892761,-1.0587536551689585,2.1535099404470373,0.2371563293892761,-1.1475472702998282,2.1049109574687876,0.2371563293892761,-1.2344309062497667,2.052808557933738,0.2371563293892761,-1.3192599537812084,1.9972894611439715,0.2371563293892761,-1.4018932233162456,1.9384460731569615,0.2371563293892761,-1.4821931799327461,1.8763763329848073,0.2371563293892761,-1.5600261722776565,1.811183549584415,0.2371563293892761,-1.6352626550164924,1.742976229909933,0.2371563293892761,-1.7077774044487604,1.671867898313636,0.2371563293892761,-1.777449726930451,1.5979769075958439,0.2371563293892761,-1.8441636597566964,1.521426242018367,0.2371563293892761,-1.9078081641702496,1.4423433126093368,0.2371563293892761,-1.9682773101745379,1.3608597451001265,0.2371563293892761,-2.0254704528436864,1.2771111608473096,0.2371563293892761,-2.079292399836066,1.1912369511042975,0.2371563293892761,-2.1296535698325507,1.103380045018354,0.2371563293892761,-2.176470141635783,1.0136866717391328,0.2371563293892761,-2.2196641936822847,0.9223061170346941,0.2371563293892761,-2.2591638337352107,0.8293904748200728,0.2371563293892761,-2.2949033185418775,0.7350943940119671,0.2371563293892761,-2.3268231632569263,0.6395748211308808,0.2371563293892761,-2.35487024044897,0.5429907390791279,0.2371563293892761,-2.3789978685259663,0.44550290252948066,0.2371563293892761,-2.3991658894321164,0.3472735703648835,0.2371563293892761,-2.415340735486986,0.24846623561455233,0.2371563293892761,-2.4274954852556005,0.1492453533359663,0.2371563293892761,-2.435609908356511,0.04977606689565196,0.2371563293892761,-2.439670499133272,-0.049776066895651665,0.2371563293892761,-2.439670499133272,-0.149245353335966,0.2371563293892761,-2.4356099083565113,-0.2484662356145515,0.2371563293892761,-2.4274954852556005,-0.3472735703648832,0.2371563293892761,-2.415340735486986,-0.4455029025294804,0.2371563293892761,-2.3991658894321164,-0.542990739079127,0.2371563293892761,-2.3789978685259663,-0.6395748211308805,0.2371563293892761,-2.3548702404489705,-0.7350943940119662,0.2371563293892761,-2.3268231632569263,-0.8293904748200726,0.2371563293892761,-2.294903318541878,-0.9223061170346939,0.2371563293892761,-2.2591638337352107,-1.0136866717391322,0.2371563293892761,-2.219664193682285,-1.1033800450183542,0.2371563293892761,-2.176470141635783,-1.1912369511042975,0.2371563293892761,-2.1296535698325507,-1.27711116084731,0.2371563293892761,-2.0792923998360657,-1.3608597451001259,0.2371563293892761,-2.025470452843687,-1.4423433126093363,0.2371563293892761,-1.9682773101745383,-1.5214262420183666,0.2371563293892761,-1.90780816417025,-1.597976907595844,0.2371563293892761,-1.8441636597566964,-1.6718678983136361,0.2371563293892761,-1.777449726930451,-1.742976229909933,0.2371563293892761,-1.7077774044487604,-1.811183549584415,0.2371563293892761,-1.6352626550164924,-1.8763763329848067,0.2371563293892761,-1.5600261722776572,-1.9384460731569615,0.2371563293892761,-1.482193179932746,-1.997289461143971,0.2371563293892761,-1.4018932233162464,-2.052808557933738,0.2371563293892761,-1.3192599537812082,-2.1049109574687876,0.2371563293892761,-1.234430906249767,-2.153509940447037,0.2371563293892761,-1.1475472702998286,-2.198524618657512,0.2371563293892761,-1.058753655168959,-2.2398800696107917,0.2371563293892761,-0.9681978490665754,-2.2775074612401003,0.2371563293892761,-0.8760305731950585,-2.3113441664654855,0.2371563293892761,-0.782405230889185,-2.3413338674304165,0.2371563293892761,-0.6874776522914169,-2.367426649237296,0.2371563293892761,-0.5914058349880037,-2.3895790830258803,0.2371563293892761,-0.4943496810376036,-2.407754298256335,0.2371563293892761,-0.3964707308300729,-2.421922044076605,0.2371563293892761,-0.29793189421844124,-2.4320587396719753,0.2371563293892761,-0.19889717937153303,-2.4381475135130097,0.2371563293892761,-0.09953141979855762,-2.440178231436546,0.2371563293892761,-2.988356460477815e-16,-2.4381475135130097,0.2371563293892761,0.09953141979855702,-2.4320587396719753,0.2371563293892761,0.19889717937153242,-2.421922044076605,0.2371563293892761,0.29793189421844063,-2.4077542982563354,0.2371563293892761,0.3964707308300713,-2.389579083025881,0.2371563293892761,0.49434968103760196,-2.367426649237296,0.2371563293892761,0.5914058349880043,-2.3413338674304165,0.2371563293892761,0.6874776522914164,-2.3113441664654855,0.2371563293892761,0.7824052308891845,-2.2775074612401003,0.2371563293892761,0.876030573195058,-2.239880069610792,0.2371563293892761,0.9681978490665748,-2.1985246186575127,0.2371563293892761,1.0587536551689574,-2.1535099404470373,0.2371563293892761,1.147547270299828,-2.1049109574687876,0.2371563293892761,1.2344309062497665,-2.052808557933738,0.2371563293892761,1.3192599537812084,-1.9972894611439718,0.2371563293892761,1.401893223316245,-1.938446073156962,0.2371563293892761,1.4821931799327452,-1.876376332984807,0.2371563293892761,1.5600261722776567,-1.8111835495844149,0.2371563293892761,1.6352626550164926,-1.7429762299099336,0.2371563293892761,1.7077774044487601,-1.6718678983136366,0.2371563293892761,1.7774497269304508,-1.5979769075958452,0.2371563293892761,1.8441636597566953,-1.521426242018367,0.2371563293892761,1.9078081641702496,-1.4423433126093357,0.2371563293892761,1.9682773101745386,-1.3608597451001265,0.2371563293892761,2.0254704528436864,-1.2771111608473107,0.2371563293892761,2.0792923998360653,-1.1912369511042977,0.2371563293892761,2.1296535698325503,-1.1033800450183529,0.2371563293892761,2.1764701416357832,-1.0136866717391328,0.2371563293892761,2.219664193682285,-0.9223061170346949,0.2371563293892761,2.2591638337352102,-0.8293904748200721,0.2371563293892761,2.294903318541878,-0.7350943940119673,0.2371563293892761,2.3268231632569263,-0.6395748211308822,0.2371563293892761,2.3548702404489696,-0.5429907390791282,0.2371563293892761,2.3789978685259663,-0.4455029025294799,0.2371563293892761,2.3991658894321164,-0.3472735703648832,0.2371563293892761,2.415340735486986,-0.24846623561455317,0.2371563293892761,2.4274954852556,-0.14924535333596606,0.2371563293892761,2.4356099083565113,-0.049776066895650645,0.2371563293892761,2.439670499133272,0.049776066895651915,0.2371563293892761,2.439670499133272,0.14924535333596517,0.2371563293892761,2.4356099083565113,0.24846623561455228,0.2371563293892761,2.4274954852556005,0.3472735703648823,0.2371563293892761,2.4153407354869865,0.445502902529479,0.2371563293892761,2.3991658894321164,0.5429907390791273,0.2371563293892761,2.3789978685259663,0.6395748211308814,0.2371563293892761,2.35487024044897,0.7350943940119665,0.2371563293892761,2.3268231632569263,0.8293904748200713,0.2371563293892761,2.2949033185418783,0.9223061170346921,0.2371563293892761,2.259163833735211,1.013686671739134,0.2371563293892761,2.2196641936822847,1.103380045018354,0.2371563293892761,2.176470141635783,1.191236951104297,0.2371563293892761,2.1296535698325507,1.2771111608473082,0.2371563293892761,2.079292399836067,1.3608597451001256,0.2371563293892761,2.025470452843687,1.4423433126093368,0.2371563293892761,1.9682773101745377,1.5214262420183664,0.2371563293892761,1.90780816417025,1.5979769075958448,0.2371563293892761,1.844163659756696,1.6718678983136357,0.2371563293892761,1.7774497269304512,1.7429762299099323,0.2371563293892761,1.7077774044487615,1.8111835495844149,0.2371563293892761,1.6352626550164926,1.8763763329848078,0.2371563293892761,1.5600261722776556,1.938446073156961,0.2371563293892761,1.4821931799327461,1.9972894611439707,0.2371563293892761,1.4018932233162467,2.0528085579337367,0.2371563293892761,1.3192599537812102,2.1049109574687876,0.2371563293892761,1.2344309062497674,2.1535099404470377,0.2371563293892761,1.147547270299828,2.198524618657512,0.2371563293892761,1.0587536551689591,2.2398800696107912,0.2371563293892761,0.9681978490665767,2.2775074612401003,0.2371563293892761,0.876030573195059,2.311344166465485,0.2371563293892761,0.7824052308891863,2.3413338674304165,0.2371563293892761,0.6874776522914162,2.367426649237296,0.2371563293892761,0.5914058349880051,2.3895790830258807,0.2371563293892761,0.49434968103760285,2.407754298256335,0.2371563293892761,0.39647073083007317,2.421922044076605,0.2371563293892761,0.29793189421844263,2.4320587396719753,0.2371563293892761,0.1988971793715333,2.4381475135130097,0.2371563293892761,0.09953141979855684,2.440178231436546,0.2371563293892761,0.0,2.422352997927441,0.26761529317604965,0.0,2.4203371141749144,0.26761529317604965,-0.09880435372750325,2.414292818156894,0.26761529317604965,-0.1974442573591463,2.404230170007595,0.26761529317604965,-0.29575553451002407,2.3901659180117893,0.26761529317604965,-0.39357455576157696,2.372123470728933,0.26761529317604965,-0.49073851100660926,2.3501328580319343,0.26761529317604965,-0.5870856804306452,2.324230681125393,0.26761529317604965,-0.6824557036786004,2.2944600516265052,0.26761529317604965,-0.7766898467587666,2.260870519810034,0.26761529317604965,-0.8696312662398759,2.2235179921367636,0.26761529317604965,-0.9611252703015132,2.1824646382027177,0.26761529317604965,-1.0510195762033845,2.137778787263998,0.26761529317604965,-1.1391645637449108,2.089534814509482,0.26761529317604965,-1.225413524293284,2.037813017270663,0.26761529317604965,-1.3096229049655093,1.9826994813746635,0.26761529317604965,-1.3916525475580113,1.9242859378628734,0.26761529317604965,-1.4713659218261324,1.8626696103136844,0.26761529317604965,-1.5486303527252487,1.7979530530234384,0.26761529317604965,-1.6233172412352919,1.7302439803149217,0.26761529317604965,-1.695302278401123,1.6596550872575058,0.26761529317604965,-1.764465652232523,1.5863038620973255,0.26761529317604965,-1.8306922471194251,1.5103123907096896,0.26761529317604965,-1.8938718354304873,1.4318071533991872,0.26761529317604965,-1.9538992609761074,1.350918814385705,0.26761529317604965,-2.0106746140305205,1.2677820043267283,0.26761529317604965,-2.0641033976216807,1.1825350962378982,0.26761529317604965,-2.1140966848121416,1.0953199751847829,0.26761529317604965,-2.1605712667091703,1.0062818021291866,0.26761529317604965,-2.2034497909577375,0.9155687723230578,0.26761529317604965,-2.242660890485878,0.8233318686521162,0.26761529317604965,-2.278139302288136,0.7297246103397378,0.26761529317604965,-2.3098259760494035,0.634902797429362,0.26761529317604965,-2.337668172428335,0.5390242514706924,0.26761529317604965,-2.3616195508367817,0.4422485528413036,0.26761529317604965,-2.3816402465691233,0.34473677514085166,0.26761529317604965,-2.39769693715314,0.246651217099962,0.26761529317604965,-2.4097628978119756,0.1481551324500123,0.26761529317604965,-2.417818045944893,0.04941245820340596,0.26761529317604965,-2.4218489745527814,-0.04941245820340567,0.26761529317604965,-2.4218489745527814,-0.148155132450012,0.26761529317604965,-2.4178180459448932,-0.24665121709996118,0.26761529317604965,-2.4097628978119756,-0.34473677514085144,0.26761529317604965,-2.39769693715314,-0.4422485528413033,0.26761529317604965,-2.3816402465691233,-0.5390242514706917,0.26761529317604965,-2.3616195508367817,-0.6349027974293617,0.26761529317604965,-2.3376681724283355,-0.729724610339737,0.26761529317604965,-2.309825976049404,-0.8233318686521159,0.26761529317604965,-2.2781393022881367,-0.9155687723230576,0.26761529317604965,-2.2426608904858782,-1.006281802129186,0.26761529317604965,-2.203449790957738,-1.095319975184783,0.26761529317604965,-2.1605712667091703,-1.182535096237898,0.26761529317604965,-2.1140966848121416,-1.2677820043267287,0.26761529317604965,-2.0641033976216803,-1.3509188143857043,0.26761529317604965,-2.010674614030521,-1.4318071533991865,0.26761529317604965,-1.9538992609761079,-1.5103123907096894,0.26761529317604965,-1.893871835430488,-1.5863038620973258,0.26761529317604965,-1.8306922471194251,-1.659655087257506,0.26761529317604965,-1.764465652232523,-1.7302439803149217,0.26761529317604965,-1.695302278401123,-1.7979530530234384,0.26761529317604965,-1.6233172412352919,-1.862669610313684,0.26761529317604965,-1.5486303527252496,-1.9242859378628734,0.26761529317604965,-1.4713659218261321,-1.982699481374663,0.26761529317604965,-1.3916525475580122,-2.037813017270663,0.26761529317604965,-1.309622904965509,-2.0895348145094816,0.26761529317604965,-1.2254135242932842,-2.1377787872639975,0.26761529317604965,-1.139164563744911,-2.1824646382027173,0.26761529317604965,-1.051019576203385,-2.2235179921367636,0.26761529317604965,-0.9611252703015134,-2.260870519810034,0.26761529317604965,-0.8696312662398761,-2.2944600516265052,0.26761529317604965,-0.7766898467587666,-2.3242306811253926,0.26761529317604965,-0.6824557036786013,-2.3501328580319343,0.26761529317604965,-0.587085680430645,-2.3721234707289325,0.26761529317604965,-0.49073851100661,-2.3901659180117893,0.26761529317604965,-0.3935745557615776,-2.404230170007595,0.26761529317604965,-0.2957555345100247,-2.414292818156894,0.26761529317604965,-0.1974442573591468,-2.4203371141749144,0.26761529317604965,-0.09880435372750364,-2.422352997927441,0.26761529317604965,-2.966526845316836e-16,-2.4203371141749144,0.26761529317604965,0.09880435372750306,-2.414292818156894,0.26761529317604965,0.1974442573591462,-2.404230170007595,0.26761529317604965,0.2957555345100241,-2.3901659180117893,0.26761529317604965,0.393574555761576,-2.372123470728933,0.26761529317604965,0.49073851100660837,-2.3501328580319343,0.26761529317604965,0.5870856804306455,-2.324230681125393,0.26761529317604965,0.6824557036786008,-2.2944600516265052,0.26761529317604965,0.7766898467587662,-2.260870519810034,0.26761529317604965,0.8696312662398755,-2.2235179921367636,0.26761529317604965,0.9611252703015128,-2.182464638202718,0.26761529317604965,1.0510195762033836,-2.137778787263998,0.26761529317604965,1.1391645637449106,-2.089534814509482,0.26761529317604965,1.2254135242932838,-2.037813017270663,0.26761529317604965,1.3096229049655093,-1.9826994813746637,0.26761529317604965,1.3916525475580108,-1.924285937862874,0.26761529317604965,1.4713659218261317,-1.8626696103136842,0.26761529317604965,1.5486303527252492,-1.7979530530234382,0.26761529317604965,1.623317241235292,-1.730243980314922,0.26761529317604965,1.6953022784011227,-1.6596550872575062,0.26761529317604965,1.7644656522325228,-1.5863038620973269,0.26761529317604965,1.830692247119424,-1.5103123907096896,0.26761529317604965,1.8938718354304873,-1.431807153399186,0.26761529317604965,1.953899260976108,-1.350918814385705,0.26761529317604965,2.0106746140305205,-1.2677820043267292,0.26761529317604965,2.06410339762168,-1.1825350962378984,0.26761529317604965,2.114096684812141,-1.0953199751847817,0.26761529317604965,2.1605712667091708,-1.0062818021291864,0.26761529317604965,2.203449790957738,-0.9155687723230587,0.26761529317604965,2.2426608904858774,-0.8233318686521155,0.26761529317604965,2.2781393022881367,-0.7297246103397381,0.26761529317604965,2.3098259760494035,-0.6349027974293633,0.26761529317604965,2.3376681724283346,-0.5390242514706928,0.26761529317604965,2.3616195508367817,-0.4422485528413029,0.26761529317604965,2.3816402465691233,-0.34473677514085144,0.26761529317604965,2.39769693715314,-0.24665121709996282,0.26761529317604965,2.409762897811975,-0.14815513245001205,0.26761529317604965,2.4178180459448932,-0.04941245820340465,0.26761529317604965,2.4218489745527814,0.04941245820340591,0.26761529317604965,2.4218489745527814,0.14815513245001116,0.26761529317604965,2.4178180459448932,0.24665121709996196,0.26761529317604965,2.4097628978119756,0.34473677514085055,0.26761529317604965,2.3976969371531403,0.442248552841302,0.26761529317604965,2.3816402465691238,0.5390242514706919,0.26761529317604965,2.3616195508367817,0.6349027974293625,0.26761529317604965,2.337668172428335,0.7297246103397373,0.26761529317604965,2.309825976049404,0.8233318686521147,0.26761529317604965,2.2781393022881367,0.9155687723230558,0.26761529317604965,2.2426608904858787,1.0062818021291875,0.26761529317604965,2.2034497909577375,1.0953199751847829,0.26761529317604965,2.1605712667091703,1.1825350962378978,0.26761529317604965,2.1140966848121416,1.2677820043267267,0.26761529317604965,2.0641033976216816,1.350918814385704,0.26761529317604965,2.010674614030521,1.4318071533991872,0.26761529317604965,1.9538992609761072,1.5103123907096891,0.26761529317604965,1.893871835430488,1.5863038620973264,0.26761529317604965,1.830692247119425,1.6596550872575053,0.26761529317604965,1.7644656522325233,1.7302439803149208,0.26761529317604965,1.695302278401124,1.7979530530234382,0.26761529317604965,1.623317241235292,1.862669610313685,0.26761529317604965,1.548630352725248,1.9242859378628732,0.26761529317604965,1.4713659218261324,1.9826994813746626,0.26761529317604965,1.3916525475580124,2.0378130172706617,0.26761529317604965,1.3096229049655108,2.0895348145094816,0.26761529317604965,1.2254135242932847,2.137778787263998,0.26761529317604965,1.1391645637449104,2.1824646382027173,0.26761529317604965,1.0510195762033852,2.223517992136763,0.26761529317604965,0.9611252703015147,2.260870519810034,0.26761529317604965,0.8696312662398764,2.294460051626505,0.26761529317604965,0.7766898467587681,2.324230681125393,0.26761529317604965,0.6824557036786006,2.3501328580319343,0.26761529317604965,0.5870856804306464,2.372123470728933,0.26761529317604965,0.49073851100660926,2.3901659180117893,0.26761529317604965,0.3935745557615779,2.404230170007595,0.26761529317604965,0.29575553451002606,2.414292818156894,0.26761529317604965,0.19744425735914709,2.4203371141749144,0.26761529317604965,0.09880435372750286,2.422352997927441,0.26761529317604965,0.0,2.402423623027663,0.2967410110336781,0.0,2.40042432451399,0.2967410110336781,-0.09799146270425005,2.394429756608037,0.2967410110336781,-0.19581982828952457,2.384449896676485,0.2967410110336781,-0.29332227109590664,2.3705013552112866,0.2967410110336781,-0.39033650792977986,2.35260734818314,0.2967410110336781,-0.4867010681681883,2.330797658400799,0.2967410110336781,-0.582255562510753,2.3051085859405265,0.2967410110336781,-0.6768409499318345,2.2755828877281994,0.2967410110336781,-0.7702998023886231,2.2422697063746253,0.2967410110336781,-0.8624765668445805,2.2052244883825174,0.2967410110336781,-0.9532178241721183,2.164508891861262,0.2967410110336781,-1.0423725445035932,2.12019068390308,0.2967410110336781,-1.129792338605619,2.072343627791391,0.2967410110336781,-1.215331704858299,2.0210473602291072,0.2967410110336781,-1.298848271428314,1.9663872587912004,0.2967410110336781,-1.380203033232789,1.9084542998221532,0.2967410110336781,-1.4592605832995347,1.847344907014815,0.2967410110336781,-1.5358893381385872,1.7831607909226783,0.2967410110336781,-1.6099617567499456,1.7160087796727015,0.2967410110336781,-1.6813545529029765,1.6460006411604347,0.2967410110336781,-1.7499489003341786,1.573252897023386,0.2967410110336781,-1.8156306305217713,1.4978866287022585,0.2967410110336781,-1.8782904227079327,1.4200272759128376,0.2967410110336781,-1.937823985852412,1.3398044278639667,0.2967410110336781,-1.9941322322146722,1.2573516075690974,0.2967410110336781,-2.047121442275658,1.1728060496104105,0.2967410110336781,-2.096703420724683,1.0863084717253986,0.2967410110336781,-2.1427956432518225,0.9980028405960757,0.2967410110336781,-2.1853213939014844,0.9080361322306486,0.2967410110336781,-2.2242098927585423,0.8165580873364524,0.2967410110336781,-2.2593964137545224,0.7237209620913206,0.2967410110336781,-2.2908223923977555,0.6296792747282058,0.2967410110336781,-2.3184355232481932,0.5345895483548283,0.2967410110336781,-2.342189846974652,0.4386100504364106,0.2967410110336781,-2.3620458268495836,0.34190052937510207,0.2967410110336781,-2.3779704145540554,0.24462194862452613,0.2967410110336781,-2.3899371051834195,0.14693621878200072,0.2967410110336781,-2.397925981362104,0.0490059281043255,0.2967410110336781,-2.4019237463941265,-0.049005928104325205,0.2967410110336781,-2.4019237463941265,-0.14693621878200042,0.2967410110336781,-2.3979259813621043,-0.24462194862452533,0.2967410110336781,-2.3899371051834195,-0.3419005293751018,0.2967410110336781,-2.3779704145540554,-0.4386100504364104,0.2967410110336781,-2.3620458268495836,-0.5345895483548275,0.2967410110336781,-2.342189846974652,-0.6296792747282055,0.2967410110336781,-2.3184355232481932,-0.7237209620913199,0.2967410110336781,-2.2908223923977555,-0.8165580873364522,0.2967410110336781,-2.259396413754523,-0.9080361322306484,0.2967410110336781,-2.2242098927585428,-0.998002840596075,0.2967410110336781,-2.1853213939014844,-1.0863084717253986,0.2967410110336781,-2.1427956432518225,-1.1728060496104105,0.2967410110336781,-2.096703420724683,-1.257351607569098,0.2967410110336781,-2.047121442275657,-1.3398044278639663,0.2967410110336781,-1.9941322322146726,-1.4200272759128372,0.2967410110336781,-1.9378239858524122,-1.4978866287022583,0.2967410110336781,-1.8782904227079331,-1.5732528970233863,0.2967410110336781,-1.8156306305217713,-1.6460006411604349,0.2967410110336781,-1.7499489003341786,-1.7160087796727015,0.2967410110336781,-1.6813545529029765,-1.7831607909226783,0.2967410110336781,-1.6099617567499456,-1.8473449070148145,0.2967410110336781,-1.5358893381385879,-1.9084542998221532,0.2967410110336781,-1.4592605832995342,-1.9663872587912,0.2967410110336781,-1.38020303323279,-2.0210473602291077,0.2967410110336781,-1.2988482714283138,-2.072343627791391,0.2967410110336781,-1.2153317048582992,-2.1201906839030795,0.2967410110336781,-1.1297923386056192,-2.1645088918612614,0.2967410110336781,-1.0423725445035936,-2.205224488382517,0.2967410110336781,-0.9532178241721184,-2.2422697063746253,0.2967410110336781,-0.8624765668445807,-2.2755828877281994,0.2967410110336781,-0.7702998023886231,-2.3051085859405265,0.2967410110336781,-0.6768409499318354,-2.330797658400799,0.2967410110336781,-0.5822555625107528,-2.35260734818314,0.2967410110336781,-0.48670106816818903,-2.3705013552112866,0.2967410110336781,-0.39033650792978053,-2.384449896676485,0.2967410110336781,-0.29332227109590725,-2.394429756608037,0.2967410110336781,-0.19581982828952507,-2.40042432451399,0.2967410110336781,-0.09799146270425042,-2.402423623027663,0.2967410110336781,-2.942120400136815e-16,-2.40042432451399,0.2967410110336781,0.09799146270424984,-2.394429756608037,0.2967410110336781,0.19581982828952446,-2.384449896676485,0.2967410110336781,0.2933222710959067,-2.370501355211287,0.2967410110336781,0.3903365079297789,-2.3526073481831404,0.2967410110336781,0.4867010681681874,-2.330797658400799,0.2967410110336781,0.5822555625107534,-2.3051085859405265,0.2967410110336781,0.6768409499318349,-2.2755828877281994,0.2967410110336781,0.7702998023886225,-2.2422697063746253,0.2967410110336781,0.8624765668445802,-2.2052244883825174,0.2967410110336781,0.9532178241721179,-2.1645088918612623,0.2967410110336781,1.042372544503592,-2.12019068390308,0.2967410110336781,1.1297923386056186,-2.072343627791391,0.2967410110336781,1.2153317048582986,-2.0210473602291072,0.2967410110336781,1.298848271428314,-1.9663872587912006,0.2967410110336781,1.3802030332327886,-1.9084542998221539,0.2967410110336781,1.4592605832995338,-1.8473449070148147,0.2967410110336781,1.5358893381385874,-1.783160790922678,0.2967410110336781,1.6099617567499458,-1.7160087796727022,0.2967410110336781,1.681354552902976,-1.646000641160435,0.2967410110336781,1.7499489003341784,-1.5732528970233874,0.2967410110336781,1.8156306305217702,-1.4978866287022585,0.2967410110336781,1.8782904227079327,-1.4200272759128365,0.2967410110336781,1.9378239858524124,-1.3398044278639667,0.2967410110336781,1.9941322322146722,-1.2573516075690985,0.2967410110336781,2.047121442275657,-1.172806049610411,0.2967410110336781,2.0967034207246824,-1.0863084717253972,0.2967410110336781,2.1427956432518234,-0.9980028405960756,0.2967410110336781,2.1853213939014844,-0.9080361322306494,0.2967410110336781,2.224209892758542,-0.8165580873364517,0.2967410110336781,2.259396413754523,-0.7237209620913209,0.2967410110336781,2.2908223923977555,-0.6296792747282072,0.2967410110336781,2.318435523248193,-0.5345895483548286,0.2967410110336781,2.342189846974652,-0.4386100504364099,0.2967410110336781,2.3620458268495836,-0.3419005293751018,0.2967410110336781,2.3779704145540554,-0.24462194862452696,0.2967410110336781,2.389937105183419,-0.14693621878200047,0.2967410110336781,2.3979259813621043,-0.04900592810432419,0.2967410110336781,2.4019237463941265,0.04900592810432545,0.2967410110336781,2.4019237463941265,0.14693621878199958,0.2967410110336781,2.3979259813621043,0.2446219486245261,0.2967410110336781,2.3899371051834195,0.34190052937510096,0.2967410110336781,2.377970414554056,0.43861005043640905,0.2967410110336781,2.3620458268495836,0.5345895483548277,0.2967410110336781,2.342189846974652,0.6296792747282064,0.2967410110336781,2.3184355232481932,0.7237209620913201,0.2967410110336781,2.2908223923977555,0.816558087336451,0.2967410110336781,2.259396413754523,0.9080361322306466,0.2967410110336781,2.224209892758543,0.9980028405960767,0.2967410110336781,2.185321393901484,1.0863084717253984,0.2967410110336781,2.1427956432518225,1.1728060496104102,0.2967410110336781,2.096703420724683,1.257351607569096,0.2967410110336781,2.0471214422756585,1.339804427863966,0.2967410110336781,1.9941322322146726,1.4200272759128376,0.2967410110336781,1.9378239858524118,1.4978866287022579,0.2967410110336781,1.8782904227079331,1.5732528970233868,0.2967410110336781,1.815630630521771,1.6460006411604342,0.2967410110336781,1.7499489003341788,1.7160087796727008,0.2967410110336781,1.6813545529029774,1.783160790922678,0.2967410110336781,1.6099617567499458,1.8473449070148154,0.2967410110336781,1.5358893381385863,1.908454299822153,0.2967410110336781,1.4592605832995347,1.9663872587911997,0.2967410110336781,1.3802030332327901,2.0210473602291064,0.2967410110336781,1.2988482714283156,2.072343627791391,0.2967410110336781,1.2153317048582994,2.12019068390308,0.2967410110336781,1.1297923386056186,2.1645088918612614,0.2967410110336781,1.0423725445035938,2.2052244883825165,0.2967410110336781,0.9532178241721198,2.2422697063746253,0.2967410110336781,0.8624765668445811,2.275582887728199,0.2967410110336781,0.7702998023886244,2.3051085859405265,0.2967410110336781,0.6768409499318347,2.330797658400799,0.2967410110336781,0.5822555625107542,2.35260734818314,0.2967410110336781,0.4867010681681883,2.3705013552112866,0.2967410110336781,0.3903365079297808,2.384449896676485,0.2967410110336781,0.2933222710959086,2.394429756608037,0.2967410110336781,0.19581982828952532,2.40042432451399,0.2967410110336781,0.09799146270424966,2.402423623027663,0.2967410110336781,0.0,2.380489393880484,0.32438838010086307,0.0,2.378508349046683,0.32438838010086307,-0.0970967965109858,2.3725685117988986,0.32438838010086307,-0.1940319849865809,2.362679768410032,0.32438838010086307,-0.29064422637202053,2.3488585777175426,0.32438838010086307,-0.38677271912610023,2.3311279437293355,0.32438838010086307,-0.4822574668594715,2.309517377335863,0.32438838010086307,-0.5769395446328422,2.2840628471921582,0.32438838010086307,-0.6706613634718539,2.254806719851559,0.32438838010086307,-0.7632669326583773,2.221797689250761,0.32438838010086307,-0.8546021193616674,2.1850906956635656,0.32438838010086307,-0.9445149051772506,2.144746834258216,0.32438838010086307,-1.032855639146554,2.100833253410519,0.32438838010086307,-1.1194772868361593,2.0534230429420015,0.32438838010086307,-1.2042356750621057,2.0025951124691144,0.32438838010086307,-1.2869897318519248,1.9484340600659635,0.32438838010086307,-1.3676017212450196,1.8910300314591644,0.32438838010086307,-1.4459374725405751,1.8304785699891777,0.32438838010086307,-1.5218666036114497,1.7668804575878472,0.32438838010086307,-1.5952627379123585,1.70034154703682,0.32438838010086307,-1.6660037148211608,1.6309725857860387,0.32438838010086307,-1.7339717929631604,1.5588890316255404,0.32438838010086307,-1.799053846180002,1.4842108605173598,0.32438838010086307,-1.8611415518170003,1.4070623669073796,0.32438838010086307,-1.9201315710155067,1.3275719568494928,0.32438838010086307,-1.9759257207102403,1.2458719342863989,0.32438838010086307,-2.028431137045313,1.1620982808427471,0.32438838010086307,-2.0775604299369506,1.0763904294971431,0.32438838010086307,-2.123231828525663,0.9888910325097141,0.32438838010086307,-2.165369317275773,0.8997457239915037,0.32438838010086307,-2.2039027624957686,0.8091028775108647,0.32438838010086307,-2.2387680290689147,0.7171133591402965,0.32438838010086307,-2.269907087199825,0.6239302763547583,0.32438838010086307,-2.297268108999328,0.5297087231993862,0.32438838010086307,-2.320805554746875,0.43460552215076065,0.32438838010086307,-2.34048024868691,0.3387789631013721,0.32438838010086307,-2.3562594442330482,0.24238853990171405,0.32438838010086307,-2.36811687847154,0.14559468489851238,0.32438838010086307,-2.3760328158732946,0.04855850191091511,0.32438838010086307,-2.3799940811417244,-0.04855850191091482,0.32438838010086307,-2.3799940811417244,-0.1455946848985121,0.32438838010086307,-2.3760328158732946,-0.24238853990171325,0.32438838010086307,-2.36811687847154,-0.33877896310137184,0.32438838010086307,-2.3562594442330482,-0.4346055221507604,0.32438838010086307,-2.34048024868691,-0.5297087231993854,0.32438838010086307,-2.320805554746875,-0.6239302763547581,0.32438838010086307,-2.2972681089993285,-0.7171133591402956,0.32438838010086307,-2.2699070871998255,-0.8091028775108644,0.32438838010086307,-2.238768029068915,-0.8997457239915033,0.32438838010086307,-2.2039027624957686,-0.9888910325097134,0.32438838010086307,-2.165369317275773,-1.0763904294971434,0.32438838010086307,-2.123231828525663,-1.162098280842747,0.32438838010086307,-2.0775604299369506,-1.2458719342863995,0.32438838010086307,-2.0284311370453127,-1.3275719568494924,0.32438838010086307,-1.9759257207102408,-1.4070623669073792,0.32438838010086307,-1.920131571015507,-1.4842108605173596,0.32438838010086307,-1.861141551817001,-1.5588890316255406,0.32438838010086307,-1.799053846180002,-1.6309725857860389,0.32438838010086307,-1.7339717929631604,-1.70034154703682,0.32438838010086307,-1.6660037148211608,-1.7668804575878472,0.32438838010086307,-1.5952627379123585,-1.8304785699891772,0.32438838010086307,-1.5218666036114505,-1.8910300314591644,0.32438838010086307,-1.445937472540575,-1.948434060065963,0.32438838010086307,-1.3676017212450202,-2.002595112469115,0.32438838010086307,-1.2869897318519246,-2.0534230429420015,0.32438838010086307,-1.204235675062106,-2.1008332534105185,0.32438838010086307,-1.1194772868361598,-2.1447468342582154,0.32438838010086307,-1.0328556391465542,-2.185090695663565,0.32438838010086307,-0.9445149051772507,-2.221797689250761,0.32438838010086307,-0.8546021193616675,-2.254806719851559,0.32438838010086307,-0.7632669326583773,-2.284062847192158,0.32438838010086307,-0.6706613634718549,-2.309517377335863,0.32438838010086307,-0.576939544632842,-2.331127943729335,0.32438838010086307,-0.48225746685947224,-2.3488585777175426,0.32438838010086307,-0.3867727191261009,-2.362679768410032,0.32438838010086307,-0.29064422637202114,-2.3725685117988986,0.32438838010086307,-0.1940319849865814,-2.378508349046683,0.32438838010086307,-0.09709679651098618,-2.380489393880484,0.32438838010086307,-2.915258716619958e-16,-2.378508349046683,0.32438838010086307,0.0970967965109856,-2.3725685117988986,0.32438838010086307,0.1940319849865808,-2.362679768410032,0.32438838010086307,0.2906442263720206,-2.3488585777175426,0.32438838010086307,0.3867727191260993,-2.331127943729336,0.32438838010086307,0.4822574668594707,-2.309517377335863,0.32438838010086307,0.5769395446328425,-2.2840628471921582,0.32438838010086307,0.6706613634718543,-2.254806719851559,0.32438838010086307,0.7632669326583768,-2.221797689250761,0.32438838010086307,0.8546021193616671,-2.1850906956635656,0.32438838010086307,0.9445149051772501,-2.1447468342582163,0.32438838010086307,1.0328556391465529,-2.100833253410519,0.32438838010086307,1.119477286836159,-2.0534230429420015,0.32438838010086307,1.2042356750621053,-2.0025951124691144,0.32438838010086307,1.2869897318519248,-1.9484340600659638,0.32438838010086307,1.3676017212450189,-1.891030031459165,0.32438838010086307,1.4459374725405745,-1.8304785699891775,0.32438838010086307,1.5218666036114499,-1.766880457587847,0.32438838010086307,1.5952627379123587,-1.7003415470368204,0.32438838010086307,1.6660037148211606,-1.6309725857860393,0.32438838010086307,1.7339717929631602,-1.5588890316255417,0.32438838010086307,1.799053846180001,-1.4842108605173598,0.32438838010086307,1.8611415518170003,-1.4070623669073785,0.32438838010086307,1.9201315710155071,-1.3275719568494928,0.32438838010086307,1.9759257207102403,-1.2458719342864,0.32438838010086307,2.0284311370453123,-1.1620982808427474,0.32438838010086307,2.07756042993695,-1.076390429497142,0.32438838010086307,2.1232318285256637,-0.9888910325097139,0.32438838010086307,2.165369317275773,-0.8997457239915044,0.32438838010086307,2.203902762495768,-0.8091028775108641,0.32438838010086307,2.238768029068915,-0.7171133591402967,0.32438838010086307,2.269907087199825,-0.6239302763547596,0.32438838010086307,2.2972681089993277,-0.5297087231993866,0.32438838010086307,2.320805554746875,-0.4346055221507599,0.32438838010086307,2.34048024868691,-0.33877896310137184,0.32438838010086307,2.3562594442330482,-0.24238853990171488,0.32438838010086307,2.3681168784715396,-0.14559468489851213,0.32438838010086307,2.3760328158732946,-0.04855850191091382,0.32438838010086307,2.3799940811417244,0.04855850191091506,0.32438838010086307,2.3799940811417244,0.14559468489851127,0.32438838010086307,2.3760328158732946,0.24238853990171402,0.32438838010086307,2.36811687847154,0.338778963101371,0.32438838010086307,2.3562594442330487,0.4346055221507591,0.32438838010086307,2.34048024868691,0.5297087231993857,0.32438838010086307,2.320805554746875,0.6239302763547588,0.32438838010086307,2.297268108999328,0.717113359140296,0.32438838010086307,2.2699070871998255,0.8091028775108633,0.32438838010086307,2.238768029068915,0.8997457239915017,0.32438838010086307,2.203902762495769,0.988891032509715,0.32438838010086307,2.1653693172757724,1.0763904294971431,0.32438838010086307,2.123231828525663,1.1620982808427467,0.32438838010086307,2.0775604299369506,1.2458719342863975,0.32438838010086307,2.028431137045314,1.3275719568494921,0.32438838010086307,1.9759257207102408,1.4070623669073796,0.32438838010086307,1.9201315710155065,1.4842108605173594,0.32438838010086307,1.861141551817001,1.5588890316255413,0.32438838010086307,1.7990538461800019,1.6309725857860384,0.32438838010086307,1.7339717929631606,1.7003415470368193,0.32438838010086307,1.666003714821162,1.766880457587847,0.32438838010086307,1.5952627379123587,1.8304785699891784,0.32438838010086307,1.5218666036114488,1.8910300314591642,0.32438838010086307,1.4459374725405751,1.9484340600659626,0.32438838010086307,1.3676017212450207,2.0025951124691135,0.32438838010086307,1.2869897318519266,2.0534230429420015,0.32438838010086307,1.2042356750621062,2.1008332534105194,0.32438838010086307,1.119477286836159,2.1447468342582154,0.32438838010086307,1.0328556391465546,2.1850906956635647,0.32438838010086307,0.944514905177252,2.221797689250761,0.32438838010086307,0.854602119361668,2.2548067198515587,0.32438838010086307,0.7632669326583786,2.2840628471921582,0.32438838010086307,0.6706613634718542,2.3095173773358626,0.32438838010086307,0.5769395446328434,2.3311279437293355,0.32438838010086307,0.4822574668594715,2.3488585777175426,0.32438838010086307,0.38677271912610117,2.362679768410032,0.32438838010086307,0.2906442263720225,2.3725685117988986,0.32438838010086307,0.19403198498658167,2.378508349046683,0.32438838010086307,0.09709679651098542,2.380489393880484,0.32438838010086307,0.0,2.3566595857121904,0.35041966257561646,0.0,2.3546983720603656,0.35041966257561646,-0.09612481232968227,2.3488179953514643,0.35041966257561646,-0.19208963439571933,2.339028242892189,0.35041966257561646,-0.2877347422224704,2.3253454087593655,0.35041966257561646,-0.38290094396709395,2.3077922666800608,0.35041966257561646,-0.477429844878659,2.2863980321269604,0.35041966257561646,-0.5711641109305765,2.261198313692099,0.35041966257561646,-0.6639477306875597,2.2322350538198723,0.35041966257561646,-0.7556262749712612,2.1995564589979777,0.35041966257561646,-0.8460471538923996,2.163216919522473,0.35041966257561646,-0.9350598708215722,2.1232769189704963,0.35041966257561646,-1.0225162728760404,2.079802933531321,0.35041966257561646,-1.1082707975055848,2.032867321363299,0.35041966257561646,-1.1921807147670016,1.982548202160848,0.35041966257561646,-1.2741063648840085,1.9289293271319286,0.35041966257561646,-1.3539113906971603,1.8720999396024238,0.35041966257561646,-1.4314629646168826,1.8121546264794322,0.35041966257561646,-1.5066320097018855,1.7491931608206936,0.35041966257561646,-1.579293414494996,1.683320335772183,0.35041966257561646,-1.649326241258829,1.614645790150259,0.35041966257561646,-1.7166139272647178,1.5432838259586763,0.35041966257561646,-1.7810444787998705,1.4693532181441789,0.35041966257561646,-1.8425106575698544,1.3929770169073235,0.35041966257561646,-1.9009101591861535,1.3142823428975654,0.35041966257561646,-1.9561457834417246,1.233400175633482,0.35041966257561646,-2.008125596091153,1.150465135500291,0.35041966257561646,-2.0567630818661273,1.0656152596875053,0.35041966257561646,-2.1019772884715673,0.9789917724396499,0.35041966257561646,-2.1436929613227322,0.89073885000245,0.35041966257561646,-2.1818406687990453,0.8010033806556957,0.35041966257561646,-2.2163569178061775,0.7099347202321947,0.35041966257561646,-2.2471842594540368,0.6176844435297326,0.35041966257561646,-2.274271384674775,0.5244060920297777,0.35041966257561646,-2.297573209621667,0.4302549183428389,0.35041966257561646,-2.3170509507067276,0.335387627805818,0.35041966257561646,-2.3326721891521616,0.23996211766143893,0.35041966257561646,-2.344410924948225,0.14413721425387363,0.35041966257561646,-2.3522476201276694,0.048072408677964154,0.35041966257561646,-2.3561692312847673,-0.04807240867796387,0.35041966257561646,-2.3561692312847673,-0.14413721425387332,0.35041966257561646,-2.35224762012767,-0.23996211766143813,0.35041966257561646,-2.344410924948225,-0.3353876278058177,0.35041966257561646,-2.3326721891521616,-0.4302549183428387,0.35041966257561646,-2.3170509507067276,-0.524406092029777,0.35041966257561646,-2.297573209621667,-0.6176844435297323,0.35041966257561646,-2.274271384674775,-0.709934720232194,0.35041966257561646,-2.247184259454037,-0.8010033806556953,0.35041966257561646,-2.216356917806178,-0.8907388500024498,0.35041966257561646,-2.1818406687990457,-0.9789917724396492,0.35041966257561646,-2.1436929613227327,-1.0656152596875055,0.35041966257561646,-2.1019772884715673,-1.150465135500291,0.35041966257561646,-2.0567630818661273,-1.2334001756334825,0.35041966257561646,-2.0081255960911526,-1.3142823428975647,0.35041966257561646,-1.9561457834417253,-1.392977016907323,0.35041966257561646,-1.9009101591861537,-1.4693532181441784,0.35041966257561646,-1.842510657569855,-1.5432838259586765,0.35041966257561646,-1.7810444787998705,-1.6146457901502596,0.35041966257561646,-1.7166139272647178,-1.683320335772183,0.35041966257561646,-1.649326241258829,-1.7491931608206936,0.35041966257561646,-1.579293414494996,-1.8121546264794315,0.35041966257561646,-1.5066320097018864,-1.8720999396024238,0.35041966257561646,-1.4314629646168824,-1.928929327131928,0.35041966257561646,-1.353911390697161,-1.9825482021608483,0.35041966257561646,-1.2741063648840083,-2.0328673213632986,0.35041966257561646,-1.1921807147670018,-2.0798029335313206,0.35041966257561646,-1.108270797505585,-2.123276918970496,0.35041966257561646,-1.0225162728760409,-2.1632169195224726,0.35041966257561646,-0.9350598708215723,-2.1995564589979777,0.35041966257561646,-0.8460471538923998,-2.2322350538198723,0.35041966257561646,-0.7556262749712612,-2.261198313692099,0.35041966257561646,-0.6639477306875606,-2.2863980321269604,0.35041966257561646,-0.5711641109305764,-2.3077922666800603,0.35041966257561646,-0.47742984487865975,-2.3253454087593655,0.35041966257561646,-0.3829009439670946,-2.339028242892189,0.35041966257561646,-0.28773474222247103,-2.3488179953514643,0.35041966257561646,-0.1920896343957198,-2.3546983720603656,0.35041966257561646,-0.09612481232968265,-2.3566595857121904,0.35041966257561646,-2.8860756183223616e-16,-2.3546983720603656,0.35041966257561646,0.09612481232968208,-2.3488179953514643,0.35041966257561646,0.19208963439571922,-2.339028242892189,0.35041966257561646,0.2877347422224705,-2.325345408759366,0.35041966257561646,0.38290094396709307,-2.3077922666800608,0.35041966257561646,0.4774298448786582,-2.2863980321269604,0.35041966257561646,0.5711641109305768,-2.261198313692099,0.35041966257561646,0.6639477306875602,-2.2322350538198723,0.35041966257561646,0.7556262749712607,-2.199556458997978,0.35041966257561646,0.8460471538923993,-2.163216919522473,0.35041966257561646,0.9350598708215717,-2.1232769189704968,0.35041966257561646,1.0225162728760395,-2.079802933531321,0.35041966257561646,1.1082707975055845,-2.032867321363299,0.35041966257561646,1.1921807147670014,-1.982548202160848,0.35041966257561646,1.2741063648840085,-1.9289293271319288,0.35041966257561646,1.3539113906971596,-1.8720999396024243,0.35041966257561646,1.4314629646168817,-1.8121546264794317,0.35041966257561646,1.5066320097018857,-1.7491931608206934,0.35041966257561646,1.5792934144949964,-1.6833203357721835,0.35041966257561646,1.6493262412588288,-1.6146457901502598,0.35041966257561646,1.7166139272647176,-1.5432838259586776,0.35041966257561646,1.7810444787998694,-1.4693532181441789,0.35041966257561646,1.8425106575698544,-1.3929770169073223,0.35041966257561646,1.9009101591861541,-1.3142823428975654,0.35041966257561646,1.9561457834417246,-1.233400175633483,0.35041966257561646,2.0081255960911526,-1.1504651355002915,0.35041966257561646,2.056763081866127,-1.0656152596875041,0.35041966257561646,2.101977288471568,-0.9789917724396497,0.35041966257561646,2.1436929613227327,-0.8907388500024508,0.35041966257561646,2.1818406687990453,-0.801003380655695,0.35041966257561646,2.216356917806178,-0.709934720232195,0.35041966257561646,2.2471842594540368,-0.6176844435297338,0.35041966257561646,2.2742713846747744,-0.5244060920297781,0.35041966257561646,2.297573209621667,-0.4302549183428382,0.35041966257561646,2.3170509507067276,-0.3353876278058177,0.35041966257561646,2.3326721891521616,-0.23996211766143974,0.35041966257561646,2.3444109249482246,-0.14413721425387338,0.35041966257561646,2.35224762012767,-0.04807240867796288,0.35041966257561646,2.3561692312847673,0.048072408677964105,0.35041966257561646,2.3561692312847673,0.14413721425387252,0.35041966257561646,2.35224762012767,0.23996211766143888,0.35041966257561646,2.344410924948225,0.3353876278058169,0.35041966257561646,2.332672189152162,0.43025491834283736,0.35041966257561646,2.3170509507067276,0.5244060920297772,0.35041966257561646,2.297573209621667,0.6176844435297331,0.35041966257561646,2.274271384674775,0.7099347202321942,0.35041966257561646,2.247184259454037,0.8010033806556942,0.35041966257561646,2.216356917806178,0.890738850002448,0.35041966257561646,2.181840668799046,0.9789917724396507,0.35041966257561646,2.143692961322732,1.0656152596875053,0.35041966257561646,2.1019772884715673,1.1504651355002908,0.35041966257561646,2.0567630818661273,1.2334001756334807,0.35041966257561646,2.008125596091154,1.3142823428975645,0.35041966257561646,1.9561457834417253,1.3929770169073235,0.35041966257561646,1.9009101591861532,1.4693532181441782,0.35041966257561646,1.842510657569855,1.543283825958677,0.35041966257561646,1.7810444787998703,1.6146457901502589,0.35041966257561646,1.716613927264718,1.6833203357721822,0.35041966257561646,1.64932624125883,1.7491931608206934,0.35041966257561646,1.5792934144949964,1.8121546264794326,0.35041966257561646,1.5066320097018848,1.8720999396024236,0.35041966257561646,1.4314629646168826,1.9289293271319277,0.35041966257561646,1.3539113906971612,1.982548202160847,0.35041966257561646,1.27410636488401,2.0328673213632986,0.35041966257561646,1.192180714767002,2.079802933531321,0.35041966257561646,1.1082707975055843,2.123276918970496,0.35041966257561646,1.022516272876041,2.163216919522472,0.35041966257561646,0.9350598708215736,2.1995564589979777,0.35041966257561646,0.8460471538924001,2.232235053819872,0.35041966257561646,0.7556262749712626,2.261198313692099,0.35041966257561646,0.6639477306875601,2.28639803212696,0.35041966257561646,0.5711641109305776,2.3077922666800608,0.35041966257561646,0.477429844878659,2.3253454087593655,0.35041966257561646,0.3829009439670949,2.339028242892189,0.35041966257561646,0.28773474222247236,2.3488179953514643,0.35041966257561646,0.19208963439572008,2.3546983720603656,0.35041966257561646,0.0961248123296819,2.3566595857121904,0.35041966257561646,0.0,2.3310529174284,0.3747051719180626,0.0,2.3291130136627043,0.3747051719180626,-0.09508035253663842,2.3232965311439866,0.3747051719180626,-0.19000245320988027,2.313613150833377,0.3747051719180626,-0.28460831355093785,2.3000789897617566,0.3747051719180626,-0.37874047144184697,2.2827165742045543,0.3747051719180626,-0.4722422531956223,2.261554802188983,0.3747051719180626,-0.5649580343241475,2.236628895396125,0.3747051719180626,-0.6567334985597771,2.2079803405379197,0.3747051719180626,-0.7474158946995353,2.175656820306627,0.3747051719180626,-0.836854290844417,2.1397121340116905,0.3747051719180626,-0.9248998256106402,2.100206108036097,0.3747051719180626,-1.0114059558947266,2.057204496261266,0.3747051719180626,-1.0962287007800364,2.0107788706262038,0.3747051719180626,-1.1792268811787936,1.9610065020030734,0.3747051719180626,-1.2602623548107457,1.9079702315874547,0.3747051719180626,-1.3392002461273558,1.8517583330173488,0.3747051719180626,-1.4159091707988423,1.7924643654504206,0.3747051719180626,-1.4902614543904287,1.7301870178440133,0.3747051719180626,-1.562133344863842,1.665029944697118,0.3747051719180626,-1.631405218550366,1.5971015935276904,0.3747051719180626,-1.697961779252633,1.52651502437246,0.3747051719180626,-1.7616922501437653,1.453387721609656,0.3747051719180626,-1.8224905581444717,1.3778413984178546,0.3747051719180626,-1.8802555104712197,1.3000017941964044,0.3747051719180626,-1.9348909630616358,1.2199984652846065,0.3747051719180626,-1.9863059805968113,1.1379645693279736,0.3747051719180626,-2.0344149878541633,1.0540366436504687,0.3747051719180626,-2.0791379121389495,0.9683543780016041,0.3747051719180626,-2.1204003165573666,0.8810603820566416,0.3747051719180626,-2.1581335239094126,0.7922999480568603,0.3747051719180626,-2.192274730995308,0.702220808984959,0.3747051719180626,-2.2227671131452267,0.6109728926780877,0.3747051719180626,-2.249559918798347,0.5187080722877592,0.3747051719180626,-2.272608553973819,0.4255799135019744,0.3747051719180626,-2.291874656493044,0.3317434189502927,0.3747051719180626,-2.3073261598297337,0.23735477021724902,0.3747051719180626,-2.318937346481481,0.142571067893523,0.3747051719180626,-2.326688890774002,0.04755007009750794,0.3747051719180626,-2.3305678910268153,-0.04755007009750766,0.3747051719180626,-2.3305678910268153,-0.14257106789352272,0.3747051719180626,-2.326688890774002,-0.23735477021724824,0.3747051719180626,-2.318937346481481,-0.33174341895029247,0.3747051719180626,-2.3073261598297337,-0.4255799135019741,0.3747051719180626,-2.291874656493044,-0.5187080722877584,0.3747051719180626,-2.272608553973819,-0.6109728926780875,0.3747051719180626,-2.2495599187983473,-0.7022208089849582,0.3747051719180626,-2.222767113145227,-0.7922999480568601,0.3747051719180626,-2.1922747309953086,-0.8810603820566414,0.3747051719180626,-2.158133523909413,-0.9683543780016035,0.3747051719180626,-2.120400316557367,-1.054036643650469,0.3747051719180626,-2.0791379121389495,-1.1379645693279734,0.3747051719180626,-2.0344149878541633,-1.2199984652846072,0.3747051719180626,-1.9863059805968108,-1.300001794196404,0.3747051719180626,-1.9348909630616362,-1.377841398417854,0.3747051719180626,-1.88025551047122,-1.4533877216096558,0.3747051719180626,-1.8224905581444721,-1.5265150243724601,0.3747051719180626,-1.7616922501437653,-1.5971015935276907,0.3747051719180626,-1.697961779252633,-1.665029944697118,0.3747051719180626,-1.631405218550366,-1.7301870178440133,0.3747051719180626,-1.562133344863842,-1.7924643654504202,0.3747051719180626,-1.4902614543904293,-1.8517583330173488,0.3747051719180626,-1.415909170798842,-1.907970231587454,0.3747051719180626,-1.3392002461273564,-1.9610065020030736,0.3747051719180626,-1.2602623548107454,-2.0107788706262033,0.3747051719180626,-1.1792268811787938,-2.057204496261266,0.3747051719180626,-1.0962287007800366,-2.1002061080360965,0.3747051719180626,-1.011405955894727,-2.1397121340116905,0.3747051719180626,-0.9248998256106404,-2.175656820306627,0.3747051719180626,-0.8368542908444172,-2.2079803405379197,0.3747051719180626,-0.7474158946995353,-2.236628895396125,0.3747051719180626,-0.6567334985597781,-2.261554802188983,0.3747051719180626,-0.5649580343241473,-2.2827165742045543,0.3747051719180626,-0.472242253195623,-2.3000789897617566,0.3747051719180626,-0.37874047144184764,-2.313613150833377,0.3747051719180626,-0.2846083135509384,-2.3232965311439866,0.3747051719180626,-0.19000245320988074,-2.3291130136627043,0.3747051719180626,-0.0950803525366388,-2.3310529174284,0.3747051719180626,-2.8547164939717895e-16,-2.3291130136627043,0.3747051719180626,0.09508035253663823,-2.3232965311439866,0.3747051719180626,0.19000245320988016,-2.313613150833377,0.3747051719180626,0.2846083135509379,-2.3000789897617566,0.3747051719180626,0.3787404714418461,-2.2827165742045548,0.3747051719180626,0.47224225319562146,-2.261554802188983,0.3747051719180626,0.5649580343241477,-2.236628895396125,0.3747051719180626,0.6567334985597776,-2.2079803405379197,0.3747051719180626,0.7474158946995347,-2.175656820306627,0.3747051719180626,0.8368542908444166,-2.1397121340116905,0.3747051719180626,0.9248998256106399,-2.1002061080360974,0.3747051719180626,1.0114059558947255,-2.057204496261266,0.3747051719180626,1.096228700780036,-2.0107788706262038,0.3747051719180626,1.1792268811787934,-1.9610065020030734,0.3747051719180626,1.2602623548107457,-1.907970231587455,0.3747051719180626,1.3392002461273553,-1.8517583330173493,0.3747051719180626,1.4159091707988416,-1.7924643654504204,0.3747051719180626,1.490261454390429,-1.730187017844013,0.3747051719180626,1.5621333448638421,-1.6650299446971184,0.3747051719180626,1.6314052185503656,-1.5971015935276909,0.3747051719180626,1.6979617792526327,-1.5265150243724612,0.3747051719180626,1.7616922501437642,-1.453387721609656,0.3747051719180626,1.8224905581444717,-1.3778413984178535,0.3747051719180626,1.8802555104712202,-1.3000017941964044,0.3747051719180626,1.9348909630616358,-1.2199984652846076,0.3747051719180626,1.9863059805968106,-1.1379645693279739,0.3747051719180626,2.034414987854163,-1.0540366436504676,0.3747051719180626,2.0791379121389504,-0.968354378001604,0.3747051719180626,2.120400316557367,-0.8810603820566424,0.3747051719180626,2.1581335239094126,-0.7922999480568598,0.3747051719180626,2.1922747309953086,-0.7022208089849592,0.3747051719180626,2.2227671131452267,-0.6109728926780891,0.3747051719180626,2.2495599187983464,-0.5187080722877594,0.3747051719180626,2.272608553973819,-0.42557991350197366,0.3747051719180626,2.291874656493044,-0.33174341895029247,0.3747051719180626,2.3073261598297337,-0.23735477021724982,0.3747051719180626,2.318937346481481,-0.14257106789352275,0.3747051719180626,2.326688890774002,-0.047550070097506676,0.3747051719180626,2.3305678910268153,0.04755007009750789,0.3747051719180626,2.3305678910268153,0.1425710678935219,0.3747051719180626,2.326688890774002,0.237354770217249,0.3747051719180626,2.318937346481481,0.33174341895029164,0.3747051719180626,2.307326159829734,0.42557991350197283,0.3747051719180626,2.291874656493044,0.5187080722877586,0.3747051719180626,2.272608553973819,0.6109728926780883,0.3747051719180626,2.249559918798347,0.7022208089849584,0.3747051719180626,2.222767113145227,0.792299948056859,0.3747051719180626,2.1922747309953086,0.8810603820566396,0.3747051719180626,2.1581335239094135,0.9683543780016051,0.3747051719180626,2.1204003165573666,1.0540366436504687,0.3747051719180626,2.0791379121389495,1.1379645693279732,0.3747051719180626,2.0344149878541633,1.2199984652846054,0.3747051719180626,1.9863059805968122,1.3000017941964035,0.3747051719180626,1.9348909630616362,1.3778413984178546,0.3747051719180626,1.8802555104712193,1.4533877216096556,0.3747051719180626,1.8224905581444721,1.5265150243724606,0.3747051719180626,1.761692250143765,1.5971015935276902,0.3747051719180626,1.6979617792526331,1.6650299446971173,0.3747051719180626,1.631405218550367,1.730187017844013,0.3747051719180626,1.5621333448638421,1.7924643654504213,0.3747051719180626,1.4902614543904278,1.8517583330173486,0.3747051719180626,1.4159091707988423,1.9079702315874538,0.3747051719180626,1.3392002461273569,1.9610065020030722,0.3747051719180626,1.2602623548107472,2.0107788706262033,0.3747051719180626,1.1792268811787943,2.0572044962612663,0.3747051719180626,1.096228700780036,2.1002061080360965,0.3747051719180626,1.0114059558947273,2.1397121340116896,0.3747051719180626,0.9248998256106417,2.175656820306627,0.3747051719180626,0.8368542908444175,2.2079803405379193,0.3747051719180626,0.7474158946995366,2.236628895396125,0.3747051719180626,0.6567334985597775,2.261554802188983,0.3747051719180626,0.5649580343241486,2.2827165742045543,0.3747051719180626,0.4722422531956223,2.3000789897617566,0.3747051719180626,0.37874047144184786,2.313613150833377,0.3747051719180626,0.28460831355093974,2.3232965311439866,0.3747051719180626,0.19000245320988102,2.3291130136627043,0.3747051719180626,0.09508035253663805,2.3310529174284,0.3747051719180626,0.0,2.3037969601624493,0.39712391894225546,0.0,2.301879738822254,0.39712391894225546,-0.09396862057800529,2.2961312658273805,0.39712391894225546,-0.18778083966074324,2.286561108943816,0.39712391894225546,-0.2812805160678003,2.273185196753223,0.39712391894225546,-0.37431202881520204,2.256025792141385,0.39712391894225546,-0.4667205361311827,2.235111455243831,0.39712391894225546,-0.5583522331750332,2.2104769959103083,0.39712391894225546,-0.6490546080300801,2.182163415767224,0.39712391894225546,-0.7386766955447187,2.1502178399744816,0.39712391894225546,-0.82706932859901,2.1146934387903067,0.39712391894225546,-0.9140853863786303,2.0756493390745967,0.39712391894225546,-0.9995800392429475,2.0331505258780984,0.39712391894225546,-1.083410989779665,1.9872677342812055,0.39712391894225546,-1.1654387096448213,1.9380773316623965,0.39712391894225546,-1.2455266717939473,1.8856611905922704,0.39712391894225546,-1.3235415777178605,1.830106552564733,0.39712391894225546,-1.3993535793048768,1.7715058827921406,0.39712391894225546,-1.472836494960177,1.7099567163060785,0.39712391894225546,-1.5438680196226215,1.6455614956199267,0.39712391894225546,-1.6123299283294508,1.5784274002234067,0.39712391894225546,-1.6781082729900714,1.5086661681928981,0.39712391894225546,-1.7410935720414051,1.4363939102144383,0.39712391894225546,-1.801180992669148,1.361730916328943,0.39712391894225546,-1.8582705252916432,1.2848014557213063,0.39712391894225546,-1.9122671500159578,1.205733569886605,0.39712391894225546,-1.9630809947891212,1.1246588595176676,0.39712391894225546,-2.0106274849812835,1.0417122654687083,0.39712391894225546,-2.054827484151843,0.9570318441595903,0.39712391894225546,-2.0956074257642423,0.8707585377945429,0.39712391894225546,-2.132899435630206,0.7830359397777708,0.39712391894225546,-2.1666414448796285,0.6940100557164016,0.39712391894225546,-2.1967772932680862,0.6038290604085659,0.39712391894225546,-2.223256822650017,0.5126430512210663,0.39712391894225546,-2.2460359604620086,0.4206037982671244,0.39712391894225546,-2.2650767930772258,0.3278644918000054,0.39712391894225546,-2.2803476289089053,0.2345794872429578,0.39712391894225546,-2.2918230511578708,0.1409040482798483,0.39712391894225546,-2.299483960116289,0.04699408843407989,0.39712391894225546,-2.3033176049572477,-0.046994088434079616,0.39712391894225546,-2.3033176049572477,-0.14090404827984798,0.39712391894225546,-2.2994839601162895,-0.23457948724295702,0.39712391894225546,-2.2918230511578708,-0.3278644918000051,0.39712391894225546,-2.2803476289089053,-0.4206037982671242,0.39712391894225546,-2.2650767930772258,-0.5126430512210656,0.39712391894225546,-2.2460359604620086,-0.6038290604085655,0.39712391894225546,-2.2232568226500176,-0.6940100557164008,0.39712391894225546,-2.1967772932680862,-0.7830359397777704,0.39712391894225546,-2.166641444879629,-0.8707585377945427,0.39712391894225546,-2.1328994356302062,-0.9570318441595896,0.39712391894225546,-2.0956074257642427,-1.0417122654687085,0.39712391894225546,-2.054827484151843,-1.1246588595176674,0.39712391894225546,-2.0106274849812835,-1.2057335698866054,0.39712391894225546,-1.9630809947891208,-1.2848014557213059,0.39712391894225546,-1.9122671500159583,-1.3617309163289426,0.39712391894225546,-1.8582705252916434,-1.436393910214438,0.39712391894225546,-1.8011809926691487,-1.5086661681928983,0.39712391894225546,-1.7410935720414051,-1.5784274002234069,0.39712391894225546,-1.6781082729900714,-1.6455614956199267,0.39712391894225546,-1.6123299283294508,-1.7099567163060785,0.39712391894225546,-1.5438680196226215,-1.7715058827921402,0.39712391894225546,-1.472836494960178,-1.830106552564733,0.39712391894225546,-1.3993535793048766,-1.88566119059227,0.39712391894225546,-1.3235415777178612,-1.9380773316623967,0.39712391894225546,-1.245526671793947,-1.9872677342812053,0.39712391894225546,-1.1654387096448215,-2.0331505258780984,0.39712391894225546,-1.0834109897796653,-2.0756493390745963,0.39712391894225546,-0.9995800392429478,-2.1146934387903062,0.39712391894225546,-0.9140853863786305,-2.1502178399744816,0.39712391894225546,-0.8270693285990101,-2.182163415767224,0.39712391894225546,-0.7386766955447187,-2.210476995910308,0.39712391894225546,-0.649054608030081,-2.235111455243831,0.39712391894225546,-0.5583522331750331,-2.2560257921413847,0.39712391894225546,-0.4667205361311834,-2.273185196753223,0.39712391894225546,-0.37431202881520265,-2.286561108943816,0.39712391894225546,-0.28128051606780086,-2.2961312658273805,0.39712391894225546,-0.18778083966074374,-2.301879738822254,0.39712391894225546,-0.09396862057800566,-2.3037969601624493,0.39712391894225546,-2.821337573148346e-16,-2.301879738822254,0.39712391894225546,0.09396862057800509,-2.2961312658273805,0.39712391894225546,0.18778083966074316,-2.286561108943816,0.39712391894225546,0.28128051606780036,-2.2731851967532233,0.39712391894225546,0.3743120288152011,-2.256025792141385,0.39712391894225546,0.46672053613118186,-2.235111455243831,0.39712391894225546,0.5583522331750336,-2.2104769959103083,0.39712391894225546,0.6490546080300805,-2.182163415767224,0.39712391894225546,0.7386766955447183,-2.1502178399744816,0.39712391894225546,0.8270693285990095,-2.1146934387903067,0.39712391894225546,0.9140853863786299,-2.075649339074597,0.39712391894225546,0.9995800392429465,-2.0331505258780984,0.39712391894225546,1.0834109897796649,-1.9872677342812055,0.39712391894225546,1.1654387096448209,-1.9380773316623965,0.39712391894225546,1.2455266717939473,-1.8856611905922707,0.39712391894225546,1.3235415777178599,-1.8301065525647335,0.39712391894225546,1.3993535793048761,-1.7715058827921404,0.39712391894225546,1.4728364949601775,-1.7099567163060783,0.39712391894225546,1.5438680196226218,-1.6455614956199272,0.39712391894225546,1.6123299283294505,-1.578427400223407,0.39712391894225546,1.678108272990071,-1.5086661681928992,0.39712391894225546,1.741093572041404,-1.4363939102144383,0.39712391894225546,1.801180992669148,-1.3617309163289422,0.39712391894225546,1.8582705252916436,-1.2848014557213063,0.39712391894225546,1.9122671500159578,-1.205733569886606,0.39712391894225546,1.9630809947891206,-1.1246588595176679,0.39712391894225546,2.010627484981283,-1.0417122654687072,0.39712391894225546,2.054827484151844,-0.9570318441595902,0.39712391894225546,2.0956074257642427,-0.8707585377945437,0.39712391894225546,2.132899435630206,-0.7830359397777701,0.39712391894225546,2.166641444879629,-0.6940100557164018,0.39712391894225546,2.1967772932680862,-0.6038290604085671,0.39712391894225546,2.223256822650017,-0.5126430512210667,0.39712391894225546,2.2460359604620086,-0.42060379826712374,0.39712391894225546,2.2650767930772258,-0.3278644918000051,0.39712391894225546,2.2803476289089053,-0.2345794872429586,0.39712391894225546,2.2918230511578708,-0.14090404827984804,0.39712391894225546,2.2994839601162895,-0.04699408843407865,0.39712391894225546,2.3033176049572477,0.046994088434079845,0.39712391894225546,2.3033176049572477,0.1409040482798472,0.39712391894225546,2.2994839601162895,0.23457948724295777,0.39712391894225546,2.2918230511578708,0.32786449180000427,0.39712391894225546,2.2803476289089053,0.4206037982671229,0.39712391894225546,2.265076793077226,0.5126430512210659,0.39712391894225546,2.2460359604620086,0.6038290604085663,0.39712391894225546,2.223256822650017,0.6940100557164011,0.39712391894225546,2.1967772932680862,0.7830359397777693,0.39712391894225546,2.1666414448796294,0.870758537794541,0.39712391894225546,2.1328994356302067,0.9570318441595912,0.39712391894225546,2.0956074257642423,1.0417122654687083,0.39712391894225546,2.054827484151843,1.1246588595176672,0.39712391894225546,2.0106274849812835,1.2057335698866036,0.39712391894225546,1.9630809947891221,1.2848014557213054,0.39712391894225546,1.9122671500159583,1.361730916328943,0.39712391894225546,1.858270525291643,1.4363939102144379,0.39712391894225546,1.8011809926691487,1.5086661681928988,0.39712391894225546,1.741093572041405,1.5784274002234064,0.39712391894225546,1.6781082729900716,1.6455614956199258,0.39712391894225546,1.6123299283294517,1.7099567163060783,0.39712391894225546,1.5438680196226218,1.7715058827921413,0.39712391894225546,1.4728364949601764,1.8301065525647329,0.39712391894225546,1.3993535793048768,1.8856611905922698,0.39712391894225546,1.3235415777178614,1.9380773316623954,0.39712391894225546,1.2455266717939488,1.9872677342812053,0.39712391894225546,1.1654387096448218,2.033150525878099,0.39712391894225546,1.0834109897796647,2.0756493390745963,0.39712391894225546,0.9995800392429481,2.114693438790306,0.39712391894225546,0.9140853863786317,2.1502178399744816,0.39712391894225546,0.8270693285990105,2.1821634157672234,0.39712391894225546,0.73867669554472,2.2104769959103083,0.39712391894225546,0.6490546080300804,2.2351114552438305,0.39712391894225546,0.5583522331750344,2.256025792141385,0.39712391894225546,0.4667205361311827,2.273185196753223,0.39712391894225546,0.3743120288152029,2.286561108943816,0.39712391894225546,0.2812805160678022,2.2961312658273805,0.39712391894225546,0.187780839660744,2.301879738822254,0.39712391894225546,0.09396862057800492,2.3037969601624493,0.39712391894225546,0.0,2.2750275017224206,0.4175642145782178,0.0,2.273134222344395,0.4175642145782178,-0.09279515504647898,2.2674575353869977,0.4175642145782178,-0.1854358617152595,2.258006889135441,0.4175642145782178,-0.2777679286927255,2.2447980132577343,0.4175642145782178,-0.3696376783655676,2.227852892624204,0.4175642145782178,-0.46089220260200436,2.2071997307158435,0.4175642145782178,-0.5513796172522769,2.1828729026824063,0.4175642145782178,-0.6409493149448277,2.1549128981283605,0.4175642145782178,-0.7294522157574058,2.1233662537219358,0.4175642145782178,-0.8167410153458864,2.088285475739436,0.4175642145782178,-0.9026704301178133,2.049728952673718,0.4175642145782178,-0.9870974390426006,2.007760858052316,0.4175642145782178,-1.0698815216959245,1.9624510436269296,0.4175642145782178,-1.1508848921421004,1.9138749231120826,0.4175642145782178,-1.2299727282651753,1.862113346666435,0.4175642145782178,-1.3070133961670354,1.807252466325674,0.4175642145782178,-1.3818786692590375,1.7493835926109518,0.4175642145782178,-1.4544439416825106,1.6886030425515361,0.4175642145782178,-1.5245884357029125,1.625011979374619,0.4175642145782178,-1.5921954027324476,1.5587162441291094,0.4175642145782178,-1.657152317646571,1.4898261795236525,0.4175642145782178,-1.719351066070951,1.4184564462720817,0.4175642145782178,-1.7786881243271782,1.3447258322519744,0.4175642145782178,-1.8350647317377102,1.2687570547939537,0.4175642145782178,-1.888387055003272,1.1906765564308037,0.4175642145782178,-1.9385663443791246,1.1106142944463506,0.4175642145782178,-1.9855190813902528,1.0287035245743907,0.4175642145782178,-2.0291671178396284,0.9450805792076685,0.4175642145782178,-2.06943780587817,0.8598846404860693,0.4175642145782178,-2.1062641189199187,0.7732575086416793,0.4175642145782178,-2.1395847632011735,0.6853433659862931,0.4175642145782178,-2.1693442797979157,0.5962885369341878,0.4175642145782178,-2.1954931369317108,0.5062412444595739,0.4175642145782178,-2.2179878124104695,0.41535136339408296,0.4175642145782178,-2.236790866066838,0.3237701709749027,0.4175642145782178,-2.2518710020736625,0.2316500950587422,0.4175642145782178,-2.2632031210328063,0.13914446042071116,0.4175642145782178,-2.2707683617506182,0.046407233560360486,0.4175642145782178,-2.274554132630532,-0.04640723356036021,0.4175642145782178,-2.274554132630532,-0.13914446042071085,0.4175642145782178,-2.2707683617506187,-0.23165009505874146,0.4175642145782178,-2.2632031210328063,-0.3237701709749024,0.4175642145782178,-2.2518710020736625,-0.4153513633940827,0.4175642145782178,-2.236790866066838,-0.5062412444595731,0.4175642145782178,-2.2179878124104695,-0.5962885369341875,0.4175642145782178,-2.195493136931711,-0.6853433659862923,0.4175642145782178,-2.1693442797979157,-0.773257508641679,0.4175642145782178,-2.1395847632011735,-0.859884640486069,0.4175642145782178,-2.106264118919919,-0.945080579207668,0.4175642145782178,-2.0694378058781706,-1.0287035245743907,0.4175642145782178,-2.0291671178396284,-1.1106142944463506,0.4175642145782178,-1.9855190813902528,-1.1906765564308042,0.4175642145782178,-1.938566344379124,-1.2687570547939533,0.4175642145782178,-1.8883870550032724,-1.3447258322519737,0.4175642145782178,-1.8350647317377105,-1.4184564462720815,0.4175642145782178,-1.7786881243271786,-1.4898261795236527,0.4175642145782178,-1.719351066070951,-1.5587162441291096,0.4175642145782178,-1.657152317646571,-1.625011979374619,0.4175642145782178,-1.5921954027324476,-1.6886030425515361,0.4175642145782178,-1.5245884357029125,-1.749383592610951,0.4175642145782178,-1.4544439416825112,-1.807252466325674,0.4175642145782178,-1.3818786692590372,-1.8621133466664346,0.4175642145782178,-1.307013396167036,-1.9138749231120828,0.4175642145782178,-1.2299727282651751,-1.9624510436269291,0.4175642145782178,-1.1508848921421007,-2.0077608580523156,0.4175642145782178,-1.0698815216959248,-2.049728952673718,0.4175642145782178,-0.987097439042601,-2.0882854757394353,0.4175642145782178,-0.9026704301178134,-2.1233662537219358,0.4175642145782178,-0.8167410153458865,-2.1549128981283605,0.4175642145782178,-0.7294522157574058,-2.1828729026824063,0.4175642145782178,-0.6409493149448285,-2.2071997307158435,0.4175642145782178,-0.5513796172522768,-2.2278528926242034,0.4175642145782178,-0.46089220260200503,-2.2447980132577343,0.4175642145782178,-0.3696376783655682,-2.258006889135441,0.4175642145782178,-0.27776792869272604,-2.2674575353869977,0.4175642145782178,-0.18543586171525997,-2.273134222344395,0.4175642145782178,-0.09279515504647934,-2.2750275017224206,0.4175642145782178,-2.786105147956562e-16,-2.273134222344395,0.4175642145782178,0.09279515504647878,-2.2674575353869977,0.4175642145782178,0.18543586171525941,-2.258006889135441,0.4175642145782178,0.2777679286927255,-2.2447980132577348,0.4175642145782178,0.3696376783655667,-2.227852892624204,0.4175642145782178,0.46089220260200353,-2.2071997307158435,0.4175642145782178,0.5513796172522772,-2.1828729026824063,0.4175642145782178,0.640949314944828,-2.1549128981283605,0.4175642145782178,0.7294522157574053,-2.123366253721936,0.4175642145782178,0.816741015345886,-2.088285475739436,0.4175642145782178,0.9026704301178129,-2.0497289526737186,0.4175642145782178,0.9870974390425996,-2.007760858052316,0.4175642145782178,1.0698815216959243,-1.9624510436269296,0.4175642145782178,1.1508848921421002,-1.9138749231120826,0.4175642145782178,1.2299727282651753,-1.8621133466664355,0.4175642145782178,1.307013396167035,-1.8072524663256744,0.4175642145782178,1.3818786692590368,-1.7493835926109516,0.4175642145782178,1.4544439416825108,-1.688603042551536,0.4175642145782178,1.5245884357029127,-1.6250119793746194,0.4175642145782178,1.5921954027324474,-1.5587162441291098,0.4175642145782178,1.6571523176465708,-1.4898261795236538,0.4175642145782178,1.71935106607095,-1.4184564462720817,0.4175642145782178,1.7786881243271782,-1.3447258322519733,0.4175642145782178,1.8350647317377107,-1.2687570547939537,0.4175642145782178,1.888387055003272,-1.1906765564308046,0.4175642145782178,1.9385663443791237,-1.110614294446351,0.4175642145782178,1.9855190813902526,-1.0287035245743896,0.4175642145782178,2.0291671178396293,-0.9450805792076684,0.4175642145782178,2.0694378058781706,-0.85988464048607,0.4175642145782178,2.1062641189199187,-0.7732575086416786,0.4175642145782178,2.1395847632011735,-0.6853433659862933,0.4175642145782178,2.1693442797979157,-0.596288536934189,0.4175642145782178,2.1954931369317108,-0.5062412444595741,0.4175642145782178,2.2179878124104695,-0.41535136339408224,0.4175642145782178,2.236790866066838,-0.3237701709749024,0.4175642145782178,2.2518710020736625,-0.231650095058743,0.4175642145782178,2.263203121032806,-0.1391444604207109,0.4175642145782178,2.2707683617506187,-0.04640723356035926,0.4175642145782178,2.274554132630532,0.04640723356036044,0.4175642145782178,2.274554132630532,0.13914446042071008,0.4175642145782178,2.2707683617506187,0.23165009505874218,0.4175642145782178,2.2632031210328063,0.32377017097490157,0.4175642145782178,2.2518710020736625,0.4153513633940814,0.4175642145782178,2.2367908660668383,0.5062412444595733,0.4175642145782178,2.2179878124104695,0.5962885369341883,0.4175642145782178,2.1954931369317108,0.6853433659862926,0.4175642145782178,2.1693442797979157,0.7732575086416779,0.4175642145782178,2.139584763201174,0.8598846404860674,0.4175642145782178,2.1062641189199196,0.9450805792076694,0.4175642145782178,2.06943780587817,1.0287035245743905,0.4175642145782178,2.0291671178396284,1.1106142944463504,0.4175642145782178,1.985519081390253,1.1906765564308024,0.4175642145782178,1.9385663443791252,1.268757054793953,0.4175642145782178,1.8883870550032724,1.3447258322519744,0.4175642145782178,1.83506473173771,1.4184564462720812,0.4175642145782178,1.7786881243271786,1.4898261795236531,0.4175642145782178,1.7193510660709508,1.5587162441291091,0.4175642145782178,1.6571523176465712,1.625011979374618,0.4175642145782178,1.5921954027324488,1.688603042551536,0.4175642145782178,1.5245884357029127,1.7493835926109522,0.4175642145782178,1.45444394168251,1.8072524663256737,0.4175642145782178,1.3818786692590375,1.8621133466664344,0.4175642145782178,1.3070133961670365,1.9138749231120815,0.4175642145782178,1.229972728265177,1.9624510436269291,0.4175642145782178,1.1508848921421009,2.007760858052316,0.4175642145782178,1.069881521695924,2.049728952673718,0.4175642145782178,0.9870974390426013,2.088285475739435,0.4175642145782178,0.9026704301178147,2.1233662537219358,0.4175642145782178,0.8167410153458868,2.15491289812836,0.4175642145782178,0.729452215757407,2.1828729026824063,0.4175642145782178,0.6409493149448279,2.207199730715843,0.4175642145782178,0.551379617252278,2.227852892624204,0.4175642145782178,0.46089220260200436,2.2447980132577343,0.4175642145782178,0.3696376783655685,2.258006889135441,0.4175642145782178,0.2777679286927273,2.2674575353869977,0.4175642145782178,0.18543586171526022,2.273134222344395,0.4175642145782178,0.0927951550464786,2.2750275017224206,0.4175642145782178,0.0,2.2448878701031143,0.4359242263012693,0.0,2.2430196729462764,0.4359242263012693,-0.09156580208831042,2.2374181909055597,0.4359242263012693,-0.18297920193559744,2.228092747095016,0.4359242263012693,-0.2740880509593274,2.2150588627955465,0.4359242263012693,-0.36474070747175674,2.198338231621265,0.4359242263012693,-0.4547862890725555,2.1779586834126166,0.4359242263012693,-0.5440749237776724,2.1539541379163527,0.4359242263012693,-0.6324579994664616,2.126364548329464,0.4359242263012693,-0.7197884112318914,2.095235834801025,0.4359242263012693,-0.8059208062221433,2.060619808002636,0.4359242263012693,-0.8907118255660907,2.0225740828946757,0.4359242263012693,-0.9740203429799902,1.9811619828318867,0.4359242263012693,-1.0557076996582548,1.9364524341678973,0.4359242263012693,-1.1356379350573502,1.8885198515341106,0.4359242263012693,-1.213678013188703,1.8374440139838943,0.4359242263012693,-1.2896980440439738,1.7833099322082184,0.4359242263012693,-1.3635714997841577,1.7262077070437514,0.4359242263012693,-1.4351754253326843,1.6662323795089085,0.4359242263012693,-1.5043906430220102,1.6034837726174564,0.4359242263012693,-1.5711019509530837,1.5380663252329587,0.4359242263012693,-1.6351983147375364,1.470088918240593,0.4359242263012693,-1.6965730523034661,1.3996646933256651,0.4359242263012693,-1.7551240114572155,1.3269108646604375,0.4359242263012693,-1.8107537399056193,1.2519485238127086,0.4359242263012693,-1.863369647455726,1.1749024382008486,0.4359242263012693,-1.912884160122039,1.095900843430745,0.4359242263012693,-1.9592148658847703,1.0150752298602914,0.4359242263012693,-2.002284651856517,0.9325601237466624,0.4359242263012693,-2.0420218326290507,0.8484928633406377,0.4359242263012693,-2.078360269586604,0.7630133703006359,0.4359242263012693,-2.1112394809870723,0.6762639168069211,0.4359242263012693,-2.1406047426279002,0.588388888763603,0.4359242263012693,-2.1664071789291204,0.4995345454825465,0.4359242263012693,-2.18860384428193,0.4098487762491818,0.4359242263012693,-2.2071577945274203,0.3194808541753847,0.4359242263012693,-2.2220381484464804,0.22858118774911107,0.4359242263012693,-2.233220139158543,0.13730107049431584,0.4359242263012693,-2.2406851553436087,0.04579242915781088,0.4359242263012693,-2.2444207722189535,-0.04579242915781061,0.4359242263012693,-2.2444207722189535,-0.13730107049431556,0.4359242263012693,-2.2406851553436087,-0.22858118774911035,0.4359242263012693,-2.233220139158543,-0.31948085417538447,0.4359242263012693,-2.2220381484464804,-0.4098487762491816,0.4359242263012693,-2.2071577945274203,-0.4995345454825458,0.4359242263012693,-2.18860384428193,-0.5883888887636027,0.4359242263012693,-2.1664071789291204,-0.6762639168069203,0.4359242263012693,-2.1406047426279002,-0.7630133703006357,0.4359242263012693,-2.1112394809870723,-0.8484928633406374,0.4359242263012693,-2.0783602695866046,-0.9325601237466618,0.4359242263012693,-2.0420218326290507,-1.0150752298602916,0.4359242263012693,-2.002284651856517,-1.095900843430745,0.4359242263012693,-1.9592148658847703,-1.1749024382008493,0.4359242263012693,-1.9128841601220385,-1.2519485238127082,0.4359242263012693,-1.8633696474557264,-1.326910864660437,0.4359242263012693,-1.8107537399056195,-1.399664693325665,0.4359242263012693,-1.755124011457216,-1.4700889182405932,0.4359242263012693,-1.6965730523034661,-1.538066325232959,0.4359242263012693,-1.6351983147375364,-1.6034837726174564,0.4359242263012693,-1.5711019509530837,-1.6662323795089085,0.4359242263012693,-1.5043906430220102,-1.7262077070437507,0.4359242263012693,-1.4351754253326852,-1.7833099322082184,0.4359242263012693,-1.3635714997841575,-1.837444013983894,0.4359242263012693,-1.2896980440439745,-1.888519851534111,0.4359242263012693,-1.2136780131887028,-1.936452434167897,0.4359242263012693,-1.1356379350573504,-1.9811619828318865,0.4359242263012693,-1.055707699658255,-2.0225740828946757,0.4359242263012693,-0.9740203429799906,-2.0606198080026354,0.4359242263012693,-0.8907118255660909,-2.095235834801025,0.4359242263012693,-0.8059208062221435,-2.126364548329464,0.4359242263012693,-0.7197884112318914,-2.1539541379163523,0.4359242263012693,-0.6324579994664625,-2.1779586834126166,0.4359242263012693,-0.5440749237776722,-2.198338231621265,0.4359242263012693,-0.4547862890725562,-2.2150588627955465,0.4359242263012693,-0.3647407074717574,-2.228092747095016,0.4359242263012693,-0.274088050959328,-2.2374181909055597,0.4359242263012693,-0.1829792019355979,-2.2430196729462764,0.4359242263012693,-0.09156580208831076,-2.2448878701031143,0.4359242263012693,-2.749194744566498e-16,-2.2430196729462764,0.4359242263012693,0.09156580208831022,-2.2374181909055597,0.4359242263012693,0.18297920193559733,-2.228092747095016,0.4359242263012693,0.2740880509593275,-2.2150588627955465,0.4359242263012693,0.3647407074717559,-2.1983382316212654,0.4359242263012693,0.4547862890725547,-2.1779586834126166,0.4359242263012693,0.5440749237776726,-2.1539541379163527,0.4359242263012693,0.632457999466462,-2.126364548329464,0.4359242263012693,0.7197884112318909,-2.095235834801025,0.4359242263012693,0.805920806222143,-2.060619808002636,0.4359242263012693,0.8907118255660904,-2.0225740828946766,0.4359242263012693,0.9740203429799892,-1.9811619828318867,0.4359242263012693,1.0557076996582546,-1.9364524341678973,0.4359242263012693,1.13563793505735,-1.8885198515341106,0.4359242263012693,1.213678013188703,-1.8374440139838946,0.4359242263012693,1.2896980440439731,-1.7833099322082189,0.4359242263012693,1.363571499784157,-1.726207707043751,0.4359242263012693,1.4351754253326845,-1.6662323795089082,0.4359242263012693,1.5043906430220106,-1.6034837726174571,0.4359242263012693,1.5711019509530835,-1.5380663252329592,0.4359242263012693,1.6351983147375362,-1.4700889182405943,0.4359242263012693,1.696573052303465,-1.3996646933256651,0.4359242263012693,1.7551240114572155,-1.3269108646604366,0.4359242263012693,1.8107537399056197,-1.2519485238127086,0.4359242263012693,1.863369647455726,-1.1749024382008497,0.4359242263012693,1.9128841601220383,-1.0959008434307451,0.4359242263012693,1.9592148658847701,-1.0150752298602903,0.4359242263012693,2.0022846518565176,-0.9325601237466622,0.4359242263012693,2.0420218326290507,-0.8484928633406384,0.4359242263012693,2.078360269586604,-0.7630133703006352,0.4359242263012693,2.1112394809870723,-0.6762639168069213,0.4359242263012693,2.1406047426279002,-0.5883888887636043,0.4359242263012693,2.16640717892912,-0.49953454548254683,0.4359242263012693,2.18860384428193,-0.40984877624918115,0.4359242263012693,2.2071577945274203,-0.31948085417538447,0.4359242263012693,2.2220381484464804,-0.22858118774911187,0.4359242263012693,2.233220139158543,-0.1373010704943156,0.4359242263012693,2.2406851553436087,-0.045792429157809664,0.4359242263012693,2.2444207722189535,0.045792429157810836,0.4359242263012693,2.2444207722189535,0.13730107049431478,0.4359242263012693,2.2406851553436087,0.22858118774911104,0.4359242263012693,2.233220139158543,0.31948085417538363,0.4359242263012693,2.222038148446481,0.4098487762491803,0.4359242263012693,2.2071577945274203,0.499534545482546,0.4359242263012693,2.18860384428193,0.5883888887636035,0.4359242263012693,2.1664071789291204,0.6762639168069207,0.4359242263012693,2.1406047426279002,0.7630133703006345,0.4359242263012693,2.1112394809870727,0.8484928633406359,0.4359242263012693,2.078360269586605,0.9325601237466632,0.4359242263012693,2.0420218326290502,1.0150752298602912,0.4359242263012693,2.002284651856517,1.0959008434307447,0.4359242263012693,1.9592148658847706,1.1749024382008475,0.4359242263012693,1.9128841601220397,1.251948523812708,0.4359242263012693,1.8633696474557264,1.3269108646604375,0.4359242263012693,1.810753739905619,1.3996646933256647,0.4359242263012693,1.755124011457216,1.4700889182405938,0.4359242263012693,1.696573052303466,1.5380663252329585,0.4359242263012693,1.6351983147375369,1.6034837726174558,0.4359242263012693,1.5711019509530846,1.6662323795089082,0.4359242263012693,1.5043906430220106,1.7262077070437518,0.4359242263012693,1.4351754253326836,1.7833099322082182,0.4359242263012693,1.3635714997841577,1.8374440139838937,0.4359242263012693,1.2896980440439747,1.8885198515341097,0.4359242263012693,1.2136780131887046,1.936452434167897,0.4359242263012693,1.135637935057351,1.981161982831887,0.4359242263012693,1.0557076996582544,2.0225740828946757,0.4359242263012693,0.9740203429799908,2.060619808002635,0.4359242263012693,0.8907118255660922,2.095235834801025,0.4359242263012693,0.8059208062221438,2.1263645483294638,0.4359242263012693,0.7197884112318926,2.1539541379163527,0.4359242263012693,0.6324579994664619,2.177958683412616,0.4359242263012693,0.5440749237776734,2.198338231621265,0.4359242263012693,0.4547862890725555,2.2150588627955465,0.4359242263012693,0.36474070747175763,2.228092747095016,0.4359242263012693,0.27408805095932925,2.2374181909055597,0.4359242263012693,0.18297920193559816,2.2430196729462764,0.4359242263012693,0.09156580208831006,2.2448878701031143,0.4359242263012693,0.0,2.2135282194331882,0.4521124854565425,0.0,2.2116861197981916,0.4521124854565425,-0.09028668627809887,2.206162886886249,0.4521124854565425,-0.1804230992772056,2.196967713573459,0.4521124854565425,-0.27025921583337054,2.184115904318337,0.4521124854565425,-0.3596455125964374,2.1676288496890534,0.4521124854565425,-0.44843321489690313,2.1475339907609454,0.4521124854565425,-0.5364745443666727,2.1238647734435534,0.4521124854565425,-0.6236229649015693,2.096660592813208,0.4521124854565425,-0.7097334265562174,2.0659667275438087,0.4521124854565425,-0.7946626069653623,2.0318342645449463,0.4521124854565425,-0.8782691498898033,1.9943200139327772,0.4521124854565425,-0.9604139004899023,1.9534864144751953,0.4521124854565425,-1.0409601369350825,1.909401429668664,0.4521124854565425,-1.119773797963822,1.8621384346196836,0.4521124854565425,-1.1967237060153921,1.8117760939191674,0.4521124854565425,-1.271681785561964,1.7583982307129937,0.4521124854565425,-1.3445232762776829,1.7020936871866528,0.4521124854565425,-1.4151269406899176,1.6429561766961958,0.4521124854565425,-1.4833752659670714,1.5810841277916021,0.4521124854565425,-1.5491546595070904,1.516580520392173,0.4521124854565425,-1.612355638001141,1.4495527143866176,0.4521124854565425,-1.6728730096577713,1.380112270943114,0.4521124854565425,-1.7306060492842676,1.308374766826754,0.4521124854565425,-1.785458665933798,1.234459602033425,0.4521124854565425,-1.8373395628393077,1.1584898010602993,0.4521124854565425,-1.8861623893679804,1.080591808143701,0.4521124854565425,-1.9318458847433424,1.0008952768051522,0.4521124854565425,-1.9743140132958075,0.9195328540558804,0.4521124854565425,-2.013496091016545,0.836639959618963,0.4521124854565425,-2.049326903204035,0.7523545605365599,0.4521124854565425,-2.0817468130075043,0.6668169415373871,0.4521124854565425,-2.1107018606865786,0.580169471546633,0.4521124854565425,-2.136143853421946,0.4925563667269308,0.4521124854565425,-2.158030445527548,0.4041234504447885,0.4521124854565425,-2.176325208930793,0.31501791056198747,0.4521124854565425,-2.190997693803492,0.22538805445591029,0.4521124854565425,-2.2020234792425897,0.13538306217655036,0.4521124854565425,-2.209384213916348,0.045152738151038985,0.4521124854565425,-2.2130676466083283,-0.045152738151038714,0.4521124854565425,-2.2130676466083283,-0.13538306217655008,0.4521124854565425,-2.2093842139163486,-0.22538805445590954,0.4521124854565425,-2.2020234792425897,-0.3150179105619872,0.4521124854565425,-2.190997693803492,-0.4041234504447882,0.4521124854565425,-2.176325208930793,-0.49255636672693004,0.4521124854565425,-2.158030445527548,-0.5801694715466328,0.4521124854565425,-2.1361438534219466,-0.6668169415373864,0.4521124854565425,-2.110701860686579,-0.7523545605365596,0.4521124854565425,-2.0817468130075043,-0.8366399596189628,0.4521124854565425,-2.0493269032040353,-0.9195328540558798,0.4521124854565425,-2.0134960910165454,-1.0008952768051522,0.4521124854565425,-1.9743140132958075,-1.080591808143701,0.4521124854565425,-1.9318458847433424,-1.1584898010603,0.4521124854565425,-1.88616238936798,-1.2344596020334244,0.4521124854565425,-1.837339562839308,-1.3083747668267536,0.4521124854565425,-1.7854586659337983,-1.3801122709431137,0.4521124854565425,-1.730606049284268,-1.4495527143866178,0.4521124854565425,-1.6728730096577713,-1.5165805203921732,0.4521124854565425,-1.612355638001141,-1.5810841277916021,0.4521124854565425,-1.5491546595070904,-1.6429561766961958,0.4521124854565425,-1.4833752659670714,-1.7020936871866523,0.4521124854565425,-1.4151269406899183,-1.7583982307129937,0.4521124854565425,-1.3445232762776826,-1.811776093919167,0.4521124854565425,-1.2716817855619647,-1.8621384346196839,0.4521124854565425,-1.196723706015392,-1.9094014296686639,0.4521124854565425,-1.1197737979638223,-1.953486414475195,0.4521124854565425,-1.040960136935083,-1.994320013932777,0.4521124854565425,-0.9604139004899026,-2.0318342645449463,0.4521124854565425,-0.8782691498898034,-2.0659667275438087,0.4521124854565425,-0.7946626069653625,-2.096660592813208,0.4521124854565425,-0.7097334265562174,-2.123864773443553,0.4521124854565425,-0.6236229649015702,-2.1475339907609454,0.4521124854565425,-0.5364745443666725,-2.167628849689053,0.4521124854565425,-0.4484332148969038,-2.184115904318337,0.4521124854565425,-0.359645512596438,-2.196967713573459,0.4521124854565425,-0.2702592158333711,-2.206162886886249,0.4521124854565425,-0.18042309927720604,-2.2116861197981916,0.4521124854565425,-0.09028668627809923,-2.2135282194331882,0.4521124854565425,-2.710790248751194e-16,-2.2116861197981916,0.4521124854565425,0.09028668627809869,-2.206162886886249,0.4521124854565425,0.18042309927720548,-2.196967713573459,0.4521124854565425,0.27025921583337054,-2.184115904318337,0.4521124854565425,0.35964551259643657,-2.1676288496890534,0.4521124854565425,0.44843321489690235,-2.1475339907609454,0.4521124854565425,0.536474544366673,-2.1238647734435534,0.4521124854565425,0.6236229649015697,-2.096660592813208,0.4521124854565425,0.7097334265562169,-2.065966727543809,0.4521124854565425,0.794662606965362,-2.0318342645449463,0.4521124854565425,0.878269149889803,-1.9943200139327777,0.4521124854565425,0.9604139004899013,-1.9534864144751953,0.4521124854565425,1.0409601369350823,-1.909401429668664,0.4521124854565425,1.1197737979638216,-1.8621384346196836,0.4521124854565425,1.1967237060153921,-1.8117760939191676,0.4521124854565425,1.2716817855619635,-1.7583982307129942,0.4521124854565425,1.3445232762776822,-1.7020936871866525,0.4521124854565425,1.4151269406899178,-1.6429561766961953,0.4521124854565425,1.4833752659670716,-1.5810841277916026,0.4521124854565425,1.5491546595070902,-1.5165805203921736,0.4521124854565425,1.6123556380011408,-1.4495527143866187,0.4521124854565425,1.6728730096577702,-1.380112270943114,0.4521124854565425,1.7306060492842676,-1.3083747668267531,0.4521124854565425,1.7854586659337985,-1.234459602033425,0.4521124854565425,1.8373395628393077,-1.1584898010603004,0.4521124854565425,1.8861623893679798,-1.0805918081437011,0.4521124854565425,1.9318458847433422,-1.0008952768051511,0.4521124854565425,1.9743140132958084,-0.9195328540558803,0.4521124854565425,2.0134960910165454,-0.8366399596189638,0.4521124854565425,2.049326903204035,-0.7523545605365592,0.4521124854565425,2.0817468130075043,-0.6668169415373874,0.4521124854565425,2.1107018606865786,-0.5801694715466342,0.4521124854565425,2.1361438534219457,-0.4925563667269311,0.4521124854565425,2.158030445527548,-0.40412345044478776,0.4521124854565425,2.176325208930793,-0.3150179105619872,0.4521124854565425,2.190997693803492,-0.22538805445591104,0.4521124854565425,2.2020234792425897,-0.1353830621765501,0.4521124854565425,2.2093842139163486,-0.045152738151037784,0.4521124854565425,2.2130676466083283,0.045152738151038936,0.4521124854565425,2.2130676466083283,0.1353830621765493,0.4521124854565425,2.2093842139163486,0.22538805445591023,0.4521124854565425,2.2020234792425897,0.3150179105619864,0.4521124854565425,2.1909976938034923,0.404123450444787,0.4521124854565425,2.1763252089307934,0.4925563667269303,0.4521124854565425,2.158030445527548,0.5801694715466336,0.4521124854565425,2.136143853421946,0.6668169415373866,0.4521124854565425,2.110701860686579,0.7523545605365585,0.4521124854565425,2.0817468130075047,0.8366399596189611,0.4521124854565425,2.0493269032040358,0.9195328540558813,0.4521124854565425,2.013496091016545,1.000895276805152,0.4521124854565425,1.9743140132958075,1.0805918081437007,0.4521124854565425,1.9318458847433426,1.1584898010602982,0.4521124854565425,1.8861623893679813,1.2344596020334242,0.4521124854565425,1.837339562839308,1.308374766826754,0.4521124854565425,1.7854586659337979,1.3801122709431135,0.4521124854565425,1.730606049284268,1.4495527143866183,0.4521124854565425,1.6728730096577709,1.5165805203921727,0.4521124854565425,1.6123556380011412,1.5810841277916015,0.4521124854565425,1.5491546595070913,1.6429561766961953,0.4521124854565425,1.4833752659670716,1.7020936871866532,0.4521124854565425,1.415126940689917,1.7583982307129935,0.4521124854565425,1.3445232762776829,1.8117760939191667,0.4521124854565425,1.271681785561965,1.8621384346196828,0.4521124854565425,1.1967237060153937,1.9094014296686639,0.4521124854565425,1.1197737979638225,1.9534864144751956,0.4521124854565425,1.0409601369350823,1.994320013932777,0.4521124854565425,0.9604139004899028,2.031834264544946,0.4521124854565425,0.8782691498898046,2.0659667275438087,0.4521124854565425,0.7946626069653628,2.0966605928132074,0.4521124854565425,0.7097334265562186,2.1238647734435534,0.4521124854565425,0.6236229649015695,2.147533990760945,0.4521124854565425,0.5364745443666739,2.1676288496890534,0.4521124854565425,0.44843321489690313,2.184115904318337,0.4521124854565425,0.3596455125964383,2.196967713573459,0.4521124854565425,0.2702592158333723,2.206162886886249,0.4521124854565425,0.1804230992772063,2.2116861197981916,0.4521124854565425,0.09028668627809852,2.2135282194331882,0.4521124854565425,0.0,2.1811047819148413,0.46604834295121983,0.0,2.1792896650857982,0.46604834295121983,-0.08896418010646934,2.173847335681499,0.46604834295121983,-0.17778028811495214,2.1647868519221376,0.46604834295121983,-0.26630049837885594,2.152123294088761,0.46604834295121983,-0.3543774777441822,2.1358777394236257,0.46604834295121983,-0.4418646307710209,2.1160772270492116,0.46604834295121983,-0.5286163437271929,2.092754712964271,0.46604834295121983,-0.614488226947937,2.065949015191818,0.46604834295121983,-0.6993373551582569,2.0357047491703626,0.46604834295121983,-0.7830225053579358,2.0020722534959168,0.46604834295121983,-0.8654043918732846,1.9651075061383674,0.46604834295121983,-0.9463458981843974,1.9248720312716712,0.46604834295121983,-1.0257123051420698,1.881432796872938,0.46604834295121983,-1.1033715151945247,1.8348621032608432,0.46604834295121983,-1.1791942722507527,1.785237462758881,0.46604834295121983,-1.2530543768145181,1.7326414706837523,0.46604834295121983,-1.3248288960309673,1.6771616678736119,0.46604834295121983,-1.394398368296231,1.6188903949849816,0.46604834295121983,-1.4616470020894776,1.5579246388008414,0.46604834295121983,-1.5264628686964694,1.4943658708057,0.46604834295121983,-1.588738088503863,1.4283198782963238,0.46604834295121983,-1.6483690105541786,1.3598965883092207,0.46604834295121983,-1.705256385062589,1.2892098846579367,0.46604834295121983,-1.759305528608393,1.2163774183846874,0.46604834295121983,-1.810426481726223,1.141520411941809,0.46604834295121983,-1.8585341586346995,1.0647634574289508,0.46604834295121983,-1.9035484888533136,0.9862343092218202,0.46604834295121983,-1.9453945504718435,0.9060636713376304,0.46604834295121983,-1.9840026948504785,0.8243849798911671,0.46604834295121983,-2.019308662543106,0.7413341810035413,0.46604834295121983,-2.0512536902508174,0.6570495045332865,0.46604834295121983,-2.0797846086276217,0.5716712340064035,0.46604834295121983,-2.1048539307755734,0.485341473128274,0.46604834295121983,-2.1264199312820278,0.39820390926606763,0.46604834295121983,-2.1444467156674722,0.3104035742952999,0.46604834295121983,-2.1589042801283456,0.22208660320858697,0.46604834295121983,-2.1697685614754074,0.133399990888375,0.46604834295121983,-2.177021477184543,0.04449134744846308,0.46604834295121983,-2.1806509554933378,-0.044491347448462816,0.46604834295121983,-2.1806509554933378,-0.13339999088837473,0.46604834295121983,-2.177021477184543,-0.22208660320858625,0.46604834295121983,-2.1697685614754074,-0.3104035742952997,0.46604834295121983,-2.1589042801283456,-0.39820390926606736,0.46604834295121983,-2.1444467156674722,-0.4853414731282733,0.46604834295121983,-2.1264199312820278,-0.5716712340064032,0.46604834295121983,-2.104853930775574,-0.6570495045332858,0.46604834295121983,-2.079784608627622,-0.7413341810035411,0.46604834295121983,-2.0512536902508174,-0.8243849798911669,0.46604834295121983,-2.0193086625431063,-0.9060636713376299,0.46604834295121983,-1.9840026948504788,-0.9862343092218203,0.46604834295121983,-1.9453945504718435,-1.0647634574289508,0.46604834295121983,-1.9035484888533136,-1.1415204119418094,0.46604834295121983,-1.858534158634699,-1.2163774183846867,0.46604834295121983,-1.8104264817262234,-1.2892098846579363,0.46604834295121983,-1.7593055286083932,-1.3598965883092204,0.46604834295121983,-1.7052563850625895,-1.428319878296324,0.46604834295121983,-1.6483690105541786,-1.4943658708057004,0.46604834295121983,-1.588738088503863,-1.5579246388008414,0.46604834295121983,-1.5264628686964694,-1.6188903949849816,0.46604834295121983,-1.4616470020894776,-1.6771616678736114,0.46604834295121983,-1.3943983682962318,-1.7326414706837523,0.46604834295121983,-1.324828896030967,-1.7852374627588805,0.46604834295121983,-1.2530543768145188,-1.8348621032608434,0.46604834295121983,-1.1791942722507525,-1.8814327968729379,0.46604834295121983,-1.103371515194525,-1.9248720312716707,0.46604834295121983,-1.02571230514207,-1.9651075061383672,0.46604834295121983,-0.9463458981843977,-2.0020722534959163,0.46604834295121983,-0.8654043918732847,-2.0357047491703626,0.46604834295121983,-0.7830225053579359,-2.065949015191818,0.46604834295121983,-0.6993373551582569,-2.0927547129642705,0.46604834295121983,-0.6144882269479379,-2.1160772270492116,0.46604834295121983,-0.5286163437271928,-2.1358777394236252,0.46604834295121983,-0.44186463077102156,-2.152123294088761,0.46604834295121983,-0.3543774777441828,-2.1647868519221376,0.46604834295121983,-0.2663004983788565,-2.173847335681499,0.46604834295121983,-0.1777802881149526,-2.1792896650857982,0.46604834295121983,-0.08896418010646968,-2.1811047819148413,0.46604834295121983,-2.671082989776996e-16,-2.1792896650857982,0.46604834295121983,0.08896418010646916,-2.173847335681499,0.46604834295121983,0.17778028811495206,-2.1647868519221376,0.46604834295121983,0.266300498378856,-2.152123294088761,0.46604834295121983,0.3543774777441813,-2.1358777394236257,0.46604834295121983,0.4418646307710201,-2.1160772270492116,0.46604834295121983,0.5286163437271932,-2.092754712964271,0.46604834295121983,0.6144882269479375,-2.065949015191818,0.46604834295121983,0.6993373551582563,-2.0357047491703626,0.46604834295121983,0.7830225053579355,-2.0020722534959168,0.46604834295121983,0.8654043918732841,-1.9651075061383678,0.46604834295121983,0.9463458981843964,-1.9248720312716712,0.46604834295121983,1.0257123051420696,-1.881432796872938,0.46604834295121983,1.1033715151945245,-1.8348621032608432,0.46604834295121983,1.1791942722507527,-1.7852374627588814,0.46604834295121983,1.2530543768145175,-1.732641470683753,0.46604834295121983,1.3248288960309667,-1.6771616678736117,0.46604834295121983,1.3943983682962313,-1.6188903949849813,0.46604834295121983,1.4616470020894778,-1.5579246388008419,0.46604834295121983,1.5264628686964692,-1.4943658708057006,0.46604834295121983,1.5887380885038629,-1.428319878296325,0.46604834295121983,1.6483690105541775,-1.3598965883092207,0.46604834295121983,1.705256385062589,-1.2892098846579356,0.46604834295121983,1.7593055286083934,-1.2163774183846874,0.46604834295121983,1.810426481726223,-1.14152041194181,0.46604834295121983,1.8585341586346988,-1.064763457428951,0.46604834295121983,1.9035484888533134,-0.9862343092218191,0.46604834295121983,1.9453945504718442,-0.9060636713376303,0.46604834295121983,1.9840026948504788,-0.8243849798911679,0.46604834295121983,2.019308662543106,-0.7413341810035406,0.46604834295121983,2.0512536902508174,-0.6570495045332867,0.46604834295121983,2.0797846086276217,-0.5716712340064046,0.46604834295121983,2.1048539307755734,-0.4853414731282743,0.46604834295121983,2.1264199312820278,-0.39820390926606697,0.46604834295121983,2.1444467156674722,-0.3104035742952997,0.46604834295121983,2.1589042801283456,-0.22208660320858775,0.46604834295121983,2.1697685614754074,-0.13339999088837476,0.46604834295121983,2.177021477184543,-0.0444913474484619,0.46604834295121983,2.1806509554933378,0.04449134744846304,0.46604834295121983,2.1806509554933378,0.13339999088837395,0.46604834295121983,2.177021477184543,0.22208660320858695,0.46604834295121983,2.1697685614754074,0.3104035742952989,0.46604834295121983,2.1589042801283456,0.39820390926606614,0.46604834295121983,2.1444467156674727,0.4853414731282735,0.46604834295121983,2.1264199312820278,0.571671234006404,0.46604834295121983,2.1048539307755734,0.657049504533286,0.46604834295121983,2.079784608627622,0.74133418100354,0.46604834295121983,2.051253690250818,0.8243849798911653,0.46604834295121983,2.0193086625431067,0.9060636713376313,0.46604834295121983,1.9840026948504783,0.9862343092218201,0.46604834295121983,1.9453945504718435,1.0647634574289506,0.46604834295121983,1.9035484888533138,1.1415204119418079,0.46604834295121983,1.8585341586347002,1.2163774183846865,0.46604834295121983,1.8104264817262234,1.2892098846579367,0.46604834295121983,1.7593055286083927,1.3598965883092202,0.46604834295121983,1.7052563850625895,1.4283198782963245,0.46604834295121983,1.6483690105541784,1.4943658708056997,0.46604834295121983,1.5887380885038633,1.5579246388008408,0.46604834295121983,1.5264628686964703,1.6188903949849813,0.46604834295121983,1.4616470020894778,1.6771616678736123,0.46604834295121983,1.3943983682962302,1.732641470683752,0.46604834295121983,1.3248288960309673,1.7852374627588803,0.46604834295121983,1.253054376814519,1.8348621032608423,0.46604834295121983,1.179194272250754,1.8814327968729379,0.46604834295121983,1.1033715151945251,1.9248720312716714,0.46604834295121983,1.0257123051420693,1.9651075061383672,0.46604834295121983,0.946345898184398,2.002072253495916,0.46604834295121983,0.8654043918732859,2.0357047491703626,0.46604834295121983,0.7830225053579363,2.0659490151918174,0.46604834295121983,0.6993373551582581,2.092754712964271,0.46604834295121983,0.6144882269479374,2.1160772270492116,0.46604834295121983,0.528616343727194,2.1358777394236257,0.46604834295121983,0.4418646307710209,2.152123294088761,0.46604834295121983,0.35437747774418304,2.1647868519221376,0.46604834295121983,0.2663004983788577,2.173847335681499,0.46604834295121983,0.17778028811495286,2.1792896650857982,0.46604834295121983,0.08896418010646899,2.1811047819148413,0.46604834295121983,0.0,2.147779089482842,0.47766237104425774,0.0,2.145991706316883,0.47766237104425774,-0.08760487223264479,2.140632531741891,0.47766237104425774,-0.1750639348011101,2.131710485575053,0.47766237104425774,-0.2622316207270126,2.1192404176817052,0.47766237104425774,-0.34896284799963495,2.103243083259195,0.47766237104425774,-0.43511326105061426,2.083745108291905,0.47766237104425774,-0.5205394710195395,2.0607789452349294,0.47766237104425774,-0.6050992944105589,2.034382819000166,0.47766237104425774,-0.6886519897427764,2.004600663334719,0.47766237104425774,-0.7710584918005574,1.9714820476975188,0.47766237104425774,-0.8521816430938558,1.9350820947558447,0.47766237104425774,-0.9318864221433197,1.895461388639087,0.47766237104425774,-1.01004016821022,1.8526858741024428,0.47766237104425774,-1.0865128020971557,1.8068267467683785,0.47766237104425774,-1.1611770426520427,1.7579603346285424,0.47766237104425774,-1.2339086186150288,1.706167971003356,0.47766237104425774,-1.3045864754557428,1.6515358591707279,0.47766237104425774,-1.3730929768566114,1.5941549288892065,0.47766237104425774,-1.4393141005069026,1.5341206850543692,0.47766237104425774,-1.5031396278816078,1.471533048740349,0.47766237104425774,-1.5644633276893003,1.4064961908910698,0.47766237104425774,-1.6231831326836341,1.3391183589379883,0.47766237104425774,-1.6792013095442053,1.2695116966329283,0.47766237104425774,-1.7324246215440169,1.1977920573958725,0.47766237104425774,-1.7827644837328087,1.1240788114883802,0.47766237104425774,-1.8301371103779676,1.0484946473335655,0.47766237104425774,-1.8744636544176063,0.9711653673133269,0.47766237104425774,-1.915670338693721,0.8922196783826946,0.47766237104425774,-1.953688578746988,0.8117889778498101,0.47766237104425774,-1.9884550969688266,0.7300071346780719,0.47766237104425774,-2.019912027920735,0.6470102666744568,0.47766237104425774,-2.0480070146456026,0.5629365139348655,0.47766237104425774,-2.072693295810696,0.4779258089235646,0.47766237104425774,-2.093929783537285,0.3921196435694098,0.47766237104425774,-2.1116811317873596,0.30566083376649467,0.47766237104425774,-2.1259177951936232,0.2186932816711874,0.47766237104425774,-2.136616078234841,0.131361736191196,0.47766237104425774,-2.1437581746746948,0.04381155206529447,0.47766237104425774,-2.1473321971985087,-0.043811552065294204,0.47766237104425774,-2.1473321971985087,-0.13136173619119573,0.47766237104425774,-2.143758174674695,-0.21869328167118668,0.47766237104425774,-2.136616078234841,-0.30566083376649444,0.47766237104425774,-2.1259177951936232,-0.3921196435694096,0.47766237104425774,-2.1116811317873596,-0.47792580892356396,0.47766237104425774,-2.093929783537285,-0.5629365139348653,0.47766237104425774,-2.0726932958106965,-0.6470102666744562,0.47766237104425774,-2.048007014645603,-0.7300071346780717,0.47766237104425774,-2.019912027920735,-0.8117889778498099,0.47766237104425774,-1.9884550969688268,-0.892219678382694,0.47766237104425774,-1.9536885787469882,-0.971165367313327,0.47766237104425774,-1.915670338693721,-1.0484946473335655,0.47766237104425774,-1.8744636544176063,-1.1240788114883806,0.47766237104425774,-1.8301371103779671,-1.197792057395872,0.47766237104425774,-1.7827644837328092,-1.2695116966329276,0.47766237104425774,-1.732424621544017,-1.339118358937988,0.47766237104425774,-1.6792013095442058,-1.40649619089107,0.47766237104425774,-1.6231831326836341,-1.4715330487403493,0.47766237104425774,-1.5644633276893003,-1.5341206850543692,0.47766237104425774,-1.5031396278816078,-1.5941549288892065,0.47766237104425774,-1.4393141005069026,-1.6515358591707274,0.47766237104425774,-1.373092976856612,-1.706167971003356,0.47766237104425774,-1.3045864754557426,-1.757960334628542,0.47766237104425774,-1.2339086186150297,-1.8068267467683787,0.47766237104425774,-1.1611770426520425,-1.8526858741024426,0.47766237104425774,-1.086512802097156,-1.8954613886390868,0.47766237104425774,-1.0100401682102202,-1.9350820947558445,0.47766237104425774,-0.9318864221433202,-1.9714820476975186,0.47766237104425774,-0.8521816430938559,-2.004600663334719,0.47766237104425774,-0.7710584918005575,-2.034382819000166,0.47766237104425774,-0.6886519897427764,-2.0607789452349294,0.47766237104425774,-0.6050992944105597,-2.083745108291905,0.47766237104425774,-0.5205394710195393,-2.1032430832591946,0.47766237104425774,-0.43511326105061493,-2.1192404176817052,0.47766237104425774,-0.34896284799963556,-2.131710485575053,0.47766237104425774,-0.26223162072701317,-2.140632531741891,0.47766237104425774,-0.17506393480111054,-2.145991706316883,0.47766237104425774,-0.08760487223264514,-2.147779089482842,0.47766237104425774,-2.630270787210779e-16,-2.145991706316883,0.47766237104425774,0.08760487223264461,-2.140632531741891,0.47766237104425774,0.17506393480111002,-2.131710485575053,0.47766237104425774,0.26223162072701267,-2.1192404176817057,0.47766237104425774,0.3489628479996341,-2.1032430832591955,0.47766237104425774,0.4351132610506135,-2.083745108291905,0.47766237104425774,0.5205394710195397,-2.0607789452349294,0.47766237104425774,0.6050992944105592,-2.034382819000166,0.47766237104425774,0.688651989742776,-2.004600663334719,0.47766237104425774,0.7710584918005571,-1.9714820476975188,0.47766237104425774,0.8521816430938555,-1.9350820947558451,0.47766237104425774,0.9318864221433188,-1.895461388639087,0.47766237104425774,1.0100401682102198,-1.8526858741024428,0.47766237104425774,1.0865128020971555,-1.8068267467683785,0.47766237104425774,1.1611770426520427,-1.7579603346285426,0.47766237104425774,1.2339086186150283,-1.7061679710033564,0.47766237104425774,1.3045864754557421,-1.6515358591707276,0.47766237104425774,1.3730929768566116,-1.5941549288892063,0.47766237104425774,1.4393141005069028,-1.5341206850543696,0.47766237104425774,1.5031396278816076,-1.4715330487403495,0.47766237104425774,1.5644633276893,-1.406496190891071,0.47766237104425774,1.6231831326836332,-1.3391183589379883,0.47766237104425774,1.6792013095442053,-1.2695116966329272,0.47766237104425774,1.7324246215440173,-1.1977920573958725,0.47766237104425774,1.7827644837328087,-1.124078811488381,0.47766237104425774,1.830137110377967,-1.0484946473335657,0.47766237104425774,1.874463654417606,-0.9711653673133258,0.47766237104425774,1.9156703386937217,-0.8922196783826944,0.47766237104425774,1.9536885787469882,-0.8117889778498107,0.47766237104425774,1.9884550969688264,-0.7300071346780713,0.47766237104425774,2.019912027920735,-0.647010266674457,0.47766237104425774,2.0480070146456026,-0.5629365139348668,0.47766237104425774,2.072693295810696,-0.47792580892356495,0.47766237104425774,2.093929783537285,-0.39211964356940915,0.47766237104425774,2.1116811317873596,-0.30566083376649444,0.47766237104425774,2.1259177951936232,-0.21869328167118815,0.47766237104425774,2.136616078234841,-0.13136173619119576,0.47766237104425774,2.143758174674695,-0.0438115520652933,0.47766237104425774,2.1473321971985087,0.04381155206529442,0.47766237104425774,2.1473321971985087,0.13136173619119498,0.47766237104425774,2.143758174674695,0.21869328167118737,0.47766237104425774,2.136616078234841,0.30566083376649367,0.47766237104425774,2.1259177951936237,0.39211964356940837,0.47766237104425774,2.11168113178736,0.4779258089235642,0.47766237104425774,2.093929783537285,0.562936513934866,0.47766237104425774,2.072693295810696,0.6470102666744564,0.47766237104425774,2.048007014645603,0.7300071346780707,0.47766237104425774,2.0199120279207357,0.8117889778498083,0.47766237104425774,1.9884550969688273,0.8922196783826954,0.47766237104425774,1.9536885787469878,0.9711653673133268,0.47766237104425774,1.915670338693721,1.0484946473335652,0.47766237104425774,1.8744636544176065,1.1240788114883788,0.47766237104425774,1.8301371103779682,1.1977920573958718,0.47766237104425774,1.7827644837328092,1.2695116966329283,0.47766237104425774,1.7324246215440167,1.3391183589379878,0.47766237104425774,1.6792013095442058,1.4064961908910705,0.47766237104425774,1.623183132683634,1.4715330487403488,0.47766237104425774,1.5644633276893005,1.5341206850543683,0.47766237104425774,1.5031396278816087,1.5941549288892063,0.47766237104425774,1.4393141005069028,1.6515358591707283,0.47766237104425774,1.3730929768566107,1.7061679710033557,0.47766237104425774,1.3045864754557428,1.7579603346285417,0.47766237104425774,1.2339086186150299,1.8068267467683774,0.47766237104425774,1.161177042652044,1.8526858741024426,0.47766237104425774,1.0865128020971562,1.8954613886390872,0.47766237104425774,1.0100401682102196,1.9350820947558445,0.47766237104425774,0.9318864221433204,1.9714820476975181,0.47766237104425774,0.8521816430938571,2.004600663334719,0.47766237104425774,0.7710584918005579,2.0343828190001654,0.47766237104425774,0.6886519897427776,2.0607789452349294,0.47766237104425774,0.6050992944105591,2.0837451082919047,0.47766237104425774,0.5205394710195405,2.103243083259195,0.47766237104425774,0.43511326105061426,2.1192404176817052,0.47766237104425774,0.3489628479996358,2.131710485575053,0.47766237104425774,0.2622316207270144,2.140632531741891,0.47766237104425774,0.1750639348011108,2.145991706316883,0.47766237104425774,0.08760487223264446,2.147779089482842,0.47766237104425774,0.0,2.1137171690605534,0.48689670923190015,0.0,2.1119581322470946,0.48689670923190015,-0.0862155346600774,2.106683949549897,0.48689670923190015,-0.17228757207125228,2.097903399325555,0.48689670923190015,-0.2580728538216313,2.0856310959333717,0.48689670923190015,-0.34342859877581894,2.0698874654112007,0.48689670923190015,-0.4282127407200261,2.0506987114783195,0.48689670923190015,-0.5122841648172629,2.028096771921928,0.48689670923190015,-0.5955029424790582,2.0021192654398505,0.48689670923190015,-0.6777305642627858,1.9728094290279286,0.48689670923190015,-0.7588301704069618,1.9402160460163065,0.48689670923190015,-0.8386667786208121,1.9043933648743931,0.48689670923190015,-0.9171075087489725,1.8654010089196387,0.48689670923190015,-0.9940218039373955,1.823303877080407,0.48689670923190015,-1.0692816479323473,1.7781720358781137,0.48689670923190015,-1.142761778150829,1.730080602808416,0.48689670923190015,-1.2143398941677854,1.6791096213155547,0.48689670923190015,-1.283896861273091,1.6253439275679415,0.48689670923190015,-1.3513169087595174,1.568873009256728,0.48689670923190015,-1.416487822611652,1.509790856652375,0.48689670923190015,-1.479301132275051,1.448195806167124,0.48689670923190015,-1.5396522911947728,1.384190376683744,0.48689670923190015,-1.597440850822801,1.3178810989229677,0.48689670923190015,-1.6525706278047394,1.2493783381336203,0.48689670923190015,-1.7049498640675138,1.1787961104005549,0.48689670923190015,-1.7544913795416273,1.1062518928761325,0.48689670923190015,-1.8011127172637849,1.0318664282510943,0.48689670923190015,-1.8447362806183678,0.9557635237902696,0.48689670923190015,-1.885289462489346,0.8780698452676003,0.48689670923190015,-1.9227047661076548,0.7989147061434637,0.48689670923190015,-1.9569199173929073,0.7184298523351776,0.48689670923190015,-1.9878779686024528,0.6367492429389219,0.48689670923190015,-2.015527393115278,0.5540088272680459,0.48689670923190015,-2.0398221711929803,0.47034631857885134,0.48689670923190015,-2.06072186657508,0.385900964860468,0.48689670923190015,-2.0781916937811875,0.30081331707031916,0.48689670923190015,-2.0922025760080003,0.21522499520092161,0.48689670923190015,-2.1027311935247748,0.12927845256738738,0.48689670923190015,-2.1097600224867143,0.04311673870793734,0.48689670923190015,-2.1132773641016818,-0.043116738707937086,0.48689670923190015,-2.1132773641016818,-0.12927845256738713,0.48689670923190015,-2.109760022486715,-0.21522499520092092,0.48689670923190015,-2.1027311935247748,-0.30081331707031894,0.48689670923190015,-2.0922025760080003,-0.38590096486046777,0.48689670923190015,-2.0781916937811875,-0.4703463185788506,0.48689670923190015,-2.06072186657508,-0.5540088272680457,0.48689670923190015,-2.0398221711929807,-0.6367492429389212,0.48689670923190015,-2.0155273931152786,-0.7184298523351774,0.48689670923190015,-1.987877968602453,-0.7989147061434635,0.48689670923190015,-1.9569199173929075,-0.8780698452675998,0.48689670923190015,-1.9227047661076553,-0.9557635237902697,0.48689670923190015,-1.885289462489346,-1.031866428251094,0.48689670923190015,-1.8447362806183678,-1.106251892876133,0.48689670923190015,-1.8011127172637844,-1.1787961104005544,0.48689670923190015,-1.7544913795416277,-1.2493783381336199,0.48689670923190015,-1.704949864067514,-1.3178810989229675,0.48689670923190015,-1.6525706278047398,-1.3841903766837442,0.48689670923190015,-1.597440850822801,-1.4481958061671243,0.48689670923190015,-1.5396522911947728,-1.509790856652375,0.48689670923190015,-1.479301132275051,-1.568873009256728,0.48689670923190015,-1.416487822611652,-1.625343927567941,0.48689670923190015,-1.351316908759518,-1.6791096213155547,0.48689670923190015,-1.2838968612730908,-1.7300806028084155,0.48689670923190015,-1.214339894167786,-1.778172035878114,0.48689670923190015,-1.1427617781508288,-1.8233038770804069,0.48689670923190015,-1.0692816479323475,-1.8654010089196384,0.48689670923190015,-0.9940218039373957,-1.904393364874393,0.48689670923190015,-0.9171075087489728,-1.9402160460163063,0.48689670923190015,-0.8386667786208122,-1.9728094290279286,0.48689670923190015,-0.7588301704069619,-2.0021192654398505,0.48689670923190015,-0.6777305642627858,-2.0280967719219274,0.48689670923190015,-0.595502942479059,-2.0506987114783195,0.48689670923190015,-0.5122841648172627,-2.0698874654112003,0.48689670923190015,-0.42821274072002674,-2.0856310959333717,0.48689670923190015,-0.3434285987758195,-2.097903399325555,0.48689670923190015,-0.25807285382163186,-2.106683949549897,0.48689670923190015,-0.17228757207125273,-2.1119581322470946,0.48689670923190015,-0.08621553466007774,-2.1137171690605534,0.48689670923190015,-2.588556965392812e-16,-2.1119581322470946,0.48689670923190015,0.08621553466007723,-2.106683949549897,0.48689670923190015,0.1722875720712522,-2.097903399325555,0.48689670923190015,0.25807285382163137,-2.085631095933372,0.48689670923190015,0.3434285987758181,-2.0698874654112007,0.48689670923190015,0.42821274072002535,-2.0506987114783195,0.48689670923190015,0.5122841648172631,-2.028096771921928,0.48689670923190015,0.5955029424790586,-2.0021192654398505,0.48689670923190015,0.6777305642627852,-1.9728094290279288,0.48689670923190015,0.7588301704069614,-1.9402160460163065,0.48689670923190015,0.8386667786208117,-1.9043933648743936,0.48689670923190015,0.9171075087489716,-1.8654010089196387,0.48689670923190015,0.9940218039373953,-1.823303877080407,0.48689670923190015,1.069281647932347,-1.7781720358781137,0.48689670923190015,1.142761778150829,-1.7300806028084161,0.48689670923190015,1.214339894167785,-1.6791096213155554,0.48689670923190015,1.2838968612730903,-1.6253439275679413,0.48689670923190015,1.3513169087595176,-1.5688730092567278,0.48689670923190015,1.4164878226116522,-1.5097908566523754,0.48689670923190015,1.4793011322750507,-1.4481958061671245,0.48689670923190015,1.5396522911947725,-1.3841903766837451,0.48689670923190015,1.5974408508228,-1.3178810989229677,0.48689670923190015,1.6525706278047394,-1.2493783381336192,0.48689670923190015,1.7049498640675143,-1.1787961104005549,0.48689670923190015,1.7544913795416273,-1.1062518928761336,0.48689670923190015,1.8011127172637842,-1.0318664282510945,0.48689670923190015,1.8447362806183676,-0.9557635237902686,0.48689670923190015,1.8852894624893466,-0.8780698452676002,0.48689670923190015,1.9227047661076553,-0.7989147061434645,0.48689670923190015,1.956919917392907,-0.718429852335177,0.48689670923190015,1.987877968602453,-0.6367492429389222,0.48689670923190015,2.015527393115278,-0.5540088272680471,0.48689670923190015,2.0398221711929803,-0.4703463185788516,0.48689670923190015,2.06072186657508,-0.3859009648604674,0.48689670923190015,2.0781916937811875,-0.30081331707031894,0.48689670923190015,2.0922025760080003,-0.21522499520092236,0.48689670923190015,2.1027311935247743,-0.12927845256738715,0.48689670923190015,2.109760022486715,-0.0431167387079362,0.48689670923190015,2.1132773641016818,0.043116738707937294,0.48689670923190015,2.1132773641016818,0.12927845256738638,0.48689670923190015,2.109760022486715,0.2152249952009216,0.48689670923190015,2.1027311935247748,0.30081331707031816,0.48689670923190015,2.0922025760080003,0.3859009648604666,0.48689670923190015,2.0781916937811875,0.4703463185788509,0.48689670923190015,2.06072186657508,0.5540088272680465,0.48689670923190015,2.0398221711929803,0.6367492429389214,0.48689670923190015,2.0155273931152786,0.7184298523351763,0.48689670923190015,1.9878779686024535,0.7989147061434619,0.48689670923190015,1.956919917392908,0.8780698452676012,0.48689670923190015,1.9227047661076546,0.9557635237902695,0.48689670923190015,1.885289462489346,1.0318664282510939,0.48689670923190015,1.844736280618368,1.1062518928761313,0.48689670923190015,1.8011127172637855,1.1787961104005542,0.48689670923190015,1.7544913795416277,1.2493783381336203,0.48689670923190015,1.7049498640675136,1.3178810989229672,0.48689670923190015,1.6525706278047398,1.3841903766837447,0.48689670923190015,1.5974408508228009,1.4481958061671238,0.48689670923190015,1.539652291194773,1.5097908566523743,0.48689670923190015,1.4793011322750518,1.5688730092567278,0.48689670923190015,1.4164878226116522,1.6253439275679422,0.48689670923190015,1.3513169087595165,1.6791096213155545,0.48689670923190015,1.283896861273091,1.7300806028084152,0.48689670923190015,1.2143398941677863,1.7781720358781128,0.48689670923190015,1.1427617781508306,1.8233038770804069,0.48689670923190015,1.0692816479323477,1.8654010089196391,0.48689670923190015,0.9940218039373951,1.904393364874393,0.48689670923190015,0.9171075087489731,1.9402160460163058,0.48689670923190015,0.8386667786208133,1.9728094290279286,0.48689670923190015,0.7588301704069622,2.00211926543985,0.48689670923190015,0.6777305642627869,2.028096771921928,0.48689670923190015,0.5955029424790584,2.050698711478319,0.48689670923190015,0.5122841648172639,2.0698874654112007,0.48689670923190015,0.4282127407200261,2.0856310959333717,0.48689670923190015,0.34342859877581977,2.097903399325555,0.48689670923190015,0.25807285382163303,2.106683949549897,0.48689670923190015,0.17228757207125295,2.1119581322470946,0.48689670923190015,0.08621553466007706,2.1137171690605534,0.48689670923190015,0.0,2.0790887154221522,0.4937053525057976,0.0,2.0773584964305076,0.4937053525057976,-0.08480308899866786,2.072170719234331,0.4937053525057976,-0.16946503162484353,2.0635340183767887,0.4937053525057976,-0.2538449164302422,2.051462768794109,0.4937053525057976,-0.3378023014239858,2.035977061889921,0.4937053525057976,-0.4211974478244366,2.017102672095095,0.4937053525057976,-0.5038915526406081,1.9948710139687464,0.4937053525057976,-0.5857469796960465,1.9693190899118032,0.4937053525057976,-0.6666274887106616,1.9404894285801595,0.4937053525057976,-0.7463984620592283,1.908430014099927,0.4937053525057976,-0.8249271288291392,1.8731942062025915,0.4937053525057976,-0.902082785804484,1.834840651413005,0.4937053525057976,-0.977737015008655,1.7934331854380348,0.4937053525057976,-1.0517638974433945,1.749040726918328,0.4937053525057976,-1.1240402226685406,1.7017371627200357,0.4937053525057976,-1.194445693873647,1.6516012249574168,0.4937053525057976,-1.26286312810015,1.5987163599510033,0.4937053525057976,-1.3291786512808386,1.5431705893394354,0.4937053525057976,-1.3932818877720026,1.4850563635761298,0.4937053525057976,-1.455066144062797,1.4244704080546255,0.4937053525057976,-1.5144285863560631,1.3615135621187104,0.4937053525057976,-1.5712704117250331,1.2962906112252865,0.4937053525057976,-1.6254970125610528,1.2289101125393174,0.4937053525057976,-1.677018134038609,1.15948421425114,0.4937053525057976,-1.7257480243355783,1.0881284689168709,0.4937053525057976,-1.7716055773586743,1.0149616411325808,0.4937053525057976,-1.814514467736533,0.9401055098623438,0.4937053525057976,-1.8544032778557629,0.8636846657491687,0.4937053525057976,-1.8912056167285147,0.7858263037461735,0.4937053525057976,-1.9248602304937263,0.7066600114131354,0.4937053525057976,-1.9553111043681315,0.6263175532307867,0.4937053525057976,-1.9825075558773377,0.5449326512918423,0.4937053525057976,-2.006404319211803,0.46264076273377136,0.4937053525057976,-2.0269616205673087,0.379578854283761,0.4937053525057976,-2.044145244344531,0.29588517429111616,0.4937053525057976,-2.057926590097531,0.21169902262652432,0.4937053525057976,-2.068282720136372,0.12716051883117058,0.4937053525057976,-2.0751963977046417,0.042410368901587864,0.4937053525057976,-2.078656115668334,-0.042410368901587614,0.4937053525057976,-2.078656115668334,-0.12716051883117033,0.4937053525057976,-2.075196397704642,-0.21169902262652363,0.4937053525057976,-2.068282720136372,-0.29588517429111594,0.4937053525057976,-2.057926590097531,-0.37957885428376076,0.4937053525057976,-2.044145244344531,-0.4626407627337707,0.4937053525057976,-2.0269616205673087,-0.5449326512918421,0.4937053525057976,-2.006404319211803,-0.6263175532307861,0.4937053525057976,-1.982507555877338,-0.7066600114131352,0.4937053525057976,-1.9553111043681317,-0.7858263037461732,0.4937053525057976,-1.9248602304937266,-0.8636846657491681,0.4937053525057976,-1.891205616728515,-0.9401055098623439,0.4937053525057976,-1.8544032778557629,-1.0149616411325806,0.4937053525057976,-1.814514467736533,-1.0881284689168713,0.4937053525057976,-1.7716055773586739,-1.1594842142511392,0.4937053525057976,-1.7257480243355787,-1.228910112539317,0.4937053525057976,-1.6770181340386092,-1.2962906112252863,0.4937053525057976,-1.6254970125610533,-1.3615135621187107,0.4937053525057976,-1.5712704117250331,-1.4244704080546258,0.4937053525057976,-1.5144285863560631,-1.4850563635761298,0.4937053525057976,-1.455066144062797,-1.5431705893394354,0.4937053525057976,-1.3932818877720026,-1.5987163599510028,0.4937053525057976,-1.3291786512808392,-1.6516012249574168,0.4937053525057976,-1.2628631281001497,-1.7017371627200353,0.4937053525057976,-1.1944456938736476,-1.7490407269183281,0.4937053525057976,-1.1240402226685404,-1.7934331854380345,0.4937053525057976,-1.0517638974433947,-1.8348406514130047,0.4937053525057976,-0.9777370150086553,-1.8731942062025912,0.4937053525057976,-0.9020827858044844,-1.9084300140999269,0.4937053525057976,-0.8249271288291393,-1.9404894285801595,0.4937053525057976,-0.7463984620592284,-1.9693190899118032,0.4937053525057976,-0.6666274887106616,-1.9948710139687462,0.4937053525057976,-0.5857469796960473,-2.017102672095095,0.4937053525057976,-0.503891552640608,-2.035977061889921,0.4937053525057976,-0.4211974478244372,-2.051462768794109,0.4937053525057976,-0.33780230142398643,-2.0635340183767887,0.4937053525057976,-0.2538449164302427,-2.072170719234331,0.4937053525057976,-0.16946503162484394,-2.0773584964305076,0.4937053525057976,-0.08480308899866817,-2.0790887154221522,0.4937053525057976,-2.546149340485121e-16,-2.0773584964305076,0.4937053525057976,0.08480308899866768,-2.072170719234331,0.4937053525057976,0.16946503162484342,-2.0635340183767887,0.4937053525057976,0.25384491643024226,-2.0514627687941096,0.4937053525057976,0.33780230142398504,-2.0359770618899216,0.4937053525057976,0.4211974478244358,-2.017102672095095,0.4937053525057976,0.5038915526406084,-1.9948710139687464,0.4937053525057976,0.5857469796960468,-1.9693190899118032,0.4937053525057976,0.6666274887106611,-1.9404894285801597,0.4937053525057976,0.746398462059228,-1.908430014099927,0.4937053525057976,0.8249271288291389,-1.873194206202592,0.4937053525057976,0.9020827858044831,-1.834840651413005,0.4937053525057976,0.9777370150086547,-1.7934331854380348,0.4937053525057976,1.0517638974433943,-1.749040726918328,0.4937053525057976,1.1240402226685406,-1.701737162720036,0.4937053525057976,1.1944456938736465,-1.6516012249574172,0.4937053525057976,1.2628631281001492,-1.598716359951003,0.4937053525057976,1.3291786512808388,-1.5431705893394352,0.4937053525057976,1.3932818877720028,-1.4850563635761302,0.4937053525057976,1.4550661440627968,-1.424470408054626,0.4937053525057976,1.514428586356063,-1.3615135621187116,0.4937053525057976,1.5712704117250322,-1.2962906112252865,0.4937053525057976,1.6254970125610528,-1.2289101125393165,0.4937053525057976,1.6770181340386094,-1.15948421425114,0.4937053525057976,1.7257480243355783,-1.0881284689168718,0.4937053525057976,1.7716055773586736,-1.014961641132581,0.4937053525057976,1.8145144677365328,-0.9401055098623428,0.4937053525057976,1.8544032778557635,-0.8636846657491686,0.4937053525057976,1.891205616728515,-0.7858263037461741,0.4937053525057976,1.9248602304937261,-0.7066600114131348,0.4937053525057976,1.9553111043681317,-0.626317553230787,0.4937053525057976,1.9825075558773377,-0.5449326512918434,0.4937053525057976,2.0064043192118026,-0.4626407627337717,0.4937053525057976,2.0269616205673087,-0.3795788542837603,0.4937053525057976,2.044145244344531,-0.29588517429111594,0.4937053525057976,2.057926590097531,-0.21169902262652504,0.4937053525057976,2.068282720136372,-0.12716051883117035,0.4937053525057976,2.075196397704642,-0.04241036890158674,0.4937053525057976,2.078656115668334,0.04241036890158782,0.4937053525057976,2.078656115668334,0.1271605188311696,0.4937053525057976,2.075196397704642,0.2116990226265243,0.4937053525057976,2.068282720136372,0.29588517429111516,0.4937053525057976,2.057926590097531,0.3795788542837596,0.4937053525057976,2.044145244344531,0.4626407627337709,0.4937053525057976,2.0269616205673087,0.5449326512918428,0.4937053525057976,2.006404319211803,0.6263175532307863,0.4937053525057976,1.982507555877338,0.7066600114131342,0.4937053525057976,1.955311104368132,0.7858263037461717,0.4937053525057976,1.9248602304937272,0.8636846657491696,0.4937053525057976,1.8912056167285145,0.9401055098623436,0.4937053525057976,1.8544032778557629,1.0149616411325804,0.4937053525057976,1.8145144677365332,1.0881284689168698,0.4937053525057976,1.771605577358675,1.159484214251139,0.4937053525057976,1.7257480243355787,1.2289101125393174,0.4937053525057976,1.6770181340386088,1.296290611225286,0.4937053525057976,1.6254970125610533,1.361513562118711,0.4937053525057976,1.571270411725033,1.4244704080546253,0.4937053525057976,1.5144285863560634,1.4850563635761291,0.4937053525057976,1.455066144062798,1.5431705893394352,0.4937053525057976,1.3932818877720028,1.5987163599510037,0.4937053525057976,1.329178651280838,1.6516012249574166,0.4937053525057976,1.26286312810015,1.701737162720035,0.4937053525057976,1.1944456938736479,1.749040726918327,0.4937053525057976,1.124040222668542,1.7934331854380345,0.4937053525057976,1.051763897443395,1.8348406514130051,0.4937053525057976,0.9777370150086546,1.8731942062025912,0.4937053525057976,0.9020827858044846,1.9084300140999264,0.4937053525057976,0.8249271288291405,1.9404894285801595,0.4937053525057976,0.7463984620592288,1.9693190899118025,0.4937053525057976,0.6666274887106627,1.9948710139687464,0.4937053525057976,0.5857469796960467,2.0171026720950946,0.4937053525057976,0.5038915526406091,2.035977061889921,0.4937053525057976,0.4211974478244366,2.051462768794109,0.4937053525057976,0.33780230142398665,2.0635340183767887,0.4937053525057976,0.2538449164302439,2.072170719234331,0.4937053525057976,0.16946503162484416,2.0773584964305076,0.4937053525057976,0.08480308899866752,2.0790887154221522,0.4937053525057976,0.0,2.044066245781801,0.4980543805476445,0.0,2.0423651725123455,0.4980543805476445,-0.08337457198165311,2.037264783972558,0.4980543805476445,-0.16661037521640973,2.02877356925581,0.4980543805476445,-0.24956887192425753,2.0169056611510072,0.4980543805476445,-0.3321119858745312,2.0016808126199552,0.4980543805476445,-0.41410233220017156,1.9831243639205072,0.4980543805476445,-0.4954034460612784,1.9612672004302023,0.4980543805476445,-0.575880009777369,1.9361457012406054,0.4980543805476445,-0.655398078050303,1.9078016786078973,0.4980543805476445,-0.7338253009030117,1.8762823083605025,0.4980543805476445,-0.8110311439629743,1.8416400513795774,0.4980543805476445,-0.8868871057237973,1.8039325662830512,0.4980543805476445,-0.9612669314232918,1.763222613458547,0.4980543805476445,-1.0340468231820639,1.7195779506049098,0.4980543805476445,-1.1051056460528674,1.6730712199562041,0.4980543805476445,-1.1743251296377701,1.623779827375884,0.4980543805476445,-1.2415900649375586,1.5717858135223735,0.4980543805476445,-1.3067884961057459,1.5171757173004856,0.4980543805476445,-1.3698119067880317,1.4600404318259568,0.4980543805476445,-1.430555400737063,1.400475053142826,0.4980543805476445,-1.488917876401887,1.3385787219454535,0.4980543805476445,-1.5448021952015027,1.2744544585686206,0.4980543805476445,-1.5981153432024455,1.2082089915203473,0.4980543805476445,-1.648768585931298,1.1399525798428256,0.4980543805476445,-1.6966776160644639,1.0697988295971246,0.4980543805476445,-1.7417626937493895,0.9978645047771131,0.4980543805476445,-1.783948779323676,0.9242693329673162,0.4980543805476445,-1.8231656582111946,0.8491358060681641,0.4980543805476445,-1.859348057787319,0.7725889764203159,0.4980543805476445,-1.892435756018765,0.6947562486673781,0.4980543805476445,-1.9223736816972237,0.6157671677034475,0.4980543805476445,-1.9491120060999523,0.5357532030584227,0.4980543805476445,-1.9726062259247654,0.4548475300799438,0.4980543805476445,-1.9928172373613897,0.37318480827617007,0.4980543805476445,-2.0097114011758963,0.29090095718831854,0.4980543805476445,-2.023260598699887,0.20813293016599935,0.4980543805476445,-2.033442278631243,0.12501848642188415,0.4980543805476445,-2.0402394945685387,0.0416959617450898,0.4980543805476445,-2.0436409332166594,-0.04169596174508955,0.4980543805476445,-2.0436409332166594,-0.12501848642188387,0.4980543805476445,-2.040239494568539,-0.20813293016599868,0.4980543805476445,-2.033442278631243,-0.2909009571883183,0.4980543805476445,-2.023260598699887,-0.37318480827616984,0.4980543805476445,-2.0097114011758963,-0.4548475300799431,0.4980543805476445,-1.9928172373613897,-0.5357532030584224,0.4980543805476445,-1.9726062259247656,-0.6157671677034469,0.4980543805476445,-1.9491120060999525,-0.6947562486673778,0.4980543805476445,-1.922373681697224,-0.7725889764203157,0.4980543805476445,-1.8924357560187652,-0.8491358060681635,0.4980543805476445,-1.8593480577873192,-0.9242693329673163,0.4980543805476445,-1.8231656582111946,-0.997864504777113,0.4980543805476445,-1.783948779323676,-1.069798829597125,0.4980543805476445,-1.741762693749389,-1.1399525798428252,0.4980543805476445,-1.6966776160644643,-1.2082089915203469,0.4980543805476445,-1.6487685859312982,-1.2744544585686204,0.4980543805476445,-1.598115343202446,-1.3385787219454537,0.4980543805476445,-1.5448021952015027,-1.4004750531428263,0.4980543805476445,-1.488917876401887,-1.4600404318259568,0.4980543805476445,-1.430555400737063,-1.5171757173004856,0.4980543805476445,-1.3698119067880317,-1.571785813522373,0.4980543805476445,-1.3067884961057465,-1.623779827375884,0.4980543805476445,-1.2415900649375584,-1.6730712199562037,0.4980543805476445,-1.1743251296377708,-1.71957795060491,0.4980543805476445,-1.105105646052867,-1.7632226134585467,0.4980543805476445,-1.034046823182064,-1.803932566283051,0.4980543805476445,-0.961266931423292,-1.8416400513795772,0.4980543805476445,-0.8868871057237976,-1.8762823083605022,0.4980543805476445,-0.8110311439629744,-1.9078016786078973,0.4980543805476445,-0.7338253009030118,-1.9361457012406054,0.4980543805476445,-0.655398078050303,-1.961267200430202,0.4980543805476445,-0.5758800097773699,-1.9831243639205072,0.4980543805476445,-0.49540344606127823,-2.0016808126199552,0.4980543805476445,-0.41410233220017223,-2.0169056611510072,0.4980543805476445,-0.3321119858745318,-2.02877356925581,0.4980543805476445,-0.24956887192425803,-2.037264783972558,0.4980543805476445,-0.16661037521641014,-2.0423651725123455,0.4980543805476445,-0.08337457198165345,-2.044066245781801,0.4980543805476445,-2.5032591851418296e-16,-2.0423651725123455,0.4980543805476445,0.08337457198165295,-2.037264783972558,0.4980543805476445,0.16661037521640965,-2.02877356925581,0.4980543805476445,0.24956887192425756,-2.0169056611510072,0.4980543805476445,0.33211198587453045,-2.0016808126199557,0.4980543805476445,0.41410233220017084,-1.9831243639205072,0.4980543805476445,0.4954034460612786,-1.9612672004302023,0.4980543805476445,0.5758800097773694,-1.9361457012406054,0.4980543805476445,0.6553980780503026,-1.9078016786078975,0.4980543805476445,0.7338253009030113,-1.8762823083605025,0.4980543805476445,0.8110311439629739,-1.8416400513795779,0.4980543805476445,0.8868871057237964,-1.8039325662830512,0.4980543805476445,0.9612669314232916,-1.763222613458547,0.4980543805476445,1.0340468231820636,-1.7195779506049098,0.4980543805476445,1.1051056460528674,-1.6730712199562043,0.4980543805476445,1.1743251296377697,-1.6237798273758846,0.4980543805476445,1.241590064937558,-1.5717858135223732,0.4980543805476445,1.306788496105746,-1.5171757173004854,0.4980543805476445,1.3698119067880319,-1.4600404318259572,0.4980543805476445,1.4305554007370629,-1.4004750531428265,0.4980543805476445,1.4889178764018869,-1.3385787219454546,0.4980543805476445,1.5448021952015019,-1.2744544585686206,0.4980543805476445,1.5981153432024455,-1.2082089915203464,0.4980543805476445,1.6487685859312984,-1.1399525798428256,0.4980543805476445,1.6966776160644639,-1.0697988295971255,0.4980543805476445,1.7417626937493889,-0.9978645047771133,0.4980543805476445,1.7839487793236757,-0.9242693329673152,0.4980543805476445,1.8231656582111953,-0.849135806068164,0.4980543805476445,1.8593480577873192,-0.7725889764203165,0.4980543805476445,1.8924357560187648,-0.6947562486673775,0.4980543805476445,1.922373681697224,-0.6157671677034477,0.4980543805476445,1.9491120060999523,-0.5357532030584238,0.4980543805476445,1.9726062259247652,-0.45484753007994405,0.4980543805476445,1.9928172373613897,-0.37318480827616946,0.4980543805476445,2.0097114011758963,-0.2909009571883183,0.4980543805476445,2.023260598699887,-0.20813293016600007,0.4980543805476445,2.033442278631243,-0.12501848642188393,0.4980543805476445,2.040239494568539,-0.04169596174508869,0.4980543805476445,2.0436409332166594,0.04169596174508976,0.4980543805476445,2.0436409332166594,0.12501848642188318,0.4980543805476445,2.040239494568539,0.20813293016599932,0.4980543805476445,2.033442278631243,0.2909009571883176,0.4980543805476445,2.0232605986998875,0.37318480827616873,0.4980543805476445,2.0097114011758968,0.45484753007994333,0.4980543805476445,1.9928172373613897,0.5357532030584231,0.4980543805476445,1.9726062259247654,0.6157671677034471,0.4980543805476445,1.9491120060999525,0.6947562486673768,0.4980543805476445,1.9223736816972241,0.7725889764203142,0.4980543805476445,1.8924357560187657,0.8491358060681649,0.4980543805476445,1.8593480577873187,0.9242693329673161,0.4980543805476445,1.8231656582111946,0.9978645047771127,0.4980543805476445,1.7839487793236761,1.0697988295971235,0.4980543805476445,1.7417626937493902,1.139952579842825,0.4980543805476445,1.6966776160644643,1.2082089915203473,0.4980543805476445,1.6487685859312977,1.2744544585686202,0.4980543805476445,1.598115343202446,1.3385787219454541,0.4980543805476445,1.5448021952015025,1.4004750531428258,0.4980543805476445,1.4889178764018873,1.460040431825956,0.4980543805476445,1.4305554007370642,1.5171757173004854,0.4980543805476445,1.3698119067880319,1.571785813522374,0.4980543805476445,1.3067884961057452,1.6237798273758839,0.4980543805476445,1.2415900649375586,1.6730712199562034,0.4980543805476445,1.174325129637771,1.7195779506049087,0.4980543805476445,1.1051056460528688,1.7632226134585467,0.4980543805476445,1.0340468231820643,1.8039325662830514,0.4980543805476445,0.9612669314232914,1.8416400513795772,0.4980543805476445,0.8868871057237979,1.8762823083605018,0.4980543805476445,0.8110311439629755,1.9078016786078973,0.4980543805476445,0.7338253009030121,1.936145701240605,0.4980543805476445,0.6553980780503041,1.9612672004302023,0.4980543805476445,0.5758800097773692,1.983124363920507,0.4980543805476445,0.49540344606127934,2.0016808126199552,0.4980543805476445,0.41410233220017156,2.0169056611510072,0.4980543805476445,0.332111985874532,2.02877356925581,0.4980543805476445,0.2495688719242592,2.037264783972558,0.4980543805476445,0.16661037521641037,2.0423651725123455,0.4980543805476445,0.0833745719816528,2.044066245781801,0.4980543805476445,0.0,2.0088242403215646,0.4999221267184993,0.0,2.0071524954721514,0.4999221267184993,-0.0819371004089554,2.0021400433782297,0.4999221267184993,-0.16373782460058417,1.9937952267716357,0.4999221267184993,-0.24526602334231454,1.982131934776068,0.4999221267184993,-0.3263860009932909,1.9671695797900126,0.4999221267184993,-0.4069627413563761,1.9489330651767214,0.4999221267184993,-0.486862132399287,1.9274527438150217,0.4999221267184993,-0.565951189470837,1.9027643675799455,0.4999221267184993,-0.6440982766407635,1.8749090278372602,0.4999221267184993,-0.721173325794743,1.8439330870509478,0.4999221267184993,-0.7970480531199312,1.8098881016174573,0.4999221267184993,-0.8715961726207085,1.7728307360551712,0.4999221267184993,-0.944693606309258,1.7328226686919075,0.4999221267184993,-1.0162186907211295,1.68993048900743,0.4999221267184993,-1.0860523794120704,1.6442255868018296,0.4999221267184993,-1.1540784410990832,1.595784033374249,0.4999221267184993,-1.2201836531159254,1.544686454909712,0.4999221267184993,-1.2842579898610627,1.4910178982847953,0.4999221267184993,-1.346194805924426,1.434867689515496,0.4999221267184993,-1.4058910135881704,1.3763292850828934,0.4999221267184993,-1.4632472544060087,1.3155001163840594,0.4999221267184993,-1.5181680645755387,1.2524814275671134,0.4999221267184993,-1.5705620338283224,1.1873781070203289,0.4999221267184993,-1.620341957573256,1.1202985127957639,0.4999221267184993,-1.6674249820400049,1.0513542922579755,0.4999221267184993,-1.711732742180929,0.9806601962580019,0.4999221267184993,-1.753191492101969,0.9083338881418942,0.4999221267184993,-1.7917322278054106,0.8344957479116873,0.4999221267184993,-1.8272908020402292,0.7592686718647708,0.4999221267184993,-1.8598080310688567,0.6827778680451275,0.4999221267184993,-1.8892297931726731,0.6051506478469009,0.4999221267184993,-1.9155071187322659,0.5265162141171457,0.4999221267184993,-1.9385962717325282,0.4470054461104384,0.4999221267184993,-1.9584588225569408,0.3667506816532749,0.4999221267184993,-1.9750617119498746,0.28588549688081877,0.4999221267184993,-1.9883773060404604,0.20454448391260519,0.4999221267184993,-1.9983834423364395,0.12286302683724362,0.4999221267184993,-2.005063466611444,0.04097707637896105,0.4999221267184993,-2.008406260624315,-0.04097707637896081,0.4999221267184993,-2.008406260624315,-0.12286302683724337,0.4999221267184993,-2.0050634666114444,-0.20454448391260452,0.4999221267184993,-1.9983834423364395,-0.28588549688081855,0.4999221267184993,-1.9883773060404604,-0.3667506816532747,0.4999221267184993,-1.9750617119498746,-0.44700544611043774,0.4999221267184993,-1.9584588225569408,-0.5265162141171454,0.4999221267184993,-1.9385962717325285,-0.6051506478469002,0.4999221267184993,-1.915507118732266,-0.6827778680451273,0.4999221267184993,-1.8892297931726734,-0.7592686718647705,0.4999221267184993,-1.859808031068857,-0.8344957479116868,0.4999221267184993,-1.8272908020402294,-0.9083338881418943,0.4999221267184993,-1.7917322278054106,-0.9806601962580018,0.4999221267184993,-1.753191492101969,-1.051354292257976,0.4999221267184993,-1.7117327421809285,-1.1202985127957634,0.4999221267184993,-1.6674249820400053,-1.1873781070203284,0.4999221267184993,-1.6203419575732563,-1.2524814275671132,0.4999221267184993,-1.5705620338283228,-1.3155001163840596,0.4999221267184993,-1.5181680645755387,-1.3763292850828936,0.4999221267184993,-1.4632472544060087,-1.434867689515496,0.4999221267184993,-1.4058910135881704,-1.4910178982847953,0.4999221267184993,-1.346194805924426,-1.5446864549097115,0.4999221267184993,-1.2842579898610633,-1.595784033374249,0.4999221267184993,-1.2201836531159251,-1.6442255868018292,0.4999221267184993,-1.1540784410990839,-1.6899304890074303,0.4999221267184993,-1.0860523794120702,-1.7328226686919073,0.4999221267184993,-1.0162186907211297,-1.772830736055171,0.4999221267184993,-0.9446936063092582,-1.809888101617457,0.4999221267184993,-0.8715961726207089,-1.8439330870509476,0.4999221267184993,-0.7970480531199313,-1.8749090278372602,0.4999221267184993,-0.7211733257947431,-1.9027643675799455,0.4999221267184993,-0.6440982766407635,-1.9274527438150215,0.4999221267184993,-0.5659511894708378,-1.9489330651767214,0.4999221267184993,-0.48686213239928683,-1.9671695797900124,0.4999221267184993,-0.4069627413563767,-1.982131934776068,0.4999221267184993,-0.32638600099329146,-1.9937952267716357,0.4999221267184993,-0.24526602334231504,-2.0021400433782297,0.4999221267184993,-0.16373782460058459,-2.0071524954721514,0.4999221267184993,-0.08193710040895572,-2.0088242403215646,0.4999221267184993,-2.4601001759594173e-16,-2.0071524954721514,0.4999221267184993,0.08193710040895523,-2.0021400433782297,0.4999221267184993,0.1637378246005841,-1.9937952267716357,0.4999221267184993,0.24526602334231457,-1.9821319347760682,0.4999221267184993,0.3263860009932901,-1.9671695797900128,0.4999221267184993,0.4069627413563754,-1.9489330651767214,0.4999221267184993,0.4868621323992872,-1.9274527438150217,0.4999221267184993,0.5659511894708373,-1.9027643675799455,0.4999221267184993,0.644098276640763,-1.8749090278372604,0.4999221267184993,0.7211733257947427,-1.8439330870509478,0.4999221267184993,0.7970480531199309,-1.8098881016174577,0.4999221267184993,0.8715961726207077,-1.7728307360551712,0.4999221267184993,0.9446936063092578,-1.7328226686919075,0.4999221267184993,1.0162186907211292,-1.68993048900743,0.4999221267184993,1.0860523794120704,-1.6442255868018298,0.4999221267184993,1.1540784410990828,-1.5957840333742495,0.4999221267184993,1.2201836531159247,-1.5446864549097117,0.4999221267184993,1.2842579898610629,-1.491017898284795,0.4999221267184993,1.3461948059244262,-1.4348676895154964,0.4999221267184993,1.4058910135881701,-1.3763292850828939,0.4999221267184993,1.4632472544060084,-1.3155001163840605,0.4999221267184993,1.5181680645755378,-1.2524814275671134,0.4999221267184993,1.5705620338283224,-1.187378107020328,0.4999221267184993,1.6203419575732565,-1.1202985127957639,0.4999221267184993,1.6674249820400049,-1.0513542922579764,0.4999221267184993,1.7117327421809283,-0.9806601962580022,0.4999221267184993,1.7531914921019687,-0.9083338881418932,0.4999221267184993,1.7917322278054113,-0.8344957479116872,0.4999221267184993,1.8272908020402294,-0.7592686718647714,0.4999221267184993,1.8598080310688565,-0.682777868045127,0.4999221267184993,1.8892297931726734,-0.6051506478469011,0.4999221267184993,1.9155071187322659,-0.5265162141171468,0.4999221267184993,1.938596271732528,-0.4470054461104387,0.4999221267184993,1.9584588225569408,-0.3667506816532743,0.4999221267184993,1.9750617119498746,-0.28588549688081855,0.4999221267184993,1.9883773060404604,-0.20454448391260588,0.4999221267184993,1.9983834423364393,-0.12286302683724341,0.4999221267184993,2.0050634666114444,-0.04097707637895996,0.4999221267184993,2.008406260624315,0.04097707637896101,0.4999221267184993,2.008406260624315,0.12286302683724268,0.4999221267184993,2.0050634666114444,0.20454448391260516,0.4999221267184993,1.9983834423364395,0.28588549688081777,0.4999221267184993,1.9883773060404606,0.36675068165327357,0.4999221267184993,1.9750617119498748,0.44700544611043797,0.4999221267184993,1.9584588225569408,0.5265162141171461,0.4999221267184993,1.9385962717325282,0.6051506478469004,0.4999221267184993,1.915507118732266,0.6827778680451263,0.4999221267184993,1.8892297931726736,0.7592686718647691,0.4999221267184993,1.8598080310688574,0.8344957479116881,0.4999221267184993,1.827290802040229,0.9083338881418941,0.4999221267184993,1.7917322278054106,0.9806601962580016,0.4999221267184993,1.7531914921019691,1.0513542922579744,0.4999221267184993,1.7117327421809296,1.1202985127957632,0.4999221267184993,1.6674249820400053,1.1873781070203289,0.4999221267184993,1.6203419575732558,1.252481427567113,0.4999221267184993,1.5705620338283228,1.31550011638406,0.4999221267184993,1.5181680645755384,1.3763292850828932,0.4999221267184993,1.463247254406009,1.4348676895154953,0.4999221267184993,1.4058910135881713,1.491017898284795,0.4999221267184993,1.3461948059244262,1.5446864549097123,0.4999221267184993,1.284257989861062,1.5957840333742488,0.4999221267184993,1.2201836531159254,1.644225586801829,0.4999221267184993,1.154078441099084,1.6899304890074291,0.4999221267184993,1.0860523794120718,1.7328226686919073,0.4999221267184993,1.01621869072113,1.7728307360551714,0.4999221267184993,0.9446936063092577,1.809888101617457,0.4999221267184993,0.8715961726207091,1.8439330870509472,0.4999221267184993,0.7970480531199324,1.8749090278372602,0.4999221267184993,0.7211733257947435,1.902764367579945,0.4999221267184993,0.6440982766407646,1.9274527438150217,0.4999221267184993,0.5659511894708372,1.9489330651767212,0.4999221267184993,0.48686213239928794,1.9671695797900126,0.4999221267184993,0.4069627413563761,1.982131934776068,0.4999221267184993,0.3263860009932917,1.9937952267716357,0.4999221267184993,0.2452660233423162,2.0021400433782297,0.4999221267184993,0.1637378246005848,2.0071524954721514,0.4999221267184993,0.08193710040895508,2.0088242403215646,0.4999221267184993,0.0,1.9735382729399185,0.4992992860008892,0.0,1.9718958930956871,0.4992992860008892,-0.08049783569164282,1.9669714871421584,0.4992992860008892,-0.16086169068004114,1.958773251267057,0.4992992860008892,-0.24095780725960858,1.9473148306249195,0.4992992860008892,-0.3206528733489174,1.932615296626083,0.4992992860008892,-0.39981424437550156,1.9146991151942037,0.4992992860008892,-0.4783101640496568,1.8935961060451418,0.4992992860008892,-0.5560099836597825,1.869341393054988,0.4992992860008892,-0.632784379524268,1.8419753457998367,0.4992992860008892,-0.7085055682379977,1.8115435123646118,0.4992992860008892,-0.783047519355219,1.7780965435327738,0.4992992860008892,-0.8562861651547797,1.74169010848309,0.4992992860008892,-0.9280996071386077,1.70238480213378,0.4992992860008892,-0.9983683189197295,1.6602460442882543,0.4992992860008892,-1.0669753451621458,1.615343970750308,0.4992992860008892,-1.1338064962414462,1.5677533165899964,0.4992992860008892,-1.1987505383021704,1.5175532917544876,0.4992992860008892,-1.2616993783955808,1.464827449230922,0.4992992860008892,-1.3225482443897112,1.4096635459807103,0.4992992860008892,-1.3811958593522375,1.352153396876733,0.4992992860008892,-1.4375446101159395,1.2923927218865452,0.4992992860008892,-1.4915007097461814,1.230480986755937,0.4992992860008892,-1.5429743536400116,1.1665212374580156,0.4992992860008892,-1.59187986899706,1.100619928683353,0.4992992860008892,-1.6381358574134586,1.032886746656659,0.4992992860008892,-1.6816653303614544,0.9634344265748865,0.4992992860008892,-1.7223958373292114,0.8923785649706206,0.4992992860008892,-1.7602595864075423,0.8198374273130568,0.4992992860008892,-1.7951935571228481,0.745931751166802,0.4992992860008892,-1.8271396053284774,0.6707845452361128,0.4992992860008892,-1.8560445599799207,0.5945208846290448,0.4992992860008892,-1.8818603116327668,0.517267702682282,0.4992992860008892,-1.9045438925161242,0.43915357969312246,0.4992992860008892,-1.9240575480482367,0.3603085289102642,0.4992992860008892,-1.940368799675257,0.28086378013958335,0.4992992860008892,-1.9534504989285957,0.20095156132507186,0.4992992860008892,-1.9632808726108681,0.12070487846847677,0.4992992860008892,-1.969843559035231,0.04025729425393463,0.4992992860008892,-1.9731276352577976,-0.040257294253934386,0.4992992860008892,-1.9731276352577976,-0.12070487846847652,0.4992992860008892,-1.9698435590352312,-0.2009515613250712,0.4992992860008892,-1.9632808726108681,-0.28086378013958313,0.4992992860008892,-1.9534504989285957,-0.360308528910264,0.4992992860008892,-1.940368799675257,-0.4391535796931218,0.4992992860008892,-1.9240575480482367,-0.5172677026822817,0.4992992860008892,-1.9045438925161244,-0.5945208846290442,0.4992992860008892,-1.881860311632767,-0.6707845452361126,0.4992992860008892,-1.856044559979921,-0.7459317511668018,0.4992992860008892,-1.8271396053284776,-0.8198374273130562,0.4992992860008892,-1.7951935571228483,-0.8923785649706207,0.4992992860008892,-1.7602595864075423,-0.9634344265748864,0.4992992860008892,-1.7223958373292114,-1.0328867466566594,0.4992992860008892,-1.681665330361454,-1.1006199286833525,0.4992992860008892,-1.638135857413459,-1.1665212374580152,0.4992992860008892,-1.5918798689970601,-1.2304809867559368,0.4992992860008892,-1.5429743536400118,-1.2923927218865454,0.4992992860008892,-1.4915007097461814,-1.3521533968767332,0.4992992860008892,-1.4375446101159395,-1.4096635459807103,0.4992992860008892,-1.3811958593522375,-1.464827449230922,0.4992992860008892,-1.3225482443897112,-1.5175532917544872,0.4992992860008892,-1.2616993783955814,-1.5677533165899964,0.4992992860008892,-1.1987505383021702,-1.6153439707503074,0.4992992860008892,-1.133806496241447,-1.6602460442882545,0.4992992860008892,-1.0669753451621455,-1.7023848021337797,0.4992992860008892,-0.9983683189197298,-1.7416901084830898,0.4992992860008892,-0.928099607138608,-1.7780965435327736,0.4992992860008892,-0.8562861651547801,-1.8115435123646115,0.4992992860008892,-0.7830475193552191,-1.8419753457998367,0.4992992860008892,-0.7085055682379978,-1.869341393054988,0.4992992860008892,-0.632784379524268,-1.8935961060451416,0.4992992860008892,-0.5560099836597833,-1.9146991151942037,0.4992992860008892,-0.47831016404965665,-1.9326152966260828,0.4992992860008892,-0.39981424437550217,-1.9473148306249195,0.4992992860008892,-0.32065287334891796,-1.958773251267057,0.4992992860008892,-0.24095780725960908,-1.9669714871421584,0.4992992860008892,-0.16086169068004155,-1.9718958930956871,0.4992992860008892,-0.08049783569164314,-1.9735382729399185,0.4992992860008892,-2.416887328950667e-16,-1.9718958930956871,0.4992992860008892,0.08049783569164265,-1.9669714871421584,0.4992992860008892,0.16086169068004105,-1.958773251267057,0.4992992860008892,0.2409578072596086,-1.9473148306249197,0.4992992860008892,0.3206528733489166,-1.9326152966260832,0.4992992860008892,0.39981424437550084,-1.9146991151942037,0.4992992860008892,0.47831016404965704,-1.8935961060451418,0.4992992860008892,0.5560099836597828,-1.869341393054988,0.4992992860008892,0.6327843795242676,-1.841975345799837,0.4992992860008892,0.7085055682379974,-1.8115435123646118,0.4992992860008892,0.7830475193552187,-1.7780965435327742,0.4992992860008892,0.8562861651547788,-1.74169010848309,0.4992992860008892,0.9280996071386075,-1.70238480213378,0.4992992860008892,0.9983683189197293,-1.6602460442882543,0.4992992860008892,1.0669753451621458,-1.6153439707503081,0.4992992860008892,1.1338064962414458,-1.5677533165899968,0.4992992860008892,1.1987505383021697,-1.5175532917544874,0.4992992860008892,1.261699378395581,-1.4648274492309217,0.4992992860008892,1.3225482443897114,-1.4096635459807108,0.4992992860008892,1.3811958593522373,-1.3521533968767334,0.4992992860008892,1.4375446101159393,-1.2923927218865463,0.4992992860008892,1.4915007097461805,-1.230480986755937,0.4992992860008892,1.5429743536400116,-1.1665212374580147,0.4992992860008892,1.5918798689970604,-1.100619928683353,0.4992992860008892,1.6381358574134586,-1.0328867466566598,0.4992992860008892,1.6816653303614537,-0.9634344265748868,0.4992992860008892,1.7223958373292112,-0.8923785649706197,0.4992992860008892,1.760259586407543,-0.8198374273130566,0.4992992860008892,1.7951935571228483,-0.7459317511668027,0.4992992860008892,1.8271396053284772,-0.6707845452361123,0.4992992860008892,1.856044559979921,-0.5945208846290451,0.4992992860008892,1.8818603116327668,-0.5172677026822831,0.4992992860008892,1.904543892516124,-0.43915357969312274,0.4992992860008892,1.9240575480482367,-0.3603085289102636,0.4992992860008892,1.940368799675257,-0.28086378013958313,0.4992992860008892,1.9534504989285957,-0.20095156132507255,0.4992992860008892,1.963280872610868,-0.12070487846847656,0.4992992860008892,1.9698435590352312,-0.04025729425393356,0.4992992860008892,1.9731276352577976,0.04025729425393459,0.4992992860008892,1.9731276352577976,0.12070487846847584,0.4992992860008892,1.9698435590352312,0.20095156132507183,0.4992992860008892,1.9632808726108681,0.2808637801395824,0.4992992860008892,1.953450498928596,0.3603085289102629,0.4992992860008892,1.9403687996752572,0.439153579693122,0.4992992860008892,1.9240575480482367,0.5172677026822824,0.4992992860008892,1.9045438925161242,0.5945208846290444,0.4992992860008892,1.881860311632767,0.6707845452361116,0.4992992860008892,1.8560445599799211,0.7459317511668003,0.4992992860008892,1.827139605328478,0.8198374273130575,0.4992992860008892,1.795193557122848,0.8923785649706205,0.4992992860008892,1.7602595864075423,0.9634344265748862,0.4992992860008892,1.7223958373292116,1.0328867466566578,0.4992992860008892,1.681665330361455,1.1006199286833522,0.4992992860008892,1.638135857413459,1.1665212374580156,0.4992992860008892,1.5918798689970597,1.2304809867559365,0.4992992860008892,1.5429743536400118,1.2923927218865459,0.4992992860008892,1.4915007097461812,1.3521533968767327,0.4992992860008892,1.4375446101159397,1.4096635459807096,0.4992992860008892,1.3811958593522384,1.4648274492309217,0.4992992860008892,1.3225482443897114,1.517553291754488,0.4992992860008892,1.26169937839558,1.5677533165899962,0.4992992860008892,1.1987505383021704,1.6153439707503072,0.4992992860008892,1.1338064962414471,1.6602460442882534,0.4992992860008892,1.066975345162147,1.7023848021337797,0.4992992860008892,0.99836831891973,1.7416901084830902,0.4992992860008892,0.9280996071386074,1.7780965435327736,0.4992992860008892,0.8562861651547802,1.811543512364611,0.4992992860008892,0.7830475193552202,1.8419753457998367,0.4992992860008892,0.7085055682379982,1.8693413930549876,0.4992992860008892,0.6327843795242691,1.8935961060451418,0.4992992860008892,0.5560099836597827,1.9146991151942034,0.4992992860008892,0.47831016404965776,1.932615296626083,0.4992992860008892,0.39981424437550156,1.9473148306249195,0.4992992860008892,0.3206528733489182,1.958773251267057,0.4992992860008892,0.2409578072596102,1.9669714871421584,0.4992992860008892,0.16086169068004177,1.9718958930956871,0.4992992860008892,0.08049783569164251,1.9735382729399185,0.4992992860008892,0.0,1.9383841365514207,0.4961889613559395,0.0,1.9367710120025325,0.4961889613559395,-0.07906394817413986,1.9319343232424813,0.4961889613559395,-0.15799630220929894,1.9238821204623797,0.4961889613559395,-0.2366656869919539,1.9126278057590902,0.4961889613559395,-0.31494116509495,1.898190110828757,0.4961889613559395,-0.39269245470992636,1.880593065789748,0.4961889613559395,-0.4697901464885288,1.8598659591868927,0.4961889613559395,-0.5461059189314987,1.8360432892435907,0.4961889613559395,-0.6215127519671447,1.809164706442923,0.4961889613559395,-0.6958851383637163,1.779274947533337,0.4961889613559395,-0.7690992926238055,1.7464237610687419,0.4961889613559395,-0.8410333570130897,1.7106658246069517,0.4961889613559395,-0.9115676043805061,1.6720606537042864,0.4961889613559395,-0.9805846374322776,1.6306725028578015,0.4961889613559395,-1.0479695841281227,1.5865702585600185,0.4961889613559395,-1.1136102888744306,1.5398273246441576,0.4961889613559395,-1.1773974991961775,1.4905215001107006,0.4961889613559395,-1.2392250475768891,1.4387348496386327,0.4961889613559395,-1.2989900281639954,1.3845535669968834,0.4961889613559395,-1.3565929680454647,1.328067831583304,0.4961889613559395,-1.4119379928126508,1.2693716583299608,0.4961889613559395,-1.4649329861337828,1.2085627412245576,0.4961889613559395,-1.515489743072509,1.1457422907084347,0.4961889613559395,-1.5635241168963099,1.0810148652217784,0.4961889613559395,-1.6089561591304273,1.0144881971764153,0.4961889613559395,-1.6517102526242138,0.9462730136458459,0.4961889613559395,-1.6917152374084137,0.876482852070958,0.4961889613559395,-1.7289045291339125,0.8052338712881588,0.4961889613559395,-1.763216229894814,0.7326446581944597,0.4961889613559395,-1.7945932312513941,0.6588360303712862,0.4961889613559395,-1.8229833092814651,0.5839308349955377,0.4961889613559395,-1.8483392115019395,0.508053744372586,0.4961889613559395,-1.8706187355159232,0.4313310484315263,0.4961889613559395,-1.8897847992544434,0.3538904445280537,0.4961889613559395,-1.90580550269589,0.2758608249048173,0.4961889613559395,-1.918654180960454,0.19737206216300104,0.4961889613559395,-1.92830944869119,0.11855479310219867,0.4961889613559395,-1.9347552356478304,0.03954020128835129,0.4961889613559395,-1.937980813454115,-0.039540201288351055,0.4961889613559395,-1.937980813454115,-0.11855479310219842,0.4961889613559395,-1.9347552356478306,-0.1973720621630004,0.4961889613559395,-1.92830944869119,-0.27586082490481706,0.4961889613559395,-1.918654180960454,-0.35389044452805346,0.4961889613559395,-1.90580550269589,-0.43133104843152564,0.4961889613559395,-1.8897847992544434,-0.5080537443725858,0.4961889613559395,-1.8706187355159234,-0.583930834995537,0.4961889613559395,-1.8483392115019397,-0.658836030371286,0.4961889613559395,-1.8229833092814653,-0.7326446581944595,0.4961889613559395,-1.7945932312513944,-0.8052338712881583,0.4961889613559395,-1.7632162298948142,-0.8764828520709581,0.4961889613559395,-1.7289045291339125,-0.9462730136458458,0.4961889613559395,-1.6917152374084137,-1.0144881971764157,0.4961889613559395,-1.6517102526242133,-1.081014865221778,0.4961889613559395,-1.6089561591304278,-1.1457422907084345,0.4961889613559395,-1.56352411689631,-1.2085627412245574,0.4961889613559395,-1.5154897430725094,-1.269371658329961,0.4961889613559395,-1.4649329861337828,-1.3280678315833043,0.4961889613559395,-1.4119379928126508,-1.3845535669968834,0.4961889613559395,-1.3565929680454647,-1.4387348496386327,0.4961889613559395,-1.2989900281639954,-1.4905215001107002,0.4961889613559395,-1.2392250475768898,-1.5398273246441576,0.4961889613559395,-1.1773974991961773,-1.5865702585600183,0.4961889613559395,-1.1136102888744313,-1.6306725028578017,0.4961889613559395,-1.0479695841281225,-1.6720606537042861,0.4961889613559395,-0.9805846374322779,-1.7106658246069515,0.4961889613559395,-0.9115676043805063,-1.7464237610687416,0.4961889613559395,-0.84103335701309,-1.7792749475333367,0.4961889613559395,-0.7690992926238056,-1.809164706442923,0.4961889613559395,-0.6958851383637165,-1.8360432892435907,0.4961889613559395,-0.6215127519671447,-1.8598659591868925,0.4961889613559395,-0.5461059189314995,-1.880593065789748,0.4961889613559395,-0.4697901464885286,-1.8981901108287569,0.4961889613559395,-0.392692454709927,-1.9126278057590902,0.4961889613559395,-0.3149411650949505,-1.9238821204623797,0.4961889613559395,-0.2366656869919544,-1.9319343232424813,0.4961889613559395,-0.15799630220929936,-1.9367710120025325,0.4961889613559395,-0.07906394817414017,-1.9383841365514207,0.4961889613559395,-2.373835928345703e-16,-1.9367710120025325,0.4961889613559395,0.0790639481741397,-1.9319343232424813,0.4961889613559395,0.15799630220929886,-1.9238821204623797,0.4961889613559395,0.23666568699195392,-1.9126278057590904,0.4961889613559395,0.31494116509494924,-1.8981901108287573,0.4961889613559395,0.3926924547099257,-1.880593065789748,0.4961889613559395,0.469790146488529,-1.8598659591868927,0.4961889613559395,0.5461059189314991,-1.8360432892435907,0.4961889613559395,0.6215127519671443,-1.8091647064429233,0.4961889613559395,0.695885138363716,-1.779274947533337,0.4961889613559395,0.7690992926238052,-1.7464237610687423,0.4961889613559395,0.8410333570130888,-1.7106658246069517,0.4961889613559395,0.911567604380506,-1.6720606537042864,0.4961889613559395,0.9805846374322774,-1.6306725028578015,0.4961889613559395,1.0479695841281227,-1.5865702585600188,0.4961889613559395,1.1136102888744301,-1.539827324644158,0.4961889613559395,1.1773974991961769,-1.4905215001107004,0.4961889613559395,1.2392250475768893,-1.4387348496386325,0.4961889613559395,1.2989900281639957,-1.3845535669968838,0.4961889613559395,1.3565929680454645,-1.3280678315833045,0.4961889613559395,1.4119379928126505,-1.269371658329962,0.4961889613559395,1.4649329861337819,-1.2085627412245576,0.4961889613559395,1.515489743072509,-1.145742290708434,0.4961889613559395,1.5635241168963103,-1.0810148652217784,0.4961889613559395,1.6089561591304273,-1.0144881971764161,0.4961889613559395,1.6517102526242131,-0.9462730136458462,0.4961889613559395,1.6917152374084135,-0.876482852070957,0.4961889613559395,1.7289045291339131,-0.8052338712881587,0.4961889613559395,1.7632162298948142,-0.7326446581944602,0.4961889613559395,1.794593231251394,-0.6588360303712857,0.4961889613559395,1.8229833092814653,-0.5839308349955379,0.4961889613559395,1.8483392115019395,-0.508053744372587,0.4961889613559395,1.870618735515923,-0.4313310484315266,0.4961889613559395,1.8897847992544434,-0.35389044452805307,0.4961889613559395,1.90580550269589,-0.27586082490481706,0.4961889613559395,1.918654180960454,-0.1973720621630017,0.4961889613559395,1.9283094486911898,-0.11855479310219846,0.4961889613559395,1.9347552356478306,-0.039540201288350244,0.4961889613559395,1.937980813454115,0.03954020128835125,0.4961889613559395,1.937980813454115,0.11855479310219776,0.4961889613559395,1.9347552356478306,0.19737206216300102,0.4961889613559395,1.92830944869119,0.2758608249048164,0.4961889613559395,1.9186541809604543,0.3538904445280524,0.4961889613559395,1.9058055026958902,0.43133104843152587,0.4961889613559395,1.8897847992544434,0.5080537443725863,0.4961889613559395,1.8706187355159232,0.5839308349955372,0.4961889613559395,1.8483392115019397,0.658836030371285,0.4961889613559395,1.8229833092814656,0.732644658194458,0.4961889613559395,1.7945932312513948,0.8052338712881596,0.4961889613559395,1.7632162298948137,0.8764828520709579,0.4961889613559395,1.7289045291339125,0.9462730136458456,0.4961889613559395,1.691715237408414,1.0144881971764141,0.4961889613559395,1.6517102526242144,1.0810148652217777,0.4961889613559395,1.6089561591304278,1.1457422907084347,0.4961889613559395,1.5635241168963097,1.2085627412245572,0.4961889613559395,1.5154897430725094,1.2693716583299615,0.4961889613559395,1.4649329861337825,1.3280678315833039,0.4961889613559395,1.411937992812651,1.3845535669968827,0.4961889613559395,1.3565929680454656,1.4387348496386325,0.4961889613559395,1.2989900281639957,1.490521500110701,0.4961889613559395,1.2392250475768887,1.5398273246441576,0.4961889613559395,1.1773974991961775,1.586570258560018,0.4961889613559395,1.1136102888744315,1.6306725028578006,0.4961889613559395,1.047969584128124,1.6720606537042861,0.4961889613559395,0.9805846374322781,1.710665824606952,0.4961889613559395,0.9115676043805059,1.7464237610687416,0.4961889613559395,0.8410333570130902,1.7792749475333363,0.4961889613559395,0.7690992926238066,1.809164706442923,0.4961889613559395,0.6958851383637168,1.8360432892435903,0.4961889613559395,0.6215127519671458,1.8598659591868927,0.4961889613559395,0.546105918931499,1.8805930657897478,0.4961889613559395,0.46979014648852974,1.898190110828757,0.4961889613559395,0.39269245470992636,1.9126278057590902,0.4961889613559395,0.31494116509495074,1.9238821204623797,0.4961889613559395,0.23666568699195548,1.9319343232424813,0.4961889613559395,0.15799630220929956,1.9367710120025325,0.4961889613559395,0.07906394817413956,1.9383841365514207,0.4961889613559395,0.0,1.9035369672952516,0.4906066482645773,0.0,1.9019528425834578,0.4906066482645773,-0.07764258141193196,1.897203105067323,0.4906066482645773,-0.15515593440958536,1.8892956602161954,0.4906066482645773,-0.2324110456666237,1.8782436691916506,0.4906066482645773,-0.3092793316746022,1.8640655269420372,0.4906066482645773,-0.38563285275752757,1.8467848315859003,0.4906066482645773,-0.46134452601482245,1.826430345135243,0.4906066482645773,-0.5362883368382722,1.8030359456239955,0.4906066482645773,-0.6103395486509031,1.77664057072137,0.4906066482645773,-0.6833749105187054,1.7472881529239543,0.4906066482645773,-0.7552728622896493,1.7150275464344071,0.4906066482645773,-0.8259137369185606,1.6799124458484576,0.4906066482645773,-0.8951799596411089,1.6420012967855508,0.4906066482645773,-0.9629562436653978,1.601357198611883,0.4906066482645773,-1.029129782055454,1.5580477994177317,0.4906066482645773,-1.0935904354872383,1.512145183423887,0.4906066482645773,-1.1562309155646824,1.4637257510045798,0.4906066482645773,-1.216946963390633,1.4128700915265988,0.4906066482645773,-1.2756375230955008,1.3596628492162424,0.4906066482645773,-1.332204910034779,1.3041925822773577,0.4906066482645773,-1.3865549733754956,1.2465516154949503,0.4906066482645773,-1.438597252800982,1.186835886569691,0.4906066482645773,-1.488245129073144,1.1251447864390798,0.4906066482645773,-1.5354159682016342,1.061580993851041,0.4906066482645773,-1.5800312589799734,0.9962503044652794,0.4906066482645773,-1.6220167436597077,0.929261454766847,0.4906066482645773,-1.6613025415450995,0.8607259410849935,0.4906066482645773,-1.697823265302651,0.7907578340185284,0.4906066482645773,-1.7315181297918683,0.7194735885765706,0.4906066482645773,-1.7623310532361265,0.6469918503506756,0.4906066482645773,-1.7902107505652547,0.5734332580409577,0.4906066482645773,-1.8151108187744724,0.4989202426648803,0.4906066482645773,-1.8369898141576126,0.4235768237829098,0.4906066482645773,-1.8558113212860798,0.34752840308019595,0.4906066482645773,-1.8715440136187373,0.2709015556478438,0.4906066482645773,-1.8841617056418438,0.19382381931116383,0.4906066482645773,-1.8936433964522563,0.1164234823555515,0.4906066482645773,-1.899973304711361,0.03882937000329469,0.4906066482645773,-1.903140894911554,-0.03882937000329446,0.4906066482645773,-1.903140894911554,-0.11642348235555126,0.4906066482645773,-1.8999733047113612,-0.1938238193111632,0.4906066482645773,-1.8936433964522563,-0.2709015556478436,0.4906066482645773,-1.8841617056418438,-0.3475284030801957,0.4906066482645773,-1.8715440136187373,-0.4235768237829091,0.4906066482645773,-1.8558113212860798,-0.49892024266488005,0.4906066482645773,-1.8369898141576129,-0.573433258040957,0.4906066482645773,-1.8151108187744727,-0.6469918503506754,0.4906066482645773,-1.790210750565255,-0.7194735885765704,0.4906066482645773,-1.7623310532361267,-0.7907578340185278,0.4906066482645773,-1.7315181297918685,-0.8607259410849936,0.4906066482645773,-1.697823265302651,-0.9292614547668469,0.4906066482645773,-1.6613025415450995,-0.9962503044652798,0.4906066482645773,-1.6220167436597073,-1.0615809938510405,0.4906066482645773,-1.5800312589799737,-1.1251447864390793,0.4906066482645773,-1.5354159682016342,-1.1868358865696909,0.4906066482645773,-1.4882451290731442,-1.2465516154949505,0.4906066482645773,-1.438597252800982,-1.304192582277358,0.4906066482645773,-1.3865549733754956,-1.3596628492162424,0.4906066482645773,-1.332204910034779,-1.4128700915265988,0.4906066482645773,-1.2756375230955008,-1.4637257510045794,0.4906066482645773,-1.2169469633906336,-1.512145183423887,0.4906066482645773,-1.1562309155646822,-1.5580477994177313,0.4906066482645773,-1.093590435487239,-1.6013571986118833,0.4906066482645773,-1.0291297820554537,-1.6420012967855506,0.4906066482645773,-0.962956243665398,-1.6799124458484573,0.4906066482645773,-0.8951799596411091,-1.715027546434407,0.4906066482645773,-0.825913736918561,-1.7472881529239541,0.4906066482645773,-0.7552728622896494,-1.77664057072137,0.4906066482645773,-0.6833749105187055,-1.8030359456239955,0.4906066482645773,-0.6103395486509031,-1.826430345135243,0.4906066482645773,-0.5362883368382728,-1.8467848315859003,0.4906066482645773,-0.4613445260148223,-1.864065526942037,0.4906066482645773,-0.3856328527575281,-1.8782436691916506,0.4906066482645773,-0.3092793316746027,-1.8892956602161954,0.4906066482645773,-0.23241104566662418,-1.897203105067323,0.4906066482645773,-0.15515593440958575,-1.9019528425834578,0.4906066482645773,-0.07764258141193227,-1.9035369672952516,0.4906066482645773,-2.3311604540567897e-16,-1.9019528425834578,0.4906066482645773,0.07764258141193181,-1.897203105067323,0.4906066482645773,0.15515593440958528,-1.8892956602161954,0.4906066482645773,0.23241104566662374,-1.8782436691916509,0.4906066482645773,0.3092793316746015,-1.8640655269420374,0.4906066482645773,0.38563285275752685,-1.8467848315859003,0.4906066482645773,0.4613445260148227,-1.826430345135243,0.4906066482645773,0.5362883368382725,-1.8030359456239955,0.4906066482645773,0.6103395486509027,-1.7766405707213702,0.4906066482645773,0.683374910518705,-1.7472881529239543,0.4906066482645773,0.755272862289649,-1.7150275464344076,0.4906066482645773,0.8259137369185597,-1.6799124458484576,0.4906066482645773,0.8951799596411086,-1.6420012967855508,0.4906066482645773,0.9629562436653976,-1.601357198611883,0.4906066482645773,1.029129782055454,-1.558047799417732,0.4906066482645773,1.0935904354872379,-1.5121451834238873,0.4906066482645773,1.1562309155646817,-1.4637257510045796,0.4906066482645773,1.2169469633906331,-1.4128700915265986,0.4906066482645773,1.275637523095501,-1.3596628492162428,0.4906066482645773,1.3322049100347788,-1.3041925822773581,0.4906066482645773,1.3865549733754954,-1.2465516154949514,0.4906066482645773,1.4385972528009812,-1.186835886569691,0.4906066482645773,1.488245129073144,-1.1251447864390791,0.4906066482645773,1.5354159682016344,-1.061580993851041,0.4906066482645773,1.5800312589799734,-0.9962503044652803,0.4906066482645773,1.622016743659707,-0.9292614547668472,0.4906066482645773,1.6613025415450993,-0.8607259410849925,0.4906066482645773,1.6978232653026517,-0.7907578340185283,0.4906066482645773,1.7315181297918685,-0.7194735885765712,0.4906066482645773,1.7623310532361263,-0.6469918503506752,0.4906066482645773,1.790210750565255,-0.5734332580409579,0.4906066482645773,1.8151108187744724,-0.49892024266488133,0.4906066482645773,1.8369898141576124,-0.42357682378291006,0.4906066482645773,1.8558113212860798,-0.3475284030801954,0.4906066482645773,1.8715440136187373,-0.2709015556478436,0.4906066482645773,1.8841617056418438,-0.1938238193111645,0.4906066482645773,1.893643396452256,-0.1164234823555513,0.4906066482645773,1.8999733047113612,-0.03882937000329366,0.4906066482645773,1.903140894911554,0.03882937000329465,0.4906066482645773,1.903140894911554,0.11642348235555061,0.4906066482645773,1.8999733047113612,0.1938238193111638,0.4906066482645773,1.8936433964522563,0.27090155564784285,0.4906066482645773,1.8841617056418438,0.34752840308019467,0.4906066482645773,1.8715440136187376,0.42357682378290934,0.4906066482645773,1.8558113212860798,0.49892024266488066,0.4906066482645773,1.8369898141576126,0.5734332580409572,0.4906066482645773,1.8151108187744727,0.6469918503506745,0.4906066482645773,1.7902107505652551,0.719473588576569,0.4906066482645773,1.7623310532361272,0.7907578340185292,0.4906066482645773,1.731518129791868,0.8607259410849933,0.4906066482645773,1.697823265302651,0.9292614547668466,0.4906066482645773,1.6613025415450997,0.9962503044652784,0.4906066482645773,1.6220167436597084,1.0615809938510403,0.4906066482645773,1.5800312589799737,1.1251447864390798,0.4906066482645773,1.535415968201634,1.1868358865696906,0.4906066482645773,1.4882451290731442,1.246551615494951,0.4906066482645773,1.4385972528009818,1.3041925822773575,0.4906066482645773,1.3865549733754958,1.3596628492162417,0.4906066482645773,1.3322049100347797,1.4128700915265986,0.4906066482645773,1.275637523095501,1.46372575100458,0.4906066482645773,1.2169469633906325,1.5121451834238868,0.4906066482645773,1.1562309155646824,1.558047799417731,0.4906066482645773,1.0935904354872392,1.6013571986118822,0.4906066482645773,1.0291297820554552,1.6420012967855506,0.4906066482645773,0.9629562436653982,1.6799124458484578,0.4906066482645773,0.8951799596411085,1.715027546434407,0.4906066482645773,0.8259137369185611,1.747288152923954,0.4906066482645773,0.7552728622896504,1.77664057072137,0.4906066482645773,0.6833749105187058,1.803035945623995,0.4906066482645773,0.6103395486509041,1.826430345135243,0.4906066482645773,0.5362883368382724,1.8467848315859001,0.4906066482645773,0.46134452601482334,1.8640655269420372,0.4906066482645773,0.38563285275752757,1.8782436691916506,0.4906066482645773,0.3092793316746029,1.8892956602161954,0.4906066482645773,0.23241104566662527,1.897203105067323,0.4906066482645773,0.15515593440958597,1.9019528425834578,0.4906066482645773,0.07764258141193166,1.9035369672952516,0.4906066482645773,0.0,1.8691703720157795,0.4825801575298248,0.0,1.8676148472071017,0.4825801575298248,-0.07624081658273155,1.8629508617985988,0.4825801575298248,-0.1523547378504037,1.8551861785336987,0.4825801575298248,-0.22821507969268584,1.8443337209614055,0.4825801575298248,-0.30369558005717534,1.8304115519263282,0.4825801575298248,-0.3786706091001231,1.8134428435048586,0.4825801575298248,-0.45301537828491023,1.7934558384375365,0.4825801575298248,-0.5266061480802519,1.7704838031217958,0.4825801575298248,-0.5993204339124354,1.7445649722433283,0.4825801575298248,-0.6710372100288052,1.7157424851382235,0.4825801575298248,-0.7416371109331846,1.6840643139918006,0.4825801575298248,-0.8110026300579635,1.6495831839936403,0.4825801575298248,-0.8790183153421839,1.6123564855817112,0.4825801575298248,-0.9455709613901015,1.5724461789216477,0.4825801575298248,-1.0105497978903943,1.529918690780171,0.4825801575298248,-1.0738466739824153,1.4848448039642894,0.4825801575298248,-1.1353562382626252,1.4372995395103045,0.4825801575298248,-1.1949761141316064,1.387362031818697,0.4825801575298248,-1.2526070701898118,1.335115396942729,0.4825801575298248,-1.3081531853984327,1.2806465942499745,0.4825801575298248,-1.3615220087305049,1.2240462816870348,0.4825801575298248,-1.412624713046516,1.1654086648883337,0.4825801575298248,-1.4613762429384154,1.1048313403801349,0.4825801575298248,-1.5076954562959466,1.0424151331407563,0.4825801575298248,-1.5515052593596796,0.9782639287873446,0.4825801575298248,-1.5927327350359686,0.9124845006685175,0.4825801575298248,-1.631309264260255,0.8451863321506624,0.4825801575298248,-1.66717064020673,0.776481434393676,0.4825801575298248,-1.7002571751542608,0.7064841599194462,0.4825801575298248,-1.7305137998307096,0.6353110122833635,0.4825801575298248,-1.7578901550703034,0.5630804521656484,0.4825801575298248,-1.7823406756314957,0.48991270020524363,0.4825801575298248,-1.8038246660358135,0.41592953690442297,0.4825801575298248,-1.8223063683014664,0.3412540999371639,0.4825801575298248,-1.8377550214589766,0.26601067919864224,0.4825801575298248,-1.8501449127497782,0.19032450993696604,0.4825801575298248,-1.859455420422566,0.1143215643114669,0.4825801575298248,-1.8656710480561642,0.03812834172447108,0.4825801575298248,-1.8687814503517926,-0.038128341724470854,0.4825801575298248,-1.8687814503517926,-0.11432156431146666,0.4825801575298248,-1.8656710480561645,-0.19032450993696542,0.4825801575298248,-1.859455420422566,-0.266010679198642,0.4825801575298248,-1.8501449127497782,-0.3412540999371637,0.4825801575298248,-1.8377550214589766,-0.41592953690442236,0.4825801575298248,-1.8223063683014664,-0.4899127002052434,0.4825801575298248,-1.8038246660358137,-0.5630804521656477,0.4825801575298248,-1.782340675631496,-0.6353110122833633,0.4825801575298248,-1.7578901550703037,-0.7064841599194461,0.4825801575298248,-1.7305137998307099,-0.7764814343936755,0.4825801575298248,-1.700257175154261,-0.8451863321506625,0.4825801575298248,-1.66717064020673,-0.9124845006685174,0.4825801575298248,-1.631309264260255,-0.978263928787345,0.4825801575298248,-1.5927327350359683,-1.0424151331407558,0.4825801575298248,-1.55150525935968,-1.1048313403801344,0.4825801575298248,-1.5076954562959468,-1.1654086648883335,0.4825801575298248,-1.4613762429384158,-1.224046281687035,0.4825801575298248,-1.412624713046516,-1.2806465942499747,0.4825801575298248,-1.3615220087305049,-1.335115396942729,0.4825801575298248,-1.3081531853984327,-1.387362031818697,0.4825801575298248,-1.2526070701898118,-1.437299539510304,0.4825801575298248,-1.194976114131607,-1.4848448039642894,0.4825801575298248,-1.135356238262625,-1.5299186907801705,0.4825801575298248,-1.073846673982416,-1.572446178921648,0.4825801575298248,-1.0105497978903941,-1.612356485581711,0.4825801575298248,-0.9455709613901018,-1.6495831839936401,0.4825801575298248,-0.8790183153421841,-1.6840643139918003,0.4825801575298248,-0.8110026300579637,-1.7157424851382233,0.4825801575298248,-0.7416371109331847,-1.7445649722433283,0.4825801575298248,-0.6710372100288052,-1.7704838031217958,0.4825801575298248,-0.5993204339124354,-1.7934558384375363,0.4825801575298248,-0.5266061480802525,-1.8134428435048586,0.4825801575298248,-0.45301537828491006,-1.830411551926328,0.4825801575298248,-0.37867060910012373,-1.8443337209614055,0.4825801575298248,-0.30369558005717584,-1.8551861785336987,0.4825801575298248,-0.2282150796926863,-1.8629508617985988,0.4825801575298248,-0.1523547378504041,-1.8676148472071017,0.4825801575298248,-0.07624081658273184,-1.8691703720157795,0.4825801575298248,-2.289073513150192e-16,-1.8676148472071017,0.4825801575298248,0.07624081658273138,-1.8629508617985988,0.4825801575298248,0.15235473785040363,-1.8551861785336987,0.4825801575298248,0.22821507969268587,-1.8443337209614055,0.4825801575298248,0.3036955800571746,-1.8304115519263284,0.4825801575298248,0.37867060910012246,-1.8134428435048586,0.4825801575298248,0.45301537828491045,-1.7934558384375365,0.4825801575298248,0.5266061480802522,-1.7704838031217958,0.4825801575298248,0.599320433912435,-1.7445649722433285,0.4825801575298248,0.6710372100288049,-1.7157424851382235,0.4825801575298248,0.7416371109331843,-1.684064313991801,0.4825801575298248,0.8110026300579626,-1.6495831839936403,0.4825801575298248,0.8790183153421837,-1.6123564855817112,0.4825801575298248,0.9455709613901013,-1.5724461789216477,0.4825801575298248,1.0105497978903943,-1.5299186907801712,0.4825801575298248,1.0738466739824148,-1.4848448039642899,0.4825801575298248,1.1353562382626246,-1.4372995395103043,0.4825801575298248,1.1949761141316066,-1.387362031818697,0.4825801575298248,1.252607070189812,-1.3351153969427294,0.4825801575298248,1.3081531853984327,-1.2806465942499747,0.4825801575298248,1.3615220087305047,-1.2240462816870359,0.4825801575298248,1.412624713046515,-1.1654086648883337,0.4825801575298248,1.4613762429384154,-1.104831340380134,0.4825801575298248,1.507695456295947,-1.0424151331407563,0.4825801575298248,1.5515052593596796,-0.9782639287873455,0.4825801575298248,1.5927327350359681,-0.9124845006685177,0.4825801575298248,1.6313092642602547,-0.8451863321506614,0.4825801575298248,1.6671706402067306,-0.7764814343936759,0.4825801575298248,1.700257175154261,-0.7064841599194469,0.4825801575298248,1.7305137998307094,-0.635311012283363,0.4825801575298248,1.7578901550703037,-0.5630804521656486,0.4825801575298248,1.7823406756314957,-0.4899127002052447,0.4825801575298248,1.8038246660358133,-0.41592953690442325,0.4825801575298248,1.8223063683014664,-0.3412540999371633,0.4825801575298248,1.8377550214589766,-0.266010679198642,0.4825801575298248,1.8501449127497782,-0.1903245099369667,0.4825801575298248,1.8594554204225657,-0.1143215643114667,0.4825801575298248,1.8656710480561645,-0.03812834172447007,0.4825801575298248,1.8687814503517926,0.03812834172447104,0.4825801575298248,1.8687814503517926,0.114321564311466,0.4825801575298248,1.8656710480561645,0.190324509936966,0.4825801575298248,1.859455420422566,0.26601067919864135,0.4825801575298248,1.8501449127497784,0.34125409993716266,0.4825801575298248,1.8377550214589768,0.4159295369044226,0.4825801575298248,1.8223063683014664,0.489912700205244,0.4825801575298248,1.8038246660358135,0.563080452165648,0.4825801575298248,1.782340675631496,0.6353110122833623,0.4825801575298248,1.7578901550703039,0.7064841599194447,0.4825801575298248,1.7305137998307103,0.7764814343936767,0.4825801575298248,1.7002571751542606,0.8451863321506623,0.4825801575298248,1.66717064020673,0.9124845006685172,0.4825801575298248,1.631309264260255,0.9782639287873436,0.4825801575298248,1.5927327350359692,1.0424151331407556,0.4825801575298248,1.55150525935968,1.1048313403801349,0.4825801575298248,1.5076954562959464,1.1654086648883333,0.4825801575298248,1.4613762429384158,1.2240462816870354,0.4825801575298248,1.4126247130465157,1.2806465942499743,0.4825801575298248,1.361522008730505,1.3351153969427283,0.4825801575298248,1.3081531853984336,1.387362031818697,0.4825801575298248,1.252607070189812,1.4372995395103048,0.4825801575298248,1.194976114131606,1.4848448039642892,0.4825801575298248,1.1353562382626252,1.5299186907801703,0.4825801575298248,1.0738466739824162,1.5724461789216468,0.4825801575298248,1.0105497978903957,1.612356485581711,0.4825801575298248,0.9455709613901019,1.6495831839936406,0.4825801575298248,0.8790183153421836,1.6840643139918003,0.4825801575298248,0.8110026300579639,1.7157424851382228,0.4825801575298248,0.7416371109331857,1.7445649722433283,0.4825801575298248,0.6710372100288056,1.7704838031217955,0.4825801575298248,0.5993204339124364,1.7934558384375365,0.4825801575298248,0.5266061480802521,1.8134428435048584,0.4825801575298248,0.4530153782849111,1.8304115519263282,0.4825801575298248,0.3786706091001231,1.8443337209614055,0.4825801575298248,0.30369558005717606,1.8551861785336987,0.4825801575298248,0.22821507969268737,1.8629508617985988,0.4825801575298248,0.1523547378504043,1.8676148472071017,0.4825801575298248,0.07624081658273124,1.8691703720157795,0.4825801575298248,0.0,1.8354555633619951,0.4721494767247779,0.0,1.8339280960391762,0.4721494767247779,-0.07486563720840676,1.8293482363893339,0.4721494767247779,-0.14960666795210742,1.821723607136873,0.4721494767247779,-0.224098693161559,1.8110668987247158,0.4721494767247779,-0.2982177282123563,1.7973958481923127,0.4721494767247779,-0.37184040928540296,1.7807332096540902,0.4721494767247779,-0.44484419869381375,1.7611067164274732,0.4721494767247779,-0.5171075888348016,1.7385490348735155,0.4721494767247779,-0.5885103044270905,1.7130977100269646,0.4721494767247779,-0.6589335026972514,1.6847951031062578,0.4721494767247779,-0.7282599711817705,1.6536883210074538,0.4721494767247779,-0.7963743228156265,1.6198291378994536,0.4721494767247779,-0.8631631879826734,1.583273909051006,0.4721494767247779,-0.9285154032081783,1.5440834770329244,0.4721494767247779,-0.9923221961794534,1.5023230704516317,0.4721494767247779,-1.0544773667866374,1.4580621953825825,0.4721494767247779,-1.1148774638822971,1.4113745196842609,0.4721494767247779,-1.1734219574656513,1.3623377503852987,0.4721494767247779,-1.230013406004838,1.3110335043487962,0.4721494767247779,-1.2845576186187266,1.2575471724291079,0.4721494767247779,-1.336963811848344,1.2019677773471926,0.4721494767247779,-1.3871447607569831,1.1443878255210806,0.4721494767247779,-1.4350169441075011,1.0849031530980684,0.4721494767247779,-1.4805006833751777,1.0236127664449113,0.4721494767247779,-1.5235202753647534,0.9606186773614964,0.4721494767247779,-1.5640041182109299,0.8960257332922693,0.4721494767247779,-1.6018848305526057,0.8299414428180099,0.4721494767247779,-1.6370993636825037,0.762475796718408,0.4721494767247779,-1.669589106485523,0.6937410849032687,0.4721494767247779,-1.6992999829911535,0.6238517095170372,0.4721494767247779,-1.7261825423775925,0.5529239945277189,0.4721494767247779,-1.7501920412777594,0.48107599211711694,0.4721494767247779,-1.7712885182502116,0.40842728619462443,0.4721494767247779,-1.7894368602910227,0.33509879336160914,0.4721494767247779,-1.8046068612759103,0.26121256165766416,0.4721494767247779,-1.8167732722353496,0.1868915674236894,0.4721494767247779,-1.825915843378992,0.1122595106199108,0.4721494767247779,-1.832019357799441,0.03744060893950278,0.4721494767247779,-1.8350736567992953,-0.03744060893950256,0.4721494767247779,-1.8350736567992953,-0.11225951061991056,0.4721494767247779,-1.8320193577994413,-0.1868915674236888,0.4721494767247779,-1.825915843378992,-0.26121256165766393,0.4721494767247779,-1.8167732722353496,-0.3350987933616089,0.4721494767247779,-1.8046068612759103,-0.4084272861946238,0.4721494767247779,-1.7894368602910227,-0.4810759921171167,0.4721494767247779,-1.7712885182502118,-0.5529239945277182,0.4721494767247779,-1.7501920412777596,-0.623851709517037,0.4721494767247779,-1.7261825423775927,-0.6937410849032685,0.4721494767247779,-1.6992999829911537,-0.7624757967184075,0.4721494767247779,-1.6695891064855233,-0.82994144281801,0.4721494767247779,-1.6370993636825037,-0.8960257332922692,0.4721494767247779,-1.6018848305526057,-0.9606186773614968,0.4721494767247779,-1.5640041182109294,-1.0236127664449108,0.4721494767247779,-1.5235202753647539,-1.084903153098068,0.4721494767247779,-1.480500683375178,-1.1443878255210804,0.4721494767247779,-1.4350169441075016,-1.2019677773471928,0.4721494767247779,-1.3871447607569831,-1.257547172429108,0.4721494767247779,-1.336963811848344,-1.3110335043487962,0.4721494767247779,-1.2845576186187266,-1.3623377503852987,0.4721494767247779,-1.230013406004838,-1.4113745196842604,0.4721494767247779,-1.173421957465652,-1.4580621953825825,0.4721494767247779,-1.114877463882297,-1.5023230704516313,0.4721494767247779,-1.054477366786638,-1.5440834770329246,0.4721494767247779,-0.9923221961794532,-1.5832739090510057,0.4721494767247779,-0.9285154032081785,-1.6198291378994534,0.4721494767247779,-0.8631631879826737,-1.6536883210074536,0.4721494767247779,-0.7963743228156268,-1.6847951031062576,0.4721494767247779,-0.7282599711817707,-1.7130977100269646,0.4721494767247779,-0.6589335026972515,-1.7385490348735155,0.4721494767247779,-0.5885103044270905,-1.761106716427473,0.4721494767247779,-0.5171075888348023,-1.7807332096540902,0.4721494767247779,-0.44484419869381364,-1.7973958481923125,0.4721494767247779,-0.3718404092854035,-1.8110668987247158,0.4721494767247779,-0.2982177282123568,-1.821723607136873,0.4721494767247779,-0.22409869316155948,-1.8293482363893339,0.4721494767247779,-0.1496066679521078,-1.8339280960391762,0.4721494767247779,-0.07486563720840705,-1.8354555633619951,0.4721494767247779,-2.2477847806484694e-16,-1.8339280960391762,0.4721494767247779,0.07486563720840661,-1.8293482363893339,0.4721494767247779,0.14960666795210734,-1.821723607136873,0.4721494767247779,0.22409869316155903,-1.8110668987247158,0.4721494767247779,0.2982177282123556,-1.797395848192313,0.4721494767247779,0.3718404092854023,-1.7807332096540902,0.4721494767247779,0.444844198693814,-1.7611067164274732,0.4721494767247779,0.5171075888348019,-1.7385490348735155,0.4721494767247779,0.5885103044270901,-1.7130977100269649,0.4721494767247779,0.658933502697251,-1.6847951031062578,0.4721494767247779,0.7282599711817702,-1.653688321007454,0.4721494767247779,0.7963743228156256,-1.6198291378994536,0.4721494767247779,0.8631631879826733,-1.583273909051006,0.4721494767247779,0.928515403208178,-1.5440834770329244,0.4721494767247779,0.9923221961794534,-1.502323070451632,0.4721494767247779,1.054477366786637,-1.458062195382583,0.4721494767247779,1.1148774638822965,-1.4113745196842606,0.4721494767247779,1.1734219574656515,-1.3623377503852985,0.4721494767247779,1.2300134060048382,-1.3110335043487966,0.4721494767247779,1.2845576186187264,-1.2575471724291083,0.4721494767247779,1.336963811848344,-1.2019677773471937,0.4721494767247779,1.3871447607569822,-1.1443878255210806,0.4721494767247779,1.4350169441075011,-1.0849031530980675,0.4721494767247779,1.4805006833751782,-1.0236127664449113,0.4721494767247779,1.5235202753647534,-0.9606186773614972,0.4721494767247779,1.5640041182109294,-0.8960257332922695,0.4721494767247779,1.6018848305526054,-0.829941442818009,0.4721494767247779,1.6370993636825044,-0.7624757967184079,0.4721494767247779,1.6695891064855233,-0.6937410849032692,0.4721494767247779,1.6992999829911533,-0.6238517095170366,0.4721494767247779,1.7261825423775927,-0.552923994527719,0.4721494767247779,1.7501920412777594,-0.48107599211711793,0.4721494767247779,1.7712885182502114,-0.4084272861946247,0.4721494767247779,1.7894368602910227,-0.3350987933616086,0.4721494767247779,1.8046068612759103,-0.26121256165766393,0.4721494767247779,1.8167732722353496,-0.18689156742369004,0.4721494767247779,1.8259158433789917,-0.1122595106199106,0.4721494767247779,1.8320193577994413,-0.03744060893950179,0.4721494767247779,1.8350736567992953,0.03744060893950275,0.4721494767247779,1.8350736567992953,0.11225951061990994,0.4721494767247779,1.8320193577994413,0.18689156742368937,0.4721494767247779,1.825915843378992,0.2612125616576633,0.4721494767247779,1.8167732722353498,0.3350987933616079,0.4721494767247779,1.8046068612759105,0.40842728619462404,0.4721494767247779,1.7894368602910227,0.4810759921171173,0.4721494767247779,1.7712885182502116,0.5529239945277185,0.4721494767247779,1.7501920412777596,0.623851709517036,0.4721494767247779,1.726182542377593,0.6937410849032671,0.4721494767247779,1.6992999829911541,0.7624757967184087,0.4721494767247779,1.669589106485523,0.8299414428180097,0.4721494767247779,1.6370993636825037,0.896025733292269,0.4721494767247779,1.6018848305526059,0.9606186773614954,0.4721494767247779,1.5640041182109305,1.0236127664449106,0.4721494767247779,1.5235202753647539,1.0849031530980684,0.4721494767247779,1.4805006833751777,1.1443878255210802,0.4721494767247779,1.4350169441075016,1.2019677773471933,0.4721494767247779,1.387144760756983,1.2575471724291076,0.4721494767247779,1.3369638118483442,1.3110335043487955,0.4721494767247779,1.2845576186187273,1.3623377503852985,0.4721494767247779,1.2300134060048382,1.4113745196842613,0.4721494767247779,1.1734219574656506,1.4580621953825823,0.4721494767247779,1.1148774638822971,1.502323070451631,0.4721494767247779,1.0544773667866383,1.5440834770329235,0.4721494767247779,0.9923221961794546,1.5832739090510057,0.4721494767247779,0.9285154032081787,1.6198291378994538,0.4721494767247779,0.8631631879826732,1.6536883210074536,0.4721494767247779,0.7963743228156269,1.6847951031062571,0.4721494767247779,0.7282599711817717,1.7130977100269646,0.4721494767247779,0.6589335026972518,1.738549034873515,0.4721494767247779,0.5885103044270915,1.7611067164274732,0.4721494767247779,0.5171075888348018,1.78073320965409,0.4721494767247779,0.44484419869381464,1.7973958481923127,0.4721494767247779,0.37184040928540296,1.8110668987247158,0.4721494767247779,0.29821772821235704,1.821723607136873,0.4721494767247779,0.2240986931615605,1.8293482363893339,0.4721494767247779,0.149606667952108,1.8339280960391762,0.4721494767247779,0.07486563720840647,1.8354555633619951,0.4721494767247779,0.0,1.8025605068147044,0.4593665709765281,0.0,1.8010604147795042,0.4593665709765281,-0.07352389436342688,1.7965626354290531,0.4593665709765281,-0.14692541546069762,1.7890746548731984,0.4593665709765281,-0.2200823937038807,1.7786089361165922,0.4593665709765281,-0.2928730665224915,1.7651828983152522,0.4593665709765281,-0.3651762810252743,1.748818887784094,0.4593665709765281,-0.43687169564723294,1.7295441408036962,0.4593665709765281,-0.50783998044629,1.7073907382881992,0.4593665709765281,-0.5779630157161982,1.6823955523897889,0.4593665709765281,-0.6471240885851346,1.6546001851286394,0.4593665709765281,-0.7152080872727576,1.624050899150455,0.4593665709765281,-0.7821016926824045,1.5907985407268632,0.4593665709765281,-0.8476935670095451,1.5548984551268124,0.4593665709765281,-0.9118745390525694,1.5164103944998346,0.4593665709765281,-0.974537785917478,1.4753984184244884,0.4593665709765281,-1.035579010814048,1.4319307872875136,0.4593665709765281,-1.0948966166475471,1.3860798486711536,0.4593665709765281,-1.1523918751170696,1.3379219169377445,0.4593665709765281,-1.2079690910390526,1.2875371462119902,0.4593665709765281,-1.261535761622463,1.2350093969723328,0.4593665709765281,-1.313002730430565,1.180426096473462,0.4593665709765281,-1.3622843357730088,1.1238780932322785,0.4593665709765281,-1.4092985532812632,1.0654595058195009,0.4593665709765281,-1.4539671324300798,1.0052675662085964,0.4593665709765281,-1.4962157267777696,0.9434024579427548,0.4593665709765281,-1.53597401770852,0.8799671493892692,0.4593665709765281,-1.5731758314707887,0.8150672223588497,0.4593665709765281,-1.6077592493169843,0.7488106963751174,0.4593665709765281,-1.6396667105611114,0.6813078488867718,0.4593665709765281,-1.668845108382849,0.6126710317216578,0.4593665709765281,-1.695245878218615,0.5430144840882357,0.4593665709765281,-1.7188250785924877,0.4724541424356951,0.4593665709765281,-1.7395434642524559,0.40110744748917554,0.4593665709765281,-1.7573665514902697,0.32909314878126955,0.4593665709765281,-1.7722646755361664,0.2565311070051459,0.4593665709765281,-1.7842130399329554,0.18354209451825268,0.4593665709765281,-1.7931917578072727,0.11024759432865006,0.4593665709765281,-1.7991858849693183,0.03676959789853047,0.4593665709765281,-1.8021854447859866,-0.03676959789853026,0.4593665709765281,-1.8021854447859866,-0.11024759432864983,0.4593665709765281,-1.7991858849693185,-0.1835420945182521,0.4593665709765281,-1.7931917578072727,-0.2565311070051457,0.4593665709765281,-1.7842130399329554,-0.3290931487812694,0.4593665709765281,-1.7722646755361664,-0.40110744748917493,0.4593665709765281,-1.7573665514902697,-0.4724541424356949,0.4593665709765281,-1.739543464252456,-0.543014484088235,0.4593665709765281,-1.7188250785924877,-0.6126710317216576,0.4593665709765281,-1.695245878218615,-0.6813078488867716,0.4593665709765281,-1.6688451083828493,-0.7488106963751169,0.4593665709765281,-1.6396667105611116,-0.8150672223588498,0.4593665709765281,-1.6077592493169843,-0.8799671493892691,0.4593665709765281,-1.5731758314707887,-0.9434024579427552,0.4593665709765281,-1.5359740177085197,-1.005267566208596,0.4593665709765281,-1.49621572677777,-1.0654595058195004,0.4593665709765281,-1.45396713243008,-1.1238780932322783,0.4593665709765281,-1.4092985532812636,-1.1804260964734623,0.4593665709765281,-1.3622843357730088,-1.235009396972333,0.4593665709765281,-1.313002730430565,-1.2875371462119902,0.4593665709765281,-1.261535761622463,-1.3379219169377445,0.4593665709765281,-1.2079690910390526,-1.3860798486711532,0.4593665709765281,-1.15239187511707,-1.4319307872875136,0.4593665709765281,-1.094896616647547,-1.475398418424488,0.4593665709765281,-1.0355790108140486,-1.5164103944998348,0.4593665709765281,-0.9745377859174778,-1.5548984551268124,0.4593665709765281,-0.9118745390525697,-1.590798540726863,0.4593665709765281,-0.8476935670095453,-1.624050899150455,0.4593665709765281,-0.7821016926824047,-1.6546001851286392,0.4593665709765281,-0.7152080872727578,-1.6823955523897889,0.4593665709765281,-0.6471240885851347,-1.7073907382881992,0.4593665709765281,-0.5779630157161982,-1.729544140803696,0.4593665709765281,-0.5078399804462906,-1.748818887784094,0.4593665709765281,-0.43687169564723277,-1.765182898315252,0.4593665709765281,-0.36517628102527483,-1.7786089361165922,0.4593665709765281,-0.292873066522492,-1.7890746548731984,0.4593665709765281,-0.22008239370388114,-1.7965626354290531,0.4593665709765281,-0.14692541546069798,-1.8010604147795042,0.4593665709765281,-0.07352389436342716,-1.8025605068147044,0.4593665709765281,-2.2074999549400584e-16,-1.8010604147795042,0.4593665709765281,0.07352389436342673,-1.7965626354290531,0.4593665709765281,0.14692541546069754,-1.7890746548731984,0.4593665709765281,0.22008239370388072,-1.7786089361165924,0.4593665709765281,0.29287306652249084,-1.7651828983152524,0.4593665709765281,0.3651762810252736,-1.748818887784094,0.4593665709765281,0.43687169564723316,-1.7295441408036962,0.4593665709765281,0.5078399804462903,-1.7073907382881992,0.4593665709765281,0.5779630157161978,-1.6823955523897889,0.4593665709765281,0.6471240885851343,-1.6546001851286394,0.4593665709765281,0.7152080872727573,-1.6240508991504554,0.4593665709765281,0.7821016926824036,-1.5907985407268632,0.4593665709765281,0.847693567009545,-1.5548984551268124,0.4593665709765281,0.9118745390525692,-1.5164103944998346,0.4593665709765281,0.974537785917478,-1.4753984184244886,0.4593665709765281,1.0355790108140477,-1.431930787287514,0.4593665709765281,1.0948966166475465,-1.3860798486711534,0.4593665709765281,1.1523918751170699,-1.3379219169377443,0.4593665709765281,1.2079690910390528,-1.2875371462119907,0.4593665709765281,1.261535761622463,-1.2350093969723333,0.4593665709765281,1.3130027304305647,-1.1804260964734632,0.4593665709765281,1.3622843357730081,-1.1238780932322785,0.4593665709765281,1.4092985532812632,-1.0654595058195,0.4593665709765281,1.4539671324300802,-1.0052675662085964,0.4593665709765281,1.4962157267777696,-0.9434024579427556,0.4593665709765281,1.5359740177085195,-0.8799671493892695,0.4593665709765281,1.5731758314707884,-0.8150672223588488,0.4593665709765281,1.607759249316985,-0.7488106963751173,0.4593665709765281,1.6396667105611116,-0.6813078488867724,0.4593665709765281,1.6688451083828488,-0.6126710317216573,0.4593665709765281,1.695245878218615,-0.5430144840882358,0.4593665709765281,1.7188250785924877,-0.4724541424356961,0.4593665709765281,1.7395434642524559,-0.40110744748917576,0.4593665709765281,1.7573665514902697,-0.329093148781269,0.4593665709765281,1.7722646755361664,-0.2565311070051457,0.4593665709765281,1.7842130399329554,-0.18354209451825332,0.4593665709765281,1.7931917578072727,-0.11024759432864988,0.4593665709765281,1.7991858849693185,-0.0367695978985295,0.4593665709765281,1.8021854447859866,0.03676959789853044,0.4593665709765281,1.8021854447859866,0.11024759432864921,0.4593665709765281,1.7991858849693185,0.18354209451825265,0.4593665709765281,1.7931917578072727,0.25653110700514503,0.4593665709765281,1.7842130399329557,0.3290931487812684,0.4593665709765281,1.7722646755361666,0.40110744748917515,0.4593665709765281,1.7573665514902697,0.4724541424356955,0.4593665709765281,1.7395434642524559,0.5430144840882353,0.4593665709765281,1.7188250785924877,0.6126710317216567,0.4593665709765281,1.6952458782186153,0.6813078488867703,0.4593665709765281,1.6688451083828497,0.7488106963751181,0.4593665709765281,1.6396667105611111,0.8150672223588495,0.4593665709765281,1.6077592493169843,0.8799671493892689,0.4593665709765281,1.5731758314707889,0.9434024579427538,0.4593665709765281,1.5359740177085206,1.0052675662085957,0.4593665709765281,1.49621572677777,1.0654595058195009,0.4593665709765281,1.4539671324300796,1.123878093232278,0.4593665709765281,1.4092985532812636,1.1804260964734627,0.4593665709765281,1.3622843357730088,1.2350093969723326,0.4593665709765281,1.3130027304305651,1.2875371462119896,0.4593665709765281,1.2615357616224638,1.3379219169377443,0.4593665709765281,1.2079690910390528,1.386079848671154,0.4593665709765281,1.152391875117069,1.4319307872875136,0.4593665709765281,1.0948966166475471,1.475398418424488,0.4593665709765281,1.0355790108140488,1.5164103944998337,0.4593665709765281,0.9745377859174792,1.5548984551268124,0.4593665709765281,0.9118745390525698,1.5907985407268634,0.4593665709765281,0.8476935670095449,1.624050899150455,0.4593665709765281,0.7821016926824049,1.6546001851286387,0.4593665709765281,0.7152080872727588,1.6823955523897889,0.4593665709765281,0.647124088585135,1.7073907382881988,0.4593665709765281,0.5779630157161992,1.7295441408036962,0.4593665709765281,0.5078399804462902,1.7488188877840938,0.4593665709765281,0.43687169564723377,1.7651828983152522,0.4593665709765281,0.3651762810252743,1.7786089361165922,0.4593665709765281,0.29287306652249223,1.7890746548731984,0.4593665709765281,0.22008239370388216,1.7965626354290531,0.4593665709765281,0.14692541546069818,1.8010604147795042,0.4593665709765281,0.07352389436342659,1.8025605068147044,0.4593665709765281,0.0,1.7706490838909548,0.4442951240785135,0.0,1.769175548562827,0.4442951240785135,-0.07222227254315386,1.7647573951325752,0.4442951240785135,-0.14432433824121546,1.7574019771805605,0.4442951240785135,-0.2161861903215231,1.7471215370740638,0.4442951240785135,-0.2876882218232753,1.7339331855910742,0.4442951240785135,-0.35871142467151335,1.7178588734410878,0.4442951240785135,-0.4291375877543168,1.6989253547303178,0.4442951240785135,-0.49884949367353376,1.6771641424321253,0.4442951240785135,-0.5677311138415716,1.6526114559367844,0.4442951240785135,-0.63566780159953,1.6253081607678825,0.4442951240785135,-0.7025464830352511,1.595299700565689,0.4442951240785135,-0.7682558451836862,1.5626360214507007,0.4442951240785135,-0.8326865212963417,1.5273714888932528,0.4442951240785135,-0.8957312728714375,1.4895647972275603,0.4442951240785135,-0.9572851681418107,1.4492788719607896,0.4442951240785135,-1.0172457567234865,1.406580765039763,0.4442951240785135,-1.0755132401342316,1.3615415432496099,0.4442951240785135,-1.1319906378982771,1.3142361699301168,0.4442951240785135,-1.1865839489607528,1.2647433802066463,0.4442951240785135,-1.2392023081431633,1.2131455499432908,0.4442951240785135,-1.2897581373795117,1.159528558636377,0.4442951240785135,-1.3381672914813465,1.1039816464765195,0.4442951240785135,-1.3843491981891238,1.0465972658171294,0.4442951240785135,-1.4282269922767832,0.9874709272965975,0.4442951240785135,-1.469727643486328,0.9267010408702622,0.4442951240785135,-1.5087820780794854,0.864388752016751,0.4442951240785135,-1.5453252938041238,0.8006377733913134,0.4442951240785135,-1.5792964680840889,0.7355542122063393,0.4442951240785135,-1.6106390592523792,0.6692463936263794,0.4442951240785135,-1.639300900659169,0.601824680471597,0.4442951240785135,-1.6652342874980497,0.533401289529745,0.4442951240785135,-1.6883960562059728,0.46409010478239887,0.4442951240785135,-1.7087476563047392,0.3940064878563078,0.4442951240785135,-1.7262552145644672,0.32326708601535153,0.4442951240785135,-1.7408895913822395,0.2519896380126821,0.4442951240785135,-1.7526264292820974,0.18029277812618727,0.4442951240785135,-1.7614461934556556,0.10829583870344531,0.4442951240785135,-1.767334204275862,0.036118651544807416,0.4442951240785135,-1.7702806617297897,-0.0361186515448072,0.4442951240785135,-1.7702806617297897,-0.10829583870344509,0.4442951240785135,-1.7673342042758622,-0.18029277812618666,0.4442951240785135,-1.7614461934556556,-0.25198963801268187,0.4442951240785135,-1.7526264292820974,-0.3232670860153513,0.4442951240785135,-1.7408895913822395,-0.39400648785630726,0.4442951240785135,-1.7262552145644672,-0.4640901047823987,0.4442951240785135,-1.7087476563047395,-0.5334012895297444,0.4442951240785135,-1.688396056205973,-0.6018246804715968,0.4442951240785135,-1.66523428749805,-0.6692463936263792,0.4442951240785135,-1.6393009006591692,-0.7355542122063388,0.4442951240785135,-1.6106390592523794,-0.8006377733913135,0.4442951240785135,-1.5792964680840889,-0.8643887520167509,0.4442951240785135,-1.5453252938041238,-0.9267010408702625,0.4442951240785135,-1.508782078079485,-0.987470927296597,0.4442951240785135,-1.4697276434863284,-1.046597265817129,0.4442951240785135,-1.4282269922767834,-1.1039816464765193,0.4442951240785135,-1.3843491981891243,-1.1595285586363773,0.4442951240785135,-1.3381672914813465,-1.213145549943291,0.4442951240785135,-1.2897581373795117,-1.2647433802066463,0.4442951240785135,-1.2392023081431633,-1.3142361699301168,0.4442951240785135,-1.1865839489607528,-1.3615415432496094,0.4442951240785135,-1.1319906378982776,-1.406580765039763,0.4442951240785135,-1.0755132401342313,-1.4492788719607892,0.4442951240785135,-1.0172457567234872,-1.4895647972275605,0.4442951240785135,-0.9572851681418105,-1.5273714888932526,0.4442951240785135,-0.8957312728714377,-1.5626360214507005,0.4442951240785135,-0.8326865212963419,-1.5952997005656888,0.4442951240785135,-0.7682558451836865,-1.6253081607678823,0.4442951240785135,-0.7025464830352511,-1.6526114559367844,0.4442951240785135,-0.6356678015995301,-1.6771641424321253,0.4442951240785135,-0.5677311138415716,-1.6989253547303176,0.4442951240785135,-0.4988494936735345,-1.7178588734410878,0.4442951240785135,-0.42913758775431665,-1.733933185591074,0.4442951240785135,-0.3587114246715139,-1.7471215370740638,0.4442951240785135,-0.2876882218232758,-1.7574019771805605,0.4442951240785135,-0.21618619032152353,-1.7647573951325752,0.4442951240785135,-0.14432433824121582,-1.769175548562827,0.4442951240785135,-0.07222227254315414,-1.7706490838909548,0.4442951240785135,-2.1684197330002512e-16,-1.769175548562827,0.4442951240785135,0.0722222725431537,-1.7647573951325752,0.4442951240785135,0.14432433824121538,-1.7574019771805605,0.4442951240785135,0.21618619032152311,-1.7471215370740638,0.4442951240785135,0.28768822182327464,-1.7339331855910745,0.4442951240785135,0.35871142467151274,-1.7178588734410878,0.4442951240785135,0.42913758775431704,-1.6989253547303178,0.4442951240785135,0.4988494936735341,-1.6771641424321253,0.4442951240785135,0.5677311138415712,-1.6526114559367844,0.4442951240785135,0.6356678015995296,-1.6253081607678825,0.4442951240785135,0.7025464830352508,-1.5952997005656895,0.4442951240785135,0.7682558451836854,-1.5626360214507007,0.4442951240785135,0.8326865212963415,-1.5273714888932528,0.4442951240785135,0.8957312728714374,-1.4895647972275603,0.4442951240785135,0.9572851681418107,-1.4492788719607899,0.4442951240785135,1.017245756723486,-1.4065807650397633,0.4442951240785135,1.0755132401342309,-1.3615415432496096,0.4442951240785135,1.1319906378982774,-1.3142361699301166,0.4442951240785135,1.186583948960753,-1.2647433802066468,0.4442951240785135,1.2392023081431631,-1.2131455499432913,0.4442951240785135,1.2897581373795115,-1.1595285586363782,0.4442951240785135,1.3381672914813456,-1.1039816464765195,0.4442951240785135,1.3843491981891238,-1.0465972658171285,0.4442951240785135,1.4282269922767836,-0.9874709272965975,0.4442951240785135,1.469727643486328,-0.926701040870263,0.4442951240785135,1.508782078079485,-0.8643887520167512,0.4442951240785135,1.5453252938041235,-0.8006377733913125,0.4442951240785135,1.5792964680840893,-0.7355542122063392,0.4442951240785135,1.6106390592523794,-0.6692463936263799,0.4442951240785135,1.6393009006591688,-0.6018246804715965,0.4442951240785135,1.66523428749805,-0.5334012895297452,0.4442951240785135,1.6883960562059728,-0.46409010478239987,0.4442951240785135,1.708747656304739,-0.3940064878563081,0.4442951240785135,1.7262552145644672,-0.323267086015351,0.4442951240785135,1.7408895913822395,-0.25198963801268187,0.4442951240785135,1.7526264292820974,-0.18029277812618788,0.4442951240785135,1.7614461934556553,-0.10829583870344513,0.4442951240785135,1.7673342042758622,-0.03611865154480646,0.4442951240785135,1.7702806617297897,0.03611865154480738,0.4442951240785135,1.7702806617297897,0.10829583870344447,0.4442951240785135,1.7673342042758622,0.18029277812618724,0.4442951240785135,1.7614461934556556,0.25198963801268126,0.4442951240785135,1.7526264292820977,0.3232670860153503,0.4442951240785135,1.7408895913822398,0.39400648785630743,0.4442951240785135,1.7262552145644672,0.46409010478239926,0.4442951240785135,1.7087476563047392,0.5334012895297445,0.4442951240785135,1.688396056205973,0.601824680471596,0.4442951240785135,1.6652342874980501,0.6692463936263779,0.4442951240785135,1.6393009006591694,0.73555421220634,0.4442951240785135,1.610639059252379,0.8006377733913133,0.4442951240785135,1.5792964680840889,0.8643887520167507,0.4442951240785135,1.5453252938041238,0.9267010408702612,0.4442951240785135,1.508782078079486,0.9874709272965968,0.4442951240785135,1.4697276434863284,1.0465972658171294,0.4442951240785135,1.428226992276783,1.103981646476519,0.4442951240785135,1.3843491981891243,1.1595285586363777,0.4442951240785135,1.3381672914813463,1.2131455499432906,0.4442951240785135,1.289758137379512,1.2647433802066457,0.4442951240785135,1.2392023081431642,1.3142361699301166,0.4442951240785135,1.186583948960753,1.36154154324961,0.4442951240785135,1.1319906378982765,1.4065807650397628,0.4442951240785135,1.0755132401342316,1.449278871960789,0.4442951240785135,1.0172457567234872,1.4895647972275594,0.4442951240785135,0.9572851681418119,1.5273714888932526,0.4442951240785135,0.895731272871438,1.562636021450701,0.4442951240785135,0.8326865212963414,1.5952997005656888,0.4442951240785135,0.7682558451836867,1.625308160767882,0.4442951240785135,0.7025464830352521,1.6526114559367844,0.4442951240785135,0.6356678015995303,1.6771641424321249,0.4442951240785135,0.5677311138415725,1.6989253547303178,0.4442951240785135,0.498849493673534,1.7178588734410876,0.4442951240785135,0.42913758775431765,1.7339331855910742,0.4442951240785135,0.35871142467151335,1.7471215370740638,0.4442951240785135,0.287688221823276,1.7574019771805605,0.4442951240785135,0.21618619032152453,1.7647573951325752,0.4442951240785135,0.14432433824121602,1.769175548562827,0.4442951240785135,0.07222227254315358,1.7706490838909548,0.4442951240785135,0.0,1.7398802756945588,0.4270102212210632,0.0,1.7384323461887785,0.4270102212210632,-0.07096725636202274,1.7340909676072047,0.4270102212210632,-0.14181639472953023,1.72686336574604,0.4270102212210632,-0.21242949370375205,1.7167615702352956,0.4270102212210632,-0.2826890247501936,1.703802394516662,0.4270102212210632,-0.3524780478132824,1.6880074078591925,0.4270102212210632,-0.42168040595155043,1.6694028994593837,0.4270102212210632,-0.4901809186693996,1.6480198346854047,0.4270102212210632,-0.5578655736236695,1.6238938035382942,0.4270102212210632,-0.6246217163859309,1.597064961415919,0.4270102212210632,-0.6903382379446641,1.5675779622782755,0.4270102212210632,-0.754905759635243,1.5354818843253815,0.4270102212210632,-0.8182168151899288,1.500830148311454,0.4270102212210632,-0.8801660296048659,1.4636804286313372,0.4270102212210632,-0.9406502945263767,1.4240945573271642,0.4270102212210632,-0.9995689398646412,1.3821384211750236,0.4270102212210632,-1.056823901349126,1.337881852022925,0.4270102212210632,-1.1123198837468842,1.2913985105625783,0.4270102212210632,-1.1659645194720671,1.2427657637284417,0.4270102212210632,-1.2176685223226544,1.1920645559280956,0.4270102212210632,-1.2673458360885266,1.1393792743182636,0.4270102212210632,-1.3149137777835325,1.0847976083507194,0.4270102212210632,-1.360293175263161,1.028410403821846,0.4270102212210632,-1.4034084989987634,0.9703115116687759,0.4270102212210632,-1.4441879877889958,0.9105976317637666,0.4270102212210632,-1.4825637681992616,0.8493681519668072,0.4270102212210632,-1.5184719675303409,0.7867249827043339,0.4270102212210632,-1.5518528201281974,0.7227723873493809,0.4270102212210632,-1.582650766858011,0.6576168086854889,0.4270102212210632,-1.6108145475768734,0.5913666917431945,0.4270102212210632,-1.6362972864512375,0.5241323033039786,0.4270102212210632,-1.659056569977118,0.4560255483720931,0.4270102212210632,-1.6790545175731804,0.3871597839197231,0.4270102212210632,-1.696257844629232,0.3176496302154948,0.4270102212210632,-1.7106379179051696,0.24761078005035025,0.4270102212210632,-1.7221708031881828,0.17715980617831276,0.4270102212210632,-1.7308373051288897,0.106413967292645,0.4270102212210632,-1.7366229991900988,0.03549101286032425,0.4270102212210632,-1.739518255655031,-0.035491012860324044,0.4270102212210632,-1.739518255655031,-0.10641396729264478,0.4270102212210632,-1.736622999190099,-0.1771598061783122,0.4270102212210632,-1.7308373051288897,-0.24761078005035006,0.4270102212210632,-1.7221708031881828,-0.31764963021549464,0.4270102212210632,-1.7106379179051696,-0.3871597839197225,0.4270102212210632,-1.696257844629232,-0.45602554837209286,0.4270102212210632,-1.6790545175731806,-0.5241323033039781,0.4270102212210632,-1.6590565699771183,-0.5913666917431943,0.4270102212210632,-1.6362972864512377,-0.6576168086854887,0.4270102212210632,-1.6108145475768736,-0.7227723873493804,0.4270102212210632,-1.5826507668580112,-0.786724982704334,0.4270102212210632,-1.5518528201281974,-0.849368151966807,0.4270102212210632,-1.5184719675303409,-0.910597631763767,0.4270102212210632,-1.4825637681992614,-0.9703115116687755,0.4270102212210632,-1.4441879877889963,-1.0284104038218456,0.4270102212210632,-1.4034084989987636,-1.0847976083507191,0.4270102212210632,-1.3602931752631615,-1.1393792743182638,0.4270102212210632,-1.3149137777835325,-1.1920645559280958,0.4270102212210632,-1.2673458360885266,-1.2427657637284417,0.4270102212210632,-1.2176685223226544,-1.2913985105625783,0.4270102212210632,-1.1659645194720671,-1.3378818520229248,0.4270102212210632,-1.1123198837468848,-1.3821384211750236,0.4270102212210632,-1.0568239013491258,-1.4240945573271637,0.4270102212210632,-0.9995689398646418,-1.4636804286313374,0.4270102212210632,-0.9406502945263765,-1.5008301483114537,0.4270102212210632,-0.8801660296048661,-1.5354818843253812,0.4270102212210632,-0.8182168151899291,-1.5675779622782753,0.4270102212210632,-0.7549057596352432,-1.5970649614159187,0.4270102212210632,-0.6903382379446642,-1.6238938035382942,0.4270102212210632,-0.624621716385931,-1.6480198346854047,0.4270102212210632,-0.5578655736236695,-1.6694028994593835,0.4270102212210632,-0.4901809186694003,-1.6880074078591925,0.4270102212210632,-0.4216804059515503,-1.7038023945166618,0.4270102212210632,-0.3524780478132829,-1.7167615702352956,0.4270102212210632,-0.28268902475019403,-1.72686336574604,0.4270102212210632,-0.2124294937037525,-1.7340909676072047,0.4270102212210632,-0.1418163947295306,-1.7384323461887785,0.4270102212210632,-0.07096725636202302,-1.7398802756945588,0.4270102212210632,-2.1307388105289558e-16,-1.7384323461887785,0.4270102212210632,0.0709672563620226,-1.7340909676072047,0.4270102212210632,0.14181639472953014,-1.72686336574604,0.4270102212210632,0.21242949370375208,-1.7167615702352959,0.4270102212210632,0.28268902475019286,-1.7038023945166623,0.4270102212210632,0.3524780478132818,-1.6880074078591925,0.4270102212210632,0.42168040595155065,-1.6694028994593837,0.4270102212210632,0.4901809186693999,-1.6480198346854047,0.4270102212210632,0.5578655736236692,-1.6238938035382942,0.4270102212210632,0.6246217163859306,-1.597064961415919,0.4270102212210632,0.6903382379446638,-1.567577962278276,0.4270102212210632,0.7549057596352422,-1.5354818843253815,0.4270102212210632,0.8182168151899286,-1.500830148311454,0.4270102212210632,0.8801660296048658,-1.4636804286313372,0.4270102212210632,0.9406502945263767,-1.4240945573271644,0.4270102212210632,0.9995689398646408,-1.382138421175024,0.4270102212210632,1.0568239013491254,-1.337881852022925,0.4270102212210632,1.1123198837468844,-1.291398510562578,0.4270102212210632,1.1659645194720674,-1.2427657637284422,0.4270102212210632,1.2176685223226542,-1.192064555928096,0.4270102212210632,1.2673458360885264,-1.1393792743182645,0.4270102212210632,1.3149137777835316,-1.0847976083507194,0.4270102212210632,1.360293175263161,-1.0284104038218453,0.4270102212210632,1.4034084989987636,-0.9703115116687759,0.4270102212210632,1.4441879877889958,-0.9105976317637674,0.4270102212210632,1.4825637681992612,-0.8493681519668074,0.4270102212210632,1.5184719675303409,-0.786724982704333,0.4270102212210632,1.551852820128198,-0.7227723873493808,0.4270102212210632,1.5826507668580112,-0.6576168086854894,0.4270102212210632,1.6108145475768731,-0.591366691743194,0.4270102212210632,1.6362972864512377,-0.5241323033039789,0.4270102212210632,1.659056569977118,-0.456025548372094,0.4270102212210632,1.6790545175731801,-0.38715978391972333,0.4270102212210632,1.696257844629232,-0.3176496302154943,0.4270102212210632,1.7106379179051696,-0.24761078005035006,0.4270102212210632,1.7221708031881828,-0.17715980617831337,0.4270102212210632,1.7308373051288894,-0.10641396729264482,0.4270102212210632,1.736622999190099,-0.03549101286032331,0.4270102212210632,1.739518255655031,0.03549101286032422,0.4270102212210632,1.739518255655031,0.10641396729264418,0.4270102212210632,1.736622999190099,0.17715980617831276,0.4270102212210632,1.7308373051288897,0.24761078005034942,0.4270102212210632,1.722170803188183,0.31764963021549364,0.4270102212210632,1.7106379179051698,0.3871597839197227,0.4270102212210632,1.696257844629232,0.45602554837209347,0.4270102212210632,1.6790545175731804,0.5241323033039783,0.4270102212210632,1.6590565699771183,0.5913666917431935,0.4270102212210632,1.636297286451238,0.6576168086854874,0.4270102212210632,1.6108145475768738,0.7227723873493815,0.4270102212210632,1.5826507668580108,0.7867249827043338,0.4270102212210632,1.5518528201281974,0.8493681519668069,0.4270102212210632,1.518471967530341,0.9105976317637656,0.4270102212210632,1.4825637681992623,0.9703115116687754,0.4270102212210632,1.4441879877889963,1.028410403821846,0.4270102212210632,1.4034084989987632,1.084797608350719,0.4270102212210632,1.3602931752631615,1.1393792743182642,0.4270102212210632,1.3149137777835322,1.1920645559280953,0.4270102212210632,1.2673458360885268,1.242765763728441,0.4270102212210632,1.2176685223226553,1.291398510562578,0.4270102212210632,1.1659645194720674,1.3378818520229254,0.4270102212210632,1.1123198837468835,1.3821384211750234,0.4270102212210632,1.056823901349126,1.4240945573271635,0.4270102212210632,0.999568939864642,1.4636804286313363,0.4270102212210632,0.9406502945263779,1.5008301483114537,0.4270102212210632,0.8801660296048663,1.5354818843253817,0.4270102212210632,0.8182168151899285,1.5675779622782753,0.4270102212210632,0.7549057596352434,1.5970649614159182,0.4270102212210632,0.6903382379446651,1.6238938035382942,0.4270102212210632,0.6246217163859312,1.6480198346854043,0.4270102212210632,0.5578655736236705,1.6694028994593837,0.4270102212210632,0.49018091866939983,1.6880074078591922,0.4270102212210632,0.42168040595155126,1.703802394516662,0.4270102212210632,0.3524780478132824,1.7167615702352956,0.4270102212210632,0.28268902475019425,1.72686336574604,0.4270102212210632,0.2124294937037535,1.7340909676072047,0.4270102212210632,0.14181639472953078,1.7384323461887785,0.4270102212210632,0.07096725636202246,1.7398802756945588,0.4270102212210632,0.0,1.7104073708802334,0.4075979749207561,0.0,1.7089839687451567,0.4075979749207561,-0.06976509824751871,1.7047161314523056,0.4075979749207561,-0.13941407937406378,1.697610962395665,0.4075979749207561,-0.20883101952414987,1.6876802874279266,0.4075979749207561,-0.27790038105159715,1.6749406351775242,0.4075979749207561,-0.3465072048205408,1.6594132095383627,0.4075979749207561,-0.4145373015435676,1.6411238543780295,0.4075979749207561,-0.48187744183851566,1.6201030105232286,0.4075979749207561,-0.5484155446876062,1.5963856650940262,0.4075979749207561,-0.6140408639852367,1.5700112932712442,0.4075979749207561,-0.6786441728639446,1.541023792593917,0.4075979749207561,-0.7421179454917476,1.5094714098961697,0.4075979749207561,-0.8043565360382806,1.4754066610051244,0.4075979749207561,-0.8652563545118535,1.4388862433334888,0.4075979749207561,-0.9247160391747689,1.3999709415123074,0.4075979749207561,-0.9826366252499323,1.358725526220943,0.4075979749207561,-1.0389217096379564,1.3152186463826725,0.4075979749207561,-1.0934776113706046,1.2695227149053292,0.4075979749207561,-1.1462135275335197,1.2217137881571634,0.4075979749207561,-1.1970416843987122,1.1718714393785246,0.4075979749207561,-1.2458774835152682,1.1200786262400548,0.4075979749207561,-1.2926396425151223,1.0664215527678342,0.4075979749207561,-1.337250330399538,1.0109895258652846,0.4075979749207561,-1.3796352970811234,0.9538748066706451,0.4075979749207561,-1.4197239969657736,0.895172456997411,0.4075979749207561,-1.4574497063688514,0.8349801811133277,0.4075979749207561,-1.4927496345701736,0.7733981631212793,0.4075979749207561,-1.5255650283229696,0.7105289002127345,0.4075979749207561,-1.5558412696428623,0.6464770320712901,0.4075979749207561,-1.5835279667141116,0.5813491667102425,0.4075979749207561,-1.6085790377618188,0.5152537030340695,0.4075979749207561,-1.630952787750495,0.4483006504191544,0.4075979749207561,-1.6506119777813304,0.38060144561403386,0.4075979749207561,-1.667523887072668,0.3122687672639279,0.4075979749207561,-1.6816603674205144,0.2434163483682556,0.4075979749207561,-1.692997890048448,0.1741587869832803,0.4075979749207561,-1.7015175847689452,0.10461135548495677,0.4075979749207561,-1.7072052713909456,0.0348898087094359,0.4075979749207561,-1.7100514833213798,-0.03488980870943569,0.4075979749207561,-1.7100514833213798,-0.10461135548495655,0.4075979749207561,-1.7072052713909458,-0.17415878698327975,0.4075979749207561,-1.7015175847689452,-0.2434163483682554,0.4075979749207561,-1.692997890048448,-0.31226876726392766,0.4075979749207561,-1.6816603674205144,-0.3806014456140333,0.4075979749207561,-1.667523887072668,-0.44830065041915423,0.4075979749207561,-1.6506119777813306,-0.5152537030340689,0.4075979749207561,-1.6309527877504952,-0.5813491667102423,0.4075979749207561,-1.608579037761819,-0.64647703207129,0.4075979749207561,-1.5835279667141118,-0.7105289002127341,0.4075979749207561,-1.5558412696428625,-0.7733981631212794,0.4075979749207561,-1.5255650283229696,-0.8349801811133276,0.4075979749207561,-1.4927496345701736,-0.8951724569974113,0.4075979749207561,-1.457449706368851,-0.9538748066706447,0.4075979749207561,-1.4197239969657738,-1.0109895258652843,0.4075979749207561,-1.3796352970811236,-1.066421552767834,0.4075979749207561,-1.3372503303995384,-1.120078626240055,0.4075979749207561,-1.2926396425151223,-1.1718714393785248,0.4075979749207561,-1.2458774835152682,-1.2217137881571634,0.4075979749207561,-1.1970416843987122,-1.2695227149053292,0.4075979749207561,-1.1462135275335197,-1.315218646382672,0.4075979749207561,-1.093477611370605,-1.358725526220943,0.4075979749207561,-1.0389217096379562,-1.3999709415123072,0.4075979749207561,-0.9826366252499329,-1.438886243333489,0.4075979749207561,-0.9247160391747686,-1.4754066610051242,0.4075979749207561,-0.8652563545118536,-1.5094714098961697,0.4075979749207561,-0.8043565360382808,-1.541023792593917,0.4075979749207561,-0.7421179454917478,-1.570011293271244,0.4075979749207561,-0.6786441728639447,-1.5963856650940262,0.4075979749207561,-0.6140408639852368,-1.6201030105232286,0.4075979749207561,-0.5484155446876062,-1.6411238543780293,0.4075979749207561,-0.48187744183851633,-1.6594132095383627,0.4075979749207561,-0.4145373015435675,-1.674940635177524,0.4075979749207561,-0.34650720482054137,-1.6876802874279266,0.4075979749207561,-0.27790038105159764,-1.697610962395665,0.4075979749207561,-0.20883101952415029,-1.7047161314523056,0.4075979749207561,-0.13941407937406414,-1.7089839687451567,0.4075979749207561,-0.06976509824751899,-1.7104073708802334,0.4075979749207561,-2.0946449119865177e-16,-1.7089839687451567,0.4075979749207561,0.06976509824751857,-1.7047161314523056,0.4075979749207561,0.1394140793740637,-1.697610962395665,0.4075979749207561,0.2088310195241499,-1.6876802874279269,0.4075979749207561,0.2779003810515965,-1.6749406351775245,0.4075979749207561,0.3465072048205402,-1.6594132095383627,0.4075979749207561,0.4145373015435678,-1.6411238543780295,0.4075979749207561,0.48187744183851594,-1.6201030105232286,0.4075979749207561,0.5484155446876058,-1.5963856650940262,0.4075979749207561,0.6140408639852365,-1.5700112932712442,0.4075979749207561,0.6786441728639444,-1.5410237925939174,0.4075979749207561,0.7421179454917468,-1.5094714098961697,0.4075979749207561,0.8043565360382804,-1.4754066610051244,0.4075979749207561,0.8652563545118532,-1.4388862433334888,0.4075979749207561,0.9247160391747689,-1.3999709415123076,0.4075979749207561,0.9826366252499319,-1.3587255262209434,0.4075979749207561,1.0389217096379557,-1.3152186463826723,0.4075979749207561,1.0934776113706046,-1.269522714905329,0.4075979749207561,1.14621352753352,-1.2217137881571638,0.4075979749207561,1.197041684398712,-1.171871439378525,0.4075979749207561,1.245877483515268,-1.120078626240056,0.4075979749207561,1.2926396425151216,-1.0664215527678342,0.4075979749207561,1.337250330399538,-1.010989525865284,0.4075979749207561,1.3796352970811236,-0.9538748066706451,0.4075979749207561,1.4197239969657736,-0.8951724569974117,0.4075979749207561,1.4574497063688507,-0.8349801811133278,0.4075979749207561,1.4927496345701734,-0.7733981631212784,0.4075979749207561,1.5255650283229703,-0.7105289002127345,0.4075979749207561,1.5558412696428625,-0.6464770320712908,0.4075979749207561,1.5835279667141113,-0.5813491667102421,0.4075979749207561,1.608579037761819,-0.5152537030340697,0.4075979749207561,1.630952787750495,-0.44830065041915534,0.4075979749207561,1.6506119777813302,-0.3806014456140341,0.4075979749207561,1.667523887072668,-0.3122687672639273,0.4075979749207561,1.6816603674205144,-0.2434163483682554,0.4075979749207561,1.692997890048448,-0.1741587869832809,0.4075979749207561,1.7015175847689452,-0.10461135548495659,0.4075979749207561,1.7072052713909458,-0.03488980870943498,0.4075979749207561,1.7100514833213798,0.034889808709435866,0.4075979749207561,1.7100514833213798,0.10461135548495597,0.4075979749207561,1.7072052713909458,0.17415878698328027,0.4075979749207561,1.7015175847689452,0.2434163483682548,0.4075979749207561,1.6929978900484481,0.3122687672639267,0.4075979749207561,1.6816603674205146,0.3806014456140335,0.4075979749207561,1.667523887072668,0.4483006504191548,0.4075979749207561,1.6506119777813304,0.5152537030340691,0.4075979749207561,1.6309527877504952,0.5813491667102414,0.4075979749207561,1.6085790377618192,0.6464770320712888,0.4075979749207561,1.583527966714112,0.7105289002127352,0.4075979749207561,1.5558412696428623,0.7733981631212792,0.4075979749207561,1.5255650283229696,0.8349801811133274,0.4075979749207561,1.4927496345701738,0.89517245699741,0.4075979749207561,1.457449706368852,0.9538748066706445,0.4075979749207561,1.4197239969657738,1.0109895258652846,0.4075979749207561,1.3796352970811232,1.0664215527678338,0.4075979749207561,1.3372503303995384,1.1200786262400555,0.4075979749207561,1.292639642515122,1.1718714393785243,0.4075979749207561,1.2458774835152684,1.221713788157163,0.4075979749207561,1.1970416843987128,1.269522714905329,0.4075979749207561,1.14621352753352,1.315218646382673,0.4075979749207561,1.093477611370604,1.3587255262209428,0.4075979749207561,1.0389217096379564,1.399970941512307,0.4075979749207561,0.982636625249933,1.438886243333488,0.4075979749207561,0.92471603917477,1.4754066610051242,0.4075979749207561,0.8652563545118538,1.50947140989617,0.4075979749207561,0.8043565360382803,1.541023792593917,0.4075979749207561,0.742117945491748,1.5700112932712438,0.4075979749207561,0.6786441728639456,1.5963856650940262,0.4075979749207561,0.6140408639852372,1.6201030105232284,0.4075979749207561,0.5484155446876071,1.6411238543780295,0.4075979749207561,0.48187744183851583,1.6594132095383625,0.4075979749207561,0.41453730154356844,1.6749406351775242,0.4075979749207561,0.3465072048205408,1.6876802874279266,0.4075979749207561,0.2779003810515978,1.697610962395665,0.4075979749207561,0.20883101952415126,1.7047161314523056,0.4075979749207561,0.1394140793740643,1.7089839687451567,0.4075979749207561,0.06976509824751843,1.7104073708802334,0.4075979749207561,0.0,1.6823772019772307,0.3861550960121945,0.0,1.6809771265670865,0.3861550960121945,-0.06862178729089781,1.6767792306240015,0.3861550960121945,-0.13712936038907686,1.6697905011314862,0.3861550960121945,-0.20540869520007043,1.6600225701400693,0.3861550960121945,-0.2733461475095163,1.6474916954068968,0.3861550960121945,-0.34082864213273584,1.6322187333363005,0.3861550960121945,-0.4077438611172198,1.614229104266371,0.3861550960121945,-0.4739804306847756,1.5935527501593139,0.3861550960121945,-0.5394281066021899,1.5702240847660078,0.3861550960121945,-0.6039779576718746,1.5442819363477138,0.3861550960121945,-0.6675225470370953,1.515769483050265,0.3861550960121945,-0.7299561110000161,1.484734181038305,0.3861550960121945,-0.7911747350549371,1.4512276855091841,0.3861550960121945,-0.8510765268437336,1.415305764717983,0.3861550960121945,-0.909561785745631,1.3770282071567541,0.3861550960121945,-0.9665331688190486,1.3364587220424775,0.3861550960121945,-1.0218958528193194,1.2936648332793577,0.3861550960121945,-1.0755576920226213,1.2487177670719474,0.3861550960121945,-1.127429371593443,1.2016923333761604,0.3861550960121945,-1.1774245562403114,1.152666801385483,0.3861550960121945,-1.2254600339123651,1.1017227692596243,0.3861550960121945,-1.2714558542976002,1.0489450283124342,0.3861550960121945,-1.3153354618922741,0.9944214218851277,0.3861550960121945,-1.3570258234199881,0.9382426991397138,0.3861550960121945,-1.3964575493883657,0.8805023640159709,0.3861550960121945,-1.4335650095810175,0.8212965196033674,0.3861550960121945,-1.4682864422925561,0.7607237081869528,0.3861550960121945,-1.5005640571248624,0.6988847472334486,0.3861550960121945,-1.5303441311735,0.6358825615905271,0.3861550960121945,-1.5575770984441846,0.5718220121785581,0.3861550960121945,-1.5822176323504933,0.5068097214599548,0.3861550960121945,-1.6042247211554927,0.4409538959766101,0.3861550960121945,-1.6235617362317285,0.37436414625078446,0.3861550960121945,-1.6401964930259598,0.3071513043492096,0.3861550960121945,-1.6541013046271729,0.2394272394140519,0.3861550960121945,-1.6652530278487117,0.1713046714677636,0.3861550960121945,-1.6736331017478285,0.10289698380173236,0.3861550960121945,-1.6792275785185407,0.034318034260980596,0.3861550960121945,-1.6820271467063777,-0.03431803426098039,0.3861550960121945,-1.6820271467063777,-0.10289698380173215,0.3861550960121945,-1.6792275785185409,-0.17130467146776304,0.3861550960121945,-1.6736331017478285,-0.2394272394140517,0.3861550960121945,-1.6652530278487117,-0.30715130434920945,0.3861550960121945,-1.6541013046271729,-0.3743641462507839,0.3861550960121945,-1.6401964930259598,-0.44095389597660994,0.3861550960121945,-1.6235617362317285,-0.5068097214599543,0.3861550960121945,-1.604224721155493,-0.5718220121785579,0.3861550960121945,-1.5822176323504935,-0.6358825615905269,0.3861550960121945,-1.5575770984441848,-0.6988847472334482,0.3861550960121945,-1.5303441311735,-0.7607237081869528,0.3861550960121945,-1.5005640571248624,-0.8212965196033672,0.3861550960121945,-1.4682864422925561,-0.8805023640159714,0.3861550960121945,-1.433565009581017,-0.9382426991397135,0.3861550960121945,-1.3964575493883662,-0.9944214218851274,0.3861550960121945,-1.3570258234199883,-1.048945028312434,0.3861550960121945,-1.3153354618922746,-1.1017227692596245,0.3861550960121945,-1.2714558542976002,-1.1526668013854833,0.3861550960121945,-1.2254600339123651,-1.2016923333761604,0.3861550960121945,-1.1774245562403114,-1.2487177670719474,0.3861550960121945,-1.127429371593443,-1.2936648332793572,0.3861550960121945,-1.0755576920226217,-1.3364587220424775,0.3861550960121945,-1.0218958528193192,-1.3770282071567537,0.3861550960121945,-0.9665331688190492,-1.4153057647179832,0.3861550960121945,-0.9095617857456307,-1.451227685509184,0.3861550960121945,-0.8510765268437338,-1.4847341810383048,0.3861550960121945,-0.7911747350549372,-1.515769483050265,0.3861550960121945,-0.7299561110000163,-1.5442819363477138,0.3861550960121945,-0.6675225470370955,-1.5702240847660078,0.3861550960121945,-0.6039779576718747,-1.5935527501593139,0.3861550960121945,-0.5394281066021899,-1.6142291042663708,0.3861550960121945,-0.47398043068477624,-1.6322187333363005,0.3861550960121945,-0.4077438611172196,-1.6474916954068966,0.3861550960121945,-0.3408286421327364,-1.6600225701400693,0.3861550960121945,-0.27334614750951675,-1.6697905011314862,0.3861550960121945,-0.20540869520007085,-1.6767792306240015,0.3861550960121945,-0.1371293603890772,-1.6809771265670865,0.3861550960121945,-0.06862178729089807,-1.6823772019772307,0.3861550960121945,-2.0603178553598959e-16,-1.6809771265670865,0.3861550960121945,0.06862178729089767,-1.6767792306240015,0.3861550960121945,0.13712936038907678,-1.6697905011314862,0.3861550960121945,0.20540869520007046,-1.6600225701400695,0.3861550960121945,0.27334614750951564,-1.647491695406897,0.3861550960121945,0.3408286421327352,-1.6322187333363005,0.3861550960121945,0.40774386111721994,-1.614229104266371,0.3861550960121945,0.47398043068477586,-1.5935527501593139,0.3861550960121945,0.5394281066021895,-1.570224084766008,0.3861550960121945,0.6039779576718743,-1.5442819363477138,0.3861550960121945,0.6675225470370951,-1.5157694830502655,0.3861550960121945,0.7299561110000153,-1.484734181038305,0.3861550960121945,0.7911747350549369,-1.4512276855091841,0.3861550960121945,0.8510765268437334,-1.415305764717983,0.3861550960121945,0.909561785745631,-1.3770282071567543,0.3861550960121945,0.9665331688190483,-1.336458722042478,0.3861550960121945,1.0218958528193187,-1.2936648332793574,0.3861550960121945,1.0755576920226213,-1.2487177670719471,0.3861550960121945,1.1274293715934431,-1.2016923333761609,0.3861550960121945,1.1774245562403112,-1.1526668013854833,0.3861550960121945,1.2254600339123651,-1.1017227692596252,0.3861550960121945,1.2714558542975993,-1.0489450283124342,0.3861550960121945,1.3153354618922741,-0.994421421885127,0.3861550960121945,1.3570258234199886,-0.9382426991397138,0.3861550960121945,1.3964575493883657,-0.8805023640159717,0.3861550960121945,1.4335650095810168,-0.8212965196033675,0.3861550960121945,1.468286442292556,-0.7607237081869519,0.3861550960121945,1.500564057124863,-0.6988847472334485,0.3861550960121945,1.5303441311735,-0.6358825615905277,0.3861550960121945,1.5575770984441843,-0.5718220121785577,0.3861550960121945,1.5822176323504935,-0.5068097214599551,0.3861550960121945,1.6042247211554927,-0.44095389597661105,0.3861550960121945,1.6235617362317283,-0.3743641462507847,0.3861550960121945,1.6401964930259598,-0.3071513043492091,0.3861550960121945,1.6541013046271729,-0.2394272394140517,0.3861550960121945,1.6652530278487117,-0.17130467146776418,0.3861550960121945,1.6736331017478283,-0.10289698380173218,0.3861550960121945,1.6792275785185409,-0.03431803426097968,0.3861550960121945,1.6820271467063777,0.03431803426098056,0.3861550960121945,1.6820271467063777,0.10289698380173155,0.3861550960121945,1.6792275785185409,0.17130467146776357,0.3861550960121945,1.6736331017478285,0.2394272394140511,0.3861550960121945,1.6652530278487119,0.3071513043492085,0.3861550960121945,1.654101304627173,0.37436414625078407,0.3861550960121945,1.6401964930259598,0.4409538959766105,0.3861550960121945,1.6235617362317285,0.5068097214599545,0.3861550960121945,1.604224721155493,0.5718220121785571,0.3861550960121945,1.5822176323504935,0.6358825615905258,0.3861550960121945,1.5575770984441852,0.6988847472334493,0.3861550960121945,1.5303441311734998,0.7607237081869527,0.3861550960121945,1.5005640571248624,0.821296519603367,0.3861550960121945,1.4682864422925563,0.88050236401597,0.3861550960121945,1.433565009581018,0.9382426991397133,0.3861550960121945,1.3964575493883662,0.9944214218851277,0.3861550960121945,1.357025823419988,1.0489450283124337,0.3861550960121945,1.3153354618922746,1.1017227692596248,0.3861550960121945,1.2714558542976,1.1526668013854828,0.3861550960121945,1.2254600339123654,1.20169233337616,0.3861550960121945,1.177424556240312,1.2487177670719471,0.3861550960121945,1.1274293715934431,1.2936648332793579,0.3861550960121945,1.0755576920226206,1.3364587220424773,0.3861550960121945,1.0218958528193194,1.3770282071567534,0.3861550960121945,0.9665331688190494,1.4153057647179823,0.3861550960121945,0.9095617857456321,1.451227685509184,0.3861550960121945,0.8510765268437339,1.4847341810383052,0.3861550960121945,0.7911747350549367,1.515769483050265,0.3861550960121945,0.7299561110000166,1.5442819363477134,0.3861550960121945,0.6675225470370965,1.5702240847660078,0.3861550960121945,0.603977957671875,1.5935527501593134,0.3861550960121945,0.5394281066021908,1.614229104266371,0.3861550960121945,0.47398043068477574,1.6322187333363003,0.3861550960121945,0.40774386111722055,1.6474916954068968,0.3861550960121945,0.34082864213273584,1.6600225701400693,0.3861550960121945,0.27334614750951697,1.6697905011314862,0.3861550960121945,0.20540869520007182,1.6767792306240015,0.3861550960121945,0.1371293603890774,1.6809771265670865,0.3861550960121945,0.06862178729089755,1.6823772019772307,0.3861550960121945,0.0,1.6559294138770655,0.36278841183949134,0.0,1.6545513483335126,0.36278841183949134,-0.06754301940983563,1.6504194453569505,0.36278841183949134,-0.13497362012962782,1.6435405820921103,0.36278841183949134,-0.20217957057914823,1.6339262077280265,0.36278841183949134,-0.2690490130863728,1.6215923244419939,0.36278841183949134,-0.33547065006353777,1.6065594607655214,0.36278841183949134,-0.40133392925099054,1.5888526374166172,0.36278841183949134,-0.4665292277205157,1.5685013256552747,0.36278841183949134,-0.5309480343318803,1.545539398231466,0.36278841183949134,-0.5944831303389181,1.5200050730072951,0.36278841183949134,-0.657028767844551,1.4919408493471362,0.36278841183949134,-0.7184808458077275,1.4613934373816366,0.36278841183949134,-0.7787370833093326,1.4284136802633145,0.36278841183949134,-0.8376971897886821,1.3930564695431487,0.36278841183949134,-0.8952630319672652,1.355380653809009,0.36278841183949134,-0.9513387971819005,1.3154489407379901,0.36278841183949134,-1.005831152855459,1.2733277927256714,0.36278841183949134,-1.0586494018397268,1.2290873162660185,0.36278841183949134,-1.1097056333718587,1.1828011452660425,0.36278841183949134,-1.1589148693931663,1.1345463184894296,0.36278841183949134,-1.2061952059867107,1.0844031513331207,0.36278841183949134,-1.2514679496982886,1.0324551021502604,0.36278841183949134,-1.2946577485139217,0.9787886333420036,0.36278841183949134,-1.3356927172758466,0.9234930674493825,0.36278841183949134,-1.3745045573282657,0.8666604384847523,0.36278841183949134,-1.4110286701937205,0.8083853387502598,0.36278841183949134,-1.4452042650908796,0.7487647613982927,0.36278841183949134,-1.476974460114798,0.6878979389959473,0.36278841183949134,-1.5062863769112331,0.625886178362217,0.36278841183949134,-1.5330912286874452,0.562832691952788,0.36278841183949134,-1.5573444014130013,0.49884242607309187,0.36278841183949134,-1.5790055280754256,0.43402188620554005,0.36278841183949134,-1.5980385558671064,0.368478959741657,0.36278841183949134,-1.6144118061916384,0.30232273641416507,0.36278841183949134,-1.6280980273897188,0.23566332672789075,0.36278841183949134,-1.6390744400968447,0.1686116786916946,0.36278841183949134,-1.647322775157319,0.10127939315646207,0.36278841183949134,-1.652829304031454,0.03377853806649941,0.36278841183949134,-1.6555848616453739,-0.033778538066499206,0.36278841183949134,-1.6555848616453739,-0.10127939315646187,0.36278841183949134,-1.6528293040314541,-0.16861167869169405,0.36278841183949134,-1.647322775157319,-0.23566332672789056,0.36278841183949134,-1.6390744400968447,-0.30232273641416485,0.36278841183949134,-1.6280980273897188,-0.36847895974165645,0.36278841183949134,-1.6144118061916384,-0.43402188620553983,0.36278841183949134,-1.5980385558671064,-0.4988424260730913,0.36278841183949134,-1.5790055280754258,-0.5628326919527877,0.36278841183949134,-1.5573444014130016,-0.6258861783622168,0.36278841183949134,-1.5330912286874454,-0.6878979389959469,0.36278841183949134,-1.5062863769112333,-0.7487647613982928,0.36278841183949134,-1.476974460114798,-0.8083853387502596,0.36278841183949134,-1.4452042650908796,-0.8666604384847526,0.36278841183949134,-1.41102867019372,-0.9234930674493822,0.36278841183949134,-1.3745045573282662,-0.9787886333420033,0.36278841183949134,-1.3356927172758468,-1.0324551021502602,0.36278841183949134,-1.294657748513922,-1.084403151333121,0.36278841183949134,-1.2514679496982886,-1.1345463184894298,0.36278841183949134,-1.2061952059867107,-1.1828011452660425,0.36278841183949134,-1.1589148693931663,-1.2290873162660185,0.36278841183949134,-1.1097056333718587,-1.273327792725671,0.36278841183949134,-1.0586494018397272,-1.3154489407379901,0.36278841183949134,-1.0058311528554589,-1.3553806538090085,0.36278841183949134,-0.9513387971819011,-1.3930564695431487,0.36278841183949134,-0.8952630319672651,-1.4284136802633143,0.36278841183949134,-0.8376971897886824,-1.4613934373816364,0.36278841183949134,-0.7787370833093327,-1.491940849347136,0.36278841183949134,-0.7184808458077278,-1.5200050730072951,0.36278841183949134,-0.6570287678445511,-1.545539398231466,0.36278841183949134,-0.5944831303389182,-1.5685013256552747,0.36278841183949134,-0.5309480343318803,-1.5888526374166172,0.36278841183949134,-0.4665292277205163,-1.6065594607655214,0.36278841183949134,-0.4013339292509904,-1.6215923244419939,0.36278841183949134,-0.33547065006353827,-1.6339262077280265,0.36278841183949134,-0.26904901308637325,-1.6435405820921103,0.36278841183949134,-0.20217957057914865,-1.6504194453569505,0.36278841183949134,-0.13497362012962819,-1.6545513483335126,0.36278841183949134,-0.0675430194098359,-1.6559294138770655,0.36278841183949134,-2.027928656318501e-16,-1.6545513483335126,0.36278841183949134,0.0675430194098355,-1.6504194453569505,0.36278841183949134,0.13497362012962777,-1.6435405820921103,0.36278841183949134,0.20217957057914826,-1.6339262077280268,0.36278841183949134,0.2690490130863722,-1.621592324441994,0.36278841183949134,0.33547065006353716,-1.6065594607655214,0.36278841183949134,0.4013339292509907,-1.5888526374166172,0.36278841183949134,0.46652922772051597,-1.5685013256552747,0.36278841183949134,0.53094803433188,-1.5455393982314662,0.36278841183949134,0.5944831303389178,-1.5200050730072951,0.36278841183949134,0.6570287678445508,-1.4919408493471364,0.36278841183949134,0.7184808458077268,-1.4613934373816366,0.36278841183949134,0.7787370833093323,-1.4284136802633145,0.36278841183949134,0.837697189788682,-1.3930564695431487,0.36278841183949134,0.8952630319672652,-1.3553806538090092,0.36278841183949134,0.9513387971819002,-1.3154489407379906,0.36278841183949134,1.0058311528554584,-1.2733277927256712,0.36278841183949134,1.058649401839727,-1.2290873162660183,0.36278841183949134,1.109705633371859,-1.182801145266043,0.36278841183949134,1.1589148693931661,-1.13454631848943,0.36278841183949134,1.2061952059867105,-1.0844031513331216,0.36278841183949134,1.251467949698288,-1.0324551021502604,0.36278841183949134,1.2946577485139217,-0.9787886333420028,0.36278841183949134,1.335692717275847,-0.9234930674493825,0.36278841183949134,1.3745045573282657,-0.8666604384847529,0.36278841183949134,1.4110286701937198,-0.80838533875026,0.36278841183949134,1.4452042650908794,-0.7487647613982918,0.36278841183949134,1.4769744601147987,-0.6878979389959472,0.36278841183949134,1.5062863769112333,-0.6258861783622176,0.36278841183949134,1.5330912286874452,-0.5628326919527874,0.36278841183949134,1.5573444014130016,-0.4988424260730921,0.36278841183949134,1.5790055280754256,-0.43402188620554094,0.36278841183949134,1.5980385558671062,-0.36847895974165723,0.36278841183949134,1.6144118061916384,-0.3023227364141646,0.36278841183949134,1.6280980273897188,-0.23566332672789056,0.36278841183949134,1.6390744400968447,-0.1686116786916952,0.36278841183949134,1.6473227751573187,-0.10127939315646191,0.36278841183949134,1.6528293040314541,-0.033778538066498505,0.36278841183949134,1.6555848616453739,0.03377853806649937,0.36278841183949134,1.6555848616453739,0.1012793931564613,0.36278841183949134,1.6528293040314541,0.16861167869169458,0.36278841183949134,1.647322775157319,0.23566332672788998,0.36278841183949134,1.6390744400968449,0.30232273641416396,0.36278841183949134,1.6280980273897188,0.3684789597416566,0.36278841183949134,1.6144118061916384,0.4340218862055404,0.36278841183949134,1.5980385558671064,0.49884242607309154,0.36278841183949134,1.5790055280754258,0.562832691952787,0.36278841183949134,1.5573444014130018,0.6258861783622156,0.36278841183949134,1.5330912286874459,0.687897938995948,0.36278841183949134,1.506286376911233,0.7487647613982926,0.36278841183949134,1.476974460114798,0.8083853387502595,0.36278841183949134,1.4452042650908798,0.8666604384847513,0.36278841183949134,1.411028670193721,0.923493067449382,0.36278841183949134,1.3745045573282662,0.9787886333420036,0.36278841183949134,1.3356927172758464,1.0324551021502602,0.36278841183949134,1.294657748513922,1.0844031513331212,0.36278841183949134,1.2514679496982883,1.1345463184894296,0.36278841183949134,1.206195205986711,1.182801145266042,0.36278841183949134,1.158914869393167,1.2290873162660183,0.36278841183949134,1.109705633371859,1.2733277927256716,0.36278841183949134,1.0586494018397261,1.31544894073799,0.36278841183949134,1.005831152855459,1.3553806538090085,0.36278841183949134,0.9513387971819012,1.3930564695431478,0.36278841183949134,0.8952630319672663,1.4284136802633143,0.36278841183949134,0.8376971897886826,1.4613934373816369,0.36278841183949134,0.7787370833093322,1.491940849347136,0.36278841183949134,0.718480845807728,1.5200050730072947,0.36278841183949134,0.657028767844552,1.545539398231466,0.36278841183949134,0.5944831303389184,1.5685013256552744,0.36278841183949134,0.5309480343318812,1.5888526374166172,0.36278841183949134,0.46652922772051586,1.6065594607655211,0.36278841183949134,0.4013339292509913,1.6215923244419939,0.36278841183949134,0.33547065006353777,1.6339262077280265,0.36278841183949134,0.26904901308637347,1.6435405820921103,0.36278841183949134,0.2021795705791496,1.6504194453569505,0.36278841183949134,0.13497362012962835,1.6545513483335126,0.36278841183949134,0.06754301940983537,1.6559294138770655,0.36278841183949134,0.0,1.6311957681296887,0.33761433404781516,0.0,1.6298382859423375,0.33761433404781516,-0.06653416897165199,1.625768098775416,0.33761433404781516,-0.13295759838525228,1.6189919810537803,0.33761433404781516,-0.19915973299781478,1.6095212109566486,0.33761433404781516,-0.26503038588971034,1.597371551646185,0.33761433404781516,-0.3304599218599202,1.5825632250312722,0.33761433404781516,-0.3953394399030081,1.5651208781101391,0.33761433404781516,-0.45956095446409706,1.5450735419478658,0.33761433404781516,-0.5230175751701677,1.5224545833570389,0.33761433404781516,-0.5856036847385357,1.497301649361987,0.33761433404781516,-0.6472151147663957,1.4696566045390227,0.33761433404781516,-0.7077493191088465,1.4395654613369873,0.33761433404781516,-0.7671055445568308,1.407078303494071,0.33761433404781516,-0.8251849985309064,1.3722492026783728,0.33761433404781516,-0.8818910135117463,1.3351361284909458,0.33761433404781516,-0.9371292079336841,1.2958008519811157,0.33761433404781516,-0.9908076432735166,1.2543088428346683,0.33761433404781516,-1.0428369770730996,1.210729160406016,0.33761433404781516,-1.0931306116410557,1.1651343387757185,0.33761433404781516,-1.1416048381860837,1.1176002660246647,0.33761433404781516,-1.1881789761419799,1.0682060579258508,0.33761433404781516,-1.2327755074524795,1.0170339262639843,0.33761433404781516,-1.2753202055924109,0.9641690420020811,0.33761433404781516,-1.315742259110421,0.9096993935228035,0.33761433404781516,-1.3539743894876477,0.8537156401804814,0.33761433404781516,-1.3899529631161756,0.7963109614075632,0.33761433404781516,-1.4236180972108945,0.737580901626647,0.33761433404781516,-1.454913759478488,0.6776232112262167,0.33761433404781516,-1.483787861377655,0.6165376838647699,0.33761433404781516,-1.5101923448153451,0.5544259903741201,0.33761433404781516,-1.5340832621347125,0.49139150953833083,0.33761433404781516,-1.5554208492616541,0.42753915602993303,0.33761433404781516,-1.5741695918881857,0.3629752057898061,0.33761433404781516,-1.5902982845825036,0.2978071191413622,0.33761433404781516,-1.6037800827273445,0.2321433619334441,0.33761433404781516,-1.614592547200204,0.1660932250096221,0.33761433404781516,-1.6227176817210405,0.0997666423043734,0.33761433404781516,-1.6281419628053064,0.03327400786889576,0.33761433404781516,-1.6308563622724552,-0.03327400786889557,0.33761433404781516,-1.6308563622724552,-0.0997666423043732,0.33761433404781516,-1.6281419628053067,-0.16609322500962154,0.33761433404781516,-1.6227176817210405,-0.23214336193344393,0.33761433404781516,-1.614592547200204,-0.29780711914136204,0.33761433404781516,-1.6037800827273445,-0.3629752057898055,0.33761433404781516,-1.5902982845825036,-0.4275391560299328,0.33761433404781516,-1.574169591888186,-0.4913915095383303,0.33761433404781516,-1.5554208492616544,-0.55442599037412,0.33761433404781516,-1.5340832621347127,-0.6165376838647697,0.33761433404781516,-1.5101923448153451,-0.6776232112262163,0.33761433404781516,-1.4837878613776552,-0.7375809016266471,0.33761433404781516,-1.454913759478488,-0.7963109614075631,0.33761433404781516,-1.4236180972108945,-0.8537156401804817,0.33761433404781516,-1.389952963116175,-0.9096993935228032,0.33761433404781516,-1.3539743894876481,-0.9641690420020806,0.33761433404781516,-1.3157422591104213,-1.0170339262639843,0.33761433404781516,-1.2753202055924113,-1.068206057925851,0.33761433404781516,-1.2327755074524795,-1.117600266024665,0.33761433404781516,-1.1881789761419799,-1.1651343387757185,0.33761433404781516,-1.1416048381860837,-1.210729160406016,0.33761433404781516,-1.0931306116410557,-1.254308842834668,0.33761433404781516,-1.0428369770731,-1.2958008519811157,0.33761433404781516,-0.9908076432735163,-1.3351361284909453,0.33761433404781516,-0.9371292079336847,-1.372249202678373,0.33761433404781516,-0.881891013511746,-1.4070783034940708,0.33761433404781516,-0.8251849985309067,-1.4395654613369873,0.33761433404781516,-0.767105544556831,-1.4696566045390225,0.33761433404781516,-0.7077493191088469,-1.4973016493619868,0.33761433404781516,-0.6472151147663958,-1.5224545833570389,0.33761433404781516,-0.5856036847385359,-1.5450735419478658,0.33761433404781516,-0.5230175751701677,-1.565120878110139,0.33761433404781516,-0.45956095446409767,-1.5825632250312722,0.33761433404781516,-0.395339439903008,-1.5973715516461848,0.33761433404781516,-0.3304599218599207,-1.6095212109566486,0.33761433404781516,-0.2650303858897108,-1.6189919810537803,0.33761433404781516,-0.1991597329978152,-1.625768098775416,0.33761433404781516,-0.13295759838525262,-1.6298382859423375,0.33761433404781516,-0.06653416897165225,-1.6311957681296887,0.33761433404781516,-1.9976386762227313e-16,-1.6298382859423375,0.33761433404781516,0.06653416897165186,-1.625768098775416,0.33761433404781516,0.13295759838525223,-1.6189919810537803,0.33761433404781516,0.1991597329978148,-1.6095212109566486,0.33761433404781516,0.26503038588970973,-1.597371551646185,0.33761433404781516,0.3304599218599196,-1.5825632250312722,0.33761433404781516,0.3953394399030083,-1.5651208781101391,0.33761433404781516,0.45956095446409734,-1.5450735419478658,0.33761433404781516,0.5230175751701673,-1.522454583357039,0.33761433404781516,0.5856036847385354,-1.497301649361987,0.33761433404781516,0.6472151147663954,-1.469656604539023,0.33761433404781516,0.7077493191088459,-1.4395654613369873,0.33761433404781516,0.7671055445568307,-1.407078303494071,0.33761433404781516,0.8251849985309062,-1.3722492026783728,0.33761433404781516,0.8818910135117463,-1.3351361284909458,0.33761433404781516,0.9371292079336838,-1.2958008519811162,0.33761433404781516,0.990807643273516,-1.254308842834668,0.33761433404781516,1.0428369770730996,-1.2107291604060157,0.33761433404781516,1.093130611641056,-1.165134338775719,0.33761433404781516,1.1416048381860835,-1.1176002660246651,0.33761433404781516,1.1881789761419796,-1.0682060579258517,0.33761433404781516,1.2327755074524789,-1.0170339262639843,0.33761433404781516,1.2753202055924109,-0.9641690420020803,0.33761433404781516,1.3157422591104215,-0.9096993935228035,0.33761433404781516,1.3539743894876477,-0.8537156401804821,0.33761433404781516,1.3899529631161749,-0.7963109614075633,0.33761433404781516,1.4236180972108943,-0.7375809016266462,0.33761433404781516,1.4549137594784884,-0.6776232112262167,0.33761433404781516,1.4837878613776552,-0.6165376838647705,0.33761433404781516,1.510192344815345,-0.5544259903741197,0.33761433404781516,1.5340832621347127,-0.491391509538331,0.33761433404781516,1.5554208492616541,-0.4275391560299339,0.33761433404781516,1.5741695918881857,-0.3629752057898063,0.33761433404781516,1.5902982845825036,-0.2978071191413617,0.33761433404781516,1.6037800827273445,-0.23214336193344393,0.33761433404781516,1.614592547200204,-0.16609322500962265,0.33761433404781516,1.6227176817210405,-0.09976664230437324,0.33761433404781516,1.6281419628053067,-0.03327400786889488,0.33761433404781516,1.6308563622724552,0.03327400786889573,0.33761433404781516,1.6308563622724552,0.09976664230437263,0.33761433404781516,1.6281419628053067,0.16609322500962206,0.33761433404781516,1.6227176817210405,0.23214336193344332,0.33761433404781516,1.6145925472002043,0.29780711914136115,0.33761433404781516,1.6037800827273445,0.3629752057898057,0.33761433404781516,1.5902982845825036,0.42753915602993336,0.33761433404781516,1.5741695918881857,0.49139150953833044,0.33761433404781516,1.5554208492616544,0.5544259903741191,0.33761433404781516,1.534083262134713,0.6165376838647686,0.33761433404781516,1.5101923448153456,0.6776232112262174,0.33761433404781516,1.4837878613776547,0.7375809016266469,0.33761433404781516,1.454913759478488,0.7963109614075629,0.33761433404781516,1.4236180972108945,0.8537156401804804,0.33761433404781516,1.389952963116176,0.9096993935228029,0.33761433404781516,1.3539743894876481,0.9641690420020811,0.33761433404781516,1.3157422591104209,1.017033926263984,0.33761433404781516,1.2753202055924113,1.0682060579258514,0.33761433404781516,1.2327755074524793,1.1176002660246647,0.33761433404781516,1.18817897614198,1.165134338775718,0.33761433404781516,1.1416048381860844,1.2107291604060157,0.33761433404781516,1.093130611641056,1.2543088428346687,0.33761433404781516,1.042836977073099,1.2958008519811155,0.33761433404781516,0.9908076432735166,1.335136128490945,0.33761433404781516,0.9371292079336849,1.3722492026783721,0.33761433404781516,0.8818910135117474,1.4070783034940708,0.33761433404781516,0.8251849985309068,1.4395654613369875,0.33761433404781516,0.7671055445568306,1.4696566045390225,0.33761433404781516,0.7077493191088471,1.4973016493619864,0.33761433404781516,0.6472151147663967,1.5224545833570389,0.33761433404781516,0.5856036847385361,1.5450735419478654,0.33761433404781516,0.5230175751701687,1.5651208781101391,0.33761433404781516,0.4595609544640972,1.582563225031272,0.33761433404781516,0.3953394399030089,1.597371551646185,0.33761433404781516,0.3304599218599202,1.6095212109566486,0.33761433404781516,0.265030385889711,1.6189919810537803,0.33761433404781516,0.1991597329978161,1.625768098775416,0.33761433404781516,0.1329575983852528,1.6298382859423375,0.33761433404781516,0.06653416897165174,1.6311957681296887,0.33761433404781516,0.0,1.6082994865140738,0.3107582786264297,0.0,1.6069610586272898,0.3107582786264297,-0.06560026201848318,1.602948002648067,0.3107582786264297,-0.13109133887487173,1.5962669979120374,0.3107582786264297,-0.1963642271349979,1.5869291642922416,0.3107582786264297,-0.2613102865180797,1.574950043691197,0.3107582786264297,-0.3258214207177463,1.560349574172934,0.3107582786264297,-0.38979025731767236,1.543152056778058,0.3107582786264297,-0.4531103265023685,1.523386115077064,0.3107582786264297,-0.5156762382656815,1.501084647529229,0.3107582786264297,-0.577383857822059,1.4762847727263733,0.3107582786264297,-0.6381304789286232,1.449027767612627,0.3107582786264297,-0.697814994829575,1.4193589987830313,0.3107582786264297,-0.7563380665384128,1.3873278469753165,0.3107582786264297,-0.8136022881778704,1.3529876248805393,0.3107582786264297,-0.8695123491023867,1.3163954884093685,0.3107582786264297,-0.9239751925332704,1.2776123415617124,0.3107582786264297,-0.9769001704425238,1.2367027350580233,0.3107582786264297,-1.028199194427538,1.1937347589009912,0.3107582786264297,-1.0777868823255485,1.148779929046456,0.3107582786264297,-1.1255807003238165,1.1019130683721556,0.3107582786264297,-1.1715011003290157,1.0532121821424314,0.3107582786264297,-1.2154716523671834,1.002758328176163,0.3107582786264297,-1.2574191717938692,0.9506354819340279,0.3107582786264297,-1.2972738411027536,0.8969303967496363,0.3107582786264297,-1.3349693261299953,0.8417324594371672,0.3107582786264297,-1.370442886460903,0.7851335415158393,0.3107582786264297,-1.4036354798551616,0.727227846298833,0.3107582786264297,-1.4344918605168153,0.6681117521011712,0.3107582786264297,-1.4629606710454441,0.6078836518275287,0.3107582786264297,-1.4889945279154861,0.5466437892069499,0.3107582786264297,-1.5125501003414403,0.4844940919470554,0.3107582786264297,-1.533588182397684,0.42153800208543274,0.3107582786264297,-1.5520737582728648,0.3578803038205727,0.3107582786264297,-1.567976060550265,0.29362694910891185,0.3107582786264297,-1.5812686214171285,0.22888488131825827,0.3107582786264297,-1.5919293167177242,0.16376185723110806,0.3107582786264297,-1.5999404027768196,0.09836626769411777,0.3107582786264297,-1.6052885459322765,0.03280695721223549,0.3107582786264297,-1.6079648447276165,-0.032806957212235296,0.3107582786264297,-1.6079648447276165,-0.09836626769411756,0.3107582786264297,-1.6052885459322765,-0.16376185723110753,0.3107582786264297,-1.5999404027768196,-0.2288848813182581,0.3107582786264297,-1.5919293167177242,-0.2936269491089117,0.3107582786264297,-1.5812686214171285,-0.3578803038205721,0.3107582786264297,-1.567976060550265,-0.4215380020854326,0.3107582786264297,-1.552073758272865,-0.48449409194705484,0.3107582786264297,-1.533588182397684,-0.5466437892069498,0.3107582786264297,-1.5125501003414406,-0.6078836518275285,0.3107582786264297,-1.4889945279154864,-0.6681117521011708,0.3107582786264297,-1.4629606710454444,-0.7272278462988331,0.3107582786264297,-1.4344918605168153,-0.7851335415158393,0.3107582786264297,-1.4036354798551616,-0.8417324594371676,0.3107582786264297,-1.3704428864609026,-0.8969303967496358,0.3107582786264297,-1.3349693261299955,-0.9506354819340276,0.3107582786264297,-1.2972738411027538,-1.0027583281761627,0.3107582786264297,-1.2574191717938696,-1.0532121821424316,0.3107582786264297,-1.2154716523671834,-1.1019130683721559,0.3107582786264297,-1.1715011003290157,-1.148779929046456,0.3107582786264297,-1.1255807003238165,-1.1937347589009912,0.3107582786264297,-1.0777868823255485,-1.2367027350580229,0.3107582786264297,-1.0281991944275386,-1.2776123415617124,0.3107582786264297,-0.9769001704425235,-1.316395488409368,0.3107582786264297,-0.923975192533271,-1.3529876248805395,0.3107582786264297,-0.8695123491023865,-1.3873278469753163,0.3107582786264297,-0.8136022881778705,-1.419358998783031,0.3107582786264297,-0.7563380665384131,-1.4490277676126269,0.3107582786264297,-0.6978149948295753,-1.4762847727263733,0.3107582786264297,-0.6381304789286233,-1.501084647529229,0.3107582786264297,-0.5773838578220591,-1.523386115077064,0.3107582786264297,-0.5156762382656815,-1.5431520567780577,0.3107582786264297,-0.4531103265023691,-1.560349574172934,0.3107582786264297,-0.3897902573176722,-1.5749500436911967,0.3107582786264297,-0.3258214207177468,-1.5869291642922416,0.3107582786264297,-0.26131028651808014,-1.5962669979120374,0.3107582786264297,-0.19636422713499832,-1.602948002648067,0.3107582786264297,-0.13109133887487207,-1.6069610586272898,0.3107582786264297,-0.06560026201848344,-1.6082994865140738,0.3107582786264297,-1.9695988182297923e-16,-1.6069610586272898,0.3107582786264297,0.06560026201848305,-1.602948002648067,0.3107582786264297,0.13109133887487165,-1.5962669979120374,0.3107582786264297,0.19636422713499793,-1.5869291642922418,0.3107582786264297,0.261310286518079,-1.574950043691197,0.3107582786264297,0.3258214207177457,-1.560349574172934,0.3107582786264297,0.38979025731767253,-1.543152056778058,0.3107582786264297,0.4531103265023687,-1.523386115077064,0.3107582786264297,0.5156762382656812,-1.5010846475292292,0.3107582786264297,0.5773838578220588,-1.4762847727263733,0.3107582786264297,0.6381304789286228,-1.4490277676126275,0.3107582786264297,0.6978149948295743,-1.4193589987830313,0.3107582786264297,0.7563380665384127,-1.3873278469753165,0.3107582786264297,0.8136022881778702,-1.3529876248805393,0.3107582786264297,0.8695123491023867,-1.3163954884093685,0.3107582786264297,0.9239751925332701,-1.2776123415617129,0.3107582786264297,0.9769001704425232,-1.236702735058023,0.3107582786264297,1.0281991944275382,-1.193734758900991,0.3107582786264297,1.0777868823255488,-1.1487799290464564,0.3107582786264297,1.1255807003238163,-1.101913068372156,0.3107582786264297,1.1715011003290154,-1.0532121821424323,0.3107582786264297,1.2154716523671827,-1.002758328176163,0.3107582786264297,1.2574191717938692,-0.9506354819340272,0.3107582786264297,1.297273841102754,-0.8969303967496363,0.3107582786264297,1.3349693261299953,-0.8417324594371679,0.3107582786264297,1.3704428864609024,-0.7851335415158395,0.3107582786264297,1.4036354798551613,-0.7272278462988322,0.3107582786264297,1.434491860516816,-0.6681117521011711,0.3107582786264297,1.4629606710454444,-0.6078836518275291,0.3107582786264297,1.488994527915486,-0.5466437892069494,0.3107582786264297,1.5125501003414406,-0.48449409194705556,0.3107582786264297,1.533588182397684,-0.4215380020854336,0.3107582786264297,1.5520737582728645,-0.3578803038205729,0.3107582786264297,1.567976060550265,-0.29362694910891135,0.3107582786264297,1.5812686214171285,-0.2288848813182581,0.3107582786264297,1.5919293167177242,-0.16376185723110862,0.3107582786264297,1.5999404027768196,-0.0983662676941176,0.3107582786264297,1.6052885459322765,-0.03280695721223462,0.3107582786264297,1.6079648447276165,0.03280695721223546,0.3107582786264297,1.6079648447276165,0.09836626769411701,0.3107582786264297,1.6052885459322765,0.16376185723110803,0.3107582786264297,1.5999404027768196,0.22888488131825752,0.3107582786264297,1.5919293167177244,0.2936269491089108,0.3107582786264297,1.5812686214171288,0.3578803038205723,0.3107582786264297,1.567976060550265,0.4215380020854331,0.3107582786264297,1.5520737582728648,0.48449409194705506,0.3107582786264297,1.533588182397684,0.5466437892069489,0.3107582786264297,1.5125501003414406,0.6078836518275273,0.3107582786264297,1.4889945279154866,0.6681117521011719,0.3107582786264297,1.4629606710454441,0.7272278462988329,0.3107582786264297,1.4344918605168153,0.785133541515839,0.3107582786264297,1.4036354798551616,0.8417324594371663,0.3107582786264297,1.3704428864609035,0.8969303967496357,0.3107582786264297,1.3349693261299955,0.9506354819340279,0.3107582786264297,1.2972738411027533,1.0027583281761625,0.3107582786264297,1.2574191717938696,1.0532121821424318,0.3107582786264297,1.2154716523671834,1.1019130683721554,0.3107582786264297,1.1715011003290159,1.1487799290464555,0.3107582786264297,1.1255807003238172,1.193734758900991,0.3107582786264297,1.0777868823255488,1.2367027350580237,0.3107582786264297,1.0281991944275375,1.2776123415617124,0.3107582786264297,0.9769001704425238,1.3163954884093678,0.3107582786264297,0.9239751925332711,1.3529876248805386,0.3107582786264297,0.8695123491023877,1.3873278469753163,0.3107582786264297,0.8136022881778707,1.4193589987830315,0.3107582786264297,0.7563380665384126,1.4490277676126269,0.3107582786264297,0.6978149948295754,1.4762847727263728,0.3107582786264297,0.6381304789286242,1.501084647529229,0.3107582786264297,0.5773838578220595,1.5233861150770638,0.3107582786264297,0.5156762382656824,1.543152056778058,0.3107582786264297,0.45311032650236865,1.5603495741729339,0.3107582786264297,0.3897902573176731,1.574950043691197,0.3107582786264297,0.3258214207177463,1.5869291642922416,0.3107582786264297,0.2613102865180803,1.5962669979120374,0.3107582786264297,0.19636422713499924,1.602948002648067,0.3107582786264297,0.13109133887487223,1.6069610586272898,0.3107582786264297,0.06560026201848293,1.6082994865140738,0.3107582786264297,0.0,1.5873546371535114,0.28235404109254353,0.0,1.5860336395840928,0.28235404109254353,-0.06474595122779293,1.5820728455459232,0.28235404109254353,-0.12938413920949113,1.5754788473897918,0.28235404109254353,-0.19380698006028785,1.566262620174873,0.28235404109254353,-0.25790724831942347,1.5544395034018248,0.28235404109254353,-0.3215782554163826,1.540029175481702,0.28235404109254353,-0.3847140272434908,1.5230556209831843,0.28235404109254353,-0.4472094805393586,1.5035470907126287,0.28235404109254353,-0.5089605977895993,1.48153605469339,0.28235404109254353,-0.5698646003537159,1.4570591481226716,0.28235404109254353,-0.6298201195300059,1.4301571103958546,0.28235404109254353,-0.6887273652737602,1.4008747172997957,0.28235404109254353,-0.7464882922879456,1.3692607064879487,0.28235404109254353,-0.8030067632099245,1.3353676963613492,0.28235404109254353,-0.8581887086226079,1.2992520984904803,0.28235404109254353,-0.9119422836237167,1.2609740237237796,0.28235404109254353,-0.9641780206925572,1.220597182139065,0.28235404109254353,-1.014808978599879,1.1781887770043977,0.28235404109254353,-1.0637508871129733,1.1338193929248739,0.28235404109254353,-1.1109222872551554,1.0875628783615168,0.28235404109254353,-1.1562446668861952,1.0394962227178,0.28235404109254353,-1.1996425913780244,0.9896994281983849,0.28235404109254353,-1.2410438291682313,0.9382553766533441,0.28235404109254353,-1.280379471982368,0.8852496916295042,0.28235404109254353,-1.3175840495249718,0.8307705958585004,0.28235404109254353,-1.3525956384484124,0.7749087644187471,0.28235404109254353,-1.3853559654181904,0.7177571738157189,0.28235404109254353,-1.4158105041031541,0.6594109472317307,0.28235404109254353,-1.4439085659291961,0.5999671962027915,0.28235404109254353,-1.4696033844453804,0.5395248589860352,0.28235404109254353,-1.4928521931620875,0.4781845358867546,0.28235404109254353,-1.5136162967316138,0.4160483218191233,0.28235404109254353,-1.53186113535276,0.353219636379286,0.28235404109254353,-1.5475563422922092,0.28980305171364656,0.28235404109254353,-1.5606757944269571,0.2259041184688515,0.28235404109254353,-1.5711976557236733,0.1616291901131537,0.28235404109254353,-1.5791044135826238,0.09708524592156247,0.28235404109254353,-1.5843829079856628,0.03237971291939671,0.28235404109254353,-1.5870243533997874,-0.032379712919396515,0.28235404109254353,-1.5870243533997874,-0.09708524592156226,0.28235404109254353,-1.584382907985663,-0.16162919011315316,0.28235404109254353,-1.5791044135826238,-0.22590411846885133,0.28235404109254353,-1.5711976557236733,-0.2898030517136464,0.28235404109254353,-1.5606757944269571,-0.3532196363792855,0.28235404109254353,-1.5475563422922092,-0.41604832181912316,0.28235404109254353,-1.5318611353527603,-0.4781845358867541,0.28235404109254353,-1.513616296731614,-0.5395248589860351,0.28235404109254353,-1.4928521931620877,-0.5999671962027914,0.28235404109254353,-1.4696033844453806,-0.6594109472317302,0.28235404109254353,-1.4439085659291961,-0.717757173815719,0.28235404109254353,-1.4158105041031541,-0.7749087644187471,0.28235404109254353,-1.3853559654181904,-0.8307705958585008,0.28235404109254353,-1.3525956384484121,-0.8852496916295038,0.28235404109254353,-1.3175840495249722,-0.9382553766533438,0.28235404109254353,-1.2803794719823682,-0.9896994281983846,0.28235404109254353,-1.2410438291682315,-1.0394962227178002,0.28235404109254353,-1.1996425913780244,-1.087562878361517,0.28235404109254353,-1.1562446668861952,-1.1338193929248739,0.28235404109254353,-1.1109222872551554,-1.1781887770043977,0.28235404109254353,-1.0637508871129733,-1.2205971821390647,0.28235404109254353,-1.0148089785998797,-1.2609740237237796,0.28235404109254353,-0.964178020692557,-1.29925209849048,0.28235404109254353,-0.9119422836237171,-1.3353676963613494,0.28235404109254353,-0.8581887086226077,-1.3692607064879485,0.28235404109254353,-0.8030067632099247,-1.4008747172997955,0.28235404109254353,-0.7464882922879458,-1.4301571103958544,0.28235404109254353,-0.6887273652737604,-1.4570591481226713,0.28235404109254353,-0.6298201195300059,-1.48153605469339,0.28235404109254353,-0.569864600353716,-1.5035470907126287,0.28235404109254353,-0.5089605977895993,-1.523055620983184,0.28235404109254353,-0.44720948053935927,-1.540029175481702,0.28235404109254353,-0.38471402724349063,-1.5544395034018246,0.28235404109254353,-0.321578255416383,-1.566262620174873,0.28235404109254353,-0.2579072483194239,-1.5754788473897918,0.28235404109254353,-0.19380698006028826,-1.5820728455459232,0.28235404109254353,-0.12938413920949146,-1.5860336395840928,0.28235404109254353,-0.0647459512277932,-1.5873546371535114,0.28235404109254353,-1.943948775501756e-16,-1.5860336395840928,0.28235404109254353,0.06474595122779281,-1.5820728455459232,0.28235404109254353,0.12938413920949104,-1.5754788473897918,0.28235404109254353,0.19380698006028788,-1.5662626201748733,0.28235404109254353,0.25790724831942285,-1.554439503401825,0.28235404109254353,0.32157825541638196,-1.540029175481702,0.28235404109254353,0.38471402724349096,-1.5230556209831843,0.28235404109254353,0.4472094805393589,-1.5035470907126287,0.28235404109254353,0.5089605977895989,-1.4815360546933902,0.28235404109254353,0.5698646003537157,-1.4570591481226716,0.28235404109254353,0.6298201195300056,-1.4301571103958548,0.28235404109254353,0.6887273652737594,-1.4008747172997957,0.28235404109254353,0.7464882922879454,-1.3692607064879487,0.28235404109254353,0.8030067632099244,-1.3353676963613492,0.28235404109254353,0.8581887086226079,-1.2992520984904805,0.28235404109254353,0.9119422836237162,-1.2609740237237799,0.28235404109254353,0.9641780206925566,-1.2205971821390649,0.28235404109254353,1.0148089785998793,-1.1781887770043975,0.28235404109254353,1.0637508871129735,-1.1338193929248743,0.28235404109254353,1.1109222872551552,-1.0875628783615172,0.28235404109254353,1.156244666886195,-1.0394962227178008,0.28235404109254353,1.1996425913780235,-0.9896994281983849,0.28235404109254353,1.2410438291682313,-0.9382553766533435,0.28235404109254353,1.2803794719823685,-0.8852496916295042,0.28235404109254353,1.3175840495249718,-0.8307705958585011,0.28235404109254353,1.352595638448412,-0.7749087644187473,0.28235404109254353,1.3853559654181904,-0.7177571738157181,0.28235404109254353,1.4158105041031548,-0.6594109472317305,0.28235404109254353,1.4439085659291961,-0.599967196202792,0.28235404109254353,1.4696033844453804,-0.5395248589860347,0.28235404109254353,1.4928521931620877,-0.47818453588675475,0.28235404109254353,1.5136162967316138,-0.4160483218191242,0.28235404109254353,1.5318611353527598,-0.3532196363792862,0.28235404109254353,1.5475563422922092,-0.28980305171364606,0.28235404109254353,1.5606757944269571,-0.22590411846885133,0.28235404109254353,1.5711976557236733,-0.16162919011315421,0.28235404109254353,1.5791044135826235,-0.0970852459215623,0.28235404109254353,1.584382907985663,-0.03237971291939585,0.28235404109254353,1.5870243533997874,0.032379712919396675,0.28235404109254353,1.5870243533997874,0.09708524592156172,0.28235404109254353,1.584382907985663,0.16162919011315366,0.28235404109254353,1.5791044135826238,0.22590411846885075,0.28235404109254353,1.5711976557236735,0.2898030517136455,0.28235404109254353,1.5606757944269574,0.35321963637928566,0.28235404109254353,1.5475563422922092,0.4160483218191237,0.28235404109254353,1.53186113535276,0.47818453588675425,0.28235404109254353,1.513616296731614,0.5395248589860343,0.28235404109254353,1.492852193162088,0.5999671962027902,0.28235404109254353,1.469603384445381,0.6594109472317313,0.28235404109254353,1.443908565929196,0.7177571738157188,0.28235404109254353,1.4158105041031541,0.7749087644187469,0.28235404109254353,1.3853559654181906,0.8307705958584995,0.28235404109254353,1.352595638448413,0.8852496916295037,0.28235404109254353,1.3175840495249722,0.9382553766533441,0.28235404109254353,1.2803794719823678,0.9896994281983844,0.28235404109254353,1.2410438291682315,1.0394962227178006,0.28235404109254353,1.1996425913780242,1.0875628783615165,0.28235404109254353,1.1562446668861954,1.1338193929248734,0.28235404109254353,1.110922287255156,1.1781887770043975,0.28235404109254353,1.0637508871129735,1.2205971821390653,0.28235404109254353,1.0148089785998786,1.2609740237237794,0.28235404109254353,0.9641780206925572,1.2992520984904798,0.28235404109254353,0.9119422836237173,1.3353676963613486,0.28235404109254353,0.8581887086226089,1.3692607064879485,0.28235404109254353,0.8030067632099248,1.400874717299796,0.28235404109254353,0.7464882922879453,1.4301571103958544,0.28235404109254353,0.6887273652737607,1.457059148122671,0.28235404109254353,0.6298201195300068,1.48153605469339,0.28235404109254353,0.5698646003537162,1.5035470907126285,0.28235404109254353,0.5089605977896002,1.5230556209831843,0.28235404109254353,0.44720948053935883,1.5400291754817017,0.28235404109254353,0.3847140272434915,1.5544395034018248,0.28235404109254353,0.3215782554163826,1.566262620174873,0.28235404109254353,0.2579072483194241,1.5754788473897918,0.28235404109254353,0.19380698006028915,1.5820728455459232,0.28235404109254353,0.12938413920949163,1.5860336395840928,0.28235404109254353,0.06474595122779268,1.5873546371535114,0.28235404109254353,0.0,1.568465566233958,0.2525431299287737,0.0,1.5671602881617388,0.2525431299287737,-0.06397549273296733,1.5632466264516158,0.2525431299287737,-0.12784450457196866,1.5567310950072697,0.2525431299287737,-0.1915007318499774,1.5476245382877771,0.2525431299287737,-0.2548382250588694,1.535942113258079,0.2525431299287737,-0.3177515651919227,1.5217032641617088,0.2525431299287737,-0.38013603920335093,1.5049316901577667,0.2525431299287737,-0.44188781429283375,1.4856553058760102,0.2525431299287737,-0.5029041107249659,1.4639061949557057,0.2525431299287737,-0.5630833728959834,1.4397205566455789,0.2525431299287737,-0.6223254383630441,1.4131386455537358,0.2525431299287737,-0.6805317045547276,1.3842047046478427,0.2525431299287737,-0.7376052928852861,1.3529668916170707,0.2525431299287737,-0.793451209999488,1.3194771987183762,0.2525431299287737,-0.8479765058796838,1.2837913662405187,0.2525431299287737,-0.9010904285519366,1.2459687897298528,0.2525431299287737,-0.9527045751337276,1.206072421132302,0.2525431299287737,-1.002733038971828,1.1641686640160573,0.2525431299287737,-1.051092552625447,1.1203272630493923,0.2525431299287737,-1.0977026264566683,1.0746211879175447,0.2525431299287737,-1.1424856825975087,1.0271265118718766,0.2525431299287737,-1.1853671840706244,0.9779222851134568,0.2525431299287737,-1.226275758848753,0.9270904032218,0.2525431299287737,-1.2651433186464096,0.8747154708477588,0.2525431299287737,-1.301905172246118,0.8208846608974314,0.2525431299287737,-1.3365001331705582,0.7656875694414627,0.2525431299287737,-1.3688706215214188,0.7092160665912265,0.2525431299287737,-1.3989627598154566,0.6515641435900908,0.2525431299287737,-1.4267264626582497,0.5928277563742712,0.2525431299287737,-1.4521155201063922,0.5331046658636437,0.2525431299287737,-1.4750876745793868,0.47249427524834053,0.2525431299287737,-1.4956046911932162,0.41109746454195234,0.2525431299287737,-1.5136324213985337,0.3490164226766999,0.2525431299287737,-1.5291408598175544,0.28635447742004166,0.2525431299287737,-1.5421041941850444,0.22321592339580262,0.2525431299287737,-1.5525008483102898,0.15970584849606417,0.2525431299287737,-1.5603135179885363,0.095929958972741,0.2525431299287737,-1.5655291998021303,0.03199440349995529,0.2525431299287737,-1.5681392127634244,-0.031994403499955096,0.2525431299287737,-1.5681392127634244,-0.0959299589727408,0.2525431299287737,-1.5655291998021303,-0.15970584849606365,0.2525431299287737,-1.5603135179885363,-0.22321592339580246,0.2525431299287737,-1.5525008483102898,-0.2863544774200415,0.2525431299287737,-1.5421041941850444,-0.34901642267669936,0.2525431299287737,-1.5291408598175544,-0.4110974645419521,0.2525431299287737,-1.513632421398534,-0.47249427524834,0.2525431299287737,-1.4956046911932164,-0.5331046658636435,0.2525431299287737,-1.475087674579387,-0.592827756374271,0.2525431299287737,-1.4521155201063924,-0.6515641435900904,0.2525431299287737,-1.4267264626582499,-0.7092160665912266,0.2525431299287737,-1.3989627598154566,-0.7656875694414625,0.2525431299287737,-1.3688706215214188,-0.8208846608974317,0.2525431299287737,-1.3365001331705577,-0.8747154708477584,0.2525431299287737,-1.3019051722461181,-0.9270904032217996,0.2525431299287737,-1.2651433186464098,-0.9779222851134567,0.2525431299287737,-1.2262757588487534,-1.0271265118718769,0.2525431299287737,-1.1853671840706244,-1.074621187917545,0.2525431299287737,-1.1424856825975087,-1.1203272630493923,0.2525431299287737,-1.0977026264566683,-1.1641686640160573,0.2525431299287737,-1.051092552625447,-1.2060724211323015,0.2525431299287737,-1.0027330389718285,-1.2459687897298528,0.2525431299287737,-0.9527045751337274,-1.2837913662405185,0.2525431299287737,-0.9010904285519371,-1.3194771987183764,0.2525431299287737,-0.8479765058796835,-1.3529668916170707,0.2525431299287737,-0.7934512099994883,-1.3842047046478425,0.2525431299287737,-0.7376052928852863,-1.4131386455537358,0.2525431299287737,-0.6805317045547279,-1.4397205566455786,0.2525431299287737,-0.6223254383630441,-1.4639061949557057,0.2525431299287737,-0.5630833728959835,-1.4856553058760102,0.2525431299287737,-0.5029041107249659,-1.5049316901577665,0.2525431299287737,-0.44188781429283436,-1.5217032641617088,0.2525431299287737,-0.38013603920335076,-1.5359421132580788,0.2525431299287737,-0.3177515651919232,-1.5476245382877771,0.2525431299287737,-0.2548382250588698,-1.5567310950072697,0.2525431299287737,-0.19150073184997782,-1.5632466264516158,0.2525431299287737,-0.12784450457196897,-1.5671602881617388,0.2525431299287737,-0.06397549273296758,-1.568465566233958,0.2525431299287737,-1.9208163352612575e-16,-1.5671602881617388,0.2525431299287737,0.06397549273296721,-1.5632466264516158,0.2525431299287737,0.12784450457196858,-1.5567310950072697,0.2525431299287737,0.19150073184997743,-1.5476245382877771,0.2525431299287737,0.2548382250588688,-1.5359421132580793,0.2525431299287737,0.31775156519192216,-1.5217032641617088,0.2525431299287737,0.3801360392033511,-1.5049316901577667,0.2525431299287737,0.44188781429283397,-1.4856553058760102,0.2525431299287737,0.5029041107249655,-1.463906194955706,0.2525431299287737,0.5630833728959832,-1.4397205566455789,0.2525431299287737,0.6223254383630438,-1.4131386455537363,0.2525431299287737,0.6805317045547269,-1.3842047046478427,0.2525431299287737,0.737605292885286,-1.3529668916170707,0.2525431299287737,0.7934512099994879,-1.3194771987183762,0.2525431299287737,0.8479765058796838,-1.283791366240519,0.2525431299287737,0.9010904285519362,-1.2459687897298533,0.2525431299287737,0.9527045751337271,-1.2060724211323017,0.2525431299287737,1.0027330389718283,-1.1641686640160573,0.2525431299287737,1.0510925526254473,-1.1203272630493928,0.2525431299287737,1.097702626456668,-1.0746211879175451,0.2525431299287737,1.1424856825975085,-1.0271265118718775,0.2525431299287737,1.1853671840706237,-0.9779222851134568,0.2525431299287737,1.226275758848753,-0.9270904032217993,0.2525431299287737,1.26514331864641,-0.8747154708477588,0.2525431299287737,1.301905172246118,-0.8208846608974321,0.2525431299287737,1.3365001331705577,-0.7656875694414628,0.2525431299287737,1.3688706215214188,-0.7092160665912257,0.2525431299287737,1.3989627598154573,-0.6515641435900907,0.2525431299287737,1.4267264626582499,-0.5928277563742718,0.2525431299287737,1.452115520106392,-0.5331046658636432,0.2525431299287737,1.475087674579387,-0.4724942752483407,0.2525431299287737,1.4956046911932162,-0.4110974645419532,0.2525431299287737,1.5136324213985335,-0.34901642267670013,0.2525431299287737,1.5291408598175544,-0.2863544774200412,0.2525431299287737,1.5421041941850444,-0.22321592339580246,0.2525431299287737,1.5525008483102898,-0.15970584849606473,0.2525431299287737,1.5603135179885361,-0.09592995897274083,0.2525431299287737,1.5655291998021303,-0.03199440349995444,0.2525431299287737,1.5681392127634244,0.031994403499955255,0.2525431299287737,1.5681392127634244,0.09592995897274026,0.2525431299287737,1.5655291998021303,0.15970584849606415,0.2525431299287737,1.5603135179885363,0.2232159233958019,0.2525431299287737,1.55250084831029,0.2863544774200406,0.2525431299287737,1.5421041941850446,0.3490164226766996,0.2525431299287737,1.5291408598175544,0.4110974645419527,0.2525431299287737,1.5136324213985337,0.47249427524834015,0.2525431299287737,1.4956046911932164,0.5331046658636427,0.2525431299287737,1.4750876745793873,0.5928277563742699,0.2525431299287737,1.4521155201063927,0.6515641435900914,0.2525431299287737,1.4267264626582494,0.7092160665912264,0.2525431299287737,1.3989627598154566,0.7656875694414623,0.2525431299287737,1.368870621521419,0.8208846608974305,0.2525431299287737,1.3365001331705586,0.8747154708477582,0.2525431299287737,1.3019051722461181,0.9270904032218,0.2525431299287737,1.2651433186464094,0.9779222851134565,0.2525431299287737,1.2262757588487534,1.0271265118718773,0.2525431299287737,1.1853671840706241,1.0746211879175445,0.2525431299287737,1.142485682597509,1.120327263049392,0.2525431299287737,1.097702626456669,1.1641686640160573,0.2525431299287737,1.0510925526254473,1.2060724211323022,0.2525431299287737,1.0027330389718274,1.2459687897298526,0.2525431299287737,0.9527045751337276,1.2837913662405183,0.2525431299287737,0.9010904285519372,1.3194771987183755,0.2525431299287737,0.8479765058796848,1.3529668916170707,0.2525431299287737,0.7934512099994885,1.384204704647843,0.2525431299287737,0.7376052928852859,1.4131386455537358,0.2525431299287737,0.680531704554728,1.4397205566455784,0.2525431299287737,0.622325438363045,1.4639061949557057,0.2525431299287737,0.5630833728959838,1.48565530587601,0.2525431299287737,0.5029041107249668,1.5049316901577667,0.2525431299287737,0.4418878142928339,1.5217032641617085,0.2525431299287737,0.38013603920335165,1.535942113258079,0.2525431299287737,0.3177515651919227,1.5476245382877771,0.2525431299287737,0.25483822505887,1.5567310950072697,0.2525431299287737,0.1915007318499787,1.5632466264516158,0.2525431299287737,0.12784450457196916,1.5671602881617388,0.2525431299287737,0.06397549273296708,1.568465566233958,0.2525431299287737,0.0,1.5517263781565858,0.22147406159500418,0.0,1.5504350304476557,0.22147406159500418,-0.06329272491947238,1.5465631366416854,0.22147406159500418,-0.1264801053446225,1.5401171411238006,0.22147406159500418,-0.18945697211664259,1.5311077726173912,0.22147406159500418,-0.25211850645592504,1.5195500263272088,0.22147406159500418,-0.31436041442257673,1.5054631389813293,0.22147406159500418,-0.376079100503389,1.4888705568135165,0.22147406159500418,-0.43717184003634607,1.469799896539281,0.22147406159500418,-0.4975369501856869,1.4482828993905812,0.22147406159500418,-0.5570739591829501,1.424355378285677,0.22147406159500418,-0.6156837735523177,1.3980571582220602,0.22147406159500418,-0.6732688430419254,1.3694320099916775,0.22147406159500418,-0.7297333229866302,1.3385275773287661,0.22147406159500418,-0.7849832338319968,1.3053952976115613,0.22147406159500418,-0.8389266175539902,1.2700903162498547,0.22147406159500418,-0.891473690714033,1.2326713949009038,0.22147406159500418,-0.9425369938946776,1.1932008136664523,0.22147406159500418,-0.9920315372671736,1.1517442674336456,0.22147406159500418,-1.0398749420486535,1.1083707565323766,0.22147406159500418,-1.0859875776134837,1.0631524718910446,0.22147406159500418,-1.1302926940305846,1.0161646748818807,0.22147406159500418,-1.1727165498061145,0.96748557205582,0.22147406159500418,-1.21318853461891,0.9171961849754163,0.22147406159500418,-1.2516412868443967,0.8653802153624472,0.22147406159500418,-1.2880108056713653,0.8121239057846588,0.22147406159500418,-1.3222365576250061,0.7575158961135224,0.22147406159500418,-1.3542615773189028,0.7016470759919176,0.22147406159500418,-1.384032562268297,0.6446104335572882,0.22147406159500418,-1.411499961606815,0.5865009006720687,0.22147406159500418,-1.436618058558992,0.5274151949189656,0.22147406159500418,-1.4593450465313351,0.46745165862408505,0.22147406159500418,-1.4796430986952722,0.4067100951758389,0.22147406159500418,-1.497478430946174,0.34529160291205263,0.22147406159500418,-1.5128213581336631,0.28329840685175944,0.22147406159500418,-1.5256463434696164,0.22083368855174532,0.22147406159500418,-1.535932041031629,0.1580014143710289,0.22147406159500418,-1.5436613312911969,0.09490616242911973,0.22147406159500418,-1.548821349607479,0.03165294854605724,0.22147406159500418,-1.5514035076392267,-0.031652948546057054,0.22147406159500418,-1.5514035076392267,-0.09490616242911953,0.22147406159500418,-1.5488213496074792,-0.15800141437102838,0.22147406159500418,-1.5436613312911969,-0.22083368855174515,0.22147406159500418,-1.535932041031629,-0.2832984068517593,0.22147406159500418,-1.5256463434696164,-0.34529160291205213,0.22147406159500418,-1.5128213581336631,-0.4067100951758387,0.22147406159500418,-1.4974784309461742,-0.4674516586240845,0.22147406159500418,-1.4796430986952724,-0.5274151949189654,0.22147406159500418,-1.4593450465313353,-0.5865009006720686,0.22147406159500418,-1.4366180585589923,-0.6446104335572878,0.22147406159500418,-1.4114999616068151,-0.7016470759919177,0.22147406159500418,-1.384032562268297,-0.7575158961135223,0.22147406159500418,-1.3542615773189028,-0.8121239057846591,0.22147406159500418,-1.322236557625006,-0.8653802153624469,0.22147406159500418,-1.2880108056713657,-0.917196184975416,0.22147406159500418,-1.251641286844397,-0.9674855720558198,0.22147406159500418,-1.2131885346189102,-1.0161646748818807,0.22147406159500418,-1.1727165498061145,-1.0631524718910448,0.22147406159500418,-1.1302926940305846,-1.1083707565323766,0.22147406159500418,-1.0859875776134837,-1.1517442674336456,0.22147406159500418,-1.0398749420486535,-1.1932008136664518,0.22147406159500418,-0.9920315372671742,-1.2326713949009038,0.22147406159500418,-0.9425369938946774,-1.2700903162498545,0.22147406159500418,-0.8914736907140336,-1.3053952976115615,0.22147406159500418,-0.83892661755399,-1.338527577328766,0.22147406159500418,-0.784983233831997,-1.3694320099916772,0.22147406159500418,-0.7297333229866304,-1.39805715822206,0.22147406159500418,-0.6732688430419256,-1.4243553782856768,0.22147406159500418,-0.6156837735523177,-1.4482828993905812,0.22147406159500418,-0.5570739591829502,-1.469799896539281,0.22147406159500418,-0.4975369501856869,-1.4888705568135163,0.22147406159500418,-0.4371718400363467,-1.5054631389813293,0.22147406159500418,-0.3760791005033889,-1.5195500263272088,0.22147406159500418,-0.3143604144225772,-1.5311077726173912,0.22147406159500418,-0.2521185064559255,-1.5401171411238006,0.22147406159500418,-0.18945697211664297,-1.5465631366416854,0.22147406159500418,-0.12648010534462284,-1.5504350304476557,0.22147406159500418,-0.06329272491947262,-1.5517263781565858,0.22147406159500418,-1.900316742161978e-16,-1.5504350304476557,0.22147406159500418,0.06329272491947224,-1.5465631366416854,0.22147406159500418,0.12648010534462245,-1.5401171411238006,0.22147406159500418,0.18945697211664259,-1.5311077726173914,0.22147406159500418,0.25211850645592443,-1.519550026327209,0.22147406159500418,0.3143604144225762,-1.5054631389813293,0.22147406159500418,0.37607910050338916,-1.4888705568135165,0.22147406159500418,0.43717184003634635,-1.469799896539281,0.22147406159500418,0.49753695018568656,-1.4482828993905812,0.22147406159500418,0.5570739591829499,-1.424355378285677,0.22147406159500418,0.6156837735523174,-1.3980571582220607,0.22147406159500418,0.6732688430419246,-1.3694320099916775,0.22147406159500418,0.7297333229866301,-1.3385275773287661,0.22147406159500418,0.7849832338319965,-1.3053952976115613,0.22147406159500418,0.8389266175539902,-1.270090316249855,0.22147406159500418,0.8914736907140327,-1.2326713949009043,0.22147406159500418,0.942536993894677,-1.193200813666452,0.22147406159500418,0.9920315372671739,-1.1517442674336453,0.22147406159500418,1.0398749420486537,-1.1083707565323768,0.22147406159500418,1.0859875776134837,-1.063152471891045,0.22147406159500418,1.1302926940305844,-1.0161646748818816,0.22147406159500418,1.1727165498061138,-0.96748557205582,0.22147406159500418,1.21318853461891,-0.9171961849754157,0.22147406159500418,1.2516412868443971,-0.8653802153624472,0.22147406159500418,1.2880108056713653,-0.8121239057846594,0.22147406159500418,1.3222365576250057,-0.7575158961135227,0.22147406159500418,1.3542615773189026,-0.7016470759919168,0.22147406159500418,1.3840325622682976,-0.6446104335572881,0.22147406159500418,1.4114999616068151,-0.5865009006720693,0.22147406159500418,1.4366180585589918,-0.5274151949189652,0.22147406159500418,1.4593450465313353,-0.4674516586240852,0.22147406159500418,1.4796430986952722,-0.4067100951758398,0.22147406159500418,1.497478430946174,-0.34529160291205285,0.22147406159500418,1.5128213581336631,-0.283298406851759,0.22147406159500418,1.5256463434696164,-0.22083368855174515,0.22147406159500418,1.535932041031629,-0.15800141437102944,0.22147406159500418,1.5436613312911966,-0.09490616242911956,0.22147406159500418,1.5488213496074792,-0.0316529485460564,0.22147406159500418,1.5514035076392267,0.031652948546057214,0.22147406159500418,1.5514035076392267,0.09490616242911899,0.22147406159500418,1.5488213496074792,0.15800141437102888,0.22147406159500418,1.5436613312911969,0.22083368855174457,0.22147406159500418,1.5359320410316293,0.28329840685175844,0.22147406159500418,1.5256463434696166,0.3452916029120523,0.22147406159500418,1.5128213581336631,0.4067100951758393,0.22147406159500418,1.497478430946174,0.4674516586240847,0.22147406159500418,1.4796430986952724,0.5274151949189646,0.22147406159500418,1.4593450465313356,0.5865009006720675,0.22147406159500418,1.4366180585589925,0.6446104335572889,0.22147406159500418,1.411499961606815,0.7016470759919174,0.22147406159500418,1.384032562268297,0.7575158961135222,0.22147406159500418,1.3542615773189028,0.8121239057846579,0.22147406159500418,1.3222365576250068,0.8653802153624467,0.22147406159500418,1.2880108056713657,0.9171961849754163,0.22147406159500418,1.2516412868443967,0.9674855720558196,0.22147406159500418,1.2131885346189102,1.0161646748818811,0.22147406159500418,1.1727165498061143,1.0631524718910446,0.22147406159500418,1.1302926940305849,1.108370756532376,0.22147406159500418,1.0859875776134846,1.1517442674336453,0.22147406159500418,1.0398749420486537,1.1932008136664525,0.22147406159500418,0.9920315372671732,1.2326713949009038,0.22147406159500418,0.9425369938946776,1.2700903162498542,0.22147406159500418,0.8914736907140338,1.3053952976115606,0.22147406159500418,0.8389266175539912,1.338527577328766,0.22147406159500418,0.7849832338319971,1.3694320099916777,0.22147406159500418,0.72973332298663,1.39805715822206,0.22147406159500418,0.6732688430419257,1.4243553782856764,0.22147406159500418,0.6156837735523186,1.4482828993905812,0.22147406159500418,0.5570739591829504,1.4697998965392807,0.22147406159500418,0.4975369501856878,1.4888705568135165,0.22147406159500418,0.43717184003634624,1.505463138981329,0.22147406159500418,0.3760791005033897,1.5195500263272088,0.22147406159500418,0.31436041442257673,1.5311077726173912,0.22147406159500418,0.25211850645592565,1.5401171411238006,0.22147406159500418,0.18945697211664383,1.5465631366416854,0.22147406159500418,0.126480105344623,1.5504350304476557,0.22147406159500418,0.06329272491947213,1.5517263781565858,0.22147406159500418,0.0,1.5372204667143894,0.18930162062685177,0.0,1.5359411908344691,0.18930162062685177,-0.06270104930221061,1.5321054924231594,0.18930162062685177,-0.12529773889577192,1.5257197556219244,0.18930162062685177,-0.18768588276847056,1.5167946088594666,0.18930162062685177,-0.24976164201186427,1.5053449071617568,0.18930162062685177,-0.31142169765089733,1.4913897074273093,0.18930162062685177,-0.37256342260813796,1.474952236708855,0.18930162062685177,-0.43308505251642077,1.4560598535542046,0.18930162062685177,-0.4928858550955925,1.4347440024706455,0.18930162062685177,-0.5518662978114515,1.4110401615886627,0.18930162062685177,-0.6099282135378279,1.3849877836120918,0.18930162062685177,-0.666974963946077,1.3566302301529876,0.18930162062685177,-0.7229116003500408,1.3260146995604993,0.18930162062685177,-0.7776450217387647,1.2931921483638773,0.18930162062685177,-0.8310841297339426,1.2582172064603578,0.18930162062685177,-0.8831399802141779,1.2211480861890913,0.18930162062685177,-0.9337259313536949,1.182046485442449,0.18930162062685177,-0.9827577878291066,1.1409774849759693,0.18930162062685177,-1.030153940954223,1.0980094400878633,0.18930162062685177,-1.0758355045096542,1.0532138668483666,0.18930162062685177,-1.1197264460411376,1.0066653230682985,0.18930162062685177,-1.161753713408054,0.9584412842049446,0.18930162062685177,-1.2018473563715075,0.9086220144118022,0.18930162062685177,-1.2399406430195938,0.8572904329468197,0.18930162062685177,-1.2759701708360816,0.8045319761614748,0.18930162062685177,-1.3098759722276447,0.7504344553003987,0.18930162062685177,-1.3416016143340015,0.6950879103482259,0.18930162062685177,-1.3710942929548433,0.638584460166924,0.18930162062685177,-1.398304920437214,0.581018149173042,0.18930162062685177,-1.423188207377063,0.5224847908100582,0.18930162062685177,-1.4457027379989895,0.4630818080763588,0.18930162062685177,-1.465811039088714,0.40290807137427365,0.18930162062685177,-1.483479642363544,0.3420637339500475,0.18930162062685177,-1.498679140177028,0.28065006519864605,0.18930162062685177,-1.5113842344650816,0.21876928211084254,0.18930162062685177,-1.521573778852117,0.15652437914312312,0.18930162062685177,-1.5292308138471005,0.09401895679358042,0.18930162062685177,-1.5343425950709482,0.031357049169107185,0.18930162062685177,-1.5369006144682913,-0.031357049169107,0.18930162062685177,-1.5369006144682913,-0.09401895679358023,0.18930162062685177,-1.5343425950709484,-0.15652437914312262,0.18930162062685177,-1.5292308138471005,-0.21876928211084237,0.18930162062685177,-1.521573778852117,-0.2806500651986458,0.18930162062685177,-1.5113842344650816,-0.342063733950047,0.18930162062685177,-1.498679140177028,-0.4029080713742735,0.18930162062685177,-1.4834796423635441,-0.4630818080763583,0.18930162062685177,-1.4658110390887142,-0.5224847908100579,0.18930162062685177,-1.4457027379989895,-0.5810181491730418,0.18930162062685177,-1.423188207377063,-0.6385844601669236,0.18930162062685177,-1.3983049204372142,-0.6950879103482259,0.18930162062685177,-1.3710942929548433,-0.7504344553003987,0.18930162062685177,-1.3416016143340015,-0.8045319761614751,0.18930162062685177,-1.3098759722276445,-0.8572904329468194,0.18930162062685177,-1.275970170836082,-0.9086220144118019,0.18930162062685177,-1.239940643019594,-0.9584412842049445,0.18930162062685177,-1.201847356371508,-1.0066653230682987,0.18930162062685177,-1.161753713408054,-1.0532138668483666,0.18930162062685177,-1.1197264460411376,-1.0980094400878633,0.18930162062685177,-1.0758355045096542,-1.1409774849759693,0.18930162062685177,-1.030153940954223,-1.1820464854424486,0.18930162062685177,-0.982757787829107,-1.2211480861890913,0.18930162062685177,-0.9337259313536947,-1.2582172064603574,0.18930162062685177,-0.8831399802141785,-1.2931921483638775,0.18930162062685177,-0.8310841297339425,-1.326014699560499,0.18930162062685177,-0.7776450217387648,-1.3566302301529873,0.18930162062685177,-0.722911600350041,-1.3849877836120916,0.18930162062685177,-0.6669749639460772,-1.4110401615886625,0.18930162062685177,-0.6099282135378279,-1.4347440024706455,0.18930162062685177,-0.5518662978114515,-1.4560598535542046,0.18930162062685177,-0.4928858550955925,-1.4749522367088548,0.18930162062685177,-0.4330850525164214,-1.4913897074273093,0.18930162062685177,-0.37256342260813785,-1.5053449071617566,0.18930162062685177,-0.31142169765089783,-1.5167946088594666,0.18930162062685177,-0.2497616420118647,-1.5257197556219244,0.18930162062685177,-0.18768588276847092,-1.5321054924231594,0.18930162062685177,-0.12529773889577225,-1.5359411908344691,0.18930162062685177,-0.06270104930221086,-1.5372204667143894,0.18930162062685177,-1.8825521241455774e-16,-1.5359411908344691,0.18930162062685177,0.06270104930221049,-1.5321054924231594,0.18930162062685177,0.12529773889577187,-1.5257197556219244,0.18930162062685177,0.18768588276847056,-1.5167946088594668,0.18930162062685177,0.24976164201186368,-1.505344907161757,0.18930162062685177,0.3114216976508968,-1.4913897074273093,0.18930162062685177,0.37256342260813813,-1.474952236708855,0.18930162062685177,0.433085052516421,-1.4560598535542046,0.18930162062685177,0.4928858550955922,-1.4347440024706457,0.18930162062685177,0.5518662978114511,-1.4110401615886627,0.18930162062685177,0.6099282135378276,-1.384987783612092,0.18930162062685177,0.6669749639460762,-1.3566302301529876,0.18930162062685177,0.7229116003500407,-1.3260146995604993,0.18930162062685177,0.7776450217387645,-1.2931921483638773,0.18930162062685177,0.8310841297339426,-1.258217206460358,0.18930162062685177,0.8831399802141776,-1.2211480861890918,0.18930162062685177,0.9337259313536944,-1.1820464854424488,0.18930162062685177,0.9827577878291067,-1.140977484975969,0.18930162062685177,1.030153940954223,-1.0980094400878635,0.18930162062685177,1.075835504509654,-1.0532138668483668,0.18930162062685177,1.1197264460411374,-1.0066653230682994,0.18930162062685177,1.1617537134080533,-0.9584412842049446,0.18930162062685177,1.2018473563715075,-0.9086220144118015,0.18930162062685177,1.2399406430195943,-0.8572904329468197,0.18930162062685177,1.2759701708360816,-0.8045319761614754,0.18930162062685177,1.3098759722276443,-0.7504344553003989,0.18930162062685177,1.3416016143340015,-0.6950879103482251,0.18930162062685177,1.3710942929548438,-0.6385844601669239,0.18930162062685177,1.3983049204372142,-0.5810181491730425,0.18930162062685177,1.4231882073770628,-0.5224847908100577,0.18930162062685177,1.4457027379989895,-0.463081808076359,0.18930162062685177,1.465811039088714,-0.40290807137427453,0.18930162062685177,1.4834796423635437,-0.34206373395004774,0.18930162062685177,1.498679140177028,-0.28065006519864555,0.18930162062685177,1.5113842344650816,-0.21876928211084237,0.18930162062685177,1.521573778852117,-0.15652437914312367,0.18930162062685177,1.5292308138471002,-0.09401895679358026,0.18930162062685177,1.5343425950709484,-0.03135704916910635,0.18930162062685177,1.5369006144682913,0.03135704916910715,0.18930162062685177,1.5369006144682913,0.09401895679357969,0.18930162062685177,1.5343425950709484,0.15652437914312312,0.18930162062685177,1.5292308138471005,0.21876928211084182,0.18930162062685177,1.5215737788521173,0.280650065198645,0.18930162062685177,1.5113842344650819,0.3420637339500472,0.18930162062685177,1.498679140177028,0.402908071374274,0.18930162062685177,1.483479642363544,0.46308180807635846,0.18930162062685177,1.4658110390887142,0.5224847908100573,0.18930162062685177,1.4457027379989897,0.5810181491730407,0.18930162062685177,1.4231882073770634,0.6385844601669246,0.18930162062685177,1.3983049204372138,0.6950879103482258,0.18930162062685177,1.3710942929548433,0.7504344553003984,0.18930162062685177,1.3416016143340017,0.804531976161474,0.18930162062685177,1.3098759722276452,0.8572904329468192,0.18930162062685177,1.275970170836082,0.9086220144118022,0.18930162062685177,1.2399406430195938,0.9584412842049442,0.18930162062685177,1.201847356371508,1.0066653230682991,0.18930162062685177,1.1617537134080538,1.0532138668483664,0.18930162062685177,1.1197264460411376,1.0980094400878628,0.18930162062685177,1.0758355045096548,1.140977484975969,0.18930162062685177,1.030153940954223,1.1820464854424493,0.18930162062685177,0.982757787829106,1.221148086189091,0.18930162062685177,0.9337259313536949,1.2582172064603574,0.18930162062685177,0.8831399802141786,1.2931921483638766,0.18930162062685177,0.8310841297339437,1.326014699560499,0.18930162062685177,0.777645021738765,1.3566302301529878,0.18930162062685177,0.7229116003500405,1.3849877836120916,0.18930162062685177,0.6669749639460774,1.411040161588662,0.18930162062685177,0.6099282135378288,1.4347440024706455,0.18930162062685177,0.5518662978114518,1.4560598535542042,0.18930162062685177,0.49288585509559335,1.474952236708855,0.18930162062685177,0.43308505251642093,1.491389707427309,0.18930162062685177,0.3725634226081387,1.5053449071617568,0.18930162062685177,0.31142169765089733,1.5167946088594666,0.18930162062685177,0.24976164201186488,1.5257197556219244,0.18930162062685177,0.1876858827684718,1.5321054924231594,0.18930162062685177,0.12529773889577242,1.5359411908344691,0.18930162062685177,0.06270104930221036,1.5372204667143894,0.18930162062685177,0.0,1.5250200996285046,0.15618608850689708,0.0,1.5237509769020694,0.15618608850689708,-0.062203413579345064,1.5199457210522935,0.15618608850689708,-0.12430329571559029,1.5136106655520012,0.15618608850689708,-0.18619628728350546,1.5047563544758715,0.15618608850689708,-0.24777937350679352,1.4933975249508662,0.15618608850689708,-0.3089500554160192,1.4795530826277359,0.15618608850689708,-0.3696065204480281,1.463246070214431,0.15618608850689708,-0.4296478119029104,1.4445036291237883,0.15618608850689708,-0.4889739969764645,1.423356954299327,0.15618608850689708,-0.547486333088487,1.399841242294345,0.15618608850689708,-0.6050874322300533,1.3739956326907283,0.15618608850689708,-0.6616814230562466,1.3458631429549806,0.15618608850689708,-0.7171741104545504,1.315490596839895,0.15618608850689708,-0.7714731323233172,1.2829285464510394,0.15618608850689708,-0.8244881132993716,1.248231188107764,0.15618608850689708,-0.8761308151788831,1.2114562721387774,0.15618608850689708,-0.9263152837811491,1.172665006762425,0.15618608850689708,-0.9749579920108435,1.1319219562116474,0.15618608850689708,-1.021977978880627,1.0892949332731894,0.15618608850689708,-1.0672969842627178,1.044854886419906,0.15618608850689708,-1.1108395791451522,0.9986757817240342,0.15618608850689708,-1.152533291175931,0.9508344797479625,0.15618608850689708,-1.192308725286096,0.9014106076174119,0.15618608850689708,-1.2300996791909762,0.8504864264899427,0.15618608850689708,-1.265843253577354,0.7981466946393767,0.15618608850689708,-1.2994799567931672,0.7444785263840151,0.15618608850689708,-1.330953803865491,0.6895712470934524,0.15618608850689708,-1.3602124096820007,0.6335162445153134,0.15618608850689708,-1.387207076180823,0.5764068166693683,0.15618608850689708,-1.4118928734036513,0.5183380165621853,0.15618608850689708,-1.4342287142772292,0.45940649398078054,0.15618608850689708,-1.4541774229987279,0.3997103346285884,0.15618608850689708,-1.471705796911202,0.33934889687148656,0.15618608850689708,-1.4867846617661347,0.2784226463656016,0.15618608850689708,-1.4993889202810955,0.21703298884214012,0.15618608850689708,-1.5094975939116901,0.15528210132755516,0.15618608850689708,-1.517093857768277,0.09327276207997155,0.15618608850689708,-1.5221650686193358,0.03110817952491689,0.15618608850689708,-1.5247027859348783,-0.031108179524916707,0.15618608850689708,-1.5247027859348783,-0.09327276207997137,0.15618608850689708,-1.5221650686193358,-0.15528210132755466,0.15618608850689708,-1.517093857768277,-0.21703298884213995,0.15618608850689708,-1.5094975939116901,-0.27842264636560143,0.15618608850689708,-1.4993889202810955,-0.33934889687148606,0.15618608850689708,-1.4867846617661347,-0.39971033462858824,0.15618608850689708,-1.471705796911202,-0.45940649398078004,0.15618608850689708,-1.454177422998728,-0.518338016562185,0.15618608850689708,-1.4342287142772292,-0.5764068166693681,0.15618608850689708,-1.4118928734036515,-0.633516244515313,0.15618608850689708,-1.387207076180823,-0.6895712470934525,0.15618608850689708,-1.3602124096820007,-0.744478526384015,0.15618608850689708,-1.330953803865491,-0.7981466946393772,0.15618608850689708,-1.299479956793167,-0.8504864264899423,0.15618608850689708,-1.2658432535773543,-0.9014106076174114,0.15618608850689708,-1.2300996791909764,-0.9508344797479623,0.15618608850689708,-1.1923087252860964,-0.9986757817240344,0.15618608850689708,-1.152533291175931,-1.0448548864199063,0.15618608850689708,-1.1108395791451522,-1.0892949332731894,0.15618608850689708,-1.0672969842627178,-1.1319219562116474,0.15618608850689708,-1.021977978880627,-1.1726650067624245,0.15618608850689708,-0.9749579920108441,-1.2114562721387774,0.15618608850689708,-0.9263152837811489,-1.2482311881077635,0.15618608850689708,-0.8761308151788837,-1.2829285464510396,0.15618608850689708,-0.8244881132993713,-1.315490596839895,0.15618608850689708,-0.7714731323233174,-1.3458631429549806,0.15618608850689708,-0.7171741104545505,-1.373995632690728,0.15618608850689708,-0.6616814230562468,-1.3998412422943447,0.15618608850689708,-0.6050874322300535,-1.423356954299327,0.15618608850689708,-0.547486333088487,-1.4445036291237883,0.15618608850689708,-0.4889739969764645,-1.4632460702144308,0.15618608850689708,-0.429647811902911,-1.4795530826277359,0.15618608850689708,-0.36960652044802794,-1.493397524950866,0.15618608850689708,-0.30895005541601966,-1.5047563544758715,0.15618608850689708,-0.24777937350679397,-1.5136106655520012,0.15618608850689708,-0.18619628728350585,-1.5199457210522935,0.15618608850689708,-0.12430329571559061,-1.5237509769020694,0.15618608850689708,-0.06220341357934531,-1.5250200996285046,0.15618608850689708,-1.867610983645426e-16,-1.5237509769020694,0.15618608850689708,0.06220341357934493,-1.5199457210522935,0.15618608850689708,0.12430329571559023,-1.5136106655520012,0.15618608850689708,0.18619628728350548,-1.5047563544758717,0.15618608850689708,0.24777937350679294,-1.4933975249508662,0.15618608850689708,0.30895005541601867,-1.4795530826277359,0.15618608850689708,0.3696065204480283,-1.463246070214431,0.15618608850689708,0.4296478119029107,-1.4445036291237883,0.15618608850689708,0.48897399697646415,-1.4233569542993272,0.15618608850689708,0.5474863330884867,-1.399841242294345,0.15618608850689708,0.6050874322300531,-1.3739956326907286,0.15618608850689708,0.6616814230562458,-1.3458631429549806,0.15618608850689708,0.7171741104545502,-1.315490596839895,0.15618608850689708,0.771473132323317,-1.2829285464510394,0.15618608850689708,0.8244881132993716,-1.2482311881077641,0.15618608850689708,0.8761308151788828,-1.2114562721387778,0.15618608850689708,0.9263152837811486,-1.1726650067624247,0.15618608850689708,0.9749579920108438,-1.1319219562116474,0.15618608850689708,1.0219779788806271,-1.0892949332731896,0.15618608850689708,1.0672969842627176,-1.0448548864199065,0.15618608850689708,1.1108395791451522,-0.998675781724035,0.15618608850689708,1.1525332911759303,-0.9508344797479625,0.15618608850689708,1.192308725286096,-0.9014106076174111,0.15618608850689708,1.2300996791909766,-0.8504864264899427,0.15618608850689708,1.265843253577354,-0.7981466946393775,0.15618608850689708,1.2994799567931667,-0.7444785263840152,0.15618608850689708,1.3309538038654907,-0.6895712470934516,0.15618608850689708,1.3602124096820012,-0.6335162445153133,0.15618608850689708,1.387207076180823,-0.5764068166693688,0.15618608850689708,1.4118928734036513,-0.5183380165621848,0.15618608850689708,1.4342287142772292,-0.4594064939807807,0.15618608850689708,1.4541774229987279,-0.3997103346285893,0.15618608850689708,1.4717057969112017,-0.3393488968714868,0.15618608850689708,1.4867846617661347,-0.2784226463656011,0.15618608850689708,1.4993889202810955,-0.21703298884213995,0.15618608850689708,1.5094975939116901,-0.1552821013275557,0.15618608850689708,1.5170938577682769,-0.0932727620799714,0.15618608850689708,1.5221650686193358,-0.031108179524916065,0.15618608850689708,1.5247027859348783,0.03110817952491686,0.15618608850689708,1.5247027859348783,0.09327276207997084,0.15618608850689708,1.5221650686193358,0.15528210132755516,0.15618608850689708,1.517093857768277,0.2170329888421394,0.15618608850689708,1.5094975939116901,0.2784226463656006,0.15618608850689708,1.4993889202810957,0.3393488968714862,0.15618608850689708,1.4867846617661347,0.3997103346285888,0.15618608850689708,1.471705796911202,0.4594064939807802,0.15618608850689708,1.454177422998728,0.5183380165621843,0.15618608850689708,1.4342287142772294,0.576406816669367,0.15618608850689708,1.411892873403652,0.633516244515314,0.15618608850689708,1.3872070761808228,0.6895712470934524,0.15618608850689708,1.3602124096820007,0.7444785263840149,0.15618608850689708,1.330953803865491,0.7981466946393759,0.15618608850689708,1.2994799567931679,0.8504864264899421,0.15618608850689708,1.2658432535773543,0.9014106076174119,0.15618608850689708,1.2300996791909762,0.9508344797479622,0.15618608850689708,1.1923087252860964,0.9986757817240347,0.15618608850689708,1.1525332911759307,1.044854886419906,0.15618608850689708,1.1108395791451524,1.0892949332731887,0.15618608850689708,1.0672969842627185,1.1319219562116474,0.15618608850689708,1.0219779788806271,1.1726650067624251,0.15618608850689708,0.9749579920108431,1.2114562721387774,0.15618608850689708,0.9263152837811491,1.2482311881077635,0.15618608850689708,0.8761308151788838,1.2829285464510387,0.15618608850689708,0.8244881132993725,1.315490596839895,0.15618608850689708,0.7714731323233175,1.3458631429549808,0.15618608850689708,0.71717411045455,1.373995632690728,0.15618608850689708,0.6616814230562469,1.3998412422943445,0.15618608850689708,0.6050874322300542,1.423356954299327,0.15618608850689708,0.5474863330884874,1.4445036291237878,0.15618608850689708,0.4889739969764653,1.463246070214431,0.15618608850689708,0.42964781190291057,1.4795530826277357,0.15618608850689708,0.36960652044802883,1.4933975249508662,0.15618608850689708,0.3089500554160192,1.5047563544758715,0.15618608850689708,0.24777937350679413,1.5136106655520012,0.15618608850689708,0.1861962872835067,1.5199457210522935,0.15618608850689708,0.12430329571559078,1.5237509769020694,0.15618608850689708,0.06220341357934482,1.5250200996285046,0.15618608850689708,0.0,1.515186058514056,0.12229244515040011,0.0,1.5139251196830816,0.12229244515040011,-0.0618022969470152,1.5101444018984018,0.12229244515040011,-0.12350173006997922,1.5038501977916534,0.12229244515040011,-0.18499560675152446,1.4950529834444024,0.12229244515040011,-0.2461815765026939,1.4837674009517396,0.12229244515040011,-0.30695780131522904,1.47001223405196,0.12229244515040011,-0.36722312516088423,1.4538103768628814,0.12229244515040011,-0.42687724235565305,1.435188795776848,0.12229244515040011,-0.48582086450867984,1.414178484577829,0.12229244515040011,-0.5439558857779877,1.3908144128553253,0.12229244515040011,-0.6011855461579707,1.3651354678009375,0.12229244515040011,-0.6574145925268737,1.337184389484476,0.12229244515040011,-0.7125494371862136,1.307007699717329,0.12229244515040011,-0.7664983136282665,1.2746556246215024,0.12229244515040011,-0.8191714292723641,1.2401820110331936,0.12229244515040011,-0.8704811149157828,1.2036442368800477,0.12229244515040011,-0.9203419706504795,1.1651031156812606,0.12229244515040011,-0.9686710080028095,1.1246227953294767,0.12229244515040011,-1.0153877880596527,1.0822706513229523,0.12229244515040011,-1.0604145553510442,1.0381171746256885,0.12229244515040011,-1.1036763672664818,0.9922358543421782,0.12229244515040011,-1.145101218789504,0.9447030554020442,0.12229244515040011,-1.184620162342935,0.8955978914581499,0.12229244515040011,-1.2221674225453223,0.8450020932097321,0.12229244515040011,-1.2576805056875657,0.7929998723697183,0.12229244515040011,-1.2911003037475326,0.7396777815026407,0.12229244515040011,-1.3223711927695234,0.6851245699664359,0.12229244515040011,-1.3514411254448588,0.6294310361978936,0.12229244515040011,-1.3782617177394887,0.5726898765876239,0.12229244515040011,-1.4027883294244403,0.5149955311960606,0.12229244515040011,-1.4249801383750749,0.4564440265673004,0.12229244515040011,-1.4448002085154854,0.39713281590239913,0.12229244515040011,-1.4622155512949488,0.33716061685813464,0.12229244515040011,-1.477197180594115,0.2766272472412098,0.12229244515040011,-1.4897201609695396,0.21563345887136454,0.12229244515040011,-1.49976364915627,0.15428076988991368,0.12229244515040011,-1.5073109287594035,0.0926712957928216,0.12229244515040011,-1.512349438076875,0.030907579469535193,0.12229244515040011,-1.514870791007172,-0.03090757946953501,0.12229244515040011,-1.514870791007172,-0.0926712957928214,0.12229244515040011,-1.5123494380768752,-0.15428076988991318,0.12229244515040011,-1.5073109287594035,-0.21563345887136437,0.12229244515040011,-1.49976364915627,-0.27662724724120963,0.12229244515040011,-1.4897201609695396,-0.33716061685813414,0.12229244515040011,-1.477197180594115,-0.39713281590239896,0.12229244515040011,-1.462215551294949,-0.4564440265672999,0.12229244515040011,-1.4448002085154854,-0.5149955311960603,0.12229244515040011,-1.424980138375075,-0.5726898765876237,0.12229244515040011,-1.4027883294244405,-0.6294310361978932,0.12229244515040011,-1.378261717739489,-0.6851245699664359,0.12229244515040011,-1.3514411254448588,-0.7396777815026407,0.12229244515040011,-1.3223711927695234,-0.7929998723697186,0.12229244515040011,-1.2911003037475324,-0.8450020932097317,0.12229244515040011,-1.2576805056875662,-0.8955978914581496,0.12229244515040011,-1.2221674225453225,-0.9447030554020441,0.12229244515040011,-1.1846201623429355,-0.9922358543421784,0.12229244515040011,-1.145101218789504,-1.0381171746256888,0.12229244515040011,-1.1036763672664818,-1.0822706513229523,0.12229244515040011,-1.0604145553510442,-1.1246227953294767,0.12229244515040011,-1.0153877880596527,-1.1651031156812603,0.12229244515040011,-0.96867100800281,-1.2036442368800477,0.12229244515040011,-0.9203419706504793,-1.2401820110331931,0.12229244515040011,-0.8704811149157833,-1.2746556246215026,0.12229244515040011,-0.819171429272364,-1.307007699717329,0.12229244515040011,-0.7664983136282667,-1.3371843894844757,0.12229244515040011,-0.7125494371862138,-1.3651354678009375,0.12229244515040011,-0.657414592526874,-1.390814412855325,0.12229244515040011,-0.6011855461579708,-1.414178484577829,0.12229244515040011,-0.5439558857779877,-1.435188795776848,0.12229244515040011,-0.48582086450867984,-1.4538103768628812,0.12229244515040011,-0.42687724235565366,-1.47001223405196,0.12229244515040011,-0.3672231251608841,-1.4837674009517394,0.12229244515040011,-0.3069578013152295,-1.4950529834444024,0.12229244515040011,-0.24618157650269432,-1.5038501977916534,0.12229244515040011,-0.18499560675152485,-1.5101444018984018,0.12229244515040011,-0.12350173006997954,-1.5139251196830816,0.12229244515040011,-0.061802296947015445,-1.515186058514056,0.12229244515040011,-1.8555677566719328e-16,-1.5139251196830816,0.12229244515040011,0.06180229694701508,-1.5101444018984018,0.12229244515040011,0.12350173006997915,-1.5038501977916534,0.12229244515040011,0.1849956067515245,-1.4950529834444024,0.12229244515040011,0.24618157650269332,-1.4837674009517399,0.12229244515040011,0.3069578013152285,-1.47001223405196,0.12229244515040011,0.3672231251608844,-1.4538103768628814,0.12229244515040011,0.42687724235565333,-1.435188795776848,0.12229244515040011,0.4858208645086795,-1.4141784845778291,0.12229244515040011,0.5439558857779874,-1.3908144128553253,0.12229244515040011,0.6011855461579705,-1.365135467800938,0.12229244515040011,0.6574145925268731,-1.337184389484476,0.12229244515040011,0.7125494371862134,-1.307007699717329,0.12229244515040011,0.7664983136282664,-1.2746556246215024,0.12229244515040011,0.8191714292723641,-1.2401820110331936,0.12229244515040011,0.8704811149157825,-1.203644236880048,0.12229244515040011,0.920341970650479,-1.1651031156812606,0.12229244515040011,0.9686710080028097,-1.1246227953294765,0.12229244515040011,1.0153877880596527,-1.0822706513229527,0.12229244515040011,1.060414555351044,-1.038117174625689,0.12229244515040011,1.1036763672664816,-0.9922358543421791,0.12229244515040011,1.1451012187895033,-0.9447030554020442,0.12229244515040011,1.184620162342935,-0.8955978914581493,0.12229244515040011,1.2221674225453225,-0.8450020932097321,0.12229244515040011,1.2576805056875657,-0.7929998723697189,0.12229244515040011,1.2911003037475322,-0.739677781502641,0.12229244515040011,1.3223711927695232,-0.6851245699664351,0.12229244515040011,1.3514411254448593,-0.6294310361978935,0.12229244515040011,1.378261717739489,-0.5726898765876244,0.12229244515040011,1.40278832942444,-0.5149955311960601,0.12229244515040011,1.424980138375075,-0.45644402656730054,0.12229244515040011,1.4448002085154854,-0.39713281590239996,0.12229244515040011,1.4622155512949486,-0.33716061685813487,0.12229244515040011,1.477197180594115,-0.27662724724120935,0.12229244515040011,1.4897201609695396,-0.21563345887136437,0.12229244515040011,1.49976364915627,-0.1542807698899142,0.12229244515040011,1.5073109287594035,-0.09267129579282143,0.12229244515040011,1.5123494380768752,-0.03090757946953437,0.12229244515040011,1.514870791007172,0.030907579469535162,0.12229244515040011,1.514870791007172,0.09267129579282088,0.12229244515040011,1.5123494380768752,0.15428076988991368,0.12229244515040011,1.5073109287594035,0.21563345887136381,0.12229244515040011,1.4997636491562703,0.2766272472412088,0.12229244515040011,1.4897201609695399,0.3371606168581343,0.12229244515040011,1.477197180594115,0.39713281590239946,0.12229244515040011,1.4622155512949488,0.45644402656730004,0.12229244515040011,1.4448002085154854,0.5149955311960597,0.12229244515040011,1.4249801383750753,0.5726898765876226,0.12229244515040011,1.402788329424441,0.6294310361978942,0.12229244515040011,1.3782617177394885,0.6851245699664358,0.12229244515040011,1.3514411254448588,0.7396777815026405,0.12229244515040011,1.3223711927695234,0.7929998723697174,0.12229244515040011,1.2911003037475333,0.8450020932097316,0.12229244515040011,1.2576805056875662,0.8955978914581499,0.12229244515040011,1.222167422545322,0.9447030554020439,0.12229244515040011,1.1846201623429355,0.9922358543421788,0.12229244515040011,1.145101218789504,1.0381171746256883,0.12229244515040011,1.103676367266482,1.0822706513229519,0.12229244515040011,1.060414555351045,1.1246227953294765,0.12229244515040011,1.0153877880596527,1.165103115681261,0.12229244515040011,0.968671008002809,1.2036442368800475,0.12229244515040011,0.9203419706504795,1.240182011033193,0.12229244515040011,0.8704811149157835,1.2746556246215017,0.12229244515040011,0.8191714292723651,1.307007699717329,0.12229244515040011,0.7664983136282668,1.3371843894844762,0.12229244515040011,0.7125494371862133,1.3651354678009375,0.12229244515040011,0.6574145925268742,1.3908144128553246,0.12229244515040011,0.6011855461579717,1.414178484577829,0.12229244515040011,0.543955885777988,1.4351887957768477,0.12229244515040011,0.48582086450868067,1.4538103768628814,0.12229244515040011,0.4268772423556532,1.4700122340519597,0.12229244515040011,0.36722312516088496,1.4837674009517396,0.12229244515040011,0.30695780131522904,1.4950529834444024,0.12229244515040011,0.24618157650269448,1.5038501977916534,0.12229244515040011,0.1849956067515257,1.5101444018984018,0.12229244515040011,0.1235017300699797,1.5139251196830816,0.12229244515040011,0.06180229694701496,1.515186058514056,0.12229244515040011,0.0,1.5077673360692083,0.08778954698366043,0.0,1.506512571103915,0.08778954698366043,-0.06149969774810652,1.502750364640488,0.08778954698366043,-0.12289703531866518,1.4964869785002968,0.08778954698366043,-0.18408982290254178,1.4877328374714431,0.08778954698366043,-0.2449762111438682,1.476502511957731,0.08778954698366043,-0.30545486065824357,1.4628146937276665,0.08778954698366043,-0.36542511070213857,1.446692164803854,0.08778954698366043,-0.42478714671276924,1.4281617595445681,0.08778954698366043,-0.48344216643958565,1.4072543199806131,0.08778954698366043,-0.5412925443908674,1.3840046444818053,0.08778954698366043,-0.5982419943217203,1.358451429838521,0.08778954698366043,-0.6541957294930278,1.330637206854709,0.08778954698366043,-0.7090606204346253,1.3006082695595629,0.08778954698366043,-0.7627453499501103,1.2684145981556805,0.08778954698366043,-0.8151605651053054,1.2341097758319461,0.08778954698366043,-0.866219025947399,1.1977508995796038,0.08778954698366043,-0.9158357507072363,1.1593984851599481,0.08778954698366043,-0.9639281572430874,1.119116366381813,0.08778954698366043,-1.0104162004904733,1.0769715888564961,0.08778954698366043,-1.0552225056892746,1.0330342984069552,0.08778954698366043,-1.0982724971663849,0.9873776243170089,0.08778954698366043,-1.139494522459558,0.9400775576148613,0.08778954698366043,-1.1788199715758612,0.8912128245935335,0.08778954698366043,-1.2161833911862359,0.8408647557787198,0.08778954698366043,-1.2515225935661052,0.7891171505621535,0.08778954698366043,-1.284778760100705,0.7360561377257898,0.08778954698366043,-1.3158965391828608,0.6817700320889483,0.08778954698366043,-1.3448241383402781,0.6263491875170099,0.08778954698366043,-1.3715134104390005,0.5698858465363282,0.08778954698366043,-1.3959199338195616,0.512473986805646,0.08778954698366043,-1.4180030862324537,0.4542091646995557,0.08778954698366043,-1.4377261124498535,0.39518835626434645,0.08778954698366043,-1.455056185441071,0.3355097958109445,0.08778954698366043,-1.4699644610099032,0.275272812413597,0.08778954698366043,-1.4824261258029507,0.21457766458643165,0.08778954698366043,-1.4924204386089965,0.15352537341305253,0.08778954698366043,-1.499930764880706,0.09221755440691842,0.08778954698366043,-1.5049446044211872,0.030756248382347508,0.08778954698366043,-1.5074536121893372,-0.030756248382347324,0.08778954698366043,-1.5074536121893372,-0.09221755440691824,0.08778954698366043,-1.5049446044211874,-0.15352537341305203,0.08778954698366043,-1.499930764880706,-0.2145776645864315,0.08778954698366043,-1.4924204386089965,-0.27527281241359686,0.08778954698366043,-1.4824261258029507,-0.335509795810944,0.08778954698366043,-1.4699644610099032,-0.3951883562643463,0.08778954698366043,-1.4550561854410713,-0.4542091646995552,0.08778954698366043,-1.4377261124498537,-0.5124739868056458,0.08778954698366043,-1.4180030862324537,-0.5698858465363281,0.08778954698366043,-1.3959199338195618,-0.6263491875170094,0.08778954698366043,-1.3715134104390008,-0.6817700320889483,0.08778954698366043,-1.3448241383402781,-0.7360561377257897,0.08778954698366043,-1.3158965391828608,-0.7891171505621538,0.08778954698366043,-1.2847787601007046,-0.8408647557787194,0.08778954698366043,-1.2515225935661056,-0.8912128245935331,0.08778954698366043,-1.216183391186236,-0.9400775576148611,0.08778954698366043,-1.1788199715758614,-0.9873776243170092,0.08778954698366043,-1.139494522459558,-1.0330342984069554,0.08778954698366043,-1.0982724971663849,-1.0769715888564961,0.08778954698366043,-1.0552225056892746,-1.119116366381813,0.08778954698366043,-1.0104162004904733,-1.159398485159948,0.08778954698366043,-0.963928157243088,-1.1977508995796038,0.08778954698366043,-0.9158357507072362,-1.234109775831946,0.08778954698366043,-0.8662190259473995,-1.2684145981556805,0.08778954698366043,-0.8151605651053053,-1.3006082695595629,0.08778954698366043,-0.7627453499501105,-1.3306372068547088,0.08778954698366043,-0.7090606204346255,-1.3584514298385209,0.08778954698366043,-0.6541957294930282,-1.384004644481805,0.08778954698366043,-0.5982419943217203,-1.4072543199806131,0.08778954698366043,-0.5412925443908675,-1.4281617595445681,0.08778954698366043,-0.48344216643958565,-1.4466921648038538,0.08778954698366043,-0.4247871467127698,-1.4628146937276665,0.08778954698366043,-0.36542511070213846,-1.476502511957731,0.08778954698366043,-0.305454860658244,-1.4877328374714431,0.08778954698366043,-0.2449762111438686,-1.4964869785002968,0.08778954698366043,-0.18408982290254217,-1.502750364640488,0.08778954698366043,-0.12289703531866548,-1.506512571103915,0.08778954698366043,-0.061499697748106756,-1.5077673360692083,0.08778954698366043,-1.8464824419760875e-16,-1.506512571103915,0.08778954698366043,0.061499697748106395,-1.502750364640488,0.08778954698366043,0.12289703531866511,-1.4964869785002968,0.08778954698366043,0.1840898229025418,-1.4877328374714434,0.08778954698366043,0.2449762111438676,-1.4765025119577313,0.08778954698366043,0.305454860658243,-1.4628146937276665,0.08778954698366043,0.36542511070213873,-1.446692164803854,0.08778954698366043,0.42478714671276946,-1.4281617595445681,0.08778954698366043,0.4834421664395853,-1.4072543199806133,0.08778954698366043,0.5412925443908672,-1.3840046444818053,0.08778954698366043,0.59824199432172,-1.3584514298385213,0.08778954698366043,0.6541957294930272,-1.330637206854709,0.08778954698366043,0.7090606204346251,-1.3006082695595629,0.08778954698366043,0.7627453499501101,-1.2684145981556805,0.08778954698366043,0.8151605651053054,-1.2341097758319464,0.08778954698366043,0.8662190259473986,-1.197750899579604,0.08778954698366043,0.9158357507072359,-1.159398485159948,0.08778954698366043,0.9639281572430877,-1.119116366381813,0.08778954698366043,1.0104162004904733,-1.0769715888564966,0.08778954698366043,1.0552225056892743,-1.0330342984069556,0.08778954698366043,1.0982724971663846,-0.9873776243170098,0.08778954698366043,1.1394945224595574,-0.9400775576148613,0.08778954698366043,1.1788199715758612,-0.8912128245935328,0.08778954698366043,1.2161833911862363,-0.8408647557787198,0.08778954698366043,1.2515225935661052,-0.7891171505621541,0.08778954698366043,1.2847787601007046,-0.73605613772579,0.08778954698366043,1.3158965391828605,-0.6817700320889475,0.08778954698366043,1.3448241383402786,-0.6263491875170097,0.08778954698366043,1.3715134104390008,-0.5698858465363288,0.08778954698366043,1.3959199338195616,-0.5124739868056456,0.08778954698366043,1.4180030862324537,-0.45420916469955586,0.08778954698366043,1.4377261124498535,-0.3951883562643473,0.08778954698366043,1.455056185441071,-0.3355097958109447,0.08778954698366043,1.4699644610099032,-0.2752728124135965,0.08778954698366043,1.4824261258029507,-0.2145776645864315,0.08778954698366043,1.4924204386089965,-0.15352537341305306,0.08778954698366043,1.4999307648807059,-0.09221755440691827,0.08778954698366043,1.5049446044211874,-0.030756248382346692,0.08778954698366043,1.5074536121893372,0.030756248382347477,0.08778954698366043,1.5074536121893372,0.09221755440691771,0.08778954698366043,1.5049446044211874,0.1535253734130525,0.08778954698366043,1.499930764880706,0.21457766458643093,0.08778954698366043,1.4924204386089968,0.275272812413596,0.08778954698366043,1.4824261258029507,0.33550979581094414,0.08778954698366043,1.4699644610099032,0.3951883562643468,0.08778954698366043,1.455056185441071,0.45420916469955536,0.08778954698366043,1.4377261124498537,0.512473986805645,0.08778954698366043,1.418003086232454,0.569885846536327,0.08778954698366043,1.3959199338195623,0.6263491875170104,0.08778954698366043,1.3715134104390003,0.6817700320889482,0.08778954698366043,1.3448241383402781,0.7360561377257896,0.08778954698366043,1.315896539182861,0.7891171505621526,0.08778954698366043,1.2847787601007055,0.8408647557787192,0.08778954698366043,1.2515225935661056,0.8912128245935335,0.08778954698366043,1.2161833911862356,0.9400775576148609,0.08778954698366043,1.1788199715758614,0.9873776243170095,0.08778954698366043,1.1394945224595578,1.033034298406955,0.08778954698366043,1.098272497166385,1.0769715888564957,0.08778954698366043,1.0552225056892752,1.119116366381813,0.08778954698366043,1.0104162004904733,1.1593984851599486,0.08778954698366043,0.963928157243087,1.1977508995796036,0.08778954698366043,0.9158357507072363,1.2341097758319457,0.08778954698366043,0.8662190259473996,1.2684145981556798,0.08778954698366043,0.8151605651053064,1.3006082695595629,0.08778954698366043,0.7627453499501107,1.330637206854709,0.08778954698366043,0.709060620434625,1.3584514298385209,0.08778954698366043,0.6541957294930283,1.3840046444818046,0.08778954698366043,0.5982419943217212,1.4072543199806131,0.08778954698366043,0.5412925443908677,1.428161759544568,0.08778954698366043,0.4834421664395865,1.446692164803854,0.08778954698366043,0.4247871467127694,1.4628146937276663,0.08778954698366043,0.3654251107021393,1.476502511957731,0.08778954698366043,0.30545486065824357,1.4877328374714431,0.08778954698366043,0.24497621114386878,1.4964869785002968,0.08778954698366043,0.18408982290254303,1.502750364640488,0.08778954698366043,0.12289703531866565,1.506512571103915,0.08778954698366043,0.06149969774810628,1.5077673360692083,0.08778954698366043,0.0,1.502800891996009,0.052849285709796696,0.0,1.5015502601087285,0.052849285709796696,-0.06129712351660677,1.4978004460002394,0.052849285709796696,-0.12249222402048214,1.4915576908660595,0.052849285709796696,-0.18348344830613078,1.4828323851560172,0.052849285709796696,-0.24416928249990236,1.4716390512803739,0.052849285709796696,-0.3044487210198156,1.4579963194387044,0.052849285709796696,-0.3642214346893817,1.4419268966117698,0.052849285709796696,-0.42338793772561684,1.4234575287679874,0.052849285709796696,-0.48184975332331004,1.4026189563474065,0.052849285709796696,-0.5395095775599467,1.3794458630972803,0.052849285709796696,-0.5962714413484851,1.3539768183443899,0.052849285709796696,-0.6520408701684299,1.326254212800208,0.052849285709796696,-0.706725041309349,1.296324188005742,0.052849285709796696,-0.7602329383651135,1.264236559533493,0.052849285709796696,-0.8124755027217234,1.2300447340743519,0.052849285709796696,-0.8633657817865827,1.1938056205474328,0.052849285709796696,-0.9128190737125073,1.155579535380793,0.052849285709796696,-0.9607530683755898,1.1154301021206923,0.052849285709796696,-1.0070879843722802,1.073424145536478,0.052849285709796696,-1.0517467018076558,1.0296315803973515,0.052849285709796696,-1.0946548906538776,0.9841252951061354,0.052849285709796696,-1.1357411344651833,0.9369810303837197,0.052849285709796696,-1.1749370492435172,0.8882772532061065,0.052849285709796696,-1.2121773972569423,0.8380950262038726,0.052849285709796696,-1.2474001956214094,0.7865178727414212,0.052849285709796696,-1.2805468194651495,0.7336316379005844,0.052849285709796696,-1.311562099503987,0.6795243455999569,0.052849285709796696,-1.340394413865169,0.6242860520877686,0.052849285709796696,-1.3669957740068797,0.56800869605215,0.052849285709796696,-1.3913219045904281,0.5107859455982572,0.052849285709796696,-1.4133323171721839,0.4527130423469546,0.052849285709796696,-1.4329903775925938,0.39388664291453795,0.052849285709796696,-1.450263366950126,0.3344046580373344,0.052849285709796696,-1.4651225360586513,0.27436608960894426,0.052849285709796696,-1.4775431532976278,0.21387086590136095,0.052849285709796696,-1.4875045457754414,0.1530196752442218,0.052849285709796696,-1.4949901337373948,0.09191379843902275,0.052849285709796696,-1.499987458161071,0.030654940187218028,0.052849285709796696,-1.5024882014931493,-0.030654940187217844,0.052849285709796696,-1.5024882014931493,-0.09191379843902257,0.052849285709796696,-1.4999874581610713,-0.1530196752442213,0.052849285709796696,-1.4949901337373948,-0.21387086590136078,0.052849285709796696,-1.4875045457754414,-0.2743660896089441,0.052849285709796696,-1.4775431532976278,-0.3344046580373339,0.052849285709796696,-1.4651225360586513,-0.3938866429145378,0.052849285709796696,-1.450263366950126,-0.4527130423469541,0.052849285709796696,-1.432990377592594,-0.510785945598257,0.052849285709796696,-1.4133323171721839,-0.5680086960521498,0.052849285709796696,-1.3913219045904284,-0.6242860520877682,0.052849285709796696,-1.3669957740068799,-0.679524345599957,0.052849285709796696,-1.340394413865169,-0.7336316379005843,0.052849285709796696,-1.311562099503987,-0.7865178727414215,0.052849285709796696,-1.2805468194651493,-0.8380950262038722,0.052849285709796696,-1.2474001956214096,-0.8882772532061062,0.052849285709796696,-1.2121773972569425,-0.9369810303837196,0.052849285709796696,-1.1749370492435174,-0.9841252951061356,0.052849285709796696,-1.1357411344651833,-1.0296315803973517,0.052849285709796696,-1.0946548906538776,-1.073424145536478,0.052849285709796696,-1.0517467018076558,-1.1154301021206923,0.052849285709796696,-1.0070879843722802,-1.1555795353807927,0.052849285709796696,-0.9607530683755904,-1.1938056205474328,0.052849285709796696,-0.912819073712507,-1.2300447340743517,0.052849285709796696,-0.8633657817865832,-1.264236559533493,0.052849285709796696,-0.8124755027217233,-1.2963241880057417,0.052849285709796696,-0.7602329383651136,-1.3262542128002077,0.052849285709796696,-0.7067250413093492,-1.3539768183443897,0.052849285709796696,-0.6520408701684302,-1.3794458630972801,0.052849285709796696,-0.5962714413484852,-1.4026189563474065,0.052849285709796696,-0.5395095775599468,-1.4234575287679874,0.052849285709796696,-0.48184975332331004,-1.4419268966117695,0.052849285709796696,-0.4233879377256174,-1.4579963194387044,0.052849285709796696,-0.36422143468938156,-1.4716390512803736,0.052849285709796696,-0.3044487210198161,-1.4828323851560172,0.052849285709796696,-0.24416928249990277,-1.4915576908660595,0.052849285709796696,-0.18348344830613117,-1.4978004460002394,0.052849285709796696,-0.12249222402048245,-1.5015502601087285,0.052849285709796696,-0.061297123516607016,-1.502800891996009,0.052849285709796696,-1.8404003021386995e-16,-1.5015502601087285,0.052849285709796696,0.06129712351660665,-1.4978004460002394,0.052849285709796696,0.12249222402048207,-1.4915576908660595,0.052849285709796696,0.1834834483061308,-1.4828323851560175,0.052849285709796696,0.24416928249990177,-1.4716390512803739,0.052849285709796696,0.3044487210198151,-1.4579963194387044,0.052849285709796696,0.36422143468938184,-1.4419268966117698,0.052849285709796696,0.42338793772561706,-1.4234575287679874,0.052849285709796696,0.4818497533233097,-1.4026189563474067,0.052849285709796696,0.5395095775599464,-1.3794458630972803,0.052849285709796696,0.5962714413484849,-1.35397681834439,0.052849285709796696,0.6520408701684293,-1.326254212800208,0.052849285709796696,0.7067250413093489,-1.296324188005742,0.052849285709796696,0.7602329383651133,-1.264236559533493,0.052849285709796696,0.8124755027217234,-1.230044734074352,0.052849285709796696,0.8633657817865824,-1.193805620547433,0.052849285709796696,0.9128190737125067,-1.1555795353807927,0.052849285709796696,0.96075306837559,-1.115430102120692,0.052849285709796696,1.0070879843722804,-1.0734241455364784,0.052849285709796696,1.0517467018076558,-1.029631580397352,0.052849285709796696,1.0946548906538773,-0.9841252951061362,0.052849285709796696,1.1357411344651827,-0.9369810303837197,0.052849285709796696,1.1749370492435172,-0.8882772532061058,0.052849285709796696,1.2121773972569427,-0.8380950262038726,0.052849285709796696,1.2474001956214094,-0.7865178727414218,0.052849285709796696,1.280546819465149,-0.7336316379005846,0.052849285709796696,1.3115620995039867,-0.6795243455999561,0.052849285709796696,1.3403944138651698,-0.6242860520877686,0.052849285709796696,1.3669957740068799,-0.5680086960521504,0.052849285709796696,1.391321904590428,-0.5107859455982567,0.052849285709796696,1.4133323171721839,-0.45271304234695475,0.052849285709796696,1.4329903775925938,-0.3938866429145388,0.052849285709796696,1.4502633669501257,-0.33440465803733455,0.052849285709796696,1.4651225360586513,-0.2743660896089438,0.052849285709796696,1.4775431532976278,-0.21387086590136078,0.052849285709796696,1.4875045457754414,-0.15301967524422233,0.052849285709796696,1.4949901337373945,-0.0919137984390226,0.052849285709796696,1.4999874581610713,-0.030654940187217216,0.052849285709796696,1.5024882014931493,0.030654940187217997,0.052849285709796696,1.5024882014931493,0.09191379843902205,0.052849285709796696,1.4999874581610713,0.15301967524422178,0.052849285709796696,1.4949901337373948,0.21387086590136026,0.052849285709796696,1.4875045457754414,0.27436608960894326,0.052849285709796696,1.477543153297628,0.33440465803733405,0.052849285709796696,1.4651225360586513,0.3938866429145383,0.052849285709796696,1.450263366950126,0.45271304234695425,0.052849285709796696,1.432990377592594,0.5107859455982562,0.052849285709796696,1.413332317172184,0.5680086960521487,0.052849285709796696,1.3913219045904286,0.6242860520877692,0.052849285709796696,1.3669957740068794,0.6795243455999568,0.052849285709796696,1.340394413865169,0.7336316379005842,0.052849285709796696,1.3115620995039872,0.7865178727414203,0.052849285709796696,1.28054681946515,0.8380950262038721,0.052849285709796696,1.2474001956214096,0.8882772532061065,0.052849285709796696,1.212177397256942,0.9369810303837194,0.052849285709796696,1.1749370492435174,0.9841252951061359,0.052849285709796696,1.1357411344651833,1.0296315803973513,0.052849285709796696,1.0946548906538776,1.0734241455364775,0.052849285709796696,1.0517467018076565,1.115430102120692,0.052849285709796696,1.0070879843722804,1.1555795353807934,0.052849285709796696,0.9607530683755894,1.1938056205474326,0.052849285709796696,0.9128190737125073,1.2300447340743514,0.052849285709796696,0.8633657817865834,1.2642365595334923,0.052849285709796696,0.8124755027217244,1.2963241880057417,0.052849285709796696,0.7602329383651139,1.3262542128002082,0.052849285709796696,0.7067250413093488,1.3539768183443897,0.052849285709796696,0.6520408701684304,1.3794458630972797,0.052849285709796696,0.596271441348486,1.4026189563474065,0.052849285709796696,0.539509577559947,1.4234575287679871,0.052849285709796696,0.48184975332331087,1.4419268966117698,0.052849285709796696,0.423387937725617,1.4579963194387042,0.052849285709796696,0.3642214346893824,1.4716390512803739,0.052849285709796696,0.3044487210198156,1.4828323851560172,0.052849285709796696,0.24416928249990294,1.4915576908660595,0.052849285709796696,0.18348344830613203,1.4978004460002394,0.052849285709796696,0.12249222402048261,1.5015502601087285,0.052849285709796696,0.06129712351660654,1.502800891996009,0.052849285709796696,0.0,1.500311468869011,0.017645731952926562,0.0,1.4990629086812883,0.017645731952926562,-0.06119558346714751,1.4953193062233334,0.017645731952926562,-0.12228931292495503,1.4890868923519849,0.017645731952926562,-0.18317950389002924,1.4803760403050594,0.017645731952926562,-0.24376481064871092,1.4692012484361228,0.017645731952926562,-0.30394439493701364,1.4555811160834096,0.017645731952926562,-0.36361809377596244,1.4395383126130616,0.017645731952926562,-0.42268658618298743,1.4210995396882045,0.017645731952926562,-0.4810515584818975,1.4002954868266642,0.017645731952926562,-0.5386158679362913,1.3771607803212944,0.017645731952926562,-0.595283704434056,1.3517339256079284,0.017645731952926562,-0.6509607499538422,1.3240572431768813,0.017645731952926562,-0.7055543355481039,1.294176798134671,0.017645731952926562,-0.7589735955814143,1.262142323533194,0.017645731952926562,-0.8111296189673504,1.2280071375939694,0.017645731952926562,-0.8619355971522207,1.1918280549652192,0.017645731952926562,-0.9113069685993364,1.1536652921594959,0.017645731952926562,-0.9591615595333427,1.1135823673292384,0.017645731952926562,-1.00541972071036,1.0716459945470762,0.017645731952926562,-1.0500044599862883,1.0279259727668395,0.017645731952926562,-1.0928415704626337,0.982495069650089,0.017645731952926562,-1.133859753996567,0.9354289004515252,0.017645731952926562,-1.172990739869646,0.8868058021648574,0.017645731952926562,-1.2101693984176898,0.8367067031386108,0.017645731952926562,-1.2453338494326753,0.7852149883788765,0.017645731952926562,-1.2784255651562395,0.7324163607631976,0.017645731952926562,-1.309389467693353,0.6783986983965885,0.017645731952926562,-1.3381740206840451,0.6232519083470995,0.017645731952926562,-1.3647313150805866,0.5670677770043776,0.017645731952926562,-1.3890171488873715,0.5099398173102782,0.017645731952926562,-1.4109911007307767,0.45196311311580056,0.017645731952926562,-1.4306165971365505,0.39323416092340424,0.017645731952926562,-1.4478609734027488,0.3338507092781023,0.017645731952926562,-1.4626955279669098,0.2739115960746549,0.017645731952926562,-1.4750955701769715,0.21351658405164844,0.017645731952926562,-1.4850404613864243,0.15276619474626127,0.017645731952926562,-1.4925136493053042,0.09176154118608726,0.017645731952926562,-1.497502695549842,0.030604159596478928,0.017645731952926562,-1.4999992963449291,-0.030604159596478747,0.017645731952926562,-1.4999992963449291,-0.09176154118608706,0.017645731952926562,-1.4975026955498423,-0.15276619474626077,0.017645731952926562,-1.4925136493053042,-0.21351658405164828,0.017645731952926562,-1.4850404613864243,-0.2739115960746547,0.017645731952926562,-1.4750955701769715,-0.3338507092781018,0.017645731952926562,-1.4626955279669098,-0.3932341609234041,0.017645731952926562,-1.447860973402749,-0.45196311311580006,0.017645731952926562,-1.4306165971365505,-0.509939817310278,0.017645731952926562,-1.410991100730777,-0.5670677770043775,0.017645731952926562,-1.3890171488873717,-0.6232519083470991,0.017645731952926562,-1.3647313150805869,-0.6783986983965886,0.017645731952926562,-1.3381740206840451,-0.7324163607631975,0.017645731952926562,-1.309389467693353,-0.7852149883788768,0.017645731952926562,-1.278425565156239,-0.8367067031386105,0.017645731952926562,-1.2453338494326758,-0.886805802164857,0.017645731952926562,-1.21016939841769,-0.935428900451525,0.017645731952926562,-1.1729907398696464,-0.9824950696500893,0.017645731952926562,-1.133859753996567,-1.0279259727668397,0.017645731952926562,-1.0928415704626337,-1.0716459945470762,0.017645731952926562,-1.0500044599862883,-1.1135823673292384,0.017645731952926562,-1.00541972071036,-1.1536652921594954,0.017645731952926562,-0.9591615595333431,-1.1918280549652192,0.017645731952926562,-0.9113069685993362,-1.228007137593969,0.017645731952926562,-0.8619355971522211,-1.2621423235331943,0.017645731952926562,-0.8111296189673501,-1.2941767981346708,0.017645731952926562,-0.7589735955814145,-1.3240572431768811,0.017645731952926562,-0.705554335548104,-1.3517339256079282,0.017645731952926562,-0.6509607499538426,-1.3771607803212942,0.017645731952926562,-0.5952837044340561,-1.4002954868266642,0.017645731952926562,-0.5386158679362915,-1.4210995396882045,0.017645731952926562,-0.4810515584818975,-1.4395383126130614,0.017645731952926562,-0.42268658618298804,-1.4555811160834096,0.017645731952926562,-0.36361809377596227,-1.4692012484361225,0.017645731952926562,-0.3039443949370141,-1.4803760403050594,0.017645731952926562,-0.24376481064871133,-1.4890868923519849,0.017645731952926562,-0.1831795038900296,-1.4953193062233334,0.017645731952926562,-0.12228931292495535,-1.4990629086812883,0.017645731952926562,-0.061195583467147756,-1.500311468869011,0.017645731952926562,-1.8373516380744982e-16,-1.4990629086812883,0.017645731952926562,0.06119558346714739,-1.4953193062233334,0.017645731952926562,0.12228931292495497,-1.4890868923519849,0.017645731952926562,0.18317950389002924,-1.4803760403050596,0.017645731952926562,0.24376481064871033,-1.469201248436123,0.017645731952926562,0.3039443949370131,-1.4555811160834096,0.017645731952926562,0.3636180937759626,-1.4395383126130616,0.017645731952926562,0.4226865861829877,-1.4210995396882045,0.017645731952926562,0.4810515584818972,-1.4002954868266644,0.017645731952926562,0.5386158679362911,-1.3771607803212944,0.017645731952926562,0.5952837044340558,-1.3517339256079286,0.017645731952926562,0.6509607499538416,-1.3240572431768813,0.017645731952926562,0.7055543355481037,-1.294176798134671,0.017645731952926562,0.7589735955814142,-1.262142323533194,0.017645731952926562,0.8111296189673504,-1.2280071375939696,0.017645731952926562,0.8619355971522203,-1.1918280549652196,0.017645731952926562,0.9113069685993359,-1.1536652921594956,0.017645731952926562,0.9591615595333428,-1.1135823673292382,0.017645731952926562,1.0054197207103601,-1.0716459945470764,0.017645731952926562,1.050004459986288,-1.0279259727668397,0.017645731952926562,1.0928415704626337,-0.9824950696500899,0.017645731952926562,1.1338597539965662,-0.9354289004515252,0.017645731952926562,1.172990739869646,-0.8868058021648567,0.017645731952926562,1.21016939841769,-0.8367067031386108,0.017645731952926562,1.2453338494326753,-0.7852149883788772,0.017645731952926562,1.278425565156239,-0.7324163607631978,0.017645731952926562,1.3093894676933528,-0.6783986983965878,0.017645731952926562,1.3381740206840458,-0.6232519083470994,0.017645731952926562,1.3647313150805869,-0.5670677770043782,0.017645731952926562,1.3890171488873713,-0.5099398173102777,0.017645731952926562,1.410991100730777,-0.45196311311580073,0.017645731952926562,1.4306165971365505,-0.3932341609234051,0.017645731952926562,1.4478609734027486,-0.3338507092781025,0.017645731952926562,1.4626955279669098,-0.27391159607465443,0.017645731952926562,1.4750955701769715,-0.21351658405164828,0.017645731952926562,1.4850404613864243,-0.15276619474626177,0.017645731952926562,1.492513649305304,-0.09176154118608709,0.017645731952926562,1.4975026955498423,-0.030604159596478116,0.017645731952926562,1.4999992963449291,0.030604159596478896,0.017645731952926562,1.4999992963449291,0.09176154118608655,0.017645731952926562,1.4975026955498423,0.15276619474626124,0.017645731952926562,1.4925136493053042,0.21351658405164775,0.017645731952926562,1.4850404613864245,0.2739115960746539,0.017645731952926562,1.4750955701769715,0.33385070927810195,0.017645731952926562,1.4626955279669098,0.3932341609234046,0.017645731952926562,1.4478609734027488,0.45196311311580023,0.017645731952926562,1.4306165971365505,0.5099398173102773,0.017645731952926562,1.4109911007307772,0.5670677770043764,0.017645731952926562,1.389017148887372,0.6232519083471001,0.017645731952926562,1.3647313150805864,0.6783986983965885,0.017645731952926562,1.3381740206840451,0.7324163607631974,0.017645731952926562,1.3093894676933533,0.7852149883788756,0.017645731952926562,1.27842556515624,0.8367067031386104,0.017645731952926562,1.2453338494326758,0.8868058021648574,0.017645731952926562,1.2101693984176896,0.9354289004515248,0.017645731952926562,1.1729907398696464,0.9824950696500896,0.017645731952926562,1.133859753996567,1.0279259727668393,0.017645731952926562,1.092841570462634,1.0716459945470755,0.017645731952926562,1.050004459986289,1.1135823673292382,0.017645731952926562,1.0054197207103601,1.153665292159496,0.017645731952926562,0.9591615595333421,1.191828054965219,0.017645731952926562,0.9113069685993364,1.228007137593969,0.017645731952926562,0.8619355971522213,1.2621423235331934,0.017645731952926562,0.8111296189673514,1.2941767981346708,0.017645731952926562,0.7589735955814146,1.3240572431768813,0.017645731952926562,0.7055543355481035,1.3517339256079282,0.017645731952926562,0.6509607499538427,1.377160780321294,0.017645731952926562,0.595283704434057,1.4002954868266642,0.017645731952926562,0.5386158679362917,1.4210995396882042,0.017645731952926562,0.48105155848189834,1.4395383126130616,0.017645731952926562,0.4226865861829876,1.4555811160834096,0.017645731952926562,0.3636180937759631,1.4692012484361228,0.017645731952926562,0.30394439493701364,1.4803760403050594,0.017645731952926562,0.2437648106487115,1.4890868923519849,0.017645731952926562,0.18317950389003046,1.4953193062233334,0.017645731952926562,0.12228931292495551,1.4990629086812883,0.017645731952926562,0.06119558346714728,1.500311468869011,0.017645731952926562,0.0,1.500311468869011,-0.01764573195292622,0.0,1.4990629086812883,-0.01764573195292622,-0.06119558346714751,1.4953193062233334,-0.01764573195292622,-0.12228931292495503,1.4890868923519849,-0.01764573195292622,-0.18317950389002924,1.4803760403050594,-0.01764573195292622,-0.24376481064871092,1.4692012484361228,-0.01764573195292622,-0.30394439493701364,1.4555811160834096,-0.01764573195292622,-0.36361809377596244,1.4395383126130616,-0.01764573195292622,-0.42268658618298743,1.4210995396882045,-0.01764573195292622,-0.4810515584818975,1.4002954868266642,-0.01764573195292622,-0.5386158679362913,1.3771607803212944,-0.01764573195292622,-0.595283704434056,1.3517339256079284,-0.01764573195292622,-0.6509607499538422,1.3240572431768813,-0.01764573195292622,-0.7055543355481039,1.294176798134671,-0.01764573195292622,-0.7589735955814143,1.262142323533194,-0.01764573195292622,-0.8111296189673504,1.2280071375939694,-0.01764573195292622,-0.8619355971522207,1.1918280549652192,-0.01764573195292622,-0.9113069685993364,1.1536652921594959,-0.01764573195292622,-0.9591615595333427,1.1135823673292384,-0.01764573195292622,-1.00541972071036,1.0716459945470762,-0.01764573195292622,-1.0500044599862883,1.0279259727668395,-0.01764573195292622,-1.0928415704626337,0.982495069650089,-0.01764573195292622,-1.133859753996567,0.9354289004515252,-0.01764573195292622,-1.172990739869646,0.8868058021648574,-0.01764573195292622,-1.2101693984176898,0.8367067031386108,-0.01764573195292622,-1.2453338494326753,0.7852149883788765,-0.01764573195292622,-1.2784255651562395,0.7324163607631976,-0.01764573195292622,-1.309389467693353,0.6783986983965885,-0.01764573195292622,-1.3381740206840451,0.6232519083470995,-0.01764573195292622,-1.3647313150805866,0.5670677770043776,-0.01764573195292622,-1.3890171488873715,0.5099398173102782,-0.01764573195292622,-1.4109911007307767,0.45196311311580056,-0.01764573195292622,-1.4306165971365505,0.39323416092340424,-0.01764573195292622,-1.4478609734027488,0.3338507092781023,-0.01764573195292622,-1.4626955279669098,0.2739115960746549,-0.01764573195292622,-1.4750955701769715,0.21351658405164844,-0.01764573195292622,-1.4850404613864243,0.15276619474626127,-0.01764573195292622,-1.4925136493053042,0.09176154118608726,-0.01764573195292622,-1.497502695549842,0.030604159596478928,-0.01764573195292622,-1.4999992963449291,-0.030604159596478747,-0.01764573195292622,-1.4999992963449291,-0.09176154118608706,-0.01764573195292622,-1.4975026955498423,-0.15276619474626077,-0.01764573195292622,-1.4925136493053042,-0.21351658405164828,-0.01764573195292622,-1.4850404613864243,-0.2739115960746547,-0.01764573195292622,-1.4750955701769715,-0.3338507092781018,-0.01764573195292622,-1.4626955279669098,-0.3932341609234041,-0.01764573195292622,-1.447860973402749,-0.45196311311580006,-0.01764573195292622,-1.4306165971365505,-0.509939817310278,-0.01764573195292622,-1.410991100730777,-0.5670677770043775,-0.01764573195292622,-1.3890171488873717,-0.6232519083470991,-0.01764573195292622,-1.3647313150805869,-0.6783986983965886,-0.01764573195292622,-1.3381740206840451,-0.7324163607631975,-0.01764573195292622,-1.309389467693353,-0.7852149883788768,-0.01764573195292622,-1.278425565156239,-0.8367067031386105,-0.01764573195292622,-1.2453338494326758,-0.886805802164857,-0.01764573195292622,-1.21016939841769,-0.935428900451525,-0.01764573195292622,-1.1729907398696464,-0.9824950696500893,-0.01764573195292622,-1.133859753996567,-1.0279259727668397,-0.01764573195292622,-1.0928415704626337,-1.0716459945470762,-0.01764573195292622,-1.0500044599862883,-1.1135823673292384,-0.01764573195292622,-1.00541972071036,-1.1536652921594954,-0.01764573195292622,-0.9591615595333431,-1.1918280549652192,-0.01764573195292622,-0.9113069685993362,-1.228007137593969,-0.01764573195292622,-0.8619355971522211,-1.2621423235331943,-0.01764573195292622,-0.8111296189673501,-1.2941767981346708,-0.01764573195292622,-0.7589735955814145,-1.3240572431768811,-0.01764573195292622,-0.705554335548104,-1.3517339256079282,-0.01764573195292622,-0.6509607499538426,-1.3771607803212942,-0.01764573195292622,-0.5952837044340561,-1.4002954868266642,-0.01764573195292622,-0.5386158679362915,-1.4210995396882045,-0.01764573195292622,-0.4810515584818975,-1.4395383126130614,-0.01764573195292622,-0.42268658618298804,-1.4555811160834096,-0.01764573195292622,-0.36361809377596227,-1.4692012484361225,-0.01764573195292622,-0.3039443949370141,-1.4803760403050594,-0.01764573195292622,-0.24376481064871133,-1.4890868923519849,-0.01764573195292622,-0.1831795038900296,-1.4953193062233334,-0.01764573195292622,-0.12228931292495535,-1.4990629086812883,-0.01764573195292622,-0.061195583467147756,-1.500311468869011,-0.01764573195292622,-1.8373516380744982e-16,-1.4990629086812883,-0.01764573195292622,0.06119558346714739,-1.4953193062233334,-0.01764573195292622,0.12228931292495497,-1.4890868923519849,-0.01764573195292622,0.18317950389002924,-1.4803760403050596,-0.01764573195292622,0.24376481064871033,-1.469201248436123,-0.01764573195292622,0.3039443949370131,-1.4555811160834096,-0.01764573195292622,0.3636180937759626,-1.4395383126130616,-0.01764573195292622,0.4226865861829877,-1.4210995396882045,-0.01764573195292622,0.4810515584818972,-1.4002954868266644,-0.01764573195292622,0.5386158679362911,-1.3771607803212944,-0.01764573195292622,0.5952837044340558,-1.3517339256079286,-0.01764573195292622,0.6509607499538416,-1.3240572431768813,-0.01764573195292622,0.7055543355481037,-1.294176798134671,-0.01764573195292622,0.7589735955814142,-1.262142323533194,-0.01764573195292622,0.8111296189673504,-1.2280071375939696,-0.01764573195292622,0.8619355971522203,-1.1918280549652196,-0.01764573195292622,0.9113069685993359,-1.1536652921594956,-0.01764573195292622,0.9591615595333428,-1.1135823673292382,-0.01764573195292622,1.0054197207103601,-1.0716459945470764,-0.01764573195292622,1.050004459986288,-1.0279259727668397,-0.01764573195292622,1.0928415704626337,-0.9824950696500899,-0.01764573195292622,1.1338597539965662,-0.9354289004515252,-0.01764573195292622,1.172990739869646,-0.8868058021648567,-0.01764573195292622,1.21016939841769,-0.8367067031386108,-0.01764573195292622,1.2453338494326753,-0.7852149883788772,-0.01764573195292622,1.278425565156239,-0.7324163607631978,-0.01764573195292622,1.3093894676933528,-0.6783986983965878,-0.01764573195292622,1.3381740206840458,-0.6232519083470994,-0.01764573195292622,1.3647313150805869,-0.5670677770043782,-0.01764573195292622,1.3890171488873713,-0.5099398173102777,-0.01764573195292622,1.410991100730777,-0.45196311311580073,-0.01764573195292622,1.4306165971365505,-0.3932341609234051,-0.01764573195292622,1.4478609734027486,-0.3338507092781025,-0.01764573195292622,1.4626955279669098,-0.27391159607465443,-0.01764573195292622,1.4750955701769715,-0.21351658405164828,-0.01764573195292622,1.4850404613864243,-0.15276619474626177,-0.01764573195292622,1.492513649305304,-0.09176154118608709,-0.01764573195292622,1.4975026955498423,-0.030604159596478116,-0.01764573195292622,1.4999992963449291,0.030604159596478896,-0.01764573195292622,1.4999992963449291,0.09176154118608655,-0.01764573195292622,1.4975026955498423,0.15276619474626124,-0.01764573195292622,1.4925136493053042,0.21351658405164775,-0.01764573195292622,1.4850404613864245,0.2739115960746539,-0.01764573195292622,1.4750955701769715,0.33385070927810195,-0.01764573195292622,1.4626955279669098,0.3932341609234046,-0.01764573195292622,1.4478609734027488,0.45196311311580023,-0.01764573195292622,1.4306165971365505,0.5099398173102773,-0.01764573195292622,1.4109911007307772,0.5670677770043764,-0.01764573195292622,1.389017148887372,0.6232519083471001,-0.01764573195292622,1.3647313150805864,0.6783986983965885,-0.01764573195292622,1.3381740206840451,0.7324163607631974,-0.01764573195292622,1.3093894676933533,0.7852149883788756,-0.01764573195292622,1.27842556515624,0.8367067031386104,-0.01764573195292622,1.2453338494326758,0.8868058021648574,-0.01764573195292622,1.2101693984176896,0.9354289004515248,-0.01764573195292622,1.1729907398696464,0.9824950696500896,-0.01764573195292622,1.133859753996567,1.0279259727668393,-0.01764573195292622,1.092841570462634,1.0716459945470755,-0.01764573195292622,1.050004459986289,1.1135823673292382,-0.01764573195292622,1.0054197207103601,1.153665292159496,-0.01764573195292622,0.9591615595333421,1.191828054965219,-0.01764573195292622,0.9113069685993364,1.228007137593969,-0.01764573195292622,0.8619355971522213,1.2621423235331934,-0.01764573195292622,0.8111296189673514,1.2941767981346708,-0.01764573195292622,0.7589735955814146,1.3240572431768813,-0.01764573195292622,0.7055543355481035,1.3517339256079282,-0.01764573195292622,0.6509607499538427,1.377160780321294,-0.01764573195292622,0.595283704434057,1.4002954868266642,-0.01764573195292622,0.5386158679362917,1.4210995396882042,-0.01764573195292622,0.48105155848189834,1.4395383126130616,-0.01764573195292622,0.4226865861829876,1.4555811160834096,-0.01764573195292622,0.3636180937759631,1.4692012484361228,-0.01764573195292622,0.30394439493701364,1.4803760403050594,-0.01764573195292622,0.2437648106487115,1.4890868923519849,-0.01764573195292622,0.18317950389003046,1.4953193062233334,-0.01764573195292622,0.12228931292495551,1.4990629086812883,-0.01764573195292622,0.06119558346714728,1.500311468869011,-0.01764573195292622,0.0,1.502800891996009,-0.05284928570979635,0.0,1.5015502601087285,-0.05284928570979635,-0.06129712351660677,1.4978004460002394,-0.05284928570979635,-0.12249222402048214,1.4915576908660595,-0.05284928570979635,-0.18348344830613078,1.4828323851560172,-0.05284928570979635,-0.24416928249990236,1.4716390512803739,-0.05284928570979635,-0.3044487210198156,1.4579963194387044,-0.05284928570979635,-0.3642214346893817,1.4419268966117698,-0.05284928570979635,-0.42338793772561684,1.4234575287679874,-0.05284928570979635,-0.48184975332331004,1.4026189563474065,-0.05284928570979635,-0.5395095775599467,1.3794458630972803,-0.05284928570979635,-0.5962714413484851,1.3539768183443899,-0.05284928570979635,-0.6520408701684299,1.326254212800208,-0.05284928570979635,-0.706725041309349,1.296324188005742,-0.05284928570979635,-0.7602329383651135,1.264236559533493,-0.05284928570979635,-0.8124755027217234,1.2300447340743519,-0.05284928570979635,-0.8633657817865827,1.1938056205474328,-0.05284928570979635,-0.9128190737125073,1.155579535380793,-0.05284928570979635,-0.9607530683755898,1.1154301021206923,-0.05284928570979635,-1.0070879843722802,1.073424145536478,-0.05284928570979635,-1.0517467018076558,1.0296315803973515,-0.05284928570979635,-1.0946548906538776,0.9841252951061354,-0.05284928570979635,-1.1357411344651833,0.9369810303837197,-0.05284928570979635,-1.1749370492435172,0.8882772532061065,-0.05284928570979635,-1.2121773972569423,0.8380950262038726,-0.05284928570979635,-1.2474001956214094,0.7865178727414212,-0.05284928570979635,-1.2805468194651495,0.7336316379005844,-0.05284928570979635,-1.311562099503987,0.6795243455999569,-0.05284928570979635,-1.340394413865169,0.6242860520877686,-0.05284928570979635,-1.3669957740068797,0.56800869605215,-0.05284928570979635,-1.3913219045904281,0.5107859455982572,-0.05284928570979635,-1.4133323171721839,0.4527130423469546,-0.05284928570979635,-1.4329903775925938,0.39388664291453795,-0.05284928570979635,-1.450263366950126,0.3344046580373344,-0.05284928570979635,-1.4651225360586513,0.27436608960894426,-0.05284928570979635,-1.4775431532976278,0.21387086590136095,-0.05284928570979635,-1.4875045457754414,0.1530196752442218,-0.05284928570979635,-1.4949901337373948,0.09191379843902275,-0.05284928570979635,-1.499987458161071,0.030654940187218028,-0.05284928570979635,-1.5024882014931493,-0.030654940187217844,-0.05284928570979635,-1.5024882014931493,-0.09191379843902257,-0.05284928570979635,-1.4999874581610713,-0.1530196752442213,-0.05284928570979635,-1.4949901337373948,-0.21387086590136078,-0.05284928570979635,-1.4875045457754414,-0.2743660896089441,-0.05284928570979635,-1.4775431532976278,-0.3344046580373339,-0.05284928570979635,-1.4651225360586513,-0.3938866429145378,-0.05284928570979635,-1.450263366950126,-0.4527130423469541,-0.05284928570979635,-1.432990377592594,-0.510785945598257,-0.05284928570979635,-1.4133323171721839,-0.5680086960521498,-0.05284928570979635,-1.3913219045904284,-0.6242860520877682,-0.05284928570979635,-1.3669957740068799,-0.679524345599957,-0.05284928570979635,-1.340394413865169,-0.7336316379005843,-0.05284928570979635,-1.311562099503987,-0.7865178727414215,-0.05284928570979635,-1.2805468194651493,-0.8380950262038722,-0.05284928570979635,-1.2474001956214096,-0.8882772532061062,-0.05284928570979635,-1.2121773972569425,-0.9369810303837196,-0.05284928570979635,-1.1749370492435174,-0.9841252951061356,-0.05284928570979635,-1.1357411344651833,-1.0296315803973517,-0.05284928570979635,-1.0946548906538776,-1.073424145536478,-0.05284928570979635,-1.0517467018076558,-1.1154301021206923,-0.05284928570979635,-1.0070879843722802,-1.1555795353807927,-0.05284928570979635,-0.9607530683755904,-1.1938056205474328,-0.05284928570979635,-0.912819073712507,-1.2300447340743517,-0.05284928570979635,-0.8633657817865832,-1.264236559533493,-0.05284928570979635,-0.8124755027217233,-1.2963241880057417,-0.05284928570979635,-0.7602329383651136,-1.3262542128002077,-0.05284928570979635,-0.7067250413093492,-1.3539768183443897,-0.05284928570979635,-0.6520408701684302,-1.3794458630972801,-0.05284928570979635,-0.5962714413484852,-1.4026189563474065,-0.05284928570979635,-0.5395095775599468,-1.4234575287679874,-0.05284928570979635,-0.48184975332331004,-1.4419268966117695,-0.05284928570979635,-0.4233879377256174,-1.4579963194387044,-0.05284928570979635,-0.36422143468938156,-1.4716390512803736,-0.05284928570979635,-0.3044487210198161,-1.4828323851560172,-0.05284928570979635,-0.24416928249990277,-1.4915576908660595,-0.05284928570979635,-0.18348344830613117,-1.4978004460002394,-0.05284928570979635,-0.12249222402048245,-1.5015502601087285,-0.05284928570979635,-0.061297123516607016,-1.502800891996009,-0.05284928570979635,-1.8404003021386995e-16,-1.5015502601087285,-0.05284928570979635,0.06129712351660665,-1.4978004460002394,-0.05284928570979635,0.12249222402048207,-1.4915576908660595,-0.05284928570979635,0.1834834483061308,-1.4828323851560175,-0.05284928570979635,0.24416928249990177,-1.4716390512803739,-0.05284928570979635,0.3044487210198151,-1.4579963194387044,-0.05284928570979635,0.36422143468938184,-1.4419268966117698,-0.05284928570979635,0.42338793772561706,-1.4234575287679874,-0.05284928570979635,0.4818497533233097,-1.4026189563474067,-0.05284928570979635,0.5395095775599464,-1.3794458630972803,-0.05284928570979635,0.5962714413484849,-1.35397681834439,-0.05284928570979635,0.6520408701684293,-1.326254212800208,-0.05284928570979635,0.7067250413093489,-1.296324188005742,-0.05284928570979635,0.7602329383651133,-1.264236559533493,-0.05284928570979635,0.8124755027217234,-1.230044734074352,-0.05284928570979635,0.8633657817865824,-1.193805620547433,-0.05284928570979635,0.9128190737125067,-1.1555795353807927,-0.05284928570979635,0.96075306837559,-1.115430102120692,-0.05284928570979635,1.0070879843722804,-1.0734241455364784,-0.05284928570979635,1.0517467018076558,-1.029631580397352,-0.05284928570979635,1.0946548906538773,-0.9841252951061362,-0.05284928570979635,1.1357411344651827,-0.9369810303837197,-0.05284928570979635,1.1749370492435172,-0.8882772532061058,-0.05284928570979635,1.2121773972569427,-0.8380950262038726,-0.05284928570979635,1.2474001956214094,-0.7865178727414218,-0.05284928570979635,1.280546819465149,-0.7336316379005846,-0.05284928570979635,1.3115620995039867,-0.6795243455999561,-0.05284928570979635,1.3403944138651698,-0.6242860520877686,-0.05284928570979635,1.3669957740068799,-0.5680086960521504,-0.05284928570979635,1.391321904590428,-0.5107859455982567,-0.05284928570979635,1.4133323171721839,-0.45271304234695475,-0.05284928570979635,1.4329903775925938,-0.3938866429145388,-0.05284928570979635,1.4502633669501257,-0.33440465803733455,-0.05284928570979635,1.4651225360586513,-0.2743660896089438,-0.05284928570979635,1.4775431532976278,-0.21387086590136078,-0.05284928570979635,1.4875045457754414,-0.15301967524422233,-0.05284928570979635,1.4949901337373945,-0.0919137984390226,-0.05284928570979635,1.4999874581610713,-0.030654940187217216,-0.05284928570979635,1.5024882014931493,0.030654940187217997,-0.05284928570979635,1.5024882014931493,0.09191379843902205,-0.05284928570979635,1.4999874581610713,0.15301967524422178,-0.05284928570979635,1.4949901337373948,0.21387086590136026,-0.05284928570979635,1.4875045457754414,0.27436608960894326,-0.05284928570979635,1.477543153297628,0.33440465803733405,-0.05284928570979635,1.4651225360586513,0.3938866429145383,-0.05284928570979635,1.450263366950126,0.45271304234695425,-0.05284928570979635,1.432990377592594,0.5107859455982562,-0.05284928570979635,1.413332317172184,0.5680086960521487,-0.05284928570979635,1.3913219045904286,0.6242860520877692,-0.05284928570979635,1.3669957740068794,0.6795243455999568,-0.05284928570979635,1.340394413865169,0.7336316379005842,-0.05284928570979635,1.3115620995039872,0.7865178727414203,-0.05284928570979635,1.28054681946515,0.8380950262038721,-0.05284928570979635,1.2474001956214096,0.8882772532061065,-0.05284928570979635,1.212177397256942,0.9369810303837194,-0.05284928570979635,1.1749370492435174,0.9841252951061359,-0.05284928570979635,1.1357411344651833,1.0296315803973513,-0.05284928570979635,1.0946548906538776,1.0734241455364775,-0.05284928570979635,1.0517467018076565,1.115430102120692,-0.05284928570979635,1.0070879843722804,1.1555795353807934,-0.05284928570979635,0.9607530683755894,1.1938056205474326,-0.05284928570979635,0.9128190737125073,1.2300447340743514,-0.05284928570979635,0.8633657817865834,1.2642365595334923,-0.05284928570979635,0.8124755027217244,1.2963241880057417,-0.05284928570979635,0.7602329383651139,1.3262542128002082,-0.05284928570979635,0.7067250413093488,1.3539768183443897,-0.05284928570979635,0.6520408701684304,1.3794458630972797,-0.05284928570979635,0.596271441348486,1.4026189563474065,-0.05284928570979635,0.539509577559947,1.4234575287679871,-0.05284928570979635,0.48184975332331087,1.4419268966117698,-0.05284928570979635,0.423387937725617,1.4579963194387042,-0.05284928570979635,0.3642214346893824,1.4716390512803739,-0.05284928570979635,0.3044487210198156,1.4828323851560172,-0.05284928570979635,0.24416928249990294,1.4915576908660595,-0.05284928570979635,0.18348344830613203,1.4978004460002394,-0.05284928570979635,0.12249222402048261,1.5015502601087285,-0.05284928570979635,0.06129712351660654,1.502800891996009,-0.05284928570979635,0.0,1.5077673360692083,-0.08778954698366052,0.0,1.506512571103915,-0.08778954698366052,-0.06149969774810652,1.502750364640488,-0.08778954698366052,-0.12289703531866518,1.4964869785002968,-0.08778954698366052,-0.18408982290254178,1.4877328374714431,-0.08778954698366052,-0.2449762111438682,1.476502511957731,-0.08778954698366052,-0.30545486065824357,1.4628146937276665,-0.08778954698366052,-0.36542511070213857,1.446692164803854,-0.08778954698366052,-0.42478714671276924,1.4281617595445681,-0.08778954698366052,-0.48344216643958565,1.4072543199806131,-0.08778954698366052,-0.5412925443908674,1.3840046444818053,-0.08778954698366052,-0.5982419943217203,1.358451429838521,-0.08778954698366052,-0.6541957294930278,1.330637206854709,-0.08778954698366052,-0.7090606204346253,1.3006082695595629,-0.08778954698366052,-0.7627453499501103,1.2684145981556805,-0.08778954698366052,-0.8151605651053054,1.2341097758319461,-0.08778954698366052,-0.866219025947399,1.1977508995796038,-0.08778954698366052,-0.9158357507072363,1.1593984851599481,-0.08778954698366052,-0.9639281572430874,1.119116366381813,-0.08778954698366052,-1.0104162004904733,1.0769715888564961,-0.08778954698366052,-1.0552225056892746,1.0330342984069552,-0.08778954698366052,-1.0982724971663849,0.9873776243170089,-0.08778954698366052,-1.139494522459558,0.9400775576148613,-0.08778954698366052,-1.1788199715758612,0.8912128245935335,-0.08778954698366052,-1.2161833911862359,0.8408647557787198,-0.08778954698366052,-1.2515225935661052,0.7891171505621535,-0.08778954698366052,-1.284778760100705,0.7360561377257898,-0.08778954698366052,-1.3158965391828608,0.6817700320889483,-0.08778954698366052,-1.3448241383402781,0.6263491875170099,-0.08778954698366052,-1.3715134104390005,0.5698858465363282,-0.08778954698366052,-1.3959199338195616,0.512473986805646,-0.08778954698366052,-1.4180030862324537,0.4542091646995557,-0.08778954698366052,-1.4377261124498535,0.39518835626434645,-0.08778954698366052,-1.455056185441071,0.3355097958109445,-0.08778954698366052,-1.4699644610099032,0.275272812413597,-0.08778954698366052,-1.4824261258029507,0.21457766458643165,-0.08778954698366052,-1.4924204386089965,0.15352537341305253,-0.08778954698366052,-1.499930764880706,0.09221755440691842,-0.08778954698366052,-1.5049446044211872,0.030756248382347508,-0.08778954698366052,-1.5074536121893372,-0.030756248382347324,-0.08778954698366052,-1.5074536121893372,-0.09221755440691824,-0.08778954698366052,-1.5049446044211874,-0.15352537341305203,-0.08778954698366052,-1.499930764880706,-0.2145776645864315,-0.08778954698366052,-1.4924204386089965,-0.27527281241359686,-0.08778954698366052,-1.4824261258029507,-0.335509795810944,-0.08778954698366052,-1.4699644610099032,-0.3951883562643463,-0.08778954698366052,-1.4550561854410713,-0.4542091646995552,-0.08778954698366052,-1.4377261124498537,-0.5124739868056458,-0.08778954698366052,-1.4180030862324537,-0.5698858465363281,-0.08778954698366052,-1.3959199338195618,-0.6263491875170094,-0.08778954698366052,-1.3715134104390008,-0.6817700320889483,-0.08778954698366052,-1.3448241383402781,-0.7360561377257897,-0.08778954698366052,-1.3158965391828608,-0.7891171505621538,-0.08778954698366052,-1.2847787601007046,-0.8408647557787194,-0.08778954698366052,-1.2515225935661056,-0.8912128245935331,-0.08778954698366052,-1.216183391186236,-0.9400775576148611,-0.08778954698366052,-1.1788199715758614,-0.9873776243170092,-0.08778954698366052,-1.139494522459558,-1.0330342984069554,-0.08778954698366052,-1.0982724971663849,-1.0769715888564961,-0.08778954698366052,-1.0552225056892746,-1.119116366381813,-0.08778954698366052,-1.0104162004904733,-1.159398485159948,-0.08778954698366052,-0.963928157243088,-1.1977508995796038,-0.08778954698366052,-0.9158357507072362,-1.234109775831946,-0.08778954698366052,-0.8662190259473995,-1.2684145981556805,-0.08778954698366052,-0.8151605651053053,-1.3006082695595629,-0.08778954698366052,-0.7627453499501105,-1.3306372068547088,-0.08778954698366052,-0.7090606204346255,-1.3584514298385209,-0.08778954698366052,-0.6541957294930282,-1.384004644481805,-0.08778954698366052,-0.5982419943217203,-1.4072543199806131,-0.08778954698366052,-0.5412925443908675,-1.4281617595445681,-0.08778954698366052,-0.48344216643958565,-1.4466921648038538,-0.08778954698366052,-0.4247871467127698,-1.4628146937276665,-0.08778954698366052,-0.36542511070213846,-1.476502511957731,-0.08778954698366052,-0.305454860658244,-1.4877328374714431,-0.08778954698366052,-0.2449762111438686,-1.4964869785002968,-0.08778954698366052,-0.18408982290254217,-1.502750364640488,-0.08778954698366052,-0.12289703531866548,-1.506512571103915,-0.08778954698366052,-0.061499697748106756,-1.5077673360692083,-0.08778954698366052,-1.8464824419760875e-16,-1.506512571103915,-0.08778954698366052,0.061499697748106395,-1.502750364640488,-0.08778954698366052,0.12289703531866511,-1.4964869785002968,-0.08778954698366052,0.1840898229025418,-1.4877328374714434,-0.08778954698366052,0.2449762111438676,-1.4765025119577313,-0.08778954698366052,0.305454860658243,-1.4628146937276665,-0.08778954698366052,0.36542511070213873,-1.446692164803854,-0.08778954698366052,0.42478714671276946,-1.4281617595445681,-0.08778954698366052,0.4834421664395853,-1.4072543199806133,-0.08778954698366052,0.5412925443908672,-1.3840046444818053,-0.08778954698366052,0.59824199432172,-1.3584514298385213,-0.08778954698366052,0.6541957294930272,-1.330637206854709,-0.08778954698366052,0.7090606204346251,-1.3006082695595629,-0.08778954698366052,0.7627453499501101,-1.2684145981556805,-0.08778954698366052,0.8151605651053054,-1.2341097758319464,-0.08778954698366052,0.8662190259473986,-1.197750899579604,-0.08778954698366052,0.9158357507072359,-1.159398485159948,-0.08778954698366052,0.9639281572430877,-1.119116366381813,-0.08778954698366052,1.0104162004904733,-1.0769715888564966,-0.08778954698366052,1.0552225056892743,-1.0330342984069556,-0.08778954698366052,1.0982724971663846,-0.9873776243170098,-0.08778954698366052,1.1394945224595574,-0.9400775576148613,-0.08778954698366052,1.1788199715758612,-0.8912128245935328,-0.08778954698366052,1.2161833911862363,-0.8408647557787198,-0.08778954698366052,1.2515225935661052,-0.7891171505621541,-0.08778954698366052,1.2847787601007046,-0.73605613772579,-0.08778954698366052,1.3158965391828605,-0.6817700320889475,-0.08778954698366052,1.3448241383402786,-0.6263491875170097,-0.08778954698366052,1.3715134104390008,-0.5698858465363288,-0.08778954698366052,1.3959199338195616,-0.5124739868056456,-0.08778954698366052,1.4180030862324537,-0.45420916469955586,-0.08778954698366052,1.4377261124498535,-0.3951883562643473,-0.08778954698366052,1.455056185441071,-0.3355097958109447,-0.08778954698366052,1.4699644610099032,-0.2752728124135965,-0.08778954698366052,1.4824261258029507,-0.2145776645864315,-0.08778954698366052,1.4924204386089965,-0.15352537341305306,-0.08778954698366052,1.4999307648807059,-0.09221755440691827,-0.08778954698366052,1.5049446044211874,-0.030756248382346692,-0.08778954698366052,1.5074536121893372,0.030756248382347477,-0.08778954698366052,1.5074536121893372,0.09221755440691771,-0.08778954698366052,1.5049446044211874,0.1535253734130525,-0.08778954698366052,1.499930764880706,0.21457766458643093,-0.08778954698366052,1.4924204386089968,0.275272812413596,-0.08778954698366052,1.4824261258029507,0.33550979581094414,-0.08778954698366052,1.4699644610099032,0.3951883562643468,-0.08778954698366052,1.455056185441071,0.45420916469955536,-0.08778954698366052,1.4377261124498537,0.512473986805645,-0.08778954698366052,1.418003086232454,0.569885846536327,-0.08778954698366052,1.3959199338195623,0.6263491875170104,-0.08778954698366052,1.3715134104390003,0.6817700320889482,-0.08778954698366052,1.3448241383402781,0.7360561377257896,-0.08778954698366052,1.315896539182861,0.7891171505621526,-0.08778954698366052,1.2847787601007055,0.8408647557787192,-0.08778954698366052,1.2515225935661056,0.8912128245935335,-0.08778954698366052,1.2161833911862356,0.9400775576148609,-0.08778954698366052,1.1788199715758614,0.9873776243170095,-0.08778954698366052,1.1394945224595578,1.033034298406955,-0.08778954698366052,1.098272497166385,1.0769715888564957,-0.08778954698366052,1.0552225056892752,1.119116366381813,-0.08778954698366052,1.0104162004904733,1.1593984851599486,-0.08778954698366052,0.963928157243087,1.1977508995796036,-0.08778954698366052,0.9158357507072363,1.2341097758319457,-0.08778954698366052,0.8662190259473996,1.2684145981556798,-0.08778954698366052,0.8151605651053064,1.3006082695595629,-0.08778954698366052,0.7627453499501107,1.330637206854709,-0.08778954698366052,0.709060620434625,1.3584514298385209,-0.08778954698366052,0.6541957294930283,1.3840046444818046,-0.08778954698366052,0.5982419943217212,1.4072543199806131,-0.08778954698366052,0.5412925443908677,1.428161759544568,-0.08778954698366052,0.4834421664395865,1.446692164803854,-0.08778954698366052,0.4247871467127694,1.4628146937276663,-0.08778954698366052,0.3654251107021393,1.476502511957731,-0.08778954698366052,0.30545486065824357,1.4877328374714431,-0.08778954698366052,0.24497621114386878,1.4964869785002968,-0.08778954698366052,0.18408982290254303,1.502750364640488,-0.08778954698366052,0.12289703531866565,1.506512571103915,-0.08778954698366052,0.06149969774810628,1.5077673360692083,-0.08778954698366052,0.0,1.515186058514056,-0.12229244515039998,0.0,1.5139251196830816,-0.12229244515039998,-0.0618022969470152,1.5101444018984018,-0.12229244515039998,-0.12350173006997922,1.5038501977916534,-0.12229244515039998,-0.18499560675152446,1.4950529834444024,-0.12229244515039998,-0.2461815765026939,1.4837674009517396,-0.12229244515039998,-0.30695780131522904,1.47001223405196,-0.12229244515039998,-0.36722312516088423,1.4538103768628814,-0.12229244515039998,-0.42687724235565305,1.435188795776848,-0.12229244515039998,-0.48582086450867984,1.414178484577829,-0.12229244515039998,-0.5439558857779877,1.3908144128553253,-0.12229244515039998,-0.6011855461579707,1.3651354678009375,-0.12229244515039998,-0.6574145925268737,1.337184389484476,-0.12229244515039998,-0.7125494371862136,1.307007699717329,-0.12229244515039998,-0.7664983136282665,1.2746556246215024,-0.12229244515039998,-0.8191714292723641,1.2401820110331936,-0.12229244515039998,-0.8704811149157828,1.2036442368800477,-0.12229244515039998,-0.9203419706504795,1.1651031156812606,-0.12229244515039998,-0.9686710080028095,1.1246227953294767,-0.12229244515039998,-1.0153877880596527,1.0822706513229523,-0.12229244515039998,-1.0604145553510442,1.0381171746256885,-0.12229244515039998,-1.1036763672664818,0.9922358543421782,-0.12229244515039998,-1.145101218789504,0.9447030554020442,-0.12229244515039998,-1.184620162342935,0.8955978914581499,-0.12229244515039998,-1.2221674225453223,0.8450020932097321,-0.12229244515039998,-1.2576805056875657,0.7929998723697183,-0.12229244515039998,-1.2911003037475326,0.7396777815026407,-0.12229244515039998,-1.3223711927695234,0.6851245699664359,-0.12229244515039998,-1.3514411254448588,0.6294310361978936,-0.12229244515039998,-1.3782617177394887,0.5726898765876239,-0.12229244515039998,-1.4027883294244403,0.5149955311960606,-0.12229244515039998,-1.4249801383750749,0.4564440265673004,-0.12229244515039998,-1.4448002085154854,0.39713281590239913,-0.12229244515039998,-1.4622155512949488,0.33716061685813464,-0.12229244515039998,-1.477197180594115,0.2766272472412098,-0.12229244515039998,-1.4897201609695396,0.21563345887136454,-0.12229244515039998,-1.49976364915627,0.15428076988991368,-0.12229244515039998,-1.5073109287594035,0.0926712957928216,-0.12229244515039998,-1.512349438076875,0.030907579469535193,-0.12229244515039998,-1.514870791007172,-0.03090757946953501,-0.12229244515039998,-1.514870791007172,-0.0926712957928214,-0.12229244515039998,-1.5123494380768752,-0.15428076988991318,-0.12229244515039998,-1.5073109287594035,-0.21563345887136437,-0.12229244515039998,-1.49976364915627,-0.27662724724120963,-0.12229244515039998,-1.4897201609695396,-0.33716061685813414,-0.12229244515039998,-1.477197180594115,-0.39713281590239896,-0.12229244515039998,-1.462215551294949,-0.4564440265672999,-0.12229244515039998,-1.4448002085154854,-0.5149955311960603,-0.12229244515039998,-1.424980138375075,-0.5726898765876237,-0.12229244515039998,-1.4027883294244405,-0.6294310361978932,-0.12229244515039998,-1.378261717739489,-0.6851245699664359,-0.12229244515039998,-1.3514411254448588,-0.7396777815026407,-0.12229244515039998,-1.3223711927695234,-0.7929998723697186,-0.12229244515039998,-1.2911003037475324,-0.8450020932097317,-0.12229244515039998,-1.2576805056875662,-0.8955978914581496,-0.12229244515039998,-1.2221674225453225,-0.9447030554020441,-0.12229244515039998,-1.1846201623429355,-0.9922358543421784,-0.12229244515039998,-1.145101218789504,-1.0381171746256888,-0.12229244515039998,-1.1036763672664818,-1.0822706513229523,-0.12229244515039998,-1.0604145553510442,-1.1246227953294767,-0.12229244515039998,-1.0153877880596527,-1.1651031156812603,-0.12229244515039998,-0.96867100800281,-1.2036442368800477,-0.12229244515039998,-0.9203419706504793,-1.2401820110331931,-0.12229244515039998,-0.8704811149157833,-1.2746556246215026,-0.12229244515039998,-0.819171429272364,-1.307007699717329,-0.12229244515039998,-0.7664983136282667,-1.3371843894844757,-0.12229244515039998,-0.7125494371862138,-1.3651354678009375,-0.12229244515039998,-0.657414592526874,-1.390814412855325,-0.12229244515039998,-0.6011855461579708,-1.414178484577829,-0.12229244515039998,-0.5439558857779877,-1.435188795776848,-0.12229244515039998,-0.48582086450867984,-1.4538103768628812,-0.12229244515039998,-0.42687724235565366,-1.47001223405196,-0.12229244515039998,-0.3672231251608841,-1.4837674009517394,-0.12229244515039998,-0.3069578013152295,-1.4950529834444024,-0.12229244515039998,-0.24618157650269432,-1.5038501977916534,-0.12229244515039998,-0.18499560675152485,-1.5101444018984018,-0.12229244515039998,-0.12350173006997954,-1.5139251196830816,-0.12229244515039998,-0.061802296947015445,-1.515186058514056,-0.12229244515039998,-1.8555677566719328e-16,-1.5139251196830816,-0.12229244515039998,0.06180229694701508,-1.5101444018984018,-0.12229244515039998,0.12350173006997915,-1.5038501977916534,-0.12229244515039998,0.1849956067515245,-1.4950529834444024,-0.12229244515039998,0.24618157650269332,-1.4837674009517399,-0.12229244515039998,0.3069578013152285,-1.47001223405196,-0.12229244515039998,0.3672231251608844,-1.4538103768628814,-0.12229244515039998,0.42687724235565333,-1.435188795776848,-0.12229244515039998,0.4858208645086795,-1.4141784845778291,-0.12229244515039998,0.5439558857779874,-1.3908144128553253,-0.12229244515039998,0.6011855461579705,-1.365135467800938,-0.12229244515039998,0.6574145925268731,-1.337184389484476,-0.12229244515039998,0.7125494371862134,-1.307007699717329,-0.12229244515039998,0.7664983136282664,-1.2746556246215024,-0.12229244515039998,0.8191714292723641,-1.2401820110331936,-0.12229244515039998,0.8704811149157825,-1.203644236880048,-0.12229244515039998,0.920341970650479,-1.1651031156812606,-0.12229244515039998,0.9686710080028097,-1.1246227953294765,-0.12229244515039998,1.0153877880596527,-1.0822706513229527,-0.12229244515039998,1.060414555351044,-1.038117174625689,-0.12229244515039998,1.1036763672664816,-0.9922358543421791,-0.12229244515039998,1.1451012187895033,-0.9447030554020442,-0.12229244515039998,1.184620162342935,-0.8955978914581493,-0.12229244515039998,1.2221674225453225,-0.8450020932097321,-0.12229244515039998,1.2576805056875657,-0.7929998723697189,-0.12229244515039998,1.2911003037475322,-0.739677781502641,-0.12229244515039998,1.3223711927695232,-0.6851245699664351,-0.12229244515039998,1.3514411254448593,-0.6294310361978935,-0.12229244515039998,1.378261717739489,-0.5726898765876244,-0.12229244515039998,1.40278832942444,-0.5149955311960601,-0.12229244515039998,1.424980138375075,-0.45644402656730054,-0.12229244515039998,1.4448002085154854,-0.39713281590239996,-0.12229244515039998,1.4622155512949486,-0.33716061685813487,-0.12229244515039998,1.477197180594115,-0.27662724724120935,-0.12229244515039998,1.4897201609695396,-0.21563345887136437,-0.12229244515039998,1.49976364915627,-0.1542807698899142,-0.12229244515039998,1.5073109287594035,-0.09267129579282143,-0.12229244515039998,1.5123494380768752,-0.03090757946953437,-0.12229244515039998,1.514870791007172,0.030907579469535162,-0.12229244515039998,1.514870791007172,0.09267129579282088,-0.12229244515039998,1.5123494380768752,0.15428076988991368,-0.12229244515039998,1.5073109287594035,0.21563345887136381,-0.12229244515039998,1.4997636491562703,0.2766272472412088,-0.12229244515039998,1.4897201609695399,0.3371606168581343,-0.12229244515039998,1.477197180594115,0.39713281590239946,-0.12229244515039998,1.4622155512949488,0.45644402656730004,-0.12229244515039998,1.4448002085154854,0.5149955311960597,-0.12229244515039998,1.4249801383750753,0.5726898765876226,-0.12229244515039998,1.402788329424441,0.6294310361978942,-0.12229244515039998,1.3782617177394885,0.6851245699664358,-0.12229244515039998,1.3514411254448588,0.7396777815026405,-0.12229244515039998,1.3223711927695234,0.7929998723697174,-0.12229244515039998,1.2911003037475333,0.8450020932097316,-0.12229244515039998,1.2576805056875662,0.8955978914581499,-0.12229244515039998,1.222167422545322,0.9447030554020439,-0.12229244515039998,1.1846201623429355,0.9922358543421788,-0.12229244515039998,1.145101218789504,1.0381171746256883,-0.12229244515039998,1.103676367266482,1.0822706513229519,-0.12229244515039998,1.060414555351045,1.1246227953294765,-0.12229244515039998,1.0153877880596527,1.165103115681261,-0.12229244515039998,0.968671008002809,1.2036442368800475,-0.12229244515039998,0.9203419706504795,1.240182011033193,-0.12229244515039998,0.8704811149157835,1.2746556246215017,-0.12229244515039998,0.8191714292723651,1.307007699717329,-0.12229244515039998,0.7664983136282668,1.3371843894844762,-0.12229244515039998,0.7125494371862133,1.3651354678009375,-0.12229244515039998,0.6574145925268742,1.3908144128553246,-0.12229244515039998,0.6011855461579717,1.414178484577829,-0.12229244515039998,0.543955885777988,1.4351887957768477,-0.12229244515039998,0.48582086450868067,1.4538103768628814,-0.12229244515039998,0.4268772423556532,1.4700122340519597,-0.12229244515039998,0.36722312516088496,1.4837674009517396,-0.12229244515039998,0.30695780131522904,1.4950529834444024,-0.12229244515039998,0.24618157650269448,1.5038501977916534,-0.12229244515039998,0.1849956067515257,1.5101444018984018,-0.12229244515039998,0.1235017300699797,1.5139251196830816,-0.12229244515039998,0.06180229694701496,1.515186058514056,-0.12229244515039998,0.0,1.5250200996285046,-0.15618608850689697,0.0,1.5237509769020694,-0.15618608850689697,-0.062203413579345064,1.5199457210522935,-0.15618608850689697,-0.12430329571559029,1.5136106655520012,-0.15618608850689697,-0.18619628728350546,1.5047563544758715,-0.15618608850689697,-0.24777937350679352,1.4933975249508662,-0.15618608850689697,-0.3089500554160192,1.4795530826277359,-0.15618608850689697,-0.3696065204480281,1.463246070214431,-0.15618608850689697,-0.4296478119029104,1.4445036291237883,-0.15618608850689697,-0.4889739969764645,1.423356954299327,-0.15618608850689697,-0.547486333088487,1.399841242294345,-0.15618608850689697,-0.6050874322300533,1.3739956326907283,-0.15618608850689697,-0.6616814230562466,1.3458631429549806,-0.15618608850689697,-0.7171741104545504,1.315490596839895,-0.15618608850689697,-0.7714731323233172,1.2829285464510394,-0.15618608850689697,-0.8244881132993716,1.248231188107764,-0.15618608850689697,-0.8761308151788831,1.2114562721387774,-0.15618608850689697,-0.9263152837811491,1.172665006762425,-0.15618608850689697,-0.9749579920108435,1.1319219562116474,-0.15618608850689697,-1.021977978880627,1.0892949332731894,-0.15618608850689697,-1.0672969842627178,1.044854886419906,-0.15618608850689697,-1.1108395791451522,0.9986757817240342,-0.15618608850689697,-1.152533291175931,0.9508344797479625,-0.15618608850689697,-1.192308725286096,0.9014106076174119,-0.15618608850689697,-1.2300996791909762,0.8504864264899427,-0.15618608850689697,-1.265843253577354,0.7981466946393767,-0.15618608850689697,-1.2994799567931672,0.7444785263840151,-0.15618608850689697,-1.330953803865491,0.6895712470934524,-0.15618608850689697,-1.3602124096820007,0.6335162445153134,-0.15618608850689697,-1.387207076180823,0.5764068166693683,-0.15618608850689697,-1.4118928734036513,0.5183380165621853,-0.15618608850689697,-1.4342287142772292,0.45940649398078054,-0.15618608850689697,-1.4541774229987279,0.3997103346285884,-0.15618608850689697,-1.471705796911202,0.33934889687148656,-0.15618608850689697,-1.4867846617661347,0.2784226463656016,-0.15618608850689697,-1.4993889202810955,0.21703298884214012,-0.15618608850689697,-1.5094975939116901,0.15528210132755516,-0.15618608850689697,-1.517093857768277,0.09327276207997155,-0.15618608850689697,-1.5221650686193358,0.03110817952491689,-0.15618608850689697,-1.5247027859348783,-0.031108179524916707,-0.15618608850689697,-1.5247027859348783,-0.09327276207997137,-0.15618608850689697,-1.5221650686193358,-0.15528210132755466,-0.15618608850689697,-1.517093857768277,-0.21703298884213995,-0.15618608850689697,-1.5094975939116901,-0.27842264636560143,-0.15618608850689697,-1.4993889202810955,-0.33934889687148606,-0.15618608850689697,-1.4867846617661347,-0.39971033462858824,-0.15618608850689697,-1.471705796911202,-0.45940649398078004,-0.15618608850689697,-1.454177422998728,-0.518338016562185,-0.15618608850689697,-1.4342287142772292,-0.5764068166693681,-0.15618608850689697,-1.4118928734036515,-0.633516244515313,-0.15618608850689697,-1.387207076180823,-0.6895712470934525,-0.15618608850689697,-1.3602124096820007,-0.744478526384015,-0.15618608850689697,-1.330953803865491,-0.7981466946393772,-0.15618608850689697,-1.299479956793167,-0.8504864264899423,-0.15618608850689697,-1.2658432535773543,-0.9014106076174114,-0.15618608850689697,-1.2300996791909764,-0.9508344797479623,-0.15618608850689697,-1.1923087252860964,-0.9986757817240344,-0.15618608850689697,-1.152533291175931,-1.0448548864199063,-0.15618608850689697,-1.1108395791451522,-1.0892949332731894,-0.15618608850689697,-1.0672969842627178,-1.1319219562116474,-0.15618608850689697,-1.021977978880627,-1.1726650067624245,-0.15618608850689697,-0.9749579920108441,-1.2114562721387774,-0.15618608850689697,-0.9263152837811489,-1.2482311881077635,-0.15618608850689697,-0.8761308151788837,-1.2829285464510396,-0.15618608850689697,-0.8244881132993713,-1.315490596839895,-0.15618608850689697,-0.7714731323233174,-1.3458631429549806,-0.15618608850689697,-0.7171741104545505,-1.373995632690728,-0.15618608850689697,-0.6616814230562468,-1.3998412422943447,-0.15618608850689697,-0.6050874322300535,-1.423356954299327,-0.15618608850689697,-0.547486333088487,-1.4445036291237883,-0.15618608850689697,-0.4889739969764645,-1.4632460702144308,-0.15618608850689697,-0.429647811902911,-1.4795530826277359,-0.15618608850689697,-0.36960652044802794,-1.493397524950866,-0.15618608850689697,-0.30895005541601966,-1.5047563544758715,-0.15618608850689697,-0.24777937350679397,-1.5136106655520012,-0.15618608850689697,-0.18619628728350585,-1.5199457210522935,-0.15618608850689697,-0.12430329571559061,-1.5237509769020694,-0.15618608850689697,-0.06220341357934531,-1.5250200996285046,-0.15618608850689697,-1.867610983645426e-16,-1.5237509769020694,-0.15618608850689697,0.06220341357934493,-1.5199457210522935,-0.15618608850689697,0.12430329571559023,-1.5136106655520012,-0.15618608850689697,0.18619628728350548,-1.5047563544758717,-0.15618608850689697,0.24777937350679294,-1.4933975249508662,-0.15618608850689697,0.30895005541601867,-1.4795530826277359,-0.15618608850689697,0.3696065204480283,-1.463246070214431,-0.15618608850689697,0.4296478119029107,-1.4445036291237883,-0.15618608850689697,0.48897399697646415,-1.4233569542993272,-0.15618608850689697,0.5474863330884867,-1.399841242294345,-0.15618608850689697,0.6050874322300531,-1.3739956326907286,-0.15618608850689697,0.6616814230562458,-1.3458631429549806,-0.15618608850689697,0.7171741104545502,-1.315490596839895,-0.15618608850689697,0.771473132323317,-1.2829285464510394,-0.15618608850689697,0.8244881132993716,-1.2482311881077641,-0.15618608850689697,0.8761308151788828,-1.2114562721387778,-0.15618608850689697,0.9263152837811486,-1.1726650067624247,-0.15618608850689697,0.9749579920108438,-1.1319219562116474,-0.15618608850689697,1.0219779788806271,-1.0892949332731896,-0.15618608850689697,1.0672969842627176,-1.0448548864199065,-0.15618608850689697,1.1108395791451522,-0.998675781724035,-0.15618608850689697,1.1525332911759303,-0.9508344797479625,-0.15618608850689697,1.192308725286096,-0.9014106076174111,-0.15618608850689697,1.2300996791909766,-0.8504864264899427,-0.15618608850689697,1.265843253577354,-0.7981466946393775,-0.15618608850689697,1.2994799567931667,-0.7444785263840152,-0.15618608850689697,1.3309538038654907,-0.6895712470934516,-0.15618608850689697,1.3602124096820012,-0.6335162445153133,-0.15618608850689697,1.387207076180823,-0.5764068166693688,-0.15618608850689697,1.4118928734036513,-0.5183380165621848,-0.15618608850689697,1.4342287142772292,-0.4594064939807807,-0.15618608850689697,1.4541774229987279,-0.3997103346285893,-0.15618608850689697,1.4717057969112017,-0.3393488968714868,-0.15618608850689697,1.4867846617661347,-0.2784226463656011,-0.15618608850689697,1.4993889202810955,-0.21703298884213995,-0.15618608850689697,1.5094975939116901,-0.1552821013275557,-0.15618608850689697,1.5170938577682769,-0.0932727620799714,-0.15618608850689697,1.5221650686193358,-0.031108179524916065,-0.15618608850689697,1.5247027859348783,0.03110817952491686,-0.15618608850689697,1.5247027859348783,0.09327276207997084,-0.15618608850689697,1.5221650686193358,0.15528210132755516,-0.15618608850689697,1.517093857768277,0.2170329888421394,-0.15618608850689697,1.5094975939116901,0.2784226463656006,-0.15618608850689697,1.4993889202810957,0.3393488968714862,-0.15618608850689697,1.4867846617661347,0.3997103346285888,-0.15618608850689697,1.471705796911202,0.4594064939807802,-0.15618608850689697,1.454177422998728,0.5183380165621843,-0.15618608850689697,1.4342287142772294,0.576406816669367,-0.15618608850689697,1.411892873403652,0.633516244515314,-0.15618608850689697,1.3872070761808228,0.6895712470934524,-0.15618608850689697,1.3602124096820007,0.7444785263840149,-0.15618608850689697,1.330953803865491,0.7981466946393759,-0.15618608850689697,1.2994799567931679,0.8504864264899421,-0.15618608850689697,1.2658432535773543,0.9014106076174119,-0.15618608850689697,1.2300996791909762,0.9508344797479622,-0.15618608850689697,1.1923087252860964,0.9986757817240347,-0.15618608850689697,1.1525332911759307,1.044854886419906,-0.15618608850689697,1.1108395791451524,1.0892949332731887,-0.15618608850689697,1.0672969842627185,1.1319219562116474,-0.15618608850689697,1.0219779788806271,1.1726650067624251,-0.15618608850689697,0.9749579920108431,1.2114562721387774,-0.15618608850689697,0.9263152837811491,1.2482311881077635,-0.15618608850689697,0.8761308151788838,1.2829285464510387,-0.15618608850689697,0.8244881132993725,1.315490596839895,-0.15618608850689697,0.7714731323233175,1.3458631429549808,-0.15618608850689697,0.71717411045455,1.373995632690728,-0.15618608850689697,0.6616814230562469,1.3998412422943445,-0.15618608850689697,0.6050874322300542,1.423356954299327,-0.15618608850689697,0.5474863330884874,1.4445036291237878,-0.15618608850689697,0.4889739969764653,1.463246070214431,-0.15618608850689697,0.42964781190291057,1.4795530826277357,-0.15618608850689697,0.36960652044802883,1.4933975249508662,-0.15618608850689697,0.3089500554160192,1.5047563544758715,-0.15618608850689697,0.24777937350679413,1.5136106655520012,-0.15618608850689697,0.1861962872835067,1.5199457210522935,-0.15618608850689697,0.12430329571559078,1.5237509769020694,-0.15618608850689697,0.06220341357934482,1.5250200996285046,-0.15618608850689697,0.0,1.5372204667143894,-0.18930162062685166,0.0,1.5359411908344691,-0.18930162062685166,-0.06270104930221061,1.5321054924231594,-0.18930162062685166,-0.12529773889577192,1.5257197556219244,-0.18930162062685166,-0.18768588276847056,1.5167946088594666,-0.18930162062685166,-0.24976164201186427,1.5053449071617568,-0.18930162062685166,-0.31142169765089733,1.4913897074273093,-0.18930162062685166,-0.37256342260813796,1.474952236708855,-0.18930162062685166,-0.43308505251642077,1.4560598535542046,-0.18930162062685166,-0.4928858550955925,1.4347440024706455,-0.18930162062685166,-0.5518662978114515,1.4110401615886627,-0.18930162062685166,-0.6099282135378279,1.3849877836120918,-0.18930162062685166,-0.666974963946077,1.3566302301529876,-0.18930162062685166,-0.7229116003500408,1.3260146995604993,-0.18930162062685166,-0.7776450217387647,1.2931921483638773,-0.18930162062685166,-0.8310841297339426,1.2582172064603578,-0.18930162062685166,-0.8831399802141779,1.2211480861890913,-0.18930162062685166,-0.9337259313536949,1.182046485442449,-0.18930162062685166,-0.9827577878291066,1.1409774849759693,-0.18930162062685166,-1.030153940954223,1.0980094400878633,-0.18930162062685166,-1.0758355045096542,1.0532138668483666,-0.18930162062685166,-1.1197264460411376,1.0066653230682985,-0.18930162062685166,-1.161753713408054,0.9584412842049446,-0.18930162062685166,-1.2018473563715075,0.9086220144118022,-0.18930162062685166,-1.2399406430195938,0.8572904329468197,-0.18930162062685166,-1.2759701708360816,0.8045319761614748,-0.18930162062685166,-1.3098759722276447,0.7504344553003987,-0.18930162062685166,-1.3416016143340015,0.6950879103482259,-0.18930162062685166,-1.3710942929548433,0.638584460166924,-0.18930162062685166,-1.398304920437214,0.581018149173042,-0.18930162062685166,-1.423188207377063,0.5224847908100582,-0.18930162062685166,-1.4457027379989895,0.4630818080763588,-0.18930162062685166,-1.465811039088714,0.40290807137427365,-0.18930162062685166,-1.483479642363544,0.3420637339500475,-0.18930162062685166,-1.498679140177028,0.28065006519864605,-0.18930162062685166,-1.5113842344650816,0.21876928211084254,-0.18930162062685166,-1.521573778852117,0.15652437914312312,-0.18930162062685166,-1.5292308138471005,0.09401895679358042,-0.18930162062685166,-1.5343425950709482,0.031357049169107185,-0.18930162062685166,-1.5369006144682913,-0.031357049169107,-0.18930162062685166,-1.5369006144682913,-0.09401895679358023,-0.18930162062685166,-1.5343425950709484,-0.15652437914312262,-0.18930162062685166,-1.5292308138471005,-0.21876928211084237,-0.18930162062685166,-1.521573778852117,-0.2806500651986458,-0.18930162062685166,-1.5113842344650816,-0.342063733950047,-0.18930162062685166,-1.498679140177028,-0.4029080713742735,-0.18930162062685166,-1.4834796423635441,-0.4630818080763583,-0.18930162062685166,-1.4658110390887142,-0.5224847908100579,-0.18930162062685166,-1.4457027379989895,-0.5810181491730418,-0.18930162062685166,-1.423188207377063,-0.6385844601669236,-0.18930162062685166,-1.3983049204372142,-0.6950879103482259,-0.18930162062685166,-1.3710942929548433,-0.7504344553003987,-0.18930162062685166,-1.3416016143340015,-0.8045319761614751,-0.18930162062685166,-1.3098759722276445,-0.8572904329468194,-0.18930162062685166,-1.275970170836082,-0.9086220144118019,-0.18930162062685166,-1.239940643019594,-0.9584412842049445,-0.18930162062685166,-1.201847356371508,-1.0066653230682987,-0.18930162062685166,-1.161753713408054,-1.0532138668483666,-0.18930162062685166,-1.1197264460411376,-1.0980094400878633,-0.18930162062685166,-1.0758355045096542,-1.1409774849759693,-0.18930162062685166,-1.030153940954223,-1.1820464854424486,-0.18930162062685166,-0.982757787829107,-1.2211480861890913,-0.18930162062685166,-0.9337259313536947,-1.2582172064603574,-0.18930162062685166,-0.8831399802141785,-1.2931921483638775,-0.18930162062685166,-0.8310841297339425,-1.326014699560499,-0.18930162062685166,-0.7776450217387648,-1.3566302301529873,-0.18930162062685166,-0.722911600350041,-1.3849877836120916,-0.18930162062685166,-0.6669749639460772,-1.4110401615886625,-0.18930162062685166,-0.6099282135378279,-1.4347440024706455,-0.18930162062685166,-0.5518662978114515,-1.4560598535542046,-0.18930162062685166,-0.4928858550955925,-1.4749522367088548,-0.18930162062685166,-0.4330850525164214,-1.4913897074273093,-0.18930162062685166,-0.37256342260813785,-1.5053449071617566,-0.18930162062685166,-0.31142169765089783,-1.5167946088594666,-0.18930162062685166,-0.2497616420118647,-1.5257197556219244,-0.18930162062685166,-0.18768588276847092,-1.5321054924231594,-0.18930162062685166,-0.12529773889577225,-1.5359411908344691,-0.18930162062685166,-0.06270104930221086,-1.5372204667143894,-0.18930162062685166,-1.8825521241455774e-16,-1.5359411908344691,-0.18930162062685166,0.06270104930221049,-1.5321054924231594,-0.18930162062685166,0.12529773889577187,-1.5257197556219244,-0.18930162062685166,0.18768588276847056,-1.5167946088594668,-0.18930162062685166,0.24976164201186368,-1.505344907161757,-0.18930162062685166,0.3114216976508968,-1.4913897074273093,-0.18930162062685166,0.37256342260813813,-1.474952236708855,-0.18930162062685166,0.433085052516421,-1.4560598535542046,-0.18930162062685166,0.4928858550955922,-1.4347440024706457,-0.18930162062685166,0.5518662978114511,-1.4110401615886627,-0.18930162062685166,0.6099282135378276,-1.384987783612092,-0.18930162062685166,0.6669749639460762,-1.3566302301529876,-0.18930162062685166,0.7229116003500407,-1.3260146995604993,-0.18930162062685166,0.7776450217387645,-1.2931921483638773,-0.18930162062685166,0.8310841297339426,-1.258217206460358,-0.18930162062685166,0.8831399802141776,-1.2211480861890918,-0.18930162062685166,0.9337259313536944,-1.1820464854424488,-0.18930162062685166,0.9827577878291067,-1.140977484975969,-0.18930162062685166,1.030153940954223,-1.0980094400878635,-0.18930162062685166,1.075835504509654,-1.0532138668483668,-0.18930162062685166,1.1197264460411374,-1.0066653230682994,-0.18930162062685166,1.1617537134080533,-0.9584412842049446,-0.18930162062685166,1.2018473563715075,-0.9086220144118015,-0.18930162062685166,1.2399406430195943,-0.8572904329468197,-0.18930162062685166,1.2759701708360816,-0.8045319761614754,-0.18930162062685166,1.3098759722276443,-0.7504344553003989,-0.18930162062685166,1.3416016143340015,-0.6950879103482251,-0.18930162062685166,1.3710942929548438,-0.6385844601669239,-0.18930162062685166,1.3983049204372142,-0.5810181491730425,-0.18930162062685166,1.4231882073770628,-0.5224847908100577,-0.18930162062685166,1.4457027379989895,-0.463081808076359,-0.18930162062685166,1.465811039088714,-0.40290807137427453,-0.18930162062685166,1.4834796423635437,-0.34206373395004774,-0.18930162062685166,1.498679140177028,-0.28065006519864555,-0.18930162062685166,1.5113842344650816,-0.21876928211084237,-0.18930162062685166,1.521573778852117,-0.15652437914312367,-0.18930162062685166,1.5292308138471002,-0.09401895679358026,-0.18930162062685166,1.5343425950709484,-0.03135704916910635,-0.18930162062685166,1.5369006144682913,0.03135704916910715,-0.18930162062685166,1.5369006144682913,0.09401895679357969,-0.18930162062685166,1.5343425950709484,0.15652437914312312,-0.18930162062685166,1.5292308138471005,0.21876928211084182,-0.18930162062685166,1.5215737788521173,0.280650065198645,-0.18930162062685166,1.5113842344650819,0.3420637339500472,-0.18930162062685166,1.498679140177028,0.402908071374274,-0.18930162062685166,1.483479642363544,0.46308180807635846,-0.18930162062685166,1.4658110390887142,0.5224847908100573,-0.18930162062685166,1.4457027379989897,0.5810181491730407,-0.18930162062685166,1.4231882073770634,0.6385844601669246,-0.18930162062685166,1.3983049204372138,0.6950879103482258,-0.18930162062685166,1.3710942929548433,0.7504344553003984,-0.18930162062685166,1.3416016143340017,0.804531976161474,-0.18930162062685166,1.3098759722276452,0.8572904329468192,-0.18930162062685166,1.275970170836082,0.9086220144118022,-0.18930162062685166,1.2399406430195938,0.9584412842049442,-0.18930162062685166,1.201847356371508,1.0066653230682991,-0.18930162062685166,1.1617537134080538,1.0532138668483664,-0.18930162062685166,1.1197264460411376,1.0980094400878628,-0.18930162062685166,1.0758355045096548,1.140977484975969,-0.18930162062685166,1.030153940954223,1.1820464854424493,-0.18930162062685166,0.982757787829106,1.221148086189091,-0.18930162062685166,0.9337259313536949,1.2582172064603574,-0.18930162062685166,0.8831399802141786,1.2931921483638766,-0.18930162062685166,0.8310841297339437,1.326014699560499,-0.18930162062685166,0.777645021738765,1.3566302301529878,-0.18930162062685166,0.7229116003500405,1.3849877836120916,-0.18930162062685166,0.6669749639460774,1.411040161588662,-0.18930162062685166,0.6099282135378288,1.4347440024706455,-0.18930162062685166,0.5518662978114518,1.4560598535542042,-0.18930162062685166,0.49288585509559335,1.474952236708855,-0.18930162062685166,0.43308505251642093,1.491389707427309,-0.18930162062685166,0.3725634226081387,1.5053449071617568,-0.18930162062685166,0.31142169765089733,1.5167946088594666,-0.18930162062685166,0.24976164201186488,1.5257197556219244,-0.18930162062685166,0.1876858827684718,1.5321054924231594,-0.18930162062685166,0.12529773889577242,1.5359411908344691,-0.18930162062685166,0.06270104930221036,1.5372204667143894,-0.18930162062685166,0.0,1.5517263781565855,-0.22147406159500388,0.0,1.5504350304476555,-0.22147406159500388,-0.06329272491947237,1.5465631366416852,-0.22147406159500388,-0.12648010534462248,1.5401171411238004,-0.22147406159500388,-0.18945697211664256,1.531107772617391,-0.22147406159500388,-0.25211850645592504,1.5195500263272086,-0.22147406159500388,-0.3143604144225767,1.505463138981329,-0.22147406159500388,-0.37607910050338894,1.4888705568135163,-0.22147406159500388,-0.437171840036346,1.4697998965392807,-0.22147406159500388,-0.49753695018568683,1.448282899390581,-0.22147406159500388,-0.55707395918295,1.4243553782856768,-0.22147406159500388,-0.6156837735523176,1.39805715822206,-0.22147406159500388,-0.6732688430419252,1.3694320099916772,-0.22147406159500388,-0.7297333229866301,1.338527577328766,-0.22147406159500388,-0.7849832338319966,1.305395297611561,-0.22147406159500388,-0.8389266175539901,1.2700903162498545,-0.22147406159500388,-0.8914736907140329,1.2326713949009038,-0.22147406159500388,-0.9425369938946774,1.193200813666452,-0.22147406159500388,-0.9920315372671735,1.1517442674336453,-0.22147406159500388,-1.0398749420486533,1.1083707565323764,-0.22147406159500388,-1.0859875776134837,1.0631524718910446,-0.22147406159500388,-1.1302926940305846,1.0161646748818804,-0.22147406159500388,-1.1727165498061143,0.9674855720558199,-0.22147406159500388,-1.2131885346189097,0.9171961849754162,-0.22147406159500388,-1.2516412868443967,0.8653802153624471,-0.22147406159500388,-1.288010805671365,0.8121239057846587,-0.22147406159500388,-1.3222365576250061,0.7575158961135223,-0.22147406159500388,-1.3542615773189026,0.7016470759919174,-0.22147406159500388,-1.384032562268297,0.6446104335572881,-0.22147406159500388,-1.4114999616068147,0.5865009006720687,-0.22147406159500388,-1.4366180585589918,0.5274151949189655,-0.22147406159500388,-1.459345046531335,0.467451658624085,-0.22147406159500388,-1.479643098695272,0.40671009517583884,-0.22147406159500388,-1.4974784309461737,0.3452916029120526,-0.22147406159500388,-1.512821358133663,0.28329840685175944,-0.22147406159500388,-1.5256463434696161,0.2208336885517453,-0.22147406159500388,-1.5359320410316288,0.15800141437102888,-0.22147406159500388,-1.5436613312911966,0.09490616242911971,-0.22147406159500388,-1.5488213496074787,0.03165294854605724,-0.22147406159500388,-1.5514035076392265,-0.03165294854605705,-0.22147406159500388,-1.5514035076392265,-0.09490616242911952,-0.22147406159500388,-1.548821349607479,-0.15800141437102835,-0.22147406159500388,-1.5436613312911966,-0.2208336885517451,-0.22147406159500388,-1.5359320410316288,-0.2832984068517592,-0.22147406159500388,-1.5256463434696161,-0.3452916029120521,-0.22147406159500388,-1.512821358133663,-0.40671009517583867,-0.22147406159500388,-1.497478430946174,-0.46745165862408444,-0.22147406159500388,-1.4796430986952722,-0.5274151949189654,-0.22147406159500388,-1.4593450465313351,-0.5865009006720685,-0.22147406159500388,-1.436618058558992,-0.6446104335572876,-0.22147406159500388,-1.411499961606815,-0.7016470759919176,-0.22147406159500388,-1.384032562268297,-0.7575158961135222,-0.22147406159500388,-1.3542615773189026,-0.812123905784659,-0.22147406159500388,-1.3222365576250057,-0.8653802153624468,-0.22147406159500388,-1.2880108056713655,-0.9171961849754159,-0.22147406159500388,-1.2516412868443967,-0.9674855720558196,-0.22147406159500388,-1.21318853461891,-1.0161646748818807,-0.22147406159500388,-1.1727165498061143,-1.0631524718910446,-0.22147406159500388,-1.1302926940305846,-1.1083707565323764,-0.22147406159500388,-1.0859875776134837,-1.1517442674336453,-0.22147406159500388,-1.0398749420486533,-1.1932008136664516,-0.22147406159500388,-0.9920315372671741,-1.2326713949009038,-0.22147406159500388,-0.9425369938946773,-1.2700903162498542,-0.22147406159500388,-0.8914736907140335,-1.3053952976115613,-0.22147406159500388,-0.8389266175539899,-1.338527577328766,-0.22147406159500388,-0.7849832338319969,-1.369432009991677,-0.22147406159500388,-0.7297333229866303,-1.3980571582220598,-0.22147406159500388,-0.6732688430419255,-1.4243553782856766,-0.22147406159500388,-0.6156837735523177,-1.448282899390581,-0.22147406159500388,-0.5570739591829501,-1.4697998965392807,-0.22147406159500388,-0.49753695018568683,-1.488870556813516,-0.22147406159500388,-0.4371718400363466,-1.505463138981329,-0.22147406159500388,-0.37607910050338883,-1.5195500263272086,-0.22147406159500388,-0.3143604144225772,-1.531107772617391,-0.22147406159500388,-0.25211850645592543,-1.5401171411238004,-0.22147406159500388,-0.18945697211664295,-1.5465631366416852,-0.22147406159500388,-0.1264801053446228,-1.5504350304476555,-0.22147406159500388,-0.06329272491947262,-1.5517263781565855,-0.22147406159500388,-1.9003167421619779e-16,-1.5504350304476555,-0.22147406159500388,0.06329272491947224,-1.5465631366416852,-0.22147406159500388,0.12648010534462242,-1.5401171411238004,-0.22147406159500388,0.18945697211664259,-1.5311077726173912,-0.22147406159500388,0.25211850645592443,-1.5195500263272088,-0.22147406159500388,0.3143604144225761,-1.505463138981329,-0.22147406159500388,0.3760791005033891,-1.4888705568135163,-0.22147406159500388,0.4371718400363463,-1.4697998965392807,-0.22147406159500388,0.4975369501856865,-1.4482828993905812,-0.22147406159500388,0.5570739591829498,-1.4243553782856768,-0.22147406159500388,0.6156837735523174,-1.3980571582220604,-0.22147406159500388,0.6732688430419245,-1.3694320099916772,-0.22147406159500388,0.72973332298663,-1.338527577328766,-0.22147406159500388,0.7849832338319964,-1.305395297611561,-0.22147406159500388,0.8389266175539901,-1.2700903162498547,-0.22147406159500388,0.8914736907140326,-1.232671394900904,-0.22147406159500388,0.9425369938946769,-1.1932008136664518,-0.22147406159500388,0.9920315372671737,-1.1517442674336453,-0.22147406159500388,1.0398749420486535,-1.1083707565323766,-0.22147406159500388,1.0859875776134835,-1.0631524718910448,-0.22147406159500388,1.1302926940305844,-1.0161646748818813,-0.22147406159500388,1.1727165498061136,-0.9674855720558199,-0.22147406159500388,1.2131885346189097,-0.9171961849754154,-0.22147406159500388,1.251641286844397,-0.8653802153624471,-0.22147406159500388,1.288010805671365,-0.8121239057846593,-0.22147406159500388,1.3222365576250055,-0.7575158961135225,-0.22147406159500388,1.3542615773189024,-0.7016470759919167,-0.22147406159500388,1.3840325622682974,-0.6446104335572881,-0.22147406159500388,1.411499961606815,-0.5865009006720692,-0.22147406159500388,1.4366180585589916,-0.527415194918965,-0.22147406159500388,1.4593450465313351,-0.46745165862408516,-0.22147406159500388,1.479643098695272,-0.4067100951758397,-0.22147406159500388,1.4974784309461737,-0.3452916029120528,-0.22147406159500388,1.512821358133663,-0.28329840685175894,-0.22147406159500388,1.5256463434696161,-0.2208336885517451,-0.22147406159500388,1.5359320410316288,-0.1580014143710294,-0.22147406159500388,1.5436613312911964,-0.09490616242911955,-0.22147406159500388,1.548821349607479,-0.031652948546056395,-0.22147406159500388,1.5514035076392265,0.03165294854605721,-0.22147406159500388,1.5514035076392265,0.09490616242911898,-0.22147406159500388,1.548821349607479,0.15800141437102885,-0.22147406159500388,1.5436613312911966,0.22083368855174454,-0.22147406159500388,1.535932041031629,0.2832984068517584,-0.22147406159500388,1.5256463434696164,0.34529160291205224,-0.22147406159500388,1.512821358133663,0.40671009517583917,-0.22147406159500388,1.4974784309461737,0.4674516586240846,-0.22147406159500388,1.4796430986952722,0.5274151949189646,-0.22147406159500388,1.4593450465313353,0.5865009006720674,-0.22147406159500388,1.4366180585589923,0.6446104335572888,-0.22147406159500388,1.4114999616068147,0.7016470759919173,-0.22147406159500388,1.384032562268297,0.7575158961135221,-0.22147406159500388,1.3542615773189028,0.8121239057846578,-0.22147406159500388,1.3222365576250066,0.8653802153624466,-0.22147406159500388,1.2880108056713655,0.9171961849754162,-0.22147406159500388,1.2516412868443965,0.9674855720558195,-0.22147406159500388,1.21318853461891,1.016164674881881,-0.22147406159500388,1.172716549806114,1.0631524718910443,-0.22147406159500388,1.1302926940305846,1.108370756532376,-0.22147406159500388,1.0859875776134844,1.1517442674336453,-0.22147406159500388,1.0398749420486535,1.1932008136664523,-0.22147406159500388,0.992031537267173,1.2326713949009036,-0.22147406159500388,0.9425369938946774,1.270090316249854,-0.22147406159500388,0.8914736907140336,1.3053952976115604,-0.22147406159500388,0.8389266175539911,1.338527577328766,-0.22147406159500388,0.784983233831997,1.3694320099916775,-0.22147406159500388,0.7297333229866299,1.3980571582220598,-0.22147406159500388,0.6732688430419257,1.4243553782856762,-0.22147406159500388,0.6156837735523185,1.448282899390581,-0.22147406159500388,0.5570739591829503,1.4697998965392804,-0.22147406159500388,0.49753695018568767,1.4888705568135163,-0.22147406159500388,0.4371718400363462,1.5054631389813289,-0.22147406159500388,0.37607910050338966,1.5195500263272086,-0.22147406159500388,0.3143604144225767,1.531107772617391,-0.22147406159500388,0.2521185064559256,1.5401171411238004,-0.22147406159500388,0.18945697211664383,1.5465631366416852,-0.22147406159500388,0.12648010534462298,1.5504350304476555,-0.22147406159500388,0.06329272491947212,1.5517263781565855,-0.22147406159500388,0.0,1.568465566233958,-0.2525431299287738,0.0,1.5671602881617388,-0.2525431299287738,-0.06397549273296733,1.5632466264516158,-0.2525431299287738,-0.12784450457196866,1.5567310950072697,-0.2525431299287738,-0.1915007318499774,1.5476245382877771,-0.2525431299287738,-0.2548382250588694,1.535942113258079,-0.2525431299287738,-0.3177515651919227,1.5217032641617088,-0.2525431299287738,-0.38013603920335093,1.5049316901577667,-0.2525431299287738,-0.44188781429283375,1.4856553058760102,-0.2525431299287738,-0.5029041107249659,1.4639061949557057,-0.2525431299287738,-0.5630833728959834,1.4397205566455789,-0.2525431299287738,-0.6223254383630441,1.4131386455537358,-0.2525431299287738,-0.6805317045547276,1.3842047046478427,-0.2525431299287738,-0.7376052928852861,1.3529668916170707,-0.2525431299287738,-0.793451209999488,1.3194771987183762,-0.2525431299287738,-0.8479765058796838,1.2837913662405187,-0.2525431299287738,-0.9010904285519366,1.2459687897298528,-0.2525431299287738,-0.9527045751337276,1.206072421132302,-0.2525431299287738,-1.002733038971828,1.1641686640160573,-0.2525431299287738,-1.051092552625447,1.1203272630493923,-0.2525431299287738,-1.0977026264566683,1.0746211879175447,-0.2525431299287738,-1.1424856825975087,1.0271265118718766,-0.2525431299287738,-1.1853671840706244,0.9779222851134568,-0.2525431299287738,-1.226275758848753,0.9270904032218,-0.2525431299287738,-1.2651433186464096,0.8747154708477588,-0.2525431299287738,-1.301905172246118,0.8208846608974314,-0.2525431299287738,-1.3365001331705582,0.7656875694414627,-0.2525431299287738,-1.3688706215214188,0.7092160665912265,-0.2525431299287738,-1.3989627598154566,0.6515641435900908,-0.2525431299287738,-1.4267264626582497,0.5928277563742712,-0.2525431299287738,-1.4521155201063922,0.5331046658636437,-0.2525431299287738,-1.4750876745793868,0.47249427524834053,-0.2525431299287738,-1.4956046911932162,0.41109746454195234,-0.2525431299287738,-1.5136324213985337,0.3490164226766999,-0.2525431299287738,-1.5291408598175544,0.28635447742004166,-0.2525431299287738,-1.5421041941850444,0.22321592339580262,-0.2525431299287738,-1.5525008483102898,0.15970584849606417,-0.2525431299287738,-1.5603135179885363,0.095929958972741,-0.2525431299287738,-1.5655291998021303,0.03199440349995529,-0.2525431299287738,-1.5681392127634244,-0.031994403499955096,-0.2525431299287738,-1.5681392127634244,-0.0959299589727408,-0.2525431299287738,-1.5655291998021303,-0.15970584849606365,-0.2525431299287738,-1.5603135179885363,-0.22321592339580246,-0.2525431299287738,-1.5525008483102898,-0.2863544774200415,-0.2525431299287738,-1.5421041941850444,-0.34901642267669936,-0.2525431299287738,-1.5291408598175544,-0.4110974645419521,-0.2525431299287738,-1.513632421398534,-0.47249427524834,-0.2525431299287738,-1.4956046911932164,-0.5331046658636435,-0.2525431299287738,-1.475087674579387,-0.592827756374271,-0.2525431299287738,-1.4521155201063924,-0.6515641435900904,-0.2525431299287738,-1.4267264626582499,-0.7092160665912266,-0.2525431299287738,-1.3989627598154566,-0.7656875694414625,-0.2525431299287738,-1.3688706215214188,-0.8208846608974317,-0.2525431299287738,-1.3365001331705577,-0.8747154708477584,-0.2525431299287738,-1.3019051722461181,-0.9270904032217996,-0.2525431299287738,-1.2651433186464098,-0.9779222851134567,-0.2525431299287738,-1.2262757588487534,-1.0271265118718769,-0.2525431299287738,-1.1853671840706244,-1.074621187917545,-0.2525431299287738,-1.1424856825975087,-1.1203272630493923,-0.2525431299287738,-1.0977026264566683,-1.1641686640160573,-0.2525431299287738,-1.051092552625447,-1.2060724211323015,-0.2525431299287738,-1.0027330389718285,-1.2459687897298528,-0.2525431299287738,-0.9527045751337274,-1.2837913662405185,-0.2525431299287738,-0.9010904285519371,-1.3194771987183764,-0.2525431299287738,-0.8479765058796835,-1.3529668916170707,-0.2525431299287738,-0.7934512099994883,-1.3842047046478425,-0.2525431299287738,-0.7376052928852863,-1.4131386455537358,-0.2525431299287738,-0.6805317045547279,-1.4397205566455786,-0.2525431299287738,-0.6223254383630441,-1.4639061949557057,-0.2525431299287738,-0.5630833728959835,-1.4856553058760102,-0.2525431299287738,-0.5029041107249659,-1.5049316901577665,-0.2525431299287738,-0.44188781429283436,-1.5217032641617088,-0.2525431299287738,-0.38013603920335076,-1.5359421132580788,-0.2525431299287738,-0.3177515651919232,-1.5476245382877771,-0.2525431299287738,-0.2548382250588698,-1.5567310950072697,-0.2525431299287738,-0.19150073184997782,-1.5632466264516158,-0.2525431299287738,-0.12784450457196897,-1.5671602881617388,-0.2525431299287738,-0.06397549273296758,-1.568465566233958,-0.2525431299287738,-1.9208163352612575e-16,-1.5671602881617388,-0.2525431299287738,0.06397549273296721,-1.5632466264516158,-0.2525431299287738,0.12784450457196858,-1.5567310950072697,-0.2525431299287738,0.19150073184997743,-1.5476245382877771,-0.2525431299287738,0.2548382250588688,-1.5359421132580793,-0.2525431299287738,0.31775156519192216,-1.5217032641617088,-0.2525431299287738,0.3801360392033511,-1.5049316901577667,-0.2525431299287738,0.44188781429283397,-1.4856553058760102,-0.2525431299287738,0.5029041107249655,-1.463906194955706,-0.2525431299287738,0.5630833728959832,-1.4397205566455789,-0.2525431299287738,0.6223254383630438,-1.4131386455537363,-0.2525431299287738,0.6805317045547269,-1.3842047046478427,-0.2525431299287738,0.737605292885286,-1.3529668916170707,-0.2525431299287738,0.7934512099994879,-1.3194771987183762,-0.2525431299287738,0.8479765058796838,-1.283791366240519,-0.2525431299287738,0.9010904285519362,-1.2459687897298533,-0.2525431299287738,0.9527045751337271,-1.2060724211323017,-0.2525431299287738,1.0027330389718283,-1.1641686640160573,-0.2525431299287738,1.0510925526254473,-1.1203272630493928,-0.2525431299287738,1.097702626456668,-1.0746211879175451,-0.2525431299287738,1.1424856825975085,-1.0271265118718775,-0.2525431299287738,1.1853671840706237,-0.9779222851134568,-0.2525431299287738,1.226275758848753,-0.9270904032217993,-0.2525431299287738,1.26514331864641,-0.8747154708477588,-0.2525431299287738,1.301905172246118,-0.8208846608974321,-0.2525431299287738,1.3365001331705577,-0.7656875694414628,-0.2525431299287738,1.3688706215214188,-0.7092160665912257,-0.2525431299287738,1.3989627598154573,-0.6515641435900907,-0.2525431299287738,1.4267264626582499,-0.5928277563742718,-0.2525431299287738,1.452115520106392,-0.5331046658636432,-0.2525431299287738,1.475087674579387,-0.4724942752483407,-0.2525431299287738,1.4956046911932162,-0.4110974645419532,-0.2525431299287738,1.5136324213985335,-0.34901642267670013,-0.2525431299287738,1.5291408598175544,-0.2863544774200412,-0.2525431299287738,1.5421041941850444,-0.22321592339580246,-0.2525431299287738,1.5525008483102898,-0.15970584849606473,-0.2525431299287738,1.5603135179885361,-0.09592995897274083,-0.2525431299287738,1.5655291998021303,-0.03199440349995444,-0.2525431299287738,1.5681392127634244,0.031994403499955255,-0.2525431299287738,1.5681392127634244,0.09592995897274026,-0.2525431299287738,1.5655291998021303,0.15970584849606415,-0.2525431299287738,1.5603135179885363,0.2232159233958019,-0.2525431299287738,1.55250084831029,0.2863544774200406,-0.2525431299287738,1.5421041941850446,0.3490164226766996,-0.2525431299287738,1.5291408598175544,0.4110974645419527,-0.2525431299287738,1.5136324213985337,0.47249427524834015,-0.2525431299287738,1.4956046911932164,0.5331046658636427,-0.2525431299287738,1.4750876745793873,0.5928277563742699,-0.2525431299287738,1.4521155201063927,0.6515641435900914,-0.2525431299287738,1.4267264626582494,0.7092160665912264,-0.2525431299287738,1.3989627598154566,0.7656875694414623,-0.2525431299287738,1.368870621521419,0.8208846608974305,-0.2525431299287738,1.3365001331705586,0.8747154708477582,-0.2525431299287738,1.3019051722461181,0.9270904032218,-0.2525431299287738,1.2651433186464094,0.9779222851134565,-0.2525431299287738,1.2262757588487534,1.0271265118718773,-0.2525431299287738,1.1853671840706241,1.0746211879175445,-0.2525431299287738,1.142485682597509,1.120327263049392,-0.2525431299287738,1.097702626456669,1.1641686640160573,-0.2525431299287738,1.0510925526254473,1.2060724211323022,-0.2525431299287738,1.0027330389718274,1.2459687897298526,-0.2525431299287738,0.9527045751337276,1.2837913662405183,-0.2525431299287738,0.9010904285519372,1.3194771987183755,-0.2525431299287738,0.8479765058796848,1.3529668916170707,-0.2525431299287738,0.7934512099994885,1.384204704647843,-0.2525431299287738,0.7376052928852859,1.4131386455537358,-0.2525431299287738,0.680531704554728,1.4397205566455784,-0.2525431299287738,0.622325438363045,1.4639061949557057,-0.2525431299287738,0.5630833728959838,1.48565530587601,-0.2525431299287738,0.5029041107249668,1.5049316901577667,-0.2525431299287738,0.4418878142928339,1.5217032641617085,-0.2525431299287738,0.38013603920335165,1.535942113258079,-0.2525431299287738,0.3177515651919227,1.5476245382877771,-0.2525431299287738,0.25483822505887,1.5567310950072697,-0.2525431299287738,0.1915007318499787,1.5632466264516158,-0.2525431299287738,0.12784450457196916,1.5671602881617388,-0.2525431299287738,0.06397549273296708,1.568465566233958,-0.2525431299287738,0.0,1.5873546371535112,-0.2823540410925434,0.0,1.5860336395840926,-0.2823540410925434,-0.06474595122779293,1.582072845545923,-0.2823540410925434,-0.1293841392094911,1.5754788473897916,-0.2823540410925434,-0.19380698006028782,1.5662626201748728,-0.2823540410925434,-0.2579072483194234,1.5544395034018246,-0.2823540410925434,-0.3215782554163825,1.5400291754817017,-0.2823540410925434,-0.38471402724349074,1.523055620983184,-0.2823540410925434,-0.44720948053935855,1.5035470907126285,-0.2823540410925434,-0.5089605977895992,1.4815360546933898,-0.2823540410925434,-0.5698646003537159,1.4570591481226713,-0.2823540410925434,-0.6298201195300058,1.4301571103958544,-0.2823540410925434,-0.6887273652737601,1.4008747172997955,-0.2823540410925434,-0.7464882922879454,1.3692607064879485,-0.2823540410925434,-0.8030067632099244,1.335367696361349,-0.2823540410925434,-0.8581887086226078,1.2992520984904803,-0.2823540410925434,-0.9119422836237164,1.2609740237237794,-0.2823540410925434,-0.964178020692557,1.2205971821390649,-0.2823540410925434,-1.014808978599879,1.1781887770043975,-0.2823540410925434,-1.063750887112973,1.1338193929248739,-0.2823540410925434,-1.1109222872551552,1.0875628783615165,-0.2823540410925434,-1.156244666886195,1.0394962227178,-0.2823540410925434,-1.1996425913780242,0.9896994281983846,-0.2823540410925434,-1.241043829168231,0.938255376653344,-0.2823540410925434,-1.2803794719823678,0.8852496916295041,-0.2823540410925434,-1.3175840495249715,0.8307705958585002,-0.2823540410925434,-1.3525956384484124,0.7749087644187471,-0.2823540410925434,-1.3853559654181904,0.7177571738157188,-0.2823540410925434,-1.4158105041031541,0.6594109472317305,-0.2823540410925434,-1.443908565929196,0.5999671962027915,-0.2823540410925434,-1.4696033844453804,0.5395248589860352,-0.2823540410925434,-1.4928521931620873,0.4781845358867545,-0.2823540410925434,-1.5136162967316138,0.41604832181912327,-0.2823540410925434,-1.5318611353527598,0.35321963637928594,-0.2823540410925434,-1.547556342292209,0.2898030517136465,-0.2823540410925434,-1.560675794426957,0.22590411846885147,-0.2823540410925434,-1.571197655723673,0.16162919011315366,-0.2823540410925434,-1.5791044135826235,0.09708524592156245,-0.2823540410925434,-1.5843829079856626,0.0323797129193967,-0.2823540410925434,-1.5870243533997872,-0.03237971291939651,-0.2823540410925434,-1.5870243533997872,-0.09708524592156224,-0.2823540410925434,-1.5843829079856628,-0.16162919011315313,-0.2823540410925434,-1.5791044135826235,-0.2259041184688513,-0.2823540410925434,-1.571197655723673,-0.28980305171364634,-0.2823540410925434,-1.560675794426957,-0.35321963637928544,-0.2823540410925434,-1.547556342292209,-0.4160483218191231,-0.2823540410925434,-1.53186113535276,-0.47818453588675397,-0.2823540410925434,-1.5136162967316138,-0.539524858986035,-0.2823540410925434,-1.4928521931620875,-0.5999671962027913,-0.2823540410925434,-1.4696033844453804,-0.6594109472317301,-0.2823540410925434,-1.443908565929196,-0.7177571738157189,-0.2823540410925434,-1.4158105041031541,-0.774908764418747,-0.2823540410925434,-1.3853559654181904,-0.8307705958585007,-0.2823540410925434,-1.352595638448412,-0.8852496916295037,-0.2823540410925434,-1.317584049524972,-0.9382553766533437,-0.2823540410925434,-1.280379471982368,-0.9896994281983845,-0.2823540410925434,-1.2410438291682313,-1.0394962227178002,-0.2823540410925434,-1.1996425913780242,-1.0875628783615168,-0.2823540410925434,-1.156244666886195,-1.1338193929248739,-0.2823540410925434,-1.1109222872551552,-1.1781887770043975,-0.2823540410925434,-1.063750887112973,-1.2205971821390644,-0.2823540410925434,-1.0148089785998795,-1.2609740237237794,-0.2823540410925434,-0.9641780206925569,-1.2992520984904798,-0.2823540410925434,-0.911942283623717,-1.3353676963613492,-0.2823540410925434,-0.8581887086226075,-1.3692607064879483,-0.2823540410925434,-0.8030067632099246,-1.4008747172997953,-0.2823540410925434,-0.7464882922879457,-1.4301571103958541,-0.2823540410925434,-0.6887273652737603,-1.4570591481226711,-0.2823540410925434,-0.6298201195300059,-1.4815360546933898,-0.2823540410925434,-0.5698646003537159,-1.5035470907126285,-0.2823540410925434,-0.5089605977895992,-1.5230556209831838,-0.2823540410925434,-0.44720948053935916,-1.5400291754817017,-0.2823540410925434,-0.3847140272434906,-1.5544395034018244,-0.2823540410925434,-0.321578255416383,-1.5662626201748728,-0.2823540410925434,-0.25790724831942385,-1.5754788473897916,-0.2823540410925434,-0.19380698006028824,-1.582072845545923,-0.2823540410925434,-0.12938413920949143,-1.5860336395840926,-0.2823540410925434,-0.06474595122779318,-1.5873546371535112,-0.2823540410925434,-1.9439487755017557e-16,-1.5860336395840926,-0.2823540410925434,0.0647459512277928,-1.582072845545923,-0.2823540410925434,0.12938413920949104,-1.5754788473897916,-0.2823540410925434,0.19380698006028785,-1.566262620174873,-0.2823540410925434,0.2579072483194228,-1.5544395034018248,-0.2823540410925434,0.3215782554163819,-1.5400291754817017,-0.2823540410925434,0.3847140272434909,-1.523055620983184,-0.2823540410925434,0.44720948053935883,-1.5035470907126285,-0.2823540410925434,0.5089605977895989,-1.48153605469339,-0.2823540410925434,0.5698646003537156,-1.4570591481226713,-0.2823540410925434,0.6298201195300055,-1.4301571103958546,-0.2823540410925434,0.6887273652737594,-1.4008747172997955,-0.2823540410925434,0.7464882922879453,-1.3692607064879485,-0.2823540410925434,0.8030067632099243,-1.335367696361349,-0.2823540410925434,0.8581887086226078,-1.2992520984904803,-0.2823540410925434,0.9119422836237161,-1.2609740237237799,-0.2823540410925434,0.9641780206925565,-1.2205971821390647,-0.2823540410925434,1.0148089785998793,-1.1781887770043973,-0.2823540410925434,1.0637508871129733,-1.133819392924874,-0.2823540410925434,1.1109222872551552,-1.087562878361517,-0.2823540410925434,1.156244666886195,-1.0394962227178008,-0.2823540410925434,1.1996425913780235,-0.9896994281983846,-0.2823540410925434,1.241043829168231,-0.9382553766533434,-0.2823540410925434,1.2803794719823682,-0.8852496916295041,-0.2823540410925434,1.3175840495249715,-0.830770595858501,-0.2823540410925434,1.3525956384484117,-0.7749087644187472,-0.2823540410925434,1.3853559654181902,-0.717757173815718,-0.2823540410925434,1.4158105041031546,-0.6594109472317305,-0.2823540410925434,1.443908565929196,-0.5999671962027919,-0.2823540410925434,1.4696033844453802,-0.5395248589860347,-0.2823540410925434,1.4928521931620875,-0.4781845358867547,-0.2823540410925434,1.5136162967316138,-0.41604832181912416,-0.2823540410925434,1.5318611353527596,-0.35321963637928616,-0.2823540410925434,1.547556342292209,-0.289803051713646,-0.2823540410925434,1.560675794426957,-0.2259041184688513,-0.2823540410925434,1.571197655723673,-0.16162919011315421,-0.2823540410925434,1.5791044135826233,-0.09708524592156229,-0.2823540410925434,1.5843829079856628,-0.03237971291939584,-0.2823540410925434,1.5870243533997872,0.03237971291939667,-0.2823540410925434,1.5870243533997872,0.0970852459215617,-0.2823540410925434,1.5843829079856628,0.16162919011315363,-0.2823540410925434,1.5791044135826235,0.22590411846885072,-0.2823540410925434,1.5711976557236733,0.28980305171364545,-0.2823540410925434,1.5606757944269571,0.3532196363792856,-0.2823540410925434,1.547556342292209,0.41604832181912366,-0.2823540410925434,1.5318611353527598,0.4781845358867542,-0.2823540410925434,1.5136162967316138,0.5395248589860342,-0.2823540410925434,1.4928521931620877,0.5999671962027902,-0.2823540410925434,1.4696033844453809,0.6594109472317312,-0.2823540410925434,1.4439085659291957,0.7177571738157188,-0.2823540410925434,1.4158105041031541,0.7749087644187468,-0.2823540410925434,1.3853559654181904,0.8307705958584994,-0.2823540410925434,1.3525956384484128,0.8852496916295036,-0.2823540410925434,1.317584049524972,0.938255376653344,-0.2823540410925434,1.2803794719823676,0.9896994281983843,-0.2823540410925434,1.2410438291682313,1.0394962227178004,-0.2823540410925434,1.199642591378024,1.0875628783615165,-0.2823540410925434,1.1562446668861952,1.1338193929248732,-0.2823540410925434,1.110922287255156,1.1781887770043973,-0.2823540410925434,1.0637508871129733,1.2205971821390653,-0.2823540410925434,1.0148089785998784,1.2609740237237792,-0.2823540410925434,0.964178020692557,1.2992520984904796,-0.2823540410925434,0.9119422836237172,1.3353676963613483,-0.2823540410925434,0.8581887086226088,1.3692607064879483,-0.2823540410925434,0.8030067632099247,1.4008747172997957,-0.2823540410925434,0.7464882922879452,1.4301571103958541,-0.2823540410925434,0.6887273652737606,1.4570591481226707,-0.2823540410925434,0.6298201195300067,1.4815360546933898,-0.2823540410925434,0.5698646003537162,1.5035470907126283,-0.2823540410925434,0.5089605977896001,1.523055620983184,-0.2823540410925434,0.4472094805393588,1.5400291754817015,-0.2823540410925434,0.38471402724349146,1.5544395034018246,-0.2823540410925434,0.3215782554163825,1.5662626201748728,-0.2823540410925434,0.257907248319424,1.5754788473897916,-0.2823540410925434,0.19380698006028912,1.582072845545923,-0.2823540410925434,0.1293841392094916,1.5860336395840926,-0.2823540410925434,0.06474595122779268,1.5873546371535112,-0.2823540410925434,0.0,1.6082994865140736,-0.3107582786264296,0.0,1.6069610586272896,-0.3107582786264296,-0.06560026201848318,1.6029480026480667,-0.3107582786264296,-0.1310913388748717,1.5962669979120372,-0.3107582786264296,-0.19636422713499788,1.5869291642922414,-0.3107582786264296,-0.26131028651807964,1.5749500436911967,-0.3107582786264296,-0.3258214207177462,1.5603495741729339,-0.3107582786264296,-0.3897902573176723,1.5431520567780577,-0.3107582786264296,-0.4531103265023684,1.5233861150770638,-0.3107582786264296,-0.5156762382656814,1.5010846475292288,-0.3107582786264296,-0.577383857822059,1.476284772726373,-0.3107582786264296,-0.6381304789286231,1.4490277676126269,-0.3107582786264296,-0.6978149948295749,1.419358998783031,-0.3107582786264296,-0.7563380665384127,1.3873278469753163,-0.3107582786264296,-0.8136022881778703,1.352987624880539,-0.3107582786264296,-0.8695123491023865,1.3163954884093683,-0.3107582786264296,-0.9239751925332703,1.2776123415617124,-0.3107582786264296,-0.9769001704425236,1.236702735058023,-0.3107582786264296,-1.028199194427538,1.193734758900991,-0.3107582786264296,-1.0777868823255483,1.1487799290464558,-0.3107582786264296,-1.1255807003238163,1.1019130683721556,-0.3107582786264296,-1.1715011003290154,1.0532121821424312,-0.3107582786264296,-1.2154716523671834,1.0027583281761627,-0.3107582786264296,-1.2574191717938692,0.9506354819340278,-0.3107582786264296,-1.2972738411027533,0.8969303967496361,-0.3107582786264296,-1.334969326129995,0.8417324594371671,-0.3107582786264296,-1.3704428864609028,0.7851335415158391,-0.3107582786264296,-1.4036354798551613,0.7272278462988329,-0.3107582786264296,-1.434491860516815,0.6681117521011711,-0.3107582786264296,-1.4629606710454441,0.6078836518275286,-0.3107582786264296,-1.488994527915486,0.5466437892069498,-0.3107582786264296,-1.51255010034144,0.48449409194705534,-0.3107582786264296,-1.5335881823976838,0.4215380020854327,-0.3107582786264296,-1.5520737582728645,0.3578803038205726,-0.3107582786264296,-1.5679760605502648,0.2936269491089118,-0.3107582786264296,-1.5812686214171283,0.22888488131825824,-0.3107582786264296,-1.591929316717724,0.16376185723110803,-0.3107582786264296,-1.5999404027768194,0.09836626769411776,-0.3107582786264296,-1.6052885459322763,0.03280695721223549,-0.3107582786264296,-1.6079648447276162,-0.032806957212235296,-0.3107582786264296,-1.6079648447276162,-0.09836626769411756,-0.3107582786264296,-1.6052885459322763,-0.1637618572311075,-0.3107582786264296,-1.5999404027768194,-0.22888488131825807,-0.3107582786264296,-1.591929316717724,-0.29362694910891163,-0.3107582786264296,-1.5812686214171283,-0.35788030382057207,-0.3107582786264296,-1.5679760605502648,-0.42153800208543246,-0.3107582786264296,-1.5520737582728648,-0.4844940919470548,-0.3107582786264296,-1.5335881823976838,-0.5466437892069497,-0.3107582786264296,-1.5125501003414403,-0.6078836518275283,-0.3107582786264296,-1.4889945279154861,-0.6681117521011707,-0.3107582786264296,-1.4629606710454441,-0.727227846298833,-0.3107582786264296,-1.434491860516815,-0.7851335415158391,-0.3107582786264296,-1.4036354798551613,-0.8417324594371675,-0.3107582786264296,-1.3704428864609024,-0.8969303967496357,-0.3107582786264296,-1.3349693261299953,-0.9506354819340275,-0.3107582786264296,-1.2972738411027536,-1.0027583281761625,-0.3107582786264296,-1.2574191717938694,-1.0532121821424314,-0.3107582786264296,-1.2154716523671834,-1.1019130683721556,-0.3107582786264296,-1.1715011003290154,-1.1487799290464558,-0.3107582786264296,-1.1255807003238163,-1.193734758900991,-0.3107582786264296,-1.0777868823255483,-1.2367027350580229,-0.3107582786264296,-1.0281991944275384,-1.2776123415617124,-0.3107582786264296,-0.9769001704425234,-1.3163954884093678,-0.3107582786264296,-0.9239751925332708,-1.3529876248805393,-0.3107582786264296,-0.8695123491023864,-1.387327846975316,-0.3107582786264296,-0.8136022881778704,-1.4193589987830308,-0.3107582786264296,-0.756338066538413,-1.4490277676126269,-0.3107582786264296,-0.6978149948295752,-1.476284772726373,-0.3107582786264296,-0.6381304789286232,-1.5010846475292288,-0.3107582786264296,-0.577383857822059,-1.5233861150770638,-0.3107582786264296,-0.5156762382656814,-1.5431520567780574,-0.3107582786264296,-0.45311032650236904,-1.5603495741729339,-0.3107582786264296,-0.38979025731767214,-1.5749500436911965,-0.3107582786264296,-0.3258214207177467,-1.5869291642922414,-0.3107582786264296,-0.2613102865180801,-1.5962669979120372,-0.3107582786264296,-0.1963642271349983,-1.6029480026480667,-0.3107582786264296,-0.13109133887487204,-1.6069610586272896,-0.3107582786264296,-0.06560026201848343,-1.6082994865140736,-0.3107582786264296,-1.969598818229792e-16,-1.6069610586272896,-0.3107582786264296,0.06560026201848304,-1.6029480026480667,-0.3107582786264296,0.13109133887487165,-1.5962669979120372,-0.3107582786264296,0.1963642271349979,-1.5869291642922416,-0.3107582786264296,0.261310286518079,-1.5749500436911967,-0.3107582786264296,0.32582142071774567,-1.5603495741729339,-0.3107582786264296,0.3897902573176725,-1.5431520567780577,-0.3107582786264296,0.45311032650236865,-1.5233861150770638,-0.3107582786264296,0.5156762382656811,-1.501084647529229,-0.3107582786264296,0.5773838578220587,-1.476284772726373,-0.3107582786264296,0.6381304789286228,-1.4490277676126273,-0.3107582786264296,0.6978149948295742,-1.419358998783031,-0.3107582786264296,0.7563380665384126,-1.3873278469753163,-0.3107582786264296,0.8136022881778701,-1.352987624880539,-0.3107582786264296,0.8695123491023865,-1.3163954884093685,-0.3107582786264296,0.9239751925332699,-1.2776123415617127,-0.3107582786264296,0.9769001704425231,-1.2367027350580229,-0.3107582786264296,1.0281991944275382,-1.193734758900991,-0.3107582786264296,1.0777868823255485,-1.1487799290464562,-0.3107582786264296,1.125580700323816,-1.1019130683721559,-0.3107582786264296,1.1715011003290154,-1.053212182142432,-0.3107582786264296,1.2154716523671825,-1.0027583281761627,-0.3107582786264296,1.2574191717938692,-0.950635481934027,-0.3107582786264296,1.2972738411027538,-0.8969303967496361,-0.3107582786264296,1.334969326129995,-0.8417324594371678,-0.3107582786264296,1.3704428864609024,-0.7851335415158394,-0.3107582786264296,1.4036354798551611,-0.7272278462988321,-0.3107582786264296,1.4344918605168158,-0.6681117521011711,-0.3107582786264296,1.4629606710454441,-0.6078836518275291,-0.3107582786264296,1.4889945279154857,-0.5466437892069493,-0.3107582786264296,1.5125501003414403,-0.4844940919470555,-0.3107582786264296,1.5335881823976838,-0.42153800208543357,-0.3107582786264296,1.5520737582728643,-0.35788030382057284,-0.3107582786264296,1.5679760605502648,-0.29362694910891135,-0.3107582786264296,1.5812686214171283,-0.22888488131825807,-0.3107582786264296,1.591929316717724,-0.1637618572311086,-0.3107582786264296,1.5999404027768194,-0.09836626769411759,-0.3107582786264296,1.6052885459322763,-0.032806957212234616,-0.3107582786264296,1.6079648447276162,0.032806957212235456,-0.3107582786264296,1.6079648447276162,0.098366267694117,-0.3107582786264296,1.6052885459322763,0.163761857231108,-0.3107582786264296,1.5999404027768194,0.2288848813182575,-0.3107582786264296,1.5919293167177242,0.29362694910891074,-0.3107582786264296,1.5812686214171285,0.35788030382057223,-0.3107582786264296,1.5679760605502648,0.421538002085433,-0.3107582786264296,1.5520737582728645,0.48449409194705495,-0.3107582786264296,1.5335881823976838,0.5466437892069489,-0.3107582786264296,1.5125501003414406,0.6078836518275272,-0.3107582786264296,1.4889945279154864,0.6681117521011718,-0.3107582786264296,1.462960671045444,0.7272278462988327,-0.3107582786264296,1.434491860516815,0.7851335415158389,-0.3107582786264296,1.4036354798551616,0.8417324594371662,-0.3107582786264296,1.3704428864609033,0.8969303967496356,-0.3107582786264296,1.3349693261299953,0.9506354819340278,-0.3107582786264296,1.2972738411027531,1.0027583281761623,-0.3107582786264296,1.2574191717938694,1.0532121821424318,-0.3107582786264296,1.2154716523671831,1.1019130683721554,-0.3107582786264296,1.1715011003290157,1.1487799290464553,-0.3107582786264296,1.125580700323817,1.193734758900991,-0.3107582786264296,1.0777868823255485,1.2367027350580235,-0.3107582786264296,1.0281991944275373,1.2776123415617122,-0.3107582786264296,0.9769001704425236,1.3163954884093676,-0.3107582786264296,0.923975192533271,1.3529876248805384,-0.3107582786264296,0.8695123491023876,1.387327846975316,-0.3107582786264296,0.8136022881778706,1.4193589987830313,-0.3107582786264296,0.7563380665384125,1.4490277676126269,-0.3107582786264296,0.6978149948295753,1.4762847727263726,-0.3107582786264296,0.6381304789286241,1.5010846475292288,-0.3107582786264296,0.5773838578220594,1.5233861150770636,-0.3107582786264296,0.5156762382656823,1.5431520567780577,-0.3107582786264296,0.4531103265023686,1.5603495741729336,-0.3107582786264296,0.38979025731767303,1.5749500436911967,-0.3107582786264296,0.3258214207177462,1.5869291642922414,-0.3107582786264296,0.26131028651808025,1.5962669979120372,-0.3107582786264296,0.1963642271349992,1.6029480026480667,-0.3107582786264296,0.13109133887487223,1.6069610586272896,-0.3107582786264296,0.06560026201848292,1.6082994865140736,-0.3107582786264296,0.0,1.631195768129689,-0.3376143340478152,0.0,1.6298382859423377,-0.3376143340478152,-0.066534168971652,1.6257680987754162,-0.3376143340478152,-0.1329575983852523,1.6189919810537805,-0.3376143340478152,-0.1991597329978148,1.6095212109566486,-0.3376143340478152,-0.2650303858897104,1.5973715516461853,-0.3376143340478152,-0.33045992185992024,1.5825632250312724,-0.3376143340478152,-0.3953394399030082,1.5651208781101393,-0.3376143340478152,-0.4595609544640971,1.545073541947866,-0.3376143340478152,-0.5230175751701678,1.522454583357039,-0.3376143340478152,-0.5856036847385359,1.497301649361987,-0.3376143340478152,-0.6472151147663958,1.469656604539023,-0.3376143340478152,-0.7077493191088466,1.4395654613369875,-0.3376143340478152,-0.7671055445568309,1.4070783034940713,-0.3376143340478152,-0.8251849985309065,1.372249202678373,-0.3376143340478152,-0.8818910135117464,1.3351361284909458,-0.3376143340478152,-0.9371292079336843,1.295800851981116,-0.3376143340478152,-0.9908076432735167,1.2543088428346685,-0.3376143340478152,-1.0428369770730996,1.2107291604060162,-0.3376143340478152,-1.093130611641056,1.1651343387757187,-0.3376143340478152,-1.1416048381860837,1.117600266024665,-0.3376143340478152,-1.18817897614198,1.068206057925851,-0.3376143340478152,-1.2327755074524798,1.0170339262639845,-0.3376143340478152,-1.275320205592411,0.9641690420020812,-0.3376143340478152,-1.3157422591104213,0.9096993935228036,-0.3376143340478152,-1.353974389487648,0.8537156401804815,-0.3376143340478152,-1.3899529631161758,0.7963109614075633,-0.3376143340478152,-1.4236180972108945,0.7375809016266471,-0.3376143340478152,-1.4549137594784882,0.6776232112262168,-0.3376143340478152,-1.4837878613776552,0.61653768386477,-0.3376143340478152,-1.5101923448153454,0.5544259903741202,-0.3376143340478152,-1.5340832621347127,0.4913915095383309,-0.3376143340478152,-1.5554208492616544,0.4275391560299331,-0.3376143340478152,-1.574169591888186,0.36297520578980613,-0.3376143340478152,-1.5902982845825038,0.29780711914136226,-0.3376143340478152,-1.6037800827273447,0.23214336193344412,-0.3376143340478152,-1.6145925472002043,0.16609322500962212,-0.3376143340478152,-1.6227176817210407,0.09976664230437342,-0.3376143340478152,-1.6281419628053067,0.03327400786889577,-0.3376143340478152,-1.6308563622724555,-0.03327400786889557,-0.3376143340478152,-1.6308563622724555,-0.09976664230437321,-0.3376143340478152,-1.6281419628053069,-0.16609322500962156,-0.3376143340478152,-1.6227176817210407,-0.23214336193344395,-0.3376143340478152,-1.6145925472002043,-0.2978071191413621,-0.3376143340478152,-1.6037800827273447,-0.3629752057898056,-0.3376143340478152,-1.5902982845825038,-0.42753915602993287,-0.3376143340478152,-1.5741695918881862,-0.49139150953833033,-0.3376143340478152,-1.5554208492616546,-0.55442599037412,-0.3376143340478152,-1.534083262134713,-0.6165376838647698,-0.3376143340478152,-1.5101923448153454,-0.6776232112262164,-0.3376143340478152,-1.4837878613776554,-0.7375809016266472,-0.3376143340478152,-1.4549137594784882,-0.7963109614075632,-0.3376143340478152,-1.4236180972108945,-0.8537156401804818,-0.3376143340478152,-1.3899529631161753,-0.9096993935228033,-0.3376143340478152,-1.3539743894876484,-0.9641690420020809,-0.3376143340478152,-1.3157422591104215,-1.0170339262639843,-0.3376143340478152,-1.2753202055924115,-1.0682060579258512,-0.3376143340478152,-1.2327755074524798,-1.1176002660246651,-0.3376143340478152,-1.18817897614198,-1.1651343387757187,-0.3376143340478152,-1.1416048381860837,-1.2107291604060162,-0.3376143340478152,-1.093130611641056,-1.254308842834668,-0.3376143340478152,-1.0428369770731003,-1.295800851981116,-0.3376143340478152,-0.9908076432735164,-1.3351361284909455,-0.3376143340478152,-0.9371292079336848,-1.3722492026783732,-0.3376143340478152,-0.8818910135117461,-1.407078303494071,-0.3376143340478152,-0.8251849985309068,-1.4395654613369873,-0.3376143340478152,-0.7671055445568311,-1.4696566045390227,-0.3376143340478152,-0.707749319108847,-1.497301649361987,-0.3376143340478152,-0.6472151147663959,-1.522454583357039,-0.3376143340478152,-0.5856036847385359,-1.545073541947866,-0.3376143340478152,-0.5230175751701678,-1.5651208781101391,-0.3376143340478152,-0.4595609544640977,-1.5825632250312724,-0.3376143340478152,-0.39533943990300807,-1.597371551646185,-0.3376143340478152,-0.33045992185992074,-1.6095212109566486,-0.3376143340478152,-0.26503038588971084,-1.6189919810537805,-0.3376143340478152,-0.19915973299781523,-1.6257680987754162,-0.3376143340478152,-0.13295759838525265,-1.6298382859423377,-0.3376143340478152,-0.06653416897165226,-1.631195768129689,-0.3376143340478152,-1.9976386762227318e-16,-1.6298382859423377,-0.3376143340478152,0.06653416897165186,-1.6257680987754162,-0.3376143340478152,0.13295759838525223,-1.6189919810537805,-0.3376143340478152,0.19915973299781484,-1.6095212109566488,-0.3376143340478152,0.2650303858897098,-1.5973715516461853,-0.3376143340478152,0.3304599218599196,-1.5825632250312724,-0.3376143340478152,0.39533943990300835,-1.5651208781101393,-0.3376143340478152,0.4595609544640974,-1.545073541947866,-0.3376143340478152,0.5230175751701674,-1.522454583357039,-0.3376143340478152,0.5856036847385355,-1.497301649361987,-0.3376143340478152,0.6472151147663955,-1.4696566045390231,-0.3376143340478152,0.707749319108846,-1.4395654613369875,-0.3376143340478152,0.7671055445568308,-1.4070783034940713,-0.3376143340478152,0.8251849985309063,-1.372249202678373,-0.3376143340478152,0.8818910135117464,-1.335136128490946,-0.3376143340478152,0.9371292079336839,-1.2958008519811164,-0.3376143340478152,0.9908076432735161,-1.2543088428346683,-0.3376143340478152,1.0428369770730999,-1.210729160406016,-0.3376143340478152,1.0931306116410562,-1.165134338775719,-0.3376143340478152,1.1416048381860837,-1.1176002660246653,-0.3376143340478152,1.1881789761419799,-1.0682060579258519,-0.3376143340478152,1.232775507452479,-1.0170339262639845,-0.3376143340478152,1.275320205592411,-0.9641690420020804,-0.3376143340478152,1.3157422591104218,-0.9096993935228036,-0.3376143340478152,1.353974389487648,-0.8537156401804822,-0.3376143340478152,1.389952963116175,-0.7963109614075634,-0.3376143340478152,1.4236180972108945,-0.7375809016266464,-0.3376143340478152,1.4549137594784887,-0.6776232112262167,-0.3376143340478152,1.4837878613776554,-0.6165376838647705,-0.3376143340478152,1.5101923448153451,-0.5544259903741198,-0.3376143340478152,1.534083262134713,-0.49139150953833105,-0.3376143340478152,1.5554208492616544,-0.427539156029934,-0.3376143340478152,1.574169591888186,-0.36297520578980635,-0.3376143340478152,1.5902982845825038,-0.29780711914136176,-0.3376143340478152,1.6037800827273447,-0.23214336193344395,-0.3376143340478152,1.6145925472002043,-0.16609322500962267,-0.3376143340478152,1.6227176817210407,-0.09976664230437325,-0.3376143340478152,1.6281419628053069,-0.03327400786889488,-0.3376143340478152,1.6308563622724555,0.033274007868895734,-0.3376143340478152,1.6308563622724555,0.09976664230437264,-0.3376143340478152,1.6281419628053069,0.1660932250096221,-0.3376143340478152,1.6227176817210407,0.23214336193344337,-0.3376143340478152,1.6145925472002045,0.2978071191413612,-0.3376143340478152,1.6037800827273447,0.36297520578980574,-0.3376143340478152,1.5902982845825038,0.4275391560299334,-0.3376143340478152,1.574169591888186,0.4913915095383305,-0.3376143340478152,1.5554208492616546,0.5544259903741192,-0.3376143340478152,1.5340832621347131,0.6165376838647686,-0.3376143340478152,1.5101923448153458,0.6776232112262175,-0.3376143340478152,1.483787861377655,0.737580901626647,-0.3376143340478152,1.4549137594784882,0.796310961407563,-0.3376143340478152,1.4236180972108947,0.8537156401804805,-0.3376143340478152,1.3899529631161762,0.909699393522803,-0.3376143340478152,1.3539743894876484,0.9641690420020812,-0.3376143340478152,1.315742259110421,1.017033926263984,-0.3376143340478152,1.2753202055924115,1.0682060579258514,-0.3376143340478152,1.2327755074524795,1.1176002660246647,-0.3376143340478152,1.1881789761419803,1.165134338775718,-0.3376143340478152,1.1416048381860846,1.210729160406016,-0.3376143340478152,1.0931306116410562,1.254308842834669,-0.3376143340478152,1.0428369770730992,1.2958008519811157,-0.3376143340478152,0.9908076432735167,1.3351361284909453,-0.3376143340478152,0.937129207933685,1.3722492026783724,-0.3376143340478152,0.8818910135117475,1.407078303494071,-0.3376143340478152,0.8251849985309069,1.4395654613369877,-0.3376143340478152,0.7671055445568307,1.4696566045390227,-0.3376143340478152,0.7077493191088471,1.4973016493619866,-0.3376143340478152,0.6472151147663968,1.522454583357039,-0.3376143340478152,0.5856036847385362,1.5450735419478656,-0.3376143340478152,0.5230175751701687,1.5651208781101393,-0.3376143340478152,0.4595609544640973,1.5825632250312722,-0.3376143340478152,0.39533943990300896,1.5973715516461853,-0.3376143340478152,0.33045992185992024,1.6095212109566486,-0.3376143340478152,0.265030385889711,1.6189919810537805,-0.3376143340478152,0.19915973299781614,1.6257680987754162,-0.3376143340478152,0.13295759838525284,1.6298382859423377,-0.3376143340478152,0.06653416897165174,1.631195768129689,-0.3376143340478152,0.0,1.6559294138770655,-0.36278841183949123,0.0,1.6545513483335126,-0.36278841183949123,-0.06754301940983563,1.6504194453569505,-0.36278841183949123,-0.13497362012962782,1.6435405820921103,-0.36278841183949123,-0.20217957057914823,1.6339262077280265,-0.36278841183949123,-0.2690490130863728,1.6215923244419939,-0.36278841183949123,-0.33547065006353777,1.6065594607655214,-0.36278841183949123,-0.40133392925099054,1.5888526374166172,-0.36278841183949123,-0.4665292277205157,1.5685013256552747,-0.36278841183949123,-0.5309480343318803,1.545539398231466,-0.36278841183949123,-0.5944831303389181,1.5200050730072951,-0.36278841183949123,-0.657028767844551,1.4919408493471362,-0.36278841183949123,-0.7184808458077275,1.4613934373816366,-0.36278841183949123,-0.7787370833093326,1.4284136802633145,-0.36278841183949123,-0.8376971897886821,1.3930564695431487,-0.36278841183949123,-0.8952630319672652,1.355380653809009,-0.36278841183949123,-0.9513387971819005,1.3154489407379901,-0.36278841183949123,-1.005831152855459,1.2733277927256714,-0.36278841183949123,-1.0586494018397268,1.2290873162660185,-0.36278841183949123,-1.1097056333718587,1.1828011452660425,-0.36278841183949123,-1.1589148693931663,1.1345463184894296,-0.36278841183949123,-1.2061952059867107,1.0844031513331207,-0.36278841183949123,-1.2514679496982886,1.0324551021502604,-0.36278841183949123,-1.2946577485139217,0.9787886333420036,-0.36278841183949123,-1.3356927172758466,0.9234930674493825,-0.36278841183949123,-1.3745045573282657,0.8666604384847523,-0.36278841183949123,-1.4110286701937205,0.8083853387502598,-0.36278841183949123,-1.4452042650908796,0.7487647613982927,-0.36278841183949123,-1.476974460114798,0.6878979389959473,-0.36278841183949123,-1.5062863769112331,0.625886178362217,-0.36278841183949123,-1.5330912286874452,0.562832691952788,-0.36278841183949123,-1.5573444014130013,0.49884242607309187,-0.36278841183949123,-1.5790055280754256,0.43402188620554005,-0.36278841183949123,-1.5980385558671064,0.368478959741657,-0.36278841183949123,-1.6144118061916384,0.30232273641416507,-0.36278841183949123,-1.6280980273897188,0.23566332672789075,-0.36278841183949123,-1.6390744400968447,0.1686116786916946,-0.36278841183949123,-1.647322775157319,0.10127939315646207,-0.36278841183949123,-1.652829304031454,0.03377853806649941,-0.36278841183949123,-1.6555848616453739,-0.033778538066499206,-0.36278841183949123,-1.6555848616453739,-0.10127939315646187,-0.36278841183949123,-1.6528293040314541,-0.16861167869169405,-0.36278841183949123,-1.647322775157319,-0.23566332672789056,-0.36278841183949123,-1.6390744400968447,-0.30232273641416485,-0.36278841183949123,-1.6280980273897188,-0.36847895974165645,-0.36278841183949123,-1.6144118061916384,-0.43402188620553983,-0.36278841183949123,-1.5980385558671064,-0.4988424260730913,-0.36278841183949123,-1.5790055280754258,-0.5628326919527877,-0.36278841183949123,-1.5573444014130016,-0.6258861783622168,-0.36278841183949123,-1.5330912286874454,-0.6878979389959469,-0.36278841183949123,-1.5062863769112333,-0.7487647613982928,-0.36278841183949123,-1.476974460114798,-0.8083853387502596,-0.36278841183949123,-1.4452042650908796,-0.8666604384847526,-0.36278841183949123,-1.41102867019372,-0.9234930674493822,-0.36278841183949123,-1.3745045573282662,-0.9787886333420033,-0.36278841183949123,-1.3356927172758468,-1.0324551021502602,-0.36278841183949123,-1.294657748513922,-1.084403151333121,-0.36278841183949123,-1.2514679496982886,-1.1345463184894298,-0.36278841183949123,-1.2061952059867107,-1.1828011452660425,-0.36278841183949123,-1.1589148693931663,-1.2290873162660185,-0.36278841183949123,-1.1097056333718587,-1.273327792725671,-0.36278841183949123,-1.0586494018397272,-1.3154489407379901,-0.36278841183949123,-1.0058311528554589,-1.3553806538090085,-0.36278841183949123,-0.9513387971819011,-1.3930564695431487,-0.36278841183949123,-0.8952630319672651,-1.4284136802633143,-0.36278841183949123,-0.8376971897886824,-1.4613934373816364,-0.36278841183949123,-0.7787370833093327,-1.491940849347136,-0.36278841183949123,-0.7184808458077278,-1.5200050730072951,-0.36278841183949123,-0.6570287678445511,-1.545539398231466,-0.36278841183949123,-0.5944831303389182,-1.5685013256552747,-0.36278841183949123,-0.5309480343318803,-1.5888526374166172,-0.36278841183949123,-0.4665292277205163,-1.6065594607655214,-0.36278841183949123,-0.4013339292509904,-1.6215923244419939,-0.36278841183949123,-0.33547065006353827,-1.6339262077280265,-0.36278841183949123,-0.26904901308637325,-1.6435405820921103,-0.36278841183949123,-0.20217957057914865,-1.6504194453569505,-0.36278841183949123,-0.13497362012962819,-1.6545513483335126,-0.36278841183949123,-0.0675430194098359,-1.6559294138770655,-0.36278841183949123,-2.027928656318501e-16,-1.6545513483335126,-0.36278841183949123,0.0675430194098355,-1.6504194453569505,-0.36278841183949123,0.13497362012962777,-1.6435405820921103,-0.36278841183949123,0.20217957057914826,-1.6339262077280268,-0.36278841183949123,0.2690490130863722,-1.621592324441994,-0.36278841183949123,0.33547065006353716,-1.6065594607655214,-0.36278841183949123,0.4013339292509907,-1.5888526374166172,-0.36278841183949123,0.46652922772051597,-1.5685013256552747,-0.36278841183949123,0.53094803433188,-1.5455393982314662,-0.36278841183949123,0.5944831303389178,-1.5200050730072951,-0.36278841183949123,0.6570287678445508,-1.4919408493471364,-0.36278841183949123,0.7184808458077268,-1.4613934373816366,-0.36278841183949123,0.7787370833093323,-1.4284136802633145,-0.36278841183949123,0.837697189788682,-1.3930564695431487,-0.36278841183949123,0.8952630319672652,-1.3553806538090092,-0.36278841183949123,0.9513387971819002,-1.3154489407379906,-0.36278841183949123,1.0058311528554584,-1.2733277927256712,-0.36278841183949123,1.058649401839727,-1.2290873162660183,-0.36278841183949123,1.109705633371859,-1.182801145266043,-0.36278841183949123,1.1589148693931661,-1.13454631848943,-0.36278841183949123,1.2061952059867105,-1.0844031513331216,-0.36278841183949123,1.251467949698288,-1.0324551021502604,-0.36278841183949123,1.2946577485139217,-0.9787886333420028,-0.36278841183949123,1.335692717275847,-0.9234930674493825,-0.36278841183949123,1.3745045573282657,-0.8666604384847529,-0.36278841183949123,1.4110286701937198,-0.80838533875026,-0.36278841183949123,1.4452042650908794,-0.7487647613982918,-0.36278841183949123,1.4769744601147987,-0.6878979389959472,-0.36278841183949123,1.5062863769112333,-0.6258861783622176,-0.36278841183949123,1.5330912286874452,-0.5628326919527874,-0.36278841183949123,1.5573444014130016,-0.4988424260730921,-0.36278841183949123,1.5790055280754256,-0.43402188620554094,-0.36278841183949123,1.5980385558671062,-0.36847895974165723,-0.36278841183949123,1.6144118061916384,-0.3023227364141646,-0.36278841183949123,1.6280980273897188,-0.23566332672789056,-0.36278841183949123,1.6390744400968447,-0.1686116786916952,-0.36278841183949123,1.6473227751573187,-0.10127939315646191,-0.36278841183949123,1.6528293040314541,-0.033778538066498505,-0.36278841183949123,1.6555848616453739,0.03377853806649937,-0.36278841183949123,1.6555848616453739,0.1012793931564613,-0.36278841183949123,1.6528293040314541,0.16861167869169458,-0.36278841183949123,1.647322775157319,0.23566332672788998,-0.36278841183949123,1.6390744400968449,0.30232273641416396,-0.36278841183949123,1.6280980273897188,0.3684789597416566,-0.36278841183949123,1.6144118061916384,0.4340218862055404,-0.36278841183949123,1.5980385558671064,0.49884242607309154,-0.36278841183949123,1.5790055280754258,0.562832691952787,-0.36278841183949123,1.5573444014130018,0.6258861783622156,-0.36278841183949123,1.5330912286874459,0.687897938995948,-0.36278841183949123,1.506286376911233,0.7487647613982926,-0.36278841183949123,1.476974460114798,0.8083853387502595,-0.36278841183949123,1.4452042650908798,0.8666604384847513,-0.36278841183949123,1.411028670193721,0.923493067449382,-0.36278841183949123,1.3745045573282662,0.9787886333420036,-0.36278841183949123,1.3356927172758464,1.0324551021502602,-0.36278841183949123,1.294657748513922,1.0844031513331212,-0.36278841183949123,1.2514679496982883,1.1345463184894296,-0.36278841183949123,1.206195205986711,1.182801145266042,-0.36278841183949123,1.158914869393167,1.2290873162660183,-0.36278841183949123,1.109705633371859,1.2733277927256716,-0.36278841183949123,1.0586494018397261,1.31544894073799,-0.36278841183949123,1.005831152855459,1.3553806538090085,-0.36278841183949123,0.9513387971819012,1.3930564695431478,-0.36278841183949123,0.8952630319672663,1.4284136802633143,-0.36278841183949123,0.8376971897886826,1.4613934373816369,-0.36278841183949123,0.7787370833093322,1.491940849347136,-0.36278841183949123,0.718480845807728,1.5200050730072947,-0.36278841183949123,0.657028767844552,1.545539398231466,-0.36278841183949123,0.5944831303389184,1.5685013256552744,-0.36278841183949123,0.5309480343318812,1.5888526374166172,-0.36278841183949123,0.46652922772051586,1.6065594607655211,-0.36278841183949123,0.4013339292509913,1.6215923244419939,-0.36278841183949123,0.33547065006353777,1.6339262077280265,-0.36278841183949123,0.26904901308637347,1.6435405820921103,-0.36278841183949123,0.2021795705791496,1.6504194453569505,-0.36278841183949123,0.13497362012962835,1.6545513483335126,-0.36278841183949123,0.06754301940983537,1.6559294138770655,-0.36278841183949123,0.0,1.6823772019772305,-0.3861550960121942,0.0,1.6809771265670863,-0.3861550960121942,-0.0686217872908978,1.6767792306240012,-0.3861550960121942,-0.13712936038907683,1.669790501131486,-0.3861550960121942,-0.2054086952000704,1.660022570140069,-0.3861550960121942,-0.27334614750951625,1.6474916954068966,-0.3861550960121942,-0.3408286421327358,1.6322187333363003,-0.3861550960121942,-0.4077438611172197,1.6142291042663708,-0.3861550960121942,-0.4739804306847755,1.5935527501593136,-0.3861550960121942,-0.5394281066021898,1.5702240847660076,-0.3861550960121942,-0.6039779576718746,1.5442819363477138,-0.3861550960121942,-0.6675225470370953,1.5157694830502648,-0.3861550960121942,-0.729956111000016,1.4847341810383048,-0.3861550960121942,-0.791174735054937,1.451227685509184,-0.3861550960121942,-0.8510765268437335,1.4153057647179828,-0.3861550960121942,-0.9095617857456308,1.377028207156754,-0.3861550960121942,-0.9665331688190485,1.3364587220424773,-0.3861550960121942,-1.0218958528193192,1.2936648332793574,-0.3861550960121942,-1.075557692022621,1.2487177670719471,-0.3861550960121942,-1.1274293715934427,1.2016923333761604,-0.3861550960121942,-1.1774245562403112,1.1526668013854828,-0.3861550960121942,-1.2254600339123651,1.101722769259624,-0.3861550960121942,-1.2714558542976,1.048945028312434,-0.3861550960121942,-1.315335461892274,0.9944214218851276,-0.3861550960121942,-1.357025823419988,0.9382426991397137,-0.3861550960121942,-1.3964575493883655,0.8805023640159708,-0.3861550960121942,-1.4335650095810173,0.8212965196033672,-0.3861550960121942,-1.468286442292556,0.7607237081869527,-0.3861550960121942,-1.5005640571248624,0.6988847472334485,-0.3861550960121942,-1.5303441311734998,0.635882561590527,-0.3861550960121942,-1.5575770984441843,0.571822012178558,-0.3861550960121942,-1.582217632350493,0.5068097214599548,-0.3861550960121942,-1.6042247211554925,0.44095389597661006,-0.3861550960121942,-1.6235617362317283,0.3743641462507844,-0.3861550960121942,-1.6401964930259596,0.30715130434920956,-0.3861550960121942,-1.6541013046271726,0.23942723941405186,-0.3861550960121942,-1.6652530278487114,0.17130467146776357,-0.3861550960121942,-1.6736331017478283,0.10289698380173234,-0.3861550960121942,-1.6792275785185404,0.03431803426098059,-0.3861550960121942,-1.6820271467063774,-0.03431803426098038,-0.3861550960121942,-1.6820271467063774,-0.10289698380173214,-0.3861550960121942,-1.6792275785185407,-0.17130467146776301,-0.3861550960121942,-1.6736331017478283,-0.23942723941405167,-0.3861550960121942,-1.6652530278487114,-0.3071513043492094,-0.3861550960121942,-1.6541013046271726,-0.37436414625078385,-0.3861550960121942,-1.6401964930259596,-0.44095389597660983,-0.3861550960121942,-1.6235617362317285,-0.5068097214599543,-0.3861550960121942,-1.6042247211554927,-0.5718220121785579,-0.3861550960121942,-1.5822176323504933,-0.6358825615905269,-0.3861550960121942,-1.5575770984441846,-0.6988847472334481,-0.3861550960121942,-1.5303441311734998,-0.7607237081869528,-0.3861550960121942,-1.5005640571248624,-0.8212965196033671,-0.3861550960121942,-1.468286442292556,-0.8805023640159713,-0.3861550960121942,-1.4335650095810168,-0.9382426991397134,-0.3861550960121942,-1.396457549388366,-0.9944214218851273,-0.3861550960121942,-1.3570258234199881,-1.048945028312434,-0.3861550960121942,-1.3153354618922743,-1.1017227692596243,-0.3861550960121942,-1.2714558542976,-1.152666801385483,-0.3861550960121942,-1.2254600339123651,-1.2016923333761604,-0.3861550960121942,-1.1774245562403112,-1.2487177670719471,-0.3861550960121942,-1.1274293715934427,-1.293664833279357,-0.3861550960121942,-1.0755576920226215,-1.3364587220424773,-0.3861550960121942,-1.021895852819319,-1.3770282071567534,-0.3861550960121942,-0.966533168819049,-1.415305764717983,-0.3861550960121942,-0.9095617857456306,-1.4512276855091837,-0.3861550960121942,-0.8510765268437337,-1.4847341810383046,-0.3861550960121942,-0.7911747350549371,-1.5157694830502648,-0.3861550960121942,-0.7299561110000162,-1.5442819363477136,-0.3861550960121942,-0.6675225470370953,-1.5702240847660076,-0.3861550960121942,-0.6039779576718746,-1.5935527501593136,-0.3861550960121942,-0.5394281066021898,-1.6142291042663706,-0.3861550960121942,-0.47398043068477613,-1.6322187333363003,-0.3861550960121942,-0.40774386111721955,-1.6474916954068963,-0.3861550960121942,-0.34082864213273634,-1.660022570140069,-0.3861550960121942,-0.27334614750951675,-1.669790501131486,-0.3861550960121942,-0.20540869520007082,-1.6767792306240012,-0.3861550960121942,-0.1371293603890772,-1.6809771265670863,-0.3861550960121942,-0.06862178729089807,-1.6823772019772305,-0.3861550960121942,-2.0603178553598954e-16,-1.6809771265670863,-0.3861550960121942,0.06862178729089766,-1.6767792306240012,-0.3861550960121942,0.13712936038907675,-1.669790501131486,-0.3861550960121942,0.20540869520007043,-1.6600225701400693,-0.3861550960121942,0.27334614750951564,-1.6474916954068968,-0.3861550960121942,0.3408286421327352,-1.6322187333363003,-0.3861550960121942,0.4077438611172199,-1.6142291042663708,-0.3861550960121942,0.4739804306847758,-1.5935527501593136,-0.3861550960121942,0.5394281066021894,-1.5702240847660078,-0.3861550960121942,0.6039779576718742,-1.5442819363477138,-0.3861550960121942,0.667522547037095,-1.5157694830502653,-0.3861550960121942,0.7299561110000152,-1.4847341810383048,-0.3861550960121942,0.7911747350549367,-1.451227685509184,-0.3861550960121942,0.8510765268437332,-1.4153057647179828,-0.3861550960121942,0.9095617857456308,-1.3770282071567541,-0.3861550960121942,0.966533168819048,-1.3364587220424777,-0.3861550960121942,1.0218958528193187,-1.2936648332793572,-0.3861550960121942,1.0755576920226213,-1.248717767071947,-0.3861550960121942,1.127429371593443,-1.2016923333761607,-0.3861550960121942,1.1774245562403112,-1.1526668013854833,-0.3861550960121942,1.225460033912365,-1.101722769259625,-0.3861550960121942,1.271455854297599,-1.048945028312434,-0.3861550960121942,1.315335461892274,-0.9944214218851268,-0.3861550960121942,1.3570258234199883,-0.9382426991397137,-0.3861550960121942,1.3964575493883655,-0.8805023640159716,-0.3861550960121942,1.4335650095810166,-0.8212965196033674,-0.3861550960121942,1.4682864422925557,-0.7607237081869518,-0.3861550960121942,1.5005640571248628,-0.6988847472334484,-0.3861550960121942,1.5303441311734998,-0.6358825615905276,-0.3861550960121942,1.5575770984441841,-0.5718220121785575,-0.3861550960121942,1.5822176323504933,-0.506809721459955,-0.3861550960121942,1.6042247211554925,-0.440953895976611,-0.3861550960121942,1.623561736231728,-0.3743641462507846,-0.3861550960121942,1.6401964930259596,-0.30715130434920906,-0.3861550960121942,1.6541013046271726,-0.23942723941405167,-0.3861550960121942,1.6652530278487114,-0.17130467146776415,-0.3861550960121942,1.673633101747828,-0.10289698380173216,-0.3861550960121942,1.6792275785185407,-0.03431803426097968,-0.3861550960121942,1.6820271467063774,0.034318034260980554,-0.3861550960121942,1.6820271467063774,0.10289698380173154,-0.3861550960121942,1.6792275785185407,0.17130467146776354,-0.3861550960121942,1.6736331017478283,0.23942723941405106,-0.3861550960121942,1.6652530278487117,0.30715130434920845,-0.3861550960121942,1.6541013046271729,0.374364146250784,-0.3861550960121942,1.6401964930259596,0.44095389597661044,-0.3861550960121942,1.6235617362317283,0.5068097214599544,-0.3861550960121942,1.6042247211554927,0.571822012178557,-0.3861550960121942,1.5822176323504933,0.6358825615905257,-0.3861550960121942,1.557577098444185,0.6988847472334492,-0.3861550960121942,1.5303441311734995,0.7607237081869526,-0.3861550960121942,1.5005640571248624,0.8212965196033669,-0.3861550960121942,1.4682864422925561,0.8805023640159699,-0.3861550960121942,1.4335650095810177,0.9382426991397131,-0.3861550960121942,1.396457549388366,0.9944214218851276,-0.3861550960121942,1.3570258234199877,1.0489450283124337,-0.3861550960121942,1.3153354618922743,1.1017227692596248,-0.3861550960121942,1.2714558542975998,1.1526668013854826,-0.3861550960121942,1.2254600339123651,1.2016923333761598,-0.3861550960121942,1.177424556240312,1.248717767071947,-0.3861550960121942,1.127429371593443,1.2936648332793577,-0.3861550960121942,1.0755576920226204,1.336458722042477,-0.3861550960121942,1.0218958528193192,1.3770282071567534,-0.3861550960121942,0.9665331688190493,1.415305764717982,-0.3861550960121942,0.909561785745632,1.4512276855091837,-0.3861550960121942,0.8510765268437338,1.484734181038305,-0.3861550960121942,0.7911747350549366,1.5157694830502648,-0.3861550960121942,0.7299561110000164,1.5442819363477132,-0.3861550960121942,0.6675225470370963,1.5702240847660076,-0.3861550960121942,0.6039779576718749,1.5935527501593132,-0.3861550960121942,0.5394281066021908,1.6142291042663708,-0.3861550960121942,0.4739804306847757,1.6322187333363,-0.3861550960121942,0.4077438611172205,1.6474916954068966,-0.3861550960121942,0.3408286421327358,1.660022570140069,-0.3861550960121942,0.2733461475095169,1.669790501131486,-0.3861550960121942,0.2054086952000718,1.6767792306240012,-0.3861550960121942,0.13712936038907736,1.6809771265670863,-0.3861550960121942,0.06862178729089753,1.6823772019772305,-0.3861550960121942,0.0,1.7104073708802332,-0.4075979749207559,0.0,1.7089839687451565,-0.4075979749207559,-0.0697650982475187,1.7047161314523054,-0.4075979749207559,-0.13941407937406375,1.6976109623956648,-0.4075979749207559,-0.20883101952414984,1.6876802874279264,-0.4075979749207559,-0.27790038105159715,1.674940635177524,-0.4075979749207559,-0.3465072048205408,1.6594132095383625,-0.4075979749207559,-0.4145373015435676,1.6411238543780293,-0.4075979749207559,-0.4818774418385156,1.6201030105232284,-0.4075979749207559,-0.5484155446876061,1.596385665094026,-0.4075979749207559,-0.6140408639852367,1.570011293271244,-0.4075979749207559,-0.6786441728639445,1.5410237925939168,-0.4075979749207559,-0.7421179454917475,1.5094714098961697,-0.4075979749207559,-0.8043565360382805,1.4754066610051242,-0.4075979749207559,-0.8652563545118533,1.4388862433334886,-0.4075979749207559,-0.9247160391747687,1.3999709415123074,-0.4075979749207559,-0.9826366252499321,1.3587255262209428,-0.4075979749207559,-1.0389217096379562,1.3152186463826723,-0.4075979749207559,-1.0934776113706044,1.269522714905329,-0.4075979749207559,-1.1462135275335195,1.2217137881571631,-0.4075979749207559,-1.197041684398712,1.1718714393785243,-0.4075979749207559,-1.245877483515268,1.1200786262400548,-0.4075979749207559,-1.2926396425151223,1.066421552767834,-0.4075979749207559,-1.3372503303995378,1.0109895258652846,-0.4075979749207559,-1.3796352970811232,0.953874806670645,-0.4075979749207559,-1.4197239969657733,0.8951724569974109,-0.4075979749207559,-1.4574497063688512,0.8349801811133276,-0.4075979749207559,-1.4927496345701734,0.7733981631212792,-0.4075979749207559,-1.5255650283229696,0.7105289002127345,-0.4075979749207559,-1.5558412696428623,0.6464770320712901,-0.4075979749207559,-1.5835279667141113,0.5813491667102424,-0.4075979749207559,-1.6085790377618185,0.5152537030340695,-0.4075979749207559,-1.6309527877504948,0.44830065041915435,-0.4075979749207559,-1.6506119777813302,0.3806014456140338,-0.4075979749207559,-1.6675238870726679,0.3122687672639278,-0.4075979749207559,-1.6816603674205142,0.24341634836825557,-0.4075979749207559,-1.6929978900484477,0.17415878698328027,-0.4075979749207559,-1.701517584768945,0.10461135548495676,-0.4075979749207559,-1.7072052713909454,0.0348898087094359,-0.4075979749207559,-1.7100514833213796,-0.03488980870943569,-0.4075979749207559,-1.7100514833213796,-0.10461135548495655,-0.4075979749207559,-1.7072052713909456,-0.17415878698327972,-0.4075979749207559,-1.701517584768945,-0.24341634836825538,-0.4075979749207559,-1.6929978900484477,-0.31226876726392766,-0.4075979749207559,-1.6816603674205142,-0.38060144561403325,-0.4075979749207559,-1.6675238870726679,-0.4483006504191542,-0.4075979749207559,-1.6506119777813304,-0.5152537030340689,-0.4075979749207559,-1.630952787750495,-0.5813491667102423,-0.4075979749207559,-1.6085790377618188,-0.6464770320712899,-0.4075979749207559,-1.5835279667141116,-0.710528900212734,-0.4075979749207559,-1.5558412696428623,-0.7733981631212793,-0.4075979749207559,-1.5255650283229696,-0.8349801811133275,-0.4075979749207559,-1.4927496345701734,-0.8951724569974112,-0.4075979749207559,-1.4574497063688507,-0.9538748066706446,-0.4075979749207559,-1.4197239969657738,-1.0109895258652841,-0.4075979749207559,-1.3796352970811234,-1.0664215527678338,-0.4075979749207559,-1.3372503303995382,-1.120078626240055,-0.4075979749207559,-1.2926396425151223,-1.1718714393785246,-0.4075979749207559,-1.245877483515268,-1.2217137881571631,-0.4075979749207559,-1.197041684398712,-1.269522714905329,-0.4075979749207559,-1.1462135275335195,-1.315218646382672,-0.4075979749207559,-1.093477611370605,-1.3587255262209428,-0.4075979749207559,-1.038921709637956,-1.399970941512307,-0.4075979749207559,-0.9826366252499328,-1.4388862433334888,-0.4075979749207559,-0.9247160391747685,-1.4754066610051242,-0.4075979749207559,-0.8652563545118535,-1.5094714098961695,-0.4075979749207559,-0.8043565360382807,-1.5410237925939168,-0.4075979749207559,-0.7421179454917478,-1.5700112932712438,-0.4075979749207559,-0.6786441728639446,-1.596385665094026,-0.4075979749207559,-0.6140408639852368,-1.6201030105232284,-0.4075979749207559,-0.5484155446876061,-1.6411238543780293,-0.4075979749207559,-0.4818774418385162,-1.6594132095383625,-0.4075979749207559,-0.41453730154356744,-1.6749406351775238,-0.4075979749207559,-0.3465072048205413,-1.6876802874279264,-0.4075979749207559,-0.2779003810515976,-1.6976109623956648,-0.4075979749207559,-0.20883101952415026,-1.7047161314523054,-0.4075979749207559,-0.1394140793740641,-1.7089839687451565,-0.4075979749207559,-0.06976509824751898,-1.7104073708802332,-0.4075979749207559,-2.0946449119865175e-16,-1.7089839687451565,-0.4075979749207559,0.06976509824751856,-1.7047161314523054,-0.4075979749207559,0.13941407937406367,-1.6976109623956648,-0.4075979749207559,0.20883101952414987,-1.6876802874279266,-0.4075979749207559,0.2779003810515965,-1.6749406351775242,-0.4075979749207559,0.34650720482054015,-1.6594132095383625,-0.4075979749207559,0.41453730154356777,-1.6411238543780293,-0.4075979749207559,0.4818774418385159,-1.6201030105232284,-0.4075979749207559,0.5484155446876058,-1.596385665094026,-0.4075979749207559,0.6140408639852364,-1.570011293271244,-0.4075979749207559,0.6786441728639443,-1.5410237925939172,-0.4075979749207559,0.7421179454917467,-1.5094714098961697,-0.4075979749207559,0.8043565360382803,-1.4754066610051242,-0.4075979749207559,0.8652563545118531,-1.4388862433334886,-0.4075979749207559,0.9247160391747687,-1.3999709415123074,-0.4075979749207559,0.9826366252499318,-1.3587255262209432,-0.4075979749207559,1.0389217096379557,-1.3152186463826723,-0.4075979749207559,1.0934776113706046,-1.2695227149053288,-0.4075979749207559,1.1462135275335197,-1.2217137881571636,-0.4075979749207559,1.1970416843987117,-1.1718714393785248,-0.4075979749207559,1.2458774835152677,-1.1200786262400557,-0.4075979749207559,1.2926396425151214,-1.066421552767834,-0.4075979749207559,1.3372503303995378,-1.0109895258652837,-0.4075979749207559,1.3796352970811236,-0.953874806670645,-0.4075979749207559,1.4197239969657733,-0.8951724569974115,-0.4075979749207559,1.4574497063688507,-0.8349801811133277,-0.4075979749207559,1.4927496345701732,-0.7733981631212783,-0.4075979749207559,1.52556502832297,-0.7105289002127344,-0.4075979749207559,1.5558412696428623,-0.6464770320712907,-0.4075979749207559,1.5835279667141111,-0.581349166710242,-0.4075979749207559,1.6085790377618188,-0.5152537030340696,-0.4075979749207559,1.6309527877504948,-0.4483006504191553,-0.4075979749207559,1.65061197778133,-0.38060144561403403,-0.4075979749207559,1.6675238870726679,-0.3122687672639273,-0.4075979749207559,1.6816603674205142,-0.24341634836825538,-0.4075979749207559,1.6929978900484477,-0.17415878698328088,-0.4075979749207559,1.701517584768945,-0.10461135548495658,-0.4075979749207559,1.7072052713909456,-0.03488980870943497,-0.4075979749207559,1.7100514833213796,0.034889808709435866,-0.4075979749207559,1.7100514833213796,0.10461135548495595,-0.4075979749207559,1.7072052713909456,0.17415878698328024,-0.4075979749207559,1.701517584768945,0.24341634836825476,-0.4075979749207559,1.692997890048448,0.3122687672639267,-0.4075979749207559,1.6816603674205144,0.3806014456140334,-0.4075979749207559,1.6675238870726679,0.44830065041915473,-0.4075979749207559,1.6506119777813302,0.515253703034069,-0.4075979749207559,1.630952787750495,0.5813491667102414,-0.4075979749207559,1.608579037761819,0.6464770320712887,-0.4075979749207559,1.5835279667141118,0.7105289002127352,-0.4075979749207559,1.555841269642862,0.7733981631212791,-0.4075979749207559,1.5255650283229696,0.8349801811133273,-0.4075979749207559,1.4927496345701736,0.8951724569974099,-0.4075979749207559,1.4574497063688518,0.9538748066706444,-0.4075979749207559,1.4197239969657738,1.0109895258652846,-0.4075979749207559,1.379635297081123,1.0664215527678338,-0.4075979749207559,1.3372503303995382,1.1200786262400553,-0.4075979749207559,1.292639642515122,1.1718714393785243,-0.4075979749207559,1.2458774835152682,1.2217137881571627,-0.4075979749207559,1.1970416843987128,1.2695227149053288,-0.4075979749207559,1.1462135275335197,1.3152186463826727,-0.4075979749207559,1.0934776113706037,1.3587255262209428,-0.4075979749207559,1.0389217096379562,1.3999709415123067,-0.4075979749207559,0.9826366252499329,1.438886243333488,-0.4075979749207559,0.9247160391747699,1.4754066610051242,-0.4075979749207559,0.8652563545118537,1.5094714098961697,-0.4075979749207559,0.8043565360382802,1.5410237925939168,-0.4075979749207559,0.7421179454917479,1.5700112932712436,-0.4075979749207559,0.6786441728639456,1.596385665094026,-0.4075979749207559,0.614040863985237,1.6201030105232281,-0.4075979749207559,0.5484155446876071,1.6411238543780293,-0.4075979749207559,0.4818774418385158,1.6594132095383622,-0.4075979749207559,0.4145373015435684,1.674940635177524,-0.4075979749207559,0.3465072048205408,1.6876802874279264,-0.4075979749207559,0.27790038105159776,1.6976109623956648,-0.4075979749207559,0.20883101952415123,1.7047161314523054,-0.4075979749207559,0.1394140793740643,1.7089839687451565,-0.4075979749207559,0.06976509824751843,1.7104073708802332,-0.4075979749207559,0.0,1.7398802756945588,-0.42701022122106314,0.0,1.7384323461887785,-0.42701022122106314,-0.07096725636202274,1.7340909676072047,-0.42701022122106314,-0.14181639472953023,1.72686336574604,-0.42701022122106314,-0.21242949370375205,1.7167615702352956,-0.42701022122106314,-0.2826890247501936,1.703802394516662,-0.42701022122106314,-0.3524780478132824,1.6880074078591925,-0.42701022122106314,-0.42168040595155043,1.6694028994593837,-0.42701022122106314,-0.4901809186693996,1.6480198346854047,-0.42701022122106314,-0.5578655736236695,1.6238938035382942,-0.42701022122106314,-0.6246217163859309,1.597064961415919,-0.42701022122106314,-0.6903382379446641,1.5675779622782755,-0.42701022122106314,-0.754905759635243,1.5354818843253815,-0.42701022122106314,-0.8182168151899288,1.500830148311454,-0.42701022122106314,-0.8801660296048659,1.4636804286313372,-0.42701022122106314,-0.9406502945263767,1.4240945573271642,-0.42701022122106314,-0.9995689398646412,1.3821384211750236,-0.42701022122106314,-1.056823901349126,1.337881852022925,-0.42701022122106314,-1.1123198837468842,1.2913985105625783,-0.42701022122106314,-1.1659645194720671,1.2427657637284417,-0.42701022122106314,-1.2176685223226544,1.1920645559280956,-0.42701022122106314,-1.2673458360885266,1.1393792743182636,-0.42701022122106314,-1.3149137777835325,1.0847976083507194,-0.42701022122106314,-1.360293175263161,1.028410403821846,-0.42701022122106314,-1.4034084989987634,0.9703115116687759,-0.42701022122106314,-1.4441879877889958,0.9105976317637666,-0.42701022122106314,-1.4825637681992616,0.8493681519668072,-0.42701022122106314,-1.5184719675303409,0.7867249827043339,-0.42701022122106314,-1.5518528201281974,0.7227723873493809,-0.42701022122106314,-1.582650766858011,0.6576168086854889,-0.42701022122106314,-1.6108145475768734,0.5913666917431945,-0.42701022122106314,-1.6362972864512375,0.5241323033039786,-0.42701022122106314,-1.659056569977118,0.4560255483720931,-0.42701022122106314,-1.6790545175731804,0.3871597839197231,-0.42701022122106314,-1.696257844629232,0.3176496302154948,-0.42701022122106314,-1.7106379179051696,0.24761078005035025,-0.42701022122106314,-1.7221708031881828,0.17715980617831276,-0.42701022122106314,-1.7308373051288897,0.106413967292645,-0.42701022122106314,-1.7366229991900988,0.03549101286032425,-0.42701022122106314,-1.739518255655031,-0.035491012860324044,-0.42701022122106314,-1.739518255655031,-0.10641396729264478,-0.42701022122106314,-1.736622999190099,-0.1771598061783122,-0.42701022122106314,-1.7308373051288897,-0.24761078005035006,-0.42701022122106314,-1.7221708031881828,-0.31764963021549464,-0.42701022122106314,-1.7106379179051696,-0.3871597839197225,-0.42701022122106314,-1.696257844629232,-0.45602554837209286,-0.42701022122106314,-1.6790545175731806,-0.5241323033039781,-0.42701022122106314,-1.6590565699771183,-0.5913666917431943,-0.42701022122106314,-1.6362972864512377,-0.6576168086854887,-0.42701022122106314,-1.6108145475768736,-0.7227723873493804,-0.42701022122106314,-1.5826507668580112,-0.786724982704334,-0.42701022122106314,-1.5518528201281974,-0.849368151966807,-0.42701022122106314,-1.5184719675303409,-0.910597631763767,-0.42701022122106314,-1.4825637681992614,-0.9703115116687755,-0.42701022122106314,-1.4441879877889963,-1.0284104038218456,-0.42701022122106314,-1.4034084989987636,-1.0847976083507191,-0.42701022122106314,-1.3602931752631615,-1.1393792743182638,-0.42701022122106314,-1.3149137777835325,-1.1920645559280958,-0.42701022122106314,-1.2673458360885266,-1.2427657637284417,-0.42701022122106314,-1.2176685223226544,-1.2913985105625783,-0.42701022122106314,-1.1659645194720671,-1.3378818520229248,-0.42701022122106314,-1.1123198837468848,-1.3821384211750236,-0.42701022122106314,-1.0568239013491258,-1.4240945573271637,-0.42701022122106314,-0.9995689398646418,-1.4636804286313374,-0.42701022122106314,-0.9406502945263765,-1.5008301483114537,-0.42701022122106314,-0.8801660296048661,-1.5354818843253812,-0.42701022122106314,-0.8182168151899291,-1.5675779622782753,-0.42701022122106314,-0.7549057596352432,-1.5970649614159187,-0.42701022122106314,-0.6903382379446642,-1.6238938035382942,-0.42701022122106314,-0.624621716385931,-1.6480198346854047,-0.42701022122106314,-0.5578655736236695,-1.6694028994593835,-0.42701022122106314,-0.4901809186694003,-1.6880074078591925,-0.42701022122106314,-0.4216804059515503,-1.7038023945166618,-0.42701022122106314,-0.3524780478132829,-1.7167615702352956,-0.42701022122106314,-0.28268902475019403,-1.72686336574604,-0.42701022122106314,-0.2124294937037525,-1.7340909676072047,-0.42701022122106314,-0.1418163947295306,-1.7384323461887785,-0.42701022122106314,-0.07096725636202302,-1.7398802756945588,-0.42701022122106314,-2.1307388105289558e-16,-1.7384323461887785,-0.42701022122106314,0.0709672563620226,-1.7340909676072047,-0.42701022122106314,0.14181639472953014,-1.72686336574604,-0.42701022122106314,0.21242949370375208,-1.7167615702352959,-0.42701022122106314,0.28268902475019286,-1.7038023945166623,-0.42701022122106314,0.3524780478132818,-1.6880074078591925,-0.42701022122106314,0.42168040595155065,-1.6694028994593837,-0.42701022122106314,0.4901809186693999,-1.6480198346854047,-0.42701022122106314,0.5578655736236692,-1.6238938035382942,-0.42701022122106314,0.6246217163859306,-1.597064961415919,-0.42701022122106314,0.6903382379446638,-1.567577962278276,-0.42701022122106314,0.7549057596352422,-1.5354818843253815,-0.42701022122106314,0.8182168151899286,-1.500830148311454,-0.42701022122106314,0.8801660296048658,-1.4636804286313372,-0.42701022122106314,0.9406502945263767,-1.4240945573271644,-0.42701022122106314,0.9995689398646408,-1.382138421175024,-0.42701022122106314,1.0568239013491254,-1.337881852022925,-0.42701022122106314,1.1123198837468844,-1.291398510562578,-0.42701022122106314,1.1659645194720674,-1.2427657637284422,-0.42701022122106314,1.2176685223226542,-1.192064555928096,-0.42701022122106314,1.2673458360885264,-1.1393792743182645,-0.42701022122106314,1.3149137777835316,-1.0847976083507194,-0.42701022122106314,1.360293175263161,-1.0284104038218453,-0.42701022122106314,1.4034084989987636,-0.9703115116687759,-0.42701022122106314,1.4441879877889958,-0.9105976317637674,-0.42701022122106314,1.4825637681992612,-0.8493681519668074,-0.42701022122106314,1.5184719675303409,-0.786724982704333,-0.42701022122106314,1.551852820128198,-0.7227723873493808,-0.42701022122106314,1.5826507668580112,-0.6576168086854894,-0.42701022122106314,1.6108145475768731,-0.591366691743194,-0.42701022122106314,1.6362972864512377,-0.5241323033039789,-0.42701022122106314,1.659056569977118,-0.456025548372094,-0.42701022122106314,1.6790545175731801,-0.38715978391972333,-0.42701022122106314,1.696257844629232,-0.3176496302154943,-0.42701022122106314,1.7106379179051696,-0.24761078005035006,-0.42701022122106314,1.7221708031881828,-0.17715980617831337,-0.42701022122106314,1.7308373051288894,-0.10641396729264482,-0.42701022122106314,1.736622999190099,-0.03549101286032331,-0.42701022122106314,1.739518255655031,0.03549101286032422,-0.42701022122106314,1.739518255655031,0.10641396729264418,-0.42701022122106314,1.736622999190099,0.17715980617831276,-0.42701022122106314,1.7308373051288897,0.24761078005034942,-0.42701022122106314,1.722170803188183,0.31764963021549364,-0.42701022122106314,1.7106379179051698,0.3871597839197227,-0.42701022122106314,1.696257844629232,0.45602554837209347,-0.42701022122106314,1.6790545175731804,0.5241323033039783,-0.42701022122106314,1.6590565699771183,0.5913666917431935,-0.42701022122106314,1.636297286451238,0.6576168086854874,-0.42701022122106314,1.6108145475768738,0.7227723873493815,-0.42701022122106314,1.5826507668580108,0.7867249827043338,-0.42701022122106314,1.5518528201281974,0.8493681519668069,-0.42701022122106314,1.518471967530341,0.9105976317637656,-0.42701022122106314,1.4825637681992623,0.9703115116687754,-0.42701022122106314,1.4441879877889963,1.028410403821846,-0.42701022122106314,1.4034084989987632,1.084797608350719,-0.42701022122106314,1.3602931752631615,1.1393792743182642,-0.42701022122106314,1.3149137777835322,1.1920645559280953,-0.42701022122106314,1.2673458360885268,1.242765763728441,-0.42701022122106314,1.2176685223226553,1.291398510562578,-0.42701022122106314,1.1659645194720674,1.3378818520229254,-0.42701022122106314,1.1123198837468835,1.3821384211750234,-0.42701022122106314,1.056823901349126,1.4240945573271635,-0.42701022122106314,0.999568939864642,1.4636804286313363,-0.42701022122106314,0.9406502945263779,1.5008301483114537,-0.42701022122106314,0.8801660296048663,1.5354818843253817,-0.42701022122106314,0.8182168151899285,1.5675779622782753,-0.42701022122106314,0.7549057596352434,1.5970649614159182,-0.42701022122106314,0.6903382379446651,1.6238938035382942,-0.42701022122106314,0.6246217163859312,1.6480198346854043,-0.42701022122106314,0.5578655736236705,1.6694028994593837,-0.42701022122106314,0.49018091866939983,1.6880074078591922,-0.42701022122106314,0.42168040595155126,1.703802394516662,-0.42701022122106314,0.3524780478132824,1.7167615702352956,-0.42701022122106314,0.28268902475019425,1.72686336574604,-0.42701022122106314,0.2124294937037535,1.7340909676072047,-0.42701022122106314,0.14181639472953078,1.7384323461887785,-0.42701022122106314,0.07096725636202246,1.7398802756945588,-0.42701022122106314,0.0,1.7706490838909548,-0.44429512407851346,0.0,1.769175548562827,-0.44429512407851346,-0.07222227254315386,1.7647573951325752,-0.44429512407851346,-0.14432433824121546,1.7574019771805605,-0.44429512407851346,-0.2161861903215231,1.7471215370740638,-0.44429512407851346,-0.2876882218232753,1.7339331855910742,-0.44429512407851346,-0.35871142467151335,1.7178588734410878,-0.44429512407851346,-0.4291375877543168,1.6989253547303178,-0.44429512407851346,-0.49884949367353376,1.6771641424321253,-0.44429512407851346,-0.5677311138415716,1.6526114559367844,-0.44429512407851346,-0.63566780159953,1.6253081607678825,-0.44429512407851346,-0.7025464830352511,1.595299700565689,-0.44429512407851346,-0.7682558451836862,1.5626360214507007,-0.44429512407851346,-0.8326865212963417,1.5273714888932528,-0.44429512407851346,-0.8957312728714375,1.4895647972275603,-0.44429512407851346,-0.9572851681418107,1.4492788719607896,-0.44429512407851346,-1.0172457567234865,1.406580765039763,-0.44429512407851346,-1.0755132401342316,1.3615415432496099,-0.44429512407851346,-1.1319906378982771,1.3142361699301168,-0.44429512407851346,-1.1865839489607528,1.2647433802066463,-0.44429512407851346,-1.2392023081431633,1.2131455499432908,-0.44429512407851346,-1.2897581373795117,1.159528558636377,-0.44429512407851346,-1.3381672914813465,1.1039816464765195,-0.44429512407851346,-1.3843491981891238,1.0465972658171294,-0.44429512407851346,-1.4282269922767832,0.9874709272965975,-0.44429512407851346,-1.469727643486328,0.9267010408702622,-0.44429512407851346,-1.5087820780794854,0.864388752016751,-0.44429512407851346,-1.5453252938041238,0.8006377733913134,-0.44429512407851346,-1.5792964680840889,0.7355542122063393,-0.44429512407851346,-1.6106390592523792,0.6692463936263794,-0.44429512407851346,-1.639300900659169,0.601824680471597,-0.44429512407851346,-1.6652342874980497,0.533401289529745,-0.44429512407851346,-1.6883960562059728,0.46409010478239887,-0.44429512407851346,-1.7087476563047392,0.3940064878563078,-0.44429512407851346,-1.7262552145644672,0.32326708601535153,-0.44429512407851346,-1.7408895913822395,0.2519896380126821,-0.44429512407851346,-1.7526264292820974,0.18029277812618727,-0.44429512407851346,-1.7614461934556556,0.10829583870344531,-0.44429512407851346,-1.767334204275862,0.036118651544807416,-0.44429512407851346,-1.7702806617297897,-0.0361186515448072,-0.44429512407851346,-1.7702806617297897,-0.10829583870344509,-0.44429512407851346,-1.7673342042758622,-0.18029277812618666,-0.44429512407851346,-1.7614461934556556,-0.25198963801268187,-0.44429512407851346,-1.7526264292820974,-0.3232670860153513,-0.44429512407851346,-1.7408895913822395,-0.39400648785630726,-0.44429512407851346,-1.7262552145644672,-0.4640901047823987,-0.44429512407851346,-1.7087476563047395,-0.5334012895297444,-0.44429512407851346,-1.688396056205973,-0.6018246804715968,-0.44429512407851346,-1.66523428749805,-0.6692463936263792,-0.44429512407851346,-1.6393009006591692,-0.7355542122063388,-0.44429512407851346,-1.6106390592523794,-0.8006377733913135,-0.44429512407851346,-1.5792964680840889,-0.8643887520167509,-0.44429512407851346,-1.5453252938041238,-0.9267010408702625,-0.44429512407851346,-1.508782078079485,-0.987470927296597,-0.44429512407851346,-1.4697276434863284,-1.046597265817129,-0.44429512407851346,-1.4282269922767834,-1.1039816464765193,-0.44429512407851346,-1.3843491981891243,-1.1595285586363773,-0.44429512407851346,-1.3381672914813465,-1.213145549943291,-0.44429512407851346,-1.2897581373795117,-1.2647433802066463,-0.44429512407851346,-1.2392023081431633,-1.3142361699301168,-0.44429512407851346,-1.1865839489607528,-1.3615415432496094,-0.44429512407851346,-1.1319906378982776,-1.406580765039763,-0.44429512407851346,-1.0755132401342313,-1.4492788719607892,-0.44429512407851346,-1.0172457567234872,-1.4895647972275605,-0.44429512407851346,-0.9572851681418105,-1.5273714888932526,-0.44429512407851346,-0.8957312728714377,-1.5626360214507005,-0.44429512407851346,-0.8326865212963419,-1.5952997005656888,-0.44429512407851346,-0.7682558451836865,-1.6253081607678823,-0.44429512407851346,-0.7025464830352511,-1.6526114559367844,-0.44429512407851346,-0.6356678015995301,-1.6771641424321253,-0.44429512407851346,-0.5677311138415716,-1.6989253547303176,-0.44429512407851346,-0.4988494936735345,-1.7178588734410878,-0.44429512407851346,-0.42913758775431665,-1.733933185591074,-0.44429512407851346,-0.3587114246715139,-1.7471215370740638,-0.44429512407851346,-0.2876882218232758,-1.7574019771805605,-0.44429512407851346,-0.21618619032152353,-1.7647573951325752,-0.44429512407851346,-0.14432433824121582,-1.769175548562827,-0.44429512407851346,-0.07222227254315414,-1.7706490838909548,-0.44429512407851346,-2.1684197330002512e-16,-1.769175548562827,-0.44429512407851346,0.0722222725431537,-1.7647573951325752,-0.44429512407851346,0.14432433824121538,-1.7574019771805605,-0.44429512407851346,0.21618619032152311,-1.7471215370740638,-0.44429512407851346,0.28768822182327464,-1.7339331855910745,-0.44429512407851346,0.35871142467151274,-1.7178588734410878,-0.44429512407851346,0.42913758775431704,-1.6989253547303178,-0.44429512407851346,0.4988494936735341,-1.6771641424321253,-0.44429512407851346,0.5677311138415712,-1.6526114559367844,-0.44429512407851346,0.6356678015995296,-1.6253081607678825,-0.44429512407851346,0.7025464830352508,-1.5952997005656895,-0.44429512407851346,0.7682558451836854,-1.5626360214507007,-0.44429512407851346,0.8326865212963415,-1.5273714888932528,-0.44429512407851346,0.8957312728714374,-1.4895647972275603,-0.44429512407851346,0.9572851681418107,-1.4492788719607899,-0.44429512407851346,1.017245756723486,-1.4065807650397633,-0.44429512407851346,1.0755132401342309,-1.3615415432496096,-0.44429512407851346,1.1319906378982774,-1.3142361699301166,-0.44429512407851346,1.186583948960753,-1.2647433802066468,-0.44429512407851346,1.2392023081431631,-1.2131455499432913,-0.44429512407851346,1.2897581373795115,-1.1595285586363782,-0.44429512407851346,1.3381672914813456,-1.1039816464765195,-0.44429512407851346,1.3843491981891238,-1.0465972658171285,-0.44429512407851346,1.4282269922767836,-0.9874709272965975,-0.44429512407851346,1.469727643486328,-0.926701040870263,-0.44429512407851346,1.508782078079485,-0.8643887520167512,-0.44429512407851346,1.5453252938041235,-0.8006377733913125,-0.44429512407851346,1.5792964680840893,-0.7355542122063392,-0.44429512407851346,1.6106390592523794,-0.6692463936263799,-0.44429512407851346,1.6393009006591688,-0.6018246804715965,-0.44429512407851346,1.66523428749805,-0.5334012895297452,-0.44429512407851346,1.6883960562059728,-0.46409010478239987,-0.44429512407851346,1.708747656304739,-0.3940064878563081,-0.44429512407851346,1.7262552145644672,-0.323267086015351,-0.44429512407851346,1.7408895913822395,-0.25198963801268187,-0.44429512407851346,1.7526264292820974,-0.18029277812618788,-0.44429512407851346,1.7614461934556553,-0.10829583870344513,-0.44429512407851346,1.7673342042758622,-0.03611865154480646,-0.44429512407851346,1.7702806617297897,0.03611865154480738,-0.44429512407851346,1.7702806617297897,0.10829583870344447,-0.44429512407851346,1.7673342042758622,0.18029277812618724,-0.44429512407851346,1.7614461934556556,0.25198963801268126,-0.44429512407851346,1.7526264292820977,0.3232670860153503,-0.44429512407851346,1.7408895913822398,0.39400648785630743,-0.44429512407851346,1.7262552145644672,0.46409010478239926,-0.44429512407851346,1.7087476563047392,0.5334012895297445,-0.44429512407851346,1.688396056205973,0.601824680471596,-0.44429512407851346,1.6652342874980501,0.6692463936263779,-0.44429512407851346,1.6393009006591694,0.73555421220634,-0.44429512407851346,1.610639059252379,0.8006377733913133,-0.44429512407851346,1.5792964680840889,0.8643887520167507,-0.44429512407851346,1.5453252938041238,0.9267010408702612,-0.44429512407851346,1.508782078079486,0.9874709272965968,-0.44429512407851346,1.4697276434863284,1.0465972658171294,-0.44429512407851346,1.428226992276783,1.103981646476519,-0.44429512407851346,1.3843491981891243,1.1595285586363777,-0.44429512407851346,1.3381672914813463,1.2131455499432906,-0.44429512407851346,1.289758137379512,1.2647433802066457,-0.44429512407851346,1.2392023081431642,1.3142361699301166,-0.44429512407851346,1.186583948960753,1.36154154324961,-0.44429512407851346,1.1319906378982765,1.4065807650397628,-0.44429512407851346,1.0755132401342316,1.449278871960789,-0.44429512407851346,1.0172457567234872,1.4895647972275594,-0.44429512407851346,0.9572851681418119,1.5273714888932526,-0.44429512407851346,0.895731272871438,1.562636021450701,-0.44429512407851346,0.8326865212963414,1.5952997005656888,-0.44429512407851346,0.7682558451836867,1.625308160767882,-0.44429512407851346,0.7025464830352521,1.6526114559367844,-0.44429512407851346,0.6356678015995303,1.6771641424321249,-0.44429512407851346,0.5677311138415725,1.6989253547303178,-0.44429512407851346,0.498849493673534,1.7178588734410876,-0.44429512407851346,0.42913758775431765,1.7339331855910742,-0.44429512407851346,0.35871142467151335,1.7471215370740638,-0.44429512407851346,0.287688221823276,1.7574019771805605,-0.44429512407851346,0.21618619032152453,1.7647573951325752,-0.44429512407851346,0.14432433824121602,1.769175548562827,-0.44429512407851346,0.07222227254315358,1.7706490838909548,-0.44429512407851346,0.0,1.8025605068147044,-0.4593665709765281,0.0,1.8010604147795042,-0.4593665709765281,-0.07352389436342688,1.7965626354290531,-0.4593665709765281,-0.14692541546069762,1.7890746548731984,-0.4593665709765281,-0.2200823937038807,1.7786089361165922,-0.4593665709765281,-0.2928730665224915,1.7651828983152522,-0.4593665709765281,-0.3651762810252743,1.748818887784094,-0.4593665709765281,-0.43687169564723294,1.7295441408036962,-0.4593665709765281,-0.50783998044629,1.7073907382881992,-0.4593665709765281,-0.5779630157161982,1.6823955523897889,-0.4593665709765281,-0.6471240885851346,1.6546001851286394,-0.4593665709765281,-0.7152080872727576,1.624050899150455,-0.4593665709765281,-0.7821016926824045,1.5907985407268632,-0.4593665709765281,-0.8476935670095451,1.5548984551268124,-0.4593665709765281,-0.9118745390525694,1.5164103944998346,-0.4593665709765281,-0.974537785917478,1.4753984184244884,-0.4593665709765281,-1.035579010814048,1.4319307872875136,-0.4593665709765281,-1.0948966166475471,1.3860798486711536,-0.4593665709765281,-1.1523918751170696,1.3379219169377445,-0.4593665709765281,-1.2079690910390526,1.2875371462119902,-0.4593665709765281,-1.261535761622463,1.2350093969723328,-0.4593665709765281,-1.313002730430565,1.180426096473462,-0.4593665709765281,-1.3622843357730088,1.1238780932322785,-0.4593665709765281,-1.4092985532812632,1.0654595058195009,-0.4593665709765281,-1.4539671324300798,1.0052675662085964,-0.4593665709765281,-1.4962157267777696,0.9434024579427548,-0.4593665709765281,-1.53597401770852,0.8799671493892692,-0.4593665709765281,-1.5731758314707887,0.8150672223588497,-0.4593665709765281,-1.6077592493169843,0.7488106963751174,-0.4593665709765281,-1.6396667105611114,0.6813078488867718,-0.4593665709765281,-1.668845108382849,0.6126710317216578,-0.4593665709765281,-1.695245878218615,0.5430144840882357,-0.4593665709765281,-1.7188250785924877,0.4724541424356951,-0.4593665709765281,-1.7395434642524559,0.40110744748917554,-0.4593665709765281,-1.7573665514902697,0.32909314878126955,-0.4593665709765281,-1.7722646755361664,0.2565311070051459,-0.4593665709765281,-1.7842130399329554,0.18354209451825268,-0.4593665709765281,-1.7931917578072727,0.11024759432865006,-0.4593665709765281,-1.7991858849693183,0.03676959789853047,-0.4593665709765281,-1.8021854447859866,-0.03676959789853026,-0.4593665709765281,-1.8021854447859866,-0.11024759432864983,-0.4593665709765281,-1.7991858849693185,-0.1835420945182521,-0.4593665709765281,-1.7931917578072727,-0.2565311070051457,-0.4593665709765281,-1.7842130399329554,-0.3290931487812694,-0.4593665709765281,-1.7722646755361664,-0.40110744748917493,-0.4593665709765281,-1.7573665514902697,-0.4724541424356949,-0.4593665709765281,-1.739543464252456,-0.543014484088235,-0.4593665709765281,-1.7188250785924877,-0.6126710317216576,-0.4593665709765281,-1.695245878218615,-0.6813078488867716,-0.4593665709765281,-1.6688451083828493,-0.7488106963751169,-0.4593665709765281,-1.6396667105611116,-0.8150672223588498,-0.4593665709765281,-1.6077592493169843,-0.8799671493892691,-0.4593665709765281,-1.5731758314707887,-0.9434024579427552,-0.4593665709765281,-1.5359740177085197,-1.005267566208596,-0.4593665709765281,-1.49621572677777,-1.0654595058195004,-0.4593665709765281,-1.45396713243008,-1.1238780932322783,-0.4593665709765281,-1.4092985532812636,-1.1804260964734623,-0.4593665709765281,-1.3622843357730088,-1.235009396972333,-0.4593665709765281,-1.313002730430565,-1.2875371462119902,-0.4593665709765281,-1.261535761622463,-1.3379219169377445,-0.4593665709765281,-1.2079690910390526,-1.3860798486711532,-0.4593665709765281,-1.15239187511707,-1.4319307872875136,-0.4593665709765281,-1.094896616647547,-1.475398418424488,-0.4593665709765281,-1.0355790108140486,-1.5164103944998348,-0.4593665709765281,-0.9745377859174778,-1.5548984551268124,-0.4593665709765281,-0.9118745390525697,-1.590798540726863,-0.4593665709765281,-0.8476935670095453,-1.624050899150455,-0.4593665709765281,-0.7821016926824047,-1.6546001851286392,-0.4593665709765281,-0.7152080872727578,-1.6823955523897889,-0.4593665709765281,-0.6471240885851347,-1.7073907382881992,-0.4593665709765281,-0.5779630157161982,-1.729544140803696,-0.4593665709765281,-0.5078399804462906,-1.748818887784094,-0.4593665709765281,-0.43687169564723277,-1.765182898315252,-0.4593665709765281,-0.36517628102527483,-1.7786089361165922,-0.4593665709765281,-0.292873066522492,-1.7890746548731984,-0.4593665709765281,-0.22008239370388114,-1.7965626354290531,-0.4593665709765281,-0.14692541546069798,-1.8010604147795042,-0.4593665709765281,-0.07352389436342716,-1.8025605068147044,-0.4593665709765281,-2.2074999549400584e-16,-1.8010604147795042,-0.4593665709765281,0.07352389436342673,-1.7965626354290531,-0.4593665709765281,0.14692541546069754,-1.7890746548731984,-0.4593665709765281,0.22008239370388072,-1.7786089361165924,-0.4593665709765281,0.29287306652249084,-1.7651828983152524,-0.4593665709765281,0.3651762810252736,-1.748818887784094,-0.4593665709765281,0.43687169564723316,-1.7295441408036962,-0.4593665709765281,0.5078399804462903,-1.7073907382881992,-0.4593665709765281,0.5779630157161978,-1.6823955523897889,-0.4593665709765281,0.6471240885851343,-1.6546001851286394,-0.4593665709765281,0.7152080872727573,-1.6240508991504554,-0.4593665709765281,0.7821016926824036,-1.5907985407268632,-0.4593665709765281,0.847693567009545,-1.5548984551268124,-0.4593665709765281,0.9118745390525692,-1.5164103944998346,-0.4593665709765281,0.974537785917478,-1.4753984184244886,-0.4593665709765281,1.0355790108140477,-1.431930787287514,-0.4593665709765281,1.0948966166475465,-1.3860798486711534,-0.4593665709765281,1.1523918751170699,-1.3379219169377443,-0.4593665709765281,1.2079690910390528,-1.2875371462119907,-0.4593665709765281,1.261535761622463,-1.2350093969723333,-0.4593665709765281,1.3130027304305647,-1.1804260964734632,-0.4593665709765281,1.3622843357730081,-1.1238780932322785,-0.4593665709765281,1.4092985532812632,-1.0654595058195,-0.4593665709765281,1.4539671324300802,-1.0052675662085964,-0.4593665709765281,1.4962157267777696,-0.9434024579427556,-0.4593665709765281,1.5359740177085195,-0.8799671493892695,-0.4593665709765281,1.5731758314707884,-0.8150672223588488,-0.4593665709765281,1.607759249316985,-0.7488106963751173,-0.4593665709765281,1.6396667105611116,-0.6813078488867724,-0.4593665709765281,1.6688451083828488,-0.6126710317216573,-0.4593665709765281,1.695245878218615,-0.5430144840882358,-0.4593665709765281,1.7188250785924877,-0.4724541424356961,-0.4593665709765281,1.7395434642524559,-0.40110744748917576,-0.4593665709765281,1.7573665514902697,-0.329093148781269,-0.4593665709765281,1.7722646755361664,-0.2565311070051457,-0.4593665709765281,1.7842130399329554,-0.18354209451825332,-0.4593665709765281,1.7931917578072727,-0.11024759432864988,-0.4593665709765281,1.7991858849693185,-0.0367695978985295,-0.4593665709765281,1.8021854447859866,0.03676959789853044,-0.4593665709765281,1.8021854447859866,0.11024759432864921,-0.4593665709765281,1.7991858849693185,0.18354209451825265,-0.4593665709765281,1.7931917578072727,0.25653110700514503,-0.4593665709765281,1.7842130399329557,0.3290931487812684,-0.4593665709765281,1.7722646755361666,0.40110744748917515,-0.4593665709765281,1.7573665514902697,0.4724541424356955,-0.4593665709765281,1.7395434642524559,0.5430144840882353,-0.4593665709765281,1.7188250785924877,0.6126710317216567,-0.4593665709765281,1.6952458782186153,0.6813078488867703,-0.4593665709765281,1.6688451083828497,0.7488106963751181,-0.4593665709765281,1.6396667105611111,0.8150672223588495,-0.4593665709765281,1.6077592493169843,0.8799671493892689,-0.4593665709765281,1.5731758314707889,0.9434024579427538,-0.4593665709765281,1.5359740177085206,1.0052675662085957,-0.4593665709765281,1.49621572677777,1.0654595058195009,-0.4593665709765281,1.4539671324300796,1.123878093232278,-0.4593665709765281,1.4092985532812636,1.1804260964734627,-0.4593665709765281,1.3622843357730088,1.2350093969723326,-0.4593665709765281,1.3130027304305651,1.2875371462119896,-0.4593665709765281,1.2615357616224638,1.3379219169377443,-0.4593665709765281,1.2079690910390528,1.386079848671154,-0.4593665709765281,1.152391875117069,1.4319307872875136,-0.4593665709765281,1.0948966166475471,1.475398418424488,-0.4593665709765281,1.0355790108140488,1.5164103944998337,-0.4593665709765281,0.9745377859174792,1.5548984551268124,-0.4593665709765281,0.9118745390525698,1.5907985407268634,-0.4593665709765281,0.8476935670095449,1.624050899150455,-0.4593665709765281,0.7821016926824049,1.6546001851286387,-0.4593665709765281,0.7152080872727588,1.6823955523897889,-0.4593665709765281,0.647124088585135,1.7073907382881988,-0.4593665709765281,0.5779630157161992,1.7295441408036962,-0.4593665709765281,0.5078399804462902,1.7488188877840938,-0.4593665709765281,0.43687169564723377,1.7651828983152522,-0.4593665709765281,0.3651762810252743,1.7786089361165922,-0.4593665709765281,0.29287306652249223,1.7890746548731984,-0.4593665709765281,0.22008239370388216,1.7965626354290531,-0.4593665709765281,0.14692541546069818,1.8010604147795042,-0.4593665709765281,0.07352389436342659,1.8025605068147044,-0.4593665709765281,0.0,1.8354555633619947,-0.4721494767247778,0.0,1.8339280960391757,-0.4721494767247778,-0.07486563720840675,1.8293482363893334,-0.4721494767247778,-0.14960666795210736,1.8217236071368725,-0.4721494767247778,-0.22409869316155895,1.8110668987247154,-0.4721494767247778,-0.29821772821235626,1.7973958481923122,-0.4721494767247778,-0.37184040928540285,1.7807332096540898,-0.4721494767247778,-0.44484419869381364,1.7611067164274727,-0.4721494767247778,-0.5171075888348015,1.738549034873515,-0.4721494767247778,-0.5885103044270904,1.7130977100269642,-0.4721494767247778,-0.6589335026972513,1.6847951031062574,-0.4721494767247778,-0.7282599711817703,1.6536883210074533,-0.4721494767247778,-0.7963743228156263,1.6198291378994532,-0.4721494767247778,-0.8631631879826733,1.5832739090510055,-0.4721494767247778,-0.928515403208178,1.544083477032924,-0.4721494767247778,-0.9923221961794532,1.5023230704516313,-0.4721494767247778,-1.0544773667866372,1.4580621953825823,-0.4721494767247778,-1.114877463882297,1.4113745196842604,-0.4721494767247778,-1.173421957465651,1.3623377503852983,-0.4721494767247778,-1.2300134060048378,1.3110335043487957,-0.4721494767247778,-1.2845576186187262,1.2575471724291076,-0.4721494767247778,-1.3369638118483438,1.2019677773471924,-0.4721494767247778,-1.387144760756983,1.1443878255210802,-0.4721494767247778,-1.435016944107501,1.0849031530980682,-0.4721494767247778,-1.4805006833751775,1.023612766444911,-0.4721494767247778,-1.523520275364753,0.9606186773614962,-0.4721494767247778,-1.5640041182109297,0.8960257332922691,-0.4721494767247778,-1.6018848305526052,0.8299414428180097,-0.4721494767247778,-1.6370993636825033,0.7624757967184078,-0.4721494767247778,-1.6695891064855228,0.6937410849032685,-0.4721494767247778,-1.699299982991153,0.623851709517037,-0.4721494767247778,-1.726182542377592,0.5529239945277187,-0.4721494767247778,-1.750192041277759,0.4810759921171168,-0.4721494767247778,-1.7712885182502112,0.4084272861946244,-0.4721494767247778,-1.7894368602910224,0.335098793361609,-0.4721494767247778,-1.8046068612759099,0.2612125616576641,-0.4721494767247778,-1.8167732722353491,0.18689156742368934,-0.4721494767247778,-1.8259158433789915,0.11225951061991077,-0.4721494767247778,-1.8320193577994406,0.037440608939502774,-0.4721494767247778,-1.835073656799295,-0.03744060893950255,-0.4721494767247778,-1.835073656799295,-0.11225951061991053,-0.4721494767247778,-1.8320193577994408,-0.18689156742368873,-0.4721494767247778,-1.8259158433789915,-0.2612125616576639,-0.4721494767247778,-1.8167732722353491,-0.33509879336160886,-0.4721494767247778,-1.8046068612759099,-0.4084272861946237,-0.4721494767247778,-1.7894368602910224,-0.4810759921171166,-0.4721494767247778,-1.7712885182502114,-0.5529239945277181,-0.4721494767247778,-1.7501920412777592,-0.6238517095170367,-0.4721494767247778,-1.7261825423775923,-0.6937410849032682,-0.4721494767247778,-1.6992999829911533,-0.7624757967184073,-0.4721494767247778,-1.669589106485523,-0.8299414428180097,-0.4721494767247778,-1.6370993636825033,-0.896025733292269,-0.4721494767247778,-1.6018848305526052,-0.9606186773614965,-0.4721494767247778,-1.5640041182109292,-1.0236127664449106,-0.4721494767247778,-1.5235202753647534,-1.0849031530980677,-0.4721494767247778,-1.4805006833751777,-1.1443878255210802,-0.4721494767247778,-1.4350169441075014,-1.2019677773471926,-0.4721494767247778,-1.387144760756983,-1.2575471724291079,-0.4721494767247778,-1.3369638118483438,-1.3110335043487957,-0.4721494767247778,-1.2845576186187262,-1.3623377503852983,-0.4721494767247778,-1.2300134060048378,-1.4113745196842602,-0.4721494767247778,-1.1734219574656517,-1.4580621953825823,-0.4721494767247778,-1.1148774638822967,-1.5023230704516308,-0.4721494767247778,-1.0544773667866378,-1.5440834770329241,-0.4721494767247778,-0.9923221961794529,-1.5832739090510053,-0.4721494767247778,-0.9285154032081783,-1.619829137899453,-0.4721494767247778,-0.8631631879826734,-1.653688321007453,-0.4721494767247778,-0.7963743228156266,-1.6847951031062571,-0.4721494767247778,-0.7282599711817704,-1.7130977100269642,-0.4721494767247778,-0.6589335026972514,-1.738549034873515,-0.4721494767247778,-0.5885103044270904,-1.7611067164274725,-0.4721494767247778,-0.5171075888348022,-1.7807332096540898,-0.4721494767247778,-0.44484419869381353,-1.797395848192312,-0.4721494767247778,-0.3718404092854034,-1.8110668987247154,-0.4721494767247778,-0.29821772821235676,-1.8217236071368725,-0.4721494767247778,-0.22409869316155942,-1.8293482363893334,-0.4721494767247778,-0.14960666795210775,-1.8339280960391757,-0.4721494767247778,-0.07486563720840704,-1.8354555633619947,-0.4721494767247778,-2.247784780648469e-16,-1.8339280960391757,-0.4721494767247778,0.0748656372084066,-1.8293482363893334,-0.4721494767247778,0.1496066679521073,-1.8217236071368725,-0.4721494767247778,0.22409869316155898,-1.8110668987247154,-0.4721494767247778,0.29821772821235554,-1.7973958481923125,-0.4721494767247778,0.3718404092854022,-1.7807332096540898,-0.4721494767247778,0.44484419869381386,-1.7611067164274727,-0.4721494767247778,0.5171075888348018,-1.738549034873515,-0.4721494767247778,0.58851030442709,-1.7130977100269644,-0.4721494767247778,0.6589335026972509,-1.6847951031062574,-0.4721494767247778,0.7282599711817701,-1.6536883210074538,-0.4721494767247778,0.7963743228156255,-1.6198291378994532,-0.4721494767247778,0.8631631879826731,-1.5832739090510055,-0.4721494767247778,0.9285154032081778,-1.544083477032924,-0.4721494767247778,0.9923221961794532,-1.5023230704516315,-0.4721494767247778,1.0544773667866367,-1.4580621953825827,-0.4721494767247778,1.1148774638822962,-1.4113745196842602,-0.4721494767247778,1.1734219574656513,-1.362337750385298,-0.4721494767247778,1.230013406004838,-1.3110335043487962,-0.4721494767247778,1.284557618618726,-1.257547172429108,-0.4721494767247778,1.3369638118483436,-1.2019677773471933,-0.4721494767247778,1.387144760756982,-1.1443878255210802,-0.4721494767247778,1.435016944107501,-1.0849031530980673,-0.4721494767247778,1.480500683375178,-1.023612766444911,-0.4721494767247778,1.523520275364753,-0.960618677361497,-0.4721494767247778,1.564004118210929,-0.8960257332922693,-0.4721494767247778,1.601884830552605,-0.8299414428180087,-0.4721494767247778,1.637099363682504,-0.7624757967184077,-0.4721494767247778,1.669589106485523,-0.6937410849032691,-0.4721494767247778,1.6992999829911528,-0.6238517095170365,-0.4721494767247778,1.7261825423775923,-0.5529239945277189,-0.4721494767247778,1.750192041277759,-0.4810759921171178,-0.4721494767247778,1.771288518250211,-0.4084272861946246,-0.4721494767247778,1.7894368602910224,-0.33509879336160847,-0.4721494767247778,1.8046068612759099,-0.2612125616576639,-0.4721494767247778,1.8167732722353491,-0.18689156742368998,-0.4721494767247778,1.8259158433789913,-0.11225951061991057,-0.4721494767247778,1.8320193577994408,-0.03744060893950178,-0.4721494767247778,1.835073656799295,0.03744060893950273,-0.4721494767247778,1.835073656799295,0.11225951061990991,-0.4721494767247778,1.8320193577994408,0.18689156742368931,-0.4721494767247778,1.8259158433789915,0.2612125616576632,-0.4721494767247778,1.8167732722353493,0.3350987933616078,-0.4721494767247778,1.80460686127591,0.40842728619462393,-0.4721494767247778,1.7894368602910224,0.4810759921171172,-0.4721494767247778,1.7712885182502112,0.5529239945277183,-0.4721494767247778,1.7501920412777592,0.6238517095170358,-0.4721494767247778,1.7261825423775925,0.6937410849032669,-0.4721494767247778,1.6992999829911537,0.7624757967184085,-0.4721494767247778,1.6695891064855226,0.8299414428180096,-0.4721494767247778,1.6370993636825033,0.8960257332922688,-0.4721494767247778,1.6018848305526054,0.9606186773614952,-0.4721494767247778,1.56400411821093,1.0236127664449104,-0.4721494767247778,1.5235202753647534,1.0849031530980682,-0.4721494767247778,1.4805006833751773,1.14438782552108,-0.4721494767247778,1.4350169441075014,1.201967777347193,-0.4721494767247778,1.3871447607569827,1.2575471724291074,-0.4721494767247778,1.336963811848344,1.3110335043487953,-0.4721494767247778,1.284557618618727,1.362337750385298,-0.4721494767247778,1.230013406004838,1.4113745196842609,-0.4721494767247778,1.1734219574656504,1.458062195382582,-0.4721494767247778,1.114877463882297,1.5023230704516306,-0.4721494767247778,1.054477366786638,1.5440834770329233,-0.4721494767247778,0.9923221961794544,1.5832739090510053,-0.4721494767247778,0.9285154032081785,1.6198291378994534,-0.4721494767247778,0.863163187982673,1.653688321007453,-0.4721494767247778,0.7963743228156268,1.6847951031062567,-0.4721494767247778,0.7282599711817714,1.7130977100269642,-0.4721494767247778,0.6589335026972516,1.7385490348735146,-0.4721494767247778,0.5885103044270914,1.7611067164274727,-0.4721494767247778,0.5171075888348017,1.7807332096540895,-0.4721494767247778,0.44484419869381453,1.7973958481923122,-0.4721494767247778,0.37184040928540285,1.8110668987247154,-0.4721494767247778,0.2982177282123569,1.8217236071368725,-0.4721494767247778,0.22409869316156047,1.8293482363893334,-0.4721494767247778,0.14960666795210797,1.8339280960391757,-0.4721494767247778,0.07486563720840646,1.8354555633619947,-0.4721494767247778,0.0,1.86917037201578,-0.48258015752982486,0.0,1.8676148472071021,-0.48258015752982486,-0.07624081658273156,1.8629508617985993,-0.48258015752982486,-0.15235473785040374,1.8551861785336992,-0.48258015752982486,-0.2282150796926859,1.8443337209614057,-0.48258015752982486,-0.3036955800571754,1.8304115519263287,-0.48258015752982486,-0.37867060910012323,1.813442843504859,-0.48258015752982486,-0.45301537828491034,1.793455838437537,-0.48258015752982486,-0.526606148080252,1.7704838031217962,-0.48258015752982486,-0.5993204339124355,1.7445649722433287,-0.48258015752982486,-0.6710372100288053,1.715742485138224,-0.48258015752982486,-0.7416371109331849,1.684064313991801,-0.48258015752982486,-0.8110026300579636,1.6495831839936408,-0.48258015752982486,-0.8790183153421841,1.6123564855817116,-0.48258015752982486,-0.9455709613901018,1.5724461789216482,-0.48258015752982486,-1.0105497978903946,1.5299186907801712,-0.48258015752982486,-1.0738466739824155,1.4848448039642899,-0.48258015752982486,-1.1353562382626254,1.4372995395103048,-0.48258015752982486,-1.1949761141316069,1.3873620318186974,-0.48258015752982486,-1.252607070189812,1.3351153969427294,-0.48258015752982486,-1.3081531853984332,1.2806465942499747,-0.48258015752982486,-1.361522008730505,1.224046281687035,-0.48258015752982486,-1.4126247130465164,1.165408664888334,-0.48258015752982486,-1.4613762429384158,1.104831340380135,-0.48258015752982486,-1.507695456295947,1.0424151331407565,-0.48258015752982486,-1.5515052593596799,0.9782639287873448,-0.48258015752982486,-1.592732735035969,0.9124845006685177,-0.48258015752982486,-1.6313092642602551,0.8451863321506626,-0.48258015752982486,-1.6671706402067303,0.7764814343936761,-0.48258015752982486,-1.7002571751542612,0.7064841599194465,-0.48258015752982486,-1.73051379983071,0.6353110122833636,-0.48258015752982486,-1.7578901550703039,0.5630804521656485,-0.48258015752982486,-1.7823406756314961,0.48991270020524375,-0.48258015752982486,-1.803824666035814,0.4159295369044231,-0.48258015752982486,-1.8223063683014669,0.341254099937164,-0.48258015752982486,-1.837755021458977,0.2660106791986423,-0.48258015752982486,-1.8501449127497787,0.1903245099369661,-0.48258015752982486,-1.8594554204225664,0.11432156431146692,-0.48258015752982486,-1.8656710480561647,0.03812834172447109,-0.48258015752982486,-1.868781450351793,-0.03812834172447087,-0.48258015752982486,-1.868781450351793,-0.11432156431146669,-0.48258015752982486,-1.865671048056165,-0.19032450993696545,-0.48258015752982486,-1.8594554204225664,-0.26601067919864213,-0.48258015752982486,-1.8501449127497787,-0.34125409993716377,-0.48258015752982486,-1.837755021458977,-0.41592953690442247,-0.48258015752982486,-1.8223063683014669,-0.4899127002052435,-0.48258015752982486,-1.8038246660358142,-0.563080452165648,-0.48258015752982486,-1.7823406756314963,-0.6353110122833634,-0.48258015752982486,-1.757890155070304,-0.7064841599194462,-0.48258015752982486,-1.7305137998307103,-0.7764814343936756,-0.48258015752982486,-1.7002571751542614,-0.8451863321506627,-0.48258015752982486,-1.6671706402067303,-0.9124845006685176,-0.48258015752982486,-1.6313092642602551,-0.9782639287873452,-0.48258015752982486,-1.5927327350359686,-1.042415133140756,-0.48258015752982486,-1.5515052593596803,-1.1048313403801346,-0.48258015752982486,-1.507695456295947,-1.1654086648883337,-0.48258015752982486,-1.4613762429384163,-1.2240462816870352,-0.48258015752982486,-1.4126247130465164,-1.280646594249975,-0.48258015752982486,-1.361522008730505,-1.3351153969427294,-0.48258015752982486,-1.3081531853984332,-1.3873620318186974,-0.48258015752982486,-1.252607070189812,-1.4372995395103043,-0.48258015752982486,-1.1949761141316075,-1.4848448039642899,-0.48258015752982486,-1.1353562382626252,-1.5299186907801707,-0.48258015752982486,-1.0738466739824162,-1.5724461789216484,-0.48258015752982486,-1.0105497978903946,-1.6123564855817114,-0.48258015752982486,-0.945570961390102,-1.6495831839936406,-0.48258015752982486,-0.8790183153421843,-1.6840643139918008,-0.48258015752982486,-0.8110026300579639,-1.7157424851382237,-0.48258015752982486,-0.741637110933185,-1.7445649722433287,-0.48258015752982486,-0.6710372100288055,-1.7704838031217962,-0.48258015752982486,-0.5993204339124355,-1.7934558384375368,-0.48258015752982486,-0.5266061480802527,-1.813442843504859,-0.48258015752982486,-0.4530153782849102,-1.8304115519263284,-0.48258015752982486,-0.3786706091001238,-1.8443337209614057,-0.48258015752982486,-0.30369558005717595,-1.8551861785336992,-0.48258015752982486,-0.22821507969268637,-1.8629508617985993,-0.48258015752982486,-0.15235473785040413,-1.8676148472071021,-0.48258015752982486,-0.07624081658273185,-1.86917037201578,-0.48258015752982486,-2.2890735131501924e-16,-1.8676148472071021,-0.48258015752982486,0.07624081658273141,-1.8629508617985993,-0.48258015752982486,0.15235473785040365,-1.8551861785336992,-0.48258015752982486,0.22821507969268592,-1.844333720961406,-0.48258015752982486,0.3036955800571747,-1.8304115519263289,-0.48258015752982486,0.37867060910012257,-1.813442843504859,-0.48258015752982486,0.45301537828491056,-1.793455838437537,-0.48258015752982486,0.5266061480802523,-1.7704838031217962,-0.48258015752982486,0.5993204339124351,-1.744564972243329,-0.48258015752982486,0.671037210028805,-1.715742485138224,-0.48258015752982486,0.7416371109331845,-1.6840643139918012,-0.48258015752982486,0.8110026300579628,-1.6495831839936408,-0.48258015752982486,0.8790183153421839,-1.6123564855817116,-0.48258015752982486,0.9455709613901015,-1.5724461789216482,-0.48258015752982486,1.0105497978903946,-1.5299186907801714,-0.48258015752982486,1.073846673982415,-1.4848448039642903,-0.48258015752982486,1.1353562382626248,-1.4372995395103045,-0.48258015752982486,1.194976114131607,-1.3873620318186972,-0.48258015752982486,1.2526070701898122,-1.3351153969427296,-0.48258015752982486,1.308153185398433,-1.2806465942499752,-0.48258015752982486,1.3615220087305049,-1.224046281687036,-0.48258015752982486,1.4126247130465155,-1.165408664888334,-0.48258015752982486,1.4613762429384158,-1.1048313403801342,-0.48258015752982486,1.5076954562959473,-1.0424151331407565,-0.48258015752982486,1.5515052593596799,-0.9782639287873457,-0.48258015752982486,1.5927327350359683,-0.9124845006685179,-0.48258015752982486,1.631309264260255,-0.8451863321506616,-0.48258015752982486,1.667170640206731,-0.776481434393676,-0.48258015752982486,1.7002571751542614,-0.7064841599194471,-0.48258015752982486,1.7305137998307099,-0.6353110122833631,-0.48258015752982486,1.757890155070304,-0.5630804521656487,-0.48258015752982486,1.7823406756314961,-0.4899127002052448,-0.48258015752982486,1.8038246660358137,-0.41592953690442336,-0.48258015752982486,1.8223063683014669,-0.3412540999371634,-0.48258015752982486,1.837755021458977,-0.26601067919864213,-0.48258015752982486,1.8501449127497787,-0.19032450993696673,-0.48258015752982486,1.8594554204225662,-0.11432156431146673,-0.48258015752982486,1.865671048056165,-0.03812834172447008,-0.48258015752982486,1.868781450351793,0.038128341724471056,-0.48258015752982486,1.868781450351793,0.11432156431146603,-0.48258015752982486,1.865671048056165,0.19032450993696606,-0.48258015752982486,1.8594554204225664,0.2660106791986414,-0.48258015752982486,1.8501449127497789,0.3412540999371627,-0.48258015752982486,1.8377550214589773,0.4159295369044227,-0.48258015752982486,1.8223063683014669,0.48991270020524413,-0.48258015752982486,1.803824666035814,0.5630804521656481,-0.48258015752982486,1.7823406756314963,0.6353110122833625,-0.48258015752982486,1.7578901550703043,0.7064841599194449,-0.48258015752982486,1.7305137998307107,0.7764814343936769,-0.48258015752982486,1.700257175154261,0.8451863321506625,-0.48258015752982486,1.6671706402067303,0.9124845006685174,-0.48258015752982486,1.6313092642602554,0.9782639287873438,-0.48258015752982486,1.5927327350359697,1.0424151331407558,-0.48258015752982486,1.5515052593596803,1.104831340380135,-0.48258015752982486,1.5076954562959468,1.1654086648883335,-0.48258015752982486,1.4613762429384163,1.2240462816870357,-0.48258015752982486,1.4126247130465162,1.2806465942499745,-0.48258015752982486,1.3615220087305053,1.3351153969427287,-0.48258015752982486,1.308153185398434,1.3873620318186972,-0.48258015752982486,1.2526070701898122,1.4372995395103052,-0.48258015752982486,1.1949761141316062,1.4848448039642896,-0.48258015752982486,1.1353562382626254,1.5299186907801705,-0.48258015752982486,1.0738466739824164,1.5724461789216473,-0.48258015752982486,1.010549797890396,1.6123564855817114,-0.48258015752982486,0.9455709613901021,1.649583183993641,-0.48258015752982486,0.8790183153421838,1.6840643139918008,-0.48258015752982486,0.8110026300579641,1.7157424851382233,-0.48258015752982486,0.741637110933186,1.7445649722433287,-0.48258015752982486,0.6710372100288058,1.770483803121796,-0.48258015752982486,0.5993204339124365,1.793455838437537,-0.48258015752982486,0.5266061480802522,1.8134428435048588,-0.48258015752982486,0.45301537828491123,1.8304115519263287,-0.48258015752982486,0.37867060910012323,1.8443337209614057,-0.48258015752982486,0.3036955800571762,1.8551861785336992,-0.48258015752982486,0.22821507969268742,1.8629508617985993,-0.48258015752982486,0.15235473785040435,1.8676148472071021,-0.48258015752982486,0.07624081658273126,1.86917037201578,-0.48258015752982486,0.0,1.9035369672952516,-0.4906066482645772,0.0,1.9019528425834578,-0.4906066482645772,-0.07764258141193196,1.897203105067323,-0.4906066482645772,-0.15515593440958536,1.8892956602161954,-0.4906066482645772,-0.2324110456666237,1.8782436691916506,-0.4906066482645772,-0.3092793316746022,1.8640655269420372,-0.4906066482645772,-0.38563285275752757,1.8467848315859003,-0.4906066482645772,-0.46134452601482245,1.826430345135243,-0.4906066482645772,-0.5362883368382722,1.8030359456239955,-0.4906066482645772,-0.6103395486509031,1.77664057072137,-0.4906066482645772,-0.6833749105187054,1.7472881529239543,-0.4906066482645772,-0.7552728622896493,1.7150275464344071,-0.4906066482645772,-0.8259137369185606,1.6799124458484576,-0.4906066482645772,-0.8951799596411089,1.6420012967855508,-0.4906066482645772,-0.9629562436653978,1.601357198611883,-0.4906066482645772,-1.029129782055454,1.5580477994177317,-0.4906066482645772,-1.0935904354872383,1.512145183423887,-0.4906066482645772,-1.1562309155646824,1.4637257510045798,-0.4906066482645772,-1.216946963390633,1.4128700915265988,-0.4906066482645772,-1.2756375230955008,1.3596628492162424,-0.4906066482645772,-1.332204910034779,1.3041925822773577,-0.4906066482645772,-1.3865549733754956,1.2465516154949503,-0.4906066482645772,-1.438597252800982,1.186835886569691,-0.4906066482645772,-1.488245129073144,1.1251447864390798,-0.4906066482645772,-1.5354159682016342,1.061580993851041,-0.4906066482645772,-1.5800312589799734,0.9962503044652794,-0.4906066482645772,-1.6220167436597077,0.929261454766847,-0.4906066482645772,-1.6613025415450995,0.8607259410849935,-0.4906066482645772,-1.697823265302651,0.7907578340185284,-0.4906066482645772,-1.7315181297918683,0.7194735885765706,-0.4906066482645772,-1.7623310532361265,0.6469918503506756,-0.4906066482645772,-1.7902107505652547,0.5734332580409577,-0.4906066482645772,-1.8151108187744724,0.4989202426648803,-0.4906066482645772,-1.8369898141576126,0.4235768237829098,-0.4906066482645772,-1.8558113212860798,0.34752840308019595,-0.4906066482645772,-1.8715440136187373,0.2709015556478438,-0.4906066482645772,-1.8841617056418438,0.19382381931116383,-0.4906066482645772,-1.8936433964522563,0.1164234823555515,-0.4906066482645772,-1.899973304711361,0.03882937000329469,-0.4906066482645772,-1.903140894911554,-0.03882937000329446,-0.4906066482645772,-1.903140894911554,-0.11642348235555126,-0.4906066482645772,-1.8999733047113612,-0.1938238193111632,-0.4906066482645772,-1.8936433964522563,-0.2709015556478436,-0.4906066482645772,-1.8841617056418438,-0.3475284030801957,-0.4906066482645772,-1.8715440136187373,-0.4235768237829091,-0.4906066482645772,-1.8558113212860798,-0.49892024266488005,-0.4906066482645772,-1.8369898141576129,-0.573433258040957,-0.4906066482645772,-1.8151108187744727,-0.6469918503506754,-0.4906066482645772,-1.790210750565255,-0.7194735885765704,-0.4906066482645772,-1.7623310532361267,-0.7907578340185278,-0.4906066482645772,-1.7315181297918685,-0.8607259410849936,-0.4906066482645772,-1.697823265302651,-0.9292614547668469,-0.4906066482645772,-1.6613025415450995,-0.9962503044652798,-0.4906066482645772,-1.6220167436597073,-1.0615809938510405,-0.4906066482645772,-1.5800312589799737,-1.1251447864390793,-0.4906066482645772,-1.5354159682016342,-1.1868358865696909,-0.4906066482645772,-1.4882451290731442,-1.2465516154949505,-0.4906066482645772,-1.438597252800982,-1.304192582277358,-0.4906066482645772,-1.3865549733754956,-1.3596628492162424,-0.4906066482645772,-1.332204910034779,-1.4128700915265988,-0.4906066482645772,-1.2756375230955008,-1.4637257510045794,-0.4906066482645772,-1.2169469633906336,-1.512145183423887,-0.4906066482645772,-1.1562309155646822,-1.5580477994177313,-0.4906066482645772,-1.093590435487239,-1.6013571986118833,-0.4906066482645772,-1.0291297820554537,-1.6420012967855506,-0.4906066482645772,-0.962956243665398,-1.6799124458484573,-0.4906066482645772,-0.8951799596411091,-1.715027546434407,-0.4906066482645772,-0.825913736918561,-1.7472881529239541,-0.4906066482645772,-0.7552728622896494,-1.77664057072137,-0.4906066482645772,-0.6833749105187055,-1.8030359456239955,-0.4906066482645772,-0.6103395486509031,-1.826430345135243,-0.4906066482645772,-0.5362883368382728,-1.8467848315859003,-0.4906066482645772,-0.4613445260148223,-1.864065526942037,-0.4906066482645772,-0.3856328527575281,-1.8782436691916506,-0.4906066482645772,-0.3092793316746027,-1.8892956602161954,-0.4906066482645772,-0.23241104566662418,-1.897203105067323,-0.4906066482645772,-0.15515593440958575,-1.9019528425834578,-0.4906066482645772,-0.07764258141193227,-1.9035369672952516,-0.4906066482645772,-2.3311604540567897e-16,-1.9019528425834578,-0.4906066482645772,0.07764258141193181,-1.897203105067323,-0.4906066482645772,0.15515593440958528,-1.8892956602161954,-0.4906066482645772,0.23241104566662374,-1.8782436691916509,-0.4906066482645772,0.3092793316746015,-1.8640655269420374,-0.4906066482645772,0.38563285275752685,-1.8467848315859003,-0.4906066482645772,0.4613445260148227,-1.826430345135243,-0.4906066482645772,0.5362883368382725,-1.8030359456239955,-0.4906066482645772,0.6103395486509027,-1.7766405707213702,-0.4906066482645772,0.683374910518705,-1.7472881529239543,-0.4906066482645772,0.755272862289649,-1.7150275464344076,-0.4906066482645772,0.8259137369185597,-1.6799124458484576,-0.4906066482645772,0.8951799596411086,-1.6420012967855508,-0.4906066482645772,0.9629562436653976,-1.601357198611883,-0.4906066482645772,1.029129782055454,-1.558047799417732,-0.4906066482645772,1.0935904354872379,-1.5121451834238873,-0.4906066482645772,1.1562309155646817,-1.4637257510045796,-0.4906066482645772,1.2169469633906331,-1.4128700915265986,-0.4906066482645772,1.275637523095501,-1.3596628492162428,-0.4906066482645772,1.3322049100347788,-1.3041925822773581,-0.4906066482645772,1.3865549733754954,-1.2465516154949514,-0.4906066482645772,1.4385972528009812,-1.186835886569691,-0.4906066482645772,1.488245129073144,-1.1251447864390791,-0.4906066482645772,1.5354159682016344,-1.061580993851041,-0.4906066482645772,1.5800312589799734,-0.9962503044652803,-0.4906066482645772,1.622016743659707,-0.9292614547668472,-0.4906066482645772,1.6613025415450993,-0.8607259410849925,-0.4906066482645772,1.6978232653026517,-0.7907578340185283,-0.4906066482645772,1.7315181297918685,-0.7194735885765712,-0.4906066482645772,1.7623310532361263,-0.6469918503506752,-0.4906066482645772,1.790210750565255,-0.5734332580409579,-0.4906066482645772,1.8151108187744724,-0.49892024266488133,-0.4906066482645772,1.8369898141576124,-0.42357682378291006,-0.4906066482645772,1.8558113212860798,-0.3475284030801954,-0.4906066482645772,1.8715440136187373,-0.2709015556478436,-0.4906066482645772,1.8841617056418438,-0.1938238193111645,-0.4906066482645772,1.893643396452256,-0.1164234823555513,-0.4906066482645772,1.8999733047113612,-0.03882937000329366,-0.4906066482645772,1.903140894911554,0.03882937000329465,-0.4906066482645772,1.903140894911554,0.11642348235555061,-0.4906066482645772,1.8999733047113612,0.1938238193111638,-0.4906066482645772,1.8936433964522563,0.27090155564784285,-0.4906066482645772,1.8841617056418438,0.34752840308019467,-0.4906066482645772,1.8715440136187376,0.42357682378290934,-0.4906066482645772,1.8558113212860798,0.49892024266488066,-0.4906066482645772,1.8369898141576126,0.5734332580409572,-0.4906066482645772,1.8151108187744727,0.6469918503506745,-0.4906066482645772,1.7902107505652551,0.719473588576569,-0.4906066482645772,1.7623310532361272,0.7907578340185292,-0.4906066482645772,1.731518129791868,0.8607259410849933,-0.4906066482645772,1.697823265302651,0.9292614547668466,-0.4906066482645772,1.6613025415450997,0.9962503044652784,-0.4906066482645772,1.6220167436597084,1.0615809938510403,-0.4906066482645772,1.5800312589799737,1.1251447864390798,-0.4906066482645772,1.535415968201634,1.1868358865696906,-0.4906066482645772,1.4882451290731442,1.246551615494951,-0.4906066482645772,1.4385972528009818,1.3041925822773575,-0.4906066482645772,1.3865549733754958,1.3596628492162417,-0.4906066482645772,1.3322049100347797,1.4128700915265986,-0.4906066482645772,1.275637523095501,1.46372575100458,-0.4906066482645772,1.2169469633906325,1.5121451834238868,-0.4906066482645772,1.1562309155646824,1.558047799417731,-0.4906066482645772,1.0935904354872392,1.6013571986118822,-0.4906066482645772,1.0291297820554552,1.6420012967855506,-0.4906066482645772,0.9629562436653982,1.6799124458484578,-0.4906066482645772,0.8951799596411085,1.715027546434407,-0.4906066482645772,0.8259137369185611,1.747288152923954,-0.4906066482645772,0.7552728622896504,1.77664057072137,-0.4906066482645772,0.6833749105187058,1.803035945623995,-0.4906066482645772,0.6103395486509041,1.826430345135243,-0.4906066482645772,0.5362883368382724,1.8467848315859001,-0.4906066482645772,0.46134452601482334,1.8640655269420372,-0.4906066482645772,0.38563285275752757,1.8782436691916506,-0.4906066482645772,0.3092793316746029,1.8892956602161954,-0.4906066482645772,0.23241104566662527,1.897203105067323,-0.4906066482645772,0.15515593440958597,1.9019528425834578,-0.4906066482645772,0.07764258141193166,1.9035369672952516,-0.4906066482645772,0.0,1.9383841365514205,-0.4961889613559395,0.0,1.9367710120025323,-0.4961889613559395,-0.07906394817413985,1.931934323242481,-0.4961889613559395,-0.15799630220929892,1.9238821204623795,-0.4961889613559395,-0.23666568699195387,1.91262780575909,-0.4961889613559395,-0.31494116509494996,1.8981901108287569,-0.4961889613559395,-0.39269245470992636,1.8805930657897478,-0.4961889613559395,-0.46979014648852874,1.8598659591868925,-0.4961889613559395,-0.5461059189314986,1.8360432892435905,-0.4961889613559395,-0.6215127519671446,1.8091647064429228,-0.4961889613559395,-0.6958851383637162,1.7792749475333367,-0.4961889613559395,-0.7690992926238054,1.7464237610687416,-0.4961889613559395,-0.8410333570130896,1.7106658246069515,-0.4961889613559395,-0.9115676043805061,1.6720606537042861,-0.4961889613559395,-0.9805846374322775,1.6306725028578013,-0.4961889613559395,-1.0479695841281227,1.5865702585600185,-0.4961889613559395,-1.1136102888744304,1.5398273246441576,-0.4961889613559395,-1.1773974991961775,1.4905215001107004,-0.4961889613559395,-1.2392250475768891,1.4387348496386325,-0.4961889613559395,-1.2989900281639952,1.3845535669968831,-0.4961889613559395,-1.3565929680454645,1.328067831583304,-0.4961889613559395,-1.4119379928126508,1.2693716583299608,-0.4961889613559395,-1.4649329861337825,1.2085627412245576,-0.4961889613559395,-1.5154897430725087,1.1457422907084347,-0.4961889613559395,-1.5635241168963097,1.0810148652217781,-0.4961889613559395,-1.608956159130427,1.014488197176415,-0.4961889613559395,-1.6517102526242136,0.9462730136458458,-0.4961889613559395,-1.6917152374084135,0.8764828520709579,-0.4961889613559395,-1.7289045291339125,0.8052338712881587,-0.4961889613559395,-1.7632162298948137,0.7326446581944596,-0.4961889613559395,-1.794593231251394,0.6588360303712861,-0.4961889613559395,-1.822983309281465,0.5839308349955376,-0.4961889613559395,-1.8483392115019392,0.5080537443725859,-0.4961889613559395,-1.870618735515923,0.43133104843152625,-0.4961889613559395,-1.8897847992544432,0.3538904445280536,-0.4961889613559395,-1.9058055026958898,0.2758608249048172,-0.4961889613559395,-1.9186541809604538,0.19737206216300102,-0.4961889613559395,-1.9283094486911898,0.11855479310219866,-0.4961889613559395,-1.9347552356478301,0.039540201288351284,-0.4961889613559395,-1.9379808134541148,-0.03954020128835105,-0.4961889613559395,-1.9379808134541148,-0.11855479310219841,-0.4961889613559395,-1.9347552356478304,-0.19737206216300038,-0.4961889613559395,-1.9283094486911898,-0.27586082490481706,-0.4961889613559395,-1.9186541809604538,-0.3538904445280534,-0.4961889613559395,-1.9058055026958898,-0.4313310484315256,-0.4961889613559395,-1.8897847992544432,-0.5080537443725857,-0.4961889613559395,-1.8706187355159232,-0.583930834995537,-0.4961889613559395,-1.8483392115019395,-0.6588360303712859,-0.4961889613559395,-1.8229833092814651,-0.7326446581944593,-0.4961889613559395,-1.7945932312513941,-0.8052338712881583,-0.4961889613559395,-1.763216229894814,-0.876482852070958,-0.4961889613559395,-1.7289045291339125,-0.9462730136458457,-0.4961889613559395,-1.6917152374084135,-1.0144881971764155,-0.4961889613559395,-1.6517102526242131,-1.0810148652217777,-0.4961889613559395,-1.6089561591304276,-1.1457422907084343,-0.4961889613559395,-1.5635241168963099,-1.2085627412245574,-0.4961889613559395,-1.5154897430725092,-1.269371658329961,-0.4961889613559395,-1.4649329861337825,-1.3280678315833043,-0.4961889613559395,-1.4119379928126508,-1.3845535669968831,-0.4961889613559395,-1.3565929680454645,-1.4387348496386325,-0.4961889613559395,-1.2989900281639952,-1.4905215001107,-0.4961889613559395,-1.2392250475768898,-1.5398273246441576,-0.4961889613559395,-1.1773974991961773,-1.586570258560018,-0.4961889613559395,-1.113610288874431,-1.6306725028578015,-0.4961889613559395,-1.0479695841281225,-1.672060653704286,-0.4961889613559395,-0.9805846374322778,-1.7106658246069513,-0.4961889613559395,-0.9115676043805062,-1.7464237610687414,-0.4961889613559395,-0.8410333570130899,-1.7792749475333365,-0.4961889613559395,-0.7690992926238055,-1.8091647064429228,-0.4961889613559395,-0.6958851383637163,-1.8360432892435905,-0.4961889613559395,-0.6215127519671446,-1.8598659591868922,-0.4961889613559395,-0.5461059189314994,-1.8805930657897478,-0.4961889613559395,-0.46979014648852857,-1.8981901108287567,-0.4961889613559395,-0.3926924547099269,-1.91262780575909,-0.4961889613559395,-0.3149411650949505,-1.9238821204623795,-0.4961889613559395,-0.23666568699195437,-1.931934323242481,-0.4961889613559395,-0.15799630220929933,-1.9367710120025323,-0.4961889613559395,-0.07906394817414017,-1.9383841365514205,-0.4961889613559395,-2.373835928345703e-16,-1.9367710120025323,-0.4961889613559395,0.0790639481741397,-1.931934323242481,-0.4961889613559395,0.15799630220929886,-1.9238821204623795,-0.4961889613559395,0.2366656869919539,-1.9126278057590902,-0.4961889613559395,0.3149411650949492,-1.898190110828757,-0.4961889613559395,0.39269245470992564,-1.8805930657897478,-0.4961889613559395,0.46979014648852896,-1.8598659591868925,-0.4961889613559395,0.546105918931499,-1.8360432892435905,-0.4961889613559395,0.6215127519671442,-1.809164706442923,-0.4961889613559395,0.6958851383637159,-1.7792749475333367,-0.4961889613559395,0.769099292623805,-1.746423761068742,-0.4961889613559395,0.8410333570130887,-1.7106658246069515,-0.4961889613559395,0.9115676043805059,-1.6720606537042861,-0.4961889613559395,0.9805846374322773,-1.6306725028578013,-0.4961889613559395,1.0479695841281227,-1.5865702585600188,-0.4961889613559395,1.11361028887443,-1.539827324644158,-0.4961889613559395,1.1773974991961769,-1.4905215001107002,-0.4961889613559395,1.2392250475768893,-1.4387348496386325,-0.4961889613559395,1.2989900281639954,-1.3845535669968836,-0.4961889613559395,1.3565929680454645,-1.3280678315833045,-0.4961889613559395,1.4119379928126505,-1.269371658329962,-0.4961889613559395,1.4649329861337816,-1.2085627412245576,-0.4961889613559395,1.5154897430725087,-1.1457422907084338,-0.4961889613559395,1.56352411689631,-1.0810148652217781,-0.4961889613559395,1.608956159130427,-1.014488197176416,-0.4961889613559395,1.651710252624213,-0.946273013645846,-0.4961889613559395,1.6917152374084132,-0.8764828520709569,-0.4961889613559395,1.728904529133913,-0.8052338712881586,-0.4961889613559395,1.763216229894814,-0.7326446581944602,-0.4961889613559395,1.7945932312513937,-0.6588360303712857,-0.4961889613559395,1.8229833092814651,-0.5839308349955378,-0.4961889613559395,1.8483392115019392,-0.508053744372587,-0.4961889613559395,1.8706187355159227,-0.43133104843152653,-0.4961889613559395,1.8897847992544432,-0.35389044452805307,-0.4961889613559395,1.9058055026958898,-0.27586082490481706,-0.4961889613559395,1.9186541809604538,-0.19737206216300168,-0.4961889613559395,1.9283094486911896,-0.11855479310219845,-0.4961889613559395,1.9347552356478304,-0.03954020128835024,-0.4961889613559395,1.9379808134541148,0.03954020128835125,-0.4961889613559395,1.9379808134541148,0.11855479310219774,-0.4961889613559395,1.9347552356478304,0.197372062163001,-0.4961889613559395,1.9283094486911898,0.27586082490481634,-0.4961889613559395,1.918654180960454,0.35389044452805235,-0.4961889613559395,1.90580550269589,0.4313310484315258,-0.4961889613559395,1.8897847992544432,0.5080537443725863,-0.4961889613559395,1.870618735515923,0.5839308349955372,-0.4961889613559395,1.8483392115019395,0.658836030371285,-0.4961889613559395,1.8229833092814653,0.7326446581944579,-0.4961889613559395,1.7945932312513946,0.8052338712881595,-0.4961889613559395,1.7632162298948135,0.8764828520709578,-0.4961889613559395,1.7289045291339125,0.9462730136458455,-0.4961889613559395,1.6917152374084137,1.0144881971764141,-0.4961889613559395,1.6517102526242142,1.0810148652217775,-0.4961889613559395,1.6089561591304276,1.1457422907084347,-0.4961889613559395,1.5635241168963094,1.2085627412245572,-0.4961889613559395,1.5154897430725092,1.2693716583299615,-0.4961889613559395,1.4649329861337823,1.3280678315833039,-0.4961889613559395,1.411937992812651,1.3845535669968827,-0.4961889613559395,1.3565929680454654,1.4387348496386325,-0.4961889613559395,1.2989900281639954,1.4905215001107008,-0.4961889613559395,1.2392250475768884,1.5398273246441574,-0.4961889613559395,1.1773974991961775,1.5865702585600179,-0.4961889613559395,1.1136102888744313,1.6306725028578004,-0.4961889613559395,1.0479695841281238,1.672060653704286,-0.4961889613559395,0.980584637432278,1.7106658246069517,-0.4961889613559395,0.9115676043805058,1.7464237610687414,-0.4961889613559395,0.8410333570130901,1.7792749475333363,-0.4961889613559395,0.7690992926238066,1.8091647064429228,-0.4961889613559395,0.6958851383637167,1.83604328924359,-0.4961889613559395,0.6215127519671457,1.8598659591868925,-0.4961889613559395,0.5461059189314988,1.8805930657897476,-0.4961889613559395,0.4697901464885297,1.8981901108287569,-0.4961889613559395,0.39269245470992636,1.91262780575909,-0.4961889613559395,0.3149411650949507,1.9238821204623795,-0.4961889613559395,0.23666568699195545,1.931934323242481,-0.4961889613559395,0.15799630220929956,1.9367710120025323,-0.4961889613559395,0.07906394817413955,1.9383841365514205,-0.4961889613559395,0.0,1.9735382729399185,-0.4992992860008892,0.0,1.9718958930956871,-0.4992992860008892,-0.08049783569164282,1.9669714871421584,-0.4992992860008892,-0.16086169068004114,1.958773251267057,-0.4992992860008892,-0.24095780725960858,1.9473148306249195,-0.4992992860008892,-0.3206528733489174,1.932615296626083,-0.4992992860008892,-0.39981424437550156,1.9146991151942037,-0.4992992860008892,-0.4783101640496568,1.8935961060451418,-0.4992992860008892,-0.5560099836597825,1.869341393054988,-0.4992992860008892,-0.632784379524268,1.8419753457998367,-0.4992992860008892,-0.7085055682379977,1.8115435123646118,-0.4992992860008892,-0.783047519355219,1.7780965435327738,-0.4992992860008892,-0.8562861651547797,1.74169010848309,-0.4992992860008892,-0.9280996071386077,1.70238480213378,-0.4992992860008892,-0.9983683189197295,1.6602460442882543,-0.4992992860008892,-1.0669753451621458,1.615343970750308,-0.4992992860008892,-1.1338064962414462,1.5677533165899964,-0.4992992860008892,-1.1987505383021704,1.5175532917544876,-0.4992992860008892,-1.2616993783955808,1.464827449230922,-0.4992992860008892,-1.3225482443897112,1.4096635459807103,-0.4992992860008892,-1.3811958593522375,1.352153396876733,-0.4992992860008892,-1.4375446101159395,1.2923927218865452,-0.4992992860008892,-1.4915007097461814,1.230480986755937,-0.4992992860008892,-1.5429743536400116,1.1665212374580156,-0.4992992860008892,-1.59187986899706,1.100619928683353,-0.4992992860008892,-1.6381358574134586,1.032886746656659,-0.4992992860008892,-1.6816653303614544,0.9634344265748865,-0.4992992860008892,-1.7223958373292114,0.8923785649706206,-0.4992992860008892,-1.7602595864075423,0.8198374273130568,-0.4992992860008892,-1.7951935571228481,0.745931751166802,-0.4992992860008892,-1.8271396053284774,0.6707845452361128,-0.4992992860008892,-1.8560445599799207,0.5945208846290448,-0.4992992860008892,-1.8818603116327668,0.517267702682282,-0.4992992860008892,-1.9045438925161242,0.43915357969312246,-0.4992992860008892,-1.9240575480482367,0.3603085289102642,-0.4992992860008892,-1.940368799675257,0.28086378013958335,-0.4992992860008892,-1.9534504989285957,0.20095156132507186,-0.4992992860008892,-1.9632808726108681,0.12070487846847677,-0.4992992860008892,-1.969843559035231,0.04025729425393463,-0.4992992860008892,-1.9731276352577976,-0.040257294253934386,-0.4992992860008892,-1.9731276352577976,-0.12070487846847652,-0.4992992860008892,-1.9698435590352312,-0.2009515613250712,-0.4992992860008892,-1.9632808726108681,-0.28086378013958313,-0.4992992860008892,-1.9534504989285957,-0.360308528910264,-0.4992992860008892,-1.940368799675257,-0.4391535796931218,-0.4992992860008892,-1.9240575480482367,-0.5172677026822817,-0.4992992860008892,-1.9045438925161244,-0.5945208846290442,-0.4992992860008892,-1.881860311632767,-0.6707845452361126,-0.4992992860008892,-1.856044559979921,-0.7459317511668018,-0.4992992860008892,-1.8271396053284776,-0.8198374273130562,-0.4992992860008892,-1.7951935571228483,-0.8923785649706207,-0.4992992860008892,-1.7602595864075423,-0.9634344265748864,-0.4992992860008892,-1.7223958373292114,-1.0328867466566594,-0.4992992860008892,-1.681665330361454,-1.1006199286833525,-0.4992992860008892,-1.638135857413459,-1.1665212374580152,-0.4992992860008892,-1.5918798689970601,-1.2304809867559368,-0.4992992860008892,-1.5429743536400118,-1.2923927218865454,-0.4992992860008892,-1.4915007097461814,-1.3521533968767332,-0.4992992860008892,-1.4375446101159395,-1.4096635459807103,-0.4992992860008892,-1.3811958593522375,-1.464827449230922,-0.4992992860008892,-1.3225482443897112,-1.5175532917544872,-0.4992992860008892,-1.2616993783955814,-1.5677533165899964,-0.4992992860008892,-1.1987505383021702,-1.6153439707503074,-0.4992992860008892,-1.133806496241447,-1.6602460442882545,-0.4992992860008892,-1.0669753451621455,-1.7023848021337797,-0.4992992860008892,-0.9983683189197298,-1.7416901084830898,-0.4992992860008892,-0.928099607138608,-1.7780965435327736,-0.4992992860008892,-0.8562861651547801,-1.8115435123646115,-0.4992992860008892,-0.7830475193552191,-1.8419753457998367,-0.4992992860008892,-0.7085055682379978,-1.869341393054988,-0.4992992860008892,-0.632784379524268,-1.8935961060451416,-0.4992992860008892,-0.5560099836597833,-1.9146991151942037,-0.4992992860008892,-0.47831016404965665,-1.9326152966260828,-0.4992992860008892,-0.39981424437550217,-1.9473148306249195,-0.4992992860008892,-0.32065287334891796,-1.958773251267057,-0.4992992860008892,-0.24095780725960908,-1.9669714871421584,-0.4992992860008892,-0.16086169068004155,-1.9718958930956871,-0.4992992860008892,-0.08049783569164314,-1.9735382729399185,-0.4992992860008892,-2.416887328950667e-16,-1.9718958930956871,-0.4992992860008892,0.08049783569164265,-1.9669714871421584,-0.4992992860008892,0.16086169068004105,-1.958773251267057,-0.4992992860008892,0.2409578072596086,-1.9473148306249197,-0.4992992860008892,0.3206528733489166,-1.9326152966260832,-0.4992992860008892,0.39981424437550084,-1.9146991151942037,-0.4992992860008892,0.47831016404965704,-1.8935961060451418,-0.4992992860008892,0.5560099836597828,-1.869341393054988,-0.4992992860008892,0.6327843795242676,-1.841975345799837,-0.4992992860008892,0.7085055682379974,-1.8115435123646118,-0.4992992860008892,0.7830475193552187,-1.7780965435327742,-0.4992992860008892,0.8562861651547788,-1.74169010848309,-0.4992992860008892,0.9280996071386075,-1.70238480213378,-0.4992992860008892,0.9983683189197293,-1.6602460442882543,-0.4992992860008892,1.0669753451621458,-1.6153439707503081,-0.4992992860008892,1.1338064962414458,-1.5677533165899968,-0.4992992860008892,1.1987505383021697,-1.5175532917544874,-0.4992992860008892,1.261699378395581,-1.4648274492309217,-0.4992992860008892,1.3225482443897114,-1.4096635459807108,-0.4992992860008892,1.3811958593522373,-1.3521533968767334,-0.4992992860008892,1.4375446101159393,-1.2923927218865463,-0.4992992860008892,1.4915007097461805,-1.230480986755937,-0.4992992860008892,1.5429743536400116,-1.1665212374580147,-0.4992992860008892,1.5918798689970604,-1.100619928683353,-0.4992992860008892,1.6381358574134586,-1.0328867466566598,-0.4992992860008892,1.6816653303614537,-0.9634344265748868,-0.4992992860008892,1.7223958373292112,-0.8923785649706197,-0.4992992860008892,1.760259586407543,-0.8198374273130566,-0.4992992860008892,1.7951935571228483,-0.7459317511668027,-0.4992992860008892,1.8271396053284772,-0.6707845452361123,-0.4992992860008892,1.856044559979921,-0.5945208846290451,-0.4992992860008892,1.8818603116327668,-0.5172677026822831,-0.4992992860008892,1.904543892516124,-0.43915357969312274,-0.4992992860008892,1.9240575480482367,-0.3603085289102636,-0.4992992860008892,1.940368799675257,-0.28086378013958313,-0.4992992860008892,1.9534504989285957,-0.20095156132507255,-0.4992992860008892,1.963280872610868,-0.12070487846847656,-0.4992992860008892,1.9698435590352312,-0.04025729425393356,-0.4992992860008892,1.9731276352577976,0.04025729425393459,-0.4992992860008892,1.9731276352577976,0.12070487846847584,-0.4992992860008892,1.9698435590352312,0.20095156132507183,-0.4992992860008892,1.9632808726108681,0.2808637801395824,-0.4992992860008892,1.953450498928596,0.3603085289102629,-0.4992992860008892,1.9403687996752572,0.439153579693122,-0.4992992860008892,1.9240575480482367,0.5172677026822824,-0.4992992860008892,1.9045438925161242,0.5945208846290444,-0.4992992860008892,1.881860311632767,0.6707845452361116,-0.4992992860008892,1.8560445599799211,0.7459317511668003,-0.4992992860008892,1.827139605328478,0.8198374273130575,-0.4992992860008892,1.795193557122848,0.8923785649706205,-0.4992992860008892,1.7602595864075423,0.9634344265748862,-0.4992992860008892,1.7223958373292116,1.0328867466566578,-0.4992992860008892,1.681665330361455,1.1006199286833522,-0.4992992860008892,1.638135857413459,1.1665212374580156,-0.4992992860008892,1.5918798689970597,1.2304809867559365,-0.4992992860008892,1.5429743536400118,1.2923927218865459,-0.4992992860008892,1.4915007097461812,1.3521533968767327,-0.4992992860008892,1.4375446101159397,1.4096635459807096,-0.4992992860008892,1.3811958593522384,1.4648274492309217,-0.4992992860008892,1.3225482443897114,1.517553291754488,-0.4992992860008892,1.26169937839558,1.5677533165899962,-0.4992992860008892,1.1987505383021704,1.6153439707503072,-0.4992992860008892,1.1338064962414471,1.6602460442882534,-0.4992992860008892,1.066975345162147,1.7023848021337797,-0.4992992860008892,0.99836831891973,1.7416901084830902,-0.4992992860008892,0.9280996071386074,1.7780965435327736,-0.4992992860008892,0.8562861651547802,1.811543512364611,-0.4992992860008892,0.7830475193552202,1.8419753457998367,-0.4992992860008892,0.7085055682379982,1.8693413930549876,-0.4992992860008892,0.6327843795242691,1.8935961060451418,-0.4992992860008892,0.5560099836597827,1.9146991151942034,-0.4992992860008892,0.47831016404965776,1.932615296626083,-0.4992992860008892,0.39981424437550156,1.9473148306249195,-0.4992992860008892,0.3206528733489182,1.958773251267057,-0.4992992860008892,0.2409578072596102,1.9669714871421584,-0.4992992860008892,0.16086169068004177,1.9718958930956871,-0.4992992860008892,0.08049783569164251,1.9735382729399185,-0.4992992860008892,0.0,2.008824240321564,-0.49992212671849934,0.0,2.007152495472151,-0.49992212671849934,-0.08193710040895538,2.0021400433782293,-0.49992212671849934,-0.16373782460058411,1.9937952267716352,-0.49992212671849934,-0.24526602334231448,1.9821319347760675,-0.49992212671849934,-0.32638600099329085,1.9671695797900122,-0.49992212671849934,-0.40696274135637606,1.948933065176721,-0.49992212671849934,-0.4868621323992869,1.9274527438150213,-0.49992212671849934,-0.5659511894708369,1.902764367579945,-0.49992212671849934,-0.6440982766407634,1.8749090278372598,-0.49992212671849934,-0.7211733257947428,1.8439330870509476,-0.49992212671849934,-0.797048053119931,1.8098881016174568,-0.49992212671849934,-0.8715961726207083,1.7728307360551707,-0.49992212671849934,-0.9446936063092578,1.7328226686919073,-0.49992212671849934,-1.0162186907211292,1.6899304890074296,-0.49992212671849934,-1.0860523794120702,1.6442255868018294,-0.49992212671849934,-1.154078441099083,1.5957840333742488,-0.49992212671849934,-1.2201836531159251,1.5446864549097117,-0.49992212671849934,-1.2842579898610624,1.491017898284795,-0.49992212671849934,-1.3461948059244258,1.4348676895154957,-0.49992212671849934,-1.40589101358817,1.3763292850828932,-0.49992212671849934,-1.4632472544060082,1.315500116384059,-0.49992212671849934,-1.5181680645755384,1.252481427567113,-0.49992212671849934,-1.570562033828322,1.1873781070203286,-0.49992212671849934,-1.6203419575732558,1.1202985127957636,-0.49992212671849934,-1.6674249820400044,1.0513542922579753,-0.49992212671849934,-1.7117327421809287,0.9806601962580017,-0.49992212671849934,-1.7531914921019685,0.9083338881418941,-0.49992212671849934,-1.7917322278054102,0.8344957479116872,-0.49992212671849934,-1.8272908020402288,0.7592686718647707,-0.49992212671849934,-1.8598080310688565,0.6827778680451274,-0.49992212671849934,-1.8892297931726727,0.6051506478469008,-0.49992212671849934,-1.9155071187322656,0.5265162141171456,-0.49992212671849934,-1.9385962717325278,0.4470054461104383,-0.49992212671849934,-1.9584588225569404,0.3667506816532748,-0.49992212671849934,-1.9750617119498741,0.28588549688081866,-0.49992212671849934,-1.98837730604046,0.20454448391260516,-0.49992212671849934,-1.998383442336439,0.1228630268372436,-0.49992212671849934,-2.0050634666114435,0.040977076378961044,-0.49992212671849934,-2.0084062606243145,-0.040977076378960794,-0.49992212671849934,-2.0084062606243145,-0.12286302683724334,-0.49992212671849934,-2.005063466611444,-0.2045444839126045,-0.49992212671849934,-1.998383442336439,-0.28588549688081843,-0.49992212671849934,-1.98837730604046,-0.36675068165327457,-0.49992212671849934,-1.9750617119498741,-0.44700544611043763,-0.49992212671849934,-1.9584588225569404,-0.5265162141171453,-0.49992212671849934,-1.938596271732528,-0.6051506478469001,-0.49992212671849934,-1.9155071187322659,-0.6827778680451271,-0.49992212671849934,-1.889229793172673,-0.7592686718647704,-0.49992212671849934,-1.8598080310688567,-0.8344957479116867,-0.49992212671849934,-1.827290802040229,-0.9083338881418942,-0.49992212671849934,-1.7917322278054102,-0.9806601962580016,-0.49992212671849934,-1.7531914921019685,-1.0513542922579757,-0.49992212671849934,-1.7117327421809283,-1.1202985127957632,-0.49992212671849934,-1.6674249820400049,-1.1873781070203282,-0.49992212671849934,-1.620341957573256,-1.2524814275671128,-0.49992212671849934,-1.5705620338283226,-1.3155001163840592,-0.49992212671849934,-1.5181680645755384,-1.3763292850828934,-0.49992212671849934,-1.4632472544060082,-1.4348676895154957,-0.49992212671849934,-1.40589101358817,-1.491017898284795,-0.49992212671849934,-1.3461948059244258,-1.5446864549097112,-0.49992212671849934,-1.284257989861063,-1.5957840333742488,-0.49992212671849934,-1.220183653115925,-1.644225586801829,-0.49992212671849934,-1.1540784410990836,-1.6899304890074298,-0.49992212671849934,-1.08605237941207,-1.732822668691907,-0.49992212671849934,-1.0162186907211295,-1.7728307360551705,-0.49992212671849934,-0.944693606309258,-1.8098881016174566,-0.49992212671849934,-0.8715961726207087,-1.8439330870509474,-0.49992212671849934,-0.7970480531199311,-1.8749090278372598,-0.49992212671849934,-0.7211733257947429,-1.902764367579945,-0.49992212671849934,-0.6440982766407634,-1.927452743815021,-0.49992212671849934,-0.5659511894708377,-1.948933065176721,-0.49992212671849934,-0.4868621323992867,-1.967169579790012,-0.49992212671849934,-0.40696274135637667,-1.9821319347760675,-0.49992212671849934,-0.3263860009932914,-1.9937952267716352,-0.49992212671849934,-0.24526602334231498,-2.0021400433782293,-0.49992212671849934,-0.16373782460058453,-2.007152495472151,-0.49992212671849934,-0.0819371004089557,-2.008824240321564,-0.49992212671849934,-2.460100175959417e-16,-2.007152495472151,-0.49992212671849934,0.08193710040895522,-2.0021400433782293,-0.49992212671849934,0.16373782460058403,-1.9937952267716352,-0.49992212671849934,0.2452660233423145,-1.9821319347760677,-0.49992212671849934,0.32638600099329007,-1.9671695797900124,-0.49992212671849934,0.40696274135637533,-1.948933065176721,-0.49992212671849934,0.4868621323992871,-1.9274527438150213,-0.49992212671849934,0.5659511894708372,-1.902764367579945,-0.49992212671849934,0.6440982766407629,-1.87490902783726,-0.49992212671849934,0.7211733257947425,-1.8439330870509476,-0.49992212671849934,0.7970480531199307,-1.8098881016174573,-0.49992212671849934,0.8715961726207074,-1.7728307360551707,-0.49992212671849934,0.9446936063092576,-1.7328226686919073,-0.49992212671849934,1.016218690721129,-1.6899304890074296,-0.49992212671849934,1.0860523794120702,-1.6442255868018296,-0.49992212671849934,1.1540784410990825,-1.5957840333742492,-0.49992212671849934,1.2201836531159245,-1.5446864549097115,-0.49992212671849934,1.2842579898610627,-1.4910178982847948,-0.49992212671849934,1.346194805924426,-1.4348676895154961,-0.49992212671849934,1.4058910135881697,-1.3763292850828936,-0.49992212671849934,1.463247254406008,-1.31550011638406,-0.49992212671849934,1.5181680645755373,-1.252481427567113,-0.49992212671849934,1.570562033828322,-1.1873781070203278,-0.49992212671849934,1.6203419575732563,-1.1202985127957636,-0.49992212671849934,1.6674249820400044,-1.0513542922579762,-0.49992212671849934,1.711732742180928,-0.9806601962580019,-0.49992212671849934,1.7531914921019682,-0.9083338881418931,-0.49992212671849934,1.7917322278054109,-0.8344957479116871,-0.49992212671849934,1.827290802040229,-0.7592686718647713,-0.49992212671849934,1.8598080310688563,-0.6827778680451267,-0.49992212671849934,1.889229793172673,-0.605150647846901,-0.49992212671849934,1.9155071187322656,-0.5265162141171467,-0.49992212671849934,1.9385962717325276,-0.4470054461104386,-0.49992212671849934,1.9584588225569404,-0.3667506816532742,-0.49992212671849934,1.9750617119498741,-0.28588549688081843,-0.49992212671849934,1.98837730604046,-0.20454448391260585,-0.49992212671849934,1.9983834423364388,-0.12286302683724339,-0.49992212671849934,2.005063466611444,-0.040977076378959955,-0.49992212671849934,2.0084062606243145,0.040977076378961,-0.49992212671849934,2.0084062606243145,0.12286302683724265,-0.49992212671849934,2.005063466611444,0.20454448391260513,-0.49992212671849934,1.998383442336439,0.2858854968808177,-0.49992212671849934,1.9883773060404601,0.36675068165327346,-0.49992212671849934,1.9750617119498743,0.44700544611043785,-0.49992212671849934,1.9584588225569404,0.526516214117146,-0.49992212671849934,1.9385962717325278,0.6051506478469003,-0.49992212671849934,1.9155071187322659,0.6827778680451261,-0.49992212671849934,1.8892297931726731,0.759268671864769,-0.49992212671849934,1.8598080310688572,0.834495747911688,-0.49992212671849934,1.8272908020402285,0.908333888141894,-0.49992212671849934,1.7917322278054102,0.9806601962580014,-0.49992212671849934,1.7531914921019687,1.0513542922579742,-0.49992212671849934,1.7117327421809294,1.120298512795763,-0.49992212671849934,1.6674249820400049,1.1873781070203286,-0.49992212671849934,1.6203419575732556,1.2524814275671126,-0.49992212671849934,1.5705620338283226,1.3155001163840596,-0.49992212671849934,1.5181680645755382,1.376329285082893,-0.49992212671849934,1.4632472544060084,1.434867689515495,-0.49992212671849934,1.4058910135881708,1.4910178982847948,-0.49992212671849934,1.346194805924426,1.5446864549097121,-0.49992212671849934,1.2842579898610618,1.5957840333742486,-0.49992212671849934,1.2201836531159251,1.6442255868018287,-0.49992212671849934,1.1540784410990839,1.6899304890074287,-0.49992212671849934,1.0860523794120716,1.732822668691907,-0.49992212671849934,1.0162186907211297,1.772830736055171,-0.49992212671849934,0.9446936063092575,1.8098881016174566,-0.49992212671849934,0.8715961726207089,1.843933087050947,-0.49992212671849934,0.7970480531199322,1.8749090278372598,-0.49992212671849934,0.7211733257947432,1.9027643675799446,-0.49992212671849934,0.6440982766407645,1.9274527438150213,-0.49992212671849934,0.5659511894708371,1.9489330651767207,-0.49992212671849934,0.48686213239928783,1.9671695797900122,-0.49992212671849934,0.40696274135637606,1.9821319347760675,-0.49992212671849934,0.3263860009932916,1.9937952267716352,-0.49992212671849934,0.24526602334231615,2.0021400433782293,-0.49992212671849934,0.16373782460058475,2.007152495472151,-0.49992212671849934,0.08193710040895505,2.008824240321564,-0.49992212671849934,0.0,2.044066245781801,-0.4980543805476445,0.0,2.0423651725123455,-0.4980543805476445,-0.08337457198165311,2.037264783972558,-0.4980543805476445,-0.16661037521640973,2.02877356925581,-0.4980543805476445,-0.24956887192425753,2.0169056611510072,-0.4980543805476445,-0.3321119858745312,2.0016808126199552,-0.4980543805476445,-0.41410233220017156,1.9831243639205072,-0.4980543805476445,-0.4954034460612784,1.9612672004302023,-0.4980543805476445,-0.575880009777369,1.9361457012406054,-0.4980543805476445,-0.655398078050303,1.9078016786078973,-0.4980543805476445,-0.7338253009030117,1.8762823083605025,-0.4980543805476445,-0.8110311439629743,1.8416400513795774,-0.4980543805476445,-0.8868871057237973,1.8039325662830512,-0.4980543805476445,-0.9612669314232918,1.763222613458547,-0.4980543805476445,-1.0340468231820639,1.7195779506049098,-0.4980543805476445,-1.1051056460528674,1.6730712199562041,-0.4980543805476445,-1.1743251296377701,1.623779827375884,-0.4980543805476445,-1.2415900649375586,1.5717858135223735,-0.4980543805476445,-1.3067884961057459,1.5171757173004856,-0.4980543805476445,-1.3698119067880317,1.4600404318259568,-0.4980543805476445,-1.430555400737063,1.400475053142826,-0.4980543805476445,-1.488917876401887,1.3385787219454535,-0.4980543805476445,-1.5448021952015027,1.2744544585686206,-0.4980543805476445,-1.5981153432024455,1.2082089915203473,-0.4980543805476445,-1.648768585931298,1.1399525798428256,-0.4980543805476445,-1.6966776160644639,1.0697988295971246,-0.4980543805476445,-1.7417626937493895,0.9978645047771131,-0.4980543805476445,-1.783948779323676,0.9242693329673162,-0.4980543805476445,-1.8231656582111946,0.8491358060681641,-0.4980543805476445,-1.859348057787319,0.7725889764203159,-0.4980543805476445,-1.892435756018765,0.6947562486673781,-0.4980543805476445,-1.9223736816972237,0.6157671677034475,-0.4980543805476445,-1.9491120060999523,0.5357532030584227,-0.4980543805476445,-1.9726062259247654,0.4548475300799438,-0.4980543805476445,-1.9928172373613897,0.37318480827617007,-0.4980543805476445,-2.0097114011758963,0.29090095718831854,-0.4980543805476445,-2.023260598699887,0.20813293016599935,-0.4980543805476445,-2.033442278631243,0.12501848642188415,-0.4980543805476445,-2.0402394945685387,0.0416959617450898,-0.4980543805476445,-2.0436409332166594,-0.04169596174508955,-0.4980543805476445,-2.0436409332166594,-0.12501848642188387,-0.4980543805476445,-2.040239494568539,-0.20813293016599868,-0.4980543805476445,-2.033442278631243,-0.2909009571883183,-0.4980543805476445,-2.023260598699887,-0.37318480827616984,-0.4980543805476445,-2.0097114011758963,-0.4548475300799431,-0.4980543805476445,-1.9928172373613897,-0.5357532030584224,-0.4980543805476445,-1.9726062259247656,-0.6157671677034469,-0.4980543805476445,-1.9491120060999525,-0.6947562486673778,-0.4980543805476445,-1.922373681697224,-0.7725889764203157,-0.4980543805476445,-1.8924357560187652,-0.8491358060681635,-0.4980543805476445,-1.8593480577873192,-0.9242693329673163,-0.4980543805476445,-1.8231656582111946,-0.997864504777113,-0.4980543805476445,-1.783948779323676,-1.069798829597125,-0.4980543805476445,-1.741762693749389,-1.1399525798428252,-0.4980543805476445,-1.6966776160644643,-1.2082089915203469,-0.4980543805476445,-1.6487685859312982,-1.2744544585686204,-0.4980543805476445,-1.598115343202446,-1.3385787219454537,-0.4980543805476445,-1.5448021952015027,-1.4004750531428263,-0.4980543805476445,-1.488917876401887,-1.4600404318259568,-0.4980543805476445,-1.430555400737063,-1.5171757173004856,-0.4980543805476445,-1.3698119067880317,-1.571785813522373,-0.4980543805476445,-1.3067884961057465,-1.623779827375884,-0.4980543805476445,-1.2415900649375584,-1.6730712199562037,-0.4980543805476445,-1.1743251296377708,-1.71957795060491,-0.4980543805476445,-1.105105646052867,-1.7632226134585467,-0.4980543805476445,-1.034046823182064,-1.803932566283051,-0.4980543805476445,-0.961266931423292,-1.8416400513795772,-0.4980543805476445,-0.8868871057237976,-1.8762823083605022,-0.4980543805476445,-0.8110311439629744,-1.9078016786078973,-0.4980543805476445,-0.7338253009030118,-1.9361457012406054,-0.4980543805476445,-0.655398078050303,-1.961267200430202,-0.4980543805476445,-0.5758800097773699,-1.9831243639205072,-0.4980543805476445,-0.49540344606127823,-2.0016808126199552,-0.4980543805476445,-0.41410233220017223,-2.0169056611510072,-0.4980543805476445,-0.3321119858745318,-2.02877356925581,-0.4980543805476445,-0.24956887192425803,-2.037264783972558,-0.4980543805476445,-0.16661037521641014,-2.0423651725123455,-0.4980543805476445,-0.08337457198165345,-2.044066245781801,-0.4980543805476445,-2.5032591851418296e-16,-2.0423651725123455,-0.4980543805476445,0.08337457198165295,-2.037264783972558,-0.4980543805476445,0.16661037521640965,-2.02877356925581,-0.4980543805476445,0.24956887192425756,-2.0169056611510072,-0.4980543805476445,0.33211198587453045,-2.0016808126199557,-0.4980543805476445,0.41410233220017084,-1.9831243639205072,-0.4980543805476445,0.4954034460612786,-1.9612672004302023,-0.4980543805476445,0.5758800097773694,-1.9361457012406054,-0.4980543805476445,0.6553980780503026,-1.9078016786078975,-0.4980543805476445,0.7338253009030113,-1.8762823083605025,-0.4980543805476445,0.8110311439629739,-1.8416400513795779,-0.4980543805476445,0.8868871057237964,-1.8039325662830512,-0.4980543805476445,0.9612669314232916,-1.763222613458547,-0.4980543805476445,1.0340468231820636,-1.7195779506049098,-0.4980543805476445,1.1051056460528674,-1.6730712199562043,-0.4980543805476445,1.1743251296377697,-1.6237798273758846,-0.4980543805476445,1.241590064937558,-1.5717858135223732,-0.4980543805476445,1.306788496105746,-1.5171757173004854,-0.4980543805476445,1.3698119067880319,-1.4600404318259572,-0.4980543805476445,1.4305554007370629,-1.4004750531428265,-0.4980543805476445,1.4889178764018869,-1.3385787219454546,-0.4980543805476445,1.5448021952015019,-1.2744544585686206,-0.4980543805476445,1.5981153432024455,-1.2082089915203464,-0.4980543805476445,1.6487685859312984,-1.1399525798428256,-0.4980543805476445,1.6966776160644639,-1.0697988295971255,-0.4980543805476445,1.7417626937493889,-0.9978645047771133,-0.4980543805476445,1.7839487793236757,-0.9242693329673152,-0.4980543805476445,1.8231656582111953,-0.849135806068164,-0.4980543805476445,1.8593480577873192,-0.7725889764203165,-0.4980543805476445,1.8924357560187648,-0.6947562486673775,-0.4980543805476445,1.922373681697224,-0.6157671677034477,-0.4980543805476445,1.9491120060999523,-0.5357532030584238,-0.4980543805476445,1.9726062259247652,-0.45484753007994405,-0.4980543805476445,1.9928172373613897,-0.37318480827616946,-0.4980543805476445,2.0097114011758963,-0.2909009571883183,-0.4980543805476445,2.023260598699887,-0.20813293016600007,-0.4980543805476445,2.033442278631243,-0.12501848642188393,-0.4980543805476445,2.040239494568539,-0.04169596174508869,-0.4980543805476445,2.0436409332166594,0.04169596174508976,-0.4980543805476445,2.0436409332166594,0.12501848642188318,-0.4980543805476445,2.040239494568539,0.20813293016599932,-0.4980543805476445,2.033442278631243,0.2909009571883176,-0.4980543805476445,2.0232605986998875,0.37318480827616873,-0.4980543805476445,2.0097114011758968,0.45484753007994333,-0.4980543805476445,1.9928172373613897,0.5357532030584231,-0.4980543805476445,1.9726062259247654,0.6157671677034471,-0.4980543805476445,1.9491120060999525,0.6947562486673768,-0.4980543805476445,1.9223736816972241,0.7725889764203142,-0.4980543805476445,1.8924357560187657,0.8491358060681649,-0.4980543805476445,1.8593480577873187,0.9242693329673161,-0.4980543805476445,1.8231656582111946,0.9978645047771127,-0.4980543805476445,1.7839487793236761,1.0697988295971235,-0.4980543805476445,1.7417626937493902,1.139952579842825,-0.4980543805476445,1.6966776160644643,1.2082089915203473,-0.4980543805476445,1.6487685859312977,1.2744544585686202,-0.4980543805476445,1.598115343202446,1.3385787219454541,-0.4980543805476445,1.5448021952015025,1.4004750531428258,-0.4980543805476445,1.4889178764018873,1.460040431825956,-0.4980543805476445,1.4305554007370642,1.5171757173004854,-0.4980543805476445,1.3698119067880319,1.571785813522374,-0.4980543805476445,1.3067884961057452,1.6237798273758839,-0.4980543805476445,1.2415900649375586,1.6730712199562034,-0.4980543805476445,1.174325129637771,1.7195779506049087,-0.4980543805476445,1.1051056460528688,1.7632226134585467,-0.4980543805476445,1.0340468231820643,1.8039325662830514,-0.4980543805476445,0.9612669314232914,1.8416400513795772,-0.4980543805476445,0.8868871057237979,1.8762823083605018,-0.4980543805476445,0.8110311439629755,1.9078016786078973,-0.4980543805476445,0.7338253009030121,1.936145701240605,-0.4980543805476445,0.6553980780503041,1.9612672004302023,-0.4980543805476445,0.5758800097773692,1.983124363920507,-0.4980543805476445,0.49540344606127934,2.0016808126199552,-0.4980543805476445,0.41410233220017156,2.0169056611510072,-0.4980543805476445,0.332111985874532,2.02877356925581,-0.4980543805476445,0.2495688719242592,2.037264783972558,-0.4980543805476445,0.16661037521641037,2.0423651725123455,-0.4980543805476445,0.0833745719816528,2.044066245781801,-0.4980543805476445,0.0,2.079088715422152,-0.4937053525057976,0.0,2.077358496430507,-0.4937053525057976,-0.08480308899866783,2.0721707192343306,-0.4937053525057976,-0.16946503162484347,2.0635340183767887,-0.4937053525057976,-0.25384491643024215,2.0514627687941087,-0.4937053525057976,-0.33780230142398576,2.0359770618899207,-0.4937053525057976,-0.42119744782443647,2.0171026720950946,-0.4937053525057976,-0.503891552640608,1.994871013968746,-0.4937053525057976,-0.5857469796960464,1.9693190899118027,-0.4937053525057976,-0.6666274887106614,1.940489428580159,-0.4937053525057976,-0.7463984620592282,1.9084300140999266,-0.4937053525057976,-0.8249271288291391,1.873194206202591,-0.4937053525057976,-0.9020827858044839,1.8348406514130045,-0.4937053525057976,-0.9777370150086548,1.7934331854380343,-0.4937053525057976,-1.0517638974433943,1.7490407269183275,-0.4937053525057976,-1.1240402226685404,1.7017371627200355,-0.4937053525057976,-1.1944456938736467,1.6516012249574166,-0.4937053525057976,-1.2628631281001497,1.598716359951003,-0.4937053525057976,-1.3291786512808381,1.543170589339435,-0.4937053525057976,-1.3932818877720021,1.4850563635761296,-0.4937053525057976,-1.4550661440627968,1.424470408054625,-0.4937053525057976,-1.514428586356063,1.36151356211871,-0.4937053525057976,-1.571270411725033,1.2962906112252863,-0.4937053525057976,-1.6254970125610524,1.228910112539317,-0.4937053525057976,-1.6770181340386086,1.1594842142511395,-0.4937053525057976,-1.7257480243355778,1.0881284689168707,-0.4937053525057976,-1.771605577358674,1.0149616411325806,-0.4937053525057976,-1.8145144677365326,0.9401055098623435,-0.4937053525057976,-1.8544032778557624,0.8636846657491686,-0.4937053525057976,-1.8912056167285143,0.7858263037461732,-0.4937053525057976,-1.9248602304937261,0.7066600114131353,-0.4937053525057976,-1.955311104368131,0.6263175532307866,-0.4937053525057976,-1.9825075558773373,0.5449326512918422,-0.4937053525057976,-2.0064043192118026,0.4626407627337713,-0.4937053525057976,-2.026961620567308,0.37957885428376087,-0.4937053525057976,-2.0441452443445307,0.2958851742911161,-0.4937053525057976,-2.0579265900975305,0.21169902262652426,-0.4937053525057976,-2.0682827201363714,0.12716051883117055,-0.4937053525057976,-2.0751963977046413,0.04241036890158786,-0.4937053525057976,-2.0786561156683336,-0.0424103689015876,-0.4937053525057976,-2.0786561156683336,-0.1271605188311703,-0.4937053525057976,-2.0751963977046417,-0.21169902262652357,-0.4937053525057976,-2.0682827201363714,-0.2958851742911159,-0.4937053525057976,-2.0579265900975305,-0.37957885428376065,-0.4937053525057976,-2.0441452443445307,-0.4626407627337706,-0.4937053525057976,-2.026961620567308,-0.544932651291842,-0.4937053525057976,-2.0064043192118026,-0.626317553230786,-0.4937053525057976,-1.9825075558773375,-0.7066600114131351,-0.4937053525057976,-1.9553111043681313,-0.785826303746173,-0.4937053525057976,-1.9248602304937263,-0.863684665749168,-0.4937053525057976,-1.8912056167285145,-0.9401055098623436,-0.4937053525057976,-1.8544032778557624,-1.0149616411325804,-0.4937053525057976,-1.8145144677365326,-1.088128468916871,-0.4937053525057976,-1.7716055773586736,-1.159484214251139,-0.4937053525057976,-1.7257480243355783,-1.2289101125393165,-0.4937053525057976,-1.677018134038609,-1.296290611225286,-0.4937053525057976,-1.6254970125610528,-1.3615135621187102,-0.4937053525057976,-1.571270411725033,-1.4244704080546253,-0.4937053525057976,-1.514428586356063,-1.4850563635761296,-0.4937053525057976,-1.4550661440627968,-1.543170589339435,-0.4937053525057976,-1.3932818877720021,-1.5987163599510026,-0.4937053525057976,-1.329178651280839,-1.6516012249574166,-0.4937053525057976,-1.2628631281001494,-1.701737162720035,-0.4937053525057976,-1.1944456938736474,-1.7490407269183277,-0.4937053525057976,-1.1240402226685402,-1.793433185438034,-0.4937053525057976,-1.0517638974433945,-1.8348406514130042,-0.4937053525057976,-0.9777370150086551,-1.8731942062025908,-0.4937053525057976,-0.9020827858044842,-1.9084300140999264,-0.4937053525057976,-0.8249271288291392,-1.940489428580159,-0.4937053525057976,-0.7463984620592283,-1.9693190899118027,-0.4937053525057976,-0.6666274887106614,-1.9948710139687458,-0.4937053525057976,-0.5857469796960472,-2.0171026720950946,-0.4937053525057976,-0.5038915526406079,-2.0359770618899207,-0.4937053525057976,-0.42119744782443713,-2.0514627687941087,-0.4937053525057976,-0.3378023014239863,-2.0635340183767887,-0.4937053525057976,-0.25384491643024265,-2.0721707192343306,-0.4937053525057976,-0.16946503162484391,-2.077358496430507,-0.4937053525057976,-0.08480308899866816,-2.079088715422152,-0.4937053525057976,-2.5461493404851208e-16,-2.077358496430507,-0.4937053525057976,0.08480308899866766,-2.0721707192343306,-0.4937053525057976,0.1694650316248434,-2.0635340183767887,-0.4937053525057976,0.2538449164302422,-2.051462768794109,-0.4937053525057976,0.33780230142398493,-2.035977061889921,-0.4937053525057976,0.42119744782443574,-2.0171026720950946,-0.4937053525057976,0.5038915526406084,-1.994871013968746,-0.4937053525057976,0.5857469796960467,-1.9693190899118027,-0.4937053525057976,0.6666274887106609,-1.9404894285801593,-0.4937053525057976,0.7463984620592279,-1.9084300140999266,-0.4937053525057976,0.8249271288291388,-1.8731942062025915,-0.4937053525057976,0.9020827858044829,-1.8348406514130045,-0.4937053525057976,0.9777370150086545,-1.7934331854380343,-0.4937053525057976,1.0517638974433938,-1.7490407269183275,-0.4937053525057976,1.1240402226685404,-1.7017371627200357,-0.4937053525057976,1.194445693873646,-1.651601224957417,-0.4937053525057976,1.262863128100149,-1.5987163599510028,-0.4937053525057976,1.3291786512808386,-1.5431705893394347,-0.4937053525057976,1.3932818877720023,-1.48505636357613,-0.4937053525057976,1.4550661440627966,-1.4244704080546255,-0.4937053525057976,1.5144285863560627,-1.3615135621187113,-0.4937053525057976,1.5712704117250318,-1.2962906112252863,-0.4937053525057976,1.6254970125610524,-1.228910112539316,-0.4937053525057976,1.6770181340386092,-1.1594842142511395,-0.4937053525057976,1.7257480243355778,-1.0881284689168715,-0.4937053525057976,1.7716055773586734,-1.0149616411325808,-0.4937053525057976,1.8145144677365324,-0.9401055098623425,-0.4937053525057976,1.854403277855763,-0.8636846657491685,-0.4937053525057976,1.8912056167285145,-0.785826303746174,-0.4937053525057976,1.924860230493726,-0.7066600114131347,-0.4937053525057976,1.9553111043681313,-0.6263175532307869,-0.4937053525057976,1.9825075558773373,-0.5449326512918433,-0.4937053525057976,2.006404319211802,-0.4626407627337716,-0.4937053525057976,2.026961620567308,-0.37957885428376026,-0.4937053525057976,2.0441452443445307,-0.2958851742911159,-0.4937053525057976,2.0579265900975305,-0.21169902262652499,-0.4937053525057976,2.0682827201363714,-0.12716051883117033,-0.4937053525057976,2.0751963977046417,-0.042410368901586726,-0.4937053525057976,2.0786561156683336,0.04241036890158781,-0.4937053525057976,2.0786561156683336,0.12716051883116958,-0.4937053525057976,2.0751963977046417,0.21169902262652424,-0.4937053525057976,2.0682827201363714,0.2958851742911151,-0.4937053525057976,2.0579265900975305,0.3795788542837595,-0.4937053525057976,2.0441452443445307,0.4626407627337708,-0.4937053525057976,2.026961620567308,0.5449326512918427,-0.4937053525057976,2.0064043192118026,0.6263175532307862,-0.4937053525057976,1.9825075558773375,0.706660011413134,-0.4937053525057976,1.9553111043681315,0.7858263037461716,-0.4937053525057976,1.9248602304937268,0.8636846657491694,-0.4937053525057976,1.891205616728514,0.9401055098623434,-0.4937053525057976,1.8544032778557624,1.0149616411325801,-0.4937053525057976,1.8145144677365328,1.0881284689168695,-0.4937053525057976,1.7716055773586747,1.1594842142511388,-0.4937053525057976,1.7257480243355783,1.228910112539317,-0.4937053525057976,1.6770181340386083,1.2962906112252859,-0.4937053525057976,1.6254970125610528,1.3615135621187107,-0.4937053525057976,1.5712704117250327,1.4244704080546249,-0.4937053525057976,1.5144285863560631,1.485056363576129,-0.4937053525057976,1.4550661440627977,1.5431705893394347,-0.4937053525057976,1.3932818877720023,1.5987163599510035,-0.4937053525057976,1.3291786512808375,1.6516012249574163,-0.4937053525057976,1.2628631281001497,1.7017371627200348,-0.4937053525057976,1.1944456938736476,1.7490407269183266,-0.4937053525057976,1.1240402226685418,1.793433185438034,-0.4937053525057976,1.0517638974433947,1.834840651413005,-0.4937053525057976,0.9777370150086544,1.8731942062025908,-0.4937053525057976,0.9020827858044844,1.908430014099926,-0.4937053525057976,0.8249271288291403,1.940489428580159,-0.4937053525057976,0.7463984620592287,1.9693190899118023,-0.4937053525057976,0.6666274887106626,1.994871013968746,-0.4937053525057976,0.5857469796960466,2.017102672095094,-0.4937053525057976,0.503891552640609,2.0359770618899207,-0.4937053525057976,0.42119744782443647,2.0514627687941087,-0.4937053525057976,0.3378023014239866,2.0635340183767887,-0.4937053525057976,0.25384491643024387,2.0721707192343306,-0.4937053525057976,0.16946503162484414,2.077358496430507,-0.4937053525057976,0.0848030889986675,2.079088715422152,-0.4937053525057976,0.0,2.113717169060553,-0.4868967092319002,0.0,2.111958132247094,-0.4868967092319002,-0.08621553466007738,2.1066839495498964,-0.4868967092319002,-0.17228757207125225,2.0979033993255545,-0.4868967092319002,-0.25807285382163125,2.0856310959333713,-0.4868967092319002,-0.3434285987758188,2.0698874654112003,-0.4868967092319002,-0.428212740720026,2.050698711478319,-0.4868967092319002,-0.5122841648172628,2.0280967719219274,-0.4868967092319002,-0.595502942479058,2.00211926543985,-0.4868967092319002,-0.6777305642627856,1.972809429027928,-0.4868967092319002,-0.7588301704069615,1.940216046016306,-0.4868967092319002,-0.8386667786208118,1.9043933648743927,-0.4868967092319002,-0.9171075087489723,1.8654010089196384,-0.4868967092319002,-0.9940218039373953,1.8233038770804066,-0.4868967092319002,-1.069281647932347,1.7781720358781132,-0.4868967092319002,-1.1427617781508288,1.7300806028084157,-0.4868967092319002,-1.2143398941677852,1.6791096213155545,-0.4868967092319002,-1.2838968612730908,1.6253439275679413,-0.4868967092319002,-1.351316908759517,1.5688730092567278,-0.4868967092319002,-1.4164878226116515,1.5097908566523746,-0.4868967092319002,-1.4793011322750507,1.4481958061671238,-0.4868967092319002,-1.5396522911947725,1.3841903766837438,-0.4868967092319002,-1.5974408508228006,1.3178810989229675,-0.4868967092319002,-1.652570627804739,1.2493783381336199,-0.4868967092319002,-1.7049498640675134,1.1787961104005547,-0.4868967092319002,-1.7544913795416268,1.1062518928761322,-0.4868967092319002,-1.8011127172637844,1.031866428251094,-0.4868967092319002,-1.8447362806183674,0.9557635237902695,-0.4868967092319002,-1.8852894624893455,0.8780698452676002,-0.4868967092319002,-1.9227047661076546,0.7989147061434636,-0.4868967092319002,-1.9569199173929068,0.7184298523351774,-0.4868967092319002,-1.9878779686024526,0.6367492429389218,-0.4868967092319002,-2.0155273931152777,0.5540088272680458,-0.4868967092319002,-2.03982217119298,0.47034631857885123,-0.4868967092319002,-2.0607218665750797,0.38590096486046793,-0.4868967092319002,-2.078191693781187,0.3008133170703191,-0.4868967092319002,-2.092202576008,0.21522499520092156,-0.4868967092319002,-2.1027311935247743,0.12927845256738735,-0.4868967092319002,-2.109760022486714,0.04311673870793733,-0.4868967092319002,-2.1132773641016813,-0.04311673870793707,-0.4868967092319002,-2.1132773641016813,-0.1292784525673871,-0.4868967092319002,-2.1097600224867143,-0.21522499520092087,-0.4868967092319002,-2.1027311935247743,-0.3008133170703189,-0.4868967092319002,-2.092202576008,-0.3859009648604677,-0.4868967092319002,-2.078191693781187,-0.47034631857885056,-0.4868967092319002,-2.0607218665750797,-0.5540088272680456,-0.4868967092319002,-2.0398221711929803,-0.6367492429389211,-0.4868967092319002,-2.015527393115278,-0.7184298523351772,-0.4868967092319002,-1.9878779686024528,-0.7989147061434634,-0.4868967092319002,-1.956919917392907,-0.8780698452675996,-0.4868967092319002,-1.9227047661076548,-0.9557635237902696,-0.4868967092319002,-1.8852894624893455,-1.0318664282510939,-0.4868967092319002,-1.8447362806183674,-1.1062518928761327,-0.4868967092319002,-1.801112717263784,-1.1787961104005542,-0.4868967092319002,-1.7544913795416273,-1.2493783381336194,-0.4868967092319002,-1.7049498640675136,-1.3178810989229672,-0.4868967092319002,-1.6525706278047396,-1.384190376683744,-0.4868967092319002,-1.5974408508228006,-1.448195806167124,-0.4868967092319002,-1.5396522911947725,-1.5097908566523746,-0.4868967092319002,-1.4793011322750507,-1.5688730092567278,-0.4868967092319002,-1.4164878226116515,-1.6253439275679409,-0.4868967092319002,-1.3513169087595176,-1.6791096213155545,-0.4868967092319002,-1.2838968612730906,-1.7300806028084152,-0.4868967092319002,-1.2143398941677859,-1.7781720358781135,-0.4868967092319002,-1.1427617781508286,-1.8233038770804064,-0.4868967092319002,-1.0692816479323473,-1.8654010089196382,-0.4868967092319002,-0.9940218039373955,-1.9043933648743925,-0.4868967092319002,-0.9171075087489727,-1.9402160460163058,-0.4868967092319002,-0.838666778620812,-1.972809429027928,-0.4868967092319002,-0.7588301704069618,-2.00211926543985,-0.4868967092319002,-0.6777305642627856,-2.028096771921927,-0.4868967092319002,-0.5955029424790589,-2.050698711478319,-0.4868967092319002,-0.5122841648172626,-2.0698874654112,-0.4868967092319002,-0.42821274072002663,-2.0856310959333713,-0.4868967092319002,-0.34342859877581944,-2.0979033993255545,-0.4868967092319002,-0.2580728538216318,-2.1066839495498964,-0.4868967092319002,-0.1722875720712527,-2.111958132247094,-0.4868967092319002,-0.08621553466007773,-2.113717169060553,-0.4868967092319002,-2.588556965392811e-16,-2.111958132247094,-0.4868967092319002,0.08621553466007721,-2.1066839495498964,-0.4868967092319002,0.17228757207125217,-2.0979033993255545,-0.4868967092319002,0.2580728538216313,-2.0856310959333717,-0.4868967092319002,0.34342859877581805,-2.0698874654112003,-0.4868967092319002,0.42821274072002524,-2.050698711478319,-0.4868967092319002,0.512284164817263,-2.0280967719219274,-0.4868967092319002,0.5955029424790584,-2.00211926543985,-0.4868967092319002,0.6777305642627851,-1.9728094290279283,-0.4868967092319002,0.7588301704069612,-1.940216046016306,-0.4868967092319002,0.8386667786208115,-1.9043933648743931,-0.4868967092319002,0.9171075087489714,-1.8654010089196384,-0.4868967092319002,0.994021803937395,-1.8233038770804066,-0.4868967092319002,1.0692816479323468,-1.7781720358781132,-0.4868967092319002,1.1427617781508288,-1.730080602808416,-0.4868967092319002,1.2143398941677848,-1.679109621315555,-0.4868967092319002,1.28389686127309,-1.625343927567941,-0.4868967092319002,1.3513169087595172,-1.5688730092567276,-0.4868967092319002,1.4164878226116517,-1.5097908566523752,-0.4868967092319002,1.4793011322750504,-1.4481958061671243,-0.4868967092319002,1.5396522911947723,-1.384190376683745,-0.4868967092319002,1.5974408508227997,-1.3178810989229675,-0.4868967092319002,1.652570627804739,-1.249378338133619,-0.4868967092319002,1.7049498640675138,-1.1787961104005547,-0.4868967092319002,1.7544913795416268,-1.1062518928761331,-0.4868967092319002,1.8011127172637837,-1.0318664282510943,-0.4868967092319002,1.8447362806183671,-0.9557635237902684,-0.4868967092319002,1.8852894624893461,-0.8780698452676001,-0.4868967092319002,1.9227047661076548,-0.7989147061434643,-0.4868967092319002,1.9569199173929066,-0.7184298523351769,-0.4868967092319002,1.9878779686024528,-0.636749242938922,-0.4868967092319002,2.0155273931152777,-0.554008827268047,-0.4868967092319002,2.03982217119298,-0.47034631857885156,-0.4868967092319002,2.0607218665750797,-0.38590096486046727,-0.4868967092319002,2.078191693781187,-0.3008133170703189,-0.4868967092319002,2.092202576008,-0.2152249952009223,-0.4868967092319002,2.102731193524774,-0.12927845256738713,-0.4868967092319002,2.1097600224867143,-0.043116738707936184,-0.4868967092319002,2.1132773641016813,0.04311673870793729,-0.4868967092319002,2.1132773641016813,0.12927845256738635,-0.4868967092319002,2.1097600224867143,0.21522499520092153,-0.4868967092319002,2.1027311935247743,0.3008133170703181,-0.4868967092319002,2.092202576008,0.38590096486046654,-0.4868967092319002,2.078191693781187,0.4703463185788508,-0.4868967092319002,2.0607218665750797,0.5540088272680463,-0.4868967092319002,2.03982217119298,0.6367492429389213,-0.4868967092319002,2.015527393115278,0.7184298523351762,-0.4868967092319002,1.987877968602453,0.7989147061434618,-0.4868967092319002,1.9569199173929075,0.878069845267601,-0.4868967092319002,1.9227047661076544,0.9557635237902693,-0.4868967092319002,1.8852894624893455,1.0318664282510936,-0.4868967092319002,1.8447362806183676,1.1062518928761311,-0.4868967092319002,1.801112717263785,1.178796110400554,-0.4868967092319002,1.7544913795416273,1.2493783381336199,-0.4868967092319002,1.7049498640675131,1.317881098922967,-0.4868967092319002,1.6525706278047396,1.3841903766837445,-0.4868967092319002,1.5974408508228004,1.4481958061671236,-0.4868967092319002,1.5396522911947728,1.509790856652374,-0.4868967092319002,1.4793011322750516,1.5688730092567276,-0.4868967092319002,1.4164878226116517,1.6253439275679418,-0.4868967092319002,1.3513169087595163,1.6791096213155543,-0.4868967092319002,1.2838968612730908,1.7300806028084148,-0.4868967092319002,1.214339894167786,1.7781720358781123,-0.4868967092319002,1.1427617781508304,1.8233038770804064,-0.4868967092319002,1.0692816479323475,1.8654010089196387,-0.4868967092319002,0.9940218039373949,1.9043933648743925,-0.4868967092319002,0.917107508748973,1.9402160460163054,-0.4868967092319002,0.8386667786208132,1.972809429027928,-0.4868967092319002,0.7588301704069621,2.0021192654398496,-0.4868967092319002,0.6777305642627868,2.0280967719219274,-0.4868967092319002,0.5955029424790583,2.0506987114783186,-0.4868967092319002,0.5122841648172638,2.0698874654112003,-0.4868967092319002,0.428212740720026,2.0856310959333713,-0.4868967092319002,0.34342859877581966,2.0979033993255545,-0.4868967092319002,0.258072853821633,2.1066839495498964,-0.4868967092319002,0.17228757207125292,2.111958132247094,-0.4868967092319002,0.08621553466007705,2.113717169060553,-0.4868967092319002,0.0,2.147779089482842,-0.47766237104425774,0.0,2.145991706316883,-0.47766237104425774,-0.08760487223264479,2.140632531741891,-0.47766237104425774,-0.1750639348011101,2.131710485575053,-0.47766237104425774,-0.2622316207270126,2.1192404176817052,-0.47766237104425774,-0.34896284799963495,2.103243083259195,-0.47766237104425774,-0.43511326105061426,2.083745108291905,-0.47766237104425774,-0.5205394710195395,2.0607789452349294,-0.47766237104425774,-0.6050992944105589,2.034382819000166,-0.47766237104425774,-0.6886519897427764,2.004600663334719,-0.47766237104425774,-0.7710584918005574,1.9714820476975188,-0.47766237104425774,-0.8521816430938558,1.9350820947558447,-0.47766237104425774,-0.9318864221433197,1.895461388639087,-0.47766237104425774,-1.01004016821022,1.8526858741024428,-0.47766237104425774,-1.0865128020971557,1.8068267467683785,-0.47766237104425774,-1.1611770426520427,1.7579603346285424,-0.47766237104425774,-1.2339086186150288,1.706167971003356,-0.47766237104425774,-1.3045864754557428,1.6515358591707279,-0.47766237104425774,-1.3730929768566114,1.5941549288892065,-0.47766237104425774,-1.4393141005069026,1.5341206850543692,-0.47766237104425774,-1.5031396278816078,1.471533048740349,-0.47766237104425774,-1.5644633276893003,1.4064961908910698,-0.47766237104425774,-1.6231831326836341,1.3391183589379883,-0.47766237104425774,-1.6792013095442053,1.2695116966329283,-0.47766237104425774,-1.7324246215440169,1.1977920573958725,-0.47766237104425774,-1.7827644837328087,1.1240788114883802,-0.47766237104425774,-1.8301371103779676,1.0484946473335655,-0.47766237104425774,-1.8744636544176063,0.9711653673133269,-0.47766237104425774,-1.915670338693721,0.8922196783826946,-0.47766237104425774,-1.953688578746988,0.8117889778498101,-0.47766237104425774,-1.9884550969688266,0.7300071346780719,-0.47766237104425774,-2.019912027920735,0.6470102666744568,-0.47766237104425774,-2.0480070146456026,0.5629365139348655,-0.47766237104425774,-2.072693295810696,0.4779258089235646,-0.47766237104425774,-2.093929783537285,0.3921196435694098,-0.47766237104425774,-2.1116811317873596,0.30566083376649467,-0.47766237104425774,-2.1259177951936232,0.2186932816711874,-0.47766237104425774,-2.136616078234841,0.131361736191196,-0.47766237104425774,-2.1437581746746948,0.04381155206529447,-0.47766237104425774,-2.1473321971985087,-0.043811552065294204,-0.47766237104425774,-2.1473321971985087,-0.13136173619119573,-0.47766237104425774,-2.143758174674695,-0.21869328167118668,-0.47766237104425774,-2.136616078234841,-0.30566083376649444,-0.47766237104425774,-2.1259177951936232,-0.3921196435694096,-0.47766237104425774,-2.1116811317873596,-0.47792580892356396,-0.47766237104425774,-2.093929783537285,-0.5629365139348653,-0.47766237104425774,-2.0726932958106965,-0.6470102666744562,-0.47766237104425774,-2.048007014645603,-0.7300071346780717,-0.47766237104425774,-2.019912027920735,-0.8117889778498099,-0.47766237104425774,-1.9884550969688268,-0.892219678382694,-0.47766237104425774,-1.9536885787469882,-0.971165367313327,-0.47766237104425774,-1.915670338693721,-1.0484946473335655,-0.47766237104425774,-1.8744636544176063,-1.1240788114883806,-0.47766237104425774,-1.8301371103779671,-1.197792057395872,-0.47766237104425774,-1.7827644837328092,-1.2695116966329276,-0.47766237104425774,-1.732424621544017,-1.339118358937988,-0.47766237104425774,-1.6792013095442058,-1.40649619089107,-0.47766237104425774,-1.6231831326836341,-1.4715330487403493,-0.47766237104425774,-1.5644633276893003,-1.5341206850543692,-0.47766237104425774,-1.5031396278816078,-1.5941549288892065,-0.47766237104425774,-1.4393141005069026,-1.6515358591707274,-0.47766237104425774,-1.373092976856612,-1.706167971003356,-0.47766237104425774,-1.3045864754557426,-1.757960334628542,-0.47766237104425774,-1.2339086186150297,-1.8068267467683787,-0.47766237104425774,-1.1611770426520425,-1.8526858741024426,-0.47766237104425774,-1.086512802097156,-1.8954613886390868,-0.47766237104425774,-1.0100401682102202,-1.9350820947558445,-0.47766237104425774,-0.9318864221433202,-1.9714820476975186,-0.47766237104425774,-0.8521816430938559,-2.004600663334719,-0.47766237104425774,-0.7710584918005575,-2.034382819000166,-0.47766237104425774,-0.6886519897427764,-2.0607789452349294,-0.47766237104425774,-0.6050992944105597,-2.083745108291905,-0.47766237104425774,-0.5205394710195393,-2.1032430832591946,-0.47766237104425774,-0.43511326105061493,-2.1192404176817052,-0.47766237104425774,-0.34896284799963556,-2.131710485575053,-0.47766237104425774,-0.26223162072701317,-2.140632531741891,-0.47766237104425774,-0.17506393480111054,-2.145991706316883,-0.47766237104425774,-0.08760487223264514,-2.147779089482842,-0.47766237104425774,-2.630270787210779e-16,-2.145991706316883,-0.47766237104425774,0.08760487223264461,-2.140632531741891,-0.47766237104425774,0.17506393480111002,-2.131710485575053,-0.47766237104425774,0.26223162072701267,-2.1192404176817057,-0.47766237104425774,0.3489628479996341,-2.1032430832591955,-0.47766237104425774,0.4351132610506135,-2.083745108291905,-0.47766237104425774,0.5205394710195397,-2.0607789452349294,-0.47766237104425774,0.6050992944105592,-2.034382819000166,-0.47766237104425774,0.688651989742776,-2.004600663334719,-0.47766237104425774,0.7710584918005571,-1.9714820476975188,-0.47766237104425774,0.8521816430938555,-1.9350820947558451,-0.47766237104425774,0.9318864221433188,-1.895461388639087,-0.47766237104425774,1.0100401682102198,-1.8526858741024428,-0.47766237104425774,1.0865128020971555,-1.8068267467683785,-0.47766237104425774,1.1611770426520427,-1.7579603346285426,-0.47766237104425774,1.2339086186150283,-1.7061679710033564,-0.47766237104425774,1.3045864754557421,-1.6515358591707276,-0.47766237104425774,1.3730929768566116,-1.5941549288892063,-0.47766237104425774,1.4393141005069028,-1.5341206850543696,-0.47766237104425774,1.5031396278816076,-1.4715330487403495,-0.47766237104425774,1.5644633276893,-1.406496190891071,-0.47766237104425774,1.6231831326836332,-1.3391183589379883,-0.47766237104425774,1.6792013095442053,-1.2695116966329272,-0.47766237104425774,1.7324246215440173,-1.1977920573958725,-0.47766237104425774,1.7827644837328087,-1.124078811488381,-0.47766237104425774,1.830137110377967,-1.0484946473335657,-0.47766237104425774,1.874463654417606,-0.9711653673133258,-0.47766237104425774,1.9156703386937217,-0.8922196783826944,-0.47766237104425774,1.9536885787469882,-0.8117889778498107,-0.47766237104425774,1.9884550969688264,-0.7300071346780713,-0.47766237104425774,2.019912027920735,-0.647010266674457,-0.47766237104425774,2.0480070146456026,-0.5629365139348668,-0.47766237104425774,2.072693295810696,-0.47792580892356495,-0.47766237104425774,2.093929783537285,-0.39211964356940915,-0.47766237104425774,2.1116811317873596,-0.30566083376649444,-0.47766237104425774,2.1259177951936232,-0.21869328167118815,-0.47766237104425774,2.136616078234841,-0.13136173619119576,-0.47766237104425774,2.143758174674695,-0.0438115520652933,-0.47766237104425774,2.1473321971985087,0.04381155206529442,-0.47766237104425774,2.1473321971985087,0.13136173619119498,-0.47766237104425774,2.143758174674695,0.21869328167118737,-0.47766237104425774,2.136616078234841,0.30566083376649367,-0.47766237104425774,2.1259177951936237,0.39211964356940837,-0.47766237104425774,2.11168113178736,0.4779258089235642,-0.47766237104425774,2.093929783537285,0.562936513934866,-0.47766237104425774,2.072693295810696,0.6470102666744564,-0.47766237104425774,2.048007014645603,0.7300071346780707,-0.47766237104425774,2.0199120279207357,0.8117889778498083,-0.47766237104425774,1.9884550969688273,0.8922196783826954,-0.47766237104425774,1.9536885787469878,0.9711653673133268,-0.47766237104425774,1.915670338693721,1.0484946473335652,-0.47766237104425774,1.8744636544176065,1.1240788114883788,-0.47766237104425774,1.8301371103779682,1.1977920573958718,-0.47766237104425774,1.7827644837328092,1.2695116966329283,-0.47766237104425774,1.7324246215440167,1.3391183589379878,-0.47766237104425774,1.6792013095442058,1.4064961908910705,-0.47766237104425774,1.623183132683634,1.4715330487403488,-0.47766237104425774,1.5644633276893005,1.5341206850543683,-0.47766237104425774,1.5031396278816087,1.5941549288892063,-0.47766237104425774,1.4393141005069028,1.6515358591707283,-0.47766237104425774,1.3730929768566107,1.7061679710033557,-0.47766237104425774,1.3045864754557428,1.7579603346285417,-0.47766237104425774,1.2339086186150299,1.8068267467683774,-0.47766237104425774,1.161177042652044,1.8526858741024426,-0.47766237104425774,1.0865128020971562,1.8954613886390872,-0.47766237104425774,1.0100401682102196,1.9350820947558445,-0.47766237104425774,0.9318864221433204,1.9714820476975181,-0.47766237104425774,0.8521816430938571,2.004600663334719,-0.47766237104425774,0.7710584918005579,2.0343828190001654,-0.47766237104425774,0.6886519897427776,2.0607789452349294,-0.47766237104425774,0.6050992944105591,2.0837451082919047,-0.47766237104425774,0.5205394710195405,2.103243083259195,-0.47766237104425774,0.43511326105061426,2.1192404176817052,-0.47766237104425774,0.3489628479996358,2.131710485575053,-0.47766237104425774,0.2622316207270144,2.140632531741891,-0.47766237104425774,0.1750639348011108,2.145991706316883,-0.47766237104425774,0.08760487223264446,2.147779089482842,-0.47766237104425774,0.0,2.181104781914841,-0.4660483429512199,0.0,2.179289665085798,-0.4660483429512199,-0.08896418010646931,2.1738473356814985,-0.4660483429512199,-0.1777802881149521,2.164786851922137,-0.4660483429512199,-0.2663004983788559,2.1521232940887605,-0.4660483429512199,-0.3543774777441821,2.1358777394236252,-0.4660483429512199,-0.4418646307710208,2.116077227049211,-0.4660483429512199,-0.5286163437271928,2.0927547129642705,-0.4660483429512199,-0.6144882269479369,2.0659490151918174,-0.4660483429512199,-0.6993373551582568,2.035704749170362,-0.4660483429512199,-0.7830225053579357,2.0020722534959163,-0.4660483429512199,-0.8654043918732843,1.965107506138367,-0.4660483429512199,-0.9463458981843972,1.9248720312716707,-0.4660483429512199,-1.0257123051420696,1.8814327968729376,-0.4660483429512199,-1.1033715151945245,1.8348621032608428,-0.4660483429512199,-1.1791942722507525,1.7852374627588807,-0.4660483429512199,-1.2530543768145177,1.732641470683752,-0.4660483429512199,-1.324828896030967,1.6771616678736114,-0.4660483429512199,-1.3943983682962307,1.6188903949849813,-0.4660483429512199,-1.4616470020894772,1.557924638800841,-0.4660483429512199,-1.5264628686964692,1.4943658708056997,-0.4660483429512199,-1.5887380885038629,1.4283198782963233,-0.4660483429512199,-1.6483690105541782,1.3598965883092204,-0.4660483429512199,-1.7052563850625886,1.2892098846579365,-0.4660483429512199,-1.7593055286083927,1.2163774183846872,-0.4660483429512199,-1.8104264817262228,1.1415204119418088,-0.4660483429512199,-1.858534158634699,1.0647634574289506,-0.4660483429512199,-1.9035484888533132,0.98623430922182,-0.4660483429512199,-1.945394550471843,0.9060636713376302,-0.4660483429512199,-1.984002694850478,0.824384979891167,-0.4660483429512199,-2.0193086625431054,0.7413341810035411,-0.4660483429512199,-2.051253690250817,0.6570495045332864,-0.4660483429512199,-2.0797846086276213,0.5716712340064033,-0.4660483429512199,-2.104853930775573,0.4853414731282739,-0.4660483429512199,-2.1264199312820273,0.3982039092660675,-0.4660483429512199,-2.144446715667472,0.31040357429529986,-0.4660483429512199,-2.158904280128345,0.22208660320858695,-0.4660483429512199,-2.169768561475407,0.13339999088837498,-0.4660483429512199,-2.1770214771845424,0.04449134744846307,-0.4660483429512199,-2.1806509554933373,-0.04449134744846281,-0.4660483429512199,-2.1806509554933373,-0.1333999908883747,-0.4660483429512199,-2.1770214771845424,-0.22208660320858623,-0.4660483429512199,-2.169768561475407,-0.31040357429529963,-0.4660483429512199,-2.158904280128345,-0.3982039092660673,-0.4660483429512199,-2.144446715667472,-0.4853414731282732,-0.4660483429512199,-2.1264199312820273,-0.5716712340064031,-0.4660483429512199,-2.1048539307755734,-0.6570495045332856,-0.4660483429512199,-2.0797846086276217,-0.7413341810035409,-0.4660483429512199,-2.051253690250817,-0.8243849798911667,-0.4660483429512199,-2.019308662543106,-0.9060636713376297,-0.4660483429512199,-1.9840026948504785,-0.9862343092218202,-0.4660483429512199,-1.945394550471843,-1.0647634574289506,-0.4660483429512199,-1.9035484888533132,-1.1415204119418092,-0.4660483429512199,-1.8585341586346986,-1.2163774183846865,-0.4660483429512199,-1.8104264817262232,-1.2892098846579358,-0.4660483429512199,-1.759305528608393,-1.3598965883092202,-0.4660483429512199,-1.705256385062589,-1.4283198782963236,-0.4660483429512199,-1.6483690105541782,-1.4943658708057,-0.4660483429512199,-1.5887380885038629,-1.557924638800841,-0.4660483429512199,-1.5264628686964692,-1.6188903949849813,-0.4660483429512199,-1.4616470020894772,-1.677161667873611,-0.4660483429512199,-1.3943983682962315,-1.732641470683752,-0.4660483429512199,-1.3248288960309667,-1.7852374627588803,-0.4660483429512199,-1.2530543768145186,-1.8348621032608432,-0.4660483429512199,-1.179194272250752,-1.8814327968729374,-0.4660483429512199,-1.1033715151945247,-1.9248720312716705,-0.4660483429512199,-1.0257123051420698,-1.9651075061383667,-0.4660483429512199,-0.9463458981843975,-2.002072253495916,-0.4660483429512199,-0.8654043918732844,-2.035704749170362,-0.4660483429512199,-0.7830225053579358,-2.0659490151918174,-0.4660483429512199,-0.6993373551582568,-2.09275471296427,-0.4660483429512199,-0.6144882269479378,-2.116077227049211,-0.4660483429512199,-0.5286163437271927,-2.135877739423625,-0.4660483429512199,-0.44186463077102145,-2.1521232940887605,-0.4660483429512199,-0.3543774777441827,-2.164786851922137,-0.4660483429512199,-0.26630049837885644,-2.1738473356814985,-0.4660483429512199,-0.17778028811495258,-2.179289665085798,-0.4660483429512199,-0.08896418010646966,-2.181104781914841,-0.4660483429512199,-2.6710829897769955e-16,-2.179289665085798,-0.4660483429512199,0.08896418010646913,-2.1738473356814985,-0.4660483429512199,0.17778028811495203,-2.164786851922137,-0.4660483429512199,0.26630049837885594,-2.1521232940887605,-0.4660483429512199,0.35437747774418127,-2.1358777394236252,-0.4660483429512199,0.44186463077102,-2.116077227049211,-0.4660483429512199,0.5286163437271931,-2.0927547129642705,-0.4660483429512199,0.6144882269479374,-2.0659490151918174,-0.4660483429512199,0.6993373551582562,-2.035704749170362,-0.4660483429512199,0.7830225053579353,-2.0020722534959163,-0.4660483429512199,0.865404391873284,-1.9651075061383674,-0.4660483429512199,0.9463458981843962,-1.9248720312716707,-0.4660483429512199,1.0257123051420693,-1.8814327968729376,-0.4660483429512199,1.1033715151945243,-1.8348621032608428,-0.4660483429512199,1.1791942722507525,-1.785237462758881,-0.4660483429512199,1.2530543768145173,-1.7326414706837525,-0.4660483429512199,1.3248288960309662,-1.6771616678736112,-0.4660483429512199,1.394398368296231,-1.618890394984981,-0.4660483429512199,1.4616470020894776,-1.5579246388008416,-0.4660483429512199,1.526462868696469,-1.4943658708057002,-0.4660483429512199,1.5887380885038627,-1.4283198782963247,-0.4660483429512199,1.6483690105541773,-1.3598965883092204,-0.4660483429512199,1.7052563850625886,-1.2892098846579354,-0.4660483429512199,1.7593055286083932,-1.2163774183846872,-0.4660483429512199,1.8104264817262228,-1.1415204119418099,-0.4660483429512199,1.8585341586346984,-1.0647634574289508,-0.4660483429512199,1.903548488853313,-0.986234309221819,-0.4660483429512199,1.9453945504718437,-0.9060636713376301,-0.4660483429512199,1.9840026948504785,-0.8243849798911677,-0.4660483429512199,2.0193086625431054,-0.7413341810035405,-0.4660483429512199,2.051253690250817,-0.6570495045332866,-0.4660483429512199,2.0797846086276213,-0.5716712340064045,-0.4660483429512199,2.104853930775573,-0.48534147312827425,-0.4660483429512199,2.1264199312820273,-0.39820390926606686,-0.4660483429512199,2.144446715667472,-0.31040357429529963,-0.4660483429512199,2.158904280128345,-0.2220866032085877,-0.4660483429512199,2.169768561475407,-0.13339999088837473,-0.4660483429512199,2.1770214771845424,-0.04449134744846189,-0.4660483429512199,2.1806509554933373,0.04449134744846303,-0.4660483429512199,2.1806509554933373,0.13339999088837393,-0.4660483429512199,2.1770214771845424,0.22208660320858692,-0.4660483429512199,2.169768561475407,0.3104035742952988,-0.4660483429512199,2.158904280128345,0.3982039092660661,-0.4660483429512199,2.1444467156674722,0.4853414731282734,-0.4660483429512199,2.1264199312820273,0.5716712340064039,-0.4660483429512199,2.104853930775573,0.6570495045332859,-0.4660483429512199,2.0797846086276217,0.7413341810035398,-0.4660483429512199,2.0512536902508174,0.8243849798911651,-0.4660483429512199,2.0193086625431063,0.9060636713376311,-0.4660483429512199,1.9840026948504779,0.9862343092218199,-0.4660483429512199,1.945394550471843,1.0647634574289504,-0.4660483429512199,1.9035484888533134,1.1415204119418076,-0.4660483429512199,1.8585341586347,1.2163774183846863,-0.4660483429512199,1.8104264817262232,1.2892098846579365,-0.4660483429512199,1.7593055286083925,1.35989658830922,-0.4660483429512199,1.705256385062589,1.4283198782963242,-0.4660483429512199,1.648369010554178,1.4943658708056995,-0.4660483429512199,1.588738088503863,1.5579246388008403,-0.4660483429512199,1.52646286869647,1.618890394984981,-0.4660483429512199,1.4616470020894776,1.677161667873612,-0.4660483429512199,1.39439836829623,1.7326414706837518,-0.4660483429512199,1.324828896030967,1.78523746275888,-0.4660483429512199,1.2530543768145188,1.8348621032608419,-0.4660483429512199,1.1791942722507538,1.8814327968729374,-0.4660483429512199,1.103371515194525,1.924872031271671,-0.4660483429512199,1.0257123051420691,1.9651075061383667,-0.4660483429512199,0.9463458981843977,2.0020722534959154,-0.4660483429512199,0.8654043918732857,2.035704749170362,-0.4660483429512199,0.7830225053579362,2.065949015191817,-0.4660483429512199,0.6993373551582579,2.0927547129642705,-0.4660483429512199,0.6144882269479371,2.116077227049211,-0.4660483429512199,0.5286163437271939,2.1358777394236252,-0.4660483429512199,0.4418646307710208,2.1521232940887605,-0.4660483429512199,0.354377477744183,2.164786851922137,-0.4660483429512199,0.26630049837885766,2.1738473356814985,-0.4660483429512199,0.1777802881149528,2.179289665085798,-0.4660483429512199,0.08896418010646896,2.181104781914841,-0.4660483429512199,0.0,2.213528219433188,-0.45211248545654253,0.0,2.211686119798191,-0.45211248545654253,-0.09028668627809885,2.2061628868862484,-0.45211248545654253,-0.18042309927720554,2.1969677135734584,-0.45211248545654253,-0.2702592158333705,2.1841159043183365,-0.45211248545654253,-0.35964551259643734,2.167628849689053,-0.45211248545654253,-0.448433214896903,2.147533990760945,-0.45211248545654253,-0.5364745443666726,2.123864773443553,-0.45211248545654253,-0.6236229649015692,2.0966605928132074,-0.45211248545654253,-0.7097334265562173,2.0659667275438083,-0.45211248545654253,-0.7946626069653622,2.031834264544946,-0.45211248545654253,-0.8782691498898032,1.9943200139327768,-0.45211248545654253,-0.960413900489902,1.953486414475195,-0.45211248545654253,-1.0409601369350823,1.9094014296686637,-0.45211248545654253,-1.1197737979638218,1.8621384346196834,-0.45211248545654253,-1.196723706015392,1.8117760939191672,-0.45211248545654253,-1.2716817855619638,1.7583982307129935,-0.45211248545654253,-1.3445232762776826,1.7020936871866523,-0.45211248545654253,-1.4151269406899174,1.6429561766961953,-0.45211248545654253,-1.4833752659670711,1.581084127791602,-0.45211248545654253,-1.5491546595070902,1.5165805203921727,-0.45211248545654253,-1.6123556380011406,1.4495527143866171,-0.45211248545654253,-1.6728730096577709,1.3801122709431137,-0.45211248545654253,-1.7306060492842674,1.3083747668267538,-0.45211248545654253,-1.7854586659337979,1.2344596020334246,-0.45211248545654253,-1.8373395628393074,1.158489801060299,-0.45211248545654253,-1.8861623893679802,1.0805918081437007,-0.45211248545654253,-1.9318458847433422,1.000895276805152,-0.45211248545654253,-1.9743140132958072,0.9195328540558803,-0.45211248545654253,-2.013496091016545,0.8366399596189628,-0.45211248545654253,-2.0493269032040344,0.7523545605365597,-0.45211248545654253,-2.081746813007504,0.666816941537387,-0.45211248545654253,-2.110701860686578,0.5801694715466329,-0.45211248545654253,-2.1361438534219457,0.4925563667269307,-0.45211248545654253,-2.1580304455275474,0.40412345044478837,-0.45211248545654253,-2.1763252089307925,0.31501791056198736,-0.45211248545654253,-2.1909976938034914,0.22538805445591023,-0.45211248545654253,-2.2020234792425892,0.13538306217655033,-0.45211248545654253,-2.2093842139163478,0.04515273815103897,-0.45211248545654253,-2.213067646608328,-0.04515273815103871,-0.45211248545654253,-2.213067646608328,-0.13538306217655005,-0.45211248545654253,-2.209384213916348,-0.22538805445590948,-0.45211248545654253,-2.2020234792425892,-0.31501791056198714,-0.45211248545654253,-2.1909976938034914,-0.40412345044478815,-0.45211248545654253,-2.1763252089307925,-0.49255636672693,-0.45211248545654253,-2.1580304455275474,-0.5801694715466327,-0.45211248545654253,-2.136143853421946,-0.6668169415373862,-0.45211248545654253,-2.1107018606865786,-0.7523545605365595,-0.45211248545654253,-2.081746813007504,-0.8366399596189625,-0.45211248545654253,-2.049326903204035,-0.9195328540558796,-0.45211248545654253,-2.013496091016545,-1.000895276805152,-0.45211248545654253,-1.9743140132958072,-1.0805918081437007,-0.45211248545654253,-1.9318458847433422,-1.1584898010602998,-0.45211248545654253,-1.8861623893679795,-1.2344596020334242,-0.45211248545654253,-1.8373395628393079,-1.3083747668267534,-0.45211248545654253,-1.785458665933798,-1.3801122709431133,-0.45211248545654253,-1.7306060492842679,-1.4495527143866174,-0.45211248545654253,-1.6728730096577709,-1.516580520392173,-0.45211248545654253,-1.6123556380011406,-1.581084127791602,-0.45211248545654253,-1.5491546595070902,-1.6429561766961953,-0.45211248545654253,-1.4833752659670711,-1.7020936871866519,-0.45211248545654253,-1.415126940689918,-1.7583982307129935,-0.45211248545654253,-1.3445232762776824,-1.8117760939191665,-0.45211248545654253,-1.2716817855619644,-1.8621384346196836,-0.45211248545654253,-1.1967237060153917,-1.9094014296686634,-0.45211248545654253,-1.119773797963822,-1.9534864144751947,-0.45211248545654253,-1.0409601369350827,-1.9943200139327766,-0.45211248545654253,-0.9604139004899024,-2.031834264544946,-0.45211248545654253,-0.8782691498898033,-2.0659667275438083,-0.45211248545654253,-0.7946626069653623,-2.0966605928132074,-0.45211248545654253,-0.7097334265562173,-2.123864773443553,-0.45211248545654253,-0.6236229649015701,-2.147533990760945,-0.45211248545654253,-0.5364745443666724,-2.1676288496890526,-0.45211248545654253,-0.44843321489690374,-2.1841159043183365,-0.45211248545654253,-0.35964551259643796,-2.1969677135734584,-0.45211248545654253,-0.27025921583337104,-2.2061628868862484,-0.45211248545654253,-0.180423099277206,-2.211686119798191,-0.45211248545654253,-0.0902866862780992,-2.213528219433188,-0.45211248545654253,-2.7107902487511936e-16,-2.211686119798191,-0.45211248545654253,0.09028668627809867,-2.2061628868862484,-0.45211248545654253,0.18042309927720546,-2.1969677135734584,-0.45211248545654253,0.2702592158333705,-2.1841159043183365,-0.45211248545654253,0.35964551259643646,-2.167628849689053,-0.45211248545654253,0.44843321489690224,-2.147533990760945,-0.45211248545654253,0.5364745443666729,-2.123864773443553,-0.45211248545654253,0.6236229649015695,-2.0966605928132074,-0.45211248545654253,0.7097334265562167,-2.0659667275438087,-0.45211248545654253,0.7946626069653618,-2.031834264544946,-0.45211248545654253,0.8782691498898028,-1.9943200139327772,-0.45211248545654253,0.960413900489901,-1.953486414475195,-0.45211248545654253,1.040960136935082,-1.9094014296686637,-0.45211248545654253,1.1197737979638214,-1.8621384346196834,-0.45211248545654253,1.196723706015392,-1.8117760939191674,-0.45211248545654253,1.2716817855619633,-1.758398230712994,-0.45211248545654253,1.344523276277682,-1.702093687186652,-0.45211248545654253,1.4151269406899176,-1.642956176696195,-0.45211248545654253,1.4833752659670714,-1.5810841277916023,-0.45211248545654253,1.5491546595070897,-1.5165805203921732,-0.45211248545654253,1.6123556380011403,-1.4495527143866185,-0.45211248545654253,1.67287300965777,-1.3801122709431137,-0.45211248545654253,1.7306060492842674,-1.3083747668267527,-0.45211248545654253,1.7854586659337983,-1.2344596020334246,-0.45211248545654253,1.8373395628393074,-1.1584898010603002,-0.45211248545654253,1.8861623893679793,-1.080591808143701,-0.45211248545654253,1.9318458847433417,-1.000895276805151,-0.45211248545654253,1.974314013295808,-0.9195328540558801,-0.45211248545654253,2.013496091016545,-0.8366399596189635,-0.45211248545654253,2.0493269032040344,-0.7523545605365591,-0.45211248545654253,2.081746813007504,-0.6668169415373872,-0.45211248545654253,2.110701860686578,-0.5801694715466341,-0.45211248545654253,2.1361438534219452,-0.492556366726931,-0.45211248545654253,2.1580304455275474,-0.4041234504447877,-0.45211248545654253,2.1763252089307925,-0.31501791056198714,-0.45211248545654253,2.1909976938034914,-0.225388054455911,-0.45211248545654253,2.2020234792425892,-0.13538306217655008,-0.45211248545654253,2.209384213916348,-0.04515273815103778,-0.45211248545654253,2.213067646608328,0.04515273815103893,-0.45211248545654253,2.213067646608328,0.13538306217654927,-0.45211248545654253,2.209384213916348,0.2253880544559102,-0.45211248545654253,2.2020234792425892,0.31501791056198636,-0.45211248545654253,2.190997693803492,0.4041234504447869,-0.45211248545654253,2.176325208930793,0.4925563667269302,-0.45211248545654253,2.1580304455275474,0.5801694715466335,-0.45211248545654253,2.1361438534219457,0.6668169415373865,-0.45211248545654253,2.1107018606865786,0.7523545605365584,-0.45211248545654253,2.0817468130075043,0.836639959618961,-0.45211248545654253,2.0493269032040353,0.9195328540558811,-0.45211248545654253,2.0134960910165445,1.0008952768051518,-0.45211248545654253,1.9743140132958072,1.0805918081437005,-0.45211248545654253,1.9318458847433424,1.158489801060298,-0.45211248545654253,1.8861623893679809,1.234459602033424,-0.45211248545654253,1.8373395628393079,1.3083747668267538,-0.45211248545654253,1.7854586659337974,1.380112270943113,-0.45211248545654253,1.7306060492842679,1.449552714386618,-0.45211248545654253,1.6728730096577706,1.5165805203921725,-0.45211248545654253,1.6123556380011408,1.581084127791601,-0.45211248545654253,1.549154659507091,1.642956176696195,-0.45211248545654253,1.4833752659670714,1.7020936871866528,-0.45211248545654253,1.4151269406899165,1.7583982307129933,-0.45211248545654253,1.3445232762776826,1.8117760939191663,-0.45211248545654253,1.2716817855619647,1.8621384346196823,-0.45211248545654253,1.1967237060153935,1.9094014296686634,-0.45211248545654253,1.1197737979638223,1.9534864144751953,-0.45211248545654253,1.040960136935082,1.9943200139327766,-0.45211248545654253,0.9604139004899027,2.0318342645449454,-0.45211248545654253,0.8782691498898045,2.0659667275438083,-0.45211248545654253,0.7946626069653626,2.096660592813207,-0.45211248545654253,0.7097334265562185,2.123864773443553,-0.45211248545654253,0.6236229649015694,2.1475339907609445,-0.45211248545654253,0.5364745443666737,2.167628849689053,-0.45211248545654253,0.448433214896903,2.1841159043183365,-0.45211248545654253,0.3596455125964382,2.1969677135734584,-0.45211248545654253,0.27025921583337226,2.2061628868862484,-0.45211248545654253,0.18042309927720626,2.211686119798191,-0.45211248545654253,0.0902866862780985,2.213528219433188,-0.45211248545654253,0.0,2.2448878701031143,-0.43592422630126937,0.0,2.2430196729462764,-0.43592422630126937,-0.09156580208831042,2.2374181909055597,-0.43592422630126937,-0.18297920193559744,2.228092747095016,-0.43592422630126937,-0.2740880509593274,2.2150588627955465,-0.43592422630126937,-0.36474070747175674,2.198338231621265,-0.43592422630126937,-0.4547862890725555,2.1779586834126166,-0.43592422630126937,-0.5440749237776724,2.1539541379163527,-0.43592422630126937,-0.6324579994664616,2.126364548329464,-0.43592422630126937,-0.7197884112318914,2.095235834801025,-0.43592422630126937,-0.8059208062221433,2.060619808002636,-0.43592422630126937,-0.8907118255660907,2.0225740828946757,-0.43592422630126937,-0.9740203429799902,1.9811619828318867,-0.43592422630126937,-1.0557076996582548,1.9364524341678973,-0.43592422630126937,-1.1356379350573502,1.8885198515341106,-0.43592422630126937,-1.213678013188703,1.8374440139838943,-0.43592422630126937,-1.2896980440439738,1.7833099322082184,-0.43592422630126937,-1.3635714997841577,1.7262077070437514,-0.43592422630126937,-1.4351754253326843,1.6662323795089085,-0.43592422630126937,-1.5043906430220102,1.6034837726174564,-0.43592422630126937,-1.5711019509530837,1.5380663252329587,-0.43592422630126937,-1.6351983147375364,1.470088918240593,-0.43592422630126937,-1.6965730523034661,1.3996646933256651,-0.43592422630126937,-1.7551240114572155,1.3269108646604375,-0.43592422630126937,-1.8107537399056193,1.2519485238127086,-0.43592422630126937,-1.863369647455726,1.1749024382008486,-0.43592422630126937,-1.912884160122039,1.095900843430745,-0.43592422630126937,-1.9592148658847703,1.0150752298602914,-0.43592422630126937,-2.002284651856517,0.9325601237466624,-0.43592422630126937,-2.0420218326290507,0.8484928633406377,-0.43592422630126937,-2.078360269586604,0.7630133703006359,-0.43592422630126937,-2.1112394809870723,0.6762639168069211,-0.43592422630126937,-2.1406047426279002,0.588388888763603,-0.43592422630126937,-2.1664071789291204,0.4995345454825465,-0.43592422630126937,-2.18860384428193,0.4098487762491818,-0.43592422630126937,-2.2071577945274203,0.3194808541753847,-0.43592422630126937,-2.2220381484464804,0.22858118774911107,-0.43592422630126937,-2.233220139158543,0.13730107049431584,-0.43592422630126937,-2.2406851553436087,0.04579242915781088,-0.43592422630126937,-2.2444207722189535,-0.04579242915781061,-0.43592422630126937,-2.2444207722189535,-0.13730107049431556,-0.43592422630126937,-2.2406851553436087,-0.22858118774911035,-0.43592422630126937,-2.233220139158543,-0.31948085417538447,-0.43592422630126937,-2.2220381484464804,-0.4098487762491816,-0.43592422630126937,-2.2071577945274203,-0.4995345454825458,-0.43592422630126937,-2.18860384428193,-0.5883888887636027,-0.43592422630126937,-2.1664071789291204,-0.6762639168069203,-0.43592422630126937,-2.1406047426279002,-0.7630133703006357,-0.43592422630126937,-2.1112394809870723,-0.8484928633406374,-0.43592422630126937,-2.0783602695866046,-0.9325601237466618,-0.43592422630126937,-2.0420218326290507,-1.0150752298602916,-0.43592422630126937,-2.002284651856517,-1.095900843430745,-0.43592422630126937,-1.9592148658847703,-1.1749024382008493,-0.43592422630126937,-1.9128841601220385,-1.2519485238127082,-0.43592422630126937,-1.8633696474557264,-1.326910864660437,-0.43592422630126937,-1.8107537399056195,-1.399664693325665,-0.43592422630126937,-1.755124011457216,-1.4700889182405932,-0.43592422630126937,-1.6965730523034661,-1.538066325232959,-0.43592422630126937,-1.6351983147375364,-1.6034837726174564,-0.43592422630126937,-1.5711019509530837,-1.6662323795089085,-0.43592422630126937,-1.5043906430220102,-1.7262077070437507,-0.43592422630126937,-1.4351754253326852,-1.7833099322082184,-0.43592422630126937,-1.3635714997841575,-1.837444013983894,-0.43592422630126937,-1.2896980440439745,-1.888519851534111,-0.43592422630126937,-1.2136780131887028,-1.936452434167897,-0.43592422630126937,-1.1356379350573504,-1.9811619828318865,-0.43592422630126937,-1.055707699658255,-2.0225740828946757,-0.43592422630126937,-0.9740203429799906,-2.0606198080026354,-0.43592422630126937,-0.8907118255660909,-2.095235834801025,-0.43592422630126937,-0.8059208062221435,-2.126364548329464,-0.43592422630126937,-0.7197884112318914,-2.1539541379163523,-0.43592422630126937,-0.6324579994664625,-2.1779586834126166,-0.43592422630126937,-0.5440749237776722,-2.198338231621265,-0.43592422630126937,-0.4547862890725562,-2.2150588627955465,-0.43592422630126937,-0.3647407074717574,-2.228092747095016,-0.43592422630126937,-0.274088050959328,-2.2374181909055597,-0.43592422630126937,-0.1829792019355979,-2.2430196729462764,-0.43592422630126937,-0.09156580208831076,-2.2448878701031143,-0.43592422630126937,-2.749194744566498e-16,-2.2430196729462764,-0.43592422630126937,0.09156580208831022,-2.2374181909055597,-0.43592422630126937,0.18297920193559733,-2.228092747095016,-0.43592422630126937,0.2740880509593275,-2.2150588627955465,-0.43592422630126937,0.3647407074717559,-2.1983382316212654,-0.43592422630126937,0.4547862890725547,-2.1779586834126166,-0.43592422630126937,0.5440749237776726,-2.1539541379163527,-0.43592422630126937,0.632457999466462,-2.126364548329464,-0.43592422630126937,0.7197884112318909,-2.095235834801025,-0.43592422630126937,0.805920806222143,-2.060619808002636,-0.43592422630126937,0.8907118255660904,-2.0225740828946766,-0.43592422630126937,0.9740203429799892,-1.9811619828318867,-0.43592422630126937,1.0557076996582546,-1.9364524341678973,-0.43592422630126937,1.13563793505735,-1.8885198515341106,-0.43592422630126937,1.213678013188703,-1.8374440139838946,-0.43592422630126937,1.2896980440439731,-1.7833099322082189,-0.43592422630126937,1.363571499784157,-1.726207707043751,-0.43592422630126937,1.4351754253326845,-1.6662323795089082,-0.43592422630126937,1.5043906430220106,-1.6034837726174571,-0.43592422630126937,1.5711019509530835,-1.5380663252329592,-0.43592422630126937,1.6351983147375362,-1.4700889182405943,-0.43592422630126937,1.696573052303465,-1.3996646933256651,-0.43592422630126937,1.7551240114572155,-1.3269108646604366,-0.43592422630126937,1.8107537399056197,-1.2519485238127086,-0.43592422630126937,1.863369647455726,-1.1749024382008497,-0.43592422630126937,1.9128841601220383,-1.0959008434307451,-0.43592422630126937,1.9592148658847701,-1.0150752298602903,-0.43592422630126937,2.0022846518565176,-0.9325601237466622,-0.43592422630126937,2.0420218326290507,-0.8484928633406384,-0.43592422630126937,2.078360269586604,-0.7630133703006352,-0.43592422630126937,2.1112394809870723,-0.6762639168069213,-0.43592422630126937,2.1406047426279002,-0.5883888887636043,-0.43592422630126937,2.16640717892912,-0.49953454548254683,-0.43592422630126937,2.18860384428193,-0.40984877624918115,-0.43592422630126937,2.2071577945274203,-0.31948085417538447,-0.43592422630126937,2.2220381484464804,-0.22858118774911187,-0.43592422630126937,2.233220139158543,-0.1373010704943156,-0.43592422630126937,2.2406851553436087,-0.045792429157809664,-0.43592422630126937,2.2444207722189535,0.045792429157810836,-0.43592422630126937,2.2444207722189535,0.13730107049431478,-0.43592422630126937,2.2406851553436087,0.22858118774911104,-0.43592422630126937,2.233220139158543,0.31948085417538363,-0.43592422630126937,2.222038148446481,0.4098487762491803,-0.43592422630126937,2.2071577945274203,0.499534545482546,-0.43592422630126937,2.18860384428193,0.5883888887636035,-0.43592422630126937,2.1664071789291204,0.6762639168069207,-0.43592422630126937,2.1406047426279002,0.7630133703006345,-0.43592422630126937,2.1112394809870727,0.8484928633406359,-0.43592422630126937,2.078360269586605,0.9325601237466632,-0.43592422630126937,2.0420218326290502,1.0150752298602912,-0.43592422630126937,2.002284651856517,1.0959008434307447,-0.43592422630126937,1.9592148658847706,1.1749024382008475,-0.43592422630126937,1.9128841601220397,1.251948523812708,-0.43592422630126937,1.8633696474557264,1.3269108646604375,-0.43592422630126937,1.810753739905619,1.3996646933256647,-0.43592422630126937,1.755124011457216,1.4700889182405938,-0.43592422630126937,1.696573052303466,1.5380663252329585,-0.43592422630126937,1.6351983147375369,1.6034837726174558,-0.43592422630126937,1.5711019509530846,1.6662323795089082,-0.43592422630126937,1.5043906430220106,1.7262077070437518,-0.43592422630126937,1.4351754253326836,1.7833099322082182,-0.43592422630126937,1.3635714997841577,1.8374440139838937,-0.43592422630126937,1.2896980440439747,1.8885198515341097,-0.43592422630126937,1.2136780131887046,1.936452434167897,-0.43592422630126937,1.135637935057351,1.981161982831887,-0.43592422630126937,1.0557076996582544,2.0225740828946757,-0.43592422630126937,0.9740203429799908,2.060619808002635,-0.43592422630126937,0.8907118255660922,2.095235834801025,-0.43592422630126937,0.8059208062221438,2.1263645483294638,-0.43592422630126937,0.7197884112318926,2.1539541379163527,-0.43592422630126937,0.6324579994664619,2.177958683412616,-0.43592422630126937,0.5440749237776734,2.198338231621265,-0.43592422630126937,0.4547862890725555,2.2150588627955465,-0.43592422630126937,0.36474070747175763,2.228092747095016,-0.43592422630126937,0.27408805095932925,2.2374181909055597,-0.43592422630126937,0.18297920193559816,2.2430196729462764,-0.43592422630126937,0.09156580208831006,2.2448878701031143,-0.43592422630126937,0.0,2.2750275017224206,-0.4175642145782178,0.0,2.273134222344395,-0.4175642145782178,-0.09279515504647898,2.2674575353869977,-0.4175642145782178,-0.1854358617152595,2.258006889135441,-0.4175642145782178,-0.2777679286927255,2.2447980132577343,-0.4175642145782178,-0.3696376783655676,2.227852892624204,-0.4175642145782178,-0.46089220260200436,2.2071997307158435,-0.4175642145782178,-0.5513796172522769,2.1828729026824063,-0.4175642145782178,-0.6409493149448277,2.1549128981283605,-0.4175642145782178,-0.7294522157574058,2.1233662537219358,-0.4175642145782178,-0.8167410153458864,2.088285475739436,-0.4175642145782178,-0.9026704301178133,2.049728952673718,-0.4175642145782178,-0.9870974390426006,2.007760858052316,-0.4175642145782178,-1.0698815216959245,1.9624510436269296,-0.4175642145782178,-1.1508848921421004,1.9138749231120826,-0.4175642145782178,-1.2299727282651753,1.862113346666435,-0.4175642145782178,-1.3070133961670354,1.807252466325674,-0.4175642145782178,-1.3818786692590375,1.7493835926109518,-0.4175642145782178,-1.4544439416825106,1.6886030425515361,-0.4175642145782178,-1.5245884357029125,1.625011979374619,-0.4175642145782178,-1.5921954027324476,1.5587162441291094,-0.4175642145782178,-1.657152317646571,1.4898261795236525,-0.4175642145782178,-1.719351066070951,1.4184564462720817,-0.4175642145782178,-1.7786881243271782,1.3447258322519744,-0.4175642145782178,-1.8350647317377102,1.2687570547939537,-0.4175642145782178,-1.888387055003272,1.1906765564308037,-0.4175642145782178,-1.9385663443791246,1.1106142944463506,-0.4175642145782178,-1.9855190813902528,1.0287035245743907,-0.4175642145782178,-2.0291671178396284,0.9450805792076685,-0.4175642145782178,-2.06943780587817,0.8598846404860693,-0.4175642145782178,-2.1062641189199187,0.7732575086416793,-0.4175642145782178,-2.1395847632011735,0.6853433659862931,-0.4175642145782178,-2.1693442797979157,0.5962885369341878,-0.4175642145782178,-2.1954931369317108,0.5062412444595739,-0.4175642145782178,-2.2179878124104695,0.41535136339408296,-0.4175642145782178,-2.236790866066838,0.3237701709749027,-0.4175642145782178,-2.2518710020736625,0.2316500950587422,-0.4175642145782178,-2.2632031210328063,0.13914446042071116,-0.4175642145782178,-2.2707683617506182,0.046407233560360486,-0.4175642145782178,-2.274554132630532,-0.04640723356036021,-0.4175642145782178,-2.274554132630532,-0.13914446042071085,-0.4175642145782178,-2.2707683617506187,-0.23165009505874146,-0.4175642145782178,-2.2632031210328063,-0.3237701709749024,-0.4175642145782178,-2.2518710020736625,-0.4153513633940827,-0.4175642145782178,-2.236790866066838,-0.5062412444595731,-0.4175642145782178,-2.2179878124104695,-0.5962885369341875,-0.4175642145782178,-2.195493136931711,-0.6853433659862923,-0.4175642145782178,-2.1693442797979157,-0.773257508641679,-0.4175642145782178,-2.1395847632011735,-0.859884640486069,-0.4175642145782178,-2.106264118919919,-0.945080579207668,-0.4175642145782178,-2.0694378058781706,-1.0287035245743907,-0.4175642145782178,-2.0291671178396284,-1.1106142944463506,-0.4175642145782178,-1.9855190813902528,-1.1906765564308042,-0.4175642145782178,-1.938566344379124,-1.2687570547939533,-0.4175642145782178,-1.8883870550032724,-1.3447258322519737,-0.4175642145782178,-1.8350647317377105,-1.4184564462720815,-0.4175642145782178,-1.7786881243271786,-1.4898261795236527,-0.4175642145782178,-1.719351066070951,-1.5587162441291096,-0.4175642145782178,-1.657152317646571,-1.625011979374619,-0.4175642145782178,-1.5921954027324476,-1.6886030425515361,-0.4175642145782178,-1.5245884357029125,-1.749383592610951,-0.4175642145782178,-1.4544439416825112,-1.807252466325674,-0.4175642145782178,-1.3818786692590372,-1.8621133466664346,-0.4175642145782178,-1.307013396167036,-1.9138749231120828,-0.4175642145782178,-1.2299727282651751,-1.9624510436269291,-0.4175642145782178,-1.1508848921421007,-2.0077608580523156,-0.4175642145782178,-1.0698815216959248,-2.049728952673718,-0.4175642145782178,-0.987097439042601,-2.0882854757394353,-0.4175642145782178,-0.9026704301178134,-2.1233662537219358,-0.4175642145782178,-0.8167410153458865,-2.1549128981283605,-0.4175642145782178,-0.7294522157574058,-2.1828729026824063,-0.4175642145782178,-0.6409493149448285,-2.2071997307158435,-0.4175642145782178,-0.5513796172522768,-2.2278528926242034,-0.4175642145782178,-0.46089220260200503,-2.2447980132577343,-0.4175642145782178,-0.3696376783655682,-2.258006889135441,-0.4175642145782178,-0.27776792869272604,-2.2674575353869977,-0.4175642145782178,-0.18543586171525997,-2.273134222344395,-0.4175642145782178,-0.09279515504647934,-2.2750275017224206,-0.4175642145782178,-2.786105147956562e-16,-2.273134222344395,-0.4175642145782178,0.09279515504647878,-2.2674575353869977,-0.4175642145782178,0.18543586171525941,-2.258006889135441,-0.4175642145782178,0.2777679286927255,-2.2447980132577348,-0.4175642145782178,0.3696376783655667,-2.227852892624204,-0.4175642145782178,0.46089220260200353,-2.2071997307158435,-0.4175642145782178,0.5513796172522772,-2.1828729026824063,-0.4175642145782178,0.640949314944828,-2.1549128981283605,-0.4175642145782178,0.7294522157574053,-2.123366253721936,-0.4175642145782178,0.816741015345886,-2.088285475739436,-0.4175642145782178,0.9026704301178129,-2.0497289526737186,-0.4175642145782178,0.9870974390425996,-2.007760858052316,-0.4175642145782178,1.0698815216959243,-1.9624510436269296,-0.4175642145782178,1.1508848921421002,-1.9138749231120826,-0.4175642145782178,1.2299727282651753,-1.8621133466664355,-0.4175642145782178,1.307013396167035,-1.8072524663256744,-0.4175642145782178,1.3818786692590368,-1.7493835926109516,-0.4175642145782178,1.4544439416825108,-1.688603042551536,-0.4175642145782178,1.5245884357029127,-1.6250119793746194,-0.4175642145782178,1.5921954027324474,-1.5587162441291098,-0.4175642145782178,1.6571523176465708,-1.4898261795236538,-0.4175642145782178,1.71935106607095,-1.4184564462720817,-0.4175642145782178,1.7786881243271782,-1.3447258322519733,-0.4175642145782178,1.8350647317377107,-1.2687570547939537,-0.4175642145782178,1.888387055003272,-1.1906765564308046,-0.4175642145782178,1.9385663443791237,-1.110614294446351,-0.4175642145782178,1.9855190813902526,-1.0287035245743896,-0.4175642145782178,2.0291671178396293,-0.9450805792076684,-0.4175642145782178,2.0694378058781706,-0.85988464048607,-0.4175642145782178,2.1062641189199187,-0.7732575086416786,-0.4175642145782178,2.1395847632011735,-0.6853433659862933,-0.4175642145782178,2.1693442797979157,-0.596288536934189,-0.4175642145782178,2.1954931369317108,-0.5062412444595741,-0.4175642145782178,2.2179878124104695,-0.41535136339408224,-0.4175642145782178,2.236790866066838,-0.3237701709749024,-0.4175642145782178,2.2518710020736625,-0.231650095058743,-0.4175642145782178,2.263203121032806,-0.1391444604207109,-0.4175642145782178,2.2707683617506187,-0.04640723356035926,-0.4175642145782178,2.274554132630532,0.04640723356036044,-0.4175642145782178,2.274554132630532,0.13914446042071008,-0.4175642145782178,2.2707683617506187,0.23165009505874218,-0.4175642145782178,2.2632031210328063,0.32377017097490157,-0.4175642145782178,2.2518710020736625,0.4153513633940814,-0.4175642145782178,2.2367908660668383,0.5062412444595733,-0.4175642145782178,2.2179878124104695,0.5962885369341883,-0.4175642145782178,2.1954931369317108,0.6853433659862926,-0.4175642145782178,2.1693442797979157,0.7732575086416779,-0.4175642145782178,2.139584763201174,0.8598846404860674,-0.4175642145782178,2.1062641189199196,0.9450805792076694,-0.4175642145782178,2.06943780587817,1.0287035245743905,-0.4175642145782178,2.0291671178396284,1.1106142944463504,-0.4175642145782178,1.985519081390253,1.1906765564308024,-0.4175642145782178,1.9385663443791252,1.268757054793953,-0.4175642145782178,1.8883870550032724,1.3447258322519744,-0.4175642145782178,1.83506473173771,1.4184564462720812,-0.4175642145782178,1.7786881243271786,1.4898261795236531,-0.4175642145782178,1.7193510660709508,1.5587162441291091,-0.4175642145782178,1.6571523176465712,1.625011979374618,-0.4175642145782178,1.5921954027324488,1.688603042551536,-0.4175642145782178,1.5245884357029127,1.7493835926109522,-0.4175642145782178,1.45444394168251,1.8072524663256737,-0.4175642145782178,1.3818786692590375,1.8621133466664344,-0.4175642145782178,1.3070133961670365,1.9138749231120815,-0.4175642145782178,1.229972728265177,1.9624510436269291,-0.4175642145782178,1.1508848921421009,2.007760858052316,-0.4175642145782178,1.069881521695924,2.049728952673718,-0.4175642145782178,0.9870974390426013,2.088285475739435,-0.4175642145782178,0.9026704301178147,2.1233662537219358,-0.4175642145782178,0.8167410153458868,2.15491289812836,-0.4175642145782178,0.729452215757407,2.1828729026824063,-0.4175642145782178,0.6409493149448279,2.207199730715843,-0.4175642145782178,0.551379617252278,2.227852892624204,-0.4175642145782178,0.46089220260200436,2.2447980132577343,-0.4175642145782178,0.3696376783655685,2.258006889135441,-0.4175642145782178,0.2777679286927273,2.2674575353869977,-0.4175642145782178,0.18543586171526022,2.273134222344395,-0.4175642145782178,0.0927951550464786,2.2750275017224206,-0.4175642145782178,0.0,2.3037969601624493,-0.3971239189422557,0.0,2.301879738822254,-0.3971239189422557,-0.09396862057800529,2.2961312658273805,-0.3971239189422557,-0.18778083966074324,2.286561108943816,-0.3971239189422557,-0.2812805160678003,2.273185196753223,-0.3971239189422557,-0.37431202881520204,2.256025792141385,-0.3971239189422557,-0.4667205361311827,2.235111455243831,-0.3971239189422557,-0.5583522331750332,2.2104769959103083,-0.3971239189422557,-0.6490546080300801,2.182163415767224,-0.3971239189422557,-0.7386766955447187,2.1502178399744816,-0.3971239189422557,-0.82706932859901,2.1146934387903067,-0.3971239189422557,-0.9140853863786303,2.0756493390745967,-0.3971239189422557,-0.9995800392429475,2.0331505258780984,-0.3971239189422557,-1.083410989779665,1.9872677342812055,-0.3971239189422557,-1.1654387096448213,1.9380773316623965,-0.3971239189422557,-1.2455266717939473,1.8856611905922704,-0.3971239189422557,-1.3235415777178605,1.830106552564733,-0.3971239189422557,-1.3993535793048768,1.7715058827921406,-0.3971239189422557,-1.472836494960177,1.7099567163060785,-0.3971239189422557,-1.5438680196226215,1.6455614956199267,-0.3971239189422557,-1.6123299283294508,1.5784274002234067,-0.3971239189422557,-1.6781082729900714,1.5086661681928981,-0.3971239189422557,-1.7410935720414051,1.4363939102144383,-0.3971239189422557,-1.801180992669148,1.361730916328943,-0.3971239189422557,-1.8582705252916432,1.2848014557213063,-0.3971239189422557,-1.9122671500159578,1.205733569886605,-0.3971239189422557,-1.9630809947891212,1.1246588595176676,-0.3971239189422557,-2.0106274849812835,1.0417122654687083,-0.3971239189422557,-2.054827484151843,0.9570318441595903,-0.3971239189422557,-2.0956074257642423,0.8707585377945429,-0.3971239189422557,-2.132899435630206,0.7830359397777708,-0.3971239189422557,-2.1666414448796285,0.6940100557164016,-0.3971239189422557,-2.1967772932680862,0.6038290604085659,-0.3971239189422557,-2.223256822650017,0.5126430512210663,-0.3971239189422557,-2.2460359604620086,0.4206037982671244,-0.3971239189422557,-2.2650767930772258,0.3278644918000054,-0.3971239189422557,-2.2803476289089053,0.2345794872429578,-0.3971239189422557,-2.2918230511578708,0.1409040482798483,-0.3971239189422557,-2.299483960116289,0.04699408843407989,-0.3971239189422557,-2.3033176049572477,-0.046994088434079616,-0.3971239189422557,-2.3033176049572477,-0.14090404827984798,-0.3971239189422557,-2.2994839601162895,-0.23457948724295702,-0.3971239189422557,-2.2918230511578708,-0.3278644918000051,-0.3971239189422557,-2.2803476289089053,-0.4206037982671242,-0.3971239189422557,-2.2650767930772258,-0.5126430512210656,-0.3971239189422557,-2.2460359604620086,-0.6038290604085655,-0.3971239189422557,-2.2232568226500176,-0.6940100557164008,-0.3971239189422557,-2.1967772932680862,-0.7830359397777704,-0.3971239189422557,-2.166641444879629,-0.8707585377945427,-0.3971239189422557,-2.1328994356302062,-0.9570318441595896,-0.3971239189422557,-2.0956074257642427,-1.0417122654687085,-0.3971239189422557,-2.054827484151843,-1.1246588595176674,-0.3971239189422557,-2.0106274849812835,-1.2057335698866054,-0.3971239189422557,-1.9630809947891208,-1.2848014557213059,-0.3971239189422557,-1.9122671500159583,-1.3617309163289426,-0.3971239189422557,-1.8582705252916434,-1.436393910214438,-0.3971239189422557,-1.8011809926691487,-1.5086661681928983,-0.3971239189422557,-1.7410935720414051,-1.5784274002234069,-0.3971239189422557,-1.6781082729900714,-1.6455614956199267,-0.3971239189422557,-1.6123299283294508,-1.7099567163060785,-0.3971239189422557,-1.5438680196226215,-1.7715058827921402,-0.3971239189422557,-1.472836494960178,-1.830106552564733,-0.3971239189422557,-1.3993535793048766,-1.88566119059227,-0.3971239189422557,-1.3235415777178612,-1.9380773316623967,-0.3971239189422557,-1.245526671793947,-1.9872677342812053,-0.3971239189422557,-1.1654387096448215,-2.0331505258780984,-0.3971239189422557,-1.0834109897796653,-2.0756493390745963,-0.3971239189422557,-0.9995800392429478,-2.1146934387903062,-0.3971239189422557,-0.9140853863786305,-2.1502178399744816,-0.3971239189422557,-0.8270693285990101,-2.182163415767224,-0.3971239189422557,-0.7386766955447187,-2.210476995910308,-0.3971239189422557,-0.649054608030081,-2.235111455243831,-0.3971239189422557,-0.5583522331750331,-2.2560257921413847,-0.3971239189422557,-0.4667205361311834,-2.273185196753223,-0.3971239189422557,-0.37431202881520265,-2.286561108943816,-0.3971239189422557,-0.28128051606780086,-2.2961312658273805,-0.3971239189422557,-0.18778083966074374,-2.301879738822254,-0.3971239189422557,-0.09396862057800566,-2.3037969601624493,-0.3971239189422557,-2.821337573148346e-16,-2.301879738822254,-0.3971239189422557,0.09396862057800509,-2.2961312658273805,-0.3971239189422557,0.18778083966074316,-2.286561108943816,-0.3971239189422557,0.28128051606780036,-2.2731851967532233,-0.3971239189422557,0.3743120288152011,-2.256025792141385,-0.3971239189422557,0.46672053613118186,-2.235111455243831,-0.3971239189422557,0.5583522331750336,-2.2104769959103083,-0.3971239189422557,0.6490546080300805,-2.182163415767224,-0.3971239189422557,0.7386766955447183,-2.1502178399744816,-0.3971239189422557,0.8270693285990095,-2.1146934387903067,-0.3971239189422557,0.9140853863786299,-2.075649339074597,-0.3971239189422557,0.9995800392429465,-2.0331505258780984,-0.3971239189422557,1.0834109897796649,-1.9872677342812055,-0.3971239189422557,1.1654387096448209,-1.9380773316623965,-0.3971239189422557,1.2455266717939473,-1.8856611905922707,-0.3971239189422557,1.3235415777178599,-1.8301065525647335,-0.3971239189422557,1.3993535793048761,-1.7715058827921404,-0.3971239189422557,1.4728364949601775,-1.7099567163060783,-0.3971239189422557,1.5438680196226218,-1.6455614956199272,-0.3971239189422557,1.6123299283294505,-1.578427400223407,-0.3971239189422557,1.678108272990071,-1.5086661681928992,-0.3971239189422557,1.741093572041404,-1.4363939102144383,-0.3971239189422557,1.801180992669148,-1.3617309163289422,-0.3971239189422557,1.8582705252916436,-1.2848014557213063,-0.3971239189422557,1.9122671500159578,-1.205733569886606,-0.3971239189422557,1.9630809947891206,-1.1246588595176679,-0.3971239189422557,2.010627484981283,-1.0417122654687072,-0.3971239189422557,2.054827484151844,-0.9570318441595902,-0.3971239189422557,2.0956074257642427,-0.8707585377945437,-0.3971239189422557,2.132899435630206,-0.7830359397777701,-0.3971239189422557,2.166641444879629,-0.6940100557164018,-0.3971239189422557,2.1967772932680862,-0.6038290604085671,-0.3971239189422557,2.223256822650017,-0.5126430512210667,-0.3971239189422557,2.2460359604620086,-0.42060379826712374,-0.3971239189422557,2.2650767930772258,-0.3278644918000051,-0.3971239189422557,2.2803476289089053,-0.2345794872429586,-0.3971239189422557,2.2918230511578708,-0.14090404827984804,-0.3971239189422557,2.2994839601162895,-0.04699408843407865,-0.3971239189422557,2.3033176049572477,0.046994088434079845,-0.3971239189422557,2.3033176049572477,0.1409040482798472,-0.3971239189422557,2.2994839601162895,0.23457948724295777,-0.3971239189422557,2.2918230511578708,0.32786449180000427,-0.3971239189422557,2.2803476289089053,0.4206037982671229,-0.3971239189422557,2.265076793077226,0.5126430512210659,-0.3971239189422557,2.2460359604620086,0.6038290604085663,-0.3971239189422557,2.223256822650017,0.6940100557164011,-0.3971239189422557,2.1967772932680862,0.7830359397777693,-0.3971239189422557,2.1666414448796294,0.870758537794541,-0.3971239189422557,2.1328994356302067,0.9570318441595912,-0.3971239189422557,2.0956074257642423,1.0417122654687083,-0.3971239189422557,2.054827484151843,1.1246588595176672,-0.3971239189422557,2.0106274849812835,1.2057335698866036,-0.3971239189422557,1.9630809947891221,1.2848014557213054,-0.3971239189422557,1.9122671500159583,1.361730916328943,-0.3971239189422557,1.858270525291643,1.4363939102144379,-0.3971239189422557,1.8011809926691487,1.5086661681928988,-0.3971239189422557,1.741093572041405,1.5784274002234064,-0.3971239189422557,1.6781082729900716,1.6455614956199258,-0.3971239189422557,1.6123299283294517,1.7099567163060783,-0.3971239189422557,1.5438680196226218,1.7715058827921413,-0.3971239189422557,1.4728364949601764,1.8301065525647329,-0.3971239189422557,1.3993535793048768,1.8856611905922698,-0.3971239189422557,1.3235415777178614,1.9380773316623954,-0.3971239189422557,1.2455266717939488,1.9872677342812053,-0.3971239189422557,1.1654387096448218,2.033150525878099,-0.3971239189422557,1.0834109897796647,2.0756493390745963,-0.3971239189422557,0.9995800392429481,2.114693438790306,-0.3971239189422557,0.9140853863786317,2.1502178399744816,-0.3971239189422557,0.8270693285990105,2.1821634157672234,-0.3971239189422557,0.73867669554472,2.2104769959103083,-0.3971239189422557,0.6490546080300804,2.2351114552438305,-0.3971239189422557,0.5583522331750344,2.256025792141385,-0.3971239189422557,0.4667205361311827,2.273185196753223,-0.3971239189422557,0.3743120288152029,2.286561108943816,-0.3971239189422557,0.2812805160678022,2.2961312658273805,-0.3971239189422557,0.187780839660744,2.301879738822254,-0.3971239189422557,0.09396862057800492,2.3037969601624493,-0.3971239189422557,0.0,2.3310529174284005,-0.3747051719180625,0.0,2.3291130136627047,-0.3747051719180625,-0.09508035253663844,2.323296531143987,-0.3747051719180625,-0.1900024532098803,2.3136131508333775,-0.3747051719180625,-0.2846083135509379,2.300078989761757,-0.3747051719180625,-0.378740471441847,2.2827165742045548,-0.3747051719180625,-0.4722422531956224,2.2615548021889835,-0.3747051719180625,-0.5649580343241476,2.2366288953961253,-0.3747051719180625,-0.6567334985597773,2.20798034053792,-0.3747051719180625,-0.7474158946995354,2.1756568203066275,-0.3747051719180625,-0.8368542908444172,2.139712134011691,-0.3747051719180625,-0.9248998256106404,2.1002061080360974,-0.3747051719180625,-1.0114059558947268,2.0572044962612663,-0.3747051719180625,-1.0962287007800364,2.0107788706262038,-0.3747051719180625,-1.1792268811787938,1.9610065020030736,-0.3747051719180625,-1.2602623548107459,1.907970231587455,-0.3747051719180625,-1.339200246127356,1.8517583330173493,-0.3747051719180625,-1.4159091707988427,1.792464365450421,-0.3747051719180625,-1.490261454390429,1.7301870178440137,-0.3747051719180625,-1.5621333448638421,1.6650299446971182,-0.3747051719180625,-1.6314052185503662,1.5971015935276907,-0.3747051719180625,-1.6979617792526334,1.5265150243724601,-0.3747051719180625,-1.7616922501437655,1.4533877216096562,-0.3747051719180625,-1.822490558144472,1.3778413984178548,-0.3747051719180625,-1.88025551047122,1.3000017941964046,-0.3747051719180625,-1.9348909630616362,1.219998465284607,-0.3747051719180625,-1.9863059805968117,1.1379645693279739,-0.3747051719180625,-2.0344149878541637,1.054036643650469,-0.3747051719180625,-2.07913791213895,0.9683543780016043,-0.3747051719180625,-2.120400316557367,0.8810603820566417,-0.3747051719180625,-2.158133523909413,0.7922999480568605,-0.3747051719180625,-2.1922747309953086,0.7022208089849591,-0.3747051719180625,-2.222767113145227,0.6109728926780879,-0.3747051719180625,-2.2495599187983473,0.5187080722877592,-0.3747051719180625,-2.2726085539738192,0.42557991350197444,-0.3747051719180625,-2.2918746564930443,0.3317434189502928,-0.3747051719180625,-2.307326159829734,0.23735477021724904,-0.3747051719180625,-2.3189373464814813,0.14257106789352303,-0.3747051719180625,-2.3266888907740024,0.04755007009750795,-0.3747051719180625,-2.3305678910268157,-0.04755007009750767,-0.3747051719180625,-2.3305678910268157,-0.14257106789352275,-0.3747051719180625,-2.3266888907740024,-0.23735477021724827,-0.3747051719180625,-2.3189373464814813,-0.3317434189502925,-0.3747051719180625,-2.307326159829734,-0.42557991350197416,-0.3747051719180625,-2.2918746564930443,-0.5187080722877584,-0.3747051719180625,-2.2726085539738192,-0.6109728926780876,-0.3747051719180625,-2.2495599187983473,-0.7022208089849583,-0.3747051719180625,-2.2227671131452276,-0.7922999480568602,-0.3747051719180625,-2.192274730995309,-0.8810603820566415,-0.3747051719180625,-2.1581335239094135,-0.9683543780016036,-0.3747051719180625,-2.1204003165573675,-1.0540366436504691,-0.3747051719180625,-2.07913791213895,-1.1379645693279736,-0.3747051719180625,-2.0344149878541637,-1.2199984652846074,-0.3747051719180625,-1.9863059805968113,-1.3000017941964042,-0.3747051719180625,-1.9348909630616367,-1.3778413984178544,-0.3747051719180625,-1.8802555104712202,-1.453387721609656,-0.3747051719180625,-1.8224905581444726,-1.5265150243724603,-0.3747051719180625,-1.7616922501437655,-1.5971015935276909,-0.3747051719180625,-1.6979617792526334,-1.6650299446971182,-0.3747051719180625,-1.6314052185503662,-1.7301870178440137,-0.3747051719180625,-1.5621333448638421,-1.7924643654504206,-0.3747051719180625,-1.4902614543904298,-1.8517583330173493,-0.3747051719180625,-1.4159091707988423,-1.9079702315874545,-0.3747051719180625,-1.3392002461273569,-1.9610065020030738,-0.3747051719180625,-1.2602623548107457,-2.0107788706262038,-0.3747051719180625,-1.179226881178794,-2.057204496261266,-0.3747051719180625,-1.0962287007800369,-2.100206108036097,-0.3747051719180625,-1.011405955894727,-2.139712134011691,-0.3747051719180625,-0.9248998256106405,-2.1756568203066275,-0.3747051719180625,-0.8368542908444173,-2.20798034053792,-0.3747051719180625,-0.7474158946995354,-2.2366288953961253,-0.3747051719180625,-0.6567334985597783,-2.2615548021889835,-0.3747051719180625,-0.5649580343241474,-2.2827165742045548,-0.3747051719180625,-0.47224225319562313,-2.300078989761757,-0.3747051719180625,-0.3787404714418477,-2.3136131508333775,-0.3747051719180625,-0.28460831355093846,-2.323296531143987,-0.3747051719180625,-0.1900024532098808,-2.3291130136627047,-0.3747051719180625,-0.09508035253663881,-2.3310529174284005,-0.3747051719180625,-2.85471649397179e-16,-2.3291130136627047,-0.3747051719180625,0.09508035253663824,-2.323296531143987,-0.3747051719180625,0.1900024532098802,-2.3136131508333775,-0.3747051719180625,0.28460831355093796,-2.300078989761757,-0.3747051719180625,0.37874047144184614,-2.282716574204555,-0.3747051719180625,0.4722422531956216,-2.2615548021889835,-0.3747051719180625,0.5649580343241478,-2.2366288953961253,-0.3747051719180625,0.6567334985597777,-2.20798034053792,-0.3747051719180625,0.747415894699535,-2.1756568203066275,-0.3747051719180625,0.8368542908444168,-2.139712134011691,-0.3747051719180625,0.92489982561064,-2.100206108036098,-0.3747051719180625,1.0114059558947257,-2.0572044962612663,-0.3747051719180625,1.0962287007800362,-2.0107788706262038,-0.3747051719180625,1.1792268811787936,-1.9610065020030736,-0.3747051719180625,1.2602623548107459,-1.9079702315874552,-0.3747051719180625,1.3392002461273556,-1.8517583330173497,-0.3747051719180625,1.4159091707988418,-1.7924643654504209,-0.3747051719180625,1.4902614543904291,-1.7301870178440135,-0.3747051719180625,1.5621333448638426,-1.6650299446971188,-0.3747051719180625,1.631405218550366,-1.5971015935276913,-0.3747051719180625,1.6979617792526331,-1.5265150243724614,-0.3747051719180625,1.7616922501437646,-1.4533877216096562,-0.3747051719180625,1.822490558144472,-1.3778413984178537,-0.3747051719180625,1.8802555104712206,-1.3000017941964046,-0.3747051719180625,1.9348909630616362,-1.2199984652846079,-0.3747051719180625,1.9863059805968108,-1.137964569327974,-0.3747051719180625,2.0344149878541633,-1.0540366436504678,-0.3747051719180625,2.0791379121389504,-0.9683543780016042,-0.3747051719180625,2.1204003165573675,-0.8810603820566425,-0.3747051719180625,2.158133523909413,-0.7922999480568599,-0.3747051719180625,2.192274730995309,-0.7022208089849593,-0.3747051719180625,2.222767113145227,-0.6109728926780892,-0.3747051719180625,2.249559918798347,-0.5187080722877595,-0.3747051719180625,2.2726085539738192,-0.4255799135019737,-0.3747051719180625,2.2918746564930443,-0.3317434189502925,-0.3747051719180625,2.307326159829734,-0.23735477021724988,-0.3747051719180625,2.3189373464814813,-0.14257106789352278,-0.3747051719180625,2.3266888907740024,-0.04755007009750669,-0.3747051719180625,2.3305678910268157,0.047550070097507904,-0.3747051719180625,2.3305678910268157,0.14257106789352192,-0.3747051719180625,2.3266888907740024,0.23735477021724902,-0.3747051719180625,2.3189373464814813,0.3317434189502917,-0.3747051719180625,2.3073261598297345,0.4255799135019729,-0.3747051719180625,2.2918746564930443,0.5187080722877587,-0.3747051719180625,2.2726085539738192,0.6109728926780884,-0.3747051719180625,2.2495599187983473,0.7022208089849585,-0.3747051719180625,2.2227671131452276,0.7922999480568591,-0.3747051719180625,2.192274730995309,0.8810603820566398,-0.3747051719180625,2.158133523909414,0.9683543780016052,-0.3747051719180625,2.120400316557367,1.054036643650469,-0.3747051719180625,2.07913791213895,1.1379645693279734,-0.3747051719180625,2.0344149878541637,1.2199984652846056,-0.3747051719180625,1.9863059805968124,1.300001794196404,-0.3747051719180625,1.9348909630616367,1.3778413984178548,-0.3747051719180625,1.8802555104712197,1.4533877216096558,-0.3747051719180625,1.8224905581444726,1.526515024372461,-0.3747051719180625,1.7616922501437653,1.5971015935276904,-0.3747051719180625,1.6979617792526336,1.6650299446971175,-0.3747051719180625,1.6314052185503674,1.7301870178440135,-0.3747051719180625,1.5621333448638426,1.7924643654504215,-0.3747051719180625,1.4902614543904282,1.851758333017349,-0.3747051719180625,1.4159091707988427,1.9079702315874543,-0.3747051719180625,1.339200246127357,1.9610065020030727,-0.3747051719180625,1.2602623548107474,2.0107788706262038,-0.3747051719180625,1.1792268811787945,2.057204496261267,-0.3747051719180625,1.0962287007800362,2.100206108036097,-0.3747051719180625,1.0114059558947275,2.13971213401169,-0.3747051719180625,0.9248998256106419,2.1756568203066275,-0.3747051719180625,0.8368542908444178,2.2079803405379197,-0.3747051719180625,0.7474158946995367,2.2366288953961253,-0.3747051719180625,0.6567334985597776,2.2615548021889835,-0.3747051719180625,0.5649580343241487,2.2827165742045548,-0.3747051719180625,0.4722422531956224,2.300078989761757,-0.3747051719180625,0.37874047144184797,2.3136131508333775,-0.3747051719180625,0.2846083135509398,2.323296531143987,-0.3747051719180625,0.19000245320988104,2.3291130136627047,-0.3747051719180625,0.09508035253663806,2.3310529174284005,-0.3747051719180625,0.0,2.35665958571219,-0.35041966257561663,0.0,2.354698372060365,-0.35041966257561663,-0.09612481232968226,2.348817995351464,-0.35041966257561663,-0.19208963439571927,2.3390282428921885,-0.35041966257561663,-0.2877347422224704,2.325345408759365,-0.35041966257561663,-0.3829009439670939,2.3077922666800603,-0.35041966257561663,-0.4774298448786589,2.28639803212696,-0.35041966257561663,-0.5711641109305764,2.2611983136920983,-0.35041966257561663,-0.6639477306875596,2.232235053819872,-0.35041966257561663,-0.7556262749712611,2.1995564589979772,-0.35041966257561663,-0.8460471538923995,2.1632169195224726,-0.35041966257561663,-0.9350598708215719,2.123276918970496,-0.35041966257561663,-1.0225162728760402,2.0798029335313206,-0.35041966257561663,-1.1082707975055845,2.0328673213632986,-0.35041966257561663,-1.1921807147670014,1.9825482021608476,-0.35041966257561663,-1.2741063648840083,1.928929327131928,-0.35041966257561663,-1.35391139069716,1.8720999396024234,-0.35041966257561663,-1.4314629646168824,1.8121546264794317,-0.35041966257561663,-1.5066320097018853,1.7491931608206934,-0.35041966257561663,-1.5792934144949957,1.6833203357721827,-0.35041966257561663,-1.6493262412588288,1.6146457901502589,-0.35041966257561663,-1.7166139272647174,1.5432838259586759,-0.35041966257561663,-1.7810444787998703,1.4693532181441784,-0.35041966257561663,-1.8425106575698542,1.3929770169073232,-0.35041966257561663,-1.9009101591861532,1.3142823428975652,-0.35041966257561663,-1.9561457834417244,1.2334001756334818,-0.35041966257561663,-2.0081255960911526,1.1504651355002908,-0.35041966257561663,-2.056763081866127,1.065615259687505,-0.35041966257561663,-2.1019772884715673,0.9789917724396496,-0.35041966257561663,-2.143692961322732,0.8907388500024499,-0.35041966257561663,-2.181840668799045,0.8010033806556954,-0.35041966257561663,-2.216356917806177,0.7099347202321946,-0.35041966257561663,-2.2471842594540363,0.6176844435297324,-0.35041966257561663,-2.2742713846747744,0.5244060920297776,-0.35041966257561663,-2.2975732096216666,0.43025491834283885,-0.35041966257561663,-2.317050950706727,0.33538762780581793,-0.35041966257561663,-2.332672189152161,0.23996211766143888,-0.35041966257561663,-2.3444109249482246,0.1441372142538736,-0.35041966257561663,-2.352247620127669,0.04807240867796415,-0.35041966257561663,-2.356169231284767,-0.048072408677963856,-0.35041966257561663,-2.356169231284767,-0.1441372142538733,-0.35041966257561663,-2.3522476201276694,-0.2399621176614381,-0.35041966257561663,-2.3444109249482246,-0.33538762780581766,-0.35041966257561663,-2.332672189152161,-0.4302549183428386,-0.35041966257561663,-2.317050950706727,-0.5244060920297768,-0.35041966257561663,-2.2975732096216666,-0.6176844435297322,-0.35041966257561663,-2.2742713846747744,-0.7099347202321938,-0.35041966257561663,-2.2471842594540368,-0.8010033806556952,-0.35041966257561663,-2.2163569178061775,-0.8907388500024496,-0.35041966257561663,-2.1818406687990453,-0.978991772439649,-0.35041966257561663,-2.1436929613227322,-1.0656152596875053,-0.35041966257561663,-2.1019772884715673,-1.1504651355002908,-0.35041966257561663,-2.056763081866127,-1.2334001756334823,-0.35041966257561663,-2.008125596091152,-1.3142823428975645,-0.35041966257561663,-1.9561457834417249,-1.3929770169073226,-0.35041966257561663,-1.9009101591861535,-1.4693532181441782,-0.35041966257561663,-1.8425106575698547,-1.5432838259586763,-0.35041966257561663,-1.7810444787998703,-1.614645790150259,-0.35041966257561663,-1.7166139272647174,-1.6833203357721827,-0.35041966257561663,-1.6493262412588288,-1.7491931608206934,-0.35041966257561663,-1.5792934144949957,-1.8121546264794313,-0.35041966257561663,-1.5066320097018862,-1.8720999396024234,-0.35041966257561663,-1.4314629646168822,-1.9289293271319277,-0.35041966257561663,-1.3539113906971607,-1.9825482021608478,-0.35041966257561663,-1.274106364884008,-2.032867321363298,-0.35041966257561663,-1.1921807147670016,-2.07980293353132,-0.35041966257561663,-1.1082707975055848,-2.1232769189704954,-0.35041966257561663,-1.0225162728760406,-2.1632169195224726,-0.35041966257561663,-0.935059870821572,-2.1995564589979772,-0.35041966257561663,-0.8460471538923996,-2.232235053819872,-0.35041966257561663,-0.7556262749712611,-2.2611983136920983,-0.35041966257561663,-0.6639477306875605,-2.28639803212696,-0.35041966257561663,-0.5711641109305763,-2.30779226668006,-0.35041966257561663,-0.47742984487865964,-2.325345408759365,-0.35041966257561663,-0.38290094396709456,-2.3390282428921885,-0.35041966257561663,-0.287734742222471,-2.348817995351464,-0.35041966257561663,-0.19208963439571977,-2.354698372060365,-0.35041966257561663,-0.09612481232968263,-2.35665958571219,-0.35041966257561663,-2.8860756183223606e-16,-2.354698372060365,-0.35041966257561663,0.09612481232968206,-2.348817995351464,-0.35041966257561663,0.1920896343957192,-2.3390282428921885,-0.35041966257561663,0.2877347422224704,-2.3253454087593655,-0.35041966257561663,0.382900943967093,-2.3077922666800603,-0.35041966257561663,0.4774298448786581,-2.28639803212696,-0.35041966257561663,0.5711641109305767,-2.2611983136920983,-0.35041966257561663,0.6639477306875601,-2.232235053819872,-0.35041966257561663,0.7556262749712606,-2.1995564589979777,-0.35041966257561663,0.846047153892399,-2.1632169195224726,-0.35041966257561663,0.9350598708215716,-2.1232769189704963,-0.35041966257561663,1.0225162728760393,-2.0798029335313206,-0.35041966257561663,1.1082707975055843,-2.0328673213632986,-0.35041966257561663,1.1921807147670012,-1.9825482021608476,-0.35041966257561663,1.2741063648840083,-1.9289293271319286,-0.35041966257561663,1.3539113906971594,-1.872099939602424,-0.35041966257561663,1.4314629646168815,-1.8121546264794315,-0.35041966257561663,1.5066320097018855,-1.7491931608206932,-0.35041966257561663,1.579293414494996,-1.6833203357721833,-0.35041966257561663,1.6493262412588285,-1.6146457901502593,-0.35041966257561663,1.7166139272647172,-1.5432838259586772,-0.35041966257561663,1.7810444787998692,-1.4693532181441784,-0.35041966257561663,1.8425106575698542,-1.3929770169073221,-0.35041966257561663,1.9009101591861537,-1.3142823428975652,-0.35041966257561663,1.9561457834417244,-1.2334001756334827,-0.35041966257561663,2.008125596091152,-1.1504651355002913,-0.35041966257561663,2.0567630818661264,-1.065615259687504,-0.35041966257561663,2.1019772884715677,-0.9789917724396495,-0.35041966257561663,2.1436929613227322,-0.8907388500024507,-0.35041966257561663,2.181840668799045,-0.8010033806556948,-0.35041966257561663,2.2163569178061775,-0.7099347202321948,-0.35041966257561663,2.2471842594540363,-0.6176844435297337,-0.35041966257561663,2.274271384674774,-0.524406092029778,-0.35041966257561663,2.2975732096216666,-0.43025491834283813,-0.35041966257561663,2.317050950706727,-0.33538762780581766,-0.35041966257561663,2.332672189152161,-0.23996211766143968,-0.35041966257561663,2.344410924948224,-0.14413721425387335,-0.35041966257561663,2.3522476201276694,-0.04807240867796287,-0.35041966257561663,2.356169231284767,0.0480724086779641,-0.35041966257561663,2.356169231284767,0.1441372142538725,-0.35041966257561663,2.3522476201276694,0.23996211766143885,-0.35041966257561663,2.3444109249482246,0.3353876278058168,-0.35041966257561663,2.3326721891521616,0.4302549183428373,-0.35041966257561663,2.3170509507067276,0.5244060920297771,-0.35041966257561663,2.2975732096216666,0.617684443529733,-0.35041966257561663,2.2742713846747744,0.7099347202321941,-0.35041966257561663,2.2471842594540368,0.801003380655694,-0.35041966257561663,2.2163569178061775,0.8907388500024479,-0.35041966257561663,2.1818406687990457,0.9789917724396506,-0.35041966257561663,2.1436929613227313,1.065615259687505,-0.35041966257561663,2.1019772884715673,1.1504651355002906,-0.35041966257561663,2.056763081866127,1.2334001756334805,-0.35041966257561663,2.0081255960911535,1.3142823428975643,-0.35041966257561663,1.9561457834417249,1.3929770169073232,-0.35041966257561663,1.9009101591861528,1.469353218144178,-0.35041966257561663,1.8425106575698547,1.5432838259586767,-0.35041966257561663,1.7810444787998698,1.6146457901502587,-0.35041966257561663,1.7166139272647176,1.683320335772182,-0.35041966257561663,1.6493262412588299,1.7491931608206932,-0.35041966257561663,1.579293414494996,1.8121546264794322,-0.35041966257561663,1.5066320097018844,1.8720999396024232,-0.35041966257561663,1.4314629646168824,1.9289293271319274,-0.35041966257561663,1.353911390697161,1.9825482021608467,-0.35041966257561663,1.2741063648840099,2.032867321363298,-0.35041966257561663,1.1921807147670018,2.0798029335313206,-0.35041966257561663,1.1082707975055843,2.1232769189704954,-0.35041966257561663,1.0225162728760409,2.1632169195224717,-0.35041966257561663,0.9350598708215734,2.1995564589979772,-0.35041966257561663,0.8460471538924,2.2322350538198714,-0.35041966257561663,0.7556262749712624,2.2611983136920983,-0.35041966257561663,0.6639477306875599,2.28639803212696,-0.35041966257561663,0.5711641109305775,2.3077922666800603,-0.35041966257561663,0.4774298448786589,2.325345408759365,-0.35041966257561663,0.38290094396709484,2.3390282428921885,-0.35041966257561663,0.2877347422224723,2.348817995351464,-0.35041966257561663,0.19208963439572005,2.354698372060365,-0.35041966257561663,0.09612481232968188,2.35665958571219,-0.35041966257561663,0.0,2.3804893938804836,-0.3243883801008631,0.0,2.3785083490466827,-0.3243883801008631,-0.09709679651098578,2.372568511798898,-0.3243883801008631,-0.19403198498658086,2.3626797684100316,-0.3243883801008631,-0.2906442263720205,2.348858577717542,-0.3243883801008631,-0.3867727191261002,2.331127943729335,-0.3243883801008631,-0.48225746685947146,2.3095173773358626,-0.3243883801008631,-0.5769395446328421,2.284062847192158,-0.3243883801008631,-0.6706613634718538,2.2548067198515587,-0.3243883801008631,-0.7632669326583771,2.2217976892507605,-0.3243883801008631,-0.8546021193616673,2.185090695663565,-0.3243883801008631,-0.9445149051772503,2.1447468342582154,-0.3243883801008631,-1.0328556391465538,2.1008332534105185,-0.3243883801008631,-1.119477286836159,2.053423042942001,-0.3243883801008631,-1.2042356750621055,2.002595112469114,-0.3243883801008631,-1.2869897318519246,1.948434060065963,-0.3243883801008631,-1.3676017212450193,1.8910300314591642,-0.3243883801008631,-1.445937472540575,1.8304785699891775,-0.3243883801008631,-1.5218666036114494,1.766880457587847,-0.3243883801008631,-1.595262737912358,1.7003415470368197,-0.3243883801008631,-1.6660037148211606,1.6309725857860384,-0.3243883801008631,-1.73397179296316,1.5588890316255402,-0.3243883801008631,-1.7990538461800019,1.4842108605173596,-0.3243883801008631,-1.861141551817,1.4070623669073794,-0.3243883801008631,-1.9201315710155062,1.3275719568494926,-0.3243883801008631,-1.9759257207102399,1.2458719342863986,-0.3243883801008631,-2.0284311370453127,1.162098280842747,-0.3243883801008631,-2.07756042993695,1.076390429497143,-0.3243883801008631,-2.1232318285256624,0.9888910325097139,-0.3243883801008631,-2.1653693172757724,0.8997457239915034,-0.3243883801008631,-2.203902762495768,0.8091028775108645,-0.3243883801008631,-2.2387680290689143,0.7171133591402963,-0.3243883801008631,-2.2699070871998246,0.6239302763547582,-0.3243883801008631,-2.2972681089993277,0.5297087231993861,-0.3243883801008631,-2.3208055547468747,0.4346055221507606,-0.3243883801008631,-2.3404802486869096,0.33877896310137207,-0.3243883801008631,-2.356259444233048,0.242388539901714,-0.3243883801008631,-2.3681168784715396,0.14559468489851235,-0.3243883801008631,-2.376032815873294,0.048558501910915104,-0.3243883801008631,-2.379994081141724,-0.04855850191091481,-0.3243883801008631,-2.379994081141724,-0.14559468489851207,-0.3243883801008631,-2.376032815873294,-0.24238853990171322,-0.3243883801008631,-2.3681168784715396,-0.3387789631013718,-0.3243883801008631,-2.356259444233048,-0.4346055221507603,-0.3243883801008631,-2.3404802486869096,-0.5297087231993853,-0.3243883801008631,-2.3208055547468747,-0.623930276354758,-0.3243883801008631,-2.297268108999328,-0.7171133591402955,-0.3243883801008631,-2.269907087199825,-0.8091028775108643,-0.3243883801008631,-2.2387680290689147,-0.8997457239915032,-0.3243883801008631,-2.203902762495768,-0.9888910325097132,-0.3243883801008631,-2.1653693172757724,-1.0763904294971431,-0.3243883801008631,-2.1232318285256624,-1.1620982808427467,-0.3243883801008631,-2.07756042993695,-1.2458719342863993,-0.3243883801008631,-2.0284311370453123,-1.3275719568494921,-0.3243883801008631,-1.9759257207102403,-1.4070623669073787,-0.3243883801008631,-1.9201315710155065,-1.4842108605173594,-0.3243883801008631,-1.8611415518170005,-1.5588890316255404,-0.3243883801008631,-1.7990538461800019,-1.6309725857860387,-0.3243883801008631,-1.73397179296316,-1.7003415470368197,-0.3243883801008631,-1.6660037148211606,-1.766880457587847,-0.3243883801008631,-1.595262737912358,-1.8304785699891768,-0.3243883801008631,-1.52186660361145,-1.8910300314591642,-0.3243883801008631,-1.4459374725405747,-1.9484340600659626,-0.3243883801008631,-1.36760172124502,-2.0025951124691144,-0.3243883801008631,-1.2869897318519243,-2.053423042942001,-0.3243883801008631,-1.2042356750621057,-2.1008332534105185,-0.3243883801008631,-1.1194772868361595,-2.144746834258215,-0.3243883801008631,-1.032855639146554,-2.1850906956635647,-0.3243883801008631,-0.9445149051772505,-2.2217976892507605,-0.3243883801008631,-0.8546021193616674,-2.2548067198515587,-0.3243883801008631,-0.7632669326583771,-2.2840628471921574,-0.3243883801008631,-0.6706613634718548,-2.3095173773358626,-0.3243883801008631,-0.5769395446328419,-2.3311279437293346,-0.3243883801008631,-0.4822574668594722,-2.348858577717542,-0.3243883801008631,-0.38677271912610084,-2.3626797684100316,-0.3243883801008631,-0.2906442263720211,-2.372568511798898,-0.3243883801008631,-0.19403198498658136,-2.3785083490466827,-0.3243883801008631,-0.09709679651098616,-2.3804893938804836,-0.3243883801008631,-2.915258716619957e-16,-2.3785083490466827,-0.3243883801008631,0.09709679651098559,-2.372568511798898,-0.3243883801008631,0.19403198498658075,-2.3626797684100316,-0.3243883801008631,0.29064422637202053,-2.348858577717542,-0.3243883801008631,0.38677271912609923,-2.3311279437293355,-0.3243883801008631,0.4822574668594706,-2.3095173773358626,-0.3243883801008631,0.5769395446328424,-2.284062847192158,-0.3243883801008631,0.6706613634718542,-2.2548067198515587,-0.3243883801008631,0.7632669326583766,-2.2217976892507605,-0.3243883801008631,0.8546021193616669,-2.185090695663565,-0.3243883801008631,0.9445149051772499,-2.144746834258216,-0.3243883801008631,1.0328556391465527,-2.1008332534105185,-0.3243883801008631,1.1194772868361589,-2.053423042942001,-0.3243883801008631,1.204235675062105,-2.002595112469114,-0.3243883801008631,1.2869897318519246,-1.9484340600659633,-0.3243883801008631,1.3676017212450187,-1.8910300314591646,-0.3243883801008631,1.4459374725405743,-1.8304785699891772,-0.3243883801008631,1.5218666036114497,-1.7668804575878467,-0.3243883801008631,1.5952627379123585,-1.7003415470368202,-0.3243883801008631,1.6660037148211604,-1.6309725857860389,-0.3243883801008631,1.7339717929631597,-1.5588890316255415,-0.3243883801008631,1.7990538461800007,-1.4842108605173596,-0.3243883801008631,1.861141551817,-1.4070623669073783,-0.3243883801008631,1.920131571015507,-1.3275719568494926,-0.3243883801008631,1.9759257207102399,-1.2458719342863998,-0.3243883801008631,2.028431137045312,-1.1620982808427471,-0.3243883801008631,2.0775604299369497,-1.0763904294971418,-0.3243883801008631,2.1232318285256633,-0.9888910325097138,-0.3243883801008631,2.1653693172757724,-0.8997457239915042,-0.3243883801008631,2.2039027624957677,-0.8091028775108638,-0.3243883801008631,2.2387680290689147,-0.7171133591402966,-0.3243883801008631,2.2699070871998246,-0.6239302763547595,-0.3243883801008631,2.2972681089993277,-0.5297087231993864,-0.3243883801008631,2.3208055547468747,-0.43460552215075987,-0.3243883801008631,2.3404802486869096,-0.3387789631013718,-0.3243883801008631,2.356259444233048,-0.24238853990171483,-0.3243883801008631,2.368116878471539,-0.1455946848985121,-0.3243883801008631,2.376032815873294,-0.048558501910913814,-0.3243883801008631,2.379994081141724,0.048558501910915056,-0.3243883801008631,2.379994081141724,0.14559468489851124,-0.3243883801008631,2.376032815873294,0.24238853990171397,-0.3243883801008631,2.3681168784715396,0.33877896310137096,-0.3243883801008631,2.3562594442330482,0.434605522150759,-0.3243883801008631,2.3404802486869096,0.5297087231993856,-0.3243883801008631,2.3208055547468747,0.6239302763547587,-0.3243883801008631,2.2972681089993277,0.7171133591402958,-0.3243883801008631,2.269907087199825,0.8091028775108631,-0.3243883801008631,2.2387680290689147,0.8997457239915014,-0.3243883801008631,2.2039027624957686,0.9888910325097148,-0.3243883801008631,2.165369317275772,1.076390429497143,-0.3243883801008631,2.1232318285256624,1.1620982808427465,-0.3243883801008631,2.07756042993695,1.2458719342863973,-0.3243883801008631,2.0284311370453136,1.327571956849492,-0.3243883801008631,1.9759257207102403,1.4070623669073794,-0.3243883801008631,1.920131571015506,1.484210860517359,-0.3243883801008631,1.8611415518170005,1.5588890316255408,-0.3243883801008631,1.7990538461800014,1.6309725857860382,-0.3243883801008631,1.7339717929631604,1.7003415470368188,-0.3243883801008631,1.6660037148211617,1.7668804575878467,-0.3243883801008631,1.5952627379123585,1.830478569989178,-0.3243883801008631,1.5218666036114485,1.8910300314591637,-0.3243883801008631,1.445937472540575,1.9484340600659624,-0.3243883801008631,1.3676017212450202,2.002595112469113,-0.3243883801008631,1.286989731851926,2.053423042942001,-0.3243883801008631,1.204235675062106,2.100833253410519,-0.3243883801008631,1.1194772868361589,2.144746834258215,-0.3243883801008631,1.0328556391465544,2.1850906956635643,-0.3243883801008631,0.9445149051772518,2.2217976892507605,-0.3243883801008631,0.8546021193616679,2.2548067198515582,-0.3243883801008631,0.7632669326583784,2.284062847192158,-0.3243883801008631,0.6706613634718541,2.3095173773358626,-0.3243883801008631,0.5769395446328432,2.331127943729335,-0.3243883801008631,0.48225746685947146,2.348858577717542,-0.3243883801008631,0.38677271912610106,2.3626797684100316,-0.3243883801008631,0.2906442263720224,2.372568511798898,-0.3243883801008631,0.1940319849865816,2.3785083490466827,-0.3243883801008631,0.0970967965109854,2.3804893938804836,-0.3243883801008631,0.0,2.402423623027663,-0.29674101103367817,0.0,2.40042432451399,-0.29674101103367817,-0.09799146270425005,2.394429756608037,-0.29674101103367817,-0.19581982828952457,2.384449896676485,-0.29674101103367817,-0.29332227109590664,2.3705013552112866,-0.29674101103367817,-0.39033650792977986,2.35260734818314,-0.29674101103367817,-0.4867010681681883,2.330797658400799,-0.29674101103367817,-0.582255562510753,2.3051085859405265,-0.29674101103367817,-0.6768409499318345,2.2755828877281994,-0.29674101103367817,-0.7702998023886231,2.2422697063746253,-0.29674101103367817,-0.8624765668445805,2.2052244883825174,-0.29674101103367817,-0.9532178241721183,2.164508891861262,-0.29674101103367817,-1.0423725445035932,2.12019068390308,-0.29674101103367817,-1.129792338605619,2.072343627791391,-0.29674101103367817,-1.215331704858299,2.0210473602291072,-0.29674101103367817,-1.298848271428314,1.9663872587912004,-0.29674101103367817,-1.380203033232789,1.9084542998221532,-0.29674101103367817,-1.4592605832995347,1.847344907014815,-0.29674101103367817,-1.5358893381385872,1.7831607909226783,-0.29674101103367817,-1.6099617567499456,1.7160087796727015,-0.29674101103367817,-1.6813545529029765,1.6460006411604347,-0.29674101103367817,-1.7499489003341786,1.573252897023386,-0.29674101103367817,-1.8156306305217713,1.4978866287022585,-0.29674101103367817,-1.8782904227079327,1.4200272759128376,-0.29674101103367817,-1.937823985852412,1.3398044278639667,-0.29674101103367817,-1.9941322322146722,1.2573516075690974,-0.29674101103367817,-2.047121442275658,1.1728060496104105,-0.29674101103367817,-2.096703420724683,1.0863084717253986,-0.29674101103367817,-2.1427956432518225,0.9980028405960757,-0.29674101103367817,-2.1853213939014844,0.9080361322306486,-0.29674101103367817,-2.2242098927585423,0.8165580873364524,-0.29674101103367817,-2.2593964137545224,0.7237209620913206,-0.29674101103367817,-2.2908223923977555,0.6296792747282058,-0.29674101103367817,-2.3184355232481932,0.5345895483548283,-0.29674101103367817,-2.342189846974652,0.4386100504364106,-0.29674101103367817,-2.3620458268495836,0.34190052937510207,-0.29674101103367817,-2.3779704145540554,0.24462194862452613,-0.29674101103367817,-2.3899371051834195,0.14693621878200072,-0.29674101103367817,-2.397925981362104,0.0490059281043255,-0.29674101103367817,-2.4019237463941265,-0.049005928104325205,-0.29674101103367817,-2.4019237463941265,-0.14693621878200042,-0.29674101103367817,-2.3979259813621043,-0.24462194862452533,-0.29674101103367817,-2.3899371051834195,-0.3419005293751018,-0.29674101103367817,-2.3779704145540554,-0.4386100504364104,-0.29674101103367817,-2.3620458268495836,-0.5345895483548275,-0.29674101103367817,-2.342189846974652,-0.6296792747282055,-0.29674101103367817,-2.3184355232481932,-0.7237209620913199,-0.29674101103367817,-2.2908223923977555,-0.8165580873364522,-0.29674101103367817,-2.259396413754523,-0.9080361322306484,-0.29674101103367817,-2.2242098927585428,-0.998002840596075,-0.29674101103367817,-2.1853213939014844,-1.0863084717253986,-0.29674101103367817,-2.1427956432518225,-1.1728060496104105,-0.29674101103367817,-2.096703420724683,-1.257351607569098,-0.29674101103367817,-2.047121442275657,-1.3398044278639663,-0.29674101103367817,-1.9941322322146726,-1.4200272759128372,-0.29674101103367817,-1.9378239858524122,-1.4978866287022583,-0.29674101103367817,-1.8782904227079331,-1.5732528970233863,-0.29674101103367817,-1.8156306305217713,-1.6460006411604349,-0.29674101103367817,-1.7499489003341786,-1.7160087796727015,-0.29674101103367817,-1.6813545529029765,-1.7831607909226783,-0.29674101103367817,-1.6099617567499456,-1.8473449070148145,-0.29674101103367817,-1.5358893381385879,-1.9084542998221532,-0.29674101103367817,-1.4592605832995342,-1.9663872587912,-0.29674101103367817,-1.38020303323279,-2.0210473602291077,-0.29674101103367817,-1.2988482714283138,-2.072343627791391,-0.29674101103367817,-1.2153317048582992,-2.1201906839030795,-0.29674101103367817,-1.1297923386056192,-2.1645088918612614,-0.29674101103367817,-1.0423725445035936,-2.205224488382517,-0.29674101103367817,-0.9532178241721184,-2.2422697063746253,-0.29674101103367817,-0.8624765668445807,-2.2755828877281994,-0.29674101103367817,-0.7702998023886231,-2.3051085859405265,-0.29674101103367817,-0.6768409499318354,-2.330797658400799,-0.29674101103367817,-0.5822555625107528,-2.35260734818314,-0.29674101103367817,-0.48670106816818903,-2.3705013552112866,-0.29674101103367817,-0.39033650792978053,-2.384449896676485,-0.29674101103367817,-0.29332227109590725,-2.394429756608037,-0.29674101103367817,-0.19581982828952507,-2.40042432451399,-0.29674101103367817,-0.09799146270425042,-2.402423623027663,-0.29674101103367817,-2.942120400136815e-16,-2.40042432451399,-0.29674101103367817,0.09799146270424984,-2.394429756608037,-0.29674101103367817,0.19581982828952446,-2.384449896676485,-0.29674101103367817,0.2933222710959067,-2.370501355211287,-0.29674101103367817,0.3903365079297789,-2.3526073481831404,-0.29674101103367817,0.4867010681681874,-2.330797658400799,-0.29674101103367817,0.5822555625107534,-2.3051085859405265,-0.29674101103367817,0.6768409499318349,-2.2755828877281994,-0.29674101103367817,0.7702998023886225,-2.2422697063746253,-0.29674101103367817,0.8624765668445802,-2.2052244883825174,-0.29674101103367817,0.9532178241721179,-2.1645088918612623,-0.29674101103367817,1.042372544503592,-2.12019068390308,-0.29674101103367817,1.1297923386056186,-2.072343627791391,-0.29674101103367817,1.2153317048582986,-2.0210473602291072,-0.29674101103367817,1.298848271428314,-1.9663872587912006,-0.29674101103367817,1.3802030332327886,-1.9084542998221539,-0.29674101103367817,1.4592605832995338,-1.8473449070148147,-0.29674101103367817,1.5358893381385874,-1.783160790922678,-0.29674101103367817,1.6099617567499458,-1.7160087796727022,-0.29674101103367817,1.681354552902976,-1.646000641160435,-0.29674101103367817,1.7499489003341784,-1.5732528970233874,-0.29674101103367817,1.8156306305217702,-1.4978866287022585,-0.29674101103367817,1.8782904227079327,-1.4200272759128365,-0.29674101103367817,1.9378239858524124,-1.3398044278639667,-0.29674101103367817,1.9941322322146722,-1.2573516075690985,-0.29674101103367817,2.047121442275657,-1.172806049610411,-0.29674101103367817,2.0967034207246824,-1.0863084717253972,-0.29674101103367817,2.1427956432518234,-0.9980028405960756,-0.29674101103367817,2.1853213939014844,-0.9080361322306494,-0.29674101103367817,2.224209892758542,-0.8165580873364517,-0.29674101103367817,2.259396413754523,-0.7237209620913209,-0.29674101103367817,2.2908223923977555,-0.6296792747282072,-0.29674101103367817,2.318435523248193,-0.5345895483548286,-0.29674101103367817,2.342189846974652,-0.4386100504364099,-0.29674101103367817,2.3620458268495836,-0.3419005293751018,-0.29674101103367817,2.3779704145540554,-0.24462194862452696,-0.29674101103367817,2.389937105183419,-0.14693621878200047,-0.29674101103367817,2.3979259813621043,-0.04900592810432419,-0.29674101103367817,2.4019237463941265,0.04900592810432545,-0.29674101103367817,2.4019237463941265,0.14693621878199958,-0.29674101103367817,2.3979259813621043,0.2446219486245261,-0.29674101103367817,2.3899371051834195,0.34190052937510096,-0.29674101103367817,2.377970414554056,0.43861005043640905,-0.29674101103367817,2.3620458268495836,0.5345895483548277,-0.29674101103367817,2.342189846974652,0.6296792747282064,-0.29674101103367817,2.3184355232481932,0.7237209620913201,-0.29674101103367817,2.2908223923977555,0.816558087336451,-0.29674101103367817,2.259396413754523,0.9080361322306466,-0.29674101103367817,2.224209892758543,0.9980028405960767,-0.29674101103367817,2.185321393901484,1.0863084717253984,-0.29674101103367817,2.1427956432518225,1.1728060496104102,-0.29674101103367817,2.096703420724683,1.257351607569096,-0.29674101103367817,2.0471214422756585,1.339804427863966,-0.29674101103367817,1.9941322322146726,1.4200272759128376,-0.29674101103367817,1.9378239858524118,1.4978866287022579,-0.29674101103367817,1.8782904227079331,1.5732528970233868,-0.29674101103367817,1.815630630521771,1.6460006411604342,-0.29674101103367817,1.7499489003341788,1.7160087796727008,-0.29674101103367817,1.6813545529029774,1.783160790922678,-0.29674101103367817,1.6099617567499458,1.8473449070148154,-0.29674101103367817,1.5358893381385863,1.908454299822153,-0.29674101103367817,1.4592605832995347,1.9663872587911997,-0.29674101103367817,1.3802030332327901,2.0210473602291064,-0.29674101103367817,1.2988482714283156,2.072343627791391,-0.29674101103367817,1.2153317048582994,2.12019068390308,-0.29674101103367817,1.1297923386056186,2.1645088918612614,-0.29674101103367817,1.0423725445035938,2.2052244883825165,-0.29674101103367817,0.9532178241721198,2.2422697063746253,-0.29674101103367817,0.8624765668445811,2.275582887728199,-0.29674101103367817,0.7702998023886244,2.3051085859405265,-0.29674101103367817,0.6768409499318347,2.330797658400799,-0.29674101103367817,0.5822555625107542,2.35260734818314,-0.29674101103367817,0.4867010681681883,2.3705013552112866,-0.29674101103367817,0.3903365079297808,2.384449896676485,-0.29674101103367817,0.2933222710959086,2.394429756608037,-0.29674101103367817,0.19581982828952532,2.40042432451399,-0.29674101103367817,0.09799146270424966,2.402423623027663,-0.29674101103367817,0.0,2.4223529979274407,-0.26761529317605,0.0,2.420337114174914,-0.26761529317605,-0.09880435372750324,2.4142928181568934,-0.26761529317605,-0.19744425735914628,2.404230170007595,-0.26761529317605,-0.295755534510024,2.390165918011789,-0.26761529317605,-0.39357455576157685,2.3721234707289325,-0.26761529317605,-0.49073851100660915,2.350132858031934,-0.26761529317605,-0.5870856804306451,2.3242306811253926,-0.26761529317605,-0.6824557036786003,2.294460051626505,-0.26761529317605,-0.7766898467587665,2.2608705198100334,-0.26761529317605,-0.8696312662398757,2.223517992136763,-0.26761529317605,-0.9611252703015131,2.1824646382027173,-0.26761529317605,-1.0510195762033843,2.1377787872639975,-0.26761529317605,-1.1391645637449106,2.0895348145094816,-0.26761529317605,-1.2254135242932838,2.0378130172706626,-0.26761529317605,-1.309622904965509,1.982699481374663,-0.26761529317605,-1.391652547558011,1.9242859378628732,-0.26761529317605,-1.4713659218261321,1.8626696103136842,-0.26761529317605,-1.5486303527252485,1.797953053023438,-0.26761529317605,-1.6233172412352914,1.7302439803149214,-0.26761529317605,-1.6953022784011227,1.6596550872575053,-0.26761529317605,-1.7644656522325228,1.5863038620973253,-0.26761529317605,-1.830692247119425,1.5103123907096894,-0.26761529317605,-1.893871835430487,1.4318071533991867,-0.26761529317605,-1.9538992609761072,1.3509188143857047,-0.26761529317605,-2.01067461403052,1.2677820043267278,-0.26761529317605,-2.0641033976216803,1.182535096237898,-0.26761529317605,-2.114096684812141,1.0953199751847826,-0.26761529317605,-2.16057126670917,1.0062818021291864,-0.26761529317605,-2.2034497909577375,0.9155687723230577,-0.26761529317605,-2.2426608904858774,0.823331868652116,-0.26761529317605,-2.2781393022881358,0.7297246103397377,-0.26761529317605,-2.309825976049403,0.6349027974293618,-0.26761529317605,-2.3376681724283346,0.5390242514706923,-0.26761529317605,-2.361619550836781,0.44224855284130354,-0.26761529317605,-2.381640246569123,0.3447367751408516,-0.26761529317605,-2.3976969371531394,0.24665121709996196,-0.26761529317605,-2.409762897811975,0.14815513245001227,-0.26761529317605,-2.4178180459448924,0.04941245820340595,-0.26761529317605,-2.421848974552781,-0.04941245820340565,-0.26761529317605,-2.421848974552781,-0.14815513245001197,-0.26761529317605,-2.417818045944893,-0.24665121709996113,-0.26761529317605,-2.409762897811975,-0.3447367751408513,-0.26761529317605,-2.3976969371531394,-0.44224855284130327,-0.26761529317605,-2.381640246569123,-0.5390242514706916,-0.26761529317605,-2.361619550836781,-0.6349027974293616,-0.26761529317605,-2.337668172428335,-0.7297246103397369,-0.26761529317605,-2.3098259760494035,-0.8233318686521157,-0.26761529317605,-2.278139302288136,-0.9155687723230573,-0.26761529317605,-2.242660890485878,-1.0062818021291857,-0.26761529317605,-2.2034497909577375,-1.0953199751847829,-0.26761529317605,-2.16057126670917,-1.1825350962378978,-0.26761529317605,-2.114096684812141,-1.2677820043267285,-0.26761529317605,-2.06410339762168,-1.350918814385704,-0.26761529317605,-2.010674614030521,-1.4318071533991863,-0.26761529317605,-1.9538992609761074,-1.5103123907096891,-0.26761529317605,-1.8938718354304875,-1.5863038620973255,-0.26761529317605,-1.830692247119425,-1.6596550872575055,-0.26761529317605,-1.7644656522325228,-1.7302439803149214,-0.26761529317605,-1.6953022784011227,-1.797953053023438,-0.26761529317605,-1.6233172412352914,-1.8626696103136835,-0.26761529317605,-1.5486303527252494,-1.9242859378628732,-0.26761529317605,-1.471365921826132,-1.9826994813746626,-0.26761529317605,-1.3916525475580117,-2.0378130172706626,-0.26761529317605,-1.3096229049655088,-2.089534814509481,-0.26761529317605,-1.225413524293284,-2.137778787263997,-0.26761529317605,-1.1391645637449108,-2.1824646382027173,-0.26761529317605,-1.0510195762033847,-2.223517992136763,-0.26761529317605,-0.9611252703015132,-2.2608705198100334,-0.26761529317605,-0.8696312662398759,-2.294460051626505,-0.26761529317605,-0.7766898467587665,-2.324230681125392,-0.26761529317605,-0.6824557036786012,-2.350132858031934,-0.26761529317605,-0.5870856804306449,-2.372123470728932,-0.26761529317605,-0.49073851100660987,-2.390165918011789,-0.26761529317605,-0.3935745557615775,-2.404230170007595,-0.26761529317605,-0.2957555345100246,-2.4142928181568934,-0.26761529317605,-0.19744425735914678,-2.420337114174914,-0.26761529317605,-0.09880435372750362,-2.4223529979274407,-0.26761529317605,-2.9665268453168356e-16,-2.420337114174914,-0.26761529317605,0.09880435372750303,-2.4142928181568934,-0.26761529317605,0.19744425735914617,-2.404230170007595,-0.26761529317605,0.29575553451002407,-2.390165918011789,-0.26761529317605,0.3935745557615759,-2.3721234707289325,-0.26761529317605,0.49073851100660826,-2.350132858031934,-0.26761529317605,0.5870856804306454,-2.3242306811253926,-0.26761529317605,0.6824557036786006,-2.294460051626505,-0.26761529317605,0.776689846758766,-2.2608705198100334,-0.26761529317605,0.8696312662398753,-2.223517992136763,-0.26761529317605,0.9611252703015126,-2.1824646382027177,-0.26761529317605,1.0510195762033834,-2.1377787872639975,-0.26761529317605,1.1391645637449104,-2.0895348145094816,-0.26761529317605,1.2254135242932835,-2.0378130172706626,-0.26761529317605,1.309622904965509,-1.9826994813746635,-0.26761529317605,1.3916525475580104,-1.9242859378628736,-0.26761529317605,1.4713659218261312,-1.862669610313684,-0.26761529317605,1.5486303527252487,-1.7979530530234378,-0.26761529317605,1.6233172412352919,-1.7302439803149219,-0.26761529317605,1.6953022784011222,-1.659655087257506,-0.26761529317605,1.7644656522325224,-1.5863038620973267,-0.26761529317605,1.8306922471194238,-1.5103123907096894,-0.26761529317605,1.893871835430487,-1.4318071533991859,-0.26761529317605,1.9538992609761077,-1.3509188143857047,-0.26761529317605,2.01067461403052,-1.267782004326729,-0.26761529317605,2.0641033976216794,-1.1825350962378982,-0.26761529317605,2.1140966848121407,-1.0953199751847815,-0.26761529317605,2.1605712667091708,-1.0062818021291862,-0.26761529317605,2.2034497909577375,-0.9155687723230584,-0.26761529317605,2.242660890485877,-0.8233318686521154,-0.26761529317605,2.278139302288136,-0.729724610339738,-0.26761529317605,2.309825976049403,-0.6349027974293632,-0.26761529317605,2.337668172428334,-0.5390242514706927,-0.26761529317605,2.361619550836781,-0.44224855284130277,-0.26761529317605,2.381640246569123,-0.3447367751408513,-0.26761529317605,2.3976969371531394,-0.2466512170999628,-0.26761529317605,2.4097628978119747,-0.14815513245001202,-0.26761529317605,2.417818045944893,-0.04941245820340464,-0.26761529317605,2.421848974552781,0.049412458203405896,-0.26761529317605,2.421848974552781,0.14815513245001113,-0.26761529317605,2.417818045944893,0.2466512170999619,-0.26761529317605,2.409762897811975,0.3447367751408505,-0.26761529317605,2.39769693715314,0.44224855284130193,-0.26761529317605,2.3816402465691233,0.5390242514706918,-0.26761529317605,2.361619550836781,0.6349027974293624,-0.26761529317605,2.3376681724283346,0.7297246103397371,-0.26761529317605,2.3098259760494035,0.8233318686521145,-0.26761529317605,2.278139302288136,0.9155687723230557,-0.26761529317605,2.2426608904858782,1.0062818021291873,-0.26761529317605,2.203449790957737,1.0953199751847826,-0.26761529317605,2.16057126670917,1.1825350962378975,-0.26761529317605,2.114096684812141,1.2677820043267265,-0.26761529317605,2.064103397621681,1.3509188143857038,-0.26761529317605,2.010674614030521,1.4318071533991867,-0.26761529317605,1.9538992609761068,1.5103123907096887,-0.26761529317605,1.8938718354304875,1.586303862097326,-0.26761529317605,1.8306922471194245,1.659655087257505,-0.26761529317605,1.764465652232523,1.7302439803149205,-0.26761529317605,1.6953022784011238,1.7979530530234378,-0.26761529317605,1.6233172412352919,1.8626696103136846,-0.26761529317605,1.5486303527252476,1.9242859378628727,-0.26761529317605,1.4713659218261321,1.9826994813746623,-0.26761529317605,1.3916525475580122,2.037813017270661,-0.26761529317605,1.3096229049655106,2.089534814509481,-0.26761529317605,1.2254135242932844,2.1377787872639975,-0.26761529317605,1.1391645637449102,2.1824646382027173,-0.26761529317605,1.0510195762033852,2.2235179921367627,-0.26761529317605,0.9611252703015145,2.2608705198100334,-0.26761529317605,0.8696312662398763,2.2944600516265043,-0.26761529317605,0.7766898467587678,2.3242306811253926,-0.26761529317605,0.6824557036786005,2.350132858031934,-0.26761529317605,0.5870856804306462,2.3721234707289325,-0.26761529317605,0.49073851100660915,2.390165918011789,-0.26761529317605,0.3935745557615778,2.404230170007595,-0.26761529317605,0.295755534510026,2.4142928181568934,-0.26761529317605,0.19744425735914703,2.420337114174914,-0.26761529317605,0.09880435372750285,2.4223529979274407,-0.26761529317605,0.0,2.4401782314365454,-0.2371563293892764,0.0,2.4381475135130093,-0.2371563293892764,-0.0995314197985572,2.432058739671975,-0.2371563293892764,-0.1988971793715325,2.4219220440766045,-0.2371563293892764,-0.2979318942184406,2.4077542982563345,-0.2371563293892764,-0.39647073083007217,2.3895790830258803,-0.2371563293892764,-0.4943496810376028,2.3674266492372955,-0.2371563293892764,-0.5914058349880038,2.341333867430416,-0.2371563293892764,-0.6874776522914159,2.311344166465485,-0.2371563293892764,-0.7824052308891849,2.2775074612401,-0.2371563293892764,-0.8760305731950582,2.2398800696107917,-0.2371563293892764,-0.968197849066575,2.198524618657512,-0.2371563293892764,-1.0587536551689583,2.153509940447037,-0.2371563293892764,-1.1475472702998282,2.104910957468787,-0.2371563293892764,-1.2344309062497665,2.0528085579337376,-0.2371563293892764,-1.3192599537812082,1.997289461143971,-0.2371563293892764,-1.4018932233162453,1.938446073156961,-0.2371563293892764,-1.482193179932746,1.8763763329848069,-0.2371563293892764,-1.560026172277656,1.8111835495844149,-0.2371563293892764,-1.635262655016492,1.7429762299099327,-0.2371563293892764,-1.7077774044487601,1.6718678983136357,-0.2371563293892764,-1.7774497269304506,1.5979769075958437,-0.2371563293892764,-1.844163659756696,1.5214262420183666,-0.2371563293892764,-1.9078081641702491,1.4423433126093366,-0.2371563293892764,-1.9682773101745377,1.360859745100126,-0.2371563293892764,-2.025470452843686,1.2771111608473094,-0.2371563293892764,-2.0792923998360657,1.1912369511042973,-0.2371563293892764,-2.1296535698325503,1.1033800450183537,-0.2371563293892764,-2.1764701416357823,1.0136866717391328,-0.2371563293892764,-2.2196641936822843,0.922306117034694,-0.2371563293892764,-2.2591638337352102,0.8293904748200727,-0.2371563293892764,-2.294903318541877,0.735094394011967,-0.2371563293892764,-2.326823163256926,0.6395748211308807,-0.2371563293892764,-2.3548702404489696,0.5429907390791278,-0.2371563293892764,-2.378997868525966,0.44550290252948055,-0.2371563293892764,-2.399165889432116,0.3472735703648834,-0.2371563293892764,-2.4153407354869856,0.24846623561455228,-0.2371563293892764,-2.4274954852556,0.14924535333596628,-0.2371563293892764,-2.4356099083565104,0.049776066895651956,-0.2371563293892764,-2.4396704991332716,-0.04977606689565166,-0.2371563293892764,-2.4396704991332716,-0.14924535333596597,-0.2371563293892764,-2.435609908356511,-0.24846623561455147,-0.2371563293892764,-2.4274954852556,-0.34727357036488316,-0.2371563293892764,-2.4153407354869856,-0.44550290252948027,-0.2371563293892764,-2.399165889432116,-0.5429907390791269,-0.2371563293892764,-2.378997868525966,-0.6395748211308804,-0.2371563293892764,-2.35487024044897,-0.7350943940119661,-0.2371563293892764,-2.326823163256926,-0.8293904748200724,-0.2371563293892764,-2.2949033185418775,-0.9223061170346937,-0.2371563293892764,-2.2591638337352102,-1.0136866717391322,-0.2371563293892764,-2.2196641936822847,-1.103380045018354,-0.2371563293892764,-2.1764701416357823,-1.1912369511042973,-0.2371563293892764,-2.1296535698325503,-1.2771111608473098,-0.2371563293892764,-2.0792923998360653,-1.3608597451001256,-0.2371563293892764,-2.0254704528436864,-1.4423433126093361,-0.2371563293892764,-1.9682773101745379,-1.5214262420183664,-0.2371563293892764,-1.9078081641702498,-1.5979769075958439,-0.2371563293892764,-1.844163659756696,-1.671867898313636,-0.2371563293892764,-1.7774497269304506,-1.7429762299099327,-0.2371563293892764,-1.7077774044487601,-1.8111835495844149,-0.2371563293892764,-1.635262655016492,-1.8763763329848064,-0.2371563293892764,-1.560026172277657,-1.938446073156961,-0.2371563293892764,-1.4821931799327455,-1.9972894611439707,-0.2371563293892764,-1.4018932233162462,-2.0528085579337376,-0.2371563293892764,-1.319259953781208,-2.104910957468787,-0.2371563293892764,-1.2344309062497667,-2.1535099404470364,-0.2371563293892764,-1.1475472702998284,-2.1985246186575114,-0.2371563293892764,-1.0587536551689587,-2.2398800696107912,-0.2371563293892764,-0.9681978490665751,-2.2775074612401,-0.2371563293892764,-0.8760305731950584,-2.311344166465485,-0.2371563293892764,-0.7824052308891849,-2.341333867430416,-0.2371563293892764,-0.6874776522914168,-2.3674266492372955,-0.2371563293892764,-0.5914058349880036,-2.38957908302588,-0.2371563293892764,-0.4943496810376035,-2.4077542982563345,-0.2371563293892764,-0.39647073083007284,-2.4219220440766045,-0.2371563293892764,-0.2979318942184412,-2.432058739671975,-0.2371563293892764,-0.198897179371533,-2.4381475135130093,-0.2371563293892764,-0.0995314197985576,-2.4401782314365454,-0.2371563293892764,-2.9883564604778146e-16,-2.4381475135130093,-0.2371563293892764,0.09953141979855701,-2.432058739671975,-0.2371563293892764,0.1988971793715324,-2.4219220440766045,-0.2371563293892764,0.2979318942184406,-2.407754298256335,-0.2371563293892764,0.3964707308300712,-2.3895790830258807,-0.2371563293892764,0.4943496810376019,-2.3674266492372955,-0.2371563293892764,0.5914058349880041,-2.341333867430416,-0.2371563293892764,0.6874776522914162,-2.311344166465485,-0.2371563293892764,0.7824052308891843,-2.2775074612401,-0.2371563293892764,0.8760305731950578,-2.2398800696107917,-0.2371563293892764,0.9681978490665746,-2.1985246186575123,-0.2371563293892764,1.0587536551689571,-2.153509940447037,-0.2371563293892764,1.1475472702998277,-2.104910957468787,-0.2371563293892764,1.2344309062497663,-2.0528085579337376,-0.2371563293892764,1.3192599537812082,-1.9972894611439715,-0.2371563293892764,1.4018932233162449,-1.9384460731569617,-0.2371563293892764,1.482193179932745,-1.8763763329848067,-0.2371563293892764,1.5600261722776565,-1.8111835495844144,-0.2371563293892764,1.6352626550164922,-1.7429762299099332,-0.2371563293892764,1.7077774044487597,-1.6718678983136361,-0.2371563293892764,1.7774497269304503,-1.597976907595845,-0.2371563293892764,1.8441636597566948,-1.5214262420183666,-0.2371563293892764,1.9078081641702491,-1.4423433126093355,-0.2371563293892764,1.968277310174538,-1.360859745100126,-0.2371563293892764,2.025470452843686,-1.2771111608473105,-0.2371563293892764,2.079292399836065,-1.1912369511042975,-0.2371563293892764,2.12965356983255,-1.1033800450183526,-0.2371563293892764,2.1764701416357832,-1.0136866717391326,-0.2371563293892764,2.2196641936822847,-0.9223061170346948,-0.2371563293892764,2.25916383373521,-0.829390474820072,-0.2371563293892764,2.2949033185418775,-0.7350943940119672,-0.2371563293892764,2.326823163256926,-0.639574821130882,-0.2371563293892764,2.354870240448969,-0.5429907390791281,-0.2371563293892764,2.378997868525966,-0.4455029025294798,-0.2371563293892764,2.399165889432116,-0.34727357036488316,-0.2371563293892764,2.4153407354869856,-0.24846623561455314,-0.2371563293892764,2.4274954852555997,-0.14924535333596603,-0.2371563293892764,2.435609908356511,-0.04977606689565063,-0.2371563293892764,2.4396704991332716,0.0497760668956519,-0.2371563293892764,2.4396704991332716,0.14924535333596514,-0.2371563293892764,2.435609908356511,0.24846623561455225,-0.2371563293892764,2.4274954852556,0.34727357036488227,-0.2371563293892764,2.415340735486986,0.44550290252947894,-0.2371563293892764,2.399165889432116,0.5429907390791272,-0.2371563293892764,2.378997868525966,0.6395748211308813,-0.2371563293892764,2.3548702404489696,0.7350943940119664,-0.2371563293892764,2.326823163256926,0.8293904748200712,-0.2371563293892764,2.294903318541878,0.9223061170346919,-0.2371563293892764,2.2591638337352107,1.0136866717391337,-0.2371563293892764,2.2196641936822843,1.1033800450183537,-0.2371563293892764,2.1764701416357823,1.1912369511042968,-0.2371563293892764,2.1296535698325503,1.277111160847308,-0.2371563293892764,2.0792923998360666,1.3608597451001254,-0.2371563293892764,2.0254704528436864,1.4423433126093366,-0.2371563293892764,1.9682773101745372,1.5214262420183662,-0.2371563293892764,1.9078081641702498,1.5979769075958445,-0.2371563293892764,1.8441636597566957,1.6718678983136355,-0.2371563293892764,1.777449726930451,1.7429762299099318,-0.2371563293892764,1.707777404448761,1.8111835495844144,-0.2371563293892764,1.6352626550164922,1.8763763329848075,-0.2371563293892764,1.5600261722776554,1.9384460731569608,-0.2371563293892764,1.482193179932746,1.9972894611439704,-0.2371563293892764,1.4018932233162464,2.0528085579337363,-0.2371563293892764,1.31925995378121,2.104910957468787,-0.2371563293892764,1.2344309062497671,2.1535099404470373,-0.2371563293892764,1.1475472702998277,2.1985246186575114,-0.2371563293892764,1.058753655168959,2.239880069610791,-0.2371563293892764,0.9681978490665765,2.2775074612401,-0.2371563293892764,0.8760305731950587,2.3113441664654846,-0.2371563293892764,0.7824052308891862,2.341333867430416,-0.2371563293892764,0.6874776522914161,2.3674266492372955,-0.2371563293892764,0.591405834988005,2.3895790830258803,-0.2371563293892764,0.4943496810376028,2.4077542982563345,-0.2371563293892764,0.3964707308300731,2.4219220440766045,-0.2371563293892764,0.2979318942184426,2.432058739671975,-0.2371563293892764,0.19889717937153328,2.4381475135130093,-0.2371563293892764,0.09953141979855681,2.4401782314365454,-0.2371563293892764,0.0,2.455810519138271,-0.2055158646988107,0.0,2.453766792014664,-0.2055158646988107,-0.10016903871081852,2.447639012225946,-0.2055158646988107,-0.20017135594230173,2.43743737885683,-0.2055158646988107,-0.29984050770656306,2.423178871519269,-0.2055158646988107,-0.39901060453675646,2.4048872220915647,-0.2055158646988107,-0.49751658759372214,2.382592875219003,-0.2055158646988107,-0.5951945033901349,2.356332937641742,-0.2055158646988107,-0.6918817766749039,2.3261511164343087,-0.2055158646988107,-0.7874174810236328,2.2920976462594833,-0.2055158646988107,-0.8816426066847721,2.254229205757664,-0.2055158646988107,-0.9744003252356582,2.2126088232108603,-0.2055158646988107,-1.0655362506079444,2.167305771638341,-0.2055158646988107,-1.1548986960479777,2.1183954534985285,-0.2055158646988107,-1.2423389265844278,2.065959275189048,-0.2055158646988107,-1.3277114065829703,2.010084511553812,-0.2055158646988107,-1.4108740419759864,1.9508641606226584,-0.2055158646988107,-1.491688416764116,1.888396788825305,-0.2055158646988107,-1.5700200233960269,1.8227863669372555,-0.2055158646988107,-1.6457384866429616,1.754142097030705,-0.2055158646988107,-1.7187177805954372,1.6825782307184667,-0.2055158646988107,-1.7888364384209359,1.6082138789934395,-0.2055158646988107,-1.8559777545334584,1.5311728139801144,-0.2055158646988107,-1.920029978838456,1.4515832629280867,-0.2055158646988107,-1.9808865027298344,1.369577694790456,-0.2055158646988107,-2.038446036529455,1.2852925997423268,-0.2055158646988107,-2.092612778073812,1.1988682620063809,-0.2055158646988107,-2.1432965721672734,1.1104485263636348,-0.2055158646988107,-2.190413060636513,1.0201805587379944,-0.2055158646988107,-2.233883822736361,0.9282146012531028,-0.2055158646988107,-2.2736365056733954,0.8347037221691508,-0.2055158646988107,-2.309604945030026,0.7398035611158615,-0.2055158646988107,-2.34172927488864,0.6436720700456912,-0.2055158646988107,-2.3699560274725098,0.5464692503383896,-0.2055158646988107,-2.394238222137635,0.44835688649449396,-0.2055158646988107,-2.414535443567385,0.34949827686099594,-0.2055158646988107,-2.4308139090398053,0.2500579618373556,-0.2055158646988107,-2.443046524655626,0.1502014500142487,-0.2055158646988107,-2.4512129304333805,0.05009494270084881,-0.2055158646988107,-2.4552995341965884,-0.050094942700848515,-0.2055158646988107,-2.4552995341965884,-0.1502014500142484,-0.2055158646988107,-2.451212930433381,-0.2500579618373548,-0.2055158646988107,-2.443046524655626,-0.34949827686099566,-0.2055158646988107,-2.4308139090398053,-0.4483568864944937,-0.2055158646988107,-2.414535443567385,-0.5464692503383887,-0.2055158646988107,-2.394238222137635,-0.6436720700456909,-0.2055158646988107,-2.3699560274725098,-0.7398035611158608,-0.2055158646988107,-2.34172927488864,-0.8347037221691505,-0.2055158646988107,-2.3096049450300264,-0.9282146012531026,-0.2055158646988107,-2.273636505673396,-1.0201805587379937,-0.2055158646988107,-2.2338838227363613,-1.110448526363635,-0.2055158646988107,-2.190413060636513,-1.1988682620063809,-0.2055158646988107,-2.1432965721672734,-1.2852925997423275,-0.2055158646988107,-2.092612778073811,-1.3695776947904557,-0.2055158646988107,-2.0384460365294554,-1.451583262928086,-0.2055158646988107,-1.9808865027298348,-1.531172813980114,-0.2055158646988107,-1.9200299788384567,-1.60821387899344,-0.2055158646988107,-1.8559777545334584,-1.682578230718467,-0.2055158646988107,-1.7888364384209359,-1.754142097030705,-0.2055158646988107,-1.7187177805954372,-1.8227863669372555,-0.2055158646988107,-1.6457384866429616,-1.8883967888253044,-0.2055158646988107,-1.5700200233960278,-1.9508641606226584,-0.2055158646988107,-1.4916884167641158,-2.0100845115538117,-0.2055158646988107,-1.410874041975987,-2.065959275189048,-0.2055158646988107,-1.32771140658297,-2.118395453498528,-0.2055158646988107,-1.2423389265844282,-2.167305771638341,-0.2055158646988107,-1.154898696047978,-2.2126088232108603,-0.2055158646988107,-1.0655362506079449,-2.254229205757664,-0.2055158646988107,-0.9744003252356583,-2.2920976462594833,-0.2055158646988107,-0.8816426066847722,-2.3261511164343087,-0.2055158646988107,-0.7874174810236328,-2.356332937641742,-0.2055158646988107,-0.6918817766749048,-2.382592875219003,-0.2055158646988107,-0.5951945033901347,-2.4048872220915642,-0.2055158646988107,-0.49751658759372286,-2.423178871519269,-0.2055158646988107,-0.39901060453675713,-2.43743737885683,-0.2055158646988107,-0.2998405077065637,-2.447639012225946,-0.2055158646988107,-0.20017135594230223,-2.453766792014664,-0.2055158646988107,-0.1001690387108189,-2.455810519138271,-0.2055158646988107,-3.0075004915750833e-16,-2.453766792014664,-0.2055158646988107,0.10016903871081831,-2.447639012225946,-0.2055158646988107,0.20017135594230162,-2.43743737885683,-0.2055158646988107,0.2998405077065631,-2.423178871519269,-0.2055158646988107,0.3990106045367555,-2.404887222091565,-0.2055158646988107,0.49751658759372125,-2.382592875219003,-0.2055158646988107,0.5951945033901352,-2.356332937641742,-0.2055158646988107,0.6918817766749042,-2.3261511164343087,-0.2055158646988107,0.7874174810236323,-2.2920976462594838,-0.2055158646988107,0.8816426066847717,-2.254229205757664,-0.2055158646988107,0.9744003252356578,-2.2126088232108607,-0.2055158646988107,1.0655362506079433,-2.167305771638341,-0.2055158646988107,1.1548986960479775,-2.1183954534985285,-0.2055158646988107,1.2423389265844276,-2.065959275189048,-0.2055158646988107,1.3277114065829703,-2.010084511553812,-0.2055158646988107,1.4108740419759858,-1.950864160622659,-0.2055158646988107,1.4916884167641153,-1.8883967888253046,-0.2055158646988107,1.570020023396027,-1.8227863669372553,-0.2055158646988107,1.6457384866429619,-1.7541420970307053,-0.2055158646988107,1.718717780595437,-1.6825782307184673,-0.2055158646988107,1.7888364384209356,-1.6082138789934408,-0.2055158646988107,1.8559777545334573,-1.5311728139801144,-0.2055158646988107,1.920029978838456,-1.4515832629280856,-0.2055158646988107,1.980886502729835,-1.369577694790456,-0.2055158646988107,2.038446036529455,-1.285292599742328,-0.2055158646988107,2.092612778073811,-1.1988682620063813,-0.2055158646988107,2.143296572167273,-1.1104485263636337,-0.2055158646988107,2.190413060636514,-1.0201805587379942,-0.2055158646988107,2.2338838227363613,-0.9282146012531037,-0.2055158646988107,2.273636505673395,-0.83470372216915,-0.2055158646988107,2.3096049450300264,-0.7398035611158618,-0.2055158646988107,2.34172927488864,-0.6436720700456926,-0.2055158646988107,2.3699560274725093,-0.5464692503383899,-0.2055158646988107,2.394238222137635,-0.44835688649449323,-0.2055158646988107,2.414535443567385,-0.34949827686099566,-0.2055158646988107,2.4308139090398053,-0.25005796183735646,-0.2055158646988107,2.443046524655626,-0.15020145001424845,-0.2055158646988107,2.451212930433381,-0.05009494270084748,-0.2055158646988107,2.4552995341965884,0.050094942700848764,-0.2055158646988107,2.4552995341965884,0.15020145001424753,-0.2055158646988107,2.451212930433381,0.25005796183735557,-0.2055158646988107,2.443046524655626,0.34949827686099477,-0.2055158646988107,2.4308139090398058,0.44835688649449235,-0.2055158646988107,2.4145354435673854,0.546469250338389,-0.2055158646988107,2.394238222137635,0.6436720700456917,-0.2055158646988107,2.3699560274725098,0.739803561115861,-0.2055158646988107,2.34172927488864,0.8347037221691492,-0.2055158646988107,2.3096049450300264,0.9282146012531008,-0.2055158646988107,2.2736365056733963,1.0201805587379953,-0.2055158646988107,2.233883822736361,1.1104485263636346,-0.2055158646988107,2.190413060636513,1.1988682620063806,-0.2055158646988107,2.143296572167274,1.2852925997423255,-0.2055158646988107,2.0926127780738124,1.3695776947904554,-0.2055158646988107,2.0384460365294554,1.4515832629280867,-0.2055158646988107,1.9808865027298341,1.5311728139801137,-0.2055158646988107,1.9200299788384567,1.6082138789934404,-0.2055158646988107,1.8559777545334581,1.6825782307184665,-0.2055158646988107,1.788836438420936,1.754142097030704,-0.2055158646988107,1.7187177805954383,1.8227863669372553,-0.2055158646988107,1.6457384866429619,1.8883967888253055,-0.2055158646988107,1.5700200233960262,1.9508641606226582,-0.2055158646988107,1.491688416764116,2.0100845115538113,-0.2055158646988107,1.4108740419759875,2.0659592751890465,-0.2055158646988107,1.3277114065829718,2.118395453498528,-0.2055158646988107,1.2423389265844285,2.1673057716383415,-0.2055158646988107,1.1548986960479772,2.2126088232108603,-0.2055158646988107,1.065536250607945,2.254229205757663,-0.2055158646988107,0.9744003252356597,2.2920976462594833,-0.2055158646988107,0.8816426066847727,2.3261511164343083,-0.2055158646988107,0.7874174810236342,2.356332937641742,-0.2055158646988107,0.6918817766749041,2.382592875219003,-0.2055158646988107,0.5951945033901361,2.4048872220915647,-0.2055158646988107,0.49751658759372214,2.423178871519269,-0.2055158646988107,0.3990106045367574,2.43743737885683,-0.2055158646988107,0.29984050770656506,2.447639012225946,-0.2055158646988107,0.2001713559423025,2.453766792014664,-0.2055158646988107,0.10016903871081811,2.455810519138271,-0.2055158646988107,0.0,2.469171981761615,-0.17285153030817826,0.0,2.467117135220055,-0.17285153030817826,-0.10071403387893943,2.4609560156846535,-0.17285153030817826,-0.20126043918787134,2.450698877730841,-0.17285153030817826,-0.30147186635799983,2.4363627933524734,-0.17285153030817826,-0.40118152335858254,2.417971623547182,-0.17285153030817826,-0.5002234533058046,2.395555978602099,-0.17285153030817826,-0.5984328106816346,2.3691531671460533,-0.17285153030817826,-0.6956461357029197,2.338807134053043,-0.17285153030817826,-0.7917016263840642,2.304568387300326,-0.17285153030817826,-0.8864394078404654,2.2664939139028792,-0.17285153030817826,-0.9797017983844819,2.224647085064135,-0.17285153030817826,-1.0713335719710404,2.179097550700866,-0.17285153030817826,-1.1611822165560697,2.129921123517775,-0.17285153030817826,-1.2490981879377459,2.0771996528247274,-0.17285153030817826,-1.3349351586580573,2.021020888306654,-0.17285153030817826,-1.4185502615504195,1.9614783339728594,-0.17285153030817826,-1.4998043275279735,1.8986710925288275,-0.17285153030817826,-1.5785621172167947,1.8327037004295474,-0.17285153030817826,-1.654692546048487,1.7636859538889016,-0.17285153030817826,-1.7280689024375084,1.6917327261347033,-0.17285153030817826,-1.7985690586801013,1.616963776213545,-0.17285153030817826,-1.8660756742238007,1.5395035496636857,-0.17285153030817826,-1.9304763909692062,1.4594809713877324,-0.17285153030817826,-1.9916640202789482,1.377029231069863,-0.17285153030817826,-2.049536721382596,1.292285561494744,-0.17285153030817826,-2.1039981708805726,1.2053910101371008,-0.17285153030817826,-2.154957723064942,1.1164902044021185,-0.17285153030817826,-2.2023305607902497,1.025731110907395,-0.17285153030817826,-2.2460378366432883,0.9332647892071138,-0.17285153030817826,-2.286006804176834,0.8392451403683213,-0.17285153030817826,-2.322170938988932,0.743828650817788,-0.17285153030817826,-2.354470049446195,0.6471741318857975,-0.17285153030817826,-2.3828503768668408,0.5494424554803584,-0.17285153030817826,-2.407264684996707,0.4507962863317897,-0.17285153030817826,-2.42767233862934,0.3513998112533317,-0.17285153030817826,-2.4440393712392816,0.25141846586839617,-0.17285153030817826,-2.456338541516003,0.15101865925930139,-0.17285153030817826,-2.4645493787043713,0.05036749699577496,-0.17285153030817826,-2.468658216676209,-0.050367496995774655,-0.17285153030817826,-2.468658216676209,-0.15101865925930108,-0.17285153030817826,-2.4645493787043717,-0.25141846586839534,-0.17285153030817826,-2.456338541516003,-0.3513998112533314,-0.17285153030817826,-2.4440393712392816,-0.4507962863317894,-0.17285153030817826,-2.42767233862934,-0.5494424554803575,-0.17285153030817826,-2.407264684996707,-0.6471741318857973,-0.17285153030817826,-2.3828503768668408,-0.7438286508177873,-0.17285153030817826,-2.3544700494461956,-0.839245140368321,-0.17285153030817826,-2.3221709389889322,-0.9332647892071135,-0.17285153030817826,-2.2860068041768344,-1.0257311109073943,-0.17285153030817826,-2.2460378366432883,-1.1164902044021185,-0.17285153030817826,-2.2023305607902497,-1.2053910101371006,-0.17285153030817826,-2.154957723064942,-1.2922855614947444,-0.17285153030817826,-2.103998170880572,-1.3770292310698626,-0.17285153030817826,-2.0495367213825966,-1.4594809713877317,-0.17285153030817826,-1.9916640202789484,-1.5395035496636855,-0.17285153030817826,-1.9304763909692066,-1.6169637762135451,-0.17285153030817826,-1.8660756742238007,-1.6917327261347035,-0.17285153030817826,-1.7985690586801013,-1.7636859538889016,-0.17285153030817826,-1.7280689024375084,-1.8327037004295474,-0.17285153030817826,-1.654692546048487,-1.8986710925288268,-0.17285153030817826,-1.5785621172167954,-1.9614783339728594,-0.17285153030817826,-1.4998043275279733,-2.0210208883066536,-0.17285153030817826,-1.4185502615504204,-2.077199652824728,-0.17285153030817826,-1.3349351586580571,-2.1299211235177746,-0.17285153030817826,-1.249098187937746,-2.179097550700866,-0.17285153030817826,-1.1611822165560701,-2.2246470850641344,-0.17285153030817826,-1.0713335719710408,-2.2664939139028792,-0.17285153030817826,-0.979701798384482,-2.304568387300326,-0.17285153030817826,-0.8864394078404655,-2.338807134053043,-0.17285153030817826,-0.7917016263840642,-2.3691531671460533,-0.17285153030817826,-0.6956461357029207,-2.395555978602099,-0.17285153030817826,-0.5984328106816343,-2.417971623547182,-0.17285153030817826,-0.5002234533058054,-2.4363627933524734,-0.17285153030817826,-0.4011815233585832,-2.450698877730841,-0.17285153030817826,-0.3014718663580005,-2.4609560156846535,-0.17285153030817826,-0.20126043918787184,-2.467117135220055,-0.17285153030817826,-0.10071403387893982,-2.469171981761615,-0.17285153030817826,-3.0238635640086884e-16,-2.467117135220055,-0.17285153030817826,0.10071403387893922,-2.4609560156846535,-0.17285153030817826,0.20126043918787123,-2.450698877730841,-0.17285153030817826,0.3014718663579999,-2.436362793352474,-0.17285153030817826,0.40118152335858154,-2.4179716235471824,-0.17285153030817826,0.5002234533058038,-2.395555978602099,-0.17285153030817826,0.5984328106816348,-2.3691531671460533,-0.17285153030817826,0.6956461357029201,-2.338807134053043,-0.17285153030817826,0.7917016263840636,-2.304568387300326,-0.17285153030817826,0.8864394078404649,-2.2664939139028792,-0.17285153030817826,0.9797017983844815,-2.2246470850641353,-0.17285153030817826,1.0713335719710393,-2.179097550700866,-0.17285153030817826,1.1611822165560695,-2.129921123517775,-0.17285153030817826,1.2490981879377456,-2.0771996528247274,-0.17285153030817826,1.3349351586580573,-2.0210208883066545,-0.17285153030817826,1.418550261550419,-1.96147833397286,-0.17285153030817826,1.4998043275279727,-1.898671092528827,-0.17285153030817826,1.578562117216795,-1.8327037004295472,-0.17285153030817826,1.6546925460484874,-1.7636859538889023,-0.17285153030817826,1.7280689024375082,-1.6917327261347037,-0.17285153030817826,1.798569058680101,-1.6169637762135463,-0.17285153030817826,1.8660756742237998,-1.5395035496636857,-0.17285153030817826,1.9304763909692062,-1.4594809713877313,-0.17285153030817826,1.9916640202789488,-1.377029231069863,-0.17285153030817826,2.049536721382596,-1.292285561494745,-0.17285153030817826,2.1039981708805717,-1.205391010137101,-0.17285153030817826,2.154957723064942,-1.1164902044021172,-0.17285153030817826,2.2023305607902506,-1.0257311109073948,-0.17285153030817826,2.2460378366432883,-0.9332647892071145,-0.17285153030817826,2.2860068041768336,-0.8392451403683207,-0.17285153030817826,2.3221709389889322,-0.7438286508177883,-0.17285153030817826,2.354470049446195,-0.6471741318857989,-0.17285153030817826,2.3828503768668403,-0.5494424554803587,-0.17285153030817826,2.407264684996707,-0.4507962863317889,-0.17285153030817826,2.42767233862934,-0.3513998112533314,-0.17285153030817826,2.4440393712392816,-0.251418465868397,-0.17285153030817826,2.4563385415160024,-0.15101865925930114,-0.17285153030817826,2.4645493787043717,-0.05036749699577362,-0.17285153030817826,2.468658216676209,0.050367496995774905,-0.17285153030817826,2.468658216676209,0.15101865925930022,-0.17285153030817826,2.4645493787043717,0.2514184658683961,-0.17285153030817826,2.456338541516003,0.35139981125333053,-0.17285153030817826,2.444039371239282,0.45079628633178803,-0.17285153030817826,2.42767233862934,0.5494424554803579,-0.17285153030817826,2.407264684996707,0.6471741318857981,-0.17285153030817826,2.3828503768668408,0.7438286508177875,-0.17285153030817826,2.3544700494461956,0.8392451403683198,-0.17285153030817826,2.3221709389889322,0.9332647892071118,-0.17285153030817826,2.286006804176835,1.0257311109073959,-0.17285153030817826,2.246037836643288,1.1164902044021183,-0.17285153030817826,2.2023305607902497,1.2053910101371004,-0.17285153030817826,2.1549577230649426,1.2922855614947426,-0.17285153030817826,2.1039981708805735,1.3770292310698622,-0.17285153030817826,2.0495367213825966,1.4594809713877324,-0.17285153030817826,1.991664020278948,1.5395035496636853,-0.17285153030817826,1.9304763909692066,1.6169637762135458,-0.17285153030817826,1.8660756742238005,1.691732726134703,-0.17285153030817826,1.7985690586801015,1.763685953888901,-0.17285153030817826,1.7280689024375095,1.8327037004295472,-0.17285153030817826,1.6546925460484874,1.898671092528828,-0.17285153030817826,1.5785621172167938,1.9614783339728592,-0.17285153030817826,1.4998043275279735,2.021020888306653,-0.17285153030817826,1.4185502615504206,2.0771996528247265,-0.17285153030817826,1.334935158658059,2.1299211235177746,-0.17285153030817826,1.2490981879377463,2.1790975507008663,-0.17285153030817826,1.1611822165560695,2.2246470850641344,-0.17285153030817826,1.071333571971041,2.266493913902879,-0.17285153030817826,0.9797017983844833,2.304568387300326,-0.17285153030817826,0.8864394078404659,2.3388071340530425,-0.17285153030817826,0.7917016263840656,2.3691531671460533,-0.17285153030817826,0.69564613570292,2.395555978602099,-0.17285153030817826,0.5984328106816357,2.417971623547182,-0.17285153030817826,0.5002234533058046,2.4363627933524734,-0.17285153030817826,0.4011815233585835,2.450698877730841,-0.17285153030817826,0.3014718663580019,2.4609560156846535,-0.17285153030817826,0.20126043918787212,2.467117135220055,-0.17285153030817826,0.10071403387893904,2.469171981761615,-0.17285153030817826,0.0,2.4801960531718894,-0.13932605828824612,0.0,2.4781320323908766,-0.13932605828824612,-0.10116369016440648,2.471943405406732,-0.13932605828824612,-0.20215900335029446,2.461640472578321,-0.13932605828824612,-0.30281784282600926,2.4472403821205475,-0.13932605828824612,-0.4029726718871805,2.4287671015628463,-0.13932605828824612,-0.5024567927050324,2.406251377857593,-0.13932605828824612,-0.6011046237784683,2.3797306862048364,-0.13932605828824612,-0.6987519755281388,2.349249167678528,-0.13932605828824612,-0.7952363235737924,2.314857555758059,-0.13932605828824612,-0.8903970792400666,2.276613091887392,-0.13932605828824612,-0.9840758568404908,2.2345794302023254,-0.13932605828824612,-1.0761167372948293,2.1888265315844664,-0.13932605828824612,-1.1663665276410036,2.1394305472182498,-0.13932605828824612,-1.2546750160096591,2.086473691844804,-0.13932605828824612,-1.340895221636997,2.030044106923631,-0.13932605828824612,-1.4248836394997508,1.9702357139298425,-0.13932605828824612,-1.5065004791651353,1.9071480580311366,-0.13932605828824612,-1.5856098974582247,1.840886142404689,-0.13932605828824612,-1.6620802245595128,1.7715602534697263,-0.13932605828824612,-1.7357841831563325,1.6992857773266667,-0.13932605828824612,-1.8065991002833817,1.6241830077083426,-0.13932605828824612,-1.8744071114997667,1.5463769457629544,-0.13932605828824612,-1.9390953570627345,1.4659970920019945,-0.13932605828824612,-2.000556169771575,1.3831772307594268,-0.13932605828824612,-2.0586872541690497,1.2980552075208667,-0.13932605828824612,-2.113391856802088,1.210772699493374,-0.13932605828824612,-2.164578927258358,1.1214749797977228,-0.13932605828824612,-2.2121632697106954,1.030310675675628,-0.13932605828824612,-2.256065684717147,0.9374315211143719,-0.13932605828824612,-2.296213101040627,0.8429921043005564,-0.13932605828824612,-2.33253869726878,0.7471496103233196,-0.13932605828824612,-2.3649820130316277,0.6500635595552713,-0.13932605828824612,-2.393489049631886,0.5518955421465738,-0.13932605828824612,-2.4180123599204695,0.4528089490740838,-0.13932605828824612,-2.4385111272675877,0.35296870019319804,-0.13932605828824612,-2.454951233497998,0.25254096974502666,-0.13932605828824612,-2.4673053156773403,0.15169290977577235,-0.13932605828824612,-2.4755528116550405,0.050592371928642965,-0.13932605828824612,-2.479679994287983,-0.05059237192864267,-0.13932605828824612,-2.479679994287983,-0.15169290977577204,-0.13932605828824612,-2.475552811655041,-0.2525409697450258,-0.13932605828824612,-2.4673053156773403,-0.35296870019319776,-0.13932605828824612,-2.454951233497998,-0.45280894907408353,-0.13932605828824612,-2.4385111272675877,-0.5518955421465729,-0.13932605828824612,-2.4180123599204695,-0.6500635595552711,-0.13932605828824612,-2.393489049631886,-0.7471496103233187,-0.13932605828824612,-2.3649820130316277,-0.8429921043005562,-0.13932605828824612,-2.33253869726878,-0.9374315211143717,-0.13932605828824612,-2.2962131010406273,-1.030310675675627,-0.13932605828824612,-2.256065684717147,-1.121474979797723,-0.13932605828824612,-2.2121632697106954,-1.2107726994933738,-0.13932605828824612,-2.164578927258358,-1.2980552075208673,-0.13932605828824612,-2.1133918568020875,-1.3831772307594261,-0.13932605828824612,-2.05868725416905,-1.4659970920019938,-0.13932605828824612,-2.000556169771575,-1.5463769457629541,-0.13932605828824612,-1.939095357062735,-1.6241830077083428,-0.13932605828824612,-1.8744071114997667,-1.699285777326667,-0.13932605828824612,-1.8065991002833817,-1.7715602534697263,-0.13932605828824612,-1.7357841831563325,-1.840886142404689,-0.13932605828824612,-1.6620802245595128,-1.9071480580311362,-0.13932605828824612,-1.5856098974582256,-1.9702357139298425,-0.13932605828824612,-1.506500479165135,-2.0300441069236306,-0.13932605828824612,-1.4248836394997515,-2.0864736918448044,-0.13932605828824612,-1.3408952216369967,-2.1394305472182493,-0.13932605828824612,-1.2546750160096596,-2.1888265315844664,-0.13932605828824612,-1.166366527641004,-2.234579430202325,-0.13932605828824612,-1.0761167372948297,-2.276613091887392,-0.13932605828824612,-0.984075856840491,-2.314857555758059,-0.13932605828824612,-0.8903970792400667,-2.349249167678528,-0.13932605828824612,-0.7952363235737924,-2.379730686204836,-0.13932605828824612,-0.6987519755281398,-2.406251377857593,-0.13932605828824612,-0.6011046237784681,-2.4287671015628463,-0.13932605828824612,-0.5024567927050332,-2.4472403821205475,-0.13932605828824612,-0.4029726718871812,-2.461640472578321,-0.13932605828824612,-0.3028178428260099,-2.471943405406732,-0.13932605828824612,-0.202159003350295,-2.4781320323908766,-0.13932605828824612,-0.10116369016440688,-2.4801960531718894,-0.13932605828824612,-3.037364157774853e-16,-2.4781320323908766,-0.13932605828824612,0.10116369016440627,-2.471943405406732,-0.13932605828824612,0.20215900335029438,-2.461640472578321,-0.13932605828824612,0.3028178428260093,-2.447240382120548,-0.13932605828824612,0.40297267188717956,-2.4287671015628467,-0.13932605828824612,0.5024567927050315,-2.406251377857593,-0.13932605828824612,0.6011046237784686,-2.3797306862048364,-0.13932605828824612,0.6987519755281393,-2.349249167678528,-0.13932605828824612,0.7952363235737918,-2.3148575557580595,-0.13932605828824612,0.8903970792400662,-2.276613091887392,-0.13932605828824612,0.9840758568404905,-2.234579430202326,-0.13932605828824612,1.0761167372948282,-2.1888265315844664,-0.13932605828824612,1.1663665276410033,-2.1394305472182498,-0.13932605828824612,1.254675016009659,-2.086473691844804,-0.13932605828824612,1.340895221636997,-2.030044106923631,-0.13932605828824612,1.4248836394997502,-1.970235713929843,-0.13932605828824612,1.5065004791651346,-1.9071480580311364,-0.13932605828824612,1.585609897458225,-1.8408861424046887,-0.13932605828824612,1.6620802245595132,-1.7715602534697268,-0.13932605828824612,1.7357841831563323,-1.6992857773266672,-0.13932605828824612,1.8065991002833812,-1.624183007708344,-0.13932605828824612,1.8744071114997656,-1.5463769457629544,-0.13932605828824612,1.9390953570627345,-1.4659970920019934,-0.13932605828824612,2.0005561697715755,-1.3831772307594268,-0.13932605828824612,2.0586872541690497,-1.2980552075208678,-0.13932605828824612,2.1133918568020875,-1.2107726994933743,-0.13932605828824612,2.164578927258358,-1.1214749797977217,-0.13932605828824612,2.2121632697106963,-1.0303106756756277,-0.13932605828824612,2.256065684717147,-0.9374315211143728,-0.13932605828824612,2.296213101040627,-0.8429921043005557,-0.13932605828824612,2.33253869726878,-0.7471496103233198,-0.13932605828824612,2.3649820130316277,-0.6500635595552727,-0.13932605828824612,2.3934890496318855,-0.5518955421465741,-0.13932605828824612,2.4180123599204695,-0.45280894907408303,-0.13932605828824612,2.4385111272675877,-0.35296870019319776,-0.13932605828824612,2.454951233497998,-0.25254096974502754,-0.13932605828824612,2.46730531567734,-0.1516929097757721,-0.13932605828824612,2.475552811655041,-0.050592371928641626,-0.13932605828824612,2.479679994287983,0.05059237192864292,-0.13932605828824612,2.479679994287983,0.15169290977577118,-0.13932605828824612,2.475552811655041,0.25254096974502666,-0.13932605828824612,2.4673053156773403,0.35296870019319687,-0.13932605828824612,2.454951233497998,0.45280894907408215,-0.13932605828824612,2.438511127267588,0.5518955421465732,-0.13932605828824612,2.4180123599204695,0.6500635595552718,-0.13932605828824612,2.393489049631886,0.7471496103233191,-0.13932605828824612,2.3649820130316277,0.8429921043005549,-0.13932605828824612,2.3325386972687805,0.9374315211143699,-0.13932605828824612,2.2962131010406277,1.0303106756756288,-0.13932605828824612,2.2560656847171465,1.1214749797977228,-0.13932605828824612,2.2121632697106954,1.2107726994933736,-0.13932605828824612,2.1645789272583587,1.2980552075208653,-0.13932605828824612,2.113391856802089,1.383177230759426,-0.13932605828824612,2.05868725416905,1.4659970920019945,-0.13932605828824612,2.0005561697715746,1.546376945762954,-0.13932605828824612,1.939095357062735,1.6241830077083435,-0.13932605828824612,1.8744071114997665,1.6992857773266663,-0.13932605828824612,1.8065991002833819,1.7715602534697255,-0.13932605828824612,1.7357841831563336,1.8408861424046887,-0.13932605828824612,1.6620802245595132,1.9071480580311373,-0.13932605828824612,1.5856098974582238,1.970235713929842,-0.13932605828824612,1.5065004791651353,2.03004410692363,-0.13932605828824612,1.424883639499752,2.086473691844803,-0.13932605828824612,1.3408952216369987,2.1394305472182493,-0.13932605828824612,1.2546750160096598,2.188826531584467,-0.13932605828824612,1.1663665276410033,2.234579430202325,-0.13932605828824612,1.07611673729483,2.2766130918873912,-0.13932605828824612,0.9840758568404924,2.314857555758059,-0.13932605828824612,0.8903970792400672,2.3492491676785274,-0.13932605828824612,0.7952363235737938,2.3797306862048364,-0.13932605828824612,0.6987519755281392,2.4062513778575925,-0.13932605828824612,0.6011046237784695,2.4287671015628463,-0.13932605828824612,0.5024567927050324,2.4472403821205475,-0.13932605828824612,0.4029726718871815,2.461640472578321,-0.13932605828824612,0.3028178428260113,2.471943405406732,-0.13932605828824612,0.20215900335029527,2.4781320323908766,-0.13932605828824612,0.10116369016440609,2.4801960531718894,-0.13932605828824612,0.0,2.4888278119998573,-0.10510647085423593,0.0,2.486756607863491,-0.10510647085423593,-0.1015157674022239,2.48054644276927,-0.10510647085423593,-0.20286257182811318,2.4702076529241195,-0.10510647085423593,-0.30387173152353253,2.4557574462234086,-0.10510647085423593,-0.40437512671067866,2.4372198736101134,-0.10510647085423593,-0.5042054794068588,2.41462578904439,-0.10510647085423593,-0.6031966318421843,2.388012798150206,-0.10510647085423593,-0.7011838230127808,2.357425195624493,-0.10510647085423593,-0.7980039629092192,2.322913891512988,-0.10510647085423593,-0.8934959039637405,2.2845363264754943,-0.10510647085423593,-0.9875007092644795,2.2423563761815664,-0.10510647085423593,-1.079861917090269,2.1964442449957637,-0.10510647085423593,-1.1704258013257336,2.1468763491294163,-0.10510647085423593,-1.2590416273232372,2.093735189453379,-0.10510647085423593,-1.345561902785829,2.0371092141834755,-0.10510647085423593,-1.4298426232536154,1.9770926716671722,-0.10510647085423593,-1.5117435117849725,1.9137854535165053,-0.10510647085423593,-1.591128252433669,1.8472929283483501,-0.10510647085423593,-1.6678647171333094,1.777725766408757,-0.10510647085423593,-1.741825185611459,1.7051997553732494,-0.10510647085423593,-1.8128865579674331,1.6298356076296643,-0.10510647085423593,-1.880930559559932,1.5517587593642965,-0.10510647085423593,-1.945843937863511,1.471099161785744,-0.10510647085423593,-2.0075186509662317,1.387991064833948,-0.10510647085423593,-2.0658520473947597,1.3025727937344158,-0.10510647085423593,-2.1207470369676145,1.214986518769535,-0.10510647085423593,-2.1721122523921923,1.1253780186501692,-0.10510647085423593,-2.219862201336609,1.0338964378813773,-0.10510647085423593,-2.26391740872325,0.9406940385261088,-0.10510647085423593,-2.3042045490071916,0.845925946780024,-0.10510647085423593,-2.340656568219335,0.7497498947792492,-0.10510647085423593,-2.3732127955711224,0.6523259580708062,-0.10510647085423593,-2.4018190444350718,0.5538162891826588,-0.10510647085423593,-2.426427702533077,0.45438484773683446,-0.10510647085423593,-2.446997811182342,0.3541971275548143,-0.10510647085423593,-2.4634951334670654,0.25341988120939757,-0.10510647085423593,-2.4758922112224133,0.15222084248150447,-0.10510647085423593,-2.484168410735918,0.05076844718384886,-0.10510647085423593,-2.4883099570902614,-0.050768447183848556,-0.10510647085423593,-2.4883099570902614,-0.15222084248150416,-0.10510647085423593,-2.4841684107359185,-0.25341988120939674,-0.10510647085423593,-2.4758922112224133,-0.35419712755481403,-0.10510647085423593,-2.4634951334670654,-0.4543848477368342,-0.10510647085423593,-2.446997811182342,-0.553816289182658,-0.10510647085423593,-2.426427702533077,-0.6523259580708058,-0.10510647085423593,-2.401819044435072,-0.7497498947792485,-0.10510647085423593,-2.3732127955711224,-0.8459259467800238,-0.10510647085423593,-2.3406565682193357,-0.9406940385261086,-0.10510647085423593,-2.304204549007192,-1.0338964378813766,-0.10510647085423593,-2.2639174087232505,-1.1253780186501694,-0.10510647085423593,-2.219862201336609,-1.214986518769535,-0.10510647085423593,-2.1721122523921923,-1.3025727937344163,-0.10510647085423593,-2.120747036967614,-1.3879910648339473,-0.10510647085423593,-2.06585204739476,-1.4710991617857434,-0.10510647085423593,-2.007518650966232,-1.551758759364296,-0.10510647085423593,-1.9458439378635117,-1.6298356076296645,-0.10510647085423593,-1.880930559559932,-1.7051997553732496,-0.10510647085423593,-1.8128865579674331,-1.777725766408757,-0.10510647085423593,-1.741825185611459,-1.8472929283483501,-0.10510647085423593,-1.6678647171333094,-1.9137854535165046,-0.10510647085423593,-1.5911282524336696,-1.9770926716671722,-0.10510647085423593,-1.5117435117849722,-2.0371092141834746,-0.10510647085423593,-1.4298426232536163,-2.093735189453379,-0.10510647085423593,-1.3455619027858288,-2.146876349129416,-0.10510647085423593,-1.2590416273232377,-2.1964442449957637,-0.10510647085423593,-1.1704258013257338,-2.242356376181566,-0.10510647085423593,-1.0798619170902692,-2.2845363264754943,-0.10510647085423593,-0.9875007092644796,-2.322913891512988,-0.10510647085423593,-0.8934959039637406,-2.357425195624493,-0.10510647085423593,-0.7980039629092192,-2.388012798150206,-0.10510647085423593,-0.7011838230127818,-2.41462578904439,-0.10510647085423593,-0.6031966318421841,-2.437219873610113,-0.10510647085423593,-0.5042054794068596,-2.4557574462234086,-0.10510647085423593,-0.4043751267106793,-2.4702076529241195,-0.10510647085423593,-0.30387173152353314,-2.48054644276927,-0.10510647085423593,-0.20286257182811368,-2.486756607863491,-0.10510647085423593,-0.10151576740222429,-2.4888278119998573,-0.10510647085423593,-3.047935013594536e-16,-2.486756607863491,-0.10510647085423593,0.10151576740222369,-2.48054644276927,-0.10510647085423593,0.20286257182811307,-2.4702076529241195,-0.10510647085423593,0.30387173152353253,-2.455757446223409,-0.10510647085423593,0.4043751267106777,-2.4372198736101134,-0.10510647085423593,0.5042054794068579,-2.41462578904439,-0.10510647085423593,0.6031966318421845,-2.388012798150206,-0.10510647085423593,0.7011838230127813,-2.357425195624493,-0.10510647085423593,0.7980039629092186,-2.3229138915129885,-0.10510647085423593,0.89349590396374,-2.2845363264754943,-0.10510647085423593,0.9875007092644791,-2.242356376181567,-0.10510647085423593,1.079861917090268,-2.1964442449957637,-0.10510647085423593,1.1704258013257332,-2.1468763491294163,-0.10510647085423593,1.259041627323237,-2.093735189453379,-0.10510647085423593,1.345561902785829,-2.0371092141834755,-0.10510647085423593,1.429842623253615,-1.9770926716671726,-0.10510647085423593,1.5117435117849716,-1.9137854535165049,-0.10510647085423593,1.5911282524336692,-1.84729292834835,-0.10510647085423593,1.6678647171333096,-1.7777257664087576,-0.10510647085423593,1.7418251856114588,-1.7051997553732499,-0.10510647085423593,1.812886557967433,-1.6298356076296656,-0.10510647085423593,1.8809305595599308,-1.5517587593642965,-0.10510647085423593,1.945843937863511,-1.471099161785743,-0.10510647085423593,2.007518650966232,-1.387991064833948,-0.10510647085423593,2.0658520473947597,-1.302572793734417,-0.10510647085423593,2.1207470369676136,-1.2149865187695355,-0.10510647085423593,2.172112252392192,-1.125378018650168,-0.10510647085423593,2.21986220133661,-1.033896437881377,-0.10510647085423593,2.2639174087232505,-0.9406940385261096,-0.10510647085423593,2.3042045490071916,-0.8459259467800233,-0.10510647085423593,2.3406565682193357,-0.7497498947792496,-0.10510647085423593,2.3732127955711224,-0.6523259580708075,-0.10510647085423593,2.4018190444350713,-0.5538162891826591,-0.10510647085423593,2.426427702533077,-0.45438484773683374,-0.10510647085423593,2.446997811182342,-0.35419712755481403,-0.10510647085423593,2.4634951334670654,-0.2534198812093984,-0.10510647085423593,2.4758922112224133,-0.15222084248150422,-0.10510647085423593,2.4841684107359185,-0.050768447183847515,-0.10510647085423593,2.4883099570902614,0.05076844718384881,-0.10510647085423593,2.4883099570902614,0.1522208424815033,-0.10510647085423593,2.4841684107359185,0.2534198812093975,-0.10510647085423593,2.4758922112224133,0.35419712755481314,-0.10510647085423593,2.463495133467066,0.45438484773683285,-0.10510647085423593,2.446997811182342,0.5538162891826582,-0.10510647085423593,2.426427702533077,0.6523259580708067,-0.10510647085423593,2.4018190444350718,0.7497498947792487,-0.10510647085423593,2.3732127955711224,0.8459259467800224,-0.10510647085423593,2.340656568219336,0.9406940385261067,-0.10510647085423593,2.3042045490071925,1.0338964378813782,-0.10510647085423593,2.26391740872325,1.1253780186501692,-0.10510647085423593,2.219862201336609,1.2149865187695346,-0.10510647085423593,2.1721122523921927,1.3025727937344143,-0.10510647085423593,2.1207470369676154,1.387991064833947,-0.10510647085423593,2.06585204739476,1.471099161785744,-0.10510647085423593,2.0075186509662313,1.5517587593642959,-0.10510647085423593,1.9458439378635117,1.6298356076296652,-0.10510647085423593,1.8809305595599317,1.7051997553732492,-0.10510647085423593,1.8128865579674334,1.7777257664087562,-0.10510647085423593,1.7418251856114602,1.84729292834835,-0.10510647085423593,1.6678647171333096,1.9137854535165058,-0.10510647085423593,1.591128252433668,1.9770926716671717,-0.10510647085423593,1.5117435117849725,2.0371092141834746,-0.10510647085423593,1.4298426232536166,2.0937351894533776,-0.10510647085423593,1.3455619027858308,2.146876349129416,-0.10510647085423593,1.2590416273232379,2.196444244995764,-0.10510647085423593,1.1704258013257332,2.242356376181566,-0.10510647085423593,1.0798619170902697,2.2845363264754934,-0.10510647085423593,0.9875007092644811,2.322913891512988,-0.10510647085423593,0.893495903963741,2.3574251956244923,-0.10510647085423593,0.7980039629092206,2.388012798150206,-0.10510647085423593,0.7011838230127811,2.4146257890443894,-0.10510647085423593,0.6031966318421854,2.4372198736101134,-0.10510647085423593,0.5042054794068588,2.4557574462234086,-0.10510647085423593,0.4043751267106796,2.4702076529241195,-0.10510647085423593,0.3038717315235345,2.48054644276927,-0.10510647085423593,0.20286257182811396,2.486756607863491,-0.10510647085423593,0.1015157674022235,2.4888278119998573,-0.10510647085423593,0.0,2.4950242552572273,-0.07036324826944557,0.0,2.4929478944367207,-0.07036324826944557,-0.10176851156130286,2.4867222678728718,-0.07036324826944557,-0.20336763947856143,2.476357737506695,-0.07036324826944557,-0.30462828203009057,2.4618715540761174,-0.07036324826944557,-0.4053819008696917,2.4432878284038297,-0.07036324826944557,-0.5054608015420969,2.4206374912672017,-0.07036324826944557,-0.6046984125938399,2.3939582419170535,-0.07036324826944557,-0.7029295628150025,2.363294485330977,-0.07036324826944557,-0.7999907561503916,2.3286972583056276,-0.07036324826944557,-0.8957204438225886,2.290224144511018,-0.07036324826944557,-0.9899592932139466,2.2479391786481804,-0.07036324826944557,-1.0825504530600063,2.201912739869725,-0.07036324826944557,-1.1733398145129492,2.152221434640688,-0.07036324826944557,-1.2621762676405626,2.0989479692346293,-0.07036324826944557,-1.348911952933811,2.042181012077204,-0.07036324826944557,-1.4334025074043961,1.9820150461663157,-0.07036324826944557,-1.5155073048627048,1.9185502118144955,-0.07036324826944557,-1.5950896899762208,1.8518921399752297,-0.07036324826944557,-1.6720172057188423,1.7821517764306658,-0.07036324826944557,-1.7461618138325279,1.709445197133305,-0.07036324826944557,-1.817400107934342,1.6338934150090358,-0.07036324826944557,-1.8856135189141998,1.555622178543063,-0.07036324826944557,-1.9506885122814497,1.47476176248396,-0.07036324826944557,-2.0125167771318297,1.3914467510142101,-0.07036324826944557,-2.070995406420275,1.305815813748116,-0.07036324826944557,-2.1260270682395435,1.2180114749299114,-0.07036324826944557,-2.1775201678195644,1.1281798762162232,-0.07036324826944557,-2.225388999977898,1.0364705334377031,-0.07036324826944557,-2.269553891767553,0.9430360877446883,-0.07036324826944557,-2.3099413350847406,0.8480320515510678,-0.07036324826944557,-2.346484109015863,0.7516165496992157,-0.07036324826944557,-2.379121391720088,0.6539500562767995,-0.07036324826944557,-2.4077988616612997,0.5551951275234961,-0.07036324826944557,-2.432468788020938,0.4555161312721738,-0.07036324826944557,-2.4530901101412304,0.3550789733748565,-0.07036324826944557,-2.4696285058666088,0.2540508215688038,-0.07036324826944557,-2.48205644866955,0.15259982724231355,-0.07036324826944557,-2.49035325346576,0.050894845563328045,-0.07036324826944557,-2.494505111042462,-0.05089484556332774,-0.07036324826944557,-2.494505111042462,-0.15259982724231322,-0.07036324826944557,-2.4903532534657606,-0.254050821568803,-0.07036324826944557,-2.48205644866955,-0.3550789733748562,-0.07036324826944557,-2.4696285058666088,-0.4555161312721735,-0.07036324826944557,-2.4530901101412304,-0.5551951275234953,-0.07036324826944557,-2.432468788020938,-0.6539500562767993,-0.07036324826944557,-2.4077988616613,-0.7516165496992149,-0.07036324826944557,-2.379121391720088,-0.8480320515510675,-0.07036324826944557,-2.3464841090158632,-0.9430360877446881,-0.07036324826944557,-2.309941335084741,-1.0364705334377025,-0.07036324826944557,-2.269553891767553,-1.1281798762162234,-0.07036324826944557,-2.225388999977898,-1.2180114749299114,-0.07036324826944557,-2.1775201678195644,-1.3058158137481164,-0.07036324826944557,-2.126027068239543,-1.3914467510142097,-0.07036324826944557,-2.0709954064202756,-1.4747617624839593,-0.07036324826944557,-2.0125167771318297,-1.5556221785430626,-0.07036324826944557,-1.9506885122814503,-1.6338934150090363,-0.07036324826944557,-1.8856135189141998,-1.7094451971333051,-0.07036324826944557,-1.817400107934342,-1.7821517764306658,-0.07036324826944557,-1.7461618138325279,-1.8518921399752297,-0.07036324826944557,-1.6720172057188423,-1.9185502118144948,-0.07036324826944557,-1.5950896899762217,-1.9820150461663157,-0.07036324826944557,-1.5155073048627046,-2.042181012077203,-0.07036324826944557,-1.433402507404397,-2.0989479692346293,-0.07036324826944557,-1.3489119529338105,-2.1522214346406874,-0.07036324826944557,-1.2621762676405628,-2.2019127398697247,-0.07036324826944557,-1.1733398145129494,-2.2479391786481804,-0.07036324826944557,-1.0825504530600067,-2.290224144511018,-0.07036324826944557,-0.9899592932139467,-2.3286972583056276,-0.07036324826944557,-0.8957204438225889,-2.363294485330977,-0.07036324826944557,-0.7999907561503916,-2.3939582419170535,-0.07036324826944557,-0.7029295628150035,-2.4206374912672017,-0.07036324826944557,-0.6046984125938398,-2.4432878284038297,-0.07036324826944557,-0.5054608015420977,-2.4618715540761174,-0.07036324826944557,-0.40538190086969245,-2.476357737506695,-0.07036324826944557,-0.30462828203009124,-2.4867222678728718,-0.07036324826944557,-0.20336763947856196,-2.4929478944367207,-0.07036324826944557,-0.10176851156130326,-2.4950242552572273,-0.07036324826944557,-3.0555234679957723e-16,-2.4929478944367207,-0.07036324826944557,0.10176851156130265,-2.4867222678728718,-0.07036324826944557,0.20336763947856132,-2.476357737506695,-0.07036324826944557,0.3046282820300906,-2.4618715540761174,-0.07036324826944557,0.4053819008696908,-2.44328782840383,-0.07036324826944557,0.505460801542096,-2.4206374912672017,-0.07036324826944557,0.6046984125938403,-2.3939582419170535,-0.07036324826944557,0.702929562815003,-2.363294485330977,-0.07036324826944557,0.7999907561503911,-2.3286972583056276,-0.07036324826944557,0.8957204438225883,-2.290224144511018,-0.07036324826944557,0.9899592932139462,-2.2479391786481813,-0.07036324826944557,1.0825504530600052,-2.201912739869725,-0.07036324826944557,1.173339814512949,-2.152221434640688,-0.07036324826944557,1.2621762676405623,-2.0989479692346293,-0.07036324826944557,1.348911952933811,-2.042181012077204,-0.07036324826944557,1.4334025074043955,-1.9820150461663164,-0.07036324826944557,1.515507304862704,-1.9185502118144953,-0.07036324826944557,1.595089689976221,-1.8518921399752295,-0.07036324826944557,1.6720172057188425,-1.7821517764306662,-0.07036324826944557,1.7461618138325277,-1.7094451971333053,-0.07036324826944557,1.8174001079343418,-1.6338934150090374,-0.07036324826944557,1.8856135189141987,-1.555622178543063,-0.07036324826944557,1.9506885122814497,-1.4747617624839588,-0.07036324826944557,2.01251677713183,-1.3914467510142101,-0.07036324826944557,2.070995406420275,-1.305815813748117,-0.07036324826944557,2.1260270682395426,-1.2180114749299118,-0.07036324826944557,2.177520167819564,-1.1281798762162218,-0.07036324826944557,2.225388999977899,-1.036470533437703,-0.07036324826944557,2.269553891767553,-0.9430360877446892,-0.07036324826944557,2.30994133508474,-0.8480320515510671,-0.07036324826944557,2.3464841090158632,-0.751616549699216,-0.07036324826944557,2.379121391720088,-0.6539500562768009,-0.07036324826944557,2.4077988616612997,-0.5551951275234964,-0.07036324826944557,2.432468788020938,-0.45551613127217305,-0.07036324826944557,2.4530901101412304,-0.3550789733748562,-0.07036324826944557,2.4696285058666088,-0.25405082156880465,-0.07036324826944557,2.4820564486695496,-0.15259982724231327,-0.07036324826944557,2.4903532534657606,-0.05089484556332669,-0.07036324826944557,2.494505111042462,0.050894845563327996,-0.07036324826944557,2.494505111042462,0.15259982724231236,-0.07036324826944557,2.4903532534657606,0.25405082156880376,-0.07036324826944557,2.48205644866955,0.35507897337485533,-0.07036324826944557,2.469628505866609,0.4555161312721721,-0.07036324826944557,2.453090110141231,0.5551951275234955,-0.07036324826944557,2.432468788020938,0.6539500562768,-0.07036324826944557,2.4077988616612997,0.7516165496992151,-0.07036324826944557,2.379121391720088,0.8480320515510663,-0.07036324826944557,2.3464841090158637,0.9430360877446863,-0.07036324826944557,2.3099413350847415,1.036470533437704,-0.07036324826944557,2.2695538917675524,1.128179876216223,-0.07036324826944557,2.225388999977898,1.218011474929911,-0.07036324826944557,2.1775201678195644,1.3058158137481146,-0.07036324826944557,2.1260270682395443,1.3914467510142092,-0.07036324826944557,2.0709954064202756,1.47476176248396,-0.07036324826944557,2.0125167771318293,1.5556221785430624,-0.07036324826944557,1.9506885122814503,1.6338934150090367,-0.07036324826944557,1.8856135189141994,1.7094451971333045,-0.07036324826944557,1.8174001079343423,1.782151776430665,-0.07036324826944557,1.746161813832529,1.8518921399752295,-0.07036324826944557,1.6720172057188425,1.918550211814496,-0.07036324826944557,1.59508968997622,1.9820150461663155,-0.07036324826944557,1.5155073048627048,2.042181012077203,-0.07036324826944557,1.4334025074043972,2.098947969234628,-0.07036324826944557,1.3489119529338125,2.1522214346406874,-0.07036324826944557,1.2621762676405632,2.2019127398697256,-0.07036324826944557,1.1733398145129488,2.2479391786481804,-0.07036324826944557,1.0825504530600072,2.290224144511017,-0.07036324826944557,0.989959293213948,2.3286972583056276,-0.07036324826944557,0.8957204438225892,2.363294485330976,-0.07036324826944557,0.7999907561503931,2.3939582419170535,-0.07036324826944557,0.7029295628150027,2.4206374912672013,-0.07036324826944557,0.6046984125938412,2.4432878284038297,-0.07036324826944557,0.5054608015420969,2.4618715540761174,-0.07036324826944557,0.4053819008696927,2.476357737506695,-0.07036324826944557,0.3046282820300926,2.4867222678728718,-0.07036324826944557,0.20336763947856223,2.4929478944367207,-0.07036324826944557,0.10176851156130247,2.4950242552572273,-0.07036324826944557,0.0,2.498754512575382,-0.03526947952115613,0.0,2.4966750474322903,-0.03526947952115613,-0.10192066348295628,2.490440113067525,-0.03526947952115613,-0.2036716900800104,2.480060086914016,-0.03526947952115613,-0.30508372524911553,2.4655522455009,-0.03526947952115613,-0.4059879786660019,2.4469407356984414,-0.03526947952115613,-0.5062165051590136,2.4242565345279528,-0.03526947952115613,-0.6056024842372727,2.3975373976035974,-0.03526947952115613,-0.7039804977469227,2.3668277962918967,-0.03526947952115613,-0.8011868051933211,2.3321788436935282,-0.03526947952115613,-0.8970596162709326,2.2936482095706165,-0.03526947952115613,-0.9914393601473264,2.251300024361103,-0.03526947952115613,-1.084168951053078,2.205204772439962,-0.03526947952115613,-1.1750940497355316,2.1554391748049135,-0.03526947952115613,-1.2640633203412515,2.1020860613818964,-0.03526947952115613,-1.3509286822996178,2.045234233162829,-0.03526947952115613,-1.435545556788321,1.984978314405122,-0.03526947952115613,-1.5177731073705438,1.9214185951389415,-0.03526947952115613,-1.5974744744033098,1.8546608642443507,-0.03526947952115613,-1.674517002826855,1.7848162333761586,-0.03526947952115613,-1.7487724629558807,1.7120009520295367,-0.03526947952115613,-1.820117263905212,1.6363362140542075,-0.03526947952115613,-1.888432659294627,1.5579479559392413,-0.03526947952115613,-1.95360494489049,1.4769666472041982,-0.03526947952115613,-2.0155256478552253,1.3935270732454896,-0.03526947952115613,-2.0740917072896496,1.3077681109993882,-0.03526947952115613,-2.1292056457676716,1.219832497795072,-0.03526947952115613,-2.1807757315778415,1.1298665937824284,-0.03526947952115613,-2.228716131401738,1.0380201383300247,-0.03526947952115613,-2.2729470531750517,0.9444460007987123,-0.03526947952115613,-2.3133948788936043,0.8492999261056589,-0.03526947952115613,-2.3499922871432486,0.7527402755023033,-0.03526947952115613,-2.382678365149722,0.6549277629976837,-0.03526947952115613,-2.4113987101619436,0.5560251878658293,-0.03526947952115613,-2.4361055200000274,0.456197163682434,-0.03526947952115613,-2.456757672617293,0.3556098443418074,-0.03526947952115613,-2.473320794543852,0.2544306475101122,-0.03526947952115613,-2.485767318097859,0.15282797597518472,-0.03526947952115613,-2.4940765272691943,0.05097093735671199,-0.03526947952115613,-2.498234592199222,-0.05097093735671169,-0.03526947952115613,-2.498234592199222,-0.1528279759751844,-0.03526947952115613,-2.4940765272691943,-0.25443064751011135,-0.03526947952115613,-2.485767318097859,-0.35560984434180715,-0.03526947952115613,-2.473320794543852,-0.45619716368243374,-0.03526947952115613,-2.456757672617293,-0.5560251878658284,-0.03526947952115613,-2.4361055200000274,-0.6549277629976835,-0.03526947952115613,-2.4113987101619436,-0.7527402755023024,-0.03526947952115613,-2.3826783651497223,-0.8492999261056586,-0.03526947952115613,-2.349992287143249,-0.944446000798712,-0.03526947952115613,-2.3133948788936043,-1.038020138330024,-0.03526947952115613,-2.2729470531750517,-1.1298665937824286,-0.03526947952115613,-2.228716131401738,-1.2198324977950719,-0.03526947952115613,-2.1807757315778415,-1.3077681109993886,-0.03526947952115613,-2.129205645767671,-1.3935270732454892,-0.03526947952115613,-2.0740917072896505,-1.4769666472041978,-0.03526947952115613,-2.0155256478552253,-1.557947955939241,-0.03526947952115613,-1.9536049448904904,-1.636336214054208,-0.03526947952115613,-1.888432659294627,-1.712000952029537,-0.03526947952115613,-1.820117263905212,-1.7848162333761586,-0.03526947952115613,-1.7487724629558807,-1.8546608642443507,-0.03526947952115613,-1.674517002826855,-1.921418595138941,-0.03526947952115613,-1.5974744744033107,-1.984978314405122,-0.03526947952115613,-1.5177731073705436,-2.0452342331628284,-0.03526947952115613,-1.4355455567883217,-2.102086061381897,-0.03526947952115613,-1.3509286822996176,-2.155439174804913,-0.03526947952115613,-1.2640633203412517,-2.2052047724399615,-0.03526947952115613,-1.1750940497355318,-2.251300024361103,-0.03526947952115613,-1.0841689510530785,-2.2936482095706165,-0.03526947952115613,-0.9914393601473265,-2.3321788436935282,-0.03526947952115613,-0.8970596162709327,-2.3668277962918967,-0.03526947952115613,-0.8011868051933211,-2.397537397603597,-0.03526947952115613,-0.7039804977469237,-2.4242565345279528,-0.03526947952115613,-0.6056024842372725,-2.446940735698441,-0.03526947952115613,-0.5062165051590144,-2.4655522455009,-0.03526947952115613,-0.40598797866600256,-2.480060086914016,-0.03526947952115613,-0.30508372524911614,-2.490440113067525,-0.03526947952115613,-0.20367169008001093,-2.4966750474322903,-0.03526947952115613,-0.10192066348295668,-2.498754512575382,-0.03526947952115613,-3.0600917156804463e-16,-2.4966750474322903,-0.03526947952115613,0.10192066348295607,-2.490440113067525,-0.03526947952115613,0.20367169008001032,-2.480060086914016,-0.03526947952115613,0.3050837252491156,-2.4655522455009,-0.03526947952115613,0.4059879786660009,-2.446940735698442,-0.03526947952115613,0.5062165051590127,-2.4242565345279528,-0.03526947952115613,0.6056024842372729,-2.3975373976035974,-0.03526947952115613,0.7039804977469232,-2.3668277962918967,-0.03526947952115613,0.8011868051933205,-2.3321788436935282,-0.03526947952115613,0.8970596162709321,-2.2936482095706165,-0.03526947952115613,0.9914393601473259,-2.251300024361104,-0.03526947952115613,1.084168951053077,-2.205204772439962,-0.03526947952115613,1.175094049735531,-2.1554391748049135,-0.03526947952115613,1.2640633203412512,-2.1020860613818964,-0.03526947952115613,1.3509286822996178,-2.0452342331628293,-0.03526947952115613,1.4355455567883204,-1.9849783144051225,-0.03526947952115613,1.5177731073705432,-1.9214185951389413,-0.03526947952115613,1.5974744744033103,-1.8546608642443505,-0.03526947952115613,1.6745170028268552,-1.784816233376159,-0.03526947952115613,1.7487724629558803,-1.7120009520295374,-0.03526947952115613,1.8201172639052117,-1.636336214054209,-0.03526947952115613,1.888432659294626,-1.5579479559392413,-0.03526947952115613,1.95360494489049,-1.4769666472041971,-0.03526947952115613,2.0155256478552257,-1.3935270732454896,-0.03526947952115613,2.0740917072896496,-1.3077681109993893,-0.03526947952115613,2.1292056457676707,-1.2198324977950723,-0.03526947952115613,2.1807757315778415,-1.1298665937824273,-0.03526947952115613,2.2287161314017387,-1.0380201383300247,-0.03526947952115613,2.2729470531750517,-0.9444460007987131,-0.03526947952115613,2.313394878893604,-0.8492999261056582,-0.03526947952115613,2.349992287143249,-0.7527402755023035,-0.03526947952115613,2.382678365149722,-0.6549277629976852,-0.03526947952115613,2.411398710161943,-0.5560251878658297,-0.03526947952115613,2.4361055200000274,-0.4561971636824333,-0.03526947952115613,2.456757672617293,-0.35560984434180715,-0.03526947952115613,2.473320794543852,-0.254430647510113,-0.03526947952115613,2.4857673180978583,-0.15282797597518447,-0.03526947952115613,2.4940765272691943,-0.050970937356710634,-0.03526947952115613,2.498234592199222,0.05097093735671194,-0.03526947952115613,2.498234592199222,0.15282797597518355,-0.03526947952115613,2.4940765272691943,0.25443064751011213,-0.03526947952115613,2.485767318097859,0.35560984434180626,-0.03526947952115613,2.4733207945438522,0.45619716368243235,-0.03526947952115613,2.456757672617293,0.5560251878658288,-0.03526947952115613,2.4361055200000274,0.6549277629976843,-0.03526947952115613,2.4113987101619436,0.7527402755023027,-0.03526947952115613,2.3826783651497223,0.8492999261056573,-0.03526947952115613,2.3499922871432495,0.9444460007987102,-0.03526947952115613,2.313394878893605,1.0380201383300258,-0.03526947952115613,2.2729470531750513,1.1298665937824284,-0.03526947952115613,2.228716131401738,1.2198324977950716,-0.03526947952115613,2.180775731577842,1.3077681109993868,-0.03526947952115613,2.1292056457676725,1.393527073245489,-0.03526947952115613,2.0740917072896505,1.4769666472041982,-0.03526947952115613,2.015525647855225,1.5579479559392408,-0.03526947952115613,1.9536049448904904,1.6363362140542084,-0.03526947952115613,1.8884326592946268,1.7120009520295365,-0.03526947952115613,1.8201172639052121,1.7848162333761577,-0.03526947952115613,1.7487724629558818,1.8546608642443505,-0.03526947952115613,1.6745170028268552,1.9214185951389422,-0.03526947952115613,1.5974744744033091,1.9849783144051216,-0.03526947952115613,1.5177731073705438,2.045234233162828,-0.03526947952115613,1.4355455567883222,2.1020860613818955,-0.03526947952115613,1.3509286822996194,2.155439174804913,-0.03526947952115613,1.2640633203412521,2.205204772439962,-0.03526947952115613,1.175094049735531,2.251300024361103,-0.03526947952115613,1.0841689510530788,2.293648209570616,-0.03526947952115613,0.9914393601473278,2.3321788436935282,-0.03526947952115613,0.8970596162709331,2.3668277962918958,-0.03526947952115613,0.8011868051933225,2.3975373976035974,-0.03526947952115613,0.703980497746923,2.4242565345279528,-0.03526947952115613,0.6056024842372738,2.4469407356984414,-0.03526947952115613,0.5062165051590136,2.4655522455009,-0.03526947952115613,0.40598797866600284,2.480060086914016,-0.03526947952115613,0.3050837252491176,2.490440113067525,-0.03526947952115613,0.2036716900800112,2.4966750474322903,-0.03526947952115613,0.10192066348295588,2.498754512575382,-0.03526947952115613,0.0,2.5,0.0,0.0,2.4979194983614574,0.0,-0.10197146515396394,2.491681456235483,0.0,-0.2037732088676579,2.4812962562275693,0.0,-0.30523579218539965,2.466781183478222,0.0,-0.4061903406505145,2.448160396893553,0.0,-0.5064688253802024,2.4254648889351613,0.0,-0.6059043427330308,2.3987324340362433,0.0,-0.7043313921035742,2.3680075257297752,0.0,-0.8015861513818388,2.333341302593417,0.0,-0.8975067496189966,2.2947914631343984,0.0,-0.9919335364456062,2.252422169756048,0.0,-1.0847093477938952,2.2063039419658033,0.0,-1.175679767481842,2.156513539002452,0.0,-1.2646933842236707,2.103133832077953,0.0,-1.351602043638994,2.046253666446484,0.0,-1.436261094841158,1.9859677135302816,0.0,-1.5185296311943688,1.922376313348405,0.0,-1.5982707248388786,1.8555853075106747,0.0,-1.6753516545938987,1.7857058630547589,0.0,-1.7496441268589045,1.7128542874196104,0.0,-1.8210244891456728,1.6371518348632126,0.0,-1.8893739358856456,1.5587245046468339,0.0,-1.9545787061700741,1.4777028313216918,0.0,-2.0165302730938253,1.3942216674670738,0.0,-2.075125524387701,1.30841995924153,0.0,-2.1302669340386418,1.220440515120703,0.0,-2.1818627246121407,1.1304297682067146,0.0,-2.2298270200067343,1.0385375325047161,0.0,-2.274079988386296,0.9449167535722663,0.0,-2.3145479750522453,0.8497232539565422,0.0,-2.3511636250345287,0.7531154738430859,0.0,-2.383865995197319,0.6552542073477556,0.0,-2.4126006556728496,0.5563023348907861,0.0,-2.437319780454559,0.4564245520984042,0.0,-2.4579822269987575,0.3557870956832133,0.0,-2.4745536047023315,0.25455746675958885,0.0,-2.48700633214251,0.15290415205460708,0.0,-2.495319682983417,0.05099634347851358,0.0,-2.499479820473016,-0.050996343478513284,0.0,-2.499479820473016,-0.15290415205460675,0.0,-2.495319682983417,-0.254557466759588,0.0,-2.48700633214251,-0.3557870956832131,0.0,-2.4745536047023315,-0.45642455209840394,0.0,-2.4579822269987575,-0.5563023348907853,0.0,-2.437319780454559,-0.6552542073477553,0.0,-2.41260065567285,-0.7531154738430851,0.0,-2.3838659951973193,-0.8497232539565419,0.0,-2.3511636250345287,-0.944916753572266,0.0,-2.3145479750522453,-1.0385375325047155,0.0,-2.274079988386296,-1.1304297682067146,0.0,-2.2298270200067343,-1.2204405151207027,0.0,-2.1818627246121407,-1.3084199592415304,0.0,-2.130266934038641,-1.394221667467073,0.0,-2.0751255243877016,-1.4777028313216911,0.0,-2.0165302730938253,-1.5587245046468337,0.0,-1.9545787061700748,-1.6371518348632128,0.0,-1.8893739358856456,-1.7128542874196107,0.0,-1.8210244891456728,-1.7857058630547589,0.0,-1.7496441268589045,-1.8555853075106747,0.0,-1.6753516545938987,-1.9223763133484044,0.0,-1.5982707248388794,-1.9859677135302816,0.0,-1.5185296311943686,-2.0462536664464834,0.0,-1.436261094841159,-2.1031338320779533,0.0,-1.3516020436389935,-2.1565135390024515,0.0,-1.264693384223671,-2.206303941965803,0.0,-1.1756797674818422,-2.2524221697560476,0.0,-1.0847093477938956,-2.2947914631343984,0.0,-0.9919335364456063,-2.333341302593417,0.0,-0.8975067496189968,-2.3680075257297752,0.0,-0.8015861513818388,-2.3987324340362433,0.0,-0.7043313921035752,-2.4254648889351613,0.0,-0.6059043427330306,-2.4481603968935524,0.0,-0.5064688253802032,-2.466781183478222,0.0,-0.4061903406505152,-2.4812962562275693,0.0,-0.30523579218540026,-2.491681456235483,0.0,-0.20377320886765843,-2.4979194983614574,0.0,-0.10197146515396432,-2.5,0.0,-3.061616997868383e-16,-2.4979194983614574,0.0,0.10197146515396371,-2.491681456235483,0.0,0.2037732088676578,-2.4812962562275693,0.0,0.30523579218539965,-2.4667811834782225,0.0,0.4061903406505135,-2.4481603968935532,0.0,0.5064688253802015,-2.4254648889351613,0.0,0.6059043427330311,-2.3987324340362433,0.0,0.7043313921035745,-2.3680075257297752,0.0,0.8015861513818381,-2.3333413025934173,0.0,0.8975067496189961,-2.2947914631343984,0.0,0.9919335364456059,-2.2524221697560485,0.0,1.084709347793894,-2.2063039419658033,0.0,1.1756797674818418,-2.156513539002452,0.0,1.2646933842236705,-2.103133832077953,0.0,1.351602043638994,-2.0462536664464843,0.0,1.4362610948411576,-1.9859677135302822,0.0,1.518529631194368,-1.9223763133484049,0.0,1.598270724838879,-1.8555853075106745,0.0,1.6753516545938991,-1.7857058630547595,0.0,1.7496441268589042,-1.7128542874196109,0.0,1.8210244891456726,-1.637151834863214,0.0,1.8893739358856445,-1.5587245046468339,0.0,1.9545787061700741,-1.4777028313216904,0.0,2.0165302730938257,-1.3942216674670738,0.0,2.075125524387701,-1.308419959241531,0.0,2.130266934038641,-1.2204405151207032,0.0,2.1818627246121403,-1.1304297682067133,0.0,2.229827020006735,-1.038537532504716,0.0,2.274079988386296,-0.9449167535722671,0.0,2.314547975052245,-0.8497232539565415,0.0,2.3511636250345287,-0.7531154738430862,0.0,2.383865995197319,-0.655254207347757,0.0,2.412600655672849,-0.5563023348907865,0.0,2.437319780454559,-0.45642455209840344,0.0,2.4579822269987575,-0.3557870956832131,0.0,2.4745536047023315,-0.2545574667595897,0.0,2.48700633214251,-0.1529041520546068,0.0,2.495319682983417,-0.05099634347851223,0.0,2.499479820473016,0.050996343478513534,0.0,2.499479820473016,0.1529041520546059,0.0,2.495319682983417,0.2545574667595888,0.0,2.48700633214251,0.35578709568321215,0.0,2.474553604702332,0.45642455209840255,0.0,2.457982226998758,0.5563023348907856,0.0,2.437319780454559,0.6552542073477562,0.0,2.4126006556728496,0.7531154738430854,0.0,2.3838659951973193,0.8497232539565407,0.0,2.351163625034529,0.9449167535722642,0.0,2.314547975052246,1.038537532504717,0.0,2.2740799883862954,1.1304297682067144,0.0,2.2298270200067343,1.2204405151207025,0.0,2.1818627246121407,1.3084199592415287,0.0,2.130266934038642,1.3942216674670729,0.0,2.0751255243877016,1.4777028313216918,0.0,2.016530273093825,1.5587245046468334,0.0,1.9545787061700748,1.6371518348632133,0.0,1.8893739358856454,1.71285428741961,0.0,1.821024489145673,1.7857058630547582,0.0,1.7496441268589056,1.8555853075106745,0.0,1.6753516545938991,1.9223763133484058,0.0,1.5982707248388777,1.9859677135302813,0.0,1.5185296311943688,2.046253666446483,0.0,1.4362610948411592,2.103133832077952,0.0,1.3516020436389955,2.1565135390024515,0.0,1.2646933842236714,2.2063039419658033,0.0,1.1756797674818416,2.2524221697560476,0.0,1.0847093477938958,2.2947914631343975,0.0,0.9919335364456078,2.333341302593417,0.0,0.8975067496189971,2.368007525729775,0.0,0.8015861513818401,2.3987324340362433,0.0,0.7043313921035744,2.425464888935161,0.0,0.605904342733032,2.448160396893553,0.0,0.5064688253802024,2.466781183478222,0.0,0.40619034065051546,2.4812962562275693,0.0,0.30523579218540164,2.491681456235483,0.0,0.2037732088676587,2.4979194983614574,0.0,0.10197146515396353,2.5,0.0,0.0],"revision":0,"triangleFaceIds":[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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,0,0,0,0,0,0,0]} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/arc.json b/main/opengeometry/tests/fixtures/cases/validity/arc.json new file mode 100644 index 0000000..70611a6 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/arc.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0}],"intersections":[],"pcurves":[],"surfaces":[]},"id":"validity-arc","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":3.141592653589793,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null}],"faces":[],"halfedges":[{"edge":0,"face":null,"from":0,"geometry_use":{"pcurve":null,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":null,"next":null,"prev":null,"to":1,"twin":null,"wire_ref":0}],"loops":[],"shells":[],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":null,"position":[-1.0,0.0,-1.2246467991473532e-16],"tolerance":1e-8}],"wires":[{"id":0,"is_closed":false,"start_halfedge":0}]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/arc.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/arc.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/arc.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-annular-cylinder.json b/main/opengeometry/tests/fixtures/cases/validity/builder-annular-cylinder.json new file mode 100644 index 0000000..3cb6588 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-annular-cylinder.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"annular-cylinder","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":4,"id":2,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":true,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":3},{"chart_seam":true,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":7}],"faces":[{"id":0,"key":"outer","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"annular-cylinder","entity":"annular-cylinder","face":0,"key":"outer"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":2.0,"lo":0.0}]}},{"id":1,"key":"inner","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"annular-cylinder","entity":"annular-cylinder","face":1,"key":"inner"}]},"sense":"Reverse","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":2.0,"lo":0.0}]}},{"id":2,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"annular-cylinder","entity":"annular-cylinder","face":2,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[3],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":3,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"annular-cylinder","entity":"annular-cylinder","face":3,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[5],"outer":4,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":8,"wire_ref":null},{"edge":4,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":2,"face":1,"from":2,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":2,"twin":9,"wire_ref":null},{"edge":5,"face":1,"from":2,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":3,"twin":7,"wire_ref":null},{"edge":3,"face":1,"from":3,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":3,"twin":11,"wire_ref":null},{"edge":5,"face":1,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":2,"twin":5,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":8,"prev":8,"to":0,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":2,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":3,"next":9,"prev":9,"to":2,"twin":4,"wire_ref":null},{"edge":1,"face":3,"from":1,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":4,"next":10,"prev":10,"to":1,"twin":2,"wire_ref":null},{"edge":3,"face":3,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":5,"next":11,"prev":11,"to":3,"twin":6,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":2,"id":3,"is_hole":true,"start_halfedge":9},{"face_ref":3,"id":4,"is_hole":false,"start_halfedge":10},{"face_ref":3,"id":5,"is_hole":true,"start_halfedge":11}],"shells":[{"faces":[0,1,2,3],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,2.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":4,"position":[0.4,0.0,0.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":6,"position":[0.4,2.0,0.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-annular-cylinder.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-annular-cylinder.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-annular-cylinder.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion-with-holes.json b/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion-with-holes.json new file mode 100644 index 0000000..4843f05 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion-with-holes.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,2.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,2.0]},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[2.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.0},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,2.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,2.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,0.0,-2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,2.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.5,0.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[-0.5,2.0,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.5,0.0,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.5,0.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-0.5,2.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-0.5,0.0,-0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.5,0.0,-0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[0.5,2.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.5,0.0,-0.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.5,0.0,0.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[0.5,2.0,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.5,0.0,0.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,-2.0]},{"axis_a":[2.0,0.0],"axis_b":[0.0,2.0],"kind":"Conic2","origin":[2.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-0.5,-0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.5,0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5,-0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-2.0,-2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,-2.0]},{"axis_a":[2.0,0.0],"axis_b":[0.0,-2.0],"kind":"Conic2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,-0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-0.5,-0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5707963267948966,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-1.5707963267948966,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.0},{"frame":{"origin":[2.0,0.0,-2.0],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,-2.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-0.5,0.0,0.5],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[-0.5,0.0,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.5,0.0,-0.5],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.5,0.0,0.5],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"arc-edged-extrusion-with-holes","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":11,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":19,"id":2,"tolerance":1e-8,"twin_halfedge":29},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":-1.5707963267948966}},"halfedge":10,"id":3,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":-1.5707963267948966}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":17,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":9,"id":6,"tolerance":1e-8,"twin_halfedge":24},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-8,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":21,"id":8,"tolerance":1e-8,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":8,"id":9,"tolerance":1e-8,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-8,"twin_halfedge":30},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":25,"id":11,"tolerance":1e-8,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":15,"id":12,"tolerance":1e-8,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":13,"tolerance":1e-8,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":35,"id":14,"tolerance":1e-8,"twin_halfedge":45},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":14,"id":15,"tolerance":1e-8,"twin_halfedge":36},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":16,"tolerance":1e-8,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":33,"id":17,"tolerance":1e-8,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":13,"id":18,"tolerance":1e-8,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":19,"tolerance":1e-8,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":37,"id":20,"tolerance":1e-8,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":12,"id":21,"tolerance":1e-8,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":22,"tolerance":1e-8,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":41,"id":23,"tolerance":1e-8,"twin_halfedge":47}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion-with-holes","entity":"arc-edged-extrusion-with-holes","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[1],"outer":0,"uv_bounds":[{"hi":4.00000001,"lo":-2.00000001},{"hi":2.00000001,"lo":-2.00000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion-with-holes","entity":"arc-edged-extrusion-with-holes","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[3],"outer":2,"uv_bounds":[{"hi":4.00000001,"lo":-2.00000001},{"hi":2.00000001,"lo":-2.00000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion-with-holes","entity":"arc-edged-extrusion-with-holes","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion-with-holes","entity":"arc-edged-extrusion-with-holes","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.5707963317948965,"lo":-1.5707963317948965},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion-with-holes","entity":"arc-edged-extrusion-with-holes","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion-with-holes","entity":"arc-edged-extrusion-with-holes","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":6,"key":"side-1-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion-with-holes","entity":"arc-edged-extrusion-with-holes","face":6,"key":"side-1-0"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":7,"key":"side-1-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion-with-holes","entity":"arc-edged-extrusion-with-holes","face":7,"key":"side-1-1"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":8,"key":"side-1-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion-with-holes","entity":"arc-edged-extrusion-with-holes","face":8,"key":"side-1-2"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":9,"key":"side-1-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion-with-holes","entity":"arc-edged-extrusion-with-holes","face":9,"key":"side-1-3"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":18,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":22,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":26,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":30,"wire_ref":null},{"edge":13,"face":0,"from":12,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":13,"twin":34,"wire_ref":null},{"edge":16,"face":0,"from":13,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":14,"twin":38,"wire_ref":null},{"edge":19,"face":0,"from":14,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Forward"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":15,"twin":42,"wire_ref":null},{"edge":22,"face":0,"from":15,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":12,"twin":46,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":3,"twin":28,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":2,"twin":24,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":1,"twin":20,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":16,"wire_ref":null},{"edge":21,"face":1,"from":8,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Reverse"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":11,"twin":44,"wire_ref":null},{"edge":18,"face":1,"from":11,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":10,"twin":40,"wire_ref":null},{"edge":15,"face":1,"from":10,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":9,"twin":36,"wire_ref":null},{"edge":12,"face":1,"from":9,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":8,"twin":32,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":11,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":23,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":29,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":2,"twin":10,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":6,"twin":27,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":1,"twin":17,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":3,"twin":9,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Forward"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":7,"twin":31,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Reverse"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":2,"twin":21,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":0,"twin":8,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":4,"twin":19,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":3,"twin":25,"wire_ref":null},{"edge":12,"face":6,"from":8,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":9,"twin":15,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Forward"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":13,"twin":39,"wire_ref":null},{"edge":13,"face":6,"from":13,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":12,"twin":4,"wire_ref":null},{"edge":14,"face":6,"from":12,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Reverse"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":8,"twin":45,"wire_ref":null},{"edge":15,"face":7,"from":9,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":10,"twin":14,"wire_ref":null},{"edge":20,"face":7,"from":10,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Forward"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":14,"twin":43,"wire_ref":null},{"edge":16,"face":7,"from":14,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":13,"twin":5,"wire_ref":null},{"edge":17,"face":7,"from":13,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Reverse"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":9,"twin":33,"wire_ref":null},{"edge":18,"face":8,"from":10,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":11,"twin":13,"wire_ref":null},{"edge":23,"face":8,"from":11,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":15,"twin":47,"wire_ref":null},{"edge":19,"face":8,"from":15,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":14,"twin":6,"wire_ref":null},{"edge":20,"face":8,"from":14,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":10,"twin":37,"wire_ref":null},{"edge":21,"face":9,"from":11,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":8,"twin":12,"wire_ref":null},{"edge":14,"face":9,"from":8,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":12,"twin":35,"wire_ref":null},{"edge":22,"face":9,"from":12,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":15,"twin":7,"wire_ref":null},{"edge":23,"face":9,"from":15,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":11,"twin":41,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":0,"id":1,"is_hole":true,"start_halfedge":4},{"face_ref":1,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":1,"id":3,"is_hole":true,"start_halfedge":12},{"face_ref":2,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":3,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":4,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":5,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":6,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":7,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":8,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":9,"id":11,"is_hole":false,"start_halfedge":44}],"shells":[{"faces":[0,1,2,3,4,5,6,7,8,9],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":8,"position":[-2.0,0.0,2.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":11,"position":[2.0,0.0,2.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":10,"position":[2.0,0.0,-2.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":9,"position":[-2.0,0.0,-2.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":0,"position":[-2.0,2.0,2.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":1,"position":[2.0,2.0,2.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":2,"position":[2.0,2.0,-2.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":3,"position":[-2.0,2.0,-2.0],"tolerance":1e-8},{"id":8,"outgoing_halfedge":12,"position":[-0.5,0.0,0.5],"tolerance":1e-8},{"id":9,"outgoing_halfedge":15,"position":[-0.5,0.0,-0.5],"tolerance":1e-8},{"id":10,"outgoing_halfedge":14,"position":[0.5,0.0,-0.5],"tolerance":1e-8},{"id":11,"outgoing_halfedge":13,"position":[0.5,0.0,0.5],"tolerance":1e-8},{"id":12,"outgoing_halfedge":4,"position":[-0.5,2.0,0.5],"tolerance":1e-8},{"id":13,"outgoing_halfedge":5,"position":[-0.5,2.0,-0.5],"tolerance":1e-8},{"id":14,"outgoing_halfedge":6,"position":[0.5,2.0,-0.5],"tolerance":1e-8},{"id":15,"outgoing_halfedge":7,"position":[0.5,2.0,0.5],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion-with-holes.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion-with-holes.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion-with-holes.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion.json b/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion.json new file mode 100644 index 0000000..4a47391 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,0.0,2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-2.0,2.0,2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,2.0]},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[2.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.0},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,2.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[2.0,2.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,0.0,-2.0]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-2.0,2.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-2.0,0.0,-2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,-2.0]},{"axis_a":[2.0,0.0],"axis_b":[0.0,2.0],"kind":"Conic2","origin":[2.0,0.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-2.0,-2.0]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[2.0,-2.0]},{"axis_a":[2.0,0.0],"axis_b":[0.0,-2.0],"kind":"Conic2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5707963267948966,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-1.5707963267948966,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[4.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,2.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":2.0},{"frame":{"origin":[2.0,0.0,-2.0],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-2.0,0.0,-2.0],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"arc-edged-extrusion","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":-1.5707963267948966}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":-1.5707963267948966}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":4.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-8,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion","entity":"arc-edged-extrusion","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":4.00000001,"lo":-2.00000001},{"hi":2.00000001,"lo":-2.00000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion","entity":"arc-edged-extrusion","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":4.00000001,"lo":-2.00000001},{"hi":2.00000001,"lo":-2.00000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion","entity":"arc-edged-extrusion","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion","entity":"arc-edged-extrusion","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.5707963317948965,"lo":-1.5707963317948965},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion","entity":"arc-edged-extrusion","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"arc-edged-extrusion","entity":"arc-edged-extrusion","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":4.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[-2.0,0.0,2.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":7,"position":[2.0,0.0,2.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":6,"position":[2.0,0.0,-2.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":5,"position":[-2.0,0.0,-2.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":0,"position":[-2.0,2.0,2.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":1,"position":[2.0,2.0,2.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":2,"position":[2.0,2.0,-2.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":3,"position":[-2.0,2.0,-2.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-arc-edged-extrusion.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-box-cavity.json b/main/opengeometry/tests/fixtures/cases/validity/builder-box-cavity.json new file mode 100644 index 0000000..61754bc --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-box-cavity.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,1.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,1.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,1.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,1.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,1.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,1.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,-1.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,1.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,1.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,1.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,2.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,1.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,3.0,-1.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,1.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,1.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,1.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,1.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,2.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,2.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,3.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,1.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,1.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,1.0,-1.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,1.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,-1.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,1.0,-1.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,1.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,2.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,3.0,-1.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,1.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,1.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,1.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.0,3.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,2.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,3.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,1.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,1.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,1.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,1.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,2.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,3.0,-1.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,1.0,-1.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,1.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,-1.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,3.0,-2.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,1.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,1.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,0.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,1.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,2.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,2.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,2.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[2.0,3.0,-3.0]}],"intersections":[],"pcurves":[{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,1.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,2.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,1.0,-1.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,1.0,-1.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,1.0,-1.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,2.0,-1.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,1.0,-2.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,1.0,-1.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"box-cavity","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[1],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-8,"twin_halfedge":6},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":90},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":6,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":8,"id":7,"tolerance":1e-8,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-8,"twin_halfedge":95},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":12,"id":9,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-8,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":16,"id":12,"tolerance":1e-8,"twin_halfedge":98},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":17,"id":13,"tolerance":1e-8,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":20,"id":14,"tolerance":1e-8,"twin_halfedge":31},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":21,"id":15,"tolerance":1e-8,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":23,"id":16,"tolerance":1e-8,"twin_halfedge":26},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":24,"id":17,"tolerance":1e-8,"twin_halfedge":106},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":25,"id":18,"tolerance":1e-8,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":29,"id":19,"tolerance":1e-8,"twin_halfedge":111},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":30,"id":20,"tolerance":1e-8,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":32,"id":21,"tolerance":1e-8,"twin_halfedge":42},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":33,"id":22,"tolerance":1e-8,"twin_halfedge":59},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":34,"id":23,"tolerance":1e-8,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":36,"id":24,"tolerance":1e-8,"twin_halfedge":66},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":37,"id":25,"tolerance":1e-8,"twin_halfedge":115},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":40,"id":26,"tolerance":1e-8,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":41,"id":27,"tolerance":1e-8,"twin_halfedge":71},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":44,"id":28,"tolerance":1e-8,"twin_halfedge":119},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":45,"id":29,"tolerance":1e-8,"twin_halfedge":138},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":46,"id":30,"tolerance":1e-8,"twin_halfedge":123},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":47,"id":31,"tolerance":1e-8,"twin_halfedge":100},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":48,"id":32,"tolerance":1e-8,"twin_halfedge":55},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":49,"id":33,"tolerance":1e-8,"twin_halfedge":79},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":53,"id":34,"tolerance":1e-8,"twin_halfedge":127},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":54,"id":35,"tolerance":1e-8,"twin_halfedge":84},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":56,"id":36,"tolerance":1e-8,"twin_halfedge":70},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":57,"id":37,"tolerance":1e-8,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":58,"id":38,"tolerance":1e-8,"twin_halfedge":67},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":61,"id":39,"tolerance":1e-8,"twin_halfedge":75},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":62,"id":40,"tolerance":1e-8,"twin_halfedge":128},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":63,"id":41,"tolerance":1e-8,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":65,"id":42,"tolerance":1e-8,"twin_halfedge":135},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":68,"id":43,"tolerance":1e-8,"twin_halfedge":78},{"chart_seam":false,"geometry":{"curve":44,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":69,"id":44,"tolerance":1e-8,"twin_halfedge":72},{"chart_seam":false,"geometry":{"curve":45,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":73,"id":45,"tolerance":1e-8,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":46,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":74,"id":46,"tolerance":1e-8,"twin_halfedge":140},{"chart_seam":false,"geometry":{"curve":47,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":76,"id":47,"tolerance":1e-8,"twin_halfedge":87},{"chart_seam":false,"geometry":{"curve":48,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":77,"id":48,"tolerance":1e-8,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":49,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":81,"id":49,"tolerance":1e-8,"twin_halfedge":86},{"chart_seam":false,"geometry":{"curve":50,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":82,"id":50,"tolerance":1e-8,"twin_halfedge":144},{"chart_seam":false,"geometry":{"curve":51,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":85,"id":51,"tolerance":1e-8,"twin_halfedge":151},{"chart_seam":false,"geometry":{"curve":52,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":88,"id":52,"tolerance":1e-8,"twin_halfedge":158},{"chart_seam":false,"geometry":{"curve":53,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":89,"id":53,"tolerance":1e-8,"twin_halfedge":99},{"chart_seam":false,"geometry":{"curve":54,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":91,"id":54,"tolerance":1e-8,"twin_halfedge":94},{"chart_seam":false,"geometry":{"curve":55,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":92,"id":55,"tolerance":1e-8,"twin_halfedge":114},{"chart_seam":false,"geometry":{"curve":56,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":93,"id":56,"tolerance":1e-8,"twin_halfedge":163},{"chart_seam":false,"geometry":{"curve":57,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":96,"id":57,"tolerance":1e-8,"twin_halfedge":170},{"chart_seam":false,"geometry":{"curve":58,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":97,"id":58,"tolerance":1e-8,"twin_halfedge":107},{"chart_seam":false,"geometry":{"curve":59,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":101,"id":59,"tolerance":1e-8,"twin_halfedge":122},{"chart_seam":false,"geometry":{"curve":60,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":102,"id":60,"tolerance":1e-8,"twin_halfedge":195},{"chart_seam":false,"geometry":{"curve":61,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":103,"id":61,"tolerance":1e-8,"twin_halfedge":116},{"chart_seam":false,"geometry":{"curve":62,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":104,"id":62,"tolerance":1e-8,"twin_halfedge":178},{"chart_seam":false,"geometry":{"curve":63,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":105,"id":63,"tolerance":1e-8,"twin_halfedge":108},{"chart_seam":false,"geometry":{"curve":64,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":109,"id":64,"tolerance":1e-8,"twin_halfedge":183},{"chart_seam":false,"geometry":{"curve":65,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":110,"id":65,"tolerance":1e-8,"twin_halfedge":124},{"chart_seam":false,"geometry":{"curve":66,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":112,"id":66,"tolerance":1e-8,"twin_halfedge":134},{"chart_seam":false,"geometry":{"curve":67,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":113,"id":67,"tolerance":1e-8,"twin_halfedge":191},{"chart_seam":false,"geometry":{"curve":68,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":117,"id":68,"tolerance":1e-8,"twin_halfedge":194},{"chart_seam":false,"geometry":{"curve":69,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":118,"id":69,"tolerance":1e-8,"twin_halfedge":139},{"chart_seam":false,"geometry":{"curve":70,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":120,"id":70,"tolerance":1e-8,"twin_halfedge":137},{"chart_seam":false,"geometry":{"curve":71,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":121,"id":71,"tolerance":1e-8,"twin_halfedge":192},{"chart_seam":false,"geometry":{"curve":72,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":125,"id":72,"tolerance":1e-8,"twin_halfedge":207},{"chart_seam":false,"geometry":{"curve":73,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":126,"id":73,"tolerance":1e-8,"twin_halfedge":148},{"chart_seam":false,"geometry":{"curve":74,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":129,"id":74,"tolerance":1e-8,"twin_halfedge":143},{"chart_seam":false,"geometry":{"curve":75,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":130,"id":75,"tolerance":1e-8,"twin_halfedge":212},{"chart_seam":false,"geometry":{"curve":76,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":131,"id":76,"tolerance":1e-8,"twin_halfedge":132},{"chart_seam":false,"geometry":{"curve":77,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":133,"id":77,"tolerance":1e-8,"twin_halfedge":219},{"chart_seam":false,"geometry":{"curve":78,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":136,"id":78,"tolerance":1e-8,"twin_halfedge":193},{"chart_seam":false,"geometry":{"curve":79,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":141,"id":79,"tolerance":1e-8,"twin_halfedge":147},{"chart_seam":false,"geometry":{"curve":80,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":142,"id":80,"tolerance":1e-8,"twin_halfedge":224},{"chart_seam":false,"geometry":{"curve":81,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":145,"id":81,"tolerance":1e-8,"twin_halfedge":150},{"chart_seam":false,"geometry":{"curve":82,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":146,"id":82,"tolerance":1e-8,"twin_halfedge":232},{"chart_seam":false,"geometry":{"curve":83,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":149,"id":83,"tolerance":1e-8,"twin_halfedge":239},{"chart_seam":false,"geometry":{"curve":84,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":152,"id":84,"tolerance":1e-8,"twin_halfedge":161},{"chart_seam":false,"geometry":{"curve":85,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":153,"id":85,"tolerance":1e-8,"twin_halfedge":187},{"chart_seam":false,"geometry":{"curve":86,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":154,"id":86,"tolerance":1e-8,"twin_halfedge":164},{"chart_seam":false,"geometry":{"curve":87,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":155,"id":87,"tolerance":1e-8,"twin_halfedge":156},{"chart_seam":false,"geometry":{"curve":88,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":157,"id":88,"tolerance":1e-8,"twin_halfedge":171},{"chart_seam":false,"geometry":{"curve":89,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":159,"id":89,"tolerance":1e-8,"twin_halfedge":162},{"chart_seam":false,"geometry":{"curve":90,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":160,"id":90,"tolerance":1e-8,"twin_halfedge":190},{"chart_seam":false,"geometry":{"curve":91,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":165,"id":91,"tolerance":1e-8,"twin_halfedge":199},{"chart_seam":false,"geometry":{"curve":92,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":166,"id":92,"tolerance":1e-8,"twin_halfedge":172},{"chart_seam":false,"geometry":{"curve":93,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":167,"id":93,"tolerance":1e-8,"twin_halfedge":168},{"chart_seam":false,"geometry":{"curve":94,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":169,"id":94,"tolerance":1e-8,"twin_halfedge":179},{"chart_seam":false,"geometry":{"curve":95,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":173,"id":95,"tolerance":1e-8,"twin_halfedge":203},{"chart_seam":false,"geometry":{"curve":96,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":174,"id":96,"tolerance":1e-8,"twin_halfedge":181},{"chart_seam":false,"geometry":{"curve":97,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":175,"id":97,"tolerance":1e-8,"twin_halfedge":176},{"chart_seam":false,"geometry":{"curve":98,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":177,"id":98,"tolerance":1e-8,"twin_halfedge":180},{"chart_seam":false,"geometry":{"curve":99,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":182,"id":99,"tolerance":1e-8,"twin_halfedge":204},{"chart_seam":false,"geometry":{"curve":100,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":184,"id":100,"tolerance":1e-8,"twin_halfedge":189},{"chart_seam":false,"geometry":{"curve":101,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":185,"id":101,"tolerance":1e-8,"twin_halfedge":211},{"chart_seam":false,"geometry":{"curve":102,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":186,"id":102,"tolerance":1e-8,"twin_halfedge":196},{"chart_seam":false,"geometry":{"curve":103,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":188,"id":103,"tolerance":1e-8,"twin_halfedge":218},{"chart_seam":false,"geometry":{"curve":104,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":197,"id":104,"tolerance":1e-8,"twin_halfedge":223},{"chart_seam":false,"geometry":{"curve":105,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":198,"id":105,"tolerance":1e-8,"twin_halfedge":200},{"chart_seam":false,"geometry":{"curve":106,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":201,"id":106,"tolerance":1e-8,"twin_halfedge":231},{"chart_seam":false,"geometry":{"curve":107,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":202,"id":107,"tolerance":1e-8,"twin_halfedge":205},{"chart_seam":false,"geometry":{"curve":108,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":206,"id":108,"tolerance":1e-8,"twin_halfedge":236},{"chart_seam":false,"geometry":{"curve":109,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":208,"id":109,"tolerance":1e-8,"twin_halfedge":217},{"chart_seam":false,"geometry":{"curve":110,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":209,"id":110,"tolerance":1e-8,"twin_halfedge":214},{"chart_seam":false,"geometry":{"curve":111,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":210,"id":111,"tolerance":1e-8,"twin_halfedge":220},{"chart_seam":false,"geometry":{"curve":112,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":213,"id":112,"tolerance":1e-8,"twin_halfedge":227},{"chart_seam":false,"geometry":{"curve":113,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":215,"id":113,"tolerance":1e-8,"twin_halfedge":216},{"chart_seam":false,"geometry":{"curve":114,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":221,"id":114,"tolerance":1e-8,"twin_halfedge":226},{"chart_seam":false,"geometry":{"curve":115,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":222,"id":115,"tolerance":1e-8,"twin_halfedge":228},{"chart_seam":false,"geometry":{"curve":116,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":225,"id":116,"tolerance":1e-8,"twin_halfedge":235},{"chart_seam":false,"geometry":{"curve":117,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":229,"id":117,"tolerance":1e-8,"twin_halfedge":234},{"chart_seam":false,"geometry":{"curve":118,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":230,"id":118,"tolerance":1e-8,"twin_halfedge":237},{"chart_seam":false,"geometry":{"curve":119,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":233,"id":119,"tolerance":1e-8,"twin_halfedge":238}],"faces":[{"id":0,"key":"left-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":1,"key":"front-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":2,"key":"bottom-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":3,"key":"left-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":4,"key":"front-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":0.99999999}]}},{"id":5,"key":"left-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":6,"key":"front-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.99999999}]}},{"id":7,"key":"top-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":8,"key":"left-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":0.99999999}]}},{"id":9,"key":"bottom-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":10,"key":"left-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":11,"key":"left-11","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"box-inner","entity":"box-inner","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":1,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":12,"key":"left-12","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":13,"key":"top-13","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":0.99999999}]}},{"id":14,"key":"left-14","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.99999999}]}},{"id":15,"key":"back-15","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":16,"key":"bottom-16","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[],"outer":16,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":17,"key":"left-17","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":17,"trim":{"chart":0,"holes":[],"outer":17,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":18,"key":"back-18","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":18,"trim":{"chart":0,"holes":[],"outer":18,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":19,"key":"left-19","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":19,"trim":{"chart":0,"holes":[],"outer":19,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":20,"key":"back-20","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":20,"trim":{"chart":0,"holes":[],"outer":20,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":21,"key":"top-21","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":21,"trim":{"chart":0,"holes":[],"outer":21,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.99999999}]}},{"id":22,"key":"front-22","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":22,"trim":{"chart":0,"holes":[],"outer":22,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":23,"key":"bottom-23","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":23,"trim":{"chart":0,"holes":[],"outer":23,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":0.99999999}]}},{"id":24,"key":"front-24","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":24,"trim":{"chart":0,"holes":[],"outer":24,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":25,"key":"front-25","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"box-inner","entity":"box-inner","face":4,"key":"front"}]},"sense":"Reverse","shell_ref":1,"surface":25,"trim":{"chart":0,"holes":[],"outer":25,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":26,"key":"front-26","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":26,"trim":{"chart":0,"holes":[],"outer":26,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":27,"key":"top-27","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":27,"trim":{"chart":0,"holes":[],"outer":27,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":28,"key":"bottom-28","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":28,"trim":{"chart":0,"holes":[],"outer":28,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":29,"key":"bottom-29","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"box-inner","entity":"box-inner","face":0,"key":"bottom"}]},"sense":"Reverse","shell_ref":1,"surface":29,"trim":{"chart":0,"holes":[],"outer":29,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":30,"key":"top-30","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"box-inner","entity":"box-inner","face":1,"key":"top"}]},"sense":"Reverse","shell_ref":1,"surface":30,"trim":{"chart":0,"holes":[],"outer":30,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":31,"key":"top-31","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":31,"trim":{"chart":0,"holes":[],"outer":31,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":32,"key":"back-32","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":32,"trim":{"chart":0,"holes":[],"outer":32,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":0.99999999}]}},{"id":33,"key":"bottom-33","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":33,"trim":{"chart":0,"holes":[],"outer":33,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":34,"key":"back-34","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"box-inner","entity":"box-inner","face":5,"key":"back"}]},"sense":"Reverse","shell_ref":1,"surface":34,"trim":{"chart":0,"holes":[],"outer":34,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":35,"key":"back-35","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":35,"trim":{"chart":0,"holes":[],"outer":35,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":36,"key":"back-36","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":36,"trim":{"chart":0,"holes":[],"outer":36,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":37,"key":"top-37","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":37,"trim":{"chart":0,"holes":[],"outer":37,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":38,"key":"right-38","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":38,"trim":{"chart":0,"holes":[],"outer":38,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":39,"key":"front-39","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":39,"trim":{"chart":0,"holes":[],"outer":39,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":40,"key":"bottom-40","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":40,"trim":{"chart":0,"holes":[],"outer":40,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.99999999}]}},{"id":41,"key":"right-41","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":41,"trim":{"chart":0,"holes":[],"outer":41,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":0.99999999}]}},{"id":42,"key":"front-42","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":42,"trim":{"chart":0,"holes":[],"outer":42,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":43,"key":"right-43","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":43,"trim":{"chart":0,"holes":[],"outer":43,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.99999999}]}},{"id":44,"key":"front-44","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":44,"trim":{"chart":0,"holes":[],"outer":44,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":45,"key":"top-45","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":45,"trim":{"chart":0,"holes":[],"outer":45,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":46,"key":"right-46","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":46,"trim":{"chart":0,"holes":[],"outer":46,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":47,"key":"bottom-47","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":47,"trim":{"chart":0,"holes":[],"outer":47,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":48,"key":"right-48","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"box-inner","entity":"box-inner","face":3,"key":"right"}]},"sense":"Reverse","shell_ref":1,"surface":48,"trim":{"chart":0,"holes":[],"outer":48,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":49,"key":"right-49","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":49,"trim":{"chart":0,"holes":[],"outer":49,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":50,"key":"right-50","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":50,"trim":{"chart":0,"holes":[],"outer":50,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":51,"key":"top-51","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":51,"trim":{"chart":0,"holes":[],"outer":51,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":52,"key":"right-52","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":52,"trim":{"chart":0,"holes":[],"outer":52,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":1.00000001,"lo":-1e-8}]}},{"id":53,"key":"back-53","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":53,"trim":{"chart":0,"holes":[],"outer":53,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.99999999}]}},{"id":54,"key":"bottom-54","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":54,"trim":{"chart":0,"holes":[],"outer":54,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":55,"key":"right-55","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":55,"trim":{"chart":0,"holes":[],"outer":55,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":2.00000001,"lo":0.99999999}]}},{"id":56,"key":"back-56","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":56,"trim":{"chart":0,"holes":[],"outer":56,"uv_bounds":[{"hi":2.00000001,"lo":0.99999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":57,"key":"right-57","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":57,"trim":{"chart":0,"holes":[],"outer":57,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":58,"key":"back-58","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":58,"trim":{"chart":0,"holes":[],"outer":58,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":3.00000001,"lo":1.99999999}]}},{"id":59,"key":"top-59","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":59,"trim":{"chart":0,"holes":[],"outer":59,"uv_bounds":[{"hi":3.00000001,"lo":1.99999999},{"hi":3.00000001,"lo":1.99999999}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":14,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":35,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":11,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":6,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":90,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":0,"twin":19,"wire_ref":null},{"edge":3,"face":1,"from":0,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":3,"twin":3,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":10,"wire_ref":null},{"edge":7,"face":2,"from":2,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":6,"twin":38,"wire_ref":null},{"edge":8,"face":2,"from":6,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":4,"twin":95,"wire_ref":null},{"edge":6,"face":2,"from":4,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":7,"wire_ref":null},{"edge":2,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":2,"twin":2,"wire_ref":null},{"edge":9,"face":3,"from":7,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":8,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":8,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":1,"twin":43,"wire_ref":null},{"edge":0,"face":3,"from":1,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":0,"twin":0,"wire_ref":null},{"edge":11,"face":3,"from":0,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":7,"twin":18,"wire_ref":null},{"edge":12,"face":4,"from":5,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":9,"twin":98,"wire_ref":null},{"edge":13,"face":4,"from":9,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":27,"wire_ref":null},{"edge":11,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":0,"twin":15,"wire_ref":null},{"edge":5,"face":4,"from":0,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":5,"twin":5,"wire_ref":null},{"edge":14,"face":5,"from":10,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":11,"twin":31,"wire_ref":null},{"edge":15,"face":5,"from":11,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Reverse"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":8,"twin":51,"wire_ref":null},{"edge":9,"face":5,"from":8,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":12,"wire_ref":null},{"edge":16,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Forward"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":10,"twin":26,"wire_ref":null},{"edge":17,"face":6,"from":9,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":12,"twin":106,"wire_ref":null},{"edge":18,"face":6,"from":12,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":10,"twin":28,"wire_ref":null},{"edge":16,"face":6,"from":10,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":7,"twin":23,"wire_ref":null},{"edge":13,"face":6,"from":7,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":9,"twin":17,"wire_ref":null},{"edge":18,"face":7,"from":10,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":12,"twin":25,"wire_ref":null},{"edge":19,"face":7,"from":12,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":13,"twin":111,"wire_ref":null},{"edge":20,"face":7,"from":13,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":11,"twin":52,"wire_ref":null},{"edge":14,"face":7,"from":11,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":10,"twin":20,"wire_ref":null},{"edge":21,"face":8,"from":1,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":14,"twin":42,"wire_ref":null},{"edge":22,"face":8,"from":14,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":15,"twin":59,"wire_ref":null},{"edge":23,"face":8,"from":15,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":2,"twin":39,"wire_ref":null},{"edge":1,"face":8,"from":2,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Forward"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":1,"twin":1,"wire_ref":null},{"edge":24,"face":9,"from":15,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":16,"twin":66,"wire_ref":null},{"edge":25,"face":9,"from":16,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":6,"twin":115,"wire_ref":null},{"edge":7,"face":9,"from":6,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":2,"twin":8,"wire_ref":null},{"edge":23,"face":9,"from":2,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":15,"twin":34,"wire_ref":null},{"edge":26,"face":10,"from":8,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":17,"twin":50,"wire_ref":null},{"edge":27,"face":10,"from":17,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Reverse"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":14,"twin":71,"wire_ref":null},{"edge":21,"face":10,"from":14,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":1,"twin":32,"wire_ref":null},{"edge":10,"face":10,"from":1,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Forward"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":8,"twin":13,"wire_ref":null},{"edge":28,"face":11,"from":18,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":19,"twin":119,"wire_ref":null},{"edge":29,"face":11,"from":19,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":20,"twin":138,"wire_ref":null},{"edge":30,"face":11,"from":20,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":21,"twin":123,"wire_ref":null},{"edge":31,"face":11,"from":21,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":18,"twin":100,"wire_ref":null},{"edge":32,"face":12,"from":11,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":12,"next":49,"prev":51,"to":22,"twin":55,"wire_ref":null},{"edge":33,"face":12,"from":22,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Reverse"},"id":49,"loop_ref":12,"next":50,"prev":48,"to":17,"twin":79,"wire_ref":null},{"edge":26,"face":12,"from":17,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":12,"next":51,"prev":49,"to":8,"twin":40,"wire_ref":null},{"edge":15,"face":12,"from":8,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Forward"},"id":51,"loop_ref":12,"next":48,"prev":50,"to":11,"twin":21,"wire_ref":null},{"edge":20,"face":13,"from":11,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":13,"next":53,"prev":55,"to":13,"twin":30,"wire_ref":null},{"edge":34,"face":13,"from":13,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":13,"next":54,"prev":52,"to":23,"twin":127,"wire_ref":null},{"edge":35,"face":13,"from":23,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":13,"next":55,"prev":53,"to":22,"twin":84,"wire_ref":null},{"edge":32,"face":13,"from":22,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Reverse"},"id":55,"loop_ref":13,"next":52,"prev":54,"to":11,"twin":48,"wire_ref":null},{"edge":36,"face":14,"from":14,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":14,"next":57,"prev":59,"to":24,"twin":70,"wire_ref":null},{"edge":37,"face":14,"from":24,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Reverse"},"id":57,"loop_ref":14,"next":58,"prev":56,"to":25,"twin":60,"wire_ref":null},{"edge":38,"face":14,"from":25,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":14,"next":59,"prev":57,"to":15,"twin":67,"wire_ref":null},{"edge":22,"face":14,"from":15,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Forward"},"id":59,"loop_ref":14,"next":56,"prev":58,"to":14,"twin":33,"wire_ref":null},{"edge":37,"face":15,"from":25,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Forward"},"id":60,"loop_ref":15,"next":61,"prev":63,"to":24,"twin":57,"wire_ref":null},{"edge":39,"face":15,"from":24,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Forward"},"id":61,"loop_ref":15,"next":62,"prev":60,"to":26,"twin":75,"wire_ref":null},{"edge":40,"face":15,"from":26,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":15,"next":63,"prev":61,"to":27,"twin":128,"wire_ref":null},{"edge":41,"face":15,"from":27,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Reverse"},"id":63,"loop_ref":15,"next":60,"prev":62,"to":25,"twin":64,"wire_ref":null},{"edge":41,"face":16,"from":25,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Forward"},"id":64,"loop_ref":16,"next":65,"prev":67,"to":27,"twin":63,"wire_ref":null},{"edge":42,"face":16,"from":27,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Reverse"},"id":65,"loop_ref":16,"next":66,"prev":64,"to":16,"twin":135,"wire_ref":null},{"edge":24,"face":16,"from":16,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Reverse"},"id":66,"loop_ref":16,"next":67,"prev":65,"to":15,"twin":36,"wire_ref":null},{"edge":38,"face":16,"from":15,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Forward"},"id":67,"loop_ref":16,"next":64,"prev":66,"to":25,"twin":58,"wire_ref":null},{"edge":43,"face":17,"from":17,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":17,"next":69,"prev":71,"to":28,"twin":78,"wire_ref":null},{"edge":44,"face":17,"from":28,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Reverse"},"id":69,"loop_ref":17,"next":70,"prev":68,"to":24,"twin":72,"wire_ref":null},{"edge":36,"face":17,"from":24,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":17,"next":71,"prev":69,"to":14,"twin":56,"wire_ref":null},{"edge":27,"face":17,"from":14,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Forward"},"id":71,"loop_ref":17,"next":68,"prev":70,"to":17,"twin":41,"wire_ref":null},{"edge":44,"face":18,"from":24,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Forward"},"id":72,"loop_ref":18,"next":73,"prev":75,"to":28,"twin":69,"wire_ref":null},{"edge":45,"face":18,"from":28,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Forward"},"id":73,"loop_ref":18,"next":74,"prev":72,"to":29,"twin":83,"wire_ref":null},{"edge":46,"face":18,"from":29,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":18,"next":75,"prev":73,"to":26,"twin":140,"wire_ref":null},{"edge":39,"face":18,"from":26,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Reverse"},"id":75,"loop_ref":18,"next":72,"prev":74,"to":24,"twin":61,"wire_ref":null},{"edge":47,"face":19,"from":22,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Forward"},"id":76,"loop_ref":19,"next":77,"prev":79,"to":30,"twin":87,"wire_ref":null},{"edge":48,"face":19,"from":30,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Reverse"},"id":77,"loop_ref":19,"next":78,"prev":76,"to":28,"twin":80,"wire_ref":null},{"edge":43,"face":19,"from":28,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":19,"next":79,"prev":77,"to":17,"twin":68,"wire_ref":null},{"edge":33,"face":19,"from":17,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Forward"},"id":79,"loop_ref":19,"next":76,"prev":78,"to":22,"twin":49,"wire_ref":null},{"edge":48,"face":20,"from":28,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Forward"},"id":80,"loop_ref":20,"next":81,"prev":83,"to":30,"twin":77,"wire_ref":null},{"edge":49,"face":20,"from":30,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Forward"},"id":81,"loop_ref":20,"next":82,"prev":80,"to":31,"twin":86,"wire_ref":null},{"edge":50,"face":20,"from":31,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":20,"next":83,"prev":81,"to":29,"twin":144,"wire_ref":null},{"edge":45,"face":20,"from":29,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":20,"next":80,"prev":82,"to":28,"twin":73,"wire_ref":null},{"edge":35,"face":21,"from":22,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Forward"},"id":84,"loop_ref":21,"next":85,"prev":87,"to":23,"twin":54,"wire_ref":null},{"edge":51,"face":21,"from":23,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Forward"},"id":85,"loop_ref":21,"next":86,"prev":84,"to":31,"twin":151,"wire_ref":null},{"edge":49,"face":21,"from":31,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Reverse"},"id":86,"loop_ref":21,"next":87,"prev":85,"to":30,"twin":81,"wire_ref":null},{"edge":47,"face":21,"from":30,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Reverse"},"id":87,"loop_ref":21,"next":84,"prev":86,"to":22,"twin":76,"wire_ref":null},{"edge":52,"face":22,"from":32,"geometry_use":{"pcurve":88,"periodic_lift":[0,0],"sense":"Forward"},"id":88,"loop_ref":22,"next":89,"prev":91,"to":33,"twin":158,"wire_ref":null},{"edge":53,"face":22,"from":33,"geometry_use":{"pcurve":89,"periodic_lift":[0,0],"sense":"Reverse"},"id":89,"loop_ref":22,"next":90,"prev":88,"to":5,"twin":99,"wire_ref":null},{"edge":4,"face":22,"from":5,"geometry_use":{"pcurve":90,"periodic_lift":[0,0],"sense":"Reverse"},"id":90,"loop_ref":22,"next":91,"prev":89,"to":4,"twin":4,"wire_ref":null},{"edge":54,"face":22,"from":4,"geometry_use":{"pcurve":91,"periodic_lift":[0,0],"sense":"Forward"},"id":91,"loop_ref":22,"next":88,"prev":90,"to":32,"twin":94,"wire_ref":null},{"edge":55,"face":23,"from":6,"geometry_use":{"pcurve":92,"periodic_lift":[0,0],"sense":"Forward"},"id":92,"loop_ref":23,"next":93,"prev":95,"to":34,"twin":114,"wire_ref":null},{"edge":56,"face":23,"from":34,"geometry_use":{"pcurve":93,"periodic_lift":[0,0],"sense":"Reverse"},"id":93,"loop_ref":23,"next":94,"prev":92,"to":32,"twin":163,"wire_ref":null},{"edge":54,"face":23,"from":32,"geometry_use":{"pcurve":94,"periodic_lift":[0,0],"sense":"Reverse"},"id":94,"loop_ref":23,"next":95,"prev":93,"to":4,"twin":91,"wire_ref":null},{"edge":8,"face":23,"from":4,"geometry_use":{"pcurve":95,"periodic_lift":[0,0],"sense":"Forward"},"id":95,"loop_ref":23,"next":92,"prev":94,"to":6,"twin":9,"wire_ref":null},{"edge":57,"face":24,"from":33,"geometry_use":{"pcurve":96,"periodic_lift":[0,0],"sense":"Forward"},"id":96,"loop_ref":24,"next":97,"prev":99,"to":35,"twin":170,"wire_ref":null},{"edge":58,"face":24,"from":35,"geometry_use":{"pcurve":97,"periodic_lift":[0,0],"sense":"Reverse"},"id":97,"loop_ref":24,"next":98,"prev":96,"to":9,"twin":107,"wire_ref":null},{"edge":12,"face":24,"from":9,"geometry_use":{"pcurve":98,"periodic_lift":[0,0],"sense":"Reverse"},"id":98,"loop_ref":24,"next":99,"prev":97,"to":5,"twin":16,"wire_ref":null},{"edge":53,"face":24,"from":5,"geometry_use":{"pcurve":99,"periodic_lift":[0,0],"sense":"Forward"},"id":99,"loop_ref":24,"next":96,"prev":98,"to":33,"twin":89,"wire_ref":null},{"edge":31,"face":25,"from":18,"geometry_use":{"pcurve":100,"periodic_lift":[0,0],"sense":"Forward"},"id":100,"loop_ref":25,"next":101,"prev":103,"to":21,"twin":47,"wire_ref":null},{"edge":59,"face":25,"from":21,"geometry_use":{"pcurve":101,"periodic_lift":[0,0],"sense":"Forward"},"id":101,"loop_ref":25,"next":102,"prev":100,"to":36,"twin":122,"wire_ref":null},{"edge":60,"face":25,"from":36,"geometry_use":{"pcurve":102,"periodic_lift":[0,0],"sense":"Reverse"},"id":102,"loop_ref":25,"next":103,"prev":101,"to":37,"twin":195,"wire_ref":null},{"edge":61,"face":25,"from":37,"geometry_use":{"pcurve":103,"periodic_lift":[0,0],"sense":"Reverse"},"id":103,"loop_ref":25,"next":100,"prev":102,"to":18,"twin":116,"wire_ref":null},{"edge":62,"face":26,"from":35,"geometry_use":{"pcurve":104,"periodic_lift":[0,0],"sense":"Forward"},"id":104,"loop_ref":26,"next":105,"prev":107,"to":38,"twin":178,"wire_ref":null},{"edge":63,"face":26,"from":38,"geometry_use":{"pcurve":105,"periodic_lift":[0,0],"sense":"Reverse"},"id":105,"loop_ref":26,"next":106,"prev":104,"to":12,"twin":108,"wire_ref":null},{"edge":17,"face":26,"from":12,"geometry_use":{"pcurve":106,"periodic_lift":[0,0],"sense":"Reverse"},"id":106,"loop_ref":26,"next":107,"prev":105,"to":9,"twin":24,"wire_ref":null},{"edge":58,"face":26,"from":9,"geometry_use":{"pcurve":107,"periodic_lift":[0,0],"sense":"Forward"},"id":107,"loop_ref":26,"next":104,"prev":106,"to":35,"twin":97,"wire_ref":null},{"edge":63,"face":27,"from":12,"geometry_use":{"pcurve":108,"periodic_lift":[0,0],"sense":"Forward"},"id":108,"loop_ref":27,"next":109,"prev":111,"to":38,"twin":105,"wire_ref":null},{"edge":64,"face":27,"from":38,"geometry_use":{"pcurve":109,"periodic_lift":[0,0],"sense":"Forward"},"id":109,"loop_ref":27,"next":110,"prev":108,"to":39,"twin":183,"wire_ref":null},{"edge":65,"face":27,"from":39,"geometry_use":{"pcurve":110,"periodic_lift":[0,0],"sense":"Reverse"},"id":110,"loop_ref":27,"next":111,"prev":109,"to":13,"twin":124,"wire_ref":null},{"edge":19,"face":27,"from":13,"geometry_use":{"pcurve":111,"periodic_lift":[0,0],"sense":"Reverse"},"id":111,"loop_ref":27,"next":108,"prev":110,"to":12,"twin":29,"wire_ref":null},{"edge":66,"face":28,"from":16,"geometry_use":{"pcurve":112,"periodic_lift":[0,0],"sense":"Forward"},"id":112,"loop_ref":28,"next":113,"prev":115,"to":40,"twin":134,"wire_ref":null},{"edge":67,"face":28,"from":40,"geometry_use":{"pcurve":113,"periodic_lift":[0,0],"sense":"Reverse"},"id":113,"loop_ref":28,"next":114,"prev":112,"to":34,"twin":191,"wire_ref":null},{"edge":55,"face":28,"from":34,"geometry_use":{"pcurve":114,"periodic_lift":[0,0],"sense":"Reverse"},"id":114,"loop_ref":28,"next":115,"prev":113,"to":6,"twin":92,"wire_ref":null},{"edge":25,"face":28,"from":6,"geometry_use":{"pcurve":115,"periodic_lift":[0,0],"sense":"Forward"},"id":115,"loop_ref":28,"next":112,"prev":114,"to":16,"twin":37,"wire_ref":null},{"edge":61,"face":29,"from":18,"geometry_use":{"pcurve":116,"periodic_lift":[0,0],"sense":"Forward"},"id":116,"loop_ref":29,"next":117,"prev":119,"to":37,"twin":103,"wire_ref":null},{"edge":68,"face":29,"from":37,"geometry_use":{"pcurve":117,"periodic_lift":[0,0],"sense":"Forward"},"id":117,"loop_ref":29,"next":118,"prev":116,"to":41,"twin":194,"wire_ref":null},{"edge":69,"face":29,"from":41,"geometry_use":{"pcurve":118,"periodic_lift":[0,0],"sense":"Reverse"},"id":118,"loop_ref":29,"next":119,"prev":117,"to":19,"twin":139,"wire_ref":null},{"edge":28,"face":29,"from":19,"geometry_use":{"pcurve":119,"periodic_lift":[0,0],"sense":"Reverse"},"id":119,"loop_ref":29,"next":116,"prev":118,"to":18,"twin":44,"wire_ref":null},{"edge":70,"face":30,"from":20,"geometry_use":{"pcurve":120,"periodic_lift":[0,0],"sense":"Forward"},"id":120,"loop_ref":30,"next":121,"prev":123,"to":42,"twin":137,"wire_ref":null},{"edge":71,"face":30,"from":42,"geometry_use":{"pcurve":121,"periodic_lift":[0,0],"sense":"Reverse"},"id":121,"loop_ref":30,"next":122,"prev":120,"to":36,"twin":192,"wire_ref":null},{"edge":59,"face":30,"from":36,"geometry_use":{"pcurve":122,"periodic_lift":[0,0],"sense":"Reverse"},"id":122,"loop_ref":30,"next":123,"prev":121,"to":21,"twin":101,"wire_ref":null},{"edge":30,"face":30,"from":21,"geometry_use":{"pcurve":123,"periodic_lift":[0,0],"sense":"Forward"},"id":123,"loop_ref":30,"next":120,"prev":122,"to":20,"twin":46,"wire_ref":null},{"edge":65,"face":31,"from":13,"geometry_use":{"pcurve":124,"periodic_lift":[0,0],"sense":"Forward"},"id":124,"loop_ref":31,"next":125,"prev":127,"to":39,"twin":110,"wire_ref":null},{"edge":72,"face":31,"from":39,"geometry_use":{"pcurve":125,"periodic_lift":[0,0],"sense":"Forward"},"id":125,"loop_ref":31,"next":126,"prev":124,"to":43,"twin":207,"wire_ref":null},{"edge":73,"face":31,"from":43,"geometry_use":{"pcurve":126,"periodic_lift":[0,0],"sense":"Reverse"},"id":126,"loop_ref":31,"next":127,"prev":125,"to":23,"twin":148,"wire_ref":null},{"edge":34,"face":31,"from":23,"geometry_use":{"pcurve":127,"periodic_lift":[0,0],"sense":"Reverse"},"id":127,"loop_ref":31,"next":124,"prev":126,"to":13,"twin":53,"wire_ref":null},{"edge":40,"face":32,"from":27,"geometry_use":{"pcurve":128,"periodic_lift":[0,0],"sense":"Forward"},"id":128,"loop_ref":32,"next":129,"prev":131,"to":26,"twin":62,"wire_ref":null},{"edge":74,"face":32,"from":26,"geometry_use":{"pcurve":129,"periodic_lift":[0,0],"sense":"Forward"},"id":129,"loop_ref":32,"next":130,"prev":128,"to":44,"twin":143,"wire_ref":null},{"edge":75,"face":32,"from":44,"geometry_use":{"pcurve":130,"periodic_lift":[0,0],"sense":"Reverse"},"id":130,"loop_ref":32,"next":131,"prev":129,"to":45,"twin":212,"wire_ref":null},{"edge":76,"face":32,"from":45,"geometry_use":{"pcurve":131,"periodic_lift":[0,0],"sense":"Reverse"},"id":131,"loop_ref":32,"next":128,"prev":130,"to":27,"twin":132,"wire_ref":null},{"edge":76,"face":33,"from":27,"geometry_use":{"pcurve":132,"periodic_lift":[0,0],"sense":"Forward"},"id":132,"loop_ref":33,"next":133,"prev":135,"to":45,"twin":131,"wire_ref":null},{"edge":77,"face":33,"from":45,"geometry_use":{"pcurve":133,"periodic_lift":[0,0],"sense":"Reverse"},"id":133,"loop_ref":33,"next":134,"prev":132,"to":40,"twin":219,"wire_ref":null},{"edge":66,"face":33,"from":40,"geometry_use":{"pcurve":134,"periodic_lift":[0,0],"sense":"Reverse"},"id":134,"loop_ref":33,"next":135,"prev":133,"to":16,"twin":112,"wire_ref":null},{"edge":42,"face":33,"from":16,"geometry_use":{"pcurve":135,"periodic_lift":[0,0],"sense":"Forward"},"id":135,"loop_ref":33,"next":132,"prev":134,"to":27,"twin":65,"wire_ref":null},{"edge":78,"face":34,"from":41,"geometry_use":{"pcurve":136,"periodic_lift":[0,0],"sense":"Forward"},"id":136,"loop_ref":34,"next":137,"prev":139,"to":42,"twin":193,"wire_ref":null},{"edge":70,"face":34,"from":42,"geometry_use":{"pcurve":137,"periodic_lift":[0,0],"sense":"Reverse"},"id":137,"loop_ref":34,"next":138,"prev":136,"to":20,"twin":120,"wire_ref":null},{"edge":29,"face":34,"from":20,"geometry_use":{"pcurve":138,"periodic_lift":[0,0],"sense":"Reverse"},"id":138,"loop_ref":34,"next":139,"prev":137,"to":19,"twin":45,"wire_ref":null},{"edge":69,"face":34,"from":19,"geometry_use":{"pcurve":139,"periodic_lift":[0,0],"sense":"Forward"},"id":139,"loop_ref":34,"next":136,"prev":138,"to":41,"twin":118,"wire_ref":null},{"edge":46,"face":35,"from":26,"geometry_use":{"pcurve":140,"periodic_lift":[0,0],"sense":"Forward"},"id":140,"loop_ref":35,"next":141,"prev":143,"to":29,"twin":74,"wire_ref":null},{"edge":79,"face":35,"from":29,"geometry_use":{"pcurve":141,"periodic_lift":[0,0],"sense":"Forward"},"id":141,"loop_ref":35,"next":142,"prev":140,"to":46,"twin":147,"wire_ref":null},{"edge":80,"face":35,"from":46,"geometry_use":{"pcurve":142,"periodic_lift":[0,0],"sense":"Reverse"},"id":142,"loop_ref":35,"next":143,"prev":141,"to":44,"twin":224,"wire_ref":null},{"edge":74,"face":35,"from":44,"geometry_use":{"pcurve":143,"periodic_lift":[0,0],"sense":"Reverse"},"id":143,"loop_ref":35,"next":140,"prev":142,"to":26,"twin":129,"wire_ref":null},{"edge":50,"face":36,"from":29,"geometry_use":{"pcurve":144,"periodic_lift":[0,0],"sense":"Forward"},"id":144,"loop_ref":36,"next":145,"prev":147,"to":31,"twin":82,"wire_ref":null},{"edge":81,"face":36,"from":31,"geometry_use":{"pcurve":145,"periodic_lift":[0,0],"sense":"Forward"},"id":145,"loop_ref":36,"next":146,"prev":144,"to":47,"twin":150,"wire_ref":null},{"edge":82,"face":36,"from":47,"geometry_use":{"pcurve":146,"periodic_lift":[0,0],"sense":"Reverse"},"id":146,"loop_ref":36,"next":147,"prev":145,"to":46,"twin":232,"wire_ref":null},{"edge":79,"face":36,"from":46,"geometry_use":{"pcurve":147,"periodic_lift":[0,0],"sense":"Reverse"},"id":147,"loop_ref":36,"next":144,"prev":146,"to":29,"twin":141,"wire_ref":null},{"edge":73,"face":37,"from":23,"geometry_use":{"pcurve":148,"periodic_lift":[0,0],"sense":"Forward"},"id":148,"loop_ref":37,"next":149,"prev":151,"to":43,"twin":126,"wire_ref":null},{"edge":83,"face":37,"from":43,"geometry_use":{"pcurve":149,"periodic_lift":[0,0],"sense":"Forward"},"id":149,"loop_ref":37,"next":150,"prev":148,"to":47,"twin":239,"wire_ref":null},{"edge":81,"face":37,"from":47,"geometry_use":{"pcurve":150,"periodic_lift":[0,0],"sense":"Reverse"},"id":150,"loop_ref":37,"next":151,"prev":149,"to":31,"twin":145,"wire_ref":null},{"edge":51,"face":37,"from":31,"geometry_use":{"pcurve":151,"periodic_lift":[0,0],"sense":"Reverse"},"id":151,"loop_ref":37,"next":148,"prev":150,"to":23,"twin":85,"wire_ref":null},{"edge":84,"face":38,"from":48,"geometry_use":{"pcurve":152,"periodic_lift":[0,0],"sense":"Forward"},"id":152,"loop_ref":38,"next":153,"prev":155,"to":49,"twin":161,"wire_ref":null},{"edge":85,"face":38,"from":49,"geometry_use":{"pcurve":153,"periodic_lift":[0,0],"sense":"Forward"},"id":153,"loop_ref":38,"next":154,"prev":152,"to":50,"twin":187,"wire_ref":null},{"edge":86,"face":38,"from":50,"geometry_use":{"pcurve":154,"periodic_lift":[0,0],"sense":"Reverse"},"id":154,"loop_ref":38,"next":155,"prev":153,"to":51,"twin":164,"wire_ref":null},{"edge":87,"face":38,"from":51,"geometry_use":{"pcurve":155,"periodic_lift":[0,0],"sense":"Reverse"},"id":155,"loop_ref":38,"next":152,"prev":154,"to":48,"twin":156,"wire_ref":null},{"edge":87,"face":39,"from":48,"geometry_use":{"pcurve":156,"periodic_lift":[0,0],"sense":"Forward"},"id":156,"loop_ref":39,"next":157,"prev":159,"to":51,"twin":155,"wire_ref":null},{"edge":88,"face":39,"from":51,"geometry_use":{"pcurve":157,"periodic_lift":[0,0],"sense":"Reverse"},"id":157,"loop_ref":39,"next":158,"prev":156,"to":33,"twin":171,"wire_ref":null},{"edge":52,"face":39,"from":33,"geometry_use":{"pcurve":158,"periodic_lift":[0,0],"sense":"Reverse"},"id":158,"loop_ref":39,"next":159,"prev":157,"to":32,"twin":88,"wire_ref":null},{"edge":89,"face":39,"from":32,"geometry_use":{"pcurve":159,"periodic_lift":[0,0],"sense":"Forward"},"id":159,"loop_ref":39,"next":156,"prev":158,"to":48,"twin":162,"wire_ref":null},{"edge":90,"face":40,"from":34,"geometry_use":{"pcurve":160,"periodic_lift":[0,0],"sense":"Forward"},"id":160,"loop_ref":40,"next":161,"prev":163,"to":49,"twin":190,"wire_ref":null},{"edge":84,"face":40,"from":49,"geometry_use":{"pcurve":161,"periodic_lift":[0,0],"sense":"Reverse"},"id":161,"loop_ref":40,"next":162,"prev":160,"to":48,"twin":152,"wire_ref":null},{"edge":89,"face":40,"from":48,"geometry_use":{"pcurve":162,"periodic_lift":[0,0],"sense":"Reverse"},"id":162,"loop_ref":40,"next":163,"prev":161,"to":32,"twin":159,"wire_ref":null},{"edge":56,"face":40,"from":32,"geometry_use":{"pcurve":163,"periodic_lift":[0,0],"sense":"Forward"},"id":163,"loop_ref":40,"next":160,"prev":162,"to":34,"twin":93,"wire_ref":null},{"edge":86,"face":41,"from":51,"geometry_use":{"pcurve":164,"periodic_lift":[0,0],"sense":"Forward"},"id":164,"loop_ref":41,"next":165,"prev":167,"to":50,"twin":154,"wire_ref":null},{"edge":91,"face":41,"from":50,"geometry_use":{"pcurve":165,"periodic_lift":[0,0],"sense":"Forward"},"id":165,"loop_ref":41,"next":166,"prev":164,"to":52,"twin":199,"wire_ref":null},{"edge":92,"face":41,"from":52,"geometry_use":{"pcurve":166,"periodic_lift":[0,0],"sense":"Reverse"},"id":166,"loop_ref":41,"next":167,"prev":165,"to":53,"twin":172,"wire_ref":null},{"edge":93,"face":41,"from":53,"geometry_use":{"pcurve":167,"periodic_lift":[0,0],"sense":"Reverse"},"id":167,"loop_ref":41,"next":164,"prev":166,"to":51,"twin":168,"wire_ref":null},{"edge":93,"face":42,"from":51,"geometry_use":{"pcurve":168,"periodic_lift":[0,0],"sense":"Forward"},"id":168,"loop_ref":42,"next":169,"prev":171,"to":53,"twin":167,"wire_ref":null},{"edge":94,"face":42,"from":53,"geometry_use":{"pcurve":169,"periodic_lift":[0,0],"sense":"Reverse"},"id":169,"loop_ref":42,"next":170,"prev":168,"to":35,"twin":179,"wire_ref":null},{"edge":57,"face":42,"from":35,"geometry_use":{"pcurve":170,"periodic_lift":[0,0],"sense":"Reverse"},"id":170,"loop_ref":42,"next":171,"prev":169,"to":33,"twin":96,"wire_ref":null},{"edge":88,"face":42,"from":33,"geometry_use":{"pcurve":171,"periodic_lift":[0,0],"sense":"Forward"},"id":171,"loop_ref":42,"next":168,"prev":170,"to":51,"twin":157,"wire_ref":null},{"edge":92,"face":43,"from":53,"geometry_use":{"pcurve":172,"periodic_lift":[0,0],"sense":"Forward"},"id":172,"loop_ref":43,"next":173,"prev":175,"to":52,"twin":166,"wire_ref":null},{"edge":95,"face":43,"from":52,"geometry_use":{"pcurve":173,"periodic_lift":[0,0],"sense":"Forward"},"id":173,"loop_ref":43,"next":174,"prev":172,"to":54,"twin":203,"wire_ref":null},{"edge":96,"face":43,"from":54,"geometry_use":{"pcurve":174,"periodic_lift":[0,0],"sense":"Reverse"},"id":174,"loop_ref":43,"next":175,"prev":173,"to":55,"twin":181,"wire_ref":null},{"edge":97,"face":43,"from":55,"geometry_use":{"pcurve":175,"periodic_lift":[0,0],"sense":"Reverse"},"id":175,"loop_ref":43,"next":172,"prev":174,"to":53,"twin":176,"wire_ref":null},{"edge":97,"face":44,"from":53,"geometry_use":{"pcurve":176,"periodic_lift":[0,0],"sense":"Forward"},"id":176,"loop_ref":44,"next":177,"prev":179,"to":55,"twin":175,"wire_ref":null},{"edge":98,"face":44,"from":55,"geometry_use":{"pcurve":177,"periodic_lift":[0,0],"sense":"Reverse"},"id":177,"loop_ref":44,"next":178,"prev":176,"to":38,"twin":180,"wire_ref":null},{"edge":62,"face":44,"from":38,"geometry_use":{"pcurve":178,"periodic_lift":[0,0],"sense":"Reverse"},"id":178,"loop_ref":44,"next":179,"prev":177,"to":35,"twin":104,"wire_ref":null},{"edge":94,"face":44,"from":35,"geometry_use":{"pcurve":179,"periodic_lift":[0,0],"sense":"Forward"},"id":179,"loop_ref":44,"next":176,"prev":178,"to":53,"twin":169,"wire_ref":null},{"edge":98,"face":45,"from":38,"geometry_use":{"pcurve":180,"periodic_lift":[0,0],"sense":"Forward"},"id":180,"loop_ref":45,"next":181,"prev":183,"to":55,"twin":177,"wire_ref":null},{"edge":96,"face":45,"from":55,"geometry_use":{"pcurve":181,"periodic_lift":[0,0],"sense":"Forward"},"id":181,"loop_ref":45,"next":182,"prev":180,"to":54,"twin":174,"wire_ref":null},{"edge":99,"face":45,"from":54,"geometry_use":{"pcurve":182,"periodic_lift":[0,0],"sense":"Reverse"},"id":182,"loop_ref":45,"next":183,"prev":181,"to":39,"twin":204,"wire_ref":null},{"edge":64,"face":45,"from":39,"geometry_use":{"pcurve":183,"periodic_lift":[0,0],"sense":"Reverse"},"id":183,"loop_ref":45,"next":180,"prev":182,"to":38,"twin":109,"wire_ref":null},{"edge":100,"face":46,"from":49,"geometry_use":{"pcurve":184,"periodic_lift":[0,0],"sense":"Forward"},"id":184,"loop_ref":46,"next":185,"prev":187,"to":56,"twin":189,"wire_ref":null},{"edge":101,"face":46,"from":56,"geometry_use":{"pcurve":185,"periodic_lift":[0,0],"sense":"Forward"},"id":185,"loop_ref":46,"next":186,"prev":184,"to":57,"twin":211,"wire_ref":null},{"edge":102,"face":46,"from":57,"geometry_use":{"pcurve":186,"periodic_lift":[0,0],"sense":"Reverse"},"id":186,"loop_ref":46,"next":187,"prev":185,"to":50,"twin":196,"wire_ref":null},{"edge":85,"face":46,"from":50,"geometry_use":{"pcurve":187,"periodic_lift":[0,0],"sense":"Reverse"},"id":187,"loop_ref":46,"next":184,"prev":186,"to":49,"twin":153,"wire_ref":null},{"edge":103,"face":47,"from":40,"geometry_use":{"pcurve":188,"periodic_lift":[0,0],"sense":"Forward"},"id":188,"loop_ref":47,"next":189,"prev":191,"to":56,"twin":218,"wire_ref":null},{"edge":100,"face":47,"from":56,"geometry_use":{"pcurve":189,"periodic_lift":[0,0],"sense":"Reverse"},"id":189,"loop_ref":47,"next":190,"prev":188,"to":49,"twin":184,"wire_ref":null},{"edge":90,"face":47,"from":49,"geometry_use":{"pcurve":190,"periodic_lift":[0,0],"sense":"Reverse"},"id":190,"loop_ref":47,"next":191,"prev":189,"to":34,"twin":160,"wire_ref":null},{"edge":67,"face":47,"from":34,"geometry_use":{"pcurve":191,"periodic_lift":[0,0],"sense":"Forward"},"id":191,"loop_ref":47,"next":188,"prev":190,"to":40,"twin":113,"wire_ref":null},{"edge":71,"face":48,"from":36,"geometry_use":{"pcurve":192,"periodic_lift":[0,0],"sense":"Forward"},"id":192,"loop_ref":48,"next":193,"prev":195,"to":42,"twin":121,"wire_ref":null},{"edge":78,"face":48,"from":42,"geometry_use":{"pcurve":193,"periodic_lift":[0,0],"sense":"Reverse"},"id":193,"loop_ref":48,"next":194,"prev":192,"to":41,"twin":136,"wire_ref":null},{"edge":68,"face":48,"from":41,"geometry_use":{"pcurve":194,"periodic_lift":[0,0],"sense":"Reverse"},"id":194,"loop_ref":48,"next":195,"prev":193,"to":37,"twin":117,"wire_ref":null},{"edge":60,"face":48,"from":37,"geometry_use":{"pcurve":195,"periodic_lift":[0,0],"sense":"Forward"},"id":195,"loop_ref":48,"next":192,"prev":194,"to":36,"twin":102,"wire_ref":null},{"edge":102,"face":49,"from":50,"geometry_use":{"pcurve":196,"periodic_lift":[0,0],"sense":"Forward"},"id":196,"loop_ref":49,"next":197,"prev":199,"to":57,"twin":186,"wire_ref":null},{"edge":104,"face":49,"from":57,"geometry_use":{"pcurve":197,"periodic_lift":[0,0],"sense":"Forward"},"id":197,"loop_ref":49,"next":198,"prev":196,"to":58,"twin":223,"wire_ref":null},{"edge":105,"face":49,"from":58,"geometry_use":{"pcurve":198,"periodic_lift":[0,0],"sense":"Reverse"},"id":198,"loop_ref":49,"next":199,"prev":197,"to":52,"twin":200,"wire_ref":null},{"edge":91,"face":49,"from":52,"geometry_use":{"pcurve":199,"periodic_lift":[0,0],"sense":"Reverse"},"id":199,"loop_ref":49,"next":196,"prev":198,"to":50,"twin":165,"wire_ref":null},{"edge":105,"face":50,"from":52,"geometry_use":{"pcurve":200,"periodic_lift":[0,0],"sense":"Forward"},"id":200,"loop_ref":50,"next":201,"prev":203,"to":58,"twin":198,"wire_ref":null},{"edge":106,"face":50,"from":58,"geometry_use":{"pcurve":201,"periodic_lift":[0,0],"sense":"Forward"},"id":201,"loop_ref":50,"next":202,"prev":200,"to":59,"twin":231,"wire_ref":null},{"edge":107,"face":50,"from":59,"geometry_use":{"pcurve":202,"periodic_lift":[0,0],"sense":"Reverse"},"id":202,"loop_ref":50,"next":203,"prev":201,"to":54,"twin":205,"wire_ref":null},{"edge":95,"face":50,"from":54,"geometry_use":{"pcurve":203,"periodic_lift":[0,0],"sense":"Reverse"},"id":203,"loop_ref":50,"next":200,"prev":202,"to":52,"twin":173,"wire_ref":null},{"edge":99,"face":51,"from":39,"geometry_use":{"pcurve":204,"periodic_lift":[0,0],"sense":"Forward"},"id":204,"loop_ref":51,"next":205,"prev":207,"to":54,"twin":182,"wire_ref":null},{"edge":107,"face":51,"from":54,"geometry_use":{"pcurve":205,"periodic_lift":[0,0],"sense":"Forward"},"id":205,"loop_ref":51,"next":206,"prev":204,"to":59,"twin":202,"wire_ref":null},{"edge":108,"face":51,"from":59,"geometry_use":{"pcurve":206,"periodic_lift":[0,0],"sense":"Reverse"},"id":206,"loop_ref":51,"next":207,"prev":205,"to":43,"twin":236,"wire_ref":null},{"edge":72,"face":51,"from":43,"geometry_use":{"pcurve":207,"periodic_lift":[0,0],"sense":"Reverse"},"id":207,"loop_ref":51,"next":204,"prev":206,"to":39,"twin":125,"wire_ref":null},{"edge":109,"face":52,"from":56,"geometry_use":{"pcurve":208,"periodic_lift":[0,0],"sense":"Forward"},"id":208,"loop_ref":52,"next":209,"prev":211,"to":60,"twin":217,"wire_ref":null},{"edge":110,"face":52,"from":60,"geometry_use":{"pcurve":209,"periodic_lift":[0,0],"sense":"Forward"},"id":209,"loop_ref":52,"next":210,"prev":208,"to":61,"twin":214,"wire_ref":null},{"edge":111,"face":52,"from":61,"geometry_use":{"pcurve":210,"periodic_lift":[0,0],"sense":"Reverse"},"id":210,"loop_ref":52,"next":211,"prev":209,"to":57,"twin":220,"wire_ref":null},{"edge":101,"face":52,"from":57,"geometry_use":{"pcurve":211,"periodic_lift":[0,0],"sense":"Reverse"},"id":211,"loop_ref":52,"next":208,"prev":210,"to":56,"twin":185,"wire_ref":null},{"edge":75,"face":53,"from":45,"geometry_use":{"pcurve":212,"periodic_lift":[0,0],"sense":"Forward"},"id":212,"loop_ref":53,"next":213,"prev":215,"to":44,"twin":130,"wire_ref":null},{"edge":112,"face":53,"from":44,"geometry_use":{"pcurve":213,"periodic_lift":[0,0],"sense":"Forward"},"id":213,"loop_ref":53,"next":214,"prev":212,"to":61,"twin":227,"wire_ref":null},{"edge":110,"face":53,"from":61,"geometry_use":{"pcurve":214,"periodic_lift":[0,0],"sense":"Reverse"},"id":214,"loop_ref":53,"next":215,"prev":213,"to":60,"twin":209,"wire_ref":null},{"edge":113,"face":53,"from":60,"geometry_use":{"pcurve":215,"periodic_lift":[0,0],"sense":"Reverse"},"id":215,"loop_ref":53,"next":212,"prev":214,"to":45,"twin":216,"wire_ref":null},{"edge":113,"face":54,"from":45,"geometry_use":{"pcurve":216,"periodic_lift":[0,0],"sense":"Forward"},"id":216,"loop_ref":54,"next":217,"prev":219,"to":60,"twin":215,"wire_ref":null},{"edge":109,"face":54,"from":60,"geometry_use":{"pcurve":217,"periodic_lift":[0,0],"sense":"Reverse"},"id":217,"loop_ref":54,"next":218,"prev":216,"to":56,"twin":208,"wire_ref":null},{"edge":103,"face":54,"from":56,"geometry_use":{"pcurve":218,"periodic_lift":[0,0],"sense":"Reverse"},"id":218,"loop_ref":54,"next":219,"prev":217,"to":40,"twin":188,"wire_ref":null},{"edge":77,"face":54,"from":40,"geometry_use":{"pcurve":219,"periodic_lift":[0,0],"sense":"Forward"},"id":219,"loop_ref":54,"next":216,"prev":218,"to":45,"twin":133,"wire_ref":null},{"edge":111,"face":55,"from":57,"geometry_use":{"pcurve":220,"periodic_lift":[0,0],"sense":"Forward"},"id":220,"loop_ref":55,"next":221,"prev":223,"to":61,"twin":210,"wire_ref":null},{"edge":114,"face":55,"from":61,"geometry_use":{"pcurve":221,"periodic_lift":[0,0],"sense":"Forward"},"id":221,"loop_ref":55,"next":222,"prev":220,"to":62,"twin":226,"wire_ref":null},{"edge":115,"face":55,"from":62,"geometry_use":{"pcurve":222,"periodic_lift":[0,0],"sense":"Reverse"},"id":222,"loop_ref":55,"next":223,"prev":221,"to":58,"twin":228,"wire_ref":null},{"edge":104,"face":55,"from":58,"geometry_use":{"pcurve":223,"periodic_lift":[0,0],"sense":"Reverse"},"id":223,"loop_ref":55,"next":220,"prev":222,"to":57,"twin":197,"wire_ref":null},{"edge":80,"face":56,"from":44,"geometry_use":{"pcurve":224,"periodic_lift":[0,0],"sense":"Forward"},"id":224,"loop_ref":56,"next":225,"prev":227,"to":46,"twin":142,"wire_ref":null},{"edge":116,"face":56,"from":46,"geometry_use":{"pcurve":225,"periodic_lift":[0,0],"sense":"Forward"},"id":225,"loop_ref":56,"next":226,"prev":224,"to":62,"twin":235,"wire_ref":null},{"edge":114,"face":56,"from":62,"geometry_use":{"pcurve":226,"periodic_lift":[0,0],"sense":"Reverse"},"id":226,"loop_ref":56,"next":227,"prev":225,"to":61,"twin":221,"wire_ref":null},{"edge":112,"face":56,"from":61,"geometry_use":{"pcurve":227,"periodic_lift":[0,0],"sense":"Reverse"},"id":227,"loop_ref":56,"next":224,"prev":226,"to":44,"twin":213,"wire_ref":null},{"edge":115,"face":57,"from":58,"geometry_use":{"pcurve":228,"periodic_lift":[0,0],"sense":"Forward"},"id":228,"loop_ref":57,"next":229,"prev":231,"to":62,"twin":222,"wire_ref":null},{"edge":117,"face":57,"from":62,"geometry_use":{"pcurve":229,"periodic_lift":[0,0],"sense":"Forward"},"id":229,"loop_ref":57,"next":230,"prev":228,"to":63,"twin":234,"wire_ref":null},{"edge":118,"face":57,"from":63,"geometry_use":{"pcurve":230,"periodic_lift":[0,0],"sense":"Reverse"},"id":230,"loop_ref":57,"next":231,"prev":229,"to":59,"twin":237,"wire_ref":null},{"edge":106,"face":57,"from":59,"geometry_use":{"pcurve":231,"periodic_lift":[0,0],"sense":"Reverse"},"id":231,"loop_ref":57,"next":228,"prev":230,"to":58,"twin":201,"wire_ref":null},{"edge":82,"face":58,"from":46,"geometry_use":{"pcurve":232,"periodic_lift":[0,0],"sense":"Forward"},"id":232,"loop_ref":58,"next":233,"prev":235,"to":47,"twin":146,"wire_ref":null},{"edge":119,"face":58,"from":47,"geometry_use":{"pcurve":233,"periodic_lift":[0,0],"sense":"Forward"},"id":233,"loop_ref":58,"next":234,"prev":232,"to":63,"twin":238,"wire_ref":null},{"edge":117,"face":58,"from":63,"geometry_use":{"pcurve":234,"periodic_lift":[0,0],"sense":"Reverse"},"id":234,"loop_ref":58,"next":235,"prev":233,"to":62,"twin":229,"wire_ref":null},{"edge":116,"face":58,"from":62,"geometry_use":{"pcurve":235,"periodic_lift":[0,0],"sense":"Reverse"},"id":235,"loop_ref":58,"next":232,"prev":234,"to":46,"twin":225,"wire_ref":null},{"edge":108,"face":59,"from":43,"geometry_use":{"pcurve":236,"periodic_lift":[0,0],"sense":"Forward"},"id":236,"loop_ref":59,"next":237,"prev":239,"to":59,"twin":206,"wire_ref":null},{"edge":118,"face":59,"from":59,"geometry_use":{"pcurve":237,"periodic_lift":[0,0],"sense":"Forward"},"id":237,"loop_ref":59,"next":238,"prev":236,"to":63,"twin":230,"wire_ref":null},{"edge":119,"face":59,"from":63,"geometry_use":{"pcurve":238,"periodic_lift":[0,0],"sense":"Reverse"},"id":238,"loop_ref":59,"next":239,"prev":237,"to":47,"twin":233,"wire_ref":null},{"edge":83,"face":59,"from":47,"geometry_use":{"pcurve":239,"periodic_lift":[0,0],"sense":"Reverse"},"id":239,"loop_ref":59,"next":236,"prev":238,"to":43,"twin":149,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":48},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":52},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":56},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":60},{"face_ref":16,"id":16,"is_hole":false,"start_halfedge":64},{"face_ref":17,"id":17,"is_hole":false,"start_halfedge":68},{"face_ref":18,"id":18,"is_hole":false,"start_halfedge":72},{"face_ref":19,"id":19,"is_hole":false,"start_halfedge":76},{"face_ref":20,"id":20,"is_hole":false,"start_halfedge":80},{"face_ref":21,"id":21,"is_hole":false,"start_halfedge":84},{"face_ref":22,"id":22,"is_hole":false,"start_halfedge":88},{"face_ref":23,"id":23,"is_hole":false,"start_halfedge":92},{"face_ref":24,"id":24,"is_hole":false,"start_halfedge":96},{"face_ref":25,"id":25,"is_hole":false,"start_halfedge":100},{"face_ref":26,"id":26,"is_hole":false,"start_halfedge":104},{"face_ref":27,"id":27,"is_hole":false,"start_halfedge":108},{"face_ref":28,"id":28,"is_hole":false,"start_halfedge":112},{"face_ref":29,"id":29,"is_hole":false,"start_halfedge":116},{"face_ref":30,"id":30,"is_hole":false,"start_halfedge":120},{"face_ref":31,"id":31,"is_hole":false,"start_halfedge":124},{"face_ref":32,"id":32,"is_hole":false,"start_halfedge":128},{"face_ref":33,"id":33,"is_hole":false,"start_halfedge":132},{"face_ref":34,"id":34,"is_hole":false,"start_halfedge":136},{"face_ref":35,"id":35,"is_hole":false,"start_halfedge":140},{"face_ref":36,"id":36,"is_hole":false,"start_halfedge":144},{"face_ref":37,"id":37,"is_hole":false,"start_halfedge":148},{"face_ref":38,"id":38,"is_hole":false,"start_halfedge":152},{"face_ref":39,"id":39,"is_hole":false,"start_halfedge":156},{"face_ref":40,"id":40,"is_hole":false,"start_halfedge":160},{"face_ref":41,"id":41,"is_hole":false,"start_halfedge":164},{"face_ref":42,"id":42,"is_hole":false,"start_halfedge":168},{"face_ref":43,"id":43,"is_hole":false,"start_halfedge":172},{"face_ref":44,"id":44,"is_hole":false,"start_halfedge":176},{"face_ref":45,"id":45,"is_hole":false,"start_halfedge":180},{"face_ref":46,"id":46,"is_hole":false,"start_halfedge":184},{"face_ref":47,"id":47,"is_hole":false,"start_halfedge":188},{"face_ref":48,"id":48,"is_hole":false,"start_halfedge":192},{"face_ref":49,"id":49,"is_hole":false,"start_halfedge":196},{"face_ref":50,"id":50,"is_hole":false,"start_halfedge":200},{"face_ref":51,"id":51,"is_hole":false,"start_halfedge":204},{"face_ref":52,"id":52,"is_hole":false,"start_halfedge":208},{"face_ref":53,"id":53,"is_hole":false,"start_halfedge":212},{"face_ref":54,"id":54,"is_hole":false,"start_halfedge":216},{"face_ref":55,"id":55,"is_hole":false,"start_halfedge":220},{"face_ref":56,"id":56,"is_hole":false,"start_halfedge":224},{"face_ref":57,"id":57,"is_hole":false,"start_halfedge":228},{"face_ref":58,"id":58,"is_hole":false,"start_halfedge":232},{"face_ref":59,"id":59,"is_hole":false,"start_halfedge":236}],"shells":[{"faces":[0,3,8,2,1,5,10,4,14,9,23,22,7,12,6,17,24,15,16,28,40,39,27,13,19,26,18,42,32,33,47,38,45,31,21,20,44,35,41,53,54,46,43,51,37,36,56,49,52,50,59,58,55,57],"id":0,"is_closed":true},{"faces":[11,29,34,30,25,48],"id":1,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,1.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":1,"position":[0.0,1.0,-1.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":2,"position":[0.0,0.0,-1.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":3,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[1.0,1.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":9,"position":[1.0,0.0,-1.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":12,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":8,"outgoing_halfedge":13,"position":[0.0,2.0,-1.0],"tolerance":1e-8},{"id":9,"outgoing_halfedge":17,"position":[1.0,2.0,0.0],"tolerance":1e-8},{"id":10,"outgoing_halfedge":20,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":11,"outgoing_halfedge":21,"position":[0.0,3.0,-1.0],"tolerance":1e-8},{"id":12,"outgoing_halfedge":25,"position":[1.0,3.0,0.0],"tolerance":1e-8},{"id":13,"outgoing_halfedge":30,"position":[1.0,3.0,-1.0],"tolerance":1e-8},{"id":14,"outgoing_halfedge":33,"position":[0.0,1.0,-2.0],"tolerance":1e-8},{"id":15,"outgoing_halfedge":34,"position":[0.0,0.0,-2.0],"tolerance":1e-8},{"id":16,"outgoing_halfedge":37,"position":[1.0,0.0,-2.0],"tolerance":1e-8},{"id":17,"outgoing_halfedge":41,"position":[0.0,2.0,-2.0],"tolerance":1e-8},{"id":18,"outgoing_halfedge":44,"position":[1.0,1.0,-1.0],"tolerance":1e-8},{"id":19,"outgoing_halfedge":45,"position":[1.0,1.0,-2.0],"tolerance":1e-8},{"id":20,"outgoing_halfedge":46,"position":[1.0,2.0,-2.0],"tolerance":1e-8},{"id":21,"outgoing_halfedge":47,"position":[1.0,2.0,-1.0],"tolerance":1e-8},{"id":22,"outgoing_halfedge":49,"position":[0.0,3.0,-2.0],"tolerance":1e-8},{"id":23,"outgoing_halfedge":54,"position":[1.0,3.0,-2.0],"tolerance":1e-8},{"id":24,"outgoing_halfedge":57,"position":[0.0,1.0,-3.0],"tolerance":1e-8},{"id":25,"outgoing_halfedge":58,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":26,"outgoing_halfedge":62,"position":[1.0,1.0,-3.0],"tolerance":1e-8},{"id":27,"outgoing_halfedge":63,"position":[1.0,0.0,-3.0],"tolerance":1e-8},{"id":28,"outgoing_halfedge":69,"position":[0.0,2.0,-3.0],"tolerance":1e-8},{"id":29,"outgoing_halfedge":74,"position":[1.0,2.0,-3.0],"tolerance":1e-8},{"id":30,"outgoing_halfedge":77,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":31,"outgoing_halfedge":82,"position":[1.0,3.0,-3.0],"tolerance":1e-8},{"id":32,"outgoing_halfedge":88,"position":[2.0,0.0,0.0],"tolerance":1e-8},{"id":33,"outgoing_halfedge":89,"position":[2.0,1.0,0.0],"tolerance":1e-8},{"id":34,"outgoing_halfedge":93,"position":[2.0,0.0,-1.0],"tolerance":1e-8},{"id":35,"outgoing_halfedge":97,"position":[2.0,2.0,0.0],"tolerance":1e-8},{"id":36,"outgoing_halfedge":102,"position":[2.0,2.0,-1.0],"tolerance":1e-8},{"id":37,"outgoing_halfedge":103,"position":[2.0,1.0,-1.0],"tolerance":1e-8},{"id":38,"outgoing_halfedge":105,"position":[2.0,3.0,0.0],"tolerance":1e-8},{"id":39,"outgoing_halfedge":110,"position":[2.0,3.0,-1.0],"tolerance":1e-8},{"id":40,"outgoing_halfedge":113,"position":[2.0,0.0,-2.0],"tolerance":1e-8},{"id":41,"outgoing_halfedge":118,"position":[2.0,1.0,-2.0],"tolerance":1e-8},{"id":42,"outgoing_halfedge":121,"position":[2.0,2.0,-2.0],"tolerance":1e-8},{"id":43,"outgoing_halfedge":126,"position":[2.0,3.0,-2.0],"tolerance":1e-8},{"id":44,"outgoing_halfedge":130,"position":[2.0,1.0,-3.0],"tolerance":1e-8},{"id":45,"outgoing_halfedge":131,"position":[2.0,0.0,-3.0],"tolerance":1e-8},{"id":46,"outgoing_halfedge":142,"position":[2.0,2.0,-3.0],"tolerance":1e-8},{"id":47,"outgoing_halfedge":146,"position":[2.0,3.0,-3.0],"tolerance":1e-8},{"id":48,"outgoing_halfedge":152,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":49,"outgoing_halfedge":153,"position":[3.0,0.0,-1.0],"tolerance":1e-8},{"id":50,"outgoing_halfedge":154,"position":[3.0,1.0,-1.0],"tolerance":1e-8},{"id":51,"outgoing_halfedge":155,"position":[3.0,1.0,0.0],"tolerance":1e-8},{"id":52,"outgoing_halfedge":166,"position":[3.0,2.0,-1.0],"tolerance":1e-8},{"id":53,"outgoing_halfedge":167,"position":[3.0,2.0,0.0],"tolerance":1e-8},{"id":54,"outgoing_halfedge":174,"position":[3.0,3.0,-1.0],"tolerance":1e-8},{"id":55,"outgoing_halfedge":175,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":56,"outgoing_halfedge":185,"position":[3.0,0.0,-2.0],"tolerance":1e-8},{"id":57,"outgoing_halfedge":186,"position":[3.0,1.0,-2.0],"tolerance":1e-8},{"id":58,"outgoing_halfedge":198,"position":[3.0,2.0,-2.0],"tolerance":1e-8},{"id":59,"outgoing_halfedge":202,"position":[3.0,3.0,-2.0],"tolerance":1e-8},{"id":60,"outgoing_halfedge":209,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":61,"outgoing_halfedge":210,"position":[3.0,1.0,-3.0],"tolerance":1e-8},{"id":62,"outgoing_halfedge":222,"position":[3.0,2.0,-3.0],"tolerance":1e-8},{"id":63,"outgoing_halfedge":230,"position":[3.0,3.0,-3.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-box-cavity.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-box-cavity.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-box-cavity.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-box-cut.json b/main/opengeometry/tests/fixtures/cases/validity/builder-box-cut.json new file mode 100644 index 0000000..6853e9a --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-box-cut.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.5,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,3.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.5,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,-3.0]}],"intersections":[],"pcurves":[{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"}]},"id":"box-cut","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-8,"twin_halfedge":6},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":7,"id":6,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":8,"id":7,"tolerance":1e-8,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-8,"twin_halfedge":63},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":12,"id":9,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-8,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":16,"id":12,"tolerance":1e-8,"twin_halfedge":70},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":17,"id":13,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":21,"id":14,"tolerance":1e-8,"twin_halfedge":79},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":22,"id":15,"tolerance":1e-8,"twin_halfedge":48},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":24,"id":16,"tolerance":1e-8,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":25,"id":17,"tolerance":1e-8,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":26,"id":18,"tolerance":1e-8,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":29,"id":19,"tolerance":1e-8,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":30,"id":20,"tolerance":1e-8,"twin_halfedge":84},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":31,"id":21,"tolerance":1e-8,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":33,"id":22,"tolerance":1e-8,"twin_halfedge":91},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":36,"id":23,"tolerance":1e-8,"twin_halfedge":51},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":37,"id":24,"tolerance":1e-8,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":40,"id":25,"tolerance":1e-8,"twin_halfedge":95},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":41,"id":26,"tolerance":1e-8,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":42,"id":27,"tolerance":1e-8,"twin_halfedge":49},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":43,"id":28,"tolerance":1e-8,"twin_halfedge":72},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":45,"id":29,"tolerance":1e-8,"twin_halfedge":50},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":52,"id":30,"tolerance":1e-8,"twin_halfedge":61},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":53,"id":31,"tolerance":1e-8,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":54,"id":32,"tolerance":1e-8,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":55,"id":33,"tolerance":1e-8,"twin_halfedge":56},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":57,"id":34,"tolerance":1e-8,"twin_halfedge":71},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":59,"id":35,"tolerance":1e-8,"twin_halfedge":62},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":60,"id":36,"tolerance":1e-8,"twin_halfedge":90},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":65,"id":37,"tolerance":1e-8,"twin_halfedge":74},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":66,"id":38,"tolerance":1e-8,"twin_halfedge":77},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":67,"id":39,"tolerance":1e-8,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":69,"id":40,"tolerance":1e-8,"twin_halfedge":76},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":73,"id":41,"tolerance":1e-8,"twin_halfedge":78},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":75,"id":42,"tolerance":1e-8,"twin_halfedge":92},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":80,"id":43,"tolerance":1e-8,"twin_halfedge":89},{"chart_seam":false,"geometry":{"curve":44,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":81,"id":44,"tolerance":1e-8,"twin_halfedge":86},{"chart_seam":false,"geometry":{"curve":45,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":82,"id":45,"tolerance":1e-8,"twin_halfedge":93},{"chart_seam":false,"geometry":{"curve":46,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":85,"id":46,"tolerance":1e-8,"twin_halfedge":94},{"chart_seam":false,"geometry":{"curve":47,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":87,"id":47,"tolerance":1e-8,"twin_halfedge":88}],"faces":[{"id":0,"key":"left-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"front-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":2,"key":"bottom-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":1.50000001,"lo":-1e-8}]}},{"id":3,"key":"left-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":4,"key":"front-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.49999999}]}},{"id":5,"key":"top-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":6,"key":"left-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.49999999}]}},{"id":7,"key":"back-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":1.50000001,"lo":-1e-8}]}},{"id":8,"key":"bottom-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":1.50000001,"lo":-1e-8}]}},{"id":9,"key":"left-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":3.00000001,"lo":0.49999999}]}},{"id":10,"key":"left-10","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"box-cutter","entity":"box-cutter","face":2,"key":"left"}]},"sense":"Reverse","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":2.50000001,"lo":-1e-8},{"hi":2.50000001,"lo":-1e-8}]}},{"id":11,"key":"back-11","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":1.50000001,"lo":-1e-8}]}},{"id":12,"key":"top-12","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.49999999}]}},{"id":13,"key":"right-13","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":14,"key":"front-14","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":3.00000001,"lo":1.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":15,"key":"bottom-15","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.49999999}]}},{"id":16,"key":"right-16","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[],"outer":16,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.49999999}]}},{"id":17,"key":"front-17","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":17,"trim":{"chart":0,"holes":[],"outer":17,"uv_bounds":[{"hi":3.00000001,"lo":1.49999999},{"hi":3.00000001,"lo":0.49999999}]}},{"id":18,"key":"front-18","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"box-cutter","entity":"box-cutter","face":4,"key":"front"}]},"sense":"Reverse","shell_ref":0,"surface":18,"trim":{"chart":0,"holes":[],"outer":18,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":2.50000001,"lo":-1e-8}]}},{"id":19,"key":"top-19","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":19,"trim":{"chart":0,"holes":[],"outer":19,"uv_bounds":[{"hi":3.00000001,"lo":1.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":20,"key":"right-20","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":20,"trim":{"chart":0,"holes":[],"outer":20,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":21,"key":"back-21","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":21,"trim":{"chart":0,"holes":[],"outer":21,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.49999999}]}},{"id":22,"key":"bottom-22","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":22,"trim":{"chart":0,"holes":[],"outer":22,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":3.00000001,"lo":1.49999999}]}},{"id":23,"key":"bottom-23","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"box-cutter","entity":"box-cutter","face":0,"key":"bottom"}]},"sense":"Reverse","shell_ref":0,"surface":23,"trim":{"chart":0,"holes":[],"outer":23,"uv_bounds":[{"hi":2.50000001,"lo":-1e-8},{"hi":1.50000001,"lo":-1e-8}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":14,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":27,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":11,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":6,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":58,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":0,"twin":19,"wire_ref":null},{"edge":3,"face":1,"from":0,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":3,"twin":3,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":10,"wire_ref":null},{"edge":7,"face":2,"from":2,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":6,"twin":34,"wire_ref":null},{"edge":8,"face":2,"from":6,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":4,"twin":63,"wire_ref":null},{"edge":6,"face":2,"from":4,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":7,"wire_ref":null},{"edge":2,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":2,"twin":2,"wire_ref":null},{"edge":9,"face":3,"from":7,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":8,"twin":23,"wire_ref":null},{"edge":10,"face":3,"from":8,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":1,"twin":39,"wire_ref":null},{"edge":0,"face":3,"from":1,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":0,"twin":0,"wire_ref":null},{"edge":11,"face":3,"from":0,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":7,"twin":18,"wire_ref":null},{"edge":12,"face":4,"from":5,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":9,"twin":70,"wire_ref":null},{"edge":13,"face":4,"from":9,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":20,"wire_ref":null},{"edge":11,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":0,"twin":15,"wire_ref":null},{"edge":5,"face":4,"from":0,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":5,"twin":5,"wire_ref":null},{"edge":13,"face":5,"from":7,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":9,"twin":17,"wire_ref":null},{"edge":14,"face":5,"from":9,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":10,"twin":79,"wire_ref":null},{"edge":15,"face":5,"from":10,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":8,"twin":48,"wire_ref":null},{"edge":9,"face":5,"from":8,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":7,"twin":12,"wire_ref":null},{"edge":16,"face":6,"from":1,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":11,"twin":38,"wire_ref":null},{"edge":17,"face":6,"from":11,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":12,"twin":28,"wire_ref":null},{"edge":18,"face":6,"from":12,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":2,"twin":35,"wire_ref":null},{"edge":1,"face":6,"from":2,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":1,"twin":1,"wire_ref":null},{"edge":17,"face":7,"from":12,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":11,"twin":25,"wire_ref":null},{"edge":19,"face":7,"from":11,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":13,"twin":47,"wire_ref":null},{"edge":20,"face":7,"from":13,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":14,"twin":84,"wire_ref":null},{"edge":21,"face":7,"from":14,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":12,"twin":32,"wire_ref":null},{"edge":21,"face":8,"from":12,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":14,"twin":31,"wire_ref":null},{"edge":22,"face":8,"from":14,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":6,"twin":91,"wire_ref":null},{"edge":7,"face":8,"from":6,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":2,"twin":8,"wire_ref":null},{"edge":18,"face":8,"from":2,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Forward"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":12,"twin":26,"wire_ref":null},{"edge":23,"face":9,"from":8,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":15,"twin":51,"wire_ref":null},{"edge":24,"face":9,"from":15,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":11,"twin":44,"wire_ref":null},{"edge":16,"face":9,"from":11,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":1,"twin":24,"wire_ref":null},{"edge":10,"face":9,"from":1,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":8,"twin":13,"wire_ref":null},{"edge":25,"face":10,"from":16,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":13,"twin":95,"wire_ref":null},{"edge":26,"face":10,"from":13,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":17,"twin":46,"wire_ref":null},{"edge":27,"face":10,"from":17,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":10,"twin":49,"wire_ref":null},{"edge":28,"face":10,"from":10,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":16,"twin":72,"wire_ref":null},{"edge":24,"face":11,"from":11,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":15,"twin":37,"wire_ref":null},{"edge":29,"face":11,"from":15,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":17,"twin":50,"wire_ref":null},{"edge":26,"face":11,"from":17,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":13,"twin":41,"wire_ref":null},{"edge":19,"face":11,"from":13,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":11,"twin":29,"wire_ref":null},{"edge":15,"face":12,"from":8,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":12,"next":49,"prev":51,"to":10,"twin":22,"wire_ref":null},{"edge":27,"face":12,"from":10,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":12,"next":50,"prev":48,"to":17,"twin":42,"wire_ref":null},{"edge":29,"face":12,"from":17,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":12,"next":51,"prev":49,"to":15,"twin":45,"wire_ref":null},{"edge":23,"face":12,"from":15,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":12,"next":48,"prev":50,"to":8,"twin":36,"wire_ref":null},{"edge":30,"face":13,"from":18,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":13,"next":53,"prev":55,"to":19,"twin":61,"wire_ref":null},{"edge":31,"face":13,"from":19,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Forward"},"id":53,"loop_ref":13,"next":54,"prev":52,"to":20,"twin":83,"wire_ref":null},{"edge":32,"face":13,"from":20,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":13,"next":55,"prev":53,"to":21,"twin":64,"wire_ref":null},{"edge":33,"face":13,"from":21,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Reverse"},"id":55,"loop_ref":13,"next":52,"prev":54,"to":18,"twin":56,"wire_ref":null},{"edge":33,"face":14,"from":18,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":14,"next":57,"prev":59,"to":21,"twin":55,"wire_ref":null},{"edge":34,"face":14,"from":21,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Reverse"},"id":57,"loop_ref":14,"next":58,"prev":56,"to":5,"twin":71,"wire_ref":null},{"edge":4,"face":14,"from":5,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":14,"next":59,"prev":57,"to":4,"twin":4,"wire_ref":null},{"edge":35,"face":14,"from":4,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Forward"},"id":59,"loop_ref":14,"next":56,"prev":58,"to":18,"twin":62,"wire_ref":null},{"edge":36,"face":15,"from":6,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Forward"},"id":60,"loop_ref":15,"next":61,"prev":63,"to":19,"twin":90,"wire_ref":null},{"edge":30,"face":15,"from":19,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Reverse"},"id":61,"loop_ref":15,"next":62,"prev":60,"to":18,"twin":52,"wire_ref":null},{"edge":35,"face":15,"from":18,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":15,"next":63,"prev":61,"to":4,"twin":59,"wire_ref":null},{"edge":8,"face":15,"from":4,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Forward"},"id":63,"loop_ref":15,"next":60,"prev":62,"to":6,"twin":9,"wire_ref":null},{"edge":32,"face":16,"from":21,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Forward"},"id":64,"loop_ref":16,"next":65,"prev":67,"to":20,"twin":54,"wire_ref":null},{"edge":37,"face":16,"from":20,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Forward"},"id":65,"loop_ref":16,"next":66,"prev":64,"to":22,"twin":74,"wire_ref":null},{"edge":38,"face":16,"from":22,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Reverse"},"id":66,"loop_ref":16,"next":67,"prev":65,"to":23,"twin":77,"wire_ref":null},{"edge":39,"face":16,"from":23,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Reverse"},"id":67,"loop_ref":16,"next":64,"prev":66,"to":21,"twin":68,"wire_ref":null},{"edge":39,"face":17,"from":21,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":17,"next":69,"prev":71,"to":23,"twin":67,"wire_ref":null},{"edge":40,"face":17,"from":23,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Reverse"},"id":69,"loop_ref":17,"next":70,"prev":68,"to":9,"twin":76,"wire_ref":null},{"edge":12,"face":17,"from":9,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":17,"next":71,"prev":69,"to":5,"twin":16,"wire_ref":null},{"edge":34,"face":17,"from":5,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Forward"},"id":71,"loop_ref":17,"next":68,"prev":70,"to":21,"twin":57,"wire_ref":null},{"edge":28,"face":18,"from":16,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Forward"},"id":72,"loop_ref":18,"next":73,"prev":75,"to":10,"twin":43,"wire_ref":null},{"edge":41,"face":18,"from":10,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Forward"},"id":73,"loop_ref":18,"next":74,"prev":72,"to":22,"twin":78,"wire_ref":null},{"edge":37,"face":18,"from":22,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":18,"next":75,"prev":73,"to":20,"twin":65,"wire_ref":null},{"edge":42,"face":18,"from":20,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Reverse"},"id":75,"loop_ref":18,"next":72,"prev":74,"to":16,"twin":92,"wire_ref":null},{"edge":40,"face":19,"from":9,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Forward"},"id":76,"loop_ref":19,"next":77,"prev":79,"to":23,"twin":69,"wire_ref":null},{"edge":38,"face":19,"from":23,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Forward"},"id":77,"loop_ref":19,"next":78,"prev":76,"to":22,"twin":66,"wire_ref":null},{"edge":41,"face":19,"from":22,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":19,"next":79,"prev":77,"to":10,"twin":73,"wire_ref":null},{"edge":14,"face":19,"from":10,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Reverse"},"id":79,"loop_ref":19,"next":76,"prev":78,"to":9,"twin":21,"wire_ref":null},{"edge":43,"face":20,"from":19,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Forward"},"id":80,"loop_ref":20,"next":81,"prev":83,"to":24,"twin":89,"wire_ref":null},{"edge":44,"face":20,"from":24,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Forward"},"id":81,"loop_ref":20,"next":82,"prev":80,"to":25,"twin":86,"wire_ref":null},{"edge":45,"face":20,"from":25,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":20,"next":83,"prev":81,"to":20,"twin":93,"wire_ref":null},{"edge":31,"face":20,"from":20,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Reverse"},"id":83,"loop_ref":20,"next":80,"prev":82,"to":19,"twin":53,"wire_ref":null},{"edge":20,"face":21,"from":14,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Forward"},"id":84,"loop_ref":21,"next":85,"prev":87,"to":13,"twin":30,"wire_ref":null},{"edge":46,"face":21,"from":13,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Forward"},"id":85,"loop_ref":21,"next":86,"prev":84,"to":25,"twin":94,"wire_ref":null},{"edge":44,"face":21,"from":25,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Reverse"},"id":86,"loop_ref":21,"next":87,"prev":85,"to":24,"twin":81,"wire_ref":null},{"edge":47,"face":21,"from":24,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Reverse"},"id":87,"loop_ref":21,"next":84,"prev":86,"to":14,"twin":88,"wire_ref":null},{"edge":47,"face":22,"from":14,"geometry_use":{"pcurve":88,"periodic_lift":[0,0],"sense":"Forward"},"id":88,"loop_ref":22,"next":89,"prev":91,"to":24,"twin":87,"wire_ref":null},{"edge":43,"face":22,"from":24,"geometry_use":{"pcurve":89,"periodic_lift":[0,0],"sense":"Reverse"},"id":89,"loop_ref":22,"next":90,"prev":88,"to":19,"twin":80,"wire_ref":null},{"edge":36,"face":22,"from":19,"geometry_use":{"pcurve":90,"periodic_lift":[0,0],"sense":"Reverse"},"id":90,"loop_ref":22,"next":91,"prev":89,"to":6,"twin":60,"wire_ref":null},{"edge":22,"face":22,"from":6,"geometry_use":{"pcurve":91,"periodic_lift":[0,0],"sense":"Forward"},"id":91,"loop_ref":22,"next":88,"prev":90,"to":14,"twin":33,"wire_ref":null},{"edge":42,"face":23,"from":16,"geometry_use":{"pcurve":92,"periodic_lift":[0,0],"sense":"Forward"},"id":92,"loop_ref":23,"next":93,"prev":95,"to":20,"twin":75,"wire_ref":null},{"edge":45,"face":23,"from":20,"geometry_use":{"pcurve":93,"periodic_lift":[0,0],"sense":"Forward"},"id":93,"loop_ref":23,"next":94,"prev":92,"to":25,"twin":82,"wire_ref":null},{"edge":46,"face":23,"from":25,"geometry_use":{"pcurve":94,"periodic_lift":[0,0],"sense":"Reverse"},"id":94,"loop_ref":23,"next":95,"prev":93,"to":13,"twin":85,"wire_ref":null},{"edge":25,"face":23,"from":13,"geometry_use":{"pcurve":95,"periodic_lift":[0,0],"sense":"Reverse"},"id":95,"loop_ref":23,"next":92,"prev":94,"to":16,"twin":40,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":48},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":52},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":56},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":60},{"face_ref":16,"id":16,"is_hole":false,"start_halfedge":64},{"face_ref":17,"id":17,"is_hole":false,"start_halfedge":68},{"face_ref":18,"id":18,"is_hole":false,"start_halfedge":72},{"face_ref":19,"id":19,"is_hole":false,"start_halfedge":76},{"face_ref":20,"id":20,"is_hole":false,"start_halfedge":80},{"face_ref":21,"id":21,"is_hole":false,"start_halfedge":84},{"face_ref":22,"id":22,"is_hole":false,"start_halfedge":88},{"face_ref":23,"id":23,"is_hole":false,"start_halfedge":92}],"shells":[{"faces":[0,3,6,2,1,5,9,4,7,8,15,14,19,12,11,17,21,22,13,16,18,10,23,20],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.5,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":1,"position":[0.0,0.5,-0.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":2,"position":[0.0,0.0,-0.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":3,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.5,0.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[1.5,0.5,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":9,"position":[1.5,0.0,-0.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":12,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":8,"outgoing_halfedge":13,"position":[0.0,3.0,-0.5],"tolerance":1e-8},{"id":9,"outgoing_halfedge":17,"position":[1.5,3.0,0.0],"tolerance":1e-8},{"id":10,"outgoing_halfedge":22,"position":[1.5,3.0,-0.5],"tolerance":1e-8},{"id":11,"outgoing_halfedge":25,"position":[0.0,0.5,-3.0],"tolerance":1e-8},{"id":12,"outgoing_halfedge":26,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":13,"outgoing_halfedge":30,"position":[1.5,0.5,-3.0],"tolerance":1e-8},{"id":14,"outgoing_halfedge":31,"position":[1.5,0.0,-3.0],"tolerance":1e-8},{"id":15,"outgoing_halfedge":37,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":16,"outgoing_halfedge":40,"position":[1.5,0.5,-0.5],"tolerance":1e-8},{"id":17,"outgoing_halfedge":42,"position":[1.5,3.0,-3.0],"tolerance":1e-8},{"id":18,"outgoing_halfedge":52,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":19,"outgoing_halfedge":53,"position":[3.0,0.0,-0.5],"tolerance":1e-8},{"id":20,"outgoing_halfedge":54,"position":[3.0,0.5,-0.5],"tolerance":1e-8},{"id":21,"outgoing_halfedge":55,"position":[3.0,0.5,0.0],"tolerance":1e-8},{"id":22,"outgoing_halfedge":66,"position":[3.0,3.0,-0.5],"tolerance":1e-8},{"id":23,"outgoing_halfedge":67,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":24,"outgoing_halfedge":81,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":25,"outgoing_halfedge":82,"position":[3.0,0.5,-3.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-box-cut.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-box-cut.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-box-cut.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-box-intersection.json b/main/opengeometry/tests/fixtures/cases/validity/builder-box-intersection.json new file mode 100644 index 0000000..1ec49f8 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-box-intersection.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,3.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,3.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.5,-3.0]}],"intersections":[],"pcurves":[{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.5]}],"surfaces":[{"frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"box-intersection","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":6,"id":6,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":7,"id":7,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":11,"id":9,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-8,"twin_halfedge":16}],"faces":[{"id":0,"key":"left-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.50000001,"lo":-1e-8},{"hi":2.50000001,"lo":-1e-8}]}},{"id":1,"key":"right-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":3.00000001,"lo":0.49999999}]}},{"id":2,"key":"front-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":2.50000001,"lo":-1e-8}]}},{"id":3,"key":"back-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":3.00000001,"lo":1.49999999}]}},{"id":4,"key":"bottom-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.50000001,"lo":-1e-8},{"hi":1.50000001,"lo":-1e-8}]}},{"id":5,"key":"top-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":1.49999999},{"hi":3.00000001,"lo":0.49999999}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":23,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":12,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":19,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":10,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":17,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":6,"twin":14,"wire_ref":null},{"edge":6,"face":1,"from":6,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":7,"twin":21,"wire_ref":null},{"edge":7,"face":1,"from":7,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":8,"wire_ref":null},{"edge":7,"face":2,"from":4,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":7,"twin":7,"wire_ref":null},{"edge":8,"face":2,"from":7,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":0,"twin":20,"wire_ref":null},{"edge":3,"face":2,"from":0,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":3,"wire_ref":null},{"edge":9,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":4,"twin":18,"wire_ref":null},{"edge":1,"face":3,"from":2,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":1,"twin":1,"wire_ref":null},{"edge":10,"face":3,"from":1,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":22,"wire_ref":null},{"edge":5,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":11,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":2,"twin":16,"wire_ref":null},{"edge":11,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":5,"twin":15,"wire_ref":null},{"edge":4,"face":4,"from":5,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":4,"twin":4,"wire_ref":null},{"edge":9,"face":4,"from":4,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":3,"twin":11,"wire_ref":null},{"edge":2,"face":4,"from":3,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":2,"wire_ref":null},{"edge":8,"face":5,"from":0,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":7,"twin":9,"wire_ref":null},{"edge":6,"face":5,"from":7,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":6,"twin":6,"wire_ref":null},{"edge":10,"face":5,"from":6,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":1,"twin":13,"wire_ref":null},{"edge":0,"face":5,"from":1,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":0,"twin":0,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,5,3,4,2,1],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.5,3.0,-0.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":1,"position":[1.5,3.0,-3.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":2,"position":[1.5,0.5,-3.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":3,"position":[1.5,0.5,-0.5],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[3.0,0.5,-0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[3.0,0.5,-3.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":6,"position":[3.0,3.0,-3.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":7,"position":[3.0,3.0,-0.5],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-box-intersection.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-box-intersection.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-box-intersection.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-box-union.json b/main/opengeometry/tests/fixtures/cases/validity/builder-box-union.json new file mode 100644 index 0000000..cb87997 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-box-union.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.5,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.5,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,3.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,3.0,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.5,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,3.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,3.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,3.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,3.5,-0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,3.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,3.0,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,0.5,-3.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,0.5,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,3.0,-3.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,0.5,-3.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,0.5,-3.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,0.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.5,3.5,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.5,3.0,-3.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[1.5,3.5,-3.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[3.0,3.0,-3.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[3.0,3.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.5,0.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.5,0.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.5,3.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.5,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,3.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.5,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.5,3.0,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.5,3.5,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.5,3.0,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,3.5,-0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,3.5,-3.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.5,0.5,-3.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.5,0.5,-3.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.5,3.0,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,3.0,-3.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,0.5,-3.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[4.5,3.0,-3.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[4.5,3.5,-3.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[3.0,3.5,-3.5]}],"intersections":[],"pcurves":[{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,1.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[2.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.5,1.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,2.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,2.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[1.5,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.5,2.5]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[3.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-3.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,3.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-3.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-3.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,3.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,3.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-3.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[4.5,0.5,-0.5],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.5,0.5,-3.5],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[1.5,3.5,-0.5],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"box-union","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":27},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":2,"id":2,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":3,"id":3,"tolerance":1e-8,"twin_halfedge":6},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":4,"id":4,"tolerance":1e-8,"twin_halfedge":54},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":7,"id":6,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":8,"id":7,"tolerance":1e-8,"twin_halfedge":34},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":9,"id":8,"tolerance":1e-8,"twin_halfedge":59},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":12,"id":9,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":13,"id":10,"tolerance":1e-8,"twin_halfedge":39},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":15,"id":11,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":12,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":16,"id":12,"tolerance":1e-8,"twin_halfedge":66},{"chart_seam":false,"geometry":{"curve":13,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":17,"id":13,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":14,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":21,"id":14,"tolerance":1e-8,"twin_halfedge":71},{"chart_seam":false,"geometry":{"curve":15,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":22,"id":15,"tolerance":1e-8,"twin_halfedge":44},{"chart_seam":false,"geometry":{"curve":16,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":24,"id":16,"tolerance":1e-8,"twin_halfedge":38},{"chart_seam":false,"geometry":{"curve":17,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":25,"id":17,"tolerance":1e-8,"twin_halfedge":28},{"chart_seam":false,"geometry":{"curve":18,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":26,"id":18,"tolerance":1e-8,"twin_halfedge":35},{"chart_seam":false,"geometry":{"curve":19,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":29,"id":19,"tolerance":1e-8,"twin_halfedge":43},{"chart_seam":false,"geometry":{"curve":20,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":30,"id":20,"tolerance":1e-8,"twin_halfedge":76},{"chart_seam":false,"geometry":{"curve":21,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":31,"id":21,"tolerance":1e-8,"twin_halfedge":32},{"chart_seam":false,"geometry":{"curve":22,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":33,"id":22,"tolerance":1e-8,"twin_halfedge":83},{"chart_seam":false,"geometry":{"curve":23,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":36,"id":23,"tolerance":1e-8,"twin_halfedge":47},{"chart_seam":false,"geometry":{"curve":24,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":37,"id":24,"tolerance":1e-8,"twin_halfedge":40},{"chart_seam":false,"geometry":{"curve":25,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":41,"id":25,"tolerance":1e-8,"twin_halfedge":46},{"chart_seam":false,"geometry":{"curve":26,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":42,"id":26,"tolerance":1e-8,"twin_halfedge":99},{"chart_seam":false,"geometry":{"curve":27,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":45,"id":27,"tolerance":1e-8,"twin_halfedge":86},{"chart_seam":false,"geometry":{"curve":28,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":48,"id":28,"tolerance":1e-8,"twin_halfedge":57},{"chart_seam":false,"geometry":{"curve":29,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":49,"id":29,"tolerance":1e-8,"twin_halfedge":75},{"chart_seam":false,"geometry":{"curve":30,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":50,"id":30,"tolerance":1e-8,"twin_halfedge":60},{"chart_seam":false,"geometry":{"curve":31,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":51,"id":31,"tolerance":1e-8,"twin_halfedge":52},{"chart_seam":false,"geometry":{"curve":32,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":53,"id":32,"tolerance":1e-8,"twin_halfedge":67},{"chart_seam":false,"geometry":{"curve":33,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":55,"id":33,"tolerance":1e-8,"twin_halfedge":58},{"chart_seam":false,"geometry":{"curve":34,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":56,"id":34,"tolerance":1e-8,"twin_halfedge":82},{"chart_seam":false,"geometry":{"curve":35,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":61,"id":35,"tolerance":1e-8,"twin_halfedge":126},{"chart_seam":false,"geometry":{"curve":36,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":62,"id":36,"tolerance":1e-8,"twin_halfedge":69},{"chart_seam":false,"geometry":{"curve":37,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":63,"id":37,"tolerance":1e-8,"twin_halfedge":64},{"chart_seam":false,"geometry":{"curve":38,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":65,"id":38,"tolerance":1e-8,"twin_halfedge":68},{"chart_seam":false,"geometry":{"curve":39,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":70,"id":39,"tolerance":1e-8,"twin_halfedge":91},{"chart_seam":false,"geometry":{"curve":40,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":72,"id":40,"tolerance":1e-8,"twin_halfedge":81},{"chart_seam":false,"geometry":{"curve":41,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":73,"id":41,"tolerance":1e-8,"twin_halfedge":78},{"chart_seam":false,"geometry":{"curve":42,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":74,"id":42,"tolerance":1e-8,"twin_halfedge":131},{"chart_seam":false,"geometry":{"curve":43,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":77,"id":43,"tolerance":1e-8,"twin_halfedge":106},{"chart_seam":false,"geometry":{"curve":44,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":79,"id":44,"tolerance":1e-8,"twin_halfedge":80},{"chart_seam":false,"geometry":{"curve":45,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":84,"id":45,"tolerance":1e-8,"twin_halfedge":95},{"chart_seam":false,"geometry":{"curve":46,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":85,"id":46,"tolerance":1e-8,"twin_halfedge":111},{"chart_seam":false,"geometry":{"curve":47,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":87,"id":47,"tolerance":1e-8,"twin_halfedge":90},{"chart_seam":false,"geometry":{"curve":48,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":88,"id":48,"tolerance":1e-8,"twin_halfedge":138},{"chart_seam":false,"geometry":{"curve":49,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":89,"id":49,"tolerance":1e-8,"twin_halfedge":92},{"chart_seam":false,"geometry":{"curve":50,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":93,"id":50,"tolerance":1e-8,"twin_halfedge":143},{"chart_seam":false,"geometry":{"curve":51,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":94,"id":51,"tolerance":1e-8,"twin_halfedge":116},{"chart_seam":false,"geometry":{"curve":52,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":96,"id":52,"tolerance":1e-8,"twin_halfedge":110},{"chart_seam":false,"geometry":{"curve":53,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":97,"id":53,"tolerance":1e-8,"twin_halfedge":100},{"chart_seam":false,"geometry":{"curve":54,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":98,"id":54,"tolerance":1e-8,"twin_halfedge":107},{"chart_seam":false,"geometry":{"curve":55,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":101,"id":55,"tolerance":1e-8,"twin_halfedge":115},{"chart_seam":false,"geometry":{"curve":56,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":102,"id":56,"tolerance":1e-8,"twin_halfedge":148},{"chart_seam":false,"geometry":{"curve":57,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":103,"id":57,"tolerance":1e-8,"twin_halfedge":104},{"chart_seam":false,"geometry":{"curve":58,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":105,"id":58,"tolerance":1e-8,"twin_halfedge":155},{"chart_seam":false,"geometry":{"curve":59,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":108,"id":59,"tolerance":1e-8,"twin_halfedge":119},{"chart_seam":false,"geometry":{"curve":60,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":109,"id":60,"tolerance":1e-8,"twin_halfedge":112},{"chart_seam":false,"geometry":{"curve":61,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":113,"id":61,"tolerance":1e-8,"twin_halfedge":118},{"chart_seam":false,"geometry":{"curve":62,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":114,"id":62,"tolerance":1e-8,"twin_halfedge":160},{"chart_seam":false,"geometry":{"curve":63,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":117,"id":63,"tolerance":1e-8,"twin_halfedge":167},{"chart_seam":false,"geometry":{"curve":64,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":120,"id":64,"tolerance":1e-8,"twin_halfedge":129},{"chart_seam":false,"geometry":{"curve":65,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":121,"id":65,"tolerance":1e-8,"twin_halfedge":147},{"chart_seam":false,"geometry":{"curve":66,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":122,"id":66,"tolerance":1e-8,"twin_halfedge":132},{"chart_seam":false,"geometry":{"curve":67,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":123,"id":67,"tolerance":1e-8,"twin_halfedge":124},{"chart_seam":false,"geometry":{"curve":68,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":125,"id":68,"tolerance":1e-8,"twin_halfedge":139},{"chart_seam":false,"geometry":{"curve":69,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":127,"id":69,"tolerance":1e-8,"twin_halfedge":130},{"chart_seam":false,"geometry":{"curve":70,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":128,"id":70,"tolerance":1e-8,"twin_halfedge":154},{"chart_seam":false,"geometry":{"curve":71,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":133,"id":71,"tolerance":1e-8,"twin_halfedge":159},{"chart_seam":false,"geometry":{"curve":72,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":134,"id":72,"tolerance":1e-8,"twin_halfedge":141},{"chart_seam":false,"geometry":{"curve":73,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":135,"id":73,"tolerance":1e-8,"twin_halfedge":136},{"chart_seam":false,"geometry":{"curve":74,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":137,"id":74,"tolerance":1e-8,"twin_halfedge":140},{"chart_seam":false,"geometry":{"curve":75,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":142,"id":75,"tolerance":1e-8,"twin_halfedge":164},{"chart_seam":false,"geometry":{"curve":76,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":144,"id":76,"tolerance":1e-8,"twin_halfedge":153},{"chart_seam":false,"geometry":{"curve":77,"kind":"Curve","range":{"hi":2.5,"lo":0.0}},"halfedge":145,"id":77,"tolerance":1e-8,"twin_halfedge":150},{"chart_seam":false,"geometry":{"curve":78,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":146,"id":78,"tolerance":1e-8,"twin_halfedge":156},{"chart_seam":false,"geometry":{"curve":79,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":149,"id":79,"tolerance":1e-8,"twin_halfedge":163},{"chart_seam":false,"geometry":{"curve":80,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":151,"id":80,"tolerance":1e-8,"twin_halfedge":152},{"chart_seam":false,"geometry":{"curve":81,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":157,"id":81,"tolerance":1e-8,"twin_halfedge":162},{"chart_seam":false,"geometry":{"curve":82,"kind":"Curve","range":{"hi":0.5,"lo":0.0}},"halfedge":158,"id":82,"tolerance":1e-8,"twin_halfedge":165},{"chart_seam":false,"geometry":{"curve":83,"kind":"Curve","range":{"hi":1.5,"lo":0.0}},"halfedge":161,"id":83,"tolerance":1e-8,"twin_halfedge":166}],"faces":[{"id":0,"key":"left-0","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":1,"key":"front-1","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":2,"key":"bottom-2","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":1.50000001,"lo":-1e-8}]}},{"id":3,"key":"left-3","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":4,"key":"front-4","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.49999999}]}},{"id":5,"key":"top-5","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":6,"key":"left-6","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":6,"trim":{"chart":0,"holes":[],"outer":6,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.49999999}]}},{"id":7,"key":"back-7","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":7,"trim":{"chart":0,"holes":[],"outer":7,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":1.50000001,"lo":-1e-8}]}},{"id":8,"key":"bottom-8","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":8,"trim":{"chart":0,"holes":[],"outer":8,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":1.50000001,"lo":-1e-8}]}},{"id":9,"key":"left-9","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":9,"trim":{"chart":0,"holes":[],"outer":9,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":3.00000001,"lo":0.49999999}]}},{"id":10,"key":"back-10","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":10,"trim":{"chart":0,"holes":[],"outer":10,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":1.50000001,"lo":-1e-8}]}},{"id":11,"key":"top-11","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":11,"trim":{"chart":0,"holes":[],"outer":11,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.49999999}]}},{"id":12,"key":"right-12","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":12,"trim":{"chart":0,"holes":[],"outer":12,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":0.50000001,"lo":-1e-8}]}},{"id":13,"key":"front-13","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":13,"trim":{"chart":0,"holes":[],"outer":13,"uv_bounds":[{"hi":3.00000001,"lo":1.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":14,"key":"bottom-14","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":14,"trim":{"chart":0,"holes":[],"outer":14,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.49999999}]}},{"id":15,"key":"right-15","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":15,"trim":{"chart":0,"holes":[],"outer":15,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":0.49999999}]}},{"id":16,"key":"front-16","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":16,"trim":{"chart":0,"holes":[],"outer":16,"uv_bounds":[{"hi":3.00000001,"lo":1.49999999},{"hi":3.00000001,"lo":0.49999999}]}},{"id":17,"key":"top-17","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":17,"trim":{"chart":0,"holes":[],"outer":17,"uv_bounds":[{"hi":3.00000001,"lo":1.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":18,"key":"right-18","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":18,"trim":{"chart":0,"holes":[],"outer":18,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":0.50000001,"lo":-1e-8}]}},{"id":19,"key":"back-19","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":19,"trim":{"chart":0,"holes":[],"outer":19,"uv_bounds":[{"hi":0.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.49999999}]}},{"id":20,"key":"bottom-20","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-host","entity":"box-host","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":20,"trim":{"chart":0,"holes":[],"outer":20,"uv_bounds":[{"hi":3.00000001,"lo":0.49999999},{"hi":3.00000001,"lo":1.49999999}]}},{"id":21,"key":"left-21","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":21,"trim":{"chart":0,"holes":[],"outer":21,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":2.50000001,"lo":-1e-8}]}},{"id":22,"key":"front-22","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":22,"trim":{"chart":0,"holes":[],"outer":22,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":23,"key":"top-23","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":23,"trim":{"chart":0,"holes":[],"outer":23,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":2.50000001,"lo":-1e-8}]}},{"id":24,"key":"left-24","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":24,"trim":{"chart":0,"holes":[],"outer":24,"uv_bounds":[{"hi":2.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":25,"key":"back-25","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":25,"trim":{"chart":0,"holes":[],"outer":25,"uv_bounds":[{"hi":2.50000001,"lo":-1e-8},{"hi":1.50000001,"lo":-1e-8}]}},{"id":26,"key":"bottom-26","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":26,"trim":{"chart":0,"holes":[],"outer":26,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":1.50000001,"lo":-1e-8}]}},{"id":27,"key":"left-27","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":27,"trim":{"chart":0,"holes":[],"outer":27,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":28,"key":"back-28","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":28,"trim":{"chart":0,"holes":[],"outer":28,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":1.50000001,"lo":-1e-8}]}},{"id":29,"key":"top-29","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":29,"trim":{"chart":0,"holes":[],"outer":29,"uv_bounds":[{"hi":1.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":30,"key":"right-30","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":30,"trim":{"chart":0,"holes":[],"outer":30,"uv_bounds":[{"hi":2.50000001,"lo":-1e-8},{"hi":2.50000001,"lo":-1e-8}]}},{"id":31,"key":"front-31","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":31,"trim":{"chart":0,"holes":[],"outer":31,"uv_bounds":[{"hi":3.00000001,"lo":1.49999999},{"hi":2.50000001,"lo":-1e-8}]}},{"id":32,"key":"bottom-32","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":32,"trim":{"chart":0,"holes":[],"outer":32,"uv_bounds":[{"hi":2.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.49999999}]}},{"id":33,"key":"right-33","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":33,"trim":{"chart":0,"holes":[],"outer":33,"uv_bounds":[{"hi":2.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":2.49999999}]}},{"id":34,"key":"front-34","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":34,"trim":{"chart":0,"holes":[],"outer":34,"uv_bounds":[{"hi":3.00000001,"lo":1.49999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":35,"key":"top-35","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":35,"trim":{"chart":0,"holes":[],"outer":35,"uv_bounds":[{"hi":3.00000001,"lo":1.49999999},{"hi":2.50000001,"lo":-1e-8}]}},{"id":36,"key":"right-36","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":36,"trim":{"chart":0,"holes":[],"outer":36,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":2.50000001,"lo":-1e-8}]}},{"id":37,"key":"back-37","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":37,"trim":{"chart":0,"holes":[],"outer":37,"uv_bounds":[{"hi":2.50000001,"lo":-1e-8},{"hi":3.00000001,"lo":1.49999999}]}},{"id":38,"key":"bottom-38","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":38,"trim":{"chart":0,"holes":[],"outer":38,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":1.49999999}]}},{"id":39,"key":"right-39","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":39,"trim":{"chart":0,"holes":[],"outer":39,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":2.49999999}]}},{"id":40,"key":"back-40","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":40,"trim":{"chart":0,"holes":[],"outer":40,"uv_bounds":[{"hi":3.00000001,"lo":2.49999999},{"hi":3.00000001,"lo":1.49999999}]}},{"id":41,"key":"top-41","provenance":{"reversed":false,"role":"Split","sources":[{"body":"box-cutter","entity":"box-cutter","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":41,"trim":{"chart":0,"holes":[],"outer":41,"uv_bounds":[{"hi":3.00000001,"lo":1.49999999},{"hi":3.00000001,"lo":2.49999999}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":1,"twin":14,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Reverse"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":2,"twin":27,"wire_ref":null},{"edge":2,"face":0,"from":2,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":3,"twin":11,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":6,"wire_ref":null},{"edge":4,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":54,"wire_ref":null},{"edge":5,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":0,"twin":19,"wire_ref":null},{"edge":3,"face":1,"from":0,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":3,"twin":3,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":10,"wire_ref":null},{"edge":7,"face":2,"from":2,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":6,"twin":34,"wire_ref":null},{"edge":8,"face":2,"from":6,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":4,"twin":59,"wire_ref":null},{"edge":6,"face":2,"from":4,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":3,"twin":7,"wire_ref":null},{"edge":2,"face":2,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":2,"twin":2,"wire_ref":null},{"edge":9,"face":3,"from":7,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":8,"twin":23,"wire_ref":null},{"edge":10,"face":3,"from":8,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Reverse"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":1,"twin":39,"wire_ref":null},{"edge":0,"face":3,"from":1,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":0,"twin":0,"wire_ref":null},{"edge":11,"face":3,"from":0,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Forward"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":7,"twin":18,"wire_ref":null},{"edge":12,"face":4,"from":5,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":9,"twin":66,"wire_ref":null},{"edge":13,"face":4,"from":9,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Reverse"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":20,"wire_ref":null},{"edge":11,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":0,"twin":15,"wire_ref":null},{"edge":5,"face":4,"from":0,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Forward"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":5,"twin":5,"wire_ref":null},{"edge":13,"face":5,"from":7,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":9,"twin":17,"wire_ref":null},{"edge":14,"face":5,"from":9,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":10,"twin":71,"wire_ref":null},{"edge":15,"face":5,"from":10,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":8,"twin":44,"wire_ref":null},{"edge":9,"face":5,"from":8,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":7,"twin":12,"wire_ref":null},{"edge":16,"face":6,"from":1,"geometry_use":{"pcurve":24,"periodic_lift":[0,0],"sense":"Forward"},"id":24,"loop_ref":6,"next":25,"prev":27,"to":11,"twin":38,"wire_ref":null},{"edge":17,"face":6,"from":11,"geometry_use":{"pcurve":25,"periodic_lift":[0,0],"sense":"Reverse"},"id":25,"loop_ref":6,"next":26,"prev":24,"to":12,"twin":28,"wire_ref":null},{"edge":18,"face":6,"from":12,"geometry_use":{"pcurve":26,"periodic_lift":[0,0],"sense":"Reverse"},"id":26,"loop_ref":6,"next":27,"prev":25,"to":2,"twin":35,"wire_ref":null},{"edge":1,"face":6,"from":2,"geometry_use":{"pcurve":27,"periodic_lift":[0,0],"sense":"Forward"},"id":27,"loop_ref":6,"next":24,"prev":26,"to":1,"twin":1,"wire_ref":null},{"edge":17,"face":7,"from":12,"geometry_use":{"pcurve":28,"periodic_lift":[0,0],"sense":"Forward"},"id":28,"loop_ref":7,"next":29,"prev":31,"to":11,"twin":25,"wire_ref":null},{"edge":19,"face":7,"from":11,"geometry_use":{"pcurve":29,"periodic_lift":[0,0],"sense":"Forward"},"id":29,"loop_ref":7,"next":30,"prev":28,"to":13,"twin":43,"wire_ref":null},{"edge":20,"face":7,"from":13,"geometry_use":{"pcurve":30,"periodic_lift":[0,0],"sense":"Reverse"},"id":30,"loop_ref":7,"next":31,"prev":29,"to":14,"twin":76,"wire_ref":null},{"edge":21,"face":7,"from":14,"geometry_use":{"pcurve":31,"periodic_lift":[0,0],"sense":"Reverse"},"id":31,"loop_ref":7,"next":28,"prev":30,"to":12,"twin":32,"wire_ref":null},{"edge":21,"face":8,"from":12,"geometry_use":{"pcurve":32,"periodic_lift":[0,0],"sense":"Forward"},"id":32,"loop_ref":8,"next":33,"prev":35,"to":14,"twin":31,"wire_ref":null},{"edge":22,"face":8,"from":14,"geometry_use":{"pcurve":33,"periodic_lift":[0,0],"sense":"Reverse"},"id":33,"loop_ref":8,"next":34,"prev":32,"to":6,"twin":83,"wire_ref":null},{"edge":7,"face":8,"from":6,"geometry_use":{"pcurve":34,"periodic_lift":[0,0],"sense":"Reverse"},"id":34,"loop_ref":8,"next":35,"prev":33,"to":2,"twin":8,"wire_ref":null},{"edge":18,"face":8,"from":2,"geometry_use":{"pcurve":35,"periodic_lift":[0,0],"sense":"Forward"},"id":35,"loop_ref":8,"next":32,"prev":34,"to":12,"twin":26,"wire_ref":null},{"edge":23,"face":9,"from":8,"geometry_use":{"pcurve":36,"periodic_lift":[0,0],"sense":"Forward"},"id":36,"loop_ref":9,"next":37,"prev":39,"to":15,"twin":47,"wire_ref":null},{"edge":24,"face":9,"from":15,"geometry_use":{"pcurve":37,"periodic_lift":[0,0],"sense":"Reverse"},"id":37,"loop_ref":9,"next":38,"prev":36,"to":11,"twin":40,"wire_ref":null},{"edge":16,"face":9,"from":11,"geometry_use":{"pcurve":38,"periodic_lift":[0,0],"sense":"Reverse"},"id":38,"loop_ref":9,"next":39,"prev":37,"to":1,"twin":24,"wire_ref":null},{"edge":10,"face":9,"from":1,"geometry_use":{"pcurve":39,"periodic_lift":[0,0],"sense":"Forward"},"id":39,"loop_ref":9,"next":36,"prev":38,"to":8,"twin":13,"wire_ref":null},{"edge":24,"face":10,"from":11,"geometry_use":{"pcurve":40,"periodic_lift":[0,0],"sense":"Forward"},"id":40,"loop_ref":10,"next":41,"prev":43,"to":15,"twin":37,"wire_ref":null},{"edge":25,"face":10,"from":15,"geometry_use":{"pcurve":41,"periodic_lift":[0,0],"sense":"Forward"},"id":41,"loop_ref":10,"next":42,"prev":40,"to":16,"twin":46,"wire_ref":null},{"edge":26,"face":10,"from":16,"geometry_use":{"pcurve":42,"periodic_lift":[0,0],"sense":"Reverse"},"id":42,"loop_ref":10,"next":43,"prev":41,"to":13,"twin":99,"wire_ref":null},{"edge":19,"face":10,"from":13,"geometry_use":{"pcurve":43,"periodic_lift":[0,0],"sense":"Reverse"},"id":43,"loop_ref":10,"next":40,"prev":42,"to":11,"twin":29,"wire_ref":null},{"edge":15,"face":11,"from":8,"geometry_use":{"pcurve":44,"periodic_lift":[0,0],"sense":"Forward"},"id":44,"loop_ref":11,"next":45,"prev":47,"to":10,"twin":22,"wire_ref":null},{"edge":27,"face":11,"from":10,"geometry_use":{"pcurve":45,"periodic_lift":[0,0],"sense":"Forward"},"id":45,"loop_ref":11,"next":46,"prev":44,"to":16,"twin":86,"wire_ref":null},{"edge":25,"face":11,"from":16,"geometry_use":{"pcurve":46,"periodic_lift":[0,0],"sense":"Reverse"},"id":46,"loop_ref":11,"next":47,"prev":45,"to":15,"twin":41,"wire_ref":null},{"edge":23,"face":11,"from":15,"geometry_use":{"pcurve":47,"periodic_lift":[0,0],"sense":"Reverse"},"id":47,"loop_ref":11,"next":44,"prev":46,"to":8,"twin":36,"wire_ref":null},{"edge":28,"face":12,"from":17,"geometry_use":{"pcurve":48,"periodic_lift":[0,0],"sense":"Forward"},"id":48,"loop_ref":12,"next":49,"prev":51,"to":18,"twin":57,"wire_ref":null},{"edge":29,"face":12,"from":18,"geometry_use":{"pcurve":49,"periodic_lift":[0,0],"sense":"Forward"},"id":49,"loop_ref":12,"next":50,"prev":48,"to":19,"twin":75,"wire_ref":null},{"edge":30,"face":12,"from":19,"geometry_use":{"pcurve":50,"periodic_lift":[0,0],"sense":"Reverse"},"id":50,"loop_ref":12,"next":51,"prev":49,"to":20,"twin":60,"wire_ref":null},{"edge":31,"face":12,"from":20,"geometry_use":{"pcurve":51,"periodic_lift":[0,0],"sense":"Reverse"},"id":51,"loop_ref":12,"next":48,"prev":50,"to":17,"twin":52,"wire_ref":null},{"edge":31,"face":13,"from":17,"geometry_use":{"pcurve":52,"periodic_lift":[0,0],"sense":"Forward"},"id":52,"loop_ref":13,"next":53,"prev":55,"to":20,"twin":51,"wire_ref":null},{"edge":32,"face":13,"from":20,"geometry_use":{"pcurve":53,"periodic_lift":[0,0],"sense":"Reverse"},"id":53,"loop_ref":13,"next":54,"prev":52,"to":5,"twin":67,"wire_ref":null},{"edge":4,"face":13,"from":5,"geometry_use":{"pcurve":54,"periodic_lift":[0,0],"sense":"Reverse"},"id":54,"loop_ref":13,"next":55,"prev":53,"to":4,"twin":4,"wire_ref":null},{"edge":33,"face":13,"from":4,"geometry_use":{"pcurve":55,"periodic_lift":[0,0],"sense":"Forward"},"id":55,"loop_ref":13,"next":52,"prev":54,"to":17,"twin":58,"wire_ref":null},{"edge":34,"face":14,"from":6,"geometry_use":{"pcurve":56,"periodic_lift":[0,0],"sense":"Forward"},"id":56,"loop_ref":14,"next":57,"prev":59,"to":18,"twin":82,"wire_ref":null},{"edge":28,"face":14,"from":18,"geometry_use":{"pcurve":57,"periodic_lift":[0,0],"sense":"Reverse"},"id":57,"loop_ref":14,"next":58,"prev":56,"to":17,"twin":48,"wire_ref":null},{"edge":33,"face":14,"from":17,"geometry_use":{"pcurve":58,"periodic_lift":[0,0],"sense":"Reverse"},"id":58,"loop_ref":14,"next":59,"prev":57,"to":4,"twin":55,"wire_ref":null},{"edge":8,"face":14,"from":4,"geometry_use":{"pcurve":59,"periodic_lift":[0,0],"sense":"Forward"},"id":59,"loop_ref":14,"next":56,"prev":58,"to":6,"twin":9,"wire_ref":null},{"edge":30,"face":15,"from":20,"geometry_use":{"pcurve":60,"periodic_lift":[0,0],"sense":"Forward"},"id":60,"loop_ref":15,"next":61,"prev":63,"to":19,"twin":50,"wire_ref":null},{"edge":35,"face":15,"from":19,"geometry_use":{"pcurve":61,"periodic_lift":[0,0],"sense":"Forward"},"id":61,"loop_ref":15,"next":62,"prev":60,"to":21,"twin":126,"wire_ref":null},{"edge":36,"face":15,"from":21,"geometry_use":{"pcurve":62,"periodic_lift":[0,0],"sense":"Reverse"},"id":62,"loop_ref":15,"next":63,"prev":61,"to":22,"twin":69,"wire_ref":null},{"edge":37,"face":15,"from":22,"geometry_use":{"pcurve":63,"periodic_lift":[0,0],"sense":"Reverse"},"id":63,"loop_ref":15,"next":60,"prev":62,"to":20,"twin":64,"wire_ref":null},{"edge":37,"face":16,"from":20,"geometry_use":{"pcurve":64,"periodic_lift":[0,0],"sense":"Forward"},"id":64,"loop_ref":16,"next":65,"prev":67,"to":22,"twin":63,"wire_ref":null},{"edge":38,"face":16,"from":22,"geometry_use":{"pcurve":65,"periodic_lift":[0,0],"sense":"Reverse"},"id":65,"loop_ref":16,"next":66,"prev":64,"to":9,"twin":68,"wire_ref":null},{"edge":12,"face":16,"from":9,"geometry_use":{"pcurve":66,"periodic_lift":[0,0],"sense":"Reverse"},"id":66,"loop_ref":16,"next":67,"prev":65,"to":5,"twin":16,"wire_ref":null},{"edge":32,"face":16,"from":5,"geometry_use":{"pcurve":67,"periodic_lift":[0,0],"sense":"Forward"},"id":67,"loop_ref":16,"next":64,"prev":66,"to":20,"twin":53,"wire_ref":null},{"edge":38,"face":17,"from":9,"geometry_use":{"pcurve":68,"periodic_lift":[0,0],"sense":"Forward"},"id":68,"loop_ref":17,"next":69,"prev":71,"to":22,"twin":65,"wire_ref":null},{"edge":36,"face":17,"from":22,"geometry_use":{"pcurve":69,"periodic_lift":[0,0],"sense":"Forward"},"id":69,"loop_ref":17,"next":70,"prev":68,"to":21,"twin":62,"wire_ref":null},{"edge":39,"face":17,"from":21,"geometry_use":{"pcurve":70,"periodic_lift":[0,0],"sense":"Reverse"},"id":70,"loop_ref":17,"next":71,"prev":69,"to":10,"twin":91,"wire_ref":null},{"edge":14,"face":17,"from":10,"geometry_use":{"pcurve":71,"periodic_lift":[0,0],"sense":"Reverse"},"id":71,"loop_ref":17,"next":68,"prev":70,"to":9,"twin":21,"wire_ref":null},{"edge":40,"face":18,"from":18,"geometry_use":{"pcurve":72,"periodic_lift":[0,0],"sense":"Forward"},"id":72,"loop_ref":18,"next":73,"prev":75,"to":23,"twin":81,"wire_ref":null},{"edge":41,"face":18,"from":23,"geometry_use":{"pcurve":73,"periodic_lift":[0,0],"sense":"Forward"},"id":73,"loop_ref":18,"next":74,"prev":72,"to":24,"twin":78,"wire_ref":null},{"edge":42,"face":18,"from":24,"geometry_use":{"pcurve":74,"periodic_lift":[0,0],"sense":"Reverse"},"id":74,"loop_ref":18,"next":75,"prev":73,"to":19,"twin":131,"wire_ref":null},{"edge":29,"face":18,"from":19,"geometry_use":{"pcurve":75,"periodic_lift":[0,0],"sense":"Reverse"},"id":75,"loop_ref":18,"next":72,"prev":74,"to":18,"twin":49,"wire_ref":null},{"edge":20,"face":19,"from":14,"geometry_use":{"pcurve":76,"periodic_lift":[0,0],"sense":"Forward"},"id":76,"loop_ref":19,"next":77,"prev":79,"to":13,"twin":30,"wire_ref":null},{"edge":43,"face":19,"from":13,"geometry_use":{"pcurve":77,"periodic_lift":[0,0],"sense":"Forward"},"id":77,"loop_ref":19,"next":78,"prev":76,"to":24,"twin":106,"wire_ref":null},{"edge":41,"face":19,"from":24,"geometry_use":{"pcurve":78,"periodic_lift":[0,0],"sense":"Reverse"},"id":78,"loop_ref":19,"next":79,"prev":77,"to":23,"twin":73,"wire_ref":null},{"edge":44,"face":19,"from":23,"geometry_use":{"pcurve":79,"periodic_lift":[0,0],"sense":"Reverse"},"id":79,"loop_ref":19,"next":76,"prev":78,"to":14,"twin":80,"wire_ref":null},{"edge":44,"face":20,"from":14,"geometry_use":{"pcurve":80,"periodic_lift":[0,0],"sense":"Forward"},"id":80,"loop_ref":20,"next":81,"prev":83,"to":23,"twin":79,"wire_ref":null},{"edge":40,"face":20,"from":23,"geometry_use":{"pcurve":81,"periodic_lift":[0,0],"sense":"Reverse"},"id":81,"loop_ref":20,"next":82,"prev":80,"to":18,"twin":72,"wire_ref":null},{"edge":34,"face":20,"from":18,"geometry_use":{"pcurve":82,"periodic_lift":[0,0],"sense":"Reverse"},"id":82,"loop_ref":20,"next":83,"prev":81,"to":6,"twin":56,"wire_ref":null},{"edge":22,"face":20,"from":6,"geometry_use":{"pcurve":83,"periodic_lift":[0,0],"sense":"Forward"},"id":83,"loop_ref":20,"next":80,"prev":82,"to":14,"twin":33,"wire_ref":null},{"edge":45,"face":21,"from":25,"geometry_use":{"pcurve":84,"periodic_lift":[0,0],"sense":"Forward"},"id":84,"loop_ref":21,"next":85,"prev":87,"to":26,"twin":95,"wire_ref":null},{"edge":46,"face":21,"from":26,"geometry_use":{"pcurve":85,"periodic_lift":[0,0],"sense":"Reverse"},"id":85,"loop_ref":21,"next":86,"prev":84,"to":16,"twin":111,"wire_ref":null},{"edge":27,"face":21,"from":16,"geometry_use":{"pcurve":86,"periodic_lift":[0,0],"sense":"Reverse"},"id":86,"loop_ref":21,"next":87,"prev":85,"to":10,"twin":45,"wire_ref":null},{"edge":47,"face":21,"from":10,"geometry_use":{"pcurve":87,"periodic_lift":[0,0],"sense":"Forward"},"id":87,"loop_ref":21,"next":84,"prev":86,"to":25,"twin":90,"wire_ref":null},{"edge":48,"face":22,"from":21,"geometry_use":{"pcurve":88,"periodic_lift":[0,0],"sense":"Forward"},"id":88,"loop_ref":22,"next":89,"prev":91,"to":27,"twin":138,"wire_ref":null},{"edge":49,"face":22,"from":27,"geometry_use":{"pcurve":89,"periodic_lift":[0,0],"sense":"Reverse"},"id":89,"loop_ref":22,"next":90,"prev":88,"to":25,"twin":92,"wire_ref":null},{"edge":47,"face":22,"from":25,"geometry_use":{"pcurve":90,"periodic_lift":[0,0],"sense":"Reverse"},"id":90,"loop_ref":22,"next":91,"prev":89,"to":10,"twin":87,"wire_ref":null},{"edge":39,"face":22,"from":10,"geometry_use":{"pcurve":91,"periodic_lift":[0,0],"sense":"Forward"},"id":91,"loop_ref":22,"next":88,"prev":90,"to":21,"twin":70,"wire_ref":null},{"edge":49,"face":23,"from":25,"geometry_use":{"pcurve":92,"periodic_lift":[0,0],"sense":"Forward"},"id":92,"loop_ref":23,"next":93,"prev":95,"to":27,"twin":89,"wire_ref":null},{"edge":50,"face":23,"from":27,"geometry_use":{"pcurve":93,"periodic_lift":[0,0],"sense":"Forward"},"id":93,"loop_ref":23,"next":94,"prev":92,"to":28,"twin":143,"wire_ref":null},{"edge":51,"face":23,"from":28,"geometry_use":{"pcurve":94,"periodic_lift":[0,0],"sense":"Reverse"},"id":94,"loop_ref":23,"next":95,"prev":93,"to":26,"twin":116,"wire_ref":null},{"edge":45,"face":23,"from":26,"geometry_use":{"pcurve":95,"periodic_lift":[0,0],"sense":"Reverse"},"id":95,"loop_ref":23,"next":92,"prev":94,"to":25,"twin":84,"wire_ref":null},{"edge":52,"face":24,"from":16,"geometry_use":{"pcurve":96,"periodic_lift":[0,0],"sense":"Forward"},"id":96,"loop_ref":24,"next":97,"prev":99,"to":29,"twin":110,"wire_ref":null},{"edge":53,"face":24,"from":29,"geometry_use":{"pcurve":97,"periodic_lift":[0,0],"sense":"Reverse"},"id":97,"loop_ref":24,"next":98,"prev":96,"to":30,"twin":100,"wire_ref":null},{"edge":54,"face":24,"from":30,"geometry_use":{"pcurve":98,"periodic_lift":[0,0],"sense":"Reverse"},"id":98,"loop_ref":24,"next":99,"prev":97,"to":13,"twin":107,"wire_ref":null},{"edge":26,"face":24,"from":13,"geometry_use":{"pcurve":99,"periodic_lift":[0,0],"sense":"Forward"},"id":99,"loop_ref":24,"next":96,"prev":98,"to":16,"twin":42,"wire_ref":null},{"edge":53,"face":25,"from":30,"geometry_use":{"pcurve":100,"periodic_lift":[0,0],"sense":"Forward"},"id":100,"loop_ref":25,"next":101,"prev":103,"to":29,"twin":97,"wire_ref":null},{"edge":55,"face":25,"from":29,"geometry_use":{"pcurve":101,"periodic_lift":[0,0],"sense":"Forward"},"id":101,"loop_ref":25,"next":102,"prev":100,"to":31,"twin":115,"wire_ref":null},{"edge":56,"face":25,"from":31,"geometry_use":{"pcurve":102,"periodic_lift":[0,0],"sense":"Reverse"},"id":102,"loop_ref":25,"next":103,"prev":101,"to":32,"twin":148,"wire_ref":null},{"edge":57,"face":25,"from":32,"geometry_use":{"pcurve":103,"periodic_lift":[0,0],"sense":"Reverse"},"id":103,"loop_ref":25,"next":100,"prev":102,"to":30,"twin":104,"wire_ref":null},{"edge":57,"face":26,"from":30,"geometry_use":{"pcurve":104,"periodic_lift":[0,0],"sense":"Forward"},"id":104,"loop_ref":26,"next":105,"prev":107,"to":32,"twin":103,"wire_ref":null},{"edge":58,"face":26,"from":32,"geometry_use":{"pcurve":105,"periodic_lift":[0,0],"sense":"Reverse"},"id":105,"loop_ref":26,"next":106,"prev":104,"to":24,"twin":155,"wire_ref":null},{"edge":43,"face":26,"from":24,"geometry_use":{"pcurve":106,"periodic_lift":[0,0],"sense":"Reverse"},"id":106,"loop_ref":26,"next":107,"prev":105,"to":13,"twin":77,"wire_ref":null},{"edge":54,"face":26,"from":13,"geometry_use":{"pcurve":107,"periodic_lift":[0,0],"sense":"Forward"},"id":107,"loop_ref":26,"next":104,"prev":106,"to":30,"twin":98,"wire_ref":null},{"edge":59,"face":27,"from":26,"geometry_use":{"pcurve":108,"periodic_lift":[0,0],"sense":"Forward"},"id":108,"loop_ref":27,"next":109,"prev":111,"to":33,"twin":119,"wire_ref":null},{"edge":60,"face":27,"from":33,"geometry_use":{"pcurve":109,"periodic_lift":[0,0],"sense":"Reverse"},"id":109,"loop_ref":27,"next":110,"prev":108,"to":29,"twin":112,"wire_ref":null},{"edge":52,"face":27,"from":29,"geometry_use":{"pcurve":110,"periodic_lift":[0,0],"sense":"Reverse"},"id":110,"loop_ref":27,"next":111,"prev":109,"to":16,"twin":96,"wire_ref":null},{"edge":46,"face":27,"from":16,"geometry_use":{"pcurve":111,"periodic_lift":[0,0],"sense":"Forward"},"id":111,"loop_ref":27,"next":108,"prev":110,"to":26,"twin":85,"wire_ref":null},{"edge":60,"face":28,"from":29,"geometry_use":{"pcurve":112,"periodic_lift":[0,0],"sense":"Forward"},"id":112,"loop_ref":28,"next":113,"prev":115,"to":33,"twin":109,"wire_ref":null},{"edge":61,"face":28,"from":33,"geometry_use":{"pcurve":113,"periodic_lift":[0,0],"sense":"Forward"},"id":113,"loop_ref":28,"next":114,"prev":112,"to":34,"twin":118,"wire_ref":null},{"edge":62,"face":28,"from":34,"geometry_use":{"pcurve":114,"periodic_lift":[0,0],"sense":"Reverse"},"id":114,"loop_ref":28,"next":115,"prev":113,"to":31,"twin":160,"wire_ref":null},{"edge":55,"face":28,"from":31,"geometry_use":{"pcurve":115,"periodic_lift":[0,0],"sense":"Reverse"},"id":115,"loop_ref":28,"next":112,"prev":114,"to":29,"twin":101,"wire_ref":null},{"edge":51,"face":29,"from":26,"geometry_use":{"pcurve":116,"periodic_lift":[0,0],"sense":"Forward"},"id":116,"loop_ref":29,"next":117,"prev":119,"to":28,"twin":94,"wire_ref":null},{"edge":63,"face":29,"from":28,"geometry_use":{"pcurve":117,"periodic_lift":[0,0],"sense":"Forward"},"id":117,"loop_ref":29,"next":118,"prev":116,"to":34,"twin":167,"wire_ref":null},{"edge":61,"face":29,"from":34,"geometry_use":{"pcurve":118,"periodic_lift":[0,0],"sense":"Reverse"},"id":118,"loop_ref":29,"next":119,"prev":117,"to":33,"twin":113,"wire_ref":null},{"edge":59,"face":29,"from":33,"geometry_use":{"pcurve":119,"periodic_lift":[0,0],"sense":"Reverse"},"id":119,"loop_ref":29,"next":116,"prev":118,"to":26,"twin":108,"wire_ref":null},{"edge":64,"face":30,"from":35,"geometry_use":{"pcurve":120,"periodic_lift":[0,0],"sense":"Forward"},"id":120,"loop_ref":30,"next":121,"prev":123,"to":36,"twin":129,"wire_ref":null},{"edge":65,"face":30,"from":36,"geometry_use":{"pcurve":121,"periodic_lift":[0,0],"sense":"Forward"},"id":121,"loop_ref":30,"next":122,"prev":120,"to":37,"twin":147,"wire_ref":null},{"edge":66,"face":30,"from":37,"geometry_use":{"pcurve":122,"periodic_lift":[0,0],"sense":"Reverse"},"id":122,"loop_ref":30,"next":123,"prev":121,"to":38,"twin":132,"wire_ref":null},{"edge":67,"face":30,"from":38,"geometry_use":{"pcurve":123,"periodic_lift":[0,0],"sense":"Reverse"},"id":123,"loop_ref":30,"next":120,"prev":122,"to":35,"twin":124,"wire_ref":null},{"edge":67,"face":31,"from":35,"geometry_use":{"pcurve":124,"periodic_lift":[0,0],"sense":"Forward"},"id":124,"loop_ref":31,"next":125,"prev":127,"to":38,"twin":123,"wire_ref":null},{"edge":68,"face":31,"from":38,"geometry_use":{"pcurve":125,"periodic_lift":[0,0],"sense":"Reverse"},"id":125,"loop_ref":31,"next":126,"prev":124,"to":21,"twin":139,"wire_ref":null},{"edge":35,"face":31,"from":21,"geometry_use":{"pcurve":126,"periodic_lift":[0,0],"sense":"Reverse"},"id":126,"loop_ref":31,"next":127,"prev":125,"to":19,"twin":61,"wire_ref":null},{"edge":69,"face":31,"from":19,"geometry_use":{"pcurve":127,"periodic_lift":[0,0],"sense":"Forward"},"id":127,"loop_ref":31,"next":124,"prev":126,"to":35,"twin":130,"wire_ref":null},{"edge":70,"face":32,"from":24,"geometry_use":{"pcurve":128,"periodic_lift":[0,0],"sense":"Forward"},"id":128,"loop_ref":32,"next":129,"prev":131,"to":36,"twin":154,"wire_ref":null},{"edge":64,"face":32,"from":36,"geometry_use":{"pcurve":129,"periodic_lift":[0,0],"sense":"Reverse"},"id":129,"loop_ref":32,"next":130,"prev":128,"to":35,"twin":120,"wire_ref":null},{"edge":69,"face":32,"from":35,"geometry_use":{"pcurve":130,"periodic_lift":[0,0],"sense":"Reverse"},"id":130,"loop_ref":32,"next":131,"prev":129,"to":19,"twin":127,"wire_ref":null},{"edge":42,"face":32,"from":19,"geometry_use":{"pcurve":131,"periodic_lift":[0,0],"sense":"Forward"},"id":131,"loop_ref":32,"next":128,"prev":130,"to":24,"twin":74,"wire_ref":null},{"edge":66,"face":33,"from":38,"geometry_use":{"pcurve":132,"periodic_lift":[0,0],"sense":"Forward"},"id":132,"loop_ref":33,"next":133,"prev":135,"to":37,"twin":122,"wire_ref":null},{"edge":71,"face":33,"from":37,"geometry_use":{"pcurve":133,"periodic_lift":[0,0],"sense":"Forward"},"id":133,"loop_ref":33,"next":134,"prev":132,"to":39,"twin":159,"wire_ref":null},{"edge":72,"face":33,"from":39,"geometry_use":{"pcurve":134,"periodic_lift":[0,0],"sense":"Reverse"},"id":134,"loop_ref":33,"next":135,"prev":133,"to":40,"twin":141,"wire_ref":null},{"edge":73,"face":33,"from":40,"geometry_use":{"pcurve":135,"periodic_lift":[0,0],"sense":"Reverse"},"id":135,"loop_ref":33,"next":132,"prev":134,"to":38,"twin":136,"wire_ref":null},{"edge":73,"face":34,"from":38,"geometry_use":{"pcurve":136,"periodic_lift":[0,0],"sense":"Forward"},"id":136,"loop_ref":34,"next":137,"prev":139,"to":40,"twin":135,"wire_ref":null},{"edge":74,"face":34,"from":40,"geometry_use":{"pcurve":137,"periodic_lift":[0,0],"sense":"Reverse"},"id":137,"loop_ref":34,"next":138,"prev":136,"to":27,"twin":140,"wire_ref":null},{"edge":48,"face":34,"from":27,"geometry_use":{"pcurve":138,"periodic_lift":[0,0],"sense":"Reverse"},"id":138,"loop_ref":34,"next":139,"prev":137,"to":21,"twin":88,"wire_ref":null},{"edge":68,"face":34,"from":21,"geometry_use":{"pcurve":139,"periodic_lift":[0,0],"sense":"Forward"},"id":139,"loop_ref":34,"next":136,"prev":138,"to":38,"twin":125,"wire_ref":null},{"edge":74,"face":35,"from":27,"geometry_use":{"pcurve":140,"periodic_lift":[0,0],"sense":"Forward"},"id":140,"loop_ref":35,"next":141,"prev":143,"to":40,"twin":137,"wire_ref":null},{"edge":72,"face":35,"from":40,"geometry_use":{"pcurve":141,"periodic_lift":[0,0],"sense":"Forward"},"id":141,"loop_ref":35,"next":142,"prev":140,"to":39,"twin":134,"wire_ref":null},{"edge":75,"face":35,"from":39,"geometry_use":{"pcurve":142,"periodic_lift":[0,0],"sense":"Reverse"},"id":142,"loop_ref":35,"next":143,"prev":141,"to":28,"twin":164,"wire_ref":null},{"edge":50,"face":35,"from":28,"geometry_use":{"pcurve":143,"periodic_lift":[0,0],"sense":"Reverse"},"id":143,"loop_ref":35,"next":140,"prev":142,"to":27,"twin":93,"wire_ref":null},{"edge":76,"face":36,"from":36,"geometry_use":{"pcurve":144,"periodic_lift":[0,0],"sense":"Forward"},"id":144,"loop_ref":36,"next":145,"prev":147,"to":41,"twin":153,"wire_ref":null},{"edge":77,"face":36,"from":41,"geometry_use":{"pcurve":145,"periodic_lift":[0,0],"sense":"Forward"},"id":145,"loop_ref":36,"next":146,"prev":144,"to":42,"twin":150,"wire_ref":null},{"edge":78,"face":36,"from":42,"geometry_use":{"pcurve":146,"periodic_lift":[0,0],"sense":"Reverse"},"id":146,"loop_ref":36,"next":147,"prev":145,"to":37,"twin":156,"wire_ref":null},{"edge":65,"face":36,"from":37,"geometry_use":{"pcurve":147,"periodic_lift":[0,0],"sense":"Reverse"},"id":147,"loop_ref":36,"next":144,"prev":146,"to":36,"twin":121,"wire_ref":null},{"edge":56,"face":37,"from":32,"geometry_use":{"pcurve":148,"periodic_lift":[0,0],"sense":"Forward"},"id":148,"loop_ref":37,"next":149,"prev":151,"to":31,"twin":102,"wire_ref":null},{"edge":79,"face":37,"from":31,"geometry_use":{"pcurve":149,"periodic_lift":[0,0],"sense":"Forward"},"id":149,"loop_ref":37,"next":150,"prev":148,"to":42,"twin":163,"wire_ref":null},{"edge":77,"face":37,"from":42,"geometry_use":{"pcurve":150,"periodic_lift":[0,0],"sense":"Reverse"},"id":150,"loop_ref":37,"next":151,"prev":149,"to":41,"twin":145,"wire_ref":null},{"edge":80,"face":37,"from":41,"geometry_use":{"pcurve":151,"periodic_lift":[0,0],"sense":"Reverse"},"id":151,"loop_ref":37,"next":148,"prev":150,"to":32,"twin":152,"wire_ref":null},{"edge":80,"face":38,"from":32,"geometry_use":{"pcurve":152,"periodic_lift":[0,0],"sense":"Forward"},"id":152,"loop_ref":38,"next":153,"prev":155,"to":41,"twin":151,"wire_ref":null},{"edge":76,"face":38,"from":41,"geometry_use":{"pcurve":153,"periodic_lift":[0,0],"sense":"Reverse"},"id":153,"loop_ref":38,"next":154,"prev":152,"to":36,"twin":144,"wire_ref":null},{"edge":70,"face":38,"from":36,"geometry_use":{"pcurve":154,"periodic_lift":[0,0],"sense":"Reverse"},"id":154,"loop_ref":38,"next":155,"prev":153,"to":24,"twin":128,"wire_ref":null},{"edge":58,"face":38,"from":24,"geometry_use":{"pcurve":155,"periodic_lift":[0,0],"sense":"Forward"},"id":155,"loop_ref":38,"next":152,"prev":154,"to":32,"twin":105,"wire_ref":null},{"edge":78,"face":39,"from":37,"geometry_use":{"pcurve":156,"periodic_lift":[0,0],"sense":"Forward"},"id":156,"loop_ref":39,"next":157,"prev":159,"to":42,"twin":146,"wire_ref":null},{"edge":81,"face":39,"from":42,"geometry_use":{"pcurve":157,"periodic_lift":[0,0],"sense":"Forward"},"id":157,"loop_ref":39,"next":158,"prev":156,"to":43,"twin":162,"wire_ref":null},{"edge":82,"face":39,"from":43,"geometry_use":{"pcurve":158,"periodic_lift":[0,0],"sense":"Reverse"},"id":158,"loop_ref":39,"next":159,"prev":157,"to":39,"twin":165,"wire_ref":null},{"edge":71,"face":39,"from":39,"geometry_use":{"pcurve":159,"periodic_lift":[0,0],"sense":"Reverse"},"id":159,"loop_ref":39,"next":156,"prev":158,"to":37,"twin":133,"wire_ref":null},{"edge":62,"face":40,"from":31,"geometry_use":{"pcurve":160,"periodic_lift":[0,0],"sense":"Forward"},"id":160,"loop_ref":40,"next":161,"prev":163,"to":34,"twin":114,"wire_ref":null},{"edge":83,"face":40,"from":34,"geometry_use":{"pcurve":161,"periodic_lift":[0,0],"sense":"Forward"},"id":161,"loop_ref":40,"next":162,"prev":160,"to":43,"twin":166,"wire_ref":null},{"edge":81,"face":40,"from":43,"geometry_use":{"pcurve":162,"periodic_lift":[0,0],"sense":"Reverse"},"id":162,"loop_ref":40,"next":163,"prev":161,"to":42,"twin":157,"wire_ref":null},{"edge":79,"face":40,"from":42,"geometry_use":{"pcurve":163,"periodic_lift":[0,0],"sense":"Reverse"},"id":163,"loop_ref":40,"next":160,"prev":162,"to":31,"twin":149,"wire_ref":null},{"edge":75,"face":41,"from":28,"geometry_use":{"pcurve":164,"periodic_lift":[0,0],"sense":"Forward"},"id":164,"loop_ref":41,"next":165,"prev":167,"to":39,"twin":142,"wire_ref":null},{"edge":82,"face":41,"from":39,"geometry_use":{"pcurve":165,"periodic_lift":[0,0],"sense":"Forward"},"id":165,"loop_ref":41,"next":166,"prev":164,"to":43,"twin":158,"wire_ref":null},{"edge":83,"face":41,"from":43,"geometry_use":{"pcurve":166,"periodic_lift":[0,0],"sense":"Reverse"},"id":166,"loop_ref":41,"next":167,"prev":165,"to":34,"twin":161,"wire_ref":null},{"edge":63,"face":41,"from":34,"geometry_use":{"pcurve":167,"periodic_lift":[0,0],"sense":"Reverse"},"id":167,"loop_ref":41,"next":164,"prev":166,"to":28,"twin":117,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20},{"face_ref":6,"id":6,"is_hole":false,"start_halfedge":24},{"face_ref":7,"id":7,"is_hole":false,"start_halfedge":28},{"face_ref":8,"id":8,"is_hole":false,"start_halfedge":32},{"face_ref":9,"id":9,"is_hole":false,"start_halfedge":36},{"face_ref":10,"id":10,"is_hole":false,"start_halfedge":40},{"face_ref":11,"id":11,"is_hole":false,"start_halfedge":44},{"face_ref":12,"id":12,"is_hole":false,"start_halfedge":48},{"face_ref":13,"id":13,"is_hole":false,"start_halfedge":52},{"face_ref":14,"id":14,"is_hole":false,"start_halfedge":56},{"face_ref":15,"id":15,"is_hole":false,"start_halfedge":60},{"face_ref":16,"id":16,"is_hole":false,"start_halfedge":64},{"face_ref":17,"id":17,"is_hole":false,"start_halfedge":68},{"face_ref":18,"id":18,"is_hole":false,"start_halfedge":72},{"face_ref":19,"id":19,"is_hole":false,"start_halfedge":76},{"face_ref":20,"id":20,"is_hole":false,"start_halfedge":80},{"face_ref":21,"id":21,"is_hole":false,"start_halfedge":84},{"face_ref":22,"id":22,"is_hole":false,"start_halfedge":88},{"face_ref":23,"id":23,"is_hole":false,"start_halfedge":92},{"face_ref":24,"id":24,"is_hole":false,"start_halfedge":96},{"face_ref":25,"id":25,"is_hole":false,"start_halfedge":100},{"face_ref":26,"id":26,"is_hole":false,"start_halfedge":104},{"face_ref":27,"id":27,"is_hole":false,"start_halfedge":108},{"face_ref":28,"id":28,"is_hole":false,"start_halfedge":112},{"face_ref":29,"id":29,"is_hole":false,"start_halfedge":116},{"face_ref":30,"id":30,"is_hole":false,"start_halfedge":120},{"face_ref":31,"id":31,"is_hole":false,"start_halfedge":124},{"face_ref":32,"id":32,"is_hole":false,"start_halfedge":128},{"face_ref":33,"id":33,"is_hole":false,"start_halfedge":132},{"face_ref":34,"id":34,"is_hole":false,"start_halfedge":136},{"face_ref":35,"id":35,"is_hole":false,"start_halfedge":140},{"face_ref":36,"id":36,"is_hole":false,"start_halfedge":144},{"face_ref":37,"id":37,"is_hole":false,"start_halfedge":148},{"face_ref":38,"id":38,"is_hole":false,"start_halfedge":152},{"face_ref":39,"id":39,"is_hole":false,"start_halfedge":156},{"face_ref":40,"id":40,"is_hole":false,"start_halfedge":160},{"face_ref":41,"id":41,"is_hole":false,"start_halfedge":164}],"shells":[{"faces":[0,3,6,2,1,5,9,4,7,8,14,13,17,11,10,16,19,20,12,15,22,21,24,26,18,31,34,23,27,25,38,32,30,33,35,29,28,37,36,39,41,40],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.5,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":1,"position":[0.0,0.5,-0.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":2,"position":[0.0,0.0,-0.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":3,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[1.5,0.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[1.5,0.5,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":9,"position":[1.5,0.0,-0.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":12,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":8,"outgoing_halfedge":13,"position":[0.0,3.0,-0.5],"tolerance":1e-8},{"id":9,"outgoing_halfedge":17,"position":[1.5,3.0,0.0],"tolerance":1e-8},{"id":10,"outgoing_halfedge":22,"position":[1.5,3.0,-0.5],"tolerance":1e-8},{"id":11,"outgoing_halfedge":25,"position":[0.0,0.5,-3.0],"tolerance":1e-8},{"id":12,"outgoing_halfedge":26,"position":[0.0,0.0,-3.0],"tolerance":1e-8},{"id":13,"outgoing_halfedge":30,"position":[1.5,0.5,-3.0],"tolerance":1e-8},{"id":14,"outgoing_halfedge":31,"position":[1.5,0.0,-3.0],"tolerance":1e-8},{"id":15,"outgoing_halfedge":37,"position":[0.0,3.0,-3.0],"tolerance":1e-8},{"id":16,"outgoing_halfedge":42,"position":[1.5,3.0,-3.0],"tolerance":1e-8},{"id":17,"outgoing_halfedge":48,"position":[3.0,0.0,0.0],"tolerance":1e-8},{"id":18,"outgoing_halfedge":49,"position":[3.0,0.0,-0.5],"tolerance":1e-8},{"id":19,"outgoing_halfedge":50,"position":[3.0,0.5,-0.5],"tolerance":1e-8},{"id":20,"outgoing_halfedge":51,"position":[3.0,0.5,0.0],"tolerance":1e-8},{"id":21,"outgoing_halfedge":62,"position":[3.0,3.0,-0.5],"tolerance":1e-8},{"id":22,"outgoing_halfedge":63,"position":[3.0,3.0,0.0],"tolerance":1e-8},{"id":23,"outgoing_halfedge":73,"position":[3.0,0.0,-3.0],"tolerance":1e-8},{"id":24,"outgoing_halfedge":74,"position":[3.0,0.5,-3.0],"tolerance":1e-8},{"id":25,"outgoing_halfedge":84,"position":[1.5,3.5,-0.5],"tolerance":1e-8},{"id":26,"outgoing_halfedge":85,"position":[1.5,3.5,-3.0],"tolerance":1e-8},{"id":27,"outgoing_halfedge":89,"position":[3.0,3.5,-0.5],"tolerance":1e-8},{"id":28,"outgoing_halfedge":94,"position":[3.0,3.5,-3.0],"tolerance":1e-8},{"id":29,"outgoing_halfedge":97,"position":[1.5,3.0,-3.5],"tolerance":1e-8},{"id":30,"outgoing_halfedge":98,"position":[1.5,0.5,-3.5],"tolerance":1e-8},{"id":31,"outgoing_halfedge":102,"position":[3.0,3.0,-3.5],"tolerance":1e-8},{"id":32,"outgoing_halfedge":103,"position":[3.0,0.5,-3.5],"tolerance":1e-8},{"id":33,"outgoing_halfedge":109,"position":[1.5,3.5,-3.5],"tolerance":1e-8},{"id":34,"outgoing_halfedge":114,"position":[3.0,3.5,-3.5],"tolerance":1e-8},{"id":35,"outgoing_halfedge":120,"position":[4.5,0.5,-0.5],"tolerance":1e-8},{"id":36,"outgoing_halfedge":121,"position":[4.5,0.5,-3.0],"tolerance":1e-8},{"id":37,"outgoing_halfedge":122,"position":[4.5,3.0,-3.0],"tolerance":1e-8},{"id":38,"outgoing_halfedge":123,"position":[4.5,3.0,-0.5],"tolerance":1e-8},{"id":39,"outgoing_halfedge":134,"position":[4.5,3.5,-3.0],"tolerance":1e-8},{"id":40,"outgoing_halfedge":135,"position":[4.5,3.5,-0.5],"tolerance":1e-8},{"id":41,"outgoing_halfedge":145,"position":[4.5,0.5,-3.5],"tolerance":1e-8},{"id":42,"outgoing_halfedge":146,"position":[4.5,3.0,-3.5],"tolerance":1e-8},{"id":43,"outgoing_halfedge":158,"position":[4.5,3.5,-3.5],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-box-union.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-box-union.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-box-union.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-circle.json b/main/opengeometry/tests/fixtures/cases/validity/builder-circle.json new file mode 100644 index 0000000..dc7212d --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-circle.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0}],"intersections":[],"pcurves":[],"surfaces":[]},"id":"circle","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null}],"faces":[],"halfedges":[{"edge":0,"face":null,"from":0,"geometry_use":{"pcurve":null,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":null,"next":0,"prev":0,"to":0,"twin":null,"wire_ref":0}],"loops":[],"shells":[],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8}],"wires":[{"id":0,"is_closed":true,"start_halfedge":0}]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-circle.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-circle.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-circle.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-cone.json b/main/opengeometry/tests/fixtures/cases/validity/builder-cone.json new file mode 100644 index 0000000..d548a17 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-cone.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Circle","radius":1.0},{"direction":[0.4472135954999579,-0.8944271909999159,0.0],"kind":"Line","origin":[0.0,2.0,0.0]}],"intersections":[],"pcurves":[{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,0.8944271909999159],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,0.8944271909999159],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Cone","semi_angle":0.4636476090008061},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"}]},"id":"cone","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":true,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.23606797749979,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cone","entity":"cone","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":2.0,"lo":0.0}]}},{"id":1,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cone","entity":"cone","face":1,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":1,"face":1,"from":1,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4}],"shells":[{"faces":[0,1],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,0.0,0.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-cone.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-cone.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-cone.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-cuboid.json b/main/opengeometry/tests/fixtures/cases/validity/builder-cuboid.json new file mode 100644 index 0000000..29237b5 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-cuboid.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-1.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-1.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,3.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,3.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,3.0,-1.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[3.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,3.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-1.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"cuboid","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":3.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cuboid","entity":"cuboid","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cuboid","entity":"cuboid","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cuboid","entity":"cuboid","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":1.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cuboid","entity":"cuboid","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cuboid","entity":"cuboid","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":3.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cuboid","entity":"cuboid","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":3.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[2.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-1.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[2.0,0.0,-1.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,3.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[2.0,3.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,3.0,-1.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[2.0,3.0,-1.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-cuboid.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-cuboid.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-cuboid.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder-with-hole.json b/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder-with-hole.json new file mode 100644 index 0000000..ae57a75 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder-with-hole.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":0.4},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.4,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[0.4,0.0],"axis_b":[0.0,0.4],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":0.4},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"cylinder-with-hole","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":4,"id":2,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":true,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":3},{"chart_seam":true,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":7}],"faces":[{"id":0,"key":"outer","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cylinder-with-hole","entity":"cylinder-with-hole","face":0,"key":"outer"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":2.0,"lo":0.0}]}},{"id":1,"key":"inner","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cylinder-with-hole","entity":"cylinder-with-hole","face":1,"key":"inner"}]},"sense":"Reverse","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":2.0,"lo":0.0}]}},{"id":2,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cylinder-with-hole","entity":"cylinder-with-hole","face":2,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[3],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":3,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cylinder-with-hole","entity":"cylinder-with-hole","face":3,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[5],"outer":4,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":8,"wire_ref":null},{"edge":4,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":2,"face":1,"from":2,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":2,"twin":9,"wire_ref":null},{"edge":5,"face":1,"from":2,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":3,"twin":7,"wire_ref":null},{"edge":3,"face":1,"from":3,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":3,"twin":11,"wire_ref":null},{"edge":5,"face":1,"from":3,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":2,"twin":5,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Reverse"},"id":8,"loop_ref":2,"next":8,"prev":8,"to":0,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":2,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Reverse"},"id":9,"loop_ref":3,"next":9,"prev":9,"to":2,"twin":4,"wire_ref":null},{"edge":1,"face":3,"from":1,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Forward"},"id":10,"loop_ref":4,"next":10,"prev":10,"to":1,"twin":2,"wire_ref":null},{"edge":3,"face":3,"from":3,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Forward"},"id":11,"loop_ref":5,"next":11,"prev":11,"to":3,"twin":6,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":2,"id":3,"is_hole":true,"start_halfedge":9},{"face_ref":3,"id":4,"is_hole":false,"start_halfedge":10},{"face_ref":3,"id":5,"is_hole":true,"start_halfedge":11}],"shells":[{"faces":[0,1,2,3],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,2.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":4,"position":[0.4,0.0,0.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":6,"position":[0.4,2.0,0.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder-with-hole.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder-with-hole.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder-with-hole.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder.json b/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder.json new file mode 100644 index 0000000..3a70db5 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,-1.0],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Cylinder","radius":1.0},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"}]},"id":"cylinder","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cylinder","entity":"cylinder","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":2.0,"lo":0.0}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cylinder","entity":"cylinder","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"cylinder","entity":"cylinder","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,2.0,0.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-cylinder.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-frustum.json b/main/opengeometry/tests/fixtures/cases/validity/builder-frustum.json new file mode 100644 index 0000000..0475f59 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-frustum.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Circle","radius":0.4},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Circle","radius":1.0},{"direction":[0.2873478855663454,-0.9578262852211513,0.0],"kind":"Line","origin":[0.4,2.0,0.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,1.3333333333333335]},{"direction":[0.0,0.9578262852211513],"kind":"Line2","origin":[6.283185307179586,1.3333333333333335]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,3.3333333333333335]},{"direction":[0.0,0.9578262852211513],"kind":"Line2","origin":[0.0,1.3333333333333335]},{"axis_a":[0.4,0.0],"axis_b":[0.0,-0.4],"kind":"Conic2","origin":[0.0,0.0]},{"axis_a":[1.0,0.0],"axis_b":[0.0,1.0],"kind":"Conic2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,3.3333333333333335,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Cone","semi_angle":0.2914567944778671},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"}]},"id":"frustum","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":4},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":5},{"chart_seam":true,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.08806130178211,"lo":0.0}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"lateral","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"frustum","entity":"frustum","face":0,"key":"lateral"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":3.3333333333333335,"lo":1.3333333333333335}]}},{"id":1,"key":"lower_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"frustum","entity":"frustum","face":1,"key":"lower_cap"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":0.4,"lo":-0.4},{"hi":0.4,"lo":-0.4}]}},{"id":2,"key":"upper_cap","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"frustum","entity":"frustum","face":2,"key":"upper_cap"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":1.0,"lo":-1.0},{"hi":1.0,"lo":-1.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":5,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":0,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":4,"prev":4,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":2,"from":1,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":2,"next":5,"prev":5,"to":1,"twin":2,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":5}],"shells":[{"faces":[0,1,2],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.4,2.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[1.0,0.0,0.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-frustum.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-frustum.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-frustum.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-linear-extrusion.json b/main/opengeometry/tests/fixtures/cases/validity/builder-linear-extrusion.json new file mode 100644 index 0000000..cb87916 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-linear-extrusion.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.0,0.0,0.5]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[-1.0,2.0,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-1.0,0.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,0.0,0.5]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[1.0,2.0,0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,0.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.0,0.0,-0.5]},{"direction":[-1.0,0.0,0.0],"kind":"Line","origin":[1.0,2.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[1.0,0.0,-0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-1.0,0.0,-0.5]},{"direction":[0.0,0.0,1.0],"kind":"Line","origin":[-1.0,2.0,-0.5]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[-1.0,0.0,-0.5]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[-1.0,-0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[-1.0,0.5]},{"direction":[0.0,1.0],"kind":"Line2","origin":[-1.0,-0.5]},{"direction":[-1.0,0.0],"kind":"Line2","origin":[1.0,-0.5]},{"direction":[0.0,-1.0],"kind":"Line2","origin":[1.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[-1.0,0.5]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[1.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,-0.0,1.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[-1.0,0.0,0.5],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.0,0.5],"x":[0.0,0.0,-1.0],"y":[0.0,1.0,0.0],"z":[1.0,-0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[1.0,0.0,-0.5],"x":[-1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"},{"frame":{"origin":[-1.0,0.0,-0.5],"x":[0.0,0.0,1.0],"y":[0.0,1.0,-0.0],"z":[-1.0,0.0,0.0]},"kind":"Plane"}]},"id":"linear-extrusion","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":0,"tolerance":1e-8,"twin_halfedge":8},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":10},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":11,"id":2,"tolerance":1e-8,"twin_halfedge":21},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":6,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":1,"id":4,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":9,"id":5,"tolerance":1e-8,"twin_halfedge":15},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":6,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":7,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":8,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":4,"id":9,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":1.0,"lo":0.0}},"halfedge":3,"id":10,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":17,"id":11,"tolerance":1e-8,"twin_halfedge":23}],"faces":[{"id":0,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"linear-extrusion","entity":"linear-extrusion","face":0,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":1.00000001,"lo":-1.00000001},{"hi":0.50000001,"lo":-0.50000001}]}},{"id":1,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"linear-extrusion","entity":"linear-extrusion","face":1,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":1.00000001,"lo":-1.00000001},{"hi":0.50000001,"lo":-0.50000001}]}},{"id":2,"key":"side-0-0","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"linear-extrusion","entity":"linear-extrusion","face":2,"key":"side-0-0"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"side-0-1","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"linear-extrusion","entity":"linear-extrusion","face":3,"key":"side-0-1"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"side-0-2","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"linear-extrusion","entity":"linear-extrusion","face":4,"key":"side-0-2"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"side-0-3","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"linear-extrusion","entity":"linear-extrusion","face":5,"key":"side-0-3"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":1.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":4,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":5,"twin":10,"wire_ref":null},{"edge":4,"face":0,"from":5,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":6,"twin":14,"wire_ref":null},{"edge":7,"face":0,"from":6,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":7,"twin":18,"wire_ref":null},{"edge":10,"face":0,"from":7,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Forward"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":4,"twin":22,"wire_ref":null},{"edge":9,"face":1,"from":0,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":20,"wire_ref":null},{"edge":6,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":2,"twin":16,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":1,"from":1,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":0,"twin":8,"wire_ref":null},{"edge":0,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":1,"twin":7,"wire_ref":null},{"edge":5,"face":2,"from":1,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":5,"twin":15,"wire_ref":null},{"edge":1,"face":2,"from":5,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":4,"twin":0,"wire_ref":null},{"edge":2,"face":2,"from":4,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":21,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":2,"twin":6,"wire_ref":null},{"edge":8,"face":3,"from":2,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":6,"twin":19,"wire_ref":null},{"edge":4,"face":3,"from":6,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":1,"wire_ref":null},{"edge":5,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":9,"wire_ref":null},{"edge":6,"face":4,"from":2,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":3,"twin":5,"wire_ref":null},{"edge":11,"face":4,"from":3,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":7,"twin":23,"wire_ref":null},{"edge":7,"face":4,"from":7,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":6,"twin":2,"wire_ref":null},{"edge":8,"face":4,"from":6,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":2,"twin":13,"wire_ref":null},{"edge":9,"face":5,"from":3,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":0,"twin":4,"wire_ref":null},{"edge":2,"face":5,"from":0,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":4,"twin":11,"wire_ref":null},{"edge":10,"face":5,"from":4,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":7,"twin":3,"wire_ref":null},{"edge":11,"face":5,"from":7,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":3,"twin":17,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":4,"position":[-1.0,0.0,0.5],"tolerance":1e-8},{"id":1,"outgoing_halfedge":7,"position":[1.0,0.0,0.5],"tolerance":1e-8},{"id":2,"outgoing_halfedge":6,"position":[1.0,0.0,-0.5],"tolerance":1e-8},{"id":3,"outgoing_halfedge":5,"position":[-1.0,0.0,-0.5],"tolerance":1e-8},{"id":4,"outgoing_halfedge":0,"position":[-1.0,2.0,0.5],"tolerance":1e-8},{"id":5,"outgoing_halfedge":1,"position":[1.0,2.0,0.5],"tolerance":1e-8},{"id":6,"outgoing_halfedge":2,"position":[1.0,2.0,-0.5],"tolerance":1e-8},{"id":7,"outgoing_halfedge":3,"position":[-1.0,2.0,-0.5],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-linear-extrusion.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-linear-extrusion.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-linear-extrusion.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-sphere-cut.json b/main/opengeometry/tests/fixtures/cases/validity/builder-sphere-cut.json new file mode 100644 index 0000000..f1ea21f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-sphere-cut.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":2.0},{"frame":{"origin":[0.7,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":1.0}],"intersections":[],"pcurves":[{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,-1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[-6.283185307179586,0.0],"kind":"Line2","origin":[6.283185307179586,1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,-1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[-6.283185307179586,0.0],"kind":"Line2","origin":[6.283185307179586,1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Sphere","radius":2.0},{"frame":{"origin":[0.7,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Sphere","radius":1.0}]},"id":"sphere-cut","quality":{"kind":"Analytic"},"revision":1,"schema_version":2,"solids":[{"cavity_shells":[1],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":1},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":-1.5707963267948966}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3},{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":2},"halfedge":4,"id":3,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":3},"halfedge":6,"id":4,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"curve":1,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":-1.5707963267948966}},"halfedge":5,"id":5,"tolerance":1e-8,"twin_halfedge":7}],"faces":[{"id":0,"key":"0:skin","provenance":{"reversed":false,"role":"Preserved","sources":[{"body":"sphere-host","entity":"sphere-host","face":0,"key":"skin"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.5707963267948966,"lo":-1.5707963267948966}]}},{"id":1,"key":"1:skin","provenance":{"reversed":true,"role":"Cut","sources":[{"body":"sphere-cutter","entity":"sphere-cutter","face":0,"key":"skin"}]},"sense":"Reverse","shell_ref":1,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.5707963267948966,"lo":-1.5707963267948966}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null},{"edge":3,"face":1,"from":2,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Reverse"},"id":4,"loop_ref":1,"next":7,"prev":5,"to":2,"twin":null,"wire_ref":null},{"edge":5,"face":1,"from":3,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Reverse"},"id":5,"loop_ref":1,"next":4,"prev":6,"to":2,"twin":7,"wire_ref":null},{"edge":4,"face":1,"from":3,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":5,"prev":7,"to":3,"twin":null,"wire_ref":null},{"edge":5,"face":1,"from":2,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Forward"},"id":7,"loop_ref":1,"next":6,"prev":4,"to":3,"twin":5,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4}],"shells":[{"faces":[0],"id":0,"is_closed":true},{"faces":[1],"id":1,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,-2.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":4,"position":[0.7,-1.0,0.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":5,"position":[0.7,1.0,0.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-sphere-cut.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-sphere-cut.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-sphere-cut.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-sphere.json b/main/opengeometry/tests/fixtures/cases/validity/builder-sphere.json new file mode 100644 index 0000000..c25c2fb --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-sphere.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":1.0}],"intersections":[],"pcurves":[{"direction":[6.283185307179586,0.0],"kind":"Line2","origin":[0.0,-1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[-6.283185307179586,0.0],"kind":"Line2","origin":[6.283185307179586,1.5707963267948966]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Sphere","radius":1.0}]},"id":"sphere","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":0},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"kind":"Collapsed","vertex":1},"halfedge":2,"id":1,"tolerance":1e-8,"twin_halfedge":null},{"chart_seam":true,"geometry":{"curve":0,"kind":"Curve","range":{"hi":1.5707963267948966,"lo":-1.5707963267948966}},"halfedge":1,"id":2,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"skin","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"sphere","entity":"sphere","face":0,"key":"skin"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":1.5707963267948966,"lo":-1.5707963267948966}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":1,"twin":3,"wire_ref":null},{"edge":1,"face":0,"from":1,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Forward"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":null,"wire_ref":null},{"edge":2,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0}],"shells":[{"faces":[0],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,-1.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":2,"position":[0.0,1.0,0.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-sphere.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-sphere.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-sphere.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-torus.json b/main/opengeometry/tests/fixtures/cases/validity/builder-torus.json new file mode 100644 index 0000000..5de6bdc --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-torus.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":2.5},{"frame":{"origin":[2.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Circle","radius":0.5}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[6.283185307179586,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,6.283185307179586]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Torus","major_radius":2.0,"minor_radius":0.5}]},"id":"torus","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":true,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":2},{"chart_seam":true,"geometry":{"curve":1,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":1,"id":1,"tolerance":1e-8,"twin_halfedge":3}],"faces":[{"id":0,"key":"skin","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"torus","entity":"torus","face":0,"key":"skin"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":6.283185307179586,"lo":0.0},{"hi":6.283185307179586,"lo":0.0}]}}],"halfedges":[{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":0,"twin":2,"wire_ref":null},{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":0,"twin":3,"wire_ref":null},{"edge":0,"face":0,"from":0,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":0,"twin":0,"wire_ref":null},{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0}],"shells":[{"faces":[0],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[2.5,0.0,0.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/builder-torus.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/builder-torus.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/builder-torus.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/circle.json b/main/opengeometry/tests/fixtures/cases/validity/circle.json new file mode 100644 index 0000000..5ba6be9 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/circle.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0}],"intersections":[],"pcurves":[],"surfaces":[]},"id":"validity-circle","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null}],"faces":[],"halfedges":[{"edge":0,"face":null,"from":0,"geometry_use":{"pcurve":null,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":null,"next":0,"prev":0,"to":0,"twin":null,"wire_ref":0}],"loops":[],"shells":[],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8}],"wires":[{"id":0,"is_closed":true,"start_halfedge":0}]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/circle.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/circle.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/circle.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/closed-wire-cycle.json b/main/opengeometry/tests/fixtures/cases/validity/closed-wire-cycle.json new file mode 100644 index 0000000..15376ac --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/closed-wire-cycle.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0}],"intersections":[],"pcurves":[],"surfaces":[]},"id":"validity-circle","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null}],"faces":[],"halfedges":[{"edge":0,"face":null,"from":0,"geometry_use":{"pcurve":null,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":null,"next":0,"prev":0,"to":0,"twin":null,"wire_ref":0}],"loops":[],"shells":[],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8}],"wires":[{"id":0,"is_closed":false,"start_halfedge":0}]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/closed-wire-cycle.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/closed-wire-cycle.verdict.json new file mode 100644 index 0000000..88c7c2a --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/closed-wire-cycle.verdict.json @@ -0,0 +1 @@ +{"error":{"InvalidTopology":"open wire forms a cycle"},"valid":false} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/cuboid.json b/main/opengeometry/tests/fixtures/cases/validity/cuboid.json new file mode 100644 index 0000000..5e03ced --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/cuboid.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,-2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-2.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"validity-cuboid","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[2.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-2.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[2.0,0.0,-2.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[2.0,2.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,2.0,-2.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[2.0,2.0,-2.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/cuboid.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/cuboid.verdict.json new file mode 100644 index 0000000..a9d0d8f --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/cuboid.verdict.json @@ -0,0 +1 @@ +{"valid":true} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/dense-ids.json b/main/opengeometry/tests/fixtures/cases/validity/dense-ids.json new file mode 100644 index 0000000..95799c5 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/dense-ids.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,-2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-2.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"validity-cuboid","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":9,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[2.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-2.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[2.0,0.0,-2.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[2.0,2.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,2.0,-2.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[2.0,2.0,-2.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/dense-ids.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/dense-ids.verdict.json new file mode 100644 index 0000000..2b86d20 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/dense-ids.verdict.json @@ -0,0 +1 @@ +{"error":{"InvalidTopology":"vertex IDs must be dense"},"valid":false} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/face-pcurve.json b/main/opengeometry/tests/fixtures/cases/validity/face-pcurve.json new file mode 100644 index 0000000..e673443 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/face-pcurve.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,-2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-2.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"validity-cuboid","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":null,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[2.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-2.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[2.0,0.0,-2.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[2.0,2.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,2.0,-2.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[2.0,2.0,-2.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/face-pcurve.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/face-pcurve.verdict.json new file mode 100644 index 0000000..f51979e --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/face-pcurve.verdict.json @@ -0,0 +1 @@ +{"error":{"InvalidTopology":"face halfedge has no pcurve"},"valid":false} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/loop-membership.json b/main/opengeometry/tests/fixtures/cases/validity/loop-membership.json new file mode 100644 index 0000000..2b11f8c --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/loop-membership.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,-2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-2.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"validity-cuboid","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":null,"next":1,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[2.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-2.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[2.0,0.0,-2.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[2.0,2.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,2.0,-2.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[2.0,2.0,-2.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/loop-membership.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/loop-membership.verdict.json new file mode 100644 index 0000000..bf4a1a2 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/loop-membership.verdict.json @@ -0,0 +1 @@ +{"error":{"InvalidTopology":"inconsistent next link"},"valid":false} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/next-prev.json b/main/opengeometry/tests/fixtures/cases/validity/next-prev.json new file mode 100644 index 0000000..a764667 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/next-prev.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,-2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-2.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"validity-cuboid","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":null,"prev":3,"to":2,"twin":11,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[2.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-2.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[2.0,0.0,-2.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[2.0,2.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,2.0,-2.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[2.0,2.0,-2.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/next-prev.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/next-prev.verdict.json new file mode 100644 index 0000000..e9b1129 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/next-prev.verdict.json @@ -0,0 +1 @@ +{"error":{"InvalidTopology":"face halfedge has incomplete loop links"},"valid":false} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/open-wire-end.json b/main/opengeometry/tests/fixtures/cases/validity/open-wire-end.json new file mode 100644 index 0000000..dfad342 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/open-wire-end.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0}],"intersections":[],"pcurves":[],"surfaces":[]},"id":"validity-arc","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":3.141592653589793,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null}],"faces":[],"halfedges":[{"edge":0,"face":null,"from":0,"geometry_use":{"pcurve":null,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":null,"next":null,"prev":null,"to":1,"twin":null,"wire_ref":0}],"loops":[],"shells":[],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":null,"position":[-1.0,0.0,-1.2246467991473532e-16],"tolerance":1e-8}],"wires":[{"id":0,"is_closed":true,"start_halfedge":0}]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/open-wire-end.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/open-wire-end.verdict.json new file mode 100644 index 0000000..1ae22e1 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/open-wire-end.verdict.json @@ -0,0 +1 @@ +{"error":{"InvalidTopology":"closed wire is open"},"valid":false} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/twin-symmetry.json b/main/opengeometry/tests/fixtures/cases/validity/twin-symmetry.json new file mode 100644 index 0000000..d95d5e9 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/twin-symmetry.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[0.0,0.0,-2.0]},{"direction":[0.0,1.0,0.0],"kind":"Line","origin":[2.0,0.0,-2.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[0.0,2.0,0.0]},{"direction":[0.0,0.0,-1.0],"kind":"Line","origin":[2.0,2.0,0.0]},{"direction":[1.0,0.0,0.0],"kind":"Line","origin":[0.0,2.0,-2.0]}],"intersections":[],"pcurves":[{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,0.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[2.0,0.0]},{"direction":[1.0,0.0],"kind":"Line2","origin":[0.0,2.0]},{"direction":[0.0,1.0],"kind":"Line2","origin":[0.0,0.0]}],"surfaces":[{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[1.0,-0.0,0.0],"z":[-0.0,-1.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,2.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[0.0,1.0,0.0],"y":[0.0,0.0,-1.0],"z":[-1.0,-0.0,-0.0]},"kind":"Plane"},{"frame":{"origin":[2.0,0.0,0.0],"x":[0.0,0.0,-1.0],"y":[-0.0,1.0,0.0],"z":[1.0,0.0,0.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[-0.0,1.0,0.0],"z":[-0.0,-0.0,1.0]},"kind":"Plane"},{"frame":{"origin":[0.0,0.0,-2.0],"x":[0.0,1.0,0.0],"y":[1.0,-0.0,0.0],"z":[0.0,0.0,-1.0]},"kind":"Plane"}]},"id":"validity-cuboid","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[{"cavity_shells":[],"outer_shell":0}],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":3,"id":0,"tolerance":1e-8,"twin_halfedge":16},{"chart_seam":false,"geometry":{"curve":1,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":0,"id":1,"tolerance":1e-8,"twin_halfedge":11},{"chart_seam":false,"geometry":{"curve":2,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":8,"id":2,"tolerance":1e-8,"twin_halfedge":19},{"chart_seam":false,"geometry":{"curve":3,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":2,"id":3,"tolerance":1e-8,"twin_halfedge":12},{"chart_seam":false,"geometry":{"curve":4,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":15,"id":4,"tolerance":1e-8,"twin_halfedge":17},{"chart_seam":false,"geometry":{"curve":5,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":1,"id":5,"tolerance":1e-8,"twin_halfedge":23},{"chart_seam":false,"geometry":{"curve":6,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":10,"id":6,"tolerance":1e-8,"twin_halfedge":20},{"chart_seam":false,"geometry":{"curve":7,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":13,"id":7,"tolerance":1e-8,"twin_halfedge":22},{"chart_seam":false,"geometry":{"curve":8,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":4,"id":8,"tolerance":1e-8,"twin_halfedge":18},{"chart_seam":false,"geometry":{"curve":9,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":7,"id":9,"tolerance":1e-8,"twin_halfedge":9},{"chart_seam":false,"geometry":{"curve":10,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":5,"id":10,"tolerance":1e-8,"twin_halfedge":14},{"chart_seam":false,"geometry":{"curve":11,"kind":"Curve","range":{"hi":2.0,"lo":0.0}},"halfedge":6,"id":11,"tolerance":1e-8,"twin_halfedge":21}],"faces":[{"id":0,"key":"bottom","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":0,"key":"bottom"}]},"sense":"Forward","shell_ref":0,"surface":0,"trim":{"chart":0,"holes":[],"outer":0,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":1,"key":"top","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":1,"key":"top"}]},"sense":"Forward","shell_ref":0,"surface":1,"trim":{"chart":0,"holes":[],"outer":1,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":2,"key":"left","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":2,"key":"left"}]},"sense":"Forward","shell_ref":0,"surface":2,"trim":{"chart":0,"holes":[],"outer":2,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":3,"key":"right","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":3,"key":"right"}]},"sense":"Forward","shell_ref":0,"surface":3,"trim":{"chart":0,"holes":[],"outer":3,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":4,"key":"front","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":4,"key":"front"}]},"sense":"Forward","shell_ref":0,"surface":4,"trim":{"chart":0,"holes":[],"outer":4,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}},{"id":5,"key":"back","provenance":{"reversed":false,"role":"Authored","sources":[{"body":"validity-cuboid","entity":"validity-cuboid","face":5,"key":"back"}]},"sense":"Forward","shell_ref":0,"surface":5,"trim":{"chart":0,"holes":[],"outer":5,"uv_bounds":[{"hi":2.00000001,"lo":-1e-8},{"hi":2.00000001,"lo":-1e-8}]}}],"halfedges":[{"edge":1,"face":0,"from":0,"geometry_use":{"pcurve":0,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":0,"next":1,"prev":3,"to":2,"twin":null,"wire_ref":null},{"edge":5,"face":0,"from":2,"geometry_use":{"pcurve":1,"periodic_lift":[0,0],"sense":"Forward"},"id":1,"loop_ref":0,"next":2,"prev":0,"to":3,"twin":23,"wire_ref":null},{"edge":3,"face":0,"from":3,"geometry_use":{"pcurve":2,"periodic_lift":[0,0],"sense":"Reverse"},"id":2,"loop_ref":0,"next":3,"prev":1,"to":1,"twin":12,"wire_ref":null},{"edge":0,"face":0,"from":1,"geometry_use":{"pcurve":3,"periodic_lift":[0,0],"sense":"Reverse"},"id":3,"loop_ref":0,"next":0,"prev":2,"to":0,"twin":16,"wire_ref":null},{"edge":8,"face":1,"from":4,"geometry_use":{"pcurve":4,"periodic_lift":[0,0],"sense":"Forward"},"id":4,"loop_ref":1,"next":5,"prev":7,"to":5,"twin":18,"wire_ref":null},{"edge":10,"face":1,"from":5,"geometry_use":{"pcurve":5,"periodic_lift":[0,0],"sense":"Forward"},"id":5,"loop_ref":1,"next":6,"prev":4,"to":7,"twin":14,"wire_ref":null},{"edge":11,"face":1,"from":7,"geometry_use":{"pcurve":6,"periodic_lift":[0,0],"sense":"Reverse"},"id":6,"loop_ref":1,"next":7,"prev":5,"to":6,"twin":21,"wire_ref":null},{"edge":9,"face":1,"from":6,"geometry_use":{"pcurve":7,"periodic_lift":[0,0],"sense":"Reverse"},"id":7,"loop_ref":1,"next":4,"prev":6,"to":4,"twin":9,"wire_ref":null},{"edge":2,"face":2,"from":0,"geometry_use":{"pcurve":8,"periodic_lift":[0,0],"sense":"Forward"},"id":8,"loop_ref":2,"next":9,"prev":11,"to":4,"twin":19,"wire_ref":null},{"edge":9,"face":2,"from":4,"geometry_use":{"pcurve":9,"periodic_lift":[0,0],"sense":"Forward"},"id":9,"loop_ref":2,"next":10,"prev":8,"to":6,"twin":7,"wire_ref":null},{"edge":6,"face":2,"from":6,"geometry_use":{"pcurve":10,"periodic_lift":[0,0],"sense":"Reverse"},"id":10,"loop_ref":2,"next":11,"prev":9,"to":2,"twin":20,"wire_ref":null},{"edge":1,"face":2,"from":2,"geometry_use":{"pcurve":11,"periodic_lift":[0,0],"sense":"Reverse"},"id":11,"loop_ref":2,"next":8,"prev":10,"to":0,"twin":0,"wire_ref":null},{"edge":3,"face":3,"from":1,"geometry_use":{"pcurve":12,"periodic_lift":[0,0],"sense":"Forward"},"id":12,"loop_ref":3,"next":13,"prev":15,"to":3,"twin":2,"wire_ref":null},{"edge":7,"face":3,"from":3,"geometry_use":{"pcurve":13,"periodic_lift":[0,0],"sense":"Forward"},"id":13,"loop_ref":3,"next":14,"prev":12,"to":7,"twin":22,"wire_ref":null},{"edge":10,"face":3,"from":7,"geometry_use":{"pcurve":14,"periodic_lift":[0,0],"sense":"Reverse"},"id":14,"loop_ref":3,"next":15,"prev":13,"to":5,"twin":5,"wire_ref":null},{"edge":4,"face":3,"from":5,"geometry_use":{"pcurve":15,"periodic_lift":[0,0],"sense":"Reverse"},"id":15,"loop_ref":3,"next":12,"prev":14,"to":1,"twin":17,"wire_ref":null},{"edge":0,"face":4,"from":0,"geometry_use":{"pcurve":16,"periodic_lift":[0,0],"sense":"Forward"},"id":16,"loop_ref":4,"next":17,"prev":19,"to":1,"twin":3,"wire_ref":null},{"edge":4,"face":4,"from":1,"geometry_use":{"pcurve":17,"periodic_lift":[0,0],"sense":"Forward"},"id":17,"loop_ref":4,"next":18,"prev":16,"to":5,"twin":15,"wire_ref":null},{"edge":8,"face":4,"from":5,"geometry_use":{"pcurve":18,"periodic_lift":[0,0],"sense":"Reverse"},"id":18,"loop_ref":4,"next":19,"prev":17,"to":4,"twin":4,"wire_ref":null},{"edge":2,"face":4,"from":4,"geometry_use":{"pcurve":19,"periodic_lift":[0,0],"sense":"Reverse"},"id":19,"loop_ref":4,"next":16,"prev":18,"to":0,"twin":8,"wire_ref":null},{"edge":6,"face":5,"from":2,"geometry_use":{"pcurve":20,"periodic_lift":[0,0],"sense":"Forward"},"id":20,"loop_ref":5,"next":21,"prev":23,"to":6,"twin":10,"wire_ref":null},{"edge":11,"face":5,"from":6,"geometry_use":{"pcurve":21,"periodic_lift":[0,0],"sense":"Forward"},"id":21,"loop_ref":5,"next":22,"prev":20,"to":7,"twin":6,"wire_ref":null},{"edge":7,"face":5,"from":7,"geometry_use":{"pcurve":22,"periodic_lift":[0,0],"sense":"Reverse"},"id":22,"loop_ref":5,"next":23,"prev":21,"to":3,"twin":13,"wire_ref":null},{"edge":5,"face":5,"from":3,"geometry_use":{"pcurve":23,"periodic_lift":[0,0],"sense":"Reverse"},"id":23,"loop_ref":5,"next":20,"prev":22,"to":2,"twin":1,"wire_ref":null}],"loops":[{"face_ref":0,"id":0,"is_hole":false,"start_halfedge":0},{"face_ref":1,"id":1,"is_hole":false,"start_halfedge":4},{"face_ref":2,"id":2,"is_hole":false,"start_halfedge":8},{"face_ref":3,"id":3,"is_hole":false,"start_halfedge":12},{"face_ref":4,"id":4,"is_hole":false,"start_halfedge":16},{"face_ref":5,"id":5,"is_hole":false,"start_halfedge":20}],"shells":[{"faces":[0,1,2,3,4,5],"id":0,"is_closed":true}],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[0.0,0.0,0.0],"tolerance":1e-8},{"id":1,"outgoing_halfedge":3,"position":[2.0,0.0,0.0],"tolerance":1e-8},{"id":2,"outgoing_halfedge":1,"position":[0.0,0.0,-2.0],"tolerance":1e-8},{"id":3,"outgoing_halfedge":2,"position":[2.0,0.0,-2.0],"tolerance":1e-8},{"id":4,"outgoing_halfedge":4,"position":[0.0,2.0,0.0],"tolerance":1e-8},{"id":5,"outgoing_halfedge":5,"position":[2.0,2.0,0.0],"tolerance":1e-8},{"id":6,"outgoing_halfedge":7,"position":[0.0,2.0,-2.0],"tolerance":1e-8},{"id":7,"outgoing_halfedge":6,"position":[2.0,2.0,-2.0],"tolerance":1e-8}],"wires":[]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/twin-symmetry.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/twin-symmetry.verdict.json new file mode 100644 index 0000000..9e6ad93 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/twin-symmetry.verdict.json @@ -0,0 +1 @@ +{"error":{"InvalidTopology":"inconsistent halfedge twins"},"valid":false} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/wire-membership.json b/main/opengeometry/tests/fixtures/cases/validity/wire-membership.json new file mode 100644 index 0000000..96e833d --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/wire-membership.json @@ -0,0 +1 @@ +{"accuracy":{"exchange":1e-6,"geometric":1e-8,"intersection":1e-9,"tessellation":0.01},"geometry":{"curves":[{"frame":{"origin":[0.0,0.0,0.0],"x":[1.0,0.0,0.0],"y":[0.0,0.0,-1.0],"z":[0.0,1.0,0.0]},"kind":"Circle","radius":1.0}],"intersections":[],"pcurves":[],"surfaces":[]},"id":"validity-circle","quality":{"kind":"Analytic"},"revision":0,"schema_version":2,"solids":[],"topology":{"edges":[{"chart_seam":false,"geometry":{"curve":0,"kind":"Curve","range":{"hi":6.283185307179586,"lo":0.0}},"halfedge":0,"id":0,"tolerance":1e-8,"twin_halfedge":null}],"faces":[],"halfedges":[{"edge":0,"face":null,"from":0,"geometry_use":{"pcurve":null,"periodic_lift":[0,0],"sense":"Forward"},"id":0,"loop_ref":null,"next":0,"prev":0,"to":0,"twin":null,"wire_ref":null}],"loops":[],"shells":[],"vertices":[{"id":0,"outgoing_halfedge":0,"position":[1.0,0.0,0.0],"tolerance":1e-8}],"wires":[{"id":0,"is_closed":true,"start_halfedge":0}]}} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/cases/validity/wire-membership.verdict.json b/main/opengeometry/tests/fixtures/cases/validity/wire-membership.verdict.json new file mode 100644 index 0000000..22da2f6 --- /dev/null +++ b/main/opengeometry/tests/fixtures/cases/validity/wire-membership.verdict.json @@ -0,0 +1 @@ +{"error":{"InvalidTopology":"halfedge must belong to one face or wire"},"valid":false} \ No newline at end of file diff --git a/main/opengeometry/tests/fixtures/golden/TARGETS.md b/main/opengeometry/tests/fixtures/golden/TARGETS.md new file mode 100644 index 0000000..a5f44a7 --- /dev/null +++ b/main/opengeometry/tests/fixtures/golden/TARGETS.md @@ -0,0 +1,84 @@ +# Golden targets + +Each `.json` maps every golden scene key to the SHA-256 of its text +(`sweep-3d` excepted, see below), sorted, one key per line. The `golden` test binary +(`tests/golden/`) builds every scene, hashes it and, on a recorded target off CI, fails +on any key that is missing from the file, any key in the file with no scene, and any key +whose hash differs. The texts of the differing scenes are written under +`target/tmp/golden//`. + +The file stem is the target triple with `_` written as `-`, because file names in the +tree are kebab-case. + +## Recorded targets + +| Target | File | How it was produced | +| --- | --- | --- | +| aarch64-apple-darwin | `aarch64-apple-darwin.json` | `OG_GOLDEN_UPDATE=1 cargo test --offline --test golden` from `opengeometry/`, debug profile, rustc 1.88.0 (6b00bc388 2025-06-23), macOS 26.4.1 (25E253), Apple silicon | + +The comparison runs only on a recorded target and never on CI (where `CI` is `true`). On +CI, and on a target with no golden file, the test still builds every scene and applies +the corpus checks, and compares no hash; with `-- --nocapture` it prints which it did. +On CI a boolean matrix row that differs from its stored result is not a failure either. +The golden file was recorded on the macOS version the table names; a run on another macOS +version can differ if its math library rounds differently (the first of the 'Known +sources of difference between targets'). + +## Recording + +Only `OG_GOLDEN_UPDATE=1` writes a golden file, and never on CI: there the variable is +ignored. Review the diff of the recorded file before committing it. + +`OG_GOLDEN_DUMP=` (used by `npm run snapshot:kernel -- `) writes the record +text of every case and the three handler listings into an empty directory, skips the +golden comparison and still applies the corpus checks. + +## Debug profile only + +The goldens are recorded and compared in the debug profile. Release builds let the +compiler fuse and reorder floating-point work, which moves low bits; at the time of +recording, 82 of the 461 cases differ between debug and release. On a recorded target, +off CI, a release run of the golden binary fails by design. + +## Known sources of difference between targets + +- The platform math library: `sin`, `cos`, `atan2`, `sqrt`-based helpers and `powf` + may round differently on another libm, which moves curved-surface tessellation, + classification grids and STEP reals. +- Release-mode fusion (see above). +- Architecture-specific code generation for fused multiply-add. + +## Not recorded + +wasm32 is not recorded. It waits for the embedded fixtures of the deferred wasm comparison +work: the golden test reads the boolean matrix rows, the three batch-matrix fallback rows +and the kernel's boolean handler source files from disk, which a wasm test run cannot do. + +No other native target is recorded. x86-64 Linux is left out on purpose: CI, which runs on +it and on macOS, does not compare. + +## What the goldens carry + +- Every case of the corpus (461): builders, profiles, the boolean matrix (its rows read + from `tests/fixtures/cases/boolean-matrix`) and the five single-body boolean cases + (`booleans.fixture.*`, built in code in `tests/golden/boolean_fixtures.rs`; they read no + file), the cylinder, sphere, direct, planar, oblique and curved booleans, the + batch and staged batch subtractions, shells, creating operations and the world graph + session, operate and scenario cases. +- The three handler listings: `handlers-expected`, `handlers-covered`, + `handlers-uncovered`. +- `sweep-3d`: the non-coplanar rectangular sweep (BRep JSON, tessellation at 0.01, + three classify verdicts, the STEP text and report), hashed as the earlier single + golden did, so its value continues the earlier `aarch64-apple-darwin` hash. +- `fallback.`: the three batch-matrix fallback rows run through + `multi_tool_boolean` (result or error, handlers, tessellation, classification, STEP + in metre and millimetre). +- `level.step..`: the acceptance level exported as STEP in metre and + millimetre with Y and Z up (text and report). + +## Adding a target + +A further target needs its own `include_str!` line in the `COMMITTED` table of +`tests/golden/golden_file.rs`, a recorded `.json`, a row in the table above, and a +list in this file of every key whose hash differs from the aarch64-apple-darwin file, with +the reason. diff --git a/main/opengeometry/tests/fixtures/golden/aarch64-apple-darwin.json b/main/opengeometry/tests/fixtures/golden/aarch64-apple-darwin.json new file mode 100644 index 0000000..d43f260 --- /dev/null +++ b/main/opengeometry/tests/fixtures/golden/aarch64-apple-darwin.json @@ -0,0 +1,474 @@ +{ + "batch.planar.fifty-openings": "1db67754b6fee8e097ffa47e2e1153fef991c7bbdb1fe77aa077b1cb67757649", + "batch.planar.no-cutters": "c163eea9171b643beb8270902042670d68e3593d02327572ee5eda58687607c3", + "batch.planar.oblique-cutters": "c1ce10630b20040ea69096c16ac21988c93b53900e67501217c63f175e387c72", + "batch.planar.overlapping-cutters": "71eb369abc8614bf594081eab487749ed3ed672df52858c36b7cbcedabdb16f1", + "batch.planar.single-cutter": "84343315d4fde9db3019eed77e22fa4ca83d598adcb94f7adcafc476109ada73", + "batch.planar.single-oblique": "f6bd9e2b2e8a5da489b44aaa4cc98d22920f62cd94ce93b08ea25e99a8d41c82", + "batch.planar.ten-flush-openings": "f874dd6524af534618e8a28ed4342d7eba2e1990b7bfbb9b8615baafe5ac167d", + "batch.planar.too-many-cutters": "b190fbccd64c29061ca65dcaae025a1c5b1515616e306ec42e2602dfa8669dc0", + "batch.planar.two-cuboids": "292812dbe36278abd55f2fca551bca6e9ef074a8c49deac87159bad95bb226fe", + "batch.prismatic.arched": "cde2f4728f9c5996f6754428bf15d9a4cd6865511103b90fb16afb1ae0d2509b", + "batch.prismatic.disjoint-mixed.forward": "8be8343669937638228de09fca4a893a2c187fd0e1e0d4b446b55714587fb013", + "batch.prismatic.disjoint-mixed.reverse": "fc16bd4adf8c273f56313c7518807de6fae480d5e87b17fd435c062bfadb9c48", + "batch.prismatic.flush-mixed.forward": "8e808bb152040c501a6124e645e773c5471455ce554228f8bede75fbb94555fa", + "batch.prismatic.flush-mixed.reverse": "cc29ef7a6b8a10514fc26a40d0b453ed29bdbb716751aff734b96d503a1e118a", + "batch.prismatic.single-round": "2b75beb984306c9c4bf3932df8600d4813d2b46d71f3db44824cc1fa1ad95944", + "batch.prismatic.split-mixed.forward": "b3889c236530aee1b1f6b4983f9f7b33a26e172a563f369fee5e2bfd7afee0dc", + "batch.prismatic.split-mixed.reverse": "e7023b2522260c9897fe2952595ee0bc2c0f5d1ab67642b22457112b80da914c", + "batch.prismatic.two-round.forward": "a38f3b8bbe3223d75f13419040af4b0c98aba1cfb19278b1d150b64ec1dabfa9", + "batch.prismatic.two-round.reverse": "6c7001b351fb9a50f033a32762eda9660c4577ee5d9e4a3ed7a4ea0f48fc9997", + "batch.staged.mixed-curved-openings.forward": "dceff75a9e987d86a8a49c58e2b79751449a7b92757ae6ab0fcc1586244dc72d", + "batch.staged.mixed-curved-openings.reverse": "36250e85bab66490cfd4d7ba6d43dcc2bddc27ad014a936abc523be05e859a8b", + "batch.staged.oblique-cylinder-stage": "cbd61e33df8f75b4290b9627166a1ca2a5322db35373303cdd238fdf4e6568b3", + "batch.staged.oblique-planar-stage": "5c9308f770c26b491e8dcbe87af392db96ab07fcf7106638253bd09bdf96bf14", + "batch.staged.overlapping-oblique-planar-stage": "c5370747dd052dcfa1c48297458a0b8e23029b0af15a8dfa7bab1621adb49e44", + "batch.staged.overlapping-planar-cutters": "d569891056eeb84ca22c08fcd19bbe74019b5457e5da24ee472421de122798f0", + "batch.staged.pitched-planar-stage": "d836bc45e2782208a14afd2a549930daf0f65859aadc2978454282f84741a442", + "batch.staged.skew-planar-stage": "82d65e7cc11abb941de55d728c7d5823f0b6fc9e1e93be37254fde6a099e497b", + "batch.vertical-arc.arc-host": "b9039f344c19933738755291392766d217f2fe262d5ab84f14df6a47ad75f444", + "batch.vertical-arc.wide-opening": "7e553aec2698e0616a0145460d1968c2cd2b3e6d520cfbdf462732d1b463b36b", + "booleans.boxes-direct.aligned.intersection": "d106f0de05faa7c01f7d28a4b165e0e1fede6c9ed88bb914a0f6df0d6b0184de", + "booleans.boxes-direct.aligned.subtraction": "ca949b9944d9907b521139cd9d5f8b65e99e7f458e8006f6dcc6f394f90ef704", + "booleans.boxes-direct.aligned.union": "1a534db5a6c5877d3a30f15f82c032b3de46eeb8da43d5c1363907d5978d156c", + "booleans.boxes-direct.cavity": "2c675354f47d330689c75f8a6b8ab8082ca0595bfe5776646cb435ead4db1954", + "booleans.boxes-direct.contact-corner.intersection": "d6518bba0435261d2ebbace66ab79d91d9ac960a3c38d338ad9955b4a5bf3a46", + "booleans.boxes-direct.contact-corner.union": "1e81e570f91df62faf13853a89cdc25145c6205d61ca18d0308fbe9f20d0119f", + "booleans.boxes-direct.contact-edge.intersection": "bb5457bc7712e5398e3a05bbfa960877c68d8bc0277f96355c9ea23a99a35400", + "booleans.boxes-direct.contact-edge.union": "3accf0a0114775b8c37beac50b42dba6e254f323f94cf7d2743ce3bf25f87e0f", + "booleans.boxes-direct.contact-face.intersection": "1c0c9113858ff946749b34ef4f1da3de5b03c9e747a73a864fa696417183abec", + "booleans.boxes-direct.contact-face.union": "5bd0dea6a84e623491d8d6fa871ec101cb6ce99c9a9a85fb406a91083ff697dc", + "booleans.boxes-direct.different-axes": "4be9094f36d7e7845527b23fb2a77b9d442b2ec7039296c28c548b0914e341c9", + "booleans.boxes-direct.placed-similarity": "899afe6c3c6d5562a671311a2b79a62fa30598bd4daedc5d69a17b88060541b4", + "booleans.boxes-direct.scaled-large": "7522e285caaf1667f3c59a13343eeea5308bee4bf5c66df0656e2369ae9be981", + "booleans.boxes-direct.scaled-tiny": "d8365146d04eb326334281ae95a0c600fc9e6ce8aed7f4518fbb13778ae4a637", + "booleans.boxes-direct.scaled-unit": "e76106ae0bc1f0b04e9ea4185f6c20ae5cb2ded85b9b1b831d8af892c15a25fa", + "booleans.boxes-direct.through": "6323f0a5c16507ad33cee86dca82af5dc31028e7aedd507ffc118f158a94fb86", + "booleans.boxes-direct.unresolved-gap": "90c1052b98f70d484ac0704edc18ef5b1272678f35205a37ffc1e60ff109ad1c", + "booleans.boxes.identity.intersection": "e3447e918dc6a156785b8af0517421a1d825b6c9cfe854df2e2b8c3f4b1579fc", + "booleans.boxes.identity.subtraction": "8dcb9176abc65233a533c71e227b4cc61037361d9b4bb854d0c8609aebb0885f", + "booleans.boxes.identity.union": "955560bb170ec6985b3e1566019d58e8b7f58fee343cd8af3772c80b0ee71a4e", + "booleans.coincident.annular-subtraction": "92652b4c46ed79857ab01400d2c039ef8419cd3abef135dc0682bcf6e01d3ba1", + "booleans.coincident.annular-union": "6d4247da87eb322b591c60f11757c661864c08c1feeeae35b96c574a27428660", + "booleans.conic-containment.frustum-minus-inner-box": "3770e1602d0f2b843e61c7a868b53b51da39cf5b8bf21ff5a854c3c007accd4b", + "booleans.conic-containment.frustum-minus-inner-cylinder": "476497ef1480968484ceb96ca6ab69aa7a232b2d70d6fcd050016941708c6bed", + "booleans.conic-containment.frustum-minus-inner-sphere": "f408219144048e958c8b1a72a225d0380b7fa3724c94f5fab5e2d24a6df64978", + "booleans.conic-containment.outer-box-minus-frustum": "68852d7ea1517cc693b71a3aa134991aea46cef11be9b87e574ffc382f5d3333", + "booleans.conic-containment.outer-cylinder-minus-frustum": "d161bdb9402ffbd7dfeec61cfc1d8955102b0bbc9e8cb11e2b2b4ab2206395e5", + "booleans.conic-containment.outer-sphere-minus-frustum": "7441cb441bc6a7bd571b9ba1da84ba5d32d7b0e2b859e0611ea28dbca0fd2b6b", + "booleans.conic.coaxial.intersection": "4650c92e3a6b1979ed9f3382a454aff7d1e3a8888fe97426bf8b53c44c966014", + "booleans.conic.coaxial.subtraction": "2a59d4fd0614743963cec6c19cafca1a39c6f266561212748e9a41fb71cb8740", + "booleans.conic.coaxial.union": "1cffcc050ed9b30973aead7ccac6f32a893337307e780838c9beee74228e32d7", + "booleans.conic.touching": "7777980bc9ee161ec5fee4a951350a279e7915810d8e7595d15159edba0e340e", + "booleans.cuboid-containment.box-in-cylinder": "606cdb3d64f3b7ccbb219f5348ef196112a5f55773ee7bdbb54433d25e59ab6d", + "booleans.cuboid-containment.box-in-sphere": "17cc827a3416aff0babb8ed5988acabbc03598dec73daef4b3bcba15dab61206", + "booleans.cuboid-containment.crossing-union": "65e207a193d20fd1096a8afeb1de3eb95e54d82f7f1d3007678cd54e9ecf562c", + "booleans.cuboid-containment.cylinder-in-box": "702f0c9b509096ff5e85d78a24f136e45cc37763d8e665907b021ec8439ab4c6", + "booleans.cuboid-containment.sphere-in-box": "b7655a7257032cb9c51387863a92c5811ca1bc968e3d90bc0874d39f3d7ff8f3", + "booleans.curved.arc-host-chained": "9422c77d194134677081204260202a92e5b8279b3d2f7978eeadbecb0fb3aa1e", + "booleans.curved.arc-host-opening": "0aba85e2de2e2436d4fe6520b4c56a33eb7c6bbcf5ba17f70fd193c6d0903d28", + "booleans.curved.arc-host-third": "75faf7c0f9e668048ee580af65acfe8b5a6efa156679306c042a408c68afab52", + "booleans.curved.internal-cavity": "197232ccb7f45991e3f90b3c384fcba28fa0ff078527633ba6bd4a2a1c1178ed", + "booleans.curved.ring-sector": "1ff76f7747fc3959281edbeac402e1de04d73a0e3be67cb8ddaa25c810582740", + "booleans.curved.ring-sector-chained": "9b75bc42ff68c8ec43887a6530aa0809f55276b79054b27fa0f3459d2be99127", + "booleans.curved.same-component-cavities": "ee3361a31d225eb6d2eb6b545bf67146df1f315cf443db022a1c77297d4862ba", + "booleans.curved.split-arc-provenance": "71a0d74139551d69c678a2ffc483ab063392e76093298ce693ab46964fd62d1a", + "booleans.curved.split-cap-first": "228c21872afd698c5fd9385716d8b750f14664d0baed31dd5f53cb0f6da06c74", + "booleans.curved.split-cap-second": "3bb10a4226d81955e32d09c76046c585a7ac873b1a1cfb69e52449153ca8c4fb", + "booleans.curved.split-sector": "7f6210cb1be00982e346dfbcbd31da8c96837cf92d6e314ddebce4fa3f3ed90a", + "booleans.curved.split-with-cavity": "0dd51bd15297ba82a1ee81258a871229f7bc54f0ceb3f987f2e2d642762fd147", + "booleans.curved.two-cavities": "607778a6d7da6564b29f7bfb091267d4ceda0c32124c4c0bd3fc7bdbf44059ec", + "booleans.cylinder-box.pocket-0-high.intersection": "0bbd4ddcf3290c753c76d0bf81b45451cb91b457a52a9348cf5dac04d3e3326e", + "booleans.cylinder-box.pocket-0-high.reversed-subtraction": "726b4fd468f124b87c17eead08748e5a707ad90b1691be868b336b921eae071d", + "booleans.cylinder-box.pocket-0-high.subtraction": "e526b5ea7442c0309c70bb1eb93dc68665c6556f806784a33c6330fa8587e663", + "booleans.cylinder-box.pocket-0-high.union": "bb9f9787544d3fc0e0a653aabaf34a100dad9028d9ed272b8b90bb947f8823cd", + "booleans.cylinder-box.pocket-0-low.intersection": "9622a1db80e7f29783b0351fed373a9ac097f9434831b7abc8d70503f229792d", + "booleans.cylinder-box.pocket-0-low.reversed-subtraction": "60a06b3cfd25234f58b816e2f94f15d1610be640d2e001f1810f02169e4dd123", + "booleans.cylinder-box.pocket-0-low.subtraction": "e01bfb7acd57fc2ebd41ca35e812f39ea88f993243d1e3c996a0872b55d03a56", + "booleans.cylinder-box.pocket-0-low.union": "911a21f5bb873d7ff67f81aba5c726fc25f201da0938fef69a1b28c4a2b75a19", + "booleans.cylinder-box.pocket-1-high.intersection": "ffc0021795312d5a63f93dff1a91e95c732526e33db0135b3e996db914eab846", + "booleans.cylinder-box.pocket-1-high.reversed-subtraction": "0ba6f8cfa08285f257ef0c50d315d649c9557910d3f2a64c547eb8b235dc7a32", + "booleans.cylinder-box.pocket-1-high.subtraction": "f603b4a7b695537569ff35c4af1590bc4dd05be38d7d7a4cfee21967ffa0d8af", + "booleans.cylinder-box.pocket-1-high.union": "b74795d43a8e3f59b764dd8f1fd9c3471d84afec3e28a0e4c8df292e08c48a12", + "booleans.cylinder-box.pocket-1-low.intersection": "60bba24bc444b9d6d0a5a6b5bfcc632c0fe9f6837d66a43c9cd93be718076d29", + "booleans.cylinder-box.pocket-1-low.reversed-subtraction": "605f9e1cc65fbde244bd8cba5eb16e72dae30ba5269bc4efb7b21dd6cbb63173", + "booleans.cylinder-box.pocket-1-low.subtraction": "48ce411f9407c0f70bab3d17955b7dc33650b5fbed27bc77a74d21b5ac20a47f", + "booleans.cylinder-box.pocket-1-low.union": "caa1d127b8392ec62285b3b950cecc5467cbf46e526291f5c7de5f046340d09d", + "booleans.cylinder-box.pocket-2-high.intersection": "29454e9b45bdf7534e29e7ac1b57304774e44a2ef5f98699d925c12dbbaadcf0", + "booleans.cylinder-box.pocket-2-high.reversed-subtraction": "3b8535e7289f7caf56fd4bcb535b33d75f07f0c602505af130470b7b4af31c6e", + "booleans.cylinder-box.pocket-2-high.subtraction": "0ff89b5bf54dec823868efa936496a86ea8bae65d45aa402f4e144ee19833fe0", + "booleans.cylinder-box.pocket-2-high.union": "2711fa6222eca6fedd154411170b567951f0e92f2f461bf027fe0fe4c29b38a7", + "booleans.cylinder-box.pocket-2-low.intersection": "dd4c267d312fc8f595555f1ee247f563933221e566094979c471facfc168d63b", + "booleans.cylinder-box.pocket-2-low.reversed-subtraction": "c4ce37738d535d2e12d994fb1877731a092368d09de08ded98e09396059b68ce", + "booleans.cylinder-box.pocket-2-low.subtraction": "9b7b5c8930f48aa57d84a77d41d021e8056db352d5762a7a7874547b27ca1ab7", + "booleans.cylinder-box.pocket-2-low.union": "d8016a98766f36fc63ece494c888c5c38c1f3d9f845b8bcaab166a730e49e9e0", + "booleans.cylinder-box.through-0.intersection": "f33a4b2f37c6b242844c5e258ac5f285d47fc9891c91c8dd8366a858c147d4d8", + "booleans.cylinder-box.through-0.reversed-subtraction": "407fb6eba3191ce83acd1d83a08fc1f9fad3e7ff6e67aecdc85d8fbbfc625e9d", + "booleans.cylinder-box.through-0.subtraction": "21d438ec5a93648269b13ba9a02ce307d4f0c19743d87ad86001f5bf1d5a3f26", + "booleans.cylinder-box.through-0.union": "cc231935f30e687caae4ea11a84806dd20f55f75605f2ba69d0eec8b54b14db2", + "booleans.cylinder-box.through-1.intersection": "eb1b511fb3f6b5d8968e823df6555e6fa51b04a33245ee4b97641935997d42c7", + "booleans.cylinder-box.through-1.reversed-subtraction": "57b2152a1f7670c6422a212a398182bc3d31dc89d1c21301870b6a5eedd47f32", + "booleans.cylinder-box.through-1.subtraction": "cf113ddcd15115c33f831c1d17dd2f6b6cec83541253bd6654c698c1799989f3", + "booleans.cylinder-box.through-1.union": "df0725309154d086cb8c34838e4a44af3434d2a53db562efbcf0ff4b73b293d4", + "booleans.cylinder-box.through-2.intersection": "9f9de79ea9fb5194ca45716cd0868928263f90f7b8d528644809117a879389c2", + "booleans.cylinder-box.through-2.reversed-subtraction": "54421c87c3ea641c2c49887bdcc4eed99e52688d4044baa63e53d44323336d1a", + "booleans.cylinder-box.through-2.subtraction": "ac32478bc39158c1849775dc25aeb52dcf3d8d7a2ee1b266f0ff5c1514c7ab27", + "booleans.cylinder-box.through-2.union": "e7831a6359ac214169c90b092391cd596976132136efe0dc56ca5acf29930d1c", + "booleans.cylinders.coaxial-contact.intersection": "c2517ccc637b639e57c1c84cbf6bb3b3d083e32305ee650ebb6cd9d59da9ff3c", + "booleans.cylinders.coaxial-contact.subtraction": "f8f6cf574d77ce1562f6a553b66bf1be17391fd39a663893107e17555e23b9a7", + "booleans.cylinders.coaxial-contact.union": "813fcbc022690435901ab93f801a52fa58b61481348955ca1fec5a43c09e306e", + "booleans.cylinders.coaxial-near-gap": "ad2d592db4b4b14c938e5af890279aed13ac0c6f6a605424797c6da2a18600cb", + "booleans.cylinders.coaxial-split.intersection": "1e6ead1a9c4091c56676a72b3014a35ff963e9497769480b86a9a24650ff3e6b", + "booleans.cylinders.coaxial-split.subtraction": "54d294f12a073d607893db8f423c2558c19bdbc314437f9b65f5e1bedd963d59", + "booleans.cylinders.coaxial-split.union": "e94ec9b4d09d604fbe7cee6ba0c0d242708915650f805f919881f6880cc81a8a", + "booleans.cylinders.coextensive-shifted": "cf12ed2434fd1dcb2ada3bd52279f7fc1c87e9f8ed320de4b99c7152b5277960", + "booleans.cylinders.coextensive.intersection": "280ade065a274877720860d1812b00fa3ceb944797aadb043ed5bb370ab2f1d9", + "booleans.cylinders.coextensive.reversed-subtraction": "562b6a1692a53d65a337e32ff5024bc814f1c3c32afa0f78eaed0f2545d76240", + "booleans.cylinders.coextensive.subtraction": "68f319ff1f873c07fc2c92ab45e268dc3dcec899df00c5422c2174c2f98a00e4", + "booleans.cylinders.coextensive.union": "7001d168c279ccb753ee838438be1efb4ca977ae68dad27dbb505f91cbc6b72a", + "booleans.cylinders.diagonal": "04111b7b792592604690a7b6b837c32f5e90c07166b127dc4e1d14ef69e5e721", + "booleans.cylinders.eccentric-touching": "f40be3e2eef2e3baa747ccc1fddce304f00080e1ce8c87c116e5ce24829c569d", + "booleans.cylinders.eccentric.intersection": "dabee2d5963998de6ce3314b4b2407febac5e6f5f656783d1ef9a00a36d3eee4", + "booleans.cylinders.eccentric.subtraction": "49aab484d96f417a85f06137508650ec929f1de7fda56617addc2e7fa1821c1d", + "booleans.cylinders.eccentric.union": "60debece0f1ba65258ac406df5a1c1ff352f136f0199caa044e0fb216942da78", + "booleans.cylinders.enclosed.intersection": "6f9de09114f3fa9795206e5ab0d7eae03b396bc79a2f0297fc392af8b9219498", + "booleans.cylinders.enclosed.reversed-subtraction": "27ac02df5232d8cd3e738e722a45894a61edfb5804a58e2185839f1321cc57f3", + "booleans.cylinders.enclosed.subtraction": "154cbb4e66893709c4f1143de8695983c6676b6f946fd4fd61627a49e59fbd24", + "booleans.cylinders.enclosed.union": "a33956cc2755a2997a49624967d0a870898f57363ae1f201245a9f184bdcaa1e", + "booleans.cylinders.near-tangent": "1ed3d71365650403d9681ddf2fa3ac4ff20abc56eaac43e3dde08c44443d5efe", + "booleans.cylinders.nonparallel-through-cut": "f3cb827fc5ac6048309fa4fe5c68e5fefc3336bafa447c46125ba89db2920f36", + "booleans.cylinders.partial-span.intersection": "8c5d057d331f6a1c0df4a854e481155863ba5ea067f377daf09172b9fa705d17", + "booleans.cylinders.partial-span.reversed-union": "66afd0f10a284a115735e795486b414d9483fc672dd1d8d1f161d8f5fd3ba011", + "booleans.cylinders.partial-span.subtraction": "72f1b6c69199384b0851aa4fc278b8f6af0bfe62d408a0ac7bfda2c801bac8ca", + "booleans.cylinders.partial-span.union": "651c826c9174477714eceeb07ebf65b198396d01cab14367616d9da963836a2c", + "booleans.cylinders.partial-through": "9b34fd38b316c65f6f5252556f47d7528c8ea63e56fab6bb9a887096c1eb2adf", + "booleans.cylinders.placed-roundoff.intersection": "1f90bcc8724e1e5e63a777cfbc6f823f40a401f347452268f65920f5c1d3e393", + "booleans.cylinders.placed-roundoff.subtraction": "e471bc9c33bab7747ec4dcde7b445faceb2280cffe34e9cf1f7d6e0652f4cda5", + "booleans.cylinders.placed-roundoff.union": "11f1badeafc14ca0c450aaf6d381e5ebcc99b7b3e177bcce7030b2bdfb5ea502", + "booleans.cylinders.tangent.intersection": "ae46aacc5130c1d6894ec7a77b3cd83842d461e1eadd3cbbe8fddea3e177de62", + "booleans.cylinders.tangent.subtraction": "7d18d487547df4d970db7d5226381f7f5151a39de0a2be99fc92c0bb736e1459", + "booleans.cylinders.tangent.union": "6b1479a6e676d6f38d0bb788345522ee38bcd03871150149f8b6ed67d332817f", + "booleans.cylinders.translated-y-split": "6ca60480f8bd13562cee8daead9a1d2c779968860338f04fdf1c86dc828de839", + "booleans.cylinders.transverse-reversed": "109eb7526f8997857f7c78c514031f697844784c0f13131cb104a2d89f1a354a", + "booleans.cylinders.transverse-shifted": "b4937b265586293a5c384c1d70f2f7aa5e19a1d36c66cbab6e2d88a683f2148d", + "booleans.cylinders.transverse.intersection": "174dc4c488f28b33cc4cc0b9f3a89b7e8520b63678557a4d92c9bc7f014614e4", + "booleans.cylinders.transverse.subtraction": "6951da9857178b18051e19c1d80599790cbac1fe9a0ceed25330fe7c5a64cdcc", + "booleans.cylinders.transverse.union": "64e8a5e7eb6c0b4fe8eebce2321ba2f432cd84a27656e59c585cf41092e83e4a", + "booleans.fixture.box-cavity": "d839c45ecedad0f36a8c5957bd68a1ae4b981a93742e19a4c748d04eec258e00", + "booleans.fixture.box-cut": "7b2e00165adedba1612d7e28fa70fc75f865f12eb4cbb74944a600726bb9e4e6", + "booleans.fixture.box-intersection": "c8d40783b3620e57bec528dc0fbf34bc8f98cc9074268dba35ded48c2db31f49", + "booleans.fixture.box-union": "4f0f576d162893e3413fdd8336f246ed77ab25e091444cc57790f6d09d7a0a13", + "booleans.fixture.sphere-cut": "cfa75c7299b18cf889600248d9da5f488219e4cee513ade8a2bbd8382b43aab3", + "booleans.generic.noncanonical-planar-containment.intersection": "e4a3b70eecea8ca951d9ebcad60523a79dccc10e87408088dd9948eb28b34e94", + "booleans.generic.noncanonical-planar-containment.subtraction": "a92b98bdf7d4a331a683f2d060dc62e979fd83cd826635281645d89ed908cb3b", + "booleans.generic.noncanonical-planar-containment.union": "28a9a6eae8d2e429c68324e988ec9c2ff280196978bbad391678416b5a5940fa", + "booleans.generic.periodic-winding": "20b62fcd0d68ceeb366d93f06c7d42ad7864964b55f74d3459178a2b98a0e4cf", + "booleans.generic.sphere-cone.intersection": "e42cd3964e9337a69211324093c3853ce4b2b99a38ba69d858dcd073337dcf4f", + "booleans.generic.sphere-cone.subtraction": "d6aab3af97a0742265d08c3582d12240f62d3167408ad6f49b4d18dc3cb0e66d", + "booleans.generic.sphere-cone.union": "eea9039b06b3435b2ee2ccaf0d32f48fd1b0595b12b3a2c6b5e2679ce409d103", + "booleans.matrix.annular-first": "6bb979fca74453bc7b3dcad005f8252ad6a3bdde9eec82f49263ff65b43dd861", + "booleans.matrix.box-intersection": "a577bebc8cf5d95449144b90df7fc2aee9da38d5f61944a392f1c4cca6d313bf", + "booleans.matrix.box-round": "73079a415c1e7606a3518c2554ca3ce3e5251b83d24a26fdee9aeb79fad6120a", + "booleans.matrix.box-subtract": "08dce1dadc92d4b2ed0a0be12cf34682ede170ac52cf1b613f77930e84535823", + "booleans.matrix.box-union": "171ab96f88fdb62c6a71cc4fa9dbb4b68e5f034f08678b4324f5d8dcca3997f4", + "booleans.matrix.cone-contained": "593bbb4fb812d1e079f14e359d900af27887032f3851af517caae6162895d312", + "booleans.matrix.cylinder-coaxial": "8a85249379f321fc82ec2b1999cdcf3e3d81a9f7af0d28dab0caefdc11206612", + "booleans.matrix.cylinder-cross": "ddc424b39b2a7e5af1cdb542036162279585318776e84e17de743a7acf46a698", + "booleans.matrix.cylinder-empty": "fdaa637fce1424ef2c622f81b24378421ac6c6c78f25a0cbecfd17767a0ebec7", + "booleans.matrix.cylinder-offset": "2bece3f5d28c91bc13626a160cad47075847e632d0c7fafb6f0adb7b2ff8a40e", + "booleans.matrix.cylinder-sliced": "c73e888640abff0440384345dfd94a7813568049bcc8f25aa18c38aa15311753", + "booleans.matrix.planar-extrusions": "f5ae62f54dabdf2fe517aa17868e5d8032e81b82ff6d965ccd482f6c1a6941b3", + "booleans.matrix.rotated-box-intersection": "8ff89e8fb45061169615439444fd8bcba05fcce79b9321ec752ddd1b8deee21f", + "booleans.matrix.rotated-box-union": "4515ec1c8edc2d0bd509d91839f33f955015ef9107e2718682fe2c57000e3489", + "booleans.matrix.sphere-contained": "a9d9eb41303dea0242df3198cc0a598796004310c3dc18cc91ac1c9ad3854cea", + "booleans.matrix.torus-contained": "ca00d042068d6a915a4749bfe33622b7301144e8016ca3955a3a20c783cf1f6e", + "booleans.matrix.wall-rectilinear": "fbb432d9fc8c9335007280f90135ac2c205bca5fbe0fa2a8356bc613ecaa1274", + "booleans.matrix.wall-round": "2a625dbac9cd93b0682064e1ce8e380f807a7ddc12f56e0125bf9614d9950a7a", + "booleans.oblique.after-cavity": "861b52027297df3d4470b41f7b50309398a2864d7ba88c4f380b3c02a65f03fa", + "booleans.oblique.chained": "2a54f4df1d6a5f250dad1bd1175b2e67a19f0cdece8d30530c565981e9bf547f", + "booleans.oblique.cutter": "fa50e43be7e4c797f6910d25cd2ec8ec0acf98a184270d9cb8f1a0e1efb0dc90", + "booleans.oblique.disconnected-host": "cb2c7f9bf0e37f76b99527a4caaf4a2e02bcc51780a92e7bb7444d74aee99910", + "booleans.oblique.nonconvex-cutter": "3cb2586d04734f4064c18f5676498b34b470f6138b3cd12a19d24533bd95daad", + "booleans.oblique.profile-hole-host": "d77136a83fbc24112dc6f5c8592f53bdd86301f7619f6746d4611be83010ec30", + "booleans.oblique.remote-cavity": "39ce0f127c8b3db5e6b8d0b36a2b20eeafaf3541e630ac622c81777a036284da", + "booleans.oblique.tilt-0-minus-15": "b96919d294c92daf55fd420b3508139eacb99f61be625e134155095b6730a0f9", + "booleans.oblique.tilt-0-minus-25": "28c775891fc72a7e1e9ddca122eb2baccd4cb5e1ecb3a28c938ec82a4953edfe", + "booleans.oblique.tilt-0-minus-5": "42a1daf924f93f39544bd0102fe64b49d895896fa3d0ff50611022df475dd88d", + "booleans.oblique.tilt-0-plus-15": "10cef15789d2927ad937b3f7f563d8d55c1315f219352827cd2217a5723ca88b", + "booleans.oblique.tilt-0-plus-25": "16c519fe01707def74a8637b74ba97b7b75acd362369335a592e97ba07f3f588", + "booleans.oblique.tilt-0-plus-5": "a70b0c32bd070bbf3b023b07c38a6364b4f8cdb2483cd6f6a8aa2db12555fd80", + "booleans.oblique.tilt-1-minus-15": "c26194c96b2baca6c267197acf064534fef6d629fde98204eaeb7c15689bc2b0", + "booleans.oblique.tilt-1-minus-25": "00c62421e4cd0c8de9176ee5c5f79b55b1ba2fae2f0626875c6206038edf648d", + "booleans.oblique.tilt-1-minus-5": "020f8ca0d89f0cea8647ea01107eba152b035608abaf5303523c643c7070924a", + "booleans.oblique.tilt-1-plus-15": "8b8be0b09fffee38e061067fee0ce062bf613f5cb62132afc5a930dbd319c828", + "booleans.oblique.tilt-1-plus-25": "6f7a0fe6fadcf41b0553a868d33ae588eca48014246ac3e4fb30e83f6a8da243", + "booleans.oblique.tilt-1-plus-5": "8d66a80f7b6aefbc7c300a8c9484a2ef4ab0084855630b3472e9b4f3da56ccd2", + "booleans.planar.angled-chained": "9f144f107977358fb6741c17e4fcaaf41d0aa2463a937ef55724de43fea87d8c", + "booleans.planar.angled-full-height": "4175d08f3a222c79dc5810c278833f94441d7e075fa604bdad39de41d70242b5", + "booleans.planar.angled-lower-cutout": "efcb3fe5b2fc1ec1ab38ad170aafc2e68d467614b964dec47570d2788ad01339", + "booleans.planar.angled-overlapping-twice": "8bbd43311c9c82e05c0c48deba8403d74541823ee3d2953385103158749de057", + "booleans.planar.angled-rotated-cutter": "3315add1b93525fd17dee63846790b6cd0669bae6756dc2c78d835ed51c7bb1e", + "booleans.planar.arched-cap": "5263e60536ed8209a2b509ce8aa2c6ae51048c214e86d4437d86d90650c12dde", + "booleans.planar.arched-chained": "6ac5ae4f65ce7d7cdd315ba69cd87b0023a841613775d55d8d6feade4e35de3d", + "booleans.planar.axial-disjoint.intersection": "97e3ea19be8f90011a13888d662584708e8554c68caa051375c0f7c27da5aa5f", + "booleans.planar.axial-disjoint.union": "f3a069aeec45e1a71b47195b48bdb5d8d0d9d9b62a9c5488e2221a53b5786d19", + "booleans.planar.axial-embedded": "e5d035960271f5a8e89ffc01ff1722152e4a8fd3aa88ea4bc77e8e5296ee613d", + "booleans.planar.axial.intersection": "0fedd36e8ca948438f50b526cf8fb323a4538ddb363122c2e2e2fac7dfd3ab8b", + "booleans.planar.axial.subtraction": "237f9e1cf5ac0e690ba8afc8e4e51ec839a331828ee76d22d15ebfa437e81124", + "booleans.planar.axial.union": "5ca85a6e9f9215cafbadadc07e3689bbb3392e0e0dcd86659d9a022cd6b80455", + "booleans.planar.band-split": "0d798b21efc5fdf3770a7773fb5df5103a89645bc13c2d5ec93f37a09b4a5811", + "booleans.planar.coextensive.intersection": "6679d501b5c955670adade5366642d2e3016ad755ce1528851d3753ffe75f5f8", + "booleans.planar.coextensive.subtraction": "469646f9f1ac11dfb2c97f7ed757590f99515ab9812265a2b4cb94e292f8b563", + "booleans.planar.coextensive.union": "3fd62f99956d97000fd68e32ef4950f364c38d0ef299525712eb92cbfd2d317e", + "booleans.planar.flush-chained": "2adba1d0159e6e843f20e24a582ea23ab115f82ade70986aaca7ed6b52349849", + "booleans.planar.flush-lower-cutout": "485a047b155b5e1cc653d2beba69f9d8b99ea45450988787bd713d2e6f12bbe9", + "booleans.planar.layered-cavity": "00cf42ca0ce3f0523983e1ea1fa1100d11794b3ed1034e884da0225bf493864e", + "booleans.planar.mitered-chained": "00b87587a726feb83111cc33be3f1bdc55a68456f4726f94bed36eaf75135fb1", + "booleans.planar.mitered-first": "0bf627263365e8fec14885e6b422dcd777302ec0f0e89610549e1d0969b0c23e", + "booleans.planar.round-through-cutter": "905afdb3656829e0b1f8f04d8c2ee972426d64abe84ca6b9b9cd44d749708085", + "booleans.planar.through-hole": "cb09d1e8de1ccde9a76f20d7c2f3be68791a16d64fbb494116ab8f477c460658", + "booleans.sphere-box.pocket-0-lower-inside.intersection": "db042d25ef18b6821e3aadf4b046ac875aa1b999ecfb318b6eff8665450cf94a", + "booleans.sphere-box.pocket-0-lower-inside.reversed-subtraction": "e68031e79352ac664797641e3bb3739972d0a46ad9138dca471a6c4dc3a90491", + "booleans.sphere-box.pocket-0-lower-inside.subtraction": "13ddbc55659fc201ab8208bd00e63c9c3851f9aab01dbef5776d2d700e725c8b", + "booleans.sphere-box.pocket-0-lower-inside.union": "3f9fa2fabc158bf5acf9d781e167a641c467b530a0b1ed44de48eb55887018dd", + "booleans.sphere-box.pocket-0-lower-outside.intersection": "b8eb685aefa22b52c8eed7fccc9f53825ec2fe1d5039a4071a5a14d0bbe6fdc6", + "booleans.sphere-box.pocket-0-lower-outside.reversed-subtraction": "dcc7ad74945b8e8da24944212d6451ca6a6c8456c60c4f8679e9e518815ca2b7", + "booleans.sphere-box.pocket-0-lower-outside.subtraction": "891dd2a77f33db63ae9a831643c0dce27577255ec6930b0ae8bb1b9fcffdda0e", + "booleans.sphere-box.pocket-0-lower-outside.union": "a7a2f5be054215207a7fd3943ff201787756e0b7c0da390f72e8f23a1cc46e6a", + "booleans.sphere-box.pocket-0-upper-inside.intersection": "a00d1659fd5544e16427a70ec57e1768bff373cd4ed590d6229aeab8c3666c99", + "booleans.sphere-box.pocket-0-upper-inside.reversed-subtraction": "0c32f7c4360df606f663900c8d64cbf2893f36af17c22987f0fc13e899fc08c4", + "booleans.sphere-box.pocket-0-upper-inside.subtraction": "a619d546a1c064868304c5130e6ff52c4edef238ca3f030621d34435be33e48b", + "booleans.sphere-box.pocket-0-upper-inside.union": "b270f8cb0e9c2b6785421fd289c7d082d719e26bbec164e523a775a9b6a1d884", + "booleans.sphere-box.pocket-0-upper-outside.intersection": "e24bb61fb141700eb5472a64390fd96e91d9d9422453aaf46d66d3ab4559d6f0", + "booleans.sphere-box.pocket-0-upper-outside.reversed-subtraction": "cc15169e2a8d2a69c83673768eaa7e373e59858309ec9af28a403cf543371f70", + "booleans.sphere-box.pocket-0-upper-outside.subtraction": "6ee4a29a82bfab1f1e399cd7f0edc4d4be4c099076e66c00145a7e69e94c90a9", + "booleans.sphere-box.pocket-0-upper-outside.union": "399b14be57c5bc076d0926c0a183a33ce56c534a0472e57f1cb5fc5d186ab982", + "booleans.sphere-box.pocket-1-lower-inside.intersection": "6b52c05228f86812ee70b779c73873a2ee527a703746c8a0802b712a91345470", + "booleans.sphere-box.pocket-1-lower-inside.reversed-subtraction": "5f8383592d55d6cfcf42c801571f6ab0ea4d28e7079a7509fef623c8f2d53546", + "booleans.sphere-box.pocket-1-lower-inside.subtraction": "ee52f8c8d07aa246083bf583a836ff9f5e317220a7e276aa24781653ad49164e", + "booleans.sphere-box.pocket-1-lower-inside.union": "dcc406048a57ba19f58851372861bf07b8135ba445921f12402f91c2100588e7", + "booleans.sphere-box.pocket-1-lower-outside.intersection": "140057da896074af16876bdf36d2658fcefba08fed82c4e2a8d27a6b727251b5", + "booleans.sphere-box.pocket-1-lower-outside.reversed-subtraction": "cbffddad9fde20ef1af60faffc5ea2288b197d78842a52d76d326b94bd7b2bb1", + "booleans.sphere-box.pocket-1-lower-outside.subtraction": "31669389232edb18e9bca41a43563530b60079403a839583c896d924eb1091f0", + "booleans.sphere-box.pocket-1-lower-outside.union": "132b2e39bd31c1ab37e95dae9c0b1d510413db26b2ded5fd1db32692725a4aca", + "booleans.sphere-box.pocket-1-upper-inside.intersection": "7e1554a7e5d42d480615e59ce78968273237919fe96766950562711d0fa1a05d", + "booleans.sphere-box.pocket-1-upper-inside.reversed-subtraction": "d8b08d2f2668521874860ae47ac3b7fe0da280636b8f01a1bffac8ea81dd8777", + "booleans.sphere-box.pocket-1-upper-inside.subtraction": "b22b2e835b65ab24be3fb0250bf682d471fc353e13fa5656d19093393f1f5ab9", + "booleans.sphere-box.pocket-1-upper-inside.union": "2baa444cb6ab38ec738e2dc0ccc686712b9d6f06677009df52a572d5c7030d1f", + "booleans.sphere-box.pocket-1-upper-outside.intersection": "faf943f77469ca7b6c01723802d9761c0235d33011e8bdffc5d905591f439179", + "booleans.sphere-box.pocket-1-upper-outside.reversed-subtraction": "e3b0cb619ed86bb379a6b3c7eef7b56472d5f3fa1b11872bef0f640e142b90da", + "booleans.sphere-box.pocket-1-upper-outside.subtraction": "93eebbbd1b6184e48f25f4652f9088548c3364284ca5cf7c6b3101d6406982a3", + "booleans.sphere-box.pocket-1-upper-outside.union": "6c5b49647579f0ffb42a4118633fa5f2c3006dab4fcd85dd62f70cca0d1a7d4b", + "booleans.sphere-box.pocket-2-lower-inside.intersection": "62be38d1c89c2a96c7a5782994b93ad5b143cc211fa109395bb833d8157379f8", + "booleans.sphere-box.pocket-2-lower-inside.reversed-subtraction": "1cfd1ee046038e0e94e5bf61846c2cde9c2c470b35153749cb4fb17619b1a1c4", + "booleans.sphere-box.pocket-2-lower-inside.subtraction": "0633872fc80e5a2a33d17c5a38eb857efa1cec92ce5750d6ee671c7b9dcff3f4", + "booleans.sphere-box.pocket-2-lower-inside.union": "31fb5cc846133e0175c7183bbb1056fec8cd3acad6c5656b50a780684f628e68", + "booleans.sphere-box.pocket-2-lower-outside.intersection": "0a539c197b35435145b8256ba08d6c43c29d6ddaef085529f6befe239f73ab8a", + "booleans.sphere-box.pocket-2-lower-outside.reversed-subtraction": "a64836c2973898e23e80c91ef94a4437c632c6869446581c32c8719f6a0cdbfc", + "booleans.sphere-box.pocket-2-lower-outside.subtraction": "6cb157f4c62c47accacb6510d6eea261d5a4e30e17353319ed138cacd1dd7be8", + "booleans.sphere-box.pocket-2-lower-outside.union": "0c228a04bde39525c491d3979da61d81623d6814c9cb0bd03b62ad6e72e3bfed", + "booleans.sphere-box.pocket-2-upper-inside.intersection": "a626e0ff3d729ea898763d5448486311cb6c5b74476fca5c462bd43a802bc769", + "booleans.sphere-box.pocket-2-upper-inside.reversed-subtraction": "a720c54296f1df3053b6215226b67eb845bb855ffd3439829cbf0ba586bebf89", + "booleans.sphere-box.pocket-2-upper-inside.subtraction": "96ec3e5cc08877371271ec159f5de7e3265f8907d2404dc0b90834d205933ee3", + "booleans.sphere-box.pocket-2-upper-inside.union": "4c1f09f3b5eda4d589b945bd2ec5b22adb6cfdfc3329b85c0a7e33d2b3c583d1", + "booleans.sphere-box.pocket-2-upper-outside.intersection": "eafbbcdee3f9b8ea0a9699698080a234675342f52071c55c9c6eda718ef0688f", + "booleans.sphere-box.pocket-2-upper-outside.reversed-subtraction": "a8637c8381ec2cc4799a92956fd13f9c6c3615cd10ae72bdc6de147d0ea2ce21", + "booleans.sphere-box.pocket-2-upper-outside.subtraction": "01423d026052a568d52d0f5e15e3d70b3936e4dde81ecb47cbec9cc47c3f3d3b", + "booleans.sphere-box.pocket-2-upper-outside.union": "1f07f1d1f7fe3e6eda3972dcf86c173dba23b6ee1dc9c8993d7b7a47ae05cc73", + "booleans.sphere-cylinder.coaxial.intersection": "12882ee586a4851aa5ab96450591914cf15f334b65c254dec17f1f6d56a41afe", + "booleans.sphere-cylinder.coaxial.reversed-subtraction": "4bfa71207d87ae9b80c595b17a7b0136d049a2d5af567a63ce4d16cece6f560c", + "booleans.sphere-cylinder.coaxial.subtraction": "0584b09d745d47e08f8ce5ba49591c32327115ea8ab260a03af1c4a7a146985a", + "booleans.sphere-cylinder.coaxial.union": "ccc4989b22a29f505cdb5d86a203c237f290d08c6232533766947b5eefcf7e2f", + "booleans.sphere-cylinder.contained.intersection": "4d1d3c56f5da470fde9895b9d578d594a7e9653a67201c72f378882eb9d88b9b", + "booleans.sphere-cylinder.contained.subtraction": "484b90599a8ef3b8198217110016267c260ab9244d74bce49e49ae97109fc446", + "booleans.sphere-cylinder.contained.union": "4d9c4aaca19678c2381cf570d8a63153b4206196a0f69af9ad31add1a08f970d", + "booleans.sphere-cylinder.crossing.intersection": "af1347ef4355fb1076c74e2f1ef45e513795d4f94cf716dae5cbb112290b97a2", + "booleans.sphere-cylinder.crossing.reversed-subtraction": "b411d12424ea6f6ab8453e7c623d88352cd8674580bfbfdd83618910d66ce181", + "booleans.sphere-cylinder.crossing.subtraction": "ca7cb3ca10266c0a8cb3058887e98e88fd03b823b6d1ae75e8d9a74f3cb0005d", + "booleans.sphere-cylinder.crossing.union": "a31796b122b40b4c1190c949c821dfccee0f2062d53aeac4fe5dacc2ac12d9af", + "booleans.sphere-cylinder.noncoaxial": "48665dae4ba8467911cb08d6618893cb2ded7fff419029c0007670b164d5c5c0", + "booleans.sphere-cylinder.spherical-cavity": "4b11272c12ff37abd0a76a30e9fb1d7577799da1bbd1c99fcb790fca3e1e88a4", + "booleans.sphere-torus.large-sphere": "d0334cd5e250ffb3318bd5de9a1da45db77d24ec5b014784d86c63fc502527da", + "booleans.sphere-torus.small-sphere": "3ba69a1b87e43145a10fdb379931edfb8832a98aa3c2b6b5d369a1b09f7f1e68", + "booleans.spheres-direct.coincident": "a9c9f5395dbbd7f2d1b5d4a9ecc8361945169cb82791913c22d5603f23371a95", + "booleans.spheres-direct.contact": "4fc69c5c6da3c540faf7195e77dacbc7238fe12767e34dde8ae6a252f9fd815f", + "booleans.spheres-direct.disjoint": "04667041cc6b291243df3096b0b230fdb775dc549310b1d3ea407e74cd907376", + "booleans.spheres-direct.enclosed": "f38d46962c91c0f50ca22e1359251be1c9003e715046ecc52cf5ce897e9874cc", + "booleans.spheres-direct.far-coordinates": "4f63c3642d4af2690ae20ac5f9ca931a21eedada297aa4bb5c96b40663f7d8ae", + "booleans.spheres-direct.inside-big.intersection": "519f347fb7dd137c53237a121c5ad3ea2940010f707cf53f8bfe5a1545458100", + "booleans.spheres-direct.inside-big.subtraction": "d3119bbf41217d3d656cf93e062cc1c009aada3a1e9582dab2b309505e011a0d", + "booleans.spheres-direct.internal-tangency": "34aef951ae92981694e9109524d6548413a61ad45d7d3091331995021ce96bda", + "booleans.spheres-direct.placed.intersection": "0114cd165a79395277cd914502440bb1107ac5976e84c24c791d5d29fdd7705d", + "booleans.spheres-direct.placed.subtraction": "84f4b146cd60f0843e1f4185443df292cd340b750b7698162597309e020bae91", + "booleans.spheres-direct.placed.union": "beda2fe83bc81d752d4c042a8fa5d216fcc808a4464c739c5009e38329377413", + "booleans.spheres-direct.rotated-unequal": "f7b2b08c166ee1bc92ca7c1c037737e04ea0c7b11ed69f798ca421c560651349", + "booleans.spheres-direct.scaled-mega": "15a402a2621d8234c6e76dff0f00a579777e356d1e749508f7d63a389738a101", + "booleans.spheres-direct.scaled-micro": "d01db18de7b84fa662fa2a53b73b9056f25bae7309374307b21211c368b44552", + "booleans.spheres-direct.scaled-unit": "f51f4188fe02d94ab2a85afdc779834b5d46462a8f393e144e1557336d89c22c", + "booleans.spheres-direct.small-cap": "0430c3fb380951e2c421055753895c482c4ac8ede8354e7c11e988d2046ce098", + "booleans.spheres-direct.transverse.intersection": "4b4823f596bb627e57f3d63e76469626d49e5471a97da18e8d4f2cbf9eb8091f", + "booleans.spheres-direct.transverse.subtraction": "1f75d141e0e320433001dfcfd5bc528093e6b171e310daed2ad950ffed4bc1f6", + "booleans.spheres-direct.transverse.union": "7c4bff60d3b042d9076ec6cb944ee299aee13291277d307da458948c59d30507", + "booleans.spheres-direct.unsupported-cylinder": "97d8cbf6b6516016e017b9f9233ff677f70f2ccfb7b0247c47d3ae57d771baa0", + "booleans.spheres.enclosed": "067ed9aaece390bf427beef1395a3ea8b0fb154182da414c4eca60f95ffb95b1", + "booleans.spheres.transverse.intersection": "9ddd2a68ee62748d88b8c9333d1f3f63802856405c5970949b97754439d4969d", + "booleans.spheres.transverse.subtraction": "9188b3fd6d402b1c7d72f25e186e07872b6e1a79b0f2e25e12db90f9a23fc225", + "booleans.spheres.transverse.union": "05bddf9b9f406d209574ed9254170352c5d0ce08cf69c168dac92b98c834f3b7", + "booleans.torus-containment.box-minus-torus": "8f25d4c646a22947a34d9591efa2ea4704cd61ede996c5079101bbbdd556ef8e", + "booleans.torus-containment.cylinder-minus-torus": "d36131dfc9b94d91b8a55ca70d135e16d8be6289376312e9c4bcffd4e5d48c5e", + "booleans.torus-containment.frustum-minus-torus": "5aff529c664bfc62cbd0742a31a551da608cc13ff68cdbff7ef2e29f70ca953a", + "booleans.torus.concentric.intersection": "bf76860ba0792bcfd34c0d90b1d5d58b6ff294bae881b0c65b9cb03b220aaafa", + "booleans.torus.concentric.subtraction": "ab22591d1ee68f9079ad96639e0293fc63f1934c931aae620a6391023aec353f", + "booleans.torus.concentric.union": "24b2e1fabe237641f8e4b6446d1da5b0c41845fb88292a27005cb236ec73a9d0", + "booleans.torus.identical": "170a46f8c275e17e1579dccb7ef004f6507cce755475010c899c67a0ffe39049", + "booleans.torus.poking-ring.intersection": "3e2fed88eb1da18f587f251a1b9dfb81afaa4265d7f849d2eaa5aa360c9c1e2c", + "booleans.torus.poking-ring.subtraction": "0977919dd35385128d06f8c8786ca61258b796af687637f35f4db06af84cf883", + "booleans.torus.poking-ring.union": "03cb8356544fb953b1ce28ebe51ce1c314244cb5b8af13a83d724d750ddeffc4", + "builders.annular-cylinder.inverted-radii": "9c2be04be4f397776ec5c55c554ec78b53cc502ec40677ba8e9894c14cfdfc46", + "builders.annular-cylinder.oracle": "8872072704b99db0702fa922fae7ad875d1608e3b02a67338189b3dac9c99f44", + "builders.annular-cylinder.thin-ring": "a1de2afe4048e5aa132f14f248792dd1d08b82acac86774155b3d4fb9d784bf8", + "builders.arc-edged-extrusion-with-holes.oracle": "1b711726a56c5f3b2a5998028af5199860ad99ec85c8844bf5143fcf98d8faae", + "builders.arc-edged-extrusion-with-holes.round-hole": "59017e36a931841375ec8829be9ae6e0405d20d2bd8bd7104340ddd9778a42c3", + "builders.arc-edged-extrusion.ground-band": "09af69878c93fe70684bcc86c8706383f1201d6706fc00058b5b75035407ee33", + "builders.arc-edged-extrusion.open-profile": "c017a0207666d52acaf1bc8634525b6615ad14c62eab82137f78a119bc758082", + "builders.arc-edged-extrusion.oracle": "97f3098f7ebd9c969ac1fdc0746734353cfaad4957133a32f7c3d8dfe1479ef2", + "builders.arc-edged-extrusion.polygon-only": "de1042b7f123429db00e0484160626a06c57e5901885d2f6a82c85f3036dd8eb", + "builders.arc-edged-extrusion.quarter-annulus": "4603a7ca7e7183cf6e3f1f4e3c326400429cb832aa3d963634e803ddadb18fa9", + "builders.arc-edged-extrusion.split-arcs": "d1d2de991a0ae995941dbf6739cd7fae41fa2ecdd4cf763e25735352cabd34dc", + "builders.arc-edged-extrusion.wide": "f031b72d105b797f668ccd57ac60c9dfa9df86c95d2b0f9c612f1a790f9d8b72", + "builders.arc-wire.circle": "356b5dd7a5dfb49a89404c332b2e3aa6fefebab9ec1462fb30ad1d9628ec535e", + "builders.arc-wire.ellipse": "6d2c7fd5e358c4528b84658d6d92c5524176faa8b603808081e942242ae7eb9d", + "builders.arc-wire.half": "b9462fdaca71795c34117cfe4cd11dbbcb47aff1a961e91959c0005c89892bd7", + "builders.arc-wire.line-rejected": "126e3b9c1fff1f6889d4554cf6b7d64e9e494639bd9df6777de5c56c3a0bbe08", + "builders.arc-wire.zero-sweep": "017540164a951e3bdffca955f68e20408469694202daf2fc24e829414986666b", + "builders.cone.identity": "b06b0e3e30cfffdcc69b1c7752c1294b157683faabd2aa1d63ed1e471cfd03a1", + "builders.cone.oracle": "6f6dc78a6282a5b26876390df53726aa645ac769eca824f9e30eae4ef89fb15f", + "builders.cone.zero-height": "7fd364113e199f53d1a85e7810a2ea35567f7e9a7b9684a6fbdd04aad0488d5c", + "builders.cuboid.identity": "67b6dad8866bbbbf5c36cc063f8f17abea670148707aa7a750a24a0f37c6c30e", + "builders.cuboid.oracle": "9dc5750a01a06e66784517f21c6d5a377f4619e80af0405d5db65d8b2f7fe2a5", + "builders.cuboid.skewed": "edc87e337f19908b1536da1ec9ce1d4eccf3d66424359a9ee7156e48a45230cf", + "builders.cuboid.translated": "16d7963e675d4dbde11fc6b991f47eb5097b6151e5e041b38e0e6e57aa3808fa", + "builders.cuboid.zero-size": "395b5d726bd95674ec890ab3bbefaa1cb033a00d5158279c976e64b39c15ad6b", + "builders.cylinder-sector.full-turn": "b7ba35f201da68d9490c170d2adfda9e2eda7f8c6d7ef11473e9941f41ab23fe", + "builders.cylinder-sector.ground": "e4db1189dad4131e856a950b75a3f99a8470cac7fb441ecbc09524e5e0f82675", + "builders.cylinder-sector.quarter": "72323e701206414145cc4fe6f0cd7b966b77cf14a5b4c83d5d232c2fdbe55414", + "builders.cylinder-sector.zero-sweep": "3d8560c29b626a873a1f3213b4899b8172283dd71e08e9c7158cc2e34f6e3d4a", + "builders.cylinder-with-hole.breaching": "d136d5e68189e2b5e9cb1270c4a2e5f2c374b254d9d5c592c46be5209480a7d0", + "builders.cylinder-with-hole.off-centre": "6a692f8d8a5b84475369c0f59e5204e9d13d79fc4448fab9d3764d373cda0ecd", + "builders.cylinder-with-hole.oracle": "005cfc77faa40453eef9eb69631b0c7c2de5ce03a703dd81c961d3c44a2f9fc8", + "builders.cylinder.identity-fine": "2fce43f6d97212228bf97aa7bfd41878ccdc5ee2f259f24e7cc721bd3f01f9f1", + "builders.cylinder.negative-height": "b2a877a2a53bbe2827c49b31b53af53658a10d27e4cf0600b7edc92b8cf2d65f", + "builders.cylinder.oracle": "c502b8a8b7e37cf98b7f348739f3637b0e1a1308cb6c1af41d13ed0fcfab5f16", + "builders.cylinder.skewed": "a7b60b0b47d3fe648ee309b5bd1d55b3d24dac4c4a969dc895005a43d5b450ca", + "builders.cylinder.zero-radius": "60993177577cf9f899e3f16c0e64e2b21a2a1db3128b6ce47cc7d36a9e616cc8", + "builders.frustum.expanding": "f83b77a20bef5c681c24e5e5494b012bfa4e7c65651a29e4a34839b3b4c9453b", + "builders.frustum.negative-radius": "4be0f319922dd6d82b0e5575b49205842e8b996d979c1de02c59068ee22e44eb", + "builders.frustum.oracle": "9b6256733ed4a13f2f51d2f8c9c6fed97bcaf85ea6f7d63214ef1e133edbbac0", + "builders.frustum.shell-family": "748ec17c07e2293f3ab006a8e8fde7785447a79b44e4caf4f5be5e8f0ce651a3", + "builders.linear-extrusion.angled": "52af2dc87a99464a8568efe59f9d7c942a9ffbbd6763d657b13d9a2d2d182b5e", + "builders.linear-extrusion.hole-outside": "77043451aabc4666da39ee3668a85ffa9c632aacac9512007b86a3f5f4d5e245", + "builders.linear-extrusion.oracle": "531b09ce5a3ee709860f5a13486b3a73a6f7c67dac62f9cd12873e7df4626d41", + "builders.linear-extrusion.self-crossing": "e3a72f0afa92d3b4d9abb0c104952fb9408e2a518bbf98b907b6aa8f5e5a1a07", + "builders.linear-extrusion.upright-sector": "fdbeb87035b021e04daaafbc58d80895bbae37051096986a096a50df6303d2d7", + "builders.linear-extrusion.with-hole": "ce18d55e12c1cd2cea89cb3f3b4d58dbfeabef852a3c9cd10dc189f19d4aa980", + "builders.placed.cuboid-scaled": "edd948c54a8736a507a6191347d561227c1c80128302b5618194d3de35d9efb5", + "builders.placed.cylinder-rotated": "ed89ed2a4384e9522d933a38435921d1c31ef95659cbbb4bd93e72d2bf1a535f", + "builders.placed.zero-scale": "b0d66d72efc738d90870067da896fcf1c6a1fbcefc0b7e52f0ac53b1718e08d3", + "builders.polyline-keys.closed": "81f2a96fdc6764b1ccf373d14d8ae5399a06cf1c0e3ccdc13352c2b8010a698d", + "builders.polyline.noncoplanar": "de106849f9cff95a618f76b860f88e797de65c30944c0a4b081b1cb62f140456", + "builders.polyline.open": "485a0b9bebbe46e69ac2f3e44b6567aa57e430857f87c8eba1dc9652f37d5ed4", + "builders.polyline.repeated-point": "d608bd770ef548c6c94c9bee36a322fa486575308e1521e96c1f92bf0fad7156", + "builders.rectangle-keys.ground": "12e2f757757dc4f3e3fb5cd08cb103ed86f9ea393295ed2897fff72605186ed2", + "builders.rectangle.identity": "31332072019245900457cc87ce607bc825c04cddf1589c11f1e870ba7b4a78c1", + "builders.rectangle.zero-width": "7d3835f3229822d788bc85fcdb3881956ee01c96088fc7d615201f01b0472b28", + "builders.sphere.micro": "c3b5b76a3be404831a25d85a8a4cc52c8b2fec75f71b090a6c355ff01fdf8db6", + "builders.sphere.offset": "246a283bb1850d69b2a71e6506558cce8317e77f59e07ba3647af7d48f9085dc", + "builders.sphere.oracle": "ed386c51d41d4b248c7aefccb7808c6884ce91a0fc27c9e816323e0f766ad1c4", + "builders.sphere.zero-radius": "8dbc715dc485ee79e005cfe393cf5a5d80f19bb5b8ac316baebe85b8ab3220a1", + "builders.torus.oracle": "6ca29df1762a086a6e8c99aa7729ff08e3e590bfe163726368777959d33e5e46", + "builders.torus.ring": "6efd19d2f475ae694d9c44629cc6c45c70164140ebfc189811c9d306ab27ec4d", + "builders.torus.self-intersecting": "2bc7b808168fb1310cf4c67ea4d83239769cb71e528a23b1e13e11a213d5cb62", + "creating.extrude.circle": "22c2c6b0ab14ce2b1d6f5e3f1feca12177008d3670c4cf7a730d3710d2f2f1d8", + "creating.extrude.closed-polyline": "927562e27b9d0285bdf989ef18cc2533bf7c5efa71b1e8fe9bde9cf2ec1b09ea", + "creating.extrude.far-parent": "c87f5de160f0b8437be2bfb7b942c6731517966642ce18c1d99613576c1751b7", + "creating.extrude.ground-polyline-forward": "c1ac27abe2dfd0b80e03659fec0e44d25ae06ea7f5ad97c13ed169de97e02d94", + "creating.extrude.ground-polyline-reverse": "24d92a0a22584ff8bfc8ea865217c615db2ea2ac52c5f0a36cd4984ca9da0316", + "creating.extrude.hole-is-profile": "2d50d7d66bf789a1f4998c11ee947e7465606ae3603a82c650f676945bd831c6", + "creating.extrude.negative-annular": "af09b3c73dbae0a980ea417105209ea604502a5b0a2f0a6e15f600f31102a8cf", + "creating.extrude.noncoplanar-hole": "db979d1f079ceecdc96763734de65829cb29f5e1e1a928169a4b873805271096", + "creating.extrude.offset-circle-hole": "dd4300e6f38b2f312a5231bff6667aa74464baf19642d5a0b8815ee7118e9072", + "creating.extrude.open-polyline-profile": "0b3e0afa42d51c285fe787cda1278656a89be0e4708af77e7b1d50804df3723a", + "creating.extrude.polyline-with-polyline-hole": "0a062dc7369cb8b362d4eb4bcd55d35cee24f796cb9ba8345c559e907eae626f", + "creating.extrude.rectangle": "90070b436b7c7c512046e5b126626b1bbafd5e03cdb5c94948ef4a48478785f1", + "creating.extrude.rectangle-round-hole": "c49096e3660a02a331cd90cdaf9cb19c10fef68d1cec9b062c7403c1555496bb", + "creating.extrude.self-crossing": "0242c5183d8e1e35b176a57115443ccc7c33a5833532e402f0e53a9a1f99d09f", + "creating.extrude.too-short": "6b65a724cae7d6df78ee8c5af88812d3ec589de2c6d0c52f0a558ef976ff2702", + "creating.extrude.unknown-profile": "f59a09928fa77a659d6e82b4ccbbb930d18c4651cb8edf029bc7adf92c295091", + "creating.rebuild.keeps-placement": "f79e7d148e1c5222473dc2bc0e3d4e48f11ae5a098352107208dbd1449c88509", + "creating.rebuild.shared-scope": "9dc49b0fcd16d4afb4e70eccb447b0c2347ba70b8440f59f83d325beb0ec44cf", + "creating.rebuild.sweep-and-path": "590b3b1e863292b44448f6d5c962acdf1d232c26d74c7d70635402ddf8f78a7c", + "creating.sweep.circle-horizontal-path": "e8ef9e6f86e608c4cd28cff7ec247e6a22f1dddcaef24c8e5e5639400fc90cf6", + "creating.sweep.circle-l-path": "157b2d8f2bf1bbfe2a71058049aff5f8c512474a062c88a050bb2cb6cd8544a4", + "creating.sweep.circle-offset-profile": "9f523304fd99f0568a08de990e0fa3805ee5f07969403e522a1cf5648303a1e8", + "creating.sweep.circle-path": "72e9d741311256d06799ea1973ad45c2839c7f1b9d67f43cbe7b37391cde0ceb", + "creating.sweep.circle-spatial-path": "b9e987f59589c4d8b067708f6f134f584782222c2ac92b23d083d84bdcfeb3f2", + "creating.sweep.closed-path": "ba3431bd12cd4d5aad80711f42d2370ad06c494eaf9bb78831445f1d7084dba8", + "creating.sweep.collinear-path": "613be9e7d07f6a76c60ced2b93638987b863f322a53bceef1611dfe26e4942dc", + "creating.sweep.miter-collision": "9a8b1f37733629004a6131a6066b9c785958145074619eab57da7b0f58101b28", + "creating.sweep.path-off-profile": "05ec501c5c041a2fac3c93bf05401615f99c7d718c20fe011c6ac6dbfdd05455", + "creating.sweep.rectangle-l-path": "245080349031de8f9b1fdd4cb441e8fee5b57679aa1ef26e7ecddad27f53190d", + "creating.sweep.rectangle-spatial-path": "4e3cf5fcb3fde1b3b0eaec36ec4b827661134ff7cece763a1dd44dd3f0d3d8d3", + "creating.sweep.reversing-path": "52e7d3543968aca03d0f4cb27ce4450d08c791a6f1cbb39e7601b8568beb3ca9", + "creating.sweep.separated-collision": "48992e86585680d072f5903157d61847acd33942f4e476c4c564827fffd9f447", + "creating.sweep.straight-path": "689b9cc757963af26eb6a001d3b049ab4b4e299d05231ddb3dabb9eaff1cd34f", + "fallback.planar.oblique-cutters": "ebf7d29ba112b6db5bee0b311d5600e1ed85b7f7454a21d2bedcc89818bae40d", + "fallback.planar.overlapping-cutters": "718b20930f316734b5bdb2d05191c2896060622972632b2b0b688ed961ee2400", + "fallback.prismatic.single-round": "eb4f4e4ede13f9f4cbb2d3b9516629b990bb0ee38ba869913687adba9920d74c", + "graph.acceptance-session": "de02d88f13382d9da9c078b53c11ae3ca556e0a0ea8b5a79aca229007a3e9236", + "graph.operate.box-subtraction": "d29d33b21c068ba94b5481a6447dc1216d76761885f8cfb27fa6bf21fb3f28c5", + "graph.operate.corotated-box-0": "6fd8ee24714e9fd28f93af33610f8f09f5211e413a68923b602ae0b41b2e97c6", + "graph.operate.corotated-box-30": "7e2d9594a4eb27ed598d885942660bf1a6d4c064b07ce8300b25e53b0685c42b", + "graph.operate.corotated-box-90": "7c396c9e0503d89379c85c3da687bd9cd385c2b1e8fad7a773fd4526ecd91659", + "graph.operate.corotated-wall-0": "bb604e045e1eaed8b9bfad2acc492ebd759e3fb2748ad3d10c54c527a3a57fd0", + "graph.operate.corotated-wall-30": "68afc1ea14ddfdd1c920fb0acdd905b138442943f17a023c7896a2c700cb715d", + "graph.operate.corotated-wall-90": "539ab559adb6ee55121e98080698346c4865441b444f9ea24f64ff039005fb07", + "graph.operate.empty-and-limits": "c506ab76c6bc57e8502c10ac4ce51b087b4c23a2f41bed48c76ad5033db3dcb2", + "graph.operate.empty-subtraction": "479383b7220098f4e2964166121c879d0e96345928d647e6b215cd2e7360762e", + "graph.operate.make-unique-then-operate": "b01331d663af9382896593ef19fddc5b348672958ea3a27c7c81a0f5b9c8a282", + "graph.operate.mixed-cutter-batch": "edad008d178b1689b642149d207382d55897de5f1cd952164cbd8b4ba4f143d1", + "graph.operate.planar-batch": "b122b01ea8c2fa2d9997aef881660e9238a3b6644428bf701feb415a24c1903f", + "graph.operate.scaled-tool": "8352fb19134365621a693658a7f7b768d5803e887d398be2edf1da23074fdfdc", + "graph.operate.serial-union-and-intersection": "ef868c48bc13320e8379398d52657b7fcaa0ebb8fb0e6411a5beb74bdd709b8f", + "graph.operate.shared-target": "9a9a58e2ba1ba20d4a10a9dee72deaba30b99d0209dbfac691edaa42eabdf7cf", + "graph.operate.sweep-result": "2cac0a8631bc84a3af46ac987fe9ef96af4f0cf85d4b336b71a0d21fa8db8f4f", + "graph.scenario.copies-and-planes": "435b1e4bb1d258c8cbd258fcfcbcefc6e398616669966fc2db9dda197238d592", + "graph.scenario.failed-calls": "5bd6c20c7e44aa77b5021118987d64ca4af4110568c6870baca83da9c34f1d00", + "graph.scenario.instances-and-duplicates": "dc00a666d143f42bba422b662bbf168b1b18b83dead2c3c37f6ccfd8ecc0fa73", + "graph.scenario.many-rotations": "d93e0deba63f364b14a73037925eb16a3b45051a285d8b1ace475d4e3279bacd", + "graph.scenario.marks": "a9d452e19c457142fcccf01b997cd45914fc6fc5eb5ed2bfa0db8cd3b0ff1cba", + "graph.scenario.nested-marks": "ab10b55edb609dab390ccab475f91fe51d4eed7d899f2aef9bbde2672fb396ab", + "graph.scenario.place-round-trip": "00eb935f39e25f5727371d2d26b4bdb04c7ba9b1c68d4b3b2702c571a6324ff5", + "graph.scenario.rejected-primitives": "a777185b3dc70ed3a24ec8ae0b8576d3cca05dcfacb2931796297fe1aa65190d", + "graph.scenario.reparenting": "def3dca7be774dea6b24c7eca2f7163aa1118f9d7b08e89a349a65286eb6c829", + "graph.scenario.shared-rebuild": "f3c980ffc0762c7f0de906b1aa1dba04e424ebd493e6744a9e3db28db9a5b70f", + "graph.scenario.subtree-dispose": "3393656291d922a7f7206b51849264b6e68092170b8a6f67849135ea6179f00b", + "handlers-covered": "786bdc2315486b549e189b9cad6500600eb9991537f96de85b4ec69daaef47b3", + "handlers-expected": "28674ce57df978909b9f7c9706ae15272410348af793c7d6c34f05fac7fbbcb9", + "handlers-uncovered": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "level.step.metre.y": "3bfe75db34f9ee266e502be4b3b641abd5d26b3a51c1bf8657f41252ee614f49", + "level.step.metre.z": "6108f7b562c88424cd12180dcb82584d60810117a73b36b6ac410bfcdbabb0c2", + "level.step.millimetre.y": "1c38087a54a0407778dd69d6ee89688731b27f98aede2098ffee09bd4a51183d", + "level.step.millimetre.z": "8f9edbe81afd24b8cf95a229215e2d9def24d56474309f031e6228089133dcac", + "shell.arc-edged-unsupported": "3c8ad9b57742fa525f38f291f308d0422c3b710a04ebe7ac6280ed4d9c7ec811", + "shell.box": "5aa29362fe4c62993e490bbc466dce23629efb2a58c61879b6c54d9cf317c767", + "shell.box-below-resolution": "17556323ab36b22e051fda473d7c9cc9f1094996e5b2f6858fcba09bda75a65a", + "shell.cone": "cc4c53e7bb5974098d19bfaf5d56efcc656fbf26dc8b18ff65c9bbe1736f1b7a", + "shell.cylinder": "f14bb1497af457f3cb0dbd703af4bc5c1ee6abe2d5ce8ab9d264c355c1d73128", + "shell.frustum": "83f340621aaaae9b9b3f30ac0e87351df689037afa28c9bbc51f2ac5ae6a50cc", + "shell.profile": "0671db8a1f8cae528bf386a3632e3d8fceef99dc6174c2a263ac64948438f0b7", + "shell.profile-collapsed": "590f2c79bb09d4705ba2a7dcc599378048ab1263cf6033a9fbb3cb96c972da4f", + "shell.sphere": "162a0e55d1b60d2a98fca1318ae516a33bd75d51827d821958fd30929de8146c", + "shell.sphere-collapsed": "2ee1754001dea36b57bdf794740e33fcd14583265e23fbfbff66166e8a660f80", + "shell.torus": "3c96e552aa39f1f7e20e5d1832091627c5bb366a169cba9d7e542f51bc5ffcd1", + "sweep-3d": "3d596e4aec236d98119014480ef3a7bb167ec1fa36a5dac56453709d78c7f576" +} diff --git a/main/opengeometry/tests/golden/batch.rs b/main/opengeometry/tests/golden/batch.rs new file mode 100644 index 0000000..3378253 --- /dev/null +++ b/main/opengeometry/tests/golden/batch.rs @@ -0,0 +1,189 @@ +use crate::batch_case::{batch_table, both_orders, BatchBuilt, BatchEntry}; +use crate::fixtures::{ + at, fine, ground, moved, polar, rectangle, standard, tilted_about_y, upright, +}; +use crate::kernel::{primitives, Frame3, Point3}; +use crate::planar_shapes::{framed, opening, wall, Shape}; +use crate::profiles::{quarter_annulus, wide_arc}; +use crate::runner::Case; +use std::f64::consts::{FRAC_PI_2, PI}; + +fn ground_cylinder(name: &str, origin: Point3, radius: f64, height: f64) -> Shape { + primitives::cylinder(name.into(), moved(ground(), origin), radius, height, fine()) +} + +fn slab(name: &str, depth: f64, lo: [f64; 2], hi: [f64; 2], height: f64) -> Shape { + framed(name, upright([0.0, depth, 0.0]), rectangle(lo, hi), height) +} + +fn door(name: &str, from: f64, to: f64) -> Shape { + opening(name, 0.0, 2.1, from, to) +} + +fn two_cuboids() -> BatchBuilt { + let block = |name: &str, x: f64| { + primitives::cuboid(name.into(), at([x, 0.0, 0.0]), [1.0, 2.0, 2.0], standard()) + }; + let host = primitives::cuboid("host".into(), Frame3::IDENTITY, [6.0, 2.0, 2.0], standard())?; + Ok((host, vec![block("left", 1.0)?, block("right", 4.0)?])) +} + +fn ten_openings() -> BatchBuilt { + let cutters = (0..10) + .map(|index| { + let left = 1.0 + index as f64 * 1.8; + door(&format!("opening-{index}"), left, left + 0.5) + }) + .collect::, _>>()?; + Ok((wall("host", 20.0)?, cutters)) +} + +fn single_cutter() -> BatchBuilt { + Ok((wall("host", 10.0)?, vec![door("lower_cutout", 2.0, 3.0)?])) +} + +fn overlapping() -> BatchBuilt { + let cutters = vec![door("first", 2.0, 3.0)?, door("second", 2.5, 3.5)?]; + Ok((wall("host", 10.0)?, cutters)) +} + +fn oblique_pair() -> BatchBuilt { + let cutter = |name: &str, station: f64| { + let frame = tilted_about_y([station, 0.15, 0.0], PI / 6.0); + framed(name, frame, rectangle([-0.5, -0.5], [0.5, 0.5]), 2.1) + }; + let cutters = vec![ + cutter("first-oblique", 2.5)?, + cutter("second-oblique", 6.0)?, + ]; + Ok((wall("oblique-host", 10.0)?, cutters)) +} + +fn single_oblique() -> BatchBuilt { + let (host, mut cutters) = oblique_pair()?; + cutters.truncate(1); + Ok((host, cutters)) +} + +fn no_cutters() -> BatchBuilt { + Ok((wall("host", 10.0)?, Vec::new())) +} + +fn too_many_cutters() -> BatchBuilt { + let cutters = (0..101) + .map(|index| { + let from = 0.05 * index as f64; + door(&format!("crowded-{index}"), from, from + 0.01) + }) + .collect::, _>>()?; + Ok((wall("host", 10.0)?, cutters)) +} + +fn fifty_openings() -> BatchBuilt { + let frame = ground(); + let outer = rectangle([-30.0, -0.1], [30.0, 0.1]); + let name = "wall".into(); + let host = primitives::linear_extrusion(name, frame, outer, Vec::new(), 3.0, standard())?; + let cutters = (0..50) + .map(|index| { + let origin = [-27.0 + index as f64 * 1.1 - 0.25, 0.0, 0.2]; + let name = format!("cut-{index}"); + primitives::cuboid(name, moved(frame, origin), [0.5, 0.4, 2.0], standard()) + }) + .collect::, _>>()?; + Ok((host, cutters)) +} + +fn disjoint_mixed() -> BatchBuilt { + let cylinder = ground_cylinder("round", [1.5, -0.5, 1.5], 0.4, 1.3)?; + let slab = slab("rectangle", 0.8, [3.5, 0.7], [4.5, 2.2], 1.3)?; + Ok((wall("mixed-host", 6.0)?, vec![cylinder, slab])) +} + +fn two_round() -> BatchBuilt { + let first = ground_cylinder("round-a", [1.5, 0.0, 1.5], 0.4, 0.3)?; + let second = ground_cylinder("round-b", [4.5, 0.0, 1.5], 0.4, 0.3)?; + Ok((wall("two-round-host", 6.0)?, vec![first, second])) +} + +fn single_round() -> BatchBuilt { + let (host, mut cutters) = two_round()?; + cutters.truncate(1); + Ok((host, cutters)) +} + +fn split_mixed() -> BatchBuilt { + let cylinder = ground_cylinder("split-round", [1.5, -0.5, 1.5], 0.4, 1.3)?; + let slab = slab("full-height", 0.3, [2.5, 0.0], [3.5, 3.0], 0.3)?; + Ok((wall("split-mixed-host", 6.0)?, vec![cylinder, slab])) +} + +fn flush_mixed() -> BatchBuilt { + let cylinder = ground_cylinder("flush-round", [1.5, 0.0, 1.5], 0.4, 0.3)?; + let slab = slab("flush-rectangle", 0.3, [3.5, 0.7], [4.5, 1.5], 0.3)?; + Ok((wall("flush-mixed-host", 6.0)?, vec![cylinder, slab])) +} + +fn arched() -> BatchBuilt { + let cap = ground_cylinder("arched-cap", [2.0, -0.5, 2.0], 0.5, 1.3)?; + let lower = slab("arched-lower", 0.8, [1.5, 0.5], [2.5, 2.0], 1.3)?; + Ok((wall("arched-host", 4.0)?, vec![cap, lower])) +} + +fn arc_host() -> BatchBuilt { + let outer = quarter_annulus(2.0, 1.5); + let name = "arc-host".into(); + let host = primitives::arc_edged_extrusion(name, Frame3::IDENTITY, outer, 3.0, fine())?; + let size = [1.0, 0.35, 1.5]; + let first = primitives::cuboid("opening".into(), at([1.1, 0.9, 0.5]), size, fine())?; + let size = [0.45, 0.8, 1.2]; + let second = primitives::cuboid("second-opening".into(), at([0.6, 1.3, 0.8]), size, fine())?; + Ok((host, vec![first, second])) +} + +fn wide_sector(index: usize) -> Shape { + let start = -FRAC_PI_2; + let opening_start = start + 1.2 / 2.0; + let opening_end = start + 8.2 / 2.0; + let from = opening_start + (opening_end - opening_start) * index as f64 / 3.0; + let to = opening_start + (opening_end - opening_start) * (index + 1) as f64 / 3.0; + let reach = 2.1 / ((to - from) / 2.0).cos() + 0.2; + let outer = vec![[0.0, 0.0], polar(reach, from), polar(reach, to)]; + framed(&format!("sector-{index}"), Frame3::IDENTITY, outer, 2.0) +} + +fn wide_opening() -> BatchBuilt { + let name = "wide-arc".into(); + let host = primitives::arc_edged_extrusion(name, Frame3::IDENTITY, wide_arc(), 3.0, fine())?; + let cutters = (0..3).map(wide_sector).collect::, _>>()?; + Ok((host, cutters)) +} + +const ENTRIES: [BatchEntry; 13] = [ + ("planar.two-cuboids", "two", two_cuboids), + ("planar.ten-flush-openings", "batch", ten_openings), + ("planar.single-cutter", "single", single_cutter), + ("planar.single-oblique", "single-oblique", single_oblique), + ("planar.overlapping-cutters", "overlap", overlapping), + ("planar.oblique-cutters", "oblique", oblique_pair), + ("planar.no-cutters", "empty", no_cutters), + ("planar.too-many-cutters", "crowded", too_many_cutters), + ("planar.fifty-openings", "result", fifty_openings), + ("prismatic.single-round", "single-round", single_round), + ("prismatic.arched", "arched-batch", arched), + ("vertical-arc.arc-host", "arc-cut-batch", arc_host), + ("vertical-arc.wide-opening", "wide-cut", wide_opening), +]; + +const ORDERED: [BatchEntry; 4] = [ + ("prismatic.disjoint-mixed", "mixed", disjoint_mixed), + ("prismatic.two-round", "two-round", two_round), + ("prismatic.split-mixed", "split-mixed", split_mixed), + ("prismatic.flush-mixed", "flush-mixed", flush_mixed), +]; + +pub(crate) fn cases() -> Vec { + let mut cases = batch_table(&ENTRIES); + cases.extend(both_orders(&ORDERED)); + cases +} diff --git a/main/opengeometry/tests/golden/batch_case.rs b/main/opengeometry/tests/golden/batch_case.rs new file mode 100644 index 0000000..a139ea3 --- /dev/null +++ b/main/opengeometry/tests/golden/batch_case.rs @@ -0,0 +1,90 @@ +use crate::batch_route::{batch_route, BatchOutcome}; +use crate::boolean_case::result_body; +use crate::json::brep_sha256; +use crate::kernel::{subtract_planar_cutters_with_handlers, BrepEnvelope}; +use crate::record::{Failure, Record}; +use crate::runner::Case; +use std::sync::Arc; + +pub(crate) type Inputs = + Arc Result<(BrepEnvelope, Vec), Failure> + Send + Sync>; + +pub(crate) type BatchBuilt = Result<(BrepEnvelope, Vec), Failure>; + +pub(crate) type Batch = fn() -> BatchBuilt; + +pub(crate) type BatchEntry = (&'static str, &'static str, Batch); + +pub(crate) fn inputs( + build: impl Fn() -> Result<(BrepEnvelope, Vec), Failure> + Send + Sync + 'static, +) -> Inputs { + Arc::new(build) +} + +pub(crate) fn reversed(inputs: &Inputs) -> Inputs { + let inputs = Arc::clone(inputs); + Arc::new(move || { + let (host, mut cutters) = inputs()?; + cutters.reverse(); + Ok((host, cutters)) + }) +} + +fn record_inputs(record: &mut Record, host: &BrepEnvelope, cutters: &[BrepEnvelope]) { + record.section("inputs"); + record.field("host", &host.id); + record.field("host.sha256", brep_sha256(host)); + record.field("cutters", cutters.len()); + for cutter in cutters { + record.field(&format!("cutter {}", cutter.id), brep_sha256(cutter)); + } +} + +pub(crate) fn batch(name: &str, id: &str, inputs: &Inputs) -> Case { + let inputs = Arc::clone(inputs); + let id = id.to_string(); + Case::new(format!("batch.{name}"), move |record| { + let (host, cutters) = inputs()?; + record_inputs(record, &host, &cutters); + record.section("batch"); + record.field("id", &id); + match subtract_planar_cutters_with_handlers(&host, &cutters, id.clone()) { + Ok((result, handlers)) => { + let route = batch_route(cutters.len(), &BatchOutcome::Succeeded(&handlers)); + record.field("route", route); + record.debug("handlers", &handlers); + record.cover(route); + record.cover_all(&handlers); + result_body(record, Ok(result)); + } + Err(error) => { + let route = batch_route(cutters.len(), &BatchOutcome::Failed(&error)); + record.field("route", route); + record.cover(route); + result_body(record, Err(error)); + } + } + Ok(()) + }) +} + +pub(crate) fn batch_table(entries: &[BatchEntry]) -> Vec { + entries + .iter() + .map(|(name, id, build)| batch(name, id, &inputs(*build))) + .collect() +} + +pub(crate) fn both_orders(entries: &[BatchEntry]) -> Vec { + entries + .iter() + .flat_map(|(name, id, build)| { + let forward = inputs(*build); + let backward = reversed(&forward); + [ + batch(&format!("{name}.forward"), id, &forward), + batch(&format!("{name}.reverse"), id, &backward), + ] + }) + .collect() +} diff --git a/main/opengeometry/tests/golden/batch_route.rs b/main/opengeometry/tests/golden/batch_route.rs new file mode 100644 index 0000000..941255c --- /dev/null +++ b/main/opengeometry/tests/golden/batch_route.rs @@ -0,0 +1,125 @@ +use crate::kernel::GeometryError; + +pub(crate) enum BatchOutcome<'a> { + Succeeded(&'a [String]), + Failed(&'a GeometryError), +} + +struct BatchRoute { + name: &'static str, + taken: fn(usize, &BatchOutcome) -> bool, +} + +fn coverage_gap<'a>(outcome: &'a BatchOutcome) -> Option<&'a [String; 2]> { + match outcome { + BatchOutcome::Failed(GeometryError::CoverageGap { families }) => Some(families), + _ => None, + } +} + +fn mixed_batch_gap<'a>(outcome: &'a BatchOutcome) -> Option<&'a String> { + coverage_gap(outcome) + .filter(|families| families[0] == "mixed cutter batch") + .map(|families| &families[1]) +} + +fn nested_handler(outcome: &BatchOutcome, name: &str) -> bool { + match outcome { + BatchOutcome::Succeeded(handlers) => handlers.get(1).is_some_and(|handler| handler == name), + BatchOutcome::Failed(_) => false, + } +} + +fn cutter_count_rejected(_: usize, outcome: &BatchOutcome) -> bool { + matches!( + outcome, + BatchOutcome::Failed(GeometryError::InvalidGeometry(message)) + if message.starts_with("planar batch subtraction requires") + ) +} + +fn overlapping_rejected(_: usize, outcome: &BatchOutcome) -> bool { + coverage_gap(outcome).is_some_and(|families| families[0] == "overlapping planar batch cutters") +} + +fn mixed_planar_stage_gap(_: usize, outcome: &BatchOutcome) -> bool { + mixed_batch_gap(outcome).is_some_and(|stage| stage == "planar stage") +} + +fn mixed_cylindrical_stage_gap(_: usize, outcome: &BatchOutcome) -> bool { + mixed_batch_gap(outcome).is_some_and(|stage| stage != "planar stage") +} + +fn single_cutter(cutters: usize, _: &BatchOutcome) -> bool { + cutters == 1 +} + +fn mixed_staged(_: usize, outcome: &BatchOutcome) -> bool { + nested_handler(outcome, "subtract_planar_cutters") +} + +fn prismatic_profile(_: usize, outcome: &BatchOutcome) -> bool { + nested_handler(outcome, "subtract_prismatic_profile_batch") +} + +fn vertical_arc(_: usize, outcome: &BatchOutcome) -> bool { + nested_handler(outcome, "subtract_vertical_arc_extrusion_batch") +} + +fn rectilinear(_: usize, outcome: &BatchOutcome) -> bool { + nested_handler(outcome, "boolean_rectilinear") +} + +const BATCH_ROUTES: [BatchRoute; 9] = [ + BatchRoute { + name: "batch-route:cutter-count-rejected", + taken: cutter_count_rejected, + }, + BatchRoute { + name: "batch-route:overlapping-rejected", + taken: overlapping_rejected, + }, + BatchRoute { + name: "batch-route:mixed-planar-stage-gap", + taken: mixed_planar_stage_gap, + }, + BatchRoute { + name: "batch-route:mixed-cylindrical-stage-gap", + taken: mixed_cylindrical_stage_gap, + }, + BatchRoute { + name: "batch-route:single-cutter", + taken: single_cutter, + }, + BatchRoute { + name: "batch-route:mixed-staged", + taken: mixed_staged, + }, + BatchRoute { + name: "batch-route:prismatic-profile", + taken: prismatic_profile, + }, + BatchRoute { + name: "batch-route:vertical-arc", + taken: vertical_arc, + }, + BatchRoute { + name: "batch-route:rectilinear", + taken: rectilinear, + }, +]; + +pub(crate) fn batch_route(cutters: usize, outcome: &BatchOutcome) -> &'static str { + match BATCH_ROUTES + .iter() + .find(|route| (route.taken)(cutters, outcome)) + { + Some(route) => route.name, + None if matches!(outcome, BatchOutcome::Succeeded(_)) => "batch-route:other-success", + None => "batch-route:other-failure", + } +} + +pub(crate) fn batch_route_names() -> impl Iterator { + BATCH_ROUTES.iter().map(|route| route.name) +} diff --git a/main/opengeometry/tests/golden/batch_staged.rs b/main/opengeometry/tests/golden/batch_staged.rs new file mode 100644 index 0000000..af24eeb --- /dev/null +++ b/main/opengeometry/tests/golden/batch_staged.rs @@ -0,0 +1,117 @@ +use crate::batch_case::{batch_table, both_orders, BatchBuilt, BatchEntry}; +use crate::fixtures::{accuracy, at, ground, tilted_about_x, tilted_about_y}; +use crate::kernel::{primitives, Accuracy, Frame3}; +use crate::planar_shapes::Shape; +use crate::profiles::band; +use crate::runner::Case; + +const OPENING: [f64; 3] = [1.0, 0.5, 0.4]; + +fn staged_accuracy() -> Accuracy { + accuracy(6.3e-8, 1.575e-8, 0.01, 1e-5) +} + +fn host() -> Shape { + let outer = band(3.0, 2.7, 0.8, -1.6); + primitives::arc_edged_extrusion( + "mixed-curved-host".into(), + ground(), + outer, + 3.0, + staged_accuracy(), + ) +} + +fn opening(name: &str, frame: Frame3) -> Shape { + primitives::cuboid(name.into(), frame, OPENING, staged_accuracy()) +} + +fn rectangular() -> Shape { + opening("rectangular-opening", at([2.2, 1.0, 1.1])) +} + +fn round_opening() -> Shape { + let frame = Frame3 { + origin: [2.65, 1.5, 0.0], + x: [0.0, 0.0, -1.0], + y: [0.0, 1.0, 0.0], + z: [1.0, 0.0, 0.0], + }; + primitives::cylinder("round-opening".into(), frame, 0.5, 0.4, staged_accuracy()) +} + +fn mixed() -> BatchBuilt { + Ok((host()?, vec![rectangular()?, round_opening()?])) +} + +fn overlapping() -> BatchBuilt { + let overlapping = opening("overlapping-opening", at([2.3, 1.2, 1.0]))?; + Ok((host()?, vec![rectangular()?, overlapping, round_opening()?])) +} + +fn oblique_planar() -> BatchBuilt { + let oblique = opening("oblique-opening", tilted_about_y([2.2, 1.0, 0.9], 0.5))?; + Ok((host()?, vec![oblique, round_opening()?])) +} + +fn oblique_planar_pair() -> BatchBuilt { + let first = opening("oblique-first", tilted_about_y([2.2, 1.0, 0.9], 0.5))?; + let second = opening("oblique-second", tilted_about_y([2.3, 1.2, 0.8], 0.5))?; + Ok((host()?, vec![first, second, round_opening()?])) +} + +fn pitched_planar() -> BatchBuilt { + let pitched = opening("pitched-opening", tilted_about_x([2.2, 1.0, 1.1], 0.4))?; + Ok((host()?, vec![pitched, round_opening()?])) +} + +fn skew_planar() -> BatchBuilt { + let frame = Frame3::from_axis([2.2, 1.0, 1.1], [1.0, 1.0, 1.0], [1.0, 0.0, 0.0])?; + Ok(( + host()?, + vec![opening("skew-opening", frame)?, round_opening()?], + )) +} + +fn oblique_round() -> BatchBuilt { + let frame = Frame3::from_axis([2.4, 1.2, 0.0], [0.8, 0.6, 0.0], [0.0, 0.0, 1.0])?; + let oblique = primitives::cylinder("oblique-round".into(), frame, 0.2, 0.9, staged_accuracy())?; + Ok((host()?, vec![rectangular()?, oblique])) +} + +const ORDERED: [BatchEntry; 1] = [("staged.mixed-curved-openings", "mixed-curved-cut", mixed)]; + +const ENTRIES: [BatchEntry; 6] = [ + ( + "staged.overlapping-planar-cutters", + "mixed-overlap", + overlapping, + ), + ( + "staged.oblique-planar-stage", + "mixed-oblique-planar", + oblique_planar, + ), + ( + "staged.overlapping-oblique-planar-stage", + "mixed-oblique-pair", + oblique_planar_pair, + ), + ( + "staged.pitched-planar-stage", + "mixed-pitched", + pitched_planar, + ), + ("staged.skew-planar-stage", "mixed-skew", skew_planar), + ( + "staged.oblique-cylinder-stage", + "mixed-oblique-round", + oblique_round, + ), +]; + +pub(crate) fn cases() -> Vec { + let mut cases = both_orders(&ORDERED); + cases.extend(batch_table(&ENTRIES)); + cases +} diff --git a/main/opengeometry/tests/golden/body.rs b/main/opengeometry/tests/golden/body.rs new file mode 100644 index 0000000..af70d03 --- /dev/null +++ b/main/opengeometry/tests/golden/body.rs @@ -0,0 +1,24 @@ +use crate::classify::classify_grid; +use crate::display::record_display_buffers; +use crate::json::record_brep; +use crate::kernel::{BodyType, BrepEnvelope}; +use crate::mesh::record_tessellations; +use crate::record::Record; +use crate::step::record_step_exports; +use crate::volume::record_volume; + +pub(crate) fn record_body(record: &mut Record, label: &str, shape: &BrepEnvelope) { + record_brep(record, label, shape); + let body_type = shape.body_type(); + record.section(&format!("{label}.body-type")); + record.debug("body_type", &body_type); + let mesh = record_tessellations(record, label, shape); + record_display_buffers(record, label, shape); + record_step_exports(record, label, shape); + if matches!(body_type, Ok(BodyType::Solid)) { + classify_grid(record, label, shape); + if let Some(mesh) = mesh { + record_volume(record, label, shape, &mesh); + } + } +} diff --git a/main/opengeometry/tests/golden/boolean_case.rs b/main/opengeometry/tests/golden/boolean_case.rs new file mode 100644 index 0000000..123c855 --- /dev/null +++ b/main/opengeometry/tests/golden/boolean_case.rs @@ -0,0 +1,134 @@ +use crate::body::record_body; +use crate::json::{brep_sha256, pretty}; +use crate::kernel::{ + boolean_brep_outcome_with_handlers, BooleanOp, BooleanResult, BrepEnvelope, GeometryError, +}; +use crate::record::{note_staged_handlers, Failure, Record}; +use crate::runner::Case; +use serde_json::to_value; +use std::sync::Arc; + +pub(crate) type Operands = + Arc Result<(BrepEnvelope, BrepEnvelope), Failure> + Send + Sync>; + +pub(crate) type Built = Result<(BrepEnvelope, BrepEnvelope), Failure>; + +pub(crate) type Pair = fn() -> Built; + +pub(crate) type Entry = (&'static str, &'static str, BooleanOp, Pair); + +pub(crate) type Family = (&'static str, &'static str, Pair); + +pub(crate) const OPERATIONS: [(&str, BooleanOp); 3] = [ + ("union", BooleanOp::Union), + ("intersection", BooleanOp::Intersection), + ("subtraction", BooleanOp::Subtraction), +]; + +pub(crate) fn operands( + build: impl Fn() -> Result<(BrepEnvelope, BrepEnvelope), Failure> + Send + Sync + 'static, +) -> Operands { + Arc::new(build) +} + +pub(crate) fn boolean(name: &str, id: &str, operation: BooleanOp, operands: &Operands) -> Case { + let operands = Arc::clone(operands); + let id = id.to_string(); + Case::new(name, move |record| { + let (a, b) = operands()?; + let (result, _) = record_boolean_handlers(record, &a, &b, operation, &id); + result_body(record, result); + Ok(()) + }) +} + +pub(crate) fn every_operation(prefix: &str, id: &str, operands: &Operands) -> Vec { + OPERATIONS + .iter() + .map(|(label, operation)| { + boolean( + &format!("{prefix}.{label}"), + &format!("{id}-{label}"), + *operation, + operands, + ) + }) + .collect() +} + +pub(crate) fn swapped(operands: &Operands) -> Operands { + let operands = Arc::clone(operands); + Arc::new(move || { + let (a, b) = operands()?; + Ok((b, a)) + }) +} + +pub(crate) fn record_operands(record: &mut Record, a: &BrepEnvelope, b: &BrepEnvelope) { + record.section("operands"); + record.field("a", &a.id); + record.field("a.sha256", brep_sha256(a)); + record.field("b", &b.id); + record.field("b.sha256", brep_sha256(b)); +} + +pub(crate) fn record_boolean_handlers( + record: &mut Record, + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: &str, +) -> (Result, Vec) { + record_operands(record, a, b); + record.section("boolean"); + record.debug("operation", operation); + record.field("id", id); + let (result, handlers) = boolean_brep_outcome_with_handlers(a, b, operation, id.to_string()); + record.debug("handlers", &handlers); + record.cover_all(&handlers); + (result, handlers) +} + +pub(crate) fn result_body(record: &mut Record, result: Result) { + match result { + Ok(result) => { + record.block("report", &pretty(to_value(&result.report))); + record_body(record, "result", &result.brep); + } + Err(error) => record.error("result", error), + } +} + +pub(crate) fn stage( + a: &BrepEnvelope, + b: &BrepEnvelope, + operation: BooleanOp, + id: &str, +) -> Result { + let (result, handlers) = boolean_brep_outcome_with_handlers(a, b, operation, id.to_string()); + note_staged_handlers(handlers); + Ok(result?.brep) +} + +pub(crate) fn table(prefix: &str, entries: &[Entry]) -> Vec { + entries + .iter() + .map(|(name, id, operation, pair)| { + boolean( + &format!("{prefix}.{name}"), + id, + *operation, + &operands(*pair), + ) + }) + .collect() +} + +pub(crate) fn families(prefix: &str, entries: &[Family]) -> Vec { + entries + .iter() + .flat_map(|(name, id, pair)| { + every_operation(&format!("{prefix}.{name}"), id, &operands(*pair)) + }) + .collect() +} diff --git a/main/opengeometry/tests/golden/boolean_fixtures.rs b/main/opengeometry/tests/golden/boolean_fixtures.rs new file mode 100644 index 0000000..d074be2 --- /dev/null +++ b/main/opengeometry/tests/golden/boolean_fixtures.rs @@ -0,0 +1,75 @@ +use crate::boolean_case::{boolean, operands}; +use crate::fixtures::{ground, moved, standard}; +use crate::kernel::{primitives, BooleanOp, BrepEnvelope, GeometryError}; +use crate::runner::Case; + +type Build = fn() -> Result; + +fn box_host() -> Result { + primitives::cuboid("box-host".into(), ground(), [3.0; 3], standard()) +} + +fn box_cutter() -> Result { + let frame = ground(); + let origin = frame.point([1.5, 0.5, 0.5]); + primitives::cuboid( + "box-cutter".into(), + moved(frame, origin), + [3.0; 3], + standard(), + ) +} + +fn box_inner() -> Result { + let frame = ground(); + let origin = frame.point([1.0; 3]); + primitives::cuboid( + "box-inner".into(), + moved(frame, origin), + [1.0; 3], + standard(), + ) +} + +fn sphere_host() -> Result { + primitives::sphere("sphere-host".into(), ground(), 2.0, standard()) +} + +fn sphere_cutter() -> Result { + let frame = ground(); + let origin = frame.point([0.7, 0.0, 0.0]); + primitives::sphere( + "sphere-cutter".into(), + moved(frame, origin), + 1.0, + standard(), + ) +} + +const FIXTURES: [(&str, Build, Build, BooleanOp); 5] = [ + ("box-union", box_host, box_cutter, BooleanOp::Union), + ( + "box-intersection", + box_host, + box_cutter, + BooleanOp::Intersection, + ), + ("box-cut", box_host, box_cutter, BooleanOp::Subtraction), + ("box-cavity", box_host, box_inner, BooleanOp::Subtraction), + ( + "sphere-cut", + sphere_host, + sphere_cutter, + BooleanOp::Subtraction, + ), +]; + +pub(crate) fn cases() -> Vec { + FIXTURES + .iter() + .map(|&(name, a, b, operation)| { + let pair = operands(move || Ok((a()?, b()?))); + boolean(&format!("booleans.fixture.{name}"), name, operation, &pair) + }) + .collect() +} diff --git a/main/opengeometry/tests/golden/booleans_curved.rs b/main/opengeometry/tests/golden/booleans_curved.rs new file mode 100644 index 0000000..4ba3e68 --- /dev/null +++ b/main/opengeometry/tests/golden/booleans_curved.rs @@ -0,0 +1,235 @@ +use crate::boolean_case::{boolean, operands, stage, Operands}; +use crate::fine_solids::cuboid_at; +use crate::fixtures::{at, fine, rectangle}; +use crate::kernel::{primitives, BooleanOp, BrepEnvelope, Frame3, GeometryError}; +use crate::profiles::{quarter_annulus, sector, split_quarter_annulus}; +use crate::record::Failure; +use crate::runner::Case; +use std::f64::consts::FRAC_PI_2; + +fn prism( + name: &str, + bottom: f64, + outer: Vec<[f64; 2]>, + height: f64, +) -> Result { + primitives::linear_extrusion( + name.into(), + at([0.0, 0.0, bottom]), + outer, + Vec::new(), + height, + fine(), + ) +} + +fn quarter(name: &str, inner: f64) -> Result { + primitives::arc_edged_extrusion( + name.into(), + Frame3::IDENTITY, + quarter_annulus(2.0, inner), + 3.0, + fine(), + ) +} + +fn curved_subtraction(name: &str, id: &str, pair: Operands) -> Case { + boolean( + &format!("booleans.curved.{name}"), + id, + BooleanOp::Subtraction, + &pair, + ) +} + +fn ring_sectors() -> Vec { + let ring = || { + primitives::annular_cylinder("ring-host".into(), Frame3::IDENTITY, 1.9, 2.1, 3.0, fine()) + }; + let first = || prism("ring-opening", 0.5, sector(2.3, -0.1, 0.1), 2.0); + let chained = operands(move || { + let cut = stage(&ring()?, &first()?, BooleanOp::Subtraction, "ring-cut")?; + let second = prism( + "second-ring-opening", + 0.8, + sector(2.3, FRAC_PI_2 - 0.1, FRAC_PI_2 + 0.1), + 1.3, + )?; + Ok((cut, second)) + }); + vec![ + curved_subtraction( + "ring-sector", + "ring-cut", + operands(move || Ok((ring()?, first()?))), + ), + curved_subtraction("ring-sector-chained", "ring-cut-chained", chained), + ] +} + +fn arc_host_openings() -> Vec { + let opening = || cuboid_at("opening", [1.1, 0.9, 0.5], [1.0, 0.35, 1.5]); + let second = || cuboid_at("second-opening", [0.6, 1.3, 0.8], [0.45, 0.8, 1.2]); + let chained = operands(move || { + let cut = stage( + &quarter("arc-host", 1.5)?, + &opening()?, + BooleanOp::Subtraction, + "arc-cut", + )?; + Ok((cut, second()?)) + }); + let third = operands(move || { + let cut = stage( + &quarter("arc-host", 1.5)?, + &opening()?, + BooleanOp::Subtraction, + "arc-cut", + )?; + let chained = stage(&cut, &second()?, BooleanOp::Subtraction, "arc-cut-chained")?; + Ok(( + chained, + cuboid_at("overlapping-opening", [1.4, 0.95, 0.2], [0.5, 0.4, 2.2])?, + )) + }); + let split_arcs = operands(|| { + let host = primitives::arc_edged_extrusion( + "split-arc-host".into(), + Frame3::IDENTITY, + split_quarter_annulus(), + 3.0, + fine(), + )?; + Ok(( + host, + cuboid_at("split-arc-opening", [1.1, 0.9, 0.5], [1.0, 0.35, 1.5])?, + )) + }); + vec![ + curved_subtraction( + "arc-host-opening", + "arc-cut", + operands(move || Ok((quarter("arc-host", 1.5)?, opening()?))), + ), + curved_subtraction("arc-host-chained", "arc-cut-chained", chained), + curved_subtraction("arc-host-third", "arc-cut-third", third), + curved_subtraction("split-arc-provenance", "split-arc-cut", split_arcs), + ] +} + +fn cavities() -> Vec { + let host = || quarter("curved-cavity-host", 1.0); + let interior = || { + prism( + "curved-interior-cutter", + 1.0, + rectangle([1.0, 1.0], [1.2, 1.2]), + 1.0, + ) + }; + let splitter = || prism("curved-split-sector", -1.0, sector(3.0, 0.2, 0.3), 5.0); + let other = || { + prism( + "curved-other-component-cutter", + 1.1, + rectangle([1.55, 0.1], [1.67, 0.22]), + 0.8, + ) + }; + let split_with_cavity = move || -> Result { + let split = stage( + &host()?, + &splitter()?, + BooleanOp::Subtraction, + "curved-split", + )?; + stage( + &split, + &interior()?, + BooleanOp::Subtraction, + "curved-split-with-cavity", + ) + }; + let split_then_cavity = operands(move || { + let split = stage( + &host()?, + &splitter()?, + BooleanOp::Subtraction, + "curved-split", + )?; + Ok((split, interior()?)) + }); + let same_component = operands(move || { + let cavity = stage( + &host()?, + &interior()?, + BooleanOp::Subtraction, + "curved-cavity", + )?; + Ok((cavity, other()?)) + }); + vec![ + curved_subtraction( + "internal-cavity", + "curved-cavity", + operands(move || Ok((host()?, interior()?))), + ), + curved_subtraction( + "split-sector", + "curved-split", + operands(move || Ok((host()?, splitter()?))), + ), + curved_subtraction( + "split-with-cavity", + "curved-split-with-cavity", + split_then_cavity, + ), + curved_subtraction( + "same-component-cavities", + "curved-same-component-cavities", + same_component, + ), + curved_subtraction( + "two-cavities", + "curved-two-cavities", + operands(move || Ok((split_with_cavity()?, other()?))), + ), + ] +} + +fn split_caps() -> Vec { + let host = || quarter("split-cap-arc-host", 1.5); + let first = || prism("full-height-sector", -1.0, sector(3.0, 0.7, 0.9), 5.0); + let second = operands(move || { + let cut = stage( + &host()?, + &first()?, + BooleanOp::Subtraction, + "split-cap-first", + )?; + Ok(( + cut, + prism("lower_cutout-sector", 0.5, sector(3.0, 0.2, 0.3), 1.5)?, + )) + }); + vec![ + curved_subtraction( + "split-cap-first", + "split-cap-first", + operands(move || Ok((host()?, first()?))), + ), + curved_subtraction("split-cap-second", "split-cap-second", second), + ] +} + +pub(crate) fn cases() -> Vec { + [ + ring_sectors(), + arc_host_openings(), + cavities(), + split_caps(), + ] + .into_iter() + .flatten() + .collect() +} diff --git a/main/opengeometry/tests/golden/booleans_cylinders.rs b/main/opengeometry/tests/golden/booleans_cylinders.rs new file mode 100644 index 0000000..5ae178d --- /dev/null +++ b/main/opengeometry/tests/golden/booleans_cylinders.rs @@ -0,0 +1,319 @@ +use crate::boolean_case::{boolean, every_operation, operands, swapped, Operands}; +use crate::fixtures::{accuracy, at, fine, ground, moved, reversed_z, tilted_about_y}; +use crate::kernel::{placed, primitives, BooleanOp, BrepEnvelope, Frame3, GeometryError, Point3}; +use crate::runner::Case; + +const SIZE: [f64; 3] = [4.0, 3.0, 2.0]; + +fn cylinder( + name: &str, + frame: Frame3, + radius: f64, + height: f64, +) -> Result { + primitives::cylinder(name.into(), frame, radius, height, fine()) +} + +fn host_box() -> Result { + primitives::cuboid("box".into(), Frame3::IDENTITY, SIZE, fine()) +} + +fn unit_axis(axis: usize, sign: f64) -> Point3 { + std::array::from_fn(|coordinate| if coordinate == axis { sign } else { 0.0 }) +} + +fn full_family(prefix: &str, id: &str, pair: &Operands) -> Vec { + let mut cases = every_operation(prefix, id, pair); + let reverse = format!("{prefix}.reversed-subtraction"); + cases.push(boolean( + &reverse, + &format!("{id}-reversed"), + BooleanOp::Subtraction, + &swapped(pair), + )); + cases +} + +fn through_axes() -> Vec { + (0..3) + .flat_map(|axis| { + let pair = operands(move || { + let mut origin = [2.0, 1.5, 1.0]; + origin[axis] = -1.0; + let frame = Frame3::from_axis( + origin, + unit_axis(axis, 1.0), + unit_axis((axis + 1) % 3, 1.0), + )?; + Ok(( + host_box()?, + cylinder("round-opening", frame, 0.4, SIZE[axis] + 2.0)?, + )) + }); + full_family( + &format!("booleans.cylinder-box.through-{axis}"), + &format!("through-{axis}"), + &pair, + ) + }) + .collect() +} + +fn blind_pockets() -> Vec { + let mut cases = Vec::new(); + for axis in 0..3 { + for (side, low) in [("low", true), ("high", false)] { + let pair = operands(move || { + let mut origin = [2.0, 1.5, 1.0]; + origin[axis] = if low { -1.0 } else { SIZE[axis] + 1.0 }; + let direction = unit_axis(axis, if low { 1.0 } else { -1.0 }); + let frame = Frame3::from_axis(origin, direction, unit_axis((axis + 1) % 3, 1.0))?; + Ok((host_box()?, cylinder("pocket", frame, 0.4, 1.75)?)) + }); + let prefix = format!("booleans.cylinder-box.pocket-{axis}-{side}"); + cases.extend(full_family( + &prefix, + &format!("pocket-{axis}-{side}"), + &pair, + )); + } + } + cases +} + +fn through_cut() -> Vec { + let pair = operands(|| { + let cut_accuracy = accuracy(4e-8, 1e-8, 0.01, 1e-5); + let host = + primitives::cylinder("host".into(), at([0.0, 0.0, -2.0]), 1.0, 4.0, cut_accuracy)?; + let frame = Frame3::from_axis([-2.0, 0.0, 0.2], [1.0, 0.0, 0.0], [0.0, 0.0, 1.0])?; + let cutter = primitives::cylinder("cutter".into(), frame, 0.6, 4.0, cut_accuracy)?; + Ok((host, cutter)) + }); + vec![boolean( + "booleans.cylinders.nonparallel-through-cut", + "through-cut", + BooleanOp::Subtraction, + &pair, + )] +} + +fn coaxial() -> Vec { + let split = operands(|| { + Ok(( + cylinder("a", at([0.0; 3]), 1.0, 4.0)?, + cylinder("b", reversed_z([0.0, 0.0, 3.0]), 1.0, 2.0)?, + )) + }); + let contact = operands(|| { + Ok(( + cylinder("a", at([0.0; 3]), 1.0, 4.0)?, + cylinder("b", at([0.0, 0.0, 4.0]), 1.0, 2.0)?, + )) + }); + let near = operands(|| { + Ok(( + cylinder("a", at([0.0; 3]), 1.0, 4.0)?, + cylinder("near", at([0.0, 0.0, 4.0 + 2e-9]), 1.0, 2.0)?, + )) + }); + let split_y = operands(|| { + let host = cylinder("host", ground(), 1.0, 2.0)?; + let cutter = placed( + &cylinder("cutter", ground(), 1.0, 0.8)?, + at([0.0, 0.6, 0.0]), + 1.0, + )?; + Ok((host, cutter)) + }); + let mut cases = every_operation("booleans.cylinders.coaxial-split", "coaxial", &split); + cases.extend(every_operation( + "booleans.cylinders.coaxial-contact", + "contact", + &contact, + )); + cases.push(boolean( + "booleans.cylinders.coaxial-near-gap", + "near", + BooleanOp::Union, + &near, + )); + cases.push(boolean( + "booleans.cylinders.translated-y-split", + "result", + BooleanOp::Subtraction, + &split_y, + )); + cases +} + +fn radial() -> Vec { + let coextensive = operands(|| { + Ok(( + cylinder("outer", at([0.0; 3]), 2.0, 3.0)?, + cylinder("inner", at([0.0; 3]), 1.0, 3.0)?, + )) + }); + let shifted = operands(|| { + Ok(( + cylinder("outer", at([0.0; 3]), 2.0, 3.0)?, + cylinder("shifted", at([0.0, 0.0, 0.5]), 1.0, 3.0)?, + )) + }); + let enclosed = operands(|| { + Ok(( + cylinder("host", at([0.0; 3]), 2.0, 4.0)?, + cylinder("cutter", at([0.0, 0.0, 1.0]), 0.75, 2.0)?, + )) + }); + let mut cases = full_family( + "booleans.cylinders.coextensive", + "coextensive", + &coextensive, + ); + cases.push(boolean( + "booleans.cylinders.coextensive-shifted", + "gap", + BooleanOp::Subtraction, + &shifted, + )); + cases.extend(full_family( + "booleans.cylinders.enclosed", + "enclosed", + &enclosed, + )); + cases +} + +fn transverse() -> Vec { + let neighbour = |name: &'static str, frame: Frame3| { + operands(move || { + Ok(( + cylinder("a", Frame3::IDENTITY, 1.0, 2.0)?, + cylinder(name, frame, 1.0, 2.0)?, + )) + }) + }; + let mut cases = every_operation( + "booleans.cylinders.transverse", + "transverse", + &neighbour("b", at([1.0, 0.0, 0.0])), + ); + let reversed = neighbour("reversed", reversed_z([1.0, 0.0, 2.0])); + cases.push(boolean( + "booleans.cylinders.transverse-reversed", + "reversed-result", + BooleanOp::Intersection, + &reversed, + )); + let shifted = neighbour("shifted", at([1.0, 0.0, 0.25])); + cases.push(boolean( + "booleans.cylinders.transverse-shifted", + "gap", + BooleanOp::Union, + &shifted, + )); + cases.extend(every_operation( + "booleans.cylinders.tangent", + "contact", + &neighbour("tangent", at([2.0, 0.0, 0.0])), + )); + let near = neighbour("near-tangent", at([2.0 + 2e-9, 0.0, 0.0])); + cases.push(boolean( + "booleans.cylinders.near-tangent", + "near-tangent-result", + BooleanOp::Union, + &near, + )); + let diagonal = neighbour("diagonal", at([1.5, 1.5, 0.0])); + cases.push(boolean( + "booleans.cylinders.diagonal", + "diagonal-union", + BooleanOp::Union, + &diagonal, + )); + cases +} + +fn parallel() -> Vec { + let eccentric = operands(|| { + Ok(( + cylinder("outer", Frame3::IDENTITY, 2.0, 2.0)?, + cylinder("inner", at([0.75, 0.0, 0.0]), 0.5, 2.0)?, + )) + }); + let touching = operands(|| { + Ok(( + cylinder("outer", Frame3::IDENTITY, 2.0, 2.0)?, + cylinder("touching", at([1.5, 0.0, 0.0]), 0.5, 2.0)?, + )) + }); + let partial = operands(|| { + Ok(( + cylinder("host", Frame3::IDENTITY, 1.0, 3.0)?, + cylinder("short", at([1.0, 0.0, 1.0]), 1.0, 1.0)?, + )) + }); + let through = operands(|| { + Ok(( + cylinder("host", Frame3::IDENTITY, 1.0, 3.0)?, + cylinder("through", at([1.0, 0.0, -1.0]), 1.0, 5.0)?, + )) + }); + let mut cases = every_operation("booleans.cylinders.eccentric", "eccentric", &eccentric); + cases.push(boolean( + "booleans.cylinders.eccentric-touching", + "touching-result", + BooleanOp::Subtraction, + &touching, + )); + cases.extend(every_operation( + "booleans.cylinders.partial-span", + "partial", + &partial, + )); + cases.push(boolean( + "booleans.cylinders.partial-span.reversed-union", + "reversed-partial-union", + BooleanOp::Union, + &swapped(&partial), + )); + cases.push(boolean( + "booleans.cylinders.partial-through", + "through-cut", + BooleanOp::Subtraction, + &through, + )); + cases +} + +fn placed_roundoff() -> Vec { + let pair = operands(|| { + let source = cylinder("c", moved(ground(), [0.0, 1.2, 0.0]), 1.0, 2.0)?; + let frame = tilted_about_y([0.2, 0.1, 0.2], std::f64::consts::PI / 7.0); + let host = placed(&source, frame, 1.25)?; + let inner = placed( + &cylinder("inner", moved(ground(), [0.0, 1.2, 0.0]), 0.5, 2.0)?, + frame, + 1.25, + )?; + Ok((host, inner)) + }); + every_operation("booleans.cylinders.placed-roundoff", "placed", &pair) +} + +pub(crate) fn cases() -> Vec { + [ + through_axes(), + blind_pockets(), + through_cut(), + coaxial(), + radial(), + transverse(), + parallel(), + placed_roundoff(), + ] + .into_iter() + .flatten() + .collect() +} diff --git a/main/opengeometry/tests/golden/booleans_direct.rs b/main/opengeometry/tests/golden/booleans_direct.rs new file mode 100644 index 0000000..0c9076f --- /dev/null +++ b/main/opengeometry/tests/golden/booleans_direct.rs @@ -0,0 +1,316 @@ +use crate::boolean_case::{boolean, operands, record_operands, result_body, Operands, OPERATIONS}; +use crate::fine_solids::sphere_at; +use crate::fixtures::{ + accuracy, at, fine, ground, moved, scaled_accuracy, tilted_about_y, y_axis_frame, +}; +use crate::kernel::{ + boolean_boxes, boolean_spheres, placed, primitives, Accuracy, BooleanOp, BooleanResult, + BrepEnvelope, Frame3, GeometryError, Point3, +}; +use crate::runner::Case; +use std::sync::Arc; + +type Direct = + fn(&BrepEnvelope, &BrepEnvelope, BooleanOp, String) -> Result; + +fn direct(name: &str, operation: BooleanOp, pair: &Operands, call: Direct) -> Case { + let pair = Arc::clone(pair); + Case::new(name, move |record| { + let (a, b) = pair()?; + record_operands(record, &a, &b); + record.section("direct"); + record.debug("operation", operation); + result_body(record, call(&a, &b, operation, "r".into())); + Ok(()) + }) +} + +fn every_direct(prefix: &str, pair: &Operands, call: Direct) -> Vec { + OPERATIONS + .iter() + .map(|(label, operation)| direct(&format!("{prefix}.{label}"), *operation, pair, call)) + .collect() +} + +fn spheres(a: (Point3, f64), b: (Point3, f64)) -> Operands { + operands(move || Ok((sphere_at("a", a.0, a.1)?, sphere_at("b", b.0, b.1)?))) +} + +type SpherePair = (&'static str, BooleanOp, (Point3, f64), (Point3, f64)); + +const ORIGIN: Point3 = [0.0; 3]; + +const SPHERE_PAIRS: [SpherePair; 9] = [ + ( + "enclosed", + BooleanOp::Subtraction, + (ORIGIN, 2.0), + ([0.2, 0.0, 0.0], 0.5), + ), + ( + "contact", + BooleanOp::Union, + (ORIGIN, 1.0), + ([2.0, 0.0, 0.0], 1.0), + ), + ( + "disjoint", + BooleanOp::Union, + (ORIGIN, 1.0), + ([2.5, 0.0, 0.0], 1.0), + ), + ("coincident", BooleanOp::Union, (ORIGIN, 1.0), (ORIGIN, 1.0)), + ( + "inside-big.subtraction", + BooleanOp::Subtraction, + (ORIGIN, 1.0), + (ORIGIN, 2.0), + ), + ( + "inside-big.intersection", + BooleanOp::Intersection, + (ORIGIN, 1.0), + (ORIGIN, 2.0), + ), + ( + "internal-tangency", + BooleanOp::Subtraction, + (ORIGIN, 2.0), + ([1.0, 0.0, 0.0], 1.0), + ), + ( + "small-cap", + BooleanOp::Intersection, + (ORIGIN, 1.0), + ([2.0 - 1e-10, 0.0, 0.0], 1.0), + ), + ( + "rotated-unequal", + BooleanOp::Subtraction, + ([5.0, -3.0, 8.0], 2.0), + ([6.0, -2.0, 9.0], 1.25), + ), +]; + +fn sphere_cases() -> Vec { + let transverse = spheres((ORIGIN, 1.0), ([1.0, 0.0, 0.0], 1.0)); + let prefix = "booleans.spheres-direct"; + let mut cases = every_direct( + &format!("{prefix}.transverse"), + &transverse, + boolean_spheres, + ); + for (label, operation) in OPERATIONS { + let name = format!("booleans.spheres.transverse.{label}"); + cases.push(boolean( + &name, + &format!("r-{label}"), + operation, + &transverse, + )); + } + for (name, operation, a, b) in SPHERE_PAIRS { + let pair = spheres(a, b); + cases.push(direct( + &format!("{prefix}.{name}"), + operation, + &pair, + boolean_spheres, + )); + } + let enclosed = spheres((ORIGIN, 2.0), ([0.2, 0.0, 0.0], 0.5)); + let subtract = BooleanOp::Subtraction; + cases.push(boolean( + "booleans.spheres.enclosed", + "r", + subtract, + &enclosed, + )); + let cylinder = operands(|| { + let cylinder = primitives::cylinder("c".into(), Frame3::IDENTITY, 1.0, 2.0, fine())?; + Ok((sphere_at("a", ORIGIN, 1.0)?, cylinder)) + }); + let name = format!("{prefix}.unsupported-cylinder"); + cases.push(direct(&name, BooleanOp::Union, &cylinder, boolean_spheres)); + cases.extend(scaled_spheres()); + cases +} + +fn scaled_spheres() -> Vec { + let mut cases = Vec::new(); + for (label, size) in [("micro", 1e-6), ("unit", 1.0), ("mega", 1e6)] { + let pair = operands(move || { + let a = primitives::sphere("a".into(), Frame3::IDENTITY, size, scaled_accuracy(size))?; + let b = primitives::sphere( + "b".into(), + at([size, 0.0, 0.0]), + size, + scaled_accuracy(size), + )?; + Ok((a, b)) + }); + cases.push(direct( + &format!("booleans.spheres-direct.scaled-{label}"), + BooleanOp::Subtraction, + &pair, + boolean_spheres, + )); + } + let far = operands(|| { + let far_accuracy = accuracy(1e-6, 5e-7, 0.01, 1e-5); + let a = primitives::sphere("a".into(), at([1e9; 3]), 1.0, far_accuracy)?; + let b = primitives::sphere("b".into(), at([1e9 + 1.0, 1e9, 1e9]), 1.0, far_accuracy)?; + Ok((a, b)) + }); + cases.push(direct( + "booleans.spheres-direct.far-coordinates", + BooleanOp::Intersection, + &far, + boolean_spheres, + )); + let placed_pair = operands(|| { + let placement = tilted_about_y([0.2, 0.1, 0.2], std::f64::consts::PI / 7.0); + let frame = moved(ground(), [0.0, 1.2, 0.0]); + let first = primitives::sphere("a".into(), frame, 1.0, fine())?; + let second = primitives::sphere("b".into(), moved(frame, [1.0, 1.2, 0.0]), 1.0, fine())?; + Ok(( + placed(&first, placement, 1.25)?, + placed(&second, placement, 1.25)?, + )) + }); + cases.extend(every_direct( + "booleans.spheres-direct.placed", + &placed_pair, + boolean_spheres, + )); + cases +} + +fn box_accuracy() -> Accuracy { + accuracy(1e-8, 1e-9, 1e-3, 1e-6) +} + +fn box_at(name: &str, origin: Point3, size: Point3) -> Result { + primitives::cuboid(name.into(), at(origin), size, box_accuracy()) +} + +fn boxes(a: (&'static str, Point3, Point3), b: (&'static str, Point3, Point3)) -> Operands { + operands(move || Ok((box_at(a.0, a.1, a.2)?, box_at(b.0, b.1, b.2)?))) +} + +fn box_cases() -> Vec { + let aligned = boxes( + ("host", [0.0; 3], [2.0; 3]), + ("cutter", [1.0, 0.5, 0.5], [2.0; 3]), + ); + let mut cases = every_direct("booleans.boxes-direct.aligned", &aligned, boolean_boxes); + let cavity = boxes(("host", [0.0; 3], [3.0; 3]), ("cavity", [1.0; 3], [1.0; 3])); + cases.push(direct( + "booleans.boxes-direct.cavity", + BooleanOp::Subtraction, + &cavity, + boolean_boxes, + )); + let through = boxes( + ("host", [0.0; 3], [3.0; 3]), + ("through", [1.0, -1.0, -1.0], [1.0, 5.0, 5.0]), + ); + cases.push(direct( + "booleans.boxes-direct.through", + BooleanOp::Subtraction, + &through, + boolean_boxes, + )); + for (label, origin) in [ + ("face", [3.0, 0.0, 0.0]), + ("edge", [3.0, 3.0, 0.0]), + ("corner", [3.0; 3]), + ] { + let pair = boxes(("host", [0.0; 3], [3.0; 3]), ("contact", origin, [3.0; 3])); + cases.push(direct( + &format!("booleans.boxes-direct.contact-{label}.union"), + BooleanOp::Union, + &pair, + boolean_boxes, + )); + cases.push(direct( + &format!("booleans.boxes-direct.contact-{label}.intersection"), + BooleanOp::Intersection, + &pair, + boolean_boxes, + )); + } + let identity = boxes(("a", [0.0; 3], [2.0; 3]), ("a", [0.0; 3], [2.0; 3])); + for (label, operation) in OPERATIONS { + cases.push(boolean( + &format!("booleans.boxes.identity.{label}"), + "identity", + operation, + &identity, + )); + } + for (label, factor) in [("tiny", 1e-8), ("unit", 1.0), ("large", 1e5)] { + let pair = operands(move || { + let frame = y_axis_frame([0.0; 3])?; + let a = placed(&box_at("a", [0.0; 3], [2.0; 3])?, frame, factor)?; + let b = placed(&box_at("b", [1.0, 0.5, 0.5], [2.0; 3])?, frame, factor)?; + Ok((a, b)) + }); + cases.push(direct( + &format!("booleans.boxes-direct.scaled-{label}"), + BooleanOp::Subtraction, + &pair, + boolean_boxes, + )); + } + cases.extend(box_errors()); + cases +} + +fn box_errors() -> Vec { + let similar = operands(|| { + let frame = y_axis_frame([5.0, 6.0, 7.0])?; + let a = placed(&box_at("a", [0.0; 3], [2.0; 3])?, frame, 1.25)?; + let b = placed(&box_at("b", [1.0, 0.5, 0.5], [2.0; 3])?, frame, 1.25)?; + Ok((a, b)) + }); + let mixed_axes = operands(|| { + let a = placed( + &box_at("a", [0.0; 3], [2.0; 3])?, + y_axis_frame([5.0, 6.0, 7.0])?, + 1.25, + )?; + Ok((a, box_at("different-axes", [0.0; 3], [2.0; 3])?)) + }); + let unresolved = boxes( + ("a", [0.0; 3], [2.0; 3]), + ("b", [2.0 + 2.0 * 1e-8, 0.0, 0.0], [2.0; 3]), + ); + vec![ + direct( + "booleans.boxes-direct.placed-similarity", + BooleanOp::Subtraction, + &similar, + boolean_boxes, + ), + direct( + "booleans.boxes-direct.different-axes", + BooleanOp::Union, + &mixed_axes, + boolean_boxes, + ), + direct( + "booleans.boxes-direct.unresolved-gap", + BooleanOp::Union, + &unresolved, + boolean_boxes, + ), + ] +} + +pub(crate) fn cases() -> Vec { + [sphere_cases(), box_cases()] + .into_iter() + .flatten() + .collect() +} diff --git a/main/opengeometry/tests/golden/booleans_oblique.rs b/main/opengeometry/tests/golden/booleans_oblique.rs new file mode 100644 index 0000000..844fe22 --- /dev/null +++ b/main/opengeometry/tests/golden/booleans_oblique.rs @@ -0,0 +1,155 @@ +use crate::boolean_case::{boolean, operands, stage, table, Built, Entry}; +use crate::fixtures::{rectangle, tilted_about_x, tilted_about_y}; +use crate::kernel::BooleanOp; +use crate::planar_shapes::{angled, extrusion, framed, holed, oblique, span, square, wall}; +use crate::runner::Case; + +const SUBTRACT: BooleanOp = BooleanOp::Subtraction; + +fn tall_cutter_profile() -> Vec<[f64; 2]> { + rectangle([-0.5, -2.0], [0.5, 2.0]) +} + +fn single() -> Built { + let cutter = oblique("oblique-cutter", [2.5, 0.15, 0.0], 1.0, square(0.5))?; + Ok((angled("oblique-host")?, cutter)) +} + +fn chained() -> Built { + let first = oblique("first-oblique", [2.5, 0.15, 0.0], 1.0, square(0.5))?; + let once = stage(&angled("chained-oblique-host")?, &first, SUBTRACT, "once")?; + Ok(( + once, + oblique("second-oblique", [5.0, 0.15, 0.0], -1.0, square(0.5))?, + )) +} + +fn disconnected() -> Built { + let through = span("through", -1.0, 5.0, rectangle([3.0, -1.0], [4.0, 1.0]))?; + let split = stage(&wall("split-host", 10.0)?, &through, SUBTRACT, "split")?; + Ok(( + split, + oblique("split-oblique", [6.0, 0.15, 0.0], 1.0, square(0.5))?, + )) +} + +fn profile_hole() -> Built { + let hole = rectangle([2.0, 1.0], [3.0, 2.0]); + let host = holed( + "profile-hole-host", + 0.0, + 3.0, + rectangle([0.0, 0.0], [10.0, 3.0]), + vec![hole], + )?; + Ok(( + host, + oblique("pocket-oblique", [6.0, 1.5, 0.0], 1.0, square(0.5))?, + )) +} + +fn nonconvex() -> Built { + let outer = vec![ + [-1.0, -1.0], + [1.0, -1.0], + [1.0, -0.05], + [0.0, -0.05], + [0.0, 1.0], + [-1.0, 1.0], + ]; + let cutter = oblique("nonconvex-cutter", [5.0, 0.15, 0.0], 1.0, outer)?; + Ok((wall("nonconvex-host", 10.0)?, cutter)) +} + +fn after_cavity() -> Built { + let host = extrusion("cavity-host", rectangle([0.0, 0.0], [10.0, 3.0]))?; + let cavity = span( + "internal-cavity", + 1.0, + 1.0, + rectangle([2.0, 1.0], [3.0, 2.0]), + )?; + let host = stage(&host, &cavity, SUBTRACT, "cavity-host")?; + Ok(( + host, + oblique( + "oblique-after-cavity", + [6.0, 1.5, 0.0], + 1.0, + tall_cutter_profile(), + )?, + )) +} + +fn remote_cavity() -> Built { + let left = extrusion("left-cavity-host", rectangle([0.0, 0.0], [4.0, 3.0]))?; + let cavity = span("left-cavity", 1.0, 1.0, rectangle([1.0, 1.0], [2.0, 2.0]))?; + let left = stage(&left, &cavity, SUBTRACT, "left-with-cavity")?; + let right = extrusion("right-host", rectangle([6.0, 0.0], [10.0, 3.0]))?; + let host = stage(&left, &right, BooleanOp::Union, "two-components")?; + Ok(( + host, + oblique( + "right-oblique-cutter", + [8.5, 1.5, 0.0], + 1.0, + tall_cutter_profile(), + )?, + )) +} + +const ENTRIES: [Entry; 7] = [ + ("cutter", "oblique-cut", SUBTRACT, single), + ("chained", "twice", SUBTRACT, chained), + ( + "disconnected-host", + "split-oblique-result", + SUBTRACT, + disconnected, + ), + ("profile-hole-host", "two-voids", SUBTRACT, profile_hole), + ( + "nonconvex-cutter", + "nonconvex-oblique-cut", + SUBTRACT, + nonconvex, + ), + ("after-cavity", "oblique-cavity-cut", SUBTRACT, after_cavity), + ( + "remote-cavity", + "remote-cavity-cut", + SUBTRACT, + remote_cavity, + ), +]; + +fn tilt(axis: usize, degrees: f64) -> Case { + let pair = operands(move || { + let angle = degrees.to_radians(); + let frame = if axis == 0 { + tilted_about_y([2.5, 0.15, 0.0], angle) + } else { + tilted_about_x([2.5, 0.15, 0.0], angle) + }; + let cutter = framed(&format!("tilt-{axis}-{degrees}"), frame, square(0.8), 2.1)?; + Ok((wall("tilt-sweep-host", 10.0)?, cutter)) + }); + let sign = if degrees < 0.0 { "minus" } else { "plus" }; + let name = format!("booleans.oblique.tilt-{axis}-{sign}-{}", degrees.abs()); + boolean( + &name, + &format!("tilt-result-{axis}-{degrees}"), + SUBTRACT, + &pair, + ) +} + +pub(crate) fn cases() -> Vec { + let mut cases = table("booleans.oblique", &ENTRIES); + for axis in 0..2 { + for degrees in [-25.0, -15.0, -5.0, 5.0, 15.0, 25.0] { + cases.push(tilt(axis, degrees)); + } + } + cases +} diff --git a/main/opengeometry/tests/golden/booleans_planar.rs b/main/opengeometry/tests/golden/booleans_planar.rs new file mode 100644 index 0000000..c6338c3 --- /dev/null +++ b/main/opengeometry/tests/golden/booleans_planar.rs @@ -0,0 +1,225 @@ +use crate::boolean_case::{families, stage, table, Built, Entry, Family}; +use crate::fixtures::{fine, ground, moved, rectangle, turned_about_z, upright}; +use crate::kernel::{primitives, BooleanOp}; +use crate::planar_shapes::{angled, extrusion, framed, opening, span, square, wall, Shape}; +use crate::runner::Case; +use std::f64::consts::FRAC_PI_6; + +const SUBTRACT: BooleanOp = BooleanOp::Subtraction; + +fn coextensive() -> Built { + let a = extrusion("a", rectangle([0.0, 0.0], [4.0, 4.0]))?; + Ok((a, extrusion("b", rectangle([2.0, -1.0], [5.0, 2.0]))?)) +} + +fn axial_base() -> Shape { + span("a", 0.0, 4.0, rectangle([0.0, 0.0], [4.0, 4.0])) +} + +fn axial() -> Built { + let reversed = vec![[4.0, 4.0], [0.0, 4.0], [0.0, 0.0], [4.0, 0.0]]; + Ok((axial_base()?, span("b", 2.0, 4.0, reversed)?)) +} + +fn axial_embedded() -> Built { + let embedded = span("embedded", 1.0, 2.0, rectangle([0.0, 0.0], [4.0, 4.0]))?; + Ok((axial_base()?, embedded)) +} + +fn axial_disjoint() -> Built { + let disjoint = span("disjoint", 6.0, 2.0, rectangle([0.0, 0.0], [4.0, 4.0]))?; + Ok((axial_base()?, disjoint)) +} + +fn square_host() -> Shape { + extrusion("host", rectangle([0.0, 0.0], [10.0, 10.0])) +} + +fn through_hole() -> Built { + Ok(( + square_host()?, + extrusion("inner", rectangle([4.0, 4.0], [6.0, 6.0]))?, + )) +} + +fn band_split() -> Built { + Ok(( + square_host()?, + extrusion("band", rectangle([-1.0, 4.0], [11.0, 6.0]))?, + )) +} + +fn lower_cutout() -> Shape { + opening("lower_cutout", 0.0, 2.1, 2.0, 3.0) +} + +fn raised_cutout() -> Shape { + opening("raised_cutout", 0.8, 1.2, 6.0, 7.0) +} + +fn flush_lower() -> Built { + Ok((wall("host", 10.0)?, lower_cutout()?)) +} + +fn flush_chained() -> Built { + let (host, cutter) = flush_lower()?; + let cut = stage(&host, &cutter, SUBTRACT, "one-lower_cutout")?; + Ok((cut, raised_cutout()?)) +} + +fn mitered_first() -> Built { + let outer = vec![ + [-0.16, -0.14], + [0.16, -0.46], + [13.84, -0.46], + [14.16, -0.14], + ]; + let host = span("mitered-host", 0.0, 3.8, outer)?; + let lower = vec![[1.2, -0.14], [2.8, -0.14], [2.8, -0.46], [1.2, -0.46]]; + Ok((host, span("lower_cutout", 0.0, 2.45, lower)?)) +} + +fn mitered_chained() -> Built { + let (host, cutter) = mitered_first()?; + let first = stage(&host, &cutter, SUBTRACT, "lower_cutout-cut")?; + let raised = vec![ + [3.625, -0.14], + [5.175, -0.14], + [5.175, -0.46], + [3.625, -0.46], + ]; + Ok((first, span("raised_cutout", 0.3, 2.8, raised)?)) +} + +fn angled_lower() -> Built { + Ok((angled("angled-host")?, lower_cutout()?)) +} + +fn angled_chained() -> Built { + let (host, cutter) = angled_lower()?; + let cut = stage(&host, &cutter, SUBTRACT, "angled-cut")?; + Ok((cut, raised_cutout()?)) +} + +fn angled_full_height() -> Built { + let through = extrusion("through-lower_cutout", rectangle([4.0, 0.0], [5.0, 0.3]))?; + Ok((angled("angled-host")?, through)) +} + +fn angled_twice() -> Built { + let first = opening("first", 0.0, 2.1, 2.0, 3.0)?; + let once = stage(&angled("angled-host")?, &first, SUBTRACT, "once")?; + Ok((once, opening("second", 0.0, 2.1, 2.5, 3.5)?)) +} + +fn layered_cavity() -> Built { + let outer = vec![ + [0.0, 0.0], + [10.0, 0.0], + [10.0, 10.0], + [0.3, 10.0], + [0.0, 9.8], + ]; + let cavity = span("cavity", 1.0, 1.0, rectangle([4.0, 4.0], [6.0, 6.0]))?; + Ok((extrusion("angled-host", outer)?, cavity)) +} + +fn angled_rotated() -> Built { + let frame = turned_about_z([2.5, 0.15, 0.0], FRAC_PI_6); + let cutter = framed("rotated-cutter", frame, square(0.5), 2.1)?; + Ok((angled("angled-host")?, cutter)) +} + +fn round_through() -> Built { + let frame = moved(ground(), [2.0, -0.5, 1.5]); + let cutter = primitives::cylinder("round-opening".into(), frame, 0.5, 1.3, fine())?; + Ok((wall("round-host", 4.0)?, cutter)) +} + +fn arched_cap() -> Built { + let frame = moved(ground(), [2.0, -0.5, 2.0]); + let cap = primitives::cylinder("arched-cap".into(), frame, 0.5, 1.3, fine())?; + Ok((wall("arched-host", 4.0)?, cap)) +} + +fn arched_chained() -> Built { + let (host, cap) = arched_cap()?; + let capped = stage(&host, &cap, SUBTRACT, "cap")?; + let lower = rectangle([1.5, 0.5], [2.5, 2.0]); + Ok(( + capped, + framed("arched-lower", upright([0.0, 0.8, 0.0]), lower, 1.3)?, + )) +} + +const FAMILIES: [Family; 2] = [ + ("coextensive", "coextensive", coextensive), + ("axial", "axial", axial), +]; + +const ENTRIES: [Entry; 18] = [ + ("through-hole", "cavity", SUBTRACT, through_hole), + ("band-split", "split", SUBTRACT, band_split), + ("axial-embedded", "axial-split", SUBTRACT, axial_embedded), + ( + "axial-disjoint.union", + "axial-disjoint", + BooleanOp::Union, + axial_disjoint, + ), + ( + "axial-disjoint.intersection", + "axial-empty", + BooleanOp::Intersection, + axial_disjoint, + ), + ( + "flush-lower-cutout", + "one-lower_cutout", + SUBTRACT, + flush_lower, + ), + ( + "flush-chained", + "lower_cutout-raised_cutout", + SUBTRACT, + flush_chained, + ), + ("mitered-first", "lower_cutout-cut", SUBTRACT, mitered_first), + ( + "mitered-chained", + "raised_cutout-cut", + SUBTRACT, + mitered_chained, + ), + ("angled-lower-cutout", "angled-cut", SUBTRACT, angled_lower), + ("angled-chained", "angled-chain", SUBTRACT, angled_chained), + ( + "angled-full-height", + "split-angled", + SUBTRACT, + angled_full_height, + ), + ("angled-overlapping-twice", "twice", SUBTRACT, angled_twice), + ("layered-cavity", "cavity-result", SUBTRACT, layered_cavity), + ( + "angled-rotated-cutter", + "rotated-cut", + SUBTRACT, + angled_rotated, + ), + ( + "round-through-cutter", + "round-host-cut", + SUBTRACT, + round_through, + ), + ("arched-cap", "cap", SUBTRACT, arched_cap), + ("arched-chained", "arch", SUBTRACT, arched_chained), +]; + +pub(crate) fn cases() -> Vec { + let mut cases = families("booleans.planar", &FAMILIES); + cases.extend(table("booleans.planar", &ENTRIES)); + cases +} diff --git a/main/opengeometry/tests/golden/booleans_spheres.rs b/main/opengeometry/tests/golden/booleans_spheres.rs new file mode 100644 index 0000000..64387cb --- /dev/null +++ b/main/opengeometry/tests/golden/booleans_spheres.rs @@ -0,0 +1,421 @@ +use crate::boolean_case::{boolean, every_operation, operands, swapped, Operands}; +use crate::fine_solids::{cuboid_at, sphere_at}; +use crate::fixtures::{at, fine}; +use crate::kernel::{primitives, BooleanOp, BrepEnvelope, Frame3, GeometryError, Point3}; +use crate::runner::Case; + +const SIZE: [f64; 3] = [4.0, 3.0, 2.0]; + +fn cylinder( + name: &str, + origin: Point3, + radius: f64, + height: f64, +) -> Result { + primitives::cylinder(name.into(), at(origin), radius, height, fine()) +} + +fn pocket_center(axis: usize, upper: bool, inside: bool) -> Point3 { + let mut center = [2.0, 1.5, 1.0]; + center[axis] = match (upper, inside) { + (false, false) => -0.25, + (false, true) => 0.25, + (true, false) => SIZE[axis] + 0.25, + (true, true) => SIZE[axis] - 0.25, + }; + center +} + +fn pockets() -> Vec { + let mut cases = Vec::new(); + for axis in 0..3 { + for upper in [false, true] { + for inside in [false, true] { + let pair = operands(move || { + let center = pocket_center(axis, upper, inside); + Ok(( + cuboid_at("box", [0.0; 3], SIZE)?, + sphere_at("spherical-pocket", center, 0.75)?, + )) + }); + let label = format!( + "{axis}-{}-{}", + if upper { "upper" } else { "lower" }, + if inside { "inside" } else { "outside" } + ); + let prefix = format!("booleans.sphere-box.pocket-{label}"); + cases.extend(every_operation( + &prefix, + &format!("sphere-pocket-{label}"), + &pair, + )); + let reverse = format!("{prefix}.reversed-subtraction"); + cases.push(boolean( + &reverse, + &format!("sphere-cut-{label}"), + BooleanOp::Subtraction, + &swapped(&pair), + )); + } + } + } + cases +} + +fn sphere_cylinder() -> Vec { + let contained = operands(|| { + Ok(( + cylinder("cylinder", [0.0; 3], 2.0, 4.0)?, + sphere_at("sphere", [0.0, 0.0, 2.0], 0.5)?, + )) + }); + let cavity = operands(|| { + Ok(( + sphere_at("sphere-host", [0.0; 3], 3.0)?, + cylinder("cylinder-cutter", [0.0, 0.0, -0.5], 0.5, 1.0)?, + )) + }); + let crossing = operands(|| { + Ok(( + cylinder("cylinder", [0.0; 3], 2.0, 4.0)?, + sphere_at("crossing", [1.8, 0.0, 2.0], 0.5)?, + )) + }); + let coaxial = operands(|| { + Ok(( + sphere_at("sphere", [0.0; 3], 1.5)?, + cylinder("cylinder", [0.0, 0.0, -2.0], 0.75, 4.0)?, + )) + }); + let noncoaxial = operands(|| { + Ok(( + sphere_at("sphere", [0.0; 3], 1.5)?, + cylinder("noncoaxial", [0.2, 0.0, -2.0], 0.75, 4.0)?, + )) + }); + let mut cases = every_operation( + "booleans.sphere-cylinder.contained", + "contained", + &contained, + ); + cases.push(boolean( + "booleans.sphere-cylinder.spherical-cavity", + "spherical-cavity", + BooleanOp::Subtraction, + &cavity, + )); + cases.extend(every_operation( + "booleans.sphere-cylinder.crossing", + "crossing", + &crossing, + )); + cases.push(boolean( + "booleans.sphere-cylinder.crossing.reversed-subtraction", + "crossing-sphere-cut", + BooleanOp::Subtraction, + &swapped(&crossing), + )); + cases.extend(every_operation( + "booleans.sphere-cylinder.coaxial", + "coaxial", + &coaxial, + )); + cases.push(boolean( + "booleans.sphere-cylinder.coaxial.reversed-subtraction", + "cylinder-cut", + BooleanOp::Subtraction, + &swapped(&coaxial), + )); + cases.push(boolean( + "booleans.sphere-cylinder.noncoaxial", + "gap", + BooleanOp::Union, + &noncoaxial, + )); + cases +} + +fn cuboid_containment() -> Vec { + let box_host = || cuboid_at("box", [-2.0; 3], [4.0; 3]); + let small_box = || cuboid_at("small-box", [-0.5; 3], [1.0; 3]); + let pairs: [(&str, &str, BooleanOp, Operands); 5] = [ + ( + "sphere-in-box", + "sphere-cavity", + BooleanOp::Subtraction, + operands(move || Ok((box_host()?, sphere_at("sphere", [0.0; 3], 0.5)?))), + ), + ( + "cylinder-in-box", + "cylinder-cavity", + BooleanOp::Subtraction, + operands(move || { + Ok(( + box_host()?, + cylinder("cylinder", [0.0, 0.0, -0.5], 0.5, 1.0)?, + )) + }), + ), + ( + "box-in-sphere", + "sphere-box", + BooleanOp::Subtraction, + operands(move || Ok((sphere_at("sphere-host", [0.0; 3], 4.0)?, small_box()?))), + ), + ( + "box-in-cylinder", + "cylinder-box", + BooleanOp::Subtraction, + operands(move || { + Ok(( + cylinder("cylinder-host", [0.0, 0.0, -2.0], 3.0, 4.0)?, + small_box()?, + )) + }), + ), + ( + "crossing-union", + "crossing-union", + BooleanOp::Union, + operands(move || Ok((box_host()?, sphere_at("crossing", [1.8, 0.0, 0.0], 0.5)?))), + ), + ]; + pairs + .iter() + .map(|(name, id, operation, pair)| { + boolean( + &format!("booleans.cuboid-containment.{name}"), + id, + *operation, + pair, + ) + }) + .collect() +} + +fn torus_family() -> Vec { + let torus = || primitives::torus("torus".into(), Frame3::IDENTITY, 3.0, 1.0, fine()); + let concentric = operands(|| { + let host = primitives::torus("host".into(), Frame3::IDENTITY, 3.0, 1.0, fine())?; + let cutter = primitives::torus("cutter".into(), Frame3::IDENTITY, 3.0, 0.4, fine())?; + Ok((host, cutter)) + }); + let identical = operands(|| { + let host = primitives::torus("host".into(), Frame3::IDENTITY, 3.0, 1.0, fine())?; + Ok((host.clone(), host)) + }); + let small = operands(move || Ok((torus()?, sphere_at("small", [3.0, 0.0, 0.0], 0.25)?))); + let large = operands(move || Ok((sphere_at("large", [0.0; 3], 5.0)?, torus()?))); + let mut cases = every_operation("booleans.torus.concentric", "torus", &concentric); + cases.push(boolean( + "booleans.torus.identical", + "empty-torus", + BooleanOp::Subtraction, + &identical, + )); + cases.push(boolean( + "booleans.sphere-torus.small-sphere", + "torus-cut", + BooleanOp::Subtraction, + &small, + )); + cases.push(boolean( + "booleans.sphere-torus.large-sphere", + "sphere-cut", + BooleanOp::Subtraction, + &large, + )); + let poking = operands(|| { + let host = primitives::torus("host".into(), Frame3::IDENTITY, 3.0, 1.0, fine())?; + let ring = primitives::torus("ring".into(), at([3.0, 0.0, 0.0]), 0.8, 0.3, fine())?; + Ok((host, ring)) + }); + cases.extend(every_operation( + "booleans.torus.poking-ring", + "poking", + &poking, + )); + cases.extend(ring_hosts()); + cases +} + +fn ring_hosts() -> Vec { + let ring = || primitives::torus("torus".into(), at([0.0, 0.0, 2.0]), 2.0, 0.5, fine()); + let hosts: [(&str, Operands); 3] = [ + ( + "cylinder", + operands(move || Ok((cylinder("cylinder", [0.0; 3], 4.0, 4.0)?, ring()?))), + ), + ( + "box", + operands(move || { + Ok(( + cuboid_at("box", [-4.0, -4.0, -1.0], [8.0, 8.0, 6.0])?, + ring()?, + )) + }), + ), + ( + "frustum", + operands(move || { + Ok(( + primitives::frustum("frustum".into(), Frame3::IDENTITY, 4.0, 5.0, 4.0, fine())?, + ring()?, + )) + }), + ), + ]; + hosts + .iter() + .map(|(name, pair)| { + boolean( + &format!("booleans.torus-containment.{name}-minus-torus"), + &format!("{name}-minus-torus"), + BooleanOp::Subtraction, + pair, + ) + }) + .collect() +} + +fn frustum( + name: &str, + origin: Point3, + lower: f64, + upper: f64, + height: f64, +) -> Result { + primitives::frustum(name.into(), at(origin), lower, upper, height, fine()) +} + +fn conic_family() -> Vec { + let host = || frustum("host", [0.0; 3], 3.0, 4.0, 4.0); + let coaxial = + operands(move || Ok((host()?, frustum("cutter", [0.0, 0.0, 1.0], 1.0, 1.5, 1.0)?))); + let touching = operands(move || Ok((host()?, frustum("touching", [0.0; 3], 1.0, 1.5, 1.0)?))); + let mut cases = every_operation("booleans.conic.coaxial", "conic", &coaxial); + cases.push(boolean( + "booleans.conic.touching", + "touching-result", + BooleanOp::Subtraction, + &touching, + )); + let conic = || frustum("frustum", [0.0; 3], 3.0, 4.0, 4.0); + let mixed: [(&str, Operands); 6] = [ + ( + "frustum-minus-inner-sphere", + operands(move || Ok((conic()?, sphere_at("inner-sphere", [0.0, 0.0, 2.0], 0.5)?))), + ), + ( + "outer-sphere-minus-frustum", + operands(move || Ok((sphere_at("outer-sphere", [0.0, 0.0, 2.0], 6.0)?, conic()?))), + ), + ( + "frustum-minus-inner-cylinder", + operands(move || { + Ok(( + conic()?, + cylinder("inner-cylinder", [0.0, 0.0, 1.0], 0.5, 1.0)?, + )) + }), + ), + ( + "outer-cylinder-minus-frustum", + operands(move || { + Ok(( + cylinder("outer-cylinder", [0.0, 0.0, -1.0], 5.0, 6.0)?, + conic()?, + )) + }), + ), + ( + "frustum-minus-inner-box", + operands(move || { + Ok(( + conic()?, + cuboid_at("inner-box", [-0.25, -0.25, 1.75], [0.5; 3])?, + )) + }), + ), + ( + "outer-box-minus-frustum", + operands(move || { + Ok(( + cuboid_at("outer-box", [-5.0, -5.0, -1.0], [10.0, 10.0, 6.0])?, + conic()?, + )) + }), + ), + ]; + cases.extend(mixed.iter().map(|(name, pair)| { + boolean( + &format!("booleans.conic-containment.{name}"), + name, + BooleanOp::Subtraction, + pair, + ) + })); + cases.extend(generic_loops()); + cases +} + +fn generic_loops() -> Vec { + let cone = || primitives::cone("cone".into(), Frame3::IDENTITY, 2.0, 2.0, fine()); + let off_axis = operands(move || Ok((cone()?, sphere_at("sphere", [1.42, 0.0, 1.0], 0.35)?))); + let winding = operands(move || Ok((cone()?, sphere_at("sphere", [1.1, 0.0, 0.8], 0.35)?))); + let mut cases = every_operation("booleans.generic.sphere-cone", "sphere-cone", &off_axis); + cases.push(boolean( + "booleans.generic.periodic-winding", + "winding", + BooleanOp::Intersection, + &winding, + )); + let prism = operands(|| { + let triangle = vec![[-0.2, -0.2], [0.2, -0.2], [0.0, 0.2]]; + let prism = primitives::linear_extrusion( + "prism".into(), + at([0.0, 0.0, 0.8]), + triangle, + Vec::new(), + 0.4, + fine(), + )?; + Ok((cylinder("cylinder", [0.0; 3], 2.0, 2.0)?, prism)) + }); + cases.extend(every_operation( + "booleans.generic.noncanonical-planar-containment", + "noncanonical", + &prism, + )); + let annular = operands(|| { + let host = + primitives::annular_cylinder("host".into(), Frame3::IDENTITY, 0.4, 2.0, 3.0, fine())?; + Ok((host.clone(), host)) + }); + cases.push(boolean( + "booleans.coincident.annular-union", + "same", + BooleanOp::Union, + &annular, + )); + cases.push(boolean( + "booleans.coincident.annular-subtraction", + "empty", + BooleanOp::Subtraction, + &annular, + )); + cases +} + +pub(crate) fn cases() -> Vec { + [ + pockets(), + sphere_cylinder(), + cuboid_containment(), + torus_family(), + conic_family(), + ] + .into_iter() + .flatten() + .collect() +} diff --git a/main/opengeometry/tests/golden/builder_case.rs b/main/opengeometry/tests/golden/builder_case.rs new file mode 100644 index 0000000..072dfa7 --- /dev/null +++ b/main/opengeometry/tests/golden/builder_case.rs @@ -0,0 +1,35 @@ +use crate::body::record_body; +use crate::kernel::{BrepEnvelope, GeometryError}; +use crate::runner::Case; + +pub(crate) fn built( + name: &str, + build: impl Fn() -> Result + Send + Sync + 'static, +) -> Case { + Case::new(format!("builders.{name}"), move |record| { + match build() { + Ok(shape) => record_body(record, "body", &shape), + Err(error) => record.error("body", error), + } + Ok(()) + }) +} + +pub(crate) fn keyed( + name: &str, + build: impl Fn() -> Result<(BrepEnvelope, Vec), GeometryError> + Send + Sync + 'static, +) -> Case { + Case::new(format!("builders.{name}"), move |record| { + match build() { + Ok((shape, keys)) => { + record.section("keys"); + for key in &keys { + record.line(key); + } + record_body(record, "body", &shape); + } + Err(error) => record.error("body", error), + } + Ok(()) + }) +} diff --git a/main/opengeometry/tests/golden/builders_profiles.rs b/main/opengeometry/tests/golden/builders_profiles.rs new file mode 100644 index 0000000..039b028 --- /dev/null +++ b/main/opengeometry/tests/golden/builders_profiles.rs @@ -0,0 +1,233 @@ +use crate::builder_case::{built, keyed}; +use crate::fixtures::{fine, ground, rectangle, standard, upright}; +use crate::kernel::{primitives, CurveGeometry, Frame3}; +use crate::planar_shapes::profile_shell; +use crate::profiles::{ + band, line, polygon, quarter_annulus, rounded_outer, sector, split_quarter_annulus, + square_hole, wide_arc, +}; +use crate::runner::Case; +use std::f64::consts::{PI, TAU}; + +fn wires() -> Vec { + vec![ + built("rectangle.identity", || { + primitives::rectangle("rectangle".into(), Frame3::IDENTITY, 4.0, 2.0, standard()) + }), + keyed("rectangle-keys.ground", || { + primitives::rectangle_with_keys("rectangle".into(), ground(), 4.0, 2.0, standard()) + }), + built("rectangle.zero-width", || { + primitives::rectangle("rectangle".into(), Frame3::IDENTITY, 0.0, 2.0, standard()) + }), + built("polyline.open", || { + let points = [[0.0, 0.0, 0.0], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]]; + primitives::polyline("path".into(), &points, false, standard()) + }), + keyed("polyline-keys.closed", || { + let points = [[0.0; 3], [2.0, 0.0, 0.0], [2.0, 3.0, 0.0], [0.0, 3.0, 0.0]]; + primitives::polyline_with_keys("closed".into(), &points, true, standard()) + }), + built("polyline.noncoplanar", || { + let points = [[0.0; 3], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0], [3.0, 3.0, 3.0]]; + primitives::polyline("path".into(), &points, false, standard()) + }), + built("polyline.repeated-point", || { + primitives::polyline("bad".into(), &[[0.0; 3], [0.0; 3]], false, standard()) + }), + ] +} + +fn arcs() -> Vec { + let frame = ground(); + vec![ + built("arc-wire.circle", move || { + let curve = CurveGeometry::Circle { frame, radius: 1.0 }; + primitives::arc_wire("circle".into(), curve, 0.0, TAU, standard()) + }), + built("arc-wire.half", move || { + let curve = CurveGeometry::Circle { frame, radius: 1.0 }; + primitives::arc_wire("arc".into(), curve, 0.0, PI, standard()) + }), + built("arc-wire.ellipse", move || { + let curve = CurveGeometry::Ellipse { + frame, + major_radius: 2.0, + minor_radius: 1.0, + }; + primitives::arc_wire("ellipse".into(), curve, 0.5, 2.0, standard()) + }), + built("arc-wire.line-rejected", || { + let curve = CurveGeometry::Line { + origin: [0.0; 3], + direction: [1.0, 0.0, 0.0], + }; + primitives::arc_wire("line".into(), curve, 0.0, 1.0, standard()) + }), + built("arc-wire.zero-sweep", move || { + let curve = CurveGeometry::Circle { frame, radius: 1.0 }; + primitives::arc_wire("empty".into(), curve, 0.0, 0.0, standard()) + }), + ] +} + +fn linear() -> Vec { + vec![ + built("linear-extrusion.oracle", || { + let outer = rectangle([-1.0, -0.5], [1.0, 0.5]); + primitives::linear_extrusion( + "linear-extrusion".into(), + ground(), + outer, + Vec::new(), + 2.0, + standard(), + ) + }), + built("linear-extrusion.with-hole", profile_shell), + built("linear-extrusion.angled", || { + let outer = vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]]; + primitives::linear_extrusion( + "angled-host".into(), + Frame3::IDENTITY, + outer, + Vec::new(), + 3.0, + fine(), + ) + }), + built("linear-extrusion.upright-sector", || { + let outer = sector(2.3, -0.1, 0.1); + primitives::linear_extrusion( + "sector".into(), + upright([0.0, 0.8, 0.0]), + outer, + Vec::new(), + 2.0, + fine(), + ) + }), + built("linear-extrusion.self-crossing", || { + let outer = vec![[0.0, 0.0], [1.0, 1.0], [0.0, 1.0], [1.0, 0.0]]; + primitives::linear_extrusion( + "crossing".into(), + Frame3::IDENTITY, + outer, + Vec::new(), + 1.0, + standard(), + ) + }), + built("linear-extrusion.hole-outside", || { + let outer = rectangle([0.0, 0.0], [1.0, 1.0]); + let hole = rectangle([2.0, 2.0], [3.0, 3.0]); + primitives::linear_extrusion( + "outside".into(), + Frame3::IDENTITY, + outer, + vec![hole], + 1.0, + standard(), + ) + }), + ] +} + +fn curved() -> Vec { + vec![ + built("arc-edged-extrusion.oracle", || { + primitives::arc_edged_extrusion( + "arc-edged-extrusion".into(), + ground(), + rounded_outer(), + 2.0, + standard(), + ) + }), + built("arc-edged-extrusion.quarter-annulus", || { + primitives::arc_edged_extrusion( + "arc-host".into(), + Frame3::IDENTITY, + quarter_annulus(2.0, 1.5), + 3.0, + fine(), + ) + }), + built("arc-edged-extrusion.split-arcs", || { + primitives::arc_edged_extrusion( + "split-arc-host".into(), + Frame3::IDENTITY, + split_quarter_annulus(), + 3.0, + fine(), + ) + }), + built("arc-edged-extrusion.ground-band", || { + primitives::arc_edged_extrusion( + "mixed-curved-host".into(), + ground(), + band(3.0, 2.7, 0.8, -1.6), + 3.0, + fine(), + ) + }), + built("arc-edged-extrusion.wide", || { + primitives::arc_edged_extrusion( + "wide-arc".into(), + Frame3::IDENTITY, + wide_arc(), + 3.0, + fine(), + ) + }), + built("arc-edged-extrusion.polygon-only", || { + let outer = polygon(&[[0.0, 0.0], [2.0, 0.0], [2.0, 1.0], [0.0, 1.0]]); + primitives::arc_edged_extrusion( + "polygon".into(), + Frame3::IDENTITY, + outer, + 1.0, + standard(), + ) + }), + built("arc-edged-extrusion.open-profile", || { + let outer = vec![line([0.0, 0.0], [1.0, 0.0])]; + primitives::arc_edged_extrusion("open".into(), Frame3::IDENTITY, outer, 1.0, standard()) + }), + ] +} + +fn curved_with_holes() -> Vec { + vec![ + built("arc-edged-extrusion-with-holes.oracle", || { + let name = "arc-edged-extrusion-with-holes".into(); + primitives::arc_edged_extrusion_with_holes( + name, + ground(), + rounded_outer(), + vec![square_hole()], + 2.0, + standard(), + ) + }), + built("arc-edged-extrusion-with-holes.round-hole", || { + let outer = polygon(&rectangle([-2.0, -2.0], [2.0, 2.0])); + let hole = band(1.0, 0.5, 0.0, PI); + primitives::arc_edged_extrusion_with_holes( + "round-hole".into(), + Frame3::IDENTITY, + outer, + vec![hole], + 2.0, + standard(), + ) + }), + ] +} + +pub(crate) fn cases() -> Vec { + [wires(), arcs(), linear(), curved(), curved_with_holes()] + .into_iter() + .flatten() + .collect() +} diff --git a/main/opengeometry/tests/golden/builders_solids.rs b/main/opengeometry/tests/golden/builders_solids.rs new file mode 100644 index 0000000..6d5b250 --- /dev/null +++ b/main/opengeometry/tests/golden/builders_solids.rs @@ -0,0 +1,202 @@ +use crate::builder_case::built; +use crate::fixtures::{ + at, fine, ground, moved, scaled_accuracy, standard, tilted_about_y, y_axis_frame, +}; +use crate::kernel::{placed, primitives, Frame3, GeometryError}; +use crate::runner::Case; +use std::f64::consts::{FRAC_PI_2, PI, TAU}; + +fn skewed() -> Result { + Frame3::from_axis([1.0, -2.0, 0.5], [1.0, 1.0, 1.0], [1.0, 0.0, 0.0]) +} + +fn cuboids() -> Vec { + vec![ + built("cuboid.oracle", || { + primitives::cuboid("cuboid".into(), ground(), [2.0, 1.0, 3.0], standard()) + }), + built("cuboid.identity", || { + primitives::cuboid("box".into(), Frame3::IDENTITY, [2.0; 3], standard()) + }), + built("cuboid.translated", || { + let frame = at([10.0, -7.0, 4.0]); + primitives::cuboid("translated".into(), frame, [2.0, 3.0, 4.0], standard()) + }), + built("cuboid.skewed", || { + primitives::cuboid("skewed".into(), skewed()?, [1.0, 2.0, 0.5], fine()) + }), + built("cuboid.zero-size", || { + primitives::cuboid("zero".into(), Frame3::IDENTITY, [0.0, 1.0, 1.0], standard()) + }), + ] +} + +fn cylinders() -> Vec { + vec![ + built("cylinder.oracle", || { + primitives::cylinder("cylinder".into(), ground(), 1.0, 2.0, standard()) + }), + built("cylinder.identity-fine", || { + primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 1.0, 2.0, fine()) + }), + built("cylinder.skewed", || { + primitives::cylinder("skewed".into(), skewed()?, 0.6, 4.0, fine()) + }), + built("cylinder.zero-radius", || { + primitives::cylinder("zero".into(), Frame3::IDENTITY, 0.0, 2.0, standard()) + }), + built("cylinder.negative-height", || { + primitives::cylinder("negative".into(), Frame3::IDENTITY, 1.0, -2.0, standard()) + }), + built("cylinder-sector.quarter", || { + let frame = Frame3::IDENTITY; + primitives::cylinder_sector("sector".into(), frame, 1.0, 2.0, 0.0, FRAC_PI_2, fine()) + }), + built("cylinder-sector.ground", || { + primitives::cylinder_sector("sector".into(), ground(), 2.0, 1.0, 0.3, 4.0, standard()) + }), + built("cylinder-sector.full-turn", || { + let frame = Frame3::IDENTITY; + primitives::cylinder_sector("sector".into(), frame, 1.0, 1.0, 0.0, TAU, standard()) + }), + built("cylinder-sector.zero-sweep", || { + let frame = Frame3::IDENTITY; + primitives::cylinder_sector("sector".into(), frame, 1.0, 1.0, 0.0, 0.0, standard()) + }), + ] +} + +fn annular() -> Vec { + vec![ + built("annular-cylinder.oracle", || { + let name = "annular-cylinder".into(); + primitives::annular_cylinder(name, ground(), 0.4, 1.0, 2.0, standard()) + }), + built("annular-cylinder.thin-ring", || { + let name = "ring-host".into(); + primitives::annular_cylinder(name, Frame3::IDENTITY, 1.9, 2.1, 3.0, fine()) + }), + built("annular-cylinder.inverted-radii", || { + let name = "inverted".into(); + primitives::annular_cylinder(name, Frame3::IDENTITY, 1.0, 0.5, 1.0, standard()) + }), + built("cylinder-with-hole.oracle", || { + let frame = ground(); + let name = "cylinder-with-hole".into(); + primitives::cylinder_with_circular_hole(name, frame, frame, 0.4, 1.0, 2.0, standard()) + }), + built("cylinder-with-hole.off-centre", || { + let frame = ground(); + let inner = moved(frame, frame.point([0.3, 0.0, 0.0])); + let name = "off-centre".into(); + primitives::cylinder_with_circular_hole(name, frame, inner, 0.4, 1.0, 2.0, standard()) + }), + built("cylinder-with-hole.breaching", || { + let frame = Frame3::IDENTITY; + let inner = at([0.8, 0.0, 0.0]); + let name = "breaching".into(); + primitives::cylinder_with_circular_hole(name, frame, inner, 0.4, 1.0, 2.0, standard()) + }), + ] +} + +fn conics() -> Vec { + vec![ + built("cone.oracle", || { + primitives::cone("cone".into(), ground(), 1.0, 2.0, standard()) + }), + built("cone.identity", || { + primitives::cone("cone".into(), Frame3::IDENTITY, 2.0, 2.0, fine()) + }), + built("cone.zero-height", || { + primitives::cone("cone".into(), Frame3::IDENTITY, 1.0, 0.0, standard()) + }), + built("frustum.oracle", || { + primitives::frustum("frustum".into(), ground(), 1.0, 0.4, 2.0, standard()) + }), + built("frustum.expanding", || { + primitives::frustum("frustum".into(), Frame3::IDENTITY, 3.0, 4.0, 4.0, fine()) + }), + built("frustum.shell-family", || { + primitives::frustum("frustum".into(), Frame3::IDENTITY, 2.0, 1.0, 3.0, fine()) + }), + built("frustum.negative-radius", || { + primitives::frustum( + "frustum".into(), + Frame3::IDENTITY, + -1.0, + 1.0, + 1.0, + standard(), + ) + }), + ] +} + +fn rounds() -> Vec { + vec![ + built("sphere.oracle", || { + primitives::sphere("sphere".into(), ground(), 1.0, standard()) + }), + built("sphere.offset", || { + primitives::sphere("sphere".into(), at([5.0, -3.0, 8.0]), 2.0, fine()) + }), + built("sphere.micro", || { + primitives::sphere( + "micro".into(), + Frame3::IDENTITY, + 1e-6, + scaled_accuracy(1e-6), + ) + }), + built("sphere.zero-radius", || { + primitives::sphere("zero".into(), Frame3::IDENTITY, 0.0, standard()) + }), + built("torus.oracle", || { + primitives::torus("torus".into(), ground(), 2.0, 0.5, standard()) + }), + built("torus.ring", || { + primitives::torus("torus".into(), Frame3::IDENTITY, 3.0, 1.0, fine()) + }), + built("torus.self-intersecting", || { + primitives::torus("torus".into(), Frame3::IDENTITY, 1.0, 1.5, standard()) + }), + ] +} + +fn placements() -> Vec { + vec![ + built("placed.cuboid-scaled", || { + let source = primitives::cuboid("box".into(), Frame3::IDENTITY, [2.0; 3], standard())?; + placed(&source, y_axis_frame([5.0, 6.0, 7.0])?, 1.25) + }), + built("placed.cylinder-rotated", || { + let source = primitives::cylinder( + "c".into(), + moved(ground(), [0.0, 1.2, 0.0]), + 1.0, + 2.0, + fine(), + )?; + placed(&source, tilted_about_y([0.2, 0.1, 0.2], PI / 7.0), 1.25) + }), + built("placed.zero-scale", || { + let source = primitives::sphere("s".into(), Frame3::IDENTITY, 1.0, standard())?; + placed(&source, Frame3::IDENTITY, 0.0) + }), + ] +} + +pub(crate) fn cases() -> Vec { + [ + cuboids(), + cylinders(), + annular(), + conics(), + rounds(), + placements(), + ] + .into_iter() + .flatten() + .collect() +} diff --git a/main/opengeometry/tests/golden/classify.rs b/main/opengeometry/tests/golden/classify.rs new file mode 100644 index 0000000..7c4e658 --- /dev/null +++ b/main/opengeometry/tests/golden/classify.rs @@ -0,0 +1,69 @@ +use crate::digest::bits_list; +use crate::kernel::{classify_point, BrepEnvelope, PointClassification}; +use crate::record::Record; + +const STEPS: usize = 7; +const PADDING: f64 = 0.125; + +fn symbol(classification: PointClassification) -> char { + match classification { + PointClassification::Inside => 'I', + PointClassification::Outside => 'O', + PointClassification::Boundary => 'B', + PointClassification::Unknown => 'U', + } +} + +fn grid_axes(lo: [f64; 3], hi: [f64; 3]) -> [[f64; STEPS]; 3] { + let span = (0..3) + .map(|axis| hi[axis] - lo[axis]) + .fold(0.0_f64, f64::max); + let pad = span * PADDING; + std::array::from_fn(|axis| { + let start = lo[axis] - pad; + let width = hi[axis] - lo[axis] + 2.0 * pad; + std::array::from_fn(|step| start + width * step as f64 / (STEPS - 1) as f64) + }) +} + +pub(crate) fn classify_grid(record: &mut Record, label: &str, brep: &BrepEnvelope) { + let title = format!("{label}.classify"); + let bounds = match brep.bounds() { + Ok(Some(bounds)) => bounds, + Ok(None) => { + record.section(&title); + record.line("bounds: none"); + return; + } + Err(error) => { + record.error(&title, error); + return; + } + }; + let lo = std::array::from_fn(|axis| bounds.axes[axis].lo); + let hi = std::array::from_fn(|axis| bounds.axes[axis].hi); + let axes = grid_axes(lo, hi); + record.section(&title); + for (name, values) in ["x", "y", "z"].iter().zip(&axes) { + record.field(name, bits_list(values)); + } + let mut errors = Vec::new(); + for (i, x) in axes[0].iter().enumerate() { + for (j, y) in axes[1].iter().enumerate() { + let mut row = String::with_capacity(STEPS); + for (k, z) in axes[2].iter().enumerate() { + match classify_point(brep, [*x, *y, *z]) { + Ok(classification) => row.push(symbol(classification)), + Err(error) => { + row.push('E'); + errors.push(format!("{i} {j} {k}: {error:?}")); + } + } + } + record.line(&format!("{i} {j} {row}")); + } + } + for error in errors { + record.line(&error); + } +} diff --git a/main/opengeometry/tests/golden/contract.rs b/main/opengeometry/tests/golden/contract.rs new file mode 100644 index 0000000..ac24687 --- /dev/null +++ b/main/opengeometry/tests/golden/contract.rs @@ -0,0 +1,153 @@ +use crate::check_findings; +use crate::extras::{sweep_digest, sweep_outputs, SweepOutputs}; +use crate::golden_file::{committed, compare, dump, mode, parse, Mode, RECORD_INSTRUCTION}; +use crate::record::Findings; +use std::collections::{BTreeMap, BTreeSet}; +use std::fs; +use std::path::PathBuf; + +fn keyed(entries: &[(&str, &str)]) -> BTreeMap { + entries + .iter() + .map(|(key, digest)| (key.to_string(), digest.to_string())) + .collect() +} + +fn flip_first_bit(bytes: &mut [u8]) { + bytes[0] ^= 1; +} + +#[test] +fn hash_changes_when_one_bit_of_any_hashed_output_flips() { + let outputs = sweep_outputs().unwrap(); + let original = sweep_digest(&outputs); + let flips: [fn(&mut SweepOutputs); 8] = [ + |outputs| flip_first_bit(&mut outputs.brep_json), + |outputs| outputs.positions[0] = f64::from_bits(outputs.positions[0].to_bits() ^ 1), + |outputs| outputs.normals[0] = f32::from_bits(outputs.normals[0].to_bits() ^ 1), + |outputs| outputs.indices[0] ^= 1, + |outputs| outputs.face_ids[0] ^= 1, + |outputs| flip_first_bit(&mut outputs.verdicts[0]), + |outputs| flip_first_bit(&mut outputs.step), + |outputs| flip_first_bit(&mut outputs.report), + ]; + for (index, flip) in flips.iter().enumerate() { + let mut changed = outputs.clone(); + flip(&mut changed); + assert_ne!(sweep_digest(&changed), original, "flip {index}"); + } +} + +#[test] +fn a_scene_missing_from_the_golden_file_fails_with_the_record_instruction() { + let expected = keyed(&[("kept", "aa")]); + let actual = keyed(&[("kept", "aa"), ("added", "bb")]); + let mismatch = compare(&expected, &actual).unwrap_err(); + assert_eq!(mismatch.missing, ["added"]); + let message = mismatch.to_string(); + assert!(message.contains("added"), "{message}"); + assert!(message.contains(RECORD_INSTRUCTION), "{message}"); +} + +#[test] +fn a_golden_key_with_no_scene_fails() { + let expected = keyed(&[("kept", "aa"), ("removed", "bb")]); + let actual = keyed(&[("kept", "aa")]); + let mismatch = compare(&expected, &actual).unwrap_err(); + assert_eq!(mismatch.stale, ["removed"]); + assert!(mismatch.to_string().contains("removed")); +} + +#[test] +fn a_scene_whose_digest_differs_from_the_golden_fails() { + let expected = keyed(&[("kept", "aa"), ("moved", "bb")]); + let actual = keyed(&[("kept", "aa"), ("moved", "cc")]); + let mismatch = compare(&expected, &actual).unwrap_err(); + assert_eq!(mismatch.differing, ["moved"]); + assert!(mismatch.to_string().contains("moved")); + assert!(compare(&actual, &actual).is_ok()); +} + +#[test] +fn an_empty_golden_file_fails_with_the_record_instruction() { + let expected = parse("{}").unwrap(); + let actual = keyed(&[("scene", "aa")]); + let message = compare(&expected, &actual).unwrap_err().to_string(); + assert!(message.contains("no golden is recorded"), "{message}"); + assert!(message.contains(RECORD_INSTRUCTION), "{message}"); +} + +#[test] +fn ci_builds_without_comparing_or_recording() { + let table = [ + (None, Some("true"), Mode::Build), + (Some("1"), Some("true"), Mode::Build), + (Some("1"), None, Mode::Record), + (Some("1"), Some("false"), Mode::Record), + (Some("1"), Some("1"), Mode::Record), + (None, None, Mode::Compare), + (Some("0"), None, Mode::Compare), + (Some("true"), None, Mode::Compare), + (Some(""), None, Mode::Compare), + (None, Some("false"), Mode::Compare), + (Some("0"), Some("false"), Mode::Compare), + (Some("true"), Some("false"), Mode::Compare), + (Some(""), Some("false"), Mode::Compare), + (None, Some("1"), Mode::Compare), + (Some("0"), Some("1"), Mode::Compare), + (Some("true"), Some("1"), Mode::Compare), + (Some(""), Some("1"), Mode::Compare), + ]; + for (update, ci, chosen) in table { + assert_eq!(mode(update, ci, None), chosen, "{update:?} {ci:?}"); + } +} + +fn lone_disagreement(disagreement: &str, compared: bool) -> bool { + let findings = Findings { + disagreements: BTreeSet::from([disagreement.to_string()]), + ..Findings::default() + }; + check_findings(&BTreeSet::new(), findings, &[], compared).is_ok() +} + +#[test] +fn matrix_result_disagreements_fail_only_where_stored_results_are_compared() { + assert!(!lone_disagreement("booleans.matrix.x result", true)); + assert!(lone_disagreement("booleans.matrix.x result", false)); + assert!(!lone_disagreement("booleans.matrix.x handlers", true)); + assert!(!lone_disagreement("booleans.matrix.x handlers", false)); +} + +#[test] +fn only_apple_silicon_macos_has_a_recorded_golden() { + let recorded = committed("aarch64-apple-darwin").unwrap(); + assert!(recorded.is_some_and(|golden| !golden.is_empty())); + assert!(committed("x86-64-unknown-linux-gnu").unwrap().is_none()); +} + +#[test] +fn the_dump_mode_writes_every_case_without_comparing_the_golden() { + let directory = PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("golden-dump-mode"); + let chosen = mode(Some("1"), None, Some(directory.clone())); + assert_eq!(chosen, Mode::Dump(directory.clone())); + let on_ci = mode(None, Some("true"), Some(directory.clone())); + assert_eq!(on_ci, Mode::Dump(directory.clone())); + if directory.exists() { + fs::remove_dir_all(&directory).unwrap(); + } + let texts = keyed(&[ + ("first.case", "first text\n"), + ("second.case", "second text\n"), + ]); + dump(&directory, &texts).unwrap(); + let mut written = fs::read_dir(&directory) + .unwrap() + .map(|entry| entry.unwrap().file_name().into_string().unwrap()) + .collect::>(); + written.sort(); + assert_eq!(written, ["first.case.txt", "second.case.txt"]); + let first = fs::read_to_string(directory.join("first.case.txt")).unwrap(); + assert_eq!(first, "first text\n"); + assert!(dump(&directory, &texts).is_err()); +} diff --git a/main/opengeometry/tests/golden/coverage.rs b/main/opengeometry/tests/golden/coverage.rs new file mode 100644 index 0000000..3ef9ec4 --- /dev/null +++ b/main/opengeometry/tests/golden/coverage.rs @@ -0,0 +1,43 @@ +use crate::batch_route::batch_route_names; +use crate::handler_source::boolean_handler_names; +use crate::record::Failure; +use std::collections::BTreeSet; + +const UNSCANNED_EXEMPTION: &str = "batch-route:other-"; + +pub(crate) struct Listings { + pub(crate) files: [(&'static str, String); 3], + pub(crate) uncovered: Vec, +} + +pub(crate) fn expected_names() -> Result, Failure> { + let mut expected = boolean_handler_names()?; + expected.extend(batch_route_names().map(str::to_string)); + Ok(expected) +} + +pub(crate) fn unscanned_names<'a>( + expected: &BTreeSet, + seen: impl IntoIterator, +) -> Vec { + seen.into_iter() + .filter(|name| !expected.contains(*name) && !name.starts_with(UNSCANNED_EXEMPTION)) + .cloned() + .collect() +} + +fn listing<'a>(names: impl IntoIterator) -> String { + names.into_iter().map(|name| format!("{name}\n")).collect() +} + +pub(crate) fn listings(expected: &BTreeSet, covered: &BTreeSet) -> Listings { + let uncovered = expected.difference(covered).cloned().collect::>(); + Listings { + files: [ + ("handlers-expected", listing(expected)), + ("handlers-covered", listing(covered)), + ("handlers-uncovered", listing(&uncovered)), + ], + uncovered, + } +} diff --git a/main/opengeometry/tests/golden/creating.rs b/main/opengeometry/tests/golden/creating.rs new file mode 100644 index 0000000..f07e6e0 --- /dev/null +++ b/main/opengeometry/tests/golden/creating.rs @@ -0,0 +1,388 @@ +use crate::graph_case::{ + child, circle, copy_options, create_options, extrude, graph_case, inspect, on_plane, operation, + polyline, primitive, rectangle, sweep, +}; +use crate::graph_record::{changes, created, node_state}; +use crate::kernel::{CreatingOperation, EditScope, Point3, Primitive, Transform, WorldGraph}; +use crate::record::{Failure, Record}; +use crate::runner::Case; + +type Profiles = fn() -> Vec<(&'static str, Primitive)>; + +type Extrusion = (&'static str, Profiles, &'static [&'static str], f64); + +type Sweep = (&'static str, fn() -> Primitive, &'static [Point3]); + +const L_PATH: [Point3; 3] = [[0.0, 0.0, 0.0], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]]; + +pub(crate) const SPATIAL_PATH: [Point3; 4] = [ + [0.0, 0.0, 0.0], + [0.0, 3.0, 0.0], + [3.0, 3.0, 0.0], + [3.0, 3.0, 3.0], +]; + +const SQUARE: [Point3; 4] = [ + [-1.0, 0.0, -1.0], + [1.0, 0.0, -1.0], + [1.0, 0.0, 1.0], + [-1.0, 0.0, 1.0], +]; + +fn solid( + record: &mut Record, + graph: &mut WorldGraph, + value: CreatingOperation, +) -> Result<(), Failure> { + if operation(record, graph, value, create_options("solid")).is_some() { + inspect(record, graph, "solid")?; + } + Ok(()) +} + +fn extruded(entry: &Extrusion) -> Case { + let (name, profiles, holes, distance) = *entry; + graph_case(&format!("creating.extrude.{name}"), move |graph, record| { + for (og_id, shape) in profiles() { + primitive(record, graph, shape, create_options(og_id)); + } + solid(record, graph, extrude("profile", holes, distance)) + }) +} + +fn swept(entry: &Sweep) -> Case { + let (name, profile, path) = *entry; + graph_case(&format!("creating.sweep.{name}"), move |graph, record| { + primitive(record, graph, profile(), create_options("profile")); + primitive(record, graph, polyline(path, false), create_options("path")); + solid(record, graph, sweep("profile", "path")) + }) +} + +fn rectangle_profile() -> Vec<(&'static str, Primitive)> { + vec![("profile", rectangle(2.0, 3.0))] +} + +fn square_profile() -> Vec<(&'static str, Primitive)> { + vec![("profile", rectangle(2.0, 2.0))] +} + +fn circle_profile() -> Vec<(&'static str, Primitive)> { + vec![("profile", circle(2.0))] +} + +fn closed_polyline() -> Vec<(&'static str, Primitive)> { + let points = [[0.0; 3], [2.0, 0.0, 0.0], [2.0, 3.0, 0.0], [0.0, 3.0, 0.0]]; + vec![("profile", polyline(&points, true))] +} + +fn ground_forward() -> Vec<(&'static str, Primitive)> { + vec![("profile", polyline(&SQUARE, true))] +} + +fn ground_reverse() -> Vec<(&'static str, Primitive)> { + let points = [SQUARE[3], SQUARE[2], SQUARE[1], SQUARE[0]]; + vec![("profile", polyline(&points, true))] +} + +fn annular() -> Vec<(&'static str, Primitive)> { + vec![("profile", circle(2.0)), ("hole", circle(1.0))] +} + +fn rectangle_round_hole() -> Vec<(&'static str, Primitive)> { + vec![("profile", rectangle(4.0, 4.0)), ("hole", circle(1.0))] +} + +fn polyline_hole() -> Vec<(&'static str, Primitive)> { + let hole = [ + [-0.5, 0.0, -0.5], + [0.5, 0.0, -0.5], + [0.5, 0.0, 0.5], + [-0.5, 0.0, 0.5], + ]; + vec![ + ("profile", polyline(&SQUARE, true)), + ("hole", polyline(&hole, true)), + ] +} + +fn self_crossing() -> Vec<(&'static str, Primitive)> { + let points = [ + [-1.0, 0.0, -1.0], + [1.0, 0.0, 1.0], + [-1.0, 0.0, 1.0], + [1.0, 0.0, -1.0], + ]; + vec![("profile", polyline(&points, true))] +} + +fn open_polyline() -> Vec<(&'static str, Primitive)> { + vec![("profile", polyline(&L_PATH, false))] +} + +const EXTRUSIONS: [Extrusion; 12] = [ + ("rectangle", rectangle_profile, &[], 4.0), + ("circle", circle_profile, &[], 3.0), + ("closed-polyline", closed_polyline, &[], 4.0), + ("ground-polyline-forward", ground_forward, &[], 2.0), + ("ground-polyline-reverse", ground_reverse, &[], 2.0), + ("negative-annular", annular, &["hole"], -3.0), + ("rectangle-round-hole", rectangle_round_hole, &["hole"], 2.0), + ("polyline-with-polyline-hole", polyline_hole, &["hole"], 1.0), + ("too-short", square_profile, &[], 1e-9), + ("self-crossing", self_crossing, &[], 2.0), + ("hole-is-profile", square_profile, &["profile"], 2.0), + ("open-polyline-profile", open_polyline, &[], 2.0), +]; + +fn square_one() -> Primitive { + rectangle(1.0, 1.0) +} + +fn square_two() -> Primitive { + rectangle(2.0, 2.0) +} + +fn slot() -> Primitive { + rectangle(1.0, 2.0) +} + +fn disc_half() -> Primitive { + circle(0.5) +} + +fn disc_small() -> Primitive { + circle(0.4) +} + +fn disc_wide() -> Primitive { + circle(0.6) +} + +const SWEEPS: [Sweep; 11] = [ + ("rectangle-l-path", square_two, &L_PATH), + ("rectangle-spatial-path", square_one, &SPATIAL_PATH), + ("circle-l-path", disc_half, &L_PATH), + ("circle-spatial-path", disc_small, &SPATIAL_PATH), + ("straight-path", slot, &[[0.0; 3], [0.0, 3.0, 0.0]]), + ( + "collinear-path", + square_one, + &[[0.0; 3], [0.0, 1.5, 0.0], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]], + ), + ( + "path-off-profile", + square_two, + &[[0.0, 1.0, 0.0], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]], + ), + ( + "miter-collision", + disc_wide, + &[[0.0; 3], [0.0, 0.2, 0.0], [2.0, 0.2, 0.0]], + ), + ( + "separated-collision", + disc_wide, + &[[0.0; 3], [0.0, 3.0, 0.0], [1.0, 3.0, 0.0], [1.0, 0.0, 0.0]], + ), + ( + "reversing-path", + square_one, + &[[0.0; 3], [0.0, 3.0, 0.0], [0.0, 1.0, 0.0]], + ), + ( + "circle-horizontal-path", + disc_half, + &[[0.0; 3], [3.0, 0.0, 0.0]], + ), +]; + +fn offset_hole(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive(record, graph, circle(2.0), create_options("profile")); + primitive(record, graph, circle(0.5), create_options("hole")); + let offset = Transform::Translate { + offset: [0.8, 0.0, 0.0], + }; + changes(record, "move hole", graph.transform("hole", offset)); + solid(record, graph, extrude("profile", &["hole"], 1.5)) +} + +fn far_parent(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + created( + record, + "assembly", + graph.create_system_assembly(create_options("assembly")), + ); + let offset = Transform::Translate { + offset: [10_000.0, 0.0, 0.0], + }; + changes( + record, + "translate assembly", + graph.transform("assembly", offset), + ); + primitive( + record, + graph, + rectangle(2.0, 2.0), + child("profile", "assembly"), + ); + solid(record, graph, extrude("profile", &[], 3.0)) +} + +fn noncoplanar_hole(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive( + record, + graph, + rectangle(4.0, 4.0), + create_options("profile"), + ); + let options = on_plane( + create_options("hole"), + [0.0, 0.5, 0.0], + [0.0, 1.0, 0.0], + [1.0, 0.0, 0.0], + ); + primitive(record, graph, circle(1.0), options); + solid(record, graph, extrude("profile", &["hole"], 2.0)) +} + +fn unknown_profile(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + solid(record, graph, extrude("missing", &[], 1.0)) +} + +fn offset_profile(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive(record, graph, circle(0.3), create_options("profile")); + let offset = Transform::Translate { + offset: [1.0, 0.0, 0.0], + }; + changes(record, "move profile", graph.transform("profile", offset)); + primitive( + record, + graph, + polyline(&L_PATH, false), + create_options("path"), + ); + solid(record, graph, sweep("profile", "path")) +} + +fn closed_path(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive( + record, + graph, + rectangle(1.0, 1.0), + create_options("profile"), + ); + primitive( + record, + graph, + polyline(&SPATIAL_PATH, true), + create_options("path"), + ); + solid(record, graph, sweep("profile", "path")) +} + +fn circle_path(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive( + record, + graph, + rectangle(1.0, 1.0), + create_options("profile"), + ); + primitive(record, graph, circle(3.0), create_options("path")); + solid(record, graph, sweep("profile", "path")) +} + +fn rebuild_keeps_placement(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive( + record, + graph, + rectangle(2.0, 2.0), + create_options("profile"), + ); + operation( + record, + graph, + extrude("profile", &[], 2.0), + create_options("solid"), + ); + let offset = Transform::Translate { + offset: [5.0, 0.0, 0.0], + }; + changes(record, "translate", graph.transform("solid", offset)); + let rebuilt = graph.rebuild_operation("solid", extrude("profile", &[], 3.0), EditScope::Node); + changes(record, "rebuild", rebuilt); + inspect(record, graph, "solid") +} + +fn rebuild_shared(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive( + record, + graph, + rectangle(2.0, 2.0), + create_options("profile"), + ); + operation( + record, + graph, + extrude("profile", &[], 2.0), + create_options("solid"), + ); + let copy = copy_options("instance", None); + created(record, "instance", graph.instance("solid", copy)); + for scope in [EditScope::Node, EditScope::AllInstances] { + let rebuilt = graph.rebuild_operation("solid", extrude("profile", &[], 3.0), scope); + changes(record, &format!("rebuild {scope:?}"), rebuilt); + } + inspect(record, graph, "solid")?; + node_state(record, graph, "instance"); + Ok(()) +} + +fn rebuild_sweep(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive( + record, + graph, + rectangle(1.0, 1.0), + create_options("profile"), + ); + primitive( + record, + graph, + polyline(&SPATIAL_PATH, false), + create_options("path"), + ); + operation( + record, + graph, + sweep("profile", "path"), + create_options("solid"), + ); + let same = graph.rebuild_operation("solid", sweep("profile", "path"), EditScope::Node); + changes(record, "rebuild same", same); + let path = graph.rebuild_primitive("path", polyline(&L_PATH, false), EditScope::Node); + changes(record, "rebuild path", path); + let again = graph.rebuild_operation("solid", sweep("profile", "path"), EditScope::Node); + changes(record, "rebuild sweep", again); + let mismatch = graph.rebuild_primitive("path", circle(1.0), EditScope::Node); + changes(record, "rebuild type mismatch", mismatch); + inspect(record, graph, "solid") +} + +pub(crate) fn cases() -> Vec { + let mut cases = EXTRUSIONS.iter().map(extruded).collect::>(); + cases.extend(SWEEPS.iter().map(swept)); + cases.extend([ + graph_case("creating.extrude.offset-circle-hole", offset_hole), + graph_case("creating.extrude.far-parent", far_parent), + graph_case("creating.extrude.noncoplanar-hole", noncoplanar_hole), + graph_case("creating.extrude.unknown-profile", unknown_profile), + graph_case("creating.sweep.circle-offset-profile", offset_profile), + graph_case("creating.sweep.closed-path", closed_path), + graph_case("creating.sweep.circle-path", circle_path), + graph_case("creating.rebuild.keeps-placement", rebuild_keeps_placement), + graph_case("creating.rebuild.shared-scope", rebuild_shared), + graph_case("creating.rebuild.sweep-and-path", rebuild_sweep), + ]); + cases +} diff --git a/main/opengeometry/tests/golden/digest.rs b/main/opengeometry/tests/golden/digest.rs new file mode 100644 index 0000000..20ec9f1 --- /dev/null +++ b/main/opengeometry/tests/golden/digest.rs @@ -0,0 +1,72 @@ +use sha2::{Digest, Sha256}; + +pub(crate) trait LittleEndianWord: Copy { + type Bytes: AsRef<[u8]>; + + fn little_endian_bytes(self) -> Self::Bytes; +} + +impl LittleEndianWord for f64 { + type Bytes = [u8; 8]; + + fn little_endian_bytes(self) -> Self::Bytes { + self.to_le_bytes() + } +} + +impl LittleEndianWord for f32 { + type Bytes = [u8; 4]; + + fn little_endian_bytes(self) -> Self::Bytes { + self.to_le_bytes() + } +} + +impl LittleEndianWord for u32 { + type Bytes = [u8; 4]; + + fn little_endian_bytes(self) -> Self::Bytes { + self.to_le_bytes() + } +} + +pub(crate) fn hex(bytes: &[u8]) -> String { + let mut text = String::with_capacity(bytes.len() * 2); + for byte in bytes { + text.push_str(&format!("{byte:02x}")); + } + text +} + +pub(crate) fn sha256(bytes: &[u8]) -> String { + hex(&Sha256::digest(bytes)) +} + +pub(crate) fn sha256_words(values: &[Word]) -> String { + let mut hasher = Sha256::new(); + for value in values { + hasher.update(value.little_endian_bytes()); + } + hex(&hasher.finalize()) +} + +pub(crate) fn bits(value: f64) -> String { + format!("{:016x} {value:?}", value.to_bits()) +} + +pub(crate) fn bits_list(values: &[f64]) -> String { + values + .iter() + .map(|value| format!("{:016x}", value.to_bits())) + .collect::>() + .join(" ") +} + +pub(crate) fn triples(values: &[f64]) -> String { + let mut text = String::with_capacity(values.len() * 17); + for triple in values.chunks(3) { + text.push_str(&bits_list(triple)); + text.push('\n'); + } + text +} diff --git a/main/opengeometry/tests/golden/display.rs b/main/opengeometry/tests/golden/display.rs new file mode 100644 index 0000000..b73e447 --- /dev/null +++ b/main/opengeometry/tests/golden/display.rs @@ -0,0 +1,59 @@ +use crate::digest::{bits, bits_list, sha256_words}; +use crate::kernel::{bucket_floor, display_buffers, static_bucket, BrepEnvelope, DisplayBuffers}; +use crate::record::Record; + +const MAX_TRIANGLES: usize = 2_000_000; + +pub(crate) fn record_display_buffers(record: &mut Record, label: &str, brep: &BrepEnvelope) { + let title = format!("{label}.display"); + record.section(&title); + match bucket_floor(brep) { + Ok(floor) => record.field("bucket_floor", bits(floor)), + Err(error) => record.debug("bucket_floor.error", error), + } + let bucket = match static_bucket(brep) { + Ok(bucket) => bucket, + Err(error) => { + record.debug("static_bucket.error", error); + return; + } + }; + record.field("static_bucket", bits(bucket)); + for (name, value) in [ + ("finer", bucket / 2.0), + ("static", bucket), + ("coarser", bucket * 2.0), + ] { + let section = format!("{title}.{name}"); + match display_buffers(brep, value, MAX_TRIANGLES) { + Ok(buffers) => write_buffers(record, §ion, value, &buffers), + Err(error) => record.error(§ion, error), + } + } +} + +pub(crate) fn write_buffers( + record: &mut Record, + title: &str, + requested: f64, + buffers: &DisplayBuffers, +) { + record.section(title); + record.field("requested_bucket", bits(requested)); + record.field("bucket", bits(buffers.bucket)); + record.field("achieved_deflection", bits(buffers.achieved_deflection)); + record.field("origin", bits_list(&buffers.origin)); + record.field("revision", buffers.revision); + record.field("triangles", buffers.triangles); + record.field("positions.len", buffers.positions.len()); + record.field("positions.sha256", sha256_words(&buffers.positions)); + record.field("normals.sha256", sha256_words(&buffers.normals)); + record.field("indices.len", buffers.indices.len()); + record.field("indices.sha256", sha256_words(&buffers.indices)); + record.field("face_ranges.len", buffers.face_ranges.len()); + record.field("face_ranges.sha256", sha256_words(&buffers.face_ranges)); + record.field("outline.len", buffers.outline.len()); + record.field("outline.sha256", sha256_words(&buffers.outline)); + record.field("edge_ids.len", buffers.edge_ids.len()); + record.field("edge_ids.sha256", sha256_words(&buffers.edge_ids)); +} diff --git a/main/opengeometry/tests/golden/extras.rs b/main/opengeometry/tests/golden/extras.rs new file mode 100644 index 0000000..c3c395e --- /dev/null +++ b/main/opengeometry/tests/golden/extras.rs @@ -0,0 +1,141 @@ +use crate::boolean_case::result_body; +use crate::creating::SPATIAL_PATH; +use crate::digest::hex; +use crate::fixtures::standard; +use crate::graph_case::{create_options, polyline, rectangle, sweep}; +use crate::json::pretty; +use crate::kernel::{ + classify_point, multi_tool_boolean, tessellate, BooleanOp, BrepEnvelope, StepOptions, + WorldGraph, +}; +use crate::record::{Failure, Record}; +use opengeometry_test_support::scenes::acceptance::{acceptance_scene, level_export}; +use serde_json::{to_value, Value}; +use sha2::{Digest, Sha256}; +use std::fs; +use std::path::Path; + +pub(crate) const SWEEP_KEY: &str = "sweep-3d"; + +const FALLBACK_DIRECTORY: &str = concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/fixtures/cases/batch-matrix" +); + +const FALLBACK_ROWS: [&str; 3] = [ + "planar.oblique-cutters", + "planar.overlapping-cutters", + "prismatic.single-round", +]; + +const LEVEL_UNITS: [&str; 2] = ["metre", "millimetre"]; + +const LEVEL_UP_AXES: [&str; 2] = ["Y", "Z"]; + +const SWEEP_PROBES: [[f64; 3]; 3] = [[0.0, 1.0, 0.0], [0.0, 1.0, 2.0], [3.0, 3.0, 3.0]]; + +#[derive(Clone)] +pub(crate) struct SweepOutputs { + pub(crate) brep_json: Vec, + pub(crate) positions: Vec, + pub(crate) normals: Vec, + pub(crate) indices: Vec, + pub(crate) face_ids: Vec, + pub(crate) verdicts: Vec>, + pub(crate) step: Vec, + pub(crate) report: Vec, +} + +fn sweep_graph() -> Result { + let mut world = WorldGraph::new(standard())?; + world.create_primitive(rectangle(1.0, 1.0), create_options("profile"))?; + world.create_primitive(polyline(&SPATIAL_PATH, false), create_options("path"))?; + world.create_operation(sweep("profile", "path"), create_options("solid"))?; + Ok(world) +} + +pub(crate) fn sweep_outputs() -> Result { + let world = sweep_graph()?; + let body = world.brep("solid")?; + let mesh = tessellate(&body, 0.01, 2_000_000)?; + let verdicts = SWEEP_PROBES + .iter() + .map(|point| Ok(format!("{:?}", classify_point(&body, *point)?).into_bytes())) + .collect::, Failure>>()?; + let (step, report) = world.export_step(&["solid".into()], &StepOptions::default())?; + Ok(SweepOutputs { + brep_json: body.to_json()?.into_bytes(), + positions: mesh.positions, + normals: mesh.normals, + indices: mesh.indices, + face_ids: mesh.triangle_face_ids, + verdicts, + step: step.into_bytes(), + report: serde_json::to_vec(&report)?, + }) +} + +pub(crate) fn sweep_digest(outputs: &SweepOutputs) -> String { + let mut hash = Sha256::new(); + hash.update(&outputs.brep_json); + for value in &outputs.positions { + hash.update(value.to_bits().to_le_bytes()); + } + for value in &outputs.normals { + hash.update(value.to_bits().to_le_bytes()); + } + for value in outputs.indices.iter().chain(&outputs.face_ids) { + hash.update(value.to_le_bytes()); + } + for verdict in &outputs.verdicts { + hash.update(verdict); + } + hash.update(&outputs.step); + hash.update(&outputs.report); + hex(&hash.finalize()) +} + +fn fallback_record(name: &str) -> Result<(String, String), Failure> { + let path = Path::new(FALLBACK_DIRECTORY).join(format!("{name}.fallback.json")); + let fixture: Value = serde_json::from_slice(&fs::read(path)?)?; + let host: BrepEnvelope = serde_json::from_value(fixture["host"].clone())?; + let cutters: Vec = serde_json::from_value(fixture["cutters"].clone())?; + let key = format!("fallback.{name}"); + let id = format!("batch-{name}"); + let mut record = Record::new(&key); + record.section("fallback"); + record.field("id", &id); + let outcome = multi_tool_boolean(&host, &cutters, BooleanOp::Subtraction, id); + record.debug("handlers", &outcome.handlers); + record.field("tool_index", outcome.tool_index); + match outcome.result { + Ok(result) => result_body(&mut record, Ok(result)), + Err(error) => record.block("result.error", &pretty(to_value(&error))), + } + Ok((key, record.finish().0)) +} + +fn level_records() -> Vec<(String, String)> { + let scene = acceptance_scene(); + let mut records = Vec::new(); + for unit in LEVEL_UNITS { + for axis in LEVEL_UP_AXES { + let key = format!("level.step.{unit}.{}", axis.to_lowercase()); + let (text, report) = level_export(&scene, unit, axis); + let mut record = Record::new(&key); + record.block("report", &pretty(to_value(&report))); + record.block("text", &text); + records.push((key, record.finish().0)); + } + } + records +} + +pub(crate) fn records() -> Result, Failure> { + let mut records = FALLBACK_ROWS + .into_iter() + .map(fallback_record) + .collect::, _>>()?; + records.extend(level_records()); + Ok(records) +} diff --git a/main/opengeometry/tests/golden/fine_solids.rs b/main/opengeometry/tests/golden/fine_solids.rs new file mode 100644 index 0000000..348d380 --- /dev/null +++ b/main/opengeometry/tests/golden/fine_solids.rs @@ -0,0 +1,18 @@ +use crate::fixtures::{at, fine}; +use crate::kernel::{primitives, BrepEnvelope, GeometryError, Point3}; + +pub(crate) fn sphere_at( + name: &str, + center: Point3, + radius: f64, +) -> Result { + primitives::sphere(name.into(), at(center), radius, fine()) +} + +pub(crate) fn cuboid_at( + name: &str, + origin: Point3, + size: Point3, +) -> Result { + primitives::cuboid(name.into(), at(origin), size, fine()) +} diff --git a/main/opengeometry/tests/golden/fixtures.rs b/main/opengeometry/tests/golden/fixtures.rs new file mode 100644 index 0000000..5c37a93 --- /dev/null +++ b/main/opengeometry/tests/golden/fixtures.rs @@ -0,0 +1,109 @@ +use crate::kernel::{Accuracy, Frame3, GeometryError, Point3}; + +pub(crate) fn accuracy( + geometric: f64, + intersection: f64, + tessellation: f64, + exchange: f64, +) -> Accuracy { + Accuracy { + geometric, + intersection, + tessellation, + exchange, + } +} + +pub(crate) fn standard() -> Accuracy { + accuracy(1e-8, 1e-9, 0.01, 1e-6) +} + +pub(crate) fn fine() -> Accuracy { + accuracy(1e-9, 1e-10, 0.01, 1e-5) +} + +pub(crate) fn scaled_accuracy(size: f64) -> Accuracy { + accuracy(size * 1e-9, size * 1e-10, size * 0.01, size * 1e-5) +} + +pub(crate) fn ground() -> Frame3 { + Frame3 { + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + ..Frame3::IDENTITY + } +} + +pub(crate) fn upright(origin: Point3) -> Frame3 { + Frame3 { + origin, + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, 1.0], + z: [0.0, -1.0, 0.0], + } +} + +pub(crate) fn at(origin: Point3) -> Frame3 { + Frame3 { + origin, + ..Frame3::IDENTITY + } +} + +pub(crate) fn moved(frame: Frame3, origin: Point3) -> Frame3 { + Frame3 { origin, ..frame } +} + +pub(crate) fn tilted_about_y(origin: Point3, angle: f64) -> Frame3 { + Frame3 { + origin, + x: [angle.cos(), 0.0, -angle.sin()], + y: [0.0, 1.0, 0.0], + z: [angle.sin(), 0.0, angle.cos()], + } +} + +pub(crate) fn tilted_about_x(origin: Point3, angle: f64) -> Frame3 { + Frame3 { + origin, + x: [1.0, 0.0, 0.0], + y: [0.0, angle.cos(), -angle.sin()], + z: [0.0, angle.sin(), angle.cos()], + } +} + +pub(crate) fn turned_about_z(origin: Point3, angle: f64) -> Frame3 { + Frame3 { + origin, + x: [angle.cos(), angle.sin(), 0.0], + y: [-angle.sin(), angle.cos(), 0.0], + z: [0.0, 0.0, 1.0], + } +} + +pub(crate) fn y_axis_frame(origin: Point3) -> Result { + Frame3::from_axis(origin, [0.0, 1.0, 0.0], [0.8, 0.0, 0.6]) +} + +pub(crate) fn reversed_z(origin: Point3) -> Frame3 { + Frame3 { + origin, + x: [1.0, 0.0, 0.0], + y: [0.0, -1.0, 0.0], + z: [0.0, 0.0, -1.0], + } +} + +pub(crate) fn rectangle(lo: [f64; 2], hi: [f64; 2]) -> Vec<[f64; 2]> { + vec![ + [lo[0], lo[1]], + [hi[0], lo[1]], + [hi[0], hi[1]], + [lo[0], hi[1]], + ] +} + +pub(crate) fn polar(radius: f64, angle: f64) -> [f64; 2] { + [radius * angle.cos(), radius * angle.sin()] +} diff --git a/main/opengeometry/tests/golden/golden_file.rs b/main/opengeometry/tests/golden/golden_file.rs new file mode 100644 index 0000000..2ae1cf5 --- /dev/null +++ b/main/opengeometry/tests/golden/golden_file.rs @@ -0,0 +1,171 @@ +use crate::digest::sha256; +use crate::record::Failure; +use std::collections::BTreeMap; +use std::fmt::{self, Display, Formatter}; +use std::fs; +use std::path::{Path, PathBuf}; + +pub(crate) const RECORD_INSTRUCTION: &str = "record it with `OG_GOLDEN_UPDATE=1 cargo test --offline --test golden` from opengeometry/ in the debug profile, then review and commit tests/fixtures/golden/.json"; + +const GOLDEN_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/golden"); + +const DIFFERING_DIRECTORY: &str = concat!(env!("CARGO_TARGET_TMPDIR"), "/golden"); + +const COMMITTED: [(&str, &str); 1] = [( + "aarch64-apple-darwin", + include_str!("../fixtures/golden/aarch64-apple-darwin.json"), +)]; + +type Golden = BTreeMap; + +#[derive(Debug, PartialEq)] +pub(crate) enum Mode { + Compare, + Build, + Record, + Dump(PathBuf), +} + +#[derive(Debug)] +pub(crate) struct Mismatch { + pub(crate) missing: Vec, + pub(crate) stale: Vec, + pub(crate) differing: Vec, + unrecorded: bool, +} + +impl Display for Mismatch { + fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { + if self.unrecorded { + return write!( + formatter, + "no golden is recorded for this target ({} scenes); {RECORD_INSTRUCTION}", + self.missing.len() + ); + } + let groups = [ + ("scenes missing from the golden", &self.missing), + ("golden keys with no scene", &self.stale), + ("scenes differing from the golden", &self.differing), + ]; + for (title, keys) in groups.iter().filter(|(_, keys)| !keys.is_empty()) { + writeln!(formatter, "{title} ({}): {}", keys.len(), keys.join(", "))?; + } + write!(formatter, "if the change is intended, {RECORD_INSTRUCTION}") + } +} + +pub(crate) fn current_target() -> String { + let system = if cfg!(target_os = "macos") { + "apple-darwin" + } else if cfg!(target_os = "linux") { + "unknown-linux-gnu" + } else { + std::env::consts::OS + }; + format!("{}-{system}", std::env::consts::ARCH).replace('_', "-") +} + +pub(crate) fn parse(text: &str) -> Result { + Ok(serde_json::from_str(text)?) +} + +pub(crate) fn committed(target: &str) -> Result, Failure> { + COMMITTED + .iter() + .find(|(stem, _)| *stem == target) + .map(|(_, text)| parse(text)) + .transpose() +} + +pub(crate) fn digests(texts: &BTreeMap) -> Golden { + texts + .iter() + .map(|(key, text)| (key.clone(), sha256(text.as_bytes()))) + .collect() +} + +pub(crate) fn compare(expected: &Golden, actual: &Golden) -> Result<(), Mismatch> { + let absent = |keys: &Golden, other: &Golden| { + keys.keys() + .filter(|key| !other.contains_key(*key)) + .cloned() + .collect::>() + }; + let mismatch = Mismatch { + missing: absent(actual, expected), + stale: absent(expected, actual), + differing: actual + .iter() + .filter(|(key, digest)| expected.get(*key).is_some_and(|golden| golden != *digest)) + .map(|(key, _)| key.clone()) + .collect(), + unrecorded: expected.is_empty(), + }; + if mismatch.missing.is_empty() && mismatch.stale.is_empty() && mismatch.differing.is_empty() { + return Ok(()); + } + Err(mismatch) +} + +pub(crate) fn mode(update: Option<&str>, ci: Option<&str>, dump: Option) -> Mode { + if let Some(directory) = dump { + return Mode::Dump(directory); + } + if ci == Some("true") { + return Mode::Build; + } + if update == Some("1") { + return Mode::Record; + } + Mode::Compare +} + +pub(crate) fn record(target: &str, actual: &Golden) -> Result { + let file = Path::new(GOLDEN_DIRECTORY).join(format!("{target}.json")); + fs::write( + &file, + format!("{}\n", serde_json::to_string_pretty(actual)?), + )?; + Ok(file) +} + +fn prepare(output: &Path) -> Result<(), Failure> { + fs::create_dir_all(output)?; + if fs::read_dir(output)?.next().is_some() { + return Err(format!("output directory {} must be empty", output.display()).into()); + } + Ok(()) +} + +fn write_texts<'a>( + directory: &Path, + texts: impl IntoIterator, +) -> Result<(), Failure> { + for (name, text) in texts { + fs::write(directory.join(format!("{name}.txt")), text)?; + } + Ok(()) +} + +pub(crate) fn dump(directory: &Path, texts: &BTreeMap) -> Result<(), Failure> { + prepare(directory)?; + write_texts(directory, texts) +} + +pub(crate) fn write_differing( + target: &str, + differing: &[String], + texts: &BTreeMap, +) -> Result { + let directory = Path::new(DIFFERING_DIRECTORY).join(target); + if directory.exists() { + fs::remove_dir_all(&directory)?; + } + fs::create_dir_all(&directory)?; + write_texts( + &directory, + differing.iter().filter_map(|key| texts.get_key_value(key)), + )?; + Ok(directory) +} diff --git a/main/opengeometry/tests/golden/graph_case.rs b/main/opengeometry/tests/golden/graph_case.rs new file mode 100644 index 0000000..617c305 --- /dev/null +++ b/main/opengeometry/tests/golden/graph_case.rs @@ -0,0 +1,130 @@ +use crate::body::record_body; +use crate::fixtures::standard; +use crate::graph_record::{counters, created, node_state}; +use crate::kernel::{ + CopyOptions, CreateOptions, CreatingOperation, Plane, Point3, Primitive, WorldGraph, +}; +use crate::record::{Failure, Record}; +use crate::runner::Case; + +pub(crate) trait Script: + Fn(&mut WorldGraph, &mut Record) -> Result<(), Failure> + Send + Sync + 'static +{ +} + +impl Script for T where + T: Fn(&mut WorldGraph, &mut Record) -> Result<(), Failure> + Send + Sync + 'static +{ +} + +pub(crate) fn graph_case(name: &str, script: impl Script) -> Case { + Case::new(name, move |record| { + let mut graph = WorldGraph::new(standard())?; + script(&mut graph, record)?; + record.section("final"); + counters(record, &graph); + Ok(()) + }) +} + +pub(crate) fn create_options(og_id: &str) -> CreateOptions { + CreateOptions { + og_id: Some(og_id.into()), + ..CreateOptions::default() + } +} + +pub(crate) fn child(og_id: &str, parent: &str) -> CreateOptions { + CreateOptions { + parent: Some(parent.into()), + ..create_options(og_id) + } +} + +pub(crate) fn on_plane( + options: CreateOptions, + origin: Point3, + normal: Point3, + x_direction: Point3, +) -> CreateOptions { + CreateOptions { + plane: Some(Plane { + origin: Some(origin), + normal: Some(normal), + x_direction: Some(x_direction), + }), + ..options + } +} + +pub(crate) fn copy_options(og_id: &str, parent: Option>) -> CopyOptions { + CopyOptions { + og_id: Some(og_id.into()), + parent: parent.map(|parent| parent.map(str::to_string)), + } +} + +pub(crate) fn rectangle(width: f64, breadth: f64) -> Primitive { + Primitive::Rectangle { width, breadth } +} + +pub(crate) fn circle(radius: f64) -> Primitive { + Primitive::Circle { radius } +} + +pub(crate) fn cuboid(width: f64, height: f64, depth: f64) -> Primitive { + Primitive::Cuboid { + width, + height, + depth, + } +} + +pub(crate) fn polyline(points: &[Point3], closed: bool) -> Primitive { + Primitive::Polyline { + points: points.to_vec(), + closed, + } +} + +pub(crate) fn extrude(profile: &str, holes: &[&str], distance: f64) -> CreatingOperation { + CreatingOperation::Extrude { + profile: profile.into(), + holes: holes.iter().map(|hole| hole.to_string()).collect(), + distance, + } +} + +pub(crate) fn sweep(profile: &str, path: &str) -> CreatingOperation { + CreatingOperation::Sweep { + profile: profile.into(), + path: path.into(), + } +} + +pub(crate) fn primitive( + record: &mut Record, + graph: &mut WorldGraph, + primitive: Primitive, + options: CreateOptions, +) -> Option { + let label = format!("create {:?} {primitive:?}", options.og_id); + created(record, &label, graph.create_primitive(primitive, options)) +} + +pub(crate) fn operation( + record: &mut Record, + graph: &mut WorldGraph, + operation: CreatingOperation, + options: CreateOptions, +) -> Option { + let label = format!("operation {:?} {operation:?}", options.og_id); + created(record, &label, graph.create_operation(operation, options)) +} + +pub(crate) fn inspect(record: &mut Record, graph: &WorldGraph, og_id: &str) -> Result<(), Failure> { + node_state(record, graph, og_id); + let shape = graph.brep(og_id)?; + record_body(record, og_id, &shape); + Ok(()) +} diff --git a/main/opengeometry/tests/golden/graph_operate.rs b/main/opengeometry/tests/golden/graph_operate.rs new file mode 100644 index 0000000..d95e429 --- /dev/null +++ b/main/opengeometry/tests/golden/graph_operate.rs @@ -0,0 +1,309 @@ +use crate::graph_case::{ + child, copy_options, create_options, cuboid, extrude, graph_case, inspect, operation, polyline, + primitive, rectangle, sweep, Script, +}; +use crate::graph_record::{changes, created, node_state, record_operation}; +use crate::kernel::{EditScope, ModifyingOperation, Primitive, Transform, WorldGraph}; +use crate::record::{Failure, Record}; +use crate::runner::Case; + +const SUBTRACT: ModifyingOperation = ModifyingOperation::Subtract; + +fn translate(record: &mut Record, graph: &mut WorldGraph, og_id: &str, offset: [f64; 3]) { + let result = graph.transform(og_id, Transform::Translate { offset }); + changes(record, &format!("translate {og_id}"), result); +} + +fn operate( + record: &mut Record, + graph: &mut WorldGraph, + target: &str, + kind: ModifyingOperation, + tools: &[&str], + scope: EditScope, +) { + let label = format!("operate {target} {kind:?} {tools:?} {scope:?}"); + record_operation(record, graph, &label, target, kind, tools, scope); +} + +fn instance(record: &mut Record, graph: &mut WorldGraph, source: &str, og_id: &str) { + let options = copy_options(og_id, None); + created( + record, + &format!("instance {og_id}"), + graph.instance(source, options), + ); +} + +fn host_and_tool(record: &mut Record, graph: &mut WorldGraph, offset: [f64; 3]) { + primitive(record, graph, cuboid(2.0, 2.0, 2.0), create_options("host")); + primitive(record, graph, cuboid(2.0, 2.0, 2.0), create_options("tool")); + translate(record, graph, "tool", offset); +} + +fn box_subtraction(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + host_and_tool(record, graph, [1.0, 0.0, 0.0]); + operate(record, graph, "host", SUBTRACT, &["tool"], EditScope::Node); + inspect(record, graph, "host")?; + node_state(record, graph, "tool"); + Ok(()) +} + +fn shared_target(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + host_and_tool(record, graph, [1.0, 0.0, 0.0]); + instance(record, graph, "host", "host-copy"); + instance(record, graph, "tool", "tool-copy"); + operate( + record, + graph, + "host", + SUBTRACT, + &["tool-copy"], + EditScope::Node, + ); + operate( + record, + graph, + "host", + SUBTRACT, + &["tool-copy"], + EditScope::AllInstances, + ); + operate( + record, + graph, + "host", + SUBTRACT, + &["host-copy"], + EditScope::AllInstances, + ); + inspect(record, graph, "host")?; + node_state(record, graph, "host-copy"); + Ok(()) +} + +fn empty_and_limits(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + host_and_tool(record, graph, [10.0, 0.0, 0.0]); + let intersect = ModifyingOperation::Intersect; + operate(record, graph, "host", intersect, &["tool"], EditScope::Node); + let union = ModifyingOperation::Union; + operate(record, graph, "host", union, &[], EditScope::Node); + operate(record, graph, "host", union, &["host"], EditScope::Node); + operate(record, graph, "host", union, &["missing"], EditScope::Node); + node_state(record, graph, "host"); + Ok(()) +} + +fn empty_subtraction(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive( + record, + graph, + cuboid(1.0, 1.0, 1.0), + create_options("small"), + ); + primitive( + record, + graph, + cuboid(2.0, 2.0, 2.0), + create_options("large"), + ); + operate( + record, + graph, + "small", + SUBTRACT, + &["large"], + EditScope::Node, + ); + node_state(record, graph, "small"); + Ok(()) +} + +fn unique_then_operate(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + host_and_tool(record, graph, [1.0, 0.0, 0.0]); + instance(record, graph, "host", "instance"); + let unique = graph.make_unique("host").map(|changes| changes.is_some()); + record.debug("make_unique", unique); + operate(record, graph, "host", SUBTRACT, &["tool"], EditScope::Node); + inspect(record, graph, "host")?; + node_state(record, graph, "instance"); + Ok(()) +} + +fn planar_batch(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive(record, graph, cuboid(6.0, 2.0, 2.0), create_options("host")); + for (name, x) in [("left", -1.5), ("right", 1.5)] { + primitive(record, graph, cuboid(1.0, 2.0, 2.0), create_options(name)); + translate(record, graph, name, [x, 0.0, 0.0]); + } + operate( + record, + graph, + "host", + SUBTRACT, + &["left", "right"], + EditScope::Node, + ); + inspect(record, graph, "host") +} + +fn mixed_batch(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive( + record, + graph, + rectangle(6.0, 0.3), + create_options("profile"), + ); + operation( + record, + graph, + extrude("profile", &[], 3.0), + create_options("wall"), + ); + primitive(record, graph, cuboid(0.8, 3.0, 1.0), create_options("rect")); + translate(record, graph, "rect", [1.5, 0.0, 0.5]); + let round = Primitive::Cylinder { + radius: 0.4, + height: 2.0, + }; + primitive(record, graph, round, create_options("round")); + let rotate = Transform::Rotate { + axis: [1.0, 0.0, 0.0], + degrees: 90.0, + pivot: Some([0.0; 3]), + }; + changes(record, "rotate round", graph.transform("round", rotate)); + translate(record, graph, "round", [-1.5, 1.5, -1.0]); + operate( + record, + graph, + "wall", + SUBTRACT, + &["rect", "round"], + EditScope::Node, + ); + inspect(record, graph, "wall") +} + +fn serial_tools(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive(record, graph, cuboid(4.0, 2.0, 2.0), create_options("host")); + for (name, x) in [("first", 1.0), ("second", -1.0)] { + primitive(record, graph, cuboid(2.0, 2.0, 2.0), create_options(name)); + translate(record, graph, name, [x, 0.5, 0.0]); + } + let union = ModifyingOperation::Union; + operate( + record, + graph, + "host", + union, + &["first", "second"], + EditScope::Node, + ); + inspect(record, graph, "host")?; + let intersect = ModifyingOperation::Intersect; + operate( + record, + graph, + "host", + intersect, + &["first", "second"], + EditScope::Node, + ); + node_state(record, graph, "host"); + Ok(()) +} + +fn scaled_tool(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + host_and_tool(record, graph, [1.0, 0.0, 0.0]); + let scale = Transform::Scale { + factor: 2.0, + pivot: None, + }; + changes(record, "scale tool", graph.transform("tool", scale)); + operate(record, graph, "host", SUBTRACT, &["tool"], EditScope::Node); + Ok(()) +} + +fn swept_host(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive( + record, + graph, + rectangle(2.0, 2.0), + create_options("profile"), + ); + let path = polyline(&[[0.0; 3], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]], false); + primitive(record, graph, path, create_options("path")); + operation( + record, + graph, + sweep("profile", "path"), + create_options("sweep"), + ); + primitive(record, graph, cuboid(1.0, 1.0, 1.0), create_options("tool")); + operate(record, graph, "sweep", SUBTRACT, &["tool"], EditScope::Node); + inspect(record, graph, "sweep") +} + +fn level_members(record: &mut Record, graph: &mut WorldGraph, wall: bool) { + created( + record, + "level", + graph.create_system_assembly(create_options("level")), + ); + if wall { + primitive( + record, + graph, + rectangle(4.0, 0.5), + child("profile", "level"), + ); + let host = child("host", "level"); + operation(record, graph, extrude("profile", &[], 3.0), host); + primitive(record, graph, cuboid(0.8, 3.0, 1.0), child("tool", "level")); + translate(record, graph, "tool", [0.0, 0.0, 0.5]); + } else { + primitive(record, graph, cuboid(2.0, 2.0, 2.0), child("host", "level")); + primitive(record, graph, cuboid(2.0, 2.0, 2.0), child("tool", "level")); + translate(record, graph, "tool", [1.0, 0.0, 0.0]); + } +} + +fn corotated(degrees: f64, wall: bool) -> impl Script { + move |graph: &mut WorldGraph, record: &mut Record| { + level_members(record, graph, wall); + let rotate = Transform::Rotate { + axis: [0.0, 1.0, 0.0], + degrees, + pivot: Some([0.0; 3]), + }; + changes(record, "rotate level", graph.transform("level", rotate)); + operate(record, graph, "host", SUBTRACT, &["tool"], EditScope::Node); + inspect(record, graph, "host") + } +} + +pub(crate) fn cases() -> Vec { + let mut cases = vec![ + graph_case("graph.operate.box-subtraction", box_subtraction), + graph_case("graph.operate.shared-target", shared_target), + graph_case("graph.operate.empty-and-limits", empty_and_limits), + graph_case("graph.operate.empty-subtraction", empty_subtraction), + graph_case( + "graph.operate.make-unique-then-operate", + unique_then_operate, + ), + graph_case("graph.operate.planar-batch", planar_batch), + graph_case("graph.operate.mixed-cutter-batch", mixed_batch), + graph_case("graph.operate.serial-union-and-intersection", serial_tools), + graph_case("graph.operate.scaled-tool", scaled_tool), + graph_case("graph.operate.sweep-result", swept_host), + ]; + for degrees in [0.0, 30.0, 90.0] { + let box_name = format!("graph.operate.corotated-box-{degrees}"); + cases.push(graph_case(&box_name, corotated(degrees, false))); + let wall_name = format!("graph.operate.corotated-wall-{degrees}"); + cases.push(graph_case(&wall_name, corotated(degrees, true))); + } + cases +} diff --git a/main/opengeometry/tests/golden/graph_record.rs b/main/opengeometry/tests/golden/graph_record.rs new file mode 100644 index 0000000..4828c92 --- /dev/null +++ b/main/opengeometry/tests/golden/graph_record.rs @@ -0,0 +1,161 @@ +use crate::digest::{bits_list, sha256}; +use crate::json::pretty; +use crate::kernel::{ + ChangeSet, EditScope, GraphError, ModifyingOperation, StepOptions, WorldGraph, +}; +use crate::record::Record; +use serde_json::{to_value, Value}; + +pub(crate) fn changes( + record: &mut Record, + label: &str, + result: Result, +) -> bool { + match result { + Ok(changes) => { + record.block(&format!("step {label}"), &pretty(to_value(&changes))); + true + } + Err(error) => { + record.graph_error(&format!("step {label}"), &error); + false + } + } +} + +pub(crate) fn record_operation( + record: &mut Record, + graph: &mut WorldGraph, + label: &str, + target: &str, + kind: ModifyingOperation, + tools: &[&str], + scope: EditScope, +) { + let tools = tools + .iter() + .map(|tool| tool.to_string()) + .collect::>(); + changes(record, label, graph.operate(target, kind, &tools, scope)); +} + +pub(crate) fn created( + record: &mut Record, + label: &str, + result: Result<(String, ChangeSet), GraphError>, +) -> Option { + match result { + Ok((og_id, changes)) => { + record.section(&format!("step {label}")); + record.field("created", &og_id); + record.line(&pretty(to_value(&changes))); + Some(og_id) + } + Err(error) => { + record.graph_error(&format!("step {label}"), &error); + None + } + } +} + +pub(crate) fn counters(record: &mut Record, graph: &WorldGraph) { + record.field("revision", graph.revision()); + record.field("node_count", graph.node_count()); + record.field("shape_count", graph.shape_count()); + record.debug("mark_stats", graph.mark_stats()); + match graph.reserve(1, &[]) { + Ok(reserved) => record.debug("next_reservation", reserved), + Err(error) => record.graph_error_field("next_reservation.error", &error), + } +} + +fn placement_state(record: &mut Record, graph: &WorldGraph, og_id: &str) { + match graph.placement(og_id) { + Ok(placement) => record.debug("placement", placement), + Err(error) => record.graph_error_field("placement.error", &error), + } + match graph.world_placement(og_id) { + Ok(world) => record.debug("world_placement", world), + Err(error) => record.graph_error_field("world_placement.error", &error), + } + match graph.world_matrix(og_id) { + Ok(matrix) => record.field("world_matrix", bits_list(&matrix)), + Err(error) => record.graph_error_field("world_matrix.error", &error), + } + match graph.bounds(og_id) { + Ok(Some(bounds)) => record.field("bounds", bits_list(&bounds)), + Ok(None) => record.line("bounds: none"), + Err(error) => record.graph_error_field("bounds.error", &error), + } +} + +fn cover_report(record: &mut Record, report: &Value) { + let names = report.get("handlers").and_then(Value::as_array); + for name in names.into_iter().flatten().filter_map(Value::as_str) { + record.cover(name); + } +} + +fn shape_state(record: &mut Record, graph: &WorldGraph, shape_id: &str, og_id: &str) { + match graph.shape(og_id) { + Ok(shape) => { + record.field("shape.revision", shape.revision); + record.field("shape.users", shape.users); + record.debug("shape.edge_keys", &shape.edge_keys); + record.debug("shape.body_type", shape.brep.body_type()); + match &shape.report { + Some(report) => { + record.field("shape.report", pretty(to_value(report))); + cover_report(record, report); + } + None => record.line("shape.report: none"), + } + } + Err(error) => record.graph_error_field("shape.error", &error), + } + match graph.snapshot(shape_id) { + Ok(bytes) => record.field("snapshot.sha256", sha256(&bytes)), + Err(error) => record.graph_error_field("snapshot.error", &error), + } +} + +pub(crate) fn node_state(record: &mut Record, graph: &WorldGraph, og_id: &str) { + record.section(&format!("node {og_id}")); + let node = match graph.node(og_id) { + Ok(node) => node.clone(), + Err(error) => { + record.graph_error_field("error", &error); + return; + } + }; + record.debug("node", &node); + match graph.node_by_handle(node.handle, node.generation) { + Ok(found) => record.field("by_handle", &found.og_id), + Err(error) => record.graph_error_field("by_handle.error", &error), + } + placement_state(record, graph, og_id); + if let Some(shape_id) = &node.shape { + shape_state(record, graph, shape_id, og_id); + } +} + +pub(crate) fn export( + record: &mut Record, + graph: &WorldGraph, + label: &str, + nodes: &[String], + options: &StepOptions, +) { + let title = format!("export {label}"); + match graph.export_step(nodes, options) { + Ok((text, report)) => { + record.section(&title); + record.debug("nodes", nodes); + record.line(&pretty(to_value(options))); + record.field("text.sha256", sha256(text.as_bytes())); + record.line(&pretty(to_value(&report))); + record.block(&format!("{title}.text"), &text); + } + Err(error) => record.graph_error(&title, &error), + } +} diff --git a/main/opengeometry/tests/golden/graph_scenarios.rs b/main/opengeometry/tests/golden/graph_scenarios.rs new file mode 100644 index 0000000..76db381 --- /dev/null +++ b/main/opengeometry/tests/golden/graph_scenarios.rs @@ -0,0 +1,460 @@ +use crate::graph_case::{ + child, copy_options, create_options, cuboid, graph_case, on_plane, polyline, primitive, +}; +use crate::graph_record::{changes, counters, created, node_state}; +use crate::kernel::{CreateOptions, EditScope, Primitive, Transform, WorldGraph}; +use crate::record::{Failure, Record}; +use crate::runner::Case; + +fn unit_box() -> Primitive { + cuboid(2.0, 2.0, 2.0) +} + +fn nodes(record: &mut Record, graph: &WorldGraph, ids: &[&str]) { + for og_id in ids { + node_state(record, graph, og_id); + } + record.section("counters"); + counters(record, graph); +} + +fn sharing(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive(record, graph, unit_box(), create_options("body")); + created( + record, + "instance", + graph.instance("body", copy_options("instance", None)), + ); + record.graph_result( + "ensure node", + &graph + .ensure_editable("body", EditScope::Node) + .map(|shape| shape.revision), + ); + record.graph_result( + "ensure all", + &graph + .ensure_editable("body", EditScope::AllInstances) + .map(|shape| shape.revision), + ); + created( + record, + "duplicate", + graph.duplicate("body", copy_options("duplicate", None)), + ); + record.graph_result( + "make unique instance", + &graph + .make_unique("instance") + .map(|changes| changes.is_some()), + ); + record.graph_result( + "make unique body", + &graph.make_unique("body").map(|changes| changes.is_some()), + ); + nodes(record, graph, &["body", "instance", "duplicate"]); + Ok(()) +} + +fn reparenting(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + created( + record, + "a", + graph.create_system_assembly(create_options("a")), + ); + created( + record, + "b", + graph.create_system_assembly(create_options("b")), + ); + primitive(record, graph, unit_box(), child("child", "a")); + changes( + record, + "move a", + graph.transform( + "a", + Transform::Translate { + offset: [10.0, 0.0, 0.0], + }, + ), + ); + changes( + record, + "to b local", + graph.add_child("b", &["child".into()], false), + ); + changes( + record, + "to a world", + graph.add_child("a", &["child".into()], true), + ); + changes( + record, + "remove world", + graph.remove_child("a", "child", true), + ); + nodes(record, graph, &["a", "b", "child"]); + Ok(()) +} + +fn place_round_trip(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive(record, graph, unit_box(), create_options("body")); + let empty = Transform::Place { + origin: None, + x_direction: None, + normal: None, + scale: None, + }; + changes(record, "place empty", graph.transform("body", empty)); + for (label, transform) in [ + ( + "rotate quarter", + Transform::Rotate { + axis: [0.0, 1.0, 0.0], + degrees: 90.0, + pivot: Some([0.0; 3]), + }, + ), + ( + "rotate skew", + Transform::Rotate { + axis: [0.1, 0.2, 0.3], + degrees: 13.37, + pivot: None, + }, + ), + ( + "scale about pivot", + Transform::Scale { + factor: 1.5, + pivot: Some([1.0, 0.0, 0.0]), + }, + ), + ] { + changes(record, label, graph.transform("body", transform)); + let placement = graph.placement("body")?; + let place = Transform::Place { + origin: Some(placement.origin), + x_direction: Some(placement.x_direction), + normal: Some(placement.normal), + scale: Some(placement.scale), + }; + changes( + record, + &format!("{label} place round trip"), + graph.transform("body", place), + ); + node_state(record, graph, "body"); + } + Ok(()) +} + +fn failed_calls(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + created( + record, + "a", + graph.create_system_assembly(create_options("a")), + ); + created(record, "b", graph.create_system_assembly(child("b", "a"))); + changes(record, "cycle", graph.add_child("b", &["a".into()], false)); + created( + record, + "duplicate id", + graph.create_system_assembly(create_options("a")), + ); + changes( + record, + "negative scale", + graph.transform( + "a", + Transform::Scale { + factor: -1.0, + pivot: None, + }, + ), + ); + nodes(record, graph, &["a", "b"]); + Ok(()) +} + +fn rejected_primitives(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + let zero = Primitive::Cylinder { + radius: 0.0, + height: 2.0, + }; + created( + record, + "zero radius", + graph.create_primitive(zero, CreateOptions::default()), + ); + created( + record, + "short polyline", + graph.create_primitive( + polyline(&[[0.0; 3], [0.0; 3]], false), + CreateOptions::default(), + ), + ); + let huge = Primitive::Cuboid { + width: 2e6, + height: 2.0, + depth: 2.0, + }; + created( + record, + "precision guard", + graph.create_primitive(huge, CreateOptions::default()), + ); + let bad_plane = on_plane( + CreateOptions::default(), + [0.0; 3], + [0.0; 3], + [1.0, 0.0, 0.0], + ); + created( + record, + "singular plane", + graph.create_primitive(unit_box(), bad_plane), + ); + let generated = created( + record, + "generated", + graph.create_primitive(unit_box(), CreateOptions::default()), + ); + if let Some(generated) = generated { + let node = graph.node(&generated)?.clone(); + changes(record, "dispose generated", graph.dispose(&generated)); + record.graph_result( + "disposed handle", + &graph + .node_by_handle(node.handle, node.generation) + .map(|node| node.og_id.clone()), + ); + } + created( + record, + "next generated", + graph.create_primitive(unit_box(), CreateOptions::default()), + ); + counters(record, graph); + Ok(()) +} + +fn marks(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive(record, graph, unit_box(), create_options("body")); + let mark = graph.mark()?; + created( + record, + "instance", + graph.instance("body", copy_options("instance", None)), + ); + record.graph_result( + "make unique", + &graph + .make_unique("instance") + .map(|changes| changes.is_some()), + ); + let later = graph.mark()?; + changes(record, "dispose body", graph.dispose("body")); + record.debug("stats before rollback", graph.mark_stats()); + changes(record, "rollback", graph.rollback(mark)); + changes(record, "rollback later", graph.rollback(later)); + created( + record, + "after rollback", + graph.create_primitive(unit_box(), CreateOptions::default()), + ); + record.graph_result("release", &graph.release(mark)); + nodes(record, graph, &["body", "instance"]); + Ok(()) +} + +fn nested_marks(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + let baseline = graph.revision(); + let outer = graph.mark()?; + primitive(record, graph, unit_box(), create_options("body")); + let inner = graph.mark()?; + created( + record, + "temporary", + graph.instance("body", copy_options("temporary", None)), + ); + record.debug("stats with inner", graph.mark_stats()); + changes(record, "rollback inner", graph.rollback(inner)); + record.graph_result("release inner", &graph.release(inner)); + changes(record, "rollback outer", graph.rollback(outer)); + changes( + record, + "changes since baseline", + graph.changes_since(baseline), + ); + record.graph_result("release outer", &graph.release(outer)); + nodes(record, graph, &["body", "temporary"]); + Ok(()) +} + +fn shared_rebuild(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + primitive(record, graph, unit_box(), create_options("body")); + created( + record, + "instance", + graph.instance("body", copy_options("instance", None)), + ); + changes( + record, + "rebuild node", + graph.rebuild_primitive("body", unit_box(), EditScope::Node), + ); + let mark = graph.mark()?; + changes( + record, + "rebuild wider", + graph.rebuild_primitive("body", cuboid(3.0, 2.0, 2.0), EditScope::AllInstances), + ); + changes(record, "rollback", graph.rollback(mark)); + changes( + record, + "rebuild widest", + graph.rebuild_primitive("body", cuboid(4.0, 2.0, 2.0), EditScope::AllInstances), + ); + let rectangle = Primitive::Rectangle { + width: 2.0, + breadth: 2.0, + }; + changes( + record, + "rebuild type mismatch", + graph.rebuild_primitive("body", rectangle, EditScope::AllInstances), + ); + record.graph_result("release", &graph.release(mark)); + nodes(record, graph, &["body", "instance"]); + Ok(()) +} + +fn subtree_dispose(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + created( + record, + "parent", + graph.create_system_assembly(create_options("parent")), + ); + let mark = graph.mark()?; + let options = CreateOptions { + parent: Some("parent".into()), + ..CreateOptions::default() + }; + created( + record, + "generated child", + graph.create_primitive(unit_box(), options), + ); + changes(record, "dispose parent", graph.dispose("parent")); + changes(record, "rollback", graph.rollback(mark)); + created( + record, + "next generated", + graph.create_primitive(unit_box(), CreateOptions::default()), + ); + record.graph_result("release", &graph.release(mark)); + nodes(record, graph, &["parent"]); + Ok(()) +} + +fn copies_and_planes(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + created( + record, + "a", + graph.create_system_assembly(create_options("a")), + ); + created( + record, + "b", + graph.create_system_assembly(create_options("b")), + ); + changes( + record, + "move a", + graph.transform( + "a", + Transform::Translate { + offset: [7.0, 0.0, 0.0], + }, + ), + ); + changes( + record, + "move b", + graph.transform( + "b", + Transform::Translate { + offset: [-3.0, 0.0, 0.0], + }, + ), + ); + primitive(record, graph, unit_box(), child("body", "a")); + created( + record, + "instance under b", + graph.instance("body", copy_options("copy", Some(Some("b")))), + ); + created( + record, + "duplicate at root", + graph.duplicate("body", copy_options("root-copy", Some(None))), + ); + let planned = on_plane( + child("planned", "a"), + [4.0, 5.0, 6.0], + [0.0, 0.0, 1.0], + [0.0, 1.0, 0.0], + ); + primitive(record, graph, unit_box(), planned); + record.graph_result( + "relative copy to body", + &graph.relative_placement("copy", "body"), + ); + nodes( + record, + graph, + &["a", "b", "body", "copy", "root-copy", "planned"], + ); + Ok(()) +} + +fn many_rotations(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + created( + record, + "pivot", + graph.create_system_assembly(create_options("pivot")), + ); + let rotate = Transform::Rotate { + axis: [0.1, 0.2, 0.3], + degrees: 0.001, + pivot: None, + }; + let mut failures = 0_usize; + for _ in 0..100_000 { + if graph.transform("pivot", rotate).is_err() { + failures += 1; + } + } + record.section("rotations"); + record.field("failures", failures); + nodes(record, graph, &["pivot"]); + Ok(()) +} + +pub(crate) fn cases() -> Vec { + vec![ + graph_case("graph.scenario.instances-and-duplicates", sharing), + graph_case("graph.scenario.reparenting", reparenting), + graph_case("graph.scenario.place-round-trip", place_round_trip), + graph_case("graph.scenario.failed-calls", failed_calls), + graph_case("graph.scenario.rejected-primitives", rejected_primitives), + graph_case("graph.scenario.marks", marks), + graph_case("graph.scenario.nested-marks", nested_marks), + graph_case("graph.scenario.shared-rebuild", shared_rebuild), + graph_case("graph.scenario.subtree-dispose", subtree_dispose), + graph_case("graph.scenario.copies-and-planes", copies_and_planes), + graph_case("graph.scenario.many-rotations", many_rotations), + ] +} diff --git a/main/opengeometry/tests/golden/graph_session.rs b/main/opengeometry/tests/golden/graph_session.rs new file mode 100644 index 0000000..0350eff --- /dev/null +++ b/main/opengeometry/tests/golden/graph_session.rs @@ -0,0 +1,368 @@ +use crate::graph_case::{ + child, circle, copy_options, create_options, cuboid, extrude, graph_case, inspect, on_plane, + operation, polyline, primitive, rectangle, sweep, +}; +use crate::graph_record::{changes, created, export, record_operation}; +use crate::graph_state::{state, Tracked}; +use crate::kernel::{ + ChangeSet, CreateOptions, EditScope, GraphError, ModifyingOperation, StepOptions, Transform, + WorldGraph, +}; +use crate::record::{Failure, Record}; +use crate::runner::Case; + +struct Scene { + level: String, + profile: String, + wall: String, + cutter: String, + rail: String, +} + +fn unique(record: &mut Record, label: &str, result: Result, GraphError>) { + match result { + Ok(Some(changeset)) => { + changes(record, label, Ok(changeset)); + } + Ok(None) => record.block(&format!("step {label}"), "unchanged"), + Err(error) => record.graph_error(&format!("step {label}"), &error), + } +} + +fn subtract( + record: &mut Record, + graph: &mut WorldGraph, + label: &str, + target: &str, + tools: &[&str], +) { + let subtract = ModifyingOperation::Subtract; + record_operation( + record, + graph, + label, + target, + subtract, + tools, + EditScope::Node, + ); +} + +fn build_scene(record: &mut Record, graph: &mut WorldGraph, tracked: &mut Tracked) -> Scene { + let level = tracked.add( + created( + record, + "level", + graph.create_system_assembly(create_options("level-1")), + ), + "level-1", + ); + let profile = tracked.add( + primitive(record, graph, rectangle(6.0, 0.2), CreateOptions::default()), + "profile", + ); + let wall = tracked.add( + operation( + record, + graph, + extrude(&profile, &[], 3.0), + create_options("wall-1"), + ), + "wall-1", + ); + let cutter = tracked.add( + primitive( + record, + graph, + cuboid(0.9, 2.1, 0.4), + create_options("door-cutter"), + ), + "door-cutter", + ); + changes( + record, + "translate cutter", + graph.transform( + &cutter, + Transform::Translate { + offset: [2.0, 0.0, 0.0], + }, + ), + ); + subtract(record, graph, "door cut", &wall, &[&cutter]); + let path_points = [[0.0, 1.0, 1.0], [4.0, 1.0, 1.0], [4.0, 1.0, 4.0]]; + let path = tracked.add( + primitive( + record, + graph, + polyline(&path_points, false), + CreateOptions::default(), + ), + "path", + ); + let disc_options = on_plane( + CreateOptions::default(), + [0.0, 1.0, 1.0], + [1.0, 0.0, 0.0], + [0.0, 0.0, 1.0], + ); + let disc = tracked.add(primitive(record, graph, circle(0.05), disc_options), "disc"); + let rail = tracked.add( + operation(record, graph, sweep(&disc, &path), create_options("rail-1")), + "rail-1", + ); + let members = [&profile, &wall, &cutter, &path, &disc, &rail].map(|id| id.to_string()); + changes( + record, + "add members", + graph.add_child(&level, &members, false), + ); + tracked.remember(graph); + Scene { + level, + profile, + wall, + cutter, + rail, + } +} + +fn edit_scene(record: &mut Record, graph: &mut WorldGraph, scene: &Scene) { + let place = Transform::Place { + origin: Some([0.0, 3.0, 0.0]), + x_direction: None, + normal: None, + scale: None, + }; + changes(record, "place level", graph.transform(&scene.level, place)); + let rotate = Transform::Rotate { + axis: [0.0, 1.0, 0.0], + degrees: 90.0, + pivot: None, + }; + changes(record, "rotate rail", graph.transform(&scene.rail, rotate)); + changes( + record, + "rebuild wall", + graph.rebuild_operation( + &scene.wall, + extrude(&scene.profile, &[], 4.0), + EditScope::Node, + ), + ); + subtract(record, graph, "re-cut", &scene.wall, &[&scene.cutter]); + subtract( + record, + graph, + "rail minus wall", + &scene.rail, + &[&scene.wall], + ); +} + +fn trial_edit(record: &mut Record, graph: &mut WorldGraph, scene: &Scene) { + let mark = graph.mark(); + record.section("step mark"); + record.graph_result("mark", &mark); + record.debug("mark_stats", graph.mark_stats()); + changes( + record, + "trial rebuild", + graph.rebuild_operation( + &scene.wall, + extrude(&scene.profile, &[], 5.0), + EditScope::Node, + ), + ); + record.debug("mark_stats after trial", graph.mark_stats()); + if let Ok(mark) = mark { + changes(record, "rollback trial", graph.rollback(mark)); + record.graph_result("release trial", &graph.release(mark)); + record.graph_result("release again", &graph.release(mark)); + } + record.debug("mark_stats after release", graph.mark_stats()); +} + +fn instances(record: &mut Record, graph: &mut WorldGraph, tracked: &mut Tracked, scene: &Scene) { + let rail_copy = tracked.add( + created( + record, + "instance rail", + graph.instance(&scene.rail, copy_options("rail-2", None)), + ), + "rail-2", + ); + changes( + record, + "translate rail copy", + graph.transform( + &rail_copy, + Transform::Translate { + offset: [0.0, 0.0, 2.0], + }, + ), + ); + for index in 0..10 { + let og_id = format!("wall-copy-{index}"); + let result = graph.instance(&scene.wall, copy_options(&og_id, Some(Some(&scene.level)))); + let og_id = tracked.add( + created(record, &format!("instance wall {index}"), result), + &og_id, + ); + let offset = [index as f64 * 1.5, 0.0, 6.0]; + changes( + record, + &format!("translate wall copy {index}"), + graph.transform(&og_id, Transform::Translate { offset }), + ); + } + unique( + record, + "make unique wall copy 1", + graph.make_unique("wall-copy-1"), + ); + unique(record, "make unique rail 1", graph.make_unique(&scene.rail)); + tracked.add( + created( + record, + "duplicate rail", + graph.duplicate(&scene.rail, copy_options("rail-3", None)), + ), + "rail-3", + ); + tracked.remember(graph); +} + +fn reparent(record: &mut Record, graph: &mut WorldGraph, tracked: &mut Tracked) { + let level = tracked.add( + created( + record, + "second level", + graph.create_system_assembly(create_options("level-2")), + ), + "level-2", + ); + let place = Transform::Place { + origin: Some([10.0, 0.0, 0.0]), + x_direction: Some([0.0, 0.0, -1.0]), + normal: Some([0.0, 1.0, 0.0]), + scale: None, + }; + changes(record, "place level-2", graph.transform(&level, place)); + changes( + record, + "add rail copy keep world", + graph.add_child(&level, &["rail-2".into()], true), + ); + changes( + record, + "add wall copy keep local", + graph.add_child(&level, &["wall-copy-0".into()], false), + ); + changes( + record, + "remove rail copy keep world", + graph.remove_child(&level, "rail-2", true), + ); + changes( + record, + "remove rail copy again", + graph.remove_child(&level, "rail-2", true), + ); + changes( + record, + "cycle rejected", + graph.add_child("wall-copy-0", &[level.clone()], false), + ); + let nested = graph.create_primitive(cuboid(0.5, 0.5, 0.5), child("nested", &level)); + tracked.add(created(record, "nested", nested), "nested"); + tracked.remember(graph); +} + +fn dispose_and_reuse( + record: &mut Record, + graph: &mut WorldGraph, + tracked: &mut Tracked, + scene: &Scene, +) { + changes(record, "dispose cutter", graph.dispose(&scene.cutter)); + tracked.remember(graph); + let mark = graph.mark(); + record.section("step reuse mark"); + record.graph_result("mark", &mark); + changes(record, "dispose wall copy 9", graph.dispose("wall-copy-9")); + created( + record, + "reuse wall copy 9", + graph.create_primitive(cuboid(1.0, 1.0, 1.0), create_options("wall-copy-9")), + ); + tracked.remember(graph); + state(record, graph, tracked, "after reuse"); + if let Ok(mark) = mark { + changes(record, "rollback reuse", graph.rollback(mark)); + record.graph_result("release reuse", &graph.release(mark)); + } + tracked.remember(graph); +} + +fn exports(record: &mut Record, graph: &WorldGraph, scene: &Scene) { + let level = vec![scene.level.clone()]; + let metre_y = StepOptions { + unit: "metre".into(), + up_axis: "Y".into(), + ..StepOptions::default() + }; + export( + record, + graph, + "level default", + &level, + &StepOptions::default(), + ); + export(record, graph, "level metre y", &level, &metre_y); + export( + record, + graph, + "bodies", + &[scene.wall.clone(), "rail-2".into(), "rail-3".into()], + &metre_y, + ); + export( + record, + graph, + "wire rejected", + &[scene.profile.clone()], + &metre_y, + ); + export(record, graph, "nothing selected", &[], &metre_y); + let invalid = StepOptions { + timestamp: "1970-02-30T00:00:00".into(), + ..StepOptions::default() + }; + export(record, graph, "invalid timestamp", &level, &invalid); +} + +fn acceptance(graph: &mut WorldGraph, record: &mut Record) -> Result<(), Failure> { + let mut tracked = Tracked::new(); + let scene = build_scene(record, graph, &mut tracked); + state(record, graph, &tracked, "after door cut and rail"); + edit_scene(record, graph, &scene); + tracked.remember(graph); + state(record, graph, &tracked, "after placement and re-cut"); + trial_edit(record, graph, &scene); + state(record, graph, &tracked, "after trial rollback"); + instances(record, graph, &mut tracked, &scene); + reparent(record, graph, &mut tracked); + state(record, graph, &tracked, "after instances and reparenting"); + dispose_and_reuse(record, graph, &mut tracked, &scene); + state(record, graph, &tracked, "after dispose and rollback"); + exports(record, graph, &scene); + for og_id in [scene.wall.as_str(), scene.rail.as_str(), "rail-2"] { + inspect(record, graph, og_id)?; + } + Ok(()) +} + +pub(crate) fn cases() -> Vec { + vec![graph_case("graph.acceptance-session", acceptance)] +} diff --git a/main/opengeometry/tests/golden/graph_state.rs b/main/opengeometry/tests/golden/graph_state.rs new file mode 100644 index 0000000..c568d70 --- /dev/null +++ b/main/opengeometry/tests/golden/graph_state.rs @@ -0,0 +1,92 @@ +use crate::display::write_buffers; +use crate::graph_record::{counters, node_state}; +use crate::json::pretty; +use crate::kernel::{static_bucket, WorldGraph}; +use crate::record::Record; +use serde_json::to_value; +use std::collections::BTreeSet; + +pub(crate) struct Tracked { + ids: BTreeSet, + revisions: BTreeSet, +} + +impl Tracked { + pub(crate) fn new() -> Self { + Self { + ids: BTreeSet::new(), + revisions: BTreeSet::from([0]), + } + } + + pub(crate) fn add(&mut self, og_id: Option, fallback: &str) -> String { + match og_id { + Some(og_id) => { + self.ids.insert(og_id.clone()); + og_id + } + None => fallback.to_string(), + } + } + + pub(crate) fn remember(&mut self, graph: &WorldGraph) { + self.revisions.insert(graph.revision()); + } +} + +fn history(record: &mut Record, graph: &WorldGraph, tracked: &Tracked) { + let current = graph.revision(); + let mut probes = tracked.revisions.clone(); + probes.insert(current / 2); + probes.insert(current.saturating_sub(1)); + probes.insert(current + 1); + for revision in probes { + match graph.changes_since(revision) { + Ok(changes) => record.block( + &format!("changes_since {revision}"), + &pretty(to_value(&changes)), + ), + Err(error) => record.graph_error(&format!("changes_since {revision}"), &error), + } + } +} + +fn display_state(record: &mut Record, graph: &WorldGraph, og_id: &str) { + let Ok(node) = graph.node(og_id) else { + return; + }; + let Some(shape_id) = node.shape.clone() else { + return; + }; + let title = format!("buffers {og_id}"); + match graph.brep(og_id).map(|brep| static_bucket(&brep)) { + Ok(Ok(bucket)) => match graph.buffers(&shape_id, bucket, 2_000_000) { + Ok(buffers) => write_buffers(record, &title, bucket, &buffers), + Err(error) => record.graph_error(&title, &error), + }, + Ok(Err(error)) => record.error(&title, error), + Err(error) => record.graph_error(&title, &error), + } +} + +pub(crate) fn state(record: &mut Record, graph: &WorldGraph, tracked: &Tracked, label: &str) { + record.section(&format!("state {label}")); + counters(record, graph); + for og_id in &tracked.ids { + node_state(record, graph, og_id); + match graph.children(og_id) { + Ok(children) => record.debug("children", children), + Err(error) => record.graph_error_field("children.error", &error), + } + match graph.parent(og_id) { + Ok(parent) => record.debug("parent", parent), + Err(error) => record.graph_error_field("parent.error", &error), + } + match graph.instance_count(og_id) { + Ok(count) => record.field("instance_count", count), + Err(error) => record.graph_error_field("instance_count.error", &error), + } + display_state(record, graph, og_id); + } + history(record, graph, tracked); +} diff --git a/main/opengeometry/tests/golden/handler_source.rs b/main/opengeometry/tests/golden/handler_source.rs new file mode 100644 index 0000000..e04bf77 --- /dev/null +++ b/main/opengeometry/tests/golden/handler_source.rs @@ -0,0 +1,288 @@ +use crate::record::Failure; +use std::collections::{BTreeMap, BTreeSet}; +use std::fs; +use std::path::{Path, PathBuf}; + +const BOOLEAN_SOURCE: &str = concat!( + env!("CARGO_MANIFEST_DIR"), + "/src/operations/modifying/boolean" +); + +const BOOLEAN_HANDLER_FLOOR: usize = 29; + +const HANDLER_LITERAL: &str = "Handler {"; + +const HANDLER_ID_IMPL: &str = "impl HandlerId {"; + +const NAME_METHOD: &str = "fn as_str("; + +const NAME_MATCH: &str = "match self {"; + +const HANDLER_ID_PATH: &str = "HandlerId::"; + +const TABLE_ENTRY_NAME: &str = ".name"; + +const DECLARATION_KEYWORDS: [&str; 6] = ["struct", "enum", "union", "impl", "trait", "for"]; + +const PATTERN_KEYWORDS: [&str; 2] = ["in", "if"]; + +const RECORDING_CALLS: [(&str, usize); 3] = [ + ("record_handler(", 1), + ("record_optional(", 1), + ("handlers.push(", 0), +]; + +type HandlerNames = BTreeMap; + +struct Source { + path: PathBuf, + text: String, +} + +impl Source { + fn failure(&self, rule: &str, offset: usize, found: &str) -> Failure { + let offset = offset + found.len() - found.trim_start().len(); + let line = self.text[..offset].matches('\n').count() + 1; + format!( + "handler scan: {rule}; {}:{line} has `{}`", + self.path.display(), + found.trim() + ) + .into() + } +} + +fn rust_sources(directory: &Path, sources: &mut Vec) -> Result<(), Failure> { + for entry in fs::read_dir(directory)? { + let path = entry?.path(); + if path.is_dir() { + rust_sources(&path, sources)?; + } else if path.extension().is_some_and(|extension| extension == "rs") { + sources.push(path); + } + } + Ok(()) +} + +fn string_literal(value: &str) -> Option<&str> { + let value = value.trim(); + let value = value + .strip_prefix('{') + .and_then(|block| block.strip_suffix('}')) + .map_or(value, str::trim); + let quoted = value.strip_prefix('"')?.strip_suffix('"')?; + (!quoted.contains('"')).then_some(quoted) +} + +fn is_identifier(character: char) -> bool { + character.is_ascii_alphanumeric() || character == '_' +} + +fn preceding_word(before: &str) -> &str { + before + .trim_end() + .rsplit(|character: char| !is_identifier(character)) + .next() + .unwrap_or_default() +} + +fn declares_type(before: &str) -> bool { + let before = before.trim_end_matches(is_identifier).trim_end(); + DECLARATION_KEYWORDS.contains(&preceding_word(before)) || before.ends_with("->") +} + +fn binds_pattern(after: &str) -> bool { + let rest = after.trim_start_matches(|character: char| { + character.is_whitespace() || character == ')' || character == ']' + }); + let word = rest + .split(|character: char| !is_identifier(character)) + .next(); + word.is_some_and(|word| PATTERN_KEYWORDS.contains(&word)) + || rest.starts_with(['|', ':']) + || (rest.starts_with('=') && !rest.starts_with("==")) +} + +fn top_level_fields(body: &str) -> (Vec<(usize, &str)>, usize) { + let mut fields = Vec::new(); + let mut depth = 0usize; + let mut start = 0; + for (index, byte) in body.bytes().enumerate() { + match byte { + b'{' | b'(' | b'[' => depth += 1, + b'}' | b')' | b']' if depth == 0 => { + fields.push((start, &body[start..index])); + return (fields, index); + } + b'}' | b')' | b']' => depth -= 1, + b',' if depth == 0 => { + fields.push((start, &body[start..index])); + start = index + 1; + } + _ => {} + } + } + (fields, body.len()) +} + +fn name_value(field: &str) -> Option<&str> { + let rest = field.trim_start().strip_prefix("name")?.trim_start(); + if rest.is_empty() { + return Some(rest); + } + rest.strip_prefix(':') + .filter(|value| !value.starts_with(':')) +} + +fn arm_variants(pattern: &str) -> impl Iterator { + pattern + .split('|') + .filter_map(|alternative| alternative.trim().rsplit("::").next()) +} + +fn arm_value_len(value: &str) -> usize { + let block = value.trim_start(); + if let Some(inner) = block.strip_prefix('{') { + return value.len() - block.len() + top_level_fields(inner).1 + 2; + } + let (fields, end) = top_level_fields(value); + fields.first().map_or(end, |(_, field)| field.len()) +} + +fn match_arms(body: &str) -> Vec<(usize, &str, &str)> { + let mut arms = Vec::new(); + let mut start = 0; + while let Some(arrow) = body[start..].find("=>") { + let pattern = &body[start..start + arrow]; + let value = start + arrow + 2; + let end = (value + arm_value_len(&body[value..])).min(body.len()); + arms.push((value, pattern, &body[value..end])); + start = end; + } + arms +} + +fn name_arms(source: &Source) -> Result { + let text = &source.text; + let Some(arms) = text + .find(HANDLER_ID_IMPL) + .and_then(|start| Some(start + text[start..].find(NAME_METHOD)?)) + .and_then(|start| Some(start + text[start..].find(NAME_MATCH)? + NAME_MATCH.len())) + else { + return Ok(HandlerNames::new()); + }; + let body = &text[arms..arms + top_level_fields(&text[arms..]).1]; + let mut names = HandlerNames::new(); + for (offset, pattern, value) in match_arms(body) { + let Some(name) = string_literal(value) else { + let rule = "every HandlerId::as_str arm must return a string literal"; + return Err(source.failure(rule, arms + offset, value)); + }; + for variant in arm_variants(pattern) { + names.insert(variant.to_string(), name.to_string()); + } + } + Ok(names) +} + +fn names_handler(value: &str, names: &HandlerNames) -> bool { + value + .trim_start() + .strip_prefix(HANDLER_ID_PATH) + .is_some_and(|path| { + let end = path.find(|character| !is_identifier(character)); + names.contains_key(&path[..end.unwrap_or(path.len())]) + }) +} + +fn table_entry_name(value: &str) -> bool { + value + .trim() + .strip_suffix(TABLE_ENTRY_NAME) + .is_some_and(|entry| !entry.is_empty() && entry.chars().all(is_identifier)) +} + +fn table_references(source: &Source, names: &HandlerNames) -> Result { + let mut references = 0; + for (start, _) in source.text.match_indices(HANDLER_LITERAL) { + if declares_type(&source.text[..start]) { + continue; + } + let body = start + HANDLER_LITERAL.len(); + let (fields, end) = top_level_fields(&source.text[body..]); + if binds_pattern(&source.text[(body + end + 1).min(source.text.len())..]) { + continue; + } + for (offset, field) in fields { + let Some(value) = name_value(field) else { + continue; + }; + if !names_handler(value, names) { + let rule = "the name field of every *Handler literal must be a HandlerId variant"; + return Err(source.failure(rule, body + offset, field)); + } + references += 1; + } + } + Ok(references) +} + +fn recording_references(source: &Source, names: &HandlerNames) -> Result { + let mut references = 0; + for (call, argument) in RECORDING_CALLS { + for (start, _) in source.text.match_indices(call) { + if preceding_word(&source.text[..start]) == "fn" { + continue; + } + let arguments = start + call.len(); + let (fields, _) = top_level_fields(&source.text[arguments..]); + let (offset, value) = fields.get(argument).copied().unwrap_or((0, "")); + if !names_handler(value, names) && !table_entry_name(value) { + let rule = "every record_handler, record_optional and handlers.push handler argument must be a HandlerId variant or a handler table entry's name"; + return Err(source.failure(rule, arguments + offset, value)); + } + references += 1; + } + } + Ok(references) +} + +fn boolean_sources() -> Result, Failure> { + let mut paths = Vec::new(); + rust_sources(Path::new(BOOLEAN_SOURCE), &mut paths)?; + paths.sort(); + paths + .into_iter() + .map(|path| { + Ok(Source { + text: fs::read_to_string(&path)?, + path, + }) + }) + .collect() +} + +pub(crate) fn boolean_handler_names() -> Result, Failure> { + let sources = boolean_sources()?; + let mut names = HandlerNames::new(); + for source in &sources { + names.extend(name_arms(source)?); + } + let mut references = 0; + for source in &sources { + references += table_references(source, &names)?; + references += recording_references(source, &names)?; + } + if names.is_empty() || references == 0 { + return Err(format!("no boolean handler names found under {BOOLEAN_SOURCE}").into()); + } + let names = names.into_values().collect::>(); + if names.len() < BOOLEAN_HANDLER_FLOOR { + return Err(format!( + "handler scan: at least {BOOLEAN_HANDLER_FLOOR} boolean handler names must be scanned; found {} under {BOOLEAN_SOURCE}", + names.len() + ) + .into()); + } + Ok(names) +} diff --git a/main/opengeometry/tests/golden/json.rs b/main/opengeometry/tests/golden/json.rs new file mode 100644 index 0000000..402a798 --- /dev/null +++ b/main/opengeometry/tests/golden/json.rs @@ -0,0 +1,34 @@ +use crate::digest::sha256; +use crate::kernel::BrepEnvelope; +use crate::record::Record; +use serde_json::Value; + +pub(crate) fn pretty(value: serde_json::Result) -> String { + match value.and_then(|value| serde_json::to_string_pretty(&value)) { + Ok(text) => text, + Err(error) => format!("serialization error: {error:?}"), + } +} + +pub(crate) fn pretty_text(json: &str) -> String { + pretty(serde_json::from_str(json)) +} + +pub(crate) fn brep_sha256(brep: &BrepEnvelope) -> String { + match brep.to_json() { + Ok(json) => sha256(json.as_bytes()), + Err(error) => format!("error: {error:?}"), + } +} + +pub(crate) fn record_brep(record: &mut Record, label: &str, brep: &BrepEnvelope) { + match brep.to_json() { + Ok(json) => { + record.section(&format!("{label}.brep")); + record.field("sha256", sha256(json.as_bytes())); + record.field("bytes", json.len()); + record.block(&format!("{label}.brep.json"), &pretty_text(&json)); + } + Err(error) => record.error(&format!("{label}.brep"), error), + } +} diff --git a/main/opengeometry/tests/golden/kernel.rs b/main/opengeometry/tests/golden/kernel.rs new file mode 100644 index 0000000..6bfae9d --- /dev/null +++ b/main/opengeometry/tests/golden/kernel.rs @@ -0,0 +1,20 @@ +pub(crate) use opengeometry::brep::{ + placed, Accuracy, BodyType, BrepEnvelope, CurveGeometry, Frame3, GeometryError, SurfaceGeometry, +}; +pub(crate) use opengeometry::exchange::export_step; +pub(crate) use opengeometry::math::Point3; +pub(crate) use opengeometry::operations::modifying::boolean::{ + boolean_boxes, boolean_brep_outcome_with_handlers, boolean_spheres, multi_tool_boolean, + shell_brep, subtract_planar_cutters_with_handlers, BooleanOp, BooleanResult, +}; +pub(crate) use opengeometry::primitives; +pub(crate) use opengeometry::primitives::ProfileEdge; +pub(crate) use opengeometry::query::{classify_point, PointClassification}; +pub(crate) use opengeometry::tessellation::display::{ + bucket_floor, display_buffers, static_bucket, DisplayBuffers, +}; +pub(crate) use opengeometry::tessellation::{tessellate, Tessellation}; +pub(crate) use opengeometry::world_graph::{ + ChangeSet, CopyOptions, CreateOptions, CreatingOperation, EditScope, GraphError, + ModifyingOperation, Plane, Primitive, StepOptions, Transform, WorldGraph, +}; diff --git a/main/opengeometry/tests/golden/main.rs b/main/opengeometry/tests/golden/main.rs new file mode 100644 index 0000000..10dfb44 --- /dev/null +++ b/main/opengeometry/tests/golden/main.rs @@ -0,0 +1,191 @@ +#![cfg(not(target_arch = "wasm32"))] + +mod batch; +mod batch_case; +mod batch_route; +mod batch_staged; +mod body; +mod boolean_case; +mod boolean_fixtures; +mod booleans_curved; +mod booleans_cylinders; +mod booleans_direct; +mod booleans_oblique; +mod booleans_planar; +mod booleans_spheres; +mod builder_case; +mod builders_profiles; +mod builders_solids; +mod classify; +mod contract; +mod coverage; +mod creating; +mod digest; +mod display; +mod extras; +mod fine_solids; +mod fixtures; +mod golden_file; +mod graph_case; +mod graph_operate; +mod graph_record; +mod graph_scenarios; +mod graph_session; +mod graph_state; +mod handler_source; +mod json; +mod kernel; +mod matrix; +mod mesh; +mod planar_shapes; +mod profiles; +mod record; +mod runner; +mod shell; +mod step; +mod volume; + +use golden_file::Mode; +use opengeometry_test_support::stored; +use record::{Failure, Findings}; +use runner::Case; +use std::collections::BTreeSet; +use std::env; +use std::path::PathBuf; + +fn corpus() -> Result, Failure> { + Ok([ + builders_solids::cases(), + builders_profiles::cases(), + matrix::cases()?, + boolean_fixtures::cases(), + booleans_cylinders::cases(), + booleans_spheres::cases(), + booleans_direct::cases(), + booleans_planar::cases(), + booleans_oblique::cases(), + booleans_curved::cases(), + batch::cases(), + batch_staged::cases(), + shell::cases(), + creating::cases(), + graph_session::cases(), + graph_operate::cases(), + graph_scenarios::cases(), + ] + .into_iter() + .flatten() + .collect()) +} + +fn check_findings( + expected: &BTreeSet, + findings: Findings, + uncovered: &[String], + compared: bool, +) -> Result<(), Failure> { + let seen = findings.covered.union(&findings.staged_handlers); + let unscanned = coverage::unscanned_names(expected, seen); + if !unscanned.is_empty() { + return Err(format!( + "every handler seen at runtime must be in the scanned set, only batch-route:other-* is exempt; not scanned: {}", + unscanned.join(", ") + ) + .into()); + } + if !findings.broken_cases.is_empty() { + let broken = findings.broken_cases.into_iter().collect::>(); + return Err(format!( + "every matrix case must finish without panicking or returning an error and record the result and handlers verdicts; broken: {}", + broken.join(", ") + ) + .into()); + } + if !uncovered.is_empty() { + return Err(format!("uncovered handlers or routes: {}", uncovered.join(", ")).into()); + } + let disagreements = findings + .disagreements + .into_iter() + .filter(|disagreement| compared || !disagreement.ends_with(" result")) + .collect::>(); + if !disagreements.is_empty() { + return Err(format!("fixture disagreements: {}", disagreements.join(", ")).into()); + } + Ok(()) +} + +fn environment_mode() -> Mode { + let variable = |name| env::var(name).ok(); + golden_file::mode( + variable("OG_GOLDEN_UPDATE").as_deref(), + variable("CI").as_deref(), + env::var_os("OG_GOLDEN_DUMP").map(PathBuf::from), + ) +} + +#[test] +fn every_golden_scene_matches_the_recorded_golden_for_this_target() { + let mode = environment_mode(); + let expected = coverage::expected_names().unwrap(); + let cases = corpus().unwrap(); + let run = runner::run(&cases).unwrap(); + let listings = coverage::listings(&expected, &run.findings.covered); + println!( + "{} cases, {} expected names, {} covered names, {} uncovered entries, {} fixture disagreements", + cases.len(), + expected.len(), + run.findings.covered.len(), + listings.uncovered.len(), + run.findings.disagreements.len() + ); + let mut texts = run.records; + texts.extend(listings.files.map(|(name, text)| (name.to_string(), text))); + if let Mode::Dump(directory) = &mode { + golden_file::dump(directory, &texts).unwrap(); + } + check_findings( + &expected, + run.findings, + &listings.uncovered, + stored::compared(), + ) + .unwrap(); + if matches!(mode, Mode::Dump(_)) { + return; + } + texts.extend(extras::records().unwrap()); + let mut actual = golden_file::digests(&texts); + actual.insert( + extras::SWEEP_KEY.to_string(), + extras::sweep_digest(&extras::sweep_outputs().unwrap()), + ); + let target = golden_file::current_target(); + if mode == Mode::Record { + let file = golden_file::record(&target, &actual).unwrap(); + println!("recorded {} keys into {}", actual.len(), file.display()); + return; + } + if mode == Mode::Build { + println!( + "{} scenes built; the golden comparison does not run on CI", + actual.len() + ); + return; + } + let Some(golden) = golden_file::committed(&target).unwrap() else { + println!( + "{} scenes built; no golden is recorded for {target}, so no hash is compared", + actual.len() + ); + return; + }; + if let Err(mismatch) = golden_file::compare(&golden, &actual) { + let directory = golden_file::write_differing(&target, &mismatch.differing, &texts).unwrap(); + panic!( + "{target}: {mismatch}\nthe texts of the differing scenes are in {}", + directory.display() + ); + } + println!("{} scenes match the golden for {target}", actual.len()); +} diff --git a/main/opengeometry/tests/golden/matrix.rs b/main/opengeometry/tests/golden/matrix.rs new file mode 100644 index 0000000..907daaf --- /dev/null +++ b/main/opengeometry/tests/golden/matrix.rs @@ -0,0 +1,111 @@ +use crate::boolean_case::{record_boolean_handlers, result_body}; +use crate::kernel::{BooleanOp, BooleanResult, BrepEnvelope, GeometryError}; +use crate::record::{Failure, Record}; +use crate::runner::Case; +use serde_json::{json, to_value, Value}; +use std::fs; +use std::path::{Path, PathBuf}; + +const FIXTURE_DIRECTORY: &str = concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/fixtures/cases/boolean-matrix" +); + +const MATRIX_FIXTURES: usize = 18; + +const VERDICTS: [&str; 2] = ["result", "handlers"]; + +fn fixture_paths() -> Result, Failure> { + let mut paths = Vec::new(); + for entry in fs::read_dir(FIXTURE_DIRECTORY)? { + let path = entry?.path(); + let json = path + .extension() + .is_some_and(|extension| extension == "json"); + if json { + paths.push(path); + } + } + paths.sort(); + Ok(paths) +} + +fn record_verdict(record: &mut Record, subject: &str, equal: bool) { + record.field(subject, if equal { "equal" } else { "different" }); + record.note_verdict(subject); + if !equal { + record.disagree(subject); + } +} + +fn record_fixture_agreement( + record: &mut Record, + fixture: &Value, + result: &Result, + handlers: &[String], +) -> Result<(), Failure> { + let actual = match result { + Ok(result) => json!({"brep": to_value(&result.brep)?}), + Err(error) => json!({"error": to_value(error)?}), + }; + record.section("fixture"); + record_verdict(record, "result", actual == fixture["result"]); + record_verdict(record, "handlers", json!(handlers) == fixture["handlers"]); + Ok(()) +} + +struct MatrixFixture { + expected: Value, + a: BrepEnvelope, + b: BrepEnvelope, + operation: BooleanOp, +} + +fn load_fixture(path: &Path) -> Result { + let expected: Value = serde_json::from_slice(&fs::read(path)?)?; + Ok(MatrixFixture { + a: serde_json::from_value(expected["a"].clone())?, + b: serde_json::from_value(expected["b"].clone())?, + operation: serde_json::from_value(expected["operation"].clone())?, + expected, + }) +} + +fn matrix_case(name: &str, path: PathBuf) -> Case { + let id = format!("matrix-{name}"); + Case::new(format!("booleans.matrix.{name}"), move |record| { + let MatrixFixture { + expected, + a, + b, + operation, + } = load_fixture(&path).inspect_err(|_| record.disagree("fixture-unreadable"))?; + let (result, handlers) = record_boolean_handlers(record, &a, &b, operation, &id); + record_fixture_agreement(record, &expected, &result, &handlers)?; + result_body(record, result); + Ok(()) + }) + .requiring_verdicts(&VERDICTS) +} + +pub(crate) fn cases() -> Result, Failure> { + let paths = fixture_paths()?; + if paths.len() != MATRIX_FIXTURES { + return Err(format!( + "matrix: exactly {MATRIX_FIXTURES} matrix fixtures are required; found {} in {FIXTURE_DIRECTORY}", + paths.len() + ) + .into()); + } + paths + .into_iter() + .map(|path| { + let name = path + .file_stem() + .and_then(|stem| stem.to_str()) + .ok_or("matrix fixture name is not UTF-8")? + .to_string(); + Ok(matrix_case(&name, path)) + }) + .collect() +} diff --git a/main/opengeometry/tests/golden/mesh.rs b/main/opengeometry/tests/golden/mesh.rs new file mode 100644 index 0000000..bd3c935 --- /dev/null +++ b/main/opengeometry/tests/golden/mesh.rs @@ -0,0 +1,53 @@ +use crate::digest::{bits, sha256_words, triples}; +use crate::kernel::{tessellate, BrepEnvelope, Tessellation}; +use crate::record::Record; + +pub(crate) const VOLUME_DEFLECTION: f64 = 0.01; +const DEFLECTIONS: [f64; 2] = [VOLUME_DEFLECTION, 0.05]; +const MAX_TRIANGLES: usize = 2_000_000; + +pub(crate) fn record_tessellations( + record: &mut Record, + label: &str, + brep: &BrepEnvelope, +) -> Option { + let mut volume_mesh = None; + for deflection in DEFLECTIONS { + let title = format!("{label}.tessellate@{deflection}"); + match tessellate(brep, deflection, MAX_TRIANGLES) { + Ok(mesh) => { + write_mesh(record, &title, &mesh); + if deflection.to_bits() == VOLUME_DEFLECTION.to_bits() { + volume_mesh = Some(mesh); + } + } + Err(error) => record.error(&title, error), + } + } + volume_mesh +} + +fn write_mesh(record: &mut Record, title: &str, mesh: &Tessellation) { + record.section(title); + record.field("revision", mesh.revision); + record.field("achieved_deflection", bits(mesh.achieved_deflection)); + record.field("vertices", mesh.positions.len() / 3); + record.field("triangles", mesh.triangle_face_ids.len()); + record.field("outline_segments", mesh.outline_edge_ids.len()); + record.field("positions.sha256", sha256_words(&mesh.positions)); + record.field("normals.sha256", sha256_words(&mesh.normals)); + record.field("indices.sha256", sha256_words(&mesh.indices)); + record.field( + "triangle_face_ids.sha256", + sha256_words(&mesh.triangle_face_ids), + ); + record.field( + "outline_positions.sha256", + sha256_words(&mesh.outline_positions), + ); + record.field( + "outline_edge_ids.sha256", + sha256_words(&mesh.outline_edge_ids), + ); + record.block(&format!("{title}.positions"), &triples(&mesh.positions)); +} diff --git a/main/opengeometry/tests/golden/planar_shapes.rs b/main/opengeometry/tests/golden/planar_shapes.rs new file mode 100644 index 0000000..c33043d --- /dev/null +++ b/main/opengeometry/tests/golden/planar_shapes.rs @@ -0,0 +1,62 @@ +use crate::fixtures::{at, fine, rectangle}; +use crate::kernel::{primitives, BrepEnvelope, Frame3, GeometryError, Point3}; +use std::f64::consts::PI; + +pub(crate) type Shape = Result; + +pub(crate) fn span(name: &str, bottom: f64, height: f64, outer: Vec<[f64; 2]>) -> Shape { + holed(name, bottom, height, outer, Vec::new()) +} + +pub(crate) fn holed( + name: &str, + bottom: f64, + height: f64, + outer: Vec<[f64; 2]>, + holes: Vec>, +) -> Shape { + let frame = at([0.0, 0.0, bottom]); + primitives::linear_extrusion(name.into(), frame, outer, holes, height, fine()) +} + +pub(crate) fn profile_shell() -> Shape { + let outer = rectangle([0.0, 0.0], [4.0, 4.0]); + let hole = vec![[1.0, 1.0], [1.0, 3.0], [3.0, 3.0], [3.0, 1.0]]; + holed("profile-shell", 0.0, 3.0, outer, vec![hole]) +} + +pub(crate) fn extrusion(name: &str, outer: Vec<[f64; 2]>) -> Shape { + span(name, 0.0, 3.0, outer) +} + +pub(crate) fn wall(name: &str, length: f64) -> Shape { + extrusion(name, rectangle([0.0, 0.0], [length, 0.3])) +} + +pub(crate) fn angled(name: &str) -> Shape { + let outer = vec![[0.0, 0.0], [10.0, 0.0], [10.0, 0.3], [0.3, 0.3], [0.0, 0.1]]; + extrusion(name, outer) +} + +pub(crate) fn opening(name: &str, bottom: f64, height: f64, from: f64, to: f64) -> Shape { + span(name, bottom, height, rectangle([from, 0.0], [to, 0.3])) +} + +pub(crate) fn square(half: f64) -> Vec<[f64; 2]> { + rectangle([-half, -half], [half, half]) +} + +pub(crate) fn framed(name: &str, frame: Frame3, outer: Vec<[f64; 2]>, height: f64) -> Shape { + primitives::linear_extrusion(name.into(), frame, outer, Vec::new(), height, fine()) +} + +pub(crate) fn oblique(name: &str, origin: Point3, sign: f64, outer: Vec<[f64; 2]>) -> Shape { + let angle = PI / 12.0; + let frame = Frame3 { + origin, + x: [angle.cos(), 0.0, -sign * angle.sin()], + y: [0.0, 1.0, 0.0], + z: [sign * angle.sin(), 0.0, angle.cos()], + }; + framed(name, frame, outer, 2.1) +} diff --git a/main/opengeometry/tests/golden/profiles.rs b/main/opengeometry/tests/golden/profiles.rs new file mode 100644 index 0000000..c257022 --- /dev/null +++ b/main/opengeometry/tests/golden/profiles.rs @@ -0,0 +1,81 @@ +use crate::fixtures::polar; +use crate::kernel::ProfileEdge; +use std::f64::consts::{FRAC_PI_2, PI}; + +pub(crate) fn line(from: [f64; 2], to: [f64; 2]) -> ProfileEdge { + ProfileEdge::Line { from, to } +} + +pub(crate) fn arc(radius: f64, start_angle: f64, sweep_angle: f64) -> ProfileEdge { + arc_at([0.0, 0.0], radius, start_angle, sweep_angle) +} + +pub(crate) fn arc_at( + center: [f64; 2], + radius: f64, + start_angle: f64, + sweep_angle: f64, +) -> ProfileEdge { + ProfileEdge::Arc { + center, + radius, + start_angle, + sweep_angle, + } +} + +pub(crate) fn polygon(points: &[[f64; 2]]) -> Vec { + (0..points.len()) + .map(|index| line(points[index], points[(index + 1) % points.len()])) + .collect() +} + +pub(crate) fn rounded_outer() -> Vec { + vec![ + line([-2.0, -2.0], [2.0, -2.0]), + arc_at([2.0, 0.0], 2.0, -FRAC_PI_2, PI), + line([2.0, 2.0], [-2.0, 2.0]), + line([-2.0, 2.0], [-2.0, -2.0]), + ] +} + +pub(crate) fn square_hole() -> Vec { + polygon(&[[-0.5, -0.5], [0.5, -0.5], [0.5, 0.5], [-0.5, 0.5]]) +} + +pub(crate) fn quarter_annulus(outer: f64, inner: f64) -> Vec { + vec![ + arc(outer, 0.0, FRAC_PI_2), + line([0.0, outer], [0.0, inner]), + arc(inner, FRAC_PI_2, -FRAC_PI_2), + line([inner, 0.0], [outer, 0.0]), + ] +} + +pub(crate) fn split_quarter_annulus() -> Vec { + let quarter = FRAC_PI_2; + vec![ + arc(2.0, 0.0, quarter / 2.0), + arc(2.0, quarter / 2.0, quarter / 2.0), + line([0.0, 2.0], [0.0, 1.5]), + arc(1.5, quarter, -quarter), + line([1.5, 0.0], [2.0, 0.0]), + ] +} + +pub(crate) fn band(outer: f64, inner: f64, start: f64, sweep: f64) -> Vec { + vec![ + arc(outer, start, sweep), + line(polar(outer, start + sweep), polar(inner, start + sweep)), + arc(inner, start + sweep, -sweep), + line(polar(inner, start), polar(outer, start)), + ] +} + +pub(crate) fn wide_arc() -> Vec { + band(2.1, 1.9, -FRAC_PI_2, 3.0 * FRAC_PI_2) +} + +pub(crate) fn sector(reach: f64, start: f64, end: f64) -> Vec<[f64; 2]> { + vec![[0.0, 0.0], polar(reach, start), polar(reach, end)] +} diff --git a/main/opengeometry/tests/golden/record.rs b/main/opengeometry/tests/golden/record.rs new file mode 100644 index 0000000..5f4c6c9 --- /dev/null +++ b/main/opengeometry/tests/golden/record.rs @@ -0,0 +1,144 @@ +use crate::kernel::GraphError; +use serde_json::{json, Value}; +use std::cell::RefCell; +use std::collections::BTreeSet; +use std::fmt::{Debug, Display}; + +pub(crate) type Failure = Box; + +thread_local! { + static STAGED_HANDLERS: RefCell> = const { RefCell::new(Vec::new()) }; +} + +pub(crate) fn note_staged_handlers(handlers: Vec) { + STAGED_HANDLERS.with_borrow_mut(|staged| staged.extend(handlers)); +} + +#[derive(Default)] +pub(crate) struct Findings { + pub(crate) covered: BTreeSet, + pub(crate) disagreements: BTreeSet, + pub(crate) broken_cases: BTreeSet, + pub(crate) staged_handlers: BTreeSet, +} + +impl Findings { + pub(crate) fn absorb(&mut self, other: Findings) { + self.covered.extend(other.covered); + self.disagreements.extend(other.disagreements); + self.broken_cases.extend(other.broken_cases); + self.staged_handlers.extend(other.staged_handlers); + } +} + +pub(crate) struct Record { + name: String, + text: String, + verdicts: BTreeSet, + findings: Findings, +} + +impl Record { + pub(crate) fn new(name: &str) -> Self { + let mut record = Self { + name: name.to_string(), + text: String::new(), + verdicts: BTreeSet::new(), + findings: Findings::default(), + }; + record.section("case"); + record.line(name); + record + } + + pub(crate) fn section(&mut self, title: &str) { + self.text.push_str("### "); + self.text.push_str(title); + self.text.push('\n'); + } + + pub(crate) fn line(&mut self, text: &str) { + self.text.push_str(text); + self.text.push('\n'); + } + + pub(crate) fn field(&mut self, key: &str, value: impl Display) { + self.line(&format!("{key}: {value}")); + } + + pub(crate) fn debug(&mut self, key: &str, value: impl Debug) { + self.line(&format!("{key}: {value:?}")); + } + + pub(crate) fn block(&mut self, title: &str, body: &str) { + self.section(title); + self.text.push_str(body); + if !body.ends_with('\n') { + self.text.push('\n'); + } + } + + pub(crate) fn error(&mut self, title: &str, error: impl Debug) { + self.section(title); + self.line(&format!("error: {error:?}")); + } + + pub(crate) fn graph_error(&mut self, title: &str, error: &GraphError) { + self.section(title); + self.graph_error_field("error", error); + } + + pub(crate) fn graph_error_field(&mut self, key: &str, error: &GraphError) { + self.line(&format!("{key}: {}", graph_error_json(error))); + } + + pub(crate) fn graph_result(&mut self, key: &str, result: &Result) { + match result { + Ok(value) => self.line(&format!("{key}: Ok({value:?})")), + Err(error) => self.line(&format!("{key}: Err({})", graph_error_json(error))), + } + } + + pub(crate) fn cover(&mut self, name: &str) { + self.findings.covered.insert(name.to_string()); + } + + pub(crate) fn cover_all<'a>(&mut self, names: impl IntoIterator) { + for name in names { + self.cover(name); + } + } + + pub(crate) fn disagree(&mut self, subject: &str) { + let disagreement = format!("{} {subject}", self.name); + self.findings.disagreements.insert(disagreement); + } + + pub(crate) fn note_verdict(&mut self, subject: &str) { + self.verdicts.insert(subject.to_string()); + } + + pub(crate) fn has_verdict(&self, subject: &str) -> bool { + self.verdicts.contains(subject) + } + + pub(crate) fn break_case(&mut self, problem: &str) { + let broken = format!("{} {problem}", self.name); + self.findings.broken_cases.insert(broken); + } + + pub(crate) fn finish(mut self) -> (String, Findings) { + let staged = STAGED_HANDLERS.with_borrow_mut(std::mem::take); + self.findings.staged_handlers.extend(staged); + (self.text, self.findings) + } +} + +fn graph_error_json(error: &GraphError) -> String { + let details = match serde_json::to_value(error.details()) { + Ok(Value::Null) => json!({}), + Ok(details) => details, + Err(failure) => json!(format!("serialization error: {failure:?}")), + }; + json!({"code": error.error_code(), "details": details, "message": error.message()}).to_string() +} diff --git a/main/opengeometry/tests/golden/runner.rs b/main/opengeometry/tests/golden/runner.rs new file mode 100644 index 0000000..69df745 --- /dev/null +++ b/main/opengeometry/tests/golden/runner.rs @@ -0,0 +1,177 @@ +use crate::record::{Failure, Findings, Record}; +use std::collections::{BTreeMap, BTreeSet}; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::thread; + +type CaseBody = Box Result<(), Failure> + Send + Sync>; + +const SLOWEST_CASES: [&str; 12] = [ + "batch.planar.fifty-openings", + "booleans.cylinders.nonparallel-through-cut", + "booleans.matrix.cylinder-cross", + "booleans.generic.periodic-winding", + "batch.staged.overlapping-planar-cutters", + "batch.staged.mixed-curved-openings.reverse", + "batch.staged.mixed-curved-openings.forward", + "booleans.sphere-cylinder.crossing.subtraction", + "booleans.sphere-cylinder.crossing.union", + "booleans.sphere-cylinder.crossing.intersection", + "booleans.sphere-cylinder.crossing.reversed-subtraction", + "booleans.generic.sphere-cone.subtraction", +]; + +#[derive(Default)] +pub(crate) struct Run { + pub(crate) records: BTreeMap, + pub(crate) findings: Findings, +} + +impl Run { + fn absorb(&mut self, other: Run) { + self.records.extend(other.records); + self.findings.absorb(other.findings); + } +} + +pub(crate) struct Case { + name: String, + body: CaseBody, + required_verdicts: &'static [&'static str], +} + +impl Case { + pub(crate) fn new( + name: impl Into, + body: impl Fn(&mut Record) -> Result<(), Failure> + Send + Sync + 'static, + ) -> Self { + Self { + name: name.into(), + body: Box::new(body), + required_verdicts: &[], + } + } + + pub(crate) fn requiring_verdicts(self, verdicts: &'static [&'static str]) -> Self { + Self { + required_verdicts: verdicts, + ..self + } + } + + fn check_verdicts(&self, record: &mut Record, problem: Option<&str>) { + if self.required_verdicts.is_empty() { + return; + } + if let Some(problem) = problem { + record.break_case(problem); + } + for subject in self.required_verdicts { + if !record.has_verdict(subject) { + record.break_case(&format!("lacks the {subject} verdict")); + } + } + } + + fn execute(&self) -> (String, Findings) { + let mut record = Record::new(&self.name); + let outcome = catch_unwind(AssertUnwindSafe(|| (self.body)(&mut record))); + let problem = match outcome { + Ok(Ok(())) => None, + Ok(Err(failure)) => { + record.error("case-failure", failure); + Some("returned an error") + } + Err(payload) => { + record.error("case-panic", panic_message(payload.as_ref())); + Some("panicked") + } + }; + self.check_verdicts(&mut record, problem); + record.finish() + } +} + +fn panic_message(payload: &(dyn std::any::Any + Send)) -> String { + if let Some(text) = payload.downcast_ref::<&str>() { + (*text).to_string() + } else if let Some(text) = payload.downcast_ref::() { + text.clone() + } else { + "non-text panic payload".to_string() + } +} + +fn check_names(cases: &[Case]) -> Result<(), Failure> { + let mut seen = BTreeSet::new(); + for case in cases { + let valid = !case.name.is_empty() + && case.name.bytes().all(|byte| { + byte.is_ascii_lowercase() || byte.is_ascii_digit() || b".-".contains(&byte) + }); + if !valid { + return Err(format!( + "case name {:?} is not lowercase, digits, dots and dashes", + case.name + ) + .into()); + } + if !seen.insert(case.name.as_str()) { + return Err(format!("duplicate case name {}", case.name).into()); + } + } + Ok(()) +} + +fn slowest_first(cases: &[Case]) -> Result, Failure> { + let missing = SLOWEST_CASES + .iter() + .find(|slow| !cases.iter().any(|case| case.name == **slow)); + if let Some(missing) = missing { + return Err( + format!("the slowest-first schedule names {missing}, which is not a case").into(), + ); + } + let mut order = cases.iter().collect::>(); + order.sort_by_key(|case| { + SLOWEST_CASES + .iter() + .position(|slow| *slow == case.name) + .unwrap_or(SLOWEST_CASES.len()) + }); + Ok(order) +} + +fn work(cases: &[&Case], next: &AtomicUsize) -> Run { + let mut run = Run::default(); + loop { + let index = next.fetch_add(1, Ordering::Relaxed); + let Some(case) = cases.get(index) else { + return run; + }; + let (text, case_findings) = case.execute(); + run.records.insert(case.name.clone(), text); + run.findings.absorb(case_findings); + } +} + +pub(crate) fn run(cases: &[Case]) -> Result { + check_names(cases)?; + let order = slowest_first(cases)?; + let next = AtomicUsize::new(0); + let workers = thread::available_parallelism().map_or(1, |count| count.get()); + let results = thread::scope(|scope| { + let handles = (0..workers) + .map(|_| scope.spawn(|| work(&order, &next))) + .collect::>(); + handles + .into_iter() + .map(|handle| handle.join()) + .collect::>() + }); + let mut run = Run::default(); + for result in results { + run.absorb(result.map_err(|payload| panic_message(payload.as_ref()))?); + } + Ok(run) +} diff --git a/main/opengeometry/tests/golden/shell.rs b/main/opengeometry/tests/golden/shell.rs new file mode 100644 index 0000000..1215547 --- /dev/null +++ b/main/opengeometry/tests/golden/shell.rs @@ -0,0 +1,79 @@ +use crate::boolean_case::result_body; +use crate::fixtures::fine; +use crate::json::brep_sha256; +use crate::kernel::{primitives, shell_brep, BrepEnvelope, Frame3, GeometryError}; +use crate::planar_shapes::profile_shell; +use crate::profiles::rounded_outer; +use crate::runner::Case; + +type Build = fn() -> Result; + +fn cuboid() -> Result { + primitives::cuboid("box".into(), Frame3::IDENTITY, [2.0; 3], fine()) +} + +fn cylinder() -> Result { + primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 1.0, 2.0, fine()) +} + +fn sphere() -> Result { + primitives::sphere("sphere".into(), Frame3::IDENTITY, 1.0, fine()) +} + +fn torus() -> Result { + primitives::torus("torus".into(), Frame3::IDENTITY, 3.0, 1.0, fine()) +} + +fn cone() -> Result { + primitives::cone("cone".into(), Frame3::IDENTITY, 2.0, 3.0, fine()) +} + +fn frustum() -> Result { + primitives::frustum("frustum".into(), Frame3::IDENTITY, 2.0, 1.0, 3.0, fine()) +} + +fn arc_edged() -> Result { + primitives::arc_edged_extrusion( + "arc-edged".into(), + Frame3::IDENTITY, + rounded_outer(), + 2.0, + fine(), + ) +} + +const SHELLS: [(&str, Build, f64); 11] = [ + ("box", cuboid, 0.2), + ("cylinder", cylinder, 0.2), + ("sphere", sphere, 0.2), + ("torus", torus, 0.2), + ("cone", cone, 0.2), + ("frustum", frustum, 0.2), + ("profile", profile_shell, 0.2), + ("sphere-collapsed", sphere, 1.0), + ("box-below-resolution", cuboid, 1e-9), + ("profile-collapsed", profile_shell, 0.6), + ("arc-edged-unsupported", arc_edged, 0.2), +]; + +fn shell(name: &str, build: Build, thickness: f64) -> Case { + Case::new(format!("shell.{name}"), move |record| { + let input = build()?; + record.section("shell"); + record.field("input", &input.id); + record.field("input.sha256", brep_sha256(&input)); + record.debug("thickness", thickness); + result_body( + record, + shell_brep(&input, thickness, format!("{}-shell", input.id)), + ); + Ok(()) + }) +} + +pub(crate) fn cases() -> Vec { + SHELLS + .iter() + .map(|(name, build, thickness)| shell(name, *build, *thickness)) + .collect() +} diff --git a/main/opengeometry/tests/golden/step.rs b/main/opengeometry/tests/golden/step.rs new file mode 100644 index 0000000..dfe64ab --- /dev/null +++ b/main/opengeometry/tests/golden/step.rs @@ -0,0 +1,21 @@ +use crate::digest::sha256; +use crate::json::pretty; +use crate::kernel::{export_step, BrepEnvelope}; +use crate::record::Record; +use serde_json::to_value; + +pub(crate) fn record_step_exports(record: &mut Record, label: &str, brep: &BrepEnvelope) { + for unit in ["metre", "millimetre"] { + let title = format!("{label}.step.{unit}"); + match export_step(brep, unit) { + Ok((text, report)) => { + record.section(&format!("{title}.report")); + record.field("text.sha256", sha256(text.as_bytes())); + record.field("text.bytes", text.len()); + record.line(&pretty(to_value(&report))); + record.block(&format!("{title}.text"), &text); + } + Err(error) => record.error(&title, error), + } + } +} diff --git a/main/opengeometry/tests/golden/volume.rs b/main/opengeometry/tests/golden/volume.rs new file mode 100644 index 0000000..ff7268d --- /dev/null +++ b/main/opengeometry/tests/golden/volume.rs @@ -0,0 +1,72 @@ +use crate::digest::bits; +use crate::kernel::{BrepEnvelope, SurfaceGeometry, Tessellation}; +use crate::mesh::VOLUME_DEFLECTION; +use crate::record::Record; + +fn vertex(mesh: &Tessellation, index: u32) -> [f64; 3] { + let at = index as usize * 3; + [ + mesh.positions[at], + mesh.positions[at + 1], + mesh.positions[at + 2], + ] +} + +fn sums(mesh: &Tessellation) -> (f64, f64) { + let mut six_volume = 0.0; + let mut area = 0.0; + for indices in mesh.indices.chunks_exact(3) { + let [a, b, c] = [ + vertex(mesh, indices[0]), + vertex(mesh, indices[1]), + vertex(mesh, indices[2]), + ]; + let cross = [ + b[1] * c[2] - b[2] * c[1], + b[2] * c[0] - b[0] * c[2], + b[0] * c[1] - b[1] * c[0], + ]; + six_volume += a[0] * cross[0] + a[1] * cross[1] + a[2] * cross[2]; + let ab = [b[0] - a[0], b[1] - a[1], b[2] - a[2]]; + let ac = [c[0] - a[0], c[1] - a[1], c[2] - a[2]]; + let normal = [ + ab[1] * ac[2] - ab[2] * ac[1], + ab[2] * ac[0] - ab[0] * ac[2], + ab[0] * ac[1] - ab[1] * ac[0], + ]; + area += normal[0].hypot(normal[1]).hypot(normal[2]) * 0.5; + } + (six_volume, area) +} + +pub(crate) fn record_volume( + record: &mut Record, + label: &str, + brep: &BrepEnvelope, + mesh: &Tessellation, +) { + let valid_indices = mesh.indices.iter().all(|index| { + let at = *index as usize * 3; + at + 2 < mesh.positions.len() + }); + record.section(&format!("{label}.volume")); + record.field("deflection", bits(VOLUME_DEFLECTION)); + if !valid_indices { + record.line("error: tessellation index out of range"); + return; + } + let (six_volume, area) = sums(mesh); + let curved = brep + .geometry + .surfaces + .iter() + .any(|surface| !matches!(surface, SurfaceGeometry::Plane { .. })); + let error_bound = if curved { + area * VOLUME_DEFLECTION * 4.0 + } else { + 1e-10 * area.max(1.0) + }; + record.field("value", bits(six_volume / 6.0)); + record.field("area", bits(area)); + record.field("error_bound", bits(error_bound)); +} diff --git a/main/opengeometry/tests/invariants.rs b/main/opengeometry/tests/invariants.rs new file mode 100644 index 0000000..70d8236 --- /dev/null +++ b/main/opengeometry/tests/invariants.rs @@ -0,0 +1,140 @@ +use opengeometry::brep::{Accuracy, BrepEnvelope, Frame3, GeometryError, PatchBounds}; +use opengeometry::primitives; +use opengeometry::tessellation::display::static_bucket; +use opengeometry::world_graph::{ + CopyOptions, CreateOptions, CreatingOperation, ErrorCode, Primitive, StepOptions, Transform, + WorldGraph, +}; + +const CUBOID: &str = include_str!("fixtures/cases/cuboid.brep.json"); + +#[test] +fn i5_graph_accepts_only_the_standard_accuracy_and_the_display_bucket_ignores_the_budget() { + let code = |accuracy| { + WorldGraph::new(accuracy) + .err() + .map(|error| error.error_code()) + }; + assert_eq!( + code(Accuracy { + geometric: 0.0, + ..Accuracy::STANDARD + }), + Some(ErrorCode::InvalidGeometry) + ); + assert_eq!( + code(Accuracy { + intersection: 1e-7, + ..Accuracy::STANDARD + }), + Some(ErrorCode::InvalidGeometry) + ); + let coarse = Accuracy { + tessellation: 0.1, + ..Accuracy::STANDARD + }; + assert_eq!(code(coarse), Some(ErrorCode::InvalidParameter)); + assert_eq!(code(Accuracy::STANDARD), None); + let cylinder = |accuracy| { + primitives::cylinder("cylinder".into(), Frame3::IDENTITY, 1.0, 2.0, accuracy).unwrap() + }; + assert_eq!( + static_bucket(&cylinder(Accuracy::STANDARD)).unwrap(), + static_bucket(&cylinder(coarse)).unwrap() + ); +} + +#[test] +fn i8_library_enables_clippy_panics_and_unwrap_denials() { + let root = include_str!("../src/lib.rs"); + for rule in [ + "clippy::unwrap_used", + "clippy::expect_used", + "clippy::panic", + "clippy::unreachable", + ] { + assert!(root.contains(rule), "{rule}"); + } +} + +#[test] +fn i9_public_input_types_reject_unknown_fields() { + assert!(serde_json::from_str::( + r#"{"geometric":1e-8,"intersection":1e-9,"tessellation":0.01,"exchange":1e-6,"extra":1}"# + ) + .is_err()); + assert!(serde_json::from_str::( + r#"{"kind":"Cuboid","width":1,"height":1,"depth":1,"extra":1}"# + ) + .is_err()); + assert!(serde_json::from_str::(r#"{"ogId":"body","extra":1}"#).is_err()); + assert!(BrepEnvelope::from_json(CUBOID).is_ok()); + for key in [r#""range":{"#, r#""uv_bounds":[{"#] { + assert!(CUBOID.contains(key), "{key}"); + let injected = CUBOID.replacen(key, &format!(r#"{key}"extra":1,"#), 1); + assert!(BrepEnvelope::from_json(&injected).is_err(), "{key}"); + } + assert!(serde_json::from_str::( + r#"{"axes":[{"lo":0,"hi":1},{"lo":0,"hi":1},{"lo":0,"hi":1}],"extra":1}"# + ) + .is_err()); + assert!(serde_json::from_str::( + r#"{"CoverageGap":{"families":["a","b"],"extra":1}}"# + ) + .is_err()); + assert!(serde_json::from_str::( + r#"{"kind":"Translate","offset":[0,0,0],"extra":1}"# + ) + .is_err()); + assert!(serde_json::from_str::(r#"{"ogId":"copy","extra":1}"#).is_err()); + assert!(serde_json::from_str::( + r#"{"kind":"Sweep","profile":"p","path":"q","extra":1}"# + ) + .is_err()); + assert!(serde_json::from_str::(r#"{"unit":"metre","extra":1}"#).is_err()); +} + +#[test] +fn i11_features_at_or_below_four_geometric_are_invalid_parameters() { + let mut graph = WorldGraph::new(Accuracy::STANDARD).unwrap(); + for primitive in [ + Primitive::Cuboid { + width: 4e-8, + height: 1.0, + depth: 1.0, + }, + Primitive::Cylinder { + radius: 4e-8, + height: 1.0, + }, + Primitive::Rectangle { + width: 1.0, + breadth: 4e-8, + }, + Primitive::Circle { radius: 4e-8 }, + ] { + let error = graph + .create_primitive(primitive, CreateOptions::default()) + .unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::InvalidParameter); + } +} + +#[cfg(not(target_arch = "wasm32"))] +#[test] +fn i12_crate_has_no_cargo_features_or_feature_gated_source() { + let manifest = include_str!("../Cargo.toml"); + assert!(!manifest.lines().any(|line| line.trim() == "[features]")); + fn inspect(directory: &std::path::Path) { + for entry in std::fs::read_dir(directory).unwrap() { + let path = entry.unwrap().path(); + if path.is_dir() { + inspect(&path); + } else if path.extension().is_some_and(|ext| ext == "rs") { + let source = std::fs::read_to_string(&path).unwrap(); + assert!(!source.contains("cfg(feature"), "{}", path.display()); + } + } + } + inspect(&std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src")); +} diff --git a/main/opengeometry/tests/occ_oracle.rs b/main/opengeometry/tests/occ_oracle.rs deleted file mode 100644 index 5fe0c28..0000000 --- a/main/opengeometry/tests/occ_oracle.rs +++ /dev/null @@ -1,112 +0,0 @@ -//! Optional OpenCASCADE cross-check (debt items D1/D9, NFR-4). -//! -//! When a real OCC binding (pythonocc `OCC.Core` or `OCP`) is available on the -//! machine, this exports a cylinder to STEP, reads it back through OCC, and -//! asserts OCC sees a genuine `Geom_CylindricalSurface` — the strongest form of -//! NFR-4. When OCC is not installed (the common case, and always in WASM/CI -//! without it), the test prints a skip notice and passes, so it never blocks the -//! build. The always-on guarantee lives in `tests/step_roundtrip_oracle.rs`. - -#![cfg(not(target_arch = "wasm32"))] - -use std::process::Command; - -use opengeometry::export::step::{export_brep_to_step_text, StepExportConfig}; -use opengeometry::primitives::cylinder::OGCylinder; -use openmaths::Vector3; - -const PY_CHECK: &str = r#" -import sys -step_path = sys.argv[1] -try: - try: - from OCC.Core.STEPControl import STEPControl_Reader - from OCC.Core.TopExp import TopExp_Explorer - from OCC.Core.TopAbs import TopAbs_FACE - from OCC.Core.BRep import BRep_Tool - from OCC.Core.TopoDS import topods - from OCC.Core.GeomAbs import GeomAbs_Cylinder - from OCC.Core.BRepAdaptor import BRepAdaptor_Surface - backend = "pythonocc" - except ImportError: - from OCP.STEPControl import STEPControl_Reader - from OCP.TopExp import TopExp_Explorer - from OCP.TopAbs import TopAbs_FACE - from OCP.TopoDS import TopoDS - from OCP.GeomAbs import GeomAbs_Cylinder - from OCP.BRepAdaptor import BRepAdaptor_Surface - backend = "ocp" -except Exception: - print("SKIP: no OCC binding") - sys.exit(0) - -reader = STEPControl_Reader() -status = reader.ReadFile(step_path) -reader.TransferRoots() -shape = reader.OneShape() - -cyl = 0 -exp = TopExp_Explorer(shape, TopAbs_FACE) -while exp.More(): - face = exp.Current() - try: - ad = BRepAdaptor_Surface(face if backend=="pythonocc" else face) - if ad.GetType() == GeomAbs_Cylinder: - cyl += 1 - except Exception: - pass - exp.Next() - -print("CYLINDERS=%d" % cyl) -sys.exit(0 if cyl >= 1 else 2) -"#; - -#[test] -fn occ_reads_exported_cylinder_as_cylindrical_surface() { - // Build + export a cylinder to a temp STEP file. - let mut cyl = OGCylinder::new("occ-cyl".into()); - cyl.set_config( - Vector3::new(0.0, 0.0, 0.0), - 1.0, - 2.0, - 2.0 * std::f64::consts::PI, - 32, - ) - .unwrap(); - let (text, _) = export_brep_to_step_text(&cyl.world_brep(), &StepExportConfig::default()) - .expect("step export"); - - let dir = std::env::temp_dir(); - let step_path = dir.join("opengeometry_occ_oracle.step"); - let py_path = dir.join("opengeometry_occ_oracle.py"); - if std::fs::write(&step_path, &text).is_err() || std::fs::write(&py_path, PY_CHECK).is_err() { - eprintln!("SKIP: could not write temp files for OCC oracle"); - return; - } - - let output = match Command::new("python3") - .arg(&py_path) - .arg(&step_path) - .output() - { - Ok(o) => o, - Err(_) => { - eprintln!("SKIP: python3 not available for OCC oracle"); - return; - } - }; - - let stdout = String::from_utf8_lossy(&output.stdout); - if stdout.contains("SKIP") { - eprintln!("SKIP: OCC binding not installed ({})", stdout.trim()); - return; - } - - assert!( - stdout.contains("CYLINDERS=") && output.status.success(), - "OCC did not read a cylindrical surface from the exported STEP: stdout={:?} stderr={:?}", - stdout, - String::from_utf8_lossy(&output.stderr) - ); - eprintln!("OCC oracle: {}", stdout.trim()); -} diff --git a/main/opengeometry/tests/primitives.rs b/main/opengeometry/tests/primitives.rs new file mode 100644 index 0000000..fe8c570 --- /dev/null +++ b/main/opengeometry/tests/primitives.rs @@ -0,0 +1,146 @@ +use opengeometry::brep::{BodyType, Frame3}; +use opengeometry::primitives; +use opengeometry::query::{classify_point, PointClassification}; +use opengeometry_test_support::accuracy::accuracy; +use opengeometry_test_support::volume::estimate; + +#[test] +fn independent_volumes_match_analytic_formulas() { + let frame = Frame3::IDENTITY; + let tolerance = accuracy(1e-6); + let shapes = [ + ( + primitives::cuboid("box".into(), frame, [2.0, 3.0, 4.0], tolerance).unwrap(), + 24.0, + ), + ( + primitives::cylinder("cylinder".into(), frame, 1.0, 2.0, tolerance).unwrap(), + 2.0 * std::f64::consts::PI, + ), + ( + primitives::sphere("sphere".into(), frame, 1.0, tolerance).unwrap(), + 4.0 * std::f64::consts::PI / 3.0, + ), + ( + primitives::cone("cone".into(), frame, 1.0, 2.0, tolerance).unwrap(), + 2.0 * std::f64::consts::PI / 3.0, + ), + ( + primitives::frustum("frustum".into(), frame, 1.0, 0.4, 2.0, tolerance).unwrap(), + 2.0 * std::f64::consts::PI * (1.0 + 0.4 + 0.16) / 3.0, + ), + ( + primitives::torus("torus".into(), frame, 2.0, 0.5, tolerance).unwrap(), + std::f64::consts::PI.powi(2), + ), + ( + primitives::annular_cylinder("tube".into(), frame, 0.4, 1.0, 2.0, tolerance).unwrap(), + 2.0 * std::f64::consts::PI * (1.0 - 0.16), + ), + ]; + for (body, expected) in shapes { + assert_eq!(body.body_type().unwrap(), BodyType::Solid); + let measured = estimate(&body, 0.01); + assert!( + (measured.value - expected).abs() <= measured.error_bound, + "{}: {} versus {} with bound {}", + body.id, + measured.value, + expected, + measured.error_bound + ); + } +} + +#[test] +fn wire_types_and_point_classification_are_independent_of_fixtures() { + let rectangle = primitives::rectangle( + "rectangle".into(), + Frame3::IDENTITY, + 4.0, + 2.0, + accuracy(1e-6), + ) + .unwrap(); + let (polyline, polyline_keys) = primitives::polyline_with_keys( + "polyline".into(), + &[[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0]], + false, + accuracy(1e-6), + ) + .unwrap(); + assert_eq!(polyline_keys, ["seg-0", "seg-1"]); + assert_eq!(rectangle.body_type().unwrap(), BodyType::Wire); + assert_eq!(polyline.body_type().unwrap(), BodyType::Wire); + assert_eq!(rectangle.topology.edges.len(), 4); + assert_eq!(polyline.topology.edges.len(), 2); + let box_body = + primitives::cuboid("box".into(), Frame3::IDENTITY, [2.0; 3], accuracy(1e-6)).unwrap(); + assert_eq!( + classify_point(&box_body, [1.0; 3]).unwrap(), + PointClassification::Inside + ); + assert_eq!( + classify_point(&box_body, [3.0; 3]).unwrap(), + PointClassification::Outside + ); +} + +#[test] +fn ground_anchors_fit_the_conservative_bounds_and_edge_order() { + let ground = Frame3 { + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, -1.0], + z: [0.0, 1.0, 0.0], + ..Frame3::IDENTITY + }; + let (rectangle, rectangle_keys) = + primitives::rectangle_with_keys("rectangle".into(), ground, 4.0, 2.0, accuracy(1e-6)) + .unwrap(); + assert_eq!(rectangle_keys, ["edge-0", "edge-1", "edge-2", "edge-3"]); + let bounds = rectangle.bounds().unwrap().unwrap(); + assert!(bounds.contains([-2.0, 0.0, -1.0])); + assert!(bounds.contains([2.0, 0.0, 1.0])); + let edges = &rectangle.topology.halfedges; + let vertices = &rectangle.topology.vertices; + let endpoints = |index: usize| { + ( + vertices[edges[index].from as usize].position, + vertices[edges[index].to as usize].position, + ) + }; + let (a, b) = endpoints(0); + assert_eq!((a[0], b[0]), (2.0, 2.0)); + let (a, b) = endpoints(1); + assert_eq!((a[2], b[2]), (-1.0, -1.0)); + let (a, b) = endpoints(2); + assert_eq!((a[0], b[0]), (-2.0, -2.0)); + let (a, b) = endpoints(3); + assert_eq!((a[2], b[2]), (1.0, 1.0)); + + let cuboid_frame = Frame3 { + origin: [-2.0, 0.0, 1.5], + ..ground + }; + let cuboid = primitives::cuboid( + "cuboid".into(), + cuboid_frame, + [4.0, 3.0, 5.0], + accuracy(1e-6), + ) + .unwrap(); + let bounds = cuboid.bounds().unwrap().unwrap(); + assert!(bounds.contains([-2.0, 0.0, -1.5])); + assert!(bounds.contains([2.0, 5.0, 1.5])); + let cylinder = + primitives::cylinder("cylinder".into(), ground, 1.0, 3.0, accuracy(1e-6)).unwrap(); + let bounds = cylinder.bounds().unwrap().unwrap(); + for point in [ + [-1.0, 0.0, 0.0], + [1.0, 3.0, 0.0], + [0.0, 1.5, -1.0], + [0.0, 1.5, 1.0], + ] { + assert!(bounds.contains(point)); + } +} diff --git a/main/opengeometry/tests/primitives_smoke.rs b/main/opengeometry/tests/primitives_smoke.rs deleted file mode 100644 index f41ba89..0000000 --- a/main/opengeometry/tests/primitives_smoke.rs +++ /dev/null @@ -1,62 +0,0 @@ -use opengeometry::primitives::line::OGLine; -use opengeometry::primitives::rectangle::OGRectangle; -use opengeometry::primitives::sphere::OGSphere; -use openmaths::Vector3; - -#[test] -fn sphere_geometry_and_outline_are_non_empty() { - let mut sphere = OGSphere::new("sphere-smoke".to_string()); - sphere - .set_config(Vector3::new(0.0, 0.0, 0.0), 1.0, 16, 10) - .unwrap(); - - assert!(!sphere.brep().vertices.is_empty()); - assert!(!sphere.brep().edges.is_empty()); - assert!(!sphere.brep().faces.is_empty()); - - let geometry: Vec = serde_json::from_str(&sphere.get_geometry_serialized()).unwrap(); - let outline: Vec = - serde_json::from_str(&sphere.get_outline_geometry_serialized()).unwrap(); - - assert!(!geometry.is_empty()); - assert!(!outline.is_empty()); - assert_eq!(geometry.len() % 9, 0); - assert_eq!(outline.len() % 6, 0); -} - -#[test] -fn sphere_segment_inputs_are_clamped() { - let mut sphere = OGSphere::new("sphere-clamp".to_string()); - sphere - .set_config(Vector3::new(0.0, 0.0, 0.0), 1.0, 1, 1) - .unwrap(); - - assert!(!sphere.brep().vertices.is_empty()); - assert!(!sphere.brep().faces.is_empty()); -} - -#[test] -fn line_offset_smoke() { - let mut line = OGLine::new("line-smoke".to_string()); - line.set_config(Vector3::new(-1.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0)) - .unwrap(); - line.generate_geometry().unwrap(); - - let result = line.get_offset_result(0.25, 35.0, true); - assert_eq!(result.points.len(), 2); -} - -#[test] -fn rectangle_generates_face_loop_without_duplicate_halfedges() { - let mut rectangle = OGRectangle::new("rectangle-smoke".to_string()); - rectangle - .set_config(Vector3::new(0.0, 0.0, 0.0), 2.0, 1.0) - .unwrap(); - rectangle.generate_geometry().unwrap(); - - assert_eq!(rectangle.brep().faces.len(), 1); - assert!(rectangle.brep().wires.is_empty()); - - let geometry: Vec = serde_json::from_str(&rectangle.get_geometry_serialized()).unwrap(); - assert_eq!(geometry.len(), 15); -} diff --git a/main/opengeometry/tests/source_rules/determinism_rules.rs b/main/opengeometry/tests/source_rules/determinism_rules.rs new file mode 100644 index 0000000..5b8a255 --- /dev/null +++ b/main/opengeometry/tests/source_rules/determinism_rules.rs @@ -0,0 +1,97 @@ +use crate::exceptions::{enforce, Violation}; +use crate::kernel_sources; +use crate::path_aliases::{path_at, Exports, PathFinder, STD_CRATES}; +use crate::source_tree::{SourceFile, SourceTree}; + +const GLOBAL_STATE_ITEMS: [&str; 6] = [ + "OnceLock", + "OnceCell", + "LazyLock", + "LazyCell", + "lazy_static", + "thread_local", +]; + +fn nondeterministic_path(path: &[String]) -> Option<&'static str> { + if let Some(name) = GLOBAL_STATE_ITEMS + .iter() + .copied() + .find(|name| path.iter().any(|segment| segment == name)) + { + return Some(name); + } + match path { + [root, time, ..] if STD_CRATES.contains(&root.as_str()) && time == "time" => { + Some("std::time") + } + [rand, _, ..] if rand == "rand" => Some("rand"), + _ => None, + } +} + +fn static_mut_lines(file: &SourceFile) -> Vec { + file.code + .windows(2) + .filter(|pair| pair[0].is("static") && pair[1].is("mut")) + .map(|pair| pair[0].line) + .collect() +} + +fn file_violations(file: &SourceFile, exports: &Exports) -> Vec { + let finder = PathFinder::new(file, nondeterministic_path, exports); + let mut violations = Vec::new(); + for line in static_mut_lines(file) { + let detail = "`static mut` makes results depend on hidden state"; + violations.push(Violation::new(&file.path, line, &[&file.path], detail)); + } + for index in 0..file.code.len() { + if file.scan.contexts[index].use_item.is_some() { + continue; + } + if let Some(name) = finder.target_at(index) { + let path = path_at(&file.code, index).unwrap_or_default().join("::"); + let detail = + format!("`{path}` reaches `{name}`, which makes results depend on hidden state"); + let line = file.code[index].line; + violations.push(Violation::new(&file.path, line, &[&file.path], detail)); + } + } + for item in &file.scan.uses { + let scope = [file.module.as_slice(), &item.scope].concat(); + for path in &item.paths { + if let Some(name) = finder.path_target(&path.segments, &scope) { + let detail = format!("`use {}` brings in `{name}`", path.text()); + violations.push(Violation::new(&file.path, item.line, &[&file.path], detail)); + } + } + } + violations +} + +#[test] +fn source_has_no_global_state_clock_or_randomness() { + let tree = kernel_sources(); + let exports = Exports::build(&tree, nondeterministic_path); + let violations: Vec = tree + .files + .iter() + .flat_map(|file| file_violations(file, &exports)) + .collect(); + enforce( + "no thread_local!, static mut, OnceLock, OnceCell, LazyLock, LazyCell, lazy_static, std::time or rand", + &violations, + None, + ); +} + +#[test] +fn determinism_rule_sees_a_rooted_clock_path_after_a_comparison() { + let tree = SourceTree::from_sources(&[( + "math/mod.rs", + "fn late(stamp: u64) -> bool {\n stamp > ::std::time::UNIX_EPOCH\n}\n", + )]); + let exports = Exports::build(&tree, nondeterministic_path); + let violations = file_violations(&tree.files[0], &exports); + assert_eq!(violations.len(), 1); + assert_eq!(violations[0].location, "src/math/mod.rs:2"); +} diff --git a/main/opengeometry/tests/source_rules/exceptions.rs b/main/opengeometry/tests/source_rules/exceptions.rs new file mode 100644 index 0000000..ad1330f --- /dev/null +++ b/main/opengeometry/tests/source_rules/exceptions.rs @@ -0,0 +1,387 @@ +use std::collections::BTreeMap; +use std::fs; +use std::path::Path; + +const SOURCE_ROOT: &str = "src"; +const OWNERS: [&str; 11] = [ + "A8b", "A8c", "A9a", "A9b", "A9c", "A9d", "A9e", "A9f", "A9g", "A9h", "B5", +]; + +type FrozenSet = BTreeMap, FrozenKey>; + +struct FrozenKey { + max: Option, + owner: String, + well_formed: bool, +} + +#[derive(Clone, Debug)] +pub(crate) struct Violation { + pub(crate) location: String, + pub(crate) key: Vec, + pub(crate) measure: Option, + pub(crate) detail: String, +} + +impl Violation { + pub(crate) fn new( + file: &str, + line: usize, + key: &[&str], + detail: impl Into, + ) -> Violation { + Violation { + location: format!("{SOURCE_ROOT}/{file}:{line}"), + key: key.iter().map(|part| (*part).to_string()).collect(), + measure: None, + detail: detail.into(), + } + } + + pub(crate) fn with_measure(mut self, measure: usize) -> Violation { + self.measure = Some(measure); + self + } + + pub(crate) fn under(mut self, root: &str) -> Violation { + self.location = self.location.replacen(SOURCE_ROOT, root, 1); + self + } +} + +pub(crate) struct ListSpec { + pub(crate) file: &'static str, + pub(crate) key_fields: usize, + pub(crate) measured: bool, + pub(crate) frozen: &'static [&'static str], +} + +struct Entry { + key: Vec, + measure: Option, + frozen_max: Option, + line: usize, +} + +pub(crate) struct ExceptionList { + name: String, + entries: Vec, + problems: Vec<(usize, String)>, +} + +fn field_count(spec: &ListSpec) -> usize { + spec.key_fields + usize::from(spec.measured) + 1 +} + +fn frozen_set(spec: &ListSpec) -> FrozenSet { + spec.frozen + .iter() + .map(|text| { + let fields: Vec<&str> = text.split_whitespace().collect(); + let key = fields.iter().take(spec.key_fields); + let max = fields + .get(spec.key_fields) + .filter(|_| spec.measured) + .and_then(|field| field.parse().ok()); + let frozen = FrozenKey { + max, + owner: fields + .last() + .map_or_else(String::new, |owner| (*owner).to_string()), + well_formed: fields.len() == field_count(spec), + }; + (key.map(|field| (*field).to_string()).collect(), frozen) + }) + .collect() +} + +impl ExceptionList { + pub(crate) fn load(path: &Path, spec: &ListSpec) -> ExceptionList { + match fs::read_to_string(path) { + Ok(text) => ExceptionList::parse(&text, spec), + Err(error) => ExceptionList { + name: spec.file.to_string(), + entries: Vec::new(), + problems: vec![(0, format!("cannot read the list: {error}"))], + }, + } + } + + fn parse(text: &str, spec: &ListSpec) -> ExceptionList { + let mut list = ExceptionList { + name: spec.file.to_string(), + entries: Vec::new(), + problems: Vec::new(), + }; + let frozen = frozen_set(spec); + for (index, line) in text.lines().enumerate() { + list.parse_entry(index + 1, line, spec, &frozen); + } + list.check_retired(&frozen); + list + } + + fn check_retired(&mut self, frozen: &FrozenSet) { + for (key, frozen_key) in frozen { + let key_text = key.join(" "); + let problem = match self.entry(key).map(|(_, entry)| entry) { + None => ( + 0, + format!("frozen key `{key_text}` has no entry; retire it in frozen_lists.rs"), + ), + Some(entry) => match frozen_key.max.zip(entry.measure) { + Some((max, listed)) if max > listed => ( + entry.line, + format!( + "frozen maximum of `{key_text}` is {max}, above the listed {listed}; lower it to {listed}" + ), + ), + _ => continue, + }, + }; + self.problems.push(problem); + } + } + + fn frozen_max( + &mut self, + line: usize, + key: &[String], + owner: &str, + spec: &ListSpec, + frozen: &FrozenSet, + ) -> Option { + let key_text = key.join(" "); + let Some(frozen_key) = frozen.get(key) else { + self.problems.push(( + line, + format!("entry `{key_text}` is not in the frozen set; the list may only shrink"), + )); + return None; + }; + if !frozen_key.well_formed || (spec.measured && frozen_key.max.is_none()) { + self.problems.push(( + line, + format!("frozen key `{key_text}` must give its key, maximum and owner"), + )); + } else if frozen_key.owner != owner { + self.problems.push(( + line, + format!( + "entry `{key_text}` belongs to {} in the frozen set, not {owner}", + frozen_key.owner + ), + )); + } + frozen_key.max + } + + fn parse_entry(&mut self, line: usize, text: &str, spec: &ListSpec, frozen: &FrozenSet) { + let fields: Vec<&str> = text.split_whitespace().collect(); + let expected = field_count(spec); + if fields.len() != expected { + self.problems + .push((line, format!("expected {expected} fields, found `{text}`"))); + return; + } + let owner = fields[expected - 1]; + if !OWNERS.contains(&owner) { + let runs = OWNERS.join(", "); + self.problems.push(( + line, + format!("`{owner}` is not a run the plan defines ({runs})"), + )); + } + let key: Vec = fields[..spec.key_fields] + .iter() + .map(|field| (*field).to_string()) + .collect(); + if self.entries.iter().any(|entry| entry.key == key) { + self.problems + .push((line, format!("duplicate entry `{}`", key.join(" ")))); + } + let measure = if spec.measured { + match fields[spec.key_fields].parse::() { + Ok(measure) => Some(measure), + Err(_) => { + self.problems.push(( + line, + format!("`{}` is not a line count", fields[spec.key_fields]), + )); + None + } + } + } else { + None + }; + let frozen_max = self.frozen_max(line, &key, owner, spec, frozen); + if let Some((listed, max)) = measure.zip(frozen_max).filter(|(listed, max)| listed > max) { + self.problems.push(( + line, + format!("lists {listed} lines, above its frozen maximum of {max}"), + )); + } + self.entries.push(Entry { + key, + measure, + frozen_max, + line, + }); + } + + fn entry(&self, key: &[String]) -> Option<(usize, &Entry)> { + self.entries + .iter() + .enumerate() + .find(|(_, entry)| entry.key == key) + } + + fn location(&self, line: usize) -> String { + format!("tests/source_rules/{}:{line}", self.name) + } +} + +fn measure_failure(rule: &str, violation: &Violation, entry: &Entry, list: &str) -> Option { + let (now, listed) = (violation.measure?, entry.measure?); + let change = if let Some(max) = entry.frozen_max.filter(|max| now > *max) { + format!("grew past its frozen maximum of {max} lines") + } else if now > listed { + format!("grew from the {listed} lines listed in {list}") + } else if now < listed { + format!("is shorter than the {listed} lines listed in {list}; set the entry to {now}") + } else { + return None; + }; + Some(format!( + "{}: {rule}: {} {change}", + violation.location, violation.detail + )) +} + +fn list_failures( + rule: &str, + list: &ExceptionList, + matches: &BTreeMap>, +) -> Vec { + let mut failures = Vec::new(); + for (line, problem) in &list.problems { + failures.push(format!("{}: {rule}: {problem}", list.location(*line))); + } + for (index, entry) in list.entries.iter().enumerate() { + let key = entry.key.join(" "); + let location = list.location(entry.line); + match matches.get(&index) { + None => failures.push(format!( + "{location}: {rule}: stale entry `{key}` no longer violates the rule; remove it" + )), + Some(found) if found.len() > 1 => failures.push(format!( + "{location}: {rule}: entry `{key}` matches {} violations ({}); one entry covers exactly one violation", + found.len(), + found.join(", ") + )), + Some(_) => {} + } + } + failures +} + +fn failures(rule: &str, violations: &[Violation], list: Option<&ExceptionList>) -> Vec { + let mut failures = Vec::new(); + let mut matches: BTreeMap> = BTreeMap::new(); + for violation in violations { + let Some(list) = list else { + failures.push(format!( + "{}: {rule}: {}", + violation.location, violation.detail + )); + continue; + }; + let Some((index, entry)) = list.entry(&violation.key) else { + failures.push(format!( + "{}: {rule}: {} (not in {})", + violation.location, violation.detail, list.name + )); + continue; + }; + matches + .entry(index) + .or_default() + .push(violation.location.as_str()); + failures.extend(measure_failure(rule, violation, entry, &list.name)); + } + if let Some(list) = list { + failures.extend(list_failures(rule, list, &matches)); + } + failures +} + +pub(crate) fn enforce(rule: &str, violations: &[Violation], list: Option<&ExceptionList>) { + let failures = failures(rule, violations, list); + assert!( + failures.is_empty(), + "rule `{rule}` failed {} time(s):\n{}", + failures.len(), + failures.join("\n") + ); +} + +const RATCHET: ListSpec = ListSpec { + file: "ratchet.txt", + key_fields: 2, + measured: true, + frozen: &["math/solve.rs solve 90 A9f"], +}; + +fn problems_of(text: &str, spec: &ListSpec) -> Vec { + ExceptionList::parse(text, spec) + .problems + .into_iter() + .map(|(_, problem)| problem) + .collect() +} + +#[test] +fn frozen_list_rejects_a_deleted_entry_that_comes_back() { + assert_eq!( + problems_of("", &RATCHET), + ["frozen key `math/solve.rs solve` has no entry; retire it in frozen_lists.rs"] + ); + let retired = ListSpec { + frozen: &[], + ..RATCHET + }; + assert!(problems_of("", &retired).is_empty()); + assert_eq!( + problems_of("math/solve.rs solve 90 A9f\n", &retired), + ["entry `math/solve.rs solve` is not in the frozen set; the list may only shrink"] + ); +} + +#[test] +fn frozen_maximum_never_rises_above_the_listed_length() { + assert!(problems_of("math/solve.rs solve 90 A9f\n", &RATCHET).is_empty()); + assert_eq!( + problems_of("math/solve.rs solve 95 A9f\n", &RATCHET), + ["lists 95 lines, above its frozen maximum of 90"] + ); + assert_eq!( + problems_of("math/solve.rs solve 85 A9f\n", &RATCHET), + ["frozen maximum of `math/solve.rs solve` is 90, above the listed 85; lower it to 85"] + ); +} + +#[test] +fn frozen_entry_stays_with_its_owner() { + assert_eq!( + problems_of("math/solve.rs solve 90 B5\n", &RATCHET), + ["entry `math/solve.rs solve` belongs to A9f in the frozen set, not B5"] + ); + let ownerless = ListSpec { + frozen: &["math/solve.rs solve 90"], + ..RATCHET + }; + assert_eq!( + problems_of("math/solve.rs solve 90 A9f\n", &ownerless), + ["frozen key `math/solve.rs solve` must give its key, maximum and owner"] + ); +} diff --git a/main/opengeometry/tests/source_rules/external_crate_rules.rs b/main/opengeometry/tests/source_rules/external_crate_rules.rs new file mode 100644 index 0000000..423666d --- /dev/null +++ b/main/opengeometry/tests/source_rules/external_crate_rules.rs @@ -0,0 +1,114 @@ +use crate::exceptions::{enforce, Violation}; +use crate::lexer::Token; +use crate::path_aliases::opens_path; +use crate::source_tree::{SourceFile, SourceTree}; +use crate::{kernel_manifest, kernel_sources}; + +const MATH: &str = "math"; +const MATH_EXTERNAL_CRATES: [&str; 1] = ["serde"]; +const DEPENDENCY_TABLE_SUFFIX: &str = "dependencies]"; + +fn manifest_crates(manifest: &str) -> Vec { + let mut crates = Vec::new(); + let mut in_dependencies = false; + for line in manifest.lines().map(str::trim) { + if line.starts_with('[') { + in_dependencies = line.ends_with(DEPENDENCY_TABLE_SUFFIX); + continue; + } + let Some((name, _)) = line.split_once('=') else { + continue; + }; + let name = name.trim().split('.').next().unwrap_or_default(); + if in_dependencies && !name.is_empty() { + crates.push(name.replace('-', "_")); + } + } + crates +} + +fn names_crate_root(code: &[Token], index: usize) -> bool { + let Some(before) = index.checked_sub(1).map(|at| &code[at]) else { + return true; + }; + if before.is(".") { + return false; + } + !before.is("::") || opens_path(code, index - 1) +} + +fn file_violations(file: &SourceFile, forbidden: &[String]) -> Vec { + let mut violations = Vec::new(); + for (index, token) in file.code.iter().enumerate() { + let external = forbidden.iter().any(|name| token.is(name)); + if !external || file.is_test_at(index) || !names_crate_root(&file.code, index) { + continue; + } + let detail = format!( + "math reaches the external crate `{}`; it may use only std and serde", + token.text + ); + violations.push(Violation::new( + &file.path, + token.line, + &[&file.path], + detail, + )); + } + violations +} + +fn math_external_crate_violations(tree: &SourceTree, manifest: &str) -> Vec { + let forbidden: Vec = manifest_crates(manifest) + .into_iter() + .filter(|name| !MATH_EXTERNAL_CRATES.contains(&name.as_str())) + .collect(); + tree.files + .iter() + .filter(|file| !file.test && file.module.first().is_some_and(|top| top == MATH)) + .flat_map(|file| file_violations(file, &forbidden)) + .collect() +} + +#[test] +fn math_imports_only_std_and_serde_among_external_crates() { + let violations = math_external_crate_violations(&kernel_sources(), &kernel_manifest()); + enforce( + "math imports only std and serde among external crates", + &violations, + None, + ); +} + +#[test] +fn math_external_crate_rule_sees_rooted_paths_after_comparisons_and_generic_lists() { + let tree = SourceTree::from_sources(&[ + ("math/mod.rs", "mod bound;\nmod compare;\nmod generic;\n"), + ( + "math/bound.rs", + "fn apply(visit: F)\nwhere\n F: for<'a> ::earcutr::Visit<'a>,\n{\n}\n", + ), + ( + "math/compare.rs", + "fn near(count: usize) -> bool {\n count > ::earcutr::LIMIT\n}\n", + ), + ( + "math/generic.rs", + "impl ::earcutr::Flatten for Points {}\n", + ), + ]); + let manifest = "[dependencies]\nearcutr = \"=0.3.0\"\n"; + let violations = math_external_crate_violations(&tree, manifest); + let locations: Vec<&str> = violations + .iter() + .map(|violation| violation.location.as_str()) + .collect(); + assert_eq!( + locations, + [ + "src/math/bound.rs:3", + "src/math/compare.rs:2", + "src/math/generic.rs:1" + ] + ); +} diff --git a/main/opengeometry/tests/source_rules/frozen_lists.rs b/main/opengeometry/tests/source_rules/frozen_lists.rs new file mode 100644 index 0000000..5f2ce38 --- /dev/null +++ b/main/opengeometry/tests/source_rules/frozen_lists.rs @@ -0,0 +1,78 @@ +use crate::exceptions::ListSpec; + +pub(crate) const PORTED_LONG_FUNCTIONS: ListSpec = ListSpec { + file: "ported_long_functions.txt", + key_fields: 2, + measured: true, + frozen: &[], +}; + +pub(crate) const UPWARD_IMPORTS: ListSpec = ListSpec { + file: "upward_imports.txt", + key_fields: 2, + measured: false, + frozen: &[], +}; + +pub(crate) const MODULE_CYCLES: ListSpec = ListSpec { + file: "module_cycles.txt", + key_fields: 1, + measured: false, + frozen: &[], +}; + +pub(crate) const MUTUAL_IMPORTS: ListSpec = ListSpec { + file: "mutual_imports.txt", + key_fields: 2, + measured: false, + frozen: &[], +}; + +pub(crate) const SIBLING_CYCLES: ListSpec = ListSpec { + file: "sibling_cycles.txt", + key_fields: 1, + measured: false, + frozen: &[], +}; + +pub(crate) const RESERVED_FN_NAMES: ListSpec = ListSpec { + file: "reserved_fn_names.txt", + key_fields: 2, + measured: false, + frozen: &[], +}; + +pub(crate) const STRING_ERRORS: ListSpec = ListSpec { + file: "string_errors.txt", + key_fields: 2, + measured: false, + frozen: &[], +}; + +pub(crate) const SERDE_JSON_OUTSIDE_EDGES: ListSpec = ListSpec { + file: "serde_json_outside_edges.txt", + key_fields: 2, + measured: false, + frozen: &[ + "world_graph/modifying.rs WorldGraph::operate B5", + "world_graph/modifying.rs WorldGraph::report B5", + "world_graph/shape_store.rs Shape B5", + ], +}; + +pub(crate) const JSON_VALUES_IN_SIGNATURES: ListSpec = ListSpec { + file: "json_values_in_signatures.txt", + key_fields: 2, + measured: false, + frozen: &[ + "world_graph/modifying.rs WorldGraph::report B5", + "world_graph/shape_store.rs Shape B5", + ], +}; + +pub(crate) const INLINE_TESTS: ListSpec = ListSpec { + file: "inline_tests.txt", + key_fields: 2, + measured: false, + frozen: &[], +}; diff --git a/main/opengeometry/tests/source_rules/function_rules.rs b/main/opengeometry/tests/source_rules/function_rules.rs new file mode 100644 index 0000000..dcf2ade --- /dev/null +++ b/main/opengeometry/tests/source_rules/function_rules.rs @@ -0,0 +1,72 @@ +use crate::exceptions::{enforce, Violation}; +use crate::frozen_lists::PORTED_LONG_FUNCTIONS; +use crate::source_tree::SourceTree; +use crate::{kernel_sources, rule_list, rule_violations, violations_outside_kernel_src}; + +const MAX_FN_LINES: usize = 80; + +fn long_function_violations(tree: &SourceTree, tests_count: bool) -> Vec { + let mut violations = Vec::new(); + for file in tree.files.iter().filter(|file| tests_count || !file.test) { + let counted = file + .scan + .fns + .iter() + .filter(|function| tests_count || !function.test); + for function in counted { + let span = function.span(); + if span <= MAX_FN_LINES { + continue; + } + let detail = format!( + "fn `{}` spans {span} lines, above {MAX_FN_LINES}", + function.name + ); + let key = [file.path.as_str(), function.name.as_str()]; + violations + .push(Violation::new(&file.path, function.start, &key, detail).with_measure(span)); + } + } + violations +} + +#[test] +fn non_test_functions_are_at_most_80_lines() { + let violations = long_function_violations(&kernel_sources(), false); + let list = rule_list(&PORTED_LONG_FUNCTIONS); + enforce( + "every non-test fn at most 80 lines", + &violations, + Some(&list), + ); +} + +#[test] +fn non_test_functions_outside_the_kernel_src_are_at_most_80_lines() { + let violations = violations_outside_kernel_src(|tree| long_function_violations(tree, false)); + enforce( + "every non-test fn outside the kernel src at most 80 lines", + &violations, + None, + ); +} + +#[test] +fn source_rules_functions_are_at_most_80_lines() { + let violations = rule_violations(|tree| long_function_violations(tree, true)); + enforce("every source_rules fn at most 80 lines", &violations, None); +} + +#[test] +fn long_function_rule_counts_items_with_a_cfg_test_generic_parameter() { + let body = " step();\n".repeat(MAX_FN_LINES); + let text = format!( + "fn long<#[cfg(test)] T>() {{\n{body}}}\n\nimpl<#[cfg(test)] T> Holder {{\n fn method() {{\n{body} }}\n}}\n" + ); + let tree = SourceTree::from_sources(&[("math/mod.rs", &text)]); + let names: Vec = long_function_violations(&tree, false) + .into_iter() + .map(|violation| violation.key[1].clone()) + .collect(); + assert_eq!(names, ["long", "Holder::method"]); +} diff --git a/main/opengeometry/tests/source_rules/graph.rs b/main/opengeometry/tests/source_rules/graph.rs new file mode 100644 index 0000000..bd0ede0 --- /dev/null +++ b/main/opengeometry/tests/source_rules/graph.rs @@ -0,0 +1,187 @@ +use crate::module_paths::{absolute_path, module_table, ModuleTable}; +use crate::path_aliases::path_at; +use crate::source_tree::{SourceFile, SourceTree}; +use std::collections::{BTreeMap, BTreeSet}; + +pub(crate) struct Edge { + pub(crate) from: usize, + pub(crate) to: usize, + pub(crate) from_module: Vec, + pub(crate) defining_module: Vec, + pub(crate) line: usize, + pub(crate) path: String, + pub(crate) test: bool, +} + +type Bindings = BTreeMap, BTreeMap>>; + +const RE_EXPORT_HOPS: usize = 16; +const PATH_HEADS: [&str; 3] = ["crate", "self", "super"]; + +struct Reference { + segments: Vec, + scope: Vec, + line: usize, + test: bool, +} + +struct Resolver { + modules: ModuleTable, + bindings: Vec, +} + +impl Resolver { + fn new(tree: &SourceTree) -> Resolver { + let modules = module_table(tree); + let bindings = tree + .files + .iter() + .map(|file| file_bindings(file, &modules)) + .collect(); + Resolver { modules, bindings } + } + + fn locate(&self, absolute: &[String]) -> Option<(usize, usize)> { + (0..=absolute.len()).rev().find_map(|length| { + self.modules + .get(&absolute[..length]) + .map(|&file| (file, length)) + }) + } + + fn follow(&self, mut absolute: Vec) -> Option<(usize, Vec)> { + for _ in 0..RE_EXPORT_HOPS { + let (file, length) = self.locate(&absolute)?; + let rebound = (length < absolute.len()) + .then(|| self.bindings[file].get(&absolute[..length])) + .flatten() + .and_then(|names| names.get(&absolute[length])); + match rebound { + Some(target) => absolute = [target.as_slice(), &absolute[length + 1..]].concat(), + None => return Some((file, absolute[..length].to_vec())), + } + } + None + } + + fn edge(&self, from: usize, reference: Reference) -> Option { + let local = self.bindings[from].get(&reference.scope); + let absolute = absolute_path(&reference.segments, &reference.scope, local, &self.modules)?; + let (named, depth) = self.locate(&absolute)?; + let (defining, defining_module) = self.follow(absolute)?; + let to = if depth == 0 { defining } else { named }; + (to != from).then(|| Edge { + from, + to, + from_module: reference.scope, + defining_module, + line: reference.line, + path: reference.segments.join("::"), + test: reference.test, + }) + } +} + +pub(crate) fn import_edges(tree: &SourceTree) -> Vec { + let resolver = Resolver::new(tree); + let mut edges = Vec::new(); + for (index, file) in tree.files.iter().enumerate() { + let references = use_references(file) + .into_iter() + .chain(code_references(file, &resolver.modules)); + edges.extend(references.filter_map(|reference| resolver.edge(index, reference))); + } + edges +} + +fn use_references(file: &SourceFile) -> Vec { + let mut references = Vec::new(); + for item in &file.scan.uses { + for path in &item.paths { + references.push(Reference { + segments: path.segments.clone(), + scope: [file.module.clone(), item.scope.clone()].concat(), + line: item.line, + test: file.test || item.test, + }); + } + } + references +} + +fn code_references(file: &SourceFile, modules: &ModuleTable) -> Vec { + let mut references = Vec::new(); + for index in 0..file.code.len() { + if file.scan.contexts[index].use_item.is_some() { + continue; + } + let Some(segments) = path_at(&file.code, index).filter(|path| path.len() > 1) else { + continue; + }; + let scope = [file.module.as_slice(), file.scan.scope_at(index)].concat(); + let child = [scope.as_slice(), &segments[..1]].concat(); + if !PATH_HEADS.contains(&segments[0].as_str()) && !modules.contains_key(&child) { + continue; + } + references.push(Reference { + segments, + scope, + line: file.code[index].line, + test: file.is_test_at(index), + }); + } + references +} + +fn file_bindings(file: &SourceFile, modules: &ModuleTable) -> Bindings { + let mut bindings = Bindings::new(); + for _ in 0..2 { + for item in &file.scan.uses { + let scope = [file.module.clone(), item.scope.clone()].concat(); + for path in &item.paths { + let Some(binding) = &path.binding else { + continue; + }; + let local = bindings.get(&scope); + if let Some(absolute) = absolute_path(&path.segments, &scope, local, modules) { + bindings + .entry(scope.clone()) + .or_default() + .insert(binding.clone(), absolute); + } + } + } + } + bindings +} + +pub(crate) fn top_module(file: &SourceFile) -> Option<&str> { + file.module.first().map(String::as_str) +} + +pub(crate) fn cycles(adjacency: &BTreeMap>) -> Vec> { + let mut found: Vec> = Vec::new(); + for module in adjacency.keys() { + let cycle: BTreeSet = reachable(adjacency, module) + .into_iter() + .filter(|other| reachable(adjacency, other).contains(module)) + .collect(); + if cycle.len() >= 2 && !found.contains(&cycle) { + found.push(cycle); + } + } + found +} + +fn reachable(adjacency: &BTreeMap>, from: &str) -> BTreeSet { + let mut seen = BTreeSet::new(); + let mut stack = vec![from.to_string()]; + while let Some(node) = stack.pop() { + for next in adjacency.get(&node).into_iter().flatten() { + if seen.insert(next.clone()) { + stack.push(next.clone()); + } + } + } + seen +} diff --git a/main/opengeometry/tests/source_rules/import_rules.rs b/main/opengeometry/tests/source_rules/import_rules.rs new file mode 100644 index 0000000..5c10bb7 --- /dev/null +++ b/main/opengeometry/tests/source_rules/import_rules.rs @@ -0,0 +1,69 @@ +use crate::exceptions::{enforce, Violation}; +use crate::kernel_sources; +use crate::lexer::Token; +use crate::source_tree::SourceTree; + +fn function_local_use_violations(tree: &SourceTree) -> Vec { + let mut violations = Vec::new(); + for file in &tree.files { + for item in file.scan.uses.iter().filter(|item| item.local) { + let paths: Vec = item.paths.iter().map(|path| path.text()).collect(); + let detail = format!("`use {}` inside a function or block", paths.join(", ")); + violations.push(Violation::new(&file.path, item.line, &[&file.path], detail)); + } + } + violations +} + +fn is_restricted_visibility(code: &[Token], index: usize) -> bool { + index >= 3 && code[index - 3].is("pub") && code[index - 2].is("(") && code[index - 1].is("in") +} + +fn is_parent_qualifier(code: &[Token], index: usize) -> bool { + let self_super = + code[index].is("self") && code.get(index + 2).is_some_and(|next| next.is("super")); + code[index].is("crate") || code[index].is("super") || self_super +} + +fn inline_path_violations(tree: &SourceTree) -> Vec { + let mut violations = Vec::new(); + for file in &tree.files { + for (index, pair) in file.code.windows(2).enumerate() { + let qualifier = is_parent_qualifier(&file.code, index); + if !qualifier || !pair[1].is("::") || file.scan.contexts[index].use_item.is_some() { + continue; + } + let previous = index.checked_sub(1).map(|before| &file.code[before]); + if previous.is_some_and(|before| before.is("::") || before.is("$")) + || is_restricted_visibility(&file.code, index) + { + continue; + } + let head = if pair[0].is("self") { + "self::super" + } else { + &pair[0].text + }; + let detail = format!("inline `{head}::` path outside a use item"); + violations.push(Violation::new( + &file.path, + pair[0].line, + &[&file.path], + detail, + )); + } + } + violations +} + +#[test] +fn no_use_is_function_local() { + let violations = function_local_use_violations(&kernel_sources()); + enforce("no function-local use", &violations, None); +} + +#[test] +fn crate_and_super_paths_appear_only_in_use_items() { + let violations = inline_path_violations(&kernel_sources()); + enforce("no inline crate:: or super:: paths", &violations, None); +} diff --git a/main/opengeometry-three/src/markup/baseMarker.ts b/main/opengeometry/tests/source_rules/inline_tests.txt similarity index 100% rename from main/opengeometry-three/src/markup/baseMarker.ts rename to main/opengeometry/tests/source_rules/inline_tests.txt diff --git a/main/opengeometry/tests/source_rules/item_rules.rs b/main/opengeometry/tests/source_rules/item_rules.rs new file mode 100644 index 0000000..481e868 --- /dev/null +++ b/main/opengeometry/tests/source_rules/item_rules.rs @@ -0,0 +1,239 @@ +use crate::exceptions::{enforce, Violation}; +use crate::frozen_lists::RESERVED_FN_NAMES; +use crate::path_aliases::STD_CRATES; +use crate::scanner::AttributeItem; +use crate::source_tree::{SourceFile, SourceTree}; +use crate::{enforce_on_kernel, kernel_sources}; +use std::collections::BTreeSet; + +const RESERVED_NAMES: [&str; 4] = ["unwrap", "drop", "clone", "into"]; +const PRELUDE_TRAITS_WITH_RESERVED_METHODS: [&str; 3] = ["Clone", "Drop", "Into"]; +const LINT_ALLOWANCES: [&str; 2] = ["allow", "expect"]; +const CRATE_ROOT: &str = "lib.rs"; + +fn token_pair_violations(tree: &SourceTree, pair: [&str; 2], detail: &str) -> Vec { + let mut violations = Vec::new(); + for file in &tree.files { + for window in file.code.windows(2) { + if window[0].is(pair[0]) && window[1].is(pair[1]) { + violations.push(Violation::new( + &file.path, + window[0].line, + &[&file.path], + detail, + )); + } + } + } + violations +} + +fn include_macro_violations(tree: &SourceTree) -> Vec { + token_pair_violations( + tree, + ["include", "!"], + "`include!` pulls a file into a module", + ) +} + +fn attribute_violations(tree: &SourceTree, name: &str) -> Vec { + let mut violations = Vec::new(); + for file in &tree.files { + for attribute in file + .scan + .attributes + .iter() + .filter(|attribute| attribute.name == name) + { + let detail = format!("`#[{name}]` attribute"); + violations.push(Violation::new( + &file.path, + attribute.line, + &[&file.path], + detail, + )); + } + } + violations +} + +fn extern_crate_violations(tree: &SourceTree) -> Vec { + token_pair_violations(tree, ["extern", "crate"], "`extern crate` declaration") +} + +fn super_glob_violations(tree: &SourceTree) -> Vec { + let mut violations = Vec::new(); + for file in tree.files.iter().filter(|file| !file.test) { + for item in file.scan.uses.iter().filter(|item| !item.test) { + for path in item.paths.iter().filter(|path| path.is_super_glob()) { + let detail = format!("`use {};` outside a test module", path.text()); + violations.push(Violation::new(&file.path, item.line, &[&file.path], detail)); + } + } + } + violations +} + +fn is_std_trait(path: &[String], file: &SourceFile, crate_traits: &BTreeSet<&str>) -> bool { + let from_std = |segments: &[String]| { + segments.len() > 1 + && segments + .first() + .is_some_and(|root| STD_CRATES.contains(&root.as_str())) + }; + let [name] = path else { + return from_std(path); + }; + let imported = file + .scan + .uses + .iter() + .flat_map(|item| &item.paths) + .find(|use_path| use_path.binding.as_ref() == Some(name)); + match imported { + Some(use_path) => from_std(&use_path.segments), + None => { + PRELUDE_TRAITS_WITH_RESERVED_METHODS.contains(&name.as_str()) + && !crate_traits.contains(name.as_str()) + } + } +} + +fn reserved_fn_name_violations(tree: &SourceTree) -> Vec { + let crate_traits: BTreeSet<&str> = tree + .files + .iter() + .flat_map(|file| &file.scan.traits) + .map(String::as_str) + .collect(); + let mut violations = Vec::new(); + for file in &tree.files { + for function in &file.scan.fns { + let std_impl = function + .impl_trait + .as_ref() + .is_some_and(|path| is_std_trait(path, file, &crate_traits)); + if std_impl || !RESERVED_NAMES.contains(&function.bare.as_str()) { + continue; + } + let detail = format!("fn `{}` reuses a std method name", function.name); + let key = [file.path.as_str(), function.name.as_str()]; + violations.push(Violation::new(&file.path, function.start, &key, detail)); + } + } + violations +} + +fn names_lint_allowance(attribute: &AttributeItem) -> bool { + attribute + .words + .windows(2) + .any(|pair| LINT_ALLOWANCES.contains(&pair[0].as_str()) && pair[1] == "(") +} + +fn is_doc_text(attribute: &AttributeItem) -> bool { + attribute + .words + .windows(2) + .any(|pair| pair[0] == "doc" && pair[1] == "=") +} + +fn attribute_matches<'a>( + files: impl Iterator, + matches: fn(&AttributeItem) -> bool, + detail: &str, +) -> Vec { + let mut violations = Vec::new(); + for file in files { + for attribute in file.scan.attributes.iter().filter(|item| matches(item)) { + let text = format!("`#[{}]` {detail}", attribute.words.join("")); + violations.push(Violation::new( + &file.path, + attribute.line, + &[&file.path], + text, + )); + } + } + violations +} + +#[test] +fn source_uses_no_include_macro() { + enforce( + "no include!", + &include_macro_violations(&kernel_sources()), + None, + ); +} + +#[test] +fn source_uses_no_path_attribute() { + let violations = attribute_violations(&kernel_sources(), "path"); + enforce("no #[path]", &violations, None); +} + +#[test] +fn use_super_glob_appears_only_in_test_modules() { + let violations = super_glob_violations(&kernel_sources()); + enforce("no use super::* outside test modules", &violations, None); +} + +#[test] +fn no_test_is_ignored() { + let violations = attribute_violations(&kernel_sources(), "ignore"); + enforce("no #[ignore]", &violations, None); +} + +#[test] +fn no_fn_is_named_unwrap_drop_clone_or_into() { + enforce_on_kernel( + "no fn named unwrap, drop, clone or into", + reserved_fn_name_violations, + Some(&RESERVED_FN_NAMES), + ); +} + +#[test] +fn no_lint_is_allowed_outside_lib_rs() { + let tree = kernel_sources(); + let files = tree.files.iter().filter(|file| file.path != CRATE_ROOT); + let violations = attribute_matches(files, names_lint_allowance, "outside lib.rs"); + enforce("no #[allow] outside lib.rs", &violations, None); +} + +#[test] +fn no_doc_attribute_carries_text() { + let tree = kernel_sources(); + let violations = attribute_matches(tree.files.iter(), is_doc_text, "is a comment"); + enforce("zero comments, doc attributes included", &violations, None); +} + +#[test] +fn use_super_glob_rule_catches_a_self_super_glob() { + let tree = SourceTree::from_sources(&[ + ("geom2d/mod.rs", "mod edge;\n"), + ("geom2d/edge.rs", "use self::super::*;\n"), + ]); + let violations = super_glob_violations(&tree); + assert_eq!(violations.len(), 1); + assert_eq!(violations[0].location, "src/geom2d/edge.rs:1"); +} + +#[test] +fn source_declares_no_extern_crate() { + enforce_on_kernel("no extern crate", extern_crate_violations, None); +} + +#[test] +fn extern_crate_rule_catches_plain_aliased_and_self_forms() { + let tree = SourceTree::from_sources(&[( + "lib.rs", + "extern crate earcutr;\nextern crate earcutr as ear;\nextern crate self as kernel;\n", + )]); + let lines: Vec = extern_crate_violations(&tree) + .into_iter() + .map(|violation| violation.location) + .collect(); + assert_eq!(lines, ["src/lib.rs:1", "src/lib.rs:2", "src/lib.rs:3"]); +} diff --git a/main/opengeometry/tests/source_rules/json_values_in_signatures.txt b/main/opengeometry/tests/source_rules/json_values_in_signatures.txt new file mode 100644 index 0000000..691ab0d --- /dev/null +++ b/main/opengeometry/tests/source_rules/json_values_in_signatures.txt @@ -0,0 +1,2 @@ +world_graph/modifying.rs WorldGraph::report B5 +world_graph/shape_store.rs Shape B5 diff --git a/main/opengeometry/tests/source_rules/layer_rules.rs b/main/opengeometry/tests/source_rules/layer_rules.rs new file mode 100644 index 0000000..dc1a69a --- /dev/null +++ b/main/opengeometry/tests/source_rules/layer_rules.rs @@ -0,0 +1,286 @@ +use crate::exceptions::{enforce, ListSpec, Violation}; +use crate::frozen_lists::{MODULE_CYCLES, MUTUAL_IMPORTS, SIBLING_CYCLES, UPWARD_IMPORTS}; +use crate::graph::{cycles, import_edges, top_module, Edge}; +use crate::source_tree::SourceTree; +use crate::{kernel_sources, rule_list}; +use std::collections::{BTreeMap, BTreeSet}; + +const LAYERS: [(&str, usize); 11] = [ + ("math", 0), + ("brep", 1), + ("geom2d", 2), + ("query", 3), + ("primitives", 4), + ("tessellation", 4), + ("intersection", 5), + ("operations", 6), + ("exchange", 7), + ("world_graph", 8), + ("bindings", 9), +]; +const TEST_FIXTURE_LAYERS: usize = 7; +const BOOLEAN: [&str; 3] = ["operations", "modifying", "boolean"]; +const BOOLEAN_ORDER: [&str; 8] = [ + "types", + "operands", + "assembly", + "handlers", + "dispatch", + "batch", + "multi_tool", + "shell", +]; + +fn layer_of(module: &str) -> Option { + LAYERS + .iter() + .find(|(name, _)| *name == module) + .map(|(_, layer)| *layer) +} + +fn may_import(from: &str, to: &str) -> bool { + if from == to { + return true; + } + if from == "exchange" && to == "operations" { + return false; + } + match (layer_of(from), layer_of(to)) { + (Some(importer), Some(imported)) => imported < importer, + _ => false, + } +} + +fn test_may_import(from: &str, to: &str) -> bool { + may_import(from, to) || layer_of(to).is_some_and(|layer| layer <= TEST_FIXTURE_LAYERS) +} + +fn top_modules<'a>(tree: &'a SourceTree, edge: &Edge) -> Option<(&'a str, &'a str)> { + let from = top_module(&tree.files[edge.from])?; + let to = top_module(&tree.files[edge.to])?; + Some((from, to)) +} + +fn edge_layer_violation( + tree: &SourceTree, + edge: &Edge, + allowed: fn(&str, &str) -> bool, +) -> Option { + let file = &tree.files[edge.from].path; + let from = top_module(&tree.files[edge.from])?; + let detail = match top_module(&tree.files[edge.to]) { + None => format!("`{}` resolves to lib.rs, not to a layer module", edge.path), + Some(to) if !allowed(from, to) => { + format!("`{from}` may not import `{to}` (`{}`)", edge.path) + } + Some(_) => return None, + }; + Some(Violation::new(file, edge.line, &[file, &edge.path], detail)) +} + +fn layer_violations(tree: &SourceTree, edges: &[Edge]) -> Vec { + edges + .iter() + .filter(|edge| !edge.test) + .filter_map(|edge| edge_layer_violation(tree, edge, may_import)) + .collect() +} + +fn test_layer_violations(tree: &SourceTree, edges: &[Edge]) -> Vec { + edges + .iter() + .filter(|edge| edge.test) + .filter_map(|edge| edge_layer_violation(tree, edge, test_may_import)) + .collect() +} + +fn cycle_violations(tree: &SourceTree, edges: &[Edge]) -> Vec { + let mut adjacency: BTreeMap> = BTreeMap::new(); + for edge in edges.iter().filter(|edge| !edge.test) { + if let Some((from, to)) = top_modules(tree, edge) { + if from != to { + adjacency.entry(from.into()).or_default().insert(to.into()); + } + } + } + let mut violations = Vec::new(); + for cycle in cycles(&adjacency) { + let members: Vec<&str> = cycle.iter().map(String::as_str).collect(); + let key = members.join("<->"); + let closing = edges.iter().find(|edge| { + !edge.test + && top_modules(tree, edge).is_some_and(|(from, to)| { + from != to + && cycle.contains(from) + && cycle.contains(to) + && !may_import(from, to) + }) + }); + let (file, line) = closing.map_or(("lib.rs", 0), |edge| { + (tree.files[edge.from].path.as_str(), edge.line) + }); + let detail = format!("top-level modules {} import each other", members.join(", ")); + violations.push(Violation::new(file, line, &[&key], detail)); + } + violations +} + +fn mutual_import_violations(tree: &SourceTree, edges: &[Edge]) -> Vec { + let mut first_lines: BTreeMap<(usize, usize), usize> = BTreeMap::new(); + for edge in edges { + first_lines.entry((edge.from, edge.to)).or_insert(edge.line); + } + let mut violations = Vec::new(); + for (&(from, to), &line) in &first_lines { + let (first, second) = (&tree.files[from].path, &tree.files[to].path); + if first < second && first_lines.contains_key(&(to, from)) { + let detail = format!("`{first}` and `{second}` import each other"); + violations.push(Violation::new(first, line, &[first, second], detail)); + } + } + violations +} + +fn sibling_modules(edge: &Edge) -> Option<(&[String], &str, &str)> { + let (from, to) = (&edge.from_module, &edge.defining_module); + let common = from.iter().zip(to).take_while(|(a, b)| a == b).count(); + let siblings = common > 0 && common < from.len() && common < to.len(); + siblings.then(|| (&from[..common], from[common].as_str(), to[common].as_str())) +} + +fn sibling_cycle_violations(tree: &SourceTree, edges: &[Edge]) -> Vec { + let mut parents: BTreeMap<&[String], BTreeMap>> = BTreeMap::new(); + let mut first_edges: BTreeMap<(&[String], &str, &str), &Edge> = BTreeMap::new(); + for edge in edges.iter().filter(|edge| !edge.test) { + if let Some((parent, from, to)) = sibling_modules(edge) { + let adjacency = parents.entry(parent).or_default(); + adjacency.entry(from.into()).or_default().insert(to.into()); + first_edges.entry((parent, from, to)).or_insert(edge); + } + } + let mut violations = Vec::new(); + for (parent, adjacency) in &parents { + for cycle in cycles(adjacency) { + let members: Vec<&str> = cycle.iter().map(String::as_str).collect(); + let key = format!("{}::{}", parent.join("::"), members.join("<->")); + let edge = first_edges + .iter() + .find(|((owner, from, to), _)| { + owner == parent && cycle.contains(*from) && cycle.contains(*to) + }) + .map(|(_, edge)| *edge); + let (file, line) = edge.map_or(("lib.rs", 0), |edge| { + (tree.files[edge.from].path.as_str(), edge.line) + }); + let detail = format!( + "sibling modules {} under `{}` import each other", + members.join(", "), + parent.join("::") + ); + violations.push(Violation::new(file, line, &[&key], detail)); + } + } + violations +} + +fn boolean_stage(module: &[String]) -> Option<&str> { + let inside = module.len() > BOOLEAN.len() && module.iter().zip(BOOLEAN).all(|(a, b)| a == b); + inside.then(|| module[BOOLEAN.len()].as_str()) +} + +fn stage_rank(stage: &str) -> Option { + BOOLEAN_ORDER.iter().position(|name| *name == stage) +} + +fn boolean_order_violations(tree: &SourceTree, edges: &[Edge]) -> Vec { + let mut violations = Vec::new(); + for file in tree.files.iter().filter(|file| !file.test) { + if let Some(stage) = boolean_stage(&file.module).filter(|stage| stage_rank(stage).is_none()) + { + let detail = format!("boolean stage `{stage}` is not in the boolean order"); + violations.push(Violation::new(&file.path, 1, &[&file.path], detail)); + } + } + for edge in edges.iter().filter(|edge| !edge.test) { + let (from, to) = (&tree.files[edge.from], &tree.files[edge.to]); + let Some(from_rank) = boolean_stage(&from.module).and_then(stage_rank) else { + continue; + }; + let inside_boolean = + to.module.len() >= BOOLEAN.len() && to.module[..BOOLEAN.len()] == BOOLEAN; + if !inside_boolean { + continue; + } + let to_rank = boolean_stage(&to.module).and_then(stage_rank); + if to_rank.is_none_or(|rank| rank > from_rank) { + let stage = boolean_stage(&to.module).unwrap_or("the boolean root"); + let detail = format!( + "`{}` may not import `{stage}` (`{}`)", + BOOLEAN_ORDER[from_rank], edge.path + ); + violations.push(Violation::new( + &from.path, + edge.line, + &[&from.path, &edge.path], + detail, + )); + } + } + violations +} + +#[test] +fn non_test_imports_follow_the_layer_table() { + enforce_on_import_edges("layer table", layer_violations, Some(&UPWARD_IMPORTS)); +} + +#[test] +fn top_level_modules_have_no_import_cycle() { + enforce_on_import_edges( + "no cycle between top-level modules", + cycle_violations, + Some(&MODULE_CYCLES), + ); +} + +#[test] +fn no_two_files_import_each_other() { + enforce_on_import_edges( + "no two files import each other", + mutual_import_violations, + Some(&MUTUAL_IMPORTS), + ); +} + +#[test] +fn no_two_sibling_modules_import_each_other() { + enforce_on_import_edges( + "no import cycle between sibling modules", + sibling_cycle_violations, + Some(&SIBLING_CYCLES), + ); +} + +#[test] +fn test_code_imports_world_graph_and_bindings_only_from_inside_them() { + enforce_on_import_edges( + "test code imports L0-L7, and world_graph or bindings only from inside them", + test_layer_violations, + None, + ); +} + +#[test] +fn boolean_stages_import_only_earlier_stages() { + enforce_on_import_edges("boolean order types > operands > assembly > handlers > dispatch > batch > multi_tool > shell", boolean_order_violations, None); +} + +fn enforce_on_import_edges( + rule: &str, + check: fn(&SourceTree, &[Edge]) -> Vec, + spec: Option<&ListSpec>, +) { + let tree = kernel_sources(); + let list = spec.map(rule_list); + enforce(rule, &check(&tree, &import_edges(&tree)), list.as_ref()); +} diff --git a/main/opengeometry/tests/source_rules/lexer.rs b/main/opengeometry/tests/source_rules/lexer.rs new file mode 100644 index 0000000..882d025 --- /dev/null +++ b/main/opengeometry/tests/source_rules/lexer.rs @@ -0,0 +1,297 @@ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum TokenKind { + Ident, + Lifetime, + Literal, + Punct, + LineComment, + BlockComment, +} + +#[derive(Clone, Debug)] +pub(crate) struct Token { + pub(crate) kind: TokenKind, + pub(crate) text: String, + pub(crate) line: usize, +} + +impl Token { + pub(crate) fn is_comment(&self) -> bool { + matches!(self.kind, TokenKind::LineComment | TokenKind::BlockComment) + } + + pub(crate) fn is(&self, text: &str) -> bool { + matches!(self.kind, TokenKind::Ident | TokenKind::Punct) && self.text == text + } + + pub(crate) fn is_ident(&self) -> bool { + self.kind == TokenKind::Ident + } +} + +#[derive(Debug)] +pub(crate) struct LexError { + pub(crate) line: usize, + pub(crate) reason: &'static str, +} + +pub(crate) fn tokenize(source: &str) -> Result, LexError> { + let mut lexer = Lexer { + chars: source.chars().collect(), + at: 0, + line: 1, + tokens: Vec::new(), + }; + while let Some(current) = lexer.peek(0) { + lexer.next_token(current)?; + } + Ok(lexer.tokens) +} + +fn is_ident_start(c: char) -> bool { + c == '_' || c.is_alphabetic() +} + +fn is_ident_continue(c: char) -> bool { + c == '_' || c.is_alphanumeric() +} + +struct Lexer { + chars: Vec, + at: usize, + line: usize, + tokens: Vec, +} + +impl Lexer { + fn peek(&self, offset: usize) -> Option { + self.chars.get(self.at + offset).copied() + } + + fn next_token(&mut self, current: char) -> Result<(), LexError> { + let next = self.peek(1); + if current == '\n' { + self.line += 1; + self.at += 1; + } else if current.is_whitespace() { + self.at += 1; + } else if current == '/' && next == Some('/') { + self.line_comment(); + } else if current == '/' && next == Some('*') { + self.block_comment()?; + } else if let Some(prefix) = self.raw_string_prefix() { + self.raw_string(prefix)?; + } else if current == '"' || (matches!(current, 'b' | 'c') && next == Some('"')) { + self.quoted_string()?; + } else if current == '\'' || (current == 'b' && next == Some('\'')) { + self.char_or_lifetime()?; + } else if current == 'r' && next == Some('#') && self.peek(2).is_some_and(is_ident_start) { + self.raw_identifier(); + } else if is_ident_start(current) { + let start = self.at; + self.consume_while(is_ident_continue); + self.push(TokenKind::Ident, start, self.line); + } else if current.is_ascii_digit() { + self.number(); + } else { + self.punct(current); + } + Ok(()) + } + + fn push(&mut self, kind: TokenKind, start: usize, line: usize) { + let text = self.chars[start..self.at].iter().collect(); + self.tokens.push(Token { kind, text, line }); + } + + fn consume_while(&mut self, accept: fn(char) -> bool) { + while self.peek(0).is_some_and(accept) { + self.at += 1; + } + } + + fn advance_counting_lines(&mut self) { + if self.peek(0) == Some('\n') { + self.line += 1; + } + self.at += 1; + } + + fn line_comment(&mut self) { + let start = self.at; + while self.peek(0).is_some_and(|c| c != '\n') { + self.at += 1; + } + self.push(TokenKind::LineComment, start, self.line); + } + + fn block_comment(&mut self) -> Result<(), LexError> { + let (start, line) = (self.at, self.line); + let mut depth = 0usize; + loop { + match (self.peek(0), self.peek(1)) { + (Some('/'), Some('*')) => { + depth += 1; + self.at += 2; + } + (Some('*'), Some('/')) => { + depth -= 1; + self.at += 2; + if depth == 0 { + break; + } + } + (Some(_), _) => self.advance_counting_lines(), + (None, _) => { + return Err(LexError { + line, + reason: "unterminated block comment", + }) + } + } + } + self.push(TokenKind::BlockComment, start, line); + Ok(()) + } + + fn raw_string_prefix(&self) -> Option { + let mut offset = 0; + if matches!(self.peek(0), Some('b' | 'c')) { + offset = 1; + } + if self.peek(offset) != Some('r') { + return None; + } + let mut probe = offset + 1; + while self.peek(probe) == Some('#') { + probe += 1; + } + (self.peek(probe) == Some('"')).then_some(offset + 1) + } + + fn raw_string(&mut self, prefix: usize) -> Result<(), LexError> { + let (start, line) = (self.at, self.line); + self.at += prefix; + let mut hashes = 0; + while self.peek(0) == Some('#') { + hashes += 1; + self.at += 1; + } + self.at += 1; + loop { + match self.peek(0) { + Some('"') if (1..=hashes).all(|k| self.peek(k) == Some('#')) => { + self.at += 1 + hashes; + break; + } + Some(_) => self.advance_counting_lines(), + None => { + return Err(LexError { + line, + reason: "unterminated raw string", + }) + } + } + } + self.push(TokenKind::Literal, start, line); + Ok(()) + } + + fn quoted_string(&mut self) -> Result<(), LexError> { + let (start, line) = (self.at, self.line); + if self.peek(0) != Some('"') { + self.at += 1; + } + self.at += 1; + loop { + match self.peek(0) { + Some('"') => { + self.at += 1; + break; + } + Some('\\') => { + self.at += 1; + self.advance_counting_lines(); + } + Some(_) => self.advance_counting_lines(), + None => { + return Err(LexError { + line, + reason: "unterminated string", + }) + } + } + } + self.push(TokenKind::Literal, start, line); + Ok(()) + } + + fn char_or_lifetime(&mut self) -> Result<(), LexError> { + let start = self.at; + let byte = self.peek(0) == Some('b'); + let quote = usize::from(byte); + let escaped = self.peek(quote + 1) == Some('\\'); + let single = self.peek(quote + 2) == Some('\''); + if !escaped && !single { + if byte { + return Err(LexError { + line: self.line, + reason: "malformed byte literal", + }); + } + self.at += 1; + self.consume_while(is_ident_continue); + self.push(TokenKind::Lifetime, start, self.line); + return Ok(()); + } + self.at += quote + 1; + if escaped { + self.at += 2; + } + while self.peek(0).is_some_and(|c| c != '\'' && c != '\n') { + self.at += 1; + } + if self.peek(0) != Some('\'') { + return Err(LexError { + line: self.line, + reason: "unterminated character literal", + }); + } + self.at += 1; + self.push(TokenKind::Literal, start, self.line); + Ok(()) + } + + fn raw_identifier(&mut self) { + self.at += 2; + let start = self.at; + self.consume_while(is_ident_continue); + self.push(TokenKind::Ident, start, self.line); + } + + fn number(&mut self) { + let start = self.at; + self.consume_while(is_ident_continue); + if self.peek(0) == Some('.') && self.peek(1).is_some_and(|c| c.is_ascii_digit()) { + self.at += 1; + self.consume_while(is_ident_continue); + } + let hex = self.chars[start..].starts_with(&['0', 'x']); + let exponent = matches!(self.chars[self.at - 1], 'e' | 'E'); + if exponent && !hex && matches!(self.peek(0), Some('+' | '-')) { + self.at += 1; + self.consume_while(is_ident_continue); + } + self.push(TokenKind::Literal, start, self.line); + } + + fn punct(&mut self, current: char) { + let start = self.at; + self.at += if current == ':' && self.peek(1) == Some(':') { + 2 + } else { + 1 + }; + self.push(TokenKind::Punct, start, self.line); + } +} diff --git a/main/opengeometry/tests/source_rules/lexer_cases.rs b/main/opengeometry/tests/source_rules/lexer_cases.rs new file mode 100644 index 0000000..7d464de --- /dev/null +++ b/main/opengeometry/tests/source_rules/lexer_cases.rs @@ -0,0 +1,90 @@ +use crate::lexer::{tokenize, Token, TokenKind}; + +fn tokens_of(source: &str) -> Vec { + tokenize(source).unwrap_or_else(|error| panic!("line {}: {}", error.line, error.reason)) +} + +fn kinds_of(source: &str) -> Vec<(TokenKind, String)> { + tokens_of(source) + .into_iter() + .map(|token| (token.kind, token.text)) + .collect() +} + +#[test] +fn lexer_keeps_comment_markers_inside_strings_raw_strings_and_byte_strings() { + let source = r####"let a = "// not /* a comment"; let b = r#"say "// hi" "#; let c = b"/*"; let d = br##"x"#"##;"####; + let tokens = tokens_of(source); + assert!(tokens.iter().all(|token| !token.is_comment())); + let literals: Vec<&str> = tokens + .iter() + .filter(|token| token.kind == TokenKind::Literal) + .map(|token| token.text.as_str()) + .collect(); + assert_eq!( + literals, + [ + "\"// not /* a comment\"", + "r#\"say \"// hi\" \"#", + "b\"/*\"", + "br##\"x\"#\"##" + ] + ); +} + +#[test] +fn lexer_tells_char_literals_from_lifetimes() { + let kinds = kinds_of( + "fn f<'a>(x: &'a str) -> char { let q = '\\''; let s = 'x'; let b = b'{'; '\\u{7f}' }", + ); + let literals: Vec<&str> = kinds + .iter() + .filter(|(kind, _)| *kind == TokenKind::Literal) + .map(|(_, text)| text.as_str()) + .collect(); + let lifetimes = kinds + .iter() + .filter(|(kind, _)| *kind == TokenKind::Lifetime) + .count(); + assert_eq!(literals, ["'\\''", "'x'", "b'{'", "'\\u{7f}'"]); + assert_eq!(lifetimes, 2); + assert_eq!(kinds.iter().filter(|(_, text)| text == "{").count(), 1); +} + +#[test] +fn lexer_nests_block_comments_and_counts_their_lines() { + let tokens = tokens_of("a /* outer /* inner */ still outer\n */ b // tail\nc"); + let comments: Vec<(TokenKind, usize)> = tokens + .iter() + .filter(|token| token.is_comment()) + .map(|token| (token.kind, token.line)) + .collect(); + assert_eq!( + comments, + [(TokenKind::BlockComment, 1), (TokenKind::LineComment, 2)] + ); + let idents: Vec<(&str, usize)> = tokens + .iter() + .filter(|token| token.is_ident()) + .map(|token| (token.text.as_str(), token.line)) + .collect(); + assert_eq!(idents, [("a", 1), ("b", 2), ("c", 3)]); +} + +#[test] +fn lexer_rejects_unterminated_literals_and_comments() { + for source in ["\"open", "r#\"open\"", "/* open /* */", "b'x"] { + assert!(tokenize(source).is_err(), "{source}"); + } +} + +#[test] +fn lexer_reads_a_raw_identifier_as_its_plain_name() { + let kinds = kinds_of("let r#type = r#OnceLock::new(); fn r#unwrap() {}"); + let idents: Vec<&str> = kinds + .iter() + .filter(|(kind, _)| *kind == TokenKind::Ident) + .map(|(_, text)| text.as_str()) + .collect(); + assert_eq!(idents, ["let", "type", "OnceLock", "new", "fn", "unwrap"]); +} diff --git a/main/opengeometry/tests/source_rules/main.rs b/main/opengeometry/tests/source_rules/main.rs new file mode 100644 index 0000000..9bdc66d --- /dev/null +++ b/main/opengeometry/tests/source_rules/main.rs @@ -0,0 +1,87 @@ +#![cfg(not(target_arch = "wasm32"))] + +mod determinism_rules; +mod exceptions; +mod external_crate_rules; +mod frozen_lists; +mod function_rules; +mod graph; +mod import_rules; +mod item_rules; +mod layer_rules; +mod lexer; +mod lexer_cases; +mod module_paths; +mod module_rules; +mod path_aliases; +mod scanner; +mod source_tree; +mod test_rules; +mod text_rules; +mod type_rules; +mod use_tree; + +use exceptions::{enforce, ExceptionList, ListSpec, Violation}; +use source_tree::SourceTree; +use std::path::{Path, PathBuf}; + +const RULES_ROOT: &str = "tests/source_rules"; +const ROOTS_OUTSIDE_KERNEL_SRC: [&str; 3] = ["tests", "test-support/src", "examples"]; + +fn kernel_dir() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) +} + +fn load_tree(root: &Path) -> SourceTree { + SourceTree::load(root) + .unwrap_or_else(|error| panic!("cannot read {}: {}", error.path.display(), error.reason)) +} + +fn kernel_sources() -> SourceTree { + load_tree(&kernel_dir().join("src")) +} + +fn rule_sources() -> SourceTree { + load_tree(&kernel_dir().join(RULES_ROOT)) +} + +fn rule_violations(check: fn(&SourceTree) -> Vec) -> Vec { + check(&rule_sources()) + .into_iter() + .map(|violation| violation.under(RULES_ROOT)) + .collect() +} + +fn violations_outside_kernel_src(check: fn(&SourceTree) -> Vec) -> Vec { + let mut violations = Vec::new(); + for root in ROOTS_OUTSIDE_KERNEL_SRC { + let mut tree = load_tree(&kernel_dir().join(root)); + tree.files + .retain(|file| !Path::new(root).join(&file.path).starts_with(RULES_ROOT)); + violations.extend( + check(&tree) + .into_iter() + .map(|violation| violation.under(root)), + ); + } + violations +} + +fn kernel_manifest() -> String { + let path = kernel_dir().join("Cargo.toml"); + std::fs::read_to_string(&path) + .unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display())) +} + +fn rule_list(spec: &ListSpec) -> ExceptionList { + ExceptionList::load(&kernel_dir().join(RULES_ROOT).join(spec.file), spec) +} + +fn enforce_on_kernel( + rule: &str, + check: fn(&SourceTree) -> Vec, + spec: Option<&ListSpec>, +) { + let list = spec.map(rule_list); + enforce(rule, &check(&kernel_sources()), list.as_ref()); +} diff --git a/main/opengeometry-three/src/utils/store.ts b/main/opengeometry/tests/source_rules/module_cycles.txt similarity index 100% rename from main/opengeometry-three/src/utils/store.ts rename to main/opengeometry/tests/source_rules/module_cycles.txt diff --git a/main/opengeometry/tests/source_rules/module_paths.rs b/main/opengeometry/tests/source_rules/module_paths.rs new file mode 100644 index 0000000..d6d7646 --- /dev/null +++ b/main/opengeometry/tests/source_rules/module_paths.rs @@ -0,0 +1,59 @@ +use crate::source_tree::SourceTree; +use std::collections::BTreeMap; + +pub(crate) type ModuleTable = BTreeMap, usize>; + +pub(crate) fn module_table(tree: &SourceTree) -> ModuleTable { + let mut modules = ModuleTable::new(); + for (index, file) in tree.files.iter().enumerate() { + modules.insert(file.module.clone(), index); + for declaration in file + .scan + .mods + .iter() + .filter(|declaration| declaration.inline) + { + let inline = [ + file.module.clone(), + declaration.scope.clone(), + vec![declaration.name.clone()], + ] + .concat(); + modules.insert(inline, index); + } + } + modules +} + +pub(crate) fn absolute_path( + segments: &[String], + scope: &[String], + local: Option<&BTreeMap>>, + modules: &ModuleTable, +) -> Option> { + let (head, rest) = segments.split_first()?; + match head.as_str() { + "crate" => Some(rest.to_vec()), + "self" if rest.first().is_some_and(|next| next == "super") => { + absolute_path(rest, scope, local, modules) + } + "self" => Some([scope, rest].concat()), + "super" => { + let supers = segments + .iter() + .take_while(|segment| *segment == "super") + .count(); + let base = scope.get(..scope.len().checked_sub(supers)?)?; + Some([base, &segments[supers..]].concat()) + } + _ => { + let child = [scope, std::slice::from_ref(head)].concat(); + if modules.contains_key(&child) { + Some([scope, segments].concat()) + } else { + let base = local?.get(head)?; + Some([base.as_slice(), rest].concat()) + } + } + } +} diff --git a/main/opengeometry/tests/source_rules/module_rules.rs b/main/opengeometry/tests/source_rules/module_rules.rs new file mode 100644 index 0000000..edd3d74 --- /dev/null +++ b/main/opengeometry/tests/source_rules/module_rules.rs @@ -0,0 +1,151 @@ +use crate::exceptions::{enforce, Violation}; +use crate::source_tree::SourceTree; +use crate::{enforce_on_kernel, kernel_sources}; + +const CATCH_ALL_NAMES: [&str; 5] = ["utils", "helpers", "common", "misc", "booleans"]; +const HANDLERS: &str = "operations/modifying/boolean/handlers"; +const OPERANDS: [&str; 6] = [ + "box", + "conic", + "cylinder", + "planar_extrusion", + "sphere", + "torus", +]; +const RELATIONS: [&str; 2] = ["coincident", "disjoint"]; +const FAMILIES: [&str; 5] = [ + "containment", + "generic", + "planar", + "rectilinear", + "vertical_arc_extrusion", +]; + +fn missing_mod_rs_violations(tree: &SourceTree) -> Vec { + tree.directories + .iter() + .filter(|directory| !tree.has_file(&format!("{directory}/mod.rs"))) + .map(|directory| { + Violation::new( + directory, + 0, + &[directory], + "directory module without mod.rs", + ) + }) + .collect() +} + +fn sibling_file_violations(tree: &SourceTree) -> Vec { + tree.directories + .iter() + .filter(|directory| tree.has_file(&format!("{directory}.rs"))) + .map(|directory| { + let file = format!("{directory}.rs"); + let detail = format!("`{file}` sits beside the directory `{directory}/`"); + Violation::new(&file, 1, &[&file], detail) + }) + .collect() +} + +fn catch_all_name_violations(tree: &SourceTree) -> Vec { + let mut violations = Vec::new(); + for file in &tree.files { + let declared = file.module.last().map(|name| (name.as_str(), 1)); + let inline = file + .scan + .mods + .iter() + .filter(|declaration| declaration.inline) + .map(|declaration| (declaration.name.as_str(), declaration.line)); + for (name, line) in declared.into_iter().chain(inline) { + if CATCH_ALL_NAMES.contains(&name) { + let detail = format!("module `{name}` has a catch-all name"); + violations.push(Violation::new(&file.path, line, &[&file.path], detail)); + } + } + } + violations +} + +fn is_operand_pair(stem: &str) -> bool { + OPERANDS.iter().any(|first| { + stem.strip_prefix(first) + .and_then(|rest| rest.strip_prefix('_')) + .is_some_and(|second| OPERANDS.contains(&second)) + }) +} + +fn is_same_operand_pair(stem: &str) -> bool { + OPERANDS.iter().any(|operand| { + let plural = if operand.ends_with('x') { "es" } else { "s" }; + stem == format!("{operand}{plural}") + }) +} + +fn is_handler_file_name(stem: &str) -> bool { + stem == "mod" + || is_operand_pair(stem) + || is_same_operand_pair(stem) + || RELATIONS.contains(&stem) +} + +fn is_family_name(name: &str) -> bool { + is_operand_pair(name) || FAMILIES.contains(&name) +} + +fn handler_name_violations(tree: &SourceTree) -> Vec { + let prefix = format!("{HANDLERS}/"); + let mut violations: Vec = tree + .files + .iter() + .filter(|file| file.directory() == HANDLERS && !is_handler_file_name(file.stem())) + .map(|file| { + let detail = "handler file is neither `_.rs` nor a family directory"; + Violation::new(&file.path, 1, &[&file.path], detail) + }) + .collect(); + for directory in &tree.directories { + let Some(name) = directory.strip_prefix(&prefix) else { + continue; + }; + if !name.contains('/') && !is_family_name(name) { + let detail = format!("handler family `{name}/` is not in the operand vocabulary"); + violations.push(Violation::new(directory, 0, &[directory], detail)); + } + } + violations +} + +#[test] +fn directory_modules_use_mod_rs() { + enforce( + "directory modules use mod.rs", + &missing_mod_rs_violations(&kernel_sources()), + None, + ); +} + +#[test] +fn no_module_file_sits_beside_its_directory() { + let violations = sibling_file_violations(&kernel_sources()); + enforce("no x.rs beside x/", &violations, None); +} + +#[test] +fn no_module_has_a_catch_all_name() { + enforce_on_kernel( + "no module named utils, helpers, common, misc or booleans", + catch_all_name_violations, + None, + ); +} + +#[test] +fn boolean_handler_files_are_operand_pairs_or_family_directories() { + enforce_on_kernel( + "handler files are _.rs or /", + handler_name_violations, + None, + ); +} diff --git a/main/opengeometry/tests/source_rules/mutual_imports.txt b/main/opengeometry/tests/source_rules/mutual_imports.txt new file mode 100644 index 0000000..e69de29 diff --git a/main/opengeometry/tests/source_rules/path_aliases.rs b/main/opengeometry/tests/source_rules/path_aliases.rs new file mode 100644 index 0000000..c6ceccb --- /dev/null +++ b/main/opengeometry/tests/source_rules/path_aliases.rs @@ -0,0 +1,255 @@ +use crate::lexer::Token; +use crate::module_paths::{absolute_path, module_table, ModuleTable}; +use crate::source_tree::{SourceFile, SourceTree}; +use std::collections::BTreeMap; + +const EXPANSION_LIMIT: usize = 32; +const EXPORT_HOPS: usize = 16; +const CRATE: &str = "crate"; +pub(crate) const STD_CRATES: [&str; 2] = ["std", "core"]; +const KEYWORDS: [&str; 32] = [ + "as", "async", "await", "break", "const", "continue", "dyn", "else", "enum", "extern", "fn", + "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref", + "return", "static", "struct", "trait", "type", "unsafe", "use", "where", "while", +]; +const PARAMETER_LIST_HEADS: [&str; 2] = ["impl", "for"]; +const BLOCK_BOUNDARIES: [&str; 2] = ["{", "}"]; +const GROUP_OPENERS: [&str; 2] = ["(", "["]; +const GROUP_CLOSERS: [&str; 2] = [")", "]"]; + +pub(crate) type Target = fn(&[String]) -> Option<&'static str>; +type NameTable = BTreeMap, BTreeMap>; + +fn is_arrow(code: &[Token], index: usize) -> bool { + index > 0 && (code[index - 1].is("-") || code[index - 1].is("=")) +} + +fn is_one_of(token: &Token, words: &[&str]) -> bool { + words.iter().any(|word| token.is(word)) +} + +fn closes_path_arguments(code: &[Token], close: usize) -> bool { + let (mut angles, mut groups) = (0usize, 0usize); + for index in (0..=close).rev() { + let token = &code[index]; + let ends_statement = token.is(";") || is_one_of(token, &GROUP_OPENERS); + if is_one_of(token, &BLOCK_BOUNDARIES) || (groups == 0 && ends_statement) { + return false; + } + if is_one_of(token, &GROUP_CLOSERS) { + groups += 1; + } else if is_one_of(token, &GROUP_OPENERS) { + groups -= 1; + } else if groups == 0 && token.is(">") && !is_arrow(code, index) { + angles += 1; + } else if groups == 0 && token.is("<") { + angles -= 1; + if angles == 0 { + let head = index.checked_sub(1).map(|before| &code[before]); + return !head.is_some_and(|head| is_one_of(head, &PARAMETER_LIST_HEADS)); + } + } + } + false +} + +fn qualifies(code: &[Token], index: usize) -> bool { + let token = &code[index]; + if token.is(">") { + return !is_arrow(code, index) && closes_path_arguments(code, index); + } + token.is_ident() && !KEYWORDS.contains(&token.text.as_str()) +} + +pub(crate) fn opens_path(code: &[Token], index: usize) -> bool { + code[index].is("::") && !(index > 0 && qualifies(code, index - 1)) +} + +pub(crate) fn path_at(code: &[Token], index: usize) -> Option> { + let leading = opens_path(code, index); + let first = index + usize::from(leading); + let starts = leading || !(index > 0 && code[index - 1].is("::")); + if !starts || !code.get(first).is_some_and(Token::is_ident) { + return None; + } + let mut segments = vec![code[first].text.clone()]; + let mut next = first + 1; + while next + 1 < code.len() && code[next].is("::") && code[next + 1].is_ident() { + segments.push(code[next + 1].text.clone()); + next += 2; + } + Some(segments) +} + +fn type_alias_spans(code: &[Token]) -> Vec<(String, usize, usize)> { + let mut spans = Vec::new(); + for index in 0..code.len().saturating_sub(1) { + if !code[index].is("type") || !code[index + 1].is_ident() { + continue; + } + let Some(end) = (index..code.len()).find(|&at| code[at].is(";")) else { + continue; + }; + if let Some(equals) = (index..end).find(|&at| code[at].is("=")) { + spans.push((code[index + 1].text.clone(), equals + 1, end)); + } + } + spans +} + +pub(crate) struct Exports { + modules: ModuleTable, + names: NameTable, +} + +impl Exports { + pub(crate) fn build(tree: &SourceTree, target: Target) -> Exports { + let mut exports = Exports { + modules: module_table(tree), + names: NameTable::new(), + }; + for _ in 0..EXPORT_HOPS { + let mut names = NameTable::new(); + for file in &tree.files { + let finder = PathFinder::new(file, target, &exports); + for (scope, name, found) in finder.named_targets() { + names.entry(scope).or_default().insert(name, found); + } + } + if names == exports.names { + break; + } + exports.names = names; + } + exports + } + + fn target(&self, path: &[String], scope: &[String]) -> Option<&'static str> { + let absolute = absolute_path(path, scope, None, &self.modules)?; + (0..absolute.len()).find_map(|split| { + let names = self.names.get(&absolute[..split])?; + names.get(&absolute[split]).copied() + }) + } +} + +pub(crate) struct PathFinder<'a> { + file: &'a SourceFile, + bound: BTreeMap>>, + type_aliases: BTreeMap, + target: Target, + exports: &'a Exports, +} + +impl<'a> PathFinder<'a> { + pub(crate) fn new( + file: &'a SourceFile, + target: Target, + exports: &'a Exports, + ) -> PathFinder<'a> { + let mut bound: BTreeMap>> = BTreeMap::new(); + for item in &file.scan.uses { + let scope = [file.module.as_slice(), &item.scope].concat(); + for path in &item.paths { + let Some(binding) = &path.binding else { + continue; + }; + let resolved = absolute_path(&path.segments, &scope, None, &exports.modules) + .map_or_else( + || path.segments.clone(), + |absolute| [vec![CRATE.to_string()], absolute].concat(), + ); + bound.entry(binding.clone()).or_default().push(resolved); + } + } + let mut finder = PathFinder { + file, + bound, + type_aliases: BTreeMap::new(), + target, + exports, + }; + finder.follow_type_aliases(); + finder + } + + fn follow_type_aliases(&mut self) { + let declared = type_alias_spans(&self.file.code); + loop { + let known = self.type_aliases.len(); + for (name, start, end) in &declared { + if self.type_aliases.contains_key(name) { + continue; + } + if let Some(found) = (*start..*end).find_map(|index| self.target_at(index)) { + self.type_aliases.insert(name.clone(), found); + } + } + if self.type_aliases.len() == known { + return; + } + } + } + + fn named_targets(&self) -> Vec<(Vec, String, &'static str)> { + let mut named = Vec::new(); + for item in &self.file.scan.uses { + let scope = [self.file.module.as_slice(), &item.scope].concat(); + for path in &item.paths { + let found = self.path_target(&path.segments, &scope); + if let (Some(binding), Some(found)) = (&path.binding, found) { + named.push((scope.clone(), binding.clone(), found)); + } + } + } + let code = &self.file.code; + for index in 1..code.len() { + let found = self.type_aliases.get(&code[index].text); + if let Some(&found) = found.filter(|_| code[index - 1].is("type")) { + named.push((self.scope_at(index), code[index].text.clone(), found)); + } + } + named + } + + fn expansions(&self, segments: &[String]) -> Vec> { + let mut found = vec![segments.to_vec()]; + let mut next = 0; + while next < found.len() && found.len() < EXPANSION_LIMIT { + let path = found[next].clone(); + next += 1; + for bound in self.bound.get(&path[0]).into_iter().flatten() { + let expanded = [bound.as_slice(), &path[1..]].concat(); + if !found.contains(&expanded) { + found.push(expanded); + } + } + } + found + } + + pub(crate) fn scope_at(&self, index: usize) -> Vec { + [self.file.module.as_slice(), self.file.scan.scope_at(index)].concat() + } + + pub(crate) fn path_target( + &self, + segments: &[String], + scope: &[String], + ) -> Option<&'static str> { + if let Some(found) = segments + .first() + .and_then(|head| self.type_aliases.get(head)) + { + return Some(found); + } + self.expansions(segments).iter().find_map(|expanded| { + (self.target)(expanded).or_else(|| self.exports.target(expanded, scope)) + }) + } + + pub(crate) fn target_at(&self, index: usize) -> Option<&'static str> { + let path = path_at(&self.file.code, index)?; + self.path_target(&path, &self.scope_at(index)) + } +} diff --git a/main/opengeometry/tests/source_rules/ported_long_functions.txt b/main/opengeometry/tests/source_rules/ported_long_functions.txt new file mode 100644 index 0000000..e69de29 diff --git a/main/opengeometry/tests/source_rules/reserved_fn_names.txt b/main/opengeometry/tests/source_rules/reserved_fn_names.txt new file mode 100644 index 0000000..e69de29 diff --git a/main/opengeometry/tests/source_rules/scanner.rs b/main/opengeometry/tests/source_rules/scanner.rs new file mode 100644 index 0000000..2ed0676 --- /dev/null +++ b/main/opengeometry/tests/source_rules/scanner.rs @@ -0,0 +1,563 @@ +use crate::lexer::Token; +use crate::use_tree::{expand_use_tree, UsePath}; +use std::collections::BTreeMap; + +#[derive(Clone, Debug)] +pub(crate) struct UseItem { + pub(crate) line: usize, + pub(crate) label: String, + pub(crate) paths: Vec, + pub(crate) scope: Vec, + pub(crate) test: bool, + pub(crate) local: bool, +} + +#[derive(Clone, Debug)] +pub(crate) struct FnItem { + pub(crate) name: String, + pub(crate) bare: String, + pub(crate) impl_trait: Option>, + pub(crate) start: usize, + pub(crate) end: usize, + pub(crate) test: bool, +} + +impl FnItem { + pub(crate) fn span(&self) -> usize { + self.end + 1 - self.start + } +} + +#[derive(Clone, Debug)] +pub(crate) struct ModItem { + pub(crate) name: String, + pub(crate) line: usize, + pub(crate) test: bool, + pub(crate) inline: bool, + pub(crate) scope: Vec, +} + +#[derive(Clone, Debug)] +pub(crate) struct AttributeItem { + pub(crate) name: String, + pub(crate) words: Vec, + pub(crate) line: usize, +} + +#[derive(Clone, Debug)] +pub(crate) struct TestRoot { + pub(crate) name: String, + pub(crate) line: usize, +} + +#[derive(Clone, Copy, Debug, Default)] +pub(crate) struct Context { + pub(crate) test: bool, + pub(crate) use_item: Option, + pub(crate) item: usize, + pub(crate) scope: usize, +} + +#[derive(Debug, Default)] +pub(crate) struct FileScan { + pub(crate) uses: Vec, + pub(crate) fns: Vec, + pub(crate) mods: Vec, + pub(crate) attributes: Vec, + pub(crate) contexts: Vec, + pub(crate) item_names: Vec, + pub(crate) scopes: Vec>, + pub(crate) traits: Vec, + pub(crate) test_roots: Vec, + pub(crate) impl_labels: BTreeMap, +} + +impl FileScan { + pub(crate) fn item_name(&self, index: usize) -> &str { + &self.item_names[self.contexts[index].item] + } + + pub(crate) fn scope_at(&self, index: usize) -> &[String] { + &self.scopes[self.contexts[index].scope] + } +} + +pub(crate) fn scan_file(tokens: &[Token]) -> FileScan { + let mut scanner = Scanner { + tokens, + at: 0, + frames: vec![Frame { + kind: FrameKind::Block, + test: false, + test_element: None, + item: 0, + scope: 0, + depth: 0, + generics: 0, + }], + pending: Pending::default(), + seen: BTreeMap::new(), + scan: FileScan { + item_names: vec![String::new()], + scopes: vec![Vec::new()], + ..FileScan::default() + }, + }; + while scanner.at < tokens.len() { + scanner.step(); + } + scanner.scan +} + +type TraitPath = Option>; + +enum FrameKind { + Module, + Function(usize), + Owner(TraitPath), + Block, +} + +struct Frame { + kind: FrameKind, + test: bool, + test_element: Option, + item: usize, + scope: usize, + depth: usize, + generics: usize, +} + +#[derive(Default)] +struct Pending { + test: bool, + test_case: bool, + function: Option<(String, usize)>, + module: Option, + owner: Option, + item: Option, +} + +impl Pending { + fn names_nothing(&self) -> bool { + self.function.is_none() + && self.module.is_none() + && self.owner.is_none() + && self.item.is_none() + } +} + +struct Scanner<'a> { + tokens: &'a [Token], + at: usize, + frames: Vec, + pending: Pending, + seen: BTreeMap, + scan: FileScan, +} + +const NAMED_ITEMS: [&str; 6] = ["struct", "enum", "union", "const", "static", "type"]; +const NOT_NAMES: [&str; 5] = ["fn", "mut", "dyn", "unsafe", "const"]; +const GENERIC_ITEMS: [&str; 6] = ["fn", "struct", "enum", "union", "trait", "type"]; +const GENERIC_HEADS: [&str; 2] = ["impl", "for"]; + +impl Scanner<'_> { + fn token(&self, offset: usize) -> Option<&Token> { + self.tokens.get(self.at + offset) + } + + fn top(&self) -> &Frame { + &self.frames[self.frames.len() - 1] + } + + fn in_function(&self) -> bool { + self.frames + .iter() + .any(|frame| matches!(frame.kind, FrameKind::Function(_))) + } + + fn opens_generic_parameters(&self) -> bool { + let before = |offset: usize| self.at.checked_sub(offset).map(|at| &self.tokens[at]); + let after_head = + before(1).is_some_and(|head| GENERIC_HEADS.iter().any(|keyword| head.is(keyword))); + let after_name = before(1).is_some_and(Token::is_ident) + && before(2).is_some_and(|item| GENERIC_ITEMS.iter().any(|keyword| item.is(keyword))); + after_head || after_name + } + + fn in_test(&self) -> bool { + self.pending.test || self.top().test || self.top().test_element.is_some() + } + + fn scope(&self) -> Vec { + self.scan.scopes[self.top().scope].clone() + } + + fn numbered(&mut self, path: String) -> String { + let count = self.seen.entry(path.clone()).or_insert(0); + *count += 1; + if *count > 1 { + format!("{path}#{count}") + } else { + path + } + } + + fn new_item(&mut self, name: &str, numbered: bool) -> usize { + let parent = &self.scan.item_names[self.top().item]; + let path = if parent.is_empty() { + name.to_string() + } else { + format!("{parent}::{name}") + }; + let path = if numbered { self.numbered(path) } else { path }; + self.scan.item_names.push(path); + self.scan.item_names.len() - 1 + } + + fn record_context(&mut self, use_item: Option) { + let context = Context { + test: self.in_test(), + use_item, + item: self.pending.item.unwrap_or(self.top().item), + scope: self.top().scope, + }; + self.scan.contexts.push(context); + } + + fn step(&mut self) { + let tokens = self.tokens; + let token = &tokens[self.at]; + if token.is("#") + && self + .token(1) + .is_some_and(|next| next.is("[") || next.is("!")) + { + self.attribute(); + return; + } + if token.is("use") { + self.use_item(); + return; + } + self.note_keyword(); + self.record_context(None); + self.structure(); + self.at += 1; + } + + fn matching_close(&self, open: usize, left: &str, right: &str) -> usize { + let mut depth = 0usize; + for (index, token) in self.tokens.iter().enumerate().skip(open) { + if token.is(left) { + depth += 1; + } else if token.is(right) { + depth -= 1; + if depth == 0 { + return index; + } + } + } + self.tokens.len() - 1 + } + + fn attribute(&mut self) { + let inner = self.token(1).is_some_and(|next| next.is("!")); + let open = self.at + 1 + usize::from(inner); + let close = self.matching_close(open, "[", "]"); + let words: Vec = self.tokens[open + 1..close] + .iter() + .map(|token| token.text.clone()) + .collect(); + let test_case = + words == ["test"] || words.first().is_some_and(|w| w == "wasm_bindgen_test"); + let marks_test = test_case || words == ["cfg", "(", "test", ")"]; + if let Some(name) = words.first() { + self.scan.attributes.push(AttributeItem { + name: name.clone(), + words: words.clone(), + line: self.tokens[self.at].line, + }); + } + let depth = self.top().depth; + if marks_test && !inner && depth > 0 { + if let Some(frame) = self.frames.last_mut() { + frame.test_element = Some(depth); + } + } else if marks_test && !inner { + self.pending.test = true; + self.pending.test_case |= test_case; + } + while self.at <= close { + self.record_context(None); + self.at += 1; + } + } + + fn use_item(&mut self) { + let start = self.at; + let mut end = start; + let mut depth = 0usize; + while end < self.tokens.len() { + let token = &self.tokens[end]; + if token.is("{") { + depth += 1; + } else if token.is("}") { + depth = depth.saturating_sub(1); + } else if token.is(";") && depth == 0 { + break; + } + end += 1; + } + let body: Vec<&Token> = self.tokens[start + 1..end.min(self.tokens.len())] + .iter() + .collect(); + let index = self.scan.uses.len(); + let paths = expand_use_tree(&body); + let text: Vec = paths.iter().map(UsePath::text).collect(); + let item = UseItem { + line: self.tokens[start].line, + label: self.numbered(format!("use@{}", text.join(","))), + paths, + scope: self.scope(), + test: self.in_test(), + local: self.frames[1..] + .iter() + .any(|frame| !matches!(frame.kind, FrameKind::Module)), + }; + self.scan.uses.push(item); + while self.at <= end && self.at < self.tokens.len() { + self.record_context(Some(index)); + self.at += 1; + } + self.pending = Pending::default(); + } + + fn next_name(&self) -> Option { + self.token(1) + .filter(|next| next.is_ident() && !NOT_NAMES.contains(&next.text.as_str())) + .map(|next| next.text.clone()) + } + + fn note_keyword(&mut self) { + let tokens = self.tokens; + let token = &tokens[self.at]; + let word = token.text.as_str(); + if !token.is_ident() { + return; + } + if word == "fn" { + if let Some(bare) = self.next_name() { + self.pending.item = Some(self.new_item(&bare, true)); + self.pending.function = Some((bare, token.line)); + } + } else if word == "mod" { + self.note_module(); + } else if word == "impl" && self.pending.function.is_none() { + self.note_impl(); + } else if word == "trait" { + if let Some(name) = self.next_name() { + self.scan.traits.push(name.clone()); + self.pending.item = Some(self.new_item(&name, true)); + self.pending.owner = Some(None); + } + } else if NAMED_ITEMS.contains(&word) && self.pending.item.is_none() && !self.keys_to_fn() { + let name = self.next_name().or_else(|| { + self.token(1) + .filter(|next| next.is("mut")) + .and_then(|_| self.token(2)) + .map(|name| name.text.clone()) + }); + if let Some(name) = name { + self.pending.item = Some(self.new_item(&name, true)); + } + } + } + + fn keys_to_fn(&self) -> bool { + let pointer = self.at > 0 && self.tokens[self.at - 1].is("*"); + self.tokens[self.at].is("const") && (pointer || self.in_function()) + } + + fn note_impl(&mut self) { + let (owner, trait_path) = self.impl_owner(); + let item = self.new_item(&owner, false); + let owner = &self.scan.item_names[item]; + let label = match &trait_path { + Some(path) => format!("impl@{owner}:{}", path.join("::")), + None => format!("impl@{owner}"), + }; + let label = self.numbered(label); + self.scan.impl_labels.insert(item, label); + self.pending.item = Some(item); + self.pending.owner = Some(trait_path); + } + + fn note_module(&mut self) { + let Some(name) = self.next_name() else { + return; + }; + if self.token(2).is_some_and(|after| after.is(";")) { + let item = ModItem { + name, + line: self.tokens[self.at].line, + test: self.in_test(), + inline: false, + scope: self.scope(), + }; + self.scan.mods.push(item); + } else { + self.pending.item = Some(self.new_item(&name, true)); + self.pending.module = Some(name); + } + } + + fn impl_owner(&self) -> (String, TraitPath) { + let mut depth = 0usize; + let mut owner = String::new(); + let mut segments = Vec::new(); + let mut trait_path = None; + for (index, token) in self.tokens.iter().enumerate().skip(self.at + 1) { + let after_dash = self.tokens[index - 1].is("-"); + if depth == 0 && (token.is("{") || token.is("where") || token.is(";")) { + break; + } + if token.is("<") || token.is("(") || token.is("[") { + depth += 1; + } else if (token.is(">") && !after_dash) || token.is(")") || token.is("]") { + depth = depth.saturating_sub(1); + } else if depth == 0 && token.is("for") { + trait_path = Some(std::mem::take(&mut segments)); + owner.clear(); + } else if depth == 0 && token.is_ident() && !NOT_NAMES.contains(&token.text.as_str()) { + owner = token.text.clone(); + segments.push(token.text.clone()); + } + } + (owner, trait_path) + } + + fn structure(&mut self) { + let tokens = self.tokens; + let token = &tokens[self.at]; + let depth = self.top().depth; + if token.is("{") { + self.open_frame(); + } else if token.is("}") { + self.close_frame(); + } else if token.is("(") || token.is("[") || self.opens_angle() { + if let Some(frame) = self.frames.last_mut() { + frame.depth += 1; + frame.generics += usize::from(token.is("<")); + } + } else if token.is(")") || token.is("]") || self.closes_angle() { + if let Some(frame) = self.frames.last_mut() { + frame.depth = frame.depth.saturating_sub(1); + frame.generics -= usize::from(token.is(">")); + frame.test_element = frame.test_element.filter(|element| *element <= frame.depth); + } + } else if token.is(";") && depth == 0 { + let pending = std::mem::take(&mut self.pending); + if let Some((bare, start)) = pending.function { + let test = pending.test || self.top().test; + let index = self.declare_fn(bare, start, test, pending.item); + self.scan.fns[index].end = token.line; + } + } else if token.is(",") { + self.end_element(depth); + } + } + + fn opens_angle(&self) -> bool { + self.tokens[self.at].is("<") && (self.top().generics > 0 || self.opens_generic_parameters()) + } + + fn closes_angle(&self) -> bool { + let arrow = + self.at > 0 && (self.tokens[self.at - 1].is("-") || self.tokens[self.at - 1].is("=")); + self.tokens[self.at].is(">") && self.top().generics > 0 && !arrow + } + + fn end_element(&mut self, depth: usize) { + if depth == 0 && self.pending.names_nothing() { + self.pending = Pending::default(); + } + if let Some(frame) = self.frames.last_mut() { + frame.test_element = frame.test_element.filter(|element| *element != depth); + } + } + + fn declare_fn(&mut self, bare: String, start: usize, test: bool, item: Option) -> usize { + let impl_trait = match &self.top().kind { + FrameKind::Owner(trait_path) => trait_path.clone(), + _ => None, + }; + let name = item.map_or(bare.clone(), |item| self.scan.item_names[item].clone()); + self.scan.fns.push(FnItem { + name, + bare, + impl_trait, + start, + end: start, + test, + }); + self.scan.fns.len() - 1 + } + + fn note_test_root(&mut self, item: usize, marked: bool) { + if marked && !self.top().test { + self.scan.test_roots.push(TestRoot { + name: self.scan.item_names[item].clone(), + line: self.tokens[self.at].line, + }); + } + } + + fn open_frame(&mut self) { + let pending = std::mem::take(&mut self.pending); + let test = pending.test || self.top().test || self.top().test_element.is_some(); + let item = pending.item.unwrap_or(self.top().item); + let mut scope = self.top().scope; + let kind = if let Some((bare, start)) = pending.function { + self.note_test_root(item, pending.test_case); + FrameKind::Function(self.declare_fn(bare, start, test, pending.item)) + } else if let Some(name) = pending.module { + self.note_test_root(item, pending.test); + self.scan.mods.push(ModItem { + name: name.clone(), + line: self.tokens[self.at].line, + test, + inline: true, + scope: self.scope(), + }); + let nested = [self.scope(), vec![name]].concat(); + self.scan.scopes.push(nested); + scope = self.scan.scopes.len() - 1; + FrameKind::Module + } else if let Some(trait_path) = pending.owner { + FrameKind::Owner(trait_path) + } else { + FrameKind::Block + }; + self.frames.push(Frame { + kind, + test, + test_element: None, + item, + scope, + depth: 0, + generics: 0, + }); + } + + fn close_frame(&mut self) { + if self.frames.len() > 1 { + if let Some(frame) = self.frames.pop() { + if let FrameKind::Function(index) = frame.kind { + self.scan.fns[index].end = self.tokens[self.at].line; + } + } + } + self.pending = Pending::default(); + } +} diff --git a/main/opengeometry/tests/source_rules/serde_json_outside_edges.txt b/main/opengeometry/tests/source_rules/serde_json_outside_edges.txt new file mode 100644 index 0000000..9510bcb --- /dev/null +++ b/main/opengeometry/tests/source_rules/serde_json_outside_edges.txt @@ -0,0 +1,3 @@ +world_graph/modifying.rs WorldGraph::operate B5 +world_graph/modifying.rs WorldGraph::report B5 +world_graph/shape_store.rs Shape B5 diff --git a/main/opengeometry/tests/source_rules/sibling_cycles.txt b/main/opengeometry/tests/source_rules/sibling_cycles.txt new file mode 100644 index 0000000..e69de29 diff --git a/main/opengeometry/tests/source_rules/source_tree.rs b/main/opengeometry/tests/source_rules/source_tree.rs new file mode 100644 index 0000000..42e1de9 --- /dev/null +++ b/main/opengeometry/tests/source_rules/source_tree.rs @@ -0,0 +1,190 @@ +use crate::lexer::{tokenize, Token}; +use crate::scanner::{scan_file, FileScan}; +use std::fs; +use std::path::{Path, PathBuf}; + +pub(crate) struct SourceFile { + pub(crate) path: String, + pub(crate) module: Vec, + pub(crate) line_count: usize, + pub(crate) comments: Vec, + pub(crate) code: Vec, + pub(crate) scan: FileScan, + pub(crate) test: bool, +} + +impl SourceFile { + pub(crate) fn is_test_at(&self, index: usize) -> bool { + self.test || self.scan.contexts[index].test + } + + pub(crate) fn directory(&self) -> &str { + self.path + .rsplit_once('/') + .map_or("", |(directory, _)| directory) + } + + pub(crate) fn stem(&self) -> &str { + let name = self + .path + .rsplit_once('/') + .map_or(self.path.as_str(), |(_, name)| name); + name.strip_suffix(".rs").unwrap_or(name) + } +} + +pub(crate) struct SourceTree { + pub(crate) files: Vec, + pub(crate) directories: Vec, +} + +#[derive(Debug)] +pub(crate) struct LoadError { + pub(crate) path: PathBuf, + pub(crate) reason: String, +} + +impl SourceTree { + pub(crate) fn load(root: &Path) -> Result { + let mut paths = Vec::new(); + let mut directories = Vec::new(); + collect(root, "", &mut paths, &mut directories)?; + let files = paths + .into_iter() + .map(|path| load_file(root, path)) + .collect::, _>>()?; + let mut tree = SourceTree { files, directories }; + mark_test_files(&mut tree); + Ok(tree) + } + + pub(crate) fn from_sources(sources: &[(&str, &str)]) -> SourceTree { + let mut sources = sources.to_vec(); + sources.sort(); + let files = sources + .iter() + .map(|(path, text)| { + parse_file(path.to_string(), text) + .unwrap_or_else(|reason| panic!("{path}: {reason}")) + }) + .collect(); + let mut directories: Vec = sources + .iter() + .flat_map(|(path, _)| { + path.match_indices('/') + .map(|(end, _)| path[..end].to_string()) + .collect::>() + }) + .collect(); + directories.sort(); + directories.dedup(); + let mut tree = SourceTree { files, directories }; + mark_test_files(&mut tree); + tree + } + + pub(crate) fn module_index(&self, module: &[String]) -> Option { + self.files.iter().position(|file| file.module == module) + } + + pub(crate) fn has_file(&self, path: &str) -> bool { + self.files.iter().any(|file| file.path == path) + } +} + +fn read_error(path: &Path, error: &std::io::Error) -> LoadError { + LoadError { + path: path.to_path_buf(), + reason: error.to_string(), + } +} + +fn collect( + root: &Path, + relative: &str, + files: &mut Vec, + directories: &mut Vec, +) -> Result<(), LoadError> { + let directory = root.join(relative); + let mut entries = fs::read_dir(&directory) + .map_err(|error| read_error(&directory, &error))? + .map(|entry| entry.map(|entry| entry.path())) + .collect::, _>>() + .map_err(|error| read_error(&directory, &error))?; + entries.sort(); + for entry in entries { + let name = entry + .file_name() + .map_or(String::new(), |name| name.to_string_lossy().into_owned()); + let child = if relative.is_empty() { + name.clone() + } else { + format!("{relative}/{name}") + }; + if entry.is_dir() { + directories.push(child.clone()); + collect(root, &child, files, directories)?; + } else if name.ends_with(".rs") { + files.push(child); + } + } + Ok(()) +} + +fn module_path(relative: &str) -> Vec { + let without_extension = relative.strip_suffix(".rs").unwrap_or(relative); + let mut module: Vec = without_extension.split('/').map(str::to_string).collect(); + if module.last().is_some_and(|last| last == "mod") || module == ["lib"] { + module.pop(); + } + module +} + +fn load_file(root: &Path, path: String) -> Result { + let absolute = root.join(&path); + let text = fs::read_to_string(&absolute).map_err(|error| read_error(&absolute, &error))?; + parse_file(path, &text).map_err(|reason| LoadError { + path: absolute, + reason, + }) +} + +fn parse_file(path: String, text: &str) -> Result { + let tokens = + tokenize(text).map_err(|error| format!("line {}: {}", error.line, error.reason))?; + let (comments, code): (Vec, Vec) = + tokens.into_iter().partition(Token::is_comment); + let scan = scan_file(&code); + Ok(SourceFile { + module: module_path(&path), + line_count: text.lines().count(), + path, + comments, + code, + scan, + test: false, + }) +} + +fn declared_as_test(parent: &SourceFile, name: &str) -> bool { + parent.scan.mods.iter().any(|declaration| { + declaration.name == name && declaration.scope.is_empty() && declaration.test + }) +} + +fn mark_test_files(tree: &mut SourceTree) { + let mut order: Vec = (0..tree.files.len()).collect(); + order.sort_by_key(|&index| tree.files[index].module.len()); + for index in order { + let module = tree.files[index].module.clone(); + let Some((name, parent_module)) = module.split_last() else { + continue; + }; + let parent = tree + .module_index(parent_module) + .map(|parent| &tree.files[parent]); + let test = module.iter().any(|segment| segment == "tests") + || parent.is_some_and(|parent| parent.test || declared_as_test(parent, name)); + tree.files[index].test = test; + } +} diff --git a/main/opengeometry/tests/source_rules/string_errors.txt b/main/opengeometry/tests/source_rules/string_errors.txt new file mode 100644 index 0000000..e69de29 diff --git a/main/opengeometry/tests/source_rules/test_rules.rs b/main/opengeometry/tests/source_rules/test_rules.rs new file mode 100644 index 0000000..0aa5bf4 --- /dev/null +++ b/main/opengeometry/tests/source_rules/test_rules.rs @@ -0,0 +1,59 @@ +use crate::exceptions::{enforce, Violation}; +use crate::frozen_lists::INLINE_TESTS; +use crate::source_tree::{SourceFile, SourceTree}; +use crate::{enforce_on_kernel, kernel_sources}; + +const TEST_MODULE: &str = "tests"; + +fn is_test_location(file: &SourceFile) -> bool { + file.module.iter().any(|segment| segment == TEST_MODULE) +} + +fn inline_test_violations(tree: &SourceTree) -> Vec { + let mut violations = Vec::new(); + for file in tree.files.iter().filter(|file| !is_test_location(file)) { + for root in &file.scan.test_roots { + let detail = format!( + "test code `{}` sits inline; tests live in a sibling tests.rs or tests/", + root.name + ); + let key = [file.path.as_str(), root.name.as_str()]; + violations.push(Violation::new(&file.path, root.line, &key, detail)); + } + } + violations +} + +fn ungated_test_module_violations(tree: &SourceTree) -> Vec { + let mut violations = Vec::new(); + for file in tree.files.iter().filter(|file| !file.test) { + for declaration in &file.scan.mods { + if declaration.name != TEST_MODULE || declaration.test { + continue; + } + let detail = "`mod tests` is declared without `#[cfg(test)]`"; + violations.push(Violation::new( + &file.path, + declaration.line, + &[&file.path], + detail, + )); + } + } + violations +} + +#[test] +fn tests_live_in_a_sibling_tests_rs_or_tests_directory() { + enforce_on_kernel( + "tests live in a sibling tests.rs or tests/", + inline_test_violations, + Some(&INLINE_TESTS), + ); +} + +#[test] +fn every_tests_module_is_declared_under_cfg_test() { + let violations = ungated_test_module_violations(&kernel_sources()); + enforce("mod tests only under #[cfg(test)]", &violations, None); +} diff --git a/main/opengeometry/tests/source_rules/text_rules.rs b/main/opengeometry/tests/source_rules/text_rules.rs new file mode 100644 index 0000000..b994303 --- /dev/null +++ b/main/opengeometry/tests/source_rules/text_rules.rs @@ -0,0 +1,64 @@ +use crate::exceptions::{enforce, Violation}; +use crate::source_tree::SourceTree; +use crate::{kernel_sources, rule_violations}; + +const MAX_FILE_LINES: usize = 600; + +fn comment_violations(tree: &SourceTree) -> Vec { + let mut violations = Vec::new(); + for file in &tree.files { + for comment in &file.comments { + let preview: String = comment.text.chars().take(40).collect(); + let detail = format!("comment `{preview}`"); + violations.push(Violation::new( + &file.path, + comment.line, + &[&file.path], + detail, + )); + } + } + violations +} + +fn oversized_file_violations(tree: &SourceTree) -> Vec { + tree.files + .iter() + .filter(|file| file.line_count > MAX_FILE_LINES) + .map(|file| { + let detail = format!("{} lines, above {MAX_FILE_LINES}", file.line_count); + Violation::new(&file.path, file.line_count, &[&file.path], detail) + }) + .collect() +} + +#[test] +fn source_files_contain_no_comments() { + enforce( + "zero comments", + &comment_violations(&kernel_sources()), + None, + ); +} + +#[test] +fn source_files_are_at_most_600_lines() { + let violations = oversized_file_violations(&kernel_sources()); + enforce("every file at most 600 lines", &violations, None); +} + +#[test] +fn source_rules_files_contain_no_comments() { + let violations = rule_violations(comment_violations); + enforce("zero comments in source_rules", &violations, None); +} + +#[test] +fn source_rules_files_are_at_most_600_lines() { + let violations = rule_violations(oversized_file_violations); + enforce( + "every source_rules file at most 600 lines", + &violations, + None, + ); +} diff --git a/main/opengeometry/tests/source_rules/type_rules.rs b/main/opengeometry/tests/source_rules/type_rules.rs new file mode 100644 index 0000000..df02d0a --- /dev/null +++ b/main/opengeometry/tests/source_rules/type_rules.rs @@ -0,0 +1,468 @@ +use crate::enforce_on_kernel; +use crate::exceptions::Violation; +use crate::frozen_lists::{JSON_VALUES_IN_SIGNATURES, SERDE_JSON_OUTSIDE_EDGES, STRING_ERRORS}; +use crate::lexer::Token; +use crate::path_aliases::{Exports, PathFinder, STD_CRATES}; +use crate::source_tree::{SourceFile, SourceTree}; +use std::collections::BTreeSet; + +const JSON_EDGE_FILES: [&str; 4] = [ + "brep/topology/envelope.rs", + "world_graph/queries.rs", + "tessellation/snapshot_store.rs", + "tessellation/cache.rs", +]; +const BINDINGS: &str = "bindings/"; +const STRING_CRATES: [&str; 2] = ["std", "alloc"]; +const ERROR_TYPE_NAMES: [&str; 2] = ["Err", "Error"]; +const BINDING_OPENERS: [&str; 2] = ["<", ","]; +const BOXED: &str = "Box"; +const STR: &str = "str"; + +const DECLARATIONS: [&str; 5] = ["fn", "struct", "enum", "union", "type"]; + +fn item_label(file: &SourceFile, index: usize) -> String { + let context = file.scan.contexts[index]; + let name = file.scan.item_name(index); + if let Some(item) = context.use_item { + file.scan.uses[item].label.clone() + } else if let Some(label) = file.scan.impl_labels.get(&context.item) { + label.clone() + } else if name.is_empty() { + "file".to_string() + } else { + name.to_string() + } +} + +fn once_per_item(file: &SourceFile, index: usize, seen: &mut BTreeSet) -> Option { + let item = item_label(file, index); + seen.insert(item.clone()).then_some(item) +} + +fn closing_index(code: &[Token], open: usize) -> usize { + let mut depth = 0usize; + for (index, token) in code.iter().enumerate().skip(open) { + let arrow = index > 0 && (code[index - 1].is("-") || code[index - 1].is("=")); + if token.is("<") || token.is("(") || token.is("[") || token.is("{") { + depth += 1; + } else if (token.is(">") && !arrow) || token.is(")") || token.is("]") || token.is("}") { + depth = depth.saturating_sub(1); + if depth == 0 { + return index; + } + } + } + code.len() - 1 +} + +fn last_type_argument(code: &[Token], open: usize) -> Vec<&str> { + let mut close = closing_index(code, open); + if code[close - 1].is(",") { + close -= 1; + } + let mut depth = 0usize; + let mut start = open + 1; + for index in open + 1..close { + let token = &code[index]; + let arrow = code[index - 1].is("-") || code[index - 1].is("="); + if token.is("<") || token.is("(") || token.is("[") { + depth += 1; + } else if (token.is(">") && !arrow) || token.is(")") || token.is("]") { + depth = depth.saturating_sub(1); + } else if token.is(",") && depth == 0 { + start = index + 1; + } + } + code[start..close] + .iter() + .map(|token| token.text.as_str()) + .collect() +} + +fn string_type(path: &[String]) -> Option<&'static str> { + let qualified = matches!(path, [root, module, name] + if STRING_CRATES.contains(&root.as_str()) && module == "string" && name == "String"); + (qualified || path == ["String"]).then_some("String") +} + +fn result_type(path: &[String]) -> Option<&'static str> { + let qualified = matches!(path, [root, module, name] + if STD_CRATES.contains(&root.as_str()) && module == "result" && name == "Result"); + (qualified || path == ["Result"]).then_some("Result") +} + +fn names_string(finder: &PathFinder, error: &[&str], scope: &[String]) -> bool { + let error = error.strip_prefix(&["::"]).unwrap_or(error); + let segments: Vec = error + .iter() + .step_by(2) + .map(|segment| (*segment).to_string()) + .collect(); + let is_path = error + .iter() + .skip(1) + .step_by(2) + .all(|separator| *separator == "::"); + is_path && !segments.is_empty() && finder.path_target(&segments, scope).is_some() +} + +fn unboxed<'a>(error: &'a [&'a str]) -> &'a [&'a str] { + match error.iter().position(|token| *token == "<") { + Some(open) if open > 0 && error[open - 1] == BOXED && error.last() == Some(&">") => { + &error[open + 1..error.len() - 1] + } + _ => error, + } +} + +fn referent<'a>(error: &'a [&'a str]) -> &'a [&'a str] { + let Some(rest) = error.strip_prefix(&["&"]) else { + return error; + }; + match rest { + [lifetime, after @ ..] if lifetime.starts_with('\'') => after, + _ => rest, + } +} + +fn text_error_kind(finder: &PathFinder, error: &[&str], scope: &[String]) -> Option<&'static str> { + let text = referent(unboxed(error)); + if text == [STR] { + return Some(STR); + } + names_string(finder, text, scope).then_some("String") +} + +fn names_error_type(name: &str) -> bool { + name.starts_with(|first: char| first == '_' || first.is_alphabetic()) +} + +fn result_error<'a>( + code: &'a [Token], + result_finder: &PathFinder, + index: usize, +) -> Option> { + let generic = code[index + 1].is("<"); + if !generic || !(code[index].is("Result") || result_finder.target_at(index).is_some()) { + return None; + } + Some(last_type_argument(code, index + 1)) +} + +fn error_type_names(tree: &SourceTree, results: &Exports) -> BTreeSet { + let mut names: BTreeSet = ERROR_TYPE_NAMES + .iter() + .map(|name| name.to_string()) + .collect(); + for file in &tree.files { + let result_finder = PathFinder::new(file, result_type, results); + for index in 0..file.code.len().saturating_sub(1) { + let error = result_error(&file.code, &result_finder, index).unwrap_or_default(); + if let Some(name) = error.last().filter(|name| names_error_type(name)) { + names.insert((*name).to_string()); + } + } + } + names +} + +fn binding_value(code: &[Token], start: usize) -> Vec<&str> { + let mut depth = 0usize; + let mut end = start; + while let Some(token) = code.get(end) { + let arrow = code[end - 1].is("-") || code[end - 1].is("="); + if token.is("<") || token.is("(") || token.is("[") { + depth += 1; + } else if (token.is(">") && !arrow) || token.is(")") || token.is("]") { + if depth == 0 { + break; + } + depth -= 1; + } else if token.is(",") && depth == 0 { + break; + } + end += 1; + } + code[start..end] + .iter() + .map(|token| token.text.as_str()) + .collect() +} + +fn error_binding_value<'a>( + code: &'a [Token], + index: usize, + names: &BTreeSet, +) -> Option<(&'a str, Vec<&'a str>)> { + let opened = index + .checked_sub(1) + .is_some_and(|before| BINDING_OPENERS.iter().any(|opener| code[before].is(opener))); + let assigned = code.get(index + 1).is_some_and(|next| next.is("=")) + && !code + .get(index + 2) + .is_some_and(|value| value.is("=") || value.is(">")); + if !opened || !assigned || !names.contains(&code[index].text) { + return None; + } + Some((code[index].text.as_str(), binding_value(code, index + 2))) +} + +fn error_type_value<'a>( + code: &'a [Token], + index: usize, + names: &BTreeSet, +) -> Option<(&'a str, Vec<&'a str>)> { + let name = code.get(index + 1)?; + if !code[index].is("type") || !names.contains(&name.text) { + return None; + } + let end = (index..code.len()).find(|&at| code[at].is(";"))?; + let equals = (index..end).find(|&at| code[at].is("="))?; + let value = code[equals + 1..end] + .iter() + .map(|token| token.text.as_str()) + .collect(); + Some((name.text.as_str(), value)) +} + +fn string_error_at( + code: &[Token], + finder: &PathFinder, + result_finder: &PathFinder, + names: &BTreeSet, + index: usize, +) -> Option { + let scope = finder.scope_at(index); + if let Some((name, value)) = error_type_value(code, index, names) { + let kind = text_error_kind(finder, &value, &scope)?; + return Some(format!("`type {name} = {kind}`")); + } + if let Some((name, value)) = error_binding_value(code, index, names) { + let kind = text_error_kind(finder, &value, &scope)?; + return Some(format!("`{name} = {kind}` binding")); + } + let error = result_error(code, result_finder, index)?; + let kind = text_error_kind(finder, &error, &scope)?; + Some(format!("`Result<…, {kind}>`")) +} + +fn string_error_violations(tree: &SourceTree) -> Vec { + let (strings, results) = ( + Exports::build(tree, string_type), + Exports::build(tree, result_type), + ); + let names = error_type_names(tree, &results); + let mut violations = Vec::new(); + for file in &tree.files { + let finder = PathFinder::new(file, string_type, &strings); + let result_finder = PathFinder::new(file, result_type, &results); + let mut seen = BTreeSet::new(); + for index in 0..file.code.len().saturating_sub(1) { + let Some(form) = string_error_at(&file.code, &finder, &result_finder, &names, index) + else { + continue; + }; + if let Some(item) = once_per_item(file, index, &mut seen) { + let detail = format!("{form} in `{item}`"); + let line = file.code[index].line; + violations.push(Violation::new( + &file.path, + line, + &[&file.path, &item], + detail, + )); + } + } + } + violations +} + +fn is_json_edge(path: &str) -> bool { + path.starts_with(BINDINGS) || JSON_EDGE_FILES.contains(&path) +} + +fn serde_json_item(path: &[String]) -> Option<&'static str> { + path.first() + .is_some_and(|head| head == "serde_json") + .then_some("serde_json") +} + +fn only_through_other_files(_: &[String]) -> Option<&'static str> { + None +} + +fn serde_json_violations(tree: &SourceTree) -> Vec { + let exports = Exports::build(tree, serde_json_item); + let mut violations = Vec::new(); + for file in tree.files.iter().filter(|file| !is_json_edge(&file.path)) { + let reexported = PathFinder::new(file, only_through_other_files, &exports); + let mut seen = BTreeSet::new(); + for (index, token) in file.code.iter().enumerate() { + let named = token.is("serde_json") || reexported.target_at(index).is_some(); + if !named || file.is_test_at(index) { + continue; + } + if let Some(item) = once_per_item(file, index, &mut seen) { + let detail = format!("serde_json in `{item}` outside the JSON edges"); + violations.push(Violation::new( + &file.path, + token.line, + &[&file.path, &item], + detail, + )); + } + } + } + violations +} + +fn json_value(path: &[String]) -> Option<&'static str> { + let crate_path = path.first().is_some_and(|head| head == "serde_json"); + (crate_path && path.iter().any(|segment| segment == "Value")).then_some("serde_json::Value") +} + +fn declaration_end(code: &[Token], start: usize) -> usize { + let function = code[start].is("fn"); + let mut index = start; + while index < code.len() { + let token = &code[index]; + if token.is(";") { + return index; + } + if token.is("{") { + return if function { + index + } else { + closing_index(code, index) + }; + } + if token.is("(") && !function { + return closing_index(code, index); + } + if token.is("(") || token.is("<") || token.is("[") { + index = closing_index(code, index); + } + index += 1; + } + code.len() +} + +fn json_value_violations(tree: &SourceTree) -> Vec { + let exports = Exports::build(tree, json_value); + let mut violations = Vec::new(); + for file in tree + .files + .iter() + .filter(|file| !file.path.starts_with(BINDINGS)) + { + let finder = PathFinder::new(file, json_value, &exports); + for (index, token) in file.code.iter().enumerate() { + let declares = DECLARATIONS.iter().any(|keyword| token.is(keyword)); + let named = file.code.get(index + 1).is_some_and(Token::is_ident); + if !declares || !named || file.is_test_at(index) { + continue; + } + let end = declaration_end(&file.code, index); + if (index..end).any(|at| finder.target_at(at).is_some()) { + let item = item_label(file, index); + let detail = format!("serde_json::Value in the fields or signature of `{item}`"); + violations.push(Violation::new( + &file.path, + token.line, + &[&file.path, &item], + detail, + )); + } + } + } + violations +} + +#[test] +fn no_result_uses_string_as_its_error() { + enforce_on_kernel( + "no String, &str or Box error type in a Result, an Err or Error type or binding, or an alias a Result names", + string_error_violations, + Some(&STRING_ERRORS), + ); +} + +#[test] +fn serde_json_appears_only_at_json_edges_and_in_tests() { + enforce_on_kernel( + "serde_json only at the JSON edges", + serde_json_violations, + Some(&SERDE_JSON_OUTSIDE_EDGES), + ); +} + +#[test] +fn json_values_stay_out_of_fields_and_signatures_outside_bindings() { + enforce_on_kernel( + "no serde_json::Value in fields or signatures outside bindings", + json_value_violations, + Some(&JSON_VALUES_IN_SIGNATURES), + ); +} + +fn items_in(check: fn(&SourceTree) -> Vec, source: &str) -> Vec { + check(&SourceTree::from_sources(&[("geom2d/mod.rs", source)])) + .into_iter() + .map(|violation| violation.key[1].clone()) + .collect() +} + +const ERROR_BINDINGS: &str = "fn parse>(text: &str) -> Option {\n None\n}\n\ + fn convert(value: u8) -> Option\nwhere\n T: TryFrom,\n{\n None\n}\n\ + impl> Parsed for Wrapper {}\n\ + fn count>(items: I) -> usize {\n items.count()\n}\n"; + +#[test] +fn string_error_rule_catches_error_bindings_in_bounds_and_impls() { + assert_eq!( + items_in(string_error_violations, ERROR_BINDINGS), + ["parse", "convert", "impl@Wrapper:Parsed"] + ); +} + +const STR_ERRORS: &str = "fn parse(text: &str) -> Result {\n Err(text)\n}\n\ + fn build() -> Result<(), Box> {\n Ok(())\n}\n\ + struct Reader;\nimpl FromStr for Reader {\n type Err = &'static str;\n}\n"; + +#[test] +fn string_error_rule_catches_str_and_boxed_str_errors() { + assert_eq!( + items_in(string_error_violations, STR_ERRORS), + ["parse", "build", "Reader::Err"] + ); +} + +const ALIASED_ERROR: &str = + "trait Parse {\n type Failure;\n fn parse(&self) -> Result;\n}\n\ + impl Parse for Text {\n type Failure = String;\n}\n\ + impl Iterator for Text {\n type Item = String;\n}\n"; + +#[test] +fn string_error_rule_catches_a_string_alias_used_as_an_error_type() { + assert_eq!( + items_in(string_error_violations, ALIASED_ERROR), + ["Text::Failure"] + ); +} + +const CONST_IN_FN: &str = "fn encode(data: &[u8]) -> usize {\n if data.as_ptr() as *const u8 == PTR {\n\ + serde_json::to_string(&0);\n }\n const EMPTY: Option = None;\n 0\n}\n"; + +#[test] +fn serde_json_rule_keys_statements_with_const_to_their_fn() { + assert_eq!(items_in(serde_json_violations, CONST_IN_FN), ["encode"]); +} + +const POINTER_FIELD: &str = + "struct Probe {\n #[cfg(test)]\n origin: *const u8,\n value: serde_json::Value,\n}\n"; + +#[test] +fn serde_json_rule_sees_a_field_after_a_cfg_test_pointer_field() { + assert_eq!(items_in(serde_json_violations, POINTER_FIELD), ["Probe"]); +} diff --git a/main/opengeometry/tests/source_rules/upward_imports.txt b/main/opengeometry/tests/source_rules/upward_imports.txt new file mode 100644 index 0000000..e69de29 diff --git a/main/opengeometry/tests/source_rules/use_tree.rs b/main/opengeometry/tests/source_rules/use_tree.rs new file mode 100644 index 0000000..0e85b92 --- /dev/null +++ b/main/opengeometry/tests/source_rules/use_tree.rs @@ -0,0 +1,88 @@ +use crate::lexer::Token; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) struct UsePath { + pub(crate) segments: Vec, + pub(crate) binding: Option, +} + +impl UsePath { + pub(crate) fn text(&self) -> String { + self.segments.join("::") + } + + pub(crate) fn is_super_glob(&self) -> bool { + let segments = match self.segments.split_first() { + Some((head, rest)) if head == "self" => rest, + _ => &self.segments, + }; + segments.last().is_some_and(|last| last == "*") + && segments.len() > 1 + && segments[..segments.len() - 1] + .iter() + .all(|segment| segment == "super") + } +} + +pub(crate) fn expand_use_tree(tokens: &[&Token]) -> Vec { + let mut paths = Vec::new(); + let mut at = 0; + expand_subtree(tokens, &mut at, &[], &mut paths); + paths +} + +fn expand_subtree(tokens: &[&Token], at: &mut usize, prefix: &[String], out: &mut Vec) { + let mut segments = prefix.to_vec(); + while let Some(token) = tokens.get(*at) { + *at += 1; + if token.is("::") { + continue; + } + if token.is("{") { + expand_group(tokens, at, &segments, out); + return; + } + if token.is("*") { + segments.push("*".into()); + out.push(UsePath { + segments, + binding: None, + }); + return; + } + segments.push(token.text.clone()); + if tokens.get(*at).is_some_and(|next| next.is("::")) { + continue; + } + let alias = aliased_name(tokens, at); + if segments.len() > 1 && segments.last().is_some_and(|last| last == "self") { + segments.pop(); + } + let binding = alias.or_else(|| segments.last().cloned()); + out.push(UsePath { segments, binding }); + return; + } +} + +fn expand_group(tokens: &[&Token], at: &mut usize, prefix: &[String], out: &mut Vec) { + while let Some(token) = tokens.get(*at) { + if token.is("}") { + *at += 1; + return; + } + if token.is(",") { + *at += 1; + continue; + } + expand_subtree(tokens, at, prefix, out); + } +} + +fn aliased_name(tokens: &[&Token], at: &mut usize) -> Option { + if !tokens.get(*at).is_some_and(|token| token.is("as")) { + return None; + } + let alias = tokens.get(*at + 1).map(|token| token.text.clone()); + *at += 2; + alias +} diff --git a/main/opengeometry/tests/step_oracle/acceptance.rs b/main/opengeometry/tests/step_oracle/acceptance.rs new file mode 100644 index 0000000..2cdcb1a --- /dev/null +++ b/main/opengeometry/tests/step_oracle/acceptance.rs @@ -0,0 +1,142 @@ +use crate::support::export_matched; +use opengeometry::world_graph::StepOptions; +use opengeometry_test_support::part21::Document; +use opengeometry_test_support::scenes::acceptance::{acceptance_scene, level_export}; +use opengeometry_test_support::tolerance::ordered_bits; +use serde_json::Value; + +type Extents = [f64; 6]; + +#[test] +fn acceptance_level_passes_the_oracle_in_metre_and_millimetre_with_y_and_z_up() { + let scene = acceptance_scene(); + let mut products = vec!["wall-1".to_string()]; + products.extend((1..=11).map(|index| format!("rail-{index}"))); + let quoted: Vec = products.iter().map(|og_id| format!("'{og_id}'")).collect(); + for unit in ["metre", "millimetre"] { + for up_axis in ["Y", "Z"] { + let exported = export_matched(&scene.graph, &[&scene.level], &options(unit, up_axis)); + assert_eq!( + og_ids(&exported.report["bodies"]), + products, + "{unit} {up_axis}" + ); + assert_eq!( + og_ids(&exported.report["skipped"]), + ["wall-profile", "rail-path", "rail-disc"], + "{unit} {up_axis}" + ); + assert_eq!( + exported.document.product_names().unwrap(), + quoted, + "{unit} {up_axis}" + ); + } + } +} + +#[test] +fn acceptance_rails_each_parse_pcurveless_edges() { + let scene = acceptance_scene(); + let (text, _) = level_export(&scene, "millimetre", "Z"); + let document = Document::parse(&text).unwrap(); + let counts = document.product_counts().unwrap(); + let names = document.product_names().unwrap(); + let rails: Vec = counts + .iter() + .zip(&names) + .filter(|(_, name)| name.starts_with("'rail-")) + .map(|(product, _)| product.pcurveless_edges) + .collect(); + assert_eq!(rails.len(), 11, "{names:?}"); + assert!(rails.iter().all(|edges| *edges > 0), "{rails:?}"); + let total: usize = counts.iter().map(|product| product.pcurveless_edges).sum(); + assert_eq!(total, document.pcurveless_edges().unwrap()); +} + +#[test] +fn acceptance_z_up_millimetre_extents_are_the_y_up_metre_extents_mapped() { + let scene = acceptance_scene(); + let (z_up, report) = level_export(&scene, "millimetre", "Z"); + let (y_up, _) = level_export(&scene, "metre", "Y"); + let z_up = vertex_extents(&Document::parse(&z_up).unwrap()); + let y_up = vertex_extents(&Document::parse(&y_up).unwrap()); + assert_within_one_ulp(z_up, mapped(y_up)); + let hull = report + .bodies + .iter() + .map(|body| mapped(scene.graph.bounds(&body.og_id).unwrap().unwrap())) + .reduce(union) + .unwrap(); + assert_inside(z_up, hull); + let wall = export_matched(&scene.graph, &[&scene.wall], &StepOptions::default()); + assert_within_one_ulp( + vertex_extents(&wall.document), + [-3000.0, -100.0, 3000.0, 3000.0, 100.0, 7000.0], + ); +} + +fn options(unit: &str, up_axis: &str) -> StepOptions { + StepOptions { + unit: unit.into(), + up_axis: up_axis.into(), + ..StepOptions::default() + } +} + +fn og_ids(items: &Value) -> Vec { + items + .as_array() + .unwrap() + .iter() + .map(|item| item["ogId"].as_str().unwrap().to_string()) + .collect() +} + +fn vertex_extents(document: &Document) -> Extents { + let points = document.vertex_positions().unwrap(); + assert!(!points.is_empty()); + points + .iter() + .map(|point| [point[0], point[1], point[2], point[0], point[1], point[2]]) + .reduce(union) + .unwrap() +} + +fn union(left: Extents, right: Extents) -> Extents { + [ + left[0].min(right[0]), + left[1].min(right[1]), + left[2].min(right[2]), + left[3].max(right[3]), + left[4].max(right[4]), + left[5].max(right[5]), + ] +} + +fn mapped(metres_y_up: Extents) -> Extents { + let [xmin, ymin, zmin, xmax, ymax, zmax] = metres_y_up; + [xmin, -zmax, ymin, xmax, -zmin, ymax].map(|value| value * 1000.0) +} + +fn assert_within_one_ulp(actual: Extents, expected: Extents) { + let close = actual + .iter() + .zip(&expected) + .all(|(a, b)| ordered_bits(*a).abs_diff(ordered_bits(*b)) <= 1); + assert!( + close, + "extents {actual:?} differ from {expected:?} by more than 1 ULP" + ); +} + +fn assert_inside(extents: Extents, hull: Extents) { + let inside = (0..3).all(|axis| { + ordered_bits(extents[axis]) + 1 >= ordered_bits(hull[axis]) + && ordered_bits(extents[axis + 3]) <= ordered_bits(hull[axis + 3]) + 1 + }); + assert!( + inside, + "extents {extents:?} leave the exported bodies' hull {hull:?}" + ); +} diff --git a/main/opengeometry/tests/step_oracle/fixtures.rs b/main/opengeometry/tests/step_oracle/fixtures.rs new file mode 100644 index 0000000..675ac27 --- /dev/null +++ b/main/opengeometry/tests/step_oracle/fixtures.rs @@ -0,0 +1,313 @@ +use crate::support::{fixture_names, fixture_text, parse_and_match, unit_scale}; +use opengeometry::{ + brep::{BrepEnvelope, GeometryError}, + exchange::export_step, +}; +use opengeometry_test_support::part21::{ + normalise_step, pcurve_expectations, references, Document, +}; +use opengeometry_test_support::stored; +use serde_json::Value; +use std::slice::from_ref; + +const UNITS: [(&str, &str); 2] = [("metre", "m"), ("millimetre", "mm")]; + +const UNEXPORTABLE_SOLID: &str = + "analytic analytic exchange export requires nonempty closed solid regions without wires"; + +struct ConePcurve { + point: usize, + point_line: String, + pair: [u64; 4], +} + +fn single_body_fixtures() -> Vec<(String, BrepEnvelope)> { + fixture_names("") + .into_iter() + .filter_map(|file| { + let name = file.strip_suffix(".brep.json")?.to_string(); + let body = BrepEnvelope::from_json(&fixture_text(&file).unwrap()).unwrap(); + Some((name, body)) + }) + .collect() +} + +fn named_lines(text: &str) -> Vec<&str> { + text.lines() + .filter(|line| { + line.starts_with("FILE_NAME(") + || ["=PRODUCT(", "=MANIFOLD_SOLID_BREP(", "=BREP_WITH_VOIDS("] + .iter() + .any(|kind| line.contains(kind)) + }) + .collect() +} + +fn assert_report_fixture(report: &Value, stored: &str, label: &str) { + let stored: Value = serde_json::from_str(stored).unwrap(); + let (actual, stored) = (report.as_object().unwrap(), stored.as_object().unwrap()); + assert!(actual.keys().eq(stored.keys()), "{label} report keys"); + for (key, value) in actual { + if key != "validation_level" { + assert_eq!(value, &stored[key], "{label} report {key}"); + } + } +} + +fn checked_export(name: &str, unit: &str, body: &BrepEnvelope) -> (String, Value) { + let (text, report) = + export_step(body, unit).unwrap_or_else(|error| panic!("{name} {unit}: {error}")); + let report = serde_json::to_value(&report).unwrap(); + let parsed = parse_and_match(&text, &report, unit, from_ref(body)); + assert_eq!(parsed.count("POLY_LOOP"), 0, "{name} {unit}"); + assert!( + text.contains("\nFILE_DESCRIPTION(('OpenGeometry analytic BRep'),'2;1');\n"), + "{name} {unit}" + ); + (text, report) +} + +fn assert_export_matches_stored(name: &str, body: &BrepEnvelope) { + for (unit, suffix) in UNITS { + let stem = format!("{name}.step.{suffix}"); + let Some(stored) = fixture_text(&stem) else { + let error = fixture_text(&format!("{stem}.error.json")) + .unwrap_or_else(|| panic!("missing STEP oracle outcome for {name} {unit}")); + assert_eq!( + serde_json::to_value(export_step(body, unit).unwrap_err()).unwrap(), + serde_json::from_str::(&error).unwrap(), + "{name} {unit}" + ); + continue; + }; + let (text, report) = checked_export(name, unit, body); + assert_eq!(named_lines(&text), named_lines(&stored), "{name} {unit}"); + if stored::compared() { + assert_report_fixture( + &report, + &fixture_text(&format!("{stem}.report.json")).unwrap(), + &format!("{name} {unit}"), + ); + assert_eq!( + normalise_step(&text), + normalise_step(&stored), + "{name} {unit}" + ); + } + } +} + +#[test] +fn single_body_step_matches_stored_fixtures_in_both_units() { + let fixtures = single_body_fixtures(); + assert_eq!(fixtures.len(), 17); + for (name, body) in fixtures { + assert_export_matches_stored(&name, &body); + } +} + +fn matrix_bodies(matrix: &str, count: usize, skipped: &[&str]) -> Vec<(String, BrepEnvelope)> { + let cases = fixture_names(matrix) + .into_iter() + .filter(|name| name.ends_with(".json") && !skipped.iter().any(|part| name.contains(part))) + .collect::>(); + assert_eq!(cases.len(), count); + cases + .into_iter() + .filter_map(|case| { + let fixture: Value = + serde_json::from_str(&fixture_text(&format!("{matrix}/{case}")).unwrap()).unwrap(); + let body = serde_json::from_value(fixture["result"].get("brep")?.clone()).unwrap(); + Some((case.trim_end_matches(".json").to_string(), body)) + }) + .collect() +} + +fn exported_matrix_rows( + matrix: &str, + count: usize, + skipped: &[&str], + unexportable: &[&str], +) -> usize { + let mut exported = 0; + for (name, body) in matrix_bodies(matrix, count, skipped) { + if unexportable.contains(&name.as_str()) { + for (unit, _) in UNITS { + assert_eq!( + export_step(&body, unit).unwrap_err(), + GeometryError::UnsupportedGeometry(UNEXPORTABLE_SOLID.into()), + "{name} {unit}" + ); + } + continue; + } + for (unit, _) in UNITS { + checked_export(&name, unit, &body); + } + exported += 1; + } + exported +} + +#[test] +fn boolean_matrix_results_export_through_the_oracle_or_pin_an_error() { + assert_eq!( + exported_matrix_rows("boolean-matrix", 18, &[], &["cylinder-empty"]), + 14 + ); +} + +#[test] +fn batch_matrix_results_export_through_the_oracle() { + assert_eq!( + exported_matrix_rows("batch-matrix", 23, &[".fallback."], &[]), + 18 + ); +} + +fn cylinder_cross() -> BrepEnvelope { + let fixture: Value = + serde_json::from_str(&fixture_text("boolean-matrix/cylinder-cross.json").unwrap()).unwrap(); + serde_json::from_value(fixture["result"]["brep"].clone()).unwrap() +} + +#[test] +fn nonparallel_cylinder_step_has_fitted_curves_within_exchange_budget() { + let body = cylinder_cross(); + let (text, report) = export_step(&body, "metre").unwrap(); + assert!(text.contains("B_SPLINE_CURVE_WITH_KNOTS")); + assert!(report.exchange_error_bound <= 1e-6); + let report = serde_json::to_value(&report).unwrap(); + let parsed = parse_and_match(&text, &report, "metre", from_ref(&body)); + assert!(parsed.count("B_SPLINE_CURVE_WITH_KNOTS") > 0); + let direction = parsed.parameter_direction_report().unwrap(); + assert!(direction.assessed > 0); + assert_eq!(direction.assessed, direction.aligned + direction.reversed); +} + +#[test] +fn nonparallel_cylinder_step_size_stays_within_its_baseline() { + let baseline: Value = serde_json::from_str(include_str!( + "../../../scripts/bench/performance-baseline.json" + )) + .unwrap(); + let budget = baseline["sizes"]["nonparallelCylinderStepBytes"] + .as_u64() + .expect("sizes.nonparallelCylinderStepBytes in the performance baseline"); + let (text, _) = export_step(&cylinder_cross(), "metre").unwrap(); + let bytes = text.len() as u64; + assert!( + bytes * 4 <= budget * 5, + "{bytes} bytes exceed 1.25 x {budget}" + ); +} + +fn cone_pcurves(parsed: &Document) -> Vec { + let entity = |id: usize| parsed.entity(id).unwrap(); + let cone = (1..=parsed.entity_count()) + .find(|id| entity(*id).starts_with("CONICAL_SURFACE(")) + .unwrap(); + assert_eq!(entity(cone).split(',').nth(2), Some("0.")); + let points = parsed.points2().unwrap(); + let directions = parsed.directions2().unwrap(); + let mut pcurves = Vec::new(); + for id in 1..=parsed.entity_count() { + let expression = entity(id); + if !expression.starts_with("PCURVE(") || references(expression)[0] != cone { + continue; + } + let line = references(entity(references(expression)[1]))[0]; + let [point, vector] = references(entity(line))[..] else { + panic!("#{line} is not a pcurve LINE"); + }; + let direction = references(entity(vector))[0]; + let ([u, v], [du, dv]) = (points[&point], directions[&direction]); + pcurves.push(ConePcurve { + point, + point_line: format!("#{point}={};", entity(point)), + pair: [u, v, du, dv].map(f64::to_bits), + }); + } + pcurves +} + +fn cone_expectations(brep: &BrepEnvelope, unit: &str) -> Vec<[u64; 4]> { + pcurve_expectations(brep, unit_scale(unit)) + .into_iter() + .filter(|expectation| expectation.surface == "Cone") + .filter_map(|expectation| expectation.bits) + .collect() +} + +fn axial_rewrite(text: &str, pcurves: &[ConePcurve], semi_angle: f64) -> String { + let mut rewritten = text.to_string(); + for pcurve in pcurves { + let line = format!( + "#{}=CARTESIAN_POINT('',({:.17E},{:.17E}));", + pcurve.point, + f64::from_bits(pcurve.pair[0]), + f64::from_bits(pcurve.pair[1]) * semi_angle.cos() + ); + rewritten = rewritten.replace(&pcurve.point_line, &line); + } + rewritten +} + +#[test] +fn cone_pcurve_v_measures_distance_along_the_generator() { + let body: Value = serde_json::from_str(&fixture_text("cone.brep.json").unwrap()).unwrap(); + let brep = BrepEnvelope::from_json(&body.to_string()).unwrap(); + let semi_angle = body["geometry"]["surfaces"] + .as_array() + .unwrap() + .iter() + .find(|surface| surface["kind"] == "Cone") + .unwrap()["semi_angle"] + .as_f64() + .unwrap(); + for (unit, _) in UNITS { + let (text, report) = export_step(&brep, unit).unwrap(); + let report = serde_json::to_value(&report).unwrap(); + let parsed = parse_and_match(&text, &report, unit, from_ref(&brep)); + let pcurves = cone_pcurves(&parsed); + let mut actual = pcurves.iter().map(|pcurve| pcurve.pair).collect::>(); + let mut expected = cone_expectations(&brep, unit); + actual.sort(); + expected.sort(); + assert_eq!(actual, expected, "{unit}"); + let rewritten = axial_rewrite(&text, &pcurves, semi_angle); + assert_ne!(rewritten, text, "{unit}"); + match Document::parse(&rewritten) { + Ok(_) => panic!("{unit}: the axial-height pcurve parsed"), + Err(error) => assert!(error.contains("misses 3D curve"), "{unit}: {error}"), + } + } +} + +fn conic_uses(body: &BrepEnvelope) -> usize { + pcurve_expectations(body, 1.0) + .iter() + .filter(|expectation| expectation.curve == "Circle" || expectation.curve == "Ellipse") + .count() +} + +#[test] +fn conic_pcurves_are_assessed_for_parameter_direction() { + for (name, body) in single_body_fixtures() { + for (unit, suffix) in UNITS { + if fixture_text(&format!("{name}.step.{suffix}")).is_none() { + continue; + } + let (text, _) = export_step(&body, unit).unwrap(); + let direction = Document::parse(&text) + .and_then(|parsed| parsed.parameter_direction_report()) + .unwrap_or_else(|error| panic!("{name} {unit}: {error}")); + assert_eq!(direction.conic_assessed, conic_uses(&body), "{name} {unit}"); + assert_eq!( + direction.conic_assessed, + direction.conic_aligned + direction.conic_reversed, + "{name} {unit}" + ); + } + } +} diff --git a/main/opengeometry/tests/step_oracle/graph_stored_step.rs b/main/opengeometry/tests/step_oracle/graph_stored_step.rs new file mode 100644 index 0000000..d1725fc --- /dev/null +++ b/main/opengeometry/tests/step_oracle/graph_stored_step.rs @@ -0,0 +1,40 @@ +use crate::support::{add_cuboid, cylinder, export_matched, fixture_text}; +use opengeometry::world_graph::{EditScope, ModifyingOperation, StepOptions}; +use opengeometry_test_support::part21::normalise_step; +use opengeometry_test_support::stored; +use opengeometry_test_support::world_graph::graph; + +#[test] +fn graph_export_of_fixture_primitives_matches_stored_text_after_normalisation() { + let mut world = graph(); + cylinder(&mut world, "cylinder"); + add_cuboid(&mut world, "cuboid", [2.0, 3.0, 1.0], [1.0, 0.0, -0.5]); + add_cuboid(&mut world, "box-cut", [3.0; 3], [1.5, 0.0, -1.5]); + add_cuboid(&mut world, "box-cutter", [3.0; 3], [3.0, 0.5, -2.0]); + world + .operate( + "box-cut", + ModifyingOperation::Subtract, + &["box-cutter".into()], + EditScope::Node, + ) + .unwrap(); + for (unit, suffix) in [("metre", "m"), ("millimetre", "mm")] { + let options = StepOptions { + unit: unit.into(), + up_axis: "Y".into(), + ..StepOptions::default() + }; + for name in ["cylinder", "cuboid", "box-cut"] { + let exported = export_matched(&world, &[name], &options); + if stored::compared() { + let stored = fixture_text(&format!("{name}.step.{suffix}")).unwrap(); + assert_eq!( + normalise_step(&exported.text), + normalise_step(&stored), + "{name} {unit}" + ); + } + } + } +} diff --git a/main/opengeometry/tests/step_oracle/main.rs b/main/opengeometry/tests/step_oracle/main.rs new file mode 100644 index 0000000..749659c --- /dev/null +++ b/main/opengeometry/tests/step_oracle/main.rs @@ -0,0 +1,7 @@ +mod acceptance; +mod fixtures; +mod graph_stored_step; +mod membership; +mod placed; +mod rejections; +mod support; diff --git a/main/opengeometry/tests/step_oracle/membership.rs b/main/opengeometry/tests/step_oracle/membership.rs new file mode 100644 index 0000000..d4baa54 --- /dev/null +++ b/main/opengeometry/tests/step_oracle/membership.rs @@ -0,0 +1,132 @@ +use crate::support::{add_cuboid, cylinder, export_matched}; +use opengeometry::world_graph::{ + CopyOptions, EditScope, ModifyingOperation, StepOptions, Transform, +}; +use opengeometry_test_support::part21::SolidEntry; +use opengeometry_test_support::world_graph::graph; + +fn both_conventions() -> [StepOptions; 2] { + [ + StepOptions { + unit: "metre".into(), + up_axis: "Y".into(), + ..StepOptions::default() + }, + StepOptions::default(), + ] +} + +#[test] +fn z_up_export_values_equal_the_placed_brep_bit_for_bit() { + let mut world = graph(); + add_cuboid(&mut world, "box", [2.0, 2.0, 2.0], [2.0, 0.0, 2.0]); + cylinder(&mut world, "cylinder"); + for options in both_conventions() { + export_matched(&world, &["box", "cylinder"], &options); + } +} + +#[test] +fn rotated_and_scaled_export_values_equal_the_placed_brep_bit_for_bit() { + let mut world = graph(); + add_cuboid(&mut world, "box", [2.0, 3.0, 1.0], [0.0; 3]); + cylinder(&mut world, "cylinder"); + for og_id in ["box", "cylinder"] { + for transform in [ + Transform::Rotate { + axis: [1.0, 2.0, 3.0], + degrees: 30.0, + pivot: None, + }, + Transform::Scale { + factor: 2.5, + pivot: None, + }, + ] { + world.transform(og_id, transform).unwrap(); + } + } + for options in both_conventions() { + export_matched(&world, &["box", "cylinder"], &options); + } +} + +#[test] +fn instance_export_values_equal_each_instances_placed_brep_bit_for_bit() { + let mut world = graph(); + add_cuboid(&mut world, "wall", [2.0, 2.0, 2.0], [3.0, 0.0, 0.0]); + world + .instance( + "wall", + CopyOptions { + og_id: Some("wall-copy".into()), + ..CopyOptions::default() + }, + ) + .unwrap(); + for transform in [ + Transform::Rotate { + axis: [0.0, 1.0, 0.0], + degrees: 90.0, + pivot: None, + }, + Transform::Translate { + offset: [0.0, 0.0, 5.0], + }, + ] { + world.transform("wall-copy", transform).unwrap(); + } + for options in both_conventions() { + let exported = export_matched(&world, &["wall", "wall-copy"], &options); + assert_eq!(exported.report["products"], 2); + } +} + +#[test] +fn far_body_export_values_equal_the_placed_brep_bit_for_bit() { + let mut world = graph(); + cylinder(&mut world, "far"); + world + .transform( + "far", + Transform::Translate { + offset: [10_000.0, 0.0, 0.0], + }, + ) + .unwrap(); + for options in both_conventions() { + export_matched(&world, &["far"], &options); + } +} + +#[test] +fn cavity_solid_exports_brep_with_voids_and_plain_solid_exports_manifold() { + let mut world = graph(); + add_cuboid(&mut world, "host", [4.0, 4.0, 4.0], [0.0; 3]); + add_cuboid(&mut world, "inner", [1.0, 1.0, 1.0], [0.0, 1.5, 0.0]); + add_cuboid(&mut world, "plain", [1.0, 1.0, 1.0], [10.0, 0.0, 0.0]); + world + .operate( + "host", + ModifyingOperation::Subtract, + &["inner".into()], + EditScope::Node, + ) + .unwrap(); + for options in both_conventions() { + let exported = export_matched(&world, &["host", "plain"], &options); + assert_eq!( + exported.document.solids().unwrap(), + [ + SolidEntry { + voids: 1, + with_voids: true + }, + SolidEntry { + voids: 0, + with_voids: false + } + ] + ); + } +} diff --git a/main/opengeometry/tests/step_oracle/placed.rs b/main/opengeometry/tests/step_oracle/placed.rs new file mode 100644 index 0000000..c5303e7 --- /dev/null +++ b/main/opengeometry/tests/step_oracle/placed.rs @@ -0,0 +1,387 @@ +use crate::support::{export_matched, parse_and_match, placed_export_bodies, unit_scale}; +use opengeometry::world_graph::{ + CopyOptions, CreateOptions, CreatingOperation, ErrorCode, Primitive, StepOptions, Transform, + WorldGraph, +}; +use opengeometry_test_support::part21::bits; +use opengeometry_test_support::world_graph::{graph, named}; +use serde_json::{json, Value}; + +#[test] +fn world_export_expands_assemblies_deduplicates_and_preserves_state() { + let mut world = graph(); + world.create_system_assembly(named("level")).unwrap(); + world + .create_primitive( + Primitive::Cuboid { + width: 2.0, + height: 2.0, + depth: 2.0, + }, + CreateOptions { + parent: Some("level".into()), + ..named("wall") + }, + ) + .unwrap(); + world + .create_primitive( + Primitive::Circle { radius: 0.5 }, + CreateOptions { + parent: Some("level".into()), + ..named("profile") + }, + ) + .unwrap(); + world + .instance( + "wall", + CopyOptions { + og_id: Some("wall-copy".into()), + ..CopyOptions::default() + }, + ) + .unwrap(); + let before = world.brep("wall").unwrap().to_json().unwrap(); + let revision = world.revision(); + let options = StepOptions::default(); + let (first, report) = world + .export_step(&["level".into(), "wall".into()], &options) + .unwrap(); + let (second, _) = world + .export_step(&["level".into(), "wall".into()], &options) + .unwrap(); + assert_eq!(first, second); + assert_eq!(report.products, 2); + assert_eq!(report.solids, 2); + assert_eq!( + report + .bodies + .iter() + .map(|body| body.og_id.as_str()) + .collect::>(), + ["wall", "wall-copy"] + ); + assert_eq!(report.skipped.len(), 1); + assert_eq!(report.skipped[0].og_id, "profile"); + assert!(first.contains("PRODUCT('wall','wall'")); + assert!(first.contains("PRODUCT('wall-copy','wall-copy'")); + let bodies = placed_export_bodies(&world, &report, &options); + let report = serde_json::to_value(&report).unwrap(); + let parsed = parse_and_match(&first, &report, &options.unit, &bodies); + assert_eq!(parsed.product_names().unwrap(), ["'wall'", "'wall-copy'"]); + assert_eq!(world.revision(), revision); + assert_eq!(world.brep("wall").unwrap().to_json().unwrap(), before); + assert_eq!( + world + .export_step(&["profile".into()], &options) + .unwrap_err() + .error_code(), + ErrorCode::InvalidOperand + ); + assert_eq!( + world + .export_step(&["level".into(), "profile".into()], &options) + .unwrap_err() + .error_code(), + ErrorCode::InvalidOperand, + ); + assert_eq!(world.revision(), revision); +} + +#[test] +fn world_export_applies_z_up_and_escapes_file_name() { + let mut world = graph(); + world + .create_primitive( + Primitive::Cuboid { + width: 2.0, + height: 2.0, + depth: 2.0, + }, + named("body"), + ) + .unwrap(); + world + .transform( + "body", + Transform::Translate { + offset: [2.0, 0.0, 2.0], + }, + ) + .unwrap(); + let options = StepOptions { + unit: "metre".into(), + name: "quote'\\#12\tø".into(), + ..StepOptions::default() + }; + let exported = export_matched(&world, &["body"], &options); + assert!(exported + .text + .contains("quote''\\\\#12\\X2\\0009\\X0\\\\X2\\00F8\\X0\\")); + assert!(exported.text.contains( + "CARTESIAN_POINT('',(1.00000000000000000E0,-3.00000000000000000E0,2.00000000000000000E0))" + )); + assert_eq!(exported.report["upAxis"], "Z"); + assert_eq!(exported.report["products"], 1); +} + +#[test] +fn world_export_handles_far_body_and_rejects_options_without_mutation() { + let defaults: StepOptions = serde_json::from_str("{}").unwrap(); + assert_eq!(defaults.unit, "millimetre"); + assert_eq!(defaults.up_axis, "Z"); + let mut world = graph(); + world + .create_primitive( + Primitive::Cuboid { + width: 1.0, + height: 1.0, + depth: 1.0, + }, + named("far"), + ) + .unwrap(); + world + .transform( + "far", + Transform::Translate { + offset: [10_000.0, 0.0, 0.0], + }, + ) + .unwrap(); + let revision = world.revision(); + export_matched(&world, &["far"], &StepOptions::default()); + for options in [ + StepOptions { + unit: "inch".into(), + ..StepOptions::default() + }, + StepOptions { + up_axis: "X".into(), + ..StepOptions::default() + }, + StepOptions { + timestamp: "2026-02-30T00:00:00".into(), + ..StepOptions::default() + }, + StepOptions { + name: String::new(), + ..StepOptions::default() + }, + ] { + assert_eq!( + world + .export_step(&["far".into()], &options) + .unwrap_err() + .error_code(), + ErrorCode::InvalidParameter + ); + } + assert_eq!( + world + .export_step( + &["far".into()], + &StepOptions { + name: "x".repeat(4097), + ..StepOptions::default() + }, + ) + .unwrap_err() + .error_code(), + ErrorCode::LimitExceeded + ); + assert!(world + .export_step( + &["far".into()], + &StepOptions { + name: "x".repeat(4096), + ..StepOptions::default() + }, + ) + .is_ok()); + assert_eq!(world.revision(), revision); +} + +#[test] +fn body_children_are_exported_or_skipped_in_stored_order() { + let mut world = graph(); + let cube = Primitive::Cuboid { + width: 2.0, + height: 2.0, + depth: 2.0, + }; + world + .create_primitive(cube.clone(), named("parent")) + .unwrap(); + world + .transform( + "parent", + Transform::Translate { + offset: [10.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .create_primitive( + cube, + CreateOptions { + parent: Some("parent".into()), + ..named("child") + }, + ) + .unwrap(); + world + .transform( + "child", + Transform::Translate { + offset: [0.0, 0.0, 20.0], + }, + ) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: vec![[0.0; 3], [1.0, 0.0, 0.0]], + closed: false, + }, + CreateOptions { + parent: Some("parent".into()), + ..named("rail") + }, + ) + .unwrap(); + let revision = world.revision(); + let options = StepOptions { + unit: "metre".into(), + up_axis: "Y".into(), + ..StepOptions::default() + }; + let exported = export_matched(&world, &["parent"], &options); + assert_eq!(exported.report["products"], 2); + assert_eq!( + exported.report["bodies"] + .as_array() + .unwrap() + .iter() + .map(|body| &body["ogId"]) + .collect::>(), + ["parent", "child"] + ); + assert_eq!( + exported.report["skipped"], + json!([{"ogId": "rail", "reason": "Wire"}]) + ); + assert!(exported.text.contains( + "CARTESIAN_POINT('',(9.00000000000000000E0,0.00000000000000000E0,1.90000000000000000E1))" + )); + assert_eq!(world.revision(), revision); +} + +#[test] +fn sweep_rail_exports_projected_edges_without_pcurves() { + let mut world = graph(); + world + .create_primitive(Primitive::Circle { radius: 0.5 }, named("profile")) + .unwrap(); + world + .create_primitive( + Primitive::Polyline { + points: vec![[0.0; 3], [0.0, 3.0, 0.0], [3.0, 3.0, 0.0]], + closed: false, + }, + named("path"), + ) + .unwrap(); + world + .create_operation( + CreatingOperation::Sweep { + profile: "profile".into(), + path: "path".into(), + }, + named("rail"), + ) + .unwrap(); + let exported = export_matched(&world, &["rail"], &StepOptions::default()); + assert_eq!(exported.report["pcurvelessEdges"], 2); + assert_eq!(exported.report["bodies"][0]["pcurvelessEdges"], 2); + assert_eq!(exported.document.pcurveless_edges().unwrap(), 2); +} + +fn circle_edges(world: &WorldGraph, og_id: &str) -> usize { + let brep: Value = serde_json::from_str(&world.brep(og_id).unwrap().to_json().unwrap()).unwrap(); + let curves = &brep["geometry"]["curves"]; + brep["topology"]["edges"] + .as_array() + .unwrap() + .iter() + .filter(|edge| { + edge["geometry"]["kind"] == "Curve" + && curves[edge["geometry"]["curve"].as_u64().unwrap() as usize]["kind"] == "Circle" + }) + .count() +} + +#[test] +fn circle_extrusion_and_cylinder_edges_are_circles_of_the_source_radius() { + let mut world = graph(); + world + .create_primitive(Primitive::Circle { radius: 1.75 }, named("circle")) + .unwrap(); + world + .create_operation( + CreatingOperation::Extrude { + profile: "circle".into(), + holes: Vec::new(), + distance: 3.0, + }, + named("extruded"), + ) + .unwrap(); + world + .create_primitive( + Primitive::Cylinder { + radius: 1.75, + height: 3.0, + }, + named("primitive"), + ) + .unwrap(); + let metre_y = StepOptions { + unit: "metre".into(), + up_axis: "Y".into(), + ..StepOptions::default() + }; + for options in [metre_y, StepOptions::default()] { + let radius = bits([1.75 * unit_scale(&options.unit)]); + for name in ["extruded", "primitive"] { + let exported = export_matched(&world, &[name], &options); + let circles = exported + .document + .edge_supports() + .unwrap() + .into_iter() + .filter(|support| support.kind == "CIRCLE") + .collect::>(); + assert_eq!(circles.len(), circle_edges(&world, name), "{name}"); + for circle in circles { + assert!(circle.with_pcurves, "{name}"); + assert_eq!( + circle.radii.iter().map(|r| bits([*r])).collect::>(), + [radius], + "{name}" + ); + } + let surfaces = exported.document.surface_radii().unwrap(); + assert_eq!(surfaces.len(), 1, "{name}"); + assert_eq!(surfaces[0].0, "cylinder", "{name}"); + assert_eq!( + surfaces[0].1.iter().map(|r| bits([*r])).collect::>(), + [radius], + "{name}" + ); + assert_eq!(exported.report["solids"], 1); + assert_eq!(exported.document.count("POLY_LOOP"), 0); + } + } +} diff --git a/main/opengeometry/tests/step_oracle/rejections.rs b/main/opengeometry/tests/step_oracle/rejections.rs new file mode 100644 index 0000000..31980d5 --- /dev/null +++ b/main/opengeometry/tests/step_oracle/rejections.rs @@ -0,0 +1,147 @@ +use crate::support::{add_cuboid, fixture_text}; +use opengeometry::brep::{BrepEnvelope, GeometryError}; +use opengeometry::exchange::export_step; +use opengeometry::world_graph::{CreateOptions, ErrorCode, Primitive, StepOptions, WorldGraph}; +use opengeometry_test_support::world_graph::{graph, named}; +use serde_json::{json, Value}; +use std::f64::consts::TAU; + +#[derive(Debug, PartialEq)] +struct GraphState { + revision: u64, + nodes: usize, + shapes: usize, + breps: Vec, +} + +fn graph_state(world: &WorldGraph, bodies: &[&str]) -> GraphState { + GraphState { + revision: world.revision(), + nodes: world.node_count(), + shapes: world.shape_count(), + breps: bodies + .iter() + .map(|og_id| world.brep(og_id).unwrap().to_json().unwrap()) + .collect(), + } +} + +fn assert_export_rejected_unchanged( + world: &WorldGraph, + nodes: &[&str], + code: ErrorCode, + bodies: &[&str], +) { + let before = graph_state(world, bodies); + let nodes = nodes + .iter() + .map(|node| node.to_string()) + .collect::>(); + let error = world + .export_step(&nodes, &StepOptions::default()) + .unwrap_err(); + assert_eq!(error.error_code(), code, "{error}"); + assert_eq!(graph_state(world, bodies), before); +} + +#[test] +fn empty_node_list_is_invalid_parameter_and_leaves_the_graph_unchanged() { + let mut world = graph(); + add_cuboid(&mut world, "body", [1.0; 3], [0.0; 3]); + assert_export_rejected_unchanged(&world, &[], ErrorCode::InvalidParameter, &["body"]); +} + +#[test] +fn unknown_node_is_unknown_node_and_leaves_the_graph_unchanged() { + let mut world = graph(); + add_cuboid(&mut world, "body", [1.0; 3], [0.0; 3]); + assert_export_rejected_unchanged(&world, &["missing"], ErrorCode::UnknownNode, &["body"]); +} + +#[test] +fn assembly_holding_only_wires_is_empty_result_and_leaves_the_graph_unchanged() { + let mut world = graph(); + world.create_system_assembly(named("level")).unwrap(); + for (og_id, primitive) in [ + ("profile", Primitive::Circle { radius: 0.5 }), + ( + "rail", + Primitive::Polyline { + points: vec![[0.0; 3], [1.0, 0.0, 0.0]], + closed: false, + }, + ), + ] { + world + .create_primitive( + primitive, + CreateOptions { + parent: Some("level".into()), + ..named(og_id) + }, + ) + .unwrap(); + } + assert_export_rejected_unchanged( + &world, + &["level"], + ErrorCode::EmptyResult, + &["profile", "rail"], + ); +} + +#[test] +fn empty_assembly_is_empty_result_and_leaves_the_graph_unchanged() { + let mut world = graph(); + world.create_system_assembly(named("level")).unwrap(); + assert_export_rejected_unchanged(&world, &["level"], ErrorCode::EmptyResult, &[]); +} + +fn edited_fixture(name: &str, edit: impl FnOnce(&mut Value)) -> BrepEnvelope { + let mut body: Value = + serde_json::from_str(&fixture_text(&format!("{name}.brep.json")).unwrap()).unwrap(); + edit(&mut body); + BrepEnvelope::from_json(&body.to_string()).unwrap_or_else(|error| panic!("{error}")) +} + +fn assert_export_rejected(brep: &BrepEnvelope, matches: fn(&GeometryError) -> bool) { + let before = brep.to_json().unwrap(); + let error = export_step(brep, "metre").unwrap_err(); + assert!(matches(&error), "{error}"); + assert_eq!(brep.to_json().unwrap(), before); +} + +#[test] +fn face_key_over_4_kib_is_limit_exceeded() { + let over = edited_fixture("cuboid", |body| { + body["topology"]["faces"][0]["key"] = json!("k".repeat(4097)); + }); + assert_export_rejected(&over, |error| { + matches!(error, GeometryError::LimitExceeded(_)) + }); + let at_limit = edited_fixture("cuboid", |body| { + body["topology"]["faces"][0]["key"] = json!("k".repeat(4096)); + }); + assert!(export_step(&at_limit, "metre").is_ok()); +} + +#[test] +fn seam_edge_with_a_projected_pcurve_is_unsupported_geometry() { + let brep = edited_fixture("cylinder", |body| { + assert_eq!(body["topology"]["edges"][2]["chart_seam"], true); + assert_eq!(body["topology"]["halfedges"][1]["edge"], 2); + body["geometry"]["pcurves"][1] = json!({ + "kind": "ProjectedCurve", + "curve": 2, + "surface": 0, + "chart": 0, + "uv_hint": [TAU, 0.0], + "uv_rate": [0.0, 1.0], + "parameter_origin": 0.0, + }); + }); + assert_export_rejected( + &brep, + |error| matches!(error, GeometryError::UnsupportedGeometry(message) if message.contains("seam")), + ); +} diff --git a/main/opengeometry/tests/step_oracle/support.rs b/main/opengeometry/tests/step_oracle/support.rs new file mode 100644 index 0000000..b34fc38 --- /dev/null +++ b/main/opengeometry/tests/step_oracle/support.rs @@ -0,0 +1,186 @@ +use opengeometry::brep::{placed, BrepEnvelope, Frame3, Similarity3}; +use opengeometry::exchange::StepExportReport; +use opengeometry::world_graph::{Primitive, StepOptions, Transform, WorldGraph}; +use opengeometry_test_support::part21::{ + check_report_matches, check_single_report_matches, check_values_belong, Document, + ExpectedValues, SolidEntry, +}; +use opengeometry_test_support::world_graph::named; +use serde_json::Value; +use std::{fs, path::Path}; + +pub(super) struct MatchedExport { + pub(super) text: String, + pub(super) report: Value, + pub(super) document: Document, +} + +pub(super) fn fixture_text(relative: &str) -> Option { + let path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/cases") + .join(relative); + path.exists() + .then(|| fs::read_to_string(&path).unwrap_or_else(|error| panic!("{relative}: {error}"))) +} + +pub(super) fn fixture_names(directory: &str) -> Vec { + let path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/cases") + .join(directory); + let mut names = fs::read_dir(path) + .unwrap() + .map(|entry| entry.unwrap().file_name().into_string().unwrap()) + .collect::>(); + names.sort(); + names +} + +pub(super) fn unit_scale(unit: &str) -> f64 { + if unit == "millimetre" { + 1000.0 + } else { + 1.0 + } +} + +fn export_placement(world: &WorldGraph, og_id: &str, up_axis: &str) -> Similarity3 { + let mut conversion = Similarity3::IDENTITY; + if up_axis == "Z" { + conversion.frame = Frame3 { + origin: [0.0; 3], + x: [1.0, 0.0, 0.0], + y: [0.0, 0.0, 1.0], + z: [0.0, -1.0, 0.0], + }; + } + conversion.compose(&world.world_placement(og_id).unwrap()) +} + +pub(super) fn placed_export_bodies( + world: &WorldGraph, + report: &StepExportReport, + options: &StepOptions, +) -> Vec { + report + .bodies + .iter() + .map(|body| { + let placement = export_placement(world, &body.og_id, &options.up_axis); + let shape = world.brep(&body.og_id).unwrap(); + let mut brep = if identity_bits(placement) { + (*shape).clone() + } else { + placed(&shape, placement.frame, similarity_scale(placement)).unwrap() + }; + brep.id = body.og_id.clone(); + brep + }) + .collect() +} + +fn identity_bits(placement: Similarity3) -> bool { + let entries = |similarity: Similarity3| { + let frame = similarity.frame; + [frame.origin, frame.x, frame.y, frame.z] + .into_iter() + .flatten() + .chain([similarity_scale(similarity)]) + .map(f64::to_bits) + .collect::>() + }; + entries(placement) == entries(Similarity3::IDENTITY) +} + +fn similarity_scale(placement: Similarity3) -> f64 { + serde_json::to_value(placement).unwrap()["scale"] + .as_f64() + .unwrap() +} + +pub(super) fn parse_and_match( + text: &str, + report: &Value, + unit: &str, + bodies: &[BrepEnvelope], +) -> Document { + let document = Document::parse(text).unwrap_or_else(|error| panic!("{error}")); + document + .check_length_unit(unit) + .unwrap_or_else(|error| panic!("{error}")); + if report.get("length_unit").is_some() { + check_single_report_matches(&document, report) + } else { + check_report_matches(&document, text, report) + } + .unwrap_or_else(|error| panic!("{error}")); + assert_eq!(document.solids().unwrap(), solid_entries(bodies)); + let mut expected = ExpectedValues::default(); + for body in bodies { + expected.add_body(body, unit_scale(unit)); + } + check_values_belong(&document, &expected).unwrap_or_else(|error| panic!("{error}")); + document +} + +fn solid_entries(bodies: &[BrepEnvelope]) -> Vec { + let mut entries = Vec::new(); + for body in bodies { + let json: Value = serde_json::from_str(&body.to_json().unwrap()).unwrap(); + for solid in json["solids"].as_array().unwrap() { + let voids = solid["cavity_shells"].as_array().unwrap().len(); + entries.push(SolidEntry { + voids, + with_voids: voids > 0, + }); + } + } + entries +} + +pub(super) fn export_matched( + world: &WorldGraph, + nodes: &[&str], + options: &StepOptions, +) -> MatchedExport { + let nodes = nodes + .iter() + .map(|node| node.to_string()) + .collect::>(); + let (text, report) = world.export_step(&nodes, options).unwrap(); + let bodies = placed_export_bodies(world, &report, options); + let report = serde_json::to_value(&report).unwrap(); + let document = parse_and_match(&text, &report, &options.unit, &bodies); + MatchedExport { + text, + report, + document, + } +} + +pub(super) fn add_cuboid(world: &mut WorldGraph, og_id: &str, size: [f64; 3], offset: [f64; 3]) { + world + .create_primitive( + Primitive::Cuboid { + width: size[0], + height: size[1], + depth: size[2], + }, + named(og_id), + ) + .unwrap(); + world + .transform(og_id, Transform::Translate { offset }) + .unwrap(); +} + +pub(super) fn cylinder(world: &mut WorldGraph, og_id: &str) { + world + .create_primitive( + Primitive::Cylinder { + radius: 1.0, + height: 2.0, + }, + named(og_id), + ) + .unwrap(); +} diff --git a/main/opengeometry/tests/step_roundtrip_oracle.rs b/main/opengeometry/tests/step_roundtrip_oracle.rs deleted file mode 100644 index 81a80a6..0000000 --- a/main/opengeometry/tests/step_roundtrip_oracle.rs +++ /dev/null @@ -1,117 +0,0 @@ -//! STEP exact-geometry round-trip oracle (debt items D1/D9, NFR-4). -//! -//! NFR-4 requires a unit cylinder exported to STEP to round-trip through a -//! reference reader as a single cylindrical face, not a facet fan. A real -//! OpenCASCADE kernel cannot be linked into this WASM crate, so this oracle -//! independently *re-parses* the exported Part-21 text — acting as the reference -//! reader — and checks that the analytic cylinder/circle radii it recovers match -//! the source geometry exactly. (An optional pythonocc/OCP cross-check is in -//! `tests/occ_oracle.rs`, skipped when OCC is absent.) - -use opengeometry::export::step::{export_brep_to_step_text, StepExportConfig}; -use opengeometry::primitives::cylinder::OGCylinder; -use openmaths::Vector3; - -/// Pulls the trailing real argument out of each `KEYWORD(...,radius)` entity — -/// the reference reader re-deriving the analytic radius from the file. -fn parse_trailing_reals(text: &str, keyword: &str) -> Vec { - let mut out = Vec::new(); - let needle = format!("{}(", keyword); - let mut rest = text; - while let Some(pos) = rest.find(&needle) { - let after = &rest[pos + needle.len()..]; - if let Some(end) = after.find(')') { - let args = &after[..end]; - if let Some(last) = args.rsplit(',').next() { - if let Ok(v) = last.trim().parse::() { - out.push(v); - } - } - rest = &after[end..]; - } else { - break; - } - } - out -} - -#[test] -fn exported_cylinder_round_trips_as_one_analytic_cylinder() { - let radius = 1.5; - let height = 4.0; - let mut cyl = OGCylinder::new("oracle-cyl".into()); - cyl.set_config( - Vector3::new(0.0, 0.0, 0.0), - radius, - height, - 2.0 * std::f64::consts::PI, - 48, - ) - .unwrap(); - let brep = cyl.world_brep(); - - let (text, _) = - export_brep_to_step_text(&brep, &StepExportConfig::default()).expect("step export"); - - // Reference reader: exactly one cylindrical surface, recovered at the exact - // source radius — not a fan of planar facets. - let cyl_radii = parse_trailing_reals(&text, "CYLINDRICAL_SURFACE"); - assert_eq!( - cyl_radii.len(), - 1, - "exactly one cylindrical surface in file" - ); - assert!( - (cyl_radii[0] - radius).abs() < 1.0e-9, - "round-tripped cylinder radius {} != source {}", - cyl_radii[0], - radius - ); - - // The circular rings round-trip at the same exact radius. - let circle_radii = parse_trailing_reals(&text, "CIRCLE"); - assert!(!circle_radii.is_empty(), "circular edges present"); - for r in &circle_radii { - assert!( - (r - radius).abs() < 1.0e-9, - "circle radius {} != source {}", - r, - radius - ); - } - - // No planar facet fan stands in for the curved surface: the cylinder is one - // advanced face, so advanced faces are few (cylinder + 2 caps). - assert!( - text.matches("ADVANCED_FACE").count() <= 4, - "cylindrical face must not be a facet fan" - ); -} - -#[test] -fn exported_cylinder_scales_radius_with_export_scale() { - let mut cyl = OGCylinder::new("oracle-scale".into()); - cyl.set_config( - Vector3::new(0.0, 0.0, 0.0), - 1.0, - 2.0, - 2.0 * std::f64::consts::PI, - 24, - ) - .unwrap(); - let brep = cyl.world_brep(); - - let config = StepExportConfig { - scale: 10.0, - ..StepExportConfig::default() - }; - let (text, _) = export_brep_to_step_text(&brep, &config).expect("scaled export"); - - let cyl_radii = parse_trailing_reals(&text, "CYLINDRICAL_SURFACE"); - assert_eq!(cyl_radii.len(), 1); - assert!( - (cyl_radii[0] - 10.0).abs() < 1.0e-9, - "radius should scale by export scale, got {}", - cyl_radii[0] - ); -} diff --git a/main/opengeometry/tests/validity.rs b/main/opengeometry/tests/validity.rs new file mode 100644 index 0000000..dd0974c --- /dev/null +++ b/main/opengeometry/tests/validity.rs @@ -0,0 +1,29 @@ +use opengeometry::brep::BrepEnvelope; +use serde_json::{json, Value}; +use std::path::Path; + +const VERDICT_SUFFIX: &str = ".verdict.json"; + +#[test] +fn i10_every_validity_fixture_reproduces_its_stored_verdict() { + let folder = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cases/validity"); + let mut names: Vec = std::fs::read_dir(&folder) + .unwrap() + .filter_map(|entry| { + let file = entry.unwrap().file_name().to_string_lossy().into_owned(); + file.strip_suffix(VERDICT_SUFFIX).map(str::to_owned) + }) + .collect(); + names.sort(); + assert_eq!(names.len(), 28); + for name in names { + let source = std::fs::read_to_string(folder.join(format!("{name}.json"))).unwrap(); + let verdict = std::fs::read(folder.join(format!("{name}{VERDICT_SUFFIX}"))).unwrap(); + let expected: Value = serde_json::from_slice(&verdict).expect(&name); + let actual = match BrepEnvelope::from_json(&source) { + Ok(_) => json!({ "valid": true }), + Err(error) => json!({ "valid": false, "error": error }), + }; + assert_eq!(actual, expected, "{name}"); + } +} diff --git a/main/opengeometry/tests/validity_corpus.rs b/main/opengeometry/tests/validity_corpus.rs deleted file mode 100644 index 70ae0d7..0000000 --- a/main/opengeometry/tests/validity_corpus.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! Labelled validity corpus (debt item D5, NFR-3). -//! -//! NFR-3 requires the validity checker to classify a labelled set of valid / -//! invalid B-reps with ≥99% precision on "invalid" — i.e. it must never wave a -//! genuinely-invalid B-rep through as valid. This corpus pairs known-good -//! primitives with deliberately-broken B-reps and asserts the checker's verdict -//! matches the label for every case (so: zero false "valid"). - -use opengeometry::brep::validity::check_validity; -use opengeometry::brep::{Brep, BrepBuilder, HalfEdge}; -use opengeometry::primitives::cuboid::OGCuboid; -use opengeometry::primitives::cylinder::OGCylinder; -use opengeometry::primitives::sphere::OGSphere; -use openmaths::Vector3; -use uuid::Uuid; - -fn cuboid() -> Brep { - let mut c = OGCuboid::new("v".into()); - c.set_config(Vector3::new(0.0, 0.0, 0.0), 2.0, 2.0, 2.0) - .unwrap(); - c.world_brep() -} - -fn cylinder() -> Brep { - let mut c = OGCylinder::new("v".into()); - c.set_config( - Vector3::new(0.0, 0.0, 0.0), - 1.0, - 2.0, - 2.0 * std::f64::consts::PI, - 24, - ) - .unwrap(); - c.world_brep() -} - -fn sphere() -> Brep { - let mut s = OGSphere::new("v".into()); - s.set_config(Vector3::new(0.0, 0.0, 0.0), 1.0, 18, 12) - .unwrap(); - s.world_brep() -} - -fn open_triangle() -> Brep { - let mut b = BrepBuilder::new(Uuid::new_v4()); - b.add_vertices(&[ - Vector3::new(0.0, 0.0, 0.0), - Vector3::new(1.0, 0.0, 0.0), - Vector3::new(0.0, 0.0, 1.0), - ]); - b.add_face(&[0, 1, 2], &[]).unwrap(); - b.build().unwrap() -} - -fn non_finite_vertex() -> Brep { - let mut brep = cuboid(); - if let Some(v) = brep.vertices.get_mut(0) { - v.position = Vector3::new(f64::NAN, 0.0, 0.0); - } - brep -} - -fn non_manifold_edge() -> Brep { - // A closed cuboid with a third half-edge grafted onto an existing edge: - // that edge is now shared by three half-edges, which is non-manifold and - // must be flagged. - let mut brep = cuboid(); - let edge_id = brep.edges[0].id; - let he_id = brep.halfedges.len() as u32; - brep.halfedges - .push(HalfEdge::new(he_id, 0, 1, edge_id, None, None, None)); - brep -} - -#[test] -fn checker_never_passes_an_invalid_brep() { - // (brep, expected_valid) - let cases: Vec<(&str, Brep, bool)> = vec![ - ("cuboid", cuboid(), true), - ("cylinder", cylinder(), true), - ("sphere", sphere(), true), - ("open-triangle", open_triangle(), false), - ("non-finite-vertex", non_finite_vertex(), false), - ("non-manifold-edge", non_manifold_edge(), false), - ]; - - let mut false_valids = Vec::new(); - let mut false_invalids = Vec::new(); - - for (name, brep, expected_valid) in &cases { - let verdict = check_validity(brep).is_valid(); - if *expected_valid && !verdict { - false_invalids.push(*name); - } - if !*expected_valid && verdict { - // The cardinal sin (NFR-3): an invalid B-rep waved through as valid. - false_valids.push(*name); - } - println!( - "{}: expected_valid={} got_valid={}", - name, expected_valid, verdict - ); - } - - assert!( - false_valids.is_empty(), - "checker passed invalid B-reps as valid (NFR-3 violation): {:?}", - false_valids - ); - // Valids must also be recognized; otherwise the checker is uselessly strict. - assert!( - false_invalids.is_empty(), - "checker rejected valid B-reps: {:?}", - false_invalids - ); -} diff --git a/main/opengeometry/tests/wasm_core.rs b/main/opengeometry/tests/wasm_core.rs new file mode 100644 index 0000000..3063955 --- /dev/null +++ b/main/opengeometry/tests/wasm_core.rs @@ -0,0 +1,185 @@ +use opengeometry::brep::BrepEnvelope; +use opengeometry::tessellation::display::display_buffers; +use opengeometry::tessellation::{tessellate, SnapshotStore}; +use opengeometry_test_support::tolerance::{ordered_bits, step_parts, within_four_ulp}; +use serde_json::Value; + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[cfg_attr(not(target_arch = "wasm32"), test)] +fn embedded_stored_corpus_roundtrips_with_cross_target_float_tolerance() { + let corpus = [ + ("cuboid", include_str!("fixtures/cases/cuboid.brep.json")), + ( + "cylinder", + include_str!("fixtures/cases/cylinder.brep.json"), + ), + ("sphere", include_str!("fixtures/cases/sphere.brep.json")), + ("cone", include_str!("fixtures/cases/cone.brep.json")), + ("frustum", include_str!("fixtures/cases/frustum.brep.json")), + ("torus", include_str!("fixtures/cases/torus.brep.json")), + ( + "annular-cylinder", + include_str!("fixtures/cases/annular-cylinder.brep.json"), + ), + ( + "cylinder-with-hole", + include_str!("fixtures/cases/cylinder-with-hole.brep.json"), + ), + ("circle", include_str!("fixtures/cases/circle.brep.json")), + ( + "linear-extrusion", + include_str!("fixtures/cases/linear-extrusion.brep.json"), + ), + ( + "arc-edged-extrusion", + include_str!("fixtures/cases/arc-edged-extrusion.brep.json"), + ), + ( + "arc-edged-extrusion-with-holes", + include_str!("fixtures/cases/arc-edged-extrusion-with-holes.brep.json"), + ), + ( + "box-union", + include_str!("fixtures/cases/box-union.brep.json"), + ), + ( + "box-intersection", + include_str!("fixtures/cases/box-intersection.brep.json"), + ), + ("box-cut", include_str!("fixtures/cases/box-cut.brep.json")), + ( + "box-cavity", + include_str!("fixtures/cases/box-cavity.brep.json"), + ), + ( + "sphere-cut", + include_str!("fixtures/cases/sphere-cut.brep.json"), + ), + ]; + for (name, source) in corpus { + let body = BrepEnvelope::from_json(source).unwrap(); + body.validate().unwrap(); + let output = body.to_json().unwrap(); + let actual: Value = serde_json::from_str(&output).unwrap(); + let expected: Value = serde_json::from_str(source).unwrap(); + assert!(within_four_ulp(&actual, &expected), "{name}"); + } +} + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[cfg_attr(not(target_arch = "wasm32"), test)] +fn embedded_stored_tessellation_corpus_matches_within_four_ulp() { + macro_rules! check { + ($name:literal) => {{ + let body = BrepEnvelope::from_json(include_str!(concat!("fixtures/cases/", $name, ".brep.json"))).unwrap(); + let mesh = tessellate(&body, 0.01, 2_000_000).unwrap(); + let actual = serde_json::json!({ + "positions": mesh.positions, + "normals": mesh.normals, + "indices": mesh.indices, + "triangleFaceIds": mesh.triangle_face_ids, + "outlinePositions": mesh.outline_positions, + "outlineEdgeIds": mesh.outline_edge_ids, + "revision": mesh.revision, + "achievedDeflection": mesh.achieved_deflection, + }); + let expected: Value = serde_json::from_str(include_str!(concat!("fixtures/cases/", $name, ".tess.json"))).unwrap(); + assert!(within_four_ulp(&actual, &expected), "{}", $name); + }}; + } + check!("cuboid"); + check!("cylinder"); + check!("sphere"); + check!("cone"); + check!("frustum"); + check!("torus"); + check!("annular-cylinder"); + check!("cylinder-with-hole"); + check!("circle"); + check!("linear-extrusion"); + check!("arc-edged-extrusion"); + check!("arc-edged-extrusion-with-holes"); +} + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[cfg_attr(not(target_arch = "wasm32"), test)] +fn embedded_stored_step_corpus_keeps_entity_structure_and_four_ulp_reals() { + macro_rules! check { + ($name:literal) => {{ + let body = BrepEnvelope::from_json(include_str!(concat!( + "fixtures/cases/", + $name, + ".brep.json" + ))) + .unwrap(); + let actual = opengeometry::exchange::export_step(&body, "metre") + .unwrap() + .0; + let expected = include_str!(concat!("fixtures/cases/", $name, ".step.m")); + let (actual_structure, actual_reals) = step_parts(&actual); + let (expected_structure, expected_reals) = step_parts(expected); + assert_eq!(actual_structure, expected_structure, "{} structure", $name); + assert_eq!( + actual_reals.len(), + expected_reals.len(), + "{} real count", + $name + ); + for (a, b) in actual_reals.into_iter().zip(expected_reals) { + assert!( + a == b + || (a == 0.0 && b == 0.0) + || ordered_bits(a).abs_diff(ordered_bits(b)) <= 4, + "{} real {} differs from {}", + $name, + a, + b + ); + } + }}; + } + check!("cuboid"); + check!("cylinder"); + check!("sphere"); + check!("cone"); + check!("frustum"); + check!("torus"); + check!("annular-cylinder"); + check!("cylinder-with-hole"); + check!("linear-extrusion"); + check!("arc-edged-extrusion"); + check!("arc-edged-extrusion-with-holes"); + check!("box-union"); + check!("box-intersection"); + check!("box-cut"); + check!("box-cavity"); + check!("sphere-cut"); +} + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[cfg_attr(not(target_arch = "wasm32"), test)] +fn embedded_brep_round_trip_and_snapshot_buffers() { + let bytes = include_bytes!("fixtures/cases/cuboid.brep.json"); + let json = std::str::from_utf8(bytes).unwrap(); + let brep = BrepEnvelope::from_json(json).unwrap(); + let roundtrip = brep.to_json().unwrap(); + assert_eq!(roundtrip, json); + let mesh = tessellate(&brep, 0.01, 2_000_000).unwrap(); + let tessellation = serde_json::json!({ + "positions": mesh.positions, + "normals": mesh.normals, + "indices": mesh.indices, + "triangleFaceIds": mesh.triangle_face_ids, + "outlinePositions": mesh.outline_positions, + "outlineEdgeIds": mesh.outline_edge_ids, + "revision": mesh.revision, + "achievedDeflection": mesh.achieved_deflection, + }); + let fixture: serde_json::Value = + serde_json::from_slice(include_bytes!("fixtures/cases/cuboid.tess.json")).unwrap(); + assert_eq!(tessellation, fixture); + let direct = display_buffers(&brep, 0.01, 2_000_000).unwrap(); + let mut tessellator = SnapshotStore::new(); + let slot = tessellator.load(roundtrip.as_bytes()).unwrap(); + assert_eq!(direct, tessellator.buffers(slot, 0.01, 2_000_000).unwrap()); +} diff --git a/main/opengeometry/tests/world_graph/bounds.rs b/main/opengeometry/tests/world_graph/bounds.rs new file mode 100644 index 0000000..5f7abd2 --- /dev/null +++ b/main/opengeometry/tests/world_graph/bounds.rs @@ -0,0 +1,45 @@ +use super::support::{box_primitive, near_within}; +use opengeometry::world_graph::{CreateOptions, Transform}; +use opengeometry_test_support::world_graph::{graph, named}; + +#[test] +fn local_bounds_ignore_placement_and_parents() { + let mut world = graph(); + world.create_system_assembly(named("parent")).unwrap(); + world + .transform( + "parent", + Transform::Translate { + offset: [10.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .create_primitive( + box_primitive(), + CreateOptions { + parent: Some("parent".into()), + ..named("child") + }, + ) + .unwrap(); + world + .transform( + "child", + Transform::Translate { + offset: [1.0, 2.0, 3.0], + }, + ) + .unwrap(); + near_within( + &world.local_bounds("child").unwrap().unwrap(), + &[-1.0, 0.0, -1.0, 1.0, 2.0, 1.0], + 1e-7, + ); + near_within( + &world.bounds("child").unwrap().unwrap(), + &[10.0, 2.0, 2.0, 12.0, 4.0, 4.0], + 1e-7, + ); + assert_eq!(world.local_bounds("parent").unwrap(), None); +} diff --git a/main/opengeometry/tests/world_graph/contract.rs b/main/opengeometry/tests/world_graph/contract.rs new file mode 100644 index 0000000..d76128a --- /dev/null +++ b/main/opengeometry/tests/world_graph/contract.rs @@ -0,0 +1,138 @@ +use super::support::box_primitive; +use opengeometry::brep::BodyType; +use opengeometry::world_graph::{ + CopyOptions, CreateOptions, CreatingOperation, ErrorCode, Plane, Primitive, WorldGraph, +}; +use opengeometry_test_support::world_graph::{graph, named}; + +fn rectangle() -> Primitive { + Primitive::Rectangle { + width: 2.0, + breadth: 3.0, + } +} + +fn extrude() -> CreatingOperation { + CreatingOperation::Extrude { + profile: "profile".into(), + holes: Vec::new(), + distance: 1.0, + } +} + +fn profiled_graph() -> WorldGraph { + let mut world = graph(); + world + .create_primitive(rectangle(), named("profile")) + .unwrap(); + world +} + +fn unnamed_box(world: &mut WorldGraph) -> String { + world + .create_primitive(box_primitive(), CreateOptions::default()) + .unwrap() + .0 +} + +#[test] +fn mismatched_expected_body_type_is_rejected_before_commit() { + let mut world = profiled_graph(); + let revision = world.revision(); + let nodes = world.node_count(); + let shape_ids = world.reserve(1, &[]).unwrap().shape_ids; + let wire_as_solid = world + .create_primitive( + rectangle(), + CreateOptions { + body_type: Some(BodyType::Solid), + ..CreateOptions::default() + }, + ) + .unwrap_err(); + assert_eq!(wire_as_solid.error_code(), ErrorCode::BodyTypeMismatch); + let solid_as_wire = world + .create_operation( + extrude(), + CreateOptions { + body_type: Some(BodyType::Wire), + ..CreateOptions::default() + }, + ) + .unwrap_err(); + assert_eq!(solid_as_wire.error_code(), ErrorCode::BodyTypeMismatch); + assert_eq!(world.revision(), revision); + assert_eq!(world.node_count(), nodes); + assert_eq!(world.reserve(1, &[]).unwrap().shape_ids, shape_ids); + assert_eq!(unnamed_box(&mut world), "solid-1"); +} + +#[test] +fn generated_ids_are_kind_prefixed_from_one() { + let mut world = graph(); + let cuboid = unnamed_box(&mut world); + let (wire, _) = world + .create_primitive(rectangle(), CreateOptions::default()) + .unwrap(); + let (assembly, _) = world + .create_system_assembly(CreateOptions::default()) + .unwrap(); + let (instance, _) = world.instance(&cuboid, CopyOptions::default()).unwrap(); + let (duplicate, _) = world.duplicate(&wire, CopyOptions::default()).unwrap(); + assert_eq!( + [cuboid, wire, assembly, instance, duplicate], + ["solid-1", "wire-1", "assembly-1", "solid-2", "wire-2"] + ); + world + .create_primitive(box_primitive(), named("solid-3")) + .unwrap(); + assert_eq!(unnamed_box(&mut world), "solid-4"); +} + +#[test] +fn json_null_parent_moves_a_copy_to_the_top_level() { + let to_top: CopyOptions = serde_json::from_str(r#"{"parent":null}"#).unwrap(); + assert_eq!(to_top.parent, Some(None)); + let keep: CopyOptions = serde_json::from_str("{}").unwrap(); + assert_eq!(keep.parent, None); + let mut world = graph(); + world.create_system_assembly(named("group")).unwrap(); + world + .create_primitive( + box_primitive(), + CreateOptions { + og_id: Some("body".into()), + parent: Some("group".into()), + ..CreateOptions::default() + }, + ) + .unwrap(); + let (copy, _) = world.instance("body", to_top).unwrap(); + assert_eq!(world.parent(©).unwrap(), None); +} + +#[test] +fn plane_is_rejected_on_operations_and_assemblies() { + let mut world = profiled_graph(); + let revision = world.revision(); + let nodes = world.node_count(); + let planed = || CreateOptions { + plane: Some(Plane::default()), + ..CreateOptions::default() + }; + let errors = [ + world.create_operation(extrude(), planed()).unwrap_err(), + world.create_system_assembly(planed()).unwrap_err(), + world + .create_system_assembly(CreateOptions { + body_type: Some(BodyType::Solid), + ..CreateOptions::default() + }) + .unwrap_err(), + ]; + for error in errors { + assert_eq!(error.error_code(), ErrorCode::InvalidParameter); + } + assert_eq!(world.revision(), revision); + assert_eq!(world.node_count(), nodes); +} diff --git a/main/opengeometry/tests/world_graph/hierarchy.rs b/main/opengeometry/tests/world_graph/hierarchy.rs new file mode 100644 index 0000000..876cd85 --- /dev/null +++ b/main/opengeometry/tests/world_graph/hierarchy.rs @@ -0,0 +1,221 @@ +use super::support::{box_primitive, copy_named, near}; +use opengeometry::world_graph::{ + CreateOptions, EditScope, ErrorCode, ModifyingOperation, Primitive, Transform, +}; +use opengeometry_test_support::world_graph::{graph, named}; +use std::sync::Arc; + +#[test] +fn instances_share_one_immutable_shape_and_duplicates_do_not() { + let mut world = graph(); + world + .create_primitive(box_primitive(), named("body")) + .unwrap(); + world.instance("body", copy_named("instance")).unwrap(); + assert_eq!(world.shape_count(), 1); + assert_eq!(world.instance_count("body").unwrap(), 2); + assert_eq!( + world + .ensure_editable("body", EditScope::Node) + .unwrap_err() + .error_code(), + ErrorCode::SharedShape + ); + world + .ensure_editable("body", EditScope::AllInstances) + .unwrap(); + assert!(Arc::ptr_eq( + &world.brep("body").unwrap(), + &world.brep("instance").unwrap() + )); + world.duplicate("body", copy_named("duplicate")).unwrap(); + assert_eq!(world.shape_count(), 2); + assert!(!Arc::ptr_eq( + &world.brep("body").unwrap(), + &world.brep("duplicate").unwrap() + )); + let copied_id = world.brep("duplicate").unwrap().id.clone(); + assert_ne!(copied_id, world.brep("body").unwrap().id); + assert!(world + .brep("duplicate") + .unwrap() + .topology + .faces + .iter() + .all(|face| face + .provenance + .sources + .iter() + .all(|source| source.entity == copied_id && source.body == copied_id))); + world.make_unique("instance").unwrap().unwrap(); + assert_eq!(world.instance_count("body").unwrap(), 1); + assert_eq!(world.shape_count(), 3); + assert!(world.make_unique("body").unwrap().is_none()); +} + +#[test] +fn copies_of_an_operated_body_carry_no_report() { + let mut world = graph(); + world + .create_primitive(box_primitive(), named("body")) + .unwrap(); + world + .create_primitive(box_primitive(), named("tool")) + .unwrap(); + world + .transform( + "tool", + Transform::Translate { + offset: [1.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .operate( + "body", + ModifyingOperation::Subtract, + &["tool".into()], + EditScope::Node, + ) + .unwrap(); + world.duplicate("body", copy_named("duplicate")).unwrap(); + world.instance("body", copy_named("instance")).unwrap(); + world.make_unique("instance").unwrap().unwrap(); + assert!(world.report("body").unwrap().is_some()); + assert!(world.report("duplicate").unwrap().is_none()); + assert!(world.report("instance").unwrap().is_none()); +} + +#[test] +fn parent_motion_and_reparenting_obey_local_and_world_rules() { + let mut world = graph(); + world.create_system_assembly(named("a")).unwrap(); + world.create_system_assembly(named("b")).unwrap(); + world + .create_primitive( + box_primitive(), + CreateOptions { + og_id: Some("child".into()), + parent: Some("a".into()), + plane: None, + body_type: None, + }, + ) + .unwrap(); + world + .transform( + "a", + Transform::Translate { + offset: [10.0, 0.0, 0.0], + }, + ) + .unwrap(); + near( + world.world_placement("child").unwrap().frame.origin, + [10.0, 0.0, 0.0], + ); + world.add_child("b", &["child".into()], false).unwrap(); + near( + world.world_placement("child").unwrap().frame.origin, + [0.0, 0.0, 0.0], + ); + world.add_child("a", &["child".into()], true).unwrap(); + near( + world.world_placement("child").unwrap().frame.origin, + [0.0, 0.0, 0.0], + ); + near(world.placement("child").unwrap().origin, [-10.0, 0.0, 0.0]); + world.remove_child("a", "child", true).unwrap(); + near( + world.world_placement("child").unwrap().frame.origin, + [0.0, 0.0, 0.0], + ); + assert_eq!(world.parent("child").unwrap(), None); +} + +#[test] +fn i6_failed_calls_do_not_consume_ids_or_change_the_graph() { + let mut world = graph(); + let reserved = world.reserve(1, &[]).unwrap(); + assert_eq!(reserved.shape_ids, ["shape-0"]); + world.create_system_assembly(named("a")).unwrap(); + world + .create_system_assembly(CreateOptions { + og_id: Some("b".into()), + parent: Some("a".into()), + plane: None, + body_type: None, + }) + .unwrap(); + let revision = world.revision(); + let cycle = world.add_child("b", &["a".into()], false).unwrap_err(); + assert_eq!(cycle.error_code(), ErrorCode::CycleDetected); + assert_eq!(world.revision(), revision); + assert_eq!(world.parent("a").unwrap(), None); + assert_eq!( + world + .create_system_assembly(named("a")) + .unwrap_err() + .error_code(), + ErrorCode::DuplicateNode + ); + assert_eq!( + world + .transform( + "a", + Transform::Scale { + factor: -1.0, + pivot: None + } + ) + .unwrap_err() + .error_code(), + ErrorCode::InvalidTransform + ); + assert_eq!( + world + .create_primitive( + Primitive::Cylinder { + radius: 0.0, + height: 2.0 + }, + CreateOptions::default() + ) + .unwrap_err() + .error_code(), + ErrorCode::InvalidParameter + ); + assert_eq!( + world + .create_primitive( + Primitive::Polyline { + points: vec![[0.0; 3], [0.0; 3]], + closed: false + }, + CreateOptions::default(), + ) + .unwrap_err() + .error_code(), + ErrorCode::InvalidParameter + ); + let (generated, _) = world + .create_primitive(box_primitive(), CreateOptions::default()) + .unwrap(); + assert_eq!(generated, "solid-1"); + assert_eq!(world.brep(&generated).unwrap().id, reserved.shape_ids[0]); + let body = world.node(&generated).unwrap(); + let handle = body.handle; + let generation = body.generation; + world.dispose(&generated).unwrap(); + assert_eq!( + world + .node_by_handle(handle, generation) + .unwrap_err() + .error_code(), + ErrorCode::Disposed + ); + let (next, _) = world + .create_primitive(box_primitive(), CreateOptions::default()) + .unwrap(); + assert_eq!(next, "solid-2"); +} diff --git a/main/opengeometry/tests/world_graph/main.rs b/main/opengeometry/tests/world_graph/main.rs new file mode 100644 index 0000000..5b853b8 --- /dev/null +++ b/main/opengeometry/tests/world_graph/main.rs @@ -0,0 +1,6 @@ +mod bounds; +mod contract; +mod hierarchy; +mod marks; +mod placement; +mod support; diff --git a/main/opengeometry/tests/world_graph/marks.rs b/main/opengeometry/tests/world_graph/marks.rs new file mode 100644 index 0000000..68f8b28 --- /dev/null +++ b/main/opengeometry/tests/world_graph/marks.rs @@ -0,0 +1,219 @@ +use super::support::{box_primitive, copy_named}; +use opengeometry::world_graph::{CreateOptions, EditScope, ErrorCode, Primitive, Transform}; +use opengeometry_test_support::world_graph::{graph, named}; +use std::sync::Arc; + +#[test] +fn marks_restore_nodes_and_shape_pointers_without_reusing_counters() { + let mut world = graph(); + world + .create_primitive(box_primitive(), named("body")) + .unwrap(); + let original = world.brep("body").unwrap(); + let original_handle = world.node("body").unwrap().handle; + let original_generation = world.node("body").unwrap().generation; + let mark = world.mark().unwrap(); + world.instance("body", copy_named("instance")).unwrap(); + world.make_unique("instance").unwrap().unwrap(); + let later = world.mark().unwrap(); + world.dispose("body").unwrap(); + assert_eq!( + world + .node_by_handle(original_handle, original_generation) + .unwrap_err() + .error_code(), + ErrorCode::Disposed + ); + let rollback = world.rollback(mark).unwrap(); + assert!(rollback.added.iter().any(|node| node.og_id == "body")); + assert!(Arc::ptr_eq(&world.brep("body").unwrap(), &original)); + assert_eq!( + world + .node_by_handle(original_handle, original_generation) + .unwrap() + .og_id, + "body" + ); + assert_eq!( + world.rollback(later).unwrap_err().error_code(), + ErrorCode::InvalidMark + ); + let (_, next) = world + .create_primitive(box_primitive(), CreateOptions::default()) + .unwrap(); + assert!(next.added[0].handle > original_handle); + assert_eq!(world.mark_stats().live_marks, 1); + world.release(mark).unwrap(); + assert_eq!(world.mark_stats().live_marks, 0); + assert_eq!(world.mark_stats().retained_revisions, 0); +} + +#[test] +fn changes_since_compacts_updates_and_reports_subtree_motion() { + let mut world = graph(); + world.create_system_assembly(named("parent")).unwrap(); + world + .create_primitive( + box_primitive(), + CreateOptions { + og_id: Some("child".into()), + parent: Some("parent".into()), + plane: None, + body_type: None, + }, + ) + .unwrap(); + let revision = world.revision(); + world + .transform( + "parent", + Transform::Translate { + offset: [1.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .transform( + "parent", + Transform::Translate { + offset: [2.0, 0.0, 0.0], + }, + ) + .unwrap(); + let changes = world.changes_since(revision).unwrap(); + assert_eq!(changes.changed.len(), 2); + assert!(changes.changed.iter().any(|node| node.og_id == "child" + && node.shape_revision == Some(world.brep("child").unwrap().revision))); + let bounds = world.bounds("parent").unwrap().unwrap(); + assert!(bounds[0] <= 2.0 && bounds[3] >= 4.0); + assert!(bounds[1] <= 0.0 && bounds[4] >= 2.0); + assert!(bounds[2] <= -1.0 && bounds[5] >= 1.0); +} + +#[test] +fn nested_marks_and_compacted_changes_preserve_live_history() { + let mut world = graph(); + let baseline = world.revision(); + let outer = world.mark().unwrap(); + world + .create_primitive(box_primitive(), named("body")) + .unwrap(); + let inner = world.mark().unwrap(); + world.instance("body", copy_named("temporary")).unwrap(); + assert!(world.mark_stats().retained_revisions >= 1); + world.rollback(inner).unwrap(); + assert_eq!(world.instance_count("body").unwrap(), 1); + world.release(inner).unwrap(); + let revision_before_outer_rollback = world.revision(); + world.rollback(outer).unwrap(); + assert!(world.node("body").is_err()); + assert!(world.revision() > revision_before_outer_rollback); + let compact = world.changes_since(baseline).unwrap(); + assert!(compact.added.is_empty()); + assert!(compact.changed.is_empty()); + assert!(compact.removed.is_empty()); + world.release(outer).unwrap(); +} + +#[test] +fn shared_rebuild_and_rollback_keep_shape_revision_high_water() { + let mut world = graph(); + world + .create_primitive(box_primitive(), named("body")) + .unwrap(); + world.instance("body", copy_named("instance")).unwrap(); + let denied = world + .rebuild_primitive("body", box_primitive(), EditScope::Node) + .unwrap_err(); + assert_eq!(denied.error_code(), ErrorCode::SharedShape); + let original = world.brep("body").unwrap(); + let mark = world.mark().unwrap(); + let changed = world + .rebuild_primitive( + "body", + Primitive::Cuboid { + width: 3.0, + height: 2.0, + depth: 2.0, + }, + EditScope::AllInstances, + ) + .unwrap(); + assert_eq!(changed.changed.len(), 2); + assert_eq!(world.brep("body").unwrap().revision, 1); + assert!(changed + .changed + .iter() + .all(|node| node.shape_revision == Some(world.brep("body").unwrap().revision))); + assert!(Arc::ptr_eq( + &world.brep("body").unwrap(), + &world.brep("instance").unwrap() + )); + world.rollback(mark).unwrap(); + assert!(Arc::ptr_eq(&world.brep("body").unwrap(), &original)); + assert_eq!(world.brep("body").unwrap().revision, 0); + world + .rebuild_primitive( + "body", + Primitive::Cuboid { + width: 4.0, + height: 2.0, + depth: 2.0, + }, + EditScope::AllInstances, + ) + .unwrap(); + assert_eq!(world.brep("body").unwrap().revision, 2); + assert_eq!( + world + .rebuild_primitive( + "body", + Primitive::Rectangle { + width: 2.0, + breadth: 2.0 + }, + EditScope::AllInstances + ) + .unwrap_err() + .error_code(), + ErrorCode::BodyTypeMismatch + ); + world.release(mark).unwrap(); +} + +#[test] +fn subtree_dispose_and_rollback_preserve_counters_and_handles() { + let mut world = graph(); + world.create_system_assembly(named("parent")).unwrap(); + assert!(world.bounds("parent").unwrap().is_none()); + let mark = world.mark().unwrap(); + let (generated, _) = world + .create_primitive( + box_primitive(), + CreateOptions { + og_id: None, + parent: Some("parent".into()), + plane: None, + body_type: None, + }, + ) + .unwrap(); + let handle = world.node(&generated).unwrap().handle; + let generation = world.node(&generated).unwrap().generation; + world.dispose("parent").unwrap(); + assert_eq!( + world + .node_by_handle(handle, generation) + .unwrap_err() + .error_code(), + ErrorCode::Disposed + ); + world.rollback(mark).unwrap(); + assert!(world.node("parent").is_ok()); + assert!(world.node(&generated).is_err()); + let (next, _) = world + .create_primitive(box_primitive(), CreateOptions::default()) + .unwrap(); + assert_ne!(generated, next); + world.release(mark).unwrap(); +} diff --git a/main/opengeometry/tests/world_graph/placement.rs b/main/opengeometry/tests/world_graph/placement.rs new file mode 100644 index 0000000..40e8dd9 --- /dev/null +++ b/main/opengeometry/tests/world_graph/placement.rs @@ -0,0 +1,327 @@ +use super::support::{bits, box_primitive, near, place}; +use opengeometry::brep::Similarity3; +use opengeometry::world_graph::{ + CopyOptions, CreateOptions, ErrorCode, Placement, Plane, Primitive, Transform, +}; +use opengeometry_test_support::world_graph::{graph, named}; + +#[test] +fn place_round_trip_runs_the_formula_and_preserves_shape_revision() { + let mut world = graph(); + world + .create_primitive(box_primitive(), named("body")) + .unwrap(); + let before = world.brep("body").unwrap().revision; + world + .transform( + "body", + Transform::Place { + origin: None, + x_direction: None, + normal: None, + scale: None, + }, + ) + .unwrap(); + assert_eq!( + bits(&world.node("body").unwrap().local), + bits(&Similarity3::IDENTITY) + ); + let placement = world.placement("body").unwrap(); + world.transform("body", place(placement)).unwrap(); + assert_eq!( + bits(&world.node("body").unwrap().local), + bits(&Similarity3::IDENTITY) + ); + world + .transform( + "body", + Transform::Rotate { + axis: [0.0, 1.0, 0.0], + degrees: 90.0, + pivot: Some([0.0; 3]), + }, + ) + .unwrap(); + near( + world + .world_placement("body") + .unwrap() + .apply_point([1.0, 0.0, 0.0]), + [0.0, 0.0, -1.0], + ); + world + .transform( + "body", + Transform::Rotate { + axis: [0.1, 0.2, 0.3], + degrees: 13.37, + pivot: None, + }, + ) + .unwrap(); + let placement = world.placement("body").unwrap(); + world.transform("body", place(placement)).unwrap(); + let after = bits(&world.node("body").unwrap().local); + assert_eq!(bits(&world.placement("body").unwrap()), bits(&placement)); + world.transform("body", place(placement)).unwrap(); + assert_eq!(bits(&world.node("body").unwrap().local), after); + let mut fresh = graph(); + fresh + .create_primitive(box_primitive(), named("body")) + .unwrap(); + fresh.transform("body", place(placement)).unwrap(); + assert_eq!(bits(&fresh.node("body").unwrap().local), after); + assert_eq!(world.brep("body").unwrap().revision, before); +} + +#[test] +fn quarter_turn_rotations_are_bitwise_exact() { + let exact = [0.0_f64, 1.0, -1.0].map(f64::to_bits); + for axis in [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]] { + for degrees in [90.0, 180.0, 270.0, -90.0, 1440.0] { + let mut world = graph(); + world.create_system_assembly(named("pivot")).unwrap(); + world + .transform( + "pivot", + Transform::Rotate { + axis, + degrees, + pivot: None, + }, + ) + .unwrap(); + let frame = world.node("pivot").unwrap().local.frame; + for entry in [frame.x, frame.y, frame.z].concat() { + assert!( + exact.contains(&entry.to_bits()), + "{degrees} about {axis:?} gave {entry:e}" + ); + } + } + } +} + +#[test] +fn place_returns_its_input_and_replays_bitwise_in_a_fresh_graph() { + let input = Placement { + origin: [1.0, 2.0, 3.0], + x_direction: [3.0, 4.0, 0.0], + normal: [0.0, 0.0, 1.0], + scale: 2.0, + }; + let mut a = graph(); + a.create_primitive(box_primitive(), named("body")).unwrap(); + a.transform("body", place(input)).unwrap(); + assert_eq!(bits(&a.placement("body").unwrap()), bits(&input)); + let mut b = graph(); + b.create_primitive(box_primitive(), named("body")).unwrap(); + b.transform("body", place(input)).unwrap(); + let local = bits(&a.node("body").unwrap().local); + assert_eq!(bits(&b.node("body").unwrap().local), local); + b.transform("body", place(a.placement("body").unwrap())) + .unwrap(); + assert_eq!(bits(&b.node("body").unwrap().local), local); +} + +#[test] +fn place_applied_twice_is_bitwise_stable() { + let input = Placement { + origin: [1.0, 2.0, 3.0], + x_direction: [3.0, 4.0, 0.0], + normal: [0.0, 0.0, 1.0], + scale: 2.0, + }; + let mut world = graph(); + world + .create_primitive(box_primitive(), named("body")) + .unwrap(); + world.transform("body", place(input)).unwrap(); + let first = bits(&world.node("body").unwrap().local); + world.transform("body", place(input)).unwrap(); + assert_eq!(bits(&world.node("body").unwrap().local), first); + world + .transform( + "body", + Transform::Rotate { + axis: [0.1, 0.2, 0.3], + degrees: 13.37, + pivot: None, + }, + ) + .unwrap(); + let placement = world.placement("body").unwrap(); + world.transform("body", place(placement)).unwrap(); + let once = bits(&world.node("body").unwrap().local); + world.transform("body", place(placement)).unwrap(); + assert_eq!(bits(&world.node("body").unwrap().local), once); + assert_eq!(bits(&world.placement("body").unwrap()), bits(&placement)); +} + +#[test] +fn primitive_plane_and_precision_guard_are_applied_at_commit() { + let mut world = graph(); + world.create_system_assembly(named("parent")).unwrap(); + world + .transform( + "parent", + Transform::Translate { + offset: [10.0, 0.0, 0.0], + }, + ) + .unwrap(); + let (id, _) = world + .create_primitive( + box_primitive(), + CreateOptions { + og_id: Some("planned".into()), + parent: Some("parent".into()), + plane: Some(Plane { + origin: Some([4.0, 5.0, 6.0]), + ..Plane::default() + }), + body_type: None, + }, + ) + .unwrap(); + near( + world.world_placement(&id).unwrap().frame.origin, + [14.0, 5.0, 6.0], + ); + let revision = world.revision(); + let error = world + .create_primitive( + Primitive::Cuboid { + width: 2e6, + height: 2.0, + depth: 2.0, + }, + CreateOptions::default(), + ) + .unwrap_err(); + assert_eq!(error.error_code(), ErrorCode::LimitExceeded); + assert_eq!(world.revision(), revision); +} + +#[test] +fn world_placement_form_composes_the_parent_chain() { + let mut world = graph(); + world.create_system_assembly(named("parent")).unwrap(); + world + .transform( + "parent", + Transform::Translate { + offset: [10.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .create_primitive( + box_primitive(), + CreateOptions { + og_id: Some("body".into()), + parent: Some("parent".into()), + plane: Some(Plane { + origin: Some([4.0, 5.0, 6.0]), + ..Plane::default() + }), + body_type: None, + }, + ) + .unwrap(); + assert_eq!( + world.world_placement_form("body").unwrap(), + Placement { + origin: [14.0, 5.0, 6.0], + x_direction: [1.0, 0.0, 0.0], + normal: [0.0, 1.0, 0.0], + scale: 1.0, + } + ); +} + +#[test] +fn one_hundred_thousand_incremental_rotations_keep_a_valid_frame() { + let mut world = graph(); + world.create_system_assembly(named("pivot")).unwrap(); + for _ in 0..100_000 { + world + .transform( + "pivot", + Transform::Rotate { + axis: [0.1, 0.2, 0.3], + degrees: 0.001, + pivot: None, + }, + ) + .unwrap(); + } + world.node("pivot").unwrap().local.validate().unwrap(); +} + +#[test] +fn copies_under_another_parent_keep_their_world_placement() { + let mut world = graph(); + world.create_system_assembly(named("a")).unwrap(); + world.create_system_assembly(named("b")).unwrap(); + world + .transform( + "a", + Transform::Translate { + offset: [7.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .transform( + "b", + Transform::Translate { + offset: [-3.0, 0.0, 0.0], + }, + ) + .unwrap(); + world + .create_primitive( + box_primitive(), + CreateOptions { + og_id: Some("body".into()), + parent: Some("a".into()), + plane: None, + body_type: None, + }, + ) + .unwrap(); + let options = CopyOptions { + og_id: Some("copy".into()), + parent: Some(Some("b".into())), + }; + world.instance("body", options).unwrap(); + near( + world.world_placement("body").unwrap().frame.origin, + world.world_placement("copy").unwrap().frame.origin, + ); + near( + world + .relative_placement("copy", "body") + .unwrap() + .frame + .origin, + [0.0; 3], + ); + assert_eq!(world.parent("copy").unwrap(), Some("b")); + world + .duplicate( + "body", + CopyOptions { + og_id: Some("root-copy".into()), + parent: Some(None), + }, + ) + .unwrap(); + assert_eq!(world.parent("root-copy").unwrap(), None); + near( + world.world_placement("root-copy").unwrap().frame.origin, + world.world_placement("body").unwrap().frame.origin, + ); +} diff --git a/main/opengeometry/tests/world_graph/support.rs b/main/opengeometry/tests/world_graph/support.rs new file mode 100644 index 0000000..0611fb2 --- /dev/null +++ b/main/opengeometry/tests/world_graph/support.rs @@ -0,0 +1,41 @@ +use opengeometry::world_graph::{CopyOptions, Placement, Primitive, Transform}; +use serde::Serialize; + +pub(super) fn copy_named(name: &str) -> CopyOptions { + CopyOptions { + og_id: Some(name.into()), + ..CopyOptions::default() + } +} + +pub(super) fn box_primitive() -> Primitive { + Primitive::Cuboid { + width: 2.0, + height: 2.0, + depth: 2.0, + } +} + +pub(super) fn near(a: [f64; 3], b: [f64; 3]) { + near_within(&a, &b, 1e-12); +} + +pub(super) fn near_within(a: &[f64], b: &[f64], tolerance: f64) { + assert_eq!(a.len(), b.len(), "{a:?} != {b:?}"); + for (x, y) in a.iter().zip(b) { + assert!((x - y).abs() < tolerance, "{a:?} != {b:?}"); + } +} + +pub(super) fn bits(value: &impl Serialize) -> String { + serde_json::to_string(value).unwrap() +} + +pub(super) fn place(placement: Placement) -> Transform { + Transform::Place { + origin: Some(placement.origin), + x_direction: Some(placement.x_direction), + normal: Some(placement.normal), + scale: Some(placement.scale), + } +} diff --git a/main/package-lock.json b/main/package-lock.json new file mode 100644 index 0000000..b8a7b6f --- /dev/null +++ b/main/package-lock.json @@ -0,0 +1,3083 @@ +{ + "name": "opengeometry", + "version": "2.5.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "opengeometry", + "version": "2.5.0", + "license": "MPL-2.0", + "devDependencies": { + "@eslint/js": "9.35.0", + "@playwright/test": "1.59.1", + "@rollup/plugin-node-resolve": "15.2.3", + "@rollup/plugin-typescript": "11.1.6", + "@types/three": "0.168.0", + "@typescript-eslint/eslint-plugin": "8.43.0", + "@typescript-eslint/parser": "8.43.0", + "eslint": "9.35.0", + "rollup": "4.36.0", + "three": "0.168.0", + "three-184": "npm:three@0.184.0", + "tslib": "2.7.0", + "typescript": "5.6.2", + "vite": "6.2.2" + }, + "peerDependencies": { + "three": ">=0.168.0 <0.185.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.7.tgz", + "integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.2", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-typescript": { + "version": "11.1.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz", + "integrity": "sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0||^4.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.36.0.tgz", + "integrity": "sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.36.0.tgz", + "integrity": "sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.36.0.tgz", + "integrity": "sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.36.0.tgz", + "integrity": "sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.36.0.tgz", + "integrity": "sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.36.0.tgz", + "integrity": "sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.36.0.tgz", + "integrity": "sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.36.0.tgz", + "integrity": "sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.36.0.tgz", + "integrity": "sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.36.0.tgz", + "integrity": "sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.36.0.tgz", + "integrity": "sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.36.0.tgz", + "integrity": "sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.36.0.tgz", + "integrity": "sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.36.0.tgz", + "integrity": "sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.36.0.tgz", + "integrity": "sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.36.0.tgz", + "integrity": "sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.36.0.tgz", + "integrity": "sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.36.0.tgz", + "integrity": "sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.36.0.tgz", + "integrity": "sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.168.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.168.0.tgz", + "integrity": "sha512-qAGLGzbaYgkkonOBfwOr+TZpOskPfFjrDAj801WQSVkUz0/D9zwir4vhruJ/CC/GteywzR9pqeVVfs5th/2oKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": "*", + "@webgpu/types": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~0.18.1" + } + }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.43.0.tgz", + "integrity": "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/type-utils": "8.43.0", + "@typescript-eslint/utils": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.43.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.43.0.tgz", + "integrity": "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.43.0.tgz", + "integrity": "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.43.0.tgz", + "integrity": "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.43.0.tgz", + "integrity": "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.43.0.tgz", + "integrity": "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0", + "@typescript-eslint/utils": "8.43.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.43.0.tgz", + "integrity": "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.43.0.tgz", + "integrity": "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.43.0", + "@typescript-eslint/tsconfig-utils": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.43.0.tgz", + "integrity": "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.43.0.tgz", + "integrity": "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.43.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@webgpu/types": { + "version": "0.1.74", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.74.tgz", + "integrity": "sha512-lgiI4hbuLcI9unnm2cL/tvCaQU45dp0xcLWJh5uB/9MBGvIA4F8XIk7nSiBeg+K4xWGYwgZKn80LmERI5CxoTA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", + "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.35.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ignore": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.10.tgz", + "integrity": "sha512-HpbUakT7xp5miBUywCHf36ZEuAJNklBJDDsGpUIjMzOSmM8ELSfA9Sa/QDPeNeqeoN31u+UTCkL4klCOVvRm4Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-core-module": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.17.0.tgz", + "integrity": "sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/meshoptimizer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.36.0.tgz", + "integrity": "sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.36.0", + "@rollup/rollup-android-arm64": "4.36.0", + "@rollup/rollup-darwin-arm64": "4.36.0", + "@rollup/rollup-darwin-x64": "4.36.0", + "@rollup/rollup-freebsd-arm64": "4.36.0", + "@rollup/rollup-freebsd-x64": "4.36.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.36.0", + "@rollup/rollup-linux-arm-musleabihf": "4.36.0", + "@rollup/rollup-linux-arm64-gnu": "4.36.0", + "@rollup/rollup-linux-arm64-musl": "4.36.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.36.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.36.0", + "@rollup/rollup-linux-riscv64-gnu": "4.36.0", + "@rollup/rollup-linux-s390x-gnu": "4.36.0", + "@rollup/rollup-linux-x64-gnu": "4.36.0", + "@rollup/rollup-linux-x64-musl": "4.36.0", + "@rollup/rollup-win32-arm64-msvc": "4.36.0", + "@rollup/rollup-win32-ia32-msvc": "4.36.0", + "@rollup/rollup-win32-x64-msvc": "4.36.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/three": { + "version": "0.168.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.168.0.tgz", + "integrity": "sha512-6m6jXtDwMJEK/GGMbAOTSAmxNdzKvvBzgd7q8bE/7Tr6m7PaBh5kKLrN7faWtlglXbzj7sVba48Idwx+NRsZXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/three-184": { + "name": "three", + "version": "0.184.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.184.0.tgz", + "integrity": "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz", + "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/main/package.json b/main/package.json new file mode 100644 index 0000000..867b580 --- /dev/null +++ b/main/package.json @@ -0,0 +1,83 @@ +{ + "name": "opengeometry", + "version": "2.5.0", + "private": true, + "type": "module", + "license": "MPL-2.0", + "description": "Open-source CAD kernel for the browser: B-rep solids, boolean operations, extrude and sweep, and STEP export, written in Rust, compiled to WebAssembly, with a TypeScript SDK for Three.js.", + "keywords": [ + "cad", + "cad-kernel", + "brep", + "geometry", + "geometry-kernel", + "solid-modeling", + "boolean", + "csg", + "extrude", + "sweep", + "step", + "step-export", + "threejs", + "three", + "webassembly", + "wasm", + "rust", + "typescript", + "browser", + "parametric", + "3d" + ], + "homepage": "https://opengeometry.io?utm_source=npm", + "repository": { + "type": "git", + "url": "git+https://github.com/OpenGeometry-io/OpenGeometry.git", + "directory": "main" + }, + "bugs": { + "url": "https://github.com/OpenGeometry-io/OpenGeometry/issues" + }, + "peerDependencies": { + "three": ">=0.168.0 <0.185.0" + }, + "scripts": { + "build-core": "cd opengeometry && wasm-pack build --target web --out-dir pkg", + "build": "npm run build-core && rollup -c && node scripts/build/prepare-dist.mjs", + "lint:check": "eslint .", + "lint:fix": "eslint --fix .", + "typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.worker.json && tsc --noEmit -p tsconfig.examples.json && tsc --noEmit -p tsconfig.tests.json && tsc --noEmit -p tsconfig.scripts.json", + "test": "cargo test --manifest-path opengeometry/Cargo.toml", + "test:wasm": "cd opengeometry && wasm-pack test --node", + "test:bindings": "node scripts/test/bindings.test.mjs", + "test:node": "node --test 'opengeometry-three/tests/node/*.test.mjs' 'scripts/check/*.test.mjs' 'scripts/bench/*.test.mjs' 'scripts/build/*.test.mjs'", + "test:performance": "node --max-old-space-size=512 scripts/bench/performance.mjs --check", + "test:browser": "playwright test", + "snapshot:kernel": "node scripts/snapshot/kernel-snapshot.mjs", + "snapshot:sdk": "node scripts/snapshot/sdk-snapshot.mjs", + "check:api": "node scripts/check/api-surface.mjs", + "check:duplicates": "node scripts/check/duplicates.mjs", + "check:source": "node scripts/check/source-rules.mjs && node scripts/check/lint-exceptions.mjs", + "check:cycles": "node scripts/check/import-cycles.mjs", + "check": "node scripts/check/verify.mjs", + "check:full": "node scripts/check/verify.mjs --full", + "dev-example": "vite --config opengeometry-three/examples-vite/vite.config.ts", + "build-example": "vite build --config opengeometry-three/examples-vite/vite.config.ts", + "dev-test-pages": "vite --config opengeometry-three/tests/browser/vite.config.ts" + }, + "devDependencies": { + "@eslint/js": "9.35.0", + "@playwright/test": "1.59.1", + "@rollup/plugin-node-resolve": "15.2.3", + "@rollup/plugin-typescript": "11.1.6", + "@types/three": "0.168.0", + "@typescript-eslint/eslint-plugin": "8.43.0", + "@typescript-eslint/parser": "8.43.0", + "eslint": "9.35.0", + "rollup": "4.36.0", + "three": "0.168.0", + "three-184": "npm:three@0.184.0", + "tslib": "2.7.0", + "typescript": "5.6.2", + "vite": "6.2.2" + } +} diff --git a/main/playwright.config.ts b/main/playwright.config.ts new file mode 100644 index 0000000..97ea4ae --- /dev/null +++ b/main/playwright.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from '@playwright/test'; +import { EXAMPLES_URL, PAGES_URL, PORT_BASE } from './opengeometry-three/tests/browser/support/servers'; + +export default defineConfig({ + testDir: './opengeometry-three/tests/browser/specs', + workers: 1, + fullyParallel: false, + retries: 0, + outputDir: `test-results/${String(PORT_BASE)}`, + timeout: 30_000, + use: { browserName: 'chromium', headless: true, baseURL: PAGES_URL }, + webServer: [ + { + command: `npm run dev-example -- --host 127.0.0.1 --port ${new URL(EXAMPLES_URL).port} --strictPort`, + url: EXAMPLES_URL, + reuseExistingServer: false, + timeout: 30_000, + }, + { + command: `npm run dev-test-pages -- --host 127.0.0.1 --port ${new URL(PAGES_URL).port} --strictPort`, + url: `${PAGES_URL}/smoke.html`, + reuseExistingServer: false, + timeout: 30_000, + }, + ], +}); diff --git a/main/rollup.config.js b/main/rollup.config.js new file mode 100644 index 0000000..927e001 --- /dev/null +++ b/main/rollup.config.js @@ -0,0 +1,16 @@ +import { nodeResolve } from '@rollup/plugin-node-resolve'; +import typescript from '@rollup/plugin-typescript'; + +export default [ + { + input: { index: 'opengeometry-three/index.ts', testing: 'opengeometry-three/src/testing/internals.ts' }, + external: ['three', /^three\//], + output: { dir: 'dist', format: 'esm', sourcemap: true, entryFileNames: '[name].js' }, + plugins: [nodeResolve(), typescript({ tsconfig: './tsconfig.json' })], + }, + { + input: 'opengeometry-three/src/rendering/tessellation-worker.ts', + output: { file: 'dist/tessellation-worker.js', format: 'esm', sourcemap: true }, + plugins: [nodeResolve(), typescript({ tsconfig: './tsconfig.worker.json' })], + }, +]; diff --git a/main/rust-toolchain.toml b/main/rust-toolchain.toml new file mode 100644 index 0000000..e8346a8 --- /dev/null +++ b/main/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.88.0" +targets = ["wasm32-unknown-unknown"] diff --git a/main/scripts/bench/budgets.mjs b/main/scripts/bench/budgets.mjs new file mode 100644 index 0000000..4178a9b --- /dev/null +++ b/main/scripts/bench/budgets.mjs @@ -0,0 +1,33 @@ +const MARGIN = 1.25; + +export function median(values) { + const sorted = [...values].sort((a, b) => a - b); + const middle = Math.floor(sorted.length / 2); + return sorted.length % 2 === 1 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2; +} + +function sizeFindings(sizes, baseline) { + return Object.entries(sizes).flatMap(([name, value]) => { + const budget = baseline[name]; + if (budget === undefined) return [`missing size baseline for ${name}`]; + if (value > budget * MARGIN) return [`${name} regressed: ${String(value)} exceeds ${String(budget * MARGIN)}`]; + return []; + }); +} + +function timingReport(timings, block, key) { + if (block === undefined) return { findings: [], missing: [`missing timing baseline for ${key}`] }; + const entries = Object.entries(timings); + const missing = entries.filter(([name]) => block[name] === undefined) + .map(([name]) => `missing timing baseline for ${name} on ${key}`); + const findings = entries.filter(([name, value]) => block[name] !== undefined && value > block[name] * MARGIN) + .map(([name, value]) => `${name} regressed: ${String(value)} ms exceeds ${String(block[name] * MARGIN)} ms`); + return { findings, missing }; +} + +export function budgetFindings(result, baseline, key, ci) { + const sizes = sizeFindings(result.sizes, baseline.sizes); + if (ci) return { findings: sizes, notes: ['timings are not checked on CI'] }; + const timing = timingReport(result.timings, baseline.timings[key], key); + return { findings: [...sizes, ...timing.findings], notes: timing.missing }; +} diff --git a/main/scripts/bench/budgets.test.mjs b/main/scripts/bench/budgets.test.mjs new file mode 100644 index 0000000..d970226 --- /dev/null +++ b/main/scripts/bench/budgets.test.mjs @@ -0,0 +1,67 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { budgetFindings, median } from './budgets.mjs'; + +const BASELINE = { + sizes: { wall50Triangles: 1000 }, + timings: { 'darwin-arm64': { measuredOn: '2026-10-01', wall50BooleanMs: 100 } }, +}; +const KEYS = [['darwin-arm64', false], ['linux-x64', false], ['darwin-arm64', true], ['linux-x64', true]]; + +function sized(sizes) { + return { sizes, timings: {} }; +} + +function timed(timings) { + return { sizes: { wall50Triangles: 1000 }, timings }; +} + +test('a size more than 25 percent over its baseline fails on every platform', () => { + for (const [key, ci] of KEYS) { + assert.deepEqual(budgetFindings(sized({ wall50Triangles: 1250 }), BASELINE, key, ci).findings, []); + assert.deepEqual(budgetFindings(sized({ wall50Triangles: 1251 }), BASELINE, key, ci).findings, [ + 'wall50Triangles regressed: 1251 exceeds 1250', + ]); + } +}); + +test('a measured size with no baseline key fails on every platform', () => { + for (const [key, ci] of KEYS) { + assert.deepEqual(budgetFindings(sized({ mitred20Triangles: 10 }), BASELINE, key, ci).findings, [ + 'missing size baseline for mitred20Triangles', + ]); + } +}); + +test('a timing more than 25 percent over its baseline fails on the baseline platform', () => { + assert.deepEqual(budgetFindings(timed({ wall50BooleanMs: 125 }), BASELINE, 'darwin-arm64', false), { + findings: [], notes: [], + }); + assert.deepEqual(budgetFindings(timed({ wall50BooleanMs: 126 }), BASELINE, 'darwin-arm64', false).findings, [ + 'wall50BooleanMs regressed: 126 ms exceeds 125 ms', + ]); +}); + +test('no timing is checked when CI is true', () => { + for (const key of ['darwin-arm64', 'linux-x64']) { + assert.deepEqual(budgetFindings(timed({ wall50BooleanMs: 1000 }), BASELINE, key, true), { + findings: [], notes: ['timings are not checked on CI'], + }); + } +}); + +test('a missing timing block is only noted when CI is unset', () => { + assert.deepEqual(budgetFindings(timed({ wall50BooleanMs: 1 }), BASELINE, 'linux-x64', false), { + findings: [], notes: ['missing timing baseline for linux-x64'], + }); +}); + +test('a timing key missing from an existing block is only noted when CI is unset', () => { + assert.deepEqual(budgetFindings(timed({ mitred20BooleanMs: 1 }), BASELINE, 'darwin-arm64', false), { + findings: [], notes: ['missing timing baseline for mitred20BooleanMs on darwin-arm64'], + }); +}); + +test('the median of five ignores one slow run', () => { + assert.equal(median([10, 11, 500, 12, 9]), 11); +}); diff --git a/main/scripts/bench/performance-baseline.json b/main/scripts/bench/performance-baseline.json new file mode 100644 index 0000000..47b7dfb --- /dev/null +++ b/main/scripts/bench/performance-baseline.json @@ -0,0 +1,25 @@ +{ + "sizes": { + "wall50Triangles": 1220, + "sliderTriangles": 92, + "mitred20Triangles": 332, + "storeyStepBytes": 48476818, + "storeyStepEntities": 830410, + "nonparallelCylinderStepBytes": 4924611 + }, + "timings": { + "darwin-arm64": { + "measuredOn": "2026-10-01", + "wall50BooleanMs": 627.670167, + "wall50TessellationMs": 19.105958, + "rotated20BooleanMs": 119.950083, + "mitred20BooleanMs": 637.486417, + "sliderRebuildMs": 1.542709, + "sliderBooleanMs": 9.603958, + "sliderTessellationMs": 1.4455, + "storeyFirstRenderMs": 341.2, + "storeyTransformFlushMs": 13.1, + "storeyStepMs": 1615.8 + } + } +} diff --git a/main/scripts/bench/performance.mjs b/main/scripts/bench/performance.mjs new file mode 100644 index 0000000..80ca007 --- /dev/null +++ b/main/scripts/bench/performance.mjs @@ -0,0 +1,112 @@ +import { readFileSync } from 'node:fs'; +import { performance } from 'node:perf_hooks'; +import { OpenGeometry, OG_OPERATION_EXTRUDE, OG_OPERATION_SUBTRACT } from '../../dist/index.js'; +import { createInlineKernel } from '../lib/kernel.mjs'; +import { budgetFindings, median } from './budgets.mjs'; +import { cutters, mesh, mitredWall, wall } from './scenes.mjs'; + +const RUNS = 5; +const MITRED_CUTS = 20; +const MITRED_PRISM_FACES = 6; +const FACES_PER_OPENING = 4; +const SERIAL_HANDLER = 'subtract_planar_polyhedra'; + +await createInlineKernel(); +const MEDIANS = measured(); +console.log(JSON.stringify(MEDIANS)); +if (process.argv.includes('--check')) checkBudgets(MEDIANS); + +function measured() { + const sizes = {}; + const timings = {}; + for (const scene of [wall50, rotated20, mitred20, slider]) { + const runs = repeated(scene); + Object.assign(sizes, medians(runs.map((run) => run.sizes))); + Object.assign(timings, medians(runs.map((run) => run.timings))); + } + return { sizes, timings }; +} + +function checkBudgets(medianResult) { + const baseline = JSON.parse(readFileSync(new URL('./performance-baseline.json', import.meta.url), 'utf8')); + const ci = process.env.CI === 'true'; + const key = `${process.platform}-${process.arch}`; + const { findings, notes } = budgetFindings(medianResult, baseline, key, ci); + for (const note of notes) console.log(note); + if (findings.length > 0) throw new Error(findings.join('\n')); +} + +function repeated(scene) { + const runs = []; + for (let run = 0; run < RUNS; run += 1) { + try { + runs.push(scene()); + } finally { + OpenGeometry.reset(); + } + } + return runs; +} + +function medians(records) { + const names = Object.keys(records[0] ?? {}); + return Object.fromEntries(names.map((name) => [name, median(records.map((record) => record[name]))])); +} + +function timed(action) { + const start = performance.now(); + const value = action(); + return { value, ms: performance.now() - start }; +} + +function wall50() { + const host = wall('straight', 60); + const tools = cutters(50, 'straight', 60, 1.1); + const boolean = timed(() => host.body.operate(OG_OPERATION_SUBTRACT, { tools })); + const tessellation = timed(() => mesh(host.body).triangles); + return { + sizes: { wall50Triangles: tessellation.value }, + timings: { wall50BooleanMs: boolean.ms, wall50TessellationMs: tessellation.ms }, + }; +} + +function rotated20() { + const host = wall('angled', 30); + host.body.transform('Rotate', { axis: [0, 1, 0], degrees: 30, pivot: [0, 0, 0] }); + const tools = cutters(20, 'angled', 30, 1.35, 30); + const boolean = timed(() => host.body.operate(OG_OPERATION_SUBTRACT, { tools })); + return { sizes: {}, timings: { rotated20BooleanMs: boolean.ms } }; +} + +function mitred20() { + const host = mitredWall('mitred', 30); + const tools = cutters(MITRED_CUTS, 'mitred', 30, 1.35); + const boolean = timed(() => host.operate(OG_OPERATION_SUBTRACT, { tools })); + checkMitredOpenings(host); + return { sizes: { mitred20Triangles: mesh(host).triangles }, timings: { mitred20BooleanMs: boolean.ms } }; +} + +function checkMitredOpenings(host) { + const handlers = host.getReport()?.handlers ?? []; + const faces = host.getBrep().topology.faces.length; + const expectedFaces = MITRED_PRISM_FACES + FACES_PER_OPENING * MITRED_CUTS; + if (faces === expectedFaces && handlers.length === MITRED_CUTS && handlers.every((name) => name === SERIAL_HANDLER)) { + return; + } + throw new Error( + `mitred wall openings changed: expected ${String(MITRED_CUTS)} ${SERIAL_HANDLER} cuts and ` + + `${String(expectedFaces)} faces, got ${String(faces)} faces and handlers ${JSON.stringify(handlers)}`, + ); +} + +function slider() { + const host = wall('slider', 6); + const tools = cutters(4, 'slider', 6, 1.2); + const rebuild = timed(() => host.body.rebuild(OG_OPERATION_EXTRUDE, { profile: host.profile, distance: 4 })); + const boolean = timed(() => host.body.operate(OG_OPERATION_SUBTRACT, { tools })); + const tessellation = timed(() => mesh(host.body).triangles); + return { + sizes: { sliderTriangles: tessellation.value }, + timings: { sliderRebuildMs: rebuild.ms, sliderBooleanMs: boolean.ms, sliderTessellationMs: tessellation.ms }, + }; +} diff --git a/main/scripts/bench/scenes.mjs b/main/scripts/bench/scenes.mjs new file mode 100644 index 0000000..6aeda6e --- /dev/null +++ b/main/scripts/bench/scenes.mjs @@ -0,0 +1,32 @@ +import { + Wire, Solid, OG_PRIMITIVE_RECTANGLE, OG_PRIMITIVE_POLYLINE, OG_PRIMITIVE_CUBOID, OG_OPERATION_EXTRUDE, +} from '../../dist/index.js'; +import { graph } from '../../dist/testing.js'; + +export function mesh(body) { + const info = JSON.parse(graph().node(body.ogId)); + return graph().buffers(info.shapeId, 0.01, 2_000_000); +} + +export function cutters(count, prefix, width, step, rotation = 0) { + const tools = []; + for (let i = 0; i < count; i++) { + const tool = new Solid(OG_PRIMITIVE_CUBOID, { width: 0.5, height: 2, depth: 0.4 }, { ogId: `${prefix}-cut-${i}` }); + tool.transform('Translate', { offset: [-width / 2 + 3 + i * step, 0, 0] }); + if (rotation) tool.transform('Rotate', { axis: [0, 1, 0], degrees: rotation, pivot: [0, 0, 0] }); + tools.push(tool); + } + return tools; +} + +export function wall(prefix, width, height = 3) { + const profile = new Wire(OG_PRIMITIVE_RECTANGLE, { width, breadth: 0.2 }, { ogId: `${prefix}-profile` }); + return { profile, body: new Solid(OG_OPERATION_EXTRUDE, { profile, distance: height }, { ogId: `${prefix}-wall` }) }; +} + +export function mitredWall(prefix, length) { + const end = length / 2; + const points = [[-end, 0, -0.1], [end, 0, -0.1], [end - 0.2, 0, 0.1], [-end + 0.2, 0, 0.1]]; + const profile = new Wire(OG_PRIMITIVE_POLYLINE, { points, closed: true }, { ogId: `${prefix}-profile` }); + return new Solid(OG_OPERATION_EXTRUDE, { profile, distance: 3 }, { ogId: `${prefix}-wall` }); +} diff --git a/main/scripts/build/declaration-specifiers.mjs b/main/scripts/build/declaration-specifiers.mjs new file mode 100644 index 0000000..68231b2 --- /dev/null +++ b/main/scripts/build/declaration-specifiers.mjs @@ -0,0 +1,9 @@ +const RELATIVE_FROM = /(\bfrom\s*)(['"])(\.{1,2}\/[^'"]*)\2/g; +const EXTENSION = /\.[A-Za-z0-9]+$/; + +export function withJsSpecifiers(source) { + return source.replace(RELATIVE_FROM, (clause, from, quote, specifier) => { + if (EXTENSION.test(specifier.split('/').at(-1) ?? '')) return clause; + return `${from}${quote}${specifier}.js${quote}`; + }); +} diff --git a/main/scripts/build/declaration-specifiers.test.mjs b/main/scripts/build/declaration-specifiers.test.mjs new file mode 100644 index 0000000..2c7e425 --- /dev/null +++ b/main/scripts/build/declaration-specifiers.test.mjs @@ -0,0 +1,28 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { withJsSpecifiers } from './declaration-specifiers.mjs'; + +test('a relative specifier gains .js', () => { + const source = [ + "export { OpenGeometry } from './src/opengeometry';", + "export * from './src/constants/index';", + "import type { Body } from '../bodies/body';", + 'import { Vector } from "./vector";', + ].join('\n'); + assert.equal(withJsSpecifiers(source), [ + "export { OpenGeometry } from './src/opengeometry.js';", + "export * from './src/constants/index.js';", + "import type { Body } from '../bodies/body.js';", + 'import { Vector } from "./vector.js";', + ].join('\n')); +}); + +test('a specifier with an extension or a package name is left alone', () => { + const source = [ + "export * from './src/testing/internals.js';", + "import type { InitInput } from '../../opengeometry/pkg/opengeometry.js';", + "import * as THREE from 'three';", + "import type { Line2 } from 'three/examples/jsm/lines/Line2';", + ].join('\n'); + assert.equal(withJsSpecifiers(source), source); +}); diff --git a/main/scripts/build/dist-declarations.test.mjs b/main/scripts/build/dist-declarations.test.mjs new file mode 100644 index 0000000..d1aa7a5 --- /dev/null +++ b/main/scripts/build/dist-declarations.test.mjs @@ -0,0 +1,31 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import path from 'node:path'; +import ts from 'typescript'; +import { REPOSITORY_ROOT, relative } from '../lib/paths.mjs'; + +const ENTRY = path.join(REPOSITORY_ROOT, 'dist', 'index.d.ts'); +const OPTIONS = { + noEmit: true, + skipLibCheck: false, + target: ts.ScriptTarget.ES2022, + lib: ['lib.es2022.d.ts', 'lib.dom.d.ts'], + types: [], + module: ts.ModuleKind.Node16, + moduleResolution: ts.ModuleResolutionKind.Node16, +}; + +function describe(diagnostic) { + const file = diagnostic.file === undefined ? '' : `${relative(diagnostic.file.fileName)} `; + return `${file}TS${String(diagnostic.code)}: ${ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`; +} + +test('the built declarations resolve under node16', () => { + const program = ts.createProgram([ENTRY], OPTIONS); + assert.deepEqual(program.getOptionsDiagnostics().map(describe), []); + const distFiles = program.getSourceFiles().filter((file) => relative(file.fileName).startsWith('dist/')); + const diagnostics = distFiles + .flatMap((file) => [...program.getSyntacticDiagnostics(file), ...program.getSemanticDiagnostics(file)]); + assert.deepEqual(diagnostics.map(describe), []); + assert(distFiles.length > 1); +}); diff --git a/main/scripts/build/dist-files.test.mjs b/main/scripts/build/dist-files.test.mjs new file mode 100644 index 0000000..1c36d9b --- /dev/null +++ b/main/scripts/build/dist-files.test.mjs @@ -0,0 +1,25 @@ +import assert from 'node:assert/strict'; +import { existsSync, readFileSync } from 'node:fs'; +import { test } from 'node:test'; +import path from 'node:path'; +import { REPOSITORY_ROOT } from '../lib/paths.mjs'; + +const DIST = path.join(REPOSITORY_ROOT, 'dist'); +const LICENCE = path.join(REPOSITORY_ROOT, '..', 'LICENSE.md'); +const FENCE = /^```[^\n]*\n[\s\S]*?^```$/gm; +const TARGET = /\]\(([^()\s]+)|^\s{0,3}\[[^\]]+\]:\s*(\S+)|\b(?:href|src)="([^"]*)"/gm; +const ABSOLUTE = /^(?:[a-z][a-z0-9+.-]*:|#|\/\/)/i; + +test('the built package ships its readme and licence', () => { + const readmeFile = path.join(DIST, 'README.md'); + const licenceFile = path.join(DIST, 'LICENSE.md'); + assert(existsSync(readmeFile), `${readmeFile} is missing`); + assert(existsSync(licenceFile), `${licenceFile} is missing`); + const readme = readFileSync(readmeFile, 'utf8'); + assert.equal(readme.includes('utm_source=github'), false); + const targets = [...readme.replace(FENCE, '').matchAll(TARGET)] + .map((match) => match[1] ?? match[2] ?? match[3] ?? ''); + assert(targets.length > 0, 'the readme has no link'); + assert.deepEqual(targets.filter((target) => !ABSOLUTE.test(target)), []); + assert.equal(readFileSync(licenceFile, 'utf8'), readFileSync(LICENCE, 'utf8')); +}); diff --git a/main/scripts/build/dist-package.mjs b/main/scripts/build/dist-package.mjs new file mode 100644 index 0000000..f24077b --- /dev/null +++ b/main/scripts/build/dist-package.mjs @@ -0,0 +1,63 @@ +const GITHUB = 'https://github.com/OpenGeometry-io/OpenGeometry'; +const RAW = 'https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/main'; +const ABSOLUTE = /^(?:[a-z][a-z0-9+.-]*:|#|\/\/)/i; +const FENCE = /^ {0,3}(`{3,}|~{3,})[^\n]*\n[\s\S]*?^ {0,3}\1[ \t]*$/gm; +const INLINE_CODE = /(`+)(?!`)[\s\S]*?(? `\uE000${String(held.push(code) - 1)}\uE000`; + const prose = text.replace(FENCE, hold).replace(INLINE_CODE, hold); + const rewritten = markdownLinks(prose) + .replace(REFERENCE, (_whole, label, target) => `${label}${absolute(target, false)}`) + .replace(ATTRIBUTE, (_whole, name, target) => `${name}="${absolute(target, name === 'src')}"`); + return restore(rewritten, held).replaceAll('utm_source=github', 'utm_source=npm'); +} + +function restore(text, held) { + const restored = text.replace(HELD, (_whole, index) => held[Number(index)] ?? ''); + return restored === text ? restored : restore(restored, held); +} + +function markdownLinks(text) { + return text.replace(LINK, (_whole, bang, label, target, title) => { + const image = bang === '!'; + return `${bang}[${image ? label : markdownLinks(label)}](${absolute(target, image)}${title})`; + }); +} + +function absolute(target, image) { + if (ABSOLUTE.test(target)) return target; + if (target.startsWith('../')) throw new Error(`${target} points outside the repository`); + const relative = target.replace(/^\.?\//, ''); + if (image) return `${RAW}/${relative}`; + const pathPart = relative.split(/[?#]/)[0] ?? ''; + return `${GITHUB}/${pathPart.endsWith('/') ? 'tree' : 'blob'}/main/${relative}`; +} diff --git a/main/scripts/build/dist-package.test.mjs b/main/scripts/build/dist-package.test.mjs new file mode 100644 index 0000000..ffe3886 --- /dev/null +++ b/main/scripts/build/dist-package.test.mjs @@ -0,0 +1,102 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { distManifest, packageReadme } from './dist-package.mjs'; + +const REPOSITORY = { + type: 'git', url: 'git+https://github.com/OpenGeometry-io/OpenGeometry.git', directory: 'main', +}; +const BUGS = { url: 'https://github.com/OpenGeometry-io/OpenGeometry/issues' }; +const SOURCE_MANIFEST = { + name: 'opengeometry', + version: '2.5.0', + private: true, + type: 'module', + license: 'MPL-2.0', + description: 'A CAD kernel.', + keywords: ['cad', 'brep'], + homepage: 'https://opengeometry.io?utm_source=npm', + repository: REPOSITORY, + bugs: BUGS, + peerDependencies: { three: '>=0.168.0 <0.185.0' }, + scripts: { build: 'rollup -c' }, + devDependencies: { rollup: '4.36.0' }, +}; +const BLOB = 'https://github.com/OpenGeometry-io/OpenGeometry/blob/main'; +const TREE = 'https://github.com/OpenGeometry-io/OpenGeometry/tree/main'; +const RAW = 'https://raw.githubusercontent.com/OpenGeometry-io/OpenGeometry/main'; + +test('the dist manifest carries the package details and the entry points', () => { + const manifest = distManifest(SOURCE_MANIFEST); + assert.deepEqual(manifest, { + name: 'opengeometry', + version: '2.5.0', + description: 'A CAD kernel.', + keywords: ['cad', 'brep'], + homepage: 'https://opengeometry.io?utm_source=npm', + repository: REPOSITORY, + bugs: BUGS, + license: 'MPL-2.0', + type: 'module', + main: './index.js', + types: './index.d.ts', + exports: { + '.': { types: './index.d.ts', import: './index.js' }, + './tessellation-worker.js': './tessellation-worker.js', + './opengeometry_bg.wasm': './opengeometry_bg.wasm', + './package.json': './package.json', + }, + peerDependencies: { three: '>=0.168.0 <0.185.0' }, + }); + assert.deepEqual(Object.keys(manifest), [ + 'name', 'version', 'description', 'keywords', 'homepage', 'repository', 'bugs', 'license', 'type', 'main', + 'types', 'exports', 'peerDependencies', + ]); +}); + +test('the package readme tags its links for npm', () => { + const source = [ + '[Website](https://opengeometry.io?utm_source=github)', + 'Demos', + '[Guide](https://docs.opengeometry.io/OpenGeometry?utm_source=github#start)', + ].join('\n'); + assert.equal(packageReadme(source), [ + '[Website](https://opengeometry.io?utm_source=npm)', + 'Demos', + '[Guide](https://docs.opengeometry.io/OpenGeometry?utm_source=npm#start)', + ].join('\n')); +}); + +test('the package readme makes relative links absolute and leaves absolute links and anchors alone', () => { + const source = [ + 'See the [licence](./LICENSE.md) and the [examples](./main/opengeometry-three/examples-vite/).', + 'Read [verification](./developer.md#verification-and-ci) or jump to [features](#features).', + 'Write to [us](mailto:someone@example.com) or open the licence.', + '![x](./docs/x.png) and [`release.md`](release.md "Releases")', + '```md', + '[inside](./LICENSE.md)', + '```', + 'Inline `[code](./LICENSE.md)` stays.', + ].join('\n'); + assert.equal(packageReadme(source), [ + `See the [licence](${BLOB}/LICENSE.md) and the [examples](${TREE}/main/opengeometry-three/examples-vite/).`, + `Read [verification](${BLOB}/developer.md#verification-and-ci) or jump to [features](#features).`, + `Write to [us](mailto:someone@example.com) or open the licence.`, + `![x](${RAW}/docs/x.png) and [\`release.md\`](${BLOB}/release.md "Releases")`, + '```md', + '[inside](./LICENSE.md)', + '```', + 'Inline `[code](./LICENSE.md)` stays.', + ].join('\n')); + assert.throws(() => packageReadme('[up](../README.md)')); +}); + +test('a footnote definition is left alone', () => { + assert.equal(packageReadme('[^1]: A note.'), '[^1]: A note.'); +}); + +test('a data-src attribute is left alone', () => { + assert.equal( + packageReadme(''), + ``, + ); +}); diff --git a/main/scripts/build/prepare-dist.mjs b/main/scripts/build/prepare-dist.mjs new file mode 100644 index 0000000..15b22d1 --- /dev/null +++ b/main/scripts/build/prepare-dist.mjs @@ -0,0 +1,47 @@ +import { cp, mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import { REPOSITORY_ROOT } from '../lib/paths.mjs'; +import { withJsSpecifiers } from './declaration-specifiers.mjs'; +import { distManifest, packageReadme } from './dist-package.mjs'; + +const DIST = path.join(REPOSITORY_ROOT, 'dist'); +const PKG = path.join(REPOSITORY_ROOT, 'opengeometry', 'pkg'); +const DIST_PKG = path.join(DIST, 'opengeometry', 'pkg'); +const PUBLIC_ROOT = path.join(REPOSITORY_ROOT, '..'); + +async function declarationFiles(directory) { + const files = []; + for (const entry of await readdir(directory)) { + const candidate = path.join(directory, entry); + if ((await stat(candidate)).isDirectory()) { + files.push(...await declarationFiles(candidate)); + } else if (candidate.endsWith('.d.ts')) { + files.push(candidate); + } + } + return files; +} + +await mkdir(DIST_PKG, { recursive: true }); +for (const file of ['opengeometry_bg.wasm', 'opengeometry_bg.wasm.d.ts']) { + await cp(path.join(PKG, file), path.join(DIST, file)); +} +for (const file of ['opengeometry.js', 'opengeometry.d.ts', 'opengeometry_bg.wasm', 'opengeometry_bg.wasm.d.ts']) { + await cp(path.join(PKG, file), path.join(DIST_PKG, file)); +} + +await writeFile(path.join(DIST, 'testing.d.ts'), "export * from './src/testing/internals.js';\n"); + +for (const file of await declarationFiles(DIST)) { + const source = await readFile(file, 'utf8'); + const target = path.relative(path.dirname(file), path.join(DIST_PKG, 'opengeometry.js')).split(path.sep).join('/'); + const specifier = target.startsWith('.') ? target : `./${target}`; + const rewritten = withJsSpecifiers(source.replace(/(?:\.\.\/)+opengeometry\/pkg\/opengeometry(?:\.js)?/g, specifier)); + if (rewritten !== source) await writeFile(file, rewritten); +} + +const MANIFEST = JSON.parse(await readFile(path.join(REPOSITORY_ROOT, 'package.json'), 'utf8')); +await writeFile(path.join(DIST, 'package.json'), `${JSON.stringify(distManifest(MANIFEST), null, 2)}\n`); +const README = await readFile(path.join(PUBLIC_ROOT, 'README.md'), 'utf8'); +await writeFile(path.join(DIST, 'README.md'), packageReadme(README)); +await cp(path.join(PUBLIC_ROOT, 'LICENSE.md'), path.join(DIST, 'LICENSE.md')); diff --git a/main/scripts/build/readme-code.test.mjs b/main/scripts/build/readme-code.test.mjs new file mode 100644 index 0000000..363e40e --- /dev/null +++ b/main/scripts/build/readme-code.test.mjs @@ -0,0 +1,100 @@ +import assert from 'node:assert/strict'; +import { existsSync, readFileSync } from 'node:fs'; +import { test } from 'node:test'; +import path from 'node:path'; +import ts from 'typescript'; +import { REPOSITORY_ROOT } from '../lib/paths.mjs'; + +const README = { + label: 'the README', file: path.join(REPOSITORY_ROOT, '..', 'README.md'), prefix: 'readme-block', +}; +const MIGRATION = { + label: 'MIGRATION.md', file: path.join(REPOSITORY_ROOT, '..', 'MIGRATION.md'), prefix: 'migration-block', +}; +const TS_BLOCK = /^```ts\n([\s\S]*?)^```$/gm; +const TS_OPENER = /^[ \t]*(?:`{3,}|~{3,})[ \t]*(?:typescript|tsx|ts)(?![\w-])/gim; +const QUICK_START_PAGE = path.join( + REPOSITORY_ROOT, 'opengeometry-three', 'tests', 'browser', 'pages', 'quick-start.ts', +); +const OPTIONS = { + noEmit: true, + strict: true, + skipLibCheck: true, + types: [], + target: ts.ScriptTarget.ES2022, + module: ts.ModuleKind.ESNext, + moduleResolution: ts.ModuleResolutionKind.Bundler, + lib: ['lib.es2022.d.ts', 'lib.dom.d.ts'], + paths: { opengeometry: [path.join(REPOSITORY_ROOT, 'dist', 'index.d.ts')] }, +}; + +function documentText(source) { + assert(existsSync(source.file), `${source.label} does not exist at ${source.file}`); + return readFileSync(source.file, 'utf8'); +} + +function tsBlocks(source) { + const blocks = new Map(); + for (const match of documentText(source).matchAll(TS_BLOCK)) { + blocks.set(path.join(REPOSITORY_ROOT, `${source.prefix}-${String(blocks.size + 1)}.ts`), match[1]); + } + return blocks; +} + +function blockHost(blocks) { + const host = ts.createCompilerHost(OPTIONS); + return { + ...host, + fileExists: (name) => blocks.has(name) || host.fileExists(name), + readFile: (name) => blocks.get(name) ?? host.readFile(name), + getSourceFile: (name, language, onError, shouldCreate) => { + const text = blocks.get(name); + if (text === undefined) return host.getSourceFile(name, language, onError, shouldCreate); + return ts.createSourceFile(name, text, language, true); + }, + }; +} + +function diagnosticText(diagnostic) { + const { file, start } = diagnostic; + const where = file === undefined || start === undefined + ? '' + : `${path.basename(file.fileName)} line ${String(file.getLineAndCharacterOfPosition(start).line + 1)} `; + return `${where}TS${String(diagnostic.code)}: ${ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`; +} + +function blockDiagnostics(program, names) { + return names.flatMap((name) => { + const file = program.getSourceFile(name); + assert(file !== undefined, `${name} was not loaded`); + return [...program.getSyntacticDiagnostics(file), ...program.getSemanticDiagnostics(file)]; + }); +} + +for (const source of [README, MIGRATION]) { + test(`every ts block in ${source.label} type-checks against the built package`, () => { + const blocks = tsBlocks(source); + assert(blocks.size > 0, `${source.label} has no ts block`); + const program = ts.createProgram([...blocks.keys()], OPTIONS, blockHost(blocks)); + const diagnostics = [ + ...program.getOptionsDiagnostics(), + ...program.getGlobalDiagnostics(), + ...blockDiagnostics(program, [...blocks.keys()]), + ]; + assert.deepEqual(diagnostics.map(diagnosticText), []); + }); + + test(`every ts fence in ${source.label} is one the type check reads`, () => { + const openers = [...documentText(source).matchAll(TS_OPENER)].length; + assert.equal(openers, tsBlocks(source).size); + }); +} + +test('the browser quick-start page boots with the README\'s create call', () => { + const [first] = tsBlocks(README).values(); + assert(first !== undefined, 'the README has no ts block'); + const create = first.split('\n').find((line) => line.includes('OpenGeometry.create(')); + assert(create !== undefined, 'the first ts block of the README has no OpenGeometry.create call'); + const page = readFileSync(QUICK_START_PAGE, 'utf8').split('\n').map((line) => line.trim()); + assert(page.includes(create.trim()), `quick-start.ts does not contain: ${create.trim()}`); +}); diff --git a/main/scripts/check/api-surface.mjs b/main/scripts/check/api-surface.mjs new file mode 100644 index 0000000..394326d --- /dev/null +++ b/main/scripts/check/api-surface.mjs @@ -0,0 +1,110 @@ +import path from 'node:path'; +import ts from 'typescript'; +import { REPOSITORY_ROOT } from '../lib/paths.mjs'; + +const ENTRY = path.join(REPOSITORY_ROOT, 'dist', 'index.d.ts'); +const DIST_ROOT = `${path.join(REPOSITORY_ROOT, 'dist')}${path.sep}`; +const PRINTER = ts.createPrinter({ removeComments: true }); + +const PROGRAM = ts.createProgram([ENTRY], { + noEmit: true, + skipLibCheck: true, + target: ts.ScriptTarget.ES2022, + module: ts.ModuleKind.ESNext, + moduleResolution: ts.ModuleResolutionKind.Bundler, +}); +const CHECKER = PROGRAM.getTypeChecker(); + +function normalise(text) { + return text.replace(/\s+/g, ' ').replace(/ ;/g, ';').trim(); +} + +function print(node) { + return normalise(PRINTER.printNode(ts.EmitHint.Unspecified, node, node.getSourceFile())); +} + +function resolved(symbol) { + return symbol.flags & ts.SymbolFlags.Alias ? CHECKER.getAliasedSymbol(symbol) : symbol; +} + +function isPrivate(member) { + const modifiers = ts.canHaveModifiers(member) ? ts.getModifiers(member) ?? [] : []; + const privateModifier = modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.PrivateKeyword); + return privateModifier || (member.name !== undefined && ts.isPrivateIdentifier(member.name)); +} + +function header(node, keyword) { + const typeParameters = node.typeParameters ? `<${node.typeParameters.map(print).join(', ')}>` : ''; + const heritage = (node.heritageClauses ?? []).map(print).join(' '); + return normalise(`${keyword} ${node.name?.text ?? 'default'}${typeParameters} ${heritage}`); +} + +function describeMembers(node, keyword) { + const members = node.members.filter((member) => !isPrivate(member)).map(print).sort(); + return [header(node, keyword), ...members.map((member) => ` ${member}`)]; +} + +function withoutExport(text) { + return text.replace(/^(export\s+)?(default\s+)?(declare\s+)?/, ''); +} + +function describeDeclaration(node) { + if (ts.isClassDeclaration(node)) return describeMembers(node, 'class'); + if (ts.isInterfaceDeclaration(node)) return describeMembers(node, 'interface'); + if (ts.isVariableDeclaration(node)) { + const list = node.parent; + const keyword = list.flags & ts.NodeFlags.Const ? 'const' : list.flags & ts.NodeFlags.Let ? 'let' : 'var'; + return [`${keyword} ${print(node)}`]; + } + return [withoutExport(print(node))]; +} + +function localDeclarations(symbol) { + return (symbol.declarations ?? []) + .filter((declaration) => declaration.getSourceFile().fileName.startsWith(DIST_ROOT)); +} + +function referencedSymbols(node, found) { + const visit = (child) => { + let name; + if (ts.isTypeReferenceNode(child)) name = child.typeName; + else if (ts.isExpressionWithTypeArguments(child)) name = child.expression; + else if (ts.isTypeQueryNode(child)) name = child.exprName; + if (name && ts.isIdentifier(name)) { + const symbol = CHECKER.getSymbolAtLocation(name); + if (symbol) found.add(resolved(symbol)); + } + ts.forEachChild(child, visit); + }; + visit(node); +} + +function describeSymbol(label, symbol, pending) { + const declarations = localDeclarations(symbol); + const blocks = declarations.map((declaration) => { + referencedSymbols(declaration, pending); + return describeDeclaration(declaration).map((line) => ` ${line}`).join('\n'); + }); + return `${label}\n${blocks.sort().join('\n')}`; +} + +function surface() { + const entryFile = PROGRAM.getSourceFile(ENTRY); + if (!entryFile) throw new Error(`${ENTRY} is missing; build the SDK first`); + const moduleSymbol = CHECKER.getSymbolAtLocation(entryFile); + if (!moduleSymbol) throw new Error(`${ENTRY} is not a module`); + const exported = CHECKER.getExportsOfModule(moduleSymbol); + const emitted = new Set(exported.map(resolved)); + const pending = new Set(); + const sections = exported + .map((symbol) => describeSymbol(`export ${symbol.getName()}`, resolved(symbol), pending)); + const referenced = []; + for (const symbol of pending) { + if (emitted.has(symbol) || localDeclarations(symbol).length === 0) continue; + emitted.add(symbol); + referenced.push(describeSymbol(`referenced ${symbol.getName()}`, symbol, pending)); + } + return [...sections.sort(), ...referenced.sort()].join('\n'); +} + +process.stdout.write(`${surface()}\n`); diff --git a/main/scripts/check/duplicates-allowed.json b/main/scripts/check/duplicates-allowed.json new file mode 100644 index 0000000..8114e6a --- /dev/null +++ b/main/scripts/check/duplicates-allowed.json @@ -0,0 +1,40 @@ +{ + "groups": [ + { + "sha256": "4b3909d13af4ba99e95196ca70bd183ca7c861dfc9e7bc431257081a525ce2d7", + "count": 2, + "members": [ + "opengeometry/src/operations/creating/sweep/circle.rs:253", + "opengeometry/src/operations/creating/sweep/mod.rs:267" + ], + "reason": "sweep line_edge pair: messages differ; the merge is deferred to the debt note (B5 extended)" + }, + { + "sha256": "6b542763acdc5252b13bb34053775241ed7e9c9d4ce8ac1f445c3a59bf1bfa3a", + "count": 2, + "members": [ + "opengeometry/src/brep/geometry/store.rs:107", + "opengeometry/src/brep/geometry/store.rs:117" + ], + "reason": "CurveView point_at and tangent_at: equal shape, but they return different fields of the evaluation and call different elementary fns" + }, + { + "sha256": "1bd7edf464269be4c44d32dadbdd40d9504bd136deac717907af75e5e201e14d", + "count": 2, + "members": [ + "opengeometry/src/brep/topology/fixtures.rs:93", + "opengeometry/src/intersection/universal_ssi/tests.rs:7" + ], + "reason": "universal_ssi's unit-test budget (geometric 1e-5, intersection 2.5e-6) differs from the shared cfg(test) fine_accuracy (1e-9, 1e-10); the bodies match only in shape" + }, + { + "sha256": "d889b94bfc6ee9cbf42f9550c21a1128fe9b3815e2813251f49dad3c61b9ada7", + "count": 2, + "members": [ + "opengeometry/src/brep/topology/fixtures.rs:102", + "opengeometry/test-support/src/accuracy.rs:3" + ], + "reason": "the same exchange-parameterised budget in both homes: src unit tests cannot call test-support's accuracy(), which links a separate kernel crate instance, and test-support cannot see the kernel's cfg(test) coarse_accuracy" + } + ] +} diff --git a/main/scripts/check/duplicates.mjs b/main/scripts/check/duplicates.mjs new file mode 100644 index 0000000..e170712 --- /dev/null +++ b/main/scripts/check/duplicates.mjs @@ -0,0 +1,102 @@ +import { createHash } from 'node:crypto'; +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { REPOSITORY_ROOT, listFiles, relative } from '../lib/paths.mjs'; +import { matchingClose, rustFunctionOpens, typeScriptFunctionOpens } from './function-bodies.mjs'; +import { lexRust } from './lexers/rust-lexer.mjs'; +import { lexTypeScript } from './lexers/typescript-lexer.mjs'; + +const ALLOWED_FILE = path.join(REPOSITORY_ROOT, 'scripts', 'check', 'duplicates-allowed.json'); +const MINIMUM_LINES = 8; +const RUST_ROOTS = ['opengeometry/src', 'opengeometry/examples', 'opengeometry/tests', 'opengeometry/test-support/src', + 'opengeometry/test-support/tests'] + .map((directory) => ({ path: directory, recursive: true })); +const SCRIPT_ROOTS = [ + { path: '.', recursive: false }, + ...['scripts', 'opengeometry-three/src', 'opengeometry-three/tests', 'opengeometry-three/examples-vite'] + .map((directory) => ({ path: directory, recursive: true })), +]; +const SCRIPT_EXTENSIONS = ['.ts', '.mjs', '.js']; +const RUST_WORDS = { + literals: new Set(['true', 'false']), + keywords: new Set(['as', 'async', 'await', 'break', 'const', 'continue', 'crate', 'dyn', 'else', 'enum', 'extern', + 'fn', 'for', 'if', 'impl', 'in', 'let', 'loop', 'match', 'mod', 'move', 'mut', 'pub', 'ref', 'return', 'self', + 'Self', 'static', 'struct', 'super', 'trait', 'type', 'union', 'unsafe', 'use', 'where', 'while']), +}; +const SCRIPT_WORDS = { + literals: new Set(['true', 'false', 'null', 'undefined']), + keywords: new Set(['abstract', 'any', 'as', 'async', 'await', 'bigint', 'boolean', 'break', 'case', 'catch', 'class', + 'const', 'continue', 'debugger', 'declare', 'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', + 'finally', 'for', 'from', 'function', 'get', 'if', 'implements', 'import', 'in', 'infer', 'instanceof', 'interface', + 'is', 'keyof', 'let', 'never', 'new', 'number', 'object', 'of', 'override', 'private', 'protected', 'public', + 'readonly', 'return', 'satisfies', 'set', 'static', 'string', 'super', 'switch', 'symbol', 'this', 'throw', 'try', + 'type', 'typeof', 'unknown', 'var', 'void', 'while', 'yield']), +}; + +function files(roots, extensions) { + return listFiles(roots, extensions, []).filter((file) => !file.endsWith('.d.ts')).sort(); +} + +function normalisedBody(tokens, words) { + const names = new Map(); + return tokens.map((token) => { + if (token.kind === 'literal' || (token.kind === 'identifier' && words.literals.has(token.text))) return ''; + if (token.kind !== 'identifier' || words.keywords.has(token.text)) return token.text; + if (!names.has(token.text)) names.set(token.text, ``); + return names.get(token.text); + }).join(' '); +} + +function bodies(file, tokens, opens, words) { + return opens.flatMap((open) => { + const close = matchingClose(tokens, open); + if (tokens[close].line - tokens[open].line + 1 < MINIMUM_LINES) return []; + const normalised = normalisedBody(tokens.slice(open, close + 1), words); + const sha256 = createHash('sha256').update(normalised).digest('hex'); + return [{ sha256, location: `${relative(file)}:${tokens[open].line}` }]; + }); +} + +function rustBodies() { + return files(RUST_ROOTS, ['.rs']).flatMap((file) => { + const tokens = lexRust(readFileSync(file, 'utf8')); + return bodies(file, tokens, rustFunctionOpens(tokens), RUST_WORDS); + }); +} + +function scriptBodies() { + return files(SCRIPT_ROOTS, SCRIPT_EXTENSIONS).flatMap((file) => { + const tokens = lexTypeScript(readFileSync(file, 'utf8')); + return bodies(file, tokens, typeScriptFunctionOpens(tokens), SCRIPT_WORDS); + }); +} + +function duplicateGroups() { + const byHash = new Map(); + for (const body of [...rustBodies(), ...scriptBodies()]) { + byHash.set(body.sha256, [...(byHash.get(body.sha256) ?? []), body.location]); + } + return [...byHash.entries()] + .filter(([, members]) => members.length > 1) + .map(([sha256, members]) => ({ sha256, count: members.length, members })) + .sort((a, b) => a.members[0].localeCompare(b.members[0]) || a.sha256.localeCompare(b.sha256)); +} + +function allowedCounts() { + const allowed = JSON.parse(readFileSync(ALLOWED_FILE, 'utf8')); + return new Map(allowed.groups.map((group) => [group.sha256, group.count])); +} + +function report(groups, allowed) { + let unexpected = 0; + for (const group of groups) { + const permitted = (allowed.get(group.sha256) ?? 0) >= group.count; + if (!permitted) unexpected += 1; + process.stdout.write(`${permitted ? 'allowed' : 'NEW'} ${group.sha256} x${group.count}\n`); + for (const member of group.members) process.stdout.write(` ${member}\n`); + } + process.stdout.write(`${groups.length} duplicate groups, ${unexpected} not in duplicates-allowed.json\n`); + return unexpected; +} + +process.exitCode = report(duplicateGroups(), allowedCounts()) > 0 ? 1 : 0; diff --git a/main/scripts/check/function-bodies.mjs b/main/scripts/check/function-bodies.mjs new file mode 100644 index 0000000..a37ef42 --- /dev/null +++ b/main/scripts/check/function-bodies.mjs @@ -0,0 +1,92 @@ +const CONTROL_KEYWORDS = new Set(['if', 'for', 'while', 'switch', 'catch', 'with', 'return', 'typeof', 'await', + 'yield']); +const ANNOTATION_STOPS = new Set(['=', 'class', 'extends', 'implements', 'interface', 'return', 'const', 'let', 'var', + 'new', 'import', 'export']); + +export function matchingClose(tokens, open) { + const pairs = { '{': '}', '(': ')', '[': ']' }; + const opener = tokens[open].text; + const closer = pairs[opener]; + let depth = 0; + for (let index = open; index < tokens.length; index += 1) { + if (tokens[index].text === opener) depth += 1; + else if (tokens[index].text === closer) { + depth -= 1; + if (depth === 0) return index; + } + } + return tokens.length - 1; +} + +export function matchingOpen(tokens, close) { + const pairs = { '}': '{', ')': '(', ']': '[' }; + const closer = tokens[close].text; + const opener = pairs[closer]; + let depth = 0; + for (let index = close; index >= 0; index -= 1) { + if (tokens[index].text === closer) depth += 1; + else if (tokens[index].text === opener) { + depth -= 1; + if (depth === 0) return index; + } + } + return 0; +} + +export function topLevelDelimiter(tokens, start) { + let depth = 0; + for (let index = start; index < tokens.length; index += 1) { + const text = tokens[index].text; + if (text === '(' || text === '[') depth += 1; + else if (text === ')' || text === ']') depth -= 1; + else if (depth === 0 && (text === ';' || text === '{')) return index; + } + return -1; +} + +function rustBodyOpen(tokens, fnIndex) { + const delimiter = topLevelDelimiter(tokens, fnIndex + 1); + return delimiter !== -1 && tokens[delimiter].text === '{' ? delimiter : -1; +} + +export function rustFunctionOpens(tokens) { + const opens = []; + tokens.forEach((token, index) => { + if (token.text !== 'fn') return; + if (tokens[index + 1]?.kind !== 'identifier') return; + const open = rustBodyOpen(tokens, index); + if (open !== -1) opens.push(open); + }); + return opens; +} + +function annotatedParen(tokens, open) { + if (tokens[open - 1]?.text === ')') return open - 1; + let depth = 0; + for (let index = open - 1; index > 0 && index > open - 200; index -= 1) { + const text = tokens[index].text; + if ([')', ']', '}', '>'].includes(text)) depth += 1; + else if (['(', '[', '{', '<'].includes(text)) depth -= 1; + if (depth < 0 || (depth === 0 && (text === ';' || ANNOTATION_STOPS.has(text)))) return -1; + if (depth === 0 && text === ':' && tokens[index - 1].text === ')') return index - 1; + } + return -1; +} + +function isTypeScriptFunctionOpen(tokens, open) { + if (tokens[open - 1]?.text === '=>') return true; + const close = annotatedParen(tokens, open); + if (close === -1) return false; + const before = tokens[matchingOpen(tokens, close) - 1]; + if (!before) return false; + if (before.text === 'function' || before.text === '>' || before.text === '?') return true; + return before.kind === 'identifier' && !CONTROL_KEYWORDS.has(before.text); +} + +export function typeScriptFunctionOpens(tokens) { + const opens = []; + tokens.forEach((token, index) => { + if (token.text === '{' && isTypeScriptFunctionOpen(tokens, index)) opens.push(index); + }); + return opens; +} diff --git a/main/scripts/check/import-cycles.mjs b/main/scripts/check/import-cycles.mjs new file mode 100644 index 0000000..2023e06 --- /dev/null +++ b/main/scripts/check/import-cycles.mjs @@ -0,0 +1,93 @@ +import path from 'node:path'; +import ts from 'typescript'; +import { REPOSITORY_ROOT, relative } from '../lib/paths.mjs'; + +const CONFIGS = [ + 'tsconfig.json', 'tsconfig.worker.json', 'tsconfig.examples.json', 'tsconfig.tests.json', 'tsconfig.scripts.json', +]; +const FIRST_PARTY = ['opengeometry-three', 'scripts'].map((root) => `${path.join(REPOSITORY_ROOT, root)}${path.sep}`); + +function isFirstParty(file) { + return FIRST_PARTY.some((root) => path.resolve(file).startsWith(root)) && !file.endsWith('.d.ts'); +} + +function parsedConfig(name) { + const file = path.join(REPOSITORY_ROOT, name); + const read = ts.readConfigFile(file, ts.sys.readFile); + if (read.error) throw new Error(ts.flattenDiagnosticMessageText(read.error.messageText, '\n')); + return ts.parseJsonConfigFileContent(read.config, ts.sys, path.dirname(file)); +} + +function isValueImport(statement) { + if (ts.isImportDeclaration(statement)) return !statement.importClause?.isTypeOnly; + if (ts.isExportDeclaration(statement)) return Boolean(statement.moduleSpecifier) && !statement.isTypeOnly; + return false; +} + +function valueTargets(source, options) { + return source.statements + .filter(isValueImport) + .map((statement) => statement.moduleSpecifier) + .filter((specifier) => specifier && ts.isStringLiteral(specifier)) + .map((specifier) => ts.resolveModuleName(specifier.text, source.fileName, options, ts.sys).resolvedModule) + .filter((resolved) => resolved && isFirstParty(resolved.resolvedFileName)) + .map((resolved) => path.resolve(resolved.resolvedFileName)); +} + +function importGraph() { + const graph = new Map(); + for (const name of CONFIGS) { + const config = parsedConfig(name); + const program = ts.createProgram(config.fileNames, config.options); + for (const source of program.getSourceFiles()) { + if (!isFirstParty(source.fileName)) continue; + const file = path.resolve(source.fileName); + graph.set(file, new Set([...(graph.get(file) ?? []), ...valueTargets(source, config.options)])); + } + } + return graph; +} + +function stronglyConnected(graph) { + const index = new Map(); + const low = new Map(); + const stack = []; + const onStack = new Set(); + const components = []; + const connect = (node) => { + index.set(node, index.size); + low.set(node, index.get(node)); + stack.push(node); + onStack.add(node); + for (const next of graph.get(node) ?? []) { + if (!index.has(next)) { + connect(next); + low.set(node, Math.min(low.get(node), low.get(next))); + } else if (onStack.has(next)) low.set(node, Math.min(low.get(node), index.get(next))); + } + if (low.get(node) !== index.get(node)) return; + const component = []; + let member; + do { + member = stack.pop(); + onStack.delete(member); + component.push(member); + } while (member !== node); + components.push(component); + }; + for (const node of [...graph.keys()].sort()) if (!index.has(node)) connect(node); + return components; +} + +function cycles(graph) { + return stronglyConnected(graph) + .filter((component) => component.length > 1 || graph.get(component[0])?.has(component[0])) + .map((component) => component.map(relative).sort()); +} + +const GRAPH = importGraph(); +const CYCLES = cycles(GRAPH); +for (const cycle of CYCLES) process.stdout.write(`value-import cycle: ${cycle.join(' -> ')}\n`); +const EDGES = [...GRAPH.values()].reduce((sum, targets) => sum + targets.size, 0); +process.stdout.write(`${GRAPH.size} files, ${EDGES} value imports, ${CYCLES.length} value-import cycles\n`); +process.exitCode = CYCLES.length > 0 ? 1 : 0; diff --git a/main/scripts/check/lexers/rust-lexer.mjs b/main/scripts/check/lexers/rust-lexer.mjs new file mode 100644 index 0000000..b0029db --- /dev/null +++ b/main/scripts/check/lexers/rust-lexer.mjs @@ -0,0 +1,79 @@ +import { punctuatorEnd, skipLineComment, tokenize } from './tokenize.mjs'; + +const IDENTIFIER_START = /[A-Za-z_]/; +const IDENTIFIER_PART = /[A-Za-z0-9_]/; +const NUMBER = /^(?:0[xob][0-9a-fA-F_]+|[0-9][0-9_]*(?:\.[0-9][0-9_]*)?(?:[eE][+-]?[0-9_]+)?)(?:[a-z][a-z0-9]*)?/; +const PUNCTUATORS = ['<<=', '>>=', '...', '..=', '::', '->', '=>', '==', '!=', '<=', '>=', '&&', '||', '+=', '-=', '*=', + '/=', '%=', '^=', '&=', '|=', '<<', '>>', '..']; + +function skipBlockComment(source, index) { + let depth = 0; + let cursor = index; + while (cursor < source.length) { + if (source.startsWith('/*', cursor)) { + depth += 1; + cursor += 2; + } else if (source.startsWith('*/', cursor)) { + depth -= 1; + cursor += 2; + if (depth === 0) return cursor; + } else cursor += 1; + } + return cursor; +} + +function quotedEnd(source, index) { + let cursor = index + 1; + while (cursor < source.length && source[cursor] !== '"') cursor += source[cursor] === '\\' ? 2 : 1; + return cursor + 1; +} + +function rawStringEnd(source, index) { + const match = /^[bc]?r(#*)"/.exec(source.slice(index, index + 260)); + if (!match) return -1; + const closing = `"${match[1]}`; + const end = source.indexOf(closing, index + match[0].length); + return end === -1 ? source.length : end + closing.length; +} + +function quoteEnd(source, index) { + if (source[index + 1] === '\\') { + const end = source.indexOf("'", index + 2 + (source[index + 2] === "'" ? 1 : 0)); + return { end: end + 1, kind: 'literal' }; + } + const width = source.codePointAt(index + 1) > 0xffff ? 2 : 1; + if (source[index + 1 + width] === "'") return { end: index + 2 + width, kind: 'literal' }; + let cursor = index + 1; + while (cursor < source.length && IDENTIFIER_PART.test(source[cursor])) cursor += 1; + return { end: cursor, kind: 'lifetime' }; +} + +function literalEnd(source, index) { + const raw = rawStringEnd(source, index); + if (raw !== -1) return raw; + if (/^[bc]"/.test(source.slice(index, index + 2))) return quotedEnd(source, index + 1); + if (source.startsWith("b'", index)) return quoteEnd(source, index + 1).end; + return -1; +} + +function nextToken(source, index) { + const char = source[index]; + if (source.startsWith('//', index)) return { skip: skipLineComment(source, index) }; + if (source.startsWith('/*', index)) return { skip: skipBlockComment(source, index) }; + const literal = literalEnd(source, index); + if (literal !== -1) return { end: literal, kind: 'literal' }; + if (char === '"') return { end: quotedEnd(source, index), kind: 'literal' }; + if (char === "'") return quoteEnd(source, index); + if (IDENTIFIER_START.test(char)) { + let cursor = index + (source.startsWith('r#', index) && IDENTIFIER_START.test(source[index + 2] ?? '') ? 2 : 0); + while (cursor < source.length && IDENTIFIER_PART.test(source[cursor])) cursor += 1; + return { end: cursor, kind: 'identifier' }; + } + const number = NUMBER.exec(source.slice(index, index + 64)); + if (number) return { end: index + number[0].length, kind: 'literal' }; + return { end: punctuatorEnd(source, index, PUNCTUATORS), kind: 'punctuator' }; +} + +export function lexRust(source) { + return tokenize(source, nextToken); +} diff --git a/main/scripts/check/lexers/tokenize.mjs b/main/scripts/check/lexers/tokenize.mjs new file mode 100644 index 0000000..458e7ab --- /dev/null +++ b/main/scripts/check/lexers/tokenize.mjs @@ -0,0 +1,40 @@ +export function skipLineComment(source, index) { + const end = source.indexOf('\n', index); + return end === -1 ? source.length : end; +} + +export function skipBlockComment(source, index) { + const end = source.indexOf('*/', index + 2); + return end === -1 ? source.length : end + 2; +} + +export function quotedEnd(source, index) { + const quote = source[index]; + let cursor = index + 1; + while (cursor < source.length && source[cursor] !== quote) cursor += source[cursor] === '\\' ? 2 : 1; + return cursor + 1; +} + +export function punctuatorEnd(source, index, punctuators) { + const match = punctuators.find((candidate) => source.startsWith(candidate, index)); + return index + (match ? match.length : 1); +} + +export function tokenize(source, nextToken) { + const tokens = []; + let index = 0; + let line = 1; + while (index < source.length) { + if (/\s/.test(source[index])) { + if (source[index] === '\n') line += 1; + index += 1; + continue; + } + const token = nextToken(source, index, tokens[tokens.length - 1]); + const end = token.skip ?? token.end; + if (token.kind) tokens.push({ text: source.slice(index, end), kind: token.kind, line }); + for (let cursor = index; cursor < end; cursor += 1) if (source[cursor] === '\n') line += 1; + index = end; + } + return tokens; +} diff --git a/main/scripts/check/lexers/typescript-lexer.mjs b/main/scripts/check/lexers/typescript-lexer.mjs new file mode 100644 index 0000000..f7ff2e1 --- /dev/null +++ b/main/scripts/check/lexers/typescript-lexer.mjs @@ -0,0 +1,77 @@ +import { punctuatorEnd, quotedEnd, skipBlockComment, skipLineComment, tokenize } from './tokenize.mjs'; + +const IDENTIFIER_START = /[A-Za-z_$#]/; +const IDENTIFIER_PART = /[A-Za-z0-9_$]/; +const NUMBER = /^(?:0[xob][0-9a-fA-F_]+n?|(?:[0-9][0-9_]*)?\.?[0-9][0-9_]*(?:[eE][+-]?[0-9_]+)?n?)/; +const PUNCTUATORS = ['>>>=', '...', '===', '!==', '**=', '<<=', '>>=', '>>>', '&&=', '||=', '??=', '=>', '==', '!=', + '<=', '>=', '&&', '||', '??', '?.', '**', '++', '--', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '<<', '>>']; +const REGEX_PREFIX = new Set(['(', ',', '=', ':', '[', '!', '&', '|', '?', '{', '}', ';', '+', '-', '*', '%', '<', '>', + '~', '^', '=>', '==', '===', '!=', '!==', '&&', '||', '??', 'return', 'typeof', 'case', 'in', 'of', 'new', 'void', + 'throw', 'delete', 'else', 'do', 'yield', 'await']); + +function codeEnd(source, index) { + let depth = 1; + let cursor = index; + while (cursor < source.length && depth > 0) { + const char = source[cursor]; + if (char === "'" || char === '"') cursor = quotedEnd(source, cursor); + else if (char === '`') cursor = templateEnd(source, cursor); + else if (source.startsWith('//', cursor)) cursor = skipLineComment(source, cursor); + else if (source.startsWith('/*', cursor)) cursor = skipBlockComment(source, cursor); + else { + if (char === '{') depth += 1; + if (char === '}') depth -= 1; + cursor += 1; + } + } + return cursor; +} + +function templateEnd(source, index) { + let cursor = index + 1; + while (cursor < source.length && source[cursor] !== '`') { + if (source[cursor] === '\\') cursor += 2; + else if (source.startsWith('${', cursor)) cursor = codeEnd(source, cursor + 2); + else cursor += 1; + } + return cursor + 1; +} + +function regexEnd(source, index) { + let cursor = index + 1; + let inClass = false; + while (cursor < source.length && source[cursor] !== '\n') { + const char = source[cursor]; + if (char === '\\') cursor += 1; + else if (char === '[') inClass = true; + else if (char === ']') inClass = false; + else if (char === '/' && !inClass) break; + cursor += 1; + } + cursor += 1; + while (cursor < source.length && /[a-z]/.test(source[cursor])) cursor += 1; + return cursor; +} + +function nextToken(source, index, previous) { + const char = source[index]; + if (source.startsWith('//', index)) return { skip: skipLineComment(source, index) }; + if (source.startsWith('/*', index)) return { skip: skipBlockComment(source, index) }; + if (char === "'" || char === '"') return { end: quotedEnd(source, index), kind: 'literal' }; + if (char === '`') return { end: templateEnd(source, index), kind: 'literal' }; + if (char === '/' && (previous === undefined || REGEX_PREFIX.has(previous.text))) { + return { end: regexEnd(source, index), kind: 'literal' }; + } + if (IDENTIFIER_START.test(char)) { + let cursor = index + 1; + while (cursor < source.length && IDENTIFIER_PART.test(source[cursor])) cursor += 1; + return { end: cursor, kind: 'identifier' }; + } + const number = /[0-9.]/.test(char) ? NUMBER.exec(source.slice(index, index + 64)) : null; + if (number && number[0] !== '.') return { end: index + number[0].length, kind: 'literal' }; + return { end: punctuatorEnd(source, index, PUNCTUATORS), kind: 'punctuator' }; +} + +export function lexTypeScript(source) { + return tokenize(source, nextToken); +} diff --git a/main/scripts/check/lint-exceptions.json b/main/scripts/check/lint-exceptions.json new file mode 100644 index 0000000..5a882d4 --- /dev/null +++ b/main/scripts/check/lint-exceptions.json @@ -0,0 +1,31 @@ +{ + "returnTypeNames": { + "owner": "permanent", + "reason": "No public member needs an implicit return type now that kernel payloads have DTO types; the list stays so a later exemption is declared here.", + "names": [] + }, + "exceptions": [ + { + "owner": "permanent", + "files": ["opengeometry-three/src/bodies/body.ts"], + "rules": ["@typescript-eslint/no-unsafe-type-assertion"], + "reason": "clone() returns a DisplayClone, which cannot be typed as this; a display copy is a plain Group, not a Body." + }, + { + "owner": "permanent", + "files": ["opengeometry-three/src/rendering/inline-backend.ts"], + "rules": ["@typescript-eslint/no-empty-function"], + "reason": "The no-op members satisfy TessellationProvider, whose shape C5 owns." + }, + { + "owner": "permanent", + "files": [ + "opengeometry-three/tests/browser/pages/acceptance.ts", + "opengeometry-three/tests/browser/pages/acceptance-probes.ts", + "opengeometry-three/tests/browser/pages/smoke.ts" + ], + "rules": ["@typescript-eslint/no-unsafe-return", "@typescript-eslint/no-unsafe-type-assertion"], + "reason": "The pages read graph() payloads through dist/testing.js, whose glue methods return any or JSON text, and assert them." + } + ] +} diff --git a/main/scripts/check/lint-exceptions.mjs b/main/scripts/check/lint-exceptions.mjs new file mode 100644 index 0000000..0d6298f --- /dev/null +++ b/main/scripts/check/lint-exceptions.mjs @@ -0,0 +1,69 @@ +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { ESLint } from 'eslint'; +import { RESTRICTED_SYNTAX, lintConfig } from '../../eslint.config.js'; +import { REPOSITORY_ROOT, relative } from '../lib/paths.mjs'; +import EXCEPTIONS from './lint-exceptions.json' with { type: 'json' }; + +const RETURN_TYPE_RULES = new Set([ + '@typescript-eslint/explicit-function-return-type', + '@typescript-eslint/explicit-module-boundary-types', +]); +const SYNTAX_GROUP_BY_MESSAGE = new Map(Object.entries(RESTRICTED_SYNTAX) + .flatMap(([group, options]) => options.map((option) => [option.message, group]))); + +async function unsuppressedMessages() { + const eslint = new ESLint({ cwd: REPOSITORY_ROOT, overrideConfigFile: true, overrideConfig: lintConfig([], []) }); + const results = await eslint.lintFiles(['.']); + return results.flatMap((result) => result.messages + .map((message) => ({ ...message, file: relative(result.filePath) }))); +} + +function suppressionKey(message) { + const group = message.ruleId === 'no-restricted-syntax' ? SYNTAX_GROUP_BY_MESSAGE.get(message.message) : undefined; + return group ? `syntax:${group}` : message.ruleId; +} + +function staleEntryParts(entry, index, found) { + const keys = [...(entry.rules ?? []), ...(entry.syntax ?? []).map((group) => `syntax:${group}`)]; + const label = `lint exception ${String(index + 1)} (${entry.owner})`; + const staleFiles = entry.files + .filter((file) => !keys.some((key) => found.has(`${file} ${key}`))) + .map((file) => `${label}: ${file} no longer needs ${keys.join(', ')}`); + const staleKeys = keys + .filter((key) => !entry.files.some((file) => found.has(`${file} ${key}`))) + .map((key) => `${label}: ${key} no longer suppresses anything in its files`); + return [...staleFiles, ...staleKeys]; +} + +function offsetOf(text, line, column) { + const before = text.split('\n').slice(0, line - 1).join('\n'); + return (line > 1 ? before.length + 1 : 0) + column - 1; +} + +function reportedText(message) { + const text = readFileSync(path.join(REPOSITORY_ROOT, message.file), 'utf8'); + const end = offsetOf(text, message.endLine ?? message.line, message.endColumn ?? message.column); + return text.slice(offsetOf(text, message.line, message.column), end); +} + +function staleReturnTypeNames(messages) { + const heads = messages + .filter((message) => RETURN_TYPE_RULES.has(message.ruleId ?? '') && message.messageId === 'missingReturnType') + .map(reportedText); + return EXCEPTIONS.returnTypeNames.names + .filter((name) => !heads.some((head) => new RegExp(`\\b${name}\\b`).test(head))) + .map((name) => `returnTypeNames: ${name} no longer needs an exemption`); +} + +const MESSAGES = await unsuppressedMessages(); +const FOUND = new Set(MESSAGES.map((message) => `${message.file} ${suppressionKey(message)}`)); +const STALE = [ + ...EXCEPTIONS.exceptions.flatMap((entry, index) => staleEntryParts(entry, index, FOUND)), + ...staleReturnTypeNames(MESSAGES), +]; +for (const stale of STALE) process.stdout.write(`${stale}\n`); +const CHECKED = `${String(EXCEPTIONS.exceptions.length)} lint exceptions and ` + + `${String(EXCEPTIONS.returnTypeNames.names.length)} return-type names checked`; +process.stdout.write(`${CHECKED}, ${String(STALE.length)} stale\n`); +process.exitCode = STALE.length > 0 ? 1 : 0; diff --git a/main/scripts/check/source-rules.json b/main/scripts/check/source-rules.json new file mode 100644 index 0000000..e3bc87c --- /dev/null +++ b/main/scripts/check/source-rules.json @@ -0,0 +1,24 @@ +{ + "roots": [ + { "path": ".", "recursive": true } + ], + "skipDirectories": ["node_modules", "target", "pkg", "dist", "examples-dist", "test-results"], + "fileTypes": { + "script": [".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"], + "markup": [".html"], + "style": [".css"], + "json": [".json", ".jsonc"] + }, + "kebabCase": "^[a-z0-9]+(-[a-z0-9]+)*$", + "rustRoots": [ + { "path": "opengeometry/tests", "recursive": true }, + { "path": "opengeometry/test-support", "recursive": true }, + { "path": "opengeometry/examples", "recursive": true } + ], + "exceptionListRoots": [ + { "path": "opengeometry/tests/source_rules", "recursive": false } + ], + "vendoredDirectories": [], + "maxFileLines": 600, + "forbiddenMentions": ["[c]laude", "[a]nthropic", "[o]pencascade", "[p]ythonocc", "\\b[o]cc\\b", "\\b[o]cp\\b"] +} diff --git a/main/scripts/check/source-rules.mjs b/main/scripts/check/source-rules.mjs new file mode 100644 index 0000000..a9bff13 --- /dev/null +++ b/main/scripts/check/source-rules.mjs @@ -0,0 +1,193 @@ +import { readdirSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import ts from 'typescript'; +import { REPOSITORY_ROOT, listFiles, relative } from '../lib/paths.mjs'; +import { lexRust } from './lexers/rust-lexer.mjs'; +import { quotedEnd, skipBlockComment } from './lexers/tokenize.mjs'; +import CONFIG from './source-rules.json' with { type: 'json' }; + +const KEBAB_CASE = new RegExp(CONFIG.kebabCase); +const KIND_BY_EXTENSION = new Map(Object.entries(CONFIG.fileTypes) + .flatMap(([kind, extensions]) => extensions.map((extension) => [extension, kind]))); +const COMMENT_START = /\/\/|\/\*/g; +const TOML_STRING_OR_COMMENT = /"""[\s\S]*?"""|'''[\s\S]*?'''|"(?:\\.|[^"\\\n])*"|'[^'\n]*'|#/g; +const LIST_COMMENT = /^\s*(?:#|\/\/)/; +const FORBIDDEN_MENTION = new RegExp(CONFIG.forbiddenMentions.join('|'), 'gi'); +const COMMENT_SKIPPED_DIRECTORIES = [...CONFIG.skipDirectories, ...CONFIG.vendoredDirectories]; + +function kindOf(file) { + return KIND_BY_EXTENSION.get(path.extname(file)); +} + +function lineAt(text, index) { + return text.slice(0, index).split('\n').length; +} + +function syntaxComments(source) { + const lines = new Set(); + const visit = (node) => { + if (ts.isJSDoc(node)) return; + const children = node.getChildren(source); + if (children.length === 0) { + const trivia = source.text.slice(node.pos, node.getStart(source)); + for (const match of trivia.matchAll(COMMENT_START)) lines.add(lineAt(source.text, node.pos + match.index)); + } + for (const child of children) visit(child); + }; + visit(source); + return [...lines]; +} + +function scriptComments(text, file) { + return syntaxComments(ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true)); +} + +function jsonComments(text, file) { + return syntaxComments(ts.parseJsonText(file, text)); +} + +function styleComments(text) { + const lines = []; + let index = 0; + while (index < text.length) { + if (text[index] === '"' || text[index] === "'") index = quotedEnd(text, index); + else if (text.startsWith('/*', index)) { + lines.push(lineAt(text, index)); + index = skipBlockComment(text, index); + } else index += 1; + } + return lines; +} + +function tagEnd(text, index) { + let cursor = index + 1; + while (cursor < text.length && text[cursor] !== '>') { + cursor = text[cursor] === '"' || text[cursor] === "'" ? quotedEnd(text, cursor) : cursor + 1; + } + return cursor + 1; +} + +function embeddedComments(text, index, element) { + const open = tagEnd(text, index); + const close = text.toLowerCase().indexOf(` line + offset), end }; +} + +function markupComments(text) { + const lines = []; + let index = 0; + while (index < text.length) { + const element = /^<(script|style)\b/i.exec(text.slice(index, index + 8))?.[1]?.toLowerCase(); + if (text.startsWith('', index + 4); + index = end === -1 ? text.length : end + 3; + } else if (element) { + const embedded = embeddedComments(text, index, element); + lines.push(...embedded.lines); + index = embedded.end; + } else index = text[index] === '<' ? tagEnd(text, index) : index + 1; + } + return lines; +} + +function opensComment(text, index) { + return text.startsWith('//', index) || text.startsWith('/*', index); +} + +function closesGap(text, from, index) { + const gap = text.slice(from, index); + return !opensComment(text, index) && (/^\s*$/.test(gap) || lexRust(`${gap} x`).length === 1); +} + +function tokenStart(text, from, token) { + let index = text.indexOf(token, from); + while (!closesGap(text, from, index)) index = text.indexOf(token, index + 1); + return index; +} + +function gapComments(text, from, to) { + return [...text.slice(from, to).matchAll(COMMENT_START)].map((match) => lineAt(text, from + match.index)); +} + +function rustComments(text) { + const lines = new Set(); + let from = 0; + for (const token of lexRust(text)) { + const start = tokenStart(text, from, token.text); + for (const line of gapComments(text, from, start)) lines.add(line); + from = start + token.text.length; + } + for (const line of gapComments(text, from, text.length)) lines.add(line); + return [...lines]; +} + +function tomlComments(text) { + return [...text.matchAll(TOML_STRING_OR_COMMENT)] + .filter((match) => match[0] === '#') + .map((match) => lineAt(text, match.index)); +} + +function listComments(text) { + return text.split('\n').flatMap((line, index) => (LIST_COMMENT.test(line) ? [index + 1] : [])); +} + +const COMMENT_FINDERS = { + script: scriptComments, json: jsonComments, style: styleComments, markup: markupComments, + rust: rustComments, toml: tomlComments, list: listComments, +}; + +function commentViolations(file, kind = kindOf(file)) { + const text = readFileSync(file, 'utf8'); + const finder = COMMENT_FINDERS[kind]; + return finder(text, file).map((line) => `${relative(file)}:${line} has a comment`); +} + +function lengthViolations(file) { + const text = readFileSync(file, 'utf8'); + const lines = text.split('\n').length - (text.endsWith('\n') ? 1 : 0); + return lines > CONFIG.maxFileLines ? [`${relative(file)} has ${lines} lines, above ${CONFIG.maxFileLines}`] : []; +} + +function mentionViolations(file) { + const text = readFileSync(file, 'utf8'); + const lines = new Set([...text.matchAll(FORBIDDEN_MENTION)].map((match) => lineAt(text, match.index))); + return [...lines].map((line) => `${relative(file)}:${line} has a forbidden mention`); +} + +function namingViolations(file) { + const segments = relative(file).split('/'); + const badDirectory = segments.slice(0, -1).find((segment) => !KEBAB_CASE.test(segment)); + if (badDirectory) return [`${relative(file)} has a directory name that is not kebab-case: ${badDirectory}`]; + const name = path.basename(file); + return name.split('.').every((part) => KEBAB_CASE.test(part)) ? [] : [`${relative(file)} is not kebab-case`]; +} + +function textFiles() { + return readdirSync(REPOSITORY_ROOT, { withFileTypes: true, recursive: true }) + .filter((entry) => entry.isFile()) + .map((entry) => path.join(entry.parentPath, entry.name)) + .filter((file) => !relative(file).split('/').some((segment) => CONFIG.skipDirectories.includes(segment))) + .filter((file) => !readFileSync(file).includes(0)); +} + +const FILES = listFiles(CONFIG.roots, [...KIND_BY_EXTENSION.keys()], CONFIG.skipDirectories).sort(); +const RUST_FILES = listFiles(CONFIG.rustRoots, ['.rs'], COMMENT_SKIPPED_DIRECTORIES).sort(); +const TOML_FILES = listFiles(CONFIG.roots, ['.toml'], COMMENT_SKIPPED_DIRECTORIES).sort(); +const LIST_FILES = listFiles(CONFIG.exceptionListRoots, ['.txt'], CONFIG.skipDirectories).sort(); +const TEXT_FILES = textFiles().sort(); +const VIOLATIONS = [ + ...FILES.flatMap((file) => [...namingViolations(file), ...commentViolations(file)]), + ...RUST_FILES.flatMap((file) => [...commentViolations(file, 'rust'), ...lengthViolations(file)]), + ...TOML_FILES.flatMap((file) => commentViolations(file, 'toml')), + ...LIST_FILES.flatMap((file) => commentViolations(file, 'list')), + ...TEXT_FILES.flatMap(mentionViolations), +]; +const CHECKED = new Set([...FILES, ...RUST_FILES, ...TOML_FILES, ...LIST_FILES, ...TEXT_FILES]); +for (const violation of VIOLATIONS) process.stdout.write(`${violation}\n`); +process.stdout.write(`${CHECKED.size} files checked, ${VIOLATIONS.length} source rule violations\n`); +process.exitCode = VIOLATIONS.length > 0 ? 1 : 0; diff --git a/main/scripts/check/verify.mjs b/main/scripts/check/verify.mjs new file mode 100644 index 0000000..3a2032c --- /dev/null +++ b/main/scripts/check/verify.mjs @@ -0,0 +1,228 @@ +import { spawn } from 'node:child_process'; +import { closeSync, existsSync, mkdirSync, openSync, readFileSync, writeSync } from 'node:fs'; +import path from 'node:path'; +import { performance } from 'node:perf_hooks'; +import { pathToFileURL } from 'node:url'; +import { REPOSITORY_ROOT } from '../lib/paths.mjs'; + +const LOG_DIRECTORY = path.join(REPOSITORY_ROOT, '.check'); +const WORKER_BUNDLE = path.join(REPOSITORY_ROOT, 'dist/tessellation-worker.js'); +const THREE_IMPORT = /(?:\bfrom\s*|\bimport\s*\(?\s*)['"]three(?:\/[^'"]*)?['"]/; +const WASM_CORE = 'tests/wasm_core.rs'; +const WASM_RUNNING = /^\s*Running (?:unittests )?(\S+)/; +const WASM_PASSED = /^\s*test result: ok\. (\d+) passed/; + +function crateSteps(name, directory, lintTargets, withTests) { + const steps = [ + { name: `rust:${name}:fmt`, cwd: directory, commands: [['cargo', 'fmt', '--all', '--', '--check']] }, + { + name: `rust:${name}:clippy`, + cwd: directory, + commands: [[ + 'cargo', 'clippy', '--offline', '--target-dir', 'target/clippy', lintTargets, '--', '-D', 'warnings', + ]], + }, + ]; + if (!withTests) return steps; + return [ + ...steps, + { + name: `rust:${name}:test`, + cwd: directory, + commands: [['cargo', 'test', '--offline', '--all-targets', '--no-fail-fast']], + }, + ]; +} + +function npmStep(script) { + return { name: script, cwd: '.', commands: [['npm', 'run', script]] }; +} + +function browserStep(threeVersion, portBase) { + return { + name: `browser:three-${threeVersion}`, + cwd: '.', + threeVersion, + portBase, + concurrent: true, + commands: [['npm', 'run', 'test:browser']], + }; +} + +const CHECK_STEPS = [ + ...crateSteps('opengeometry', 'opengeometry', '--all-targets', true), + ...crateSteps('test-support', 'opengeometry/test-support', '--all-targets', true), + { name: 'test:wasm', cwd: 'opengeometry', commands: [['wasm-pack', 'test', '--node']], logCheck: wasmFloorFindings }, + npmStep('build'), + { name: 'worker-bundle', check: workerBundleFindings }, + ...['lint:check', 'typecheck', 'check:source', 'check:cycles', 'check:duplicates', 'build-example'] + .map(npmStep), + ...['test:bindings', 'test:node', 'test:performance'].map(npmStep), +]; + +const FULL_STEPS = [ + ...CHECK_STEPS, + browserStep('168', '4175'), + browserStep('184', '4185'), +]; + +function workerBundleFindings() { + if (!existsSync(WORKER_BUNDLE)) return [`missing ${WORKER_BUNDLE}`]; + return readFileSync(WORKER_BUNDLE, 'utf8') + .split('\n') + .map((line, index) => ({ line, number: index + 1 })) + .filter((entry) => THREE_IMPORT.test(entry.line)) + .map((entry) => `dist/tessellation-worker.js:${String(entry.number)} imports three: ${entry.line.trim()}`); +} + +export function wasmFloorFindings(log) { + let binary = ''; + let passed = 0; + let coreRanNone = false; + for (const line of log.split(/\r\n|\r|\n/)) { + binary = WASM_RUNNING.exec(line)?.[1] ?? binary; + passed += Number(WASM_PASSED.exec(line)?.[1] ?? 0); + if (binary === WASM_CORE && line.trim() === 'no tests to run!') coreRanNone = true; + } + return [ + ...(passed === 0 ? ['no wasm test binary reported a passing test'] : []), + ...(coreRanNone ? [`${WASM_CORE} ran no tests`] : []), + ]; +} + +function errorText(error) { + return error instanceof Error ? error.message : String(error); +} + +function optionValue(argv, flag) { + const index = argv.indexOf(flag); + if (index < 0) return undefined; + const value = argv[index + 1]; + if (value === undefined || value.startsWith('--')) throw new Error(`${flag} needs a value`); + return value; +} + +function listOption(argv, flag) { + return optionValue(argv, flag)?.split(',').filter(Boolean); +} + +function checkKnown(names, steps) { + const known = new Set(steps.map((step) => step.name)); + const unknown = names.filter((name) => !known.has(name)); + if (unknown.length === 0) return; + throw new Error(`unknown step ${unknown.join(', ')}; steps are ${[...known].join(', ')}`); +} + +export function selectSteps(argv) { + const steps = argv.includes('--full') ? FULL_STEPS : CHECK_STEPS; + const only = listOption(argv, '--only'); + const skip = listOption(argv, '--skip') ?? []; + const from = optionValue(argv, '--from'); + checkKnown([...(only ?? []), ...skip, ...(from === undefined ? [] : [from])], steps); + const start = from === undefined ? 0 : steps.findIndex((step) => step.name === from); + const selected = steps.slice(start) + .filter((step) => (only === undefined || only.includes(step.name)) && !skip.includes(step.name)); + if (selected.length === 0) throw new Error('nothing selected'); + return selected; +} + +function commandEnvironment(step) { + const env = { ...process.env }; + if (step.threeVersion !== undefined) env.OG_THREE_VERSION = step.threeVersion; + if (step.portBase !== undefined) env.OG_PW_PORT_BASE = step.portBase; + return env; +} + +function commandStatus(command, step, log) { + writeSync(log, `$ ${command.join(' ')}\n`); + return new Promise((resolve) => { + const child = spawn(command[0], command.slice(1), { + cwd: path.join(REPOSITORY_ROOT, step.cwd), + env: commandEnvironment(step), + stdio: ['ignore', log, log], + }); + child.on('error', (error) => { + writeSync(log, `${error.message}\n`); + resolve(127); + }); + child.on('close', (code) => resolve(code ?? 127)); + }); +} + +function checkFindings(check) { + try { + return check(); + } catch (error) { + return [`check threw: ${errorText(error)}`]; + } +} + +function writeFindings(findings, log) { + writeSync(log, findings.length === 0 ? 'no findings\n' : `${findings.join('\n')}\n`); + return findings.length === 0 ? 0 : 1; +} + +async function stepStatus(step, log) { + if (step.check) return writeFindings(checkFindings(step.check), log); + for (const command of step.commands) { + const status = await commandStatus(command, step, log); + if (status !== 0) return status; + } + return 0; +} + +async function attempt(step) { + const file = path.join(LOG_DIRECTORY, `${step.name}.log`); + const log = openSync(file, 'w'); + const status = await stepStatus(step, log); + const checked = status === 0 && step.logCheck + ? writeFindings(checkFindings(() => step.logCheck(readFileSync(file, 'utf8'))), log) + : status; + closeSync(log); + return checked; +} + +async function runStep(step) { + const started = performance.now(); + const status = await attempt(step); + const seconds = ((performance.now() - started) / 1000).toFixed(1); + const verdict = status === 0 ? 'ok ' : 'FAIL'; + process.stdout.write(`${verdict} ${step.name.padEnd(28)} exit ${String(status)} ${seconds}s\n`); + return { name: step.name, status }; +} + +function stepBatches(steps) { + const batches = []; + for (const step of steps) { + const last = batches.at(-1); + if (step.concurrent && last?.[0]?.concurrent) last.push(step); + else batches.push([step]); + } + return batches; +} + +async function runSteps(steps) { + const results = []; + for (const batch of stepBatches(steps)) results.push(...await Promise.all(batch.map(runStep))); + return results; +} + +async function main(argv) { + let steps; + try { + steps = selectSteps(argv); + } catch (error) { + process.stderr.write(`verify: ${errorText(error)}\n`); + process.exitCode = 1; + return; + } + mkdirSync(LOG_DIRECTORY, { recursive: true }); + const results = await runSteps(steps); + const failed = results.filter((result) => result.status !== 0).map((result) => result.name); + process.stdout.write(failed.length === 0 + ? `${String(steps.length)} steps passed; logs in .check/\n` + : `failed: ${failed.join(', ')}; logs in .check/\n`); + process.exitCode = failed.length === 0 ? 0 : 1; +} + +if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) await main(process.argv.slice(2)); diff --git a/main/scripts/check/verify.test.mjs b/main/scripts/check/verify.test.mjs new file mode 100644 index 0000000..ba213f3 --- /dev/null +++ b/main/scripts/check/verify.test.mjs @@ -0,0 +1,115 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { selectSteps, wasmFloorFindings } from './verify.mjs'; +import { existsSync } from 'node:fs'; +import path from 'node:path'; +import { REPOSITORY_ROOT } from '../lib/paths.mjs'; + +const PADDING = ' '.repeat(50); +const NO_TESTS = 'no tests to run!'; +const WASM_CORE = 'tests/wasm_core.rs'; +const PASSED = [ + `Executing bindgen...${PADDING}\r${PADDING}\rrunning 4 tests`, + 'test embedded_brep_round_trip_and_snapshot_buffers ... ok', + '', + 'test result: ok. 4 passed; 0 failed; 0 ignored; 0 filtered out; finished in 1.22s', + '', +]; +const BINARIES = [ + 'unittests src/lib.rs', 'tests/booleans/main.rs', 'tests/errors.rs', WASM_CORE, 'tests/world_graph/main.rs', +]; + +function wasmLog(outputs) { + const entries = BINARIES.map((binary, index) => [ + `${index === 0 ? '' : `${PADDING}\r`} Running ${binary} (target/wasm32-unknown-unknown/debug/deps/x.wasm)`, + ...(outputs.get(binary) ?? [NO_TESTS]), + ]); + return [ + '$ wasm-pack test --node', + ' Finished `test` profile [unoptimized + debuginfo] target(s) in 0.03s', + ...entries.flat(), + `${PADDING}\r[WARN]: There's a newer version of wasm-pack available`, + ].join('\n'); +} + +function names(argv) { + return selectSteps(argv).map((step) => step.name); +} + +function failsWith(message) { + return (error) => error instanceof Error && error.message === message; +} + +test('--only keeps the named steps in check order', () => { + assert.deepEqual(names(['--only', 'typecheck,build']), ['build', 'typecheck']); +}); + +test('the wasm test step is named test:wasm like its npm script', () => { + assert.deepEqual(names(['--only', 'test:wasm']), ['test:wasm']); +}); + +test('every cargo gate step runs in a directory that holds a Cargo.toml', () => { + const cargoSteps = selectSteps(['--full']).filter((step) => step.commands?.some((command) => command[0] === 'cargo')); + assert(cargoSteps.length > 0); + const missing = cargoSteps + .filter((step) => !existsSync(path.join(REPOSITORY_ROOT, step.cwd ?? '', 'Cargo.toml'))) + .map((step) => step.name); + assert.deepEqual(missing, []); +}); + +test('--from starts at the named step', () => { + assert.deepEqual(names(['--from', 'test:node']), ['test:node', 'test:performance']); +}); + +test('--skip drops the named steps', () => { + assert.deepEqual(names(['--from', 'test:bindings', '--skip', 'test:node']), ['test:bindings', 'test:performance']); +}); + +test('--full adds both browser steps', () => { + assert.deepEqual(names(['--full', '--from', 'test:performance']), [ + 'test:performance', 'browser:three-168', 'browser:three-184', + ]); +}); + +test('--only names before --from select nothing and fail', () => { + assert.throws(() => selectSteps(['--from', 'test:node', '--only', 'build']), failsWith('nothing selected')); +}); + +test('--skip of every selected step selects nothing and fails', () => { + assert.throws(() => selectSteps(['--only', 'build', '--skip', 'build']), failsWith('nothing selected')); +}); + +test('an unknown step name fails with one line naming it', () => { + assert.throws( + () => selectSteps(['--skip', 'nope']), + (error) => error instanceof Error && error.message.startsWith('unknown step nope;') + && !error.message.includes('\n'), + ); +}); + +test('an option without a value fails naming the option', () => { + assert.throws(() => selectSteps(['--only']), failsWith('--only needs a value')); + assert.throws(() => selectSteps(['--from', '--full']), failsWith('--from needs a value')); +}); + +test('a wasm log where every binary runs no tests fails the wasm floor', () => { + assert.deepEqual(wasmFloorFindings(wasmLog(new Map())), [ + 'no wasm test binary reported a passing test', + `${WASM_CORE} ran no tests`, + ]); +}); + +test('a wasm log whose wasm_core binary runs no tests fails the wasm floor', () => { + const log = wasmLog(new Map([['tests/errors.rs', PASSED]])); + assert.deepEqual(wasmFloorFindings(log), [`${WASM_CORE} ran no tests`]); +}); + +test('the recorded wasm log shape passes the wasm floor', () => { + assert.deepEqual(wasmFloorFindings(wasmLog(new Map([[WASM_CORE, PASSED]]))), []); +}); + +test('the wasm test step carries both its command and the floor', () => { + const [step] = selectSteps(['--only', 'test:wasm']); + assert.deepEqual(step?.commands, [['wasm-pack', 'test', '--node']]); + assert.equal(step?.logCheck, wasmFloorFindings); +}); diff --git a/main/scripts/lib/kernel.mjs b/main/scripts/lib/kernel.mjs new file mode 100644 index 0000000..4514e6d --- /dev/null +++ b/main/scripts/lib/kernel.mjs @@ -0,0 +1,7 @@ +import { readFileSync } from 'node:fs'; +import { OpenGeometry } from '../../dist/index.js'; + +export async function createInlineKernel() { + const bytes = readFileSync(new URL('../../dist/opengeometry_bg.wasm', import.meta.url)); + await OpenGeometry.create({ wasmModule: new WebAssembly.Module(bytes) }, { tessellation: 'inline' }); +} diff --git a/main/scripts/lib/node-builtins.d.ts b/main/scripts/lib/node-builtins.d.ts new file mode 100644 index 0000000..cc6979f --- /dev/null +++ b/main/scripts/lib/node-builtins.d.ts @@ -0,0 +1,101 @@ +declare module 'node:fs' { + export interface Dirent { + name: string; + parentPath: string; + isFile(): boolean; + isDirectory(): boolean; + } + export function readFileSync(path: string | URL, encoding: 'utf8'): string; + export function readFileSync(path: string | URL): Uint8Array; + export function readdirSync(path: string, options: { withFileTypes: true; recursive?: boolean }): Dirent[]; + export function existsSync(path: string): boolean; + export function mkdirSync(path: string, options?: { recursive?: boolean }): string | undefined; + export function openSync(path: string, flags: string): number; + export function writeSync(fd: number, text: string): number; + export function closeSync(fd: number): void; +} + +declare module 'node:child_process' { + export interface ChildProcess { + on(event: 'close', listener: (code: number | null) => void): ChildProcess; + on(event: 'error', listener: (error: Error) => void): ChildProcess; + } + export function spawn( + command: string, + args: string[], + options: { cwd: string; env: Record; stdio: ['ignore', number, number] }, + ): ChildProcess; +} + +declare module 'node:test' { + export function test(name: string, body: () => void | Promise): Promise; +} + +declare module 'node:fs/promises' { + export interface Stats { + isDirectory(): boolean; + } + export function cp(source: string, destination: string): Promise; + export function mkdir(path: string, options?: { recursive?: boolean }): Promise; + export function readFile(path: string, encoding: 'utf8'): Promise; + export function readdir(path: string): Promise; + export function stat(path: string): Promise; + export function writeFile(path: string, data: string | Uint8Array): Promise; +} + +declare module 'node:path' { + interface Path { + sep: string; + resolve(...segments: string[]): string; + dirname(path: string): string; + join(...segments: string[]): string; + relative(from: string, to: string): string; + basename(path: string, suffix?: string): string; + extname(path: string): string; + } + const path: Path; + export default path; +} + +declare module 'node:url' { + export function fileURLToPath(url: string | URL): string; + export function pathToFileURL(path: string): URL; +} + +declare module 'node:crypto' { + export interface Hash { + update(data: string | Uint8Array): Hash; + digest(encoding: 'hex'): string; + } + export function createHash(algorithm: string): Hash; +} + +declare module 'node:assert/strict' { + interface Assert { + (value: unknown, message?: string): asserts value; + equal(actual: unknown, expected: unknown, message?: string): void; + notEqual(actual: unknown, expected: unknown, message?: string): void; + deepEqual(actual: unknown, expected: unknown, message?: string): void; + throws(block: () => unknown, error?: (error: unknown) => boolean, message?: string): void; + } + const assert: Assert; + export default assert; +} + +declare module 'node:perf_hooks' { + export const performance: { now(): number }; +} + +declare const process: { + argv: string[]; + arch: string; + platform: string; + env: { OG_THREE_VERSION?: string; OG_PW_PORT_BASE?: string; CI?: string }; + exitCode: number | undefined; + stdout: { write(text: string): boolean }; + stderr: { write(text: string): boolean }; +}; + +interface ImportMeta { + readonly dirname: string; +} diff --git a/main/scripts/lib/paths.mjs b/main/scripts/lib/paths.mjs new file mode 100644 index 0000000..919e43b --- /dev/null +++ b/main/scripts/lib/paths.mjs @@ -0,0 +1,19 @@ +import { readdirSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export const REPOSITORY_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); + +export function relative(file) { + return path.relative(REPOSITORY_ROOT, file).split(path.sep).join('/'); +} + +export function listFiles(roots, extensions, skippedDirectories) { + return roots + .flatMap((root) => readdirSync(path.join(REPOSITORY_ROOT, root.path), { + withFileTypes: true, recursive: root.recursive, + })) + .filter((entry) => entry.isFile() && extensions.includes(path.extname(entry.name))) + .map((entry) => path.join(entry.parentPath, entry.name)) + .filter((file) => !relative(file).split('/').some((segment) => skippedDirectories.includes(segment))); +} diff --git a/main/scripts/snapshot/kernel-snapshot.mjs b/main/scripts/snapshot/kernel-snapshot.mjs new file mode 100644 index 0000000..fa38ddc --- /dev/null +++ b/main/scripts/snapshot/kernel-snapshot.mjs @@ -0,0 +1,29 @@ +import { spawn } from 'node:child_process'; +import path from 'node:path'; +import { REPOSITORY_ROOT } from '../lib/paths.mjs'; + +const DUMP_VARIABLE = 'OG_GOLDEN_DUMP'; +const COMPARING_TEST = 'every_golden_scene_matches_the_recorded_golden_for_this_target'; +const CARGO_ARGUMENTS = [ + 'test', '--locked', '--offline', '--manifest-path', 'opengeometry/Cargo.toml', '--test', 'golden', + '--', COMPARING_TEST, '--exact', '--nocapture', +]; + +function main() { + const directory = process.argv[2]; + if (!directory) throw new Error('usage: npm run snapshot:kernel -- '); + const child = spawn('cargo', CARGO_ARGUMENTS, { + cwd: REPOSITORY_ROOT, + env: { ...process.env, [DUMP_VARIABLE]: path.resolve(directory) }, + stdio: ['ignore', 1, 2], + }); + child.on('error', (error) => { + process.stderr.write(`${error.message}\n`); + process.exitCode = 127; + }); + child.on('close', (code) => { + process.exitCode = code ?? 127; + }); +} + +main(); diff --git a/main/scripts/snapshot/sdk-dump.mjs b/main/scripts/snapshot/sdk-dump.mjs new file mode 100644 index 0000000..e7d0c5e --- /dev/null +++ b/main/scripts/snapshot/sdk-dump.mjs @@ -0,0 +1,64 @@ +import { OpenGeometry } from '../../dist/index.js'; +import { digest, outcome, outcomeAsync } from './stable-output.mjs'; + +export function getters(item) { + const result = { + ogId: item.ogId, + handle: item.handle, + generation: item.generation, + placement: outcome(() => item.getPlacement()), + worldPlacement: outcome(() => item.getWorldPlacement()), + children: outcome(() => item.getChildren()), + parent: outcome(() => item.getParent()), + bounds: outcome(() => item.getBounds()), + }; + if (typeof item.getInstanceCount === 'function') { + result.bodyType = item.bodyType; + result.appearance = item.appearance; + result.instanceCount = outcome(() => item.getInstanceCount()); + } + if (typeof item.getReport === 'function') result.report = outcome(() => item.getReport()); + return result; +} + +export function allGetters(items) { + return Object.fromEntries(items.map((item) => [item.ogId, getters(item)])); +} + +function attributeDigest(attribute) { + return { + itemSize: attribute.itemSize, + count: attribute.count, + type: attribute.array.constructor.name, + sha256: digest(attribute.array), + }; +} + +function geometryDigest(geometry) { + const names = Object.keys(geometry.attributes).sort(); + return { + attributes: Object.fromEntries(names.map((name) => [name, attributeDigest(geometry.attributes[name])])), + index: geometry.index ? attributeDigest(geometry.index) : null, + }; +} + +function objectDigest(object) { + return { + visible: object.visible, + position: object.position.toArray(), + geometry: geometryDigest(object.geometry), + }; +} + +export function geometry(bodies) { + return Object.fromEntries(bodies.map((body) => [body.ogId, { + visible: body.visible, + matrixWorld: [...body.matrixWorld.elements], + surface: objectDigest(body.surface), + outline: objectDigest(body.outline), + }])); +} + +export async function exportStep(options) { + return outcomeAsync(() => OpenGeometry.exportStep(options)); +} diff --git a/main/scripts/snapshot/sdk-scene.mjs b/main/scripts/snapshot/sdk-scene.mjs new file mode 100644 index 0000000..170ed24 --- /dev/null +++ b/main/scripts/snapshot/sdk-scene.mjs @@ -0,0 +1,101 @@ +import * as THREE from 'three'; +import { + OpenGeometry, SystemAssembly, Wire, Solid, + OG_PRIMITIVE_RECTANGLE, OG_PRIMITIVE_PARAMS_RECTANGLE, + OG_PRIMITIVE_CIRCLE, OG_PRIMITIVE_PARAMS_CIRCLE, + OG_PRIMITIVE_POLYLINE, OG_PRIMITIVE_PARAMS_POLYLINE, + OG_PRIMITIVE_CUBOID, OG_PRIMITIVE_PARAMS_CUBOID, + OG_OPERATION_EXTRUDE, OG_OPERATION_PARAMS_EXTRUDE, + OG_OPERATION_SWEEP, OG_OPERATION_PARAMS_SWEEP, + OG_OPERATION_SUBTRACT, + OG_TRANSFORM_TRANSLATE, OG_TRANSFORM_ROTATE, OG_TRANSFORM_PLACE, +} from '../../dist/index.js'; +import { outcome } from './stable-output.mjs'; + +const PICK_ORIGIN = [-1.5, 5, 5]; +const PICK_DIRECTION = [0, 0, -1]; + +export function stepLog() { + const entries = []; + return { + entries, + run(step, fn) { + const result = outcome(fn); + entries.push({ step, ...result, ok: !('error' in result) }); + return result.value; + }, + }; +} + +function wallParams(profile, distance) { + return OG_OPERATION_PARAMS_EXTRUDE({ profile, distance }); +} + +export function buildScene(log) { + const scene = new THREE.Scene(); + const level = new SystemAssembly({ ogId: 'level-1' }); + const profile = new Wire(OG_PRIMITIVE_RECTANGLE, OG_PRIMITIVE_PARAMS_RECTANGLE({ width: 6, breadth: 0.2 })); + const wall = new Solid(OG_OPERATION_EXTRUDE, wallParams(profile, 3), { ogId: 'wall-1' }); + const cutterParams = OG_PRIMITIVE_PARAMS_CUBOID({ width: 0.9, height: 2.1, depth: 0.4 }); + const cutter = new Solid(OG_PRIMITIVE_CUBOID, cutterParams, { ogId: 'door-cutter' }); + log.run('translate cutter', () => cutter.transform(OG_TRANSFORM_TRANSLATE, { offset: [2, 0, 0] })); + log.run('door cut', () => wall.operate(OG_OPERATION_SUBTRACT, { tools: [cutter] })); + const path = new Wire(OG_PRIMITIVE_POLYLINE, OG_PRIMITIVE_PARAMS_POLYLINE({ + points: [[0, 1, 1], [4, 1, 1], [4, 1, 4]], closed: false, + })); + const disc = new Wire(OG_PRIMITIVE_CIRCLE, OG_PRIMITIVE_PARAMS_CIRCLE({ radius: 0.05 }), { + plane: { origin: [0, 1, 1], normal: [1, 0, 0], xDirection: [0, 0, 1] }, + }); + const rail = new Solid(OG_OPERATION_SWEEP, OG_OPERATION_PARAMS_SWEEP({ profile: disc, path }), { ogId: 'rail-1' }); + log.run('add members', () => level.addChild([profile, wall, cutter, path, disc, rail])); + scene.add(wall, rail); + return { scene, level, profile, wall, cutter, path, disc, rail }; +} + +export function editScene(log, parts) { + const { level, profile, wall, cutter, rail } = parts; + log.run('place level', () => level.transform(OG_TRANSFORM_PLACE, { origin: [0, 3, 0] })); + log.run('rotate rail', () => rail.transform(OG_TRANSFORM_ROTATE, { axis: [0, 1, 0], degrees: 90 })); + log.run('rebuild wall', () => wall.rebuild(OG_OPERATION_EXTRUDE, wallParams(profile, 4))); + log.run('re-cut', () => wall.operate(OG_OPERATION_SUBTRACT, { tools: [cutter] })); + log.run('rail minus wall', () => rail.operate(OG_OPERATION_SUBTRACT, { tools: [wall] })); +} + +export function trialEdit(log, parts) { + const { profile, wall } = parts; + const mark = OpenGeometry.mark(); + log.run('mark stats with mark', () => OpenGeometry.markStats()); + log.run('trial rebuild', () => wall.rebuild(OG_OPERATION_EXTRUDE, wallParams(profile, 5))); + log.run('rollback', () => mark.rollback()); + log.run('release', () => mark.release()); + log.run('release again', () => mark.release()); + const dryRun = () => wall.rebuild(OG_OPERATION_EXTRUDE, wallParams(profile, 6)); + log.run('dry-run transaction', () => OpenGeometry.transaction(dryRun, { dryRun: true })); + log.run('mark stats after release', () => OpenGeometry.markStats()); +} + +export function copyAndDispose(log, parts) { + const copy = parts.rail.instance({ ogId: 'rail-2' }); + log.run('translate copy', () => copy.transform(OG_TRANSFORM_TRANSLATE, { offset: [0, 0, 2] })); + parts.scene.add(copy); + log.run('dispose cutter', () => parts.cutter.dispose()); + return copy; +} + +export function pickWall(parts) { + parts.scene.updateMatrixWorld(true); + const raycaster = new THREE.Raycaster(new THREE.Vector3(...PICK_ORIGIN), new THREE.Vector3(...PICK_DIRECTION)); + const hits = raycaster.intersectObjects(parts.scene.children, true); + return outcome(() => { + const hit = hits[0]; + if (!hit) throw new Error('the pick ray hit nothing'); + return { + origin: PICK_ORIGIN, + direction: PICK_DIRECTION, + distance: hit.distance, + point: hit.point.toArray(), + faceIndex: hit.faceIndex, + resolved: OpenGeometry.resolveHit(hit), + }; + }); +} diff --git a/main/scripts/snapshot/sdk-snapshot.mjs b/main/scripts/snapshot/sdk-snapshot.mjs new file mode 100644 index 0000000..b57a1ba --- /dev/null +++ b/main/scripts/snapshot/sdk-snapshot.mjs @@ -0,0 +1,66 @@ +import { OpenGeometry } from '../../dist/index.js'; +import { createInlineKernel } from '../lib/kernel.mjs'; +import { allGetters, exportStep, geometry } from './sdk-dump.mjs'; +import { buildScene, copyAndDispose, editScene, pickWall, stepLog, trialEdit } from './sdk-scene.mjs'; +import { outcome, outcomeAsync, prepareDirectory, writer } from './stable-output.mjs'; + +const EXPORTS = [ + { name: 'level-default', options: (parts) => ({ nodes: [parts.level] }) }, + { name: 'level-metre-y', options: (parts) => ({ nodes: [parts.level], unit: 'metre', upAxis: 'Y' }) }, + { + name: 'bodies-millimetre-z', + options: (parts) => ({ nodes: [parts.wall, 'rail-2'], unit: 'millimetre', upAxis: 'Z' }), + }, + { name: 'wire-rejected', options: (parts) => ({ nodes: [parts.profile] }) }, + { name: 'invalid-unit', options: (parts) => ({ nodes: [parts.level], unit: 'inch' }) }, +]; + +function members(parts, copy) { + const { level, profile, wall, cutter, path, disc, rail } = parts; + return [level, profile, wall, cutter, path, disc, rail, copy].filter(Boolean); +} + +async function writeExports(out, parts) { + for (const { name, options } of EXPORTS) { + const result = await exportStep(options(parts)); + if (result.value) { + await out.text(`export.${name}.step`, result.value.text); + await out.json(`export.${name}.report`, result.value.report); + } else { + await out.json(`export.${name}.error`, result.error); + } + } +} + +async function writeBodies(out, bodies) { + for (const body of bodies) { + await out.json(`brep.${body.ogId}`, outcome(() => body.getBrep())); + } +} + +async function main() { + const directory = process.argv[2]; + if (!directory) throw new Error('usage: node scripts/snapshot/sdk-snapshot.mjs '); + await prepareDirectory(directory); + const out = writer(directory); + await createInlineKernel(); + const log = stepLog(); + const parts = buildScene(log); + await out.json('nodes.after-build', allGetters(members(parts))); + editScene(log, parts); + trialEdit(log, parts); + await out.json('nodes.after-edit', allGetters(members(parts))); + const copy = copyAndDispose(log, parts); + parts.scene.updateMatrixWorld(true); + log.run('flush sync', () => OpenGeometry.flush({ geometry: 'sync' })); + await out.json('geometry', geometry([parts.wall, parts.rail, copy])); + await out.json('settled', await outcomeAsync(() => OpenGeometry.settled())); + await out.json('pick', pickWall(parts)); + await writeExports(out, parts); + await out.json('nodes.final', allGetters(members(parts, copy))); + await writeBodies(out, [parts.profile, parts.wall, parts.path, parts.disc, parts.rail, copy]); + await out.json('mark-stats', outcome(() => OpenGeometry.markStats())); + await out.json('steps', log.entries); +} + +await main(); diff --git a/main/scripts/snapshot/stable-output.mjs b/main/scripts/snapshot/stable-output.mjs new file mode 100644 index 0000000..e87aba0 --- /dev/null +++ b/main/scripts/snapshot/stable-output.mjs @@ -0,0 +1,63 @@ +import { createHash } from 'node:crypto'; +import { mkdir, readdir, writeFile } from 'node:fs/promises'; +import path from 'node:path'; + +function exactNumber(value) { + if (Number.isInteger(value) && !Object.is(value, -0)) return value; + const view = new DataView(new ArrayBuffer(8)); + view.setFloat64(0, value); + return `f64:${view.getBigUint64(0).toString(16).padStart(16, '0')}`; +} + +export function canonical(value) { + if (typeof value === 'number') return exactNumber(value); + if (Array.isArray(value)) return value.map(canonical); + if (value && typeof value === 'object') { + return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical(value[key])])); + } + return value; +} + +export function stableJson(value) { + return `${JSON.stringify(canonical(value), null, 1)}\n`; +} + +export function digest(view) { + const bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength); + return createHash('sha256').update(bytes).digest('hex'); +} + +export function outcome(fn) { + try { + return { value: fn() }; + } catch (error) { + return { error: describeError(error) }; + } +} + +export async function outcomeAsync(fn) { + try { + return { value: await fn() }; + } catch (error) { + return { error: describeError(error) }; + } +} + +export function describeError(error) { + if (error && typeof error.toJSON === 'function') return error.toJSON(); + return { name: error?.name ?? 'Error', message: String(error?.message ?? error) }; +} + +export async function prepareDirectory(directory) { + await mkdir(directory, { recursive: true }); + if ((await readdir(directory)).length > 0) { + throw new Error(`output directory ${directory} must be empty`); + } +} + +export function writer(directory) { + return { + json: (name, value) => writeFile(path.join(directory, `${name}.json`), stableJson(value)), + text: (name, value) => writeFile(path.join(directory, name), value), + }; +} diff --git a/main/scripts/test/bindings.test.mjs b/main/scripts/test/bindings.test.mjs new file mode 100644 index 0000000..7621d02 --- /dev/null +++ b/main/scripts/test/bindings.test.mjs @@ -0,0 +1,117 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { initSync, OGWorldGraph, OGTessellator } from '../../opengeometry/pkg/opengeometry.js'; + +initSync({ module: readFileSync(new URL('../../opengeometry/pkg/opengeometry_bg.wasm', import.meta.url)) }); +const ACCURACY = { geometric: 1e-8, intersection: 1e-9, tessellation: 0.01, exchange: 1e-6 }; +const GRAPH = new OGWorldGraph(JSON.stringify(ACCURACY)); +const ENCODE = JSON.stringify; +const CAMEL_CASE_KEY = /^[a-z][A-Za-z0-9]*$/; +const CAMEL_CASE_KEYS = (value) => { + if (Array.isArray(value)) { + value.forEach(CAMEL_CASE_KEYS); + } else if (value !== null && typeof value === 'object') { + for (const [key, inner] of Object.entries(value)) { + assert(CAMEL_CASE_KEY.test(key), key); + CAMEL_CASE_KEYS(inner); + } + } +}; +const UNIT_CUBE = { kind: 'Cuboid', width: 1, height: 1, depth: 1 }; +const CUBE = JSON.parse(GRAPH.createPrimitive(ENCODE(UNIT_CUBE), ENCODE({ ogId: 'cube' }))); +assert.equal(CUBE.ogId, 'cube'); +CAMEL_CASE_KEYS(CUBE); +const INFO = JSON.parse(GRAPH.node('cube')); +CAMEL_CASE_KEYS(INFO); +assert.equal(INFO.bodyType, 'Solid'); +assert.equal(JSON.parse(GRAPH.nodeByHandle(INFO.handle, INFO.generation)).ogId, 'cube'); +GRAPH.transform('cube', ENCODE({ kind: 'Place', origin: [0, 0, 0], xDirection: [1, 0, 0] })); +assert.throws( + () => GRAPH.transform('cube', '{"kind":"Place","x_direction":[1,0,0]}'), + (error) => JSON.parse(String(error)).code === 'InvalidParameter', +); +const PLACEMENT = JSON.parse(GRAPH.placement('cube')); +CAMEL_CASE_KEYS(PLACEMENT); +assert.deepEqual(Object.keys(PLACEMENT), ['origin', 'xDirection', 'normal', 'scale']); +assert.throws( + () => GRAPH.createPrimitive(ENCODE({ kind: 'Cuboid', width: 1, height: 1, depth: 1, extra: 1 }), '{}'), + (error) => { + const parsed = JSON.parse(String(error)); + CAMEL_CASE_KEYS(parsed); + return parsed.code === 'InvalidParameter'; + }, +); +assert.throws( + () => GRAPH.createPrimitive(ENCODE({ kind: 'Polyline', points: [[0, 0, 0], [1, 0, 0]], closed: false }), '{}'), + (error) => JSON.parse(String(error)).code === 'InvalidParameter', +); +assert.throws( + () => new OGWorldGraph(JSON.stringify({ ...ACCURACY, tessellation: 0.1 })), + (error) => JSON.parse(String(error)).code === 'InvalidParameter', +); + +const SNAPSHOT = GRAPH.snapshot(INFO.shapeId); +assert(SNAPSHOT instanceof Uint8Array); +const TESSELLATOR = new OGTessellator(); +const SLOT = TESSELLATOR.load(SNAPSHOT); +const DIRECT = GRAPH.buffers(INFO.shapeId, 0.01, 1000); +const LOADED = TESSELLATOR.buffers(SLOT, 0.01, 1000); +assert.deepEqual(DIRECT.positions, LOADED.positions); +assert.deepEqual(DIRECT.faceRanges, LOADED.faceRanges); +assert.equal(DIRECT.triangles, 12); +assert.equal(TESSELLATOR.drop(SLOT), true); + +const MARK = GRAPH.mark(); +GRAPH.transform('cube', ENCODE({ kind: 'Translate', offset: [2, 0, 0] })); +assert(Math.abs(JSON.parse(GRAPH.bounds('cube'))[0] - 1.5) < 1e-6); +GRAPH.rollback(MARK); +GRAPH.release(MARK); +const M1 = GRAPH.mark(); +const M2 = GRAPH.mark(); +GRAPH.rollback(M1); +assert.throws( + () => GRAPH.release(M2), + (error) => { + const parsed = JSON.parse(String(error)); + return parsed.code === 'InvalidMark' && parsed.message === 'mark handle is stale'; + }, +); +const NEAR = (actual, expected) => actual.every((value, index) => Math.abs(value - expected[index]) < 1e-9); +const LOCAL = JSON.parse(GRAPH.localBounds('cube')); +const WORLD = JSON.parse(GRAPH.bounds('cube')); +assert(NEAR(LOCAL, WORLD)); +GRAPH.transform('cube', ENCODE({ kind: 'Translate', offset: [2, 0, 0] })); +assert(NEAR(JSON.parse(GRAPH.localBounds('cube')), LOCAL)); +assert(Math.abs(JSON.parse(GRAPH.bounds('cube'))[0] - WORLD[0] - 2) < 1e-9); +const BUCKETS = JSON.parse(GRAPH.displayBuckets('cube')); +assert.deepEqual(Object.keys(BUCKETS).sort(), ['floor', 'static']); +CAMEL_CASE_KEYS(BUCKETS); +assert.equal(BUCKETS.floor, 2 ** -21); +assert(BUCKETS.static >= BUCKETS.floor); +GRAPH.rollback(M1); +GRAPH.release(M1); +assert(Math.abs(JSON.parse(GRAPH.bounds('cube'))[0] + 0.5) < 1e-6); +const CHANGES = GRAPH.changesSince(0n); +assert(CHANGES.matrices instanceof Float64Array); +assert.equal(CHANGES.matrices.length, 16); +const CHANGES_JSON = JSON.parse(CHANGES.changesJson); +CAMEL_CASE_KEYS(CHANGES_JSON); +assert([...CHANGES_JSON.added, ...CHANGES_JSON.changed].some((entry) => entry.ogId === 'cube')); +CAMEL_CASE_KEYS(JSON.parse(GRAPH.markStats())); + +const COPY = JSON.parse(GRAPH.instance('cube', '{}')); +assert.equal(GRAPH.instanceCount('cube'), 2); +GRAPH.makeUnique(COPY.ogId); +assert.equal(GRAPH.instanceCount('cube'), 1); +const EXPORTED = GRAPH.exportStep(ENCODE(['cube']), '{}'); +assert(EXPORTED.text.includes('MANIFOLD_SOLID_BREP')); +assert.equal(JSON.parse(EXPORTED.reportJson).products, 1); +CAMEL_CASE_KEYS(JSON.parse(EXPORTED.reportJson)); +GRAPH.dispose('cube'); +assert.throws( + () => GRAPH.nodeByHandle(INFO.handle, INFO.generation), + (error) => JSON.parse(String(error)).code === 'Disposed', +); +TESSELLATOR.free(); +GRAPH.free(); +console.log('WASM bindings passed'); diff --git a/main/scripts/tsconfig.json b/main/scripts/tsconfig.json new file mode 100644 index 0000000..887874a --- /dev/null +++ b/main/scripts/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.scripts.json" +} diff --git a/main/tsconfig.base.json b/main/tsconfig.base.json new file mode 100644 index 0000000..c00d231 --- /dev/null +++ b/main/tsconfig.base.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "noImplicitOverride": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noPropertyAccessFromIndexSignature": true, + "verbatimModuleSyntax": true, + "isolatedModules": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true + } +} diff --git a/main/tsconfig.examples.json b/main/tsconfig.examples.json new file mode 100644 index 0000000..dd19756 --- /dev/null +++ b/main/tsconfig.examples.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "lib": ["ES2022", "DOM"] + }, + "include": ["opengeometry-three/examples-vite/**/*.ts", "scripts/lib/node-builtins.d.ts"] +} diff --git a/main/tsconfig.json b/main/tsconfig.json new file mode 100644 index 0000000..19fa183 --- /dev/null +++ b/main/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "dist", + "lib": ["ES2022", "DOM", "WebWorker"] + }, + "include": ["opengeometry-three/index.ts", "opengeometry-three/src/**/*.ts"] +} diff --git a/main/tsconfig.scripts.json b/main/tsconfig.scripts.json new file mode 100644 index 0000000..2935973 --- /dev/null +++ b/main/tsconfig.scripts.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "allowJs": true, + "checkJs": true, + "noImplicitAny": false, + "resolveJsonModule": true, + "lib": ["ES2022", "DOM"] + }, + "include": [ + "opengeometry-three/tests/**/*.mjs", + "scripts/**/*.mjs", + "scripts/**/*.d.ts", + "eslint.config.js", + "rollup.config.js" + ] +} diff --git a/main/tsconfig.tests.json b/main/tsconfig.tests.json new file mode 100644 index 0000000..3e6aada --- /dev/null +++ b/main/tsconfig.tests.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "resolveJsonModule": true, + "lib": ["ES2022", "DOM"] + }, + "include": ["opengeometry-three/tests/**/*.ts", "scripts/lib/node-builtins.d.ts", "playwright.config.ts"] +} diff --git a/main/tsconfig.worker.json b/main/tsconfig.worker.json new file mode 100644 index 0000000..2117f69 --- /dev/null +++ b/main/tsconfig.worker.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "lib": ["ES2022", "WebWorker"] + }, + "files": ["opengeometry-three/src/rendering/tessellation-worker.ts"] +} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 504ea41..0000000 --- a/package-lock.json +++ /dev/null @@ -1,3905 +0,0 @@ -{ - "name": "opengeometry", - "version": "2.0.13", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "opengeometry", - "version": "2.0.13", - "license": "MPL-2.0", - "dependencies": { - "uuid": "^10.0.0" - }, - "devDependencies": { - "@eslint/js": "^9.35.0", - "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-commonjs": "^26.0.1", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", - "@types/three": "^0.168.0", - "@types/uuid": "^10.0.0", - "@typescript-eslint/eslint-plugin": "^8.43.0", - "@typescript-eslint/parser": "^8.43.0", - "eslint": "^9.35.0", - "rollup": "^4.21.3", - "tslib": "^2.7.0", - "vite": "^6.2.2" - }, - "peerDependencies": { - "three": ">=0.168.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", - "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", - "dev": true, - "peer": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", - "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.25.6", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", - "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "peer": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", - "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", - "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.25.6" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", - "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.6", - "@babel/parser": "^7.25.6", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.6", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", - "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", - "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", - "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", - "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", - "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", - "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", - "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", - "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", - "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", - "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", - "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", - "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", - "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", - "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", - "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", - "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", - "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", - "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", - "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", - "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", - "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", - "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", - "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", - "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", - "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", - "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.35.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", - "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.15.2", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@rollup/plugin-babel": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz", - "integrity": "sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@rollup/pluginutils": "^5.0.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "@types/babel__core": "^7.1.9", - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "@types/babel__core": { - "optional": true - }, - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-commonjs": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-26.0.1.tgz", - "integrity": "sha512-UnsKoZK6/aGIH6AdkptXhNvhaqftcjq3zZdT+LY5Ftms6JR06nADcDsYp5hTU9E2lbJUEOhdlY5J4DNTneM+jQ==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "commondir": "^1.0.1", - "estree-walker": "^2.0.2", - "glob": "^10.4.1", - "is-reference": "1.2.1", - "magic-string": "^0.30.3" - }, - "engines": { - "node": ">=16.0.0 || 14 >= 14.17" - }, - "peerDependencies": { - "rollup": "^2.68.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "15.2.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", - "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-builtin-module": "^3.2.1", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-typescript": { - "version": "11.1.6", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz", - "integrity": "sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==", - "dev": true, - "dependencies": { - "@rollup/pluginutils": "^5.1.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.14.0||^3.0.0||^4.0.0", - "tslib": "*", - "typescript": ">=3.7.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - }, - "tslib": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.36.0.tgz", - "integrity": "sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.36.0.tgz", - "integrity": "sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.36.0.tgz", - "integrity": "sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.36.0.tgz", - "integrity": "sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.36.0.tgz", - "integrity": "sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.36.0.tgz", - "integrity": "sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.36.0.tgz", - "integrity": "sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.36.0.tgz", - "integrity": "sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.36.0.tgz", - "integrity": "sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.36.0.tgz", - "integrity": "sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.36.0.tgz", - "integrity": "sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.36.0.tgz", - "integrity": "sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.36.0.tgz", - "integrity": "sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.36.0.tgz", - "integrity": "sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.36.0.tgz", - "integrity": "sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.36.0.tgz", - "integrity": "sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.36.0.tgz", - "integrity": "sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.36.0.tgz", - "integrity": "sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.36.0.tgz", - "integrity": "sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@tweenjs/tween.js": { - "version": "23.1.3", - "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", - "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", - "dev": true - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/resolve": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", - "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", - "dev": true - }, - "node_modules/@types/stats.js": { - "version": "0.17.3", - "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.3.tgz", - "integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==", - "dev": true - }, - "node_modules/@types/three": { - "version": "0.168.0", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.168.0.tgz", - "integrity": "sha512-qAGLGzbaYgkkonOBfwOr+TZpOskPfFjrDAj801WQSVkUz0/D9zwir4vhruJ/CC/GteywzR9pqeVVfs5th/2oKw==", - "dev": true, - "dependencies": { - "@tweenjs/tween.js": "~23.1.3", - "@types/stats.js": "*", - "@types/webxr": "*", - "@webgpu/types": "*", - "fflate": "~0.8.2", - "meshoptimizer": "~0.18.1" - } - }, - "node_modules/@types/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "dev": true - }, - "node_modules/@types/webxr": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.20.tgz", - "integrity": "sha512-JGpU6qiIJQKUuVSKx1GtQnHJGxRjtfGIhzO2ilq43VZZS//f1h1Sgexbdk+Lq+7569a6EYhOWrUpIruR/1Enmg==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.43.0.tgz", - "integrity": "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.43.0", - "@typescript-eslint/type-utils": "8.43.0", - "@typescript-eslint/utils": "8.43.0", - "@typescript-eslint/visitor-keys": "8.43.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.43.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.43.0.tgz", - "integrity": "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.43.0", - "@typescript-eslint/types": "8.43.0", - "@typescript-eslint/typescript-estree": "8.43.0", - "@typescript-eslint/visitor-keys": "8.43.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.43.0.tgz", - "integrity": "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.43.0", - "@typescript-eslint/types": "^8.43.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.43.0.tgz", - "integrity": "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.43.0", - "@typescript-eslint/visitor-keys": "8.43.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.43.0.tgz", - "integrity": "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.43.0.tgz", - "integrity": "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.43.0", - "@typescript-eslint/typescript-estree": "8.43.0", - "@typescript-eslint/utils": "8.43.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.43.0.tgz", - "integrity": "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.43.0.tgz", - "integrity": "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.43.0", - "@typescript-eslint/tsconfig-utils": "8.43.0", - "@typescript-eslint/types": "8.43.0", - "@typescript-eslint/visitor-keys": "8.43.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.43.0.tgz", - "integrity": "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.43.0", - "@typescript-eslint/types": "8.43.0", - "@typescript-eslint/typescript-estree": "8.43.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.43.0.tgz", - "integrity": "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.43.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@webgpu/types": { - "version": "0.1.45", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.45.tgz", - "integrity": "sha512-0TBBF/mhakJoK0qUWCZugBnh23x+VwmYA5RLmtNQwvZt1pQ4P2fzIvQUiSe6jxzkBi4GF8R4BejJjro0ZSoSXQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true, - "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001660", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", - "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/chalk/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "peer": true - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/electron-to-chromium": { - "version": "1.5.20", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.20.tgz", - "integrity": "sha512-74mdl6Fs1HHzK9SUX4CKFxAtAe3nUns48y79TskHNAG6fGOlLfyKA4j855x+0b5u8rWJIrlaG9tcTPstMlwjIw==", - "dev": true, - "peer": true - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/esbuild": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", - "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.1", - "@esbuild/android-arm": "0.25.1", - "@esbuild/android-arm64": "0.25.1", - "@esbuild/android-x64": "0.25.1", - "@esbuild/darwin-arm64": "0.25.1", - "@esbuild/darwin-x64": "0.25.1", - "@esbuild/freebsd-arm64": "0.25.1", - "@esbuild/freebsd-x64": "0.25.1", - "@esbuild/linux-arm": "0.25.1", - "@esbuild/linux-arm64": "0.25.1", - "@esbuild/linux-ia32": "0.25.1", - "@esbuild/linux-loong64": "0.25.1", - "@esbuild/linux-mips64el": "0.25.1", - "@esbuild/linux-ppc64": "0.25.1", - "@esbuild/linux-riscv64": "0.25.1", - "@esbuild/linux-s390x": "0.25.1", - "@esbuild/linux-x64": "0.25.1", - "@esbuild/netbsd-arm64": "0.25.1", - "@esbuild/netbsd-x64": "0.25.1", - "@esbuild/openbsd-arm64": "0.25.1", - "@esbuild/openbsd-x64": "0.25.1", - "@esbuild/sunos-x64": "0.25.1", - "@esbuild/win32-arm64": "0.25.1", - "@esbuild/win32-ia32": "0.25.1", - "@esbuild/win32-x64": "0.25.1" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "9.35.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", - "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.1", - "@eslint/core": "^0.15.2", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.35.0", - "@eslint/plugin-kit": "^0.3.5", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "dev": true - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "dev": true, - "dependencies": { - "builtin-modules": "^3.3.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "peer": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, - "node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/meshoptimizer": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", - "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", - "dev": true - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true, - "peer": true - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.36.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.36.0.tgz", - "integrity": "sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.6" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.36.0", - "@rollup/rollup-android-arm64": "4.36.0", - "@rollup/rollup-darwin-arm64": "4.36.0", - "@rollup/rollup-darwin-x64": "4.36.0", - "@rollup/rollup-freebsd-arm64": "4.36.0", - "@rollup/rollup-freebsd-x64": "4.36.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.36.0", - "@rollup/rollup-linux-arm-musleabihf": "4.36.0", - "@rollup/rollup-linux-arm64-gnu": "4.36.0", - "@rollup/rollup-linux-arm64-musl": "4.36.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.36.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.36.0", - "@rollup/rollup-linux-riscv64-gnu": "4.36.0", - "@rollup/rollup-linux-s390x-gnu": "4.36.0", - "@rollup/rollup-linux-x64-gnu": "4.36.0", - "@rollup/rollup-linux-x64-musl": "4.36.0", - "@rollup/rollup-win32-arm64-msvc": "4.36.0", - "@rollup/rollup-win32-ia32-msvc": "4.36.0", - "@rollup/rollup-win32-x64-msvc": "4.36.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/three": { - "version": "0.168.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.168.0.tgz", - "integrity": "sha512-6m6jXtDwMJEK/GGMbAOTSAmxNdzKvvBzgd7q8bE/7Tr6m7PaBh5kKLrN7faWtlglXbzj7sVba48Idwx+NRsZXw==", - "peer": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", - "dev": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true, - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vite": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz", - "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "postcss": "^8.5.3", - "rollup": "^4.30.1" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "peer": true - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/package.json b/package.json index 727e687..393402b 100644 --- a/package.json +++ b/package.json @@ -1,67 +1,13 @@ { - "name": "opengeometry", - "author": "Vishwajeet Vinayak Mane", - "license": "MPL-2.0", - "version": "2.0.16", - "description": "CAD geometry kernel for the web built with Rust, WebAssembly, and Three.js.", - "type": "module", - "main": "index.js", + "name": "opengeometry-repository", + "private": true, "scripts": { - "dev": "vite", - "test": "cargo test --manifest-path main/opengeometry/Cargo.toml && cargo test --examples --manifest-path main/opengeometry/Cargo.toml", - "lint": "eslint main/opengeometry-three/src/**/*.ts --fix", - "lint:check": "eslint main/opengeometry-three/src/**/*.ts", - "clean-dist": "node ./scripts/clean-dist.mjs", - "build-three": "npm run clean-dist && rollup -c rollup.config.js", - "build-example-three": "npm --prefix main/opengeometry-three run build-example-three", - "build-core": "cd main/opengeometry && wasm-pack build --target web && cargo build --release", - "prepare-dist": "node ./scripts/prepare-dist.mjs", - "copy-wasm": "npm run prepare-dist", - "build": "npm run build-core && npm run build-three && npm run prepare-dist" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/OpenGeometry-io/OpenGeometry.git" - }, - "keywords": [ - "opengeometry", - "cad", - "cad-kernel", - "browser-cad", - "threejs", - "three.js", - "geometry", - "webassembly", - "wasm", - "ifc", - "step", - "stl", - "parametric-modeling" - ], - "bugs": { - "url": "https://github.com/OpenGeometry-io/OpenGeometry/issues" - }, - "homepage": "https://docs.opengeometry.io", - "peerDependencies": { - "three": ">=0.168.0" - }, - "dependencies": { - "uuid": "^10.0.0" - }, - "devDependencies": { - "@eslint/js": "^9.35.0", - "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-commonjs": "^26.0.1", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.6", - "@types/three": "^0.168.0", - "@types/uuid": "^10.0.0", - "@typescript-eslint/eslint-plugin": "^8.43.0", - "@typescript-eslint/parser": "^8.43.0", - "eslint": "^9.35.0", - "rollup": "^4.21.3", - "tslib": "^2.7.0", - "vite": "^6.2.2" - }, - "packageManager": "yarn@4.15.0+sha512.07ec708ac11e2eaa4ea2b04cfbb272812f7e74a753f1595eaef4486c663a98306a30cca3e6fc40f7a0b168dcfb3a2490b6a5e0501e20fb69cc36f563dd161c53" + "build": "npm --prefix main run build --", + "build-core": "npm --prefix main run build-core --", + "check": "npm --prefix main run check --", + "check:full": "npm --prefix main run check:full --", + "test": "npm --prefix main run test --", + "lint:check": "npm --prefix main run lint:check --", + "dev-example": "npm --prefix main run dev-example --" + } } diff --git a/release.md b/release.md index 8b13789..a81f755 100644 --- a/release.md +++ b/release.md @@ -1 +1,5 @@ +# Releases +The `opengeometry` package is published only by starting `.github/workflows/release.yml` +by hand on the `main` branch; a push never publishes. See the release process in +[developer.md](./developer.md#release-process). diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index db75e1f..0000000 --- a/rollup.config.js +++ /dev/null @@ -1,28 +0,0 @@ -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import babel from '@rollup/plugin-babel'; -import typescript from '@rollup/plugin-typescript'; - -export default { - input: 'main/opengeometry-three/index.ts', - external: ['three', /^three\//], - output: { - file: 'dist/index.js', - format: 'esm', - name: 'opengeometry', - sourcemap: true, - }, - plugins: [ - nodeResolve(), - commonjs(), - typescript({ - tsconfig: './tsconfig.json', // Use your project's tsconfig - declaration: true, - declarationDir: 'dist/', // Outputs declarations in a specific folder - }), - babel({ - babelHelpers: 'bundled', - exclude: 'node_modules/**', - }), - ], -}; diff --git a/scripts/clean-dist.mjs b/scripts/clean-dist.mjs deleted file mode 100644 index 222354a..0000000 --- a/scripts/clean-dist.mjs +++ /dev/null @@ -1,4 +0,0 @@ -import { rm } from "node:fs/promises"; -import { fileURLToPath } from "node:url"; - -await rm(fileURLToPath(new URL("../dist", import.meta.url)), { recursive: true, force: true }); diff --git a/scripts/prepare-dist.mjs b/scripts/prepare-dist.mjs deleted file mode 100644 index 3a542c4..0000000 --- a/scripts/prepare-dist.mjs +++ /dev/null @@ -1,170 +0,0 @@ -/* eslint-env node */ -import { cp, mkdir, readdir, readFile, rm, stat, writeFile } from "node:fs/promises"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -const scriptDir = path.dirname(fileURLToPath(import.meta.url)); -const rootDir = path.resolve(scriptDir, ".."); -const distDir = path.join(rootDir, "dist"); -const kernelPkgDir = path.join(rootDir, "main", "opengeometry", "pkg"); -const distKernelPkgDir = path.join(distDir, "opengeometry", "pkg"); - -const distRootKernelFiles = [ - "opengeometry_bg.wasm", - "opengeometry_bg.wasm.d.ts", -]; - -const distKernelSubpathFiles = [ - "opengeometry.js", - "opengeometry.d.ts", - "opengeometry_bg.wasm.d.ts", - "opengeometry_bg.wasm", -]; - -async function listFilesRecursively(dirPath) { - const entries = await readdir(dirPath); - const files = []; - - for (const entry of entries) { - const entryPath = path.join(dirPath, entry); - const entryStats = await stat(entryPath); - if (entryStats.isDirectory()) { - files.push(...(await listFilesRecursively(entryPath))); - continue; - } - files.push(entryPath); - } - - return files; -} - -function toPosixSpecifier(relativePath) { - const specifier = relativePath.split(path.sep).join("/"); - return specifier.startsWith(".") ? specifier : `./${specifier}`; -} - -function ensureExplicitJsExtension(specifier) { - if (!specifier.startsWith(".")) { - return specifier; - } - - const extension = path.extname(specifier); - if (extension.length > 0) { - return specifier; - } - - if (specifier.endsWith("/")) { - return `${specifier}index.js`; - } - - return `${specifier}.js`; -} - -async function rewriteKernelImportSpecifiers() { - const declarationFiles = (await listFilesRecursively(distDir)).filter((filePath) => - filePath.endsWith(".d.ts") - ); - const kernelTypeTarget = path.join(distDir, "opengeometry", "pkg", "opengeometry"); - const importPattern = /(["'])(?:\.\/)?(?:\.\.\/)+opengeometry\/pkg\/opengeometry\1/g; - const fromPattern = /(from\s+["'])(\.{1,2}\/[^"']+)(["'])/g; - const dynamicImportPattern = /(import\(\s*["'])(\.{1,2}\/[^"')]+)(["']\s*\))/g; - - for (const filePath of declarationFiles) { - const contents = await readFile(filePath, "utf8"); - const withKernelSpecifierRewrite = contents.replace(importPattern, (_fullMatch, quote) => { - const relative = path.relative(path.dirname(filePath), kernelTypeTarget); - return `${quote}${toPosixSpecifier(relative)}${quote}`; - }); - const withFromExtensions = withKernelSpecifierRewrite.replace( - fromPattern, - (_fullMatch, prefix, specifier, suffix) => - `${prefix}${ensureExplicitJsExtension(specifier)}${suffix}` - ); - const replacement = withFromExtensions.replace( - dynamicImportPattern, - (_fullMatch, prefix, specifier, suffix) => - `${prefix}${ensureExplicitJsExtension(specifier)}${suffix}` - ); - - if (replacement !== contents) { - await writeFile(filePath, replacement, "utf8"); - } - } -} - -function buildDistPackageJson(rootPackageJson) { - return { - name: rootPackageJson.name, - version: rootPackageJson.version, - description: rootPackageJson.description, - author: rootPackageJson.author, - license: rootPackageJson.license, - type: "module", - main: "./index.js", - types: "./index.d.ts", - exports: { - ".": { - types: "./index.d.ts", - import: "./index.js", - default: "./index.js", - }, - "./opengeometry/pkg/opengeometry": { - types: "./opengeometry/pkg/opengeometry.d.ts", - import: "./opengeometry/pkg/opengeometry.js", - default: "./opengeometry/pkg/opengeometry.js", - }, - "./opengeometry_bg.wasm": "./opengeometry_bg.wasm", - "./analytic-worker.js": "./analytic-worker.js", - "./package.json": "./package.json", - }, - keywords: rootPackageJson.keywords ?? [], - repository: rootPackageJson.repository, - bugs: rootPackageJson.bugs, - homepage: rootPackageJson.homepage, - peerDependencies: rootPackageJson.peerDependencies ?? {}, - dependencies: rootPackageJson.dependencies ?? {}, - }; -} - -async function main() { - await mkdir(distDir, { recursive: true }); - await mkdir(distKernelPkgDir, { recursive: true }); - - const distEntries = await readdir(distDir); - for (const entry of distEntries) { - if (!entry.endsWith(".tgz")) { - continue; - } - await rm(path.join(distDir, entry), { force: true }); - } - - for (const fileName of distRootKernelFiles) { - await cp(path.join(kernelPkgDir, fileName), path.join(distDir, fileName), { force: true }); - } - - for (const fileName of distKernelSubpathFiles) { - await cp(path.join(kernelPkgDir, fileName), path.join(distKernelPkgDir, fileName), { - force: true, - }); - } - - await cp(path.join(rootDir, "README.md"), path.join(distDir, "README.md"), { force: true }); - await cp(path.join(rootDir, "main/opengeometry-three/src/rendering/analytic-worker.js"), path.join(distDir, "analytic-worker.js"), { force: true }); - - await rewriteKernelImportSpecifiers(); - - const rootPackageJson = JSON.parse( - await readFile(path.join(rootDir, "package.json"), "utf8") - ); - const distPackageJson = buildDistPackageJson(rootPackageJson); - await writeFile( - path.join(distDir, "package.json"), - `${JSON.stringify(distPackageJson, null, 2)}\n`, - "utf8" - ); -} - -main().catch((error) => { - console.error("Failed to prepare dist package:", error); - process.exit(1); -}); diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index e7578e9..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "module": "es2020", // Use "es2020" for ESM - "outDir": "dist", - "strict": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, // Add this line for ESM - "moduleResolution": "node", // Change module resolution to "node" for ESM - "experimentalDecorators": true, // Enable decorators - "emitDecoratorMetadata": true, // Enable metadata for decorators - "baseUrl": ".", // Add this line for ESM - "paths": { - "*": ["main/*"] // Add this line for ESM - } - }, - "include": [ - "main/**/*.ts" - ], - "exclude": [ - "node_modules", - "main/opengeometry-three/examples-vite" - ] -} diff --git a/vite.config.js b/vite.config.js deleted file mode 100644 index 5719d12..0000000 --- a/vite.config.js +++ /dev/null @@ -1,17 +0,0 @@ -import { defineConfig } from 'vite'; - -export default defineConfig({ - root: 'test', - server: { - port: 7070, - fs: { - // Allow serving files from one level up to the project root - allow: ['..'] - } - }, - resolve: { - alias: { - three: '../node_modules/three/build/three.module.js' - } - } -}); \ No newline at end of file